summaryrefslogtreecommitdiff
path: root/storage/ndb/src
diff options
context:
space:
mode:
authorbrian@zim.(none) <>2005-04-26 18:19:54 -0700
committerbrian@zim.(none) <>2005-04-26 18:19:54 -0700
commit2a7c71e309337588b3815ea7ff1fe20d734d50d9 (patch)
treedf9016f3d70b4657f89dcddca2ec4e188fc7fbdf /storage/ndb/src
parent8e0eb65f9aaa3dc5a4f713988e58190b82466cde (diff)
downloadmariadb-git-2a7c71e309337588b3815ea7ff1fe20d734d50d9.tar.gz
Changes to create storage directory for storage engines.
Diffstat (limited to 'storage/ndb/src')
-rw-r--r--storage/ndb/src/Makefile.am33
-rw-r--r--storage/ndb/src/common/Makefile.am15
-rw-r--r--storage/ndb/src/common/debugger/BlockNames.cpp39
-rw-r--r--storage/ndb/src/common/debugger/DebuggerNames.cpp154
-rw-r--r--storage/ndb/src/common/debugger/EventLogger.cpp861
-rw-r--r--storage/ndb/src/common/debugger/GrepError.cpp133
-rw-r--r--storage/ndb/src/common/debugger/Makefile.am25
-rw-r--r--storage/ndb/src/common/debugger/SignalLoggerManager.cpp502
-rw-r--r--storage/ndb/src/common/debugger/signaldata/AccLock.cpp75
-rw-r--r--storage/ndb/src/common/debugger/signaldata/AlterIndx.cpp35
-rw-r--r--storage/ndb/src/common/debugger/signaldata/AlterTab.cpp38
-rw-r--r--storage/ndb/src/common/debugger/signaldata/AlterTable.cpp38
-rw-r--r--storage/ndb/src/common/debugger/signaldata/AlterTrig.cpp51
-rw-r--r--storage/ndb/src/common/debugger/signaldata/BackupImpl.cpp142
-rw-r--r--storage/ndb/src/common/debugger/signaldata/BackupSignalData.cpp129
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CloseComReqConf.cpp52
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CntrStart.cpp37
-rw-r--r--storage/ndb/src/common/debugger/signaldata/ContinueB.cpp35
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CopyGCI.cpp58
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CreateEvnt.cpp38
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CreateFragmentation.cpp56
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CreateIndx.cpp38
-rw-r--r--storage/ndb/src/common/debugger/signaldata/CreateTrig.cpp120
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp145
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DihContinueB.cpp220
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DihSwitchReplicaReq.cpp48
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DisconnectRep.cpp30
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DropIndx.cpp38
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DropTab.cpp50
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DropTrig.cpp89
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FailRep.cpp31
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FireTrigOrd.cpp56
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FsAppendReq.cpp38
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FsCloseReq.cpp40
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FsConf.cpp33
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FsOpenReq.cpp59
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp85
-rw-r--r--storage/ndb/src/common/debugger/signaldata/FsRef.cpp75
-rw-r--r--storage/ndb/src/common/debugger/signaldata/GCPSave.cpp78
-rwxr-xr-xstorage/ndb/src/common/debugger/signaldata/IndxAttrInfo.cpp31
-rwxr-xr-xstorage/ndb/src/common/debugger/signaldata/IndxKeyInfo.cpp31
-rw-r--r--storage/ndb/src/common/debugger/signaldata/LCP.cpp89
-rw-r--r--storage/ndb/src/common/debugger/signaldata/LqhFrag.cpp61
-rw-r--r--storage/ndb/src/common/debugger/signaldata/LqhKey.cpp161
-rw-r--r--storage/ndb/src/common/debugger/signaldata/LqhTrans.cpp40
-rw-r--r--storage/ndb/src/common/debugger/signaldata/Makefile.am47
-rw-r--r--storage/ndb/src/common/debugger/signaldata/MasterLCP.cpp87
-rw-r--r--storage/ndb/src/common/debugger/signaldata/NFCompleteRep.cpp44
-rw-r--r--storage/ndb/src/common/debugger/signaldata/NdbSttor.cpp50
-rw-r--r--storage/ndb/src/common/debugger/signaldata/NdbfsContinueB.cpp41
-rw-r--r--storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp104
-rw-r--r--storage/ndb/src/common/debugger/signaldata/PrepDropTab.cpp50
-rw-r--r--storage/ndb/src/common/debugger/signaldata/PrepFailReqRef.cpp52
-rw-r--r--storage/ndb/src/common/debugger/signaldata/ReadNodesConf.cpp24
-rw-r--r--storage/ndb/src/common/debugger/signaldata/ScanFrag.cpp42
-rw-r--r--storage/ndb/src/common/debugger/signaldata/ScanTab.cpp156
-rw-r--r--storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp211
-rw-r--r--storage/ndb/src/common/debugger/signaldata/SignalDroppedRep.cpp34
-rw-r--r--storage/ndb/src/common/debugger/signaldata/SignalNames.cpp652
-rw-r--r--storage/ndb/src/common/debugger/signaldata/StartRec.cpp52
-rw-r--r--storage/ndb/src/common/debugger/signaldata/SumaImpl.cpp241
-rw-r--r--storage/ndb/src/common/debugger/signaldata/SystemError.cpp40
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TcIndx.cpp74
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp69
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TcKeyRef.cpp28
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp111
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TcRollbackRep.cpp28
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TrigAttrInfo.cpp53
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TupCommit.cpp28
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TupKey.cpp50
-rw-r--r--storage/ndb/src/common/debugger/signaldata/TuxMaint.cpp45
-rw-r--r--storage/ndb/src/common/debugger/signaldata/UtilDelete.cpp65
-rw-r--r--storage/ndb/src/common/debugger/signaldata/UtilExecute.cpp59
-rw-r--r--storage/ndb/src/common/debugger/signaldata/UtilLock.cpp158
-rw-r--r--storage/ndb/src/common/debugger/signaldata/UtilPrepare.cpp64
-rw-r--r--storage/ndb/src/common/debugger/signaldata/UtilSequence.cpp67
-rw-r--r--storage/ndb/src/common/debugger/signaldata/print.awk55
-rw-r--r--storage/ndb/src/common/logger/ConsoleLogHandler.cpp68
-rw-r--r--storage/ndb/src/common/logger/FileLogHandler.cpp235
-rw-r--r--storage/ndb/src/common/logger/LogHandler.cpp184
-rw-r--r--storage/ndb/src/common/logger/LogHandlerList.cpp181
-rw-r--r--storage/ndb/src/common/logger/LogHandlerList.hpp94
-rw-r--r--storage/ndb/src/common/logger/Logger.cpp369
-rw-r--r--storage/ndb/src/common/logger/Makefile.am25
-rw-r--r--storage/ndb/src/common/logger/SysLogHandler.cpp158
-rw-r--r--storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.cpp164
-rw-r--r--storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.hpp40
-rw-r--r--storage/ndb/src/common/logger/listtest/Makefile14
-rw-r--r--storage/ndb/src/common/logger/loggertest/LoggerUnitTest.cpp197
-rw-r--r--storage/ndb/src/common/logger/loggertest/LoggerUnitTest.hpp49
-rw-r--r--storage/ndb/src/common/logger/loggertest/Makefile16
-rw-r--r--storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp345
-rw-r--r--storage/ndb/src/common/mgmcommon/IPCConfig.cpp388
-rw-r--r--storage/ndb/src/common/mgmcommon/Makefile.am28
-rw-r--r--storage/ndb/src/common/mgmcommon/printConfig/Makefile16
-rw-r--r--storage/ndb/src/common/mgmcommon/printConfig/printConfig.cpp89
-rw-r--r--storage/ndb/src/common/portlib/Makefile.am43
-rw-r--r--storage/ndb/src/common/portlib/NdbCondition.c142
-rw-r--r--storage/ndb/src/common/portlib/NdbConfig.c145
-rw-r--r--storage/ndb/src/common/portlib/NdbDaemon.c171
-rw-r--r--storage/ndb/src/common/portlib/NdbEnv.c34
-rw-r--r--storage/ndb/src/common/portlib/NdbHost.c34
-rw-r--r--storage/ndb/src/common/portlib/NdbMem.c75
-rw-r--r--storage/ndb/src/common/portlib/NdbMutex.c93
-rw-r--r--storage/ndb/src/common/portlib/NdbPortLibTest.cpp608
-rw-r--r--storage/ndb/src/common/portlib/NdbSleep.c43
-rw-r--r--storage/ndb/src/common/portlib/NdbTCP.cpp76
-rw-r--r--storage/ndb/src/common/portlib/NdbThread.c155
-rw-r--r--storage/ndb/src/common/portlib/NdbTick.c106
-rw-r--r--storage/ndb/src/common/portlib/gcc.cpp7
-rw-r--r--storage/ndb/src/common/portlib/memtest.c243
-rw-r--r--storage/ndb/src/common/portlib/mmslist.cpp103
-rw-r--r--storage/ndb/src/common/portlib/mmstest.cpp76
-rw-r--r--storage/ndb/src/common/portlib/munmaptest.cpp246
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/memtest/Makefile12
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/memtest/munmaptest/Makefile14
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/Makefile31
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbCondition.c243
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbConditionOSE.h103
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbEnv.c55
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbHost.c55
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbMem.c181
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbMem_SoftOse.cpp53
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbMutex.c85
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbOut.cpp96
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbSleep.c36
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbTCP.c38
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbThread.c183
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/ose/NdbTick.c64
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/test/Makefile15
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/Makefile30
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbCondition.c183
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbDaemon.c47
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbEnv.c33
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbHost.c53
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbMem.c235
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbMutex.c77
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbSleep.c35
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbTCP.c39
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbThread.c117
-rw-r--r--storage/ndb/src/common/portlib/old_dirs/win32/NdbTick.c64
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbCondition.c178
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbDaemon.c44
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbEnv.c31
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbHost.c52
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbMem.c283
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbMutex.c73
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbSleep.c32
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbTCP.c39
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbThread.c114
-rw-r--r--storage/ndb/src/common/portlib/win32/NdbTick.c64
-rw-r--r--storage/ndb/src/common/transporter/Makefile.am36
-rw-r--r--storage/ndb/src/common/transporter/OSE_Receiver.cpp359
-rw-r--r--storage/ndb/src/common/transporter/OSE_Receiver.hpp119
-rw-r--r--storage/ndb/src/common/transporter/OSE_Signals.hpp144
-rw-r--r--storage/ndb/src/common/transporter/OSE_Transporter.cpp487
-rw-r--r--storage/ndb/src/common/transporter/OSE_Transporter.hpp159
-rw-r--r--storage/ndb/src/common/transporter/Packer.cpp511
-rw-r--r--storage/ndb/src/common/transporter/Packer.hpp85
-rw-r--r--storage/ndb/src/common/transporter/SCI_Transporter.cpp1031
-rw-r--r--storage/ndb/src/common/transporter/SCI_Transporter.hpp395
-rw-r--r--storage/ndb/src/common/transporter/SHM_Buffer.hpp216
-rw-r--r--storage/ndb/src/common/transporter/SHM_Transporter.cpp367
-rw-r--r--storage/ndb/src/common/transporter/SHM_Transporter.hpp173
-rw-r--r--storage/ndb/src/common/transporter/SHM_Transporter.unix.cpp100
-rw-r--r--storage/ndb/src/common/transporter/SHM_Transporter.win32.cpp172
-rw-r--r--storage/ndb/src/common/transporter/SendBuffer.cpp89
-rw-r--r--storage/ndb/src/common/transporter/SendBuffer.hpp190
-rw-r--r--storage/ndb/src/common/transporter/TCP_Transporter.cpp434
-rw-r--r--storage/ndb/src/common/transporter/TCP_Transporter.hpp232
-rw-r--r--storage/ndb/src/common/transporter/Transporter.cpp208
-rw-r--r--storage/ndb/src/common/transporter/Transporter.hpp189
-rw-r--r--storage/ndb/src/common/transporter/TransporterInternalDefinitions.hpp302
-rw-r--r--storage/ndb/src/common/transporter/TransporterRegistry.cpp1625
-rw-r--r--storage/ndb/src/common/transporter/basictest/Makefile15
-rw-r--r--storage/ndb/src/common/transporter/basictest/basicTransporterTest.cpp535
-rw-r--r--storage/ndb/src/common/transporter/buddy.cpp325
-rw-r--r--storage/ndb/src/common/transporter/buddy.hpp172
-rw-r--r--storage/ndb/src/common/transporter/failoverSCI/Makefile18
-rw-r--r--storage/ndb/src/common/transporter/failoverSCI/failoverSCI.cpp863
-rw-r--r--storage/ndb/src/common/transporter/perftest/Makefile15
-rw-r--r--storage/ndb/src/common/transporter/perftest/perfTransporterTest.cpp773
-rw-r--r--storage/ndb/src/common/transporter/priotest/Makefile15
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioOSE/Makefile17
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioSCI/Makefile17
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioSCI/prioSCI.cpp29
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioSHM/Makefile13
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioSHM/prioSHM.cpp26
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioTCP/Makefile13
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioTCP/prioTCP.cpp26
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioTransporterTest.cpp768
-rw-r--r--storage/ndb/src/common/transporter/priotest/prioTransporterTest.hpp35
-rw-r--r--storage/ndb/src/common/util/Base64.cpp212
-rw-r--r--storage/ndb/src/common/util/BaseString.cpp434
-rw-r--r--storage/ndb/src/common/util/Bitmask.cpp351
-rw-r--r--storage/ndb/src/common/util/ConfigValues.cpp748
-rw-r--r--storage/ndb/src/common/util/File.cpp205
-rw-r--r--storage/ndb/src/common/util/InputStream.cpp61
-rw-r--r--storage/ndb/src/common/util/Makefile.am49
-rw-r--r--storage/ndb/src/common/util/NdbErrHnd.cpp492
-rw-r--r--storage/ndb/src/common/util/NdbOut.cpp173
-rw-r--r--storage/ndb/src/common/util/NdbSqlUtil.cpp1007
-rw-r--r--storage/ndb/src/common/util/OutputStream.cpp99
-rw-r--r--storage/ndb/src/common/util/Parser.cpp350
-rw-r--r--storage/ndb/src/common/util/Properties.cpp1136
-rw-r--r--storage/ndb/src/common/util/SimpleProperties.cpp507
-rw-r--r--storage/ndb/src/common/util/SocketAuthenticator.cpp91
-rw-r--r--storage/ndb/src/common/util/SocketClient.cpp94
-rw-r--r--storage/ndb/src/common/util/SocketServer.cpp345
-rw-r--r--storage/ndb/src/common/util/basestring_vsnprintf.c71
-rw-r--r--storage/ndb/src/common/util/filetest/FileUnitTest.cpp237
-rw-r--r--storage/ndb/src/common/util/filetest/FileUnitTest.hpp41
-rw-r--r--storage/ndb/src/common/util/filetest/Makefile14
-rw-r--r--storage/ndb/src/common/util/getarg.cat3237
-rw-r--r--storage/ndb/src/common/util/md5_hash.cpp239
-rw-r--r--storage/ndb/src/common/util/ndb_init.c35
-rw-r--r--storage/ndb/src/common/util/new.cpp43
-rw-r--r--storage/ndb/src/common/util/random.c284
-rw-r--r--storage/ndb/src/common/util/socket_io.cpp281
-rw-r--r--storage/ndb/src/common/util/strdup.c28
-rw-r--r--storage/ndb/src/common/util/testConfigValues/Makefile12
-rw-r--r--storage/ndb/src/common/util/testConfigValues/testConfigValues.cpp122
-rw-r--r--storage/ndb/src/common/util/testProperties/Makefile9
-rw-r--r--storage/ndb/src/common/util/testProperties/testProperties.cpp195
-rw-r--r--storage/ndb/src/common/util/testSimpleProperties/Makefile12
-rw-r--r--storage/ndb/src/common/util/testSimpleProperties/sp_test.cpp95
-rw-r--r--storage/ndb/src/common/util/uucode.c234
-rw-r--r--storage/ndb/src/common/util/version.c238
-rw-r--r--storage/ndb/src/cw/Makefile.am4
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.cpp215
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsp216
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsw29
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.h40
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.icobin0 -> 1078 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.rc193
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.sln21
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.suobin0 -> 8704 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.vcproj240
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/Closed.ICObin0 -> 1078 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/NdbControls.cpp436
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/Open.ICObin0 -> 1078 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/StdAfx.cpp24
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/StdAfx.h69
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/TreeView.cpp19
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/TreeView.h19
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/bmp00001.bmpbin0 -> 622 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/resource.h90
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/small.icobin0 -> 318 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/C++/toolbar.bmpbin0 -> 622 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/App.icobin0 -> 1078 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/CPC_Form.cs1400
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/Computer.cs256
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/ComputerAddDialog.cs242
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/ComputerRemoveDialog.cs228
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/DATABASE.ICObin0 -> 1078 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/Database.cs162
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj240
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj.user48
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.ncbbin0 -> 19456 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.sln21
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/PanelWizard.cs1883
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/Process.cs144
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/ProcessDefineDialog.cs435
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/fileaccess/FileMgmt.cs41
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/simpleparser/SimpleCPCParser.cs360
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/SocketComm.cs207
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/myTcpClient.cs26
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/startDatabaseDlg.cs251
-rw-r--r--storage/ndb/src/cw/cpcc-win32/csharp/telnetclient/telnetClient.cs408
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Computer.cls20
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Database.cls18
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 110.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 231.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 237.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 241.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 242.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 270.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 271.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 273.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 31.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 337.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 338.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Icon 339.icobin0 -> 766 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/MSSCCPRJ.SCC5
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Module1.bas233
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbp49
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbw13
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/Process.cls22
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/closed folder.icobin0 -> 10134 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/computer.icobin0 -> 10134 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmAbout.frm245
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmLogin.frm119
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmMain.frm1207
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frm124
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frxbin0 -> 4 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase.frxbin0 -> 12 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase1.frm187
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.frm136
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.log1
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase3.frm88
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmOptions.frm231
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/frmSplash.frxbin0 -> 70450 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/networking.icobin0 -> 10134 bytes
-rw-r--r--storage/ndb/src/cw/cpcc-win32/vb6/open folder.icobin0 -> 10134 bytes
-rw-r--r--storage/ndb/src/cw/cpcd/APIService.cpp386
-rw-r--r--storage/ndb/src/cw/cpcd/APIService.hpp64
-rw-r--r--storage/ndb/src/cw/cpcd/CPCD.cpp435
-rw-r--r--storage/ndb/src/cw/cpcd/CPCD.hpp382
-rw-r--r--storage/ndb/src/cw/cpcd/Makefile.am20
-rw-r--r--storage/ndb/src/cw/cpcd/Monitor.cpp79
-rw-r--r--storage/ndb/src/cw/cpcd/Process.cpp479
-rw-r--r--storage/ndb/src/cw/cpcd/common.cpp98
-rw-r--r--storage/ndb/src/cw/cpcd/common.hpp36
-rw-r--r--storage/ndb/src/cw/cpcd/main.cpp187
-rw-r--r--storage/ndb/src/cw/test/socketclient/Makefile24
-rw-r--r--storage/ndb/src/cw/test/socketclient/socketClientTest.cpp64
-rw-r--r--storage/ndb/src/cw/util/ClientInterface.cpp185
-rw-r--r--storage/ndb/src/cw/util/ClientInterface.hpp49
-rw-r--r--storage/ndb/src/cw/util/Makefile10
-rw-r--r--storage/ndb/src/cw/util/SocketRegistry.cpp213
-rw-r--r--storage/ndb/src/cw/util/SocketRegistry.hpp290
-rw-r--r--storage/ndb/src/cw/util/SocketService.cpp60
-rw-r--r--storage/ndb/src/cw/util/SocketService.hpp46
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/SISCI_LIBRARY_WIN32.TXT77
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/scilib.libbin0 -> 17918 bytes
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/scilib_md.libbin0 -> 18000 bytes
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/scilib_mt.libbin0 -> 17924 bytes
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api.libbin0 -> 264284 bytes
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_md.libbin0 -> 265578 bytes
-rw-r--r--storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_mt.libbin0 -> 264386 bytes
-rw-r--r--storage/ndb/src/kernel/Makefile.am75
-rw-r--r--storage/ndb/src/kernel/SimBlockList.cpp126
-rw-r--r--storage/ndb/src/kernel/blocks/ERROR_codes.txt451
-rw-r--r--storage/ndb/src/kernel/blocks/Makefile.am19
-rw-r--r--storage/ndb/src/kernel/blocks/NodeRestart.new.txt82
-rw-r--r--storage/ndb/src/kernel/blocks/NodeRestart.txt80
-rw-r--r--storage/ndb/src/kernel/blocks/Start.txt97
-rw-r--r--storage/ndb/src/kernel/blocks/SystemRestart.new.txt61
-rw-r--r--storage/ndb/src/kernel/blocks/SystemRestart.txt61
-rw-r--r--storage/ndb/src/kernel/blocks/backup/Backup.cpp4660
-rw-r--r--storage/ndb/src/kernel/blocks/backup/Backup.hpp696
-rw-r--r--storage/ndb/src/kernel/blocks/backup/Backup.txt343
-rw-r--r--storage/ndb/src/kernel/blocks/backup/BackupFormat.hpp149
-rw-r--r--storage/ndb/src/kernel/blocks/backup/BackupInit.cpp211
-rw-r--r--storage/ndb/src/kernel/blocks/backup/FsBuffer.hpp343
-rw-r--r--storage/ndb/src/kernel/blocks/backup/Makefile.am24
-rw-r--r--storage/ndb/src/kernel/blocks/backup/read.cpp478
-rw-r--r--storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp1393
-rw-r--r--storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.hpp124
-rw-r--r--storage/ndb/src/kernel/blocks/cmvmi/Makefile.am24
-rw-r--r--storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp1470
-rw-r--r--storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp269
-rw-r--r--storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp11653
-rw-r--r--storage/ndb/src/kernel/blocks/dbacc/Makefile.am26
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/CreateIndex.txt152
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/CreateTable.new.txt29
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/CreateTable.txt35
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp11884
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp1990
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.txt88
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/DropTable.txt140
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Event.txt102
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Makefile.am25
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Master_AddTable.sfl751
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/SchemaFile.hpp57
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Slave_AddTable.sfl416
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/printSchemaFile.cpp112
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp1603
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp319
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp14272
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/LCP.txt35
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/Makefile.am23
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/Sysfile.hpp275
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/printSysfile/Makefile12
-rw-r--r--storage/ndb/src/kernel/blocks/dbdih/printSysfile/printSysfile.cpp158
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp2953
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp455
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp18661
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/Makefile.am25
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/redoLogReader/Makefile9
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp312
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp235
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp464
-rw-r--r--storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp1974
-rw-r--r--storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp368
-rw-r--r--storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp13098
-rw-r--r--storage/ndb/src/kernel/blocks/dbtc/Makefile.am23
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/AttributeOffset.hpp136
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp2409
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp473
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupBuffer.cpp273
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp586
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp411
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp2052
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp384
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp1343
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp569
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupLCP.cpp596
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp711
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupPagMan.cpp370
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp556
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp1185
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupStoredProcDef.cpp230
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupSystemRestart.cpp1021
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupTabDesMan.cpp212
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp1150
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupUndoLog.cpp284
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/Makefile.am41
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/Notes.txt183
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp1291
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp175
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp443
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp317
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp183
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp513
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp581
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp1041
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxSearch.cpp449
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp709
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/Makefile.am34
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/Times.txt151
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/tuxstatus.html120
-rw-r--r--storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp2589
-rw-r--r--storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp485
-rw-r--r--storage/ndb/src/kernel/blocks/dbutil/DbUtil.txt68
-rw-r--r--storage/ndb/src/kernel/blocks/dbutil/Makefile.am23
-rw-r--r--storage/ndb/src/kernel/blocks/grep/Grep.cpp2010
-rw-r--r--storage/ndb/src/kernel/blocks/grep/Grep.hpp535
-rw-r--r--storage/ndb/src/kernel/blocks/grep/GrepInit.cpp164
-rw-r--r--storage/ndb/src/kernel/blocks/grep/Makefile.am23
-rw-r--r--storage/ndb/src/kernel/blocks/grep/systab_test/Makefile12
-rw-r--r--storage/ndb/src/kernel/blocks/grep/systab_test/grep_systab_test.cpp138
-rw-r--r--storage/ndb/src/kernel/blocks/mutexes.hpp39
-rw-r--r--storage/ndb/src/kernel/blocks/ndbcntr/Makefile.am26
-rw-r--r--storage/ndb/src/kernel/blocks/ndbcntr/Ndbcntr.hpp376
-rw-r--r--storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp117
-rw-r--r--storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp2695
-rw-r--r--storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrSysTable.cpp94
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.cpp1033
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp234
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/AsyncFileTest.cpp695
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/Makefile27
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.cpp20
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.hpp116
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/Filename.cpp219
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/Filename.hpp100
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/Makefile.am27
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.cpp18
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp166
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelOSE.hpp204
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/Makefile13
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/MemoryChannelTest.cpp193
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp1018
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp127
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/OpenFiles.hpp114
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/Pool.hpp261
-rw-r--r--storage/ndb/src/kernel/blocks/ndbfs/VoidFs.cpp200
-rw-r--r--storage/ndb/src/kernel/blocks/new-block.tar.gzbin0 -> 1816 bytes
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/Makefile.am25
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp392
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp106
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp3928
-rw-r--r--storage/ndb/src/kernel/blocks/qmgr/timer.hpp72
-rw-r--r--storage/ndb/src/kernel/blocks/suma/Makefile.am23
-rw-r--r--storage/ndb/src/kernel/blocks/suma/Suma.cpp4073
-rw-r--r--storage/ndb/src/kernel/blocks/suma/Suma.hpp600
-rw-r--r--storage/ndb/src/kernel/blocks/suma/Suma.txt192
-rw-r--r--storage/ndb/src/kernel/blocks/suma/SumaInit.cpp192
-rw-r--r--storage/ndb/src/kernel/blocks/trix/Makefile.am23
-rw-r--r--storage/ndb/src/kernel/blocks/trix/Trix.cpp967
-rw-r--r--storage/ndb/src/kernel/blocks/trix/Trix.hpp191
-rw-r--r--storage/ndb/src/kernel/error/Error.hpp85
-rw-r--r--storage/ndb/src/kernel/error/ErrorHandlingMacros.hpp52
-rw-r--r--storage/ndb/src/kernel/error/ErrorMessages.cpp75
-rw-r--r--storage/ndb/src/kernel/error/ErrorMessages.hpp22
-rw-r--r--storage/ndb/src/kernel/error/ErrorReporter.cpp393
-rw-r--r--storage/ndb/src/kernel/error/ErrorReporter.hpp62
-rw-r--r--storage/ndb/src/kernel/error/Makefile.am25
-rw-r--r--storage/ndb/src/kernel/error/TimeModule.cpp109
-rw-r--r--storage/ndb/src/kernel/error/TimeModule.hpp46
-rw-r--r--storage/ndb/src/kernel/main.cpp369
-rw-r--r--storage/ndb/src/kernel/vm/Array.hpp165
-rw-r--r--storage/ndb/src/kernel/vm/ArrayFifoList.hpp30
-rw-r--r--storage/ndb/src/kernel/vm/ArrayList.hpp30
-rw-r--r--storage/ndb/src/kernel/vm/ArrayPool.hpp856
-rw-r--r--storage/ndb/src/kernel/vm/CArray.hpp141
-rw-r--r--storage/ndb/src/kernel/vm/Callback.hpp24
-rw-r--r--storage/ndb/src/kernel/vm/ClusterConfiguration.cpp484
-rw-r--r--storage/ndb/src/kernel/vm/ClusterConfiguration.hpp105
-rw-r--r--storage/ndb/src/kernel/vm/Configuration.cpp801
-rw-r--r--storage/ndb/src/kernel/vm/Configuration.hpp139
-rw-r--r--storage/ndb/src/kernel/vm/DLFifoList.hpp360
-rw-r--r--storage/ndb/src/kernel/vm/DLHashTable.hpp493
-rw-r--r--storage/ndb/src/kernel/vm/DLHashTable2.hpp500
-rw-r--r--storage/ndb/src/kernel/vm/DLList.hpp369
-rw-r--r--storage/ndb/src/kernel/vm/DataBuffer.hpp532
-rw-r--r--storage/ndb/src/kernel/vm/Emulator.cpp269
-rw-r--r--storage/ndb/src/kernel/vm/Emulator.hpp107
-rw-r--r--storage/ndb/src/kernel/vm/FastScheduler.cpp500
-rw-r--r--storage/ndb/src/kernel/vm/FastScheduler.hpp345
-rw-r--r--storage/ndb/src/kernel/vm/GlobalData.hpp117
-rw-r--r--storage/ndb/src/kernel/vm/KeyTable.hpp43
-rw-r--r--storage/ndb/src/kernel/vm/KeyTable2.hpp43
-rw-r--r--storage/ndb/src/kernel/vm/LongSignal.hpp79
-rw-r--r--storage/ndb/src/kernel/vm/Makefile.am43
-rw-r--r--storage/ndb/src/kernel/vm/MetaData.cpp113
-rw-r--r--storage/ndb/src/kernel/vm/MetaData.hpp243
-rw-r--r--storage/ndb/src/kernel/vm/Mutex.cpp287
-rw-r--r--storage/ndb/src/kernel/vm/Mutex.hpp267
-rw-r--r--storage/ndb/src/kernel/vm/Prio.hpp32
-rw-r--r--storage/ndb/src/kernel/vm/RequestTracker.hpp58
-rw-r--r--storage/ndb/src/kernel/vm/SLList.hpp295
-rw-r--r--storage/ndb/src/kernel/vm/SafeCounter.cpp159
-rw-r--r--storage/ndb/src/kernel/vm/SafeCounter.hpp301
-rw-r--r--storage/ndb/src/kernel/vm/SectionReader.cpp143
-rw-r--r--storage/ndb/src/kernel/vm/SectionReader.hpp49
-rw-r--r--storage/ndb/src/kernel/vm/SignalCounter.hpp166
-rw-r--r--storage/ndb/src/kernel/vm/SimBlockList.hpp51
-rw-r--r--storage/ndb/src/kernel/vm/SimplePropertiesSection.cpp223
-rw-r--r--storage/ndb/src/kernel/vm/SimulatedBlock.cpp1804
-rw-r--r--storage/ndb/src/kernel/vm/SimulatedBlock.hpp756
-rw-r--r--storage/ndb/src/kernel/vm/ThreadConfig.cpp204
-rw-r--r--storage/ndb/src/kernel/vm/ThreadConfig.hpp39
-rw-r--r--storage/ndb/src/kernel/vm/TimeQueue.cpp209
-rw-r--r--storage/ndb/src/kernel/vm/TimeQueue.hpp62
-rw-r--r--storage/ndb/src/kernel/vm/TransporterCallback.cpp454
-rw-r--r--storage/ndb/src/kernel/vm/VMSignal.cpp34
-rw-r--r--storage/ndb/src/kernel/vm/VMSignal.hpp198
-rw-r--r--storage/ndb/src/kernel/vm/WaitQueue.hpp35
-rw-r--r--storage/ndb/src/kernel/vm/WatchDog.cpp145
-rw-r--r--storage/ndb/src/kernel/vm/WatchDog.hpp56
-rw-r--r--storage/ndb/src/kernel/vm/al_test/Makefile12
-rw-r--r--storage/ndb/src/kernel/vm/al_test/arrayListTest.cpp317
-rw-r--r--storage/ndb/src/kernel/vm/al_test/arrayPoolTest.cpp298
-rw-r--r--storage/ndb/src/kernel/vm/al_test/main.cpp69
-rw-r--r--storage/ndb/src/kernel/vm/pc.hpp251
-rw-r--r--storage/ndb/src/kernel/vm/testCopy/Makefile9
-rw-r--r--storage/ndb/src/kernel/vm/testCopy/rr.cpp32
-rw-r--r--storage/ndb/src/kernel/vm/testCopy/testCopy.cpp341
-rw-r--r--storage/ndb/src/kernel/vm/testDataBuffer/Makefile10
-rw-r--r--storage/ndb/src/kernel/vm/testDataBuffer/testDataBuffer.cpp188
-rw-r--r--storage/ndb/src/kernel/vm/testLongSig/Makefile9
-rw-r--r--storage/ndb/src/kernel/vm/testLongSig/testLongSig.cpp333
-rw-r--r--storage/ndb/src/kernel/vm/testSimplePropertiesSection/Makefile10
-rw-r--r--storage/ndb/src/kernel/vm/testSimplePropertiesSection/test.cpp171
-rw-r--r--storage/ndb/src/mgmapi/LocalConfig.cpp319
-rw-r--r--storage/ndb/src/mgmapi/LocalConfig.hpp68
-rw-r--r--storage/ndb/src/mgmapi/Makefile.am30
-rw-r--r--storage/ndb/src/mgmapi/mgmapi.cpp2251
-rw-r--r--storage/ndb/src/mgmapi/mgmapi_configuration.cpp157
-rw-r--r--storage/ndb/src/mgmapi/mgmapi_configuration.hpp48
-rw-r--r--storage/ndb/src/mgmapi/mgmapi_internal.h77
-rw-r--r--storage/ndb/src/mgmapi/ndb_logevent.cpp483
-rw-r--r--storage/ndb/src/mgmapi/ndb_logevent.hpp34
-rw-r--r--storage/ndb/src/mgmapi/test/Makefile13
-rw-r--r--storage/ndb/src/mgmapi/test/keso.c470
-rw-r--r--storage/ndb/src/mgmapi/test/mgmSrvApi.cpp126
-rw-r--r--storage/ndb/src/mgmclient/CommandInterpreter.cpp2297
-rw-r--r--storage/ndb/src/mgmclient/Makefile.am58
-rw-r--r--storage/ndb/src/mgmclient/main.cpp169
-rw-r--r--storage/ndb/src/mgmclient/ndb_mgmclient.h33
-rw-r--r--storage/ndb/src/mgmclient/ndb_mgmclient.hpp33
-rw-r--r--storage/ndb/src/mgmclient/test_cpcd/Makefile17
-rw-r--r--storage/ndb/src/mgmclient/test_cpcd/test_cpcd.cpp157
-rw-r--r--storage/ndb/src/mgmsrv/Config.cpp268
-rw-r--r--storage/ndb/src/mgmsrv/Config.hpp92
-rw-r--r--storage/ndb/src/mgmsrv/ConfigInfo.cpp3876
-rw-r--r--storage/ndb/src/mgmsrv/ConfigInfo.hpp142
-rw-r--r--storage/ndb/src/mgmsrv/InitConfigFileParser.cpp590
-rw-r--r--storage/ndb/src/mgmsrv/InitConfigFileParser.hpp127
-rw-r--r--storage/ndb/src/mgmsrv/Makefile.am60
-rw-r--r--storage/ndb/src/mgmsrv/MgmtSrvr.cpp2933
-rw-r--r--storage/ndb/src/mgmsrv/MgmtSrvr.hpp794
-rw-r--r--storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp299
-rw-r--r--storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp140
-rw-r--r--storage/ndb/src/mgmsrv/Services.cpp1568
-rw-r--r--storage/ndb/src/mgmsrv/Services.hpp124
-rw-r--r--storage/ndb/src/mgmsrv/SignalQueue.cpp104
-rw-r--r--storage/ndb/src/mgmsrv/SignalQueue.hpp100
-rw-r--r--storage/ndb/src/mgmsrv/convertStrToInt.cpp43
-rw-r--r--storage/ndb/src/mgmsrv/convertStrToInt.hpp25
-rw-r--r--storage/ndb/src/mgmsrv/main.cpp382
-rw-r--r--storage/ndb/src/mgmsrv/mkconfig/Makefile13
-rw-r--r--storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp61
-rw-r--r--storage/ndb/src/ndbapi/API.hpp26
-rw-r--r--storage/ndb/src/ndbapi/ClusterMgr.cpp820
-rw-r--r--storage/ndb/src/ndbapi/ClusterMgr.hpp215
-rw-r--r--storage/ndb/src/ndbapi/DictCache.cpp277
-rw-r--r--storage/ndb/src/ndbapi/DictCache.hpp92
-rw-r--r--storage/ndb/src/ndbapi/Makefile.am62
-rw-r--r--storage/ndb/src/ndbapi/Ndb.cpp1317
-rw-r--r--storage/ndb/src/ndbapi/NdbApiSignal.cpp279
-rw-r--r--storage/ndb/src/ndbapi/NdbApiSignal.hpp224
-rw-r--r--storage/ndb/src/ndbapi/NdbBlob.cpp1602
-rw-r--r--storage/ndb/src/ndbapi/NdbBlobImpl.hpp39
-rw-r--r--storage/ndb/src/ndbapi/NdbDictionary.cpp1050
-rw-r--r--storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp3124
-rw-r--r--storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp704
-rw-r--r--storage/ndb/src/ndbapi/NdbErrorOut.cpp45
-rw-r--r--storage/ndb/src/ndbapi/NdbEventOperation.cpp115
-rw-r--r--storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp1366
-rw-r--r--storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp204
-rw-r--r--storage/ndb/src/ndbapi/NdbImpl.hpp136
-rw-r--r--storage/ndb/src/ndbapi/NdbIndexOperation.cpp412
-rw-r--r--storage/ndb/src/ndbapi/NdbLinHash.hpp447
-rw-r--r--storage/ndb/src/ndbapi/NdbOperation.cpp400
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationDefine.cpp723
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationExec.cpp580
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationInt.cpp1168
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationScan.cpp16
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationSearch.cpp592
-rw-r--r--storage/ndb/src/ndbapi/NdbPool.cpp71
-rw-r--r--storage/ndb/src/ndbapi/NdbPoolImpl.cpp528
-rw-r--r--storage/ndb/src/ndbapi/NdbPoolImpl.hpp166
-rw-r--r--storage/ndb/src/ndbapi/NdbRecAttr.cpp415
-rw-r--r--storage/ndb/src/ndbapi/NdbReceiver.cpp295
-rw-r--r--storage/ndb/src/ndbapi/NdbScanFilter.cpp577
-rw-r--r--storage/ndb/src/ndbapi/NdbScanOperation.cpp1669
-rw-r--r--storage/ndb/src/ndbapi/NdbTransaction.cpp2124
-rw-r--r--storage/ndb/src/ndbapi/NdbTransactionScan.cpp122
-rw-r--r--storage/ndb/src/ndbapi/NdbUtil.cpp69
-rw-r--r--storage/ndb/src/ndbapi/NdbUtil.hpp97
-rw-r--r--storage/ndb/src/ndbapi/NdbWaiter.hpp102
-rw-r--r--storage/ndb/src/ndbapi/Ndberr.cpp82
-rw-r--r--storage/ndb/src/ndbapi/Ndbif.cpp1368
-rw-r--r--storage/ndb/src/ndbapi/Ndbinit.cpp254
-rw-r--r--storage/ndb/src/ndbapi/Ndblist.cpp824
-rw-r--r--storage/ndb/src/ndbapi/ObjectMap.hpp151
-rw-r--r--storage/ndb/src/ndbapi/ScanOperation.txt56
-rw-r--r--storage/ndb/src/ndbapi/TransporterFacade.cpp1108
-rw-r--r--storage/ndb/src/ndbapi/TransporterFacade.hpp363
-rw-r--r--storage/ndb/src/ndbapi/ndb_cluster_connection.cpp550
-rw-r--r--storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp82
-rw-r--r--storage/ndb/src/ndbapi/ndberror.c692
-rw-r--r--storage/ndb/src/ndbapi/signal-sender/Makefile19
-rw-r--r--storage/ndb/src/ndbapi/signal-sender/SignalSender.cpp237
-rw-r--r--storage/ndb/src/ndbapi/signal-sender/SignalSender.hpp82
-rw-r--r--storage/ndb/src/old_files/client/Makefile9
-rw-r--r--storage/ndb/src/old_files/client/odbc/Extra.mk59
-rw-r--r--storage/ndb/src/old_files/client/odbc/Makefile75
-rwxr-xr-xstorage/ndb/src/old_files/client/odbc/NdbOdbc.cpp78
-rwxr-xr-xstorage/ndb/src/old_files/client/odbc/NdbOdbc.def85
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/CodeGen.cpp229
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/CodeGen.hpp69
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_base.cpp167
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_base.hpp237
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_column.cpp72
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_column.hpp122
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.cpp485
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.hpp172
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.cpp124
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.hpp203
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.cpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.hpp99
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.cpp137
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.hpp178
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.cpp44
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.hpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.cpp37
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.hpp63
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.cpp104
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.hpp150
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.cpp51
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.hpp65
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.cpp54
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.hpp72
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete.cpp205
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete.hpp69
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.cpp164
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.hpp156
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.cpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.hpp152
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.cpp110
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.hpp130
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_dml.cpp51
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_dml.hpp67
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.hpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.cpp56
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.hpp76
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.cpp87
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.hpp136
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.cpp87
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.hpp124
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr.cpp79
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr.hpp219
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.cpp160
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.hpp120
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.cpp138
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.hpp120
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.cpp273
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.hpp141
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.cpp401
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.hpp193
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.cpp424
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.hpp166
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.cpp279
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.hpp136
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.cpp204
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.hpp272
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.hpp50
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_insert.cpp253
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_insert.hpp229
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_pred.cpp70
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_pred.hpp172
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.cpp188
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.hpp158
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query.cpp299
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query.hpp155
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.cpp177
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.hpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.cpp204
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.hpp165
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.cpp161
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.hpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.cpp301
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.hpp221
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.cpp186
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.hpp160
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.cpp192
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.hpp159
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.cpp184
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.hpp155
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.cpp184
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.hpp178
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.cpp211
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.hpp186
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.cpp109
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.hpp133
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.cpp177
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.hpp174
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.cpp239
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.hpp208
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.cpp130
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.hpp148
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_root.cpp307
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_root.hpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_select.cpp406
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_select.hpp132
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.cpp44
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.hpp76
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.hpp76
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_table.cpp254
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_table.hpp202
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.cpp53
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.hpp73
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update.cpp246
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update.hpp102
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.cpp196
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.hpp171
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.cpp194
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.hpp167
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.cpp146
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.hpp160
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/Makefile104
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/SimpleGram.ypp1649
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.cpp96
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.hpp161
-rw-r--r--storage/ndb/src/old_files/client/odbc/codegen/SimpleScan.lpp243
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/AttrArea.cpp91
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/AttrArea.hpp135
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/CodeTree.cpp37
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/CodeTree.hpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/ConnArea.cpp109
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/ConnArea.hpp135
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/Ctx.cpp355
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/Ctx.hpp182
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DataField.cpp3023
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DataField.hpp446
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DataRow.cpp140
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DataRow.hpp185
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DataType.cpp551
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DataType.hpp293
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DescArea.cpp167
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DescArea.hpp266
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DiagArea.cpp284
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/DiagArea.hpp196
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/Makefile29
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/OdbcData.cpp560
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/OdbcData.hpp283
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/ResultArea.cpp29
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/ResultArea.hpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/Sqlstate.cpp93
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/Sqlstate.hpp85
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/StmtArea.cpp112
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/StmtArea.hpp157
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/StmtInfo.cpp78
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/StmtInfo.hpp86
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/common.cpp17
-rw-r--r--storage/ndb/src/old_files/client/odbc/common/common.hpp124
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.cpp42
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.hpp64
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.cpp23
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.hpp143
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.cpp29
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.hpp108
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.cpp155
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.hpp89
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictSys.cpp433
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictSys.hpp77
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictTable.cpp91
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/DictTable.hpp192
-rw-r--r--storage/ndb/src/old_files/client/odbc/dictionary/Makefile20
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/class.fig332
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/descfield.pl1482
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/diag.txt48
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/getinfo.pl3676
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/gettypeinfo.pl645
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/handleattr.pl2232
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/main.hpp104
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/ndbodbc.html659
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/select.fig94
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/systables.pl2192
-rw-r--r--storage/ndb/src/old_files/client/odbc/docs/type.txt333
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/Func.data2822
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/Func.pl352
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/Makefile16
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLAllocConnect.cpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLAllocEnv.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandle.cpp62
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandleStd.cpp56
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLAllocStmt.cpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLBindCol.cpp50
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLBindParam.cpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLBindParameter.cpp54
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLBrowseConnect.cpp61
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLBulkOperations.cpp53
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLCancel.cpp45
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLCloseCursor.cpp45
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLColAttribute.cpp51
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLColAttributes.cpp51
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLColumnPrivileges.cpp67
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLColumns.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLConnect.cpp51
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLCopyDesc.cpp58
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLDataSources.cpp65
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLDescribeCol.cpp53
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLDescribeParam.cpp50
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLDisconnect.cpp45
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLDriverConnect.cpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLDrivers.cpp65
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLEndTran.cpp70
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLError.cpp67
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLExecDirect.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLExecute.cpp45
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLExtendedFetch.cpp59
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLFetch.cpp45
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLFetchScroll.cpp55
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLForeignKeys.cpp75
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLFreeConnect.cpp53
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLFreeEnv.cpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLFreeHandle.cpp60
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLFreeStmt.cpp54
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectAttr.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectOption.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetCursorName.cpp48
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetData.cpp50
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetDescField.cpp50
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetDescRec.cpp55
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagField.cpp50
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagRec.cpp51
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetEnvAttr.cpp66
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetFunctions.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetInfo.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtAttr.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtOption.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLGetTypeInfo.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLMoreResults.cpp42
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLNativeSql.cpp61
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLNumParams.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLNumResultCols.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLParamData.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLParamOptions.cpp55
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLPrepare.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLPrimaryKeys.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLProcedureColumns.cpp67
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLProcedures.cpp63
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLPutData.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLRowCount.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectAttr.cpp48
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectOption.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetCursorName.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetDescField.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetDescRec.cpp54
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetEnvAttr.cpp65
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetParam.cpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetPos.cpp57
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetScrollOptions.cpp57
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtAttr.cpp48
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtOption.cpp47
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLSpecialColumns.cpp69
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLStatistics.cpp67
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLTablePrivileges.cpp63
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLTables.cpp49
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/SQLTransact.cpp71
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/driver.cpp150
-rw-r--r--storage/ndb/src/old_files/client/odbc/driver/driver.hpp28
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_comp_op.cpp518
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_create_index.cpp46
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_create_table.cpp83
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_delete_index.cpp82
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_delete_lookup.cpp82
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_delete_scan.cpp54
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_drop_index.cpp38
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_drop_table.cpp38
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_expr_conv.cpp54
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_expr_func.cpp284
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_expr_op.cpp147
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_insert.cpp144
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_pred_op.cpp197
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_query_index.cpp136
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_query_lookup.cpp136
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_query_range.cpp143
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_query_scan.cpp130
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_query_sys.cpp761
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_update_index.cpp96
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_update_lookup.cpp96
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Exec_update_scan.cpp61
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Executor.cpp68
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Executor.hpp52
-rw-r--r--storage/ndb/src/old_files/client/odbc/executor/Makefile36
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/AttrDbc.cpp473
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/AttrEnv.cpp123
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/AttrRoot.cpp92
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/AttrStmt.cpp1005
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/DescSpec.cpp1140
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/FuncTab.cpp100
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleBase.cpp162
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleBase.hpp67
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleDbc.cpp419
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleDbc.hpp111
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleDesc.cpp254
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleDesc.hpp89
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleEnv.cpp144
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleEnv.hpp77
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleRoot.cpp271
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleRoot.hpp103
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp823
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/HandleStmt.hpp117
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/InfoTab.cpp878
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/Makefile28
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/PoolNdb.cpp81
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/PoolNdb.hpp44
-rw-r--r--storage/ndb/src/old_files/client/odbc/handles/handles.hpp28
-rw-r--r--storage/ndb/src/old_files/ndbbaseclient/Makefile29
-rw-r--r--storage/ndb/src/old_files/ndbbaseclient/ndbbaseclient_dummy.cpp0
-rw-r--r--storage/ndb/src/old_files/ndbclient/Makefile37
-rw-r--r--storage/ndb/src/old_files/ndbclient/ndbclient_dummy.cpp0
-rw-r--r--storage/ndb/src/old_files/newtonapi/Makefile27
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_binding.cpp439
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_bulkread.cpp267
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_config.cpp115
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_dac.cpp842
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_error.cpp118
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_init.cpp86
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_internal.hpp122
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_process.cpp123
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_process.hpp56
-rw-r--r--storage/ndb/src/old_files/newtonapi/dba_schema.cpp150
-rw-r--r--storage/ndb/src/old_files/rep/ExtSender.cpp149
-rw-r--r--storage/ndb/src/old_files/rep/ExtSender.hpp76
-rw-r--r--storage/ndb/src/old_files/rep/Makefile28
-rw-r--r--storage/ndb/src/old_files/rep/NodeConnectInfo.hpp29
-rw-r--r--storage/ndb/src/old_files/rep/README147
-rw-r--r--storage/ndb/src/old_files/rep/RepApiInterpreter.cpp80
-rw-r--r--storage/ndb/src/old_files/rep/RepApiInterpreter.hpp54
-rw-r--r--storage/ndb/src/old_files/rep/RepApiService.cpp318
-rw-r--r--storage/ndb/src/old_files/rep/RepApiService.hpp59
-rw-r--r--storage/ndb/src/old_files/rep/RepCommandInterpreter.cpp456
-rw-r--r--storage/ndb/src/old_files/rep/RepCommandInterpreter.hpp45
-rw-r--r--storage/ndb/src/old_files/rep/RepComponents.cpp138
-rw-r--r--storage/ndb/src/old_files/rep/RepComponents.hpp60
-rw-r--r--storage/ndb/src/old_files/rep/RepMain.cpp97
-rw-r--r--storage/ndb/src/old_files/rep/Requestor.cpp224
-rw-r--r--storage/ndb/src/old_files/rep/Requestor.hpp154
-rw-r--r--storage/ndb/src/old_files/rep/RequestorSubscriptions.cpp60
-rw-r--r--storage/ndb/src/old_files/rep/SignalQueue.cpp106
-rw-r--r--storage/ndb/src/old_files/rep/SignalQueue.hpp117
-rw-r--r--storage/ndb/src/old_files/rep/TODO119
-rw-r--r--storage/ndb/src/old_files/rep/adapters/AppNDB.cpp583
-rw-r--r--storage/ndb/src/old_files/rep/adapters/AppNDB.hpp141
-rw-r--r--storage/ndb/src/old_files/rep/adapters/ExtAPI.cpp31
-rw-r--r--storage/ndb/src/old_files/rep/adapters/ExtAPI.hpp107
-rw-r--r--storage/ndb/src/old_files/rep/adapters/ExtNDB.cpp559
-rw-r--r--storage/ndb/src/old_files/rep/adapters/ExtNDB.hpp118
-rw-r--r--storage/ndb/src/old_files/rep/adapters/Makefile11
-rw-r--r--storage/ndb/src/old_files/rep/adapters/TableInfoPs.hpp118
-rw-r--r--storage/ndb/src/old_files/rep/dbug_hack.cpp75
-rw-r--r--storage/ndb/src/old_files/rep/rep_version.hpp88
-rw-r--r--storage/ndb/src/old_files/rep/repapi/Makefile25
-rw-r--r--storage/ndb/src/old_files/rep/repapi/repapi.cpp598
-rw-r--r--storage/ndb/src/old_files/rep/repapi/repapi.h216
-rw-r--r--storage/ndb/src/old_files/rep/state/Channel.cpp487
-rw-r--r--storage/ndb/src/old_files/rep/state/Channel.hpp206
-rw-r--r--storage/ndb/src/old_files/rep/state/Interval.cpp171
-rw-r--r--storage/ndb/src/old_files/rep/state/Interval.hpp107
-rw-r--r--storage/ndb/src/old_files/rep/state/Makefile17
-rw-r--r--storage/ndb/src/old_files/rep/state/RepState.cpp869
-rw-r--r--storage/ndb/src/old_files/rep/state/RepState.hpp276
-rw-r--r--storage/ndb/src/old_files/rep/state/RepStateEvent.cpp284
-rw-r--r--storage/ndb/src/old_files/rep/state/RepStateRequests.cpp294
-rw-r--r--storage/ndb/src/old_files/rep/state/testInterval/Makefile12
-rw-r--r--storage/ndb/src/old_files/rep/state/testInterval/testInterval.cpp127
-rw-r--r--storage/ndb/src/old_files/rep/state/testRepState/Makefile15
-rw-r--r--storage/ndb/src/old_files/rep/state/testRepState/testRequestor.cpp166
-rw-r--r--storage/ndb/src/old_files/rep/state/testRepState/testRequestor.hpp24
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIBuffer.cpp173
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIBuffer.hpp112
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIContainer.cpp272
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIContainer.hpp121
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIContainerPS.cpp128
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIContainerPS.hpp90
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIPage.cpp165
-rw-r--r--storage/ndb/src/old_files/rep/storage/GCIPage.hpp114
-rw-r--r--storage/ndb/src/old_files/rep/storage/LogRecord.hpp81
-rw-r--r--storage/ndb/src/old_files/rep/storage/Makefile14
-rw-r--r--storage/ndb/src/old_files/rep/storage/NodeConnectInfo.hpp29
-rw-r--r--storage/ndb/src/old_files/rep/storage/NodeGroup.cpp149
-rw-r--r--storage/ndb/src/old_files/rep/storage/NodeGroup.hpp109
-rw-r--r--storage/ndb/src/old_files/rep/storage/NodeGroupInfo.cpp218
-rw-r--r--storage/ndb/src/old_files/rep/storage/NodeGroupInfo.hpp145
-rw-r--r--storage/ndb/src/old_files/rep/transfer/Makefile11
-rw-r--r--storage/ndb/src/old_files/rep/transfer/TransPS.cpp553
-rw-r--r--storage/ndb/src/old_files/rep/transfer/TransPS.hpp136
-rw-r--r--storage/ndb/src/old_files/rep/transfer/TransSS.cpp653
-rw-r--r--storage/ndb/src/old_files/rep/transfer/TransSS.hpp145
-rw-r--r--storage/ndb/src/old_files/rep/transfer/TransSSSubscriptions.cpp193
1029 files changed, 326365 insertions, 0 deletions
diff --git a/storage/ndb/src/Makefile.am b/storage/ndb/src/Makefile.am
new file mode 100644
index 00000000000..d35790a2e43
--- /dev/null
+++ b/storage/ndb/src/Makefile.am
@@ -0,0 +1,33 @@
+SUBDIRS = common mgmapi ndbapi . kernel mgmclient mgmsrv cw
+
+include $(top_srcdir)/ndb/config/common.mk.am
+
+ndblib_LTLIBRARIES = libndbclient.la
+
+libndbclient_la_SOURCES =
+
+libndbclient_la_LIBADD = \
+ ndbapi/libndbapi.la \
+ common/transporter/libtransporter.la \
+ common/debugger/libtrace.la \
+ common/debugger/signaldata/libsignaldataprint.la \
+ mgmapi/libmgmapi.la \
+ common/mgmcommon/libmgmsrvcommon.la \
+ common/logger/liblogger.la \
+ common/portlib/libportlib.la \
+ common/util/libgeneral.la
+
+windoze-dsp: libndbclient.dsp
+
+libndbclient.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(ndblib_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ dummy.cpp
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(libndbclient_la_LIBADD)
+ @touch dummy.cpp
diff --git a/storage/ndb/src/common/Makefile.am b/storage/ndb/src/common/Makefile.am
new file mode 100644
index 00000000000..0059f3fb210
--- /dev/null
+++ b/storage/ndb/src/common/Makefile.am
@@ -0,0 +1,15 @@
+SUBDIRS = portlib debugger util logger transporter mgmcommon
+
+noinst_LTLIBRARIES = libcommon.la
+
+libcommon_la_SOURCES =
+libcommon_la_LIBADD = \
+ transporter/libtransporter.la \
+ debugger/libtrace.la \
+ debugger/signaldata/libsignaldataprint.la \
+ mgmcommon/libmgmsrvcommon.la \
+ portlib/libportlib.la \
+ logger/liblogger.la \
+ util/libgeneral.la
+
+windoze-dsp:
diff --git a/storage/ndb/src/common/debugger/BlockNames.cpp b/storage/ndb/src/common/debugger/BlockNames.cpp
new file mode 100644
index 00000000000..44650b84c5c
--- /dev/null
+++ b/storage/ndb/src/common/debugger/BlockNames.cpp
@@ -0,0 +1,39 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <BlockNumbers.h>
+
+const BlockName BlockNames[] = {
+ { "CMVMI", CMVMI },
+ { "DBACC", DBACC },
+ { "DBDICT", DBDICT },
+ { "DBDIH", DBDIH },
+ { "DBLQH", DBLQH },
+ { "DBTC", DBTC },
+ { "DBTUP", DBTUP },
+ { "NDBFS", NDBFS },
+ { "NDBCNTR", NDBCNTR },
+ { "QMGR", QMGR },
+ { "TRIX", TRIX },
+ { "BACKUP", BACKUP },
+ { "DBUTIL", DBUTIL },
+ { "SUMA", SUMA },
+ { "GREP", GREP },
+ { "DBTUX", DBTUX }
+};
+
+const BlockNumber NO_OF_BLOCK_NAMES = sizeof(BlockNames) / sizeof(BlockName);
diff --git a/storage/ndb/src/common/debugger/DebuggerNames.cpp b/storage/ndb/src/common/debugger/DebuggerNames.cpp
new file mode 100644
index 00000000000..8571b8ece86
--- /dev/null
+++ b/storage/ndb/src/common/debugger/DebuggerNames.cpp
@@ -0,0 +1,154 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <BaseString.hpp>
+
+#include "DebuggerNames.hpp"
+
+#include <BlockNumbers.h>
+#include <GlobalSignalNumbers.h>
+#include <signaldata/SignalDataPrint.hpp>
+
+static const char * localSignalNames[MAX_GSN+1];
+static SignalDataPrintFunction localPrintFunctions[MAX_GSN+1];
+static const char * localBlockNames[NO_OF_BLOCKS];
+
+static
+int
+initSignalNames(const char * dst[], const GsnName src[], unsigned short len){
+ unsigned i;
+ for(i = 0; i<=MAX_GSN; i++)
+ dst[i] = 0;
+
+ for(i = 0; i<len; i++){
+ unsigned short gsn = src[i].gsn;
+ const char * name = src[i].name;
+
+ if(dst[gsn] != 0 && name != 0){
+ if(strcmp(dst[gsn], name) != 0){
+ fprintf(stderr,
+ "Multiple definition of signal name for gsn: %d (%s, %s)\n",
+ gsn, dst[gsn], name);
+ exit(0);
+ }
+ }
+ dst[gsn] = name;
+ }
+ return 0;
+}
+
+static
+int
+initSignalPrinters(SignalDataPrintFunction dst[],
+ const NameFunctionPair src[]){
+ unsigned i;
+ for(i = 0; i<=MAX_GSN; i++)
+ dst[i] = 0;
+
+ unsigned short gsn;
+ for(i = 0; (gsn = src[i].gsn) > 0; i++){
+ SignalDataPrintFunction fun = src[i].function;
+
+ if(dst[gsn] != 0 && fun != 0){
+ if(dst[gsn] != fun){
+ fprintf(stderr,
+ "Multiple definition of signal print function for gsn: %d\n",
+ gsn);
+ exit(0);
+ }
+ }
+ dst[gsn] = fun;
+ }
+ return 0;
+}
+
+static
+int
+initBlockNames(const char * dst[],
+ const BlockName src[],
+ unsigned len){
+ unsigned i;
+ for(i = 0; i<NO_OF_BLOCKS; i++)
+ dst[i] = 0;
+
+ for(i = 0; i<len; i++){
+ const int index = src[i].number - MIN_BLOCK_NO;
+ if(index < 0 && index >= NO_OF_BLOCKS || dst[index] != 0){
+ fprintf(stderr,
+ "Invalid block name definition: %d %s\n",
+ src[i].number, src[i].name);
+ exit(0);
+ }
+ dst[index] = src[i].name;
+ }
+ return 0;
+}
+
+/**
+ * Run static initializer
+ */
+static const int
+xxx_DUMMY_SIGNAL_NAMES_xxx = initSignalNames(localSignalNames,
+ SignalNames,
+ NO_OF_SIGNAL_NAMES);
+static const int
+xxx_DUMMY_PRINT_FUNCTIONS_xxx = initSignalPrinters(localPrintFunctions,
+ SignalDataPrintFunctions);
+
+static const int
+xxx_DUMMY_BLOCK_NAMES_xxx = initBlockNames(localBlockNames,
+ BlockNames,
+ NO_OF_BLOCK_NAMES);
+
+const char *
+getSignalName(unsigned short gsn, const char * defVal){
+ if(gsn > 0 && gsn <= MAX_GSN)
+ return (localSignalNames[gsn] ? localSignalNames[gsn] : defVal);
+ return defVal;
+}
+
+unsigned short
+getGsn(const char * signalName){
+ return 0;
+}
+
+const char *
+getBlockName(unsigned short blockNo, const char * ret){
+ if(blockNo >= MIN_BLOCK_NO && blockNo <= MAX_BLOCK_NO)
+ return localBlockNames[blockNo-MIN_BLOCK_NO];
+ if (ret == 0) {
+ static char buf[20];
+ BaseString::snprintf(buf, sizeof(buf), "BLOCK#%d", (int)blockNo);
+ return buf;
+ }
+ return ret;
+}
+
+unsigned short
+getBlockNo(const char * blockName){
+ for(int i = 0; i<NO_OF_BLOCKS; i++)
+ if(localBlockNames[i] != 0 && strcmp(localBlockNames[i], blockName) == 0)
+ return i + MIN_BLOCK_NO;
+ return 0;
+}
+
+SignalDataPrintFunction
+findPrintFunction(unsigned short gsn){
+ if(gsn > 0 && gsn <= MAX_GSN)
+ return localPrintFunctions[gsn];
+ return 0;
+}
diff --git a/storage/ndb/src/common/debugger/EventLogger.cpp b/storage/ndb/src/common/debugger/EventLogger.cpp
new file mode 100644
index 00000000000..5a534b36b59
--- /dev/null
+++ b/storage/ndb/src/common/debugger/EventLogger.cpp
@@ -0,0 +1,861 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "EventLogger.hpp"
+
+#include <NdbConfig.h>
+#include <kernel/BlockNumbers.h>
+#include <signaldata/ArbitSignalData.hpp>
+#include <GrepEvent.hpp>
+#include <NodeState.hpp>
+#include <version.h>
+
+//
+// PUBLIC
+//
+EventLoggerBase::~EventLoggerBase()
+{
+
+}
+
+
+#define QQQQ char *m_text, size_t m_text_len, const Uint32* theData
+
+void getTextConnected(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %u Connected",
+ theData[1]);
+}
+void getTextConnectedApiVersion(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %u: API version %d.%d.%d",
+ theData[1],
+ getMajor(theData[2]),
+ getMinor(theData[2]),
+ getBuild(theData[2]));
+}
+void getTextDisconnected(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %u Disconnected",
+ theData[1]);
+}
+void getTextCommunicationClosed(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT communication to node closed.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Communication to Node %u closed",
+ theData[1]);
+}
+void getTextCommunicationOpened(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT communication to node opened.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Communication to Node %u opened",
+ theData[1]);
+}
+void getTextNDBStartStarted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // Start of NDB has been initiated.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Start initiated (version %d.%d.%d)",
+ getMajor(theData[1]),
+ getMinor(theData[1]),
+ getBuild(theData[1]));
+}
+void getTextNDBStopStarted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "%s shutdown initiated",
+ (theData[1] == 1 ? "Cluster" : "Node"));
+}
+void getTextNDBStopAborted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node shutdown aborted");
+}
+void getTextNDBStartCompleted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // Start of NDB has been completed.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Started (version %d.%d.%d)",
+ getMajor(theData[1]),
+ getMinor(theData[1]),
+ getBuild(theData[1]));
+}
+void getTextSTTORRYRecieved(QQQQ) {
+ //-----------------------------------------------------------------------
+ // STTORRY recevied after restart finished.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "STTORRY received after restart finished");
+}
+void getTextStartPhaseCompleted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Start phase completed.
+ //-----------------------------------------------------------------------
+ const char *type = "<Unknown>";
+ switch((NodeState::StartType)theData[2]){
+ case NodeState::ST_INITIAL_START:
+ type = "(initial start)";
+ break;
+ case NodeState::ST_SYSTEM_RESTART:
+ type = "(system restart)";
+ break;
+ case NodeState::ST_NODE_RESTART:
+ type = "(node restart)";
+ break;
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ type = "(initial node restart)";
+ break;
+ case NodeState::ST_ILLEGAL_TYPE:
+ type = "";
+ break;
+ default:
+ BaseString::snprintf(m_text, m_text_len,
+ "Start phase %u completed (unknown = %d)",
+ theData[1],
+ theData[2]);
+ return;
+ }
+ BaseString::snprintf(m_text, m_text_len,
+ "Start phase %u completed %s",
+ theData[1],
+ type);
+}
+void getTextCM_REGCONF(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "CM_REGCONF president = %u, own Node = %u, our dynamic id = %u",
+ theData[2],
+ theData[1],
+ theData[3]);
+}
+void getTextCM_REGREF(QQQQ) {
+ const char* line = "";
+ switch (theData[3]) {
+ case 0:
+ line = "Busy";
+ break;
+ case 1:
+ line = "Election with wait = false";
+ break;
+ case 2:
+ line = "Election with wait = false";
+ break;
+ case 3:
+ line = "Not president";
+ break;
+ case 4:
+ line = "Election without selecting new candidate";
+ break;
+ default:
+ line = "No such cause";
+ break;
+ }//switch
+
+ BaseString::snprintf(m_text, m_text_len,
+ "CM_REGREF from Node %u to our Node %u. Cause = %s",
+ theData[2],
+ theData[1],
+ line);
+}
+void getTextFIND_NEIGHBOURS(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Node Restart copied a fragment.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "We are Node %u with dynamic ID %u, our left neighbour "
+ "is Node %u, our right is Node %u",
+ theData[1],
+ theData[4],
+ theData[2],
+ theData[3]);
+}
+void getTextNodeFailCompleted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Node failure phase completed.
+ //-----------------------------------------------------------------------
+ if (theData[1] == 0)
+ {
+ if (theData[3] != 0) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %u completed failure of Node %u",
+ theData[3],
+ theData[2]);
+ } else {
+ BaseString::snprintf(m_text, m_text_len,
+ "All nodes completed failure of Node %u",
+ theData[2]);
+ }//if
+ } else {
+ const char* line = "";
+ if (theData[1] == DBTC){
+ line = "DBTC";
+ }else if (theData[1] == DBDICT){
+ line = "DBDICT";
+ }else if (theData[1] == DBDIH){
+ line = "DBDIH";
+ }else if (theData[1] == DBLQH){
+ line = "DBLQH";
+ }
+ BaseString::snprintf(m_text, m_text_len,
+ "Node failure of %u %s completed",
+ theData[2],
+ line);
+ }
+}
+void getTextNODE_FAILREP(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %u has failed. The Node state at failure "
+ "was %u",
+ theData[1],
+ theData[2]);
+}
+void getTextArbitState(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT arbitrator found or lost.
+ //-----------------------------------------------------------------------
+ {
+ const ArbitSignalData* sd = (ArbitSignalData*)theData;
+ char ticketText[ArbitTicket::TextLength + 1];
+ char errText[ArbitCode::ErrTextLength + 1];
+ const unsigned code = sd->code & 0xFFFF;
+ const unsigned state = sd->code >> 16;
+ switch (code) {
+ case ArbitCode::ThreadStart:
+ BaseString::snprintf(m_text, m_text_len,
+ "President restarts arbitration thread [state=%u]",
+ state);
+ break;
+ case ArbitCode::PrepPart2:
+ sd->ticket.getText(ticketText, sizeof(ticketText));
+ BaseString::snprintf(m_text, m_text_len,
+ "Prepare arbitrator node %u [ticket=%s]",
+ sd->node, ticketText);
+ break;
+ case ArbitCode::PrepAtrun:
+ sd->ticket.getText(ticketText, sizeof(ticketText));
+ BaseString::snprintf(m_text, m_text_len,
+ "Receive arbitrator node %u [ticket=%s]",
+ sd->node, ticketText);
+ break;
+ case ArbitCode::ApiStart:
+ sd->ticket.getText(ticketText, sizeof(ticketText));
+ BaseString::snprintf(m_text, m_text_len,
+ "Started arbitrator node %u [ticket=%s]",
+ sd->node, ticketText);
+ break;
+ case ArbitCode::ApiFail:
+ BaseString::snprintf(m_text, m_text_len,
+ "Lost arbitrator node %u - process failure [state=%u]",
+ sd->node, state);
+ break;
+ case ArbitCode::ApiExit:
+ BaseString::snprintf(m_text, m_text_len,
+ "Lost arbitrator node %u - process exit [state=%u]",
+ sd->node, state);
+ break;
+ default:
+ ArbitCode::getErrText(code, errText, sizeof(errText));
+ BaseString::snprintf(m_text, m_text_len,
+ "Lost arbitrator node %u - %s [state=%u]",
+ sd->node, errText, state);
+ break;
+ }
+ }
+}
+
+void getTextArbitResult(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT arbitration result (the failures may not reach us).
+ //-----------------------------------------------------------------------
+ {
+ const ArbitSignalData* sd = (ArbitSignalData*)theData;
+ char errText[ArbitCode::ErrTextLength + 1];
+ const unsigned code = sd->code & 0xFFFF;
+ const unsigned state = sd->code >> 16;
+ switch (code) {
+ case ArbitCode::LoseNodes:
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration check lost - less than 1/2 nodes left");
+ break;
+ case ArbitCode::WinNodes:
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration check won - all node groups and more than 1/2 nodes left");
+ break;
+ case ArbitCode::WinGroups:
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration check won - node group majority");
+ break;
+ case ArbitCode::LoseGroups:
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration check lost - missing node group");
+ break;
+ case ArbitCode::Partitioning:
+ BaseString::snprintf(m_text, m_text_len,
+ "Network partitioning - arbitration required");
+ break;
+ case ArbitCode::WinChoose:
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration won - positive reply from node %u",
+ sd->node);
+ break;
+ case ArbitCode::LoseChoose:
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration lost - negative reply from node %u",
+ sd->node);
+ break;
+ case ArbitCode::LoseNorun:
+ BaseString::snprintf(m_text, m_text_len,
+ "Network partitioning - no arbitrator available");
+ break;
+ case ArbitCode::LoseNocfg:
+ BaseString::snprintf(m_text, m_text_len,
+ "Network partitioning - no arbitrator configured");
+ break;
+ default:
+ ArbitCode::getErrText(code, errText, sizeof(errText));
+ BaseString::snprintf(m_text, m_text_len,
+ "Arbitration failure - %s [state=%u]",
+ errText, state);
+ break;
+ }
+ }
+}
+void getTextGlobalCheckpointStarted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // This event reports that a global checkpoint has been started and this
+ // node is the master of this global checkpoint.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Global checkpoint %u started",
+ theData[1]);
+}
+void getTextGlobalCheckpointCompleted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // This event reports that a global checkpoint has been completed on this
+ // node and the node is the master of this global checkpoint.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Global checkpoint %u completed",
+ theData[1]);
+}
+void getTextLocalCheckpointStarted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // This event reports that a local checkpoint has been started and this
+ // node is the master of this local checkpoint.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Local checkpoint %u started. "
+ "Keep GCI = %u oldest restorable GCI = %u",
+ theData[1],
+ theData[2],
+ theData[3]);
+}
+void getTextLocalCheckpointCompleted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // This event reports that a local checkpoint has been completed on this
+ // node and the node is the master of this local checkpoint.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Local checkpoint %u completed",
+ theData[1]);
+}
+void getTextTableCreated(QQQQ) {
+ //-----------------------------------------------------------------------
+ // This event reports that a table has been created.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Table with ID = %u created",
+ theData[1]);
+}
+/* STRANGE */
+void getTextLCPStoppedInCalcKeepGci(QQQQ) {
+ if (theData[1] == 0)
+ BaseString::snprintf(m_text, m_text_len,
+ "Local Checkpoint stopped in CALCULATED_KEEP_GCI");
+}
+void getTextNR_CopyDict(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Node Restart completed copy of dictionary information.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Node restart completed copy of dictionary information");
+}
+void getTextNR_CopyDistr(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Node Restart completed copy of distribution information.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Node restart completed copy of distribution information");
+}
+void getTextNR_CopyFragsStarted(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Node Restart is starting to copy the fragments.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Node restart starting to copy the fragments "
+ "to Node %u",
+ theData[1]);
+}
+void getTextNR_CopyFragDone(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Node Restart copied a fragment.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Table ID = %u, fragment ID = %u have been copied "
+ "to Node %u",
+ theData[2],
+ theData[3],
+ theData[1]);
+}
+void getTextNR_CopyFragsCompleted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node restart completed copying the fragments "
+ "to Node %u",
+ theData[1]);
+}
+void getTextLCPFragmentCompleted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Table ID = %u, fragment ID = %u has completed LCP "
+ "on Node %u",
+ theData[2],
+ theData[3],
+ theData[1]);
+}
+void getTextTransReportCounters(QQQQ) {
+ // -------------------------------------------------------------------
+ // Report information about transaction activity once per 10 seconds.
+ // -------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Trans. Count = %u, Commit Count = %u, "
+ "Read Count = %u, Simple Read Count = %u,\n"
+ "Write Count = %u, AttrInfo Count = %u, "
+ "Concurrent Operations = %u, Abort Count = %u\n"
+ " Scans: %u Range scans: %u",
+ theData[1],
+ theData[2],
+ theData[3],
+ theData[4],
+ theData[5],
+ theData[6],
+ theData[7],
+ theData[8],
+ theData[9],
+ theData[10]);
+}
+void getTextOperationReportCounters(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Operations=%u",
+ theData[1]);
+}
+void getTextUndoLogBlocked(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Undo Logging blocked due to buffer near to overflow.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "ACC Blocked %u and TUP Blocked %u times last second",
+ theData[1],
+ theData[2]);
+}
+void getTextTransporterError(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Transporter to node %d reported error 0x%x",
+ theData[1],
+ theData[2]);
+}
+void getTextTransporterWarning(QQQQ) {
+ getTextTransporterError(m_text, m_text_len, theData);
+}
+void getTextMissedHeartbeat(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Undo Logging blocked due to buffer near to overflow.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %d missed heartbeat %d",
+ theData[1],
+ theData[2]);
+}
+void getTextDeadDueToHeartbeat(QQQQ) {
+ //-----------------------------------------------------------------------
+ // REPORT Undo Logging blocked due to buffer near to overflow.
+ //-----------------------------------------------------------------------
+ BaseString::snprintf(m_text, m_text_len,
+ "Node %d declared dead due to missed heartbeat",
+ theData[1]);
+}
+void getTextJobStatistic(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Mean loop Counter in doJob last 8192 times = %u",
+ theData[1]);
+}
+void getTextSendBytesStatistic(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Mean send size to Node = %d last 4096 sends = %u bytes",
+ theData[1],
+ theData[2]);
+}
+void getTextReceiveBytesStatistic(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Mean receive size to Node = %d last 4096 sends = %u bytes",
+ theData[1],
+ theData[2]);
+}
+void getTextSentHeartbeat(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node Sent Heartbeat to node = %d",
+ theData[1]);
+}
+void getTextCreateLogBytes(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Log part %u, log file %u, MB %u",
+ theData[1],
+ theData[2],
+ theData[3]);
+}
+void getTextStartLog(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Log part %u, start MB %u, stop MB %u, last GCI, log exec %u",
+ theData[1],
+ theData[2],
+ theData[3],
+ theData[4]);
+}
+void getTextStartREDOLog(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Node: %d StartLog: [GCI Keep: %d LastCompleted: %d NewestRestorable: %d]",
+ theData[1],
+ theData[2],
+ theData[3],
+ theData[4]);
+}
+void getTextUNDORecordsExecuted(QQQQ) {
+ const char* line = "";
+ if (theData[1] == DBTUP){
+ line = "DBTUP";
+ }else if (theData[1] == DBACC){
+ line = "DBACC";
+ }
+
+ BaseString::snprintf(m_text, m_text_len,
+ " UNDO %s %d [%d %d %d %d %d %d %d %d %d]",
+ line,
+ theData[2],
+ theData[3],
+ theData[4],
+ theData[5],
+ theData[6],
+ theData[7],
+ theData[8],
+ theData[9],
+ theData[10],
+ theData[11]);
+}
+void getTextInfoEvent(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len, (char *)&theData[1]);
+}
+void getTextWarningEvent(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len, (char *)&theData[1]);
+}
+void getTextGCP_TakeoverStarted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len, "GCP Take over started");
+}
+void getTextGCP_TakeoverCompleted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len, "GCP Take over completed");
+}
+void getTextLCP_TakeoverStarted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len, "LCP Take over started");
+}
+void getTextLCP_TakeoverCompleted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "LCP Take over completed (state = %d)",
+ theData[1]);
+}
+void getTextMemoryUsage(QQQQ) {
+ const int gth = theData[1];
+ const int size = theData[2];
+ const int used = theData[3];
+ const int total = theData[4];
+ const int block = theData[5];
+ const int percent = (used*100)/total;
+
+ BaseString::snprintf(m_text, m_text_len,
+ "%s usage %s %d%s(%d %dK pages of total %d)",
+ (block==DBACC ? "Index" : (block == DBTUP ?"Data":"<unknown>")),
+ (gth == 0 ? "is" : (gth > 0 ? "increased to" : "decreased to")),
+ percent, "%",
+ used, size/1024, total
+ );
+}
+
+void getTextBackupStarted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Backup %d started from node %d",
+ theData[2], refToNode(theData[1]));
+}
+void getTextBackupFailedToStart(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Backup request from %d failed to start. Error: %d",
+ refToNode(theData[1]), theData[2]);
+}
+void getTextBackupCompleted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Backup %u started from node %u completed\n"
+ " StartGCP: %u StopGCP: %u\n"
+ " #Records: %u #LogRecords: %u\n"
+ " Data: %u bytes Log: %u bytes",
+ theData[2], refToNode(theData[1]),
+ theData[3], theData[4], theData[6], theData[8],
+ theData[5], theData[7]);
+}
+void getTextBackupAborted(QQQQ) {
+ BaseString::snprintf(m_text, m_text_len,
+ "Backup %d started from %d has been aborted. Error: %d",
+ theData[2],
+ refToNode(theData[1]),
+ theData[3]);
+}
+
+#if 0
+BaseString::snprintf(m_text,
+ m_text_len,
+ "Unknown event: %d",
+ theData[0]);
+#endif
+
+/**
+ * This matrix defines which event should be printed when
+ *
+ * threshold - is in range [0-15]
+ * severity - DEBUG to ALERT (Type of log message)
+ */
+
+#define ROW(a,b,c,d) \
+{ NDB_LE_ ## a, b, c, d, getText ## a}
+
+const EventLoggerBase::EventRepLogLevelMatrix EventLoggerBase::matrix[] = {
+ // CONNECTION
+ ROW(Connected, LogLevel::llConnection, 8, Logger::LL_INFO ),
+ ROW(Disconnected, LogLevel::llConnection, 8, Logger::LL_ALERT ),
+ ROW(CommunicationClosed, LogLevel::llConnection, 8, Logger::LL_INFO ),
+ ROW(CommunicationOpened, LogLevel::llConnection, 8, Logger::LL_INFO ),
+ ROW(ConnectedApiVersion, LogLevel::llConnection, 8, Logger::LL_INFO ),
+ // CHECKPOINT
+ ROW(GlobalCheckpointStarted, LogLevel::llCheckpoint, 9, Logger::LL_INFO ),
+ ROW(GlobalCheckpointCompleted,LogLevel::llCheckpoint,10, Logger::LL_INFO ),
+ ROW(LocalCheckpointStarted, LogLevel::llCheckpoint, 7, Logger::LL_INFO ),
+ ROW(LocalCheckpointCompleted,LogLevel::llCheckpoint, 8, Logger::LL_INFO ),
+ ROW(LCPStoppedInCalcKeepGci, LogLevel::llCheckpoint, 0, Logger::LL_ALERT ),
+ ROW(LCPFragmentCompleted, LogLevel::llCheckpoint, 11, Logger::LL_INFO ),
+ ROW(UndoLogBlocked, LogLevel::llCheckpoint, 7, Logger::LL_INFO ),
+
+ // STARTUP
+ ROW(NDBStartStarted, LogLevel::llStartUp, 1, Logger::LL_INFO ),
+ ROW(NDBStartCompleted, LogLevel::llStartUp, 1, Logger::LL_INFO ),
+ ROW(STTORRYRecieved, LogLevel::llStartUp, 15, Logger::LL_INFO ),
+ ROW(StartPhaseCompleted, LogLevel::llStartUp, 4, Logger::LL_INFO ),
+ ROW(CM_REGCONF, LogLevel::llStartUp, 3, Logger::LL_INFO ),
+ ROW(CM_REGREF, LogLevel::llStartUp, 8, Logger::LL_INFO ),
+ ROW(FIND_NEIGHBOURS, LogLevel::llStartUp, 8, Logger::LL_INFO ),
+ ROW(NDBStopStarted, LogLevel::llStartUp, 1, Logger::LL_INFO ),
+ ROW(NDBStopAborted, LogLevel::llStartUp, 1, Logger::LL_INFO ),
+ ROW(StartREDOLog, LogLevel::llStartUp, 10, Logger::LL_INFO ),
+ ROW(StartLog, LogLevel::llStartUp, 10, Logger::LL_INFO ),
+ ROW(UNDORecordsExecuted, LogLevel::llStartUp, 15, Logger::LL_INFO ),
+
+ // NODERESTART
+ ROW(NR_CopyDict, LogLevel::llNodeRestart, 8, Logger::LL_INFO ),
+ ROW(NR_CopyDistr, LogLevel::llNodeRestart, 8, Logger::LL_INFO ),
+ ROW(NR_CopyFragsStarted, LogLevel::llNodeRestart, 8, Logger::LL_INFO ),
+ ROW(NR_CopyFragDone, LogLevel::llNodeRestart,10, Logger::LL_INFO ),
+ ROW(NR_CopyFragsCompleted, LogLevel::llNodeRestart, 8, Logger::LL_INFO ),
+
+ ROW(NodeFailCompleted, LogLevel::llNodeRestart, 8, Logger::LL_ALERT),
+ ROW(NODE_FAILREP, LogLevel::llNodeRestart, 8, Logger::LL_ALERT),
+ ROW(ArbitState, LogLevel::llNodeRestart, 6, Logger::LL_INFO ),
+ ROW(ArbitResult, LogLevel::llNodeRestart, 2, Logger::LL_ALERT),
+ ROW(GCP_TakeoverStarted, LogLevel::llNodeRestart, 7, Logger::LL_INFO ),
+ ROW(GCP_TakeoverCompleted, LogLevel::llNodeRestart, 7, Logger::LL_INFO ),
+ ROW(LCP_TakeoverStarted, LogLevel::llNodeRestart, 7, Logger::LL_INFO ),
+ ROW(LCP_TakeoverCompleted, LogLevel::llNodeRestart, 7, Logger::LL_INFO ),
+
+ // STATISTIC
+ ROW(TransReportCounters, LogLevel::llStatistic, 8, Logger::LL_INFO ),
+ ROW(OperationReportCounters, LogLevel::llStatistic, 8, Logger::LL_INFO ),
+ ROW(TableCreated, LogLevel::llStatistic, 7, Logger::LL_INFO ),
+ ROW(JobStatistic, LogLevel::llStatistic, 9, Logger::LL_INFO ),
+ ROW(SendBytesStatistic, LogLevel::llStatistic, 9, Logger::LL_INFO ),
+ ROW(ReceiveBytesStatistic, LogLevel::llStatistic, 9, Logger::LL_INFO ),
+ ROW(MemoryUsage, LogLevel::llStatistic, 5, Logger::LL_INFO ),
+
+ // ERROR
+ ROW(TransporterError, LogLevel::llError, 2, Logger::LL_ERROR ),
+ ROW(TransporterWarning, LogLevel::llError, 8, Logger::LL_WARNING ),
+ ROW(MissedHeartbeat, LogLevel::llError, 8, Logger::LL_WARNING ),
+ ROW(DeadDueToHeartbeat, LogLevel::llError, 8, Logger::LL_ALERT ),
+ ROW(WarningEvent, LogLevel::llError, 2, Logger::LL_WARNING ),
+ // INFO
+ ROW(SentHeartbeat, LogLevel::llInfo, 12, Logger::LL_INFO ),
+ ROW(CreateLogBytes, LogLevel::llInfo, 11, Logger::LL_INFO ),
+ ROW(InfoEvent, LogLevel::llInfo, 2, Logger::LL_INFO ),
+
+ // Backup
+ ROW(BackupStarted, LogLevel::llBackup, 7, Logger::LL_INFO ),
+ ROW(BackupCompleted, LogLevel::llBackup, 7, Logger::LL_INFO ),
+ ROW(BackupFailedToStart, LogLevel::llBackup, 7, Logger::LL_ALERT),
+ ROW(BackupAborted, LogLevel::llBackup, 7, Logger::LL_ALERT )
+};
+
+const Uint32 EventLoggerBase::matrixSize=
+sizeof(EventLoggerBase::matrix)/sizeof(EventRepLogLevelMatrix);
+
+EventLogger::EventLogger() : m_filterLevel(15)
+{
+ setCategory("EventLogger");
+ enable(Logger::LL_INFO, Logger::LL_ALERT);
+}
+
+EventLogger::~EventLogger()
+{
+}
+
+bool
+EventLogger::open(const char* logFileName, int maxNoFiles, long maxFileSize,
+ unsigned int maxLogEntries)
+{
+ return addHandler(new FileLogHandler(logFileName, maxNoFiles, maxFileSize,
+ maxLogEntries));
+}
+
+void
+EventLogger::close()
+{
+ removeAllHandlers();
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const LogLevel & ll)
+{
+ out << "[LogLevel: ";
+ for(size_t i = 0; i<LogLevel::LOGLEVEL_CATEGORIES; i++)
+ out << ll.getLogLevel((LogLevel::EventCategory)i) << " ";
+ out << "]";
+ return out;
+}
+
+int
+EventLoggerBase::event_lookup(int eventType,
+ LogLevel::EventCategory &cat,
+ Uint32 &threshold,
+ Logger::LoggerLevel &severity,
+ EventTextFunction &textF)
+{
+ for(unsigned i = 0; i<EventLoggerBase::matrixSize; i++){
+ if(EventLoggerBase::matrix[i].eventType == eventType){
+ cat = EventLoggerBase::matrix[i].eventCategory;
+ threshold = EventLoggerBase::matrix[i].threshold;
+ severity = EventLoggerBase::matrix[i].severity;
+ textF= EventLoggerBase::matrix[i].textF;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+const char*
+EventLogger::getText(char * dst, size_t dst_len,
+ EventTextFunction textF,
+ const Uint32* theData, NodeId nodeId )
+{
+ int pos= 0;
+ if (nodeId != 0)
+ {
+ BaseString::snprintf(dst, dst_len, "Node %u: ", nodeId);
+ pos= strlen(dst);
+ }
+ if (dst_len-pos > 0)
+ textF(dst+pos,dst_len-pos,theData);
+ return dst;
+}
+
+void
+EventLogger::log(int eventType, const Uint32* theData, NodeId nodeId,
+ const LogLevel* ll)
+{
+ Uint32 threshold = 0;
+ Logger::LoggerLevel severity = Logger::LL_WARNING;
+ LogLevel::EventCategory cat= LogLevel::llInvalid;
+ EventTextFunction textF;
+
+ DBUG_ENTER("EventLogger::log");
+ DBUG_PRINT("enter",("eventType=%d, nodeid=%d", eventType, nodeId));
+
+ if (EventLoggerBase::event_lookup(eventType,cat,threshold,severity,textF))
+ DBUG_VOID_RETURN;
+
+ Uint32 set = ll?ll->getLogLevel(cat) : m_logLevel.getLogLevel(cat);
+ DBUG_PRINT("info",("threshold=%d, set=%d", threshold, set));
+ if (ll)
+ DBUG_PRINT("info",("m_logLevel.getLogLevel=%d", m_logLevel.getLogLevel(cat)));
+
+ if (threshold <= set){
+ getText(m_text,sizeof(m_text),textF,theData,nodeId);
+
+ switch (severity){
+ case Logger::LL_ALERT:
+ alert(m_text);
+ break;
+ case Logger::LL_CRITICAL:
+ critical(m_text);
+ break;
+ case Logger::LL_WARNING:
+ warning(m_text);
+ break;
+ case Logger::LL_ERROR:
+ error(m_text);
+ break;
+ case Logger::LL_INFO:
+ info(m_text);
+ break;
+ case Logger::LL_DEBUG:
+ debug(m_text);
+ break;
+ default:
+ info(m_text);
+ break;
+ }
+ } // if (..
+ DBUG_VOID_RETURN;
+}
+
+int
+EventLogger::getFilterLevel() const
+{
+ return m_filterLevel;
+}
+
+void
+EventLogger::setFilterLevel(int filterLevel)
+{
+ m_filterLevel = filterLevel;
+}
+
+//
+// PRIVATE
+//
diff --git a/storage/ndb/src/common/debugger/GrepError.cpp b/storage/ndb/src/common/debugger/GrepError.cpp
new file mode 100644
index 00000000000..20aeaa6dd77
--- /dev/null
+++ b/storage/ndb/src/common/debugger/GrepError.cpp
@@ -0,0 +1,133 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <GrepError.hpp>
+
+/**
+ * Error descriptions.
+ */
+
+const GrepError::ErrorDescription GrepError::errorDescriptions[] = {
+ { GrepError::GE_NO_ERROR,
+ "No error" },
+ { GrepError::SUBSCRIPTION_ID_NOMEM,
+ "Not enough resources to allocate the subscription" },
+ { GrepError::SUBSCRIPTION_ID_NOT_FOUND,
+ "The requested subscription (id, key) does not exist"},
+ { GrepError::SUBSCRIPTION_ID_NOT_UNIQUE,
+ "A subscription with (id, key) does already exist"},
+ { GrepError::SUBSCRIPTION_ID_SUMA_FAILED_CREATE,
+ "Suma failed to create a new subscription id"},
+ { GrepError::NULL_VALUE,
+ "NULL"},
+ { GrepError::SEQUENCE_ERROR,
+ "Error when creating or using sequence."},
+ { GrepError::NOSPACE_IN_POOL,
+ "No space left in pool when trying to seize data"},
+ { GrepError::SUBSCRIPTION_ID_ALREADY_EXIST,
+ "A subscription for this replication channel does already exist"},
+ { GrepError::SUBSCRIPTION_NOT_STARTED,
+ "No subscription is started"},
+ { GrepError::SUBSCRIBER_NOT_FOUND,
+ "The subscriber does not exist in SUMA."},
+ { GrepError::WRONG_NO_OF_SECTIONS,
+ "Something is wrong with the supplied arguments"},
+ { GrepError::ILLEGAL_ACTION_WHEN_STOPPING,
+ "Action can not be performed while channel is in stopping state"},
+ { GrepError::SELECTED_TABLE_NOT_FOUND,
+ "The selected table was not found. "},
+ { GrepError::REP_APPLY_LOGRECORD_FAILED,
+ "Failed applying a log record (permanent error)"},
+ { GrepError::REP_APPLY_METARECORD_FAILED,
+ "Failed applying a meta record (permanent error)"},
+ { GrepError::REP_DELETE_NEGATIVE_EPOCH,
+ "Trying to delete a GCI Buffer using a negative epoch."},
+ { GrepError::REP_DELETE_NONEXISTING_EPOCH,
+ "Trying to delete a non-existing GCI Buffer."},
+ { GrepError::REP_NO_CONNECTED_NODES,
+ "There are no connected nodes in the node group."},
+ { GrepError::REP_DISCONNECT,
+ "Global Replication Server disconnected."},
+ { GrepError::COULD_NOT_ALLOCATE_MEM_FOR_SIGNAL,
+ "Could not allocate memory for signal."},
+ { GrepError::REP_NOT_PROPER_TABLE,
+ "Specified table is not a valid table. "
+ "Either the format is not <db>/<schema>/<tablename> or "
+ "the table name is too long "},
+ { GrepError::REP_TABLE_ALREADY_SELECTED,
+ "The specified table is already selected for replication" },
+ { GrepError::REP_TABLE_NOT_FOUND,
+ "The specified table was not found" },
+ { GrepError::START_OF_COMPONENT_IN_WRONG_STATE,
+ "Component or protocol can not be started in the current state."},
+ { GrepError::START_ALREADY_IN_PROGRESS,
+ "Start of replication protocol is already in progress."},
+ { GrepError::ILLEGAL_STOP_EPOCH_ID,
+ "It is not possible to stop on the requested epoch id."},
+ { GrepError::ILLEGAL_USE_OF_COMMAND,
+ "The command cannot be executed in this state."},
+ { GrepError::CHANNEL_NOT_STOPPABLE,
+ "It is not possible to stop the in this state."},
+
+ /**
+ * Applier stuff
+ */
+ { GrepError::REP_APPLY_NONCOMPLETE_GCIBUFFER,
+ "Applier: Ordered to apply an incomplete GCI Buffer."},
+ { GrepError::REP_APPLY_NULL_GCIBUFFER,
+ "Applier: Tried to apply a NULL GCI Buffer."},
+ { GrepError::REP_APPLIER_START_TRANSACTION,
+ "Applier: Could not start a transaction."},
+ { GrepError::REP_APPLIER_NO_TABLE,
+ "Applier: Table does not exist"},
+ { GrepError::REP_APPLIER_NO_OPERATION,
+ "Applier: Cannot get NdbOperation record."},
+ { GrepError::REP_APPLIER_EXECUTE_TRANSACTION,
+ "Applier: Execute transaction failed."},
+ { GrepError::REP_APPLIER_CREATE_TABLE,
+ "Applier: Create table failed."},
+ { GrepError::REP_APPLIER_PREPARE_TABLE,
+ "Applier: Prepare table for create failed."},
+
+ { GrepError::NOT_YET_IMPLEMENTED,
+ "Command or event not yet implemented."}
+};
+
+
+
+
+
+const Uint32
+GrepError::noOfErrorDescs = sizeof(GrepError::errorDescriptions) /
+ sizeof(GrepError::ErrorDescription);
+
+
+/**
+ * gets the corresponding error message to an err code
+ */
+const char *
+GrepError::getErrorDesc(GrepError::GE_Code err) {
+
+ for(Uint32 i = 0; i<noOfErrorDescs; i++){
+ if(err == errorDescriptions[i].errCode){
+ return errorDescriptions[i].name;
+ }
+ }
+ return 0;
+}
+
+
+
diff --git a/storage/ndb/src/common/debugger/Makefile.am b/storage/ndb/src/common/debugger/Makefile.am
new file mode 100644
index 00000000000..e25a11c9bee
--- /dev/null
+++ b/storage/ndb/src/common/debugger/Makefile.am
@@ -0,0 +1,25 @@
+SUBDIRS = signaldata
+
+noinst_LTLIBRARIES = libtrace.la
+
+libtrace_la_SOURCES = SignalLoggerManager.cpp DebuggerNames.cpp BlockNames.cpp EventLogger.cpp GrepError.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libtrace.dsp
+
+libtrace.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libtrace_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/debugger/SignalLoggerManager.cpp b/storage/ndb/src/common/debugger/SignalLoggerManager.cpp
new file mode 100644
index 00000000000..d642ed09a68
--- /dev/null
+++ b/storage/ndb/src/common/debugger/SignalLoggerManager.cpp
@@ -0,0 +1,502 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "SignalLoggerManager.hpp"
+#include <LongSignal.hpp>
+
+#include <DebuggerNames.hpp>
+
+SignalLoggerManager::SignalLoggerManager()
+{
+ for (int i = 0; i < NO_OF_BLOCKS; i++){
+ logModes[i] = 0;
+ }
+ outputStream = 0;
+ m_ownNodeId = 0;
+ m_logDistributed = false;
+}
+
+SignalLoggerManager::~SignalLoggerManager()
+{
+ if(outputStream != 0){
+ fflush(outputStream);
+ fclose(outputStream);
+ outputStream = 0;
+ }
+}
+
+FILE *
+SignalLoggerManager::setOutputStream(FILE * output)
+{
+ if(outputStream != 0){
+ fflush(outputStream);
+ }
+
+ FILE * out = outputStream;
+ outputStream = output;
+ return out;
+}
+
+FILE *
+SignalLoggerManager::getOutputStream() const
+{
+ return outputStream;
+}
+
+void
+SignalLoggerManager::flushSignalLog()
+{
+ if(outputStream != 0)
+ fflush(outputStream);
+}
+
+void
+SignalLoggerManager::setTrace(unsigned long trace)
+{
+ traceId = trace;
+}
+
+unsigned long
+SignalLoggerManager::getTrace() const
+{
+ return traceId;
+}
+
+void
+SignalLoggerManager::setOwnNodeId(int nodeId){
+ m_ownNodeId = nodeId;
+}
+
+void
+SignalLoggerManager::setLogDistributed(bool val){
+ m_logDistributed = val;
+}
+
+int
+getParameter(char *blocks[NO_OF_BLOCKS], const char * par, const char * line)
+{
+ const char * loc = strstr(line, par);
+ if(loc == NULL)
+ return 0;
+
+ loc += strlen(par);
+
+ int found = 0;
+
+ char * copy = strdup(loc);
+ char * tmp = copy;
+ bool done = false;
+ while(!done){
+ int len = strcspn(tmp, ", ;:\0");
+ if(len == 0)
+ done = true;
+ else {
+ if(* (tmp + len) != ',')
+ done = true;
+ * (tmp + len) = 0;
+ blocks[found] = strdup(tmp);
+ found ++;
+ tmp += (len + 1);
+ }
+ }
+ free(copy);
+ return found;
+}
+
+
+#define SLM_OFF 0
+#define SLM_ON 1
+#define SLM_TOGGLE 2
+
+int
+SignalLoggerManager::log(LogMode logMode, const char * params)
+{
+ char * blocks[NO_OF_BLOCKS];
+ const int count = getParameter(blocks, "BLOCK=", params);
+
+ int cnt = 0;
+ if((count == 1 && blocks[0] == "ALL") ||
+ count == 0){
+
+ for (int number = 0; number < NO_OF_BLOCKS; ++number){
+ cnt += log(SLM_ON, number, logMode);
+ }
+ } else {
+ for (int i = 0; i < count; ++i){
+ BlockNumber number = getBlockNo(blocks[i]);
+ cnt += log(SLM_ON, number-MIN_BLOCK_NO, logMode);
+ }
+ }
+ for(int i = 0; i<count; i++){
+ free(blocks[i]);
+ }
+
+ return cnt;
+}
+
+int
+SignalLoggerManager::log(int cmd, BlockNumber bno, LogMode logMode)
+{
+ // Normalise blocknumber for use in logModes array
+ const BlockNumber bno2 = bno-MIN_BLOCK_NO;
+ assert(bno2<NO_OF_BLOCKS);
+ switch(cmd){
+ case SLM_ON:
+ logModes[bno2] |= logMode;
+ return 1;
+ break;
+ case SLM_OFF:
+ logModes[bno2] &= (~logMode);
+ return 1;
+ break;
+ case SLM_TOGGLE:
+ logModes[bno2] ^= logMode;
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int
+SignalLoggerManager::logOn(bool allBlocks, BlockNumber bno, LogMode logMode)
+{
+ if(!allBlocks){
+ return log(SLM_ON, bno, logMode);
+ }
+ int cnt = 0;
+ for(unsigned int i = MIN_BLOCK_NO; i <= MAX_BLOCK_NO; i++)
+ cnt += log(SLM_ON, i, logMode);
+ return cnt;
+}
+
+int
+SignalLoggerManager::logOff(bool allBlocks, BlockNumber bno, LogMode logMode)
+{
+ if(!allBlocks){
+ return log(SLM_OFF, bno, logMode);
+ }
+ int cnt = 0;
+ for(unsigned int i = MIN_BLOCK_NO; i <= MAX_BLOCK_NO; i++)
+ cnt += log(SLM_OFF, i, logMode);
+ return cnt;
+
+}
+
+int
+SignalLoggerManager::logToggle(bool allBlocks, BlockNumber bno, LogMode logMode)
+{
+ if(!allBlocks){
+ return log(SLM_TOGGLE, bno, logMode);
+ }
+ int cnt = 0;
+ for(unsigned int i = MIN_BLOCK_NO; i <= MAX_BLOCK_NO; i++)
+ cnt += log(SLM_TOGGLE, i, logMode);
+ return cnt;
+}
+
+void
+SignalLoggerManager::executeDirect(const SignalHeader& sh,
+ Uint8 prio, // in-out flag
+ const Uint32 * theData, Uint32 node)
+{
+ Uint32 trace = sh.theTrace;
+ Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+
+ if(outputStream != 0 &&
+ (traceId == 0 || traceId == trace) &&
+ (logMatch(senderBlockNo, LogOut) || logMatch(receiverBlockNo, LogIn))){
+ const char* inOutStr = prio == 0 ? "In" : "Out";
+#ifdef VM_TRACE_TIME
+ fprintf(outputStream, "---- Direct --- Signal --- %s - %d ----\n", inOutStr, time(0));
+#else
+ fprintf(outputStream, "---- Direct --- Signal --- %s ----------------\n", inOutStr);
+#endif
+ // XXX pass in/out to print* function somehow
+ printSignalHeader(outputStream, sh, 0, node, true);
+ printSignalData(outputStream, sh, theData);
+ }
+}
+
+/**
+ * For input signals
+ */
+void
+SignalLoggerManager::executeSignal(const SignalHeader& sh, Uint8 prio,
+ const Uint32 * theData, Uint32 node,
+ const SegmentedSectionPtr ptr[3], Uint32 secs)
+{
+ Uint32 trace = sh.theTrace;
+ //Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+ Uint32 senderNode = refToNode(sh.theSendersBlockRef);
+
+ if(outputStream != 0 &&
+ (traceId == 0 || traceId == trace) &&
+ (logMatch(receiverBlockNo, LogOut) ||
+ (m_logDistributed && m_ownNodeId != senderNode))){
+#ifdef VM_TRACE_TIME
+ fprintf(outputStream, "---- Received - Signal - %d ----\n", time(0));
+#else
+ fprintf(outputStream, "---- Received - Signal ----------------\n");
+#endif
+
+ printSignalHeader(outputStream, sh, prio, node, true);
+ printSignalData(outputStream, sh, theData);
+ for (unsigned i = 0; i < secs; i++)
+ printSegmentedSection(outputStream, sh, ptr, i);
+ }
+}
+
+void
+SignalLoggerManager::executeSignal(const SignalHeader& sh, Uint8 prio,
+ const Uint32 * theData, Uint32 node,
+ const LinearSectionPtr ptr[3], Uint32 secs)
+{
+ Uint32 trace = sh.theTrace;
+ //Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+ Uint32 senderNode = refToNode(sh.theSendersBlockRef);
+
+ if(outputStream != 0 &&
+ (traceId == 0 || traceId == trace) &&
+ (logMatch(receiverBlockNo, LogOut) ||
+ (m_logDistributed && m_ownNodeId != senderNode))){
+#ifdef VM_TRACE_TIME
+ fprintf(outputStream, "---- Received - Signal - %d ----\n", time(0));
+#else
+ fprintf(outputStream, "---- Received - Signal ----------------\n");
+#endif
+
+ printSignalHeader(outputStream, sh, prio, node, true);
+ printSignalData(outputStream, sh, theData);
+ for (unsigned i = 0; i < secs; i++)
+ printLinearSection(outputStream, sh, ptr, i);
+ }
+}
+
+/**
+ * For output signals
+ */
+void
+SignalLoggerManager::sendSignal(const SignalHeader& sh,
+ Uint8 prio,
+ const Uint32 * theData, Uint32 node,
+ const LinearSectionPtr ptr[3], Uint32 secs)
+{
+ Uint32 trace = sh.theTrace;
+ Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ //Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+
+ if(outputStream != 0 &&
+ (traceId == 0 || traceId == trace) &&
+ (logMatch(senderBlockNo, LogOut) ||
+ (m_logDistributed && m_ownNodeId != node))){
+#ifdef VM_TRACE_TIME
+ fprintf(outputStream, "---- Send ----- Signal - %d ----\n", time(0));
+#else
+ fprintf(outputStream, "---- Send ----- Signal ----------------\n");
+#endif
+
+ printSignalHeader(outputStream, sh, prio, node, false);
+ printSignalData(outputStream, sh, theData);
+ for (unsigned i = 0; i < secs; i++)
+ printLinearSection(outputStream, sh, ptr, i);
+ }
+}
+
+/**
+ * For output signals
+ */
+void
+SignalLoggerManager::sendSignal(const SignalHeader& sh, Uint8 prio,
+ const Uint32 * theData, Uint32 node,
+ const SegmentedSectionPtr ptr[3], Uint32 secs)
+{
+ Uint32 trace = sh.theTrace;
+ Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ //Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+
+ if(outputStream != 0 &&
+ (traceId == 0 || traceId == trace) &&
+ (logMatch(senderBlockNo, LogOut) ||
+ (m_logDistributed && m_ownNodeId != node))){
+#ifdef VM_TRACE_TIME
+ fprintf(outputStream, "---- Send ----- Signal - %d ----\n", time(0));
+#else
+ fprintf(outputStream, "---- Send ----- Signal ----------------\n");
+#endif
+
+ printSignalHeader(outputStream, sh, prio, node, false);
+ printSignalData(outputStream, sh, theData);
+ for (unsigned i = 0; i < secs; i++)
+ printSegmentedSection(outputStream, sh, ptr, i);
+ }
+}
+
+void
+SignalLoggerManager::sendSignalWithDelay(Uint32 delayInMilliSeconds,
+ const SignalHeader & sh, Uint8 prio,
+ const Uint32 * theData, Uint32 node,
+ const SegmentedSectionPtr ptr[3], Uint32 secs)
+{
+ Uint32 trace = sh.theTrace;
+ Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ //Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+
+ if(outputStream != 0 &&
+ (traceId == 0 || traceId == trace) &&
+ logMatch(senderBlockNo, LogOut)){
+#ifdef VM_TRACE_TIME
+ fprintf(outputStream,
+ "---- Send ----- Signal (%d ms) %d\n",
+ delayInMilliSeconds,
+ time(0));
+#else
+ fprintf(outputStream, "---- Send delay Signal (%d ms) ----------\n",
+ delayInMilliSeconds);
+#endif
+
+ printSignalHeader(outputStream, sh, prio, node, false);
+ printSignalData(outputStream, sh, theData);
+ for (unsigned i = 0; i < secs; i++)
+ printSegmentedSection(outputStream, sh, ptr, i);
+ }
+}
+
+/**
+ * Generic messages in the signal log
+ */
+void
+SignalLoggerManager::log(BlockNumber bno, const char * msg)
+{
+ // Normalise blocknumber for use in logModes array
+ const BlockNumber bno2 = bno - MIN_BLOCK_NO;
+ assert(bno2<NO_OF_BLOCKS);
+
+ if(outputStream != 0 &&
+ logModes[bno2] != LogOff){
+ fprintf(outputStream, "%s: %s\n", getBlockName(bno, "API"), msg);
+ }
+}
+
+
+void
+SignalLoggerManager::printSignalHeader(FILE * output,
+ const SignalHeader & sh,
+ Uint8 prio,
+ Uint32 node,
+ bool printReceiversSignalId)
+{
+ Uint32 receiverBlockNo = sh.theReceiversBlockNumber;
+ Uint32 receiverProcessor = node;
+ Uint32 gsn = sh.theVerId_signalNumber;
+ Uint32 senderBlockNo = refToBlock(sh.theSendersBlockRef);
+ Uint32 senderProcessor = refToNode(sh.theSendersBlockRef);
+ Uint32 length = sh.theLength;
+ Uint32 trace = sh.theTrace;
+ Uint32 rSigId = sh.theSignalId;
+ Uint32 sSigId = sh.theSendersSignalId;
+
+ const char * signalName = getSignalName(gsn);
+ const char * rBlockName = getBlockName(receiverBlockNo, "API");
+ const char * sBlockName = getBlockName(senderBlockNo, "API");
+
+ if(printReceiversSignalId)
+ fprintf(output,
+ "r.bn: %d \"%s\", r.proc: %d, r.sigId: %d gsn: %d \"%s\" prio: %d\n"
+ ,receiverBlockNo, rBlockName, receiverProcessor, rSigId,
+ gsn, signalName, prio);
+ else
+ fprintf(output,
+ "r.bn: %d \"%s\", r.proc: %d, gsn: %d \"%s\" prio: %d\n",
+ receiverBlockNo, rBlockName, receiverProcessor, gsn,
+ signalName, prio);
+
+ fprintf(output,
+ "s.bn: %d \"%s\", s.proc: %d, s.sigId: %d length: %d trace: %d "
+ "#sec: %d fragInf: %d\n",
+ senderBlockNo, sBlockName, senderProcessor, sSigId, length, trace,
+ sh.m_noOfSections, sh.m_fragmentInfo);
+}
+
+void
+SignalLoggerManager::printSignalData(FILE * output,
+ const SignalHeader & sh,
+ const Uint32 * signalData)
+{
+ Uint32 len = sh.theLength;
+ SignalDataPrintFunction printFunction =
+ findPrintFunction(sh.theVerId_signalNumber);
+
+ bool ok = false; // done with printing
+ if(printFunction != 0){
+ ok = (* printFunction)(output, signalData, len, sh.theReceiversBlockNumber);
+ }
+ if(!ok){
+ while(len >= 7){
+ fprintf(output,
+ " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n",
+ signalData[0], signalData[1], signalData[2], signalData[3],
+ signalData[4], signalData[5], signalData[6]);
+ len -= 7;
+ signalData += 7;
+ }
+ if(len > 0){
+ for(Uint32 i = 0; i<len; i++)
+ fprintf(output, " H\'%.8x", signalData[i]);
+ fprintf(output, "\n");
+ }
+ }
+}
+
+void
+SignalLoggerManager::printLinearSection(FILE * output,
+ const SignalHeader & sh,
+ const LinearSectionPtr ptr[3],
+ unsigned i)
+{
+ fprintf(output, "SECTION %u type=linear", i);
+ if (i >= 3) {
+ fprintf(output, " *** invalid ***\n");
+ return;
+ }
+ const Uint32 len = ptr[i].sz;
+ const Uint32 * data = ptr[i].p;
+ Uint32 pos = 0;
+ fprintf(output, " size=%u\n", (unsigned)len);
+ while (pos < len) {
+ printDataWord(output, pos, data[pos]);
+ }
+ if (len > 0)
+ putc('\n', output);
+}
+
+void
+SignalLoggerManager::printDataWord(FILE * output, Uint32 & pos, const Uint32 data)
+{
+ const char* const hex = "0123456789abcdef";
+ if (pos > 0 && pos % 7 == 0)
+ putc('\n', output);
+ putc(' ', output);
+ putc('H', output);
+ putc('\'', output);
+ for (int i = 7; i >= 0; i--)
+ putc(hex[(data >> (i << 2)) & 0xf], output);
+ pos++;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/AccLock.cpp b/storage/ndb/src/common/debugger/signaldata/AccLock.cpp
new file mode 100644
index 00000000000..affed431957
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/AccLock.cpp
@@ -0,0 +1,75 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/AccLock.hpp>
+#include <SignalLoggerManager.hpp>
+
+bool
+printACC_LOCKREQ(FILE* output, const Uint32* theData, Uint32 len, Uint16 rbn)
+{
+ const AccLockReq* const sig = (const AccLockReq*)theData;
+ Uint32 reqtype = sig->requestInfo & 0xFF;
+ switch (sig->returnCode) {
+ case RNIL:
+ fprintf(output, " returnCode=RNIL");
+ break;
+ case AccLockReq::Success:
+ fprintf(output, " returnCode=Success");
+ break;
+ case AccLockReq::IsBlocked:
+ fprintf(output, " returnCode=IsBlocked");
+ break;
+ case AccLockReq::WouldBlock:
+ fprintf(output, " returnCode=WouldBlock");
+ break;
+ case AccLockReq::Refused:
+ fprintf(output, " returnCode=Refused");
+ break;
+ case AccLockReq::NoFreeOp:
+ fprintf(output, " returnCode=NoFreeOp");
+ break;
+ default:
+ fprintf(output, " returnCode=%u?", sig->returnCode);
+ break;
+ }
+ switch (reqtype) {
+ case AccLockReq::LockShared:
+ fprintf(output, " req=LockShared\n");
+ break;
+ case AccLockReq::LockExclusive:
+ fprintf(output, " req=LockExclusive\n");
+ break;
+ case AccLockReq::Unlock:
+ fprintf(output, " req=Unlock\n");
+ break;
+ case AccLockReq::Abort:
+ fprintf(output, " req=Abort\n");
+ break;
+ default:
+ fprintf(output, " req=%u\n", reqtype);
+ break;
+ }
+ fprintf(output, " accOpPtr: 0x%x\n", sig->accOpPtr);
+ if (reqtype == AccLockReq::LockShared ||
+ reqtype == AccLockReq::LockExclusive) {
+ fprintf(output, " userPtr: 0x%x userRef: 0x%x\n", sig->userPtr, sig->userRef);
+ fprintf(output, " table: id=%u", sig->tableId);
+ fprintf(output, " fragment: id=%u ptr=0x%x\n", sig->fragId, sig->fragPtrI);
+ fprintf(output, " tuple: addr=0x%x hashValue=%x\n", sig->tupAddr, sig->hashValue);
+ fprintf(output, " transid: %08x %08x\n", sig->transId1, sig->transId2);
+ }
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/AlterIndx.cpp b/storage/ndb/src/common/debugger/signaldata/AlterIndx.cpp
new file mode 100644
index 00000000000..e1865136fc3
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/AlterIndx.cpp
@@ -0,0 +1,35 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/AlterIndx.hpp>
+
+bool printALTER_INDX_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterIndxReq * const sig = (AlterIndxReq *) theData;
+ return false;
+}
+
+bool printALTER_INDX_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterIndxConf * const sig = (AlterIndxConf *) theData;
+ return false;
+}
+
+bool printALTER_INDX_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterIndxRef * const sig = (AlterIndxRef *) theData;
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/AlterTab.cpp b/storage/ndb/src/common/debugger/signaldata/AlterTab.cpp
new file mode 100644
index 00000000000..f9521984095
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/AlterTab.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/AlterTab.hpp>
+
+bool printALTER_TAB_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterTabReq * const sig = (AlterTabReq *) theData;
+
+ return false;
+}
+
+bool printALTER_TAB_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterTabConf * const sig = (AlterTabConf *) theData;
+
+ return false;
+}
+
+bool printALTER_TAB_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterTabRef * const sig = (AlterTabRef *) theData;
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/AlterTable.cpp b/storage/ndb/src/common/debugger/signaldata/AlterTable.cpp
new file mode 100644
index 00000000000..59909c8e490
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/AlterTable.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/AlterTable.hpp>
+
+bool printALTER_TABLE_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterTableReq * const sig = (AlterTableReq *) theData;
+
+ return false;
+}
+
+bool printALTER_TABLE_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterTableConf * const sig = (AlterTableConf *) theData;
+
+ return false;
+}
+
+bool printALTER_TABLE_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const AlterTableRef * const sig = (AlterTableRef *) theData;
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/AlterTrig.cpp b/storage/ndb/src/common/debugger/signaldata/AlterTrig.cpp
new file mode 100644
index 00000000000..d488fd6e348
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/AlterTrig.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/AlterTrig.hpp>
+
+bool printALTER_TRIG_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const AlterTrigReq * const sig = (AlterTrigReq *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "\n");
+
+ return false;
+}
+
+bool printALTER_TRIG_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const AlterTrigConf * const sig = (AlterTrigConf *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "\n");
+
+ return false;
+}
+
+bool printALTER_TRIG_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const AlterTrigRef * const sig = (AlterTrigRef *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Error code: %u, ", sig->getErrorCode());
+ fprintf(output, "\n");
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/BackupImpl.cpp b/storage/ndb/src/common/debugger/signaldata/BackupImpl.cpp
new file mode 100644
index 00000000000..bdc34d614cf
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/BackupImpl.cpp
@@ -0,0 +1,142 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <trigger_definitions.h>
+#include <signaldata/BackupImpl.hpp>
+
+bool
+printDEFINE_BACKUP_REQ(FILE * out, const Uint32 * data, Uint32 len, Uint16 bno){
+ DefineBackupReq* sig = (DefineBackupReq*)data;
+ fprintf(out, " backupPtr: %d backupId: %d clientRef: %d clientData: %d\n",
+ sig->backupPtr, sig->backupId, sig->clientRef, sig->clientData);
+ fprintf(out, " backupKey: [ %08x%08x ] DataLength: %d\n",
+ sig->backupKey[0], sig->backupKey[1], sig->backupDataLen);
+ char buf[_NDB_NODE_BITMASK_SIZE * 8 + 1];
+ fprintf(out, " Nodes: %s\n", sig->nodes.getText(buf));
+ return true;
+}
+
+bool
+printDEFINE_BACKUP_REF(FILE * out, const Uint32 * data, Uint32 len, Uint16 bno){
+ DefineBackupRef* sig = (DefineBackupRef*)data;
+ fprintf(out, " backupPtr: %d backupId: %d errorCode: %d\n",
+ sig->backupPtr, sig->backupId, sig->errorCode);
+ return true;
+}
+
+bool
+printDEFINE_BACKUP_CONF(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ DefineBackupConf* sig = (DefineBackupConf*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+}
+
+bool
+printSTART_BACKUP_REQ(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ StartBackupReq* sig = (StartBackupReq*)data;
+ fprintf(out, " backupPtr: %d backupId: %d signalNo: %d of %d\n",
+ sig->backupPtr, sig->backupId,
+ sig->signalNo + 1, sig->noOfSignals);
+ for(Uint32 i = 0; i<sig->noOfTableTriggers; i++)
+ fprintf(out,
+ " Table: %d Triggers = [ insert: %d update: %d delete: %d ]\n",
+ sig->tableTriggers[i].tableId,
+ sig->tableTriggers[i].triggerIds[TriggerEvent::TE_INSERT],
+ sig->tableTriggers[i].triggerIds[TriggerEvent::TE_UPDATE],
+ sig->tableTriggers[i].triggerIds[TriggerEvent::TE_DELETE]);
+ return true;
+}
+
+bool
+printSTART_BACKUP_REF(FILE * out, const Uint32 * data, Uint32 len, Uint16 bno){
+ StartBackupRef* sig = (StartBackupRef*)data;
+ fprintf(out, " backupPtr: %d backupId: %d errorCode: %d\n",
+ sig->backupPtr, sig->backupId, sig->errorCode);
+ return true;
+}
+
+bool
+printSTART_BACKUP_CONF(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ StartBackupConf* sig = (StartBackupConf*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+}
+
+bool
+printBACKUP_FRAGMENT_REQ(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ BackupFragmentReq* sig = (BackupFragmentReq*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ fprintf(out, " tableId: %d fragmentNo: %d (count = %d)\n",
+ sig->tableId, sig->fragmentNo, sig->count);
+ return true;
+}
+
+bool
+printBACKUP_FRAGMENT_REF(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ BackupFragmentRef* sig = (BackupFragmentRef*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ fprintf(out, " tableId: %d fragmentNo: %d errorCode: %d\n",
+ sig->tableId, sig->fragmentNo, sig->errorCode);
+ return true;
+}
+
+bool
+printBACKUP_FRAGMENT_CONF(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ BackupFragmentConf* sig = (BackupFragmentConf*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ fprintf(out, " tableId: %d fragmentNo: %d records: %d bytes: %d\n",
+ sig->tableId, sig->fragmentNo, sig->noOfRecords, sig->noOfBytes);
+ return true;
+}
+
+bool
+printSTOP_BACKUP_REQ(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ StopBackupReq* sig = (StopBackupReq*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+}
+
+bool
+printSTOP_BACKUP_REF(FILE * out, const Uint32 * data, Uint32 len, Uint16 bno){
+ StopBackupRef* sig = (StopBackupRef*)data;
+ fprintf(out, " backupPtr: %d backupId: %d errorCode: %d\n",
+ sig->backupPtr, sig->backupId, sig->errorCode);
+ return true;
+}
+
+bool
+printSTOP_BACKUP_CONF(FILE * out, const Uint32 * data, Uint32 l, Uint16 bno){
+ StopBackupConf* sig = (StopBackupConf*)data;
+ fprintf(out, " backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+}
+
+bool
+printBACKUP_STATUS_REQ(FILE *, const Uint32 *, Uint32, Uint16){
+ return false;
+}
+
+bool
+printBACKUP_STATUS_CONF(FILE *, const Uint32 *, Uint32, Uint16){
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/BackupSignalData.cpp b/storage/ndb/src/common/debugger/signaldata/BackupSignalData.cpp
new file mode 100644
index 00000000000..4b0a0e07b66
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/BackupSignalData.cpp
@@ -0,0 +1,129 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/BackupSignalData.hpp>
+
+bool
+printBACKUP_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 bno){
+ BackupReq* sig = (BackupReq*)theData;
+ fprintf(output, " senderData: %d DataLength: %d\n",
+ sig->senderData,
+ sig->backupDataLen);
+ return true;
+}
+
+bool
+printBACKUP_DATA(FILE * output, const Uint32 * theData, Uint32 len, Uint16 bno){
+ BackupData * sig = (BackupData*)theData;
+ if(sig->requestType == BackupData::ClientToMaster){
+ fprintf(output, " ClientToMaster: senderData: %d backupId: %d\n",
+ sig->senderData, sig->backupId);
+ } else if(sig->requestType == BackupData::MasterToSlave){
+ fprintf(output, " MasterToSlave: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ }
+ return false;
+}
+
+bool
+printBACKUP_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 bno){
+
+ BackupRef* sig = (BackupRef*)theData;
+ fprintf(output, " senderData: %d errorCode: %d masterRef: %d\n",
+ sig->senderData,
+ sig->errorCode,
+ sig->masterRef);
+ return true;
+}
+
+bool
+printBACKUP_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 bno){
+ BackupConf* sig = (BackupConf*)theData;
+ fprintf(output, " senderData: %d backupId: %d\n",
+ sig->senderData,
+ sig->backupId);
+ return true;
+}
+
+bool
+printBACKUP_ABORT_REP(FILE * out, const Uint32 * data, Uint32 len, Uint16 bno){
+ BackupAbortRep* sig = (BackupAbortRep*)data;
+ fprintf(out, " senderData: %d backupId: %d reason: %d\n",
+ sig->senderData,
+ sig->backupId,
+ sig->reason);
+ return true;
+}
+
+bool
+printBACKUP_COMPLETE_REP(FILE * out, const Uint32 * data, Uint32 len, Uint16 b){
+ BackupCompleteRep* sig = (BackupCompleteRep*)data;
+ fprintf(out, " senderData: %d backupId: %d records: %d bytes: %d\n",
+ sig->senderData,
+ sig->backupId,
+ sig->noOfRecords,
+ sig->noOfBytes);
+ return true;
+}
+
+bool
+printBACKUP_NF_COMPLETE_REP(FILE*, const Uint32*, Uint32, Uint16){
+ return false;
+}
+
+bool
+printABORT_BACKUP_ORD(FILE * out, const Uint32 * data, Uint32 len, Uint16 b){
+ AbortBackupOrd* sig = (AbortBackupOrd*)data;
+
+ AbortBackupOrd::RequestType rt =(AbortBackupOrd::RequestType)sig->requestType;
+ switch(rt){
+ case AbortBackupOrd::ClientAbort:
+ fprintf(out, " ClientAbort: senderData: %d backupId: %d\n",
+ sig->senderData, sig->backupId);
+ return true;
+ break;
+ case AbortBackupOrd::BackupComplete:
+ fprintf(out, " BackupComplete: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+ case AbortBackupOrd::BackupFailure:
+ fprintf(out, " BackupFailure: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+ case AbortBackupOrd::LogBufferFull:
+ fprintf(out, " LogBufferFull: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+ break;
+ case AbortBackupOrd::FileOrScanError:
+ fprintf(out, " FileOrScanError: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+ break;
+ case AbortBackupOrd::BackupFailureDueToNodeFail:
+ fprintf(out, " BackupFailureDueToNodeFail: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+ break;
+ case AbortBackupOrd::OkToClean:
+ fprintf(out, " OkToClean: backupPtr: %d backupId: %d\n",
+ sig->backupPtr, sig->backupId);
+ return true;
+ break;
+ }
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/CloseComReqConf.cpp b/storage/ndb/src/common/debugger/signaldata/CloseComReqConf.cpp
new file mode 100644
index 00000000000..84410a2b2db
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CloseComReqConf.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <kernel_types.h>
+#include <BlockNumbers.h>
+#include <signaldata/CloseComReqConf.hpp>
+
+bool
+printCLOSECOMREQCONF(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 receiverBlockNo){
+
+ CloseComReqConf * cc = (CloseComReqConf*)theData;
+
+ fprintf(output, " xxxBlockRef = (%d, %d) failNo = %d noOfNodes = %d\n",
+ refToBlock(cc->xxxBlockRef), refToNode(cc->xxxBlockRef),
+ cc->failNo, cc->noOfNodes);
+
+ int hits = 0;
+ fprintf(output, " Nodes: ");
+ for(int i = 0; i<MAX_NODES; i++){
+ if(NodeBitmask::get(cc->theNodes, i)){
+ hits++;
+ fprintf(output, " %d", i);
+ }
+ if(hits == 16){
+ fprintf(output, "\n Nodes: ");
+ hits = 0;
+ }
+ }
+ if(hits != 0)
+ fprintf(output, "\n");
+
+ return true;
+}
+
+
diff --git a/storage/ndb/src/common/debugger/signaldata/CntrStart.cpp b/storage/ndb/src/common/debugger/signaldata/CntrStart.cpp
new file mode 100644
index 00000000000..154013f40b0
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CntrStart.cpp
@@ -0,0 +1,37 @@
+#include <signaldata/CntrStart.hpp>
+
+bool
+printCNTR_START_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const CntrStartReq * const sig = (CntrStartReq *)theData;
+ fprintf(output, " nodeId: %x\n", sig->nodeId);
+ fprintf(output, " startType: %x\n", sig->startType);
+ fprintf(output, " lastGci: %x\n", sig->lastGci);
+ return true;
+}
+
+bool
+printCNTR_START_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const CntrStartRef * const sig = (CntrStartRef *)theData;
+ fprintf(output, " errorCode: %x\n", sig->errorCode);
+ fprintf(output, " masterNodeId: %x\n", sig->masterNodeId);
+ return true;
+}
+
+bool
+printCNTR_START_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const CntrStartConf * const sig = (CntrStartConf *)theData;
+ fprintf(output, " startType: %x\n", sig->startType);
+ fprintf(output, " startGci: %x\n", sig->startGci);
+ fprintf(output, " masterNodeId: %x\n", sig->masterNodeId);
+ fprintf(output, " noStartNodes: %x\n", sig->noStartNodes);
+
+ char buf[32*NdbNodeBitmask::Size+1];
+ fprintf(output, " startedNodes: %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->startedNodes, buf));
+ fprintf(output, " startingNodes: %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->startingNodes, buf));
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/ContinueB.cpp b/storage/ndb/src/common/debugger/signaldata/ContinueB.cpp
new file mode 100644
index 00000000000..c295041bc01
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/ContinueB.cpp
@@ -0,0 +1,35 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <kernel_types.h>
+#include <BlockNumbers.h>
+#include <signaldata/DihContinueB.hpp>
+#include <signaldata/NdbfsContinueB.hpp>
+
+bool
+printCONTINUEB(FILE * output, const Uint32 * theData, Uint32 len,
+ Uint16 receiverBlockNo){
+ if(receiverBlockNo == DBDIH){
+ return printCONTINUEB_DBDIH(output, theData, len, 0);
+ } else if(receiverBlockNo == NDBFS) {
+ return printCONTINUEB_NDBFS(output, theData, len, 0);
+ }
+
+ return false;
+}
+
+
diff --git a/storage/ndb/src/common/debugger/signaldata/CopyGCI.cpp b/storage/ndb/src/common/debugger/signaldata/CopyGCI.cpp
new file mode 100644
index 00000000000..173b3f6708f
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CopyGCI.cpp
@@ -0,0 +1,58 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/CopyGCIReq.hpp>
+
+static
+void
+print(char * buf, size_t buf_len, CopyGCIReq::CopyReason r){
+ switch(r){
+ case CopyGCIReq::IDLE:
+ BaseString::snprintf(buf, buf_len, "IDLE");
+ break;
+ case CopyGCIReq::LOCAL_CHECKPOINT:
+ BaseString::snprintf(buf, buf_len, "LOCAL_CHECKPOINT");
+ break;
+ case CopyGCIReq::RESTART:
+ BaseString::snprintf(buf, buf_len, "RESTART");
+ break;
+ case CopyGCIReq::GLOBAL_CHECKPOINT:
+ BaseString::snprintf(buf, buf_len, "GLOBAL_CHECKPOINT");
+ break;
+ case CopyGCIReq::INITIAL_START_COMPLETED:
+ BaseString::snprintf(buf, buf_len, "INITIAL_START_COMPLETED");
+ break;
+ default:
+ BaseString::snprintf(buf, buf_len, "<Unknown>");
+ }
+}
+
+bool
+printCOPY_GCI_REQ(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+ CopyGCIReq * sig = (CopyGCIReq*)theData;
+
+ static char buf[255];
+ print(buf, sizeof(buf), (CopyGCIReq::CopyReason)sig->copyReason);
+
+ fprintf(output, " SenderData: %d CopyReason: %s StartWord: %d\n",
+ sig->anyData,
+ buf,
+ sig->startWord);
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/CreateEvnt.cpp b/storage/ndb/src/common/debugger/signaldata/CreateEvnt.cpp
new file mode 100644
index 00000000000..7b497d6a974
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CreateEvnt.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/CreateEvnt.hpp>
+
+bool printCREATE_EVNT_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const CreateEvntReq * const sig = (CreateEvntReq *) theData;
+
+ return false;
+}
+
+bool printCREATE_EVNT_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const CreateEvntConf * const sig = (CreateEvntConf *) theData;
+
+ return false;
+}
+
+bool printCREATE_EVNT_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const CreateEvntRef * const sig = (CreateEvntRef *) theData;
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/CreateFragmentation.cpp b/storage/ndb/src/common/debugger/signaldata/CreateFragmentation.cpp
new file mode 100644
index 00000000000..027f743b5ea
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CreateFragmentation.cpp
@@ -0,0 +1,56 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/CreateFragmentation.hpp>
+
+bool
+printCREATE_FRAGMENTATION_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const CreateFragmentationReq * const sig = (CreateFragmentationReq *)theData;
+ fprintf(output, " senderRef: %x\n", sig->senderRef);
+ fprintf(output, " senderData: %x\n", sig->senderData);
+ fprintf(output, " fragmentationType: %x\n", sig->fragmentationType);
+ fprintf(output, " noOfFragments: %x\n", sig->noOfFragments);
+ fprintf(output, " fragmentNode: %x\n", sig->fragmentNode);
+ if (sig->primaryTableId == RNIL)
+ fprintf(output, " primaryTableId: none\n");
+ else
+ fprintf(output, " primaryTableId: %x\n", sig->primaryTableId);
+ return true;
+}
+
+bool
+printCREATE_FRAGMENTATION_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const CreateFragmentationRef * const sig = (CreateFragmentationRef *)theData;
+ fprintf(output, " senderRef: %x\n", sig->senderRef);
+ fprintf(output, " senderData: %x\n", sig->senderData);
+ fprintf(output, " errorCode: %x\n", sig->errorCode);
+ return true;
+}
+
+bool
+printCREATE_FRAGMENTATION_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const CreateFragmentationConf * const sig =
+ (CreateFragmentationConf *)theData;
+ fprintf(output, " senderRef: %x\n", sig->senderRef);
+ fprintf(output, " senderData: %x\n", sig->senderData);
+ fprintf(output, " noOfReplicas: %x\n", sig->noOfReplicas);
+ fprintf(output, " noOfFragments: %x\n", sig->noOfFragments);
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/CreateIndx.cpp b/storage/ndb/src/common/debugger/signaldata/CreateIndx.cpp
new file mode 100644
index 00000000000..8fcbb9279ed
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CreateIndx.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/CreateIndx.hpp>
+
+bool printCREATE_INDX_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const CreateIndxReq * const sig = (CreateIndxReq *) theData;
+
+ return false;
+}
+
+bool printCREATE_INDX_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const CreateIndxConf * const sig = (CreateIndxConf *) theData;
+
+ return false;
+}
+
+bool printCREATE_INDX_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const CreateIndxRef * const sig = (CreateIndxRef *) theData;
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/CreateTrig.cpp b/storage/ndb/src/common/debugger/signaldata/CreateTrig.cpp
new file mode 100644
index 00000000000..db5344cfbe7
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/CreateTrig.cpp
@@ -0,0 +1,120 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/CreateTrig.hpp>
+
+bool printCREATE_TRIG_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const CreateTrigReq * const sig = (CreateTrigReq *) theData;
+
+ //char triggerName[MAX_TAB_NAME_SIZE];
+ char triggerType[32];
+ char triggerActionTime[32];
+ char triggerEvent[32];
+
+ //sig->getTriggerName((char *) &triggerName);
+ switch (sig->getTriggerType()) {
+ case(TriggerType::SECONDARY_INDEX):
+ BaseString::snprintf(triggerType, sizeof(triggerType), "SECONDARY_INDEX");
+ break;
+ case(TriggerType::SUBSCRIPTION):
+ BaseString::snprintf(triggerType, sizeof(triggerType), "SUBSCRIPTION");
+ break;
+ case(TriggerType::ORDERED_INDEX):
+ BaseString::snprintf(triggerType, sizeof(triggerType), "ORDERED_INDEX");
+ break;
+ default:
+ BaseString::snprintf(triggerType, sizeof(triggerType), "UNKNOWN [%d]", (int)sig->getTriggerType());
+ break;
+ }
+ switch (sig->getTriggerActionTime()) {
+ case (TriggerActionTime::TA_BEFORE):
+ BaseString::snprintf(triggerActionTime, sizeof(triggerActionTime), "BEFORE");
+ break;
+ case(TriggerActionTime::TA_AFTER):
+ BaseString::snprintf(triggerActionTime, sizeof(triggerActionTime), "AFTER");
+ break;
+ case (TriggerActionTime::TA_DEFERRED):
+ BaseString::snprintf(triggerActionTime, sizeof(triggerActionTime), "DEFERRED");
+ break;
+ case (TriggerActionTime::TA_DETACHED):
+ BaseString::snprintf(triggerActionTime, sizeof(triggerActionTime), "DETACHED");
+ break;
+ default:
+ BaseString::snprintf(triggerActionTime, sizeof(triggerActionTime),
+ "UNKNOWN [%d]", (int)sig->getTriggerActionTime());
+ break;
+ }
+ switch (sig->getTriggerEvent()) {
+ case (TriggerEvent::TE_INSERT):
+ BaseString::snprintf(triggerEvent, sizeof(triggerEvent), "INSERT");
+ break;
+ case(TriggerEvent::TE_DELETE):
+ BaseString::snprintf(triggerEvent, sizeof(triggerEvent), "DELETE");
+ break;
+ case(TriggerEvent::TE_UPDATE):
+ BaseString::snprintf(triggerEvent, sizeof(triggerEvent), "UPDATE");
+ break;
+ case(TriggerEvent::TE_CUSTOM):
+ BaseString::snprintf(triggerEvent, sizeof(triggerEvent), "CUSTOM");
+ break;
+ default:
+ BaseString::snprintf(triggerEvent, sizeof(triggerEvent), "UNKNOWN [%d]", (int)sig->getTriggerEvent());
+ break;
+ }
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ //fprintf(output, "Trigger name: \"%s\"\n", triggerName);
+ fprintf(output, "Type: %s, ", triggerType);
+ fprintf(output, "Action: %s, ", triggerActionTime);
+ fprintf(output, "Event: %s, ", triggerEvent);
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Table id: %u, ", sig->getTableId());
+ fprintf(output, "Monitor replicas: %s ", (sig->getMonitorReplicas())?"true":"false");
+ fprintf(output, "Monitor all attributes: %s ", (sig->getMonitorAllAttributes())?"true":"false");
+ const AttributeMask& attributeMask = sig->getAttributeMask();
+
+ char buf[MAXNROFATTRIBUTESINWORDS * 8 + 1];
+ fprintf(output, "Attribute mask: %s", attributeMask.getText(buf));
+ fprintf(output, "\n");
+
+ return false;
+}
+
+bool printCREATE_TRIG_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const CreateTrigConf * const sig = (CreateTrigConf *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Table id: %u, ", sig->getTableId());
+ fprintf(output, "\n");
+
+ return false;
+}
+
+bool printCREATE_TRIG_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const CreateTrigRef * const sig = (CreateTrigRef *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Table id: %u, ", sig->getTableId());
+ fprintf(output, "Error code: %u, ", sig->getErrorCode());
+ fprintf(output, "\n");
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp b/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp
new file mode 100644
index 00000000000..43c129347c0
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp
@@ -0,0 +1,145 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/DictTabInfo.hpp>
+#include <ndb_limits.h>
+
+//static
+const
+SimpleProperties::SP2StructMapping
+DictTabInfo::TableMapping[] = {
+ DTIMAPS(Table, TableName, TableName, 0, MAX_TAB_NAME_SIZE),
+ DTIMAP(Table, TableId, TableId),
+ DTIMAPS(Table, PrimaryTable, PrimaryTable, 0, MAX_TAB_NAME_SIZE),
+ DTIMAP(Table, PrimaryTableId, PrimaryTableId),
+ DTIMAP2(Table, TableLoggedFlag, TableLoggedFlag, 0, 1),
+ DTIMAP2(Table, TableKValue, TableKValue, 6, 6),
+ DTIMAP2(Table, MinLoadFactor, MinLoadFactor, 0, 90),
+ DTIMAP2(Table, MaxLoadFactor, MaxLoadFactor, 25, 110),
+ DTIMAP2(Table, FragmentTypeVal, FragmentType, 0, 3),
+ DTIMAP2(Table, TableStorageVal, TableStorage, 0, 0),
+ DTIMAP2(Table, TableTypeVal, TableType, 1, 3),
+ DTIMAP(Table, NoOfKeyAttr, NoOfKeyAttr),
+ DTIMAP2(Table, NoOfAttributes, NoOfAttributes, 1, MAX_ATTRIBUTES_IN_TABLE),
+ DTIMAP(Table, NoOfNullable, NoOfNullable),
+ DTIMAP2(Table, NoOfVariable, NoOfVariable, 0, 0),
+ DTIMAP(Table, KeyLength, KeyLength),
+ DTIMAP(Table, TableVersion, TableVersion),
+ DTIMAP(Table, IndexState, IndexState),
+ DTIMAP(Table, InsertTriggerId, InsertTriggerId),
+ DTIMAP(Table, UpdateTriggerId, UpdateTriggerId),
+ DTIMAP(Table, DeleteTriggerId, DeleteTriggerId),
+ DTIMAP(Table, CustomTriggerId, CustomTriggerId),
+ DTIMAP2(Table, FrmLen, FrmLen, 0, MAX_FRM_DATA_SIZE),
+ DTIMAPB(Table, FrmData, FrmData, 0, MAX_FRM_DATA_SIZE, FrmLen),
+ DTIMAP(Table, FragmentCount, FragmentCount),
+ DTIMAP2(Table, FragmentDataLen, FragmentDataLen, 0, MAX_FRAGMENT_DATA_BYTES),
+ DTIMAPB(Table, FragmentData, FragmentData, 0, MAX_FRAGMENT_DATA_BYTES, FragmentDataLen),
+ DTIBREAK(AttributeName)
+};
+
+//static
+const Uint32 DictTabInfo::TableMappingSize =
+sizeof(DictTabInfo::TableMapping) / sizeof(SimpleProperties::SP2StructMapping);
+
+//static
+const
+SimpleProperties::SP2StructMapping
+DictTabInfo::AttributeMapping[] = {
+ DTIMAPS(Attribute, AttributeName, AttributeName, 0, MAX_ATTR_NAME_SIZE),
+ DTIMAP(Attribute, AttributeId, AttributeId),
+ DTIMAP(Attribute, AttributeType, AttributeType),
+ DTIMAP2(Attribute, AttributeSize, AttributeSize, 3, 7),
+ DTIMAP2(Attribute, AttributeArraySize, AttributeArraySize, 0, 65535),
+ DTIMAP2(Attribute, AttributeKeyFlag, AttributeKeyFlag, 0, 1),
+ DTIMAP2(Attribute, AttributeNullableFlag, AttributeNullableFlag, 0, 1),
+ DTIMAP2(Attribute, AttributeDKey, AttributeDKey, 0, 1),
+ DTIMAP(Attribute, AttributeExtType, AttributeExtType),
+ DTIMAP(Attribute, AttributeExtPrecision, AttributeExtPrecision),
+ DTIMAP(Attribute, AttributeExtScale, AttributeExtScale),
+ DTIMAP(Attribute, AttributeExtLength, AttributeExtLength),
+ DTIMAP2(Attribute, AttributeAutoIncrement, AttributeAutoIncrement, 0, 1),
+ DTIMAPS(Attribute, AttributeDefaultValue, AttributeDefaultValue,
+ 0, MAX_ATTR_DEFAULT_VALUE_SIZE),
+ DTIBREAK(AttributeEnd)
+};
+
+//static
+const Uint32 DictTabInfo::AttributeMappingSize =
+sizeof(DictTabInfo::AttributeMapping) /
+sizeof(SimpleProperties::SP2StructMapping);
+
+bool printDICTTABINFO(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+// const DictTabInfo * const sig = (DictTabInfo *) theData;
+
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ return true;
+}
+
+void
+DictTabInfo::Table::init(){
+ memset(TableName, 0, sizeof(TableName));//TableName[0] = 0;
+ TableId = ~0;
+ memset(PrimaryTable, 0, sizeof(PrimaryTable));//PrimaryTable[0] = 0; // Only used when "index"
+ PrimaryTableId = RNIL;
+ TableLoggedFlag = 1;
+ NoOfKeyAttr = 0;
+ NoOfAttributes = 0;
+ NoOfNullable = 0;
+ NoOfVariable = 0;
+ TableKValue = 6;
+ MinLoadFactor = 78;
+ MaxLoadFactor = 80;
+ KeyLength = 0;
+ FragmentType = DictTabInfo::AllNodesSmallTable;
+ TableStorage = 0;
+ TableType = DictTabInfo::UndefTableType;
+ TableVersion = 0;
+ IndexState = ~0;
+ InsertTriggerId = RNIL;
+ UpdateTriggerId = RNIL;
+ DeleteTriggerId = RNIL;
+ CustomTriggerId = RNIL;
+ FrmLen = 0;
+ memset(FrmData, 0, sizeof(FrmData));
+ FragmentCount = 0;
+ FragmentDataLen = 0;
+ memset(FragmentData, 0, sizeof(FragmentData));
+}
+
+void
+DictTabInfo::Attribute::init(){
+ memset(AttributeName, 0, sizeof(AttributeName));//AttributeName[0] = 0;
+ AttributeId = 0;
+ AttributeType = ~0, // deprecated
+ AttributeSize = DictTabInfo::a32Bit;
+ AttributeArraySize = 1;
+ AttributeKeyFlag = 0;
+ AttributeNullableFlag = 0;
+ AttributeDKey = 0;
+ AttributeExtType = DictTabInfo::ExtUnsigned,
+ AttributeExtPrecision = 0,
+ AttributeExtScale = 0,
+ AttributeExtLength = 0,
+ AttributeAutoIncrement = false;
+ memset(AttributeDefaultValue, 0, sizeof(AttributeDefaultValue));//AttributeDefaultValue[0] = 0;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DihContinueB.cpp b/storage/ndb/src/common/debugger/signaldata/DihContinueB.cpp
new file mode 100644
index 00000000000..9fece17315c
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DihContinueB.cpp
@@ -0,0 +1,220 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/DihContinueB.hpp>
+
+bool
+printCONTINUEB_DBDIH(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 not_used){
+
+ (void)not_used;
+
+ switch (theData[0]) {
+ case DihContinueB::ZPACK_TABLE_INTO_PAGES:
+ fprintf(output, " Pack Table Into Pages: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZPACK_FRAG_INTO_PAGES:
+ fprintf(output, " Pack Frag Into Pages: Table: %d Fragment: %d PageIndex: %d WordIndex: %d\n",
+ theData[1], theData[2], theData[3], theData[4]);
+ return true;
+ break;
+ case DihContinueB::ZREAD_PAGES_INTO_TABLE:
+ fprintf(output, " Read Pages Into Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZREAD_PAGES_INTO_FRAG:
+ fprintf(output, " Read Pages Into Frag: Table: %d Fragment: %d PageIndex: %d WordIndex: %d\n",
+ theData[1], theData[2], theData[3], theData[4]);
+ return true;
+ break;
+#if 0
+ case DihContinueB::ZREAD_TAB_DESCRIPTION:
+ fprintf(output, " Read Table description: %d\n", theData[1]);
+ return true;
+ break;
+#endif
+ case DihContinueB::ZCOPY_TABLE:
+ fprintf(output, " Copy Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZCOPY_TABLE_NODE:
+ fprintf(output, " Copy table node: TableId: %d NodeId: %d\n",
+ theData[1], theData[2]);
+ fprintf(output, "PageIndex: %d WordIndex: %d NoOfWords: %d\n",
+ theData[3], theData[4], theData[5]);
+ return true;
+ break;
+ case DihContinueB::ZSTART_FRAGMENT:
+ fprintf(output, " Start fragment: Table: %d Fragment: %d\n",
+ theData[1], theData[2]);
+ return true;
+ break;
+ case DihContinueB::ZCOMPLETE_RESTART:
+ fprintf(output, "Complete Restart\n");
+ return true;
+ break;
+ case DihContinueB::ZREAD_TABLE_FROM_PAGES:
+ fprintf(output, " Read Table From Pages: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZSR_PHASE2_READ_TABLE:
+ fprintf(output, " Phase 2 Read Table: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZCHECK_TC_COUNTER:
+ fprintf(output, " Check Tc Counter from place %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZCALCULATE_KEEP_GCI:
+ fprintf(output, " Calc Keep GCI: Table: %d Fragment: %d\n",
+ theData[1], theData[2]);
+ return true;
+ break;
+ case DihContinueB::ZSTORE_NEW_LCP_ID:
+ fprintf(output, " Store New LCP Id\n");
+ return true;
+ break;
+ case DihContinueB::ZTABLE_UPDATE:
+ fprintf(output, " Table Update: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZCHECK_LCP_COMPLETED:
+ fprintf(output, " Check LCP Completed: TableId %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZINIT_LCP:
+ fprintf(output, " Init LCP: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZADD_TABLE_MASTER_PAGES:
+ fprintf(output, " Add Table Master Pages: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZDIH_ADD_TABLE_MASTER:
+ fprintf(output, " Dih Add Table Master: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZADD_TABLE_SLAVE_PAGES:
+ fprintf(output, " Add Table Slave Pages: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZDIH_ADD_TABLE_SLAVE:
+ fprintf(output, " Add Table Slave: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZSTART_GCP:
+ fprintf(output, " Start GCP\n");
+ return true;
+ break;
+ case DihContinueB::ZCOPY_GCI:
+ fprintf(output, " Copy GCI\n");
+ return true;
+ break;
+ case DihContinueB::ZEMPTY_VERIFY_QUEUE:
+ fprintf(output, " Empty Verify Queue\n");
+ return true;
+ break;
+ case DihContinueB::ZCHECK_GCP_STOP:
+ fprintf(output, " Check GCP Stop\n");
+ if (len == 6){
+ fprintf(output, "coldGcpStatus = %d\n", theData[1]);
+ fprintf(output, "cgcpStatus = %d\n", theData[2]);
+ fprintf(output, "coldGcpId = %d\n", theData[3]);
+ fprintf(output, "cnewgcp = %d\n", theData[4]);
+ fprintf(output, "cgcpSameCounter = %d\n", theData[5]);
+ }
+ return true;
+ break;
+ case DihContinueB::ZREMOVE_NODE_FROM_TABLE:
+ fprintf(output, " Remove Node From Table: Node: %d Table: %d\n",
+ theData[1], theData[2]);
+ return true;
+ break;
+ case DihContinueB::ZCOPY_NODE:
+ fprintf(output, " Copy Node: Table: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZSTART_TAKE_OVER:
+ fprintf(output, " Start Take Over: TakeOverPtr: %d, startNode: %d, toNode: %d\n",
+ theData[1], theData[2], theData[3]);
+ return true;
+ break;
+ case DihContinueB::ZCHECK_START_TAKE_OVER:
+ fprintf(output, " Check Start Take Over\n");
+ return true;
+ break;
+ case DihContinueB::ZTO_START_COPY_FRAG:
+ fprintf(output, " To Start Copy Frag: TakeOverPtr: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZINVALIDATE_NODE_LCP:
+ fprintf(output, " Invalide LCP: NodeId: %d TableId %d\n",
+ theData[1], theData[2]);
+ return true;
+ break;
+ case DihContinueB::ZINITIALISE_RECORDS:
+ fprintf(output, " Initialise Records: tdata0: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::ZSTART_PERMREQ_AGAIN:
+ fprintf(output, " START_PERMREQ again for node: %d\n", theData[1]);
+ return true;
+ break;
+ case DihContinueB::SwitchReplica:
+ fprintf(output, " NodeId = %d TableId = %d FragNo = %d\n",
+ theData[1], theData[2], theData[3]);
+ return true;
+ break;
+ case DihContinueB::ZSEND_START_TO:
+ fprintf(output, " Send Start Take Over: TakeOverPtr: %d, startNode: %d, toNode: %d\n",
+ theData[1], theData[2], theData[3]);
+ return true;
+ break;
+ case DihContinueB::ZSEND_UPDATE_TO:
+ fprintf(output, " Send Update Take Over: TakeOverPtr: %d, startNode: %d, toNode: %d\n",
+ theData[1], theData[2], theData[3]);
+ return true;
+ break;
+ case DihContinueB::ZSEND_END_TO:
+ fprintf(output, " Send End Take Over: TakeOverPtr: %d, startNode: %d, toNode: %d\n",
+ theData[1], theData[2], theData[3]);
+ return true;
+ break;
+ case DihContinueB::ZSEND_ADD_FRAG:
+ fprintf(output, " Send Add Fragment: TakeOverPtr: %d, startNode: %d, toNode: %d\n",
+ theData[1], theData[2], theData[3]);
+ return true;
+ break;
+ case DihContinueB::ZSEND_CREATE_FRAG:
+ fprintf(output, " Send Create Fragment: TakeOverPtr: %d, storedType: %d, start Gci: %d, startNode: %d, toNode: %d\n",
+ theData[1], theData[2], theData[3], theData[4], theData[5]);
+ return true;
+ break;
+ case DihContinueB::WAIT_DROP_TAB_WRITING_TO_FILE:
+ fprintf(output, " Wait drop tab writing to file TableId: %d\n", theData[1]);
+ return true;
+ case DihContinueB::CHECK_WAIT_DROP_TAB_FAILED_LQH:
+ fprintf(output, " Wait drop tab FailedNodeId: %d TableId: %d\n",
+ theData[1], theData[2]);
+ return true;
+ default:
+ fprintf(output, " Default system error lab...\n");
+ break;
+ }//switch
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DihSwitchReplicaReq.cpp b/storage/ndb/src/common/debugger/signaldata/DihSwitchReplicaReq.cpp
new file mode 100644
index 00000000000..2e4318f4033
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DihSwitchReplicaReq.cpp
@@ -0,0 +1,48 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/DihSwitchReplicaReq.hpp>
+
+bool
+printDIH_SWITCH_REPLICA_REQ(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+
+ DihSwitchReplicaReq * req = (DihSwitchReplicaReq *)&theData[0];
+
+ const Uint32 requestInfo = req->requestInfo;
+
+ switch(DihSwitchReplicaReq::getRequestType(requestInfo)){
+ case DihSwitchReplicaReq::RemoveNodeAsPrimary:{
+ fprintf(output, " RemoveNodeAsPrimary: Node=%d", req->nodeId);
+ if(DihSwitchReplicaReq::getAllTables(requestInfo))
+ fprintf(output, " All Tables");
+ else
+ fprintf(output, " TableId=%d", req->tableId);
+
+ if(DihSwitchReplicaReq::getDistribute(requestInfo))
+ fprintf(output, " Distribute");
+ fprintf(output, "\n");
+ return true;
+ }
+ break;
+ default:
+ fprintf(output, " Unknown request type:\n");
+ }
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DisconnectRep.cpp b/storage/ndb/src/common/debugger/signaldata/DisconnectRep.cpp
new file mode 100644
index 00000000000..3a73747a978
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DisconnectRep.cpp
@@ -0,0 +1,30 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/DisconnectRep.hpp>
+
+bool
+printDISCONNECT_REP(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const DisconnectRep * const sig = (DisconnectRep *) theData;
+
+ fprintf(output, " NodeId: %d, ErrorCode: %d\n",
+ sig->nodeId, sig->err);
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DropIndx.cpp b/storage/ndb/src/common/debugger/signaldata/DropIndx.cpp
new file mode 100644
index 00000000000..0d59a981a18
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DropIndx.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/DropIndx.hpp>
+
+bool printDROP_INDX_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const DropIndxReq * const sig = (DropIndxReq *) theData;
+
+ return false;
+}
+
+bool printDROP_INDX_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const DropIndxConf * const sig = (DropIndxConf *) theData;
+
+ return false;
+}
+
+bool printDROP_INDX_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+// const DropIndxRef * const sig = (DropIndxRef *) theData;
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DropTab.cpp b/storage/ndb/src/common/debugger/signaldata/DropTab.cpp
new file mode 100644
index 00000000000..83c95b0e344
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DropTab.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/DropTab.hpp>
+
+bool
+printDROP_TAB_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const DropTabReq * const sig = (DropTabReq *) theData;
+
+ fprintf(output,
+ " senderRef: %x senderData: %d TableId: %d requestType: %d\n",
+ sig->senderRef, sig->senderData, sig->tableId, sig->requestType);
+ return true;
+}
+
+bool printDROP_TAB_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const DropTabConf * const sig = (DropTabConf *) theData;
+
+ fprintf(output,
+ " senderRef: %x senderData: %d TableId: %d\n",
+ sig->senderRef, sig->senderData, sig->tableId);
+
+ return true;
+}
+
+bool printDROP_TAB_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const DropTabRef * const sig = (DropTabRef *) theData;
+
+ fprintf(output,
+ " senderRef: %x senderData: %d TableId: %d errorCode: %d\n",
+ sig->senderRef, sig->senderData, sig->tableId, sig->errorCode);
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/DropTrig.cpp b/storage/ndb/src/common/debugger/signaldata/DropTrig.cpp
new file mode 100644
index 00000000000..54e8734439f
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/DropTrig.cpp
@@ -0,0 +1,89 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/DropTrig.hpp>
+
+bool printDROP_TRIG_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const DropTrigReq * const sig = (DropTrigReq *) theData;
+
+ //char triggerName[MAX_TAB_NAME_SIZE];
+ //char triggerType[32];
+ //char triggerActionTime[32];
+ //char triggerEvent[32];
+
+ //sig->getTriggerName((char *) &triggerName);
+ //switch(sig->getTriggerType()) {
+ //case(TriggerType::SECONDARY_INDEX):
+ //strcpy(triggerType, "SECONDARY_INDEX");
+ //break;
+ //case(TriggerType::SUBSCRIPTION):
+ //strcpy(triggerType, "SUBSCRIPTION");
+ //break;
+ //default:
+ //strcpy(triggerType, "UNSUPPORTED");
+ //}
+ //strcpy(triggerActionTime,
+ //(sig->getTriggerActionTime() == TriggerActionTime::BEFORE)?
+ //"BEFORE":"AFTER");
+ //switch(sig->getTriggerEvent()) {
+ //case (TriggerEvent::TE_INSERT):
+ //strcpy(triggerEvent, "INSERT");
+ //break;
+ //case(TriggerEvent::TE_DELETE):
+ //strcpy(triggerEvent, "DELETE");
+ //break;
+ //case(TriggerEvent::TE_UPDATE):
+ //strcpy(triggerEvent, "UPDATE");
+ //break;
+ //}
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ //fprintf(output, "Trigger name: \"%s\"\n", triggerName);
+ //fprintf(output, "Type: %s, ", triggerType);
+ //fprintf(output, "Action: %s, ", triggerActionTime);
+ //fprintf(output, "Event: %s, ", triggerEvent);
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Table id: %u, ", sig->getTableId());
+ fprintf(output, "\n");
+
+ return false;
+}
+
+bool printDROP_TRIG_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const DropTrigConf * const sig = (DropTrigConf *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Table id: %u, ", sig->getTableId());
+ fprintf(output, "\n");
+
+ return false;
+}
+
+bool printDROP_TRIG_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const DropTrigRef * const sig = (DropTrigRef *) theData;
+
+ fprintf(output, "User: %u, ", sig->getUserRef());
+ fprintf(output, "Trigger id: %u, ", sig->getTriggerId());
+ fprintf(output, "Table id: %u, ", sig->getTableId());
+ fprintf(output, "Error code: %u, ", sig->getErrorCode());
+ fprintf(output, "\n");
+
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FailRep.cpp b/storage/ndb/src/common/debugger/signaldata/FailRep.cpp
new file mode 100644
index 00000000000..d70912fe8c7
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FailRep.cpp
@@ -0,0 +1,31 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FailRep.hpp>
+
+bool
+printFAIL_REP(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const FailRep * const sig = (FailRep *) theData;
+
+ fprintf(output, " FailedNode: %d, FailCause: %d\n",
+ sig->failNodeId, sig->failCause);
+
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FireTrigOrd.cpp b/storage/ndb/src/common/debugger/signaldata/FireTrigOrd.cpp
new file mode 100644
index 00000000000..d86aa2e06de
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FireTrigOrd.cpp
@@ -0,0 +1,56 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/FireTrigOrd.hpp>
+#include <RefConvert.hpp>
+
+static
+const char *
+trigEvent(Uint32 i){
+ switch(i){
+ case TriggerEvent::TE_INSERT:
+ return "insert";
+ break;
+ case TriggerEvent::TE_UPDATE:
+ return "update";
+ break;
+ case TriggerEvent::TE_DELETE:
+ return "delete";
+ break;
+ }
+ return "UNKNOWN";
+}
+
+bool
+printFIRE_TRIG_ORD(FILE * output, const Uint32 * theData, Uint32 len,
+ Uint16 receiverBlockNo)
+{
+ const FireTrigOrd * const sig = (FireTrigOrd *) theData;
+
+ fprintf(output, " TriggerId: %d TriggerEvent: %s\n",
+ sig->getTriggerId(),
+ trigEvent(sig->getTriggerEvent()));
+ fprintf(output, " UserRef: (%d, %d) User data: %x\n",
+ refToNode(sig->getUserRef()),
+ refToBlock(sig->getUserRef()),
+ sig->getConnectionPtr());
+ fprintf(output, " Signal: PK=%d BEFORE=%d AFTER=%d\n",
+ sig->getNoOfPrimaryKeyWords(),
+ sig->getNoOfBeforeValueWords(),
+ sig->getNoOfAfterValueWords());
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FsAppendReq.cpp b/storage/ndb/src/common/debugger/signaldata/FsAppendReq.cpp
new file mode 100644
index 00000000000..6e443ffe5fc
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FsAppendReq.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FsAppendReq.hpp>
+
+bool
+printFSAPPENDREQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ bool ret = true;
+
+ const FsAppendReq * const sig = (FsAppendReq *) theData;
+
+ fprintf(output, " FilePointer: %d\n", sig->filePointer);
+ fprintf(output, " UserReference: H\'%.8x, UserPointer: H\'%.8x\n",
+ sig->userReference, sig->userPointer);
+
+ fprintf(output, " varIndex: %d offset: %d size: %d\n",
+ sig->varIndex,
+ sig->offset,
+ sig->size);
+ return ret;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FsCloseReq.cpp b/storage/ndb/src/common/debugger/signaldata/FsCloseReq.cpp
new file mode 100644
index 00000000000..df9f3cc9fbc
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FsCloseReq.cpp
@@ -0,0 +1,40 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FsCloseReq.hpp>
+
+bool
+printFSCLOSEREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const FsCloseReq * const sig = (FsCloseReq *) theData;
+
+ fprintf(output, " UserPointer: %d\n",
+ sig->userPointer);
+ fprintf(output, " FilePointer: %d\n",
+ sig->filePointer);
+ fprintf(output, " UserReference: H\'%.8x\n",
+ sig->userReference);
+
+ fprintf(output, " Flags: H\'%.8x, ", sig->fileFlag);
+ if (sig->getRemoveFileFlag(sig->fileFlag))
+ fprintf(output, "Remove file");
+ else
+ fprintf(output, "Don't remove file");
+ fprintf(output, "\n");
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FsConf.cpp b/storage/ndb/src/common/debugger/signaldata/FsConf.cpp
new file mode 100644
index 00000000000..f0ab57aadcf
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FsConf.cpp
@@ -0,0 +1,33 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FsConf.hpp>
+
+bool
+printFSCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const FsConf * const sig = (FsConf *) theData;
+
+ fprintf(output, " UserPointer: %d\n", sig->userPointer);
+
+ if (len > 1){
+ // Only valid if this is a FSOPENCONF
+ fprintf(output, " FilePointer: %d\n", sig->filePointer);
+ }
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FsOpenReq.cpp b/storage/ndb/src/common/debugger/signaldata/FsOpenReq.cpp
new file mode 100644
index 00000000000..31d351a8a84
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FsOpenReq.cpp
@@ -0,0 +1,59 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FsOpenReq.hpp>
+
+bool
+printFSOPENREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const FsOpenReq * const sig = (FsOpenReq *) theData;
+
+
+ fprintf(output, " UserReference: H\'%.8x, userPointer: H\'%.8x\n",
+ sig->userReference, sig->userPointer);
+ fprintf(output, " FileNumber[1-4]: H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n",
+ sig->fileNumber[0], sig->fileNumber[1], sig->fileNumber[2], sig->fileNumber[3]);
+ fprintf(output, " FileFlags: H\'%.8x ",
+ sig->fileFlags);
+
+ // File open mode must be one of ReadOnly, WriteOnly or ReadWrite
+ const Uint32 flags = sig->fileFlags;
+ switch(flags & 3){
+ case FsOpenReq::OM_READONLY:
+ fprintf(output, "Open read only");
+ break;
+ case FsOpenReq::OM_WRITEONLY:
+ fprintf(output, "Open write only");
+ break;
+ case FsOpenReq::OM_READWRITE:
+ fprintf(output, "Open read and write");
+ break;
+ default:
+ fprintf(output, "Open mode unknown!");
+ }
+
+ if (flags & FsOpenReq::OM_CREATE)
+ fprintf(output, ", Create new file");
+ if (flags & FsOpenReq::OM_TRUNCATE)
+ fprintf(output, ", Truncate existing file");
+ if (flags & FsOpenReq::OM_APPEND)
+ fprintf(output, ", Append");
+
+ fprintf(output, "\n");
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp b/storage/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp
new file mode 100644
index 00000000000..a9f240d3cb4
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp
@@ -0,0 +1,85 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FsReadWriteReq.hpp>
+
+bool
+printFSREADWRITEREQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ bool ret = true;
+
+ const FsReadWriteReq * const sig = (FsReadWriteReq *) theData;
+
+ fprintf(output, " UserPointer: %d\n", sig->userPointer);
+ fprintf(output, " FilePointer: %d\n", sig->filePointer);
+ fprintf(output, " UserReference: H\'%.8x", sig->userReference);
+
+ fprintf(output, " Operation flag: H\'%.8x (", sig->operationFlag);
+ if (sig->getSyncFlag(sig->operationFlag))
+ fprintf(output, "Sync,");
+ else
+ fprintf(output, "No sync,");
+
+ fprintf(output, " Format=");
+ switch(sig->getFormatFlag(sig->operationFlag)){
+ case FsReadWriteReq::fsFormatListOfPairs:
+ fprintf(output, "List of pairs)\n");
+ break;
+ case FsReadWriteReq::fsFormatArrayOfPages:
+ fprintf(output, "Array of pages)\n");
+ break;
+ case FsReadWriteReq::fsFormatListOfMemPages:
+ fprintf(output, "List of mem pages)\n");
+ break;
+ default:
+ fprintf(output, "fsFormatMax not handled\n");
+ ret = false;
+ break;
+ }
+
+ fprintf(output, " varIndex: %d\n",
+ sig->varIndex);
+ fprintf(output, " numberOfPages: %d\n",
+ sig->numberOfPages);
+ fprintf(output, " pageData: ");
+
+ unsigned int i;
+ switch(sig->getFormatFlag(sig->operationFlag)){
+ case FsReadWriteReq::fsFormatListOfPairs:
+ for (i= 0; i < sig->numberOfPages*2; i += 2){
+ fprintf(output, " H\'%.8x, H\'%.8x\n", sig->data.pageData[i],
+ sig->data.pageData[i + 1]);
+ }
+ break;
+ case FsReadWriteReq::fsFormatArrayOfPages:
+ fprintf(output, " H\'%.8x, H\'%.8x\n", sig->data.pageData[0],
+ sig->data.pageData[1]);
+ break;
+ case FsReadWriteReq::fsFormatListOfMemPages:
+ for (i= 0; i < (sig->numberOfPages + 1); i++){
+ fprintf(output, " H\'%.8x, ", sig->data.pageData[i]);
+ }
+ break;
+ default:
+ fprintf(output, "Impossible event\n");
+ }
+
+ fprintf(output, "\n");
+ return ret;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/FsRef.cpp b/storage/ndb/src/common/debugger/signaldata/FsRef.cpp
new file mode 100644
index 00000000000..ccf3d6da9c8
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/FsRef.cpp
@@ -0,0 +1,75 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/FsRef.hpp>
+
+bool
+printFSREF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ bool ret = true;
+
+ const FsRef * const sig = (FsRef *) theData;
+
+ fprintf(output, " UserPointer: %d\n",
+ sig->userPointer);
+
+ fprintf(output, " ErrorCode: %d, ", sig->errorCode);
+ switch (sig->getErrorCode(sig->errorCode)){
+ case FsRef::fsErrNone:
+ fprintf(output, "No error");
+ break;
+ case FsRef::fsErrHardwareFailed:
+ fprintf(output, "Hardware failure!");
+ break;
+ case FsRef::fsErrUserError:
+ fprintf(output, "User error!");
+ break;
+ case FsRef::fsErrEnvironmentError:
+ fprintf(output, "Environment error!");
+ break;
+ case FsRef::fsErrTemporaryNotAccessible:
+ fprintf(output, "Temporary not accesible!");
+ break;
+ case FsRef::fsErrNoSpaceLeftOnDevice:
+ fprintf(output, "No space left on device!");
+ break;
+ case FsRef::fsErrPermissionDenied:
+ fprintf(output, "Permission denied!");
+ break;
+ case FsRef::fsErrInvalidParameters:
+ fprintf(output, "Invalid parameters!");
+ break;
+ case FsRef::fsErrNoMoreResources:
+ fprintf(output, "No more resources!");
+ break;
+ case FsRef::fsErrFileDoesNotExist:
+ fprintf(output, "File does not exist!");
+ break;
+
+ case FsRef::fsErrUnknown:
+ default:
+ fprintf(output, "Unknown!");
+ ret = false;
+ break;
+ }
+ fprintf(output, "\n");
+ fprintf(output, " OS ErrorCode: %d \n", sig->osErrorCode);
+
+ return ret;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/GCPSave.cpp b/storage/ndb/src/common/debugger/signaldata/GCPSave.cpp
new file mode 100644
index 00000000000..7566f004bfd
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/GCPSave.cpp
@@ -0,0 +1,78 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/GCPSave.hpp>
+#include <RefConvert.hpp>
+
+bool
+printGCPSaveReq(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 receiverBlockNo){
+
+ GCPSaveReq * sr = (GCPSaveReq*)theData;
+
+ fprintf(output, " dihBlockRef = (%d, %d) dihPtr = %d gci = %d\n",
+ refToBlock(sr->dihBlockRef), refToNode(sr->dihBlockRef),
+ sr->dihPtr, sr->gci);
+
+ return true;
+}
+
+bool
+printGCPSaveRef(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 receiverBlockNo){
+
+ GCPSaveRef * sr = (GCPSaveRef*)theData;
+
+ fprintf(output, " nodeId = %d dihPtr = %d gci = %d reason: ",
+ sr->nodeId,
+ sr->dihPtr, sr->gci);
+
+ switch(sr->errorCode){
+ case GCPSaveRef::NodeShutdownInProgress:
+ fprintf(output, "NodeShutdownInProgress\n");
+ break;
+ case GCPSaveRef::FakedSignalDueToNodeFailure:
+ fprintf(output, "FakedSignalDueToNodeFailure\n");
+ break;
+ default:
+ fprintf(output, "Unknown reason: %d\n", sr->errorCode);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+printGCPSaveConf(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 receiverBlockNo){
+
+ GCPSaveConf * sr = (GCPSaveConf*)theData;
+
+ fprintf(output, " nodeId = %d dihPtr = %d gci = %d\n",
+ sr->nodeId,
+ sr->dihPtr, sr->gci);
+
+ return true;
+}
+
+
diff --git a/storage/ndb/src/common/debugger/signaldata/IndxAttrInfo.cpp b/storage/ndb/src/common/debugger/signaldata/IndxAttrInfo.cpp
new file mode 100755
index 00000000000..2ef5feaada7
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/IndxAttrInfo.cpp
@@ -0,0 +1,31 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/IndxAttrInfo.hpp>
+
+bool
+printINDXATTRINFO(FILE * output, const Uint32 * theData, Uint32 len,
+ Uint16 receiverBlockNo)
+{
+// const IndxAttrInfo * const sig = (IndxAttrInfo *) theData;
+
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, " H\'%.8x", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/IndxKeyInfo.cpp b/storage/ndb/src/common/debugger/signaldata/IndxKeyInfo.cpp
new file mode 100755
index 00000000000..6fe5567188d
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/IndxKeyInfo.cpp
@@ -0,0 +1,31 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/IndxKeyInfo.hpp>
+
+bool
+printINDXKEYINFO(FILE * output, const Uint32 * theData, Uint32 len,
+ Uint16 receiverBlockNo)
+{
+// const IndxKeyInfo * const sig = (IndxKeyInfo *) theData;
+
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, " H\'%.8x", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/LCP.cpp b/storage/ndb/src/common/debugger/signaldata/LCP.cpp
new file mode 100644
index 00000000000..6b4bb13e2cd
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/LCP.cpp
@@ -0,0 +1,89 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <RefConvert.hpp>
+#include <signaldata/LCP.hpp>
+#include <DebuggerNames.hpp>
+
+bool
+printSTART_LCP_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ const StartLcpReq * const sig = (StartLcpReq *) theData;
+
+ char buf1[8*_NDB_NODE_BITMASK_SIZE+1];
+ char buf2[8*_NDB_NODE_BITMASK_SIZE+1];
+ fprintf(output,
+ " Sender: %d LcpId: %d\n"
+ " ParticipatingDIH = %s\n"
+ " ParticipatingLQH = %s\n",
+ refToNode(sig->senderRef), sig->lcpId,
+ sig->participatingDIH.getText(buf1),
+ sig->participatingLQH.getText(buf2));
+
+ return true;
+}
+
+bool
+printSTART_LCP_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ const StartLcpConf * const sig = (StartLcpConf *) theData;
+
+ fprintf(output, " Sender: %d LcpId: %d\n",
+ refToNode(sig->senderRef), sig->lcpId);
+
+ return true;
+}
+
+bool
+printLCP_FRAG_ORD(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ const LcpFragOrd * const sig = (LcpFragOrd *) theData;
+
+ fprintf(output, " LcpId: %d LcpNo: %d Table: %d Fragment: %d\n",
+ sig->lcpId, sig->lcpNo, sig->tableId, sig->fragmentId);
+
+ fprintf(output, " KeepGCI: %d LastFragmentFlag: %d\n",
+ sig->keepGci, sig->lastFragmentFlag);
+ return true;
+}
+
+bool
+printLCP_FRAG_REP(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ const LcpFragRep * const sig = (LcpFragRep *) theData;
+
+ fprintf(output, " LcpId: %d LcpNo: %d NodeId: %d Table: %d Fragment: %d\n",
+ sig->lcpId, sig->lcpNo, sig->nodeId, sig->tableId, sig->fragId);
+ fprintf(output, " Max GCI Started: %d Max GCI Completed: %d\n",
+ sig->maxGciStarted, sig->maxGciCompleted);
+ return true;
+}
+
+bool
+printLCP_COMPLETE_REP(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo){
+
+ const LcpCompleteRep * const sig = (LcpCompleteRep *) theData;
+
+ fprintf(output, " LcpId: %d NodeId: %d Block: %s\n",
+ sig->lcpId, sig->nodeId, getBlockName(sig->blockNo));
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/LqhFrag.cpp b/storage/ndb/src/common/debugger/signaldata/LqhFrag.cpp
new file mode 100644
index 00000000000..6d727959a67
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/LqhFrag.cpp
@@ -0,0 +1,61 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/LqhFrag.hpp>
+
+bool
+printLQH_FRAG_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 recB){
+ LqhFragReq* sig = (LqhFragReq*)theData;
+
+ fprintf(output, " senderData: %d senderRef: %x",
+ sig->senderData, sig->senderRef);
+ fprintf(output, " tableId: %d fragmentId: %d tableType: %d",
+ sig->tableId, sig->fragmentId, sig->tableType);
+ if (sig->primaryTableId == RNIL)
+ fprintf(output, " primaryTableId: RNIL\n");
+ else
+ fprintf(output, " primaryTableId: %d\n", sig->primaryTableId);
+ fprintf(output, " localKeyLength: %d maxLoadFactor: %d minLoadFactor: %d\n",
+ sig->localKeyLength, sig->maxLoadFactor, sig->minLoadFactor);
+ fprintf(output, " kValue: %d lh3DistrBits: %d lh3PageBits: %d\n",
+ sig->kValue, sig->lh3DistrBits, sig->lh3PageBits);
+
+ fprintf(output, " noOfAttributes: %d noOfNullAttributes: %d keyLength: %d\n",
+ sig->noOfAttributes, sig->noOfNullAttributes, sig->keyLength);
+
+ fprintf(output, " noOfPagesToPreAllocate: %d schemaVersion: %d nextLCP: %d\n",
+ sig->noOfPagesToPreAllocate, sig->schemaVersion, sig->nextLCP);
+
+ return true;
+}
+bool
+printLQH_FRAG_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 rec){
+ LqhFragConf* sig = (LqhFragConf*)theData;
+
+ fprintf(output, " senderData: %d lqhFragPtr: %d\n",
+ sig->senderData, sig->lqhFragPtr);
+ return true;
+}
+
+bool
+printLQH_FRAG_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 rec){
+ LqhFragRef* sig = (LqhFragRef*)theData;
+
+ fprintf(output, " senderData: %d errorCode: %d\n",
+ sig->senderData, sig->errorCode);
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/LqhKey.cpp b/storage/ndb/src/common/debugger/signaldata/LqhKey.cpp
new file mode 100644
index 00000000000..2796437fd8b
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/LqhKey.cpp
@@ -0,0 +1,161 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/LqhKey.hpp>
+
+bool
+printLQHKEYREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const LqhKeyReq * const sig = (LqhKeyReq *) theData;
+
+ fprintf(output,
+ " ClientPtr = H\'%.8x hashValue = H\'%.8x tcBlockRef = H\'%.8x\n"
+ " transId1 = H\'%.8x transId2 = H\'%.8x savePointId = H\'%.8x\n",
+ sig->clientConnectPtr, // DATA 0
+ sig->hashValue, // DATA 2
+ sig->tcBlockref, // DATA 4
+ sig->transId1, // DATA 7
+ sig->transId2, // DATA 8
+ sig->savePointId // DATA 9
+ );
+
+ const Uint32 reqInfo = sig->requestInfo;
+ const Uint32 attrLen = sig->attrLen;
+
+ fprintf(output,
+ " Op: %d Lock: %d Flags: ",
+ LqhKeyReq::getOperation(reqInfo),
+ LqhKeyReq::getLockType(reqInfo));
+ if(LqhKeyReq::getSimpleFlag(reqInfo))
+ fprintf(output, "Simple ");
+ if(LqhKeyReq::getDirtyFlag(reqInfo))
+ fprintf(output, "Dirty ");
+ if(LqhKeyReq::getInterpretedFlag(reqInfo))
+ fprintf(output, "Interpreted ");
+ if(LqhKeyReq::getScanTakeOverFlag(attrLen))
+ fprintf(output, "ScanTakeOver ");
+ if(LqhKeyReq::getMarkerFlag(reqInfo))
+ fprintf(output, "CommitAckMarker ");
+
+ fprintf(output, "ScanInfo/noFiredTriggers: H\'%x\n", sig->scanInfo);
+
+ fprintf(output,
+ " AttrLen: %d (%d in this) KeyLen: %d TableId: %d SchemaVer: %d\n",
+ LqhKeyReq::getAttrLen(attrLen),
+ LqhKeyReq::getAIInLqhKeyReq(reqInfo),
+ LqhKeyReq::getKeyLen(reqInfo),
+ LqhKeyReq::getTableId(sig->tableSchemaVersion),
+ LqhKeyReq::getSchemaVersion(sig->tableSchemaVersion));
+
+ fprintf(output,
+ " FragId: %d ReplicaNo: %d LastReplica: %d NextNodeId: %d\n",
+ LqhKeyReq::getFragmentId(sig->fragmentData),
+ LqhKeyReq::getSeqNoReplica(reqInfo),
+ LqhKeyReq::getLastReplicaNo(reqInfo),
+ LqhKeyReq::getNextReplicaNodeId(sig->fragmentData));
+
+ bool printed = false;
+ Uint32 nextPos = LqhKeyReq::getApplicationAddressFlag(reqInfo) << 1;
+ if(nextPos != 0){
+ fprintf(output,
+ " ApiRef: H\'%.8x ApiOpRef: H\'%.8x",
+ sig->variableData[0],
+ sig->variableData[1]);
+ printed = true;
+ }
+
+ if(LqhKeyReq::getSameClientAndTcFlag(reqInfo)){
+ fprintf(output, " TcOpRec: H\'%.8x", sig->variableData[nextPos]);
+ nextPos++;
+ printed = true;
+ }
+
+ Uint32 tmp = LqhKeyReq::getLastReplicaNo(reqInfo) -
+ LqhKeyReq::getSeqNoReplica(reqInfo);
+ if(tmp > 1){
+ NodeId node2 = sig->variableData[nextPos] & 0xffff;
+ NodeId node3 = sig->variableData[nextPos] >> 16;
+ fprintf(output, " NextNodeId2: %d NextNodeId3: %d",
+ node2, node3);
+ nextPos ++;
+ printed = true;
+ }
+ if(printed)
+ fprintf(output, "\n");
+
+ printed = false;
+ if(LqhKeyReq::getStoredProcFlag(attrLen)){
+ fprintf(output, " StoredProcId: %d", sig->variableData[nextPos]);
+ nextPos++;
+ printed = true;
+ }
+
+ if(LqhKeyReq::getReturnedReadLenAIFlag(reqInfo)){
+ fprintf(output, " ReturnedReadLenAI: %d",
+ sig->variableData[nextPos]);
+ nextPos++;
+ printed = true;
+ }
+
+ const UintR keyLen = LqhKeyReq::getKeyLen(reqInfo);
+ if(keyLen > 0){
+ fprintf(output, " KeyInfo: ");
+ for(UintR i = 0; i<keyLen && i<4; i++, nextPos++)
+ fprintf(output, "H\'%.8x ", sig->variableData[nextPos]);
+ fprintf(output, "\n");
+ }
+
+ if(!LqhKeyReq::getInterpretedFlag(reqInfo)){
+ fprintf(output, " AttrInfo: ");
+ for(int i = 0; i<LqhKeyReq::getAIInLqhKeyReq(reqInfo); i++, nextPos++)
+ fprintf(output, "H\'%.8x ", sig->variableData[nextPos]);
+ fprintf(output, "\n");
+ } else {
+ fprintf(output, " InitialReadSize: %d InterpretedSize: %d "
+ "FinalUpdateSize: %d FinalReadSize: %d SubroutineSize: %d\n",
+ sig->variableData[nextPos+0], sig->variableData[nextPos+1],
+ sig->variableData[nextPos+2], sig->variableData[nextPos+3],
+ sig->variableData[nextPos+4]);
+ nextPos += 5;
+ }
+ return true;
+}
+
+bool
+printLQHKEYCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+// const LqhKeyConf * const sig = (LqhKeyConf *) theData;
+
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
+
+bool
+printLQHKEYREF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+// const LqhKeyRef * const sig = (LqhKeyRef *) theData;
+
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/LqhTrans.cpp b/storage/ndb/src/common/debugger/signaldata/LqhTrans.cpp
new file mode 100644
index 00000000000..8282530cae6
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/LqhTrans.cpp
@@ -0,0 +1,40 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/LqhTransConf.hpp>
+
+bool
+printLQH_TRANSCONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const LqhTransConf * const sig = (LqhTransConf *)theData;
+ fprintf(output, " tcRef: %x\n", sig->tcRef);
+ fprintf(output, " lqhNodeId: %x\n", sig->lqhNodeId);
+ fprintf(output, " operationStatus: %x\n", sig->operationStatus);
+ fprintf(output, " transId1: %x\n", sig->transId1);
+ fprintf(output, " transId2: %x\n", sig->transId2);
+ fprintf(output, " apiRef: %x\n", sig->apiRef);
+ fprintf(output, " apiOpRec: %x\n", sig->apiOpRec);
+ fprintf(output, " lqhConnectPtr: %x\n", sig->lqhConnectPtr);
+ fprintf(output, " oldTcOpRec: %x\n", sig->oldTcOpRec);
+ fprintf(output, " requestInfo: %x\n", sig->requestInfo);
+ fprintf(output, " gci: %x\n", sig->gci);
+ fprintf(output, " nextNodeId1: %x\n", sig->nextNodeId1);
+ fprintf(output, " nextNodeId2: %x\n", sig->nextNodeId2);
+ fprintf(output, " nextNodeId3: %x\n", sig->nextNodeId3);
+ fprintf(output, " tableId: %x\n", sig->tableId);
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/Makefile.am b/storage/ndb/src/common/debugger/signaldata/Makefile.am
new file mode 100644
index 00000000000..9146d552568
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/Makefile.am
@@ -0,0 +1,47 @@
+
+noinst_LTLIBRARIES = libsignaldataprint.la
+
+libsignaldataprint_la_SOURCES = \
+ TcKeyReq.cpp TcKeyConf.cpp TcKeyRef.cpp \
+ TcRollbackRep.cpp \
+ TupKey.cpp TupCommit.cpp LqhKey.cpp \
+ FsOpenReq.cpp FsCloseReq.cpp FsRef.cpp FsConf.cpp FsReadWriteReq.cpp\
+ SignalDataPrint.cpp SignalNames.cpp \
+ ContinueB.cpp DihContinueB.cpp NdbfsContinueB.cpp \
+ CloseComReqConf.cpp PackedSignal.cpp PrepFailReqRef.cpp \
+ GCPSave.cpp DictTabInfo.cpp \
+ AlterTable.cpp AlterTab.cpp \
+ CreateTrig.cpp AlterTrig.cpp DropTrig.cpp \
+ FireTrigOrd.cpp TrigAttrInfo.cpp \
+ CreateIndx.cpp AlterIndx.cpp DropIndx.cpp TcIndx.cpp \
+ IndxKeyInfo.cpp IndxAttrInfo.cpp \
+ FsAppendReq.cpp ScanTab.cpp \
+ BackupImpl.cpp BackupSignalData.cpp \
+ UtilSequence.cpp UtilPrepare.cpp UtilDelete.cpp UtilExecute.cpp \
+ LqhFrag.cpp DropTab.cpp PrepDropTab.cpp LCP.cpp MasterLCP.cpp \
+ CopyGCI.cpp SystemError.cpp StartRec.cpp NFCompleteRep.cpp \
+ FailRep.cpp DisconnectRep.cpp SignalDroppedRep.cpp \
+ SumaImpl.cpp NdbSttor.cpp CreateFragmentation.cpp \
+ UtilLock.cpp TuxMaint.cpp AccLock.cpp \
+ LqhTrans.cpp ReadNodesConf.cpp CntrStart.cpp \
+ ScanFrag.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libsignaldataprint.dsp
+
+libsignaldataprint.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libsignaldataprint_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/debugger/signaldata/MasterLCP.cpp b/storage/ndb/src/common/debugger/signaldata/MasterLCP.cpp
new file mode 100644
index 00000000000..078b92f6f2e
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/MasterLCP.cpp
@@ -0,0 +1,87 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/MasterLCP.hpp>
+#include <RefConvert.hpp>
+
+static
+void
+print(char *buf, size_t buf_len, MasterLCPConf::State s){
+ switch(s){
+ case MasterLCPConf::LCP_STATUS_IDLE:
+ BaseString::snprintf(buf, buf_len, "LCP_STATUS_IDLE");
+ break;
+ case MasterLCPConf::LCP_STATUS_ACTIVE:
+ BaseString::snprintf(buf, buf_len, "LCP_STATUS_ACTIVE");
+ break;
+ case MasterLCPConf::LCP_TAB_COMPLETED:
+ BaseString::snprintf(buf, buf_len, "LCP_TAB_COMPLETED");
+ break;
+ case MasterLCPConf::LCP_TAB_SAVED:
+ BaseString::snprintf(buf, buf_len, "LCP_TAB_SAVED");
+ break;
+ }
+}
+
+NdbOut &
+operator<<(NdbOut& out, const MasterLCPConf::State& s){
+ static char buf[255];
+ print(buf, sizeof(buf), s);
+ out << buf;
+ return out;
+}
+
+bool
+printMASTER_LCP_CONF(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+
+ MasterLCPConf * sig = (MasterLCPConf *)&theData[0];
+
+ static char buf[255];
+ print(buf, sizeof(buf), (MasterLCPConf::State)sig->lcpState);
+ fprintf(output, " senderNode=%d failedNode=%d SenderState=%s\n",
+ sig->senderNodeId, sig->failedNodeId, buf);
+ return true;
+}
+
+bool
+printMASTER_LCP_REQ(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+
+ MasterLCPReq * sig = (MasterLCPReq *)&theData[0];
+
+ fprintf(output, " masterRef=(node=%d, block=%d), failedNode=%d\n",
+ refToNode(sig->masterRef), refToBlock(sig->masterRef),
+ sig->failedNodeId);
+ return true;
+}
+
+bool
+printMASTER_LCP_REF(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+
+ MasterLCPRef * sig = (MasterLCPRef *)&theData[0];
+ fprintf(output, " senderNode=%d failedNode=%d\n",
+ sig->senderNodeId, sig->failedNodeId);
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/NFCompleteRep.cpp b/storage/ndb/src/common/debugger/signaldata/NFCompleteRep.cpp
new file mode 100644
index 00000000000..f2d6f2f104a
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/NFCompleteRep.cpp
@@ -0,0 +1,44 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <DebuggerNames.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+
+bool
+printNF_COMPLETE_REP(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+
+ NFCompleteRep * sig = (NFCompleteRep*)theData;
+ const char * who = getBlockName(sig->blockNo, 0);
+
+ if(who == 0){
+ fprintf(output,
+ " Node: %d has completed failure of node %d\n",
+ sig->nodeId, sig->failedNodeId);
+ } else {
+ fprintf(output,
+ " Node: %d block: %s has completed failure of node %d\n",
+ sig->nodeId, who, sig->failedNodeId);
+ }
+
+ fprintf(output, "Sent from line: %d\n",
+ sig->from);
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/NdbSttor.cpp b/storage/ndb/src/common/debugger/signaldata/NdbSttor.cpp
new file mode 100644
index 00000000000..9fd081313be
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/NdbSttor.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/NdbSttor.hpp>
+
+bool
+printNDB_STTOR(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const NdbSttor * const sig = (NdbSttor *)theData;
+ fprintf(output, " senderRef: %x\n", sig->senderRef);
+ fprintf(output, " nodeId: %x\n", sig->nodeId);
+ fprintf(output, " internalStartPhase: %x\n", sig->internalStartPhase);
+ fprintf(output, " typeOfStart: %x\n", sig->typeOfStart);
+ fprintf(output, " masterNodeId: %x\n", sig->masterNodeId);
+
+ int left = len - NdbSttor::SignalLength;
+ if(left > 0){
+ fprintf(output, " config: ");
+ for(int i = 0; i<left; i++){
+ fprintf(output, "%x ", sig->config[i]);
+ if(((i + 1) % 7) == 0 && (i+1) < left){
+ fprintf(output, "\n config: ");
+ }
+ }
+ fprintf(output, "\n");
+ }
+ return true;
+}
+
+bool
+printNDB_STTORRY(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const NdbSttorry * const sig = (NdbSttorry *)theData;
+ fprintf(output, " senderRef: %x\n", sig->senderRef);
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/NdbfsContinueB.cpp b/storage/ndb/src/common/debugger/signaldata/NdbfsContinueB.cpp
new file mode 100644
index 00000000000..9f55efae017
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/NdbfsContinueB.cpp
@@ -0,0 +1,41 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <signaldata/NdbfsContinueB.hpp>
+
+bool
+printCONTINUEB_NDBFS(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 not_used){
+
+ (void)not_used;
+
+ switch (theData[0]) {
+ case NdbfsContinueB::ZSCAN_MEMORYCHANNEL_10MS_DELAY:
+ fprintf(output, " Scanning the memory channel every 10ms\n");
+ return true;
+ break;
+ case NdbfsContinueB::ZSCAN_MEMORYCHANNEL_NO_DELAY:
+ fprintf(output, " Scanning the memory channel again with no delay\n");
+ return true;
+ break;
+ default:
+ fprintf(output, " Default system error lab...\n");
+ return false;
+ break;
+ }//switch
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp b/storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp
new file mode 100644
index 00000000000..f0f7aee74e4
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp
@@ -0,0 +1,104 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/PackedSignal.hpp>
+#include <signaldata/LqhKey.hpp>
+#include <debugger/DebuggerNames.hpp>
+
+bool
+printPACKED_SIGNAL(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ fprintf(output, "--------- Begin Packed Signals --------\n");
+ // Print each signal separately
+ for (i = 0; i < len;) {
+ switch (PackedSignal::getSignalType(theData[i])) {
+ case ZCOMMIT: {
+ Uint32 signalLength = 4;
+ fprintf(output, "--------------- Signal ----------------\n");
+ fprintf(output, "r.bn: %u \"%s\", length: %u \"COMMIT\"\n",
+ receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+ fprintf(output, "Signal data: ");
+ for(Uint32 j = 0; j < signalLength; j++)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ break;
+ }
+ case ZCOMPLETE: {
+ Uint32 signalLength = 3;
+ fprintf(output, "--------------- Signal ----------------\n");
+ fprintf(output, "r.bn: %u \"%s\", length: %u \"COMPLETE\"\n",
+ receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+ fprintf(output, "Signal data: ");
+ for(Uint32 j = 0; j < signalLength; j++)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ break;
+ }
+ case ZCOMMITTED: {
+ Uint32 signalLength = 3;
+ fprintf(output, "--------------- Signal ----------------\n");
+ fprintf(output, "r.bn: %u \"%s\", length: %u \"COMMITTED\"\n",
+ receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+ fprintf(output, "Signal data: ");
+ for(Uint32 j = 0; j < signalLength; j++)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ break;
+ }
+ case ZCOMPLETED: {
+ Uint32 signalLength = 3;
+ fprintf(output, "--------------- Signal ----------------\n");
+ fprintf(output, "r.bn: %u \"%s\", length: %u \"COMPLETED\"\n",
+ receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+ fprintf(output, "Signal data: ");
+ for(Uint32 j = 0; j < signalLength; j++)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ break;
+ }
+ case ZLQHKEYCONF: {
+ Uint32 signalLength = LqhKeyConf::SignalLength;
+
+ fprintf(output, "--------------- Signal ----------------\n");
+ fprintf(output, "r.bn: %u \"%s\", length: %u \"LQHKEYCONF\"\n",
+ receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+ printLQHKEYCONF(output, theData + i, signalLength, receiverBlockNo);
+ i += signalLength;
+ break;
+ }
+ case ZREMOVE_MARKER: {
+ Uint32 signalLength = 2;
+ fprintf(output, "--------------- Signal ----------------\n");
+ fprintf(output, "r.bn: %u \"%s\", length: %u \"REMOVE_MARKER\"\n",
+ receiverBlockNo, getBlockName(receiverBlockNo,""), signalLength);
+ fprintf(output, "Signal data: ");
+ i++; // Skip first word!
+ for(Uint32 j = 0; j < signalLength; j++)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ break;
+ }
+ default:
+ fprintf(output, "Unknown signal type\n");
+ }
+ }//for
+ fprintf(output, "--------- End Packed Signals ----------\n");
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/PrepDropTab.cpp b/storage/ndb/src/common/debugger/signaldata/PrepDropTab.cpp
new file mode 100644
index 00000000000..59001bcd6f6
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/PrepDropTab.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/PrepDropTab.hpp>
+
+bool
+printPREP_DROP_TAB_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const PrepDropTabReq * const sig = (PrepDropTabReq *) theData;
+
+ fprintf(output,
+ " senderRef: %x senderData: %d TableId: %d\n",
+ sig->senderRef, sig->senderData, sig->tableId);
+ return true;
+}
+
+bool printPREP_DROP_TAB_CONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const PrepDropTabConf * const sig = (PrepDropTabConf *) theData;
+
+ fprintf(output,
+ " senderRef: %x senderData: %d TableId: %d\n",
+ sig->senderRef, sig->senderData, sig->tableId);
+
+ return true;
+}
+
+bool printPREP_DROP_TAB_REF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo)
+{
+ const PrepDropTabRef * const sig = (PrepDropTabRef *) theData;
+
+ fprintf(output,
+ " senderRef: %x senderData: %d TableId: %d errorCode: %d\n",
+ sig->senderRef, sig->senderData, sig->tableId, sig->errorCode);
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/PrepFailReqRef.cpp b/storage/ndb/src/common/debugger/signaldata/PrepFailReqRef.cpp
new file mode 100644
index 00000000000..2e900de8f70
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/PrepFailReqRef.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <kernel_types.h>
+#include <BlockNumbers.h>
+#include <signaldata/PrepFailReqRef.hpp>
+
+bool
+printPREPFAILREQREF(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 receiverBlockNo){
+
+ PrepFailReqRef * cc = (PrepFailReqRef*)theData;
+
+ fprintf(output, " xxxBlockRef = (%d, %d) failNo = %d noOfNodes = %d\n",
+ refToBlock(cc->xxxBlockRef), refToNode(cc->xxxBlockRef),
+ cc->failNo, cc->noOfNodes);
+
+ int hits = 0;
+ fprintf(output, " Nodes: ");
+ for(int i = 0; i<MAX_NODES; i++){
+ if(NodeBitmask::get(cc->theNodes, i)){
+ hits++;
+ fprintf(output, " %d", i);
+ }
+ if(hits == 16){
+ fprintf(output, "\n Nodes: ");
+ hits = 0;
+ }
+ }
+ if(hits != 0)
+ fprintf(output, "\n");
+
+ return true;
+}
+
+
diff --git a/storage/ndb/src/common/debugger/signaldata/ReadNodesConf.cpp b/storage/ndb/src/common/debugger/signaldata/ReadNodesConf.cpp
new file mode 100644
index 00000000000..103f4a884f1
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/ReadNodesConf.cpp
@@ -0,0 +1,24 @@
+#include <signaldata/ReadNodesConf.hpp>
+
+bool
+printREAD_NODES_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const ReadNodesConf * const sig = (ReadNodesConf *)theData;
+ fprintf(output, " noOfNodes: %x\n", sig->noOfNodes);
+ fprintf(output, " ndynamicId: %x\n", sig->ndynamicId);
+ fprintf(output, " masterNodeId: %x\n", sig->masterNodeId);
+
+ char buf[32*NdbNodeBitmask::Size+1];
+ fprintf(output, " allNodes(defined): %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->allNodes, buf));
+ fprintf(output, " inactiveNodes: %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->inactiveNodes, buf));
+ fprintf(output, " clusterNodes: %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->clusterNodes, buf));
+ fprintf(output, " startedNodes: %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->startedNodes, buf));
+ fprintf(output, " startingNodes: %s\n",
+ BitmaskImpl::getText(NdbNodeBitmask::Size, sig->startingNodes, buf));
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/ScanFrag.cpp b/storage/ndb/src/common/debugger/signaldata/ScanFrag.cpp
new file mode 100644
index 00000000000..4d19a325637
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/ScanFrag.cpp
@@ -0,0 +1,42 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <BlockNumbers.h>
+#include <signaldata/ScanTab.hpp>
+#include <signaldata/ScanFrag.hpp>
+
+bool
+printSCAN_FRAGREQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const ScanFragReq * const sig = (ScanFragReq *)theData;
+ fprintf(output, " senderData: %x\n", sig->senderData);
+ fprintf(output, " resultRef: %x\n", sig->resultRef);
+ fprintf(output, " savePointId: %x\n", sig->savePointId);
+ fprintf(output, " requestInfo: %x\n", sig->requestInfo);
+ fprintf(output, " tableId: %x\n", sig->tableId);
+ fprintf(output, " fragmentNo: %x\n", sig->fragmentNoKeyLen & 0xFFFF);
+ fprintf(output, " keyLen: %x\n", sig->fragmentNoKeyLen >> 16);
+ fprintf(output, " schemaVersion: %x\n", sig->schemaVersion);
+ fprintf(output, " transId1: %x\n", sig->transId1);
+ fprintf(output, " transId2: %x\n", sig->transId2);
+ fprintf(output, " clientOpPtr: %x\n", sig->clientOpPtr);
+ fprintf(output, " batch_size_rows: %x\n", sig->batch_size_rows);
+ fprintf(output, " batch_size_bytes: %x\n", sig->batch_size_bytes);
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/ScanTab.cpp b/storage/ndb/src/common/debugger/signaldata/ScanTab.cpp
new file mode 100644
index 00000000000..e9c5ba6cc52
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/ScanTab.cpp
@@ -0,0 +1,156 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <BlockNumbers.h>
+#include <signaldata/ScanTab.hpp>
+#include <signaldata/ScanFrag.hpp>
+
+bool
+printSCANTABREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const ScanTabReq * const sig = (ScanTabReq *) theData;
+
+ const UintR requestInfo = sig->requestInfo;
+
+ fprintf(output, " apiConnectPtr: H\'%.8x",
+ sig->apiConnectPtr);
+ fprintf(output, " requestInfo: H\'%.8x:\n", requestInfo);
+ fprintf(output, " Parallellism: %u, Batch: %u LockMode: %u Keyinfo: %u Holdlock: %u RangeScan: %u Descending: %u ReadCommitted: %u\n DistributionKeyFlag: %u",
+ sig->getParallelism(requestInfo),
+ sig->getScanBatch(requestInfo),
+ sig->getLockMode(requestInfo),
+ sig->getKeyinfoFlag(requestInfo),
+ sig->getHoldLockFlag(requestInfo),
+ sig->getRangeScanFlag(requestInfo),
+ sig->getDescendingFlag(requestInfo),
+ sig->getReadCommittedFlag(requestInfo),
+ sig->getDistributionKeyFlag(requestInfo));
+
+ if(sig->getDistributionKeyFlag(requestInfo))
+ fprintf(output, " DKey: %x", sig->distributionKey);
+
+ Uint32 keyLen = (sig->attrLenKeyLen >> 16);
+ Uint32 attrLen = (sig->attrLenKeyLen & 0xFFFF);
+ fprintf(output, " attrLen: %d, keyLen: %d tableId: %d, tableSchemaVer: %d\n",
+ attrLen, keyLen, sig->tableId, sig->tableSchemaVersion);
+
+ fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x) storedProcId: H\'%.8x\n",
+ sig->transId1, sig->transId2, sig->storedProcId);
+ fprintf(output, " batch_byte_size: %d, first_batch_size: %d\n",
+ sig->batch_byte_size, sig->first_batch_size);
+ return false;
+}
+
+bool
+printSCANTABCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const ScanTabConf * const sig = (ScanTabConf *) theData;
+
+ const UintR requestInfo = sig->requestInfo;
+
+ fprintf(output, " apiConnectPtr: H\'%.8x\n",
+ sig->apiConnectPtr);
+ fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x)\n",
+ sig->transId1, sig->transId2);
+
+ fprintf(output, " requestInfo: Eod: %d OpCount: %d\n",
+ (requestInfo & ScanTabConf::EndOfData == ScanTabConf::EndOfData),
+ (requestInfo & (~ScanTabConf::EndOfData)));
+ size_t op_count= requestInfo & (~ScanTabConf::EndOfData);
+ if(op_count){
+ fprintf(output, " Operation(s) [api tc rows len]:\n");
+ ScanTabConf::OpData * op = (ScanTabConf::OpData*)
+ (theData + ScanTabConf::SignalLength);
+ for(size_t i = 0; i<op_count; i++){
+ if(op->info != ScanTabConf::EndOfData)
+ fprintf(output, " [0x%x 0x%x %d %d]",
+ op->apiPtrI, op->tcPtrI,
+ ScanTabConf::getRows(op->info),
+ ScanTabConf::getLength(op->info));
+ else
+ fprintf(output, " [0x%x 0x%x eod]",
+ op->apiPtrI, op->tcPtrI);
+
+ op++;
+ }
+ fprintf(output, "\n");
+ }
+ return false;
+}
+
+bool
+printSCANTABREF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const ScanTabRef * const sig = (ScanTabRef *) theData;
+
+ fprintf(output, " apiConnectPtr: H\'%.8x\n",
+ sig->apiConnectPtr);
+
+ fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x)\n",
+ sig->transId1, sig->transId2);
+
+ fprintf(output, " Errorcode: %u\n", sig->errorCode);
+
+ fprintf(output, " closeNeeded: %u\n", sig->closeNeeded);
+ return false;
+}
+
+
+bool
+printSCANFRAGNEXTREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ const ScanFragNextReq * const sig = (ScanFragNextReq *) theData;
+
+ fprintf(output, " senderData: H\'%.8x\n",
+ sig->senderData);
+
+ fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x)\n",
+ sig->transId1, sig->transId2);
+
+ fprintf(output, " Close scan: %u\n", sig->closeFlag);
+
+ return false;
+}
+
+bool
+printSCANNEXTREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ if(receiverBlockNo == DBTC){
+ const ScanNextReq * const sig = (ScanNextReq *) theData;
+
+ fprintf(output, " apiConnectPtr: H\'%.8x\n",
+ sig->apiConnectPtr);
+
+ fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x) ",
+ sig->transId1, sig->transId2);
+
+ fprintf(output, " Stop this scan: %u\n", sig->stopScan);
+
+ const Uint32 * ops = theData + ScanNextReq::SignalLength;
+ if(len > ScanNextReq::SignalLength){
+ fprintf(output, " tcFragPtr(s): ");
+ for(size_t i = ScanNextReq::SignalLength; i<len; i++)
+ fprintf(output, " 0x%x", * ops++);
+ fprintf(output, "\n");
+ }
+ }
+ if (receiverBlockNo == DBLQH){
+ return printSCANFRAGNEXTREQ(output, theData, len, receiverBlockNo);
+ }
+ return false;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp b/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp
new file mode 100644
index 00000000000..ab23c04bffa
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp
@@ -0,0 +1,211 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <GlobalSignalNumbers.h>
+#include <signaldata/SignalData.hpp>
+#include <signaldata/SignalDataPrint.hpp>
+
+/**
+ * This is the register
+ */
+
+const NameFunctionPair
+SignalDataPrintFunctions[] = {
+ { GSN_TCKEYREQ, printTCKEYREQ },
+ { GSN_TCINDXREQ, printTCKEYREQ },
+ { GSN_TCKEYCONF, printTCKEYCONF },
+ { GSN_TCKEYREF, printTCKEYREF },
+ { GSN_LQHKEYREQ, printLQHKEYREQ },
+ { GSN_LQHKEYCONF, printLQHKEYCONF },
+ { GSN_LQHKEYREF, printLQHKEYREF },
+ { GSN_TUPKEYREQ, printTUPKEYREQ },
+ { GSN_TUPKEYCONF, printTUPKEYCONF },
+ { GSN_TUPKEYREF, printTUPKEYREF },
+ { GSN_TUP_COMMITREQ, printTUPCOMMITREQ },
+ { GSN_CONTINUEB, printCONTINUEB },
+ { GSN_FSOPENREQ, printFSOPENREQ },
+ { GSN_FSCLOSEREQ, printFSCLOSEREQ },
+ { GSN_FSREADREQ, printFSREADWRITEREQ },
+ { GSN_FSWRITEREQ, printFSREADWRITEREQ },
+ { GSN_FSCLOSEREF, printFSREF },
+ { GSN_FSOPENREF, printFSREF },
+ { GSN_FSWRITEREF, printFSREF },
+ { GSN_FSREADREF, printFSREF },
+ { GSN_FSSYNCREF, printFSREF },
+ { GSN_FSCLOSECONF, printFSCONF },
+ { GSN_FSOPENCONF, printFSCONF },
+ { GSN_FSWRITECONF, printFSCONF },
+ { GSN_FSREADCONF, printFSCONF },
+ { GSN_FSSYNCCONF, printFSCONF },
+ { GSN_CLOSE_COMREQ, printCLOSECOMREQCONF },
+ { GSN_CLOSE_COMCONF, printCLOSECOMREQCONF },
+ { GSN_PACKED_SIGNAL, printPACKED_SIGNAL },
+ { GSN_PREP_FAILREQ, printPREPFAILREQREF },
+ { GSN_PREP_FAILREF, printPREPFAILREQREF },
+ { GSN_ALTER_TABLE_REQ, printALTER_TABLE_REQ },
+ { GSN_ALTER_TABLE_CONF, printALTER_TABLE_CONF },
+ { GSN_ALTER_TABLE_REF, printALTER_TABLE_REF },
+ { GSN_ALTER_TAB_REQ, printALTER_TAB_REQ },
+ { GSN_ALTER_TAB_CONF, printALTER_TAB_CONF },
+ { GSN_ALTER_TAB_REF, printALTER_TAB_REF },
+ { GSN_CREATE_TRIG_REQ, printCREATE_TRIG_REQ },
+ { GSN_CREATE_TRIG_CONF, printCREATE_TRIG_CONF },
+ { GSN_CREATE_TRIG_REF, printCREATE_TRIG_REF },
+ { GSN_ALTER_TRIG_REQ, printALTER_TRIG_REQ },
+ { GSN_ALTER_TRIG_CONF, printALTER_TRIG_CONF },
+ { GSN_ALTER_TRIG_REF, printALTER_TRIG_REF },
+ { GSN_DROP_TRIG_REQ, printDROP_TRIG_REQ },
+ { GSN_DROP_TRIG_CONF, printDROP_TRIG_CONF },
+ { GSN_DROP_TRIG_REF, printDROP_TRIG_REF },
+ { GSN_FIRE_TRIG_ORD, printFIRE_TRIG_ORD },
+ { GSN_TRIG_ATTRINFO, printTRIG_ATTRINFO },
+ { GSN_CREATE_INDX_REQ, printCREATE_INDX_REQ },
+ { GSN_CREATE_INDX_CONF, printCREATE_INDX_CONF },
+ { GSN_CREATE_INDX_REF, printCREATE_INDX_REF },
+ { GSN_DROP_INDX_REQ, printDROP_INDX_REQ },
+ { GSN_DROP_INDX_CONF, printDROP_INDX_CONF },
+ { GSN_DROP_INDX_REF, printDROP_INDX_REF },
+ { GSN_ALTER_INDX_REQ, printALTER_INDX_REQ },
+ { GSN_ALTER_INDX_CONF, printALTER_INDX_CONF },
+ { GSN_ALTER_INDX_REF, printALTER_INDX_REF },
+ { GSN_TCINDXCONF, printTCINDXCONF },
+ { GSN_TCINDXREF, printTCINDXREF },
+ { GSN_INDXKEYINFO, printINDXKEYINFO },
+ { GSN_INDXATTRINFO, printINDXATTRINFO },
+ { GSN_FSAPPENDREQ, printFSAPPENDREQ },
+ { GSN_BACKUP_REQ, printBACKUP_REQ },
+ { GSN_BACKUP_DATA, printBACKUP_DATA },
+ { GSN_BACKUP_REF, printBACKUP_REF },
+ { GSN_BACKUP_CONF, printBACKUP_CONF },
+ { GSN_ABORT_BACKUP_ORD, printABORT_BACKUP_ORD },
+ { GSN_BACKUP_ABORT_REP, printBACKUP_ABORT_REP },
+ { GSN_BACKUP_COMPLETE_REP, printBACKUP_COMPLETE_REP },
+ { GSN_BACKUP_NF_COMPLETE_REP, printBACKUP_NF_COMPLETE_REP },
+ { GSN_DEFINE_BACKUP_REQ, printDEFINE_BACKUP_REQ },
+ { GSN_DEFINE_BACKUP_REF, printDEFINE_BACKUP_REF },
+ { GSN_DEFINE_BACKUP_CONF, printDEFINE_BACKUP_CONF },
+ { GSN_START_BACKUP_REQ, printSTART_BACKUP_REQ },
+ { GSN_START_BACKUP_REF, printSTART_BACKUP_REF },
+ { GSN_START_BACKUP_CONF, printSTART_BACKUP_CONF },
+ { GSN_BACKUP_FRAGMENT_REQ, printBACKUP_FRAGMENT_REQ },
+ { GSN_BACKUP_FRAGMENT_REF, printBACKUP_FRAGMENT_REF },
+ { GSN_BACKUP_FRAGMENT_CONF, printBACKUP_FRAGMENT_CONF },
+ { GSN_STOP_BACKUP_REQ, printSTOP_BACKUP_REQ },
+ { GSN_STOP_BACKUP_REF, printSTOP_BACKUP_REF },
+ { GSN_STOP_BACKUP_CONF, printSTOP_BACKUP_CONF },
+ { GSN_BACKUP_STATUS_REQ, printBACKUP_STATUS_REQ },
+ //{ GSN_BACKUP_STATUS_REF, printBACKUP_STATUS_REF },
+ { GSN_BACKUP_STATUS_CONF, printBACKUP_STATUS_CONF },
+ { GSN_UTIL_SEQUENCE_REQ, printUTIL_SEQUENCE_REQ },
+ { GSN_UTIL_SEQUENCE_REF, printUTIL_SEQUENCE_REF },
+ { GSN_UTIL_SEQUENCE_CONF, printUTIL_SEQUENCE_CONF },
+ { GSN_UTIL_PREPARE_REQ, printUTIL_PREPARE_REQ },
+ { GSN_UTIL_PREPARE_REF, printUTIL_PREPARE_REF },
+ { GSN_UTIL_PREPARE_CONF, printUTIL_PREPARE_CONF },
+ { GSN_UTIL_EXECUTE_REQ, printUTIL_EXECUTE_REQ },
+ { GSN_UTIL_EXECUTE_REF, printUTIL_EXECUTE_REF },
+ { GSN_UTIL_EXECUTE_CONF, printUTIL_EXECUTE_CONF },
+ { GSN_SCAN_TABREQ, printSCANTABREQ },
+ { GSN_SCAN_TABCONF, printSCANTABCONF },
+ { GSN_SCAN_TABREF, printSCANTABREF },
+ { GSN_SCAN_NEXTREQ, printSCANNEXTREQ },
+ { GSN_LQHFRAGREQ, printLQH_FRAG_REQ },
+ { GSN_LQHFRAGREF, printLQH_FRAG_REF },
+ { GSN_LQHFRAGCONF, printLQH_FRAG_CONF },
+ { GSN_PREP_DROP_TAB_REQ, printPREP_DROP_TAB_REQ },
+ { GSN_PREP_DROP_TAB_REF, printPREP_DROP_TAB_REF },
+ { GSN_PREP_DROP_TAB_CONF, printPREP_DROP_TAB_CONF },
+ { GSN_DROP_TAB_REQ, printDROP_TAB_REQ },
+ { GSN_DROP_TAB_REF, printDROP_TAB_REF },
+ { GSN_DROP_TAB_CONF, printDROP_TAB_CONF },
+ { GSN_LCP_FRAG_ORD, printLCP_FRAG_ORD },
+ { GSN_LCP_FRAG_REP, printLCP_FRAG_REP },
+ { GSN_LCP_COMPLETE_REP, printLCP_COMPLETE_REP },
+ { GSN_START_LCP_REQ, printSTART_LCP_REQ },
+ { GSN_START_LCP_CONF, printSTART_LCP_CONF },
+ { GSN_MASTER_LCPREQ, printMASTER_LCP_REQ },
+ { GSN_MASTER_LCPREF, printMASTER_LCP_REF },
+ { GSN_MASTER_LCPCONF, printMASTER_LCP_CONF },
+ { GSN_COPY_GCIREQ, printCOPY_GCI_REQ },
+ { GSN_SYSTEM_ERROR, printSYSTEM_ERROR },
+ { GSN_START_RECREQ, printSTART_REC_REQ },
+ { GSN_START_RECCONF, printSTART_REC_CONF },
+ { GSN_NF_COMPLETEREP, printNF_COMPLETE_REP },
+ { GSN_SIGNAL_DROPPED_REP, printSIGNAL_DROPPED_REP },
+ { GSN_FAIL_REP, printFAIL_REP },
+ { GSN_DISCONNECT_REP, printDISCONNECT_REP },
+
+ { GSN_SUB_CREATE_REQ, printSUB_CREATE_REQ },
+ { GSN_SUB_CREATE_REF, printSUB_CREATE_REF },
+ { GSN_SUB_CREATE_CONF, printSUB_CREATE_CONF },
+ { GSN_SUB_REMOVE_REQ, printSUB_REMOVE_REQ },
+ { GSN_SUB_REMOVE_REF, printSUB_REMOVE_REF },
+ { GSN_SUB_REMOVE_CONF, printSUB_REMOVE_CONF },
+ { GSN_SUB_START_REQ, printSUB_START_REQ },
+ { GSN_SUB_START_REF, printSUB_START_REF },
+ { GSN_SUB_START_CONF, printSUB_START_CONF },
+ { GSN_SUB_STOP_REQ, printSUB_STOP_REQ },
+ { GSN_SUB_STOP_REF, printSUB_STOP_REF },
+ { GSN_SUB_STOP_CONF, printSUB_STOP_CONF },
+ { GSN_SUB_SYNC_REQ, printSUB_SYNC_REQ },
+ { GSN_SUB_SYNC_REF, printSUB_SYNC_REF },
+ { GSN_SUB_SYNC_CONF, printSUB_SYNC_CONF },
+ { GSN_SUB_META_DATA, printSUB_META_DATA },
+ { GSN_SUB_TABLE_DATA, printSUB_TABLE_DATA },
+ { GSN_SUB_SYNC_CONTINUE_REQ, printSUB_SYNC_CONTINUE_REQ },
+ { GSN_SUB_SYNC_CONTINUE_REF, printSUB_SYNC_CONTINUE_REF },
+ { GSN_SUB_SYNC_CONTINUE_CONF, printSUB_SYNC_CONTINUE_CONF },
+ { GSN_SUB_GCP_COMPLETE_REP, printSUB_GCP_COMPLETE_REP }
+
+ ,{ GSN_CREATE_FRAGMENTATION_REQ, printCREATE_FRAGMENTATION_REQ }
+ ,{ GSN_CREATE_FRAGMENTATION_REF, printCREATE_FRAGMENTATION_REF }
+ ,{ GSN_CREATE_FRAGMENTATION_CONF, printCREATE_FRAGMENTATION_CONF }
+
+ ,{ GSN_UTIL_CREATE_LOCK_REQ, printUTIL_CREATE_LOCK_REQ }
+ ,{ GSN_UTIL_CREATE_LOCK_REF, printUTIL_CREATE_LOCK_REF }
+ ,{ GSN_UTIL_CREATE_LOCK_CONF, printUTIL_CREATE_LOCK_CONF }
+ ,{ GSN_UTIL_DESTROY_LOCK_REQ, printUTIL_DESTROY_LOCK_REQ }
+ ,{ GSN_UTIL_DESTROY_LOCK_REF, printUTIL_DESTROY_LOCK_REF }
+ ,{ GSN_UTIL_DESTROY_LOCK_CONF, printUTIL_DESTROY_LOCK_CONF }
+ ,{ GSN_UTIL_LOCK_REQ, printUTIL_LOCK_REQ }
+ ,{ GSN_UTIL_LOCK_REF, printUTIL_LOCK_REF }
+ ,{ GSN_UTIL_LOCK_CONF, printUTIL_LOCK_CONF }
+ ,{ GSN_UTIL_UNLOCK_REQ, printUTIL_UNLOCK_REQ }
+ ,{ GSN_UTIL_UNLOCK_REF, printUTIL_UNLOCK_REF }
+ ,{ GSN_UTIL_UNLOCK_CONF, printUTIL_UNLOCK_CONF }
+ ,{ GSN_CNTR_START_REQ, printCNTR_START_REQ }
+ ,{ GSN_CNTR_START_REF, printCNTR_START_REF }
+ ,{ GSN_CNTR_START_CONF, printCNTR_START_CONF }
+
+ ,{ GSN_READ_NODESCONF, printREAD_NODES_CONF }
+
+ ,{ GSN_TUX_MAINT_REQ, printTUX_MAINT_REQ }
+ ,{ GSN_ACC_LOCKREQ, printACC_LOCKREQ }
+ ,{ GSN_LQH_TRANSCONF, printLQH_TRANSCONF }
+ ,{ GSN_SCAN_FRAGREQ, printSCAN_FRAGREQ }
+ ,{ 0, 0 }
+};
+
+#include <Bitmask.hpp>
+
+template struct BitmaskPOD<1>;
+template struct BitmaskPOD<2>;
+template struct BitmaskPOD<4>;
+template class Bitmask<1>;
+template class Bitmask<2>;
+template class Bitmask<4>;
diff --git a/storage/ndb/src/common/debugger/signaldata/SignalDroppedRep.cpp b/storage/ndb/src/common/debugger/signaldata/SignalDroppedRep.cpp
new file mode 100644
index 00000000000..be31b4edb22
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/SignalDroppedRep.cpp
@@ -0,0 +1,34 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <DebuggerNames.hpp>
+#include <signaldata/SignalDroppedRep.hpp>
+
+bool
+printSIGNAL_DROPPED_REP(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+ SignalDroppedRep * sig = (SignalDroppedRep*)theData;
+
+ fprintf(output, " originalGsn: %s(%d) Length: %d SectionCount: %d\n",
+ getSignalName(sig->originalGsn),
+ sig->originalGsn,
+ sig->originalLength,
+ sig->originalSectionCount);
+ return false;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp b/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
new file mode 100644
index 00000000000..984d28819c0
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
@@ -0,0 +1,652 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "GlobalSignalNumbers.h"
+
+const GsnName SignalNames [] = {
+ { GSN_API_REGCONF, "API_REGCONF" }
+ ,{ GSN_API_REGREF, "API_REGREF" }
+ ,{ GSN_API_REGREQ, "API_REGREQ" }
+ ,{ GSN_ATTRINFO, "ATTRINFO" }
+ ,{ GSN_SCHEMA_INFO, "SCHEMA_INFO" }
+ ,{ GSN_SCHEMA_INFOCONF, "SCHEMA_INFOCONF" }
+ ,{ GSN_GET_SCHEMA_INFOREQ, "GET_SCHEMA_INFOREQ" }
+ ,{ GSN_DIHNDBTAMPER, "DIHNDBTAMPER" }
+ ,{ GSN_KEYINFO, "KEYINFO" }
+ ,{ GSN_KEYINFO20, "KEYINFO20" }
+ ,{ GSN_KEYINFO20_R, "KEYINFO20_R" }
+ ,{ GSN_NODE_FAILREP, "NODE_FAILREP" }
+ ,{ GSN_READCONF, "READCONF" }
+ ,{ GSN_SCAN_NEXTREQ, "SCAN_NEXTREQ" }
+ ,{ GSN_SCAN_TABCONF, "SCAN_TABCONF" }
+ ,{ GSN_SCAN_TABREF, "SCAN_TABREF" }
+ ,{ GSN_SCAN_TABREQ, "SCAN_TABREQ" }
+ ,{ GSN_TC_COMMITCONF, "TC_COMMITCONF" }
+ ,{ GSN_TC_COMMITREF, "TC_COMMITREF" }
+ ,{ GSN_TC_COMMITREQ, "TC_COMMITREQ" }
+ ,{ GSN_TCKEY_FAILCONF, "TCKEY_FAILCONF" }
+ ,{ GSN_TCKEY_FAILREF, "TCKEY_FAILREF" }
+ ,{ GSN_TCKEYCONF, "TCKEYCONF" }
+ ,{ GSN_TCKEYREF, "TCKEYREF" }
+ ,{ GSN_TCKEYREQ, "TCKEYREQ" }
+ ,{ GSN_TCRELEASECONF, "TCRELEASECONF" }
+ ,{ GSN_TCRELEASEREF, "TCRELEASEREF" }
+ ,{ GSN_TCRELEASEREQ, "TCRELEASEREQ" }
+ ,{ GSN_TCROLLBACKCONF, "TCROLLBACKCONF" }
+ ,{ GSN_TCROLLBACKREF, "TCROLLBACKREF" }
+ ,{ GSN_TCROLLBACKREQ, "TCROLLBACKREQ" }
+ ,{ GSN_TCROLLBACKREP, "TCROLLBACKREP" }
+ ,{ GSN_TCSEIZECONF, "TCSEIZECONF" }
+ ,{ GSN_TCSEIZEREF, "TCSEIZEREF" }
+ ,{ GSN_TCSEIZEREQ, "TCSEIZEREQ" }
+ ,{ GSN_TRANSID_AI, "TRANSID_AI" }
+ ,{ GSN_TRANSID_AI_R, "TRANSID_AI_R" }
+ ,{ GSN_ABORT, "ABORT" }
+ ,{ GSN_ABORTCONF, "ABORTCONF" }
+ ,{ GSN_ABORTED, "ABORTED" }
+ ,{ GSN_ABORTREQ, "ABORTREQ" }
+ ,{ GSN_ACC_ABORTCONF, "ACC_ABORTCONF" }
+ ,{ GSN_ACC_ABORTREQ, "ACC_ABORTREQ" }
+ ,{ GSN_ACC_CHECK_SCAN, "ACC_CHECK_SCAN" }
+ ,{ GSN_ACC_COMMITCONF, "ACC_COMMITCONF" }
+ ,{ GSN_ACC_COMMITREQ, "ACC_COMMITREQ" }
+ ,{ GSN_ACC_CONTOPCONF, "ACC_CONTOPCONF" }
+ ,{ GSN_ACC_CONTOPREQ, "ACC_CONTOPREQ" }
+ ,{ GSN_ACC_LCPCONF, "ACC_LCPCONF" }
+ ,{ GSN_ACC_LCPREF, "ACC_LCPREF" }
+ ,{ GSN_ACC_LCPREQ, "ACC_LCPREQ" }
+ ,{ GSN_ACC_LCPSTARTED, "ACC_LCPSTARTED" }
+ ,{ GSN_ACC_OVER_REC, "ACC_OVER_REC" }
+ ,{ GSN_ACC_SAVE_PAGES, "ACC_SAVE_PAGES" }
+ ,{ GSN_ACC_SCAN_INFO, "ACC_SCAN_INFO" }
+ ,{ GSN_ACC_SCAN_INFO24, "ACC_SCAN_INFO24" }
+ ,{ GSN_ACC_SCANCONF, "ACC_SCANCONF" }
+ ,{ GSN_ACC_SCANREF, "ACC_SCANREF" }
+ ,{ GSN_ACC_SCANREQ, "ACC_SCANREQ" }
+ ,{ GSN_ACC_SRCONF, "ACC_SRCONF" }
+ ,{ GSN_ACC_SRREF, "ACC_SRREF" }
+ ,{ GSN_ACC_SRREQ, "ACC_SRREQ" }
+ ,{ GSN_ACC_TO_CONF, "ACC_TO_CONF" }
+ ,{ GSN_ACC_TO_REF, "ACC_TO_REF" }
+ ,{ GSN_ACC_TO_REQ, "ACC_TO_REQ" }
+ ,{ GSN_ACCFRAGCONF, "ACCFRAGCONF" }
+ ,{ GSN_ACCFRAGREF, "ACCFRAGREF" }
+ ,{ GSN_ACCFRAGREQ, "ACCFRAGREQ" }
+ ,{ GSN_ACCKEYCONF, "ACCKEYCONF" }
+ ,{ GSN_ACCKEYREF, "ACCKEYREF" }
+ ,{ GSN_ACCKEYREQ, "ACCKEYREQ" }
+ ,{ GSN_ACCMINUPDATE, "ACCMINUPDATE" }
+ ,{ GSN_ACCSEIZECONF, "ACCSEIZECONF" }
+ ,{ GSN_ACCSEIZEREF, "ACCSEIZEREF" }
+ ,{ GSN_ACCSEIZEREQ, "ACCSEIZEREQ" }
+ ,{ GSN_ACCUPDATECONF, "ACCUPDATECONF" }
+ ,{ GSN_ACCUPDATEKEY, "ACCUPDATEKEY" }
+ ,{ GSN_ACCUPDATEREF, "ACCUPDATEREF" }
+ ,{ GSN_ADD_FRAGCONF, "ADD_FRAGCONF" }
+ ,{ GSN_ADD_FRAGREF, "ADD_FRAGREF" }
+ ,{ GSN_ADD_FRAGREQ, "ADD_FRAGREQ" }
+ ,{ GSN_API_FAILCONF, "API_FAILCONF" }
+ ,{ GSN_API_FAILREQ, "API_FAILREQ" }
+ ,{ GSN_CHECK_LCP_STOP, "CHECK_LCP_STOP" }
+ ,{ GSN_CLOSE_COMCONF, "CLOSE_COMCONF" }
+ ,{ GSN_CLOSE_COMREQ, "CLOSE_COMREQ" }
+ ,{ GSN_CM_ACKADD, "CM_ACKADD" }
+ ,{ GSN_CM_ADD, "CM_ADD" }
+ ,{ GSN_CM_ADD_REP, "CM_ADD_REP" }
+ ,{ GSN_CM_HEARTBEAT, "CM_HEARTBEAT" }
+ ,{ GSN_CM_NODEINFOCONF, "CM_NODEINFOCONF" }
+ ,{ GSN_CM_NODEINFOREF, "CM_NODEINFOREF" }
+ ,{ GSN_CM_NODEINFOREQ, "CM_NODEINFOREQ" }
+ ,{ GSN_CM_REGCONF, "CM_REGCONF" }
+ ,{ GSN_CM_REGREF, "CM_REGREF" }
+ ,{ GSN_CM_REGREQ, "CM_REGREQ" }
+ ,{ GSN_CNTR_START_REQ, "CNTR_START_REQ" }
+ ,{ GSN_CNTR_START_REF, "CNTR_START_REF" }
+ ,{ GSN_CNTR_START_CONF, "CNTR_START_CONF" }
+ ,{ GSN_CNTR_START_REP, "CNTR_START_REP" }
+ ,{ GSN_CNTR_WAITREP, "CNTR_WAITREP" }
+ ,{ GSN_COMMIT, "COMMIT" }
+ ,{ GSN_COMMIT_FAILCONF, "COMMIT_FAILCONF" }
+ ,{ GSN_COMMIT_FAILREQ, "COMMIT_FAILREQ" }
+ ,{ GSN_COMMITCONF, "COMMITCONF" }
+ ,{ GSN_COMMITREQ, "COMMITREQ" }
+ ,{ GSN_COMMITTED, "COMMITTED" }
+ ,{ GSN_LCP_FRAG_ORD, "LCP_FRAG_ORD" }
+ ,{ GSN_LCP_FRAG_REP, "LCP_FRAG_REP" }
+ ,{ GSN_LCP_COMPLETE_REP, "LCP_COMPLETE_REP" }
+ ,{ GSN_START_LCP_REQ, "START_LCP_REQ" }
+ ,{ GSN_START_LCP_CONF, "START_LCP_CONF" }
+ ,{ GSN_COMPLETE, "COMPLETE" }
+ ,{ GSN_COMPLETECONF, "COMPLETECONF" }
+ ,{ GSN_COMPLETED, "COMPLETED" }
+ ,{ GSN_COMPLETEREQ, "COMPLETEREQ" }
+ ,{ GSN_CONNECT_REP, "CONNECT_REP" }
+ ,{ GSN_CONTINUEB, "CONTINUEB" }
+ ,{ GSN_COPY_ACTIVECONF, "COPY_ACTIVECONF" }
+ ,{ GSN_COPY_ACTIVEREF, "COPY_ACTIVEREF" }
+ ,{ GSN_COPY_ACTIVEREQ, "COPY_ACTIVEREQ" }
+ ,{ GSN_COPY_FRAGCONF, "COPY_FRAGCONF" }
+ ,{ GSN_COPY_FRAGREF, "COPY_FRAGREF" }
+ ,{ GSN_COPY_FRAGREQ, "COPY_FRAGREQ" }
+ ,{ GSN_COPY_GCICONF, "COPY_GCICONF" }
+ ,{ GSN_COPY_GCIREQ, "COPY_GCIREQ" }
+ ,{ GSN_COPY_STATECONF, "COPY_STATECONF" }
+ ,{ GSN_COPY_STATEREQ, "COPY_STATEREQ" }
+ ,{ GSN_COPY_TABCONF, "COPY_TABCONF" }
+ ,{ GSN_COPY_TABREQ, "COPY_TABREQ" }
+ ,{ GSN_CREATE_FRAGCONF, "CREATE_FRAGCONF" }
+ ,{ GSN_CREATE_FRAGREF, "CREATE_FRAGREF" }
+ ,{ GSN_CREATE_FRAGREQ, "CREATE_FRAGREQ" }
+ ,{ GSN_DEBUG_SIG, "DEBUG_SIG" }
+ ,{ GSN_DI_FCOUNTCONF, "DI_FCOUNTCONF" }
+ ,{ GSN_DI_FCOUNTREF, "DI_FCOUNTREF" }
+ ,{ GSN_DI_FCOUNTREQ, "DI_FCOUNTREQ" }
+ ,{ GSN_DIADDTABCONF, "DIADDTABCONF" }
+ ,{ GSN_DIADDTABREF, "DIADDTABREF" }
+ ,{ GSN_DIADDTABREQ, "DIADDTABREQ" }
+ ,{ GSN_DICTSTARTCONF, "DICTSTARTCONF" }
+ ,{ GSN_DICTSTARTREQ, "DICTSTARTREQ" }
+ ,{ GSN_LIST_TABLES_REQ, "LIST_TABLES_REQ" }
+ ,{ GSN_LIST_TABLES_CONF, "LIST_TABLES_CONF" }
+ ,{ GSN_DIGETNODESCONF, "DIGETNODESCONF" }
+ ,{ GSN_DIGETNODESREF, "DIGETNODESREF" }
+ ,{ GSN_DIGETNODESREQ, "DIGETNODESREQ" }
+ ,{ GSN_DIGETPRIMCONF, "DIGETPRIMCONF" }
+ ,{ GSN_DIGETPRIMREF, "DIGETPRIMREF" }
+ ,{ GSN_DIGETPRIMREQ, "DIGETPRIMREQ" }
+ ,{ GSN_DIH_RESTARTCONF, "DIH_RESTARTCONF" }
+ ,{ GSN_DIH_RESTARTREF, "DIH_RESTARTREF" }
+ ,{ GSN_DIH_RESTARTREQ, "DIH_RESTARTREQ" }
+
+ ,{ GSN_DIRELEASECONF, "DIRELEASECONF" }
+ ,{ GSN_DIRELEASEREF, "DIRELEASEREF" }
+ ,{ GSN_DIRELEASEREQ, "DIRELEASEREQ" }
+ ,{ GSN_DISCONNECT_REP, "DISCONNECT_REP" }
+ ,{ GSN_DISEIZECONF, "DISEIZECONF" }
+ ,{ GSN_DISEIZEREF, "DISEIZEREF" }
+ ,{ GSN_DISEIZEREQ, "DISEIZEREQ" }
+ ,{ GSN_DIVERIFYCONF, "DIVERIFYCONF" }
+ ,{ GSN_DIVERIFYREF, "DIVERIFYREF" }
+ ,{ GSN_DIVERIFYREQ, "DIVERIFYREQ" }
+ ,{ GSN_EMPTY_LCP_REQ, "EMPTY_LCP_REQ" }
+ ,{ GSN_EMPTY_LCP_CONF, "EMPTY_LCP_CONF" }
+ ,{ GSN_ENABLE_COMORD, "ENABLE_COMORD" }
+ ,{ GSN_END_LCPCONF, "END_LCPCONF" }
+ ,{ GSN_END_LCPREQ, "END_LCPREQ" }
+ ,{ GSN_END_TOCONF, "END_TOCONF" }
+ ,{ GSN_END_TOREQ, "END_TOREQ" }
+ ,{ GSN_EVENT_REP, "EVENT_REP" }
+ ,{ GSN_EXEC_FRAGCONF, "EXEC_FRAGCONF" }
+ ,{ GSN_EXEC_FRAGREF, "EXEC_FRAGREF" }
+ ,{ GSN_EXEC_FRAGREQ, "EXEC_FRAGREQ" }
+ ,{ GSN_EXEC_SRCONF, "EXEC_SRCONF" }
+ ,{ GSN_EXEC_SRREQ, "EXEC_SRREQ" }
+ ,{ GSN_EXPANDCHECK2, "EXPANDCHECK2" }
+ ,{ GSN_FAIL_REP, "FAIL_REP" }
+ ,{ GSN_FSCLOSECONF, "FSCLOSECONF" }
+ ,{ GSN_FSCLOSEREF, "FSCLOSEREF" }
+ ,{ GSN_FSCLOSEREQ, "FSCLOSEREQ" }
+ ,{ GSN_FSOPENCONF, "FSOPENCONF" }
+ ,{ GSN_FSOPENREF, "FSOPENREF" }
+ ,{ GSN_FSOPENREQ, "FSOPENREQ" }
+ ,{ GSN_FSREADCONF, "FSREADCONF" }
+ ,{ GSN_FSREADREF, "FSREADREF" }
+ ,{ GSN_FSREADREQ, "FSREADREQ" }
+ ,{ GSN_FSSYNCCONF, "FSSYNCCONF" }
+ ,{ GSN_FSSYNCREF, "FSSYNCREF" }
+ ,{ GSN_FSSYNCREQ, "FSSYNCREQ" }
+ ,{ GSN_FSWRITECONF, "FSWRITECONF" }
+ ,{ GSN_FSWRITEREF, "FSWRITEREF" }
+ ,{ GSN_FSWRITEREQ, "FSWRITEREQ" }
+ ,{ GSN_FSAPPENDCONF, "FSAPPENDCONF" }
+ ,{ GSN_FSAPPENDREF, "FSAPPENDREF" }
+ ,{ GSN_FSAPPENDREQ, "FSAPPENDREQ" }
+ ,{ GSN_FSREMOVECONF, "FSREMOVECONF" }
+ ,{ GSN_FSREMOVEREF, "FSREMOVEREF" }
+ ,{ GSN_FSREMOVEREQ, "FSREMOVEREQ" }
+ ,{ GSN_GCP_ABORT, "GCP_ABORT" }
+ ,{ GSN_GCP_ABORTED, "GCP_ABORTED" }
+ ,{ GSN_GCP_COMMIT, "GCP_COMMIT" }
+ ,{ GSN_GCP_NODEFINISH, "GCP_NODEFINISH" }
+ ,{ GSN_GCP_NOMORETRANS, "GCP_NOMORETRANS" }
+ ,{ GSN_GCP_PREPARE, "GCP_PREPARE" }
+ ,{ GSN_GCP_PREPARECONF, "GCP_PREPARECONF" }
+ ,{ GSN_GCP_PREPAREREF, "GCP_PREPAREREF" }
+ ,{ GSN_GCP_SAVECONF, "GCP_SAVECONF" }
+ ,{ GSN_GCP_SAVEREF, "GCP_SAVEREF" }
+ ,{ GSN_GCP_SAVEREQ, "GCP_SAVEREQ" }
+ ,{ GSN_GCP_TCFINISHED, "GCP_TCFINISHED" }
+ ,{ GSN_GET_TABINFOREF, "GET_TABINFOREF" }
+ ,{ GSN_GET_TABINFOREQ, "GET_TABINFOREQ" }
+ ,{ GSN_GET_TABINFO_CONF, "GET_TABINFO_CONF" }
+ ,{ GSN_GETGCICONF, "GETGCICONF" }
+ ,{ GSN_GETGCIREQ, "GETGCIREQ" }
+ ,{ GSN_HOT_SPAREREP, "HOT_SPAREREP" }
+ ,{ GSN_INCL_NODECONF, "INCL_NODECONF" }
+ ,{ GSN_INCL_NODEREF, "INCL_NODEREF" }
+ ,{ GSN_INCL_NODEREQ, "INCL_NODEREQ" }
+ ,{ GSN_LCP_FRAGIDCONF, "LCP_FRAGIDCONF" }
+ ,{ GSN_LCP_FRAGIDREF, "LCP_FRAGIDREF" }
+ ,{ GSN_LCP_FRAGIDREQ, "LCP_FRAGIDREQ" }
+ ,{ GSN_LCP_HOLDOPCONF, "LCP_HOLDOPCONF" }
+ ,{ GSN_LCP_HOLDOPREF, "LCP_HOLDOPREF" }
+ ,{ GSN_LCP_HOLDOPREQ, "LCP_HOLDOPREQ" }
+ ,{ GSN_LQH_RESTART_OP, "LQH_RESTART_OP" }
+ ,{ GSN_LQH_TRANSCONF, "LQH_TRANSCONF" }
+ ,{ GSN_LQH_TRANSREQ, "LQH_TRANSREQ" }
+ ,{ GSN_LQHADDATTCONF, "LQHADDATTCONF" }
+ ,{ GSN_LQHADDATTREF, "LQHADDATTREF" }
+ ,{ GSN_LQHADDATTREQ, "LQHADDATTREQ" }
+ ,{ GSN_LQHFRAGCONF, "LQHFRAGCONF" }
+ ,{ GSN_LQHFRAGREF, "LQHFRAGREF" }
+ ,{ GSN_LQHFRAGREQ, "LQHFRAGREQ" }
+ ,{ GSN_LQHKEYCONF, "LQHKEYCONF" }
+ ,{ GSN_LQHKEYREF, "LQHKEYREF" }
+ ,{ GSN_LQHKEYREQ, "LQHKEYREQ" }
+ ,{ GSN_MASTER_GCPCONF, "MASTER_GCPCONF" }
+ ,{ GSN_MASTER_GCPREF, "MASTER_GCPREF" }
+ ,{ GSN_MASTER_GCPREQ, "MASTER_GCPREQ" }
+ ,{ GSN_MASTER_LCPCONF, "MASTER_LCPCONF" }
+ ,{ GSN_MASTER_LCPREF, "MASTER_LCPREF" }
+ ,{ GSN_MASTER_LCPREQ, "MASTER_LCPREQ" }
+ ,{ GSN_MEMCHECKCONF, "MEMCHECKCONF" }
+ ,{ GSN_MEMCHECKREQ, "MEMCHECKREQ" }
+ ,{ GSN_NDB_FAILCONF, "NDB_FAILCONF" }
+ ,{ GSN_NDB_STARTCONF, "NDB_STARTCONF" }
+ ,{ GSN_NDB_STARTREF, "NDB_STARTREF" }
+ ,{ GSN_NDB_STARTREQ, "NDB_STARTREQ" }
+ ,{ GSN_NDB_STTOR, "NDB_STTOR" }
+ ,{ GSN_NDB_STTORRY, "NDB_STTORRY" }
+ ,{ GSN_NDB_TAMPER, "NDB_TAMPER" }
+ ,{ GSN_NEXT_SCANCONF, "NEXT_SCANCONF" }
+ ,{ GSN_NEXT_SCANREF, "NEXT_SCANREF" }
+ ,{ GSN_NEXT_SCANREQ, "NEXT_SCANREQ" }
+ ,{ GSN_NEXTOPERATION, "NEXTOPERATION" }
+ ,{ GSN_NF_COMPLETEREP, "NF_COMPLETEREP" }
+ ,{ GSN_OPEN_COMCONF, "OPEN_COMCONF" }
+ ,{ GSN_OPEN_COMREF, "OPEN_COMREF" }
+ ,{ GSN_OPEN_COMREQ, "OPEN_COMREQ" }
+ ,{ GSN_PACKED_SIGNAL, "PACKED_SIGNAL" }
+ ,{ GSN_PREP_FAILCONF, "PREP_FAILCONF" }
+ ,{ GSN_PREP_FAILREF, "PREP_FAILREF" }
+ ,{ GSN_PREP_FAILREQ, "PREP_FAILREQ" }
+ ,{ GSN_PRES_TOCONF, "PRES_TOCONF" }
+ ,{ GSN_PRES_TOREQ, "PRES_TOREQ" }
+ ,{ GSN_READ_NODESCONF, "READ_NODESCONF" }
+ ,{ GSN_READ_NODESREF, "READ_NODESREF" }
+ ,{ GSN_READ_NODESREQ, "READ_NODESREQ" }
+ ,{ GSN_SCAN_FRAGCONF, "SCAN_FRAGCONF" }
+ ,{ GSN_SCAN_FRAGREF, "SCAN_FRAGREF" }
+ ,{ GSN_SCAN_FRAGREQ, "SCAN_FRAGREQ" }
+ ,{ GSN_SCAN_HBREP, "SCAN_HBREP" }
+ ,{ GSN_SCAN_PROCCONF, "SCAN_PROCCONF" }
+ ,{ GSN_SCAN_PROCREQ, "SCAN_PROCREQ" }
+ ,{ GSN_SEND_PACKED, "SEND_PACKED" }
+ ,{ GSN_SET_LOGLEVELORD, "SET_LOGLEVELORD" }
+ ,{ GSN_SHRINKCHECK2, "SHRINKCHECK2" }
+ ,{ GSN_READ_CONFIG_REQ, "READ_CONFIG_REQ" }
+ ,{ GSN_READ_CONFIG_CONF, "READ_CONFIG_CONF" }
+ ,{ GSN_SR_FRAGIDCONF, "SR_FRAGIDCONF" }
+ ,{ GSN_SR_FRAGIDREF, "SR_FRAGIDREF" }
+ ,{ GSN_SR_FRAGIDREQ, "SR_FRAGIDREQ" }
+ ,{ GSN_START_COPYCONF, "START_COPYCONF" }
+ ,{ GSN_START_COPYREF, "START_COPYREF" }
+ ,{ GSN_START_COPYREQ, "START_COPYREQ" }
+ ,{ GSN_START_EXEC_SR, "START_EXEC_SR" }
+ ,{ GSN_START_FRAGCONF, "START_FRAGCONF" }
+ ,{ GSN_START_FRAGREF, "START_FRAGREF" }
+ ,{ GSN_START_FRAGREQ, "START_FRAGREQ" }
+ ,{ GSN_START_LCP_REF, "START_LCP_REF" }
+ ,{ GSN_START_LCP_ROUND, "START_LCP_ROUND" }
+ ,{ GSN_START_MECONF, "START_MECONF" }
+ ,{ GSN_START_MEREF, "START_MEREF" }
+ ,{ GSN_START_MEREQ, "START_MEREQ" }
+ ,{ GSN_START_PERMCONF, "START_PERMCONF" }
+ ,{ GSN_START_PERMREF, "START_PERMREF" }
+ ,{ GSN_START_PERMREQ, "START_PERMREQ" }
+ ,{ GSN_START_RECCONF, "START_RECCONF" }
+ ,{ GSN_START_RECREF, "START_RECREF" }
+ ,{ GSN_START_RECREQ, "START_RECREQ" }
+ ,{ GSN_START_TOCONF, "START_TOCONF" }
+ ,{ GSN_START_TOREQ, "START_TOREQ" }
+ ,{ GSN_STORED_PROCCONF, "STORED_PROCCONF" }
+ ,{ GSN_STORED_PROCREF, "STORED_PROCREF" }
+ ,{ GSN_STORED_PROCREQ, "STORED_PROCREQ" }
+ ,{ GSN_STTOR, "STTOR" }
+ ,{ GSN_STTORRY, "STTORRY" }
+ ,{ GSN_SYSTEM_ERROR, "SYSTEM_ERROR" }
+ ,{ GSN_TAB_COMMITCONF, "TAB_COMMITCONF" }
+ ,{ GSN_TAB_COMMITREF, "TAB_COMMITREF" }
+ ,{ GSN_TAB_COMMITREQ, "TAB_COMMITREQ" }
+ ,{ GSN_TAKE_OVERTCCONF, "TAKE_OVERTCCONF" }
+ ,{ GSN_TAKE_OVERTCREQ, "TAKE_OVERTCREQ" }
+ ,{ GSN_TC_CLOPSIZECONF, "TC_CLOPSIZECONF" }
+ ,{ GSN_TC_CLOPSIZEREQ, "TC_CLOPSIZEREQ" }
+ ,{ GSN_TC_SCHVERCONF, "TC_SCHVERCONF" }
+ ,{ GSN_TC_SCHVERREQ, "TC_SCHVERREQ" }
+ ,{ GSN_TCGETOPSIZECONF, "TCGETOPSIZECONF" }
+ ,{ GSN_TCGETOPSIZEREQ, "TCGETOPSIZEREQ" }
+ ,{ GSN_TEST_ORD, "TEST_ORD" }
+ ,{ GSN_TESTSIG, "TESTSIG" }
+ ,{ GSN_TIME_SIGNAL, "TIME_SIGNAL" }
+ ,{ GSN_TUP_ABORTREQ, "TUP_ABORTREQ" }
+ ,{ GSN_TUP_ADD_ATTCONF, "TUP_ADD_ATTCONF" }
+ ,{ GSN_TUP_ADD_ATTRREF, "TUP_ADD_ATTRREF" }
+ ,{ GSN_TUP_ADD_ATTRREQ, "TUP_ADD_ATTRREQ" }
+ ,{ GSN_TUP_ATTRINFO, "TUP_ATTRINFO" }
+ ,{ GSN_TUP_COMMITREQ, "TUP_COMMITREQ" }
+ ,{ GSN_TUP_LCPCONF, "TUP_LCPCONF" }
+ ,{ GSN_TUP_LCPREF, "TUP_LCPREF" }
+ ,{ GSN_TUP_LCPREQ, "TUP_LCPREQ" }
+ ,{ GSN_TUP_LCPSTARTED, "TUP_LCPSTARTED" }
+ ,{ GSN_TUP_PREPLCPCONF, "TUP_PREPLCPCONF" }
+ ,{ GSN_TUP_PREPLCPREF, "TUP_PREPLCPREF" }
+ ,{ GSN_TUP_PREPLCPREQ, "TUP_PREPLCPREQ" }
+ ,{ GSN_TUP_SRCONF, "TUP_SRCONF" }
+ ,{ GSN_TUP_SRREF, "TUP_SRREF" }
+ ,{ GSN_TUP_SRREQ, "TUP_SRREQ" }
+ ,{ GSN_TUPFRAGCONF, "TUPFRAGCONF" }
+ ,{ GSN_TUPFRAGREF, "TUPFRAGREF" }
+ ,{ GSN_TUPFRAGREQ, "TUPFRAGREQ" }
+ ,{ GSN_TUPKEYCONF, "TUPKEYCONF" }
+ ,{ GSN_TUPKEYREF, "TUPKEYREF" }
+ ,{ GSN_TUPKEYREQ, "TUPKEYREQ" }
+ ,{ GSN_TUPRELEASECONF, "TUPRELEASECONF" }
+ ,{ GSN_TUPRELEASEREF, "TUPRELEASEREF" }
+ ,{ GSN_TUPRELEASEREQ, "TUPRELEASEREQ" }
+ ,{ GSN_TUPSEIZECONF, "TUPSEIZECONF" }
+ ,{ GSN_TUPSEIZEREF, "TUPSEIZEREF" }
+ ,{ GSN_TUPSEIZEREQ, "TUPSEIZEREQ" }
+ ,{ GSN_UNBLO_DICTCONF, "UNBLO_DICTCONF" }
+ ,{ GSN_UNBLO_DICTREQ, "UNBLO_DICTREQ" }
+ ,{ GSN_UPDATE_TOCONF, "UPDATE_TOCONF" }
+ ,{ GSN_UPDATE_TOREF, "UPDATE_TOREF" }
+ ,{ GSN_UPDATE_TOREQ, "UPDATE_TOREQ" }
+ ,{ GSN_TUP_ALLOCREQ, "TUP_ALLOCREQ" }
+ ,{ GSN_LQH_ALLOCREQ, "LQH_ALLOCREQ" }
+ ,{ GSN_TUP_DEALLOCREQ, "TUP_DEALLOCREQ" }
+ ,{ GSN_TUP_WRITELOG_REQ, "TUP_WRITELOG_REQ" }
+ ,{ GSN_LQH_WRITELOG_REQ, "LQH_WRITELOG_REQ" }
+
+ ,{ GSN_STATISTICS_REQ, "STATISTICS_REQ" }
+ ,{ GSN_START_ORD, "START_ORD" }
+ ,{ GSN_STOP_ORD, "STOP_ORD" }
+ ,{ GSN_TAMPER_ORD, "TAMPER_ORD" }
+ ,{ GSN_SET_VAR_REQ, "SET_VAR_REQ" }
+ ,{ GSN_SET_VAR_CONF, "SET_VAR_CONF" }
+ ,{ GSN_SET_VAR_REF, "SET_VAR_REF" }
+ ,{ GSN_STATISTICS_CONF, "STATISTICS_CONF" }
+
+ ,{ GSN_EVENT_SUBSCRIBE_REQ, "EVENT_SUBSCRIBE_REQ" }
+ ,{ GSN_EVENT_SUBSCRIBE_CONF, "EVENT_SUBSCRIBE_CONF" }
+ ,{ GSN_EVENT_SUBSCRIBE_REF, "EVENT_SUBSCRIBE_REF" }
+ ,{ GSN_ACC_COM_BLOCK, "ACC_COM_BLOCK" }
+ ,{ GSN_ACC_COM_UNBLOCK, "ACC_COM_UNBLOCK" }
+ ,{ GSN_TUP_COM_BLOCK, "TUP_COM_BLOCK" }
+ ,{ GSN_TUP_COM_UNBLOCK, "TUP_COM_UNBLOCK" }
+ ,{ GSN_DUMP_STATE_ORD, "DUMP_STATE_ORD" }
+
+ ,{ GSN_START_INFOREQ, "START_INFOREQ" }
+ ,{ GSN_START_INFOREF, "START_INFOREF" }
+ ,{ GSN_START_INFOCONF, "START_INFOCONF" }
+
+ ,{ GSN_CHECKNODEGROUPSREQ, "CHECKNODEGROUPSREQ" }
+ ,{ GSN_CHECKNODEGROUPSCONF, "CHECKNODEGROUPSCONF" }
+
+ ,{ GSN_ARBIT_PREPREQ, "ARBIT_PREPREQ" }
+ ,{ GSN_ARBIT_PREPCONF, "ARBIT_PREPCONF" }
+ ,{ GSN_ARBIT_PREPREF, "ARBIT_PREPREF" }
+ ,{ GSN_ARBIT_STARTREQ, "ARBIT_STARTREQ" }
+ ,{ GSN_ARBIT_STARTCONF, "ARBIT_STARTCONF" }
+ ,{ GSN_ARBIT_STARTREF, "ARBIT_STARTREF" }
+ ,{ GSN_ARBIT_CHOOSEREQ, "ARBIT_CHOOSEREQ" }
+ ,{ GSN_ARBIT_CHOOSECONF, "ARBIT_CHOOSECONF" }
+ ,{ GSN_ARBIT_CHOOSEREF, "ARBIT_CHOOSEREF" }
+ ,{ GSN_ARBIT_STOPORD, "ARBIT_STOPORD" }
+ ,{ GSN_ARBIT_STOPREP, "ARBIT_STOPREP" }
+
+ ,{ GSN_TC_COMMIT_ACK, "TC_COMMIT_ACK" }
+ ,{ GSN_REMOVE_MARKER_ORD, "REMOVE_MARKER_ORD" }
+
+ ,{ GSN_NODE_STATE_REP, "NODE_STATE_REP" }
+ ,{ GSN_CHANGE_NODE_STATE_REQ, "CHANGE_NODE_STATE_REQ" }
+ ,{ GSN_CHANGE_NODE_STATE_CONF, "CHANGE_NODE_STATE_CONF" }
+
+ ,{ GSN_BLOCK_COMMIT_ORD, "BLOCK_COMMIT_ORD" }
+ ,{ GSN_UNBLOCK_COMMIT_ORD, "UNBLOCK_COMMIT_ORD" }
+
+ ,{ GSN_DIH_SWITCH_REPLICA_REQ, "DIH_SWITCH_REPLICA_REQ" }
+ ,{ GSN_DIH_SWITCH_REPLICA_REF, "DIH_SWITCH_REPLICA_REF" }
+ ,{ GSN_DIH_SWITCH_REPLICA_CONF, "DIH_SWITCH_REPLICA_CONF" }
+
+ ,{ GSN_STOP_PERM_REQ, "STOP_PERM_REQ" }
+ ,{ GSN_STOP_PERM_REF, "STOP_PERM_REF" }
+ ,{ GSN_STOP_PERM_CONF, "STOP_PERM_CONF" }
+
+ ,{ GSN_STOP_ME_REQ, "STOP_ME_REQ" }
+ ,{ GSN_STOP_ME_REF, "STOP_ME_REF" }
+ ,{ GSN_STOP_ME_CONF, "STOP_ME_CONF" }
+
+ ,{ GSN_WAIT_GCP_REQ, "WAIT_GCP_REQ" }
+ ,{ GSN_WAIT_GCP_REF, "WAIT_GCP_REF" }
+ ,{ GSN_WAIT_GCP_CONF, "WAIT_GCP_CONF" }
+
+ ,{ GSN_STOP_REQ, "STOP_REQ" }
+ ,{ GSN_STOP_REF, "STOP_REF" }
+ ,{ GSN_API_VERSION_REQ, "API_VERSION_REQ" }
+ ,{ GSN_API_VERSION_CONF, "API_VERSION_CONF" }
+
+ ,{ GSN_ABORT_ALL_REQ, "ABORT_ALL_REQ" }
+ ,{ GSN_ABORT_ALL_REF, "ABORT_ALL_REF" }
+ ,{ GSN_ABORT_ALL_CONF, "ABORT_ALL_CONF" }
+
+ ,{ GSN_DROP_TABLE_REQ, "DROP_TABLE_REQ" }
+ ,{ GSN_DROP_TABLE_REF, "DROP_TABLE_REF" }
+ ,{ GSN_DROP_TABLE_CONF, "DROP_TABLE_CONF" }
+
+ ,{ GSN_DROP_TAB_REQ, "DROP_TAB_REQ" }
+ ,{ GSN_DROP_TAB_REF, "DROP_TAB_REF" }
+ ,{ GSN_DROP_TAB_CONF, "DROP_TAB_CONF" }
+
+ ,{ GSN_PREP_DROP_TAB_REQ, "PREP_DROP_TAB_REQ" }
+ ,{ GSN_PREP_DROP_TAB_REF, "PREP_DROP_TAB_REF" }
+ ,{ GSN_PREP_DROP_TAB_CONF, "PREP_DROP_TAB_CONF" }
+
+ ,{ GSN_WAIT_DROP_TAB_REQ, "WAIT_DROP_TAB_REQ" }
+ ,{ GSN_WAIT_DROP_TAB_REF, "WAIT_DROP_TAB_REF" }
+ ,{ GSN_WAIT_DROP_TAB_CONF, "WAIT_DROP_TAB_CONF" }
+
+ ,{ GSN_CREATE_TRIG_REQ, "CREATE_TRIG_REQ" }
+ ,{ GSN_CREATE_TRIG_CONF, "CREATE_TRIG_CONF" }
+ ,{ GSN_CREATE_TRIG_REF, "CREATE_TRIG_REF" }
+ ,{ GSN_ALTER_TRIG_REQ, "ALTER_TRIG_REQ" }
+ ,{ GSN_ALTER_TRIG_CONF, "ALTER_TRIG_CONF" }
+ ,{ GSN_ALTER_TRIG_REF, "ALTER_TRIG_REF" }
+ ,{ GSN_DROP_TRIG_REQ, "DROP_TRIG_REQ" }
+ ,{ GSN_DROP_TRIG_CONF, "DROP_TRIG_CONF" }
+ ,{ GSN_DROP_TRIG_REF, "DROP_TRIG_REF" }
+ ,{ GSN_FIRE_TRIG_ORD, "FIRE_TRIG_ORD" }
+ ,{ GSN_TRIG_ATTRINFO, "TRIG_ATTRINFO" }
+
+ ,{ GSN_CREATE_INDX_REQ, "CREATE_INDX_REQ" }
+ ,{ GSN_CREATE_INDX_CONF, "CREATE_INDX_CONF" }
+ ,{ GSN_CREATE_INDX_REF, "CREATE_INDX_REF" }
+ ,{ GSN_DROP_INDX_REQ, "DROP_INDX_REQ" }
+ ,{ GSN_DROP_INDX_CONF, "DROP_INDX_CONF" }
+ ,{ GSN_DROP_INDX_REF, "DROP_INDX_REF" }
+ ,{ GSN_ALTER_INDX_REQ, "ALTER_INDX_REQ" }
+ ,{ GSN_ALTER_INDX_CONF, "ALTER_INDX_CONF" }
+ ,{ GSN_ALTER_INDX_REF, "ALTER_INDX_REF" }
+ ,{ GSN_TCINDXREQ, "TCINDXREQ" }
+ ,{ GSN_TCINDXCONF, "TCINDXCONF" }
+ ,{ GSN_TCINDXREF, "TCINDXREF" }
+ ,{ GSN_INDXKEYINFO, "INDXKEYINFO" }
+ ,{ GSN_INDXATTRINFO, "INDXATTRINFO" }
+ ,{ GSN_BUILDINDXREQ, "BUILDINDXREQ" }
+ ,{ GSN_BUILDINDXCONF, "BUILDINDXCONF" }
+ ,{ GSN_BUILDINDXREF, "BUILDINDXREF" }
+ //,{ GSN_TCINDXNEXTREQ, "TCINDXNEXTREQ" }
+ //,{ GSN_TCINDEXNEXTCONF, "TCINDEXNEXTCONF" }
+ //,{ GSN_TCINDEXNEXREF, "TCINDEXNEXREF" }
+
+ ,{ GSN_CREATE_EVNT_REQ, "CREATE_EVNT_REQ" }
+ ,{ GSN_CREATE_EVNT_CONF, "CREATE_EVNT_CONF" }
+ ,{ GSN_CREATE_EVNT_REF, "CREATE_EVNT_REF" }
+
+ ,{ GSN_SUMA_START_ME, "SUMA_START_ME" }
+ ,{ GSN_SUMA_HANDOVER_REQ, "SUMA_HANDOVER_REQ"}
+ ,{ GSN_SUMA_HANDOVER_CONF, "SUMA_HANDOVER_CONF"}
+
+ ,{ GSN_DROP_EVNT_REQ, "DROP_EVNT_REQ" }
+ ,{ GSN_DROP_EVNT_CONF, "DROP_EVNT_CONF" }
+ ,{ GSN_DROP_EVNT_REF, "DROP_EVNT_REF" }
+
+ ,{ GSN_BACKUP_TRIG_REQ, "BACKUP_TRIG_REQ" }
+ ,{ GSN_BACKUP_REQ, "BACKUP_REQ" }
+ ,{ GSN_BACKUP_DATA, "BACKUP_DATA" }
+ ,{ GSN_BACKUP_REF, "BACKUP_REF" }
+ ,{ GSN_BACKUP_CONF, "BACKUP_CONF" }
+ ,{ GSN_ABORT_BACKUP_ORD, "ABORT_BACKUP_ORD" }
+ ,{ GSN_BACKUP_ABORT_REP, "BACKUP_ABORT_REP" }
+ ,{ GSN_BACKUP_COMPLETE_REP, "BACKUP_COMPLETE_REP" }
+ ,{ GSN_BACKUP_NF_COMPLETE_REP, "BACKUP_NF_COMPLETE_REP" }
+ ,{ GSN_DEFINE_BACKUP_REQ, "DEFINE_BACKUP_REQ" }
+ ,{ GSN_DEFINE_BACKUP_REF, "DEFINE_BACKUP_REF" }
+ ,{ GSN_DEFINE_BACKUP_CONF, "DEFINE_BACKUP_CONF" }
+ ,{ GSN_START_BACKUP_REQ, "START_BACKUP_REQ" }
+ ,{ GSN_START_BACKUP_REF, "START_BACKUP_REF" }
+ ,{ GSN_START_BACKUP_CONF, "START_BACKUP_CONF" }
+ ,{ GSN_BACKUP_FRAGMENT_REQ, "BACKUP_FRAGMENT_REQ" }
+ ,{ GSN_BACKUP_FRAGMENT_REF, "BACKUP_FRAGMENT_REF" }
+ ,{ GSN_BACKUP_FRAGMENT_CONF, "BACKUP_FRAGMENT_CONF" }
+ ,{ GSN_STOP_BACKUP_REQ, "STOP_BACKUP_REQ" }
+ ,{ GSN_STOP_BACKUP_REF, "STOP_BACKUP_REF" }
+ ,{ GSN_STOP_BACKUP_CONF, "STOP_BACKUP_CONF" }
+ ,{ GSN_BACKUP_STATUS_REQ, "BACKUP_STATUS_REQ" }
+ ,{ GSN_BACKUP_STATUS_REF, "BACKUP_STATUS_REF" }
+ ,{ GSN_BACKUP_STATUS_CONF, "BACKUP_STATUS_CONF" }
+ ,{ GSN_SIGNAL_DROPPED_REP, "SIGNAL_DROPPED_REP" }
+ ,{ GSN_CONTINUE_FRAGMENTED, "CONTINUE_FRAGMENTED" }
+
+ /** Util Block Services **/
+ ,{ GSN_UTIL_SEQUENCE_REQ, "UTIL_SEQUENCE_REQ" }
+ ,{ GSN_UTIL_SEQUENCE_REF, "UTIL_SEQUENCE_REF" }
+ ,{ GSN_UTIL_SEQUENCE_CONF, "UTIL_SEQUENCE_CONF" }
+ ,{ GSN_UTIL_PREPARE_REQ, "UTIL_PREPARE_REQ" }
+ ,{ GSN_UTIL_PREPARE_CONF, "UTIL_PREPARE_CONF" }
+ ,{ GSN_UTIL_PREPARE_REF, "UTIL_PREPARE_REF" }
+ ,{ GSN_UTIL_EXECUTE_REQ, "UTIL_EXECUTE_REQ" }
+ ,{ GSN_UTIL_EXECUTE_CONF, "UTIL_EXECUTE_CONF" }
+ ,{ GSN_UTIL_EXECUTE_REF, "UTIL_EXECUTE_REF" }
+ ,{ GSN_UTIL_RELEASE_REQ, "UTIL_RELEASE_REQ" }
+ ,{ GSN_UTIL_RELEASE_CONF, "UTIL_RELEASE_CONF" }
+ ,{ GSN_UTIL_RELEASE_REF, "UTIL_RELASE_REF" }
+
+ ,{ GSN_GREP_CREATE_REQ, "GREP_CREATE_REQ" },
+ { GSN_GREP_CREATE_REF, "GREP_CREATE_REF" },
+ { GSN_GREP_CREATE_CONF, "GREP_CREATE_CONF" },
+ { GSN_GREP_START_REQ, "GREP_START_REQ" },
+ { GSN_GREP_START_REF, "GREP_START_REF" },
+ { GSN_GREP_START_CONF, "GREP_START_CONF" },
+ { GSN_GREP_SYNC_REQ, "GREP_SYNC_REQ" },
+ { GSN_GREP_SYNC_REF, "GREP_SYNC_REF" },
+ { GSN_GREP_SYNC_CONF, "GREP_SYNC_CONF" },
+ //{ GSN_REP_CONNECT_REQ, "REP_CONNECT_REQ" }, Not used
+ //{ GSN_REP_CONNECT_REF, "REP_CONNECT_REF" }, Not used
+ //{ GSN_REP_CONNECT_CONF, "REP_CONNECT_CONF" }, Not used
+ { GSN_REP_WAITGCP_REQ, "REP_WAIT_GCP_REQ" },
+ { GSN_REP_WAITGCP_REF, "REP_WAIT_GCP_REF" },
+ { GSN_REP_WAITGCP_CONF, "REP_WAIT_GCP_CONF" },
+ { GSN_GREP_WAITGCP_REQ, "GREP_WAIT_GCP_REQ" },
+ { GSN_GREP_WAITGCP_REF, "GREP_WAIT_GCP_REF" },
+ { GSN_GREP_WAITGCP_CONF, "GREP_WAIT_GCP_CONF" }
+
+ /* Suma Block Services **/
+ ,{ GSN_SUB_CREATE_REQ, "SUB_CREATE_REQ" }
+ ,{ GSN_SUB_CREATE_REF, "SUB_CREATE_REF" }
+ ,{ GSN_SUB_CREATE_CONF, "SUB_CREATE_CONF" }
+ ,{ GSN_SUB_REMOVE_REQ, "SUB_REMOVE_REQ" }
+ ,{ GSN_SUB_REMOVE_REF, "SUB_REMOVE_REF" }
+ ,{ GSN_SUB_REMOVE_CONF, "SUB_REMOVE_CONF" }
+ ,{ GSN_SUB_START_REQ, "SUB_START_REQ" }
+ ,{ GSN_SUB_START_REF, "SUB_START_REF" }
+ ,{ GSN_SUB_START_CONF, "SUB_START_CONF" }
+ ,{ GSN_SUB_STOP_REQ, "SUB_STOP_REQ" }
+ ,{ GSN_SUB_STOP_REF, "SUB_STOP_REF" }
+ ,{ GSN_SUB_STOP_CONF, "SUB_STOP_CONF" }
+ ,{ GSN_SUB_SYNC_REQ, "SUB_SYNC_REQ" }
+ ,{ GSN_SUB_SYNC_REF, "SUB_SYNC_REF" }
+ ,{ GSN_SUB_SYNC_CONF, "SUB_SYNC_CONF" }
+ ,{ GSN_SUB_META_DATA, "SUB_META_DATA" }
+ ,{ GSN_SUB_TABLE_DATA, "SUB_TABLE_DATA" }
+ ,{ GSN_SUB_SYNC_CONTINUE_REQ, "SUB_SYNC_CONTINUE_REQ" }
+ ,{ GSN_SUB_SYNC_CONTINUE_REF, "SUB_SYNC_CONTINUE_REF" }
+ ,{ GSN_SUB_SYNC_CONTINUE_CONF, "SUB_SYNC_CONTINUE_CONF" }
+ ,{ GSN_SUB_GCP_COMPLETE_REP, "SUB_GCP_COMPLETE_REP" }
+ ,{ GSN_SUB_GCP_COMPLETE_ACC, "SUB_GCP_COMPLETE_ACC" }
+
+ ,{ GSN_CREATE_SUBID_REQ, "CREATE_SUBID_REQ" }
+ ,{ GSN_CREATE_SUBID_REF, "CREATE_SUBID_REF" }
+ ,{ GSN_CREATE_SUBID_CONF, "CREATE_SUBID_CONF" }
+
+ ,{ GSN_CREATE_TABLE_REQ, "CREATE_TABLE_REQ" }
+ ,{ GSN_CREATE_TABLE_REF, "CREATE_TABLE_REF" }
+ ,{ GSN_CREATE_TABLE_CONF, "CREATE_TABLE_CONF" }
+
+ ,{ GSN_CREATE_TAB_REQ, "CREATE_TAB_REQ" }
+ ,{ GSN_CREATE_TAB_REF, "CREATE_TAB_REF" }
+ ,{ GSN_CREATE_TAB_CONF, "CREATE_TAB_CONF" }
+
+ ,{ GSN_ALTER_TABLE_REQ, "ALTER_TABLE_REQ" }
+ ,{ GSN_ALTER_TABLE_REF, "ALTER_TABLE_REF" }
+ ,{ GSN_ALTER_TABLE_CONF, "ALTER_TABLE_CONF" }
+
+ ,{ GSN_ALTER_TAB_REQ, "ALTER_TAB_REQ" }
+ ,{ GSN_ALTER_TAB_REF, "ALTER_TAB_REF" }
+ ,{ GSN_ALTER_TAB_CONF, "ALTER_TAB_CONF" }
+
+ ,{ GSN_CREATE_FRAGMENTATION_REQ, "CREATE_FRAGMENTATION_REQ" }
+ ,{ GSN_CREATE_FRAGMENTATION_REF, "CREATE_FRAGMENTATION_REF" }
+ ,{ GSN_CREATE_FRAGMENTATION_CONF, "CREATE_FRAGMENTATION_CONF" }
+
+ ,{ GSN_UTIL_CREATE_LOCK_REQ, "UTIL_CREATE_LOCK_REQ" }
+ ,{ GSN_UTIL_CREATE_LOCK_REF, "UTIL_CREATE_LOCK_REF" }
+ ,{ GSN_UTIL_CREATE_LOCK_CONF, "UTIL_CREATE_LOCK_CONF" }
+ ,{ GSN_UTIL_DESTROY_LOCK_REQ, "UTIL_DESTROY_LOCK_REQ" }
+ ,{ GSN_UTIL_DESTROY_LOCK_REF, "UTIL_DESTROY_LOCK_REF" }
+ ,{ GSN_UTIL_DESTROY_LOCK_CONF, "UTIL_DESTROY_LOCK_CONF" }
+ ,{ GSN_UTIL_LOCK_REQ, "UTIL_LOCK_REQ" }
+ ,{ GSN_UTIL_LOCK_REF, "UTIL_LOCK_REF" }
+ ,{ GSN_UTIL_LOCK_CONF, "UTIL_LOCK_CONF" }
+ ,{ GSN_UTIL_UNLOCK_REQ, "UTIL_UNLOCK_REQ" }
+ ,{ GSN_UTIL_UNLOCK_REF, "UTIL_UNLOCK_REF" }
+ ,{ GSN_UTIL_UNLOCK_CONF, "UTIL_UNLOCK_CONF" }
+
+ /* TUX */
+ ,{ GSN_TUXFRAGREQ, "TUXFRAGREQ" }
+ ,{ GSN_TUXFRAGCONF, "TUXFRAGCONF" }
+ ,{ GSN_TUXFRAGREF, "TUXFRAGREF" }
+ ,{ GSN_TUX_ADD_ATTRREQ, "TUX_ADD_ATTRREQ" }
+ ,{ GSN_TUX_ADD_ATTRCONF, "TUX_ADD_ATTRCONF" }
+ ,{ GSN_TUX_ADD_ATTRREF, "TUX_ADD_ATTRREF" }
+ ,{ GSN_TUX_MAINT_REQ, "TUX_MAINT_REQ" }
+ ,{ GSN_TUX_MAINT_CONF, "TUX_MAINT_CONF" }
+ ,{ GSN_TUX_MAINT_REF, "TUX_MAINT_REF" }
+ ,{ GSN_TUX_BOUND_INFO, "TUX_BOUND_INFO" }
+ ,{ GSN_ACC_LOCKREQ, "ACC_LOCKREQ" }
+
+};
+const unsigned short NO_OF_SIGNAL_NAMES = sizeof(SignalNames)/sizeof(GsnName);
diff --git a/storage/ndb/src/common/debugger/signaldata/StartRec.cpp b/storage/ndb/src/common/debugger/signaldata/StartRec.cpp
new file mode 100644
index 00000000000..482e3cb0728
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/StartRec.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <RefConvert.hpp>
+#include <signaldata/StartRec.hpp>
+
+bool
+printSTART_REC_REQ(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+ StartRecReq * sig = (StartRecReq *) theData;
+
+ fprintf(output, " receivingNodeId: %d senderRef: (%d, %d)\n",
+ sig->receivingNodeId,
+ refToNode(sig->senderRef),
+ refToBlock(sig->senderRef));
+
+ fprintf(output, " keepGci: %d lastCompletedGci: %d newestGci: %d\n",
+ sig->keepGci,
+ sig->lastCompletedGci,
+ sig->newestGci);
+
+ return true;
+}
+
+bool
+printSTART_REC_CONF(FILE * output,
+ const Uint32 * theData,
+ Uint32 len,
+ Uint16 recBlockNo){
+ StartRecConf * sig = (StartRecConf *) theData;
+
+ fprintf(output, " startingNodeId: %d\n",
+ sig->startingNodeId);
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/SumaImpl.cpp b/storage/ndb/src/common/debugger/signaldata/SumaImpl.cpp
new file mode 100644
index 00000000000..e50a3040fe3
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/SumaImpl.cpp
@@ -0,0 +1,241 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/SumaImpl.hpp>
+
+bool
+printSUB_CREATE_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubCreateReq * const sig = (SubCreateReq *)theData;
+ fprintf(output, " subscriberRef: %x\n", sig->subscriberRef);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriptionType: %x\n", sig->subscriptionType);
+ fprintf(output, " tableId: %x\n", sig->tableId);
+ return false;
+}
+
+bool
+printSUB_CREATE_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubCreateConf * const sig = (SubCreateConf *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_CREATE_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubCreateRef * const sig = (SubCreateRef *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_REMOVE_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const SubRemoveReq * const sig = (SubRemoveReq *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ return false;
+}
+
+bool
+printSUB_REMOVE_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const SubRemoveConf * const sig = (SubRemoveConf *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_REMOVE_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const SubRemoveRef * const sig = (SubRemoveRef *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " err: %x\n", sig->err);
+ return false;
+}
+
+bool
+printSUB_START_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubStartReq * const sig = (SubStartReq *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_START_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubStartRef * const sig = (SubStartRef *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " startPart: %x\n", sig->part);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " err: %x\n", sig->err);
+ return false;
+}
+
+bool
+printSUB_START_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubStartConf * const sig = (SubStartConf *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " startPart: %x\n", sig->part);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_STOP_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubStopReq * const sig = (SubStopReq *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_STOP_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubStopRef * const sig = (SubStopRef *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " err: %x\n", sig->err);
+ return false;
+}
+
+bool
+printSUB_STOP_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubStopConf * const sig = (SubStopConf *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_SYNC_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubSyncReq * const sig = (SubSyncReq *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " syncPart: %x\n", sig->part);
+ return false;
+}
+
+bool
+printSUB_SYNC_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubSyncRef * const sig = (SubSyncRef *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " syncPart: %x\n", sig->part);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " err: %x\n", sig->err);
+ return false;
+}
+
+bool
+printSUB_SYNC_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubSyncConf * const sig = (SubSyncConf *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ fprintf(output, " syncPart: %x\n", sig->part);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ return false;
+}
+
+bool
+printSUB_META_DATA(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubMetaData * const sig = (SubMetaData *)theData;
+ fprintf(output, " gci: %x\n", sig->gci);
+ fprintf(output, " senderData: %x\n", sig->senderData);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " tableId: %x\n", sig->tableId);
+ return false;
+}
+
+bool
+printSUB_TABLE_DATA(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubTableData * const sig = (SubTableData *)theData;
+ fprintf(output, " senderData: %x\n", sig->senderData);
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " gci: %x\n", sig->gci);
+ fprintf(output, " tableId: %x\n", sig->tableId);
+ fprintf(output, " operation: %x\n", sig->operation);
+ fprintf(output, " noOfAttributes: %x\n", sig->noOfAttributes);
+ fprintf(output, " dataSize: %x\n", sig->dataSize);
+ return false;
+}
+
+bool
+printSUB_SYNC_CONTINUE_REQ(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubSyncContinueReq * const sig = (SubSyncContinueReq *)theData;
+ fprintf(output, " subscriberData: %x\n", sig->subscriberData);
+ fprintf(output, " noOfRowsSent: %x\n", sig->noOfRowsSent);
+ return false;
+}
+
+bool
+printSUB_SYNC_CONTINUE_REF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubSyncContinueRef * const sig = (SubSyncContinueRef *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ return false;
+}
+
+bool
+printSUB_SYNC_CONTINUE_CONF(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubSyncContinueConf * const sig = (SubSyncContinueConf *)theData;
+ fprintf(output, " subscriptionId: %x\n", sig->subscriptionId);
+ fprintf(output, " subscriptionKey: %x\n", sig->subscriptionKey);
+ return false;
+}
+
+bool
+printSUB_GCP_COMPLETE_REP(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo) {
+ const SubGcpCompleteRep * const sig = (SubGcpCompleteRep *)theData;
+ fprintf(output, " gci: %x\n", sig->gci);
+ return false;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/SystemError.cpp b/storage/ndb/src/common/debugger/signaldata/SystemError.cpp
new file mode 100644
index 00000000000..549c34710a0
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/SystemError.cpp
@@ -0,0 +1,40 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <kernel_types.h>
+#include <BlockNumbers.h>
+#include <signaldata/SystemError.hpp>
+
+bool
+printSYSTEM_ERROR(FILE * output, const Uint32 * theData, Uint32 len,
+ Uint16 receiverBlockNo){
+
+ const SystemError * const sig = (SystemError *) theData;
+
+ fprintf(output, "errorRef: H\'%.8x\n",
+ sig->errorRef);
+ fprintf(output, "errorCode: %d\n",
+ sig->errorCode);
+ fprintf(output, "data1: H\'%.8x\n",
+ sig->data1);
+ fprintf(output, "data2: H\'%.8x\n",
+ sig->data2);
+
+ return true;
+}
+
+
diff --git a/storage/ndb/src/common/debugger/signaldata/TcIndx.cpp b/storage/ndb/src/common/debugger/signaldata/TcIndx.cpp
new file mode 100644
index 00000000000..b0578f5b646
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TcIndx.cpp
@@ -0,0 +1,74 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <BlockNumbers.h>
+
+
+bool
+printTCINDXCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ if (receiverBlockNo == API_PACKED) {
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ }
+ else {
+ const TcIndxConf * const sig = (TcIndxConf *) theData;
+
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ Uint32 confInfo = sig->confInfo;
+ Uint32 noOfOp = TcIndxConf::getNoOfOperations(confInfo);
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+ fprintf(output, "apiConnectPtr: H'%.8x, gci: %u, transId:(H'%.8x, H'%.8x)\n",
+ sig->apiConnectPtr, sig->gci, sig->transId1, sig->transId2);
+
+ fprintf(output, "noOfOperations: %u, commitFlag: %s, markerFlag: %s\n",
+ noOfOp,
+ (TcIndxConf::getCommitFlag(confInfo) == 0)?"false":"true",
+ (TcIndxConf::getMarkerFlag(confInfo) == 0)?"false":"true");
+ fprintf(output, "Operations:\n");
+ for(i = 0; i < noOfOp; i++) {
+ fprintf(output,
+ "apiOperationPtr: H'%.8x, attrInfoLen: %u\n",
+ sig->operations[i].apiOperationPtr,
+ sig->operations[i].attrInfoLen);
+ }
+ }
+
+ return true;
+}
+
+bool
+printTCINDXREF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+// const TcIndxRef * const sig = (TcIndxRef *) theData;
+
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp b/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp
new file mode 100644
index 00000000000..652c2b8a557
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TcKeyConf.hpp>
+#include <BlockNumbers.h>
+
+bool
+printTCKEYCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+
+ if (receiverBlockNo == API_PACKED) {
+ return false;
+ Uint32 Theader = * theData++;
+ Uint32 TpacketLen = (Theader & 0x1F) + 3;
+ Uint32 TrecBlockNo = Theader >> 16;
+
+ do {
+ fprintf(output, "Block: %d %d %d\n", TrecBlockNo, len, TpacketLen);
+ printTCKEYCONF(output, theData, TpacketLen, TrecBlockNo);
+ assert(len >= (1 + TpacketLen));
+ len -= (1 + TpacketLen);
+ theData += TpacketLen;
+ } while(len);
+ return true;
+ }
+ else {
+ const TcKeyConf * const sig = (TcKeyConf *) theData;
+
+ Uint32 i = 0;
+ Uint32 confInfo = sig->confInfo;
+ Uint32 noOfOp = TcKeyConf::getNoOfOperations(confInfo);
+ if (noOfOp > 10) noOfOp = 10;
+ fprintf(output, " apiConnectPtr: H'%.8x, gci: %u, transId:(H'%.8x, H'%.8x)\n",
+ sig->apiConnectPtr, sig->gci, sig->transId1, sig->transId2);
+
+ fprintf(output, " noOfOperations: %u, commitFlag: %s, markerFlag: %s\n",
+ noOfOp,
+ (TcKeyConf::getCommitFlag(confInfo) == 0)?"false":"true",
+ (TcKeyConf::getMarkerFlag(confInfo) == 0)?"false":"true");
+ fprintf(output, "Operations:\n");
+ for(i = 0; i < noOfOp; i++) {
+ if(sig->operations[i].attrInfoLen > TcKeyConf::SimpleReadBit)
+ fprintf(output,
+ " apiOperationPtr: H'%.8x, simplereadnode: %u\n",
+ sig->operations[i].apiOperationPtr,
+ sig->operations[i].attrInfoLen & (~TcKeyConf::SimpleReadBit));
+ else
+ fprintf(output,
+ " apiOperationPtr: H'%.8x, attrInfoLen: %u\n",
+ sig->operations[i].apiOperationPtr,
+ sig->operations[i].attrInfoLen);
+ }
+ }
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/TcKeyRef.cpp b/storage/ndb/src/common/debugger/signaldata/TcKeyRef.cpp
new file mode 100644
index 00000000000..0dba9909caf
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TcKeyRef.cpp
@@ -0,0 +1,28 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TcKeyRef.hpp>
+
+bool
+printTCKEYREF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp b/storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp
new file mode 100644
index 00000000000..3918bd5db26
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp
@@ -0,0 +1,111 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <signaldata/TcKeyReq.hpp>
+
+bool
+printTCKEYREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+
+ const TcKeyReq * const sig = (TcKeyReq *) theData;
+
+ UintR requestInfo = sig->requestInfo;
+
+ fprintf(output, " apiConnectPtr: H\'%.8x, apiOperationPtr: H\'%.8x\n",
+ sig->apiConnectPtr, sig->apiOperationPtr);
+ fprintf(output, " Operation: %s, Flags: ",
+ sig->getOperationType(requestInfo) == ZREAD ? "Read" :
+ sig->getOperationType(requestInfo) == ZREAD_EX ? "Read-Ex" :
+ sig->getOperationType(requestInfo) == ZUPDATE ? "Update" :
+ sig->getOperationType(requestInfo) == ZINSERT ? "Insert" :
+ sig->getOperationType(requestInfo) == ZDELETE ? "Delete" :
+ sig->getOperationType(requestInfo) == ZWRITE ? "Write" :
+ "Unknown");
+ {
+ if(sig->getDirtyFlag(requestInfo)){
+ fprintf(output, "Dirty ");
+ }
+ if(sig->getStartFlag(requestInfo)){
+ fprintf(output, "Start ");
+ }
+ if(sig->getExecuteFlag(requestInfo)){
+ fprintf(output, "Execute ");
+ }
+ if(sig->getCommitFlag(requestInfo)){
+ fprintf(output, "Commit ");
+ }
+ if (sig->getExecutingTrigger(requestInfo)) {
+ fprintf(output, "Trigger ");
+ }
+
+ UintR TcommitType = sig->getAbortOption(requestInfo);
+ if (TcommitType == TcKeyReq::AbortOnError) {
+ fprintf(output, "AbortOnError ");
+ } else if (TcommitType == TcKeyReq::IgnoreError) {
+ fprintf(output, "IgnoreError ");
+ }//if
+
+ if(sig->getSimpleFlag(requestInfo)){
+ fprintf(output, "Simple ");
+ }
+ if(sig->getScanIndFlag(requestInfo)){
+ fprintf(output, "ScanInd ");
+ }
+ if(sig->getInterpretedFlag(requestInfo)){
+ fprintf(output, "Interpreted ");
+ }
+ if(sig->getDistributionKeyFlag(sig->requestInfo)){
+ fprintf(output, " d-key");
+ }
+ fprintf(output, "\n");
+ }
+
+ const int keyLen = sig->getKeyLength(requestInfo);
+ const int attrInThis = sig->getAIInTcKeyReq(requestInfo);
+ const int attrLen = sig->getAttrinfoLen(sig->attrLen);
+ const int apiVer = sig->getAPIVersion(sig->attrLen);
+ fprintf(output,
+ " keyLen: %d, attrLen: %d, AI in this: %d, tableId: %d, "
+ "tableSchemaVer: %d, API Ver: %d\n",
+ keyLen, attrLen, attrInThis,
+ sig->tableId, sig->tableSchemaVersion, apiVer);
+
+ fprintf(output, " transId(1, 2): (H\'%.8x, H\'%.8x)\n -- Variable Data --\n",
+ sig->transId1, sig->transId2);
+
+ if (len >= TcKeyReq::StaticLength) {
+ Uint32 restLen = (len - TcKeyReq::StaticLength);
+ const Uint32 * rest = &sig->scanInfo;
+ while(restLen >= 7){
+ fprintf(output,
+ " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n",
+ rest[0], rest[1], rest[2], rest[3],
+ rest[4], rest[5], rest[6]);
+ restLen -= 7;
+ rest += 7;
+ }
+ if(restLen > 0){
+ for(Uint32 i = 0; i<restLen; i++)
+ fprintf(output, " H\'%.8x", rest[i]);
+ fprintf(output, "\n");
+ }
+ } else {
+ fprintf(output, "*** invalid len %u ***\n", len);
+ }
+ return true;
+}
+
diff --git a/storage/ndb/src/common/debugger/signaldata/TcRollbackRep.cpp b/storage/ndb/src/common/debugger/signaldata/TcRollbackRep.cpp
new file mode 100644
index 00000000000..961f0c3619d
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TcRollbackRep.cpp
@@ -0,0 +1,28 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TcRollbackRep.hpp>
+
+bool
+printTCROLLBACKREP(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/TrigAttrInfo.cpp b/storage/ndb/src/common/debugger/signaldata/TrigAttrInfo.cpp
new file mode 100644
index 00000000000..7a8d176ec61
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TrigAttrInfo.cpp
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TrigAttrInfo.hpp>
+
+static
+const char *
+tatype(Uint32 i){
+ switch(i){
+ case TrigAttrInfo::PRIMARY_KEY:
+ return "PK";
+ break;
+ case TrigAttrInfo::BEFORE_VALUES:
+ return "BEFORE";
+ break;
+ case TrigAttrInfo::AFTER_VALUES:
+ return "AFTER";
+ break;
+ }
+ return "UNKNOWN";
+}
+
+bool
+printTRIG_ATTRINFO(FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const TrigAttrInfo * const sig = (TrigAttrInfo *) theData;
+
+ fprintf(output, " TriggerId: %d Type: %s ConnectPtr: %x\n",
+ sig->getTriggerId(),
+ tatype(sig->getAttrInfoType()),
+ sig->getConnectionPtr());
+
+ Uint32 i = 0;
+ while (i < len - TrigAttrInfo::StaticLength)
+ fprintf(output, " H\'%.8x", sig->getData()[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/TupCommit.cpp b/storage/ndb/src/common/debugger/signaldata/TupCommit.cpp
new file mode 100644
index 00000000000..d0391b2a8e6
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TupCommit.cpp
@@ -0,0 +1,28 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TupCommit.hpp>
+
+bool
+printTUPCOMMITREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/TupKey.cpp b/storage/ndb/src/common/debugger/signaldata/TupKey.cpp
new file mode 100644
index 00000000000..134b5fde8bc
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TupKey.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TupKey.hpp>
+
+bool
+printTUPKEYREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
+
+bool
+printTUPKEYCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
+
+bool
+printTUPKEYREF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo){
+ fprintf(output, "Signal data: ");
+ Uint32 i = 0;
+ while (i < len)
+ fprintf(output, "H\'%.8x ", theData[i++]);
+ fprintf(output,"\n");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/TuxMaint.cpp b/storage/ndb/src/common/debugger/signaldata/TuxMaint.cpp
new file mode 100644
index 00000000000..ba6a299b77d
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/TuxMaint.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TuxMaint.hpp>
+#include <SignalLoggerManager.hpp>
+#include <AttributeHeader.hpp>
+
+bool
+printTUX_MAINT_REQ(FILE* output, const Uint32* theData, Uint32 len, Uint16 rbn)
+{
+ //const bool inOut = rbn & (1 << 15);
+ const TuxMaintReq* const sig = (const TuxMaintReq*)theData;
+ fprintf(output, " errorCode=%d\n", sig->errorCode);
+ fprintf(output, " table: id=%u", sig->tableId);
+ fprintf(output, " index: id=%u", sig->indexId);
+ fprintf(output, " fragment: id=%u\n", sig->fragId);
+ fprintf(output, " tuple: loc=%u.%u version=%u\n", sig->pageId, sig->pageOffset, sig->tupVersion);
+ const Uint32 opCode = sig->opInfo & 0xFF;
+ const Uint32 opFlag = sig->opInfo >> 8;
+ switch (opCode ) {
+ case TuxMaintReq::OpAdd:
+ fprintf(output, " opCode=Add opFlag=%u\n", opFlag);
+ break;
+ case TuxMaintReq::OpRemove:
+ fprintf(output, " opCode=Remove opFlag=%u\n", opFlag);
+ break;
+ default:
+ fprintf(output, " opInfo=%x ***invalid***\n", sig->opInfo);
+ break;
+ }
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/UtilDelete.cpp b/storage/ndb/src/common/debugger/signaldata/UtilDelete.cpp
new file mode 100644
index 00000000000..b6ba53559ac
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/UtilDelete.cpp
@@ -0,0 +1,65 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/UtilDelete.hpp>
+
+bool
+printUTIL_DELETE_REQ(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ (void)l; // Don't want compiler warning
+ (void)b; // Don't want compiler warning
+
+ UtilDeleteReq* sig = (UtilDeleteReq*)data;
+ fprintf(out, " senderData: %d prepareId: %d totalDataLen: %d\n",
+ sig->senderData,
+ sig->prepareId,
+ sig->totalDataLen);
+ fprintf(out,
+ " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n"
+ " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n"
+ " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n",
+ sig->attrData[0], sig->attrData[1], sig->attrData[2],
+ sig->attrData[3], sig->attrData[4], sig->attrData[5],
+ sig->attrData[6], sig->attrData[7], sig->attrData[8],
+ sig->attrData[9], sig->attrData[10], sig->attrData[11],
+ sig->attrData[12], sig->attrData[13], sig->attrData[14],
+ sig->attrData[15], sig->attrData[16], sig->attrData[17],
+ sig->attrData[18], sig->attrData[19], sig->attrData[20],
+ sig->attrData[21]
+ );
+
+ return true;
+}
+
+bool
+printUTIL_DELETE_CONF(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ (void)l; // Don't want compiler warning
+ (void)b; // Don't want compiler warning
+
+ UtilDeleteConf* sig = (UtilDeleteConf*)data;
+ fprintf(out, " senderData: %d\n", sig->senderData);
+ return true;
+}
+
+bool
+printUTIL_DELETE_REF(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ (void)l; // Don't want compiler warning
+ (void)b; // Don't want compiler warning
+
+ UtilDeleteRef* sig = (UtilDeleteRef*)data;
+ fprintf(out, " senderData: %d\n", sig->senderData);
+ fprintf(out, " errorCode: %d\n", sig->errorCode);
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/UtilExecute.cpp b/storage/ndb/src/common/debugger/signaldata/UtilExecute.cpp
new file mode 100644
index 00000000000..2c88fa174d4
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/UtilExecute.cpp
@@ -0,0 +1,59 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/UtilExecute.hpp>
+
+bool
+printUTIL_EXECUTE_REQ(FILE* out, const Uint32 * data, Uint32 len, Uint16 rec)
+{
+ const UtilExecuteReq* const sig = (UtilExecuteReq*)data;
+ fprintf(out, " senderRef: H'%.8x, senderData: H'%.8x prepareId: %d\n",
+ sig->senderRef,
+ sig->senderData,
+ sig->prepareId);
+ return true;
+}
+
+bool
+printUTIL_EXECUTE_CONF(FILE* out, const Uint32 * data, Uint32 len, Uint16 rec)
+{
+ UtilExecuteConf* sig = (UtilExecuteConf*)data;
+ fprintf(out, " senderData: H'%.8x\n",
+ sig->senderData);
+ return true;
+}
+
+bool
+printUTIL_EXECUTE_REF(FILE* out, const Uint32 * data, Uint32 len, Uint16 rec)
+{
+ UtilExecuteRef* sig = (UtilExecuteRef*)data;
+ fprintf(out, " senderData: H'%.8x, ", sig->senderData);
+ fprintf(out, " errorCode: %s, ",
+ sig->errorCode == UtilExecuteRef::IllegalKeyNumber ?
+ "IllegalKeyNumber" :
+ sig->errorCode == UtilExecuteRef::IllegalAttrNumber ?
+ "IllegalAttrNumber" :
+ sig->errorCode == UtilExecuteRef::TCError ?
+ "TCError" :
+ sig->errorCode == UtilExecuteRef::IllegalPrepareId ?
+ "IllegalPrepareId" :
+ sig->errorCode == UtilExecuteRef::AllocationError ?
+ "AllocationError" :
+ "Unknown");
+ fprintf(out, " TCErrorCode: %d\n",
+ sig->TCErrorCode);
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp b/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp
new file mode 100644
index 00000000000..34e37c3e2d8
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp
@@ -0,0 +1,158 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/UtilLock.hpp>
+
+bool
+printUTIL_LOCK_REQ (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilLockReq *const sig = (UtilLockReq *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " requestInfo: %x\n", sig->requestInfo);
+ return true;
+}
+
+bool
+printUTIL_LOCK_CONF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilLockConf *const sig = (UtilLockConf *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " lockKey: %x\n", sig->lockKey);
+ return true;
+}
+
+bool
+printUTIL_LOCK_REF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilLockRef *const sig = (UtilLockRef *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " errorCode: %x\n", sig->errorCode);
+ return true;
+}
+
+bool
+printUTIL_UNLOCK_REQ (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilUnlockReq *const sig = (UtilUnlockReq *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " lockKey: %x\n", sig->lockKey);
+ return true;
+}
+
+bool
+printUTIL_UNLOCK_CONF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilUnlockConf *const sig = (UtilUnlockConf *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ return true;
+}
+
+bool
+printUTIL_UNLOCK_REF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilUnlockRef *const sig = (UtilUnlockRef *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " errorCode: %x\n", sig->errorCode);
+ return true;
+}
+
+bool
+printUTIL_CREATE_LOCK_REQ (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilCreateLockReq *const sig = (UtilCreateLockReq *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " lockType: %x\n", sig->lockType);
+ return true;
+}
+
+bool
+printUTIL_CREATE_LOCK_REF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilCreateLockRef *const sig = (UtilCreateLockRef *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " errorCode: %x\n", sig->errorCode);
+ return true;
+}
+
+bool
+printUTIL_CREATE_LOCK_CONF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilCreateLockConf *const sig = (UtilCreateLockConf *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ return true;
+}
+
+bool
+printUTIL_DESTROY_LOCK_REQ (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilDestroyLockReq *const sig = (UtilDestroyLockReq *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " lockKey: %x\n", sig->lockKey);
+ return true;
+}
+
+bool
+printUTIL_DESTROY_LOCK_REF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilDestroyLockRef *const sig = (UtilDestroyLockRef *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ fprintf (output, " errorCode: %x\n", sig->errorCode);
+ return true;
+}
+
+bool
+printUTIL_DESTROY_LOCK_CONF (FILE * output, const Uint32 * theData,
+ Uint32 len, Uint16 receiverBlockNo)
+{
+ const UtilDestroyLockConf *const sig = (UtilDestroyLockConf *) theData;
+ fprintf (output, " senderData: %x\n", sig->senderData);
+ fprintf (output, " senderRef: %x\n", sig->senderRef);
+ fprintf (output, " lockId: %x\n", sig->lockId);
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/UtilPrepare.cpp b/storage/ndb/src/common/debugger/signaldata/UtilPrepare.cpp
new file mode 100644
index 00000000000..adc2e299380
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/UtilPrepare.cpp
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/UtilPrepare.hpp>
+
+bool
+printUTIL_PREPARE_REQ(FILE* out, const Uint32 * data, Uint32 len, Uint16 rec)
+{
+ UtilPrepareReq* sig = (UtilPrepareReq*)data;
+ fprintf(out, " senderRef: H'%.8x senderData: H'%.8x\n",
+ sig->senderRef,
+ sig->senderData);
+
+ return true;
+}
+
+bool
+printUTIL_PREPARE_CONF(FILE* out, const Uint32 * data, Uint32 len, Uint16 rec)
+{
+ UtilPrepareConf* sig = (UtilPrepareConf*)data;
+ fprintf(out, " senderData: H'%.8x prepareId: %d\n",
+ sig->senderData,
+ sig->prepareId);
+ return true;
+}
+
+bool
+printUTIL_PREPARE_REF(FILE* out, const Uint32 * data, Uint32 len, Uint16 rec)
+{
+ UtilPrepareRef* sig = (UtilPrepareRef*)data;
+ fprintf(out, " senderData: H'%.8x, ", sig->senderData);
+ fprintf(out, " error: %d, ", sig->errorCode);
+
+ fprintf(out, " errorMsg: ");
+ switch(sig->errorCode) {
+ case UtilPrepareRef::NO_ERROR:
+ fprintf(out, "No error");
+ break;
+ case UtilPrepareRef::PREPARE_SEIZE_ERROR:
+ fprintf(out, "Failed to seize Prepare record");
+ break;
+ case UtilPrepareRef::PREPARED_OPERATION_SEIZE_ERROR:
+ fprintf(out, "Failed to seize PreparedOperation record");
+ break;
+ case UtilPrepareRef::DICT_TAB_INFO_ERROR:
+ fprintf(out, "Failed to get table info from DICT");
+ break;
+ }
+ fprintf(out, "\n");
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/UtilSequence.cpp b/storage/ndb/src/common/debugger/signaldata/UtilSequence.cpp
new file mode 100644
index 00000000000..e91999d9abf
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/UtilSequence.cpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/UtilSequence.hpp>
+
+inline
+const char *
+type2string(UtilSequenceReq::RequestType type){
+ switch(type){
+ case UtilSequenceReq::NextVal:
+ return "NextVal";
+ case UtilSequenceReq::CurrVal:
+ return "CurrVal";
+ case UtilSequenceReq::Create:
+ return "Create";
+ default:
+ return "Unknown";
+ }
+}
+
+bool
+printUTIL_SEQUENCE_REQ(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ UtilSequenceReq* sig = (UtilSequenceReq*)data;
+ fprintf(out, " senderData: %d sequenceId: %d RequestType: %s\n",
+ sig->senderData,
+ sig->sequenceId,
+ type2string((UtilSequenceReq::RequestType)sig->requestType));
+ return true;
+}
+
+bool
+printUTIL_SEQUENCE_CONF(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ UtilSequenceConf* sig = (UtilSequenceConf*)data;
+ fprintf(out, " senderData: %d sequenceId: %d RequestType: %s\n",
+ sig->senderData,
+ sig->sequenceId,
+ type2string((UtilSequenceReq::RequestType)sig->requestType));
+ fprintf(out, " val: [ %d %d ]\n",
+ sig->sequenceValue[0],
+ sig->sequenceValue[1]);
+ return true;
+}
+
+bool
+printUTIL_SEQUENCE_REF(FILE * out, const Uint32 * data, Uint32 l, Uint16 b){
+ UtilSequenceRef* sig = (UtilSequenceRef*)data;
+ fprintf(out, " senderData: %d sequenceId: %d RequestType: %s\n",
+ sig->senderData,
+ sig->sequenceId,
+ type2string((UtilSequenceReq::RequestType)sig->requestType));
+ fprintf(out, " errorCode: %d, TCErrorCode: %d\n",
+ sig->errorCode, sig->TCErrorCode);
+ return true;
+}
diff --git a/storage/ndb/src/common/debugger/signaldata/print.awk b/storage/ndb/src/common/debugger/signaldata/print.awk
new file mode 100644
index 00000000000..9730fb4a236
--- /dev/null
+++ b/storage/ndb/src/common/debugger/signaldata/print.awk
@@ -0,0 +1,55 @@
+BEGIN {
+ m_curr="";
+ m_count=0;
+ m_level=0;
+}
+/^[ ]*class[ ]+.*{/ {
+ if(m_curr != ""){
+ print;
+ print "ERROR: " m_curr;
+ exit;
+ }
+ m_curr = $2;
+}
+/{/ {
+ m_level++;
+}
+/bool print/{
+ m_print=$3;
+ i=index($3, "(");
+ if(i > 0){
+ m_print=substr($3, 0, i-1);
+ }
+}
+
+/[ ]+Uint32[ ]+[^)]*;/ {
+ if(m_level >= 0){
+ m=$2;
+ i=index($2, ";");
+ if(i > 0){
+ m=substr($2, 0, i-1);
+ }
+ m_members[m_count]=m;
+ m_count++;
+ }
+}
+/^[ ]*}[ ]*;/ {
+ m_level--;
+ if(m_level == 0){
+ if(m_count > 0 && m_print != ""){
+ print "bool";
+ print m_print "(FILE * output, const Uint32 * theData, ";
+ print "Uint32 len, Uint16 receiverBlockNo) {";
+ print "const " m_curr " * const sig = (" m_curr " *)theData;";
+ for(i = 0; i<m_count; i++){
+ print "fprintf(output, \" " m_members[i] ": %x\\n\", sig->" m_members[i] ");";
+ }
+ print "return true;";
+ print "}";
+ print "";
+ }
+ m_curr="";
+ m_print="";
+ m_count=0;
+ }
+}
diff --git a/storage/ndb/src/common/logger/ConsoleLogHandler.cpp b/storage/ndb/src/common/logger/ConsoleLogHandler.cpp
new file mode 100644
index 00000000000..94367d2fc45
--- /dev/null
+++ b/storage/ndb/src/common/logger/ConsoleLogHandler.cpp
@@ -0,0 +1,68 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ConsoleLogHandler.hpp"
+
+#include <NdbOut.hpp>
+
+ConsoleLogHandler::ConsoleLogHandler() : LogHandler()
+{
+}
+
+ConsoleLogHandler::~ConsoleLogHandler()
+{
+
+}
+
+bool
+ConsoleLogHandler::open()
+{
+ return true;
+}
+
+bool
+ConsoleLogHandler::close()
+{
+ return true;
+}
+
+//
+// PROTECTED
+//
+void
+ConsoleLogHandler::writeHeader(const char* pCategory, Logger::LoggerLevel level)
+{
+ char str[LogHandler::MAX_HEADER_LENGTH];
+ ndbout << getDefaultHeader(str, pCategory, level);
+}
+
+void
+ConsoleLogHandler::writeMessage(const char* pMsg)
+{
+ ndbout << pMsg;
+}
+
+void
+ConsoleLogHandler::writeFooter()
+{
+ ndbout << getDefaultFooter() << flush;
+}
+
+
+bool
+ConsoleLogHandler::setParam(const BaseString &param, const BaseString &value) {
+ return false;
+}
diff --git a/storage/ndb/src/common/logger/FileLogHandler.cpp b/storage/ndb/src/common/logger/FileLogHandler.cpp
new file mode 100644
index 00000000000..8678b999b6f
--- /dev/null
+++ b/storage/ndb/src/common/logger/FileLogHandler.cpp
@@ -0,0 +1,235 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <FileLogHandler.hpp>
+#include <File.hpp>
+
+//
+// PUBLIC
+//
+
+FileLogHandler::FileLogHandler() :
+ LogHandler(),
+ m_maxNoFiles(MAX_NO_FILES),
+ m_maxFileSize(MAX_FILE_SIZE),
+ m_maxLogEntries(MAX_LOG_ENTRIES)
+
+{
+ m_pLogFile = new File_class("logger.log", "a+");
+}
+
+FileLogHandler::FileLogHandler(const char* aFileName,
+ int maxNoFiles,
+ long maxFileSize,
+ unsigned int maxLogEntries) :
+ LogHandler(),
+ m_maxNoFiles(maxNoFiles),
+ m_maxFileSize(maxFileSize),
+ m_maxLogEntries(maxLogEntries)
+{
+ m_pLogFile = new File_class(aFileName, "a+");
+}
+
+FileLogHandler::~FileLogHandler()
+{
+ delete m_pLogFile;
+}
+
+bool
+FileLogHandler::open()
+{
+ bool rc = true;
+
+ if (m_pLogFile->open())
+ {
+ if (isTimeForNewFile())
+ {
+ if (!createNewFile())
+ {
+ setErrorCode(errno);
+ rc = false;
+ }
+ }
+ }
+ else
+ {
+ setErrorCode(errno);
+ rc = false;
+ }
+
+ return rc;
+}
+
+bool
+FileLogHandler::close()
+{
+ bool rc = true;
+ if (!m_pLogFile->close())
+ {
+ setErrorCode(errno);
+ rc = false;
+ }
+
+ return rc;
+}
+
+void
+FileLogHandler::writeHeader(const char* pCategory, Logger::LoggerLevel level)
+{
+ char str[LogHandler::MAX_HEADER_LENGTH];
+ m_pLogFile->writeChar(getDefaultHeader(str, pCategory, level));
+}
+
+void
+FileLogHandler::writeMessage(const char* pMsg)
+{
+ m_pLogFile->writeChar(pMsg);
+}
+
+void
+FileLogHandler::writeFooter()
+{
+ static int callCount = 0;
+ m_pLogFile->writeChar(getDefaultFooter());
+ /**
+ * The reason I also check the number of log entries instead of
+ * only the log size, is that I do not want to check the file size
+ * after each log entry which requires system calls and is quite slow.
+ * TODO: Any better way?
+ */
+ if (callCount % m_maxLogEntries != 0) // Check every m_maxLogEntries
+ {
+ if (isTimeForNewFile())
+ {
+ if (!createNewFile())
+ {
+ // Baby one more time...
+ createNewFile();
+ }
+ }
+ callCount = 0;
+ }
+ callCount++;
+
+ // Needed on Cello since writes to the flash disk does not happen until
+ // we flush and fsync.
+ m_pLogFile->flush();
+}
+
+
+//
+// PRIVATE
+//
+
+bool
+FileLogHandler::isTimeForNewFile()
+{
+ return (m_pLogFile->size() >= m_maxFileSize);
+}
+
+bool
+FileLogHandler::createNewFile()
+{
+ bool rc = true;
+ int fileNo = 1;
+ char newName[PATH_MAX];
+
+ do
+ {
+ if (fileNo >= m_maxNoFiles)
+ {
+ fileNo = 1;
+ BaseString::snprintf(newName, sizeof(newName),
+ "%s.%d", m_pLogFile->getName(), fileNo);
+ break;
+ }
+ BaseString::snprintf(newName, sizeof(newName),
+ "%s.%d", m_pLogFile->getName(), fileNo++);
+
+ } while (File_class::exists(newName));
+
+ m_pLogFile->close();
+ if (!File_class::rename(m_pLogFile->getName(), newName))
+ {
+ setErrorCode(errno);
+ rc = false;
+ }
+
+ // Open again
+ if (!m_pLogFile->open())
+ {
+ setErrorCode(errno);
+ rc = false;
+ }
+
+ return rc;
+}
+
+bool
+FileLogHandler::setParam(const BaseString &param, const BaseString &value){
+ if(param == "filename")
+ return setFilename(value);
+ if(param == "maxsize")
+ return setMaxSize(value);
+ if(param == "maxfiles")
+ return setMaxFiles(value);
+ return false;
+}
+
+bool
+FileLogHandler::setFilename(const BaseString &filename) {
+ close();
+ if(m_pLogFile)
+ delete m_pLogFile;
+ m_pLogFile = new File_class(filename.c_str(), "a+");
+ open();
+ return true;
+}
+
+bool
+FileLogHandler::setMaxSize(const BaseString &size) {
+ char *end;
+ long val = strtol(size.c_str(), &end, 0); /* XXX */
+ if(size.c_str() == end)
+ return false;
+ if(end[0] == 'M')
+ val *= 1024*1024;
+ if(end[0] == 'k')
+ val *= 1024;
+
+ m_maxFileSize = val;
+
+ return true;
+}
+
+bool
+FileLogHandler::setMaxFiles(const BaseString &files) {
+ char *end;
+ long val = strtol(files.c_str(), &end, 0);
+ if(files.c_str() == end)
+ return false;
+ m_maxNoFiles = val;
+
+ return true;
+}
+
+bool
+FileLogHandler::checkParams() {
+ if(m_pLogFile == NULL)
+ return false;
+ return true;
+}
diff --git a/storage/ndb/src/common/logger/LogHandler.cpp b/storage/ndb/src/common/logger/LogHandler.cpp
new file mode 100644
index 00000000000..ec4137297f1
--- /dev/null
+++ b/storage/ndb/src/common/logger/LogHandler.cpp
@@ -0,0 +1,184 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "LogHandler.hpp"
+
+#include <NdbTick.h>
+
+//
+// PUBLIC
+//
+LogHandler::LogHandler() :
+ m_pDateTimeFormat("%d-%.2d-%.2d %.2d:%.2d:%.2d"),
+ m_errorCode(0)
+{
+ m_max_repeat_frequency= 3; // repeat messages maximum every 3 seconds
+ m_count_repeated_messages= 0;
+ m_last_category[0]= 0;
+ m_last_message[0]= 0;
+ m_last_log_time= 0;
+ m_now= 0;
+}
+
+LogHandler::~LogHandler()
+{
+}
+
+void
+LogHandler::append(const char* pCategory, Logger::LoggerLevel level,
+ const char* pMsg)
+{
+ time_t now;
+ now= ::time((time_t*)NULL);
+
+ if (level != m_last_level ||
+ strcmp(pCategory, m_last_category) ||
+ strcmp(pMsg, m_last_message))
+ {
+ if (m_count_repeated_messages > 0) // print that message
+ append_impl(m_last_category, m_last_level, m_last_message);
+
+ m_last_level= level;
+ strncpy(m_last_category, pCategory, sizeof(m_last_category));
+ strncpy(m_last_message, pMsg, sizeof(m_last_message));
+ }
+ else // repeated message
+ {
+ if (now < m_last_log_time+m_max_repeat_frequency)
+ {
+ m_count_repeated_messages++;
+ m_now= now;
+ return;
+ }
+ }
+
+ m_now= now;
+
+ append_impl(pCategory, level, pMsg);
+ m_last_log_time= now;
+}
+
+void
+LogHandler::append_impl(const char* pCategory, Logger::LoggerLevel level,
+ const char* pMsg)
+{
+ writeHeader(pCategory, level);
+ if (m_count_repeated_messages <= 1)
+ writeMessage(pMsg);
+ else
+ {
+ BaseString str(pMsg);
+ str.appfmt(" - Repeated %d times", m_count_repeated_messages);
+ writeMessage(str.c_str());
+ }
+ m_count_repeated_messages= 0;
+ writeFooter();
+}
+
+const char*
+LogHandler::getDefaultHeader(char* pStr, const char* pCategory,
+ Logger::LoggerLevel level) const
+{
+ char time[MAX_DATE_TIME_HEADER_LENGTH];
+ BaseString::snprintf(pStr, MAX_HEADER_LENGTH, "%s [%s] %s -- ",
+ getTimeAsString((char*)time),
+ pCategory,
+ Logger::LoggerLevelNames[level]);
+
+ return pStr;
+}
+
+
+const char*
+LogHandler::getDefaultFooter() const
+{
+ return "\n";
+}
+
+const char*
+LogHandler::getDateTimeFormat() const
+{
+ return m_pDateTimeFormat;
+}
+
+void
+LogHandler::setDateTimeFormat(const char* pFormat)
+{
+ m_pDateTimeFormat = (char*)pFormat;
+}
+
+char*
+LogHandler::getTimeAsString(char* pStr) const
+{
+ struct tm* tm_now;
+#ifdef NDB_WIN32
+ tm_now = localtime(&m_now);
+#else
+ tm_now = ::localtime(&m_now); //uses the "current" timezone
+#endif
+
+ BaseString::snprintf(pStr, MAX_DATE_TIME_HEADER_LENGTH,
+ m_pDateTimeFormat,
+ tm_now->tm_year + 1900,
+ tm_now->tm_mon + 1, //month is [0,11]. +1 -> [1,12]
+ tm_now->tm_mday,
+ tm_now->tm_hour,
+ tm_now->tm_min,
+ tm_now->tm_sec);
+
+ return pStr;
+}
+
+int
+LogHandler::getErrorCode() const
+{
+ return m_errorCode;
+}
+
+void
+LogHandler::setErrorCode(int code)
+{
+ m_errorCode = code;
+}
+
+bool
+LogHandler::parseParams(const BaseString &_params) {
+ Vector<BaseString> v_args;
+
+ bool ret = true;
+
+ _params.split(v_args, ",");
+ for(size_t i=0; i < v_args.size(); i++) {
+ Vector<BaseString> v_param_value;
+ if(v_args[i].split(v_param_value, "=", 2) != 2)
+ ret = false;
+ else if (!setParam(v_param_value[0], v_param_value[1]))
+ ret = false;
+ }
+
+ if(!checkParams())
+ ret = false;
+ return ret;
+}
+
+bool
+LogHandler::checkParams() {
+ return true;
+}
+
+//
+// PRIVATE
+//
diff --git a/storage/ndb/src/common/logger/LogHandlerList.cpp b/storage/ndb/src/common/logger/LogHandlerList.cpp
new file mode 100644
index 00000000000..62495d7566b
--- /dev/null
+++ b/storage/ndb/src/common/logger/LogHandlerList.cpp
@@ -0,0 +1,181 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "LogHandlerList.hpp"
+
+#include <LogHandler.hpp>
+
+//
+// PUBLIC
+//
+
+LogHandlerList::LogHandlerList() :
+ m_size(0),
+ m_pHeadNode(NULL),
+ m_pTailNode(NULL),
+ m_pCurrNode(NULL)
+{
+}
+
+LogHandlerList::~LogHandlerList()
+{
+ removeAll();
+}
+
+void
+LogHandlerList::add(LogHandler* pNewHandler)
+{
+ LogHandlerNode* pNode = new LogHandlerNode();
+
+ if (m_pHeadNode == NULL)
+ {
+ m_pHeadNode = pNode;
+ pNode->pPrev = NULL;
+ }
+ else
+ {
+ m_pTailNode->pNext = pNode;
+ pNode->pPrev = m_pTailNode;
+ }
+ m_pTailNode = pNode;
+ pNode->pNext = NULL;
+ pNode->pHandler = pNewHandler;
+
+ m_size++;
+}
+
+bool
+LogHandlerList::remove(LogHandler* pRemoveHandler)
+{
+ LogHandlerNode* pNode = m_pHeadNode;
+ bool removed = false;
+ do
+ {
+ if (pNode->pHandler == pRemoveHandler)
+ {
+ removeNode(pNode);
+ removed = true;
+ break;
+ }
+ } while ( (pNode = next(pNode)) != NULL);
+
+ return removed;
+}
+
+void
+LogHandlerList::removeAll()
+{
+ while (m_pHeadNode != NULL)
+ {
+ removeNode(m_pHeadNode);
+ }
+}
+
+LogHandler*
+LogHandlerList::next()
+{
+ LogHandler* pHandler = NULL;
+ if (m_pCurrNode == NULL)
+ {
+ m_pCurrNode = m_pHeadNode;
+ if (m_pCurrNode != NULL)
+ {
+ pHandler = m_pCurrNode->pHandler;
+ }
+ }
+ else
+ {
+ m_pCurrNode = next(m_pCurrNode); // Next node
+ if (m_pCurrNode != NULL)
+ {
+ pHandler = m_pCurrNode->pHandler;
+ }
+ }
+
+ return pHandler;
+}
+
+int
+LogHandlerList::size() const
+{
+ return m_size;
+}
+
+//
+// PRIVATE
+//
+
+LogHandlerList::LogHandlerNode*
+LogHandlerList::next(LogHandlerNode* pNode)
+{
+ LogHandlerNode* pCurr = pNode;
+ if (pNode->pNext != NULL)
+ {
+ pCurr = pNode->pNext;
+ }
+ else
+ {
+ // Tail
+ pCurr = NULL;
+ }
+ return pCurr;
+}
+
+LogHandlerList::LogHandlerNode*
+LogHandlerList::prev(LogHandlerNode* pNode)
+{
+ LogHandlerNode* pCurr = pNode;
+ if (pNode->pPrev != NULL) // head
+ {
+ pCurr = pNode->pPrev;
+ }
+ else
+ {
+ // Head
+ pCurr = NULL;
+ }
+
+ return pCurr;
+}
+
+void
+LogHandlerList::removeNode(LogHandlerNode* pNode)
+{
+ if (pNode->pPrev == NULL) // If head
+ {
+ m_pHeadNode = pNode->pNext;
+ }
+ else
+ {
+ pNode->pPrev->pNext = pNode->pNext;
+ }
+
+ if (pNode->pNext == NULL) // if tail
+ {
+ m_pTailNode = pNode->pPrev;
+ }
+ else
+ {
+ pNode->pNext->pPrev = pNode->pPrev;
+ }
+
+ pNode->pNext = NULL;
+ pNode->pPrev = NULL;
+ delete pNode->pHandler; // Delete log handler
+ delete pNode;
+
+ m_size--;
+}
diff --git a/storage/ndb/src/common/logger/LogHandlerList.hpp b/storage/ndb/src/common/logger/LogHandlerList.hpp
new file mode 100644
index 00000000000..21344023560
--- /dev/null
+++ b/storage/ndb/src/common/logger/LogHandlerList.hpp
@@ -0,0 +1,94 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 LOGHANDLERLIST_H
+#define LOGHANDLERLIST_H
+
+class LogHandler;
+#include <ndb_global.h>
+
+/**
+ * Provides a simple linked list of log handlers.
+ *
+ * @see LogHandler
+ * @version #@ $Id: LogHandlerList.hpp,v 1.2 2002/03/14 13:07:21 eyualex Exp $
+ */
+class LogHandlerList
+{
+public:
+ /**
+ * Default Constructor.
+ */
+ LogHandlerList();
+
+ /**
+ * Destructor.
+ */
+ ~LogHandlerList();
+
+ /**
+ * Adds a new log handler.
+ *
+ * @param pNewHandler log handler.
+ */
+ void add(LogHandler* pNewHandler);
+
+ /**
+ * Removes a log handler from the list and call its destructor.
+ *
+ * @param pRemoveHandler the handler to remove
+ */
+ bool remove(LogHandler* pRemoveHandler);
+
+ /**
+ * Removes all log handlers.
+ */
+ void removeAll();
+
+ /**
+ * Returns the next log handler in the list.
+ * returns a log handler or NULL.
+ */
+ LogHandler* next();
+
+ /**
+ * Returns the size of the list.
+ */
+ int size() const;
+private:
+ /** List node */
+ struct LogHandlerNode
+ {
+ LogHandlerNode* pPrev;
+ LogHandlerNode* pNext;
+ LogHandler* pHandler;
+ };
+
+ LogHandlerNode* next(LogHandlerNode* pNode);
+ LogHandlerNode* prev(LogHandlerNode* pNode);
+
+ void removeNode(LogHandlerNode* pNode);
+
+ int m_size;
+
+ LogHandlerNode* m_pHeadNode;
+ LogHandlerNode* m_pTailNode;
+ LogHandlerNode* m_pCurrNode;
+};
+
+#endif
+
+
diff --git a/storage/ndb/src/common/logger/Logger.cpp b/storage/ndb/src/common/logger/Logger.cpp
new file mode 100644
index 00000000000..7f18f5bd3ec
--- /dev/null
+++ b/storage/ndb/src/common/logger/Logger.cpp
@@ -0,0 +1,369 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "Logger.hpp"
+
+#include <LogHandler.hpp>
+#include <ConsoleLogHandler.hpp>
+#include <FileLogHandler.hpp>
+#include "LogHandlerList.hpp"
+
+#if !defined NDB_OSE || !defined NDB_SOFTOSE || !defined NDB_WIN32
+#include <SysLogHandler.hpp>
+#endif
+
+//
+// PUBLIC
+//
+const char* Logger::LoggerLevelNames[] = { "ON ",
+ "DEBUG ",
+ "INFO ",
+ "WARNING ",
+ "ERROR ",
+ "CRITICAL",
+ "ALERT ",
+ "ALL "
+ };
+Logger::Logger() :
+ m_pCategory("Logger"),
+ m_pConsoleHandler(NULL),
+ m_pFileHandler(NULL),
+ m_pSyslogHandler(NULL)
+{
+ m_pHandlerList = new LogHandlerList();
+ disable(LL_ALL);
+ enable(LL_ON);
+ enable(LL_INFO);
+}
+
+Logger::~Logger()
+{
+ removeAllHandlers();
+ delete m_pHandlerList;
+}
+
+void
+Logger::setCategory(const char* pCategory)
+{
+ m_pCategory = pCategory;
+}
+
+bool
+Logger::createConsoleHandler()
+{
+ bool rc = true;
+ if (m_pConsoleHandler == NULL)
+ {
+ m_pConsoleHandler = new ConsoleLogHandler();
+ if (!addHandler(m_pConsoleHandler)) // TODO: check error code
+ {
+ rc = false;
+ delete m_pConsoleHandler;
+ m_pConsoleHandler = NULL;
+ }
+ }
+
+ return rc;
+}
+
+void
+Logger::removeConsoleHandler()
+{
+ if (removeHandler(m_pConsoleHandler))
+ {
+ m_pConsoleHandler = NULL;
+ }
+}
+
+bool
+Logger::createFileHandler()
+{
+ bool rc = true;
+ if (m_pFileHandler == NULL)
+ {
+ m_pFileHandler = new FileLogHandler();
+ if (!addHandler(m_pFileHandler)) // TODO: check error code
+ {
+ rc = false;
+ delete m_pFileHandler;
+ m_pFileHandler = NULL;
+ }
+ }
+
+ return rc;
+}
+
+void
+Logger::removeFileHandler()
+{
+ if (removeHandler(m_pFileHandler))
+ {
+ m_pFileHandler = NULL;
+ }
+}
+
+bool
+Logger::createSyslogHandler()
+{
+ bool rc = true;
+ if (m_pSyslogHandler == NULL)
+ {
+#if defined NDB_OSE || defined NDB_SOFTOSE || defined NDB_WIN32
+ m_pSyslogHandler = new ConsoleLogHandler();
+#else
+ m_pSyslogHandler = new SysLogHandler();
+#endif
+ if (!addHandler(m_pSyslogHandler)) // TODO: check error code
+ {
+ rc = false;
+ delete m_pSyslogHandler;
+ m_pSyslogHandler = NULL;
+ }
+ }
+
+ return rc;
+}
+
+void
+Logger::removeSyslogHandler()
+{
+ if (removeHandler(m_pSyslogHandler))
+ {
+ m_pSyslogHandler = NULL;
+ }
+}
+
+bool
+Logger::addHandler(LogHandler* pHandler)
+{
+ assert(pHandler != NULL);
+
+ bool rc = pHandler->open();
+ if (rc)
+ {
+ m_pHandlerList->add(pHandler);
+ }
+ else
+ {
+ delete pHandler;
+ }
+
+ return rc;
+}
+
+bool
+Logger::addHandler(const BaseString &logstring) {
+ size_t i;
+ Vector<BaseString> logdest;
+ Vector<LogHandler *>loghandlers;
+ DBUG_ENTER("Logger::addHandler");
+
+ logstring.split(logdest, ";");
+
+ for(i = 0; i < logdest.size(); i++) {
+ DBUG_PRINT("info",("adding: %s",logdest[i].c_str()));
+
+ Vector<BaseString> v_type_args;
+ logdest[i].split(v_type_args, ":", 2);
+
+ BaseString type(v_type_args[0]);
+ BaseString params;
+ if(v_type_args.size() >= 2)
+ params = v_type_args[1];
+
+ LogHandler *handler = NULL;
+
+#ifndef NDB_WIN32
+ if(type == "SYSLOG")
+ {
+ handler = new SysLogHandler();
+ } else
+#endif
+ if(type == "FILE")
+ handler = new FileLogHandler();
+ else if(type == "CONSOLE")
+ handler = new ConsoleLogHandler();
+
+ if(handler == NULL)
+ DBUG_RETURN(false);
+ if(!handler->parseParams(params))
+ DBUG_RETURN(false);
+ loghandlers.push_back(handler);
+ }
+
+ for(i = 0; i < loghandlers.size(); i++)
+ addHandler(loghandlers[i]);
+
+ DBUG_RETURN(true); /* @todo handle errors */
+}
+
+bool
+Logger::removeHandler(LogHandler* pHandler)
+{
+ int rc = false;
+ if (pHandler != NULL)
+ {
+ rc = m_pHandlerList->remove(pHandler);
+ }
+
+ return rc;
+}
+
+void
+Logger::removeAllHandlers()
+{
+ m_pHandlerList->removeAll();
+}
+
+bool
+Logger::isEnable(LoggerLevel logLevel) const
+{
+ if (logLevel == LL_ALL)
+ {
+ for (unsigned i = 1; i < MAX_LOG_LEVELS; i++)
+ if (!m_logLevels[i])
+ return false;
+ return true;
+ }
+ return m_logLevels[logLevel];
+}
+
+void
+Logger::enable(LoggerLevel logLevel)
+{
+ if (logLevel == LL_ALL)
+ {
+ for (unsigned i = 0; i < MAX_LOG_LEVELS; i++)
+ {
+ m_logLevels[i] = true;
+ }
+ }
+ else
+ {
+ m_logLevels[logLevel] = true;
+ }
+}
+
+void
+Logger::enable(LoggerLevel fromLogLevel, LoggerLevel toLogLevel)
+{
+ if (fromLogLevel > toLogLevel)
+ {
+ LoggerLevel tmp = toLogLevel;
+ toLogLevel = fromLogLevel;
+ fromLogLevel = tmp;
+ }
+
+ for (int i = fromLogLevel; i <= toLogLevel; i++)
+ {
+ m_logLevels[i] = true;
+ }
+}
+
+void
+Logger::disable(LoggerLevel logLevel)
+{
+ if (logLevel == LL_ALL)
+ {
+ for (unsigned i = 0; i < MAX_LOG_LEVELS; i++)
+ {
+ m_logLevels[i] = false;
+ }
+ }
+ else
+ {
+ m_logLevels[logLevel] = false;
+ }
+}
+
+void
+Logger::alert(const char* pMsg, ...) const
+{
+ va_list ap;
+ va_start(ap, pMsg);
+ log(LL_ALERT, pMsg, ap);
+ va_end(ap);
+}
+
+void
+Logger::critical(const char* pMsg, ...) const
+{
+ va_list ap;
+ va_start(ap, pMsg);
+ log(LL_CRITICAL, pMsg, ap);
+ va_end(ap);
+}
+void
+Logger::error(const char* pMsg, ...) const
+{
+ va_list ap;
+ va_start(ap, pMsg);
+ log(LL_ERROR, pMsg, ap);
+ va_end(ap);
+}
+void
+Logger::warning(const char* pMsg, ...) const
+{
+ va_list ap;
+ va_start(ap, pMsg);
+ log(LL_WARNING, pMsg, ap);
+ va_end(ap);
+}
+
+void
+Logger::info(const char* pMsg, ...) const
+{
+ va_list ap;
+ va_start(ap, pMsg);
+ log(LL_INFO, pMsg, ap);
+ va_end(ap);
+}
+
+void
+Logger::debug(const char* pMsg, ...) const
+{
+ va_list ap;
+ va_start(ap, pMsg);
+ log(LL_DEBUG, pMsg, ap);
+ va_end(ap);
+}
+
+//
+// PROTECTED
+//
+
+void
+Logger::log(LoggerLevel logLevel, const char* pMsg, va_list ap) const
+{
+ if (m_logLevels[LL_ON] && m_logLevels[logLevel])
+ {
+ LogHandler* pHandler = NULL;
+ while ( (pHandler = m_pHandlerList->next()) != NULL)
+ {
+ char buf[MAX_LOG_MESSAGE_SIZE];
+ BaseString::vsnprintf(buf, sizeof(buf), pMsg, ap);
+ pHandler->append(m_pCategory, logLevel, buf);
+ }
+ }
+}
+
+//
+// PRIVATE
+//
+
+template class Vector<LogHandler*>;
diff --git a/storage/ndb/src/common/logger/Makefile.am b/storage/ndb/src/common/logger/Makefile.am
new file mode 100644
index 00000000000..0af21f9fbde
--- /dev/null
+++ b/storage/ndb/src/common/logger/Makefile.am
@@ -0,0 +1,25 @@
+
+noinst_LTLIBRARIES = liblogger.la
+
+SOURCE_WIN = Logger.cpp LogHandlerList.cpp LogHandler.cpp \
+ ConsoleLogHandler.cpp FileLogHandler.cpp
+liblogger_la_SOURCES = $(SOURCE_WIN) SysLogHandler.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+windoze-dsp: liblogger.dsp
+
+liblogger.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(SOURCE_WIN)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/logger/SysLogHandler.cpp b/storage/ndb/src/common/logger/SysLogHandler.cpp
new file mode 100644
index 00000000000..5b1b8d85ca7
--- /dev/null
+++ b/storage/ndb/src/common/logger/SysLogHandler.cpp
@@ -0,0 +1,158 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "SysLogHandler.hpp"
+
+#include <syslog.h>
+
+//
+// PUBLIC
+//
+
+SysLogHandler::SysLogHandler() :
+ m_severity(LOG_INFO),
+ m_pIdentity("NDB"),
+ m_facility(LOG_USER)
+{
+}
+
+SysLogHandler::SysLogHandler(const char* pIdentity, int facility) :
+ m_severity(LOG_INFO),
+ m_pIdentity(pIdentity),
+ m_facility(facility)
+{
+
+}
+
+SysLogHandler::~SysLogHandler()
+{
+}
+
+bool
+SysLogHandler::open()
+{
+ ::setlogmask(LOG_UPTO(LOG_DEBUG)); // Log from EMERGENCY down to DEBUG
+ ::openlog(m_pIdentity, LOG_PID|LOG_CONS|LOG_ODELAY, m_facility); // PID, CONSOLE delay openlog
+
+ return true;
+}
+
+bool
+SysLogHandler::close()
+{
+ ::closelog();
+
+ return true;
+}
+
+void
+SysLogHandler::writeHeader(const char* pCategory, Logger::LoggerLevel level)
+{
+ // Save category to be used by writeMessage...
+ m_pCategory = pCategory;
+ // Map LogLevel to syslog severity
+ switch (level)
+ {
+ case Logger::LL_ALERT:
+ m_severity = LOG_ALERT;
+ break;
+ case Logger::LL_CRITICAL:
+ m_severity = LOG_CRIT;
+ break;
+ case Logger::LL_ERROR:
+ m_severity = LOG_ERR;
+ break;
+ case Logger::LL_WARNING:
+ m_severity = LOG_WARNING;
+ break;
+ case Logger::LL_INFO:
+ m_severity = LOG_INFO;
+ break;
+ case Logger::LL_DEBUG:
+ m_severity = LOG_DEBUG;
+ break;
+ default:
+ m_severity = LOG_INFO;
+ break;
+ }
+
+}
+
+void
+SysLogHandler::writeMessage(const char* pMsg)
+{
+ ::syslog(m_facility | m_severity, "[%s] %s", m_pCategory, pMsg);
+}
+
+void
+SysLogHandler::writeFooter()
+{
+ // Need to close it everytime? Do we run out of file descriptors?
+ //::closelog();
+}
+
+bool
+SysLogHandler::setParam(const BaseString &param, const BaseString &value) {
+ if(param == "facility") {
+ return setFacility(value);
+ }
+ return false;
+}
+
+static const struct syslog_facility {
+ const char *name;
+ int value;
+} facilitynames[] = {
+ { "auth", LOG_AUTH },
+#ifdef LOG_AUTHPRIV
+ { "authpriv", LOG_AUTHPRIV },
+#endif
+ { "cron", LOG_CRON },
+ { "daemon", LOG_DAEMON },
+#ifdef LOG_FTP
+ { "ftp", LOG_FTP },
+#endif
+ { "kern", LOG_KERN },
+ { "lpr", LOG_LPR },
+ { "mail", LOG_MAIL },
+ { "news", LOG_NEWS },
+ { "syslog", LOG_SYSLOG },
+ { "user", LOG_USER },
+ { "uucp", LOG_UUCP },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { NULL, -1 }
+};
+
+bool
+SysLogHandler::setFacility(const BaseString &facility) {
+ const struct syslog_facility *c;
+ for(c = facilitynames; c->name != NULL; c++) {
+ if(facility == c->name) {
+ m_facility = c->value;
+ close();
+ open();
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.cpp b/storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.cpp
new file mode 100644
index 00000000000..7de9ee46479
--- /dev/null
+++ b/storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.cpp
@@ -0,0 +1,164 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "LogHandlerListUnitTest.hpp"
+
+#include <ConsoleLogHandler.hpp>
+#include <FileLogHandler.hpp>
+#include <SysLogHandler.hpp>
+
+#include <NdbOut.hpp>
+
+typedef bool (*TESTFUNC)(const char*);
+typedef struct
+{
+ const char* name;
+ TESTFUNC test;
+}Tests;
+
+static Tests testCases[] = { {"Add", &LogHandlerListUnitTest::testAdd},
+ {"Remove", &LogHandlerListUnitTest::testRemove},
+ {"Traverse Next", &LogHandlerListUnitTest::testTraverseNext}
+ };
+
+
+int testFailed = 0;
+
+int main(int argc, char* argv[])
+{
+ char str[256];
+ int testCount = (sizeof(testCases) / sizeof(Tests));
+ ndbout << "Starting " << testCount << " tests..." << endl;
+ for (int i = 0; i < testCount; i++)
+ {
+ ndbout << "-- " << " Test " << i + 1
+ << " [" << testCases[i].name << "] --" << endl;
+ BaseString::snprintf(str, 256, "%s %s %s %d", "Logging ",
+ testCases[i].name, " message ", i);
+ if (testCases[i].test(str))
+ {
+ ndbout << "-- Passed --" << endl;
+ }
+ else
+ {
+ ndbout << "-- Failed -- " << endl;
+ }
+
+ }
+ ndbout << endl << "-- " << testCount - testFailed << " passed, "
+ << testFailed << " failed --" << endl;
+
+ return 0;
+}
+
+bool
+LogHandlerListUnitTest::testAdd(const char* msg)
+{
+ bool rc = true;
+ LogHandlerList list;
+ int size = 10;
+ for (int i = 0; i < size; i++)
+ {
+ list.add(new ConsoleLogHandler());
+ }
+ if (list.size() != size)
+ {
+ rc = false;
+ }
+ ndbout << "List size: " << list.size() << endl;
+
+
+ return rc;
+}
+bool
+LogHandlerListUnitTest::testRemove(const char* msg)
+{
+ bool rc = true;
+
+ LogHandlerList list;
+ int size = 10;
+ LogHandler* pHandlers[10];
+ for (int i = 0; i < size; i++)
+ {
+ pHandlers[i] = new ConsoleLogHandler();
+ list.add(pHandlers[i]);
+ }
+
+ // Remove
+
+ for (int i = 0; i < size; i++)
+ {
+ if (!list.remove(pHandlers[i]))
+ {
+ ndbout << "Could not remove handler!" << endl;
+ }
+ else
+ {
+ ndbout << "List size: " << list.size() << endl;
+ }
+ }
+
+ return rc;
+
+}
+bool
+LogHandlerListUnitTest::testTraverseNext(const char* msg)
+{
+ bool rc = true;
+ LogHandlerList list;
+ int size = 10;
+ LogHandler* pHandlers[10];
+
+ for (int i = 0; i < size; i++)
+ {
+ char* str = new char[3];
+ pHandlers[i] = new ConsoleLogHandler();
+ BaseString::snprintf(str, 3, "%d", i);
+ pHandlers[i]->setDateTimeFormat(str);
+ list.add(pHandlers[i]);
+ }
+
+ ndbout << "List size: " << list.size() << endl;
+
+ LogHandler* pHandler = NULL;
+ int i = 0;
+ while ((pHandler = list.next()) != NULL)
+ {
+ ndbout << "Handler[" << i++ << "]:dateformat = "
+ << pHandler->getDateTimeFormat() << endl;
+ }
+
+ list.removeAll();
+
+ return rc;
+
+}
+
+void
+LogHandlerListUnitTest::error(const char* msg)
+{
+ testFailed++;
+ ndbout << "Test failed: " << msg << endl;
+}
+
+LogHandlerListUnitTest::LogHandlerListUnitTest()
+{
+}
+LogHandlerListUnitTest::~LogHandlerListUnitTest()
+{
+}
diff --git a/storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.hpp b/storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.hpp
new file mode 100644
index 00000000000..e98a2722b8d
--- /dev/null
+++ b/storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.hpp
@@ -0,0 +1,40 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 LOGHANDLERLISTUNITTEST_H
+#define LOGHANDLERLISTUNITTEST_H
+
+#include "LogHandlerList.hpp"
+
+/**
+ * Unit test of LogHandlerList.
+ *
+ * @version #@ $Id: LogHandlerListUnitTest.hpp,v 1.1 2002/03/13 17:59:15 eyualex Exp $
+ */
+class LogHandlerListUnitTest
+{
+public:
+
+ static bool testAdd(const char* msg);
+ static bool testRemove(const char* msg);
+ static bool testTraverseNext(const char* msg);
+
+ void error(const char* msg);
+
+ LogHandlerListUnitTest();
+ ~LogHandlerListUnitTest();
+};
+#endif
diff --git a/storage/ndb/src/common/logger/listtest/Makefile b/storage/ndb/src/common/logger/listtest/Makefile
new file mode 100644
index 00000000000..4688a5e5a2f
--- /dev/null
+++ b/storage/ndb/src/common/logger/listtest/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE :=
+
+BIN_TARGET := listtest
+BIN_TARGET_ARCHIVES := portlib logger general
+
+SOURCES := LogHandlerListUnitTest.cpp
+
+CCFLAGS_LOC += -I../ -I$(NDB_TOP)/include/logger -I$(NDB_TOP)/include/portlib
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/src/common/logger/loggertest/LoggerUnitTest.cpp b/storage/ndb/src/common/logger/loggertest/LoggerUnitTest.cpp
new file mode 100644
index 00000000000..990d2e0eada
--- /dev/null
+++ b/storage/ndb/src/common/logger/loggertest/LoggerUnitTest.cpp
@@ -0,0 +1,197 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "LoggerUnitTest.hpp"
+
+#include <Logger.hpp>
+#include <ConsoleLogHandler.hpp>
+#include <FileLogHandler.hpp>
+
+#if !defined NDB_OSE || !defined NDB_SOFTOSE
+#include <SysLogHandler.hpp>
+#endif
+
+#include <NdbOut.hpp>
+#include <NdbMain.h>
+
+typedef bool (*TESTFUNC)(const char*);
+typedef struct
+{
+ const char* name;
+ TESTFUNC test;
+}Tests;
+
+static Tests testCases[] = { {"Alert", &LoggerUnitTest::testAlert},
+ {"Critical", &LoggerUnitTest::testCritical},
+ {"Error", &LoggerUnitTest::testError},
+ {"Warning", &LoggerUnitTest::testWarning},
+ {"Info", &LoggerUnitTest::testInfo},
+ {"Debug", &LoggerUnitTest::testDebug},
+ {"Info to Critical", &LoggerUnitTest::testInfoCritical},
+ {"All", &LoggerUnitTest::testAll},
+ {"Off", &LoggerUnitTest::testOff}
+ };
+
+static Logger logger;
+int testFailed = 0;
+
+NDB_COMMAND(loggertest, "loggertest", "loggertest -console | -file",
+ "loggertest", 16384)
+{
+ if (argc < 2)
+ {
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ ndbout << "Usage: loggertest -console | -file" << endl;
+#else
+ ndbout << "Usage: loggertest -console | -file | -syslog" << endl;
+#endif
+ return 0;
+ }
+
+ if (strcmp(argv[1], "-console") == 0)
+ {
+ logger.createConsoleHandler();
+ }
+ else if (strcmp(argv[1], "-file") == 0)
+ {
+ logger.createFileHandler();
+ //logger.addHandler(new FileLogHandler(argv[2]));
+ }
+#if !defined NDB_OSE || !defined NDB_SOFTOSE
+ else if (strcmp(argv[1], "-syslog") == 0)
+ {
+ logger.createSyslogHandler();
+ }
+#endif
+
+ logger.disable(Logger::LL_ALL);
+
+ char str[256];
+ int testCount = (sizeof(testCases) / sizeof(Tests));
+ ndbout << "Starting " << testCount << " tests..." << endl;
+ for (int i = 0; i < testCount; i++)
+ {
+ ndbout << "-- " << " Test " << i + 1
+ << " [" << testCases[i].name << "] --" << endl;
+ BaseString::snprintf(str, 256, "%s %s %s %d", "Logging ",
+ testCases[i].name, " message ", i);
+ if (testCases[i].test(str))
+ {
+ ndbout << "-- Passed --" << endl;
+ }
+ else
+ {
+ ndbout << "-- Failed -- " << endl;
+ }
+
+ }
+ ndbout << endl << "-- " << testCount - testFailed << " passed, "
+ << testFailed << " failed --" << endl;
+
+ logger.removeAllHandlers(); // Need to remove all for OSE,
+ // because logger is global
+ return 0;
+}
+
+bool
+LoggerUnitTest::logTo(Logger::LoggerLevel from, Logger::LoggerLevel to, const char* msg)
+{
+ logger.enable(from, to);
+ return logTo(from, msg);
+}
+
+bool
+LoggerUnitTest::logTo(Logger::LoggerLevel level, const char* msg)
+{
+ logger.enable(level);
+ logger.alert(msg);
+ logger.critical(msg);
+ logger.error(msg);
+ logger.warning(msg);
+ logger.info(msg);
+ logger.debug(msg);
+ logger.disable(level);
+ return true;
+}
+
+bool
+LoggerUnitTest::testAll(const char* msg)
+{
+ return logTo(Logger::LL_ALL, msg);
+}
+
+bool
+LoggerUnitTest::testOff(const char* msg)
+{
+ return logTo(Logger::LL_OFF, msg);
+
+}
+
+bool
+LoggerUnitTest::testAlert(const char* msg)
+{
+ return logTo(Logger::LL_ALERT, msg);
+}
+
+bool
+LoggerUnitTest::testCritical(const char* msg)
+{
+ return logTo(Logger::LL_CRITICAL, msg);
+}
+
+bool
+LoggerUnitTest::testError(const char* msg)
+{
+ return logTo(Logger::LL_ERROR, msg);
+}
+
+bool
+LoggerUnitTest::testWarning(const char* msg)
+{
+ return logTo(Logger::LL_WARNING, msg);
+}
+
+bool
+LoggerUnitTest::testInfo(const char* msg)
+{
+ return logTo(Logger::LL_INFO, msg);
+}
+
+bool
+LoggerUnitTest::testDebug(const char* msg)
+{
+ return logTo(Logger::LL_DEBUG, msg);
+}
+
+bool
+LoggerUnitTest::testInfoCritical(const char* msg)
+{
+ return logTo(Logger::LL_CRITICAL, Logger::LL_INFO, msg);
+}
+
+void
+LoggerUnitTest::error(const char* msg)
+{
+ testFailed++;
+ ndbout << "Test failed: " << msg << endl;
+}
+
+LoggerUnitTest::LoggerUnitTest()
+{
+}
+LoggerUnitTest::~LoggerUnitTest()
+{
+}
diff --git a/storage/ndb/src/common/logger/loggertest/LoggerUnitTest.hpp b/storage/ndb/src/common/logger/loggertest/LoggerUnitTest.hpp
new file mode 100644
index 00000000000..79f560750d5
--- /dev/null
+++ b/storage/ndb/src/common/logger/loggertest/LoggerUnitTest.hpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 LOGGERUNITTEST_H
+#define LOGGERUNITTEST_H
+
+#include "Logger.hpp"
+
+/**
+ * Unit test of Logger.
+ *
+ * @version #@ $Id: LoggerUnitTest.hpp,v 1.1 2002/03/13 17:55:31 eyualex Exp $
+ */
+class LoggerUnitTest
+{
+public:
+
+ static bool testAll(const char* msg);
+ static bool testOff(const char* msg);
+ static bool testAlert(const char* msg);
+ static bool testCritical(const char* msg);
+ static bool testError(const char* msg);
+ static bool testWarning(const char* msg);
+ static bool testInfo(const char* msg);
+ static bool testDebug(const char* msg);
+ static bool testInfoCritical(const char* msg);
+
+ static bool logTo(Logger::LoggerLevel level, const char* msg);
+ static bool logTo(Logger::LoggerLevel from, Logger::LoggerLevel to, const char* msg);
+
+ void error(const char* msg);
+
+ LoggerUnitTest();
+ ~LoggerUnitTest();
+};
+#endif
diff --git a/storage/ndb/src/common/logger/loggertest/Makefile b/storage/ndb/src/common/logger/loggertest/Makefile
new file mode 100644
index 00000000000..0aef0ca2bce
--- /dev/null
+++ b/storage/ndb/src/common/logger/loggertest/Makefile
@@ -0,0 +1,16 @@
+include .defs.mk
+
+TYPE :=
+
+BIN_TARGET := loggertest
+BIN_TARGET_ARCHIVES := logger portlib general
+
+SOURCES := LoggerUnitTest.cpp
+
+CCFLAGS_LOC += -I$(NDB_TOP)/include/logger \
+ -I$(NDB_TOP)/include/util \
+ -I$(NDB_TOP)/include/portlib
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp b/storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp
new file mode 100644
index 00000000000..fd04ad393eb
--- /dev/null
+++ b/storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp
@@ -0,0 +1,345 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_version.h>
+
+#include <ConfigRetriever.hpp>
+#include <SocketServer.hpp>
+
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+
+#include <NdbTCP.h>
+#include <NdbEnv.h>
+#include "MgmtErrorReporter.hpp"
+
+#include <uucode.h>
+#include <Properties.hpp>
+
+#include <socket_io.h>
+#include <NdbConfig.h>
+
+#include <NdbAutoPtr.hpp>
+
+#include <mgmapi.h>
+#include <mgmapi_config_parameters.h>
+#include <mgmapi_configuration.hpp>
+#include <ConfigValues.hpp>
+#include <NdbHost.h>
+
+//****************************************************************************
+//****************************************************************************
+
+ConfigRetriever::ConfigRetriever(const char * _connect_string,
+ Uint32 version, Uint32 node_type)
+{
+ m_version = version;
+ m_node_type = node_type;
+ _ownNodeId= 0;
+
+ m_handle= ndb_mgm_create_handle();
+
+ if (m_handle == 0) {
+ setError(CR_ERROR, "Unable to allocate mgm handle");
+ return;
+ }
+
+ if (ndb_mgm_set_connectstring(m_handle, _connect_string))
+ {
+ setError(CR_ERROR, ndb_mgm_get_latest_error_desc(m_handle));
+ return;
+ }
+ resetError();
+}
+
+ConfigRetriever::~ConfigRetriever()
+{
+ if (m_handle) {
+ ndb_mgm_disconnect(m_handle);
+ ndb_mgm_destroy_handle(&m_handle);
+ }
+}
+
+Uint32
+ConfigRetriever::get_configuration_nodeid() const
+{
+ return ndb_mgm_get_configuration_nodeid(m_handle);
+}
+
+Uint32 ConfigRetriever::get_mgmd_port() const
+{
+ return ndb_mgm_get_connected_port(m_handle);
+}
+
+const char *ConfigRetriever::get_mgmd_host() const
+{
+ return ndb_mgm_get_connected_host(m_handle);
+}
+
+const char *ConfigRetriever::get_connectstring(char *buf, int buf_sz) const
+{
+ return ndb_mgm_get_connectstring(m_handle, buf, buf_sz);
+}
+
+//****************************************************************************
+//****************************************************************************
+
+int
+ConfigRetriever::do_connect(int no_retries,
+ int retry_delay_in_seconds, int verbose)
+{
+ return
+ (ndb_mgm_connect(m_handle,no_retries,retry_delay_in_seconds,verbose)==0) ?
+ 0 : -1;
+}
+
+//****************************************************************************
+//****************************************************************************
+//****************************************************************************
+//****************************************************************************
+struct ndb_mgm_configuration*
+ConfigRetriever::getConfig() {
+
+ struct ndb_mgm_configuration * p = 0;
+
+ if(m_handle != 0)
+ p = getConfig(m_handle);
+
+ if(p == 0)
+ return 0;
+
+ if(!verifyConfig(p, _ownNodeId)){
+ free(p);
+ p= 0;
+ }
+
+ return p;
+}
+
+ndb_mgm_configuration *
+ConfigRetriever::getConfig(NdbMgmHandle m_handle){
+
+ ndb_mgm_configuration * conf = ndb_mgm_get_configuration(m_handle,m_version);
+ if(conf == 0){
+ setError(CR_ERROR, ndb_mgm_get_latest_error_desc(m_handle));
+ return 0;
+ }
+
+ return conf;
+}
+
+ndb_mgm_configuration *
+ConfigRetriever::getConfig(const char * filename){
+#ifndef NDB_WIN32
+
+ struct stat sbuf;
+ const int res = stat(filename, &sbuf);
+ if(res != 0){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf), "Could not find file: \"%s\"", filename);
+ setError(CR_ERROR, buf);
+ return 0;
+ }
+ const Uint32 bytes = sbuf.st_size;
+
+ Uint32 * buf2 = new Uint32[bytes/4+1];
+
+ FILE * f = fopen(filename, "rb");
+ if(f == 0){
+ setError(CR_ERROR, "Failed to open file");
+ delete []buf2;
+ return 0;
+ }
+ Uint32 sz = fread(buf2, 1, bytes, f);
+ fclose(f);
+ if(sz != bytes){
+ setError(CR_ERROR, "Failed to read file");
+ delete []buf2;
+ return 0;
+ }
+
+ ConfigValuesFactory cvf;
+ if(!cvf.unpack(buf2, bytes)){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf), "Error while unpacking");
+ setError(CR_ERROR, buf);
+ delete []buf2;
+ return 0;
+ }
+ delete [] buf2;
+ return (ndb_mgm_configuration*)cvf.m_cfg;
+#else
+ return 0;
+#endif
+}
+
+void
+ConfigRetriever::setError(ErrorType et, const char * s){
+ errorString.assign(s ? s : "");
+ latestErrorType = et;
+}
+
+void
+ConfigRetriever::resetError(){
+ setError(CR_NO_ERROR,0);
+}
+
+int
+ConfigRetriever::hasError()
+{
+ return latestErrorType != CR_NO_ERROR;
+}
+
+const char *
+ConfigRetriever::getErrorString(){
+ return errorString.c_str();
+}
+
+bool
+ConfigRetriever::verifyConfig(const struct ndb_mgm_configuration * conf, Uint32 nodeid){
+
+ char buf[255];
+ ndb_mgm_configuration_iterator * it;
+ it = ndb_mgm_create_configuration_iterator((struct ndb_mgm_configuration *)conf,
+ CFG_SECTION_NODE);
+
+ if(it == 0){
+ BaseString::snprintf(buf, 255, "Unable to create config iterator");
+ setError(CR_ERROR, buf);
+ return false;
+
+ }
+ NdbAutoPtr<ndb_mgm_configuration_iterator> ptr(it);
+
+ if(ndb_mgm_find(it, CFG_NODE_ID, nodeid) != 0){
+ BaseString::snprintf(buf, 255, "Unable to find node with id: %d", nodeid);
+ setError(CR_ERROR, buf);
+ return false;
+ }
+
+ const char * hostname;
+ if(ndb_mgm_get_string_parameter(it, CFG_NODE_HOST, &hostname)){
+ BaseString::snprintf(buf, 255, "Unable to get hostname(%d) from config",CFG_NODE_HOST);
+ setError(CR_ERROR, buf);
+ return false;
+ }
+
+ const char * datadir;
+ if(!ndb_mgm_get_string_parameter(it, CFG_NODE_DATADIR, &datadir)){
+ NdbConfig_SetPath(datadir);
+ }
+
+ if (hostname && hostname[0] != 0 &&
+ !SocketServer::tryBind(0,hostname)) {
+ BaseString::snprintf(buf, 255, "Config hostname(%s) don't match a local interface,"
+ " tried to bind, error = %d - %s",
+ hostname, errno, strerror(errno));
+ setError(CR_ERROR, buf);
+ return false;
+ }
+
+ unsigned int _type;
+ if(ndb_mgm_get_int_parameter(it, CFG_TYPE_OF_SECTION, &_type)){
+ BaseString::snprintf(buf, 255, "Unable to get type of node(%d) from config",
+ CFG_TYPE_OF_SECTION);
+ setError(CR_ERROR, buf);
+ return false;
+ }
+
+ if(_type != m_node_type){
+ const char *type_s, *alias_s, *type_s2, *alias_s2;
+ alias_s= ndb_mgm_get_node_type_alias_string((enum ndb_mgm_node_type)m_node_type,
+ &type_s);
+ alias_s2= ndb_mgm_get_node_type_alias_string((enum ndb_mgm_node_type)_type,
+ &type_s2);
+ BaseString::snprintf(buf, 255, "This node type %s(%s) and config "
+ "node type %s(%s) don't match for nodeid %d",
+ alias_s, type_s, alias_s2, type_s2, nodeid);
+ setError(CR_ERROR, buf);
+ return false;
+ }
+
+ /**
+ * Check hostnames
+ */
+ ndb_mgm_configuration_iterator iter(* conf, CFG_SECTION_CONNECTION);
+ for(iter.first(); iter.valid(); iter.next()){
+
+ Uint32 type = CONNECTION_TYPE_TCP + 1;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type)) continue;
+ if(type != CONNECTION_TYPE_TCP) continue;
+
+ Uint32 nodeId1, nodeId2, remoteNodeId;
+ if(iter.get(CFG_CONNECTION_NODE_1, &nodeId1)) continue;
+ if(iter.get(CFG_CONNECTION_NODE_2, &nodeId2)) continue;
+
+ if(nodeId1 != nodeid && nodeId2 != nodeid) continue;
+ remoteNodeId = (nodeid == nodeId1 ? nodeId2 : nodeId1);
+
+ const char * name;
+ struct in_addr addr;
+ BaseString tmp;
+ if(!iter.get(CFG_CONNECTION_HOSTNAME_1, &name) && strlen(name)){
+ if(Ndb_getInAddr(&addr, name) != 0){
+ tmp.assfmt("Unable to lookup/illegal hostname %s, "
+ "connection from node %d to node %d",
+ name, nodeid, remoteNodeId);
+ setError(CR_ERROR, tmp.c_str());
+ return false;
+ }
+ }
+
+ if(!iter.get(CFG_CONNECTION_HOSTNAME_2, &name) && strlen(name)){
+ if(Ndb_getInAddr(&addr, name) != 0){
+ tmp.assfmt("Unable to lookup/illegal hostname %s, "
+ "connection from node %d to node %d",
+ name, nodeid, remoteNodeId);
+ setError(CR_ERROR, tmp.c_str());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+int
+ConfigRetriever::setNodeId(Uint32 nodeid)
+{
+ return ndb_mgm_set_configuration_nodeid(m_handle, nodeid);
+}
+
+Uint32
+ConfigRetriever::allocNodeId(int no_retries, int retry_delay_in_seconds)
+{
+ _ownNodeId= 0;
+ if(m_handle != 0)
+ {
+ while (1)
+ {
+ int res= ndb_mgm_alloc_nodeid(m_handle, m_version, m_node_type);
+ if(res >= 0)
+ return _ownNodeId= (Uint32)res;
+ if (no_retries == 0)
+ break;
+ no_retries--;
+ NdbSleep_SecSleep(retry_delay_in_seconds);
+ }
+ setError(CR_ERROR, ndb_mgm_get_latest_error_desc(m_handle));
+ } else
+ setError(CR_ERROR, "management server handle not initialized");
+ return 0;
+}
diff --git a/storage/ndb/src/common/mgmcommon/IPCConfig.cpp b/storage/ndb/src/common/mgmcommon/IPCConfig.cpp
new file mode 100644
index 00000000000..f188a433f1b
--- /dev/null
+++ b/storage/ndb/src/common/mgmcommon/IPCConfig.cpp
@@ -0,0 +1,388 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_opt_defaults.h>
+#include <IPCConfig.hpp>
+#include <NdbOut.hpp>
+#include <NdbHost.h>
+
+#include <TransporterDefinitions.hpp>
+#include <TransporterRegistry.hpp>
+#include <Properties.hpp>
+
+#include <mgmapi_configuration.hpp>
+#include <mgmapi_config_parameters.h>
+
+#if defined DEBUG_TRANSPORTER
+#define DEBUG(t) ndbout << __FILE__ << ":" << __LINE__ << ":" << t << endl;
+#else
+#define DEBUG(t)
+#endif
+
+IPCConfig::IPCConfig(Properties * p)
+{
+ theNoOfRemoteNodes = 0;
+ the_ownId = 0;
+ if(p != 0)
+ props = new Properties(* p);
+ else
+ props = 0;
+}
+
+
+IPCConfig::~IPCConfig()
+{
+ if(props != 0){
+ delete props;
+ }
+}
+
+int
+IPCConfig::init(){
+ Uint32 nodeId;
+
+ if(props == 0) return -1;
+ if(!props->get("LocalNodeId", &nodeId)) {
+ DEBUG( "Did not find local node id." );
+ return -1;
+ }
+ the_ownId = nodeId;
+
+ Uint32 noOfConnections;
+ if(!props->get("NoOfConnections", &noOfConnections)) {
+ DEBUG( "Did not find noOfConnections." );
+ return -1;
+ }
+
+ for(Uint32 i = 0; i<noOfConnections; i++){
+ const Properties * tmp;
+ Uint32 node1, node2;
+
+ if(!props->get("Connection", i, &tmp)) {
+ DEBUG( "Did not find Connection." );
+ return -1;
+ }
+ if(!tmp->get("NodeId1", &node1)) {
+ DEBUG( "Did not find NodeId1." );
+ return -1;
+ }
+ if(!tmp->get("NodeId2", &node2)) {
+ DEBUG( "Did not find NodeId2." );
+ return -1;
+ }
+
+ if(node1 == the_ownId && node2 != the_ownId)
+ if(!addRemoteNodeId(node2)) {
+ DEBUG( "addRemoteNodeId(node2) failed." );
+ return -1;
+ }
+
+ if(node1 != the_ownId && node2 == the_ownId)
+ if(!addRemoteNodeId(node1)) {
+ DEBUG( "addRemoteNodeId(node2) failed." );
+ return -1;
+ }
+ }
+ return 0;
+}
+
+bool
+IPCConfig::addRemoteNodeId(NodeId nodeId){
+ for(int i = 0; i<theNoOfRemoteNodes; i++)
+ if(theRemoteNodeIds[i] == nodeId)
+ return false;
+ theRemoteNodeIds[theNoOfRemoteNodes] = nodeId;
+ theNoOfRemoteNodes++;
+ return true;
+}
+
+/**
+ * Supply a nodeId,
+ * and get next higher node id
+ * Returns false if none found
+ */
+bool
+IPCConfig::getNextRemoteNodeId(NodeId & nodeId) const {
+ NodeId returnNode = MAX_NODES + 1;
+ for(int i = 0; i<theNoOfRemoteNodes; i++)
+ if(theRemoteNodeIds[i] > nodeId){
+ if(theRemoteNodeIds[i] < returnNode){
+ returnNode = theRemoteNodeIds[i];
+ }
+ }
+ if(returnNode == (MAX_NODES + 1))
+ return false;
+ nodeId = returnNode;
+ return true;
+}
+
+
+Uint32
+IPCConfig::getREPHBFrequency(NodeId id) const {
+ const Properties * tmp;
+ Uint32 out;
+
+ /**
+ * Todo: Fix correct heartbeat
+ */
+ if (!props->get("Node", id, &tmp) ||
+ !tmp->get("HeartbeatIntervalRepRep", &out)) {
+ DEBUG("Illegal Node or HeartbeatIntervalRepRep in config.");
+ out = 10000;
+ }
+
+ return out;
+}
+
+const char*
+IPCConfig::getNodeType(NodeId id) const {
+ const char * out;
+ const Properties * tmp;
+
+ if (!props->get("Node", id, &tmp) || !tmp->get("Type", &out)) {
+ DEBUG("Illegal Node or NodeType in config.");
+ out = "Unknown";
+ }
+
+ return out;
+}
+
+#include <mgmapi.h>
+Uint32
+IPCConfig::configureTransporters(Uint32 nodeId,
+ const class ndb_mgm_configuration & config,
+ class TransporterRegistry & tr){
+ TransporterConfiguration conf;
+
+ DBUG_ENTER("IPCConfig::configureTransporters");
+
+ /**
+ * Iterate over all MGM's an construct a connectstring
+ * create mgm_handle and give it to the Transporter Registry
+ */
+ {
+ const char *separator= "";
+ BaseString connect_string;
+ ndb_mgm_configuration_iterator iter(config, CFG_SECTION_NODE);
+ for(iter.first(); iter.valid(); iter.next())
+ {
+ Uint32 type;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type)) continue;
+ if(type != NODE_TYPE_MGM) continue;
+ const char* hostname;
+ Uint32 port;
+ if(iter.get(CFG_NODE_HOST, &hostname)) continue;
+ if( strlen(hostname) == 0 ) continue;
+ if(iter.get(CFG_MGM_PORT, &port)) continue;
+ connect_string.appfmt("%s%s:port",separator,hostname,port);
+ separator= ",";
+ }
+ NdbMgmHandle h= ndb_mgm_create_handle();
+ if ( h && connect_string.length() > 0 )
+ {
+ ndb_mgm_set_connectstring(h,connect_string.c_str());
+ tr.set_mgm_handle(h);
+ }
+ }
+
+ Uint32 noOfTransportersCreated= 0;
+ ndb_mgm_configuration_iterator iter(config, CFG_SECTION_CONNECTION);
+
+ for(iter.first(); iter.valid(); iter.next()){
+
+ Uint32 nodeId1, nodeId2, remoteNodeId;
+ const char * remoteHostName= 0, * localHostName= 0;
+ if(iter.get(CFG_CONNECTION_NODE_1, &nodeId1)) continue;
+ if(iter.get(CFG_CONNECTION_NODE_2, &nodeId2)) continue;
+
+ if(nodeId1 != nodeId && nodeId2 != nodeId) continue;
+ remoteNodeId = (nodeId == nodeId1 ? nodeId2 : nodeId1);
+
+ {
+ const char * host1= 0, * host2= 0;
+ iter.get(CFG_CONNECTION_HOSTNAME_1, &host1);
+ iter.get(CFG_CONNECTION_HOSTNAME_2, &host2);
+ localHostName = (nodeId == nodeId1 ? host1 : host2);
+ remoteHostName = (nodeId == nodeId1 ? host2 : host1);
+ }
+
+ Uint32 sendSignalId = 1;
+ Uint32 checksum = 1;
+ if(iter.get(CFG_CONNECTION_SEND_SIGNAL_ID, &sendSignalId)) continue;
+ if(iter.get(CFG_CONNECTION_CHECKSUM, &checksum)) continue;
+
+ Uint32 type = ~0;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type)) continue;
+
+ Uint32 server_port= 0;
+ if(iter.get(CFG_CONNECTION_SERVER_PORT, &server_port)) break;
+
+ /*
+ We check the node type. MGM node becomes server.
+ */
+ Uint32 node1type, node2type;
+ ndb_mgm_configuration_iterator node1iter(config, CFG_SECTION_NODE);
+ ndb_mgm_configuration_iterator node2iter(config, CFG_SECTION_NODE);
+ node1iter.find(CFG_NODE_ID,nodeId1);
+ node2iter.find(CFG_NODE_ID,nodeId2);
+ node1iter.get(CFG_TYPE_OF_SECTION,&node1type);
+ node2iter.get(CFG_TYPE_OF_SECTION,&node2type);
+
+ conf.serverNodeId= (nodeId1 < nodeId2)? nodeId1:nodeId2;
+
+ conf.isMgmConnection= false;
+ if(node2type==NODE_TYPE_MGM)
+ {
+ conf.isMgmConnection= true;
+ conf.serverNodeId= nodeId2;
+ }
+ else if(node1type==NODE_TYPE_MGM)
+ {
+ conf.isMgmConnection= true;
+ conf.serverNodeId= nodeId1;
+ }
+ else if (nodeId == conf.serverNodeId) {
+ tr.add_transporter_interface(remoteNodeId, localHostName, server_port);
+ }
+
+ DBUG_PRINT("info", ("Transporter between this node %d and node %d using port %d, signalId %d, checksum %d",
+ nodeId, remoteNodeId, server_port, sendSignalId, checksum));
+ /*
+ This may be a dynamic port. It depends on when we're getting
+ our configuration. If we've been restarted, we'll be getting
+ a configuration with our old dynamic port in it, hence the number
+ here is negative (and we try the old port number first).
+
+ On a first-run, server_port will be zero (with dynamic ports)
+
+ If we're not using dynamic ports, we don't do anything.
+ */
+ if((int)server_port<0)
+ server_port= -server_port;
+
+ conf.localNodeId = nodeId;
+ conf.remoteNodeId = remoteNodeId;
+ conf.checksum = checksum;
+ conf.signalId = sendSignalId;
+ conf.port = server_port;
+ conf.localHostName = localHostName;
+ conf.remoteHostName = remoteHostName;
+
+ switch(type){
+ case CONNECTION_TYPE_SHM:
+ if(iter.get(CFG_SHM_KEY, &conf.shm.shmKey)) break;
+ if(iter.get(CFG_SHM_BUFFER_MEM, &conf.shm.shmSize)) break;
+
+ Uint32 tmp;
+ if(iter.get(CFG_SHM_SIGNUM, &tmp)) break;
+ conf.shm.signum= tmp;
+
+ if(!tr.createSHMTransporter(&conf)){
+ DBUG_PRINT("error", ("Failed to create SHM Transporter from %d to %d",
+ conf.localNodeId, conf.remoteNodeId));
+ ndbout << "Failed to create SHM Transporter from: "
+ << conf.localNodeId << " to: " << conf.remoteNodeId << endl;
+ } else {
+ noOfTransportersCreated++;
+ }
+ DBUG_PRINT("info", ("Created SHM Transporter using shmkey %d, "
+ "buf size = %d", conf.shm.shmKey, conf.shm.shmSize));
+
+ break;
+
+ case CONNECTION_TYPE_SCI:
+ if(iter.get(CFG_SCI_SEND_LIMIT, &conf.sci.sendLimit)) break;
+ if(iter.get(CFG_SCI_BUFFER_MEM, &conf.sci.bufferSize)) break;
+ if (nodeId == nodeId1) {
+ if(iter.get(CFG_SCI_HOST2_ID_0, &conf.sci.remoteSciNodeId0)) break;
+ if(iter.get(CFG_SCI_HOST2_ID_1, &conf.sci.remoteSciNodeId1)) break;
+ } else {
+ if(iter.get(CFG_SCI_HOST1_ID_0, &conf.sci.remoteSciNodeId0)) break;
+ if(iter.get(CFG_SCI_HOST1_ID_1, &conf.sci.remoteSciNodeId1)) break;
+ }
+ if (conf.sci.remoteSciNodeId1 == 0) {
+ conf.sci.nLocalAdapters = 1;
+ } else {
+ conf.sci.nLocalAdapters = 2;
+ }
+ if(!tr.createSCITransporter(&conf)){
+ DBUG_PRINT("error", ("Failed to create SCI Transporter from %d to %d",
+ conf.localNodeId, conf.remoteNodeId));
+ ndbout << "Failed to create SCI Transporter from: "
+ << conf.localNodeId << " to: " << conf.remoteNodeId << endl;
+ } else {
+ DBUG_PRINT("info", ("Created SCI Transporter: Adapters = %d, "
+ "remote SCI node id %d",
+ conf.sci.nLocalAdapters, conf.sci.remoteSciNodeId0));
+ DBUG_PRINT("info", ("Host 1 = %s, Host 2 = %s, sendLimit = %d, "
+ "buf size = %d", conf.localHostName,
+ conf.remoteHostName, conf.sci.sendLimit,
+ conf.sci.bufferSize));
+ if (conf.sci.nLocalAdapters > 1) {
+ DBUG_PRINT("info", ("Fault-tolerant with 2 Remote Adapters, "
+ "second remote SCI node id = %d",
+ conf.sci.remoteSciNodeId1));
+ }
+ noOfTransportersCreated++;
+ continue;
+ }
+ break;
+
+ case CONNECTION_TYPE_TCP:
+ if(iter.get(CFG_TCP_SEND_BUFFER_SIZE, &conf.tcp.sendBufferSize)) break;
+ if(iter.get(CFG_TCP_RECEIVE_BUFFER_SIZE, &conf.tcp.maxReceiveSize)) break;
+
+ const char * proxy;
+ if (!iter.get(CFG_TCP_PROXY, &proxy)) {
+ if (strlen(proxy) > 0 && nodeId2 == nodeId) {
+ // TODO handle host:port
+ conf.port = atoi(proxy);
+ }
+ }
+
+ if(!tr.createTCPTransporter(&conf)){
+ ndbout << "Failed to create TCP Transporter from: "
+ << nodeId << " to: " << remoteNodeId << endl;
+ } else {
+ noOfTransportersCreated++;
+ }
+ DBUG_PRINT("info", ("Created TCP Transporter: sendBufferSize = %d, "
+ "maxReceiveSize = %d", conf.tcp.sendBufferSize,
+ conf.tcp.maxReceiveSize));
+ break;
+ case CONNECTION_TYPE_OSE:
+ if(iter.get(CFG_OSE_PRIO_A_SIZE, &conf.ose.prioASignalSize)) break;
+ if(iter.get(CFG_OSE_PRIO_B_SIZE, &conf.ose.prioBSignalSize)) break;
+
+ if(!tr.createOSETransporter(&conf)){
+ ndbout << "Failed to create OSE Transporter from: "
+ << nodeId << " to: " << remoteNodeId << endl;
+ } else {
+ noOfTransportersCreated++;
+ }
+ break;
+
+ default:
+ ndbout << "Unknown transporter type from: " << nodeId <<
+ " to: " << remoteNodeId << endl;
+ break;
+ } // switch
+ } // for
+
+ DBUG_RETURN(noOfTransportersCreated);
+}
+
diff --git a/storage/ndb/src/common/mgmcommon/Makefile.am b/storage/ndb/src/common/mgmcommon/Makefile.am
new file mode 100644
index 00000000000..104bf0b29f2
--- /dev/null
+++ b/storage/ndb/src/common/mgmcommon/Makefile.am
@@ -0,0 +1,28 @@
+noinst_LTLIBRARIES = libmgmsrvcommon.la
+
+libmgmsrvcommon_la_SOURCES = \
+ ConfigRetriever.cpp \
+ IPCConfig.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/mgmapi -I$(top_srcdir)/ndb/src/mgmsrv
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
+include $(top_srcdir)/ndb/config/type_mgmapiclient.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libmgmsrvcommon.dsp
+
+libmgmsrvcommon.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libmgmsrvcommon_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/mgmcommon/printConfig/Makefile b/storage/ndb/src/common/mgmcommon/printConfig/Makefile
new file mode 100644
index 00000000000..77e8943e2c6
--- /dev/null
+++ b/storage/ndb/src/common/mgmcommon/printConfig/Makefile
@@ -0,0 +1,16 @@
+include .defs.mk
+
+TYPE := ndbapi mgmapiclient
+
+BIN_TARGET := printConfig
+BIN_TARGET_ARCHIVES := general portlib
+
+CCFLAGS_LOC += -I..
+
+SOURCES := printConfig.cpp ../ConfigRetriever.cpp
+
+SOURCES.c := ../NdbConfig.c ../LocalConfig.c
+
+CFLAGS_printConfig.cpp := -I$(call fixpath,$(NDB_TOP)/src/mgmapi)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/common/mgmcommon/printConfig/printConfig.cpp b/storage/ndb/src/common/mgmcommon/printConfig/printConfig.cpp
new file mode 100644
index 00000000000..7cedbb451e2
--- /dev/null
+++ b/storage/ndb/src/common/mgmcommon/printConfig/printConfig.cpp
@@ -0,0 +1,89 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbMain.h>
+#include <mgmapi.h>
+#include <ConfigRetriever.hpp>
+#include <Properties.hpp>
+#include <NdbOut.hpp>
+
+void usage(const char * prg){
+ ndbout << "Usage " << prg
+ << " host <mgm host> <mgm port> <node id> [<ver id>]" << endl;
+
+ char buf[255];
+ for(unsigned i = 0; i<strlen(prg); i++)
+ buf[i] = ' ';
+ buf[strlen(prg)] = 0;
+ ndbout << " " << buf << " file <filename> <node id> [<ver id>]"
+ << endl;
+}
+
+NDB_COMMAND(printConfig,
+ "printConfig", "printConfig", "Prints configuration", 16384){
+ if(argc < 4){
+ usage(argv[0]);
+ return 0;
+ }
+ if(strcmp("file", argv[1]) != 0 && strcmp("host", argv[1]) != 0){
+ usage(argv[0]);
+ return 0;
+ }
+
+ if(strcmp("host", argv[1]) == 0 && argc < 5){
+ usage(argv[0]);
+ return 0;
+ }
+
+ ConfigRetriever c;
+ struct ndb_mgm_configuration * p = 0;
+
+ if(strcmp("host", argv[1]) == 0){
+ int verId = 0;
+ if(argc > 5)
+ verId = atoi(argv[5]);
+
+ ndbout << "Getting config from: " << argv[2] << ":" << atoi(argv[3])
+ << " NodeId =" << atoi(argv[4])
+ << " VersionId = " << verId << endl;
+
+ p = c.getConfig(argv[2],
+ atoi(argv[3]),
+ verId);
+ } else if (strcmp("file", argv[1]) == 0){
+ int verId = 0;
+ if(argc > 4)
+ verId = atoi(argv[4]);
+
+ ndbout << "Getting config from: " << argv[2]
+ << " NodeId =" << atoi(argv[3])
+ << " VersionId = " << verId << endl;
+
+ p = c.getConfig(argv[2], atoi(argv[3]), verId);
+ }
+
+ if(p != 0){
+ //
+ free(p);
+ } else {
+ ndbout << "Configuration not found: " << c.getErrorString() << endl;
+ }
+
+ return 0;
+}
diff --git a/storage/ndb/src/common/portlib/Makefile.am b/storage/ndb/src/common/portlib/Makefile.am
new file mode 100644
index 00000000000..99138a7414e
--- /dev/null
+++ b/storage/ndb/src/common/portlib/Makefile.am
@@ -0,0 +1,43 @@
+noinst_HEADERS = gcc.cpp
+
+noinst_LTLIBRARIES = libportlib.la
+
+libportlib_la_SOURCES = \
+ NdbCondition.c NdbMutex.c NdbSleep.c NdbTick.c \
+ NdbEnv.c NdbThread.c NdbHost.c NdbTCP.cpp \
+ NdbDaemon.c NdbMem.c \
+ NdbConfig.c
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_util.mk.am
+
+EXTRA_PROGRAMS = memtest PortLibTest munmaptest
+
+PortLibTest_SOURCES = NdbPortLibTest.cpp
+munmaptest_SOURCES = munmaptest.cpp
+
+# Don't update the files from bitkeeper
+WIN_src = win32/NdbCondition.c \
+ win32/NdbDaemon.c \
+ win32/NdbEnv.c \
+ win32/NdbHost.c \
+ win32/NdbMem.c \
+ win32/NdbMutex.c \
+ win32/NdbSleep.c \
+ win32/NdbTCP.c \
+ win32/NdbThread.c \
+ win32/NdbTick.c
+
+windoze-dsp: libportlib.dsp
+
+libportlib.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(WIN_src)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/portlib/NdbCondition.c b/storage/ndb/src/common/portlib/NdbCondition.c
new file mode 100644
index 00000000000..df312c7cc24
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbCondition.c
@@ -0,0 +1,142 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbCondition.h>
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbMem.h>
+
+struct NdbCondition
+{
+ pthread_cond_t cond;
+};
+
+
+
+struct NdbCondition*
+NdbCondition_Create(void)
+{
+ struct NdbCondition* tmpCond;
+ int result;
+
+ tmpCond = (struct NdbCondition*)NdbMem_Allocate(sizeof(struct NdbCondition));
+
+ if (tmpCond == NULL)
+ return NULL;
+
+ result = pthread_cond_init(&tmpCond->cond, NULL);
+
+ assert(result==0);
+ return tmpCond;
+}
+
+
+
+int
+NdbCondition_Wait(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex)
+{
+ int result;
+
+ if (p_cond == NULL || p_mutex == NULL)
+ return 1;
+
+ result = pthread_cond_wait(&p_cond->cond, p_mutex);
+
+ return result;
+}
+
+int
+NdbCondition_WaitTimeout(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex,
+ int msecs){
+ int result;
+ struct timespec abstime;
+ int secs = 0;
+
+ if (p_cond == NULL || p_mutex == NULL)
+ return 1;
+
+#ifdef HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_REALTIME, &abstime);
+#else
+ {
+ struct timeval tick_time;
+ gettimeofday(&tick_time, 0);
+ abstime.tv_sec = tick_time.tv_sec;
+ abstime.tv_nsec = tick_time.tv_usec * 1000;
+ }
+#endif
+
+ if(msecs >= 1000){
+ secs = msecs / 1000;
+ msecs = msecs % 1000;
+ }
+
+ abstime.tv_sec += secs;
+ abstime.tv_nsec += msecs * 1000000;
+ if (abstime.tv_nsec >= 1000000000) {
+ abstime.tv_sec += 1;
+ abstime.tv_nsec -= 1000000000;
+ }
+
+ result = pthread_cond_timedwait(&p_cond->cond, p_mutex, &abstime);
+
+ return result;
+}
+
+int
+NdbCondition_Signal(struct NdbCondition* p_cond){
+ int result;
+
+ if (p_cond == NULL)
+ return 1;
+
+ result = pthread_cond_signal(&p_cond->cond);
+
+ return result;
+}
+
+
+int NdbCondition_Broadcast(struct NdbCondition* p_cond)
+{
+ int result;
+
+ if (p_cond == NULL)
+ return 1;
+
+ result = pthread_cond_broadcast(&p_cond->cond);
+
+ return result;
+}
+
+
+int NdbCondition_Destroy(struct NdbCondition* p_cond)
+{
+ int result;
+
+ if (p_cond == NULL)
+ return 1;
+
+ result = pthread_cond_destroy(&p_cond->cond);
+ free(p_cond);
+
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/NdbConfig.c b/storage/ndb/src/common/portlib/NdbConfig.c
new file mode 100644
index 00000000000..b275143646f
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbConfig.c
@@ -0,0 +1,145 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbConfig.h>
+#include <NdbEnv.h>
+#include <NdbMem.h>
+#include <basestring_vsnprintf.h>
+
+static const char *datadir_path= 0;
+
+const char *
+NdbConfig_get_path(int *_len)
+{
+ const char *path= NdbEnv_GetEnv("NDB_HOME", 0, 0);
+ int path_len= 0;
+ if (path)
+ path_len= strlen(path);
+ if (path_len == 0 && datadir_path) {
+ path= datadir_path;
+ path_len= strlen(path);
+ }
+ if (path_len == 0) {
+ path= ".";
+ path_len= strlen(path);
+ }
+ if (_len)
+ *_len= path_len;
+ return path;
+}
+
+static char*
+NdbConfig_AllocHomePath(int _len)
+{
+ int path_len;
+ const char *path= NdbConfig_get_path(&path_len);
+ int len= _len+path_len;
+ char *buf= NdbMem_Allocate(len);
+ basestring_snprintf(buf, len, "%s%s", path, DIR_SEPARATOR);
+ return buf;
+}
+
+void
+NdbConfig_SetPath(const char* path){
+ datadir_path= path;
+}
+
+char*
+NdbConfig_NdbCfgName(int with_ndb_home){
+ char *buf;
+ int len= 0;
+
+ if (with_ndb_home) {
+ buf= NdbConfig_AllocHomePath(128);
+ len= strlen(buf);
+ } else
+ buf= NdbMem_Allocate(128);
+ basestring_snprintf(buf+len, 128, "Ndb.cfg");
+ return buf;
+}
+
+static
+char *get_prefix_buf(int len, int node_id)
+{
+ char tmp_buf[sizeof("ndb_pid#############")+1];
+ char *buf;
+ if (node_id > 0)
+ basestring_snprintf(tmp_buf, sizeof(tmp_buf), "ndb_%u", node_id);
+ else
+ basestring_snprintf(tmp_buf, sizeof(tmp_buf), "ndb_pid%u", getpid());
+ tmp_buf[sizeof(tmp_buf)-1]= 0;
+
+ buf= NdbConfig_AllocHomePath(len+strlen(tmp_buf));
+ strcat(buf, tmp_buf);
+ return buf;
+}
+
+char*
+NdbConfig_ErrorFileName(int node_id){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, "_error.log");
+ return buf;
+}
+
+char*
+NdbConfig_ClusterLogFileName(int node_id){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, "_cluster.log");
+ return buf;
+}
+
+char*
+NdbConfig_SignalLogFileName(int node_id){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, "_signal.log");
+ return buf;
+}
+
+char*
+NdbConfig_TraceFileName(int node_id, int file_no){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, "_trace.log.%u", file_no);
+ return buf;
+}
+
+char*
+NdbConfig_NextTraceFileName(int node_id){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, "_trace.log.next");
+ return buf;
+}
+
+char*
+NdbConfig_PidFileName(int node_id){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, ".pid");
+ return buf;
+}
+
+char*
+NdbConfig_StdoutFileName(int node_id){
+ char *buf= get_prefix_buf(128, node_id);
+ int len= strlen(buf);
+ basestring_snprintf(buf+len, 128, "_out.log");
+ return buf;
+}
diff --git a/storage/ndb/src/common/portlib/NdbDaemon.c b/storage/ndb/src/common/portlib/NdbDaemon.c
new file mode 100644
index 00000000000..3f1c1998501
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbDaemon.c
@@ -0,0 +1,171 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbDaemon.h"
+
+#define NdbDaemon_ErrorSize 500
+long NdbDaemon_DaemonPid = 0;
+int NdbDaemon_ErrorCode = 0;
+char NdbDaemon_ErrorText[NdbDaemon_ErrorSize] = "";
+
+int
+NdbDaemon_Make(const char* lockfile, const char* logfile, unsigned flags)
+{
+ int lockfd = -1, logfd = -1, n;
+ char buf[64];
+
+ (void)flags; /* remove warning for unused parameter */
+
+ /* Check that we have write access to lock file */
+ assert(lockfile != NULL);
+ lockfd = open(lockfile, O_CREAT|O_RDWR, 0644);
+ if (lockfd == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: open for write failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+ /* Read any old pid from lock file */
+ buf[0] = 0;
+ n = read(lockfd, buf, sizeof(buf));
+ if (n < 0) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: read failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+ NdbDaemon_DaemonPid = atol(buf);
+ if (lseek(lockfd, 0, SEEK_SET) == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: lseek failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+#ifdef F_TLOCK
+ /* Test for lock before becoming daemon */
+ if (lockf(lockfd, F_TLOCK, 0) == -1)
+ {
+ if (errno == EACCES || errno == EAGAIN) { /* results may vary */
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: already locked by pid=%ld", lockfile, NdbDaemon_DaemonPid);
+ return -1;
+ }
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: lock test failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+#endif
+ /* Test open log file before becoming daemon */
+ if (logfile != NULL) {
+ logfd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0644);
+ if (logfd == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: open for write failed: %s", logfile, strerror(errno));
+ return -1;
+ }
+ }
+#ifdef F_TLOCK
+ if (lockf(lockfd, F_ULOCK, 0) == -1)
+ {
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: fail to unlock", lockfile);
+ return -1;
+ }
+#endif
+
+ /* Fork */
+ n = fork();
+ if (n == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "fork failed: %s", strerror(errno));
+ return -1;
+ }
+ /* Exit if we are the parent */
+ if (n != 0) {
+ exit(0);
+ }
+ /* Running in child process */
+ NdbDaemon_DaemonPid = getpid();
+ /* Lock the lock file (likely to succeed due to test above) */
+ if (lockf(lockfd, F_LOCK, 0) == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: lock failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+ /* Become process group leader */
+ if (setsid() == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "setsid failed: %s", strerror(errno));
+ return -1;
+ }
+ /* Write pid to lock file */
+ if (ftruncate(lockfd, 0) == -1) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: ftruncate failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+ sprintf(buf, "%ld\n", NdbDaemon_DaemonPid);
+ n = strlen(buf);
+ if (write(lockfd, buf, n) != n) {
+ NdbDaemon_ErrorCode = errno;
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "%s: write failed: %s", lockfile, strerror(errno));
+ return -1;
+ }
+ /* Do input/output redirections (assume fd 0,1,2 not in use) */
+ close(0);
+ open("/dev/null", O_RDONLY);
+ if (logfile != 0) {
+ dup2(logfd, 1);
+ dup2(logfd, 2);
+ close(logfd);
+ }
+ /* Success */
+ return 0;
+}
+
+#if 0
+int
+NdbDaemon_Make(const char* lockfile, const char* logfile, unsigned flags)
+{
+ /* Fail */
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "Daemon mode not implemented");
+ return -1;
+}
+#endif
+
+#ifdef NDB_DAEMON_TEST
+
+int
+main()
+{
+ if (NdbDaemon_Make("test.pid", "test.log", 0) == -1) {
+ fprintf(stderr, "NdbDaemon_Make: %s\n", NdbDaemon_ErrorText);
+ return 1;
+ }
+ sleep(10);
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/common/portlib/NdbEnv.c b/storage/ndb/src/common/portlib/NdbEnv.c
new file mode 100644
index 00000000000..d294e0b52ca
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbEnv.c
@@ -0,0 +1,34 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbEnv.h>
+
+const char* NdbEnv_GetEnv(const char* name, char * buf, int buflen)
+{
+ char* p = NULL;
+ p = getenv(name);
+
+ if (p != NULL && buf != NULL){
+ strncpy(buf, p, buflen);
+ buf[buflen-1] = 0;
+ }
+ return p;
+
+}
+
diff --git a/storage/ndb/src/common/portlib/NdbHost.c b/storage/ndb/src/common/portlib/NdbHost.c
new file mode 100644
index 00000000000..4749bb39ea7
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbHost.c
@@ -0,0 +1,34 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include "NdbHost.h"
+
+int NdbHost_GetHostName(char* buf)
+{
+ if (gethostname(buf, MAXHOSTNAMELEN) != 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int NdbHost_GetProcessId(void)
+{
+ return getpid();
+}
+
diff --git a/storage/ndb/src/common/portlib/NdbMem.c b/storage/ndb/src/common/portlib/NdbMem.c
new file mode 100644
index 00000000000..f964f4d9937
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbMem.c
@@ -0,0 +1,75 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbMem.h>
+
+void NdbMem_Create()
+{
+ /* Do nothing */
+ return;
+}
+
+void NdbMem_Destroy()
+{
+ /* Do nothing */
+ return;
+}
+
+
+void* NdbMem_Allocate(size_t size)
+{
+ void* mem_allocated;
+ assert(size > 0);
+ mem_allocated= (void*)malloc(size);
+ return mem_allocated;
+}
+
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ (void)alignment; /* remove warning for unused parameter */
+ /*
+ return (void*)memalign(alignment, size);
+ TEMP fix
+ */
+ return (void*)malloc(size);
+}
+
+
+void NdbMem_Free(void* ptr)
+{
+ free(ptr);
+}
+
+
+int NdbMem_MemLockAll(){
+#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT)
+ return mlockall(MCL_CURRENT);
+#else
+ return -1;
+#endif
+}
+
+int NdbMem_MemUnlockAll(){
+#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT)
+ return munlockall();
+#else
+ return -1;
+#endif
+}
+
diff --git a/storage/ndb/src/common/portlib/NdbMutex.c b/storage/ndb/src/common/portlib/NdbMutex.c
new file mode 100644
index 00000000000..d3d39ea8cf7
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbMutex.c
@@ -0,0 +1,93 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbMem.h>
+
+NdbMutex* NdbMutex_Create(void)
+{
+ NdbMutex* pNdbMutex;
+ int result;
+
+ pNdbMutex = (NdbMutex*)NdbMem_Allocate(sizeof(NdbMutex));
+
+ if (pNdbMutex == NULL)
+ return NULL;
+
+ result = pthread_mutex_init(pNdbMutex, NULL);
+ assert(result == 0);
+
+ return pNdbMutex;
+
+}
+
+
+int NdbMutex_Destroy(NdbMutex* p_mutex)
+{
+ int result;
+
+ if (p_mutex == NULL)
+ return -1;
+
+ result = pthread_mutex_destroy(p_mutex);
+ free(p_mutex);
+
+ return result;
+
+}
+
+
+int NdbMutex_Lock(NdbMutex* p_mutex)
+{
+ int result;
+
+ if (p_mutex == NULL)
+ return -1;
+
+ result = pthread_mutex_lock(p_mutex);
+
+ return result;
+}
+
+
+int NdbMutex_Unlock(NdbMutex* p_mutex)
+{
+ int result;
+
+ if (p_mutex == NULL)
+ return -1;
+
+ result = pthread_mutex_unlock(p_mutex);
+
+ return result;
+}
+
+
+int NdbMutex_Trylock(NdbMutex* p_mutex)
+{
+ int result = -1;
+
+ if (p_mutex != NULL) {
+ result = pthread_mutex_trylock(p_mutex);
+ }
+
+ return result;
+}
+
diff --git a/storage/ndb/src/common/portlib/NdbPortLibTest.cpp b/storage/ndb/src/common/portlib/NdbPortLibTest.cpp
new file mode 100644
index 00000000000..d7892411851
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbPortLibTest.cpp
@@ -0,0 +1,608 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/**
+ * NdbPortLibTest.cpp
+ * Test the functionality of portlib
+ * TODO - Add tests for NdbMem
+ */
+
+#include <ndb_global.h>
+
+#include "NdbOut.hpp"
+#include "NdbThread.h"
+#include "NdbMutex.h"
+#include "NdbCondition.h"
+#include "NdbSleep.h"
+#include "NdbTick.h"
+#include "NdbEnv.h"
+#include "NdbHost.h"
+#include "NdbMain.h"
+
+int TestHasFailed;
+int verbose = 0;
+
+static void fail(const char* test, const char* cause)
+{
+ TestHasFailed = 1;
+ ndbout << test << " failed, " << cause << endl;
+}
+
+// test 1 variables and funcs
+
+extern "C" void* thread1func(void* arg)
+{
+ int arg1;
+ int returnvalue = 8;
+ arg1 = *(int*)arg;
+ ndbout << "thread1: thread1func called with arg = " << arg1 << endl;
+
+ // delay(1000);
+ if (arg1 != 7)
+ fail("TEST1", "Wrong arg");
+
+ return returnvalue;
+}
+
+// test 2 variables and funcs
+
+NdbMutex* test2mutex;
+
+extern "C" void* test2func(void* arg)
+{
+
+ int arg1;
+ arg1 = *(int*)arg;
+ ndbout << "thread" << arg1 << " started in test2func" << endl;
+
+ if (NdbMutex_Lock(test2mutex) != 0)
+ fail("TEST2", "Failed to lock mutex");
+
+ ndbout << "thread" << arg1 << ", test2func " << endl;
+
+ if (NdbMutex_Unlock(test2mutex) != 0)
+ fail("TEST2", "Failed to unlock mutex");
+
+ int returnvalue = arg1;
+ return returnvalue;
+}
+
+
+// test 3 and 7 variables and funcs
+
+NdbMutex* testmutex;
+NdbCondition* testcond;
+int testthreadsdone;
+
+extern "C" void* testfunc(void* arg)
+{
+ int tmpVar;
+ int threadno;
+ int result;
+
+ threadno = *(int*)arg;
+
+ ndbout << "Thread" << threadno << " started in testfunc" << endl;
+ do
+ {
+
+ if ((threadno % 2) == 0)
+ result = NdbSleep_SecSleep(1);
+ else
+ result = NdbSleep_MilliSleep(100);
+
+ if (result != 0)
+ fail("TEST3", "Wrong result from sleep function");
+
+ if (NdbMutex_Lock(testmutex) != 0)
+ fail("TEST3", "Wrong result from NdbMutex_Lock function");
+
+ ndbout << "thread" << threadno << ", testfunc " << endl;
+ testthreadsdone++;
+ tmpVar = testthreadsdone;
+
+ if (NdbCondition_Signal(testcond) != 0)
+ fail("TEST3", "Wrong result from NdbCondition_Signal function");
+
+ if (NdbMutex_Unlock(testmutex) != 0)
+ fail("TEST3", "Wrong result from NdbMutex_Unlock function");
+
+ }
+ while(tmpVar<100);
+
+ return 0;
+}
+
+extern "C" void* testTryLockfunc(void* arg)
+{
+ int tmpVar = 0;
+ int threadno;
+ int result;
+
+ threadno = *(int*)arg;
+
+ ndbout << "Thread" << threadno << " started" << endl;
+ do
+ {
+
+ if ((threadno % 2) == 0)
+ result = NdbSleep_SecSleep(1);
+ else
+ result = NdbSleep_MilliSleep(100);
+
+ if (result != 0)
+ fail("TEST3", "Wrong result from sleep function");
+
+ if (NdbMutex_Trylock(testmutex) == 0){
+
+ ndbout << "thread" << threadno << ", testTryLockfunc locked" << endl;
+ testthreadsdone++;
+ tmpVar = testthreadsdone;
+
+ if (NdbCondition_Signal(testcond) != 0)
+ fail("TEST3", "Wrong result from NdbCondition_Signal function");
+
+ if (NdbMutex_Unlock(testmutex) != 0)
+ fail("TEST3", "Wrong result from NdbMutex_Unlock function");
+ }
+
+ }
+ while(tmpVar<100);
+
+ return 0;
+}
+
+
+
+void testMicros(int count);
+Uint64 time_diff(Uint64 s1, Uint64 s2, Uint32 m1, Uint32 m2);
+
+NDB_COMMAND(PortLibTest, "portlibtest", "portlibtest", "Test the portable function layer", 4096){
+
+ ndbout << "= TESTING ARGUMENT PASSING ============" << endl;
+ ndbout << "ARGC: " << argc << endl;
+ for(int i = 1; i < argc; i++){
+ ndbout << " ARGV"<<i<<": " << (char*)argv[i] << endl;
+ }
+ ndbout << endl << endl;
+
+
+ struct NdbThread* thread1var;
+ void *status = 0;
+ int arg = 7;
+
+ TestHasFailed = 0;
+ // create one thread and wait for it to return
+ ndbout << "= TEST1 ===============================" << endl;
+
+ thread1var = NdbThread_Create(thread1func, // Function
+ (void**)&arg,// Arg
+ 2048, // Stacksize
+ (char*)"thread1", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread priority
+
+
+ if(NdbThread_WaitFor(thread1var, &status) != 0)
+ fail("TEST1", "NdbThread_WaitFor failed");
+ // NOTE! thread return value is not yet used in Ndb and thus not tested(does not work)
+ //ndbout << "thread1 returned, status = " << status << endl;
+ //if (status != 8)
+ // fail("TEST1", "Wrong status");
+ ndbout << "TEST1 completed" << endl;
+
+
+ NdbThread_Destroy(&thread1var);
+
+ // Create 10 threads that will wait for a mutex before printing it's message to screen
+ ndbout << "= TEST2 ===============================" << endl;
+#define T2_THREADS 10
+ NdbThread* threads[T2_THREADS];
+ int args[T2_THREADS];
+ void *status2 = 0;
+ test2mutex = NdbMutex_Create();
+ NdbMutex_Lock(test2mutex);
+
+ for (int i = 0; i < T2_THREADS; i++)
+ {
+ args[i] = i;
+ threads[i] = NdbThread_Create(test2func, // Function
+ (void**)&args[i],// Arg
+ 2048, // Stacksize
+ (char*)"test2thread", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread priority
+ if (threads[i] == NULL)
+ fail("TEST2", "NdbThread_Create failed");
+ }
+
+ ndbout << "All threads created" << endl;
+
+ NdbMutex_Unlock(test2mutex);
+
+ for (int i = 0; i < T2_THREADS; i++)
+ {
+ if (NdbThread_WaitFor(threads[i], &status2))
+ fail("TEST2", "NdbThread_WaitFor failed");
+
+ NdbThread_Destroy(&threads[i]);
+ // Don't test return values
+ // ndbout << "thread" << i << " returned, status = " << status2 << endl;
+ // if (status2 != i)
+ // fail("TEST2", "Wrong status");
+ }
+
+ if (NdbMutex_Lock(test2mutex) != 0)
+ fail("TEST2", "NdbMutex_Lock failed");
+ if (NdbMutex_Unlock(test2mutex) != 0)
+ fail("TEST2", "NdbMutex_Unlock failed");
+ if (NdbMutex_Destroy(test2mutex) != 0)
+ fail("TEST2", "NdbMutex_Destroy failed");
+ ndbout << "TEST2 completed" << endl;
+
+ ndbout << "= TEST3 ===============================" << endl;
+ // Create 10 threads that will by synchronised by a condition
+ // When they are awakened and have the mutex they will increment a global variable
+#define T3_THREADS 10
+ NdbThread* t3threads[T3_THREADS];
+ int t3args[T3_THREADS];
+ void *status3 = 0;
+
+ testmutex = NdbMutex_Create();
+ testcond = NdbCondition_Create();
+ testthreadsdone = 0;
+
+ for (int i = 0; i < T3_THREADS; i++)
+ {
+ t3args[i] = i;
+ t3threads[i] = NdbThread_Create(testfunc, // Function
+ (void**)&t3args[i],// Arg
+ 2048, // Stacksize
+ (char*)"test3thread", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread priority
+ }
+
+ ndbout << "All threads created" << endl;
+
+ if (NdbMutex_Lock(testmutex) != 0)
+ fail("TEST3", "NdbMutex_Lock failed");
+
+ while (testthreadsdone < T3_THREADS*10)
+ {
+ if(NdbCondition_Wait(testcond, testmutex) != 0)
+ fail("TEST3", "NdbCondition_Wait failed");
+ ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
+ }
+ if (NdbMutex_Unlock(testmutex) != 0)
+ fail("TEST3", "NdbMutex_Unlock failed");
+
+ for (int i = 0; i < T3_THREADS; i++)
+ {
+ if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
+ fail("TEST3", "NdbThread_WaitFor failed");
+
+ NdbThread_Destroy(&t3threads[i]);
+ //ndbout << "thread" << i << " returned, status = " << status3 << endl;
+ //if (status3 != i)
+ // fail("TEST3", "Wrong status");
+ }
+
+ NdbMutex_Destroy(testmutex);
+ NdbCondition_Destroy(testcond);
+ ndbout << "TEST3 completed" << endl;
+
+ ndbout << "= TEST4 ===============================" << endl;
+ // Check tick functions
+
+ //#if 0
+
+ int sleeptimes[] = {78, 12, 199, 567, 899};
+
+
+ for (int i = 0; i < 5; i++)
+ {
+ ndbout << "*------------------------------- Measure" << i << endl;
+
+ NDB_TICKS millisec_now;
+ NDB_TICKS millisec_now2;
+
+ millisec_now = NdbTick_CurrentMillisecond();
+ NdbSleep_MilliSleep(sleeptimes[i]);
+ millisec_now2 = NdbTick_CurrentMillisecond();
+
+ ndbout << " Time before sleep = " << millisec_now << endl;
+ ndbout << " Time after sleep = " << millisec_now2 << endl;
+ ndbout << " Tried to sleep "<<sleeptimes[i]<<" milliseconds." << endl;
+ ndbout << " Sleep time was " << millisec_now2 -millisec_now <<" milliseconds." << endl;
+
+ }
+
+ ndbout << "TEST4 completed" << endl;
+
+ ndbout << "= TEST5 ===============================" << endl;
+ // Check NdbOut
+
+ ndbout << "Testing hex and dec functions of NdbOut" << endl;
+
+ for (int i = 0; i<= 0xFF; i++)
+ {
+ ndbout << i << "=" <<hex << i << "="<<dec << i << ", ";
+ }
+
+ ndbout << endl<< "Testing that hex is reset to dec by endl" << endl;
+ ndbout << hex << 67 << endl;
+ ndbout << 67 << endl;
+
+ ndbout << "TEST5 completed" << endl;
+
+
+ ndbout << "= TEST6 ===============================" << endl;
+ const char* theEnvHostNamePtr;
+ char buf[255];
+ char theHostHostName[256];
+ theEnvHostNamePtr = NdbEnv_GetEnv("HOSTNAME", buf, 255);
+ if(theEnvHostNamePtr == NULL)
+ fail("TEST6", "Could not get HOSTNAME from env");
+ else{
+ ndbout << "HOSTNAME from GetEnv" << theEnvHostNamePtr << endl;
+
+ NdbHost_GetHostName(theHostHostName);
+
+ ndbout << "HOSTNAME from GetHostName" <<theHostHostName << endl;
+
+ if (strcmp(theEnvHostNamePtr, theHostHostName) != 0)
+ fail("TEST6", "NdbHost_GetHostName or NdbEnv_GetEnv failed");
+ }
+
+ ndbout << "= TEST7 ===============================" << endl;
+
+ testmutex = NdbMutex_Create();
+ testcond = NdbCondition_Create();
+ testthreadsdone = 0;
+
+ for (int i = 0; i < T3_THREADS; i++)
+ {
+ t3args[i] = i;
+ t3threads[i] = NdbThread_Create(testfunc, // Function
+ (void**)&t3args[i],// Arg
+ 2048, // Stacksize
+ (char*)"test7thread", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread priority
+ }
+
+ ndbout << "All threads created" << endl;
+
+ if (NdbMutex_Lock(testmutex) != 0)
+ fail("TEST7", "NdbMutex_Lock failed");
+
+ while (testthreadsdone < T3_THREADS*10)
+ {
+ // just testing the functionality without timing out, therefor 20 sec.
+ if(NdbCondition_WaitTimeout(testcond, testmutex, 20000) != 0)
+ fail("TEST7", "NdbCondition_WaitTimeout failed");
+ ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
+ }
+ if (NdbMutex_Unlock(testmutex) != 0)
+ fail("TEST7", "NdbMutex_Unlock failed");
+
+ for (int i = 0; i < T3_THREADS; i++)
+ {
+ if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
+ fail("TEST7", "NdbThread_WaitFor failed");
+
+ NdbThread_Destroy(&t3threads[i]);
+ }
+
+ NdbMutex_Destroy(testmutex);
+ NdbCondition_Destroy(testcond);
+
+ ndbout << "TEST7 completed" << endl;
+
+
+ ndbout << "= TEST8 ===============================" << endl;
+ ndbout << " NdbCondition_WaitTimeout" << endl;
+ testmutex = NdbMutex_Create();
+ testcond = NdbCondition_Create();
+
+ for (int i = 0; i < 5; i++)
+ {
+ ndbout << "*------------------------------- Measure" << i << endl;
+
+ NDB_TICKS millisec_now;
+ NDB_TICKS millisec_now2;
+
+ millisec_now = NdbTick_CurrentMillisecond();
+ if (NdbCondition_WaitTimeout(testcond, testmutex, sleeptimes[i]) != 0)
+ fail("TEST8", "NdbCondition_WaitTimeout failed");
+ millisec_now2 = NdbTick_CurrentMillisecond();
+
+ ndbout << " Time before WaitTimeout = " << millisec_now << endl;
+ ndbout << " Time after WaitTimeout = " << millisec_now2 << endl;
+ ndbout << " Tried to wait "<<sleeptimes[i]<<" milliseconds." << endl;
+ ndbout << " Wait time was " << millisec_now2 -millisec_now <<" milliseconds." << endl;
+
+ }
+
+ ndbout << "TEST8 completed" << endl;
+
+
+ ndbout << "= TEST9 ===============================" << endl;
+ ndbout << " NdbTick_CurrentXXXXXsecond compare" << endl;
+
+ for (int i = 0; i < 5; i++)
+ {
+ ndbout << "*------------------------------- Measure" << i << endl;
+
+ NDB_TICKS millisec_now;
+ NDB_TICKS millisec_now2;
+ Uint32 usec_now, usec_now2;
+ Uint64 msec_now, msec_now2;
+
+
+ millisec_now = NdbTick_CurrentMillisecond();
+ NdbTick_CurrentMicrosecond( &msec_now, &usec_now);
+
+ NdbSleep_MilliSleep(sleeptimes[i]);
+
+ millisec_now2 = NdbTick_CurrentMillisecond();
+ NdbTick_CurrentMicrosecond( &msec_now2, &usec_now2);
+
+ Uint64 usecdiff = time_diff(msec_now,msec_now2,usec_now,usec_now2);
+ NDB_TICKS msecdiff = millisec_now2 -millisec_now;
+
+ ndbout << " Slept "<<sleeptimes[i]<<" milliseconds." << endl;
+ ndbout << " Measured " << msecdiff <<" milliseconds with milli function ." << endl;
+ ndbout << " Measured " << usecdiff/1000 << "," << usecdiff%1000<<" milliseconds with micro function ." << endl;
+ }
+
+ ndbout << "TEST9 completed" << endl;
+
+
+ const int iter = 20;
+ ndbout << "Testing microsecond timer - " << iter << " iterations" << endl;
+ testMicros(iter);
+ ndbout << "Testing microsecond timer - COMPLETED" << endl;
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ ndbout << "system_tick() = " << system_tick() << " us per tick" << endl;
+#endif
+
+
+ ndbout << "= TEST10 ===============================" << endl;
+
+ testmutex = NdbMutex_Create();
+ testcond = NdbCondition_Create();
+ testthreadsdone = 0;
+
+ for (int i = 0; i < T3_THREADS; i++)
+ {
+ t3args[i] = i;
+ t3threads[i] = NdbThread_Create(testTryLockfunc, // Function
+ (void**)&t3args[i],// Arg
+ 2048, // Stacksize
+ (char*)"test10thread", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread priority
+ }
+
+ ndbout << "All threads created" << endl;
+
+ if (NdbMutex_Lock(testmutex) != 0)
+ fail("TEST10", "NdbMutex_Lock failed");
+
+ while (testthreadsdone < T3_THREADS*10)
+ {
+ if(NdbCondition_Wait(testcond, testmutex) != 0)
+ fail("TEST10", "NdbCondition_WaitTimeout failed");
+ ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
+ }
+ if (NdbMutex_Unlock(testmutex) != 0)
+ fail("TEST10", "NdbMutex_Unlock failed");
+
+ for (int i = 0; i < T3_THREADS; i++)
+ {
+ if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
+ fail("TEST10", "NdbThread_WaitFor failed");
+
+ NdbThread_Destroy(&t3threads[i]);
+ }
+
+ NdbMutex_Destroy(testmutex);
+ NdbCondition_Destroy(testcond);
+
+ ndbout << "TEST10 completed" << endl;
+
+
+ // Check total status of test
+
+ if (TestHasFailed == 1)
+ ndbout << endl << "TEST FAILED!" << endl;
+ else
+ ndbout << endl << "TEST PASSED!" << endl;
+
+ return TestHasFailed;
+
+};
+
+Uint64 time_diff(Uint64 s1, Uint64 s2, Uint32 m1, Uint32 m2){
+
+ Uint64 diff = 0;
+ diff += (s2 - s1) * 1000000;
+ if(m2 >= m1)
+ diff += (m2 - m1);
+ else {
+ diff += m2;
+ diff -= m1;
+ }
+
+ // if(0)
+ // ndbout("(s1,m1) = (%d, %d) (s2,m2) = (%d, %d) -> diff = %d\n",
+ // (Uint32)s1,m1,(Uint32)s2,m2, (Uint32)diff);
+
+ return diff;
+};
+
+void
+testMicros(int count){
+ Uint32 avg = 0;
+ Uint32 sum2 = 0;
+
+ for(int i = 0; i<count; i++){
+ Uint64 s1, s2;
+ Uint32 m1, m2;
+ if(NdbTick_CurrentMicrosecond(&s1, &m1) != 0){
+ ndbout << "Failed to get current micro" << endl;
+ TestHasFailed = 1;
+ return;
+ }
+ Uint32 r = (rand() % 1000) + 1;
+ NdbSleep_MilliSleep(r);
+ if(NdbTick_CurrentMicrosecond(&s2, &m2) != 0){
+ ndbout << "Failed to get current micro" << endl;
+ TestHasFailed = 1;
+ return;
+ }
+ Uint64 m = time_diff(s1,s2,m1,m2);
+ if(verbose)
+ ndbout << "Slept for " << r << " ms"
+ << " - Measured " << m << " us" << endl;
+
+ if(m > (r*1000)){
+ avg += (m - (r*1000));
+ sum2 += (m - (r*1000)) * (m - (r*1000));
+ } else {
+ avg += ((r*1000) - m);
+ sum2 += ((r*1000) - m) * ((r*1000) - m);
+ }
+#if 0
+ m /= 1000;
+ if(m > r && ((m - r) > 10)){
+ ndbout << "Difference to big: " << (m - r) << " - Test failed" << endl;
+ TestHasFailed = 1;
+ }
+ if(m < r && ((r - m) > 10)){
+ ndbout << "Difference to big: " << (r - m) << " - Test failed" << endl;
+ TestHasFailed = 1;
+ }
+#endif
+ }
+
+ Uint32 dev = (avg * avg - sum2) / count; dev /= count;
+ avg /= count;
+
+ Uint32 t = 0;
+ while((t*t)<dev) t++;
+ ndbout << "NOTE - measure are compared to NdbSleep_MilliSleep(...)" << endl;
+ ndbout << "Average error = " << avg << " us" << endl;
+ ndbout << "Stddev error = " << t << " us" << endl;
+}
diff --git a/storage/ndb/src/common/portlib/NdbSleep.c b/storage/ndb/src/common/portlib/NdbSleep.c
new file mode 100644
index 00000000000..44bafe98a37
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbSleep.c
@@ -0,0 +1,43 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <my_sys.h>
+#include <NdbSleep.h>
+
+int
+NdbSleep_MilliSleep(int milliseconds){
+ my_sleep(milliseconds*1000);
+ return 0;
+#if 0
+ int result = 0;
+ struct timespec sleeptime;
+ sleeptime.tv_sec = milliseconds / 1000;
+ sleeptime.tv_nsec = (milliseconds - (sleeptime.tv_sec * 1000)) * 1000000;
+ result = nanosleep(&sleeptime, NULL);
+ return result;
+#endif
+}
+
+int
+NdbSleep_SecSleep(int seconds){
+ int result = 0;
+ result = sleep(seconds);
+ return result;
+}
+
+
diff --git a/storage/ndb/src/common/portlib/NdbTCP.cpp b/storage/ndb/src/common/portlib/NdbTCP.cpp
new file mode 100644
index 00000000000..a63f5a7ba27
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbTCP.cpp
@@ -0,0 +1,76 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <my_net.h>
+#include <NdbTCP.h>
+
+extern "C"
+int
+Ndb_getInAddr(struct in_addr * dst, const char *address) {
+ // DBUG_ENTER("Ndb_getInAddr");
+ {
+ int tmp_errno;
+ struct hostent tmp_hostent, *hp;
+ char buff[GETHOSTBYNAME_BUFF_SIZE];
+ hp = my_gethostbyname_r(address,&tmp_hostent,buff,sizeof(buff),
+ &tmp_errno);
+ if (hp)
+ {
+ memcpy(dst, hp->h_addr, min(sizeof(*dst), (size_t) hp->h_length));
+ my_gethostbyname_r_free();
+ return 0; //DBUG_RETURN(0);
+ }
+ my_gethostbyname_r_free();
+ }
+ /* Try it as aaa.bbb.ccc.ddd. */
+ dst->s_addr = inet_addr(address);
+ if (dst->s_addr !=
+#ifdef INADDR_NONE
+ INADDR_NONE
+#else
+ -1
+#endif
+ )
+ {
+ return 0; //DBUG_RETURN(0);
+ }
+ // DBUG_PRINT("error",("inet_addr(%s) - %d - %s",
+ // address, errno, strerror(errno)));
+ return -1; //DBUG_RETURN(-1);
+}
+
+#if 0
+int
+Ndb_getInAddr(struct in_addr * dst, const char *address) {
+ struct hostent host, * hostPtr;
+ char buf[1024];
+ int h_errno;
+ hostPtr = gethostbyname_r(address, &host, &buf[0], 1024, &h_errno);
+ if (hostPtr != NULL) {
+ dst->s_addr = ((struct in_addr *) *hostPtr->h_addr_list)->s_addr;
+ return 0;
+ }
+
+ /* Try it as aaa.bbb.ccc.ddd. */
+ dst->s_addr = inet_addr(address);
+ if (dst->s_addr != -1) {
+ return 0;
+ }
+ return -1;
+}
+#endif
diff --git a/storage/ndb/src/common/portlib/NdbThread.c b/storage/ndb/src/common/portlib/NdbThread.c
new file mode 100644
index 00000000000..aaee9b45069
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbThread.c
@@ -0,0 +1,155 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbThread.h>
+#include <my_pthread.h>
+#include <NdbMem.h>
+
+#define MAX_THREAD_NAME 16
+
+/*#define USE_PTHREAD_EXTRAS*/
+
+#ifdef NDB_SHM_TRANSPORTER
+int g_ndb_shm_signum= 0;
+#endif
+
+struct NdbThread
+{
+ pthread_t thread;
+ char thread_name[MAX_THREAD_NAME];
+ NDB_THREAD_FUNC * func;
+ void * object;
+};
+
+static
+void*
+ndb_thread_wrapper(void* _ss){
+ my_thread_init();
+ {
+ DBUG_ENTER("ndb_thread_wrapper");
+#ifdef NDB_SHM_TRANSPORTER
+ if (g_ndb_shm_signum)
+ {
+ sigset_t mask;
+ DBUG_PRINT("info",("Block signum %d",g_ndb_shm_signum));
+ sigemptyset(&mask);
+ sigaddset(&mask, g_ndb_shm_signum);
+ pthread_sigmask(SIG_BLOCK, &mask, 0);
+ }
+#endif
+ {
+ void *ret;
+ struct NdbThread * ss = (struct NdbThread *)_ss;
+ ret= (* ss->func)(ss->object);
+ NdbThread_Exit(ret);
+ }
+ /* will never be reached */
+ DBUG_RETURN(0);
+ }
+}
+
+
+struct NdbThread* NdbThread_Create(NDB_THREAD_FUNC *p_thread_func,
+ NDB_THREAD_ARG *p_thread_arg,
+ const NDB_THREAD_STACKSIZE thread_stack_size,
+ const char* p_thread_name,
+ NDB_THREAD_PRIO thread_prio)
+{
+ struct NdbThread* tmpThread;
+ int result;
+ pthread_attr_t thread_attr;
+
+ (void)thread_prio; /* remove warning for unused parameter */
+
+ if (p_thread_func == NULL)
+ return 0;
+
+ tmpThread = (struct NdbThread*)NdbMem_Allocate(sizeof(struct NdbThread));
+ if (tmpThread == NULL)
+ return NULL;
+
+ strnmov(tmpThread->thread_name,p_thread_name,sizeof(tmpThread->thread_name));
+
+ pthread_attr_init(&thread_attr);
+#if (SIZEOF_CHARP == 8)
+ pthread_attr_setstacksize(&thread_attr, 2*thread_stack_size);
+#else
+ pthread_attr_setstacksize(&thread_attr, thread_stack_size);
+#endif
+#ifdef USE_PTHREAD_EXTRAS
+ /* Guard stack overflow with a 2k databuffer */
+ pthread_attr_setguardsize(&thread_attr, 2048);
+#endif
+
+#ifdef PTHREAD_CREATE_JOINABLE /* needed on SCO */
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
+#endif
+ tmpThread->func= p_thread_func;
+ tmpThread->object= p_thread_arg;
+ result = pthread_create(&tmpThread->thread,
+ &thread_attr,
+ ndb_thread_wrapper,
+ tmpThread);
+ assert(result==0);
+
+ pthread_attr_destroy(&thread_attr);
+ return tmpThread;
+}
+
+
+void NdbThread_Destroy(struct NdbThread** p_thread)
+{
+ if (*p_thread != NULL){
+ free(* p_thread);
+ * p_thread = 0;
+ }
+}
+
+
+int NdbThread_WaitFor(struct NdbThread* p_wait_thread, void** status)
+{
+ int result;
+
+ if (p_wait_thread == NULL)
+ return 0;
+
+ if (p_wait_thread->thread == 0)
+ return 0;
+
+ result = pthread_join(p_wait_thread->thread, status);
+
+ return result;
+}
+
+
+void NdbThread_Exit(void *status)
+{
+ my_thread_end();
+ pthread_exit(status);
+}
+
+
+int NdbThread_SetConcurrencyLevel(int level)
+{
+#ifdef USE_PTHREAD_EXTRAS
+ return pthread_setconcurrency(level);
+#else
+ (void)level; /* remove warning for unused parameter */
+ return 0;
+#endif
+}
diff --git a/storage/ndb/src/common/portlib/NdbTick.c b/storage/ndb/src/common/portlib/NdbTick.c
new file mode 100644
index 00000000000..d8f0b6ec27a
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbTick.c
@@ -0,0 +1,106 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include "NdbTick.h"
+
+#define NANOSEC_PER_SEC 1000000000
+#define MICROSEC_PER_SEC 1000000
+#define MILLISEC_PER_SEC 1000
+#define MICROSEC_PER_MILLISEC 1000
+#define MILLISEC_PER_NANOSEC 1000000
+
+
+#ifdef HAVE_CLOCK_GETTIME
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ struct timespec tick_time;
+ clock_gettime(CLOCK_REALTIME, &tick_time);
+
+ return
+ ((NDB_TICKS)tick_time.tv_sec) * ((NDB_TICKS)MILLISEC_PER_SEC) +
+ ((NDB_TICKS)tick_time.tv_nsec) / ((NDB_TICKS)MILLISEC_PER_NANOSEC);
+}
+
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros){
+ struct timespec t;
+ int res = clock_gettime(CLOCK_REALTIME, &t);
+ * secs = t.tv_sec;
+ * micros = t.tv_nsec / 1000;
+ return res;
+}
+#else
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ struct timeval tick_time;
+ gettimeofday(&tick_time, 0);
+
+ return
+ ((NDB_TICKS)tick_time.tv_sec) * ((NDB_TICKS)MILLISEC_PER_SEC) +
+ ((NDB_TICKS)tick_time.tv_usec) / ((NDB_TICKS)MICROSEC_PER_MILLISEC);
+}
+
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros){
+ struct timeval tick_time;
+ int res = gettimeofday(&tick_time, 0);
+
+ if(secs==0) {
+ NDB_TICKS secs = tick_time.tv_sec;
+ *micros = tick_time.tv_usec;
+ *micros = secs*1000000+*micros;
+ } else {
+ * secs = tick_time.tv_sec;
+ * micros = tick_time.tv_usec;
+ }
+ return res;
+}
+
+#endif
+#ifdef TIME_MEASUREMENT
+int
+NdbTick_getMicroTimer(struct MicroSecondTimer* input_timer)
+{
+ NDB_TICKS secs;
+ Uint32 mics;
+ int ret_value;
+ ret_value = NdbTick_CurrentMicrosecond(&secs, &mics);
+ input_timer->seconds = secs;
+ input_timer->micro_seconds = (NDB_TICKS)mics;
+ return ret_value;
+}
+
+NDB_TICKS
+NdbTick_getMicrosPassed(struct MicroSecondTimer start,
+ struct MicroSecondTimer stop)
+{
+ NDB_TICKS ret_value = (NDB_TICKS)0;
+ if (start.seconds < stop.seconds) {
+ NDB_TICKS sec_passed = stop.seconds - start.seconds;
+ ret_value = ((NDB_TICKS)MICROSEC_PER_SEC) * sec_passed;
+ } else if (start.seconds > stop.seconds) {
+ return ret_value;
+ }
+ if (start.micro_seconds < stop.micro_seconds) {
+ ret_value += (stop.micro_seconds - start.micro_seconds);
+ } else if (ret_value != (NDB_TICKS)0) {
+ ret_value -= (start.micro_seconds - stop.micro_seconds);
+ }
+ return ret_value;
+}
+#endif
diff --git a/storage/ndb/src/common/portlib/gcc.cpp b/storage/ndb/src/common/portlib/gcc.cpp
new file mode 100644
index 00000000000..66aa4812dc6
--- /dev/null
+++ b/storage/ndb/src/common/portlib/gcc.cpp
@@ -0,0 +1,7 @@
+
+/**
+ * GCC linking problem...
+ */
+#ifdef DEFINE_CXA_PURE_VIRTUAL
+extern "C" { int __cxa_pure_virtual() { return 0;} }
+#endif
diff --git a/storage/ndb/src/common/portlib/memtest.c b/storage/ndb/src/common/portlib/memtest.c
new file mode 100644
index 00000000000..673f23fa803
--- /dev/null
+++ b/storage/ndb/src/common/portlib/memtest.c
@@ -0,0 +1,243 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <ndb_global.h>
+
+long long getMilli();
+long long getMicro();
+void malloctest(int loopcount, int memsize, int touch);
+void freetest(int loopcount, int memsize);
+void mmaptest(int loopcount, int memsize, int touch);
+void unmaptest(int loopcount, int memsize);
+
+
+main(int argc, char ** argv)
+{
+
+ int loopcount;
+ int memsize;
+ if(argc < 4) {
+ printf("Usage: memtest X loopcount memsize(MB)\n");
+ printf("where X = \n");
+ printf("1 : malloc test \n");
+ printf("2 : mmap test \n");
+ printf("3 : malloc test + touch pages\n");
+ printf("4 : mmap test + touch pages\n");
+ printf("5 : malloc/free test \n");
+ printf("6 : mmap/munmap test \n");
+ printf("loopcount - number of loops\n");
+ printf("memsize - memory segment size to allocate in MB.\n");
+ exit(1);
+ }
+
+
+ loopcount = atoi(argv[2]);
+ memsize = atoi(argv[3]);
+ switch(atoi(argv[1])) {
+ case 1: malloctest(loopcount, memsize , 0 );
+ break;
+ case 2: mmaptest(loopcount, memsize,0);
+ break;
+ case 3: malloctest(loopcount, memsize,1);
+ break;
+ case 4: mmaptest(loopcount, memsize,1);
+ break;
+ case 5: freetest(loopcount, memsize);
+ break;
+ case 6: unmaptest(loopcount, memsize);
+ break;
+ default:
+ break;
+ }
+}
+
+long long getMilli() {
+ struct timeval tick_time;
+ gettimeofday(&tick_time, 0);
+
+ return
+ ((long long)tick_time.tv_sec) * ((long long)1000) +
+ ((long long)tick_time.tv_usec) / ((long long)1000);
+}
+
+long long getMicro(){
+ struct timeval tick_time;
+ int res = gettimeofday(&tick_time, 0);
+
+ long long secs = tick_time.tv_sec;
+ long long micros = tick_time.tv_usec;
+
+ micros = secs*1000000+micros;
+ return micros;
+}
+
+void malloctest(int loopcount, int memsize, int touch) {
+ long long start=0;
+ int total=0;
+ int i=0, j=0;
+ int size=memsize*1024*1024; /*bytes*/;
+ float mean;
+ char * ptr =0;
+
+ printf("Staring malloctest ");
+ if(touch)
+ printf("with touch\n");
+ else
+ printf("\n");
+
+ start=getMicro();
+
+ for(i=0; i<loopcount; i++){
+ ptr=(char *)malloc((size_t)(size));
+ if(ptr==0) {
+ printf("failed to malloc!\n");
+ return;
+ }
+ if(touch) {
+ for(j=0; j<size; j=j+4096)
+ ptr[j]=1;
+ }
+ }
+ total=(int)(getMicro()-start);
+
+ mean=(float)((float)total/(float)loopcount);
+ printf("Total time malloc %d bytes: %2.3f microsecs loopcount %d touch %d \n",
+ size, mean,loopcount, touch);
+}
+
+
+void mmaptest(int loopcount, int memsize, int touch) {
+ long long start=0;
+ int total=0;
+ int i=0, j=0;
+ char * ptr;
+ int size=memsize*1024*1024; /*bytes*/;
+ float mean;
+
+ printf("Staring mmaptest ");
+ if(touch)
+ printf("with touch \n");
+ else
+ printf("\n");
+
+ start=getMicro();
+ for(i=0; i<loopcount; i++){
+ ptr = mmap(0,
+ size,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ 0,
+ 0);
+ if(ptr<0) {
+ printf("failed to mmap!\n");
+ return;
+ }
+
+ if(touch) {
+ for(j=0; j<size; j=j+4096)
+ ptr[j]=1;
+ }
+ }
+ total=(int)(getMicro()-start);
+ mean=(float)((float)total/(float)loopcount);
+ printf("Total time mmap %d bytes: %2.3f microsecs \n",size, mean);
+}
+
+
+void unmaptest(loopcount, memsize)
+{
+ long long start=0;
+ int total=0;
+ int i=0, j=0;
+ char * ptr;
+ int size=memsize*1024*1024; /*bytes*/;
+ float mean;
+
+ printf("Staring munmap test (loopcount = 1 no matter what you prev. set)\n");
+
+ loopcount = 1;
+
+
+ for(i=0; i<loopcount; i++){
+ ptr =(char*) mmap(0,
+ size,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ 0,
+ 0);
+ if(ptr<0) {
+ printf("failed to mmap!\n");
+ return;
+ }
+
+
+ for(j=0; j<size; j=j+1)
+ ptr[j]='1';
+ start=getMicro();
+ if(munmap(ptr, size)<0) {
+ printf("failed to munmap!\n");
+ return;
+ }
+
+ total=(int)(getMicro()-start);
+ /*
+ for(j=8192; j<size; j=j+4096) {
+
+ *(ptr+j)='1';
+ }
+
+ for(j=0; j<4096; j=j+4096) {
+ *(ptr+j)='1';
+ }
+
+ */
+ }
+ mean=(float)((float)total/(float)loopcount);
+ printf("Total time unmap %d bytes: %2.3f microsecs \n",size, mean);
+}
+
+void freetest(int loopcount, int memsize) {
+ long long start=0;
+ int total=0;
+ int i=0, j=0;
+ int size=memsize*1024*1024; /*bytes*/;
+ float mean;
+ char * ptr =0;
+
+ loopcount = 1;
+ printf("Staring free test (loopcount = 1 no matter what you prev. set)\n");
+
+
+ for(i=0; i<loopcount; i++){
+ ptr=(char*)malloc((size_t)(size));
+ if(ptr==0) {
+ printf("failed to malloc!\n");
+ return;
+ }
+ for(j=0; j<size; j=j+4096)
+ ptr[j]='1';
+ start=getMicro();
+ free(ptr);
+ total=(int)(getMicro()-start);
+ }
+
+
+ mean=(float)((float)total/(float)loopcount);
+ printf("Total time free %d bytes: %2.3f microsecs loopcount %d \n",
+ size, mean,loopcount);
+}
diff --git a/storage/ndb/src/common/portlib/mmslist.cpp b/storage/ndb/src/common/portlib/mmslist.cpp
new file mode 100644
index 00000000000..05538785293
--- /dev/null
+++ b/storage/ndb/src/common/portlib/mmslist.cpp
@@ -0,0 +1,103 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_common.h>
+
+#include <NdbOut.hpp>
+#include <NdbMain.h>
+
+#include <ose.h>
+#include <mms.sig>
+#include <mms_err.h>
+#include <NdbOut.hpp>
+
+/**
+ * NOTE: To use NdbMem from a OSE system ose_mms has to be defined
+ * as a "Required External Process"(see OSE Kernel User's Guide/R1.1(p. 148)),
+ * like this:
+ * EXT_PROC(ose_mms, ose_mms, 50000)
+ * This will create a global variable ose_mms_ that is used from here.
+ */
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct MmsListDomainRequest mmsListDomainRequest;
+ struct MmsListDomainReply mmsListDomainReply;
+}; /* union SIGNAL */
+
+extern PROCESS ose_mms_;
+
+struct ARegion
+{
+ unsigned long int address;
+ unsigned long int size;
+ char name[32];
+
+ U32 resident; /* Boolean, nonzero if resident. */
+ U32 access; /* See values for AccessType (above) .*/
+ U32 type; /* either RAM-mem (1) or Io-mem (2) */
+ U32 cache; /* 0-copyback,1-writethrough, 2-CacheInhibit.*/
+};
+
+NDB_COMMAND(mmslist, "mmslist", "mmslist", "LIst the MMS memory segments", 4096){
+ if (argc == 1){
+
+ static SIGSELECT allocate_sig[] = {1,MMS_LIST_DOMAIN_REPLY};
+ union SIGNAL *sig;
+
+ /* Send request to list all segments and regions. */
+ sig = alloc(sizeof(struct MmsListDomainRequest),
+ MMS_LIST_DOMAIN_REQUEST);
+ send(&sig, ose_mms_);
+
+ while (true){
+ sig = receive(allocate_sig);
+ if (sig != NIL){
+ if (sig->mmsListDomainReply.status == MMS_SUCCESS){
+ /* Print domain info */
+ ndbout << "=================================" << endl;
+ ndbout << "domain: " << sig->mmsListDomainReply.domain << endl;
+ ndbout << "name : " << sig->mmsListDomainReply.name << endl;
+ ndbout << "used : " << sig->mmsListDomainReply.used << endl;
+ ndbout << "lock : " << sig->mmsListDomainReply.lock << endl;
+ ndbout << "numOfRegions:" << sig->mmsListDomainReply.numOfRegions << endl;
+ struct ARegion * tmp = (struct ARegion*)&sig->mmsListDomainReply.regions[0];
+ for (int i = 0; i < sig->mmsListDomainReply.numOfRegions && i < 256; i++){
+ ndbout << i << ": adress=" << tmp->address <<
+ ", size=" << tmp->size <<
+ ", name=" << tmp->name <<
+ ", resident=" << tmp->resident <<
+ ", access=" << tmp->access <<
+ ", type=" << tmp->type <<
+ ", cache=" << tmp->cache << endl;
+ tmp++;
+ }
+
+ free_buf(&sig);
+ }else{
+ free_buf(&sig);
+ break;
+ }
+ }
+
+ }
+
+ }else{
+ ndbout << "Usage: mmslist" << endl;
+ }
+ return NULL;
+}
diff --git a/storage/ndb/src/common/portlib/mmstest.cpp b/storage/ndb/src/common/portlib/mmstest.cpp
new file mode 100644
index 00000000000..9cc7d810985
--- /dev/null
+++ b/storage/ndb/src/common/portlib/mmstest.cpp
@@ -0,0 +1,76 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbOut.hpp>
+#include "NdbThread.h"
+#include <NdbMem.h>
+#include <NdbMain.h>
+
+NDB_COMMAND(ndbmem, "ndbmem", "ndbmem", "Test the ndbmem functionality", 4096){
+
+ ndbout << "Starting test of NdbMem" << endl;
+ ndbout << "=======================" << endl;
+
+ ndbout << "Creating NdbMem" << endl;
+ NdbMem_Create();
+
+
+ ndbout << "NdbMem - test 1" << endl;
+ if (argc == 2){
+ int size1 = atoi(argv[1]);
+ ndbout << "Allocate and test "<<size1<<" bytes of memory" << endl;
+ char* mem1 = (char*)NdbMem_Allocate(size1);
+ ndbout << "mem1 = " << hex << (int)mem1 << endl;
+ if (mem1 != NULL){
+ char* p1;
+
+ // Write to the memory allocated
+ p1 = mem1;
+ for(int i = 0; i < size1; i++){
+ *p1 = (char)(i%256);
+ p1++;
+ }
+
+ // Read from the memory and check value
+ char read1;
+ char* pread1;
+ pread1 = mem1;
+ for(int i = 0; i < size1; i++){
+ read1 = *pread1;
+ //ndbout << i << "=" << read1 << endl;
+ if (read1 != (i%256))
+ ndbout << "Byte " << i << " was not correct, read1=" << read1 << endl;
+ pread1++;
+ }
+
+ ndbout << "Freeing NdbMem" << endl;
+ NdbMem_Free(mem1);
+ }
+
+ ndbout << "Destroying NdbMem" << endl;
+ NdbMem_Destroy();
+ }else{
+ ndbout << "Usage: ndbmem <size(bytes)>"<< endl;
+ }
+
+ return NULL;
+
+}
+
+
+
diff --git a/storage/ndb/src/common/portlib/munmaptest.cpp b/storage/ndb/src/common/portlib/munmaptest.cpp
new file mode 100644
index 00000000000..b1d84131810
--- /dev/null
+++ b/storage/ndb/src/common/portlib/munmaptest.cpp
@@ -0,0 +1,246 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+
+#include <ndb_global.h>
+
+#include <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbEnv.h>
+#include <NdbHost.h>
+#include <NdbMain.h>
+#include <getarg.h>
+
+struct ThreadData
+{
+ char * mapAddr;
+ Uint32 mapSize;
+ Uint32 chunk;
+ Uint32 idx;
+
+};
+
+long long getMilli();
+long long getMicro();
+
+
+void* mapSegment(void * arg);
+void* unmapSegment(void * arg);
+
+
+void* mapSegment(void * arg) {
+
+ ThreadData * threadArgs;
+ long long start=0;
+ int total=0;
+ int id = *(int *)arg;
+ threadArgs = new ThreadData [1];
+ Uint32 size=5*1024*1024;
+ struct NdbThread* unmapthread_var;
+ void *status = 0;
+ int run = 1;
+ int max=0, min =100000000, sum=0;
+ while(run < 1001) {
+ start=getMicro();
+ char * ptr =(char*) mmap(0,
+ size,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ 0,
+ 0);
+
+ total=(int)(getMicro()-start);
+
+ ndbout << "T" << id << ": mmap took : " << total << " microsecs. "
+ << " Run: " << run ;
+ ndbout_c(" mapped @ %p \n", ptr);
+
+ if(total>max)
+ max = total;
+ if(total<min)
+ min=total;
+
+ sum+=total;
+
+ if(ptr<0) {
+ ndbout << "failed to mmap!" << endl;
+ exit(1);
+ }
+
+
+ threadArgs[0].mapAddr = (char *)ptr;
+ threadArgs[0].mapSize = size;
+ threadArgs[0].chunk = 4096;
+ threadArgs[0].idx = 0;
+
+
+ for(Uint32 j=0; j<size; j=j+4096)
+ ptr[j]='1';
+
+ unmapthread_var = NdbThread_Create(unmapSegment, // Function
+ (void**)&threadArgs[0],// Arg
+ 32768, // Stacksize
+ (char*)"unmapthread", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread prio
+
+
+ if(NdbThread_WaitFor(unmapthread_var, &status) != 0) {
+ ndbout << "test failed - exitting " << endl;
+ exit(1);
+ }
+ run++;
+ }
+
+ ndbout << "MAX: " << max << " MIN: " << min;
+ float mean = (float) ((float)sum/(float)run);
+ ndbout_c(" AVERAGE: %2.5f\n",mean);
+}
+
+
+
+void* unmapSegment(void * arg)
+{
+
+ char * freeAddr;
+ char * mapAddr;
+ ThreadData * threadData = (ThreadData*) arg;
+ int start=0;
+ int total=0;
+ Uint32 mapSize = threadData->mapSize;
+ Uint32 chunk = threadData->chunk;
+ mapAddr = threadData->mapAddr;
+
+
+
+ freeAddr = mapAddr+mapSize-chunk;
+ NdbSleep_MilliSleep(100);
+ for(Uint32 i=0;i<mapSize; i = i+chunk) {
+ start=getMicro();
+ if(munmap(freeAddr, chunk) < 0){
+ ndbout << "munmap failed" << endl;
+ exit(1);
+ }
+ total=(int)(getMicro()-start);
+ freeAddr = freeAddr - chunk;
+ NdbSleep_MilliSleep(10);
+ ndbout << "unmap 4096 bytes : " << total << "microsecs" << endl;
+ }
+ return NULL;
+}
+
+
+static int trash;
+static int segmentsize=1;
+
+
+static struct getargs args[] = {
+ { "trash", 't', arg_integer, &trash,
+ "trash the memory before (1 to trash 0 to not trash)", "trash"},
+ { "segment", 's', arg_integer, &segmentsize,
+ "segment size (in MB)", "segment"},
+};
+
+
+static const int num_args = sizeof(args) / sizeof(args[0]);
+
+NDB_MAIN(munmaptest) {
+
+ const char *progname = "munmaptest";
+ int optind = 0;
+
+ if(getarg(args, num_args, argc, argv, &optind)) {
+ arg_printusage(args, num_args, progname, "");
+ exit(1);
+ }
+
+ int size;
+ char * ptr;
+ if(trash) {
+ for(int i=0; i<100; i++) {
+ size=1+(int) (10.0*rand()/(RAND_MAX+1.0));
+ NdbSleep_MilliSleep(10);
+ ptr =(char*) mmap(0,
+ size*1024*1024,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ 0,
+ 0);
+ for(int i=0;i<(size*1024*1024); i=i+4096) {
+ *(ptr+i)='1';
+ }
+ NdbSleep_MilliSleep(10);
+
+ munmap(ptr,size);
+
+ }
+
+
+ }
+
+ int noThreads = 1;
+ struct NdbThread* mapthread_var;
+ int id[noThreads];
+ void *status=0;
+
+ ThreadData * threadArgs = new ThreadData[noThreads];
+
+
+
+
+ for(int i=0; i < noThreads; i++) {
+ threadArgs[i].mapSize = segmentsize*1024*1024;
+ threadArgs[i].idx = i;
+ mapthread_var = NdbThread_Create(mapSegment, // Function
+ (void**)&threadArgs[i],// Arg
+ 32768, // Stacksize
+ (char*)"mapthread", // Thread name
+ NDB_THREAD_PRIO_MEAN); // Thread prio
+
+ }
+
+
+ if(NdbThread_WaitFor(mapthread_var, &status) != 0) {
+ ndbout << "test failed - exitting " << endl;
+ exit(1);
+ }
+
+}
+
+long long getMilli() {
+ struct timeval tick_time;
+ gettimeofday(&tick_time, 0);
+
+ return
+ ((long long)tick_time.tv_sec) * ((long long)1000) +
+ ((long long)tick_time.tv_usec) / ((long long)1000);
+}
+
+long long getMicro(){
+ struct timeval tick_time;
+ int res = gettimeofday(&tick_time, 0);
+
+ long long secs = tick_time.tv_sec;
+ long long micros = tick_time.tv_usec;
+
+ micros = secs*1000000+micros;
+ return micros;
+}
diff --git a/storage/ndb/src/common/portlib/old_dirs/memtest/Makefile b/storage/ndb/src/common/portlib/old_dirs/memtest/Makefile
new file mode 100644
index 00000000000..716cdbdea82
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/memtest/Makefile
@@ -0,0 +1,12 @@
+CC=gcc
+LD=$(CC)
+SOURCES=memtest.c
+OUTPUT=memtest
+all:
+ $(CC) $(SOURCES) -o $(OUTPUT)
+
+debug:
+ $(CC) -g $(SOURCES) -o $(OUTPUT)
+
+clean: rm -rf *.o
+ rm -rf core*
diff --git a/storage/ndb/src/common/portlib/old_dirs/memtest/munmaptest/Makefile b/storage/ndb/src/common/portlib/old_dirs/memtest/munmaptest/Makefile
new file mode 100644
index 00000000000..ea8c5238d1c
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/memtest/munmaptest/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE := ndbapitest
+BIN_TARGET := munmaptest
+
+
+SOURCES = munmaptest.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/Makefile b/storage/ndb/src/common/portlib/old_dirs/ose/Makefile
new file mode 100644
index 00000000000..4ef93b7824a
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/Makefile
@@ -0,0 +1,31 @@
+include .defs.mk
+
+TYPE :=
+
+PIC_ARCHIVE := Y
+ARCHIVE_TARGET := portlib
+
+SOURCES = NdbOut.cpp
+
+SOURCES.c = NdbCondition.c \
+ NdbMutex.c \
+ NdbSleep.c \
+ NdbTick.c \
+ NdbEnv.c \
+ NdbThread.c \
+ NdbHost.c \
+ NdbTCP.c
+
+ifeq ($(NDB_OS), SOFTOSE)
+ SOURCES += NdbMem_SoftOse.cpp
+else
+ SOURCES.c += NdbMem.c
+endif
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbCondition.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbCondition.c
new file mode 100644
index 00000000000..73a2dbc5d66
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbCondition.c
@@ -0,0 +1,243 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbCondition.h"
+#include <pthread.h>
+#include <sys/types.h>
+#include <malloc.h>
+
+#include <NdbMutex.h>
+
+#include "NdbConditionOSE.h"
+struct NdbCondition
+{
+ PROCESS condserv_pid;
+};
+
+
+OS_PROCESS(ndbcond_serv){
+
+ union SIGNAL* sig;
+ union SIGNAL* sig2;
+
+ static const SIGSELECT sel_signal[] = {2, NDBCOND_SIGNAL, NDBCOND_BROADCAST};
+ static const SIGSELECT sel_cond[] = {2, NDBCOND_WAIT, NDBCOND_WAITTIMEOUT};
+
+ for(;;){
+ /* Receive condition wait signal */
+ sig = receive((SIGSELECT*)sel_cond);
+ if (sig != NIL){
+ switch (sig->sigNo){
+
+ case NDBCOND_WAIT:
+ /* Wait for a SIGNAL or BROADCAST from anyone */
+ sig2 = receive((SIGSELECT*)sel_signal);
+ if (sig2 != NIL){
+ switch(sig2->sigNo){
+
+ case NDBCOND_SIGNAL:
+ ((struct NdbCondWait*)sig)->status = NDBCOND_SIGNALED;
+ /* Send signal back to the one waiting for this condition */
+ send(&sig, sender(&sig));
+ break;
+ case NDBCOND_BROADCAST:
+ /* Not handled yet */
+ assert(1==0);
+ break;
+ default:
+ assert(1==0);
+ break;
+ }
+ free_buf(&sig2);
+ }
+ break;
+
+ case NDBCOND_WAITTIMEOUT:
+ /* Wait for a SIGNAL or BROADCAST from anyone */
+ sig2 = receive_w_tmo(((struct NdbCondWaitTimeout*)sig)->timeout, (SIGSELECT*)sel_signal);
+ if (sig2 != NIL){
+ switch(sig2->sigNo){
+
+ case NDBCOND_SIGNAL:
+ ((struct NdbCondWaitTimeout*)sig)->status = NDBCOND_SIGNALED;
+ /* Send signal back to the one waiting for this condition */
+ send(&sig, sender(&sig));
+ break;
+ case NDBCOND_BROADCAST:
+ /* Not handled yet */
+ assert(1==0);
+ break;
+ default:
+ assert(1==0);
+ break;
+ }
+ free_buf(&sig2);
+ }else{
+ ((struct NdbCondWaitTimeout*)sig)->status = NDBCOND_TIMEOUT;
+ send(&sig, sender(&sig));
+ }
+ break;
+
+ default:
+ assert(1==0);
+ break;
+
+ }
+ }
+
+ }
+}
+
+
+struct NdbCondition*
+NdbCondition_Create(void)
+{
+ struct NdbCondition* tmpCond;
+
+
+ tmpCond = (struct NdbCondition*)malloc(sizeof(struct NdbCondition));
+
+ if (tmpCond == NULL)
+ return NULL;
+
+ /**
+ * Start this process with a quite high
+ * priority, we want it to be responsive
+ */
+ tmpCond->condserv_pid = create_process(OS_PRI_PROC, /* Process type */
+ "ndbcond_serv", /* Name */
+ ndbcond_serv, /* Entry point */
+ 2048, /* Stack size */
+ 10, /* Priority */
+ 0, /* Time slice */
+ get_bid(current_process()), /* Block */
+ NULL, /* Redir table */
+ 0,
+ 0);
+
+ start(tmpCond->condserv_pid);
+
+ return tmpCond;
+}
+
+
+int
+NdbCondition_Wait(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex)
+{
+ static const SIGSELECT sel_cond[] = {1, NDBCOND_WAIT};
+ union SIGNAL* sig;
+ int result;
+ if (p_cond == NULL || p_mutex == NULL)
+ return 0;
+
+ sig = alloc(sizeof(struct NdbCondWait), NDBCOND_WAIT);
+ send(&sig, p_cond->condserv_pid);
+
+ NdbMutex_Unlock(p_mutex);
+
+ result = 1;
+ while(NIL == (sig = receive_from((OSTIME)-1, (SIGSELECT*)sel_cond, p_cond->condserv_pid)));
+ if (sig != NIL){
+ if (sig->sigNo == NDBCOND_WAIT){
+ /* Condition is signaled */
+ result = 0;
+ }else{
+ assert(1==0);
+ }
+ free_buf(&sig);
+
+ }
+ NdbMutex_Lock(p_mutex);
+
+ return result;
+}
+
+
+int
+NdbCondition_WaitTimeout(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex,
+ int msecs){
+ static const SIGSELECT sel_cond[] = {1, NDBCOND_WAITTIMEOUT};
+ union SIGNAL* sig;
+ int result;
+ if (p_cond == NULL || p_mutex == NULL)
+ return 0;
+
+ sig = alloc(sizeof(struct NdbCondWaitTimeout), NDBCOND_WAITTIMEOUT);
+ ((struct NdbCondWaitTimeout*)sig)->timeout = msecs;
+ send(&sig, p_cond->condserv_pid);
+
+ NdbMutex_Unlock(p_mutex);
+
+ result = 1;
+ while(NIL == (sig = receive_from((OSTIME)-1, (SIGSELECT*)sel_cond, p_cond->condserv_pid)));
+ if (sig != NIL){
+ if (sig->sigNo == NDBCOND_WAITTIMEOUT){
+ /* Condition is signaled */
+ result = 0;
+ }else{
+ assert(1==0);
+ }
+ free_buf(&sig);
+
+ }
+
+ NdbMutex_Lock(p_mutex);
+
+ return result;
+}
+
+
+int
+NdbCondition_Signal(struct NdbCondition* p_cond){
+
+ union SIGNAL* sig;
+ if (p_cond == NULL)
+ return 1;
+
+ sig = alloc(sizeof(struct NdbCondSignal), NDBCOND_SIGNAL);
+ send(&sig, p_cond->condserv_pid);
+
+ return 0;
+}
+
+
+int NdbCondition_Broadcast(struct NdbCondition* p_cond)
+{
+ union SIGNAL* sig;
+ if (p_cond == NULL)
+ return 1;
+
+ sig = alloc(sizeof(struct NdbCondBroadcast), NDBCOND_BROADCAST);
+ send(&sig, p_cond->condserv_pid);
+
+ return 0;
+}
+
+
+int NdbCondition_Destroy(struct NdbCondition* p_cond)
+{
+ if (p_cond == NULL)
+ return 1;
+
+ kill_proc(p_cond->condserv_pid);
+ free(p_cond);
+
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbConditionOSE.h b/storage/ndb/src/common/portlib/old_dirs/ose/NdbConditionOSE.h
new file mode 100644
index 00000000000..bd0306261cc
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbConditionOSE.h
@@ -0,0 +1,103 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_CONDITIONOSE_H
+#define NDB_CONDITIONOSE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define NDBCOND_SIGBASE 4000
+
+#define NDBCOND_WAIT (NDBCOND_SIGBASE + 1) /* !-SIGNO(struct NdbCondWait)-! */
+#define NDBCOND_WAITTIMEOUT (NDBCOND_SIGBASE + 2) /* !-SIGNO(struct NdbCondWaitTimeOut)-! */
+#define NDBCOND_SIGNAL (NDBCOND_SIGBASE + 3) /* !-SIGNO(struct NdbCondSignal)-! */
+#define NDBCOND_BROADCAST (NDBCOND_SIGBASE + 4) /* !-SIGNO(struct NdbCondBroadcast)-! */
+
+
+const char *
+sigNo2String(SIGSELECT sigNo){
+ switch(sigNo){
+ case NDBCOND_WAIT:
+ return "NDBCOND_WAIT";
+ break;
+ case NDBCOND_WAITTIMEOUT:
+ return "NDBCOND_WAITTIMEOUT";
+ break;
+ case NDBCOND_SIGNAL:
+ return "NDBCOND_SIGNAL";
+ break;
+ case NDBCOND_BROADCAST:
+ return "NDBCOND_BROADCAST";
+ break;
+ }
+ return "UNKNOWN";
+}
+
+struct NdbCondWait
+{
+ SIGSELECT sigNo;
+ int status;
+};
+
+/**
+ * Signal received
+ */
+#define NDBCOND_SIGNALED 1
+
+/**
+ * Timeout occured
+ */
+#define NDBCOND_TIMEOUT 2
+
+struct NdbCondWaitTimeout
+{
+ SIGSELECT sigNo;
+ int timeout;
+ int status;
+
+};
+
+struct NdbCondSignal
+{
+ SIGSELECT sigNo;
+};
+
+struct NdbCondBroadcast
+{
+ SIGSELECT sigNo;
+};
+
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct NdbCondWait condWait;
+ struct NdbCondWaitTimeout condWaitTimeout;
+ struct NdbCondSignal condSignal;
+ struct NdbCondBroadcast condBroadcast;
+};
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbEnv.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbEnv.c
new file mode 100644
index 00000000000..e2ac4d879d2
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbEnv.c
@@ -0,0 +1,55 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbEnv.h"
+#include <string.h>
+#include <stdlib.h>
+
+const char* NdbEnv_GetEnv(const char* name, char * buf, int buflen)
+{
+ /**
+ * All environment variables are associated with a process
+ * it's important to read env from the correct process
+ * for now read from own process, own block and last the "ose_shell" process.
+ *
+ * TODO! What process should this be read from in the future?
+ *
+ */
+ PROCESS proc_;
+ char* p = NULL;
+ /* Look in own process */
+ p = get_env(current_process(), (char*)name);
+ if (p == NULL){
+ /* Look in block process */
+ p = get_env(get_bid(current_process()), (char*)name);
+ if (p == NULL){
+ /* Look in ose_shell process */
+ if (hunt("ose_shell", 0, &proc_, NULL)){
+ p = get_env(proc_, (char*)name);
+ }
+ }
+ }
+
+ if (p != NULL){
+ strncpy(buf, p, buflen);
+ buf[buflen-1] = 0;
+ free_buf((union SIGNAL **)&p);
+ p = buf;
+ }
+ return p;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbHost.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbHost.c
new file mode 100644
index 00000000000..f5e1e511c16
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbHost.c
@@ -0,0 +1,55 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbHost.h"
+#include <unistd.h>
+
+
+#include <inet.sig>
+#include <string.h>
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct InetIfUp inetIfUp;
+};
+
+int NdbHost_GetHostName(char* buf)
+{
+#if 0
+ extern PROCESS ose_inet_;
+ union SIGNAL *signal;
+ static const SIGSELECT select_if_up_reply[] = { 1, INET_IF_UP_REPLY };
+
+ signal = alloc(sizeof(struct InetIfUp), INET_IF_UP_REQUEST);
+ strcpy(signal->inetIfUp.ifName, "*");
+ send((union SIGNAL **)&signal, ose_inet_);
+ signal = receive((SIGSELECT *)select_if_up_reply);
+ strcpy(buf, signal->inetIfUp.ifName);
+ free_buf(&signal);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+
+int NdbHost_GetProcessId(void)
+{
+ return current_process();
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbMem.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbMem.c
new file mode 100644
index 00000000000..0e38024bbb4
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbMem.c
@@ -0,0 +1,181 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbMem.h"
+
+
+#if defined NDB_OSE
+#include <ose.h>
+#include <mms.sig>
+#include <mms_err.h>
+#include <string.h>
+#include <stdio.h>
+#include <NdbOut.hpp>
+
+// Page size for mp750 is 4096 bytes.
+#define PAGE_SIZE 4096
+
+/**
+ * NOTE: To use NdbMem from a OSE system ose_mms has to be defined
+ * as a "Required External Process"(see OSE Kernel User's Guide/R1.1(p. 148)),
+ * like this in osemain.con:
+ * EXT_PROC(ose_mms, ose_mms, 50000)
+ * This will create a global variable ose_mms_ that is used from here.
+ */
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct MmsAllocateRegionRequest mmsAllocateRegionRequest;
+ struct MmsAllocateRegionReply mmsAllocateRegionReply;
+ struct MmsFreeRegionRequest mmsFreeRegionRequest;
+ struct MmsFreeRegionReply mmsFreeRegionReply;
+}; /* union SIGNAL */
+
+extern PROCESS ose_mms_;
+
+void NdbMem_Create(void)
+{
+ /* Do nothing */
+ return;
+}
+
+void NdbMem_Destroy(void)
+{
+ /* Do nothing */
+ return;
+}
+
+void* NdbMem_Allocate(size_t size)
+{
+ static SIGSELECT allocate_sig[] = {1,MMS_ALLOCATE_REGION_REPLY};
+ union SIGNAL *sig;
+ U32 allocatedAdress;
+
+ assert(size > 0);
+
+ // Only allowed to allocate multiples of the page size.
+ if(size % PAGE_SIZE != 0) {
+ size += PAGE_SIZE - size%PAGE_SIZE;
+ }
+
+ /* Allocate a new region in the callers memory segment. */
+ sig = alloc(sizeof(struct MmsAllocateRegionRequest),
+ MMS_ALLOCATE_REGION_REQUEST);
+ /* -1: The callers domain is used */
+ sig->mmsAllocateRegionRequest.domain = (MemoryDomain)-1;
+ sig->mmsAllocateRegionRequest.useAddr = False;
+ sig->mmsAllocateRegionRequest.size = size;
+ sig->mmsAllocateRegionRequest.access = SuperRW_UserRW;
+ sig->mmsAllocateRegionRequest.resident = False;
+ sig->mmsAllocateRegionRequest.memory = CodeData;
+ sig->mmsAllocateRegionRequest.cache = CopyBack;
+ strcpy(sig->mmsAllocateRegionRequest.name, "NDB_DATA");
+ send(&sig, ose_mms_);
+ sig = receive(allocate_sig);
+
+ if (sig->mmsAllocateRegionReply.status != MMS_SUCCESS){
+ /* Memory allocation failed, make sure this function returns NULL */
+ allocatedAdress = NULL;
+ }
+ else{
+ allocatedAdress = sig->mmsAllocateRegionReply.address;
+ }
+ free_buf(&sig);
+ return (void*)allocatedAdress;
+}
+
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ return NdbMem_Allocate(size);
+}
+
+
+void NdbMem_Free(void* ptr)
+{
+ static SIGSELECT free_sig[] = {1,MMS_FREE_REGION_REPLY};
+ union SIGNAL *sig;
+
+ /* Free a region in the callers domain. */
+ sig = alloc(sizeof(struct MmsFreeRegionRequest),
+ MMS_FREE_REGION_REQUEST);
+ sig->mmsFreeRegionRequest.address = (U32)ptr;
+ send(&sig, ose_mms_);
+ sig = receive(free_sig);
+
+ if (sig->mmsFreeRegionReply.status != MMS_SUCCESS){
+ ndbout_c("The MMS Region could not be deallocated.\r\n");
+ error(sig->mmsFreeRegionReply.status);
+ };
+ free_buf(&sig);
+}
+
+int NdbMem_MemLockAll(){
+ return -1;
+}
+
+int NdbMem_MemUnlockAll(){
+ return -1;
+}
+
+#else
+#include <stdlib.h>
+
+
+void NdbMem_Create()
+{
+ /* Do nothing */
+ return;
+}
+
+void NdbMem_Destroy()
+{
+ /* Do nothing */
+ return;
+}
+
+void* NdbMem_Allocate(size_t size)
+{
+ assert(size > 0);
+ return (void*)malloc(size);
+}
+
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ /*
+ return (void*)memalign(alignment, size);
+ TEMP fix
+ */
+ return (void*)malloc(size);
+}
+
+
+void NdbMem_Free(void* ptr)
+{
+ free(ptr);
+}
+
+
+int NdbMem_MemLockAll(){
+ return -1;
+}
+
+int NdbMem_MemUnlockAll(){
+ return -1;
+}
+
+#endif
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbMem_SoftOse.cpp b/storage/ndb/src/common/portlib/old_dirs/ose/NdbMem_SoftOse.cpp
new file mode 100644
index 00000000000..cad22c0474b
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbMem_SoftOse.cpp
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "NdbMem.h"
+
+extern "C"
+void NdbMem_Create()
+{
+}
+extern "C"
+void NdbMem_Destroy()
+{
+}
+
+extern "C"
+void* NdbMem_Allocate(size_t size)
+{
+ return new char[size];
+}
+
+extern "C"
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ return NdbMem_Allocate(size);
+}
+
+extern "C"
+void NdbMem_Free(void* ptr)
+{
+ delete [] (char *)(ptr);
+}
+
+int NdbMem_MemLockAll(){
+ return -1;
+}
+
+int NdbMem_MemUnlockAll(){
+ return -1;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbMutex.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbMutex.c
new file mode 100644
index 00000000000..253c0e412ff
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbMutex.c
@@ -0,0 +1,85 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbMutex.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+
+NdbMutex* NdbMutex_Create(void)
+{
+ NdbMutex* pNdbMutex;
+
+ pNdbMutex = create_sem(1);
+
+ return pNdbMutex;
+}
+
+
+int NdbMutex_Destroy(NdbMutex* p_mutex)
+{
+
+ if (p_mutex == NULL)
+ return -1;
+
+ kill_sem(p_mutex);
+
+ return 0;
+
+}
+
+
+int NdbMutex_Lock(NdbMutex* p_mutex)
+{
+ if (p_mutex == NULL)
+ return -1;
+
+ wait_sem(p_mutex);
+
+ return 0;
+}
+
+
+int NdbMutex_Unlock(NdbMutex* p_mutex)
+{
+
+ if (p_mutex == NULL)
+ return -1;
+
+ signal_sem(p_mutex);
+
+ return 0;
+}
+
+
+int NdbMutex_Trylock(NdbMutex* p_mutex)
+{
+ int result = -1;
+
+ if (p_mutex != NULL) {
+ OSSEMVAL semvalue = get_sem(p_mutex);
+ if (semvalue > 0) {
+ wait_sem(p_mutex);
+ result = 0;
+ }
+ }
+
+ return result;
+
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbOut.cpp b/storage/ndb/src/common/portlib/old_dirs/ose/NdbOut.cpp
new file mode 100644
index 00000000000..eb81bc9d971
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbOut.cpp
@@ -0,0 +1,96 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "NdbOut.hpp"
+
+#if defined NDB_SOFTOSE
+#include <dbgprintf.h>
+#define printfunc dbgprintf
+#else
+#define printfunc printf
+#endif
+
+static char const* const endlineString = "\r\n";
+
+static int CtrlC = 0;
+NdbOut ndbout;
+
+
+NdbOut& NdbOut::operator<<(int aVal)
+{
+ char* format;
+ char HexFormat[] = "0x%08x";
+ char DecFormat[] = "%d";
+ if (isHexFormat == 1)
+ format = HexFormat;
+ else
+ format = DecFormat;
+
+ printfunc(format, aVal);
+ return *this;
+}
+
+NdbOut& NdbOut::operator<<(char* pVal)
+{
+ printfunc("%s", pVal);
+ return *this;
+}
+
+NdbOut& NdbOut::endline()
+{
+ isHexFormat = 0; // Reset hex to normal, if user forgot this
+ printfunc(endlineString);
+ return *this;
+}
+
+NdbOut& NdbOut::flushline()
+{
+ isHexFormat = 0; // Reset hex to normal, if user forgot this
+ return *this;
+}
+
+NdbOut& NdbOut::setHexFormat(int _format)
+{
+ isHexFormat = _format;
+ return *this;
+}
+
+NdbOut::NdbOut()
+{
+ CtrlC = 0;
+ isHexFormat = 0;
+}
+
+NdbOut::~NdbOut()
+{
+}
+
+
+
+extern "C"
+void
+ndbout_c(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf << endl;
+ va_end(ap);
+}
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbSleep.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbSleep.c
new file mode 100644
index 00000000000..70fd83117ef
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbSleep.c
@@ -0,0 +1,36 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbSleep.h"
+
+#include <ose.h>
+
+
+int
+NdbSleep_MilliSleep(int milliseconds){
+ const OSTIME millisecond_delay = milliseconds;
+ delay(millisecond_delay);
+ return 0;
+}
+
+int
+NdbSleep_SecSleep(int seconds){
+ const OSTIME millisecond_delay = seconds*1000;
+ delay(millisecond_delay);
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbTCP.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbTCP.c
new file mode 100644
index 00000000000..9994697b3f8
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbTCP.c
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbTCP.h"
+
+
+int
+Ndb_getInAddr(struct in_addr * dst, const char *address) {
+ struct hostent * host;
+ host = gethostbyname_r(address);
+ if(host != 0){
+ dst->s_addr = ((struct in_addr *) *host->h_addr_list)->s_addr;
+ free_buf((union SIGNAL **)&host);
+ return 0;
+ }
+ /* Try it as aaa.bbb.ccc.ddd. */
+ dst->s_addr = inet_addr(address);
+ if (dst->s_addr != INADDR_NONE) {
+ return 0;
+ }
+ return -1;
+}
+
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbThread.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbThread.c
new file mode 100644
index 00000000000..e46903a5cce
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbThread.c
@@ -0,0 +1,183 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbThread.h"
+#include <pthread.h>
+#include <malloc.h>
+#include <string.h>
+#include <NdbOut.hpp>
+
+#define MAX_THREAD_NAME 16
+
+
+struct NdbThread
+{
+ PROCESS pid;
+ char thread_name[MAX_THREAD_NAME];
+};
+
+#define NDBTHREAD_SIGBASE 4010
+
+#define NDBTHREAD_START (NDBTHREAD_SIGBASE + 1) /* !-SIGNO(struct NdbThreadStart)-! */
+
+struct NdbThreadStart
+{
+ SIGSELECT sigNo;
+ NDB_THREAD_FUNC* func;
+ NDB_THREAD_ARG arg;
+};
+
+struct NdbThreadStopped
+{
+ SIGSELECT sigNo;
+};
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct NdbThreadStart threadStart;
+ struct NdbThreadStopped threadStopped;
+};
+
+OS_PROCESS(thread_starter){
+ static const SIGSELECT sel_start[] = {1, NDBTHREAD_START};
+ struct NdbThreadStart* sigstart;
+ union SIGNAL* sig;
+
+ /* Receive function adress and params */
+ sig = receive((SIGSELECT*)sel_start);
+ if (sig != NIL){
+ if (sig->sigNo == NDBTHREAD_START){
+ sigstart = ((struct NdbThreadStart*)sig);
+ /* Execute function with arg */
+ (*sigstart->func)(sigstart->arg);
+ }else{
+ assert(1==0);
+ }
+ free_buf(&sig);
+ }
+}
+
+struct NdbThread* NdbThread_Create(NDB_THREAD_FUNC* p_thread_func,
+ NDB_THREAD_ARG *p_thread_arg,
+ const NDB_THREAD_STACKSIZE thread_stack_size,
+ const char* p_thread_name,
+ NDB_THREAD_PRIO thread_prio)
+{
+ struct NdbThread* tmpThread;
+ union SIGNAL* sig;
+ int ose_prio;
+
+ if (p_thread_func == NULL)
+ return 0;
+
+ tmpThread = (struct NdbThread*)malloc(sizeof(struct NdbThread));
+ if (tmpThread == NULL)
+ return NULL;
+
+ strncpy((char*)&tmpThread->thread_name, p_thread_name, MAX_THREAD_NAME);
+
+ switch(thread_prio){
+ case NDB_THREAD_PRIO_HIGHEST:
+ ose_prio = 1;
+ break;
+ case NDB_THREAD_PRIO_HIGH:
+ ose_prio = 10;
+ break;
+ case NDB_THREAD_PRIO_MEAN:
+ ose_prio = 16;
+ break;
+ case NDB_THREAD_PRIO_LOW:
+ ose_prio = 23;
+ break;
+ case NDB_THREAD_PRIO_LOWEST:
+ ose_prio = 31;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ /* Create process */
+ tmpThread->pid = create_process(OS_PRI_PROC, /* Process type */
+ (char*)p_thread_name, /* Name */
+ thread_starter, /* Entry point */
+ thread_stack_size, /* Stack size */
+ ose_prio, /* Priority */
+ 0, /* Time slice */
+ get_bid(current_process()), /* Block */
+ NULL, /* Redir table */
+ 0,
+ 0);
+
+ /* Send params to process */
+ sig = alloc(sizeof(struct NdbThreadStart), NDBTHREAD_START);
+ ((struct NdbThreadStart*)sig)->func = p_thread_func;
+ ((struct NdbThreadStart*)sig)->arg = p_thread_arg;
+ send(&sig, tmpThread->pid);
+
+ /* Enable NDB_HOME environment variable for the thread */
+ {
+ /* Hardcoded NDB_HOME...*/
+ char* ndb_home_env = get_env(current_process(), "NDB_HOME");
+ if (ndb_home_env != NULL)
+ {
+ /* Set NDB_HOME */
+ int rc = set_env(tmpThread->pid, "NDB_HOME", ndb_home_env);
+ if (rc != 0)
+ {
+ /* Not really a problem */
+ }
+ } /* Enable NDB_HOME */
+ }
+
+ /* Start process */
+ start(tmpThread->pid);
+
+ return tmpThread;
+}
+
+
+
+void NdbThread_Destroy(struct NdbThread** p_thread)
+{
+ free(* p_thread); * p_thread = 0;
+}
+
+
+int NdbThread_WaitFor(struct NdbThread* p_wait_thread, void** status)
+{
+ while(hunt(p_wait_thread->thread_name, 0, NULL, NULL) != 0)
+ delay(1000);
+
+ * status = 0;
+
+ return 0;
+}
+
+
+void NdbThread_Exit(int a)
+{
+ kill_proc(current_process());
+}
+
+
+int NdbThread_SetConcurrencyLevel(int level)
+{
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/ose/NdbTick.c b/storage/ndb/src/common/portlib/old_dirs/ose/NdbTick.c
new file mode 100644
index 00000000000..c3deae2bec3
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/ose/NdbTick.c
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbTick.h"
+#include <time.h>
+
+#define NANOSEC_PER_SEC 1000000000
+#define MICROSEC_PER_SEC 1000000
+#define MILLISEC_PER_SEC 1000
+#define MICROSEC_PER_MILLISEC 1000
+#define MILLISEC_PER_NANOSEC 1000000
+
+#ifdef NDB_OSE
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ return get_ticks()*4;
+}
+#include <rtc.h>
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros){
+ struct TimePair tvp;
+ rtc_get_time(&tvp);
+ * secs = tvp.seconds;
+ * micros = tvp.micros;
+ return 0;
+}
+
+#endif
+
+#if defined NDB_SOFTOSE
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ /**
+ * Depends on the interval counter in solaris
+ * that means each "tick" in OSE is really 10 milliseconds
+ */
+ return get_ticks()*10;
+}
+
+#include <rtc.h>
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros){
+ struct TimePair tvp;
+ rtc_get_time(&tvp);
+ * secs = tvp.seconds;
+ * micros = tvp.micros;
+ return 0;
+}
+#endif
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/test/Makefile b/storage/ndb/src/common/portlib/old_dirs/test/Makefile
new file mode 100644
index 00000000000..4edc98ede75
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/test/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := PortLibTest
+BIN_TARGET_ARCHIVES := portlib general
+
+SOURCES = NdbPortLibTest.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/Makefile b/storage/ndb/src/common/portlib/old_dirs/win32/Makefile
new file mode 100644
index 00000000000..bb29ac5547e
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/Makefile
@@ -0,0 +1,30 @@
+include .defs.mk
+
+TYPE := util
+
+PIC_ARCHIVE := Y
+ARCHIVE_TARGET := portlib
+
+SOURCES.c = NdbCondition.c \
+ NdbMutex.c \
+ NdbSleep.c \
+ NdbTick.c \
+ NdbEnv.c \
+ NdbThread.c \
+ NdbHost.c \
+ NdbTCP.c \
+ NdbDaemon.c
+
+ifeq ($(NDB_OS), SOFTOSE)
+ SOURCES += NdbMem_SoftOse.cpp
+else
+ SOURCES.c += NdbMem.c
+endif
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbCondition.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbCondition.c
new file mode 100644
index 00000000000..77869b673de
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbCondition.c
@@ -0,0 +1,183 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <sys/types.h>
+
+#include "NdbCondition.h"
+#include <NdbMutex.h>
+
+
+struct NdbCondition
+{
+ long nWaiters;
+ NdbMutex* pNdbMutexWaitersLock;
+ HANDLE hSemaphore;
+ HANDLE hEventWaitersDone;
+ int bWasBroadcast;
+};
+
+
+struct NdbCondition*
+NdbCondition_Create(void)
+{
+ int result = 0;
+ struct NdbCondition* pNdbCondition = (struct NdbCondition*)malloc(sizeof(struct NdbCondition));
+ if(!pNdbCondition)
+ return 0;
+
+ pNdbCondition->nWaiters = 0;
+ pNdbCondition->bWasBroadcast = 0;
+ if(!(pNdbCondition->hSemaphore = CreateSemaphore(0, 0, MAXLONG, 0)))
+ result = -1;
+ else if(!(pNdbCondition->pNdbMutexWaitersLock = NdbMutex_Create()))
+ result = -1;
+ else if(!(pNdbCondition->hEventWaitersDone = CreateEvent(0, 0, 0, 0)))
+ result = -1;
+
+ assert(!result);
+ return pNdbCondition;
+}
+
+
+int
+NdbCondition_Wait(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex)
+{
+ int result;
+ int bLastWaiter;
+ if(!p_cond || !p_mutex)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters++;
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(NdbMutex_Unlock(p_mutex))
+ return -1;
+ result = WaitForSingleObject (p_cond->hSemaphore, INFINITE);
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters--;
+ bLastWaiter = (p_cond->bWasBroadcast && p_cond->nWaiters==0);
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(result==WAIT_OBJECT_0 && bLastWaiter)
+ SetEvent(p_cond->hEventWaitersDone);
+
+ NdbMutex_Lock(p_mutex);
+ return result;
+}
+
+
+int
+NdbCondition_WaitTimeout(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex,
+ int msecs)
+{
+ int result;
+ int bLastWaiter;
+ if (!p_cond || !p_mutex)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters++;
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+ if(msecs<0)
+ msecs = 0;
+
+ if(NdbMutex_Unlock(p_mutex))
+ return -1;
+ result = WaitForSingleObject(p_cond->hSemaphore, msecs);
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters--;
+ bLastWaiter = (p_cond->bWasBroadcast && p_cond->nWaiters==0);
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(result!=WAIT_OBJECT_0)
+ result = -1;
+
+ if(bLastWaiter)
+ SetEvent(p_cond->hEventWaitersDone);
+
+ NdbMutex_Lock(p_mutex);
+ return result;
+}
+
+
+int
+NdbCondition_Signal(struct NdbCondition* p_cond)
+{
+ int bHaveWaiters;
+ if(!p_cond)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ bHaveWaiters = (p_cond->nWaiters > 0);
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(bHaveWaiters)
+ return (ReleaseSemaphore(p_cond->hSemaphore, 1, 0) ? 0 : -1);
+ else
+ return 0;
+}
+
+
+int NdbCondition_Broadcast(struct NdbCondition* p_cond)
+{
+ int bHaveWaiters;
+ int result = 0;
+ if(!p_cond)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ bHaveWaiters = 0;
+ if(p_cond->nWaiters > 0)
+ {
+ p_cond->bWasBroadcast = !0;
+ bHaveWaiters = 1;
+ }
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+ if(bHaveWaiters)
+ {
+ if(!ReleaseSemaphore(p_cond->hSemaphore, p_cond->nWaiters, 0))
+ result = -1;
+ else if(WaitForSingleObject (p_cond->hEventWaitersDone, INFINITE) != WAIT_OBJECT_0)
+ result = -1;
+ p_cond->bWasBroadcast = 0;
+ }
+ return result;
+}
+
+
+int NdbCondition_Destroy(struct NdbCondition* p_cond)
+{
+ int result;
+ if(!p_cond)
+ return 1;
+
+ CloseHandle(p_cond->hEventWaitersDone);
+ NdbMutex_Destroy(p_cond->pNdbMutexWaitersLock);
+ result = (CloseHandle(p_cond->hSemaphore) ? 0 : -1);
+
+ free(p_cond);
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbDaemon.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbDaemon.c
new file mode 100644
index 00000000000..972fb1b88d8
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbDaemon.c
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbDaemon.h"
+
+#define NdbDaemon_ErrorSize 500
+long NdbDaemon_DaemonPid;
+int NdbDaemon_ErrorCode;
+char NdbDaemon_ErrorText[NdbDaemon_ErrorSize];
+
+int
+NdbDaemon_Make(const char* lockfile, const char* logfile, unsigned flags)
+{
+ /* Fail */
+ snprintf(NdbDaemon_ErrorText, NdbDaemon_ErrorSize,
+ "Daemon mode not implemented");
+ return -1;
+}
+
+#ifdef NDB_DAEMON_TEST
+
+int
+main()
+{
+ if (NdbDaemon_Make("test.pid", "test.log", 0) == -1) {
+ fprintf(stderr, "NdbDaemon_Make: %s\n", NdbDaemon_ErrorText);
+ return 1;
+ }
+ sleep(10);
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbEnv.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbEnv.c
new file mode 100644
index 00000000000..0df703a5e97
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbEnv.c
@@ -0,0 +1,33 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbEnv.h"
+#include <string.h>
+#include <stdlib.h>
+
+const char* NdbEnv_GetEnv(const char* name, char * buf, int buflen)
+{
+ char* p = NULL;
+ p = getenv(name);
+
+ if (p != NULL && buf != NULL){
+ strncpy(buf, p, buflen);
+ buf[buflen-1] = 0;
+ }
+ return p;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbHost.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbHost.c
new file mode 100644
index 00000000000..f91dd1a531c
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbHost.c
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbHost.h"
+#include <windows.h>
+#include <process.h>
+
+
+int NdbHost_GetHostName(char* buf)
+{
+ /* We must initialize TCP/IP if we want to call gethostname */
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD( 2, 0 );
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if ( err != 0 ) {
+ /**
+ * Tell the user that we couldn't find a usable
+ * WinSock DLL.
+ */
+ return -1;
+ }
+
+ /* Get host name */
+ if(gethostname(buf, MAXHOSTNAMELEN))
+ {
+ return -1;
+ }
+ return 0;
+}
+
+
+int NdbHost_GetProcessId(void)
+{
+ return _getpid();
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbMem.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbMem.c
new file mode 100644
index 00000000000..ab7123b0a29
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbMem.c
@@ -0,0 +1,235 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <windows.h>
+
+#include "NdbMem.h"
+
+
+struct AWEINFO
+{
+ SIZE_T dwSizeInBytesRequested;
+ ULONG_PTR nNumberOfPagesRequested;
+ ULONG_PTR nNumberOfPagesActual;
+ ULONG_PTR nNumberOfPagesFreed;
+ ULONG_PTR* pnPhysicalMemoryPageArray;
+ void* pRegionReserved;
+};
+
+const size_t cNdbMem_nMaxAWEinfo = 256;
+size_t gNdbMem_nAWEinfo = 0;
+
+struct AWEINFO* gNdbMem_pAWEinfo = 0;
+
+
+void ShowLastError(const char* szContext, const char* szFunction)
+{
+ DWORD dwError = GetLastError();
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ dwError,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR)&lpMsgBuf,
+ 0,
+ NULL
+ );
+ printf("%s : %s failed : %lu : %s\n", szContext, szFunction, dwError, (char*)lpMsgBuf);
+ LocalFree(lpMsgBuf);
+}
+
+
+
+void NdbMem_Create()
+{
+ // Address Windowing Extensions
+ struct PRIVINFO
+ {
+ DWORD Count;
+ LUID_AND_ATTRIBUTES Privilege[1];
+ } Info;
+
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE hToken;
+ if(!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ ShowLastError("NdbMem_Create", "OpenProcessToken");
+ }
+
+ Info.Count = 1;
+ Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if(!LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &(Info.Privilege[0].Luid)))
+ {
+ ShowLastError("NdbMem_Create", "LookupPrivilegeValue");
+ }
+
+ if(!AdjustTokenPrivileges(hToken, FALSE, (PTOKEN_PRIVILEGES)&Info, 0, 0, 0))
+ {
+ ShowLastError("NdbMem_Create", "AdjustTokenPrivileges");
+ }
+
+ if(!CloseHandle(hToken))
+ {
+ ShowLastError("NdbMem_Create", "CloseHandle");
+ }
+
+ return;
+}
+
+void NdbMem_Destroy()
+{
+ /* Do nothing */
+ return;
+}
+
+void* NdbMem_Allocate(size_t size)
+{
+ // Address Windowing Extensions
+ struct AWEINFO* pAWEinfo;
+ HANDLE hProcess;
+ SYSTEM_INFO sysinfo;
+
+ if(!gNdbMem_pAWEinfo)
+ {
+ gNdbMem_pAWEinfo = VirtualAlloc(0,
+ sizeof(struct AWEINFO)*cNdbMem_nMaxAWEinfo,
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ }
+
+ assert(gNdbMem_nAWEinfo < cNdbMem_nMaxAWEinfo);
+ pAWEinfo = gNdbMem_pAWEinfo+gNdbMem_nAWEinfo++;
+
+ hProcess = GetCurrentProcess();
+ GetSystemInfo(&sysinfo);
+ pAWEinfo->nNumberOfPagesRequested = (size+sysinfo.dwPageSize-1)/sysinfo.dwPageSize;
+ pAWEinfo->pnPhysicalMemoryPageArray = VirtualAlloc(0,
+ sizeof(ULONG_PTR)*pAWEinfo->nNumberOfPagesRequested,
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ pAWEinfo->nNumberOfPagesActual = pAWEinfo->nNumberOfPagesRequested;
+ if(!AllocateUserPhysicalPages(hProcess, &(pAWEinfo->nNumberOfPagesActual), pAWEinfo->pnPhysicalMemoryPageArray))
+ {
+ ShowLastError("NdbMem_Allocate", "AllocateUserPhysicalPages");
+ return 0;
+ }
+ if(pAWEinfo->nNumberOfPagesRequested != pAWEinfo->nNumberOfPagesActual)
+ {
+ ShowLastError("NdbMem_Allocate", "nNumberOfPagesRequested != nNumberOfPagesActual");
+ return 0;
+ }
+
+ pAWEinfo->dwSizeInBytesRequested = size;
+ pAWEinfo->pRegionReserved = VirtualAlloc(0, pAWEinfo->dwSizeInBytesRequested, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE);
+ if(!pAWEinfo->pRegionReserved)
+ {
+ ShowLastError("NdbMem_Allocate", "VirtualAlloc");
+ return 0;
+ }
+
+ if(!MapUserPhysicalPages(pAWEinfo->pRegionReserved, pAWEinfo->nNumberOfPagesActual, pAWEinfo->pnPhysicalMemoryPageArray))
+ {
+ ShowLastError("NdbMem_Allocate", "MapUserPhysicalPages");
+ return 0;
+ }
+
+ /*
+ printf("allocate AWE memory: %lu bytes, %lu pages, address %lx\n",
+ pAWEinfo->dwSizeInBytesRequested,
+ pAWEinfo->nNumberOfPagesActual,
+ pAWEinfo->pRegionReserved);
+ */
+ return pAWEinfo->pRegionReserved;
+}
+
+
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ /*
+ return (void*)memalign(alignment, size);
+ TEMP fix
+ */
+ return NdbMem_Allocate(size);
+}
+
+
+void NdbMem_Free(void* ptr)
+{
+ // VirtualFree(ptr, 0, MEM_DECOMMIT|MEM_RELEASE);
+
+ // Address Windowing Extensions
+ struct AWEINFO* pAWEinfo = 0;
+ size_t i;
+ HANDLE hProcess;
+
+ for(i=0; i<gNdbMem_nAWEinfo; ++i)
+ {
+ if(ptr==gNdbMem_pAWEinfo[i].pRegionReserved)
+ {
+ pAWEinfo = gNdbMem_pAWEinfo+i;
+ }
+ }
+ if(!pAWEinfo)
+ {
+ ShowLastError("NdbMem_Free", "ptr is not AWE memory");
+ }
+
+ hProcess = GetCurrentProcess();
+ if(!MapUserPhysicalPages(ptr, pAWEinfo->nNumberOfPagesActual, 0))
+ {
+ ShowLastError("NdbMem_Free", "MapUserPhysicalPages");
+ }
+
+ if(!VirtualFree(ptr, 0, MEM_RELEASE))
+ {
+ ShowLastError("NdbMem_Free", "VirtualFree");
+ }
+
+ pAWEinfo->nNumberOfPagesFreed = pAWEinfo->nNumberOfPagesActual;
+ if(!FreeUserPhysicalPages(hProcess, &(pAWEinfo->nNumberOfPagesFreed), pAWEinfo->pnPhysicalMemoryPageArray))
+ {
+ ShowLastError("NdbMem_Free", "FreeUserPhysicalPages");
+ }
+
+ VirtualFree(pAWEinfo->pnPhysicalMemoryPageArray, 0, MEM_DECOMMIT|MEM_RELEASE);
+}
+
+
+int NdbMem_MemLockAll()
+{
+ /*
+ HANDLE hProcess = GetCurrentProcess();
+ SIZE_T nMinimumWorkingSetSize;
+ SIZE_T nMaximumWorkingSetSize;
+ GetProcessWorkingSetSize(hProcess, &nMinimumWorkingSetSize, &nMaximumWorkingSetSize);
+ ndbout << "nMinimumWorkingSetSize=" << nMinimumWorkingSetSize << ", nMaximumWorkingSetSize=" << nMaximumWorkingSetSize << endl;
+
+ SetProcessWorkingSetSize(hProcess, 50000000, 100000000);
+
+ GetProcessWorkingSetSize(hProcess, &nMinimumWorkingSetSize, &nMaximumWorkingSetSize);
+ ndbout << "nMinimumWorkingSetSize=" << nMinimumWorkingSetSize << ", nMaximumWorkingSetSize=" << nMaximumWorkingSetSize << endl;
+ */
+ return -1;
+}
+
+int NdbMem_MemUnlockAll()
+{
+ //VirtualUnlock();
+ return -1;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbMutex.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbMutex.c
new file mode 100644
index 00000000000..e797024d5bb
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbMutex.c
@@ -0,0 +1,77 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <time.h>
+
+#include "NdbMutex.h"
+
+
+NdbMutex* NdbMutex_Create(void)
+{
+ NdbMutex* pNdbMutex = (NdbMutex*)malloc(sizeof(NdbMutex));
+ if(!pNdbMutex)
+ return 0;
+
+ InitializeCriticalSection(pNdbMutex);
+ return pNdbMutex;
+}
+
+
+int NdbMutex_Destroy(NdbMutex* p_mutex)
+{
+ if(!p_mutex)
+ return -1;
+
+ DeleteCriticalSection(p_mutex);
+ free(p_mutex);
+ return 0;
+}
+
+
+int NdbMutex_Lock(NdbMutex* p_mutex)
+{
+ if(!p_mutex)
+ return -1;
+
+ EnterCriticalSection (p_mutex);
+ return 0;
+}
+
+
+int NdbMutex_Unlock(NdbMutex* p_mutex)
+{
+ if(!p_mutex)
+ return -1;
+
+ LeaveCriticalSection(p_mutex);
+ return 0;
+}
+
+
+int NdbMutex_Trylock(NdbMutex* p_mutex)
+{
+ int result = -1;
+ if(p_mutex)
+ {
+ result = (TryEnterCriticalSection(p_mutex) ? 0 : -1);
+ }
+ return result;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbSleep.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbSleep.c
new file mode 100644
index 00000000000..ac0f44dd07f
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbSleep.c
@@ -0,0 +1,35 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbSleep.h"
+
+#include <windows.h>
+
+
+int
+NdbSleep_MilliSleep(int milliseconds)
+{
+ Sleep(milliseconds);
+ return 0;
+}
+
+int
+NdbSleep_SecSleep(int seconds)
+{
+ return NdbSleep_MilliSleep(seconds*1000);
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbTCP.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbTCP.c
new file mode 100644
index 00000000000..483a53bd606
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbTCP.c
@@ -0,0 +1,39 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "NdbTCP.h"
+
+int
+Ndb_getInAddr(struct in_addr * dst, const char *address)
+{
+ struct hostent * hostPtr;
+
+ /* Try it as aaa.bbb.ccc.ddd. */
+ dst->s_addr = inet_addr(address);
+ if (dst->s_addr != -1) {
+ return 0;
+ }
+
+ hostPtr = gethostbyname(address);
+ if (hostPtr != NULL) {
+ dst->s_addr = ((struct in_addr *) *hostPtr->h_addr_list)->s_addr;
+ return 0;
+ }
+
+ return -1;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbThread.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbThread.c
new file mode 100644
index 00000000000..1f052f034e8
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbThread.c
@@ -0,0 +1,117 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <windows.h>
+#include <process.h>
+
+#include "NdbThread.h"
+
+
+#define MAX_THREAD_NAME 16
+
+typedef unsigned (WINAPI* NDB_WIN32_THREAD_FUNC)(void*);
+
+
+struct NdbThread
+{
+ HANDLE hThread;
+ unsigned nThreadId;
+ char thread_name[MAX_THREAD_NAME];
+};
+
+
+struct NdbThread* NdbThread_Create(NDB_THREAD_FUNC *p_thread_func,
+ NDB_THREAD_ARG *p_thread_arg,
+ const NDB_THREAD_STACKSIZE thread_stack_size,
+ const char* p_thread_name,
+ NDB_THREAD_PRIO thread_prio)
+{
+ struct NdbThread* tmpThread;
+ unsigned initflag;
+ int nPriority = 0;
+
+ if(!p_thread_func)
+ return 0;
+
+ tmpThread = (struct NdbThread*)malloc(sizeof(struct NdbThread));
+ if(!tmpThread)
+ return 0;
+
+ strncpy((char*)&tmpThread->thread_name, p_thread_name, MAX_THREAD_NAME);
+
+ switch(thread_prio)
+ {
+ case NDB_THREAD_PRIO_HIGHEST: nPriority=THREAD_PRIORITY_HIGHEST; break;
+ case NDB_THREAD_PRIO_HIGH: nPriority=THREAD_PRIORITY_ABOVE_NORMAL; break;
+ case NDB_THREAD_PRIO_MEAN: nPriority=THREAD_PRIORITY_NORMAL; break;
+ case NDB_THREAD_PRIO_LOW: nPriority=THREAD_PRIORITY_BELOW_NORMAL; break;
+ case NDB_THREAD_PRIO_LOWEST: nPriority=THREAD_PRIORITY_LOWEST; break;
+ }
+ initflag = (nPriority ? CREATE_SUSPENDED : 0);
+
+ tmpThread->hThread = (HANDLE)_beginthreadex(0, thread_stack_size,
+ (NDB_WIN32_THREAD_FUNC)p_thread_func, p_thread_arg,
+ initflag, &tmpThread->nThreadId);
+
+ if(nPriority && tmpThread->hThread)
+ {
+ SetThreadPriority(tmpThread->hThread, nPriority);
+ ResumeThread (tmpThread->hThread);
+ }
+
+ assert(tmpThread->hThread);
+ return tmpThread;
+}
+
+
+void NdbThread_Destroy(struct NdbThread** p_thread)
+{
+ CloseHandle((*p_thread)->hThread);
+ (*p_thread)->hThread = 0;
+ free(*p_thread);
+ *p_thread = 0;
+}
+
+
+int NdbThread_WaitFor(struct NdbThread* p_wait_thread, void** status)
+{
+ void *local_status = 0;
+ if (status == 0)
+ status = &local_status;
+
+ if(WaitForSingleObject(p_wait_thread->hThread, INFINITE) == WAIT_OBJECT_0
+ && GetExitCodeThread(p_wait_thread->hThread, (LPDWORD)status))
+ {
+ CloseHandle(p_wait_thread->hThread);
+ p_wait_thread->hThread = 0;
+ return 0;
+ }
+ return -1;
+}
+
+
+void NdbThread_Exit(int status)
+{
+ _endthreadex((DWORD) status);
+}
+
+
+int NdbThread_SetConcurrencyLevel(int level)
+{
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/old_dirs/win32/NdbTick.c b/storage/ndb/src/common/portlib/old_dirs/win32/NdbTick.c
new file mode 100644
index 00000000000..e3a67d8437d
--- /dev/null
+++ b/storage/ndb/src/common/portlib/old_dirs/win32/NdbTick.c
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <windows.h>
+#include "NdbTick.h"
+
+/*
+#define FILETIME_PER_MICROSEC 10
+#define FILETIME_PER_MILLISEC 10000
+#define FILETIME_PER_SEC 10000000
+
+
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ ULONGLONG ullTime;
+ GetSystemTimeAsFileTime((LPFILETIME)&ullTime);
+ return (ullTime / FILETIME_PER_MILLISEC);
+}
+
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros)
+{
+ ULONGLONG ullTime;
+ GetSystemTimeAsFileTime((LPFILETIME)&ullTime);
+ *secs = (ullTime / FILETIME_PER_SEC);
+ *micros = (Uint32)((ullTime % FILETIME_PER_SEC) / FILETIME_PER_MICROSEC);
+ return 0;
+}
+*/
+
+
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ LARGE_INTEGER liCount, liFreq;
+ QueryPerformanceCounter(&liCount);
+ QueryPerformanceFrequency(&liFreq);
+ return (liCount.QuadPart*1000) / liFreq.QuadPart;
+}
+
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros)
+{
+ LARGE_INTEGER liCount, liFreq;
+ QueryPerformanceCounter(&liCount);
+ QueryPerformanceFrequency(&liFreq);
+ *secs = liCount.QuadPart / liFreq.QuadPart;
+ liCount.QuadPart -= *secs * liFreq.QuadPart;
+ *micros = (liCount.QuadPart*1000000) / liFreq.QuadPart;
+ return 0;
+}
diff --git a/storage/ndb/src/common/portlib/win32/NdbCondition.c b/storage/ndb/src/common/portlib/win32/NdbCondition.c
new file mode 100644
index 00000000000..4046db1d60a
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbCondition.c
@@ -0,0 +1,178 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include "NdbCondition.h"
+#include <NdbMutex.h>
+
+struct NdbCondition
+{
+ long nWaiters;
+ NdbMutex* pNdbMutexWaitersLock;
+ HANDLE hSemaphore;
+ HANDLE hEventWaitersDone;
+ int bWasBroadcast;
+};
+
+
+struct NdbCondition*
+NdbCondition_Create(void)
+{
+ int result = 0;
+ struct NdbCondition* pNdbCondition = (struct NdbCondition*)malloc(sizeof(struct NdbCondition));
+ if(!pNdbCondition)
+ return 0;
+
+ pNdbCondition->nWaiters = 0;
+ pNdbCondition->bWasBroadcast = 0;
+ if(!(pNdbCondition->hSemaphore = CreateSemaphore(0, 0, MAXLONG, 0)))
+ result = -1;
+ else if(!(pNdbCondition->pNdbMutexWaitersLock = NdbMutex_Create()))
+ result = -1;
+ else if(!(pNdbCondition->hEventWaitersDone = CreateEvent(0, 0, 0, 0)))
+ result = -1;
+
+ assert(!result);
+ return pNdbCondition;
+}
+
+
+int
+NdbCondition_Wait(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex)
+{
+ int result;
+ int bLastWaiter;
+ if(!p_cond || !p_mutex)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters++;
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(NdbMutex_Unlock(p_mutex))
+ return -1;
+ result = WaitForSingleObject (p_cond->hSemaphore, INFINITE);
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters--;
+ bLastWaiter = (p_cond->bWasBroadcast && p_cond->nWaiters==0);
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(result==WAIT_OBJECT_0 && bLastWaiter)
+ SetEvent(p_cond->hEventWaitersDone);
+
+ NdbMutex_Lock(p_mutex);
+ return result;
+}
+
+
+int
+NdbCondition_WaitTimeout(struct NdbCondition* p_cond,
+ NdbMutex* p_mutex,
+ int msecs)
+{
+ int result;
+ int bLastWaiter;
+ if (!p_cond || !p_mutex)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters++;
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+ if(msecs<0)
+ msecs = 0;
+
+ if(NdbMutex_Unlock(p_mutex))
+ return -1;
+ result = WaitForSingleObject(p_cond->hSemaphore, msecs);
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ p_cond->nWaiters--;
+ bLastWaiter = (p_cond->bWasBroadcast && p_cond->nWaiters==0);
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(result!=WAIT_OBJECT_0)
+ result = -1;
+
+ if(bLastWaiter)
+ SetEvent(p_cond->hEventWaitersDone);
+
+ NdbMutex_Lock(p_mutex);
+ return result;
+}
+
+
+int
+NdbCondition_Signal(struct NdbCondition* p_cond)
+{
+ int bHaveWaiters;
+ if(!p_cond)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ bHaveWaiters = (p_cond->nWaiters > 0);
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+
+ if(bHaveWaiters)
+ return (ReleaseSemaphore(p_cond->hSemaphore, 1, 0) ? 0 : -1);
+ else
+ return 0;
+}
+
+
+int NdbCondition_Broadcast(struct NdbCondition* p_cond)
+{
+ int bHaveWaiters;
+ int result = 0;
+ if(!p_cond)
+ return 1;
+
+ NdbMutex_Lock(p_cond->pNdbMutexWaitersLock);
+ bHaveWaiters = 0;
+ if(p_cond->nWaiters > 0)
+ {
+ p_cond->bWasBroadcast = !0;
+ bHaveWaiters = 1;
+ }
+ NdbMutex_Unlock(p_cond->pNdbMutexWaitersLock);
+ if(bHaveWaiters)
+ {
+ if(!ReleaseSemaphore(p_cond->hSemaphore, p_cond->nWaiters, 0))
+ result = -1;
+ else if(WaitForSingleObject (p_cond->hEventWaitersDone, INFINITE) != WAIT_OBJECT_0)
+ result = -1;
+ p_cond->bWasBroadcast = 0;
+ }
+ return result;
+}
+
+
+int NdbCondition_Destroy(struct NdbCondition* p_cond)
+{
+ int result;
+ if(!p_cond)
+ return 1;
+
+ CloseHandle(p_cond->hEventWaitersDone);
+ NdbMutex_Destroy(p_cond->pNdbMutexWaitersLock);
+ result = (CloseHandle(p_cond->hSemaphore) ? 0 : -1);
+
+ free(p_cond);
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbDaemon.c b/storage/ndb/src/common/portlib/win32/NdbDaemon.c
new file mode 100644
index 00000000000..b96d4c20260
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbDaemon.c
@@ -0,0 +1,44 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "NdbDaemon.h"
+
+#define NdbDaemon_ErrorSize 500
+long NdbDaemon_DaemonPid;
+int NdbDaemon_ErrorCode;
+char NdbDaemon_ErrorText[NdbDaemon_ErrorSize];
+
+int
+NdbDaemon_Make(const char* lockfile, const char* logfile, unsigned flags)
+{
+ // XXX do something
+ return 0;
+}
+
+#ifdef NDB_DAEMON_TEST
+
+int
+main()
+{
+ if (NdbDaemon_Make("test.pid", "test.log", 0) == -1) {
+ fprintf(stderr, "NdbDaemon_Make: %s\n", NdbDaemon_ErrorText);
+ return 1;
+ }
+ sleep(10);
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/common/portlib/win32/NdbEnv.c b/storage/ndb/src/common/portlib/win32/NdbEnv.c
new file mode 100644
index 00000000000..f42e685fe15
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbEnv.c
@@ -0,0 +1,31 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbEnv.h"
+
+const char* NdbEnv_GetEnv(const char* name, char * buf, int buflen)
+{
+ char* p = NULL;
+ p = getenv(name);
+
+ if (p != NULL && buf != NULL){
+ strncpy(buf, p, buflen);
+ buf[buflen-1] = 0;
+ }
+ return p;
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbHost.c b/storage/ndb/src/common/portlib/win32/NdbHost.c
new file mode 100644
index 00000000000..7df96c45991
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbHost.c
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include "NdbHost.h"
+
+
+int NdbHost_GetHostName(char* buf)
+{
+ /* We must initialize TCP/IP if we want to call gethostname */
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD( 2, 0 );
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if ( err != 0 ) {
+ /**
+ * Tell the user that we couldn't find a usable
+ * WinSock DLL.
+ */
+ return -1;
+ }
+
+ /* Get host name */
+ if(gethostname(buf, MAXHOSTNAMELEN))
+ {
+ return -1;
+ }
+ return 0;
+}
+
+
+int NdbHost_GetProcessId(void)
+{
+ return _getpid();
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbMem.c b/storage/ndb/src/common/portlib/win32/NdbMem.c
new file mode 100644
index 00000000000..313ca9dff66
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbMem.c
@@ -0,0 +1,283 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbMem.h"
+
+#if 0
+struct AWEINFO
+{
+ SIZE_T dwSizeInBytesRequested;
+ ULONG_PTR nNumberOfPagesRequested;
+ ULONG_PTR nNumberOfPagesActual;
+ ULONG_PTR nNumberOfPagesFreed;
+ ULONG_PTR* pnPhysicalMemoryPageArray;
+ void* pRegionReserved;
+};
+
+const size_t cNdbMem_nMaxAWEinfo = 256;
+size_t gNdbMem_nAWEinfo = 0;
+
+struct AWEINFO* gNdbMem_pAWEinfo = 0;
+
+
+void ShowLastError(const char* szContext, const char* szFunction)
+{
+ DWORD dwError = GetLastError();
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ dwError,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR)&lpMsgBuf,
+ 0,
+ NULL
+ );
+ printf("%s : %s failed : %lu : %s\n", szContext, szFunction, dwError, (char*)lpMsgBuf);
+ LocalFree(lpMsgBuf);
+}
+
+
+
+void NdbMem_Create()
+{
+ // Address Windowing Extensions
+ struct PRIVINFO
+ {
+ DWORD Count;
+ LUID_AND_ATTRIBUTES Privilege[1];
+ } Info;
+
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE hToken;
+ if(!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ ShowLastError("NdbMem_Create", "OpenProcessToken");
+ }
+
+ Info.Count = 1;
+ Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if(!LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &(Info.Privilege[0].Luid)))
+ {
+ ShowLastError("NdbMem_Create", "LookupPrivilegeValue");
+ }
+
+ if(!AdjustTokenPrivileges(hToken, FALSE, (PTOKEN_PRIVILEGES)&Info, 0, 0, 0))
+ {
+ ShowLastError("NdbMem_Create", "AdjustTokenPrivileges");
+ }
+
+ if(!CloseHandle(hToken))
+ {
+ ShowLastError("NdbMem_Create", "CloseHandle");
+ }
+
+ return;
+}
+
+void NdbMem_Destroy()
+{
+ /* Do nothing */
+ return;
+}
+
+void* NdbMem_Allocate(size_t size)
+{
+ // Address Windowing Extensions
+ struct AWEINFO* pAWEinfo;
+ HANDLE hProcess;
+ SYSTEM_INFO sysinfo;
+
+ if(!gNdbMem_pAWEinfo)
+ {
+ gNdbMem_pAWEinfo = VirtualAlloc(0,
+ sizeof(struct AWEINFO)*cNdbMem_nMaxAWEinfo,
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ }
+
+ assert(gNdbMem_nAWEinfo < cNdbMem_nMaxAWEinfo);
+ pAWEinfo = gNdbMem_pAWEinfo+gNdbMem_nAWEinfo++;
+
+ hProcess = GetCurrentProcess();
+ GetSystemInfo(&sysinfo);
+ pAWEinfo->nNumberOfPagesRequested = (size+sysinfo.dwPageSize-1)/sysinfo.dwPageSize;
+ pAWEinfo->pnPhysicalMemoryPageArray = VirtualAlloc(0,
+ sizeof(ULONG_PTR)*pAWEinfo->nNumberOfPagesRequested,
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ pAWEinfo->nNumberOfPagesActual = pAWEinfo->nNumberOfPagesRequested;
+ if(!AllocateUserPhysicalPages(hProcess, &(pAWEinfo->nNumberOfPagesActual), pAWEinfo->pnPhysicalMemoryPageArray))
+ {
+ ShowLastError("NdbMem_Allocate", "AllocateUserPhysicalPages");
+ return 0;
+ }
+ if(pAWEinfo->nNumberOfPagesRequested != pAWEinfo->nNumberOfPagesActual)
+ {
+ ShowLastError("NdbMem_Allocate", "nNumberOfPagesRequested != nNumberOfPagesActual");
+ return 0;
+ }
+
+ pAWEinfo->dwSizeInBytesRequested = size;
+ pAWEinfo->pRegionReserved = VirtualAlloc(0, pAWEinfo->dwSizeInBytesRequested, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE);
+ if(!pAWEinfo->pRegionReserved)
+ {
+ ShowLastError("NdbMem_Allocate", "VirtualAlloc");
+ return 0;
+ }
+
+ if(!MapUserPhysicalPages(pAWEinfo->pRegionReserved, pAWEinfo->nNumberOfPagesActual, pAWEinfo->pnPhysicalMemoryPageArray))
+ {
+ ShowLastError("NdbMem_Allocate", "MapUserPhysicalPages");
+ return 0;
+ }
+
+ /*
+ printf("allocate AWE memory: %lu bytes, %lu pages, address %lx\n",
+ pAWEinfo->dwSizeInBytesRequested,
+ pAWEinfo->nNumberOfPagesActual,
+ pAWEinfo->pRegionReserved);
+ */
+ return pAWEinfo->pRegionReserved;
+}
+
+
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ /*
+ return (void*)memalign(alignment, size);
+ TEMP fix
+ */
+ return NdbMem_Allocate(size);
+}
+
+
+void NdbMem_Free(void* ptr)
+{
+ // VirtualFree(ptr, 0, MEM_DECOMMIT|MEM_RELEASE);
+
+ // Address Windowing Extensions
+ struct AWEINFO* pAWEinfo = 0;
+ size_t i;
+ HANDLE hProcess;
+
+ for(i=0; i<gNdbMem_nAWEinfo; ++i)
+ {
+ if(ptr==gNdbMem_pAWEinfo[i].pRegionReserved)
+ {
+ pAWEinfo = gNdbMem_pAWEinfo+i;
+ }
+ }
+ if(!pAWEinfo)
+ {
+ ShowLastError("NdbMem_Free", "ptr is not AWE memory");
+ }
+
+ hProcess = GetCurrentProcess();
+ if(!MapUserPhysicalPages(ptr, pAWEinfo->nNumberOfPagesActual, 0))
+ {
+ ShowLastError("NdbMem_Free", "MapUserPhysicalPages");
+ }
+
+ if(!VirtualFree(ptr, 0, MEM_RELEASE))
+ {
+ ShowLastError("NdbMem_Free", "VirtualFree");
+ }
+
+ pAWEinfo->nNumberOfPagesFreed = pAWEinfo->nNumberOfPagesActual;
+ if(!FreeUserPhysicalPages(hProcess, &(pAWEinfo->nNumberOfPagesFreed), pAWEinfo->pnPhysicalMemoryPageArray))
+ {
+ ShowLastError("NdbMem_Free", "FreeUserPhysicalPages");
+ }
+
+ VirtualFree(pAWEinfo->pnPhysicalMemoryPageArray, 0, MEM_DECOMMIT|MEM_RELEASE);
+}
+
+
+int NdbMem_MemLockAll()
+{
+ /*
+ HANDLE hProcess = GetCurrentProcess();
+ SIZE_T nMinimumWorkingSetSize;
+ SIZE_T nMaximumWorkingSetSize;
+ GetProcessWorkingSetSize(hProcess, &nMinimumWorkingSetSize, &nMaximumWorkingSetSize);
+ ndbout << "nMinimumWorkingSetSize=" << nMinimumWorkingSetSize << ", nMaximumWorkingSetSize=" << nMaximumWorkingSetSize << endl;
+
+ SetProcessWorkingSetSize(hProcess, 50000000, 100000000);
+
+ GetProcessWorkingSetSize(hProcess, &nMinimumWorkingSetSize, &nMaximumWorkingSetSize);
+ ndbout << "nMinimumWorkingSetSize=" << nMinimumWorkingSetSize << ", nMaximumWorkingSetSize=" << nMaximumWorkingSetSize << endl;
+ */
+ return -1;
+}
+
+int NdbMem_MemUnlockAll()
+{
+ //VirtualUnlock();
+ return -1;
+}
+
+#endif
+
+void NdbMem_Create()
+{
+ /* Do nothing */
+ return;
+}
+
+void NdbMem_Destroy()
+{
+ /* Do nothing */
+ return;
+}
+
+
+void* NdbMem_Allocate(size_t size)
+{
+ void* mem_allocated;
+ assert(size > 0);
+ mem_allocated= (void*)malloc(size);
+ return mem_allocated;
+}
+
+void* NdbMem_AllocateAlign(size_t size, size_t alignment)
+{
+ (void)alignment; /* remove warning for unused parameter */
+ /*
+ return (void*)memalign(alignment, size);
+ TEMP fix
+ */
+ return (void*)malloc(size);
+}
+
+
+void NdbMem_Free(void* ptr)
+{
+ free(ptr);
+}
+
+
+int NdbMem_MemLockAll()
+{
+ return 0;
+}
+
+int NdbMem_MemUnlockAll()
+{
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbMutex.c b/storage/ndb/src/common/portlib/win32/NdbMutex.c
new file mode 100644
index 00000000000..e6d1f081e9a
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbMutex.c
@@ -0,0 +1,73 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include "NdbMutex.h"
+
+NdbMutex* NdbMutex_Create(void)
+{
+ NdbMutex* pNdbMutex = (NdbMutex*)malloc(sizeof(NdbMutex));
+ if(!pNdbMutex)
+ return 0;
+
+ InitializeCriticalSection(pNdbMutex);
+ return pNdbMutex;
+}
+
+
+int NdbMutex_Destroy(NdbMutex* p_mutex)
+{
+ if(!p_mutex)
+ return -1;
+
+ DeleteCriticalSection(p_mutex);
+ free(p_mutex);
+ return 0;
+}
+
+
+int NdbMutex_Lock(NdbMutex* p_mutex)
+{
+ if(!p_mutex)
+ return -1;
+
+ EnterCriticalSection (p_mutex);
+ return 0;
+}
+
+
+int NdbMutex_Unlock(NdbMutex* p_mutex)
+{
+ if(!p_mutex)
+ return -1;
+
+ LeaveCriticalSection(p_mutex);
+ return 0;
+}
+
+
+int NdbMutex_Trylock(NdbMutex* p_mutex)
+{
+ int result = -1;
+ if(p_mutex)
+ {
+ result = NdbMutex_Lock(p_mutex);
+ //(TryEnterCriticalSection(p_mutex) ? 0 : -1);
+ }
+ return result;
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbSleep.c b/storage/ndb/src/common/portlib/win32/NdbSleep.c
new file mode 100644
index 00000000000..8f5bdc49acd
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbSleep.c
@@ -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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbSleep.h"
+
+int
+NdbSleep_MilliSleep(int milliseconds)
+{
+ Sleep(milliseconds);
+ return 0;
+}
+
+int
+NdbSleep_SecSleep(int seconds)
+{
+ return NdbSleep_MilliSleep(seconds*1000);
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbTCP.c b/storage/ndb/src/common/portlib/win32/NdbTCP.c
new file mode 100644
index 00000000000..b936cd2db6c
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbTCP.c
@@ -0,0 +1,39 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbTCP.h"
+
+int
+Ndb_getInAddr(struct in_addr * dst, const char *address)
+{
+ struct hostent * hostPtr;
+
+ /* Try it as aaa.bbb.ccc.ddd. */
+ dst->s_addr = inet_addr(address);
+ if (dst->s_addr != -1) {
+ return 0;
+ }
+
+ hostPtr = gethostbyname(address);
+ if (hostPtr != NULL) {
+ dst->s_addr = ((struct in_addr *) *hostPtr->h_addr_list)->s_addr;
+ return 0;
+ }
+
+ return -1;
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbThread.c b/storage/ndb/src/common/portlib/win32/NdbThread.c
new file mode 100644
index 00000000000..98db0d5c287
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbThread.c
@@ -0,0 +1,114 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbThread.h"
+#include <process.h>
+
+#define MAX_THREAD_NAME 16
+
+typedef unsigned (WINAPI* NDB_WIN32_THREAD_FUNC)(void*);
+
+
+struct NdbThread
+{
+ HANDLE hThread;
+ unsigned nThreadId;
+ char thread_name[MAX_THREAD_NAME];
+};
+
+
+struct NdbThread* NdbThread_Create(NDB_THREAD_FUNC *p_thread_func,
+ NDB_THREAD_ARG *p_thread_arg,
+ const NDB_THREAD_STACKSIZE thread_stack_size,
+ const char* p_thread_name,
+ NDB_THREAD_PRIO thread_prio)
+{
+ struct NdbThread* tmpThread;
+ unsigned initflag;
+ int nPriority = 0;
+
+ if(!p_thread_func)
+ return 0;
+
+ tmpThread = (struct NdbThread*)malloc(sizeof(struct NdbThread));
+ if(!tmpThread)
+ return 0;
+
+ strncpy((char*)&tmpThread->thread_name, p_thread_name, MAX_THREAD_NAME);
+
+ switch(thread_prio)
+ {
+ case NDB_THREAD_PRIO_HIGHEST: nPriority=THREAD_PRIORITY_HIGHEST; break;
+ case NDB_THREAD_PRIO_HIGH: nPriority=THREAD_PRIORITY_ABOVE_NORMAL; break;
+ case NDB_THREAD_PRIO_MEAN: nPriority=THREAD_PRIORITY_NORMAL; break;
+ case NDB_THREAD_PRIO_LOW: nPriority=THREAD_PRIORITY_BELOW_NORMAL; break;
+ case NDB_THREAD_PRIO_LOWEST: nPriority=THREAD_PRIORITY_LOWEST; break;
+ }
+ initflag = (nPriority ? CREATE_SUSPENDED : 0);
+
+ tmpThread->hThread = (HANDLE)_beginthreadex(0, thread_stack_size,
+ (NDB_WIN32_THREAD_FUNC)p_thread_func, p_thread_arg,
+ initflag, &tmpThread->nThreadId);
+
+ if(nPriority && tmpThread->hThread)
+ {
+ SetThreadPriority(tmpThread->hThread, nPriority);
+ ResumeThread (tmpThread->hThread);
+ }
+
+ assert(tmpThread->hThread);
+ return tmpThread;
+}
+
+
+void NdbThread_Destroy(struct NdbThread** p_thread)
+{
+ CloseHandle((*p_thread)->hThread);
+ (*p_thread)->hThread = 0;
+ free(*p_thread);
+ *p_thread = 0;
+}
+
+
+int NdbThread_WaitFor(struct NdbThread* p_wait_thread, void** status)
+{
+ void *local_status = 0;
+ if (status == 0)
+ status = &local_status;
+
+ if(WaitForSingleObject(p_wait_thread->hThread, INFINITE) == WAIT_OBJECT_0
+ && GetExitCodeThread(p_wait_thread->hThread, (LPDWORD)status))
+ {
+ CloseHandle(p_wait_thread->hThread);
+ p_wait_thread->hThread = 0;
+ return 0;
+ }
+ return -1;
+}
+
+
+void NdbThread_Exit(int status)
+{
+ _endthreadex((DWORD) status);
+}
+
+
+int NdbThread_SetConcurrencyLevel(int level)
+{
+ return 0;
+}
+
diff --git a/storage/ndb/src/common/portlib/win32/NdbTick.c b/storage/ndb/src/common/portlib/win32/NdbTick.c
new file mode 100644
index 00000000000..4430cbf419b
--- /dev/null
+++ b/storage/ndb/src/common/portlib/win32/NdbTick.c
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbTick.h"
+//#include <windows.h>
+
+/*
+#define FILETIME_PER_MICROSEC 10
+#define FILETIME_PER_MILLISEC 10000
+#define FILETIME_PER_SEC 10000000
+
+
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ ULONGLONG ullTime;
+ GetSystemTimeAsFileTime((LPFILETIME)&ullTime);
+ return (ullTime / FILETIME_PER_MILLISEC);
+}
+
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros)
+{
+ ULONGLONG ullTime;
+ GetSystemTimeAsFileTime((LPFILETIME)&ullTime);
+ *secs = (ullTime / FILETIME_PER_SEC);
+ *micros = (Uint32)((ullTime % FILETIME_PER_SEC) / FILETIME_PER_MICROSEC);
+ return 0;
+}
+*/
+
+
+NDB_TICKS NdbTick_CurrentMillisecond(void)
+{
+ LARGE_INTEGER liCount, liFreq;
+ QueryPerformanceCounter(&liCount);
+ QueryPerformanceFrequency(&liFreq);
+ return (liCount.QuadPart*1000) / liFreq.QuadPart;
+}
+
+int
+NdbTick_CurrentMicrosecond(NDB_TICKS * secs, Uint32 * micros)
+{
+ LARGE_INTEGER liCount, liFreq;
+ QueryPerformanceCounter(&liCount);
+ QueryPerformanceFrequency(&liFreq);
+ *secs = liCount.QuadPart / liFreq.QuadPart;
+ liCount.QuadPart -= *secs * liFreq.QuadPart;
+ *micros = (liCount.QuadPart*1000000) / liFreq.QuadPart;
+ return 0;
+}
diff --git a/storage/ndb/src/common/transporter/Makefile.am b/storage/ndb/src/common/transporter/Makefile.am
new file mode 100644
index 00000000000..4c277097a91
--- /dev/null
+++ b/storage/ndb/src/common/transporter/Makefile.am
@@ -0,0 +1,36 @@
+
+noinst_LTLIBRARIES = libtransporter.la
+
+libtransporter_la_SOURCES = \
+ Transporter.cpp \
+ SendBuffer.cpp \
+ TCP_Transporter.cpp \
+ TransporterRegistry.cpp \
+ Packer.cpp
+
+EXTRA_libtransporter_la_SOURCES = SHM_Transporter.cpp SHM_Transporter.unix.cpp SCI_Transporter.cpp
+
+libtransporter_la_LIBADD = @ndb_transporter_opt_objs@
+libtransporter_la_DEPENDENCIES = @ndb_transporter_opt_objs@
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/include/mgmapi -I$(top_srcdir)/ndb/src/mgmapi -I$(top_srcdir)/ndb/include/debugger -I$(top_srcdir)/ndb/include/kernel -I$(top_srcdir)/ndb/include/transporter @NDB_SCI_INCLUDES@
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_util.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libtransporter.dsp
+
+libtransporter.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libtransporter_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/transporter/OSE_Receiver.cpp b/storage/ndb/src/common/transporter/OSE_Receiver.cpp
new file mode 100644
index 00000000000..63a33fc8f24
--- /dev/null
+++ b/storage/ndb/src/common/transporter/OSE_Receiver.cpp
@@ -0,0 +1,359 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbOut.hpp>
+#include "OSE_Receiver.hpp"
+#include "OSE_Transporter.hpp"
+#include "TransporterCallback.hpp"
+#include <TransporterRegistry.hpp>
+#include "TransporterInternalDefinitions.hpp"
+
+OSE_Receiver::OSE_Receiver(TransporterRegistry * tr,
+ int _recBufSize,
+ NodeId _localNodeId) {
+ theTransporterRegistry = tr;
+
+ recBufSize = _recBufSize;
+ recBufReadIndex = 0;
+ recBufWriteIndex = 0;
+ receiveBuffer = new union SIGNAL * [recBufSize];
+
+ waitStackCount = 0;
+ waitStackSize = _recBufSize;
+ waitStack = new union SIGNAL * [waitStackSize];
+
+ nextSigId = new Uint32[MAX_NTRANSPORTERS];
+ for (int i = 0; i < MAX_NTRANSPORTERS; i++)
+ nextSigId[i] = 0;
+
+ phantomCreated = false;
+ localNodeId = _localNodeId;
+ BaseString::snprintf(localHostName, sizeof(localHostName),
+ "ndb_node%d", localNodeId);
+
+ DEBUG("localNodeId = " << localNodeId << " -> localHostName = "
+ << localHostName);
+}
+
+OSE_Receiver::~OSE_Receiver(){
+ while(recBufReadIndex != recBufWriteIndex){
+ free_buf(&receiveBuffer[recBufReadIndex]);
+ recBufReadIndex = (recBufReadIndex + 1) % recBufSize;
+ }
+ delete [] receiveBuffer;
+ destroyPhantom();
+}
+
+PROCESS
+OSE_Receiver::createPhantom(){
+ redir.sig = 1;
+ redir.pid = current_process();
+
+ if(!phantomCreated){
+ phantomPid = create_process
+ (OS_PHANTOM, // Type
+ localHostName, // Name
+ NULL, // Entry point
+ 0, // Stack size
+ 0, // Prio - Not used
+ (OSTIME)0, // Timeslice - Not used
+ 0, // Block - current block
+ &redir,
+ (OSVECTOR)0, // vector
+ (OSUSER)0); // user
+ phantomCreated = true;
+ DEBUG("Created phantom pid: " << hex << phantomPid);
+ }
+ return phantomPid;
+}
+
+void
+OSE_Receiver::destroyPhantom(){
+ if(phantomCreated){
+ DEBUG("Destroying phantom pid: " << hex << phantomPid);
+ kill_proc(phantomPid);
+ phantomCreated = false;
+ }
+}
+
+static SIGSELECT PRIO_A_SIGNALS[] = { 6,
+ NDB_TRANSPORTER_PRIO_A,
+ NDB_TRANSPORTER_HUNT,
+ NDB_TRANSPORTER_CONNECT_REQ,
+ NDB_TRANSPORTER_CONNECT_REF,
+ NDB_TRANSPORTER_CONNECT_CONF,
+ NDB_TRANSPORTER_DISCONNECT_ORD
+};
+
+static SIGSELECT PRIO_B_SIGNALS[] = { 1,
+ NDB_TRANSPORTER_DATA
+};
+
+/**
+ * Check waitstack for signals that are next in sequence
+ * Put any found signal in receive buffer
+ * Returns true if one signal is found
+ */
+bool
+OSE_Receiver::checkWaitStack(NodeId _nodeId){
+
+ for(int i = 0; i < waitStackCount; i++){
+ if (waitStack[i]->dataSignal.senderNodeId == _nodeId &&
+ waitStack[i]->dataSignal.sigId == nextSigId[_nodeId]){
+
+ ndbout_c("INFO: signal popped from waitStack, sigId = %d",
+ waitStack[i]->dataSignal.sigId);
+
+ if(isFull()){
+ ndbout_c("ERROR: receiveBuffer is full");
+ reportError(callbackObj, _nodeId, TE_RECEIVE_BUFFER_FULL);
+ return false;
+ }
+
+ // The next signal was found, put it in the receive buffer
+ insertReceiveBuffer(waitStack[i]);
+
+ // Increase sequence id, set it to the next expected id
+ nextSigId[_nodeId]++;
+
+ // Move signals below up one step
+ for(int j = i; j < waitStackCount-1; j++)
+ waitStack[j] = waitStack[j+1];
+ waitStack[waitStackCount] = NULL;
+ waitStackCount--;
+
+ // return true since signal was found
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Clear waitstack for signals from node with _nodeId
+ */
+void
+OSE_Receiver::clearWaitStack(NodeId _nodeId){
+
+ for(int i = 0; i < waitStackCount; i++){
+ if (waitStack[i]->dataSignal.senderNodeId == _nodeId){
+
+ // Free signal buffer
+ free_buf(&waitStack[i]);
+
+ // Move signals below up one step
+ for(int j = i; j < waitStackCount-1; j++)
+ waitStack[j] = waitStack[j+1];
+ waitStack[waitStackCount] = NULL;
+ waitStackCount--;
+ }
+ }
+ nextSigId[_nodeId] = 0;
+}
+
+
+inline
+void
+OSE_Receiver::insertWaitStack(union SIGNAL* _sig){
+ if (waitStackCount <= waitStackSize){
+ waitStack[waitStackCount] = _sig;
+ waitStackCount++;
+ } else {
+ ndbout_c("ERROR: waitStack is full");
+ reportError(callbackObj, localNodeId, TE_WAIT_STACK_FULL);
+ }
+}
+
+bool
+OSE_Receiver::doReceive(Uint32 timeOutMillis) {
+ if(isFull())
+ return false;
+
+ union SIGNAL * sig = receive_w_tmo(0,
+ PRIO_A_SIGNALS);
+ if(sig == NIL){
+ sig = receive_w_tmo(timeOutMillis,
+ PRIO_B_SIGNALS);
+ if(sig == NIL)
+ return false;
+ }
+
+ DEBUG("Received signal: " << sig->sigNo << " "
+ << sigNo2String(sig->sigNo));
+
+ switch(sig->sigNo){
+ case NDB_TRANSPORTER_PRIO_A:
+ {
+ OSE_Transporter * t = getTransporter(sig->dataSignal.senderNodeId);
+ if (t != 0 && t->isConnected()){
+ insertReceiveBuffer(sig);
+ } else {
+ free_buf(&sig);
+ }
+ }
+ break;
+ case NDB_TRANSPORTER_DATA:
+ {
+ OSE_Transporter * t = getTransporter(sig->dataSignal.senderNodeId);
+ if (t != 0 && t->isConnected()){
+ int nodeId = sig->dataSignal.senderNodeId;
+ Uint32 currSigId = sig->dataSignal.sigId;
+
+ /**
+ * Check if signal is the next in sequence
+ * nextSigId is always set to the next sigId to wait for
+ */
+ if (nextSigId[nodeId] == currSigId){
+
+ // Insert in receive buffer
+ insertReceiveBuffer(sig);
+
+ // Increase sequence id, set it to the next expected id
+ nextSigId[nodeId]++;
+
+ // Check if there are any signal in the wait stack
+ if (waitStackCount > 0){
+ while(checkWaitStack(nodeId));
+ }
+ } else {
+ // Signal was not received in correct order
+ // Check values and put it in the waitStack
+ ndbout_c("WARNING: sigId out of order,"
+ " currSigId = %d, nextSigId = %d",
+ currSigId, nextSigId[nodeId]);
+
+ if (currSigId < nextSigId[nodeId]){
+ // Current recieved sigId was smaller than nextSigId
+ // There is no use to put it in the waitStack
+ ndbout_c("ERROR: recieved sigId was smaller than nextSigId");
+ reportError(callbackObj, nodeId, TE_TOO_SMALL_SIGID);
+ return false;
+ }
+
+ if (currSigId > (nextSigId[nodeId] + waitStackSize)){
+ // Current sigId was larger than nextSigId + size of waitStack
+ // we can never "save" so many signal's on the stack
+ ndbout_c("ERROR: currSigId > (nextSigId + size of waitStack)");
+ reportError(callbackObj, nodeId, TE_TOO_LARGE_SIGID);
+ return false;
+ }
+
+ // Insert in wait stack
+ insertWaitStack(sig);
+ }
+ } else {
+ free_buf(&sig);
+ }
+ }
+ break;
+ case NDB_TRANSPORTER_HUNT:
+ {
+ NdbTransporterHunt * s = (NdbTransporterHunt*)sig;
+ OSE_Transporter * t = getTransporter(s->remoteNodeId);
+ if(t != 0)
+ t->huntReceived(s);
+ free_buf(&sig);
+ }
+ break;
+ case NDB_TRANSPORTER_CONNECT_REQ:
+ {
+ NdbTransporterConnectReq * s = (NdbTransporterConnectReq*)sig;
+ OSE_Transporter * t = getTransporter(s->senderNodeId);
+ if(t != 0){
+ if(t->connectReq(s)){
+ clearWaitStack(s->senderNodeId);
+ clearRecvBuffer(s->senderNodeId);
+ }
+ }
+ free_buf(&sig);
+ }
+ break;
+ case NDB_TRANSPORTER_CONNECT_REF:
+ {
+ NdbTransporterConnectRef * s = (NdbTransporterConnectRef*)sig;
+ OSE_Transporter * t = getTransporter(s->senderNodeId);
+ if(t != 0){
+ if(t->connectRef(s)){
+ clearWaitStack(s->senderNodeId);
+ clearRecvBuffer(s->senderNodeId);
+ }
+ }
+ free_buf(&sig);
+ }
+ break;
+ case NDB_TRANSPORTER_CONNECT_CONF:
+ {
+ NdbTransporterConnectConf * s = (NdbTransporterConnectConf*)sig;
+ OSE_Transporter * t = getTransporter(s->senderNodeId);
+ if(t != 0){
+ if(t->connectConf(s)){
+ clearWaitStack(s->senderNodeId);
+ clearRecvBuffer(s->senderNodeId);
+ }
+ }
+ free_buf(&sig);
+ }
+ break;
+ case NDB_TRANSPORTER_DISCONNECT_ORD:
+ {
+ NdbTransporterDisconnectOrd * s = (NdbTransporterDisconnectOrd*)sig;
+ OSE_Transporter * t = getTransporter(s->senderNodeId);
+ if(t != 0){
+ if(t->disconnectOrd(s)){
+ clearWaitStack(s->senderNodeId);
+ clearRecvBuffer(s->senderNodeId);
+ }
+ }
+ free_buf(&sig);
+ }
+ }
+ return true;
+}
+
+OSE_Transporter *
+OSE_Receiver::getTransporter(NodeId nodeId){
+ if(theTransporterRegistry->theTransporterTypes[nodeId] != tt_OSE_TRANSPORTER)
+ return 0;
+ return (OSE_Transporter *)
+ theTransporterRegistry->theTransporters[nodeId];
+}
+
+void
+OSE_Receiver::clearRecvBuffer(NodeId nodeId){
+ int tmpIndex = 0;
+ union SIGNAL** tmp = new union SIGNAL * [recBufSize];
+
+ /**
+ * Put all signal that I want to keep into tmp
+ */
+ while(recBufReadIndex != recBufWriteIndex){
+ if(receiveBuffer[recBufReadIndex]->dataSignal.senderNodeId != nodeId){
+ tmp[tmpIndex] = receiveBuffer[recBufReadIndex];
+ tmpIndex++;
+ } else {
+ free_buf(&receiveBuffer[recBufReadIndex]);
+ }
+ recBufReadIndex = (recBufReadIndex + 1) % recBufSize;
+ }
+
+ /**
+ * Put all signals that I kept back into receiveBuffer
+ */
+ for(int i = 0; i<tmpIndex; i++)
+ insertReceiveBuffer(tmp[i]);
+
+ delete [] tmp;
+}
diff --git a/storage/ndb/src/common/transporter/OSE_Receiver.hpp b/storage/ndb/src/common/transporter/OSE_Receiver.hpp
new file mode 100644
index 00000000000..1812ab51065
--- /dev/null
+++ b/storage/ndb/src/common/transporter/OSE_Receiver.hpp
@@ -0,0 +1,119 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 OSE_RECEIVER_HPP
+#define OSE_RECEIVER_HPP
+
+#include "ose.h"
+#include "OSE_Signals.hpp"
+#include <kernel_types.h>
+
+class OSE_Receiver {
+public:
+ OSE_Receiver(class TransporterRegistry *,
+ int recBufSize,
+ NodeId localNodeId);
+
+ ~OSE_Receiver();
+
+ bool hasData() const ;
+ bool isFull() const ;
+
+ Uint32 getReceiveData(NodeId * remoteNodeId,
+ Uint32 ** readPtr);
+
+ void updateReceiveDataPtr(Uint32 szRead);
+
+ bool doReceive(Uint32 timeOutMillis);
+
+ PROCESS createPhantom();
+ void destroyPhantom();
+
+private:
+ class TransporterRegistry * theTransporterRegistry;
+
+ NodeId localNodeId;
+ char localHostName[255];
+
+ bool phantomCreated;
+ PROCESS phantomPid;
+ struct OS_redir_entry redir;
+
+ int recBufReadIndex;
+ int recBufWriteIndex;
+ int recBufSize;
+ union SIGNAL **receiveBuffer;
+
+ // Stack for signals that are received out of order
+ int waitStackCount;
+ int waitStackSize;
+ union SIGNAL** waitStack;
+
+ // Counters for the next signal id
+ Uint32* nextSigId;
+
+ class OSE_Transporter * getTransporter(NodeId nodeId);
+
+ void insertReceiveBuffer(union SIGNAL * _sig);
+ void clearRecvBuffer(NodeId _nodeId);
+ bool checkWaitStack(NodeId _nodeId);
+ void clearWaitStack(NodeId _nodeId);
+ void insertWaitStack(union SIGNAL* _sig);
+};
+
+inline
+bool
+OSE_Receiver::hasData () const {
+ return recBufReadIndex != recBufWriteIndex;
+}
+
+inline
+bool
+OSE_Receiver::isFull () const {
+ return ((recBufWriteIndex + 1) % recBufSize) == recBufWriteIndex;
+}
+
+inline
+Uint32
+OSE_Receiver::getReceiveData(NodeId * remoteNodeId,
+ Uint32 ** readPtr){
+ NdbTransporterData *s = (NdbTransporterData *)receiveBuffer[recBufReadIndex];
+ if(recBufReadIndex != recBufWriteIndex){
+ * remoteNodeId = s->senderNodeId;
+ * readPtr = &s->data[0];
+ return s->length;
+ }
+ return 0;
+}
+
+inline
+void
+OSE_Receiver::updateReceiveDataPtr(Uint32 bytesRead){
+ if(bytesRead != 0){
+ free_buf(&receiveBuffer[recBufReadIndex]);
+ recBufReadIndex = (recBufReadIndex + 1) % recBufSize;
+ }
+}
+
+inline
+void
+OSE_Receiver::insertReceiveBuffer(union SIGNAL * _sig){
+ receiveBuffer[recBufWriteIndex] = _sig;
+ recBufWriteIndex = (recBufWriteIndex + 1) % recBufSize;
+}
+
+
+#endif
diff --git a/storage/ndb/src/common/transporter/OSE_Signals.hpp b/storage/ndb/src/common/transporter/OSE_Signals.hpp
new file mode 100644
index 00000000000..3f6cc07b473
--- /dev/null
+++ b/storage/ndb/src/common/transporter/OSE_Signals.hpp
@@ -0,0 +1,144 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 OSE_SIGNALS_HPP
+#define OSE_SIGNALS_HPP
+
+#include <ose.h>
+#include <kernel_types.h>
+
+#define NDB_TRANSPORTER_SIGBASE 3000
+
+#define NDB_TRANSPORTER_DATA (NDB_TRANSPORTER_SIGBASE + 1) /* !-SIGNO(struct NdbTransporterData)-! */
+#define NDB_TRANSPORTER_HUNT (NDB_TRANSPORTER_SIGBASE + 2) /* !-SIGNO(struct NdbTransporterHunt)-! */
+#define NDB_TRANSPORTER_CONNECT_REQ (NDB_TRANSPORTER_SIGBASE + 3) /* !-SIGNO(struct NdbTransporterConnectReq)-! */
+#define NDB_TRANSPORTER_CONNECT_REF (NDB_TRANSPORTER_SIGBASE + 4) /* !-SIGNO(struct NdbTransporterConnectRef)-! */
+#define NDB_TRANSPORTER_CONNECT_CONF (NDB_TRANSPORTER_SIGBASE + 5) /* !-SIGNO(struct NdbTransporterConnectConf)-! */
+#define NDB_TRANSPORTER_DISCONNECT_ORD (NDB_TRANSPORTER_SIGBASE + 6) /* !-SIGNO(struct NdbTransporterDisconnectOrd)-! */
+#define NDB_TRANSPORTER_PRIO_A (NDB_TRANSPORTER_SIGBASE + 7)
+
+inline
+const char *
+sigNo2String(SIGSELECT sigNo){
+ switch(sigNo){
+ case NDB_TRANSPORTER_PRIO_A:
+ return "PRIO_A_DATA";
+ break;
+ case NDB_TRANSPORTER_DATA:
+ return "PRIO_B_DATA";
+ break;
+ case NDB_TRANSPORTER_HUNT:
+ return "HUNT";
+ break;
+ case NDB_TRANSPORTER_CONNECT_REQ:
+ return "CONNECT_REQ";
+ break;
+ case NDB_TRANSPORTER_CONNECT_REF:
+ return "CONNECT_REF";
+ break;
+ case NDB_TRANSPORTER_CONNECT_CONF:
+ return "CONNECT_CONF";
+ break;
+ case NDB_TRANSPORTER_DISCONNECT_ORD:
+ return "DISCONNECT_ORD";
+ break;
+ }
+ return "UNKNOWN";
+}
+
+struct NdbTransporterData
+{
+ SIGSELECT sigNo;
+ Uint32 sigId; // Sequence number for this signal
+ Uint32 senderNodeId;
+ Uint32 length;
+ Uint32 data[1];
+};
+
+struct NdbTransporterData_PrioA
+{
+ SIGSELECT sigNo;
+ Uint32 sigId; // Sequence number for this signal
+ Uint32 senderNodeId;
+ Uint32 length;
+ Uint32 data[1];
+};
+
+struct NdbTransporterHunt
+{
+ SIGSELECT sigNo;
+ NodeId remoteNodeId;
+};
+
+
+struct NdbTransporterConnectReq
+{
+ SIGSELECT sigNo;
+ NodeId remoteNodeId;
+ NodeId senderNodeId;
+};
+
+
+struct NdbTransporterConnectConf
+{
+ SIGSELECT sigNo;
+ NodeId remoteNodeId;
+ NodeId senderNodeId;
+};
+
+struct NdbTransporterConnectRef
+{
+ SIGSELECT sigNo;
+ NodeId remoteNodeId;
+ NodeId senderNodeId;
+ Uint32 reason;
+
+ /**
+ * Node is not accepting connections
+ */
+ static const Uint32 INVALID_STATE = 1;
+};
+
+struct NdbTransporterDisconnectOrd
+{
+ SIGSELECT sigNo;
+ NodeId senderNodeId;
+ Uint32 reason;
+
+ /**
+ * Process died
+ */
+ static const Uint32 PROCESS_DIED = 1;
+
+ /**
+ * Ndb disconnected
+ */
+ static const Uint32 NDB_DISCONNECT = 2;
+};
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct NdbTransporterData dataSignal;
+ struct NdbTransporterData prioAData;
+ struct NdbTransporterHunt ndbHunt;
+ struct NdbTransporterConnectReq ndbConnectReq;
+ struct NdbTransporterConnectRef ndbConnectRef;
+ struct NdbTransporterConnectConf ndbConnectConf;
+ struct NdbTransporterDisconnectOrd ndbDisconnect;
+};
+
+#endif
diff --git a/storage/ndb/src/common/transporter/OSE_Transporter.cpp b/storage/ndb/src/common/transporter/OSE_Transporter.cpp
new file mode 100644
index 00000000000..ad67791fc0c
--- /dev/null
+++ b/storage/ndb/src/common/transporter/OSE_Transporter.cpp
@@ -0,0 +1,487 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ose.h>
+#include "OSE_Transporter.hpp"
+#include "OSE_Signals.hpp"
+
+#include <TransporterCallback.hpp>
+#include "TransporterInternalDefinitions.hpp"
+
+#include <NdbMutex.h>
+
+#include <NdbHost.h>
+#include <NdbOut.hpp>
+#include <time.h>
+
+OSE_Transporter::OSE_Transporter(int _prioASignalSize,
+ int _prioBSignalSize,
+ NodeId localNodeId,
+ const char * lHostName,
+ NodeId remoteNodeId,
+ NodeId serverNodeId,
+ const char * rHostName,
+ int byteorder,
+ bool compression,
+ bool checksum,
+ bool signalId,
+ Uint32 reportFreq) :
+ Transporter(localNodeId,
+ remoteNodeId,
+ serverNodeId,
+ byteorder,
+ compression,
+ checksum,
+ signalId),
+ isServer(localNodeId < remoteNodeId)
+{
+
+ signalIdCounter = 0;
+ prioBSignalSize = _prioBSignalSize;
+
+ if (strcmp(lHostName, rHostName) == 0){
+ BaseString::snprintf(remoteNodeName, sizeof(remoteNodeName),
+ "ndb_node%d", remoteNodeId);
+ } else {
+ BaseString::snprintf(remoteNodeName, sizeof(remoteNodeName),
+ "%s/ndb_node%d", rHostName, remoteNodeId);
+ }
+
+ prioBSignal = NIL;
+}
+
+OSE_Transporter::~OSE_Transporter() {
+
+#if 0
+ /**
+ * Don't free these buffers since they have already been freed
+ * when the process allocating them died (wild pointers)
+ */
+ if(prioBSignal != NIL)
+ free_buf(&prioBSignal);
+#endif
+}
+
+bool
+OSE_Transporter::initTransporter() {
+
+ struct OS_pcb * pcb = get_pcb(current_process());
+ if(pcb != NULL){
+ if(pcb->type != OS_ILLEGAL){
+ if(prioBSignalSize > pcb->max_sigsize){
+ DEBUG("prioBSignalSize(" << prioBSignalSize << ") > max_sigsize("
+ << pcb->max_sigsize << ") using max_sigsize");
+ prioBSignalSize = pcb->max_sigsize;
+ }
+ }
+ free_buf((union SIGNAL **)&pcb);
+ }
+
+ maxPrioBDataSize = prioBSignalSize;
+ maxPrioBDataSize -= (sizeof(NdbTransporterData) + MAX_MESSAGE_SIZE - 4);
+
+ if(maxPrioBDataSize < 0){
+
+#ifdef DEBUG_TRANSPORTER
+ printf("maxPrioBDataSize < 0 %d\n",
+ maxPrioBDataSize);
+#endif
+ return false;
+ }
+
+ initSignals();
+
+ return true;
+}
+
+void
+OSE_Transporter::initSignals(){
+ if(prioBSignal == NIL){
+ prioBSignal = alloc(prioBSignalSize, NDB_TRANSPORTER_DATA);
+ prioBInsertPtr = &prioBSignal->dataSignal.data[0];
+
+ prioBSignal->dataSignal.length = 0;
+ prioBSignal->dataSignal.senderNodeId = localNodeId;
+ }
+ dataToSend = 0;
+}
+
+NdbTransporterData *
+OSE_Transporter::allocPrioASignal(Uint32 messageLenBytes) const
+{
+
+ const Uint32 lenBytes = messageLenBytes + sizeof(NdbTransporterData) - 4;
+
+ NdbTransporterData * sig =
+ (NdbTransporterData*)alloc(lenBytes, NDB_TRANSPORTER_PRIO_A);
+
+ sig->length = 0;
+ sig->senderNodeId = localNodeId;
+
+ return sig;
+}
+
+Uint32 *
+OSE_Transporter::getWritePtr(Uint32 lenBytes, Uint32 prio){
+ if(prio >= 1){
+ prio = 1;
+ insertPtr = prioBInsertPtr;
+ signal = (NdbTransporterData*)prioBSignal;
+ } else {
+ signal = allocPrioASignal(lenBytes);
+ insertPtr = &signal->data[0];
+ }
+ return insertPtr;
+}
+
+void
+OSE_Transporter::updateWritePtr(Uint32 lenBytes, Uint32 prio){
+
+ Uint32 bufferSize = signal->length;
+ bufferSize += lenBytes;
+ signal->length = bufferSize;
+ if(prio >= 1){
+ prioBInsertPtr += (lenBytes / 4);
+ if(bufferSize >= maxPrioBDataSize)
+ doSend();
+ } else {
+ /**
+ * Prio A signal are sent directly
+ */
+ signal->sigId = 0;
+
+ ::send((union SIGNAL**)&signal, remoteNodePid);
+ }
+}
+
+#if 0
+int getSeq(int _seq){
+ if (_seq > 0){
+ switch (_seq % 100){
+ case 10:
+ return _seq - 1;
+ case 9:
+ return _seq + 1;
+ default:
+ return _seq;
+ }
+ }else{
+ return _seq;
+ }
+}
+int getSeq(int _seq){
+
+ switch (_seq % 40){
+ case 10:
+ return _seq-4;
+ case 9:
+ return _seq-2;
+ case 8:
+ return _seq;
+ case 7:
+ return _seq+2;
+ case 6:
+ return _seq+4;
+
+
+ case 30:
+ return _seq-9;
+ case 29:
+ return _seq-7;
+ case 28:
+ return _seq-5;
+ case 27:
+ return _seq-3;
+ case 26:
+ return _seq-1;
+ case 25:
+ return _seq+1;
+ case 24:
+ return _seq+3;
+ case 23:
+ return _seq+5;
+ case 22:
+ return _seq+7;
+ case 21:
+ return _seq+9;
+
+ default:
+ return _seq;
+
+ }
+}
+#endif
+
+void
+OSE_Transporter::doSend() {
+ /**
+ * restore is always called to make sure the signal buffer is taken over
+ * by a process that is alive, this will otherwise lead to that these buffers
+ * are removed when the process that allocated them dies
+ */
+ restore(prioBSignal);
+ if(prioBSignal->dataSignal.length > 0){
+
+ prioBSignal->dataSignal.sigId = signalIdCounter;
+ signalIdCounter++;
+
+ ::send(&prioBSignal, remoteNodePid);
+ }
+
+ initSignals();
+}
+
+void
+OSE_Transporter::doConnect() {
+
+ NdbMutex_Lock(theMutexPtr);
+ if(_connecting || _disconnecting || _connected){
+ NdbMutex_Unlock(theMutexPtr);
+ return;
+ }
+
+ _connecting = true;
+ signalIdCounter = 0;
+
+ if(isServer){
+ DEBUG("Waiting for connect req: ");
+ state = WAITING_FOR_CONNECT_REQ;
+ } else {
+ state = WAITING_FOR_HUNT;
+
+ DEBUG("Hunting for: " << remoteNodeName);
+
+ union SIGNAL* huntsig;
+ huntsig = alloc(sizeof(NdbTransporterHunt), NDB_TRANSPORTER_HUNT);
+ huntsig->ndbHunt.remoteNodeId = remoteNodeId;
+ hunt(remoteNodeName, 0, NULL, &huntsig);
+ }
+ NdbMutex_Unlock(theMutexPtr);
+}
+
+void
+OSE_Transporter::doDisconnect() {
+ NdbMutex_Lock(theMutexPtr);
+
+ switch(state){
+ case DISCONNECTED:
+ case WAITING_FOR_HUNT:
+ case WAITING_FOR_CONNECT_REQ:
+ case WAITING_FOR_CONNECT_CONF:
+ break;
+ case CONNECTED:
+ {
+#if 0
+ /**
+ * There should not be anything in the buffer that needs to be sent here
+ */
+ DEBUG("Doing send before disconnect");
+ doSend();
+#endif
+ union SIGNAL * sig = alloc(sizeof(NdbTransporterDisconnectOrd),
+ NDB_TRANSPORTER_DISCONNECT_ORD);
+ sig->ndbDisconnect.senderNodeId = localNodeId;
+ sig->ndbDisconnect.reason = NdbTransporterDisconnectOrd::NDB_DISCONNECT;
+ ::send(&sig, remoteNodePid);
+ detach(&remoteNodeRef);
+
+ }
+ break;
+ }
+ state = DISCONNECTED;
+
+ _connected = false;
+ _connecting = false;
+ _disconnecting = false;
+
+ NdbMutex_Unlock(theMutexPtr);
+}
+
+void
+OSE_Transporter::huntReceived(struct NdbTransporterHunt * sig){
+ if(isServer){
+ WARNING("Hunt received for server: remoteNodeId: " <<
+ sig->remoteNodeId);
+ return;
+ }
+
+ if(state != WAITING_FOR_HUNT){
+ WARNING("Hunt received while in state: " << state);
+ return;
+ }
+ remoteNodePid = sender((union SIGNAL**)&sig);
+ union SIGNAL * signal = alloc(sizeof(NdbTransporterConnectReq),
+ NDB_TRANSPORTER_CONNECT_REQ);
+ signal->ndbConnectReq.remoteNodeId = remoteNodeId;
+ signal->ndbConnectReq.senderNodeId = localNodeId;
+
+ DEBUG("Sending connect req to pid: " << hex << remoteNodePid);
+
+ ::send(&signal, remoteNodePid);
+ state = WAITING_FOR_CONNECT_CONF;
+ return;
+}
+
+bool
+OSE_Transporter::connectReq(struct NdbTransporterConnectReq * sig){
+ if(!isServer){
+ WARNING("OSE Connect Req received for client: senderNodeId: " <<
+ sig->senderNodeId);
+ return false;
+ }
+
+ if(state != WAITING_FOR_CONNECT_REQ){
+ PROCESS pid = sender((union SIGNAL**)&sig);
+ union SIGNAL * signal = alloc(sizeof(NdbTransporterConnectRef),
+ NDB_TRANSPORTER_CONNECT_REF);
+ signal->ndbConnectRef.senderNodeId = localNodeId;
+ signal->ndbConnectRef.reason = NdbTransporterConnectRef::INVALID_STATE;
+
+ DEBUG("Sending connect ref to pid: " << hex << pid);
+
+ ::send(&signal, pid);
+ return false;
+ }
+
+ NdbMutex_Lock(theMutexPtr);
+
+ if(prioBSignal != NIL){
+ restore(prioBSignal);
+ free_buf(&prioBSignal);
+ }
+ initSignals();
+
+ remoteNodePid = sender((union SIGNAL**)&sig);
+ union SIGNAL * signal = alloc(sizeof(NdbTransporterConnectRef),
+ NDB_TRANSPORTER_CONNECT_CONF);
+ signal->ndbConnectConf.senderNodeId = localNodeId;
+ signal->ndbConnectConf.remoteNodeId = remoteNodeId;
+
+ union SIGNAL * discon = alloc(sizeof(NdbTransporterDisconnectOrd),
+ NDB_TRANSPORTER_DISCONNECT_ORD);
+ discon->ndbDisconnect.senderNodeId = remoteNodeId;
+ discon->ndbDisconnect.reason = NdbTransporterDisconnectOrd::PROCESS_DIED;
+
+ DEBUG("Attaching to pid: " << hex << remoteNodePid);
+
+ remoteNodeRef = attach(&discon, remoteNodePid);
+
+ DEBUG("Sending connect conf to pid: " << hex << remoteNodePid);
+
+ ::send(&signal, remoteNodePid);
+ state = CONNECTED;
+
+ _connected = true;
+ _connecting = false;
+ _disconnecting = false;
+
+ NdbMutex_Unlock(theMutexPtr);
+
+ return true;
+}
+
+bool
+OSE_Transporter::connectRef(struct NdbTransporterConnectRef * sig){
+ if(isServer){
+ WARNING("OSE Connect Ref received for server: senderNodeId: " <<
+ sig->senderNodeId);
+ return false;
+ }
+ if(state != WAITING_FOR_CONNECT_CONF){
+ WARNING("OSE Connect Ref received for client while in state: " <<
+ state << " senderNodeId: " << sig->senderNodeId);
+ return false;
+ }
+ doDisconnect();
+#if 0
+ /**
+ * Don't call connect directly, wait until the next time
+ * checkConnections is called which will trigger a new connect attempt
+ */
+ doConnect();
+#endif
+ return true;
+}
+
+
+bool
+OSE_Transporter::connectConf(struct NdbTransporterConnectConf * sig){
+ if(isServer){
+ WARNING("OSE Connect Conf received for server: senderNodeId: " <<
+ sig->senderNodeId);
+ return false;
+ }
+ if(state != WAITING_FOR_CONNECT_CONF){
+ WARNING("OSE Connect Conf received while in state: " <<
+ state);
+ return false;
+ }
+ NdbMutex_Lock(theMutexPtr);
+
+ // Free the buffers to get rid of any "junk" that they might contain
+ if(prioBSignal != NIL){
+ restore(prioBSignal);
+ free_buf(&prioBSignal);
+ }
+ initSignals();
+
+ union SIGNAL * discon = alloc(sizeof(NdbTransporterDisconnectOrd),
+ NDB_TRANSPORTER_DISCONNECT_ORD);
+ discon->ndbDisconnect.senderNodeId = remoteNodeId;
+ discon->ndbDisconnect.reason= NdbTransporterDisconnectOrd::PROCESS_DIED;
+
+ remoteNodeRef = attach(&discon, remoteNodePid);
+
+ state = CONNECTED;
+ _connected = true;
+ _connecting = false;
+ _disconnecting = false;
+
+ // Free the buffers to get rid of any "junk" that they might contain
+ if(prioBSignal != NIL){
+ restore(prioBSignal);
+ free_buf(&prioBSignal);
+ }
+ initSignals();
+
+ NdbMutex_Unlock(theMutexPtr);
+ return true;
+}
+
+
+bool
+OSE_Transporter::disconnectOrd(struct NdbTransporterDisconnectOrd * sig){
+ if(state != CONNECTED){
+ WARNING("OSE Disconnect Ord received while in state: " << state <<
+ " reason: " << sig->reason);
+ return false;
+ }
+
+ if(sig->reason == NdbTransporterDisconnectOrd::PROCESS_DIED){
+ state = DISCONNECTED;
+ }
+
+ doDisconnect();
+ reportDisconnect(callbackObj, remoteNodeId,0);
+ return true;
+}
+
+
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/OSE_Transporter.hpp b/storage/ndb/src/common/transporter/OSE_Transporter.hpp
new file mode 100644
index 00000000000..898352366ba
--- /dev/null
+++ b/storage/ndb/src/common/transporter/OSE_Transporter.hpp
@@ -0,0 +1,159 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//****************************************************************************
+//
+// AUTHOR
+// Magnus Svensson
+//
+// NAME
+// OSE_Transporter
+//
+// DESCRIPTION
+// A OSE_Transporter instance is created when OSE-signal communication
+// shall be used (user specified). It handles connect, disconnect,
+// send and receive.
+//
+//
+//
+//***************************************************************************/
+#ifndef OSE_Transporter_H
+#define OSE_Transporter_H
+
+#include "Transporter.hpp"
+
+#include "ose.h"
+
+class OSE_Transporter : public Transporter {
+ friend class OSE_Receiver;
+ friend class TransporterRegistry;
+public:
+
+ // Initialize member variables
+ OSE_Transporter(int prioASignalSize,
+ int prioBSignalSize,
+ NodeId localNodeId,
+ const char * lHostName,
+ NodeId remoteNodeId,
+ NodeId serverNodeId,
+ const char * rHostName,
+ int byteorder,
+ bool compression,
+ bool checksum,
+ bool signalId,
+ Uint32 reportFreq = 4096);
+
+ // Disconnect, delete send buffers and receive buffer
+ ~OSE_Transporter();
+
+ /**
+ * Allocate buffers for sending and receiving
+ */
+ bool initTransporter();
+
+ /**
+ * Connect
+ */
+ virtual void doConnect();
+
+ /**
+ * Disconnect
+ */
+ virtual void doDisconnect();
+
+ Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio);
+ void updateWritePtr(Uint32 lenBytes, Uint32 prio);
+
+ /**
+ * Retrieves the contents of the send buffers, copies it into
+ * an OSE signal and sends it. Until the send buffers are empty
+ */
+ void doSend();
+
+ bool hasDataToSend() const {
+ return prioBSignal->dataSignal.length > 0;
+ }
+
+protected:
+ /**
+ * Not implemented
+ * OSE uses async connect/disconnect
+ */
+ virtual bool connectImpl(Uint32 timeOut){
+ return false;
+ }
+
+ /**
+ * Not implemented
+ * OSE uses async connect/disconnect
+ */
+ virtual void disconnectImpl(){
+ }
+
+private:
+ const bool isServer;
+
+ int maxPrioBDataSize;
+
+ /**
+ * Remote node name
+ * On same machine: ndb_node1
+ * On remote machine: rhost/ndb_node1
+ **/
+ PROCESS remoteNodePid;
+ OSATTREF remoteNodeRef;
+ char remoteNodeName[256];
+
+ Uint32 signalIdCounter;
+
+ int prioBSignalSize;
+
+ Uint32 * prioBInsertPtr;
+ union SIGNAL * prioBSignal;
+
+ struct NdbTransporterData * allocPrioASignal(Uint32 lenBytes) const;
+
+ /**
+ * Statistics
+ */
+ Uint32 reportFreq;
+ Uint32 receiveCount;
+ Uint64 receiveSize;
+ Uint32 sendCount;
+ Uint64 sendSize;
+
+ void initSignals();
+
+ /**
+ * OSE Receiver callbacks
+ */
+ void huntReceived(struct NdbTransporterHunt * sig);
+ bool connectReq(struct NdbTransporterConnectReq * sig);
+ bool connectRef(struct NdbTransporterConnectRef * sig);
+ bool connectConf(struct NdbTransporterConnectConf * sig);
+ bool disconnectOrd(struct NdbTransporterDisconnectOrd * sig);
+
+ enum OSETransporterState {
+ DISCONNECTED = 0,
+ WAITING_FOR_HUNT = 1,
+ WAITING_FOR_CONNECT_REQ = 2,
+ WAITING_FOR_CONNECT_CONF = 3,
+ CONNECTED = 4
+ } state;
+};
+
+// Define of OSE_Transporter_H
+#endif
diff --git a/storage/ndb/src/common/transporter/Packer.cpp b/storage/ndb/src/common/transporter/Packer.cpp
new file mode 100644
index 00000000000..9eba335330d
--- /dev/null
+++ b/storage/ndb/src/common/transporter/Packer.cpp
@@ -0,0 +1,511 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "Packer.hpp"
+#include <TransporterRegistry.hpp>
+#include <TransporterCallback.hpp>
+#include <RefConvert.hpp>
+
+#define MAX_RECEIVED_SIGNALS 1024
+Uint32
+TransporterRegistry::unpack(Uint32 * readPtr,
+ Uint32 sizeOfData,
+ NodeId remoteNodeId,
+ IOState state) {
+ SignalHeader signalHeader;
+ LinearSectionPtr ptr[3];
+
+ Uint32 usedData = 0;
+ Uint32 loop_count = 0;
+
+ if(state == NoHalt || state == HaltOutput){
+ while ((sizeOfData >= 4 + sizeof(Protocol6)) &&
+ (loop_count < MAX_RECEIVED_SIGNALS)) {
+ Uint32 word1 = readPtr[0];
+ Uint32 word2 = readPtr[1];
+ Uint32 word3 = readPtr[2];
+ loop_count++;
+
+#if 0
+ if(Protocol6::getByteOrder(word1) != MY_OWN_BYTE_ORDER){
+ //Do funky stuff
+ }
+#endif
+
+ const Uint16 messageLen32 = Protocol6::getMessageLength(word1);
+ const Uint32 messageLenBytes = ((Uint32)messageLen32) << 2;
+
+ if(messageLen32 == 0 || messageLen32 > MAX_MESSAGE_SIZE){
+ DEBUG("Message Size = " << messageLenBytes);
+ reportError(callbackObj, remoteNodeId, TE_INVALID_MESSAGE_LENGTH);
+ return usedData;
+ }//if
+
+ if (sizeOfData < messageLenBytes) {
+ break;
+ }//if
+
+ if(Protocol6::getCheckSumIncluded(word1)){
+ const Uint32 tmpLen = messageLen32 - 1;
+ const Uint32 checkSumSent = readPtr[tmpLen];
+ const Uint32 checkSumComputed = computeChecksum(&readPtr[0], tmpLen);
+
+ if(checkSumComputed != checkSumSent){
+ reportError(callbackObj, remoteNodeId, TE_INVALID_CHECKSUM);
+ return usedData;
+ }//if
+ }//if
+
+#if 0
+ if(Protocol6::getCompressed(word1)){
+ //Do funky stuff
+ }//if
+#endif
+
+ Protocol6::createSignalHeader(&signalHeader, word1, word2, word3);
+
+ Uint32 sBlockNum = signalHeader.theSendersBlockRef;
+ sBlockNum = numberToRef(sBlockNum, remoteNodeId);
+ signalHeader.theSendersBlockRef = sBlockNum;
+
+ Uint8 prio = Protocol6::getPrio(word1);
+
+ Uint32 * signalData = &readPtr[3];
+
+ if(Protocol6::getSignalIdIncluded(word1) == 0){
+ signalHeader.theSendersSignalId = ~0;
+ } else {
+ signalHeader.theSendersSignalId = * signalData;
+ signalData ++;
+ }//if
+
+ Uint32 * sectionPtr = signalData + signalHeader.theLength;
+ Uint32 * sectionData = sectionPtr + signalHeader.m_noOfSections;
+ for(Uint32 i = 0; i<signalHeader.m_noOfSections; i++){
+ Uint32 sz = * sectionPtr;
+ ptr[i].sz = sz;
+ ptr[i].p = sectionData;
+
+ sectionPtr ++;
+ sectionData += sz;
+ }
+
+ execute(callbackObj, &signalHeader, prio, signalData, ptr);
+
+ readPtr += messageLen32;
+ sizeOfData -= messageLenBytes;
+ usedData += messageLenBytes;
+ }//while
+
+ return usedData;
+ } else {
+ /** state = HaltIO || state == HaltInput */
+
+ while ((sizeOfData >= 4 + sizeof(Protocol6)) &&
+ (loop_count < MAX_RECEIVED_SIGNALS)) {
+ Uint32 word1 = readPtr[0];
+ Uint32 word2 = readPtr[1];
+ Uint32 word3 = readPtr[2];
+ loop_count++;
+
+#if 0
+ if(Protocol6::getByteOrder(word1) != MY_OWN_BYTE_ORDER){
+ //Do funky stuff
+ }//if
+#endif
+
+ const Uint16 messageLen32 = Protocol6::getMessageLength(word1);
+ const Uint32 messageLenBytes = ((Uint32)messageLen32) << 2;
+ if(messageLen32 == 0 || messageLen32 > MAX_MESSAGE_SIZE){
+ DEBUG("Message Size = " << messageLenBytes);
+ reportError(callbackObj, remoteNodeId, TE_INVALID_MESSAGE_LENGTH);
+ return usedData;
+ }//if
+
+ if (sizeOfData < messageLenBytes) {
+ break;
+ }//if
+
+ if(Protocol6::getCheckSumIncluded(word1)){
+ const Uint32 tmpLen = messageLen32 - 1;
+ const Uint32 checkSumSent = readPtr[tmpLen];
+ const Uint32 checkSumComputed = computeChecksum(&readPtr[0], tmpLen);
+
+ if(checkSumComputed != checkSumSent){
+
+ //theTransporters[remoteNodeId]->disconnect();
+ reportError(callbackObj, remoteNodeId, TE_INVALID_CHECKSUM);
+ return usedData;
+ }//if
+ }//if
+
+#if 0
+ if(Protocol6::getCompressed(word1)){
+ //Do funky stuff
+ }//if
+#endif
+
+ Protocol6::createSignalHeader(&signalHeader, word1, word2, word3);
+
+ Uint32 rBlockNum = signalHeader.theReceiversBlockNumber;
+
+ if(rBlockNum == 252){
+ Uint32 sBlockNum = signalHeader.theSendersBlockRef;
+ sBlockNum = numberToRef(sBlockNum, remoteNodeId);
+ signalHeader.theSendersBlockRef = sBlockNum;
+
+ Uint8 prio = Protocol6::getPrio(word1);
+
+ Uint32 * signalData = &readPtr[3];
+
+ if(Protocol6::getSignalIdIncluded(word1) == 0){
+ signalHeader.theSendersSignalId = ~0;
+ } else {
+ signalHeader.theSendersSignalId = * signalData;
+ signalData ++;
+ }//if
+
+ Uint32 * sectionPtr = signalData + signalHeader.theLength;
+ Uint32 * sectionData = sectionPtr + signalHeader.m_noOfSections;
+ for(Uint32 i = 0; i<signalHeader.m_noOfSections; i++){
+ Uint32 sz = * sectionPtr;
+ ptr[i].sz = sz;
+ ptr[i].p = sectionData;
+
+ sectionPtr ++;
+ sectionData += sz;
+ }
+
+ execute(callbackObj, &signalHeader, prio, signalData, ptr);
+ } else {
+ DEBUG("prepareReceive(...) - Discarding message to block: "
+ << rBlockNum << " from Node: " << remoteNodeId);
+ }//if
+
+ readPtr += messageLen32;
+ sizeOfData -= messageLenBytes;
+ usedData += messageLenBytes;
+ }//while
+
+
+ return usedData;
+ }//if
+}
+
+Uint32 *
+TransporterRegistry::unpack(Uint32 * readPtr,
+ Uint32 * eodPtr,
+ NodeId remoteNodeId,
+ IOState state) {
+ static SignalHeader signalHeader;
+ static LinearSectionPtr ptr[3];
+ Uint32 loop_count = 0;
+ if(state == NoHalt || state == HaltOutput){
+ while ((readPtr < eodPtr) && (loop_count < MAX_RECEIVED_SIGNALS)) {
+ Uint32 word1 = readPtr[0];
+ Uint32 word2 = readPtr[1];
+ Uint32 word3 = readPtr[2];
+ loop_count++;
+#if 0
+ if(Protocol6::getByteOrder(word1) != MY_OWN_BYTE_ORDER){
+ //Do funky stuff
+ }
+#endif
+
+ const Uint16 messageLen32 = Protocol6::getMessageLength(word1);
+
+ if(messageLen32 == 0 || messageLen32 > MAX_MESSAGE_SIZE){
+ DEBUG("Message Size(words) = " << messageLen32);
+ reportError(callbackObj, remoteNodeId, TE_INVALID_MESSAGE_LENGTH);
+ return readPtr;
+ }//if
+
+ if(Protocol6::getCheckSumIncluded(word1)){
+ const Uint32 tmpLen = messageLen32 - 1;
+ const Uint32 checkSumSent = readPtr[tmpLen];
+ const Uint32 checkSumComputed = computeChecksum(&readPtr[0], tmpLen);
+
+ if(checkSumComputed != checkSumSent){
+ reportError(callbackObj, remoteNodeId, TE_INVALID_CHECKSUM);
+ return readPtr;
+ }//if
+ }//if
+
+#if 0
+ if(Protocol6::getCompressed(word1)){
+ //Do funky stuff
+ }//if
+#endif
+
+ Protocol6::createSignalHeader(&signalHeader, word1, word2, word3);
+
+ Uint32 sBlockNum = signalHeader.theSendersBlockRef;
+ sBlockNum = numberToRef(sBlockNum, remoteNodeId);
+ signalHeader.theSendersBlockRef = sBlockNum;
+
+ Uint8 prio = Protocol6::getPrio(word1);
+
+ Uint32 * signalData = &readPtr[3];
+
+ if(Protocol6::getSignalIdIncluded(word1) == 0){
+ signalHeader.theSendersSignalId = ~0;
+ } else {
+ signalHeader.theSendersSignalId = * signalData;
+ signalData ++;
+ }//if
+
+ Uint32 * sectionPtr = signalData + signalHeader.theLength;
+ Uint32 * sectionData = sectionPtr + signalHeader.m_noOfSections;
+ for(Uint32 i = 0; i<signalHeader.m_noOfSections; i++){
+ Uint32 sz = * sectionPtr;
+ ptr[i].sz = sz;
+ ptr[i].p = sectionData;
+
+ sectionPtr ++;
+ sectionData += sz;
+ }
+
+ execute(callbackObj, &signalHeader, prio, signalData, ptr);
+
+ readPtr += messageLen32;
+ }//while
+ } else {
+ /** state = HaltIO || state == HaltInput */
+
+ while ((readPtr < eodPtr) && (loop_count < MAX_RECEIVED_SIGNALS)) {
+ Uint32 word1 = readPtr[0];
+ Uint32 word2 = readPtr[1];
+ Uint32 word3 = readPtr[2];
+ loop_count++;
+#if 0
+ if(Protocol6::getByteOrder(word1) != MY_OWN_BYTE_ORDER){
+ //Do funky stuff
+ }//if
+#endif
+
+ const Uint16 messageLen32 = Protocol6::getMessageLength(word1);
+ if(messageLen32 == 0 || messageLen32 > MAX_MESSAGE_SIZE){
+ DEBUG("Message Size(words) = " << messageLen32);
+ reportError(callbackObj, remoteNodeId, TE_INVALID_MESSAGE_LENGTH);
+ return readPtr;
+ }//if
+
+ if(Protocol6::getCheckSumIncluded(word1)){
+ const Uint32 tmpLen = messageLen32 - 1;
+ const Uint32 checkSumSent = readPtr[tmpLen];
+ const Uint32 checkSumComputed = computeChecksum(&readPtr[0], tmpLen);
+
+ if(checkSumComputed != checkSumSent){
+
+ //theTransporters[remoteNodeId]->disconnect();
+ reportError(callbackObj, remoteNodeId, TE_INVALID_CHECKSUM);
+ return readPtr;
+ }//if
+ }//if
+
+#if 0
+ if(Protocol6::getCompressed(word1)){
+ //Do funky stuff
+ }//if
+#endif
+
+ Protocol6::createSignalHeader(&signalHeader, word1, word2, word3);
+
+ Uint32 rBlockNum = signalHeader.theReceiversBlockNumber;
+
+ if(rBlockNum == 252){
+ Uint32 sBlockNum = signalHeader.theSendersBlockRef;
+ sBlockNum = numberToRef(sBlockNum, remoteNodeId);
+ signalHeader.theSendersBlockRef = sBlockNum;
+
+ Uint8 prio = Protocol6::getPrio(word1);
+
+ Uint32 * signalData = &readPtr[3];
+
+ if(Protocol6::getSignalIdIncluded(word1) == 0){
+ signalHeader.theSendersSignalId = ~0;
+ } else {
+ signalHeader.theSendersSignalId = * signalData;
+ signalData ++;
+ }//if
+
+ Uint32 * sectionPtr = signalData + signalHeader.theLength;
+ Uint32 * sectionData = sectionPtr + signalHeader.m_noOfSections;
+ for(Uint32 i = 0; i<signalHeader.m_noOfSections; i++){
+ Uint32 sz = * sectionPtr;
+ ptr[i].sz = sz;
+ ptr[i].p = sectionData;
+
+ sectionPtr ++;
+ sectionData += sz;
+ }
+
+ execute(callbackObj, &signalHeader, prio, signalData, ptr);
+ } else {
+ DEBUG("prepareReceive(...) - Discarding message to block: "
+ << rBlockNum << " from Node: " << remoteNodeId);
+ }//if
+
+ readPtr += messageLen32;
+ }//while
+ }//if
+ return readPtr;
+}
+
+Packer::Packer(bool signalId, bool checksum) {
+
+ checksumUsed = (checksum ? 1 : 0);
+ signalIdUsed = (signalId ? 1 : 0);
+
+ // Set the priority
+
+ preComputedWord1 = 0;
+ Protocol6::setByteOrder(preComputedWord1, 0);
+ Protocol6::setSignalIdIncluded(preComputedWord1, signalIdUsed);
+ Protocol6::setCheckSumIncluded(preComputedWord1, checksumUsed);
+ Protocol6::setCompressed(preComputedWord1, 0);
+}
+
+inline
+void
+import(Uint32 * & insertPtr, const LinearSectionPtr & ptr){
+ const Uint32 sz = ptr.sz;
+ memcpy(insertPtr, ptr.p, 4 * sz);
+ insertPtr += sz;
+}
+
+void copy(Uint32 * & insertPtr,
+ class SectionSegmentPool &, const SegmentedSectionPtr & ptr);
+
+void
+Packer::pack(Uint32 * insertPtr,
+ Uint32 prio,
+ const SignalHeader * header,
+ const Uint32 * theData,
+ const LinearSectionPtr ptr[3]) const {
+ Uint32 i;
+
+ Uint32 dataLen32 = header->theLength;
+ Uint32 no_segs = header->m_noOfSections;
+
+ Uint32 len32 =
+ dataLen32 + no_segs +
+ checksumUsed + signalIdUsed + (sizeof(Protocol6)/4);
+
+
+ for(i = 0; i<no_segs; i++){
+ len32 += ptr[i].sz;
+ }
+
+ /**
+ * Do insert of data
+ */
+ Uint32 word1 = preComputedWord1;
+ Uint32 word2 = 0;
+ Uint32 word3 = 0;
+
+ Protocol6::setPrio(word1, prio);
+ Protocol6::setMessageLength(word1, len32);
+ Protocol6::createProtocol6Header(word1, word2, word3, header);
+
+ insertPtr[0] = word1;
+ insertPtr[1] = word2;
+ insertPtr[2] = word3;
+
+ Uint32 * tmpInserPtr = &insertPtr[3];
+
+ if(signalIdUsed){
+ * tmpInserPtr = header->theSignalId;
+ tmpInserPtr++;
+ }
+
+ memcpy(tmpInserPtr, theData, 4 * dataLen32);
+
+ tmpInserPtr += dataLen32;
+ for(i = 0; i<no_segs; i++){
+ tmpInserPtr[i] = ptr[i].sz;
+ }
+
+ tmpInserPtr += no_segs;
+ for(i = 0; i<no_segs; i++){
+ import(tmpInserPtr, ptr[i]);
+ }
+
+ if(checksumUsed){
+ * tmpInserPtr = computeChecksum(&insertPtr[0], len32-1);
+ }
+}
+
+void
+Packer::pack(Uint32 * insertPtr,
+ Uint32 prio,
+ const SignalHeader * header,
+ const Uint32 * theData,
+ class SectionSegmentPool & thePool,
+ const SegmentedSectionPtr ptr[3]) const {
+ Uint32 i;
+
+ Uint32 dataLen32 = header->theLength;
+ Uint32 no_segs = header->m_noOfSections;
+
+ Uint32 len32 =
+ dataLen32 + no_segs +
+ checksumUsed + signalIdUsed + (sizeof(Protocol6)/4);
+
+ for(i = 0; i<no_segs; i++){
+ len32 += ptr[i].sz;
+ }
+
+ /**
+ * Do insert of data
+ */
+ Uint32 word1 = preComputedWord1;
+ Uint32 word2 = 0;
+ Uint32 word3 = 0;
+
+ Protocol6::setPrio(word1, prio);
+ Protocol6::setMessageLength(word1, len32);
+ Protocol6::createProtocol6Header(word1, word2, word3, header);
+
+ insertPtr[0] = word1;
+ insertPtr[1] = word2;
+ insertPtr[2] = word3;
+
+ Uint32 * tmpInserPtr = &insertPtr[3];
+
+ if(signalIdUsed){
+ * tmpInserPtr = header->theSignalId;
+ tmpInserPtr++;
+ }
+
+ memcpy(tmpInserPtr, theData, 4 * dataLen32);
+
+ tmpInserPtr += dataLen32;
+ for(i = 0; i<no_segs; i++){
+ tmpInserPtr[i] = ptr[i].sz;
+ }
+
+ tmpInserPtr += no_segs;
+ for(i = 0; i<no_segs; i++){
+ copy(tmpInserPtr, thePool, ptr[i]);
+ }
+
+ if(checksumUsed){
+ * tmpInserPtr = computeChecksum(&insertPtr[0], len32-1);
+ }
+}
diff --git a/storage/ndb/src/common/transporter/Packer.hpp b/storage/ndb/src/common/transporter/Packer.hpp
new file mode 100644
index 00000000000..5c191203201
--- /dev/null
+++ b/storage/ndb/src/common/transporter/Packer.hpp
@@ -0,0 +1,85 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 PACKER_HPP
+#define PACKER_HPP
+
+#include <TransporterDefinitions.hpp>
+#include "TransporterInternalDefinitions.hpp"
+
+class Packer {
+ Uint32 preComputedWord1;
+ Uint32 checksumUsed; // Checksum shall be included in the message
+ Uint32 signalIdUsed; // Senders signal id shall be included in the message
+public:
+ Packer(bool signalId, bool checksum);
+
+ Uint32 getMessageLength(const SignalHeader* header,
+ const LinearSectionPtr ptr[3]) const ;
+
+
+ Uint32 getMessageLength(const SignalHeader* header,
+ const SegmentedSectionPtr ptr[3]) const ;
+
+ void pack(Uint32 * insertPtr,
+ Uint32 prio,
+ const SignalHeader* header,
+ const Uint32* data,
+ const LinearSectionPtr ptr[3]) const ;
+
+ void pack(Uint32 * insertPtr,
+ Uint32 prio,
+ const SignalHeader* header,
+ const Uint32* data,
+ class SectionSegmentPool & thePool,
+ const SegmentedSectionPtr ptr[3]) const ;
+};
+
+inline
+Uint32
+Packer::getMessageLength(const SignalHeader* header,
+ const LinearSectionPtr ptr[3]) const {
+ Uint32 tLen32 = header->theLength;
+ Uint32 no_seg = header->m_noOfSections;
+ tLen32 += checksumUsed;
+ tLen32 += signalIdUsed;
+ tLen32 += no_seg;
+
+ for(Uint32 i = 0; i<no_seg; i++){
+ tLen32 += ptr[i].sz;
+ }
+
+ return (tLen32 * 4) + sizeof(Protocol6);
+}
+
+inline
+Uint32
+Packer::getMessageLength(const SignalHeader* header,
+ const SegmentedSectionPtr ptr[3]) const {
+ Uint32 tLen32 = header->theLength;
+ Uint32 no_seg = header->m_noOfSections;
+ tLen32 += checksumUsed;
+ tLen32 += signalIdUsed;
+ tLen32 += no_seg;
+
+ for(Uint32 i = 0; i<no_seg; i++){
+ tLen32 += ptr[i].sz;
+ }
+
+ return (tLen32 * 4) + sizeof(Protocol6);
+}
+
+#endif
diff --git a/storage/ndb/src/common/transporter/SCI_Transporter.cpp b/storage/ndb/src/common/transporter/SCI_Transporter.cpp
new file mode 100644
index 00000000000..506140a887f
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SCI_Transporter.cpp
@@ -0,0 +1,1031 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "SCI_Transporter.hpp"
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbTick.h>
+
+#include "TransporterInternalDefinitions.hpp"
+#include <TransporterCallback.hpp>
+
+#include <InputStream.hpp>
+#include <OutputStream.hpp>
+
+#define FLAGS 0
+#define DEBUG_TRANSPORTER
+SCI_Transporter::SCI_Transporter(TransporterRegistry &t_reg,
+ const char *lHostName,
+ const char *rHostName,
+ int r_port,
+ bool isMgmConnection,
+ Uint32 packetSize,
+ Uint32 bufferSize,
+ Uint32 nAdapters,
+ Uint16 remoteSciNodeId0,
+ Uint16 remoteSciNodeId1,
+ NodeId _localNodeId,
+ NodeId _remoteNodeId,
+ NodeId serverNodeId,
+ bool chksm,
+ bool signalId,
+ Uint32 reportFreq) :
+ Transporter(t_reg, tt_SCI_TRANSPORTER,
+ lHostName, rHostName, r_port, isMgmConnection, _localNodeId,
+ _remoteNodeId, serverNodeID, 0, false, chksm, signalId)
+{
+ DBUG_ENTER("SCI_Transporter::SCI_Transporter");
+ m_PacketSize = (packetSize + 3)/4 ;
+ m_BufferSize = bufferSize;
+ m_sendBuffer.m_buffer = NULL;
+
+ m_RemoteSciNodeId = remoteSciNodeId0;
+
+ if(remoteSciNodeId0 == 0 || remoteSciNodeId1 == 0)
+ m_numberOfRemoteNodes=1;
+ else
+ m_numberOfRemoteNodes=2;
+
+ m_RemoteSciNodeId1 = remoteSciNodeId1;
+
+
+ m_initLocal=false;
+ m_swapCounter=0;
+ m_failCounter=0;
+ m_remoteNodes[0]=remoteSciNodeId0;
+ m_remoteNodes[1]=remoteSciNodeId1;
+ m_adapters = nAdapters;
+ // The maximum number of times to try and create,
+ // start and destroy a sequence
+ m_ActiveAdapterId=0;
+ m_StandbyAdapterId=1;
+
+ m_mapped = false;
+ m_sciinit=false;
+
+ sciAdapters= new SciAdapter[nAdapters* (sizeof (SciAdapter))];
+ if(sciAdapters==NULL) {
+ }
+ m_SourceSegm= new sourceSegm[nAdapters* (sizeof (sourceSegm))];
+ if(m_SourceSegm==NULL) {
+ }
+ m_TargetSegm= new targetSegm[nAdapters* (sizeof (targetSegm))];
+ if(m_TargetSegm==NULL) {
+ }
+ m_reportFreq= reportFreq;
+
+ //reset all statistic counters.
+#ifdef DEBUG_TRANSPORTER
+ i1024=0;
+ i2048=0;
+ i2049=0;
+ i10242048=0;
+ i20484096=0;
+ i4096=0;
+ i4097=0;
+#endif
+ DBUG_VOID_RETURN;
+}
+
+
+
+void SCI_Transporter::disconnectImpl()
+{
+ DBUG_ENTER("SCI_Transporter::disconnectImpl");
+ sci_error_t err;
+ if(m_mapped){
+ setDisconnect();
+ DBUG_PRINT("info", ("connect status = %d, remote node = %d",
+ (int)getConnectionStatus(), remoteNodeId));
+ disconnectRemote();
+ disconnectLocal();
+ }
+
+ // Empty send buffer
+
+ m_sendBuffer.m_dataSize = 0;
+
+ m_initLocal=false;
+ m_mapped = false;
+
+ if(m_sciinit) {
+ for(Uint32 i=0; i<m_adapters ; i++) {
+ SCIClose(sciAdapters[i].scidesc, FLAGS, &err);
+
+ if(err != SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_CLOSE_CHANNEL);
+ DBUG_PRINT("error", ("Cannot close channel to the driver. Error code 0x%x",
+ err));
+ }
+ }
+ }
+ m_sciinit=false;
+
+#ifdef DEBUG_TRANSPORTER
+ ndbout << "total: " << i1024+ i10242048 + i2048+i2049 << endl;
+ ndbout << "<1024: " << i1024 << endl;
+ ndbout << "1024-2047: " << i10242048 << endl;
+ ndbout << "==2048: " << i2048 << endl;
+ ndbout << "2049-4096: " << i20484096 << endl;
+ ndbout << "==4096: " << i4096 << endl;
+ ndbout << ">4096: " << i4097 << endl;
+#endif
+ DBUG_VOID_RETURN;
+}
+
+
+bool SCI_Transporter::initTransporter() {
+ DBUG_ENTER("SCI_Transporter::initTransporter");
+ if(m_BufferSize < (2*MAX_MESSAGE_SIZE + 4096)){
+ m_BufferSize = 2 * MAX_MESSAGE_SIZE + 4096;
+ }
+
+ // Allocate buffers for sending, send buffer size plus 2048 bytes for avoiding
+ // the need to send twice when a large message comes around. Send buffer size is
+ // measured in words.
+ Uint32 sz = 4 * m_PacketSize + MAX_MESSAGE_SIZE;;
+
+ m_sendBuffer.m_sendBufferSize = 4 * ((sz + 3) / 4);
+ m_sendBuffer.m_buffer = new Uint32[m_sendBuffer.m_sendBufferSize / 4];
+ m_sendBuffer.m_dataSize = 0;
+
+ DBUG_PRINT("info", ("Created SCI Send Buffer with buffer size %d and packet size %d",
+ m_sendBuffer.m_sendBufferSize, m_PacketSize * 4));
+ if(!getLinkStatus(m_ActiveAdapterId) ||
+ (m_adapters > 1 &&
+ !getLinkStatus(m_StandbyAdapterId))) {
+ DBUG_PRINT("error", ("The link is not fully operational. Check the cables and the switches"));
+ //reportDisconnect(remoteNodeId, 0);
+ //doDisconnect();
+ //NDB should terminate
+ report_error(TE_SCI_LINK_ERROR);
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+} // initTransporter()
+
+
+
+Uint32 SCI_Transporter::getLocalNodeId(Uint32 adapterNo)
+{
+ sci_query_adapter_t queryAdapter;
+ sci_error_t error;
+ Uint32 _localNodeId;
+
+ queryAdapter.subcommand = SCI_Q_ADAPTER_NODEID;
+ queryAdapter.localAdapterNo = adapterNo;
+ queryAdapter.data = &_localNodeId;
+
+ SCIQuery(SCI_Q_ADAPTER,(void*)(&queryAdapter),(Uint32)NULL,&error);
+
+ if(error != SCI_ERR_OK)
+ return 0;
+ return _localNodeId;
+}
+
+
+bool SCI_Transporter::getLinkStatus(Uint32 adapterNo)
+{
+ sci_query_adapter_t queryAdapter;
+ sci_error_t error;
+ int linkstatus;
+ queryAdapter.subcommand = SCI_Q_ADAPTER_LINK_OPERATIONAL;
+
+ queryAdapter.localAdapterNo = adapterNo;
+ queryAdapter.data = &linkstatus;
+
+ SCIQuery(SCI_Q_ADAPTER,(void*)(&queryAdapter),(Uint32)NULL,&error);
+
+ if(error != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("error %d querying adapter", error));
+ return false;
+ }
+ if(linkstatus<=0)
+ return false;
+ return true;
+}
+
+
+
+sci_error_t SCI_Transporter::initLocalSegment() {
+ DBUG_ENTER("SCI_Transporter::initLocalSegment");
+ Uint32 segmentSize = m_BufferSize;
+ Uint32 offset = 0;
+ sci_error_t err;
+ if(!m_sciinit) {
+ for(Uint32 i=0; i<m_adapters ; i++) {
+ SCIOpen(&(sciAdapters[i].scidesc), FLAGS, &err);
+ sciAdapters[i].localSciNodeId=getLocalNodeId(i);
+ DBUG_PRINT("info", ("SCInode iD %d adapter %d\n",
+ sciAdapters[i].localSciNodeId, i));
+ if(err != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Cannot open an SCI virtual device. Error code 0x%x",
+ err));
+ DBUG_RETURN(err);
+ }
+ }
+ }
+
+ m_sciinit=true;
+
+ SCICreateSegment(sciAdapters[0].scidesc,
+ &(m_SourceSegm[0].localHandle),
+ hostSegmentId(localNodeId, remoteNodeId),
+ segmentSize,
+ 0,
+ 0,
+ 0,
+ &err);
+
+ if(err != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Error creating segment, err = 0x%x", err));
+ DBUG_RETURN(err);
+ } else {
+ DBUG_PRINT("info", ("created segment id : %d",
+ hostSegmentId(localNodeId, remoteNodeId)));
+ }
+
+ /** Prepare the segment*/
+ for(Uint32 i=0; i < m_adapters; i++) {
+ SCIPrepareSegment((m_SourceSegm[0].localHandle),
+ i,
+ FLAGS,
+ &err);
+
+ if(err != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Local Segment is not accessible by an SCI adapter. Error code 0x%x\n",
+ err));
+ DBUG_RETURN(err);
+ }
+ }
+
+
+ m_SourceSegm[0].mappedMemory =
+ SCIMapLocalSegment((m_SourceSegm[0].localHandle),
+ &(m_SourceSegm[0].lhm[0].map),
+ offset,
+ segmentSize,
+ NULL,
+ FLAGS,
+ &err);
+
+
+
+ if(err != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Cannot map area of size %d. Error code 0x%x",
+ segmentSize,err));
+ doDisconnect();
+ DBUG_RETURN(err);
+ }
+
+
+ /** Make the local segment available*/
+ for(Uint32 i=0; i < m_adapters; i++) {
+ SCISetSegmentAvailable((m_SourceSegm[0].localHandle),
+ i,
+ FLAGS,
+ &err);
+
+ if(err != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Local Segment is not available for remote connections. Error code 0x%x\n",
+ err));
+ DBUG_RETURN(err);
+ }
+ }
+
+
+ setupLocalSegment();
+
+ DBUG_RETURN(err);
+
+} // initLocalSegment()
+
+
+bool SCI_Transporter::doSend() {
+#ifdef DEBUG_TRANSPORTER
+ NDB_TICKS startSec=0, stopSec=0;
+ Uint32 startMicro=0, stopMicro=0, totalMicro=0;
+#endif
+ sci_error_t err;
+ Uint32 retry=0;
+
+ const char * const sendPtr = (char*)m_sendBuffer.m_buffer;
+ const Uint32 sizeToSend = 4 * m_sendBuffer.m_dataSize; //Convert to number of bytes
+
+ if (sizeToSend > 0){
+#ifdef DEBUG_TRANSPORTER
+ if(sizeToSend < 1024 )
+ i1024++;
+ if(sizeToSend > 1024 && sizeToSend < 2048 )
+ i10242048++;
+ if(sizeToSend==2048)
+ i2048++;
+ if(sizeToSend>2048 && sizeToSend < 4096)
+ i20484096++;
+ if(sizeToSend==4096)
+ i4096++;
+ if(sizeToSend==4097)
+ i4097++;
+#endif
+ if(startSequence(m_ActiveAdapterId)!=SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Start sequence failed"));
+ report_error(TE_SCI_UNABLE_TO_START_SEQUENCE);
+ return false;
+ }
+
+
+ tryagain:
+ retry++;
+ if (retry > 3) {
+ DBUG_PRINT("error", ("SCI Transfer failed"));
+ report_error(TE_SCI_UNRECOVERABLE_DATA_TFX_ERROR);
+ return false;
+ }
+ Uint32 * insertPtr = (Uint32 *)
+ (m_TargetSegm[m_ActiveAdapterId].writer)->getWritePtr(sizeToSend);
+
+ if(insertPtr != 0) {
+
+ const Uint32 remoteOffset=(Uint32)
+ ((char*)insertPtr -
+ (char*)(m_TargetSegm[m_ActiveAdapterId].mappedMemory));
+
+ SCIMemCpy(m_TargetSegm[m_ActiveAdapterId].sequence,
+ (void*)sendPtr,
+ m_TargetSegm[m_ActiveAdapterId].rhm[m_ActiveAdapterId].map,
+ remoteOffset,
+ sizeToSend,
+ SCI_FLAG_ERROR_CHECK,
+ &err);
+
+
+ if (err != SCI_ERR_OK) {
+ if(err == SCI_ERR_OUT_OF_RANGE) {
+ DBUG_PRINT("error", ("Data transfer : out of range error"));
+ goto tryagain;
+ }
+ if(err == SCI_ERR_SIZE_ALIGNMENT) {
+ DBUG_PRINT("error", ("Data transfer : alignment error"));
+ DBUG_PRINT("info", ("sendPtr 0x%x, sizeToSend = %d", sendPtr, sizeToSend));
+ goto tryagain;
+ }
+ if(err == SCI_ERR_OFFSET_ALIGNMENT) {
+ DBUG_PRINT("error", ("Data transfer : offset alignment"));
+ goto tryagain;
+ }
+ if(err == SCI_ERR_TRANSFER_FAILED) {
+ //(m_TargetSegm[m_StandbyAdapterId].writer)->heavyLock();
+ if(getLinkStatus(m_ActiveAdapterId)) {
+ goto tryagain;
+ }
+ if (m_adapters == 1) {
+ DBUG_PRINT("error", ("SCI Transfer failed"));
+ report_error(TE_SCI_UNRECOVERABLE_DATA_TFX_ERROR);
+ return false;
+ }
+ m_failCounter++;
+ Uint32 temp=m_ActiveAdapterId;
+ switch(m_swapCounter) {
+ case 0:
+ /**swap from active (0) to standby (1)*/
+ if(getLinkStatus(m_StandbyAdapterId)) {
+ DBUG_PRINT("error", ("Swapping from adapter 0 to 1"));
+ failoverShmWriter();
+ SCIStoreBarrier(m_TargetSegm[m_StandbyAdapterId].sequence,0);
+ m_ActiveAdapterId=m_StandbyAdapterId;
+ m_StandbyAdapterId=temp;
+ SCIRemoveSequence((m_TargetSegm[m_StandbyAdapterId].sequence),
+ FLAGS,
+ &err);
+ if(err!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_REMOVE_SEQUENCE);
+ DBUG_PRINT("error", ("Unable to remove sequence"));
+ return false;
+ }
+ if(startSequence(m_ActiveAdapterId)!=SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Start sequence failed"));
+ report_error(TE_SCI_UNABLE_TO_START_SEQUENCE);
+ return false;
+ }
+ m_swapCounter++;
+ DBUG_PRINT("info", ("failover complete"));
+ goto tryagain;
+ } else {
+ report_error(TE_SCI_UNRECOVERABLE_DATA_TFX_ERROR);
+ DBUG_PRINT("error", ("SCI Transfer failed"));
+ return false;
+ }
+ return false;
+ break;
+ case 1:
+ /** swap back from 1 to 0
+ must check that the link is up */
+
+ if(getLinkStatus(m_StandbyAdapterId)) {
+ failoverShmWriter();
+ m_ActiveAdapterId=m_StandbyAdapterId;
+ m_StandbyAdapterId=temp;
+ DBUG_PRINT("info", ("Swapping from 1 to 0"));
+ if(createSequence(m_ActiveAdapterId)!=SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Unable to create sequence"));
+ report_error(TE_SCI_UNABLE_TO_CREATE_SEQUENCE);
+ return false;
+ }
+ if(startSequence(m_ActiveAdapterId)!=SCI_ERR_OK) {
+ DBUG_PRINT("error", ("startSequence failed... disconnecting"));
+ report_error(TE_SCI_UNABLE_TO_START_SEQUENCE);
+ return false;
+ }
+
+ SCIRemoveSequence((m_TargetSegm[m_StandbyAdapterId].sequence)
+ , FLAGS,
+ &err);
+ if(err!=SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Unable to remove sequence"));
+ report_error(TE_SCI_UNABLE_TO_REMOVE_SEQUENCE);
+ return false;
+ }
+
+ if(createSequence(m_StandbyAdapterId)!=SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Unable to create sequence on standby"));
+ report_error(TE_SCI_UNABLE_TO_CREATE_SEQUENCE);
+ return false;
+ }
+
+ m_swapCounter=0;
+
+ DBUG_PRINT("info", ("failover complete.."));
+ goto tryagain;
+
+ } else {
+ DBUG_PRINT("error", ("Unrecoverable data transfer error"));
+ report_error(TE_SCI_UNRECOVERABLE_DATA_TFX_ERROR);
+ return false;
+ }
+
+ break;
+ default:
+ DBUG_PRINT("error", ("Unrecoverable data transfer error"));
+ report_error(TE_SCI_UNRECOVERABLE_DATA_TFX_ERROR);
+ return false;
+ break;
+ }
+ }
+ } else {
+ SHM_Writer * writer = (m_TargetSegm[m_ActiveAdapterId].writer);
+ writer->updateWritePtr(sizeToSend);
+
+ Uint32 sendLimit = writer->getBufferSize();
+ sendLimit -= writer->getWriteIndex();
+
+ m_sendBuffer.m_dataSize = 0;
+ m_sendBuffer.m_forceSendLimit = sendLimit;
+ }
+
+ } else {
+ /**
+ * If we end up here, the SCI segment is full.
+ */
+ DBUG_PRINT("error", ("the segment is full for some reason"));
+ return false;
+ } //if
+ }
+ return true;
+} // doSend()
+
+
+
+void SCI_Transporter::failoverShmWriter() {
+#if 0
+ (m_TargetSegm[m_StandbyAdapterId].writer)
+ ->copyIndexes((m_TargetSegm[m_StandbyAdapterId].writer));
+#endif
+} //failoverShm
+
+
+void SCI_Transporter::setupLocalSegment()
+{
+ DBUG_ENTER("SCI_Transporter::setupLocalSegment");
+ Uint32 sharedSize = 0;
+ sharedSize =4096; //start of the buffer is page aligend
+
+ Uint32 sizeOfBuffer = m_BufferSize;
+
+ sizeOfBuffer -= sharedSize;
+
+ Uint32 * localReadIndex =
+ (Uint32*)m_SourceSegm[m_ActiveAdapterId].mappedMemory;
+ Uint32 * localWriteIndex = (Uint32*)(localReadIndex+ 1);
+ m_localStatusFlag = (Uint32*)(localReadIndex + 3);
+
+ char * localStartOfBuf = (char*)
+ ((char*)m_SourceSegm[m_ActiveAdapterId].mappedMemory+sharedSize);
+
+ * localReadIndex = 0;
+ * localWriteIndex = 0;
+
+ const Uint32 slack = MAX_MESSAGE_SIZE;
+
+ reader = new SHM_Reader(localStartOfBuf,
+ sizeOfBuffer,
+ slack,
+ localReadIndex,
+ localWriteIndex);
+
+ reader->clear();
+ DBUG_VOID_RETURN;
+} //setupLocalSegment
+
+
+
+void SCI_Transporter::setupRemoteSegment()
+{
+ DBUG_ENTER("SCI_Transporter::setupRemoteSegment");
+ Uint32 sharedSize = 0;
+ sharedSize =4096; //start of the buffer is page aligned
+
+
+ Uint32 sizeOfBuffer = m_BufferSize;
+ const Uint32 slack = MAX_MESSAGE_SIZE;
+ sizeOfBuffer -= sharedSize;
+
+ Uint32 *segPtr = (Uint32*) m_TargetSegm[m_ActiveAdapterId].mappedMemory ;
+
+ Uint32 * remoteReadIndex = (Uint32*)segPtr;
+ Uint32 * remoteWriteIndex = (Uint32*)(segPtr + 1);
+ m_remoteStatusFlag = (Uint32*)(segPtr + 3);
+
+ char * remoteStartOfBuf = ( char*)((char*)segPtr+(sharedSize));
+
+ writer = new SHM_Writer(remoteStartOfBuf,
+ sizeOfBuffer,
+ slack,
+ remoteReadIndex,
+ remoteWriteIndex);
+
+ writer->clear();
+
+ m_TargetSegm[0].writer=writer;
+
+ m_sendBuffer.m_forceSendLimit = writer->getBufferSize();
+
+ if(createSequence(m_ActiveAdapterId)!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_CREATE_SEQUENCE);
+ DBUG_PRINT("error", ("Unable to create sequence on active"));
+ doDisconnect();
+ }
+ if (m_adapters > 1) {
+ segPtr = (Uint32*) m_TargetSegm[m_StandbyAdapterId].mappedMemory ;
+
+ Uint32 * remoteReadIndex2 = (Uint32*)segPtr;
+ Uint32 * remoteWriteIndex2 = (Uint32*) (segPtr + 1);
+ m_remoteStatusFlag2 = (Uint32*)(segPtr + 3);
+
+ char * remoteStartOfBuf2 = ( char*)((char *)segPtr+sharedSize);
+
+ /**
+ * setup a writer. writer2 is used to mirror the changes of
+ * writer on the standby
+ * segment, so that in the case of a failover, we can switch
+ * to the stdby seg. quickly.*
+ */
+ writer2 = new SHM_Writer(remoteStartOfBuf2,
+ sizeOfBuffer,
+ slack,
+ remoteReadIndex2,
+ remoteWriteIndex2);
+
+ * remoteReadIndex = 0;
+ * remoteWriteIndex = 0;
+ writer2->clear();
+ m_TargetSegm[1].writer=writer2;
+ if(createSequence(m_StandbyAdapterId)!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_CREATE_SEQUENCE);
+ DBUG_PRINT("error", ("Unable to create sequence on standby"));
+ doDisconnect();
+ }
+ }
+ DBUG_VOID_RETURN;
+} //setupRemoteSegment
+
+bool
+SCI_Transporter::init_local()
+{
+ DBUG_ENTER("SCI_Transporter::init_local");
+ if(!m_initLocal) {
+ if(initLocalSegment()!=SCI_ERR_OK){
+ NdbSleep_MilliSleep(10);
+ //NDB SHOULD TERMINATE AND COMPUTER REBOOTED!
+ report_error(TE_SCI_CANNOT_INIT_LOCALSEGMENT);
+ DBUG_RETURN(false);
+ }
+ m_initLocal=true;
+ }
+ DBUG_RETURN(true);
+}
+
+bool
+SCI_Transporter::init_remote()
+{
+ DBUG_ENTER("SCI_Transporter::init_remote");
+ sci_error_t err;
+ Uint32 offset = 0;
+ if(!m_mapped ) {
+ DBUG_PRINT("info", ("Map remote segments"));
+ for(Uint32 i=0; i < m_adapters ; i++) {
+ m_TargetSegm[i].rhm[i].remoteHandle=0;
+ SCIConnectSegment(sciAdapters[i].scidesc,
+ &(m_TargetSegm[i].rhm[i].remoteHandle),
+ m_remoteNodes[i],
+ remoteSegmentId(localNodeId, remoteNodeId),
+ i,
+ 0,
+ 0,
+ 0,
+ 0,
+ &err);
+
+ if(err != SCI_ERR_OK) {
+ NdbSleep_MilliSleep(10);
+ DBUG_PRINT("error", ("Error connecting segment, err 0x%x", err));
+ DBUG_RETURN(false);
+ }
+
+ }
+ // Map the remote memory segment into program space
+ for(Uint32 i=0; i < m_adapters ; i++) {
+ m_TargetSegm[i].mappedMemory =
+ SCIMapRemoteSegment((m_TargetSegm[i].rhm[i].remoteHandle),
+ &(m_TargetSegm[i].rhm[i].map),
+ offset,
+ m_BufferSize,
+ NULL,
+ FLAGS,
+ &err);
+
+
+ if(err!= SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Cannot map a segment to the remote node %d. Error code 0x%x",m_RemoteSciNodeId, err));
+ //NDB SHOULD TERMINATE AND COMPUTER REBOOTED!
+ report_error(TE_SCI_CANNOT_MAP_REMOTESEGMENT);
+ DBUG_RETURN(false);
+ }
+ }
+ m_mapped=true;
+ setupRemoteSegment();
+ setConnected();
+ DBUG_PRINT("info", ("connected and mapped to segment, remoteNode: %d",
+ remoteNodeId));
+ DBUG_PRINT("info", ("remoteSegId: %d",
+ remoteSegmentId(localNodeId, remoteNodeId)));
+ DBUG_RETURN(true);
+ } else {
+ DBUG_RETURN(getConnectionStatus());
+ }
+}
+
+bool
+SCI_Transporter::connect_client_impl(NDB_SOCKET_TYPE sockfd)
+{
+ SocketInputStream s_input(sockfd);
+ SocketOutputStream s_output(sockfd);
+ char buf[256];
+ DBUG_ENTER("SCI_Transporter::connect_client_impl");
+ // Wait for server to create and attach
+ if (s_input.gets(buf, 256) == 0) {
+ DBUG_PRINT("error", ("No initial response from server in SCI"));
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ if (!init_local()) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ // Send ok to server
+ s_output.println("sci client 1 ok");
+
+ if (!init_remote()) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ // Wait for ok from server
+ if (s_input.gets(buf, 256) == 0) {
+ DBUG_PRINT("error", ("No second response from server in SCI"));
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ // Send ok to server
+ s_output.println("sci client 2 ok");
+
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_PRINT("info", ("Successfully connected client to node %d",
+ remoteNodeId));
+ DBUG_RETURN(true);
+}
+
+bool
+SCI_Transporter::connect_server_impl(NDB_SOCKET_TYPE sockfd)
+{
+ SocketOutputStream s_output(sockfd);
+ SocketInputStream s_input(sockfd);
+ char buf[256];
+ DBUG_ENTER("SCI_Transporter::connect_server_impl");
+
+ if (!init_local()) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ // Send ok to client
+ s_output.println("sci server 1 ok");
+
+ // Wait for ok from client
+ if (s_input.gets(buf, 256) == 0) {
+ DBUG_PRINT("error", ("No response from client in SCI"));
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ if (!init_remote()) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ // Send ok to client
+ s_output.println("sci server 2 ok");
+ // Wait for ok from client
+ if (s_input.gets(buf, 256) == 0) {
+ DBUG_PRINT("error", ("No second response from client in SCI"));
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_PRINT("info", ("Successfully connected server to node %d",
+ remoteNodeId));
+ DBUG_RETURN(true);
+}
+
+sci_error_t SCI_Transporter::createSequence(Uint32 adapterid) {
+ sci_error_t err;
+ SCICreateMapSequence((m_TargetSegm[adapterid].rhm[adapterid].map),
+ &(m_TargetSegm[adapterid].sequence),
+ SCI_FLAG_FAST_BARRIER,
+ &err);
+
+
+ return err;
+} // createSequence()
+
+
+sci_error_t SCI_Transporter::startSequence(Uint32 adapterid) {
+
+ sci_error_t err;
+ /** Perform preliminary error check on an SCI adapter before starting a
+ * sequence of read and write operations on the mapped segment.
+ */
+ m_SequenceStatus = SCIStartSequence(
+ (m_TargetSegm[adapterid].sequence),
+ FLAGS, &err);
+
+
+ // If there still is an error then data cannot be safely send
+ return err;
+} // startSequence()
+
+
+
+bool SCI_Transporter::disconnectLocal()
+{
+ DBUG_ENTER("SCI_Transporter::disconnectLocal");
+ sci_error_t err;
+ m_ActiveAdapterId=0;
+
+ /** Free resources used by a local segment
+ */
+
+ SCIUnmapSegment(m_SourceSegm[0].lhm[0].map,0,&err);
+ if(err!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_UNMAP_SEGMENT);
+ DBUG_PRINT("error", ("Unable to unmap segment"));
+ DBUG_RETURN(false);
+ }
+
+ SCIRemoveSegment((m_SourceSegm[m_ActiveAdapterId].localHandle),
+ FLAGS,
+ &err);
+
+ if(err!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_REMOVE_SEGMENT);
+ DBUG_PRINT("error", ("Unable to remove segment"));
+ DBUG_RETURN(false);
+ }
+ DBUG_PRINT("info", ("Local memory segment is unmapped and removed"));
+ DBUG_RETURN(true);
+} // disconnectLocal()
+
+
+bool SCI_Transporter::disconnectRemote() {
+ DBUG_ENTER("SCI_Transporter::disconnectRemote");
+ sci_error_t err;
+ for(Uint32 i=0; i<m_adapters; i++) {
+ /**
+ * Segment unmapped, disconnect from the remotely connected segment
+ */
+ SCIUnmapSegment(m_TargetSegm[i].rhm[i].map,0,&err);
+ if(err!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_UNMAP_SEGMENT);
+ DBUG_PRINT("error", ("Unable to unmap segment"));
+ DBUG_RETURN(false);
+ }
+
+ SCIDisconnectSegment(m_TargetSegm[i].rhm[i].remoteHandle,
+ FLAGS,
+ &err);
+ if(err!=SCI_ERR_OK) {
+ report_error(TE_SCI_UNABLE_TO_DISCONNECT_SEGMENT);
+ DBUG_PRINT("error", ("Unable to disconnect segment"));
+ DBUG_RETURN(false);
+ }
+ DBUG_PRINT("info", ("Remote memory segment is unmapped and disconnected"));
+ }
+ DBUG_RETURN(true);
+} // disconnectRemote()
+
+
+SCI_Transporter::~SCI_Transporter() {
+ DBUG_ENTER("SCI_Transporter::~SCI_Transporter");
+ // Close channel to the driver
+ doDisconnect();
+ if(m_sendBuffer.m_buffer != NULL)
+ delete[] m_sendBuffer.m_buffer;
+ DBUG_VOID_RETURN;
+} // ~SCI_Transporter()
+
+
+
+
+void SCI_Transporter::closeSCI() {
+ // Termination of SCI
+ sci_error_t err;
+ DBUG_ENTER("SCI_Transporter::closeSCI");
+
+ // Disconnect and remove remote segment
+ disconnectRemote();
+
+ // Unmap and remove local segment
+
+ disconnectLocal();
+
+ // Closes an SCI virtual device
+ SCIClose(activeSCIDescriptor, FLAGS, &err);
+
+ if(err != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Cannot close SCI channel to the driver. Error code 0x%x",
+ err));
+ }
+ SCITerminate();
+ DBUG_VOID_RETURN;
+} // closeSCI()
+
+Uint32 *
+SCI_Transporter::getWritePtr(Uint32 lenBytes, Uint32 prio)
+{
+
+ Uint32 sci_buffer_remaining = m_sendBuffer.m_forceSendLimit;
+ Uint32 send_buf_size = m_sendBuffer.m_sendBufferSize;
+ Uint32 curr_data_size = m_sendBuffer.m_dataSize << 2;
+ Uint32 new_curr_data_size = curr_data_size + lenBytes;
+ if ((curr_data_size >= send_buf_size) ||
+ (curr_data_size >= sci_buffer_remaining)) {
+ /**
+ * The new message will not fit in the send buffer. We need to
+ * send the send buffer before filling it up with the new
+ * signal data. If current data size will spill over buffer edge
+ * we will also send to ensure correct operation.
+ */
+ if (!doSend()) {
+ /**
+ * We were not successfull sending, report 0 as meaning buffer full and
+ * upper levels handle retries and other recovery matters.
+ */
+ return 0;
+ }
+ }
+ /**
+ * New signal fits, simply fill it up with more data.
+ */
+ Uint32 sz = m_sendBuffer.m_dataSize;
+ return &m_sendBuffer.m_buffer[sz];
+}
+
+void
+SCI_Transporter::updateWritePtr(Uint32 lenBytes, Uint32 prio){
+
+ Uint32 sz = m_sendBuffer.m_dataSize;
+ Uint32 packet_size = m_PacketSize;
+ sz += ((lenBytes + 3) >> 2);
+ m_sendBuffer.m_dataSize = sz;
+
+ if(sz > packet_size) {
+ /**-------------------------------------------------
+ * Buffer is full and we are ready to send. We will
+ * not wait since the signal is already in the buffer.
+ * Force flag set has the same indication that we
+ * should always send. If it is not possible to send
+ * we will not worry since we will soon be back for
+ * a renewed trial.
+ *-------------------------------------------------
+ */
+ doSend();
+ }
+}
+
+enum SciStatus {
+ SCIDISCONNECT = 1,
+ SCICONNECTED = 2
+};
+
+bool
+SCI_Transporter::getConnectionStatus() {
+ if(*m_localStatusFlag == SCICONNECTED &&
+ (*m_remoteStatusFlag == SCICONNECTED ||
+ ((m_adapters > 1) &&
+ *m_remoteStatusFlag2 == SCICONNECTED)))
+ return true;
+ else
+ return false;
+}
+
+
+void
+SCI_Transporter::setConnected() {
+ *m_remoteStatusFlag = SCICONNECTED;
+ if (m_adapters > 1) {
+ *m_remoteStatusFlag2 = SCICONNECTED;
+ }
+ *m_localStatusFlag = SCICONNECTED;
+}
+
+
+void
+SCI_Transporter::setDisconnect() {
+ if(getLinkStatus(m_ActiveAdapterId))
+ *m_remoteStatusFlag = SCIDISCONNECT;
+ if (m_adapters > 1) {
+ if(getLinkStatus(m_StandbyAdapterId))
+ *m_remoteStatusFlag2 = SCIDISCONNECT;
+ }
+}
+
+
+bool
+SCI_Transporter::checkConnected() {
+ if (*m_localStatusFlag == SCIDISCONNECT) {
+ return false;
+ }
+ else
+ return true;
+}
+
+static bool init = false;
+
+bool
+SCI_Transporter::initSCI() {
+ DBUG_ENTER("SCI_Transporter::initSCI");
+ if(!init){
+ sci_error_t error;
+ // Initialize SISCI library
+ SCIInitialize(0, &error);
+ if(error != SCI_ERR_OK) {
+ DBUG_PRINT("error", ("Cannot initialize SISCI library."));
+ DBUG_PRINT("error", ("Inconsistency between SISCI library and SISCI driver. Error code 0x%x",
+ error));
+ DBUG_RETURN(false);
+ }
+ init = true;
+ }
+ DBUG_RETURN(true);
+}
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/SCI_Transporter.hpp b/storage/ndb/src/common/transporter/SCI_Transporter.hpp
new file mode 100644
index 00000000000..8d263f32a57
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SCI_Transporter.hpp
@@ -0,0 +1,395 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SCI_Transporter_H
+#define SCI_Transporter_H
+#include "Transporter.hpp"
+#include "SHM_Buffer.hpp"
+
+
+#include <sisci_api.h>
+#include <sisci_error.h>
+#include <sisci_types.h>
+
+#include <ndb_types.h>
+
+/**
+ * The SCI Transporter
+ *
+ * The design goal of the SCI transporter is to deliver high performance
+ * data transfers (low latency, high bandwidth) combined with very high
+ * availability (failover support).
+ * High performance is an inherit feature of SCI and the, whereas failover
+ * support is implemented at the application level.
+ * In SCI the programming model is similar to the shared memory paradigm.
+ * A process on one node (A) allocates a memory segment and import the
+ * segment to its virtual address space. Another node (B) can connect to
+ * the segment and map this segment into its virtual address space.
+ * If A writes data to the segment, then B can read it and vice versa, through
+ * ordinary loads and stores. This is also called PIO (programmable IO), and
+ * is one thing that distinguish SCI from other interconnects such as,
+ * ethernet, Gig-e, Myrinet, and Infiniband. By using PIO, lower network
+ * latency is achieved, compared to the interconnects mentioned above.
+ * In order for NDB to utilize SCI, the SCI transporter relies on the
+ * SISCI api. The SISCI api provides a high level abstraction to the low
+ * level SCI driver called PCISCI driver.
+ * The SISCI api provides functions to setup, export, and import
+ * memory segments in a process virtual address space, and also functions to
+ * guarantee the correctness of data transfers between nodes. Basically, the
+ *
+ * In NDB Cluster, each SCI transporter creates a local segment
+ * that is mapped into the virtual address space. After the creation of the
+ * local segment, the SCI transporter connects to a segment created by another
+ * transporter at a remote node, and the maps the remote segment into its
+ * virtual address space. However, since NDB Cluster relies on redundancy
+ * at the network level, by using dual SCI adapters communica
+ *
+ *
+ */
+
+
+/**
+ * class SCITransporter
+ * @brief - main class for the SCI transporter.
+ */
+class SCI_Transporter : public Transporter {
+ friend class TransporterRegistry;
+public:
+
+ /**
+ * Init the transporter. Allocate sendbuffers and open a SCI virtual device
+ * for each adapter.
+ * @return true if successful, otherwize false
+ */
+ bool initTransporter();
+
+
+ /**
+ * Creates a sequence for error checking.
+ * @param adapterid the adapter on which to create a new sequence.
+ * @return SCI_ERR_OK if ok, otherwize something else.
+ */
+ sci_error_t createSequence(Uint32 adapterid);
+
+
+ /**
+ * starts a sequence for error checking.
+ * The actual checking that a sequence is correct is done implicitly
+ * in SCIMemCpy (in doSend).
+ * @param adapterid the adapter on which to start the sequence.
+ * @return SCI_ERR_OK if ok, otherwize something else.
+ */
+ sci_error_t startSequence(Uint32 adapterid);
+
+
+ /** Initiate Local Segment: create a memory segment,
+ * prepare a memory segment, map the local segment
+ * into memory space and make segment available.
+ * @return SCI_ERR_OK if ok, otherwize something else.
+ */
+ sci_error_t initLocalSegment();
+
+ /**
+ * Calculate the segment id for the remote segment
+ * @param localNodeId - local id (e.g. 1 = mgm , 2 = ndb.2 etc.)
+ * @param remoteNodeId - remote id (e.g. 1 = mgm , 2 = ndb.2 etc.)
+ * @return a segment id
+ */
+ Uint32 remoteSegmentId(Uint16 localNodeId, Uint16 remoteNodeId);
+
+ // Get local segment id (inline)
+ Uint32 hostSegmentId(Uint16 localNodeId, Uint16 remoteNodeId);
+
+ /**
+ * closeSCI closes the SCI virtual device
+ */
+ void closeSCI();
+
+
+ /**
+ * Check the status of the remote node,
+ * if it is connected or has disconnected
+ * @return true if connected, otherwize false.
+ */
+ bool checkConnected();
+
+ /**
+ * Check if the segment are properly connected to each other (remotely
+ * and locally).
+ * @return True if the both the local segment is mapped and the
+ * remote segment is mapped. Otherwize false.
+ */
+ bool getConnectionStatus();
+
+private:
+ SCI_Transporter(TransporterRegistry &t_reg,
+ const char *local_host,
+ const char *remote_host,
+ int port,
+ bool isMgmConnection,
+ Uint32 packetSize,
+ Uint32 bufferSize,
+ Uint32 nAdapters,
+ Uint16 remoteSciNodeId0,
+ Uint16 remoteSciNodeId1,
+ NodeId localNodeID,
+ NodeId remoteNodeID,
+ NodeId serverNodeId,
+ bool checksum,
+ bool signalId,
+ Uint32 reportFreq = 4096);
+
+ /**
+ * Destructor. Disconnects the transporter.
+ */
+ ~SCI_Transporter();
+ bool m_mapped;
+ bool m_initLocal;
+ bool m_sciinit;
+ Uint32 m_swapCounter;
+ Uint32 m_failCounter;
+ /**
+ * For statistics on transfered packets
+ */
+//#ifdef DEBUG_TRANSPORTER
+#if 1
+ Uint32 i1024;
+ Uint32 i2048;
+ Uint32 i2049;
+ Uint32 i10242048;
+ Uint32 i20484096;
+ Uint32 i4096;
+ Uint32 i4097;
+#endif
+
+ volatile Uint32 * m_localStatusFlag;
+ volatile Uint32 * m_remoteStatusFlag;
+ volatile Uint32 * m_remoteStatusFlag2;
+
+ struct {
+ Uint32 * m_buffer; // The buffer
+ Uint32 m_dataSize; // No of words in buffer
+ Uint32 m_sendBufferSize; // Buffer size
+ Uint32 m_forceSendLimit; // Send when buffer is this full
+ } m_sendBuffer;
+
+ SHM_Reader * reader;
+ SHM_Writer * writer;
+ SHM_Writer * writer2;
+
+ /**
+ * Statistics
+ */
+ Uint32 m_reportFreq;
+
+
+ Uint32 m_adapters;
+ Uint32 m_numberOfRemoteNodes;
+
+ Uint16 m_remoteNodes[2];
+
+ typedef struct SciAdapter {
+ sci_desc_t scidesc;
+ Uint32 localSciNodeId;
+ bool linkStatus;
+ } SciAdapter;
+
+ SciAdapter* sciAdapters;
+ Uint32 m_ActiveAdapterId;
+ Uint32 m_StandbyAdapterId;
+
+ typedef struct sourceSegm {
+ sci_local_segment_t localHandle; // Handle to local segment to be mapped
+ struct localHandleMap {
+ sci_map_t map; // Handle to the new mapped segment.
+ // 2 = max adapters in one node
+ } lhm[2];
+
+ volatile void *mappedMemory; // Used when reading
+ } sourceSegm;
+
+ typedef struct targetSegm {
+ struct remoteHandleMap {
+ sci_remote_segment_t remoteHandle; //Handle to local segment to be mapped
+ sci_map_t map; //Handle to the new mapped segment
+ } rhm[2];
+
+ sci_sequence_status_t m_SequenceStatus; // Used for error checking
+ sci_sequence_t sequence;
+ volatile void * mappedMemory; // Used when writing
+ SHM_Writer * writer;
+ } targetSegm;
+
+ sci_sequence_status_t m_SequenceStatus; // Used for error checking
+
+
+ // Shared between all SCI users active=(either prim or second)
+ sci_desc_t activeSCIDescriptor;
+
+ sourceSegm* m_SourceSegm; // Local segment reference
+ targetSegm* m_TargetSegm; // Remote segment reference
+
+ Uint32 m_LocalAdapterId; // Adapter Id
+ Uint16 m_LocalSciNodeId; // The SCI-node Id of this machine (adapter 0)
+ Uint16 m_LocalSciNodeId1; // The SCI-node Id of this machine (adapter 1)
+ Uint16 m_RemoteSciNodeId; // The SCI-node Id of remote machine (adapter 0)
+ Uint16 m_RemoteSciNodeId1; // The SCI-node Id of remote machine (adapter 1)
+
+ Uint32 m_PacketSize; // The size of each data packet
+ Uint32 m_BufferSize; // Mapped SCI buffer size
+
+ Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio);
+ void updateWritePtr(Uint32 lenBytes, Uint32 prio);
+
+ /**
+ * doSend. Copies the data from the source (the send buffer) to the
+ * shared mem. segment.
+ * Sequences are used for error checking.
+ * If an error occurs, the transfer is retried.
+ * If the link that we need to swap to is broken, we will disconnect.
+ * @return Returns true if datatransfer ok. If not retriable
+ * then false is returned.
+ */
+ bool doSend();
+
+ /**
+ * @param adapterNo the adapter for which to retrieve the node id.
+ * @return Returns the node id for an adapter.
+ */
+ Uint32 getLocalNodeId(Uint32 adapterNo);
+
+ bool hasDataToRead() const {
+ return reader->empty() == false;
+ }
+
+ bool hasDataToSend() const {
+ return m_sendBuffer.m_dataSize > 0;
+ }
+
+ /**
+ * Make the local segment unavailable, no new connections will be accepted.
+ * @return Returns true if the segment was successfully disconnected.
+ */
+ bool disconnectLocal();
+
+ /**
+ * Make the local segment unavailable, no new connections will be accepted.
+ * @return Returns true if the segment was successfully disconnected.
+ */
+ bool disconnectRemote();
+
+ void resetToInitialState();
+
+ /**
+ * It is always possible to send data with SCI!
+ * @return True (always)
+ */
+ bool sendIsPossible(struct timeval * timeout);
+
+ void getReceivePtr(Uint32 ** ptr, Uint32 ** eod){
+ reader->getReadPtr(* ptr, * eod);
+ }
+
+ void updateReceivePtr(Uint32 *ptr){
+ reader->updateReadPtr(ptr);
+ }
+
+ /**
+ * Corresponds to SHM_Transporter::setupBuffers()
+ * Initiates the start pointer of the buffer and read pointers.
+ * Initiate the localSegment for the SHM reader.
+ */
+ void setupLocalSegment();
+
+ /**
+ * Initiate the remoteSegment for the SHM writer
+ */
+ void setupRemoteSegment();
+
+ /**
+ * Set the connect flag in the remote memory segment (write through)
+ */
+ void setConnected();
+
+ /**
+ * Set the disconnect flag in the remote memory segment (write through)
+ */
+ void setDisconnect();
+
+ /**
+ * Check if there is a link between the adapter and the switch
+ * @param adapterNo the adapter for which to retrieve the link status.
+ * @return Returns true if there is a link between adapter and switch.
+ * Otherwize false is returned and the cables must be checked.
+ */
+ bool getLinkStatus(Uint32 adapterNo);
+
+ /**
+ * failoverShmWriter takes the state of the active writer and inserts into
+ * the standby writer.
+ */
+ void failoverShmWriter();
+
+ bool init_local();
+ bool init_remote();
+
+protected:
+
+ /** Perform a connection between segment
+ * This is a client node, trying to connect to a remote segment.
+ * @param timeout, the time the connect thread sleeps before
+ * retrying.
+ * @return Returns true on success, otherwize falser
+ */
+ bool connect_server_impl(NDB_SOCKET_TYPE sockfd);
+ bool connect_client_impl(NDB_SOCKET_TYPE sockfd);
+
+ /**
+ * We will disconnect if:
+ * -# the other node has disconnected from us
+ * -# unrecoverable error in transmission, on both adapters
+ * -# if we are shutdown properly
+ */
+ void disconnectImpl();
+
+ static bool initSCI();
+};
+
+
+/** The theLocalAdapterId combined with the theRemoteNodeId constructs
+ * (SCI ids)* a unique identifier for the local segment
+ */
+inline
+Uint32
+SCI_Transporter::hostSegmentId(Uint16 SciLocalNodeId,
+ Uint16 SciRemoteNodeId) {
+
+ return (SciLocalNodeId << 16) | SciRemoteNodeId;
+}
+
+/** The theLocalAdapterId combined with the theRemoteNodeId constructs
+ * (SCI ids)* a unique identifier for the remote segment
+ */
+inline
+Uint32
+SCI_Transporter::remoteSegmentId(Uint16 SciLocalNodeId,
+ Uint16 SciRemoteNodeId) {
+
+ return (SciRemoteNodeId << 16) | SciLocalNodeId;
+}
+
+
+#endif
diff --git a/storage/ndb/src/common/transporter/SHM_Buffer.hpp b/storage/ndb/src/common/transporter/SHM_Buffer.hpp
new file mode 100644
index 00000000000..f49b4fe73cb
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SHM_Buffer.hpp
@@ -0,0 +1,216 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SHM_BUFFER_HPP
+#define SHM_BUFFER_HPP
+
+#include <ndb_global.h>
+
+#include <NdbSleep.h>
+
+/**
+ * These classes implement a circular buffer
+ *
+ * One reader and one writer
+ */
+
+/**
+ * SHM_Reader
+ *
+ * Use as follows:
+ * getReadPtr(ptr, sz);
+ * for(int i = 0; i<sz; i++)
+ * printf("%c\n", ptr[i]);
+ * updateReadPtr(sz);
+ */
+class SHM_Reader {
+public:
+ SHM_Reader(char * const _startOfBuffer,
+ Uint32 _sizeOfBuffer,
+ Uint32 _slack,
+ Uint32 * _readIndex,
+ Uint32 * _writeIndex) :
+ m_startOfBuffer(_startOfBuffer),
+ m_totalBufferSize(_sizeOfBuffer),
+ m_bufferSize(_sizeOfBuffer - _slack),
+ m_sharedReadIndex(_readIndex),
+ m_sharedWriteIndex(_writeIndex)
+ {
+ }
+
+ void clear() {
+ m_readIndex = 0;
+ }
+
+ /**
+ *
+ */
+ inline bool empty() const;
+
+ /**
+ * Get read pointer
+ *
+ * returns ptr - where to start reading
+ * sz - how much can I read
+ */
+ inline void getReadPtr(Uint32 * & ptr, Uint32 * & eod);
+
+ /**
+ * Update read ptr
+ */
+ inline void updateReadPtr(Uint32 *ptr);
+
+private:
+ char * const m_startOfBuffer;
+ Uint32 m_totalBufferSize;
+ Uint32 m_bufferSize;
+ Uint32 m_readIndex;
+
+ Uint32 * m_sharedReadIndex;
+ Uint32 * m_sharedWriteIndex;
+};
+
+inline
+bool
+SHM_Reader::empty() const{
+ bool ret = (m_readIndex == * m_sharedWriteIndex);
+ return ret;
+}
+
+/**
+ * Get read pointer
+ *
+ * returns ptr - where to start reading
+ * sz - how much can I read
+ */
+inline
+void
+SHM_Reader::getReadPtr(Uint32 * & ptr, Uint32 * & eod)
+{
+ Uint32 tReadIndex = m_readIndex;
+ Uint32 tWriteIndex = * m_sharedWriteIndex;
+
+ ptr = (Uint32*)&m_startOfBuffer[tReadIndex];
+
+ if(tReadIndex <= tWriteIndex){
+ eod = (Uint32*)&m_startOfBuffer[tWriteIndex];
+ } else {
+ eod = (Uint32*)&m_startOfBuffer[m_bufferSize];
+ }
+}
+
+/**
+ * Update read ptr
+ */
+inline
+void
+SHM_Reader::updateReadPtr(Uint32 *ptr)
+{
+ Uint32 tReadIndex = ((char*)ptr) - m_startOfBuffer;
+
+ assert(tReadIndex < m_totalBufferSize);
+
+ if(tReadIndex >= m_bufferSize){
+ tReadIndex = 0;
+ }
+
+ m_readIndex = tReadIndex;
+ * m_sharedReadIndex = tReadIndex;
+}
+
+#define WRITER_SLACK 4
+
+class SHM_Writer {
+public:
+ SHM_Writer(char * const _startOfBuffer,
+ Uint32 _sizeOfBuffer,
+ Uint32 _slack,
+ Uint32 * _readIndex,
+ Uint32 * _writeIndex) :
+ m_startOfBuffer(_startOfBuffer),
+ m_totalBufferSize(_sizeOfBuffer),
+ m_bufferSize(_sizeOfBuffer - _slack),
+ m_sharedReadIndex(_readIndex),
+ m_sharedWriteIndex(_writeIndex)
+ {
+ }
+
+ void clear() {
+ m_writeIndex = 0;
+ }
+
+ inline char * getWritePtr(Uint32 sz);
+ inline void updateWritePtr(Uint32 sz);
+
+ inline Uint32 getWriteIndex() const { return m_writeIndex;}
+ inline Uint32 getBufferSize() const { return m_bufferSize;}
+
+ inline void copyIndexes(SHM_Writer * standbyWriter);
+
+private:
+ char * const m_startOfBuffer;
+ Uint32 m_totalBufferSize;
+ Uint32 m_bufferSize;
+
+ Uint32 m_writeIndex;
+
+ Uint32 * m_sharedReadIndex;
+ Uint32 * m_sharedWriteIndex;
+};
+
+inline
+char *
+SHM_Writer::getWritePtr(Uint32 sz){
+ Uint32 tReadIndex = * m_sharedReadIndex;
+ Uint32 tWriteIndex = m_writeIndex;
+
+ char * ptr = &m_startOfBuffer[tWriteIndex];
+
+ Uint32 free;
+ if(tReadIndex <= tWriteIndex){
+ free = m_bufferSize + tReadIndex - tWriteIndex;
+ } else {
+ free = tReadIndex - tWriteIndex;
+ }
+
+ sz += 4;
+ if(sz < free){
+ return ptr;
+ }
+
+ return 0;
+}
+
+inline
+void
+SHM_Writer::updateWritePtr(Uint32 sz){
+
+ assert(m_writeIndex == * m_sharedWriteIndex);
+
+ Uint32 tWriteIndex = m_writeIndex;
+ tWriteIndex += sz;
+
+ assert(tWriteIndex < m_totalBufferSize);
+
+ if(tWriteIndex >= m_bufferSize){
+ tWriteIndex = 0;
+ }
+
+ m_writeIndex = tWriteIndex;
+ * m_sharedWriteIndex = tWriteIndex;
+}
+
+#endif
diff --git a/storage/ndb/src/common/transporter/SHM_Transporter.cpp b/storage/ndb/src/common/transporter/SHM_Transporter.cpp
new file mode 100644
index 00000000000..e2d23cf94e2
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SHM_Transporter.cpp
@@ -0,0 +1,367 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "SHM_Transporter.hpp"
+#include "TransporterInternalDefinitions.hpp"
+#include <TransporterCallback.hpp>
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+
+#include <InputStream.hpp>
+#include <OutputStream.hpp>
+
+extern int g_ndb_shm_signum;
+
+SHM_Transporter::SHM_Transporter(TransporterRegistry &t_reg,
+ const char *lHostName,
+ const char *rHostName,
+ int r_port,
+ bool isMgmConnection,
+ NodeId lNodeId,
+ NodeId rNodeId,
+ NodeId serverNodeId,
+ bool checksum,
+ bool signalId,
+ key_t _shmKey,
+ Uint32 _shmSize) :
+ Transporter(t_reg, tt_SHM_TRANSPORTER,
+ lHostName, rHostName, r_port, isMgmConnection,
+ lNodeId, rNodeId, serverNodeId,
+ 0, false, checksum, signalId),
+ shmKey(_shmKey),
+ shmSize(_shmSize)
+{
+ _shmSegCreated = false;
+ _attached = false;
+
+ shmBuf = 0;
+ reader = 0;
+ writer = 0;
+
+ setupBuffersDone=false;
+#ifdef DEBUG_TRANSPORTER
+ printf("shm key (%d - %d) = %d\n", lNodeId, rNodeId, shmKey);
+#endif
+ m_signal_threshold = 4096;
+}
+
+SHM_Transporter::~SHM_Transporter(){
+ doDisconnect();
+}
+
+bool
+SHM_Transporter::initTransporter(){
+ if (g_ndb_shm_signum)
+ return true;
+ return false;
+}
+
+void
+SHM_Transporter::setupBuffers(){
+ Uint32 sharedSize = 0;
+ sharedSize += 28; //SHM_Reader::getSharedSize();
+ sharedSize += 28; //SHM_Writer::getSharedSize();
+
+ const Uint32 slack = MAX_MESSAGE_SIZE;
+
+ /**
+ * NOTE: There is 7th shared variable in Win2k (sharedCountAttached).
+ */
+ Uint32 sizeOfBuffer = shmSize;
+ sizeOfBuffer -= 2*sharedSize;
+ sizeOfBuffer /= 2;
+
+ Uint32 * base1 = (Uint32*)shmBuf;
+
+ Uint32 * sharedReadIndex1 = base1;
+ Uint32 * sharedWriteIndex1 = base1 + 1;
+ serverStatusFlag = base1 + 4;
+ char * startOfBuf1 = shmBuf+sharedSize;
+
+ Uint32 * base2 = (Uint32*)(shmBuf + sizeOfBuffer + sharedSize);
+ Uint32 * sharedReadIndex2 = base2;
+ Uint32 * sharedWriteIndex2 = base2 + 1;
+ clientStatusFlag = base2 + 4;
+ char * startOfBuf2 = ((char *)base2)+sharedSize;
+
+ if(isServer){
+ * serverStatusFlag = 0;
+ reader = new SHM_Reader(startOfBuf1,
+ sizeOfBuffer,
+ slack,
+ sharedReadIndex1,
+ sharedWriteIndex1);
+
+ writer = new SHM_Writer(startOfBuf2,
+ sizeOfBuffer,
+ slack,
+ sharedReadIndex2,
+ sharedWriteIndex2);
+
+ * sharedReadIndex1 = 0;
+ * sharedWriteIndex1 = 0;
+
+ * sharedReadIndex2 = 0;
+ * sharedWriteIndex2 = 0;
+
+ reader->clear();
+ writer->clear();
+
+ * serverStatusFlag = 1;
+
+#ifdef DEBUG_TRANSPORTER
+ printf("-- (%d - %d) - Server -\n", localNodeId, remoteNodeId);
+ printf("Reader at: %d (%p)\n", startOfBuf1 - shmBuf, startOfBuf1);
+ printf("sharedReadIndex1 at %d (%p) = %d\n",
+ (char*)sharedReadIndex1-shmBuf,
+ sharedReadIndex1, *sharedReadIndex1);
+ printf("sharedWriteIndex1 at %d (%p) = %d\n",
+ (char*)sharedWriteIndex1-shmBuf,
+ sharedWriteIndex1, *sharedWriteIndex1);
+
+ printf("Writer at: %d (%p)\n", startOfBuf2 - shmBuf, startOfBuf2);
+ printf("sharedReadIndex2 at %d (%p) = %d\n",
+ (char*)sharedReadIndex2-shmBuf,
+ sharedReadIndex2, *sharedReadIndex2);
+ printf("sharedWriteIndex2 at %d (%p) = %d\n",
+ (char*)sharedWriteIndex2-shmBuf,
+ sharedWriteIndex2, *sharedWriteIndex2);
+
+ printf("sizeOfBuffer = %d\n", sizeOfBuffer);
+#endif
+ } else {
+ * clientStatusFlag = 0;
+ reader = new SHM_Reader(startOfBuf2,
+ sizeOfBuffer,
+ slack,
+ sharedReadIndex2,
+ sharedWriteIndex2);
+
+ writer = new SHM_Writer(startOfBuf1,
+ sizeOfBuffer,
+ slack,
+ sharedReadIndex1,
+ sharedWriteIndex1);
+
+ * sharedReadIndex2 = 0;
+ * sharedWriteIndex1 = 0;
+
+ reader->clear();
+ writer->clear();
+ * clientStatusFlag = 1;
+#ifdef DEBUG_TRANSPORTER
+ printf("-- (%d - %d) - Client -\n", localNodeId, remoteNodeId);
+ printf("Reader at: %d (%p)\n", startOfBuf2 - shmBuf, startOfBuf2);
+ printf("sharedReadIndex2 at %d (%p) = %d\n",
+ (char*)sharedReadIndex2-shmBuf,
+ sharedReadIndex2, *sharedReadIndex2);
+ printf("sharedWriteIndex2 at %d (%p) = %d\n",
+ (char*)sharedWriteIndex2-shmBuf,
+ sharedWriteIndex2, *sharedWriteIndex2);
+
+ printf("Writer at: %d (%p)\n", startOfBuf1 - shmBuf, startOfBuf1);
+ printf("sharedReadIndex1 at %d (%p) = %d\n",
+ (char*)sharedReadIndex1-shmBuf,
+ sharedReadIndex1, *sharedReadIndex1);
+ printf("sharedWriteIndex1 at %d (%p) = %d\n",
+ (char*)sharedWriteIndex1-shmBuf,
+ sharedWriteIndex1, *sharedWriteIndex1);
+
+ printf("sizeOfBuffer = %d\n", sizeOfBuffer);
+#endif
+ }
+#ifdef DEBUG_TRANSPORTER
+ printf("Mapping from %p to %p\n", shmBuf, shmBuf+shmSize);
+#endif
+}
+
+bool
+SHM_Transporter::connect_server_impl(NDB_SOCKET_TYPE sockfd)
+{
+ DBUG_ENTER("SHM_Transporter::connect_server_impl");
+ SocketOutputStream s_output(sockfd);
+ SocketInputStream s_input(sockfd);
+ char buf[256];
+
+ // Create
+ if(!_shmSegCreated){
+ if (!ndb_shm_create()) {
+ report_error(TE_SHM_UNABLE_TO_CREATE_SEGMENT);
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ _shmSegCreated = true;
+ }
+
+ // Attach
+ if(!_attached){
+ if (!ndb_shm_attach()) {
+ report_error(TE_SHM_UNABLE_TO_ATTACH_SEGMENT);
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ _attached = true;
+ }
+
+ // Send ok to client
+ s_output.println("shm server 1 ok: %d",
+ m_transporter_registry.m_shm_own_pid);
+
+ // Wait for ok from client
+ if (s_input.gets(buf, 256) == 0)
+ {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ if(sscanf(buf, "shm client 1 ok: %d", &m_remote_pid) != 1)
+ {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ int r= connect_common(sockfd);
+
+ if (r) {
+ // Send ok to client
+ s_output.println("shm server 2 ok");
+ // Wait for ok from client
+ if (s_input.gets(buf, 256) == 0) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+ DBUG_PRINT("info", ("Successfully connected server to node %d",
+ remoteNodeId));
+ }
+
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(r);
+}
+
+bool
+SHM_Transporter::connect_client_impl(NDB_SOCKET_TYPE sockfd)
+{
+ DBUG_ENTER("SHM_Transporter::connect_client_impl");
+ SocketInputStream s_input(sockfd);
+ SocketOutputStream s_output(sockfd);
+ char buf[256];
+
+#if 1
+#endif
+
+ // Wait for server to create and attach
+ if (s_input.gets(buf, 256) == 0) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_PRINT("error", ("Server id %d did not attach",
+ remoteNodeId));
+ DBUG_RETURN(false);
+ }
+
+ if(sscanf(buf, "shm server 1 ok: %d", &m_remote_pid) != 1)
+ {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ // Create
+ if(!_shmSegCreated){
+ if (!ndb_shm_get()) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_PRINT("error", ("Failed create of shm seg to node %d",
+ remoteNodeId));
+ DBUG_RETURN(false);
+ }
+ _shmSegCreated = true;
+ }
+
+ // Attach
+ if(!_attached){
+ if (!ndb_shm_attach()) {
+ report_error(TE_SHM_UNABLE_TO_ATTACH_SEGMENT);
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_PRINT("error", ("Failed attach of shm seg to node %d",
+ remoteNodeId));
+ DBUG_RETURN(false);
+ }
+ _attached = true;
+ }
+
+ // Send ok to server
+ s_output.println("shm client 1 ok: %d",
+ m_transporter_registry.m_shm_own_pid);
+
+ int r= connect_common(sockfd);
+
+ if (r) {
+ // Wait for ok from server
+ if (s_input.gets(buf, 256) == 0) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_PRINT("error", ("No ok from server node %d",
+ remoteNodeId));
+ DBUG_RETURN(false);
+ }
+ // Send ok to server
+ s_output.println("shm client 2 ok");
+ DBUG_PRINT("info", ("Successfully connected client to node %d",
+ remoteNodeId));
+ }
+
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(r);
+}
+
+bool
+SHM_Transporter::connect_common(NDB_SOCKET_TYPE sockfd)
+{
+ if (!checkConnected()) {
+ DBUG_PRINT("error", ("Already connected to node %d",
+ remoteNodeId));
+ return false;
+ }
+
+ if(!setupBuffersDone)
+ {
+ setupBuffers();
+ setupBuffersDone=true;
+ }
+
+ if(setupBuffersDone)
+ {
+ NdbSleep_MilliSleep(m_timeOutMillis);
+ if(*serverStatusFlag == 1 && *clientStatusFlag == 1)
+ {
+ m_last_signal = 0;
+ return true;
+ }
+ }
+
+ DBUG_PRINT("error", ("Failed to set up buffers to node %d",
+ remoteNodeId));
+ return false;
+}
+
+void
+SHM_Transporter::doSend()
+{
+ if(m_last_signal)
+ {
+ m_last_signal = 0;
+ kill(m_remote_pid, g_ndb_shm_signum);
+ }
+}
diff --git a/storage/ndb/src/common/transporter/SHM_Transporter.hpp b/storage/ndb/src/common/transporter/SHM_Transporter.hpp
new file mode 100644
index 00000000000..677bd6efc37
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SHM_Transporter.hpp
@@ -0,0 +1,173 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SHM_Transporter_H
+#define SHM_Transporter_H
+
+#include "Transporter.hpp"
+#include "SHM_Buffer.hpp"
+
+#ifdef NDB_WIN32
+typedef Uint32 key_t;
+#endif
+
+/**
+ * class SHMTransporter
+ * @brief - main class for the SHM transporter.
+ */
+
+class SHM_Transporter : public Transporter {
+ friend class TransporterRegistry;
+public:
+ SHM_Transporter(TransporterRegistry &,
+ const char *lHostName,
+ const char *rHostName,
+ int r_port,
+ bool isMgmConnection,
+ NodeId lNodeId,
+ NodeId rNodeId,
+ NodeId serverNodeId,
+ bool checksum,
+ bool signalId,
+ key_t shmKey,
+ Uint32 shmSize);
+
+ /**
+ * SHM destructor
+ */
+ virtual ~SHM_Transporter();
+
+ /**
+ * Do initialization
+ */
+ bool initTransporter();
+
+ Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio)
+ {
+ return (Uint32 *)writer->getWritePtr(lenBytes);
+ }
+
+ void updateWritePtr(Uint32 lenBytes, Uint32 prio)
+ {
+ writer->updateWritePtr(lenBytes);
+ m_last_signal += lenBytes;
+ if(m_last_signal >= m_signal_threshold)
+ {
+ doSend();
+ }
+ }
+
+ void getReceivePtr(Uint32 ** ptr, Uint32 ** eod){
+ reader->getReadPtr(* ptr, * eod);
+ }
+
+ void updateReceivePtr(Uint32 * ptr){
+ reader->updateReadPtr(ptr);
+ }
+
+protected:
+ /**
+ * disconnect a segmnet
+ * -# deletes the shm buffer associated with a segment
+ * -# marks the segment for removal
+ */
+ void disconnectImpl();
+
+ /**
+ * Blocking
+ *
+ * -# Create shm segment
+ * -# Attach to it
+ * -# Wait for someone to attach (max wait = timeout), then rerun again
+ * until connection established.
+ * @param timeOutMillis - the time to sleep before (ms) trying again.
+ * @returns - True if the server managed to hook up with the client,
+ * i.e., both agrees that the other one has setup the segment.
+ * Otherwise false.
+ */
+ virtual bool connect_server_impl(NDB_SOCKET_TYPE sockfd);
+
+ /**
+ * Blocking
+ *
+ * -# Attach to shm segment
+ * -# Check if the segment is setup
+ * -# Check if the server set it up
+ * -# If all clear, return.
+ * @param timeOutMillis - the time to sleep before (ms) trying again.
+ * @returns - True if the client managed to hook up with the server,
+ * i.e., both agrees that the other one has setup the segment.
+ * Otherwise false.
+ */
+ virtual bool connect_client_impl(NDB_SOCKET_TYPE sockfd);
+
+ bool connect_common(NDB_SOCKET_TYPE sockfd);
+
+ bool ndb_shm_create();
+ bool ndb_shm_get();
+ bool ndb_shm_attach();
+
+ /**
+ * Check if there are two processes attached to the segment (a connection)
+ * @return - True if the above holds. Otherwise false.
+ */
+ bool checkConnected();
+
+
+ /**
+ * Initialises the SHM_Reader and SHM_Writer on the segment
+ */
+ void setupBuffers();
+
+ /**
+ * doSend (i.e signal receiver)
+ */
+ void doSend();
+ int m_remote_pid;
+ Uint32 m_last_signal;
+ Uint32 m_signal_threshold;
+
+private:
+ bool _shmSegCreated;
+ bool _attached;
+ bool m_connected;
+
+ key_t shmKey;
+ volatile Uint32 * serverStatusFlag;
+ volatile Uint32 * clientStatusFlag;
+ bool setupBuffersDone;
+
+#ifdef NDB_WIN32
+ HANDLE hFileMapping;
+#else
+ int shmId;
+#endif
+
+ int shmSize;
+ char * shmBuf;
+
+ SHM_Reader * reader;
+ SHM_Writer * writer;
+
+ /**
+ * @return - True if the reader has data to read on its segment.
+ */
+ bool hasDataToRead() const {
+ return reader->empty() == false;
+ }
+};
+
+#endif
diff --git a/storage/ndb/src/common/transporter/SHM_Transporter.unix.cpp b/storage/ndb/src/common/transporter/SHM_Transporter.unix.cpp
new file mode 100644
index 00000000000..28882324fc0
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SHM_Transporter.unix.cpp
@@ -0,0 +1,100 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "SHM_Transporter.hpp"
+#include "TransporterInternalDefinitions.hpp"
+#include <TransporterCallback.hpp>
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+bool
+SHM_Transporter::ndb_shm_create()
+{
+ shmId = shmget(shmKey, shmSize, IPC_CREAT | 960);
+ if(shmId == -1) {
+ perror("shmget: ");
+ return false;
+ }
+ return true;
+}
+
+bool
+SHM_Transporter::ndb_shm_get()
+{
+ shmId = shmget(shmKey, shmSize, 0);
+ if(shmId == -1) {
+ perror("shmget: ");
+ return false;
+ }
+ return true;
+}
+
+bool
+SHM_Transporter::ndb_shm_attach()
+{
+ shmBuf = (char *)shmat(shmId, 0, 0);
+ if(shmBuf == 0) {
+ perror("shmat: ");
+ return false;
+ }
+ return true;
+}
+
+bool
+SHM_Transporter::checkConnected(){
+ struct shmid_ds info;
+ const int res = shmctl(shmId, IPC_STAT, &info);
+ if(res == -1){
+ report_error(TE_SHM_IPC_STAT);
+ return false;
+ }
+
+ if(info.shm_nattch != 2){
+ report_error(TE_SHM_DISCONNECT);
+ return false;
+ }
+ return true;
+}
+
+void
+SHM_Transporter::disconnectImpl(){
+ if(_attached){
+ const int res = shmdt(shmBuf);
+ if(res == -1){
+ perror("shmdelete: ");
+ return;
+ }
+ _attached = false;
+ if(!isServer && _shmSegCreated)
+ _shmSegCreated = false;
+ }
+
+ if(isServer && _shmSegCreated){
+ const int res = shmctl(shmId, IPC_RMID, 0);
+ if(res == -1){
+ report_error(TE_SHM_UNABLE_TO_REMOVE_SEGMENT);
+ return;
+ }
+ _shmSegCreated = false;
+ }
+ setupBuffersDone=false;
+}
diff --git a/storage/ndb/src/common/transporter/SHM_Transporter.win32.cpp b/storage/ndb/src/common/transporter/SHM_Transporter.win32.cpp
new file mode 100644
index 00000000000..c289a85da0e
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SHM_Transporter.win32.cpp
@@ -0,0 +1,172 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "SHM_Transporter.hpp"
+#include "TransporterInternalDefinitions.hpp"
+#include <TransporterCallback.hpp>
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+
+#include <windows.h>
+
+
+bool
+SHM_Transporter::connectServer(Uint32 timeOutMillis){
+ if(!_shmSegCreated)
+ {
+ char szName[32];
+ sprintf(szName, "ndb%lu", shmKey);
+ hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
+ 0,
+ PAGE_READWRITE,
+ 0,
+ shmSize,
+ szName);
+
+ if(!hFileMapping)
+ {
+ reportThreadError(remoteNodeId, TE_SHM_UNABLE_TO_CREATE_SEGMENT);
+ NdbSleep_MilliSleep(timeOutMillis);
+ return false;
+ }
+ _shmSegCreated = true;
+ }
+
+ if(!_attached){
+ shmBuf = (char*)MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if(shmBuf == 0){
+ reportThreadError(remoteNodeId, TE_SHM_UNABLE_TO_ATTACH_SEGMENT);
+ NdbSleep_MilliSleep(timeOutMillis);
+ return false;
+ }
+ volatile Uint32 * sharedCountAttached =
+ (volatile Uint32*)(shmBuf + 6*sizeof(Uint32*));
+ ++*sharedCountAttached;
+ _attached = true;
+ }
+
+ volatile Uint32 * sharedCountAttached =
+ (volatile Uint32*)(shmBuf + 6*sizeof(Uint32*));
+
+ if(*sharedCountAttached == 2 && !setupBuffersDone) {
+ setupBuffers();
+ setupBuffersDone=true;
+ }
+ if(*sharedCountAttached > 2) {
+ reportThreadError(remoteNodeId, TE_SHM_DISCONNECT);
+ return false;
+ }
+
+ if(setupBuffersDone) {
+ NdbSleep_MilliSleep(timeOutMillis);
+ if(*serverStatusFlag==1 && *clientStatusFlag==1)
+ return true;
+ }
+
+ NdbSleep_MilliSleep(timeOutMillis);
+ return false;
+}
+
+bool
+SHM_Transporter::connectClient(Uint32 timeOutMillis){
+ if(!_shmSegCreated)
+ {
+ char szName[32];
+ sprintf(szName, "ndb%lu", shmKey);
+ hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szName);
+
+ if(!hFileMapping)
+ {
+ NdbSleep_MilliSleep(timeOutMillis);
+ return false;
+ }
+ _shmSegCreated = true;
+ }
+
+ if(!_attached){
+ shmBuf = (char*)MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if(shmBuf == 0){
+ reportThreadError(remoteNodeId, TE_SHM_UNABLE_TO_ATTACH_SEGMENT);
+ NdbSleep_MilliSleep(timeOutMillis);
+ return false;
+ }
+ volatile Uint32 * sharedCountAttached =
+ (volatile Uint32*)(shmBuf + 6*sizeof(Uint32*));
+ ++*sharedCountAttached;
+ _attached = true;
+ }
+
+ volatile Uint32 * sharedCountAttached =
+ (volatile Uint32*)(shmBuf + 6*sizeof(Uint32*));
+
+ if(*sharedCountAttached == 2 && !setupBuffersDone) {
+ setupBuffers();
+ setupBuffersDone=true;
+ }
+
+ if(setupBuffersDone) {
+ if(*serverStatusFlag==1 && *clientStatusFlag==1)
+ return true;
+ }
+ NdbSleep_MilliSleep(timeOutMillis);
+ return false;
+
+}
+
+
+bool
+SHM_Transporter::checkConnected(){
+ volatile Uint32 * sharedCountAttached =
+ (volatile Uint32*)(shmBuf + 6*sizeof(Uint32*));
+ if(*sharedCountAttached != 2) {
+ reportError(callbackObj, remoteNodeId, TE_SHM_DISCONNECT);
+ return false;
+ }
+ return true;
+}
+
+void
+SHM_Transporter::disconnectImpl(){
+ if(_attached) {
+ volatile Uint32 * sharedCountAttached =
+ (volatile Uint32*)(shmBuf + 6*sizeof(Uint32*));
+
+ --*sharedCountAttached;
+
+ if(!UnmapViewOfFile(shmBuf)) {
+ reportError(callbackObj, remoteNodeId, TE_SHM_UNABLE_TO_REMOVE_SEGMENT);
+ return;
+ }
+
+ _attached = false;
+ if(!isServer && _shmSegCreated)
+ _shmSegCreated = false;
+ }
+
+ if(_shmSegCreated){
+ if(!CloseHandle(hFileMapping)) {
+ reportError(callbackObj, remoteNodeId, TE_SHM_UNABLE_TO_REMOVE_SEGMENT);
+ return;
+ }
+ _shmSegCreated = false;
+ }
+ setupBuffersDone=false;
+
+}
+
diff --git a/storage/ndb/src/common/transporter/SendBuffer.cpp b/storage/ndb/src/common/transporter/SendBuffer.cpp
new file mode 100644
index 00000000000..58cad96931f
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SendBuffer.cpp
@@ -0,0 +1,89 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "SendBuffer.hpp"
+#include "TransporterInternalDefinitions.hpp"
+
+SendBuffer::SendBuffer(Uint32 bufSize) {
+
+ sizeOfBuffer = bufSize;
+ if(sizeOfBuffer < MAX_MESSAGE_SIZE)
+ sizeOfBuffer = 2 * MAX_MESSAGE_SIZE;
+ startOfBuffer = NULL;
+
+ // Initalise pointers
+ endOfBuffer = NULL;
+ insertPtr = NULL;
+ sendPtr = NULL;
+ sendDataSize = 0;
+ dataSize = 0;
+}
+
+bool
+SendBuffer::initBuffer(Uint32 aRemoteNodeId) {
+
+ // Allocate memory for the buffer
+#ifdef DEBUG_TRANSPORTER
+ ndbout << "Allocating " << sizeOfBuffer << " bytes for send buffer" << endl;
+#endif
+
+ startOfBuffer = new Uint32[(sizeOfBuffer >> 2) + 1];
+ endOfBuffer = startOfBuffer + (sizeOfBuffer >> 2);
+
+ emptyBuffer();
+ theRemoteNodeId = aRemoteNodeId;
+ return true;
+}
+
+SendBuffer::~SendBuffer() {
+ // Deallocate the buffer memory
+ if(startOfBuffer != NULL)
+ delete[] startOfBuffer;
+}
+
+int
+SendBuffer::bufferSize() {
+ return dataSize;
+}
+
+Uint32
+SendBuffer::bufferSizeRemaining() {
+ return (sizeOfBuffer - dataSize);
+}
+
+void
+SendBuffer::emptyBuffer() {
+ insertPtr = startOfBuffer;
+ sendPtr = (char*)startOfBuffer;
+ dataSize = 0;
+ sendDataSize = 0;
+}
+
+#ifdef DEBUG_TRANSPORTER
+void
+SendBuffer::print() {
+
+ printf("SendBuffer status printouts\n");
+
+ printf( "sizeOfBuffer: %d\n", sizeOfBuffer);
+ printf( "startOfBuffer: %.8x\n", startOfBuffer);
+ printf( "endOfBuffer: %.8x\n", endOfBuffer);
+ printf( "insertPtr: %.8x\n", insertPtr);
+ printf( "sendPtr: %.8x\n", sendPtr);
+ printf( "sendDataSize: %d\n", sendDataSize);
+ printf( "dataSize: %d\n", dataSize);
+}
+#endif
diff --git a/storage/ndb/src/common/transporter/SendBuffer.hpp b/storage/ndb/src/common/transporter/SendBuffer.hpp
new file mode 100644
index 00000000000..63a01f3de24
--- /dev/null
+++ b/storage/ndb/src/common/transporter/SendBuffer.hpp
@@ -0,0 +1,190 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//****************************************************************************
+//
+// NAME
+// SendBuffer
+//
+// DESCRIPTION
+// The SendBuffer is a circular buffer storing signals waiting to be sent.
+// The signals can be of variable size and are copied into the buffer
+// in Protocol 6 format. There will be two SendBuffer instances
+// (priority level A and B) for each transporter using a buffer for
+// sending. The buffering will in most cases be done to send as big
+// packages as possible over TCP/IP.
+//
+//***************************************************************************/
+#ifndef SendBuffer_H
+#define SendBuffer_H
+
+#include "TransporterDefinitions.hpp"
+#include <TransporterCallback.hpp>
+
+#ifdef DEBUG_TRANSPORTER
+#include <ndb_global.h>
+#endif
+
+class SendBuffer {
+ friend class TCP_Transporter;
+public:
+ // Set member variables
+ SendBuffer(Uint32 bufSize);
+
+ // Deallocate the buffer memory
+ ~SendBuffer();
+
+ // Allocate memory for the buffer and initialize the buffer pointers
+ bool initBuffer(Uint32 aRemoteNodeId);
+
+ // Number of bytes remaining in the buffer
+ Uint32 bufferSizeRemaining();
+
+ // Number of bytes of data in the buffer
+ int bufferSize();
+
+ // Empty the buffer
+ void emptyBuffer();
+
+ /**
+ * The transporter calls updateBuffer after a retrieve followed by
+ * a successful send, to update the cirkular buffer pointers.
+ * updateBuffer is called with the number of bytes really sent,
+ * it may be that it is less than what was retrived from the buffer.
+ * If that is the case there will be an incomplete message (slack)
+ * in the SendBuffer.
+ *
+ * Returns 0 if buffer empty
+ * else ~0
+ */
+ Uint32 bytesSent(Uint32 len);
+
+#ifdef DEBUG_TRANSPORTER
+ // Prints the buffer status on the screen. Can be used for testing purposes.
+ void print();
+#endif
+
+ Uint32* getInsertPtr(Uint32 bytes);
+ void updateInsertPtr(Uint32 bytes);
+
+private:
+
+ Uint32 sizeOfBuffer; // Length, in number of bytes, of the buffer memory
+ Uint32 dataSize; // Number of bytes in buffer
+
+ Uint32 * startOfBuffer; // Pointer to the start of the buffer memory
+ Uint32 * endOfBuffer; // Pointer to end of buffer
+
+ Uint32 * insertPtr; // Where to insert next
+
+ char * sendPtr; // Where data to send starts
+ Uint32 sendDataSize; // Num bytes to send
+
+ Uint32 theRemoteNodeId;
+};
+
+inline
+Uint32
+SendBuffer::bytesSent(Uint32 bytes) {
+
+ if(bytes > dataSize){
+#ifdef DEBUG_TRANSPORTER
+ printf("bytes(%d) > dataSize(%d)\n", bytes, dataSize);
+#endif
+ abort();
+ // reportError(0 ,theRemoteNodeId, TE_INVALID_MESSAGE_LENGTH);
+ return 0;
+ }//if
+
+ if(bytes > sendDataSize){
+#ifdef DEBUG_TRANSPORTER
+ printf("bytes(%d) > sendDataSize(%d)\n", bytes, sendDataSize);
+#endif
+ abort();
+ //reportError(0,theRemoteNodeId, TE_INVALID_MESSAGE_LENGTH);
+ return 0;
+ }//if
+
+ dataSize -= bytes;
+ sendPtr += bytes;
+ sendDataSize -= bytes;
+
+ if(sendDataSize == 0){
+ if(sendPtr > (char*)insertPtr){
+ sendPtr = (char *)startOfBuffer;
+ sendDataSize = dataSize;
+ } else {
+ sendPtr = ((char*)insertPtr) - dataSize;
+ sendDataSize = dataSize;
+ }
+ }
+
+ if(dataSize == 0)
+ return 0;
+ return ~0;
+}
+
+inline
+Uint32*
+SendBuffer::getInsertPtr(Uint32 len){
+ if (bufferSizeRemaining() < len){
+ return 0;
+ }
+
+ const char * const tmpInsertPtr = (char *) insertPtr;
+
+ if(tmpInsertPtr >= sendPtr){
+ // Is there enough space at the end of the buffer?
+ if ((tmpInsertPtr + len) < (char*)endOfBuffer){
+ sendDataSize += len;
+ return insertPtr;
+ } else {
+ // We have passed the end of the cirkular buffer,
+ // must start from the beginning
+ // Is there enough space in the beginning of the buffer?
+ if ((Uint32)(sendPtr - (char *)startOfBuffer) <= len){
+ // Not enough space available, insert failed
+ return 0;
+ } else {
+ // There is space available at the beginning of the buffer
+ // We start from the beginning, set endOfData and insertPtr
+ insertPtr = startOfBuffer;
+ if(sendDataSize != 0){
+ return insertPtr;
+ }
+ sendPtr = (char *)startOfBuffer;
+ sendDataSize = len;
+ return insertPtr;
+ }
+ }
+ } else {
+ // sendPtr > insertPtr
+ // Is there enought room
+ if((tmpInsertPtr + len) < sendPtr){
+ return insertPtr;
+ }
+ return 0;
+ }
+}
+
+inline
+void
+SendBuffer::updateInsertPtr(Uint32 lenBytes){
+ dataSize += lenBytes;
+ insertPtr += (lenBytes / 4);
+}
+
+#endif // Define of SendBuffer_H
diff --git a/storage/ndb/src/common/transporter/TCP_Transporter.cpp b/storage/ndb/src/common/transporter/TCP_Transporter.cpp
new file mode 100644
index 00000000000..fd71cf71cd9
--- /dev/null
+++ b/storage/ndb/src/common/transporter/TCP_Transporter.cpp
@@ -0,0 +1,434 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbTCP.h>
+#include "TCP_Transporter.hpp"
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+// End of stuff to be moved
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+#define inet_send inet_send
+#else
+#define inet_send send
+#endif
+
+#ifdef NDB_WIN32
+class ndbstrerror
+{
+public:
+ ndbstrerror(int iError);
+ ~ndbstrerror(void);
+ operator char*(void) { return m_szError; };
+
+private:
+ int m_iError;
+ char* m_szError;
+};
+
+ndbstrerror::ndbstrerror(int iError)
+: m_iError(iError)
+{
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ iError,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&m_szError,
+ 0,
+ 0);
+}
+
+ndbstrerror::~ndbstrerror(void)
+{
+ LocalFree( m_szError );
+ m_szError = 0;
+}
+#else
+#define ndbstrerror strerror
+#endif
+
+TCP_Transporter::TCP_Transporter(TransporterRegistry &t_reg,
+ int sendBufSize, int maxRecvSize,
+ const char *lHostName,
+ const char *rHostName,
+ int r_port,
+ bool isMgmConnection,
+ NodeId lNodeId,
+ NodeId rNodeId,
+ NodeId serverNodeId,
+ bool chksm, bool signalId,
+ Uint32 _reportFreq) :
+ Transporter(t_reg, tt_TCP_TRANSPORTER,
+ lHostName, rHostName, r_port, isMgmConnection,
+ lNodeId, rNodeId, serverNodeId,
+ 0, false, chksm, signalId),
+ m_sendBuffer(sendBufSize)
+{
+ maxReceiveSize = maxRecvSize;
+
+ // Initialize member variables
+ theSocket = NDB_INVALID_SOCKET;
+
+ sendCount = receiveCount = 0;
+ sendSize = receiveSize = 0;
+ reportFreq = _reportFreq;
+
+ sockOptRcvBufSize = 70080;
+ sockOptSndBufSize = 71540;
+ sockOptNodelay = 1;
+ sockOptTcpMaxSeg = 4096;
+}
+
+TCP_Transporter::~TCP_Transporter() {
+
+ // Disconnect
+ if (theSocket != NDB_INVALID_SOCKET)
+ doDisconnect();
+
+ // Delete send buffers
+
+ // Delete receive buffer!!
+ receiveBuffer.destroy();
+}
+
+bool TCP_Transporter::connect_server_impl(NDB_SOCKET_TYPE sockfd)
+{
+ DBUG_ENTER("TCP_Transpporter::connect_server_impl");
+ DBUG_RETURN(connect_common(sockfd));
+}
+
+bool TCP_Transporter::connect_client_impl(NDB_SOCKET_TYPE sockfd)
+{
+ DBUG_ENTER("TCP_Transpporter::connect_client_impl");
+ DBUG_RETURN(connect_common(sockfd));
+}
+
+bool TCP_Transporter::connect_common(NDB_SOCKET_TYPE sockfd)
+{
+ theSocket = sockfd;
+ setSocketOptions();
+ setSocketNonBlocking(theSocket);
+ DBUG_PRINT("info", ("Successfully set-up TCP transporter to node %d",
+ remoteNodeId));
+ return true;
+}
+
+bool
+TCP_Transporter::initTransporter() {
+
+ // Allocate buffer for receiving
+ // Let it be the maximum size we receive plus 8 kB for any earlier received
+ // incomplete messages (slack)
+ Uint32 recBufSize = maxReceiveSize;
+ if(recBufSize < MAX_MESSAGE_SIZE){
+ recBufSize = MAX_MESSAGE_SIZE;
+ }
+
+ if(!receiveBuffer.init(recBufSize+MAX_MESSAGE_SIZE)){
+ return false;
+ }
+
+ // Allocate buffers for sending
+ if (!m_sendBuffer.initBuffer(remoteNodeId)) {
+ // XXX What shall be done here?
+ // The same is valid for the other init-methods
+ return false;
+ }
+
+ return true;
+}
+
+void
+TCP_Transporter::setSocketOptions(){
+ if (setsockopt(theSocket, SOL_SOCKET, SO_RCVBUF,
+ (char*)&sockOptRcvBufSize, sizeof(sockOptRcvBufSize)) < 0) {
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("The setsockopt SO_RCVBUF error code = %d", InetErrno);
+#endif
+ }//if
+
+ if (setsockopt(theSocket, SOL_SOCKET, SO_SNDBUF,
+ (char*)&sockOptSndBufSize, sizeof(sockOptSndBufSize)) < 0) {
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("The setsockopt SO_SNDBUF error code = %d", InetErrno);
+#endif
+ }//if
+
+ //-----------------------------------------------
+ // Set the TCP_NODELAY option so also small packets are sent
+ // as soon as possible
+ //-----------------------------------------------
+ if (setsockopt(theSocket, IPPROTO_TCP, TCP_NODELAY,
+ (char*)&sockOptNodelay, sizeof(sockOptNodelay)) < 0) {
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("The setsockopt TCP_NODELAY error code = %d", InetErrno);
+#endif
+ }//if
+}
+
+
+#ifdef NDB_WIN32
+
+bool
+TCP_Transporter::setSocketNonBlocking(NDB_SOCKET_TYPE socket){
+ unsigned long ul = 1;
+ if(ioctlsocket(socket, FIONBIO, &ul))
+ {
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("Set non-blocking server error3: %d", InetErrno);
+#endif
+ }//if
+ return true;
+}
+
+#else
+
+bool
+TCP_Transporter::setSocketNonBlocking(NDB_SOCKET_TYPE socket){
+ int flags;
+ flags = fcntl(socket, F_GETFL, 0);
+ if (flags < 0) {
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("Set non-blocking server error1: %s", strerror(InetErrno));
+#endif
+ }//if
+ flags |= NDB_NONBLOCK;
+ if (fcntl(socket, F_SETFL, flags) == -1) {
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("Set non-blocking server error2: %s", strerror(InetErrno));
+#endif
+ }//if
+ return true;
+}
+
+#endif
+
+bool
+TCP_Transporter::sendIsPossible(struct timeval * timeout) {
+#ifdef NDB_OSE
+ /**
+ * In OSE you cant do select without owning a socket,
+ * and since this method might be called by any thread in the api
+ * we choose not to implementet and always return true after sleeping
+ * a while.
+ *
+ * Note that this only sensible as long as the sockets are non blocking
+ */
+ if(theSocket >= 0){
+ Uint32 timeOutMillis = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+ NdbSleep_MilliSleep(timeOutMillis);
+ return true;
+ }
+ return false;
+#else
+ if(theSocket != NDB_INVALID_SOCKET){
+ fd_set writeset;
+ FD_ZERO(&writeset);
+ FD_SET(theSocket, &writeset);
+
+ int selectReply = select(theSocket + 1, NULL, &writeset, NULL, timeout);
+
+ if ((selectReply > 0) && FD_ISSET(theSocket, &writeset))
+ return true;
+ else
+ return false;
+ }
+ return false;
+#endif
+}
+
+
+Uint32 *
+TCP_Transporter::getWritePtr(Uint32 lenBytes, Uint32 prio){
+
+ Uint32 * insertPtr = m_sendBuffer.getInsertPtr(lenBytes);
+
+ struct timeval timeout = {0, 10000};
+
+ if (insertPtr == 0) {
+ //-------------------------------------------------
+ // Buffer was completely full. We have severe problems.
+ // We will attempt to wait for a small time
+ //-------------------------------------------------
+ if(sendIsPossible(&timeout)) {
+ //-------------------------------------------------
+ // Send is possible after the small timeout.
+ //-------------------------------------------------
+ if(!doSend()){
+ return 0;
+ } else {
+ //-------------------------------------------------
+ // Since send was successful we will make a renewed
+ // attempt at inserting the signal into the buffer.
+ //-------------------------------------------------
+ insertPtr = m_sendBuffer.getInsertPtr(lenBytes);
+ }//if
+ } else {
+ return 0;
+ }//if
+ }
+ return insertPtr;
+}
+
+void
+TCP_Transporter::updateWritePtr(Uint32 lenBytes, Uint32 prio){
+ m_sendBuffer.updateInsertPtr(lenBytes);
+
+ const int bufsize = m_sendBuffer.bufferSize();
+ if(bufsize > TCP_SEND_LIMIT) {
+ //-------------------------------------------------
+ // Buffer is full and we are ready to send. We will
+ // not wait since the signal is already in the buffer.
+ // Force flag set has the same indication that we
+ // should always send. If it is not possible to send
+ // we will not worry since we will soon be back for
+ // a renewed trial.
+ //-------------------------------------------------
+ struct timeval no_timeout = {0,0};
+ if(sendIsPossible(&no_timeout)) {
+ //-------------------------------------------------
+ // Send was possible, attempt at a send.
+ //-------------------------------------------------
+ doSend();
+ }//if
+ }
+}
+
+#define DISCONNECT_ERRNO(e, sz) ((sz == 0) || \
+ (!((sz == -1) && (e == EAGAIN) || (e == EWOULDBLOCK) || (e == EINTR))))
+
+
+bool
+TCP_Transporter::doSend() {
+ // If no sendbuffers are used nothing is done
+ // Sends the contents of the SendBuffers until they are empty
+ // or until select does not select the socket for write.
+ // Before calling send, the socket must be selected for write
+ // using "select"
+ // It writes on the external TCP/IP interface until the send buffer is empty
+ // and as long as write is possible (test it using select)
+
+ // Empty the SendBuffers
+
+ const char * const sendPtr = m_sendBuffer.sendPtr;
+ const Uint32 sizeToSend = m_sendBuffer.sendDataSize;
+ if (sizeToSend > 0){
+ const int nBytesSent = inet_send(theSocket, sendPtr, sizeToSend, 0);
+
+ if (nBytesSent > 0) {
+ m_sendBuffer.bytesSent(nBytesSent);
+
+ sendCount ++;
+ sendSize += nBytesSent;
+ if(sendCount == reportFreq){
+ reportSendLen(get_callback_obj(), remoteNodeId, sendCount, sendSize);
+ sendCount = 0;
+ sendSize = 0;
+ }
+ } else {
+ // Send failed
+#if defined DEBUG_TRANSPORTER
+ ndbout_c("Send Failure(disconnect==%d) to node = %d nBytesSent = %d "
+ "errno = %d strerror = %s",
+ DISCONNECT_ERRNO(InetErrno, nBytesSent),
+ remoteNodeId, nBytesSent, InetErrno,
+ (char*)ndbstrerror(InetErrno));
+#endif
+ if(DISCONNECT_ERRNO(InetErrno, nBytesSent)){
+ doDisconnect();
+ report_disconnect(InetErrno);
+ }
+
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+TCP_Transporter::doReceive() {
+ // Select-function must return the socket for read
+ // before this method is called
+ // It reads the external TCP/IP interface once
+ Uint32 size = receiveBuffer.sizeOfBuffer - receiveBuffer.sizeOfData;
+ if(size > 0){
+ const int nBytesRead = recv(theSocket,
+ receiveBuffer.insertPtr,
+ size < maxReceiveSize ? size : maxReceiveSize,
+ 0);
+
+ if (nBytesRead > 0) {
+ receiveBuffer.sizeOfData += nBytesRead;
+ receiveBuffer.insertPtr += nBytesRead;
+
+ if(receiveBuffer.sizeOfData > receiveBuffer.sizeOfBuffer){
+#ifdef DEBUG_TRANSPORTER
+ ndbout_c("receiveBuffer.sizeOfData(%d) > receiveBuffer.sizeOfBuffer(%d)",
+ receiveBuffer.sizeOfData, receiveBuffer.sizeOfBuffer);
+ ndbout_c("nBytesRead = %d", nBytesRead);
+#endif
+ ndbout_c("receiveBuffer.sizeOfData(%d) > receiveBuffer.sizeOfBuffer(%d)",
+ receiveBuffer.sizeOfData, receiveBuffer.sizeOfBuffer);
+ report_error(TE_INVALID_MESSAGE_LENGTH);
+ return 0;
+ }
+
+ receiveCount ++;
+ receiveSize += nBytesRead;
+
+ if(receiveCount == reportFreq){
+ reportReceiveLen(get_callback_obj(), remoteNodeId, receiveCount, receiveSize);
+ receiveCount = 0;
+ receiveSize = 0;
+ }
+ return nBytesRead;
+ } else {
+#if defined DEBUG_TRANSPORTER
+ ndbout_c("Receive Failure(disconnect==%d) to node = %d nBytesSent = %d "
+ "errno = %d strerror = %s",
+ DISCONNECT_ERRNO(InetErrno, nBytesRead),
+ remoteNodeId, nBytesRead, InetErrno,
+ (char*)ndbstrerror(InetErrno));
+#endif
+ if(DISCONNECT_ERRNO(InetErrno, nBytesRead)){
+ // The remote node has closed down
+ doDisconnect();
+ report_disconnect(InetErrno);
+ }
+ }
+ return nBytesRead;
+ } else {
+ return 0;
+ }
+}
+
+void
+TCP_Transporter::disconnectImpl() {
+ if(theSocket != NDB_INVALID_SOCKET){
+ if(NDB_CLOSE_SOCKET(theSocket) < 0){
+ report_error(TE_ERROR_CLOSING_SOCKET);
+ }
+ }
+
+ // Empty send och receive buffers
+ receiveBuffer.clear();
+ m_sendBuffer.emptyBuffer();
+
+ theSocket = NDB_INVALID_SOCKET;
+}
diff --git a/storage/ndb/src/common/transporter/TCP_Transporter.hpp b/storage/ndb/src/common/transporter/TCP_Transporter.hpp
new file mode 100644
index 00000000000..9cd174150c1
--- /dev/null
+++ b/storage/ndb/src/common/transporter/TCP_Transporter.hpp
@@ -0,0 +1,232 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TCP_TRANSPORTER_HPP
+#define TCP_TRANSPORTER_HPP
+
+#include "Transporter.hpp"
+#include "SendBuffer.hpp"
+
+#include <NdbTCP.h>
+
+struct ReceiveBuffer {
+ Uint32 *startOfBuffer; // Pointer to start of the receive buffer
+ Uint32 *readPtr; // Pointer to start reading data
+
+ char *insertPtr; // Pointer to first position in the receiveBuffer
+ // in which to insert received data. Earlier
+ // received incomplete messages (slack) are
+ // copied into the first part of the receiveBuffer
+
+ Uint32 sizeOfData; // In bytes
+ Uint32 sizeOfBuffer;
+
+ bool init(int bytes);
+ void destroy();
+
+ void clear();
+ void incompleteMessage();
+};
+
+class TCP_Transporter : public Transporter {
+ friend class TransporterRegistry;
+private:
+ // Initialize member variables
+ TCP_Transporter(TransporterRegistry&,
+ int sendBufferSize, int maxReceiveSize,
+ const char *lHostName,
+ const char *rHostName,
+ int r_port,
+ bool isMgmConnection,
+ NodeId lHostId,
+ NodeId rHostId,
+ NodeId serverNodeId,
+ bool checksum, bool signalId,
+ Uint32 reportFreq = 4096);
+
+ // Disconnect, delete send buffers and receive buffer
+ virtual ~TCP_Transporter();
+
+ /**
+ * Allocate buffers for sending and receiving
+ */
+ bool initTransporter();
+
+ Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio);
+ void updateWritePtr(Uint32 lenBytes, Uint32 prio);
+
+ bool hasDataToSend() const ;
+
+ /**
+ * Retrieves the contents of the send buffers and writes it on
+ * the external TCP/IP interface until the send buffers are empty
+ * and as long as write is possible.
+ */
+ bool doSend();
+
+ /**
+ * It reads the external TCP/IP interface once
+ * and puts the data in the receiveBuffer
+ */
+ int doReceive();
+
+ /**
+ * Returns socket (used for select)
+ */
+ NDB_SOCKET_TYPE getSocket() const;
+
+ /**
+ * Get Receive Data
+ *
+ * Returns - no of bytes to read
+ * and set ptr
+ */
+ virtual Uint32 getReceiveData(Uint32 ** ptr);
+
+ /**
+ * Update receive data ptr
+ */
+ virtual void updateReceiveDataPtr(Uint32 bytesRead);
+
+protected:
+ /**
+ * Setup client/server and perform connect/accept
+ * Is used both by clients and servers
+ * A client connects to the remote server
+ * A server accepts any new connections
+ */
+ virtual bool connect_server_impl(NDB_SOCKET_TYPE sockfd);
+ virtual bool connect_client_impl(NDB_SOCKET_TYPE sockfd);
+ bool connect_common(NDB_SOCKET_TYPE sockfd);
+
+ /**
+ * Disconnects a TCP/IP node. Empty send and receivebuffer.
+ */
+ virtual void disconnectImpl();
+
+private:
+ /**
+ * Send buffers
+ */
+ SendBuffer m_sendBuffer;
+
+ // Sending/Receiving socket used by both client and server
+ NDB_SOCKET_TYPE theSocket;
+
+ Uint32 maxReceiveSize;
+
+ /**
+ * Socket options
+ */
+ int sockOptRcvBufSize;
+ int sockOptSndBufSize;
+ int sockOptNodelay;
+ int sockOptTcpMaxSeg;
+
+ void setSocketOptions();
+
+ static bool setSocketNonBlocking(NDB_SOCKET_TYPE aSocket);
+
+ bool sendIsPossible(struct timeval * timeout);
+
+ /**
+ * Statistics
+ */
+ Uint32 reportFreq;
+ Uint32 receiveCount;
+ Uint64 receiveSize;
+ Uint32 sendCount;
+ Uint64 sendSize;
+
+ ReceiveBuffer receiveBuffer;
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ PROCESS theReceiverPid;
+#endif
+};
+
+inline
+NDB_SOCKET_TYPE
+TCP_Transporter::getSocket() const {
+ return theSocket;
+}
+
+inline
+Uint32
+TCP_Transporter::getReceiveData(Uint32 ** ptr){
+ (* ptr) = receiveBuffer.readPtr;
+ return receiveBuffer.sizeOfData;
+}
+
+inline
+void
+TCP_Transporter::updateReceiveDataPtr(Uint32 bytesRead){
+ char * ptr = (char *)receiveBuffer.readPtr;
+ ptr += bytesRead;
+ receiveBuffer.readPtr = (Uint32*)ptr;
+ receiveBuffer.sizeOfData -= bytesRead;
+ receiveBuffer.incompleteMessage();
+}
+
+inline
+bool
+TCP_Transporter::hasDataToSend() const {
+ return m_sendBuffer.dataSize > 0;
+}
+
+inline
+bool
+ReceiveBuffer::init(int bytes){
+#ifdef DEBUG_TRANSPORTER
+ ndbout << "Allocating " << bytes << " bytes as receivebuffer" << endl;
+#endif
+
+ startOfBuffer = new Uint32[((bytes + 0) >> 2) + 1];
+ sizeOfBuffer = bytes + sizeof(Uint32);
+ clear();
+ return true;
+}
+
+inline
+void
+ReceiveBuffer::destroy(){
+ delete[] startOfBuffer;
+ sizeOfBuffer = 0;
+ startOfBuffer = 0;
+ clear();
+}
+
+inline
+void
+ReceiveBuffer::clear(){
+ readPtr = startOfBuffer;
+ insertPtr = (char *)startOfBuffer;
+ sizeOfData = 0;
+}
+
+inline
+void
+ReceiveBuffer::incompleteMessage() {
+ if(startOfBuffer != readPtr){
+ if(sizeOfData != 0)
+ memmove(startOfBuffer, readPtr, sizeOfData);
+ readPtr = startOfBuffer;
+ insertPtr = ((char *)startOfBuffer) + sizeOfData;
+ }
+}
+
+
+#endif // Define of TCP_Transporter_H
diff --git a/storage/ndb/src/common/transporter/Transporter.cpp b/storage/ndb/src/common/transporter/Transporter.cpp
new file mode 100644
index 00000000000..124ed5f7241
--- /dev/null
+++ b/storage/ndb/src/common/transporter/Transporter.cpp
@@ -0,0 +1,208 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <TransporterRegistry.hpp>
+#include <TransporterCallback.hpp>
+#include "Transporter.hpp"
+#include "TransporterInternalDefinitions.hpp"
+#include <NdbSleep.h>
+#include <SocketAuthenticator.hpp>
+#include <InputStream.hpp>
+#include <OutputStream.hpp>
+
+#include <EventLogger.hpp>
+extern EventLogger g_eventLogger;
+
+Transporter::Transporter(TransporterRegistry &t_reg,
+ TransporterType _type,
+ const char *lHostName,
+ const char *rHostName,
+ int s_port,
+ bool _isMgmConnection,
+ NodeId lNodeId,
+ NodeId rNodeId,
+ NodeId serverNodeId,
+ int _byteorder,
+ bool _compression, bool _checksum, bool _signalId)
+ : m_s_port(s_port), remoteNodeId(rNodeId), localNodeId(lNodeId),
+ isServer(lNodeId==serverNodeId), isMgmConnection(_isMgmConnection),
+ m_packer(_signalId, _checksum),
+ m_type(_type),
+ m_transporter_registry(t_reg)
+{
+ DBUG_ENTER("Transporter::Transporter");
+ if (rHostName && strlen(rHostName) > 0){
+ strncpy(remoteHostName, rHostName, sizeof(remoteHostName));
+ Ndb_getInAddr(&remoteHostAddress, rHostName);
+ }
+ else
+ {
+ if (!isServer) {
+ ndbout << "Unable to setup transporter. Node " << rNodeId
+ << " must have hostname. Update configuration." << endl;
+ exit(-1);
+ }
+ remoteHostName[0]= 0;
+ }
+ strncpy(localHostName, lHostName, sizeof(localHostName));
+
+ if (strlen(lHostName) > 0)
+ Ndb_getInAddr(&localHostAddress, lHostName);
+
+ DBUG_PRINT("info",("rId=%d lId=%d isServer=%d rHost=%s lHost=%s s_port=%d",
+ remoteNodeId, localNodeId, isServer,
+ remoteHostName, localHostName,
+ s_port));
+
+ byteOrder = _byteorder;
+ compressionUsed = _compression;
+ checksumUsed = _checksum;
+ signalIdUsed = _signalId;
+
+ m_connected = false;
+ m_timeOutMillis = 1000;
+
+ if(s_port<0)
+ s_port= -s_port; // was dynamic
+
+ if (isServer)
+ m_socket_client= 0;
+ else
+ m_socket_client= new SocketClient(remoteHostName, s_port,
+ new SocketAuthSimple("ndbd",
+ "ndbd passwd"));
+ DBUG_VOID_RETURN;
+}
+
+Transporter::~Transporter(){
+ if (m_socket_client)
+ delete m_socket_client;
+}
+
+bool
+Transporter::connect_server(NDB_SOCKET_TYPE sockfd) {
+ // all initial negotiation is done in TransporterRegistry::connect_server
+ DBUG_ENTER("Transporter::connect_server");
+
+ if(m_connected)
+ {
+ DBUG_RETURN(true); // TODO assert(0);
+ }
+
+ bool res = connect_server_impl(sockfd);
+ if(res){
+ m_connected = true;
+ m_errorCount = 0;
+ }
+
+ DBUG_RETURN(res);
+}
+
+bool
+Transporter::connect_client() {
+ NDB_SOCKET_TYPE sockfd;
+
+ if(m_connected)
+ return true;
+
+ if(isMgmConnection)
+ sockfd= m_transporter_registry.connect_ndb_mgmd(m_socket_client);
+ else
+ sockfd= m_socket_client->connect();
+
+ return connect_client(sockfd);
+}
+
+bool
+Transporter::connect_client(NDB_SOCKET_TYPE sockfd) {
+
+ if(m_connected)
+ return true;
+
+ if (sockfd == NDB_INVALID_SOCKET)
+ return false;
+
+ DBUG_ENTER("Transporter::connect_client");
+
+ DBUG_PRINT("info",("port %d isMgmConnection=%d",m_s_port,isMgmConnection));
+
+ SocketOutputStream s_output(sockfd);
+ SocketInputStream s_input(sockfd);
+
+ // send info about own id
+ // send info about own transporter type
+
+ s_output.println("%d %d", localNodeId, m_type);
+ // get remote id
+ int nodeId, remote_transporter_type= -1;
+
+ char buf[256];
+ if (s_input.gets(buf, 256) == 0) {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ int r= sscanf(buf, "%d %d", &nodeId, &remote_transporter_type);
+ switch (r) {
+ case 2:
+ break;
+ case 1:
+ // we're running version prior to 4.1.9
+ // ok, but with no checks on transporter configuration compatability
+ break;
+ default:
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(false);
+ }
+
+ DBUG_PRINT("info", ("nodeId=%d remote_transporter_type=%d",
+ nodeId, remote_transporter_type));
+
+ if (remote_transporter_type != -1)
+ {
+ if (remote_transporter_type != m_type)
+ {
+ DBUG_PRINT("error", ("Transporter types mismatch this=%d remote=%d",
+ m_type, remote_transporter_type));
+ NDB_CLOSE_SOCKET(sockfd);
+ g_eventLogger.error("Incompatible configuration: transporter type "
+ "mismatch with node %d", nodeId);
+ DBUG_RETURN(false);
+ }
+ }
+ else if (m_type == tt_SHM_TRANSPORTER)
+ {
+ g_eventLogger.warning("Unable to verify transporter compatability with node %d", nodeId);
+ }
+
+ bool res = connect_client_impl(sockfd);
+ if(res){
+ m_connected = true;
+ m_errorCount = 0;
+ }
+ DBUG_RETURN(res);
+}
+
+void
+Transporter::doDisconnect() {
+
+ if(!m_connected)
+ return; //assert(0); TODO will fail
+
+ m_connected= false;
+ disconnectImpl();
+}
diff --git a/storage/ndb/src/common/transporter/Transporter.hpp b/storage/ndb/src/common/transporter/Transporter.hpp
new file mode 100644
index 00000000000..53414f1179d
--- /dev/null
+++ b/storage/ndb/src/common/transporter/Transporter.hpp
@@ -0,0 +1,189 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 Transporter_H
+#define Transporter_H
+
+#include <ndb_global.h>
+
+#include <SocketClient.hpp>
+
+#include <TransporterRegistry.hpp>
+#include <TransporterCallback.hpp>
+#include "TransporterDefinitions.hpp"
+#include "Packer.hpp"
+
+#include <NdbMutex.h>
+#include <NdbThread.h>
+
+class Transporter {
+ friend class TransporterRegistry;
+public:
+ virtual bool initTransporter() = 0;
+
+ /**
+ * Destructor
+ */
+ virtual ~Transporter();
+
+ /**
+ * None blocking
+ * Use isConnected() to check status
+ */
+ bool connect_client();
+ bool connect_client(NDB_SOCKET_TYPE sockfd);
+ bool connect_server(NDB_SOCKET_TYPE socket);
+
+ /**
+ * Blocking
+ */
+ virtual void doDisconnect();
+
+ virtual Uint32 * getWritePtr(Uint32 lenBytes, Uint32 prio) = 0;
+ virtual void updateWritePtr(Uint32 lenBytes, Uint32 prio) = 0;
+
+ /**
+ * Are we currently connected
+ */
+ bool isConnected() const;
+
+ /**
+ * Remote Node Id
+ */
+ NodeId getRemoteNodeId() const;
+
+ /**
+ * Local (own) Node Id
+ */
+ NodeId getLocalNodeId() const;
+
+ /**
+ * Get port we're connecting to (signed)
+ */
+ int get_s_port() { return m_s_port; };
+
+ /**
+ * Set port to connect to (signed)
+ */
+ void set_s_port(int port) {
+ m_s_port = port;
+ if(port<0)
+ port= -port;
+ if(m_socket_client)
+ m_socket_client->set_port(port);
+ };
+
+protected:
+ Transporter(TransporterRegistry &,
+ TransporterType,
+ const char *lHostName,
+ const char *rHostName,
+ int s_port,
+ bool isMgmConnection,
+ NodeId lNodeId,
+ NodeId rNodeId,
+ NodeId serverNodeId,
+ int byteorder,
+ bool compression,
+ bool checksum,
+ bool signalId);
+
+ /**
+ * Blocking, for max timeOut milli seconds
+ * Returns true if connect succeded
+ */
+ virtual bool connect_server_impl(NDB_SOCKET_TYPE sockfd) = 0;
+ virtual bool connect_client_impl(NDB_SOCKET_TYPE sockfd) = 0;
+
+ /**
+ * Blocking
+ */
+ virtual void disconnectImpl() = 0;
+
+ /**
+ * Remote host name/and address
+ */
+ char remoteHostName[256];
+ char localHostName[256];
+ struct in_addr remoteHostAddress;
+ struct in_addr localHostAddress;
+
+ int m_s_port;
+
+ const NodeId remoteNodeId;
+ const NodeId localNodeId;
+
+ const bool isServer;
+
+ unsigned createIndex;
+
+ int byteOrder;
+ bool compressionUsed;
+ bool checksumUsed;
+ bool signalIdUsed;
+ Packer m_packer;
+
+private:
+
+ /**
+ * means that we transform an MGM connection into
+ * a transporter connection
+ */
+ bool isMgmConnection;
+
+ SocketClient *m_socket_client;
+
+protected:
+ Uint32 getErrorCount();
+ Uint32 m_errorCount;
+ Uint32 m_timeOutMillis;
+
+protected:
+ bool m_connected; // Are we connected
+ TransporterType m_type;
+
+ TransporterRegistry &m_transporter_registry;
+ void *get_callback_obj() { return m_transporter_registry.callbackObj; };
+ void report_disconnect(int err){m_transporter_registry.report_disconnect(remoteNodeId,err);};
+ void report_error(enum TransporterError err){reportError(get_callback_obj(),remoteNodeId,err);};
+};
+
+inline
+bool
+Transporter::isConnected() const {
+ return m_connected;
+}
+
+inline
+NodeId
+Transporter::getRemoteNodeId() const {
+ return remoteNodeId;
+}
+
+inline
+NodeId
+Transporter::getLocalNodeId() const {
+ return localNodeId;
+}
+
+inline
+Uint32
+Transporter::getErrorCount()
+{
+ return m_errorCount;
+}
+
+#endif // Define of Transporter_H
diff --git a/storage/ndb/src/common/transporter/TransporterInternalDefinitions.hpp b/storage/ndb/src/common/transporter/TransporterInternalDefinitions.hpp
new file mode 100644
index 00000000000..624b495422f
--- /dev/null
+++ b/storage/ndb/src/common/transporter/TransporterInternalDefinitions.hpp
@@ -0,0 +1,302 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TransporterInternalDefinitions_H
+#define TransporterInternalDefinitions_H
+
+#if defined DEBUG_TRANSPORTER || defined VM_TRACE
+#include <NdbOut.hpp>
+#endif
+
+#define NDB_TCP_TRANSPORTER
+
+#ifdef HAVE_NDB_SHM
+#define NDB_SHM_TRANSPORTER
+#endif
+
+#ifdef HAVE_NDB_SCI
+#define NDB_SCI_TRANSPORTER
+#endif
+
+#ifdef HAVE_NDB_OSE
+#define NDB_OSE_TRANSPORTER
+#endif
+
+#ifdef DEBUG_TRANSPORTER
+#define DEBUG(x) ndbout << x << endl
+#else
+#define DEBUG(x)
+#endif
+
+#if defined VM_TRACE || defined DEBUG_TRANSPORTER
+#define WARNING(X) ndbout << X << endl;
+#else
+#define WARNING(X)
+#endif
+
+// Calculate a checksum
+inline
+Uint32
+computeChecksum(const Uint32 * const startOfData, int nWords) {
+ Uint32 chksum = startOfData[0];
+ for (int i=1; i < nWords; i++)
+ chksum ^= startOfData[i];
+ return chksum;
+}
+
+struct Protocol6 {
+ Uint32 word1;
+ Uint32 word2;
+ Uint32 word3;
+
+/**
+ *
+ * b = Byte order - 4 Bits (Note 1 significant bit)
+ * g = GSN - 16 Bits
+ * p = Prio - 2 Bits
+ * c = Checksum included - 1 Bit
+ * z = Compression - 1 Bit
+ * v = Version id - 4 Bits
+ * i = Signal id included - 1 Bit
+ * m = Message length - 16 Bits (0-65536) (In word -> 0-256k bytes)
+ * d = Signal data length - 5 Bits (0-31)
+ * t = trace - 6 Bits (0-63)
+ * r = Recievers block no - 16 Bits
+ * s = Senders block no - 16 Bits
+ * u = Unused - 7 Bits
+ * f = FragmentInfo1 - 1 Bit
+ * h = FragmentInfo2 - 1 bit
+ * n = No of segments - 2 Bits
+
+ * Word 1
+ *
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * bfizcppbmmmmmmmmmmmmmmmmbhdddddb
+
+ **
+ * Word 2
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * ggggggggggggggggvvvvttttttnn
+
+ **
+ * Word 3
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * rrrrrrrrrrrrrrrrssssssssssssssss
+
+ **
+ * Word 4 (Optional Signal Id)
+ */
+
+ /**
+ * 0 = Big endian (Sparc), 1 = Little endian (Intel)
+ */
+ static Uint32 getByteOrder (const Uint32 & word1);
+ static Uint32 getCompressed (const Uint32 & word1);
+ static Uint32 getSignalIdIncluded(const Uint32 & word1);
+ static Uint32 getCheckSumIncluded(const Uint32 & word1);
+ static Uint32 getPrio (const Uint32 & word1);
+ static Uint32 getMessageLength (const Uint32 & word1);
+
+ static void setByteOrder (Uint32 & word1, Uint32 byteOrder);
+ static void setCompressed (Uint32 & word1, Uint32 compressed);
+ static void setSignalIdIncluded(Uint32 & word1, Uint32 signalId);
+ static void setCheckSumIncluded(Uint32 & word1, Uint32 checkSum);
+ static void setPrio (Uint32 & word1, Uint32 prio);
+ static void setMessageLength (Uint32 & word1, Uint32 messageLen);
+
+ static void createSignalHeader(SignalHeader * const dst,
+ const Uint32 & word1,
+ const Uint32 & word2,
+ const Uint32 & word3);
+
+ static void createProtocol6Header(Uint32 & word1,
+ Uint32 & word2,
+ Uint32 & word3,
+ const SignalHeader * const src);
+};
+
+#define WORD1_BYTEORDER_MASK (0x81000081)
+#define WORD1_SIGNALID_MASK (0x00000004)
+#define WORD1_COMPRESSED_MASK (0x00000008)
+#define WORD1_CHECKSUM_MASK (0x00000010)
+#define WORD1_PRIO_MASK (0x00000060)
+#define WORD1_MESSAGELEN_MASK (0x00FFFF00)
+#define WORD1_SIGNAL_LEN_MASK (0x7C000000)
+#define WORD1_FRAG_INF_MASK (0x00000002)
+#define WORD1_FRAG_INF2_MASK (0x02000000)
+
+#define WORD1_FRAG_INF_SHIFT (1)
+#define WORD1_SIGNALID_SHIFT (2)
+#define WORD1_COMPRESSED_SHIFT (3)
+#define WORD1_CHECKSUM_SHIFT (4)
+#define WORD1_PRIO_SHIFT (5)
+#define WORD1_MESSAGELEN_SHIFT (8)
+#define WORD1_FRAG_INF2_SHIFT (25)
+#define WORD1_SIGNAL_LEN_SHIFT (26)
+
+#define WORD2_VERID_GSN_MASK (0x000FFFFF)
+#define WORD2_TRACE_MASK (0x03f00000)
+#define WORD2_SEC_COUNT_MASK (0x0c000000)
+
+#define WORD2_TRACE_SHIFT (20)
+#define WORD2_SEC_COUNT_SHIFT (26)
+
+#define WORD3_SENDER_MASK (0x0000FFFF)
+#define WORD3_RECEIVER_MASK (0xFFFF0000)
+
+#define WORD3_RECEIVER_SHIFT (16)
+
+inline
+Uint32
+Protocol6::getByteOrder(const Uint32 & word1){
+ return word1 & 1;
+}
+
+inline
+Uint32
+Protocol6::getCompressed(const Uint32 & word1){
+ return (word1 & WORD1_COMPRESSED_MASK) >> WORD1_COMPRESSED_SHIFT;
+}
+
+inline
+Uint32
+Protocol6::getSignalIdIncluded(const Uint32 & word1){
+ return (word1 & WORD1_SIGNALID_MASK) >> WORD1_SIGNALID_SHIFT;
+}
+
+inline
+Uint32
+Protocol6::getCheckSumIncluded(const Uint32 & word1){
+ return (word1 & WORD1_CHECKSUM_MASK) >> WORD1_CHECKSUM_SHIFT;
+}
+
+inline
+Uint32
+Protocol6::getMessageLength(const Uint32 & word1){
+ return (word1 & WORD1_MESSAGELEN_MASK) >> WORD1_MESSAGELEN_SHIFT;
+}
+
+inline
+Uint32
+Protocol6::getPrio(const Uint32 & word1){
+ return (word1 & WORD1_PRIO_MASK) >> WORD1_PRIO_SHIFT;
+}
+
+inline
+void
+Protocol6::setByteOrder(Uint32 & word1, Uint32 byteOrder){
+ Uint32 tmp = byteOrder;
+ tmp |= (tmp << 7);
+ tmp |= (tmp << 24);
+ word1 |= (tmp & WORD1_BYTEORDER_MASK);
+}
+
+inline
+void
+Protocol6::setCompressed(Uint32 & word1, Uint32 compressed){
+ word1 |= ((compressed << WORD1_COMPRESSED_SHIFT) & WORD1_COMPRESSED_MASK);
+}
+
+inline
+void
+Protocol6::setSignalIdIncluded(Uint32 & word1, Uint32 signalId){
+ word1 |= ((signalId << WORD1_SIGNALID_SHIFT) & WORD1_SIGNALID_MASK);
+}
+
+inline
+void
+Protocol6::setCheckSumIncluded(Uint32 & word1, Uint32 checkSum){
+ word1 |= ((checkSum << WORD1_CHECKSUM_SHIFT) & WORD1_CHECKSUM_MASK);
+}
+
+inline
+void
+Protocol6::setMessageLength(Uint32 & word1, Uint32 messageLen){
+ word1 |= ((messageLen << WORD1_MESSAGELEN_SHIFT) & WORD1_MESSAGELEN_MASK);
+}
+
+inline
+void
+Protocol6::setPrio(Uint32 & word1, Uint32 prio){
+ word1 |= ((prio << WORD1_PRIO_SHIFT) & WORD1_PRIO_MASK);
+}
+
+inline
+void
+Protocol6::createSignalHeader(SignalHeader * const dst,
+ const Uint32 & word1,
+ const Uint32 & word2,
+ const Uint32 & word3){
+
+ Uint32 signal_len = (word1 & WORD1_SIGNAL_LEN_MASK)>> WORD1_SIGNAL_LEN_SHIFT;
+ Uint32 fragInfo1 = (word1 & WORD1_FRAG_INF_MASK) >> (WORD1_FRAG_INF_SHIFT-1);
+ Uint32 fragInfo2 = (word1 & WORD1_FRAG_INF2_MASK) >> (WORD1_FRAG_INF2_SHIFT);
+ Uint32 trace = (word2 & WORD2_TRACE_MASK) >> WORD2_TRACE_SHIFT;
+ Uint32 verid_gsn = (word2 & WORD2_VERID_GSN_MASK);
+ Uint32 secCount = (word2 & WORD2_SEC_COUNT_MASK) >> WORD2_SEC_COUNT_SHIFT;
+
+ dst->theTrace = trace;
+ dst->m_noOfSections = secCount;
+ dst->m_fragmentInfo = fragInfo1 | fragInfo2;
+
+ dst->theLength = signal_len;
+ dst->theVerId_signalNumber = verid_gsn;
+
+ Uint32 sBlockNum = (word3 & WORD3_SENDER_MASK);
+ Uint32 rBlockNum = (word3 & WORD3_RECEIVER_MASK) >> WORD3_RECEIVER_SHIFT;
+
+ dst->theSendersBlockRef = sBlockNum;
+ dst->theReceiversBlockNumber = rBlockNum;
+}
+
+inline
+void
+Protocol6::createProtocol6Header(Uint32 & word1,
+ Uint32 & word2,
+ Uint32 & word3,
+ const SignalHeader * const src){
+ const Uint32 signal_len = src->theLength;
+ const Uint32 fragInfo = src->m_fragmentInfo;
+ const Uint32 fragInfo1 = (fragInfo & 2);
+ const Uint32 fragInfo2 = (fragInfo & 1);
+
+ const Uint32 trace = src->theTrace;
+ const Uint32 verid_gsn = src->theVerId_signalNumber;
+ const Uint32 secCount = src->m_noOfSections;
+
+ word1 |= ((signal_len << WORD1_SIGNAL_LEN_SHIFT) & WORD1_SIGNAL_LEN_MASK);
+ word1 |= ((fragInfo1 << (WORD1_FRAG_INF_SHIFT-1)) & WORD1_FRAG_INF_MASK);
+ word1 |= ((fragInfo2 << WORD1_FRAG_INF2_SHIFT) & WORD1_FRAG_INF2_MASK);
+
+ word2 |= ((trace << WORD2_TRACE_SHIFT) & WORD2_TRACE_MASK);
+ word2 |= (verid_gsn & WORD2_VERID_GSN_MASK);
+ word2 |= ((secCount << WORD2_SEC_COUNT_SHIFT) & WORD2_SEC_COUNT_MASK);
+
+ Uint32 sBlockNum = src->theSendersBlockRef ;
+ Uint32 rBlockNum = src->theReceiversBlockNumber ;
+
+ word3 |= (sBlockNum & WORD3_SENDER_MASK);
+ word3 |= ((rBlockNum << WORD3_RECEIVER_SHIFT) & WORD3_RECEIVER_MASK);
+}
+
+// Define of TransporterInternalDefinitions_H
+#endif
diff --git a/storage/ndb/src/common/transporter/TransporterRegistry.cpp b/storage/ndb/src/common/transporter/TransporterRegistry.cpp
new file mode 100644
index 00000000000..f331b1660c1
--- /dev/null
+++ b/storage/ndb/src/common/transporter/TransporterRegistry.cpp
@@ -0,0 +1,1625 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+
+#include <TransporterRegistry.hpp>
+#include "TransporterInternalDefinitions.hpp"
+
+#include "Transporter.hpp"
+#include <SocketAuthenticator.hpp>
+
+#ifdef NDB_TCP_TRANSPORTER
+#include "TCP_Transporter.hpp"
+#endif
+
+#ifdef NDB_OSE_TRANSPORTER
+#include "OSE_Receiver.hpp"
+#include "OSE_Transporter.hpp"
+#endif
+
+#ifdef NDB_SCI_TRANSPORTER
+#include "SCI_Transporter.hpp"
+#endif
+
+#ifdef NDB_SHM_TRANSPORTER
+#include "SHM_Transporter.hpp"
+extern int g_ndb_shm_signum;
+#endif
+
+#include "TransporterCallback.hpp"
+#include "NdbOut.hpp"
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <InputStream.hpp>
+#include <OutputStream.hpp>
+
+#include <mgmapi/mgmapi.h>
+#include <mgmapi_internal.h>
+#include <mgmapi/mgmapi_debug.h>
+
+#include <EventLogger.hpp>
+extern EventLogger g_eventLogger;
+
+SocketServer::Session * TransporterService::newSession(NDB_SOCKET_TYPE sockfd)
+{
+ DBUG_ENTER("SocketServer::Session * TransporterService::newSession");
+ if (m_auth && !m_auth->server_authenticate(sockfd)){
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(0);
+ }
+
+ if (!m_transporter_registry->connect_server(sockfd))
+ {
+ NDB_CLOSE_SOCKET(sockfd);
+ DBUG_RETURN(0);
+ }
+
+ DBUG_RETURN(0);
+}
+
+TransporterRegistry::TransporterRegistry(void * callback,
+ unsigned _maxTransporters,
+ unsigned sizeOfLongSignalMemory) {
+
+ nodeIdSpecified = false;
+ maxTransporters = _maxTransporters;
+ sendCounter = 1;
+ m_mgm_handle= 0;
+
+ callbackObj=callback;
+
+ theTCPTransporters = new TCP_Transporter * [maxTransporters];
+ theSCITransporters = new SCI_Transporter * [maxTransporters];
+ theSHMTransporters = new SHM_Transporter * [maxTransporters];
+ theOSETransporters = new OSE_Transporter * [maxTransporters];
+ theTransporterTypes = new TransporterType [maxTransporters];
+ theTransporters = new Transporter * [maxTransporters];
+ performStates = new PerformState [maxTransporters];
+ ioStates = new IOState [maxTransporters];
+
+ // Initialize member variables
+ nTransporters = 0;
+ nTCPTransporters = 0;
+ nSCITransporters = 0;
+ nSHMTransporters = 0;
+ nOSETransporters = 0;
+
+ // Initialize the transporter arrays
+ for (unsigned i=0; i<maxTransporters; i++) {
+ theTCPTransporters[i] = NULL;
+ theSCITransporters[i] = NULL;
+ theSHMTransporters[i] = NULL;
+ theOSETransporters[i] = NULL;
+ theTransporters[i] = NULL;
+ performStates[i] = DISCONNECTED;
+ ioStates[i] = NoHalt;
+ }
+ theOSEReceiver = 0;
+ theOSEJunkSocketSend = 0;
+ theOSEJunkSocketRecv = 0;
+}
+
+void TransporterRegistry::set_mgm_handle(NdbMgmHandle h)
+{
+ DBUG_ENTER("TransporterRegistry::set_mgm_handle");
+ if (m_mgm_handle)
+ ndb_mgm_destroy_handle(&m_mgm_handle);
+ m_mgm_handle= h;
+#ifndef DBUG_OFF
+ if (h)
+ {
+ char buf[256];
+ DBUG_PRINT("info",("handle set with connectstring: %s",
+ ndb_mgm_get_connectstring(h,buf, sizeof(buf))));
+ }
+ else
+ {
+ DBUG_PRINT("info",("handle set to NULL"));
+ }
+#endif
+ DBUG_VOID_RETURN;
+}
+
+TransporterRegistry::~TransporterRegistry() {
+
+ removeAll();
+
+ delete[] theTCPTransporters;
+ delete[] theSCITransporters;
+ delete[] theSHMTransporters;
+ delete[] theOSETransporters;
+ delete[] theTransporterTypes;
+ delete[] theTransporters;
+ delete[] performStates;
+ delete[] ioStates;
+
+#ifdef NDB_OSE_TRANSPORTER
+ if(theOSEReceiver != NULL){
+ theOSEReceiver->destroyPhantom();
+ delete theOSEReceiver;
+ theOSEReceiver = 0;
+ }
+#endif
+ if (m_mgm_handle)
+ ndb_mgm_destroy_handle(&m_mgm_handle);
+}
+
+void
+TransporterRegistry::removeAll(){
+ for(unsigned i = 0; i<maxTransporters; i++){
+ if(theTransporters[i] != NULL)
+ removeTransporter(theTransporters[i]->getRemoteNodeId());
+ }
+}
+
+void
+TransporterRegistry::disconnectAll(){
+ for(unsigned i = 0; i<maxTransporters; i++){
+ if(theTransporters[i] != NULL)
+ theTransporters[i]->doDisconnect();
+ }
+}
+
+bool
+TransporterRegistry::init(NodeId nodeId) {
+ DBUG_ENTER("TransporterRegistry::init");
+ nodeIdSpecified = true;
+ localNodeId = nodeId;
+
+ DEBUG("TransporterRegistry started node: " << localNodeId);
+
+ DBUG_RETURN(true);
+}
+
+bool
+TransporterRegistry::connect_server(NDB_SOCKET_TYPE sockfd)
+{
+ DBUG_ENTER("TransporterRegistry::connect_server");
+
+ // read node id from client
+ // read transporter type
+ int nodeId, remote_transporter_type= -1;
+ SocketInputStream s_input(sockfd);
+ char buf[256];
+ if (s_input.gets(buf, 256) == 0) {
+ DBUG_PRINT("error", ("Could not get node id from client"));
+ DBUG_RETURN(false);
+ }
+ int r= sscanf(buf, "%d %d", &nodeId, &remote_transporter_type);
+ switch (r) {
+ case 2:
+ break;
+ case 1:
+ // we're running version prior to 4.1.9
+ // ok, but with no checks on transporter configuration compatability
+ break;
+ default:
+ DBUG_PRINT("error", ("Error in node id from client"));
+ DBUG_RETURN(false);
+ }
+
+ DBUG_PRINT("info", ("nodeId=%d remote_transporter_type=%d",
+ nodeId,remote_transporter_type));
+
+ //check that nodeid is valid and that there is an allocated transporter
+ if ( nodeId < 0 || nodeId >= (int)maxTransporters) {
+ DBUG_PRINT("error", ("Node id out of range from client"));
+ DBUG_RETURN(false);
+ }
+ if (theTransporters[nodeId] == 0) {
+ DBUG_PRINT("error", ("No transporter for this node id from client"));
+ DBUG_RETURN(false);
+ }
+
+ //check that the transporter should be connected
+ if (performStates[nodeId] != TransporterRegistry::CONNECTING) {
+ DBUG_PRINT("error", ("Transporter in wrong state for this node id from client"));
+ DBUG_RETURN(false);
+ }
+
+ Transporter *t= theTransporters[nodeId];
+
+ // send info about own id (just as response to acknowledge connection)
+ // send info on own transporter type
+ SocketOutputStream s_output(sockfd);
+ s_output.println("%d %d", t->getLocalNodeId(), t->m_type);
+
+ if (remote_transporter_type != -1)
+ {
+ if (remote_transporter_type != t->m_type)
+ {
+ DBUG_PRINT("error", ("Transporter types mismatch this=%d remote=%d",
+ t->m_type, remote_transporter_type));
+ g_eventLogger.error("Incompatible configuration: Transporter type "
+ "mismatch with node %d", nodeId);
+
+ // wait for socket close for 1 second to let message arrive at client
+ {
+ fd_set a_set;
+ FD_ZERO(&a_set);
+ FD_SET(sockfd, &a_set);
+ struct timeval timeout;
+ timeout.tv_sec = 1; timeout.tv_usec = 0;
+ select(sockfd+1, &a_set, 0, 0, &timeout);
+ }
+ DBUG_RETURN(false);
+ }
+ }
+ else if (t->m_type == tt_SHM_TRANSPORTER)
+ {
+ g_eventLogger.warning("Unable to verify transporter compatability with node %d", nodeId);
+ }
+
+ // setup transporter (transporter responsible for closing sockfd)
+ t->connect_server(sockfd);
+
+ DBUG_RETURN(true);
+}
+
+bool
+TransporterRegistry::createTCPTransporter(TransporterConfiguration *config) {
+#ifdef NDB_TCP_TRANSPORTER
+
+ if(!nodeIdSpecified){
+ init(config->localNodeId);
+ }
+
+ if(config->localNodeId != localNodeId)
+ return false;
+
+ if(theTransporters[config->remoteNodeId] != NULL)
+ return false;
+
+ TCP_Transporter * t = new TCP_Transporter(*this,
+ config->tcp.sendBufferSize,
+ config->tcp.maxReceiveSize,
+ config->localHostName,
+ config->remoteHostName,
+ config->port,
+ config->isMgmConnection,
+ localNodeId,
+ config->remoteNodeId,
+ config->serverNodeId,
+ config->checksum,
+ config->signalId);
+ if (t == NULL)
+ return false;
+ else if (!t->initTransporter()) {
+ delete t;
+ return false;
+ }
+
+ // Put the transporter in the transporter arrays
+ theTCPTransporters[nTCPTransporters] = t;
+ theTransporters[t->getRemoteNodeId()] = t;
+ theTransporterTypes[t->getRemoteNodeId()] = tt_TCP_TRANSPORTER;
+ performStates[t->getRemoteNodeId()] = DISCONNECTED;
+ nTransporters++;
+ nTCPTransporters++;
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ t->theReceiverPid = theReceiverPid;
+#endif
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+TransporterRegistry::createOSETransporter(TransporterConfiguration *conf) {
+#ifdef NDB_OSE_TRANSPORTER
+
+ if(!nodeIdSpecified){
+ init(conf->localNodeId);
+ }
+
+ if(conf->localNodeId != localNodeId)
+ return false;
+
+ if(theTransporters[conf->remoteNodeId] != NULL)
+ return false;
+
+ if(theOSEReceiver == NULL){
+ theOSEReceiver = new OSE_Receiver(this,
+ 10,
+ localNodeId);
+ }
+
+ OSE_Transporter * t = new OSE_Transporter(conf->ose.prioASignalSize,
+ conf->ose.prioBSignalSize,
+ localNodeId,
+ conf->localHostName,
+ conf->remoteNodeId,
+ conf->serverNodeId,
+ conf->remoteHostName,
+ conf->checksum,
+ conf->signalId);
+ if (t == NULL)
+ return false;
+ else if (!t->initTransporter()) {
+ delete t;
+ return false;
+ }
+ // Put the transporter in the transporter arrays
+ theOSETransporters[nOSETransporters] = t;
+ theTransporters[t->getRemoteNodeId()] = t;
+ theTransporterTypes[t->getRemoteNodeId()] = tt_OSE_TRANSPORTER;
+ performStates[t->getRemoteNodeId()] = DISCONNECTED;
+
+ nTransporters++;
+ nOSETransporters++;
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+TransporterRegistry::createSCITransporter(TransporterConfiguration *config) {
+#ifdef NDB_SCI_TRANSPORTER
+
+ if(!SCI_Transporter::initSCI())
+ abort();
+
+ if(!nodeIdSpecified){
+ init(config->localNodeId);
+ }
+
+ if(config->localNodeId != localNodeId)
+ return false;
+
+ if(theTransporters[config->remoteNodeId] != NULL)
+ return false;
+
+ SCI_Transporter * t = new SCI_Transporter(*this,
+ config->localHostName,
+ config->remoteHostName,
+ config->port,
+ config->isMgmConnection,
+ config->sci.sendLimit,
+ config->sci.bufferSize,
+ config->sci.nLocalAdapters,
+ config->sci.remoteSciNodeId0,
+ config->sci.remoteSciNodeId1,
+ localNodeId,
+ config->remoteNodeId,
+ config->serverNodeId,
+ config->checksum,
+ config->signalId);
+
+ if (t == NULL)
+ return false;
+ else if (!t->initTransporter()) {
+ delete t;
+ return false;
+ }
+ // Put the transporter in the transporter arrays
+ theSCITransporters[nSCITransporters] = t;
+ theTransporters[t->getRemoteNodeId()] = t;
+ theTransporterTypes[t->getRemoteNodeId()] = tt_SCI_TRANSPORTER;
+ performStates[t->getRemoteNodeId()] = DISCONNECTED;
+ nTransporters++;
+ nSCITransporters++;
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+TransporterRegistry::createSHMTransporter(TransporterConfiguration *config) {
+ DBUG_ENTER("TransporterRegistry::createTransporter SHM");
+#ifdef NDB_SHM_TRANSPORTER
+ if(!nodeIdSpecified){
+ init(config->localNodeId);
+ }
+
+ if(config->localNodeId != localNodeId)
+ return false;
+
+ if (!g_ndb_shm_signum) {
+ g_ndb_shm_signum= config->shm.signum;
+ DBUG_PRINT("info",("Block signum %d",g_ndb_shm_signum));
+ /**
+ * Make sure to block g_ndb_shm_signum
+ * TransporterRegistry::init is run from "main" thread
+ */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, g_ndb_shm_signum);
+ pthread_sigmask(SIG_BLOCK, &mask, 0);
+ }
+
+ if(config->shm.signum != g_ndb_shm_signum)
+ return false;
+
+ if(theTransporters[config->remoteNodeId] != NULL)
+ return false;
+
+ SHM_Transporter * t = new SHM_Transporter(*this,
+ config->localHostName,
+ config->remoteHostName,
+ config->port,
+ config->isMgmConnection,
+ localNodeId,
+ config->remoteNodeId,
+ config->serverNodeId,
+ config->checksum,
+ config->signalId,
+ config->shm.shmKey,
+ config->shm.shmSize
+ );
+ if (t == NULL)
+ return false;
+ else if (!t->initTransporter()) {
+ delete t;
+ return false;
+ }
+ // Put the transporter in the transporter arrays
+ theSHMTransporters[nSHMTransporters] = t;
+ theTransporters[t->getRemoteNodeId()] = t;
+ theTransporterTypes[t->getRemoteNodeId()] = tt_SHM_TRANSPORTER;
+ performStates[t->getRemoteNodeId()] = DISCONNECTED;
+
+ nTransporters++;
+ nSHMTransporters++;
+
+ DBUG_RETURN(true);
+#else
+ DBUG_RETURN(false);
+#endif
+}
+
+
+void
+TransporterRegistry::removeTransporter(NodeId nodeId) {
+
+ DEBUG("Removing transporter from " << localNodeId
+ << " to " << nodeId);
+
+ if(theTransporters[nodeId] == NULL)
+ return;
+
+ theTransporters[nodeId]->doDisconnect();
+
+ const TransporterType type = theTransporterTypes[nodeId];
+
+ int ind = 0;
+ switch(type){
+ case tt_TCP_TRANSPORTER:
+#ifdef NDB_TCP_TRANSPORTER
+ for(; ind < nTCPTransporters; ind++)
+ if(theTCPTransporters[ind]->getRemoteNodeId() == nodeId)
+ break;
+ ind++;
+ for(; ind<nTCPTransporters; ind++)
+ theTCPTransporters[ind-1] = theTCPTransporters[ind];
+ nTCPTransporters --;
+#endif
+ break;
+ case tt_SCI_TRANSPORTER:
+#ifdef NDB_SCI_TRANSPORTER
+ for(; ind < nSCITransporters; ind++)
+ if(theSCITransporters[ind]->getRemoteNodeId() == nodeId)
+ break;
+ ind++;
+ for(; ind<nSCITransporters; ind++)
+ theSCITransporters[ind-1] = theSCITransporters[ind];
+ nSCITransporters --;
+#endif
+ break;
+ case tt_SHM_TRANSPORTER:
+#ifdef NDB_SHM_TRANSPORTER
+ for(; ind < nSHMTransporters; ind++)
+ if(theSHMTransporters[ind]->getRemoteNodeId() == nodeId)
+ break;
+ ind++;
+ for(; ind<nSHMTransporters; ind++)
+ theSHMTransporters[ind-1] = theSHMTransporters[ind];
+ nSHMTransporters --;
+#endif
+ break;
+ case tt_OSE_TRANSPORTER:
+#ifdef NDB_OSE_TRANSPORTER
+ for(; ind < nOSETransporters; ind++)
+ if(theOSETransporters[ind]->getRemoteNodeId() == nodeId)
+ break;
+ ind++;
+ for(; ind<nOSETransporters; ind++)
+ theOSETransporters[ind-1] = theOSETransporters[ind];
+ nOSETransporters --;
+#endif
+ break;
+ }
+
+ nTransporters--;
+
+ // Delete the transporter and remove it from theTransporters array
+ delete theTransporters[nodeId];
+ theTransporters[nodeId] = NULL;
+}
+
+SendStatus
+TransporterRegistry::prepareSend(const SignalHeader * const signalHeader,
+ Uint8 prio,
+ const Uint32 * const signalData,
+ NodeId nodeId,
+ const LinearSectionPtr ptr[3]){
+
+
+ Transporter *t = theTransporters[nodeId];
+ if(t != NULL &&
+ (((ioStates[nodeId] != HaltOutput) && (ioStates[nodeId] != HaltIO)) ||
+ ((signalHeader->theReceiversBlockNumber == 252) ||
+ (signalHeader->theReceiversBlockNumber == 4002)))) {
+
+ if(t->isConnected()){
+ Uint32 lenBytes = t->m_packer.getMessageLength(signalHeader, ptr);
+ if(lenBytes <= MAX_MESSAGE_SIZE){
+ Uint32 * insertPtr = t->getWritePtr(lenBytes, prio);
+ if(insertPtr != 0){
+ t->m_packer.pack(insertPtr, prio, signalHeader, signalData, ptr);
+ t->updateWritePtr(lenBytes, prio);
+ return SEND_OK;
+ }
+
+ int sleepTime = 2;
+
+ /**
+ * @note: on linux/i386 the granularity is 10ms
+ * so sleepTime = 2 generates a 10 ms sleep.
+ */
+ for(int i = 0; i<50; i++){
+ if((nSHMTransporters+nSCITransporters) == 0)
+ NdbSleep_MilliSleep(sleepTime);
+ insertPtr = t->getWritePtr(lenBytes, prio);
+ if(insertPtr != 0){
+ t->m_packer.pack(insertPtr, prio, signalHeader, signalData, ptr);
+ t->updateWritePtr(lenBytes, prio);
+ break;
+ }
+ }
+
+ if(insertPtr != 0){
+ /**
+ * Send buffer full, but resend works
+ */
+ reportError(callbackObj, nodeId, TE_SEND_BUFFER_FULL);
+ return SEND_OK;
+ }
+
+ WARNING("Signal to " << nodeId << " lost(buffer)");
+ reportError(callbackObj, nodeId, TE_SIGNAL_LOST_SEND_BUFFER_FULL);
+ return SEND_BUFFER_FULL;
+ } else {
+ return SEND_MESSAGE_TOO_BIG;
+ }
+ } else {
+ DEBUG("Signal to " << nodeId << " lost(disconnect) ");
+ return SEND_DISCONNECTED;
+ }
+ } else {
+ DEBUG("Discarding message to block: "
+ << signalHeader->theReceiversBlockNumber
+ << " node: " << nodeId);
+
+ if(t == NULL)
+ return SEND_UNKNOWN_NODE;
+
+ return SEND_BLOCKED;
+ }
+}
+
+SendStatus
+TransporterRegistry::prepareSend(const SignalHeader * const signalHeader,
+ Uint8 prio,
+ const Uint32 * const signalData,
+ NodeId nodeId,
+ class SectionSegmentPool & thePool,
+ const SegmentedSectionPtr ptr[3]){
+
+
+ Transporter *t = theTransporters[nodeId];
+ if(t != NULL &&
+ (((ioStates[nodeId] != HaltOutput) && (ioStates[nodeId] != HaltIO)) ||
+ ((signalHeader->theReceiversBlockNumber == 252)||
+ (signalHeader->theReceiversBlockNumber == 4002)))) {
+
+ if(t->isConnected()){
+ Uint32 lenBytes = t->m_packer.getMessageLength(signalHeader, ptr);
+ if(lenBytes <= MAX_MESSAGE_SIZE){
+ Uint32 * insertPtr = t->getWritePtr(lenBytes, prio);
+ if(insertPtr != 0){
+ t->m_packer.pack(insertPtr, prio, signalHeader, signalData, thePool, ptr);
+ t->updateWritePtr(lenBytes, prio);
+ return SEND_OK;
+ }
+
+
+ /**
+ * @note: on linux/i386 the granularity is 10ms
+ * so sleepTime = 2 generates a 10 ms sleep.
+ */
+ int sleepTime = 2;
+ for(int i = 0; i<50; i++){
+ if((nSHMTransporters+nSCITransporters) == 0)
+ NdbSleep_MilliSleep(sleepTime);
+ insertPtr = t->getWritePtr(lenBytes, prio);
+ if(insertPtr != 0){
+ t->m_packer.pack(insertPtr, prio, signalHeader, signalData, thePool, ptr);
+ t->updateWritePtr(lenBytes, prio);
+ break;
+ }
+ }
+
+ if(insertPtr != 0){
+ /**
+ * Send buffer full, but resend works
+ */
+ reportError(callbackObj, nodeId, TE_SEND_BUFFER_FULL);
+ return SEND_OK;
+ }
+
+ WARNING("Signal to " << nodeId << " lost(buffer)");
+ reportError(callbackObj, nodeId, TE_SIGNAL_LOST_SEND_BUFFER_FULL);
+ return SEND_BUFFER_FULL;
+ } else {
+ return SEND_MESSAGE_TOO_BIG;
+ }
+ } else {
+ DEBUG("Signal to " << nodeId << " lost(disconnect) ");
+ return SEND_DISCONNECTED;
+ }
+ } else {
+ DEBUG("Discarding message to block: "
+ << signalHeader->theReceiversBlockNumber
+ << " node: " << nodeId);
+
+ if(t == NULL)
+ return SEND_UNKNOWN_NODE;
+
+ return SEND_BLOCKED;
+ }
+}
+
+void
+TransporterRegistry::external_IO(Uint32 timeOutMillis) {
+ //-----------------------------------------------------------
+ // Most of the time we will send the buffers here and then wait
+ // for new signals. Thus we start by sending without timeout
+ // followed by the receive part where we expect to sleep for
+ // a while.
+ //-----------------------------------------------------------
+ if(pollReceive(timeOutMillis)){
+ performReceive();
+ }
+ performSend();
+}
+
+Uint32
+TransporterRegistry::pollReceive(Uint32 timeOutMillis){
+ Uint32 retVal = 0;
+#ifdef NDB_OSE_TRANSPORTER
+ retVal |= poll_OSE(timeOutMillis);
+ retVal |= poll_TCP(0);
+ return retVal;
+#endif
+
+ if((nSCITransporters) > 0)
+ {
+ timeOutMillis=0;
+ }
+
+#ifdef NDB_SHM_TRANSPORTER
+ if(nSHMTransporters > 0)
+ {
+ Uint32 res = poll_SHM(0);
+ if(res)
+ {
+ retVal |= res;
+ timeOutMillis = 0;
+ }
+ }
+#endif
+
+#ifdef NDB_TCP_TRANSPORTER
+ if(nTCPTransporters > 0 || retVal == 0)
+ {
+ retVal |= poll_TCP(timeOutMillis);
+ }
+ else
+ tcpReadSelectReply = 0;
+#endif
+#ifdef NDB_SCI_TRANSPORTER
+ if(nSCITransporters > 0)
+ retVal |= poll_SCI(timeOutMillis);
+#endif
+#ifdef NDB_SHM_TRANSPORTER
+ if(nSHMTransporters > 0 && retVal == 0)
+ {
+ int res = poll_SHM(0);
+ retVal |= res;
+ }
+#endif
+ return retVal;
+}
+
+
+#ifdef NDB_SCI_TRANSPORTER
+Uint32
+TransporterRegistry::poll_SCI(Uint32 timeOutMillis)
+{
+ for (int i=0; i<nSCITransporters; i++) {
+ SCI_Transporter * t = theSCITransporters[i];
+ if (t->isConnected()) {
+ if(t->hasDataToRead())
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+
+#ifdef NDB_SHM_TRANSPORTER
+static int g_shm_counter = 0;
+Uint32
+TransporterRegistry::poll_SHM(Uint32 timeOutMillis)
+{
+ for(int j=0; j < 100; j++)
+ {
+ for (int i=0; i<nSHMTransporters; i++) {
+ SHM_Transporter * t = theSHMTransporters[i];
+ if (t->isConnected()) {
+ if(t->hasDataToRead()) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+#ifdef NDB_OSE_TRANSPORTER
+Uint32
+TransporterRegistry::poll_OSE(Uint32 timeOutMillis)
+{
+ if(theOSEReceiver != NULL){
+ return theOSEReceiver->doReceive(timeOutMillis);
+ }
+ NdbSleep_MilliSleep(timeOutMillis);
+ return 0;
+}
+#endif
+
+#ifdef NDB_TCP_TRANSPORTER
+Uint32
+TransporterRegistry::poll_TCP(Uint32 timeOutMillis)
+{
+ if (false && nTCPTransporters == 0)
+ {
+ tcpReadSelectReply = 0;
+ return 0;
+ }
+
+ struct timeval timeout;
+#ifdef NDB_OSE
+ // Return directly if there are no TCP transporters configured
+
+ if(timeOutMillis <= 1){
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 1025;
+ } else {
+ timeout.tv_sec = timeOutMillis / 1000;
+ timeout.tv_usec = (timeOutMillis % 1000) * 1000;
+ }
+#else
+ timeout.tv_sec = timeOutMillis / 1000;
+ timeout.tv_usec = (timeOutMillis % 1000) * 1000;
+#endif
+
+ NDB_SOCKET_TYPE maxSocketValue = -1;
+
+ // Needed for TCP/IP connections
+ // The read- and writeset are used by select
+
+ FD_ZERO(&tcpReadset);
+
+ // Prepare for sending and receiving
+ for (int i = 0; i < nTCPTransporters; i++) {
+ TCP_Transporter * t = theTCPTransporters[i];
+
+ // If the transporter is connected
+ if (t->isConnected()) {
+
+ const NDB_SOCKET_TYPE socket = t->getSocket();
+ // Find the highest socket value. It will be used by select
+ if (socket > maxSocketValue)
+ maxSocketValue = socket;
+
+ // Put the connected transporters in the socket read-set
+ FD_SET(socket, &tcpReadset);
+ }
+ }
+
+ // The highest socket value plus one
+ maxSocketValue++;
+
+ tcpReadSelectReply = select(maxSocketValue, &tcpReadset, 0, 0, &timeout);
+ if(false && tcpReadSelectReply == -1 && errno == EINTR)
+ ndbout_c("woke-up by signal");
+
+#ifdef NDB_WIN32
+ if(tcpReadSelectReply == SOCKET_ERROR)
+ {
+ NdbSleep_MilliSleep(timeOutMillis);
+ }
+#endif
+
+ return tcpReadSelectReply;
+}
+#endif
+
+
+void
+TransporterRegistry::performReceive()
+{
+#ifdef NDB_OSE_TRANSPORTER
+ if(theOSEReceiver != 0)
+ {
+ while(theOSEReceiver->hasData())
+ {
+ NodeId remoteNodeId;
+ Uint32 * readPtr;
+ Uint32 sz = theOSEReceiver->getReceiveData(&remoteNodeId, &readPtr);
+ Uint32 szUsed = unpack(readPtr,
+ sz,
+ remoteNodeId,
+ ioStates[remoteNodeId]);
+#ifdef DEBUG_TRANSPORTER
+ /**
+ * OSE transporter can handle executions of
+ * half signals
+ */
+ assert(sz == szUsed);
+#endif
+ theOSEReceiver->updateReceiveDataPtr(szUsed);
+ theOSEReceiver->doReceive(0);
+ // checkJobBuffer();
+ }
+ }
+#endif
+
+#ifdef NDB_TCP_TRANSPORTER
+ if(tcpReadSelectReply > 0)
+ {
+ for (int i=0; i<nTCPTransporters; i++)
+ {
+ checkJobBuffer();
+ TCP_Transporter *t = theTCPTransporters[i];
+ const NodeId nodeId = t->getRemoteNodeId();
+ const NDB_SOCKET_TYPE socket = t->getSocket();
+ if(is_connected(nodeId)){
+ if(t->isConnected() && FD_ISSET(socket, &tcpReadset))
+ {
+ const int receiveSize = t->doReceive();
+ if(receiveSize > 0)
+ {
+ Uint32 * ptr;
+ Uint32 sz = t->getReceiveData(&ptr);
+ Uint32 szUsed = unpack(ptr, sz, nodeId, ioStates[nodeId]);
+ t->updateReceiveDataPtr(szUsed);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+#ifdef NDB_SCI_TRANSPORTER
+ //performReceive
+ //do prepareReceive on the SCI transporters (prepareReceive(t,,,,))
+ for (int i=0; i<nSCITransporters; i++)
+ {
+ checkJobBuffer();
+ SCI_Transporter *t = theSCITransporters[i];
+ const NodeId nodeId = t->getRemoteNodeId();
+ if(is_connected(nodeId))
+ {
+ if(t->isConnected() && t->checkConnected())
+ {
+ Uint32 * readPtr, * eodPtr;
+ t->getReceivePtr(&readPtr, &eodPtr);
+ Uint32 *newPtr = unpack(readPtr, eodPtr, nodeId, ioStates[nodeId]);
+ t->updateReceivePtr(newPtr);
+ }
+ }
+ }
+#endif
+#ifdef NDB_SHM_TRANSPORTER
+ for (int i=0; i<nSHMTransporters; i++)
+ {
+ checkJobBuffer();
+ SHM_Transporter *t = theSHMTransporters[i];
+ const NodeId nodeId = t->getRemoteNodeId();
+ if(is_connected(nodeId)){
+ if(t->isConnected() && t->checkConnected())
+ {
+ Uint32 * readPtr, * eodPtr;
+ t->getReceivePtr(&readPtr, &eodPtr);
+ Uint32 *newPtr = unpack(readPtr, eodPtr, nodeId, ioStates[nodeId]);
+ t->updateReceivePtr(newPtr);
+ }
+ }
+ }
+#endif
+}
+
+static int x = 0;
+void
+TransporterRegistry::performSend()
+{
+ int i;
+ sendCounter = 1;
+
+#ifdef NDB_OSE_TRANSPORTER
+ for (int i = 0; i < nOSETransporters; i++)
+ {
+ OSE_Transporter *t = theOSETransporters[i];
+ if(is_connected(t->getRemoteNodeId()) &&& (t->isConnected()))
+ {
+ t->doSend();
+ }//if
+ }//for
+#endif
+
+#ifdef NDB_TCP_TRANSPORTER
+#ifdef NDB_OSE
+ {
+ int maxSocketValue = 0;
+
+ // Needed for TCP/IP connections
+ // The writeset are used by select
+ fd_set writeset;
+ FD_ZERO(&writeset);
+
+ // Prepare for sending and receiving
+ for (i = 0; i < nTCPTransporters; i++) {
+ TCP_Transporter * t = theTCPTransporters[i];
+
+ // If the transporter is connected
+ if ((t->hasDataToSend()) && (t->isConnected())) {
+ const int socket = t->getSocket();
+ // Find the highest socket value. It will be used by select
+ if (socket > maxSocketValue) {
+ maxSocketValue = socket;
+ }//if
+ FD_SET(socket, &writeset);
+ }//if
+ }//for
+
+ // The highest socket value plus one
+ if(maxSocketValue == 0)
+ return;
+
+ maxSocketValue++;
+ struct timeval timeout = { 0, 1025 };
+ Uint32 tmp = select(maxSocketValue, 0, &writeset, 0, &timeout);
+
+ if (tmp == 0)
+ {
+ return;
+ }//if
+ for (i = 0; i < nTCPTransporters; i++) {
+ TCP_Transporter *t = theTCPTransporters[i];
+ const NodeId nodeId = t->getRemoteNodeId();
+ const int socket = t->getSocket();
+ if(is_connected(nodeId)){
+ if(t->isConnected() && FD_ISSET(socket, &writeset)) {
+ t->doSend();
+ }//if
+ }//if
+ }//for
+ }
+#endif
+#ifdef NDB_TCP_TRANSPORTER
+ for (i = x; i < nTCPTransporters; i++)
+ {
+ TCP_Transporter *t = theTCPTransporters[i];
+ if (t && t->hasDataToSend() && t->isConnected() &&
+ is_connected(t->getRemoteNodeId()))
+ {
+ t->doSend();
+ }
+ }
+ for (i = 0; i < x && i < nTCPTransporters; i++)
+ {
+ TCP_Transporter *t = theTCPTransporters[i];
+ if (t && t->hasDataToSend() && t->isConnected() &&
+ is_connected(t->getRemoteNodeId()))
+ {
+ t->doSend();
+ }
+ }
+ x++;
+ if (x == nTCPTransporters) x = 0;
+#endif
+#endif
+#ifdef NDB_SCI_TRANSPORTER
+ //scroll through the SCI transporters,
+ // get each transporter, check if connected, send data
+ for (i=0; i<nSCITransporters; i++) {
+ SCI_Transporter *t = theSCITransporters[i];
+ const NodeId nodeId = t->getRemoteNodeId();
+
+ if(is_connected(nodeId))
+ {
+ if(t->isConnected() && t->hasDataToSend()) {
+ t->doSend();
+ } //if
+ } //if
+ }
+#endif
+
+#ifdef NDB_SHM_TRANSPORTER
+ for (i=0; i<nSHMTransporters; i++)
+ {
+ SHM_Transporter *t = theSHMTransporters[i];
+ const NodeId nodeId = t->getRemoteNodeId();
+ if(is_connected(nodeId))
+ {
+ if(t->isConnected())
+ {
+ t->doSend();
+ }
+ }
+ }
+#endif
+}
+
+int
+TransporterRegistry::forceSendCheck(int sendLimit){
+ int tSendCounter = sendCounter;
+ sendCounter = tSendCounter + 1;
+ if (tSendCounter >= sendLimit) {
+ performSend();
+ sendCounter = 1;
+ return 1;
+ }//if
+ return 0;
+}//TransporterRegistry::forceSendCheck()
+
+#ifdef DEBUG_TRANSPORTER
+void
+TransporterRegistry::printState(){
+ ndbout << "-- TransporterRegistry -- " << endl << endl
+ << "Transporters = " << nTransporters << endl;
+ for(int i = 0; i<maxTransporters; i++)
+ if(theTransporters[i] != NULL){
+ const NodeId remoteNodeId = theTransporters[i]->getRemoteNodeId();
+ ndbout << "Transporter: " << remoteNodeId
+ << " PerformState: " << performStates[remoteNodeId]
+ << " IOState: " << ioStates[remoteNodeId] << endl;
+ }
+}
+#endif
+
+IOState
+TransporterRegistry::ioState(NodeId nodeId) {
+ return ioStates[nodeId];
+}
+
+void
+TransporterRegistry::setIOState(NodeId nodeId, IOState state) {
+ DEBUG("TransporterRegistry::setIOState("
+ << nodeId << ", " << state << ")");
+ ioStates[nodeId] = state;
+}
+
+static void *
+run_start_clients_C(void * me)
+{
+ ((TransporterRegistry*) me)->start_clients_thread();
+ return 0;
+}
+
+// Run by kernel thread
+void
+TransporterRegistry::do_connect(NodeId node_id)
+{
+ PerformState &curr_state = performStates[node_id];
+ switch(curr_state){
+ case DISCONNECTED:
+ break;
+ case CONNECTED:
+ return;
+ case CONNECTING:
+ return;
+ case DISCONNECTING:
+ break;
+ }
+ curr_state= CONNECTING;
+}
+void
+TransporterRegistry::do_disconnect(NodeId node_id)
+{
+ PerformState &curr_state = performStates[node_id];
+ switch(curr_state){
+ case DISCONNECTED:
+ return;
+ case CONNECTED:
+ break;
+ case CONNECTING:
+ break;
+ case DISCONNECTING:
+ return;
+ }
+ curr_state= DISCONNECTING;
+}
+
+void
+TransporterRegistry::report_connect(NodeId node_id)
+{
+ performStates[node_id] = CONNECTED;
+ reportConnect(callbackObj, node_id);
+}
+
+void
+TransporterRegistry::report_disconnect(NodeId node_id, int errnum)
+{
+ performStates[node_id] = DISCONNECTED;
+ reportDisconnect(callbackObj, node_id, errnum);
+}
+
+void
+TransporterRegistry::update_connections()
+{
+ for (int i= 0, n= 0; n < nTransporters; i++){
+ Transporter * t = theTransporters[i];
+ if (!t)
+ continue;
+ n++;
+
+ const NodeId nodeId = t->getRemoteNodeId();
+ switch(performStates[nodeId]){
+ case CONNECTED:
+ case DISCONNECTED:
+ break;
+ case CONNECTING:
+ if(t->isConnected())
+ report_connect(nodeId);
+ break;
+ case DISCONNECTING:
+ if(!t->isConnected())
+ report_disconnect(nodeId, 0);
+ break;
+ }
+ }
+}
+
+// run as own thread
+void
+TransporterRegistry::start_clients_thread()
+{
+ DBUG_ENTER("TransporterRegistry::start_clients_thread");
+ while (m_run_start_clients_thread) {
+ NdbSleep_MilliSleep(100);
+ for (int i= 0, n= 0; n < nTransporters && m_run_start_clients_thread; i++){
+ Transporter * t = theTransporters[i];
+ if (!t)
+ continue;
+ n++;
+
+ const NodeId nodeId = t->getRemoteNodeId();
+ switch(performStates[nodeId]){
+ case CONNECTING:
+ if(!t->isConnected() && !t->isServer) {
+ bool connected= false;
+ /**
+ * First, we try to connect (if we have a port number).
+ */
+ if (t->get_s_port())
+ connected= t->connect_client();
+
+ /**
+ * If dynamic, get the port for connecting from the management server
+ */
+ if( !connected && t->get_s_port() <= 0) { // Port is dynamic
+ int server_port= 0;
+ struct ndb_mgm_reply mgm_reply;
+
+ if(!ndb_mgm_is_connected(m_mgm_handle))
+ ndb_mgm_connect(m_mgm_handle, 0, 0, 0);
+
+ if(ndb_mgm_is_connected(m_mgm_handle))
+ {
+ int res=
+ ndb_mgm_get_connection_int_parameter(m_mgm_handle,
+ t->getRemoteNodeId(),
+ t->getLocalNodeId(),
+ CFG_CONNECTION_SERVER_PORT,
+ &server_port,
+ &mgm_reply);
+ DBUG_PRINT("info",("Got dynamic port %d for %d -> %d (ret: %d)",
+ server_port,t->getRemoteNodeId(),
+ t->getLocalNodeId(),res));
+ if( res >= 0 )
+ {
+ /**
+ * Server_port == 0 just means that that a mgmt server
+ * has not received a new port yet. Keep the old.
+ */
+ if (server_port)
+ t->set_s_port(server_port);
+ }
+ else
+ {
+ ndbout_c("Failed to get dynamic port to connect to: %d", res);
+ ndb_mgm_disconnect(m_mgm_handle);
+ }
+ }
+ /** else
+ * We will not be able to get a new port unless
+ * the m_mgm_handle is connected. Note that not
+ * being connected is an ok state, just continue
+ * until it is able to connect. Continue using the
+ * old port until we can connect again and get a
+ * new port.
+ */
+ }
+ }
+ break;
+ case DISCONNECTING:
+ if(t->isConnected())
+ t->doDisconnect();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool
+TransporterRegistry::start_clients()
+{
+ m_run_start_clients_thread= true;
+ m_start_clients_thread= NdbThread_Create(run_start_clients_C,
+ (void**)this,
+ 32768,
+ "ndb_start_clients",
+ NDB_THREAD_PRIO_LOW);
+ if (m_start_clients_thread == 0) {
+ m_run_start_clients_thread= false;
+ return false;
+ }
+ return true;
+}
+
+bool
+TransporterRegistry::stop_clients()
+{
+ if (m_start_clients_thread) {
+ m_run_start_clients_thread= false;
+ void* status;
+ NdbThread_WaitFor(m_start_clients_thread, &status);
+ NdbThread_Destroy(&m_start_clients_thread);
+ }
+ return true;
+}
+
+void
+TransporterRegistry::add_transporter_interface(NodeId remoteNodeId,
+ const char *interf,
+ int s_port)
+{
+ DBUG_ENTER("TransporterRegistry::add_transporter_interface");
+ DBUG_PRINT("enter",("interface=%s, s_port= %d", interf, s_port));
+ if (interf && strlen(interf) == 0)
+ interf= 0;
+
+ for (unsigned i= 0; i < m_transporter_interface.size(); i++)
+ {
+ Transporter_interface &tmp= m_transporter_interface[i];
+ if (s_port != tmp.m_s_service_port || tmp.m_s_service_port==0)
+ continue;
+ if (interf != 0 && tmp.m_interface != 0 &&
+ strcmp(interf, tmp.m_interface) == 0)
+ {
+ DBUG_VOID_RETURN; // found match, no need to insert
+ }
+ if (interf == 0 && tmp.m_interface == 0)
+ {
+ DBUG_VOID_RETURN; // found match, no need to insert
+ }
+ }
+ Transporter_interface t;
+ t.m_remote_nodeId= remoteNodeId;
+ t.m_s_service_port= s_port;
+ t.m_interface= interf;
+ m_transporter_interface.push_back(t);
+ DBUG_PRINT("exit",("interface and port added"));
+ DBUG_VOID_RETURN;
+}
+
+bool
+TransporterRegistry::start_service(SocketServer& socket_server)
+{
+ struct ndb_mgm_reply mgm_reply;
+
+ DBUG_ENTER("TransporterRegistry::start_service");
+ if (m_transporter_interface.size() > 0 && !nodeIdSpecified)
+ {
+ ndbout_c("TransporterRegistry::startReceiving: localNodeId not specified");
+ DBUG_RETURN(false);
+ }
+
+ for (unsigned i= 0; i < m_transporter_interface.size(); i++)
+ {
+ Transporter_interface &t= m_transporter_interface[i];
+
+ unsigned short port= (unsigned short)t.m_s_service_port;
+ if(t.m_s_service_port<0)
+ port= -t.m_s_service_port; // is a dynamic port
+ TransporterService *transporter_service =
+ new TransporterService(new SocketAuthSimple("ndbd", "ndbd passwd"));
+ if(!socket_server.setup(transporter_service,
+ &port, t.m_interface))
+ {
+ DBUG_PRINT("info", ("Trying new port"));
+ port= 0;
+ if(t.m_s_service_port>0
+ || !socket_server.setup(transporter_service,
+ &port, t.m_interface))
+ {
+ /*
+ * If it wasn't a dynamically allocated port, or
+ * our attempts at getting a new dynamic port failed
+ */
+ ndbout_c("Unable to setup transporter service port: %s:%d!\n"
+ "Please check if the port is already used,\n"
+ "(perhaps the node is already running)",
+ t.m_interface ? t.m_interface : "*", t.m_s_service_port);
+ delete transporter_service;
+ DBUG_RETURN(false);
+ }
+ }
+ t.m_s_service_port= (t.m_s_service_port<=0)?-port:port; // -`ve if dynamic
+ DBUG_PRINT("info", ("t.m_s_service_port = %d",t.m_s_service_port));
+ transporter_service->setTransporterRegistry(this);
+ }
+ DBUG_RETURN(true);
+}
+
+#ifdef NDB_SHM_TRANSPORTER
+static
+RETSIGTYPE
+shm_sig_handler(int signo)
+{
+ g_shm_counter++;
+}
+#endif
+
+void
+TransporterRegistry::startReceiving()
+{
+ DBUG_ENTER("TransporterRegistry::startReceiving");
+#ifdef NDB_OSE_TRANSPORTER
+ if(theOSEReceiver != NULL){
+ theOSEReceiver->createPhantom();
+ }
+#endif
+
+#ifdef NDB_OSE
+ theOSEJunkSocketRecv = socket(AF_INET, SOCK_STREAM, 0);
+#endif
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ theReceiverPid = current_process();
+ for(int i = 0; i<nTCPTransporters; i++)
+ theTCPTransporters[i]->theReceiverPid = theReceiverPid;
+#endif
+
+#ifdef NDB_SHM_TRANSPORTER
+ m_shm_own_pid = getpid();
+ if (g_ndb_shm_signum)
+ {
+ DBUG_PRINT("info",("Install signal handler for signum %d",
+ g_ndb_shm_signum));
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, g_ndb_shm_signum);
+ pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, 0);
+ sa.sa_handler = shm_sig_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ int ret;
+ while((ret = sigaction(g_ndb_shm_signum, &sa, 0)) == -1 && errno == EINTR);
+ if(ret != 0)
+ {
+ DBUG_PRINT("error",("Install failed"));
+ g_eventLogger.error("Failed to install signal handler for"
+ " SHM transporter errno: %d (%s)", errno,
+ strerror(errno));
+ }
+ }
+#endif // NDB_SHM_TRANSPORTER
+ DBUG_VOID_RETURN;
+}
+
+void
+TransporterRegistry::stopReceiving(){
+#ifdef NDB_OSE_TRANSPORTER
+ if(theOSEReceiver != NULL){
+ theOSEReceiver->destroyPhantom();
+ }
+#endif
+
+ /**
+ * Disconnect all transporters, this includes detach from remote node
+ * and since that must be done from the same process that called attach
+ * it's done here in the receive thread
+ */
+ disconnectAll();
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ if(theOSEJunkSocketRecv > 0)
+ close(theOSEJunkSocketRecv);
+ theOSEJunkSocketRecv = -1;
+#endif
+
+}
+
+void
+TransporterRegistry::startSending(){
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ theOSEJunkSocketSend = socket(AF_INET, SOCK_STREAM, 0);
+#endif
+}
+
+void
+TransporterRegistry::stopSending(){
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ if(theOSEJunkSocketSend > 0)
+ close(theOSEJunkSocketSend);
+ theOSEJunkSocketSend = -1;
+#endif
+}
+
+NdbOut & operator <<(NdbOut & out, SignalHeader & sh){
+ out << "-- Signal Header --" << endl;
+ out << "theLength: " << sh.theLength << endl;
+ out << "gsn: " << sh.theVerId_signalNumber << endl;
+ out << "recBlockNo: " << sh.theReceiversBlockNumber << endl;
+ out << "sendBlockRef: " << sh.theSendersBlockRef << endl;
+ out << "sendersSig: " << sh.theSendersSignalId << endl;
+ out << "theSignalId: " << sh.theSignalId << endl;
+ out << "trace: " << (int)sh.theTrace << endl;
+ return out;
+}
+
+Transporter*
+TransporterRegistry::get_transporter(NodeId nodeId) {
+ return theTransporters[nodeId];
+}
+
+bool TransporterRegistry::connect_client(NdbMgmHandle *h)
+{
+ DBUG_ENTER("TransporterRegistry::connect_client(NdbMgmHandle)");
+
+ Uint32 mgm_nodeid= ndb_mgm_get_mgmd_nodeid(*h);
+
+ if(!mgm_nodeid)
+ {
+ ndbout_c("%s: %d", __FILE__, __LINE__);
+ return false;
+ }
+ Transporter * t = theTransporters[mgm_nodeid];
+ if (!t)
+ {
+ ndbout_c("%s: %d", __FILE__, __LINE__);
+ return false;
+ }
+ DBUG_RETURN(t->connect_client(connect_ndb_mgmd(h)));
+}
+
+/**
+ * Given a connected NdbMgmHandle, turns it into a transporter
+ * and returns the socket.
+ */
+NDB_SOCKET_TYPE TransporterRegistry::connect_ndb_mgmd(NdbMgmHandle *h)
+{
+ struct ndb_mgm_reply mgm_reply;
+
+ if ( h==NULL || *h == NULL )
+ {
+ ndbout_c("%s: %d", __FILE__, __LINE__);
+ return NDB_INVALID_SOCKET;
+ }
+
+ for(unsigned int i=0;i < m_transporter_interface.size();i++)
+ if (m_transporter_interface[i].m_s_service_port < 0
+ && ndb_mgm_set_connection_int_parameter(*h,
+ get_localNodeId(),
+ m_transporter_interface[i].m_remote_nodeId,
+ CFG_CONNECTION_SERVER_PORT,
+ m_transporter_interface[i].m_s_service_port,
+ &mgm_reply) < 0)
+ {
+ ndbout_c("Error: %s: %d",
+ ndb_mgm_get_latest_error_desc(*h),
+ ndb_mgm_get_latest_error(*h));
+ ndbout_c("%s: %d", __FILE__, __LINE__);
+ ndb_mgm_destroy_handle(h);
+ return NDB_INVALID_SOCKET;
+ }
+
+ /**
+ * convert_to_transporter also disposes of the handle (i.e. we don't leak
+ * memory here.
+ */
+ NDB_SOCKET_TYPE sockfd= ndb_mgm_convert_to_transporter(h);
+ if ( sockfd == NDB_INVALID_SOCKET)
+ {
+ ndbout_c("Error: %s: %d",
+ ndb_mgm_get_latest_error_desc(*h),
+ ndb_mgm_get_latest_error(*h));
+ ndbout_c("%s: %d", __FILE__, __LINE__);
+ ndb_mgm_destroy_handle(h);
+ }
+ return sockfd;
+}
+
+/**
+ * Given a SocketClient, creates a NdbMgmHandle, turns it into a transporter
+ * and returns the socket.
+ */
+NDB_SOCKET_TYPE TransporterRegistry::connect_ndb_mgmd(SocketClient *sc)
+{
+ NdbMgmHandle h= ndb_mgm_create_handle();
+
+ if ( h == NULL )
+ {
+ return NDB_INVALID_SOCKET;
+ }
+
+ /**
+ * Set connectstring
+ */
+ {
+ BaseString cs;
+ cs.assfmt("%s:%u",sc->get_server_name(),sc->get_port());
+ ndb_mgm_set_connectstring(h, cs.c_str());
+ }
+
+ if(ndb_mgm_connect(h, 0, 0, 0)<0)
+ {
+ ndb_mgm_destroy_handle(&h);
+ return NDB_INVALID_SOCKET;
+ }
+
+ return connect_ndb_mgmd(&h);
+}
+
+template class Vector<TransporterRegistry::Transporter_interface>;
diff --git a/storage/ndb/src/common/transporter/basictest/Makefile b/storage/ndb/src/common/transporter/basictest/Makefile
new file mode 100644
index 00000000000..d86af360408
--- /dev/null
+++ b/storage/ndb/src/common/transporter/basictest/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := basicTransporterTest
+BIN_TARGET_ARCHIVES := transporter portlib general
+
+SOURCES = basicTransporterTest.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/basictest/basicTransporterTest.cpp b/storage/ndb/src/common/transporter/basictest/basicTransporterTest.cpp
new file mode 100644
index 00000000000..c0a437c4907
--- /dev/null
+++ b/storage/ndb/src/common/transporter/basictest/basicTransporterTest.cpp
@@ -0,0 +1,535 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "TransporterRegistry.hpp"
+#include "TransporterDefinitions.hpp"
+#include "TransporterCallback.hpp"
+#include <RefConvert.hpp>
+
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+
+int basePortTCP = 17000;
+
+SCI_TransporterConfiguration sciTemplate = {
+ 8000,
+ // Packet size
+ 2500000, // Buffer size
+ 2, // number of adapters
+ 1, // remote node id SCI
+ 2, // Remote node Id SCI
+ 0, // local ndb node id (server)
+ 0, // remote ndb node id (client)
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+TCP_TransporterConfiguration tcpTemplate = {
+ 17000, // port;
+ "", // remoteHostName;
+ "", // localhostname
+ 2, // remoteNodeId;
+ 1, // localNodeId;
+ 10000, // sendBufferSize - Size of SendBuffer of priority B
+ 10000, // maxReceiveSize - Maximum no of bytes to receive
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+OSE_TransporterConfiguration oseTemplate = {
+ "", // remoteHostName;
+ "", // localHostName;
+ 0, // remoteNodeId;
+ 0, // localNodeId;
+ false, // compression;
+ true, // checksum;
+ true, // signalId;
+ 0, // byteOrder;
+
+ 2000, // prioASignalSize;
+ 1000, // prioBSignalSize;
+ 10
+};
+
+SHM_TransporterConfiguration shmTemplate = {
+ 0, //remoteNodeId
+ 0, //localNodeId;
+ false, //compression
+ true, //checksum;
+ true, //signalId;
+ 0, //byteOrder;
+ 123, //shmKey;
+ 2500000 //shmSize;
+};
+
+TransporterRegistry *tReg = 0;
+
+#ifndef OSE_DELTA
+#include <signal.h>
+#endif
+
+extern "C"
+void
+signalHandler(int signo){
+#ifndef OSE_DELTA
+ ::signal(13, signalHandler);
+#endif
+ char buf[255];
+ sprintf(buf,"Signal: %d\n", signo);
+ ndbout << buf << endl;
+}
+
+void
+usage(const char * progName){
+ ndbout << "Usage: " << progName << " <type> localNodeId localHostName"
+ << " remoteHostName1 remoteHostName2" << endl;
+ ndbout << " type = shm tcp ose sci" << endl;
+ ndbout << " localNodeId - 1 to 3" << endl;
+}
+
+typedef void (* CreateTransporterFunc)(void * conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName);
+
+void createOSETransporter(void *, NodeId, NodeId, const char *, const char *);
+void createSCITransporter(void *, NodeId, NodeId, const char *, const char *);
+void createTCPTransporter(void *, NodeId, NodeId, const char *, const char *);
+void createSHMTransporter(void *, NodeId, NodeId, const char *, const char *);
+
+int signalReceived[4];
+
+int
+main(int argc, const char **argv){
+
+ signalHandler(0);
+
+ for(int i = 0; i<4; i++)
+ signalReceived[i] = 0;
+
+ if(argc < 5){
+ usage(argv[0]);
+ return 0;
+ }
+
+ Uint32 noOfConnections = 0;
+ const char * progName = argv[0];
+ const char * type = argv[1];
+ const NodeId localNodeId = atoi(argv[2]);
+ const char * localHostName = argv[3];
+ const char * remoteHost1 = argv[4];
+ const char * remoteHost2 = NULL;
+
+ if(argc == 5)
+ noOfConnections = 1;
+ else {
+ noOfConnections = 2;
+ remoteHost2 = argv[5];
+ }
+
+ if(localNodeId < 1 || localNodeId > 3){
+ ndbout << "localNodeId = " << localNodeId << endl << endl;
+ usage(progName);
+ return 0;
+ }
+
+ ndbout << "-----------------" << endl;
+ ndbout << "localNodeId: " << localNodeId << endl;
+ ndbout << "localHostName: " << localHostName << endl;
+ ndbout << "remoteHost1 (node " << (localNodeId == 1?2:1) << "): "
+ << remoteHost1 << endl;
+ if(noOfConnections == 2){
+ ndbout << "remoteHost2 (node " << (localNodeId == 3?2:3) << "): "
+ << remoteHost2 << endl;
+ }
+ ndbout << "-----------------" << endl;
+
+ void * confTemplate = 0;
+ CreateTransporterFunc func = 0;
+
+ if(strcasecmp(type, "tcp") == 0){
+ func = createTCPTransporter;
+ confTemplate = &tcpTemplate;
+ } else if(strcasecmp(type, "ose") == 0){
+ func = createOSETransporter;
+ confTemplate = &oseTemplate;
+ } else if(strcasecmp(type, "sci") == 0){
+ func = createSCITransporter;
+ confTemplate = &sciTemplate;
+ } else if(strcasecmp(type, "shm") == 0){
+ func = createSHMTransporter;
+ confTemplate = &shmTemplate;
+ } else {
+ ndbout << "Unsupported transporter type" << endl;
+ return 0;
+ }
+
+ ndbout << "Creating transporter registry" << endl;
+ tReg = new TransporterRegistry;
+ tReg->init(localNodeId);
+
+ switch(localNodeId){
+ case 1:
+ (* func)(confTemplate, 1, 2, localHostName, remoteHost1);
+ if(noOfConnections == 2)
+ (* func)(confTemplate, 1, 3, localHostName, remoteHost2);
+ break;
+ case 2:
+ (* func)(confTemplate, 2, 1, localHostName, remoteHost1);
+ if(noOfConnections == 2)
+ (* func)(confTemplate, 2, 3, localHostName, remoteHost2);
+ break;
+ case 3:
+ (* func)(confTemplate, 3, 1, localHostName, remoteHost1);
+ if(noOfConnections == 2)
+ (* func)(confTemplate, 3, 2, localHostName, remoteHost2);
+ break;
+ }
+
+ ndbout << "Doing startSending/startReceiving" << endl;
+ tReg->startSending();
+ tReg->startReceiving();
+
+ ndbout << "Connecting" << endl;
+ tReg->setPerformState(PerformConnect);
+ tReg->checkConnections();
+
+ unsigned sum = 0;
+ do {
+ sum = 0;
+ for(int i = 0; i<4; i++)
+ sum += signalReceived[i];
+
+ tReg->checkConnections();
+
+ tReg->external_IO(500);
+ NdbSleep_MilliSleep(500);
+
+ ndbout << "In main loop" << endl;
+ } while(sum != 2*noOfConnections);
+
+ ndbout << "Doing setPerformState(Disconnect)" << endl;
+ tReg->setPerformState(PerformDisconnect);
+
+ ndbout << "Doing checkConnections()" << endl;
+ tReg->checkConnections();
+
+ ndbout << "Sleeping 3 secs" << endl;
+ NdbSleep_SecSleep(3);
+
+ ndbout << "Deleting transporter registry" << endl;
+ delete tReg; tReg = 0;
+
+ return 0;
+}
+
+void
+checkData(SignalHeader * const header, Uint8 prio, Uint32 * const theData,
+ LinearSectionPtr ptr[3]){
+ Uint32 expectedLength = 0;
+ if(prio == 0)
+ expectedLength = 17;
+ else
+ expectedLength = 19;
+
+ if(header->theLength != expectedLength){
+ ndbout << "Unexpected signal length: " << header->theLength
+ << " expected: " << expectedLength << endl;
+ abort();
+ }
+
+ if(header->theVerId_signalNumber != expectedLength + 1)
+ abort();
+
+ if(header->theReceiversBlockNumber != expectedLength + 2)
+ abort();
+
+ if(refToBlock(header->theSendersBlockRef) != expectedLength + 3)
+ abort();
+
+ if(header->theSendersSignalId != expectedLength + 5)
+ abort();
+
+ if(header->theTrace != expectedLength + 6)
+ abort();
+
+ if(header->m_noOfSections != (prio == 0 ? 0 : 1))
+ abort();
+
+ if(header->m_fragmentInfo != (prio + 1))
+ abort();
+
+ Uint32 dataWordStart = header->theLength ;
+ for(unsigned i = 0; i<header->theLength; i++){
+ if(theData[i] != i){ //dataWordStart){
+ ndbout << "data corrupt!\n" << endl;
+ abort();
+ }
+ dataWordStart ^= (~i*i);
+ }
+
+ if(prio != 0){
+ ndbout_c("Found section");
+ if(ptr[0].sz != header->theLength)
+ abort();
+
+ if(memcmp(ptr[0].p, theData, (ptr[0].sz * 4)) != 0)
+ abort();
+ }
+}
+
+void
+sendSignalTo(NodeId nodeId, int prio){
+ SignalHeader sh;
+ sh.theLength = (prio == 0 ? 17 : 19);
+ sh.theVerId_signalNumber = sh.theLength + 1;
+ sh.theReceiversBlockNumber = sh.theLength + 2;
+ sh.theSendersBlockRef = sh.theLength + 3;
+ sh.theSendersSignalId = sh.theLength + 4;
+ sh.theSignalId = sh.theLength + 5;
+ sh.theTrace = sh.theLength + 6;
+ sh.m_noOfSections = (prio == 0 ? 0 : 1);
+ sh.m_fragmentInfo = prio + 1;
+
+ Uint32 theData[25];
+
+ Uint32 dataWordStart = sh.theLength;
+ for(unsigned i = 0; i<sh.theLength; i++){
+ theData[i] = i;
+ dataWordStart ^= (~i*i);
+ }
+ ndbout << "Sending prio " << (int)prio << " signal to node: "
+ << nodeId
+ << " gsn = " << sh.theVerId_signalNumber << endl;
+
+ LinearSectionPtr ptr[3];
+ ptr[0].p = &theData[0];
+ ptr[0].sz = sh.theLength;
+
+ SendStatus s = tReg->prepareSend(&sh, prio, theData, nodeId, ptr);
+ if(s != SEND_OK){
+ ndbout << "Send was not ok. Send was: " << s << endl;
+ }
+}
+
+void
+execute(void* callbackObj,
+ SignalHeader * const header, Uint8 prio, Uint32 * const theData,
+ LinearSectionPtr ptr[3]){
+ const NodeId nodeId = refToNode(header->theSendersBlockRef);
+
+ ndbout << "Recieved prio " << (int)prio << " signal from node: "
+ << nodeId
+ << " gsn = " << header->theVerId_signalNumber << endl;
+ checkData(header, prio, theData, ptr);
+ ndbout << " Data is ok!\n" << endl;
+
+ signalReceived[nodeId]++;
+
+ if(prio == 0)
+ sendSignalTo(nodeId, 1);
+ else
+ tReg->setPerformState(nodeId, PerformDisconnect);
+}
+
+void
+copy(Uint32 * & insertPtr,
+ class SectionSegmentPool & thePool, const SegmentedSectionPtr & _ptr){
+ abort();
+}
+
+void
+reportError(void* callbackObj, NodeId nodeId, TransporterError errorCode){
+ char buf[255];
+ sprintf(buf, "reportError (%d, %x)", nodeId, errorCode);
+ ndbout << buf << endl;
+ if(errorCode & 0x8000){
+ tReg->setPerformState(nodeId, PerformDisconnect);
+ abort();
+ }
+}
+
+/**
+ * Report average send theLength in bytes (4096 last sends)
+ */
+void
+reportSendLen(void* callbackObj, NodeId nodeId, Uint32 count, Uint64 bytes){
+ char buf[255];
+ sprintf(buf, "reportSendLen(%d, %d)", nodeId, (Uint32)(bytes/count));
+ ndbout << buf << endl;
+}
+
+/**
+ * Report average receive theLength in bytes (4096 last receives)
+ */
+void
+reportReceiveLen(void* callbackObj, NodeId nodeId, Uint32 count, Uint64 bytes){
+ char buf[255];
+ sprintf(buf, "reportReceiveLen(%d, %d)", nodeId, (Uint32)(bytes/count));
+ ndbout << buf << endl;
+}
+
+/**
+ * Report connection established
+ */
+void
+reportConnect(void* callbackObj, NodeId nodeId){
+ char buf[255];
+ sprintf(buf, "reportConnect(%d)", nodeId);
+ ndbout << buf << endl;
+ tReg->setPerformState(nodeId, PerformIO);
+
+ sendSignalTo(nodeId, 0);
+}
+
+/**
+ * Report connection broken
+ */
+void
+reportDisconnect(void* callbackObj, NodeId nodeId, Uint32 errNo){
+ char buf[255];
+ sprintf(buf, "reportDisconnect(%d)", nodeId);
+ ndbout << buf << endl;
+ if(signalReceived[nodeId] < 2)
+ tReg->setPerformState(nodeId, PerformConnect);
+}
+
+int
+checkJobBuffer() {
+ /**
+ * Check to see if jobbbuffers are starting to get full
+ * and if so call doJob
+ */
+ return 0;
+}
+
+void
+createOSETransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName){
+ ndbout << "Creating OSE transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+ OSE_TransporterConfiguration * conf = (OSE_TransporterConfiguration*)_conf;
+
+ conf->localNodeId = localNodeId;
+ conf->localHostName = localHostName;
+ conf->remoteNodeId = remoteNodeId;
+ conf->remoteHostName = remoteHostName;
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+void
+createTCPTransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName){
+ ndbout << "Creating TCP transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+ TCP_TransporterConfiguration * conf = (TCP_TransporterConfiguration*)_conf;
+
+ int port;
+ if(localNodeId == 1 && remoteNodeId == 2) port = basePortTCP + 0;
+ if(localNodeId == 1 && remoteNodeId == 3) port = basePortTCP + 1;
+ if(localNodeId == 2 && remoteNodeId == 1) port = basePortTCP + 0;
+ if(localNodeId == 2 && remoteNodeId == 3) port = basePortTCP + 2;
+ if(localNodeId == 3 && remoteNodeId == 1) port = basePortTCP + 1;
+ if(localNodeId == 3 && remoteNodeId == 2) port = basePortTCP + 2;
+
+ conf->localNodeId = localNodeId;
+ conf->localHostName = localHostName;
+ conf->remoteNodeId = remoteNodeId;
+ conf->remoteHostName = remoteHostName;
+ conf->port = port;
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+void
+createSCITransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName){
+
+
+ ndbout << "Creating SCI transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+
+ SCI_TransporterConfiguration * conf = (SCI_TransporterConfiguration*)_conf;
+
+ conf->remoteSciNodeId0= (Uint16)atoi(localHostName);
+ conf->remoteSciNodeId1= (Uint16)atoi(remoteHostName);
+
+
+ conf->localNodeId = localNodeId;
+ conf->remoteNodeId = remoteNodeId;
+
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+void
+createSHMTransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName){
+
+
+ ndbout << "Creating SHM transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+
+ SHM_TransporterConfiguration * conf = (SHM_TransporterConfiguration*)_conf;
+
+ conf->localNodeId = localNodeId;
+ conf->remoteNodeId = remoteNodeId;
+
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
diff --git a/storage/ndb/src/common/transporter/buddy.cpp b/storage/ndb/src/common/transporter/buddy.cpp
new file mode 100644
index 00000000000..dc25e2dc66c
--- /dev/null
+++ b/storage/ndb/src/common/transporter/buddy.cpp
@@ -0,0 +1,325 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "buddy.hpp"
+
+void Chunk256::setFree(bool free){
+ // Bit 0 of allocationTimeStamp represents if the segment is free or not
+ Uint32 offMask = 0x0; // A mask to set the 0 bit to 0
+ allocationTimeStamp = 0x0;
+ if(free)
+ // Set this bit to 0, if segment should be free
+ allocationTimeStamp = allocationTimeStamp & offMask;
+}
+
+bool Chunk256::getFree(){
+ Uint32 offMask = 0x0;
+ return ((allocationTimeStamp | offMask) == offMask ? true : false);
+}
+
+void Chunk256::setAllocationTimeStamp(Uint32 cTime){
+ // Bits 1-31 of allocationTimeStamp represent the allocation time for segment
+
+ // printf("\nSet allocation time. Current time %d", cTime);
+ Uint32 onMask = 0x80000000; // A mask to set the 0 bit to 1
+ allocationTimeStamp = 0x0;
+ allocationTimeStamp = onMask | cTime;
+}
+
+Uint32 Chunk256::getAllocationTimeStamp(){
+ Uint32 onMask = 0x80000000;
+ allocationTimeStamp = allocationTimeStamp ^ onMask;
+ printf("\nGet allocation time. Time is %d", allocationTimeStamp);
+ return allocationTimeStamp;
+};
+
+bool BuddyMemory::allocate(int nChunksToAllocate) {
+
+ // Allocate the memory block needed. This memory is deallocated in the
+ // destructor of TransporterRegistry.
+
+ printf("\nAllocating %d chunks...", nChunksToAllocate);
+
+ startOfMemoryBlock = (Uint32*) malloc(256 * nChunksToAllocate);
+
+ if (startOfMemoryBlock == NULL)
+ return false;
+
+ // Allocate the array of 256-byte chunks
+ chunk = new Chunk256[nChunksToAllocate];
+
+ // Initialize the chunk-array. Every 8 kB segment consists of 32 chunks.
+ // Set all chunks to free and set the prev and next pointer
+ for (int i=0; i < nChunksToAllocate; i++) {
+ chunk[i].setFree(true);
+ if (i%32 == 0) {
+ // The first chunk in every segment will point to the prev and next segment
+ chunk[i].prevSegmentOfSameSize = i-32;
+ chunk[i].nextSegmentOfSameSize = i + 32;
+ chunk[0].prevSegmentOfSameSize = END_OF_CHUNK_LIST;
+ chunk[totalNoOfChunks-32].nextSegmentOfSameSize = END_OF_CHUNK_LIST;
+ } else {
+ // The rest of the chunks in the segments have undefined prev and next pointers
+ chunk[i].prevSegmentOfSameSize = UNDEFINED_CHUNK;
+ chunk[i].nextSegmentOfSameSize = UNDEFINED_CHUNK;
+ }
+ }
+
+ // Initialize the freeSegment-pointers
+ for (int i=0; i<sz_MAX; i++)
+ freeSegment[i] = UNDEFINED_CHUNK;
+
+ // There are only 8 kB segments at startup
+ freeSegment[sz_8192] = 0;
+
+ for (int i=0; i<sz_MAX; i++)
+ printf("\nPointers: %d", freeSegment[i]);
+
+ return true;
+}
+
+
+bool BuddyMemory::getSegment(Uint32 size, Segment * dst) {
+
+ // The no of chunks the user asked for
+ Uint32 nChunksAskedFor = ceil((double(size)/double(256)));
+ int segm;
+
+ printf("\n%d chunks asked for", nChunksAskedFor);
+
+ // It may be that the closest segment size above
+ // nChunksAskedFor*256 is not a size that is available in
+ // the freeSegment-list, i.e. it may not be of FreeSegmentSize.
+ int nChunksToAllocate = nChunksAskedFor;
+
+ // Find the FreeSegmentSize closest above nChunksAskedFor
+ if ((nChunksToAllocate != 1) && (nChunksToAllocate % 2 != 0))
+ nChunksToAllocate++;
+
+ printf("\n%d chunks to allocate", nChunksToAllocate);
+ int segmSize = logTwoPlus(nChunksToAllocate) - 1;
+ if (size-pow(2,segmSize) > 256)
+ segmSize ++;
+ printf("\nSegment size: %f", pow(2,int(8+segmSize)));
+
+ while ((segmSize <= sz_GET_MAX) && (freeSegment[segmSize] == UNDEFINED_CHUNK))
+ segmSize++;
+
+ segm = freeSegment[segmSize];
+ if (segm != UNDEFINED_CHUNK){
+ // Free segment of asked size or larger is found
+
+ // Remove the found segment from the freeSegment-list
+ removeFromFreeSegmentList(segmSize, segm);
+
+ // Set all chunks to allocated (not free) and set the allocation time
+ // for the segment we are about to allocate
+ for (int i = segm; i <= segm+nChunksToAllocate; i++) {
+ chunk[i].setFree(false);
+ chunk[i].setAllocationTimeStamp(currentTime);
+ }
+
+ // Before returning the segment, check if it is larger than the segment asked for
+ if (nChunksAskedFor < nChunksToAllocate)
+ release(nChunksAskedFor, nChunksToAllocate - nChunksAskedFor - 1);
+
+ Segment segment;
+ segment.segmentAddress = startOfMemoryBlock+(segm * 256);
+ segment.segmentSize = 256 * nChunksAskedFor;
+ segment.releaseId = segm;
+
+ printf("\nSegment: segment address = %d, segment size = %d, release Id = %d",
+ segment.segmentAddress, segment.segmentSize, segment.releaseId);
+
+ return true;
+ }
+ printf("\nNo segments of asked size or larger are found");
+ return false;
+}
+
+void BuddyMemory::removeFromFreeSegmentList(int sz, int index) {
+ // Remove the segment from the freeSegment list
+
+ printf("\nRemoving segment from list...");
+ if (index != UNDEFINED_CHUNK) {
+ Chunk256 prevChunk;
+ Chunk256 nextChunk;
+ int prevChunkIndex = chunk[index].prevSegmentOfSameSize;
+ int nextChunkIndex = chunk[index].nextSegmentOfSameSize;
+
+ if (prevChunkIndex == END_OF_CHUNK_LIST) {
+ if (nextChunkIndex == END_OF_CHUNK_LIST)
+ // We are about to remove the only element in the list
+ freeSegment[sz] = UNDEFINED_CHUNK;
+ else {
+ // We are about to remove the first element in the list
+ nextChunk = chunk[nextChunkIndex];
+ nextChunk.prevSegmentOfSameSize = END_OF_CHUNK_LIST;
+ freeSegment[sz] = nextChunkIndex;
+ }
+ } else {
+ if (nextChunkIndex == END_OF_CHUNK_LIST) {
+ // We are about to remove the last element in the list
+ prevChunk = chunk[prevChunkIndex];
+ prevChunk.nextSegmentOfSameSize = END_OF_CHUNK_LIST;
+ } else {
+ // We are about to remove an element in the middle of the list
+ prevChunk = chunk[prevChunkIndex];
+ nextChunk = chunk[nextChunkIndex];
+ prevChunk.nextSegmentOfSameSize = nextChunkIndex;
+ nextChunk.prevSegmentOfSameSize = prevChunkIndex;
+ }
+ }
+ }
+ for (int i=0; i<sz_MAX; i++)
+ printf("\nPointers: %d", freeSegment[i]);
+}
+
+void BuddyMemory::release(int releaseId, int size) {
+
+ int nChunksToRelease = (size == 0 ? 1 : ceil(double(size)/double(256)));
+ //nChunksToRelease = ceil(double(size)/double(256));
+ int startChunk = releaseId;
+ int endChunk = releaseId + nChunksToRelease - 1;
+
+ printf("\n%d chunks to release (initially)", nChunksToRelease);
+
+ // Set the chunks we are about to release to free
+ for (int i = startChunk; i <= endChunk; i++){
+ chunk[i].setFree(true);
+ }
+
+ // Look at the chunks before the segment we are about to release
+ for (int i = releaseId-1; i >= 0; i--) {
+ if (!chunk[i].getFree())
+ break;
+ else {
+ startChunk = i;
+ nChunksToRelease++;
+ // Look at the next-pointer. If it is valid, we have a
+ // chunk that is the start of a free segment. Remove it
+ // from the freeSegment-list.
+ if (chunk[i].nextSegmentOfSameSize != UNDEFINED_CHUNK)
+ removeFromFreeSegmentList(size, i);
+ }
+ }
+
+ // Look at the chunks after the segment we are about to release
+ for (int i = endChunk+1; i <= totalNoOfChunks; i++) {
+ if (!chunk[i].getFree())
+ break;
+ else {
+ endChunk = i;
+ nChunksToRelease++;
+ // Look at the next-pointer. If it is valid, we have a
+ // chunk that is the start of a free segment. Remove it
+ // from the free segment list
+ if (chunk[i].nextSegmentOfSameSize != UNDEFINED_CHUNK)
+ removeFromFreeSegmentList(size, i);
+ }
+ }
+
+ // We have the start and end indexes and total no of free chunks.
+ // Separate the chunks into segments that can be added to the
+ // freeSegments-list.
+ int restChunk = 0;
+ int segmSize;
+
+ printf("\n%d chunks to release (finally)", nChunksToRelease);
+
+ segmSize = logTwoPlus(nChunksToRelease) - 1;
+ if (segmSize > sz_MAX) {
+ segmSize = sz_MAX;
+ }
+
+ nChunksToRelease = pow(2,segmSize);
+ addToFreeSegmentList(nChunksToRelease*256, startChunk);
+}
+
+void BuddyMemory::addToFreeSegmentList(int sz, int index) {
+ // Add a segment to the freeSegment list
+
+ printf("\nAsked to add segment of size %d", sz);
+
+ // Get an index in freeSegment list corresponding to sz size
+ int segmSize = logTwoPlus(sz) - 1;
+ if (sz - pow(2,segmSize) >= 256)
+ segmSize ++;
+ sz = segmSize - 8;
+
+ int nextSegm = freeSegment[sz];
+
+ printf("\nAdding a segment of size %f", pow(2,(8 + sz)));
+
+ freeSegment[sz] = index;
+ if (nextSegm == UNDEFINED_CHUNK) {
+ // We are about to add a segment to an empty list
+ chunk[index].prevSegmentOfSameSize = END_OF_CHUNK_LIST;
+ chunk[index].nextSegmentOfSameSize = END_OF_CHUNK_LIST;
+ }
+ else {
+ // Add the segment first in the list
+ chunk[index].prevSegmentOfSameSize = END_OF_CHUNK_LIST;
+ chunk[index].nextSegmentOfSameSize = nextSegm;
+ chunk[nextSegm].prevSegmentOfSameSize = index;
+ }
+
+ for (int i=0; i<sz_MAX; i++)
+ printf("\nPointers: %d", freeSegment[i]);
+
+}
+
+Uint32 BuddyMemory::logTwoPlus(Uint32 arg) {
+ // Calculate log2(arg) + 1
+
+ Uint32 resValue;
+
+ arg = arg | (arg >> 8);
+ arg = arg | (arg >> 4);
+ arg = arg | (arg >> 2);
+ arg = arg | (arg >> 1);
+ resValue = (arg & 0x5555) + ((arg >> 1) & 0x5555);
+ resValue = (resValue & 0x3333) + ((resValue >> 2) & 0x3333);
+ resValue = resValue + (resValue >> 4);
+ resValue = (resValue & 0xf) + ((resValue >> 8) & 0xf);
+
+ return resValue;
+}
+
+bool BuddyMemory::memoryAvailable() {
+ // Return true if there is at least 8 kB memory available
+ for (int i = sz_8192; i < sz_MAX; i++)
+ if (freeSegment[i] != UNDEFINED_CHUNK)
+ return true;
+ return false;
+}
+
+
+void BuddyMemory::refreshTime(Uint32 time) {
+ if (time - currentTime > 1000) {
+ // Update current time
+ currentTime = time;
+ // Go through the chunk-list every second and release
+ // any chunks that have been allocated for too long
+ for (int i=0; i<totalNoOfChunks; i++) {
+ if ((!chunk[i].getFree()) &&
+ (currentTime-chunk[i].getAllocationTimeStamp() > ALLOCATION_TIMEOUT)) {
+ release(i, 256);
+ printf("\nChunks hve been allocated for too long");
+ }
+ }
+ }
+}
diff --git a/storage/ndb/src/common/transporter/buddy.hpp b/storage/ndb/src/common/transporter/buddy.hpp
new file mode 100644
index 00000000000..f720e9e61a1
--- /dev/null
+++ b/storage/ndb/src/common/transporter/buddy.hpp
@@ -0,0 +1,172 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 BUDDY_H
+#define BUDDY_H
+
+#include <ndb_global.h>
+
+typedef unsigned int Uint32;
+typedef unsigned short Uint16;
+typedef unsigned long long Uint64;
+
+//
+const int UNDEFINED_CHUNK = -2; // XXX Set to hex
+
+//
+const int END_OF_CHUNK_LIST = -1; // XXX Set to hex
+
+// A timeout (no of seconds) for the memory segments in the TransporterRegistry
+// memory pool. If a segment has been occupied (free=false) for a longer period
+// than this timeout, it will be released.
+const int ALLOCATION_TIMEOUT = 10000;
+
+// Free segments should always be as large as possible
+// and are only allowed to be in any of these sizes
+enum FreeSegmentSize {
+ sz_256 = 0,
+ sz_512 = 1,
+ sz_1024 = 2,
+ sz_2048 = 3,
+ sz_4096 = 4,
+ sz_8192 = 5,
+ sz_16384 = 6,
+ sz_32768 = 7,
+ sz_65536 = 8,
+ sz_131072 = 9,
+ sz_GET_MAX = 5,
+ sz_MAX = 9
+};
+
+struct Segment;
+
+class BuddyMemory {
+public:
+
+ // Return true if there is at least 8 kB memory available
+ bool memoryAvailable();
+
+ //
+ bool allocate(int nChunksToAllocate);
+
+ // Remove the segment from the freeSegment list
+ void removeFromFreeSegmentList(int sz, int index);
+
+ // Release the segment of size
+ void release(int releaseId, int size);
+
+ // Add a segment to the freeSegment list
+ void addToFreeSegmentList(int sz, int index);
+
+ bool getSegment(Uint32 size, Segment * dst);
+
+ void refreshTime(Uint32 time);
+
+ //Calculate log2(arg) + 1
+ Uint32 logTwoPlus(Uint32 arg);
+
+ // The current time
+ Uint32 currentTime;
+
+ // Pointer to the first free segment of size FreeSegmentSize
+ Uint32 freeSegment[sz_MAX];
+
+ // Start address of the memory block allocated
+ Uint32* startOfMemoryBlock;
+
+ // Total number of 256 byte chunks.
+ Uint32 totalNoOfChunks;
+
+ // Array of 256-byte chunks
+ struct Chunk256* chunk;
+};
+
+struct Segment {
+ Uint32 segmentSize; // Size of the segment in no of words
+ Uint16 index; // Index in the array of SegmentListElements
+ Uint16 releaseId; // Unique no used when releasing the segment
+ // Undefined if Long_signal.deallocIndicator==0
+ union {
+ Uint32* segmentAddress; // Address to the memory segment
+ Uint64 _padding_NOT_TO_BE_USED_;
+ };
+};
+
+struct Chunk256 {
+ Uint32 allocationTimeStamp; // Bit 0 represents if the segment is free or not
+ // Bit 1-31 is the allocation time for the segment
+ // Bit 1-31 are undefined if the segment is free
+ Uint32 nextSegmentOfSameSize; // Undefined if allocated.
+ // The first chunk in a free segment has a valid
+ // next-pointer. In the rest of the chunks
+ // belonging to the segment it is UNDEFINED_CHUNK.
+ Uint32 prevSegmentOfSameSize; // Undefined if allocated
+ // The first chunk in a free segment has a valid
+ // prev-pointer. In the rest of the chunks
+ // belonging to the segment it is UNDEFINED_CHUNK.
+
+ void setFree(bool free);
+
+ bool getFree();
+
+ void setAllocationTimeStamp(Uint32 cTime);
+
+ Uint32 getAllocationTimeStamp();
+};
+
+// inline void Chunk256::setFree(bool free){
+// // Bit 0 of allocationTimeStamp represents if the segment is free or not
+// allocationTimeStamp = 0x0;
+
+// printf("\nSet free segment");
+// Uint32 offMask = 0x0; // A mask to set the 0 bit to 0
+// if(free)
+// // Set this bit to 0, if segment should be free
+// allocationTimeStamp = allocationTimeStamp & offMask;
+// }
+
+// inline bool Chunk256::getFree(){
+// // Get free segment
+
+// allocationTimeStamp = 0x0;
+// Uint32 offMask = 0x0;
+
+// printf("\nGet free segment");
+// return ((allocationTimeStamp | offMask) == offMask ? true : false);
+// }
+
+// inline void Chunk256::setAllocationTimeStamp(Uint32 cTime){
+// // Bits 1-31 of allocationTimeStamp represent the allocation time for segment
+
+// Uint32 onMask = 0x80000000; // A mask to set the 0 bit to 1
+// allocationTimeStamp = 0x0;
+
+// printf("\nSet allocation time");
+
+// allocationTimeStamp = onMask | cTime;
+// }
+
+// inline Uint32 Chunk256::getAllocationTimeStamp(){
+
+// Uint32 onMask = 0x80000000; // A mask to set the 0 bit to 1
+// allocationTimeStamp = 0x0;
+
+// printf("\nGet allocation time");
+// allocationTimeStamp = allocationTimeStamp ^ onMask;
+// return allocationTimeStamp;
+// };
+
+#endif
diff --git a/storage/ndb/src/common/transporter/failoverSCI/Makefile b/storage/ndb/src/common/transporter/failoverSCI/Makefile
new file mode 100644
index 00000000000..1e3d5f4a4b7
--- /dev/null
+++ b/storage/ndb/src/common/transporter/failoverSCI/Makefile
@@ -0,0 +1,18 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := failoverSCI
+BIN_TARGET_LIBS := sisci
+BIN_TARGET_ARCHIVES := portlib
+
+CCFLAGS_LOC += -I..
+
+SOURCES = failoverSCI.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/failoverSCI/failoverSCI.cpp b/storage/ndb/src/common/transporter/failoverSCI/failoverSCI.cpp
new file mode 100644
index 00000000000..803029ee565
--- /dev/null
+++ b/storage/ndb/src/common/transporter/failoverSCI/failoverSCI.cpp
@@ -0,0 +1,863 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "sisci_types.h"
+#include "sisci_api.h"
+#include "sisci_error.h"
+//#include "sisci_demolib.h"
+#include <NdbTick.h>
+#include <NdbSleep.h>
+#define NO_CALLBACK NULL
+#define NO_FLAGS 0
+#define DATA_TRANSFER_READY 8
+
+sci_error_t error;
+sci_desc_t sdOne;
+sci_desc_t sdTwo;
+sci_local_segment_t localSegmentOne;
+sci_local_segment_t localSegmentTwo;
+sci_remote_segment_t remoteSegmentOne;
+sci_remote_segment_t remoteSegmentTwo;
+sci_map_t localMapOne;
+sci_map_t localMapTwo;
+sci_map_t remoteMapOne;
+sci_map_t remoteMapTwo;
+unsigned int localAdapterNo = 0;
+unsigned int standbyAdapterNo = 1;
+unsigned int localNodeId1;
+unsigned int localNodeId2;
+unsigned int remoteNodeId1 = 0;
+unsigned int remoteNodeId2 = 0;
+unsigned int localSegmentId;
+unsigned int remoteSegmentId1;
+unsigned int remoteSegmentId2;
+unsigned int segmentSize = 8192;
+unsigned int offset = 0;
+unsigned int client = 0;
+unsigned int server = 0;
+unsigned int *localbufferPtr;
+static int data;
+static int interruptConnected=0;
+
+/*********************************************************************************/
+/* U S A G E */
+/* */
+/*********************************************************************************/
+
+void Usage()
+{
+ printf("Usage of shmem\n");
+ printf("shmem -rn <remote node-id> -client/server [ -adapterno <adapter no> -size <segment size> ] \n\n");
+ printf(" -rn : Remote node-id\n");
+ printf(" -client : The local node is client\n");
+ printf(" -server : The local node is server\n");
+ printf(" -adapterno : Local adapter number (default %d)\n", localAdapterNo);
+ printf(" -size : Segment block size (default %d)\n", segmentSize);
+ printf(" -help : This helpscreen\n");
+
+ printf("\n");
+}
+
+
+/*********************************************************************************/
+/* P R I N T P A R A M E T E R S */
+/* */
+/*********************************************************************************/
+void PrintParameters(void)
+{
+
+ printf("Test parameters for %s \n",(client) ? "client" : "server" );
+ printf("----------------------------\n\n");
+ printf("Local node-id1 : %d\n",localNodeId1);
+ printf("Local node-id2 : %d\n",localNodeId2);
+ // printf("Remote node-id : %d\n",remoteNodeId);
+ printf("Local adapter no. : %d\n",localAdapterNo);
+ printf("Segment size : %d\n",segmentSize);
+ printf("----------------------------\n\n");
+
+}
+
+
+/*********************************************************************************/
+/* F I L L S E G M E N T W I T H D A T A */
+/* */
+/*********************************************************************************/
+
+sci_error_t GetLocalNodeId(Uint32 localAdapterNo, Uint32* localNodeId)
+{
+ sci_query_adapter_t queryAdapter;
+ sci_error_t error;
+ unsigned int _localNodeId;
+
+ queryAdapter.subcommand = SCI_Q_ADAPTER_NODEID;
+ queryAdapter.localAdapterNo = localAdapterNo;
+ queryAdapter.data = &_localNodeId;
+
+ SCIQuery(SCI_Q_ADAPTER,&queryAdapter,NO_FLAGS,&error);
+
+ *localNodeId=_localNodeId;
+
+ return error;
+}
+
+
+
+
+
+
+sci_error_t SendInterrupt(sci_desc_t sd,
+ Uint32 localAdapterNo,
+ Uint32 localSciNodeId,
+ Uint32 remoteSciNodeId,
+ Uint32 interruptNo){
+
+ sci_error_t error;
+ sci_remote_interrupt_t remoteInterrupt;
+ Uint32 timeOut = SCI_INFINITE_TIMEOUT;
+
+ // Now connect to the other sides interrupt flag
+ do {
+ SCIConnectInterrupt(sd, &remoteInterrupt, remoteSciNodeId, localAdapterNo,
+ interruptNo, timeOut, NO_FLAGS, &error);
+ } while (error != SCI_ERR_OK);
+
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr, "SCIConnectInterrupt failed - Error code 0x%x\n", error);
+ return error;
+ }
+
+ // Trigger interrupt
+ printf("\nNode %u sent interrupt (0x%x) to node %d\n",localSciNodeId, interruptNo, remoteSciNodeId);
+ SCITriggerInterrupt(remoteInterrupt, NO_FLAGS, &error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr, "SCITriggerInterrupt failed - Error code 0x%x\n", error);
+ return error;
+ }
+
+
+ // Disconnect and remove interrupts
+ SCIDisconnectInterrupt(remoteInterrupt, NO_FLAGS, &error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr, "SCIDisconnectInterrupt failed - Error code 0x%x\n", error);
+ return error;
+ }
+
+ return error;
+}
+
+
+sci_error_t ReceiveInterrupt(sci_desc_t sd,
+ Uint32 localAdapterNo,
+ Uint32 localSciNodeId,
+ Uint32 interruptNo,
+ Uint32 timeout) {
+
+ sci_error_t error;
+ sci_local_interrupt_t localInterrupt;
+ Uint32 timeOut = SCI_INFINITE_TIMEOUT;
+
+ // Create an interrupt
+ SCICreateInterrupt(sd, &localInterrupt, localAdapterNo,
+ &interruptNo, 0, NULL, SCI_FLAG_FIXED_INTNO, &error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr, "SCICreateInterrupt failed - Error code 0x%x\n", error);
+ return error;
+ }
+
+
+ // Wait for an interrupt
+ SCIWaitForInterrupt(localInterrupt, timeOut, NO_FLAGS, &error);
+
+ printf("\nNode %u received interrupt (0x%x)\n", localSciNodeId, interruptNo);
+
+ // Remove interrupt
+
+ SCIRemoveInterrupt(localInterrupt, NO_FLAGS, &error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr, "SCIRemoveInterrupt failed - Error code 0x%x\n", error);
+ return error;
+ }
+ return error;
+}
+
+
+sci_error_t FillSegmentWithData(unsigned int segmentSize, int reverse)
+{
+ unsigned int i;
+ unsigned int nostores;
+
+
+ nostores = (segmentSize) / sizeof(unsigned int);
+
+ /* Allocate buffer */
+
+ localbufferPtr = (unsigned int*)malloc( segmentSize );
+ if ( localbufferPtr == NULL ) {
+ /*
+ * Unable to create local buffer - Insufficient memory available
+ */
+ return SCI_ERR_NOSPC;
+ }
+ if(reverse) {
+ /* Fill in the data into a local buffer */
+ printf("Filling forward order \n");
+ for (i=0;i<nostores;i++) {
+ localbufferPtr[i] = i;
+ }
+ }
+ else {
+ int temp=nostores;
+ printf("Filling reverse order \n");
+ for (i=0;i<nostores;i++) {
+ localbufferPtr[i] = temp-- ;
+
+ }
+
+ }
+
+ return SCI_ERR_OK;
+}
+
+
+
+
+/*********************************************************************************/
+/* P R I N T C L I E N T D A T A */
+/* */
+/*********************************************************************************/
+
+void PrintClientData(void)
+{
+ unsigned int i;
+
+ printf("\nClient data: ");
+ /* Print the first 20 entries in the segment */
+ for (i=0;i<20;i++) {
+ printf("%d ",localbufferPtr[i]);
+ }
+
+ printf("\n");
+}
+
+
+/*********************************************************************************/
+/* P R I N T S E R V E R D A T A */
+/* */
+/*********************************************************************************/
+
+void PrintServerData(volatile unsigned int *localMapAddr)
+{
+
+ unsigned int *buffer;
+ int i;
+
+ // printf("\nServer data: ");
+ buffer = (unsigned int *)localMapAddr;
+
+ /* Print the first 20 entries in the segment */
+ for (i=0; i< 20; i++) {
+
+ printf("%d ",buffer[i]);
+ }
+ printf("\n");
+
+}
+
+
+
+/*********************************************************************************/
+/* T R A N S F E R D A T A */
+/* */
+/*********************************************************************************/
+
+unsigned int TransferData(sci_map_t remoteMap,
+ volatile unsigned int *remoteSegmentAddr1,
+ volatile unsigned int *remoteSegmentAddr2,
+ unsigned int segmentSize)
+
+{
+
+ volatile unsigned int *remoteBuffer1;
+ volatile unsigned int *remoteBuffer;
+ volatile unsigned int *remoteBuffer2;
+ static int times = 0;
+ sci_sequence_t sequence;
+ sci_error_t error;
+ unsigned int nostores;
+ unsigned int j;
+ sci_sequence_status_t sequenceStatus;
+
+
+ remoteBuffer1 = (volatile unsigned int *)remoteSegmentAddr1;
+ remoteBuffer2 = (volatile unsigned int *)remoteSegmentAddr2;
+ remoteBuffer=remoteBuffer1;
+
+ /* 4-byte test only */
+ nostores = (segmentSize) / sizeof(unsigned int);
+
+ /* Create a sequence for data error checking */
+ SCICreateMapSequence(remoteMapOne,&sequence,NO_FLAGS,&error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"SCICreateMapSequence failed - Error code 0x%x\n",error);
+ return error;
+ }
+
+
+
+ /* Fill in the data into a local buffer */
+ error = SendInterrupt(sdOne,localAdapterNo,localNodeId1,remoteNodeId1, DATA_TRANSFER_READY);
+
+ error = FillSegmentWithData(segmentSize, 0);
+
+ tryagain:
+ PrintServerData(localbufferPtr);
+ fprintf(stderr,"After recover \n");
+ while(1){
+
+
+ //data=0;
+
+ if (error != SCI_ERR_OK) {
+ /*
+ * Unable to create local buffer - Insufficient memory available
+ */
+ printf( "Unable to create local buffer - Insufficient memory available\n" );
+
+ return error;
+ }
+
+ do {
+ /* Start data error checking */
+ sequenceStatus = SCIStartSequence(sequence,NO_FLAGS,&error);
+ } while (sequenceStatus != SCI_SEQ_OK) ;
+
+
+ /* Transfer data to remote node */
+ for (j=0;j<nostores;j++) {
+ remoteBuffer[j] = localbufferPtr[j];
+ }
+
+ /* Check for error after data transfer */
+ sequenceStatus = SCICheckSequence(sequence,NO_FLAGS,&error);
+ if (sequenceStatus != SCI_SEQ_OK) {
+ fprintf(stderr,"Data transfer failed\n");
+ if(times==0) {
+ error = FillSegmentWithData(segmentSize, 1);
+
+ SCICreateMapSequence(remoteMapTwo,&sequence,NO_FLAGS,&error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"SCICreateMapSequence failed - Error code 0x%x\n",error);
+ return error;
+ return SCI_ERR_TRANSFER_FAILED;
+ }
+ }
+ else
+ {
+ error = FillSegmentWithData(segmentSize, 0);
+ /* Create a sequence for data error checking */
+ SCICreateMapSequence(remoteMapOne,&sequence,NO_FLAGS,&error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"SCICreateMapSequence failed - Error code 0x%x\n",error);
+ return error;
+ return SCI_ERR_TRANSFER_FAILED;
+ }
+
+ }
+ fprintf(stderr,"Recovery \n");
+ if(times==0)
+ remoteBuffer=remoteBuffer2;
+ else
+ remoteBuffer=remoteBuffer1;
+ times++;
+ printf("remotebuffer %p times %d\n", remoteBuffer, times);
+ goto tryagain;
+
+ }
+ int timeout=0;
+ // error = SendInterrupt(sdOne,localAdapterNo,localNodeId1,remoteNodeId1, DATA_TRANSFER_READY);
+ // NdbSleep_MilliSleep(100);
+ //error = ReceiveInterrupt(sdOne,localAdapterNo,localNodeId1,DATA_TRANSFER_READY, timeout);
+
+ }
+ /* Remove the Sequence */
+ SCIRemoveSequence(sequence,NO_FLAGS, &error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"SCIRemoveSequence failed - Error code 0x%x\n",error);
+ return error;
+ }
+
+ return SCI_ERR_OK;
+}
+
+
+/*********************************************************************************/
+/* S H M E M C L I E N T N O D E */
+/* */
+/*********************************************************************************/
+
+unsigned int ShmemClientNode(void)
+{
+
+ volatile unsigned int *remoteMapAddr1;
+ volatile unsigned int *remoteMapAddr2;
+ printf("here?\n");
+
+
+ /* Create a segmentId */
+ remoteSegmentId1 = 1;//(remoteNodeId1 << 16) | localNodeId1;
+
+ /* Connect to remote segment */
+
+ printf("Connect to remote segment .... \n");
+ printf("segid = %d node %d \n",remoteSegmentId1, remoteNodeId1 );
+
+ do {
+ SCIConnectSegment(sdOne,
+ &remoteSegmentOne,
+ remoteNodeId1,
+ remoteSegmentId1,
+ localAdapterNo,
+ NO_CALLBACK,
+ NULL,
+ SCI_INFINITE_TIMEOUT,
+ NO_FLAGS,
+ &error);
+
+ } while (error != SCI_ERR_OK);
+
+
+ printf("connected\n");
+
+ // remoteSegmentId2 = (remoteNodeId2 << 16) | localNodeId2;
+ // printf("segid = %d\n",remoteSegmentId2 );
+ printf("segid = %d node %d \n",remoteSegmentId1, remoteNodeId1 );
+ do {
+ SCIConnectSegment(sdTwo,
+ &remoteSegmentTwo,
+ remoteNodeId2,
+ remoteSegmentId1,
+ standbyAdapterNo,
+ NO_CALLBACK,
+ NULL,
+ SCI_INFINITE_TIMEOUT,
+ NO_FLAGS,
+ &error);
+
+ } while (error != SCI_ERR_OK);
+
+
+
+ printf("connected 3\n");
+ printf("Remote segment (id=0x%x) is connected.\n", remoteSegmentId2);
+
+
+ /* Map remote segment to user space */
+ remoteMapAddr1 = (unsigned int*)SCIMapRemoteSegment(remoteSegmentOne,&remoteMapOne,offset,segmentSize,NULL,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Remote segment (id=0x%x) is mapped to user space @ 0x%x. \n", remoteSegmentId1, remoteMapAddr1);
+ } else {
+ fprintf(stderr,"SCIMapRemoteSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ remoteMapAddr2 = (unsigned int *)SCIMapRemoteSegment(remoteSegmentTwo,&remoteMapTwo,offset,segmentSize,NULL,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Remote segment (id=0x%x) is mapped to user space @ 0x%x. \n", remoteSegmentId2, remoteMapAddr2);
+ } else {
+ fprintf(stderr,"SCIMapRemoteSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+ /* Start data transfer and error checking */
+ error = (sci_error_t)TransferData(remoteMapOne,remoteMapAddr1, remoteMapAddr2,segmentSize);
+ if (error == SCI_ERR_OK) {
+ printf("Data transfer done!\n\n");
+ } else {
+ fprintf(stderr,"Data transfer failed - Error code 0x%x\n\n",error);
+ return 0;
+ }
+
+ /* Send an interrupt to remote node telling that the data transfer is ready */
+ error = SendInterrupt(sdOne,localAdapterNo,localNodeId1,remoteNodeId1, DATA_TRANSFER_READY);
+ if (error == SCI_ERR_OK) {
+ printf("\nInterrupt message sent to remote node\n");
+ } else {
+ printf("\nInterrupt synchronization failed\n");
+ return 0;
+ }
+
+ PrintClientData();
+
+ /* Unmap remote segment */
+ SCIUnmapSegment(remoteMapOne,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The remote segment is unmapped\n");
+ } else {
+ fprintf(stderr,"SCIUnmapSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ SCIUnmapSegment(remoteMapTwo,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The remote segment is unmapped\n");
+ } else {
+ fprintf(stderr,"SCIUnmapSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+ /* Disconnect segment */
+ SCIDisconnectSegment(remoteSegmentOne,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The segment is disconnected\n");
+ } else {
+ fprintf(stderr,"SCIDisconnectSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ SCIDisconnectSegment(remoteSegmentTwo,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The segment is disconnected\n");
+ } else {
+ fprintf(stderr,"SCIDisconnectSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+ return 1;
+}
+
+
+/*********************************************************************************/
+/* S H M E M S E R V E R N O D E */
+/* */
+/*********************************************************************************/
+
+unsigned int ShmemServerNode(void)
+{
+
+ unsigned int *localMapAddr;
+
+ /* Create a segmentId */
+ localSegmentId =1;// (localNodeId1 << 16) | remoteNodeId1;
+
+ /* Create local segment */
+ SCICreateSegment(sdOne,&localSegmentOne,localSegmentId, segmentSize, NO_CALLBACK, NULL, NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=%d, size=%d) is created. \n", localSegmentId, segmentSize);
+ } else {
+ fprintf(stderr,"SCICreateSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ //localSegmentId = (localNodeId2 << 16) | remoteNodeId2;
+ /*
+ SCICreateSegment(sdTwo,&localSegmentTwo,localSegmentId+1, segmentSize, NO_CALLBACK, NULL, NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=%d, size=%d) is created (2). \n", localSegmentId, segmentSize);
+ } else {
+ fprintf(stderr,"SCICreateSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ printf("segment one %p segment 2 %p\n", localSegmentOne, localSegmentTwo);
+ */
+ /* Prepare the segment */
+ SCIPrepareSegment(localSegmentOne,localAdapterNo,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=%d, size=%d) is prepared. \n", localSegmentId, segmentSize);
+ } else {
+ fprintf(stderr,"SCIPrepareSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+ /* Prepare the segment */
+
+ SCIPrepareSegment(localSegmentOne,standbyAdapterNo,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=%d, size=%d) is created. \n", localSegmentId, segmentSize);
+ } else {
+ fprintf(stderr,"SCIPrepareSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+ /* Map local segment to user space */
+ localMapAddr = (unsigned int *)SCIMapLocalSegment(localSegmentOne,&localMapOne, offset,segmentSize, NULL,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=0x%x) is mapped to user space @ 0x%x.\n", localSegmentId, localMapAddr);
+ } else {
+ fprintf(stderr,"SCIMapLocalSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+ /* Map local segment to user space */
+ /*
+ localMapAddr = (unsigned int *)SCIMapLocalSegment(localSegmentTwo,&localMapTwo, offset,segmentSize, NULL,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=0x%x) is mapped to user space @ 0x%x.\n", localSegmentId, localMapAddr);
+ printf("Local segment (id=%d) is mapped to user space.\n", localSegmentId);
+ } else {
+ fprintf(stderr,"SCIMapLocalSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+ */
+
+ /* Set the segment available */
+ SCISetSegmentAvailable(localSegmentOne, localAdapterNo, NO_FLAGS, &error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=0x%x) is available for remote connections. \n", localSegmentId);
+ } else {
+ fprintf(stderr,"SCISetSegmentAvailable failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+ SCISetSegmentAvailable(localSegmentOne, standbyAdapterNo, NO_FLAGS, &error);
+ if (error == SCI_ERR_OK) {
+ printf("Local segment (id=0x%x) is available for remote connections. \n", localSegmentId);
+ } else {
+ fprintf(stderr,"SCISetSegmentAvailable failed - Error code 0x%x\n",error);
+ return 0;
+ }
+ int timeout=0;
+ error = ReceiveInterrupt(sdOne,localAdapterNo,localNodeId1,DATA_TRANSFER_READY, timeout);
+
+ if (error == SCI_ERR_OK) {
+ printf("\nThe data transfer is ready\n");
+ } else {
+ printf("\nInterrupt synchronization failed\n");
+ return 0;
+ }
+
+
+ again:
+
+ // printf("Wait for the shared memory data transfer .....");
+ /* Wait for interrupt signal telling that block transfer is ready */
+
+ //printf("\nData transfer done!\n");
+ //PrintClientData()
+ PrintServerData(localMapAddr);
+ /*Uint32 micros;
+ Uint32 micros2;
+ NDB_TICKS secs;
+ NdbTick_CurrentMicrosecond(&secs, &micros);
+ error = SendInterrupt(sdOne,localAdapterNo,localNodeId1,remoteNodeId1, DATA_TRANSFER_READY);
+ NdbTick_CurrentMicrosecond(&secs, &micros2);
+ printf("TIME ELAPSED %d \n", micros2-micros);
+// NdbSleep_MilliSleep(100);
+ */
+ goto again;
+
+ /* Unmap local segment */
+ SCIUnmapSegment(localMapTwo,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The local segment is unmapped\n");
+ } else {
+ fprintf(stderr,"SCIUnmapSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ /* Unmap local segment */
+ SCIUnmapSegment(localMapOne,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The local segment is unmapped\n");
+ } else {
+ fprintf(stderr,"SCIUnmapSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+ /* Remove local segment */
+ SCIRemoveSegment(localSegmentOne,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The local segment is removed\n");
+ } else {
+ fprintf(stderr,"SCIRemoveSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+ /* Remove local segment */
+ SCIRemoveSegment(localSegmentTwo,NO_FLAGS,&error);
+ if (error == SCI_ERR_OK) {
+ printf("The local segment is removed\n");
+ } else {
+ fprintf(stderr,"SCIRemoveSegment failed - Error code 0x%x\n",error);
+ return 0;
+ }
+
+
+
+
+ return 1;
+}
+
+
+
+/*********************************************************************************/
+/* M A I N */
+/* */
+/*********************************************************************************/
+
+int main(int argc,char *argv[])
+{
+
+ int counter;
+
+ printf("\n %s compiled %s : %s\n\n",argv[0],__DATE__,__TIME__);
+
+ if (argc<3) {
+ Usage();
+ exit(-1);
+ }
+
+
+ /* Get the parameters */
+ for (counter=1; counter<argc; counter++) {
+
+ if (!strcmp("-rn",argv[counter])) {
+ // remoteNodeId = strtol(argv[counter+1],(char **) NULL,10);
+ continue;
+ }
+
+ if (!strcmp("-size",argv[counter])) {
+ segmentSize = strtol(argv[counter+1],(char **) NULL,10);
+ continue;
+ }
+
+ if (!strcmp("-adapterno",argv[counter])) {
+ localAdapterNo = strtol(argv[counter+1],(char **) NULL,10);
+ continue;
+ }
+
+ if (!strcmp("-client",argv[counter])) {
+ client = 1;
+ continue;
+ }
+
+ if (!strcmp("-server",argv[counter])) {
+ server = 1;
+ continue;
+ }
+
+ if (!strcmp("-help",argv[counter])) {
+ Usage();
+ exit(0);
+ }
+ }
+
+ // if (remoteNodeId == 0) {
+ // fprintf(stderr,"Remote node-id is not specified. Use -rn <remote node-id>\n");
+ // exit(-1);
+ //}
+
+ if (server == 0 && client == 0) {
+ fprintf(stderr,"You must specify a client node or a server node\n");
+ exit(-1);
+ }
+
+ if (server == 1 && client == 1) {
+ fprintf(stderr,"Both server node and client node is selected.\n");
+ fprintf(stderr,"You must specify either a client or a server node\n");
+ exit(-1);
+ }
+
+
+ /* Initialize the SISCI library */
+ SCIInitialize(NO_FLAGS, &error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"SCIInitialize failed - Error code: 0x%x\n",error);
+ exit(error);
+ }
+
+
+ /* Open a file descriptor */
+ SCIOpen(&sdOne,NO_FLAGS,&error);
+ if (error != SCI_ERR_OK) {
+ if (error == SCI_ERR_INCONSISTENT_VERSIONS) {
+ fprintf(stderr,"Version mismatch between SISCI user library and SISCI driver\n");
+ }
+ fprintf(stderr,"SCIOpen failed - Error code 0x%x\n",error);
+ exit(error);
+ }
+
+ /* Open a file descriptor */
+ SCIOpen(&sdTwo,NO_FLAGS,&error);
+ if (error != SCI_ERR_OK) {
+ if (error == SCI_ERR_INCONSISTENT_VERSIONS) {
+ fprintf(stderr,"Version mismatch between SISCI user library and SISCI driver\n");
+ }
+ fprintf(stderr,"SCIOpen failed - Error code 0x%x\n",error);
+ exit(error);
+ }
+
+
+ /* Get local node-id */
+ error = GetLocalNodeId(localAdapterNo, &localNodeId1);
+ error = GetLocalNodeId(standbyAdapterNo, &localNodeId2);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"Could not find the local adapter %d\n", localAdapterNo);
+ SCIClose(sdOne,NO_FLAGS,&error);
+ SCIClose(sdTwo,NO_FLAGS,&error);
+ exit(-1);
+ }
+
+
+ /* Print parameters */
+ PrintParameters();
+
+ if (client) {
+ remoteNodeId1=324;
+ remoteNodeId2=328;
+ ShmemClientNode();
+ } else {
+ remoteNodeId1=452;
+ remoteNodeId2=456;
+ ShmemServerNode();
+ }
+
+ /* Close the file descriptor */
+ SCIClose(sdOne,NO_FLAGS,&error);
+ SCIClose(sdTwo,NO_FLAGS,&error);
+ if (error != SCI_ERR_OK) {
+ fprintf(stderr,"SCIClose failed - Error code: 0x%x\n",error);
+ }
+
+
+ /* Free allocated resources */
+ SCITerminate();
+
+ return SCI_ERR_OK;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/perftest/Makefile b/storage/ndb/src/common/transporter/perftest/Makefile
new file mode 100644
index 00000000000..01869e1acf9
--- /dev/null
+++ b/storage/ndb/src/common/transporter/perftest/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := perfTransporterTest
+BIN_TARGET_ARCHIVES := transporter portlib general
+
+SOURCES = perfTransporterTest.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/perftest/perfTransporterTest.cpp b/storage/ndb/src/common/transporter/perftest/perfTransporterTest.cpp
new file mode 100644
index 00000000000..71df9f12a4c
--- /dev/null
+++ b/storage/ndb/src/common/transporter/perftest/perfTransporterTest.cpp
@@ -0,0 +1,773 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "TransporterRegistry.hpp"
+#include "TransporterDefinitions.hpp"
+#include "TransporterCallback.hpp"
+#include <RefConvert.hpp>
+
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+
+int basePortTCP = 17000;
+
+SCI_TransporterConfiguration sciTemplate = {
+ 2000,
+ // Packet size
+ 2000000, // Buffer size
+ 2, // number of adapters
+ 1, // remote node id SCI
+ 2, // Remote node Id SCI
+ 0, // local ndb node id (server)
+ 0, // remote ndb node id (client)
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+
+SHM_TransporterConfiguration shmTemplate = {
+ 0, //remoteNodeId
+ 0, //localNodeId;
+ false, //compression
+ true, //checksum;
+ true, //signalId;
+ 0, //byteOrder;
+ 123, //shmKey;
+ 25000000 //shmSize;
+};
+
+
+TCP_TransporterConfiguration tcpTemplate = {
+ 17000, // port;
+ "", // remoteHostName;
+ "", // localhostname
+ 2, // remoteNodeId;
+ 1, // localNodeId;
+ 25000000, // sendBufferSize - Size of SendBuffer of priority B
+ 5000000, // maxReceiveSize - Maximum no of bytes to receive
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+OSE_TransporterConfiguration oseTemplate = {
+ "", // remoteHostName;
+ "", // localHostName;
+ 0, // remoteNodeId;
+ 0, // localNodeId;
+ false, // compression;
+ true, // checksum;
+ true, // signalId;
+ 0, // byteOrder;
+
+ 2000, // prioASignalSize;
+ 2000, // prioBSignalSize;
+ 10 // Recv buf size
+};
+
+TransporterRegistry *tReg = 0;
+
+#ifndef OSE_DELTA
+#include <signal.h>
+#endif
+
+extern "C"
+void
+signalHandler(int signo){
+#ifndef OSE_DELTA
+ ::signal(13, signalHandler);
+#endif
+ char buf[255];
+ sprintf(buf,"Signal: %d\n", signo);
+ ndbout << buf << endl;
+}
+
+void
+usage(const char * progName){
+ ndbout << "Usage: " << progName << " <type> localNodeId localHostName"
+ << " remoteHostName"
+ << " [<loop count>] [<send buf size>] [<recv buf size>]" << endl;
+ ndbout << " type = shm tcp ose sci" << endl;
+ ndbout << " localNodeId - {1,2}" << endl;
+}
+
+typedef void (* CreateTransporterFunc)(void * conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendBuf,
+ int recvBuf);
+
+void
+createOSETransporter(void*, NodeId, NodeId, const char*, const char*, int, int);
+void
+createTCPTransporter(void*, NodeId, NodeId, const char*, const char*, int, int);
+void
+createSHMTransporter(void*, NodeId, NodeId, const char*, const char*, int, int);
+void
+createSCITransporter(void*, NodeId, NodeId, const char*, const char*, int, int);
+
+struct TestPhase {
+ int signalSize;
+ int noOfSignals;
+ int noOfSignalSent;
+ int noOfSignalReceived;
+ NDB_TICKS startTime;
+ NDB_TICKS stopTime;
+ NDB_TICKS accTime;
+ int loopCount;
+ Uint64 sendLenBytes, sendCount;
+ Uint64 recvLenBytes, recvCount;
+};
+
+TestPhase testSpec[] = {
+ { 1, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 1, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 1, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 1, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 8, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 8, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 8, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 8, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 16, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 16, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 16, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 16, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 24, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 24, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 24, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 24, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 0, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of random size
+ ,{ 0, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of random size
+ ,{ 0, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of random size
+ ,{ 0, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of random size
+
+ ,{ 100, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals
+ ,{ 100, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals
+ ,{ 100, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals
+ ,{ 100, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals
+
+ ,{ 500, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals
+ ,{ 500, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals
+ ,{ 500, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals
+ ,{ 500, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals
+
+ ,{ 1000, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals
+ ,{ 1000, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals
+ ,{ 1000, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals
+ ,{ 1000, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals
+};
+
+const int noOfTests = sizeof(testSpec)/sizeof(TestPhase);
+
+Uint32 StaticBuffer[1000];
+
+SendStatus
+sendSignalTo(NodeId nodeId, int signalSize, Uint32 count){
+ if(signalSize == 0)
+ signalSize = (rand() % 25) + 1;
+
+ SignalHeader sh;
+ sh.theLength = (signalSize > 25 ? 25 : signalSize);
+ sh.theVerId_signalNumber = count;
+ sh.theReceiversBlockNumber = rand();
+ sh.theSendersBlockRef = rand();
+ sh.theSendersSignalId = rand();
+ sh.theSignalId = rand();
+ sh.theTrace = rand();
+
+ Uint32 theData[25];
+ for(int i = 0; i<25; i++)
+ theData[i] = (i+1) * (Uint32)(&theData[i]);
+
+ theData[0] = count;
+ LinearSectionPtr ptr[3];
+
+ if(signalSize <= 25){
+ sh.m_noOfSections = 0;
+ } else {
+ sh.m_noOfSections = 1;
+ ptr[0].sz = signalSize - 25;
+ ptr[0].p = &StaticBuffer[0];
+ }
+
+ return tReg->prepareSend(&sh, 1, theData, nodeId, ptr);
+}
+
+void
+reportHeader(){
+ ndbout << "#Sigs\tSz\tTime\tSig/sec\tBps\tBps-tot\t"
+ << "s len\tr len" << endl;
+}
+
+void
+print(char * dst, int i){
+ if(i > 1000000){
+ const int d = i / 1000000;
+ const int r = (i - (d * 1000000)) / 100000;
+ if(d < 100)
+ sprintf(dst, "%d.%dM", d, r);
+ else
+ sprintf(dst, "%dM", d);
+ } else if(i > 1000){
+ const int d = i / 1000;
+ const int r = (i - (d * 1000)) / 100;
+ if(d < 100)
+ sprintf(dst, "%d.%dk", d, r);
+ else
+ sprintf(dst, "%dk", d);
+ } else {
+ sprintf(dst, "%d", i);
+ }
+}
+
+void
+printReport(TestPhase & p){
+ if(p.accTime > 0) {
+ Uint32 secs = (p.accTime/p.loopCount)/1000;
+ Uint32 mill = (p.accTime/p.loopCount)%1000;
+ char st[255];
+ if(secs > 0){
+ sprintf(st, "%d.%.2ds", secs, (mill/10));
+ } else {
+ sprintf(st, "%dms", mill);
+ }
+
+ Uint32 sps = (1000*p.noOfSignals*p.loopCount)/p.accTime;
+ Uint32 dps = ((4000*p.noOfSignals)/p.accTime)*(p.loopCount*p.signalSize);
+ Uint32 bps = ((4000*p.noOfSignals)/p.accTime)*(p.loopCount*(p.signalSize+3));
+ if(p.signalSize == 0){
+ dps = ((4000*p.noOfSignals)/p.accTime)*(p.loopCount*(13));
+ bps = ((4000*p.noOfSignals)/p.accTime)*(p.loopCount*(13+3));
+ }
+ char ssps[255];
+ char sbps[255];
+ char sdps[255];
+
+ print(ssps, sps);
+ print(sbps, bps);
+ print(sdps, dps);
+
+
+ char buf[255];
+ if(p.signalSize != 0){
+ BaseString::snprintf(buf, 255,
+ "%d\t%d\t%s\t%s\t%s\t%s\t%d\t%d",
+ p.noOfSignals,
+ 4*p.signalSize,
+ st,
+ ssps,
+ sdps,
+ sbps,
+ (int)(p.sendLenBytes / (p.sendCount == 0 ? 1 : p.sendCount)),
+ (int)(p.recvLenBytes / (p.recvCount == 0 ? 1 : p.recvCount)));
+ } else {
+ BaseString::snprintf(buf, 255,
+ "%d\trand\t%s\t%s\t%s\t%s\t%d\t%d",
+ p.noOfSignals,
+ st,
+ ssps,
+ sdps,
+ sbps,
+ (int)(p.sendLenBytes / (p.sendCount == 0 ? 1 : p.sendCount)),
+ (int)(p.recvLenBytes / (p.recvCount == 0 ? 1 : p.recvCount)));
+
+ }
+ ndbout << buf << endl;
+ }
+}
+
+int loopCount = 1;
+int sendBufSz = -1;
+int recvBufSz = -1;
+
+bool isClient = false;
+bool isConnected = false;
+bool isStarted = false;
+int currentPhase = 0;
+TestPhase allPhases[noOfTests];
+Uint32 signalToEcho;
+Uint32 signalsEchoed;
+NDB_TICKS startTime, stopTime;
+
+void
+client(NodeId remoteNodeId){
+ isClient = true;
+
+ currentPhase = 0;
+ memcpy(allPhases, testSpec, sizeof(testSpec));
+
+ int counter = 0;
+ int sigCounter = 0;
+
+ while(true){
+ TestPhase * current = &allPhases[currentPhase];
+ if(current->noOfSignals == current->noOfSignalSent &&
+ current->noOfSignals == current->noOfSignalReceived){
+
+ /**
+ * Test phase done
+ */
+ current->stopTime = NdbTick_CurrentMillisecond();
+ current->accTime += (current->stopTime - current->startTime);
+
+ NdbSleep_MilliSleep(500 / loopCount);
+
+ current->startTime = NdbTick_CurrentMillisecond();
+
+ current->noOfSignalSent = 0;
+ current->noOfSignalReceived = 0;
+
+ current->loopCount ++;
+ if(current->loopCount == loopCount){
+
+ printReport(allPhases[currentPhase]);
+
+ currentPhase ++;
+ if(currentPhase == noOfTests){
+ /**
+ * Now we are done
+ */
+ break;
+ }
+ NdbSleep_MilliSleep(500);
+ current = &allPhases[currentPhase];
+ current->startTime = NdbTick_CurrentMillisecond();
+ }
+ }
+
+ int signalsLeft = current->noOfSignals - current->noOfSignalSent;
+ if(signalsLeft > 0){
+ for(; signalsLeft > 0; signalsLeft--){
+ if(sendSignalTo(remoteNodeId,current->signalSize,sigCounter)== SEND_OK){
+ current->noOfSignalSent++;
+ sigCounter++;
+ } else {
+ ndbout << "Failed to send: " << sigCounter << endl;
+ tReg->external_IO(10);
+ break;
+ }
+ }
+ }
+ if(counter % 10 == 0)
+ tReg->checkConnections();
+ tReg->external_IO(0);
+ counter++;
+ }
+}
+
+void
+server(){
+ isClient = false;
+
+ signalToEcho = 0;
+ signalsEchoed = 0;
+ for(int i = 0; i<noOfTests; i++)
+ signalToEcho += testSpec[i].noOfSignals;
+
+ signalToEcho *= loopCount;
+
+ while(signalToEcho > signalsEchoed){
+ tReg->checkConnections();
+ for(int i = 0; i<10; i++)
+ tReg->external_IO(10);
+ }
+}
+
+int
+main(int argc, const char **argv){
+
+ const char * progName = argv[0];
+
+ loopCount = 100;
+ sendBufSz = -1;
+ recvBufSz = -1;
+
+ isClient = false;
+ isConnected = false;
+ isStarted = false;
+ currentPhase = 0;
+
+ signalHandler(0);
+
+ if(argc < 5){
+ usage(progName);
+ return 0;
+ }
+
+ const char * type = argv[1];
+ const NodeId localNodeId = atoi(argv[2]);
+ const char * localHostName = argv[3];
+ const char * remoteHost1 = argv[4];
+
+ if(argc >= 6)
+ loopCount = atoi(argv[5]);
+ if(argc >= 7)
+ sendBufSz = atoi(argv[6]);
+ if(argc >= 8)
+ recvBufSz = atoi(argv[7]);
+
+ if(localNodeId < 1 || localNodeId > 2){
+ ndbout << "localNodeId = " << localNodeId << endl << endl;
+ usage(progName);
+ return 0;
+ }
+
+ if(localNodeId == 1)
+ ndbout << "-- ECHO CLIENT --" << endl;
+ else
+ ndbout << "-- ECHO SERVER --" << endl;
+
+ ndbout << "localNodeId: " << localNodeId << endl;
+ ndbout << "localHostName: " << localHostName << endl;
+ ndbout << "remoteHost1 (node " << (localNodeId == 1?2:1) << "): "
+ << remoteHost1 << endl;
+ ndbout << "Loop count: " << loopCount << endl;
+ ndbout << "-----------------" << endl;
+
+ void * confTemplate = 0;
+ CreateTransporterFunc func = 0;
+ if(strcasecmp(type, "tcp") == 0){
+ func = createTCPTransporter;
+ confTemplate = &tcpTemplate;
+ } else if(strcasecmp(type, "ose") == 0){
+ func = createOSETransporter;
+ confTemplate = &oseTemplate;
+ } else if(strcasecmp(type, "sci") == 0){
+ func = createSCITransporter;
+ confTemplate = &sciTemplate;
+ } else if(strcasecmp(type, "shm") == 0){
+ func = createSHMTransporter;
+ confTemplate = &shmTemplate;
+ } else {
+ ndbout << "Unsupported transporter type" << endl;
+ return 0;
+ }
+
+ ndbout << "Creating transporter registry" << endl;
+ tReg = new TransporterRegistry;
+ tReg->init(localNodeId);
+
+ switch(localNodeId){
+ case 1:
+ (* func)(confTemplate, 1, 2, localHostName, remoteHost1,
+ sendBufSz, recvBufSz);
+ break;
+ case 2:
+ (* func)(confTemplate, 2, 1, localHostName, remoteHost1,
+ sendBufSz, recvBufSz);
+ break;
+ }
+
+ ndbout << "Doing startSending/startReceiving" << endl;
+ tReg->startSending();
+ tReg->startReceiving();
+
+ ndbout << "Connecting" << endl;
+ tReg->setPerformState(PerformConnect);
+ tReg->checkConnections();
+
+ if(localNodeId == 1)
+ client(2);
+ else
+ server();
+
+ isStarted = false;
+
+ ndbout << "Sleep 3 secs" << endl;
+ NdbSleep_SecSleep(3);
+
+ ndbout << "Doing setPerformState(Disconnect)" << endl;
+ tReg->setPerformState(PerformDisconnect);
+
+ ndbout << "Doing checkConnections()" << endl;
+ tReg->checkConnections();
+
+ ndbout << "Deleting transporter registry" << endl;
+ delete tReg; tReg = 0;
+
+ return 0;
+}
+
+void
+execute(void* callbackObj, SignalHeader * const header, Uint8 prio,
+ Uint32 * const theData,
+ LinearSectionPtr ptr[3]){
+ const NodeId nodeId = refToNode(header->theSendersBlockRef);
+
+ if(isClient){
+ allPhases[currentPhase].noOfSignalReceived++;
+ } else {
+ int sleepTime = 10;
+ if(theData[0] != signalsEchoed){
+ ndbout << "Missing signal theData[0] = " << theData[0]
+ << " signalsEchoed = " << signalsEchoed << endl;
+ ndbout << (* header) << endl;
+ abort();
+ }
+ while(tReg->prepareSend(header, prio, theData, nodeId, ptr) != SEND_OK){
+ ndbout << "Failed to echo " << theData[0] << endl;
+ NdbSleep_MilliSleep(sleepTime);
+ // sleepTime += 10;
+ }
+ signalsEchoed++;
+ }
+}
+
+void
+copy(Uint32 * & insertPtr,
+ class SectionSegmentPool & thePool, const SegmentedSectionPtr & _ptr){
+ abort();
+}
+
+void
+reportError(void* callbackObj, NodeId nodeId, TransporterError errorCode){
+ char buf[255];
+ sprintf(buf, "reportError (%d, %x) in perfTest", nodeId, errorCode);
+ ndbout << buf << endl;
+ if(errorCode & 0x8000 && errorCode != 0x8014){
+ abort(); //tReg->setPerformState(nodeId, PerformDisconnect);
+ }
+}
+
+/**
+ * Report average send theLength in bytes (4096 last sends)
+ */
+void
+reportSendLen(void* callbackObj, NodeId nodeId, Uint32 count, Uint64 bytes){
+ allPhases[currentPhase].sendCount += count;
+ allPhases[currentPhase].sendLenBytes += bytes;
+
+ if(!isClient){
+ ndbout << "reportSendLen(" << nodeId << ", "
+ << (bytes/count) << ")" << endl;
+ }
+}
+
+/**
+ * Report average receive theLength in bytes (4096 last receives)
+ */
+void
+reportReceiveLen(void* callbackObj, NodeId nodeId, Uint32 count, Uint64 bytes){
+ allPhases[currentPhase].recvCount += count;
+ allPhases[currentPhase].recvLenBytes += bytes;
+
+ if(!isClient){
+ ndbout << "reportReceiveLen(" << nodeId << ", "
+ << (bytes/count) << ")" << endl;
+ }
+}
+
+/**
+ * Report connection established
+ */
+void
+reportConnect(void* callbackObj, NodeId nodeId){
+ char buf[255];
+ sprintf(buf, "reportConnect(%d)", nodeId);
+ ndbout << buf << endl;
+ tReg->setPerformState(nodeId, PerformIO);
+
+ if(!isStarted){
+ isStarted = true;
+ startTime = NdbTick_CurrentMillisecond();
+ if(isClient){
+ reportHeader();
+ allPhases[0].startTime = startTime;
+ }
+ }
+ else{
+ // Resend signals that were lost when connection failed
+ TestPhase * current = &allPhases[currentPhase];
+ current->noOfSignalSent = current->noOfSignalReceived;
+ }
+}
+
+/**
+ * Report connection broken
+ */
+void
+reportDisconnect(void* callbackObj, NodeId nodeId, Uint32 errNo){
+ char buf[255];
+ sprintf(buf, "reportDisconnect(%d)", nodeId);
+ ndbout << buf << endl;
+
+ if(isStarted)
+ tReg->setPerformState(nodeId, PerformConnect);
+}
+
+
+int
+checkJobBuffer() {
+ /**
+ * Check to see if jobbbuffers are starting to get full
+ * and if so call doJob
+ */
+ return 0;
+}
+
+void
+createOSETransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendBuf,
+ int recvBuf){
+
+ ndbout << "Creating OSE transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+ OSE_TransporterConfiguration * conf = (OSE_TransporterConfiguration*)_conf;
+
+ if(sendBuf != -1){
+ conf->prioBSignalSize = sendBuf;
+ }
+ if(recvBuf != -1){
+ conf->receiveBufferSize = recvBuf;
+ }
+
+ ndbout << "\tSendBufferSize: " << conf->prioBSignalSize << endl;
+ ndbout << "\tReceiveBufferSize: " << conf->receiveBufferSize << endl;
+
+ conf->localNodeId = localNodeId;
+ conf->localHostName = localHostName;
+ conf->remoteNodeId = remoteNodeId;
+ conf->remoteHostName = remoteHostName;
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+
+void
+createSCITransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendbuf,
+ int recvbuf) {
+
+
+ ndbout << "Creating SCI transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+
+ SCI_TransporterConfiguration * conf = (SCI_TransporterConfiguration*)_conf;
+
+ conf->remoteSciNodeId0= (Uint16)atoi(localHostName);
+ conf->remoteSciNodeId1= (Uint16)atoi(remoteHostName);
+
+
+ conf->localNodeId = localNodeId;
+ conf->remoteNodeId = remoteNodeId;
+
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+void
+createSHMTransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendbuf,
+ int recvbuf) {
+
+
+ ndbout << "Creating SHM transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+
+ SHM_TransporterConfiguration * conf = (SHM_TransporterConfiguration*)_conf;
+
+
+ conf->localNodeId = localNodeId;
+ conf->remoteNodeId = remoteNodeId;
+
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+
+void
+createTCPTransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendBuf,
+ int recvBuf){
+ ndbout << "Creating TCP transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+ TCP_TransporterConfiguration * conf = (TCP_TransporterConfiguration*)_conf;
+
+ int port;
+ if(localNodeId == 1 && remoteNodeId == 2) port = basePortTCP + 0;
+ if(localNodeId == 1 && remoteNodeId == 3) port = basePortTCP + 1;
+ if(localNodeId == 2 && remoteNodeId == 1) port = basePortTCP + 0;
+ if(localNodeId == 2 && remoteNodeId == 3) port = basePortTCP + 2;
+ if(localNodeId == 3 && remoteNodeId == 1) port = basePortTCP + 1;
+ if(localNodeId == 3 && remoteNodeId == 2) port = basePortTCP + 2;
+
+ if(sendBuf != -1){
+ conf->sendBufferSize = sendBuf;
+ }
+ if(recvBuf != -1){
+ conf->maxReceiveSize = recvBuf;
+ }
+
+ ndbout << "\tSendBufferSize: " << conf->sendBufferSize << endl;
+ ndbout << "\tReceiveBufferSize: " << conf->maxReceiveSize << endl;
+
+ conf->localNodeId = localNodeId;
+ conf->localHostName = localHostName;
+ conf->remoteNodeId = remoteNodeId;
+ conf->remoteHostName = remoteHostName;
+ conf->port = port;
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
diff --git a/storage/ndb/src/common/transporter/priotest/Makefile b/storage/ndb/src/common/transporter/priotest/Makefile
new file mode 100644
index 00000000000..483fc0f1f07
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+SOURCES = prioTransporterTest.cpp
+ARCHIVE_TARGET := libpriotransportertest.a
+
+DIRS := prioTCP prioSHM prioSCI
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioOSE/Makefile b/storage/ndb/src/common/transporter/priotest/prioOSE/Makefile
new file mode 100644
index 00000000000..4df66fa35e0
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioOSE/Makefile
@@ -0,0 +1,17 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := perfOSE
+BIN_TARGET_ARCHIVES := perftransportertest transporter portlib
+
+CCFLAGS_LOC += -I..
+
+SOURCES = perfOSE.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioSCI/Makefile b/storage/ndb/src/common/transporter/priotest/prioSCI/Makefile
new file mode 100644
index 00000000000..7d403539bf3
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioSCI/Makefile
@@ -0,0 +1,17 @@
+include .defs.mk
+
+TYPE := ndbapi
+BIN_TARGET := prioSCI
+BIN_TARGET_LIBS := sisci
+BIN_TARGET_ARCHIVES := priotransportertest transporter portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES = prioSCI.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioSCI/prioSCI.cpp b/storage/ndb/src/common/transporter/priotest/prioSCI/prioSCI.cpp
new file mode 100644
index 00000000000..6218b764e09
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioSCI/prioSCI.cpp
@@ -0,0 +1,29 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <prioTransporterTest.hpp>
+#include <NdbMain.h>
+
+NDB_COMMAND(prioSCI, "prioSCI", "prioSCI", "Test the SCI Transporter", 65535)
+{
+ basePortTCP = 17000;
+ return prioTransporterTest(TestSCI, "prioSCI", argc, argv);
+}
+
+
+
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioSHM/Makefile b/storage/ndb/src/common/transporter/priotest/prioSHM/Makefile
new file mode 100644
index 00000000000..a827c6e3f1e
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioSHM/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := prioSHM
+BIN_TARGET_ARCHIVES := priotransportertest transporter portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES = prioSHM.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioSHM/prioSHM.cpp b/storage/ndb/src/common/transporter/priotest/prioSHM/prioSHM.cpp
new file mode 100644
index 00000000000..4c1701a91e4
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioSHM/prioSHM.cpp
@@ -0,0 +1,26 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <prioTransporterTest.hpp>
+#include <NdbMain.h>
+
+NDB_COMMAND(prioSHM, "prioSHM", "prioSHM", "Test the SHM Transporter", 65535)
+{
+ basePortTCP = 17000;
+ return prioTransporterTest(TestSHM, "prioSHM", argc, argv);
+}
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioTCP/Makefile b/storage/ndb/src/common/transporter/priotest/prioTCP/Makefile
new file mode 100644
index 00000000000..92abf3e7424
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioTCP/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := prioTCP
+BIN_TARGET_ARCHIVES := priotransportertest transporter portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES = prioTCP.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioTCP/prioTCP.cpp b/storage/ndb/src/common/transporter/priotest/prioTCP/prioTCP.cpp
new file mode 100644
index 00000000000..f993dd05ac8
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioTCP/prioTCP.cpp
@@ -0,0 +1,26 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <prioTransporterTest.hpp>
+#include <NdbMain.h>
+
+NDB_COMMAND(prioTCP, "prioTCP", "prioTCP", "Test the TCP Transporter", 65535)
+{
+ basePortTCP = 17000;
+ return prioTransporterTest(TestTCP, "prioTCP", argc, argv);
+}
+
diff --git a/storage/ndb/src/common/transporter/priotest/prioTransporterTest.cpp b/storage/ndb/src/common/transporter/priotest/prioTransporterTest.cpp
new file mode 100644
index 00000000000..6c5623a49a6
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioTransporterTest.cpp
@@ -0,0 +1,768 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "TransporterRegistry.hpp"
+#include "TransporterDefinitions.hpp"
+#include "TransporterCallback.hpp"
+#include <RefConvert.hpp>
+
+#include "prioTransporterTest.hpp"
+
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+
+int basePortTCP = 17000;
+
+SCI_TransporterConfiguration sciTemplate = {
+ 2000,
+ // Packet size
+ 2000000, // Buffer size
+ 2, // number of adapters
+ 1, // remote node id SCI
+ 2, // Remote node Id SCI
+ 0, // local ndb node id (server)
+ 0, // remote ndb node id (client)
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+
+SHM_TransporterConfiguration shmTemplate = {
+ 100000, // shmSize
+ 0, // shmKey
+ 1, // local ndb node id (server)
+ 2, // remote ndb node id (client)
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+TCP_TransporterConfiguration tcpTemplate = {
+ 17000, // port;
+ "", // remoteHostName;
+ "", // localhostname
+ 2, // remoteNodeId;
+ 1, // localNodeId;
+ 2000000, // sendBufferSize - Size of SendBuffer of priority B
+ 2000, // maxReceiveSize - Maximum no of bytes to receive
+ 0, // byteOrder;
+ false, // compression;
+ true, // checksum;
+ true // signalId;
+};
+
+OSE_TransporterConfiguration oseTemplate = {
+ "", // remoteHostName;
+ "", // localHostName;
+ 0, // remoteNodeId;
+ 0, // localNodeId;
+ false, // compression;
+ true, // checksum;
+ true, // signalId;
+ 0, // byteOrder;
+
+ 2000, // prioASignalSize;
+ 2000, // prioBSignalSize;
+ 10 // Recv buf size
+};
+
+TransporterRegistry *tReg = 0;
+
+#ifndef OSE_DELTA
+#include <signal.h>
+#endif
+
+extern "C"
+void
+signalHandler(int signo){
+#ifndef OSE_DELTA
+ ::signal(13, signalHandler);
+#endif
+ char buf[255];
+ sprintf(buf,"Signal: %d\n", signo);
+ ndbout << buf << endl;
+}
+
+void
+usage(const char * progName){
+ ndbout << "Usage: " << progName << " localNodeId localHostName"
+ << " remoteHostName"
+ << " [<loop count>] [<send buf size>] [<recv buf size>]" << endl;
+ ndbout << " localNodeId - {1,2}" << endl;
+}
+
+typedef void (* CreateTransporterFunc)(void * conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendBuf,
+ int recvBuf);
+
+void
+createOSETransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendBuf,
+ int recvBuf){
+
+ ndbout << "Creating OSE transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+ OSE_TransporterConfiguration * conf = (OSE_TransporterConfiguration*)_conf;
+
+ if(sendBuf != -1){
+ conf->prioBSignalSize = sendBuf;
+ }
+ if(recvBuf != -1){
+ conf->receiveBufferSize = recvBuf;
+ }
+
+ ndbout << "\tSendBufferSize: " << conf->prioBSignalSize << endl;
+ ndbout << "\tReceiveBufferSize: " << conf->receiveBufferSize << endl;
+
+ conf->localNodeId = localNodeId;
+ conf->localHostName = localHostName;
+ conf->remoteNodeId = remoteNodeId;
+ conf->remoteHostName = remoteHostName;
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+
+void
+createSCITransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendbuf,
+ int recvbuf) {
+
+
+ ndbout << "Creating SCI transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+
+ SCI_TransporterConfiguration * conf = (SCI_TransporterConfiguration*)_conf;
+
+ conf->remoteSciNodeId0= (Uint16)atoi(localHostName);
+ conf->remoteSciNodeId1= (Uint16)atoi(remoteHostName);
+
+
+ conf->localNodeId = localNodeId;
+ conf->remoteNodeId = remoteNodeId;
+
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+void
+createSHMTransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendbuf,
+ int recvbuf) {
+
+
+ ndbout << "Creating SHM transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+
+ SHM_TransporterConfiguration * conf = (SHM_TransporterConfiguration*)_conf;
+
+
+ conf->localNodeId = localNodeId;
+ conf->remoteNodeId = remoteNodeId;
+
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+
+void
+createTCPTransporter(void * _conf,
+ NodeId localNodeId,
+ NodeId remoteNodeId,
+ const char * localHostName,
+ const char * remoteHostName,
+ int sendBuf,
+ int recvBuf){
+ ndbout << "Creating TCP transporter from node "
+ << localNodeId << "(" << localHostName << ") to "
+ << remoteNodeId << "(" << remoteHostName << ")..." << endl;;
+
+ TCP_TransporterConfiguration * conf = (TCP_TransporterConfiguration*)_conf;
+
+ int port;
+ if(localNodeId == 1 && remoteNodeId == 2) port = basePortTCP + 0;
+ if(localNodeId == 1 && remoteNodeId == 3) port = basePortTCP + 1;
+ if(localNodeId == 2 && remoteNodeId == 1) port = basePortTCP + 0;
+ if(localNodeId == 2 && remoteNodeId == 3) port = basePortTCP + 2;
+ if(localNodeId == 3 && remoteNodeId == 1) port = basePortTCP + 1;
+ if(localNodeId == 3 && remoteNodeId == 2) port = basePortTCP + 2;
+
+ if(sendBuf != -1){
+ conf->sendBufferSize = sendBuf;
+ }
+ if(recvBuf != -1){
+ conf->maxReceiveSize = recvBuf;
+ }
+
+ ndbout << "\tSendBufferSize: " << conf->sendBufferSize << endl;
+ ndbout << "\tReceiveBufferSize: " << conf->maxReceiveSize << endl;
+
+ conf->localNodeId = localNodeId;
+ conf->localHostName = localHostName;
+ conf->remoteNodeId = remoteNodeId;
+ conf->remoteHostName = remoteHostName;
+ conf->port = port;
+ bool res = tReg->createTransporter(conf);
+ if(res)
+ ndbout << "... -- Success " << endl;
+ else
+ ndbout << "... -- Failure " << endl;
+}
+
+struct TestPhase {
+ int signalSize;
+ int noOfSignals;
+ int noOfSignalSent;
+ int noOfSignalReceived;
+ NDB_TICKS startTime;
+ NDB_TICKS stopTime;
+
+ NDB_TICKS startTimePrioA;
+ NDB_TICKS stopTimePrioA;
+ NDB_TICKS totTimePrioA;
+ int bytesSentBeforePrioA;
+ NDB_TICKS accTime;
+ int loopCount;
+ Uint64 sendLenBytes, sendCount;
+ Uint64 recvLenBytes, recvCount;
+};
+
+TestPhase testSpec[] = {
+ { 1, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 1, 10000, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 1, 10000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 1, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 8, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 8, 10000, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 8, 10000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 8, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 16, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 16, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 16, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 16, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 24, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of size 1 word
+ ,{ 24, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of size 1 word
+ ,{ 24, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of size 1 word
+ ,{ 24, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of size 1 word
+
+ ,{ 0, 10, 0,0, 0,0,0,0,0,0,0 } // 10 signals of random size
+ ,{ 0, 100, 0,0, 0,0,0,0,0,0,0 } // 100 signals of random size
+ ,{ 0, 1000, 0,0, 0,0,0,0,0,0,0 } // 1000 signals of random size
+ ,{ 0, 10000, 0,0, 0,0,0,0,0,0,0 } // 10000 signals of random size
+};
+
+const int noOfTests = sizeof(testSpec)/sizeof(TestPhase);
+
+SendStatus
+sendSignalTo(NodeId nodeId, int signalSize, int prio){
+ if(signalSize == 0)
+ signalSize = (rand() % 25) + 1;
+
+ SignalHeader sh;
+ sh.theLength = signalSize;
+ sh.theVerId_signalNumber = rand();
+ sh.theReceiversBlockNumber = rand();
+ sh.theSendersBlockRef = rand();
+ sh.theSendersSignalId = rand();
+ sh.theSignalId = rand();
+ sh.theTrace = rand();
+
+ Uint32 theData[25];
+ for(int i = 0; i<signalSize; i++)
+ theData[i] = (i+1) * (Uint32)(&theData[i]);
+
+ return tReg->prepareSend(&sh, prio, theData, nodeId);
+}
+
+void
+reportHeader(){
+ ndbout << "#Sigs\tSz\tPayload\tTime\tSig/sec\tBps\t"
+ << "s len\tr len\tprioAtime\tbytesb4pA" << endl;
+}
+
+void
+printReport(TestPhase & p){
+ if(p.accTime > 0) {
+ Uint32 secs = (p.accTime/p.loopCount)/1000;
+ Uint32 mill = (p.accTime/p.loopCount)%1000;
+ char st[255];
+ if(secs > 0){
+ sprintf(st, "%d.%.2ds", secs, (mill/10));
+ } else {
+ sprintf(st, "%dms", mill);
+ }
+
+ Uint32 sps = (1000*p.noOfSignals*p.loopCount)/p.accTime;
+ Uint32 bps = ((4000*p.noOfSignals)/p.accTime)*(p.loopCount*(p.signalSize+3));
+ if(p.signalSize == 0)
+ ((4000*p.noOfSignals)/p.accTime)*(p.loopCount*(13+3));
+
+ char ssps[255];
+ if(sps > 1000000){
+ sps /= 1000000;
+ sprintf(ssps, "%dM", (int)sps);
+ } else if(sps > 1000){
+ sps /= 1000;
+ sprintf(ssps, "%dk", (int)sps);
+ } else {
+ sprintf(ssps, "%d", (int)sps);
+ }
+
+ char sbps[255];
+ if(bps > 1000000){
+ bps /= 1000000;
+ sprintf(sbps, "%dM", bps);
+ } else if(bps>1000){
+ bps /= 1000;
+ sprintf(sbps, "%dk", bps);
+ } else {
+ sprintf(sbps, "%d", bps);
+ }
+
+ char buf[255];
+ if(p.signalSize != 0){
+ BaseString::snprintf(buf, 255,
+ "%d\t%d\t%d\t%s\t%s\t%s\t%d\t%d\t%d\t%d",
+ p.noOfSignals,
+ p.signalSize,
+ (4*p.signalSize),
+ st,
+ ssps,
+ sbps,
+ (int)(p.sendLenBytes / (p.sendCount == 0 ? 1 : p.sendCount)),
+ (int)(p.recvLenBytes / (p.recvCount == 0 ? 1 : p.recvCount)),
+ (int)(p.totTimePrioA / p.loopCount),
+ (int)(p.bytesSentBeforePrioA));
+ } else {
+ BaseString::snprintf(buf, 255,
+ "%d\trand\t4*rand\t%s\t%s\t%s\t%d\t%d\t%d\t%d",
+ p.noOfSignals,
+ st,
+ ssps,
+ sbps,
+ (int)(p.sendLenBytes / (p.sendCount == 0 ? 1 : p.sendCount)),
+ (int)(p.recvLenBytes / (p.recvCount == 0 ? 1 : p.recvCount)),
+ (int)(p.totTimePrioA / p.loopCount),
+ (int)(p.bytesSentBeforePrioA));
+
+ }
+ ndbout << buf << endl;
+ }
+}
+
+int loopCount = 1;
+int sendBufSz = -1;
+int recvBufSz = -1;
+
+NDB_TICKS startSec=0;
+NDB_TICKS stopSec=0;
+Uint32 startMicro=0;
+Uint32 stopMicro=0;
+int timerStarted;
+int timerStopped;
+
+bool isClient = false;
+bool isConnected = false;
+bool isStarted = false;
+int currentPhase = 0;
+TestPhase allPhases[noOfTests];
+Uint32 signalToEcho;
+NDB_TICKS startTime, stopTime;
+
+void
+client(NodeId remoteNodeId){
+ isClient = true;
+
+ currentPhase = 0;
+ memcpy(allPhases, testSpec, sizeof(testSpec));
+
+ int counter = 0;
+
+ while(true){
+ TestPhase * current = &allPhases[currentPhase];
+ if(current->noOfSignals == current->noOfSignalSent &&
+ current->noOfSignals == current->noOfSignalReceived){
+
+ /**
+ * Test phase done
+ */
+ current->stopTime = NdbTick_CurrentMillisecond();
+ current->accTime += (current->stopTime - current->startTime);
+
+ NdbSleep_MilliSleep(500 / loopCount);
+
+ current->startTime = NdbTick_CurrentMillisecond();
+
+ current->noOfSignalSent = 0;
+ current->noOfSignalReceived = 0;
+
+ current->loopCount ++;
+ if(current->loopCount == loopCount){
+
+ printReport(allPhases[currentPhase]);
+
+ currentPhase ++;
+ if(currentPhase == noOfTests){
+ /**
+ * Now we are done
+ */
+ break;
+ }
+ NdbSleep_MilliSleep(500);
+ current = &allPhases[currentPhase];
+ current->startTime = NdbTick_CurrentMillisecond();
+ }
+ }
+ int signalsLeft = current->noOfSignals - current->noOfSignalSent;
+ if(signalsLeft > 0){
+ for(; signalsLeft > 1; signalsLeft--){
+ if(sendSignalTo(remoteNodeId, current->signalSize, 1) == SEND_OK) {
+ current->noOfSignalSent++;
+ // ndbout << "sent prio b" << endl;
+ current->bytesSentBeforePrioA += (current->signalSize << 2);
+ }
+ else {
+ tReg->external_IO(10);
+ break;
+ }
+ }
+ //prio A
+ if(signalsLeft==1) {
+ NDB_TICKS sec = 0;
+ Uint32 micro=0;
+ int ret = NdbTick_CurrentMicrosecond(&sec,&micro);
+ if(ret==0)
+ current->startTimePrioA = micro + sec*1000000;
+ if(sendSignalTo(remoteNodeId, current->signalSize, 0) == SEND_OK) {
+ current->noOfSignalSent++;
+ signalsLeft--;
+ }
+ else {
+ tReg->external_IO(10);
+ break;
+ }
+ }
+ }
+
+ if(counter % 10 == 0)
+ tReg->checkConnections();
+ tReg->external_IO(0);
+ counter++;
+ }
+}
+
+void
+server(){
+ isClient = false;
+
+ signalToEcho = 0;
+ for(int i = 0; i<noOfTests; i++)
+ signalToEcho += testSpec[i].noOfSignals;
+
+ signalToEcho *= loopCount;
+
+ while(signalToEcho > 0){
+ tReg->checkConnections();
+ for(int i = 0; i<10; i++)
+ tReg->external_IO(10);
+ }
+}
+
+int
+prioTransporterTest(TestType tt, const char * progName,
+ int argc, const char **argv){
+
+ loopCount = 100;
+ sendBufSz = -1;
+ recvBufSz = -1;
+
+ isClient = false;
+ isConnected = false;
+ isStarted = false;
+ currentPhase = 0;
+
+ signalHandler(0);
+
+ if(argc < 4){
+ usage(progName);
+ return 0;
+ }
+
+ const NodeId localNodeId = atoi(argv[1]);
+ const char * localHostName = argv[2];
+ const char * remoteHost1 = argv[3];
+
+ if(argc >= 5)
+ loopCount = atoi(argv[4]);
+ if(argc >= 6)
+ sendBufSz = atoi(argv[5]);
+ if(argc >= 7)
+ recvBufSz = atoi(argv[6]);
+
+ if(localNodeId < 1 || localNodeId > 2){
+ ndbout << "localNodeId = " << localNodeId << endl << endl;
+ usage(progName);
+ return 0;
+ }
+
+ if(localNodeId == 1)
+ ndbout << "-- ECHO CLIENT --" << endl;
+ else
+ ndbout << "-- ECHO SERVER --" << endl;
+
+ ndbout << "localNodeId: " << localNodeId << endl;
+ ndbout << "localHostName: " << localHostName << endl;
+ ndbout << "remoteHost1 (node " << (localNodeId == 1?2:1) << "): "
+ << remoteHost1 << endl;
+ ndbout << "Loop count: " << loopCount << endl;
+ ndbout << "-----------------" << endl;
+
+ void * confTemplate = 0;
+ CreateTransporterFunc func = 0;
+ switch(tt){
+ case TestTCP:
+ func = createTCPTransporter;
+ confTemplate = &tcpTemplate;
+ break;
+ case TestOSE:
+ func = createOSETransporter;
+ confTemplate = &oseTemplate;
+ break;
+ case TestSCI:
+ func = createSCITransporter;
+ confTemplate = &sciTemplate;
+ break;
+ case TestSHM:
+ func = createSHMTransporter;
+ confTemplate = &shmTemplate;
+ break;
+ default:
+ ndbout << "Unsupported transporter type" << endl;
+ return 0;
+ }
+
+ ndbout << "Creating transporter registry" << endl;
+ tReg = new TransporterRegistry;
+ tReg->init(localNodeId);
+
+ switch(localNodeId){
+ case 1:
+ (* func)(confTemplate, 1, 2, localHostName, remoteHost1,
+ sendBufSz, recvBufSz);
+ break;
+ case 2:
+ (* func)(confTemplate, 2, 1, localHostName, remoteHost1,
+ sendBufSz, recvBufSz);
+ break;
+ }
+
+ ndbout << "Doing startSending/startReceiving" << endl;
+ tReg->startSending();
+ tReg->startReceiving();
+
+ ndbout << "Connecting" << endl;
+ tReg->setPerformState(PerformConnect);
+ tReg->checkConnections();
+
+ if(localNodeId == 1)
+ client(2);
+ else
+ server();
+
+ isStarted = false;
+
+ ndbout << "Sleep 3 secs" << endl;
+ NdbSleep_SecSleep(3);
+
+ ndbout << "Doing setPerformState(Disconnect)" << endl;
+ tReg->setPerformState(PerformDisconnect);
+
+ ndbout << "Doing checkConnections()" << endl;
+ tReg->checkConnections();
+
+ ndbout << "Deleting transporter registry" << endl;
+ delete tReg; tReg = 0;
+
+ return 0;
+}
+
+NdbOut & operator <<(NdbOut & out, SignalHeader & sh){
+ out << "-- Signal Header --" << endl;
+ out << "theLength: " << sh.theLength << endl;
+ out << "gsn: " << sh.theVerId_signalNumber << endl;
+ out << "recBlockNo: " << sh.theReceiversBlockNumber << endl;
+ out << "sendBlockRef: " << sh.theSendersBlockRef << endl;
+ out << "sendersSig: " << sh.theSendersSignalId << endl;
+ out << "theSignalId: " << sh.theSignalId << endl;
+ out << "trace: " << (int)sh.theTrace << endl;
+ return out;
+}
+
+void
+execute(SignalHeader * const header, Uint8 prio, Uint32 * const theData){
+ const NodeId nodeId = refToNode(header->theSendersBlockRef);
+ NDB_TICKS sec = 0;
+ Uint32 micro=0;
+ int ret = NdbTick_CurrentMicrosecond(&sec,&micro);
+ if(prio == 0 && isClient && ret == 0) {
+ allPhases[currentPhase].stopTimePrioA = micro + sec*1000000;
+ allPhases[currentPhase].totTimePrioA +=
+ allPhases[currentPhase].stopTimePrioA -
+ allPhases[currentPhase].startTimePrioA;
+ }
+ if(ret!=0)
+ allPhases[currentPhase].totTimePrioA = -1;
+
+ if(isClient){
+ allPhases[currentPhase].noOfSignalReceived++;
+ } else {
+ int sleepTime = 10;
+ while(tReg->prepareSend(header, prio, theData, nodeId) != SEND_OK){
+ ndbout << "Failed to echo" << sleepTime << endl;
+ NdbSleep_MilliSleep(sleepTime);
+ // sleepTime += 10;
+ }
+
+ signalToEcho--;
+ }
+}
+
+void
+reportError(NodeId nodeId, TransporterError errorCode){
+ char buf[255];
+ sprintf(buf, "reportError (%d, %x) in perfTest", nodeId, errorCode);
+ ndbout << buf << endl;
+ if(errorCode & 0x8000){
+ tReg->setPerformState(nodeId, PerformDisconnect);
+ }
+}
+
+/**
+ * Report average send theLength in bytes (4096 last sends)
+ */
+void
+reportSendLen(NodeId nodeId, Uint32 count, Uint64 bytes){
+ allPhases[currentPhase].sendCount += count;
+ allPhases[currentPhase].sendLenBytes += bytes;
+
+ if(!isClient){
+ ndbout << "reportSendLen(" << nodeId << ", "
+ << (bytes/count) << ")" << endl;
+ }
+}
+
+/**
+ * Report average receive theLength in bytes (4096 last receives)
+ */
+void
+reportReceiveLen(NodeId nodeId, Uint32 count, Uint64 bytes){
+ allPhases[currentPhase].recvCount += count;
+ allPhases[currentPhase].recvLenBytes += bytes;
+
+ if(!isClient){
+ ndbout << "reportReceiveLen(" << nodeId << ", "
+ << (bytes/count) << ")" << endl;
+ }
+}
+
+/**
+ * Report connection established
+ */
+void
+reportConnect(NodeId nodeId){
+ char buf[255];
+ sprintf(buf, "reportConnect(%d)", nodeId);
+ ndbout << buf << endl;
+ tReg->setPerformState(nodeId, PerformIO);
+
+ if(!isStarted){
+ isStarted = true;
+ startTime = NdbTick_CurrentMillisecond();
+ if(isClient){
+ reportHeader();
+ allPhases[0].startTime = startTime;
+ }
+ }
+ else{
+ // Resend signals that were lost when connection failed
+ TestPhase * current = &allPhases[currentPhase];
+ current->noOfSignalSent = current->noOfSignalReceived;
+ }
+}
+
+/**
+ * Report connection broken
+ */
+void
+reportDisconnect(NodeId nodeId, Uint32 errNo){
+ char buf[255];
+ sprintf(buf, "reportDisconnect(%d)", nodeId);
+ ndbout << buf << endl;
+
+ if(isStarted)
+ tReg->setPerformState(nodeId, PerformConnect);
+}
+
+
+int
+checkJobBuffer() {
+ /**
+ * Check to see if jobbbuffers are starting to get full
+ * and if so call doJob
+ */
+ return 0;
+}
diff --git a/storage/ndb/src/common/transporter/priotest/prioTransporterTest.hpp b/storage/ndb/src/common/transporter/priotest/prioTransporterTest.hpp
new file mode 100644
index 00000000000..787a9f46433
--- /dev/null
+++ b/storage/ndb/src/common/transporter/priotest/prioTransporterTest.hpp
@@ -0,0 +1,35 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 PRIO_TRANSPORTER_TEST_HPP
+#define PRIO_TRANSPORTER_TEST_HPP
+
+
+enum TestType {
+ TestTCP,
+ TestOSE,
+ TestSCI,
+ TestSHM
+};
+
+extern int basePortTCP;
+
+int prioTransporterTest(TestType tt, const char * pName,
+ int argc, const char **argv);
+
+
+
+#endif
diff --git a/storage/ndb/src/common/util/Base64.cpp b/storage/ndb/src/common/util/Base64.cpp
new file mode 100644
index 00000000000..3db911f481f
--- /dev/null
+++ b/storage/ndb/src/common/util/Base64.cpp
@@ -0,0 +1,212 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <Base64.hpp>
+
+static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+int
+base64_encode(const UtilBuffer &src, BaseString &dst)
+{
+ return base64_encode(src.get_data(), src.length(), dst);
+}
+
+int
+base64_encode(const void * _s, size_t src_len, BaseString &dst) {
+ const unsigned char * s = (const unsigned char*)_s;
+ size_t i = 0;
+ size_t len = 0;
+ while(i < src_len) {
+ if(len == 76){
+ len = 0;
+ dst.append('\n');
+ }
+
+ unsigned c;
+ c = s[i++];
+ c <<= 8;
+
+ if(i < src_len)
+ c += s[i];
+ c <<= 8;
+ i++;
+
+ if(i < src_len)
+ c += s[i];
+ i++;
+
+ dst.append(base64_table[(c >> 18) & 0x3f]);
+ dst.append(base64_table[(c >> 12) & 0x3f]);
+
+ if(i > (src_len + 1))
+ dst.append('=');
+ else
+ dst.append(base64_table[(c >> 6) & 0x3f]);
+
+ if(i > src_len)
+ dst.append('=');
+ else
+ dst.append(base64_table[(c >> 0) & 0x3f]);
+
+ len += 4;
+ }
+ return 0;
+}
+
+static inline unsigned
+pos(unsigned char c) {
+ return strchr(base64_table, c) - base64_table;
+}
+
+
+int
+base64_decode(const BaseString &src, UtilBuffer &dst) {
+ return base64_decode(src.c_str(), src.length(), dst);
+}
+
+#define SKIP_SPACE(src, i, size){ \
+ while(i < size && isspace(* src)){ \
+ i++; \
+ src++; \
+ } \
+ if(i == size){ \
+ i = size + 1; \
+ break; \
+ } \
+}
+
+int
+base64_decode(const char * src, size_t size, UtilBuffer &dst) {
+ size_t i = 0;
+ while(i < size){
+ unsigned c = 0;
+ int mark = 0;
+
+ SKIP_SPACE(src, i, size);
+
+ c += pos(*src++);
+ c <<= 6;
+ i++;
+
+ SKIP_SPACE(src, i, size);
+
+ c += pos(*src++);
+ c <<= 6;
+ i++;
+
+ SKIP_SPACE(src, i, size);
+
+ if(* src != '=')
+ c += pos(*src++);
+ else {
+ i = size;
+ mark = 2;
+ c <<= 6;
+ goto end;
+ }
+ c <<= 6;
+ i++;
+
+ SKIP_SPACE(src, i, size);
+
+ if(*src != '=')
+ c += pos(*src++);
+ else {
+ i = size;
+ mark = 1;
+ goto end;
+ }
+ i++;
+
+ end:
+ char b[3];
+ b[0] = (c >> 16) & 0xff;
+ b[1] = (c >> 8) & 0xff;
+ b[2] = (c >> 0) & 0xff;
+
+ dst.append((void *)b, 3-mark);
+ }
+
+ if(i != size){
+ abort();
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef __TEST__B64
+/**
+ * USER_FLAGS="-D__TEST__B64" make Base64.o && g++ Base64.o BaseString.o
+ */
+inline
+void
+require(bool b){
+ if(!b)
+ abort();
+}
+
+int
+main(void){
+ for(int i = 0; i < 500; i++){
+ const size_t len = rand() % 10000 + 1;
+ UtilBuffer src;
+ for(size_t j = 0; j<len; j++){
+ char c = rand();
+ src.append(&c, 1);
+ }
+ require(src.length() == len);
+
+ BaseString str;
+ require(base64_encode(src, str) == 0);
+
+ if(str.length() == 3850){
+ printf(">%s<\n", str.c_str());
+ }
+
+ UtilBuffer dst;
+ require(base64_decode(str, dst) == 0);
+ require(dst.length() == src.length());
+
+ const char * c_src = (char*)src.get_data();
+ const char * c_dst = (char*)dst.get_data();
+ if(memcmp(src.get_data(), dst.get_data(), src.length()) != 0){
+ printf("-- src --\n");
+ for(int i2 = 0; i2<len; i2++){
+ unsigned char c = c_src[i2];
+ printf("%.2x ", (unsigned)c);
+ if((i2 % 8) == 7)
+ printf("\n");
+ }
+ printf("\n");
+
+ printf("-- dst --\n");
+ for(int i2 = 0; i2<len; i2++){
+ unsigned char c = c_dst[i2];
+ printf("%.2x ", (unsigned)c);
+ if((i2 % 8) == 7)
+ printf("\n");
+ }
+ printf("\n");
+ abort();
+ }
+ }
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/common/util/BaseString.cpp b/storage/ndb/src/common/util/BaseString.cpp
new file mode 100644
index 00000000000..dbff44c377d
--- /dev/null
+++ b/storage/ndb/src/common/util/BaseString.cpp
@@ -0,0 +1,434 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/* -*- c-basic-offset: 4; -*- */
+#include <ndb_global.h>
+#include <BaseString.hpp>
+#include <basestring_vsnprintf.h>
+
+BaseString::BaseString()
+{
+ m_chr = new char[1];
+ m_chr[0] = 0;
+ m_len = 0;
+}
+
+BaseString::BaseString(const char* s)
+{
+ const size_t n = strlen(s);
+ m_chr = new char[n + 1];
+ memcpy(m_chr, s, n + 1);
+ m_len = n;
+}
+
+BaseString::BaseString(const BaseString& str)
+{
+ const char* const s = str.m_chr;
+ const size_t n = str.m_len;
+ char* t = new char[n + 1];
+ memcpy(t, s, n + 1);
+ m_chr = t;
+ m_len = n;
+}
+
+BaseString::~BaseString()
+{
+ delete[] m_chr;
+}
+
+BaseString&
+BaseString::assign(const char* s)
+{
+ const size_t n = strlen(s);
+ char* t = new char[n + 1];
+ memcpy(t, s, n + 1);
+ delete[] m_chr;
+ m_chr = t;
+ m_len = n;
+ return *this;
+}
+
+BaseString&
+BaseString::assign(const char* s, size_t n)
+{
+ char* t = new char[n + 1];
+ memcpy(t, s, n);
+ t[n] = 0;
+ delete[] m_chr;
+ m_chr = t;
+ m_len = n;
+ return *this;
+}
+
+BaseString&
+BaseString::assign(const BaseString& str, size_t n)
+{
+ if (n > str.m_len)
+ n = str.m_len;
+ return assign(str.m_chr, n);
+}
+
+BaseString&
+BaseString::append(const char* s)
+{
+ const size_t n = strlen(s);
+ char* t = new char[m_len + n + 1];
+ memcpy(t, m_chr, m_len);
+ memcpy(t + m_len, s, n + 1);
+ delete[] m_chr;
+ m_chr = t;
+ m_len += n;
+ return *this;
+}
+
+BaseString&
+BaseString::append(char c) {
+ return appfmt("%c", c);
+}
+
+BaseString&
+BaseString::append(const BaseString& str)
+{
+ return append(str.m_chr);
+}
+
+BaseString&
+BaseString::append(const Vector<BaseString> &vector,
+ const BaseString &separator) {
+ for(size_t i=0;i<vector.size(); i++) {
+ append(vector[i]);
+ if(i<vector.size()-1)
+ append(separator);
+ }
+ return *this;
+}
+
+BaseString&
+BaseString::assfmt(const char *fmt, ...)
+{
+ char buf[1];
+ va_list ap;
+ int l;
+
+ /* Figure out how long the formatted string will be. A small temporary
+ * buffer is used, because I don't trust all implementations to work
+ * when called as vsnprintf(NULL, 0, ...).
+ */
+ va_start(ap, fmt);
+ l = basestring_vsnprintf(buf, sizeof(buf), fmt, ap) + 1;
+ va_end(ap);
+ if(l > (int)m_len) {
+ delete[] m_chr;
+ m_chr = new char[l];
+ }
+ va_start(ap, fmt);
+ basestring_vsnprintf(m_chr, l, fmt, ap);
+ va_end(ap);
+ m_len = strlen(m_chr);
+ return *this;
+}
+
+BaseString&
+BaseString::appfmt(const char *fmt, ...)
+{
+ char buf[1];
+ va_list ap;
+ int l;
+
+ /* Figure out how long the formatted string will be. A small temporary
+ * buffer is used, because I don't trust all implementations to work
+ * when called as vsnprintf(NULL, 0, ...).
+ */
+ va_start(ap, fmt);
+ l = basestring_vsnprintf(buf, sizeof(buf), fmt, ap) + 1;
+ va_end(ap);
+ char *tmp = new char[l];
+ va_start(ap, fmt);
+ basestring_vsnprintf(tmp, l, fmt, ap);
+ va_end(ap);
+ append(tmp);
+ delete[] tmp;
+ return *this;
+}
+
+BaseString&
+BaseString::operator=(const BaseString& str)
+{
+ if (this != &str) {
+ this->assign(str);
+ }
+ return *this;
+}
+
+int
+BaseString::split(Vector<BaseString> &v,
+ const BaseString &separator,
+ int maxSize) const {
+ char *str = strdup(m_chr);
+ int i, start, len, num = 0;
+ len = strlen(str);
+ for(start = i = 0;
+ (i <= len) && ( (maxSize<0) || ((int)v.size()<=maxSize-1) );
+ i++) {
+ if(strchr(separator.c_str(), str[i]) || i == len) {
+ if(maxSize < 0 || (int)v.size() < maxSize-1)
+ str[i] = '\0';
+ v.push_back(BaseString(str+start));
+ num++;
+ start = i+1;
+ }
+ }
+ free(str);
+
+ return num;
+}
+
+ssize_t
+BaseString::indexOf(char c) {
+ char *p;
+ p = strchr(m_chr, c);
+ if(p == NULL)
+ return -1;
+ return (ssize_t)(p-m_chr);
+}
+
+ssize_t
+BaseString::lastIndexOf(char c) {
+ char *p;
+ p = strrchr(m_chr, c);
+ if(p == NULL)
+ return -1;
+ return (ssize_t)(p-m_chr);
+}
+
+BaseString
+BaseString::substr(ssize_t start, ssize_t stop) {
+ if(stop < 0)
+ stop = length();
+ ssize_t len = stop-start;
+ if(len <= 0)
+ return BaseString("");
+ BaseString s;
+ s.assign(m_chr+start, len);
+ return s;
+}
+
+static bool
+iswhite(char c) {
+ switch(c) {
+ case ' ':
+ case '\t':
+ return true;
+ default:
+ return false;
+ }
+ /* NOTREACHED */
+}
+
+char **
+BaseString::argify(const char *argv0, const char *src) {
+ Vector<char *> vargv;
+
+ if(argv0 != NULL)
+ vargv.push_back(strdup(argv0));
+
+ char *tmp = new char[strlen(src)+1];
+ char *dst = tmp;
+ const char *end = src + strlen(src);
+ /* Copy characters from src to destination, while compacting them
+ * so that all whitespace is compacted and replaced by a NUL-byte.
+ * At the same time, add pointers to strings in the vargv vector.
+ * When whitespace is detected, the characters '"' and '\' are honored,
+ * to make it possible to give arguments containing whitespace.
+ * The semantics of '"' and '\' match that of most Unix shells.
+ */
+ while(src < end && *src) {
+ /* Skip initial whitespace */
+ while(src < end && *src && iswhite(*src))
+ src++;
+
+ char *begin = dst;
+ while(src < end && *src) {
+ /* Handle '"' quotation */
+ if(*src == '"') {
+ src++;
+ while(src < end && *src && *src != '"') {
+ if(*src == '\\')
+ src++;
+ *dst++ = *src++;
+ }
+ src++;
+ if(src >= end)
+ goto end;
+ }
+
+ /* Handle '\' */
+ if(*src == '\\')
+ src++;
+ else if(iswhite(*src))
+ break;
+
+ /* Actually copy characters */
+ *dst++ = *src++;
+ }
+
+ /* Make sure the string is properly terminated */
+ *dst++ = '\0';
+ src++;
+
+ vargv.push_back(strdup(begin));
+ }
+ end:
+
+ delete[] tmp;
+ vargv.push_back(NULL);
+
+ /* Convert the C++ Vector into a C-vector of strings, suitable for
+ * calling execv().
+ */
+ char **argv = (char **)malloc(sizeof(*argv) * (vargv.size()));
+ if(argv == NULL)
+ return NULL;
+
+ for(size_t i = 0; i < vargv.size(); i++){
+ argv[i] = vargv[i];
+ }
+
+ return argv;
+}
+
+BaseString&
+BaseString::trim(const char * delim){
+ trim(m_chr, delim);
+ m_len = strlen(m_chr);
+ return * this;
+}
+
+char*
+BaseString::trim(char * str, const char * delim){
+ int len = strlen(str) - 1;
+ for(; len > 0 && strchr(delim, str[len]); len--);
+
+ int pos = 0;
+ for(; pos <= len && strchr(delim, str[pos]); pos++);
+
+ if(pos > len){
+ str[0] = 0;
+ return 0;
+ } else {
+ memmove(str, &str[pos], len - pos + 1);
+ str[len-pos+1] = 0;
+ }
+
+ return str;
+}
+
+int
+BaseString::vsnprintf(char *str, size_t size, const char *format, va_list ap)
+{
+ return(basestring_vsnprintf(str, size, format, ap));
+}
+
+int
+BaseString::snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret= basestring_vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return(ret);
+}
+
+
+#ifdef TEST_BASE_STRING
+
+/*
+g++ -g -Wall -o tbs -DTEST_BASE_STRING -I$NDB_TOP/include/util \
+ -I$NDB_TOP/include/portlib BaseString.cpp
+valgrind ./tbs
+*/
+
+int main()
+{
+ BaseString s("abc");
+ BaseString t(s);
+ s.assign("def");
+ t.append("123");
+ assert(s == "def");
+ assert(t == "abc123");
+ s.assign("");
+ t.assign("");
+ for (unsigned i = 0; i < 1000; i++) {
+ s.append("xyz");
+ t.assign(s);
+ assert(strlen(t.c_str()) % 3 == 0);
+ }
+
+ {
+ BaseString s(":123:abc:;:foo:");
+ Vector<BaseString> v;
+ assert(s.split(v, ":;") == 7);
+
+ assert(v[0] == "");
+ assert(v[1] == "123");
+ assert(v[2] == "abc");
+ assert(v[3] == "");
+ assert(v[4] == "");
+ assert(v[5] == "foo");
+ assert(v[6] == "");
+ }
+
+ {
+ BaseString s(":123:abc:foo:bar");
+ Vector<BaseString> v;
+ assert(s.split(v, ":;", 4) == 4);
+
+ assert(v[0] == "");
+ assert(v[1] == "123");
+ assert(v[2] == "abc");
+ assert(v[3] == "foo:bar");
+
+ BaseString n;
+ n.append(v, "()");
+ assert(n == "()123()abc()foo:bar");
+ n = "";
+ n.append(v);
+ assert(n == " 123 abc foo:bar");
+ }
+
+ {
+ assert(BaseString("hamburger").substr(4,2) == "");
+ assert(BaseString("hamburger").substr(3) == "burger");
+ assert(BaseString("hamburger").substr(4,8) == "urge");
+ assert(BaseString("smiles").substr(1,5) == "mile");
+ assert(BaseString("012345").indexOf('2') == 2);
+ assert(BaseString("hej").indexOf('X') == -1);
+ }
+
+ {
+ assert(BaseString(" 1").trim(" ") == "1");
+ assert(BaseString("1 ").trim(" ") == "1");
+ assert(BaseString(" 1 ").trim(" ") == "1");
+ assert(BaseString("abc\t\n\r kalleabc\t\r\n").trim("abc\t\r\n ") == "kalle");
+ assert(BaseString(" ").trim(" ") == "");
+ }
+ return 0;
+}
+
+#endif
+
+template class Vector<char *>;
+template class Vector<BaseString>;
diff --git a/storage/ndb/src/common/util/Bitmask.cpp b/storage/ndb/src/common/util/Bitmask.cpp
new file mode 100644
index 00000000000..0aa39a37204
--- /dev/null
+++ b/storage/ndb/src/common/util/Bitmask.cpp
@@ -0,0 +1,351 @@
+#include <Bitmask.hpp>
+#include <NdbOut.hpp>
+
+static
+void print(const Uint32 src[], Uint32 len, Uint32 pos = 0)
+{
+ printf("b'");
+ for(unsigned i = 0; i<len; i++)
+ {
+ if(BitmaskImpl::get((pos + len + 31) >> 5, src, i+pos))
+ printf("1");
+ else
+ printf("0");
+ if((i & 31) == 31)
+ printf(" ");
+ }
+}
+
+#ifndef __TEST_BITMASK__
+
+void
+BitmaskImpl::getFieldImpl(const Uint32 src[],
+ unsigned shiftL, unsigned len, Uint32 dst[])
+{
+ assert(shiftL < 32);
+
+ unsigned shiftR = 32 - shiftL;
+ unsigned undefined = shiftL ? ~0 : 0;
+
+ * dst = shiftL ? * dst : 0;
+
+ while(len >= 32)
+ {
+ * dst++ |= (* src) << shiftL;
+ * dst = ((* src++) >> shiftR) & undefined;
+ len -= 32;
+ }
+
+ if(len < shiftR)
+ {
+ * dst |= ((* src) & ((1 << len) - 1)) << shiftL;
+ }
+ else
+ {
+ * dst++ |= ((* src) << shiftL);
+ * dst = ((* src) >> shiftR) & ((1 << (len - shiftR)) - 1) & undefined;
+ }
+}
+
+void
+BitmaskImpl::setFieldImpl(Uint32 dst[],
+ unsigned shiftL, unsigned len, const Uint32 src[])
+{
+ /**
+ *
+ * abcd ef00
+ * 00ab cdef
+ */
+ assert(shiftL < 32);
+ unsigned shiftR = 32 - shiftL;
+ unsigned undefined = shiftL ? ~0 : 0;
+ while(len >= 32)
+ {
+ * dst = (* src++) >> shiftL;
+ * dst++ |= ((* src) << shiftR) & undefined;
+ len -= 32;
+ }
+
+ Uint32 mask = ((1 << len) -1);
+ * dst = (* dst & ~mask);
+ if(len < shiftR)
+ {
+ * dst |= ((* src++) >> shiftL) & mask;
+ }
+ else
+ {
+ * dst |= ((* src++) >> shiftL);
+ * dst |= ((* src) & ((1 << (len - shiftR)) - 1)) << shiftR ;
+ }
+}
+#else
+
+#define DEBUG 0
+#include <Vector.hpp>
+static void do_test(int bitmask_size);
+
+int
+main(int argc, char** argv)
+{
+ int loops = argc > 1 ? atoi(argv[1]) : 1000;
+ int max_size = argc > 2 ? atoi(argv[2]) : 1000;
+
+
+ for(int i = 0; i<loops; i++)
+ do_test(1 + (rand() % max_size));
+}
+
+struct Alloc
+{
+ Uint32 pos;
+ Uint32 size;
+ Vector<Uint32> data;
+};
+
+static void require(bool b)
+{
+ if(!b) abort();
+}
+
+static
+bool cmp(const Uint32 b1[], const Uint32 b2[], Uint32 len)
+{
+ Uint32 sz32 = (len + 31) >> 5;
+ for(int i = 0; i<len; i++)
+ {
+ if(BitmaskImpl::get(sz32, b1, i) ^ BitmaskImpl::get(sz32, b2, i))
+ return false;
+ }
+ return true;
+}
+
+
+static int val_pos = 0;
+static int val[] = { 384, 241, 32,
+ 1,1,1,1, 0,0,0,0, 1,1,1,1, 0,0,0,0,
+ 241 };
+
+static int lrand()
+{
+#if 0
+ return val[val_pos++];
+#else
+ return rand();
+#endif
+}
+
+static
+void rand(Uint32 dst[], Uint32 len)
+{
+ for(int i = 0; i<len; i++)
+ BitmaskImpl::set((len + 31) >> 5, dst, i, (lrand() % 1000) > 500);
+}
+
+static
+void simple(int pos, int size)
+{
+ ndbout_c("simple pos: %d size: %d", pos, size);
+ Vector<Uint32> _mask;
+ Vector<Uint32> _src;
+ Vector<Uint32> _dst;
+ Uint32 sz32 = (size + pos + 32) >> 5;
+ const Uint32 sz = 4 * sz32;
+
+ Uint32 zero = 0;
+ _mask.fill(sz32+1, zero);
+ _src.fill(sz32+1, zero);
+ _dst.fill(sz32+1, zero);
+
+ Uint32 * src = _src.getBase();
+ Uint32 * dst = _dst.getBase();
+ Uint32 * mask = _mask.getBase();
+
+ memset(src, 0x0, sz);
+ memset(dst, 0x0, sz);
+ memset(mask, 0xFF, sz);
+ rand(src, size);
+ BitmaskImpl::setField(sz32, mask, pos, size, src);
+ BitmaskImpl::getField(sz32, mask, pos, size, dst);
+ printf("src: "); print(src, size+31); printf("\n");
+ printf("msk: "); print(mask, (sz32 << 5) + 31); printf("\n");
+ printf("dst: "); print(dst, size+31); printf("\n");
+ require(cmp(src, dst, size+31));
+};
+
+static
+void simple2(int size, int loops)
+{
+ ndbout_c("simple2 %d - ", size);
+ Vector<Uint32> _mask;
+ Vector<Uint32> _src;
+ Vector<Uint32> _dst;
+
+ Uint32 sz32 = (size + 32) >> 5;
+ Uint32 sz = sz32 << 2;
+
+ Uint32 zero = 0;
+ _mask.fill(sz32+1, zero);
+ _src.fill(sz32+1, zero);
+ _dst.fill(sz32+1, zero);
+
+ Uint32 * src = _src.getBase();
+ Uint32 * dst = _dst.getBase();
+ Uint32 * mask = _mask.getBase();
+
+ Vector<Uint32> save;
+ for(int i = 0; i<loops; i++)
+ {
+ memset(mask, 0xFF, sz);
+ memset(dst, 0xFF, sz);
+ int len;
+ int pos = 0;
+ while(pos+1 < size)
+ {
+ memset(src, 0xFF, sz);
+ while(!(len = rand() % (size - pos)));
+ BitmaskImpl::setField(sz32, mask, pos, len, src);
+ if(memcmp(dst, mask, sz))
+ {
+ ndbout_c("pos: %d len: %d", pos, len);
+ print(mask, size);
+ abort();
+ }
+ printf("[ %d %d ]", pos, len);
+ save.push_back(pos);
+ save.push_back(len);
+ pos += len;
+ }
+
+ for(int j = 0; j<save.size(); )
+ {
+ pos = save[j++];
+ len = save[j++];
+ memset(src, 0xFF, sz);
+ BitmaskImpl::getField(sz32, mask, pos, len, src);
+ if(memcmp(dst, src, sz))
+ {
+ ndbout_c("pos: %d len: %d", pos, len);
+ printf("src: "); print(src, size); printf("\n");
+ printf("dst: "); print(dst, size); printf("\n");
+ printf("msk: "); print(mask, size); printf("\n");
+ abort();
+ }
+ }
+ ndbout_c("");
+ }
+}
+
+static void
+do_test(int bitmask_size)
+{
+#if 1
+ simple(rand() % 33, (rand() % 63)+1);
+//#else
+ Vector<Alloc> alloc_list;
+ bitmask_size = (bitmask_size + 31) & ~31;
+ Uint32 sz32 = (bitmask_size >> 5);
+ Vector<Uint32> alloc_mask;
+ Vector<Uint32> test_mask;
+
+ ndbout_c("Testing bitmask of size %d", bitmask_size);
+ Uint32 zero = 0;
+ alloc_mask.fill(sz32, zero);
+ test_mask.fill(sz32, zero);
+
+ for(int i = 0; i<5000; i++)
+ {
+ Vector<Uint32> tmp;
+ tmp.fill(sz32, zero);
+
+ int pos = lrand() % (bitmask_size - 1);
+ int free = 0;
+ if(BitmaskImpl::get(sz32, alloc_mask.getBase(), pos))
+ {
+ // Bit was allocated
+ // 1) Look up allocation
+ // 2) Check data
+ // 3) free it
+ size_t j;
+ int min, max;
+ for(j = 0; j<alloc_list.size(); j++)
+ {
+ min = alloc_list[j].pos;
+ max = min + alloc_list[j].size;
+ if(pos >= min && pos < max)
+ {
+ break;
+ }
+ }
+ require(pos >= min && pos < max);
+ BitmaskImpl::getField(sz32, test_mask.getBase(), min, max-min,
+ tmp.getBase());
+ if(DEBUG)
+ {
+ printf("freeing [ %d %d ]", min, max);
+ printf("- mask: ");
+ print(tmp.getBase(), max - min);
+
+ printf(" save: ");
+ size_t k;
+ Alloc& a = alloc_list[j];
+ for(k = 0; k<a.data.size(); k++)
+ printf("%.8x ", a.data[k]);
+ printf("\n");
+ }
+ int bytes = (max - min + 7) >> 3;
+ if(!cmp(tmp.getBase(), alloc_list[j].data.getBase(), max - min))
+ {
+ abort();
+ }
+ while(min < max)
+ BitmaskImpl::clear(sz32, alloc_mask.getBase(), min++);
+ alloc_list.erase(j);
+ }
+ else
+ {
+ Vector<Uint32> tmp;
+ tmp.fill(sz32, zero);
+
+ // Bit was free
+ // 1) Check how much space is avaiable
+ // 2) Create new allocation of lrandom size
+ // 3) Fill data with lrandom data
+ // 4) Update alloc mask
+ while(pos+free < bitmask_size &&
+ !BitmaskImpl::get(sz32, alloc_mask.getBase(), pos+free))
+ free++;
+
+ Uint32 sz =
+ (free <= 64 && ((lrand() % 100) > 80)) ? free : (lrand() % free);
+ sz = sz ? sz : 1;
+ sz = pos + sz == bitmask_size ? sz - 1 : sz;
+ Alloc a;
+ a.pos = pos;
+ a.size = sz;
+ a.data.fill(((sz+31)>> 5)-1, zero);
+ if(DEBUG)
+ printf("pos %d -> alloc [ %d %d ]", pos, pos, pos+sz);
+ for(size_t j = 0; j<sz; j++)
+ {
+ BitmaskImpl::set(sz32, alloc_mask.getBase(), pos+j);
+ if((lrand() % 1000) > 500)
+ BitmaskImpl::set((sz + 31) >> 5, a.data.getBase(), j);
+ }
+ if(DEBUG)
+ {
+ printf("- mask: ");
+ print(a.data.getBase(), sz);
+ printf("\n");
+ }
+ BitmaskImpl::setField(sz32, test_mask.getBase(), pos, sz,
+ a.data.getBase());
+ alloc_list.push_back(a);
+ }
+ }
+#endif
+}
+
+template class Vector<Alloc>;
+template class Vector<Uint32>;
+
+#endif
diff --git a/storage/ndb/src/common/util/ConfigValues.cpp b/storage/ndb/src/common/util/ConfigValues.cpp
new file mode 100644
index 00000000000..5c4b17c73ca
--- /dev/null
+++ b/storage/ndb/src/common/util/ConfigValues.cpp
@@ -0,0 +1,748 @@
+
+#include <ndb_global.h>
+#include <ConfigValues.hpp>
+#include <NdbOut.hpp>
+#include <NdbTCP.h>
+
+static Uint32 hash(Uint32 key, Uint32 size);
+static Uint32 nextHash(Uint32 key, Uint32 size, Uint32 pos, Uint32 count);
+static bool findKey(const Uint32 * vals, Uint32 sz, Uint32 key, Uint32 * pos);
+
+/**
+ * Key
+ *
+ * t = Type - 4 bits 0-15
+ * s = Section - 14 bits 0-16383
+ * k = Key value - 14 bits 0-16383
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * kkkkkkkkkkkkkkssssssssssssssoooo
+ */
+#define KP_TYPE_MASK (15)
+#define KP_TYPE_SHIFT (28)
+#define KP_SECTION_MASK (0x3FFF)
+#define KP_SECTION_SHIFT (14)
+#define KP_KEYVAL_MASK (0x3FFF)
+#define KP_KEYVAL_SHIFT (0)
+#define KP_MASK (0x0FFFFFFF)
+
+static const Uint32 CFV_KEY_PARENT = (KP_KEYVAL_MASK - 1);
+static const Uint32 CFV_KEY_FREE = ~0;
+
+static const char Magic[] = { 'N', 'D', 'B', 'C', 'O', 'N', 'F', 'V' };
+
+//#define DEBUG_CV
+#ifdef DEBUG_CV
+#define DEBUG
+#else
+#define DEBUG if(0)
+#endif
+
+inline
+ConfigValues::ValueType
+getTypeOf(Uint32 k) {
+ return (ConfigValues::ValueType)((k >> KP_TYPE_SHIFT) & KP_TYPE_MASK);
+}
+
+ConfigValues::ConfigValues(Uint32 sz, Uint32 dsz){
+ m_size = sz;
+ m_dataSize = dsz;
+ m_stringCount = 0;
+ m_int64Count = 0;
+ for(Uint32 i = 0; i<m_size; i++){
+ m_values[i << 1] = CFV_KEY_FREE;
+ }
+}
+
+ConfigValues::~ConfigValues(){
+ for(Uint32 i = 0; i<m_stringCount; i++){
+ free(* getString(i));
+ }
+}
+
+bool
+ConfigValues::ConstIterator::get(Uint32 key, Entry * result) const {
+ Uint32 pos;
+ if(!findKey(m_cfg.m_values, m_cfg.m_size, key | m_currentSection, &pos)){
+ return false;
+ }
+
+ result->m_key = key;
+ return m_cfg.getByPos(pos, result);
+}
+
+bool
+ConfigValues::getByPos(Uint32 pos, Entry * result) const {
+ assert(pos < (2 * m_size));
+ Uint32 keypart = m_values[pos];
+ Uint32 val = m_values[pos+1];
+
+ switch(::getTypeOf(keypart)){
+ case IntType:
+ case SectionType:
+ result->m_int = val;
+ break;
+ case StringType:
+ result->m_string = * getString(val);
+ break;
+ case Int64Type:
+ result->m_int64 = * get64(val);
+ break;
+ case InvalidType:
+ default:
+ return false;
+ }
+
+ result->m_type = ::getTypeOf(keypart);
+
+ return true;
+}
+
+Uint64 *
+ConfigValues::get64(Uint32 index) const {
+ assert(index < m_int64Count);
+ const Uint32 * data = m_values + (m_size << 1);
+ Uint64 * ptr = (Uint64*)data;
+ ptr += index;
+ return ptr;
+}
+
+char **
+ConfigValues::getString(Uint32 index) const {
+ assert(index < m_stringCount);
+ const Uint32 * data = m_values + (m_size << 1);
+ char * ptr = (char*)data;
+ ptr += m_dataSize;
+ ptr -= (index * sizeof(char *));
+ return (char**)ptr;
+}
+
+bool
+ConfigValues::ConstIterator::openSection(Uint32 key, Uint32 no){
+ Uint32 curr = m_currentSection;
+
+ Entry tmp;
+ if(get(key, &tmp) && tmp.m_type == SectionType){
+ m_currentSection = tmp.m_int;
+ if(get(no, &tmp) && tmp.m_type == IntType){
+ m_currentSection = tmp.m_int;
+ /**
+ * Validate
+ */
+ if(get(CFV_KEY_PARENT, &tmp)){
+ return true;
+ }
+ }
+ }
+
+ m_currentSection = curr;
+ return false;
+}
+
+bool
+ConfigValues::ConstIterator::closeSection() {
+
+ Entry tmp;
+ if(get(CFV_KEY_PARENT, &tmp) && tmp.m_type == IntType){
+ m_currentSection = tmp.m_int;
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ConfigValues::Iterator::set(Uint32 key, Uint32 value){
+ Uint32 pos;
+ if(!findKey(m_cfg.m_values, m_cfg.m_size, key | m_currentSection, &pos)){
+ return false;
+ }
+
+ if(::getTypeOf(m_cfg.m_values[pos]) != IntType){
+ return false;
+ }
+
+ m_cfg.m_values[pos+1] = value;
+ return true;
+}
+
+bool
+ConfigValues::Iterator::set(Uint32 key, Uint64 value){
+ Uint32 pos;
+ if(!findKey(m_cfg.m_values, m_cfg.m_size, key | m_currentSection, &pos)){
+ return false;
+ }
+
+ if(::getTypeOf(m_cfg.m_values[pos]) != Int64Type){
+ return false;
+ }
+
+ * m_cfg.get64(m_cfg.m_values[pos+1]) = value;
+ return true;
+}
+
+bool
+ConfigValues::Iterator::set(Uint32 key, const char * value){
+ Uint32 pos;
+ if(!findKey(m_cfg.m_values, m_cfg.m_size, key | m_currentSection, &pos)){
+ return false;
+ }
+
+ if(::getTypeOf(m_cfg.m_values[pos]) != StringType){
+ return false;
+ }
+
+ char ** str = m_cfg.getString(m_cfg.m_values[pos+1]);
+ free(* str);
+ * str = strdup(value ? value : "");
+ return true;
+}
+
+static
+bool
+findKey(const Uint32 * values, Uint32 sz, Uint32 key, Uint32 * _pos){
+ Uint32 pos = hash(key, sz);
+ Uint32 count = 0;
+ while((values[pos] & KP_MASK) != key && count < sz){
+ pos = nextHash(key, sz, pos, ++count);
+ }
+
+ if((values[pos] & KP_MASK)== key){
+ *_pos = pos;
+ return true;
+ }
+ return false;
+}
+
+static
+Uint32
+hash(Uint32 key, Uint32 size){
+ Uint32 tmp = (key >> 16) ^ (key & 0xFFFF);
+ return (((tmp << 16) | tmp) % size) << 1;
+}
+
+static
+Uint32
+nextHash(Uint32 key, Uint32 size, Uint32 pos, Uint32 count){
+ Uint32 p = (pos >> 1);
+ if((key % size) != 0)
+ p += key;
+ else
+ p += 1;
+ return (p % size) << 1;
+}
+
+static
+Uint32
+directory(Uint32 sz){
+ const Uint32 _input = sz;
+ if((sz & 1) == 0)
+ sz ++;
+
+ bool prime = false;
+ while(!prime){
+ prime = true;
+ for(Uint32 n = 3; n*n <= sz; n += 2){
+ if((sz % n) == 0){
+ prime = false;
+ sz += 2;
+ break;
+ }
+ }
+ }
+ DEBUG printf("directory %d -> %d\n", _input, sz);
+ return sz;
+}
+
+ConfigValuesFactory::ConfigValuesFactory(Uint32 keys, Uint32 data){
+ m_sectionCounter = (1 << KP_SECTION_SHIFT);
+ m_freeKeys = directory(keys);
+ m_freeData = (data + 7) & ~7;
+ m_currentSection = 0;
+ m_cfg = create(m_freeKeys, m_freeData);
+}
+
+ConfigValuesFactory::ConfigValuesFactory(ConfigValues * cfg){
+ m_cfg = cfg;
+ m_freeKeys = 0;
+ m_freeData = m_cfg->m_dataSize;
+ m_sectionCounter = (1 << KP_SECTION_SHIFT);
+ m_currentSection = 0;
+ const Uint32 sz = 2 * m_cfg->m_size;
+ for(Uint32 i = 0; i<sz; i += 2){
+ const Uint32 key = m_cfg->m_values[i];
+ if(key == CFV_KEY_FREE){
+ m_freeKeys++;
+ } else {
+ switch(::getTypeOf(key)){
+ case ConfigValues::IntType:
+ case ConfigValues::SectionType:
+ break;
+ case ConfigValues::Int64Type:
+ m_freeData -= sizeof(Uint64);
+ break;
+ case ConfigValues::StringType:
+ m_freeData -= sizeof(char *);
+ break;
+ case ConfigValues::InvalidType:
+ abort();
+ }
+ Uint32 sec = key & (KP_SECTION_MASK << KP_SECTION_SHIFT);
+ m_sectionCounter = (sec > m_sectionCounter ? sec : m_sectionCounter);
+ }
+ }
+}
+
+ConfigValues *
+ConfigValuesFactory::create(Uint32 keys, Uint32 data){
+ Uint32 sz = sizeof(ConfigValues);
+ sz += (2 * keys * sizeof(Uint32));
+ sz += data;
+
+ void * tmp = malloc(sz);
+ return new (tmp) ConfigValues(keys, data);
+}
+
+void
+ConfigValuesFactory::expand(Uint32 fk, Uint32 fs){
+ if(m_freeKeys >= fk && m_freeData >= fs){
+ return ;
+ }
+
+ m_freeKeys = (m_freeKeys >= fk ? m_cfg->m_size : fk + m_cfg->m_size);
+ m_freeData = (m_freeData >= fs ? m_cfg->m_dataSize : fs + m_cfg->m_dataSize);
+ m_freeKeys = directory(m_freeKeys);
+ m_freeData = (m_freeData + 7) & ~7;
+
+ ConfigValues * m_tmp = m_cfg;
+ m_cfg = create(m_freeKeys, m_freeData);
+ put(* m_tmp);
+ m_tmp->~ConfigValues();
+ free(m_tmp);
+}
+
+void
+ConfigValuesFactory::shrink(){
+ if(m_freeKeys == 0 && m_freeData == 0){
+ return ;
+ }
+
+ m_freeKeys = m_cfg->m_size - m_freeKeys;
+ m_freeData = m_cfg->m_dataSize - m_freeData;
+ m_freeKeys = directory(m_freeKeys);
+ m_freeData = (m_freeData + 7) & ~7;
+
+ ConfigValues * m_tmp = m_cfg;
+ m_cfg = create(m_freeKeys, m_freeData);
+ put(* m_tmp);
+ m_tmp->~ConfigValues();
+ free(m_tmp);
+}
+
+bool
+ConfigValuesFactory::openSection(Uint32 key, Uint32 no){
+ ConfigValues::Entry tmp;
+ const Uint32 parent = m_currentSection;
+
+ ConfigValues::ConstIterator iter(* m_cfg);
+ iter.m_currentSection = m_currentSection;
+ if(!iter.get(key, &tmp)){
+
+ tmp.m_key = key;
+ tmp.m_type = ConfigValues::SectionType;
+ tmp.m_int = m_sectionCounter;
+ m_sectionCounter += (1 << KP_SECTION_SHIFT);
+
+ if(!put(tmp)){
+ return false;
+ }
+ }
+
+ if(tmp.m_type != ConfigValues::SectionType){
+ return false;
+ }
+
+ m_currentSection = tmp.m_int;
+
+ tmp.m_key = no;
+ tmp.m_type = ConfigValues::IntType;
+ tmp.m_int = m_sectionCounter;
+ if(!put(tmp)){
+ m_currentSection = parent;
+ return false;
+ }
+ m_sectionCounter += (1 << KP_SECTION_SHIFT);
+
+ m_currentSection = tmp.m_int;
+ tmp.m_type = ConfigValues::IntType;
+ tmp.m_key = CFV_KEY_PARENT;
+ tmp.m_int = parent;
+ if(!put(tmp)){
+ m_currentSection = parent;
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ConfigValuesFactory::closeSection(){
+ ConfigValues::ConstIterator iter(* m_cfg);
+ iter.m_currentSection = m_currentSection;
+ const bool b = iter.closeSection();
+ m_currentSection = iter.m_currentSection;
+ return b;
+}
+
+bool
+ConfigValuesFactory::put(const ConfigValues::Entry & entry){
+
+ if(m_freeKeys == 0 ||
+ (entry.m_type == ConfigValues::StringType && m_freeData < sizeof(char *))
+ || (entry.m_type == ConfigValues::Int64Type && m_freeData < 8 )){
+
+ DEBUG ndbout_c("m_freeKeys = %d, m_freeData = %d -> expand",
+ m_freeKeys, m_freeData);
+
+ expand(31, 20);
+ }
+
+ const Uint32 tmp = entry.m_key | m_currentSection;
+ const Uint32 sz = m_cfg->m_size;
+ Uint32 pos = hash(tmp, sz);
+ Uint32 count = 0;
+ Uint32 val = m_cfg->m_values[pos];
+
+ while((val & KP_MASK) != tmp && val != CFV_KEY_FREE && count < sz){
+ pos = nextHash(tmp, sz, pos, ++count);
+ val = m_cfg->m_values[pos];
+ }
+
+ if((val & KP_MASK) == tmp){
+ DEBUG ndbout_c("key %x already found at pos: %d", tmp, pos);
+ return false;
+ }
+
+ if(count >= sz){
+ pos = hash(tmp, sz);
+ count = 0;
+ Uint32 val = m_cfg->m_values[pos];
+
+ printf("key: %d, (key %% size): %d\n", entry.m_key, (entry.m_key % sz));
+ printf("pos: %d", pos);
+ while((val & KP_MASK) != tmp && val != CFV_KEY_FREE && count < sz){
+ pos = nextHash(tmp, sz, pos, ++count);
+ val = m_cfg->m_values[pos];
+ printf(" %d", pos);
+ }
+ printf("\n");
+
+ abort();
+ printf("Full\n");
+ return false;
+ }
+
+ assert(pos < (sz << 1));
+
+ Uint32 key = tmp;
+ key |= (entry.m_type << KP_TYPE_SHIFT);
+ m_cfg->m_values[pos] = key;
+ switch(entry.m_type){
+ case ConfigValues::IntType:
+ case ConfigValues::SectionType:
+ m_cfg->m_values[pos+1] = entry.m_int;
+ m_freeKeys--;
+ DEBUG printf("Putting at: %d(%d) (loop = %d) key: %d value: %d\n",
+ pos, sz, count,
+ (key >> KP_KEYVAL_SHIFT) & KP_KEYVAL_MASK,
+ entry.m_int);
+ return true;
+ case ConfigValues::StringType:{
+ Uint32 index = m_cfg->m_stringCount++;
+ m_cfg->m_values[pos+1] = index;
+ char ** ref = m_cfg->getString(index);
+ * ref = strdup(entry.m_string ? entry.m_string : "");
+ m_freeKeys--;
+ m_freeData -= sizeof(char *);
+ DEBUG printf("Putting at: %d(%d) (loop = %d) key: %d value(%d): %s\n",
+ pos, sz, count,
+ (key >> KP_KEYVAL_SHIFT) & KP_KEYVAL_MASK,
+ index,
+ entry.m_string);
+ return true;
+ }
+ case ConfigValues::Int64Type:{
+ Uint32 index = m_cfg->m_int64Count++;
+ m_cfg->m_values[pos+1] = index;
+ * m_cfg->get64(index) = entry.m_int64;
+ m_freeKeys--;
+ m_freeData -= 8;
+ DEBUG printf("Putting at: %d(%d) (loop = %d) key: %d value64(%d): %lld\n",
+ pos, sz, count,
+ (key >> KP_KEYVAL_SHIFT) & KP_KEYVAL_MASK,
+ index,
+ entry.m_int64);
+ return true;
+ }
+ case ConfigValues::InvalidType:
+ default:
+ return false;
+ }
+ return false;
+}
+
+void
+ConfigValuesFactory::put(const ConfigValues & cfg){
+
+ Uint32 curr = m_currentSection;
+ m_currentSection = 0;
+
+ ConfigValues::Entry tmp;
+ for(Uint32 i = 0; i < 2 * cfg.m_size; i += 2){
+ if(cfg.m_values[i] != CFV_KEY_FREE){
+ tmp.m_key = cfg.m_values[i];
+ cfg.getByPos(i, &tmp);
+ put(tmp);
+ }
+ }
+
+ m_currentSection = curr;
+}
+
+ConfigValues *
+ConfigValuesFactory::extractCurrentSection(const ConfigValues::ConstIterator & cfg){
+ ConfigValuesFactory * fac = new ConfigValuesFactory(20, 20);
+ Uint32 curr = cfg.m_currentSection;
+
+ ConfigValues::Entry tmp;
+ for(Uint32 i = 0; i < 2 * cfg.m_cfg.m_size; i += 2){
+ Uint32 keypart = cfg.m_cfg.m_values[i];
+ const Uint32 sec = keypart & (KP_SECTION_MASK << KP_SECTION_SHIFT);
+ const Uint32 key = keypart & KP_KEYVAL_MASK;
+ if(sec == curr && key != CFV_KEY_PARENT){
+ tmp.m_key = cfg.m_cfg.m_values[i];
+ cfg.m_cfg.getByPos(i, &tmp);
+ tmp.m_key = key;
+ fac->put(tmp);
+ }
+ }
+
+ ConfigValues * ret = fac->m_cfg;
+ delete fac;
+ return ret;
+}
+
+ConfigValues *
+ConfigValuesFactory::getConfigValues(){
+ ConfigValues * ret = m_cfg;
+ m_cfg = create(10, 10);
+ return ret;
+}
+
+static int
+mod4(unsigned int i){
+ int res = i + (4 - (i % 4));
+ return res;
+}
+
+Uint32
+ConfigValues::getPackedSize() const {
+
+ Uint32 size = 0;
+ for(Uint32 i = 0; i < 2 * m_size; i += 2){
+ Uint32 key = m_values[i];
+ if(key != CFV_KEY_FREE){
+ switch(::getTypeOf(key)){
+ case IntType:
+ case SectionType:
+ size += 8;
+ break;
+ case Int64Type:
+ size += 12;
+ break;
+ case StringType:
+ size += 8; // key + len
+ size += mod4(strlen(* getString(m_values[i+1])) + 1);
+ break;
+ case InvalidType:
+ default:
+ abort();
+ }
+ }
+ }
+
+ return size + sizeof(Magic) + 4; // checksum also
+}
+
+Uint32
+ConfigValues::pack(void * _dst, Uint32 _len) const {
+ Uint32 i;
+ char * dst = (char*)_dst;
+ memcpy(dst, Magic, sizeof(Magic)); dst += sizeof(Magic);
+
+ for(i = 0; i < 2 * m_size; i += 2){
+ Uint32 key = m_values[i];
+ Uint32 val = m_values[i+1];
+ if(key != CFV_KEY_FREE){
+ switch(::getTypeOf(key)){
+ case IntType:
+ case SectionType:
+ * (Uint32*)dst = htonl(key); dst += 4;
+ * (Uint32*)dst = htonl(val); dst += 4;
+ break;
+ case Int64Type:{
+ Uint64 i64 = * get64(val);
+ Uint32 hi = (i64 >> 32);
+ Uint32 lo = (i64 & 0xFFFFFFFF);
+ * (Uint32*)dst = htonl(key); dst += 4;
+ * (Uint32*)dst = htonl(hi); dst += 4;
+ * (Uint32*)dst = htonl(lo); dst += 4;
+ }
+ break;
+ case StringType:{
+ const char * str = * getString(val);
+ Uint32 len = strlen(str) + 1;
+ * (Uint32*)dst = htonl(key); dst += 4;
+ * (Uint32*)dst = htonl(len); dst += 4;
+ memcpy(dst, str, len);
+ memset(dst+len, 0, mod4(len) - len);
+ dst += mod4(len);
+ }
+ break;
+ case InvalidType:
+ default:
+ abort();
+ }
+ }
+ }
+
+ const Uint32 * sum = (Uint32*)_dst;
+ const Uint32 len = ((Uint32*)dst) - sum;
+ Uint32 chk = 0;
+ for(i = 0; i<len; i++){
+ chk ^= htonl(sum[i]);
+ }
+
+ * (Uint32*)dst = htonl(chk); dst += 4;
+ return 4 * (len + 1);
+}
+
+bool
+ConfigValuesFactory::unpack(const void * _src, Uint32 len){
+
+ if(len < sizeof(Magic) + 4){
+ DEBUG abort();
+ return false;
+ }
+
+ if(memcmp(_src, Magic, sizeof(Magic)) != 0){
+ DEBUG abort();
+ return false;
+ }
+
+ const char * src = (const char *)_src;
+
+ {
+ Uint32 len32 = (len >> 2);
+ const Uint32 * tmp = (const Uint32*)_src;
+ Uint32 chk = 0;
+ for(Uint32 i = 0; (i+1)<len32; i++){
+ chk ^= ntohl(tmp[i]);
+ }
+
+ if(chk != ntohl(tmp[len32-1])){
+ DEBUG abort();
+ return false;
+ }
+ }
+
+ const char * end = src + len - 4;
+ src += sizeof(Magic);
+
+ ConfigValues::Entry entry;
+ while(end - src > 4){
+ Uint32 tmp = ntohl(* (const Uint32 *)src); src += 4;
+ entry.m_key = tmp & KP_MASK;
+ entry.m_type = ::getTypeOf(tmp);
+ switch(entry.m_type){
+ case ConfigValues::IntType:
+ case ConfigValues::SectionType:
+ entry.m_int = ntohl(* (const Uint32 *)src); src += 4;
+ break;
+ case ConfigValues::Int64Type:{
+ Uint64 hi = ntohl(* (const Uint32 *)src); src += 4;
+ Uint64 lo = ntohl(* (const Uint32 *)src); src += 4;
+ entry.m_int64 = (hi <<32) | lo;
+ }
+ break;
+ case ConfigValues::StringType:{
+ Uint32 s_len = ntohl(* (const Uint32 *)src); src += 4;
+ size_t s_len2 = strlen((const char*)src);
+ if(s_len2 + 1 != s_len){
+ DEBUG abort();
+ return false;
+ }
+
+ entry.m_string = (const char*)src; src+= mod4(s_len);
+ }
+ break;
+ case ConfigValues::InvalidType:
+ default:
+ DEBUG abort();
+ return false;
+ }
+ if(!put(entry)){
+ DEBUG abort();
+ return false;
+ }
+ }
+ if(src != end){
+ DEBUG abort();
+ return false;
+ }
+ return true;
+}
+
+#ifdef __TEST_CV_HASH_HPP
+
+int
+main(void){
+ srand(time(0));
+ for(int t = 0; t<100; t++){
+ const size_t len = directory(rand() % 1000);
+
+ printf("size = %d\n", len);
+ unsigned * buf = new unsigned[len];
+ for(size_t key = 0; key<len; key++){
+ Uint32 p = hash(key, len);
+ for(size_t j = 0; j<len; j++){
+ buf[j] = p;
+ p = nextHash(key, len, p, j+1);
+ }
+
+ for(size_t j = 0; j<len; j++){
+ Uint32 pos = buf[j];
+ int unique = 0;
+ for(size_t k = j + 1; k<len; k++){
+ if(pos == buf[k]){
+ if(unique > 0)
+ printf("size=%d key=%d pos(%d)=%d buf[%d]=%d\n", len, key, j, pos, k, buf[k]);
+ unique ++;
+ }
+ }
+ if(unique > 1){
+ printf("key = %d size = %d not uniqe!!\n", key, len);
+ for(size_t k = 0; k<len; k++){
+ printf("%d ", buf[k]);
+ }
+ printf("\n");
+ }
+ }
+ }
+ delete[] buf;
+ }
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/common/util/File.cpp b/storage/ndb/src/common/util/File.cpp
new file mode 100644
index 00000000000..937b8c0fa59
--- /dev/null
+++ b/storage/ndb/src/common/util/File.cpp
@@ -0,0 +1,205 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <File.hpp>
+
+#include <NdbOut.hpp>
+#include <my_dir.h>
+
+//
+// PUBLIC
+//
+
+bool
+File_class::exists(const char* aFileName)
+{
+ bool rc = true;
+#ifdef USE_MY_STAT_STRUCT
+ struct my_stat stmp;
+#else
+ struct stat stmp;
+#endif
+ if (my_stat(aFileName, &stmp, MYF(0)) != 0)
+ {
+ rc = false;
+ }
+
+ /*
+ File f;
+ if (!f.open(aFileName, "r"))
+ {
+ rc = (errno == ENOENT ? false : true);
+ }
+ else
+ {
+ f.close();
+ }
+ */
+ return rc;
+}
+
+long
+File_class::size(FILE* f)
+{
+ long cur_pos = 0, length = 0;
+
+ cur_pos = ::ftell(f);
+ ::fseek(f, 0, SEEK_END);
+ length = ::ftell(f);
+ ::fseek(f, cur_pos, SEEK_SET); // restore original position
+
+ return length;
+}
+
+bool
+File_class::rename(const char* currFileName, const char* newFileName)
+{
+ return ::rename(currFileName, newFileName) == 0 ? true : false;
+}
+bool
+File_class::remove(const char* aFileName)
+{
+ return ::remove(aFileName) == 0 ? true : false;
+}
+
+File_class::File_class() :
+ m_file(NULL),
+ m_fileMode("r")
+{
+}
+
+File_class::File_class(const char* aFileName, const char* mode) :
+ m_file(NULL),
+ m_fileMode(mode)
+{
+ BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName);
+}
+
+bool
+File_class::open()
+{
+ return open(m_fileName, m_fileMode);
+}
+
+bool
+File_class::open(const char* aFileName, const char* mode)
+{
+ if(m_fileName != aFileName){
+ /**
+ * Only copy if it's not the same string
+ */
+ BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName);
+ }
+ m_fileMode = mode;
+ bool rc = true;
+ if ((m_file = ::fopen(m_fileName, m_fileMode))== NULL)
+ {
+ rc = false;
+ }
+
+ return rc;
+}
+File_class::~File_class()
+{
+ close();
+}
+
+bool
+File_class::remove()
+{
+ // Close the file first!
+ close();
+ return File_class::remove(m_fileName);
+}
+
+bool
+File_class::close()
+{
+ bool rc = true;
+ if (m_file != NULL)
+ {
+ ::fflush(m_file);
+ rc = (::fclose(m_file) == 0 ? true : false);
+ m_file = NULL; // Try again?
+ }
+
+ return rc;
+}
+
+int
+File_class::read(void* buf, size_t itemSize, size_t nitems) const
+{
+ return ::fread(buf, itemSize, nitems, m_file);
+}
+
+int
+File_class::readChar(char* buf, long start, long length) const
+{
+ return ::fread((void*)&buf[start], 1, length, m_file);
+}
+
+int
+File_class::readChar(char* buf)
+{
+ return readChar(buf, 0, strlen(buf));
+}
+
+int
+File_class::write(const void* buf, size_t size, size_t nitems)
+{
+ return ::fwrite(buf, size, nitems, m_file);
+}
+
+int
+File_class::writeChar(const char* buf, long start, long length)
+{
+ return ::fwrite((const void*)&buf[start], sizeof(char), length, m_file);
+}
+
+int
+File_class::writeChar(const char* buf)
+{
+ return writeChar(buf, 0, ::strlen(buf));
+}
+
+long
+File_class::size() const
+{
+ return File_class::size(m_file);
+}
+
+const char*
+File_class::getName() const
+{
+ return m_fileName;
+}
+
+int
+File_class::flush() const
+{
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ ::fflush(m_file);
+ return ::fsync(::fileno(m_file));
+#else
+ return 0;
+#endif
+}
+
+//
+// PRIVATE
+//
diff --git a/storage/ndb/src/common/util/InputStream.cpp b/storage/ndb/src/common/util/InputStream.cpp
new file mode 100644
index 00000000000..410e9a70e9c
--- /dev/null
+++ b/storage/ndb/src/common/util/InputStream.cpp
@@ -0,0 +1,61 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "InputStream.hpp"
+#include <socket_io.h>
+
+FileInputStream Stdin(stdin);
+
+FileInputStream::FileInputStream(FILE * file)
+ : f(file) {
+}
+
+char*
+FileInputStream::gets(char * buf, int bufLen){
+ if(!feof(f)){
+ return fgets(buf, bufLen, f);
+ }
+ return 0;
+}
+
+SocketInputStream::SocketInputStream(NDB_SOCKET_TYPE socket,
+ unsigned readTimeout)
+ : m_socket(socket) {
+ m_timeout = readTimeout;
+}
+
+char*
+SocketInputStream::gets(char * buf, int bufLen) {
+ buf[0] = 77;
+ assert(bufLen >= 2);
+ int res = readln_socket(m_socket, m_timeout, buf, bufLen - 1);
+ if(res == -1)
+ return 0;
+ if(res == 0 && buf[0] == 77){ // select return 0
+ buf[0] = 0;
+ } else if(res == 0 && buf[0] == 0){ // only newline
+ buf[0] = '\n';
+ buf[1] = 0;
+ } else {
+ int len = strlen(buf);
+ buf[len + 1] = '\0';
+ buf[len] = '\n';
+ }
+ return buf;
+}
diff --git a/storage/ndb/src/common/util/Makefile.am b/storage/ndb/src/common/util/Makefile.am
new file mode 100644
index 00000000000..2719d14ee92
--- /dev/null
+++ b/storage/ndb/src/common/util/Makefile.am
@@ -0,0 +1,49 @@
+
+noinst_LTLIBRARIES = libgeneral.la
+
+libgeneral_la_SOURCES = \
+ File.cpp md5_hash.cpp Properties.cpp socket_io.cpp \
+ SimpleProperties.cpp Parser.cpp InputStream.cpp \
+ SocketServer.cpp SocketClient.cpp SocketAuthenticator.cpp\
+ OutputStream.cpp NdbOut.cpp BaseString.cpp Base64.cpp \
+ NdbSqlUtil.cpp new.cpp \
+ uucode.c random.c version.c \
+ strdup.c \
+ ConfigValues.cpp ndb_init.c basestring_vsnprintf.c \
+ Bitmask.cpp
+
+EXTRA_PROGRAMS = testBitmask
+testBitmask_SOURCES = testBitmask.cpp
+testBitmask_LDFLAGS = @ndb_bin_am_ldflags@ \
+ $(top_builddir)/ndb/src/libndbclient.la \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/strings/libmystrings.a
+
+testBitmask.cpp : Bitmask.cpp
+ rm -f testBitmask.cpp
+ @LN_CP_F@ Bitmask.cpp testBitmask.cpp
+
+testBitmask.o: $(testBitmask_SOURCES)
+ $(CXXCOMPILE) -c $(INCLUDES) -D__TEST_BITMASK__ $<
+
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_util.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libgeneral.dsp
+
+libgeneral.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libgeneral_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/common/util/NdbErrHnd.cpp b/storage/ndb/src/common/util/NdbErrHnd.cpp
new file mode 100644
index 00000000000..38a67f29853
--- /dev/null
+++ b/storage/ndb/src/common/util/NdbErrHnd.cpp
@@ -0,0 +1,492 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+
+#include <NdbOut.hpp>
+#include <ndb_types.h>
+
+#include "ose.h"
+#include "ose_err.h"
+#include "osetypes.h"
+
+
+#define BUFSIZE 100
+
+typedef struct {
+ char header1[BUFSIZE];
+ char header2[BUFSIZE];
+ char error_code_line[BUFSIZE];
+ char subcode_line[BUFSIZE];
+ char product_line[BUFSIZE];
+ char header_file_line[BUFSIZE];
+ char extra_line[BUFSIZE];
+ char user_called_line[BUFSIZE];
+ char current_process_id_line[BUFSIZE];
+ char current_process_name_line[BUFSIZE];
+ char file_line[BUFSIZE];
+ char line_line[BUFSIZE];
+ char err_hnd_file[BUFSIZE];
+} Error_message;
+
+char assert_line[BUFSIZE];
+char unknown_signal_line[BUFSIZE];
+char signal_number_line[BUFSIZE];
+char sender_line[BUFSIZE];
+char receiver_line[BUFSIZE];
+
+extern "C" OSBOOLEAN ndb_err_hnd(bool user_called,
+ Uint32 error_code,
+ Uint32 extra)
+{
+ static Error_message error_message;
+ bool error_handled;
+ Uint32 subcode;
+
+ char* subcode_mnemonic;
+ char* product_name;
+ char* file_name;
+
+ /*The subcode (bit 16 - 30) is extracted from error_code */
+ subcode = (error_code & 0x7fff0000) >> 16;
+
+ if (user_called) {
+ switch (subcode) {
+ case 0x0050 :
+ subcode_mnemonic= "OSE_PRH_PLS";
+ product_name= "Program Loader";
+ file_name = "prherr.h";
+ break;
+ case 0x0051 :
+ subcode_mnemonic = "OSE_PRH_START_PRH";
+ product_name= "start_prh";
+ file_name= " start_prh.c";
+ break;
+ case 0x0052 :
+ subcode_mnemonic= "OSE_PRH_ASF";
+ product_name= "Archive Server";
+ file_name = "prherr.h";
+ break;
+ case 0x0058 :
+ case 0x4058 :
+ case 0x3fff :
+ case 0x8058 :
+ subcode_mnemonic= "OSE_MMS_EBASE";
+ product_name= "MMS";
+ file_name= "mms_err.h";
+ break;
+ /*Link Handler G3***************************************/
+ case 0x0060 :
+ case 0x8060 :
+ subcode_mnemonic= "OSE_GLH_EBASE";
+ product_name= "General Link Handler";
+ file_name= "glherr.h";
+ break;
+ case 0x0064 :
+ case 0x8064 :
+ subcode_mnemonic= "OSE_GPL_EBASE";
+ product_name= "General Protocol Link Handler";
+ file_name= "gplerr.h";
+ break;
+ case 0x0066 :
+ case 0x8066 :
+ subcode_mnemonic= "OSE_UDPPDR_EBASE";
+ product_name= "UDP driver for GPL";
+ file_name= "udppdrerr.h";
+ break;
+ case 0x0067 :
+ case 0x8067 :
+ subcode_mnemonic= "OSE_SERPDR_EBASE";
+ product_name= "Serial driver for GPL";
+ file_name= "serpdrerr.h";
+ break;
+ case 0x0068 :
+ case 0x8068 :
+ subcode_mnemonic= "OSE_ETHPDR_EBASE";
+ product_name= "Ethernet driver for GPL";
+ file_name= "ethpdrerr.h";
+ break;
+ /*Link handler G4***************************************/
+ case 0x0061 :
+ subcode_mnemonic= "OSE_OTL_EBASE";
+ product_name= "OSE Transport Layer";
+ file_name= "otlerr.h";
+ break;
+ case 0x0062 :
+ subcode_mnemonic= "OSE_LALUDP_EBASE";
+ product_name= "Link Adaption Layer for UDP";
+ file_name= "header file unknown";
+ break;
+ /*Internet Utilities************************************/
+ case 0x0069 :
+ subcode_mnemonic= "OSE_TFTPD";
+ product_name= "TFTP server";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x006a :
+ subcode_mnemonic= "OSE_TELUDPD";
+ product_name= "TELNET/UDP server";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x006b :
+ subcode_mnemonic= "OSE_FTPD";
+ product_name= "FTP server";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x006c :
+ subcode_mnemonic= "OSE_TELNETD";
+ product_name= "TELNET server";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x006d :
+ subcode_mnemonic= "OSE_SURFER";
+ product_name= "OSE System Surfer";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x006e :
+ subcode_mnemonic= "OSE_BOOTP";
+ product_name= "BOOTP client";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x006f :
+ switch((error_code & 0x0000f000)){
+ case 0x00000000 :
+ subcode_mnemonic= "OSE_RES";
+ product_name= "DNS resolver";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x00001000 :
+ subcode_mnemonic= "OSE_DHCPC";
+ product_name= "DHCP client";
+ file_name= "inetutilerr.h";
+ break;
+ case 0x00002000 :
+ subcode_mnemonic= "OSE_FTP";
+ product_name= "FTP client";
+ file_name= "inetutilerr.h";
+ break;
+ default :
+ subcode_mnemonic= "Unknown error";
+ product_name= "unknown product";
+ file_name = "header file unknown";
+ break;
+ }
+ break;
+ case 0x00c2 :
+ subcode_mnemonic= "OSE_DNS";
+ product_name= "DNS server";
+ file_name= "dns_err.h";
+ break;
+ /*INET**************************/
+ case 0x0070 :
+ subcode_mnemonic= "INET_ERRBASE";
+ product_name= "Internet Protocols (INET)";
+ file_name= "ineterr.h";
+ break;
+ case 0x0071 :
+ subcode_mnemonic= "WEBS_ERRBASE";
+ product_name= "Web Server (WEBS)";
+ file_name= "webserr.h";
+ break;
+ case 0x0072 :
+ subcode_mnemonic= "SNMP";
+ product_name= "SNMP";
+ file_name= "header file unknown";
+ break;
+ case 0x0073 :
+ subcode_mnemonic= "STP_BRIDGE";
+ product_name= "STP bridge";
+ file_name= "header file unknown";
+ break;
+ case 0x0200 :
+ case 0x0201 :
+ case 0x0202 :
+ case 0x0203 :
+ case 0x0204 :
+ case 0x0205 :
+ case 0x0206 :
+ case 0x0207 :
+ case 0x0208 :
+ case 0x0209 :
+ case 0x020a :
+ case 0x020b :
+ case 0x020c :
+ case 0x020d :
+ case 0x020e :
+ case 0x020f :
+ subcode_mnemonic = "INETINIT_ERR_BASE";
+ product_name = "INET";
+ file_name = "startinet.c";
+ break;
+ /*Miscellanous******************************************/
+ case 0x0082 :
+ subcode_mnemonic= "OSE_HEAP_EBASE";
+ product_name= "Heap Manager";
+ file_name= "heap_err.h";
+ break;
+ case 0x0088 :
+ subcode_mnemonic= "OSE_BSP";
+ product_name= "Board Support Package";
+ file_name= "bsperr.h";
+ break;
+ case 0x008a :
+ subcode_mnemonic= "OSE_TOSV_EBASE";
+ product_name= "Time Out Server";
+ file_name= "tosverr.h";
+ break;
+ case 0x008b :
+ subcode_mnemonic= "OSE_RTC_EBASE";
+ product_name= "Real Time Clock";
+ file_name= "rtcerr.h";
+ break;
+ case 0x008d :
+ case 0x808d :
+ subcode_mnemonic= "OSENS_ERR_BASE";
+ product_name= "Name Server";
+ file_name= "osens_err.h";
+ break;
+ case 0x008e :
+ subcode_mnemonic= "PMD_ERR_BASE";
+ product_name= "Post Mortem Dump";
+ file_name= "pmderr.h";
+ break;
+ /*Embedded File System***********************************/
+ case 0x0090 :
+ subcode_mnemonic= "OSE_EFS_COMMON";
+ product_name= "EFS common";
+ file_name= "efs_err.h";
+ break;
+ case 0x0091 :
+ subcode_mnemonic= "OSE_EFS_FLIB";
+ product_name= "EFS function library";
+ file_name= "efs_err.h";
+ break;
+ case 0x0092 :
+ subcode_mnemonic= "OSE_EFS_SERDD";
+ product_name= "EFS serdd";
+ file_name= "efs_err.h";
+ break;
+ case 0x0093 :
+ subcode_mnemonic= "OSE_EFS_SHELL";
+ product_name= "OSE shell";
+ file_name= "efs_err.h";
+ break;
+ case 0x0094 :
+ subcode_mnemonic= "OSE_EFS_STARTEFS";
+ product_name= "EFS startefs.c";
+ file_name= "efs_err.h";
+ break;
+ /*Debugger related***************************************/
+ case 0x00a0 :
+ subcode_mnemonic= "DBGSERVER_ERR_BASE";
+ product_name= "Debug server for Illuminator";
+ file_name= "degservererr.h";
+ break;
+ case 0x00b2 :
+ subcode_mnemonic= "OSE_MDM";
+ product_name= "Multi INDRT monitor";
+ file_name= "header file unknown";
+ break;
+ /*Miscellanous*******************************************/
+ case 0x00c0 :
+ subcode_mnemonic= "OSE_POTS_EBASE";
+ product_name= "POTS tutorial example";
+ file_name= "pots_err.h";
+ break;
+ case 0x00c1 :
+ subcode_mnemonic= "OSE_PTH_ECODE_BASE";
+ product_name= "Pthreads";
+ file_name= "pthread_err.h";
+ break;
+ case 0x00c3 :
+ subcode_mnemonic= "OSE_NTP_EBASE";
+ product_name= "OSE NTP/SNTP";
+ file_name= "ntp_err.h";
+ break;
+ case 0x00c4 :
+ subcode_mnemonic= "TRILLIUM_BASE";
+ product_name= "Trillium OSE port";
+ file_name= "sk_ss.c";
+ break;
+ case 0x00c5 :
+ subcode_mnemonic= "OSE_OSECPP_EBASE";
+ product_name= "C++ Support with libosecpp.a";
+ file_name= "cpp_err.h";
+ break;
+ case 0x00c6 :
+ subcode_mnemonic= "OSE_RIP_ERR_BASE";
+ product_name= "OSE RIP";
+ file_name= "oserip.h";
+ break;
+ /*Unknown error_code*************************************/
+ default :
+ subcode_mnemonic= "Unknown error";
+ product_name= "unknown product";
+ file_name = "header file unknown";
+ break;
+ }
+ } else {
+ /* user_called = 0, i.e. reported by the kernel */
+ subcode_mnemonic= "OSE_KRN";
+ product_name= "Kernel";
+ file_name = "ose_err.h";
+ }
+
+ BaseString::snprintf(error_message.header1,
+ BUFSIZE,
+ "This is the OSE Example System Error handler\r\n");
+
+ BaseString::snprintf(error_message.err_hnd_file,
+ BUFSIZE,
+ "located in: " __FILE__ "\r\n");
+
+ BaseString::snprintf(error_message.header2,
+ BUFSIZE,
+ "An Error has been reported:\r\n");
+
+ if (user_called == (OSBOOLEAN) 0 ) {
+ BaseString::snprintf(error_message.user_called_line,
+ BUFSIZE,
+ "user_called: 0x%x (Error detected by the kernel)\r\n",
+ user_called);
+ }
+ else {
+ BaseString::snprintf(error_message.user_called_line,
+ BUFSIZE,
+ "user_called: 0x%x (Error detected by an application)\r\n",
+ user_called);
+ }
+
+ BaseString::snprintf(error_message.error_code_line,
+ BUFSIZE,
+ "error code: 0x%08x\r\n",
+ error_code);
+
+ BaseString::snprintf(error_message.subcode_line,
+ BUFSIZE,
+ " subcode: %s (0x%08x)\r\n",
+ subcode_mnemonic,
+ ( subcode << 16));
+
+ BaseString::snprintf(error_message.product_line,
+ BUFSIZE,
+ " product: %s\r\n",
+ product_name);
+
+ BaseString::snprintf(error_message.header_file_line,
+ BUFSIZE,
+ " header file: %s\r\n",
+ file_name);
+
+ BaseString::snprintf(error_message.extra_line,
+ BUFSIZE,
+ "extra: 0x%08x\r\n",
+ extra);
+
+ if (error_code != OSE_ENO_KERN_SPACE || user_called){
+ struct OS_pcb *pcb = get_pcb(current_process());
+ const char *process_name = &pcb->strings[pcb->name];
+
+ BaseString::snprintf(error_message.current_process_id_line,
+ BUFSIZE,
+ "Current Process: 0x%08x\r\n",
+ current_process());
+
+ BaseString::snprintf(error_message.current_process_name_line,
+ BUFSIZE,
+ "Process Name: %s\r\n",
+ process_name);
+
+ BaseString::snprintf(error_message.file_line,
+ BUFSIZE,
+ "File: %s\r\n",
+ &pcb->strings[pcb->file]);
+
+ BaseString::snprintf(error_message.line_line,
+ BUFSIZE,
+ "Line: %d\r\n",
+ pcb->line);
+
+ free_buf((union SIGNAL **)&pcb);
+ }
+
+ if ( !(((error_code & OSE_EFATAL_MASK) != 0) && (user_called == 0))){
+ /* If the error is reported by the kernel and the fatal flag is set,
+ * dbgprintf can't be trusted */
+ ndbout << error_message.header1;
+ ndbout << error_message.err_hnd_file;
+ ndbout << error_message.header2;
+ ndbout << error_message.user_called_line;
+ ndbout << error_message.error_code_line;
+ ndbout << error_message.subcode_line;
+ ndbout << error_message.product_line;
+ ndbout << error_message.header_file_line;
+ ndbout << error_message.extra_line;
+ ndbout << error_message.current_process_id_line;
+ ndbout << error_message.current_process_name_line;
+ ndbout << error_message.file_line;
+ ndbout << error_message.line_line;
+ ndbout << endl;
+ }
+
+ if(user_called){
+ switch (error_code) {
+ /* Check for assertion failure (see oseassert.h and assert.c). */
+ case (OSERRCODE) 0xffffffff:
+ {
+ if(extra != 0){
+ char *expr = ((char **)extra)[0];
+ char *file = ((char **)extra)[1];
+ unsigned line = ((unsigned *)extra)[2];
+ BaseString::snprintf(assert_line, BUFSIZE, "Assertion Failed: %s:%u: %s\r\n", file, line, expr);
+ ndbout << assert_line;
+ }
+ }
+ /* Check for unknown signal */
+ case (OSERRCODE) 0xfffffffe:
+ {
+ union SIGNAL *sig = (union SIGNAL *)extra;
+ SIGSELECT signo = *(SIGSELECT*)sig;
+ PROCESS rcv_ = current_process();
+ PROCESS snd_ = sender(&sig);
+ struct OS_pcb *rcv = get_pcb(rcv_);
+ const char *rcv_name = &rcv->strings[rcv->name];
+ struct OS_pcb *snd = get_pcb(snd_);
+ const char *snd_name = &snd->strings[snd->name];
+ BaseString::snprintf(unknown_signal_line, BUFSIZE,
+ "Unknown Signal Received\r\n");
+ BaseString::snprintf(unknown_signal_line, BUFSIZE,
+ "Signal Number: 0x%08lx\r\n", signo);
+ BaseString::snprintf(unknown_signal_line, BUFSIZE,
+ "Sending Process: 0x%08lx (%s))\r\n", snd_, snd_name);
+ BaseString::snprintf(unknown_signal_line, BUFSIZE,
+ "Receiving Process: 0x%08lx (%s))\r\n", rcv_, rcv_name);
+ free_buf((union SIGNAL **)&rcv);
+ free_buf((union SIGNAL **)&snd); }
+ ndbout << unknown_signal_line;
+ ndbout << signal_number_line;
+ ndbout << sender_line;
+ ndbout << receiver_line;
+ } /* switch */
+ } /* if */
+
+ /* Zero means the error has not been fixed by the error handler. */
+ error_handled = 0;
+ return error_handled;
+}
+
+#endif
diff --git a/storage/ndb/src/common/util/NdbOut.cpp b/storage/ndb/src/common/util/NdbOut.cpp
new file mode 100644
index 00000000000..e20119a7987
--- /dev/null
+++ b/storage/ndb/src/common/util/NdbOut.cpp
@@ -0,0 +1,173 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbOut.hpp>
+#include <OutputStream.hpp>
+
+static FileOutputStream ndbouts_fileoutputstream(stdout);
+NdbOut ndbout(ndbouts_fileoutputstream);
+
+static const char * fms[] = {
+ "%d", "0x%02x", // Int8
+ "%u", "0x%02x", // Uint8
+ "%d", "0x%04x", // Int16
+ "%u", "0x%04x", // Uint16
+ "%d", "0x%08x", // Int32
+ "%u", "0x%08x", // Uint32
+ "%lld", "0x%016llx", // Int64
+ "%llu", "0x%016llx" // Uint64
+ "%llu", "0x%016llx" // UintPtr
+};
+
+NdbOut&
+NdbOut::operator<<(Int8 v) { m_out->print(fms[0+isHex],(int)v); return *this;}
+NdbOut&
+NdbOut::operator<<(Uint8 v) { m_out->print(fms[2+isHex],(int)v); return *this;}
+NdbOut&
+NdbOut::operator<<(Int16 v) { m_out->print(fms[4+isHex],(int)v); return *this;}
+NdbOut&
+NdbOut::operator<<(Uint16 v) { m_out->print(fms[6+isHex],(int)v); return *this;}
+NdbOut&
+NdbOut::operator<<(Int32 v) { m_out->print(fms[8+isHex], v); return *this;}
+NdbOut&
+NdbOut::operator<<(Uint32 v) { m_out->print(fms[10+isHex], v); return *this;}
+NdbOut&
+NdbOut::operator<<(Int64 v) { m_out->print(fms[12+isHex], v); return *this;}
+NdbOut&
+NdbOut::operator<<(Uint64 v) { m_out->print(fms[14+isHex], v); return *this;}
+NdbOut&
+NdbOut::operator<<(unsigned long int v) { return *this << (Uint64) v; }
+
+NdbOut&
+NdbOut::operator<<(const char* val){ m_out->print("%s", val ? val : "(null)"); return * this; }
+NdbOut&
+NdbOut::operator<<(const void* val){ m_out->print("%p", val); return * this; }
+NdbOut&
+NdbOut::operator<<(BaseString &val){ return *this << val.c_str(); }
+
+NdbOut&
+NdbOut::operator<<(float val){ m_out->print("%f", (double)val); return * this;}
+NdbOut&
+NdbOut::operator<<(double val){ m_out->print("%f", val); return * this; }
+
+NdbOut& NdbOut::endline()
+{
+ isHex = 0; // Reset hex to normal, if user forgot this
+ m_out->println("");
+ m_out->flush();
+ return *this;
+}
+
+NdbOut& NdbOut::flushline()
+{
+ m_out->flush();
+ return *this;
+}
+
+NdbOut& NdbOut::setHexFormat(int _format)
+{
+ isHex = (_format == 0 ? 0 : 1);
+ return *this;
+}
+
+NdbOut::NdbOut(OutputStream & out)
+ : m_out(& out)
+{
+ isHex = 0;
+}
+
+NdbOut::~NdbOut()
+{
+}
+
+void
+NdbOut::print(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf;
+ va_end(ap);
+}
+
+void
+NdbOut::println(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf << endl;
+ va_end(ap);
+}
+
+extern "C"
+void
+ndbout_c(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf << endl;
+ va_end(ap);
+}
+
+FilteredNdbOut::FilteredNdbOut(OutputStream & out,
+ int threshold, int level)
+ : NdbOut(out) {
+ m_level = level;
+ m_threshold = threshold;
+ m_org = &out;
+ m_null = new NullOutputStream();
+ setLevel(level);
+}
+
+FilteredNdbOut::~FilteredNdbOut(){
+ delete m_null;
+}
+
+void
+FilteredNdbOut::setLevel(int i){
+ m_level = i;
+ if(m_level >= m_threshold){
+ m_out = m_org;
+ } else {
+ m_out = m_null;
+ }
+}
+
+void
+FilteredNdbOut::setThreshold(int i){
+ m_threshold = i;
+ setLevel(m_level);
+}
+
+int
+FilteredNdbOut::getLevel() const {
+ return m_level;
+}
+int
+FilteredNdbOut::getThreshold() const {
+ return m_threshold;
+}
+
diff --git a/storage/ndb/src/common/util/NdbSqlUtil.cpp b/storage/ndb/src/common/util/NdbSqlUtil.cpp
new file mode 100644
index 00000000000..09e150dbacf
--- /dev/null
+++ b/storage/ndb/src/common/util/NdbSqlUtil.cpp
@@ -0,0 +1,1007 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbSqlUtil.hpp>
+#include <NdbOut.hpp>
+#include <my_sys.h>
+
+/*
+ * Data types. The entries must be in the numerical order.
+ */
+
+const NdbSqlUtil::Type
+NdbSqlUtil::m_typeList[] = {
+ { // 0
+ Type::Undefined,
+ NULL,
+ NULL
+ },
+ { // 1
+ Type::Tinyint,
+ cmpTinyint,
+ NULL
+ },
+ { // 2
+ Type::Tinyunsigned,
+ cmpTinyunsigned,
+ NULL
+ },
+ { // 3
+ Type::Smallint,
+ cmpSmallint,
+ NULL
+ },
+ { // 4
+ Type::Smallunsigned,
+ cmpSmallunsigned,
+ NULL
+ },
+ { // 5
+ Type::Mediumint,
+ cmpMediumint,
+ NULL
+ },
+ { // 6
+ Type::Mediumunsigned,
+ cmpMediumunsigned,
+ NULL
+ },
+ { // 7
+ Type::Int,
+ cmpInt,
+ NULL
+ },
+ { // 8
+ Type::Unsigned,
+ cmpUnsigned,
+ NULL
+ },
+ { // 9
+ Type::Bigint,
+ cmpBigint,
+ NULL
+ },
+ { // 10
+ Type::Bigunsigned,
+ cmpBigunsigned,
+ NULL
+ },
+ { // 11
+ Type::Float,
+ cmpFloat,
+ NULL
+ },
+ { // 12
+ Type::Double,
+ cmpDouble,
+ NULL
+ },
+ { // 13
+ Type::Olddecimal,
+ cmpOlddecimal,
+ NULL
+ },
+ { // 14
+ Type::Char,
+ cmpChar,
+ likeChar
+ },
+ { // 15
+ Type::Varchar,
+ cmpVarchar,
+ likeVarchar
+ },
+ { // 16
+ Type::Binary,
+ cmpBinary,
+ likeBinary
+ },
+ { // 17
+ Type::Varbinary,
+ cmpVarbinary,
+ likeVarbinary
+ },
+ { // 18
+ Type::Datetime,
+ cmpDatetime,
+ NULL
+ },
+ { // 19
+ Type::Date,
+ cmpDate,
+ NULL
+ },
+ { // 20
+ Type::Blob,
+ NULL,
+ NULL
+ },
+ { // 21
+ Type::Text,
+ NULL,
+ NULL
+ },
+ { // 22
+ Type::Bit,
+ NULL,
+ NULL
+ },
+ { // 23
+ Type::Longvarchar,
+ cmpLongvarchar,
+ likeLongvarchar
+ },
+ { // 24
+ Type::Longvarbinary,
+ cmpLongvarbinary,
+ likeLongvarbinary
+ },
+ { // 25
+ Type::Time,
+ cmpTime,
+ NULL
+ },
+ { // 26
+ Type::Year,
+ cmpYear,
+ NULL
+ },
+ { // 27
+ Type::Timestamp,
+ cmpTimestamp,
+ NULL
+ },
+ { // 28
+ Type::Olddecimalunsigned,
+ cmpOlddecimalunsigned,
+ NULL
+ },
+ { // 29
+ Type::Decimal,
+ cmpDecimal,
+ NULL
+ },
+ { // 30
+ Type::Decimalunsigned,
+ cmpDecimalunsigned,
+ NULL
+ }
+};
+
+const NdbSqlUtil::Type&
+NdbSqlUtil::getType(Uint32 typeId)
+{
+ if (typeId < sizeof(m_typeList) / sizeof(m_typeList[0]) &&
+ m_typeList[typeId].m_typeId != Type::Undefined) {
+ return m_typeList[typeId];
+ }
+ return m_typeList[Type::Undefined];
+}
+
+const NdbSqlUtil::Type&
+NdbSqlUtil::getTypeBinary(Uint32 typeId)
+{
+ switch (typeId) {
+ case Type::Char:
+ case Type::Varchar:
+ case Type::Binary:
+ case Type::Varbinary:
+ case Type::Longvarchar:
+ case Type::Longvarbinary:
+ typeId = Type::Binary;
+ break;
+ case Type::Text:
+ typeId = Type::Blob;
+ break;
+ default:
+ break;
+ }
+ return getType(typeId);
+}
+
+/*
+ * Comparison functions.
+ */
+
+int
+NdbSqlUtil::cmpTinyint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Int8)) {
+ Int8 v1, v2;
+ memcpy(&v1, p1, sizeof(Int8));
+ memcpy(&v2, p2, sizeof(Int8));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpTinyunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Uint8)) {
+ Uint8 v1, v2;
+ memcpy(&v1, p1, sizeof(Uint8));
+ memcpy(&v2, p2, sizeof(Uint8));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpSmallint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Int16)) {
+ Int16 v1, v2;
+ memcpy(&v1, p1, sizeof(Int16));
+ memcpy(&v2, p2, sizeof(Int16));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpSmallunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Uint16)) {
+ Uint16 v1, v2;
+ memcpy(&v1, p1, sizeof(Uint16));
+ memcpy(&v2, p2, sizeof(Uint16));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpMediumint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= 3) {
+ Int32 v1, v2;
+ v1 = sint3korr((const uchar*)p1);
+ v2 = sint3korr((const uchar*)p2);
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpMediumunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= 3) {
+ Uint32 v1, v2;
+ v1 = uint3korr((const uchar*)p1);
+ v2 = uint3korr((const uchar*)p2);
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpInt(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Int32)) {
+ Int32 v1, v2;
+ memcpy(&v1, p1, sizeof(Int32));
+ memcpy(&v2, p2, sizeof(Int32));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpUnsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Uint32)) {
+ Uint32 v1, v2;
+ memcpy(&v1, p1, sizeof(Uint32));
+ memcpy(&v2, p2, sizeof(Uint32));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpBigint(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Int64)) {
+ Int64 v1, v2;
+ memcpy(&v1, p1, sizeof(Int64));
+ memcpy(&v2, p2, sizeof(Int64));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpBigunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Uint64)) {
+ Uint64 v1, v2;
+ memcpy(&v1, p1, sizeof(Uint64));
+ memcpy(&v2, p2, sizeof(Uint64));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpFloat(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(float)) {
+ float v1, v2;
+ memcpy(&v1, p1, sizeof(float));
+ memcpy(&v2, p2, sizeof(float));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpDouble(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(double)) {
+ double v1, v2;
+ memcpy(&v1, p1, sizeof(double));
+ memcpy(&v2, p2, sizeof(double));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmp_olddecimal(const uchar* s1, const uchar* s2, unsigned n)
+{
+ int sgn = +1;
+ unsigned i = 0;
+ while (i < n) {
+ int c1 = s1[i];
+ int c2 = s2[i];
+ if (c1 == c2) {
+ if (c1 == '-')
+ sgn = -1;
+ } else if (c1 == '-') {
+ return -1;
+ } else if (c2 == '-') {
+ return +1;
+ } else if (c1 < c2) {
+ return -1 * sgn;
+ } else {
+ return +1 * sgn;
+ }
+ i++;
+ }
+ return 0;
+}
+
+int
+NdbSqlUtil::cmpOlddecimal(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (full) {
+ assert(n1 == n2);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ return cmp_olddecimal(v1, v2, n1);
+ }
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpOlddecimalunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (full) {
+ assert(n1 == n2);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ return cmp_olddecimal(v1, v2, n1);
+ }
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpDecimal(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ // compare as binary strings
+ unsigned n = (n1 <= n2 ? n1 : n2);
+ int k = memcmp(v1, v2, n);
+ if (k == 0) {
+ k = (full ? n1 : n) - n2;
+ }
+ return k < 0 ? -1 : k > 0 ? +1 : full ? 0 : CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpDecimalunsigned(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ // compare as binary strings
+ unsigned n = (n1 <= n2 ? n1 : n2);
+ int k = memcmp(v1, v2, n);
+ if (k == 0) {
+ k = (full ? n1 : n) - n2;
+ }
+ return k < 0 ? -1 : k > 0 ? +1 : full ? 0 : CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpChar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ // collation does not work on prefix for some charsets
+ assert(full);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ // not const in MySQL
+ CHARSET_INFO* cs = (CHARSET_INFO*)(info);
+ // compare with space padding
+ int k = (*cs->coll->strnncollsp)(cs, v1, n1, v2, n2, false);
+ return k < 0 ? -1 : k > 0 ? +1 : 0;
+}
+
+int
+NdbSqlUtil::cmpVarchar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const unsigned lb = 1;
+ // collation does not work on prefix for some charsets
+ assert(full && n1 >= lb && n2 >= lb);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ unsigned m1 = *v1;
+ unsigned m2 = *v2;
+ if (m1 <= n1 - lb && m2 <= n2 - lb) {
+ CHARSET_INFO* cs = (CHARSET_INFO*)(info);
+ // compare with space padding
+ int k = (*cs->coll->strnncollsp)(cs, v1 + lb, m1, v2 + lb, m2, false);
+ return k < 0 ? -1 : k > 0 ? +1 : 0;
+ }
+ // treat bad data as NULL
+ if (m1 > n1 - lb && m2 <= n2 - lb)
+ return -1;
+ if (m1 <= n1 - lb && m2 > n2 - lb)
+ return +1;
+ return 0;
+}
+
+int
+NdbSqlUtil::cmpBinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ // compare as binary strings
+ unsigned n = (n1 <= n2 ? n1 : n2);
+ int k = memcmp(v1, v2, n);
+ if (k == 0) {
+ k = (full ? n1 : n) - n2;
+ }
+ return k < 0 ? -1 : k > 0 ? +1 : full ? 0 : CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpVarbinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const unsigned lb = 1;
+ if (n2 >= lb) {
+ assert(n1 >= lb);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ unsigned m1 = *v1;
+ unsigned m2 = *v2;
+ if (m1 <= n1 - lb && m2 <= n2 - lb) {
+ // compare as binary strings
+ unsigned m = (m1 <= m2 ? m1 : m2);
+ int k = memcmp(v1 + lb, v2 + lb, m);
+ if (k == 0) {
+ k = (full ? m1 : m) - m2;
+ }
+ return k < 0 ? -1 : k > 0 ? +1 : full ? 0 : CmpUnknown;
+ }
+ // treat bad data as NULL
+ if (m1 > n1 - lb && m2 <= n2 - lb)
+ return -1;
+ if (m1 <= n1 - lb && m2 > n2 - lb)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpDatetime(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Int64)) {
+ Int64 v1, v2;
+ memcpy(&v1, p1, sizeof(Int64));
+ memcpy(&v2, p2, sizeof(Int64));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpDate(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+#ifdef ndb_date_is_4_byte_native_int
+ if (n2 >= sizeof(Int32)) {
+ Int32 v1, v2;
+ memcpy(&v1, p1, sizeof(Int32));
+ memcpy(&v2, p2, sizeof(Int32));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+#else
+#ifdef ndb_date_sol9x86_cc_xO3_madness
+ if (n2 >= 3) {
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ // from Field_newdate::val_int
+ Uint64 j1 = uint3korr(v1);
+ Uint64 j2 = uint3korr(v2);
+ j1 = (j1 % 32L)+(j1 / 32L % 16L)*100L + (j1/(16L*32L))*10000L;
+ j2 = (j2 % 32L)+(j2 / 32L % 16L)*100L + (j2/(16L*32L))*10000L;
+ if (j1 < j2)
+ return -1;
+ if (j1 > j2)
+ return +1;
+ return 0;
+ }
+#else
+ if (n2 >= 3) {
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ uint j1 = uint3korr(v1);
+ uint j2 = uint3korr(v2);
+ uint d1 = (j1 & 31);
+ uint d2 = (j2 & 31);
+ j1 = (j1 >> 5);
+ j2 = (j2 >> 5);
+ uint m1 = (j1 & 15);
+ uint m2 = (j2 & 15);
+ j1 = (j1 >> 4);
+ j2 = (j2 >> 4);
+ uint y1 = j1;
+ uint y2 = j2;
+ if (y1 < y2)
+ return -1;
+ if (y1 > y2)
+ return +1;
+ if (m1 < m2)
+ return -1;
+ if (m1 > m2)
+ return +1;
+ if (d1 < d2)
+ return -1;
+ if (d1 > d2)
+ return +1;
+ return 0;
+ }
+#endif
+#endif
+ assert(! full);
+ return CmpUnknown;
+}
+
+// not supported
+int
+NdbSqlUtil::cmpBlob(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ assert(false);
+ return 0;
+}
+
+// not supported
+int
+NdbSqlUtil::cmpText(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ assert(false);
+ return 0;
+}
+
+int
+NdbSqlUtil::cmpTime(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= 3) {
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ // from Field_time::val_int
+ Int32 j1 = sint3korr(v1);
+ Int32 j2 = sint3korr(v2);
+ if (j1 < j2)
+ return -1;
+ if (j1 > j2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+// not yet
+int
+NdbSqlUtil::cmpBit(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ assert(false);
+ return 0;
+}
+
+int
+NdbSqlUtil::cmpLongvarchar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const unsigned lb = 2;
+ // collation does not work on prefix for some charsets
+ assert(full && n1 >= lb && n2 >= lb);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ unsigned m1 = uint2korr(v1);
+ unsigned m2 = uint2korr(v2);
+ if (m1 <= n1 - lb && m2 <= n2 - lb) {
+ CHARSET_INFO* cs = (CHARSET_INFO*)(info);
+ // compare with space padding
+ int k = (*cs->coll->strnncollsp)(cs, v1 + lb, m1, v2 + lb, m2, false);
+ return k < 0 ? -1 : k > 0 ? +1 : 0;
+ }
+ // treat bad data as NULL
+ if (m1 > n1 - lb && m2 <= n2 - lb)
+ return -1;
+ if (m1 <= n1 - lb && m2 > n2 - lb)
+ return +1;
+ return 0;
+}
+
+int
+NdbSqlUtil::cmpLongvarbinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ const unsigned lb = 2;
+ if (n2 >= lb) {
+ assert(n1 >= lb);
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ unsigned m1 = uint2korr(v1);
+ unsigned m2 = uint2korr(v2);
+ if (m1 <= n1 - lb && m2 <= n2 - lb) {
+ // compare as binary strings
+ unsigned m = (m1 <= m2 ? m1 : m2);
+ int k = memcmp(v1 + lb, v2 + lb, m);
+ if (k == 0) {
+ k = (full ? m1 : m) - m2;
+ }
+ return k < 0 ? -1 : k > 0 ? +1 : full ? 0 : CmpUnknown;
+ }
+ // treat bad data as NULL
+ if (m1 > n1 - lb && m2 <= n2 - lb)
+ return -1;
+ if (m1 <= n1 - lb && m2 > n2 - lb)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpYear(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Uint8)) {
+ Uint8 v1, v2;
+ memcpy(&v1, p1, sizeof(Uint8));
+ memcpy(&v2, p2, sizeof(Uint8));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+int
+NdbSqlUtil::cmpTimestamp(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2, bool full)
+{
+ if (n2 >= sizeof(Uint32)) {
+ Uint32 v1, v2;
+ memcpy(&v1, p1, sizeof(Uint32));
+ memcpy(&v2, p2, sizeof(Uint32));
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return +1;
+ return 0;
+ }
+ assert(! full);
+ return CmpUnknown;
+}
+
+// like
+
+static const int ndb_wild_prefix = '\\';
+static const int ndb_wild_one = '_';
+static const int ndb_wild_many = '%';
+
+int
+NdbSqlUtil::likeChar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2)
+{
+ const char* v1 = (const char*)p1;
+ const char* v2 = (const char*)p2;
+ CHARSET_INFO* cs = (CHARSET_INFO*)(info);
+ int k = (cs->coll->wildcmp)(cs, v1, v1 + n1, v2, v2 + n2, ndb_wild_prefix, ndb_wild_one, ndb_wild_many);
+ return k == 0 ? 0 : +1;
+}
+
+int
+NdbSqlUtil::likeBinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2)
+{
+ assert(info == 0);
+ return likeChar(&my_charset_bin, p1, n1, p2, n2);
+}
+
+int
+NdbSqlUtil::likeVarchar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2)
+{
+ const unsigned lb = 1;
+ if (n1 >= lb && n2 >= lb) {
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ unsigned m1 = *v1;
+ unsigned m2 = *v2;
+ if (lb + m1 <= n1 && lb + m2 <= n2) {
+ const char* w1 = (const char*)v1 + lb;
+ const char* w2 = (const char*)v2 + lb;
+ CHARSET_INFO* cs = (CHARSET_INFO*)(info);
+ int k = (cs->coll->wildcmp)(cs, w1, w1 + m1, w2, w2 + m2, ndb_wild_prefix, ndb_wild_one, ndb_wild_many);
+ return k == 0 ? 0 : +1;
+ }
+ }
+ return -1;
+}
+
+int
+NdbSqlUtil::likeVarbinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2)
+{
+ assert(info == 0);
+ return likeVarchar(&my_charset_bin, p1, n1, p2, n2);
+}
+
+int
+NdbSqlUtil::likeLongvarchar(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2)
+{
+ const unsigned lb = 2;
+ if (n1 >= lb && n2 >= lb) {
+ const uchar* v1 = (const uchar*)p1;
+ const uchar* v2 = (const uchar*)p2;
+ unsigned m1 = uint2korr(v1);
+ unsigned m2 = uint2korr(v2);
+ if (lb + m1 <= n1 && lb + m2 <= n2) {
+ const char* w1 = (const char*)v1 + lb;
+ const char* w2 = (const char*)v2 + lb;
+ CHARSET_INFO* cs = (CHARSET_INFO*)(info);
+ int k = (cs->coll->wildcmp)(cs, w1, w1 + m1, w2, w2 + m2, ndb_wild_prefix, ndb_wild_one, ndb_wild_many);
+ return k == 0 ? 0 : +1;
+ }
+ }
+ return -1;
+}
+
+int
+NdbSqlUtil::likeLongvarbinary(const void* info, const void* p1, unsigned n1, const void* p2, unsigned n2)
+{
+ assert(info == 0);
+ return likeLongvarchar(&my_charset_bin, p1, n1, p2, n2);
+}
+
+// check charset
+
+bool
+NdbSqlUtil::usable_in_pk(Uint32 typeId, const void* info)
+{
+ const Type& type = getType(typeId);
+ switch (type.m_typeId) {
+ case Type::Char:
+ case Type::Varchar:
+ case Type::Longvarchar:
+ {
+ const CHARSET_INFO *cs = (const CHARSET_INFO*)info;
+ return
+ cs != 0 &&
+ cs->cset != 0 &&
+ cs->coll != 0 &&
+ cs->coll->strnxfrm != 0 &&
+ cs->strxfrm_multiply <= MAX_XFRM_MULTIPLY;
+ }
+ break;
+ case Type::Undefined:
+ case Type::Blob:
+ case Type::Text:
+ case Type::Bit:
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+bool
+NdbSqlUtil::usable_in_hash_index(Uint32 typeId, const void* info)
+{
+ return usable_in_pk(typeId, info);
+}
+
+bool
+NdbSqlUtil::usable_in_ordered_index(Uint32 typeId, const void* info)
+{
+ const Type& type = getType(typeId);
+ if (type.m_cmp == NULL)
+ return false;
+ switch (type.m_typeId) {
+ case Type::Char:
+ case Type::Varchar:
+ case Type::Longvarchar:
+ {
+ const CHARSET_INFO *cs = (const CHARSET_INFO*)info;
+ return
+ cs != 0 &&
+ cs->cset != 0 &&
+ cs->coll != 0 &&
+ cs->coll->strnxfrm != 0 &&
+ cs->coll->strnncollsp != 0 &&
+ cs->strxfrm_multiply <= MAX_XFRM_MULTIPLY;
+ }
+ break;
+ case Type::Undefined:
+ case Type::Blob:
+ case Type::Text:
+ case Type::Bit: // can be fixed
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+// utilities
+
+bool
+NdbSqlUtil::get_var_length(Uint32 typeId, const void* p, unsigned attrlen, Uint32& lb, Uint32& len)
+{
+ const unsigned char* const src = (const unsigned char*)p;
+ switch (typeId) {
+ case NdbSqlUtil::Type::Varchar:
+ case NdbSqlUtil::Type::Varbinary:
+ lb = 1;
+ if (attrlen >= lb) {
+ len = src[0];
+ if (attrlen >= lb + len)
+ return true;
+ }
+ break;
+ case NdbSqlUtil::Type::Longvarchar:
+ case NdbSqlUtil::Type::Longvarbinary:
+ lb = 2;
+ if (attrlen >= lb) {
+ len = src[0] + (src[1] << 8);
+ if (attrlen >= lb + len)
+ return true;
+ }
+ break;
+ default:
+ lb = 0;
+ len = attrlen;
+ return true;
+ break;
+ }
+ return false;
+}
+
+// workaround
+
+int
+NdbSqlUtil::strnxfrm_bug7284(CHARSET_INFO* cs, unsigned char* dst, unsigned dstLen, const unsigned char*src, unsigned srcLen)
+{
+ unsigned char nsp[20]; // native space char
+ unsigned char xsp[20]; // strxfrm-ed space char
+#ifdef VM_TRACE
+ memset(nsp, 0x1f, sizeof(nsp));
+ memset(xsp, 0x1f, sizeof(xsp));
+#endif
+ // convert from unicode codepoint for space
+ int n1 = (*cs->cset->wc_mb)(cs, (my_wc_t)0x20, nsp, nsp + sizeof(nsp));
+ if (n1 <= 0)
+ return -1;
+ // strxfrm to binary
+ int n2 = (*cs->coll->strnxfrm)(cs, xsp, sizeof(xsp), nsp, n1);
+ if (n2 <= 0)
+ return -1;
+ // XXX bug workaround - strnxfrm may not write full string
+ memset(dst, 0x0, dstLen);
+ // strxfrm argument string - returns no error indication
+ int n3 = (*cs->coll->strnxfrm)(cs, dst, dstLen, src, srcLen);
+ // pad with strxfrm-ed space chars
+ int n4 = n3;
+ while (n4 < (int)dstLen) {
+ dst[n4] = xsp[(n4 - n3) % n2];
+ n4++;
+ }
+ // no check for partial last
+ return dstLen;
+}
diff --git a/storage/ndb/src/common/util/OutputStream.cpp b/storage/ndb/src/common/util/OutputStream.cpp
new file mode 100644
index 00000000000..a41eef649dd
--- /dev/null
+++ b/storage/ndb/src/common/util/OutputStream.cpp
@@ -0,0 +1,99 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <OutputStream.hpp>
+#include <socket_io.h>
+
+FileOutputStream::FileOutputStream(FILE * file){
+ f = file;
+}
+
+int
+FileOutputStream::print(const char * fmt, ...){
+ va_list ap;
+ va_start(ap, fmt);
+ const int ret = vfprintf(f, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int
+FileOutputStream::println(const char * fmt, ...){
+ va_list ap;
+ va_start(ap, fmt);
+ const int ret = vfprintf(f, fmt, ap);
+ va_end(ap);
+ return ret + fprintf(f, "\n");
+}
+
+SocketOutputStream::SocketOutputStream(NDB_SOCKET_TYPE socket,
+ unsigned timeout){
+ m_socket = socket;
+ m_timeout = timeout;
+}
+
+int
+SocketOutputStream::print(const char * fmt, ...){
+ va_list ap;
+ va_start(ap, fmt);
+ const int ret = vprint_socket(m_socket, m_timeout, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+int
+SocketOutputStream::println(const char * fmt, ...){
+ va_list ap;
+ va_start(ap, fmt);
+ const int ret = vprintln_socket(m_socket, m_timeout, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+#ifdef NDB_SOFTOSE
+#include <dbgprintf.h>
+int
+SoftOseOutputStream::print(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ else
+ buf[0] = 0;
+ va_end(ap);
+ dbgprintf(buf);
+}
+
+int
+SoftOseOutputStream::println(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ else
+ buf[0] = 0;
+ va_end(ap);
+
+ strcat(buf, "\n\r");
+ dbgprintf(buf);
+}
+#endif
diff --git a/storage/ndb/src/common/util/Parser.cpp b/storage/ndb/src/common/util/Parser.cpp
new file mode 100644
index 00000000000..dea128ccf66
--- /dev/null
+++ b/storage/ndb/src/common/util/Parser.cpp
@@ -0,0 +1,350 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "Parser.hpp"
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <Base64.hpp>
+
+#undef DEBUG
+#define DEBUG(x) ndbout << x << endl;
+
+static void trim(char * str);
+
+class ParseInputStream : public InputStream {
+public:
+ ParseInputStream(InputStream & in, bool trim = true, char eofComment = '#');
+
+ char* gets(char * buf, int bufLen);
+ void push_back(const char *);
+private:
+ InputStream & in;
+ char * buffer;
+};
+
+ParseInputStream::ParseInputStream(InputStream & _in,
+ bool /* unused */,
+ char /* unused */)
+ : in(_in){
+ buffer = 0;
+}
+
+char*
+ParseInputStream::gets(char * buf, int bufLen){
+ if(buffer != 0){
+ strncpy(buf, buffer, bufLen);
+ free(buffer);
+ buffer = 0;
+ return buf;
+ }
+ char *t = in.gets(buf, bufLen);
+ return t;
+}
+
+void
+ParseInputStream::push_back(const char * str){
+ if(buffer != 0)
+ abort();
+ buffer = strdup(str);
+}
+
+ParserImpl::ParserImpl(const DummyRow * rows, InputStream & in,
+ bool b_cmd, bool b_empty, bool b_iarg)
+ : m_rows(rows), input(* new ParseInputStream(in))
+{
+ m_breakOnCmd = b_cmd;
+ m_breakOnEmpty = b_empty;
+ m_breakOnInvalidArg = b_iarg;
+}
+
+ParserImpl::~ParserImpl(){
+ delete & input;
+}
+
+static
+bool
+Empty(const char * str){
+ if(str == 0)
+ return true;
+ const int len = strlen(str);
+ if(len == 0)
+ return false;
+ for(int i = 0; i<len; i++)
+ if(str[i] != ' ' && str[i] != '\t' && str[i] != '\n')
+ return false;
+ return true;
+}
+
+static
+bool
+Eof(const char * str) { return str == 0;}
+
+static
+void
+trim(char * str){
+ if(str == NULL)
+ return;
+ int len = strlen(str);
+ for(len--; str[len] == '\n' || str[len] == ' ' || str[len] == '\t'; len--)
+ str[len] = 0;
+
+ int pos = 0;
+ while(str[pos] == ' ' || str[pos] == '\t')
+ pos++;
+
+ if(str[pos] == '\"' && str[len] == '\"') {
+ pos++;
+ str[len] = 0;
+ len--;
+ }
+
+ memmove(str, &str[pos], len - pos + 2);
+}
+
+static
+bool
+split(char * buf, char ** name, char ** value){
+
+ * value = strchr(buf, ':');
+ if(* value == 0)
+ * value = strchr(buf, '=');
+
+
+ if(* value == 0){
+ return false;
+ }
+ (* value)[0] = 0;
+ * value = (* value + 1);
+ * name = buf;
+
+ trim(* name);
+ trim(* value);
+
+ return true;
+}
+
+bool
+ParserImpl::run(Context * ctx, const class Properties ** pDst,
+ volatile bool * stop) const {
+ * pDst = 0;
+ bool ownStop = false;
+ if(stop == 0)
+ stop = &ownStop;
+
+ ctx->m_aliasUsed.clear();
+
+ const unsigned sz = sizeof(ctx->m_tokenBuffer);
+ ctx->m_currentToken = input.gets(ctx->m_tokenBuffer, sz);
+ if(Eof(ctx->m_currentToken)){
+ ctx->m_status = Parser<Dummy>::Eof;
+ return false;
+ }
+
+ if(ctx->m_currentToken[0] == 0){
+ ctx->m_status = Parser<Dummy>::NoLine;
+ return false;
+ }
+
+ if(Empty(ctx->m_currentToken)){
+ ctx->m_status = Parser<Dummy>::EmptyLine;
+ return false;
+ }
+
+ trim(ctx->m_currentToken);
+ ctx->m_currentCmd = matchCommand(ctx, ctx->m_currentToken, m_rows);
+ if(ctx->m_currentCmd == 0){
+ ctx->m_status = Parser<Dummy>::UnknownCommand;
+ return false;
+ }
+
+ Properties * p = new Properties();
+
+ bool invalidArgument = false;
+ ctx->m_currentToken = input.gets(ctx->m_tokenBuffer, sz);
+
+ while((! * stop) &&
+ !Eof(ctx->m_currentToken) &&
+ !Empty(ctx->m_currentToken)){
+ if(ctx->m_currentToken[0] != 0){
+ trim(ctx->m_currentToken);
+ if(!parseArg(ctx, ctx->m_currentToken, ctx->m_currentCmd + 1, p)){
+ delete p;
+ invalidArgument = true;
+ break;
+ }
+ }
+ ctx->m_currentToken = input.gets(ctx->m_tokenBuffer, sz);
+ }
+
+ if(invalidArgument){
+ char buf[sz];
+ char * tmp;
+ if(!m_breakOnInvalidArg){
+ do {
+ tmp = input.gets(buf, sz);
+ } while((! * stop) && !Eof(tmp) && !Empty(tmp));
+ }
+ return false;
+ }
+
+ if(* stop){
+ delete p;
+ ctx->m_status = Parser<Dummy>::ExternalStop;
+ return false;
+ }
+
+ if(!checkMandatory(ctx, p)){
+ ctx->m_status = Parser<Dummy>::MissingMandatoryArgument;
+ delete p;
+ return false;
+ }
+
+ /**
+ * Add alias to properties
+ */
+ for(unsigned i = 0; i<ctx->m_aliasUsed.size(); i++){
+ const ParserRow<Dummy> * alias = ctx->m_aliasUsed[i];
+ Properties tmp;
+ tmp.put("name", alias->name);
+ tmp.put("realName", alias->realName);
+ p->put("$ALIAS", i, &tmp);
+ }
+ p->put("$ALIAS", ctx->m_aliasUsed.size());
+
+ ctx->m_status = Parser<Dummy>::Ok;
+ * pDst = p;
+ return true;
+}
+
+const ParserImpl::DummyRow*
+ParserImpl::matchCommand(Context* ctx, const char* buf, const DummyRow rows[]){
+ const char * name = buf;
+ const DummyRow * tmp = &rows[0];
+ while(tmp->name != 0 && name != 0){
+ if(strcmp(tmp->name, name) == 0){
+ if(tmp->type == DummyRow::Cmd)
+ return tmp;
+ if(tmp->type == DummyRow::CmdAlias){
+ if(ctx != 0)
+ ctx->m_aliasUsed.push_back(tmp);
+ name = tmp->realName;
+ tmp = &rows[0];
+ continue;
+ }
+ }
+ tmp++;
+ }
+ return 0;
+}
+
+const ParserImpl::DummyRow*
+ParserImpl::matchArg(Context* ctx, const char * buf, const DummyRow rows[]){
+ const char * name = buf;
+ const DummyRow * tmp = &rows[0];
+ while(tmp->name != 0){
+ const DummyRow::Type t = tmp->type;
+ if(t != DummyRow::Arg && t != DummyRow::ArgAlias && t !=DummyRow::CmdAlias)
+ break;
+ if(t !=DummyRow::CmdAlias && strcmp(tmp->name, name) == 0){
+ if(tmp->type == DummyRow::Arg){
+ return tmp;
+ }
+ if(tmp->type == DummyRow::ArgAlias){
+ if(ctx != 0)
+ ctx->m_aliasUsed.push_back(tmp);
+ name = tmp->realName;
+ tmp = &rows[0];
+ continue;
+ }
+ }
+ tmp++;
+ }
+ return 0;
+}
+
+bool
+ParserImpl::parseArg(Context * ctx,
+ char * buf,
+ const DummyRow * rows,
+ Properties * p){
+ char * name;
+ char * value;
+ if(!split(buf, &name, &value)){
+ ctx->m_status = Parser<Dummy>::InvalidArgumentFormat;
+ return false;
+ }
+ const DummyRow * arg = matchArg(ctx, name, rows);
+ if(arg == 0){
+ ctx->m_status = Parser<Dummy>::UnknownArgument;
+ return false;
+ }
+
+ switch(arg->argType){
+ case DummyRow::String:
+ if(p->put(arg->name, value))
+ return true;
+ break;
+ case DummyRow::Int:{
+ Uint32 i;
+ int c = sscanf(value, "%u", &i);
+ if(c != 1){
+ ctx->m_status = Parser<Dummy>::TypeMismatch;
+ return false;
+ }
+ if(p->put(arg->name, i))
+ return true;
+ break;
+ }
+
+ case DummyRow::Properties: {
+ Properties *sp = new Properties();
+ BaseString v(value);
+ UtilBuffer b;
+ base64_decode(v, b);
+ sp->unpack((const Uint32 *)b.get_data(), b.length());
+ break;
+ }
+ default:
+ ctx->m_status = Parser<Dummy>::UnknownArgumentType;
+ return false;
+ }
+ if(p->getPropertiesErrno() == E_PROPERTIES_ELEMENT_ALREADY_EXISTS){
+ ctx->m_status = Parser<Dummy>::ArgumentGivenTwice;
+ return false;
+ }
+
+ abort();
+}
+
+bool
+ParserImpl::checkMandatory(Context* ctx, const Properties* props){
+ const DummyRow * tmp = &ctx->m_currentCmd[1];
+ while(tmp->name != 0 && tmp->type == DummyRow::Arg){
+ if(tmp->argRequired == ParserRow<Dummy>::Mandatory &&
+ !props->contains(tmp->name)){
+ ctx->m_status = Parser<Dummy>::MissingMandatoryArgument;
+ ctx->m_currentArg = tmp;
+ return false;
+ }
+ tmp++;
+ }
+ return true;
+}
+
+template class Vector<const ParserRow<ParserImpl::Dummy>*>;
diff --git a/storage/ndb/src/common/util/Properties.cpp b/storage/ndb/src/common/util/Properties.cpp
new file mode 100644
index 00000000000..0edcda0e726
--- /dev/null
+++ b/storage/ndb/src/common/util/Properties.cpp
@@ -0,0 +1,1136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <Properties.hpp>
+
+#include <NdbTCP.h>
+#include <NdbOut.hpp>
+
+static
+char * f_strdup(const char * s){
+ if(!s) return 0;
+ return strdup(s);
+}
+
+/**
+ * Note has to be a multiple of 4 bytes
+ */
+const char Properties::version[] = { 2, 0, 0, 1, 1, 1, 1, 4 };
+const char Properties::delimiter = ':';
+
+/**
+ * PropertyImpl
+ */
+struct PropertyImpl{
+ PropertiesType valueType;
+ const char * name;
+ void * value;
+
+ ~PropertyImpl();
+ PropertyImpl(const char * name, Uint32 value);
+ PropertyImpl(const char * name, Uint64 value);
+ PropertyImpl(const char * name, const char * value);
+ PropertyImpl(const char * name, const Properties * value);
+
+ static PropertyImpl * copyPropertyImpl(const PropertyImpl &);
+};
+
+/**
+ * PropertiesImpl
+ */
+class PropertiesImpl {
+ PropertiesImpl(const PropertiesImpl &); // Not implemented
+ PropertiesImpl& operator=(const PropertiesImpl&); // Not implemented
+public:
+ PropertiesImpl(Properties *, bool case_insensitive);
+ PropertiesImpl(Properties *, const PropertiesImpl &);
+ ~PropertiesImpl();
+
+ Properties * properties;
+
+ Uint32 size;
+ Uint32 items;
+ PropertyImpl **content;
+
+ bool m_insensitive;
+ int (* compare)(const char *s1, const char *s2);
+
+ void setCaseInsensitiveNames(bool value);
+ void grow(int sizeToAdd);
+
+ PropertyImpl * get(const char * name) const;
+ PropertyImpl * put(PropertyImpl *);
+ void remove(const char * name);
+
+ Uint32 getPackedSize(Uint32 pLen) const;
+ bool pack(Uint32 *& buf, const char * prefix, Uint32 prefixLen) const;
+ bool unpack(const Uint32 * buf, Uint32 &bufLen, Properties * top, int items);
+
+ Uint32 getTotalItems() const;
+
+ void setErrno(Uint32 pErr, Uint32 osErr = 0){
+ properties->setErrno(pErr, osErr);
+ }
+
+ const char * getProps(const char * name, const PropertiesImpl ** impl) const;
+ const char * getPropsPut(const char * name, PropertiesImpl ** impl);
+};
+
+/**
+ * Methods for Property
+ */
+Property::Property(const char * name, Uint32 value){
+ impl = new PropertyImpl(name, value);
+}
+
+Property::Property(const char * name, const char * value){
+ impl = new PropertyImpl(name, value);
+}
+
+Property::Property(const char * name, const class Properties * value){
+ impl = new PropertyImpl(name, value);
+
+ ((Properties*)impl->value)->setCaseInsensitiveNames(value->getCaseInsensitiveNames());
+}
+
+Property::~Property(){
+ delete impl;
+}
+
+/**
+ * Methods for Properties
+ */
+Properties::Properties(bool case_insensitive){
+ parent = 0;
+ impl = new PropertiesImpl(this, case_insensitive);
+}
+
+Properties::Properties(const Properties & org){
+ parent = 0;
+ impl = new PropertiesImpl(this, * org.impl);
+}
+
+Properties::Properties(const Property * anArray, int arrayLen){
+ impl = new PropertiesImpl(this, false);
+
+ put(anArray, arrayLen);
+}
+
+Properties::~Properties(){
+ clear();
+ delete impl;
+}
+
+void
+Properties::put(const Property * anArray, int arrayLen){
+ if(anArray == 0)
+ return;
+ for(int i = 0; i<arrayLen; i++)
+ impl->put(anArray[i].impl);
+}
+
+template <class T>
+bool
+put(PropertiesImpl * impl, const char * name, T value, bool replace){
+ if(name == 0){
+ impl->setErrno(E_PROPERTIES_INVALID_NAME);
+ return false;
+ }
+
+ PropertiesImpl * tmp = 0;
+ const char * short_name = impl->getPropsPut(name, &tmp);
+
+ if(tmp == 0){
+ impl->setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+
+ if(tmp->get(short_name) != 0){
+ if(replace){
+ tmp->remove(short_name);
+ } else {
+ impl->setErrno(E_PROPERTIES_ELEMENT_ALREADY_EXISTS);
+ return false;
+ }
+ }
+ return tmp->put(new PropertyImpl(short_name, value));
+}
+
+
+bool
+Properties::put(const char * name, Uint32 value, bool replace){
+ return ::put(impl, name, value, replace);
+}
+
+bool
+Properties::put64(const char * name, Uint64 value, bool replace){
+ return ::put(impl, name, value, replace);
+}
+
+bool
+Properties::put(const char * name, const char * value, bool replace){
+ return ::put(impl, name, value, replace);
+}
+
+bool
+Properties::put(const char * name, const Properties * value, bool replace){
+ return ::put(impl, name, value, replace);
+}
+
+bool
+Properties::getTypeOf(const char * name, PropertiesType * type) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+ setErrno(E_PROPERTIES_OK);
+ * type = nvp->valueType;
+ return true;
+}
+
+bool
+Properties::contains(const char * name) const {
+ PropertyImpl * nvp = impl->get(name);
+ return nvp != 0;
+}
+
+bool
+Properties::get(const char * name, Uint32 * value) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+
+ if(nvp->valueType == PropertiesType_Uint32){
+ * value = * (Uint32 *)nvp->value;
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+
+ if(nvp->valueType == PropertiesType_Uint64){
+ Uint64 tmp = * (Uint64 *)nvp->value;
+ Uint64 max = 1; max <<= 32;
+ if(tmp < max){
+ * value = (Uint32)tmp;
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+ }
+ setErrno(E_PROPERTIES_INVALID_TYPE);
+ return false;
+}
+
+bool
+Properties::get(const char * name, Uint64 * value) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+
+ if(nvp->valueType == PropertiesType_Uint32){
+ Uint32 tmp = * (Uint32 *)nvp->value;
+ * value = (Uint64)tmp;
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+
+ if(nvp->valueType == PropertiesType_Uint64){
+ * value = * (Uint64 *)nvp->value;
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+ setErrno(E_PROPERTIES_INVALID_TYPE);
+ return false;
+}
+
+bool
+Properties::get(const char * name, const char ** value) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+
+ if(nvp->valueType == PropertiesType_char){
+ * value = (const char *)nvp->value;
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+ setErrno(E_PROPERTIES_INVALID_TYPE);
+ return false;
+}
+
+bool
+Properties::get(const char * name, BaseString& value) const {
+ const char *tmp = "";
+ bool ret;
+ ret = get(name, &tmp);
+ value.assign(tmp);
+ return ret;
+}
+
+bool
+Properties::get(const char * name, const Properties ** value) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+ if(nvp->valueType == PropertiesType_Properties){
+ * value = (const Properties *)nvp->value;
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+ setErrno(E_PROPERTIES_INVALID_TYPE);
+ return false;
+}
+
+bool
+Properties::getCopy(const char * name, char ** value) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+
+ if(nvp->valueType == PropertiesType_char){
+ * value = f_strdup((const char *)nvp->value);
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+ setErrno(E_PROPERTIES_INVALID_TYPE);
+ return false;
+}
+
+bool
+Properties::getCopy(const char * name, Properties ** value) const {
+ PropertyImpl * nvp = impl->get(name);
+ if(nvp == 0){
+ setErrno(E_PROPERTIES_NO_SUCH_ELEMENT);
+ return false;
+ }
+
+ if(nvp->valueType == PropertiesType_Properties){
+ * value = new Properties(* (const Properties *)nvp->value);
+ setErrno(E_PROPERTIES_OK);
+ return true;
+ }
+ setErrno(E_PROPERTIES_INVALID_TYPE);
+ return false;
+}
+
+void
+Properties::clear(){
+ while(impl->items > 0)
+ impl->remove(impl->content[0]->name);
+}
+
+void
+Properties::remove(const char * name) {
+ impl->remove(name);
+}
+
+void
+Properties::print(FILE * out, const char * prefix) const{
+ char buf[1024];
+ if(prefix == 0)
+ buf[0] = 0;
+ else
+ strncpy(buf, prefix, 1024);
+
+ for(unsigned int i = 0; i<impl->items; i++){
+ switch(impl->content[i]->valueType){
+ case PropertiesType_Uint32:
+ fprintf(out, "%s%s = (Uint32) %d\n", buf, impl->content[i]->name,
+ *(Uint32 *)impl->content[i]->value);
+ break;
+ case PropertiesType_Uint64:
+ fprintf(out, "%s%s = (Uint64) %lld\n", buf, impl->content[i]->name,
+ *(Uint64 *)impl->content[i]->value);
+ break;
+ case PropertiesType_char:
+ fprintf(out, "%s%s = (char*) \"%s\"\n", buf, impl->content[i]->name,
+ (char *)impl->content[i]->value);
+ break;
+ case PropertiesType_Properties:
+ char buf2 [1024];
+ BaseString::snprintf(buf2, sizeof(buf2), "%s%s%c",buf, impl->content[i]->name,
+ Properties::delimiter);
+ ((Properties *)impl->content[i]->value)->print(out, buf2);
+ break;
+ }
+ }
+}
+
+Properties::Iterator::Iterator(const Properties* prop) :
+ m_prop(prop),
+ m_iterator(0) {
+}
+
+const char*
+Properties::Iterator::first() {
+ m_iterator = 0;
+ return next();
+}
+
+const char*
+Properties::Iterator::next() {
+ if (m_iterator < m_prop->impl->items)
+ return m_prop->impl->content[m_iterator++]->name;
+ else
+ return NULL;
+}
+
+Uint32
+Properties::getPackedSize() const {
+ Uint32 sz = 0;
+
+ sz += sizeof(version); // Version id of properties object
+ sz += 4; // No Of Items
+ sz += 4; // Checksum
+
+ return sz + impl->getPackedSize(0);
+}
+
+static
+Uint32
+computeChecksum(const Uint32 * buf, Uint32 words){
+ Uint32 sum = 0;
+ for(unsigned int i = 0; i<words; i++)
+ sum ^= htonl(buf[i]);
+
+ return sum;
+}
+
+bool
+Properties::pack(Uint32 * buf) const {
+ Uint32 * bufStart = buf;
+
+ memcpy(buf, version, sizeof(version));
+
+ // Note that version must be a multiple of 4
+ buf += (sizeof(version) / 4);
+
+ * buf = htonl(impl->getTotalItems());
+ buf++;
+ bool res = impl->pack(buf, "", 0);
+ if(!res)
+ return res;
+
+ * buf = htonl(computeChecksum(bufStart, (buf - bufStart)));
+
+ return true;
+}
+
+bool
+Properties::unpack(const Uint32 * buf, Uint32 bufLen){
+ const Uint32 * bufStart = buf;
+ Uint32 bufLenOrg = bufLen;
+
+ if(bufLen < sizeof(version)){
+ setErrno(E_PROPERTIES_INVALID_BUFFER_TO_SHORT);
+ return false;
+ }
+
+ if(memcmp(buf, version, sizeof(version)) != 0){
+ setErrno(E_PROPERTIES_INVALID_VERSION_WHILE_UNPACKING);
+ return false;
+ }
+ bufLen -= sizeof(version);
+
+ // Note that version must be a multiple of 4
+ buf += (sizeof(version) / 4);
+
+ if(bufLen < 4){
+ setErrno(E_PROPERTIES_INVALID_BUFFER_TO_SHORT);
+ return false;
+ }
+
+ Uint32 totalItems = ntohl(* buf);
+ buf++; bufLen -= 4;
+ bool res = impl->unpack(buf, bufLen, this, totalItems);
+ if(!res)
+ return res;
+
+ Uint32 sum = computeChecksum(bufStart, (bufLenOrg-bufLen)/4);
+ if(sum != ntohl(bufStart[(bufLenOrg-bufLen)/4])){
+ setErrno(E_PROPERTIES_INVALID_CHECKSUM);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Methods for PropertiesImpl
+ */
+PropertiesImpl::PropertiesImpl(Properties * p, bool case_insensitive){
+ this->properties = p;
+ items = 0;
+ size = 25;
+ content = new PropertyImpl * [size];
+ setCaseInsensitiveNames(case_insensitive);
+}
+
+PropertiesImpl::PropertiesImpl(Properties * p, const PropertiesImpl & org){
+ this->properties = p;
+ this->size = org.size;
+ this->items = org.items;
+ this->m_insensitive = org.m_insensitive;
+ this->compare = org.compare;
+ content = new PropertyImpl * [size];
+ for(unsigned int i = 0; i<items; i++){
+ content[i] = PropertyImpl::copyPropertyImpl(* org.content[i]);
+ }
+}
+
+PropertiesImpl::~PropertiesImpl(){
+ for(unsigned int i = 0; i<items; i++)
+ delete content[i];
+ delete [] content;
+}
+
+void
+PropertiesImpl::setCaseInsensitiveNames(bool value){
+ m_insensitive = value;
+ if(value)
+ compare = strcasecmp;
+ else
+ compare = strcmp;
+}
+
+void
+PropertiesImpl::grow(int sizeToAdd){
+ PropertyImpl ** newContent = new PropertyImpl * [size + sizeToAdd];
+ memcpy(newContent, content, items * sizeof(PropertyImpl *));
+ delete [] content;
+ content = newContent;
+ size += sizeToAdd;
+}
+
+PropertyImpl *
+PropertiesImpl::get(const char * name) const {
+ const PropertiesImpl * tmp = 0;
+ const char * short_name = getProps(name, &tmp);
+ if(tmp == 0){
+ return 0;
+ }
+
+ for(unsigned int i = 0; i<tmp->items; i++) {
+ if((* compare)(tmp->content[i]->name, short_name) == 0)
+ return tmp->content[i];
+ }
+
+ return 0;
+}
+
+PropertyImpl *
+PropertiesImpl::put(PropertyImpl * nvp){
+ if(items == size)
+ grow(size);
+ content[items] = nvp;
+
+ items ++;
+
+ if(nvp->valueType == PropertiesType_Properties){
+ ((Properties*)nvp->value)->parent = properties;
+ }
+ return nvp;
+}
+
+void
+PropertiesImpl::remove(const char * name){
+ for(unsigned int i = 0; i<items; i++){
+ if((* compare)(content[i]->name, name) == 0){
+ delete content[i];
+ memmove(&content[i], &content[i+1], (items-i-1)*sizeof(PropertyImpl *));
+ items --;
+ return;
+ }
+ }
+}
+
+Uint32
+PropertiesImpl::getTotalItems() const {
+ int ret = 0;
+ for(unsigned int i = 0; i<items; i++)
+ if(content[i]->valueType == PropertiesType_Properties){
+ ret += ((Properties*)content[i]->value)->impl->getTotalItems();
+ } else {
+ ret ++;
+ }
+ return ret;
+}
+
+const char *
+PropertiesImpl::getProps(const char * name,
+ const PropertiesImpl ** impl) const {
+ const char * ret = name;
+ const char * tmp = strchr(name, Properties::delimiter);
+ if(tmp == 0){
+ * impl = this;
+ return ret;
+ } else {
+ Uint32 sz = tmp - name;
+ char * tmp2 = (char*)malloc(sz + 1);
+ memcpy(tmp2, name, sz);
+ tmp2[sz] = 0;
+
+ PropertyImpl * nvp = get(tmp2);
+
+ free(tmp2);
+
+ if(nvp == 0){
+ * impl = 0;
+ return 0;
+ }
+ if(nvp->valueType != PropertiesType_Properties){
+ * impl = 0;
+ return name;
+ }
+ return ((Properties*)nvp->value)->impl->getProps(tmp+1, impl);
+ }
+}
+
+const char *
+PropertiesImpl::getPropsPut(const char * name,
+ PropertiesImpl ** impl) {
+ const char * ret = name;
+ const char * tmp = strchr(name, Properties::delimiter);
+ if(tmp == 0){
+ * impl = this;
+ return ret;
+ } else {
+ Uint32 sz = tmp - name;
+ char * tmp2 = (char*)malloc(sz + 1);
+ memcpy(tmp2, name, sz);
+ tmp2[sz] = 0;
+
+ PropertyImpl * nvp = get(tmp2);
+
+ if(nvp == 0){
+ Properties * tmpP = new Properties();
+ PropertyImpl * tmpPI = new PropertyImpl(tmp2, tmpP);
+ PropertyImpl * nvp = put(tmpPI);
+
+ delete tmpP;
+ free(tmp2);
+ return ((Properties*)nvp->value)->impl->getPropsPut(tmp+1, impl);
+ }
+ free(tmp2);
+ if(nvp->valueType != PropertiesType_Properties){
+ * impl = 0;
+ return name;
+ }
+ return ((Properties*)nvp->value)->impl->getPropsPut(tmp+1, impl);
+ }
+}
+
+int
+mod4(unsigned int i){
+ int res = i + (4 - (i % 4));
+ return res;
+}
+
+Uint32
+PropertiesImpl::getPackedSize(Uint32 pLen) const {
+ Uint32 sz = 0;
+ for(unsigned int i = 0; i<items; i++){
+ if(content[i]->valueType == PropertiesType_Properties){
+ Properties * p = (Properties*)content[i]->value;
+ sz += p->impl->getPackedSize(pLen+strlen(content[i]->name)+1);
+ } else {
+ sz += 4; // Type
+ sz += 4; // Name Len
+ sz += 4; // Value Len
+ sz += mod4(pLen + strlen(content[i]->name)); // Name
+ switch(content[i]->valueType){
+ case PropertiesType_char:
+ sz += mod4(strlen((char *)content[i]->value));
+ break;
+ case PropertiesType_Uint32:
+ sz += mod4(4);
+ break;
+ case PropertiesType_Uint64:
+ sz += mod4(8);
+ break;
+ case PropertiesType_Properties:
+ default:
+ assert(0);
+ }
+ }
+ }
+ return sz;
+}
+
+struct CharBuf {
+ char * buffer;
+ Uint32 bufLen;
+ Uint32 contentLen;
+
+ CharBuf(){
+ buffer = 0;
+ bufLen = 0;
+ contentLen = 0;
+ }
+
+ ~CharBuf(){
+ free(buffer);
+ }
+
+ void clear() { contentLen = 0;}
+ bool add(const char * str, Uint32 strLen){
+ if(!expand(contentLen + strLen + 1))
+ return false;
+ memcpy(&buffer[contentLen], str, strLen);
+ contentLen += strLen;
+ buffer[contentLen] = 0;
+ return true;
+ }
+
+ bool add(char c){
+ return add(&c, 1);
+ }
+
+ bool expand(Uint32 newSize){
+ if(newSize >= bufLen){
+
+ char * tmp = (char*)malloc(newSize + 1024);
+ memset(tmp, 0, newSize + 1024);
+ if(tmp == 0)
+ return false;
+ if(contentLen > 0)
+ memcpy(tmp, buffer, contentLen);
+ if(buffer != 0)
+ free(buffer);
+ buffer = tmp;
+ bufLen = newSize + 1024;
+ }
+ return true;
+ }
+};
+
+bool
+PropertiesImpl::pack(Uint32 *& buf, const char * prefix, Uint32 pLen) const {
+ CharBuf charBuf;
+
+ for(unsigned int i = 0; i<items; i++){
+ const int strLenName = strlen(content[i]->name);
+
+ if(content[i]->valueType == PropertiesType_Properties){
+ charBuf.clear();
+ if(!charBuf.add(prefix, pLen)){
+ properties->setErrno(E_PROPERTIES_ERROR_MALLOC_WHILE_PACKING,
+ errno);
+ return false;
+ }
+
+ if(!charBuf.add(content[i]->name, strLenName)){
+ properties->setErrno(E_PROPERTIES_ERROR_MALLOC_WHILE_PACKING,
+ errno);
+ return false;
+ }
+
+ if(!charBuf.add(Properties::delimiter)){
+ properties->setErrno(E_PROPERTIES_ERROR_MALLOC_WHILE_PACKING,
+ errno);
+ return false;
+ }
+
+ if(!((Properties*)(content[i]->value))->impl->pack(buf,
+ charBuf.buffer,
+ charBuf.contentLen)){
+
+ return false;
+ }
+ continue;
+ }
+
+ Uint32 valLenData = 0;
+ Uint32 valLenWrite = 0;
+ Uint32 sz = 4 + 4 + 4 + mod4(pLen + strLenName);
+ switch(content[i]->valueType){
+ case PropertiesType_Uint32:
+ valLenData = 4;
+ break;
+ case PropertiesType_Uint64:
+ valLenData = 8;
+ break;
+ case PropertiesType_char:
+ valLenData = strlen((char *)content[i]->value);
+ break;
+ case PropertiesType_Properties:
+ assert(0);
+ }
+ valLenWrite = mod4(valLenData);
+ sz += valLenWrite;
+
+ * (buf + 0) = htonl(content[i]->valueType);
+ * (buf + 1) = htonl(pLen + strLenName);
+ * (buf + 2) = htonl(valLenData);
+
+ char * valBuf = (char*)(buf + 3);
+ char * nameBuf = (char*)(buf + 3 + (valLenWrite / 4));
+
+ memset(valBuf, 0, sz-12);
+
+ switch(content[i]->valueType){
+ case PropertiesType_Uint32:
+ * (Uint32 *)valBuf = htonl(* (Uint32 *)content[i]->value);
+ break;
+ case PropertiesType_Uint64:{
+ Uint64 val = * (Uint64 *)content[i]->value;
+ Uint32 hi = (val >> 32);
+ Uint32 lo = (val & 0xFFFFFFFF);
+ * (Uint32 *)valBuf = htonl(hi);
+ * (Uint32 *)(valBuf + 4) = htonl(lo);
+ }
+ break;
+ case PropertiesType_char:
+ memcpy(valBuf, content[i]->value, strlen((char*)content[i]->value));
+ break;
+ case PropertiesType_Properties:
+ assert(0);
+ }
+ if(pLen > 0)
+ memcpy(nameBuf, prefix, pLen);
+ memcpy(nameBuf + pLen, content[i]->name, strLenName);
+
+ buf += (sz / 4);
+ }
+
+ return true;
+}
+
+bool
+PropertiesImpl::unpack(const Uint32 * buf, Uint32 &bufLen, Properties * top,
+ int _items){
+ CharBuf charBuf;
+ while(_items > 0){
+ Uint32 tmp[3];
+
+ if(bufLen <= 12){
+ top->setErrno(E_PROPERTIES_BUFFER_TO_SMALL_WHILE_UNPACKING);
+ return false;
+ }
+
+ tmp[0] = ntohl(buf[0]);
+ tmp[1] = ntohl(buf[1]);
+ tmp[2] = ntohl(buf[2]);
+ buf += 3;
+ bufLen -= 12;
+
+ PropertiesType pt = (PropertiesType)tmp[0];
+ Uint32 nameLen = tmp[1];
+ Uint32 valueLen = tmp[2];
+ Uint32 nameLenRead = mod4(nameLen);
+ Uint32 valueLenRead = mod4(valueLen);
+
+ Uint32 sz = nameLenRead + valueLenRead;
+ if(bufLen < sz){
+ top->setErrno(E_PROPERTIES_BUFFER_TO_SMALL_WHILE_UNPACKING);
+ return false;
+ }
+
+ if(!charBuf.expand(sz)){
+ top->setErrno(E_PROPERTIES_ERROR_MALLOC_WHILE_UNPACKING, errno);
+ return false;
+ }
+
+ memcpy(charBuf.buffer, buf, sz);
+ buf += (sz / 4);
+ bufLen -= sz ;
+
+ char * valBuf = charBuf.buffer;
+ char * nameBuf = charBuf.buffer + valueLenRead;
+
+ nameBuf[nameLen] = 0;
+ valBuf[valueLen] = 0;
+
+ bool res3 = false;
+ switch(pt){
+ case PropertiesType_Uint32:
+ res3 = top->put(nameBuf, ntohl(* (Uint32 *)valBuf), true);
+ break;
+ case PropertiesType_Uint64:{
+ Uint64 hi = ntohl(* (Uint32 *)valBuf);
+ Uint64 lo = ntohl(* (Uint32 *)(valBuf + 4));
+ res3 = top->put64(nameBuf, (hi << 32) + lo, true);
+ }
+ break;
+ case PropertiesType_char:
+ res3 = top->put(nameBuf, valBuf, true);
+ break;
+ case PropertiesType_Properties:
+ assert(0);
+ }
+ if(!res3){
+ return false;
+ }
+ _items--;
+ }
+ return true;
+}
+
+PropertyImpl::~PropertyImpl(){
+ free((char*)name);
+ switch(valueType){
+ case PropertiesType_Uint32:
+ delete (Uint32 *)value;
+ break;
+ case PropertiesType_Uint64:
+ delete (Uint64 *)value;
+ break;
+ case PropertiesType_char:
+ free((char *)value);
+ break;
+ case PropertiesType_Properties:
+ delete (Properties *)value;
+ break;
+ }
+}
+
+PropertyImpl *
+PropertyImpl::copyPropertyImpl(const PropertyImpl & org){
+ switch(org.valueType){
+ case PropertiesType_Uint32:
+ return new PropertyImpl(org.name, * (Uint32 *)org.value);
+ case PropertiesType_Uint64:
+ return new PropertyImpl(org.name, * (Uint64 *)org.value);
+ break;
+ case PropertiesType_char:
+ return new PropertyImpl(org.name, (char *)org.value);
+ break;
+ case PropertiesType_Properties:
+ return new PropertyImpl(org.name, (Properties *)org.value);
+ break;
+ default:
+ assert(0);
+ }
+ return 0;
+}
+
+PropertyImpl::PropertyImpl(const char * _name, Uint32 _value){
+ this->name = f_strdup(_name);
+ this->value = new Uint32;
+ * ((Uint32 *)this->value) = _value;
+ this->valueType = PropertiesType_Uint32;
+}
+
+PropertyImpl::PropertyImpl(const char * _name, Uint64 _value){
+ this->name = f_strdup(_name);
+ this->value = new Uint64;
+ * ((Uint64 *)this->value) = _value;
+ this->valueType = PropertiesType_Uint64;
+}
+
+PropertyImpl::PropertyImpl(const char * _name, const char * _value){
+ this->name = f_strdup(_name);
+ this->value = f_strdup(_value);
+ this->valueType = PropertiesType_char;
+
+}
+
+PropertyImpl::PropertyImpl(const char * _name, const Properties * _value){
+ this->name = f_strdup(_name);
+ this->value = new Properties(* _value);
+ this->valueType = PropertiesType_Properties;
+}
+
+const Uint32 E_PROPERTIES_OK = 0;
+const Uint32 E_PROPERTIES_INVALID_NAME = 1;
+const Uint32 E_PROPERTIES_NO_SUCH_ELEMENT = 2;
+const Uint32 E_PROPERTIES_INVALID_TYPE = 3;
+const Uint32 E_PROPERTIES_ELEMENT_ALREADY_EXISTS = 4;
+
+const Uint32 E_PROPERTIES_ERROR_MALLOC_WHILE_PACKING = 5;
+const Uint32 E_PROPERTIES_INVALID_VERSION_WHILE_UNPACKING = 6;
+const Uint32 E_PROPERTIES_INVALID_BUFFER_TO_SHORT = 7;
+const Uint32 E_PROPERTIES_ERROR_MALLOC_WHILE_UNPACKING = 8;
+const Uint32 E_PROPERTIES_INVALID_CHECKSUM = 9;
+const Uint32 E_PROPERTIES_BUFFER_TO_SMALL_WHILE_UNPACKING = 10;
+
+/**
+ * These are methods that used to be inline
+ *
+ * But Diab 4.1f could not compile -release with to many inlines
+ */
+void
+Properties::setErrno(Uint32 pErr, Uint32 osErr) const {
+ if(parent != 0){
+ parent->setErrno(pErr, osErr);
+ return ;
+ }
+
+ /**
+ * propErrno & osErrno used to be mutable,
+ * but diab didn't know what mutable meant.
+ */
+ *((Uint32*)&propErrno) = pErr;
+ *((Uint32*)&osErrno) = osErr;
+}
+
+/**
+ * Inlined get/put(name, no, ...) - methods
+ */
+
+bool
+Properties::put(const char * name, Uint32 no, Uint32 val, bool replace){
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = put(tmp, val, replace);
+ free(tmp);
+ return res;
+}
+
+bool
+Properties::put64(const char * name, Uint32 no, Uint64 val, bool replace){
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = put(tmp, val, replace);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::put(const char * name, Uint32 no, const char * val, bool replace){
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = put(tmp, val, replace);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::put(const char * name, Uint32 no, const Properties * val,
+ bool replace){
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = put(tmp, val, replace);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::getTypeOf(const char * name, Uint32 no,
+ PropertiesType * type) const {
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = getTypeOf(tmp, type);
+ free(tmp);
+ return res;
+}
+
+bool
+Properties::contains(const char * name, Uint32 no) const {
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = contains(tmp);
+ free(tmp);
+ return res;
+}
+
+bool
+Properties::get(const char * name, Uint32 no, Uint32 * value) const{
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = get(tmp, value);
+ free(tmp);
+ return res;
+}
+
+bool
+Properties::get(const char * name, Uint32 no, Uint64 * value) const{
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = get(tmp, value);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::get(const char * name, Uint32 no, const char ** value) const {
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = get(tmp, value);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::get(const char * name, Uint32 no, const Properties ** value) const{
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = get(tmp, value);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::getCopy(const char * name, Uint32 no, char ** value) const {
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = getCopy(tmp, value);
+ free(tmp);
+ return res;
+}
+
+
+bool
+Properties::getCopy(const char * name, Uint32 no, Properties ** value) const {
+ size_t tmp_len = strlen(name)+20;
+ char * tmp = (char*)malloc(tmp_len);
+ BaseString::snprintf(tmp, tmp_len, "%s_%d", name, no);
+ bool res = getCopy(tmp, value);
+ free(tmp);
+ return res;
+}
+
+void
+Properties::setCaseInsensitiveNames(bool value){
+ impl->setCaseInsensitiveNames(value);
+}
+
+bool
+Properties::getCaseInsensitiveNames() const {
+ return impl->m_insensitive;
+}
+
+template bool put(PropertiesImpl *, const char *, Uint32, bool);
+template bool put(PropertiesImpl *, const char *, Uint64, bool);
+template bool put(PropertiesImpl *, const char *, const char *, bool);
+template bool put(PropertiesImpl *, const char *, const Properties*, bool);
diff --git a/storage/ndb/src/common/util/SimpleProperties.cpp b/storage/ndb/src/common/util/SimpleProperties.cpp
new file mode 100644
index 00000000000..00c440fcb4e
--- /dev/null
+++ b/storage/ndb/src/common/util/SimpleProperties.cpp
@@ -0,0 +1,507 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <SimpleProperties.hpp>
+#include <NdbOut.hpp>
+#include <NdbTCP.h>
+#include <UtilBuffer.hpp>
+
+bool
+SimpleProperties::Writer::first(){
+ return reset();
+}
+
+bool
+SimpleProperties::Writer::add(Uint16 key, Uint32 value){
+ Uint32 head = Uint32Value;
+ head <<= 16;
+ head += key;
+ if(!putWord(htonl(head)))
+ return false;
+
+ return putWord(htonl(value));
+}
+
+bool
+SimpleProperties::Writer::add(Uint16 key, const char * value){
+ Uint32 head = StringValue;
+ head <<= 16;
+ head += key;
+ if(!putWord(htonl(head)))
+ return false;
+ Uint32 strLen = strlen(value) + 1; // Including NULL-byte
+ if(!putWord(htonl(strLen)))
+ return false;
+
+ const Uint32 valLen = (strLen + 3) / 4;
+ return putWords((Uint32*)value, valLen);
+}
+
+bool
+SimpleProperties::Writer::add(Uint16 key, const void* value, int len){
+ Uint32 head = BinaryValue;
+ head <<= 16;
+ head += key;
+ if(!putWord(htonl(head)))
+ return false;
+ if(!putWord(htonl(len)))
+ return false;
+
+ const Uint32 valLen = (len + 3) / 4;
+ return putWords((Uint32*)value, valLen);
+}
+
+SimpleProperties::Reader::Reader(){
+ m_itemLen = 0;
+}
+
+bool
+SimpleProperties::Reader::first(){
+ reset();
+ m_itemLen = 0;
+ return readValue();
+}
+
+bool
+SimpleProperties::Reader::next(){
+ return readValue();
+}
+
+bool
+SimpleProperties::Reader::valid() const {
+ return m_type != InvalidValue;
+}
+
+Uint16
+SimpleProperties::Reader::getKey() const{
+ return m_key;
+}
+
+Uint16
+SimpleProperties::Reader::getValueLen() const {
+ switch(m_type){
+ case Uint32Value:
+ return 4;
+ case StringValue:
+ case BinaryValue:
+ return m_strLen;
+ case InvalidValue:
+ return 0;
+ }
+ return 0;
+}
+
+SimpleProperties::ValueType
+SimpleProperties::Reader::getValueType() const {
+ return m_type;
+}
+
+Uint32
+SimpleProperties::Reader::getUint32() const {
+ return m_ui32_value;
+}
+
+char *
+SimpleProperties::Reader::getString(char * dst) const {
+ if(peekWords((Uint32*)dst, m_itemLen))
+ return dst;
+ return 0;
+}
+
+bool
+SimpleProperties::Reader::readValue(){
+ if(!step(m_itemLen)){
+ m_type = InvalidValue;
+ return false;
+ }
+
+ Uint32 tmp;
+ if(!getWord(&tmp)){
+ m_type = InvalidValue;
+ return false;
+ }
+
+ tmp = ntohl(tmp);
+ m_key = tmp & 0xFFFF;
+ m_type = (SimpleProperties::ValueType)(tmp >> 16);
+ switch(m_type){
+ case Uint32Value:
+ m_itemLen = 1;
+ if(!peekWord(&m_ui32_value))
+ return false;
+ m_ui32_value = ntohl(m_ui32_value);
+ return true;
+ case StringValue:
+ case BinaryValue:
+ if(!getWord(&tmp))
+ return false;
+ m_strLen = ntohl(tmp);
+ m_itemLen = (m_strLen + 3)/4;
+ return true;
+ default:
+ m_itemLen = 0;
+ m_type = InvalidValue;
+ return false;
+ }
+}
+
+SimpleProperties::UnpackStatus
+SimpleProperties::unpack(Reader & it, void * dst,
+ const SP2StructMapping _map[], Uint32 mapSz,
+ bool ignoreMinMax,
+ bool ignoreUnknownKeys){
+ do {
+ if(!it.valid())
+ break;
+
+ bool found = false;
+ Uint16 key = it.getKey();
+ for(Uint32 i = 0; i<mapSz; i++){
+ if(key == _map[i].Key){
+ found = true;
+ if(_map[i].Type == InvalidValue)
+ return Break;
+ if(_map[i].Type != it.getValueType())
+ return TypeMismatch;
+
+ char * _dst = (char *)dst;
+ _dst += _map[i].Offset;
+
+ switch(it.getValueType()){
+ case Uint32Value:{
+ const Uint32 val = it.getUint32();
+ if(!ignoreMinMax){
+ if(val < _map[i].minValue)
+ return ValueTooLow;
+ if(val > _map[i].maxValue)
+ return ValueTooHigh;
+ }
+ * ((Uint32 *)_dst) = val;
+ break;
+ }
+ case BinaryValue:
+ case StringValue:{
+ unsigned len = it.getValueLen();
+ if(len < _map[i].minValue)
+ return ValueTooLow;
+ if(len > _map[i].maxValue)
+ return ValueTooHigh;
+ it.getString(_dst);
+ break;
+ }
+ default:
+ abort();
+ }
+ break;
+ }
+ }
+ if(!found && !ignoreUnknownKeys)
+ return UnknownKey;
+ } while(it.next());
+
+ return Eof;
+}
+
+SimpleProperties::UnpackStatus
+SimpleProperties::pack(Writer & it, const void * __src,
+ const SP2StructMapping _map[], Uint32 mapSz,
+ bool ignoreMinMax){
+
+ const char * _src = (const char *)__src;
+
+ for(Uint32 i = 0; i<mapSz; i++){
+ bool ok = false;
+ const char * src = _src + _map[i].Offset;
+ switch(_map[i].Type){
+ case SimpleProperties::InvalidValue:
+ ok = true;
+ break;
+ case SimpleProperties::Uint32Value:{
+ Uint32 val = * ((Uint32*)src);
+ if(!ignoreMinMax){
+ if(val < _map[i].minValue)
+ return ValueTooLow;
+ if(val > _map[i].maxValue)
+ return ValueTooHigh;
+ }
+ ok = it.add(_map[i].Key, val);
+ }
+ break;
+ case SimpleProperties::BinaryValue:{
+ const char * src_len = _src + _map[i].Length_Offset;
+ Uint32 len = *((Uint32*)src_len);
+ if(!ignoreMinMax){
+ if(len == _map[i].maxValue)
+ return ValueTooHigh;
+ }
+ ok = it.add(_map[i].Key, src, len);
+ break;
+ }
+ case SimpleProperties::StringValue:
+ if(!ignoreMinMax){
+ size_t len = strlen(src);
+ if(len == _map[i].maxValue)
+ return ValueTooHigh;
+ }
+ ok = it.add(_map[i].Key, src);
+ break;
+ }
+ if(!ok)
+ return OutOfMemory;
+ }
+
+ return Eof;
+}
+
+void
+SimpleProperties::Reader::printAll(NdbOut& ndbout){
+ char tmp[1024];
+ for(first(); valid(); next()){
+ switch(getValueType()){
+ case SimpleProperties::Uint32Value:
+ ndbout << "Key: " << getKey()
+ << " value(" << getValueLen() << ") : "
+ << getUint32() << endl;
+ break;
+ case SimpleProperties::BinaryValue:
+ case SimpleProperties::StringValue:
+ if(getValueLen() < 1024){
+ getString(tmp);
+ ndbout << "Key: " << getKey()
+ << " value(" << getValueLen() << ") : "
+ << "\"" << tmp << "\"" << endl;
+ } else {
+ ndbout << "Key: " << getKey()
+ << " value(" << getValueLen() << ") : "
+ << "\"" << "<TOO LONG>" << "\"" << endl;
+
+ }
+ break;
+ default:
+ ndbout << "Unknown type for key: " << getKey()
+ << " type: " << (Uint32)getValueType() << endl;
+ }
+ }
+}
+
+SimplePropertiesLinearReader::SimplePropertiesLinearReader
+(const Uint32 * src, Uint32 len){
+ m_src = src;
+ m_len = len;
+ m_pos = 0;
+ first();
+}
+
+void
+SimplePropertiesLinearReader::reset() {
+ m_pos = 0;
+}
+
+bool
+SimplePropertiesLinearReader::step(Uint32 len){
+ m_pos += len;
+ return m_pos < m_len;
+}
+
+bool
+SimplePropertiesLinearReader::getWord(Uint32 * dst) {
+ if(m_pos<m_len){
+ * dst = m_src[m_pos++];
+ return true;
+ }
+ return false;
+}
+
+bool
+SimplePropertiesLinearReader::peekWord(Uint32 * dst) const {
+ if(m_pos<m_len){
+ * dst = m_src[m_pos];
+ return true;
+ }
+ return false;
+}
+
+bool
+SimplePropertiesLinearReader::peekWords(Uint32 * dst, Uint32 len) const {
+ if(m_pos + len <= m_len){
+ memcpy(dst, &m_src[m_pos], 4 * len);
+ return true;
+ }
+ return false;
+}
+
+LinearWriter::LinearWriter(Uint32 * src, Uint32 len){
+ m_src = src;
+ m_len = len;
+ reset();
+}
+
+bool LinearWriter::reset() { m_pos = 0; return m_len > 0;}
+
+bool
+LinearWriter::putWord(Uint32 val){
+ if(m_pos < m_len){
+ m_src[m_pos++] = val;
+ return true;
+ }
+ return false;
+}
+
+bool
+LinearWriter::putWords(const Uint32 * src, Uint32 len){
+ if(m_pos + len <= m_len){
+ memcpy(&m_src[m_pos], src, 4 * len);
+ m_pos += len;
+ return true;
+ }
+ return false;
+}
+
+Uint32
+LinearWriter::getWordsUsed() const { return m_pos;}
+
+UtilBufferWriter::UtilBufferWriter(UtilBuffer & b)
+ : m_buf(b)
+{
+ reset();
+}
+
+bool UtilBufferWriter::reset() { m_buf.clear(); return true;}
+
+bool
+UtilBufferWriter::putWord(Uint32 val){
+ return (m_buf.append(&val, 4) == 0);
+}
+
+bool
+UtilBufferWriter::putWords(const Uint32 * src, Uint32 len){
+ return (m_buf.append(src, 4 * len) == 0);
+}
+
+Uint32
+UtilBufferWriter::getWordsUsed() const { return m_buf.length() / 4;}
+
+#if 0
+LinearPagesReader::LinearPagesReader(const Uint32 * base,
+ Uint32 pageSize,
+ Uint32 headerSize,
+ Uint32 noOfPages,
+ Uint32 len){
+ m_base = base;
+ m_pageSz = pageSize;
+ m_noOfPages = noOfPages;
+ m_pageHeaderSz = headerSize;
+ m_len = len;
+ reset();
+}
+
+void
+LinearPagesReader::reset() { m_pos = 0;}
+
+bool
+LinearPagesReader::step(Uint32 len){
+ m_pos += len;
+ return m_pos < m_len;
+}
+
+bool
+LinearPagesReader::getWord(Uint32 * dst) {
+ if(m_pos<m_len){
+ * dst = m_base[getPos(m_pos++)];
+ return true;
+ }
+ return false;
+}
+
+bool
+LinearPagesReader::peekWord(Uint32 * dst) const {
+ if(m_pos<m_len){
+ * dst = m_base[getPos(m_pos)];
+ return true;
+ }
+ return false;
+}
+
+bool
+LinearPagesReader::peekWords(Uint32 * dst, Uint32 len) const {
+ if(m_pos + len <= m_len){
+ for(Uint32 i = 0; i<len; i++)
+ * (dst + i) = m_base[getPos(m_pos + i)];
+ return true;
+ }
+ return false;
+}
+
+Uint32
+LinearPagesReader::getPos(Uint32 pos) const {
+ const Uint32 sz = (m_pageSz - m_pageHeaderSz);
+ Uint32 no = pos / sz;
+ Uint32 in = pos % sz;
+ return no * m_pageSz + m_pageHeaderSz + in;
+}
+
+LinearPagesWriter::LinearPagesWriter(Uint32 * base,
+ Uint32 pageSize,
+ Uint32 noOfPages,
+ Uint32 headerSize){
+ m_base = base;
+ m_pageSz = pageSize;
+ m_noOfPages = noOfPages;
+ m_pageHeaderSz = headerSize;
+ m_len = noOfPages * (pageSize - headerSize);
+ reset();
+}
+
+bool
+LinearPagesWriter::putWord(Uint32 val){
+ if(m_pos < m_len){
+ m_base[getPos(m_pos++)] = val;
+ return true;
+ }
+ return false;
+}
+
+bool
+LinearPagesWriter::putWords(const Uint32 * src, Uint32 len){
+ if(m_pos + len <= m_len){
+ for(Uint32 i = 0; i<len; i++)
+ m_base[getPos(m_pos++)] = src[i];
+ return true;
+ }
+ return false;
+}
+
+#if 0
+Uint32
+LinearPagesWriter::getWordsUsed() const {
+ return getPos(m_pos);
+}
+#endif
+
+Uint32
+LinearPagesWriter::getPagesUsed() const {
+ return m_pos / (m_pageSz - m_pageHeaderSz);
+}
+
+Uint32
+LinearPagesWriter::getPos(Uint32 pos) const {
+ const Uint32 sz = (m_pageSz - m_pageHeaderSz);
+ Uint32 no = pos / sz;
+ Uint32 in = pos % sz;
+ return no * m_pageSz + m_pageHeaderSz + in;
+}
+#endif
diff --git a/storage/ndb/src/common/util/SocketAuthenticator.cpp b/storage/ndb/src/common/util/SocketAuthenticator.cpp
new file mode 100644
index 00000000000..aed4db39231
--- /dev/null
+++ b/storage/ndb/src/common/util/SocketAuthenticator.cpp
@@ -0,0 +1,91 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <SocketClient.hpp>
+#include <SocketAuthenticator.hpp>
+#include <InputStream.hpp>
+#include <OutputStream.hpp>
+#include <NdbOut.hpp>
+
+SocketAuthSimple::SocketAuthSimple(const char *username, const char *passwd) {
+ if (username)
+ m_username= strdup(username);
+ else
+ m_username= 0;
+ if (passwd)
+ m_passwd= strdup(passwd);
+ else
+ m_passwd= 0;
+}
+
+SocketAuthSimple::~SocketAuthSimple()
+{
+ if (m_passwd)
+ free((void*)m_passwd);
+ if (m_username)
+ free((void*)m_username);
+}
+
+bool SocketAuthSimple::client_authenticate(int sockfd)
+{
+ SocketOutputStream s_output(sockfd);
+ SocketInputStream s_input(sockfd);
+
+ if (m_username)
+ s_output.println("%s", m_username);
+ else
+ s_output.println("");
+
+ if (m_passwd)
+ s_output.println("%s", m_passwd);
+ else
+ s_output.println("");
+
+ char buf[16];
+ if (s_input.gets(buf, 16) == 0) return false;
+ if (strncmp("ok", buf, 2) == 0)
+ return true;
+
+ return false;
+}
+
+bool SocketAuthSimple::server_authenticate(int sockfd)
+{
+
+ SocketOutputStream s_output(sockfd);
+ SocketInputStream s_input(sockfd);
+
+ char buf[256];
+
+ if (s_input.gets(buf, 256) == 0) return false;
+ buf[255]= 0;
+ if (m_username)
+ free((void*)m_username);
+ m_username= strdup(buf);
+
+ if (s_input.gets(buf, 256) == 0) return false;
+ buf[255]= 0;
+ if (m_passwd)
+ free((void*)m_passwd);
+ m_passwd= strdup(buf);
+
+ s_output.println("ok");
+
+ return true;
+}
diff --git a/storage/ndb/src/common/util/SocketClient.cpp b/storage/ndb/src/common/util/SocketClient.cpp
new file mode 100644
index 00000000000..38df1417eb8
--- /dev/null
+++ b/storage/ndb/src/common/util/SocketClient.cpp
@@ -0,0 +1,94 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+#include <SocketClient.hpp>
+#include <SocketAuthenticator.hpp>
+
+SocketClient::SocketClient(const char *server_name, unsigned short port, SocketAuthenticator *sa)
+{
+ m_auth= sa;
+ m_port= port;
+ m_server_name= strdup(server_name);
+ m_sockfd= NDB_INVALID_SOCKET;
+}
+
+SocketClient::~SocketClient()
+{
+ if (m_server_name)
+ free(m_server_name);
+ if (m_sockfd != NDB_INVALID_SOCKET)
+ NDB_CLOSE_SOCKET(m_sockfd);
+ if (m_auth)
+ delete m_auth;
+}
+
+bool
+SocketClient::init()
+{
+ if (m_sockfd != NDB_INVALID_SOCKET)
+ NDB_CLOSE_SOCKET(m_sockfd);
+
+ memset(&m_servaddr, 0, sizeof(m_servaddr));
+ m_servaddr.sin_family = AF_INET;
+ m_servaddr.sin_port = htons(m_port);
+ // Convert ip address presentation format to numeric format
+ if (Ndb_getInAddr(&m_servaddr.sin_addr, m_server_name))
+ return false;
+
+ m_sockfd= socket(AF_INET, SOCK_STREAM, 0);
+ if (m_sockfd == NDB_INVALID_SOCKET) {
+ return false;
+ }
+
+ return true;
+}
+
+NDB_SOCKET_TYPE
+SocketClient::connect()
+{
+ if (m_sockfd == NDB_INVALID_SOCKET)
+ {
+ if (!init()) {
+#ifdef VM_TRACE
+ ndbout << "SocketClient::connect() failed " << m_server_name << " " << m_port << endl;
+#endif
+ return NDB_INVALID_SOCKET;
+ }
+ }
+ const int r = ::connect(m_sockfd, (struct sockaddr*) &m_servaddr, sizeof(m_servaddr));
+ if (r == -1) {
+ NDB_CLOSE_SOCKET(m_sockfd);
+ m_sockfd= NDB_INVALID_SOCKET;
+ return NDB_INVALID_SOCKET;
+ }
+
+ if (m_auth) {
+ if (!m_auth->client_authenticate(m_sockfd))
+ {
+ NDB_CLOSE_SOCKET(m_sockfd);
+ m_sockfd= NDB_INVALID_SOCKET;
+ return NDB_INVALID_SOCKET;
+ }
+ }
+ NDB_SOCKET_TYPE sockfd= m_sockfd;
+ m_sockfd= NDB_INVALID_SOCKET;
+
+ return sockfd;
+}
diff --git a/storage/ndb/src/common/util/SocketServer.cpp b/storage/ndb/src/common/util/SocketServer.cpp
new file mode 100644
index 00000000000..6019d24d736
--- /dev/null
+++ b/storage/ndb/src/common/util/SocketServer.cpp
@@ -0,0 +1,345 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+
+#include <SocketServer.hpp>
+
+#include <NdbTCP.h>
+#include <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbSleep.h>
+
+#define DEBUG(x) ndbout << x << endl;
+
+SocketServer::SocketServer(int maxSessions) :
+ m_sessions(10),
+ m_services(5)
+{
+ m_thread = 0;
+ m_stopThread = false;
+ m_maxSessions = maxSessions;
+}
+
+SocketServer::~SocketServer() {
+ unsigned i;
+ for(i = 0; i<m_sessions.size(); i++){
+ delete m_sessions[i].m_session;
+ }
+ for(i = 0; i<m_services.size(); i++){
+ delete m_services[i].m_service;
+ }
+}
+
+bool
+SocketServer::tryBind(unsigned short port, const char * intface) {
+ struct sockaddr_in servaddr;
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servaddr.sin_port = htons(port);
+
+ if(intface != 0){
+ if(Ndb_getInAddr(&servaddr.sin_addr, intface))
+ return false;
+ }
+
+ const NDB_SOCKET_TYPE sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == NDB_INVALID_SOCKET) {
+ return false;
+ }
+
+ const int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&on, sizeof(on)) == -1) {
+ NDB_CLOSE_SOCKET(sock);
+ return false;
+ }
+
+ if (bind(sock, (struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
+ NDB_CLOSE_SOCKET(sock);
+ return false;
+ }
+
+ NDB_CLOSE_SOCKET(sock);
+ return true;
+}
+
+bool
+SocketServer::setup(SocketServer::Service * service,
+ unsigned short * port,
+ const char * intface){
+ DBUG_ENTER("SocketServer::setup");
+ DBUG_PRINT("enter",("interface=%s, port=%u", intface, *port));
+ struct sockaddr_in servaddr;
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servaddr.sin_port = htons(*port);
+
+ if(intface != 0){
+ if(Ndb_getInAddr(&servaddr.sin_addr, intface))
+ DBUG_RETURN(false);
+ }
+
+ const NDB_SOCKET_TYPE sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == NDB_INVALID_SOCKET) {
+ DBUG_PRINT("error",("socket() - %d - %s",
+ errno, strerror(errno)));
+ DBUG_RETURN(false);
+ }
+
+ const int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&on, sizeof(on)) == -1) {
+ DBUG_PRINT("error",("getsockopt() - %d - %s",
+ errno, strerror(errno)));
+ NDB_CLOSE_SOCKET(sock);
+ DBUG_RETURN(false);
+ }
+
+ if (bind(sock, (struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
+ DBUG_PRINT("error",("bind() - %d - %s",
+ errno, strerror(errno)));
+ NDB_CLOSE_SOCKET(sock);
+ DBUG_RETURN(false);
+ }
+
+ /* Get the port we bound to */
+ SOCKET_SIZE_TYPE sock_len = sizeof(servaddr);
+ if(getsockname(sock,(struct sockaddr*)&servaddr,&sock_len)<0) {
+ ndbout_c("An error occurred while trying to find out what"
+ " port we bound to. Error: %s",strerror(errno));
+ NDB_CLOSE_SOCKET(sock);
+ DBUG_RETURN(false);
+ }
+
+ DBUG_PRINT("info",("bound to %u",ntohs(servaddr.sin_port)));
+ if (listen(sock, m_maxSessions) == -1){
+ DBUG_PRINT("error",("listen() - %d - %s",
+ errno, strerror(errno)));
+ NDB_CLOSE_SOCKET(sock);
+ DBUG_RETURN(false);
+ }
+
+ ServiceInstance i;
+ i.m_socket = sock;
+ i.m_service = service;
+ m_services.push_back(i);
+
+ *port = ntohs(servaddr.sin_port);
+
+ DBUG_RETURN(true);
+}
+
+void
+SocketServer::doAccept(){
+ fd_set readSet, exceptionSet;
+ FD_ZERO(&readSet);
+ FD_ZERO(&exceptionSet);
+
+ m_services.lock();
+ int maxSock = 0;
+ for (unsigned i = 0; i < m_services.size(); i++){
+ const NDB_SOCKET_TYPE s = m_services[i].m_socket;
+ FD_SET(s, &readSet);
+ FD_SET(s, &exceptionSet);
+ maxSock = (maxSock > s ? maxSock : s);
+ }
+ struct timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ if(select(maxSock + 1, &readSet, 0, &exceptionSet, &timeout) > 0){
+ for (unsigned i = 0; i < m_services.size(); i++){
+ ServiceInstance & si = m_services[i];
+
+ if(FD_ISSET(si.m_socket, &readSet)){
+ NDB_SOCKET_TYPE childSock = accept(si.m_socket, 0, 0);
+ if(childSock == NDB_INVALID_SOCKET){
+ continue;
+ }
+
+ SessionInstance s;
+ s.m_service = si.m_service;
+ s.m_session = si.m_service->newSession(childSock);
+ if(s.m_session != 0){
+ m_sessions.push_back(s);
+ startSession(m_sessions.back());
+ }
+
+ continue;
+ }
+
+ if(FD_ISSET(si.m_socket, &exceptionSet)){
+ DEBUG("socket in the exceptionSet");
+ continue;
+ }
+ }
+ }
+ m_services.unlock();
+}
+
+extern "C"
+void*
+socketServerThread_C(void* _ss){
+ SocketServer * ss = (SocketServer *)_ss;
+ ss->doRun();
+ return 0;
+}
+
+void
+SocketServer::startServer(){
+ m_threadLock.lock();
+ if(m_thread == 0 && m_stopThread == false){
+ m_thread = NdbThread_Create(socketServerThread_C,
+ (void**)this,
+ 32768,
+ "NdbSockServ",
+ NDB_THREAD_PRIO_LOW);
+ }
+ m_threadLock.unlock();
+}
+
+void
+SocketServer::stopServer(){
+ m_threadLock.lock();
+ if(m_thread != 0){
+ m_stopThread = true;
+
+ void * res;
+ NdbThread_WaitFor(m_thread, &res);
+ NdbThread_Destroy(&m_thread);
+ m_thread = 0;
+ }
+ m_threadLock.unlock();
+}
+
+void
+SocketServer::doRun(){
+
+ while(!m_stopThread){
+ checkSessions();
+ if(m_sessions.size() < m_maxSessions){
+ doAccept();
+ } else {
+ NdbSleep_MilliSleep(200);
+ }
+ }
+}
+
+void
+SocketServer::startSession(SessionInstance & si){
+ si.m_thread = NdbThread_Create(sessionThread_C,
+ (void**)si.m_session,
+ 32768,
+ "NdbSock_Session",
+ NDB_THREAD_PRIO_LOW);
+}
+
+static
+bool
+transfer(NDB_SOCKET_TYPE sock){
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ const PROCESS p = current_process();
+ const size_t ps = sizeof(PROCESS);
+ int res = setsockopt(sock, SOL_SOCKET, SO_OSEOWNER, &p, ps);
+ if(res != 0){
+ ndbout << "Failed to transfer ownership of socket" << endl;
+ return false;
+ }
+#endif
+ return true;
+}
+
+void
+SocketServer::foreachSession(void (*func)(SocketServer::Session*, void *), void *data)
+{
+ for(int i = m_sessions.size() - 1; i >= 0; i--){
+ (*func)(m_sessions[i].m_session, data);
+ }
+ checkSessions();
+}
+
+void
+SocketServer::checkSessions(){
+ for(int i = m_sessions.size() - 1; i >= 0; i--){
+ if(m_sessions[i].m_session->m_stopped){
+ if(m_sessions[i].m_thread != 0){
+ void* ret;
+ NdbThread_WaitFor(m_sessions[i].m_thread, &ret);
+ NdbThread_Destroy(&m_sessions[i].m_thread);
+ }
+ m_sessions[i].m_session->stopSession();
+ delete m_sessions[i].m_session;
+ m_sessions.erase(i);
+ }
+ }
+}
+
+void
+SocketServer::stopSessions(bool wait){
+ int i;
+ for(i = m_sessions.size() - 1; i>=0; i--)
+ {
+ m_sessions[i].m_session->stopSession();
+ m_sessions[i].m_session->m_stop = true; // to make sure
+ }
+ for(i = m_services.size() - 1; i>=0; i--)
+ m_services[i].m_service->stopSessions();
+
+ if(wait){
+ while(m_sessions.size() > 0){
+ checkSessions();
+ NdbSleep_MilliSleep(100);
+ }
+ }
+}
+
+/***** Session code ******/
+
+extern "C"
+void*
+sessionThread_C(void* _sc){
+ SocketServer::Session * si = (SocketServer::Session *)_sc;
+
+ if(!transfer(si->m_socket)){
+ si->m_stopped = true;
+ return 0;
+ }
+
+ /**
+ * may have m_stopped set if we're transforming a mgm
+ * connection into a transporter connection.
+ */
+ if(!si->m_stopped)
+ {
+ if(!si->m_stop){
+ si->m_stopped = false;
+ si->runSession();
+ } else {
+ NDB_CLOSE_SOCKET(si->m_socket);
+ }
+ }
+
+ si->m_stopped = true;
+ return 0;
+}
+
+template class MutexVector<SocketServer::ServiceInstance>;
+template class MutexVector<SocketServer::SessionInstance>;
diff --git a/storage/ndb/src/common/util/basestring_vsnprintf.c b/storage/ndb/src/common/util/basestring_vsnprintf.c
new file mode 100644
index 00000000000..f5d01fb1532
--- /dev/null
+++ b/storage/ndb/src/common/util/basestring_vsnprintf.c
@@ -0,0 +1,71 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#ifdef __sgi
+/* define on IRIX to get posix compliant vsnprintf */
+#define _XOPEN_SOURCE 500
+#endif
+#include <stdio.h>
+#include <basestring_vsnprintf.h>
+#include <my_config.h>
+
+#ifdef _WINDOWS
+#define SNPRINTF_RETURN_TRUNC
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+int
+basestring_snprintf(char *str, size_t size, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, format);
+ ret= basestring_vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return(ret);
+}
+
+#ifdef SNPRINTF_RETURN_TRUNC
+static char basestring_vsnprintf_buf[16*1024];
+#endif
+int
+basestring_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+{
+ if (size == 0)
+ {
+#ifdef SNPRINTF_RETURN_TRUNC
+ return vsnprintf(basestring_vsnprintf_buf,
+ sizeof(basestring_vsnprintf_buf),
+ format, ap);
+#else
+ char buf[1];
+ return vsnprintf(buf, 1, format, ap);
+#endif
+ }
+ {
+ int ret= vsnprintf(str, size, format, ap);
+#ifdef SNPRINTF_RETURN_TRUNC
+ if (ret == size-1 || ret == -1)
+ {
+ ret= vsnprintf(basestring_vsnprintf_buf,
+ sizeof(basestring_vsnprintf_buf),
+ format, ap);
+ }
+#endif
+ return ret;
+ }
+}
diff --git a/storage/ndb/src/common/util/filetest/FileUnitTest.cpp b/storage/ndb/src/common/util/filetest/FileUnitTest.cpp
new file mode 100644
index 00000000000..b6e7b7e8ec0
--- /dev/null
+++ b/storage/ndb/src/common/util/filetest/FileUnitTest.cpp
@@ -0,0 +1,237 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "FileUnitTest.hpp"
+#include <File.hpp>
+
+#include <NdbOut.hpp>
+
+typedef bool (*TESTFUNC)(const char*);
+
+typedef const char TESTNAME;
+typedef struct
+{
+ const char* name;
+ TESTFUNC test;
+}Tests;
+
+static Tests testCases[] = { {"Create/Write", &FileUnitTest::testWrite},
+ {"Read", &FileUnitTest::testRead},
+ {"Exists", &FileUnitTest::testExists},
+ {"File Size", &FileUnitTest::testSize},
+ {"Rename", &FileUnitTest::testRename},
+ {"Remove", &FileUnitTest::testRemove} };
+
+static int testFailed = 0;
+
+int main(int argc, char* argv[])
+{
+ if (argc < 2)
+ {
+ ndbout << "Usage: filetest <filename>" << endl;
+ return 0;
+ }
+ const char* fileName = argv[1];
+
+ int testCount = (sizeof(testCases) / sizeof(Tests));
+ ndbout << "Starting " << testCount << " tests..." << endl;
+ for (int i = 0; i < testCount; i++)
+ {
+ ndbout << "-- " << " Test " << i + 1
+ << " [" << testCases[i].name << "] --" << endl;
+ if (testCases[i].test(fileName))
+ {
+ ndbout << "-- Passed --" << endl;
+ }
+ else
+ {
+ ndbout << "-- Failed -- " << endl;
+ }
+
+ }
+ ndbout << endl << "-- " << testCount - testFailed << " passed, "
+ << testFailed << " failed --" << endl;
+ return 0;
+}
+
+
+bool
+FileUnitTest::testWrite(const char* aFileName)
+{
+ bool rc = true;
+ File f;
+ if (f.open(aFileName, "w"))
+ {
+ f.writeChar("ABABABABABAB ABBABAB ABBABA ABAB JKH KJHA JHHAHAH...");
+ f.writeChar("12129791242 1298371923 912738912 378129837128371128132...\n");
+ f.close();
+ }
+ else
+ {
+ error("testWrite failed: ");
+ rc = false;
+ }
+ return rc;
+}
+
+bool
+FileUnitTest::testRead(const char* aFileName)
+{
+ bool rc = true;
+ // Read file
+ File f;
+ if (f.open(aFileName, "r"))
+ {
+ long size = f.size();
+ ndbout << "File size = " << size << endl;
+ ndbout << "Allocating buf of " << size << " bytes" << endl;
+ char* buf = new char[size];
+ buf[size - 1] = '\0';
+ int r = 0;
+ while ((r = f.readChar(buf, r, size)) > 0)
+ {
+ ndbout << "Read(" << r << "):" << buf << endl;
+ }
+ f.close();
+ delete buf;
+ }
+ else
+ {
+ error("readTest failed: ");
+ rc = false;
+ }
+ return rc;
+}
+
+bool
+FileUnitTest::testExists(const char* aFileName)
+{
+ bool rc = true;
+ if (File::exists(aFileName))
+ {
+ if (File::exists("ThisFileShouldnotbe.txt"))
+ {
+ rc = false;
+ error("testExists failed, the file should NOT be found.");
+ }
+ }
+ else
+ {
+ rc = false;
+ error("testExists failed, the file should exist.");
+ }
+
+ return rc;
+}
+
+
+bool
+FileUnitTest::testSize(const char* aFileName)
+{
+ bool rc = true;
+ File f;
+ if (f.open(aFileName, "r"))
+ {
+ long size = f.size();
+ if (size <= 0)
+ {
+ rc = false;
+ error("testSize failed, size is <= 0");
+ }
+ ndbout << "File size = " << size << endl;
+ }
+ else
+ {
+ rc = false;
+ error("testSize failed, could no open file.");
+ }
+ f.close();
+ return rc;
+}
+
+bool
+FileUnitTest::testRename(const char* aFileName)
+{
+ bool rc = true;
+ if (File::rename(aFileName, "filetest_new.txt"))
+ {
+ if (!File::exists("filetest_new.txt"))
+ {
+ rc = false;
+ error("testRename failed, new file does not exists.");
+ }
+ else
+ {
+ ndbout << "Renamed " << aFileName << " to filetest_new.txt" << endl;
+ }
+ }
+ else
+ {
+ rc = false;
+ error("testRename failed, unable to rename file.");
+ }
+
+ return rc;
+}
+
+bool
+FileUnitTest::testRemove(const char* aFileName)
+{
+ bool rc = true;
+ File f;
+ if (f.open("filetest_new.txt", "r"))
+ {
+ if (!f.remove())
+ {
+ rc = false;
+ error("testRemove failed, could not remove file.");
+ }
+ else
+ {
+ if (File::exists("filetest_new"))
+ {
+ rc = false;
+ error("testRemove failed, file was not removed, it still exists.");
+ }
+ }
+ } // (f.open("filetest_new", "r"))
+ else
+ {
+ rc = false;
+ error("testRemove failed, could not read the file.");
+ }
+
+ return rc;
+}
+
+void
+FileUnitTest::error(const char* msg)
+{
+ testFailed++;
+ ndbout << "Test failed: " << msg << endl;
+ perror("Errno msg");
+}
+
+
+FileUnitTest::FileUnitTest()
+{
+
+}
+
+FileUnitTest::~FileUnitTest()
+{
+
+}
diff --git a/storage/ndb/src/common/util/filetest/FileUnitTest.hpp b/storage/ndb/src/common/util/filetest/FileUnitTest.hpp
new file mode 100644
index 00000000000..a589615e9b2
--- /dev/null
+++ b/storage/ndb/src/common/util/filetest/FileUnitTest.hpp
@@ -0,0 +1,41 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 FILEUNITTEST_H
+#define FILEUNITTEST_H
+
+/**
+ * Unit test of File.
+ *
+ * @version #@ $Id: FileUnitTest.hpp,v 1.1 2002/03/13 18:09:03 eyualex Exp $
+ */
+class FileUnitTest
+{
+public:
+ static bool testWrite(const char* aFileName);
+ static bool testRead(const char* aFileName);
+ static bool testExists(const char* aFileName);
+ static bool testSize(const char* aFileName);
+ static bool testRename(const char* aFileName);
+ static bool testRemove(const char* aFileName);
+
+ static void error(const char* msg);
+private:
+ FileUnitTest();
+ ~FileUnitTest();
+
+};
+#endif
diff --git a/storage/ndb/src/common/util/filetest/Makefile b/storage/ndb/src/common/util/filetest/Makefile
new file mode 100644
index 00000000000..fe1842921f9
--- /dev/null
+++ b/storage/ndb/src/common/util/filetest/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE :=
+
+BIN_TARGET := filetest
+BIN_TARGET_ARCHIVES := portlib general
+
+SOURCES := FileUnitTest.cpp
+
+CCFLAGS_LOC += -I$(NDB_TOP)/include/logger -I$(NDB_TOP)/include/portlib
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/src/common/util/getarg.cat3 b/storage/ndb/src/common/util/getarg.cat3
new file mode 100644
index 00000000000..31685510537
--- /dev/null
+++ b/storage/ndb/src/common/util/getarg.cat3
@@ -0,0 +1,237 @@
+GETARG(3) OpenBSD Programmer's Manual GETARG(3)
+
+NNAAMMEE
+ ggeettaarrgg, aarrgg__pprriinnttuussaaggee - collect command line options
+
+SSYYNNOOPPSSIISS
+ ##iinncclluuddee <<ggeettaarrgg..hh>>
+
+
+ _i_n_t
+ ggeettaarrgg(_s_t_r_u_c_t _g_e_t_a_r_g_s _*_a_r_g_s, _s_i_z_e___t _n_u_m___a_r_g_s, _i_n_t _a_r_g_c, _c_h_a_r _*_*_a_r_g_v,
+ _i_n_t _*_o_p_t_i_n_d);
+
+
+ _v_o_i_d
+ aarrgg__pprriinnttuussaaggee(_s_t_r_u_c_t _g_e_t_a_r_g_s _*_a_r_g_s, _s_i_z_e___t _n_u_m___a_r_g_s,
+ _c_o_n_s_t _c_h_a_r _*_p_r_o_g_n_a_m_e, _c_o_n_s_t _c_h_a_r _*_e_x_t_r_a___s_t_r_i_n_g);
+
+
+DDEESSCCRRIIPPTTIIOONN
+ ggeettaarrgg() collects any command line options given to a program in an easi­
+ ly used way. aarrgg__pprriinnttuussaaggee() pretty-prints the available options, with
+ a short help text.
+
+ _a_r_g_s is the option specification to use, and it's an array of _s_t_r_u_c_t
+ _g_e_t_a_r_g_s elements. _n_u_m___a_r_g_s is the size of _a_r_g_s (in elements). _a_r_g_c and
+ _a_r_g_v are the argument count and argument vector to extract option from.
+ _o_p_t_i_n_d is a pointer to an integer where the index to the last processed
+ argument is stored, it must be initialised to the first index (minus one)
+ to process (normally 0) before the first call.
+
+ _a_r_g___p_r_i_n_t_u_s_a_g_e take the same _a_r_g_s and _n_u_m___a_r_g_s as getarg; _p_r_o_g_n_a_m_e _i_s _t_h_e
+ _n_a_m_e _o_f _t_h_e _p_r_o_g_r_a_m _(_t_o _b_e progname0 _0progname1 _1progname2 _2progname3
+ _3progname4 _4progname5 _e_x_t_r_a___s_t_r_i_n_g is a string to print after the actual
+ options to indicate more arguments. The usefulness of this function is
+ realised only be people who has used programs that has help strings that
+ doesn't match what the code does.
+
+ The _g_e_t_a_r_g_s struct has the following elements.
+
+
+ struct getargs{
+ const char *long_name;
+ char short_name;
+ enum { arg_integer,
+ arg_string,
+ arg_flag,
+ arg_negative_flag,
+ arg_strings,
+ arg_double,
+ arg_collect
+ } type;
+ void *value;
+ const char *help;
+ const char *arg_help;
+ };
+
+ _l_o_n_g___n_a_m_e is the long name of the option, it can be NULL, if you don't
+ want a long name. _s_h_o_r_t___n_a_m_e is the characted to use as short option, it
+ can be zero. If the option has a value the _v_a_l_u_e field gets filled in
+ with that value interpreted as specified by the _t_y_p_e field. _h_e_l_p is a
+ longer help string for the option as a whole, if it's NULL the help text
+ for the option is omitted (but it's still displayed in the synopsis).
+ _a_r_g___h_e_l_p is a description of the argument, if NULL a default value will
+ be used, depending on the type of the option:
+
+
+ arg_integer the argument is a signed integer, and _v_a_l_u_e should
+ point to an _i_n_t.
+
+ _a_r_g___s_t_r_i_n_g the argument is a string, and _v_a_l_u_e should point to a
+ _c_h_a_r_*.
+
+ _a_r_g___f_l_a_g the argument is a flag, and _v_a_l_u_e should point to a
+ _i_n_t. It gets filled in with either zero or one, de­
+ pending on how the option is given, the normal case
+ beeing one. Note that if the option isn't given, the
+ value isn't altered, so it should be initialised to
+ some useful default.
+
+ _a_r_g___n_e_g_a_t_i_v_e___f_l_a_g this is the same as _a_r_g___f_l_a_g but it reverses the mean­
+ ing of the flag (a given short option clears the
+ flag), and the synopsis of a long option is negated.
+
+ _a_r_g___s_t_r_i_n_g_s the argument can be given multiple times, and the val­
+ ues are collected in an array; _v_a_l_u_e should be a
+ pointer to a _s_t_r_u_c_t _g_e_t_a_r_g___s_t_r_i_n_g_s structure, which
+ holds a length and a string pointer.
+
+ _a_r_g___d_o_u_b_l_e argument is a double precision floating point value,
+ and _v_a_l_u_e should point to a _d_o_u_b_l_e.
+
+ _a_r_g___c_o_l_l_e_c_t allows more fine-grained control of the option parsing
+ process. _v_a_l_u_e should be a pointer to a
+ _g_e_t_a_r_g___c_o_l_l_e_c_t___i_n_f_o structure:
+
+ typedef int (*getarg_collect_func)(int short_opt,
+ int argc,
+ char **argv,
+ int *optind,
+ int *optarg,
+ void *data);
+
+ typedef struct getarg_collect_info {
+ getarg_collect_func func;
+ void *data;
+ } getarg_collect_info;
+
+ With the _f_u_n_c member set to a function to call, and
+ _d_a_t_a to some application specific data. The parameters
+ to the collect function are:
+
+ _s_h_o_r_t___f_l_a_g non-zero if this call is via a short option
+ flag, zero otherwise
+
+ _a_r_g_c, _a_r_g_v the whole argument list
+
+ _o_p_t_i_n_d pointer to the index in argv where the flag is
+
+ _o_p_t_a_r_g pointer to the index in argv[*optind] where the
+ flag name starts
+
+ _d_a_t_a application specific data
+
+ You can modify _*_o_p_t_i_n_d, and _*_o_p_t_a_r_g, but to do this
+ correct you (more or less) have to know about the in­
+ ner workings of getarg.
+
+ You can skip parts of arguments by increasing _*_o_p_t_a_r_g
+ (you could implement the --zz_3 set of flags from ggzziipp
+ with this), or whole argument strings by increasing
+ _*_o_p_t_i_n_d (let's say you want a flag --cc _x _y _z to specify
+ a coordinate); if you also have to set _*_o_p_t_a_r_g to a
+ sane value.
+
+ The collect function should return one of
+ ARG_ERR_NO_MATCH, ARG_ERR_BAD_ARG, ARG_ERR_NO_ARG on
+ error, zero otherwise.
+
+ For your convenience there is a function,
+ ggeettaarrgg__ooppttaarrgg(), that returns the traditional argument
+ string, and you pass it all arguments, sans data, that
+ where given to the collection function.
+
+ Don't use this more this unless you absolutely have
+ to.
+
+ Option parsing is similar to what getopt uses. Short options without ar­
+ guments can be compressed (--xxyyzz is the same as --xx --yy --zz), and short op­
+ tions with arguments take these as either the rest of the argv-string or
+ as the next option (--oo_f_o_o, or --oo _f_o_o).
+
+ Long option names are prefixed with -- (double dash), and the value with
+ a = (equal), ----ffoooo==_b_a_r. Long option flags can either be specified as they
+ are (----hheellpp), or with an (boolean parsable) option (----hheellpp==_y_e_s,
+ ----hheellpp==_t_r_u_e, or similar), or they can also be negated (----nnoo--hheellpp is the
+ same as ----hheellpp==no), and if you're really confused you can do it multiple
+ times (----nnoo--nnoo--hheellpp==_f_a_l_s_e, or even ----nnoo--nnoo--hheellpp==_m_a_y_b_e).
+
+EEXXAAMMPPLLEE
+ #include <stdio.h>
+ #include <string.h>
+ #include <getarg.h>
+
+ char *source = "Ouagadougou";
+ char *destination;
+ int weight;
+ int include_catalog = 1;
+ int help_flag;
+
+ struct getargs args[] = {
+ { "source", 's', arg_string, &source,
+ "source of shippment", "city" },
+ { "destination", 'd', arg_string, &destination,
+ "destination of shippment", "city" },
+ { "weight", 'w', arg_integer, &weight,
+ "weight of shippment", "tons" },
+ { "catalog", 'c', arg_negative_flag, &include_catalog,
+ "include product catalog" },
+ { "help", 'h', arg_flag, &help_flag }
+ };
+
+ int num_args = sizeof(args) / sizeof(args[0]); /* number of elements in args */
+
+ const char *progname = "ship++";
+
+ int
+ main(int argc, char **argv)
+ {
+ int optind = 0;
+ if (getarg(args, num_args, argc, argv, &optind)) {
+ arg_printusage(args, num_args, progname, "stuff...");
+ exit (1);
+ }
+ if (help_flag) {
+ arg_printusage(args, num_args, progname, "stuff...");
+ exit (0);
+ }
+ if (destination == NULL) {
+ fprintf(stderr, "%s: must specify destination0, progname);
+ exit(1);
+ }
+ if (strcmp(source, destination) == 0) {
+ fprintf(stderr, "%s: destination must be different from source0);
+ exit(1);
+ }
+ /* include more stuff here ... */
+ exit(2);
+ }
+
+ The output help output from this program looks like this:
+
+ $ ship++ --help
+ Usage: ship++ [--source=city] [-s city] [--destination=city] [-d city]
+ [--weight=tons] [-w tons] [--no-catalog] [-c] [--help] [-h] stuff...
+ -s city, --source=city source of shippment
+ -d city, --destination=city destination of shippment
+ -w tons, --weight=tons weight of shippment
+ -c, --no-catalog include product catalog
+
+
+BBUUGGSS
+ It should be more flexible, so it would be possible to use other more
+ complicated option syntaxes, such as what ps(1), and tar(1), uses, or the
+ AFS model where you can skip the flag names as long as the options come
+ in the correct order.
+
+ Options with multiple arguments should be handled better.
+
+ Should be integreated with SL.
+
+ It's very confusing that the struct you pass in is called getargS.
+
+SSEEEE AALLSSOO
+ getopt(3)
+
+ ROKEN September 24, 1999 4
diff --git a/storage/ndb/src/common/util/md5_hash.cpp b/storage/ndb/src/common/util/md5_hash.cpp
new file mode 100644
index 00000000000..d4eedbc40fb
--- /dev/null
+++ b/storage/ndb/src/common/util/md5_hash.cpp
@@ -0,0 +1,239 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include "md5_hash.hpp"
+
+#ifdef WORDS_BIGENDIAN
+#define HIGHFIRST 1
+#endif
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * The code has been modified by Mikael Ronstroem to handle
+ * calculating a hash value of a key that is always a multiple
+ * of 4 bytes long. Word 0 of the calculated 4-word hash value
+ * is returned as the hash value.
+ */
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ Uint32 t;
+ do {
+ t = (Uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(Uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(Uint32 buf[4], Uint32 const in[16])
+{
+ register Uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void md5_hash(Uint32 result[4], const Uint64* keybuf, Uint32 no_of_32_words)
+{
+ /**
+ * This is the external interface of the module
+ * It is assumed that keybuf is placed on 8 byte
+ * alignment.
+ */
+ Uint32 i;
+ Uint32 buf[4];
+ Uint64 transform64_buf[8];
+ Uint32* transform32_buf;
+ Uint32 len = no_of_32_words << 2;
+ const Uint64* key64buf = (const Uint64*)keybuf;
+ const Uint32* key32buf = (const Uint32*)keybuf;
+
+ transform32_buf = (Uint32*)&transform64_buf[0];
+ buf[0] = 0x67452301;
+ buf[1] = 0xefcdab89;
+ buf[2] = 0x98badcfe;
+ buf[3] = 0x10325476;
+
+ while (no_of_32_words >= 16) {
+ transform64_buf[0] = key64buf[0];
+ transform64_buf[1] = key64buf[1];
+ transform64_buf[2] = key64buf[2];
+ transform64_buf[3] = key64buf[3];
+ transform64_buf[4] = key64buf[4];
+ transform64_buf[5] = key64buf[5];
+ transform64_buf[6] = key64buf[6];
+ transform64_buf[7] = key64buf[7];
+ no_of_32_words -= 16;
+ key64buf += 8;
+ byteReverse((unsigned char *)transform32_buf, 16);
+ MD5Transform(buf, transform32_buf);
+ }
+
+ key32buf = (const Uint32*)key64buf;
+ transform64_buf[0] = 0;
+ transform64_buf[1] = 0;
+ transform64_buf[2] = 0;
+ transform64_buf[3] = 0;
+ transform64_buf[4] = 0;
+ transform64_buf[5] = 0;
+ transform64_buf[6] = 0;
+ transform64_buf[7] = (Uint64)len;
+
+ for (i = 0; i < no_of_32_words; i++)
+ transform32_buf[i] = key32buf[i];
+ transform32_buf[no_of_32_words] = 0x80000000;
+
+ if (no_of_32_words < 14) {
+ byteReverse((unsigned char *)transform32_buf, 16);
+ MD5Transform(buf, transform32_buf);
+ } else {
+ if (no_of_32_words == 14)
+ transform32_buf[15] = 0;
+ MD5Transform(buf, transform32_buf);
+ transform64_buf[0] = 0;
+ transform64_buf[1] = 0;
+ transform64_buf[2] = 0;
+ transform64_buf[3] = 0;
+ transform64_buf[4] = 0;
+ transform64_buf[5] = 0;
+ transform64_buf[6] = 0;
+ transform64_buf[7] = (Uint64)len;
+ byteReverse((unsigned char *)transform32_buf, 16);
+ MD5Transform(buf, transform32_buf);
+ }
+
+ result[0] = buf[0];
+ result[1] = buf[1];
+ result[2] = buf[2];
+ result[3] = buf[3];
+}
+
diff --git a/storage/ndb/src/common/util/ndb_init.c b/storage/ndb/src/common/util/ndb_init.c
new file mode 100644
index 00000000000..f3aa734d7f9
--- /dev/null
+++ b/storage/ndb/src/common/util/ndb_init.c
@@ -0,0 +1,35 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_sys.h>
+
+int
+ndb_init()
+{
+ if (my_init()) {
+ const char* err = "my_init() failed - exit\n";
+ write(2, err, strlen(err));
+ exit(1);
+ }
+ return 0;
+}
+
+void
+ndb_end(int flags)
+{
+ my_end(flags);
+}
diff --git a/storage/ndb/src/common/util/new.cpp b/storage/ndb/src/common/util/new.cpp
new file mode 100644
index 00000000000..901f74bf979
--- /dev/null
+++ b/storage/ndb/src/common/util/new.cpp
@@ -0,0 +1,43 @@
+
+#include <ndb_global.h>
+#include <NdbMem.h>
+
+extern "C" {
+ void (* ndb_new_handler)() = 0;
+}
+
+#ifdef USE_MYSYS_NEW
+
+void *operator new (size_t sz)
+{
+ void * p = NdbMem_Allocate(sz ? sz : 1);
+ if(p)
+ return p;
+ if(ndb_new_handler)
+ (* ndb_new_handler)();
+ abort();
+}
+
+void *operator new[] (size_t sz)
+{
+ void * p = (void *) NdbMem_Allocate(sz ? sz : 1);
+ if(p)
+ return p;
+ if(ndb_new_handler)
+ (* ndb_new_handler)();
+ abort();
+}
+
+void operator delete (void *ptr)
+{
+ if (ptr)
+ NdbMem_Free(ptr);
+}
+
+void operator delete[] (void *ptr) throw ()
+{
+ if (ptr)
+ NdbMem_Free(ptr);
+}
+
+#endif // USE_MYSYS_NEW
diff --git a/storage/ndb/src/common/util/random.c b/storage/ndb/src/common/util/random.c
new file mode 100644
index 00000000000..21235763793
--- /dev/null
+++ b/storage/ndb/src/common/util/random.c
@@ -0,0 +1,284 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <random.h>
+
+/***************************************************************
+* L O C A L C O N S T A N T S *
+***************************************************************/
+
+/***************************************************************
+* L O C A L D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef struct {
+ unsigned short int x[3]; /* Current state. */
+ unsigned short int a[3]; /* Factor in congruential formula. */
+ unsigned short int c; /* Additive const. in congruential formula. */
+ int init; /* Flag for initializing. */
+}DRand48Data;
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static void shuffleSequence(RandomSequence *seq);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static DRand48Data dRand48Data;
+
+/***************************************************************
+* P U B L I C D A T A *
+***************************************************************/
+
+
+/***************************************************************
+****************************************************************
+* L O C A L F U N C T I O N S C O D E S E C T I O N *
+****************************************************************
+***************************************************************/
+
+static void localRandom48Init(long int seedval, DRand48Data *buffer)
+{
+ /* The standards say we only have 32 bits. */
+ if (sizeof (long int) > 4)
+ seedval &= 0xffffffffl;
+
+#if USHRT_MAX == 0xffffU
+ buffer->x[2] = seedval >> 16;
+ buffer->x[1] = seedval & 0xffffl;
+ buffer->x[0] = 0x330e;
+
+ buffer->a[2] = 0x5;
+ buffer->a[1] = 0xdeec;
+ buffer->a[0] = 0xe66d;
+#else
+ buffer->x[2] = seedval;
+ buffer->x[1] = 0x330e0000UL;
+ buffer->x[0] = 0;
+
+ buffer->a[2] = 0x5deecUL;
+ buffer->a[1] = 0xe66d0000UL;
+ buffer->a[0] = 0;
+#endif
+
+ buffer->c = 0xb;
+ buffer->init = 1;
+}
+
+static void localRandom48(DRand48Data *buffer, long int *result)
+{
+ Uint64 X;
+ Uint64 a;
+ Uint64 loc_result;
+
+ /*--------------------------------------*/
+ /* Initialize buffer, if not yet done. */
+ /*--------------------------------------*/
+ if (!buffer->init) {
+#if (USHRT_MAX == 0xffffU)
+ buffer->a[2] = 0x5;
+ buffer->a[1] = 0xdeec;
+ buffer->a[0] = 0xe66d;
+#else
+ buffer->a[2] = 0x5deecUL;
+ buffer->a[1] = 0xe66d0000UL;
+ buffer->a[0] = 0;
+#endif
+ buffer->c = 0xb;
+ buffer->init = 1;
+ }
+
+ /* Do the real work. We choose a data type which contains at least
+ 48 bits. Because we compute the modulus it does not care how
+ many bits really are computed. */
+
+ if (sizeof (unsigned short int) == 2) {
+ X = (Uint64)buffer->x[2] << 32 |
+ (Uint64)buffer->x[1] << 16 |
+ buffer->x[0];
+ a = ((Uint64)buffer->a[2] << 32 |
+ (Uint64)buffer->a[1] << 16 |
+ buffer->a[0]);
+
+ loc_result = X * a + buffer->c;
+
+ buffer->x[0] = loc_result & 0xffff;
+ buffer->x[1] = (loc_result >> 16) & 0xffff;
+ buffer->x[2] = (loc_result >> 32) & 0xffff;
+ }
+ else {
+ X = (Uint64)buffer->x[2] << 16 |
+ buffer->x[1] >> 16;
+ a = (Uint64)buffer->a[2] << 16 |
+ buffer->a[1] >> 16;
+
+ loc_result = X * a + buffer->c;
+
+ buffer->x[0] = loc_result >> 16 & 0xffffffffl;
+ buffer->x[1] = loc_result << 16 & 0xffff0000l;
+ }
+
+ /*--------------------*/
+ /* Store the result. */
+ /*--------------------*/
+ if (sizeof (unsigned short int) == 2)
+ *result = buffer->x[2] << 15 | buffer->x[1] >> 1;
+ else
+ *result = buffer->x[2] >> 1;
+}
+
+static void shuffleSequence(RandomSequence *seq)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int tmp;
+
+ if( !seq ) return;
+
+ for(i = 0; i < seq->length; i++ ) {
+ j = myRandom48(seq->length);
+ if( i != j ) {
+ tmp = seq->values[i];
+ seq->values[i] = seq->values[j];
+ seq->values[j] = tmp;
+ }
+ }
+}
+
+
+/***************************************************************
+****************************************************************
+* P U B L I C F U N C T I O N S C O D E S E C T I O N *
+****************************************************************
+***************************************************************/
+
+
+double getTps(unsigned int count, double timeValue)
+{
+ double f;
+
+ if( timeValue != 0.0 )
+ f = count / timeValue;
+ else
+ f = 0.0;
+
+ return(f);
+}
+
+/*----------------------------*/
+/* Random Sequences Functions */
+/*----------------------------*/
+int initSequence(RandomSequence *seq, SequenceValues *inputValues)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int totalLength;
+ unsigned int index;
+
+ if( !seq || !inputValues ) return(-1);
+
+ /*------------------------------------*/
+ /* Find the total length of the array */
+ /*------------------------------------*/
+ totalLength = 0;
+
+ for(i = 0; inputValues[i].length != 0; i++)
+ totalLength += inputValues[i].length;
+
+ if( totalLength == 0 ) return(-1);
+
+ seq->length = totalLength;
+ seq->values = calloc(totalLength, sizeof(unsigned int));
+
+ if( seq->values == 0 ) return(-1);
+
+ /*----------------------*/
+ /* set the array values */
+ /*----------------------*/
+ index = 0;
+
+ for(i = 0; inputValues[i].length != 0; i++) {
+ for(j = 0; j < inputValues[i].length; j++ ) {
+ seq->values[index] = inputValues[i].value;
+ index++;
+ }
+ }
+
+ shuffleSequence(seq);
+
+ seq->currentIndex = 0;
+
+ return(0);
+}
+
+unsigned int getNextRandom(RandomSequence *seq)
+{
+ unsigned int nextValue;
+
+ nextValue = seq->values[seq->currentIndex];
+
+ seq->currentIndex++;
+
+ if(seq->currentIndex == seq->length){
+ seq->currentIndex = 0;
+ shuffleSequence(seq);
+ }
+
+ return nextValue;
+}
+
+void printSequence(RandomSequence *seq, unsigned int numPerRow)
+{
+ unsigned int i;
+
+ if( !seq ) return;
+
+ for(i = 0; i<seq->length; i++) {
+ ndbout_c("%d ", seq->values[i]);
+
+ if((i+1) % numPerRow == 0)
+ ndbout_c("");
+ }
+
+ if(i % numPerRow != 0)
+ ndbout_c("");
+}
+
+void myRandom48Init(long int seedval)
+{
+ localRandom48Init(seedval, &dRand48Data);
+}
+
+long int myRandom48(unsigned int maxValue)
+{
+ long int result;
+
+ localRandom48(&dRand48Data, &result);
+
+ return(result % maxValue);
+}
diff --git a/storage/ndb/src/common/util/socket_io.cpp b/storage/ndb/src/common/util/socket_io.cpp
new file mode 100644
index 00000000000..83a546de773
--- /dev/null
+++ b/storage/ndb/src/common/util/socket_io.cpp
@@ -0,0 +1,281 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbTCP.h>
+#include <socket_io.h>
+#include <NdbOut.hpp>
+
+extern "C"
+int
+read_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ char * buf, int buflen){
+ if(buflen < 1)
+ return 0;
+
+ fd_set readset;
+ FD_ZERO(&readset);
+ FD_SET(socket, &readset);
+
+ struct timeval timeout;
+ timeout.tv_sec = (timeout_millis / 1000);
+ timeout.tv_usec = (timeout_millis % 1000) * 1000;
+
+ const int selectRes = select(socket + 1, &readset, 0, 0, &timeout);
+ if(selectRes == 0)
+ return 0;
+
+ if(selectRes == -1){
+ return -1;
+ }
+
+ return recv(socket, &buf[0], buflen, 0);
+}
+
+extern "C"
+int
+readln_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ char * buf, int buflen){
+ if(buflen <= 1)
+ return 0;
+
+ fd_set readset;
+ FD_ZERO(&readset);
+ FD_SET(socket, &readset);
+
+ struct timeval timeout;
+ timeout.tv_sec = (timeout_millis / 1000);
+ timeout.tv_usec = (timeout_millis % 1000) * 1000;
+
+ const int selectRes = select(socket + 1, &readset, 0, 0, &timeout);
+ if(selectRes == 0)
+ return 0;
+
+ if(selectRes == -1){
+ return -1;
+ }
+
+ int pos = 0; buf[pos] = 0;
+ while(true){
+ const int t = recv(socket, &buf[pos], 1, 0);
+ if(t != 1){
+ return -1;
+ }
+ if(buf[pos] == '\n'){
+ buf[pos] = 0;
+
+ if(pos > 0 && buf[pos-1] == '\r'){
+ pos--;
+ buf[pos] = 0;
+ }
+
+ return pos;
+ }
+ pos++;
+ if(pos == (buflen - 1)){
+ buf[pos] = 0;
+ return buflen;
+ }
+
+ FD_ZERO(&readset);
+ FD_SET(socket, &readset);
+ timeout.tv_sec = (timeout_millis / 1000);
+ timeout.tv_usec = (timeout_millis % 1000) * 1000;
+ const int selectRes = select(socket + 1, &readset, 0, 0, &timeout);
+ if(selectRes != 1){
+ return -1;
+ }
+ }
+}
+
+extern "C"
+int
+write_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ const char buf[], int len){
+ fd_set writeset;
+ FD_ZERO(&writeset);
+ FD_SET(socket, &writeset);
+ struct timeval timeout;
+ timeout.tv_sec = (timeout_millis / 1000);
+ timeout.tv_usec = (timeout_millis % 1000) * 1000;
+
+ const int selectRes = select(socket + 1, 0, &writeset, 0, &timeout);
+ if(selectRes != 1){
+ return -1;
+ }
+
+ const char * tmp = &buf[0];
+ while(len > 0){
+ const int w = send(socket, tmp, len, 0);
+ if(w == -1){
+ return -1;
+ }
+ len -= w;
+ tmp += w;
+
+ if(len == 0)
+ break;
+
+ FD_ZERO(&writeset);
+ FD_SET(socket, &writeset);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ const int selectRes = select(socket + 1, 0, &writeset, 0, &timeout);
+ if(selectRes != 1){
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+extern "C"
+int
+print_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ const char * fmt, ...){
+ va_list ap;
+ va_start(ap, fmt);
+ int ret = vprint_socket(socket, timeout_millis, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+extern "C"
+int
+println_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ const char * fmt, ...){
+ va_list ap;
+ va_start(ap, fmt);
+ int ret = vprintln_socket(socket, timeout_millis, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+extern "C"
+int
+vprint_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ const char * fmt, va_list ap){
+ char buf[1000];
+ char *buf2 = buf;
+ size_t size;
+
+ if (fmt != 0 && fmt[0] != 0) {
+ size = BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ /* Check if the output was truncated */
+ if(size > sizeof(buf)) {
+ buf2 = (char *)malloc(size);
+ if(buf2 == NULL)
+ return -1;
+ BaseString::vsnprintf(buf2, size, fmt, ap);
+ }
+ } else
+ return 0;
+
+ int ret = write_socket(socket, timeout_millis, buf2, size);
+ if(buf2 != buf)
+ free(buf2);
+ return ret;
+}
+
+extern "C"
+int
+vprintln_socket(NDB_SOCKET_TYPE socket, int timeout_millis,
+ const char * fmt, va_list ap){
+ char buf[1000];
+ char *buf2 = buf;
+ size_t size;
+
+ if (fmt != 0 && fmt[0] != 0) {
+ size = BaseString::vsnprintf(buf, sizeof(buf), fmt, ap)+1;// extra byte for '/n'
+ /* Check if the output was truncated */
+ if(size > sizeof(buf)) {
+ buf2 = (char *)malloc(size);
+ if(buf2 == NULL)
+ return -1;
+ BaseString::vsnprintf(buf2, size, fmt, ap);
+ }
+ } else {
+ size = 1;
+ }
+ buf2[size-1]='\n';
+
+ int ret = write_socket(socket, timeout_millis, buf2, size);
+ if(buf2 != buf)
+ free(buf2);
+ return ret;
+}
+
+#ifdef NDB_WIN32
+
+class INIT_WINSOCK2
+{
+public:
+ INIT_WINSOCK2(void);
+ ~INIT_WINSOCK2(void);
+
+private:
+ bool m_bAcceptable;
+};
+
+INIT_WINSOCK2 g_init_winsock2;
+
+INIT_WINSOCK2::INIT_WINSOCK2(void)
+: m_bAcceptable(false)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD( 2, 2 );
+
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if ( err != 0 ) {
+ /* Tell the user that we could not find a usable */
+ /* WinSock DLL. */
+ m_bAcceptable = false;
+ }
+
+ /* Confirm that the WinSock DLL supports 2.2.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 2.2 in addition to 2.2, it will still return */
+ /* 2.2 in wVersion since that is the version we */
+ /* requested. */
+
+ if ( LOBYTE( wsaData.wVersion ) != 2 ||
+ HIBYTE( wsaData.wVersion ) != 2 ) {
+ /* Tell the user that we could not find a usable */
+ /* WinSock DLL. */
+ WSACleanup( );
+ m_bAcceptable = false;
+ }
+
+ /* The WinSock DLL is acceptable. Proceed. */
+ m_bAcceptable = true;
+}
+
+INIT_WINSOCK2::~INIT_WINSOCK2(void)
+{
+ if(m_bAcceptable)
+ {
+ m_bAcceptable = false;
+ WSACleanup();
+ }
+}
+
+#endif
+
diff --git a/storage/ndb/src/common/util/strdup.c b/storage/ndb/src/common/util/strdup.c
new file mode 100644
index 00000000000..d8f4d99bd28
--- /dev/null
+++ b/storage/ndb/src/common/util/strdup.c
@@ -0,0 +1,28 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#ifndef HAVE_STRDUP
+char *
+strdup(const char *s){
+ void *p2;
+ if ((p2 = malloc(strlen(s)+1)))
+ strcpy(p2, s);
+ return p2;
+}
+#endif
diff --git a/storage/ndb/src/common/util/testConfigValues/Makefile b/storage/ndb/src/common/util/testConfigValues/Makefile
new file mode 100644
index 00000000000..5b7400f5ee3
--- /dev/null
+++ b/storage/ndb/src/common/util/testConfigValues/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE := util
+
+BIN_TARGET := testConfigValues
+BIN_TARGET_ARCHIVES := portlib general
+
+SOURCES := testConfigValues.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/include/util)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/common/util/testConfigValues/testConfigValues.cpp b/storage/ndb/src/common/util/testConfigValues/testConfigValues.cpp
new file mode 100644
index 00000000000..362deb1ddad
--- /dev/null
+++ b/storage/ndb/src/common/util/testConfigValues/testConfigValues.cpp
@@ -0,0 +1,122 @@
+#include <ConfigValues.hpp>
+#include <NdbOut.hpp>
+#include <stdlib.h>
+#include <string.h>
+
+#define CF_NODES 1
+#define CF_LOG_PAGES 2
+#define CF_MEM_PAGES 3
+#define CF_START_TO 4
+#define CF_STOP_TO 5
+
+void print(Uint32 i, ConfigValues::ConstIterator & cf){
+ ndbout_c("---");
+ for(Uint32 j = 2; j<=7; j++){
+ switch(cf.getTypeOf(j)){
+ case ConfigValues::IntType:
+ ndbout_c("Node %d : CFG(%d) : %d",
+ i, j, cf.get(j, 999));
+ break;
+ case ConfigValues::Int64Type:
+ ndbout_c("Node %d : CFG(%d) : %lld (64)",
+ i, j, cf.get64(j, 999));
+ break;
+ case ConfigValues::StringType:
+ ndbout_c("Node %d : CFG(%d) : %s",
+ i, j, cf.get(j, "<NOT FOUND>"));
+ break;
+ default:
+ ndbout_c("Node %d : CFG(%d) : TYPE: %d",
+ i, j, cf.getTypeOf(j));
+ }
+ }
+}
+
+void print(Uint32 i, ConfigValues & _cf){
+ ConfigValues::ConstIterator cf(_cf);
+ print(i, cf);
+}
+
+void
+print(ConfigValues & _cf){
+ ConfigValues::ConstIterator cf(_cf);
+ Uint32 i = 0;
+ while(cf.openSection(CF_NODES, i)){
+ print(i, cf);
+ cf.closeSection();
+ i++;
+ }
+}
+
+inline
+void
+require(bool b){
+ if(!b)
+ abort();
+}
+
+int
+main(void){
+
+ {
+ ConfigValuesFactory cvf(10, 20);
+ cvf.openSection(1, 0);
+ cvf.put(2, 12);
+ cvf.put64(3, 13);
+ cvf.put(4, 14);
+ cvf.put64(5, 15);
+ cvf.put(6, "Keso");
+ cvf.put(7, "Kent");
+ cvf.closeSection();
+
+ cvf.openSection(1, 1);
+ cvf.put(2, 22);
+ cvf.put64(3, 23);
+ cvf.put(4, 24);
+ cvf.put64(5, 25);
+ cvf.put(6, "Kalle");
+ cvf.put(7, "Anka");
+ cvf.closeSection();
+
+ ndbout_c("-- print --");
+ print(* cvf.m_cfg);
+
+ cvf.shrink();
+ ndbout_c("shrink\n-- print --");
+ print(* cvf.m_cfg);
+ cvf.expand(10, 10);
+ ndbout_c("expand\n-- print --");
+ print(* cvf.m_cfg);
+
+ ndbout_c("packed size: %d", cvf.m_cfg->getPackedSize());
+
+ ConfigValues::ConstIterator iter(* cvf.m_cfg);
+ iter.openSection(CF_NODES, 0);
+ ConfigValues * cfg2 = ConfigValuesFactory::extractCurrentSection(iter);
+ print(99, * cfg2);
+
+ cvf.shrink();
+ ndbout_c("packed size: %d", cfg2->getPackedSize());
+
+ UtilBuffer buf;
+ Uint32 l1 = cvf.m_cfg->pack(buf);
+ Uint32 l2 = cvf.m_cfg->getPackedSize();
+ require(l1 == l2);
+
+ ConfigValuesFactory cvf2;
+ require(cvf2.unpack(buf));
+ UtilBuffer buf2;
+ cvf2.shrink();
+ Uint32 l3 = cvf2.m_cfg->pack(buf2);
+ require(l1 == l3);
+
+ ndbout_c("unpack\n-- print --");
+ print(* cvf2.m_cfg);
+
+ cfg2->~ConfigValues();;
+ cvf.m_cfg->~ConfigValues();
+ free(cfg2);
+ free(cvf.m_cfg);
+ }
+ return 0;
+}
diff --git a/storage/ndb/src/common/util/testProperties/Makefile b/storage/ndb/src/common/util/testProperties/Makefile
new file mode 100644
index 00000000000..343c07a49e7
--- /dev/null
+++ b/storage/ndb/src/common/util/testProperties/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := util
+
+BIN_TARGET := keso
+
+SOURCES := testProperties.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/common/util/testProperties/testProperties.cpp b/storage/ndb/src/common/util/testProperties/testProperties.cpp
new file mode 100644
index 00000000000..e445f7ca3e4
--- /dev/null
+++ b/storage/ndb/src/common/util/testProperties/testProperties.cpp
@@ -0,0 +1,195 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "Properties.hpp"
+#include <NdbOut.hpp>
+
+#include "uucode.h"
+
+bool
+writeToFile(const Properties & p, const char * fname, bool uu = true){
+ Uint32 sz = p.getPackedSize();
+ char * buffer = (char*)malloc(sz);
+
+ FILE * f = fopen(fname, "wb");
+ bool res = p.pack((Uint32*)buffer);
+ if(res != true){
+ ndbout << "Error packing" << endl;
+ ndbout << "p.getPropertiesErrno() = " << p.getPropertiesErrno() << endl;
+ ndbout << "p.getOSErrno() = " << p.getOSErrno() << endl;
+ }
+ if(uu)
+ uuencode(buffer, sz, f);
+ else {
+ fwrite(buffer, 1, sz, f);
+ }
+
+ fclose(f);
+ free(buffer);
+ return res;
+}
+
+bool
+readFromFile(Properties & p, const char *fname, bool uu = true){
+ Uint32 sz = 30000;
+ char * buffer = (char*)malloc(sz);
+ FILE * f = fopen(fname, "rb");
+ if(uu)
+ uudecode(f, buffer, sz);
+ else
+ fread(buffer, 1, sz, f);
+ fclose(f);
+ bool res = p.unpack((Uint32*)buffer, sz);
+ if(res != true){
+ ndbout << "Error unpacking" << endl;
+ ndbout << "p.getPropertiesErrno() = " << p.getPropertiesErrno() << endl;
+ ndbout << "p.getOSErrno() = " << p.getOSErrno() << endl;
+ }
+ free(buffer);
+ return res;
+}
+
+void putALot(Properties & tmp){
+ int i = 123;
+ tmp.put("LockPagesInMainMemory", i++);
+ tmp.put("SleepWhenIdle", i++);
+ tmp.put("NoOfSignalsToExecuteBetweenCommunicationInterfacePoll", i++);
+ tmp.put("TimeBetweenWatchDogCheck", i++);
+ tmp.put("StopOnError", i++);
+
+ tmp.put("MaxNoOfConcurrentOperations", i++);
+ tmp.put("MaxNoOfConcurrentTransactions", i++);
+ tmp.put("MemorySpaceIndexes", i++);
+ tmp.put("MemorySpaceTuples", i++);
+ tmp.put("MemoryDiskPages", i++);
+ tmp.put("NoOfFreeDiskClusters", i++);
+ tmp.put("NoOfDiskClusters", i++);
+
+ tmp.put("TimeToWaitAlive", i++);
+ tmp.put("HeartbeatIntervalDbDb", i++);
+ tmp.put("HeartbeatIntervalDbApi", i++);
+ tmp.put("TimeBetweenInactiveTransactionAbortCheck", i++);
+
+ tmp.put("TimeBetweenLocalCheckpoints", i++);
+ tmp.put("TimeBetweenGlobalCheckpoints", i++);
+ tmp.put("NoOfFragmentLogFiles", i++);
+ tmp.put("NoOfConcurrentCheckpointsDuringRestart", i++);
+ tmp.put("TransactionInactiveTimeBeforeAbort", i++);
+ tmp.put("NoOfConcurrentProcessesHandleTakeover", i++);
+
+ tmp.put("NoOfConcurrentCheckpointsAfterRestart", i++);
+
+ tmp.put("NoOfDiskPagesToDiskDuringRestartTUP", i++);
+ tmp.put("NoOfDiskPagesToDiskAfterRestartTUP", i++);
+ tmp.put("NoOfDiskPagesToDiskDuringRestartACC", i++);
+ tmp.put("NoOfDiskPagesToDiskAfterRestartACC", i++);
+
+ tmp.put("NoOfDiskClustersPerDiskFile", i++);
+ tmp.put("NoOfDiskFiles", i++);
+
+ // Always found
+ tmp.put("NoOfReplicas", 33);
+ tmp.put("MaxNoOfAttributes", 34);
+ tmp.put("MaxNoOfTables", 35);
+}
+
+int
+main(void){
+ Properties p;
+
+ p.put("Kalle", 1);
+ p.put("Ank1", "anka");
+ p.put("Ank2", "anka");
+ p.put("Ank3", "anka");
+ p.put("Ank4", "anka");
+ putALot(p);
+
+ Properties tmp;
+ tmp.put("Type", "TCP");
+ tmp.put("OwnNodeId", 1);
+ tmp.put("RemoteNodeId", 2);
+ tmp.put("OwnHostName", "local");
+ tmp.put("RemoteHostName", "remote");
+
+ tmp.put("SendSignalId", 1);
+ tmp.put("Compression", (Uint32)false);
+ tmp.put("Checksum", 1);
+
+ tmp.put64("SendBufferSize", 2000);
+ tmp.put64("MaxReceiveSize", 1000);
+
+ tmp.put("PortNumber", 1233);
+ putALot(tmp);
+
+ p.put("Connection", 1, &tmp);
+
+ p.put("NoOfConnections", 2);
+ p.put("NoOfConnection2", 2);
+
+ p.put("kalle", 3);
+ p.put("anka", "kalle");
+
+ Properties p2;
+ p2.put("kalle", "anka");
+
+ p.put("prop", &p2);
+
+ p.put("Connection", 2, &tmp);
+
+ p.put("Connection", 3, &tmp);
+
+ p.put("Connection", 4, &tmp);
+ /*
+ */
+
+ Uint32 a = 99;
+ const char * b;
+ const Properties * p3;
+ Properties * p4;
+
+ bool bb = p.get("kalle", &a);
+ bool cc = p.get("anka", &b);
+ bool dd = p.get("prop", &p3);
+ if(p.getCopy("prop", &p4))
+ delete p4;
+
+ p2.put("p2", &p2);
+
+ p.put("prop2", &p2);
+ /* */
+
+ p.print(stdout, "testing 1: ");
+
+ writeToFile(p, "A_1");
+ writeToFile(p, "B_1", false);
+
+ Properties r1;
+ readFromFile(r1, "A_1");
+ writeToFile(r1, "A_3");
+
+ //r1.print(stdout, "testing 2: ");
+ Properties r2;
+ readFromFile(r2, "A_1");
+ writeToFile(r2, "A_4");
+
+ Properties r3;
+ readFromFile(r3, "B_1", false);
+ writeToFile(r3, "A_5");
+ r3.print(stdout, "testing 3: ");
+
+ return 0;
+}
diff --git a/storage/ndb/src/common/util/testSimpleProperties/Makefile b/storage/ndb/src/common/util/testSimpleProperties/Makefile
new file mode 100644
index 00000000000..89d33fa8dd8
--- /dev/null
+++ b/storage/ndb/src/common/util/testSimpleProperties/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE := util
+
+BIN_TARGET := sp_test
+BIN_TARGET_ARCHIVES := portlib general
+
+SOURCES := sp_test.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/include/util)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/common/util/testSimpleProperties/sp_test.cpp b/storage/ndb/src/common/util/testSimpleProperties/sp_test.cpp
new file mode 100644
index 00000000000..d4052b64132
--- /dev/null
+++ b/storage/ndb/src/common/util/testSimpleProperties/sp_test.cpp
@@ -0,0 +1,95 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "SimpleProperties.hpp"
+#include <NdbOut.hpp>
+
+Uint32 page[8192];
+
+int writer();
+int reader(Uint32 *, Uint32 len);
+int unpack(Uint32 *, Uint32 len);
+
+int main(){
+ int len = writer();
+ reader(page, len);
+ unpack(page, len);
+
+ return 0;
+}
+
+int
+writer(){
+ LinearWriter w(&page[0], 8192);
+
+ w.first();
+ w.add(1, 2);
+ w.add(7, 3);
+ w.add(3, "jonas");
+ w.add(5, "0123456789");
+ w.add(7, 4);
+ w.add(3, "e cool");
+ w.add(5, "9876543210");
+
+ ndbout_c("WordsUsed = %d", w.getWordsUsed());
+
+ return w.getWordsUsed();
+}
+
+int
+reader(Uint32 * pages, Uint32 len){
+ SimplePropertiesLinearReader it(pages, len);
+
+ it.printAll(ndbout);
+ return 0;
+}
+
+struct Test {
+ Uint32 val1;
+ Uint32 val7;
+ char val3[100];
+ Test() : val1(0xFFFFFFFF), val7(0xFFFFFFFF) { sprintf(val3, "bad");}
+};
+
+static const
+SimpleProperties::SP2StructMapping
+test_map [] = {
+ { 1, offsetof(Test, val1), SimpleProperties::Uint32Value, 0, ~0 },
+ { 7, offsetof(Test, val7), SimpleProperties::Uint32Value, 0, ~0 },
+ { 3, offsetof(Test, val3), SimpleProperties::StringValue, 0, sizeof(100) },
+ { 5, 0, SimpleProperties::InvalidValue, 0, 0 }
+};
+
+static unsigned
+test_map_sz = sizeof(test_map)/sizeof(test_map[0]);
+
+int
+unpack(Uint32 * pages, Uint32 len){
+ Test test;
+ SimplePropertiesLinearReader it(pages, len);
+ SimpleProperties::UnpackStatus status;
+ while((status = SimpleProperties::unpack(it, &test, test_map, test_map_sz,
+ true, false)) == SimpleProperties::Break){
+ ndbout << "test.val1 = " << test.val1 << endl;
+ ndbout << "test.val7 = " << test.val7 << endl;
+ ndbout << "test.val3 = " << test.val3 << endl;
+ it.next();
+ }
+ assert(status == SimpleProperties::Eof);
+ return 0;
+}
diff --git a/storage/ndb/src/common/util/uucode.c b/storage/ndb/src/common/util/uucode.c
new file mode 100644
index 00000000000..da34d565153
--- /dev/null
+++ b/storage/ndb/src/common/util/uucode.c
@@ -0,0 +1,234 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+/* ENC is the basic 1 character encoding function to make a char printing */
+/* DEC is single character decode */
+#define ENC(c) ((c) ? ((c) & 077) + ' ': '`')
+#define DEC(c) (((c) - ' ') & 077)
+
+/*
+ * copy from in to out, encoding as you go along.
+ */
+void
+uuencode(const char * data, int dataLen, FILE * out)
+{
+ int ch, n;
+ const char *p = data;
+
+ fprintf(out, "begin\n");
+
+ while (dataLen > 0){
+ n = dataLen > 45 ? 45 : dataLen;
+ dataLen -= n;
+ ch = ENC(n);
+ if (putc(ch, out) == EOF)
+ break;
+ for (; n > 0; n -= 3, p += 3) {
+ char p_0 = * p;
+ char p_1 = 0;
+ char p_2 = 0;
+
+ if(n >= 2){
+ p_1 = p[1];
+ }
+ if(n >= 3){
+ p_2 = p[2];
+ }
+
+ ch = p_0 >> 2;
+ ch = ENC(ch);
+ if (putc(ch, out) == EOF)
+ break;
+ ch = ((p_0 << 4) & 060) | ((p_1 >> 4) & 017);
+ ch = ENC(ch);
+ if (putc(ch, out) == EOF)
+ break;
+ ch = ((p_1 << 2) & 074) | ((p_2 >> 6) & 03);
+ ch = ENC(ch);
+ if (putc(ch, out) == EOF)
+ break;
+ ch = p_2 & 077;
+ ch = ENC(ch);
+ if (putc(ch, out) == EOF)
+ break;
+ }
+ if (putc('\n', out) == EOF)
+ break;
+ }
+ ch = ENC('\0');
+ putc(ch, out);
+ putc('\n', out);
+ fprintf(out, "end\n");
+}
+
+int
+uudecode(FILE * input, char * outBuf, int bufLen){
+ int n;
+ char ch, *p, returnCode;
+ char buf[255];
+
+ returnCode = 0;
+ /* search for header line */
+ do {
+ if (!fgets(buf, sizeof(buf), input)) {
+ return 1;
+ }
+ } while (strncmp(buf, "begin", 5));
+
+ /* for each input line */
+ for (;;) {
+ if (!fgets(p = buf, sizeof(buf), input)) {
+ return 1;
+ }
+ /*
+ * `n' is used to avoid writing out all the characters
+ * at the end of the file.
+ */
+ if ((n = DEC(*p)) <= 0)
+ break;
+ if(n >= bufLen){
+ returnCode = 1;
+ break;
+ }
+ for (++p; n > 0; p += 4, n -= 3)
+ if (n >= 3) {
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ * outBuf = ch; outBuf++; bufLen--;
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ * outBuf = ch; outBuf++; bufLen--;
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ * outBuf = ch; outBuf++; bufLen--;
+ } else {
+ if (n >= 1) {
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ * outBuf = ch; outBuf++; bufLen--;
+ }
+ if (n >= 2) {
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ * outBuf = ch; outBuf++; bufLen--;
+ }
+ if (n >= 3) {
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ * outBuf = ch; outBuf++; bufLen--;
+ }
+ }
+ }
+ if (!fgets(buf, sizeof(buf), input) || strcmp(buf, "end\n")) {
+ return 1;
+ }
+ return returnCode;
+}
+
+int
+uuencode_mem(char * dst, const char * data, int dataLen)
+{
+ int sz = 0;
+
+ int ch, n;
+ const char *p = data;
+
+ while (dataLen > 0){
+ n = dataLen > 45 ? 45 : dataLen;
+ dataLen -= n;
+ ch = ENC(n);
+ * dst = ch; dst++; sz++;
+ for (; n > 0; n -= 3, p += 3) {
+ char p_0 = * p;
+ char p_1 = 0;
+ char p_2 = 0;
+
+ if(n >= 2){
+ p_1 = p[1];
+ }
+ if(n >= 3){
+ p_2 = p[2];
+ }
+
+ ch = p_0 >> 2;
+ ch = ENC(ch);
+ * dst = ch; dst++; sz++;
+
+ ch = ((p_0 << 4) & 060) | ((p_1 >> 4) & 017);
+ ch = ENC(ch);
+ * dst = ch; dst++; sz++;
+
+ ch = ((p_1 << 2) & 074) | ((p_2 >> 6) & 03);
+ ch = ENC(ch);
+ * dst = ch; dst++; sz++;
+
+ ch = p_2 & 077;
+ ch = ENC(ch);
+ * dst = ch; dst++; sz++;
+ }
+
+ * dst = '\n'; dst++; sz++;
+ }
+ ch = ENC('\0');
+ * dst = ch; dst++; sz++;
+
+ * dst = '\n'; dst++; sz++;
+ * dst = 0; dst++; sz++;
+
+ return sz;
+}
+
+int
+uudecode_mem(char * outBuf, int bufLen, const char * src){
+ int n;
+ char ch;
+ int sz = 0;
+ const char * p = src;
+
+ /*
+ * `n' is used to avoid writing out all the characters
+ * at the end of the file.
+ */
+ if ((n = DEC(*p)) <= 0)
+ return 0;
+ if(n >= bufLen){
+ return -1;
+ }
+ for (++p; n > 0; p += 4, n -= 3){
+ if (n >= 3) {
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ * outBuf = ch; outBuf++; bufLen--; sz++;
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ * outBuf = ch; outBuf++; bufLen--; sz++;
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ * outBuf = ch; outBuf++; bufLen--; sz++;
+ } else {
+ if (n >= 1) {
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ * outBuf = ch; outBuf++; bufLen--; sz++;
+ }
+ if (n >= 2) {
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ * outBuf = ch; outBuf++; bufLen--; sz++;
+ }
+ if (n >= 3) {
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ * outBuf = ch; outBuf++; bufLen--; sz++;
+ }
+ }
+ }
+ return sz;
+}
+
+
+
diff --git a/storage/ndb/src/common/util/version.c b/storage/ndb/src/common/util/version.c
new file mode 100644
index 00000000000..dfbb1e4a2e6
--- /dev/null
+++ b/storage/ndb/src/common/util/version.c
@@ -0,0 +1,238 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_version.h>
+#include <version.h>
+#include <basestring_vsnprintf.h>
+#include <NdbEnv.h>
+#include <NdbOut.hpp>
+
+Uint32 getMajor(Uint32 version) {
+ return (version >> 16) & 0xFF;
+}
+
+Uint32 getMinor(Uint32 version) {
+ return (version >> 8) & 0xFF;
+}
+
+Uint32 getBuild(Uint32 version) {
+ return (version >> 0) & 0xFF;
+}
+
+Uint32 makeVersion(Uint32 major, Uint32 minor, Uint32 build) {
+ return MAKE_VERSION(major, minor, build);
+
+}
+
+const char * getVersionString(Uint32 version, const char * status) {
+ char buff[100];
+ if (status && status[0] != 0)
+ basestring_snprintf(buff, sizeof(buff),
+ "Version %d.%d.%d (%s)",
+ getMajor(version),
+ getMinor(version),
+ getBuild(version),
+ status);
+ else
+ basestring_snprintf(buff, sizeof(buff),
+ "Version %d.%d.%d",
+ getMajor(version),
+ getMinor(version),
+ getBuild(version));
+ return strdup(buff);
+}
+
+typedef enum {
+ UG_Null,
+ UG_Range,
+ UG_Exact
+} UG_MatchType;
+
+struct NdbUpGradeCompatible {
+ Uint32 ownVersion;
+ Uint32 otherVersion;
+ UG_MatchType matchType;
+};
+
+/*#define TEST_VERSION*/
+
+#define HAVE_NDB_SETVERSION
+#ifdef HAVE_NDB_SETVERSION
+Uint32 ndbOwnVersionTesting = 0;
+void
+ndbSetOwnVersion() {
+ char buf[256];
+ if (NdbEnv_GetEnv("NDB_SETVERSION", buf, sizeof(buf))) {
+ Uint32 _v1,_v2,_v3;
+ if (sscanf(buf, "%u.%u.%u", &_v1, &_v2, &_v3) == 3) {
+ ndbOwnVersionTesting = MAKE_VERSION(_v1,_v2,_v3);
+ ndbout_c("Testing: Version set to 0x%x", ndbOwnVersionTesting);
+ }
+ }
+}
+#else
+void ndbSetOwnVersion() {}
+#endif
+
+#ifndef TEST_VERSION
+struct NdbUpGradeCompatible ndbCompatibleTable_full[] = {
+ { MAKE_VERSION(5,0,3), MAKE_VERSION(5,0,2), UG_Exact },
+ { MAKE_VERSION(4,1,9), MAKE_VERSION(4,1,8), UG_Exact },
+ { MAKE_VERSION(3,5,2), MAKE_VERSION(3,5,1), UG_Exact },
+ { 0, 0, UG_Null }
+};
+
+struct NdbUpGradeCompatible ndbCompatibleTable_upgrade[] = {
+ { MAKE_VERSION(5,0,2), MAKE_VERSION(4,1,8), UG_Exact },
+ { MAKE_VERSION(3,5,4), MAKE_VERSION(3,5,3), UG_Exact },
+ { 0, 0, UG_Null }
+};
+
+#else /* testing purposes */
+
+struct NdbUpGradeCompatible ndbCompatibleTable_full[] = {
+ { MAKE_VERSION(4,1,5), MAKE_VERSION(4,1,0), UG_Range },
+ { MAKE_VERSION(3,6,9), MAKE_VERSION(3,6,1), UG_Range },
+ { MAKE_VERSION(3,6,2), MAKE_VERSION(3,6,1), UG_Range },
+ { MAKE_VERSION(3,5,7), MAKE_VERSION(3,5,0), UG_Range },
+ { MAKE_VERSION(3,5,1), MAKE_VERSION(3,5,0), UG_Range },
+ { NDB_VERSION_D , MAKE_VERSION(NDB_VERSION_MAJOR,NDB_VERSION_MINOR,2), UG_Range },
+ { 0, 0, UG_Null }
+};
+
+struct NdbUpGradeCompatible ndbCompatibleTable_upgrade[] = {
+ { MAKE_VERSION(4,1,5), MAKE_VERSION(3,6,9), UG_Exact },
+ { MAKE_VERSION(3,6,2), MAKE_VERSION(3,5,7), UG_Exact },
+ { MAKE_VERSION(3,5,1), NDB_VERSION_D , UG_Exact },
+ { 0, 0, UG_Null }
+};
+
+
+#endif
+
+void ndbPrintVersion()
+{
+ printf("Version: %u.%u.%u\n",
+ getMajor(ndbGetOwnVersion()),
+ getMinor(ndbGetOwnVersion()),
+ getBuild(ndbGetOwnVersion()));
+}
+
+Uint32
+ndbGetOwnVersion()
+{
+#ifdef HAVE_NDB_SETVERSION
+ if (ndbOwnVersionTesting == 0)
+ return NDB_VERSION_D;
+ else
+ return ndbOwnVersionTesting;
+#else
+ return NDB_VERSION_D;
+#endif
+}
+
+int
+ndbSearchUpgradeCompatibleTable(Uint32 ownVersion, Uint32 otherVersion,
+ struct NdbUpGradeCompatible table[])
+{
+ int i;
+ for (i = 0; table[i].ownVersion != 0 && table[i].otherVersion != 0; i++) {
+ if (table[i].ownVersion == ownVersion ||
+ table[i].ownVersion == (Uint32) ~0) {
+ switch (table[i].matchType) {
+ case UG_Range:
+ if (otherVersion >= table[i].otherVersion){
+ return 1;
+ }
+ break;
+ case UG_Exact:
+ if (otherVersion == table[i].otherVersion){
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+ndbCompatible(Uint32 ownVersion, Uint32 otherVersion, struct NdbUpGradeCompatible table[])
+{
+ if (otherVersion >= ownVersion) {
+ return 1;
+ }
+ return ndbSearchUpgradeCompatibleTable(ownVersion, otherVersion, table);
+}
+
+int
+ndbCompatible_full(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible(ownVersion, otherVersion, ndbCompatibleTable_full);
+}
+
+int
+ndbCompatible_upgrade(Uint32 ownVersion, Uint32 otherVersion)
+{
+ if (ndbCompatible_full(ownVersion, otherVersion))
+ return 1;
+ return ndbCompatible(ownVersion, otherVersion, ndbCompatibleTable_upgrade);
+}
+
+int
+ndbCompatible_mgmt_ndb(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_upgrade(ownVersion, otherVersion);
+}
+
+int
+ndbCompatible_mgmt_api(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_upgrade(ownVersion, otherVersion);
+}
+
+int
+ndbCompatible_ndb_mgmt(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_full(ownVersion, otherVersion);
+}
+
+int
+ndbCompatible_api_mgmt(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_full(ownVersion, otherVersion);
+}
+
+int
+ndbCompatible_api_ndb(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_full(ownVersion, otherVersion);
+}
+
+int
+ndbCompatible_ndb_api(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_upgrade(ownVersion, otherVersion);
+}
+
+int
+ndbCompatible_ndb_ndb(Uint32 ownVersion, Uint32 otherVersion)
+{
+ return ndbCompatible_upgrade(ownVersion, otherVersion);
+}
diff --git a/storage/ndb/src/cw/Makefile.am b/storage/ndb/src/cw/Makefile.am
new file mode 100644
index 00000000000..7348fc9eab6
--- /dev/null
+++ b/storage/ndb/src/cw/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = cpcd
+
+windoze-dsp:
+
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.cpp b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.cpp
new file mode 100644
index 00000000000..59ee3e90451
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.cpp
@@ -0,0 +1,215 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "stdafx.h"
+
+HINSTANCE hInst ;
+TCHAR szTitle[MAX_LOADSTRING] ;
+TCHAR szWindowClass[MAX_LOADSTRING] ;
+
+static CNdbControls controls ;
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow){
+ MSG msg;
+ HACCEL hAccelTable;
+
+ LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING) ;
+ LoadString(hInstance, IDC_CPC_GUI, szWindowClass, MAX_LOADSTRING) ;
+ NdbRegisterClass(hInstance);
+
+ if (!InitInstance (hInstance, nCmdShow)) {
+ return FALSE;
+ }
+
+ hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_CPC_GUI);
+
+ while (GetMessage(&msg, NULL, 0, 0)){
+
+ if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+
+ }
+
+ }
+
+ return msg.wParam;
+}
+
+
+ATOM NdbRegisterClass(HINSTANCE hInstance){
+ WNDCLASSEX wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW ;
+ wcex.lpfnWndProc = (WNDPROC)WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInstance;
+ wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_CPC_GUI);
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW);
+ wcex.lpszMenuName = (LPCSTR)IDC_CPC_GUI;
+ wcex.lpszClassName = szWindowClass;
+ wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
+
+ return RegisterClassEx(&wcex);
+}
+
+
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){
+
+ HWND hWnd;
+
+ hInst = hInstance;
+
+ hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
+
+ InitCommonControls();
+
+ if (!hWnd) return FALSE ;
+
+ ShowWindow(hWnd, nCmdShow) ;
+ UpdateWindow(hWnd) ;
+
+ return TRUE;
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
+
+ int wmId, wmEvent;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ int c = 0 ;
+
+ switch (message)
+ {
+
+ case WM_CREATE:
+ _assert(controls.Create(hInst, hWnd)) ;
+ return 0 ;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId){
+ case IDM_ABOUT:
+ DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
+ break;
+ case IDM_EXIT:
+ DestroyWindow(hWnd);
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->code) {
+ case TTN_GETDISPINFO: {
+
+ LPTOOLTIPTEXT lpttt;
+ lpttt = (LPTOOLTIPTEXT) lParam;
+ lpttt->hinst = hInst;
+
+ int idButton = lpttt->hdr.idFrom;
+
+ switch (idButton){
+ case IDM_NEW:
+ lpttt->lpszText = MAKEINTRESOURCE(IDS_TIP_NEW);
+ break;
+ case IDM_DELETE:
+ lpttt->lpszText = MAKEINTRESOURCE(IDS_TIP_DELETE);
+ break;
+ case IDM_PROPS:
+ lpttt->lpszText = MAKEINTRESOURCE(IDS_TIP_PROPS);
+ break;
+ }
+ break;
+ }
+ case TVN_SELCHANGED: {
+ LPNMTREEVIEW pnmtv ;
+
+ pnmtv = (LPNMTREEVIEW) lParam ;
+ controls.ToggleListViews(pnmtv) ;
+
+ break ;
+ }
+
+ case NM_RCLICK: {
+ LPNMHDR lpnmh ;
+ lpnmh = (LPNMHDR) lParam ;
+ switch(lpnmh->idFrom){
+ case ID_TREEVIEW:
+ break;
+ default:
+ break ;
+ }
+ }
+
+ default:
+ break;
+ }
+
+
+ case WM_PAINT:
+ hdc = BeginPaint(hWnd, &ps) ;
+ EndPaint(hWnd, &ps);
+ break;
+
+ case WM_SIZE:
+ controls.Resize() ;
+ return 0 ;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+
+LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){
+
+ switch (message){
+
+ case WM_INITDIALOG:
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL){
+ EndDialog(hDlg, LOWORD(wParam));
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+
+
+
+
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsp b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsp
new file mode 100644
index 00000000000..91007b0a47e
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsp
@@ -0,0 +1,216 @@
+# Microsoft Developer Studio Project File - Name="CPC_GUI" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=CPC_GUI - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "CPC_GUI.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "CPC_GUI.mak" CFG="CPC_GUI - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "CPC_GUI - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "CPC_GUI - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "CPC_GUI - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib mfc42.lib /nologo /subsystem:windows /machine:I386
+
+!ELSEIF "$(CFG)" == "CPC_GUI - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FR /Yu"stdafx.h" /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib comctl32.lib mfc42d.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "CPC_GUI - Win32 Release"
+# Name "CPC_GUI - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\CPC_GUI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPC_GUI.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\NdbControls.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\CPC_GUI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NdbControls.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\bitmap1.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\bmp00001.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\C.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Closed.BMP
+# End Source File
+# Begin Source File
+
+SOURCE=.\Closed.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\Closed24.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Computer24.BMP
+# End Source File
+# Begin Source File
+
+SOURCE=.\CPC_GUI.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Db.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\icon1.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\O.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Open.BMP
+# End Source File
+# Begin Source File
+
+SOURCE=.\Open.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\Open24.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\small.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\toolbar.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\toolbar1.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Tower2.ICO
+# End Source File
+# Begin Source File
+
+SOURCE=.\TowerIC1.BMP
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# End Target
+# End Project
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsw b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsw
new file mode 100644
index 00000000000..1f163a31662
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "CPC_GUI"=.\CPC_GUI.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.h b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.h
new file mode 100644
index 00000000000..cf7670948a7
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.h
@@ -0,0 +1,40 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#if !defined(AFX_CPC_GUI_H__EA01C861_C56D_48F1_856F_4935E20620B1__INCLUDED_)
+#define AFX_CPC_GUI_H__EA01C861_C56D_48F1_856F_4935E20620B1__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+#define MAX_LOADSTRING 100
+
+
+
+#define TV_ROOT_ITEMS 2
+
+
+// Global Variables
+
+ATOM NdbRegisterClass(HINSTANCE) ;
+BOOL InitInstance(HINSTANCE, int) ;
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM) ;
+LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+
+#endif // !defined(AFX_CPC_GUI_H__EA01C861_C56D_48F1_856F_4935E20620B1__INCLUDED_)
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.ico b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.ico
new file mode 100644
index 00000000000..386883523bc
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.rc b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.rc
new file mode 100644
index 00000000000..41d75b2b282
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.rc
@@ -0,0 +1,193 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_CPC_GUI ICON DISCARDABLE "CPC_GUI.ICO"
+IDI_SMALL ICON DISCARDABLE "SMALL.ICO"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDM_CPC_GUI MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "E&xit", IDM_EXIT
+ END
+ POPUP "&Actions"
+ BEGIN
+ MENUITEM "&Insert...", ID_ACTIONS_INSERT
+ MENUITEM "&Delete", ID_ACTIONS_DELETE
+ MENUITEM "&Properties", ID_ACTIONS_PROPERTIES
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About ...", IDM_ABOUT
+ END
+END
+
+IDM_TREEVIEW MENU DISCARDABLE
+BEGIN
+ MENUITEM "&Insert", ID_TREEVIEW1
+ MENUITEM "&Delete", ID_DELETE
+ MENUITEM "&Properties", ID_PROPERTIES
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDC_CPC_GUI ACCELERATORS MOVEABLE PURE
+BEGIN
+ "?", IDM_ABOUT, ASCII, ALT
+ "/", IDM_ABOUT, ASCII, ALT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 230, 75
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "About"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "NDB Cluster Process Control Applet v1.0",IDC_STATIC,7,8,
+ 213,8,SS_NOPREFIX
+ LTEXT "Copyright (C) 2003 MySQL AB",
+ IDC_STATIC,7,20,213,20
+ DEFPUSHBUTTON "OK",IDOK,185,55,41,16,WS_GROUP
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+2 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""resource.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+1 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "resource.h\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDR_TOOLBAR BITMAP MOVEABLE PURE "toolbar.bmp"
+IDB_TOOLBAR BITMAP MOVEABLE PURE "bitmap1.bmp"
+IDB_COMPUTER BITMAP MOVEABLE PURE "TowerIC1.BMP"
+IDB_OPEN BITMAP MOVEABLE PURE "Open.BMP"
+IDB_CLOSED BITMAP MOVEABLE PURE "Closed.BMP"
+IDB_DATABASE BITMAP MOVEABLE PURE "DB.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar
+//
+
+IDR_TOOLBAR TOOLBAR MOVEABLE PURE 18, 18
+BEGIN
+ BUTTON ID_BUTTON32773
+ BUTTON ID_BUTTON32783
+ BUTTON ID_BUTTON32784
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_APP_TITLE "NDB Cluster Process Control Applet"
+ IDS_TV_ROOT_COMPUTERS "Computers"
+ IDS_TV_ROOT_DATABASES "Databases"
+ IDS_LV_COMPUTER_HEADER_1 "Computer"
+ IDS_LV_COMPUTER_HEADER_2 "Hostname"
+ IDS_LV_PROCESS_HEADER_1 "Process"
+ IDC_CPC_GUI "CPC_GUI"
+ IDS_TIP_NEW "Add new computer or database"
+ IDS_TIP_DELETE "Delete selected computer or database"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_TIP_PROPS "Display properties for selected computer or database"
+ IDS_LV_PROCESS_HEADER_2 "Name"
+ IDS_LV_PROCESS_HEADER_3 "Owner"
+ IDS_LV_PROCESS_HEADER_4 "Status"
+ IDS_LV_COMPUTER_HEADER_3 "Status"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.sln b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.sln
new file mode 100644
index 00000000000..86b574d851d
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CPC_GUI", "CPC_GUI.vcproj", "{F5FADD9D-4353-4A73-88DC-474A4D17B485}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {F5FADD9D-4353-4A73-88DC-474A4D17B485}.Debug.ActiveCfg = Debug|Win32
+ {F5FADD9D-4353-4A73-88DC-474A4D17B485}.Debug.Build.0 = Debug|Win32
+ {F5FADD9D-4353-4A73-88DC-474A4D17B485}.Release.ActiveCfg = Release|Win32
+ {F5FADD9D-4353-4A73-88DC-474A4D17B485}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.suo b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.suo
new file mode 100644
index 00000000000..e7d178f04c3
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.suo
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.vcproj b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.vcproj
new file mode 100644
index 00000000000..56f9f3a8511
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.vcproj
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="CPC_GUI"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32,NDEBUG,_WINDOWS"
+ StringPooling="TRUE"
+ RuntimeLibrary="4"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="stdafx.h"
+ PrecompiledHeaderFile=".\Release/CPC_GUI.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="mfc42.lib"
+ OutputFile=".\Release/CPC_GUI.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ ProgramDatabaseFile=".\Release/CPC_GUI.pdb"
+ SubSystem="2"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/CPC_GUI.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32,_DEBUG,_WINDOWS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="stdafx.h"
+ PrecompiledHeaderFile=".\Debug/CPC_GUI.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="comctl32.lib mfc70d.lib"
+ OutputFile=".\Debug/CPC_GUI.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/CPC_GUI.pdb"
+ SubSystem="2"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/CPC_GUI.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ </Configurations>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath=".\CPC_GUI.cpp">
+ </File>
+ <File
+ RelativePath=".\CPC_GUI.rc">
+ </File>
+ <File
+ RelativePath=".\NdbControls.cpp">
+ </File>
+ <File
+ RelativePath=".\StdAfx.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath=".\CPC_GUI.h">
+ </File>
+ <File
+ RelativePath=".\NdbControls.h">
+ </File>
+ <File
+ RelativePath=".\StdAfx.h">
+ </File>
+ <File
+ RelativePath=".\resource.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+ <File
+ RelativePath=".\C.bmp">
+ </File>
+ <File
+ RelativePath=".\CPC_GUI.ico">
+ </File>
+ <File
+ RelativePath=".\Closed.BMP">
+ </File>
+ <File
+ RelativePath=".\Closed.ICO">
+ </File>
+ <File
+ RelativePath=".\Closed24.bmp">
+ </File>
+ <File
+ RelativePath=".\Computer24.BMP">
+ </File>
+ <File
+ RelativePath=".\Db.bmp">
+ </File>
+ <File
+ RelativePath=".\O.bmp">
+ </File>
+ <File
+ RelativePath=".\Open.BMP">
+ </File>
+ <File
+ RelativePath=".\Open.ICO">
+ </File>
+ <File
+ RelativePath=".\Open24.bmp">
+ </File>
+ <File
+ RelativePath=".\Tower2.ICO">
+ </File>
+ <File
+ RelativePath=".\TowerIC1.BMP">
+ </File>
+ <File
+ RelativePath=".\bitmap1.bmp">
+ </File>
+ <File
+ RelativePath=".\bmp00001.bmp">
+ </File>
+ <File
+ RelativePath=".\icon1.ico">
+ </File>
+ <File
+ RelativePath=".\small.ico">
+ </File>
+ <File
+ RelativePath=".\toolbar.bmp">
+ </File>
+ <File
+ RelativePath=".\toolbar1.bmp">
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\ReadMe.txt">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/Closed.ICO b/storage/ndb/src/cw/cpcc-win32/C++/Closed.ICO
new file mode 100644
index 00000000000..044042b42fb
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/Closed.ICO
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/NdbControls.cpp b/storage/ndb/src/cw/cpcc-win32/C++/NdbControls.cpp
new file mode 100644
index 00000000000..6bbc9a9859b
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/NdbControls.cpp
@@ -0,0 +1,436 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "stdafx.h"
+#include "NdbControls.h"
+
+
+/**
+* CNdbControl implementation
+*/
+
+BOOL CNdbControl::GetRect(LPRECT lprc) const {
+
+ _ASSERT(this) ;
+
+ return GetClientRect(m_hControl, lprc) ;
+
+}
+
+BOOL CNdbControl::Resize(LONG x, LONG y, LONG w, LONG h) const {
+
+ _ASSERT(this) ;
+
+ if(!MoveWindow(m_hControl, x, y, w, h, TRUE))
+ return FALSE ;
+ if(m_bVisible){
+ ShowWindow(m_hControl, SW_SHOW) ;
+ UpdateWindow(m_hControl) ;
+ }
+ return TRUE ;
+
+}
+
+BOOL CNdbControl::Show(BOOL bShow) {
+
+ _ASSERT(this) ;
+
+ if(bShow){
+ ShowWindow(m_hControl, SW_SHOW);
+ m_bVisible = TRUE ;
+ }else{
+ ShowWindow(m_hControl, SW_HIDE);
+ m_bVisible = FALSE ;
+ }
+ EnableWindow(m_hControl, bShow) ;
+ UpdateWindow(m_hControl) ;
+
+ return TRUE ;
+}
+
+
+
+CNdbControl::~CNdbControl(){
+
+ DestroyWindow(m_hControl) ;
+ if(m_hMenu)
+ DestroyMenu(m_hMenu) ;
+
+}
+
+
+/**
+* CNdbListView implementation
+*/
+
+BOOL CNdbListView::Create(HINSTANCE hInst, HWND hParent, DWORD dwId, NDB_ITEM_TYPE enType, PNDB_LV pstH, DWORD dwWidth) {
+
+ if(!pstH)
+ return FALSE ;
+
+ LV_COLUMN lvC ;
+ m_hInstance = hInst ;
+ m_hParent = hParent ;
+ m_dwId = dwId ;
+ m_dwWidth = dwWidth ;
+ m_dwWidth = 100 ;
+ m_enType = enType;
+ char* szLabels[MAX_LV_HEADERS] ;
+ int count = 0 ;
+
+ m_hControl = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, WC_LISTVIEW, TEXT(""),
+ WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT,
+ 0, 0, 0, 0, m_hParent, (HMENU)m_dwId, hInst, NULL );
+
+ if(!m_hControl)
+ return FALSE ;
+
+ lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
+ lvC.fmt = LVCFMT_LEFT;
+
+ switch(enType){
+ case ITEM_COMPR_ROOT:
+ szLabels[0] = pstH->szComputer ;
+ szLabels[1] = pstH->szHostname ;
+ szLabels[2] = pstH->szStatus ;
+ count = 3 ;
+ break ;
+ case ITEM_DB_ROOT:
+ szLabels[0] = pstH->szDatabase ;
+ szLabels[1] = pstH->szStatus ;
+ count = 2 ;
+ break ;
+ case ITEM_COMPR:
+ szLabels[0] = pstH->szProcess ;
+ szLabels[1] = pstH->szDatabase;
+ szLabels[2] = pstH->szOwner ;
+ szLabels[3] = pstH->szStatus ;
+ count = 4 ;
+ case ITEM_DB:
+ szLabels[0] = pstH->szProcess ;
+ szLabels[1] = pstH->szComputer;
+ szLabels[2] = pstH->szOwner ;
+ szLabels[3] = pstH->szStatus ;
+ count = 4 ;
+ break ;
+ NDB_DEFAULT_UNREACHABLE ;
+ }
+
+ for(int j = 0 ; j < count ; ++j){
+ lvC.iSubItem = j ;
+ lvC.cx = m_dwWidth ;
+ lvC.pszText = szLabels[j] ;
+ if(0xFFFFFFFF == ListView_InsertColumn(m_hControl, j, &lvC))
+ return FALSE ;
+ }
+
+ SendMessage(m_hControl, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT,
+ LVS_EX_FULLROWSELECT );
+
+ ShowWindow(m_hControl, SW_SHOW) ;
+
+ return TRUE ;
+
+}
+
+
+
+/**
+* CNdbToolBar implementation
+*/
+
+
+
+/**
+* CNdbTreeView implementation
+*/
+
+BOOL CNdbTreeView::Create(HINSTANCE hInst, HWND hParent, DWORD dwMenuId, DWORD dwId){
+
+ if(!CreateTreeView(hInst, hParent, dwId))
+ return FALSE ;
+
+ m_hMenu = LoadMenu(m_hInstance,MAKEINTRESOURCE(dwMenuId)) ;
+ if(!m_hMenu)
+ return FALSE ;
+
+ return TRUE ;
+}
+
+
+BOOL CNdbTreeView::CreateTreeView(HINSTANCE hInst, HWND hParent, DWORD dwId){
+
+
+ m_hInstance = hInst ;
+ m_hParent = hParent ;
+ m_dwId = dwId ;
+ HIMAGELIST himl ;
+ HBITMAP hbmp ;
+ DWORD dwCount = 0 ;
+
+ m_hControl = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, WC_TREEVIEW, "Tree View",
+ WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES |
+ TVS_HASBUTTONS | TVS_LINESATROOT | TVS_SINGLEEXPAND,
+ 0, 0, 0, 0, m_hParent, (HMENU)m_dwId, m_hInstance, NULL) ;
+
+ if(!m_hControl)
+ return FALSE ;
+
+ if((himl = ImageList_Create(nX, nY, ILC_MASK | ILC_COLOR8, 4, 0)) == NULL)
+ return FALSE ;
+
+ hbmp = LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDI_OPEN));
+ hbmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDB_OPEN), IMAGE_BITMAP, nX, 0, LR_DEFAULTSIZE);
+ m_nOpen = ImageList_AddMasked(himl, hbmp, clr);
+ DeleteObject(hbmp);
+ hbmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDB_CLOSED), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
+ m_nClosed = ImageList_AddMasked(himl, hbmp, clr);
+ DeleteObject(hbmp);
+ hbmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDB_COMPUTER),IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
+ m_nComputer = ImageList_AddMasked(himl, hbmp, clr);
+ DeleteObject(hbmp);
+ hbmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDB_DATABASE), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
+ m_nDatabase = ImageList_AddMasked(himl, hbmp, clr);
+ DeleteObject(hbmp);
+
+ if(ImageList_GetImageCount(himl) < 4)
+ return FALSE ;
+
+ TreeView_SetImageList(m_hControl, himl, TVSIL_NORMAL);
+
+ ShowWindow(m_hControl, SW_SHOW) ;
+
+ return TRUE ;
+
+}
+
+
+
+HTREEITEM CNdbTreeView::AddItem(LPSTR szText, NDB_ITEM_TYPE enType, DWORD dwLVId){
+
+ TVITEM tvi ;
+ TVINSERTSTRUCT tvins ;
+ HTREEITEM hti ;
+ HTREEITEM hTemp ;
+ int nImage = m_nClosed ;
+
+ tvi.mask = TVIF_TEXT | TVIF_IMAGE
+ | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+
+ tvi.pszText = szText;
+ tvi.cchTextMax = lstrlen(szText);
+
+ switch(enType){
+
+ case ITEM_COMPR_ROOT:
+ nImage = m_nClosed ;
+ if(!m_hPrevRoot)
+ tvins.hParent = TVI_ROOT;
+ else
+ tvins.hInsertAfter = m_hPrevRoot ;
+ break ;
+
+ case ITEM_DB_ROOT:
+ if(!m_hPrevRoot)
+ tvins.hParent = TVI_ROOT;
+ else
+ tvins.hInsertAfter = m_hPrevRoot ;
+ break ;
+
+ case ITEM_COMPR:
+ nImage = m_nComputer ;
+ if(!m_hPrevComputersChild || !m_hComputersRoot)
+ return 0 ;
+ else
+ tvins.hInsertAfter = m_hPrevComputersChild ;
+ tvins.hParent = m_hComputersRoot ;
+ break ;
+
+ case ITEM_DB:
+ nImage = m_nDatabase ;
+ if(!m_hPrevComputersChild || !m_hComputersRoot)
+ return 0 ;
+ else
+ tvins.hInsertAfter = m_hPrevDatabasesChild ;
+ tvins.hParent = m_hDatabasesRoot ;
+ break ;
+
+ NDB_DEFAULT_UNREACHABLE ;
+
+ }
+
+ tvi.iImage = nImage ;
+ tvi.iSelectedImage = nImage ;
+ tvi.lParam = (LPARAM) dwLVId ;
+ tvins.item = tvi ;
+
+ hTemp = TreeView_InsertItem(m_hControl, &tvins);
+ if(!hTemp)
+ return NULL ;
+
+ switch(enType){
+
+ case ITEM_COMPR_ROOT:
+ m_hComputersRoot = hTemp ;
+ break ;
+
+ case ITEM_DB_ROOT:
+ m_hDatabasesRoot = hTemp ;
+ break ;
+
+ case ITEM_COMPR:
+ m_hPrevComputersChild = hTemp ;
+ break ;
+
+ case ITEM_DB:
+ m_hPrevComputersChild = hTemp ;
+ break ;
+
+ NDB_DEFAULT_UNREACHABLE ;
+
+ }
+
+ if (ITEM_COMPR_ROOT != enType && ITEM_DB_ROOT != enType) {
+
+ hti = TreeView_GetParent(m_hControl, hTemp);
+ tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = hti;
+ tvi.iImage = m_nClosed;
+ tvi.iSelectedImage = m_nClosed;
+ TreeView_SetItem(m_hControl, &tvi);
+
+ }
+
+ return hTemp ;
+}
+
+
+BOOL CNdbControls::Create(HINSTANCE hInst, HWND hParent){
+
+ m_hInstance = hInst ;
+ m_hParent = hParent ;
+ m_tb.Create(m_hInstance, m_hParent, ID_TOOLBAR, IDB_TOOLBAR) ;
+ m_sb.Create(m_hInstance, m_hParent, ID_STATUSBAR) ;
+ m_tv.Create(m_hInstance, m_hParent, IDM_TREEVIEW, ID_TREEVIEW) ;
+ _assert(AddView("Computers", ITEM_COMPR_ROOT)) ;
+ _assert(AddView("Databases", ITEM_DB_ROOT)) ;
+
+ return TRUE ;
+}
+
+BOOL CNdbControls::AddListView(NDB_ITEM_TYPE enType, DWORD dwId){
+
+ int count ;
+ CNdbListView* plv ;
+ PNDB_LV pst ;
+
+ plv = new CNdbListView ;
+
+ if(!plv)
+ return FALSE ;
+
+ count = m_map_lvc.GetCount() + m_dwFirstId_lv ;
+
+ switch(enType){
+ case ITEM_COMPR_ROOT:
+ pst = &m_stlvcRoot ;
+ break ;
+ case ITEM_DB_ROOT:
+ pst = &m_stlvdRoot ;
+ break ;
+ case ITEM_COMPR:
+ pst = &m_stlvc ;
+ break ;
+ case ITEM_DB:
+ pst = &m_stlvd ;
+ break ;
+ NDB_DEFAULT_UNREACHABLE ;
+ }
+
+ plv->Create(m_hInstance, m_hParent, dwId, enType, pst, LV_HEADER_WIDTH) ;
+
+ m_map_lvc[count] = plv ;
+
+ return TRUE ;
+}
+
+BOOL CNdbControls::AddView(LPSTR szText, NDB_ITEM_TYPE enType){
+
+ DWORD dwId_lv = m_dwNextId_lv ;
+
+ if(AddListView(enType, dwId_lv) && m_tv.AddItem(szText, enType, dwId_lv))
+ m_dwNextId_lv++ ;
+ else
+ return FALSE ;
+
+ return TRUE ;
+};
+
+
+VOID CNdbControls::ToggleListViews(LPNMTREEVIEW pnmtv){
+
+ CNdbListView* plv ;
+ int count = m_map_lvc.GetCount() + m_dwFirstId_lv ;
+
+ for(int c = FIRST_ID_LV ; c < count; ++c){
+ _assert(m_map_lvc.Lookup(c, plv)) ;
+ if(pnmtv->itemNew.lParam == (c))
+ plv->Show(TRUE) ;
+ else
+ plv->Show(FALSE) ;
+ }
+}
+
+
+
+VOID CNdbControls::Resize(){
+
+ RECT rc, rcTB, rcSB ;
+ LONG tw, sw, lx, ly, lw, lh, tvw, tvh ;
+ CNdbListView* plv ;
+ int count ; //, id ;
+
+ GetClientRect(m_hParent, &rc) ;
+ m_tb.GetRect(&rcTB) ;
+ m_sb.GetRect(&rcSB) ;
+
+ sw = rcSB.bottom ;
+ tw = rcTB.bottom ;
+
+ m_tb.Resize(0, 0, rc.right, tw) ;
+
+ tvw = rc.right / 4 ;
+ tvh = rc.bottom - sw - tw - BORDER ;
+
+ m_tv.Resize(0, tw + BORDER, tvw, tvh) ;
+
+ m_sb.Resize(0, tvh, rc.left, sw) ;
+
+ lx = tvw + BORDER - 2 ;
+ ly = tw + BORDER ;
+ lw = rc.right - tvw - BORDER + 1 ;
+ lh = tvh ;
+
+ count = m_map_lvc.GetCount() + FIRST_ID_LV ;
+
+ for(int c = FIRST_ID_LV ; c < count; ++c){
+ _assert(m_map_lvc.Lookup(c, plv)) ;
+ plv->Resize(lx, ly, lw, lh) ;
+ }
+
+ return ;
+
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/Open.ICO b/storage/ndb/src/cw/cpcc-win32/C++/Open.ICO
new file mode 100644
index 00000000000..ab7b05d9df7
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/Open.ICO
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/StdAfx.cpp b/storage/ndb/src/cw/cpcc-win32/C++/StdAfx.cpp
new file mode 100644
index 00000000000..8fcdb4ce158
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/StdAfx.cpp
@@ -0,0 +1,24 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+// stdafx.cpp : source file that includes just the standard includes
+// CPC_GUI.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/StdAfx.h b/storage/ndb/src/cw/cpcc-win32/C++/StdAfx.h
new file mode 100644
index 00000000000..370d04fb466
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/StdAfx.h
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
+#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#ifdef _DEBUG
+#define NDB_DEFAULT_UNREACHABLE default: _ASSERT(0); break
+#elif _MSC_VER >= 1200
+#define NDB_DEFAULT_UNREACHABLE default: __assume(0); break
+#else
+#define NDB_DEFAULT_UNREACHABLE default: break
+#endif;
+
+
+#ifdef _DEBUG
+#define _assert _ASSERT
+#else
+#define _assert(expr) expr
+#endif
+
+
+#include <afx.h>
+#include <afxtempl.h>
+
+// C RunTime Header Files
+#include <ndb_global.h>
+#include <memory.h>
+#include <tchar.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <crtdbg.h>
+
+// Local Header Files
+#include "resource.h"
+#include "NdbControls.h"
+#include "CPC_GUI.h"
+
+
+// TODO: reference additional headers your program requires here
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/TreeView.cpp b/storage/ndb/src/cw/cpcc-win32/C++/TreeView.cpp
new file mode 100644
index 00000000000..db5c62f14bb
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/TreeView.cpp
@@ -0,0 +1,19 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "StdAfx.h"
+#include "resource.h"
+#include "CPC_GUI.h"
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/TreeView.h b/storage/ndb/src/cw/cpcc-win32/C++/TreeView.h
new file mode 100644
index 00000000000..595f9bd6cdc
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/TreeView.h
@@ -0,0 +1,19 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/bmp00001.bmp b/storage/ndb/src/cw/cpcc-win32/C++/bmp00001.bmp
new file mode 100644
index 00000000000..e50af403eda
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/bmp00001.bmp
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/resource.h b/storage/ndb/src/cw/cpcc-win32/C++/resource.h
new file mode 100644
index 00000000000..0bec552edf6
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/resource.h
@@ -0,0 +1,90 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by CPC_GUI.rc
+//
+#define IDC_MYICON 2
+#define IDD_CPC_GUI_DIALOG 102
+#define IDD_ABOUTBOX 103
+#define IDS_APP_TITLE 103
+#define IDM_ABOUT 104
+#define IDS_LV_ROOT_COMPUTERS 104
+#define IDS_TV_ROOT_COMPUTERS 104
+#define IDM_EXIT 105
+#define IDS_LV_ROOT_DATABASES 105
+#define IDS_TV_ROOT_DATABASES 105
+#define IDS_HELLO 106
+#define IDS_LV_COMPUTER_HEADER_1 106
+#define IDI_CPC_GUI 107
+#define IDS_LV_COMPUTER_HEADER_2 107
+#define IDI_SMALL 108
+#define IDS_LV_PROCESS_HEADER_1 108
+#define IDC_CPC_GUI 109
+#define IDM_CPC_GUI 109
+#define IDS_TIP_NEW 110
+#define IDS_TIP_DELETE 111
+#define IDS_TIP_PROPS 112
+#define IDS_LV_PROCESS_HEADER_2 113
+#define IDS_LV_PROCESS_HEADER_3 114
+#define IDS_LV_PROCESS_HEADER_4 115
+#define IDS_LV_COMPUTER_HEADER_3 116
+#define IDR_MAINFRAME 128
+#define ID_TREEVIEW 130
+#define IDM_TREEVIEW 130
+#define IDB_TOOLBAR 137
+#define ID_TOOLBAR 158
+#define IDB_COMPUTER 168
+#define IDB_CLOSED 169
+#define IDB_OPEN 170
+#define IDI_DATABASE 172
+#define IDI_CLOSED 175
+#define IDI_OPEN 176
+#define IDI_COMPUTER 177
+#define IDB_MASK 178
+#define IDB_DATABASE 182
+#define IDM_TV 183
+#define ID_TREEVIEW1 32771
+#define ID_BUTTON32773 32773
+#define IDM_NEW 32774
+#define IDM_DELETE 32775
+#define IDM_PROPS 32776
+#define ID_LIST_C 32777
+#define ID_ACTIONS_INSERT 32778
+#define ID_ACTIONS_DELETE 32779
+#define ID_DELETE 32780
+#define ID_PROPERTIES 32781
+#define ID_ACTIONS_PROPERTIES 32782
+#define ID_BUTTON32783 32783
+#define ID_BUTTON32784 32784
+#define ID_LIST_P 32785
+#define ID_STATUSBAR 32786
+#define ID_LIST_C_ROOT 32787
+#define ID_LIST_D_ROOT 32788
+#define IDM_ADDNEW 32793
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 184
+#define _APS_NEXT_COMMAND_VALUE 32796
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/small.ico b/storage/ndb/src/cw/cpcc-win32/C++/small.ico
new file mode 100644
index 00000000000..8f94d9aa828
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/small.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/C++/toolbar.bmp b/storage/ndb/src/cw/cpcc-win32/C++/toolbar.bmp
new file mode 100644
index 00000000000..a1059352c66
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/C++/toolbar.bmp
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/App.ico b/storage/ndb/src/cw/cpcc-win32/csharp/App.ico
new file mode 100644
index 00000000000..3a5525fd794
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/App.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/CPC_Form.cs b/storage/ndb/src/cw/cpcc-win32/csharp/CPC_Form.cs
new file mode 100644
index 00000000000..ea1798c8c67
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/CPC_Form.cs
@@ -0,0 +1,1400 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Data;
+using System.Threading;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for Form1.
+ /// </summary>
+ public class CPC : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.TreeView tvComputerCluster;
+ private System.Windows.Forms.ContextMenu ctxTreeViewMenu;
+ private System.Windows.Forms.ColumnHeader chComputer;
+ private System.Windows.Forms.ColumnHeader chProcessName;
+ private System.Windows.Forms.ContextMenu ctxListViewMenu;
+ private System.Windows.Forms.MenuItem mainMenuItem;
+ private System.Windows.Forms.ColumnHeader chProcesses;
+ private System.Windows.Forms.MainMenu mainMenu;
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.MenuItem menuItem7;
+ private System.Windows.Forms.MenuItem menuItem10;
+ private System.Windows.Forms.MenuItem mainMenuFile;
+ private System.Windows.Forms.MenuItem mainMenuComputer;
+ private System.Windows.Forms.MenuItem subMenuComputerAdd;
+ private System.Windows.Forms.MenuItem subMenuComputerRemove;
+ private System.Windows.Forms.MenuItem subMenuComputerDisconnect;
+ private System.Windows.Forms.MenuItem subMenuComputerProperties;
+ private System.ComponentModel.IContainer components;
+
+ private System.Windows.Forms.MenuItem menuItem3;
+ private System.Windows.Forms.MenuItem computerMenuAdd;
+ private System.Windows.Forms.MenuItem computerMenuRemove;
+ private System.Windows.Forms.MenuItem menuItem5;
+ private System.Windows.Forms.MenuItem computerMenuDisconnect;
+ private System.Windows.Forms.MenuItem computerMenuConnect;
+ private System.Windows.Forms.MenuItem computerMenuProperties;
+ private System.Windows.Forms.MenuItem menuItem11;
+ private System.Windows.Forms.MenuItem tvCtxMenuComputerAdd;
+ private System.Windows.Forms.MenuItem tvCtxMenuComputerRemove;
+ private System.Windows.Forms.MenuItem tvCtxMenuComputerConnect;
+ private System.Windows.Forms.MenuItem tvCtxMenuComputerDisconnect;
+ private System.Windows.Forms.MenuItem tvCtxMenuComputerDefine;
+ private System.Windows.Forms.MenuItem tvCtxMenuDatabaseNew;
+ private System.Windows.Forms.MenuItem menuItem1;
+ private System.Windows.Forms.MenuItem menuItem2;
+ private System.Windows.Forms.MenuItem mainMenuDatabase;
+ private System.Windows.Forms.MenuItem subMenuDatabaseCreate;
+ private System.Windows.Forms.MenuItem menuItem8;
+ private System.Windows.Forms.MenuItem tvCtxMenuProperties;
+ private System.Windows.Forms.ImageList imageTV;
+
+ private ComputerMgmt computerMgmt;
+ private System.Windows.Forms.MenuItem computerMenuRefresh;
+ private System.Windows.Forms.ListView listView;
+ private System.Windows.Forms.ColumnHeader chComputerIP;
+ private System.Windows.Forms.ColumnHeader chDatabase;
+ private System.Windows.Forms.ColumnHeader chName;
+ private System.Windows.Forms.ColumnHeader chOwner;
+ private System.Windows.Forms.ColumnHeader chStatus;
+ private System.Windows.Forms.Splitter splitter2;
+ private System.Windows.Forms.Splitter splitterVertical;
+ private System.Windows.Forms.Splitter splitterHorizont;
+ private Thread guiThread;
+ private float resizeWidthRatio;
+ private System.Windows.Forms.MenuItem menuItem6;
+ private System.Windows.Forms.MenuItem menuGetStatus;
+ private System.Windows.Forms.MenuItem menuStartProcess;
+ private System.Windows.Forms.MenuItem menuRestartProcess;
+ private System.Windows.Forms.MenuItem menuStopProcess;
+ private System.Windows.Forms.MenuItem menuRemoveProcess;
+ private System.Windows.Forms.MenuItem menuRefresh;
+ private System.Windows.Forms.OpenFileDialog openHostFileDialog;
+ private System.Windows.Forms.SaveFileDialog saveHostFileDialog;
+ private float resizeHeightRatio;
+ private System.Windows.Forms.TextBox mgmConsole;
+ int i;
+ public CPC()
+ {
+ //
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ // TODO: Add any constructor code after InitializeComponent call
+ //
+ computerMgmt = new ComputerMgmt();
+ guiThread = new Thread(new ThreadStart(updateGuiThread));
+
+ // guiThread.Start();
+ }
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if (components != null)
+ {
+ components.Dispose();
+ }
+ }
+ //guiThread.Abort();
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(CPC));
+ this.tvComputerCluster = new System.Windows.Forms.TreeView();
+ this.ctxTreeViewMenu = new System.Windows.Forms.ContextMenu();
+ this.tvCtxMenuComputerAdd = new System.Windows.Forms.MenuItem();
+ this.tvCtxMenuComputerRemove = new System.Windows.Forms.MenuItem();
+ this.menuGetStatus = new System.Windows.Forms.MenuItem();
+ this.menuItem6 = new System.Windows.Forms.MenuItem();
+ this.tvCtxMenuComputerConnect = new System.Windows.Forms.MenuItem();
+ this.tvCtxMenuComputerDisconnect = new System.Windows.Forms.MenuItem();
+ this.tvCtxMenuDatabaseNew = new System.Windows.Forms.MenuItem();
+ this.tvCtxMenuComputerDefine = new System.Windows.Forms.MenuItem();
+ this.menuItem8 = new System.Windows.Forms.MenuItem();
+ this.tvCtxMenuProperties = new System.Windows.Forms.MenuItem();
+ this.imageTV = new System.Windows.Forms.ImageList(this.components);
+ this.ctxListViewMenu = new System.Windows.Forms.ContextMenu();
+ this.menuStartProcess = new System.Windows.Forms.MenuItem();
+ this.menuRestartProcess = new System.Windows.Forms.MenuItem();
+ this.menuStopProcess = new System.Windows.Forms.MenuItem();
+ this.menuRemoveProcess = new System.Windows.Forms.MenuItem();
+ this.menuRefresh = new System.Windows.Forms.MenuItem();
+ this.computerMenuAdd = new System.Windows.Forms.MenuItem();
+ this.menuItem3 = new System.Windows.Forms.MenuItem();
+ this.computerMenuRemove = new System.Windows.Forms.MenuItem();
+ this.menuItem5 = new System.Windows.Forms.MenuItem();
+ this.computerMenuDisconnect = new System.Windows.Forms.MenuItem();
+ this.computerMenuConnect = new System.Windows.Forms.MenuItem();
+ this.menuItem11 = new System.Windows.Forms.MenuItem();
+ this.computerMenuProperties = new System.Windows.Forms.MenuItem();
+ this.computerMenuRefresh = new System.Windows.Forms.MenuItem();
+ this.chComputer = new System.Windows.Forms.ColumnHeader();
+ this.chProcessName = new System.Windows.Forms.ColumnHeader();
+ this.mainMenuItem = new System.Windows.Forms.MenuItem();
+ this.chProcesses = new System.Windows.Forms.ColumnHeader();
+ this.mainMenu = new System.Windows.Forms.MainMenu();
+ this.mainMenuFile = new System.Windows.Forms.MenuItem();
+ this.menuItem2 = new System.Windows.Forms.MenuItem();
+ this.menuItem1 = new System.Windows.Forms.MenuItem();
+ this.mainMenuComputer = new System.Windows.Forms.MenuItem();
+ this.subMenuComputerAdd = new System.Windows.Forms.MenuItem();
+ this.menuItem7 = new System.Windows.Forms.MenuItem();
+ this.subMenuComputerDisconnect = new System.Windows.Forms.MenuItem();
+ this.subMenuComputerRemove = new System.Windows.Forms.MenuItem();
+ this.menuItem10 = new System.Windows.Forms.MenuItem();
+ this.subMenuComputerProperties = new System.Windows.Forms.MenuItem();
+ this.mainMenuDatabase = new System.Windows.Forms.MenuItem();
+ this.subMenuDatabaseCreate = new System.Windows.Forms.MenuItem();
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.mgmConsole = new System.Windows.Forms.TextBox();
+ this.splitterHorizont = new System.Windows.Forms.Splitter();
+ this.splitter2 = new System.Windows.Forms.Splitter();
+ this.listView = new System.Windows.Forms.ListView();
+ this.chComputerIP = new System.Windows.Forms.ColumnHeader();
+ this.chStatus = new System.Windows.Forms.ColumnHeader();
+ this.chDatabase = new System.Windows.Forms.ColumnHeader();
+ this.chName = new System.Windows.Forms.ColumnHeader();
+ this.chOwner = new System.Windows.Forms.ColumnHeader();
+ this.splitterVertical = new System.Windows.Forms.Splitter();
+ this.openHostFileDialog = new System.Windows.Forms.OpenFileDialog();
+ this.saveHostFileDialog = new System.Windows.Forms.SaveFileDialog();
+ this.panel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // tvComputerCluster
+ //
+ this.tvComputerCluster.CausesValidation = false;
+ this.tvComputerCluster.ContextMenu = this.ctxTreeViewMenu;
+ this.tvComputerCluster.Dock = System.Windows.Forms.DockStyle.Left;
+ this.tvComputerCluster.ImageList = this.imageTV;
+ this.tvComputerCluster.Name = "tvComputerCluster";
+ this.tvComputerCluster.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
+ new System.Windows.Forms.TreeNode("Computer", 0, 0),
+ new System.Windows.Forms.TreeNode("Database", 5, 5)});
+ this.tvComputerCluster.Size = new System.Drawing.Size(104, 333);
+ this.tvComputerCluster.TabIndex = 5;
+ this.tvComputerCluster.MouseDown += new System.Windows.Forms.MouseEventHandler(this.tvComputerCluster_MouseDown);
+ this.tvComputerCluster.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvComputerCluster_AfterSelect);
+ this.tvComputerCluster.BeforeCollapse += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvComputerCluster_BeforeCollapse);
+ this.tvComputerCluster.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvComputerCluster_BeforeExpand);
+ //
+ // ctxTreeViewMenu
+ //
+ this.ctxTreeViewMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.tvCtxMenuComputerAdd,
+ this.tvCtxMenuComputerRemove,
+ this.menuGetStatus,
+ this.menuItem6,
+ this.tvCtxMenuComputerConnect,
+ this.tvCtxMenuComputerDisconnect,
+ this.tvCtxMenuDatabaseNew,
+ this.tvCtxMenuComputerDefine,
+ this.menuItem8,
+ this.tvCtxMenuProperties});
+ this.ctxTreeViewMenu.Popup += new System.EventHandler(this.ctxTreeViewMenu_Popup);
+ //
+ // tvCtxMenuComputerAdd
+ //
+ this.tvCtxMenuComputerAdd.Index = 0;
+ this.tvCtxMenuComputerAdd.Text = "Add computer";
+ this.tvCtxMenuComputerAdd.Click += new System.EventHandler(this.computerMenuAdd_Click);
+ //
+ // tvCtxMenuComputerRemove
+ //
+ this.tvCtxMenuComputerRemove.Index = 1;
+ this.tvCtxMenuComputerRemove.Text = "Remove computer";
+ this.tvCtxMenuComputerRemove.Click += new System.EventHandler(this.computerMenuRemove_Click);
+ //
+ // menuGetStatus
+ //
+ this.menuGetStatus.Index = 2;
+ this.menuGetStatus.Text = "Get Status";
+ this.menuGetStatus.Click += new System.EventHandler(this.menuGetStatus_Click);
+ //
+ // menuItem6
+ //
+ this.menuItem6.Index = 3;
+ this.menuItem6.Text = "-";
+ //
+ // tvCtxMenuComputerConnect
+ //
+ this.tvCtxMenuComputerConnect.Index = 4;
+ this.tvCtxMenuComputerConnect.Text = "Connect";
+ //
+ // tvCtxMenuComputerDisconnect
+ //
+ this.tvCtxMenuComputerDisconnect.Index = 5;
+ this.tvCtxMenuComputerDisconnect.Text = "Disconnect";
+ //
+ // tvCtxMenuDatabaseNew
+ //
+ this.tvCtxMenuDatabaseNew.Index = 6;
+ this.tvCtxMenuDatabaseNew.Text = "Create database...";
+ this.tvCtxMenuDatabaseNew.Click += new System.EventHandler(this.subMenuDatabaseCreate_Click);
+ //
+ // tvCtxMenuComputerDefine
+ //
+ this.tvCtxMenuComputerDefine.Index = 7;
+ this.tvCtxMenuComputerDefine.Text = "Define process...";
+ this.tvCtxMenuComputerDefine.Click += new System.EventHandler(this.tvCtxMenuComputerDefine_Click);
+ //
+ // menuItem8
+ //
+ this.menuItem8.Index = 8;
+ this.menuItem8.Text = "-";
+ //
+ // tvCtxMenuProperties
+ //
+ this.tvCtxMenuProperties.Index = 9;
+ this.tvCtxMenuProperties.Text = "Properties";
+ //
+ // imageTV
+ //
+ this.imageTV.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+ this.imageTV.ImageSize = new System.Drawing.Size(16, 16);
+ this.imageTV.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageTV.ImageStream")));
+ this.imageTV.TransparentColor = System.Drawing.Color.Transparent;
+ //
+ // ctxListViewMenu
+ //
+ this.ctxListViewMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.menuStartProcess,
+ this.menuRestartProcess,
+ this.menuStopProcess,
+ this.menuRemoveProcess,
+ this.menuRefresh});
+ this.ctxListViewMenu.Popup += new System.EventHandler(this.ctxListViewMenu_Popup);
+ //
+ // menuStartProcess
+ //
+ this.menuStartProcess.Index = 0;
+ this.menuStartProcess.Text = "Start process";
+ this.menuStartProcess.Click += new System.EventHandler(this.startProcess);
+ //
+ // menuRestartProcess
+ //
+ this.menuRestartProcess.Index = 1;
+ this.menuRestartProcess.Text = "Restart process";
+ this.menuRestartProcess.Click += new System.EventHandler(this.restartProcess);
+ //
+ // menuStopProcess
+ //
+ this.menuStopProcess.Index = 2;
+ this.menuStopProcess.Text = "Stop process";
+ this.menuStopProcess.Click += new System.EventHandler(this.stopProcess);
+ //
+ // menuRemoveProcess
+ //
+ this.menuRemoveProcess.Index = 3;
+ this.menuRemoveProcess.Text = "Remove process";
+ this.menuRemoveProcess.Click += new System.EventHandler(this.removeProcess);
+ //
+ // menuRefresh
+ //
+ this.menuRefresh.Index = 4;
+ this.menuRefresh.Text = "Refresh";
+ this.menuRefresh.Click += new System.EventHandler(this.menuRefresh_Click);
+ //
+ // computerMenuAdd
+ //
+ this.computerMenuAdd.Index = -1;
+ this.computerMenuAdd.Text = "Add";
+ this.computerMenuAdd.Click += new System.EventHandler(this.computerMenuAdd_Click);
+ //
+ // menuItem3
+ //
+ this.menuItem3.Index = -1;
+ this.menuItem3.Text = "-";
+ //
+ // computerMenuRemove
+ //
+ this.computerMenuRemove.Index = -1;
+ this.computerMenuRemove.Text = "Remove";
+ this.computerMenuRemove.Click += new System.EventHandler(this.computerMenuRemove_Click);
+ //
+ // menuItem5
+ //
+ this.menuItem5.Index = -1;
+ this.menuItem5.Text = "-";
+ //
+ // computerMenuDisconnect
+ //
+ this.computerMenuDisconnect.Index = -1;
+ this.computerMenuDisconnect.Text = "Disconnect";
+ //
+ // computerMenuConnect
+ //
+ this.computerMenuConnect.Index = -1;
+ this.computerMenuConnect.Text = "Connect";
+ //
+ // menuItem11
+ //
+ this.menuItem11.Index = -1;
+ this.menuItem11.Text = "-";
+ //
+ // computerMenuProperties
+ //
+ this.computerMenuProperties.Index = -1;
+ this.computerMenuProperties.Text = "Properties";
+ //
+ // computerMenuRefresh
+ //
+ this.computerMenuRefresh.Index = -1;
+ this.computerMenuRefresh.Text = "Refresh";
+ this.computerMenuRefresh.Click += new System.EventHandler(this.computerMenuRefresh_Click);
+ //
+ // chComputer
+ //
+ this.chComputer.Text = "Computer";
+ //
+ // chProcessName
+ //
+ this.chProcessName.Text = "Name";
+ //
+ // mainMenuItem
+ //
+ this.mainMenuItem.Index = -1;
+ this.mainMenuItem.Text = "File";
+ //
+ // chProcesses
+ //
+ this.chProcesses.Text = "Id";
+ //
+ // mainMenu
+ //
+ this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.mainMenuFile,
+ this.mainMenuComputer,
+ this.mainMenuDatabase});
+ //
+ // mainMenuFile
+ //
+ this.mainMenuFile.Index = 0;
+ this.mainMenuFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.menuItem2,
+ this.menuItem1});
+ this.mainMenuFile.Text = "&File";
+ //
+ // menuItem2
+ //
+ this.menuItem2.Index = 0;
+ this.menuItem2.Text = "&Import...";
+ this.menuItem2.Click += new System.EventHandler(this.importHostFile);
+ //
+ // menuItem1
+ //
+ this.menuItem1.Index = 1;
+ this.menuItem1.Text = "&Export...";
+ this.menuItem1.Click += new System.EventHandler(this.exportHostFile);
+ //
+ // mainMenuComputer
+ //
+ this.mainMenuComputer.Index = 1;
+ this.mainMenuComputer.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.subMenuComputerAdd,
+ this.menuItem7,
+ this.subMenuComputerDisconnect,
+ this.subMenuComputerRemove,
+ this.menuItem10,
+ this.subMenuComputerProperties});
+ this.mainMenuComputer.Text = "&Computer";
+ //
+ // subMenuComputerAdd
+ //
+ this.subMenuComputerAdd.Index = 0;
+ this.subMenuComputerAdd.Text = "&Add Computer";
+ this.subMenuComputerAdd.Click += new System.EventHandler(this.computerMenuAdd_Click);
+ //
+ // menuItem7
+ //
+ this.menuItem7.Index = 1;
+ this.menuItem7.Text = "-";
+ //
+ // subMenuComputerDisconnect
+ //
+ this.subMenuComputerDisconnect.Index = 2;
+ this.subMenuComputerDisconnect.Text = "&Disconnect";
+ //
+ // subMenuComputerRemove
+ //
+ this.subMenuComputerRemove.Index = 3;
+ this.subMenuComputerRemove.Text = "&Remove Computer";
+ this.subMenuComputerRemove.Click += new System.EventHandler(this.computerMenuRemove_Click);
+ //
+ // menuItem10
+ //
+ this.menuItem10.Index = 4;
+ this.menuItem10.Text = "-";
+ //
+ // subMenuComputerProperties
+ //
+ this.subMenuComputerProperties.Index = 5;
+ this.subMenuComputerProperties.Text = "&Properties";
+ //
+ // mainMenuDatabase
+ //
+ this.mainMenuDatabase.Index = 2;
+ this.mainMenuDatabase.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
+ this.subMenuDatabaseCreate});
+ this.mainMenuDatabase.Text = "&Database";
+ this.mainMenuDatabase.Click += new System.EventHandler(this.subMenuDatabaseCreate_Click);
+ //
+ // subMenuDatabaseCreate
+ //
+ this.subMenuDatabaseCreate.Index = 0;
+ this.subMenuDatabaseCreate.Text = "&Create database...";
+ this.subMenuDatabaseCreate.Click += new System.EventHandler(this.subMenuDatabaseCreate_Click);
+ //
+ // panel1
+ //
+ this.panel1.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.mgmConsole,
+ this.splitterHorizont,
+ this.splitter2,
+ this.listView});
+ this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.panel1.Location = new System.Drawing.Point(104, 0);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(384, 333);
+ this.panel1.TabIndex = 6;
+ //
+ // mgmConsole
+ //
+ this.mgmConsole.AccessibleRole = System.Windows.Forms.AccessibleRole.StaticText;
+ this.mgmConsole.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.mgmConsole.Location = new System.Drawing.Point(0, 231);
+ this.mgmConsole.Multiline = true;
+ this.mgmConsole.Name = "mgmConsole";
+ this.mgmConsole.Size = new System.Drawing.Size(384, 96);
+ this.mgmConsole.TabIndex = 5;
+ this.mgmConsole.Text = "textBox1";
+ this.mgmConsole.TextChanged += new System.EventHandler(this.mgmConsole_TextChanged);
+ this.mgmConsole.Enter += new System.EventHandler(this.mgmConsole_Enter);
+ //
+ // splitterHorizont
+ //
+ this.splitterHorizont.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.splitterHorizont.Location = new System.Drawing.Point(0, 327);
+ this.splitterHorizont.MinExtra = 100;
+ this.splitterHorizont.MinSize = 100;
+ this.splitterHorizont.Name = "splitterHorizont";
+ this.splitterHorizont.Size = new System.Drawing.Size(384, 3);
+ this.splitterHorizont.TabIndex = 4;
+ this.splitterHorizont.TabStop = false;
+ //
+ // splitter2
+ //
+ this.splitter2.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.splitter2.Location = new System.Drawing.Point(0, 330);
+ this.splitter2.Name = "splitter2";
+ this.splitter2.Size = new System.Drawing.Size(384, 3);
+ this.splitter2.TabIndex = 2;
+ this.splitter2.TabStop = false;
+ //
+ // listView
+ //
+ this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.chComputerIP,
+ this.chStatus,
+ this.chDatabase,
+ this.chName,
+ this.chOwner});
+ this.listView.ContextMenu = this.ctxListViewMenu;
+ this.listView.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listView.FullRowSelect = true;
+ this.listView.Name = "listView";
+ this.listView.Size = new System.Drawing.Size(384, 333);
+ this.listView.TabIndex = 0;
+ this.listView.View = System.Windows.Forms.View.Details;
+ this.listView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView_ColumnClick_1);
+ this.listView.SelectedIndexChanged += new System.EventHandler(this.listView_SelectedIndexChanged);
+ //
+ // chComputerIP
+ //
+ this.chComputerIP.Text = "IP Adress";
+ //
+ // chStatus
+ //
+ this.chStatus.Text = "Status";
+ //
+ // chDatabase
+ //
+ this.chDatabase.Text = "Database";
+ //
+ // chName
+ //
+ this.chName.Text = "Name";
+ //
+ // chOwner
+ //
+ this.chOwner.Text = "Owner";
+ //
+ // splitterVertical
+ //
+ this.splitterVertical.Location = new System.Drawing.Point(104, 0);
+ this.splitterVertical.MinSize = 100;
+ this.splitterVertical.Name = "splitterVertical";
+ this.splitterVertical.Size = new System.Drawing.Size(3, 333);
+ this.splitterVertical.TabIndex = 7;
+ this.splitterVertical.TabStop = false;
+ this.splitterVertical.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitterVertical_SplitterMoved);
+ //
+ // openHostFileDialog
+ //
+ this.openHostFileDialog.DefaultExt = "cpc";
+ this.openHostFileDialog.Filter = "CPCd configuration files (*.cpc)|*.cpc| All Files (*.*)|*.*";
+ this.openHostFileDialog.Title = "Import a CPCd configuration file";
+ this.openHostFileDialog.FileOk += new System.ComponentModel.CancelEventHandler(this.openHostFileDialog_FileOk);
+ //
+ // saveHostFileDialog
+ //
+ this.saveHostFileDialog.Filter = "CPCd configuration files (*.cpc)|*.cpc| All Files (*.*)|*.*";
+ this.saveHostFileDialog.Title = "Export a CPCd configuration file";
+ this.saveHostFileDialog.FileOk += new System.ComponentModel.CancelEventHandler(this.saveHostFileDialog_FileOk);
+ //
+ // CPC
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(488, 333);
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.splitterVertical,
+ this.panel1,
+ this.tvComputerCluster});
+ this.Menu = this.mainMenu;
+ this.Name = "CPC";
+ this.Text = "CPC";
+ this.Resize += new System.EventHandler(this.CPC_Resize);
+ this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.CPC_MouseDown);
+ this.Closing += new System.ComponentModel.CancelEventHandler(this.CPC_Closing);
+ this.Load += new System.EventHandler(this.CPC_Load);
+ this.Activated += new System.EventHandler(this.CPC_Activated);
+ this.Paint += new System.Windows.Forms.PaintEventHandler(this.CPC_Paint);
+ this.panel1.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ [STAThread]
+ static void Main()
+ {
+ Application.Run(new CPC());
+
+ }
+
+ private void tvComputerCluster_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
+ {
+ if(e.Node.Text.ToString().Equals("Database"))
+ {
+ updateListViews("Database");
+
+ return;
+ }
+ if(e.Node.Text.ToString().Equals("Computer"))
+ {
+ //updateListViews();
+
+ updateListViews("Computer");
+ return;
+ }
+ if(e.Node.Parent.Text.ToString().Equals("Database"))
+ {
+ //updateListViews();
+ listView.Columns.Clear();
+ listView.Columns.Add(this.chName);
+ listView.Columns.Add(this.chDatabase);
+ listView.Columns.Add(this.chStatus);
+ listView.Columns.Add(this.chOwner);
+ updateDatabaseView(e.Node.Text.ToString());
+ }
+
+ if(e.Node.Parent.Text=="Computer")
+ {
+ //updateListViews();
+
+ Computer c=computerMgmt.getComputer(e.Node.Text.ToString());
+ string [] processcols= new string[5];
+ ArrayList processes;
+ processes = c.getProcesses();
+ listView.Items.Clear();
+ listView.Columns.Clear();
+ listView.Columns.Add(this.chComputer);
+ listView.Columns.Add(this.chDatabase);
+ listView.Columns.Add(this.chName);
+ listView.Columns.Add(this.chStatus);
+ listView.Columns.Add(this.chOwner);
+ if(processes != null )
+ {
+
+ listView.BeginUpdate();
+ foreach(Process p in processes)
+ {
+ processcols[0]=p.getComputer().getName();
+ processcols[1]=p.getDatabase();
+ processcols[2]=p.getName();
+ processcols[3]=p.getStatusString();
+ processcols[4]=p.getOwner();
+ ListViewItem lvp= new ListViewItem(processcols);
+ listView.Items.Add(lvp);
+ }
+
+ listView.EndUpdate();
+ }
+
+
+ listView.Show();
+ }
+
+ }
+
+
+
+ private void ctxTreeViewMenu_Popup(object sender, System.EventArgs e)
+ {
+ tvCtxMenuComputerAdd.Enabled=true;
+ tvCtxMenuComputerRemove.Enabled=true;
+ tvCtxMenuComputerConnect.Enabled=true;
+ tvCtxMenuComputerDisconnect.Enabled=true;
+ tvCtxMenuComputerDefine.Enabled=true;
+ menuGetStatus.Enabled=true;
+ tvCtxMenuDatabaseNew.Enabled=true;
+ tvCtxMenuComputerAdd.Visible=true;
+ tvCtxMenuComputerRemove.Visible=true;
+ tvCtxMenuComputerConnect.Visible=true;
+ tvCtxMenuComputerDisconnect.Visible=true;
+ tvCtxMenuComputerDefine.Visible=true;
+ tvCtxMenuDatabaseNew.Visible=true;
+ tvCtxMenuProperties.Visible=true;
+ menuGetStatus.Visible=true;
+
+ if(tvComputerCluster.SelectedNode.Text.Equals("Computer"))
+ {
+ tvCtxMenuComputerAdd.Enabled=true;
+ tvCtxMenuComputerRemove.Enabled=false;
+ tvCtxMenuComputerConnect.Enabled=false;
+ tvCtxMenuComputerDisconnect.Enabled=false;
+ tvCtxMenuComputerDefine.Enabled=false;
+ tvCtxMenuDatabaseNew.Visible=false;
+ menuGetStatus.Visible=false;
+ return;
+ }
+
+ if(tvComputerCluster.SelectedNode.Text.Equals("Database"))
+ {
+ // ctxTreeViewMenu.MenuItems.Add(menuDatabaseItem1);
+ tvCtxMenuComputerAdd.Visible=false;
+ tvCtxMenuComputerRemove.Visible=false;
+ tvCtxMenuComputerConnect.Visible=false;
+ tvCtxMenuComputerDisconnect.Visible=false;
+ tvCtxMenuComputerDefine.Visible=false;
+ tvCtxMenuDatabaseNew.Visible=true;
+ tvCtxMenuDatabaseNew.Enabled=true;
+ menuGetStatus.Visible=false;
+ menuItem6.Visible=false;
+ return;
+ }
+ if(tvComputerCluster.SelectedNode.Parent.Text.Equals("Computer"))
+ {
+
+ Computer c= computerMgmt.getComputer(tvComputerCluster.SelectedNode.Text.ToString());
+ if(c.getStatus().Equals(Computer.Status.Disconnected))
+ {
+ tvCtxMenuComputerConnect.Enabled=true;
+ tvCtxMenuComputerDisconnect.Enabled=false;
+ }
+ else
+ {
+ tvCtxMenuComputerDisconnect.Enabled=true;
+ tvCtxMenuComputerConnect.Enabled=false;
+ }
+
+ tvCtxMenuComputerAdd.Enabled=false;
+ tvCtxMenuComputerRemove.Enabled=true;
+ menuGetStatus.Visible=false;
+
+ tvCtxMenuComputerDefine.Enabled=true;
+ tvCtxMenuDatabaseNew.Visible=false;
+ return;
+ }
+
+ if(tvComputerCluster.SelectedNode.Parent.Text.Equals("Database"))
+ {
+ tvCtxMenuComputerAdd.Enabled=true;
+ tvCtxMenuComputerRemove.Enabled=false;
+ tvCtxMenuComputerConnect.Enabled=false;
+ tvCtxMenuComputerDisconnect.Enabled=false;
+ tvCtxMenuComputerDefine.Enabled=false;
+ tvCtxMenuDatabaseNew.Visible=true;
+ menuGetStatus.Visible=true;
+ return;
+ }
+
+
+ }
+
+
+ private void listView_SelectedIndexChanged(object sender, System.EventArgs e)
+ {
+ //MessageBox.Show(listView.SelectedItems[0].Text);
+ }
+
+
+ private void tvComputerCluster_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
+ { /*
+ TreeNode node = tvComputerCluster.GetNodeAt(e.X,e.Y);
+ if(node==null)
+ {
+ return;
+ }
+ tvComputerCluster.SelectedNode=node;
+// updateListViews();
+ tvComputerCluster.SelectedNode.Expand();
+ */
+ }
+
+
+ private void subMenuComputerRemove_Click(object sender, System.EventArgs e)
+ {
+ //ComputerRemoveDialog crd=new ComputerRemoveDialog(computerMgmt);
+ //crd.Show();
+ //updateListViews();
+/* string computer = tvComputerCluster.SelectedNode.Text.ToString();
+ if(MessageBox.Show(this,"Are you sure you want to remove: " +computer+ "?","Remove computer",MessageBoxButtons.YesNo)==DialogResult.Yes)
+ {
+ computerMgmt.RemoveComputer(computer);
+ }
+*/
+ }
+
+ private void subMenuComputerAdd_Click(object sender, System.EventArgs e)
+ {
+ ComputerAddDialog cad=new ComputerAddDialog(computerMgmt);
+ cad.ShowDialog();
+ cad.Dispose();
+/// updateListViews(tvComputerCluster.SelectedNode.Text.ToString());
+ }
+
+
+
+ private void updateListViews(string node)
+ {
+ if(node.Equals("Computer"))
+ {
+ listView.Columns.Clear();
+ listView.Items.Clear();
+ ArrayList list= computerMgmt.getComputerCollection();
+ string [] computercols= new string[2];
+
+
+ listView.BeginUpdate();
+ listView.Columns.Add(this.chComputer);
+ listView.Columns.Add(this.chStatus);
+ foreach (Computer computer in list)
+ {
+ computercols[0]=computer.getName();
+ computercols[1]=computer.getStatusString();
+
+ ListViewItem lvc= new ListViewItem(computercols);
+
+ listView.Items.Add(lvc);
+
+ }
+ listView.EndUpdate();
+ listView.Show();
+ }
+
+ if(node.Equals("Database"))
+ {
+
+ ArrayList databases= computerMgmt.getDatabaseCollection();
+ string [] dbcols= new string[3];
+
+
+ listView.BeginUpdate();
+ listView.Items.Clear();
+ listView.Columns.Clear();
+ listView.Columns.Add(this.chDatabase);
+ listView.Columns.Add(this.chStatus);
+ listView.Columns.Add(this.chOwner);
+ foreach (Database db in databases)
+ {
+ dbcols[0]=db.getName();
+ dbcols[1]=db.getStatusString();
+ dbcols[2]=db.getOwner();
+
+ ListViewItem lvc= new ListViewItem(dbcols);
+
+ listView.Items.Add(lvc);
+
+ }
+ listView.EndUpdate();
+
+ listView.Show();
+ }
+
+ }
+
+ public void updateDatabaseView(string database)
+ {
+ Database d=computerMgmt.getDatabase(database);
+ string [] processcols= new string[5];
+ ArrayList processes = d.getProcesses();
+ listView.Items.Clear();
+ if(processes != null )
+ {
+
+ listView.BeginUpdate();
+ listView.Columns.Clear();
+ listView.Columns.Add(this.chComputer);
+ listView.Columns.Add(this.chDatabase);
+ listView.Columns.Add(this.chName);
+ listView.Columns.Add(this.chStatus);
+ listView.Columns.Add(this.chOwner);
+
+ foreach(Process p in processes)
+ {
+ processcols[0]=p.getComputer().getName();
+ processcols[1]=p.getDatabase();
+ processcols[2]=p.getName();
+ processcols[3]=p.getStatusString();
+ processcols[4]=p.getOwner();
+ ListViewItem lvp= new ListViewItem(processcols);
+ listView.Items.Add(lvp);
+ }
+
+ listView.EndUpdate();
+ }
+
+ listView.Show();
+ }
+
+ private void updateTreeViews()
+ {
+ //tvComputerCluster.Nodes.Clear();
+ ArrayList computers= computerMgmt.getComputerCollection();
+
+ ArrayList databases= computerMgmt.getDatabaseCollection();
+
+ tvComputerCluster.BeginUpdate();
+ tvComputerCluster.Nodes[0].Nodes.Clear();
+ tvComputerCluster.Nodes[1].Nodes.Clear();
+ if(computers != null)
+ {
+ foreach (Computer computer in computers)
+ {
+ tvComputerCluster.Nodes[0].Nodes.Add(new TreeNode(computer.getName().ToString()));
+ }
+ }
+ if(databases != null)
+ {
+ foreach (Database db in databases)
+ {
+ tvComputerCluster.Nodes[1].Nodes.Add(new TreeNode(db.getName().ToString()));
+ }
+ }
+
+ tvComputerCluster.EndUpdate();
+ }
+
+
+ private void CPC_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
+ {
+ //updateListViews();
+ //updateTreeViews();
+
+ }
+
+ private void CPC_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
+ {
+ if(tvComputerCluster.SelectedNode!=null)
+ {
+ if(tvComputerCluster.SelectedNode.Text.ToString().Equals("Computer"))
+ updateListViews("Computer");
+ }
+
+ //updateListViews();
+ //updateTreeViews();
+ }
+
+ private void CPC_Activated(object sender, System.EventArgs e)
+ {
+ updateListViews(tvComputerCluster.SelectedNode.Text.ToString());
+ //updateListViews();
+ updateTreeViews();
+ }
+
+
+ private void computerMenuAdd_Click(object sender, System.EventArgs e)
+ {
+ ComputerAddDialog cad=new ComputerAddDialog(computerMgmt);
+ cad.ShowDialog();
+ cad.Dispose();
+
+ }
+
+ private void computerMenuRemove_Click(object sender, System.EventArgs e)
+ {
+
+ string computer = tvComputerCluster.SelectedNode.Text.ToString();
+ if(MessageBox.Show("Are you sure you want to remove: " + computer +"?\n" + "This will remove all processes on the computer!" ,"Remove selected computer",MessageBoxButtons.YesNo, MessageBoxIcon.Question)== DialogResult.Yes)
+ {
+ removeComputer(computer);
+ }
+ }
+
+ private void removeComputer(string computer)
+ {
+ ArrayList processes;
+ Computer c=computerMgmt.getComputer(computer);
+ processes = c.getProcesses();
+
+ /*foreach(Process p in processes)
+ {
+ removeProcess(computer,p.getName());
+ processes=c.getProcesses();
+ }
+*/
+ if(computerMgmt.RemoveComputer(computer))
+ {
+ tvComputerCluster.SelectedNode=tvComputerCluster.SelectedNode.PrevVisibleNode;
+ this.updateTreeViews();
+ this.updateListViews("Computer");
+
+ if(tvComputerCluster.SelectedNode!=null)
+ this.updateListViews(tvComputerCluster.SelectedNode.Text.ToString());
+ //updateListViews();
+ }
+ }
+
+ private void listView_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
+ {
+
+ if(listView.Sorting.Equals(SortOrder.Ascending))
+ listView.Sorting=SortOrder.Descending;
+ else
+ listView.Sorting=SortOrder.Ascending;
+
+ }
+
+
+ private void subMenuDatabaseCreate_Click(object sender, System.EventArgs e)
+ {
+ PanelWizard p = new PanelWizard(this.computerMgmt);
+ p.ShowDialog();
+ }
+
+ private void tvCtxMenuComputerDefine_Click(object sender, System.EventArgs e)
+ {
+ ProcessDefineDialog pdd = new ProcessDefineDialog(this.computerMgmt,
+ tvComputerCluster.SelectedNode.Text.ToString());
+ pdd.Show();
+ }
+
+ private void listView_ItemActivate(object sender, System.EventArgs e)
+ {
+ updateDatabaseView(listView.SelectedItems[0].Text.ToString());
+ for(int i=0;i<tvComputerCluster.Nodes[1].Nodes.Count;i++)
+ {
+ if(tvComputerCluster.Nodes[1].Nodes[i].Text.ToString().Equals(listView.SelectedItems[0].Text.ToString()))
+ {
+ tvComputerCluster.SelectedNode=tvComputerCluster.Nodes[1].Nodes[i];
+ break;
+ }
+ }
+
+
+ }
+
+ private void CPC_Resize(object sender, System.EventArgs e)
+ {
+ if(this.Width < 200) this.Width=200;
+ if(this.Height <200) this.Height=200;
+ this.tvComputerCluster.Width=(int)(this.Width*this.resizeWidthRatio);
+ this.listView.Height=(int)(this.Height*this.resizeHeightRatio);
+
+ //this.Size=new System.Drawing.Size((int)(this.Size.Width*this.tvComputerCluster.Width
+
+ }
+
+
+
+ private void updateGuiThread()
+ {
+ while(true) {
+ if(tvComputerCluster.SelectedNode!=null)
+ {
+ if(tvComputerCluster.SelectedNode.Text.ToString().Equals("Computer"))
+ updateListViews("Computer");
+ }
+ Thread.Sleep(1000);
+ }
+ }
+
+ private void computerMenuRefresh_Click(object sender, System.EventArgs e)
+ {
+ updateListViews("Computer");
+ }
+
+ private void CPC_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ /*clean up*/
+ ArrayList comp = this.computerMgmt.getComputerCollection();
+ foreach(Computer c in comp)
+ {
+ c.disconnect();
+ }
+ }
+
+ private void CPC_Load(object sender, System.EventArgs e)
+ {
+ this.tvComputerCluster.Width=104;
+ resizeWidthRatio =(float) ((float)(this.tvComputerCluster.Width)/(float)(this.Width));
+ resizeHeightRatio = (float) ((float)(this.listView.Height)/(float)(this.Height));
+ listView.Columns.Clear();
+ listView.Columns.Add(this.chComputer);
+ listView.Columns.Add(this.chStatus);
+
+ }
+
+ private void splitterVertical_SplitterMoved(object sender, System.Windows.Forms.SplitterEventArgs e)
+ {
+ if(this.Width < 500)
+ this.Width=500;
+ }
+
+ private void menuGetStatus_Click(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void tvComputerCluster_BeforeExpand(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
+ {
+ if (e.Node.Parent!=null && e.Node.Nodes.Count !=0)
+ e.Cancel=true;
+ if(e.Node.IsExpanded)
+ e.Cancel=true;
+ }
+
+ private void tvComputerCluster_BeforeCollapse(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
+ {
+ e.Cancel=true;
+ if (e.Node.Parent!=null && e.Node.Nodes.Count !=0)
+ e.Cancel=true;
+ if(e.Node.IsExpanded)
+ e.Cancel=false;
+ }
+
+
+
+ private void ctxListViewMenu_Popup(object sender, System.EventArgs e)
+ {
+
+ menuStartProcess.Visible=false;
+ menuStopProcess.Visible=false;
+ menuRestartProcess.Visible=false;
+ menuRemoveProcess.Visible=false;
+ menuRefresh.Visible=false;
+
+
+ if(this.tvComputerCluster.SelectedNode.Text.Equals("Computer"))
+ {
+ return;
+ }
+
+ if(this.tvComputerCluster.SelectedNode.Text.Equals("Database"))
+ {
+ return;
+ }
+
+ if(this.tvComputerCluster.SelectedNode.Parent.Text.Equals("Computer"))
+ {
+ if(listView.SelectedItems==null)
+ return;
+ menuRefresh.Visible=true;
+ }
+ if(this.tvComputerCluster.SelectedNode.Parent.Text.Equals("Database"))
+ {
+ if(listView.SelectedItems==null)
+ return;
+ menuStartProcess.Visible=true;
+ menuStopProcess.Visible=true;
+ menuRestartProcess.Visible=true;
+ menuRemoveProcess.Visible=true;
+ menuRefresh.Visible=true;
+ menuStopProcess.Enabled=true;
+ menuStartProcess.Enabled=true;
+ menuRestartProcess.Enabled=true;
+ menuRemoveProcess.Enabled=true;
+ menuRefresh.Enabled=true;
+ }
+
+
+ computerMenuRemove.Enabled=true;
+ computerMenuConnect.Enabled=true;
+ computerMenuDisconnect.Enabled=true;
+ computerMenuRefresh.Enabled=true;
+ string selectedItem="";
+ if(listView.SelectedItems.Count>0)
+ selectedItem=listView.FocusedItem.Text.ToString();
+
+
+ if(selectedItem.Equals(""))
+ {
+ computerMenuAdd.Enabled=true;
+ computerMenuRemove.Enabled=false;
+ computerMenuConnect.Enabled=false;
+ computerMenuDisconnect.Enabled=false;
+ return;
+ }
+ else
+ {
+ computerMenuAdd.Enabled=false;
+ if(computerMgmt.getStatus(selectedItem).Equals(Computer.Status.Connected))
+ {
+ computerMenuConnect.Enabled=false;
+ computerMenuRemove.Enabled=true;
+ }
+ if(computerMgmt.getStatus(selectedItem).Equals(Computer.Status.Disconnected))
+ computerMenuDisconnect.Enabled=false;
+ }
+
+
+ }
+
+ private void startProcess(object sender, System.EventArgs e)
+ {
+ if(listView.SelectedItems.Count==0)
+ return;
+
+ string computer = listView.SelectedItems[0].SubItems[0].Text.ToString();
+ string process = listView.SelectedItems[0].SubItems[2].Text.ToString();
+
+ if(computerMgmt.getComputer(computer).getProcessByName(process).getStatus()==Process.Status.Running)
+ {
+ MessageBox.Show(this,"The process is already started!" ,"Process failed to start",MessageBoxButtons.OK);
+ return;
+ }
+
+ int status = startProcess(listView.SelectedItems[0].SubItems[0].Text.ToString(),listView.SelectedItems[0].SubItems[2].Text.ToString());
+
+
+ if(status < 0)
+ MessageBox.Show(this,"Either the link is not OK, or the process is misconfigured! Status : " + status,"Process failed to start",MessageBoxButtons.OK);
+ else
+ MessageBox.Show(this,"The process was sucessfully started!","Process started",MessageBoxButtons.OK);
+
+ }
+
+ private int startProcess(string computer, string process)
+ {
+ Computer c=computerMgmt.getComputer(computer);
+ int status = c.startProcess(c.getProcessByName(process));
+ return status;
+ }
+
+ private void listView_ColumnClick_1(object sender, System.Windows.Forms.ColumnClickEventArgs e)
+ {
+ // if(listView.Columns[e.Column].Text.Equals("Computer"))
+ // {
+ if(listView.Sorting.Equals(SortOrder.Ascending))
+ {
+ listView.Sorting=SortOrder.Descending;
+ }
+ else
+ {
+ listView.Sorting=SortOrder.Ascending;
+ }
+ // }
+ }
+
+ private void removeProcess(object sender, System.EventArgs e)
+ {
+ if(listView.SelectedItems.Count==0)
+ return;
+ string process = listView.SelectedItems[0].SubItems[2].Text.ToString();
+ string computer = listView.SelectedItems[0].SubItems[0].Text.ToString();
+
+ if(MessageBox.Show("Are you sure that you want to remove " + process + " permanently?","Remove process",MessageBoxButtons.YesNo) == DialogResult.No)
+ return;
+ removeProcess(computer,process);
+ MessageBox.Show(this,"The process was sucessfully removed!","Remove process",MessageBoxButtons.OK);
+ }
+
+ private void removeProcess(string computer, string process)
+ {
+
+ Computer c=computerMgmt.getComputer(computer);
+ stopProcess(computer,process);
+ int status = c.undefineProcess(c.getProcessByName(process));
+ //if(status < 0)
+ // MessageBox.Show(this,"The process could not be removed!","Failed to remove process",MessageBoxButtons.OK);
+ // else
+ // {
+ Database db = computerMgmt.getDatabase((c.getProcessByName(process).getDatabase()));
+ db.removeProcess(process);
+ c.removeProcess(process,db.getName());
+ updateListViews("Database");
+ // }
+ }
+
+ private void stopProcess(object sender, System.EventArgs e)
+ {
+ if(listView.SelectedItems.Count==0)
+ return;
+ string computer = listView.SelectedItems[0].SubItems[0].Text.ToString();
+ string process = listView.SelectedItems[0].SubItems[2].Text.ToString();
+ if(computerMgmt.getComputer(computer).getProcessByName(process).getStatus()==Process.Status.Stopped)
+ {
+ MessageBox.Show(this,"The process is already stopped!" ,"Process failed to stop",MessageBoxButtons.OK);
+ return;
+ }
+
+ if(DialogResult.No==MessageBox.Show(this,"Are you sure you want to stop the " + process + " process?","Stop process!", MessageBoxButtons.YesNo))
+ return;
+
+ int status = stopProcess(computer, process);
+ if(status < 0)
+ MessageBox.Show(this,"The process could not be stopped. Status: " + status ,"Process failed to stop",MessageBoxButtons.OK);
+ else
+ MessageBox.Show(this,"The process was sucessfully stopped!","Process stopped",MessageBoxButtons.OK);
+ }
+
+ private int stopProcess(string computer, string process)
+ {
+ Computer c=computerMgmt.getComputer(computer);
+ int status = c.stopProcess(c.getProcessByName(process));
+ return status;
+ }
+
+ private void restartProcess(object sender, System.EventArgs e)
+ {
+ if(listView.SelectedItems.Count==0)
+ return;
+ string computer = listView.SelectedItems[0].SubItems[0].Text.ToString();
+ string process = listView.SelectedItems[0].SubItems[2].Text.ToString();
+ if(stopProcess(computer, process)<0)
+ {
+ MessageBox.Show("Restart process failed!!!", "Restart process");
+ return;
+ }
+ if(startProcess(computer, process)<0)
+ {
+ MessageBox.Show("Restart process failed!!!", "Restart process");
+ return;
+ }
+ MessageBox.Show("Succesfully restarted the process!","Restart process");
+ }
+
+ private void menuRefresh_Click(object sender, System.EventArgs e)
+ {
+ //string computer = tvComputerCluster.SelectedNode.Text;
+
+ this.listProcesses();
+ }
+
+ private void importHostFile(object sender, System.EventArgs e)
+ {
+ openHostFileDialog.ShowDialog();
+ }
+
+ private void exportHostFile(object sender, System.EventArgs e)
+ {
+ saveHostFileDialog.ShowDialog();
+ }
+
+ private void listProcesses()
+ {
+ /* add process in computer list*/
+ ArrayList computers = computerMgmt.getComputerCollection();
+ foreach(Computer c in computers)
+ {
+ c.listProcesses();
+ ArrayList processes = c.getProcesses();
+ if(processes!=null)
+ {
+ foreach(Process p in processes)
+ {
+ Database db = computerMgmt.getDatabase(p.getDatabase());
+ if(db!=null)
+ {
+ p.setDefined(true);
+ db.addProcessCheck(p);
+ }
+ }
+ }
+ }
+ updateListViews("Computer");
+ updateListViews("Database");
+ }
+
+ private void openHostFileDialog_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ computerMgmt.importHostFile(openHostFileDialog.FileName);
+ this.updateTreeViews();
+ openHostFileDialog.Dispose();
+ listProcesses();
+ }
+
+ private void saveHostFileDialog_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ computerMgmt.exportHostFile(saveHostFileDialog.FileName);
+ saveHostFileDialog.Dispose();
+ }
+
+ private void mgmConsole_Enter(object sender, System.EventArgs e)
+ {/*
+ //telnetclient.telnetClient tc= new telnetclient.telnetClient("10.0.13.1",10000,mgmConsole);
+ socketcomm.SocketComm sc = new socketcomm.SocketComm("10.0.13.1",10000);
+ sc.doConnect();
+ while(!sc.isConnected())
+ {
+ Thread.Sleep(100);
+ }
+ sc.writeMessage("get status\r");
+ string line = sc.readLine();
+ while(!line.Equals(""))
+ {
+ MessageBox.Show(line);
+ line=sc.readLine();
+ }
+*/
+ }
+
+ private void mgmConsole_TextChanged(object sender, System.EventArgs e)
+ {
+
+ }
+
+
+
+
+
+
+
+
+
+ }
+
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/Computer.cs b/storage/ndb/src/cw/cpcc-win32/csharp/Computer.cs
new file mode 100644
index 00000000000..9763fac5622
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/Computer.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Data;
+using System.IO;
+using NDB_CPC.socketcomm;
+using NDB_CPC.simpleparser;
+
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for Computer.
+ /// </summary>
+ public class Computer
+ {
+ public enum Status {Disconnected=1,Connected=2, Unknown=3}
+ private string m_ip;
+ private int m_cpcdPort;
+ private string m_name;
+ private Status m_status;
+ private ArrayList m_processes;
+ private SocketComm m_socket;
+ public Computer(string name, int port)
+ {
+ m_name = name;
+ m_status = Status.Disconnected;
+ m_processes = new ArrayList();
+ m_cpcdPort=port;
+ m_socket = new SocketComm(m_name,m_cpcdPort);
+ }
+
+ public Computer(string name, string ip)
+ {
+ m_ip = ip;
+ m_name = name;
+ m_status = Status.Disconnected;
+ m_processes = new ArrayList();
+ m_cpcdPort=1234; //default port
+ m_socket = new SocketComm(m_ip,m_cpcdPort);
+ }
+
+ public void connectToCpcd()
+ {
+ m_socket.doConnect();
+ }
+
+ private bool sendMessage(string str)
+ {
+ return m_socket.writeMessage(str);
+
+ }
+
+ public string getName() {return m_name;}
+ public string getIp() {return m_ip;}
+ public ArrayList getProcesses()
+ {
+ if(m_processes.Count>0)
+ return m_processes;
+ else
+ return null;
+ }
+ public string getStatusString()
+ {
+ try
+ {
+ if(m_socket.isConnected())
+ return "Connected";
+ else
+ return "Disconnected";
+ }
+ catch(Exception e)
+ {
+ return "Unknown";
+ }
+ }
+
+
+ public bool isConnected()
+ {
+ if(m_socket.isConnected())
+ return true;
+ return false;
+ }
+
+ public Status getStatus()
+ {
+ try
+ {
+ if(m_socket.isConnected())
+ return Status.Connected;
+ else
+ return Status.Disconnected;
+ }
+ catch(Exception e)
+ {
+ return Status.Unknown;
+ }
+ }
+
+ public void setStatus(Status status)
+ {
+ m_status=status;
+ }
+
+ public void addProcess(Process process)
+ {
+ m_processes.Add(process);
+ }
+
+ public Process getProcessByName(string name)
+ {
+ foreach(Process process in m_processes)
+ {
+ if(process.getName().Equals(name))
+ return process;
+ }
+ return null;
+ }
+
+
+ public bool removeProcess(string name, string database)
+ {
+ foreach(Process p in m_processes)
+ {
+ if(p.getName().Equals(name) && p.getDatabase().Equals(database))
+ {
+ m_processes.Remove(p);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void disconnect()
+ {
+ m_socket.disconnect();
+ }
+ public Process getProcess(string id)
+ {
+ foreach(Process process in m_processes)
+ {
+ if(process.getId().Equals(id))
+ return process;
+ }
+ return null;
+ }
+
+ public int listProcesses()
+ {
+ string list = "list processes\n\n";
+
+ if(!sendMessage(list))
+ return -2;
+
+ SimpleCPCParser.parse(m_processes, this, m_socket);
+ return 1;
+ }
+
+ public int defineProcess(Process p)
+ {
+ string define = "define process \n";
+ define = define + "name:" + p.getName() + "\n";
+ define = define + "group:" + p.getDatabase() + "\n";
+ define = define + "env:" + "NDB_CONNECTSTRING="+p.getConnectString() ;
+ if(p.getEnv().Equals(""))
+ define = define + "\n";
+ else
+ define = define + " " + p.getEnv() + "\n";
+
+ //if(p.getPath().EndsWith("\\"))
+ // define = define + "path:" + p.getPath()+ "ndb" + "\n";
+ //else
+ define = define + "path:" + p.getPath() + "\n";
+ define = define + "args:" + p.getArgs() + "\n";
+ define = define + "type:" + "permanent" + "\n";
+ define = define + "cwd:" + p.getCwd() + "\n";
+ define = define + "owner:" + "ejohson" + "\n\n";
+
+ if(!sendMessage(define))
+ return -2;
+
+ SimpleCPCParser.parse(p, m_socket);
+ if(p.isDefined())
+ return 1;
+ else
+ return -1;
+
+ }
+
+ public int startProcess(Process p)
+ {
+ if(!p.isDefined())
+ {
+ this.defineProcess(p);
+ if(!p.isDefined())
+ return -4; //process misconfigured
+
+ }
+ string start= "start process \n";
+ start = start + "id:" + p.getId() + "\n\n";
+ if(!sendMessage(start))
+ return -2;
+ SimpleCPCParser.parse(p, m_socket);
+ if(p.getStatus().Equals(Process.Status.Running))
+ return 1;
+ else
+ return -1;
+ }
+
+ public int stopProcess(Process p)
+ {
+ if(!p.isDefined())
+ {
+ return -4; //process not defined
+ }
+ string stop= "stop process \n";
+ stop = stop + "id:" + p.getId() + "\n\n";
+ if(!sendMessage(stop))
+ return -2;
+ SimpleCPCParser.parse(p, m_socket);
+
+ if(p.getStatus().Equals(Process.Status.Stopped))
+ return 1;
+ else
+ return -1;
+ }
+
+ public int undefineProcess(Process p)
+ {
+ if(!p.isDefined())
+ {
+ return -4; //process not defined
+ }
+ string undefine= "undefine process \n";
+ undefine = undefine + "id:" + p.getId() + "\n\n";
+ if(!sendMessage(undefine))
+ return -2;
+ SimpleCPCParser.parse(p, m_socket);
+ if(!p.isDefined())
+ {
+ return 1;
+
+ }
+ return -1;
+ }
+
+ public int getCpcdPort()
+ {
+ return this.m_cpcdPort;
+ }
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/ComputerAddDialog.cs b/storage/ndb/src/cw/cpcc-win32/csharp/ComputerAddDialog.cs
new file mode 100644
index 00000000000..c01e41f3e60
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/ComputerAddDialog.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for ComputerAddDialog.
+ /// </summary>
+ public class ComputerAddDialog : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox textboxComputerName;
+ private System.Windows.Forms.Button btnAdd;
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Label label2;
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.Container components = null;
+ private System.Windows.Forms.Label label6;
+ private System.Windows.Forms.CheckBox checkBoxDefault;
+ private System.Windows.Forms.TextBox textBoxPort;
+
+ private ComputerMgmt mgmt;
+ public ComputerAddDialog(ComputerMgmt mgmt)
+ {
+ //
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ //
+ // TODO: Add any constructor code after InitializeComponent call
+ //
+ this.mgmt=mgmt;
+ }
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.textboxComputerName = new System.Windows.Forms.TextBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.btnAdd = new System.Windows.Forms.Button();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.label2 = new System.Windows.Forms.Label();
+ this.label6 = new System.Windows.Forms.Label();
+ this.textBoxPort = new System.Windows.Forms.TextBox();
+ this.checkBoxDefault = new System.Windows.Forms.CheckBox();
+ this.SuspendLayout();
+ //
+ // textboxComputerName
+ //
+ this.textboxComputerName.Location = new System.Drawing.Point(128, 16);
+ this.textboxComputerName.Name = "textboxComputerName";
+ this.textboxComputerName.Size = new System.Drawing.Size(136, 20);
+ this.textboxComputerName.TabIndex = 0;
+ this.textboxComputerName.Text = "";
+ //
+ // label1
+ //
+ this.label1.Location = new System.Drawing.Point(40, 16);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(88, 23);
+ this.label1.TabIndex = 1;
+ this.label1.Text = "Computer name:";
+ this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // btnAdd
+ //
+ this.btnAdd.Location = new System.Drawing.Point(112, 128);
+ this.btnAdd.Name = "btnAdd";
+ this.btnAdd.Size = new System.Drawing.Size(80, 24);
+ this.btnAdd.TabIndex = 4;
+ this.btnAdd.Text = "Add";
+ this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
+ //
+ // btnCancel
+ //
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(200, 128);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(80, 24);
+ this.btnCancel.TabIndex = 5;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ //
+ // label2
+ //
+ this.label2.Location = new System.Drawing.Point(128, 40);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(136, 16);
+ this.label2.TabIndex = 4;
+ this.label2.Text = "(e.g. Ndb01 or 10.0.1.1)";
+ //
+ // label6
+ //
+ this.label6.Location = new System.Drawing.Point(48, 64);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(80, 24);
+ this.label6.TabIndex = 9;
+ this.label6.Text = "CPCd port:";
+ this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // textBoxPort
+ //
+ this.textBoxPort.Enabled = false;
+ this.textBoxPort.Location = new System.Drawing.Point(128, 64);
+ this.textBoxPort.Name = "textBoxPort";
+ this.textBoxPort.Size = new System.Drawing.Size(136, 20);
+ this.textBoxPort.TabIndex = 2;
+ this.textBoxPort.TabStop = false;
+ this.textBoxPort.Text = "";
+ //
+ // checkBoxDefault
+ //
+ this.checkBoxDefault.Checked = true;
+ this.checkBoxDefault.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.checkBoxDefault.Location = new System.Drawing.Point(96, 96);
+ this.checkBoxDefault.Name = "checkBoxDefault";
+ this.checkBoxDefault.Size = new System.Drawing.Size(168, 16);
+ this.checkBoxDefault.TabIndex = 3;
+ this.checkBoxDefault.Text = "Use default port (1234)?";
+ this.checkBoxDefault.CheckedChanged += new System.EventHandler(this.checkBoxDefault_CheckedChanged);
+ //
+ // ComputerAddDialog
+ //
+ this.AcceptButton = this.btnAdd;
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = new System.Drawing.Size(298, 159);
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.checkBoxDefault,
+ this.label6,
+ this.textBoxPort,
+ this.label2,
+ this.btnCancel,
+ this.btnAdd,
+ this.label1,
+ this.textboxComputerName});
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "ComputerAddDialog";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Add a computer";
+ this.Load += new System.EventHandler(this.ComputerAddDialog_Load);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ private void btnCancel_Click(object sender, System.EventArgs e)
+ {
+ this.Close();
+ this.Dispose();
+ }
+
+ private void btnAdd_Click(object sender, System.EventArgs e)
+ {
+ int port;
+ if(this.textboxComputerName.Text.Equals(""))
+ {
+ MessageBox.Show(this,"A computer must have an IP address or a host name","Warning!",MessageBoxButtons.OK);
+ return;
+ }
+ if(this.checkBoxDefault.Checked)
+ {
+ port=1234;
+ }
+ else
+ {
+ if(this.textBoxPort.Text.Equals(""))
+ {
+ MessageBox.Show(this,"You must specify a port number!!!","Warning!",MessageBoxButtons.OK);
+ return;
+ }
+ else
+ {
+ try
+ {
+ port=Convert.ToInt32(this.textBoxPort.Text.ToString());
+
+ }
+ catch (Exception exception)
+ {
+ MessageBox.Show(this,"Port number must be numeric!!!","Warning!",MessageBoxButtons.OK);
+ return;
+ }
+ }
+ }
+
+ if(mgmt.getComputer(this.textboxComputerName.Text)==null)
+ {
+ mgmt.AddComputer(this.textboxComputerName.Text.ToString(),port);}
+ else
+ {
+ MessageBox.Show("This computer does already exist!", "Add computer");
+ return;
+ }
+
+ this.Dispose();
+ }
+
+ private void ComputerAddDialog_Load(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void checkBoxDefault_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if(checkBoxDefault.Checked)
+ textBoxPort.Enabled=false;
+ else
+ textBoxPort.Enabled=true;
+ }
+
+
+
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/ComputerRemoveDialog.cs b/storage/ndb/src/cw/cpcc-win32/csharp/ComputerRemoveDialog.cs
new file mode 100644
index 00000000000..5b4d1b56df7
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/ComputerRemoveDialog.cs
@@ -0,0 +1,228 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for ComputerRemoveDialog.
+ /// </summary>
+ public class ComputerRemoveDialog : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.ComboBox comboComputer;
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Button btnRemove;
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.Container components = null;
+ private ComputerMgmt mgmt;
+
+ public ComputerRemoveDialog(ComputerMgmt mgmt)
+ {
+ //
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ //
+ // TODO: Add any constructor code after InitializeComponent call
+ //
+ this.mgmt=mgmt;
+ }
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(ComputerRemoveDialog));
+ this.label1 = new System.Windows.Forms.Label();
+ this.comboComputer = new System.Windows.Forms.ComboBox();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.btnRemove = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AccessibleDescription = ((string)(resources.GetObject("label1.AccessibleDescription")));
+ this.label1.AccessibleName = ((string)(resources.GetObject("label1.AccessibleName")));
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(resources.GetObject("label1.Anchor")));
+ this.label1.AutoSize = ((bool)(resources.GetObject("label1.AutoSize")));
+ this.label1.Dock = ((System.Windows.Forms.DockStyle)(resources.GetObject("label1.Dock")));
+ this.label1.Enabled = ((bool)(resources.GetObject("label1.Enabled")));
+ this.label1.Font = ((System.Drawing.Font)(resources.GetObject("label1.Font")));
+ this.label1.Image = ((System.Drawing.Image)(resources.GetObject("label1.Image")));
+ this.label1.ImageAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("label1.ImageAlign")));
+ this.label1.ImageIndex = ((int)(resources.GetObject("label1.ImageIndex")));
+ this.label1.ImeMode = ((System.Windows.Forms.ImeMode)(resources.GetObject("label1.ImeMode")));
+ this.label1.Location = ((System.Drawing.Point)(resources.GetObject("label1.Location")));
+ this.label1.Name = "label1";
+ this.label1.RightToLeft = ((System.Windows.Forms.RightToLeft)(resources.GetObject("label1.RightToLeft")));
+ this.label1.Size = ((System.Drawing.Size)(resources.GetObject("label1.Size")));
+ this.label1.TabIndex = ((int)(resources.GetObject("label1.TabIndex")));
+ this.label1.Text = resources.GetString("label1.Text");
+ this.label1.TextAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("label1.TextAlign")));
+ this.label1.Visible = ((bool)(resources.GetObject("label1.Visible")));
+ //
+ // comboComputer
+ //
+ this.comboComputer.AccessibleDescription = ((string)(resources.GetObject("comboComputer.AccessibleDescription")));
+ this.comboComputer.AccessibleName = ((string)(resources.GetObject("comboComputer.AccessibleName")));
+ this.comboComputer.Anchor = ((System.Windows.Forms.AnchorStyles)(resources.GetObject("comboComputer.Anchor")));
+ this.comboComputer.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("comboComputer.BackgroundImage")));
+ this.comboComputer.Dock = ((System.Windows.Forms.DockStyle)(resources.GetObject("comboComputer.Dock")));
+ this.comboComputer.Enabled = ((bool)(resources.GetObject("comboComputer.Enabled")));
+ this.comboComputer.Font = ((System.Drawing.Font)(resources.GetObject("comboComputer.Font")));
+ this.comboComputer.ImeMode = ((System.Windows.Forms.ImeMode)(resources.GetObject("comboComputer.ImeMode")));
+ this.comboComputer.IntegralHeight = ((bool)(resources.GetObject("comboComputer.IntegralHeight")));
+ this.comboComputer.ItemHeight = ((int)(resources.GetObject("comboComputer.ItemHeight")));
+ this.comboComputer.Location = ((System.Drawing.Point)(resources.GetObject("comboComputer.Location")));
+ this.comboComputer.MaxDropDownItems = ((int)(resources.GetObject("comboComputer.MaxDropDownItems")));
+ this.comboComputer.MaxLength = ((int)(resources.GetObject("comboComputer.MaxLength")));
+ this.comboComputer.Name = "comboComputer";
+ this.comboComputer.RightToLeft = ((System.Windows.Forms.RightToLeft)(resources.GetObject("comboComputer.RightToLeft")));
+ this.comboComputer.Size = ((System.Drawing.Size)(resources.GetObject("comboComputer.Size")));
+ this.comboComputer.Sorted = true;
+ this.comboComputer.TabIndex = ((int)(resources.GetObject("comboComputer.TabIndex")));
+ this.comboComputer.Text = resources.GetString("comboComputer.Text");
+ this.comboComputer.Visible = ((bool)(resources.GetObject("comboComputer.Visible")));
+ this.comboComputer.SelectedIndexChanged += new System.EventHandler(this.comboComputer_SelectedIndexChanged);
+ //
+ // btnCancel
+ //
+ this.btnCancel.AccessibleDescription = ((string)(resources.GetObject("btnCancel.AccessibleDescription")));
+ this.btnCancel.AccessibleName = ((string)(resources.GetObject("btnCancel.AccessibleName")));
+ this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)(resources.GetObject("btnCancel.Anchor")));
+ this.btnCancel.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("btnCancel.BackgroundImage")));
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Dock = ((System.Windows.Forms.DockStyle)(resources.GetObject("btnCancel.Dock")));
+ this.btnCancel.Enabled = ((bool)(resources.GetObject("btnCancel.Enabled")));
+ this.btnCancel.FlatStyle = ((System.Windows.Forms.FlatStyle)(resources.GetObject("btnCancel.FlatStyle")));
+ this.btnCancel.Font = ((System.Drawing.Font)(resources.GetObject("btnCancel.Font")));
+ this.btnCancel.Image = ((System.Drawing.Image)(resources.GetObject("btnCancel.Image")));
+ this.btnCancel.ImageAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("btnCancel.ImageAlign")));
+ this.btnCancel.ImageIndex = ((int)(resources.GetObject("btnCancel.ImageIndex")));
+ this.btnCancel.ImeMode = ((System.Windows.Forms.ImeMode)(resources.GetObject("btnCancel.ImeMode")));
+ this.btnCancel.Location = ((System.Drawing.Point)(resources.GetObject("btnCancel.Location")));
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.RightToLeft = ((System.Windows.Forms.RightToLeft)(resources.GetObject("btnCancel.RightToLeft")));
+ this.btnCancel.Size = ((System.Drawing.Size)(resources.GetObject("btnCancel.Size")));
+ this.btnCancel.TabIndex = ((int)(resources.GetObject("btnCancel.TabIndex")));
+ this.btnCancel.Text = resources.GetString("btnCancel.Text");
+ this.btnCancel.TextAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("btnCancel.TextAlign")));
+ this.btnCancel.Visible = ((bool)(resources.GetObject("btnCancel.Visible")));
+ this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ //
+ // btnRemove
+ //
+ this.btnRemove.AccessibleDescription = ((string)(resources.GetObject("btnRemove.AccessibleDescription")));
+ this.btnRemove.AccessibleName = ((string)(resources.GetObject("btnRemove.AccessibleName")));
+ this.btnRemove.Anchor = ((System.Windows.Forms.AnchorStyles)(resources.GetObject("btnRemove.Anchor")));
+ this.btnRemove.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("btnRemove.BackgroundImage")));
+ this.btnRemove.Dock = ((System.Windows.Forms.DockStyle)(resources.GetObject("btnRemove.Dock")));
+ this.btnRemove.Enabled = ((bool)(resources.GetObject("btnRemove.Enabled")));
+ this.btnRemove.FlatStyle = ((System.Windows.Forms.FlatStyle)(resources.GetObject("btnRemove.FlatStyle")));
+ this.btnRemove.Font = ((System.Drawing.Font)(resources.GetObject("btnRemove.Font")));
+ this.btnRemove.Image = ((System.Drawing.Image)(resources.GetObject("btnRemove.Image")));
+ this.btnRemove.ImageAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("btnRemove.ImageAlign")));
+ this.btnRemove.ImageIndex = ((int)(resources.GetObject("btnRemove.ImageIndex")));
+ this.btnRemove.ImeMode = ((System.Windows.Forms.ImeMode)(resources.GetObject("btnRemove.ImeMode")));
+ this.btnRemove.Location = ((System.Drawing.Point)(resources.GetObject("btnRemove.Location")));
+ this.btnRemove.Name = "btnRemove";
+ this.btnRemove.RightToLeft = ((System.Windows.Forms.RightToLeft)(resources.GetObject("btnRemove.RightToLeft")));
+ this.btnRemove.Size = ((System.Drawing.Size)(resources.GetObject("btnRemove.Size")));
+ this.btnRemove.TabIndex = ((int)(resources.GetObject("btnRemove.TabIndex")));
+ this.btnRemove.Text = resources.GetString("btnRemove.Text");
+ this.btnRemove.TextAlign = ((System.Drawing.ContentAlignment)(resources.GetObject("btnRemove.TextAlign")));
+ this.btnRemove.Visible = ((bool)(resources.GetObject("btnRemove.Visible")));
+ this.btnRemove.Click += new System.EventHandler(this.btnRemove_Click);
+ //
+ // ComputerRemoveDialog
+ //
+ this.AcceptButton = this.btnRemove;
+ this.AccessibleDescription = ((string)(resources.GetObject("$this.AccessibleDescription")));
+ this.AccessibleName = ((string)(resources.GetObject("$this.AccessibleName")));
+ this.Anchor = ((System.Windows.Forms.AnchorStyles)(resources.GetObject("$this.Anchor")));
+ this.AutoScaleBaseSize = ((System.Drawing.Size)(resources.GetObject("$this.AutoScaleBaseSize")));
+ this.AutoScroll = ((bool)(resources.GetObject("$this.AutoScroll")));
+ this.AutoScrollMargin = ((System.Drawing.Size)(resources.GetObject("$this.AutoScrollMargin")));
+ this.AutoScrollMinSize = ((System.Drawing.Size)(resources.GetObject("$this.AutoScrollMinSize")));
+ this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = ((System.Drawing.Size)(resources.GetObject("$this.ClientSize")));
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.btnRemove,
+ this.btnCancel,
+ this.comboComputer,
+ this.label1});
+ this.Dock = ((System.Windows.Forms.DockStyle)(resources.GetObject("$this.Dock")));
+ this.Enabled = ((bool)(resources.GetObject("$this.Enabled")));
+ this.Font = ((System.Drawing.Font)(resources.GetObject("$this.Font")));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.ImeMode = ((System.Windows.Forms.ImeMode)(resources.GetObject("$this.ImeMode")));
+ this.Location = ((System.Drawing.Point)(resources.GetObject("$this.Location")));
+ this.MaximizeBox = false;
+ this.MaximumSize = ((System.Drawing.Size)(resources.GetObject("$this.MaximumSize")));
+ this.MinimizeBox = false;
+ this.MinimumSize = ((System.Drawing.Size)(resources.GetObject("$this.MinimumSize")));
+ this.Name = "ComputerRemoveDialog";
+ this.RightToLeft = ((System.Windows.Forms.RightToLeft)(resources.GetObject("$this.RightToLeft")));
+ this.StartPosition = ((System.Windows.Forms.FormStartPosition)(resources.GetObject("$this.StartPosition")));
+ this.Text = resources.GetString("$this.Text");
+ this.Visible = ((bool)(resources.GetObject("$this.Visible")));
+ this.Load += new System.EventHandler(this.ComputerRemoveDialog_Load);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ private void btnRemove_Click(object sender, System.EventArgs e)
+ {
+ mgmt.RemoveComputer(comboComputer.SelectedItem.ToString());
+ this.Dispose();
+ }
+
+ private void ComputerRemoveDialog_Load(object sender, System.EventArgs e)
+ {
+ ArrayList list = mgmt.getComputerCollection();
+ foreach (Computer computer in list)
+ {
+ comboComputer.Items.Add(computer.getName());
+ }
+ }
+
+ private void btnCancel_Click(object sender, System.EventArgs e)
+ {
+ this.Close();
+ this.Dispose();
+ }
+
+ private void comboComputer_SelectedIndexChanged(object sender, System.EventArgs e)
+ {
+ }
+
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/DATABASE.ICO b/storage/ndb/src/cw/cpcc-win32/csharp/DATABASE.ICO
new file mode 100644
index 00000000000..9689aa88361
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/DATABASE.ICO
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/Database.cs b/storage/ndb/src/cw/cpcc-win32/csharp/Database.cs
new file mode 100644
index 00000000000..39b8c160159
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/Database.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Data;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for Database.
+ /// </summary>
+ public class Database
+ {
+ public enum Status {Disconnected=1,Connected=2, Unknown=3}
+ private string m_name;
+ private string m_owner;
+ private int m_mgmtPort;
+ private Status m_status;
+ private ArrayList m_processes;
+ public Database(string name)
+ {
+ m_name=name;
+ m_processes = new ArrayList();
+ }
+ public Database(string name, string owner)
+ {
+ m_name=name;
+ m_owner=owner;
+ m_processes = new ArrayList();
+ }
+ public Database()
+ {
+ m_processes = new ArrayList();
+ }
+
+ public string getName()
+ {
+ return m_name;
+ }
+
+ public void setName(string name)
+ {
+ m_name=name;
+ }
+
+ public void setMgmtPort(int port)
+ {
+ m_mgmtPort=port;
+ }
+
+ public string getOwner()
+ {
+ return m_owner;
+ }
+
+ public void setOwner(string name)
+ {
+ m_owner=name;
+ }
+
+
+ public Status getStatus()
+ {
+ return m_status;
+ }
+
+ public string getStatusString()
+ {
+ if(m_status.Equals(Status.Connected))
+ return "Connected";
+ if(m_status.Equals(Status.Disconnected))
+ return "Disconnected";
+ if(m_status.Equals(Status.Unknown))
+ return "Unknown";
+ return "Unknown";
+ }
+ public void setStatus(Status status)
+ {
+ m_status=status;
+ }
+
+ public void addProcess(Process process)
+ {
+ /*if(check)
+ {
+ if(m_processes==null)
+ return;
+ if(m_processes.Count>0)
+ {
+ foreach (Process p in m_processes)
+ {
+ if(process.getId().Equals(p.getId()))
+ return;
+ }
+ }
+ }
+ */
+ m_processes.Add(process);
+ }
+ public void addProcessCheck(Process process)
+ {
+
+ if(m_processes==null)
+ return;
+ if(m_processes.Count>0)
+ {
+ foreach (Process p in m_processes)
+ {
+ if(process.getId().Equals(p.getId()))
+ return;
+ }
+ }
+ m_processes.Add(process);
+ }
+
+ public Process getProcess(string id)
+ {
+ foreach(Process process in m_processes)
+ {
+ if(process.getId().Equals(id))
+ return process;
+ }
+ return null;
+ }
+
+ public Process getProcessByName(string name)
+ {
+ foreach(Process process in m_processes)
+ {
+ if(process.getName().Equals(name))
+ return process;
+ }
+ return null;
+ }
+
+ public void removeProcess( string processName)
+ {
+ Process p = this.getProcessByName(processName);
+ m_processes.Remove(p);
+ }
+
+ public void removeAllProcesses()
+ {
+ Computer c;
+ foreach(Process p in m_processes)
+ {
+ c=p.getComputer();
+ if(c.removeProcess(p.getName(),m_name).Equals(false))
+ {
+
+ }
+ }
+ m_processes.Clear();
+ }
+
+ public ArrayList getProcesses()
+ {
+ return m_processes;
+ }
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj
new file mode 100644
index 00000000000..6384eff8329
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj
@@ -0,0 +1,240 @@
+<VisualStudioProject>
+ <CSHARP
+ ProjectType = "Local"
+ ProductVersion = "7.0.9466"
+ SchemaVersion = "1.0"
+ ProjectGuid = "{B78F6720-D36C-43DD-B442-F583718D0286}"
+ >
+ <Build>
+ <Settings
+ ApplicationIcon = "App.ico"
+ AssemblyKeyContainerName = ""
+ AssemblyName = "NDB_CPC"
+ AssemblyOriginatorKeyFile = ""
+ DefaultClientScript = "JScript"
+ DefaultHTMLPageLayout = "Grid"
+ DefaultTargetSchema = "IE50"
+ DelaySign = "false"
+ OutputType = "WinExe"
+ RootNamespace = "NDB_CPC"
+ StartupObject = ""
+ >
+ <Config
+ Name = "Debug"
+ AllowUnsafeBlocks = "false"
+ BaseAddress = "285212672"
+ CheckForOverflowUnderflow = "false"
+ ConfigurationOverrideFile = ""
+ DefineConstants = "DEBUG;TRACE"
+ DocumentationFile = ""
+ DebugSymbols = "true"
+ FileAlignment = "4096"
+ IncrementalBuild = "true"
+ Optimize = "false"
+ OutputPath = "bin\Debug\"
+ RegisterForComInterop = "false"
+ RemoveIntegerChecks = "false"
+ TreatWarningsAsErrors = "false"
+ WarningLevel = "4"
+ />
+ <Config
+ Name = "Release"
+ AllowUnsafeBlocks = "false"
+ BaseAddress = "285212672"
+ CheckForOverflowUnderflow = "false"
+ ConfigurationOverrideFile = ""
+ DefineConstants = "TRACE"
+ DocumentationFile = ""
+ DebugSymbols = "false"
+ FileAlignment = "4096"
+ IncrementalBuild = "false"
+ Optimize = "true"
+ OutputPath = "bin\Release\"
+ RegisterForComInterop = "false"
+ RemoveIntegerChecks = "false"
+ TreatWarningsAsErrors = "false"
+ WarningLevel = "4"
+ />
+ </Settings>
+ <References>
+ <Reference
+ Name = "System.Data"
+ AssemblyName = "System.Data"
+ HintPath = "..\..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.Data.dll"
+ />
+ <Reference
+ Name = "System.Drawing"
+ AssemblyName = "System.Drawing"
+ HintPath = "..\..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.Drawing.dll"
+ />
+ <Reference
+ Name = "System.Windows.Forms"
+ AssemblyName = "System.Windows.Forms"
+ HintPath = "..\..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.Windows.Forms.dll"
+ />
+ <Reference
+ Name = "System.XML"
+ AssemblyName = "System.Xml"
+ HintPath = "..\..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.XML.dll"
+ />
+ <Reference
+ Name = "System"
+ AssemblyName = "System"
+ HintPath = "..\..\..\..\..\WINNT\Microsoft.NET\Framework\v1.0.3705\System.dll"
+ />
+ <Reference
+ Name = "VBIDE"
+ Guid = "{0002E157-0000-0000-C000-000000000046}"
+ VersionMajor = "5"
+ VersionMinor = "3"
+ Lcid = "0"
+ WrapperTool = "tlbimp"
+ />
+ <Reference
+ Name = "stdole"
+ Guid = "{00020430-0000-0000-C000-000000000046}"
+ VersionMajor = "2"
+ VersionMinor = "0"
+ Lcid = "0"
+ WrapperTool = "primary"
+ />
+ <Reference
+ Name = "Microsoft.Office.Core"
+ Guid = "{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}"
+ VersionMajor = "2"
+ VersionMinor = "2"
+ Lcid = "0"
+ WrapperTool = "tlbimp"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File
+ RelPath = "app.config"
+ BuildAction = "None"
+ />
+ <File
+ RelPath = "App.ico"
+ BuildAction = "Content"
+ />
+ <File
+ RelPath = "AssemblyInfo.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "Computer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "ComputerAddDialog.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "ComputerAddDialog.resx"
+ DependentUpon = "ComputerAddDialog.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "ComputerMgmt.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "ComputerRemoveDialog.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "ComputerRemoveDialog.af.resx"
+ DependentUpon = "ComputerRemoveDialog.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "ComputerRemoveDialog.resx"
+ DependentUpon = "ComputerRemoveDialog.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "CPC_Form.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "CPC_Form.resx"
+ DependentUpon = "CPC_Form.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "Database.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "PanelWizard.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "PanelWizard.resx"
+ DependentUpon = "PanelWizard.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "Process.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "ProcessDefineDialog.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "ProcessDefineDialog.resx"
+ DependentUpon = "ProcessDefineDialog.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "startDatabaseDlg.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "startDatabaseDlg.resx"
+ DependentUpon = "startDatabaseDlg.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ <File
+ RelPath = "fileaccess\FileMgmt.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "simpleparser\SimpleCPCParser.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "socketcomm\myTcpClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "socketcomm\SocketComm.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "telnetclient\telnetClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ </Include>
+ </Files>
+ </CSHARP>
+</VisualStudioProject>
+
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj.user b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj.user
new file mode 100644
index 00000000000..68937906d93
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj.user
@@ -0,0 +1,48 @@
+<VisualStudioProject>
+ <CSHARP>
+ <Build>
+ <Settings ReferencePath = "" >
+ <Config
+ Name = "Debug"
+ EnableASPDebugging = "false"
+ EnableASPXDebugging = "false"
+ EnableUnmanagedDebugging = "false"
+ EnableSQLServerDebugging = "false"
+ RemoteDebugEnabled = "false"
+ RemoteDebugMachine = ""
+ StartAction = "Project"
+ StartArguments = ""
+ StartPage = ""
+ StartProgram = ""
+ StartURL = ""
+ StartWorkingDirectory = ""
+ StartWithIE = "false"
+ />
+ <Config
+ Name = "Release"
+ EnableASPDebugging = "false"
+ EnableASPXDebugging = "false"
+ EnableUnmanagedDebugging = "false"
+ EnableSQLServerDebugging = "false"
+ RemoteDebugEnabled = "false"
+ RemoteDebugMachine = ""
+ StartAction = "Project"
+ StartArguments = ""
+ StartPage = ""
+ StartProgram = ""
+ StartURL = ""
+ StartWorkingDirectory = ""
+ StartWithIE = "true"
+ />
+ </Settings>
+ </Build>
+ <OtherProjectSettings
+ CopyProjectDestinationFolder = ""
+ CopyProjectUncPath = ""
+ CopyProjectOption = "0"
+ ProjectView = "ProjectFiles"
+ ProjectTrust = "0"
+ />
+ </CSHARP>
+</VisualStudioProject>
+
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.ncb b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.ncb
new file mode 100644
index 00000000000..ed3460476b0
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.ncb
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.sln b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.sln
new file mode 100644
index 00000000000..ef18b5e94ce
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDB_CPC", "NDB_CPC.csproj", "{B78F6720-D36C-43DD-B442-F583718D0286}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {B78F6720-D36C-43DD-B442-F583718D0286}.Debug.ActiveCfg = Debug|.NET
+ {B78F6720-D36C-43DD-B442-F583718D0286}.Debug.Build.0 = Debug|.NET
+ {B78F6720-D36C-43DD-B442-F583718D0286}.Release.ActiveCfg = Release|.NET
+ {B78F6720-D36C-43DD-B442-F583718D0286}.Release.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/PanelWizard.cs b/storage/ndb/src/cw/cpcc-win32/csharp/PanelWizard.cs
new file mode 100644
index 00000000000..f492aa64c60
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/PanelWizard.cs
@@ -0,0 +1,1883 @@
+//author:Arun
+//date:Nov 13,2002
+//Wizard using panel
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for MDXQueryBuilderWizard.
+ /// </summary>
+ public class PanelWizard : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Button btnback;
+ private System.Windows.Forms.Button btnNext;
+ private System.Windows.Forms.Button btnFinish;
+
+ //---enabling and disabling the buttons
+ private bool cancelEnabled;
+ private bool backEnabled;
+ private bool nextEnabled;
+ private bool finishEnabled;
+ //--------
+ //--set the next and back panel
+ private Panel nextPanel;
+ private Panel backPanel;
+ private Panel presentPanel;
+ //
+ private Panel[] arrayPanel;
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.Panel panel2;
+ private System.Windows.Forms.Panel panel3;
+ private System.Windows.Forms.RadioButton radioBtnYes;
+ private System.Windows.Forms.RadioButton radioBtnNo;
+ private System.Windows.Forms.ListBox listBoxComputers;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.Label label1;
+ private System.ComponentModel.IContainer components;
+ private System.Windows.Forms.Button buttonComputerAdd;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label6;
+ private System.Windows.Forms.Label label7;
+ private System.Windows.Forms.ComboBox comboNDB;
+ private System.Windows.Forms.ComboBox comboAPI;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.ComboBox comboMGM;
+ private System.Windows.Forms.Button btnTransferNodeToComp;
+ private System.Windows.Forms.TreeView tvComputer;
+ private System.Windows.Forms.ListView lvNode;
+ private System.Windows.Forms.Button btnTransferCompToNode;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label9;
+ private System.Windows.Forms.Label label10;
+ private int m_nMGM;
+ private ComputerMgmt mgmt;
+ private int m_nNDB;
+ private int m_nAPI;
+ private Database m_db;
+ private System.Windows.Forms.Label label11;
+ private System.Windows.Forms.TextBox textDbName;
+ private System.Windows.Forms.Label label31;
+ private System.Windows.Forms.Label label32;
+ private System.Windows.Forms.Label label33;
+ private System.Windows.Forms.Label label18;
+ private System.Windows.Forms.Label labelTitle;
+ private System.Windows.Forms.Label labelCwd;
+ private System.Windows.Forms.Label labelArgs;
+ private System.Windows.Forms.Label labelOther;
+ private System.Windows.Forms.Label labelPath;
+ private int m_noOfConfiguredNodes;
+ private int m_noOfConfiguredMgmt;
+ private int m_noOfConfiguredNdb;
+ private string m_mgmHost;
+ private string m_mgmPort;
+ private System.Windows.Forms.TextBox textCwd;
+ private System.Windows.Forms.TextBox textArgs;
+ private System.Windows.Forms.TextBox textOther;
+ private System.Windows.Forms.TextBox textPath;
+ private System.Windows.Forms.TextBox textComputer;
+ private System.Windows.Forms.TextBox textDatabase;
+ private System.Windows.Forms.TextBox textName;
+ private int m_noOfConfiguredApi;
+ private bool m_bMgmt;
+ private System.Windows.Forms.Button buttonSave;
+ private System.Windows.Forms.CheckBox checkBoxReuse;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.Panel panel4;
+ private System.Windows.Forms.CheckBox checkBoxLater;
+ private System.Windows.Forms.RadioButton radioYes;
+ private System.Windows.Forms.RadioButton radioNo;
+ private System.Windows.Forms.Panel panel6;
+ private System.Windows.Forms.Panel panel5;
+ private System.Windows.Forms.RadioButton radioStartNo;
+ private System.Windows.Forms.RadioButton radioStartYes;
+ private System.Windows.Forms.ImageList imageListComp;
+ private System.Windows.Forms.Label label12;
+ private System.Windows.Forms.TextBox textOwner;
+ private System.Windows.Forms.Label label13;
+ private System.Windows.Forms.TextBox textEnv;
+ private bool m_bNdb;
+ public PanelWizard(ComputerMgmt comp)
+ {
+ mgmt=comp;
+ m_noOfConfiguredNodes=0;
+ m_noOfConfiguredMgmt=0;
+ m_noOfConfiguredNdb=0;
+ m_noOfConfiguredApi=0;
+ Size panelSize= new Size(350,300);
+ Size s= new Size(355,360);
+ Point cancel= new Point(8,310);
+ Point back= new Point(96,310);
+ Point next = new Point(184,310);
+ Point finish= new Point(272,310);
+ InitializeComponent();
+ this.Size=s;
+ this.btnCancel.Location=cancel;
+
+ this.btnback.Location=back;
+ this.btnNext.Location=next;
+ this.btnFinish.Location=finish;
+
+ arrayPanel=new Panel[]{panel1,panel2,panel3,panel4,panel5,panel6};//,panel5, panel6};
+ panel1.Size=panelSize;
+
+ comboNDB.SelectedIndex=0;
+ comboAPI.SelectedIndex=0;
+ comboMGM.SelectedIndex=0;
+ m_bMgmt=false;
+ m_bNdb=false;
+
+ m_db = new Database();
+ if(listBoxComputers.Items.Count.Equals(0))
+ btnNext.Enabled=false;
+ }
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(PanelWizard));
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.buttonComputerAdd = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.listBoxComputers = new System.Windows.Forms.ListBox();
+ this.radioBtnNo = new System.Windows.Forms.RadioButton();
+ this.radioBtnYes = new System.Windows.Forms.RadioButton();
+ this.panel2 = new System.Windows.Forms.Panel();
+ this.label12 = new System.Windows.Forms.Label();
+ this.textOwner = new System.Windows.Forms.TextBox();
+ this.label11 = new System.Windows.Forms.Label();
+ this.textDbName = new System.Windows.Forms.TextBox();
+ this.label8 = new System.Windows.Forms.Label();
+ this.label7 = new System.Windows.Forms.Label();
+ this.label6 = new System.Windows.Forms.Label();
+ this.comboMGM = new System.Windows.Forms.ComboBox();
+ this.comboAPI = new System.Windows.Forms.ComboBox();
+ this.comboNDB = new System.Windows.Forms.ComboBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.panel3 = new System.Windows.Forms.Panel();
+ this.checkBoxLater = new System.Windows.Forms.CheckBox();
+ this.label10 = new System.Windows.Forms.Label();
+ this.label9 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.btnTransferCompToNode = new System.Windows.Forms.Button();
+ this.btnTransferNodeToComp = new System.Windows.Forms.Button();
+ this.lvNode = new System.Windows.Forms.ListView();
+ this.tvComputer = new System.Windows.Forms.TreeView();
+ this.imageListComp = new System.Windows.Forms.ImageList(this.components);
+ this.panel6 = new System.Windows.Forms.Panel();
+ this.radioStartNo = new System.Windows.Forms.RadioButton();
+ this.radioStartYes = new System.Windows.Forms.RadioButton();
+ this.label18 = new System.Windows.Forms.Label();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.btnback = new System.Windows.Forms.Button();
+ this.btnNext = new System.Windows.Forms.Button();
+ this.btnFinish = new System.Windows.Forms.Button();
+ this.panel4 = new System.Windows.Forms.Panel();
+ this.textEnv = new System.Windows.Forms.TextBox();
+ this.label13 = new System.Windows.Forms.Label();
+ this.checkBoxReuse = new System.Windows.Forms.CheckBox();
+ this.buttonSave = new System.Windows.Forms.Button();
+ this.labelTitle = new System.Windows.Forms.Label();
+ this.textComputer = new System.Windows.Forms.TextBox();
+ this.textCwd = new System.Windows.Forms.TextBox();
+ this.textArgs = new System.Windows.Forms.TextBox();
+ this.textOther = new System.Windows.Forms.TextBox();
+ this.textPath = new System.Windows.Forms.TextBox();
+ this.textDatabase = new System.Windows.Forms.TextBox();
+ this.textName = new System.Windows.Forms.TextBox();
+ this.labelCwd = new System.Windows.Forms.Label();
+ this.labelArgs = new System.Windows.Forms.Label();
+ this.labelOther = new System.Windows.Forms.Label();
+ this.labelPath = new System.Windows.Forms.Label();
+ this.label31 = new System.Windows.Forms.Label();
+ this.label32 = new System.Windows.Forms.Label();
+ this.label33 = new System.Windows.Forms.Label();
+ this.panel5 = new System.Windows.Forms.Panel();
+ this.radioNo = new System.Windows.Forms.RadioButton();
+ this.radioYes = new System.Windows.Forms.RadioButton();
+ this.label4 = new System.Windows.Forms.Label();
+ this.panel1.SuspendLayout();
+ this.panel2.SuspendLayout();
+ this.panel3.SuspendLayout();
+ this.panel6.SuspendLayout();
+ this.panel4.SuspendLayout();
+ this.panel5.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // panel1
+ //
+ this.panel1.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.buttonComputerAdd,
+ this.label1,
+ this.label5,
+ this.listBoxComputers,
+ this.radioBtnNo,
+ this.radioBtnYes});
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(344, 312);
+ this.panel1.TabIndex = 0;
+ this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1_Paint);
+ //
+ // buttonComputerAdd
+ //
+ this.buttonComputerAdd.Enabled = false;
+ this.buttonComputerAdd.Location = new System.Drawing.Point(192, 232);
+ this.buttonComputerAdd.Name = "buttonComputerAdd";
+ this.buttonComputerAdd.Size = new System.Drawing.Size(96, 24);
+ this.buttonComputerAdd.TabIndex = 3;
+ this.buttonComputerAdd.Text = "Add computer...";
+ this.buttonComputerAdd.Click += new System.EventHandler(this.buttonComputerAdd_Click);
+ //
+ // label1
+ //
+ this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
+ this.label1.Location = new System.Drawing.Point(80, 8);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(200, 23);
+ this.label1.TabIndex = 5;
+ this.label1.Text = "Configure computers";
+ this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label5
+ //
+ this.label5.Location = new System.Drawing.Point(24, 40);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(128, 23);
+ this.label5.TabIndex = 4;
+ this.label5.Text = "Available computers:";
+ this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // listBoxComputers
+ //
+ this.listBoxComputers.Location = new System.Drawing.Point(24, 64);
+ this.listBoxComputers.Name = "listBoxComputers";
+ this.listBoxComputers.Size = new System.Drawing.Size(128, 212);
+ this.listBoxComputers.TabIndex = 3;
+ this.listBoxComputers.SelectedIndexChanged += new System.EventHandler(this.listBoxComputers_SelectedIndexChanged);
+ //
+ // radioBtnNo
+ //
+ this.radioBtnNo.AutoCheck = false;
+ this.radioBtnNo.Location = new System.Drawing.Point(168, 168);
+ this.radioBtnNo.Name = "radioBtnNo";
+ this.radioBtnNo.Size = new System.Drawing.Size(152, 64);
+ this.radioBtnNo.TabIndex = 2;
+ this.radioBtnNo.Text = "No, I have to add more computers in order to deploy NDB Cluster. ";
+ this.radioBtnNo.Click += new System.EventHandler(this.radioBtnNo_Click);
+ //
+ // radioBtnYes
+ //
+ this.radioBtnYes.AutoCheck = false;
+ this.radioBtnYes.Location = new System.Drawing.Point(168, 72);
+ this.radioBtnYes.Name = "radioBtnYes";
+ this.radioBtnYes.Size = new System.Drawing.Size(152, 80);
+ this.radioBtnYes.TabIndex = 1;
+ this.radioBtnYes.Text = "Yes, all the computers that I need to deploy NDB Cluster exists in the list \"Avai" +
+ "lable computers\"";
+ this.radioBtnYes.Click += new System.EventHandler(this.radioBtnYes_Click);
+ //
+ // panel2
+ //
+ this.panel2.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.label12,
+ this.textOwner,
+ this.label11,
+ this.textDbName,
+ this.label8,
+ this.label7,
+ this.label6,
+ this.comboMGM,
+ this.comboAPI,
+ this.comboNDB,
+ this.label2});
+ this.panel2.Location = new System.Drawing.Point(0, 320);
+ this.panel2.Name = "panel2";
+ this.panel2.Size = new System.Drawing.Size(344, 312);
+ this.panel2.TabIndex = 1;
+ this.panel2.Validating += new System.ComponentModel.CancelEventHandler(this.panel2_Validating);
+ this.panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.panel2_Paint);
+ //
+ // label12
+ //
+ this.label12.Location = new System.Drawing.Point(72, 216);
+ this.label12.Name = "label12";
+ this.label12.Size = new System.Drawing.Size(112, 24);
+ this.label12.TabIndex = 16;
+ this.label12.Text = "Database owner:";
+ this.label12.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // textOwner
+ //
+ this.textOwner.Location = new System.Drawing.Point(192, 216);
+ this.textOwner.Name = "textOwner";
+ this.textOwner.TabIndex = 5;
+ this.textOwner.Text = "";
+ this.textOwner.TextChanged += new System.EventHandler(this.textOwner_TextChanged);
+ //
+ // label11
+ //
+ this.label11.Location = new System.Drawing.Point(72, 184);
+ this.label11.Name = "label11";
+ this.label11.Size = new System.Drawing.Size(112, 24);
+ this.label11.TabIndex = 14;
+ this.label11.Text = "Database name:";
+ this.label11.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.label11.Click += new System.EventHandler(this.label11_Click);
+ //
+ // textDbName
+ //
+ this.textDbName.Location = new System.Drawing.Point(192, 184);
+ this.textDbName.Name = "textDbName";
+ this.textDbName.TabIndex = 4;
+ this.textDbName.Text = "";
+
+ this.textDbName.TextChanged += new System.EventHandler(this.textDbName_TextChanged);
+ //
+ // label8
+ //
+ this.label8.Location = new System.Drawing.Point(16, 120);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(176, 24);
+ this.label8.TabIndex = 12;
+ this.label8.Text = "Number of management servers:";
+ this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label7
+ //
+ this.label7.Location = new System.Drawing.Point(16, 88);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(120, 24);
+ this.label7.TabIndex = 11;
+ this.label7.Text = "Number of API nodes:";
+ this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label6
+ //
+ this.label6.Location = new System.Drawing.Point(16, 56);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(144, 24);
+ this.label6.TabIndex = 10;
+ this.label6.Text = "Number of database nodes:";
+ this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // comboMGM
+ //
+ this.comboMGM.DisplayMember = "0";
+ this.comboMGM.Items.AddRange(new object[] {
+ "1"});
+ this.comboMGM.Location = new System.Drawing.Point(192, 120);
+ this.comboMGM.Name = "comboMGM";
+ this.comboMGM.Size = new System.Drawing.Size(104, 21);
+ this.comboMGM.TabIndex = 3;
+ this.comboMGM.Text = "comboBox3";
+ //
+ // comboAPI
+ //
+ this.comboAPI.DisplayMember = "0";
+ this.comboAPI.Items.AddRange(new object[] {
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10"});
+ this.comboAPI.Location = new System.Drawing.Point(192, 88);
+ this.comboAPI.Name = "comboAPI";
+ this.comboAPI.Size = new System.Drawing.Size(104, 21);
+ this.comboAPI.TabIndex = 2;
+ this.comboAPI.Text = "comboBox2";
+ //
+ // comboNDB
+ //
+ this.comboNDB.DisplayMember = "0";
+ this.comboNDB.Items.AddRange(new object[] {
+ "1",
+ "2",
+ "4",
+ "8"});
+ this.comboNDB.Location = new System.Drawing.Point(192, 56);
+ this.comboNDB.Name = "comboNDB";
+ this.comboNDB.Size = new System.Drawing.Size(104, 21);
+ this.comboNDB.TabIndex = 1;
+ this.comboNDB.Text = "comboBox1";
+ //
+ // label2
+ //
+ this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
+ this.label2.Location = new System.Drawing.Point(80, 8);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(208, 23);
+ this.label2.TabIndex = 6;
+ this.label2.Text = "Setup NDB Cluster nodes";
+ this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // panel3
+ //
+ this.panel3.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.checkBoxLater,
+ this.label10,
+ this.label9,
+ this.label3,
+ this.btnTransferCompToNode,
+ this.btnTransferNodeToComp,
+ this.lvNode,
+ this.tvComputer});
+ this.panel3.Location = new System.Drawing.Point(360, 8);
+ this.panel3.Name = "panel3";
+ this.panel3.Size = new System.Drawing.Size(320, 312);
+ this.panel3.TabIndex = 2;
+ //
+ // checkBoxLater
+ //
+ this.checkBoxLater.Location = new System.Drawing.Point(40, 256);
+ this.checkBoxLater.Name = "checkBoxLater";
+ this.checkBoxLater.Size = new System.Drawing.Size(240, 16);
+ this.checkBoxLater.TabIndex = 9;
+ this.checkBoxLater.Text = "I will configure these nodes manually, later.";
+ //
+ // label10
+ //
+ this.label10.Location = new System.Drawing.Point(16, 40);
+ this.label10.Name = "label10";
+ this.label10.Size = new System.Drawing.Size(104, 16);
+ this.label10.TabIndex = 8;
+ this.label10.Text = "NDB Cluster nodes:";
+ this.label10.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label9
+ //
+ this.label9.Location = new System.Drawing.Point(192, 40);
+ this.label9.Name = "label9";
+ this.label9.Size = new System.Drawing.Size(100, 16);
+ this.label9.TabIndex = 7;
+ this.label9.Text = "Computers:";
+ this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label3
+ //
+ this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
+ this.label3.Location = new System.Drawing.Point(40, 8);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(280, 23);
+ this.label3.TabIndex = 6;
+ this.label3.Text = "Assign NDB nodes to computers";
+ this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // btnTransferCompToNode
+ //
+ this.btnTransferCompToNode.Location = new System.Drawing.Point(144, 160);
+ this.btnTransferCompToNode.Name = "btnTransferCompToNode";
+ this.btnTransferCompToNode.Size = new System.Drawing.Size(40, 24);
+ this.btnTransferCompToNode.TabIndex = 4;
+ this.btnTransferCompToNode.Text = "<---";
+ //
+ // btnTransferNodeToComp
+ //
+ this.btnTransferNodeToComp.Location = new System.Drawing.Point(144, 128);
+ this.btnTransferNodeToComp.Name = "btnTransferNodeToComp";
+ this.btnTransferNodeToComp.Size = new System.Drawing.Size(40, 24);
+ this.btnTransferNodeToComp.TabIndex = 3;
+ this.btnTransferNodeToComp.Text = "--->";
+ this.btnTransferNodeToComp.Click += new System.EventHandler(this.btnTransferNodeToComp_Click);
+ //
+ // lvNode
+ //
+ this.lvNode.HideSelection = false;
+ this.lvNode.Location = new System.Drawing.Point(16, 56);
+ this.lvNode.Name = "lvNode";
+ this.lvNode.Size = new System.Drawing.Size(112, 192);
+ this.lvNode.TabIndex = 2;
+ this.lvNode.View = System.Windows.Forms.View.List;
+ this.lvNode.SelectedIndexChanged += new System.EventHandler(this.lvNode_SelectedIndexChanged);
+ //
+ // tvComputer
+ //
+ this.tvComputer.HideSelection = false;
+ this.tvComputer.ImageList = this.imageListComp;
+ this.tvComputer.Location = new System.Drawing.Point(192, 56);
+ this.tvComputer.Name = "tvComputer";
+ this.tvComputer.Size = new System.Drawing.Size(120, 192);
+ this.tvComputer.TabIndex = 1;
+ this.tvComputer.MouseDown += new System.Windows.Forms.MouseEventHandler(this.tvComputer_MouseDown);
+ this.tvComputer.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvComputer_AfterSelect);
+ this.tvComputer.MouseLeave += new System.EventHandler(this.tvComputer_MouseLeave);
+ this.tvComputer.DragDrop += new System.Windows.Forms.DragEventHandler(this.tvComputer_DragDrop);
+ //
+ // imageListComp
+ //
+ this.imageListComp.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+ this.imageListComp.ImageSize = new System.Drawing.Size(16, 16);
+ this.imageListComp.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageListComp.ImageStream")));
+ this.imageListComp.TransparentColor = System.Drawing.Color.Transparent;
+ //
+ // panel6
+ //
+ this.panel6.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.radioStartNo,
+ this.radioStartYes,
+ this.label18});
+ this.panel6.Location = new System.Drawing.Point(344, 336);
+ this.panel6.Name = "panel6";
+ this.panel6.Size = new System.Drawing.Size(344, 312);
+ this.panel6.TabIndex = 3;
+ this.panel6.Paint += new System.Windows.Forms.PaintEventHandler(this.panel4_Paint);
+ //
+ // radioStartNo
+ //
+ this.radioStartNo.Location = new System.Drawing.Point(40, 144);
+ this.radioStartNo.Name = "radioStartNo";
+ this.radioStartNo.Size = new System.Drawing.Size(272, 48);
+ this.radioStartNo.TabIndex = 81;
+ this.radioStartNo.Text = "Manually start NDB Cluster. The Magician will exit and you must start NDB Cluster" +
+ " manually.";
+ this.radioStartNo.CheckedChanged += new System.EventHandler(this.radioStartNo_CheckedChanged);
+ //
+ // radioStartYes
+ //
+ this.radioStartYes.Location = new System.Drawing.Point(40, 40);
+ this.radioStartYes.Name = "radioStartYes";
+ this.radioStartYes.Size = new System.Drawing.Size(272, 88);
+ this.radioStartYes.TabIndex = 80;
+ this.radioStartYes.Text = "Start NDB Cluster now. The Magician will start NDB Cluster and exit. MAKE SURE YO" +
+ "U HAVE STARTED THE MGMTSRVR WITH THE CORRECT CONFIGURATION FILE!!!";
+ this.radioStartYes.CheckedChanged += new System.EventHandler(this.radioStartYes_CheckedChanged);
+ //
+ // label18
+ //
+ this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
+ this.label18.Location = new System.Drawing.Point(56, 8);
+ this.label18.Name = "label18";
+ this.label18.Size = new System.Drawing.Size(224, 24);
+ this.label18.TabIndex = 79;
+ this.label18.Text = "Start NDB Cluster and finish";
+ //
+ // btnCancel
+ //
+ this.btnCancel.Location = new System.Drawing.Point(8, 656);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(70, 23);
+ this.btnCancel.TabIndex = 10;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ //
+ // btnback
+ //
+ this.btnback.Location = new System.Drawing.Point(96, 656);
+ this.btnback.Name = "btnback";
+ this.btnback.Size = new System.Drawing.Size(70, 23);
+ this.btnback.TabIndex = 11;
+ this.btnback.Text = "< Back";
+ this.btnback.Click += new System.EventHandler(this.btnback_Click);
+ //
+ // btnNext
+ //
+ this.btnNext.Location = new System.Drawing.Point(184, 656);
+ this.btnNext.Name = "btnNext";
+ this.btnNext.Size = new System.Drawing.Size(70, 23);
+ this.btnNext.TabIndex = 12;
+ this.btnNext.Text = "Next >";
+ this.btnNext.Click += new System.EventHandler(this.btnNext_Click);
+ //
+ // btnFinish
+ //
+ this.btnFinish.Location = new System.Drawing.Point(272, 656);
+ this.btnFinish.Name = "btnFinish";
+ this.btnFinish.Size = new System.Drawing.Size(70, 23);
+ this.btnFinish.TabIndex = 13;
+ this.btnFinish.Text = "Finish";
+ this.btnFinish.Click += new System.EventHandler(this.btnFinish_Click);
+ //
+ // panel4
+ //
+ this.panel4.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.textEnv,
+ this.label13,
+ this.checkBoxReuse,
+ this.buttonSave,
+ this.labelTitle,
+ this.textComputer,
+ this.textCwd,
+ this.textArgs,
+ this.textOther,
+ this.textPath,
+ this.textDatabase,
+ this.textName,
+ this.labelCwd,
+ this.labelArgs,
+ this.labelOther,
+ this.labelPath,
+ this.label31,
+ this.label32,
+ this.label33});
+ this.panel4.Location = new System.Drawing.Point(672, 8);
+ this.panel4.Name = "panel4";
+ this.panel4.Size = new System.Drawing.Size(344, 312);
+ this.panel4.TabIndex = 62;
+ this.panel4.Paint += new System.Windows.Forms.PaintEventHandler(this.panel5_Paint);
+ //
+ // textEnv
+ //
+ this.textEnv.Location = new System.Drawing.Point(136, 136);
+ this.textEnv.Name = "textEnv";
+ this.textEnv.Size = new System.Drawing.Size(184, 20);
+ this.textEnv.TabIndex = 2;
+ this.textEnv.TabStop = false;
+ this.textEnv.Text = "";
+ //
+ // label13
+ //
+ this.label13.Location = new System.Drawing.Point(8, 136);
+ this.label13.Name = "label13";
+ this.label13.Size = new System.Drawing.Size(136, 24);
+ this.label13.TabIndex = 81;
+ this.label13.Text = "Environment variables:";
+ //
+ // checkBoxReuse
+ //
+ this.checkBoxReuse.Location = new System.Drawing.Point(88, 232);
+ this.checkBoxReuse.Name = "checkBoxReuse";
+ this.checkBoxReuse.Size = new System.Drawing.Size(240, 32);
+ this.checkBoxReuse.TabIndex = 5;
+ this.checkBoxReuse.TabStop = false;
+ this.checkBoxReuse.Text = "Use the same configuration for ALL NDB nodes?";
+ //
+ // buttonSave
+ //
+ this.buttonSave.Location = new System.Drawing.Point(184, 264);
+ this.buttonSave.Name = "buttonSave";
+ this.buttonSave.Size = new System.Drawing.Size(88, 24);
+ this.buttonSave.TabIndex = 6;
+ this.buttonSave.Text = "Save";
+ this.buttonSave.Click += new System.EventHandler(this.buttonSave_Click);
+ //
+ // labelTitle
+ //
+ this.labelTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
+ this.labelTitle.Location = new System.Drawing.Point(80, 16);
+ this.labelTitle.Name = "labelTitle";
+ this.labelTitle.Size = new System.Drawing.Size(192, 23);
+ this.labelTitle.TabIndex = 79;
+ this.labelTitle.Text = "Mgmtsrvr configuration";
+ //
+ // textComputer
+ //
+ this.textComputer.Location = new System.Drawing.Point(136, 40);
+ this.textComputer.Name = "textComputer";
+ this.textComputer.ReadOnly = true;
+ this.textComputer.Size = new System.Drawing.Size(184, 20);
+ this.textComputer.TabIndex = 77;
+ this.textComputer.TabStop = false;
+ this.textComputer.Text = "";
+ //
+ // textCwd
+ //
+ this.textCwd.Location = new System.Drawing.Point(136, 208);
+ this.textCwd.Name = "textCwd";
+ this.textCwd.Size = new System.Drawing.Size(184, 20);
+ this.textCwd.TabIndex = 5;
+ this.textCwd.TabStop = false;
+ this.textCwd.Text = "";
+ //
+ // textArgs
+ //
+ this.textArgs.Location = new System.Drawing.Point(136, 184);
+ this.textArgs.Name = "textArgs";
+ this.textArgs.Size = new System.Drawing.Size(184, 20);
+ this.textArgs.TabIndex = 4;
+ this.textArgs.TabStop = false;
+ this.textArgs.Text = "";
+ //
+ // textOther
+ //
+ this.textOther.Location = new System.Drawing.Point(136, 160);
+ this.textOther.Name = "textOther";
+ this.textOther.Size = new System.Drawing.Size(184, 20);
+ this.textOther.TabIndex = 3;
+ this.textOther.TabStop = false;
+ this.textOther.Text = "";
+ //
+ // textPath
+ //
+ this.textPath.Location = new System.Drawing.Point(136, 112);
+ this.textPath.Name = "textPath";
+ this.textPath.Size = new System.Drawing.Size(184, 20);
+ this.textPath.TabIndex = 1;
+ this.textPath.TabStop = false;
+ this.textPath.Text = "";
+ this.textPath.TextChanged += new System.EventHandler(this.textPath_TextChanged);
+ //
+ // textDatabase
+ //
+ this.textDatabase.Location = new System.Drawing.Point(136, 88);
+ this.textDatabase.Name = "textDatabase";
+ this.textDatabase.ReadOnly = true;
+ this.textDatabase.Size = new System.Drawing.Size(184, 20);
+ this.textDatabase.TabIndex = 62;
+ this.textDatabase.TabStop = false;
+ this.textDatabase.Text = "";
+ //
+ // textName
+ //
+ this.textName.Location = new System.Drawing.Point(136, 64);
+ this.textName.Name = "textName";
+ this.textName.ReadOnly = true;
+ this.textName.Size = new System.Drawing.Size(184, 20);
+ this.textName.TabIndex = 60;
+ this.textName.TabStop = false;
+ this.textName.Text = "";
+ //
+ // labelCwd
+ //
+ this.labelCwd.Location = new System.Drawing.Point(8, 208);
+ this.labelCwd.Name = "labelCwd";
+ this.labelCwd.Size = new System.Drawing.Size(112, 24);
+ this.labelCwd.TabIndex = 72;
+ this.labelCwd.Text = "Current working dir.:";
+ //
+ // labelArgs
+ //
+ this.labelArgs.Location = new System.Drawing.Point(8, 184);
+ this.labelArgs.Name = "labelArgs";
+ this.labelArgs.Size = new System.Drawing.Size(128, 24);
+ this.labelArgs.TabIndex = 70;
+ this.labelArgs.Text = "Arguments to mgmtsrvr:";
+ //
+ // labelOther
+ //
+ this.labelOther.Location = new System.Drawing.Point(8, 160);
+ this.labelOther.Name = "labelOther";
+ this.labelOther.Size = new System.Drawing.Size(136, 24);
+ this.labelOther.TabIndex = 69;
+ this.labelOther.Text = "Mgmtsrvr port:";
+ //
+ // labelPath
+ //
+ this.labelPath.Location = new System.Drawing.Point(8, 112);
+ this.labelPath.Name = "labelPath";
+ this.labelPath.Size = new System.Drawing.Size(128, 24);
+ this.labelPath.TabIndex = 67;
+ this.labelPath.Text = "Path to mgmtsrvr binary:";
+ //
+ // label31
+ //
+ this.label31.Location = new System.Drawing.Point(8, 88);
+ this.label31.Name = "label31";
+ this.label31.Size = new System.Drawing.Size(88, 24);
+ this.label31.TabIndex = 65;
+ this.label31.Text = "Database:";
+ //
+ // label32
+ //
+ this.label32.Location = new System.Drawing.Point(8, 64);
+ this.label32.Name = "label32";
+ this.label32.Size = new System.Drawing.Size(88, 24);
+ this.label32.TabIndex = 63;
+ this.label32.Text = "Process name:";
+ //
+ // label33
+ //
+ this.label33.Location = new System.Drawing.Point(8, 40);
+ this.label33.Name = "label33";
+ this.label33.Size = new System.Drawing.Size(64, 24);
+ this.label33.TabIndex = 61;
+ this.label33.Text = "Computer:";
+ //
+ // panel5
+ //
+ this.panel5.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.radioNo,
+ this.radioYes,
+ this.label4});
+ this.panel5.Location = new System.Drawing.Point(672, 328);
+ this.panel5.Name = "panel5";
+ this.panel5.Size = new System.Drawing.Size(344, 312);
+ this.panel5.TabIndex = 63;
+ //
+ // radioNo
+ //
+ this.radioNo.Location = new System.Drawing.Point(72, 160);
+ this.radioNo.Name = "radioNo";
+ this.radioNo.Size = new System.Drawing.Size(240, 48);
+ this.radioNo.TabIndex = 1;
+ this.radioNo.Text = "I already have a configuration file that I want to use for this configuration.";
+ this.radioNo.CheckedChanged += new System.EventHandler(this.radioNo_CheckedChanged);
+ //
+ // radioYes
+ //
+ this.radioYes.Checked = true;
+ this.radioYes.Location = new System.Drawing.Point(72, 56);
+ this.radioYes.Name = "radioYes";
+ this.radioYes.Size = new System.Drawing.Size(240, 88);
+ this.radioYes.TabIndex = 0;
+ this.radioYes.TabStop = true;
+ this.radioYes.Text = "Generate a configuration file template (initconfig.txt) for the mgmtsrvr based on" +
+ " the specified configuration? Notepad will be started with a template that you m" +
+ "ust complete and save in the cwd of the mgmtsrvr.";
+ this.radioYes.CheckedChanged += new System.EventHandler(this.radioYes_CheckedChanged);
+ //
+ // label4
+ //
+ this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
+ this.label4.Location = new System.Drawing.Point(88, 8);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(192, 40);
+ this.label4.TabIndex = 79;
+ this.label4.Text = "Tying up the configuration";
+ this.label4.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+ //
+ // PanelWizard
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(1030, 755);
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.panel5,
+ this.panel4,
+ this.panel1,
+ this.btnFinish,
+ this.btnNext,
+ this.btnback,
+ this.btnCancel,
+ this.panel6,
+ this.panel3,
+ this.panel2});
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "PanelWizard";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.Text = "Create Database Magician";
+ this.Load += new System.EventHandler(this.MDXQueryBuilderWizard_Load);
+ this.Activated += new System.EventHandler(this.PanelWizard_Activated);
+ this.panel1.ResumeLayout(false);
+ this.panel2.ResumeLayout(false);
+ this.panel3.ResumeLayout(false);
+ this.panel6.ResumeLayout(false);
+ this.panel4.ResumeLayout(false);
+ this.panel5.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ private void MDXQueryBuilderWizard_Load(object sender, System.EventArgs e)
+ {
+
+ foreach(Control ct in this.Controls)
+ {
+ if(ct.GetType().Name=="Panel")
+ {
+ ct.Left=0;
+ ct.Top=0;
+ ct.Visible=false;
+ }
+
+ }
+ presentPanel=arrayPanel[0];
+ //--set the properties
+ setBtnPanProperty(getPosition(presentPanel));
+ //------
+ refreshLook();
+ }
+
+ //-set the buttons and panel
+ private void refreshLook()
+ {
+ if(cancelEnabled)
+ btnCancel.Enabled=true;
+ else
+ btnCancel.Enabled=false;
+
+ if(backEnabled)
+ btnback.Enabled=true;
+ else
+ btnback.Enabled=false;
+
+ if(nextEnabled)
+ btnNext.Enabled=true;
+ else
+ btnNext.Enabled=false;
+
+ if(finishEnabled)
+ btnFinish.Enabled=true;
+ else
+ btnFinish.Enabled=false;
+
+ if(presentPanel!=null)
+ {
+ presentPanel.Show();
+ presentPanel.BringToFront();
+ }
+ }
+ //--------
+ private int getPosition(Panel p)
+ {
+ int result=-1;
+ for(int i=0;i<arrayPanel.Length;i++)
+ {
+ if(arrayPanel[i]==p)
+ {
+ result=i;
+ break;
+ }
+ }
+ return result ;
+ }
+ //----
+
+ private void setBtnPanProperty(int presentPanelPosition )
+ {
+ int panelLength=arrayPanel.Length-1;
+ if(presentPanelPosition==0)
+ {
+ //first panel...no back ,no finish
+ cancelEnabled=true;
+ backEnabled=false;
+ nextEnabled=false;
+ finishEnabled=false;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=arrayPanel[presentPanelPosition+1];
+ backPanel=null;
+
+ }
+ else if(presentPanelPosition==1)
+ {
+ cancelEnabled=true;
+ backEnabled=true;
+ nextEnabled=false;
+ finishEnabled=false;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=arrayPanel[presentPanelPosition+1];
+ backPanel=arrayPanel[presentPanelPosition-1];
+ }
+
+ else if(presentPanelPosition==2)
+ {
+ cancelEnabled=true;
+ backEnabled=true;
+ nextEnabled=false;
+ finishEnabled=false;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=arrayPanel[presentPanelPosition+1];
+ backPanel=arrayPanel[presentPanelPosition-1];
+ }
+
+ else if(presentPanelPosition==3)
+ {
+ //last panel...no next,finish
+ cancelEnabled=true;
+ backEnabled=true;
+ nextEnabled=true;
+ finishEnabled=false;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=arrayPanel[presentPanelPosition+1];
+ backPanel=arrayPanel[presentPanelPosition-1];
+ }
+ else if(presentPanelPosition==4)
+ {
+ //last panel...no next,finish
+ cancelEnabled=true;
+ backEnabled=true;
+ nextEnabled=true;
+ finishEnabled=false;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=presentPanel;
+ backPanel=arrayPanel[presentPanelPosition-1];
+ }
+
+ else if(presentPanelPosition==5)
+ {
+ //last panel...no next,finish yes
+ cancelEnabled=true;
+ backEnabled=true;
+ nextEnabled=false;
+ finishEnabled=false;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=null;
+ backPanel=arrayPanel[presentPanelPosition-1];
+ }
+
+ else
+ {
+ //no finish,next and back
+ cancelEnabled=true;
+ backEnabled=true;
+ nextEnabled=false;
+ finishEnabled=true;
+ presentPanel=arrayPanel[presentPanelPosition];
+ nextPanel=null;
+ backPanel=arrayPanel[presentPanelPosition-1];
+ }
+ }
+
+ private void btnNext_Click(object sender, System.EventArgs e)
+ {
+
+ if(arrayPanel[getPosition(presentPanel)].Equals(panel1))
+ {
+ presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ setBtnPanProperty(getPosition(presentPanel));
+ refreshLook();
+ return;
+ }
+ if(arrayPanel[getPosition(presentPanel)].Equals(panel2))
+ {
+ m_db.setName(textDbName.Text.ToString());
+ m_db.setOwner(textOwner.Text.ToString());
+ presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ //presentPanel
+ setBtnPanProperty(getPosition(presentPanel));
+ prepareNodeAssignmentPanel();
+ refreshLook();
+
+ return;
+ }
+ if(arrayPanel[getPosition(presentPanel)].Equals(panel3))
+ {
+ prepareNodeConfigurationPanel();
+ presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ setBtnPanProperty(getPosition(presentPanel));
+ refreshLook();
+ return;
+ }
+ if(arrayPanel[getPosition(presentPanel)].Equals(panel4))
+ {
+ nextEnabled=true;
+ finishEnabled=true;
+ backEnabled=true;
+ cancelEnabled=true;
+ presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ setBtnPanProperty(getPosition(presentPanel));
+ refreshLook();
+ return;
+ }
+ if(arrayPanel[getPosition(presentPanel)].Equals(panel5))
+ {
+ generateInitConfig();
+ presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ setBtnPanProperty(getPosition(presentPanel));
+ refreshLook();
+ return;
+ }
+
+ if(arrayPanel[getPosition(presentPanel)].Equals(panel6))
+ {
+ // presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ setBtnPanProperty(getPosition(presentPanel));
+ refreshLook();
+ return;
+ }
+ /*else
+ {
+ presentPanel=arrayPanel[getPosition(presentPanel)+1];
+ setBtnPanProperty(getPosition(presentPanel));
+ updateListViews();
+ refreshLook();
+ }*/
+ }
+
+
+ private void btnback_Click(object sender, System.EventArgs e)
+ {
+ presentPanel=arrayPanel[getPosition(presentPanel)-1];
+ setBtnPanProperty(getPosition(presentPanel));
+ m_noOfConfiguredNodes=0;
+ m_noOfConfiguredNodes=0;
+ m_noOfConfiguredMgmt=0;
+ m_noOfConfiguredNdb=0;
+ m_noOfConfiguredApi=0;
+ m_bNdb=false;
+ m_bMgmt=false;
+ refreshLook();
+ }
+
+
+ private void btnCancel_Click(object sender, System.EventArgs e)
+ {
+ m_db.removeAllProcesses();
+ this.Dispose(true);
+ }
+
+
+
+
+ private void radioBtnYes_Click(object sender, System.EventArgs e)
+ {
+ if(radioBtnNo.Checked.Equals(false))
+ {
+ if(radioBtnYes.Checked.Equals(true))
+ radioBtnYes.Checked=false;
+ else
+ {
+ radioBtnYes.Checked=true;
+ this.btnNext.Enabled=true;
+ }
+
+ }
+ if(radioBtnNo.Checked.Equals(true))
+ {
+ radioBtnNo.Checked=false;
+ radioBtnYes.Checked=true;
+ buttonComputerAdd.Enabled=false;
+ this.btnNext.Enabled=true;
+ }
+
+ }
+
+ private void radioBtnNo_Click(object sender, System.EventArgs e)
+ {
+ if(radioBtnYes.Checked.Equals(false))
+ {
+ if(radioBtnNo.Checked.Equals(true))
+ {
+ radioBtnNo.Checked=false;
+ buttonComputerAdd.Enabled=false;
+ }
+ else
+ {
+ radioBtnNo.Checked=true;
+ buttonComputerAdd.Enabled=true;
+ this.btnNext.Enabled=false;
+ }
+
+ }
+ if(radioBtnYes.Checked.Equals(true))
+ {
+ radioBtnYes.Checked=false;
+ radioBtnNo.Checked=true;
+ buttonComputerAdd.Enabled=true;
+ this.btnNext.Enabled=false;
+ }
+ }
+
+ private void buttonComputerAdd_Click(object sender, System.EventArgs e)
+ {
+ if(getPosition(presentPanel)==0)
+ {
+ if(radioBtnNo.Checked.Equals(true))
+ {
+ ComputerAddDialog cad = new ComputerAddDialog(mgmt);
+ cad.ShowDialog();
+ }
+ }
+ }
+
+ private void PanelWizard_Activated(object sender, System.EventArgs e)
+ {
+ updateComputers();
+ }
+
+ private void updateComputers()
+ {
+ ArrayList list = mgmt.getComputerCollection();
+ this.listBoxComputers.BeginUpdate();
+ this.listBoxComputers.Items.Clear();
+ foreach(Computer c in list)
+ {
+ this.listBoxComputers.Items.Add(c.getName());
+ }
+ if(listBoxComputers.Items.Count > 0)
+ {
+ btnNext.Enabled=true;
+ }
+ this.listBoxComputers.EndUpdate();
+ this.listBoxComputers.Refresh();
+ }
+
+
+ private void tvComputer_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
+ {
+ tvComputer.SelectedNode.Expand();
+
+ }
+
+ private void tvComputer_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
+ {
+
+ }
+
+ private void tvComputer_MouseLeave(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void tvComputer_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
+ {
+ TreeNode prevNode = tvComputer.SelectedNode;
+ if(prevNode!=null)
+ {
+ prevNode.BackColor=Color.White;
+ }
+ TreeNode node = tvComputer.GetNodeAt(e.X,e.Y);
+ if(node==null)
+ {
+ return;
+ }
+
+ tvComputer.SelectedNode=node;
+ tvComputer.SelectedNode.BackColor=Color.LightGray;
+
+ }
+
+ private void btnTransferNodeToComp_Click(object sender, System.EventArgs e)
+ {
+
+ if(tvComputer.SelectedNode==null)
+ return;
+ if(lvNode.SelectedItems.Equals(null))
+ return;
+ int itemCount=lvNode.SelectedItems.Count;
+ lvNode.BeginUpdate();
+ tvComputer.BeginUpdate();
+ for(int i=0;i < itemCount;i++)
+ {
+ tvComputer.SelectedNode.Nodes.Add(lvNode.SelectedItems[i].Text.ToString());
+ }
+
+ for(int i=0;i < itemCount;i++)
+ {
+ lvNode.Items.RemoveAt(lvNode.SelectedIndices[0]);
+
+ }
+ if(lvNode.Items.Count.Equals(0))
+ btnNext.Enabled=true;
+ else
+ btnNext.Enabled=false;
+ tvComputer.SelectedNode.Expand();
+ lvNode.EndUpdate();
+ tvComputer.EndUpdate();
+ }
+
+ private void lvNode_SelectedIndexChanged(object sender, System.EventArgs e)
+ {
+ }
+
+ private void prepareNodeAssignmentPanel()
+ {
+ ArrayList computers = mgmt.getComputerCollection();
+ m_nNDB=Convert.ToInt32(comboNDB.SelectedItem.ToString());
+ m_nAPI=Convert.ToInt32(comboAPI.SelectedItem.ToString());
+ m_nMGM=Convert.ToInt32(comboMGM.SelectedItem.ToString());
+
+ lvNode.Items.Clear();
+ tvComputer.Nodes.Clear();
+ for (int i=1;i<=m_nMGM;i++)
+ lvNode.Items.Add("mgm."+i);
+
+ for (int i=m_nMGM+1;i<=(m_nNDB+m_nMGM);i++)
+ lvNode.Items.Add("ndb."+i);
+
+ for (int i=m_nMGM+m_nNDB+1;i<=(m_nNDB+m_nMGM+m_nAPI);i++)
+ lvNode.Items.Add("api."+i);
+
+ foreach(Computer c in computers)
+ {
+ if(c.getStatus() == Computer.Status.Connected)
+ tvComputer.Nodes.Add(c.getName());
+ }
+
+ }
+ private void prepareNodeConfigurationPanel()
+ {
+ Computer c;
+ for(int i=0;i<tvComputer.Nodes.Count;i++)
+ {
+ c=mgmt.getComputer(tvComputer.Nodes[i].Text.ToString());
+ for(int j=0; j < tvComputer.Nodes[i].Nodes.Count;j++)
+ {
+ m_db.addProcess(new Process(tvComputer.Nodes[i].Nodes[j].Text.ToString(),m_db.getOwner(),m_db.getName(),c));
+ c.addProcess(m_db.getProcessByName(tvComputer.Nodes[i].Nodes[j].Text.ToString()));
+ }
+ }
+ }
+
+ private void updateListViews()
+ {/*
+ lvConfig.Items.Clear();
+ ArrayList processes = m_db.getProcesses();
+ string [] processcols= new string[5];
+ foreach (Process process in processes)
+ {
+ processcols[0]=process.getName();
+ processcols[1]=process.getComputer().getName();
+ processcols[2]=process.getPath();
+ processcols[3]="";
+ processcols[4]="";
+
+ ListViewItem lvc= new ListViewItem(processcols);
+
+
+ lvConfig.Items.Add(lvc);
+ }
+ lvConfig.EndUpdate();
+ */
+ }
+
+ private void btnConfigure_Click(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void textDbName_TextChanged(object sender, System.EventArgs e)
+ {
+ if(textOwner.TextLength>0 && textDbName.TextLength > 0)
+ nextEnabled=true;
+ else
+ nextEnabled=false;
+
+ refreshLook();
+
+ }
+
+ private void checkBoxLater_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if(checkBoxLater.Checked.Equals(true))
+ {
+ this.finishEnabled=true;
+ this.nextEnabled=false;
+ }
+ else
+ {
+ this.finishEnabled=false;
+ this.nextEnabled=true;
+ }
+ this.refreshLook();
+ }
+
+ private void btnFinish_Click(object sender, System.EventArgs e)
+ {
+ mgmt.AddDatabase(this.m_db);
+
+ if(radioStartYes.Checked==true)
+ startDatabase();
+ this.Dispose();
+ }
+
+ private void panel4_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
+ {
+
+ // Point location= new Point(8,40);
+ // Size s= new Size(panel4.Size.Width-8,panel4.Size.Height-120);
+ // lvConfig.Location=location;
+ // lvConfig.Size=s;
+
+
+ }
+
+ private void configureMgmt()
+ {
+ //clear old
+ textOther.Text="";
+ textArgs.Text="";
+ textCwd.Text="";
+ textPath.Text="";
+
+ textPath.Clear();
+ textEnv.Clear();
+ textOther.Clear();
+ textCwd.Clear();
+ textArgs.Clear();
+
+ textPath.ClearUndo();
+ textEnv.ClearUndo();
+ textOther.ClearUndo();
+ textCwd.ClearUndo();
+ textArgs.ClearUndo();
+
+
+ textOther.Enabled=true;
+ textArgs.Enabled=true;
+ textCwd.Enabled=true;
+ textPath.Enabled=true;
+
+ textPath.TabStop=true;
+ textOther.TabStop=true;
+ textArgs.TabStop=true;
+ textCwd.TabStop=true;
+ textEnv.TabStop=true;
+
+ labelTitle.Text="Mgmtsrvr configuration";
+ labelPath.Text="Path to mgmtsrvr binary:";
+ labelArgs.Text="Arguments to mgmtsrvr:";
+ labelOther.Text="Mgmtsrvr port (-p X):";
+
+ //get new
+ String process="mgm." + Convert.ToString(m_noOfConfiguredMgmt+1);
+ Process mgmt=m_db.getProcessByName(process);
+ textComputer.Text=mgmt.getComputer().getName();
+ textName.Text=mgmt.getName().ToString();
+ textDatabase.Text=mgmt.getDatabase().ToString();
+ m_mgmHost=mgmt.getComputer().getName();
+ textPath.Focus();
+ }
+ private void configureApi()
+ {
+ checkBoxReuse.Text="Use the same configuration for ALL API nodes?";
+ if(m_nAPI > 1)
+ {
+ checkBoxReuse.Visible=true;
+ checkBoxReuse.Enabled=true;
+
+ }
+ else
+ {
+ checkBoxReuse.Enabled=false;
+ checkBoxReuse.Visible=true;
+ }
+
+ // clear previous and get a new api
+
+ textOther.Text="";
+ textArgs.Text="";
+ //textCwd.Text="";
+ //textPath.Text="";
+ //get new api
+ textOther.Enabled=false;
+ textArgs.Enabled=true;
+ labelTitle.Text="API node configuration";
+ labelPath.Text="Path to api binary:";
+ labelArgs.Text="Arguments to api:";
+ labelOther.Text="NDB_CONNECTSTRING";
+ String process="api." + Convert.ToString(m_noOfConfiguredApi+m_nMGM+m_nNDB+1);
+ Process api=m_db.getProcessByName(process);
+ textComputer.Text=api.getComputer().getName();
+ textName.Text=api.getName().ToString();
+ textOther.Text="nodeid=" + Convert.ToString(m_noOfConfiguredApi+m_nMGM+m_nNDB+1) + ";host="+this.m_mgmHost + ":" + this.m_mgmPort;
+ textDatabase.Text=api.getDatabase().ToString();
+ textPath.Focus();
+ }
+
+ private void configureNdb()
+ {
+
+
+ checkBoxReuse.Text="Use the same configuration for ALL NDB nodes?";
+
+
+ if(this.m_nNDB > 1)
+ {
+ checkBoxReuse.Visible=true;
+ checkBoxReuse.Enabled=true;
+
+ }
+ else
+ {
+ checkBoxReuse.Enabled=false;
+ checkBoxReuse.Visible=true;
+ }
+
+
+
+ labelPath.Text="Path to ndb binary:";
+ labelArgs.Text="Arguments to ndb:";
+
+ // clear previous and get a new ndb
+
+ labelOther.Text="NDB_CONNECTSTRING";
+ textArgs.Text="-i";
+ textOther.Enabled=false;
+ textArgs.Enabled=false;
+
+ textPath.TabStop=true;
+ textEnv.TabStop=true;
+ textOther.TabStop=false;
+ textArgs.TabStop=false;
+ textCwd.TabStop=true;
+
+ //textCwd.Text="";
+ //textPath.Text="";
+ //get new
+
+ String process="ndb." + Convert.ToString(m_noOfConfiguredNdb+m_nMGM+1);
+ textOther.Text="nodeid=" + Convert.ToString(m_noOfConfiguredNdb+m_nMGM+1) + ";host="+this.m_mgmHost + ":" + this.m_mgmPort;
+ Process ndb=m_db.getProcessByName(process);
+ textComputer.Text=ndb.getComputer().getName();
+ textName.Text=ndb.getName().ToString();
+ textDatabase.Text=ndb.getDatabase().ToString();
+ textPath.Focus();
+ }
+
+
+ public void saveMgm()
+ {
+ String process="mgm." + Convert.ToString(m_noOfConfiguredMgmt+1);
+ Process mgmt=m_db.getProcessByName(process);
+ mgmt.setOther(textOther.Text.ToString());
+ mgmt.setEnv(textEnv.Text.ToString());
+ m_mgmPort = textOther.Text.ToString();
+ try
+ {
+ m_db.setMgmtPort(Convert.ToInt32(m_mgmPort));
+ }
+ catch(Exception e)
+ {
+ MessageBox.Show("Port number must be numeric!!!", "Error",MessageBoxButtons.OK);
+ this.configureMgmt();
+ return;
+ }
+ mgmt.setPath(textPath.Text.ToString());
+ mgmt.setCwd(textCwd.Text.ToString());
+ mgmt.setProcessType("permanent");
+ mgmt.setArgs("-i initconfig.txt");
+ mgmt.setConnectString("nodeid=" + Convert.ToString(m_noOfConfiguredMgmt+1)+";host="+m_mgmHost+":" + m_mgmPort);
+ this.m_noOfConfiguredMgmt++;
+ }
+
+ public void saveApi()
+ {
+ if(checkBoxReuse.Checked)
+ {
+ for(;m_noOfConfiguredApi<m_nAPI;m_noOfConfiguredApi++)
+ {
+ String process="api." + Convert.ToString(m_noOfConfiguredApi+m_nMGM+m_nNDB+1);
+ Process api=m_db.getProcessByName(process);
+ textName.Text=process;
+ api.setPath(textPath.Text.ToString());
+ api.setArgs(textArgs.Text.ToString());
+ api.setCwd(textCwd.Text.ToString());
+ api.setEnv(textEnv.Text.ToString());
+ api.setConnectString("nodeid=" + Convert.ToString(m_noOfConfiguredApi+m_nNDB+m_nMGM+1)+";host="+m_mgmHost+":" + m_mgmPort);
+ api.setProcessType("permanent");
+ }
+
+ }
+ else
+ {
+ String process="api." + Convert.ToString(m_noOfConfiguredApi+m_nMGM+m_nNDB+1);
+ Process api=m_db.getProcessByName(process);
+ api.setPath(textPath.Text.ToString());
+ api.setCwd(textCwd.Text.ToString());
+ api.setEnv(textEnv.Text.ToString());
+ api.setConnectString("nodeid=" + Convert.ToString(m_noOfConfiguredApi+m_nNDB+m_nMGM+1)+";host="+m_mgmHost+":" + m_mgmPort);
+ api.setArgs(textArgs.Text.ToString());
+ api.setProcessType("permanent");
+ this.m_noOfConfiguredApi++;
+ }
+ }
+
+ public void saveNdb()
+ {
+
+ if(checkBoxReuse.Checked)
+ {
+ for(;m_noOfConfiguredNdb<m_nNDB;m_noOfConfiguredNdb++)
+ {
+ String process="ndb." + Convert.ToString(m_noOfConfiguredNdb+m_nMGM+1);
+ Process ndb=m_db.getProcessByName(process);
+ ndb.setConnectString("nodeid=" + Convert.ToString(m_noOfConfiguredNdb+m_nMGM+1)+";host="+m_mgmHost+":" + m_mgmPort);
+ ndb.setPath(textPath.Text.ToString());
+ ndb.setArgs(textArgs.Text.ToString());
+ ndb.setEnv(textEnv.Text.ToString());
+ ndb.setCwd(textCwd.Text.ToString());
+ ndb.setProcessType("permanent");
+ }
+ checkBoxReuse.Checked=false;
+ return;
+ }
+ else
+ {
+ String process="ndb." + Convert.ToString(m_noOfConfiguredNdb+m_nMGM+1);
+ Process ndb=m_db.getProcessByName(process);
+ ndb.setConnectString("nodeid=" + Convert.ToString(m_noOfConfiguredNdb+m_nMGM+1)+";host="+m_mgmHost+":" + m_mgmPort);
+ ndb.setPath(textPath.Text.ToString());
+ ndb.setCwd(textCwd.Text.ToString());
+ ndb.setArgs(textArgs.Text.ToString());
+ ndb.setEnv(textEnv.Text.ToString());
+ ndb.setProcessType("permanent");
+ m_noOfConfiguredNdb++;
+ }
+
+ }
+
+
+ private void panel5_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
+ {
+ nextEnabled=false;
+ buttonSave.Enabled=true;
+ checkBoxReuse.Visible=false;
+ refreshLook();
+ configureMgmt();
+ }
+
+ private void buttonSave_Click(object sender, System.EventArgs e)
+ {
+ Process p = m_db.getProcessByName(textName.Text.ToString());
+
+ if(textOther.Text.ToString().Equals(""))
+ {
+ if(textName.Text.StartsWith("mgm"))
+ {
+ MessageBox.Show("You have to specify a port.","Warning",MessageBoxButtons.OK);
+ return;
+ }
+ if(textName.Text.StartsWith("ndb"))
+ {
+ MessageBox.Show("You have to specify a filesystem path.","Warning",MessageBoxButtons.OK);
+ return;
+ }
+
+ }
+
+ if(textPath.Text.ToString().Equals(""))
+ {
+ if(textName.Text.StartsWith("mgm"))
+ {
+ MessageBox.Show("You have to specify the path to the mgmtsrvr.","Warning",MessageBoxButtons.OK);
+ return;
+ }
+ if(textName.Text.StartsWith("ndb"))
+ {
+ MessageBox.Show("You have to specify the path to ndb.","Warning",MessageBoxButtons.OK);
+ return;
+ }
+ }
+
+ if(textArgs.Text.ToString().Equals(""))
+ {
+ if(textName.Text.StartsWith("mgm"))
+ {
+ MessageBox.Show("You have to specify the arguments to the mgmtsrvr.","Warning",MessageBoxButtons.OK);
+ return;
+ }
+ }
+
+ if(textCwd.Text.ToString().Equals(""))
+ {
+ if(textCwd.Text.StartsWith("mgm"))
+ {
+ MessageBox.Show("You have to specify the current working directory for the mgmtsrvr.","Warning",MessageBoxButtons.OK);
+ return;
+ }
+ }
+
+
+ /*
+ * INPUT IS FINE AT THIS POINT
+ * Everything needed for respective process is ok
+ * */
+
+ if(textName.Text.StartsWith("mgm"))
+ {
+ //MessageBox.Show(textOther.Text.ToString());
+ saveMgm();
+
+ }
+
+ if(textName.Text.StartsWith("ndb"))
+ {
+ saveNdb();
+
+ }
+
+ if(textName.Text.StartsWith("api"))
+ {
+ saveApi();
+
+ }
+
+ if(m_noOfConfiguredMgmt < m_nMGM)
+ {
+ //load another Mgmt
+ labelTitle.Text="Mgmtsrvr configuration";
+ configureMgmt();
+ }
+ else
+ {
+ m_bMgmt=true;
+ }
+
+ if(m_bMgmt)
+ {
+ labelTitle.Text="NDB node configuration";
+ if(m_noOfConfiguredNdb < m_nNDB)
+ {
+ configureNdb();
+ }
+ else
+ m_bNdb=true;
+ }
+
+ if(m_bNdb && m_bMgmt)
+ {
+ labelTitle.Text="API node configuration";
+ if(m_noOfConfiguredApi < m_nAPI)
+ configureApi();
+ else
+ {
+ nextEnabled=true;
+ buttonSave.Enabled=false;
+ refreshLook();
+ }
+ }
+
+ }
+
+ private void listBoxComputers_SelectedIndexChanged(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
+ {
+ updateComputers();
+ }
+
+ private void radioYes_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if(radioYes.Checked==true)
+ {
+ radioNo.Checked=false;
+ }
+ if(radioYes.Checked==false)
+ {
+ radioNo.Checked=true;
+ }
+
+ }
+
+ private void radioNo_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if(radioNo.Checked==true)
+ {
+ radioYes.Checked=false;
+ }
+ if(radioNo.Checked==false)
+ {
+ radioYes.Checked=true;
+ }
+ }
+
+ private void radioStartYes_CheckedChanged(object sender, System.EventArgs e)
+ {
+
+ if(radioStartYes.Checked==true)
+ {
+ radioStartNo.Checked=false;
+ }
+ if(radioStartYes.Checked==false)
+ {
+ radioStartNo.Checked=true;
+ }
+ finishEnabled=true;
+ refreshLook();
+ }
+
+ private void radioStartNo_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if(radioStartNo.Checked==true)
+ {
+ radioStartYes.Checked=false;
+ }
+ if(radioStartNo.Checked==false)
+ {
+ radioStartYes.Checked=true;
+ }
+ finishEnabled=true;
+ refreshLook();
+ }
+
+
+
+
+ public void startDatabase()
+ {
+ startDatabaseDlg x = new startDatabaseDlg(this.m_db);
+
+
+ x.ShowDialog();
+
+
+ }
+
+
+ public void generateInitConfig()
+ {
+ MessageBox.Show("Generate initconfig.txt");
+ }
+
+ private void label11_Click(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void textOwner_TextChanged(object sender, System.EventArgs e)
+ {
+ if(textDbName.TextLength > 0 && textOwner.TextLength > 0)
+ nextEnabled=true;
+ else
+ nextEnabled=false;
+
+ refreshLook();
+ }
+
+ private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
+ {
+ textOwner.Text=System.Environment.UserName;
+ this.Validate();
+ if(textDbName.TextLength > 0 && textOwner.TextLength>0)
+ {
+ nextEnabled=true;
+ }
+ else
+ {
+ nextEnabled=false;
+ }
+ refreshLook();
+ }
+
+ private void textPath_TextChanged(object sender, System.EventArgs e)
+ {
+ try
+ {
+
+ }
+ catch (Exception exc)
+ {
+ MessageBox.Show(exc.ToString());
+ }
+ }
+
+ private void panel2_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ if(textOwner.TextLength>0 && textDbName.TextLength > 0)
+ nextEnabled=true;
+ else
+ nextEnabled=false;
+ }
+
+
+
+
+
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/Process.cs b/storage/ndb/src/cw/cpcc-win32/csharp/Process.cs
new file mode 100644
index 00000000000..c1ee1b2fe9e
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/Process.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Data;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for Process.
+ /// </summary>
+ public class Process
+ {
+ public enum Status {Running, Stopped, Unknown}
+ private string m_id;
+ protected string m_name;
+ private Status m_status;
+ private Computer m_computer;
+ private string m_owner;
+ private string m_cwd;
+ private string m_type;
+ private string m_path;
+ private string m_other;
+ private string m_args;
+ private string m_env;
+ private string m_database;
+ private string m_connectString;
+ private bool m_defined;
+ public Process( string name,
+ string owner, string database,
+ Computer computer)
+ {
+ m_name=name;
+ m_owner=owner;
+ m_computer=computer;
+ m_status=Status.Unknown;
+ m_database=database;
+ m_defined=false;
+ m_path="";
+ m_cwd="";
+ m_args="";
+ m_other="";
+ }
+ public Process()
+ {
+
+ }
+ public Process(string id)
+ {
+ m_id=id;
+ }
+
+ public Process( string name,
+ string database,
+ Computer computer)
+ {
+ m_name=name;
+ m_computer=computer;
+ m_status=Status.Unknown;
+ m_database=database;
+ m_defined=false;
+ }
+
+ public Process( string name,
+ Computer computer)
+ {
+ m_name=name;
+ m_computer=computer;
+ m_status=Status.Unknown;
+ m_defined=false;
+ }
+
+
+ public string getStatusString()
+ {
+ if(m_status.Equals(Status.Running))
+ return "Running";
+ if(m_status.Equals(Status.Stopped))
+ return "Stopped";
+ return "Unknown";
+ }
+
+ public Computer getComputer() {return m_computer;}
+ public string getName() {return m_name;}
+ public string getDatabase() {return m_database;}
+ public string getOwner() {return m_owner;}
+ public string getId() {return m_id;}
+ public void setId(string id) {m_id=id;}
+
+ public void setCwd(string cwd) {m_cwd=cwd;}
+ public void setPath(string path) {m_path=path;}
+ public void setArgs(string args) {m_args=args;}
+ public void setOther(string other) {m_other=other;}
+ public void setEnv(string env) {m_env=env;}
+ public void setName(string name) {m_name=name;}
+ public void setOwner(string owner) {m_owner=owner;}
+ public void setDatabase(string db) {m_database=db;}
+ public void setComputer(Computer c) {m_computer=c;}
+
+
+ public string getCwd() {return m_cwd;}
+ public string getPath() {return m_path;}
+ public string getArgs() {return m_args;}
+ public string getOther() {return m_other;}
+ public string getEnv() {return m_env;}
+
+ public bool isDefined() {return m_defined;}
+ public void setDefined(bool defined)
+ {
+ m_defined=defined;
+ }
+
+ public Status getStatus()
+ {
+ return m_status;
+ }
+
+ public void setConnectString(string cs)
+ {
+ m_connectString=cs;
+ }
+
+ public string getConnectString()
+ {
+ return m_connectString;
+ }
+ public void setStatus(Status status)
+ {
+ m_status=status;
+ }
+
+
+ public void setProcessType(string type)
+ {
+ m_type=type;
+ }
+ public string getProcessType()
+ {
+ return m_type;
+ }
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/ProcessDefineDialog.cs b/storage/ndb/src/cw/cpcc-win32/csharp/ProcessDefineDialog.cs
new file mode 100644
index 00000000000..581b8383e7c
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/ProcessDefineDialog.cs
@@ -0,0 +1,435 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for ProcessDefineDialog.
+ /// </summary>
+ public class ProcessDefineDialog : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.ComboBox comboComputer;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.Label label6;
+ private System.Windows.Forms.Label label7;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.Label label9;
+ private System.Windows.Forms.TextBox textProcessName;
+ private System.Windows.Forms.TextBox textProcessGroup;
+ private System.Windows.Forms.TextBox textProcessEnv;
+ private System.Windows.Forms.TextBox textProcessPath;
+ private System.Windows.Forms.TextBox textProcessArgs;
+ private System.Windows.Forms.TextBox textProcessCWD;
+ private System.Windows.Forms.TextBox textProcessOwner;
+ private System.Windows.Forms.ComboBox comboType;
+ private System.Windows.Forms.Label label10;
+ private System.Windows.Forms.Label label11;
+ private System.Windows.Forms.Label label12;
+ private System.Windows.Forms.Label label13;
+ private System.Windows.Forms.Label label15;
+ private System.Windows.Forms.Label label16;
+ private System.Windows.Forms.Label label14;
+ private System.Windows.Forms.Label label17;
+ private System.Windows.Forms.Label label18;
+ private System.Windows.Forms.Button btnAdd;
+ private System.Windows.Forms.Button btnCancel;
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.Container components = null;
+ private ComputerMgmt c_mgmt;
+ private string m_selComputer;
+ public ProcessDefineDialog(ComputerMgmt mgmt, string computer)
+ {
+
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ //
+ // TODO: Add any constructor code after InitializeComponent call
+ //
+ m_selComputer =computer; //the selected computer in the TreeView
+ c_mgmt=mgmt;
+ }
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.comboComputer = new System.Windows.Forms.ComboBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.label6 = new System.Windows.Forms.Label();
+ this.label7 = new System.Windows.Forms.Label();
+ this.label8 = new System.Windows.Forms.Label();
+ this.label9 = new System.Windows.Forms.Label();
+ this.textProcessName = new System.Windows.Forms.TextBox();
+ this.textProcessGroup = new System.Windows.Forms.TextBox();
+ this.textProcessEnv = new System.Windows.Forms.TextBox();
+ this.textProcessPath = new System.Windows.Forms.TextBox();
+ this.textProcessArgs = new System.Windows.Forms.TextBox();
+ this.textProcessCWD = new System.Windows.Forms.TextBox();
+ this.textProcessOwner = new System.Windows.Forms.TextBox();
+ this.comboType = new System.Windows.Forms.ComboBox();
+ this.label10 = new System.Windows.Forms.Label();
+ this.label11 = new System.Windows.Forms.Label();
+ this.label12 = new System.Windows.Forms.Label();
+ this.label13 = new System.Windows.Forms.Label();
+ this.label15 = new System.Windows.Forms.Label();
+ this.label16 = new System.Windows.Forms.Label();
+ this.label14 = new System.Windows.Forms.Label();
+ this.label17 = new System.Windows.Forms.Label();
+ this.label18 = new System.Windows.Forms.Label();
+ this.btnAdd = new System.Windows.Forms.Button();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // comboComputer
+ //
+ this.comboComputer.ItemHeight = 13;
+ this.comboComputer.Location = new System.Drawing.Point(152, 24);
+ this.comboComputer.Name = "comboComputer";
+ this.comboComputer.Size = new System.Drawing.Size(112, 21);
+ this.comboComputer.TabIndex = 0;
+ //
+ // label1
+ //
+ this.label1.Location = new System.Drawing.Point(24, 24);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(64, 24);
+ this.label1.TabIndex = 1;
+ this.label1.Text = "Computer:";
+ //
+ // label2
+ //
+ this.label2.Location = new System.Drawing.Point(24, 48);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(88, 24);
+ this.label2.TabIndex = 2;
+ this.label2.Text = "Process name:";
+ //
+ // label3
+ //
+ this.label3.Location = new System.Drawing.Point(24, 72);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(88, 24);
+ this.label3.TabIndex = 3;
+ this.label3.Text = "Group:";
+ //
+ // label4
+ //
+ this.label4.Location = new System.Drawing.Point(24, 96);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(88, 24);
+ this.label4.TabIndex = 4;
+ this.label4.Text = "Env. variables:";
+ //
+ // label5
+ //
+ this.label5.Location = new System.Drawing.Point(24, 120);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(88, 24);
+ this.label5.TabIndex = 5;
+ this.label5.Text = "Path to binary:";
+ //
+ // label6
+ //
+ this.label6.Location = new System.Drawing.Point(24, 144);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(112, 24);
+ this.label6.TabIndex = 6;
+ this.label6.Text = "Arguments to binary:";
+ //
+ // label7
+ //
+ this.label7.Location = new System.Drawing.Point(24, 168);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(112, 24);
+ this.label7.TabIndex = 7;
+ this.label7.Text = "Type of process:";
+ //
+ // label8
+ //
+ this.label8.Location = new System.Drawing.Point(24, 192);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(112, 24);
+ this.label8.TabIndex = 8;
+ this.label8.Text = "Current working dir.:";
+ //
+ // label9
+ //
+ this.label9.Location = new System.Drawing.Point(24, 216);
+ this.label9.Name = "label9";
+ this.label9.Size = new System.Drawing.Size(112, 24);
+ this.label9.TabIndex = 9;
+ this.label9.Text = "Owner:";
+ //
+ // textProcessName
+ //
+ this.textProcessName.Location = new System.Drawing.Point(152, 48);
+ this.textProcessName.Name = "textProcessName";
+ this.textProcessName.Size = new System.Drawing.Size(112, 20);
+ this.textProcessName.TabIndex = 1;
+ this.textProcessName.Text = "";
+ //
+ // textProcessGroup
+ //
+ this.textProcessGroup.Location = new System.Drawing.Point(152, 72);
+ this.textProcessGroup.Name = "textProcessGroup";
+ this.textProcessGroup.Size = new System.Drawing.Size(112, 20);
+ this.textProcessGroup.TabIndex = 2;
+ this.textProcessGroup.Text = "";
+ //
+ // textProcessEnv
+ //
+ this.textProcessEnv.Location = new System.Drawing.Point(152, 96);
+ this.textProcessEnv.Name = "textProcessEnv";
+ this.textProcessEnv.Size = new System.Drawing.Size(112, 20);
+ this.textProcessEnv.TabIndex = 3;
+ this.textProcessEnv.Text = "";
+ //
+ // textProcessPath
+ //
+ this.textProcessPath.Location = new System.Drawing.Point(152, 120);
+ this.textProcessPath.Name = "textProcessPath";
+ this.textProcessPath.Size = new System.Drawing.Size(112, 20);
+ this.textProcessPath.TabIndex = 4;
+ this.textProcessPath.Text = "";
+ //
+ // textProcessArgs
+ //
+ this.textProcessArgs.Location = new System.Drawing.Point(152, 144);
+ this.textProcessArgs.Name = "textProcessArgs";
+ this.textProcessArgs.Size = new System.Drawing.Size(112, 20);
+ this.textProcessArgs.TabIndex = 5;
+ this.textProcessArgs.Text = "";
+ //
+ // textProcessCWD
+ //
+ this.textProcessCWD.Location = new System.Drawing.Point(152, 192);
+ this.textProcessCWD.Name = "textProcessCWD";
+ this.textProcessCWD.Size = new System.Drawing.Size(112, 20);
+ this.textProcessCWD.TabIndex = 7;
+ this.textProcessCWD.Text = "";
+ //
+ // textProcessOwner
+ //
+ this.textProcessOwner.Location = new System.Drawing.Point(152, 216);
+ this.textProcessOwner.Name = "textProcessOwner";
+ this.textProcessOwner.Size = new System.Drawing.Size(112, 20);
+ this.textProcessOwner.TabIndex = 8;
+ this.textProcessOwner.Text = "";
+ //
+ // comboType
+ //
+ this.comboType.ItemHeight = 13;
+ this.comboType.Items.AddRange(new object[] {
+ "Permanent",
+ "Interactive"});
+ this.comboType.Location = new System.Drawing.Point(152, 168);
+ this.comboType.Name = "comboType";
+ this.comboType.Size = new System.Drawing.Size(112, 21);
+ this.comboType.TabIndex = 6;
+ //
+ // label10
+ //
+ this.label10.Location = new System.Drawing.Point(272, 32);
+ this.label10.Name = "label10";
+ this.label10.Size = new System.Drawing.Size(88, 16);
+ this.label10.TabIndex = 19;
+ this.label10.Text = "(Mandatory)";
+ //
+ // label11
+ //
+ this.label11.Location = new System.Drawing.Point(272, 56);
+ this.label11.Name = "label11";
+ this.label11.Size = new System.Drawing.Size(88, 16);
+ this.label11.TabIndex = 20;
+ this.label11.Text = "(Mandatory)";
+ //
+ // label12
+ //
+ this.label12.Location = new System.Drawing.Point(272, 80);
+ this.label12.Name = "label12";
+ this.label12.Size = new System.Drawing.Size(88, 16);
+ this.label12.TabIndex = 21;
+ this.label12.Text = "(Mandatory)";
+ //
+ // label13
+ //
+ this.label13.Location = new System.Drawing.Point(272, 127);
+ this.label13.Name = "label13";
+ this.label13.Size = new System.Drawing.Size(88, 16);
+ this.label13.TabIndex = 22;
+ this.label13.Text = "(Mandatory)";
+ //
+ // label15
+ //
+ this.label15.Location = new System.Drawing.Point(272, 176);
+ this.label15.Name = "label15";
+ this.label15.Size = new System.Drawing.Size(88, 16);
+ this.label15.TabIndex = 24;
+ this.label15.Text = "(Mandatory)";
+ //
+ // label16
+ //
+ this.label16.Location = new System.Drawing.Point(272, 200);
+ this.label16.Name = "label16";
+ this.label16.Size = new System.Drawing.Size(88, 16);
+ this.label16.TabIndex = 25;
+ this.label16.Text = "(Mandatory)";
+ //
+ // label14
+ //
+ this.label14.Location = new System.Drawing.Point(272, 224);
+ this.label14.Name = "label14";
+ this.label14.Size = new System.Drawing.Size(88, 16);
+ this.label14.TabIndex = 26;
+ this.label14.Text = "(Mandatory)";
+ //
+ // label17
+ //
+ this.label17.Location = new System.Drawing.Point(272, 104);
+ this.label17.Name = "label17";
+ this.label17.Size = new System.Drawing.Size(88, 16);
+ this.label17.TabIndex = 27;
+ this.label17.Text = "(Optional)";
+ //
+ // label18
+ //
+ this.label18.Location = new System.Drawing.Point(272, 152);
+ this.label18.Name = "label18";
+ this.label18.Size = new System.Drawing.Size(88, 16);
+ this.label18.TabIndex = 28;
+ this.label18.Text = "(Optional)";
+ //
+ // btnAdd
+ //
+ this.btnAdd.Location = new System.Drawing.Point(288, 248);
+ this.btnAdd.Name = "btnAdd";
+ this.btnAdd.TabIndex = 9;
+ this.btnAdd.Text = "Define...";
+ this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
+ //
+ // btnCancel
+ //
+ this.btnCancel.Location = new System.Drawing.Point(152, 248);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.TabIndex = 10;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ //
+ // ProcessDefineDialog
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(370, 279);
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.btnCancel,
+ this.btnAdd,
+ this.label18,
+ this.label17,
+ this.label14,
+ this.label16,
+ this.label15,
+ this.label13,
+ this.label12,
+ this.label11,
+ this.label10,
+ this.comboType,
+ this.textProcessOwner,
+ this.textProcessCWD,
+ this.textProcessArgs,
+ this.textProcessPath,
+ this.textProcessEnv,
+ this.textProcessGroup,
+ this.textProcessName,
+ this.label9,
+ this.label8,
+ this.label7,
+ this.label6,
+ this.label5,
+ this.label4,
+ this.label3,
+ this.label2,
+ this.label1,
+ this.comboComputer});
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "ProcessDefineDialog";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Define Process";
+ this.Load += new System.EventHandler(this.ProcessDefineDialog_Load);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ private void btnCancel_Click(object sender, System.EventArgs e)
+ {
+ this.Dispose();
+ this.Close();
+ }
+
+ private void btnAdd_Click(object sender, System.EventArgs e)
+ {
+ //TODO: ERROR CHECK
+
+ Computer c;
+ c=c_mgmt.getComputer(this.m_selComputer);
+
+ c.addProcess(new Process(this.textProcessName.Text.ToString(),
+ this.textProcessOwner.Text.ToString(),
+ this.textProcessGroup.Text.ToString(),
+ c));
+ this.Close();
+ this.Dispose();
+ }
+
+ private void ProcessDefineDialog_Load(object sender, System.EventArgs e)
+ {
+ comboType.SelectedIndex=0;
+ ArrayList list = c_mgmt.getComputerCollection();
+ int i=0, selIndex=0;
+ foreach(Computer computer in list)
+ {
+ this.comboComputer.Items.Add(computer.getName());
+ if(computer.getName().Equals(m_selComputer))
+ selIndex=i;
+ i++;
+ }
+ comboComputer.SelectedIndex=selIndex;
+ }
+
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/fileaccess/FileMgmt.cs b/storage/ndb/src/cw/cpcc-win32/csharp/fileaccess/FileMgmt.cs
new file mode 100644
index 00000000000..b3a2361bcb0
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/fileaccess/FileMgmt.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Text;
+using System.Collections.Specialized;
+using System.IO;
+using System.Windows.Forms;
+namespace NDB_CPC.fileaccess
+{
+ /// <summary>
+ /// Summary description for FileMgmt.
+ /// </summary>
+ public class FileMgmt
+ {
+ public FileMgmt()
+ {
+ }
+
+ public StringCollection importHostFile(string filename)
+ {
+ StringCollection sc = new StringCollection();
+ StreamReader SR = new StreamReader(filename);
+ string line ="";
+ line = SR.ReadLine();
+ while(!line.Equals(""))
+ {
+ sc.Add(line);
+ line = SR.ReadLine();
+ }
+ return sc;
+ }
+
+ public void exportHostFile(string filename, string content)
+ {
+ StreamWriter SW = new StreamWriter(filename,false);
+ SW.Write(content);
+ SW.WriteLine("");
+ SW.WriteLine("");
+ SW.Close();
+ }
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/simpleparser/SimpleCPCParser.cs b/storage/ndb/src/cw/cpcc-win32/csharp/simpleparser/SimpleCPCParser.cs
new file mode 100644
index 00000000000..b8ff2844af9
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/simpleparser/SimpleCPCParser.cs
@@ -0,0 +1,360 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Windows.Forms;
+using NDB_CPC;
+using NDB_CPC.socketcomm;
+
+namespace NDB_CPC.simpleparser
+{
+ /// <summary>
+ /// Summary description for SimpleCPCParser.
+ /// </summary>
+ public class SimpleCPCParser
+ {
+ public SimpleCPCParser()
+ {
+ //
+ // TODO: Add constructor logic here
+ //
+ }
+
+ public static void parse(Process p, SocketComm comm)
+ {
+
+ string line=comm.readLine();//reader.ReadLine();
+ while(line.Equals(""))
+ {
+ line=comm.readLine();
+ }
+ if(line.Equals("define process"))
+ {
+ defineProcess(p, comm);
+ line="";
+ return;
+ }
+ if(line.Equals("start process"))
+ {
+ startProcess(p,comm);
+ line="";
+ return;
+ }
+ if(line.Equals("stop process"))
+ {
+ stopProcess(p,comm);
+ line="";
+ return;
+ }
+ if(line.Equals("undefine process"))
+ {
+ undefineProcess(p,comm);
+ line="";
+ return;
+ }
+
+ }
+
+ public static void parse(ArrayList processes, Computer c, SocketComm comm)
+ {
+
+ string line=comm.readLine();//reader.ReadLine();
+ while(line.Equals(""))
+ {
+ line=comm.readLine();
+ }
+
+ if(line.Equals("start processes"))
+ {
+ listProcesses(processes, c, comm);
+ line="";
+ return;
+ }
+
+ }
+
+ private static void defineProcess(Process p, SocketComm comm)
+ {
+ string line=comm.readLine();//reader.ReadLine();
+ while(!line.Equals(""))
+ {
+ if(line.StartsWith("status:"))
+ {
+ line=line.Remove(0,7);
+ line=line.Trim();
+ if(line.Equals("1"))
+ {
+ p.setDefined(true);
+ p.setStatus(Process.Status.Stopped);
+ }
+ else
+ p.setDefined(false);
+ }
+ if(line.StartsWith("id:"))
+ {
+ line=line.Remove(0,3);
+ line=line.Trim();
+ p.setId(line);
+ }
+ line=comm.readLine();
+ }
+ }
+
+
+ private static void startProcess(Process p, SocketComm comm)
+ {
+ string line=comm.readLine();//reader.ReadLine();
+ while(!line.Equals(""))
+ {
+ if(line.StartsWith("status:"))
+ {
+ line=line.Remove(0,7);
+ line=line.Trim();
+ if(line.Equals("1"))
+ p.setStatus(NDB_CPC.Process.Status.Running);
+ else
+ p.setStatus(NDB_CPC.Process.Status.Unknown);
+
+ }
+ if(line.StartsWith("id:"))
+ {
+ line=line.Remove(0,3);
+ line=line.Trim();
+ if(p.getId().Equals(line))
+ {
+ ;
+ }
+ else
+ {
+ //damn something is wrong
+ p.setStatus(NDB_CPC.Process.Status.Unknown);
+ }
+
+ }
+ line=comm.readLine();
+ }
+ }
+ private static void undefineProcess(Process p, SocketComm comm)
+ {
+ string line=comm.readLine();//reader.ReadLine();
+ while(!line.Equals(""))
+ {
+ if(line.StartsWith("status:"))
+ {
+
+ line=line.Remove(0,7);
+ line=line.Trim();
+ if(line.Equals("1"))
+ p.setDefined(false);
+ else
+ p.setDefined(true);
+
+ }
+ if(line.StartsWith("id:"))
+ {
+ line=line.Remove(0,3);
+ line=line.Trim();
+ }
+ line=comm.readLine();
+ }
+ }
+
+ private static void stopProcess(Process p, SocketComm comm)
+ {
+ string line=comm.readLine();//reader.ReadLine();
+ while(!line.Equals(""))
+ {
+ if(line.StartsWith("status:"))
+ {
+ line=line.Remove(0,7);
+ line=line.Trim();
+ if(line.Equals("1"))
+ p.setStatus(NDB_CPC.Process.Status.Stopped);
+ else
+ p.setStatus(NDB_CPC.Process.Status.Unknown);
+
+ }
+ if(line.StartsWith("id:"))
+ {
+ line=line.Remove(0,3);
+ line=line.Trim();
+ if(p.getId().Equals(line))
+ {
+ ;
+ }
+ else
+ {
+ //damn something is wrong
+ p.setStatus(NDB_CPC.Process.Status.Unknown);
+ }
+
+ }
+ line=comm.readLine();
+ }
+ }
+ private static void listProcesses(ArrayList processes, Computer c, SocketComm comm)
+ {
+ bool processExist = false;
+
+ string line=comm.readLine();//reader.ReadLine();
+ while(!line.Equals("end processes"))
+ {
+ if(line.Equals("process"))
+ {
+ line=comm.readLine();
+ Process p = new Process();
+
+ while(!line.Equals(""))
+ {
+ if(line.StartsWith("id:"))
+ {
+ string pid;
+ line=line.Remove(0,3);
+ pid=line.Trim();
+ /*check if process already exist*/
+ processExist=findProcess(processes,pid);
+ if(!processExist)
+ {
+ p.setId(pid);
+ }
+ }
+
+ if(line.StartsWith("name:"))
+ {
+
+ line=line.Remove(0,5);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setName(line);
+ }
+ }
+
+ if(line.StartsWith("path:"))
+ {
+
+ line=line.Remove(0,5);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setPath(line);
+ }
+ }
+
+ if(line.StartsWith("args:"))
+ {
+
+ line=line.Remove(0,5);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setArgs(line);
+ }
+ }
+
+ if(line.StartsWith("type:"))
+ {
+
+ line=line.Remove(0,5);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+
+ }
+ }
+
+ if(line.StartsWith("cwd:"))
+ {
+
+ line=line.Remove(0,4);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setCwd(line);
+ }
+ }
+
+ if(line.StartsWith("env:"))
+ {
+
+ line=line.Remove(0,4);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setEnv(line);
+ }
+ }
+
+ if(line.StartsWith("owner:"))
+ {
+
+ line=line.Remove(0,6);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setOwner(line);
+ }
+ }
+ if(line.StartsWith("group:"))
+ {
+
+ line=line.Remove(0,6);
+ line=line.Trim();
+ /*check if process already exist*/
+ if(!processExist)
+ {
+ p.setDatabase(line);
+ }
+ }
+
+ if(line.StartsWith("status:"))
+ {
+
+ line=line.Remove(0,7);
+ line=line.Trim();
+ /*check if process already exist*/
+ //if(!processExist)
+ //{
+ if(line.Equals("0"))
+ p.setStatus(Process.Status.Stopped);
+ if(line.Equals("1"))
+ p.setStatus(Process.Status.Running);
+ if(line.Equals("2"))
+ p.setStatus(Process.Status.Unknown);
+ //}
+ }
+
+
+ line=comm.readLine();
+ }
+ if(!processExist)
+ {
+ p.setComputer(c);
+ p.setDefined(true);
+ processes.Add(p);
+ }
+ processExist=false;
+ }
+ line=comm.readLine();
+
+ }
+ }
+
+ private static bool findProcess(ArrayList processes, string pid)
+ {
+ foreach (Process p in processes)
+ {
+ if(p.getId().Equals(pid))
+ return true;
+ }
+ return false;
+
+ }
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/SocketComm.cs b/storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/SocketComm.cs
new file mode 100644
index 00000000000..2cef5d34f17
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/SocketComm.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Windows.Forms;
+using System.Threading;
+using System.IO;
+
+namespace NDB_CPC.socketcomm
+{
+ /// <summary>
+ /// Summary description for SocketComm.
+ /// </summary>
+ public class SocketComm
+ {
+ private myTcpClient sender;
+ private StreamWriter writer;
+ private StreamReader reader;
+ private string m_host;
+ private int m_port;
+ private bool m_connected;
+ private bool m_connecting;
+ private Thread connectThread;
+ public SocketComm(string host, int port)
+ {
+
+ m_host=host;
+ m_port=port;
+ m_connected=false;
+ m_connecting=false;
+ }
+
+
+
+ public bool isConnected()
+ {
+ return m_connected;
+ }
+
+ public void doConnect()
+ {
+ if(!m_connecting && !m_connected)
+ {
+ connectThread= new Thread(new ThreadStart(connect));
+ connectThread.Start();
+ }
+
+ }
+
+ private void connect()
+ {
+ m_connecting=true;
+ while(true)
+ {
+ if(!m_connected)
+ {
+ try
+ {
+ // Establish the remote endpoint for the socket.
+ // The name of the
+ // remote device is "host.contoso.com".
+
+ // Create a TCP/IP socket.
+ sender = new myTcpClient();
+ // Connect the socket to the remote endpoint. Catch any errors.
+ try
+ {
+ /*
+ IPAddress ipAddress = Dns.Resolve(host).AddressList[0];
+ IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress, 11000);
+*/
+
+
+ sender.Connect(m_host,m_port);;
+
+ writer = new StreamWriter(sender.GetStream(), Encoding.ASCII);
+ reader = new StreamReader(sender.GetStream(), Encoding.ASCII);
+ m_connected=true;
+ m_connecting=false;
+ // break;
+ Console.WriteLine("Socket connected to {0}",
+ sender.ToString());
+
+ }
+ catch (ArgumentNullException ane)
+ {
+ Console.WriteLine("ArgumentNullException : {0}",ane.ToString());
+ m_connected=false;
+ }
+ catch (SocketException se)
+ {
+ Console.WriteLine("SocketException : {0}",se.ToString());
+ m_connected=false;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Unexpected exception : {0}", e.ToString());
+ m_connected=false;
+ }
+
+ }
+
+ Thread.Sleep(200);
+ }
+ }
+
+ public bool disconnect()
+ {
+ try
+ {
+ this.m_connected=false;
+ this.m_connecting=false;
+ sender.GetUnderlyingSocket().Shutdown(SocketShutdown.Both);
+ sender.GetUnderlyingSocket().Close();
+ writer.Close();
+ reader.Close();
+ sender.Close();
+
+ }
+ catch (ArgumentNullException ane)
+ {
+ Console.WriteLine("ArgumentNullException : {0}",ane.ToString());
+ connectThread.Abort();
+ return false;
+ }
+ catch (SocketException se)
+ {
+ Console.WriteLine("SocketException : {0}",se.ToString());
+ connectThread.Abort();
+ return false;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Unexpected exception : {0}", e.ToString());
+ connectThread.Abort();
+ return false;
+ }
+ connectThread.Abort();
+ return true;
+ }
+
+ public bool writeMessage(string message)
+ {
+ int attempts=0;
+ while (attempts < 10)
+ {
+ try
+ {
+ writer.WriteLine(message);
+ writer.Flush();
+ message="";
+ return true;
+ }
+ catch(IOException e)
+ {
+ this.disconnect();
+ this.doConnect();
+ Thread.Sleep(200);
+ attempts++;
+ }
+ catch(System.NullReferenceException)
+ {
+ this.disconnect();
+ this.doConnect();
+
+ Thread.Sleep(200);
+ attempts++;
+ }
+ }
+ return false;
+ }
+
+ public string readLine()
+ {
+ int attempts=0;
+ string line="";
+ while (attempts < 10){
+ try
+ {
+ line = reader.ReadLine();
+ if(line==null)
+ line="";
+ return line;
+ }
+ catch(IOException e)
+ {
+ this.disconnect();
+ this.doConnect();
+ Thread.Sleep(400);
+ attempts++;
+ }
+ catch(System.NullReferenceException)
+ {
+ this.disconnect();
+ this.doConnect();
+ Thread.Sleep(400);
+ attempts++;
+ }
+ }
+ return "";
+
+ }
+
+ }
+}
+
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/myTcpClient.cs b/storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/myTcpClient.cs
new file mode 100644
index 00000000000..9c0d82a0b27
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/myTcpClient.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.IO;
+
+
+namespace NDB_CPC.socketcomm
+{
+ public class myTcpClient : TcpClient
+ {
+ private Socket s;
+ public myTcpClient(): base()
+ {
+ if(this.Active)
+ {
+ s = this.Client;
+ }
+ }
+ public Socket GetUnderlyingSocket()
+ {
+ return s;
+ }
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/startDatabaseDlg.cs b/storage/ndb/src/cw/cpcc-win32/csharp/startDatabaseDlg.cs
new file mode 100644
index 00000000000..cecfcaeb0f3
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/startDatabaseDlg.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using NDB_CPC.simpleparser;
+
+namespace NDB_CPC
+{
+ /// <summary>
+ /// Summary description for startDatabase.
+ /// </summary>
+ public class startDatabaseDlg : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.TextBox textAction;
+ private System.Windows.Forms.Label label1;
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.Container components = null;
+ private System.Windows.Forms.ProgressBar progressBar;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button buttonGo;
+ private Database m_db;
+ public startDatabaseDlg(Database db)
+ {
+
+ //
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ //
+ // TODO: Add any constructor code after InitializeComponent call
+ //
+ m_db=db;
+ }
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.textAction = new System.Windows.Forms.TextBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.progressBar = new System.Windows.Forms.ProgressBar();
+ this.label2 = new System.Windows.Forms.Label();
+ this.buttonGo = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // textAction
+ //
+ this.textAction.Location = new System.Drawing.Point(104, 40);
+ this.textAction.Name = "textAction";
+ this.textAction.ReadOnly = true;
+ this.textAction.Size = new System.Drawing.Size(256, 20);
+ this.textAction.TabIndex = 0;
+ this.textAction.Text = "";
+ //
+ // label1
+ //
+ this.label1.Location = new System.Drawing.Point(8, 40);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(96, 16);
+ this.label1.TabIndex = 1;
+ this.label1.Text = "Current activity:";
+ this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // progressBar
+ //
+ this.progressBar.Location = new System.Drawing.Point(104, 88);
+ this.progressBar.Name = "progressBar";
+ this.progressBar.Size = new System.Drawing.Size(152, 16);
+ this.progressBar.TabIndex = 2;
+ //
+ // label2
+ //
+ this.label2.Location = new System.Drawing.Point(8, 88);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(96, 16);
+ this.label2.TabIndex = 3;
+ this.label2.Text = "Activity progress:";
+ this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // buttonGo
+ //
+ this.buttonGo.Location = new System.Drawing.Point(152, 136);
+ this.buttonGo.Name = "buttonGo";
+ this.buttonGo.Size = new System.Drawing.Size(96, 24);
+ this.buttonGo.TabIndex = 4;
+ this.buttonGo.Text = "Go!";
+ this.buttonGo.Click += new System.EventHandler(this.buttonGo_Click);
+ //
+ // startDatabaseDlg
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(378, 167);
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.buttonGo,
+ this.label2,
+ this.progressBar,
+ this.label1,
+ this.textAction});
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "startDatabaseDlg";
+ this.Text = "Starting database";
+ this.Load += new System.EventHandler(this.startDatabase_Load);
+ this.Paint += new System.Windows.Forms.PaintEventHandler(this.startDatabase_Paint);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ private void startDatabase_Load(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void startDatabase_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
+ {
+
+
+
+ }
+ private void defineProcesses()
+ {
+ ArrayList processes = m_db.getProcesses();
+ progressBar.Maximum = processes.Count;
+ progressBar.Minimum = 0;
+
+ int retry=0;
+ //sc.connect("130.100.232.7");
+ foreach (Process p in processes)
+ {
+ Computer comp;
+ retry=0;
+ //if(p.getName().StartsWith("ndb") || p.getName().StartsWith("mgm"))
+ //{
+ textAction.Text="Defining process " + p.getName();
+ textAction.Refresh();
+ comp=p.getComputer();
+ while(retry<10)
+ {
+ if(!comp.isConnected())
+ {
+ comp.connectToCpcd();
+
+ }
+ else
+ {
+ if(comp.defineProcess(p)<0)
+ {
+ ;
+ }
+ else
+ break;
+ }
+ if(retry==9)
+ {
+ if(MessageBox.Show(this,"Failed to define process. Try again?","Warning!!!",MessageBoxButtons.YesNo)==DialogResult.Yes)
+ retry=0;
+ }
+ retry++;
+ //comp.undefineProcess(p);
+ }
+ //}
+ progressBar.PerformStep();
+ }
+ }
+
+ private void startProcesses()
+ {
+
+ ArrayList processes = m_db.getProcesses();
+ progressBar.Maximum = processes.Count;
+ progressBar.Minimum = 0;
+ string start = "start process \n";
+
+ int retry=0;
+ //sc.connect("130.100.232.7");
+ foreach (Process p in processes)
+ {
+ Computer comp;
+ if((p.getName().StartsWith("ndb")) || (p.getName().StartsWith("mgm")))
+ {
+ textAction.Text="Starting process " + p.getName();
+ textAction.Refresh();
+ start = start + "id:" + p.getId() + "\n\n";
+ comp=p.getComputer();
+ while(retry<10)
+ {
+ if(!comp.isConnected())
+ {
+ comp.connectToCpcd();
+ }
+ else
+ {
+ if(comp.startProcess(p)<0)
+ {
+ ;
+ }
+ else
+ break;
+ }
+ if(retry==9)
+ {
+ if(MessageBox.Show(this,"Failed to start process. Retry again?","Warning!!!",MessageBoxButtons.YesNo)==DialogResult.Yes)
+ retry=0;
+ }
+
+ retry++;
+ }
+ }
+ progressBar.PerformStep();
+
+ }
+
+ }
+
+ private void buttonGo_Click(object sender, System.EventArgs e)
+ {
+ buttonGo.Enabled=false;
+ progressBar.Step=1;
+ defineProcesses();
+ progressBar.Value=0;
+ startProcesses();
+
+ }
+
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/csharp/telnetclient/telnetClient.cs b/storage/ndb/src/cw/cpcc-win32/csharp/telnetclient/telnetClient.cs
new file mode 100644
index 00000000000..a7966947e1f
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/csharp/telnetclient/telnetClient.cs
@@ -0,0 +1,408 @@
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Data;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.IO;
+using System.Threading ;
+
+namespace NDB_CPC.telnetclient
+{
+ /// <summary>
+ /// Summary description for telnetClient.
+ /// </summary>
+ public class telnetClient
+ {
+ Char IAC = Convert.ToChar(255);
+ Char DO = Convert.ToChar(253);
+ Char DONT = Convert.ToChar(254);
+ Char WILL = Convert.ToChar(251);
+ Char WONT = Convert.ToChar(252);
+ Char SB = Convert.ToChar(250);
+ Char SE = Convert.ToChar(240);
+ const Char IS = '0';
+ const Char SEND = '1';
+ const Char INFO = '2';
+ const Char VAR = '0';
+ const Char VALUE = '1';
+ const Char ESC = '2';
+ const Char USERVAR = '3';
+ string m_strResp;
+
+ private ArrayList m_ListOptions = new ArrayList();
+ private IPEndPoint iep ;
+ private AsyncCallback callbackProc ;
+ private string address ;
+ private int port ;
+ private Socket s ;
+ private TextBox textBox1;
+ Byte[] m_byBuff = new Byte[32767];
+
+
+ public telnetClient(string ip, int p, TextBox tb)
+ {
+
+ address = ip;
+ port = p;
+ textBox1=tb;
+ IPHostEntry IPHost = Dns.Resolve(address);
+ string []aliases = IPHost.Aliases;
+ IPAddress[] addr = IPHost.AddressList;
+
+ try
+ {
+ // Create New Socket
+ s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ // Create New EndPoint
+ iep = new IPEndPoint(addr[0],port);
+ // This is a non blocking IO
+ s.Blocking = false ;
+ // Assign Callback function to read from Asyncronous Socket
+ callbackProc = new AsyncCallback(ConnectCallback);
+ // Begin Asyncronous Connection
+ s.BeginConnect(iep , callbackProc, s ) ;
+
+ }
+ catch(Exception eeeee )
+ {
+ MessageBox.Show(eeeee.Message , "Application Error!!!" , MessageBoxButtons.OK , MessageBoxIcon.Stop );
+ Application.Exit();
+ }
+ }
+
+ public void ConnectCallback( IAsyncResult ar )
+ {
+ try
+ {
+ // Get The connection socket from the callback
+ Socket sock1 = (Socket)ar.AsyncState;
+ if ( sock1.Connected )
+ {
+ // Define a new Callback to read the data
+ AsyncCallback recieveData = new AsyncCallback( OnRecievedData );
+ // Begin reading data asyncronously
+ sock1.BeginReceive( m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData , sock1 );
+ }
+ }
+ catch( Exception ex )
+ {
+ MessageBox.Show(ex.Message, "Setup Recieve callbackProc failed!" );
+ }
+ }
+
+
+ public void OnRecievedData( IAsyncResult ar )
+ {
+ // Get The connection socket from the callback
+ Socket sock = (Socket)ar.AsyncState;
+ // Get The data , if any
+ int nBytesRec = sock.EndReceive( ar );
+ if( nBytesRec > 0 )
+ {
+ string sRecieved = Encoding.ASCII.GetString( m_byBuff, 0, nBytesRec );
+ string m_strLine="";
+ for ( int i=0; i < nBytesRec;i++)
+ {
+ Char ch = Convert.ToChar(m_byBuff[i]);
+ switch( ch )
+ {
+ case '\r':
+ m_strLine += Convert.ToString("\r\n");
+ break;
+ case '\n':
+ break;
+ default:
+ m_strLine += Convert.ToString(ch);
+ break;
+ }
+ }
+ try
+ {
+ int strLinelen = m_strLine.Length ;
+ if ( strLinelen == 0 )
+ {
+ m_strLine = Convert.ToString("\r\n");
+ }
+
+ Byte[] mToProcess = new Byte[strLinelen];
+ for ( int i=0; i < strLinelen ; i++)
+ mToProcess[i] = Convert.ToByte(m_strLine[i]);
+ // Process the incoming data
+ string mOutText = ProcessOptions(mToProcess);
+ if ( mOutText != "" )
+ textBox1.AppendText(mOutText);
+
+ // Respond to any incoming commands
+ RespondToOptions();
+ }
+ catch( Exception ex )
+ {
+ Object x = this ;
+ MessageBox.Show(ex.Message , "Information!" );
+ }
+ }
+ else
+ {
+ // If no data was recieved then the connection is probably dead
+ Console.WriteLine( "Disconnected", sock.RemoteEndPoint );
+ sock.Shutdown( SocketShutdown.Both );
+ sock.Close();
+ Application.Exit();
+ }
+ }
+
+ private string ProcessOptions(byte[] m_strLineToProcess)
+ {
+ string m_DISPLAYTEXT ="";
+ string m_strTemp ="" ;
+ string m_strOption ="";
+ string m_strNormalText ="";
+ bool bScanDone =false;
+ int ndx =0;
+ int ldx =0;
+ char ch ;
+ try
+ {
+ for ( int i=0; i < m_strLineToProcess.Length ; i++)
+ {
+ Char ss = Convert.ToChar(m_strLineToProcess[i]);
+ m_strTemp = m_strTemp + Convert.ToString(ss);
+ }
+
+ while(bScanDone != true )
+ {
+ int lensmk = m_strTemp.Length;
+ ndx = m_strTemp.IndexOf(Convert.ToString(IAC));
+ if ( ndx > lensmk )
+ ndx = m_strTemp.Length;
+
+ if(ndx != -1)
+ {
+ m_DISPLAYTEXT+= m_strTemp.Substring(0,ndx);
+ ch = m_strTemp[ndx + 1];
+ if ( ch == DO || ch == DONT || ch == WILL || ch == WONT )
+ {
+ m_strOption = m_strTemp.Substring(ndx, 3);
+ string txt = m_strTemp.Substring(ndx + 3);
+ m_DISPLAYTEXT+= m_strTemp.Substring(0,ndx);
+ m_ListOptions.Add(m_strOption);
+ m_strTemp = txt ;
+ }
+ else
+ if ( ch == IAC)
+ {
+ m_DISPLAYTEXT= m_strTemp.Substring(0,ndx);
+ m_strTemp = m_strTemp.Substring(ndx + 1);
+ }
+ else
+ if ( ch == SB )
+ {
+ m_DISPLAYTEXT= m_strTemp.Substring(0,ndx);
+ ldx = m_strTemp.IndexOf(Convert.ToString(SE));
+ m_strOption = m_strTemp.Substring(ndx, ldx);
+ m_ListOptions.Add(m_strOption);
+ m_strTemp = m_strTemp.Substring(ldx);
+ }
+ }
+ else
+ {
+ m_DISPLAYTEXT = m_DISPLAYTEXT + m_strTemp;
+ bScanDone = true ;
+ }
+ }
+ m_strNormalText = m_DISPLAYTEXT;
+ }
+ catch(Exception eP)
+ {
+ MessageBox.Show(eP.Message , "Application Error!!!" , MessageBoxButtons.OK , MessageBoxIcon.Stop );
+ Application.Exit();
+ }
+ return m_strNormalText ;
+ }
+
+ void DispatchMessage(string strText)
+ {
+ try
+ {
+ Byte[] smk = new Byte[strText.Length];
+ for ( int i=0; i < strText.Length ; i++)
+ {
+ Byte ss = Convert.ToByte(strText[i]);
+ smk[i] = ss ;
+ }
+
+ IAsyncResult ar2 = s.BeginSend(smk , 0 , smk.Length , SocketFlags.None , callbackProc , s );
+ s.EndSend(ar2);
+ }
+ catch(Exception ers)
+ {
+ MessageBox.Show("ERROR IN RESPOND OPTIONS");
+ }
+ }
+
+ void RespondToOptions()
+ {
+ try
+ {
+ string strOption;
+ for ( int i=0; i < m_ListOptions.Count; i++)
+ {
+ strOption = (string)m_ListOptions[i];
+ ArrangeReply(strOption);
+ }
+ DispatchMessage(m_strResp);
+ m_strResp ="";
+ m_ListOptions.Clear();
+ }
+ catch(Exception ers)
+ {
+ MessageBox.Show("ERROR IN RESPOND OPTIONS");
+ }
+ }
+ void ArrangeReply(string strOption)
+ {
+ try
+ {
+
+ Char Verb;
+ Char Option;
+ Char Modifier;
+ Char ch;
+ bool bDefined = false;
+
+ if(strOption.Length < 3) return;
+
+ Verb = strOption[1];
+ Option = strOption[2];
+
+ if ( Option == 1 || Option == 3 )
+ {
+ // case 1: // Echo
+ // case 3: // Suppress Go-Ahead
+ bDefined = true;
+ // break;
+ }
+
+ m_strResp += IAC;
+
+ if(bDefined == true )
+ {
+ if ( Verb == DO )
+ {
+ // case DO:
+ ch = WILL;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ if ( Verb == DONT )
+ {
+ ch = WONT;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ if ( Verb == WILL )
+ {
+ ch = DO;
+ m_strResp += ch;
+ m_strResp += Option;
+ //break;
+ }
+ if ( Verb == WONT)
+ {
+ ch = DONT;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ if ( Verb == SB)
+ {
+ Modifier = strOption[3];
+ if(Modifier == SEND)
+ {
+ ch = SB;
+ m_strResp += ch;
+ m_strResp += Option;
+ m_strResp += IS;
+ m_strResp += IAC;
+ m_strResp += SE;
+ }
+ // break;
+ }
+ }
+ else
+ {
+ // switch(Verb)
+ // {
+ if ( Verb == DO )
+ {
+ ch = WONT;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ if ( Verb == DONT)
+ {
+ ch = WONT;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ if ( Verb == WILL)
+ {
+ ch = DONT;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ if ( Verb == WONT)
+ {
+ ch = DONT;
+ m_strResp += ch;
+ m_strResp += Option;
+ // break;
+ }
+ }
+ }
+ catch(Exception eeeee )
+ {
+ MessageBox.Show(eeeee.Message , "Application Error!!!" , MessageBoxButtons.OK , MessageBoxIcon.Stop );
+ Application.Exit();
+ }
+
+ }
+
+ private void textBox1_KeyPress_1(object sender, System.Windows.Forms.KeyPressEventArgs e)
+ {
+ if ( e.KeyChar == 13 )
+ {
+ DispatchMessage("\r\n");
+ }
+ else
+ if ( e.KeyChar == 8 )
+ {
+ try
+ {
+// string mtmp = textBox1.Text.Substring(0,textBox1.Text.Length-1);
+// textBox1.Text = "" ;
+ }
+ catch(Exception ebs)
+ {
+ MessageBox.Show("ERROR IN BACKSPACE");
+ }
+ }
+ else
+ {
+ string str = e.KeyChar.ToString();
+ DispatchMessage(str);
+ }
+ }
+
+
+ }
+}
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Computer.cls b/storage/ndb/src/cw/cpcc-win32/vb6/Computer.cls
new file mode 100644
index 00000000000..5b42dfeadb6
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Computer.cls
@@ -0,0 +1,20 @@
+VERSION 1.0 CLASS
+BEGIN
+ MultiUse = -1 'True
+ Persistable = 0 'NotPersistable
+ DataBindingBehavior = 0 'vbNone
+ DataSourceBehavior = 0 'vbNone
+ MTSTransactionMode = 0 'NotAnMTSObject
+END
+Attribute VB_Name = "Computer"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = True
+Attribute VB_PredeclaredId = False
+Attribute VB_Exposed = False
+Attribute VB_Ext_KEY = "SavedWithClassBuilder6" ,"Yes"
+Attribute VB_Ext_KEY = "Top_Level" ,"Yes"
+Public m_ip As String
+Public m_name As String
+Public m_status As String
+Public m_processes As Collection
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Database.cls b/storage/ndb/src/cw/cpcc-win32/vb6/Database.cls
new file mode 100644
index 00000000000..dfb1195d910
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Database.cls
@@ -0,0 +1,18 @@
+VERSION 1.0 CLASS
+BEGIN
+ MultiUse = -1 'True
+ Persistable = 0 'NotPersistable
+ DataBindingBehavior = 0 'vbNone
+ DataSourceBehavior = 0 'vbNone
+ MTSTransactionMode = 0 'NotAnMTSObject
+END
+Attribute VB_Name = "Database_"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = True
+Attribute VB_PredeclaredId = False
+Attribute VB_Exposed = False
+Attribute VB_Ext_KEY = "SavedWithClassBuilder6" ,"Yes"
+Attribute VB_Ext_KEY = "Top_Level" ,"Yes"
+Public m_name As String
+Public m_processes As Collection
+Public m_status As String
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 110.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 110.ico
new file mode 100644
index 00000000000..34b85992394
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 110.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 231.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 231.ico
new file mode 100644
index 00000000000..fe30ff5d1e6
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 231.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 237.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 237.ico
new file mode 100644
index 00000000000..af0a1294f9e
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 237.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 241.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 241.ico
new file mode 100644
index 00000000000..e8caf6e9a73
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 241.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 242.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 242.ico
new file mode 100644
index 00000000000..2deff5472bc
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 242.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 270.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 270.ico
new file mode 100644
index 00000000000..9cab239de23
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 270.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 271.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 271.ico
new file mode 100644
index 00000000000..f05c95f74fe
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 271.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 273.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 273.ico
new file mode 100644
index 00000000000..800606eda0c
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 273.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 31.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 31.ico
new file mode 100644
index 00000000000..a2404977771
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 31.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 337.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 337.ico
new file mode 100644
index 00000000000..9dadb12cfbe
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 337.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 338.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 338.ico
new file mode 100644
index 00000000000..a13c80c81b4
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 338.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Icon 339.ico b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 339.ico
new file mode 100644
index 00000000000..5eb4c06815d
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Icon 339.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/MSSCCPRJ.SCC b/storage/ndb/src/cw/cpcc-win32/vb6/MSSCCPRJ.SCC
new file mode 100644
index 00000000000..3100640f8bd
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/MSSCCPRJ.SCC
@@ -0,0 +1,5 @@
+[SCC]
+SCC=This is a source code control file
+[NdbCPC.vbp]
+SCC_Project_Name=this project is not under source code control
+SCC_Aux_Path=<This is an empty string for the mssccprj.scc file>
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Module1.bas b/storage/ndb/src/cw/cpcc-win32/vb6/Module1.bas
new file mode 100644
index 00000000000..ae8ed444a41
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Module1.bas
@@ -0,0 +1,233 @@
+Attribute VB_Name = "Module1"
+Option Explicit
+Public fMainForm As frmMain
+Public g_computers As New Collection
+Public g_databases As New Collection
+
+Sub Main()
+ If False Then
+ Dim fLogin As New frmLogin
+ fLogin.Show vbModal
+ If Not fLogin.OK Then
+ 'Login Failed so exit app
+ End
+ End If
+ Unload fLogin
+
+ frmSplash.Show
+ frmSplash.Refresh
+ End If
+
+ init
+
+ Set fMainForm = New frmMain
+ Load fMainForm
+ Unload frmSplash
+
+ fMainForm.Show
+End Sub
+
+Private Sub init()
+ Dim c As Computer
+ Dim p As Process
+
+ ' ---
+ ' One node configuration
+ '
+ Set c = New Computer
+ With c
+ .m_ip = "130.100.232.31"
+ .m_name = "ndb-client31"
+ .m_status = "Connected"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+ Set p = New Process
+ With p
+ .m_id = "1"
+ .m_name = "mgm-1"
+ .m_database = "elathal"
+ .m_status = "Running"
+ .m_owner = "elathal"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set p = New Process
+ With p
+ .m_id = "2"
+ .m_name = "ndb-2"
+ .m_database = "elathal"
+ .m_status = "Running"
+ .m_owner = "elathal"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set p = New Process
+ With p
+ .m_id = "3"
+ .m_name = "api-3"
+ .m_database = "elathal"
+ .m_status = "Running"
+ .m_owner = "elathal"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ ' ---
+ ' Two node configuration
+ '
+ Set p = New Process
+ With p
+ .m_id = "4"
+ .m_name = "mgm-1"
+ .m_database = "ejonore-2-node"
+ .m_status = "Running"
+ .m_owner = "ejonore"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set c = New Computer
+ With c
+ .m_ip = "10.0.1.1"
+ .m_name = "cluster-1"
+ .m_status = "Connected"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+ Set p = New Process
+ With p
+ .m_id = "1"
+ .m_name = "ndb-2"
+ .m_database = "ejonore-2-node"
+ .m_status = "Running"
+ .m_owner = "ejonore"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set c = New Computer
+ With c
+ .m_ip = "10.0.2.1"
+ .m_name = "cluster-2"
+ .m_status = "Connected"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+ Set p = New Process
+ With p
+ .m_id = "1"
+ .m_name = "ndb-3"
+ .m_database = "ejonore-2-node"
+ .m_status = "Running"
+ .m_owner = "ejonore"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set c = New Computer
+ With c
+ .m_ip = "10.0.3.1"
+ .m_name = "cluster-3"
+ .m_status = "Connected"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+ Set p = New Process
+ With p
+ .m_id = "1"
+ .m_name = "api-4"
+ .m_database = "ejonore-2-node"
+ .m_status = "Running"
+ .m_owner = "ejonore"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set c = New Computer
+ With c
+ .m_ip = "10.0.4.1"
+ .m_name = "cluster-4"
+ .m_status = "Connected"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+ Set p = New Process
+ With p
+ .m_id = "1"
+ .m_name = "api-5"
+ .m_database = "ejonore-2-node"
+ .m_status = "Running"
+ .m_owner = "ejonore"
+ Set .m_computer = c
+ End With
+ addProcess c, p
+
+ Set c = New Computer
+ With c
+ .m_ip = "130.100.232.5"
+ .m_name = "ndbs05"
+ .m_status = "Not connected"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+ Set c = New Computer
+ With c
+ .m_ip = "130.100.232.7"
+ .m_name = "ndb-srv7"
+ .m_status = "No contact"
+ Set .m_processes = New Collection
+ End With
+ addComputer c
+
+End Sub
+
+Public Sub addComputer(ByRef c As Computer)
+ g_computers.Add c, "_" & c.m_name
+End Sub
+
+Private Sub addProcess(ByRef c As Computer, ByRef p As Process)
+ c.m_processes.Add p, "_" & p.m_id
+
+ Dim cl As Database_
+ If Not Exists(g_databases, "_" & p.m_database) Then
+ Set cl = New Database_
+ With cl
+ .m_name = p.m_database
+ .m_status = "Unknown"
+ Set .m_processes = New Collection
+ End With
+ g_databases.Add cl, "_" & p.m_database
+ Else
+ Set cl = g_databases("_" & p.m_database)
+ End If
+ cl.m_processes.Add p, "_" & p.m_computer.m_name & "_" & p.m_id
+End Sub
+
+Public Function Exists(ByRef c As Collection, ByVal k As String) As Boolean
+ Dim r As Boolean
+ Dim o As Object
+
+ r = True
+
+ On Error GoTo NotFound
+ Set o = c.Item(k)
+ GoTo Continue
+NotFound:
+ If Err.Number <> 5 Then
+ Err.Raise Err.Number, Err.Source, Err.Description
+ End If
+
+ r = False
+Continue:
+ Exists = r
+End Function
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbp b/storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbp
new file mode 100644
index 00000000000..dc8f3780a74
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbp
@@ -0,0 +1,49 @@
+Type=Exe
+Object={831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0; mscomctl.ocx
+Module=Module1; Module1.bas
+Form=frmMain.frm
+Form=frmSplash.frm
+Form=frmLogin.frm
+Form=frmOptions.frm
+Form=frmAbout.frm
+Class=Computer; Computer.cls
+Class=Process; Process.cls
+Class=Database_; Database.cls
+Form=frmNewComputer.frm
+Form=frmNewDatabase3.frm
+Form=frmNewDatabase1.frm
+Form=frmNewDatabase2.frm
+IconForm="frmAbout"
+Startup="Sub Main"
+HelpFile=""
+Title="NdbCPC"
+ExeName32="NdbCPC.exe"
+Command32=""
+Name="NdbCPC"
+HelpContextID="0"
+CompatibleMode="0"
+MajorVer=1
+MinorVer=0
+RevisionVer=0
+AutoIncrementVer=0
+ServerSupportFiles=0
+VersionCompanyName="ctp"
+CompilationType=0
+OptimizationType=0
+FavorPentiumPro(tm)=0
+CodeViewDebugInfo=0
+NoAliasing=0
+BoundsCheck=0
+OverflowCheck=0
+FlPointCheck=0
+FDIVCheck=0
+UnroundedFP=0
+StartMode=0
+Unattended=0
+Retained=0
+ThreadPerObject=0
+MaxNumberOfThreads=1
+DebugStartupOption=0
+
+[MS Transaction Server]
+AutoRefresh=1
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbw b/storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbw
new file mode 100644
index 00000000000..825abbc923a
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbw
@@ -0,0 +1,13 @@
+Module1 = 44, 44, 577, 492,
+frmMain = 44, 44, 577, 492, , 66, 66, 599, 514, C
+frmSplash = 132, 132, 670, 576, C, 88, 88, 621, 536, C
+frmLogin = 0, 0, 538, 444, C, 110, 110, 643, 558, C
+frmOptions = 176, 176, 714, 620, C, 132, 132, 665, 580, C
+frmAbout = 132, 132, 759, 511, C, 154, 154, 687, 602, C
+Computer = 110, 110, 648, 554,
+Process = 132, 132, 670, 576, C
+Database_ = 88, 88, 626, 532, C
+frmNewComputer = 44, 44, 582, 488, , 22, 22, 390, 218, C
+frmNewDatabase3 = 0, 0, 506, 444, , 0, 0, 506, 444, C
+frmNewDatabase1 = 132, 132, 638, 550, , 154, 154, 660, 572, C
+frmNewDatabase2 = 198, 198, 704, 616, , 176, 176, 682, 594, C
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/Process.cls b/storage/ndb/src/cw/cpcc-win32/vb6/Process.cls
new file mode 100644
index 00000000000..fcb4c2cbb2c
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/Process.cls
@@ -0,0 +1,22 @@
+VERSION 1.0 CLASS
+BEGIN
+ MultiUse = -1 'True
+ Persistable = 0 'NotPersistable
+ DataBindingBehavior = 0 'vbNone
+ DataSourceBehavior = 0 'vbNone
+ MTSTransactionMode = 0 'NotAnMTSObject
+END
+Attribute VB_Name = "Process"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = True
+Attribute VB_PredeclaredId = False
+Attribute VB_Exposed = False
+Attribute VB_Ext_KEY = "SavedWithClassBuilder6" ,"Yes"
+Attribute VB_Ext_KEY = "Top_Level" ,"Yes"
+Public m_computer As Computer
+Public m_id As String
+Public m_name As String
+Public m_database As String
+Public m_status As String
+Public m_owner As String
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/closed folder.ico b/storage/ndb/src/cw/cpcc-win32/vb6/closed folder.ico
new file mode 100644
index 00000000000..fe82350d376
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/closed folder.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/computer.ico b/storage/ndb/src/cw/cpcc-win32/vb6/computer.ico
new file mode 100644
index 00000000000..d73302d1cd5
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/computer.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmAbout.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmAbout.frm
new file mode 100644
index 00000000000..b842d20de21
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmAbout.frm
@@ -0,0 +1,245 @@
+VERSION 5.00
+Begin VB.Form frmAbout
+ BorderStyle = 3 'Fixed Dialog
+ Caption = "About NdbCPC"
+ ClientHeight = 3630
+ ClientLeft = 45
+ ClientTop = 330
+ ClientWidth = 5865
+ ClipControls = 0 'False
+ LinkTopic = "Form1"
+ MaxButton = 0 'False
+ MinButton = 0 'False
+ ScaleHeight = 3630
+ ScaleWidth = 5865
+ ShowInTaskbar = 0 'False
+ StartUpPosition = 1 'CenterOwner
+ Tag = "About NdbCPC"
+ Begin VB.PictureBox picIcon
+ AutoSize = -1 'True
+ BackColor = &H00C0C0C0&
+ ClipControls = 0 'False
+ Height = 540
+ Left = 240
+ Picture = "frmAbout.frx":0000
+ ScaleHeight = 480
+ ScaleMode = 0 'User
+ ScaleWidth = 480
+ TabIndex = 2
+ TabStop = 0 'False
+ Top = 240
+ Width = 540
+ End
+ Begin VB.CommandButton cmdOK
+ Cancel = -1 'True
+ Caption = "OK"
+ Default = -1 'True
+ Height = 345
+ Left = 4245
+ TabIndex = 0
+ Tag = "OK"
+ Top = 2625
+ Width = 1467
+ End
+ Begin VB.CommandButton cmdSysInfo
+ Caption = "&System Info..."
+ Height = 345
+ Left = 4260
+ TabIndex = 1
+ Tag = "&System Info..."
+ Top = 3075
+ Width = 1452
+ End
+ Begin VB.Label lblDescription
+ Caption = "App Description"
+ ForeColor = &H00000000&
+ Height = 1170
+ Left = 1050
+ TabIndex = 6
+ Tag = "App Description"
+ Top = 1125
+ Width = 4092
+ End
+ Begin VB.Label lblTitle
+ Caption = "Application Title"
+ ForeColor = &H00000000&
+ Height = 480
+ Left = 1050
+ TabIndex = 5
+ Tag = "Application Title"
+ Top = 240
+ Width = 4092
+ End
+ Begin VB.Line Line1
+ BorderColor = &H00808080&
+ BorderStyle = 6 'Inside Solid
+ Index = 1
+ X1 = 225
+ X2 = 5657
+ Y1 = 2430
+ Y2 = 2430
+ End
+ Begin VB.Line Line1
+ BorderColor = &H00FFFFFF&
+ BorderWidth = 2
+ Index = 0
+ X1 = 240
+ X2 = 5657
+ Y1 = 2445
+ Y2 = 2445
+ End
+ Begin VB.Label lblVersion
+ Caption = "Version"
+ Height = 225
+ Left = 1050
+ TabIndex = 4
+ Tag = "Version"
+ Top = 780
+ Width = 4092
+ End
+ Begin VB.Label lblDisclaimer
+ Caption = "Warning: ..."
+ ForeColor = &H00000000&
+ Height = 825
+ Left = 255
+ TabIndex = 3
+ Tag = "Warning: ..."
+ Top = 2625
+ Width = 3870
+ End
+End
+Attribute VB_Name = "frmAbout"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+' Reg Key Security Options...
+Const KEY_ALL_ACCESS = &H2003F
+
+
+' Reg Key ROOT Types...
+Const HKEY_LOCAL_MACHINE = &H80000002
+Const ERROR_SUCCESS = 0
+Const REG_SZ = 1 ' Unicode nul terminated string
+Const REG_DWORD = 4 ' 32-bit number
+
+
+Const gREGKEYSYSINFOLOC = "SOFTWARE\Microsoft\Shared Tools Location"
+Const gREGVALSYSINFOLOC = "MSINFO"
+Const gREGKEYSYSINFO = "SOFTWARE\Microsoft\Shared Tools\MSINFO"
+Const gREGVALSYSINFO = "PATH"
+
+
+Private Declare Function RegOpenKeyEx Lib "advapi32" Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, ByRef phkResult As Long) As Long
+Private Declare Function RegQueryValueEx Lib "advapi32" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, ByRef lpType As Long, ByVal lpData As String, ByRef lpcbData As Long) As Long
+Private Declare Function RegCloseKey Lib "advapi32" (ByVal hKey As Long) As Long
+
+Private Sub Form_Load()
+ lblVersion.Caption = "Version " & App.Major & "." & App.Minor & "." & App.Revision
+ lblTitle.Caption = App.Title
+End Sub
+
+
+
+Private Sub cmdSysInfo_Click()
+ Call StartSysInfo
+End Sub
+
+
+Private Sub cmdOK_Click()
+ Unload Me
+End Sub
+
+
+Public Sub StartSysInfo()
+ On Error GoTo SysInfoErr
+
+
+ Dim rc As Long
+ Dim SysInfoPath As String
+
+
+ ' Try To Get System Info Program Path\Name From Registry...
+ If GetKeyValue(HKEY_LOCAL_MACHINE, gREGKEYSYSINFO, gREGVALSYSINFO, SysInfoPath) Then
+ ' Try To Get System Info Program Path Only From Registry...
+ ElseIf GetKeyValue(HKEY_LOCAL_MACHINE, gREGKEYSYSINFOLOC, gREGVALSYSINFOLOC, SysInfoPath) Then
+ ' Validate Existance Of Known 32 Bit File Version
+ If (Dir(SysInfoPath & "\MSINFO32.EXE") <> "") Then
+ SysInfoPath = SysInfoPath & "\MSINFO32.EXE"
+
+
+ ' Error - File Can Not Be Found...
+ Else
+ GoTo SysInfoErr
+ End If
+ ' Error - Registry Entry Can Not Be Found...
+ Else
+ GoTo SysInfoErr
+ End If
+
+
+ Call Shell(SysInfoPath, vbNormalFocus)
+
+
+ Exit Sub
+SysInfoErr:
+ MsgBox "System Information Is Unavailable At This Time", vbOKOnly
+End Sub
+
+
+Public Function GetKeyValue(KeyRoot As Long, KeyName As String, SubKeyRef As String, ByRef KeyVal As String) As Boolean
+ Dim i As Long ' Loop Counter
+ Dim rc As Long ' Return Code
+ Dim hKey As Long ' Handle To An Open Registry Key
+ Dim hDepth As Long '
+ Dim KeyValType As Long ' Data Type Of A Registry Key
+ Dim tmpVal As String ' Tempory Storage For A Registry Key Value
+ Dim KeyValSize As Long ' Size Of Registry Key Variable
+ '------------------------------------------------------------
+ ' Open RegKey Under KeyRoot {HKEY_LOCAL_MACHINE...}
+ '------------------------------------------------------------
+ rc = RegOpenKeyEx(KeyRoot, KeyName, 0, KEY_ALL_ACCESS, hKey) ' Open Registry Key
+
+
+ If (rc <> ERROR_SUCCESS) Then GoTo GetKeyError ' Handle Error...
+
+
+ tmpVal = String$(1024, 0) ' Allocate Variable Space
+ KeyValSize = 1024 ' Mark Variable Size
+
+
+ '------------------------------------------------------------
+ ' Retrieve Registry Key Value...
+ '------------------------------------------------------------
+ rc = RegQueryValueEx(hKey, SubKeyRef, 0, KeyValType, tmpVal, KeyValSize) ' Get/Create Key Value
+
+
+ If (rc <> ERROR_SUCCESS) Then GoTo GetKeyError ' Handle Errors
+
+
+ tmpVal = VBA.Left(tmpVal, InStr(tmpVal, VBA.Chr(0)) - 1)
+ '------------------------------------------------------------
+ ' Determine Key Value Type For Conversion...
+ '------------------------------------------------------------
+ Select Case KeyValType ' Search Data Types...
+ Case REG_SZ ' String Registry Key Data Type
+ KeyVal = tmpVal ' Copy String Value
+ Case REG_DWORD ' Double Word Registry Key Data Type
+ For i = Len(tmpVal) To 1 Step -1 ' Convert Each Bit
+ KeyVal = KeyVal + Hex(Asc(Mid(tmpVal, i, 1))) ' Build Value Char. By Char.
+ Next
+ KeyVal = Format$("&h" + KeyVal) ' Convert Double Word To String
+ End Select
+
+
+ GetKeyValue = True ' Return Success
+ rc = RegCloseKey(hKey) ' Close Registry Key
+ Exit Function ' Exit
+
+
+GetKeyError: ' Cleanup After An Error Has Occured...
+ KeyVal = "" ' Set Return Val To Empty String
+ GetKeyValue = False ' Return Failure
+ rc = RegCloseKey(hKey) ' Close Registry Key
+End Function
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmLogin.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmLogin.frm
new file mode 100644
index 00000000000..d4d663c93c2
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmLogin.frm
@@ -0,0 +1,119 @@
+VERSION 5.00
+Begin VB.Form frmLogin
+ BorderStyle = 3 'Fixed Dialog
+ Caption = "Login"
+ ClientHeight = 1590
+ ClientLeft = 45
+ ClientTop = 330
+ ClientWidth = 3750
+ LinkTopic = "Form1"
+ MaxButton = 0 'False
+ MinButton = 0 'False
+ ScaleHeight = 1590
+ ScaleWidth = 3750
+ ShowInTaskbar = 0 'False
+ StartUpPosition = 2 'CenterScreen
+ Tag = "Login"
+ Begin VB.CommandButton cmdCancel
+ Cancel = -1 'True
+ Caption = "Cancel"
+ Height = 360
+ Left = 2100
+ TabIndex = 5
+ Tag = "Cancel"
+ Top = 1020
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdOK
+ Caption = "OK"
+ Default = -1 'True
+ Height = 360
+ Left = 495
+ TabIndex = 4
+ Tag = "OK"
+ Top = 1020
+ Width = 1140
+ End
+ Begin VB.TextBox txtPassword
+ Height = 285
+ IMEMode = 3 'DISABLE
+ Left = 1305
+ PasswordChar = "*"
+ TabIndex = 1
+ Top = 525
+ Width = 2325
+ End
+ Begin VB.TextBox txtUserName
+ Height = 285
+ Left = 1305
+ TabIndex = 3
+ Top = 135
+ Width = 2325
+ End
+ Begin VB.Label lblLabels
+ Caption = "&Password:"
+ Height = 248
+ Index = 1
+ Left = 105
+ TabIndex = 0
+ Tag = "&Password:"
+ Top = 540
+ Width = 1080
+ End
+ Begin VB.Label lblLabels
+ Caption = "&User Name:"
+ Height = 248
+ Index = 0
+ Left = 105
+ TabIndex = 2
+ Tag = "&User Name:"
+ Top = 150
+ Width = 1080
+ End
+End
+Attribute VB_Name = "frmLogin"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpbuffer As String, nSize As Long) As Long
+
+
+Public OK As Boolean
+Private Sub Form_Load()
+ Dim sBuffer As String
+ Dim lSize As Long
+
+
+ sBuffer = Space$(255)
+ lSize = Len(sBuffer)
+ Call GetUserName(sBuffer, lSize)
+ If lSize > 0 Then
+ txtUserName.Text = Left$(sBuffer, lSize)
+ Else
+ txtUserName.Text = vbNullString
+ End If
+End Sub
+
+
+
+Private Sub cmdCancel_Click()
+ OK = False
+ Me.Hide
+End Sub
+
+
+Private Sub cmdOK_Click()
+ 'ToDo: create test for correct password
+ 'check for correct password
+ If txtPassword.Text = "" Then
+ OK = True
+ Me.Hide
+ Else
+ MsgBox "Invalid Password, try again!", , "Login"
+ txtPassword.SetFocus
+ txtPassword.SelStart = 0
+ txtPassword.SelLength = Len(txtPassword.Text)
+ End If
+End Sub
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmMain.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmMain.frm
new file mode 100644
index 00000000000..a4bf5b58941
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmMain.frm
@@ -0,0 +1,1207 @@
+VERSION 5.00
+Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "mscomctl.ocx"
+Begin VB.Form frmMain
+ Caption = "NdbCPC"
+ ClientHeight = 5955
+ ClientLeft = 2115
+ ClientTop = 2250
+ ClientWidth = 8880
+ LinkTopic = "Form1"
+ ScaleHeight = 5955
+ ScaleWidth = 8880
+ Begin MSComctlLib.ImageList ImageList1
+ Left = 6840
+ Top = 3120
+ _ExtentX = 1005
+ _ExtentY = 1005
+ BackColor = 16777215
+ ImageWidth = 16
+ ImageHeight = 16
+ MaskColor = 12632256
+ _Version = 393216
+ BeginProperty Images {2C247F25-8591-11D1-B16A-00C0F0283628}
+ NumListImages = 11
+ BeginProperty ListImage1 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":0000
+ Key = "close"
+ EndProperty
+ BeginProperty ListImage2 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":27B4
+ Key = "open"
+ EndProperty
+ BeginProperty ListImage3 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":4F68
+ Key = "computer_unknown"
+ EndProperty
+ BeginProperty ListImage4 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":5284
+ Key = "computer_stopped"
+ EndProperty
+ BeginProperty ListImage5 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":55A0
+ Key = "computer_started"
+ EndProperty
+ BeginProperty ListImage6 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":58BC
+ Key = ""
+ EndProperty
+ BeginProperty ListImage7 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":5BD8
+ Key = ""
+ EndProperty
+ BeginProperty ListImage8 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":5EF4
+ Key = ""
+ EndProperty
+ BeginProperty ListImage9 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":6210
+ Key = "db"
+ EndProperty
+ BeginProperty ListImage10 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":652A
+ Key = "computer"
+ EndProperty
+ BeginProperty ListImage11 {2C247F27-8591-11D1-B16A-00C0F0283628}
+ Picture = "frmMain.frx":6844
+ Key = "properties"
+ EndProperty
+ EndProperty
+ End
+ Begin VB.PictureBox picSplitter
+ BackColor = &H00808080&
+ BorderStyle = 0 'None
+ FillColor = &H00808080&
+ Height = 4800
+ Left = 5400
+ ScaleHeight = 2090.126
+ ScaleMode = 0 'User
+ ScaleWidth = 780
+ TabIndex = 6
+ Top = 705
+ Width = 72
+ Visible = 0 'False
+ End
+ Begin MSComctlLib.TreeView tvTreeView
+ Height = 4800
+ Left = 0
+ TabIndex = 5
+ Top = 705
+ Width = 2016
+ _ExtentX = 3545
+ _ExtentY = 8467
+ _Version = 393217
+ HideSelection = 0 'False
+ Indentation = 0
+ LineStyle = 1
+ Sorted = -1 'True
+ Style = 7
+ FullRowSelect = -1 'True
+ ImageList = "ImageList1"
+ Appearance = 1
+ End
+ Begin VB.PictureBox picTitles
+ Align = 1 'Align Top
+ Appearance = 0 'Flat
+ BorderStyle = 0 'None
+ ForeColor = &H80000008&
+ Height = 300
+ Left = 0
+ ScaleHeight = 300
+ ScaleWidth = 8880
+ TabIndex = 2
+ TabStop = 0 'False
+ Top = 420
+ Width = 8880
+ Begin VB.Label lblTitle
+ BorderStyle = 1 'Fixed Single
+ Caption = " ListView:"
+ Height = 270
+ Index = 1
+ Left = 2078
+ TabIndex = 4
+ Tag = " ListView:"
+ Top = 12
+ Width = 3216
+ End
+ Begin VB.Label lblTitle
+ BorderStyle = 1 'Fixed Single
+ Caption = " TreeView:"
+ Height = 270
+ Index = 0
+ Left = 0
+ TabIndex = 3
+ Tag = " TreeView:"
+ Top = 12
+ Width = 2016
+ End
+ End
+ Begin MSComctlLib.Toolbar tbToolBar
+ Align = 1 'Align Top
+ Height = 420
+ Left = 0
+ TabIndex = 1
+ Top = 0
+ Width = 8880
+ _ExtentX = 15663
+ _ExtentY = 741
+ ButtonWidth = 609
+ ButtonHeight = 582
+ Appearance = 1
+ ImageList = "ImageList1"
+ _Version = 393216
+ BeginProperty Buttons {66833FE8-8583-11D1-B16A-00C0F0283628}
+ NumButtons = 5
+ BeginProperty Button1 {66833FEA-8583-11D1-B16A-00C0F0283628}
+ Style = 3
+ EndProperty
+ BeginProperty Button2 {66833FEA-8583-11D1-B16A-00C0F0283628}
+ Key = "Add computer"
+ Object.ToolTipText = "Add computer"
+ ImageKey = "computer"
+ EndProperty
+ BeginProperty Button3 {66833FEA-8583-11D1-B16A-00C0F0283628}
+ Key = "New database"
+ Object.ToolTipText = "New database"
+ ImageKey = "db"
+ EndProperty
+ BeginProperty Button4 {66833FEA-8583-11D1-B16A-00C0F0283628}
+ Style = 3
+ EndProperty
+ BeginProperty Button5 {66833FEA-8583-11D1-B16A-00C0F0283628}
+ Key = "Properties"
+ Object.ToolTipText = "Properties"
+ ImageKey = "properties"
+ EndProperty
+ EndProperty
+ End
+ Begin MSComctlLib.StatusBar sbStatusBar
+ Align = 2 'Align Bottom
+ Height = 270
+ Left = 0
+ TabIndex = 0
+ Top = 5685
+ Width = 8880
+ _ExtentX = 15663
+ _ExtentY = 476
+ _Version = 393216
+ BeginProperty Panels {8E3867A5-8586-11D1-B16A-00C0F0283628}
+ NumPanels = 3
+ BeginProperty Panel1 {8E3867AB-8586-11D1-B16A-00C0F0283628}
+ AutoSize = 1
+ Object.Width = 10028
+ Text = "Status"
+ TextSave = "Status"
+ EndProperty
+ BeginProperty Panel2 {8E3867AB-8586-11D1-B16A-00C0F0283628}
+ Style = 6
+ AutoSize = 2
+ TextSave = "2002-10-15"
+ EndProperty
+ BeginProperty Panel3 {8E3867AB-8586-11D1-B16A-00C0F0283628}
+ Style = 5
+ AutoSize = 2
+ TextSave = "09:44"
+ EndProperty
+ EndProperty
+ End
+ Begin MSComctlLib.ListView lvProcesses
+ Height = 4815
+ Left = 2040
+ TabIndex = 8
+ Top = 720
+ Width = 3255
+ _ExtentX = 5741
+ _ExtentY = 8493
+ Sorted = -1 'True
+ MultiSelect = -1 'True
+ LabelWrap = -1 'True
+ HideSelection = 0 'False
+ AllowReorder = -1 'True
+ FullRowSelect = -1 'True
+ _Version = 393217
+ ForeColor = -2147483640
+ BackColor = -2147483643
+ BorderStyle = 1
+ Appearance = 1
+ NumItems = 6
+ BeginProperty ColumnHeader(1) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ Key = "Id"
+ Text = "Id"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(2) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 1
+ Key = "Computer"
+ Text = "Computer"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(3) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 2
+ Key = "Database"
+ Text = "Database"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(4) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 3
+ Key = "Name"
+ Text = "Name"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(5) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 4
+ Key = "Status"
+ Text = "Status"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(6) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 5
+ Key = "Owner"
+ Text = "Owner"
+ Object.Width = 2540
+ EndProperty
+ End
+ Begin MSComctlLib.ListView lvComputers
+ Height = 4815
+ Left = 2040
+ TabIndex = 7
+ Top = 720
+ Width = 3255
+ _ExtentX = 5741
+ _ExtentY = 8493
+ Sorted = -1 'True
+ MultiSelect = -1 'True
+ LabelWrap = -1 'True
+ HideSelection = -1 'True
+ AllowReorder = -1 'True
+ FullRowSelect = -1 'True
+ _Version = 393217
+ Icons = "ImageList1"
+ SmallIcons = "ImageList1"
+ ForeColor = -2147483640
+ BackColor = -2147483643
+ BorderStyle = 1
+ Appearance = 1
+ NumItems = 2
+ BeginProperty ColumnHeader(1) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ Text = "Computer"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(2) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 1
+ Text = "Status"
+ Object.Width = 2540
+ EndProperty
+ End
+ Begin MSComctlLib.ListView lvDatabases
+ Height = 4815
+ Left = 2040
+ TabIndex = 9
+ Top = 720
+ Width = 3255
+ _ExtentX = 5741
+ _ExtentY = 8493
+ View = 3
+ Sorted = -1 'True
+ MultiSelect = -1 'True
+ LabelWrap = -1 'True
+ HideSelection = -1 'True
+ AllowReorder = -1 'True
+ FullRowSelect = -1 'True
+ _Version = 393217
+ Icons = "ImageList1"
+ SmallIcons = "ImageList1"
+ ForeColor = -2147483640
+ BackColor = -2147483643
+ BorderStyle = 1
+ Appearance = 1
+ NumItems = 2
+ BeginProperty ColumnHeader(1) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ Key = "Database"
+ Text = "Database"
+ Object.Width = 2540
+ EndProperty
+ BeginProperty ColumnHeader(2) {BDD1F052-858B-11D1-B16A-00C0F0283628}
+ SubItemIndex = 1
+ Key = "Status"
+ Text = "Status"
+ Object.Width = 2540
+ EndProperty
+ End
+ Begin VB.Image imgSplitter
+ Height = 4788
+ Left = 1965
+ MousePointer = 9 'Size W E
+ Top = 705
+ Width = 150
+ End
+ Begin VB.Menu mnuFile
+ Caption = "&File"
+ Begin VB.Menu mnuFileOpen
+ Caption = "&Open..."
+ End
+ Begin VB.Menu mnuFileFind
+ Caption = "&Find"
+ End
+ Begin VB.Menu mnuFileBar0
+ Caption = "-"
+ End
+ Begin VB.Menu mnuFileSendTo
+ Caption = "Sen&d to"
+ End
+ Begin VB.Menu mnuFileBar1
+ Caption = "-"
+ End
+ Begin VB.Menu mnuFileNew
+ Caption = "&New"
+ Shortcut = ^N
+ End
+ Begin VB.Menu mnuFileBar2
+ Caption = "-"
+ End
+ Begin VB.Menu mnuFileDelete
+ Caption = "&Delete"
+ End
+ Begin VB.Menu mnuFileRename
+ Caption = "Rena&me"
+ End
+ Begin VB.Menu mnuFileProperties
+ Caption = "Propert&ies"
+ End
+ Begin VB.Menu mnuFileBar3
+ Caption = "-"
+ End
+ Begin VB.Menu mnuFileMRU
+ Caption = ""
+ Index = 1
+ Visible = 0 'False
+ End
+ Begin VB.Menu mnuFileMRU
+ Caption = ""
+ Index = 2
+ Visible = 0 'False
+ End
+ Begin VB.Menu mnuFileMRU
+ Caption = ""
+ Index = 3
+ Visible = 0 'False
+ End
+ Begin VB.Menu mnuFileBar4
+ Caption = "-"
+ Visible = 0 'False
+ End
+ Begin VB.Menu mnuFileBar5
+ Caption = "-"
+ End
+ Begin VB.Menu mnuFileClose
+ Caption = "&Close"
+ End
+ End
+ Begin VB.Menu mnuEdit
+ Caption = "&Edit"
+ Begin VB.Menu mnuEditUndo
+ Caption = "&Undo"
+ End
+ Begin VB.Menu mnuEditBar0
+ Caption = "-"
+ End
+ Begin VB.Menu mnuEditCut
+ Caption = "Cu&t"
+ Shortcut = ^X
+ End
+ Begin VB.Menu mnuEditCopy
+ Caption = "&Copy"
+ Shortcut = ^C
+ End
+ Begin VB.Menu mnuEditPaste
+ Caption = "&Paste"
+ Shortcut = ^V
+ End
+ Begin VB.Menu mnuEditPasteSpecial
+ Caption = "Paste &Special..."
+ End
+ Begin VB.Menu mnuEditBar1
+ Caption = "-"
+ End
+ Begin VB.Menu mnuEditSelectAll
+ Caption = "Select &All"
+ Shortcut = ^A
+ End
+ Begin VB.Menu mnuEditInvertSelection
+ Caption = "&Invert Selection"
+ End
+ End
+ Begin VB.Menu mnuView
+ Caption = "&View"
+ Begin VB.Menu mnuViewToolbar
+ Caption = "&Toolbar"
+ Checked = -1 'True
+ End
+ Begin VB.Menu mnuViewStatusBar
+ Caption = "Status &Bar"
+ Checked = -1 'True
+ End
+ Begin VB.Menu mnuViewBar0
+ Caption = "-"
+ End
+ Begin VB.Menu mnuListViewMode
+ Caption = "Lar&ge Icons"
+ Index = 0
+ End
+ Begin VB.Menu mnuListViewMode
+ Caption = "S&mall Icons"
+ Index = 1
+ End
+ Begin VB.Menu mnuListViewMode
+ Caption = "&List"
+ Index = 2
+ End
+ Begin VB.Menu mnuListViewMode
+ Caption = "&Details"
+ Index = 3
+ End
+ Begin VB.Menu mnuViewBar1
+ Caption = "-"
+ End
+ Begin VB.Menu mnuViewArrangeIcons
+ Caption = "Arrange &Icons"
+ End
+ Begin VB.Menu mnuViewBar2
+ Caption = "-"
+ End
+ Begin VB.Menu mnuViewRefresh
+ Caption = "&Refresh"
+ End
+ Begin VB.Menu mnuViewOptions
+ Caption = "&Options..."
+ End
+ Begin VB.Menu mnuViewWebBrowser
+ Caption = "&Web Browser"
+ End
+ End
+ Begin VB.Menu mnuHelp
+ Caption = "&Help"
+ Begin VB.Menu mnuHelpContents
+ Caption = "&Contents"
+ End
+ Begin VB.Menu mnuHelpSearchForHelpOn
+ Caption = "&Search For Help On..."
+ End
+ Begin VB.Menu mnuHelpBar0
+ Caption = "-"
+ End
+ Begin VB.Menu mnuHelpAbout
+ Caption = "&About "
+ End
+ End
+ Begin VB.Menu mnuPopComputers
+ Caption = ""
+ Visible = 0 'False
+ Begin VB.Menu mnuPopAddComputer
+ Caption = "Add computer"
+ End
+ Begin VB.Menu mnuPop__
+ Caption = "-"
+ End
+ Begin VB.Menu mnuPopSortComputers
+ Caption = "Sorted"
+ End
+ End
+ Begin VB.Menu mnuPopDatabases
+ Caption = ""
+ Visible = 0 'False
+ Begin VB.Menu mnuPopNewDatabase
+ Caption = "New database"
+ End
+ Begin VB.Menu mnuPopSortDatabases0
+ Caption = "-"
+ End
+ Begin VB.Menu mnuPopSortDatabases
+ Caption = "Sorted"
+ End
+ End
+ Begin VB.Menu mnuPopComputer
+ Caption = ""
+ Visible = 0 'False
+ Begin VB.Menu mnuPopComputerName
+ Caption = "ComputerName"
+ Enabled = 0 'False
+ End
+ Begin VB.Menu mnuPopComputer0
+ Caption = "-"
+ End
+ Begin VB.Menu mnuPopConnectComputer
+ Caption = "Connect"
+ End
+ Begin VB.Menu mnuPopDisconnectComputer
+ Caption = "Disconnect"
+ End
+ Begin VB.Menu mnuPopRemoveComputer
+ Caption = "Remove"
+ End
+ Begin VB.Menu mnuComputer1
+ Caption = "-"
+ End
+ Begin VB.Menu mnuPopComputerProperties
+ Caption = "Properties"
+ End
+ End
+End
+Attribute VB_Name = "frmMain"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Option Explicit
+Private Declare Function OSWinHelp% Lib "user32" Alias "WinHelpA" (ByVal hwnd&, ByVal HelpFile$, ByVal wCommand%, dwData As Any)
+
+Dim mbMoving As Boolean
+Const sglSplitLimit = 500
+Dim m_currentNode As MSComctlLib.Node
+Dim m_currentList As ListView
+
+Dim m_currentView As Integer
+Dim m_computerWidth As Integer
+Dim m_databaseWidth As Integer
+
+Dim m_currentComputer As Computer
+Dim m_currentDatabase As Database_
+
+Private Sub Form_Load()
+ tvTreeView.Nodes.Clear
+ lvComputers.ListItems.Clear
+ lvProcesses.ListItems.Clear
+ lvDatabases.ListItems.Clear
+
+ Me.Left = GetSetting(App.Title, "Settings", "MainLeft", 1000)
+ Me.Top = GetSetting(App.Title, "Settings", "MainTop", 1000)
+ Me.Width = GetSetting(App.Title, "Settings", "MainWidth", 6500)
+ Me.Height = GetSetting(App.Title, "Settings", "MainHeight", 6500)
+
+ tvTreeView.Nodes.Add , tvwChild, "Computers", "Computers", 1, 2
+ Dim c As Computer
+ For Each c In g_computers
+ addComputer c
+ Next
+
+ Set m_currentNode = tvTreeView.Nodes("Computers")
+ Set m_currentList = lvComputers
+
+ tvTreeView.Nodes.Add , tvwChild, "Databases", "Databases", 1, 2
+ Dim d As Database_
+ For Each d In g_databases
+ AddDatabase d
+ Next
+
+ lvComputers.Visible = True
+ lvProcesses.Visible = False
+ lvDatabases.Visible = False
+ lvComputers.View = lvwReport
+ lvProcesses.View = lvwReport
+ lvDatabases.View = lvwReport
+ m_computerWidth = lvProcesses.ColumnHeaders("Computer").Width
+ m_databaseWidth = lvProcesses.ColumnHeaders("Database").Width
+ lvProcesses.ColumnHeaders("Id").Width = 0
+End Sub
+
+Private Sub setComputer(ByVal f_ip As String)
+ Dim c As Computer
+ Set c = g_computers(f_ip)
+ If c Is Nothing Then
+ MsgBox "Unknown computer: " & f_ip
+ Exit Sub
+ End If
+
+ Set m_currentComputer = c
+
+ lblTitle(1).Caption = "Processes defined on computer: " & c.m_name
+ setProcesses c.m_processes
+
+ If lvProcesses.ColumnHeaders("Computer").Width <> 0 Then
+ m_computerWidth = lvProcesses.ColumnHeaders("Computer").Width
+ lvProcesses.ColumnHeaders("Computer").Width = 0
+ End If
+
+ If lvProcesses.ColumnHeaders("Database").Width = 0 Then
+ lvProcesses.ColumnHeaders("Database").Width = m_databaseWidth
+ End If
+End Sub
+
+Private Sub setDatabase(ByVal f_name As String)
+ Dim c As Database_
+ Set c = g_databases(f_name)
+ If c Is Nothing Then
+ MsgBox "Unknown database: " & f_name
+ Exit Sub
+ End If
+
+ Set m_currentDatabase = c
+
+ lblTitle(1).Caption = "Processes defined for database: " & c.m_name
+ setProcesses c.m_processes
+
+ If lvProcesses.ColumnHeaders("Database").Width <> 0 Then
+ m_databaseWidth = lvProcesses.ColumnHeaders("Database").Width
+ lvProcesses.ColumnHeaders("Database").Width = 0
+ End If
+
+ If lvProcesses.ColumnHeaders("Computer").Width = 0 Then
+ lvProcesses.ColumnHeaders("Computer").Width = m_computerWidth
+ End If
+
+End Sub
+
+Private Sub setProcesses(ByRef c As Collection)
+ lvProcesses.ListItems.Clear
+ Dim p As Process
+ For Each p In c
+ Dim li As ListItem
+ Set li = lvProcesses.ListItems.Add(, "_" & p.m_computer.m_name & "_" & p.m_id, p.m_id)
+ li.SubItems(1) = p.m_computer.m_name
+ li.SubItems(2) = p.m_database
+ li.SubItems(3) = p.m_name
+ li.SubItems(4) = p.m_status
+ li.SubItems(5) = p.m_owner
+ Next
+End Sub
+
+Public Sub addComputer(ByRef c As Computer)
+ Dim icon As Integer
+ Select Case c.m_status
+ Case "No contact"
+ icon = 4
+ Case "Connected"
+ icon = 5
+ Case Else
+ icon = 3
+ End Select
+
+ Dim li As ListItem
+ Set li = lvComputers.ListItems.Add(, "_" & c.m_name, c.m_name, icon, icon)
+ li.SubItems(1) = c.m_status
+
+ tvTreeView.Nodes.Add "Computers", tvwChild, "_" & c.m_name, c.m_name, icon, icon
+End Sub
+
+Public Sub removeComputer(ByRef name As String)
+ lvComputers.ListItems.Remove "_" & name
+ tvTreeView.Nodes.Remove "_" & name
+
+ '
+ ' Check if should remove database
+ Dim c As Computer
+ Set c = g_computers("_" & name)
+ Dim db As Database_
+ Dim dbs As New Collection
+ Dim p As Process
+ For Each p In c.m_processes
+ Set db = g_databases("_" & p.m_database)
+ db.m_processes.Remove "_" & p.m_computer.m_name & "_" & p.m_id
+ If Not Exists(dbs, p.m_database) Then dbs.Add db, p.m_database
+ Next
+
+ For Each db In dbs
+ If db.m_processes.Count = 0 Then
+ g_databases.Remove "_" & db.m_name
+ tvTreeView.Nodes.Remove "_" & db.m_name
+ End If
+ Next
+
+ g_computers.Remove "_" & name
+
+ '
+ ' Check if should remove database
+
+ Dim n As MSComctlLib.Node
+ Set n = tvTreeView.SelectedItem
+ selectNode n
+End Sub
+
+Private Sub AddDatabase(ByRef c As Database_)
+ Dim li As ListItem
+ Set li = lvDatabases.ListItems.Add(, "_" & c.m_name, c.m_name, 9, 9)
+ li.SubItems(1) = c.m_status
+ tvTreeView.Nodes.Add "Databases", tvwChild, "_" & c.m_name, c.m_name, 9, 9
+End Sub
+
+Private Sub Form_Unload(Cancel As Integer)
+ Dim i As Integer
+
+
+ 'close all sub forms
+ For i = Forms.Count - 1 To 1 Step -1
+ Unload Forms(i)
+ Next
+ If Me.WindowState <> vbMinimized Then
+ SaveSetting App.Title, "Settings", "MainLeft", Me.Left
+ SaveSetting App.Title, "Settings", "MainTop", Me.Top
+ SaveSetting App.Title, "Settings", "MainWidth", Me.Width
+ SaveSetting App.Title, "Settings", "MainHeight", Me.Height
+ End If
+End Sub
+
+Private Sub Form_Resize()
+ On Error Resume Next
+ If Me.Width < 3000 Then Me.Width = 3000
+ SizeControls imgSplitter.Left
+End Sub
+
+Private Sub imgSplitter_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
+ With imgSplitter
+ picSplitter.Move .Left, .Top, .Width \ 2, .Height - 20
+ End With
+ picSplitter.Visible = True
+ mbMoving = True
+End Sub
+
+Private Sub imgSplitter_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
+ Dim sglPos As Single
+
+
+ If mbMoving Then
+ sglPos = X + imgSplitter.Left
+ If sglPos < sglSplitLimit Then
+ picSplitter.Left = sglSplitLimit
+ ElseIf sglPos > Me.Width - sglSplitLimit Then
+ picSplitter.Left = Me.Width - sglSplitLimit
+ Else
+ picSplitter.Left = sglPos
+ End If
+ End If
+End Sub
+
+
+Private Sub imgSplitter_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
+ SizeControls picSplitter.Left
+ picSplitter.Visible = False
+ mbMoving = False
+End Sub
+
+
+Private Sub TreeView1_DragDrop(Source As Control, X As Single, Y As Single)
+ If Source = imgSplitter Then
+ SizeControls X
+ End If
+End Sub
+
+
+Sub SizeControls(X As Single)
+ On Error Resume Next
+
+ 'set the width
+ If X < 1500 Then X = 1500
+ If X > (Me.Width - 1500) Then X = Me.Width - 1500
+ tvTreeView.Width = X
+ imgSplitter.Left = X
+
+ Dim t_left, t_width As Integer
+ t_left = X + 40
+ t_width = Me.Width - (tvTreeView.Width + 140)
+
+ lblTitle(0).Width = tvTreeView.Width
+ lblTitle(1).Left = t_left + 20
+ lblTitle(1).Width = t_width - 40
+
+
+ 'set the top
+ If tbToolBar.Visible Then
+ tvTreeView.Top = tbToolBar.Height + picTitles.Height
+ Else
+ tvTreeView.Top = picTitles.Height
+ End If
+
+
+ 'set the height
+ If sbStatusBar.Visible Then
+ tvTreeView.Height = Me.ScaleHeight - (picTitles.Top + picTitles.Height + sbStatusBar.Height)
+ Else
+ tvTreeView.Height = Me.ScaleHeight - (picTitles.Top + picTitles.Height)
+ End If
+
+
+ imgSplitter.Top = tvTreeView.Top
+ imgSplitter.Height = tvTreeView.Height
+
+ setListDimensions t_left, t_width, tvTreeView.Top, tvTreeView.Height
+End Sub
+
+Private Sub setListView(ByVal f_View As Integer)
+ lvComputers.View = f_View
+ lvProcesses.View = f_View
+End Sub
+
+Private Sub setListDimensions(ByVal f_Left As Integer, ByVal f_Width As Integer, ByVal f_Top As Integer, ByVal f_Height As Integer)
+ With lvComputers
+ .Left = f_Left
+ .Width = f_Width
+ .Top = f_Top
+ .Height = f_Height
+ End With
+ With lvProcesses
+ .Left = f_Left
+ .Width = f_Width
+ .Top = f_Top
+ .Height = f_Height
+ End With
+ With lvDatabases
+ .Left = f_Left
+ .Width = f_Width
+ .Top = f_Top
+ .Height = f_Height
+ End With
+End Sub
+
+Private Sub tbToolBar_ButtonClick(ByVal Button As MSComctlLib.Button)
+ On Error Resume Next
+ Select Case Button.Key
+ Case "New database"
+ 'ToDo: Add 'Back' button code.
+ mnuPopNewDatabase_Click
+ Case "Add computer"
+ 'ToDo: Add 'Forward' button code.
+ frmNewComputer.Show vbModal, Me
+ Dim c As Computer
+ For Each c In frmNewComputer.m_hosts
+ addComputer c
+ g_computers.Add c, "_" & c.m_name
+ Next
+ Case "Properties"
+ mnuFileProperties_Click
+ End Select
+End Sub
+
+Private Sub mnuHelpAbout_Click()
+ frmAbout.Show vbModal, Me
+End Sub
+
+Private Sub mnuHelpSearchForHelpOn_Click()
+ Dim nRet As Integer
+
+
+ 'if there is no helpfile for this project display a message to the user
+ 'you can set the HelpFile for your application in the
+ 'Project Properties dialog
+ If Len(App.HelpFile) = 0 Then
+ MsgBox "Unable to display Help Contents. There is no Help associated with this project.", vbInformation, Me.Caption
+ Else
+ On Error Resume Next
+ nRet = OSWinHelp(Me.hwnd, App.HelpFile, 261, 0)
+ If Err Then
+ MsgBox Err.Description
+ End If
+ End If
+
+End Sub
+
+Private Sub mnuHelpContents_Click()
+ Dim nRet As Integer
+
+
+ 'if there is no helpfile for this project display a message to the user
+ 'you can set the HelpFile for your application in the
+ 'Project Properties dialog
+ If Len(App.HelpFile) = 0 Then
+ MsgBox "Unable to display Help Contents. There is no Help associated with this project.", vbInformation, Me.Caption
+ Else
+ On Error Resume Next
+ nRet = OSWinHelp(Me.hwnd, App.HelpFile, 3, 0)
+ If Err Then
+ MsgBox Err.Description
+ End If
+ End If
+
+End Sub
+
+
+Private Sub mnuViewWebBrowser_Click()
+ 'ToDo: Add 'mnuViewWebBrowser_Click' code.
+ MsgBox "Add 'mnuViewWebBrowser_Click' code."
+End Sub
+
+Private Sub mnuViewOptions_Click()
+ frmOptions.Show vbModal, Me
+End Sub
+
+Private Sub mnuViewRefresh_Click()
+ 'ToDo: Add 'mnuViewRefresh_Click' code.
+ MsgBox "Add 'mnuViewRefresh_Click' code."
+End Sub
+
+
+Private Sub mnuViewStatusBar_Click()
+ mnuViewStatusBar.Checked = Not mnuViewStatusBar.Checked
+ sbStatusBar.Visible = mnuViewStatusBar.Checked
+ SizeControls imgSplitter.Left
+End Sub
+
+Private Sub mnuViewToolbar_Click()
+ mnuViewToolbar.Checked = Not mnuViewToolbar.Checked
+ tbToolBar.Visible = mnuViewToolbar.Checked
+ SizeControls imgSplitter.Left
+End Sub
+
+Private Sub mnuEditInvertSelection_Click()
+ 'ToDo: Add 'mnuEditInvertSelection_Click' code.
+ MsgBox "Add 'mnuEditInvertSelection_Click' code."
+End Sub
+
+Private Sub mnuEditSelectAll_Click()
+ 'ToDo: Add 'mnuEditSelectAll_Click' code.
+ MsgBox "Add 'mnuEditSelectAll_Click' code."
+End Sub
+
+Private Sub mnuEditPasteSpecial_Click()
+ 'ToDo: Add 'mnuEditPasteSpecial_Click' code.
+ MsgBox "Add 'mnuEditPasteSpecial_Click' code."
+End Sub
+
+Private Sub mnuEditPaste_Click()
+ 'ToDo: Add 'mnuEditPaste_Click' code.
+ MsgBox "Add 'mnuEditPaste_Click' code."
+End Sub
+
+Private Sub mnuEditCopy_Click()
+ 'ToDo: Add 'mnuEditCopy_Click' code.
+ MsgBox "Add 'mnuEditCopy_Click' code."
+End Sub
+
+Private Sub mnuEditCut_Click()
+ 'ToDo: Add 'mnuEditCut_Click' code.
+ MsgBox "Add 'mnuEditCut_Click' code."
+End Sub
+
+Private Sub mnuEditUndo_Click()
+ 'ToDo: Add 'mnuEditUndo_Click' code.
+ MsgBox "Add 'mnuEditUndo_Click' code."
+End Sub
+
+Private Sub mnuFileClose_Click()
+ 'unload the form
+ Unload Me
+
+End Sub
+
+Private Sub mnuFileProperties_Click()
+ 'ToDo: Add 'mnuFileProperties_Click' code.
+ MsgBox "Add 'mnuFileProperties_Click' code."
+End Sub
+
+Private Sub mnuFileRename_Click()
+ 'ToDo: Add 'mnuFileRename_Click' code.
+ MsgBox "Add 'mnuFileRename_Click' code."
+End Sub
+
+Private Sub mnuFileDelete_Click()
+ 'ToDo: Add 'mnuFileDelete_Click' code.
+ MsgBox "Add 'mnuFileDelete_Click' code."
+End Sub
+
+Private Sub mnuFileNew_Click()
+ 'ToDo: Add 'mnuFileNew_Click' code.
+ MsgBox "Add 'mnuFileNew_Click' code."
+End Sub
+
+Private Sub mnuFileSendTo_Click()
+ 'ToDo: Add 'mnuFileSendTo_Click' code.
+ MsgBox "Add 'mnuFileSendTo_Click' code."
+End Sub
+
+Private Sub mnuFileFind_Click()
+ 'ToDo: Add 'mnuFileFind_Click' code.
+ MsgBox "Add 'mnuFileFind_Click' code."
+End Sub
+
+Private Sub mnuFileOpen_Click()
+ Dim sFile As String
+End Sub
+
+Private Sub mnuPopComputerProperties_Click()
+ mnuFileProperties_Click
+End Sub
+
+Private Sub mnuPopNewDatabase_Click()
+ frmNewDatabase1.Show vbModal, Me
+ frmNewDatabase2.Show vbModal, Me
+ frmNewDatabase3.Show vbModal, Me
+End Sub
+
+Private Sub mnuPopAddComputer_Click()
+ frmNewComputer.Show vbModal, Me
+ Dim c As Computer
+ For Each c In frmNewComputer.m_hosts
+ addComputer c
+ g_computers.Add c, "_" & c.m_name
+ Next
+End Sub
+
+Private Sub mnuPopSortComputers_Click()
+ If m_currentNode.Sorted = True Then
+ mnuPopSortComputers.Checked = False
+ m_currentNode.Sorted = False
+ Else
+ mnuPopSortComputers.Checked = True
+ m_currentNode.Sorted = True
+ End If
+End Sub
+
+Private Sub mnuPopRemoveComputer_Click()
+ Dim res As VbMsgBoxResult
+ Dim str As String
+ str = "Remove computer " & m_currentComputer.m_name
+ res = MsgBox(str, vbOKCancel, str)
+ If res = vbOK Then
+ removeComputer (m_currentComputer.m_name)
+ End If
+End Sub
+
+Private Sub mnuPopSortDatabases_Click()
+ If m_currentNode.Sorted = True Then
+ mnuPopSortDatabases.Checked = False
+ m_currentNode.Sorted = False
+ Else
+ mnuPopSortDatabases.Checked = True
+ m_currentNode.Sorted = True
+ End If
+End Sub
+
+Private Sub tvTreeView_BeforeLabelEdit(Cancel As Integer)
+ Cancel = True
+End Sub
+
+Private Sub tvTreeView_Collapse(ByVal Node As MSComctlLib.Node)
+ 'MsgBox "tvTreeView_Collapse"
+End Sub
+
+Private Sub tvTreeView_Expand(ByVal Node As MSComctlLib.Node)
+ 'MsgBox "tvTreeView_Expand"
+End Sub
+
+Private Sub tvTreeView_NodeClick(ByVal Node As MSComctlLib.Node)
+ selectNode Node
+End Sub
+
+Private Sub tvTreeView_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
+ 'MsgBox "tvTreeView_MouseUp Button: " & Button & " Shift: " & Shift
+ Dim Node As MSComctlLib.Node
+ Dim place As Integer
+
+ Set Node = tvTreeView.HitTest(X, Y)
+ place = selectNode(Node)
+ If Button = vbRightButton Then
+ ShowPopup place
+ End If
+End Sub
+
+Private Function selectNode(ByRef n As MSComctlLib.Node) As Integer
+ Dim list As ListView
+ Dim place As Integer
+
+ If n Is Nothing Then
+ If Not m_currentNode Is Nothing Then
+ place = 1
+ m_currentNode.Selected = False
+ Else
+ place = 2
+ End If
+ Else
+ n.Selected = True
+ If n.Text = "Computers" Then
+ place = 3
+ Set list = lvComputers
+ lblTitle(1).Caption = "Computers"
+ ElseIf n.Text = "Databases" Then
+ place = 4
+ Set list = lvDatabases
+ lblTitle(1).Caption = "Databases"
+ ElseIf n.Parent.Text = "Computers" Then
+ place = 5
+ Set list = lvProcesses
+ setComputer (n.Key)
+ ElseIf n.Parent.Text = "Databases" Then
+ place = 6
+ Set list = lvProcesses
+ setDatabase (n.Key)
+ End If
+
+ If m_currentList.hwnd <> list.hwnd Then
+ m_currentList.Visible = False
+ list.Visible = True
+ Set m_currentList = list
+ End If
+ End If
+ Set m_currentNode = n
+ selectNode = place
+End Function
+
+Private Sub lvComputers_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
+ Dim li As ListItem
+ Set li = lvComputers.HitTest(X, Y)
+ If Button = vbRightButton And Not li Is Nothing Then
+ Dim c As Computer
+ Set m_currentComputer = g_computers(li.Key)
+ ShowPopup 5
+ End If
+End Sub
+
+Private Sub ShowPopup(ByVal place As Integer)
+ Select Case place
+ Case 3
+ PopupMenu mnuPopComputers
+ Case 4
+ PopupMenu mnuPopDatabases
+ Case 5
+ mnuPopComputerName.Caption = m_currentComputer.m_name & ": " & m_currentComputer.m_status
+ Select Case m_currentComputer.m_status
+ Case "Connected"
+ mnuPopConnectComputer.Enabled = False
+ mnuPopDisconnectComputer.Enabled = True
+ Case "Connecting"
+ mnuPopConnectComputer.Enabled = False
+ mnuPopDisconnectComputer.Enabled = True
+ Case "Not connected"
+ mnuPopConnectComputer.Enabled = True
+ mnuPopDisconnectComputer.Enabled = False
+ Case "No contact"
+ mnuPopConnectComputer.Enabled = True
+ mnuPopDisconnectComputer.Enabled = False
+ Case Else
+ mnuPopConnectComputer.Enabled = False
+ mnuPopDisconnectComputer.Enabled = False
+ End Select
+
+ PopupMenu mnuPopComputer, , , , mnuPopComputerName
+ End Select
+End Sub
+
+Private Sub lvComputers_BeforeLabelEdit(Cancel As Integer)
+ Cancel = True
+End Sub
+
+Private Sub lvProcesses_BeforeLabelEdit(Cancel As Integer)
+ Cancel = True
+End Sub
+
+Private Sub lvDatabases_BeforeLabelEdit(Cancel As Integer)
+ Cancel = True
+End Sub
+
+Private Sub ColumnClick(ByRef list As ListView, i As Integer)
+ i = i - 1
+ If list.SortKey = i Then
+ list.SortOrder = 1 - list.SortOrder
+ Else
+ list.SortKey = i
+ End If
+End Sub
+
+Private Sub lvComputers_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
+ ColumnClick lvComputers, ColumnHeader.Index
+End Sub
+
+Private Sub lvProcesses_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
+ ColumnClick lvProcesses, ColumnHeader.Index
+End Sub
+
+Private Sub lvDatabases_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
+ ColumnClick lvDatabases, ColumnHeader.Index
+End Sub
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frm
new file mode 100644
index 00000000000..eae5802493c
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frm
@@ -0,0 +1,124 @@
+VERSION 5.00
+Begin VB.Form frmNewComputer
+ Caption = "Add computer"
+ ClientHeight = 1545
+ ClientLeft = 60
+ ClientTop = 345
+ ClientWidth = 4605
+ LinkTopic = "Form1"
+ ScaleHeight = 1545
+ ScaleWidth = 4605
+ StartUpPosition = 3 'Windows Default
+ Begin VB.CommandButton Command3
+ Caption = "Apply"
+ Default = -1 'True
+ Height = 360
+ Left = 3240
+ TabIndex = 4
+ Tag = "OK"
+ Top = 840
+ Width = 1140
+ End
+ Begin VB.CommandButton Command2
+ Caption = "Cancel"
+ Height = 360
+ Left = 1920
+ TabIndex = 3
+ Tag = "OK"
+ Top = 840
+ Width = 1140
+ End
+ Begin VB.CommandButton Command1
+ Caption = "OK"
+ Height = 360
+ Left = 600
+ TabIndex = 2
+ Tag = "OK"
+ Top = 840
+ Width = 1140
+ End
+ Begin VB.TextBox Text1
+ Height = 285
+ Left = 1440
+ TabIndex = 1
+ Top = 240
+ Width = 2925
+ End
+ Begin VB.Label lblLabels
+ Caption = "Computer name:"
+ Height = 255
+ Index = 1
+ Left = 120
+ TabIndex = 0
+ Tag = "&User Name:"
+ Top = 240
+ Width = 1440
+ End
+End
+Attribute VB_Name = "frmNewComputer"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Public m_hosts As New Collection
+
+Private Sub Form_Load()
+ If m_hosts.Count > 0 Then
+ For i = m_hosts.Count To 1 Step -1
+ m_hosts.Remove i
+ Next
+ End If
+End Sub
+
+Private Sub Command1_Click()
+ If Text1.Text = "" Then
+ MsgBox "Invalid hostname"
+ Exit Sub
+ End If
+
+ If Exists(g_computers, "_" & Text1.Text) Then
+ MsgBox Text1.Text & " already exists"
+ Exit Sub
+ End If
+
+ Dim c As New Computer
+ With c
+ .m_ip = ""
+ .m_name = Text1.Text
+ .m_status = "Not connected"
+ Set .m_processes = New Collection
+ End With
+
+ m_hosts.Add c
+
+ Unload Me
+End Sub
+
+Private Sub Command2_Click()
+ Unload Me
+End Sub
+
+Private Sub Command3_Click()
+ If Text1.Text = "" Then
+ MsgBox "Invalid hostname"
+ Exit Sub
+ End If
+
+ If Exists(g_computers, "_" & Text1.Text) Then
+ MsgBox Text1.Text & " already exists"
+ Exit Sub
+ End If
+
+ Dim c As New Computer
+ With c
+ .m_ip = ""
+ .m_name = Text1.Text
+ .m_status = "Not connected"
+ Set .m_processes = New Collection
+ End With
+
+ m_hosts.Add c
+
+ Text1.Text = ""
+End Sub
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frx b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frx
new file mode 100644
index 00000000000..593f4708db8
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frx
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase.frx b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase.frx
new file mode 100644
index 00000000000..b20c2b651ae
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase.frx
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase1.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase1.frm
new file mode 100644
index 00000000000..3fa1fd4c4e8
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase1.frm
@@ -0,0 +1,187 @@
+VERSION 5.00
+Begin VB.Form frmNewDatabase1
+ BorderStyle = 5 'Sizable ToolWindow
+ Caption = "Nodes"
+ ClientHeight = 3000
+ ClientLeft = 2850
+ ClientTop = 3450
+ ClientWidth = 6240
+ LinkTopic = "Form1"
+ MaxButton = 0 'False
+ MinButton = 0 'False
+ ScaleHeight = 3281.25
+ ScaleMode = 0 'User
+ ScaleWidth = 6359.712
+ ShowInTaskbar = 0 'False
+ StartUpPosition = 2 'CenterScreen
+ Begin VB.TextBox textApiNodes
+ Height = 285
+ Left = 2760
+ TabIndex = 12
+ Text = "4"
+ Top = 1665
+ Width = 375
+ End
+ Begin VB.VScrollBar VScroll1
+ Height = 255
+ Left = 3240
+ TabIndex = 11
+ Top = 1680
+ Width = 135
+ End
+ Begin VB.OptionButton Option4
+ Alignment = 1 'Right Justify
+ Caption = "1"
+ Height = 375
+ Left = 2760
+ TabIndex = 10
+ Top = 1020
+ Width = 375
+ End
+ Begin VB.OptionButton Option3
+ Alignment = 1 'Right Justify
+ Caption = "4"
+ Height = 375
+ Left = 3960
+ TabIndex = 9
+ Top = 360
+ Width = 375
+ End
+ Begin VB.OptionButton Option2
+ Alignment = 1 'Right Justify
+ Caption = "2"
+ Height = 375
+ Left = 3360
+ TabIndex = 8
+ Top = 360
+ Width = 375
+ End
+ Begin VB.OptionButton Option1
+ Alignment = 1 'Right Justify
+ Caption = "1"
+ Height = 375
+ Left = 2760
+ TabIndex = 7
+ Top = 360
+ Value = -1 'True
+ Width = 375
+ End
+ Begin VB.CommandButton cmdCancel
+ Cancel = -1 'True
+ Caption = "Cancel"
+ Height = 305
+ Left = 1320
+ TabIndex = 3
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdFinish
+ Caption = "Finish"
+ Enabled = 0 'False
+ Height = 305
+ Left = 5040
+ TabIndex = 2
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdBack
+ Caption = "Back"
+ Default = -1 'True
+ Enabled = 0 'False
+ Height = 305
+ Left = 2640
+ TabIndex = 0
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdNext
+ Caption = "Next"
+ Height = 305
+ Left = 3720
+ TabIndex = 1
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.Label Label3
+ Caption = "No of api nodes"
+ Height = 255
+ Left = 240
+ TabIndex = 6
+ Top = 1680
+ Width = 2415
+ End
+ Begin VB.Label Label2
+ Caption = "No of management nodes"
+ Height = 255
+ Left = 240
+ TabIndex = 5
+ Top = 1080
+ Width = 2415
+ End
+ Begin VB.Label Label1
+ Caption = "No of database nodes"
+ Height = 255
+ Left = 240
+ TabIndex = 4
+ Top = 420
+ Width = 2415
+ End
+ Begin VB.Line Line1
+ BorderColor = &H80000003&
+ X1 = 122.302
+ X2 = 6237.41
+ Y1 = 2493.75
+ Y2 = 2493.75
+ End
+End
+Attribute VB_Name = "frmNewDatabase1"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Option Explicit
+
+Private Sub Form_Resize()
+ If Me.Width < 6375 Then Me.Width = 6375
+ cmdCancel.Left = Me.ScaleWidth - 5136 + 400
+ cmdBack.Left = Me.ScaleWidth - 3897 + 400
+ cmdNext.Left = Me.ScaleWidth - 2883 + 400
+ cmdFinish.Left = Me.ScaleWidth - 1643 + 400
+ Line1.X2 = Me.ScaleWidth - 480 + 400
+
+ cmdCancel.Top = Me.ScaleHeight - 375
+ cmdBack.Top = Me.ScaleHeight - 375
+ cmdNext.Top = Me.ScaleHeight - 375
+ cmdFinish.Top = Me.ScaleHeight - 375
+ Line1.Y1 = Me.ScaleHeight - 475
+ Line1.Y2 = Me.ScaleHeight - 475
+End Sub
+
+Private Sub cmdCancel_Click()
+ 'set the global var to false
+ 'to denote a failed login
+ Unload Me
+End Sub
+
+Private Sub Option1_Click()
+ Option2.Value = False
+ Option3.Value = False
+End Sub
+
+Private Sub Option2_Click()
+ Option1.Value = False
+ Option3.Value = False
+End Sub
+
+Private Sub Option3_Click()
+ Option1.Value = False
+ Option2.Value = False
+End Sub
+
+Private Sub Option4_Click()
+ Option4.Value = True
+End Sub
+
+Private Sub textApiNodes_Validate(Cancel As Boolean)
+ 'If Not isnumber(textApiNodes.Text) Then Cancel = False
+End Sub
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.frm
new file mode 100644
index 00000000000..49806a695ea
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.frm
@@ -0,0 +1,136 @@
+VERSION 5.00
+Begin VB.Form frmNewDatabase2
+ BorderStyle = 5 'Sizable ToolWindow
+ Caption = "Computers"
+ ClientHeight = 2895
+ ClientLeft = 2850
+ ClientTop = 3450
+ ClientWidth = 6240
+ LinkTopic = "Form1"
+ MaxButton = 0 'False
+ MinButton = 0 'False
+ ScaleHeight = 3166.406
+ ScaleMode = 0 'User
+ ScaleWidth = 6359.712
+ ShowInTaskbar = 0 'False
+ StartUpPosition = 2 'CenterScreen
+ Begin VB.ComboBox Combo1
+ Height = 315
+ Left = 2400
+ TabIndex = 7
+ Text = "Combo1"
+ Top = 360
+ Width = 1455
+ End
+ Begin VB.VScrollBar VScroll2
+ Height = 255
+ Left = 4560
+ TabIndex = 6
+ Top = 375
+ Width = 135
+ End
+ Begin VB.TextBox Text1
+ Height = 285
+ Left = 4080
+ TabIndex = 5
+ Text = "4"
+ Top = 360
+ Width = 375
+ End
+ Begin VB.CommandButton cmdCancel
+ Cancel = -1 'True
+ Caption = "Cancel"
+ Height = 305
+ Left = 1320
+ TabIndex = 3
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdFinish
+ Caption = "Finish"
+ Enabled = 0 'False
+ Height = 305
+ Left = 5040
+ TabIndex = 2
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdBack
+ Caption = "Back"
+ Default = -1 'True
+ Enabled = 0 'False
+ Height = 305
+ Left = 2640
+ TabIndex = 0
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdNext
+ Caption = "Next"
+ Height = 305
+ Left = 3720
+ TabIndex = 1
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.Label Label5
+ Caption = "Computer"
+ Height = 255
+ Left = 2400
+ TabIndex = 9
+ Top = 0
+ Width = 975
+ End
+ Begin VB.Label Label4
+ Caption = "Node id"
+ Height = 255
+ Left = 4080
+ TabIndex = 8
+ Top = 120
+ Width = 615
+ End
+ Begin VB.Label Label1
+ Caption = "Database node 1"
+ Height = 255
+ Left = 240
+ TabIndex = 4
+ Top = 420
+ Width = 2415
+ End
+ Begin VB.Line Line1
+ BorderColor = &H80000003&
+ X1 = 122.302
+ X2 = 6237.41
+ Y1 = 2493.75
+ Y2 = 2493.75
+ End
+End
+Attribute VB_Name = "frmNewDatabase2"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Option Explicit
+
+Private Sub Form_Resize()
+ If Me.Width < 6375 Then Me.Width = 6375
+ cmdCancel.Left = Me.ScaleWidth - 5136 + 400
+ cmdBack.Left = Me.ScaleWidth - 3897 + 400
+ cmdNext.Left = Me.ScaleWidth - 2883 + 400
+ cmdFinish.Left = Me.ScaleWidth - 1643 + 400
+ Line1.X2 = Me.ScaleWidth - 480 + 400
+
+ cmdCancel.Top = Me.ScaleHeight - 375
+ cmdBack.Top = Me.ScaleHeight - 375
+ cmdNext.Top = Me.ScaleHeight - 375
+ cmdFinish.Top = Me.ScaleHeight - 375
+ Line1.Y1 = Me.ScaleHeight - 475
+ Line1.Y2 = Me.ScaleHeight - 475
+End Sub
+
+Private Sub cmdCancel_Click()
+ 'set the global var to false
+ 'to denote a failed login
+ Unload Me
+End Sub
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.log b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.log
new file mode 100644
index 00000000000..808b21866e5
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.log
@@ -0,0 +1 @@
+Line 2: The Form or MDIForm name frmNewDatabase1 is already in use; cannot load this form.
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase3.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase3.frm
new file mode 100644
index 00000000000..ba050a58a09
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase3.frm
@@ -0,0 +1,88 @@
+VERSION 5.00
+Begin VB.Form frmNewDatabase3
+ BorderStyle = 5 'Sizable ToolWindow
+ Caption = "Database configuration"
+ ClientHeight = 3000
+ ClientLeft = 2850
+ ClientTop = 3450
+ ClientWidth = 6240
+ LinkTopic = "Form1"
+ MaxButton = 0 'False
+ MinButton = 0 'False
+ ScaleHeight = 3281.25
+ ScaleMode = 0 'User
+ ScaleWidth = 6359.712
+ ShowInTaskbar = 0 'False
+ StartUpPosition = 2 'CenterScreen
+ Begin VB.CommandButton cmdCancel
+ Cancel = -1 'True
+ Caption = "Cancel"
+ Height = 305
+ Left = 1320
+ TabIndex = 3
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdFinish
+ Caption = "Finish"
+ Enabled = 0 'False
+ Height = 305
+ Left = 5040
+ TabIndex = 2
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdBack
+ Caption = "Back"
+ Default = -1 'True
+ Enabled = 0 'False
+ Height = 305
+ Left = 2640
+ TabIndex = 0
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.CommandButton cmdNext
+ Caption = "Next"
+ Height = 305
+ Left = 3720
+ TabIndex = 1
+ Top = 2400
+ Width = 1140
+ End
+ Begin VB.Line Line1
+ BorderColor = &H80000003&
+ X1 = 122.302
+ X2 = 6237.41
+ Y1 = 2493.75
+ Y2 = 2493.75
+ End
+End
+Attribute VB_Name = "frmNewDatabase3"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Option Explicit
+
+Private Sub Form_Resize()
+ If Me.Width < 6375 Then Me.Width = 6375
+ cmdCancel.Left = Me.ScaleWidth - 5136 + 400
+ cmdBack.Left = Me.ScaleWidth - 3897 + 400
+ cmdNext.Left = Me.ScaleWidth - 2883 + 400
+ cmdFinish.Left = Me.ScaleWidth - 1643 + 400
+ Line1.X2 = Me.ScaleWidth - 480 + 400
+
+ cmdCancel.Top = Me.ScaleHeight - 375
+ cmdBack.Top = Me.ScaleHeight - 375
+ cmdNext.Top = Me.ScaleHeight - 375
+ cmdFinish.Top = Me.ScaleHeight - 375
+ Line1.Y1 = Me.ScaleHeight - 475
+ Line1.Y2 = Me.ScaleHeight - 475
+End Sub
+
+Private Sub cmdCancel_Click()
+ 'set the global var to false
+ 'to denote a failed login
+ Unload Me
+End Sub
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmOptions.frm b/storage/ndb/src/cw/cpcc-win32/vb6/frmOptions.frm
new file mode 100644
index 00000000000..e526a35b3ec
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmOptions.frm
@@ -0,0 +1,231 @@
+VERSION 5.00
+Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "mscomctl.ocx"
+Begin VB.Form frmOptions
+ BorderStyle = 3 'Fixed Dialog
+ Caption = "Options"
+ ClientHeight = 5040
+ ClientLeft = 6600
+ ClientTop = 4575
+ ClientWidth = 6150
+ KeyPreview = -1 'True
+ LinkTopic = "Form1"
+ MaxButton = 0 'False
+ MinButton = 0 'False
+ ScaleHeight = 5040
+ ScaleWidth = 6150
+ ShowInTaskbar = 0 'False
+ Tag = "Options"
+ Begin VB.CommandButton cmdOK
+ Caption = "OK"
+ Height = 375
+ Left = 2490
+ TabIndex = 1
+ Tag = "OK"
+ Top = 4455
+ Width = 1095
+ End
+ Begin VB.CommandButton cmdCancel
+ Cancel = -1 'True
+ Caption = "Cancel"
+ Height = 375
+ Left = 3720
+ TabIndex = 3
+ Tag = "Cancel"
+ Top = 4455
+ Width = 1095
+ End
+ Begin VB.CommandButton cmdApply
+ Caption = "&Apply"
+ Height = 375
+ Left = 4920
+ TabIndex = 5
+ Tag = "&Apply"
+ Top = 4455
+ Width = 1095
+ End
+ Begin VB.PictureBox picOptions
+ BorderStyle = 0 'None
+ Height = 3780
+ Index = 3
+ Left = -20000
+ ScaleHeight = 3840.968
+ ScaleMode = 0 'User
+ ScaleWidth = 5745.64
+ TabIndex = 7
+ TabStop = 0 'False
+ Top = 480
+ Width = 5685
+ Begin VB.Frame fraSample4
+ Caption = "Sample 4"
+ Height = 2022
+ Left = 505
+ TabIndex = 11
+ Tag = "Sample 4"
+ Top = 502
+ Width = 2033
+ End
+ End
+ Begin VB.PictureBox picOptions
+ BorderStyle = 0 'None
+ Height = 3780
+ Index = 2
+ Left = -20000
+ ScaleHeight = 3840.968
+ ScaleMode = 0 'User
+ ScaleWidth = 5745.64
+ TabIndex = 9
+ TabStop = 0 'False
+ Top = 480
+ Width = 5685
+ Begin VB.Frame fraSample3
+ Caption = "Sample 3"
+ Height = 2022
+ Left = 406
+ TabIndex = 10
+ Tag = "Sample 3"
+ Top = 403
+ Width = 2033
+ End
+ End
+ Begin VB.PictureBox picOptions
+ BorderStyle = 0 'None
+ Height = 3780
+ Index = 1
+ Left = -20000
+ ScaleHeight = 3840.968
+ ScaleMode = 0 'User
+ ScaleWidth = 5745.64
+ TabIndex = 6
+ TabStop = 0 'False
+ Top = 480
+ Width = 5685
+ Begin VB.Frame fraSample2
+ Caption = "Sample 2"
+ Height = 2022
+ Left = 307
+ TabIndex = 8
+ Tag = "Sample 2"
+ Top = 305
+ Width = 2033
+ End
+ End
+ Begin VB.PictureBox picOptions
+ BorderStyle = 0 'None
+ Height = 3780
+ Index = 0
+ Left = 210
+ ScaleHeight = 3840.968
+ ScaleMode = 0 'User
+ ScaleWidth = 5745.64
+ TabIndex = 2
+ TabStop = 0 'False
+ Top = 480
+ Width = 5685
+ Begin VB.Frame fraSample1
+ Caption = "Sample 1"
+ Height = 2022
+ Left = 208
+ TabIndex = 4
+ Tag = "Sample 1"
+ Top = 207
+ Width = 2033
+ End
+ End
+ Begin MSComctlLib.TabStrip tbsOptions
+ Height = 4245
+ Left = 105
+ TabIndex = 0
+ Top = 120
+ Width = 5895
+ _ExtentX = 10398
+ _ExtentY = 7488
+ _Version = 393216
+ BeginProperty Tabs {1EFB6598-857C-11D1-B16A-00C0F0283628}
+ NumTabs = 4
+ BeginProperty Tab1 {1EFB659A-857C-11D1-B16A-00C0F0283628}
+ Caption = "Group 1"
+ ImageVarType = 2
+ EndProperty
+ BeginProperty Tab2 {1EFB659A-857C-11D1-B16A-00C0F0283628}
+ Caption = "Group 2"
+ ImageVarType = 2
+ EndProperty
+ BeginProperty Tab3 {1EFB659A-857C-11D1-B16A-00C0F0283628}
+ Caption = "Group 3"
+ ImageVarType = 2
+ EndProperty
+ BeginProperty Tab4 {1EFB659A-857C-11D1-B16A-00C0F0283628}
+ Caption = "Group 4"
+ ImageVarType = 2
+ EndProperty
+ EndProperty
+ End
+End
+Attribute VB_Name = "frmOptions"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+Private Sub cmdApply_Click()
+ 'ToDo: Add 'cmdApply_Click' code.
+ MsgBox "Apply Code goes here to set options w/o closing dialog!"
+End Sub
+
+
+Private Sub cmdCancel_Click()
+ Unload Me
+End Sub
+
+
+Private Sub cmdOK_Click()
+ 'ToDo: Add 'cmdOK_Click' code.
+ MsgBox "Code goes here to set options and close dialog!"
+ Unload Me
+End Sub
+
+
+Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
+ Dim i As Integer
+ i = tbsOptions.SelectedItem.Index
+ 'handle ctrl+tab to move to the next tab
+ If (Shift And 3) = 2 And KeyCode = vbKeyTab Then
+ If i = tbsOptions.Tabs.Count Then
+ 'last tab so we need to wrap to tab 1
+ Set tbsOptions.SelectedItem = tbsOptions.Tabs(1)
+ Else
+ 'increment the tab
+ Set tbsOptions.SelectedItem = tbsOptions.Tabs(i + 1)
+ End If
+ ElseIf (Shift And 3) = 3 And KeyCode = vbKeyTab Then
+ If i = 1 Then
+ 'last tab so we need to wrap to tab 1
+ Set tbsOptions.SelectedItem = tbsOptions.Tabs(tbsOptions.Tabs.Count)
+ Else
+ 'increment the tab
+ Set tbsOptions.SelectedItem = tbsOptions.Tabs(i - 1)
+ End If
+ End If
+End Sub
+
+
+
+
+Private Sub tbsOptions_Click()
+
+
+ Dim i As Integer
+ 'show and enable the selected tab's controls
+ 'and hide and disable all others
+ For i = 0 To tbsOptions.Tabs.Count - 1
+ If i = tbsOptions.SelectedItem.Index - 1 Then
+ picOptions(i).Left = 210
+ picOptions(i).Enabled = True
+ Else
+ picOptions(i).Left = -20000
+ picOptions(i).Enabled = False
+ End If
+ Next
+
+
+End Sub
+
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/frmSplash.frx b/storage/ndb/src/cw/cpcc-win32/vb6/frmSplash.frx
new file mode 100644
index 00000000000..fee0c5c59de
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/frmSplash.frx
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/networking.ico b/storage/ndb/src/cw/cpcc-win32/vb6/networking.ico
new file mode 100644
index 00000000000..6bbf8022fc6
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/networking.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcc-win32/vb6/open folder.ico b/storage/ndb/src/cw/cpcc-win32/vb6/open folder.ico
new file mode 100644
index 00000000000..7bb32cc83d3
--- /dev/null
+++ b/storage/ndb/src/cw/cpcc-win32/vb6/open folder.ico
Binary files differ
diff --git a/storage/ndb/src/cw/cpcd/APIService.cpp b/storage/ndb/src/cw/cpcd/APIService.cpp
new file mode 100644
index 00000000000..63d0aaafe86
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/APIService.cpp
@@ -0,0 +1,386 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <Parser.hpp>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <socket_io.h>
+
+#include "APIService.hpp"
+#include "CPCD.hpp"
+#include <NdbMutex.h>
+#include <OutputStream.hpp>
+
+/**
+ const char * name;
+ const char * realName;
+ const Type type;
+ const ArgType argType;
+ const ArgRequired argRequired;
+ const ArgMinMax argMinMax;
+ const int minVal;
+ const int maxVal;
+ void (T::* function)(const class Properties & args);
+ const char * description;
+*/
+
+#define CPCD_CMD(name, fun, desc) \
+ { name, \
+ 0, \
+ ParserRow<CPCDAPISession>::Cmd, \
+ ParserRow<CPCDAPISession>::String, \
+ ParserRow<CPCDAPISession>::Optional, \
+ ParserRow<CPCDAPISession>::IgnoreMinMax, \
+ 0, 0, \
+ fun, \
+ desc, 0 }
+
+#define CPCD_ARG(name, type, opt, desc) \
+ { name, \
+ 0, \
+ ParserRow<CPCDAPISession>::Arg, \
+ ParserRow<CPCDAPISession>::type, \
+ ParserRow<CPCDAPISession>::opt, \
+ ParserRow<CPCDAPISession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ desc, 0 }
+
+#define CPCD_ARG2(name, type, opt, min, max, desc) \
+ { name, \
+ 0, \
+ ParserRow<CPCDAPISession>::Arg, \
+ ParserRow<CPCDAPISession>::type, \
+ ParserRow<CPCDAPISession>::opt, \
+ ParserRow<CPCDAPISession>::IgnoreMinMax, \
+ min, max, \
+ 0, \
+ desc, 0 }
+
+#define CPCD_END() \
+ { 0, \
+ 0, \
+ ParserRow<CPCDAPISession>::Arg, \
+ ParserRow<CPCDAPISession>::Int, \
+ ParserRow<CPCDAPISession>::Optional, \
+ ParserRow<CPCDAPISession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+#define CPCD_CMD_ALIAS(name, realName, fun) \
+ { name, \
+ realName, \
+ ParserRow<CPCDAPISession>::CmdAlias, \
+ ParserRow<CPCDAPISession>::Int, \
+ ParserRow<CPCDAPISession>::Optional, \
+ ParserRow<CPCDAPISession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+#define CPCD_ARG_ALIAS(name, realName, fun) \
+ { name, \
+ realName, \
+ ParserRow<CPCDAPISession>::ArgAlias, \
+ ParserRow<CPCDAPISession>::Int, \
+ ParserRow<CPCDAPISession>::Optional, \
+ ParserRow<CPCDAPISession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+const
+ParserRow<CPCDAPISession> commands[] =
+{
+ CPCD_CMD("define process" , &CPCDAPISession::defineProcess, ""),
+ CPCD_ARG("id", Int, Optional, "Id of process."),
+ CPCD_ARG("name", String, Mandatory, "Name of process"),
+ CPCD_ARG("group", String, Mandatory, "Group of process"),
+ CPCD_ARG("env", String, Optional, "Environment variables for process"),
+ CPCD_ARG("path", String, Mandatory, "Path to binary"),
+ CPCD_ARG("args", String, Optional, "Arguments to process"),
+ CPCD_ARG("type", String, Mandatory, "Type of process"),
+ CPCD_ARG("cwd", String, Mandatory, "Working directory of process"),
+ CPCD_ARG("owner", String, Mandatory, "Owner of process"),
+ CPCD_ARG("runas", String, Optional, "Run as user"),
+ CPCD_ARG("stdout", String, Optional, "Redirection of stdout"),
+ CPCD_ARG("stderr", String, Optional, "Redirection of stderr"),
+ CPCD_ARG("stdin", String, Optional, "Redirection of stderr"),
+ CPCD_ARG("ulimit", String, Optional, "ulimit"),
+
+ CPCD_CMD("undefine process", &CPCDAPISession::undefineProcess, ""),
+ CPCD_CMD_ALIAS("undef", "undefine process", 0),
+ CPCD_ARG("id", Int, Mandatory, "Id of process"),
+ CPCD_ARG_ALIAS("i", "id", 0),
+
+ CPCD_CMD("start process", &CPCDAPISession::startProcess, ""),
+ CPCD_ARG("id", Int, Mandatory, "Id of process"),
+
+ CPCD_CMD("stop process", &CPCDAPISession::stopProcess, ""),
+ CPCD_ARG("id", Int, Mandatory, "Id of process"),
+
+ CPCD_CMD("list processes", &CPCDAPISession::listProcesses, ""),
+
+ CPCD_END()
+};
+CPCDAPISession::CPCDAPISession(NDB_SOCKET_TYPE sock,
+ CPCD & cpcd)
+ : SocketServer::Session(sock)
+ , m_cpcd(cpcd)
+{
+ m_input = new SocketInputStream(sock);
+ m_output = new SocketOutputStream(sock);
+ m_parser = new Parser<CPCDAPISession>(commands, *m_input, true, true, true);
+}
+
+CPCDAPISession::CPCDAPISession(FILE * f, CPCD & cpcd)
+ : SocketServer::Session(1)
+ , m_cpcd(cpcd)
+{
+ m_input = new FileInputStream(f);
+ m_parser = new Parser<CPCDAPISession>(commands, *m_input, true, true, true);
+}
+
+CPCDAPISession::~CPCDAPISession() {
+ delete m_input;
+ delete m_parser;
+}
+
+void
+CPCDAPISession::runSession(){
+ Parser_t::Context ctx;
+ while(!m_stop){
+ m_parser->run(ctx, * this);
+ if(ctx.m_currentToken == 0)
+ break;
+
+ switch(ctx.m_status){
+ case Parser_t::Ok:
+ for(size_t i = 0; i<ctx.m_aliasUsed.size(); i++)
+ ndbout_c("Used alias: %s -> %s",
+ ctx.m_aliasUsed[i]->name, ctx.m_aliasUsed[i]->realName);
+ break;
+ case Parser_t::NoLine:
+ case Parser_t::EmptyLine:
+ break;
+ default:
+ break;
+ }
+ }
+ NDB_CLOSE_SOCKET(m_socket);
+}
+
+void
+CPCDAPISession::stopSession(){
+ CPCD::RequestStatus rs;
+ for(size_t i = 0; i<m_temporaryProcesses.size(); i++){
+ Uint32 id = m_temporaryProcesses[i];
+ m_cpcd.undefineProcess(&rs, id);
+ }
+}
+
+void
+CPCDAPISession::loadFile(){
+ Parser_t::Context ctx;
+ while(!m_stop){
+ m_parser->run(ctx, * this);
+ if(ctx.m_currentToken == 0)
+ break;
+
+ switch(ctx.m_status){
+ case Parser_t::Ok:
+ for(size_t i = 0; i<ctx.m_aliasUsed.size(); i++)
+ ndbout_c("Used alias: %s -> %s",
+ ctx.m_aliasUsed[i]->name, ctx.m_aliasUsed[i]->realName);
+ break;
+ case Parser_t::NoLine:
+ case Parser_t::EmptyLine:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static const int g_TimeOut = 1000;
+
+void
+CPCDAPISession::defineProcess(Parser_t::Context & /* unused */,
+ const class Properties & args){
+
+ CPCD::Process * p = new CPCD::Process(args, &m_cpcd);
+
+ CPCD::RequestStatus rs;
+
+ bool ret = m_cpcd.defineProcess(&rs, p);
+ if(!m_cpcd.loadingProcessList) {
+ m_output->println("define process");
+ m_output->println("status: %d", rs.getStatus());
+ if(ret == true){
+ m_output->println("id: %d", p->m_id);
+ if(p->m_processType == TEMPORARY){
+ m_temporaryProcesses.push_back(p->m_id);
+ }
+ } else {
+ m_output->println("errormessage: %s", rs.getErrMsg());
+ }
+ m_output->println("");
+ }
+}
+
+void
+CPCDAPISession::undefineProcess(Parser_t::Context & /* unused */,
+ const class Properties & args){
+ Uint32 id;
+ CPCD::RequestStatus rs;
+
+ args.get("id", &id);
+ bool ret = m_cpcd.undefineProcess(&rs, id);
+
+ m_output->println("undefine process");
+ m_output->println("id: %d", id);
+ m_output->println("status: %d", rs.getStatus());
+ if(!ret)
+ m_output->println("errormessage: %s", rs.getErrMsg());
+
+ m_output->println("");
+}
+
+void
+CPCDAPISession::startProcess(Parser_t::Context & /* unused */,
+ const class Properties & args){
+ Uint32 id;
+ CPCD::RequestStatus rs;
+
+ args.get("id", &id);
+ const int ret = m_cpcd.startProcess(&rs, id);
+
+ if(!m_cpcd.loadingProcessList) {
+ m_output->println("start process");
+ m_output->println("id: %d", id);
+ m_output->println("status: %d", rs.getStatus());
+ if(!ret)
+ m_output->println("errormessage: %s", rs.getErrMsg());
+ m_output->println("");
+ }
+}
+
+void
+CPCDAPISession::stopProcess(Parser_t::Context & /* unused */,
+ const class Properties & args){
+ Uint32 id;
+ CPCD::RequestStatus rs;
+
+ args.get("id", &id);
+ int ret = m_cpcd.stopProcess(&rs, id);
+
+ m_output->println("stop process");
+ m_output->println("id: %d", id);
+ m_output->println("status: %d", rs.getStatus());
+ if(!ret)
+ m_output->println("errormessage: %s", rs.getErrMsg());
+
+ m_output->println("");
+}
+
+static const char *
+propToString(Properties *prop, const char *key) {
+ static char buf[32];
+ const char *retval = NULL;
+ PropertiesType pt;
+
+ prop->getTypeOf(key, &pt);
+ switch(pt) {
+ case PropertiesType_Uint32:
+ Uint32 val;
+ prop->get(key, &val);
+ BaseString::snprintf(buf, sizeof buf, "%d", val);
+ retval = buf;
+ break;
+ case PropertiesType_char:
+ const char *str;
+ prop->get(key, &str);
+ retval = str;
+ break;
+ default:
+ BaseString::snprintf(buf, sizeof buf, "(unknown)");
+ retval = buf;
+ }
+ return retval;
+}
+
+void
+CPCDAPISession::printProperty(Properties *prop, const char *key) {
+ m_output->println("%s: %s", key, propToString(prop, key));
+}
+
+void
+CPCDAPISession::listProcesses(Parser_t::Context & /* unused */,
+ const class Properties & /* unused */){
+ m_cpcd.m_processes.lock();
+ MutexVector<CPCD::Process *> *proclist = m_cpcd.getProcessList();
+
+ m_output->println("start processes");
+ m_output->println("");
+
+
+ for(size_t i = 0; i < proclist->size(); i++) {
+ CPCD::Process *p = (*proclist)[i];
+
+ m_output->println("process");
+
+ m_output->println("id: %d", p->m_id);
+ m_output->println("name: %s", p->m_name.c_str());
+ m_output->println("path: %s", p->m_path.c_str());
+ m_output->println("args: %s", p->m_args.c_str());
+ m_output->println("type: %s", p->m_type.c_str());
+ m_output->println("cwd: %s", p->m_cwd.c_str());
+ m_output->println("env: %s", p->m_env.c_str());
+ m_output->println("owner: %s", p->m_owner.c_str());
+ m_output->println("group: %s", p->m_group.c_str());
+ m_output->println("runas: %s", p->m_runas.c_str());
+ m_output->println("stdin: %s", p->m_stdin.c_str());
+ m_output->println("stdout: %s", p->m_stdout.c_str());
+ m_output->println("stderr: %s", p->m_stderr.c_str());
+ m_output->println("ulimit: %s", p->m_ulimit.c_str());
+ switch(p->m_status){
+ case STOPPED:
+ m_output->println("status: stopped");
+ break;
+ case STARTING:
+ m_output->println("status: starting");
+ break;
+ case RUNNING:
+ m_output->println("status: running");
+ break;
+ case STOPPING:
+ m_output->println("status: stopping");
+ break;
+ }
+
+ m_output->println("");
+
+ }
+
+ m_output->println("end processes");
+ m_output->println("");
+
+ m_cpcd.m_processes.unlock();
+}
+
+template class Vector<ParserRow<CPCDAPISession> const*>;
diff --git a/storage/ndb/src/cw/cpcd/APIService.hpp b/storage/ndb/src/cw/cpcd/APIService.hpp
new file mode 100644
index 00000000000..ef988785f89
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/APIService.hpp
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CPCD_API_HPP
+#define CPCD_API_HPP
+
+#include <Parser.hpp>
+#include <InputStream.hpp>
+#include <SocketServer.hpp>
+
+class CPCD;
+
+class CPCDAPISession : public SocketServer::Session {
+ typedef Parser<CPCDAPISession> Parser_t;
+
+ class CPCD & m_cpcd;
+ InputStream *m_input;
+ OutputStream *m_output;
+ Parser_t *m_parser;
+
+ Vector<int> m_temporaryProcesses;
+
+ void printProperty(Properties *prop, const char *key);
+public:
+ CPCDAPISession(NDB_SOCKET_TYPE, class CPCD &);
+ CPCDAPISession(FILE * f, CPCD & cpcd);
+ ~CPCDAPISession();
+
+ virtual void runSession();
+ virtual void stopSession();
+ void loadFile();
+
+ void defineProcess(Parser_t::Context & ctx, const class Properties & args);
+ void undefineProcess(Parser_t::Context & ctx, const class Properties & args);
+ void startProcess(Parser_t::Context & ctx, const class Properties & args);
+ void stopProcess(Parser_t::Context & ctx, const class Properties & args);
+ void showProcess(Parser_t::Context & ctx, const class Properties & args);
+ void listProcesses(Parser_t::Context & ctx, const class Properties & args);
+};
+
+class CPCDAPIService : public SocketServer::Service {
+ class CPCD & m_cpcd;
+public:
+ CPCDAPIService(class CPCD & cpcd) : m_cpcd(cpcd) {}
+
+ CPCDAPISession * newSession(NDB_SOCKET_TYPE theSock){
+ return new CPCDAPISession(theSock, m_cpcd);
+ }
+};
+
+#endif
diff --git a/storage/ndb/src/cw/cpcd/CPCD.cpp b/storage/ndb/src/cw/cpcd/CPCD.cpp
new file mode 100644
index 00000000000..69a7b840528
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/CPCD.cpp
@@ -0,0 +1,435 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+#include "APIService.hpp"
+#include "CPCD.hpp"
+#include <NdbMutex.h>
+
+#include "common.hpp"
+
+extern const ParserRow<CPCDAPISession> commands[];
+
+
+CPCD::CPCD() {
+ loadingProcessList = false;
+ m_processes.clear();
+ m_monitor = NULL;
+ m_monitor = new Monitor(this);
+ m_procfile = "ndb_cpcd.db";
+}
+
+CPCD::~CPCD() {
+ if(m_monitor != NULL) {
+ delete m_monitor;
+ m_monitor = NULL;
+ }
+}
+
+int
+CPCD::findUniqueId() {
+ int id;
+ bool ok = false;
+ m_processes.lock();
+
+ while(!ok) {
+ ok = true;
+ id = random() % 8192; /* Don't want so big numbers */
+
+ if(id == 0)
+ ok = false;
+
+ for(size_t i = 0; i<m_processes.size(); i++) {
+ if(m_processes[i]->m_id == id)
+ ok = false;
+ }
+ }
+ m_processes.unlock();
+ return id;
+}
+
+bool
+CPCD::defineProcess(RequestStatus * rs, Process * arg){
+ if(arg->m_id == -1)
+ arg->m_id = findUniqueId();
+
+ Guard tmp(m_processes);
+
+ for(size_t i = 0; i<m_processes.size(); i++) {
+ Process * proc = m_processes[i];
+
+ if((strcmp(arg->m_name.c_str(), proc->m_name.c_str()) == 0) &&
+ (strcmp(arg->m_group.c_str(), proc->m_group.c_str()) == 0)) {
+ /* Identical names in the same group */
+ rs->err(AlreadyExists, "Name already exists");
+ return false;
+ }
+
+ if(arg->m_id == proc->m_id) {
+ /* Identical ID numbers */
+ rs->err(AlreadyExists, "Id already exists");
+ return false;
+ }
+ }
+
+ m_processes.push_back(arg, false);
+
+ notifyChanges();
+ report(arg->m_id, CPCEvent::ET_PROC_USER_DEFINE);
+
+ return true;
+}
+
+bool
+CPCD::undefineProcess(CPCD::RequestStatus *rs, int id) {
+
+ Guard tmp(m_processes);
+
+ Process * proc = 0;
+ size_t i;
+ for(i = 0; i < m_processes.size(); i++) {
+ if(m_processes[i]->m_id == id) {
+ proc = m_processes[i];
+ break;
+ }
+ }
+
+ if(proc == 0){
+ rs->err(NotExists, "No such process");
+ return false;
+ }
+
+ switch(proc->m_status){
+ case RUNNING:
+ case STOPPED:
+ case STOPPING:
+ case STARTING:
+ proc->stop();
+ m_processes.erase(i, false /* Already locked */);
+ }
+
+
+ notifyChanges();
+
+ report(id, CPCEvent::ET_PROC_USER_UNDEFINE);
+
+ return true;
+}
+
+bool
+CPCD::startProcess(CPCD::RequestStatus *rs, int id) {
+
+ Process * proc = 0;
+ {
+
+ Guard tmp(m_processes);
+
+ for(size_t i = 0; i < m_processes.size(); i++) {
+ if(m_processes[i]->m_id == id) {
+ proc = m_processes[i];
+ break;
+ }
+ }
+
+ if(proc == 0){
+ rs->err(NotExists, "No such process");
+ return false;
+ }
+
+ switch(proc->m_status){
+ case STOPPED:
+ proc->m_status = STARTING;
+ if(proc->start() != 0){
+ rs->err(Error, "Failed to start");
+ return false;
+ }
+ break;
+ case STARTING:
+ rs->err(Error, "Already starting");
+ return false;
+ case RUNNING:
+ rs->err(Error, "Already started");
+ return false;
+ case STOPPING:
+ rs->err(Error, "Currently stopping");
+ return false;
+ }
+
+ notifyChanges();
+ }
+ report(id, CPCEvent::ET_PROC_USER_START);
+
+ return true;
+}
+
+bool
+CPCD::stopProcess(CPCD::RequestStatus *rs, int id) {
+
+ Guard tmp(m_processes);
+
+ Process * proc = 0;
+ for(size_t i = 0; i < m_processes.size(); i++) {
+ if(m_processes[i]->m_id == id) {
+ proc = m_processes[i];
+ break;
+ }
+ }
+
+ if(proc == 0){
+ rs->err(NotExists, "No such process");
+ return false;
+ }
+
+ switch(proc->m_status){
+ case STARTING:
+ case RUNNING:
+ proc->stop();
+ break;
+ case STOPPED:
+ rs->err(AlreadyStopped, "Already stopped");
+ return false;
+ break;
+ case STOPPING:
+ rs->err(Error, "Already stopping");
+ return false;
+ }
+
+ notifyChanges();
+
+ report(id, CPCEvent::ET_PROC_USER_START);
+
+ return true;
+}
+
+bool
+CPCD::notifyChanges() {
+ bool ret = true;
+ if(!loadingProcessList)
+ ret = saveProcessList();
+
+ m_monitor->signal();
+
+ return ret;
+}
+
+/* Must be called with m_processlist locked */
+bool
+CPCD::saveProcessList(){
+ char newfile[PATH_MAX+4];
+ char oldfile[PATH_MAX+4];
+ char curfile[PATH_MAX];
+ FILE *f;
+
+ /* Create the filenames that we will use later */
+ BaseString::snprintf(newfile, sizeof(newfile), "%s.new", m_procfile.c_str());
+ BaseString::snprintf(oldfile, sizeof(oldfile), "%s.old", m_procfile.c_str());
+ BaseString::snprintf(curfile, sizeof(curfile), "%s", m_procfile.c_str());
+
+ f = fopen(newfile, "w");
+
+ if(f == NULL) {
+ /* XXX What should be done here? */
+ logger.critical("Cannot open `%s': %s\n", newfile, strerror(errno));
+ return false;
+ }
+
+ for(size_t i = 0; i<m_processes.size(); i++){
+ m_processes[i]->print(f);
+ fprintf(f, "\n");
+
+ if(m_processes[i]->m_processType == TEMPORARY){
+ /**
+ * Interactive process should never be "restarted" on cpcd restart
+ */
+ continue;
+ }
+
+ if(m_processes[i]->m_status == RUNNING ||
+ m_processes[i]->m_status == STARTING){
+ fprintf(f, "start process\nid: %d\n\n", m_processes[i]->m_id);
+ }
+ }
+
+ fclose(f);
+ f = NULL;
+
+ /* This will probably only work on reasonably Unix-like systems. You have
+ * been warned...
+ *
+ * The motivation behind all this link()ing is that the daemon might
+ * crash right in the middle of updating the configuration file, and in
+ * that case we want to be sure that the old file is around until we are
+ * guaranteed that there is always at least one copy of either the old or
+ * the new configuration file left.
+ */
+
+ /* Remove an old config file if it exists */
+ unlink(oldfile);
+
+ if(link(curfile, oldfile) != 0) /* make a backup of the running config */
+ logger.error("Cannot rename '%s' -> '%s'", curfile, oldfile);
+ else {
+ if(unlink(curfile) != 0) { /* remove the running config file */
+ logger.critical("Cannot remove file '%s'", curfile);
+ return false;
+ }
+ }
+
+ if(link(newfile, curfile) != 0) { /* put the new config file in place */
+ printf("-->%d\n", __LINE__);
+
+ logger.critical("Cannot rename '%s' -> '%s': %s",
+ curfile, newfile, strerror(errno));
+ return false;
+ }
+
+ /* XXX Ideally we would fsync() the directory here, but I'm not sure if
+ * that actually works.
+ */
+
+ unlink(newfile); /* remove the temporary file */
+ unlink(oldfile); /* remove the old file */
+
+ logger.info("Process list saved as '%s'", curfile);
+
+ return true;
+}
+
+bool
+CPCD::loadProcessList(){
+ BaseString secondfile;
+ FILE *f;
+
+ loadingProcessList = true;
+
+ secondfile.assfmt("%s.new", m_procfile.c_str());
+
+ /* Try to open the config file */
+ f = fopen(m_procfile.c_str(), "r");
+
+ /* If it did not exist, try to open the backup. See the saveProcessList()
+ * method for an explanation why it is done this way.
+ */
+ if(f == NULL) {
+ f = fopen(secondfile.c_str(), "r");
+
+ if(f == NULL) {
+ /* XXX What to do here? */
+ logger.info("Configuration file `%s' not found",
+ m_procfile.c_str());
+ logger.info("Starting with empty configuration");
+ loadingProcessList = false;
+ return false;
+ } else {
+ logger.info("Configuration file `%s' missing",
+ m_procfile.c_str());
+ logger.info("Backup configuration file `%s' is used",
+ secondfile.c_str());
+ /* XXX Maybe we should just rename the backup file to the official
+ * name, and be done with it?
+ */
+ }
+ }
+
+ CPCDAPISession sess(f, *this);
+ sess.loadFile();
+ loadingProcessList = false;
+
+ size_t i;
+ Vector<int> temporary;
+ for(i = 0; i<m_processes.size(); i++){
+ Process * proc = m_processes[i];
+ proc->readPid();
+ if(proc->m_processType == TEMPORARY){
+ temporary.push_back(proc->m_id);
+ }
+ }
+
+ for(i = 0; i<temporary.size(); i++){
+ RequestStatus rs;
+ undefineProcess(&rs, temporary[i]);
+ }
+
+ /* Don't call notifyChanges here, as that would save the file we just
+ loaded */
+ m_monitor->signal();
+ return true;
+}
+
+MutexVector<CPCD::Process *> *
+CPCD::getProcessList() {
+ return &m_processes;
+}
+
+void
+CPCD::RequestStatus::err(enum RequestStatusCode status, const char *msg) {
+ m_status = status;
+ BaseString::snprintf(m_errorstring, sizeof(m_errorstring), "%s", msg);
+}
+
+#if 0
+void
+CPCD::sigchild(int pid){
+ m_processes.lock();
+ for(size_t i = 0; i<m_processes.size(); i++){
+ if(m_processes[i].m_pid == pid){
+ }
+ }
+ wait(pid, 0, 0);
+}
+#endif
+
+ /** Register event subscriber */
+void
+CPCD::do_register(EventSubscriber * sub){
+ m_subscribers.lock();
+ m_subscribers.push_back(sub, false);
+ m_subscribers.unlock();
+}
+
+EventSubscriber*
+CPCD::do_unregister(EventSubscriber * sub){
+ m_subscribers.lock();
+
+ for(size_t i = 0; i<m_subscribers.size(); i++){
+ if(m_subscribers[i] == sub){
+ m_subscribers.erase(i);
+ m_subscribers.unlock();
+ return sub;
+ }
+ }
+
+ m_subscribers.unlock();
+ return 0;
+}
+
+void
+CPCD::report(int id, CPCEvent::EventType t){
+ CPCEvent e;
+ e.m_time = time(0);
+ e.m_proc = id;
+ e.m_type = t;
+ m_subscribers.lock();
+ for(size_t i = 0; i<m_subscribers.size(); i++){
+ (* m_subscribers[i]).report(e);
+ }
+ m_subscribers.unlock();
+}
+
+template class MutexVector<EventSubscriber*>;
diff --git a/storage/ndb/src/cw/cpcd/CPCD.hpp b/storage/ndb/src/cw/cpcd/CPCD.hpp
new file mode 100644
index 00000000000..a5c0bef1dac
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/CPCD.hpp
@@ -0,0 +1,382 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CPCD_HPP
+#define CPCD_HPP
+
+#include <Vector.hpp>
+#include <Properties.hpp>
+#include <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbCondition.h>
+#include <BaseString.hpp>
+
+/* XXX Need to figure out how to do this for non-Unix systems */
+#define CPCD_DEFAULT_WORK_DIR "/var/run/ndb_cpcd"
+#define CPCD_DEFAULT_PROC_FILE "ndb_cpcd.conf"
+#define CPCD_DEFAULT_TCP_PORT 1234
+#define CPCD_DEFAULT_POLLING_INTERVAL 5 /* seconds */
+#define CPCD_DEFAULT_CONFIG_FILE "/etc/ndb_cpcd.conf"
+
+enum ProcessStatus {
+ STOPPED = 0,
+ STARTING = 1,
+ RUNNING = 2,
+ STOPPING = 3
+};
+
+enum ProcessType {
+ PERMANENT = 0,
+ TEMPORARY = 1
+};
+
+struct CPCEvent {
+ enum EventType {
+ ET_USER_CONNECT,
+ ET_USER_DISCONNECT,
+
+ ET_PROC_USER_DEFINE, // Defined proc
+ ET_PROC_USER_UNDEFINE, // Undefined proc
+ ET_PROC_USER_START, // Proc ordered to start
+ ET_PROC_USER_STOP, // Proc ordered to stop
+ ET_PROC_STATE_RUNNING, // exec returned(?) ok
+ ET_PROC_STATE_STOPPED // detected that proc is ! running
+ };
+
+ int m_proc;
+ time_t m_time;
+ EventType m_type;
+};
+
+struct EventSubscriber {
+ virtual void report(const CPCEvent &) = 0;
+};
+
+/**
+ * @brief Error codes for CPCD requests
+ */
+enum RequestStatusCode {
+ OK = 0, ///< Everything OK
+ Error = 1, ///< Generic error
+ AlreadyExists = 2, ///< Entry already exists in list
+ NotExists = 3, ///< Entry does not exist in list
+ AlreadyStopped = 4
+};
+
+/**
+ * @class CPCD
+ * @brief Manages processes, letting them be controlled with a TCP connection.
+ *
+ * The class implementing the Cluster Process Control Daemon
+ */
+class CPCD {
+public:
+ /** @brief Describes the status of a client request */
+ class RequestStatus {
+ public:
+ /** @brief Constructs an empty RequestStatus */
+ RequestStatus() { m_status = OK; m_errorstring[0] = '\0'; };
+
+ /** @brief Sets an errorcode and a printable message */
+ void err(enum RequestStatusCode, const char *);
+
+ /** @brief Returns the error message */
+ char *getErrMsg() { return m_errorstring; };
+
+ /** @brief Returns the error code */
+ enum RequestStatusCode getStatus() { return m_status; };
+ private:
+ enum RequestStatusCode m_status;
+ char m_errorstring[256];
+ };
+ /**
+ * @brief Manages a process
+ */
+ class Process {
+ int m_pid;
+ public:
+ /**
+ * @brief Constructs and empty Process
+ */
+ Process(const Properties & props, class CPCD *cpcd);
+ /**
+ * @brief Monitors the process
+ *
+ * The process is started or stopped as needed.
+ */
+ void monitor();
+
+ /**
+ * @brief Checks if the process is running or not
+ *
+ * @return
+ * - true if the process is running,
+ * - false if the process is not running
+ */
+ bool isRunning();
+
+ /** @brief Starts the process */
+ int start();
+
+ /** @brief Stops the process */
+ void stop();
+
+ /**
+ * @brief Reads the pid from stable storage
+ *
+ * @return The pid number
+ */
+ int readPid();
+
+ /**
+ * @brief Writes the pid from stable storage
+ *
+ * @return
+ * - 0 if successful
+ - -1 and sets errno if an error occured
+ */
+ int writePid(int pid);
+
+ /**
+ * @brief Prints a textual description of the process on a file
+ */
+ void print(FILE *);
+
+ /** Id number of the Process.
+ *
+ * @note This is not the same as a pid. This number is used in the
+ * protocol, and will not be changed if a processes is restarted.
+ */
+ int m_id;
+
+ /** @brief The name shown to the user */
+ BaseString m_name;
+
+ /** @brief Used to group a number of processes */
+ BaseString m_group;
+
+ /** @brief Environment variables
+ *
+ * Environmentvariables to add for the process.
+ *
+ * @note
+ * - The environment cpcd started with is preserved
+ * - There is no way to delete variables
+ */
+ BaseString m_env;
+
+ /** @brief Path to the binary to run */
+ BaseString m_path;
+
+ /** @brief Arguments to the process.
+ *
+ * @note
+ * - This includes argv[0].
+ * - If no argv[0] is given, argv[0] will be set to m_path.
+ */
+ BaseString m_args;
+
+ /**
+ * @brief Type of process
+ *
+ * Either set to "interactive" or "permanent".
+ */
+ BaseString m_type;
+ ProcessType m_processType;
+
+ /**
+ * @brief Working directory
+ *
+ * Working directory the process will start in.
+ */
+ BaseString m_cwd;
+
+ /**
+ * @brief Owner of the process.
+ *
+ * @note This will not affect the process' uid or gid;
+ * it is only used for managemental purposes.
+ * @see m_runas
+ */
+ BaseString m_owner;
+
+ /**
+ * @bried Run as
+ * @note This affects uid
+ * @see m_owner
+ */
+ BaseString m_runas;
+
+ /**
+ * @brief redirection for stdin
+ */
+ BaseString m_stdin;
+
+ /**
+ * @brief redirection for stdout
+ */
+ BaseString m_stdout;
+
+ /**
+ * @brief redirection for stderr
+ */
+ BaseString m_stderr;
+
+ /** @brief Status of the process */
+ enum ProcessStatus m_status;
+
+ /**
+ * @brief ulimits for process
+ * @desc Format c:unlimited d:0 ...
+ */
+ BaseString m_ulimit;
+ private:
+ class CPCD *m_cpcd;
+ void do_exec();
+ };
+
+ /**
+ * @brief Starts and stops processes as needed
+ *
+ * At a specified interval (default 5 seconds) calls the monitor function
+ * of all the processes in the CPCDs list, causing the to start or
+ * stop, depending on the configuration.
+ */
+ class Monitor {
+ public:
+ /** Creates a new CPCD::Monitor object, connected to the specified
+ * CPCD.
+ * A new thread will be created, which will poll the processes of
+ * the CPCD at the specifed interval.
+ */
+ Monitor(CPCD *cpcd, int poll = CPCD_DEFAULT_POLLING_INTERVAL);
+
+ /** Stops the monitor, but does not stop the processes */
+ ~Monitor();
+
+ /** Runs the monitor thread. */
+ void run();
+
+ /** Signals configuration changes to the monitor thread, causing it to
+ * do the check without waiting for the timeout */
+ void signal();
+ private:
+ class CPCD *m_cpcd;
+ struct NdbThread *m_monitorThread;
+ bool m_monitorThreadQuitFlag;
+ struct NdbCondition *m_changeCondition;
+ NdbMutex *m_changeMutex;
+ int m_pollingInterval; /* seconds */
+ };
+
+ /** @brief Constructs a CPCD object */
+ CPCD();
+
+ /**
+ * @brief Destroys a CPCD object,
+ * but does not stop the processes it manages
+ */
+ ~CPCD();
+
+ /** Adds a Process to the CPCDs list of managed Processes.
+ *
+ * @note The process will not be started until it is explicitly
+ * marked as running with CPCD::startProcess().
+ *
+ * @return
+ * - true if the addition was successful,
+ * - false if not
+ * - RequestStatus will be filled in with a suitable error
+ * if an error occured.
+ */
+ bool defineProcess(RequestStatus *rs, Process * arg);
+
+ /** Removes a Process from the CPCD.
+ *
+ * @note A Process that is running cannot be removed.
+ *
+ * @return
+ * - true if the removal was successful,
+ * - false if not
+ * - The RequestStatus will be filled in with a suitable error
+ * if an error occured.
+ */
+ bool undefineProcess(RequestStatus *rs, int id);
+
+ /** Marks a Process for starting.
+ *
+ * @note The fact that a process has started does not mean it will actually
+ * start properly. This command only makes sure the CPCD will
+ * try to start it.
+ *
+ * @return
+ * - true if the marking was successful
+ * - false if not
+ * - RequestStatus will be filled in with a suitable error
+ * if an error occured.
+ */
+ bool startProcess(RequestStatus *rs, int id);
+
+ /** Marks a Process for stopping.
+ *
+ * @return
+ * - true if the marking was successful
+ * - false if not
+ * - The RequestStatus will be filled in with a suitable error
+ * if an error occured.
+ */
+ bool stopProcess(RequestStatus *rs, int id);
+
+ /** Generates a list of processes, and sends them to the CPCD client */
+ bool listProcesses(RequestStatus *rs, MutexVector<const char *> &);
+
+ /** Set to true while the CPCD is reading the configuration file */
+ bool loadingProcessList;
+
+ /** Saves the list of Processes and their status to the configuration file.
+ * Called whenever the configuration is changed.
+ */
+ bool saveProcessList();
+
+ /** Loads the list of Processes and their status from the configuration
+ * file.
+ * @note This function should only be called when the CPCD is starting,
+ * calling it at other times will cause unspecified behaviour.
+ */
+ bool loadProcessList();
+
+ /** Returns the list of processes */
+ MutexVector<Process *> *getProcessList();
+
+ /** The list of processes. Should not be used directly */
+ MutexVector<Process *> m_processes;
+
+ /** Register event subscriber */
+ void do_register(EventSubscriber * sub);
+ EventSubscriber* do_unregister(EventSubscriber * sub);
+
+private:
+ friend class Process;
+ bool notifyChanges();
+ int findUniqueId();
+ BaseString m_procfile;
+ Monitor *m_monitor;
+
+ void report(int id, CPCEvent::EventType);
+ MutexVector<EventSubscriber *> m_subscribers;
+};
+
+#endif
diff --git a/storage/ndb/src/cw/cpcd/Makefile.am b/storage/ndb/src/cw/cpcd/Makefile.am
new file mode 100644
index 00000000000..75f557b2af7
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/Makefile.am
@@ -0,0 +1,20 @@
+
+ndbbin_PROGRAMS = ndb_cpcd
+
+ndb_cpcd_SOURCES = main.cpp CPCD.cpp Process.cpp APIService.cpp Monitor.cpp common.cpp
+
+LDADD_LOC = \
+ $(top_builddir)/ndb/src/libndbclient.la \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/strings/libmystrings.a @NDB_SCI_LIBS@
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_util.mk.am
+
+ndb_cpcd_LDFLAGS = @ndb_bin_am_ldflags@
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp:
diff --git a/storage/ndb/src/cw/cpcd/Monitor.cpp b/storage/ndb/src/cw/cpcd/Monitor.cpp
new file mode 100644
index 00000000000..141de926d4d
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/Monitor.cpp
@@ -0,0 +1,79 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbThread.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+
+#include "CPCD.hpp"
+#include "common.hpp"
+
+static void *
+monitor_thread_create_wrapper(void * arg) {
+ CPCD::Monitor *mon = (CPCD::Monitor *)arg;
+ mon->run();
+ return NULL;
+}
+
+CPCD::Monitor::Monitor(CPCD *cpcd, int poll) {
+ m_cpcd = cpcd;
+ m_pollingInterval = poll;
+ m_changeCondition = NdbCondition_Create();
+ m_changeMutex = NdbMutex_Create();
+ m_monitorThread = NdbThread_Create(monitor_thread_create_wrapper,
+ (NDB_THREAD_ARG*) this,
+ 32768,
+ "ndb_cpcd_monitor",
+ NDB_THREAD_PRIO_MEAN);
+ m_monitorThreadQuitFlag = false;
+}
+
+CPCD::Monitor::~Monitor() {
+ NdbThread_Destroy(&m_monitorThread);
+ NdbCondition_Destroy(m_changeCondition);
+ NdbMutex_Destroy(m_changeMutex);
+}
+
+void
+CPCD::Monitor::run() {
+ while(1) {
+ NdbMutex_Lock(m_changeMutex);
+ NdbCondition_WaitTimeout(m_changeCondition,
+ m_changeMutex,
+ m_pollingInterval * 1000);
+
+ MutexVector<CPCD::Process *> &proc = *m_cpcd->getProcessList();
+
+ proc.lock();
+
+ for(size_t i = 0; i < proc.size(); i++) {
+ proc[i]->monitor();
+ }
+
+ proc.unlock();
+
+ NdbMutex_Unlock(m_changeMutex);
+ }
+}
+
+void
+CPCD::Monitor::signal() {
+ NdbCondition_Signal(m_changeCondition);
+}
+
+template class MutexVector<CPCD::Process*>;
diff --git a/storage/ndb/src/cw/cpcd/Process.cpp b/storage/ndb/src/cw/cpcd/Process.cpp
new file mode 100644
index 00000000000..2509f34e882
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/Process.cpp
@@ -0,0 +1,479 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <BaseString.hpp>
+#include <InputStream.hpp>
+
+#include "common.hpp"
+#include "CPCD.hpp"
+
+#include <pwd.h>
+#ifdef HAVE_GETRLIMIT
+#include <sys/resource.h>
+#endif
+
+void
+CPCD::Process::print(FILE * f){
+ fprintf(f, "define process\n");
+ fprintf(f, "id: %d\n", m_id);
+ fprintf(f, "name: %s\n", m_name.c_str() ? m_name.c_str() : "");
+ fprintf(f, "group: %s\n", m_group.c_str() ? m_group.c_str() : "");
+ fprintf(f, "env: %s\n", m_env.c_str() ? m_env.c_str() : "");
+ fprintf(f, "path: %s\n", m_path.c_str() ? m_path.c_str() : "");
+ fprintf(f, "args: %s\n", m_args.c_str() ? m_args.c_str() : "");
+ fprintf(f, "type: %s\n", m_type.c_str() ? m_type.c_str() : "");
+ fprintf(f, "cwd: %s\n", m_cwd.c_str() ? m_cwd.c_str() : "");
+ fprintf(f, "owner: %s\n", m_owner.c_str() ? m_owner.c_str() : "");
+ fprintf(f, "runas: %s\n", m_runas.c_str() ? m_runas.c_str() : "");
+ fprintf(f, "stdin: %s\n", m_stdin.c_str() ? m_stdin.c_str() : "");
+ fprintf(f, "stdout: %s\n", m_stdout.c_str() ? m_stdout.c_str() : "");
+ fprintf(f, "stderr: %s\n", m_stderr.c_str() ? m_stderr.c_str() : "");
+ fprintf(f, "ulimit: %s\n", m_ulimit.c_str() ? m_ulimit.c_str() : "");
+}
+
+CPCD::Process::Process(const Properties & props, class CPCD *cpcd) {
+ m_id = -1;
+ m_pid = -1;
+ props.get("id", (Uint32 *) &m_id);
+ props.get("name", m_name);
+ props.get("group", m_group);
+ props.get("env", m_env);
+ props.get("path", m_path);
+ props.get("args", m_args);
+ props.get("cwd", m_cwd);
+ props.get("owner", m_owner);
+ props.get("type", m_type);
+ props.get("runas", m_runas);
+
+ props.get("stdin", m_stdin);
+ props.get("stdout", m_stdout);
+ props.get("stderr", m_stderr);
+ props.get("ulimit", m_ulimit);
+ m_status = STOPPED;
+
+ if(strcasecmp(m_type.c_str(), "temporary") == 0){
+ m_processType = TEMPORARY;
+ } else {
+ m_processType = PERMANENT;
+ }
+
+ m_cpcd = cpcd;
+}
+
+void
+CPCD::Process::monitor() {
+ switch(m_status) {
+ case STARTING:
+ break;
+ case RUNNING:
+ if(!isRunning()){
+ m_cpcd->report(m_id, CPCEvent::ET_PROC_STATE_STOPPED);
+ if(m_processType == TEMPORARY){
+ m_status = STOPPED;
+ } else {
+ start();
+ }
+ }
+ break;
+ case STOPPED:
+ assert(!isRunning());
+ break;
+ case STOPPING:
+ break;
+ }
+}
+
+bool
+CPCD::Process::isRunning() {
+
+ if(m_pid <= 1){
+ //logger.critical("isRunning(%d) invalid pid: %d", m_id, m_pid);
+ return false;
+ }
+ /* Check if there actually exists a process with such a pid */
+ errno = 0;
+ int s = kill((pid_t)-m_pid, 0); /* Sending "signal" 0 to a process only
+ * checkes if the process actually exists */
+ if(s != 0) {
+ switch(errno) {
+ case EPERM:
+ logger.critical("Not enough privileges to control pid %d\n", m_pid);
+ break;
+ case ESRCH:
+ /* The pid in the file does not exist, which probably means that it
+ has died, or the file contains garbage for some other reason */
+ break;
+ default:
+ logger.critical("Cannot not control pid %d: %s\n", m_pid, strerror(errno));
+ break;
+ }
+ return false;
+ }
+ return true;
+}
+
+int
+CPCD::Process::readPid() {
+ if(m_pid != -1){
+ logger.critical("Reading pid while != -1(%d)", m_pid);
+ return m_pid;
+ }
+
+ char filename[PATH_MAX*2+1];
+ char buf[1024];
+ FILE *f;
+
+ memset(buf, 0, sizeof(buf));
+
+ BaseString::snprintf(filename, sizeof(filename), "%d", m_id);
+
+ f = fopen(filename, "r");
+
+ if(f == NULL){
+ return -1; /* File didn't exist */
+ }
+
+ errno = 0;
+ size_t r = fread(buf, 1, sizeof(buf), f);
+ fclose(f);
+ if(r > 0)
+ m_pid = strtol(buf, (char **)NULL, 0);
+
+ if(errno == 0){
+ return m_pid;
+ }
+
+ return -1;
+}
+
+int
+CPCD::Process::writePid(int pid) {
+ char tmpfilename[PATH_MAX+1+4+8];
+ char filename[PATH_MAX*2+1];
+ FILE *f;
+
+ BaseString::snprintf(tmpfilename, sizeof(tmpfilename), "tmp.XXXXXX");
+ BaseString::snprintf(filename, sizeof(filename), "%d", m_id);
+
+ int fd = mkstemp(tmpfilename);
+ if(fd < 0) {
+ logger.error("Cannot open `%s': %s\n", tmpfilename, strerror(errno));
+ return -1; /* Couldn't open file */
+ }
+
+ f = fdopen(fd, "w");
+
+ if(f == NULL) {
+ logger.error("Cannot open `%s': %s\n", tmpfilename, strerror(errno));
+ return -1; /* Couldn't open file */
+ }
+
+ fprintf(f, "%d", pid);
+ fclose(f);
+
+ if(rename(tmpfilename, filename) == -1){
+ logger.error("Unable to rename from %s to %s", tmpfilename, filename);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+setup_environment(const char *env) {
+ char **p;
+ p = BaseString::argify("", env);
+ for(int i = 0; p[i] != NULL; i++){
+ /*int res = */ putenv(p[i]);
+ }
+}
+
+static
+int
+set_ulimit(const BaseString & pair){
+#ifdef HAVE_GETRLIMIT
+ errno = 0;
+ Vector<BaseString> list;
+ pair.split(list, ":");
+ if(list.size() != 2){
+ logger.error("Unable to process ulimit: split >%s< list.size()=%d",
+ pair.c_str(), list.size());
+ return -1;
+ }
+
+ int res;
+ rlim_t value = RLIM_INFINITY;
+ if(!(list[1].trim() == "unlimited")){
+ value = atoi(list[1].c_str());
+ }
+
+ struct rlimit rlp;
+#define _RLIMIT_FIX(x) { res = getrlimit(x,&rlp); if(!res){ rlp.rlim_cur = value; res = setrlimit(x, &rlp); }}
+
+ if(list[0].trim() == "c"){
+ _RLIMIT_FIX(RLIMIT_CORE);
+ } else if(list[0] == "d"){
+ _RLIMIT_FIX(RLIMIT_DATA);
+ } else if(list[0] == "f"){
+ _RLIMIT_FIX(RLIMIT_FSIZE);
+ } else if(list[0] == "n"){
+ _RLIMIT_FIX(RLIMIT_NOFILE);
+ } else if(list[0] == "s"){
+ _RLIMIT_FIX(RLIMIT_STACK);
+ } else if(list[0] == "t"){
+ _RLIMIT_FIX(RLIMIT_CPU);
+ } else {
+ res= -11;
+ errno = EINVAL;
+ }
+ if(res){
+ logger.error("Unable to process ulimit: %s res=%d error=%d(%s)",
+ pair.c_str(), res, errno, strerror(errno));
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+void
+CPCD::Process::do_exec() {
+ size_t i;
+ setup_environment(m_env.c_str());
+
+ char **argv = BaseString::argify(m_path.c_str(), m_args.c_str());
+
+ if(strlen(m_cwd.c_str()) > 0) {
+ int err = chdir(m_cwd.c_str());
+ if(err == -1) {
+ BaseString err;
+ logger.error("%s: %s\n", m_cwd.c_str(), strerror(errno));
+ _exit(1);
+ }
+ }
+
+ Vector<BaseString> ulimit;
+ m_ulimit.split(ulimit);
+ for(i = 0; i<ulimit.size(); i++){
+ if(ulimit[i].trim().length() > 0 && set_ulimit(ulimit[i]) != 0){
+ _exit(1);
+ }
+ }
+
+ int fd = open("/dev/null", O_RDWR, 0);
+ if(fd == -1) {
+ logger.error("Cannot open `/dev/null': %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ BaseString * redirects[] = { &m_stdin, &m_stdout, &m_stderr };
+ int fds[3];
+ for(i = 0; i<3; i++){
+ if(redirects[i]->empty()){
+#ifndef DEBUG
+ dup2(fd, i);
+#endif
+ continue;
+ }
+
+ if((* redirects[i]) == "2>&1" && i == 2){
+ dup2(fds[1], 2);
+ continue;
+ }
+
+ /**
+ * Make file
+ */
+ int flags = 0;
+ int mode = S_IRUSR | S_IWUSR ;
+ if(i == 0){
+ flags |= O_RDONLY;
+ } else {
+ flags |= O_WRONLY | O_CREAT | O_APPEND;
+ }
+ int f = fds[i]= open(redirects[i]->c_str(), flags, mode);
+ if(f == -1){
+ logger.error("Cannot redirect %d to/from '%s' : %s\n", i,
+ redirects[i]->c_str(), strerror(errno));
+ _exit(1);
+ }
+ dup2(f, i);
+ }
+
+ /* Close all filedescriptors */
+ for(i = STDERR_FILENO+1; (int)i < getdtablesize(); i++)
+ close(i);
+
+ execv(m_path.c_str(), argv);
+ /* XXX If we reach this point, an error has occurred, but it's kind of hard
+ * to report it, because we've closed all files... So we should probably
+ * create a new logger here */
+ logger.error("Exec failed: %s\n", strerror(errno));
+ /* NOTREACHED */
+}
+
+int
+CPCD::Process::start() {
+ /* We need to fork() twice, so that the second child (grandchild?) can
+ * become a daemon. The original child then writes the pid file,
+ * so that the monitor knows the pid of the new process, and then
+ * exit()s. That way, the monitor process can pickup the pid, and
+ * the running process is a daemon.
+ *
+ * This is a bit tricky but has the following advantages:
+ * - the cpcd can die, and "reconnect" to the monitored clients
+ * without restarting them.
+ * - the cpcd does not have to wait() for the childs. init(1) will
+ * take care of that.
+ */
+ logger.info("Starting %d: %s", m_id, m_name.c_str());
+ m_status = STARTING;
+
+ int pid = -1;
+ switch(m_processType){
+ case TEMPORARY:{
+ /**
+ * Simple fork
+ * don't ignore child
+ */
+ switch(pid = fork()) {
+ case 0: /* Child */
+ setsid();
+ writePid(getpgrp());
+ if(runas(m_runas.c_str()) == 0){
+ signal(SIGCHLD, SIG_DFL);
+ do_exec();
+ }
+ _exit(1);
+ break;
+ case -1: /* Error */
+ logger.error("Cannot fork: %s\n", strerror(errno));
+ m_status = STOPPED;
+ return -1;
+ break;
+ default: /* Parent */
+ logger.debug("Started temporary %d : pid=%d", m_id, pid);
+ m_cpcd->report(m_id, CPCEvent::ET_PROC_STATE_RUNNING);
+ break;
+ }
+ break;
+ }
+ case PERMANENT:{
+ /**
+ * PERMANENT
+ */
+ switch(fork()) {
+ case 0: /* Child */
+ signal(SIGCHLD, SIG_IGN);
+ switch(pid = fork()) {
+ case 0: /* Child */
+ setsid();
+ writePid(getpgrp());
+ if(runas(m_runas.c_str()) != 0){
+ _exit(1);
+ }
+ signal(SIGCHLD, SIG_DFL);
+ do_exec();
+ _exit(1);
+ /* NOTREACHED */
+ break;
+ case -1: /* Error */
+ logger.error("Cannot fork: %s\n", strerror(errno));
+ writePid(-1);
+ _exit(1);
+ break;
+ default: /* Parent */
+ logger.debug("Started permanent %d : pid=%d", m_id, pid);
+ _exit(0);
+ break;
+ }
+ break;
+ case -1: /* Error */
+ logger.error("Cannot fork: %s\n", strerror(errno));
+ m_status = STOPPED;
+ return -1;
+ break;
+ default: /* Parent */
+ m_cpcd->report(m_id, CPCEvent::ET_PROC_STATE_RUNNING);
+ break;
+ }
+ break;
+ }
+ default:
+ logger.critical("Unknown process type");
+ return -1;
+ }
+
+ while(readPid() < 0){
+ sched_yield();
+ }
+
+ errno = 0;
+ pid_t pgid = getpgid(pid);
+
+ if(pgid != -1 && pgid != m_pid){
+ logger.error("pgid and m_pid don't match: %d %d (%d)", pgid, m_pid, pid);
+ }
+
+ if(isRunning()){
+ m_status = RUNNING;
+ return 0;
+ }
+ m_status = STOPPED;
+ return -1;
+}
+
+void
+CPCD::Process::stop() {
+
+ char filename[PATH_MAX*2+1];
+ BaseString::snprintf(filename, sizeof(filename), "%d", m_id);
+ unlink(filename);
+
+ if(m_pid <= 1){
+ logger.critical("Stopping process with bogus pid: %d id: %d",
+ m_pid, m_id);
+ return;
+ }
+ m_status = STOPPING;
+
+ errno = 0;
+ int ret = kill(-m_pid, SIGTERM);
+ switch(ret) {
+ case 0:
+ logger.debug("Sent SIGTERM to pid %d", (int)-m_pid);
+ break;
+ default:
+ logger.debug("kill pid: %d : %s", (int)-m_pid, strerror(errno));
+ break;
+ }
+
+ if(isRunning()){
+ errno = 0;
+ ret = kill(-m_pid, SIGKILL);
+ switch(ret) {
+ case 0:
+ logger.debug("Sent SIGKILL to pid %d", (int)-m_pid);
+ break;
+ default:
+ logger.debug("kill pid: %d : %s\n", (int)-m_pid, strerror(errno));
+ break;
+ }
+ }
+
+ m_pid = -1;
+ m_status = STOPPED;
+}
diff --git a/storage/ndb/src/cw/cpcd/common.cpp b/storage/ndb/src/cw/cpcd/common.cpp
new file mode 100644
index 00000000000..53c0e4d5a64
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/common.cpp
@@ -0,0 +1,98 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "common.hpp"
+#include <logger/Logger.hpp>
+#include <pwd.h>
+
+#include <Properties.hpp>
+#include <BaseString.hpp>
+
+int debug = 0;
+
+Logger logger;
+
+int
+runas(const char * user){
+ if(user == 0 || strlen(user) == 0){
+ return 0;
+ }
+ struct passwd * pw = getpwnam(user);
+ if(pw == 0){
+ logger.error("Can't find user to %s", user);
+ return -1;
+ }
+ uid_t uid = pw->pw_uid;
+ gid_t gid = pw->pw_gid;
+ int res = setgid(gid);
+ if(res != 0){
+ logger.error("Can't change group to %s(%d)", user, gid);
+ return res;
+ }
+
+ res = setuid(uid);
+ if(res != 0){
+ logger.error("Can't change user to %s(%d)", user, uid);
+ }
+ return res;
+}
+
+int
+insert(const char * pair, Properties & p){
+ BaseString tmp(pair);
+
+ tmp.trim(" \t\n\r");
+
+ Vector<BaseString> split;
+ tmp.split(split, ":=", 2);
+
+ if(split.size() != 2)
+ return -1;
+
+ p.put(split[0].trim().c_str(), split[1].trim().c_str());
+
+ return 0;
+}
+
+int
+insert_file(FILE * f, class Properties& p, bool break_on_empty){
+ if(f == 0)
+ return -1;
+
+ while(!feof(f)){
+ char buf[1024];
+ fgets(buf, 1024, f);
+ BaseString tmp = buf;
+
+ if(tmp.length() > 0 && tmp.c_str()[0] == '#')
+ continue;
+
+ if(insert(tmp.c_str(), p) != 0 && break_on_empty)
+ break;
+ }
+
+ return 0;
+}
+
+int
+insert_file(const char * filename, class Properties& p){
+ FILE * f = fopen(filename, "r");
+ int res = insert_file(f, p);
+ if(f) fclose(f);
+ return res;
+}
diff --git a/storage/ndb/src/cw/cpcd/common.hpp b/storage/ndb/src/cw/cpcd/common.hpp
new file mode 100644
index 00000000000..4f5f702762f
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/common.hpp
@@ -0,0 +1,36 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 __CPCD_COMMON_HPP_INCLUDED__
+#define __CPCD_COMMON_HPP_INCLUDED__
+
+#include <ndb_global.h>
+#include <logger/Logger.hpp>
+#if 0
+#include <getarg.h>
+#endif
+
+extern int debug;
+
+extern Logger logger;
+
+int runas(const char * user);
+int insert(const char * pair, class Properties & p);
+
+int insert_file(const char * filename, class Properties&);
+int insert_file(FILE *, class Properties&, bool break_on_empty = false);
+
+#endif /* ! __CPCD_COMMON_HPP_INCLUDED__ */
diff --git a/storage/ndb/src/cw/cpcd/main.cpp b/storage/ndb/src/cw/cpcd/main.cpp
new file mode 100644
index 00000000000..9bbd5e484d4
--- /dev/null
+++ b/storage/ndb/src/cw/cpcd/main.cpp
@@ -0,0 +1,187 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h> /* Needed for mkdir(2) */
+#include <my_sys.h>
+#include <my_getopt.h>
+#include <mysql_version.h>
+#include <ndb_version.h>
+
+#include "CPCD.hpp"
+#include "APIService.hpp"
+#include <NdbMain.h>
+#include <NdbSleep.h>
+#include <BaseString.hpp>
+#include <logger/Logger.hpp>
+#include <logger/FileLogHandler.hpp>
+#include <logger/SysLogHandler.hpp>
+
+#include "common.hpp"
+
+static const char *work_dir = CPCD_DEFAULT_WORK_DIR;
+static short unsigned int port;
+static int use_syslog;
+static const char *logfile = NULL;
+static const char *config_file = CPCD_DEFAULT_CONFIG_FILE;
+static const char *user = 0;
+
+static struct my_option my_long_options[] =
+{
+ { "work-dir", 'w', "Work directory",
+ (gptr*) &work_dir, (gptr*) &work_dir, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "port", 'p', "TCP port to listen on",
+ (gptr*) &port, (gptr*) &port, 0,
+ GET_INT, REQUIRED_ARG, CPCD_DEFAULT_TCP_PORT, 0, 0, 0, 0, 0 },
+ { "syslog", 'S', "Log events to syslog",
+ (gptr*) &use_syslog, (gptr*) &use_syslog, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "logfile", 'L', "File to log events to",
+ (gptr*) &logfile, (gptr*) &logfile, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "debug", 'D', "Enable debug mode",
+ (gptr*) &debug, (gptr*) &debug, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "config", 'c', "Config file",
+ (gptr*) &config_file, (gptr*) &config_file, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "user", 'u', "Run as user",
+ (gptr*) &user, (gptr*) &user, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ return 0;
+}
+
+static CPCD * g_cpcd = 0;
+#if 0
+extern "C" static void sig_child(int signo, siginfo_t*, void*);
+#endif
+
+const char *progname = "ndb_cpcd";
+
+int main(int argc, char** argv){
+ int save_argc= argc;
+ char** save_argv= argv;
+ const char *load_default_groups[]= { "ndb_cpcd",0 };
+ MY_INIT(argv[0]);
+
+ load_defaults("ndb_cpcd",load_default_groups,&argc,&argv);
+ if (handle_options(&argc, &argv, my_long_options, get_one_option)) {
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+ exit(1);
+ }
+
+ logger.setCategory(progname);
+ logger.enable(Logger::LL_ALL);
+
+ if(debug)
+ logger.createConsoleHandler();
+
+ if(user && runas(user) != 0){
+ logger.critical("Unable to change user: %s", user);
+ _exit(1);
+ }
+
+ if(logfile != NULL){
+ BaseString tmp;
+ if(logfile[0] != '/')
+ tmp.append(work_dir);
+ tmp.append(logfile);
+ logger.addHandler(new FileLogHandler(tmp.c_str()));
+ }
+
+ if(use_syslog)
+ logger.addHandler(new SysLogHandler());
+
+ logger.info("Starting");
+
+ CPCD cpcd;
+ g_cpcd = &cpcd;
+
+ /* XXX This will probably not work on !unix */
+ int err = mkdir(work_dir, S_IRWXU | S_IRGRP | S_IROTH);
+ if(err != 0) {
+ switch(errno) {
+ case EEXIST:
+ break;
+ default:
+ fprintf(stderr, "Cannot mkdir %s: %s\n", work_dir, strerror(errno));
+ exit(1);
+ }
+ }
+
+ if(strlen(work_dir) > 0){
+ logger.debug("Changing dir to '%s'", work_dir);
+ if((err = chdir(work_dir)) != 0){
+ fprintf(stderr, "Cannot chdir %s: %s\n", work_dir, strerror(errno));
+ exit(1);
+ }
+ }
+
+ cpcd.loadProcessList();
+
+ SocketServer * ss = new SocketServer();
+ CPCDAPIService * serv = new CPCDAPIService(cpcd);
+ if(!ss->setup(serv, &port)){
+ logger.critical("Cannot setup server: %s", strerror(errno));
+ sleep(1);
+ delete ss;
+ delete serv;
+ return 1;
+ }
+
+ ss->startServer();
+
+ {
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+#if 0
+ struct sigaction act;
+ act.sa_handler = 0;
+ act.sa_sigaction = sig_child;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGCHLD, &act, 0);
+#endif
+ }
+
+ logger.debug("Start completed");
+ while(true) NdbSleep_MilliSleep(1000);
+
+ delete ss;
+ return 0;
+}
+
+#if 0
+extern "C"
+void
+sig_child(int signo, siginfo_t* info, void*){
+ printf("signo: %d si_signo: %d si_errno: %d si_code: %d si_pid: %d\n",
+ signo,
+ info->si_signo,
+ info->si_errno,
+ info->si_code,
+ info->si_pid);
+
+}
+#endif
diff --git a/storage/ndb/src/cw/test/socketclient/Makefile b/storage/ndb/src/cw/test/socketclient/Makefile
new file mode 100644
index 00000000000..04f11f031e5
--- /dev/null
+++ b/storage/ndb/src/cw/test/socketclient/Makefile
@@ -0,0 +1,24 @@
+include .defs.mk
+
+TYPE :=
+
+BIN_TARGET := socketclient
+
+
+
+CCFLAGS_LOC += -I../../util/ -I../../cpcd/
+
+LIBS_LOC += -L$(NDB_TOP)/lib/ -L$(EXTERNAL_LIB_DIR)/sci
+
+LIBS_SPEC += -lsocketclient
+
+
+SOURCES = socketClientTest.cpp
+
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+
+
+
diff --git a/storage/ndb/src/cw/test/socketclient/socketClientTest.cpp b/storage/ndb/src/cw/test/socketclient/socketClientTest.cpp
new file mode 100644
index 00000000000..423c196aa43
--- /dev/null
+++ b/storage/ndb/src/cw/test/socketclient/socketClientTest.cpp
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <socket_io.h>
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbSleep.h>
+#include "SocketService.hpp"
+#include "SocketRegistry.hpp"
+#include "SocketClient.hpp"
+#include "ClientInterface.hpp"
+
+#include <InputStream.hpp>
+
+#include <Parser.hpp>
+
+NDB_MAIN(socketclient) {
+
+
+ if(argc<3) {
+ printf("wrong args: socketclient <hostname> <port>\n");
+ return 0;
+ }
+ const char * remotehost = argv[1];
+ const int port = atoi(argv[2]);
+
+
+ ClientInterface * ci = new ClientInterface(2);
+ ci->connectCPCDdaemon(remotehost,port);
+
+ /*ci->listProcesses(remotehost);
+
+ ci->startProcess(remotehost, "1247");
+
+ ci->stopProcess(remotehost, "1247");*/
+
+ ci->defineProcess(remotehost, "ndb", "ndb-cluster1", "envirnm", "/ndb/bin",
+ "-i", "permanent", "/ndb/ndb.2", "team");
+
+ ci->startProcess(remotehost, "1247");
+
+ ci->listProcesses(remotehost);
+
+ //ci->undefineProcess(remotehost, "1247");
+
+ ci->disconnectCPCDdaemon(remotehost);
+}
diff --git a/storage/ndb/src/cw/util/ClientInterface.cpp b/storage/ndb/src/cw/util/ClientInterface.cpp
new file mode 100644
index 00000000000..627b622f1dd
--- /dev/null
+++ b/storage/ndb/src/cw/util/ClientInterface.cpp
@@ -0,0 +1,185 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ClientInterface.hpp"
+
+
+
+ClientInterface::ClientInterface(Uint32 maxNoOfCPCD) {
+ sr = new SocketRegistry<SocketService>(maxNoOfCPCD);
+ ss = new SocketService();
+}
+
+
+ClientInterface::~ClientInterface() {
+ delete sr;
+ delete ss;
+
+}
+
+
+void ClientInterface::connectCPCDdaemon(const char * remotehost, Uint16 port)
+{
+ sr->createSocketClient(remotehost, port);
+}
+
+void ClientInterface::disconnectCPCDdaemon(const char * remotehost)
+{
+ sr->removeSocketClient(remotehost);
+}
+
+void ClientInterface::removeCPCDdaemon(const char * remotehost)
+{
+ sr->removeSocketClient(remotehost);
+}
+
+void ClientInterface::startProcess(const char * remotehost, char * id) {
+ char buf[255] = "start process ";
+ char str[80];
+ char line[10];
+
+ strcpy(line, id);
+ strcpy(str, "id:");
+ strcat(str, line);
+ strcat(str, "\n\n");
+ strcat(buf, str);
+ printf("Request: %s\n", buf);
+
+ sr->performSend(buf,255,remotehost);
+ sr->syncPerformReceive(remotehost, *ss, 0);
+ ss->getPropertyObject();
+}
+
+void ClientInterface::stopProcess(const char * remotehost, char * id) {
+ char buf[255] = "stop process ";
+ char str[80];
+ char line[10];
+
+ strcpy(line, id);
+ strcpy(str, "id:");
+ strcat(str, line);
+ strcat(str, "\n\n");
+ strcat(buf, str);
+ printf("Request: %s\n", buf);
+
+ sr->performSend(buf,255,remotehost);
+ sr->syncPerformReceive(remotehost, *ss, 0);
+ ss->getPropertyObject();
+}
+
+void ClientInterface::defineProcess(const char * remotehost, char * name,
+ char * group, char * env, char * path,
+ char * args, char * type, char * cwd, char * owner){
+ char buf[255] = "define process ";
+ char str[80];
+ char line[10];
+
+ strcpy(line, name);
+ strcpy(str, "name:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, group);
+ strcpy(str, "group:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, env);
+ strcpy(str, "env:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, path);
+ strcpy(str, "path:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, args);
+ strcpy(str, "args:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, type);
+ strcpy(str, "type:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, cwd);
+ strcpy(str, "cwd:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, " \n");
+
+ strcpy(line, owner);
+ strcpy(str, "owner:");
+ strcat(str, line);
+ strcat(buf, str);
+ strcat(buf, "\n\n");
+
+ printf("Request: %s\n", buf);
+
+ sr->performSend(buf,255,remotehost);
+ sr->syncPerformReceive(remotehost, *ss, 0);
+ ss->getPropertyObject();
+}
+
+void ClientInterface::undefineProcess(const char * remotehost, char * id){
+ char buf[255] = "undefine process ";
+ char str[80];
+ char line[10];
+
+ strcpy(line, id);
+ strcpy(str, "id:");
+ strcat(str, line);
+ strcat(str, "\n\n");
+ strcat(buf, str);
+ printf("Request: %s\n", buf);
+
+ sr->performSend(buf,255,remotehost);
+ sr->syncPerformReceive(remotehost, *ss, 0);
+ ss->getPropertyObject();
+}
+
+void ClientInterface::listProcesses(const char * remotehost) {
+ char buf[255]="list processes\n\n";
+ printf("Request: %s\n", buf);
+ sr->performSend(buf,255,remotehost);
+ sr->syncPerformReceive(remotehost, *ss, 0);
+ ss->getPropertyObject();
+}
+
+void ClientInterface::showProcess(const char * remotehost, char * id) {
+ char buf[255] = "show process ";
+ char str[80];
+ char line[10];
+
+ strcpy(line, id);
+ strcpy(str, "id:");
+ strcat(str, line);
+ strcat(str, "\n\n");
+ strcat(buf, str);
+ printf("Request: %s\n", buf);
+
+ sr->performSend(buf,255,remotehost);
+ sr->syncPerformReceive(remotehost, *ss, 0);
+ ss->getPropertyObject();
+}
diff --git a/storage/ndb/src/cw/util/ClientInterface.hpp b/storage/ndb/src/cw/util/ClientInterface.hpp
new file mode 100644
index 00000000000..66ecfe05197
--- /dev/null
+++ b/storage/ndb/src/cw/util/ClientInterface.hpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CLIENT_IF_HPP
+#define CLIENT_IF_HPP
+#include <ndb_global.h>
+#include <Parser.hpp>
+#include <InputStream.hpp>
+#include <Parser.hpp>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include "SocketRegistry.hpp"
+#include "SocketService.hpp"
+
+class ClientInterface {
+private:
+ SocketService * ss;
+ SocketRegistry<SocketService> * sr;
+
+public:
+ ClientInterface(Uint32 maxNoOfCPC);
+ ~ClientInterface();
+ void startProcess(const char * remotehost, char * id);
+ void stopProcess(const char * remotehost, char * id);
+ void defineProcess(const char * remotehost, char * name, char * group,
+ char * env, char * path, char * args, char * type,
+ char * cwd, char * owner);
+ void undefineProcess(const char * remotehost, char * id);
+ void listProcesses(const char * remotehost);
+ void showProcess(const char * remotehost, char * id);
+ void connectCPCDdaemon(const char * remotehost, Uint16 port);
+ void disconnectCPCDdaemon(const char * remotehost);
+ void removeCPCDdaemon(const char * remotehost);
+
+};
+#endif
diff --git a/storage/ndb/src/cw/util/Makefile b/storage/ndb/src/cw/util/Makefile
new file mode 100644
index 00000000000..f5ab16721be
--- /dev/null
+++ b/storage/ndb/src/cw/util/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+TYPE := ndbapi
+
+PIC_ARCHIVE := Y
+ARCHIVE_TARGET := socketclient
+
+# Source files of non-templated classes (.cpp files)
+SOURCES = ClientInterface.cpp SocketService.cpp SocketClient.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/cw/util/SocketRegistry.cpp b/storage/ndb/src/cw/util/SocketRegistry.cpp
new file mode 100644
index 00000000000..1dbb402f7c9
--- /dev/null
+++ b/storage/ndb/src/cw/util/SocketRegistry.cpp
@@ -0,0 +1,213 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "SocketRegistry.hpp"
+#include <Parser.hpp>
+
+template<class T>
+SocketRegistry<T>::SocketRegistry(Uint32 maxSocketClients) {
+
+}
+
+
+template<class T>
+SocketRegistry<T>::~SocketRegistry() {
+ delete [] m_socketClients;
+}
+
+template<class T>
+bool
+SocketRegistry<T>::createSocketClient(const char * host, Uint16 port) {
+
+ if(port == 0)
+ return false;
+ if(host==NULL)
+ return false;
+
+ SocketClient * socketClient = new SocketClient(host, port);
+
+ if(socketClient->openSocket() < 0 || socketClient == NULL) {
+ ndbout << "could not connect" << endl;
+ delete socketClient;
+ return false;
+ }
+ else {
+ m_socketClients[m_nSocketClients] = socketClient;
+ m_nSocketClients++;
+ }
+ return true;
+}
+
+template<class T>
+int
+SocketRegistry<T>::pollSocketClients(Uint32 timeOutMillis) {
+
+
+
+ // Return directly if there are no TCP transporters configured
+ if (m_nSocketClients == 0){
+ tcpReadSelectReply = 0;
+ return 0;
+ }
+ struct timeval timeout;
+ timeout.tv_sec = timeOutMillis / 1000;
+ timeout.tv_usec = (timeOutMillis % 1000) * 1000;
+
+
+ NDB_SOCKET_TYPE maxSocketValue = 0;
+
+ // Needed for TCP/IP connections
+ // The read- and writeset are used by select
+
+ FD_ZERO(&tcpReadset);
+
+ // Prepare for sending and receiving
+ for (Uint32 i = 0; i < m_nSocketClients; i++) {
+ SocketClient * t = m_socketClients[i];
+
+ // If the socketclient is connected
+ if (t->isConnected()) {
+
+ const NDB_SOCKET_TYPE socket = t->getSocket();
+ // Find the highest socket value. It will be used by select
+ if (socket > maxSocketValue)
+ maxSocketValue = socket;
+
+ // Put the connected transporters in the socket read-set
+ FD_SET(socket, &tcpReadset);
+ }
+ }
+
+ // The highest socket value plus one
+ maxSocketValue++;
+
+ tcpReadSelectReply = select(maxSocketValue, &tcpReadset, 0, 0, &timeout);
+#ifdef NDB_WIN32
+ if(tcpReadSelectReply == SOCKET_ERROR)
+ {
+ NdbSleep_MilliSleep(timeOutMillis);
+ }
+#endif
+
+ return tcpReadSelectReply;
+
+}
+
+template<class T>
+bool
+SocketRegistry<T>::performSend(const char * buf,
+ Uint32 len,
+ const char * remotehost)
+{
+ SocketClient * socketClient;
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ socketClient = m_socketClients[i];
+ if(strcmp(socketClient->gethostname(), remotehost)==0) {
+ if(socketClient->isConnected()) {
+ if(socketClient->writeSocket(buf, len)>0)
+ return true;
+ else
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+template<class T>
+int
+SocketRegistry<T>::performReceive(T & t) {
+ char buf[255] ; //temp. just for testing. must fix better
+
+ if(tcpReadSelectReply > 0){
+ for (Uint32 i=0; i<m_nSocketClients; i++) {
+ SocketClient *sc = m_socketClients[i];
+ const NDB_SOCKET_TYPE socket = sc->getSocket();
+ if(sc->isConnected() && FD_ISSET(socket, &tcpReadset)) {
+ t->runSession(socket,t);
+ }
+ }
+ return 1;
+ }
+ return 0;
+
+}
+
+
+
+template<class T>
+inline
+int
+SocketRegistry<T>::syncPerformReceive(char * host,
+ T & t,
+ Uint32 timeOutMillis) {
+ char buf[255] ; //temp. just for testing. must fix better
+ struct timeval timeout;
+ timeout.tv_sec = timeOutMillis / 1000;
+ timeout.tv_usec = (timeOutMillis % 1000) * 1000;
+ int reply;
+ SocketClient * sc;
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ sc = m_socketClients[i];
+ if(strcmp(sc->gethostname(), remotehost)==0) {
+ if(sc->isConnected()) {
+ FD_ZERO(&tcpReadset);
+ reply = select(sc->getSocket(), &tcpReadset, 0, 0, &timeout);
+ if(reply > 0) {
+ return t->runSession(sc->getSocket(), t);
+ }
+ }
+
+ }
+ }
+ return 0;
+}
+
+
+
+template<class T>
+bool
+SocketRegistry<T>::reconnect(const char * host){
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ SocketClient * socketClient = m_socketClients[i];
+ if(strcmp(socketClient->gethostname(), host)==0) {
+ if(!socketClient->isConnected()) {
+ if(socketClient->openSocket() > 0)
+ return true;
+ else return false;
+ }
+ }
+ }
+ return false;
+}
+
+template<class T>
+bool
+SocketRegistry<T>::removeSocketClient(const char * host){
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ SocketClient * socketClient = m_socketClients[i];
+ if(strcmp(socketClient->gethostname(), host)==0) {
+ if(!socketClient->isConnected()) {
+ if(socketClient->closeSocket() > 0) {
+ delete socketClient;
+ return true;
+ }
+ else return false;
+ }
+ }
+ }
+ return false;
+}
diff --git a/storage/ndb/src/cw/util/SocketRegistry.hpp b/storage/ndb/src/cw/util/SocketRegistry.hpp
new file mode 100644
index 00000000000..2b079156967
--- /dev/null
+++ b/storage/ndb/src/cw/util/SocketRegistry.hpp
@@ -0,0 +1,290 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SocketClientRegistry_H
+#define SocketClientRegistry_H
+
+#include <NdbTCP.h>
+#include <NdbOut.hpp>
+
+#include "SocketClient.hpp"
+
+template<class T>
+class SocketRegistry {
+
+public:
+ SocketRegistry(Uint32 maxSocketClients);
+ ~SocketRegistry();
+ /**
+ * creates and adds a SocketClient to m_socketClients[]
+ * @param host - host name
+ * @param port - port to connect to
+ */
+ bool createSocketClient(const char * host, const Uint16 port);
+
+ /**
+ * performReceive reads from sockets should do more stuff
+ */
+ int performReceive(T &);
+
+
+ /**
+ * performReceive reads from sockets should do more stuff
+ */
+ int syncPerformReceive(const char* ,T &, Uint32);
+
+
+ /**
+ * performSend sends a command to a host
+ */
+ bool performSend(const char * buf, Uint32 len, const char * remotehost);
+
+ /**
+ * pollSocketClients performs a select (for a max. of timeoutmillis) or
+ * until there is data to be read from any SocketClient
+ * @param timeOutMillis - select timeout
+ */
+ int pollSocketClients(Uint32 timeOutMillis);
+
+ /**
+ * reconnect tries to reconnect to a cpcd given its hostname
+ * @param host - name of host to reconnect to.
+ */
+ bool reconnect(const char * host);
+
+
+ /**
+ * removeSocketClient
+ * @param host - name of host for which to remove the SocketConnection
+ */
+ bool removeSocketClient(const char * host);
+
+private:
+ SocketClient** m_socketClients;
+ Uint32 m_maxSocketClients;
+ Uint32 m_nSocketClients;
+ int tcpReadSelectReply;
+ fd_set tcpReadset;
+
+
+};
+
+
+template<class T>
+inline
+SocketRegistry<T>::SocketRegistry(Uint32 maxSocketClients) {
+ m_maxSocketClients = maxSocketClients;
+ m_socketClients = new SocketClient * [m_maxSocketClients];
+ m_nSocketClients = 0;
+}
+
+
+template<class T>
+inline
+SocketRegistry<T>::~SocketRegistry() {
+ delete [] m_socketClients;
+}
+
+template<class T>
+inline
+bool
+SocketRegistry<T>::createSocketClient(const char * host, Uint16 port) {
+
+ if(port == 0)
+ return false;
+ if(host==NULL)
+ return false;
+
+ SocketClient * socketClient = new SocketClient(host, port);
+
+ if(socketClient->openSocket() < 0 || socketClient == NULL) {
+ ndbout << "could not connect" << endl;
+ delete socketClient;
+ return false;
+ }
+ else {
+ m_socketClients[m_nSocketClients] = socketClient;
+ m_nSocketClients++;
+ }
+ return true;
+}
+
+template<class T>
+inline
+int
+SocketRegistry<T>::pollSocketClients(Uint32 timeOutMillis) {
+
+
+
+ // Return directly if there are no TCP transporters configured
+ if (m_nSocketClients == 0){
+ tcpReadSelectReply = 0;
+ return 0;
+ }
+ struct timeval timeout;
+ timeout.tv_sec = timeOutMillis / 1000;
+ timeout.tv_usec = (timeOutMillis % 1000) * 1000;
+
+
+ NDB_SOCKET_TYPE maxSocketValue = 0;
+
+ // Needed for TCP/IP connections
+ // The read- and writeset are used by select
+
+ FD_ZERO(&tcpReadset);
+
+ // Prepare for sending and receiving
+ for (Uint32 i = 0; i < m_nSocketClients; i++) {
+ SocketClient * t = m_socketClients[i];
+
+ // If the socketclient is connected
+ if (t->isConnected()) {
+
+ const NDB_SOCKET_TYPE socket = t->getSocket();
+ // Find the highest socket value. It will be used by select
+ if (socket > maxSocketValue)
+ maxSocketValue = socket;
+
+ // Put the connected transporters in the socket read-set
+ FD_SET(socket, &tcpReadset);
+ }
+ }
+
+ // The highest socket value plus one
+ maxSocketValue++;
+
+ tcpReadSelectReply = select(maxSocketValue, &tcpReadset, 0, 0, &timeout);
+#ifdef NDB_WIN32
+ if(tcpReadSelectReply == SOCKET_ERROR)
+ {
+ NdbSleep_MilliSleep(timeOutMillis);
+ }
+#endif
+
+ return tcpReadSelectReply;
+
+}
+
+template<class T>
+inline
+bool
+SocketRegistry<T>::performSend(const char * buf, Uint32 len, const char * remotehost)
+{
+ SocketClient * socketClient;
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ socketClient = m_socketClients[i];
+ if(strcmp(socketClient->gethostname(), remotehost)==0) {
+ if(socketClient->isConnected()) {
+ if(socketClient->writeSocket(buf, len)>0)
+ return true;
+ else
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+template<class T>
+inline
+int
+SocketRegistry<T>::performReceive(T & t) {
+ char buf[255] ; //temp. just for testing. must fix better
+
+ if(tcpReadSelectReply > 0){
+ for (Uint32 i=0; i<m_nSocketClients; i++) {
+ SocketClient *sc = m_socketClients[i];
+ const NDB_SOCKET_TYPE socket = sc->getSocket();
+ if(sc->isConnected() && FD_ISSET(socket, &tcpReadset)) {
+ t->runSession(socket,t);
+ }
+ }
+ return 1;
+ }
+ return 0;
+
+}
+
+
+
+template<class T>
+inline
+int
+SocketRegistry<T>::syncPerformReceive(const char * remotehost,
+ T & t,
+ Uint32 timeOutMillis) {
+ char buf[255] ; //temp. just for testing. must fix better
+ struct timeval timeout;
+ timeout.tv_sec = timeOutMillis / 1000;
+ timeout.tv_usec = (timeOutMillis % 1000) * 1000;
+ int reply;
+ SocketClient * sc;
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ sc = m_socketClients[i];
+ if(strcmp(sc->gethostname(), remotehost)==0) {
+ if(sc->isConnected()) {
+ /*FD_ZERO(&tcpReadset);
+ reply = select(sc->getSocket()+1, 0, 0, 0, &timeout);
+ reply=1;
+ if(reply > 0) {*/
+ t.runSession(sc->getSocket(), t);
+ //}
+ }
+
+ }
+ }
+}
+
+
+
+template<class T>
+inline
+bool
+SocketRegistry<T>::reconnect(const char * host){
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ SocketClient * socketClient = m_socketClients[i];
+ if(strcmp(socketClient->gethostname(), host)==0) {
+ if(!socketClient->isConnected()) {
+ if(socketClient->openSocket() > 0)
+ return true;
+ else return false;
+ }
+ }
+ }
+ return false;
+}
+
+template<class T>
+inline
+bool
+SocketRegistry<T>::removeSocketClient(const char * host){
+ for(Uint32 i=0; i < m_nSocketClients; i++) {
+ SocketClient * socketClient = m_socketClients[i];
+ if(strcmp(socketClient->gethostname(), host)==0) {
+ if(!socketClient->isConnected()) {
+ if(socketClient->closeSocket() > 0) {
+ delete socketClient;
+ return true;
+ }
+ else return false;
+ }
+ }
+ }
+ return false;
+}
+
+
+#endif // Define of SocketRegistry
diff --git a/storage/ndb/src/cw/util/SocketService.cpp b/storage/ndb/src/cw/util/SocketService.cpp
new file mode 100644
index 00000000000..b993ec8c2c1
--- /dev/null
+++ b/storage/ndb/src/cw/util/SocketService.cpp
@@ -0,0 +1,60 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <Parser.hpp>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include "SocketService.hpp"
+
+SocketService::SocketService() {
+
+}
+
+SocketService::~SocketService() {
+
+}
+
+int
+SocketService::runSession(NDB_SOCKET_TYPE socket, SocketService & ss){
+ InputStream *m_input = new SocketInputStream(socket);
+ char buf[255];
+
+ m_input->gets(buf,255);
+ ndbout_c("SocketService:received: %s\n", buf);
+ ndbout_c("This should now be parsed\n");
+ ndbout_c("and put in a property object.\n");
+ ndbout_c("The propery is then accessible from the ClientInterface.\n");
+ ndbout_c("by getPropertyObject.\n");
+ ndbout_c("At least this is the idea.");
+ /*Parser_t *m_parser =
+ new Parser<SocketService>(commands, *m_input, true, true, true);
+ */
+ /** to do
+ * add a proprty object to which the parser will put its result.
+ */
+
+ return 1 ; //succesful
+ //return 0; //unsuccesful
+
+}
+
+void
+SocketService::getPropertyObject() {
+ ndbout << "get property object. return to front end or something" << endl;
+}
+
+
diff --git a/storage/ndb/src/cw/util/SocketService.hpp b/storage/ndb/src/cw/util/SocketService.hpp
new file mode 100644
index 00000000000..7a0c3a2fd91
--- /dev/null
+++ b/storage/ndb/src/cw/util/SocketService.hpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SOCKET_SERVICE_HPP
+#define SOCKET_SERVICE_HPP
+#include <Parser.hpp>
+#include <InputStream.hpp>
+#include <Parser.hpp>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include "SocketRegistry.hpp"
+
+
+
+
+class SocketService {
+ friend class SocketRegistry<SocketService>;
+private:
+ typedef Parser<SocketService> Parser_t;
+ int runSession(NDB_SOCKET_TYPE socket, SocketService &);
+public:
+ void getPropertyObject();
+ SocketService();
+ ~SocketService();
+
+};
+
+
+
+
+
+
+#endif
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/SISCI_LIBRARY_WIN32.TXT b/storage/ndb/src/external/WIN32.x86/sci/lib/SISCI_LIBRARY_WIN32.TXT
new file mode 100644
index 00000000000..97fe959bb2c
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/SISCI_LIBRARY_WIN32.TXT
@@ -0,0 +1,77 @@
+SISCI_API LIBRARIES AND LINKING
+===============================
+
+
+/MD, /ML, /MT (Use Run-Time Library)
+
+
+
+sisci_api.lib - Single threaded
+
+sisci_api_md.lib - Multithreaded DLL
+
+sisci_api_mt.lib - Multithreaded
+
+
+
+
+With these libraries, you can select either single-threaded or multithreaded run-time routines,
+indicate that a multithreaded module is a dynamic-link library (DLL), and select the retail
+or debug version of the library.
+
+Note Having more than one copy of the run-time libraries in a process can cause problems,
+because static data in one copy is not shared with the other copy. To ensure that your process
+contains only one copy, avoid mixing static and dynamic versions of the run-time libraries.
+The linker will prevent you from linking with both static and dynamic versions within one .EXE file,
+but you can still end up with two (or more) copies of the run-time libraries.
+For example, a dynamic-link library linked with the static (non-DLL) versions of the run-time
+libraries can cause problems when used with an .EXE file that was linked with the dynamic (DLL)
+version of the run-time libraries. (You should also avoid mixing the debug and non-debug versions
+of the libraries in one process.)
+
+
+MD Multithreaded
+----------------
+
+/MD Multithreaded DLL Defines _MT and _DLL so that both multithread- and DLL-specific versions
+of the run-time routines are selected from the standard .H files. This option also causes the
+compiler to place the library name MSVCRT.LIB into the .OBJ file.
+Applications compiled with this option are statically linked to MSVCRT.LIB. This library provides
+a layer of code that allows the linker to resolve external references. The actual working code is
+contained in MSVCRT.DLL, which must be available at run time to applications linked with MSVCRT.LIB.
+
+
+/MDd Debug Multithreaded DLL Defines _DEBUG, _MT, and _DLL so that debug multithread- and DLL-specific
+versions of the run-time routines are selected from the standard .H files. It also causes the compiler
+to place the library name MSVCRTD.LIB into the .OBJ file.
+
+
+ML Single-Threaded
+------------------
+
+
+/ML Single-Threaded Causes the compiler to place the library name LIBC.LIB into the .OBJ file so
+that the linker will use LIBC.LIB to resolve external symbols. This is the compiler’s default action.
+LIBC.LIB does not provide multithread support.
+
+
+/MLd Debug Single-Threaded Defines _DEBUG and causes the compiler to place the library name LIBCD.LIB
+into the .OBJ file so that the linker will use LIBCD.LIB to resolve external symbols. LIBCD.LIB does
+not provide multithread support.
+
+
+MT Multithreaded
+----------------
+
+
+/MT Multithreaded Defines _MT so that multithread-specific versions of the run-time routines are
+selected from the standard header (.H) files. This option also causes the compiler to place the library
+name LIBCMT.LIB into the .OBJ file so that the linker will use LIBCMT.LIB to resolve external symbols.
+
+Either /MT or /MD (or their debug equivalents /MTd or /MDd) is required to create multithreaded programs.
+/MTd Debug Multithreaded Defines _DEBUG and _MT. Defining _MT causes multithread-specific versions of
+the run-time routines to be selected from the standard .H files. This option also causes the compiler
+to place the library name LIBCMTD.LIB into the .OBJ file so that the linker will use LIBCMTD.LIB to
+resolve external symbols. Either /MTd or /MDd (or their non-debug equivalents /MT or MD) is required to
+create multithreaded programs.
+
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/scilib.lib b/storage/ndb/src/external/WIN32.x86/sci/lib/scilib.lib
new file mode 100644
index 00000000000..572169a2016
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/scilib.lib
Binary files differ
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/scilib_md.lib b/storage/ndb/src/external/WIN32.x86/sci/lib/scilib_md.lib
new file mode 100644
index 00000000000..f18cba61336
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/scilib_md.lib
Binary files differ
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/scilib_mt.lib b/storage/ndb/src/external/WIN32.x86/sci/lib/scilib_mt.lib
new file mode 100644
index 00000000000..3e9982468ea
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/scilib_mt.lib
Binary files differ
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api.lib b/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api.lib
new file mode 100644
index 00000000000..3fbff6ec809
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api.lib
Binary files differ
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_md.lib b/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_md.lib
new file mode 100644
index 00000000000..1d8d42d1d35
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_md.lib
Binary files differ
diff --git a/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_mt.lib b/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_mt.lib
new file mode 100644
index 00000000000..017fad7ba31
--- /dev/null
+++ b/storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_mt.lib
Binary files differ
diff --git a/storage/ndb/src/kernel/Makefile.am b/storage/ndb/src/kernel/Makefile.am
new file mode 100644
index 00000000000..55d3c5a578f
--- /dev/null
+++ b/storage/ndb/src/kernel/Makefile.am
@@ -0,0 +1,75 @@
+SUBDIRS = error blocks vm
+
+include $(top_srcdir)/ndb/config/common.mk.am
+
+ndbbin_PROGRAMS = ndbd
+
+ndbd_SOURCES = main.cpp SimBlockList.cpp
+
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+INCLUDES += \
+ -Iblocks/cmvmi \
+ -Iblocks/dbacc \
+ -Iblocks/dbdict \
+ -Iblocks/dbdih \
+ -Iblocks/dblqh \
+ -Iblocks/dbtc \
+ -Iblocks/dbtup \
+ -Iblocks/ndbfs \
+ -Iblocks/ndbcntr \
+ -Iblocks/qmgr \
+ -Iblocks/trix \
+ -Iblocks/backup \
+ -Iblocks/dbutil \
+ -Iblocks/suma \
+ -Iblocks/grep \
+ -Iblocks/dbtux
+
+LDADD += \
+ blocks/cmvmi/libcmvmi.a \
+ blocks/dbacc/libdbacc.a \
+ blocks/dbdict/libdbdict.a \
+ blocks/dbdih/libdbdih.a \
+ blocks/dblqh/libdblqh.a \
+ blocks/dbtc/libdbtc.a \
+ blocks/dbtup/libdbtup.a \
+ blocks/ndbfs/libndbfs.a \
+ blocks/ndbcntr/libndbcntr.a \
+ blocks/qmgr/libqmgr.a \
+ blocks/trix/libtrix.a \
+ blocks/backup/libbackup.a \
+ blocks/dbutil/libdbutil.a \
+ blocks/suma/libsuma.a \
+ blocks/grep/libgrep.a \
+ blocks/dbtux/libdbtux.a \
+ vm/libkernel.a \
+ error/liberror.a \
+ $(top_builddir)/ndb/src/common/transporter/libtransporter.la \
+ $(top_builddir)/ndb/src/common/debugger/libtrace.la \
+ $(top_builddir)/ndb/src/common/debugger/signaldata/libsignaldataprint.la \
+ $(top_builddir)/ndb/src/common/logger/liblogger.la \
+ $(top_builddir)/ndb/src/common/mgmcommon/libmgmsrvcommon.la \
+ $(top_builddir)/ndb/src/mgmapi/libmgmapi.la \
+ $(top_builddir)/ndb/src/common/portlib/libportlib.la \
+ $(top_builddir)/ndb/src/common/util/libgeneral.la \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/strings/libmystrings.a @NDB_SCI_LIBS@
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: ndbd.dsp
+
+ndbd.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(ndbbin_PROGRAMS)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndbd_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
diff --git a/storage/ndb/src/kernel/SimBlockList.cpp b/storage/ndb/src/kernel/SimBlockList.cpp
new file mode 100644
index 00000000000..75a52ae0c4b
--- /dev/null
+++ b/storage/ndb/src/kernel/SimBlockList.cpp
@@ -0,0 +1,126 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "SimBlockList.hpp"
+#include <SimulatedBlock.hpp>
+#include <Cmvmi.hpp>
+#include <Ndbfs.hpp>
+#include <Dbacc.hpp>
+#include <Dbdict.hpp>
+#include <Dbdih.hpp>
+#include <Dblqh.hpp>
+#include <Dbtc.hpp>
+#include <Dbtup.hpp>
+#include <Ndbcntr.hpp>
+#include <Qmgr.hpp>
+#include <Trix.hpp>
+#include <Backup.hpp>
+#include <DbUtil.hpp>
+#include <Suma.hpp>
+#include <Grep.hpp>
+#include <Dbtux.hpp>
+#include <NdbEnv.h>
+
+#ifndef VM_TRACE
+#define NEW_BLOCK(B) new B
+#else
+enum SIMBLOCKLIST_DUMMY { A_VALUE = 0 };
+
+static
+void * operator new (size_t sz, SIMBLOCKLIST_DUMMY dummy){
+ char * tmp = (char *)malloc(sz);
+
+#ifndef NDB_PURIFY
+#ifdef VM_TRACE
+ const int initValue = 0xf3;
+#else
+ const int initValue = 0x0;
+#endif
+
+ const int p = (sz / 4096);
+ const int r = (sz % 4096);
+
+ for(int i = 0; i<p; i++)
+ memset(tmp+(i*4096), initValue, 4096);
+
+ if(r > 0)
+ memset(tmp+p*4096, initValue, r);
+
+#endif
+
+ return tmp;
+}
+#define NEW_BLOCK(B) new(A_VALUE) B
+#endif
+
+void
+SimBlockList::load(const Configuration & conf){
+ noOfBlocks = 16;
+ theList = new SimulatedBlock * [noOfBlocks];
+ Dbdict* dbdict = 0;
+ Dbdih* dbdih = 0;
+
+ SimulatedBlock * fs = 0;
+ {
+ Uint32 dl;
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ if(p && !ndb_mgm_get_int_parameter(p, CFG_DB_DISCLESS, &dl) && dl){
+ fs = NEW_BLOCK(VoidFs)(conf);
+ } else {
+ fs = NEW_BLOCK(Ndbfs)(conf);
+ }
+ }
+
+ theList[0] = NEW_BLOCK(Dbacc)(conf);
+ theList[1] = NEW_BLOCK(Cmvmi)(conf);
+ theList[2] = fs;
+ theList[3] = dbdict = NEW_BLOCK(Dbdict)(conf);
+ theList[4] = dbdih = NEW_BLOCK(Dbdih)(conf);
+ theList[5] = NEW_BLOCK(Dblqh)(conf);
+ theList[6] = NEW_BLOCK(Dbtc)(conf);
+ theList[7] = NEW_BLOCK(Dbtup)(conf);
+ theList[8] = NEW_BLOCK(Ndbcntr)(conf);
+ theList[9] = NEW_BLOCK(Qmgr)(conf);
+ theList[10] = NEW_BLOCK(Trix)(conf);
+ theList[11] = NEW_BLOCK(Backup)(conf);
+ theList[12] = NEW_BLOCK(DbUtil)(conf);
+ theList[13] = NEW_BLOCK(Suma)(conf);
+ theList[14] = NEW_BLOCK(Grep)(conf);
+ theList[15] = NEW_BLOCK(Dbtux)(conf);
+
+ // Metadata common part shared by block instances
+ ptrMetaDataCommon = new MetaData::Common(*dbdict, *dbdih);
+ for (int i = 0; i < noOfBlocks; i++)
+ theList[i]->setMetaDataCommon(ptrMetaDataCommon);
+}
+
+void
+SimBlockList::unload(){
+ if(theList != 0){
+ for(int i = 0; i<noOfBlocks; i++){
+ if(theList[i] != 0){
+ theList[i]->~SimulatedBlock();
+ free(theList[i]);
+ theList[i] = 0;
+ }
+ }
+ delete [] theList;
+ delete ptrMetaDataCommon;
+ theList = 0;
+ noOfBlocks = 0;
+ ptrMetaDataCommon = 0;
+ }
+}
diff --git a/storage/ndb/src/kernel/blocks/ERROR_codes.txt b/storage/ndb/src/kernel/blocks/ERROR_codes.txt
new file mode 100644
index 00000000000..a30021607cc
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ERROR_codes.txt
@@ -0,0 +1,451 @@
+Next QMGR 1
+Next NDBCNTR 1000
+Next NDBFS 2000
+Next DBACC 3002
+Next DBTUP 4013
+Next DBLQH 5042
+Next DBDICT 6006
+Next DBDIH 7174
+Next DBTC 8035
+Next CMVMI 9000
+Next BACKUP 10022
+Next DBUTIL 11002
+Next DBTUX 12007
+Next SUMA 13001
+
+TESTING NODE FAILURE, ARBITRATION
+---------------------------------
+
+911 - 919:
+Crash president when he starts to run in ArbitState 1-9.
+
+910: Crash new president after node crash
+
+ERROR CODES FOR TESTING NODE FAILURE, GLOBAL CHECKPOINT HANDLING:
+-----------------------------------------------------------------
+
+7000:
+Insert system error in master when global checkpoint is idle.
+
+7001:
+Insert system error in master after receiving GCP_PREPARE from
+all nodes in the cluster.
+
+7002:
+Insert system error in master after receiving GCP_NODEFINISH from
+all nodes in the cluster.
+
+7003:
+Insert system error in master after receiving GCP_SAVECONF from
+all nodes in the cluster.
+
+7004:
+Insert system error in master after completing global checkpoint with
+all nodes in the cluster.
+
+7005:
+Insert system error in GCP participant when receiving GCP_PREPARE.
+
+7006:
+Insert system error in GCP participant when receiving GCP_COMMIT.
+
+7007:
+Insert system error in GCP participant when receiving GCP_TCFINISHED.
+
+7008:
+Insert system error in GCP participant when receiving COPY_GCICONF.
+
+5000:
+Insert system error in GCP participant when receiving GCP_SAVEREQ.
+
+5007:
+Delay GCP_SAVEREQ by 10 secs
+
+ERROR CODES FOR TESTING NODE FAILURE, LOCAL CHECKPOINT HANDLING:
+-----------------------------------------------------------------
+
+7009:
+Insert system error in master when local checkpoint is idle.
+
+7010:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = CALCULATE_KEEP_GCI.
+
+7011:
+Stop local checkpoint in the state CALCULATE_KEEP_GCI.
+
+7012:
+Restart local checkpoint after stopping in CALCULATE_KEEP_GCI.
+
+Method:
+1) Error 7011 in master, wait until report of stopped.
+2) Error xxxx in participant to crash it.
+3) Error 7012 in master to start again.
+
+7013:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = COPY_GCI before sending COPY_GCIREQ.
+
+7014:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = TC_CLOPSIZE before sending TC_CLOPSIZEREQ.
+
+7015:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = START_LCP_ROUND before sending START_LCP_ROUND.
+
+7016:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = START_LCP_ROUND after receiving LCP_REPORT.
+
+7017:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = TAB_COMPLETED.
+
+7018:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = TAB_SAVED before sending DIH_LCPCOMPLETE.
+
+7019:
+Insert system error in master when local checkpoint is in the
+state clcpStatus = IDLE before sending CONTINUEB(ZCHECK_TC_COUNTER).
+
+7020:
+Insert system error in local checkpoint participant at reception of
+COPY_GCIREQ.
+
+7075: Master
+Don't send any LCP_FRAG_ORD(last=true)
+And crash when all have "not" been sent
+
+8000: Crash particpant when receiving TCGETOPSIZEREQ
+8001: Crash particpant when receiving TC_CLOPSIZEREQ
+5010: Crash any when receiving LCP_FRAGORD
+
+7021: Crash in master when receiving START_LCP_REQ
+7022: Crash in !master when receiving START_LCP_REQ
+
+7023: Crash in master when sending START_LCP_CONF
+7024: Crash in !master when sending START_LCP_CONF
+
+7025: Crash in master when receiving LCP_FRAG_REP
+7016: Crash in !master when receiving LCP_FRAG_REP
+
+7026: Crash in master when changing state to LCP_TAB_COMPLETED
+7017: Crash in !master when changing state to LCP_TAB_COMPLETED
+
+7027: Crash in master when changing state to LCP_TAB_SAVED
+7018: Crash in master when changing state to LCP_TAB_SAVED
+
+ERROR CODES FOR TESTING NODE FAILURE, FAILURE IN COPY FRAGMENT PROCESS:
+-----------------------------------------------------------------------
+
+5002:
+Insert node failure in starting node when receiving a tuple copied from the copy node
+as part of copy fragment process.
+5003:
+Insert node failure when receiving ABORT signal.
+
+5004:
+Insert node failure handling when receiving COMMITREQ.
+
+5005:
+Insert node failure handling when receiving COMPLETEREQ.
+
+5006:
+Insert node failure handling when receiving ABORTREQ.
+
+These error code can be combined with error codes for testing time-out
+handling in DBTC to ensure that node failures are also well handled in
+time-out handling. They can also be used to test multiple node failure
+handling.
+
+ERROR CODES FOR TESTING TIME-OUT HANDLING IN DBLQH
+-------------------------------------------------
+5011:
+Delay execution of COMMIT signal 2 seconds to generate time-out.
+
+5012 (use 5017):
+First delay execution of COMMIT signal 2 seconds to generate COMMITREQ.
+Delay execution of COMMITREQ signal 2 seconds to generate time-out.
+
+5013:
+Delay execution of COMPLETE signal 2 seconds to generate time-out.
+
+5014 (use 5018):
+First delay execution of COMPLETE signal 2 seconds to generate COMPLETEREQ.
+Delay execution of COMPLETEREQ signal 2 seconds to generate time-out.
+
+5015:
+Delay execution of ABORT signal 2 seconds to generate time-out.
+
+5016: (ABORTREQ only as part of take-over)
+Delay execution of ABORTREQ signal 2 seconds to generate time-out.
+
+5031: lqhKeyRef, ZNO_TC_CONNECT_ERROR
+5032: lqhKeyRef, ZTEMPORARY_REDO_LOG_FAILURE
+5033: lqhKeyRef, ZTAIL_PROBLEM_IN_LOG_ERROR
+
+5034: Don't pop scan queue
+
+5035: Delay ACC_CONTOPCONT
+
+5038: Drop LQHKEYREQ + set 5039
+5039: Drop ABORT + set 5003
+
+8048: Make TC not choose own node for simple/dirty read
+5041: Crash is receiving simple read from other TC on different node
+
+8050: Send TCKEYREF is operation is non local
+
+ERROR CODES FOR TESTING TIME-OUT HANDLING IN DBTC
+-------------------------------------------------
+8040:
+Delay execution of ABORTED signal 2 seconds to generate time-out.
+
+8041:
+Delay execution of COMMITTED signal 2 seconds to generate time-out.
+8042 (use 8046):
+Delay execution of COMMITTED signal 2 seconds to generate COMMITCONF.
+Delay execution of COMMITCONF signal 2 seconds to generate time-out.
+
+8043:
+Delay execution of COMPLETED signal 2 seconds to generate time-out.
+
+8044 (use 8047):
+Delay execution of COMPLETED signal 2 seconds to generate COMPLETECONF.
+Delay execution of COMPLETECONF signal 2 seconds to generate time-out.
+
+8045: (ABORTCONF only as part of take-over)
+Delay execution of ABORTCONF signal 2 seconds to generate time-out.
+
+ERROR CODES FOR TESTING TIME-OUT HANDLING IN DBTC
+-------------------------------------------------
+
+8003: Throw away a LQHKEYCONF in state STARTED
+8004: Throw away a LQHKEYCONF in state RECEIVING
+8005: Throw away a LQHKEYCONF in state REC_COMMITTING
+8006: Throw away a LQHKEYCONF in state START_COMMITTING
+
+8007: Ignore send of LQHKEYREQ in state STARTED
+8008: Ignore send of LQHKEYREQ in state START_COMMITTING
+
+8009: Ignore send of LQHKEYREQ+ATTRINFO in state STARTED
+8010: Ignore send of LQHKEYREQ+ATTRINFO in state START_COMMITTING
+
+8011: Abort at send of CONTINUEB(ZSEND_ATTRINFO) in state STARTED
+8012: Abort at send of CONTINUEB(ZSEND_ATTRINFO) in state START_COMMITTING
+
+8013: Ignore send of CONTINUEB(ZSEND_COMPLETE_LOOP) (should crash eventually)
+8014: Ignore send of CONTINUEB(ZSEND_COMMIT_LOOP) (should crash eventually)
+
+8015: Ignore ATTRINFO signal in DBTC in state REC_COMMITTING
+8016: Ignore ATTRINFO signal in DBTC in state RECEIVING
+
+8017: Return immediately from DIVERIFYCONF (should crash eventually)
+8018: Throw away a COMMITTED signal
+8019: Throw away a COMPLETED signal
+
+TESTING TAKE-OVER FUNCTIONALITY IN DBTC
+---------------------------------------
+
+8002: Crash when sending LQHKEYREQ
+8029: Crash when receiving LQHKEYCONF
+8030: Crash when receiving COMMITTED
+8031: Crash when receiving COMPLETED
+8020: Crash when all COMMITTED has arrived
+8021: Crash when all COMPLETED has arrived
+8022: Crash when all LQHKEYCONF has arrived
+
+COMBINATION OF TIME-OUT + CRASH
+-------------------------------
+
+8023 (use 8024): Ignore LQHKEYCONF and crash when ABORTED signal arrives by setting 8024
+8025 (use 8026): Ignore COMMITTED and crash when COMMITCONF signal arrives by setting 8026
+8027 (use 8028): Ignore COMPLETED and crash when COMPLETECONF signal arrives by setting 8028
+
+ABORT OF TCKEYREQ
+-----------------
+
+8032: No free TC records any more
+
+
+CMVMI
+-----
+9000 Set RestartOnErrorInsert to restart -n
+9998 Enter endless loop (trigger watchdog)
+9999 Crash system immediatly
+
+Test Crashes in handling node restarts
+--------------------------------------
+
+7121: Crash after receiving permission to start (START_PERMCONF) in starting
+ node.
+7122: Crash master when receiving request for permission to start (START_PERMREQ).
+7123: Crash any non-starting node when receiving information about a starting node
+ (START_INFOREQ)
+7124: Respond negatively on an info request (START_INFOREQ)
+7125: Stop an invalidate Node LCP process in the middle to test if START_INFOREQ
+ stopped by long-running processes are handled in a correct manner.
+7126: Allow node restarts for all nodes (used in conjunction with 7025)
+7127: Crash when receiving a INCL_NODEREQ message.
+7128: Crash master after receiving all INCL_NODECONF from all nodes
+7129: Crash master after receiving all INCL_NODECONF from all nodes and releasing
+ the lock on the dictionary
+7130: Crash starting node after receiving START_MECONF
+7131: Crash when receiving START_COPYREQ in master node
+7132: Crash when receiving START_COPYCONF in starting node
+
+DICT:
+6000 Crash during NR when receiving DICTSTARTREQ
+6001 Crash during NR when receiving SCHEMA_INFO
+6002 Crash during NR soon after sending GET_TABINFO_REQ
+
+LQH:
+5026 Crash when receiving COPY_ACTIVEREQ
+5027 Crash when receiving STAT_RECREQ
+
+Test Crashes in handling take over
+----------------------------------
+
+7133: Crash when receiving START_TOREQ
+7134: Crash master after receiving all START_TOCONF
+7135: Crash master after copying table 0 to starting node
+7136: Crash master after completing copy of tables
+7137: Crash master after adding a fragment before copying it
+7138: Crash when receiving CREATE_FRAGREQ in prepare phase
+7139: Crash when receiving CREATE_FRAGREQ in commit phase
+7140: Crash master when receiving all CREATE_FRAGCONF in prepare phase
+7141: Crash master when receiving all CREATE_FRAGCONF in commit phase
+7142: Crash master when receiving COPY_FRAGCONF
+7143: Crash master when receiving COPY_ACTIVECONF
+7144: Crash when receiving END_TOREQ
+7145: Crash master after receiving first END_TOCONF
+7146: Crash master after receiving all END_TOCONF
+7147: Crash master after receiving first START_TOCONF
+7148: Crash master after receiving first CREATE_FRAGCONF
+7152: Crash master after receiving first UPDATE_TOCONF
+7153: Crash master after receiving all UPDATE_TOCONF
+7154: Crash when receiving UPDATE_TOREQ
+7155: Crash master when completing writing start take over info
+7156: Crash master when completing writing end take over info
+
+Test failures in various states in take over functionality
+----------------------------------------------------------
+7157: Block take over at start take over
+7158: Block take over at sending of START_TOREQ
+7159: Block take over at selecting next fragment
+7160: Block take over at creating new fragment
+7161: Block take over at sending of CREATE_FRAGREQ in prepare phase
+7162: Block take over at sending of CREATE_FRAGREQ in commit phase
+7163: Block take over at sending of UPDATE_TOREQ at end of copy frag
+7164: Block take over at sending of END_TOREQ
+7169: Block take over at sending of UPDATE_TOREQ at end of copy
+
+5008: Crash at reception of EMPTY_LCPREQ (at master take over after NF)
+5009: Crash at sending of EMPTY_LCPCONF (at master take over after NF)
+
+Test Crashes in Handling Graceful Shutdown
+------------------------------------------
+7065: Crash when receiving STOP_PERMREQ in master
+7066: Crash when receiving STOP_PERMREQ in slave
+7067: Crash when receiving DIH_SWITCH_REPLICA_REQ
+7068: Crash when receiving DIH_SWITCH_REPLICA_CONF
+
+
+Backup Stuff:
+------------------------------------------
+10001: Crash on NODE_FAILREP in Backup coordinator
+10002: Crash on NODE_FAILREP when coordinatorTakeOver
+10003: Crash on PREP_CREATE_TRIG_{CONF/REF} (only coordinator)
+10004: Crash on START_BACKUP_{CONF/REF} (only coordinator)
+10005: Crash on CREATE_TRIG_{CONF/REF} (only coordinator)
+10006: Crash on WAIT_GCP_REF (only coordinator)
+10007: Crash on WAIT_GCP_CONF (only coordinator)
+10008: Crash on WAIT_GCP_CONF during start of backup (only coordinator)
+10009: Crash on WAIT_GCP_CONF during stop of backup (only coordinator)
+10010: Crash on BACKUP_FRAGMENT_CONF (only coordinator)
+10011: Crash on BACKUP_FRAGMENT_REF (only coordinator)
+10012: Crash on DROP_TRIG_{CONF/REF} (only coordinator)
+10013: Crash on STOP_BACKUP_{CONF/REF} (only coordinator)
+10014: Crash on DEFINE_BACKUP_REQ (participant)
+10015: Crash on START_BACKUP_REQ (participant)
+10016: Crash on BACKUP_FRAGMENT_REQ (participant)
+10017: Crash on SCAN_FRAGCONF (participant)
+10018: Crash on FSAPPENDCONF (participant)
+10019: Crash on TRIG_ATTRINFO (participant)
+10020: Crash on STOP_BACKUP_REQ (participant)
+10021: Crash on NODE_FAILREP in participant not becoming coordinator
+
+10022: Fake no backup records at DEFINE_BACKUP_REQ (participant)
+10023: Abort backup by error at reception of UTIL_SEQUENCE_CONF (code 300)
+10024: Abort backup by error at reception of DEFINE_BACKUP_CONF (code 301)
+10025: Abort backup by error at reception of CREATE_TRIG_CONF last (code 302)
+10026: Abort backup by error at reception of START_BACKUP_CONF (code 303)
+10027: Abort backup by error at reception of DEFINE_BACKUP_REQ at master (code 304)
+10028: Abort backup by error at reception of BACKUP_FRAGMENT_CONF at master (code 305)
+10029: Abort backup by error at reception of FSAPPENDCONF in slave (FileOrScanError = 5)
+10030: Simulate buffer full from trigger execution => abort backup
+
+11001: Send UTIL_SEQUENCE_REF (in master)
+
+5028: Crash when receiving LQHKEYREQ (in non-master)
+
+Failed Create Table:
+--------------------
+7173: Create table failed due to not sufficient number of fragment or
+ replica records.
+3001: Fail create 1st fragment
+4007 12001: Fail create 1st fragment
+4008 12002: Fail create 2nd fragment
+4009 12003: Fail create 1st attribute in 1st fragment
+4010 12004: Fail create last attribute in 1st fragment
+4011 12005: Fail create 1st attribute in 2nd fragment
+4012 12006: Fail create last attribute in 2nd fragment
+
+Drop Table/Index:
+-----------------
+4001: Crash on REL_TABMEMREQ in TUP
+4002: Crash on DROP_TABFILEREQ in TUP
+4003: Fail next trigger create in TUP
+8033: Fail next trigger create in TC
+8034: Fail next index create in TC
+
+
+
+System Restart:
+---------------
+
+5020: Force system to read pages form file when executing prepare operation record
+3000: Delay writing of datapages in ACC when LCP is started
+4000: Delay writing of datapages in TUP when LCP is started
+7070: Set TimeBetweenLcp to min value
+7071: Set TimeBetweenLcp to max value
+7072: Split START_FRAGREQ into several log nodes
+7073: Don't include own node in START_FRAGREQ
+7074: 7072 + 7073
+
+Scan:
+------
+
+5021: Crash when receiving SCAN_NEXTREQ if sender is own node
+5022: Crash when receiving SCAN_NEXTREQ if sender is NOT own node
+5023: Drop SCAN_NEXTREQ if sender is own node
+5024: Drop SCAN_NEXTREQ if sender is NOT own node
+5025: Delay SCAN_NEXTREQ 1 second if sender is NOT own node
+5030: Drop all SCAN_NEXTREQ until node is shutdown with SYSTEM_ERROR
+ because of scan fragment timeout
+
+Test routing of signals:
+-----------------------
+4006: Turn on routing of TRANSID_AI signals from TUP
+5029: Turn on routing of KEYINFO20 signals from LQH
+
+Ordered index:
+--------------
+
+Dbdict:
+-------
+6003 Crash in participant @ CreateTabReq::Prepare
+6004 Crash in participant @ CreateTabReq::Commit
+6005 Crash in participant @ CreateTabReq::CreateDrop
diff --git a/storage/ndb/src/kernel/blocks/Makefile.am b/storage/ndb/src/kernel/blocks/Makefile.am
new file mode 100644
index 00000000000..7ee90e6239f
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/Makefile.am
@@ -0,0 +1,19 @@
+SUBDIRS = \
+ cmvmi \
+ dbacc \
+ dbdict \
+ dbdih \
+ dblqh \
+ dbtc \
+ dbtup \
+ ndbfs \
+ ndbcntr \
+ qmgr \
+ trix \
+ backup \
+ dbutil \
+ suma \
+ grep \
+ dbtux
+
+windoze-dsp:
diff --git a/storage/ndb/src/kernel/blocks/NodeRestart.new.txt b/storage/ndb/src/kernel/blocks/NodeRestart.new.txt
new file mode 100644
index 00000000000..00ab8f0c208
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/NodeRestart.new.txt
@@ -0,0 +1,82 @@
+
+Master DIH Starting DIH Starting DICT
+---------------------- ---------------------- ---------------------
+
+ Check for sysfile
+ DIH_RESTARTCONF ->
+
+******************************************************************************
+* NDB_STTOR interal startphase = 1
+******************************************************************************
+
+ Read schema file
+
+******************************************************************************
+* NDB_STTOR interal startphase = 2
+******************************************************************************
+
+ <- START_PERMREQ
+
+XXX
+
+START_PERMCONF ->
+
+******************************************************************************
+* NDB_STTOR interal startphase = 3
+******************************************************************************
+
+ <- START_MEREQ
+
+START_RECREQ -> starting LQH
+ <- START_RECCONF
+
+For each table
+ COPY_TABREQ -> starting DIH
+
+DICTSTARTREQ -> starting DICT
+ GET_SCHEMA_INFOREQ
+ (to master DICT)
+
+ ->SCHEMA_INFO
+ (schema file)
+
+ 1) For each table
+ If TableStatus OK
+ ReadTableFile
+ else
+ GET_TABINFOREQ
+ 2) DIADDTABREQ->DIH
+
+ For each local frag
+ ADD_FRAG_REQ -> local DICT
+ DI_ADD_TAB_CONF
+ <- DICTSTARTCONF
+
+INCL_NODEREQ -> all DIH
+
+START_MECONF -> starting DIH
+ (including sysfile)
+
+******************************************************************************
+* NDB_STTOR interal startphase = 5
+******************************************************************************
+
+ <- START_COPYREQ
+
+START_TOREQ -> all DIH
+
+For each fragment
+ CREATE_FRAGREQ -> all DIH
+
+ COPY-DATA (LQHKEYREQ++)
+
+ UPDATE_TOREQ -> all DIH
+
+ COPY_ACTIVEREQ -> starting LQH
+
+ CREATE_FRAGREQ -> all DIH
+
+START_COPYCONF ->
+
+LOCAL CHECKPOINT
+
diff --git a/storage/ndb/src/kernel/blocks/NodeRestart.txt b/storage/ndb/src/kernel/blocks/NodeRestart.txt
new file mode 100644
index 00000000000..e9f277bb39e
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/NodeRestart.txt
@@ -0,0 +1,80 @@
+
+Master DIH Starting DIH Starting DICT
+---------------------- ---------------------- ---------------------
+
+ Check for sysfile
+ DIH_RESTARTCONF ->
+
+******************************************************************************
+* NDB_STTOR interal startphase = 1
+******************************************************************************
+
+ Read schema file
+
+******************************************************************************
+* NDB_STTOR interal startphase = 2
+******************************************************************************
+
+ <- START_PERMREQ
+
+XXX
+
+START_PERMCONF ->
+
+******************************************************************************
+* NDB_STTOR interal startphase = 3
+******************************************************************************
+
+ <- START_MEREQ
+
+START_RECREQ -> starting LQH
+ <- START_RECCONF
+
+DICTSTARTREQ -> starting DICT
+ GET_SCHEMA_INFOREQ
+ (to master DICT)
+
+ ->SCHEMA_INFO
+ (schema file)
+
+ 1) For each table
+ 1) If TableStatus match
+ ReadTableFile
+ else
+ GET_TABINFOREQ
+
+ <- DICTSTARTCONF
+
+For each table
+ COPY_TABREQ -> starting DIH
+
+INCL_NODEREQ -> all DIH
+
+START_MECONF -> starting DIH
+ (including sysfile)
+
+******************************************************************************
+* NDB_STTOR interal startphase = 5
+******************************************************************************
+
+ <- START_COPYREQ
+
+START_TOREQ -> all DIH
+
+For each fragment
+ ADD_FRAG_REQ -> local DICT -> LQHFRAGREQ -> starting LQH
+
+ CREATE_FRAGREQ -> all DIH
+
+ COPY-DATA (LQHKEYREQ++)
+
+ UPDATE_TOREQ -> all DIH
+
+ COPY_ACTIVEREQ -> starting LQH
+
+ CREATE_FRAGREQ -> all DIH
+
+START_COPYCONF ->
+
+LOCAL CHECKPOINT
+
diff --git a/storage/ndb/src/kernel/blocks/Start.txt b/storage/ndb/src/kernel/blocks/Start.txt
new file mode 100644
index 00000000000..3e805ebab55
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/Start.txt
@@ -0,0 +1,97 @@
+
+--- Start phase 1 - Qmgr -------------------------------------------
+
+1) Set timer 1 - TimeToWaitAlive
+
+2) Send CM_REGREQ to all connected(and connecting) nodes
+
+3) Wait until -
+a) The precident answers CM_REGCONF
+b) All nodes has answered and I'm the candidate -> election won
+c) 30s has passed and I'm the candidate -> election won
+d) TimeToWaitAlive has passed -> Failure to start
+
+When receiving CM_REGCONF
+4) Send CM_NODEINFOREQ to all connected(and connecting) nodes
+ reported in CM_REGCONF
+
+5) Wait until -
+a) All CM_NODEINFO_CONF has arrived
+b) TimeToWaitAlive has passed -> Failure to start
+
+6) Send CM_ACKADD to president
+
+7) Wait until -
+a) Receive CM_ADD(CommitNew) from president -> I'm in the qmgr cluster
+b) TimeToWaitAlive has passed -> Failure to start
+
+NOTE:
+30s is hardcoded in 3c.
+TimeToWaitAlive should be atleast X sec greater than 30s. i.e. 30+X sec
+to support "partial starts"
+
+NOTE:
+In 3b, a more correct number (instead of all) would be
+N-NG+1 where N is #nodes and NG is #node groups = (N/R where R is # replicas)
+But Qmgr has no notion about node groups or replicas
+
+--- Start phase X - Qmgr -------------------------------------------
+
+President - When accepting a CM_REGREQ
+1) Send CM_REGCONF to starting node
+2) Send CM_ADD(Prepare) to all started nodes + starting node
+3) Send CM_ADD(AddCommit) to all started nodes
+4) Send CM_ADD(CommitNew) to starting node
+
+Cluster participant -
+1) Wait for both CM_NODEINFOREQ from starting and CM_ADD(Prepare) from pres.
+2) Send CM_ACKADD(Prepare)
+3) Wait for CM_ADD(AddCommit) from president
+4) Send CM_ACKADD(AddCommit)
+
+--- Start phase 2 - NdbCntr ----------------------------------------
+
+- Use same TimeToWaitAliveTimer
+
+1) Check sysfile (DIH_RESTART_REQ)
+2) Read nodes (from Qmgr) P = qmgr president
+
+3) Send CNTR_MASTER_REQ to cntr(P)
+ including info in DIH_RESTART_REF/CONF
+
+4) Wait until -
+b) Receiving CNTR_START_CONF -> continue
+b) Receiving CNTR_START_REF -> P = node specified in REF, goto 3
+c) TimeToWaitAlive has passed -> Failure to start
+
+4) Run ndb-startphase 1
+
+--
+Initial start/System restart NdbCntr (on qmgr president node)
+
+1) Wait until -
+a) Receiving CNTR_START_REQ with GCI > than own GCI
+ send CNTR_START_REF to all waiting nodes
+b) Receiving all CNTR_START_REQ (for all defined nodes)
+c) TimeToWait has passed and partition win
+d) TimeToWait has passed and partitioning
+ and configuration "start with partition" = true
+
+2) Send CNTR_START_CONF to all nodes "with filesystem"
+
+3) Wait until -
+ Receiving CNTR_START_REP for all starting nodes
+
+4) Start waiting nodes (if any)
+
+NOTE:
+1c) Partition win = 1 node in each node group and 1 full node group
+1d) Pattitioning = at least 1 node in each node group
+--
+Running NdbCntr
+
+When receiving CNTR_MASTER_REQ
+1) If I'm not master send CNTR_MASTER_REF (including master node id)
+2) If I'm master
+ Coordinate parallell node restarts
+ send CNTR_MASTER_CONF (node restart)
diff --git a/storage/ndb/src/kernel/blocks/SystemRestart.new.txt b/storage/ndb/src/kernel/blocks/SystemRestart.new.txt
new file mode 100644
index 00000000000..3738de28df8
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/SystemRestart.new.txt
@@ -0,0 +1,61 @@
+
+DIH DICT CNTR
+---------------------- ---------------------- ---------------------
+ <- DIHRESTARTREQ
+Check for sysfile
+DIH_RESTARTCONF ->
+
+NDB_STTORY -> DICT (sp=1)
+ Read schema file
+
+******************************************************************************
+* Elect master
+******************************************************************************
+
+-- Master DIH --
+
+Read sysfile
+
+COPY_GCIREQ -> all DIHs
+
+DICTSTARTREQ -> local DICT (master)
+
+ master
+ ======
+ For each table (that should be started)
+ 1) ReadTableFile
+ 2) DI_ADD_TAB_REQ -> local DIH
+
+1) ReadTableFile (DIH)
+2) COPY_TABREQ -> all DIH (but self)
+3) For each local frag
+ ADD_FRAG_REQ -> local DICT
+4) DI_ADD_TAB_CONF
+
+ SCHEMA_INFO -> all DICTs
+ Info = schema file
+
+ Participant
+ ===========
+ 1) For each table
+ 1) If TableStatus match
+ ReadTableFile
+ else
+ GET_TABINFOREQ
+ 2) WriteTableFile
+ 3) Parse Table Data
+ 4) DI_ADD_TAB_REQ -> local DIH
+
+ <- SCHEMA_INFOCONF
+
+
+ <- DICTSTARTCONF
+
+For each fragment
+ IF Fragment is logged
+ START_FRAGREQ -> LQH x
+
+ START_RECREQ -> all LQH
+ Note does not wait for START_FRAGCONF
+
+NDB_STARTCONF ->
diff --git a/storage/ndb/src/kernel/blocks/SystemRestart.txt b/storage/ndb/src/kernel/blocks/SystemRestart.txt
new file mode 100644
index 00000000000..235dfb968fa
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/SystemRestart.txt
@@ -0,0 +1,61 @@
+
+NDBCNTR DIH DICT
+---------------------- ---------------------- ---------------
+DIH_RESTARTREQ -> DIH
+ Check for sysfile
+ <- DIH_RESTARTCONF
+
+NDB_STTORY -> DICT
+sp = 1
+ Read schema file
+
+---- Master
+
+NDB_STARTREQ -> DIH
+ Read sysfile
+
+ COPY_GCIREQ -> all DIHs
+
+ DICTSTARTREQ -> local DICT
+ local
+ ======
+ SCHEMA_INFO -> all DICTs
+ Info = schema file
+
+ Participant
+ ===========
+ 1) For each table
+ If TableStatus match
+ ReadTableFile
+ else
+ GET_TABINFOREQ
+
+ <- SCHEMA_INFOCONF
+
+ local
+ ======
+ For each table
+ DIHSTARTTABREQ -> DIH
+
+ <- DICTSTARTCONF
+
+ For each table (STARTED)
+ Read table description
+ from disk
+
+ For each fragment
+ IF Fragment dont have LCP
+ ADD_FRAGREQ -> local DICT
+ 1) LQHFRAGREQ -> LQH x
+ 2) For each attribute
+ LQHADDATTREQ
+ IF Fragment is logged
+ START_FRAGREQ -> LQH x
+
+ START_RECREQ -> all LQH
+ Note does not wait for START_FRAGCONF
+
+ For each table
+ COPY_TABREQ -> all DIH (but self)
+
+ <- NDB_STARTCONF
diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.cpp b/storage/ndb/src/kernel/blocks/backup/Backup.cpp
new file mode 100644
index 00000000000..840466460cb
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/Backup.cpp
@@ -0,0 +1,4660 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Backup.hpp"
+
+#include <ndb_version.h>
+
+#include <NdbTCP.h>
+#include <Bitmask.hpp>
+
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+
+#include <signaldata/ScanFrag.hpp>
+
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/ListTables.hpp>
+
+#include <signaldata/FsOpenReq.hpp>
+#include <signaldata/FsAppendReq.hpp>
+#include <signaldata/FsCloseReq.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+#include <signaldata/FsRemoveReq.hpp>
+
+#include <signaldata/BackupImpl.hpp>
+#include <signaldata/BackupSignalData.hpp>
+#include <signaldata/BackupContinueB.hpp>
+#include <signaldata/EventReport.hpp>
+
+#include <signaldata/UtilSequence.hpp>
+
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/AlterTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/FireTrigOrd.hpp>
+#include <signaldata/TrigAttrInfo.hpp>
+#include <AttributeHeader.hpp>
+
+#include <signaldata/WaitGCP.hpp>
+
+#include <NdbTick.h>
+
+static NDB_TICKS startTime;
+
+static const Uint32 BACKUP_SEQUENCE = 0x1F000000;
+
+#ifdef VM_TRACE
+#define DEBUG_OUT(x) ndbout << x << endl
+#else
+#define DEBUG_OUT(x)
+#endif
+
+//#define DEBUG_ABORT
+
+//---------------------------------------------------------
+// Ignore this since a completed abort could have preceded
+// this message.
+//---------------------------------------------------------
+#define slaveAbortCheck() \
+if ((ptr.p->backupId != backupId) || \
+ (ptr.p->slaveState.getState() == ABORTING)) { \
+ jam(); \
+ return; \
+}
+
+#define masterAbortCheck() \
+if ((ptr.p->backupId != backupId) || \
+ (ptr.p->masterData.state.getState() == ABORTING)) { \
+ jam(); \
+ return; \
+}
+
+#define defineSlaveAbortCheck() \
+ if (ptr.p->slaveState.getState() == ABORTING) { \
+ jam(); \
+ closeFiles(signal, ptr); \
+ return; \
+ }
+
+static Uint32 g_TypeOfStart = NodeState::ST_ILLEGAL_TYPE;
+
+void
+Backup::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 startphase = signal->theData[1];
+ const Uint32 typeOfStart = signal->theData[7];
+
+ if (startphase == 3) {
+ jam();
+ g_TypeOfStart = typeOfStart;
+ signal->theData[0] = reference();
+ sendSignal(NDBCNTR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+ return;
+ }//if
+
+ if(startphase == 7 && g_TypeOfStart == NodeState::ST_INITIAL_START &&
+ c_masterNodeId == getOwnNodeId()){
+ jam();
+ createSequence(signal);
+ return;
+ }//if
+
+ sendSTTORRY(signal);
+ return;
+}//Dbdict::execSTTOR()
+
+void
+Backup::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+ ReadNodesConf * conf = (ReadNodesConf *)signal->getDataPtr();
+
+ c_aliveNodes.clear();
+
+ Uint32 count = 0;
+ for (Uint32 i = 0; i<MAX_NDB_NODES; i++) {
+ jam();
+ if(NodeBitmask::get(conf->allNodes, i)){
+ jam();
+ count++;
+
+ NodePtr node;
+ ndbrequire(c_nodes.seize(node));
+
+ node.p->nodeId = i;
+ if(NodeBitmask::get(conf->inactiveNodes, i)) {
+ jam();
+ node.p->alive = 0;
+ } else {
+ jam();
+ node.p->alive = 1;
+ c_aliveNodes.set(i);
+ }//if
+ }//if
+ }//for
+ c_masterNodeId = conf->masterNodeId;
+ ndbrequire(count == conf->noOfNodes);
+ sendSTTORRY(signal);
+}
+
+void
+Backup::sendSTTORRY(Signal* signal)
+{
+ signal->theData[0] = 0;
+ signal->theData[3] = 1;
+ signal->theData[4] = 3;
+ signal->theData[5] = 7;
+ signal->theData[6] = 255; // No more start phases from missra
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 7, JBB);
+}
+
+void
+Backup::createSequence(Signal* signal)
+{
+ UtilSequenceReq * req = (UtilSequenceReq*)signal->getDataPtrSend();
+
+ req->senderData = RNIL;
+ req->sequenceId = BACKUP_SEQUENCE;
+ req->requestType = UtilSequenceReq::Create;
+
+ sendSignal(DBUTIL_REF, GSN_UTIL_SEQUENCE_REQ,
+ signal, UtilSequenceReq::SignalLength, JBB);
+}
+
+void
+Backup::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ const Uint32 Tdata0 = signal->theData[0];
+ const Uint32 Tdata1 = signal->theData[1];
+ const Uint32 Tdata2 = signal->theData[2];
+
+ switch(Tdata0) {
+ case BackupContinueB::START_FILE_THREAD:
+ case BackupContinueB::BUFFER_UNDERFLOW:
+ {
+ jam();
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, Tdata1);
+ checkFile(signal, filePtr);
+ return;
+ }
+ break;
+ case BackupContinueB::BUFFER_FULL_SCAN:
+ {
+ jam();
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, Tdata1);
+ checkScan(signal, filePtr);
+ return;
+ }
+ break;
+ case BackupContinueB::BUFFER_FULL_FRAG_COMPLETE:
+ {
+ jam();
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, Tdata1);
+ fragmentCompleted(signal, filePtr);
+ return;
+ }
+ break;
+ case BackupContinueB::BUFFER_FULL_META:
+ {
+ jam();
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, Tdata1);
+
+ if (ptr.p->slaveState.getState() == ABORTING) {
+ jam();
+ closeFiles(signal, ptr);
+ return;
+ }//if
+ BackupFilePtr filePtr;
+ ptr.p->files.getPtr(filePtr, ptr.p->ctlFilePtr);
+ FsBuffer & buf = filePtr.p->operation.dataBuffer;
+
+ if(buf.getFreeSize() + buf.getMinRead() < buf.getUsableSize()) {
+ jam();
+ TablePtr tabPtr;
+ c_tablePool.getPtr(tabPtr, Tdata2);
+
+ DEBUG_OUT("Backup - Buffer full - " << buf.getFreeSize()
+ << " + " << buf.getMinRead()
+ << " < " << buf.getUsableSize()
+ << " - tableId = " << tabPtr.p->tableId);
+
+ signal->theData[0] = BackupContinueB::BUFFER_FULL_META;
+ signal->theData[1] = Tdata1;
+ signal->theData[2] = Tdata2;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 100, 3);
+ return;
+ }//if
+
+ TablePtr tabPtr;
+ c_tablePool.getPtr(tabPtr, Tdata2);
+ GetTabInfoReq * req = (GetTabInfoReq *)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = ptr.i;
+ req->requestType = GetTabInfoReq::RequestById |
+ GetTabInfoReq::LongSignalConf;
+ req->tableId = tabPtr.p->tableId;
+ sendSignal(DBDICT_REF, GSN_GET_TABINFOREQ, signal,
+ GetTabInfoReq::SignalLength, JBB);
+ return;
+ }
+ default:
+ ndbrequire(0);
+ }//switch
+}
+
+void
+Backup::execDUMP_STATE_ORD(Signal* signal)
+{
+ jamEntry();
+
+ if(signal->theData[0] == 20){
+ if(signal->length() > 1){
+ c_defaults.m_dataBufferSize = (signal->theData[1] * 1024 * 1024);
+ }
+ if(signal->length() > 2){
+ c_defaults.m_logBufferSize = (signal->theData[2] * 1024 * 1024);
+ }
+ if(signal->length() > 3){
+ c_defaults.m_minWriteSize = signal->theData[3] * 1024;
+ }
+ if(signal->length() > 4){
+ c_defaults.m_maxWriteSize = signal->theData[4] * 1024;
+ }
+
+ infoEvent("Backup: data: %d log: %d min: %d max: %d",
+ c_defaults.m_dataBufferSize,
+ c_defaults.m_logBufferSize,
+ c_defaults.m_minWriteSize,
+ c_defaults.m_maxWriteSize);
+ return;
+ }
+ if(signal->theData[0] == 21){
+ BackupReq * req = (BackupReq*)signal->getDataPtrSend();
+ req->senderData = 23;
+ req->backupDataLen = 0;
+ sendSignal(BACKUP_REF, GSN_BACKUP_REQ,signal,BackupReq::SignalLength, JBB);
+ startTime = NdbTick_CurrentMillisecond();
+ return;
+ }
+
+ if(signal->theData[0] == 22){
+ const Uint32 seq = signal->theData[1];
+ FsRemoveReq * req = (FsRemoveReq *)signal->getDataPtrSend();
+ req->userReference = reference();
+ req->userPointer = 23;
+ req->directory = 1;
+ req->ownDirectory = 1;
+ FsOpenReq::setVersion(req->fileNumber, 2);
+ FsOpenReq::setSuffix(req->fileNumber, FsOpenReq::S_CTL);
+ FsOpenReq::v2_setSequence(req->fileNumber, seq);
+ FsOpenReq::v2_setNodeId(req->fileNumber, getOwnNodeId());
+ sendSignal(NDBFS_REF, GSN_FSREMOVEREQ, signal,
+ FsRemoveReq::SignalLength, JBA);
+ return;
+ }
+
+ if(signal->theData[0] == 23){
+ /**
+ * Print records
+ */
+ BackupRecordPtr ptr;
+ for(c_backups.first(ptr); ptr.i != RNIL; c_backups.next(ptr)){
+ infoEvent("BackupRecord %d: BackupId: %d MasterRef: %x ClientRef: %x",
+ ptr.i, ptr.p->backupId, ptr.p->masterRef, ptr.p->clientRef);
+ if(ptr.p->masterRef == reference()){
+ infoEvent(" MasterState: %d State: %d",
+ ptr.p->masterData.state.getState(),
+ ptr.p->slaveState.getState());
+ } else {
+ infoEvent(" State: %d", ptr.p->slaveState.getState());
+ }
+ BackupFilePtr filePtr;
+ for(ptr.p->files.first(filePtr); filePtr.i != RNIL;
+ ptr.p->files.next(filePtr)){
+ jam();
+ infoEvent(" file %d: type: %d open: %d running: %d done: %d scan: %d",
+ filePtr.i, filePtr.p->fileType, filePtr.p->fileOpened,
+ filePtr.p->fileRunning,
+ filePtr.p->fileDone, filePtr.p->scanRunning);
+ }
+ }
+ }
+ if(signal->theData[0] == 24){
+ /**
+ * Print size of records etc.
+ */
+ infoEvent("Backup - dump pool sizes");
+ infoEvent("BackupPool: %d BackupFilePool: %d TablePool: %d",
+ c_backupPool.getSize(), c_backupFilePool.getSize(),
+ c_tablePool.getSize());
+ infoEvent("AttrPool: %d TriggerPool: %d FragmentPool: %d",
+ c_backupPool.getSize(), c_backupFilePool.getSize(),
+ c_tablePool.getSize());
+ infoEvent("PagePool: %d",
+ c_pagePool.getSize());
+
+ }
+}
+
+bool
+Backup::findTable(const BackupRecordPtr & ptr,
+ TablePtr & tabPtr, Uint32 tableId) const
+{
+ for(ptr.p->tables.first(tabPtr);
+ tabPtr.i != RNIL;
+ ptr.p->tables.next(tabPtr)) {
+ jam();
+ if(tabPtr.p->tableId == tableId){
+ jam();
+ return true;
+ }//if
+ }//for
+ tabPtr.i = RNIL;
+ tabPtr.p = 0;
+ return false;
+}
+
+static Uint32 xps(Uint32 x, Uint64 ms)
+{
+ float fx = x;
+ float fs = ms;
+
+ if(ms == 0 || x == 0) {
+ jam();
+ return 0;
+ }//if
+ jam();
+ return ((Uint32)(1000.0f * (fx + fs/2.1f))) / ((Uint32)fs);
+}
+
+struct Number {
+ Number(Uint32 r) { val = r;}
+ Number & operator=(Uint32 r) { val = r; return * this; }
+ Uint32 val;
+};
+
+NdbOut &
+operator<< (NdbOut & out, const Number & val){
+ char p = 0;
+ Uint32 loop = 1;
+ while(val.val > loop){
+ loop *= 1000;
+ p += 3;
+ }
+ if(loop != 1){
+ p -= 3;
+ loop /= 1000;
+ }
+
+ switch(p){
+ case 0:
+ break;
+ case 3:
+ p = 'k';
+ break;
+ case 6:
+ p = 'M';
+ break;
+ case 9:
+ p = 'G';
+ break;
+ default:
+ p = 0;
+ }
+ char str[2];
+ str[0] = p;
+ str[1] = 0;
+ Uint32 tmp = (val.val + (loop >> 1)) / loop;
+#if 1
+ if(p > 0)
+ out << tmp << str;
+ else
+ out << tmp;
+#else
+ out << val.val;
+#endif
+
+ return out;
+}
+
+void
+Backup::execBACKUP_CONF(Signal* signal)
+{
+ jamEntry();
+ BackupConf * conf = (BackupConf*)signal->getDataPtr();
+
+ ndbout_c("Backup %d has started", conf->backupId);
+}
+
+void
+Backup::execBACKUP_REF(Signal* signal)
+{
+ jamEntry();
+ BackupRef * ref = (BackupRef*)signal->getDataPtr();
+
+ ndbout_c("Backup (%d) has NOT started %d", ref->senderData, ref->errorCode);
+}
+
+void
+Backup::execBACKUP_COMPLETE_REP(Signal* signal)
+{
+ jamEntry();
+ BackupCompleteRep* rep = (BackupCompleteRep*)signal->getDataPtr();
+
+ startTime = NdbTick_CurrentMillisecond() - startTime;
+
+ ndbout_c("Backup %d has completed", rep->backupId);
+ const Uint32 bytes = rep->noOfBytes;
+ const Uint32 records = rep->noOfRecords;
+
+ Number rps = xps(records, startTime);
+ Number bps = xps(bytes, startTime);
+
+ ndbout << " Data [ "
+ << Number(records) << " rows "
+ << Number(bytes) << " bytes " << startTime << " ms ] "
+ << " => "
+ << rps << " row/s & " << bps << "b/s" << endl;
+
+ bps = xps(rep->noOfLogBytes, startTime);
+ rps = xps(rep->noOfLogRecords, startTime);
+
+ ndbout << " Log [ "
+ << Number(rep->noOfLogRecords) << " log records "
+ << Number(rep->noOfLogBytes) << " bytes " << startTime << " ms ] "
+ << " => "
+ << rps << " records/s & " << bps << "b/s" << endl;
+
+}
+
+void
+Backup::execBACKUP_ABORT_REP(Signal* signal)
+{
+ jamEntry();
+ BackupAbortRep* rep = (BackupAbortRep*)signal->getDataPtr();
+
+ ndbout_c("Backup %d has been aborted %d", rep->backupId, rep->reason);
+}
+
+const TriggerEvent::Value triggerEventValues[] = {
+ TriggerEvent::TE_INSERT,
+ TriggerEvent::TE_UPDATE,
+ TriggerEvent::TE_DELETE
+};
+
+const char* triggerNameFormat[] = {
+ "NDB$BACKUP_%d_%d_INSERT",
+ "NDB$BACKUP_%d_%d_UPDATE",
+ "NDB$BACKUP_%d_%d_DELETE"
+};
+
+const Backup::State
+Backup::validMasterTransitions[] = {
+ INITIAL, DEFINING,
+ DEFINING, DEFINED,
+ DEFINED, STARTED,
+ STARTED, SCANNING,
+ SCANNING, STOPPING,
+ STOPPING, INITIAL,
+
+ DEFINING, ABORTING,
+ DEFINED, ABORTING,
+ STARTED, ABORTING,
+ SCANNING, ABORTING,
+ STOPPING, ABORTING,
+ ABORTING, ABORTING,
+
+ DEFINING, INITIAL,
+ ABORTING, INITIAL,
+ INITIAL, INITIAL
+};
+
+const Backup::State
+Backup::validSlaveTransitions[] = {
+ INITIAL, DEFINING,
+ DEFINING, DEFINED,
+ DEFINED, STARTED,
+ STARTED, STARTED, // Several START_BACKUP_REQ is sent
+ STARTED, SCANNING,
+ SCANNING, STARTED,
+ STARTED, STOPPING,
+ STOPPING, CLEANING,
+ CLEANING, INITIAL,
+
+ INITIAL, ABORTING, // Node fail
+ DEFINING, ABORTING,
+ DEFINED, ABORTING,
+ STARTED, ABORTING,
+ SCANNING, ABORTING,
+ STOPPING, ABORTING,
+ CLEANING, ABORTING, // Node fail w/ master takeover
+ ABORTING, ABORTING, // Slave who initiates ABORT should have this transition
+
+ ABORTING, INITIAL,
+ INITIAL, INITIAL
+};
+
+const Uint32
+Backup::validSlaveTransitionsCount =
+sizeof(Backup::validSlaveTransitions) / sizeof(Backup::State);
+
+const Uint32
+Backup::validMasterTransitionsCount =
+sizeof(Backup::validMasterTransitions) / sizeof(Backup::State);
+
+void
+Backup::CompoundState::setState(State newState){
+ bool found = false;
+ const State currState = state;
+ for(unsigned i = 0; i<noOfValidTransitions; i+= 2) {
+ jam();
+ if(validTransitions[i] == currState &&
+ validTransitions[i+1] == newState){
+ jam();
+ found = true;
+ break;
+ }
+ }
+ ndbrequire(found);
+
+ if (newState == INITIAL)
+ abortState = INITIAL;
+ if(newState == ABORTING && currState != ABORTING) {
+ jam();
+ abortState = currState;
+ }
+ state = newState;
+#ifdef DEBUG_ABORT
+ if (newState != currState) {
+ ndbout_c("%u: Old state = %u, new state = %u, abort state = %u",
+ id, currState, newState, abortState);
+ }
+#endif
+}
+
+void
+Backup::CompoundState::forceState(State newState)
+{
+ const State currState = state;
+ if (newState == INITIAL)
+ abortState = INITIAL;
+ if(newState == ABORTING && currState != ABORTING) {
+ jam();
+ abortState = currState;
+ }
+ state = newState;
+#ifdef DEBUG_ABORT
+ if (newState != currState) {
+ ndbout_c("%u: FORCE: Old state = %u, new state = %u, abort state = %u",
+ id, currState, newState, abortState);
+ }
+#endif
+}
+
+Backup::Table::Table(ArrayPool<Attribute> & ah,
+ ArrayPool<Fragment> & fh)
+ : attributes(ah), fragments(fh)
+{
+ triggerIds[0] = ILLEGAL_TRIGGER_ID;
+ triggerIds[1] = ILLEGAL_TRIGGER_ID;
+ triggerIds[2] = ILLEGAL_TRIGGER_ID;
+ triggerAllocated[0] = false;
+ triggerAllocated[1] = false;
+ triggerAllocated[2] = false;
+}
+
+/*****************************************************************************
+ *
+ * Node state handling
+ *
+ *****************************************************************************/
+void
+Backup::execNODE_FAILREP(Signal* signal)
+{
+ jamEntry();
+
+ NodeFailRep * rep = (NodeFailRep*)signal->getDataPtr();
+
+ bool doStuff = false;
+ /*
+ Start by saving important signal data which will be destroyed before the
+ process is completed.
+ */
+ NodeId new_master_node_id = rep->masterNodeId;
+ Uint32 theFailedNodes[NodeBitmask::Size];
+ for (Uint32 i = 0; i < NodeBitmask::Size; i++)
+ theFailedNodes[i] = rep->theNodes[i];
+
+// NodeId old_master_node_id = getMasterNodeId();
+ c_masterNodeId = new_master_node_id;
+
+ NodePtr nodePtr;
+ for(c_nodes.first(nodePtr); nodePtr.i != RNIL; c_nodes.next(nodePtr)) {
+ jam();
+ if(NodeBitmask::get(theFailedNodes, nodePtr.p->nodeId)){
+ if(nodePtr.p->alive){
+ jam();
+ ndbrequire(c_aliveNodes.get(nodePtr.p->nodeId));
+ doStuff = true;
+ } else {
+ jam();
+ ndbrequire(!c_aliveNodes.get(nodePtr.p->nodeId));
+ }//if
+ nodePtr.p->alive = 0;
+ c_aliveNodes.clear(nodePtr.p->nodeId);
+ }//if
+ }//for
+
+ if(!doStuff){
+ jam();
+ return;
+ }//if
+
+#ifdef DEBUG_ABORT
+ ndbout_c("****************** Node fail rep ******************");
+#endif
+
+ NodeId newCoordinator = c_masterNodeId;
+ BackupRecordPtr ptr;
+ for(c_backups.first(ptr); ptr.i != RNIL; c_backups.next(ptr)) {
+ jam();
+ checkNodeFail(signal, ptr, newCoordinator, theFailedNodes);
+ }
+}
+
+bool
+Backup::verifyNodesAlive(const NdbNodeBitmask& aNodeBitMask)
+{
+ for (Uint32 i = 0; i < MAX_NDB_NODES; i++) {
+ jam();
+ if(aNodeBitMask.get(i)) {
+ if(!c_aliveNodes.get(i)){
+ jam();
+ return false;
+ }//if
+ }//if
+ }//for
+ return true;
+}
+
+void
+Backup::checkNodeFail(Signal* signal,
+ BackupRecordPtr ptr,
+ NodeId newCoord,
+ Uint32 theFailedNodes[NodeBitmask::Size])
+{
+ ndbrequire( ptr.p->nodes.get(newCoord)); /* just to make sure newCoord
+ * is part of the backup
+ */
+ /* Update ptr.p->nodes to be up to date with current alive nodes
+ */
+ NodePtr nodePtr;
+ bool found = false;
+ for(c_nodes.first(nodePtr); nodePtr.i != RNIL; c_nodes.next(nodePtr)) {
+ jam();
+ if(NodeBitmask::get(theFailedNodes, nodePtr.p->nodeId)) {
+ jam();
+ if (ptr.p->nodes.get(nodePtr.p->nodeId)) {
+ jam();
+ ptr.p->nodes.clear(nodePtr.p->nodeId);
+ found = true;
+ }
+ }//if
+ }//for
+
+ if(!found) {
+ jam();
+ return; // failed node is not part of backup process, safe to continue
+ }
+
+ bool doMasterTakeover = false;
+ if(NodeBitmask::get(theFailedNodes, refToNode(ptr.p->masterRef))){
+ jam();
+ doMasterTakeover = true;
+ };
+
+ if (newCoord == getOwnNodeId()){
+ jam();
+ if (doMasterTakeover) {
+ /**
+ * I'm new master
+ */
+ CRASH_INSERTION((10002));
+#ifdef DEBUG_ABORT
+ ndbout_c("**** Master Takeover: Node failed: Master id = %u",
+ refToNode(ptr.p->masterRef));
+#endif
+ masterTakeOver(signal, ptr);
+ return;
+ }//if
+ /**
+ * I'm master for this backup
+ */
+ jam();
+ CRASH_INSERTION((10001));
+#ifdef DEBUG_ABORT
+ ndbout_c("**** Master: Node failed: Master id = %u",
+ refToNode(ptr.p->masterRef));
+#endif
+ masterAbort(signal, ptr, false);
+ return;
+ }//if
+
+ /**
+ * If there's a new master, (it's not me)
+ * but remember who it is
+ */
+ ptr.p->masterRef = calcBackupBlockRef(newCoord);
+#ifdef DEBUG_ABORT
+ ndbout_c("**** Slave: Node failed: Master id = %u",
+ refToNode(ptr.p->masterRef));
+#endif
+ /**
+ * I abort myself as slave if not master
+ */
+ CRASH_INSERTION((10021));
+ // slaveAbort(signal, ptr);
+}
+
+void
+Backup::masterTakeOver(Signal* signal, BackupRecordPtr ptr)
+{
+ ptr.p->masterRef = reference();
+ ptr.p->masterData.gsn = MAX_GSN + 1;
+
+ switch(ptr.p->slaveState.getState()){
+ case INITIAL:
+ jam();
+ ptr.p->masterData.state.forceState(INITIAL);
+ break;
+ case ABORTING:
+ jam();
+ case DEFINING:
+ jam();
+ case DEFINED:
+ jam();
+ case STARTED:
+ jam();
+ case SCANNING:
+ jam();
+ ptr.p->masterData.state.forceState(STARTED);
+ break;
+ case STOPPING:
+ jam();
+ case CLEANING:
+ jam();
+ ptr.p->masterData.state.forceState(STOPPING);
+ break;
+ default:
+ ndbrequire(false);
+ }
+ masterAbort(signal, ptr, false);
+}
+
+void
+Backup::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 senderRef = signal->theData[0];
+ const Uint32 inclNode = signal->theData[1];
+
+ NodePtr node;
+ for(c_nodes.first(node); node.i != RNIL; c_nodes.next(node)) {
+ jam();
+ const Uint32 nodeId = node.p->nodeId;
+ if(inclNode == nodeId){
+ jam();
+
+ ndbrequire(node.p->alive == 0);
+ ndbrequire(!c_aliveNodes.get(nodeId));
+
+ node.p->alive = 1;
+ c_aliveNodes.set(nodeId);
+
+ break;
+ }//if
+ }//for
+ signal->theData[0] = reference();
+ sendSignal(senderRef, GSN_INCL_NODECONF, signal, 1, JBB);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Define backup
+ *
+ *****************************************************************************/
+
+void
+Backup::execBACKUP_REQ(Signal* signal)
+{
+ jamEntry();
+ BackupReq * req = (BackupReq*)signal->getDataPtr();
+
+ const Uint32 senderData = req->senderData;
+ const BlockReference senderRef = signal->senderBlockRef();
+ const Uint32 dataLen32 = req->backupDataLen; // In 32 bit words
+
+ if(getOwnNodeId() != getMasterNodeId()) {
+ jam();
+ sendBackupRef(senderRef, signal, senderData, BackupRef::IAmNotMaster);
+ return;
+ }//if
+
+ if (m_diskless)
+ {
+ sendBackupRef(senderRef, signal, senderData,
+ BackupRef::CannotBackupDiskless);
+ return;
+ }
+
+ if(dataLen32 != 0) {
+ jam();
+ sendBackupRef(senderRef, signal, senderData,
+ BackupRef::BackupDefinitionNotImplemented);
+ return;
+ }//if
+
+#ifdef DEBUG_ABORT
+ dumpUsedResources();
+#endif
+ /**
+ * Seize a backup record
+ */
+ BackupRecordPtr ptr;
+ c_backups.seize(ptr);
+ if(ptr.i == RNIL) {
+ jam();
+ sendBackupRef(senderRef, signal, senderData, BackupRef::OutOfBackupRecord);
+ return;
+ }//if
+
+ ndbrequire(ptr.p->pages.empty());
+ ndbrequire(ptr.p->tables.isEmpty());
+
+ ptr.p->masterData.state.forceState(INITIAL);
+ ptr.p->masterData.state.setState(DEFINING);
+ ptr.p->clientRef = senderRef;
+ ptr.p->clientData = senderData;
+ ptr.p->masterRef = reference();
+ ptr.p->nodes = c_aliveNodes;
+ ptr.p->backupId = 0;
+ ptr.p->backupKey[0] = 0;
+ ptr.p->backupKey[1] = 0;
+ ptr.p->backupDataLen = 0;
+ ptr.p->masterData.dropTrig.tableId = RNIL;
+ ptr.p->masterData.alterTrig.tableId = RNIL;
+
+ UtilSequenceReq * utilReq = (UtilSequenceReq*)signal->getDataPtrSend();
+
+ ptr.p->masterData.gsn = GSN_UTIL_SEQUENCE_REQ;
+ utilReq->senderData = ptr.i;
+ utilReq->sequenceId = BACKUP_SEQUENCE;
+ utilReq->requestType = UtilSequenceReq::NextVal;
+ sendSignal(DBUTIL_REF, GSN_UTIL_SEQUENCE_REQ,
+ signal, UtilSequenceReq::SignalLength, JBB);
+}
+
+void
+Backup::execUTIL_SEQUENCE_REF(Signal* signal)
+{
+ BackupRecordPtr ptr;
+ jamEntry();
+ UtilSequenceRef * utilRef = (UtilSequenceRef*)signal->getDataPtr();
+ ptr.i = utilRef->senderData;
+ ndbrequire(ptr.i == RNIL);
+ c_backupPool.getPtr(ptr);
+ ndbrequire(ptr.p->masterData.gsn == GSN_UTIL_SEQUENCE_REQ);
+ ptr.p->masterData.gsn = 0;
+ sendBackupRef(signal, ptr, BackupRef::SequenceFailure);
+}//execUTIL_SEQUENCE_REF()
+
+
+void
+Backup::sendBackupRef(Signal* signal, BackupRecordPtr ptr, Uint32 errorCode)
+{
+ jam();
+ sendBackupRef(ptr.p->clientRef, signal, ptr.p->clientData, errorCode);
+ // ptr.p->masterData.state.setState(INITIAL);
+ cleanupSlaveResources(ptr);
+}
+
+void
+Backup::sendBackupRef(BlockReference senderRef, Signal *signal,
+ Uint32 senderData, Uint32 errorCode)
+{
+ jam();
+ BackupRef* ref = (BackupRef*)signal->getDataPtrSend();
+ ref->senderData = senderData;
+ ref->errorCode = errorCode;
+ ref->masterRef = numberToRef(BACKUP, getMasterNodeId());
+ sendSignal(senderRef, GSN_BACKUP_REF, signal, BackupRef::SignalLength, JBB);
+
+ if(errorCode != BackupRef::IAmNotMaster){
+ signal->theData[0] = NDB_LE_BackupFailedToStart;
+ signal->theData[1] = senderRef;
+ signal->theData[2] = errorCode;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+ }
+}
+
+void
+Backup::execUTIL_SEQUENCE_CONF(Signal* signal)
+{
+ jamEntry();
+
+ UtilSequenceConf * conf = (UtilSequenceConf*)signal->getDataPtr();
+
+ if(conf->requestType == UtilSequenceReq::Create) {
+ jam();
+ sendSTTORRY(signal); // At startup in NDB
+ return;
+ }
+
+ BackupRecordPtr ptr;
+ ptr.i = conf->senderData;
+ c_backupPool.getPtr(ptr);
+
+ ndbrequire(ptr.p->masterData.gsn == GSN_UTIL_SEQUENCE_REQ);
+ ptr.p->masterData.gsn = 0;
+ if (ptr.p->masterData.state.getState() == ABORTING) {
+ jam();
+ sendBackupRef(signal, ptr, ptr.p->errorCode);
+ return;
+ }//if
+ if (ERROR_INSERTED(10023)) {
+ ptr.p->masterData.state.setState(ABORTING);
+ sendBackupRef(signal, ptr, 323);
+ return;
+ }//if
+ ndbrequire(ptr.p->masterData.state.getState() == DEFINING);
+
+ {
+ Uint64 backupId;
+ memcpy(&backupId,conf->sequenceValue,8);
+ ptr.p->backupId= (Uint32)backupId;
+ }
+ ptr.p->backupKey[0] = (getOwnNodeId() << 16) | (ptr.p->backupId & 0xFFFF);
+ ptr.p->backupKey[1] = NdbTick_CurrentMillisecond();
+
+ ptr.p->masterData.gsn = GSN_UTIL_LOCK_REQ;
+ Mutex mutex(signal, c_mutexMgr, ptr.p->masterData.m_defineBackupMutex);
+ Callback c = { safe_cast(&Backup::defineBackupMutex_locked), ptr.i };
+ ndbrequire(mutex.lock(c));
+
+ return;
+}
+
+void
+Backup::defineBackupMutex_locked(Signal* signal, Uint32 ptrI, Uint32 retVal){
+ jamEntry();
+ ndbrequire(retVal == 0);
+
+ BackupRecordPtr ptr;
+ ptr.i = ptrI;
+ c_backupPool.getPtr(ptr);
+
+ ndbrequire(ptr.p->masterData.gsn == GSN_UTIL_LOCK_REQ);
+ ptr.p->masterData.gsn = 0;
+
+ ptr.p->masterData.gsn = GSN_UTIL_LOCK_REQ;
+ Mutex mutex(signal, c_mutexMgr, ptr.p->masterData.m_dictCommitTableMutex);
+ Callback c = { safe_cast(&Backup::dictCommitTableMutex_locked), ptr.i };
+ ndbrequire(mutex.lock(c));
+}
+
+void
+Backup::dictCommitTableMutex_locked(Signal* signal, Uint32 ptrI,Uint32 retVal)
+{
+ jamEntry();
+ ndbrequire(retVal == 0);
+
+ /**
+ * We now have both the mutexes
+ */
+ BackupRecordPtr ptr;
+ ptr.i = ptrI;
+ c_backupPool.getPtr(ptr);
+
+ ndbrequire(ptr.p->masterData.gsn == GSN_UTIL_LOCK_REQ);
+ ptr.p->masterData.gsn = 0;
+
+ if (ERROR_INSERTED(10031)) {
+ ptr.p->masterData.state.setState(ABORTING);
+ ptr.p->setErrorCode(331);
+ }//if
+
+ if (ptr.p->masterData.state.getState() == ABORTING) {
+ jam();
+
+ /**
+ * Unlock mutexes
+ */
+ jam();
+ Mutex mutex1(signal, c_mutexMgr, ptr.p->masterData.m_dictCommitTableMutex);
+ jam();
+ mutex1.unlock(); // ignore response
+
+ jam();
+ Mutex mutex2(signal, c_mutexMgr, ptr.p->masterData.m_defineBackupMutex);
+ jam();
+ mutex2.unlock(); // ignore response
+
+ sendBackupRef(signal, ptr, ptr.p->errorCode);
+ return;
+ }//if
+
+ ndbrequire(ptr.p->masterData.state.getState() == DEFINING);
+
+ sendDefineBackupReq(signal, ptr);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Define backup cont'd (from now on all slaves are in)
+ *
+ *****************************************************************************/
+
+void
+Backup::sendSignalAllWait(BackupRecordPtr ptr, Uint32 gsn, Signal *signal,
+ Uint32 signalLength, bool executeDirect)
+{
+ jam();
+ ptr.p->masterData.gsn = gsn;
+ ptr.p->masterData.sendCounter.clearWaitingFor();
+ NodePtr node;
+ for(c_nodes.first(node); node.i != RNIL; c_nodes.next(node)){
+ jam();
+ const Uint32 nodeId = node.p->nodeId;
+ if(node.p->alive && ptr.p->nodes.get(nodeId)){
+ jam();
+
+ ptr.p->masterData.sendCounter.setWaitingFor(nodeId);
+
+ const BlockReference ref = numberToRef(BACKUP, nodeId);
+ if (!executeDirect || ref != reference()) {
+ sendSignal(ref, gsn, signal, signalLength, JBB);
+ }//if
+ }//if
+ }//for
+ if (executeDirect) {
+ EXECUTE_DIRECT(BACKUP, gsn, signal, signalLength);
+ }
+}
+
+bool
+Backup::haveAllSignals(BackupRecordPtr ptr, Uint32 gsn, Uint32 nodeId)
+{
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == gsn);
+ ndbrequire(!ptr.p->masterData.sendCounter.done());
+ ndbrequire(ptr.p->masterData.sendCounter.isWaitingFor(nodeId));
+
+ ptr.p->masterData.sendCounter.clearWaitingFor(nodeId);
+
+ if (ptr.p->masterData.sendCounter.done())
+ ptr.p->masterData.gsn = 0;
+
+ return ptr.p->masterData.sendCounter.done();
+}
+
+void
+Backup::sendDefineBackupReq(Signal *signal, BackupRecordPtr ptr)
+{
+ /**
+ * Sending define backup to all participants
+ */
+ DefineBackupReq * req = (DefineBackupReq*)signal->getDataPtrSend();
+ req->backupId = ptr.p->backupId;
+ req->clientRef = ptr.p->clientRef;
+ req->clientData = ptr.p->clientData;
+ req->senderRef = reference();
+ req->backupPtr = ptr.i;
+ req->backupKey[0] = ptr.p->backupKey[0];
+ req->backupKey[1] = ptr.p->backupKey[1];
+ req->nodes = ptr.p->nodes;
+ req->backupDataLen = ptr.p->backupDataLen;
+
+ ptr.p->masterData.errorCode = 0;
+ ptr.p->okToCleanMaster = false; // master must wait with cleaning to last
+ sendSignalAllWait(ptr, GSN_DEFINE_BACKUP_REQ, signal,
+ DefineBackupReq::SignalLength,
+ true /* do execute direct on oneself */);
+ /**
+ * Now send backup data
+ */
+ const Uint32 len = ptr.p->backupDataLen;
+ if(len == 0){
+ /**
+ * No data to send
+ */
+ jam();
+ return;
+ }//if
+
+ /**
+ * Not implemented
+ */
+ ndbrequire(0);
+}
+
+void
+Backup::execDEFINE_BACKUP_REF(Signal* signal)
+{
+ jamEntry();
+
+ DefineBackupRef* ref = (DefineBackupRef*)signal->getDataPtr();
+
+ const Uint32 ptrI = ref->backupPtr;
+ const Uint32 backupId = ref->backupId;
+ const Uint32 nodeId = refToNode(signal->senderBlockRef());
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->masterData.errorCode = ref->errorCode;
+ defineBackupReply(signal, ptr, nodeId);
+}
+
+void
+Backup::execDEFINE_BACKUP_CONF(Signal* signal)
+{
+ jamEntry();
+
+ DefineBackupConf* conf = (DefineBackupConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->backupPtr;
+ const Uint32 backupId = conf->backupId;
+ const Uint32 nodeId = refToNode(signal->senderBlockRef());
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ if (ERROR_INSERTED(10024)) {
+ ptr.p->masterData.errorCode = 324;
+ }//if
+
+ defineBackupReply(signal, ptr, nodeId);
+}
+
+void
+Backup::defineBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32 nodeId)
+{
+ if (!haveAllSignals(ptr, GSN_DEFINE_BACKUP_REQ, nodeId)) {
+ jam();
+ return;
+ }
+ /**
+ * Unlock mutexes
+ */
+ jam();
+ Mutex mutex1(signal, c_mutexMgr, ptr.p->masterData.m_dictCommitTableMutex);
+ jam();
+ mutex1.unlock(); // ignore response
+
+ jam();
+ Mutex mutex2(signal, c_mutexMgr, ptr.p->masterData.m_defineBackupMutex);
+ jam();
+ mutex2.unlock(); // ignore response
+
+ if(ptr.p->errorCode) {
+ jam();
+ ptr.p->masterData.errorCode = ptr.p->errorCode;
+ }
+
+ if(ptr.p->masterData.errorCode){
+ jam();
+ ptr.p->setErrorCode(ptr.p->masterData.errorCode);
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::OkToClean);
+ masterSendAbortBackup(signal, ptr);
+ return;
+ }
+
+ /**
+ * Reply to client
+ */
+ BackupConf * conf = (BackupConf*)signal->getDataPtrSend();
+ conf->backupId = ptr.p->backupId;
+ conf->senderData = ptr.p->clientData;
+ conf->nodes = ptr.p->nodes;
+ sendSignal(ptr.p->clientRef, GSN_BACKUP_CONF, signal,
+ BackupConf::SignalLength, JBB);
+
+ signal->theData[0] = NDB_LE_BackupStarted;
+ signal->theData[1] = ptr.p->clientRef;
+ signal->theData[2] = ptr.p->backupId;
+ ptr.p->nodes.copyto(NdbNodeBitmask::Size, signal->theData+3);
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3+NdbNodeBitmask::Size, JBB);
+
+ ptr.p->masterData.state.setState(DEFINED);
+ /**
+ * Prepare Trig
+ */
+ TablePtr tabPtr;
+ ndbrequire(ptr.p->tables.first(tabPtr));
+ sendCreateTrig(signal, ptr, tabPtr);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Prepare triggers
+ *
+ *****************************************************************************/
+void
+Backup::createAttributeMask(TablePtr tabPtr,
+ Bitmask<MAXNROFATTRIBUTESINWORDS> & mask)
+{
+ mask.clear();
+ Table & table = * tabPtr.p;
+ for(Uint32 i = 0; i<table.noOfAttributes; i++) {
+ jam();
+ AttributePtr attr;
+ table.attributes.getPtr(attr, i);
+ mask.set(i);
+ }
+}
+
+void
+Backup::sendCreateTrig(Signal* signal,
+ BackupRecordPtr ptr, TablePtr tabPtr)
+{
+ CreateTrigReq * req =(CreateTrigReq *)signal->getDataPtrSend();
+
+ ptr.p->errorCode = 0;
+ ptr.p->masterData.gsn = GSN_CREATE_TRIG_REQ;
+ ptr.p->masterData.sendCounter = 3;
+ ptr.p->masterData.createTrig.tableId = tabPtr.p->tableId;
+
+ req->setUserRef(reference());
+ req->setConnectionPtr(ptr.i);
+ req->setRequestType(CreateTrigReq::RT_USER);
+
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attrMask;
+ createAttributeMask(tabPtr, attrMask);
+ req->setAttributeMask(attrMask);
+ req->setTableId(tabPtr.p->tableId);
+ req->setIndexId(RNIL); // not used
+ req->setTriggerId(RNIL); // to be created
+ req->setTriggerType(TriggerType::SUBSCRIPTION);
+ req->setTriggerActionTime(TriggerActionTime::TA_DETACHED);
+ req->setMonitorReplicas(true);
+ req->setMonitorAllAttributes(false);
+ req->setOnline(false); // leave trigger offline
+
+ char triggerName[MAX_TAB_NAME_SIZE];
+ Uint32 nameBuffer[2 + ((MAX_TAB_NAME_SIZE + 3) >> 2)]; // SP string
+ LinearWriter w(nameBuffer, sizeof(nameBuffer) >> 2);
+ LinearSectionPtr lsPtr[3];
+
+ for (int i=0; i < 3; i++) {
+ req->setTriggerEvent(triggerEventValues[i]);
+ BaseString::snprintf(triggerName, sizeof(triggerName), triggerNameFormat[i],
+ ptr.p->backupId, tabPtr.p->tableId);
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = nameBuffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(DBDICT_REF, GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB, lsPtr, 1);
+ }
+}
+
+void
+Backup::execCREATE_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+ CreateTrigConf * conf = (CreateTrigConf*)signal->getDataPtr();
+
+ const Uint32 ptrI = conf->getConnectionPtr();
+ const Uint32 tableId = conf->getTableId();
+ const TriggerEvent::Value type = conf->getTriggerEvent();
+ const Uint32 triggerId = conf->getTriggerId();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ /**
+ * Verify that I'm waiting for this conf
+ */
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == GSN_CREATE_TRIG_REQ);
+ ndbrequire(ptr.p->masterData.sendCounter.done() == false);
+ ndbrequire(ptr.p->masterData.createTrig.tableId == tableId);
+
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, tableId));
+ ndbrequire(type < 3); // if some decides to change the enums
+
+ ndbrequire(tabPtr.p->triggerIds[type] == ILLEGAL_TRIGGER_ID);
+ tabPtr.p->triggerIds[type] = triggerId;
+
+ createTrigReply(signal, ptr);
+}
+
+void
+Backup::execCREATE_TRIG_REF(Signal* signal)
+{
+ CreateTrigRef* ref = (CreateTrigRef*)signal->getDataPtr();
+
+ const Uint32 ptrI = ref->getConnectionPtr();
+ const Uint32 tableId = ref->getTableId();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ /**
+ * Verify that I'm waiting for this ref
+ */
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == GSN_CREATE_TRIG_REQ);
+ ndbrequire(ptr.p->masterData.sendCounter.done() == false);
+ ndbrequire(ptr.p->masterData.createTrig.tableId == tableId);
+
+ ptr.p->setErrorCode(ref->getErrorCode());
+
+ createTrigReply(signal, ptr);
+}
+
+void
+Backup::createTrigReply(Signal* signal, BackupRecordPtr ptr)
+{
+ CRASH_INSERTION(10003);
+
+ /**
+ * Check finished with table
+ */
+ ptr.p->masterData.sendCounter--;
+ if(ptr.p->masterData.sendCounter.done() == false){
+ jam();
+ return;
+ }//if
+
+ ptr.p->masterData.gsn = 0;
+
+ if(ptr.p->checkError()) {
+ jam();
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+
+ if (ERROR_INSERTED(10025)) {
+ ptr.p->errorCode = 325;
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, ptr.p->masterData.createTrig.tableId));
+
+ /**
+ * Next table
+ */
+ ptr.p->tables.next(tabPtr);
+ if(tabPtr.i != RNIL){
+ jam();
+ sendCreateTrig(signal, ptr, tabPtr);
+ return;
+ }//if
+
+ /**
+ * Finished with all tables, send StartBackupReq
+ */
+ ptr.p->masterData.state.setState(STARTED);
+
+ ptr.p->tables.first(tabPtr);
+ ptr.p->errorCode = 0;
+ ptr.p->masterData.startBackup.signalNo = 0;
+ ptr.p->masterData.startBackup.noOfSignals =
+ (ptr.p->tables.noOfElements() + StartBackupReq::MaxTableTriggers - 1) /
+ StartBackupReq::MaxTableTriggers;
+ sendStartBackup(signal, ptr, tabPtr);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Start backup
+ *
+ *****************************************************************************/
+void
+Backup::sendStartBackup(Signal* signal, BackupRecordPtr ptr, TablePtr tabPtr)
+{
+
+ ptr.p->masterData.startBackup.tablePtr = tabPtr.i;
+
+ StartBackupReq* req = (StartBackupReq*)signal->getDataPtrSend();
+ req->backupId = ptr.p->backupId;
+ req->backupPtr = ptr.i;
+ req->signalNo = ptr.p->masterData.startBackup.signalNo;
+ req->noOfSignals = ptr.p->masterData.startBackup.noOfSignals;
+ Uint32 i;
+ for(i = 0; i<StartBackupReq::MaxTableTriggers; i++) {
+ jam();
+ req->tableTriggers[i].tableId = tabPtr.p->tableId;
+ req->tableTriggers[i].triggerIds[0] = tabPtr.p->triggerIds[0];
+ req->tableTriggers[i].triggerIds[1] = tabPtr.p->triggerIds[1];
+ req->tableTriggers[i].triggerIds[2] = tabPtr.p->triggerIds[2];
+ if(!ptr.p->tables.next(tabPtr)){
+ jam();
+ i++;
+ break;
+ }//if
+ }//for
+ req->noOfTableTriggers = i;
+
+ sendSignalAllWait(ptr, GSN_START_BACKUP_REQ, signal,
+ StartBackupReq::HeaderLength +
+ (i * StartBackupReq::TableTriggerLength));
+}
+
+void
+Backup::execSTART_BACKUP_REF(Signal* signal)
+{
+ jamEntry();
+
+ StartBackupRef* ref = (StartBackupRef*)signal->getDataPtr();
+ const Uint32 ptrI = ref->backupPtr;
+ const Uint32 backupId = ref->backupId;
+ const Uint32 signalNo = ref->signalNo;
+ const Uint32 nodeId = refToNode(signal->senderBlockRef());
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->setErrorCode(ref->errorCode);
+ startBackupReply(signal, ptr, nodeId, signalNo);
+}
+
+void
+Backup::execSTART_BACKUP_CONF(Signal* signal)
+{
+ jamEntry();
+
+ StartBackupConf* conf = (StartBackupConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->backupPtr;
+ const Uint32 backupId = conf->backupId;
+ const Uint32 signalNo = conf->signalNo;
+ const Uint32 nodeId = refToNode(signal->senderBlockRef());
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ startBackupReply(signal, ptr, nodeId, signalNo);
+}
+
+void
+Backup::startBackupReply(Signal* signal, BackupRecordPtr ptr,
+ Uint32 nodeId, Uint32 signalNo)
+{
+
+ CRASH_INSERTION((10004));
+
+ ndbrequire(ptr.p->masterData.startBackup.signalNo == signalNo);
+ if (!haveAllSignals(ptr, GSN_START_BACKUP_REQ, nodeId)) {
+ jam();
+ return;
+ }
+
+ if(ptr.p->checkError()){
+ jam();
+ masterAbort(signal, ptr, true);
+ return;
+ }
+
+ if (ERROR_INSERTED(10026)) {
+ ptr.p->errorCode = 326;
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+
+ TablePtr tabPtr;
+ c_tablePool.getPtr(tabPtr, ptr.p->masterData.startBackup.tablePtr);
+ for(Uint32 i = 0; i<StartBackupReq::MaxTableTriggers; i++) {
+ jam();
+ if(!ptr.p->tables.next(tabPtr)) {
+ jam();
+ break;
+ }//if
+ }//for
+
+ if(tabPtr.i != RNIL) {
+ jam();
+ ptr.p->masterData.startBackup.signalNo++;
+ sendStartBackup(signal, ptr, tabPtr);
+ return;
+ }
+
+ sendAlterTrig(signal, ptr);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Activate triggers
+ *
+ *****************************************************************************/
+void
+Backup::sendAlterTrig(Signal* signal, BackupRecordPtr ptr)
+{
+ AlterTrigReq * req =(AlterTrigReq *)signal->getDataPtrSend();
+
+ ptr.p->errorCode = 0;
+ ptr.p->masterData.gsn = GSN_ALTER_TRIG_REQ;
+ ptr.p->masterData.sendCounter = 0;
+
+ req->setUserRef(reference());
+ req->setConnectionPtr(ptr.i);
+ req->setRequestType(AlterTrigReq::RT_USER);
+ req->setTriggerInfo(0); // not used on ALTER via DICT
+ req->setOnline(true);
+ req->setReceiverRef(reference());
+
+ TablePtr tabPtr;
+
+ if (ptr.p->masterData.alterTrig.tableId == RNIL) {
+ jam();
+ ptr.p->tables.first(tabPtr);
+ } else {
+ jam();
+ ndbrequire(findTable(ptr, tabPtr, ptr.p->masterData.alterTrig.tableId));
+ ptr.p->tables.next(tabPtr);
+ }//if
+ if (tabPtr.i != RNIL) {
+ jam();
+ ptr.p->masterData.alterTrig.tableId = tabPtr.p->tableId;
+ req->setTableId(tabPtr.p->tableId);
+
+ req->setTriggerId(tabPtr.p->triggerIds[0]);
+ sendSignal(DBDICT_REF, GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+
+ req->setTriggerId(tabPtr.p->triggerIds[1]);
+ sendSignal(DBDICT_REF, GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+
+ req->setTriggerId(tabPtr.p->triggerIds[2]);
+ sendSignal(DBDICT_REF, GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+
+ ptr.p->masterData.sendCounter += 3;
+ return;
+ }//if
+ ptr.p->masterData.alterTrig.tableId = RNIL;
+ /**
+ * Finished with all tables
+ */
+ ptr.p->masterData.gsn = GSN_WAIT_GCP_REQ;
+ ptr.p->masterData.waitGCP.startBackup = true;
+
+ WaitGCPReq * waitGCPReq = (WaitGCPReq*)signal->getDataPtrSend();
+ waitGCPReq->senderRef = reference();
+ waitGCPReq->senderData = ptr.i;
+ waitGCPReq->requestType = WaitGCPReq::CompleteForceStart;
+ sendSignal(DBDIH_REF, GSN_WAIT_GCP_REQ, signal,
+ WaitGCPReq::SignalLength,JBB);
+}
+
+void
+Backup::execALTER_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+
+ AlterTrigConf* conf = (AlterTrigConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->getConnectionPtr();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ alterTrigReply(signal, ptr);
+}
+
+void
+Backup::execALTER_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+
+ AlterTrigRef* ref = (AlterTrigRef*)signal->getDataPtr();
+ const Uint32 ptrI = ref->getConnectionPtr();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ ptr.p->setErrorCode(ref->getErrorCode());
+
+ alterTrigReply(signal, ptr);
+}
+
+void
+Backup::alterTrigReply(Signal* signal, BackupRecordPtr ptr)
+{
+
+ CRASH_INSERTION((10005));
+
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == GSN_ALTER_TRIG_REQ);
+ ndbrequire(ptr.p->masterData.sendCounter.done() == false);
+
+ ptr.p->masterData.sendCounter--;
+
+ if(ptr.p->masterData.sendCounter.done() == false){
+ jam();
+ return;
+ }//if
+
+ ptr.p->masterData.gsn = 0;
+
+ if(ptr.p->checkError()){
+ jam();
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+
+ sendAlterTrig(signal, ptr);
+}
+
+void
+Backup::execWAIT_GCP_REF(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION((10006));
+
+ WaitGCPRef * ref = (WaitGCPRef*)signal->getDataPtr();
+ const Uint32 ptrI = ref->senderData;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == GSN_WAIT_GCP_REQ);
+
+ WaitGCPReq * req = (WaitGCPReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = ptr.i;
+ req->requestType = WaitGCPReq::CompleteForceStart;
+ sendSignal(DBDIH_REF, GSN_WAIT_GCP_REQ, signal,
+ WaitGCPReq::SignalLength,JBB);
+}
+
+void
+Backup::execWAIT_GCP_CONF(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION((10007));
+
+ WaitGCPConf * conf = (WaitGCPConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->senderData;
+ const Uint32 gcp = conf->gcp;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == GSN_WAIT_GCP_REQ);
+ ptr.p->masterData.gsn = 0;
+
+ if(ptr.p->checkError()) {
+ jam();
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+
+ if(ptr.p->masterData.waitGCP.startBackup) {
+ jam();
+ CRASH_INSERTION((10008));
+ ptr.p->startGCP = gcp;
+ ptr.p->masterData.state.setState(SCANNING);
+ nextFragment(signal, ptr);
+ } else {
+ jam();
+ CRASH_INSERTION((10009));
+ ptr.p->stopGCP = gcp;
+ ptr.p->masterData.state.setState(STOPPING);
+ sendDropTrig(signal, ptr); // regular dropping of triggers
+ }//if
+}
+/*****************************************************************************
+ *
+ * Master functionallity - Backup fragment
+ *
+ *****************************************************************************/
+void
+Backup::nextFragment(Signal* signal, BackupRecordPtr ptr)
+{
+ jam();
+
+ BackupFragmentReq* req = (BackupFragmentReq*)signal->getDataPtrSend();
+ req->backupPtr = ptr.i;
+ req->backupId = ptr.p->backupId;
+
+ NodeBitmask nodes = ptr.p->nodes;
+ Uint32 idleNodes = nodes.count();
+ Uint32 saveIdleNodes = idleNodes;
+ ndbrequire(idleNodes > 0);
+
+ TablePtr tabPtr;
+ ptr.p->tables.first(tabPtr);
+ for(; tabPtr.i != RNIL && idleNodes > 0; ptr.p->tables.next(tabPtr)) {
+ jam();
+ FragmentPtr fragPtr;
+ Array<Fragment> & frags = tabPtr.p->fragments;
+ const Uint32 fragCount = frags.getSize();
+
+ for(Uint32 i = 0; i<fragCount && idleNodes > 0; i++) {
+ jam();
+ tabPtr.p->fragments.getPtr(fragPtr, i);
+ const Uint32 nodeId = fragPtr.p->node;
+ if(fragPtr.p->scanning != 0) {
+ jam();
+ ndbrequire(nodes.get(nodeId));
+ nodes.clear(nodeId);
+ idleNodes--;
+ } else if(fragPtr.p->scanned == 0 && nodes.get(nodeId)){
+ jam();
+ fragPtr.p->scanning = 1;
+ nodes.clear(nodeId);
+ idleNodes--;
+
+ req->tableId = tabPtr.p->tableId;
+ req->fragmentNo = i;
+ req->count = 0;
+
+ const BlockReference ref = numberToRef(BACKUP, nodeId);
+ sendSignal(ref, GSN_BACKUP_FRAGMENT_REQ, signal,
+ BackupFragmentReq::SignalLength, JBB);
+ }//if
+ }//for
+ }//for
+
+ if(idleNodes != saveIdleNodes){
+ jam();
+ return;
+ }//if
+
+ /**
+ * Finished with all tables
+ */
+ {
+ ptr.p->masterData.gsn = GSN_WAIT_GCP_REQ;
+ ptr.p->masterData.waitGCP.startBackup = false;
+
+ WaitGCPReq * req = (WaitGCPReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = ptr.i;
+ req->requestType = WaitGCPReq::CompleteForceStart;
+ sendSignal(DBDIH_REF, GSN_WAIT_GCP_REQ, signal,
+ WaitGCPReq::SignalLength, JBB);
+ }
+}
+
+void
+Backup::execBACKUP_FRAGMENT_CONF(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION((10010));
+
+ BackupFragmentConf * conf = (BackupFragmentConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->backupPtr;
+ const Uint32 backupId = conf->backupId;
+ const Uint32 tableId = conf->tableId;
+ const Uint32 fragmentNo = conf->fragmentNo;
+ const Uint32 nodeId = refToNode(signal->senderBlockRef());
+ const Uint32 noOfBytes = conf->noOfBytes;
+ const Uint32 noOfRecords = conf->noOfRecords;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->noOfBytes += noOfBytes;
+ ptr.p->noOfRecords += noOfRecords;
+
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, tableId));
+
+ FragmentPtr fragPtr;
+ tabPtr.p->fragments.getPtr(fragPtr, fragmentNo);
+
+ ndbrequire(fragPtr.p->scanned == 0);
+ ndbrequire(fragPtr.p->scanning == 1);
+ ndbrequire(fragPtr.p->node == nodeId);
+
+ fragPtr.p->scanned = 1;
+ fragPtr.p->scanning = 0;
+
+ if(ptr.p->checkError()) {
+ jam();
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+ if (ERROR_INSERTED(10028)) {
+ ptr.p->errorCode = 328;
+ masterAbort(signal, ptr, true);
+ return;
+ }//if
+ nextFragment(signal, ptr);
+}
+
+void
+Backup::execBACKUP_FRAGMENT_REF(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION((10011));
+
+ BackupFragmentRef * ref = (BackupFragmentRef*)signal->getDataPtr();
+ const Uint32 ptrI = ref->backupPtr;
+ const Uint32 backupId = ref->backupId;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->setErrorCode(ref->errorCode);
+ masterAbort(signal, ptr, true);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Drop triggers
+ *
+ *****************************************************************************/
+
+void
+Backup::sendDropTrig(Signal* signal, BackupRecordPtr ptr)
+{
+ TablePtr tabPtr;
+ if (ptr.p->masterData.dropTrig.tableId == RNIL) {
+ jam();
+ ptr.p->tables.first(tabPtr);
+ } else {
+ jam();
+ ndbrequire(findTable(ptr, tabPtr, ptr.p->masterData.dropTrig.tableId));
+ ptr.p->tables.next(tabPtr);
+ }//if
+ if (tabPtr.i != RNIL) {
+ jam();
+ sendDropTrig(signal, ptr, tabPtr);
+ } else {
+ jam();
+ ptr.p->masterData.dropTrig.tableId = RNIL;
+
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::OkToClean);
+
+ if(ptr.p->masterData.state.getState() == STOPPING) {
+ jam();
+ sendStopBackup(signal, ptr);
+ return;
+ }//if
+ ndbrequire(ptr.p->masterData.state.getState() == ABORTING);
+ masterSendAbortBackup(signal, ptr);
+ }//if
+}
+
+void
+Backup::sendDropTrig(Signal* signal, BackupRecordPtr ptr, TablePtr tabPtr)
+{
+ jam();
+ DropTrigReq * req = (DropTrigReq *)signal->getDataPtrSend();
+
+ ptr.p->masterData.gsn = GSN_DROP_TRIG_REQ;
+ ptr.p->masterData.sendCounter = 0;
+
+ req->setConnectionPtr(ptr.i);
+ req->setUserRef(reference()); // Sending to myself
+ req->setRequestType(DropTrigReq::RT_USER);
+ req->setIndexId(RNIL);
+ req->setTriggerInfo(0); // not used on DROP via DICT
+
+ char triggerName[MAX_TAB_NAME_SIZE];
+ Uint32 nameBuffer[2 + ((MAX_TAB_NAME_SIZE + 3) >> 2)]; // SP string
+ LinearWriter w(nameBuffer, sizeof(nameBuffer) >> 2);
+ LinearSectionPtr lsPtr[3];
+
+ ptr.p->masterData.dropTrig.tableId = tabPtr.p->tableId;
+ req->setTableId(tabPtr.p->tableId);
+
+ for (int i = 0; i < 3; i++) {
+ Uint32 id = tabPtr.p->triggerIds[i];
+ req->setTriggerId(id);
+ if (id != ILLEGAL_TRIGGER_ID) {
+ sendSignal(DBDICT_REF, GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ } else {
+ BaseString::snprintf(triggerName, sizeof(triggerName), triggerNameFormat[i],
+ ptr.p->backupId, tabPtr.p->tableId);
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = nameBuffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(DBDICT_REF, GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB, lsPtr, 1);
+ }
+ ptr.p->masterData.sendCounter ++;
+ }
+}
+
+void
+Backup::execDROP_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+
+ DropTrigRef* ref = (DropTrigRef*)signal->getDataPtr();
+ const Uint32 ptrI = ref->getConnectionPtr();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ //ndbrequire(ref->getErrorCode() == DropTrigRef::NoSuchTrigger);
+ dropTrigReply(signal, ptr);
+}
+
+void
+Backup::execDROP_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+
+ DropTrigConf* conf = (DropTrigConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->getConnectionPtr();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ dropTrigReply(signal, ptr);
+}
+
+void
+Backup::dropTrigReply(Signal* signal, BackupRecordPtr ptr)
+{
+
+ CRASH_INSERTION((10012));
+
+ ndbrequire(ptr.p->masterRef == reference());
+ ndbrequire(ptr.p->masterData.gsn == GSN_DROP_TRIG_REQ);
+ ndbrequire(ptr.p->masterData.sendCounter.done() == false);
+
+ ptr.p->masterData.sendCounter--;
+ if(ptr.p->masterData.sendCounter.done() == false){
+ jam();
+ return;
+ }//if
+
+ ptr.p->masterData.gsn = 0;
+ sendDropTrig(signal, ptr); // recursive next
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Stop backup
+ *
+ *****************************************************************************/
+void
+Backup::execSTOP_BACKUP_REF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(0);
+}
+
+void
+Backup::sendStopBackup(Signal* signal, BackupRecordPtr ptr)
+{
+ jam();
+ ptr.p->masterData.gsn = GSN_STOP_BACKUP_REQ;
+
+ StopBackupReq* stop = (StopBackupReq*)signal->getDataPtrSend();
+ stop->backupPtr = ptr.i;
+ stop->backupId = ptr.p->backupId;
+ stop->startGCP = ptr.p->startGCP;
+ stop->stopGCP = ptr.p->stopGCP;
+
+ sendSignalAllWait(ptr, GSN_STOP_BACKUP_REQ, signal,
+ StopBackupReq::SignalLength);
+}
+
+void
+Backup::execSTOP_BACKUP_CONF(Signal* signal)
+{
+ jamEntry();
+
+ StopBackupConf* conf = (StopBackupConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->backupPtr;
+ const Uint32 backupId = conf->backupId;
+ const Uint32 nodeId = refToNode(signal->senderBlockRef());
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ masterAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->noOfLogBytes += conf->noOfLogBytes;
+ ptr.p->noOfLogRecords += conf->noOfLogRecords;
+
+ stopBackupReply(signal, ptr, nodeId);
+}
+
+void
+Backup::stopBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32 nodeId)
+{
+ CRASH_INSERTION((10013));
+
+ if (!haveAllSignals(ptr, GSN_STOP_BACKUP_REQ, nodeId)) {
+ jam();
+ return;
+ }
+
+ // ptr.p->masterData.state.setState(INITIAL);
+
+ // send backup complete first to slaves so that they know
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::BackupComplete);
+
+ BackupCompleteRep * rep = (BackupCompleteRep*)signal->getDataPtrSend();
+ rep->backupId = ptr.p->backupId;
+ rep->senderData = ptr.p->clientData;
+ rep->startGCP = ptr.p->startGCP;
+ rep->stopGCP = ptr.p->stopGCP;
+ rep->noOfBytes = ptr.p->noOfBytes;
+ rep->noOfRecords = ptr.p->noOfRecords;
+ rep->noOfLogBytes = ptr.p->noOfLogBytes;
+ rep->noOfLogRecords = ptr.p->noOfLogRecords;
+ rep->nodes = ptr.p->nodes;
+ sendSignal(ptr.p->clientRef, GSN_BACKUP_COMPLETE_REP, signal,
+ BackupCompleteRep::SignalLength, JBB);
+
+ signal->theData[0] = NDB_LE_BackupCompleted;
+ signal->theData[1] = ptr.p->clientRef;
+ signal->theData[2] = ptr.p->backupId;
+ signal->theData[3] = ptr.p->startGCP;
+ signal->theData[4] = ptr.p->stopGCP;
+ signal->theData[5] = ptr.p->noOfBytes;
+ signal->theData[6] = ptr.p->noOfRecords;
+ signal->theData[7] = ptr.p->noOfLogBytes;
+ signal->theData[8] = ptr.p->noOfLogRecords;
+ ptr.p->nodes.copyto(NdbNodeBitmask::Size, signal->theData+9);
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 9+NdbNodeBitmask::Size, JBB);
+}
+
+/*****************************************************************************
+ *
+ * Master functionallity - Abort backup
+ *
+ *****************************************************************************/
+void
+Backup::masterAbort(Signal* signal, BackupRecordPtr ptr, bool controlledAbort)
+{
+ if(ptr.p->masterData.state.getState() == ABORTING) {
+#ifdef DEBUG_ABORT
+ ndbout_c("---- Master already aborting");
+#endif
+ jam();
+ return;
+ }
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("************ masterAbort");
+#endif
+
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::BackupFailure);
+ if (!ptr.p->checkError())
+ ptr.p->errorCode = AbortBackupOrd::BackupFailureDueToNodeFail;
+
+ const State s = ptr.p->masterData.state.getState();
+
+ ptr.p->masterData.state.setState(ABORTING);
+
+ ndbrequire(s == INITIAL ||
+ s == STARTED ||
+ s == DEFINING ||
+ s == DEFINED ||
+ s == SCANNING ||
+ s == STOPPING ||
+ s == ABORTING);
+ if(ptr.p->masterData.gsn == GSN_UTIL_SEQUENCE_REQ) {
+ jam();
+ DEBUG_OUT("masterAbort: gsn = GSN_UTIL_SEQUENCE_REQ");
+ //-------------------------------------------------------
+ // We are waiting for UTIL_SEQUENCE response. We rely on
+ // this to arrive and check for ABORTING in response.
+ // No slaves are involved at this point and ABORT simply
+ // results in BACKUP_REF to client
+ //-------------------------------------------------------
+ /**
+ * Waiting for Sequence Id
+ * @see execUTIL_SEQUENCE_CONF
+ */
+ return;
+ }//if
+
+ if(ptr.p->masterData.gsn == GSN_UTIL_LOCK_REQ) {
+ jam();
+ DEBUG_OUT("masterAbort: gsn = GSN_UTIL_LOCK_REQ");
+ //-------------------------------------------------------
+ // We are waiting for UTIL_LOCK response (mutex). We rely on
+ // this to arrive and check for ABORTING in response.
+ // No slaves are involved at this point and ABORT simply
+ // results in BACKUP_REF to client
+ //-------------------------------------------------------
+ /**
+ * Waiting for lock
+ * @see execUTIL_LOCK_CONF
+ */
+ return;
+ }//if
+
+ /**
+ * Unlock mutexes only at master
+ */
+ jam();
+ Mutex mutex1(signal, c_mutexMgr, ptr.p->masterData.m_dictCommitTableMutex);
+ jam();
+ mutex1.unlock(); // ignore response
+
+ jam();
+ Mutex mutex2(signal, c_mutexMgr, ptr.p->masterData.m_defineBackupMutex);
+ jam();
+ mutex2.unlock(); // ignore response
+
+ if (!controlledAbort) {
+ jam();
+ if (s == DEFINING) {
+ jam();
+//-------------------------------------------------------
+// If we are in the defining phase all work is done by
+// slaves. No triggers have been allocated thus slaves
+// may free all "Master" resources, let them know...
+//-------------------------------------------------------
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::OkToClean);
+ return;
+ }//if
+ if (s == DEFINED) {
+ jam();
+//-------------------------------------------------------
+// DEFINED is the state when triggers are created. We rely
+// on that DICT will report create trigger failure in case
+// of node failure. Thus no special action is needed here.
+// We will check for errorCode != 0 when receiving
+// replies on create trigger.
+//-------------------------------------------------------
+ return;
+ }//if
+ if(ptr.p->masterData.gsn == GSN_WAIT_GCP_REQ) {
+ jam();
+ DEBUG_OUT("masterAbort: gsn = GSN_WAIT_GCP_REQ");
+//-------------------------------------------------------
+// We are waiting for WAIT_GCP response. We rely on
+// this to arrive and check for ABORTING in response.
+//-------------------------------------------------------
+
+ /**
+ * Waiting for GCP
+ * @see execWAIT_GCP_CONF
+ */
+ return;
+ }//if
+
+ if(ptr.p->masterData.gsn == GSN_ALTER_TRIG_REQ) {
+ jam();
+ DEBUG_OUT("masterAbort: gsn = GSN_ALTER_TRIG_REQ");
+//-------------------------------------------------------
+// We are waiting for ALTER_TRIG response. We rely on
+// this to arrive and check for ABORTING in response.
+//-------------------------------------------------------
+
+ /**
+ * All triggers haven't been created yet
+ */
+ return;
+ }//if
+
+ if(ptr.p->masterData.gsn == GSN_DROP_TRIG_REQ) {
+ jam();
+ DEBUG_OUT("masterAbort: gsn = GSN_DROP_TRIG_REQ");
+//-------------------------------------------------------
+// We are waiting for DROP_TRIG response. We rely on
+// this to arrive and will continue dropping triggers
+// until completed.
+//-------------------------------------------------------
+
+ /**
+ * I'm currently dropping the trigger
+ */
+ return;
+ }//if
+ }//if
+
+//-------------------------------------------------------
+// If we are waiting for START_BACKUP responses we can
+// safely start dropping triggers (state == STARTED).
+// We will ignore any START_BACKUP responses after this.
+//-------------------------------------------------------
+ DEBUG_OUT("masterAbort: sendDropTrig");
+ sendDropTrig(signal, ptr); // dropping due to error
+}
+
+void
+Backup::masterSendAbortBackup(Signal* signal, BackupRecordPtr ptr)
+{
+ if (ptr.p->masterData.state.getState() != ABORTING) {
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::BackupFailure);
+ ptr.p->masterData.state.setState(ABORTING);
+ }
+ const State s = ptr.p->masterData.state.getAbortState();
+
+ /**
+ * First inform to client
+ */
+ if(s == DEFINING) {
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("** Abort: sending BACKUP_REF to mgmtsrvr");
+#endif
+ sendBackupRef(ptr.p->clientRef, signal, ptr.p->clientData,
+ ptr.p->errorCode);
+
+ } else {
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("** Abort: sending BACKUP_ABORT_REP to mgmtsrvr");
+#endif
+ BackupAbortRep* rep = (BackupAbortRep*)signal->getDataPtrSend();
+ rep->backupId = ptr.p->backupId;
+ rep->senderData = ptr.p->clientData;
+ rep->reason = ptr.p->errorCode;
+ sendSignal(ptr.p->clientRef, GSN_BACKUP_ABORT_REP, signal,
+ BackupAbortRep::SignalLength, JBB);
+
+ signal->theData[0] = NDB_LE_BackupAborted;
+ signal->theData[1] = ptr.p->clientRef;
+ signal->theData[2] = ptr.p->backupId;
+ signal->theData[3] = ptr.p->errorCode;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+ }//if
+
+ // ptr.p->masterData.state.setState(INITIAL);
+
+ sendAbortBackupOrd(signal, ptr, AbortBackupOrd::BackupFailure);
+}
+
+/*****************************************************************************
+ *
+ * Slave functionallity: Define Backup
+ *
+ *****************************************************************************/
+void
+Backup::defineBackupRef(Signal* signal, BackupRecordPtr ptr, Uint32 errCode)
+{
+ if (ptr.p->slaveState.getState() == ABORTING) {
+ jam();
+ return;
+ }
+ ptr.p->slaveState.setState(ABORTING);
+
+ if (errCode != 0) {
+ jam();
+ ptr.p->setErrorCode(errCode);
+ }//if
+ ndbrequire(ptr.p->errorCode != 0);
+
+ DefineBackupRef* ref = (DefineBackupRef*)signal->getDataPtrSend();
+ ref->backupId = ptr.p->backupId;
+ ref->backupPtr = ptr.i;
+ ref->errorCode = ptr.p->errorCode;
+ sendSignal(ptr.p->masterRef, GSN_DEFINE_BACKUP_REF, signal,
+ DefineBackupRef::SignalLength, JBB);
+
+ closeFiles(signal, ptr);
+}
+
+void
+Backup::execDEFINE_BACKUP_REQ(Signal* signal)
+{
+ jamEntry();
+
+ DefineBackupReq* req = (DefineBackupReq*)signal->getDataPtr();
+
+ BackupRecordPtr ptr;
+ const Uint32 ptrI = req->backupPtr;
+ const Uint32 backupId = req->backupId;
+ const BlockReference senderRef = req->senderRef;
+
+ if(senderRef == reference()){
+ /**
+ * Signal sent from myself -> record already seized
+ */
+ jam();
+ c_backupPool.getPtr(ptr, ptrI);
+ } else { // from other node
+ jam();
+#ifdef DEBUG_ABORT
+ dumpUsedResources();
+#endif
+ if(!c_backups.seizeId(ptr, ptrI)) {
+ jam();
+ ndbrequire(false); // If master has succeeded slave should succed
+ }//if
+ }//if
+
+ CRASH_INSERTION((10014));
+
+ ptr.p->slaveState.forceState(INITIAL);
+ ptr.p->slaveState.setState(DEFINING);
+ ptr.p->errorCode = 0;
+ ptr.p->clientRef = req->clientRef;
+ ptr.p->clientData = req->clientData;
+ ptr.p->masterRef = senderRef;
+ ptr.p->nodes = req->nodes;
+ ptr.p->backupId = backupId;
+ ptr.p->backupKey[0] = req->backupKey[0];
+ ptr.p->backupKey[1] = req->backupKey[1];
+ ptr.p->backupDataLen = req->backupDataLen;
+ ptr.p->masterData.dropTrig.tableId = RNIL;
+ ptr.p->masterData.alterTrig.tableId = RNIL;
+ ptr.p->noOfBytes = 0;
+ ptr.p->noOfRecords = 0;
+ ptr.p->noOfLogBytes = 0;
+ ptr.p->noOfLogRecords = 0;
+ ptr.p->currGCP = 0;
+
+ /**
+ * Allocate files
+ */
+ BackupFilePtr files[3];
+ Uint32 noOfPages[] = {
+ NO_OF_PAGES_META_FILE,
+ 2, // 32k
+ 0 // 3M
+ };
+ const Uint32 maxInsert[] = {
+ 2048, // Temporarily to solve TR515
+ //25, // 100 bytes
+ 2048, // 4k
+ 16*3000, // Max 16 tuples
+ };
+ Uint32 minWrite[] = {
+ 8192,
+ 8192,
+ 32768
+ };
+ Uint32 maxWrite[] = {
+ 8192,
+ 8192,
+ 32768
+ };
+
+ minWrite[1] = c_defaults.m_minWriteSize;
+ maxWrite[1] = c_defaults.m_maxWriteSize;
+ noOfPages[1] = (c_defaults.m_logBufferSize + sizeof(Page32) - 1) /
+ sizeof(Page32);
+ minWrite[2] = c_defaults.m_minWriteSize;
+ maxWrite[2] = c_defaults.m_maxWriteSize;
+ noOfPages[2] = (c_defaults.m_dataBufferSize + sizeof(Page32) - 1) /
+ sizeof(Page32);
+
+ for(Uint32 i = 0; i<3; i++) {
+ jam();
+ if(!ptr.p->files.seize(files[i])) {
+ jam();
+ defineBackupRef(signal, ptr,
+ DefineBackupRef::FailedToAllocateFileRecord);
+ return;
+ }//if
+
+ files[i].p->tableId = RNIL;
+ files[i].p->backupPtr = ptr.i;
+ files[i].p->filePointer = RNIL;
+ files[i].p->fileDone = 0;
+ files[i].p->fileOpened = 0;
+ files[i].p->fileRunning = 0;
+ files[i].p->scanRunning = 0;
+ files[i].p->errorCode = 0;
+
+ if(files[i].p->pages.seize(noOfPages[i]) == false) {
+ jam();
+ DEBUG_OUT("Failed to seize " << noOfPages[i] << " pages");
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedToAllocateBuffers);
+ return;
+ }//if
+ Page32Ptr pagePtr;
+ files[i].p->pages.getPtr(pagePtr, 0);
+
+ const char * msg = files[i].p->
+ operation.dataBuffer.setup((Uint32*)pagePtr.p,
+ noOfPages[i] * (sizeof(Page32) >> 2),
+ 128,
+ minWrite[i] >> 2,
+ maxWrite[i] >> 2,
+ maxInsert[i]);
+ if(msg != 0) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedToSetupFsBuffers);
+ return;
+ }//if
+ }//for
+ files[0].p->fileType = BackupFormat::CTL_FILE;
+ files[1].p->fileType = BackupFormat::LOG_FILE;
+ files[2].p->fileType = BackupFormat::DATA_FILE;
+
+ ptr.p->ctlFilePtr = files[0].i;
+ ptr.p->logFilePtr = files[1].i;
+ ptr.p->dataFilePtr = files[2].i;
+
+ if (!verifyNodesAlive(ptr.p->nodes)) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::Undefined);
+ // sendBackupRef(signal, ptr,
+ // ptr.p->errorCode?ptr.p->errorCode:BackupRef::Undefined);
+ return;
+ }//if
+ if (ERROR_INSERTED(10027)) {
+ jam();
+ defineBackupRef(signal, ptr, 327);
+ // sendBackupRef(signal, ptr, 327);
+ return;
+ }//if
+
+ if(ptr.p->backupDataLen == 0) {
+ jam();
+ backupAllData(signal, ptr);
+ return;
+ }//if
+
+ /**
+ * Not implemented
+ */
+ ndbrequire(0);
+}
+
+void
+Backup::backupAllData(Signal* signal, BackupRecordPtr ptr)
+{
+ /**
+ * Get all tables from dict
+ */
+ ListTablesReq * req = (ListTablesReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = ptr.i;
+ req->requestData = 0;
+ sendSignal(DBDICT_REF, GSN_LIST_TABLES_REQ, signal,
+ ListTablesReq::SignalLength, JBB);
+}
+
+void
+Backup::execLIST_TABLES_CONF(Signal* signal)
+{
+ jamEntry();
+
+ ListTablesConf* conf = (ListTablesConf*)signal->getDataPtr();
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, conf->senderData);
+
+ const Uint32 len = signal->length() - ListTablesConf::HeaderLength;
+ for(unsigned int i = 0; i<len; i++) {
+ jam();
+ Uint32 tableId = ListTablesConf::getTableId(conf->tableData[i]);
+ Uint32 tableType = ListTablesConf::getTableType(conf->tableData[i]);
+ if (!DictTabInfo::isTable(tableType) && !DictTabInfo::isIndex(tableType)){
+ jam();
+ continue;
+ }//if
+ TablePtr tabPtr;
+ ptr.p->tables.seize(tabPtr);
+ if(tabPtr.i == RNIL) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedToAllocateTables);
+ return;
+ }//if
+ tabPtr.p->tableId = tableId;
+ tabPtr.p->tableType = tableType;
+ }//for
+
+ if(len == ListTablesConf::DataLength) {
+ jam();
+ /**
+ * Not finished...
+ */
+ return;
+ }//if
+
+ defineSlaveAbortCheck();
+
+ /**
+ * All tables fetched
+ */
+ openFiles(signal, ptr);
+}
+
+void
+Backup::openFiles(Signal* signal, BackupRecordPtr ptr)
+{
+ jam();
+
+ BackupFilePtr filePtr;
+
+ FsOpenReq * req = (FsOpenReq *)signal->getDataPtrSend();
+ req->userReference = reference();
+ req->fileFlags =
+ FsOpenReq::OM_WRITEONLY |
+ FsOpenReq::OM_TRUNCATE |
+ FsOpenReq::OM_CREATE |
+ FsOpenReq::OM_APPEND |
+ FsOpenReq::OM_SYNC;
+ FsOpenReq::v2_setCount(req->fileNumber, 0xFFFFFFFF);
+
+ /**
+ * Ctl file
+ */
+ c_backupFilePool.getPtr(filePtr, ptr.p->ctlFilePtr);
+ ndbrequire(filePtr.p->fileRunning == 0);
+ filePtr.p->fileRunning = 1;
+
+ req->userPointer = filePtr.i;
+ FsOpenReq::setVersion(req->fileNumber, 2);
+ FsOpenReq::setSuffix(req->fileNumber, FsOpenReq::S_CTL);
+ FsOpenReq::v2_setSequence(req->fileNumber, ptr.p->backupId);
+ FsOpenReq::v2_setNodeId(req->fileNumber, getOwnNodeId());
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, FsOpenReq::SignalLength, JBA);
+
+ /**
+ * Log file
+ */
+ c_backupFilePool.getPtr(filePtr, ptr.p->logFilePtr);
+ ndbrequire(filePtr.p->fileRunning == 0);
+ filePtr.p->fileRunning = 1;
+
+ req->userPointer = filePtr.i;
+ FsOpenReq::setVersion(req->fileNumber, 2);
+ FsOpenReq::setSuffix(req->fileNumber, FsOpenReq::S_LOG);
+ FsOpenReq::v2_setSequence(req->fileNumber, ptr.p->backupId);
+ FsOpenReq::v2_setNodeId(req->fileNumber, getOwnNodeId());
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, FsOpenReq::SignalLength, JBA);
+
+ /**
+ * Data file
+ */
+ c_backupFilePool.getPtr(filePtr, ptr.p->dataFilePtr);
+ ndbrequire(filePtr.p->fileRunning == 0);
+ filePtr.p->fileRunning = 1;
+
+ req->userPointer = filePtr.i;
+ FsOpenReq::setVersion(req->fileNumber, 2);
+ FsOpenReq::setSuffix(req->fileNumber, FsOpenReq::S_DATA);
+ FsOpenReq::v2_setSequence(req->fileNumber, ptr.p->backupId);
+ FsOpenReq::v2_setNodeId(req->fileNumber, getOwnNodeId());
+ FsOpenReq::v2_setCount(req->fileNumber, 0);
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, FsOpenReq::SignalLength, JBA);
+}
+
+void
+Backup::execFSOPENREF(Signal* signal)
+{
+ jamEntry();
+
+ FsRef * ref = (FsRef *)signal->getDataPtr();
+
+ const Uint32 userPtr = ref->userPointer;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, userPtr);
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+ ptr.p->setErrorCode(ref->errorCode);
+ openFilesReply(signal, ptr, filePtr);
+}
+
+void
+Backup::execFSOPENCONF(Signal* signal)
+{
+ jamEntry();
+
+ FsConf * conf = (FsConf *)signal->getDataPtr();
+
+ const Uint32 userPtr = conf->userPointer;
+ const Uint32 filePointer = conf->filePointer;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, userPtr);
+ filePtr.p->filePointer = filePointer;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+
+ ndbrequire(filePtr.p->fileOpened == 0);
+ filePtr.p->fileOpened = 1;
+ openFilesReply(signal, ptr, filePtr);
+}
+
+void
+Backup::openFilesReply(Signal* signal,
+ BackupRecordPtr ptr, BackupFilePtr filePtr)
+{
+ jam();
+
+ /**
+ * Mark files as "opened"
+ */
+ ndbrequire(filePtr.p->fileRunning == 1);
+ filePtr.p->fileRunning = 0;
+
+ /**
+ * Check if all files have recived open_reply
+ */
+ for(ptr.p->files.first(filePtr); filePtr.i!=RNIL;ptr.p->files.next(filePtr))
+ {
+ jam();
+ if(filePtr.p->fileRunning == 1) {
+ jam();
+ return;
+ }//if
+ }//for
+
+ defineSlaveAbortCheck();
+
+ /**
+ * Did open succeed for all files
+ */
+ if(ptr.p->checkError()) {
+ jam();
+ defineBackupRef(signal, ptr);
+ return;
+ }//if
+
+ /**
+ * Insert file headers
+ */
+ ptr.p->files.getPtr(filePtr, ptr.p->ctlFilePtr);
+ if(!insertFileHeader(BackupFormat::CTL_FILE, ptr.p, filePtr.p)) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedInsertFileHeader);
+ return;
+ }//if
+
+ ptr.p->files.getPtr(filePtr, ptr.p->logFilePtr);
+ if(!insertFileHeader(BackupFormat::LOG_FILE, ptr.p, filePtr.p)) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedInsertFileHeader);
+ return;
+ }//if
+
+ ptr.p->files.getPtr(filePtr, ptr.p->dataFilePtr);
+ if(!insertFileHeader(BackupFormat::DATA_FILE, ptr.p, filePtr.p)) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedInsertFileHeader);
+ return;
+ }//if
+
+ /**
+ * Start CTL file thread
+ */
+ ptr.p->files.getPtr(filePtr, ptr.p->ctlFilePtr);
+ filePtr.p->fileRunning = 1;
+
+ signal->theData[0] = BackupContinueB::START_FILE_THREAD;
+ signal->theData[1] = ptr.p->ctlFilePtr;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 100, 2);
+
+ /**
+ * Insert table list in ctl file
+ */
+ FsBuffer & buf = filePtr.p->operation.dataBuffer;
+
+ const Uint32 sz =
+ (sizeof(BackupFormat::CtlFile::TableList) >> 2) +
+ ptr.p->tables.noOfElements() - 1;
+
+ Uint32 * dst;
+ ndbrequire(sz < buf.getMaxWrite());
+ if(!buf.getWritePtr(&dst, sz)) {
+ jam();
+ defineBackupRef(signal, ptr, DefineBackupRef::FailedInsertTableList);
+ return;
+ }//if
+
+ BackupFormat::CtlFile::TableList* tl =
+ (BackupFormat::CtlFile::TableList*)dst;
+ tl->SectionType = htonl(BackupFormat::TABLE_LIST);
+ tl->SectionLength = htonl(sz);
+
+ TablePtr tabPtr;
+ Uint32 count = 0;
+ for(ptr.p->tables.first(tabPtr);
+ tabPtr.i != RNIL;
+ ptr.p->tables.next(tabPtr)){
+ jam();
+ tl->TableIds[count] = htonl(tabPtr.p->tableId);
+ count++;
+ }//for
+
+ buf.updateWritePtr(sz);
+
+ /**
+ * Start getting table definition data
+ */
+ ndbrequire(ptr.p->tables.first(tabPtr));
+
+ signal->theData[0] = BackupContinueB::BUFFER_FULL_META;
+ signal->theData[1] = ptr.i;
+ signal->theData[2] = tabPtr.i;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 100, 3);
+ return;
+}
+
+bool
+Backup::insertFileHeader(BackupFormat::FileType ft,
+ BackupRecord * ptrP,
+ BackupFile * filePtrP){
+ FsBuffer & buf = filePtrP->operation.dataBuffer;
+
+ const Uint32 sz = sizeof(BackupFormat::FileHeader) >> 2;
+
+ Uint32 * dst;
+ ndbrequire(sz < buf.getMaxWrite());
+ if(!buf.getWritePtr(&dst, sz)) {
+ jam();
+ return false;
+ }//if
+
+ BackupFormat::FileHeader* header = (BackupFormat::FileHeader*)dst;
+ ndbrequire(sizeof(header->Magic) == sizeof(BACKUP_MAGIC));
+ memcpy(header->Magic, BACKUP_MAGIC, sizeof(BACKUP_MAGIC));
+ header->NdbVersion = htonl(NDB_VERSION);
+ header->SectionType = htonl(BackupFormat::FILE_HEADER);
+ header->SectionLength = htonl(sz - 3);
+ header->FileType = htonl(ft);
+ header->BackupId = htonl(ptrP->backupId);
+ header->BackupKey_0 = htonl(ptrP->backupKey[0]);
+ header->BackupKey_1 = htonl(ptrP->backupKey[1]);
+ header->ByteOrder = 0x12345678;
+
+ buf.updateWritePtr(sz);
+ return true;
+}
+
+void
+Backup::execGET_TABINFOREF(Signal* signal)
+{
+ GetTabInfoRef * ref = (GetTabInfoRef*)signal->getDataPtr();
+
+ const Uint32 senderData = ref->senderData;
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, senderData);
+
+ defineSlaveAbortCheck();
+
+ defineBackupRef(signal, ptr, ref->errorCode);
+}
+
+void
+Backup::execGET_TABINFO_CONF(Signal* signal)
+{
+ jamEntry();
+
+ if(!assembleFragments(signal)) {
+ jam();
+ return;
+ }//if
+
+ GetTabInfoConf * const conf = (GetTabInfoConf*)signal->getDataPtr();
+ //const Uint32 senderRef = info->senderRef;
+ const Uint32 len = conf->totalLen;
+ const Uint32 senderData = conf->senderData;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, senderData);
+
+ defineSlaveAbortCheck();
+
+ SegmentedSectionPtr dictTabInfoPtr;
+ signal->getSection(dictTabInfoPtr, GetTabInfoConf::DICT_TAB_INFO);
+ ndbrequire(dictTabInfoPtr.sz == len);
+
+ /**
+ * No of pages needed
+ */
+ const Uint32 noPages = (len + sizeof(Page32) - 1) / sizeof(Page32);
+ if(ptr.p->pages.getSize() < noPages) {
+ jam();
+ ptr.p->pages.release();
+ if(ptr.p->pages.seize(noPages) == false) {
+ jam();
+ ptr.p->setErrorCode(DefineBackupRef::FailedAllocateTableMem);
+ ndbrequire(false);
+ releaseSections(signal);
+ defineBackupRef(signal, ptr);
+ return;
+ }//if
+ }//if
+
+ BackupFilePtr filePtr;
+ ptr.p->files.getPtr(filePtr, ptr.p->ctlFilePtr);
+ FsBuffer & buf = filePtr.p->operation.dataBuffer;
+ { // Write into ctl file
+ Uint32* dst, dstLen = len + 2;
+ if(!buf.getWritePtr(&dst, dstLen)) {
+ jam();
+ ndbrequire(false);
+ ptr.p->setErrorCode(DefineBackupRef::FailedAllocateTableMem);
+ releaseSections(signal);
+ defineBackupRef(signal, ptr);
+ return;
+ }//if
+ if(dst != 0) {
+ jam();
+
+ BackupFormat::CtlFile::TableDescription * desc =
+ (BackupFormat::CtlFile::TableDescription*)dst;
+ desc->SectionType = htonl(BackupFormat::TABLE_DESCRIPTION);
+ desc->SectionLength = htonl(len + 2);
+ dst += 2;
+
+ copy(dst, dictTabInfoPtr);
+ buf.updateWritePtr(dstLen);
+ }//if
+ }
+
+ ndbrequire(ptr.p->pages.getSize() >= noPages);
+ Page32Ptr pagePtr;
+ ptr.p->pages.getPtr(pagePtr, 0);
+ copy(&pagePtr.p->data[0], dictTabInfoPtr);
+ releaseSections(signal);
+
+ if(ptr.p->checkError()) {
+ jam();
+ defineBackupRef(signal, ptr);
+ return;
+ }//if
+
+ TablePtr tabPtr = parseTableDescription(signal, ptr, len);
+ if(tabPtr.i == RNIL) {
+ jam();
+ defineBackupRef(signal, ptr);
+ return;
+ }//if
+
+ TablePtr tmp = tabPtr;
+ ptr.p->tables.next(tabPtr);
+ if(DictTabInfo::isIndex(tmp.p->tableType)){
+ ptr.p->tables.release(tmp);
+ }
+
+ if(tabPtr.i == RNIL) {
+ jam();
+
+ ptr.p->pages.release();
+
+ ndbrequire(ptr.p->tables.first(tabPtr));
+ signal->theData[0] = RNIL;
+ signal->theData[1] = tabPtr.p->tableId;
+ signal->theData[2] = ptr.i;
+ sendSignal(DBDIH_REF, GSN_DI_FCOUNTREQ, signal, 3, JBB);
+ return;
+ }//if
+
+ signal->theData[0] = BackupContinueB::BUFFER_FULL_META;
+ signal->theData[1] = ptr.i;
+ signal->theData[2] = tabPtr.i;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 100, 3);
+ return;
+}
+
+Backup::TablePtr
+Backup::parseTableDescription(Signal* signal, BackupRecordPtr ptr, Uint32 len)
+{
+
+ Page32Ptr pagePtr;
+ ptr.p->pages.getPtr(pagePtr, 0);
+
+ SimplePropertiesLinearReader it(&pagePtr.p->data[0], len);
+
+ it.first();
+
+ DictTabInfo::Table tmpTab; tmpTab.init();
+ SimpleProperties::UnpackStatus stat;
+ stat = SimpleProperties::unpack(it, &tmpTab,
+ DictTabInfo::TableMapping,
+ DictTabInfo::TableMappingSize,
+ true, true);
+ ndbrequire(stat == SimpleProperties::Break);
+
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, tmpTab.TableId));
+ if(DictTabInfo::isIndex(tabPtr.p->tableType)){
+ jam();
+ return tabPtr;
+ }
+
+ /**
+ * Initialize table object
+ */
+ tabPtr.p->frag_mask = RNIL;
+
+ tabPtr.p->schemaVersion = tmpTab.TableVersion;
+ tabPtr.p->noOfAttributes = tmpTab.NoOfAttributes;
+ tabPtr.p->noOfNull = 0;
+ tabPtr.p->noOfVariable = 0; // Computed while iterating over attribs
+ tabPtr.p->sz_FixedAttributes = 0; // Computed while iterating over attribs
+ tabPtr.p->triggerIds[0] = ILLEGAL_TRIGGER_ID;
+ tabPtr.p->triggerIds[1] = ILLEGAL_TRIGGER_ID;
+ tabPtr.p->triggerIds[2] = ILLEGAL_TRIGGER_ID;
+ tabPtr.p->triggerAllocated[0] = false;
+ tabPtr.p->triggerAllocated[1] = false;
+ tabPtr.p->triggerAllocated[2] = false;
+
+ if(tabPtr.p->attributes.seize(tabPtr.p->noOfAttributes) == false) {
+ jam();
+ ptr.p->setErrorCode(DefineBackupRef::FailedToAllocateAttributeRecord);
+ tabPtr.i = RNIL;
+ return tabPtr;
+ }//if
+
+ const Uint32 count = tabPtr.p->noOfAttributes;
+ for(Uint32 i = 0; i<count; i++) {
+ jam();
+ DictTabInfo::Attribute tmp; tmp.init();
+ stat = SimpleProperties::unpack(it, &tmp,
+ DictTabInfo::AttributeMapping,
+ DictTabInfo::AttributeMappingSize,
+ true, true);
+
+ ndbrequire(stat == SimpleProperties::Break);
+
+ const Uint32 arr = tmp.AttributeArraySize;
+ const Uint32 sz = 1 << tmp.AttributeSize;
+ const Uint32 sz32 = (sz * arr + 31) >> 5;
+
+ AttributePtr attrPtr;
+ tabPtr.p->attributes.getPtr(attrPtr, tmp.AttributeId);
+
+ attrPtr.p->data.nullable = tmp.AttributeNullableFlag;
+ attrPtr.p->data.fixed = (tmp.AttributeArraySize != 0);
+ attrPtr.p->data.sz32 = sz32;
+
+ /**
+ * Either
+ * 1) Fixed
+ * 2) Nullable
+ * 3) Variable
+ */
+ if(attrPtr.p->data.fixed == true && attrPtr.p->data.nullable == false) {
+ jam();
+ attrPtr.p->data.offset = tabPtr.p->sz_FixedAttributes;
+ tabPtr.p->sz_FixedAttributes += sz32;
+ }//if
+
+ if(attrPtr.p->data.fixed == true && attrPtr.p->data.nullable == true) {
+ jam();
+ attrPtr.p->data.offset = 0;
+
+ attrPtr.p->data.offsetNull = tabPtr.p->noOfNull;
+ tabPtr.p->noOfNull++;
+ tabPtr.p->noOfVariable++;
+ }//if
+
+ if(attrPtr.p->data.fixed == false) {
+ jam();
+ tabPtr.p->noOfVariable++;
+ ndbrequire(0);
+ }//if
+
+ it.next(); // Move Past EndOfAttribute
+ }//for
+ return tabPtr;
+}
+
+void
+Backup::execDI_FCOUNTCONF(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 userPtr = signal->theData[0];
+ const Uint32 fragCount = signal->theData[1];
+ const Uint32 tableId = signal->theData[2];
+ const Uint32 senderData = signal->theData[3];
+
+ ndbrequire(userPtr == RNIL && signal->length() == 5);
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, senderData);
+
+ defineSlaveAbortCheck();
+
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, tableId));
+
+ ndbrequire(tabPtr.p->fragments.seize(fragCount) != false);
+ tabPtr.p->frag_mask = calculate_frag_mask(fragCount);
+ for(Uint32 i = 0; i<fragCount; i++) {
+ jam();
+ FragmentPtr fragPtr;
+ tabPtr.p->fragments.getPtr(fragPtr, i);
+ fragPtr.p->scanned = 0;
+ fragPtr.p->scanning = 0;
+ fragPtr.p->tableId = tableId;
+ fragPtr.p->node = RNIL;
+ }//for
+
+ /**
+ * Next table
+ */
+ if(ptr.p->tables.next(tabPtr)) {
+ jam();
+ signal->theData[0] = RNIL;
+ signal->theData[1] = tabPtr.p->tableId;
+ signal->theData[2] = ptr.i;
+ sendSignal(DBDIH_REF, GSN_DI_FCOUNTREQ, signal, 3, JBB);
+ return;
+ }//if
+
+ ptr.p->tables.first(tabPtr);
+ getFragmentInfo(signal, ptr, tabPtr, 0);
+}
+
+void
+Backup::getFragmentInfo(Signal* signal,
+ BackupRecordPtr ptr, TablePtr tabPtr, Uint32 fragNo)
+{
+ jam();
+
+ for(; tabPtr.i != RNIL; ptr.p->tables.next(tabPtr)) {
+ jam();
+ const Uint32 fragCount = tabPtr.p->fragments.getSize();
+ for(; fragNo < fragCount; fragNo ++) {
+ jam();
+ FragmentPtr fragPtr;
+ tabPtr.p->fragments.getPtr(fragPtr, fragNo);
+
+ if(fragPtr.p->scanned == 0 && fragPtr.p->scanning == 0) {
+ jam();
+ signal->theData[0] = RNIL;
+ signal->theData[1] = ptr.i;
+ signal->theData[2] = tabPtr.p->tableId;
+ signal->theData[3] = fragNo;
+ sendSignal(DBDIH_REF, GSN_DIGETPRIMREQ, signal, 4, JBB);
+ return;
+ }//if
+ }//for
+ fragNo = 0;
+ }//for
+
+ getFragmentInfoDone(signal, ptr);
+}
+
+void
+Backup::execDIGETPRIMCONF(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 userPtr = signal->theData[0];
+ const Uint32 senderData = signal->theData[1];
+ const Uint32 nodeCount = signal->theData[6];
+ const Uint32 tableId = signal->theData[7];
+ const Uint32 fragNo = signal->theData[8];
+
+ ndbrequire(userPtr == RNIL && signal->length() == 9);
+ ndbrequire(nodeCount > 0 && nodeCount <= MAX_REPLICAS);
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, senderData);
+
+ defineSlaveAbortCheck();
+
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, tableId));
+
+ FragmentPtr fragPtr;
+ tabPtr.p->fragments.getPtr(fragPtr, fragNo);
+
+ fragPtr.p->node = signal->theData[2];
+
+ getFragmentInfo(signal, ptr, tabPtr, fragNo + 1);
+}
+
+void
+Backup::getFragmentInfoDone(Signal* signal, BackupRecordPtr ptr)
+{
+ // Slave must now hold on to master data until
+ // AbortBackupOrd::OkToClean signal
+ ptr.p->okToCleanMaster = false;
+ ptr.p->slaveState.setState(DEFINED);
+ DefineBackupConf * conf = (DefineBackupConf*)signal->getDataPtr();
+ conf->backupPtr = ptr.i;
+ conf->backupId = ptr.p->backupId;
+ sendSignal(ptr.p->masterRef, GSN_DEFINE_BACKUP_CONF, signal,
+ DefineBackupConf::SignalLength, JBB);
+}
+
+
+/*****************************************************************************
+ *
+ * Slave functionallity: Start backup
+ *
+ *****************************************************************************/
+void
+Backup::execSTART_BACKUP_REQ(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION((10015));
+
+ StartBackupReq* req = (StartBackupReq*)signal->getDataPtr();
+ const Uint32 ptrI = req->backupPtr;
+ const Uint32 backupId = req->backupId;
+ const Uint32 signalNo = req->signalNo;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ slaveAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->slaveState.setState(STARTED);
+
+ for(Uint32 i = 0; i<req->noOfTableTriggers; i++) {
+ jam();
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, req->tableTriggers[i].tableId));
+ for(Uint32 j = 0; j<3; j++) {
+ jam();
+ const Uint32 triggerId = req->tableTriggers[i].triggerIds[j];
+ tabPtr.p->triggerIds[j] = triggerId;
+
+ TriggerPtr trigPtr;
+ if(!ptr.p->triggers.seizeId(trigPtr, triggerId)) {
+ jam();
+ StartBackupRef* ref = (StartBackupRef*)signal->getDataPtrSend();
+ ref->backupPtr = ptr.i;
+ ref->backupId = ptr.p->backupId;
+ ref->signalNo = signalNo;
+ ref->errorCode = StartBackupRef::FailedToAllocateTriggerRecord;
+ sendSignal(ptr.p->masterRef, GSN_START_BACKUP_REF, signal,
+ StartBackupRef::SignalLength, JBB);
+ return;
+ }//if
+
+ tabPtr.p->triggerAllocated[j] = true;
+ trigPtr.p->backupPtr = ptr.i;
+ trigPtr.p->tableId = tabPtr.p->tableId;
+ trigPtr.p->tab_ptr_i = tabPtr.i;
+ trigPtr.p->logEntry = 0;
+ trigPtr.p->event = j;
+ trigPtr.p->maxRecordSize = 2048;
+ trigPtr.p->operation =
+ &ptr.p->files.getPtr(ptr.p->logFilePtr)->operation;
+ trigPtr.p->operation->noOfBytes = 0;
+ trigPtr.p->operation->noOfRecords = 0;
+ trigPtr.p->errorCode = 0;
+ }//for
+ }//for
+
+ /**
+ * Start file threads...
+ */
+ BackupFilePtr filePtr;
+ for(ptr.p->files.first(filePtr);
+ filePtr.i!=RNIL;
+ ptr.p->files.next(filePtr)){
+ jam();
+ if(filePtr.p->fileRunning == 0) {
+ jam();
+ filePtr.p->fileRunning = 1;
+ signal->theData[0] = BackupContinueB::START_FILE_THREAD;
+ signal->theData[1] = filePtr.i;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 100, 2);
+ }//if
+ }//for
+
+ StartBackupConf* conf = (StartBackupConf*)signal->getDataPtrSend();
+ conf->backupPtr = ptr.i;
+ conf->backupId = ptr.p->backupId;
+ conf->signalNo = signalNo;
+ sendSignal(ptr.p->masterRef, GSN_START_BACKUP_CONF, signal,
+ StartBackupConf::SignalLength, JBB);
+}
+
+/*****************************************************************************
+ *
+ * Slave functionallity: Backup fragment
+ *
+ *****************************************************************************/
+void
+Backup::execBACKUP_FRAGMENT_REQ(Signal* signal)
+{
+ jamEntry();
+ BackupFragmentReq* req = (BackupFragmentReq*)signal->getDataPtr();
+
+ CRASH_INSERTION((10016));
+
+ const Uint32 ptrI = req->backupPtr;
+ const Uint32 backupId = req->backupId;
+ const Uint32 tableId = req->tableId;
+ const Uint32 fragNo = req->fragmentNo;
+ const Uint32 count = req->count;
+
+ /**
+ * Get backup record
+ */
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ slaveAbortCheck(); // macro will do return if ABORTING
+
+ ptr.p->slaveState.setState(SCANNING);
+
+ /**
+ * Get file
+ */
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, ptr.p->dataFilePtr);
+
+ ndbrequire(filePtr.p->backupPtr == ptrI);
+ ndbrequire(filePtr.p->fileOpened == 1);
+ ndbrequire(filePtr.p->fileRunning == 1);
+ ndbrequire(filePtr.p->scanRunning == 0);
+ ndbrequire(filePtr.p->fileDone == 0);
+
+ /**
+ * Get table
+ */
+ TablePtr tabPtr;
+ ndbrequire(findTable(ptr, tabPtr, tableId));
+
+ /**
+ * Get fragment
+ */
+ FragmentPtr fragPtr;
+ tabPtr.p->fragments.getPtr(fragPtr, fragNo);
+
+ ndbrequire(fragPtr.p->scanned == 0);
+ ndbrequire(fragPtr.p->scanning == 0 ||
+ refToNode(ptr.p->masterRef) == getOwnNodeId());
+
+ /**
+ * Init operation
+ */
+ if(filePtr.p->tableId != tableId) {
+ jam();
+ filePtr.p->operation.init(tabPtr);
+ filePtr.p->tableId = tableId;
+ }//if
+
+ /**
+ * Check for space in buffer
+ */
+ if(!filePtr.p->operation.newFragment(tableId, fragNo)) {
+ jam();
+ req->count = count + 1;
+ sendSignalWithDelay(BACKUP_REF, GSN_BACKUP_FRAGMENT_REQ, signal, 50,
+ signal->length());
+ ptr.p->slaveState.setState(STARTED);
+ return;
+ }//if
+
+ /**
+ * Mark things as "in use"
+ */
+ fragPtr.p->scanning = 1;
+ filePtr.p->fragmentNo = fragNo;
+
+ /**
+ * Start scan
+ */
+ {
+ filePtr.p->scanRunning = 1;
+
+ Table & table = * tabPtr.p;
+ ScanFragReq * req = (ScanFragReq *)signal->getDataPtrSend();
+ const Uint32 parallelism = 16;
+ const Uint32 attrLen = 5 + table.noOfAttributes;
+
+ req->senderData = filePtr.i;
+ req->resultRef = reference();
+ req->schemaVersion = table.schemaVersion;
+ req->fragmentNoKeyLen = fragNo;
+ req->requestInfo = 0;
+ req->savePointId = 0;
+ req->tableId = table.tableId;
+ ScanFragReq::setLockMode(req->requestInfo, 0);
+ ScanFragReq::setHoldLockFlag(req->requestInfo, 0);
+ ScanFragReq::setKeyinfoFlag(req->requestInfo, 0);
+ ScanFragReq::setAttrLen(req->requestInfo,attrLen);
+ req->transId1 = 0;
+ req->transId2 = (BACKUP << 20) + (getOwnNodeId() << 8);
+ req->clientOpPtr= filePtr.i;
+ req->batch_size_rows= 16;
+ req->batch_size_bytes= 0;
+ sendSignal(DBLQH_REF, GSN_SCAN_FRAGREQ, signal,
+ ScanFragReq::SignalLength, JBB);
+
+ signal->theData[0] = filePtr.i;
+ signal->theData[1] = 0;
+ signal->theData[2] = (BACKUP << 20) + (getOwnNodeId() << 8);
+
+ // Return all
+ signal->theData[3] = table.noOfAttributes;
+ signal->theData[4] = 0;
+ signal->theData[5] = 0;
+ signal->theData[6] = 0;
+ signal->theData[7] = 0;
+
+ Uint32 dataPos = 8;
+ Uint32 i;
+ for(i = 0; i<table.noOfAttributes; i++) {
+ jam();
+ AttributePtr attr;
+ table.attributes.getPtr(attr, i);
+
+ AttributeHeader::init(&signal->theData[dataPos], i, 0);
+ dataPos++;
+ if(dataPos == 25) {
+ jam();
+ sendSignal(DBLQH_REF, GSN_ATTRINFO, signal, 25, JBB);
+ dataPos = 3;
+ }//if
+ }//for
+ if(dataPos != 3) {
+ jam();
+ sendSignal(DBLQH_REF, GSN_ATTRINFO, signal, dataPos, JBB);
+ }//if
+ }
+}
+
+void
+Backup::execSCAN_HBREP(Signal* signal)
+{
+ jamEntry();
+}
+
+void
+Backup::execTRANSID_AI(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 filePtrI = signal->theData[0];
+ //const Uint32 transId1 = signal->theData[1];
+ //const Uint32 transId2 = signal->theData[2];
+ const Uint32 dataLen = signal->length() - 3;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+ OperationRecord & op = filePtr.p->operation;
+
+ TablePtr tabPtr;
+ c_tablePool.getPtr(tabPtr, op.tablePtr);
+
+ Table & table = * tabPtr.p;
+
+ /**
+ * Unpack data
+ */
+ op.attrSzTotal += dataLen;
+
+ Uint32 srcSz = dataLen;
+ const Uint32 * src = &signal->theData[3];
+
+ Uint32 * dst = op.dst;
+ Uint32 dstSz = op.attrSzLeft;
+
+ while(srcSz > 0) {
+ jam();
+
+ if(dstSz == 0) {
+ jam();
+
+ /**
+ * Finished with one attribute now find next
+ */
+ const AttributeHeader attrHead(* src);
+ const Uint32 attrId = attrHead.getAttributeId();
+ const bool null = attrHead.isNULL();
+ const Attribute::Data attr = table.attributes.getPtr(attrId)->data;
+
+ srcSz -= attrHead.getHeaderSize();
+ src += attrHead.getHeaderSize();
+
+ if(null) {
+ jam();
+ ndbrequire(attr.nullable);
+ op.nullAttribute(attr.offsetNull);
+ dstSz = 0;
+ continue;
+ }//if
+
+ dstSz = attrHead.getDataSize();
+ ndbrequire(dstSz == attr.sz32);
+ if(attr.fixed && ! attr.nullable) {
+ jam();
+ dst = op.newAttrib(attr.offset, dstSz);
+ } else if (attr.fixed && attr.nullable) {
+ jam();
+ dst = op.newNullable(attrId, dstSz);
+ } else {
+ ndbrequire(false);
+ //dst = op.newVariable(attrId, attrSize);
+ }//if
+ }//if
+
+ const Uint32 szCopy = (dstSz > srcSz) ? srcSz : dstSz;
+ memcpy(dst, src, (szCopy << 2));
+
+ srcSz -= szCopy;
+ dstSz -= szCopy;
+ src += szCopy;
+ dst += szCopy;
+ }//while
+ op.dst = dst;
+ op.attrSzLeft = dstSz;
+
+ if(op.finished()){
+ jam();
+ op.newRecord(op.dst);
+ }
+}
+
+void
+Backup::OperationRecord::init(const TablePtr & ptr)
+{
+
+ tablePtr = ptr.i;
+ noOfAttributes = ptr.p->noOfAttributes;
+
+ sz_Bitmask = (ptr.p->noOfNull + 31) >> 5;
+ sz_FixedAttribs = ptr.p->sz_FixedAttributes;
+
+ if(ptr.p->noOfVariable == 0) {
+ jam();
+ maxRecordSize = 1 + sz_Bitmask + sz_FixedAttribs;
+ } else {
+ jam();
+ maxRecordSize =
+ 1 + sz_Bitmask + 2048 /* Max tuple size */ + 2 * ptr.p->noOfVariable;
+ }//if
+}
+
+bool
+Backup::OperationRecord::newFragment(Uint32 tableId, Uint32 fragNo)
+{
+ Uint32 * tmp;
+ const Uint32 headSz = (sizeof(BackupFormat::DataFile::FragmentHeader) >> 2);
+ const Uint32 sz = headSz + 16 * maxRecordSize;
+
+ ndbrequire(sz < dataBuffer.getMaxWrite());
+ if(dataBuffer.getWritePtr(&tmp, sz)) {
+ jam();
+ BackupFormat::DataFile::FragmentHeader * head =
+ (BackupFormat::DataFile::FragmentHeader*)tmp;
+
+ head->SectionType = htonl(BackupFormat::FRAGMENT_HEADER);
+ head->SectionLength = htonl(headSz);
+ head->TableId = htonl(tableId);
+ head->FragmentNo = htonl(fragNo);
+ head->ChecksumType = htonl(0);
+
+ opNoDone = opNoConf = opLen = 0;
+ newRecord(tmp + headSz);
+ scanStart = tmp;
+ scanStop = (tmp + headSz);
+
+ noOfRecords = 0;
+ noOfBytes = 0;
+ return true;
+ }//if
+ return false;
+}
+
+bool
+Backup::OperationRecord::fragComplete(Uint32 tableId, Uint32 fragNo)
+{
+ Uint32 * tmp;
+ const Uint32 footSz = sizeof(BackupFormat::DataFile::FragmentFooter) >> 2;
+
+ if(dataBuffer.getWritePtr(&tmp, footSz + 1)) {
+ jam();
+ * tmp = 0; // Finish record stream
+ tmp++;
+ BackupFormat::DataFile::FragmentFooter * foot =
+ (BackupFormat::DataFile::FragmentFooter*)tmp;
+ foot->SectionType = htonl(BackupFormat::FRAGMENT_FOOTER);
+ foot->SectionLength = htonl(footSz);
+ foot->TableId = htonl(tableId);
+ foot->FragmentNo = htonl(fragNo);
+ foot->NoOfRecords = htonl(noOfRecords);
+ foot->Checksum = htonl(0);
+ dataBuffer.updateWritePtr(footSz + 1);
+ return true;
+ }//if
+ return false;
+}
+
+bool
+Backup::OperationRecord::newScan()
+{
+ Uint32 * tmp;
+ ndbrequire(16 * maxRecordSize < dataBuffer.getMaxWrite());
+ if(dataBuffer.getWritePtr(&tmp, 16 * maxRecordSize)) {
+ jam();
+ opNoDone = opNoConf = opLen = 0;
+ newRecord(tmp);
+ scanStart = tmp;
+ scanStop = tmp;
+ return true;
+ }//if
+ return false;
+}
+
+bool
+Backup::OperationRecord::scanConf(Uint32 noOfOps, Uint32 total_len)
+{
+ const Uint32 done = opNoDone-opNoConf;
+
+ ndbrequire(noOfOps == done);
+ ndbrequire(opLen == total_len);
+ opNoConf = opNoDone;
+
+ const Uint32 len = (scanStop - scanStart);
+ ndbrequire(len < dataBuffer.getMaxWrite());
+ dataBuffer.updateWritePtr(len);
+ noOfBytes += (len << 2);
+ return true;
+}
+
+void
+Backup::execSCAN_FRAGREF(Signal* signal)
+{
+ jamEntry();
+
+ ScanFragRef * ref = (ScanFragRef*)signal->getDataPtr();
+
+ const Uint32 filePtrI = ref->senderData;
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+ filePtr.p->errorCode = ref->errorCode;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+
+ abortFile(signal, ptr, filePtr);
+}
+
+void
+Backup::execSCAN_FRAGCONF(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION((10017));
+
+ ScanFragConf * conf = (ScanFragConf*)signal->getDataPtr();
+
+ const Uint32 filePtrI = conf->senderData;
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+ OperationRecord & op = filePtr.p->operation;
+
+ op.scanConf(conf->completedOps, conf->total_len);
+ const Uint32 completed = conf->fragmentCompleted;
+ if(completed != 2) {
+ jam();
+
+ checkScan(signal, filePtr);
+ return;
+ }//if
+
+ fragmentCompleted(signal, filePtr);
+}
+
+void
+Backup::fragmentCompleted(Signal* signal, BackupFilePtr filePtr)
+{
+ jam();
+
+ if(filePtr.p->errorCode != 0){
+ jam();
+ abortFileHook(signal, filePtr, true); // Scan completed
+ return;
+ }//if
+
+ OperationRecord & op = filePtr.p->operation;
+ if(!op.fragComplete(filePtr.p->tableId, filePtr.p->fragmentNo)) {
+ jam();
+ signal->theData[0] = BackupContinueB::BUFFER_FULL_FRAG_COMPLETE;
+ signal->theData[1] = filePtr.i;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 50, 2);
+ return;
+ }//if
+
+ filePtr.p->scanRunning = 0;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+
+ BackupFragmentConf * conf = (BackupFragmentConf*)signal->getDataPtrSend();
+ conf->backupId = ptr.p->backupId;
+ conf->backupPtr = ptr.i;
+ conf->tableId = filePtr.p->tableId;
+ conf->fragmentNo = filePtr.p->fragmentNo;
+ conf->noOfRecords = op.noOfRecords;
+ conf->noOfBytes = op.noOfBytes;
+ sendSignal(ptr.p->masterRef, GSN_BACKUP_FRAGMENT_CONF, signal,
+ BackupFragmentConf::SignalLength, JBB);
+
+ ptr.p->slaveState.setState(STARTED);
+ return;
+}
+
+void
+Backup::checkScan(Signal* signal, BackupFilePtr filePtr)
+{
+ if(filePtr.p->errorCode != 0){
+ jam();
+ abortFileHook(signal, filePtr, false); // Scan not completed
+ return;
+ }//if
+
+ OperationRecord & op = filePtr.p->operation;
+ if(op.newScan()) {
+ jam();
+
+ ScanFragNextReq * req = (ScanFragNextReq *)signal->getDataPtrSend();
+ req->senderData = filePtr.i;
+ req->closeFlag = 0;
+ req->transId1 = 0;
+ req->transId2 = (BACKUP << 20) + (getOwnNodeId() << 8);
+ req->batch_size_rows= 16;
+ req->batch_size_bytes= 0;
+ sendSignal(DBLQH_REF, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+ return;
+ }//if
+
+ signal->theData[0] = BackupContinueB::BUFFER_FULL_SCAN;
+ signal->theData[1] = filePtr.i;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 50, 2);
+}
+
+void
+Backup::execFSAPPENDREF(Signal* signal)
+{
+ jamEntry();
+
+ FsRef * ref = (FsRef *)signal->getDataPtr();
+
+ const Uint32 filePtrI = ref->userPointer;
+ const Uint32 errCode = ref->errorCode;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+ filePtr.p->fileRunning = 0;
+ filePtr.p->errorCode = errCode;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+
+ abortFile(signal, ptr, filePtr);
+}
+
+void
+Backup::execFSAPPENDCONF(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION((10018));
+
+ //FsConf * conf = (FsConf*)signal->getDataPtr();
+ const Uint32 filePtrI = signal->theData[0]; //conf->userPointer;
+ const Uint32 bytes = signal->theData[1]; //conf->bytes;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+ if (ERROR_INSERTED(10029)) {
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+ abortFile(signal, ptr, filePtr);
+ }//if
+
+ OperationRecord & op = filePtr.p->operation;
+
+ op.dataBuffer.updateReadPtr(bytes >> 2);
+
+ checkFile(signal, filePtr);
+}
+
+void
+Backup::checkFile(Signal* signal, BackupFilePtr filePtr)
+{
+
+#ifdef DEBUG_ABORT
+ // ndbout_c("---- check file filePtr.i = %u", filePtr.i);
+#endif
+
+ OperationRecord & op = filePtr.p->operation;
+
+ Uint32 * tmp, sz; bool eof;
+ if(op.dataBuffer.getReadPtr(&tmp, &sz, &eof)) {
+ jam();
+
+ if(filePtr.p->errorCode == 0) {
+ jam();
+ FsAppendReq * req = (FsAppendReq *)signal->getDataPtrSend();
+ req->filePointer = filePtr.p->filePointer;
+ req->userPointer = filePtr.i;
+ req->userReference = reference();
+ req->varIndex = 0;
+ req->offset = tmp - c_startOfPages;
+ req->size = sz;
+
+ sendSignal(NDBFS_REF, GSN_FSAPPENDREQ, signal,
+ FsAppendReq::SignalLength, JBA);
+ return;
+ } else {
+ jam();
+ if (filePtr.p->scanRunning == 1)
+ eof = false;
+ }//if
+ }//if
+
+ if(!eof) {
+ jam();
+ signal->theData[0] = BackupContinueB::BUFFER_UNDERFLOW;
+ signal->theData[1] = filePtr.i;
+ sendSignalWithDelay(BACKUP_REF, GSN_CONTINUEB, signal, 50, 2);
+ return;
+ }//if
+
+ ndbrequire(filePtr.p->fileDone == 1);
+
+ if(sz > 0 && filePtr.p->errorCode == 0) {
+ jam();
+ FsAppendReq * req = (FsAppendReq *)signal->getDataPtrSend();
+ req->filePointer = filePtr.p->filePointer;
+ req->userPointer = filePtr.i;
+ req->userReference = reference();
+ req->varIndex = 0;
+ req->offset = tmp - c_startOfPages;
+ req->size = sz; // Avrunda uppot
+
+ sendSignal(NDBFS_REF, GSN_FSAPPENDREQ, signal,
+ FsAppendReq::SignalLength, JBA);
+ return;
+ }//if
+
+ filePtr.p->fileRunning = 0;
+
+ FsCloseReq * req = (FsCloseReq *)signal->getDataPtrSend();
+ req->filePointer = filePtr.p->filePointer;
+ req->userPointer = filePtr.i;
+ req->userReference = reference();
+ req->fileFlag = 0;
+#ifdef DEBUG_ABORT
+ ndbout_c("***** FSCLOSEREQ filePtr.i = %u", filePtr.i);
+#endif
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, FsCloseReq::SignalLength, JBA);
+}
+
+void
+Backup::abortFile(Signal* signal, BackupRecordPtr ptr, BackupFilePtr filePtr)
+{
+ jam();
+
+ if(ptr.p->slaveState.getState() != ABORTING) {
+ /**
+ * Inform master of failure
+ */
+ jam();
+ ptr.p->slaveState.setState(ABORTING);
+ ptr.p->setErrorCode(AbortBackupOrd::FileOrScanError);
+ sendAbortBackupOrdSlave(signal, ptr, AbortBackupOrd::FileOrScanError);
+ return;
+ }//if
+
+
+ for(ptr.p->files.first(filePtr);
+ filePtr.i!=RNIL;
+ ptr.p->files.next(filePtr)){
+ jam();
+ filePtr.p->errorCode = 1;
+ }//for
+
+ closeFiles(signal, ptr);
+}
+
+void
+Backup::abortFileHook(Signal* signal, BackupFilePtr filePtr, bool scanComplete)
+{
+ jam();
+
+ if(!scanComplete) {
+ jam();
+
+ ScanFragNextReq * req = (ScanFragNextReq *)signal->getDataPtrSend();
+ req->senderData = filePtr.i;
+ req->closeFlag = 1;
+ req->transId1 = 0;
+ req->transId2 = (BACKUP << 20) + (getOwnNodeId() << 8);
+ sendSignal(DBLQH_REF, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+ return;
+ }//if
+
+ filePtr.p->scanRunning = 0;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+
+ filePtr.i = RNIL;
+ abortFile(signal, ptr, filePtr);
+}
+
+/****************************************************************************
+ *
+ * Slave functionallity: Perform logging
+ *
+ ****************************************************************************/
+Uint32
+Backup::calculate_frag_mask(Uint32 count)
+{
+ Uint32 mask = 1;
+ while (mask < count) mask <<= 1;
+ mask -= 1;
+ return mask;
+}
+
+void
+Backup::execBACKUP_TRIG_REQ(Signal* signal)
+{
+ /*
+ TUP asks if this trigger is to be fired on this node.
+ */
+ TriggerPtr trigPtr;
+ TablePtr tabPtr;
+ FragmentPtr fragPtr;
+ Uint32 trigger_id = signal->theData[0];
+ Uint32 frag_id = signal->theData[1];
+ Uint32 result;
+
+ jamEntry();
+ c_triggerPool.getPtr(trigPtr, trigger_id);
+ c_tablePool.getPtr(tabPtr, trigPtr.p->tab_ptr_i);
+ frag_id = frag_id & tabPtr.p->frag_mask;
+ /*
+ At the moment the fragment identity known by TUP is the
+ actual fragment id but with possibly an extra bit set.
+ This is due to that ACC splits the fragment. Thus fragment id 5 can
+ here be either 5 or 13. Thus masking with 2 ** n - 1 where number of
+ fragments <= 2 ** n will always provide a correct fragment id.
+ */
+ tabPtr.p->fragments.getPtr(fragPtr, frag_id);
+ if (fragPtr.p->node != getOwnNodeId()) {
+ jam();
+ result = ZFALSE;
+ } else {
+ jam();
+ result = ZTRUE;
+ }//if
+ signal->theData[0] = result;
+}
+
+void
+Backup::execTRIG_ATTRINFO(Signal* signal) {
+ jamEntry();
+
+ CRASH_INSERTION((10019));
+
+ TrigAttrInfo * trg = (TrigAttrInfo*)signal->getDataPtr();
+
+ TriggerPtr trigPtr;
+ c_triggerPool.getPtr(trigPtr, trg->getTriggerId());
+ ndbrequire(trigPtr.p->event != ILLEGAL_TRIGGER_ID); // Online...
+
+ if(trigPtr.p->errorCode != 0) {
+ jam();
+ return;
+ }//if
+
+ if(trg->getAttrInfoType() == TrigAttrInfo::BEFORE_VALUES) {
+ jam();
+ /**
+ * Backup is doing REDO logging and don't need before values
+ */
+ return;
+ }//if
+
+ BackupFormat::LogFile::LogEntry * logEntry = trigPtr.p->logEntry;
+ if(logEntry == 0) {
+ jam();
+ Uint32 * dst;
+ FsBuffer & buf = trigPtr.p->operation->dataBuffer;
+ ndbrequire(trigPtr.p->maxRecordSize <= buf.getMaxWrite());
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, trigPtr.p->backupPtr);
+ if(!buf.getWritePtr(&dst, trigPtr.p->maxRecordSize)) {
+ jam();
+ trigPtr.p->errorCode = AbortBackupOrd::LogBufferFull;
+ sendAbortBackupOrdSlave(signal, ptr, AbortBackupOrd::LogBufferFull);
+ return;
+ }//if
+ if(trigPtr.p->operation->noOfBytes > 123 && ERROR_INSERTED(10030)) {
+ jam();
+ trigPtr.p->errorCode = AbortBackupOrd::LogBufferFull;
+ sendAbortBackupOrdSlave(signal, ptr, AbortBackupOrd::LogBufferFull);
+ return;
+ }//if
+
+ logEntry = (BackupFormat::LogFile::LogEntry *)dst;
+ trigPtr.p->logEntry = logEntry;
+ logEntry->Length = 0;
+ logEntry->TableId = htonl(trigPtr.p->tableId);
+ logEntry->TriggerEvent = htonl(trigPtr.p->event);
+ } else {
+ ndbrequire(logEntry->TableId == htonl(trigPtr.p->tableId));
+ ndbrequire(logEntry->TriggerEvent == htonl(trigPtr.p->event));
+ }//if
+
+ const Uint32 pos = logEntry->Length;
+ const Uint32 dataLen = signal->length() - TrigAttrInfo::StaticLength;
+ memcpy(&logEntry->Data[pos], trg->getData(), dataLen << 2);
+
+ logEntry->Length = pos + dataLen;
+}
+
+void
+Backup::execFIRE_TRIG_ORD(Signal* signal)
+{
+ jamEntry();
+ FireTrigOrd* trg = (FireTrigOrd*)signal->getDataPtr();
+
+ const Uint32 gci = trg->getGCI();
+ const Uint32 trI = trg->getTriggerId();
+
+ TriggerPtr trigPtr;
+ c_triggerPool.getPtr(trigPtr, trI);
+
+ ndbrequire(trigPtr.p->event != ILLEGAL_TRIGGER_ID);
+
+ if(trigPtr.p->errorCode != 0) {
+ jam();
+ return;
+ }//if
+
+ ndbrequire(trigPtr.p->logEntry != 0);
+ Uint32 len = trigPtr.p->logEntry->Length;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, trigPtr.p->backupPtr);
+ if(gci != ptr.p->currGCP) {
+ jam();
+
+ trigPtr.p->logEntry->TriggerEvent = htonl(trigPtr.p->event | 0x10000);
+ trigPtr.p->logEntry->Data[len] = htonl(gci);
+ len ++;
+ ptr.p->currGCP = gci;
+ }//if
+
+ len += (sizeof(BackupFormat::LogFile::LogEntry) >> 2) - 2;
+ trigPtr.p->logEntry->Length = htonl(len);
+
+ ndbrequire(len + 1 <= trigPtr.p->operation->dataBuffer.getMaxWrite());
+ trigPtr.p->operation->dataBuffer.updateWritePtr(len + 1);
+ trigPtr.p->logEntry = 0;
+
+ trigPtr.p->operation->noOfBytes += (len + 1) << 2;
+ trigPtr.p->operation->noOfRecords += 1;
+}
+
+void
+Backup::sendAbortBackupOrdSlave(Signal* signal, BackupRecordPtr ptr,
+ Uint32 requestType)
+{
+ jam();
+ AbortBackupOrd *ord = (AbortBackupOrd*)signal->getDataPtrSend();
+ ord->backupId = ptr.p->backupId;
+ ord->backupPtr = ptr.i;
+ ord->requestType = requestType;
+ ord->senderData= ptr.i;
+ sendSignal(ptr.p->masterRef, GSN_ABORT_BACKUP_ORD, signal,
+ AbortBackupOrd::SignalLength, JBB);
+}
+
+void
+Backup::sendAbortBackupOrd(Signal* signal, BackupRecordPtr ptr,
+ Uint32 requestType)
+{
+ jam();
+ AbortBackupOrd *ord = (AbortBackupOrd*)signal->getDataPtrSend();
+ ord->backupId = ptr.p->backupId;
+ ord->backupPtr = ptr.i;
+ ord->requestType = requestType;
+ ord->senderData= ptr.i;
+ NodePtr node;
+ for(c_nodes.first(node); node.i != RNIL; c_nodes.next(node)) {
+ jam();
+ const Uint32 nodeId = node.p->nodeId;
+ if(node.p->alive && ptr.p->nodes.get(nodeId)) {
+ jam();
+ sendSignal(numberToRef(BACKUP, nodeId), GSN_ABORT_BACKUP_ORD, signal,
+ AbortBackupOrd::SignalLength, JBB);
+ }//if
+ }//for
+}
+
+/*****************************************************************************
+ *
+ * Slave functionallity: Stop backup
+ *
+ *****************************************************************************/
+void
+Backup::execSTOP_BACKUP_REQ(Signal* signal)
+{
+ jamEntry();
+ StopBackupReq * req = (StopBackupReq*)signal->getDataPtr();
+
+ CRASH_INSERTION((10020));
+
+ const Uint32 ptrI = req->backupPtr;
+ const Uint32 backupId = req->backupId;
+ const Uint32 startGCP = req->startGCP;
+ const Uint32 stopGCP = req->stopGCP;
+
+ /**
+ * At least one GCP must have passed
+ */
+ ndbrequire(stopGCP > startGCP);
+
+ /**
+ * Get backup record
+ */
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ ptr.p->slaveState.setState(STOPPING);
+ slaveAbortCheck(); // macro will do return if ABORTING
+
+ /**
+ * Insert footers
+ */
+ {
+ BackupFilePtr filePtr;
+ ptr.p->files.getPtr(filePtr, ptr.p->logFilePtr);
+ Uint32 * dst;
+ ndbrequire(filePtr.p->operation.dataBuffer.getWritePtr(&dst, 1));
+ * dst = 0;
+ filePtr.p->operation.dataBuffer.updateWritePtr(1);
+ }
+
+ {
+ BackupFilePtr filePtr;
+ ptr.p->files.getPtr(filePtr, ptr.p->ctlFilePtr);
+
+ const Uint32 gcpSz = sizeof(BackupFormat::CtlFile::GCPEntry) >> 2;
+
+ Uint32 * dst;
+ ndbrequire(filePtr.p->operation.dataBuffer.getWritePtr(&dst, gcpSz));
+
+ BackupFormat::CtlFile::GCPEntry * gcp =
+ (BackupFormat::CtlFile::GCPEntry*)dst;
+
+ gcp->SectionType = htonl(BackupFormat::GCP_ENTRY);
+ gcp->SectionLength = htonl(gcpSz);
+ gcp->StartGCP = htonl(startGCP);
+ gcp->StopGCP = htonl(stopGCP - 1);
+ filePtr.p->operation.dataBuffer.updateWritePtr(gcpSz);
+ }
+
+ closeFiles(signal, ptr);
+}
+
+void
+Backup::closeFiles(Signal* sig, BackupRecordPtr ptr)
+{
+ if (ptr.p->closingFiles) {
+ jam();
+ return;
+ }
+ ptr.p->closingFiles = true;
+
+ /**
+ * Close all files
+ */
+ BackupFilePtr filePtr;
+ int openCount = 0;
+ for(ptr.p->files.first(filePtr); filePtr.i!=RNIL; ptr.p->files.next(filePtr))
+ {
+ if(filePtr.p->fileOpened == 0) {
+ jam();
+ continue;
+ }
+
+ jam();
+ openCount++;
+
+ if(filePtr.p->fileDone == 1){
+ jam();
+ continue;
+ }//if
+
+ filePtr.p->fileDone = 1;
+
+ if(filePtr.p->fileRunning == 1){
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("Close files fileRunning == 1, filePtr.i=%u", filePtr.i);
+#endif
+ filePtr.p->operation.dataBuffer.eof();
+ } else {
+ jam();
+
+ FsCloseReq * req = (FsCloseReq *)sig->getDataPtrSend();
+ req->filePointer = filePtr.p->filePointer;
+ req->userPointer = filePtr.i;
+ req->userReference = reference();
+ req->fileFlag = 0;
+#ifdef DEBUG_ABORT
+ ndbout_c("***** FSCLOSEREQ filePtr.i = %u", filePtr.i);
+#endif
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, sig,
+ FsCloseReq::SignalLength, JBA);
+ }//if
+ }//for
+
+ if(openCount == 0){
+ jam();
+ closeFilesDone(sig, ptr);
+ }//if
+}
+
+void
+Backup::execFSCLOSEREF(Signal* signal)
+{
+ jamEntry();
+
+ FsRef * ref = (FsRef*)signal->getDataPtr();
+ const Uint32 filePtrI = ref->userPointer;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+
+ /**
+ * This should only happen during abort of backup
+ */
+ ndbrequire(ptr.p->slaveState.getState() == ABORTING);
+
+ filePtr.p->fileOpened = 1;
+ FsConf * conf = (FsConf*)signal->getDataPtr();
+ conf->userPointer = filePtrI;
+
+ execFSCLOSECONF(signal);
+}
+
+void
+Backup::execFSCLOSECONF(Signal* signal)
+{
+ jamEntry();
+
+ FsConf * conf = (FsConf*)signal->getDataPtr();
+ const Uint32 filePtrI = conf->userPointer;
+
+ BackupFilePtr filePtr;
+ c_backupFilePool.getPtr(filePtr, filePtrI);
+
+#ifdef DEBUG_ABORT
+ ndbout_c("***** FSCLOSECONF filePtrI = %u", filePtrI);
+#endif
+
+ ndbrequire(filePtr.p->fileDone == 1);
+ ndbrequire(filePtr.p->fileOpened == 1);
+ ndbrequire(filePtr.p->fileRunning == 0);
+ ndbrequire(filePtr.p->scanRunning == 0);
+
+ filePtr.p->fileOpened = 0;
+
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, filePtr.p->backupPtr);
+ for(ptr.p->files.first(filePtr); filePtr.i!=RNIL;ptr.p->files.next(filePtr))
+ {
+ jam();
+ if(filePtr.p->fileOpened == 1) {
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("waiting for more FSCLOSECONF's filePtr.i = %u", filePtr.i);
+#endif
+ return; // we will be getting more FSCLOSECONF's
+ }//if
+ }//for
+ closeFilesDone(signal, ptr);
+}
+
+void
+Backup::closeFilesDone(Signal* signal, BackupRecordPtr ptr)
+{
+ jam();
+
+ if(ptr.p->slaveState.getState() == STOPPING) {
+ jam();
+ BackupFilePtr filePtr;
+ ptr.p->files.getPtr(filePtr, ptr.p->logFilePtr);
+
+ StopBackupConf* conf = (StopBackupConf*)signal->getDataPtrSend();
+ conf->backupId = ptr.p->backupId;
+ conf->backupPtr = ptr.i;
+ conf->noOfLogBytes = filePtr.p->operation.noOfBytes;
+ conf->noOfLogRecords = filePtr.p->operation.noOfRecords;
+ sendSignal(ptr.p->masterRef, GSN_STOP_BACKUP_CONF, signal,
+ StopBackupConf::SignalLength, JBB);
+
+ ptr.p->slaveState.setState(CLEANING);
+ return;
+ }//if
+
+ ndbrequire(ptr.p->slaveState.getState() == ABORTING);
+ removeBackup(signal, ptr);
+}
+
+/*****************************************************************************
+ *
+ * Slave functionallity: Abort backup
+ *
+ *****************************************************************************/
+void
+Backup::removeBackup(Signal* signal, BackupRecordPtr ptr)
+{
+ jam();
+
+ FsRemoveReq * req = (FsRemoveReq *)signal->getDataPtrSend();
+ req->userReference = reference();
+ req->userPointer = ptr.i;
+ req->directory = 1;
+ req->ownDirectory = 1;
+ FsOpenReq::setVersion(req->fileNumber, 2);
+ FsOpenReq::setSuffix(req->fileNumber, FsOpenReq::S_CTL);
+ FsOpenReq::v2_setSequence(req->fileNumber, ptr.p->backupId);
+ FsOpenReq::v2_setNodeId(req->fileNumber, getOwnNodeId());
+ sendSignal(NDBFS_REF, GSN_FSREMOVEREQ, signal,
+ FsRemoveReq::SignalLength, JBA);
+}
+
+void
+Backup::execFSREMOVEREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(0);
+}
+
+void
+Backup::execFSREMOVECONF(Signal* signal){
+ jamEntry();
+
+ FsConf * conf = (FsConf*)signal->getDataPtr();
+ const Uint32 ptrI = conf->userPointer;
+
+ /**
+ * Get backup record
+ */
+ BackupRecordPtr ptr;
+ c_backupPool.getPtr(ptr, ptrI);
+
+ ndbrequire(ptr.p->slaveState.getState() == ABORTING);
+ if (ptr.p->masterRef == reference()) {
+ if (ptr.p->masterData.state.getAbortState() == DEFINING) {
+ jam();
+ sendBackupRef(signal, ptr, ptr.p->errorCode);
+ return;
+ } else {
+ jam();
+ }//if
+ }//if
+ cleanupSlaveResources(ptr);
+}
+
+/*****************************************************************************
+ *
+ * Slave functionallity: Abort backup
+ *
+ *****************************************************************************/
+void
+Backup::execABORT_BACKUP_ORD(Signal* signal)
+{
+ jamEntry();
+ AbortBackupOrd* ord = (AbortBackupOrd*)signal->getDataPtr();
+
+ const Uint32 backupId = ord->backupId;
+ const AbortBackupOrd::RequestType requestType =
+ (AbortBackupOrd::RequestType)ord->requestType;
+ const Uint32 senderData = ord->senderData;
+
+#ifdef DEBUG_ABORT
+ ndbout_c("******** ABORT_BACKUP_ORD ********* nodeId = %u",
+ refToNode(signal->getSendersBlockRef()));
+ ndbout_c("backupId = %u, requestType = %u, senderData = %u, ",
+ backupId, requestType, senderData);
+ dumpUsedResources();
+#endif
+
+ BackupRecordPtr ptr;
+ if(requestType == AbortBackupOrd::ClientAbort) {
+ if (getOwnNodeId() != getMasterNodeId()) {
+ jam();
+ // forward to master
+#ifdef DEBUG_ABORT
+ ndbout_c("---- Forward to master nodeId = %u", getMasterNodeId());
+#endif
+ sendSignal(calcBackupBlockRef(getMasterNodeId()), GSN_ABORT_BACKUP_ORD,
+ signal, AbortBackupOrd::SignalLength, JBB);
+ return;
+ }
+ jam();
+ for(c_backups.first(ptr); ptr.i != RNIL; c_backups.next(ptr)) {
+ jam();
+ if(ptr.p->backupId == backupId && ptr.p->clientData == senderData) {
+ jam();
+ break;
+ }//if
+ }//for
+ if(ptr.i == RNIL) {
+ jam();
+ return;
+ }//if
+ } else {
+ if (c_backupPool.findId(senderData)) {
+ jam();
+ c_backupPool.getPtr(ptr, senderData);
+ } else { // TODO might be abort sent to not master,
+ // or master aborting too early
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("Backup: abort request type=%u on id=%u,%u not found",
+ requestType, backupId, senderData);
+#endif
+ return;
+ }
+ }//if
+
+ const bool isCoordinator = (ptr.p->masterRef == reference());
+
+ bool ok = false;
+ switch(requestType){
+
+ /**
+ * Requests sent to master
+ */
+
+ case AbortBackupOrd::ClientAbort:
+ jam();
+ // fall through
+ case AbortBackupOrd::LogBufferFull:
+ jam();
+ // fall through
+ case AbortBackupOrd::FileOrScanError:
+ jam();
+ if(ptr.p->masterData.state.getState() == ABORTING) {
+#ifdef DEBUG_ABORT
+ ndbout_c("---- Already aborting");
+#endif
+ jam();
+ return;
+ }
+ ptr.p->setErrorCode(requestType);
+ ndbrequire(isCoordinator); // Sent from slave to coordinator
+ masterAbort(signal, ptr, false);
+ return;
+
+ /**
+ * Info sent to slave
+ */
+
+ case AbortBackupOrd::OkToClean:
+ jam();
+ cleanupMasterResources(ptr);
+ return;
+
+ /**
+ * Requests sent to slave
+ */
+
+ case AbortBackupOrd::BackupComplete:
+ jam();
+ if (ptr.p->slaveState.getState() == CLEANING) { // TODO what if state is
+ // not CLEANING?
+ jam();
+ cleanupSlaveResources(ptr);
+ }//if
+ return;
+ break;
+ case AbortBackupOrd::BackupFailureDueToNodeFail:
+ jam();
+ ok = true;
+ if (ptr.p->errorCode != 0)
+ ptr.p->setErrorCode(requestType);
+ break;
+ case AbortBackupOrd::BackupFailure:
+ jam();
+ ok = true;
+ break;
+ }
+ ndbrequire(ok);
+
+ /**
+ * Slave abort
+ */
+ slaveAbort(signal, ptr);
+}
+
+void
+Backup::slaveAbort(Signal* signal, BackupRecordPtr ptr)
+{
+ if(ptr.p->slaveState.getState() == ABORTING) {
+#ifdef DEBUG_ABORT
+ ndbout_c("---- Slave already aborting");
+#endif
+ jam();
+ return;
+ }
+#ifdef DEBUG_ABORT
+ ndbout_c("************* slaveAbort");
+#endif
+
+ State slaveState = ptr.p->slaveState.getState();
+ ptr.p->slaveState.setState(ABORTING);
+ switch(slaveState) {
+ case DEFINING:
+ jam();
+ return;
+//------------------------------------------
+// Will watch for the abort at various places
+// in the defining phase.
+//------------------------------------------
+ case ABORTING:
+ jam();
+ //Fall through
+ case DEFINED:
+ jam();
+ //Fall through
+ case STOPPING:
+ jam();
+ closeFiles(signal, ptr);
+ return;
+ case STARTED:
+ jam();
+ //Fall through
+ case SCANNING:
+ jam();
+ BackupFilePtr filePtr;
+ filePtr.i = RNIL;
+ abortFile(signal, ptr, filePtr);
+ return;
+ case CLEANING:
+ jam();
+ cleanupSlaveResources(ptr);
+ return;
+ case INITIAL:
+ jam();
+ ndbrequire(false);
+ return;
+ }
+}
+
+void
+Backup::dumpUsedResources()
+{
+ jam();
+ BackupRecordPtr ptr;
+
+ for(c_backups.first(ptr); ptr.i != RNIL; c_backups.next(ptr)) {
+ ndbout_c("Backup id=%u, slaveState.getState = %u, errorCode=%u",
+ ptr.p->backupId,
+ ptr.p->slaveState.getState(),
+ ptr.p->errorCode);
+
+ TablePtr tabPtr;
+ for(ptr.p->tables.first(tabPtr);
+ tabPtr.i != RNIL;
+ ptr.p->tables.next(tabPtr)) {
+ jam();
+ for(Uint32 j = 0; j<3; j++) {
+ jam();
+ TriggerPtr trigPtr;
+ if(tabPtr.p->triggerAllocated[j]) {
+ jam();
+ c_triggerPool.getPtr(trigPtr, tabPtr.p->triggerIds[j]);
+ ndbout_c("Allocated[%u] Triggerid = %u, event = %u",
+ j,
+ tabPtr.p->triggerIds[j],
+ trigPtr.p->event);
+ }//if
+ }//for
+ }//for
+
+ BackupFilePtr filePtr;
+ for(ptr.p->files.first(filePtr);
+ filePtr.i != RNIL;
+ ptr.p->files.next(filePtr)) {
+ jam();
+ ndbout_c("filePtr.i = %u, filePtr.p->fileOpened=%u fileRunning=%u "
+ "scanRunning=%u",
+ filePtr.i,
+ filePtr.p->fileOpened,
+ filePtr.p->fileRunning,
+ filePtr.p->scanRunning);
+ }//for
+ }
+}
+
+void
+Backup::cleanupMasterResources(BackupRecordPtr ptr)
+{
+#ifdef DEBUG_ABORT
+ ndbout_c("******** Cleanup Master Resources *********");
+ ndbout_c("backupId = %u, errorCode = %u", ptr.p->backupId, ptr.p->errorCode);
+#endif
+
+ TablePtr tabPtr;
+ for(ptr.p->tables.first(tabPtr); tabPtr.i != RNIL;ptr.p->tables.next(tabPtr))
+ {
+ jam();
+ tabPtr.p->attributes.release();
+ tabPtr.p->fragments.release();
+ for(Uint32 j = 0; j<3; j++) {
+ jam();
+ TriggerPtr trigPtr;
+ if(tabPtr.p->triggerAllocated[j]) {
+ jam();
+ c_triggerPool.getPtr(trigPtr, tabPtr.p->triggerIds[j]);
+ trigPtr.p->event = ILLEGAL_TRIGGER_ID;
+ tabPtr.p->triggerAllocated[j] = false;
+ }//if
+ tabPtr.p->triggerIds[j] = ILLEGAL_TRIGGER_ID;
+ }//for
+ }//for
+ ptr.p->tables.release();
+ ptr.p->triggers.release();
+ ptr.p->okToCleanMaster = true;
+
+ cleanupFinalResources(ptr);
+}
+
+void
+Backup::cleanupSlaveResources(BackupRecordPtr ptr)
+{
+#ifdef DEBUG_ABORT
+ ndbout_c("******** Clean Up Slave Resources*********");
+ ndbout_c("backupId = %u, errorCode = %u", ptr.p->backupId, ptr.p->errorCode);
+#endif
+
+ BackupFilePtr filePtr;
+ for(ptr.p->files.first(filePtr);
+ filePtr.i != RNIL;
+ ptr.p->files.next(filePtr)) {
+ jam();
+ ndbrequire(filePtr.p->fileOpened == 0);
+ ndbrequire(filePtr.p->fileRunning == 0);
+ ndbrequire(filePtr.p->scanRunning == 0);
+ filePtr.p->pages.release();
+ }//for
+ ptr.p->files.release();
+
+ cleanupFinalResources(ptr);
+}
+
+void
+Backup::cleanupFinalResources(BackupRecordPtr ptr)
+{
+#ifdef DEBUG_ABORT
+ ndbout_c("******** Clean Up Final Resources*********");
+ ndbout_c("backupId = %u, errorCode = %u", ptr.p->backupId, ptr.p->errorCode);
+#endif
+
+ // if (!ptr.p->tables.empty() || !ptr.p->files.empty()) {
+ if (!ptr.p->okToCleanMaster || !ptr.p->files.empty()) {
+ jam();
+#ifdef DEBUG_ABORT
+ ndbout_c("******** Waiting to do final cleanup");
+#endif
+ return;
+ }
+ ptr.p->pages.release();
+ ptr.p->masterData.state.setState(INITIAL);
+ ptr.p->slaveState.setState(INITIAL);
+ ptr.p->backupId = 0;
+
+ ptr.p->closingFiles = false;
+ ptr.p->okToCleanMaster = true;
+
+ c_backups.release(ptr);
+ // ndbrequire(false);
+}
diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.hpp b/storage/ndb/src/kernel/blocks/backup/Backup.hpp
new file mode 100644
index 00000000000..1a5d6c7a925
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/Backup.hpp
@@ -0,0 +1,696 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 BACKUP_H
+#define BACKUP_H
+
+#include <ndb_limits.h>
+#include <SimulatedBlock.hpp>
+
+#include "FsBuffer.hpp"
+#include "BackupFormat.hpp"
+
+#include <NodeBitmask.hpp>
+#include <SimpleProperties.hpp>
+
+#include <SLList.hpp>
+#include <ArrayList.hpp>
+#include <SignalCounter.hpp>
+#include <blocks/mutexes.hpp>
+
+#include <NdbTCP.h>
+
+/**
+ * Backup - This block manages database backup and restore
+ */
+class Backup : public SimulatedBlock
+{
+public:
+ Backup(const Configuration & conf);
+ virtual ~Backup();
+ BLOCK_DEFINES(Backup);
+
+protected:
+
+ void execSTTOR(Signal* signal);
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execREAD_NODESCONF(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execINCL_NODEREQ(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+
+ /**
+ * Testing
+ */
+ void execBACKUP_REF(Signal* signal);
+ void execBACKUP_CONF(Signal* signal);
+ void execBACKUP_ABORT_REP(Signal* signal);
+ void execBACKUP_COMPLETE_REP(Signal* signal);
+
+ /**
+ * Signals sent from master
+ */
+ void execDEFINE_BACKUP_REQ(Signal* signal);
+ void execBACKUP_DATA(Signal* signal);
+ void execSTART_BACKUP_REQ(Signal* signal);
+ void execBACKUP_FRAGMENT_REQ(Signal* signal);
+ void execSTOP_BACKUP_REQ(Signal* signal);
+ void execBACKUP_STATUS_REQ(Signal* signal);
+ void execABORT_BACKUP_ORD(Signal* signal);
+
+ /**
+ * The actual scan
+ */
+ void execSCAN_HBREP(Signal* signal);
+ void execTRANSID_AI(Signal* signal);
+ void execSCAN_FRAGREF(Signal* signal);
+ void execSCAN_FRAGCONF(Signal* signal);
+
+ /**
+ * Trigger logging
+ */
+ void execBACKUP_TRIG_REQ(Signal* signal);
+ void execTRIG_ATTRINFO(Signal* signal);
+ void execFIRE_TRIG_ORD(Signal* signal);
+
+ /**
+ * DICT signals
+ */
+ void execLIST_TABLES_CONF(Signal* signal);
+ void execGET_TABINFOREF(Signal* signal);
+ void execGET_TABINFO_CONF(Signal* signal);
+ void execCREATE_TRIG_REF(Signal* signal);
+ void execCREATE_TRIG_CONF(Signal* signal);
+ void execALTER_TRIG_REF(Signal* signal);
+ void execALTER_TRIG_CONF(Signal* signal);
+ void execDROP_TRIG_REF(Signal* signal);
+ void execDROP_TRIG_CONF(Signal* signal);
+
+ /**
+ * DIH signals
+ */
+ void execDI_FCOUNTCONF(Signal* signal);
+ void execDIGETPRIMCONF(Signal* signal);
+
+ /**
+ * FS signals
+ */
+ void execFSOPENREF(Signal* signal);
+ void execFSOPENCONF(Signal* signal);
+
+ void execFSCLOSEREF(Signal* signal);
+ void execFSCLOSECONF(Signal* signal);
+
+ void execFSAPPENDREF(Signal* signal);
+ void execFSAPPENDCONF(Signal* signal);
+
+ void execFSREMOVEREF(Signal* signal);
+ void execFSREMOVECONF(Signal* signal);
+
+ /**
+ * Master functinallity
+ */
+ void execBACKUP_REQ(Signal* signal);
+ void execABORT_BACKUP_REQ(Signal* signal);
+
+ void execDEFINE_BACKUP_REF(Signal* signal);
+ void execDEFINE_BACKUP_CONF(Signal* signal);
+
+ void execSTART_BACKUP_REF(Signal* signal);
+ void execSTART_BACKUP_CONF(Signal* signal);
+
+ void execBACKUP_FRAGMENT_REF(Signal* signal);
+ void execBACKUP_FRAGMENT_CONF(Signal* signal);
+
+ void execSTOP_BACKUP_REF(Signal* signal);
+ void execSTOP_BACKUP_CONF(Signal* signal);
+
+ void execBACKUP_STATUS_CONF(Signal* signal);
+
+ void execUTIL_SEQUENCE_REF(Signal* signal);
+ void execUTIL_SEQUENCE_CONF(Signal* signal);
+
+ void execWAIT_GCP_REF(Signal* signal);
+ void execWAIT_GCP_CONF(Signal* signal);
+
+
+private:
+ void defineBackupMutex_locked(Signal* signal, Uint32 ptrI,Uint32 retVal);
+ void dictCommitTableMutex_locked(Signal* signal, Uint32 ptrI,Uint32 retVal);
+
+public:
+ struct Node {
+ Uint32 nodeId;
+ Uint32 alive;
+ Uint32 nextList;
+ union { Uint32 prevList; Uint32 nextPool; };
+ };
+ typedef Ptr<Node> NodePtr;
+
+#define BACKUP_WORDS_PER_PAGE 8191
+ struct Page32 {
+ Uint32 data[BACKUP_WORDS_PER_PAGE];
+ Uint32 nextPool;
+ };
+ typedef Ptr<Page32> Page32Ptr;
+
+ struct Attribute {
+ struct Data {
+ Uint8 nullable;
+ Uint8 fixed;
+ Uint8 unused;
+ Uint8 unused2;
+ Uint32 sz32; // No of 32 bit words
+ Uint32 offset; // Relative DataFixedAttributes/DataFixedKeys
+ Uint32 offsetNull; // In NullBitmask
+ } data;
+ Uint32 nextPool;
+ };
+ typedef Ptr<Attribute> AttributePtr;
+
+ struct Fragment {
+ Uint32 tableId;
+ Uint32 node;
+ Uint16 scanned; // 0 = not scanned x = scanned by node x
+ Uint16 scanning; // 0 = not scanning x = scanning on node x
+ Uint32 nextPool;
+ };
+ typedef Ptr<Fragment> FragmentPtr;
+
+ struct Table {
+ Table(ArrayPool<Attribute> &, ArrayPool<Fragment> &);
+
+ Uint32 tableId;
+ Uint32 schemaVersion;
+ Uint32 frag_mask;
+ Uint32 tableType;
+ Uint32 noOfNull;
+ Uint32 noOfAttributes;
+ Uint32 noOfVariable;
+ Uint32 sz_FixedAttributes;
+ Uint32 triggerIds[3];
+ bool triggerAllocated[3];
+
+ Array<Attribute> attributes;
+ Array<Fragment> fragments;
+
+ Uint32 nextList;
+ union { Uint32 nextPool; Uint32 prevList; };
+ };
+ typedef Ptr<Table> TablePtr;
+
+ struct OperationRecord {
+ public:
+ OperationRecord(Backup & b) : backup(b) {}
+
+ /**
+ * Once per table
+ */
+ void init(const TablePtr & ptr);
+
+ /**
+ * Once per fragment
+ */
+ bool newFragment(Uint32 tableId, Uint32 fragNo);
+ bool fragComplete(Uint32 tableId, Uint32 fragNo);
+
+ /**
+ * Once per scan frag (next) req/conf
+ */
+ bool newScan();
+ bool scanConf(Uint32 noOfOps, Uint32 opLen);
+
+ /**
+ * Per record
+ */
+ void newRecord(Uint32 * base);
+ bool finished();
+
+ /**
+ * Per attribute
+ */
+ void nullAttribute(Uint32 nullOffset);
+ Uint32 * newNullable(Uint32 attrId, Uint32 sz);
+ Uint32 * newAttrib(Uint32 offset, Uint32 sz);
+ Uint32 * newVariable(Uint32 id, Uint32 sz);
+
+ private:
+ Uint32* base;
+ Uint32* dst_Length;
+ Uint32* dst_Bitmask;
+ Uint32* dst_FixedAttribs;
+ BackupFormat::DataFile::VariableData* dst_VariableData;
+
+ Uint32 noOfAttributes; // No of Attributes
+ Uint32 attrLeft; // No of attributes left
+
+ Uint32 opNoDone;
+ Uint32 opNoConf;
+ Uint32 opLen;
+
+ public:
+ Uint32* dst;
+ Uint32 attrSzLeft; // No of words missing for current attribute
+ Uint32 attrSzTotal; // No of AI words received
+ Uint32 tablePtr; // Ptr.i to current table
+
+ FsBuffer dataBuffer;
+ Uint32 noOfRecords;
+ Uint32 noOfBytes;
+ Uint32 maxRecordSize;
+
+ private:
+ Uint32* scanStart;
+ Uint32* scanStop;
+
+ /**
+ * sizes of part
+ */
+ Uint32 sz_Bitmask;
+ Uint32 sz_FixedAttribs;
+
+ public:
+ union { Uint32 nextPool; Uint32 nextList; };
+ Uint32 prevList;
+ private:
+
+ Backup & backup;
+ BlockNumber number() const { return backup.number(); }
+ void progError(int line, int cause, const char * extra) {
+ backup.progError(line, cause, extra);
+ }
+ };
+ friend struct OperationRecord;
+
+ struct TriggerRecord {
+ TriggerRecord() { event = ~0;}
+ OperationRecord * operation;
+ BackupFormat::LogFile::LogEntry * logEntry;
+ Uint32 maxRecordSize;
+ Uint32 tableId;
+ Uint32 tab_ptr_i;
+ Uint32 event;
+ Uint32 backupPtr;
+ Uint32 errorCode;
+ union { Uint32 nextPool; Uint32 nextList; };
+ };
+ typedef Ptr<TriggerRecord> TriggerPtr;
+
+ /**
+ * BackupFile - At least 3 per backup
+ */
+ struct BackupFile {
+ BackupFile(Backup & backup, ArrayPool<Page32> & pp)
+ : operation(backup), pages(pp) {}
+
+ Uint32 backupPtr; // Pointer to backup record
+ Uint32 tableId;
+ Uint32 fragmentNo;
+ Uint32 filePointer;
+ Uint32 errorCode;
+ BackupFormat::FileType fileType;
+ OperationRecord operation;
+
+ Array<Page32> pages;
+ Uint32 nextList;
+ union { Uint32 prevList; Uint32 nextPool; };
+
+ Uint8 fileOpened;
+ Uint8 fileRunning;
+ Uint8 fileDone;
+ Uint8 scanRunning;
+ };
+ typedef Ptr<BackupFile> BackupFilePtr;
+
+
+ /**
+ * State for BackupRecord
+ */
+ enum State {
+ INITIAL,
+ DEFINING, // Defining backup content and parameters
+ DEFINED, // DEFINE_BACKUP_CONF sent in slave, received all in master
+ STARTED, // Creating triggers
+ SCANNING, // Scanning fragments
+ STOPPING, // Closing files
+ CLEANING, // Cleaning resources
+ ABORTING // Aborting backup
+ };
+
+ static const Uint32 validSlaveTransitionsCount;
+ static const Uint32 validMasterTransitionsCount;
+ static const State validSlaveTransitions[];
+ static const State validMasterTransitions[];
+
+ class CompoundState {
+ public:
+ CompoundState(Backup & b,
+ const State valid[],
+ Uint32 count, Uint32 _id)
+ : backup(b)
+ , validTransitions(valid),
+ noOfValidTransitions(count), id(_id)
+ {
+ state = INITIAL;
+ abortState = state;
+ }
+
+ void setState(State s);
+ State getState() const { return state;}
+ State getAbortState() const { return abortState;}
+
+ void forceState(State s);
+
+ BlockNumber number() const { return backup.number(); }
+ void progError(int line, int cause, const char * extra) {
+ backup.progError(line, cause, extra);
+ }
+ private:
+ Backup & backup;
+ State state;
+ State abortState; /**
+ When state == ABORTING, this contains the state
+ when the abort started
+ */
+ const State * validTransitions;
+ const Uint32 noOfValidTransitions;
+ const Uint32 id;
+ };
+ friend class CompoundState;
+
+ /**
+ * Backup record
+ *
+ * One record per backup
+ */
+ struct BackupRecord {
+ BackupRecord(Backup& b, ArrayPool<Page32> & pp,
+ ArrayPool<Table> & tp,
+ ArrayPool<BackupFile> & bp,
+ ArrayPool<TriggerRecord> & trp)
+ : slaveState(b, validSlaveTransitions, validSlaveTransitionsCount,1)
+ , tables(tp), triggers(trp), files(bp), pages(pp)
+ , masterData(b, validMasterTransitions, validMasterTransitionsCount)
+ , backup(b)
+ {
+ closingFiles = false;
+ okToCleanMaster = true;
+ }
+
+ CompoundState slaveState;
+
+ Uint32 clientRef;
+ Uint32 clientData;
+ Uint32 backupId;
+ Uint32 backupKey[2];
+ Uint32 masterRef;
+ Uint32 errorCode;
+ NdbNodeBitmask nodes;
+
+ bool okToCleanMaster;
+ bool closingFiles;
+
+ Uint64 noOfBytes;
+ Uint64 noOfRecords;
+ Uint64 noOfLogBytes;
+ Uint64 noOfLogRecords;
+
+ Uint32 startGCP;
+ Uint32 currGCP;
+ Uint32 stopGCP;
+ DLList<Table> tables;
+ SLList<TriggerRecord> triggers;
+
+ SLList<BackupFile> files;
+ Uint32 ctlFilePtr; // Ptr.i to ctl-file
+ Uint32 logFilePtr; // Ptr.i to log-file
+ Uint32 dataFilePtr; // Ptr.i to first data-file
+
+ Uint32 backupDataLen; // Used for (un)packing backup request
+ Array<Page32> pages; // Used for (un)packing backup request
+ SimpleProperties props;// Used for (un)packing backup request
+
+ struct MasterData {
+ MasterData(Backup & b, const State valid[], Uint32 count)
+ : state(b, valid, count, 0)
+ {
+ }
+ MutexHandle2<BACKUP_DEFINE_MUTEX> m_defineBackupMutex;
+ MutexHandle2<DICT_COMMIT_TABLE_MUTEX> m_dictCommitTableMutex;
+
+ Uint32 gsn;
+ CompoundState state;
+ SignalCounter sendCounter;
+ Uint32 errorCode;
+ struct {
+ Uint32 tableId;
+ } createTrig;
+ struct {
+ Uint32 tableId;
+ } dropTrig;
+ struct {
+ Uint32 tableId;
+ } alterTrig;
+ union {
+ struct {
+ Uint32 startBackup;
+ } waitGCP;
+ struct {
+ Uint32 signalNo;
+ Uint32 noOfSignals;
+ Uint32 tablePtr;
+ } startBackup;
+ struct {
+ Uint32 dummy;
+ } stopBackup;
+ };
+ } masterData;
+
+ Uint32 nextList;
+ union { Uint32 prevList; Uint32 nextPool; };
+
+ void setErrorCode(Uint32 errCode){
+ if(errorCode == 0)
+ errorCode = errCode;
+ }
+
+ bool checkError() const {
+ return errorCode != 0;
+ }
+
+ Backup & backup;
+ BlockNumber number() const { return backup.number(); }
+ void progError(int line, int cause, const char * extra) {
+ backup.progError(line, cause, extra);
+ }
+ };
+ friend struct BackupRecord;
+ typedef Ptr<BackupRecord> BackupRecordPtr;
+
+ struct Config {
+ Uint32 m_dataBufferSize;
+ Uint32 m_logBufferSize;
+ Uint32 m_minWriteSize;
+ Uint32 m_maxWriteSize;
+ };
+
+ /**
+ * Variables
+ */
+ Uint32 * c_startOfPages;
+ NodeId c_masterNodeId;
+ SLList<Node> c_nodes;
+ NdbNodeBitmask c_aliveNodes;
+ DLList<BackupRecord> c_backups;
+ Config c_defaults;
+ Uint32 m_diskless;
+
+ STATIC_CONST(NO_OF_PAGES_META_FILE = 2);
+
+ /**
+ * Pools
+ */
+ ArrayPool<Table> c_tablePool;
+ ArrayPool<Attribute> c_attributePool;
+ ArrayPool<BackupRecord> c_backupPool;
+ ArrayPool<BackupFile> c_backupFilePool;
+ ArrayPool<Page32> c_pagePool;
+ ArrayPool<Fragment> c_fragmentPool;
+ ArrayPool<Node> c_nodePool;
+ ArrayPool<TriggerRecord> c_triggerPool;
+
+ Uint32 calculate_frag_mask(Uint32);
+
+ void checkFile(Signal*, BackupFilePtr);
+ void checkScan(Signal*, BackupFilePtr);
+ void fragmentCompleted(Signal*, BackupFilePtr);
+
+ void backupAllData(Signal* signal, BackupRecordPtr);
+
+ void getFragmentInfo(Signal*, BackupRecordPtr, TablePtr, Uint32 fragNo);
+ void getFragmentInfoDone(Signal*, BackupRecordPtr);
+
+ void openFiles(Signal* signal, BackupRecordPtr ptr);
+ void openFilesReply(Signal*, BackupRecordPtr ptr, BackupFilePtr);
+ void closeFiles(Signal*, BackupRecordPtr ptr);
+ void closeFilesDone(Signal*, BackupRecordPtr ptr);
+
+ void sendDefineBackupReq(Signal *signal, BackupRecordPtr ptr);
+
+ void defineBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32 nodeId);
+ void createTrigReply(Signal* signal, BackupRecordPtr ptr);
+ void alterTrigReply(Signal* signal, BackupRecordPtr ptr);
+ void startBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32, Uint32);
+ void stopBackupReply(Signal* signal, BackupRecordPtr ptr, Uint32 nodeId);
+
+ void defineBackupRef(Signal*, BackupRecordPtr, Uint32 errCode = 0);
+
+ void nextFragment(Signal*, BackupRecordPtr);
+
+ void sendCreateTrig(Signal*, BackupRecordPtr ptr, TablePtr tabPtr);
+ void createAttributeMask(TablePtr tab, Bitmask<MAXNROFATTRIBUTESINWORDS>&);
+ void sendStartBackup(Signal*, BackupRecordPtr, TablePtr);
+ void sendAlterTrig(Signal*, BackupRecordPtr ptr);
+
+ void sendDropTrig(Signal*, BackupRecordPtr ptr);
+ void sendDropTrig(Signal* signal, BackupRecordPtr ptr, TablePtr tabPtr);
+ void dropTrigReply(Signal*, BackupRecordPtr ptr);
+
+ void sendSignalAllWait(BackupRecordPtr ptr, Uint32 gsn, Signal *signal,
+ Uint32 signalLength,
+ bool executeDirect = false);
+ bool haveAllSignals(BackupRecordPtr ptr, Uint32 gsn, Uint32 nodeId);
+
+ void sendStopBackup(Signal*, BackupRecordPtr ptr);
+ void sendAbortBackupOrd(Signal* signal, BackupRecordPtr ptr, Uint32 errCode);
+ void sendAbortBackupOrdSlave(Signal* signal, BackupRecordPtr ptr,
+ Uint32 errCode);
+ void masterAbort(Signal*, BackupRecordPtr ptr, bool controlledAbort);
+ void masterSendAbortBackup(Signal*, BackupRecordPtr ptr);
+ void slaveAbort(Signal*, BackupRecordPtr ptr);
+
+ void abortFile(Signal* signal, BackupRecordPtr ptr, BackupFilePtr filePtr);
+ void abortFileHook(Signal* signal, BackupFilePtr filePtr, bool scanDone);
+
+ bool verifyNodesAlive(const NdbNodeBitmask& aNodeBitMask);
+ bool checkAbort(BackupRecordPtr ptr);
+ void checkNodeFail(Signal* signal,
+ BackupRecordPtr ptr,
+ NodeId newCoord,
+ Uint32 theFailedNodes[NodeBitmask::Size]);
+ void masterTakeOver(Signal* signal, BackupRecordPtr ptr);
+
+
+ NodeId getMasterNodeId() const { return c_masterNodeId; }
+ bool findTable(const BackupRecordPtr &, TablePtr &, Uint32 tableId) const;
+ TablePtr parseTableDescription(Signal*, BackupRecordPtr ptr, Uint32 len);
+
+ bool insertFileHeader(BackupFormat::FileType, BackupRecord*, BackupFile*);
+ void sendBackupRef(Signal* signal, BackupRecordPtr ptr, Uint32 errorCode);
+ void sendBackupRef(BlockReference ref, Signal *signal,
+ Uint32 senderData, Uint32 errorCode);
+ void dumpUsedResources();
+ void cleanupMasterResources(BackupRecordPtr ptr);
+ void cleanupSlaveResources(BackupRecordPtr ptr);
+ void cleanupFinalResources(BackupRecordPtr ptr);
+ void removeBackup(Signal*, BackupRecordPtr ptr);
+
+ void sendSTTORRY(Signal*);
+ void createSequence(Signal* signal);
+ void createSequenceReply(Signal*, class UtilSequenceConf *);
+};
+
+inline
+void
+Backup::OperationRecord::newRecord(Uint32 * p){
+ base = p;
+ dst_Length = p; p += 1;
+ dst_Bitmask = p; p += sz_Bitmask;
+ dst_FixedAttribs = p; p += sz_FixedAttribs;
+ dst_VariableData = (BackupFormat::DataFile::VariableData*)p;
+ BitmaskImpl::clear(sz_Bitmask, dst_Bitmask);
+ attrLeft = noOfAttributes;
+ attrSzLeft = attrSzTotal = 0;
+}
+
+inline
+Uint32 *
+Backup::OperationRecord::newAttrib(Uint32 offset, Uint32 sz){
+ attrLeft--;
+ attrSzLeft = sz;
+ dst = dst_FixedAttribs + offset;
+ return dst;
+}
+
+inline
+void
+Backup::OperationRecord::nullAttribute(Uint32 offsetNull){
+ attrLeft --;
+ BitmaskImpl::set(sz_Bitmask, dst_Bitmask, offsetNull);
+}
+
+inline
+Uint32 *
+Backup::OperationRecord::newNullable(Uint32 id, Uint32 sz){
+ attrLeft--;
+ attrSzLeft = sz;
+
+ dst = &dst_VariableData->Data[0];
+ dst_VariableData->Sz = htonl(sz);
+ dst_VariableData->Id = htonl(id);
+
+ dst_VariableData = (BackupFormat::DataFile::VariableData *)(dst + sz);
+
+ // Clear all bits on newRecord -> dont need to clear this
+ // BitmaskImpl::clear(sz_Bitmask, dst_Bitmask, offsetNull);
+ return dst;
+}
+
+inline
+Uint32 *
+Backup::OperationRecord::newVariable(Uint32 id, Uint32 sz){
+ attrLeft--;
+ attrSzLeft = sz;
+
+ dst = &dst_VariableData->Data[0];
+ dst_VariableData->Sz = htonl(sz);
+ dst_VariableData->Id = htonl(id);
+
+ dst_VariableData = (BackupFormat::DataFile::VariableData *)(dst + sz);
+ return dst;
+}
+
+inline
+bool
+Backup::OperationRecord::finished(){
+ if(attrLeft != 0 || attrSzLeft != 0){
+ return false;
+ }
+
+ opLen += attrSzTotal;
+ opNoDone++;
+
+ scanStop = dst = (Uint32 *)dst_VariableData;
+
+ const Uint32 len = (dst - base - 1);
+ * dst_Length = htonl(len);
+
+ noOfRecords++;
+
+ return true;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.txt b/storage/ndb/src/kernel/blocks/backup/Backup.txt
new file mode 100644
index 00000000000..ee5e02bb549
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/Backup.txt
@@ -0,0 +1,343 @@
+-- BACKUP SIGNAL DIAGRAM COMPLEMENT TO BACKUP AMENDMENTS 2003-07-11 --
+
+USER MASTER MASTER SLAVE SLAVE
+---------------------------------------------------------------------
+BACKUP_REQ
+---------------->
+ UTIL_SEQUENCE
+ --------------->
+ <---------------
+ DEFINE_BACKUP
+ ------------------------------> (Local signals)
+ LIST_TABLES
+ --------------->
+ <---------------
+ FSOPEN
+ --------------->
+ GET_TABINFO
+ <---------------
+ DI_FCOUNT
+ --------------->
+ <---------------
+ DI_GETPRIM
+ --------------->
+ <---------------
+ <-------------------------------
+BACKUP_CONF
+<----------------
+ CREATE_TRIG
+ --------------> (If master crashes here -> rouge triggers/memory leak)
+ <--------------
+ START_BACKUP
+ ------------------------------>
+ <------------------------------
+ ALTER_TRIG
+ -------------->
+ <--------------
+ WAIT_GCP
+ -------------->
+ <--------------
+ BACKUP_FRAGMENT
+ ------------------------------>
+ SCAN_FRAG
+ --------------->
+ <---------------
+ <------------------------------
+ WAIT_GCP
+ -------------->
+ <--------------
+ DROP_TRIG
+ -------------->
+ <--------------
+ STOP_BACKUP
+ ------------------------------>
+ <------------------------------
+BACKUP_COMPLETE_REP
+<----------------
+ ABORT_BACKUP
+ ------------------------------>
+
+----------------------------------------------------------------------------
+
+USER BACKUP-MASTER
+
+1) BACKUP_REQ -->
+
+2) To all slaves DEFINE_BACKUP_REQ
+ This signals contains info so that all
+ slaves can take over as master
+ Tomas: Except triggerId info...
+
+3) Wait for conf
+
+4) <-- BACKUP_CONF
+
+5) For Each Table
+ PREP_CREATE_TRIG_REQ
+ Wait for Conf
+
+6) To all slaves START_BACKUP_REQ
+ Include trigger ids
+ Wait for conf
+
+7) For Each Table
+ CREATE_TRIG_REQ
+ Wait for conf
+
+8) Wait for GCP
+
+9) For each table
+ For each fragment
+ BACKUP_FRAGMENT_REQ -->
+ <-- BACKUP_FRAGMENT_CONF
+
+10) Wait for GCP
+
+11) To all slaves STOP_BACKUP_REQ
+ This signal turns off logging
+
+12) Wait for conf
+
+13) <-- BACKUP_COMPLETE_REP
+
+----
+
+Slave: Master Died
+Wait for master take-over, max 30 sec then abort everything
+
+Slave: Master TakeOver
+
+BACKUP_STATUS_REQ --> To all nodes
+<-- BACKUP_STATUS_CONF
+
+BACKUP_STATUS_CONF
+ BACKUP_DEFINED
+ BACKUP_STARTED
+ BACKUP_FRAGMENT
+
+Master: Slave died
+
+-- Define Backup Req --
+
+1) Get backup definition
+ Which tables (all)
+
+2) Open files
+ Write table list to CTL - file
+
+3) Get definitions for all tables in backup
+
+4) Get Fragment info
+
+5) Define Backup Conf
+
+-- Define Backup Req --
+
+-- Abort Backup Req --
+
+1) Report to others
+
+2) Stop logging
+3) Stop file(s)
+4) Stop scan
+
+5) If failure/abort
+ Remove files
+
+6) If XXX
+ Report to user
+7) Clean up records/stuff
+
+-- Abort Backup --
+
+Reasons for aborting:
+
+1a) client abort
+
+1b) slave failure
+
+1c) node failure
+
+Resources to be cleaned up:
+
+Slave responsability:
+
+2a) Close and remove files
+
+2b) Free allocated resources
+
+Master responsability:
+
+2c) Drop triggers
+
+USER MASTER MASTER SLAVE SLAVE
+---------------------------------------------------------------------
+ BACKUP_ABORT_ORD:
+ -------------------------(ALL)-->
+ Set Master State ABORTING Set Slave State ABORTING
+ Drop Triggers Close and Remove files
+ CleanupSlaveResources()
+
+ BACKUP_ABORT_ORD:OkToClean
+ -------------------------(ALL)-->
+
+
+ CleanupMasterResources()
+
+BACKUP_ABORT_REP
+<---------------
+
+
+
+State descriptions:
+
+Master - INITIAL
+BACKUP_REQ ->
+Master - DEFINING
+DEFINE_BACKUP_CONF ->
+Master - DEFINED
+CREATE_TRIG_CONF ->
+Master - STARTED
+<--->
+Master - SCANNING
+WAIT_GCP_CONF ->
+Master - STOPPING
+(Master - CLEANING)
+--------
+Master - ABORTING
+
+
+Slave - INITIAL
+DEFINE_BACKUP_REQ ->
+Slave - DEFINING
+ - backupId
+ - tables
+DIGETPRIMCONF ->
+Slave - DEFINED
+START_BACKUP_REQ ->
+Slave - STARTED
+Slave - SCANNING
+STOP_BACKUP_REQ ->
+Slave - STOPPING
+FSCLOSECONF ->
+Slave - CLEANING
+-----
+Slave - ABORTING
+
+
+
+Testcases:
+
+2. Master failure at first START_BACKUP_CONF
+
+<masterId> error 10004
+start backup
+
+- Ok
+
+2. Master failure at first CREATE_TRIG_CONF
+
+<masterId> error 10003
+start backup
+
+- Ok
+
+2. Master failure at first ALTER_TRIG_CONF
+
+<masterId> error 10005
+start backup
+
+- Ok
+
+2. Master failure at WAIT_GCP_CONF
+
+<masterId> error 10007
+start backup
+
+- Ok
+
+2. Master failure at WAIT_GCP_CONF, nextFragment
+
+<masterId> error 10008
+start backup
+
+- Ok
+
+2. Master failure at WAIT_GCP_CONF, stopping
+
+<masterId> error 10009
+start backup
+
+- Ok
+
+2. Master failure at BACKUP_FRAGMENT_CONF
+
+<masterId> error 10010
+start backup
+
+- Ok
+
+2. Master failure at first DROP_TRIG_CONF
+
+<masterId> error 10012
+start backup
+
+- Ok
+
+1. Master failure at first STOP_BACKUP_CONF
+
+<masterId> error 10013
+start backup
+
+- Ok
+
+3. Multiple node failiure:
+
+<masterId> error 10001
+<otheId> error 10014
+start backup
+
+- Ok (note, mgmtsrvr does gets BACKUP_ABORT_REP but expects BACKUP_REF, hangs...)
+
+4. Multiple node failiure:
+
+<masterId> error 10007
+<takeover id> error 10002
+start backup
+
+- Ok
+
+
+
+ ndbrequire(!ERROR_INSERTED(10001));
+ ndbrequire(!ERROR_INSERTED(10002));
+ ndbrequire(!ERROR_INSERTED(10021));
+ ndbrequire(!ERROR_INSERTED(10003));
+ ndbrequire(!ERROR_INSERTED(10004));
+ ndbrequire(!ERROR_INSERTED(10005));
+ ndbrequire(!ERROR_INSERTED(10006));
+ ndbrequire(!ERROR_INSERTED(10007));
+ ndbrequire(!ERROR_INSERTED(10008));
+ ndbrequire(!ERROR_INSERTED(10009));
+ ndbrequire(!ERROR_INSERTED(10010));
+ ndbrequire(!ERROR_INSERTED(10011));
+ ndbrequire(!ERROR_INSERTED(10012));
+ ndbrequire(!ERROR_INSERTED(10013));
+ ndbrequire(!ERROR_INSERTED(10014));
+ ndbrequire(!ERROR_INSERTED(10015));
+ ndbrequire(!ERROR_INSERTED(10016));
+ ndbrequire(!ERROR_INSERTED(10017));
+ ndbrequire(!ERROR_INSERTED(10018));
+ ndbrequire(!ERROR_INSERTED(10019));
+ ndbrequire(!ERROR_INSERTED(10020));
+
+ if (ERROR_INSERTED(10023)) {
+ if (ERROR_INSERTED(10023)) {
+ if (ERROR_INSERTED(10024)) {
+ if (ERROR_INSERTED(10025)) {
+ if (ERROR_INSERTED(10026)) {
+ if (ERROR_INSERTED(10028)) {
+ if (ERROR_INSERTED(10027)) {
+ (ERROR_INSERTED(10022))) {
+ if (ERROR_INSERTED(10029)) {
+ if(trigPtr.p->operation->noOfBytes > 123 && ERROR_INSERTED(10030)) {
diff --git a/storage/ndb/src/kernel/blocks/backup/BackupFormat.hpp b/storage/ndb/src/kernel/blocks/backup/BackupFormat.hpp
new file mode 100644
index 00000000000..65dd2ad9053
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/BackupFormat.hpp
@@ -0,0 +1,149 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 BACKUP_FORMAT_HPP
+#define BACKUP_FORMAT_HPP
+
+#include <ndb_types.h>
+
+static const char BACKUP_MAGIC[] = { 'N', 'D', 'B', 'B', 'C', 'K', 'U', 'P' };
+
+struct BackupFormat {
+
+ /**
+ * Section types in file
+ */
+ enum SectionType {
+ FILE_HEADER = 1,
+ FRAGMENT_HEADER = 2,
+ FRAGMENT_FOOTER = 3,
+ TABLE_LIST = 4,
+ TABLE_DESCRIPTION = 5,
+ GCP_ENTRY = 6
+ };
+
+ struct FileHeader {
+ char Magic[8];
+ Uint32 NdbVersion;
+
+ Uint32 SectionType;
+ Uint32 SectionLength;
+ Uint32 FileType;
+ Uint32 BackupId;
+ Uint32 BackupKey_0;
+ Uint32 BackupKey_1;
+ Uint32 ByteOrder;
+ };
+
+ /**
+ * File types
+ */
+ enum FileType {
+ CTL_FILE = 1,
+ LOG_FILE = 2,
+ DATA_FILE = 3
+ };
+
+ /**
+ * Data file formats
+ */
+ struct DataFile {
+
+ struct FragmentHeader {
+ Uint32 SectionType;
+ Uint32 SectionLength;
+ Uint32 TableId;
+ Uint32 FragmentNo;
+ Uint32 ChecksumType;
+ };
+
+ struct VariableData {
+ Uint32 Sz;
+ Uint32 Id;
+ Uint32 Data[1];
+ };
+
+ struct Record {
+ Uint32 Length;
+ Uint32 NullBitmask[1];
+ Uint32 DataFixedKeys[1];
+ Uint32 DataFixedAttributes[1];
+ VariableData DataVariableAttributes[1];
+ };
+
+ struct FragmentFooter {
+ Uint32 SectionType;
+ Uint32 SectionLength;
+ Uint32 TableId;
+ Uint32 FragmentNo;
+ Uint32 NoOfRecords;
+ Uint32 Checksum;
+ };
+ };
+
+ /**
+ * CTL file formats
+ */
+ struct CtlFile {
+
+ /**
+ * Table list
+ */
+ struct TableList {
+ Uint32 SectionType;
+ Uint32 SectionLength;
+ Uint32 TableIds[1]; // Length = SectionLength - 2
+ };
+
+ /**
+ * Table description(s)
+ */
+ struct TableDescription {
+ Uint32 SectionType;
+ Uint32 SectionLength;
+ Uint32 DictTabInfo[1]; // Length = SectionLength - 2
+ };
+
+ /**
+ * GCP Entry
+ */
+ struct GCPEntry {
+ Uint32 SectionType;
+ Uint32 SectionLength;
+ Uint32 StartGCP;
+ Uint32 StopGCP;
+ };
+ };
+
+ /**
+ * LOG file format
+ */
+ struct LogFile {
+
+ /**
+ * Log Entry
+ */
+ struct LogEntry {
+ Uint32 Length;
+ Uint32 TableId;
+ // If TriggerEvent & 0x10000 == true then GCI is right after data
+ Uint32 TriggerEvent;
+ Uint32 Data[1]; // Len = Length - 2
+ };
+ };
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/backup/BackupInit.cpp b/storage/ndb/src/kernel/blocks/backup/BackupInit.cpp
new file mode 100644
index 00000000000..08fa089a9c0
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/BackupInit.cpp
@@ -0,0 +1,211 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//****************************************************************************
+//
+// NAME
+// Backup - Database backup / restore
+//
+//===========================================================================
+#include "Backup.hpp"
+
+#include <Properties.hpp>
+#include <Configuration.hpp>
+
+//extern const unsigned Ndbcntr::g_sysTableCount;
+
+Backup::Backup(const Configuration & conf) :
+ SimulatedBlock(BACKUP, conf),
+ c_nodes(c_nodePool),
+ c_backups(c_backupPool)
+{
+ BLOCK_CONSTRUCTOR(Backup);
+
+ c_nodePool.setSize(MAX_NDB_NODES);
+ c_masterNodeId = getOwnNodeId();
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ Uint32 noBackups = 0, noTables = 0, noAttribs = 0;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_DISCLESS, &m_diskless));
+ ndb_mgm_get_int_parameter(p, CFG_DB_PARALLEL_BACKUPS, &noBackups);
+ // ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_NO_TABLES, &noTables));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DICT_TABLE, &noTables));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_NO_ATTRIBUTES, &noAttribs));
+
+ noAttribs++; //RT 527 bug fix
+
+ c_backupPool.setSize(noBackups);
+ c_backupFilePool.setSize(3 * noBackups);
+ c_tablePool.setSize(noBackups * noTables);
+ c_attributePool.setSize(noBackups * noAttribs);
+ c_triggerPool.setSize(noBackups * 3 * noTables);
+
+ // 2 = no of replicas
+ c_fragmentPool.setSize(noBackups * 2 * NO_OF_FRAG_PER_NODE * noTables);
+
+ Uint32 szMem = 0;
+ ndb_mgm_get_int_parameter(p, CFG_DB_BACKUP_MEM, &szMem);
+ Uint32 noPages = (szMem + sizeof(Page32) - 1) / sizeof(Page32);
+ // We need to allocate an additional of 2 pages. 1 page because of a bug in
+ // ArrayPool and another one for DICTTAINFO.
+ c_pagePool.setSize(noPages + NO_OF_PAGES_META_FILE + 2);
+
+ Uint32 szDataBuf = (2 * 1024 * 1024);
+ Uint32 szLogBuf = (2 * 1024 * 1024);
+ Uint32 szWrite = 32768;
+ ndb_mgm_get_int_parameter(p, CFG_DB_BACKUP_DATA_BUFFER_MEM, &szDataBuf);
+ ndb_mgm_get_int_parameter(p, CFG_DB_BACKUP_LOG_BUFFER_MEM, &szLogBuf);
+ ndb_mgm_get_int_parameter(p, CFG_DB_BACKUP_WRITE_SIZE, &szWrite);
+
+ c_defaults.m_logBufferSize = szLogBuf;
+ c_defaults.m_dataBufferSize = szDataBuf;
+ c_defaults.m_minWriteSize = szWrite;
+ c_defaults.m_maxWriteSize = szWrite;
+
+ { // Init all tables
+ ArrayList<Table> tables(c_tablePool);
+ TablePtr ptr;
+ while(tables.seize(ptr)){
+ new (ptr.p) Table(c_attributePool, c_fragmentPool);
+ }
+ tables.release();
+ }
+
+ {
+ ArrayList<BackupFile> ops(c_backupFilePool);
+ BackupFilePtr ptr;
+ while(ops.seize(ptr)){
+ new (ptr.p) BackupFile(* this, c_pagePool);
+ }
+ ops.release();
+ }
+
+ {
+ ArrayList<BackupRecord> recs(c_backupPool);
+ BackupRecordPtr ptr;
+ while(recs.seize(ptr)){
+ new (ptr.p) BackupRecord(* this, c_pagePool, c_tablePool,
+ c_backupFilePool, c_triggerPool);
+ }
+ recs.release();
+ }
+
+ // Initialize BAT for interface to file system
+ {
+ Page32Ptr p;
+ ndbrequire(c_pagePool.seizeId(p, 0));
+ c_startOfPages = (Uint32 *)p.p;
+ c_pagePool.release(p);
+
+ NewVARIABLE* bat = allocateBat(1);
+ bat[0].WA = c_startOfPages;
+ bat[0].nrr = c_pagePool.getSize()*sizeof(Page32)/sizeof(Uint32);
+ }
+
+ // Add received signals
+ addRecSignal(GSN_STTOR, &Backup::execSTTOR);
+ addRecSignal(GSN_DUMP_STATE_ORD, &Backup::execDUMP_STATE_ORD);
+ addRecSignal(GSN_READ_NODESCONF, &Backup::execREAD_NODESCONF);
+ addRecSignal(GSN_NODE_FAILREP, &Backup::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Backup::execINCL_NODEREQ);
+ addRecSignal(GSN_CONTINUEB, &Backup::execCONTINUEB);
+
+ addRecSignal(GSN_SCAN_HBREP, &Backup::execSCAN_HBREP);
+ addRecSignal(GSN_TRANSID_AI, &Backup::execTRANSID_AI);
+ addRecSignal(GSN_SCAN_FRAGREF, &Backup::execSCAN_FRAGREF);
+ addRecSignal(GSN_SCAN_FRAGCONF, &Backup::execSCAN_FRAGCONF);
+
+ addRecSignal(GSN_BACKUP_TRIG_REQ, &Backup::execBACKUP_TRIG_REQ);
+ addRecSignal(GSN_TRIG_ATTRINFO, &Backup::execTRIG_ATTRINFO);
+ addRecSignal(GSN_FIRE_TRIG_ORD, &Backup::execFIRE_TRIG_ORD);
+
+ addRecSignal(GSN_LIST_TABLES_CONF, &Backup::execLIST_TABLES_CONF);
+ addRecSignal(GSN_GET_TABINFOREF, &Backup::execGET_TABINFOREF);
+ addRecSignal(GSN_GET_TABINFO_CONF, &Backup::execGET_TABINFO_CONF);
+
+ addRecSignal(GSN_CREATE_TRIG_REF, &Backup::execCREATE_TRIG_REF);
+ addRecSignal(GSN_CREATE_TRIG_CONF, &Backup::execCREATE_TRIG_CONF);
+
+ addRecSignal(GSN_ALTER_TRIG_REF, &Backup::execALTER_TRIG_REF);
+ addRecSignal(GSN_ALTER_TRIG_CONF, &Backup::execALTER_TRIG_CONF);
+
+ addRecSignal(GSN_DROP_TRIG_REF, &Backup::execDROP_TRIG_REF);
+ addRecSignal(GSN_DROP_TRIG_CONF, &Backup::execDROP_TRIG_CONF);
+
+ addRecSignal(GSN_DI_FCOUNTCONF, &Backup::execDI_FCOUNTCONF);
+ addRecSignal(GSN_DIGETPRIMCONF, &Backup::execDIGETPRIMCONF);
+
+ addRecSignal(GSN_FSOPENREF, &Backup::execFSOPENREF);
+ addRecSignal(GSN_FSOPENCONF, &Backup::execFSOPENCONF);
+
+ addRecSignal(GSN_FSCLOSEREF, &Backup::execFSCLOSEREF);
+ addRecSignal(GSN_FSCLOSECONF, &Backup::execFSCLOSECONF);
+
+ addRecSignal(GSN_FSAPPENDREF, &Backup::execFSAPPENDREF);
+ addRecSignal(GSN_FSAPPENDCONF, &Backup::execFSAPPENDCONF);
+
+ addRecSignal(GSN_FSREMOVEREF, &Backup::execFSREMOVEREF);
+ addRecSignal(GSN_FSREMOVECONF, &Backup::execFSREMOVECONF);
+
+ /*****/
+ addRecSignal(GSN_BACKUP_REQ, &Backup::execBACKUP_REQ);
+ addRecSignal(GSN_ABORT_BACKUP_ORD, &Backup::execABORT_BACKUP_ORD);
+
+ addRecSignal(GSN_DEFINE_BACKUP_REQ, &Backup::execDEFINE_BACKUP_REQ);
+ addRecSignal(GSN_DEFINE_BACKUP_REF, &Backup::execDEFINE_BACKUP_REF);
+ addRecSignal(GSN_DEFINE_BACKUP_CONF, &Backup::execDEFINE_BACKUP_CONF);
+
+ addRecSignal(GSN_START_BACKUP_REQ, &Backup::execSTART_BACKUP_REQ);
+ addRecSignal(GSN_START_BACKUP_REF, &Backup::execSTART_BACKUP_REF);
+ addRecSignal(GSN_START_BACKUP_CONF, &Backup::execSTART_BACKUP_CONF);
+
+ addRecSignal(GSN_BACKUP_FRAGMENT_REQ, &Backup::execBACKUP_FRAGMENT_REQ);
+ //addRecSignal(GSN_BACKUP_FRAGMENT_REF, &Backup::execBACKUP_FRAGMENT_REF);
+ addRecSignal(GSN_BACKUP_FRAGMENT_CONF, &Backup::execBACKUP_FRAGMENT_CONF);
+
+ addRecSignal(GSN_STOP_BACKUP_REQ, &Backup::execSTOP_BACKUP_REQ);
+ addRecSignal(GSN_STOP_BACKUP_REF, &Backup::execSTOP_BACKUP_REF);
+ addRecSignal(GSN_STOP_BACKUP_CONF, &Backup::execSTOP_BACKUP_CONF);
+
+ //addRecSignal(GSN_BACKUP_STATUS_REQ, &Backup::execBACKUP_STATUS_REQ);
+ //addRecSignal(GSN_BACKUP_STATUS_CONF, &Backup::execBACKUP_STATUS_CONF);
+
+ addRecSignal(GSN_UTIL_SEQUENCE_REF, &Backup::execUTIL_SEQUENCE_REF);
+ addRecSignal(GSN_UTIL_SEQUENCE_CONF, &Backup::execUTIL_SEQUENCE_CONF);
+
+ addRecSignal(GSN_WAIT_GCP_REF, &Backup::execWAIT_GCP_REF);
+ addRecSignal(GSN_WAIT_GCP_CONF, &Backup::execWAIT_GCP_CONF);
+
+ /**
+ * Testing
+ */
+ addRecSignal(GSN_BACKUP_REF, &Backup::execBACKUP_REF);
+ addRecSignal(GSN_BACKUP_CONF, &Backup::execBACKUP_CONF);
+ addRecSignal(GSN_BACKUP_ABORT_REP, &Backup::execBACKUP_ABORT_REP);
+ addRecSignal(GSN_BACKUP_COMPLETE_REP, &Backup::execBACKUP_COMPLETE_REP);
+}
+
+Backup::~Backup()
+{
+}
+
+BLOCK_FUNCTIONS(Backup)
+
+template class ArrayPool<Backup::Page32>;
+template class ArrayPool<Backup::Attribute>;
+template class ArrayPool<Backup::Fragment>;
diff --git a/storage/ndb/src/kernel/blocks/backup/FsBuffer.hpp b/storage/ndb/src/kernel/blocks/backup/FsBuffer.hpp
new file mode 100644
index 00000000000..2f3c7daae43
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/FsBuffer.hpp
@@ -0,0 +1,343 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 FS_BUFFER_HPP
+#define FS_BUFFER_HPP
+
+#include <ndb_global.h>
+
+#define DEBUG(x)
+
+/**
+ * A circular data buffer to be used together with the FS
+ *
+ * One writer - Typically your block
+ * getWritePtr()
+ * updateWritePtr()
+ *
+ * One reader - Typically "thread" in your block sending stuff to NDBFS
+ * getReadPtr()
+ * updateReadPtr()
+ */
+class FsBuffer {
+public:
+ /**
+ * Default constructor
+ */
+ FsBuffer();
+
+ /**
+ * setup FsBuffer
+ *
+ * @param Buffer - Ptr to continuous memory
+ * @param Size - Buffer size in 32-bit words
+ * @param BlockSize - Size of block in 32-bit words
+ * @param MinRead - Min read size in 32-bit words
+ * Get rounded(down) to nearest multiple of block size.
+ * @param MaxRead - Max read size in 32-bit words
+ * Get rounded(down) to nearest multiple of block size.
+ * @param MaxWrite - Maximum write (into buffer) in 32-bit words
+ *
+ * @return NULL if everything is OK
+ * else A string describing problem
+ */
+ const char * setup(Uint32 * Buffer,
+ Uint32 Size,
+ Uint32 BlockSize = 128, // 512 bytes
+ Uint32 MinRead = 1024, // 4k
+ Uint32 MaxRead = 1024, // 4k
+ Uint32 MaxWrite = 1024); // 4k
+ /*
+ * @return NULL if everything is OK
+ * else A string describing problem
+ */
+ const char * valid() const;
+
+ Uint32 getBufferSize() const;
+ Uint32 getUsableSize() const;
+ Uint32 * getStart() const;
+
+ /**
+ * getReadPtr - Get pointer and size of data to send to FS
+ *
+ * @param ptr - Where to fetch data
+ * @param sz - How much data in 32-bit words
+ * @param eof - Is this the last fetch (only if return false)
+ *
+ * @return true - If there is data of size >= minread
+ * false - If there is can be data be if it is is < minread
+ * - else eof = true
+ */
+ bool getReadPtr(Uint32 ** ptr, Uint32 * sz, bool * eof);
+
+ /**
+ * @note: sz must be equal to sz returned by getReadPtr
+ */
+ void updateReadPtr(Uint32 sz);
+
+ /**
+ *
+ * @note Must be followed by a updateWritePtr(no of words used)
+ */
+ bool getWritePtr(Uint32 ** ptr, Uint32 sz);
+
+ void updateWritePtr(Uint32 sz);
+
+ /**
+ * There will be no more writing to this buffer
+ */
+ void eof();
+
+ /**
+ * Getters for varibles
+ */
+ Uint32 getMaxWrite() const { return m_maxWrite;}
+ Uint32 getMinRead() const { return m_minRead;}
+
+ Uint32 getFreeSize() const { return m_free; }
+
+
+private:
+
+ Uint32 m_free;
+ Uint32 m_readIndex;
+ Uint32 m_writeIndex;
+ Uint32 m_eof;
+ Uint32 * m_start;
+ Uint32 m_minRead;
+ Uint32 m_maxRead;
+ Uint32 m_maxWrite;
+ Uint32 m_size;
+
+ Uint32 * m_buffer;
+ Uint32 m_bufSize;
+ Uint32 m_blockSize;
+
+ void clear();
+};
+
+inline
+FsBuffer::FsBuffer()
+{
+ clear();
+}
+
+inline
+void
+FsBuffer::clear(){
+ m_minRead = m_maxRead = m_maxWrite = m_size = m_bufSize = m_free = 0;
+ m_buffer = m_start = 0;
+}
+
+static
+Uint32 *
+align(Uint32 * ptr, Uint32 alignment, bool downwards){
+
+ const UintPtr a = (UintPtr)ptr;
+ const UintPtr b = a % alignment;
+
+ if(downwards){
+ return (Uint32 *)(a - b);
+ } else {
+ return (Uint32 *)(a + (b == 0 ? 0 : (alignment - b)));
+ }
+}
+
+inline
+const char *
+FsBuffer::setup(Uint32 * Buffer,
+ Uint32 Size,
+ Uint32 Block,
+ Uint32 MinRead,
+ Uint32 MaxRead,
+ Uint32 MaxWrite)
+{
+ clear();
+ m_buffer = Buffer;
+ m_bufSize = Size;
+ m_blockSize = Block;
+ if(Block == 0){
+ return valid();
+ }
+
+ m_minRead = (MinRead / Block) * Block;
+ m_maxRead = (MaxRead / Block) * Block;
+ m_maxWrite = MaxWrite;
+
+ m_start = align(Buffer, Block*4, false);
+ Uint32 * stop = align(Buffer + Size - MaxWrite, Block*4, true);
+ if(stop > m_start){
+ m_size = stop - m_start;
+ } else {
+ m_size = 0;
+ }
+
+ if(m_minRead == 0)
+ m_size = 0;
+ else
+ m_size = (m_size / m_minRead) * m_minRead;
+
+#if 0
+ ndbout_c("Block = %d MinRead = %d -> %d", Block*4, MinRead*4, m_minRead*4);
+ ndbout_c("Block = %d MaxRead = %d -> %d", Block*4, MaxRead*4, m_maxRead*4);
+
+ ndbout_c("Buffer = %d -> %d", Buffer, m_start);
+ ndbout_c("Buffer = %d Size = %d MaxWrite = %d -> %d",
+ Buffer, Size*4, MaxWrite*4, m_size*4);
+#endif
+
+ m_readIndex = m_writeIndex = m_eof = 0;
+ m_free = m_size;
+ return valid();
+}
+
+inline
+const char *
+FsBuffer::valid() const {
+ if(m_buffer == 0) return "Null pointer buffer";
+ if(m_bufSize == 0) return "Zero size buffer";
+ if(m_blockSize == 0) return "Zero block size";
+ if(m_minRead < m_blockSize) return "Min read less than block size";
+ if(m_maxRead < m_blockSize) return "Max read less than block size";
+ if(m_maxRead < m_minRead) return "Max read less than min read";
+ if(m_size == 0) return "Zero usable space";
+ return 0;
+}
+
+inline
+Uint32
+FsBuffer::getBufferSize() const {
+ return m_bufSize;
+}
+
+inline
+Uint32
+FsBuffer::getUsableSize() const {
+ return m_size;
+}
+
+inline
+Uint32 *
+FsBuffer::getStart() const {
+ return m_start;
+}
+
+inline
+bool
+FsBuffer::getReadPtr(Uint32 ** ptr, Uint32 * sz, bool * _eof){
+
+ Uint32 * Tp = m_start;
+ const Uint32 Tr = m_readIndex;
+ const Uint32 Tm = m_minRead;
+ const Uint32 Ts = m_size;
+ const Uint32 Tmw = m_maxRead;
+
+ Uint32 sz1 = m_size - m_free; // Used
+
+ if(sz1 >= Tm){
+ if(Tr + sz1 > Ts)
+ sz1 = (Ts - Tr);
+
+ if(sz1 > Tmw)
+ * sz = Tmw;
+ else
+ * sz = sz1 - (sz1 % Tm);
+
+ * ptr = &Tp[Tr];
+
+ DEBUG(ndbout_c("getReadPtr() Tr: %d Tw: %d Ts: %d Tm: %d sz1: %d -> %d",
+ Tr, Tw, Ts, Tm, sz1, * sz));
+
+ return true;
+ }
+
+ if(!m_eof){
+ * _eof = false;
+
+ DEBUG(ndbout_c("getReadPtr() Tr: %d Tw: %d Ts: %d Tm: %d sz1: %d -> false",
+ Tr, Tw, Ts, Tm, sz1));
+
+ return false;
+ }
+
+ * sz = sz1;
+ * _eof = true;
+ * ptr = &Tp[Tr];
+
+ DEBUG(ndbout_c("getReadPtr() Tr: %d Tw: %d Ts: %d Tm: %d sz1: %d -> %d eof",
+ Tr, Tw, Ts, Tm, sz1, * sz));
+
+ return false;
+}
+
+inline
+void
+FsBuffer::updateReadPtr(Uint32 sz){
+ const Uint32 Tr = m_readIndex;
+ const Uint32 Ts = m_size;
+
+ m_free += sz;
+ m_readIndex = (Tr + sz) % Ts;
+}
+
+inline
+bool
+FsBuffer::getWritePtr(Uint32 ** ptr, Uint32 sz){
+ assert(sz <= m_maxWrite);
+ Uint32 * Tp = m_start;
+ const Uint32 Tw = m_writeIndex;
+ const Uint32 sz1 = m_free;
+
+ if(sz1 > sz){ // Note at least 1 word of slack
+ * ptr = &Tp[Tw];
+
+ DEBUG(ndbout_c("getWritePtr(%d) Tr: %d Tw: %d Ts: %d sz1: %d -> true",
+ sz, Tr, Tw, Ts, sz1));
+ return true;
+ }
+
+ DEBUG(ndbout_c("getWritePtr(%d) Tr: %d Tw: %d Ts: %d sz1: %d -> false",
+ sz, Tr, Tw, Ts, sz1));
+
+ return false;
+}
+
+inline
+void
+FsBuffer::updateWritePtr(Uint32 sz){
+ assert(sz <= m_maxWrite);
+ Uint32 * Tp = m_start;
+ const Uint32 Tw = m_writeIndex;
+ const Uint32 Ts = m_size;
+
+ const Uint32 Tnew = (Tw + sz);
+ m_free -= sz;
+ if(Tnew < Ts){
+ m_writeIndex = Tnew;
+ return;
+ }
+
+ memcpy(Tp, &Tp[Ts], (Tnew - Ts) << 2);
+ m_writeIndex = Tnew - Ts;
+}
+
+inline
+void
+FsBuffer::eof(){
+ m_eof = 1;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/backup/Makefile.am b/storage/ndb/src/kernel/blocks/backup/Makefile.am
new file mode 100644
index 00000000000..c8f44f31292
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/Makefile.am
@@ -0,0 +1,24 @@
+
+noinst_LIBRARIES = libbackup.a
+
+libbackup_a_SOURCES = Backup.cpp BackupInit.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libbackup.dsp
+
+libbackup.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libbackup_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/backup/read.cpp b/storage/ndb/src/kernel/blocks/backup/read.cpp
new file mode 100644
index 00000000000..89cc08ee9de
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/backup/read.cpp
@@ -0,0 +1,478 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbTCP.h>
+#include <NdbOut.hpp>
+#include "BackupFormat.hpp"
+#include <AttributeHeader.hpp>
+#include <SimpleProperties.hpp>
+
+bool readHeader(FILE*, BackupFormat::FileHeader *);
+bool readFragHeader(FILE*, BackupFormat::DataFile::FragmentHeader *);
+bool readFragFooter(FILE*, BackupFormat::DataFile::FragmentFooter *);
+Int32 readRecord(FILE*, Uint32 **);
+
+NdbOut & operator<<(NdbOut&, const BackupFormat::FileHeader &);
+NdbOut & operator<<(NdbOut&, const BackupFormat::DataFile::FragmentHeader &);
+NdbOut & operator<<(NdbOut&, const BackupFormat::DataFile::FragmentFooter &);
+
+bool readTableList(FILE*, BackupFormat::CtlFile::TableList **);
+bool readTableDesc(FILE*, BackupFormat::CtlFile::TableDescription **);
+bool readGCPEntry(FILE*, BackupFormat::CtlFile::GCPEntry **);
+
+NdbOut & operator<<(NdbOut&, const BackupFormat::CtlFile::TableList &);
+NdbOut & operator<<(NdbOut&, const BackupFormat::CtlFile::TableDescription &);
+NdbOut & operator<<(NdbOut&, const BackupFormat::CtlFile::GCPEntry &);
+
+Int32 readLogEntry(FILE*, Uint32**);
+
+static Uint32 recNo;
+static Uint32 logEntryNo;
+
+int
+main(int argc, const char * argv[]){
+
+ ndb_init();
+ if(argc <= 1){
+ printf("Usage: %s <filename>", argv[0]);
+ exit(1);
+ }
+ FILE * f = fopen(argv[1], "rb");
+ if(!f){
+ ndbout << "No such file!" << endl;
+ exit(1);
+ }
+
+ BackupFormat::FileHeader fileHeader;
+ if(!readHeader(f, &fileHeader)){
+ ndbout << "Invalid file!" << endl;
+ exit(1);
+ }
+ ndbout << fileHeader << endl;
+
+ switch(fileHeader.FileType){
+ case BackupFormat::DATA_FILE:
+ while(!feof(f)){
+ BackupFormat::DataFile::FragmentHeader fragHeader;
+ if(!readFragHeader(f, &fragHeader))
+ break;
+ ndbout << fragHeader << endl;
+
+ Uint32 len, * data;
+ while((len = readRecord(f, &data)) > 0){
+#if 0
+ ndbout << "-> " << hex;
+ for(Uint32 i = 0; i<len; i++){
+ ndbout << data[i] << " ";
+ }
+ ndbout << endl;
+#endif
+ }
+
+ BackupFormat::DataFile::FragmentFooter fragFooter;
+ if(!readFragFooter(f, &fragFooter))
+ break;
+ ndbout << fragFooter << endl;
+ }
+ break;
+ case BackupFormat::CTL_FILE:{
+ BackupFormat::CtlFile::TableList * tabList;
+ if(!readTableList(f, &tabList)){
+ ndbout << "Invalid file! No table list" << endl;
+ break;
+ }
+ ndbout << (* tabList) << endl;
+
+ const Uint32 noOfTables = tabList->SectionLength - 2;
+ for(Uint32 i = 0; i<noOfTables; i++){
+ BackupFormat::CtlFile::TableDescription * tabDesc;
+ if(!readTableDesc(f, &tabDesc)){
+ ndbout << "Invalid file missing table description" << endl;
+ break;
+ }
+ ndbout << (* tabDesc) << endl;
+ }
+
+ BackupFormat::CtlFile::GCPEntry * gcpE;
+ if(!readGCPEntry(f, &gcpE)){
+ ndbout << "Invalid file! GCP ENtry" << endl;
+ break;
+ }
+ ndbout << (* gcpE) << endl;
+
+ break;
+ }
+ case BackupFormat::LOG_FILE:{
+ logEntryNo = 0;
+
+ typedef BackupFormat::LogFile::LogEntry LogEntry;
+
+ Uint32 len, * data;
+ while((len = readLogEntry(f, &data)) > 0){
+ LogEntry * logEntry = (LogEntry *) data;
+ /**
+ * Log Entry
+ */
+ Uint32 event = ntohl(logEntry->TriggerEvent);
+ bool gcp = (event & 0x10000) != 0;
+ event &= 0xFFFF;
+ if(gcp)
+ len --;
+
+ ndbout << "LogEntry Table: " << (Uint32)ntohl(logEntry->TableId)
+ << " Event: " << event
+ << " Length: " << (len - 2);
+
+ const Uint32 dataLen = len - 2;
+#if 0
+ Uint32 pos = 0;
+ while(pos < dataLen){
+ AttributeHeader * ah = (AttributeHeader*)&logEntry->Data[pos];
+ ndbout_c(" Attribut: %d Size: %d",
+ ah->getAttributeId(),
+ ah->getDataSize());
+ pos += ah->getDataSize() + 1;
+ }
+#endif
+ if(gcp)
+ ndbout << " GCP: " << (Uint32)ntohl(logEntry->Data[dataLen]);
+ ndbout << endl;
+ }
+ break;
+ }
+ default:
+ ndbout << "Unsupported file type for printer: "
+ << fileHeader.FileType << endl;
+ break;
+ }
+ fclose(f);
+ return 0;
+}
+
+#define RETURN_FALSE() { ndbout_c("false: %d", __LINE__); abort(); return false; }
+
+static bool endian = false;
+
+bool
+readHeader(FILE* f, BackupFormat::FileHeader * dst){
+ if(fread(dst, 4, 3, f) != 3)
+ RETURN_FALSE();
+
+ if(memcmp(dst->Magic, BACKUP_MAGIC, sizeof(BACKUP_MAGIC)) != 0)
+ RETURN_FALSE();
+
+ dst->NdbVersion = ntohl(dst->NdbVersion);
+ if(dst->NdbVersion != 210)
+ RETURN_FALSE();
+
+ if(fread(&dst->SectionType, 4, 2, f) != 2)
+ RETURN_FALSE();
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+
+ if(dst->SectionType != BackupFormat::FILE_HEADER)
+ RETURN_FALSE();
+
+ if(dst->SectionLength != ((sizeof(BackupFormat::FileHeader) - 12) >> 2))
+ RETURN_FALSE();
+
+ if(fread(&dst->FileType, 4, dst->SectionLength - 2, f) !=
+ (dst->SectionLength - 2))
+ RETURN_FALSE();
+
+ dst->FileType = ntohl(dst->FileType);
+ dst->BackupId = ntohl(dst->BackupId);
+ dst->BackupKey_0 = ntohl(dst->BackupKey_0);
+ dst->BackupKey_1 = ntohl(dst->BackupKey_1);
+
+ if(dst->FileType < BackupFormat::CTL_FILE ||
+ dst->FileType > BackupFormat::DATA_FILE)
+ RETURN_FALSE();
+
+ if(dst->ByteOrder != 0x12345678)
+ endian = true;
+
+ return true;
+}
+
+bool
+readFragHeader(FILE* f, BackupFormat::DataFile::FragmentHeader * dst){
+ if(fread(dst, 1, sizeof(* dst), f) != sizeof(* dst))
+ return false;
+
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+ dst->TableId = ntohl(dst->TableId);
+ dst->FragmentNo = ntohl(dst->FragmentNo);
+ dst->ChecksumType = ntohl(dst->ChecksumType);
+
+ if(dst->SectionLength != (sizeof(* dst) >> 2))
+ RETURN_FALSE();
+
+ if(dst->SectionType != BackupFormat::FRAGMENT_HEADER)
+ RETURN_FALSE();
+
+ recNo = 0;
+
+ return true;
+}
+
+bool
+readFragFooter(FILE* f, BackupFormat::DataFile::FragmentFooter * dst){
+ if(fread(dst, 1, sizeof(* dst), f) != sizeof(* dst))
+ RETURN_FALSE();
+
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+ dst->TableId = ntohl(dst->TableId);
+ dst->FragmentNo = ntohl(dst->FragmentNo);
+ dst->NoOfRecords = ntohl(dst->NoOfRecords);
+ dst->Checksum = ntohl(dst->Checksum);
+
+ if(dst->SectionLength != (sizeof(* dst) >> 2))
+ RETURN_FALSE();
+
+ if(dst->SectionType != BackupFormat::FRAGMENT_FOOTER)
+ RETURN_FALSE();
+ return true;
+}
+
+static Uint32 buf[8192];
+
+Int32
+readRecord(FILE* f, Uint32 **dst){
+ Uint32 len;
+ if(fread(&len, 1, 4, f) != 4)
+ RETURN_FALSE();
+
+ len = ntohl(len);
+
+ if(fread(buf, 4, len, f) != len)
+ return -1;
+
+ if(len > 0)
+ recNo++;
+
+ * dst = &buf[0];
+
+ return len;
+}
+
+Int32
+readLogEntry(FILE* f, Uint32 **dst){
+ Uint32 len;
+ if(fread(&len, 1, 4, f) != 4)
+ RETURN_FALSE();
+
+ len = ntohl(len);
+
+ if(fread(&buf[1], 4, len, f) != len)
+ return -1;
+
+ buf[0] = len;
+
+ if(len > 0)
+ logEntryNo++;
+
+ * dst = &buf[0];
+
+ return len;
+}
+
+
+NdbOut &
+operator<<(NdbOut& ndbout, const BackupFormat::FileHeader & hf){
+
+ char buf[9];
+ memcpy(buf, hf.Magic, sizeof(hf.Magic));
+ buf[8] = 0;
+
+ ndbout << "-- FileHeader:" << endl;
+ ndbout << "Magic: " << buf << endl;
+ ndbout << "NdbVersion: " << hf.NdbVersion << endl;
+ ndbout << "SectionType: " << hf.SectionType << endl;
+ ndbout << "SectionLength: " << hf.SectionLength << endl;
+ ndbout << "FileType: " << hf.FileType << endl;
+ ndbout << "BackupId: " << hf.BackupId << endl;
+ ndbout << "BackupKey: [ " << hex << hf.BackupKey_0
+ << " "<< hf.BackupKey_1 << " ]" << endl;
+ ndbout << "ByteOrder: " << hex << hf.ByteOrder << endl;
+ return ndbout;
+}
+
+NdbOut & operator<<(NdbOut& ndbout,
+ const BackupFormat::DataFile::FragmentHeader & hf){
+
+ ndbout << "-- Fragment header:" << endl;
+ ndbout << "SectionType: " << hf.SectionType << endl;
+ ndbout << "SectionLength: " << hf.SectionLength << endl;
+ ndbout << "TableId: " << hf.TableId << endl;
+ ndbout << "FragmentNo: " << hf.FragmentNo << endl;
+ ndbout << "ChecksumType: " << hf.ChecksumType << endl;
+
+ return ndbout;
+}
+NdbOut & operator<<(NdbOut& ndbout,
+ const BackupFormat::DataFile::FragmentFooter & hf){
+
+ ndbout << "-- Fragment footer:" << endl;
+ ndbout << "SectionType: " << hf.SectionType << endl;
+ ndbout << "SectionLength: " << hf.SectionLength << endl;
+ ndbout << "TableId: " << hf.TableId << endl;
+ ndbout << "FragmentNo: " << hf.FragmentNo << endl;
+ ndbout << "NoOfRecords: " << hf.NoOfRecords << endl;
+ ndbout << "Checksum: " << hf.Checksum << endl;
+
+ return ndbout;
+}
+
+bool
+readTableList(FILE* f, BackupFormat::CtlFile::TableList **ret){
+ BackupFormat::CtlFile::TableList * dst =
+ (BackupFormat::CtlFile::TableList *)&buf[0];
+
+ if(fread(dst, 4, 2, f) != 2)
+ RETURN_FALSE();
+
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+
+ if(dst->SectionType != BackupFormat::TABLE_LIST)
+ RETURN_FALSE();
+
+ const Uint32 len = dst->SectionLength - 2;
+ if(fread(&dst->TableIds[0], 4, len, f) != len)
+ RETURN_FALSE();
+
+ for(Uint32 i = 0; i<len; i++){
+ dst->TableIds[i] = ntohl(dst->TableIds[i]);
+ }
+
+ * ret = dst;
+
+ return true;
+}
+
+bool
+readTableDesc(FILE* f, BackupFormat::CtlFile::TableDescription **ret){
+ BackupFormat::CtlFile::TableDescription * dst =
+ (BackupFormat::CtlFile::TableDescription *)&buf[0];
+
+ if(fread(dst, 4, 2, f) != 2)
+ RETURN_FALSE();
+
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+
+ if(dst->SectionType != BackupFormat::TABLE_DESCRIPTION)
+ RETURN_FALSE();
+
+ const Uint32 len = dst->SectionLength - 2;
+ if(fread(&dst->DictTabInfo[0], 4, len, f) != len)
+ RETURN_FALSE();
+
+ * ret = dst;
+
+ return true;
+}
+
+bool
+readGCPEntry(FILE* f, BackupFormat::CtlFile::GCPEntry **ret){
+ BackupFormat::CtlFile::GCPEntry * dst =
+ (BackupFormat::CtlFile::GCPEntry *)&buf[0];
+
+ if(fread(dst, 4, 4, f) != 4)
+ RETURN_FALSE();
+
+ dst->SectionType = ntohl(dst->SectionType);
+ dst->SectionLength = ntohl(dst->SectionLength);
+
+ if(dst->SectionType != BackupFormat::GCP_ENTRY)
+ RETURN_FALSE();
+
+ dst->StartGCP = ntohl(dst->StartGCP);
+ dst->StopGCP = ntohl(dst->StopGCP);
+
+ * ret = dst;
+
+ return true;
+}
+
+
+NdbOut &
+operator<<(NdbOut& ndbout, const BackupFormat::CtlFile::TableList & hf) {
+ ndbout << "-- Table List:" << endl;
+ ndbout << "SectionType: " << hf.SectionType << endl;
+ ndbout << "SectionLength: " << hf.SectionLength << endl;
+ for(Uint32 i = 0; i < hf.SectionLength - 2; i++){
+ ndbout << hf.TableIds[i] << " ";
+ if((i + 1) % 16 == 0)
+ ndbout << endl;
+ }
+ return ndbout;
+}
+
+NdbOut &
+operator<<(NdbOut& ndbout, const BackupFormat::CtlFile::TableDescription & hf){
+ ndbout << "-- Table Description:" << endl;
+ ndbout << "SectionType: " << hf.SectionType << endl;
+ ndbout << "SectionLength: " << hf.SectionLength << endl;
+
+ SimplePropertiesLinearReader it(&hf.DictTabInfo[0], hf.SectionLength - 2);
+ char buf[1024];
+ for(it.first(); it.valid(); it.next()){
+ switch(it.getValueType()){
+ case SimpleProperties::Uint32Value:
+ ndbout << "Key: " << it.getKey()
+ << " value(" << it.getValueLen() << ") : "
+ << it.getUint32() << endl;
+ break;
+ case SimpleProperties::StringValue:
+ if(it.getValueLen() < sizeof(buf)){
+ it.getString(buf);
+ ndbout << "Key: " << it.getKey()
+ << " value(" << it.getValueLen() << ") : "
+ << "\"" << buf << "\"" << endl;
+ } else {
+ ndbout << "Key: " << it.getKey()
+ << " value(" << it.getValueLen() << ") : "
+ << "\"" << "<TOO LONG>" << "\"" << endl;
+
+ }
+ break;
+ default:
+ ndbout << "Unknown type for key: " << it.getKey()
+ << " type: " << it.getValueType() << endl;
+ }
+ }
+
+ return ndbout;
+}
+
+NdbOut &
+operator<<(NdbOut& ndbout, const BackupFormat::CtlFile::GCPEntry & hf) {
+ ndbout << "-- GCP Entry:" << endl;
+ ndbout << "SectionType: " << hf.SectionType << endl;
+ ndbout << "SectionLength: " << hf.SectionLength << endl;
+ ndbout << "Start GCP: " << hf.StartGCP << endl;
+ ndbout << "Stop GCP: " << hf.StopGCP << endl;
+
+ return ndbout;
+}
+
diff --git a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
new file mode 100644
index 00000000000..9001491dd64
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
@@ -0,0 +1,1393 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Cmvmi.hpp"
+
+#include <Configuration.hpp>
+#include <kernel_types.h>
+#include <TransporterRegistry.hpp>
+#include <NdbOut.hpp>
+#include <NdbMem.h>
+
+#include <SignalLoggerManager.hpp>
+#include <FastScheduler.hpp>
+
+#define DEBUG(x) { ndbout << "CMVMI::" << x << endl; }
+
+#include <signaldata/TestOrd.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/TamperOrd.hpp>
+#include <signaldata/StartOrd.hpp>
+#include <signaldata/CloseComReqConf.hpp>
+#include <signaldata/SetLogLevelOrd.hpp>
+#include <signaldata/EventSubscribeReq.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/DisconnectRep.hpp>
+
+#include <EventLogger.hpp>
+#include <TimeQueue.hpp>
+
+#include <NdbSleep.h>
+#include <SafeCounter.hpp>
+
+// Used here only to print event reports on stdout/console.
+EventLogger g_eventLogger;
+extern int simulate_error_during_shutdown;
+
+Cmvmi::Cmvmi(const Configuration & conf) :
+ SimulatedBlock(CMVMI, conf)
+ ,theConfig((Configuration&)conf)
+ ,subscribers(subscriberPool)
+{
+ BLOCK_CONSTRUCTOR(Cmvmi);
+
+ Uint32 long_sig_buffer_size;
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_LONG_SIGNAL_BUFFER,
+ &long_sig_buffer_size);
+
+ long_sig_buffer_size= long_sig_buffer_size / 256;
+ g_sectionSegmentPool.setSize(long_sig_buffer_size);
+
+ // Add received signals
+ addRecSignal(GSN_CONNECT_REP, &Cmvmi::execCONNECT_REP);
+ addRecSignal(GSN_DISCONNECT_REP, &Cmvmi::execDISCONNECT_REP);
+
+ addRecSignal(GSN_NDB_TAMPER, &Cmvmi::execNDB_TAMPER, true);
+ addRecSignal(GSN_SET_LOGLEVELORD, &Cmvmi::execSET_LOGLEVELORD);
+ addRecSignal(GSN_EVENT_REP, &Cmvmi::execEVENT_REP);
+ addRecSignal(GSN_STTOR, &Cmvmi::execSTTOR);
+ addRecSignal(GSN_CLOSE_COMREQ, &Cmvmi::execCLOSE_COMREQ);
+ addRecSignal(GSN_ENABLE_COMORD, &Cmvmi::execENABLE_COMORD);
+ addRecSignal(GSN_OPEN_COMREQ, &Cmvmi::execOPEN_COMREQ);
+ addRecSignal(GSN_TEST_ORD, &Cmvmi::execTEST_ORD);
+
+ addRecSignal(GSN_STATISTICS_REQ, &Cmvmi::execSTATISTICS_REQ);
+ addRecSignal(GSN_TAMPER_ORD, &Cmvmi::execTAMPER_ORD);
+ addRecSignal(GSN_SET_VAR_REQ, &Cmvmi::execSET_VAR_REQ);
+ addRecSignal(GSN_SET_VAR_CONF, &Cmvmi::execSET_VAR_CONF);
+ addRecSignal(GSN_SET_VAR_REF, &Cmvmi::execSET_VAR_REF);
+ addRecSignal(GSN_STOP_ORD, &Cmvmi::execSTOP_ORD);
+ addRecSignal(GSN_START_ORD, &Cmvmi::execSTART_ORD);
+ addRecSignal(GSN_EVENT_SUBSCRIBE_REQ,
+ &Cmvmi::execEVENT_SUBSCRIBE_REQ);
+
+ addRecSignal(GSN_DUMP_STATE_ORD, &Cmvmi::execDUMP_STATE_ORD);
+
+ addRecSignal(GSN_TESTSIG, &Cmvmi::execTESTSIG);
+
+ subscriberPool.setSize(5);
+
+ const ndb_mgm_configuration_iterator * db = theConfig.getOwnConfigIterator();
+ for(unsigned j = 0; j<LogLevel::LOGLEVEL_CATEGORIES; j++){
+ Uint32 logLevel;
+ if(!ndb_mgm_get_int_parameter(db, CFG_MIN_LOGLEVEL+j, &logLevel)){
+ clogLevel.setLogLevel((LogLevel::EventCategory)j,
+ logLevel);
+ }
+ }
+
+ ndb_mgm_configuration_iterator * iter = theConfig.getClusterConfigIterator();
+ for(ndb_mgm_first(iter); ndb_mgm_valid(iter); ndb_mgm_next(iter)){
+ jam();
+ Uint32 nodeId;
+ Uint32 nodeType;
+
+ ndbrequire(!ndb_mgm_get_int_parameter(iter,CFG_NODE_ID, &nodeId));
+ ndbrequire(!ndb_mgm_get_int_parameter(iter,CFG_TYPE_OF_SECTION,&nodeType));
+
+ switch(nodeType){
+ case NodeInfo::DB:
+ c_dbNodes.set(nodeId);
+ break;
+ case NodeInfo::API:
+ case NodeInfo::MGM:
+ case NodeInfo::REP:
+ break;
+ default:
+ ndbrequire(false);
+ }
+ setNodeInfo(nodeId).m_type = nodeType;
+ }
+
+ setNodeInfo(getOwnNodeId()).m_connected = true;
+}
+
+Cmvmi::~Cmvmi()
+{
+}
+
+
+void Cmvmi::execNDB_TAMPER(Signal* signal)
+{
+ jamEntry();
+ SET_ERROR_INSERT_VALUE(signal->theData[0]);
+ if(ERROR_INSERTED(9999)){
+ CRASH_INSERTION(9999);
+ }
+
+ if(ERROR_INSERTED(9998)){
+ while(true) NdbSleep_SecSleep(1);
+ }
+
+ if(ERROR_INSERTED(9997)){
+ ndbrequire(false);
+ }
+
+#ifndef NDB_WIN32
+ if(ERROR_INSERTED(9996)){
+ simulate_error_during_shutdown= SIGSEGV;
+ ndbrequire(false);
+ }
+
+ if(ERROR_INSERTED(9995)){
+ simulate_error_during_shutdown= SIGSEGV;
+ kill(getpid(), SIGABRT);
+ }
+#endif
+}//execNDB_TAMPER()
+
+void Cmvmi::execSET_LOGLEVELORD(Signal* signal)
+{
+ SetLogLevelOrd * const llOrd = (SetLogLevelOrd *)&signal->theData[0];
+ LogLevel::EventCategory category;
+ Uint32 level;
+ jamEntry();
+
+ for(unsigned int i = 0; i<llOrd->noOfEntries; i++){
+ category = (LogLevel::EventCategory)(llOrd->theData[i] >> 16);
+ level = llOrd->theData[i] & 0xFFFF;
+
+ clogLevel.setLogLevel(category, level);
+ }
+}//execSET_LOGLEVELORD()
+
+void Cmvmi::execEVENT_REP(Signal* signal)
+{
+ //-----------------------------------------------------------------------
+ // This message is sent to report any types of events in NDB.
+ // Based on the log level they will be either ignored or
+ // reported. Currently they are printed, but they will be
+ // transferred to the management server for further distribution
+ // to the graphical management interface.
+ //-----------------------------------------------------------------------
+ EventReport * const eventReport = (EventReport *)&signal->theData[0];
+ Ndb_logevent_type eventType = eventReport->getEventType();
+
+ jamEntry();
+
+ /**
+ * If entry is not found
+ */
+ Uint32 threshold;
+ LogLevel::EventCategory eventCategory;
+ Logger::LoggerLevel severity;
+ EventLoggerBase::EventTextFunction textF;
+ if (EventLoggerBase::event_lookup(eventType,eventCategory,threshold,severity,textF))
+ return;
+
+ SubscriberPtr ptr;
+ for(subscribers.first(ptr); ptr.i != RNIL; subscribers.next(ptr)){
+ if(ptr.p->logLevel.getLogLevel(eventCategory) < threshold){
+ continue;
+ }
+
+ sendSignal(ptr.p->blockRef, GSN_EVENT_REP, signal, signal->length(), JBB);
+ }
+
+ if(clogLevel.getLogLevel(eventCategory) < threshold){
+ return;
+ }
+
+ // Print the event info
+ g_eventLogger.log(eventReport->getEventType(), signal->theData);
+
+ return;
+}//execEVENT_REP()
+
+void
+Cmvmi::execEVENT_SUBSCRIBE_REQ(Signal * signal){
+ EventSubscribeReq * subReq = (EventSubscribeReq *)&signal->theData[0];
+ SubscriberPtr ptr;
+ jamEntry();
+ DBUG_ENTER("Cmvmi::execEVENT_SUBSCRIBE_REQ");
+
+ /**
+ * Search for subcription
+ */
+ for(subscribers.first(ptr); ptr.i != RNIL; subscribers.next(ptr)){
+ if(ptr.p->blockRef == subReq->blockRef)
+ break;
+ }
+
+ if(ptr.i == RNIL){
+ /**
+ * Create a new one
+ */
+ if(subscribers.seize(ptr) == false){
+ sendSignal(subReq->blockRef, GSN_EVENT_SUBSCRIBE_REF, signal, 1, JBB);
+ return;
+ }
+ ptr.p->logLevel.clear();
+ ptr.p->blockRef = subReq->blockRef;
+ }
+
+ if(subReq->noOfEntries == 0){
+ /**
+ * Cancel subscription
+ */
+ subscribers.release(ptr.i);
+ } else {
+ /**
+ * Update subscription
+ */
+ LogLevel::EventCategory category;
+ Uint32 level = 0;
+ for(Uint32 i = 0; i<subReq->noOfEntries; i++){
+ category = (LogLevel::EventCategory)(subReq->theData[i] >> 16);
+ level = subReq->theData[i] & 0xFFFF;
+ ptr.p->logLevel.setLogLevel(category, level);
+ DBUG_PRINT("info",("entry %d: level=%d, category= %d", i, level, category));
+ }
+ }
+
+ signal->theData[0] = ptr.i;
+ sendSignal(ptr.p->blockRef, GSN_EVENT_SUBSCRIBE_CONF, signal, 1, JBB);
+ DBUG_VOID_RETURN;
+}
+
+void
+Cmvmi::cancelSubscription(NodeId nodeId){
+
+ SubscriberPtr ptr;
+ subscribers.first(ptr);
+
+ while(ptr.i != RNIL){
+ Uint32 i = ptr.i;
+ BlockReference blockRef = ptr.p->blockRef;
+
+ subscribers.next(ptr);
+
+ if(refToNode(blockRef) == nodeId){
+ subscribers.release(i);
+ }
+ }
+}
+
+void Cmvmi::sendSTTORRY(Signal* signal)
+{
+ jam();
+ signal->theData[3] = 1;
+ signal->theData[4] = 3;
+ signal->theData[5] = 8;
+ signal->theData[6] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 7, JBB);
+}//Cmvmi::sendSTTORRY
+
+
+void Cmvmi::execSTTOR(Signal* signal)
+{
+ Uint32 theStartPhase = signal->theData[1];
+
+ jamEntry();
+ if (theStartPhase == 1){
+ jam();
+ sendSTTORRY(signal);
+ return;
+ } else if (theStartPhase == 3) {
+ jam();
+ globalData.activateSendPacked = 1;
+ sendSTTORRY(signal);
+ } else if (theStartPhase == 8){
+ /*---------------------------------------------------*/
+ /* Open com to API + REP nodes */
+ /*---------------------------------------------------*/
+ signal->theData[0] = 0; // no answer
+ signal->theData[1] = 0; // no id
+ signal->theData[2] = NodeInfo::API;
+ execOPEN_COMREQ(signal);
+ signal->theData[0] = 0; // no answer
+ signal->theData[1] = 0; // no id
+ signal->theData[2] = NodeInfo::REP;
+ execOPEN_COMREQ(signal);
+ globalData.theStartLevel = NodeState::SL_STARTED;
+ sendSTTORRY(signal);
+ } else {
+ jam();
+
+ if(theConfig.lockPagesInMainMemory()){
+ int res = NdbMem_MemLockAll();
+ if(res != 0){
+ g_eventLogger.warning("Failed to memlock pages");
+ warningEvent("Failed to memlock pages");
+ }
+ }
+
+ sendSTTORRY(signal);
+ }
+}
+
+void Cmvmi::execCLOSE_COMREQ(Signal* signal)
+{
+ // Close communication with the node and halt input/output from
+ // other blocks than QMGR
+
+ CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
+
+ const BlockReference userRef = closeCom->xxxBlockRef;
+ Uint32 failNo = closeCom->failNo;
+// Uint32 noOfNodes = closeCom->noOfNodes;
+
+ jamEntry();
+ for (unsigned i = 0; i < MAX_NODES; i++){
+ if(NodeBitmask::get(closeCom->theNodes, i)){
+
+ jam();
+
+ //-----------------------------------------------------
+ // Report that the connection to the node is closed
+ //-----------------------------------------------------
+ signal->theData[0] = NDB_LE_CommunicationClosed;
+ signal->theData[1] = i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ globalTransporterRegistry.setIOState(i, HaltIO);
+ globalTransporterRegistry.do_disconnect(i);
+ }
+ }
+ if (failNo != 0) {
+ jam();
+ signal->theData[0] = userRef;
+ signal->theData[1] = failNo;
+ sendSignal(QMGR_REF, GSN_CLOSE_COMCONF, signal, 19, JBA);
+ }
+}
+
+void Cmvmi::execOPEN_COMREQ(Signal* signal)
+{
+ // Connect to the specifed NDB node, only QMGR allowed communication
+ // so far with the node
+
+ const BlockReference userRef = signal->theData[0];
+ Uint32 tStartingNode = signal->theData[1];
+ Uint32 tData2 = signal->theData[2];
+ jamEntry();
+
+ const Uint32 len = signal->getLength();
+ if(len == 2){
+ globalTransporterRegistry.do_connect(tStartingNode);
+ globalTransporterRegistry.setIOState(tStartingNode, HaltIO);
+
+ //-----------------------------------------------------
+ // Report that the connection to the node is opened
+ //-----------------------------------------------------
+ signal->theData[0] = NDB_LE_CommunicationOpened;
+ signal->theData[1] = tStartingNode;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+ //-----------------------------------------------------
+ } else {
+ for(unsigned int i = 1; i < MAX_NODES; i++ ) {
+ jam();
+ if (i != getOwnNodeId() && getNodeInfo(i).m_type == tData2){
+ jam();
+ globalTransporterRegistry.do_connect(i);
+ globalTransporterRegistry.setIOState(i, HaltIO);
+
+ signal->theData[0] = NDB_LE_CommunicationOpened;
+ signal->theData[1] = i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+ }
+ }
+ }
+
+ if (userRef != 0) {
+ jam();
+ signal->theData[0] = tStartingNode;
+ signal->theData[1] = tData2;
+ sendSignal(userRef, GSN_OPEN_COMCONF, signal, len - 1,JBA);
+ }
+}
+
+void Cmvmi::execENABLE_COMORD(Signal* signal)
+{
+ // Enable communication with all our NDB blocks to this node
+
+ Uint32 tStartingNode = signal->theData[0];
+ globalTransporterRegistry.setIOState(tStartingNode, NoHalt);
+ setNodeInfo(tStartingNode).m_connected = true;
+ //-----------------------------------------------------
+ // Report that the version of the node
+ //-----------------------------------------------------
+ signal->theData[0] = NDB_LE_ConnectedApiVersion;
+ signal->theData[1] = tStartingNode;
+ signal->theData[2] = getNodeInfo(tStartingNode).m_version;
+
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+ //-----------------------------------------------------
+
+ jamEntry();
+}
+
+void Cmvmi::execDISCONNECT_REP(Signal *signal)
+{
+ const DisconnectRep * const rep = (DisconnectRep *)&signal->theData[0];
+ const Uint32 hostId = rep->nodeId;
+ const Uint32 errNo = rep->err;
+
+ jamEntry();
+
+ setNodeInfo(hostId).m_connected = false;
+ setNodeInfo(hostId).m_connectCount++;
+ const NodeInfo::NodeType type = getNodeInfo(hostId).getType();
+ ndbrequire(type != NodeInfo::INVALID);
+
+ if(type == NodeInfo::DB || globalData.theStartLevel == NodeState::SL_STARTED){
+ jam();
+ DisconnectRep * const rep = (DisconnectRep *)&signal->theData[0];
+ rep->nodeId = hostId;
+ rep->err = errNo;
+ sendSignal(QMGR_REF, GSN_DISCONNECT_REP, signal,
+ DisconnectRep::SignalLength, JBA);
+ } else if((globalData.theStartLevel == NodeState::SL_CMVMI ||
+ globalData.theStartLevel == NodeState::SL_STARTING)
+ && type == NodeInfo::MGM) {
+ /**
+ * Someone disconnected during cmvmi period
+ */
+ jam();
+ globalTransporterRegistry.do_connect(hostId);
+ }
+
+ cancelSubscription(hostId);
+
+ signal->theData[0] = NDB_LE_Disconnected;
+ signal->theData[1] = hostId;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+}
+
+void Cmvmi::execCONNECT_REP(Signal *signal){
+ const Uint32 hostId = signal->theData[0];
+ jamEntry();
+
+ const NodeInfo::NodeType type = (NodeInfo::NodeType)getNodeInfo(hostId).m_type;
+ ndbrequire(type != NodeInfo::INVALID);
+ globalData.m_nodeInfo[hostId].m_version = 0;
+ globalData.m_nodeInfo[hostId].m_signalVersion = 0;
+
+ if(type == NodeInfo::DB || globalData.theStartLevel >= NodeState::SL_STARTED){
+ jam();
+
+ /**
+ * Inform QMGR that client has connected
+ */
+
+ signal->theData[0] = hostId;
+ sendSignal(QMGR_REF, GSN_CONNECT_REP, signal, 1, JBA);
+ } else if(globalData.theStartLevel == NodeState::SL_CMVMI ||
+ globalData.theStartLevel == NodeState::SL_STARTING) {
+ jam();
+ /**
+ * Someone connected before start was finished
+ */
+ if(type == NodeInfo::MGM){
+ jam();
+ } else {
+ /**
+ * Dont allow api nodes to connect
+ */
+ abort();
+ globalTransporterRegistry.do_disconnect(hostId);
+ }
+ }
+
+ /* Automatically subscribe events for MGM nodes.
+ */
+ if(type == NodeInfo::MGM){
+ jam();
+ globalTransporterRegistry.setIOState(hostId, NoHalt);
+ }
+
+ //------------------------------------------
+ // Also report this event to the Event handler
+ //------------------------------------------
+ signal->theData[0] = NDB_LE_Connected;
+ signal->theData[1] = hostId;
+ signal->header.theLength = 2;
+
+ execEVENT_REP(signal);
+}
+
+#ifdef VM_TRACE
+void
+modifySignalLogger(bool allBlocks, BlockNumber bno,
+ TestOrd::Command cmd,
+ TestOrd::SignalLoggerSpecification spec){
+ SignalLoggerManager::LogMode logMode;
+
+ /**
+ * Mapping between SignalLoggerManager::LogMode and
+ * TestOrd::SignalLoggerSpecification
+ */
+ switch(spec){
+ case TestOrd::InputSignals:
+ logMode = SignalLoggerManager::LogIn;
+ break;
+ case TestOrd::OutputSignals:
+ logMode = SignalLoggerManager::LogOut;
+ break;
+ case TestOrd::InputOutputSignals:
+ logMode = SignalLoggerManager::LogInOut;
+ break;
+ default:
+ return;
+ break;
+ }
+
+ switch(cmd){
+ case TestOrd::On:
+ globalSignalLoggers.logOn(allBlocks, bno, logMode);
+ break;
+ case TestOrd::Off:
+ globalSignalLoggers.logOff(allBlocks, bno, logMode);
+ break;
+ case TestOrd::Toggle:
+ globalSignalLoggers.logToggle(allBlocks, bno, logMode);
+ break;
+ case TestOrd::KeepUnchanged:
+ // Do nothing
+ break;
+ }
+ globalSignalLoggers.flushSignalLog();
+}
+#endif
+
+void
+Cmvmi::execTEST_ORD(Signal * signal){
+ jamEntry();
+
+#ifdef VM_TRACE
+ TestOrd * const testOrd = (TestOrd *)&signal->theData[0];
+
+ TestOrd::Command cmd;
+
+ {
+ /**
+ * Process Trace command
+ */
+ TestOrd::TraceSpecification traceSpec;
+
+ testOrd->getTraceCommand(cmd, traceSpec);
+ unsigned long traceVal = traceSpec;
+ unsigned long currentTraceVal = globalSignalLoggers.getTrace();
+ switch(cmd){
+ case TestOrd::On:
+ currentTraceVal |= traceVal;
+ break;
+ case TestOrd::Off:
+ currentTraceVal &= (~traceVal);
+ break;
+ case TestOrd::Toggle:
+ currentTraceVal ^= traceVal;
+ break;
+ case TestOrd::KeepUnchanged:
+ // Do nothing
+ break;
+ }
+ globalSignalLoggers.setTrace(currentTraceVal);
+ }
+
+ {
+ /**
+ * Process Log command
+ */
+ TestOrd::SignalLoggerSpecification logSpec;
+ BlockNumber bno;
+ unsigned int loggers = testOrd->getNoOfSignalLoggerCommands();
+
+ if(loggers == (unsigned)~0){ // Apply command to all blocks
+ testOrd->getSignalLoggerCommand(0, bno, cmd, logSpec);
+ modifySignalLogger(true, bno, cmd, logSpec);
+ } else {
+ for(unsigned int i = 0; i<loggers; i++){
+ testOrd->getSignalLoggerCommand(i, bno, cmd, logSpec);
+ modifySignalLogger(false, bno, cmd, logSpec);
+ }
+ }
+ }
+
+ {
+ /**
+ * Process test command
+ */
+ testOrd->getTestCommand(cmd);
+ switch(cmd){
+ case TestOrd::On:{
+ SET_GLOBAL_TEST_ON;
+ }
+ break;
+ case TestOrd::Off:{
+ SET_GLOBAL_TEST_OFF;
+ }
+ break;
+ case TestOrd::Toggle:{
+ TOGGLE_GLOBAL_TEST_FLAG;
+ }
+ break;
+ case TestOrd::KeepUnchanged:
+ // Do nothing
+ break;
+ }
+ }
+
+#endif
+}
+
+void Cmvmi::execSTATISTICS_REQ(Signal* signal)
+{
+ // TODO Note ! This is only a test implementation...
+
+ static int stat1 = 0;
+ jamEntry();
+
+ //ndbout << "data 1: " << signal->theData[1];
+
+ int x = signal->theData[0];
+ stat1++;
+ signal->theData[0] = stat1;
+ sendSignal(x, GSN_STATISTICS_CONF, signal, 7, JBB);
+
+}//execSTATISTICS_REQ()
+
+
+
+void Cmvmi::execSTOP_ORD(Signal* signal)
+{
+ jamEntry();
+ globalData.theRestartFlag = perform_stop;
+}//execSTOP_ORD()
+
+void
+Cmvmi::execSTART_ORD(Signal* signal) {
+
+ StartOrd * const startOrd = (StartOrd *)&signal->theData[0];
+ jamEntry();
+
+ Uint32 tmp = startOrd->restartInfo;
+ if(StopReq::getPerformRestart(tmp)){
+ jam();
+ /**
+ *
+ */
+ NdbRestartType type = NRT_Default;
+ if(StopReq::getNoStart(tmp) && StopReq::getInitialStart(tmp))
+ type = NRT_NoStart_InitialStart;
+ if(StopReq::getNoStart(tmp) && !StopReq::getInitialStart(tmp))
+ type = NRT_NoStart_Restart;
+ if(!StopReq::getNoStart(tmp) && StopReq::getInitialStart(tmp))
+ type = NRT_DoStart_InitialStart;
+ if(!StopReq::getNoStart(tmp)&&!StopReq::getInitialStart(tmp))
+ type = NRT_DoStart_Restart;
+ NdbShutdown(NST_Restart, type);
+ }
+
+ if(globalData.theRestartFlag == system_started){
+ jam()
+ /**
+ * START_ORD received when already started(ignored)
+ */
+ //ndbout << "START_ORD received when already started(ignored)" << endl;
+ return;
+ }
+
+ if(globalData.theRestartFlag == perform_stop){
+ jam()
+ /**
+ * START_ORD received when stopping(ignored)
+ */
+ //ndbout << "START_ORD received when stopping(ignored)" << endl;
+ return;
+ }
+
+ if(globalData.theStartLevel == NodeState::SL_NOTHING){
+ jam();
+ globalData.theStartLevel = NodeState::SL_CMVMI;
+ /**
+ * Open connections to management servers
+ */
+ for(unsigned int i = 1; i < MAX_NODES; i++ ){
+ if (getNodeInfo(i).m_type == NodeInfo::MGM){
+ if(!globalTransporterRegistry.is_connected(i)){
+ globalTransporterRegistry.do_connect(i);
+ globalTransporterRegistry.setIOState(i, NoHalt);
+ }
+ }
+ }
+ return ;
+ }
+
+ if(globalData.theStartLevel == NodeState::SL_CMVMI){
+ jam();
+ globalData.theStartLevel = NodeState::SL_STARTING;
+ globalData.theRestartFlag = system_started;
+ /**
+ * StartLevel 1
+ *
+ * Do Restart
+ */
+
+ globalScheduler.clear();
+ globalTimeQueue.clear();
+
+ // Disconnect all nodes as part of the system restart.
+ // We need to ensure that we are starting up
+ // without any connected nodes.
+ for(unsigned int i = 1; i < MAX_NODES; i++ ){
+ if (i != getOwnNodeId() && getNodeInfo(i).m_type != NodeInfo::MGM){
+ globalTransporterRegistry.do_disconnect(i);
+ globalTransporterRegistry.setIOState(i, HaltIO);
+ }
+ }
+
+ /**
+ * Start running startphases
+ */
+ sendSignal(NDBCNTR_REF, GSN_START_ORD, signal, 1, JBA);
+ return;
+ }
+}//execSTART_ORD()
+
+void Cmvmi::execTAMPER_ORD(Signal* signal)
+{
+ jamEntry();
+ // TODO We should maybe introduce a CONF and REF signal
+ // to be able to indicate if we really introduced an error.
+#ifdef ERROR_INSERT
+ TamperOrd* const tamperOrd = (TamperOrd*)&signal->theData[0];
+
+ signal->theData[1] = tamperOrd->errorNo;
+ signal->theData[0] = 5;
+ sendSignal(DBDIH_REF, GSN_DIHNDBTAMPER, signal, 3,JBB);
+#endif
+
+}//execTAMPER_ORD()
+
+
+
+void Cmvmi::execSET_VAR_REQ(Signal* signal)
+{
+#if 0
+
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+ jamEntry();
+ switch (var) {
+
+ // NDBCNTR_REF
+
+ // DBTC
+ case TransactionDeadlockDetectionTimeout:
+ case TransactionInactiveTime:
+ case NoOfConcurrentProcessesHandleTakeover:
+ sendSignal(DBTC_REF, GSN_SET_VAR_REQ, signal, 3, JBB);
+ break;
+
+ // DBDIH
+ case TimeBetweenLocalCheckpoints:
+ case TimeBetweenGlobalCheckpoints:
+ sendSignal(DBDIH_REF, GSN_SET_VAR_REQ, signal, 3, JBB);
+ break;
+
+ // DBLQH
+ case NoOfConcurrentCheckpointsDuringRestart:
+ case NoOfConcurrentCheckpointsAfterRestart:
+ sendSignal(DBLQH_REF, GSN_SET_VAR_REQ, signal, 3, JBB);
+ break;
+
+ // DBACC
+ case NoOfDiskPagesToDiskDuringRestartACC:
+ case NoOfDiskPagesToDiskAfterRestartACC:
+ sendSignal(DBACC_REF, GSN_SET_VAR_REQ, signal, 3, JBB);
+ break;
+
+ // DBTUP
+ case NoOfDiskPagesToDiskDuringRestartTUP:
+ case NoOfDiskPagesToDiskAfterRestartTUP:
+ sendSignal(DBTUP_REF, GSN_SET_VAR_REQ, signal, 3, JBB);
+ break;
+
+ // DBDICT
+
+ // NDBCNTR
+ case TimeToWaitAlive:
+
+ // QMGR
+ case HeartbeatIntervalDbDb: // TODO ev till Ndbcnt också
+ case HeartbeatIntervalDbApi:
+ case ArbitTimeout:
+ sendSignal(QMGR_REF, GSN_SET_VAR_REQ, signal, 3, JBB);
+ break;
+
+ // NDBFS
+
+ // CMVMI
+ case MaxNoOfSavedMessages:
+ case LockPagesInMainMemory:
+ case TimeBetweenWatchDogCheck:
+ case StopOnError:
+ handleSET_VAR_REQ(signal);
+ break;
+
+
+ // Not possible to update (this could of course be handled by each block
+ // instead but I havn't investigated where they belong)
+ case Id:
+ case ExecuteOnComputer:
+ case ShmKey:
+ case MaxNoOfConcurrentOperations:
+ case MaxNoOfConcurrentTransactions:
+ case MemorySpaceIndexes:
+ case MemorySpaceTuples:
+ case MemoryDiskPages:
+ case NoOfFreeDiskClusters:
+ case NoOfDiskClusters:
+ case NoOfFragmentLogFiles:
+ case NoOfDiskClustersPerDiskFile:
+ case NoOfDiskFiles:
+ case MaxNoOfSavedEvents:
+ default:
+
+ int mgmtSrvr = setVarReq->mgmtSrvrBlockRef();
+ sendSignal(mgmtSrvr, GSN_SET_VAR_REF, signal, 0, JBB);
+ } // switch
+
+#endif
+}//execSET_VAR_REQ()
+
+
+void Cmvmi::execSET_VAR_CONF(Signal* signal)
+{
+ int mgmtSrvr = signal->theData[0];
+ sendSignal(mgmtSrvr, GSN_SET_VAR_CONF, signal, 0, JBB);
+
+}//execSET_VAR_CONF()
+
+
+void Cmvmi::execSET_VAR_REF(Signal* signal)
+{
+ int mgmtSrvr = signal->theData[0];
+ sendSignal(mgmtSrvr, GSN_SET_VAR_REF, signal, 0, JBB);
+
+}//execSET_VAR_REF()
+
+
+void Cmvmi::handleSET_VAR_REQ(Signal* signal) {
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+ int val = setVarReq->value();
+
+ switch (var) {
+ case MaxNoOfSavedMessages:
+ theConfig.maxNoOfErrorLogs(val);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case LockPagesInMainMemory:
+ int result;
+ if (val == 0) {
+ result = NdbMem_MemUnlockAll();
+ }
+ else {
+ result = NdbMem_MemLockAll();
+ }
+ if (result == 0) {
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ }
+ else {
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ }
+ break;
+
+ case TimeBetweenWatchDogCheck:
+ theConfig.timeBetweenWatchDogCheck(val);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case StopOnError:
+ theConfig.stopOnError(val);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ return;
+ } // switch
+#endif
+}
+
+#ifdef VM_TRACE
+class RefSignalTest {
+public:
+ enum ErrorCode {
+ OK = 0,
+ NF_FakeErrorREF = 7
+ };
+ Uint32 senderRef;
+ Uint32 senderData;
+ Uint32 errorCode;
+};
+#endif
+
+
+static int iii;
+
+static
+int
+recurse(char * buf, int loops, int arg){
+ char * tmp = (char*)alloca(arg);
+ printf("tmp = %p\n", tmp);
+ for(iii = 0; iii<arg; iii += 1024){
+ tmp[iii] = (iii % 23 + (arg & iii));
+ }
+
+ if(loops == 0)
+ return tmp[345];
+ else
+ return tmp[arg/loops] + recurse(tmp, loops - 1, arg);
+}
+
+void
+Cmvmi::execDUMP_STATE_ORD(Signal* signal)
+{
+
+ sendSignal(QMGR_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(NDBCNTR_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBTC_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBDIH_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBDICT_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBLQH_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBTUP_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBACC_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(NDBFS_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(BACKUP_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBUTIL_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(SUMA_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(GREP_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(TRIX_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+ sendSignal(DBTUX_REF, GSN_DUMP_STATE_ORD, signal, signal->length(), JBB);
+
+ /**
+ *
+ * Here I can dump CMVMI state if needed
+ */
+ if(signal->theData[0] == 13){
+#if 0
+ int loop = 100;
+ int len = (10*1024*1024);
+ if(signal->getLength() > 1)
+ loop = signal->theData[1];
+ if(signal->getLength() > 2)
+ len = signal->theData[2];
+
+ ndbout_c("recurse(%d loop, %dkb per recurse)", loop, len/1024);
+ int a = recurse(0, loop, len);
+ ndbout_c("after...%d", a);
+#endif
+ }
+
+ DumpStateOrd * const & dumpState = (DumpStateOrd *)&signal->theData[0];
+ if (dumpState->args[0] == DumpStateOrd::CmvmiDumpConnections){
+ for(unsigned int i = 1; i < MAX_NODES; i++ ){
+ const char* nodeTypeStr = "";
+ switch(getNodeInfo(i).m_type){
+ case NodeInfo::DB:
+ nodeTypeStr = "DB";
+ break;
+ case NodeInfo::API:
+ nodeTypeStr = "API";
+ break;
+ case NodeInfo::MGM:
+ nodeTypeStr = "MGM";
+ break;
+ case NodeInfo::REP:
+ nodeTypeStr = "REP";
+ break;
+ case NodeInfo::INVALID:
+ nodeTypeStr = 0;
+ break;
+ default:
+ nodeTypeStr = "<UNKNOWN>";
+ }
+
+ if(nodeTypeStr == 0)
+ continue;
+
+ infoEvent("Connection to %d (%s) %s",
+ i,
+ nodeTypeStr,
+ globalTransporterRegistry.getPerformStateString(i));
+ }
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::CmvmiDumpLongSignalMemory){
+ infoEvent("Cmvmi: g_sectionSegmentPool size: %d free: %d",
+ g_sectionSegmentPool.getSize(),
+ g_sectionSegmentPool.getNoOfFree());
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::CmvmiSetRestartOnErrorInsert){
+ if(signal->getLength() == 1)
+ theConfig.setRestartOnErrorInsert((int)NRT_NoStart_Restart);
+ else
+ theConfig.setRestartOnErrorInsert(signal->theData[1]);
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::CmvmiTestLongSigWithDelay) {
+ unsigned i;
+ Uint32 loopCount = dumpState->args[1];
+ const unsigned len0 = 11;
+ const unsigned len1 = 123;
+ Uint32 sec0[len0];
+ Uint32 sec1[len1];
+ for (i = 0; i < len0; i++)
+ sec0[i] = i;
+ for (i = 0; i < len1; i++)
+ sec1[i] = 16 * i;
+ Uint32* sig = signal->getDataPtrSend();
+ sig[0] = reference();
+ sig[1] = 20; // test type
+ sig[2] = 0;
+ sig[3] = 0;
+ sig[4] = loopCount;
+ sig[5] = len0;
+ sig[6] = len1;
+ sig[7] = 0;
+ LinearSectionPtr ptr[3];
+ ptr[0].p = sec0;
+ ptr[0].sz = len0;
+ ptr[1].p = sec1;
+ ptr[1].sz = len1;
+ sendSignal(reference(), GSN_TESTSIG, signal, 8, JBB, ptr, 2);
+ }
+
+#ifdef VM_TRACE
+#if 0
+ {
+ SafeCounterManager mgr(* this); mgr.setSize(1);
+ SafeCounterHandle handle;
+
+ {
+ SafeCounter tmp(mgr, handle);
+ tmp.init<RefSignalTest>(CMVMI, GSN_TESTSIG, /* senderData */ 13);
+ tmp.setWaitingFor(3);
+ ndbrequire(!tmp.done());
+ ndbout_c("Allocted");
+ }
+ ndbrequire(!handle.done());
+ {
+ SafeCounter tmp(mgr, handle);
+ tmp.clearWaitingFor(3);
+ ndbrequire(tmp.done());
+ ndbout_c("Deallocted");
+ }
+ ndbrequire(handle.done());
+ }
+#endif
+#endif
+}//Cmvmi::execDUMP_STATE_ORD()
+
+
+BLOCK_FUNCTIONS(Cmvmi)
+
+static Uint32 g_print;
+static LinearSectionPtr g_test[3];
+
+void
+Cmvmi::execTESTSIG(Signal* signal){
+ Uint32 i;
+ /**
+ * Test of SafeCounter
+ */
+ jamEntry();
+
+ if(!assembleFragments(signal)){
+ jam();
+ return;
+ }
+
+ Uint32 ref = signal->theData[0];
+ Uint32 testType = signal->theData[1];
+ Uint32 fragmentLength = signal->theData[2];
+ g_print = signal->theData[3];
+// Uint32 returnCount = signal->theData[4];
+ Uint32 * secSizes = &signal->theData[5];
+
+ if(g_print){
+ SignalLoggerManager::printSignalHeader(stdout,
+ signal->header,
+ 0,
+ getOwnNodeId(),
+ true);
+ ndbout_c("-- Fixed section --");
+ for(i = 0; i<signal->length(); i++){
+ fprintf(stdout, "H'0x%.8x ", signal->theData[i]);
+ if(((i + 1) % 6) == 0)
+ fprintf(stdout, "\n");
+ }
+ fprintf(stdout, "\n");
+
+ for(i = 0; i<signal->header.m_noOfSections; i++){
+ SegmentedSectionPtr ptr;
+ ndbout_c("-- Section %d --", i);
+ signal->getSection(ptr, i);
+ ndbrequire(ptr.p != 0);
+ print(ptr, stdout);
+ ndbrequire(ptr.sz == secSizes[i]);
+ }
+ }
+
+ /**
+ * Validate length:s
+ */
+ for(i = 0; i<signal->header.m_noOfSections; i++){
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, i);
+ ndbrequire(ptr.p != 0);
+ ndbrequire(ptr.sz == secSizes[i]);
+ }
+
+ /**
+ * Testing send with delay.
+ */
+ if (testType == 20) {
+ if (signal->theData[4] == 0) {
+ releaseSections(signal);
+ return;
+ }
+ signal->theData[4]--;
+ sendSignalWithDelay(reference(), GSN_TESTSIG, signal, 100, 8);
+ return;
+ }
+
+ NodeReceiverGroup rg(CMVMI, c_dbNodes);
+
+ if(signal->getSendersBlockRef() == ref){
+ /**
+ * Signal from API (not via NodeReceiverGroup)
+ */
+ if((testType % 2) == 1){
+ signal->theData[4] = 1;
+ } else {
+ signal->theData[1] --;
+ signal->theData[4] = rg.m_nodes.count();
+ }
+ }
+
+ switch(testType){
+ case 1:
+ sendSignal(ref, GSN_TESTSIG, signal, signal->length(), JBB);
+ break;
+ case 2:
+ sendSignal(rg, GSN_TESTSIG, signal, signal->length(), JBB);
+ break;
+ case 3:
+ case 4:{
+ LinearSectionPtr ptr[3];
+ const Uint32 secs = signal->getNoOfSections();
+ for(i = 0; i<secs; i++){
+ SegmentedSectionPtr sptr;
+ signal->getSection(sptr, i);
+ ptr[i].sz = sptr.sz;
+ ptr[i].p = new Uint32[sptr.sz];
+ copy(ptr[i].p, sptr);
+ }
+
+ if(testType == 3){
+ sendSignal(ref, GSN_TESTSIG, signal, signal->length(), JBB, ptr, secs);
+ } else {
+ sendSignal(rg, GSN_TESTSIG, signal, signal->length(), JBB, ptr, secs);
+ }
+ for(Uint32 i = 0; i<secs; i++){
+ delete[] ptr[i].p;
+ }
+ break;
+ }
+ case 5:
+ case 6:{
+
+ NodeReceiverGroup tmp;
+ if(testType == 5){
+ tmp = ref;
+ } else {
+ tmp = rg;
+ }
+
+ FragmentSendInfo fragSend;
+ sendFirstFragment(fragSend,
+ tmp,
+ GSN_TESTSIG,
+ signal,
+ signal->length(),
+ JBB,
+ fragmentLength);
+ int count = 1;
+ while(fragSend.m_status != FragmentSendInfo::SendComplete){
+ count++;
+ if(g_print)
+ ndbout_c("Sending fragment %d", count);
+ sendNextSegmentedFragment(signal, fragSend);
+ }
+ break;
+ }
+ case 7:
+ case 8:{
+ LinearSectionPtr ptr[3];
+ const Uint32 secs = signal->getNoOfSections();
+ for(i = 0; i<secs; i++){
+ SegmentedSectionPtr sptr;
+ signal->getSection(sptr, i);
+ ptr[i].sz = sptr.sz;
+ ptr[i].p = new Uint32[sptr.sz];
+ copy(ptr[i].p, sptr);
+ }
+
+ NodeReceiverGroup tmp;
+ if(testType == 7){
+ tmp = ref;
+ } else {
+ tmp = rg;
+ }
+
+ FragmentSendInfo fragSend;
+ sendFirstFragment(fragSend,
+ tmp,
+ GSN_TESTSIG,
+ signal,
+ signal->length(),
+ JBB,
+ ptr,
+ secs,
+ fragmentLength);
+
+ int count = 1;
+ while(fragSend.m_status != FragmentSendInfo::SendComplete){
+ count++;
+ if(g_print)
+ ndbout_c("Sending fragment %d", count);
+ sendNextLinearFragment(signal, fragSend);
+ }
+
+ for(i = 0; i<secs; i++){
+ delete[] ptr[i].p;
+ }
+ break;
+ }
+ case 9:
+ case 10:{
+
+ Callback m_callBack;
+ m_callBack.m_callbackFunction =
+ safe_cast(&Cmvmi::sendFragmentedComplete);
+
+ if(testType == 9){
+ m_callBack.m_callbackData = 9;
+ sendFragmentedSignal(ref,
+ GSN_TESTSIG, signal, signal->length(), JBB,
+ m_callBack,
+ fragmentLength);
+ } else {
+ m_callBack.m_callbackData = 10;
+ sendFragmentedSignal(rg,
+ GSN_TESTSIG, signal, signal->length(), JBB,
+ m_callBack,
+ fragmentLength);
+ }
+ break;
+ }
+ case 11:
+ case 12:{
+
+ const Uint32 secs = signal->getNoOfSections();
+ memset(g_test, 0, sizeof(g_test));
+ for(i = 0; i<secs; i++){
+ SegmentedSectionPtr sptr;
+ signal->getSection(sptr, i);
+ g_test[i].sz = sptr.sz;
+ g_test[i].p = new Uint32[sptr.sz];
+ copy(g_test[i].p, sptr);
+ }
+
+
+ Callback m_callBack;
+ m_callBack.m_callbackFunction =
+ safe_cast(&Cmvmi::sendFragmentedComplete);
+
+ if(testType == 11){
+ m_callBack.m_callbackData = 11;
+ sendFragmentedSignal(ref,
+ GSN_TESTSIG, signal, signal->length(), JBB,
+ g_test, secs,
+ m_callBack,
+ fragmentLength);
+ } else {
+ m_callBack.m_callbackData = 12;
+ sendFragmentedSignal(rg,
+ GSN_TESTSIG, signal, signal->length(), JBB,
+ g_test, secs,
+ m_callBack,
+ fragmentLength);
+ }
+ break;
+ }
+ case 13:{
+ ndbrequire(signal->getNoOfSections() == 0);
+ Uint32 loop = signal->theData[9];
+ if(loop > 0){
+ signal->theData[9] --;
+ sendSignal(CMVMI_REF, GSN_TESTSIG, signal, signal->length(), JBB);
+ return;
+ }
+ sendSignal(ref, GSN_TESTSIG, signal, signal->length(), JBB);
+ return;
+ }
+ case 14:{
+ Uint32 count = signal->theData[8];
+ signal->theData[10] = count * rg.m_nodes.count();
+ for(i = 0; i<count; i++){
+ sendSignal(rg, GSN_TESTSIG, signal, signal->length(), JBB);
+ }
+ return;
+ }
+
+ default:
+ ndbrequire(false);
+ }
+ return;
+}
+
+void
+Cmvmi::sendFragmentedComplete(Signal* signal, Uint32 data, Uint32 returnCode){
+ if(g_print)
+ ndbout_c("sendFragmentedComplete: %d", data);
+ if(data == 11 || data == 12){
+ for(Uint32 i = 0; i<3; i++){
+ if(g_test[i].p != 0)
+ delete[] g_test[i].p;
+ }
+ }
+}
diff --git a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.hpp b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.hpp
new file mode 100644
index 00000000000..1c91f564749
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.hpp
@@ -0,0 +1,124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 Cmvmi_H_
+#define Cmvmi_H_
+
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+#include <LogLevel.hpp>
+
+#include <ArrayList.hpp>
+
+/**
+ * Cmvmi class
+ */
+class Cmvmi : public SimulatedBlock {
+public:
+ Cmvmi(const Configuration & conf);
+ virtual ~Cmvmi();
+
+private:
+ /**
+ * These methods used to be reportXXX
+ *
+ * But they in a nasty way intefere with the execution model
+ * they been turned in to exec-Method used via prio A signals
+ */
+ void execDISCONNECT_REP(Signal*);
+ void execCONNECT_REP(Signal*);
+
+private:
+ BLOCK_DEFINES(Cmvmi);
+
+ // The signal processing functions
+ void execNDB_TAMPER(Signal* signal);
+ void execSET_LOGLEVELORD(Signal* signal);
+ void execEVENT_REP(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execCLOSE_COMREQ(Signal* signal);
+ void execENABLE_COMORD(Signal* signal);
+ void execOPEN_COMREQ(Signal* signal);
+ void execSIZEALT_ACK(Signal* signal);
+ void execTEST_ORD(Signal* signal);
+
+ void execSTATISTICS_REQ(Signal* signal);
+ void execSTOP_ORD(Signal* signal);
+ void execSTART_ORD(Signal* signal);
+ void execTAMPER_ORD(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+ void execSET_VAR_CONF(Signal* signal);
+ void execSET_VAR_REF(Signal* signal);
+
+ void execDUMP_STATE_ORD(Signal* signal);
+
+ void execEVENT_SUBSCRIBE_REQ(Signal *);
+ void cancelSubscription(NodeId nodeId);
+
+ void handleSET_VAR_REQ(Signal* signal);
+
+ void execTESTSIG(Signal* signal);
+
+ char theErrorMessage[256];
+ void sendSTTORRY(Signal* signal);
+
+ LogLevel clogLevel;
+ NdbNodeBitmask c_dbNodes;
+
+ class Configuration & theConfig;
+
+ /**
+ * This struct defines the data needed for a EVENT_REP subscriber
+ */
+ struct EventRepSubscriber {
+ /**
+ * What log level is the subscriber using
+ */
+ LogLevel logLevel;
+
+ /**
+ * What block reference does he use
+ * (Where should the EVENT_REP's be forwarded)
+ */
+ BlockReference blockRef;
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union { Uint32 nextPool; Uint32 nextList; };
+ Uint32 prevList;
+ };
+ typedef Ptr<EventRepSubscriber> SubscriberPtr;
+
+ /**
+ * Pool of EventRepSubscriber record
+ */
+ ArrayPool<EventRepSubscriber> subscriberPool;
+
+ /**
+ * List of current subscribers
+ */
+ ArrayList<EventRepSubscriber> subscribers;
+
+private:
+ // Declared but not defined
+ Cmvmi(const Cmvmi &obj);
+ void operator = (const Cmvmi &);
+
+ void sendFragmentedComplete(Signal* signal, Uint32 data, Uint32 returnCode);
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/cmvmi/Makefile.am b/storage/ndb/src/kernel/blocks/cmvmi/Makefile.am
new file mode 100644
index 00000000000..dc2e12746fd
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Makefile.am
@@ -0,0 +1,24 @@
+
+noinst_LIBRARIES = libcmvmi.a
+
+libcmvmi_a_SOURCES = Cmvmi.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libcmvmi.dsp
+
+libcmvmi.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libcmvmi_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp b/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp
new file mode 100644
index 00000000000..6a65da5bb6a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp
@@ -0,0 +1,1470 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBACC_H
+#define DBACC_H
+
+
+
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+
+// primary key is stored in TUP
+#include <Dbtup.hpp>
+
+#ifdef DBACC_C
+// Debug Macros
+#define dbgWord32(ptr, ind, val)
+
+/*
+#define dbgWord32(ptr, ind, val) \
+if(debug_jan){ \
+tmp_val = val; \
+switch(ind){ \
+case 1: strcpy(tmp_string, "ZPOS_PAGE_TYPE "); \
+break; \
+case 2: strcpy(tmp_string, "ZPOS_NO_ELEM_IN_PAGE"); \
+break; \
+case 3: strcpy(tmp_string, "ZPOS_CHECKSUM "); \
+break; \
+case 4: strcpy(tmp_string, "ZPOS_OVERFLOWREC "); \
+break; \
+case 5: strcpy(tmp_string, "ZPOS_FREE_AREA_IN_PAGE"); \
+break; \
+case 6: strcpy(tmp_string, "ZPOS_LAST_INDEX "); \
+break; \
+case 7: strcpy(tmp_string, "ZPOS_INSERT_INDEX "); \
+break; \
+case 8: strcpy(tmp_string, "ZPOS_ARRAY_POS "); \
+break; \
+case 9: strcpy(tmp_string, "ZPOS_NEXT_FREE_INDEX"); \
+break; \
+case 10: strcpy(tmp_string, "ZPOS_NEXT_PAGE "); \
+break; \
+case 11: strcpy(tmp_string, "ZPOS_PREV_PAGE "); \
+break; \
+default: sprintf(tmp_string, "%-20d", ind);\
+} \
+ndbout << "Ptr: " << ptr.p->word32 << " \tIndex: " << tmp_string << " \tValue: " << tmp_val << " \tLINE: " << __LINE__ << endl; \
+}\
+*/
+
+#define dbgUndoword(ptr, ind, val)
+
+// Constants
+/** ------------------------------------------------------------------------
+ * THESE ARE CONSTANTS THAT ARE USED FOR DEFINING THE SIZE OF BUFFERS, THE
+ * SIZE OF PAGE HEADERS, THE NUMBER OF BUFFERS IN A PAGE AND A NUMBER OF
+ * OTHER CONSTANTS WHICH ARE CHANGED WHEN THE BUFFER SIZE IS CHANGED.
+ * ----------------------------------------------------------------------- */
+#define ZHEAD_SIZE 32
+#define ZCON_HEAD_SIZE 2
+#define ZBUF_SIZE 28
+#define ZEMPTYLIST 72
+#define ZUP_LIMIT 14
+#define ZDOWN_LIMIT 12
+#define ZSHIFT_PLUS 5
+#define ZSHIFT_MINUS 2
+#define ZFREE_LIMIT 65
+#define ZNO_CONTAINERS 64
+#define ZELEM_HEAD_SIZE 1
+/* ------------------------------------------------------------------------- */
+/* THESE CONSTANTS DEFINE THE USE OF THE PAGE HEADER IN THE INDEX PAGES. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_PAGE_ID 0
+#define ZPOS_PAGE_TYPE 1
+#define ZPOS_PAGE_TYPE_BIT 14
+#define ZPOS_EMPTY_LIST 1
+#define ZPOS_ALLOC_CONTAINERS 2
+#define ZPOS_CHECKSUM 3
+#define ZPOS_OVERFLOWREC 4
+#define ZPOS_NO_ELEM_IN_PAGE 2
+#define ZPOS_FREE_AREA_IN_PAGE 5
+#define ZPOS_LAST_INDEX 6
+#define ZPOS_INSERT_INDEX 7
+#define ZPOS_ARRAY_POS 8
+#define ZPOS_NEXT_FREE_INDEX 9
+#define ZPOS_NEXT_PAGE 10
+#define ZPOS_PREV_PAGE 11
+#define ZNORMAL_PAGE_TYPE 0
+#define ZOVERFLOW_PAGE_TYPE 1
+#define ZDEFAULT_LIST 3
+#define ZWORDS_IN_PAGE 2048
+/* --------------------------------------------------------------------------------- */
+/* CONSTANTS FOR THE ZERO PAGES */
+/* --------------------------------------------------------------------------------- */
+#define ZPAGEZERO_PREV_UNDOP 8
+#define ZPAGEZERO_NO_OVER_PAGE 9
+#define ZPAGEZERO_TABID 10
+#define ZPAGEZERO_FRAGID0 11
+#define ZPAGEZERO_FRAGID1 12
+#define ZPAGEZERO_HASH_CHECK 13
+#define ZPAGEZERO_DIRSIZE 14
+#define ZPAGEZERO_EXPCOUNTER 15
+#define ZPAGEZERO_NEXT_UNDO_FILE 16
+#define ZPAGEZERO_SLACK 17
+#define ZPAGEZERO_NO_PAGES 18
+#define ZPAGEZERO_HASHCHECKBIT 19
+#define ZPAGEZERO_K 20
+#define ZPAGEZERO_LHFRAGBITS 21
+#define ZPAGEZERO_LHDIRBITS 22
+#define ZPAGEZERO_LOCALKEYLEN 23
+#define ZPAGEZERO_MAXP 24
+#define ZPAGEZERO_MAXLOADFACTOR 25
+#define ZPAGEZERO_MINLOADFACTOR 26
+#define ZPAGEZERO_MYFID 27
+#define ZPAGEZERO_LAST_OVER_INDEX 28
+#define ZPAGEZERO_P 29
+#define ZPAGEZERO_NO_OF_ELEMENTS 30
+#define ZPAGEZERO_ELEMENT_LENGTH 31
+#define ZPAGEZERO_KEY_LENGTH 32
+#define ZPAGEZERO_NODETYPE 33
+#define ZPAGEZERO_SLACK_CHECK 34
+/* --------------------------------------------------------------------------------- */
+/* CONSTANTS IN ALPHABETICAL ORDER */
+/* --------------------------------------------------------------------------------- */
+#define ZADDFRAG 0
+#define ZCOPY_NEXT 1
+#define ZCOPY_NEXT_COMMIT 2
+#define ZCOPY_COMMIT 3
+#define ZCOPY_REPEAT 4
+#define ZCOPY_ABORT 5
+#define ZCOPY_CLOSE 6
+#define ZDIRARRAY 68
+#define ZDIRRANGESIZE 65
+//#define ZEMPTY_FRAGMENT 0
+#define ZFRAGMENTSIZE 64
+#define ZFIRSTTIME 1
+#define ZFS_CONNECTSIZE 300
+#define ZFS_OPSIZE 100
+#define ZKEYINKEYREQ 4
+#define ZLCP_CONNECTSIZE 30
+#define ZLEFT 1
+#define ZLOCALLOGFILE 2
+#define ZLOCKED 0
+#define ZMAXSCANSIGNALLEN 20
+#define ZMAINKEYLEN 8
+#define ZMAX_UNDO_VERSION 4
+#define ZNO_OF_DISK_VERSION 3
+#define ZNO_OF_OP_PER_SIGNAL 20
+//#define ZNOT_EMPTY_FRAGMENT 1
+#define ZNR_OF_UNDO_PAGE_GROUP 16
+#define ZOP_HEAD_INFO_LN 3
+#define ZOPRECSIZE 740
+#define ZOVERFLOWRECSIZE 5
+#define ZPAGE8_BASE_ADD 1
+#define ZPAGESIZE 128
+#define ZPARALLEL_QUEUE 1
+#define ZPDIRECTORY 1
+#define ZSCAN_MAX_LOCK 4
+#define ZSERIAL_QUEUE 2
+#define ZSPH1 1
+#define ZSPH2 2
+#define ZSPH3 3
+#define ZSPH6 6
+#define ZREADLOCK 0
+#define ZRIGHT 2
+#define ZROOTFRAGMENTSIZE 32
+#define ZSCAN_LOCK_ALL 3
+#define ZSCAN_OP 5
+#define ZSCAN_REC_SIZE 256
+#define ZSR_VERSION_REC_SIZE 16
+#define ZSTAND_BY 2
+#define ZTABLESIZE 16
+#define ZTABMAXINDEX 3
+#define ZUNDEFINED_OP 6
+#define ZUNDOHEADSIZE 7
+#define ZUNLOCKED 1
+#define ZUNDOPAGE_BASE_ADD 2
+#define ZUNDOPAGEINDEXBITS 13
+#define ZUNDOPAGEINDEX_MASK 0x1fff
+#define ZWRITEPAGESIZE 8
+#define ZWRITE_UNDOPAGESIZE 2
+#define ZMIN_UNDO_PAGES_AT_COMMIT 4
+#define ZMIN_UNDO_PAGES_AT_OPERATION 10
+#define ZMIN_UNDO_PAGES_AT_EXPAND 16
+
+/* --------------------------------------------------------------------------------- */
+/* CONTINUEB CODES */
+/* --------------------------------------------------------------------------------- */
+#define ZLOAD_BAL_LCP_TIMER 0
+#define ZINITIALISE_RECORDS 1
+#define ZSR_READ_PAGES_ALLOC 2
+#define ZSTART_UNDO 3
+#define ZSEND_SCAN_HBREP 4
+#define ZREL_ROOT_FRAG 5
+#define ZREL_FRAG 6
+#define ZREL_DIR 7
+#define ZREPORT_MEMORY_USAGE 8
+#define ZLCP_OP_WRITE_RT_BREAK 9
+
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES */
+/* ------------------------------------------------------------------------- */
+#define ZLIMIT_OF_ERROR 600 // Limit check for error codes
+#define ZCHECKROOT_ERROR 601 // Delete fragment error code
+#define ZCONNECT_SIZE_ERROR 602 // ACC_SEIZEREF
+#define ZDIR_RANGE_ERROR 603 // Add fragment error code
+#define ZFULL_FRAGRECORD_ERROR 604 // Add fragment error code
+#define ZFULL_ROOTFRAGRECORD_ERROR 605 // Add fragment error code
+#define ZROOTFRAG_STATE_ERROR 606 // Add fragment
+#define ZOVERTAB_REC_ERROR 607 // Add fragment
+
+#define ZSCAN_REFACC_CONNECT_ERROR 608 // ACC_SCANREF
+#define ZFOUR_ACTIVE_SCAN_ERROR 609 // ACC_SCANREF
+#define ZNULL_SCAN_REC_ERROR 610 // ACC_SCANREF
+
+#define ZDIRSIZE_ERROR 623
+#define ZOVER_REC_ERROR 624 // Insufficient Space
+#define ZPAGESIZE_ERROR 625
+#define ZTUPLE_DELETED_ERROR 626
+#define ZREAD_ERROR 626
+#define ZWRITE_ERROR 630
+#define ZTO_OP_STATE_ERROR 631
+#define ZTOO_EARLY_ACCESS_ERROR 632
+#define ZTEMPORARY_ACC_UNDO_FAILURE 677
+#endif
+
+class ElementHeader {
+ /**
+ *
+ * l = Locked -- If true contains operation else scan bits + hash value
+ * s = Scan bits
+ * h = Hash value
+ * o = Operation ptr I
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * lssssssssssss hhhhhhhhhhhhhhhh
+ * ooooooooooooooooooooooooooooooo
+ */
+public:
+ STATIC_CONST( HASH_VALUE_PART_MASK = 0xFFFF );
+
+ static bool getLocked(Uint32 data);
+ static bool getUnlocked(Uint32 data);
+ static Uint32 getScanBits(Uint32 data);
+ static Uint32 getHashValuePart(Uint32 data);
+ static Uint32 getOpPtrI(Uint32 data);
+
+ static Uint32 setLocked(Uint32 opPtrI);
+ static Uint32 setUnlocked(Uint32 hashValuePart, Uint32 scanBits);
+ static Uint32 setScanBit(Uint32 header, Uint32 scanBit);
+ static Uint32 clearScanBit(Uint32 header, Uint32 scanBit);
+};
+
+inline
+bool
+ElementHeader::getLocked(Uint32 data){
+ return (data & 1) == 0;
+}
+
+inline
+bool
+ElementHeader::getUnlocked(Uint32 data){
+ return (data & 1) == 1;
+}
+
+inline
+Uint32
+ElementHeader::getScanBits(Uint32 data){
+ assert(getUnlocked(data));
+ return (data >> 1) & ((1 << MAX_PARALLEL_SCANS_PER_FRAG) - 1);
+}
+
+inline
+Uint32
+ElementHeader::getHashValuePart(Uint32 data){
+ assert(getUnlocked(data));
+ return data >> 16;
+}
+
+inline
+Uint32
+ElementHeader::getOpPtrI(Uint32 data){
+ assert(getLocked(data));
+ return data >> 1;
+}
+
+inline
+Uint32
+ElementHeader::setLocked(Uint32 opPtrI){
+ return (opPtrI << 1) + 0;
+}
+inline
+Uint32
+ElementHeader::setUnlocked(Uint32 hashValue, Uint32 scanBits){
+ return (hashValue << 16) + (scanBits << 1) + 1;
+}
+
+inline
+Uint32
+ElementHeader::setScanBit(Uint32 header, Uint32 scanBit){
+ assert(getUnlocked(header));
+ return header | (scanBit << 1);
+}
+
+inline
+Uint32
+ElementHeader::clearScanBit(Uint32 header, Uint32 scanBit){
+ assert(getUnlocked(header));
+ return header & (~(scanBit << 1));
+}
+
+
+class Dbacc: public SimulatedBlock {
+public:
+// State values
+enum State {
+ FREEFRAG = 0,
+ ACTIVEFRAG = 1,
+ SEND_QUE_OP = 2,
+ WAIT_ACC_LCPREQ = 3,
+ LCP_SEND_PAGES = 4,
+ LCP_SEND_OVER_PAGES = 5,
+ LCP_SEND_ZERO_PAGE = 6,
+ SR_READ_PAGES = 7,
+ SR_READ_OVER_PAGES = 8,
+ WAIT_ZERO_PAGE_STORED = 9,
+ WAIT_NOTHING = 10,
+ WAIT_OPEN_UNDO_LCP = 11,
+ WAIT_OPEN_UNDO_LCP_NEXT = 12,
+ WAIT_OPEN_DATA_FILE_FOR_READ = 13,
+ WAIT_OPEN_DATA_FILE_FOR_WRITE = 14,
+ OPEN_UNDO_FILE_SR = 15,
+ READ_UNDO_PAGE = 16,
+ READ_UNDO_PAGE_AND_CLOSE = 17,
+ WAIT_READ_DATA = 18,
+ WAIT_READ_PAGE_ZERO = 19,
+ WAIT_WRITE_DATA = 20,
+ WAIT_WRITE_UNDO = 21,
+ WAIT_WRITE_UNDO_EXIT = 22,
+ WAIT_CLOSE_UNDO = 23,
+ LCP_CLOSE_DATA = 24,
+ SR_CLOSE_DATA = 25,
+ WAIT_ONE_CONF = 26,
+ WAIT_TWO_CONF = 27,
+ LCP_FREE = 28,
+ LCP_ACTIVE = 29,
+ FREE_OP = 30,
+ WAIT_EXE_OP = 32,
+ WAIT_IN_QUEUE = 34,
+ EXE_OP = 35,
+ SCAN_ACTIVE = 36,
+ SCAN_WAIT_IN_QUEUE = 37,
+ IDLE = 39,
+ ACTIVE = 40,
+ WAIT_COMMIT_ABORT = 41,
+ ABORT = 42,
+ ABORTADDFRAG = 43,
+ REFUSEADDFRAG = 44,
+ DELETEFRAG = 45,
+ DELETETABLE = 46,
+ UNDEFINEDROOT = 47,
+ ADDFIRSTFRAG = 48,
+ ADDSECONDFRAG = 49,
+ DELETEFIRSTFRAG = 50,
+ DELETESECONDFRAG = 51,
+ ACTIVEROOT = 52,
+ LCP_CREATION = 53
+};
+
+// Records
+
+/* --------------------------------------------------------------------------------- */
+/* UNDO HEADER RECORD */
+/* --------------------------------------------------------------------------------- */
+
+ struct UndoHeader {
+ enum UndoHeaderType{
+ ZPAGE_INFO = 0,
+ ZOVER_PAGE_INFO = 1,
+ ZOP_INFO = 2,
+ ZNO_UNDORECORD_TYPES = 3
+ };
+ UintR tableId;
+ UintR rootFragId;
+ UintR localFragId;
+ UintR variousInfo;
+ UintR logicalPageId;
+ UintR prevUndoAddressForThisFrag;
+ UintR prevUndoAddress;
+ };
+
+/* --------------------------------------------------------------------------------- */
+/* DIRECTORY RANGE */
+/* --------------------------------------------------------------------------------- */
+ struct DirRange {
+ Uint32 dirArray[256];
+ }; /* p2c: size = 1024 bytes */
+
+ typedef Ptr<DirRange> DirRangePtr;
+
+/* --------------------------------------------------------------------------------- */
+/* DIRECTORYARRAY */
+/* --------------------------------------------------------------------------------- */
+struct Directoryarray {
+ Uint32 pagep[256];
+}; /* p2c: size = 1024 bytes */
+
+ typedef Ptr<Directoryarray> DirectoryarrayPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* FRAGMENTREC. ALL INFORMATION ABOUT FRAMENT AND HASH TABLE IS SAVED IN FRAGMENT */
+/* REC A POINTER TO FRAGMENT RECORD IS SAVED IN ROOTFRAGMENTREC FRAGMENT */
+/* --------------------------------------------------------------------------------- */
+struct Fragmentrec {
+//-----------------------------------------------------------------------------
+// References to long key pages with free area. Some type of buddy structure
+// where references in higher index have more free space.
+//-----------------------------------------------------------------------------
+ Uint32 longKeyPageArray[4];
+
+//-----------------------------------------------------------------------------
+// These variables keep track of allocated pages, the number of them and the
+// start file page of them. Used during local checkpoints.
+//-----------------------------------------------------------------------------
+ Uint32 datapages[8];
+ Uint32 activeDataPage;
+ Uint32 activeDataFilePage;
+
+//-----------------------------------------------------------------------------
+// Temporary variables used during shrink and expand process.
+//-----------------------------------------------------------------------------
+ Uint32 expReceivePageptr;
+ Uint32 expReceiveIndex;
+ Uint32 expReceiveForward;
+ Uint32 expSenderDirIndex;
+ Uint32 expSenderDirptr;
+ Uint32 expSenderIndex;
+ Uint32 expSenderPageptr;
+
+//-----------------------------------------------------------------------------
+// List of lock owners and list of lock waiters to support LCP handling
+//-----------------------------------------------------------------------------
+ Uint32 lockOwnersList;
+ Uint32 firstWaitInQueOp;
+ Uint32 lastWaitInQueOp;
+ Uint32 sentWaitInQueOp;
+
+//-----------------------------------------------------------------------------
+// References to Directory Ranges (which in turn references directories, which
+// in its turn references the pages) for the bucket pages and the overflow
+// bucket pages.
+//-----------------------------------------------------------------------------
+ Uint32 directory;
+ Uint32 dirsize;
+ Uint32 overflowdir;
+ Uint32 lastOverIndex;
+
+//-----------------------------------------------------------------------------
+// These variables are used to support LCP and Restore from disk.
+// lcpDirIndex: used during LCP as the frag page id currently stored.
+// lcpMaxDirIndex: The dirsize at start of LCP.
+// lcpMaxOverDirIndex: The xx at start of LCP
+// During a LCP one writes the minimum of the number of pages in the directory
+// and the number of pages at the start of the LCP.
+// noStoredPages: Number of bucket pages written in LCP used at restore
+// noOfOverStoredPages: Number of overflow pages written in LCP used at restore
+// This variable is also used during LCP to calculate this number.
+//-----------------------------------------------------------------------------
+ Uint32 lcpDirIndex;
+ Uint32 lcpMaxDirIndex;
+ Uint32 lcpMaxOverDirIndex;
+ Uint32 noStoredPages;
+ Uint32 noOfStoredOverPages;
+
+//-----------------------------------------------------------------------------
+// We have a list of overflow pages with free areas. We have a special record,
+// the overflow record representing these pages. The reason is that the
+// same record is also used to represent pages in the directory array that have
+// been released since they were empty (there were however higher indexes with
+// data in them). These are put in the firstFreeDirIndexRec-list.
+// An overflow record representing a page can only be in one of these lists.
+//-----------------------------------------------------------------------------
+ Uint32 firstOverflowRec;
+ Uint32 lastOverflowRec;
+ Uint32 firstFreeDirindexRec;
+
+//-----------------------------------------------------------------------------
+// localCheckpId is used during execution of UNDO log to ensure that we only
+// apply UNDO log records from the restored LCP of the fragment.
+// lcpLqhPtr keeps track of LQH record for this fragment to checkpoint
+//-----------------------------------------------------------------------------
+ Uint32 localCheckpId;
+ Uint32 lcpLqhPtr;
+
+//-----------------------------------------------------------------------------
+// Counter keeping track of how many times we have expanded. We need to ensure
+// that we do not shrink so many times that this variable becomes negative.
+//-----------------------------------------------------------------------------
+ Uint32 expandCounter;
+//-----------------------------------------------------------------------------
+// Reference to record for open file at LCP and restore
+//-----------------------------------------------------------------------------
+ Uint32 fsConnPtr;
+
+//-----------------------------------------------------------------------------
+// These variables are important for the linear hashing algorithm.
+// localkeylen is the size of the local key (1 and 2 is currently supported)
+// maxloadfactor is the factor specifying when to expand
+// minloadfactor is the factor specifying when to shrink (hysteresis model)
+// maxp and p
+// maxp and p is the variables most central to linear hashing. p + maxp + 1 is the
+// current number of buckets. maxp is the largest value of the type 2**n - 1
+// which is smaller than the number of buckets. These values are used to find
+// correct bucket with the aid of the hash value.
+//
+// slack is the variable keeping track of whether we have inserted more than
+// the current size is suitable for or less. Slack together with the boundaries
+// set by maxloadfactor and minloadfactor decides when to expand/shrink
+// slackCheck When slack goes over this value it is time to expand.
+// slackCheck = (maxp + p + 1)*(maxloadfactor - minloadfactor) or
+// bucketSize * hysteresis
+//-----------------------------------------------------------------------------
+ Uint32 localkeylen;
+ Uint32 maxp;
+ Uint32 maxloadfactor;
+ Uint32 minloadfactor;
+ Uint32 p;
+ Uint32 slack;
+ Uint32 slackCheck;
+
+//-----------------------------------------------------------------------------
+// myfid is the fragment id of the fragment
+// myroot is the reference to the root fragment record
+// nextfreefrag is the next free fragment if linked into a free list
+//-----------------------------------------------------------------------------
+ Uint32 myfid;
+ Uint32 myroot;
+ Uint32 myTableId;
+ Uint32 nextfreefrag;
+
+//-----------------------------------------------------------------------------
+// This variable is used during restore to keep track of page id of read pages.
+// During read of bucket pages this is used to calculate the page id and also
+// to verify that the page id of the read page is correct. During read of over-
+// flow pages it is only used to keep track of the number of pages read.
+//-----------------------------------------------------------------------------
+ Uint32 nextAllocPage;
+
+//-----------------------------------------------------------------------------
+// Keeps track of undo position for fragment during LCP and restore.
+//-----------------------------------------------------------------------------
+ Uint32 prevUndoposition;
+
+//-----------------------------------------------------------------------------
+// Page reference during LCP and restore of page zero where fragment data is
+// saved
+//-----------------------------------------------------------------------------
+ Uint32 zeroPagePtr;
+
+//-----------------------------------------------------------------------------
+// Number of pages read from file during restore
+//-----------------------------------------------------------------------------
+ Uint32 noOfExpectedPages;
+
+//-----------------------------------------------------------------------------
+// Fragment State, mostly applicable during LCP and restore
+//-----------------------------------------------------------------------------
+ State fragState;
+
+//-----------------------------------------------------------------------------
+// Keep track of number of outstanding writes of UNDO log records to ensure that
+// we have saved all UNDO info before concluding local checkpoint.
+//-----------------------------------------------------------------------------
+ Uint32 nrWaitWriteUndoExit;
+
+//-----------------------------------------------------------------------------
+// lastUndoIsStored is used to handle parallel writes of UNDO log and pages to
+// know when LCP is completed
+//-----------------------------------------------------------------------------
+ Uint8 lastUndoIsStored;
+
+//-----------------------------------------------------------------------------
+// Set to ZTRUE when local checkpoint freeze occurs and set to ZFALSE when
+// local checkpoint concludes.
+//-----------------------------------------------------------------------------
+ Uint8 createLcp;
+
+//-----------------------------------------------------------------------------
+// Flag indicating whether we are in the load phase of restore still.
+//-----------------------------------------------------------------------------
+ Uint8 loadingFlag;
+
+//-----------------------------------------------------------------------------
+// elementLength: Length of element in bucket and overflow pages
+// keyLength: Length of key
+//-----------------------------------------------------------------------------
+ Uint8 elementLength;
+ Uint16 keyLength;
+
+//-----------------------------------------------------------------------------
+// This flag is used to avoid sending a big number of expand or shrink signals
+// when simultaneously committing many inserts or deletes.
+//-----------------------------------------------------------------------------
+ Uint8 expandFlag;
+
+//-----------------------------------------------------------------------------
+// hashcheckbit is the bit to check whether to send element to split bucket or not
+// k (== 6) is the number of buckets per page
+// lhfragbits is the number of bits used to calculate the fragment id
+// lhdirbits is the number of bits used to calculate the page id
+//-----------------------------------------------------------------------------
+ Uint8 hashcheckbit;
+ Uint8 k;
+ Uint8 lhfragbits;
+ Uint8 lhdirbits;
+
+//-----------------------------------------------------------------------------
+// nodetype can only be STORED in this release. Is currently only set, never read
+// stopQueOp is indicator that locked operations will not start until LCP have
+// released the lock on the fragment
+//-----------------------------------------------------------------------------
+ Uint8 nodetype;
+ Uint8 stopQueOp;
+
+//-----------------------------------------------------------------------------
+// flag to avoid accessing table record if no char attributes
+//-----------------------------------------------------------------------------
+ Uint8 hasCharAttr;
+};
+
+ typedef Ptr<Fragmentrec> FragmentrecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* FS_CONNECTREC */
+/* --------------------------------------------------------------------------------- */
+struct FsConnectrec {
+ Uint32 fsNext;
+ Uint32 fsPrev;
+ Uint32 fragrecPtr;
+ Uint32 fsPtr;
+ State fsState;
+ Uint8 activeFragId;
+ Uint8 fsPart;
+}; /* p2c: size = 24 bytes */
+
+ typedef Ptr<FsConnectrec> FsConnectrecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* FS_OPREC */
+/* --------------------------------------------------------------------------------- */
+struct FsOprec {
+ Uint32 fsOpnext;
+ Uint32 fsOpfragrecPtr;
+ Uint32 fsConptr;
+ State fsOpstate;
+ Uint16 fsOpMemPage;
+}; /* p2c: size = 20 bytes */
+
+ typedef Ptr<FsOprec> FsOprecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* LCP_CONNECTREC */
+/* --------------------------------------------------------------------------------- */
+struct LcpConnectrec {
+ Uint32 nextLcpConn;
+ Uint32 lcpUserptr;
+ Uint32 rootrecptr;
+ State syncUndopageState;
+ State lcpstate;
+ Uint32 lcpUserblockref;
+ Uint16 localCheckPid;
+ Uint8 noOfLcpConf;
+};
+ typedef Ptr<LcpConnectrec> LcpConnectrecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* OPERATIONREC */
+/* --------------------------------------------------------------------------------- */
+struct Operationrec {
+ Uint32 keydata[8];
+ Uint32 localdata[2];
+ Uint32 elementIsforward;
+ Uint32 elementPage;
+ Uint32 elementPointer;
+ Uint32 fid;
+ Uint32 fragptr;
+ Uint32 hashvaluePart;
+ Uint32 hashValue;
+ Uint32 insertDeleteLen;
+ Uint32 keyinfoPage;
+ Uint32 nextLockOwnerOp;
+ Uint32 nextOp;
+ Uint32 nextParallelQue;
+ Uint32 nextQueOp;
+ Uint32 nextSerialQue;
+ Uint32 prevOp;
+ Uint32 prevLockOwnerOp;
+ Uint32 prevParallelQue;
+ Uint32 prevQueOp;
+ Uint32 prevSerialQue;
+ Uint32 scanRecPtr;
+ Uint32 transId1;
+ Uint32 transId2;
+ Uint32 longPagePtr;
+ Uint32 longKeyPageIndex;
+ State opState;
+ Uint32 userptr;
+ State transactionstate;
+ Uint16 elementContainer;
+ Uint16 tupkeylen;
+ Uint32 xfrmtupkeylen;
+ Uint32 userblockref;
+ Uint32 scanBits;
+ Uint8 elementIsDisappeared;
+ Uint8 insertIsDone;
+ Uint8 lockMode;
+ Uint8 lockOwner;
+ Uint8 nodeType;
+ Uint8 operation;
+ Uint8 opSimple;
+ Uint8 dirtyRead;
+ Uint8 commitDeleteCheckFlag;
+ Uint8 isAccLockReq;
+ Uint8 isUndoLogReq;
+}; /* p2c: size = 168 bytes */
+
+ typedef Ptr<Operationrec> OperationrecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* OVERFLOW_RECORD */
+/* --------------------------------------------------------------------------------- */
+struct OverflowRecord {
+ Uint32 dirindex;
+ Uint32 nextOverRec;
+ Uint32 nextOverList;
+ Uint32 prevOverRec;
+ Uint32 prevOverList;
+ Uint32 overpage;
+ Uint32 nextfreeoverrec;
+};
+
+ typedef Ptr<OverflowRecord> OverflowRecordPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* PAGE8 */
+/* --------------------------------------------------------------------------------- */
+struct Page8 {
+ Uint32 word32[2048];
+}; /* p2c: size = 8192 bytes */
+
+ typedef Ptr<Page8> Page8Ptr;
+
+/* --------------------------------------------------------------------------------- */
+/* ROOTFRAGMENTREC */
+/* DURING EXPAND FRAGMENT PROCESS, EACH FRAGMEND WILL BE EXPAND INTO TWO */
+/* NEW FRAGMENTS.TO MAKE THIS PROCESS EASIER, DURING ADD FRAGMENT PROCESS */
+/* NEXT FRAGMENT IDENTIIES WILL BE CALCULATED, AND TWO FRAGMENTS WILL BE */
+/* ADDED IN (NDBACC). THEREBY EXPAND OF FRAGMENT CAN BE PERFORMED QUICK AND */
+/* EASY.THE NEW FRAGMENT ID SENDS TO TUP MANAGER FOR ALL OPERATION PROCESS. */
+/* --------------------------------------------------------------------------------- */
+struct Rootfragmentrec {
+ Uint32 scan[MAX_PARALLEL_SCANS_PER_FRAG];
+ Uint32 fragmentptr[2];
+ Uint32 fragmentid[2];
+ Uint32 lcpPtr;
+ Uint32 mytabptr;
+ Uint32 nextroot;
+ Uint32 roothashcheck;
+ Uint32 noOfElements;
+ Uint32 m_commit_count;
+ State rootState;
+}; /* p2c: size = 72 bytes */
+
+ typedef Ptr<Rootfragmentrec> RootfragmentrecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* SCAN_REC */
+/* --------------------------------------------------------------------------------- */
+struct ScanRec {
+ enum ScanState {
+ WAIT_NEXT,
+ SCAN_DISCONNECT
+ };
+ enum ScanBucketState {
+ FIRST_LAP,
+ SECOND_LAP,
+ SCAN_COMPLETED
+ };
+ Uint32 activeLocalFrag;
+ Uint32 rootPtr;
+ Uint32 nextBucketIndex;
+ Uint32 scanNextfreerec;
+ Uint32 scanFirstActiveOp;
+ Uint32 scanFirstLockedOp;
+ Uint32 scanLastLockedOp;
+ Uint32 scanFirstQueuedOp;
+ Uint32 scanLastQueuedOp;
+ Uint32 scanUserptr;
+ Uint32 scanTrid1;
+ Uint32 scanTrid2;
+ Uint32 startNoOfBuckets;
+ Uint32 minBucketIndexToRescan;
+ Uint32 maxBucketIndexToRescan;
+ Uint32 scanOpsAllocated;
+ ScanBucketState scanBucketState;
+ ScanState scanState;
+ Uint16 scanLockHeld;
+ Uint32 scanUserblockref;
+ Uint32 scanMask;
+ Uint8 scanLockMode;
+ Uint8 scanKeyinfoFlag;
+ Uint8 scanTimer;
+ Uint8 scanContinuebCounter;
+ Uint8 scanReadCommittedFlag;
+};
+
+ typedef Ptr<ScanRec> ScanRecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* SR_VERSION_REC */
+/* --------------------------------------------------------------------------------- */
+struct SrVersionRec {
+ Uint32 nextFreeSr;
+ Uint32 checkPointId;
+ Uint32 prevAddress;
+ Uint32 srUnused; /* p2c: Not used */
+}; /* p2c: size = 16 bytes */
+
+ typedef Ptr<SrVersionRec> SrVersionRecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* TABREC */
+/* --------------------------------------------------------------------------------- */
+struct Tabrec {
+ Uint32 fragholder[MAX_FRAG_PER_NODE];
+ Uint32 fragptrholder[MAX_FRAG_PER_NODE];
+ Uint32 tabUserPtr;
+ BlockReference tabUserRef;
+
+ Uint8 noOfKeyAttr;
+ Uint8 hasCharAttr;
+ struct KeyAttr {
+ Uint32 attributeDescriptor;
+ CHARSET_INFO* charsetInfo;
+ } keyAttr[MAX_ATTRIBUTES_IN_INDEX];
+};
+ typedef Ptr<Tabrec> TabrecPtr;
+
+/* --------------------------------------------------------------------------------- */
+/* UNDOPAGE */
+/* --------------------------------------------------------------------------------- */
+struct Undopage {
+ Uint32 undoword[8192];
+}; /* p2c: size = 32768 bytes */
+
+ typedef Ptr<Undopage> UndopagePtr;
+
+public:
+ Dbacc(const class Configuration &);
+ virtual ~Dbacc();
+
+ // pointer to TUP instance in this thread
+ Dbtup* c_tup;
+
+private:
+ BLOCK_DEFINES(Dbacc);
+
+ // Transit signals
+ void execDEBUG_SIG(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+ void execACC_CHECK_SCAN(Signal* signal);
+ void execEXPANDCHECK2(Signal* signal);
+ void execSHRINKCHECK2(Signal* signal);
+ void execACC_OVER_REC(Signal* signal);
+ void execACC_SAVE_PAGES(Signal* signal);
+ void execNEXTOPERATION(Signal* signal);
+ void execREAD_PSUEDO_REQ(Signal* signal);
+
+ // Received signals
+ void execSTTOR(Signal* signal);
+ void execSR_FRAGIDREQ(Signal* signal);
+ void execLCP_FRAGIDREQ(Signal* signal);
+ void execLCP_HOLDOPREQ(Signal* signal);
+ void execEND_LCPREQ(Signal* signal);
+ void execACC_LCPREQ(Signal* signal);
+ void execSTART_RECREQ(Signal* signal);
+ void execACC_CONTOPREQ(Signal* signal);
+ void execACCKEYREQ(Signal* signal);
+ void execACCSEIZEREQ(Signal* signal);
+ void execACCFRAGREQ(Signal* signal);
+ void execTC_SCHVERREQ(Signal* signal);
+ void execACC_SRREQ(Signal* signal);
+ void execNEXT_SCANREQ(Signal* signal);
+ void execACC_ABORTREQ(Signal* signal);
+ void execACC_SCANREQ(Signal* signal);
+ void execACCMINUPDATE(Signal* signal);
+ void execACC_COMMITREQ(Signal* signal);
+ void execACC_TO_REQ(Signal* signal);
+ void execACC_LOCKREQ(Signal* signal);
+ void execFSOPENCONF(Signal* signal);
+ void execFSOPENREF(Signal* signal);
+ void execFSCLOSECONF(Signal* signal);
+ void execFSCLOSEREF(Signal* signal);
+ void execFSWRITECONF(Signal* signal);
+ void execFSWRITEREF(Signal* signal);
+ void execFSREADCONF(Signal* signal);
+ void execFSREADREF(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+ void execDROP_TAB_REQ(Signal* signal);
+ void execFSREMOVECONF(Signal* signal);
+ void execFSREMOVEREF(Signal* signal);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+ void execDUMP_STATE_ORD(Signal* signal);
+
+ // Statement blocks
+ void ACCKEY_error(Uint32 fromWhere);
+
+ void commitDeleteCheck();
+
+ void initRootFragPageZero(RootfragmentrecPtr, Page8Ptr);
+ void initRootFragSr(RootfragmentrecPtr, Page8Ptr);
+ void initFragAdd(Signal*, Uint32 rootFragIndex, Uint32 rootIndex, FragmentrecPtr);
+ void initFragPageZero(FragmentrecPtr, Page8Ptr);
+ void initFragSr(FragmentrecPtr, Page8Ptr);
+ void initFragGeneral(FragmentrecPtr);
+ void verifyFragCorrect(FragmentrecPtr regFragPtr);
+ void sendFSREMOVEREQ(Signal* signal, Uint32 tableId);
+ void releaseFragResources(Signal* signal, Uint32 fragIndex);
+ void releaseRootFragRecord(Signal* signal, RootfragmentrecPtr rootPtr);
+ void releaseRootFragResources(Signal* signal, Uint32 tableId);
+ void releaseDirResources(Signal* signal,
+ Uint32 fragIndex,
+ Uint32 dirIndex,
+ Uint32 startIndex);
+ void releaseDirectoryResources(Signal* signal,
+ Uint32 fragIndex,
+ Uint32 dirIndex,
+ Uint32 startIndex,
+ Uint32 directoryIndex);
+ void releaseOverflowResources(Signal* signal, FragmentrecPtr regFragPtr);
+ void releaseDirIndexResources(Signal* signal, FragmentrecPtr regFragPtr);
+ void releaseFragRecord(Signal* signal, FragmentrecPtr regFragPtr);
+ Uint32 remainingUndoPages();
+ void updateLastUndoPageIdWritten(Signal* signal, Uint32 aNewValue);
+ void updateUndoPositionPage(Signal* signal, Uint32 aNewValue);
+ void srCheckPage(Signal* signal);
+ void srCheckContainer(Signal* signal);
+ void initScanFragmentPart(Signal* signal);
+ Uint32 checkScanExpand(Signal* signal);
+ Uint32 checkScanShrink(Signal* signal);
+ void initialiseDirRec(Signal* signal);
+ void initialiseDirRangeRec(Signal* signal);
+ void initialiseFragRec(Signal* signal);
+ void initialiseFsConnectionRec(Signal* signal);
+ void initialiseFsOpRec(Signal* signal);
+ void initialiseLcpConnectionRec(Signal* signal);
+ void initialiseOperationRec(Signal* signal);
+ void initialiseOverflowRec(Signal* signal);
+ void initialisePageRec(Signal* signal);
+ void initialiseLcpPages(Signal* signal);
+ void initialiseRootfragRec(Signal* signal);
+ void initialiseScanRec(Signal* signal);
+ void initialiseSrVerRec(Signal* signal);
+ void initialiseTableRec(Signal* signal);
+ bool addfragtotab(Signal* signal, Uint32 rootIndex, Uint32 fragId);
+ void initOpRec(Signal* signal);
+ void sendAcckeyconf(Signal* signal);
+ Uint32 placeReadInLockQueue(Signal* signal);
+ void placeSerialQueueRead(Signal* signal);
+ void checkOnlyReadEntry(Signal* signal);
+ void getNoParallelTransaction(Signal* signal);
+ void moveLastParallelQueue(Signal* signal);
+ void moveLastParallelQueueWrite(Signal* signal);
+ Uint32 placeWriteInLockQueue(Signal* signal);
+ void placeSerialQueueWrite(Signal* signal);
+ void expandcontainer(Signal* signal);
+ void shrinkcontainer(Signal* signal);
+ void nextcontainerinfoExp(Signal* signal);
+ void lcpCopyPage(Signal* signal);
+ void lcpUpdatePage(Signal* signal);
+ void checkUndoPages(Signal* signal);
+ void undoWritingProcess(Signal* signal);
+ void writeUndoDataInfo(Signal* signal);
+ void writeUndoHeader(Signal* signal,
+ Uint32 logicalPageId,
+ UndoHeader::UndoHeaderType pageType);
+ void writeUndoOpInfo(Signal* signal);
+ void checksumControl(Signal* signal, Uint32 checkPage);
+ void startActiveUndo(Signal* signal);
+ void releaseAndCommitActiveOps(Signal* signal);
+ void releaseAndCommitQueuedOps(Signal* signal);
+ void releaseAndAbortLockedOps(Signal* signal);
+ void containerinfo(Signal* signal);
+ bool getScanElement(Signal* signal);
+ void initScanOpRec(Signal* signal);
+ void nextcontainerinfo(Signal* signal);
+ void putActiveScanOp(Signal* signal);
+ void putOpScanLockQue();
+ void putReadyScanQueue(Signal* signal, Uint32 scanRecIndex);
+ void releaseScanBucket(Signal* signal);
+ void releaseScanContainer(Signal* signal);
+ void releaseScanRec(Signal* signal);
+ bool searchScanContainer(Signal* signal);
+ void sendNextScanConf(Signal* signal);
+ void setlock(Signal* signal);
+ void takeOutActiveScanOp(Signal* signal);
+ void takeOutScanLockQueue(Uint32 scanRecIndex);
+ void takeOutReadyScanQueue(Signal* signal);
+ void insertElement(Signal* signal);
+ void insertContainer(Signal* signal);
+ void addnewcontainer(Signal* signal);
+ void getfreelist(Signal* signal);
+ void increaselistcont(Signal* signal);
+ void seizeLeftlist(Signal* signal);
+ void seizeRightlist(Signal* signal);
+ Uint32 readTablePk(Uint32 localkey1);
+ void getElement(Signal* signal);
+ void getdirindex(Signal* signal);
+ void commitdelete(Signal* signal, bool systemRestart);
+ void deleteElement(Signal* signal);
+ void getLastAndRemove(Signal* signal);
+ void releaseLeftlist(Signal* signal);
+ void releaseRightlist(Signal* signal);
+ void checkoverfreelist(Signal* signal);
+ void abortOperation(Signal* signal);
+ void accAbortReqLab(Signal* signal, bool sendConf);
+ void commitOperation(Signal* signal);
+ void copyOpInfo(Signal* signal);
+ Uint32 executeNextOperation(Signal* signal);
+ void releaselock(Signal* signal);
+ void takeOutFragWaitQue(Signal* signal);
+ void allocOverflowPage(Signal* signal);
+ bool getrootfragmentrec(Signal* signal, RootfragmentrecPtr&, Uint32 fragId);
+ void insertLockOwnersList(Signal* signal, const OperationrecPtr&);
+ void takeOutLockOwnersList(Signal* signal, const OperationrecPtr&);
+ void initFsOpRec(Signal* signal);
+ void initLcpConnRec(Signal* signal);
+ void initOverpage(Signal* signal);
+ void initPage(Signal* signal);
+ void initRootfragrec(Signal* signal);
+ void putOpInFragWaitQue(Signal* signal);
+ void putOverflowRecInFrag(Signal* signal);
+ void putRecInFreeOverdir(Signal* signal);
+ void releaseDirectory(Signal* signal);
+ void releaseDirrange(Signal* signal);
+ void releaseFsConnRec(Signal* signal);
+ void releaseFsOpRec(Signal* signal);
+ void releaseLcpConnectRec(Signal* signal);
+ void releaseOpRec(Signal* signal);
+ void releaseOverflowRec(Signal* signal);
+ void releaseOverpage(Signal* signal);
+ void releasePage(Signal* signal);
+ void releaseLcpPage(Signal* signal);
+ void releaseSrRec(Signal* signal);
+ void releaseLogicalPage(Fragmentrec * fragP, Uint32 logicalPageId);
+ void seizeDirectory(Signal* signal);
+ void seizeDirrange(Signal* signal);
+ void seizeFragrec(Signal* signal);
+ void seizeFsConnectRec(Signal* signal);
+ void seizeFsOpRec(Signal* signal);
+ void seizeLcpConnectRec(Signal* signal);
+ void seizeOpRec(Signal* signal);
+ void seizeOverRec(Signal* signal);
+ void seizePage(Signal* signal);
+ void seizeLcpPage(Page8Ptr&);
+ void seizeRootfragrec(Signal* signal);
+ void seizeScanRec(Signal* signal);
+ void seizeSrVerRec(Signal* signal);
+ void sendSystemerror(Signal* signal);
+ void takeRecOutOfFreeOverdir(Signal* signal);
+ void takeRecOutOfFreeOverpage(Signal* signal);
+ void sendScanHbRep(Signal* signal, Uint32);
+
+ void addFragRefuse(Signal* signal, Uint32 errorCode);
+ void ndbsttorryLab(Signal* signal);
+ void srCloseDataFileLab(Signal* signal);
+ void acckeyref1Lab(Signal* signal, Uint32 result_code);
+ void insertelementLab(Signal* signal);
+ void startUndoLab(Signal* signal);
+ void checkNextFragmentLab(Signal* signal);
+ void endofexpLab(Signal* signal);
+ void endofshrinkbucketLab(Signal* signal);
+ void srStartUndoLab(Signal* signal);
+ void senddatapagesLab(Signal* signal);
+ void undoNext2Lab(Signal* signal);
+ void sttorrysignalLab(Signal* signal);
+ void sendholdconfsignalLab(Signal* signal);
+ void accIsLockedLab(Signal* signal);
+ void insertExistElemLab(Signal* signal);
+ void refaccConnectLab(Signal* signal);
+ void srReadOverPagesLab(Signal* signal);
+ void releaseScanLab(Signal* signal);
+ void lcpOpenUndofileConfLab(Signal* signal);
+ void srFsOpenConfLab(Signal* signal);
+ void checkSyncUndoPagesLab(Signal* signal);
+ void sendaccSrconfLab(Signal* signal);
+ void checkSendLcpConfLab(Signal* signal);
+ void endsaveoverpageLab(Signal* signal);
+ void lcpCloseDataFileLab(Signal* signal);
+ void srOpenDataFileLoopLab(Signal* signal);
+ void srReadPagesLab(Signal* signal);
+ void srDoUndoLab(Signal* signal);
+ void ndbrestart1Lab(Signal* signal);
+ void initialiseRecordsLab(Signal* signal, Uint32 ref, Uint32 data);
+ void srReadPagesAllocLab(Signal* signal);
+ void checkNextBucketLab(Signal* signal);
+ void endsavepageLab(Signal* signal);
+ void saveZeroPageLab(Signal* signal);
+ void srAllocPage0011Lab(Signal* signal);
+ void sendLcpFragidconfLab(Signal* signal);
+ void savepagesLab(Signal* signal);
+ void saveOverPagesLab(Signal* signal);
+ void srReadPageZeroLab(Signal* signal);
+ void storeDataPageInDirectoryLab(Signal* signal);
+ void lcpFsOpenConfLab(Signal* signal);
+
+ void zpagesize_error(const char* where);
+
+ void reportMemoryUsage(Signal* signal, int gth);
+ void lcp_write_op_to_undolog(Signal* signal);
+ void reenable_expand_after_redo_log_exection_complete(Signal*);
+
+ // charsets
+ void xfrmKeyData(Signal* signal);
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ // Variables
+/* --------------------------------------------------------------------------------- */
+/* DIRECTORY RANGE */
+/* --------------------------------------------------------------------------------- */
+ DirRange *dirRange;
+ DirRangePtr expDirRangePtr;
+ DirRangePtr gnsDirRangePtr;
+ DirRangePtr newDirRangePtr;
+ DirRangePtr rdDirRangePtr;
+ DirRangePtr nciOverflowrangeptr;
+ Uint32 cdirrangesize;
+ Uint32 cfirstfreeDirrange;
+/* --------------------------------------------------------------------------------- */
+/* DIRECTORYARRAY */
+/* --------------------------------------------------------------------------------- */
+ Directoryarray *directoryarray;
+ DirectoryarrayPtr expDirptr;
+ DirectoryarrayPtr rdDirptr;
+ DirectoryarrayPtr sdDirptr;
+ DirectoryarrayPtr nciOverflowDirptr;
+ Uint32 cdirarraysize;
+ Uint32 cdirmemory;
+ Uint32 cfirstfreedir;
+/* --------------------------------------------------------------------------------- */
+/* FRAGMENTREC. ALL INFORMATION ABOUT FRAMENT AND HASH TABLE IS SAVED IN FRAGMENT */
+/* REC A POINTER TO FRAGMENT RECORD IS SAVED IN ROOTFRAGMENTREC FRAGMENT */
+/* --------------------------------------------------------------------------------- */
+ Fragmentrec *fragmentrec;
+ FragmentrecPtr fragrecptr;
+ Uint32 cfirstfreefrag;
+ Uint32 cfragmentsize;
+/* --------------------------------------------------------------------------------- */
+/* FS_CONNECTREC */
+/* --------------------------------------------------------------------------------- */
+ FsConnectrec *fsConnectrec;
+ FsConnectrecPtr fsConnectptr;
+ Uint32 cfsConnectsize;
+ Uint32 cfsFirstfreeconnect;
+/* --------------------------------------------------------------------------------- */
+/* FS_OPREC */
+/* --------------------------------------------------------------------------------- */
+ FsOprec *fsOprec;
+ FsOprecPtr fsOpptr;
+ Uint32 cfsOpsize;
+ Uint32 cfsFirstfreeop;
+/* --------------------------------------------------------------------------------- */
+/* LCP_CONNECTREC */
+/* --------------------------------------------------------------------------------- */
+ LcpConnectrec *lcpConnectrec;
+ LcpConnectrecPtr lcpConnectptr;
+ Uint32 clcpConnectsize;
+ Uint32 cfirstfreelcpConnect;
+/* --------------------------------------------------------------------------------- */
+/* OPERATIONREC */
+/* --------------------------------------------------------------------------------- */
+ Operationrec *operationrec;
+ OperationrecPtr operationRecPtr;
+ OperationrecPtr idrOperationRecPtr;
+ OperationrecPtr copyInOperPtr;
+ OperationrecPtr copyOperPtr;
+ OperationrecPtr mlpqOperPtr;
+ OperationrecPtr queOperPtr;
+ OperationrecPtr readWriteOpPtr;
+ OperationrecPtr tgnptMainOpPtr;
+ Uint32 cfreeopRec;
+ Uint32 coprecsize;
+/* --------------------------------------------------------------------------------- */
+/* OVERFLOW_RECORD */
+/* --------------------------------------------------------------------------------- */
+ OverflowRecord *overflowRecord;
+ OverflowRecordPtr iopOverflowRecPtr;
+ OverflowRecordPtr tfoOverflowRecPtr;
+ OverflowRecordPtr porOverflowRecPtr;
+ OverflowRecordPtr priOverflowRecPtr;
+ OverflowRecordPtr rorOverflowRecPtr;
+ OverflowRecordPtr sorOverflowRecPtr;
+ OverflowRecordPtr troOverflowRecPtr;
+ Uint32 cfirstfreeoverrec;
+ Uint32 coverflowrecsize;
+
+/* --------------------------------------------------------------------------------- */
+/* PAGE8 */
+/* --------------------------------------------------------------------------------- */
+ Page8 *page8;
+ /* 8 KB PAGE */
+ Page8Ptr ancPageptr;
+ Page8Ptr colPageptr;
+ Page8Ptr ccoPageptr;
+ Page8Ptr datapageptr;
+ Page8Ptr delPageptr;
+ Page8Ptr excPageptr;
+ Page8Ptr expPageptr;
+ Page8Ptr gdiPageptr;
+ Page8Ptr gePageptr;
+ Page8Ptr gflPageptr;
+ Page8Ptr idrPageptr;
+ Page8Ptr ilcPageptr;
+ Page8Ptr inpPageptr;
+ Page8Ptr iopPageptr;
+ Page8Ptr lastPageptr;
+ Page8Ptr lastPrevpageptr;
+ Page8Ptr lcnPageptr;
+ Page8Ptr lcnCopyPageptr;
+ Page8Ptr lupPageptr;
+ Page8Ptr priPageptr;
+ Page8Ptr pwiPageptr;
+ Page8Ptr ciPageidptr;
+ Page8Ptr gsePageidptr;
+ Page8Ptr isoPageptr;
+ Page8Ptr nciPageidptr;
+ Page8Ptr rsbPageidptr;
+ Page8Ptr rscPageidptr;
+ Page8Ptr slPageidptr;
+ Page8Ptr sscPageidptr;
+ Page8Ptr rlPageptr;
+ Page8Ptr rlpPageptr;
+ Page8Ptr ropPageptr;
+ Page8Ptr rpPageptr;
+ Page8Ptr slPageptr;
+ Page8Ptr spPageptr;
+ Uint32 cfirstfreepage;
+ Uint32 cfreepage;
+ Uint32 cpagesize;
+ Uint32 cfirstfreeLcpPage;
+ Uint32 cnoOfAllocatedPages;
+ Uint32 cnoLcpPages;
+/* --------------------------------------------------------------------------------- */
+/* ROOTFRAGMENTREC */
+/* DURING EXPAND FRAGMENT PROCESS, EACH FRAGMEND WILL BE EXPAND INTO TWO */
+/* NEW FRAGMENTS.TO MAKE THIS PROCESS EASIER, DURING ADD FRAGMENT PROCESS */
+/* NEXT FRAGMENT IDENTIIES WILL BE CALCULATED, AND TWO FRAGMENTS WILL BE */
+/* ADDED IN (NDBACC). THEREBY EXPAND OF FRAGMENT CAN BE PERFORMED QUICK AND */
+/* EASY.THE NEW FRAGMENT ID SENDS TO TUP MANAGER FOR ALL OPERATION PROCESS. */
+/* --------------------------------------------------------------------------------- */
+ Rootfragmentrec *rootfragmentrec;
+ RootfragmentrecPtr rootfragrecptr;
+ Uint32 crootfragmentsize;
+ Uint32 cfirstfreerootfrag;
+/* --------------------------------------------------------------------------------- */
+/* SCAN_REC */
+/* --------------------------------------------------------------------------------- */
+ ScanRec *scanRec;
+ ScanRecPtr scanPtr;
+ Uint32 cscanRecSize;
+ Uint32 cfirstFreeScanRec;
+/* --------------------------------------------------------------------------------- */
+/* SR_VERSION_REC */
+/* --------------------------------------------------------------------------------- */
+ SrVersionRec *srVersionRec;
+ SrVersionRecPtr srVersionPtr;
+ Uint32 csrVersionRecSize;
+ Uint32 cfirstFreeSrVersionRec;
+/* --------------------------------------------------------------------------------- */
+/* TABREC */
+/* --------------------------------------------------------------------------------- */
+ Tabrec *tabrec;
+ TabrecPtr tabptr;
+ Uint32 ctablesize;
+/* --------------------------------------------------------------------------------- */
+/* UNDOPAGE */
+/* --------------------------------------------------------------------------------- */
+ Undopage *undopage;
+ /* 32 KB PAGE */
+ UndopagePtr undopageptr;
+ Uint32 tpwiElementptr;
+ Uint32 tpriElementptr;
+ Uint32 tgseElementptr;
+ Uint32 tgseContainerptr;
+ Uint32 trlHead;
+ Uint32 trlRelCon;
+ Uint32 trlNextused;
+ Uint32 trlPrevused;
+ Uint32 tlcnChecksum;
+ Uint32 tlupElemIndex;
+ Uint32 tlupIndex;
+ Uint32 tlupForward;
+ Uint32 tancNext;
+ Uint32 tancBufType;
+ Uint32 tancContainerptr;
+ Uint32 tancPageindex;
+ Uint32 tancPageid;
+ Uint32 tidrResult;
+ Uint32 tidrElemhead;
+ Uint32 tidrForward;
+ Uint32 tidrPageindex;
+ Uint32 tidrContainerptr;
+ Uint32 tidrContainerhead;
+ Uint32 tlastForward;
+ Uint32 tlastPageindex;
+ Uint32 tlastContainerlen;
+ Uint32 tlastElementptr;
+ Uint32 tlastContainerptr;
+ Uint32 tlastContainerhead;
+ Uint32 trlPageindex;
+ Uint32 tdelContainerptr;
+ Uint32 tdelElementptr;
+ Uint32 tdelForward;
+ Uint32 tiopPageId;
+ Uint32 tipPageId;
+ Uint32 tgeLocked;
+ Uint32 tgeResult;
+ Uint32 tgeContainerptr;
+ Uint32 tgeElementptr;
+ Uint32 tgeForward;
+ Uint32 tundoElemIndex;
+ Uint32 texpReceivedBucket;
+ Uint32 texpDirInd;
+ Uint32 texpDirRangeIndex;
+ Uint32 texpDirPageIndex;
+ Uint32 tdata0;
+ Uint32 tcheckpointid;
+ Uint32 tciContainerptr;
+ Uint32 tnciContainerptr;
+ Uint32 tisoContainerptr;
+ Uint32 trscContainerptr;
+ Uint32 tsscContainerptr;
+ Uint32 tciContainerlen;
+ Uint32 trscContainerlen;
+ Uint32 tsscContainerlen;
+ Uint32 tciContainerhead;
+ Uint32 tnciContainerhead;
+ Uint32 tslElementptr;
+ Uint32 tisoElementptr;
+ Uint32 tsscElementptr;
+ Uint32 tfid;
+ Uint32 tscanFlag;
+ Uint32 theadundoindex;
+ Uint32 tgflBufType;
+ Uint32 tgseIsforward;
+ Uint32 tsscIsforward;
+ Uint32 trscIsforward;
+ Uint32 tciIsforward;
+ Uint32 tnciIsforward;
+ Uint32 tisoIsforward;
+ Uint32 tgseIsLocked;
+ Uint32 tsscIsLocked;
+ Uint32 tkeylen;
+ Uint32 tmp;
+ Uint32 tmpP;
+ Uint32 tmpP2;
+ Uint32 tmp1;
+ Uint32 tmp2;
+ Uint32 tgflPageindex;
+ Uint32 tmpindex;
+ Uint32 tslNextfree;
+ Uint32 tslPageindex;
+ Uint32 tgsePageindex;
+ Uint32 tnciNextSamePage;
+ Uint32 tslPrevfree;
+ Uint32 tciPageindex;
+ Uint32 trsbPageindex;
+ Uint32 tnciPageindex;
+ Uint32 tlastPrevconptr;
+ Uint32 tresult;
+ Uint32 tslUpdateHeader;
+ Uint32 tuserptr;
+ BlockReference tuserblockref;
+ Uint32 tundoindex;
+ Uint32 tlqhPointer;
+ Uint32 tholdSentOp;
+ Uint32 tholdMore;
+ Uint32 tlcpLqhCheckV;
+ Uint32 tgdiPageindex;
+ Uint32 tiopIndex;
+ Uint32 tnciTmp;
+ Uint32 tullIndex;
+ Uint32 turlIndex;
+ Uint32 tlfrTmp1;
+ Uint32 tlfrTmp2;
+ Uint32 tgnptNrTransaction;
+ Uint32 tscanTrid1;
+ Uint32 tscanTrid2;
+
+ Uint16 clastUndoPageIdWritten;
+ Uint32 cactiveCheckpId;
+ Uint32 cactiveRootfrag;
+ Uint32 cactiveSrFsPtr;
+ Uint32 cactiveUndoFilePage;
+ Uint32 cactiveOpenUndoFsPtr;
+ Uint32 cactiveSrUndoPage;
+ Uint32 cprevUndoaddress;
+ Uint32 creadyUndoaddress;
+ Uint32 ctest;
+ Uint32 cundoLogActive;
+ Uint32 clqhPtr;
+ BlockReference clqhBlockRef;
+ Uint32 cminusOne;
+ NodeId cmynodeid;
+ Uint32 cactiveUndoFileVersion;
+ BlockReference cownBlockref;
+ BlockReference cndbcntrRef;
+ Uint16 csignalkey;
+ Uint32 cundopagesize;
+ Uint32 cundoposition;
+ Uint32 cundoElemIndex;
+ Uint32 cundoinfolength;
+ Uint32 czero;
+ Uint32 csrVersList[16];
+ Uint32 clblPageCounter;
+ Uint32 clblPageOver;
+ Uint32 clblPagesPerTick;
+ Uint32 clblPagesPerTickAfterSr;
+ Uint32 csystemRestart;
+ Uint32 cexcForward;
+ Uint32 cexcPageindex;
+ Uint32 cexcContainerptr;
+ Uint32 cexcContainerhead;
+ Uint32 cexcContainerlen;
+ Uint32 cexcElementptr;
+ Uint32 cexcPrevconptr;
+ Uint32 cexcMovedLen;
+ Uint32 cexcPrevpageptr;
+ Uint32 cexcPrevpageindex;
+ Uint32 cexcPrevforward;
+ Uint32 clocalkey[32];
+ union {
+ Uint32 ckeys[2048];
+ Uint64 ckeys_align;
+ };
+
+ Uint32 c_errorInsert3000_TableId;
+ Uint32 cSrUndoRecords[UndoHeader::ZNO_UNDORECORD_TYPES];
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp b/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp
new file mode 100644
index 00000000000..94782e13e00
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp
@@ -0,0 +1,269 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#define DBACC_C
+#include "Dbacc.hpp"
+
+#define DEBUG(x) { ndbout << "ACC::" << x << endl; }
+
+void Dbacc::initData()
+{
+ cdirarraysize = ZDIRARRAY;
+ coprecsize = ZOPRECSIZE;
+ cpagesize = ZPAGESIZE;
+ clcpConnectsize = ZLCP_CONNECTSIZE;
+ ctablesize = ZTABLESIZE;
+ cfragmentsize = ZFRAGMENTSIZE;
+ crootfragmentsize = ZROOTFRAGMENTSIZE;
+ cdirrangesize = ZDIRRANGESIZE;
+ coverflowrecsize = ZOVERFLOWRECSIZE;
+ cfsConnectsize = ZFS_CONNECTSIZE;
+ cfsOpsize = ZFS_OPSIZE;
+ cscanRecSize = ZSCAN_REC_SIZE;
+ csrVersionRecSize = ZSR_VERSION_REC_SIZE;
+
+
+ dirRange = 0;
+ directoryarray = 0;
+ fragmentrec = 0;
+ fsConnectrec = 0;
+ fsOprec = 0;
+ lcpConnectrec = 0;
+ operationrec = 0;
+ overflowRecord = 0;
+ page8 = 0;
+ rootfragmentrec = 0;
+ scanRec = 0;
+ srVersionRec = 0;
+ tabrec = 0;
+ undopage = 0;
+
+ // Records with constant sizes
+}//Dbacc::initData()
+
+void Dbacc::initRecords()
+{
+ // Records with dynamic sizes
+ dirRange = (DirRange*)allocRecord("DirRange",
+ sizeof(DirRange),
+ cdirrangesize);
+
+ directoryarray = (Directoryarray*)allocRecord("Directoryarray",
+ sizeof(Directoryarray),
+ cdirarraysize);
+
+ fragmentrec = (Fragmentrec*)allocRecord("Fragmentrec",
+ sizeof(Fragmentrec),
+ cfragmentsize);
+
+ fsConnectrec = (FsConnectrec*)allocRecord("FsConnectrec",
+ sizeof(FsConnectrec),
+ cfsConnectsize);
+
+ fsOprec = (FsOprec*)allocRecord("FsOprec",
+ sizeof(FsOprec),
+ cfsOpsize);
+
+ lcpConnectrec = (LcpConnectrec*)allocRecord("LcpConnectrec",
+ sizeof(LcpConnectrec),
+ clcpConnectsize);
+
+ operationrec = (Operationrec*)allocRecord("Operationrec",
+ sizeof(Operationrec),
+ coprecsize);
+
+ overflowRecord = (OverflowRecord*)allocRecord("OverflowRecord",
+ sizeof(OverflowRecord),
+ coverflowrecsize);
+
+ page8 = (Page8*)allocRecord("Page8",
+ sizeof(Page8),
+ cpagesize,
+ false);
+
+ rootfragmentrec = (Rootfragmentrec*)allocRecord("Rootfragmentrec",
+ sizeof(Rootfragmentrec),
+ crootfragmentsize);
+
+ scanRec = (ScanRec*)allocRecord("ScanRec",
+ sizeof(ScanRec),
+ cscanRecSize);
+
+ srVersionRec = (SrVersionRec*)allocRecord("SrVersionRec",
+ sizeof(SrVersionRec),
+ csrVersionRecSize);
+
+ tabrec = (Tabrec*)allocRecord("Tabrec",
+ sizeof(Tabrec),
+ ctablesize);
+
+ undopage = (Undopage*)allocRecord("Undopage",
+ sizeof(Undopage),
+ cundopagesize,
+ false);
+
+ // Initialize BAT for interface to file system
+
+ NewVARIABLE* bat = allocateBat(3);
+ bat[1].WA = &page8->word32[0];
+ bat[1].nrr = cpagesize;
+ bat[1].ClusterSize = sizeof(Page8);
+ bat[1].bits.q = 11;
+ bat[1].bits.v = 5;
+ bat[2].WA = &undopage->undoword[0];
+ bat[2].nrr = cundopagesize;
+ bat[2].ClusterSize = sizeof(Undopage);
+ bat[2].bits.q = 13;
+ bat[2].bits.v = 5;
+}//Dbacc::initRecords()
+
+Dbacc::Dbacc(const class Configuration & conf):
+ SimulatedBlock(DBACC, conf),
+ c_tup(0)
+{
+ Uint32 log_page_size= 0;
+ BLOCK_CONSTRUCTOR(Dbacc);
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_UNDO_INDEX_BUFFER,
+ &log_page_size);
+
+ /**
+ * Always set page size in half MBytes
+ */
+ cundopagesize= (log_page_size / sizeof(Undopage));
+ Uint32 mega_byte_part= cundopagesize & 15;
+ if (mega_byte_part != 0) {
+ jam();
+ cundopagesize+= (16 - mega_byte_part);
+ }
+
+ // Transit signals
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbacc::execDUMP_STATE_ORD);
+ addRecSignal(GSN_DEBUG_SIG, &Dbacc::execDEBUG_SIG);
+ addRecSignal(GSN_CONTINUEB, &Dbacc::execCONTINUEB);
+ addRecSignal(GSN_ACC_CHECK_SCAN, &Dbacc::execACC_CHECK_SCAN);
+ addRecSignal(GSN_EXPANDCHECK2, &Dbacc::execEXPANDCHECK2);
+ addRecSignal(GSN_SHRINKCHECK2, &Dbacc::execSHRINKCHECK2);
+ addRecSignal(GSN_ACC_OVER_REC, &Dbacc::execACC_OVER_REC);
+ addRecSignal(GSN_ACC_SAVE_PAGES, &Dbacc::execACC_SAVE_PAGES);
+ addRecSignal(GSN_NEXTOPERATION, &Dbacc::execNEXTOPERATION);
+ addRecSignal(GSN_READ_PSUEDO_REQ, &Dbacc::execREAD_PSUEDO_REQ);
+
+ // Received signals
+ addRecSignal(GSN_STTOR, &Dbacc::execSTTOR);
+ addRecSignal(GSN_SR_FRAGIDREQ, &Dbacc::execSR_FRAGIDREQ);
+ addRecSignal(GSN_LCP_FRAGIDREQ, &Dbacc::execLCP_FRAGIDREQ);
+ addRecSignal(GSN_LCP_HOLDOPREQ, &Dbacc::execLCP_HOLDOPREQ);
+ addRecSignal(GSN_END_LCPREQ, &Dbacc::execEND_LCPREQ);
+ addRecSignal(GSN_ACC_LCPREQ, &Dbacc::execACC_LCPREQ);
+ addRecSignal(GSN_START_RECREQ, &Dbacc::execSTART_RECREQ);
+ addRecSignal(GSN_ACC_CONTOPREQ, &Dbacc::execACC_CONTOPREQ);
+ addRecSignal(GSN_ACCKEYREQ, &Dbacc::execACCKEYREQ);
+ addRecSignal(GSN_ACCSEIZEREQ, &Dbacc::execACCSEIZEREQ);
+ addRecSignal(GSN_ACCFRAGREQ, &Dbacc::execACCFRAGREQ);
+ addRecSignal(GSN_TC_SCHVERREQ, &Dbacc::execTC_SCHVERREQ);
+ addRecSignal(GSN_ACC_SRREQ, &Dbacc::execACC_SRREQ);
+ addRecSignal(GSN_NEXT_SCANREQ, &Dbacc::execNEXT_SCANREQ);
+ addRecSignal(GSN_ACC_ABORTREQ, &Dbacc::execACC_ABORTREQ);
+ addRecSignal(GSN_ACC_SCANREQ, &Dbacc::execACC_SCANREQ);
+ addRecSignal(GSN_ACCMINUPDATE, &Dbacc::execACCMINUPDATE);
+ addRecSignal(GSN_ACC_COMMITREQ, &Dbacc::execACC_COMMITREQ);
+ addRecSignal(GSN_ACC_TO_REQ, &Dbacc::execACC_TO_REQ);
+ addRecSignal(GSN_ACC_LOCKREQ, &Dbacc::execACC_LOCKREQ);
+ addRecSignal(GSN_FSOPENCONF, &Dbacc::execFSOPENCONF);
+ addRecSignal(GSN_FSOPENREF, &Dbacc::execFSOPENREF);
+ addRecSignal(GSN_FSCLOSECONF, &Dbacc::execFSCLOSECONF);
+ addRecSignal(GSN_FSCLOSEREF, &Dbacc::execFSCLOSEREF);
+ addRecSignal(GSN_FSWRITECONF, &Dbacc::execFSWRITECONF);
+ addRecSignal(GSN_FSWRITEREF, &Dbacc::execFSWRITEREF);
+ addRecSignal(GSN_FSREADCONF, &Dbacc::execFSREADCONF);
+ addRecSignal(GSN_FSREADREF, &Dbacc::execFSREADREF);
+ addRecSignal(GSN_NDB_STTOR, &Dbacc::execNDB_STTOR);
+ addRecSignal(GSN_DROP_TAB_REQ, &Dbacc::execDROP_TAB_REQ);
+ addRecSignal(GSN_FSREMOVECONF, &Dbacc::execFSREMOVECONF);
+ addRecSignal(GSN_FSREMOVEREF, &Dbacc::execFSREMOVEREF);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbacc::execREAD_CONFIG_REQ, true);
+ addRecSignal(GSN_SET_VAR_REQ, &Dbacc::execSET_VAR_REQ);
+
+ initData();
+}//Dbacc::Dbacc()
+
+Dbacc::~Dbacc()
+{
+ deallocRecord((void **)&dirRange, "DirRange",
+ sizeof(DirRange),
+ cdirrangesize);
+
+ deallocRecord((void **)&directoryarray, "Directoryarray",
+ sizeof(Directoryarray),
+ cdirarraysize);
+
+ deallocRecord((void **)&fragmentrec, "Fragmentrec",
+ sizeof(Fragmentrec),
+ cfragmentsize);
+
+ deallocRecord((void **)&fsConnectrec, "FsConnectrec",
+ sizeof(FsConnectrec),
+ cfsConnectsize);
+
+ deallocRecord((void **)&fsOprec, "FsOprec",
+ sizeof(FsOprec),
+ cfsOpsize);
+
+ deallocRecord((void **)&lcpConnectrec, "LcpConnectrec",
+ sizeof(LcpConnectrec),
+ clcpConnectsize);
+
+ deallocRecord((void **)&operationrec, "Operationrec",
+ sizeof(Operationrec),
+ coprecsize);
+
+ deallocRecord((void **)&overflowRecord, "OverflowRecord",
+ sizeof(OverflowRecord),
+ coverflowrecsize);
+
+ deallocRecord((void **)&page8, "Page8",
+ sizeof(Page8),
+ cpagesize);
+
+ deallocRecord((void **)&rootfragmentrec, "Rootfragmentrec",
+ sizeof(Rootfragmentrec),
+ crootfragmentsize);
+
+ deallocRecord((void **)&scanRec, "ScanRec",
+ sizeof(ScanRec),
+ cscanRecSize);
+
+ deallocRecord((void **)&srVersionRec, "SrVersionRec",
+ sizeof(SrVersionRec),
+ csrVersionRecSize);
+
+ deallocRecord((void **)&tabrec, "Tabrec",
+ sizeof(Tabrec),
+ ctablesize);
+
+ deallocRecord((void **)&undopage, "Undopage",
+ sizeof(Undopage),
+ cundopagesize);
+
+}//Dbacc::~Dbacc()
+
+BLOCK_FUNCTIONS(Dbacc)
diff --git a/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp b/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp
new file mode 100644
index 00000000000..a16c0da369b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp
@@ -0,0 +1,11653 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBACC_C
+#include "Dbacc.hpp"
+#include <my_sys.h>
+
+#include <AttributeHeader.hpp>
+#include <signaldata/AccFrag.hpp>
+#include <signaldata/AccScan.hpp>
+#include <signaldata/AccLock.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+#include <signaldata/FsRemoveReq.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <SectionReader.hpp>
+
+// TO_DO_RONM is a label for comments on what needs to be improved in future versions
+// when more time is given.
+
+#ifdef VM_TRACE
+#define DEBUG(x) ndbout << "DBACC: "<< x << endl;
+#else
+#define DEBUG(x)
+#endif
+
+
+Uint32
+Dbacc::remainingUndoPages(){
+ Uint32 HeadPage = cundoposition >> ZUNDOPAGEINDEXBITS;
+ Uint32 TailPage = clastUndoPageIdWritten;
+
+ // Head must be larger or same as tail
+ ndbrequire(HeadPage>=TailPage);
+
+ Uint32 UsedPages = HeadPage - TailPage;
+ Int32 Remaining = cundopagesize - UsedPages;
+
+ // There can not be more than cundopagesize remaining
+ if (Remaining <= 0){
+ // No more undolog, crash node
+ progError(__LINE__,
+ ERR_NO_MORE_UNDOLOG,
+ "There are more than 1Mbyte undolog writes outstanding");
+ }
+ return Remaining;
+}
+
+void
+Dbacc::updateLastUndoPageIdWritten(Signal* signal, Uint32 aNewValue){
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_COMMIT) {
+ clastUndoPageIdWritten = aNewValue;
+ if (remainingUndoPages() >= ZMIN_UNDO_PAGES_AT_COMMIT) {
+ jam();
+ EXECUTE_DIRECT(DBLQH, GSN_ACC_COM_UNBLOCK, signal, 1);
+ jamEntry();
+ }//if
+ } else {
+ clastUndoPageIdWritten = aNewValue;
+ }//if
+}//Dbacc::updateLastUndoPageIdWritten()
+
+void
+Dbacc::updateUndoPositionPage(Signal* signal, Uint32 aNewValue){
+ if (remainingUndoPages() >= ZMIN_UNDO_PAGES_AT_COMMIT) {
+ cundoposition = aNewValue;
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_COMMIT) {
+ jam();
+ EXECUTE_DIRECT(DBLQH, GSN_ACC_COM_BLOCK, signal, 1);
+ jamEntry();
+ }//if
+ } else {
+ cundoposition = aNewValue;
+ }//if
+}//Dbacc::updateUndoPositionPage()
+
+// Signal entries and statement blocks
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* COMMON SIGNAL RECEPTION MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+
+/* --------------------------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* CONTINUEB CONTINUE SIGNAL */
+/* ******************------------------------------+ */
+/* SENDER: ACC, LEVEL B */
+void Dbacc::execCONTINUEB(Signal* signal)
+{
+ Uint32 tcase;
+
+ jamEntry();
+ tcase = signal->theData[0];
+ tdata0 = signal->theData[1];
+ tresult = 0;
+ switch (tcase) {
+ case ZLOAD_BAL_LCP_TIMER:
+ if (clblPageOver == 0) {
+ jam();
+ clblPageCounter = clblPagesPerTick;
+ } else {
+ if (clblPageOver > clblPagesPerTick) {
+ jam();
+ clblPageOver = clblPageOver - clblPagesPerTick;
+ } else {
+ jam();
+ clblPageOver = 0;
+ clblPageCounter = clblPagesPerTick - clblPageOver;
+ }//if
+ }//if
+ signal->theData[0] = ZLOAD_BAL_LCP_TIMER;
+ sendSignalWithDelay(cownBlockref, GSN_CONTINUEB, signal, 100, 1);
+ return;
+ break;
+ case ZINITIALISE_RECORDS:
+ jam();
+ initialiseRecordsLab(signal, signal->theData[3], signal->theData[4]);
+ return;
+ break;
+ case ZSR_READ_PAGES_ALLOC:
+ jam();
+ fragrecptr.i = tdata0;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ srReadPagesAllocLab(signal);
+ return;
+ break;
+ case ZSTART_UNDO:
+ jam();
+ startUndoLab(signal);
+ return;
+ break;
+ case ZSEND_SCAN_HBREP:
+ jam();
+ sendScanHbRep(signal, tdata0);
+ break;
+ case ZREL_ROOT_FRAG:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ releaseRootFragResources(signal, tableId);
+ break;
+ }
+ case ZREL_FRAG:
+ {
+ jam();
+ Uint32 fragIndex = signal->theData[1];
+ releaseFragResources(signal, fragIndex);
+ break;
+ }
+ case ZREL_DIR:
+ {
+ jam();
+ Uint32 fragIndex = signal->theData[1];
+ Uint32 dirIndex = signal->theData[2];
+ Uint32 startIndex = signal->theData[3];
+ releaseDirResources(signal, fragIndex, dirIndex, startIndex);
+ break;
+ }
+ case ZREPORT_MEMORY_USAGE:{
+ jam();
+ static int c_currentMemUsed = 0;
+ int now = (cnoOfAllocatedPages * 100)/cpagesize;
+ const int thresholds[] = { 99, 90, 80, 0};
+
+ Uint32 i = 0;
+ const Uint32 sz = sizeof(thresholds)/sizeof(thresholds[0]);
+ for(i = 0; i<sz; i++){
+ if(now >= thresholds[i]){
+ now = thresholds[i];
+ break;
+ }
+ }
+
+ if(now != c_currentMemUsed){
+ reportMemoryUsage(signal, now > c_currentMemUsed ? 1 : -1);
+ }
+
+ c_currentMemUsed = now;
+
+ signal->theData[0] = ZREPORT_MEMORY_USAGE;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 2000, 1);
+ return;
+ }
+
+ case ZLCP_OP_WRITE_RT_BREAK:
+ {
+ operationRecPtr.i= signal->theData[1];
+ fragrecptr.i= signal->theData[2];
+ lcpConnectptr.i= signal->theData[3];
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ lcp_write_op_to_undolog(signal);
+ return;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbacc::execCONTINUEB()
+
+/* ******************--------------------------------------------------------------- */
+/* FSCLOSECONF CLOSE FILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSCLOSECONF(Signal* signal)
+{
+ jamEntry();
+ fsConnectptr.i = signal->theData[0];
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ tresult = 0;
+ switch (fsConnectptr.p->fsState) {
+ case WAIT_CLOSE_UNDO:
+ jam();
+ releaseFsConnRec(signal);
+ break;
+ case LCP_CLOSE_DATA:
+ jam();
+ checkSyncUndoPagesLab(signal);
+ return;
+ break;
+ case SR_CLOSE_DATA:
+ jam();
+ sendaccSrconfLab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbacc::execFSCLOSECONF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSCLOSEREF OPENFILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSCLOSEREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dbacc::execFSCLOSEREF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSOPENCONF OPENFILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSOPENCONF(Signal* signal)
+{
+ jamEntry();
+ fsConnectptr.i = signal->theData[0];
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ tuserptr = signal->theData[1];
+ tresult = 0; /* RESULT CHECK VALUE */
+ switch (fsConnectptr.p->fsState) {
+ case WAIT_OPEN_UNDO_LCP:
+ jam();
+ lcpOpenUndofileConfLab(signal);
+ return;
+ break;
+ case WAIT_OPEN_UNDO_LCP_NEXT:
+ jam();
+ fsConnectptr.p->fsPtr = tuserptr;
+ return;
+ break;
+ case OPEN_UNDO_FILE_SR:
+ jam();
+ fsConnectptr.p->fsPtr = tuserptr;
+ srStartUndoLab(signal);
+ return;
+ break;
+ case WAIT_OPEN_DATA_FILE_FOR_WRITE:
+ jam();
+ lcpFsOpenConfLab(signal);
+ return;
+ break;
+ case WAIT_OPEN_DATA_FILE_FOR_READ:
+ jam();
+ fsConnectptr.p->fsPtr = tuserptr;
+ srFsOpenConfLab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbacc::execFSOPENCONF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSOPENREF OPENFILE REF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSOPENREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dbacc::execFSOPENREF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSREADCONF OPENFILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSREADCONF(Signal* signal)
+{
+ jamEntry();
+ fsConnectptr.i = signal->theData[0];
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ tresult = 0; /* RESULT CHECK VALUE */
+ switch (fsConnectptr.p->fsState) {
+ case WAIT_READ_PAGE_ZERO:
+ jam();
+ fragrecptr.i = fsConnectptr.p->fragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ srReadPageZeroLab(signal);
+ return;
+ break;
+ case WAIT_READ_DATA:
+ jam();
+ fragrecptr.i = fsConnectptr.p->fragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ storeDataPageInDirectoryLab(signal);
+ return;
+ break;
+ case READ_UNDO_PAGE:
+ jam();
+ srDoUndoLab(signal);
+ return;
+ break;
+ case READ_UNDO_PAGE_AND_CLOSE:
+ jam();
+ fsConnectptr.p->fsState = WAIT_CLOSE_UNDO;
+ /* ************************ */
+ /* FSCLOSEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = 0;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ /* FLAG = DO NOT DELETE FILE */
+ srDoUndoLab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbacc::execFSREADCONF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSREADRREF OPENFILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSREADREF(Signal* signal)
+{
+ jamEntry();
+ progError(0, __LINE__, "Read of file refused");
+ return;
+}//Dbacc::execFSREADREF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSWRITECONF OPENFILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSWRITECONF(Signal* signal)
+{
+ jamEntry();
+ fsOpptr.i = signal->theData[0];
+ ptrCheckGuard(fsOpptr, cfsOpsize, fsOprec);
+ /* FS_OPERATION PTR */
+ tresult = 0; /* RESULT CHECK VALUE */
+ fsConnectptr.i = fsOpptr.p->fsConptr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ fragrecptr.i = fsOpptr.p->fsOpfragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ switch (fsOpptr.p->fsOpstate) {
+ case WAIT_WRITE_UNDO:
+ jam();
+ updateLastUndoPageIdWritten(signal, fsOpptr.p->fsOpMemPage);
+ releaseFsOpRec(signal);
+ if (fragrecptr.p->nrWaitWriteUndoExit == 0) {
+ jam();
+ checkSendLcpConfLab(signal);
+ return;
+ } else {
+ jam();
+ fragrecptr.p->lastUndoIsStored = ZTRUE;
+ }//if
+ return;
+ break;
+ case WAIT_WRITE_UNDO_EXIT:
+ jam();
+ updateLastUndoPageIdWritten(signal, fsOpptr.p->fsOpMemPage);
+ releaseFsOpRec(signal);
+ if (fragrecptr.p->nrWaitWriteUndoExit > 0) {
+ jam();
+ fragrecptr.p->nrWaitWriteUndoExit--;
+ }//if
+ if (fsConnectptr.p->fsState == WAIT_CLOSE_UNDO) {
+ jam();
+ /* ************************ */
+ /* FSCLOSEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = ZFALSE;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ }//if
+ if (fragrecptr.p->nrWaitWriteUndoExit == 0) {
+ if (fragrecptr.p->lastUndoIsStored == ZTRUE) {
+ jam();
+ fragrecptr.p->lastUndoIsStored = ZFALSE;
+ checkSendLcpConfLab(signal);
+ return;
+ }//if
+ }//if
+ return;
+ break;
+ case WAIT_WRITE_DATA:
+ jam();
+ releaseFsOpRec(signal);
+ fragrecptr.p->activeDataFilePage += ZWRITEPAGESIZE;
+ fragrecptr.p->activeDataPage = 0;
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ switch (fragrecptr.p->fragState) {
+ case LCP_SEND_PAGES:
+ jam();
+ savepagesLab(signal);
+ return;
+ break;
+ case LCP_SEND_OVER_PAGES:
+ jam();
+ saveOverPagesLab(signal);
+ return;
+ break;
+ case LCP_SEND_ZERO_PAGE:
+ jam();
+ saveZeroPageLab(signal);
+ return;
+ break;
+ case WAIT_ZERO_PAGE_STORED:
+ jam();
+ lcpCloseDataFileLab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbacc::execFSWRITECONF()
+
+/* ******************--------------------------------------------------------------- */
+/* FSWRITEREF OPENFILE CONF */
+/* ******************------------------------------+ */
+/* SENDER: FS, LEVEL B */
+void Dbacc::execFSWRITEREF(Signal* signal)
+{
+ jamEntry();
+ progError(0, __LINE__, "Write to file refused");
+ return;
+}//Dbacc::execFSWRITEREF()
+
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* */
+/* END OF COMMON SIGNAL RECEPTION MODULE */
+/* */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* */
+/* SYSTEM RESTART MODULE */
+/* */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+void Dbacc::execNDB_STTOR(Signal* signal)
+{
+ Uint32 tstartphase;
+ Uint32 tStartType;
+
+ jamEntry();
+ cndbcntrRef = signal->theData[0];
+ cmynodeid = signal->theData[1];
+ tstartphase = signal->theData[2];
+ tStartType = signal->theData[3];
+ switch (tstartphase) {
+ case ZSPH1:
+ jam();
+ ndbsttorryLab(signal);
+ return;
+ break;
+ case ZSPH2:
+ cnoLcpPages = 2 * (ZWRITEPAGESIZE + 1);
+ initialiseLcpPages(signal);
+ ndbsttorryLab(signal);
+ return;
+ break;
+ case ZSPH3:
+ if ((tStartType == NodeState::ST_NODE_RESTART) ||
+ (tStartType == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ //---------------------------------------------
+ // csystemRestart is used to check what is needed
+ // during log execution. When starting a node it
+ // is not a log execution and rather a normal
+ // execution. Thus we reset the variable here to
+ // avoid unnecessary system crashes.
+ //---------------------------------------------
+ csystemRestart = ZFALSE;
+ }//if
+
+ signal->theData[0] = ZLOAD_BAL_LCP_TIMER;
+ sendSignalWithDelay(cownBlockref, GSN_CONTINUEB, signal, 100, 1);
+ break;
+ case ZSPH6:
+ jam();
+ clblPagesPerTick = clblPagesPerTickAfterSr;
+ csystemRestart = ZFALSE;
+
+ signal->theData[0] = ZREPORT_MEMORY_USAGE;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 2000, 1);
+ break;
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }//switch
+ ndbsttorryLab(signal);
+ return;
+}//Dbacc::execNDB_STTOR()
+
+/* ******************--------------------------------------------------------------- */
+/* STTOR START / RESTART */
+/* ******************------------------------------+ */
+/* SENDER: ANY, LEVEL B */
+void Dbacc::execSTTOR(Signal* signal)
+{
+ jamEntry();
+ Uint32 tstartphase = signal->theData[1];
+ switch (tstartphase) {
+ case 1:
+ jam();
+ c_tup = (Dbtup*)globalData.getBlock(DBTUP);
+ ndbrequire(c_tup != 0);
+ break;
+ }
+ tuserblockref = signal->theData[3];
+ csignalkey = signal->theData[6];
+ sttorrysignalLab(signal);
+ return;
+}//Dbacc::execSTTOR()
+
+/* --------------------------------------------------------------------------------- */
+/* ZSPH1 */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::ndbrestart1Lab(Signal* signal)
+{
+ cmynodeid = globalData.ownId;
+ cownBlockref = numberToRef(DBACC, cmynodeid);
+ czero = 0;
+ cminusOne = czero - 1;
+ ctest = 0;
+ cundoLogActive = ZFALSE;
+ csystemRestart = ZTRUE;
+ clblPageOver = 0;
+ clblPageCounter = 0;
+ cactiveUndoFilePage = 0;
+ cprevUndoaddress = cminusOne;
+ cundoposition = 0;
+ clastUndoPageIdWritten = 0;
+ cactiveUndoFileVersion = RNIL;
+ cactiveOpenUndoFsPtr = RNIL;
+ for (Uint32 tmp = 0; tmp < ZMAX_UNDO_VERSION; tmp++) {
+ csrVersList[tmp] = RNIL;
+ }//for
+ return;
+}//Dbacc::ndbrestart1Lab()
+
+void Dbacc::initialiseRecordsLab(Signal* signal, Uint32 ref, Uint32 data)
+{
+ switch (tdata0) {
+ case 0:
+ jam();
+ initialiseTableRec(signal);
+ break;
+ case 1:
+ jam();
+ initialiseFsConnectionRec(signal);
+ break;
+ case 2:
+ jam();
+ initialiseFsOpRec(signal);
+ break;
+ case 3:
+ jam();
+ initialiseLcpConnectionRec(signal);
+ break;
+ case 4:
+ jam();
+ initialiseDirRec(signal);
+ break;
+ case 5:
+ jam();
+ initialiseDirRangeRec(signal);
+ break;
+ case 6:
+ jam();
+ initialiseFragRec(signal);
+ break;
+ case 7:
+ jam();
+ initialiseOverflowRec(signal);
+ break;
+ case 8:
+ jam();
+ initialiseOperationRec(signal);
+ break;
+ case 9:
+ jam();
+ initialisePageRec(signal);
+ break;
+ case 10:
+ jam();
+ initialiseRootfragRec(signal);
+ break;
+ case 11:
+ jam();
+ initialiseScanRec(signal);
+ break;
+ case 12:
+ jam();
+ initialiseSrVerRec(signal);
+
+ {
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = data;
+ sendSignal(ref, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+ }
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+
+ signal->theData[0] = ZINITIALISE_RECORDS;
+ signal->theData[1] = tdata0 + 1;
+ signal->theData[2] = 0;
+ signal->theData[3] = ref;
+ signal->theData[4] = data;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 5, JBB);
+ return;
+}//Dbacc::initialiseRecordsLab()
+
+/* *********************************<< */
+/* NDB_STTORRY */
+/* *********************************<< */
+void Dbacc::ndbsttorryLab(Signal* signal)
+{
+ signal->theData[0] = cownBlockref;
+ sendSignal(cndbcntrRef, GSN_NDB_STTORRY, signal, 1, JBB);
+ return;
+}//Dbacc::ndbsttorryLab()
+
+/* *********************************<< */
+/* SIZEALT_REP SIZE ALTERATION */
+/* *********************************<< */
+void Dbacc::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ jamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_DIR_RANGE, &cdirrangesize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_DIR_ARRAY, &cdirarraysize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_FRAGMENT, &cfragmentsize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_OP_RECS, &coprecsize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_OVERFLOW_RECS,
+ &coverflowrecsize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_PAGE8, &cpagesize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_ROOT_FRAG,
+ &crootfragmentsize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_TABLE, &ctablesize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_ACC_SCAN, &cscanRecSize));
+ initRecords();
+ ndbrestart1Lab(signal);
+
+ clblPagesPerTick = 50;
+ //ndb_mgm_get_int_parameter(p, CFG_DB_, &clblPagesPerTick);
+
+ clblPagesPerTickAfterSr = 50;
+ //ndb_mgm_get_int_parameter(p, CFG_DB_, &clblPagesPerTickAfterSr);
+
+ tdata0 = 0;
+ initialiseRecordsLab(signal, ref, senderData);
+ return;
+}//Dbacc::execSIZEALT_REP()
+
+/* *********************************<< */
+/* STTORRY */
+/* *********************************<< */
+void Dbacc::sttorrysignalLab(Signal* signal)
+{
+ signal->theData[0] = csignalkey;
+ signal->theData[1] = 3;
+ /* BLOCK CATEGORY */
+ signal->theData[2] = 2;
+ /* SIGNAL VERSION NUMBER */
+ signal->theData[3] = ZSPH1;
+ signal->theData[4] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+ /* END OF START PHASES */
+ return;
+}//Dbacc::sttorrysignalLab()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_DIR_REC */
+/* INITIALATES THE DIRECTORY RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseDirRec(Signal* signal)
+{
+ DirectoryarrayPtr idrDirptr;
+ ndbrequire(cdirarraysize > 0);
+ for (idrDirptr.i = 0; idrDirptr.i < cdirarraysize; idrDirptr.i++) {
+ refresh_watch_dog();
+ ptrAss(idrDirptr, directoryarray);
+ for (Uint32 i = 0; i <= 255; i++) {
+ idrDirptr.p->pagep[i] = RNIL;
+ }//for
+ }//for
+ cdirmemory = 0;
+ cfirstfreedir = RNIL;
+}//Dbacc::initialiseDirRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_DIR_RANGE_REC */
+/* INITIALATES THE DIR_RANGE RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseDirRangeRec(Signal* signal)
+{
+ DirRangePtr idrDirRangePtr;
+
+ ndbrequire(cdirrangesize > 0);
+ for (idrDirRangePtr.i = 0; idrDirRangePtr.i < cdirrangesize; idrDirRangePtr.i++) {
+ refresh_watch_dog();
+ ptrAss(idrDirRangePtr, dirRange);
+ idrDirRangePtr.p->dirArray[0] = idrDirRangePtr.i + 1;
+ for (Uint32 i = 1; i < 256; i++) {
+ idrDirRangePtr.p->dirArray[i] = RNIL;
+ }//for
+ }//for
+ idrDirRangePtr.i = cdirrangesize - 1;
+ ptrAss(idrDirRangePtr, dirRange);
+ idrDirRangePtr.p->dirArray[0] = RNIL;
+ cfirstfreeDirrange = 0;
+}//Dbacc::initialiseDirRangeRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_FRAG_REC */
+/* INITIALATES THE FRAGMENT RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseFragRec(Signal* signal)
+{
+ FragmentrecPtr regFragPtr;
+ ndbrequire(cfragmentsize > 0);
+ for (regFragPtr.i = 0; regFragPtr.i < cfragmentsize; regFragPtr.i++) {
+ jam();
+ refresh_watch_dog();
+ ptrAss(regFragPtr, fragmentrec);
+ initFragGeneral(regFragPtr);
+ regFragPtr.p->nextfreefrag = regFragPtr.i + 1;
+ }//for
+ regFragPtr.i = cfragmentsize - 1;
+ ptrAss(regFragPtr, fragmentrec);
+ regFragPtr.p->nextfreefrag = RNIL;
+ cfirstfreefrag = 0;
+}//Dbacc::initialiseFragRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_FS_CONNECTION_REC */
+/* INITIALATES THE FS_CONNECTION RECORDS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseFsConnectionRec(Signal* signal)
+{
+ ndbrequire(cfsConnectsize > 0);
+ for (fsConnectptr.i = 0; fsConnectptr.i < cfsConnectsize; fsConnectptr.i++) {
+ ptrAss(fsConnectptr, fsConnectrec);
+ fsConnectptr.p->fsNext = fsConnectptr.i + 1;
+ fsConnectptr.p->fsPrev = RNIL;
+ fsConnectptr.p->fragrecPtr = RNIL;
+ fsConnectptr.p->fsState = WAIT_NOTHING;
+ }//for
+ fsConnectptr.i = cfsConnectsize - 1;
+ ptrAss(fsConnectptr, fsConnectrec);
+ fsConnectptr.p->fsNext = RNIL; /* INITIALITES THE LAST CONNECTRECORD */
+ cfsFirstfreeconnect = 0; /* INITIATES THE FIRST FREE CONNECT RECORD */
+}//Dbacc::initialiseFsConnectionRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_FS_OP_REC */
+/* INITIALATES THE FS_OP RECORDS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseFsOpRec(Signal* signal)
+{
+ ndbrequire(cfsOpsize > 0);
+ for (fsOpptr.i = 0; fsOpptr.i < cfsOpsize; fsOpptr.i++) {
+ ptrAss(fsOpptr, fsOprec);
+ fsOpptr.p->fsOpnext = fsOpptr.i + 1;
+ fsOpptr.p->fsOpfragrecPtr = RNIL;
+ fsOpptr.p->fsConptr = RNIL;
+ fsOpptr.p->fsOpstate = WAIT_NOTHING;
+ }//for
+ fsOpptr.i = cfsOpsize - 1;
+ ptrAss(fsOpptr, fsOprec);
+ fsOpptr.p->fsOpnext = RNIL;
+ cfsFirstfreeop = 0;
+}//Dbacc::initialiseFsOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_LCP_CONNECTION_REC */
+/* INITIALATES THE LCP_CONNECTION RECORDS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseLcpConnectionRec(Signal* signal)
+{
+ ndbrequire(clcpConnectsize > 0);
+ for (lcpConnectptr.i = 0; lcpConnectptr.i < clcpConnectsize; lcpConnectptr.i++) {
+ ptrAss(lcpConnectptr, lcpConnectrec);
+ lcpConnectptr.p->nextLcpConn = lcpConnectptr.i + 1;
+ lcpConnectptr.p->lcpUserptr = RNIL;
+ lcpConnectptr.p->rootrecptr = RNIL;
+ lcpConnectptr.p->lcpstate = LCP_FREE;
+ }//for
+ lcpConnectptr.i = clcpConnectsize - 1;
+ ptrAss(lcpConnectptr, lcpConnectrec);
+ lcpConnectptr.p->nextLcpConn = RNIL;
+ cfirstfreelcpConnect = 0;
+}//Dbacc::initialiseLcpConnectionRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_OPERATION_REC */
+/* INITIALATES THE OPERATION RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseOperationRec(Signal* signal)
+{
+ ndbrequire(coprecsize > 0);
+ for (operationRecPtr.i = 0; operationRecPtr.i < coprecsize; operationRecPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(operationRecPtr, operationrec);
+ operationRecPtr.p->transactionstate = IDLE;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ operationRecPtr.p->opState = FREE_OP;
+ operationRecPtr.p->nextOp = operationRecPtr.i + 1;
+ }//for
+ operationRecPtr.i = coprecsize - 1;
+ ptrAss(operationRecPtr, operationrec);
+ operationRecPtr.p->nextOp = RNIL;
+ cfreeopRec = 0;
+}//Dbacc::initialiseOperationRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_OVERFLOW_REC */
+/* INITIALATES THE OVERFLOW RECORDS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseOverflowRec(Signal* signal)
+{
+ OverflowRecordPtr iorOverflowRecPtr;
+
+ ndbrequire(coverflowrecsize > 0);
+ for (iorOverflowRecPtr.i = 0; iorOverflowRecPtr.i < coverflowrecsize; iorOverflowRecPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(iorOverflowRecPtr, overflowRecord);
+ iorOverflowRecPtr.p->nextfreeoverrec = iorOverflowRecPtr.i + 1;
+ }//for
+ iorOverflowRecPtr.i = coverflowrecsize - 1;
+ ptrAss(iorOverflowRecPtr, overflowRecord);
+ iorOverflowRecPtr.p->nextfreeoverrec = RNIL;
+ cfirstfreeoverrec = 0;
+}//Dbacc::initialiseOverflowRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_PAGE_REC */
+/* INITIALATES THE PAGE RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialisePageRec(Signal* signal)
+{
+ ndbrequire(cpagesize > 0);
+ cfreepage = 0;
+ cfirstfreepage = RNIL;
+ cnoOfAllocatedPages = 0;
+}//Dbacc::initialisePageRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_LCP_PAGES */
+/* INITIALATES THE LCP PAGE RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseLcpPages(Signal* signal)
+{
+ Uint32 tilpIndex;
+
+ ndbrequire(cnoLcpPages >= (2 * (ZWRITEPAGESIZE + 1)));
+ /* --------------------------------------------------------------------------------- */
+ /* AN ABSOLUTE MINIMUM IS THAT WE HAVE 16 LCP PAGES TO HANDLE TWO CONCURRENT */
+ /* LCP'S ON LOCAL FRAGMENTS. */
+ /* --------------------------------------------------------------------------------- */
+ ndbrequire(cpagesize >= (cnoLcpPages + 8));
+ /* --------------------------------------------------------------------------------- */
+ /* THE NUMBER OF PAGES MUST BE AT LEAST 8 PLUS THE NUMBER OF PAGES REQUIRED BY */
+ /* THE LOCAL CHECKPOINT PROCESS. THIS NUMBER IS 8 TIMES THE PARALLELISM OF */
+ /* LOCAL CHECKPOINTS. */
+ /* --------------------------------------------------------------------------------- */
+ /* --------------------------------------------------------------------------------- */
+ /* WE SET UP A LINKED LIST OF PAGES FOR EXCLUSIVE USE BY LOCAL CHECKPOINTS. */
+ /* --------------------------------------------------------------------------------- */
+ cfirstfreeLcpPage = RNIL;
+ for (tilpIndex = 0; tilpIndex < cnoLcpPages; tilpIndex++) {
+ jam();
+ seizePage(signal);
+ rlpPageptr = spPageptr;
+ releaseLcpPage(signal);
+ }//for
+}//Dbacc::initialiseLcpPages()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_ROOTFRAG_REC */
+/* INITIALATES THE ROOTFRAG RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseRootfragRec(Signal* signal)
+{
+ ndbrequire(crootfragmentsize > 0);
+ for (rootfragrecptr.i = 0; rootfragrecptr.i < crootfragmentsize; rootfragrecptr.i++) {
+ refresh_watch_dog();
+ ptrAss(rootfragrecptr, rootfragmentrec);
+ rootfragrecptr.p->nextroot = rootfragrecptr.i + 1;
+ rootfragrecptr.p->fragmentptr[0] = RNIL;
+ rootfragrecptr.p->fragmentptr[1] = RNIL;
+ }//for
+ rootfragrecptr.i = crootfragmentsize - 1;
+ ptrAss(rootfragrecptr, rootfragmentrec);
+ rootfragrecptr.p->nextroot = RNIL;
+ cfirstfreerootfrag = 0;
+}//Dbacc::initialiseRootfragRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_SCAN_REC */
+/* INITIALATES THE QUE_SCAN RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseScanRec(Signal* signal)
+{
+ ndbrequire(cscanRecSize > 0);
+ for (scanPtr.i = 0; scanPtr.i < cscanRecSize; scanPtr.i++) {
+ ptrAss(scanPtr, scanRec);
+ scanPtr.p->scanNextfreerec = scanPtr.i + 1;
+ scanPtr.p->scanState = ScanRec::SCAN_DISCONNECT;
+ scanPtr.p->scanTimer = 0;
+ scanPtr.p->scanContinuebCounter = 0;
+ }//for
+ scanPtr.i = cscanRecSize - 1;
+ ptrAss(scanPtr, scanRec);
+ scanPtr.p->scanNextfreerec = RNIL;
+ cfirstFreeScanRec = 0;
+}//Dbacc::initialiseScanRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_SR_VER_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseSrVerRec(Signal* signal)
+{
+ ndbrequire(csrVersionRecSize > 0);
+ for (srVersionPtr.i = 0; srVersionPtr.i < csrVersionRecSize; srVersionPtr.i++) {
+ ptrAss(srVersionPtr, srVersionRec);
+ srVersionPtr.p->nextFreeSr = srVersionPtr.i + 1;
+ }//for
+ srVersionPtr.i = csrVersionRecSize - 1;
+ ptrAss(srVersionPtr, srVersionRec);
+ srVersionPtr.p->nextFreeSr = RNIL;
+ cfirstFreeSrVersionRec = 0;
+}//Dbacc::initialiseSrVerRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INITIALISE_TABLE_REC */
+/* INITIALATES THE TABLE RECORDS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initialiseTableRec(Signal* signal)
+{
+ ndbrequire(ctablesize > 0);
+ for (tabptr.i = 0; tabptr.i < ctablesize; tabptr.i++) {
+ refresh_watch_dog();
+ ptrAss(tabptr, tabrec);
+ for (Uint32 i = 0; i < MAX_FRAG_PER_NODE; i++) {
+ tabptr.p->fragholder[i] = RNIL;
+ tabptr.p->fragptrholder[i] = RNIL;
+ }//for
+ tabptr.p->noOfKeyAttr = 0;
+ tabptr.p->hasCharAttr = 0;
+ for (Uint32 k = 0; k < MAX_ATTRIBUTES_IN_INDEX; k++) {
+ tabptr.p->keyAttr[k].attributeDescriptor = 0;
+ tabptr.p->keyAttr[k].charsetInfo = 0;
+ }
+ }//for
+}//Dbacc::initialiseTableRec()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF SYSTEM RESTART MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* ADD/DELETE FRAGMENT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+
+void Dbacc::initRootfragrec(Signal* signal)
+{
+ const AccFragReq * const req = (AccFragReq*)&signal->theData[0];
+ rootfragrecptr.p->mytabptr = req->tableId;
+ rootfragrecptr.p->roothashcheck = req->kValue + req->lhFragBits;
+ rootfragrecptr.p->noOfElements = 0;
+ rootfragrecptr.p->m_commit_count = 0;
+ for (Uint32 i = 0; i < MAX_PARALLEL_SCANS_PER_FRAG; i++) {
+ rootfragrecptr.p->scan[i] = RNIL;
+ }//for
+}//Dbacc::initRootfragrec()
+
+void Dbacc::execACCFRAGREQ(Signal* signal)
+{
+ const AccFragReq * const req = (AccFragReq*)&signal->theData[0];
+ jamEntry();
+ if (ERROR_INSERTED(3001)) {
+ jam();
+ addFragRefuse(signal, 1);
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }
+ tabptr.i = req->tableId;
+#ifndef VM_TRACE
+ // config mismatch - do not crash if release compiled
+ if (tabptr.i >= ctablesize) {
+ jam();
+ addFragRefuse(signal, 640);
+ return;
+ }
+#endif
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+ ndbrequire((req->reqInfo & 0xF) == ZADDFRAG);
+ ndbrequire(!getrootfragmentrec(signal, rootfragrecptr, req->fragId));
+ if (cfirstfreerootfrag == RNIL) {
+ jam();
+ addFragRefuse(signal, ZFULL_ROOTFRAGRECORD_ERROR);
+ return;
+ }//if
+ seizeRootfragrec(signal);
+ if (!addfragtotab(signal, rootfragrecptr.i, req->fragId)) {
+ jam();
+ releaseRootFragRecord(signal, rootfragrecptr);
+ addFragRefuse(signal, ZFULL_ROOTFRAGRECORD_ERROR);
+ return;
+ }//if
+ initRootfragrec(signal);
+ for (Uint32 i = 0; i < 2; i++) {
+ jam();
+ if (cfirstfreefrag == RNIL) {
+ jam();
+ addFragRefuse(signal, ZFULL_FRAGRECORD_ERROR);
+ return;
+ }//if
+ seizeFragrec(signal);
+ initFragGeneral(fragrecptr);
+ initFragAdd(signal, i, rootfragrecptr.i, fragrecptr);
+ rootfragrecptr.p->fragmentptr[i] = fragrecptr.i;
+ rootfragrecptr.p->fragmentid[i] = fragrecptr.p->myfid;
+ if (cfirstfreeDirrange == RNIL) {
+ jam();
+ addFragRefuse(signal, ZDIR_RANGE_ERROR);
+ return;
+ } else {
+ jam();
+ seizeDirrange(signal);
+ }//if
+ fragrecptr.p->directory = newDirRangePtr.i;
+ seizeDirectory(signal);
+ if (tresult < ZLIMIT_OF_ERROR) {
+ jam();
+ newDirRangePtr.p->dirArray[0] = sdDirptr.i;
+ } else {
+ jam();
+ addFragRefuse(signal, tresult);
+ return;
+ }//if
+ seizePage(signal);
+ if (tresult > ZLIMIT_OF_ERROR) {
+ jam();
+ addFragRefuse(signal, tresult);
+ return;
+ }//if
+ sdDirptr.p->pagep[0] = spPageptr.i;
+ tipPageId = 0;
+ inpPageptr = spPageptr;
+ initPage(signal);
+ if (cfirstfreeDirrange == RNIL) {
+ jam();
+ addFragRefuse(signal, ZDIR_RANGE_ERROR);
+ return;
+ } else {
+ jam();
+ seizeDirrange(signal);
+ }//if
+ fragrecptr.p->overflowdir = newDirRangePtr.i;
+ seizeDirectory(signal);
+ if (tresult < ZLIMIT_OF_ERROR) {
+ jam();
+ newDirRangePtr.p->dirArray[0] = sdDirptr.i;
+ } else {
+ jam();
+ addFragRefuse(signal, tresult);
+ return;
+ }//if
+ }//for
+ Uint32 userPtr = req->userPtr;
+ BlockReference retRef = req->userRef;
+ rootfragrecptr.p->rootState = ACTIVEROOT;
+ AccFragConf * const conf = (AccFragConf*)&signal->theData[0];
+
+ conf->userPtr = userPtr;
+ conf->rootFragPtr = rootfragrecptr.i;
+ conf->fragId[0] = rootfragrecptr.p->fragmentid[0];
+ conf->fragId[1] = rootfragrecptr.p->fragmentid[1];
+ conf->fragPtr[0] = rootfragrecptr.p->fragmentptr[0];
+ conf->fragPtr[1] = rootfragrecptr.p->fragmentptr[1];
+ conf->rootHashCheck = rootfragrecptr.p->roothashcheck;
+ sendSignal(retRef, GSN_ACCFRAGCONF, signal, AccFragConf::SignalLength, JBB);
+}//Dbacc::execACCFRAGREQ()
+
+void Dbacc::addFragRefuse(Signal* signal, Uint32 errorCode)
+{
+ const AccFragReq * const req = (AccFragReq*)&signal->theData[0];
+ AccFragRef * const ref = (AccFragRef*)&signal->theData[0];
+ Uint32 userPtr = req->userPtr;
+ BlockReference retRef = req->userRef;
+
+ ref->userPtr = userPtr;
+ ref->errorCode = errorCode;
+ sendSignal(retRef, GSN_ACCFRAGREF, signal, AccFragRef::SignalLength, JBB);
+ return;
+}//Dbacc::addFragRefuseEarly()
+
+void
+Dbacc::execTC_SCHVERREQ(Signal* signal)
+{
+ jamEntry();
+ if (! assembleFragments(signal)) {
+ jam();
+ return;
+ }
+ tabptr.i = signal->theData[0];
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+ Uint32 noOfKeyAttr = signal->theData[6];
+ ndbrequire(noOfKeyAttr <= MAX_ATTRIBUTES_IN_INDEX);
+ Uint32 hasCharAttr = 0;
+
+ SegmentedSectionPtr s0Ptr;
+ signal->getSection(s0Ptr, 0);
+ SectionReader r0(s0Ptr, getSectionSegmentPool());
+ Uint32 i = 0;
+ while (i < noOfKeyAttr) {
+ jam();
+ Uint32 attributeDescriptor = ~0;
+ Uint32 csNumber = ~0;
+ if (! r0.getWord(&attributeDescriptor) ||
+ ! r0.getWord(&csNumber)) {
+ jam();
+ break;
+ }
+ CHARSET_INFO* cs = 0;
+ if (csNumber != 0) {
+ cs = all_charsets[csNumber];
+ ndbrequire(cs != 0);
+ hasCharAttr = 1;
+ }
+ tabptr.p->keyAttr[i].attributeDescriptor = attributeDescriptor;
+ tabptr.p->keyAttr[i].charsetInfo = cs;
+ i++;
+ }
+ ndbrequire(i == noOfKeyAttr);
+ releaseSections(signal);
+
+ tabptr.p->noOfKeyAttr = noOfKeyAttr;
+ tabptr.p->hasCharAttr = hasCharAttr;
+
+ // copy char attr flag to each fragment
+ for (Uint32 i1 = 0; i1 < MAX_FRAG_PER_NODE; i1++) {
+ jam();
+ if (tabptr.p->fragptrholder[i1] != RNIL) {
+ rootfragrecptr.i = tabptr.p->fragptrholder[i1];
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ for (Uint32 i2 = 0; i2 < 2; i2++) {
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[i2];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ fragrecptr.p->hasCharAttr = hasCharAttr;
+ }
+ }
+ }
+
+ // no reply to DICT
+}
+
+void
+Dbacc::execDROP_TAB_REQ(Signal* signal){
+ jamEntry();
+ DropTabReq* req = (DropTabReq*)signal->getDataPtr();
+
+ TabrecPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctablesize, tabrec);
+
+ tabPtr.p->tabUserRef = req->senderRef;
+ tabPtr.p->tabUserPtr = req->senderData;
+
+ signal->theData[0] = ZREL_ROOT_FRAG;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
+}
+
+void Dbacc::releaseRootFragResources(Signal* signal, Uint32 tableId)
+{
+ RootfragmentrecPtr rootPtr;
+ TabrecPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctablesize, tabrec);
+ for (Uint32 i = 0; i < MAX_FRAG_PER_NODE; i++) {
+ jam();
+ if (tabPtr.p->fragholder[i] != RNIL) {
+ jam();
+ Uint32 fragIndex;
+ rootPtr.i = tabPtr.p->fragptrholder[i];
+ ptrCheckGuard(rootPtr, crootfragmentsize, rootfragmentrec);
+ if (rootPtr.p->fragmentptr[0] != RNIL) {
+ jam();
+ fragIndex = rootPtr.p->fragmentptr[0];
+ rootPtr.p->fragmentptr[0] = RNIL;
+ } else if (rootPtr.p->fragmentptr[1] != RNIL) {
+ jam();
+ fragIndex = rootPtr.p->fragmentptr[1];
+ rootPtr.p->fragmentptr[1] = RNIL;
+ } else {
+ jam();
+ releaseRootFragRecord(signal, rootPtr);
+ tabPtr.p->fragholder[i] = RNIL;
+ tabPtr.p->fragptrholder[i] = RNIL;
+ continue;
+ }//if
+ releaseFragResources(signal, fragIndex);
+ return;
+ }//if
+ }//for
+
+ /**
+ * Finished...
+ */
+ sendFSREMOVEREQ(signal, tableId);
+}//Dbacc::releaseRootFragResources()
+
+void Dbacc::releaseRootFragRecord(Signal* signal, RootfragmentrecPtr rootPtr)
+{
+ rootPtr.p->nextroot = cfirstfreerootfrag;
+ cfirstfreerootfrag = rootPtr.i;
+}//Dbacc::releaseRootFragRecord()
+
+void Dbacc::releaseFragResources(Signal* signal, Uint32 fragIndex)
+{
+ FragmentrecPtr regFragPtr;
+ regFragPtr.i = fragIndex;
+ ptrCheckGuard(regFragPtr, cfragmentsize, fragmentrec);
+ verifyFragCorrect(regFragPtr);
+ if (regFragPtr.p->directory != RNIL) {
+ jam();
+ releaseDirResources(signal, regFragPtr.i, regFragPtr.p->directory, 0);
+ regFragPtr.p->directory = RNIL;
+ } else if (regFragPtr.p->overflowdir != RNIL) {
+ jam();
+ releaseDirResources(signal, regFragPtr.i, regFragPtr.p->overflowdir, 0);
+ regFragPtr.p->overflowdir = RNIL;
+ } else if (regFragPtr.p->firstOverflowRec != RNIL) {
+ jam();
+ releaseOverflowResources(signal, regFragPtr);
+ } else if (regFragPtr.p->firstFreeDirindexRec != RNIL) {
+ jam();
+ releaseDirIndexResources(signal, regFragPtr);
+ } else {
+ RootfragmentrecPtr rootPtr;
+ jam();
+ rootPtr.i = regFragPtr.p->myroot;
+ ptrCheckGuard(rootPtr, crootfragmentsize, rootfragmentrec);
+ releaseFragRecord(signal, regFragPtr);
+ signal->theData[0] = ZREL_ROOT_FRAG;
+ signal->theData[1] = rootPtr.p->mytabptr;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+}//Dbacc::releaseFragResources()
+
+void Dbacc::verifyFragCorrect(FragmentrecPtr regFragPtr)
+{
+ for (Uint32 i = 0; i < ZWRITEPAGESIZE; i++) {
+ jam();
+ ndbrequire(regFragPtr.p->datapages[i] == RNIL);
+ }//for
+ ndbrequire(regFragPtr.p->lockOwnersList == RNIL);
+ ndbrequire(regFragPtr.p->firstWaitInQueOp == RNIL);
+ ndbrequire(regFragPtr.p->lastWaitInQueOp == RNIL);
+ ndbrequire(regFragPtr.p->sentWaitInQueOp == RNIL);
+ //ndbrequire(regFragPtr.p->fsConnPtr == RNIL);
+ ndbrequire(regFragPtr.p->zeroPagePtr == RNIL);
+ ndbrequire(regFragPtr.p->nrWaitWriteUndoExit == 0);
+ ndbrequire(regFragPtr.p->sentWaitInQueOp == RNIL);
+}//Dbacc::verifyFragCorrect()
+
+void Dbacc::releaseDirResources(Signal* signal,
+ Uint32 fragIndex,
+ Uint32 dirIndex,
+ Uint32 startIndex)
+{
+ DirRangePtr regDirRangePtr;
+ regDirRangePtr.i = dirIndex;
+ ptrCheckGuard(regDirRangePtr, cdirrangesize, dirRange);
+ for (Uint32 i = startIndex; i < 256; i++) {
+ jam();
+ if (regDirRangePtr.p->dirArray[i] != RNIL) {
+ jam();
+ Uint32 directoryIndex = regDirRangePtr.p->dirArray[i];
+ regDirRangePtr.p->dirArray[i] = RNIL;
+ releaseDirectoryResources(signal, fragIndex, dirIndex, (i + 1), directoryIndex);
+ return;
+ }//if
+ }//for
+ rdDirRangePtr = regDirRangePtr;
+ releaseDirrange(signal);
+ signal->theData[0] = ZREL_FRAG;
+ signal->theData[1] = fragIndex;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
+}//Dbacc::releaseDirResources()
+
+void Dbacc::releaseDirectoryResources(Signal* signal,
+ Uint32 fragIndex,
+ Uint32 dirIndex,
+ Uint32 startIndex,
+ Uint32 directoryIndex)
+{
+ DirectoryarrayPtr regDirPtr;
+ regDirPtr.i = directoryIndex;
+ ptrCheckGuard(regDirPtr, cdirarraysize, directoryarray);
+ for (Uint32 i = 0; i < 256; i++) {
+ jam();
+ if (regDirPtr.p->pagep[i] != RNIL) {
+ jam();
+ rpPageptr.i = regDirPtr.p->pagep[i];
+ ptrCheckGuard(rpPageptr, cpagesize, page8);
+ releasePage(signal);
+ regDirPtr.p->pagep[i] = RNIL;
+ }//if
+ }//for
+ rdDirptr = regDirPtr;
+ releaseDirectory(signal);
+ signal->theData[0] = ZREL_DIR;
+ signal->theData[1] = fragIndex;
+ signal->theData[2] = dirIndex;
+ signal->theData[3] = startIndex;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 4, JBB);
+}//Dbacc::releaseDirectoryResources()
+
+void Dbacc::releaseOverflowResources(Signal* signal, FragmentrecPtr regFragPtr)
+{
+ Uint32 loopCount = 0;
+ OverflowRecordPtr regOverflowRecPtr;
+ while ((regFragPtr.p->firstOverflowRec != RNIL) &&
+ (loopCount < 1)) {
+ jam();
+ regOverflowRecPtr.i = regFragPtr.p->firstOverflowRec;
+ ptrCheckGuard(regOverflowRecPtr, coverflowrecsize, overflowRecord);
+ regFragPtr.p->firstOverflowRec = regOverflowRecPtr.p->nextOverRec;
+ rorOverflowRecPtr = regOverflowRecPtr;
+ releaseOverflowRec(signal);
+ loopCount++;
+ }//while
+ signal->theData[0] = ZREL_FRAG;
+ signal->theData[1] = regFragPtr.i;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
+}//Dbacc::releaseOverflowResources()
+
+void Dbacc::releaseDirIndexResources(Signal* signal, FragmentrecPtr regFragPtr)
+{
+ Uint32 loopCount = 0;
+ OverflowRecordPtr regOverflowRecPtr;
+ while ((regFragPtr.p->firstFreeDirindexRec != RNIL) &&
+ (loopCount < 1)) {
+ jam();
+ regOverflowRecPtr.i = regFragPtr.p->firstFreeDirindexRec;
+ ptrCheckGuard(regOverflowRecPtr, coverflowrecsize, overflowRecord);
+ regFragPtr.p->firstFreeDirindexRec = regOverflowRecPtr.p->nextOverList;
+ rorOverflowRecPtr = regOverflowRecPtr;
+ releaseOverflowRec(signal);
+ loopCount++;
+ }//while
+ signal->theData[0] = ZREL_FRAG;
+ signal->theData[1] = regFragPtr.i;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
+}//Dbacc::releaseDirIndexResources()
+
+void Dbacc::releaseFragRecord(Signal* signal, FragmentrecPtr regFragPtr)
+{
+ regFragPtr.p->nextfreefrag = cfirstfreefrag;
+ cfirstfreefrag = regFragPtr.i;
+ initFragGeneral(regFragPtr);
+}//Dbacc::releaseFragRecord()
+
+void Dbacc::sendFSREMOVEREQ(Signal* signal, Uint32 tableId)
+{
+ FsRemoveReq * const fsReq = (FsRemoveReq *)signal->getDataPtrSend();
+ fsReq->userReference = cownBlockref;
+ fsReq->userPointer = tableId;
+ fsReq->fileNumber[0] = tableId;
+ fsReq->fileNumber[1] = (Uint32)-1; // Remove all fragments
+ fsReq->fileNumber[2] = (Uint32)-1; // Remove all data files within fragment
+ fsReq->fileNumber[3] = 255 | // No P-value used here
+ (3 << 8) | // Data-files in D3
+ (0 << 16) | // Data-files
+ (1 << 24); // Version 1 of fileNumber
+ fsReq->directory = 1;
+ fsReq->ownDirectory = 1;
+ sendSignal(NDBFS_REF, GSN_FSREMOVEREQ, signal, FsRemoveReq::SignalLength, JBA);
+}//Dbacc::sendFSREMOVEREQ()
+
+void Dbacc::execFSREMOVECONF(Signal* signal)
+{
+ FsConf * const fsConf = (FsConf *)signal->getDataPtrSend();
+ TabrecPtr tabPtr;
+ tabPtr.i = fsConf->userPointer;
+ ptrCheckGuard(tabPtr, ctablesize, tabrec);
+
+ DropTabConf * const dropConf = (DropTabConf *)signal->getDataPtrSend();
+ dropConf->senderRef = reference();
+ dropConf->senderData = tabPtr.p->tabUserPtr;
+ dropConf->tableId = tabPtr.i;
+ sendSignal(tabPtr.p->tabUserRef, GSN_DROP_TAB_CONF,
+ signal, DropTabConf::SignalLength, JBB);
+
+ tabPtr.p->tabUserPtr = RNIL;
+ tabPtr.p->tabUserRef = 0;
+}//Dbacc::execFSREMOVECONF()
+
+void Dbacc::execFSREMOVEREF(Signal* signal)
+{
+ ndbrequire(false);
+}//Dbacc::execFSREMOVEREF()
+
+/* -------------------------------------------------------------------------- */
+/* ADDFRAGTOTAB */
+/* DESCRIPTION: PUTS A FRAGMENT ID AND A POINTER TO ITS RECORD INTO */
+/* TABLE ARRRAY OF THE TABLE RECORD. */
+/* -------------------------------------------------------------------------- */
+bool Dbacc::addfragtotab(Signal* signal, Uint32 rootIndex, Uint32 fid)
+{
+ for (Uint32 i = 0; i < MAX_FRAG_PER_NODE; i++) {
+ jam();
+ if (tabptr.p->fragholder[i] == RNIL) {
+ jam();
+ tabptr.p->fragholder[i] = fid;
+ tabptr.p->fragptrholder[i] = rootIndex;
+ return true;
+ }//if
+ }//for
+ return false;
+}//Dbacc::addfragtotab()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF ADD/DELETE FRAGMENT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* CONNECTION MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACCSEIZEREQ SEIZE REQ */
+/* SENDER: LQH, LEVEL B */
+/* ENTER ACCSEIZEREQ WITH */
+/* TUSERPTR , CONECTION PTR OF LQH */
+/* TUSERBLOCKREF BLOCK REFERENCE OF LQH */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACCSEIZEREQ SEIZE REQ */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execACCSEIZEREQ(Signal* signal)
+{
+ jamEntry();
+ tuserptr = signal->theData[0];
+ /* CONECTION PTR OF LQH */
+ tuserblockref = signal->theData[1];
+ /* BLOCK REFERENCE OF LQH */
+ tresult = 0;
+ if (cfreeopRec == RNIL) {
+ jam();
+ refaccConnectLab(signal);
+ return;
+ }//if
+ seizeOpRec(signal);
+ ptrGuard(operationRecPtr);
+ operationRecPtr.p->userptr = tuserptr;
+ operationRecPtr.p->userblockref = tuserblockref;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ operationRecPtr.p->transactionstate = IDLE;
+ /* ******************************< */
+ /* ACCSEIZECONF */
+ /* ******************************< */
+ signal->theData[0] = tuserptr;
+ signal->theData[1] = operationRecPtr.i;
+ sendSignal(tuserblockref, GSN_ACCSEIZECONF, signal, 2, JBB);
+ return;
+}//Dbacc::execACCSEIZEREQ()
+
+void Dbacc::refaccConnectLab(Signal* signal)
+{
+ tresult = ZCONNECT_SIZE_ERROR;
+ /* ******************************< */
+ /* ACCSEIZEREF */
+ /* ******************************< */
+ signal->theData[0] = tuserptr;
+ signal->theData[1] = tresult;
+ sendSignal(tuserblockref, GSN_ACCSEIZEREF, signal, 2, JBB);
+ return;
+}//Dbacc::refaccConnectLab()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF CONNECTION MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* EXECUTE OPERATION MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* INIT_OP_REC */
+/* INFORMATION WHICH IS RECIEVED BY ACCKEYREQ WILL BE SAVED */
+/* IN THE OPERATION RECORD. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initOpRec(Signal* signal)
+{
+ register Uint32 Treqinfo;
+
+ Treqinfo = signal->theData[2];
+
+ operationRecPtr.p->hashValue = signal->theData[3];
+ operationRecPtr.p->tupkeylen = signal->theData[4];
+ operationRecPtr.p->xfrmtupkeylen = signal->theData[4];
+ operationRecPtr.p->transId1 = signal->theData[5];
+ operationRecPtr.p->transId2 = signal->theData[6];
+ operationRecPtr.p->transactionstate = ACTIVE;
+ operationRecPtr.p->commitDeleteCheckFlag = ZFALSE;
+ operationRecPtr.p->operation = Treqinfo & 0x7;
+ /* --------------------------------------------------------------------------------- */
+ // opSimple is not used in this version. Is needed for deadlock handling later on.
+ /* --------------------------------------------------------------------------------- */
+ // operationRecPtr.p->opSimple = (Treqinfo >> 3) & 0x1;
+
+ operationRecPtr.p->lockMode = (Treqinfo >> 4) & 0x3;
+
+ Uint32 readFlag = (((Treqinfo >> 4) & 0x3) == 0); // Only 1 if Read
+ Uint32 dirtyFlag = (((Treqinfo >> 6) & 0x1) == 1); // Only 1 if Dirty
+ Uint32 dirtyReadFlag = readFlag & dirtyFlag;
+ operationRecPtr.p->dirtyRead = dirtyReadFlag;
+
+ operationRecPtr.p->nodeType = (Treqinfo >> 7) & 0x3;
+ operationRecPtr.p->fid = fragrecptr.p->myfid;
+ operationRecPtr.p->fragptr = fragrecptr.i;
+ operationRecPtr.p->nextParallelQue = RNIL;
+ operationRecPtr.p->prevParallelQue = RNIL;
+ operationRecPtr.p->prevQueOp = RNIL;
+ operationRecPtr.p->nextQueOp = RNIL;
+ operationRecPtr.p->nextSerialQue = RNIL;
+ operationRecPtr.p->prevSerialQue = RNIL;
+ operationRecPtr.p->elementPage = RNIL;
+ operationRecPtr.p->keyinfoPage = RNIL;
+ operationRecPtr.p->lockOwner = ZFALSE;
+ operationRecPtr.p->insertIsDone = ZFALSE;
+ operationRecPtr.p->elementIsDisappeared = ZFALSE;
+ operationRecPtr.p->insertDeleteLen = fragrecptr.p->elementLength;
+ operationRecPtr.p->longPagePtr = RNIL;
+ operationRecPtr.p->longKeyPageIndex = RNIL;
+ operationRecPtr.p->scanRecPtr = RNIL;
+
+ // bit to mark lock operation
+ operationRecPtr.p->isAccLockReq = (Treqinfo >> 31) & 0x1;
+
+ // undo log is not run via ACCKEYREQ
+ operationRecPtr.p->isUndoLogReq = 0;
+}//Dbacc::initOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEND_ACCKEYCONF */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::sendAcckeyconf(Signal* signal)
+{
+ signal->theData[0] = operationRecPtr.p->userptr;
+ signal->theData[1] = operationRecPtr.p->insertIsDone;
+ signal->theData[2] = operationRecPtr.p->fid;
+ signal->theData[3] = operationRecPtr.p->localdata[0];
+ signal->theData[4] = operationRecPtr.p->localdata[1];
+ signal->theData[5] = fragrecptr.p->localkeylen;
+}//Dbacc::sendAcckeyconf()
+
+
+void Dbacc::ACCKEY_error(Uint32 fromWhere)
+{
+ switch(fromWhere) {
+ case 0:
+ ndbrequire(false);
+ case 1:
+ ndbrequire(false);
+ case 2:
+ ndbrequire(false);
+ case 3:
+ ndbrequire(false);
+ case 4:
+ ndbrequire(false);
+ case 5:
+ ndbrequire(false);
+ case 6:
+ ndbrequire(false);
+ case 7:
+ ndbrequire(false);
+ case 8:
+ ndbrequire(false);
+ case 9:
+ ndbrequire(false);
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dbacc::ACCKEY_error()
+
+/* ******************--------------------------------------------------------------- */
+/* ACCKEYREQ REQUEST FOR INSERT, DELETE, */
+/* RERAD AND UPDATE, A TUPLE. */
+/* SENDER: LQH, LEVEL B */
+/* SIGNAL DATA: OPERATION_REC_PTR, CONNECTION PTR */
+/* TABPTR, TABLE ID = TABLE RECORD POINTER */
+/* TREQINFO, */
+/* THASHVALUE, HASH VALUE OF THE TUP */
+/* TKEYLEN, LENGTH OF THE PRIMARY KEYS */
+/* TKEY1, PRIMARY KEY 1 */
+/* TKEY2, PRIMARY KEY 2 */
+/* TKEY3, PRIMARY KEY 3 */
+/* TKEY4, PRIMARY KEY 4 */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::execACCKEYREQ(Signal* signal)
+{
+ jamEntry();
+ operationRecPtr.i = signal->theData[0]; /* CONNECTION PTR */
+ fragrecptr.i = signal->theData[1]; /* FRAGMENT RECORD POINTER */
+ if (!((operationRecPtr.i < coprecsize) ||
+ (fragrecptr.i < cfragmentsize))) {
+ ACCKEY_error(0);
+ return;
+ }//if
+ ptrAss(operationRecPtr, operationrec);
+ ptrAss(fragrecptr, fragmentrec);
+ ndbrequire(operationRecPtr.p->transactionstate == IDLE);
+
+ initOpRec(signal);
+ // normalize key if any char attr
+ if (! operationRecPtr.p->isAccLockReq && fragrecptr.p->hasCharAttr)
+ xfrmKeyData(signal);
+
+ /*---------------------------------------------------------------*/
+ /* */
+ /* WE WILL USE THE HASH VALUE TO LOOK UP THE PROPER MEMORY */
+ /* PAGE AND MEMORY PAGE INDEX TO START THE SEARCH WITHIN. */
+ /* WE REMEMBER THESE ADDRESS IF WE LATER NEED TO INSERT */
+ /* THE ITEM AFTER NOT FINDING THE ITEM. */
+ /*---------------------------------------------------------------*/
+ getElement(signal);
+
+ if (tgeResult == ZTRUE) {
+ switch (operationRecPtr.p->operation) {
+ case ZREAD:
+ case ZUPDATE:
+ case ZDELETE:
+ case ZWRITE:
+ case ZSCAN_OP:
+ if (!tgeLocked){
+ sendAcckeyconf(signal);
+ if (operationRecPtr.p->dirtyRead == ZFALSE) {
+ /*---------------------------------------------------------------*/
+ // It is not a dirty read. We proceed by locking and continue with
+ // the operation.
+ /*---------------------------------------------------------------*/
+ Uint32 eh = gePageptr.p->word32[tgeElementptr];
+ operationRecPtr.p->scanBits = ElementHeader::getScanBits(eh);
+ operationRecPtr.p->hashvaluePart = ElementHeader::getHashValuePart(eh);
+ operationRecPtr.p->elementPage = gePageptr.i;
+ operationRecPtr.p->elementContainer = tgeContainerptr;
+ operationRecPtr.p->elementPointer = tgeElementptr;
+ operationRecPtr.p->elementIsforward = tgeForward;
+
+ eh = ElementHeader::setLocked(operationRecPtr.i);
+ dbgWord32(gePageptr, tgeElementptr, eh);
+ gePageptr.p->word32[tgeElementptr] = eh;
+
+ insertLockOwnersList(signal , operationRecPtr);
+ return;
+ } else {
+ jam();
+ /*---------------------------------------------------------------*/
+ // It is a dirty read. We do not lock anything. Set state to
+ // IDLE since no COMMIT call will come.
+ /*---------------------------------------------------------------*/
+ operationRecPtr.p->transactionstate = IDLE;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ return;
+ }//if
+ } else {
+ jam();
+ accIsLockedLab(signal);
+ return;
+ }//if
+ break;
+ case ZINSERT:
+ jam();
+ insertExistElemLab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ } else if (tgeResult == ZFALSE) {
+ switch (operationRecPtr.p->operation) {
+ case ZINSERT:
+ case ZWRITE:
+ jam();
+ // If a write operation makes an insert we switch operation to ZINSERT so
+ // that the commit-method knows an insert has been made and updates noOfElements.
+ operationRecPtr.p->operation = ZINSERT;
+ operationRecPtr.p->insertIsDone = ZTRUE;
+ insertelementLab(signal);
+ return;
+ break;
+ case ZREAD:
+ case ZUPDATE:
+ case ZDELETE:
+ case ZSCAN_OP:
+ jam();
+ acckeyref1Lab(signal, ZREAD_ERROR);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ } else {
+ jam();
+ acckeyref1Lab(signal, tgeResult);
+ return;
+ }//if
+ return;
+}//Dbacc::execACCKEYREQ()
+
+void
+Dbacc::xfrmKeyData(Signal* signal)
+{
+ tabptr.i = fragrecptr.p->myTableId;
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+
+ Uint32 dst[1024 * MAX_XFRM_MULTIPLY];
+ Uint32 dstSize = (sizeof(dst) >> 2);
+ Uint32* src = &signal->theData[7];
+ const Uint32 noOfKeyAttr = tabptr.p->noOfKeyAttr;
+ Uint32 dstPos = 0;
+ Uint32 srcPos = 0;
+ Uint32 i = 0;
+
+ while (i < noOfKeyAttr) {
+ const Tabrec::KeyAttr& keyAttr = tabptr.p->keyAttr[i];
+
+ Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(keyAttr.attributeDescriptor);
+ Uint32 srcWords = (srcBytes + 3) / 4;
+ Uint32 dstWords = ~0;
+ uchar* dstPtr = (uchar*)&dst[dstPos];
+ const uchar* srcPtr = (const uchar*)&src[srcPos];
+ CHARSET_INFO* cs = keyAttr.charsetInfo;
+
+ if (cs == 0) {
+ jam();
+ memcpy(dstPtr, srcPtr, srcWords << 2);
+ dstWords = srcWords;
+ } else {
+ jam();
+ Uint32 typeId = AttributeDescriptor::getType(keyAttr.attributeDescriptor);
+ Uint32 lb, len;
+ bool ok = NdbSqlUtil::get_var_length(typeId, srcPtr, srcBytes, lb, len);
+ ndbrequire(ok);
+ Uint32 xmul = cs->strxfrm_multiply;
+ if (xmul == 0)
+ xmul = 1;
+ // see comment in DbtcMain.cpp
+ Uint32 dstLen = xmul * (srcBytes - lb);
+ ndbrequire(dstLen <= ((dstSize - dstPos) << 2));
+ int n = NdbSqlUtil::strnxfrm_bug7284(cs, dstPtr, dstLen, srcPtr + lb, len);
+ ndbrequire(n != -1);
+ while ((n & 3) != 0)
+ dstPtr[n++] = 0;
+ dstWords = (n >> 2);
+ }
+ dstPos += dstWords;
+ srcPos += srcWords;
+ i++;
+ }
+ memcpy(src, dst, dstPos << 2);
+ operationRecPtr.p->xfrmtupkeylen = dstPos;
+}
+
+void Dbacc::accIsLockedLab(Signal* signal)
+{
+ ndbrequire(csystemRestart == ZFALSE);
+ queOperPtr.i = ElementHeader::getOpPtrI(gePageptr.p->word32[tgeElementptr]);
+ ptrCheckGuard(queOperPtr, coprecsize, operationrec);
+ if (operationRecPtr.p->dirtyRead == ZFALSE) {
+ Uint32 return_result;
+ if (operationRecPtr.p->lockMode == ZREADLOCK) {
+ jam();
+ priPageptr = gePageptr;
+ tpriElementptr = tgeElementptr;
+ return_result = placeReadInLockQueue(signal);
+ } else {
+ jam();
+ pwiPageptr = gePageptr;
+ tpwiElementptr = tgeElementptr;
+ return_result = placeWriteInLockQueue(signal);
+ }//if
+ if (return_result == ZPARALLEL_QUEUE) {
+ jam();
+ sendAcckeyconf(signal);
+ return;
+ } else if (return_result == ZSERIAL_QUEUE) {
+ jam();
+ signal->theData[0] = RNIL;
+ return;
+ } else if (return_result == ZWRITE_ERROR) {
+ jam();
+ acckeyref1Lab(signal, return_result);
+ return;
+ }//if
+ ndbrequire(false);
+ } else {
+ if (queOperPtr.p->elementIsDisappeared == ZFALSE) {
+ jam();
+ /*---------------------------------------------------------------*/
+ // It is a dirty read. We do not lock anything. Set state to
+ // IDLE since no COMMIT call will arrive.
+ /*---------------------------------------------------------------*/
+ sendAcckeyconf(signal);
+ operationRecPtr.p->transactionstate = IDLE;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ return;
+ } else {
+ jam();
+ /*---------------------------------------------------------------*/
+ // The tuple does not exist in the committed world currently.
+ // Report read error.
+ /*---------------------------------------------------------------*/
+ acckeyref1Lab(signal, ZREAD_ERROR);
+ return;
+ }//if
+ }//if
+}//Dbacc::accIsLockedLab()
+
+/* --------------------------------------------------------------------------------- */
+/* I N S E R T E X I S T E L E M E N T */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::insertExistElemLab(Signal* signal)
+{
+ if (!tgeLocked){
+ jam();
+ acckeyref1Lab(signal, ZWRITE_ERROR);/* THE ELEMENT ALREADY EXIST */
+ return;
+ }//if
+ accIsLockedLab(signal);
+}//Dbacc::insertExistElemLab()
+
+/* --------------------------------------------------------------------------------- */
+/* INSERTELEMENT */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::insertelementLab(Signal* signal)
+{
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_OPERATION) {
+ jam();
+ acckeyref1Lab(signal, ZTEMPORARY_ACC_UNDO_FAILURE);
+ return;
+ }//if
+ }//if
+ if (fragrecptr.p->firstOverflowRec == RNIL) {
+ jam();
+ allocOverflowPage(signal);
+ if (tresult > ZLIMIT_OF_ERROR) {
+ jam();
+ acckeyref1Lab(signal, tresult);
+ return;
+ }//if
+ }//if
+ if (fragrecptr.p->keyLength != operationRecPtr.p->tupkeylen) {
+ // historical
+ ndbrequire(fragrecptr.p->keyLength == 0);
+ }//if
+
+ signal->theData[0] = operationRecPtr.p->userptr;
+ Uint32 blockNo = refToBlock(operationRecPtr.p->userblockref);
+ EXECUTE_DIRECT(blockNo, GSN_LQH_ALLOCREQ, signal, 1);
+ jamEntry();
+ if (signal->theData[0] != 0) {
+ jam();
+ Uint32 result_code = signal->theData[0];
+ acckeyref1Lab(signal, result_code);
+ return;
+ }//if
+ Uint32 localKey = (signal->theData[1] << MAX_TUPLES_BITS) + signal->theData[2];
+
+ insertLockOwnersList(signal, operationRecPtr);
+
+ const Uint32 tmp = fragrecptr.p->k + fragrecptr.p->lhfragbits;
+ operationRecPtr.p->hashvaluePart =
+ (operationRecPtr.p->hashValue >> tmp) & 0xFFFF;
+ operationRecPtr.p->scanBits = 0; /* NOT ANY ACTIVE SCAN */
+ tidrElemhead = ElementHeader::setLocked(operationRecPtr.i);
+ idrPageptr = gdiPageptr;
+ tidrPageindex = tgdiPageindex;
+ tidrForward = ZTRUE;
+ idrOperationRecPtr = operationRecPtr;
+ clocalkey[0] = localKey;
+ operationRecPtr.p->localdata[0] = localKey;
+ /* --------------------------------------------------------------------------------- */
+ /* WE SET THE LOCAL KEY TO MINUS ONE TO INDICATE IT IS NOT YET VALID. */
+ /* --------------------------------------------------------------------------------- */
+ insertElement(signal);
+ sendAcckeyconf(signal);
+ return;
+}//Dbacc::insertelementLab()
+
+/* --------------------------------------------------------------------------------- */
+/* PLACE_READ_IN_LOCK_QUEUE */
+/* INPUT: OPERATION_REC_PTR OUR OPERATION POINTER */
+/* QUE_OPER_PTR LOCK QUEUE OWNER OPERATION POINTER */
+/* PRI_PAGEPTR PAGE POINTER OF ELEMENT */
+/* TPRI_ELEMENTPTR ELEMENT POINTER OF ELEMENT */
+/* OUTPUT TRESULT = */
+/* ZPARALLEL_QUEUE OPERATION PLACED IN PARALLEL QUEUE */
+/* OPERATION CAN PROCEED NOW. */
+/* ZSERIAL_QUEUE OPERATION PLACED IN SERIAL QUEUE */
+/* ERROR CODE OPERATION NEEDS ABORTING */
+/* THE ELEMENT WAS LOCKED AND WE WANT TO READ THE TUPLE. WE WILL CHECK THE LOCK */
+/* QUEUES TO PERFORM THE PROPER ACTION. */
+/* */
+/* IN SOME PLACES IN THE CODE BELOW THAT HANDLES WHAT TO DO WHEN THE TUPLE IS LOCKED */
+/* WE DO ASSUME THAT NEXT_PARALLEL_QUEUE AND NEXT_SERIAL_QUEUE ON OPERATION_REC_PTR */
+/* HAVE BEEN INITIALISED TO RNIL. THUS WE DO NOT PERFORM THIS ONCE MORE EVEN IF IT */
+/* COULD BE NICE FOR READABILITY. */
+/* --------------------------------------------------------------------------------- */
+Uint32 Dbacc::placeReadInLockQueue(Signal* signal)
+{
+ tgnptMainOpPtr = queOperPtr;
+ getNoParallelTransaction(signal);
+ if (tgnptNrTransaction == 1) {
+ if ((queOperPtr.p->transId1 == operationRecPtr.p->transId1) &&
+ (queOperPtr.p->transId2 == operationRecPtr.p->transId2)) {
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE PERFORMING A READ OPERATION AND THIS TRANSACTION ALREADY OWNS THE LOCK */
+ /* ALONE. PUT THE OPERATION LAST IN THE PARALLEL QUEUE. */
+ /* --------------------------------------------------------------------------------- */
+ jam();
+ mlpqOperPtr = queOperPtr;
+ moveLastParallelQueue(signal);
+ operationRecPtr.p->localdata[0] = queOperPtr.p->localdata[0];
+ operationRecPtr.p->localdata[1] = queOperPtr.p->localdata[1];
+ operationRecPtr.p->prevParallelQue = mlpqOperPtr.i;
+ mlpqOperPtr.p->nextParallelQue = operationRecPtr.i;
+ switch (queOperPtr.p->lockMode) {
+ case ZREADLOCK:
+ jam();
+ /*empty*/;
+ break;
+ default:
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* IF THE TRANSACTION PREVIOUSLY SET A WRITE LOCK WE MUST ENSURE THAT ALL */
+ /* OPERATIONS IN THE PARALLEL QUEUE HAVE WRITE LOCK MODE TO AVOID STRANGE BUGS.*/
+ /* --------------------------------------------------------------------------------- */
+ operationRecPtr.p->lockMode = queOperPtr.p->lockMode;
+ break;
+ }//switch
+ return ZPARALLEL_QUEUE;
+ }//if
+ }//if
+ if (queOperPtr.p->nextSerialQue == RNIL) {
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE PERFORMING A READ OPERATION AND THERE IS NO SERIAL QUEUE. IF THERE IS NO */
+ /* WRITE OPERATION THAT OWNS THE LOCK OR ANY WRITE OPERATION IN THE PARALLEL QUEUE */
+ /* IT IS ENOUGH TO CHECK THE LOCK MODE OF THE LEADER IN THE PARALLEL QUEUE. IF IT IS */
+ /* A READ LOCK THEN WE PLACE OURSELVES IN THE PARALLEL QUEUE OTHERWISE WE GO ON TO */
+ /* PLACE OURSELVES IN THE SERIAL QUEUE. */
+ /* --------------------------------------------------------------------------------- */
+ switch (queOperPtr.p->lockMode) {
+ case ZREADLOCK:
+ jam();
+ mlpqOperPtr = queOperPtr;
+ moveLastParallelQueue(signal);
+ operationRecPtr.p->prevParallelQue = mlpqOperPtr.i;
+ mlpqOperPtr.p->nextParallelQue = operationRecPtr.i;
+ operationRecPtr.p->localdata[0] = queOperPtr.p->localdata[0];
+ operationRecPtr.p->localdata[1] = queOperPtr.p->localdata[1];
+ return ZPARALLEL_QUEUE;
+ default:
+ jam();
+ queOperPtr.p->nextSerialQue = operationRecPtr.i;
+ operationRecPtr.p->prevSerialQue = queOperPtr.i;
+ putOpInFragWaitQue(signal);
+ break;
+ }//switch
+ } else {
+ jam();
+ placeSerialQueueRead(signal);
+ }//if
+ return ZSERIAL_QUEUE;
+}//Dbacc::placeReadInLockQueue()
+
+/* --------------------------------------------------------------------------------- */
+/* WE WILL CHECK IF THIS TRANSACTION IS ALREADY PLACED AT SOME SPOT IN THE PARALLEL */
+/* SERIAL QUEUE WITHOUT ANY NEIGHBORS FROM OTHER TRANSACTION. IF SO WE WILL INSERT */
+/* IT IN THAT PARALLEL QUEUE. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::placeSerialQueueRead(Signal* signal)
+{
+ readWriteOpPtr.i = queOperPtr.p->nextSerialQue;
+ ptrCheckGuard(readWriteOpPtr, coprecsize, operationrec);
+ PSQR_LOOP:
+ jam();
+ if (readWriteOpPtr.p->nextSerialQue == RNIL) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THERE WAS NO PREVIOUS OPERATION IN THIS TRANSACTION WHICH WE COULD PUT IT */
+ /* IN THE PARALLEL QUEUE TOGETHER WITH. */
+ /* --------------------------------------------------------------------------------- */
+ checkOnlyReadEntry(signal);
+ return;
+ }//if
+ tgnptMainOpPtr = readWriteOpPtr;
+ getNoParallelTransaction(signal);
+ if (tgnptNrTransaction == 1) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THERE WAS ONLY ONE TRANSACTION INVOLVED IN THE PARALLEL QUEUE. IF THIS IS OUR */
+ /* TRANSACTION WE CAN STILL GET HOLD OF THE LOCK. */
+ /* --------------------------------------------------------------------------------- */
+ if ((readWriteOpPtr.p->transId1 == operationRecPtr.p->transId1) &&
+ (readWriteOpPtr.p->transId2 == operationRecPtr.p->transId2)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE PERFORMING A READ IN THE SAME TRANSACTION WHERE WE ALREADY */
+ /* PREVIOUSLY HAVE EXECUTED AN OPERATION. INSERT-DELETE, READ-UPDATE, READ-READ, */
+ /* UPDATE-UPDATE, UPDATE-DELETE, READ-DELETE, INSERT-READ, INSERT-UPDATE ARE ALLOWED */
+ /* COMBINATIONS. A NEW INSERT AFTER A DELETE IS NOT ALLOWED AND SUCH AN INSERT WILL */
+ /* GO TO THE SERIAL LOCK QUEUE WHICH IT WILL NOT LEAVE UNTIL A TIME-OUT AND THE */
+ /* TRANSACTION IS ABORTED. READS AND UPDATES AFTER DELETES IS ALSO NOT ALLOWED. */
+ /* --------------------------------------------------------------------------------- */
+ mlpqOperPtr = readWriteOpPtr;
+ moveLastParallelQueue(signal);
+ readWriteOpPtr = mlpqOperPtr;
+ operationRecPtr.p->prevParallelQue = readWriteOpPtr.i;
+ readWriteOpPtr.p->nextParallelQue = operationRecPtr.i;
+ operationRecPtr.p->localdata[0] = readWriteOpPtr.p->localdata[0];
+ operationRecPtr.p->localdata[1] = readWriteOpPtr.p->localdata[1];
+ switch (readWriteOpPtr.p->lockMode) {
+ case ZREADLOCK:
+ jam();
+ /*empty*/;
+ break;
+ default:
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* IF THE TRANSACTION PREVIOUSLY SET A WRITE LOCK WE MUST ENSURE THAT ALL */
+ /* OPERATIONS IN THE PARALLEL QUEUE HAVE WRITE LOCK MODE TO AVOID STRANGE BUGS.*/
+ /* --------------------------------------------------------------------------------- */
+ operationRecPtr.p->lockMode = readWriteOpPtr.p->lockMode;
+ break;
+ }//switch
+ putOpInFragWaitQue(signal);
+ return;
+ }//if
+ }//if
+ readWriteOpPtr.i = readWriteOpPtr.p->nextSerialQue;
+ ptrCheckGuard(readWriteOpPtr, coprecsize, operationrec);
+ goto PSQR_LOOP;
+}//Dbacc::placeSerialQueueRead()
+
+/* --------------------------------------------------------------------------------- */
+/* WE WILL CHECK IF THE LAST ENTRY IN THE SERIAL QUEUE CONTAINS ONLY READ */
+/* OPERATIONS. IF SO WE WILL INSERT IT IN THAT PARALLEL QUEUE. OTHERWISE WE */
+/* WILL PLACE IT AT THE END OF THE SERIAL QUEUE. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::checkOnlyReadEntry(Signal* signal)
+{
+ switch (readWriteOpPtr.p->lockMode) {
+ case ZREADLOCK:
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* SINCE THIS LAST QUEUE ONLY CONTAINS READ LOCKS WE CAN JOIN THE PARALLEL QUEUE AT */
+ /* THE END. */
+ /* --------------------------------------------------------------------------------- */
+ mlpqOperPtr = readWriteOpPtr;
+ moveLastParallelQueue(signal);
+ readWriteOpPtr = mlpqOperPtr;
+ operationRecPtr.p->prevParallelQue = readWriteOpPtr.i;
+ readWriteOpPtr.p->nextParallelQue = operationRecPtr.i;
+ operationRecPtr.p->localdata[0] = readWriteOpPtr.p->localdata[0];
+ operationRecPtr.p->localdata[1] = readWriteOpPtr.p->localdata[1];
+ break;
+ default:
+ jam(); /* PUT THE OPERATION RECORD IN THE SERIAL QUEUE */
+ readWriteOpPtr.p->nextSerialQue = operationRecPtr.i;
+ operationRecPtr.p->prevSerialQue = readWriteOpPtr.i;
+ break;
+ }//switch
+ putOpInFragWaitQue(signal);
+}//Dbacc::checkOnlyReadEntry()
+
+/* --------------------------------------------------------------------------------- */
+/* GET_NO_PARALLEL_TRANSACTION */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::getNoParallelTransaction(Signal* signal)
+{
+ OperationrecPtr tnptOpPtr;
+
+ tgnptNrTransaction = 1;
+ tnptOpPtr.i = tgnptMainOpPtr.p->nextParallelQue;
+ while ((tnptOpPtr.i != RNIL) &&
+ (tgnptNrTransaction == 1)) {
+ jam();
+ ptrCheckGuard(tnptOpPtr, coprecsize, operationrec);
+ if ((tnptOpPtr.p->transId1 == tgnptMainOpPtr.p->transId1) &&
+ (tnptOpPtr.p->transId2 == tgnptMainOpPtr.p->transId2)) {
+ tnptOpPtr.i = tnptOpPtr.p->nextParallelQue;
+ } else {
+ jam();
+ tgnptNrTransaction++;
+ }//if
+ }//while
+}//Dbacc::getNoParallelTransaction()
+
+void Dbacc::moveLastParallelQueue(Signal* signal)
+{
+ while (mlpqOperPtr.p->nextParallelQue != RNIL) {
+ jam();
+ mlpqOperPtr.i = mlpqOperPtr.p->nextParallelQue;
+ ptrCheckGuard(mlpqOperPtr, coprecsize, operationrec);
+ }//if
+}//Dbacc::moveLastParallelQueue()
+
+void Dbacc::moveLastParallelQueueWrite(Signal* signal)
+{
+ /* --------------------------------------------------------------------------------- */
+ /* ENSURE THAT ALL OPERATIONS HAVE LOCK MODE SET TO WRITE SINCE WE INSERT A */
+ /* WRITE LOCK INTO THE PARALLEL QUEUE. */
+ /* --------------------------------------------------------------------------------- */
+ while (mlpqOperPtr.p->nextParallelQue != RNIL) {
+ jam();
+ mlpqOperPtr.p->lockMode = operationRecPtr.p->lockMode;
+ mlpqOperPtr.i = mlpqOperPtr.p->nextParallelQue;
+ ptrCheckGuard(mlpqOperPtr, coprecsize, operationrec);
+ }//if
+ mlpqOperPtr.p->lockMode = operationRecPtr.p->lockMode;
+}//Dbacc::moveLastParallelQueueWrite()
+
+/* --------------------------------------------------------------------------------- */
+/* PLACE_WRITE_IN_LOCK_QUEUE */
+/* INPUT: OPERATION_REC_PTR OUR OPERATION POINTER */
+/* QUE_OPER_PTR LOCK QUEUE OWNER OPERATION POINTER */
+/* PWI_PAGEPTR PAGE POINTER OF ELEMENT */
+/* TPWI_ELEMENTPTR ELEMENT POINTER OF ELEMENT */
+/* OUTPUT TRESULT = */
+/* ZPARALLEL_QUEUE OPERATION PLACED IN PARALLEL QUEUE */
+/* OPERATION CAN PROCEED NOW. */
+/* ZSERIAL_QUEUE OPERATION PLACED IN SERIAL QUEUE */
+/* ERROR CODE OPERATION NEEDS ABORTING */
+/* --------------------------------------------------------------------------------- */
+Uint32 Dbacc::placeWriteInLockQueue(Signal* signal)
+{
+ tgnptMainOpPtr = queOperPtr;
+ getNoParallelTransaction(signal);
+ if (!((tgnptNrTransaction == 1) &&
+ (queOperPtr.p->transId1 == operationRecPtr.p->transId1) &&
+ (queOperPtr.p->transId2 == operationRecPtr.p->transId2))) {
+ jam();
+ placeSerialQueueWrite(signal);
+ return ZSERIAL_QUEUE;
+ }//if
+
+ /*
+ WE ARE PERFORMING AN READ EXCLUSIVE, INSERT, UPDATE OR DELETE IN THE SAME
+ TRANSACTION WHERE WE PREVIOUSLY HAVE EXECUTED AN OPERATION.
+ Read-All, Update-All, Insert-All and Delete-Insert are allowed
+ combinations.
+ Delete-Read, Delete-Update and Delete-Delete are not an allowed
+ combination and will result in tuple not found error.
+ */
+ mlpqOperPtr = queOperPtr;
+ moveLastParallelQueueWrite(signal);
+
+ if (operationRecPtr.p->operation == ZINSERT &&
+ mlpqOperPtr.p->operation != ZDELETE){
+ jam();
+ return ZWRITE_ERROR;
+ }//if
+
+ operationRecPtr.p->localdata[0] = queOperPtr.p->localdata[0];
+ operationRecPtr.p->localdata[1] = queOperPtr.p->localdata[1];
+ operationRecPtr.p->prevParallelQue = mlpqOperPtr.i;
+ mlpqOperPtr.p->nextParallelQue = operationRecPtr.i;
+ return ZPARALLEL_QUEUE;
+}//Dbacc::placeWriteInLockQueue()
+
+/* --------------------------------------------------------------------------------- */
+/* WE HAVE TO PLACE IT SOMEWHERE IN THE SERIAL QUEUE INSTEAD. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::placeSerialQueueWrite(Signal* signal)
+{
+ readWriteOpPtr = queOperPtr;
+ PSQW_LOOP:
+ if (readWriteOpPtr.p->nextSerialQue == RNIL) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE COULD NOT PUT IN ANY PARALLEL QUEUE. WE MUST PUT IT LAST IN THE SERIAL QUEUE. */
+ /* --------------------------------------------------------------------------------- */
+ readWriteOpPtr.p->nextSerialQue = operationRecPtr.i;
+ operationRecPtr.p->prevSerialQue = readWriteOpPtr.i;
+ putOpInFragWaitQue(signal);
+ return;
+ }//if
+ readWriteOpPtr.i = readWriteOpPtr.p->nextSerialQue;
+ ptrCheckGuard(readWriteOpPtr, coprecsize, operationrec);
+ tgnptMainOpPtr = readWriteOpPtr;
+ getNoParallelTransaction(signal);
+ if (tgnptNrTransaction == 1) {
+ /* --------------------------------------------------------------------------------- */
+ /* THERE WAS ONLY ONE TRANSACTION INVOLVED IN THE PARALLEL QUEUE. IF THIS IS OUR */
+ /* TRANSACTION WE CAN STILL GET HOLD OF THE LOCK. */
+ /* --------------------------------------------------------------------------------- */
+ if ((readWriteOpPtr.p->transId1 == operationRecPtr.p->transId1) &&
+ (readWriteOpPtr.p->transId2 == operationRecPtr.p->transId2)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE PERFORMING AN UPDATE OR DELETE IN THE SAME TRANSACTION WHERE WE ALREADY */
+ /* PREVIOUSLY HAVE EXECUTED AN OPERATION. INSERT-DELETE, READ-UPDATE, READ-READ, */
+ /* UPDATE-UPDATE, UPDATE-DELETE, READ-DELETE, INSERT-READ, INSERT-UPDATE ARE ALLOWED */
+ /* COMBINATIONS. A NEW INSERT AFTER A DELETE IS NOT ALLOWED AND SUCH AN INSERT WILL */
+ /* GO TO THE SERIAL LOCK QUEUE WHICH IT WILL NOT LEAVE UNTIL A TIME-OUT AND THE */
+ /* TRANSACTION IS ABORTED. READS AND UPDATES AFTER DELETES IS ALSO NOT ALLOWED. */
+ /* --------------------------------------------------------------------------------- */
+ mlpqOperPtr = readWriteOpPtr;
+ moveLastParallelQueueWrite(signal);
+ readWriteOpPtr = mlpqOperPtr;
+ operationRecPtr.p->prevParallelQue = readWriteOpPtr.i;
+ readWriteOpPtr.p->nextParallelQue = operationRecPtr.i;
+ operationRecPtr.p->localdata[0] = readWriteOpPtr.p->localdata[0];
+ operationRecPtr.p->localdata[1] = readWriteOpPtr.p->localdata[1];
+ putOpInFragWaitQue(signal);
+ return;
+ }//if
+ }//if
+ goto PSQW_LOOP;
+}//Dbacc::placeSerialQueueWrite()
+
+/* ------------------------------------------------------------------------- */
+/* ACC KEYREQ END */
+/* ------------------------------------------------------------------------- */
+void Dbacc::acckeyref1Lab(Signal* signal, Uint32 result_code)
+{
+ if (operationRecPtr.p->keyinfoPage != RNIL) {
+ jam();
+ rpPageptr.i = operationRecPtr.p->keyinfoPage;
+ ptrCheckGuard(rpPageptr, cpagesize, page8);
+ releasePage(signal);
+ operationRecPtr.p->keyinfoPage = RNIL;
+ }//if
+ operationRecPtr.p->transactionstate = WAIT_COMMIT_ABORT;
+ /* ************************<< */
+ /* ACCKEYREF */
+ /* ************************<< */
+ signal->theData[0] = cminusOne;
+ signal->theData[1] = result_code;
+ return;
+}//Dbacc::acckeyref1Lab()
+
+/* ******************--------------------------------------------------------------- */
+/* ACCMINUPDATE UPDATE LOCAL KEY REQ */
+/* DESCRIPTION: UPDATES LOCAL KEY OF AN ELEMENTS IN THE HASH TABLE */
+/* THIS SIGNAL IS WAITED AFTER ANY INSERT REQ */
+/* ENTER ACCMINUPDATE WITH SENDER: LQH, LEVEL B */
+/* OPERATION_REC_PTR, OPERATION RECORD PTR */
+/* CLOCALKEY(0), LOCAL KEY 1 */
+/* CLOCALKEY(1) LOCAL KEY 2 */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::execACCMINUPDATE(Signal* signal)
+{
+ Page8Ptr ulkPageidptr;
+ Uint32 tulkLocalPtr;
+ Uint32 tlocalkey1, tlocalkey2;
+ Uint32 TlogStart;
+
+ jamEntry();
+ operationRecPtr.i = signal->theData[0];
+ tlocalkey1 = signal->theData[1];
+ tlocalkey2 = signal->theData[2];
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ if (operationRecPtr.p->transactionstate == ACTIVE) {
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ulkPageidptr.i = operationRecPtr.p->elementPage;
+ tulkLocalPtr = operationRecPtr.p->elementPointer + operationRecPtr.p->elementIsforward;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ ptrCheckGuard(ulkPageidptr, cpagesize, page8);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ //----------------------------------------------------------
+ // To avoid undo log the element header we take care to only
+ // undo log the local key part.
+ //----------------------------------------------------------
+ if (operationRecPtr.p->elementIsforward == 1) {
+ jam();
+ TlogStart = tulkLocalPtr;
+ } else {
+ jam();
+ TlogStart = tulkLocalPtr - fragrecptr.p->localkeylen + 1;
+ }//if
+ datapageptr.p = ulkPageidptr.p;
+ cundoinfolength = fragrecptr.p->localkeylen;
+ cundoElemIndex = TlogStart;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(ulkPageidptr, tulkLocalPtr, tlocalkey1);
+ arrGuard(tulkLocalPtr, 2048);
+ ulkPageidptr.p->word32[tulkLocalPtr] = tlocalkey1;
+ operationRecPtr.p->localdata[0] = tlocalkey1;
+ if (fragrecptr.p->localkeylen == 1) {
+ return;
+ } else if (fragrecptr.p->localkeylen == 2) {
+ jam();
+ tulkLocalPtr = tulkLocalPtr + operationRecPtr.p->elementIsforward;
+ operationRecPtr.p->localdata[1] = tlocalkey2;
+ dbgWord32(ulkPageidptr, tulkLocalPtr, tlocalkey2);
+ arrGuard(tulkLocalPtr, 2048);
+ ulkPageidptr.p->word32[tulkLocalPtr] = tlocalkey2;
+ return;
+ } else {
+ jam();
+ }//if
+ }//if
+ ndbrequire(false);
+}//Dbacc::execACCMINUPDATE()
+
+/* ******************--------------------------------------------------------------- */
+/* ACC_COMMITREQ COMMIT TRANSACTION */
+/* SENDER: LQH, LEVEL B */
+/* INPUT: OPERATION_REC_PTR , */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::execACC_COMMITREQ(Signal* signal)
+{
+ Uint8 Toperation;
+ jamEntry();
+ operationRecPtr.i = signal->theData[0];
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ ndbrequire(operationRecPtr.p->transactionstate == ACTIVE);
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ commitOperation(signal);
+ Toperation = operationRecPtr.p->operation;
+ operationRecPtr.p->transactionstate = IDLE;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ if(Toperation != ZREAD){
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ rootfragrecptr.p->m_commit_count++;
+ if (Toperation != ZINSERT) {
+ if (Toperation != ZDELETE) {
+ return;
+ } else {
+ jam();
+ rootfragrecptr.p->noOfElements--;
+ fragrecptr.p->slack += operationRecPtr.p->insertDeleteLen;
+ if (fragrecptr.p->slack > fragrecptr.p->slackCheck) {
+ /* TIME FOR JOIN BUCKETS PROCESS */
+ if (fragrecptr.p->expandCounter > 0) {
+ if (fragrecptr.p->expandFlag < 2) {
+ jam();
+ signal->theData[0] = fragrecptr.i;
+ signal->theData[1] = fragrecptr.p->p;
+ signal->theData[2] = fragrecptr.p->maxp;
+ signal->theData[3] = fragrecptr.p->expandFlag;
+ fragrecptr.p->expandFlag = 2;
+ sendSignal(cownBlockref, GSN_SHRINKCHECK2, signal, 4, JBB);
+ }//if
+ }//if
+ }//if
+ }//if
+ } else {
+ jam(); /* EXPAND PROCESS HANDLING */
+ rootfragrecptr.p->noOfElements++;
+ fragrecptr.p->slack -= operationRecPtr.p->insertDeleteLen;
+ if (fragrecptr.p->slack >= (1u << 31)) {
+ /* IT MEANS THAT IF SLACK < ZERO */
+ if (fragrecptr.p->expandFlag == 0) {
+ jam();
+ fragrecptr.p->expandFlag = 2;
+ signal->theData[0] = fragrecptr.i;
+ signal->theData[1] = fragrecptr.p->p;
+ signal->theData[2] = fragrecptr.p->maxp;
+ sendSignal(cownBlockref, GSN_EXPANDCHECK2, signal, 3, JBB);
+ }//if
+ }//if
+ }//if
+ }
+ return;
+}//Dbacc::execACC_COMMITREQ()
+
+/* ******************--------------------------------------------------------------- */
+/* ACC ABORT REQ ABORT ALL OPERATION OF THE TRANSACTION */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+/* ******************--------------------------------------------------------------- */
+/* ACC ABORT REQ ABORT TRANSACTION */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execACC_ABORTREQ(Signal* signal)
+{
+ jamEntry();
+ accAbortReqLab(signal, true);
+}//Dbacc::execACC_ABORTREQ()
+
+void Dbacc::accAbortReqLab(Signal* signal, bool sendConf)
+{
+ operationRecPtr.i = signal->theData[0];
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ tresult = 0; /* ZFALSE */
+ if ((operationRecPtr.p->transactionstate == ACTIVE) ||
+ (operationRecPtr.p->transactionstate == WAIT_COMMIT_ABORT)) {
+ jam();
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ operationRecPtr.p->transactionstate = ABORT;
+ abortOperation(signal);
+ } else {
+ ndbrequire(operationRecPtr.p->transactionstate == IDLE);
+ jam();
+ }//if
+ operationRecPtr.p->transactionstate = IDLE;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ if (! sendConf)
+ return;
+ signal->theData[0] = operationRecPtr.p->userptr;
+ sendSignal(operationRecPtr.p->userblockref, GSN_ACC_ABORTCONF, signal, 1, JBB);
+ return;
+}//Dbacc::accAbortReqLab()
+
+/*
+ * Lock or unlock tuple.
+ */
+void Dbacc::execACC_LOCKREQ(Signal* signal)
+{
+ jamEntry();
+ AccLockReq* sig = (AccLockReq*)signal->getDataPtrSend();
+ AccLockReq reqCopy = *sig;
+ AccLockReq* const req = &reqCopy;
+ Uint32 lockOp = (req->requestInfo & 0xFF);
+ if (lockOp == AccLockReq::LockShared ||
+ lockOp == AccLockReq::LockExclusive) {
+ jam();
+ // find table
+ tabptr.i = req->tableId;
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+ // find fragment (TUX will know it)
+ if (req->fragPtrI == RNIL) {
+ for (Uint32 i = 0; i < MAX_FRAG_PER_NODE; i++) {
+ jam();
+ if (tabptr.p->fragptrholder[i] != RNIL) {
+ rootfragrecptr.i = tabptr.p->fragptrholder[i];
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->fragmentid[0] == req->fragId) {
+ jam();
+ req->fragPtrI = rootfragrecptr.p->fragmentptr[0];
+ break;
+ }
+ if (rootfragrecptr.p->fragmentid[1] == req->fragId) {
+ jam();
+ req->fragPtrI = rootfragrecptr.p->fragmentptr[1];
+ break;
+ }
+ }
+ }
+ }
+ fragrecptr.i = req->fragPtrI;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ ndbrequire(req->fragId == fragrecptr.p->myfid);
+ // caller must be explicit here
+ ndbrequire(req->accOpPtr == RNIL);
+ // seize operation to hold the lock
+ if (cfreeopRec != RNIL) {
+ jam();
+ seizeOpRec(signal);
+ // init as in ACCSEIZEREQ
+ operationRecPtr.p->userptr = req->userPtr;
+ operationRecPtr.p->userblockref = req->userRef;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+ operationRecPtr.p->transactionstate = IDLE;
+ // do read with lock via ACCKEYREQ
+ Uint32 lockMode = (lockOp == AccLockReq::LockShared) ? 0 : 1;
+ Uint32 opCode = ZSCAN_OP;
+ signal->theData[0] = operationRecPtr.i;
+ signal->theData[1] = fragrecptr.i;
+ signal->theData[2] = opCode | (lockMode << 4) | (1u << 31);
+ signal->theData[3] = req->hashValue;
+ signal->theData[4] = 1; // fake primKeyLen
+ signal->theData[5] = req->transId1;
+ signal->theData[6] = req->transId2;
+ // enter local key in place of PK
+ signal->theData[7] = req->tupAddr;
+ EXECUTE_DIRECT(DBACC, GSN_ACCKEYREQ, signal, 8);
+ // translate the result
+ if (signal->theData[0] < RNIL) {
+ jam();
+ req->returnCode = AccLockReq::Success;
+ req->accOpPtr = operationRecPtr.i;
+ } else if (signal->theData[0] == RNIL) {
+ jam();
+ req->returnCode = AccLockReq::IsBlocked;
+ req->accOpPtr = operationRecPtr.i;
+ } else {
+ ndbrequire(signal->theData[0] == (UintR)-1);
+ releaseOpRec(signal);
+ req->returnCode = AccLockReq::Refused;
+ req->accOpPtr = RNIL;
+ }
+ } else {
+ jam();
+ req->returnCode = AccLockReq::NoFreeOp;
+ }
+ *sig = *req;
+ return;
+ }
+ if (lockOp == AccLockReq::Unlock) {
+ jam();
+ // do unlock via ACC_COMMITREQ (immediate)
+ signal->theData[0] = req->accOpPtr;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_COMMITREQ, signal, 1);
+ releaseOpRec(signal);
+ req->returnCode = AccLockReq::Success;
+ *sig = *req;
+ return;
+ }
+ if (lockOp == AccLockReq::Abort) {
+ jam();
+ // do abort via ACC_ABORTREQ (immediate)
+ signal->theData[0] = req->accOpPtr;
+ accAbortReqLab(signal, false);
+ releaseOpRec(signal);
+ req->returnCode = AccLockReq::Success;
+ *sig = *req;
+ return;
+ }
+ if (lockOp == AccLockReq::AbortWithConf) {
+ jam();
+ // do abort via ACC_ABORTREQ (with conf signal)
+ signal->theData[0] = req->accOpPtr;
+ accAbortReqLab(signal, true);
+ releaseOpRec(signal);
+ req->returnCode = AccLockReq::Success;
+ *sig = *req;
+ return;
+ }
+ ndbrequire(false);
+}
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF EXECUTE OPERATION MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* MODULE: INSERT */
+/* THE FOLLOWING SUBROUTINES ARE ONLY USED BY INSERT_ELEMENT. THIS */
+/* ROUTINE IS THE SOLE INTERFACE TO INSERT ELEMENTS INTO THE INDEX. */
+/* CURRENT USERS ARE INSERT REQUESTS, EXPAND CONTAINER AND SHRINK */
+/* CONTAINER. */
+/* */
+/* THE FOLLOWING SUBROUTINES ARE INCLUDED IN THIS MODULE: */
+/* INSERT_ELEMENT */
+/* INSERT_CONTAINER */
+/* ADDNEWCONTAINER */
+/* GETFREELIST */
+/* INCREASELISTCONT */
+/* SEIZE_LEFTLIST */
+/* SEIZE_RIGHTLIST */
+/* */
+/* THESE ROUTINES ARE ONLY USED BY THIS MODULE AND BY NO ONE ELSE. */
+/* ALSO THE ROUTINES MAKE NO USE OF ROUTINES IN OTHER MODULES. */
+/* TAKE_REC_OUT_OF_FREE_OVERPAGE AND RELEASE_OVERFLOW_REC ARE */
+/* EXCEPTIONS TO THIS RULE. */
+/* */
+/* THE ONLY SHORT-LIVED VARIABLES USED IN OTHER PARTS OF THE BLOCK ARE */
+/* THOSE DEFINED AS INPUT AND OUTPUT IN INSERT_ELEMENT */
+/* SHORT-LIVED VARIABLES INCLUDE TEMPORARY VARIABLES, COMMON VARIABLES */
+/* AND POINTER VARIABLES. */
+/* THE ONLY EXCEPTION TO THIS RULE IS FRAGRECPTR WHICH POINTS TO THE */
+/* FRAGMENT RECORD. THIS IS MORE LESS STATIC ALWAYS DURING A SIGNAL */
+/* EXECUTION. */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* INSERT_ELEMENT */
+/* INPUT: */
+/* IDR_PAGEPTR (POINTER TO THE ACTIVE PAGE REC) */
+/* TIDR_PAGEINDEX (INDEX OF THE CONTAINER) */
+/* TIDR_FORWARD (DIRECTION FORWARD OR BACKWARD) */
+/* TIDR_ELEMHEAD (HEADER OF ELEMENT TO BE INSERTED */
+/* CIDR_KEYS(ARRAY OF TUPLE KEYS) */
+/* CLOCALKEY(ARRAY OF LOCAL KEYS). */
+/* FRAGRECPTR */
+/* IDR_OPERATION_REC_PTR */
+/* TIDR_KEY_LEN */
+/* */
+/* OUTPUT: */
+/* TIDR_PAGEINDEX (PAGE INDEX OF INSERTED ELEMENT) */
+/* IDR_PAGEPTR (PAGE POINTER OF INSERTED ELEMENT) */
+/* TIDR_FORWARD (CONTAINER DIRECTION OF INSERTED ELEMENT) */
+/* NONE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::insertElement(Signal* signal)
+{
+ DirRangePtr inrOverflowrangeptr;
+ DirectoryarrayPtr inrOverflowDirptr;
+ OverflowRecordPtr inrOverflowRecPtr;
+ Page8Ptr inrNewPageptr;
+ Uint32 tinrNextSamePage;
+ Uint32 tinrTmp;
+
+ do {
+ insertContainer(signal);
+ if (tidrResult != ZFALSE) {
+ jam();
+ return;
+ /* INSERTION IS DONE, OR */
+ /* AN ERROR IS DETECTED */
+ }//if
+ if (((tidrContainerhead >> 7) & 0x3) != 0) {
+ tinrNextSamePage = (tidrContainerhead >> 9) & 0x1; /* CHECK BIT FOR CHECKING WHERE */
+ /* THE NEXT CONTAINER IS IN THE SAME PAGE */
+ tidrPageindex = tidrContainerhead & 0x7f; /* NEXT CONTAINER PAGE INDEX 7 BITS */
+ if (((tidrContainerhead >> 7) & 3) == ZLEFT) {
+ jam();
+ tidrForward = ZTRUE;
+ } else if (((tidrContainerhead >> 7) & 3) == ZRIGHT) {
+ jam();
+ tidrForward = cminusOne;
+ } else {
+ ndbrequire(false);
+ return;
+ }//if
+ if (tinrNextSamePage == ZFALSE) {
+ jam(); /* NEXT CONTAINER IS IN AN OVERFLOW PAGE */
+ tinrTmp = idrPageptr.p->word32[tidrContainerptr + 1];
+ inrOverflowrangeptr.i = fragrecptr.p->overflowdir;
+ ptrCheckGuard(inrOverflowrangeptr, cdirrangesize, dirRange);
+ arrGuard((tinrTmp >> 8), 256);
+ inrOverflowDirptr.i = inrOverflowrangeptr.p->dirArray[tinrTmp >> 8];
+ ptrCheckGuard(inrOverflowDirptr, cdirarraysize, directoryarray);
+ idrPageptr.i = inrOverflowDirptr.p->pagep[tinrTmp & 0xff];
+ ptrCheckGuard(idrPageptr, cpagesize, page8);
+ }//if
+ ndbrequire(tidrPageindex < ZEMPTYLIST);
+ } else {
+ break;
+ }//if
+ } while (1);
+ gflPageptr.p = idrPageptr.p;
+ getfreelist(signal);
+ if (tgflPageindex == ZEMPTYLIST) {
+ jam();
+ /* NO FREE BUFFER IS FOUND */
+ if (fragrecptr.p->firstOverflowRec == RNIL) {
+ jam();
+ allocOverflowPage(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ }//if
+ inrOverflowRecPtr.i = fragrecptr.p->firstOverflowRec;
+ ptrCheckGuard(inrOverflowRecPtr, coverflowrecsize, overflowRecord);
+ inrNewPageptr.i = inrOverflowRecPtr.p->overpage;
+ ptrCheckGuard(inrNewPageptr, cpagesize, page8);
+ gflPageptr.p = inrNewPageptr.p;
+ getfreelist(signal);
+ ndbrequire(tgflPageindex != ZEMPTYLIST);
+ tancNext = 0;
+ } else {
+ jam();
+ inrNewPageptr = idrPageptr;
+ tancNext = 1;
+ }//if
+ tslUpdateHeader = ZTRUE;
+ tslPageindex = tgflPageindex;
+ slPageptr.p = inrNewPageptr.p;
+ if (tgflBufType == ZLEFT) {
+ seizeLeftlist(signal);
+ tidrForward = ZTRUE;
+ } else {
+ seizeRightlist(signal);
+ tidrForward = cminusOne;
+ }//if
+ tancPageindex = tgflPageindex;
+ tancPageid = inrNewPageptr.p->word32[ZPOS_PAGE_ID];
+ tancBufType = tgflBufType;
+ tancContainerptr = tidrContainerptr;
+ ancPageptr.p = idrPageptr.p;
+ addnewcontainer(signal);
+
+ idrPageptr = inrNewPageptr;
+ tidrPageindex = tgflPageindex;
+ insertContainer(signal);
+ ndbrequire(tidrResult == ZTRUE);
+}//Dbacc::insertElement()
+
+/* --------------------------------------------------------------------------------- */
+/* INSERT_CONTAINER */
+/* INPUT: */
+/* IDR_PAGEPTR (POINTER TO THE ACTIVE PAGE REC) */
+/* TIDR_PAGEINDEX (INDEX OF THE CONTAINER) */
+/* TIDR_FORWARD (DIRECTION FORWARD OR BACKWARD) */
+/* TIDR_ELEMHEAD (HEADER OF ELEMENT TO BE INSERTED */
+/* CKEYS(ARRAY OF TUPLE KEYS) */
+/* CLOCALKEY(ARRAY 0F LOCAL KEYS). */
+/* TIDR_KEY_LEN */
+/* FRAGRECPTR */
+/* IDR_OPERATION_REC_PTR */
+/* OUTPUT: */
+/* TIDR_RESULT (ZTRUE FOR SUCCESS AND ZFALSE OTHERWISE) */
+/* TIDR_CONTAINERHEAD (HEADER OF CONTAINER) */
+/* TIDR_CONTAINERPTR (POINTER TO CONTAINER HEADER) */
+/* */
+/* DESCRIPTION: */
+/* THE FREE AREA OF THE CONTAINER WILL BE CALCULATED. IF IT IS */
+/* LARGER THAN OR EQUAL THE ELEMENT LENGTH. THE ELEMENT WILL BE */
+/* INSERT IN THE CONTAINER AND CONTAINER HEAD WILL BE UPDATED. */
+/* THIS ROUTINE ALWAYS DEALS WITH ONLY ONE CONTAINER AND DO NEVER */
+/* START ANYTHING OUTSIDE OF THIS CONTAINER. */
+/* */
+/* SHORT FORM: IDR */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::insertContainer(Signal* signal)
+{
+ Uint32 tidrContainerlen;
+ Uint32 tidrConfreelen;
+ Uint32 tidrNextSide;
+ Uint32 tidrNextConLen;
+ Uint32 tidrIndex;
+ Uint32 tidrInputIndex;
+ Uint32 tidrContLen;
+ Uint32 guard26;
+
+ tidrResult = ZFALSE;
+ tidrContainerptr = (tidrPageindex << ZSHIFT_PLUS) - (tidrPageindex << ZSHIFT_MINUS);
+ tidrContainerptr = tidrContainerptr + ZHEAD_SIZE;
+ /* --------------------------------------------------------------------------------- */
+ /* CALCULATE THE POINTER TO THE ELEMENT TO BE INSERTED AND THE POINTER TO THE */
+ /* CONTAINER HEADER OF THE OTHER SIDE OF THE BUFFER. */
+ /* --------------------------------------------------------------------------------- */
+ if (tidrForward == ZTRUE) {
+ jam();
+ tidrNextSide = tidrContainerptr + (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ arrGuard(tidrNextSide + 1, 2048);
+ tidrContainerhead = idrPageptr.p->word32[tidrContainerptr];
+ tidrContainerlen = tidrContainerhead >> 26;
+ tidrIndex = tidrContainerptr + tidrContainerlen;
+ } else {
+ jam();
+ tidrNextSide = tidrContainerptr;
+ tidrContainerptr = tidrContainerptr + (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ arrGuard(tidrContainerptr + 1, 2048);
+ tidrContainerhead = idrPageptr.p->word32[tidrContainerptr];
+ tidrContainerlen = tidrContainerhead >> 26;
+ tidrIndex = (tidrContainerptr - tidrContainerlen) + (ZCON_HEAD_SIZE - 1);
+ }//if
+ if (tidrContainerlen > (ZBUF_SIZE - 3)) {
+ return;
+ }//if
+ tidrConfreelen = ZBUF_SIZE - tidrContainerlen;
+ /* --------------------------------------------------------------------------------- */
+ /* WE CALCULATE THE TOTAL LENGTH THE CONTAINER CAN EXPAND TO */
+ /* THIS INCLUDES THE OTHER SIDE OF THE BUFFER IF POSSIBLE TO EXPAND THERE. */
+ /* --------------------------------------------------------------------------------- */
+ if (((tidrContainerhead >> 10) & 1) == 0) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE HAVE NOT EXPANDED TO THE ENTIRE BUFFER YET. WE CAN THUS READ THE OTHER */
+ /* SIDE'S CONTAINER HEADER TO READ HIS LENGTH. */
+ /* --------------------------------------------------------------------------------- */
+ tidrNextConLen = idrPageptr.p->word32[tidrNextSide] >> 26;
+ tidrConfreelen = tidrConfreelen - tidrNextConLen;
+ if (tidrConfreelen > ZBUF_SIZE) {
+ ndbrequire(false);
+ /* --------------------------------------------------------------------------------- */
+ /* THE BUFFERS ARE PLACED ON TOP OF EACH OTHER. THIS SHOULD NEVER OCCUR. */
+ /* --------------------------------------------------------------------------------- */
+ return;
+ }//if
+ } else {
+ jam();
+ tidrNextConLen = 1; /* INDICATE OTHER SIDE IS NOT PART OF FREE LIST */
+ }//if
+ if (tidrConfreelen < fragrecptr.p->elementLength) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE CONTAINER COULD NOT BE EXPANDED TO FIT THE NEW ELEMENT. WE HAVE TO */
+ /* RETURN AND FIND A NEW CONTAINER TO INSERT IT INTO. */
+ /* --------------------------------------------------------------------------------- */
+ return;
+ }//if
+ tidrContainerlen = tidrContainerlen + fragrecptr.p->elementLength;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = idrPageptr.p;
+ cundoElemIndex = tidrContainerptr;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ if (tidrNextConLen == 0) {
+ /* EACH SIDE OF THE BUFFER WHICH BELONG TO A FREE */
+ /* LIST, HAS ZERO AS LENGTH. */
+ if (tidrContainerlen > ZUP_LIMIT) {
+ dbgWord32(idrPageptr, tidrContainerptr, idrPageptr.p->word32[tidrContainerptr] | (1 << 10));
+ idrPageptr.p->word32[tidrContainerptr] = idrPageptr.p->word32[tidrContainerptr] | (1 << 10);
+ tslUpdateHeader = ZFALSE;
+ tslPageindex = tidrPageindex;
+ slPageptr.p = idrPageptr.p;
+ if (tidrForward == ZTRUE) {
+ jam();
+ seizeRightlist(signal); /* REMOVE THE RIGHT SIDE OF THE BUFFER FROM THE LIST */
+ } else {
+ jam();
+ /* OF THE FREE CONTAINERS */
+ seizeLeftlist(signal); /* REMOVE THE LEFT SIDE OF THE BUFFER FROM THE LIST */
+ }//if
+ }//if
+ }//if
+ /* OF THE FREE CONTAINERS */
+ /* --------------------------------------------------------------------------------- */
+ /* WE HAVE NOW FOUND A FREE SPOT IN THE CURRENT CONTAINER. WE INSERT THE */
+ /* ELEMENT HERE. THE ELEMENT CONTAINS A HEADER, A LOCAL KEY AND A TUPLE KEY. */
+ /* BEFORE INSERTING THE ELEMENT WE WILL UPDATE THE OPERATION RECORD WITH THE */
+ /* DATA CONCERNING WHERE WE INSERTED THE ELEMENT. THIS MAKES IT EASY TO FIND */
+ /* THIS INFORMATION WHEN WE RETURN TO UPDATE THE LOCAL KEY OR RETURN TO COMMIT */
+ /* OR ABORT THE INSERT. IF NO OPERATION RECORD EXIST IT MEANS THAT WE ARE */
+ /* PERFORMING THIS AS A PART OF THE EXPAND OR SHRINK PROCESS. */
+ /* --------------------------------------------------------------------------------- */
+ if (idrOperationRecPtr.i != RNIL) {
+ jam();
+ idrOperationRecPtr.p->elementIsforward = tidrForward;
+ idrOperationRecPtr.p->elementPage = idrPageptr.i;
+ idrOperationRecPtr.p->elementContainer = tidrContainerptr;
+ idrOperationRecPtr.p->elementPointer = tidrIndex;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* WE CHOOSE TO UNDO LOG INSERTS BY WRITING THE BEFORE VALUE TO THE UNDO LOG. */
+ /* WE COULD ALSO HAVE DONE THIS BY WRITING THIS BEFORE VALUE WHEN DELETING */
+ /* ELEMENTS. WE CHOOSE TO PUT IT HERE SINCE WE THEREBY ENSURE THAT WE ALWAYS */
+ /* UNDO LOG ALL WRITES TO PAGE MEMORY. IT SHOULD BE EASIER TO MAINTAIN SUCH A */
+ /* STRUCTURE. IT IS RATHER DIFFICULT TO MAINTAIN A LOGICAL STRUCTURE WHERE */
+ /* DELETES ARE INSERTS AND INSERTS ARE PURELY DELETES. */
+ /* --------------------------------------------------------------------------------- */
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (tidrForward == ZTRUE) {
+ cundoElemIndex = tidrIndex;
+ } else {
+ cundoElemIndex = (tidrIndex + 1) - fragrecptr.p->elementLength;
+ }//if
+ cundoinfolength = fragrecptr.p->elementLength;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(idrPageptr, tidrIndex, tidrElemhead);
+ idrPageptr.p->word32[tidrIndex] = tidrElemhead; /* INSERTS THE HEAD OF THE ELEMENT */
+ tidrIndex += tidrForward;
+ guard26 = fragrecptr.p->localkeylen - 1;
+ arrGuard(guard26, 2);
+ for (tidrInputIndex = 0; tidrInputIndex <= guard26; tidrInputIndex++) {
+ dbgWord32(idrPageptr, tidrIndex, clocalkey[tidrInputIndex]);
+ arrGuard(tidrIndex, 2048);
+ idrPageptr.p->word32[tidrIndex] = clocalkey[tidrInputIndex]; /* INSERTS LOCALKEY */
+ tidrIndex += tidrForward;
+ }//for
+ tidrContLen = idrPageptr.p->word32[tidrContainerptr] << 6;
+ tidrContLen = tidrContLen >> 6;
+ dbgWord32(idrPageptr, tidrContainerptr, (tidrContainerlen << 26) | tidrContLen);
+ idrPageptr.p->word32[tidrContainerptr] = (tidrContainerlen << 26) | tidrContLen;
+ tidrResult = ZTRUE;
+}//Dbacc::insertContainer()
+
+/* --------------------------------------------------------------------------------- */
+/* ADDNEWCONTAINER */
+/* INPUT: */
+/* TANC_CONTAINERPTR */
+/* ANC_PAGEPTR */
+/* TANC_NEXT */
+/* TANC_PAGEINDEX */
+/* TANC_BUF_TYPE */
+/* TANC_PAGEID */
+/* OUTPUT: */
+/* NONE */
+/* */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::addnewcontainer(Signal* signal)
+{
+ Uint32 tancTmp1;
+
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = tancContainerptr;
+ datapageptr.p = ancPageptr.p;
+ cundoinfolength = 2;
+ undoWritingProcess(signal); /* WHEN UNDO PROCESS HAS STARTED, */
+ }//if
+ /* THE OLD DATA IS STORED ON AN UNDO PAGE */
+ /* --------------------------------------------------------------------------------- */
+ /* KEEP LENGTH INFORMATION IN BIT 26-31. */
+ /* SET BIT 9 INDICATING IF NEXT BUFFER IN THE SAME PAGE USING TANC_NEXT. */
+ /* SET TYPE OF NEXT CONTAINER IN BIT 7-8. */
+ /* SET PAGE INDEX OF NEXT CONTAINER IN BIT 0-6. */
+ /* KEEP INDICATOR OF OWNING OTHER SIDE OF BUFFER IN BIT 10. */
+ /* --------------------------------------------------------------------------------- */
+ tancTmp1 = ancPageptr.p->word32[tancContainerptr] >> 10;
+ tancTmp1 = tancTmp1 << 1;
+ tancTmp1 = tancTmp1 | tancNext;
+ tancTmp1 = tancTmp1 << 2;
+ tancTmp1 = tancTmp1 | tancBufType; /* TYPE OF THE NEXT CONTAINER */
+ tancTmp1 = tancTmp1 << 7;
+ tancTmp1 = tancTmp1 | tancPageindex;
+ dbgWord32(ancPageptr, tancContainerptr, tancTmp1);
+ ancPageptr.p->word32[tancContainerptr] = tancTmp1; /* HEAD OF THE CONTAINER IS UPDATED */
+ dbgWord32(ancPageptr, tancContainerptr + 1, tancPageid);
+ ancPageptr.p->word32[tancContainerptr + 1] = tancPageid;
+}//Dbacc::addnewcontainer()
+
+/* --------------------------------------------------------------------------------- */
+/* GETFREELIST */
+/* INPUT: */
+/* GFL_PAGEPTR (POINTER TO A PAGE RECORD). */
+/* OUTPUT: */
+/* TGFL_PAGEINDEX(POINTER TO A FREE BUFFER IN THE FREEPAGE), AND */
+/* TGFL_BUF_TYPE( TYPE OF THE FREE BUFFER). */
+/* DESCRIPTION: SEARCHS IN THE FREE LIST OF THE FREE BUFFER IN THE PAGE HEAD */
+/* (WORD32(1)),AND RETURN ADDRESS OF A FREE BUFFER OR NIL. */
+/* THE FREE BUFFER CAN BE A RIGHT CONTAINER OR A LEFT ONE */
+/* THE KIND OF THE CONTAINER IS NOTED BY TGFL_BUF_TYPE. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::getfreelist(Signal* signal)
+{
+ Uint32 tgflTmp;
+
+ tgflTmp = gflPageptr.p->word32[ZPOS_EMPTY_LIST];
+ tgflPageindex = (tgflTmp >> 7) & 0x7f; /* LEFT FREE LIST */
+ tgflBufType = ZLEFT;
+ if (tgflPageindex == ZEMPTYLIST) {
+ jam();
+ tgflPageindex = tgflTmp & 0x7f; /* RIGHT FREE LIST */
+ tgflBufType = ZRIGHT;
+ }//if
+ ndbrequire(tgflPageindex <= ZEMPTYLIST);
+}//Dbacc::getfreelist()
+
+/* --------------------------------------------------------------------------------- */
+/* INCREASELISTCONT */
+/* INPUT: */
+/* ILC_PAGEPTR PAGE POINTER TO INCREASE NUMBER OF CONTAINERS IN */
+/* A CONTAINER OF AN OVERFLOW PAGE (FREEPAGEPTR) IS ALLOCATED, NR OF */
+/* ALLOCATED CONTAINER HAVE TO BE INCRESE BY ONE . */
+/* IF THE NUMBER OF ALLOCATED CONTAINERS IS ABOVE THE FREE LIMIT WE WILL */
+/* REMOVE THE PAGE FROM THE FREE LIST. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::increaselistcont(Signal* signal)
+{
+ OverflowRecordPtr ilcOverflowRecPtr;
+
+ dbgWord32(ilcPageptr, ZPOS_ALLOC_CONTAINERS, ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] + 1);
+ ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] + 1;
+ if (ilcPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] > ZFREE_LIMIT) {
+ if (ilcPageptr.p->word32[ZPOS_OVERFLOWREC] != RNIL) {
+ jam();
+ ilcOverflowRecPtr.i = ilcPageptr.p->word32[ZPOS_OVERFLOWREC];
+ dbgWord32(ilcPageptr, ZPOS_OVERFLOWREC, RNIL);
+ ilcPageptr.p->word32[ZPOS_OVERFLOWREC] = RNIL;
+ ptrCheckGuard(ilcOverflowRecPtr, coverflowrecsize, overflowRecord);
+ tfoOverflowRecPtr = ilcOverflowRecPtr;
+ takeRecOutOfFreeOverpage(signal);
+ rorOverflowRecPtr = ilcOverflowRecPtr;
+ releaseOverflowRec(signal);
+ }//if
+ }//if
+}//Dbacc::increaselistcont()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_LEFTLIST */
+/* INPUT: */
+/* TSL_PAGEINDEX PAGE INDEX OF CONTAINER TO SEIZE */
+/* SL_PAGEPTR PAGE POINTER OF CONTAINER TO SEIZE */
+/* TSL_UPDATE_HEADER SHOULD WE UPDATE THE CONTAINER HEADER */
+/* */
+/* OUTPUT: */
+/* NONE */
+/* DESCRIPTION: THE BUFFER NOTED BY TSL_PAGEINDEX WILL BE REMOVED FROM THE */
+/* LIST OF LEFT FREE CONTAINER, IN THE HEADER OF THE PAGE */
+/* (FREEPAGEPTR). PREVIOUS AND NEXT BUFFER OF REMOVED BUFFER */
+/* WILL BE UPDATED. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeLeftlist(Signal* signal)
+{
+ Uint32 tsllTmp1;
+ Uint32 tsllNewHead;
+ Uint32 tsllHeadIndex;
+ Uint32 tsllTmp;
+
+ tsllHeadIndex = ((tslPageindex << ZSHIFT_PLUS) - (tslPageindex << ZSHIFT_MINUS)) + ZHEAD_SIZE;
+ arrGuard(tsllHeadIndex + 1, 2048);
+ tslNextfree = slPageptr.p->word32[tsllHeadIndex];
+ tslPrevfree = slPageptr.p->word32[tsllHeadIndex + 1];
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = slPageptr.p;
+ cundoElemIndex = tsllHeadIndex;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = ZPOS_EMPTY_LIST;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ if (tslPrevfree == ZEMPTYLIST) {
+ jam();
+ /* UPDATE FREE LIST OF LEFT CONTAINER IN PAGE HEAD */
+ tsllTmp1 = slPageptr.p->word32[ZPOS_EMPTY_LIST];
+ tsllTmp = tsllTmp1 & 0x7f;
+ tsllTmp1 = (tsllTmp1 >> 14) << 14;
+ tsllTmp1 = (tsllTmp1 | (tslNextfree << 7)) | tsllTmp;
+ dbgWord32(slPageptr, ZPOS_EMPTY_LIST, tsllTmp1);
+ slPageptr.p->word32[ZPOS_EMPTY_LIST] = tsllTmp1;
+ } else {
+ ndbrequire(tslPrevfree < ZEMPTYLIST);
+ jam();
+ tsllTmp = ((tslPrevfree << ZSHIFT_PLUS) - (tslPrevfree << ZSHIFT_MINUS)) + ZHEAD_SIZE;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = tsllTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(slPageptr, tsllTmp, tslNextfree);
+ slPageptr.p->word32[tsllTmp] = tslNextfree;
+ }//if
+ if (tslNextfree < ZEMPTYLIST) {
+ jam();
+ tsllTmp = (((tslNextfree << ZSHIFT_PLUS) - (tslNextfree << ZSHIFT_MINUS)) + ZHEAD_SIZE) + 1;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = tsllTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(slPageptr, tsllTmp, tslPrevfree);
+ slPageptr.p->word32[tsllTmp] = tslPrevfree;
+ } else {
+ ndbrequire(tslNextfree == ZEMPTYLIST);
+ jam();
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* IF WE ARE UPDATING THE HEADER WE ARE CREATING A NEW CONTAINER IN THE PAGE. */
+ /* TO BE ABLE TO FIND ALL LOCKED ELEMENTS WE KEEP ALL CONTAINERS IN LINKED */
+ /* LISTS IN THE PAGE. */
+ /* */
+ /* ZPOS_EMPTY_LIST CONTAINS A NEXT POINTER IN BIT 16-22 THAT REFERS TO THE */
+ /* FIRST CONTAINER IN A LIST OF USED RIGHT CONTAINERS IN THE PAGE. */
+ /* ZPOS_EMPTY_LIST CONTAINS A NEXT POINTER IN BIT 23-29 THAT REFERS TO THE */
+ /* FIRST CONTAINER IN A LIST OF USED LEFT CONTAINERS IN THE PAGE. */
+ /* EACH CONTAINER IN THE LIST CONTAINS A NEXT POINTER IN BIT 11-17 AND IT */
+ /* CONTAINS A PREVIOUS POINTER IN BIT 18-24. */
+ /* WE ALSO SET BIT 25 TO INDICATE THAT IT IS A CONTAINER HEADER. */
+ /* --------------------------------------------------------------------------------- */
+ if (tslUpdateHeader == ZTRUE) {
+ jam();
+ tslNextfree = (slPageptr.p->word32[ZPOS_EMPTY_LIST] >> 23) & 0x7f;
+ tsllNewHead = ZCON_HEAD_SIZE;
+ tsllNewHead = ((tsllNewHead << 8) + ZEMPTYLIST) + (1 << 7);
+ tsllNewHead = (tsllNewHead << 7) + tslNextfree;
+ tsllNewHead = tsllNewHead << 11;
+ dbgWord32(slPageptr, tsllHeadIndex, tsllNewHead);
+ slPageptr.p->word32[tsllHeadIndex] = tsllNewHead;
+ tsllTmp = slPageptr.p->word32[ZPOS_EMPTY_LIST] & 0xc07fffff;
+ tsllTmp = tsllTmp | (tslPageindex << 23);
+ dbgWord32(slPageptr, ZPOS_EMPTY_LIST, tsllTmp);
+ slPageptr.p->word32[ZPOS_EMPTY_LIST] = tsllTmp;
+ if (tslNextfree < ZEMPTYLIST) {
+ jam();
+ tsllTmp = ((tslNextfree << ZSHIFT_PLUS) - (tslNextfree << ZSHIFT_MINUS)) + ZHEAD_SIZE;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = tsllTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ tsllTmp1 = slPageptr.p->word32[tsllTmp] & 0xfe03ffff;
+ tsllTmp1 = tsllTmp1 | (tslPageindex << 18);
+ dbgWord32(slPageptr, tsllTmp, tsllTmp1);
+ slPageptr.p->word32[tsllTmp] = tsllTmp1;
+ } else {
+ ndbrequire(tslNextfree == ZEMPTYLIST);
+ jam();
+ }//if
+ }//if
+ ilcPageptr.p = slPageptr.p;
+ increaselistcont(signal);
+}//Dbacc::seizeLeftlist()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_RIGHTLIST */
+/* DESCRIPTION: THE BUFFER NOTED BY TSL_PAGEINDEX WILL BE REMOVED FROM THE */
+/* LIST OF RIGHT FREE CONTAINER, IN THE HEADER OF THE PAGE */
+/* (SL_PAGEPTR). PREVIOUS AND NEXT BUFFER OF REMOVED BUFFER */
+/* WILL BE UPDATED. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeRightlist(Signal* signal)
+{
+ Uint32 tsrlTmp1;
+ Uint32 tsrlNewHead;
+ Uint32 tsrlHeadIndex;
+ Uint32 tsrlTmp;
+
+ tsrlHeadIndex = ((tslPageindex << ZSHIFT_PLUS) - (tslPageindex << ZSHIFT_MINUS)) + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ arrGuard(tsrlHeadIndex + 1, 2048);
+ tslNextfree = slPageptr.p->word32[tsrlHeadIndex];
+ tslPrevfree = slPageptr.p->word32[tsrlHeadIndex + 1];
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = slPageptr.p;
+ cundoElemIndex = tsrlHeadIndex;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = ZPOS_EMPTY_LIST;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ if (tslPrevfree == ZEMPTYLIST) {
+ jam();
+ tsrlTmp = slPageptr.p->word32[ZPOS_EMPTY_LIST];
+ dbgWord32(slPageptr, ZPOS_EMPTY_LIST, ((tsrlTmp >> 7) << 7) | tslNextfree);
+ slPageptr.p->word32[ZPOS_EMPTY_LIST] = ((tsrlTmp >> 7) << 7) | tslNextfree;
+ } else {
+ ndbrequire(tslPrevfree < ZEMPTYLIST);
+ jam();
+ tsrlTmp = ((tslPrevfree << ZSHIFT_PLUS) - (tslPrevfree << ZSHIFT_MINUS)) + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = tsrlTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(slPageptr, tsrlTmp, tslNextfree);
+ slPageptr.p->word32[tsrlTmp] = tslNextfree;
+ }//if
+ if (tslNextfree < ZEMPTYLIST) {
+ jam();
+ tsrlTmp = ((tslNextfree << ZSHIFT_PLUS) - (tslNextfree << ZSHIFT_MINUS)) + ((ZHEAD_SIZE + ZBUF_SIZE) - (ZCON_HEAD_SIZE - 1));
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ cundoElemIndex = tsrlTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(slPageptr, tsrlTmp, tslPrevfree);
+ slPageptr.p->word32[tsrlTmp] = tslPrevfree;
+ } else {
+ ndbrequire(tslNextfree == ZEMPTYLIST);
+ jam();
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* IF WE ARE UPDATING THE HEADER WE ARE CREATING A NEW CONTAINER IN THE PAGE. */
+ /* TO BE ABLE TO FIND ALL LOCKED ELEMENTS WE KEEP ALL CONTAINERS IN LINKED */
+ /* LISTS IN THE PAGE. */
+ /* */
+ /* ZPOS_EMPTY_LIST CONTAINS A NEXT POINTER IN BIT 16-22 THAT REFERS TO THE */
+ /* FIRST CONTAINER IN A LIST OF USED RIGHT CONTAINERS IN THE PAGE. */
+ /* ZPOS_EMPTY_LIST CONTAINS A NEXT POINTER IN BIT 23-29 THAT REFERS TO THE */
+ /* FIRST CONTAINER IN A LIST OF USED LEFT CONTAINERS IN THE PAGE. */
+ /* EACH CONTAINER IN THE LIST CONTAINS A NEXT POINTER IN BIT 11-17 AND IT */
+ /* CONTAINS A PREVIOUS POINTER IN BIT 18-24. */
+ /* --------------------------------------------------------------------------------- */
+ if (tslUpdateHeader == ZTRUE) {
+ jam();
+ tslNextfree = (slPageptr.p->word32[ZPOS_EMPTY_LIST] >> 16) & 0x7f;
+ tsrlNewHead = ZCON_HEAD_SIZE;
+ tsrlNewHead = ((tsrlNewHead << 8) + ZEMPTYLIST) + (1 << 7);
+ tsrlNewHead = (tsrlNewHead << 7) + tslNextfree;
+ tsrlNewHead = tsrlNewHead << 11;
+ dbgWord32(slPageptr, tsrlHeadIndex, tsrlNewHead);
+ slPageptr.p->word32[tsrlHeadIndex] = tsrlNewHead;
+ tsrlTmp = slPageptr.p->word32[ZPOS_EMPTY_LIST] & 0xff80ffff;
+ dbgWord32(slPageptr, ZPOS_EMPTY_LIST, tsrlTmp | (tslPageindex << 16));
+ slPageptr.p->word32[ZPOS_EMPTY_LIST] = tsrlTmp | (tslPageindex << 16);
+ if (tslNextfree < ZEMPTYLIST) {
+ jam();
+ tsrlTmp = ((tslNextfree << ZSHIFT_PLUS) - (tslNextfree << ZSHIFT_MINUS)) + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = tsrlTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ tsrlTmp1 = slPageptr.p->word32[tsrlTmp] & 0xfe03ffff;
+ dbgWord32(slPageptr, tsrlTmp, tsrlTmp1 | (tslPageindex << 18));
+ slPageptr.p->word32[tsrlTmp] = tsrlTmp1 | (tslPageindex << 18);
+ } else {
+ ndbrequire(tslNextfree == ZEMPTYLIST);
+ jam();
+ }//if
+ }//if
+ ilcPageptr.p = slPageptr.p;
+ increaselistcont(signal);
+}//Dbacc::seizeRightlist()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF INSERT_ELEMENT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* MODULE: GET_ELEMENT */
+/* THE FOLLOWING SUBROUTINES ARE ONLY USED BY GET_ELEMENT AND */
+/* GETDIRINDEX. THIS ROUTINE IS THE SOLE INTERFACE TO GET ELEMENTS */
+/* FROM THE INDEX. CURRENT USERS ARE ALL REQUESTS AND EXECUTE UNDO LOG */
+/* */
+/* THE FOLLOWING SUBROUTINES ARE INCLUDED IN THIS MODULE: */
+/* GET_ELEMENT */
+/* GET_DIRINDEX */
+/* SEARCH_LONG_KEY */
+/* */
+/* THESE ROUTINES ARE ONLY USED BY THIS MODULE AND BY NO ONE ELSE. */
+/* ALSO THE ROUTINES MAKE NO USE OF ROUTINES IN OTHER MODULES. */
+/* THE ONLY SHORT-LIVED VARIABLES USED IN OTHER PARTS OF THE BLOCK ARE */
+/* THOSE DEFINED AS INPUT AND OUTPUT IN GET_ELEMENT AND GETDIRINDEX */
+/* SHORT-LIVED VARIABLES INCLUDE TEMPORARY VARIABLES, COMMON VARIABLES */
+/* AND POINTER VARIABLES. */
+/* THE ONLY EXCEPTION TO THIS RULE IS FRAGRECPTR WHICH POINTS TO THE */
+/* FRAGMENT RECORD. THIS IS MORE LESS STATIC ALWAYS DURING A SIGNAL */
+/* EXECUTION. */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* GETDIRINDEX */
+/* SUPPORT ROUTINE FOR INSERT ELEMENT, GET ELEMENT AND COMMITDELETE */
+/* INPUT:FRAGRECPTR ( POINTER TO THE ACTIVE FRAGMENT REC) */
+/* OPERATION_REC_PTR (POINTER TO THE OPERATION REC). */
+/* */
+/* OUTPUT:GDI_PAGEPTR ( POINTER TO THE PAGE OF THE ELEMENT) */
+/* TGDI_PAGEINDEX ( INDEX OF THE ELEMENT IN THE PAGE). */
+/* */
+/* DESCRIPTION: CHECK THE HASH VALUE OF THE OPERATION REC AND CALCULATE THE */
+/* THE ADDRESS OF THE ELEMENT IN THE HASH TABLE,(GDI_PAGEPTR, */
+/* TGDI_PAGEINDEX) ACCORDING TO LH3. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::getdirindex(Signal* signal)
+{
+ DirRangePtr gdiDirRangePtr;
+ DirectoryarrayPtr gdiDirptr;
+ Uint32 tgdiTmp;
+ Uint32 tgdiAddress;
+
+ tgdiTmp = fragrecptr.p->k + fragrecptr.p->lhfragbits; /* OBS K = 6 */
+ tgdiPageindex = operationRecPtr.p->hashValue & ((1 << fragrecptr.p->k) - 1);
+ tgdiTmp = operationRecPtr.p->hashValue >> tgdiTmp;
+ tgdiTmp = (tgdiTmp << fragrecptr.p->k) | tgdiPageindex;
+ tgdiAddress = tgdiTmp & fragrecptr.p->maxp;
+ gdiDirRangePtr.i = fragrecptr.p->directory;
+ ptrCheckGuard(gdiDirRangePtr, cdirrangesize, dirRange);
+ if (tgdiAddress < fragrecptr.p->p) {
+ jam();
+ tgdiAddress = tgdiTmp & ((fragrecptr.p->maxp << 1) | 1);
+ }//if
+ tgdiTmp = tgdiAddress >> fragrecptr.p->k;
+ arrGuard((tgdiTmp >> 8), 256);
+ gdiDirptr.i = gdiDirRangePtr.p->dirArray[tgdiTmp >> 8];
+ ptrCheckGuard(gdiDirptr, cdirarraysize, directoryarray);
+ gdiPageptr.i = gdiDirptr.p->pagep[tgdiTmp & 0xff]; /* DIRECTORY INDEX OF SEND BUCKET PAGE */
+ ptrCheckGuard(gdiPageptr, cpagesize, page8);
+}//Dbacc::getdirindex()
+
+Uint32
+Dbacc::readTablePk(Uint32 localkey1)
+{
+ Uint32 tableId = fragrecptr.p->myTableId;
+ Uint32 fragId = fragrecptr.p->myfid;
+ Uint32 fragPageId = localkey1 >> MAX_TUPLES_BITS;
+ Uint32 pageIndex = localkey1 & ((1 << MAX_TUPLES_BITS ) - 1);
+#ifdef VM_TRACE
+ memset(ckeys, 0x1f, (fragrecptr.p->keyLength * MAX_XFRM_MULTIPLY) << 2);
+#endif
+ int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, ckeys, true);
+ ndbrequire(ret > 0);
+ return ret;
+}
+
+/* --------------------------------------------------------------------------------- */
+/* GET_ELEMENT */
+/* INPUT: */
+/* OPERATION_REC_PTR */
+/* FRAGRECPTR */
+/* OUTPUT: */
+/* TGE_RESULT RESULT SUCCESS = ZTRUE OTHERWISE ZFALSE */
+/* TGE_LOCKED LOCK INFORMATION IF SUCCESSFUL RESULT */
+/* GE_PAGEPTR PAGE POINTER OF FOUND ELEMENT */
+/* TGE_CONTAINERPTR CONTAINER INDEX OF FOUND ELEMENT */
+/* TGE_ELEMENTPTR ELEMENT INDEX OF FOUND ELEMENT */
+/* TGE_FORWARD DIRECTION OF CONTAINER WHERE ELEMENT FOUND */
+/* */
+/* DESCRIPTION: THE SUBROUTIN GOES THROUGH ALL CONTAINERS OF THE ACTIVE */
+/* BUCKET, AND SERCH FOR ELEMENT.THE PRIMARY KEYS WHICH IS SAVED */
+/* IN THE OPERATION REC ARE THE CHECK ITEMS IN THE SEARCHING. */
+/* --------------------------------------------------------------------------------- */
+
+#if __ia64 == 1
+#if __INTEL_COMPILER == 810
+int ndb_acc_ia64_icc810_dummy_var = 0;
+void ndb_acc_ia64_icc810_dummy_func()
+{
+ ndb_acc_ia64_icc810_dummy_var++;
+}
+#endif
+#endif
+
+void Dbacc::getElement(Signal* signal)
+{
+ DirRangePtr geOverflowrangeptr;
+ DirectoryarrayPtr geOverflowDirptr;
+ OperationrecPtr geTmpOperationRecPtr;
+ Uint32 tgeElementHeader;
+ Uint32 tgeElemStep;
+ Uint32 tgeContainerhead;
+ Uint32 tgePageindex;
+ Uint32 tgeActivePageDir;
+ Uint32 tgeNextptrtype;
+ register Uint32 tgeKeyptr;
+ register Uint32 tgeRemLen;
+ register Uint32 TelemLen = fragrecptr.p->elementLength;
+ register Uint32* Tkeydata = (Uint32*)&signal->theData[7];
+
+ getdirindex(signal);
+ tgePageindex = tgdiPageindex;
+ gePageptr = gdiPageptr;
+ tgeResult = ZFALSE;
+ /*
+ * The value seached is
+ * - table key for ACCKEYREQ, stored in TUP
+ * - local key (1 word) for ACC_LOCKREQ and UNDO, stored in ACC
+ */
+ const bool searchLocalKey =
+ operationRecPtr.p->isAccLockReq || operationRecPtr.p->isUndoLogReq;
+
+ ndbrequire(TelemLen == ZELEM_HEAD_SIZE + fragrecptr.p->localkeylen);
+ tgeNextptrtype = ZLEFT;
+ tgeLocked = 0;
+
+ const Uint32 tmp = fragrecptr.p->k + fragrecptr.p->lhfragbits;
+ const Uint32 opHashValuePart = (operationRecPtr.p->hashValue >> tmp) &0xFFFF;
+ do {
+ tgeContainerptr = (tgePageindex << ZSHIFT_PLUS) - (tgePageindex << ZSHIFT_MINUS);
+ if (tgeNextptrtype == ZLEFT) {
+ jam();
+ tgeContainerptr = tgeContainerptr + ZHEAD_SIZE;
+ tgeElementptr = tgeContainerptr + ZCON_HEAD_SIZE;
+ tgeKeyptr = (tgeElementptr + ZELEM_HEAD_SIZE) + fragrecptr.p->localkeylen;
+ tgeElemStep = TelemLen;
+ tgeForward = 1;
+ if (tgeContainerptr >= 2048) { ACCKEY_error(4); return;}
+ tgeRemLen = gePageptr.p->word32[tgeContainerptr] >> 26;
+ if ((tgeContainerptr + tgeRemLen - 1) >= 2048) { ACCKEY_error(5); return;}
+ } else if (tgeNextptrtype == ZRIGHT) {
+ jam();
+ tgeContainerptr = tgeContainerptr + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ tgeElementptr = tgeContainerptr - 1;
+ tgeKeyptr = (tgeElementptr - ZELEM_HEAD_SIZE) - fragrecptr.p->localkeylen;
+ tgeElemStep = 0 - TelemLen;
+ tgeForward = (Uint32)-1;
+ if (tgeContainerptr >= 2048) { ACCKEY_error(4); return;}
+ tgeRemLen = gePageptr.p->word32[tgeContainerptr] >> 26;
+ if ((tgeContainerptr - tgeRemLen) >= 2048) { ACCKEY_error(5); return;}
+ } else {
+ ACCKEY_error(6); return;
+ }//if
+ if (tgeRemLen >= ZCON_HEAD_SIZE + TelemLen) {
+ if (tgeRemLen > ZBUF_SIZE) {
+ ACCKEY_error(7); return;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ // There is at least one element in this container. Check if it is the element
+ // searched for.
+ /* --------------------------------------------------------------------------------- */
+ do {
+ tgeElementHeader = gePageptr.p->word32[tgeElementptr];
+ tgeRemLen = tgeRemLen - TelemLen;
+ Uint32 hashValuePart;
+ if (ElementHeader::getLocked(tgeElementHeader)) {
+ jam();
+ geTmpOperationRecPtr.i = ElementHeader::getOpPtrI(tgeElementHeader);
+ ptrCheckGuard(geTmpOperationRecPtr, coprecsize, operationrec);
+ hashValuePart = geTmpOperationRecPtr.p->hashvaluePart;
+ } else {
+ jam();
+ hashValuePart = ElementHeader::getHashValuePart(tgeElementHeader);
+ }
+ if (hashValuePart == opHashValuePart) {
+ jam();
+ Uint32 localkey1 = gePageptr.p->word32[tgeElementptr + tgeForward];
+ Uint32 localkey2 = 0;
+ bool found;
+ if (! searchLocalKey) {
+ Uint32 len = readTablePk(localkey1);
+ found = (len == operationRecPtr.p->xfrmtupkeylen) &&
+ (memcmp(Tkeydata, ckeys, len << 2) == 0);
+ } else {
+ jam();
+ found = (localkey1 == Tkeydata[0]);
+ }
+ if (found) {
+ jam();
+ tgeLocked = ElementHeader::getLocked(tgeElementHeader);
+ tgeResult = ZTRUE;
+ operationRecPtr.p->localdata[0] = localkey1;
+ operationRecPtr.p->localdata[1] = localkey2;
+ return;
+ }
+ }
+ if (tgeRemLen <= ZCON_HEAD_SIZE) {
+ break;
+ }
+ tgeElementptr = tgeElementptr + tgeElemStep;
+ } while (true);
+ }//if
+ if (tgeRemLen != ZCON_HEAD_SIZE) {
+ ACCKEY_error(8); return;
+ }//if
+ tgeContainerhead = gePageptr.p->word32[tgeContainerptr];
+ tgeNextptrtype = (tgeContainerhead >> 7) & 0x3;
+ if (tgeNextptrtype == 0) {
+ jam();
+ return; /* NO MORE CONTAINER */
+ }//if
+ tgePageindex = tgeContainerhead & 0x7f; /* NEXT CONTAINER PAGE INDEX 7 BITS */
+ if (tgePageindex > ZEMPTYLIST) {
+ ACCKEY_error(9); return;
+ }//if
+ if (((tgeContainerhead >> 9) & 1) == ZFALSE) {
+ jam();
+ tgeActivePageDir = gePageptr.p->word32[tgeContainerptr + 1]; /* NEXT PAGE ID */
+ geOverflowrangeptr.i = fragrecptr.p->overflowdir;
+ ptrCheckGuard(geOverflowrangeptr, cdirrangesize, dirRange);
+ arrGuard((tgeActivePageDir >> 8), 256);
+ geOverflowDirptr.i = geOverflowrangeptr.p->dirArray[tgeActivePageDir >> 8];
+ ptrCheckGuard(geOverflowDirptr, cdirarraysize, directoryarray);
+ gePageptr.i = geOverflowDirptr.p->pagep[tgeActivePageDir & 0xff];
+ ptrCheckGuard(gePageptr, cpagesize, page8);
+ }//if
+ } while (1);
+ return;
+}//Dbacc::getElement()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF GET_ELEMENT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* MODULE: DELETE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* COMMITDELETE */
+/* INPUT: OPERATION_REC_PTR, PTR TO AN OPERATION RECORD. */
+/* FRAGRECPTR, PTR TO A FRAGMENT RECORD */
+/* */
+/* OUTPUT: */
+/* NONE */
+/* DESCRIPTION: DELETE OPERATIONS WILL BE COMPLETED AT THE COMMIT OF TRANSA- */
+/* CTION. THIS SUBROUTINE SEARCHS FOR ELEMENT AND DELETES IT. IT DOES SO BY */
+/* REPLACING IT WITH THE LAST ELEMENT IN THE BUCKET. IF THE DELETED ELEMENT */
+/* IS ALSO THE LAST ELEMENT THEN IT IS ONLY NECESSARY TO REMOVE THE ELEMENT. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::commitdelete(Signal* signal, bool systemRestart)
+{
+ if (!systemRestart) {
+ jam();
+ signal->theData[0] = fragrecptr.p->myfid;
+ signal->theData[1] = fragrecptr.p->myTableId;
+ signal->theData[2] = operationRecPtr.p->localdata[0];
+ Uint32 localKey = operationRecPtr.p->localdata[0];
+ Uint32 pageId = localKey >> MAX_TUPLES_BITS;
+ Uint32 pageIndex = localKey & ((1 << MAX_TUPLES_BITS) - 1);
+ signal->theData[2] = pageId;
+ signal->theData[3] = pageIndex;
+ EXECUTE_DIRECT(DBTUP, GSN_TUP_DEALLOCREQ, signal, 4);
+ jamEntry();
+ }//if
+ getdirindex(signal);
+ tlastPageindex = tgdiPageindex;
+ lastPageptr.i = gdiPageptr.i;
+ lastPageptr.p = gdiPageptr.p;
+ tlastForward = ZTRUE;
+ tlastContainerptr = (tlastPageindex << ZSHIFT_PLUS) - (tlastPageindex << ZSHIFT_MINUS);
+ tlastContainerptr = tlastContainerptr + ZHEAD_SIZE;
+ arrGuard(tlastContainerptr, 2048);
+ tlastContainerhead = lastPageptr.p->word32[tlastContainerptr];
+ tlastContainerlen = tlastContainerhead >> 26;
+ lastPrevpageptr.i = RNIL;
+ ptrNull(lastPrevpageptr);
+ tlastPrevconptr = 0;
+ getLastAndRemove(signal);
+
+ delPageptr.i = operationRecPtr.p->elementPage;
+ ptrCheckGuard(delPageptr, cpagesize, page8);
+ tdelElementptr = operationRecPtr.p->elementPointer;
+ /* --------------------------------------------------------------------------------- */
+ // Here we have to take extreme care since we do not want locks to end up after the
+ // log execution. Thus it is necessary to put back the element in unlocked shape.
+ // We thus update the element header to ensure we log an unlocked element. We do not
+ // need to restore it later since it is deleted immediately anyway.
+ /* --------------------------------------------------------------------------------- */
+ const Uint32 hv = operationRecPtr.p->hashvaluePart;
+ const Uint32 eh = ElementHeader::setUnlocked(hv, 0);
+ delPageptr.p->word32[tdelElementptr] = eh;
+ if (operationRecPtr.p->elementPage == lastPageptr.i) {
+ if (operationRecPtr.p->elementPointer == tlastElementptr) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE LAST ELEMENT WAS THE ELEMENT TO BE DELETED. WE NEED NOT COPY IT. */
+ /* --------------------------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* THE DELETED ELEMENT IS NOT THE LAST. WE READ THE LAST ELEMENT AND OVERWRITE THE */
+ /* DELETED ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ tdelContainerptr = operationRecPtr.p->elementContainer;
+ tdelForward = operationRecPtr.p->elementIsforward;
+ deleteElement(signal);
+}//Dbacc::commitdelete()
+
+/* --------------------------------------------------------------------------------- */
+/* DELETE_ELEMENT */
+/* INPUT: FRAGRECPTR, POINTER TO A FRAGMENT RECORD */
+/* LAST_PAGEPTR, POINTER TO THE PAGE OF THE LAST ELEMENT */
+/* DEL_PAGEPTR, POINTER TO THE PAGE OF THE DELETED ELEMENT */
+/* TLAST_ELEMENTPTR, ELEMENT POINTER OF THE LAST ELEMENT */
+/* TDEL_ELEMENTPTR, ELEMENT POINTER OF THE DELETED ELEMENT */
+/* TLAST_FORWARD, DIRECTION OF LAST ELEMENT */
+/* TDEL_FORWARD, DIRECTION OF DELETED ELEMENT */
+/* TDEL_CONTAINERPTR, CONTAINER POINTER OF DELETED ELEMENT */
+/* DESCRIPTION: COPY LAST ELEMENT TO DELETED ELEMENT AND UPDATE UNDO LOG AND */
+/* UPDATE ANY ACTIVE OPERATION ON THE MOVED ELEMENT. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::deleteElement(Signal* signal)
+{
+ OperationrecPtr deOperationRecPtr;
+ Uint32 tdeIndex;
+ Uint32 tlastMoveElemptr;
+ Uint32 tdelMoveElemptr;
+ Uint32 guard31;
+
+ if (tlastElementptr >= 2048)
+ goto deleteElement_index_error1;
+ {
+ const Uint32 tdeElemhead = lastPageptr.p->word32[tlastElementptr];
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ datapageptr.p = delPageptr.p;
+ cundoinfolength = fragrecptr.p->elementLength;
+ if (tdelForward == ZTRUE) {
+ jam();
+ cundoElemIndex = tdelElementptr;
+ } else {
+ jam();
+ cundoElemIndex = (tdelElementptr + 1) - fragrecptr.p->elementLength;
+ }//if
+ undoWritingProcess(signal);
+ }//if
+ tlastMoveElemptr = tlastElementptr;
+ tdelMoveElemptr = tdelElementptr;
+ guard31 = fragrecptr.p->elementLength - 1;
+ for (tdeIndex = 0; tdeIndex <= guard31; tdeIndex++) {
+ dbgWord32(delPageptr, tdelMoveElemptr, lastPageptr.p->word32[tlastMoveElemptr]);
+ if ((tlastMoveElemptr >= 2048) ||
+ (tdelMoveElemptr >= 2048))
+ goto deleteElement_index_error2;
+ delPageptr.p->word32[tdelMoveElemptr] = lastPageptr.p->word32[tlastMoveElemptr];
+ tdelMoveElemptr = tdelMoveElemptr + tdelForward;
+ tlastMoveElemptr = tlastMoveElemptr + tlastForward;
+ }//for
+ if (ElementHeader::getLocked(tdeElemhead)) {
+ /* --------------------------------------------------------------------------------- */
+ /* THE LAST ELEMENT IS LOCKED AND IS THUS REFERENCED BY AN OPERATION RECORD. WE NEED */
+ /* TO UPDATE THE OPERATION RECORD WITH THE NEW REFERENCE TO THE ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ deOperationRecPtr.i = ElementHeader::getOpPtrI(tdeElemhead);
+ ptrCheckGuard(deOperationRecPtr, coprecsize, operationrec);
+ if (cundoLogActive == ZFALSE) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE DO NOT BOTHER WITH THIS INFORMATION DURING EXECUTION OF THE UNDO LOG. */
+ /* --------------------------------------------------------------------------------- */
+ deOperationRecPtr.p->elementPage = delPageptr.i;
+ deOperationRecPtr.p->elementContainer = tdelContainerptr;
+ deOperationRecPtr.p->elementPointer = tdelElementptr;
+ deOperationRecPtr.p->elementIsforward = tdelForward;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ // We need to take extreme care to not install locked records after system restart.
+ // An undo of the delete will reinstall the moved record. We have to ensure that the
+ // lock is removed to ensure that no such thing happen.
+ /* --------------------------------------------------------------------------------- */
+ Uint32 eh = ElementHeader::setUnlocked(deOperationRecPtr.p->hashvaluePart,
+ 0);
+ lastPageptr.p->word32[tlastElementptr] = eh;
+ }//if
+ return;
+ }
+
+ deleteElement_index_error1:
+ arrGuard(tlastElementptr, 2048);
+ return;
+
+ deleteElement_index_error2:
+ arrGuard(tdelMoveElemptr + guard31, 2048);
+ arrGuard(tlastMoveElemptr, 2048);
+ return;
+
+}//Dbacc::deleteElement()
+
+/* --------------------------------------------------------------------------------- */
+/* GET_LAST_AND_REMOVE */
+/* INPUT: */
+/* LAST_PAGEPTR PAGE POINTER OF FIRST CONTAINER IN SEARCH OF LAST*/
+/* TLAST_CONTAINERPTR CONTAINER INDEX OF THE SAME */
+/* TLAST_CONTAINERHEAD CONTAINER HEADER OF THE SAME */
+/* TLAST_PAGEINDEX PAGE INDEX OF THE SAME */
+/* TLAST_FORWARD CONTAINER DIRECTION OF THE SAME */
+/* TLAST_CONTAINERLEN CONTAINER LENGTH OF THE SAME */
+/* LAST_PREVPAGEPTR PAGE POINTER OF PREVIOUS CONTAINER OF THE SAME */
+/* TLAST_PREVCONPTR CONTAINER INDEX OF PREVIOUS CONTAINER OF THE SAME*/
+/* */
+/* OUTPUT: */
+/* ALL VARIABLES FROM INPUT BUT NOW CONTAINING INFO ABOUT LAST */
+/* CONTAINER. */
+/* TLAST_ELEMENTPTR LAST ELEMENT POINTER IN LAST CONTAINER */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::getLastAndRemove(Signal* signal)
+{
+ DirRangePtr glrOverflowrangeptr;
+ DirectoryarrayPtr glrOverflowDirptr;
+ Uint32 tglrHead;
+ Uint32 tglrTmp;
+
+ GLR_LOOP_10:
+ if (((tlastContainerhead >> 7) & 0x3) != 0) {
+ jam();
+ lastPrevpageptr.i = lastPageptr.i;
+ lastPrevpageptr.p = lastPageptr.p;
+ tlastPrevconptr = tlastContainerptr;
+ tlastPageindex = tlastContainerhead & 0x7f;
+ if (((tlastContainerhead >> 9) & 0x1) == ZFALSE) {
+ jam();
+ arrGuard(tlastContainerptr + 1, 2048);
+ tglrTmp = lastPageptr.p->word32[tlastContainerptr + 1];
+ glrOverflowrangeptr.i = fragrecptr.p->overflowdir;
+ ptrCheckGuard(glrOverflowrangeptr, cdirrangesize, dirRange);
+ arrGuard((tglrTmp >> 8), 256);
+ glrOverflowDirptr.i = glrOverflowrangeptr.p->dirArray[tglrTmp >> 8];
+ ptrCheckGuard(glrOverflowDirptr, cdirarraysize, directoryarray);
+ lastPageptr.i = glrOverflowDirptr.p->pagep[tglrTmp & 0xff];
+ ptrCheckGuard(lastPageptr, cpagesize, page8);
+ }//if
+ tlastContainerptr = (tlastPageindex << ZSHIFT_PLUS) - (tlastPageindex << ZSHIFT_MINUS);
+ if (((tlastContainerhead >> 7) & 3) == ZLEFT) {
+ jam();
+ tlastForward = ZTRUE;
+ tlastContainerptr = tlastContainerptr + ZHEAD_SIZE;
+ } else if (((tlastContainerhead >> 7) & 3) == ZRIGHT) {
+ jam();
+ tlastForward = cminusOne;
+ tlastContainerptr = ((tlastContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - ZCON_HEAD_SIZE;
+ } else {
+ ndbrequire(false);
+ return;
+ }//if
+ arrGuard(tlastContainerptr, 2048);
+ tlastContainerhead = lastPageptr.p->word32[tlastContainerptr];
+ tlastContainerlen = tlastContainerhead >> 26;
+ ndbrequire(tlastContainerlen >= ((Uint32)ZCON_HEAD_SIZE + fragrecptr.p->elementLength));
+ goto GLR_LOOP_10;
+ }//if
+ tlastContainerlen = tlastContainerlen - fragrecptr.p->elementLength;
+ if (tlastForward == ZTRUE) {
+ jam();
+ tlastElementptr = tlastContainerptr + tlastContainerlen;
+ } else {
+ jam();
+ tlastElementptr = (tlastContainerptr + (ZCON_HEAD_SIZE - 1)) - tlastContainerlen;
+ }//if
+ rlPageptr.i = lastPageptr.i;
+ rlPageptr.p = lastPageptr.p;
+ trlPageindex = tlastPageindex;
+ if (((tlastContainerhead >> 10) & 1) == 1) {
+ /* --------------------------------------------------------------------------------- */
+ /* WE HAVE OWNERSHIP OF BOTH PARTS OF THE CONTAINER ENDS. */
+ /* --------------------------------------------------------------------------------- */
+ if (tlastContainerlen < ZDOWN_LIMIT) {
+ /* --------------------------------------------------------------------------------- */
+ /* WE HAVE DECREASED THE SIZE BELOW THE DOWN LIMIT, WE MUST GIVE UP THE OTHER */
+ /* SIDE OF THE BUFFER. */
+ /* --------------------------------------------------------------------------------- */
+ tlastContainerhead = tlastContainerhead ^ (1 << 10);
+ trlRelCon = ZFALSE;
+ if (tlastForward == ZTRUE) {
+ jam();
+ turlIndex = tlastContainerptr + (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ releaseRightlist(signal);
+ } else {
+ jam();
+ tullIndex = tlastContainerptr - (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ releaseLeftlist(signal);
+ }//if
+ }//if
+ }//if
+ if (tlastContainerlen <= 2) {
+ ndbrequire(tlastContainerlen == 2);
+ if (lastPrevpageptr.i != RNIL) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE LAST CONTAINER IS EMPTY AND IS NOT THE FIRST CONTAINER WHICH IS NOT REMOVED. */
+ /* DELETE THE LAST CONTAINER AND UPDATE THE PREVIOUS CONTAINER. ALSO PUT THIS */
+ /* CONTAINER IN FREE CONTAINER LIST OF THE PAGE. */
+ /* --------------------------------------------------------------------------------- */
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = lastPrevpageptr.p;
+ cundoElemIndex = tlastPrevconptr;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ ndbrequire(tlastPrevconptr < 2048);
+ tglrTmp = lastPrevpageptr.p->word32[tlastPrevconptr] >> 9;
+ dbgWord32(lastPrevpageptr, tlastPrevconptr, tglrTmp << 9);
+ lastPrevpageptr.p->word32[tlastPrevconptr] = tglrTmp << 9;
+ trlRelCon = ZTRUE;
+ if (tlastForward == ZTRUE) {
+ jam();
+ tullIndex = tlastContainerptr;
+ releaseLeftlist(signal);
+ } else {
+ jam();
+ turlIndex = tlastContainerptr;
+ releaseRightlist(signal);
+ }//if
+ return;
+ }//if
+ }//if
+ tglrHead = tlastContainerhead << 6;
+ tglrHead = tglrHead >> 6;
+ tglrHead = tglrHead | (tlastContainerlen << 26);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = lastPageptr.p;
+ cundoElemIndex = tlastContainerptr;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(lastPageptr, tlastContainerptr, tglrHead);
+ arrGuard(tlastContainerptr, 2048);
+ lastPageptr.p->word32[tlastContainerptr] = tglrHead;
+}//Dbacc::getLastAndRemove()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_LEFTLIST */
+/* INPUT: */
+/* RL_PAGEPTR PAGE POINTER OF CONTAINER TO BE RELEASED */
+/* TRL_PAGEINDEX PAGE INDEX OF CONTAINER TO BE RELEASED */
+/* TURL_INDEX INDEX OF CONTAINER TO BE RELEASED */
+/* TRL_REL_CON TRUE IF CONTAINER RELEASED OTHERWISE ONLY */
+/* A PART IS RELEASED. */
+/* */
+/* OUTPUT: */
+/* NONE */
+/* */
+/* THE FREE LIST OF LEFT FREE BUFFER IN THE PAGE WILL BE UPDATE */
+/* TULL_INDEX IS INDEX TO THE FIRST WORD IN THE LEFT SIDE OF THE BUFFER */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseLeftlist(Signal* signal)
+{
+ Uint32 tullTmp;
+ Uint32 tullTmp1;
+
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = rlPageptr.p;
+ cundoElemIndex = tullIndex;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = ZPOS_EMPTY_LIST;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* IF A CONTAINER IS RELEASED AND NOT ONLY A PART THEN WE HAVE TO REMOVE IT */
+ /* FROM THE LIST OF USED CONTAINERS IN THE PAGE. THIS IN ORDER TO ENSURE THAT */
+ /* WE CAN FIND ALL LOCKED ELEMENTS DURING LOCAL CHECKPOINT. */
+ /* --------------------------------------------------------------------------------- */
+ if (trlRelCon == ZTRUE) {
+ arrGuard(tullIndex, 2048);
+ trlHead = rlPageptr.p->word32[tullIndex];
+ trlNextused = (trlHead >> 11) & 0x7f;
+ trlPrevused = (trlHead >> 18) & 0x7f;
+ if (trlNextused < ZEMPTYLIST) {
+ jam();
+ tullTmp1 = (trlNextused << ZSHIFT_PLUS) - (trlNextused << ZSHIFT_MINUS);
+ tullTmp1 = tullTmp1 + ZHEAD_SIZE;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = tullTmp1;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ tullTmp = rlPageptr.p->word32[tullTmp1] & 0xfe03ffff;
+ dbgWord32(rlPageptr, tullTmp1, tullTmp | (trlPrevused << 18));
+ rlPageptr.p->word32[tullTmp1] = tullTmp | (trlPrevused << 18);
+ } else {
+ ndbrequire(trlNextused == ZEMPTYLIST);
+ jam();
+ }//if
+ if (trlPrevused < ZEMPTYLIST) {
+ jam();
+ tullTmp1 = (trlPrevused << ZSHIFT_PLUS) - (trlPrevused << ZSHIFT_MINUS);
+ tullTmp1 = tullTmp1 + ZHEAD_SIZE;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = tullTmp1;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ tullTmp = rlPageptr.p->word32[tullTmp1] & 0xfffc07ff;
+ dbgWord32(rlPageptr, tullTmp1, tullTmp | (trlNextused << 11));
+ rlPageptr.p->word32[tullTmp1] = tullTmp | (trlNextused << 11);
+ } else {
+ ndbrequire(trlPrevused == ZEMPTYLIST);
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE FIRST IN THE LIST AND THUS WE NEED TO UPDATE THE FIRST POINTER. */
+ /* --------------------------------------------------------------------------------- */
+ tullTmp = rlPageptr.p->word32[ZPOS_EMPTY_LIST] & 0xc07fffff;
+ dbgWord32(rlPageptr, ZPOS_EMPTY_LIST, tullTmp | (trlNextused << 23));
+ rlPageptr.p->word32[ZPOS_EMPTY_LIST] = tullTmp | (trlNextused << 23);
+ }//if
+ }//if
+ dbgWord32(rlPageptr, tullIndex + 1, ZEMPTYLIST);
+ arrGuard(tullIndex + 1, 2048);
+ rlPageptr.p->word32[tullIndex + 1] = ZEMPTYLIST;
+ tullTmp1 = (rlPageptr.p->word32[ZPOS_EMPTY_LIST] >> 7) & 0x7f;
+ dbgWord32(rlPageptr, tullIndex, tullTmp1);
+ arrGuard(tullIndex, 2048);
+ rlPageptr.p->word32[tullIndex] = tullTmp1;
+ if (tullTmp1 < ZEMPTYLIST) {
+ jam();
+ tullTmp1 = (tullTmp1 << ZSHIFT_PLUS) - (tullTmp1 << ZSHIFT_MINUS);
+ tullTmp1 = (tullTmp1 + ZHEAD_SIZE) + 1;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = tullTmp1;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(rlPageptr, tullTmp1, trlPageindex);
+ rlPageptr.p->word32[tullTmp1] = trlPageindex; /* UPDATES PREV POINTER IN THE NEXT FREE */
+ } else {
+ ndbrequire(tullTmp1 == ZEMPTYLIST);
+ }//if
+ tullTmp = rlPageptr.p->word32[ZPOS_EMPTY_LIST];
+ tullTmp = (((tullTmp >> 14) << 14) | (trlPageindex << 7)) | (tullTmp & 0x7f);
+ dbgWord32(rlPageptr, ZPOS_EMPTY_LIST, tullTmp);
+ rlPageptr.p->word32[ZPOS_EMPTY_LIST] = tullTmp;
+ dbgWord32(rlPageptr, ZPOS_ALLOC_CONTAINERS, rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1);
+ rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1;
+ ndbrequire(rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] <= ZNIL);
+ if (((rlPageptr.p->word32[ZPOS_EMPTY_LIST] >> ZPOS_PAGE_TYPE_BIT) & 3) == 1) {
+ jam();
+ colPageptr.i = rlPageptr.i;
+ colPageptr.p = rlPageptr.p;
+ ptrCheck(colPageptr, cpagesize, page8);
+ checkoverfreelist(signal);
+ }//if
+}//Dbacc::releaseLeftlist()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_RIGHTLIST */
+/* INPUT: */
+/* RL_PAGEPTR PAGE POINTER OF CONTAINER TO BE RELEASED */
+/* TRL_PAGEINDEX PAGE INDEX OF CONTAINER TO BE RELEASED */
+/* TURL_INDEX INDEX OF CONTAINER TO BE RELEASED */
+/* TRL_REL_CON TRUE IF CONTAINER RELEASED OTHERWISE ONLY */
+/* A PART IS RELEASED. */
+/* */
+/* OUTPUT: */
+/* NONE */
+/* */
+/* THE FREE LIST OF RIGHT FREE BUFFER IN THE PAGE WILL BE UPDATE. */
+/* TURL_INDEX IS INDEX TO THE FIRST WORD IN THE RIGHT SIDE OF */
+/* THE BUFFER, WHICH IS THE LAST WORD IN THE BUFFER. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseRightlist(Signal* signal)
+{
+ Uint32 turlTmp1;
+ Uint32 turlTmp;
+
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = rlPageptr.p;
+ cundoElemIndex = turlIndex;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = ZPOS_EMPTY_LIST;
+ cundoinfolength = 2;
+ undoWritingProcess(signal);
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* IF A CONTAINER IS RELEASED AND NOT ONLY A PART THEN WE HAVE TO REMOVE IT */
+ /* FROM THE LIST OF USED CONTAINERS IN THE PAGE. THIS IN ORDER TO ENSURE THAT */
+ /* WE CAN FIND ALL LOCKED ELEMENTS DURING LOCAL CHECKPOINT. */
+ /* --------------------------------------------------------------------------------- */
+ if (trlRelCon == ZTRUE) {
+ jam();
+ arrGuard(turlIndex, 2048);
+ trlHead = rlPageptr.p->word32[turlIndex];
+ trlNextused = (trlHead >> 11) & 0x7f;
+ trlPrevused = (trlHead >> 18) & 0x7f;
+ if (trlNextused < ZEMPTYLIST) {
+ jam();
+ turlTmp1 = (trlNextused << ZSHIFT_PLUS) - (trlNextused << ZSHIFT_MINUS);
+ turlTmp1 = turlTmp1 + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = turlTmp1;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ turlTmp = rlPageptr.p->word32[turlTmp1] & 0xfe03ffff;
+ dbgWord32(rlPageptr, turlTmp1, turlTmp | (trlPrevused << 18));
+ rlPageptr.p->word32[turlTmp1] = turlTmp | (trlPrevused << 18);
+ } else {
+ ndbrequire(trlNextused == ZEMPTYLIST);
+ jam();
+ }//if
+ if (trlPrevused < ZEMPTYLIST) {
+ jam();
+ turlTmp1 = (trlPrevused << ZSHIFT_PLUS) - (trlPrevused << ZSHIFT_MINUS);
+ turlTmp1 = turlTmp1 + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = turlTmp1;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ turlTmp = rlPageptr.p->word32[turlTmp1] & 0xfffc07ff;
+ dbgWord32(rlPageptr, turlTmp1, turlTmp | (trlNextused << 11));
+ rlPageptr.p->word32[turlTmp1] = turlTmp | (trlNextused << 11);
+ } else {
+ ndbrequire(trlPrevused == ZEMPTYLIST);
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE FIRST IN THE LIST AND THUS WE NEED TO UPDATE THE FIRST POINTER */
+ /* OF THE RIGHT CONTAINER LIST. */
+ /* --------------------------------------------------------------------------------- */
+ turlTmp = rlPageptr.p->word32[ZPOS_EMPTY_LIST] & 0xff80ffff;
+ dbgWord32(rlPageptr, ZPOS_EMPTY_LIST, turlTmp | (trlNextused << 16));
+ rlPageptr.p->word32[ZPOS_EMPTY_LIST] = turlTmp | (trlNextused << 16);
+ }//if
+ }//if
+ dbgWord32(rlPageptr, turlIndex + 1, ZEMPTYLIST);
+ arrGuard(turlIndex + 1, 2048);
+ rlPageptr.p->word32[turlIndex + 1] = ZEMPTYLIST;
+ turlTmp1 = rlPageptr.p->word32[ZPOS_EMPTY_LIST] & 0x7f;
+ dbgWord32(rlPageptr, turlIndex, turlTmp1);
+ arrGuard(turlIndex, 2048);
+ rlPageptr.p->word32[turlIndex] = turlTmp1;
+ if (turlTmp1 < ZEMPTYLIST) {
+ jam();
+ turlTmp = (turlTmp1 << ZSHIFT_PLUS) - (turlTmp1 << ZSHIFT_MINUS);
+ turlTmp = turlTmp + ((ZHEAD_SIZE + ZBUF_SIZE) - (ZCON_HEAD_SIZE - 1));
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ cundoElemIndex = turlTmp;
+ cundoinfolength = 1;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(rlPageptr, turlTmp, trlPageindex);
+ rlPageptr.p->word32[turlTmp] = trlPageindex; /* UPDATES PREV POINTER IN THE NEXT FREE */
+ } else {
+ ndbrequire(turlTmp1 == ZEMPTYLIST);
+ }//if
+ turlTmp = rlPageptr.p->word32[ZPOS_EMPTY_LIST];
+ dbgWord32(rlPageptr, ZPOS_EMPTY_LIST, ((turlTmp >> 7) << 7) | trlPageindex);
+ rlPageptr.p->word32[ZPOS_EMPTY_LIST] = ((turlTmp >> 7) << 7) | trlPageindex;
+ dbgWord32(rlPageptr, ZPOS_ALLOC_CONTAINERS, rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1);
+ rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] - 1;
+ ndbrequire(rlPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] <= ZNIL);
+ if (((rlPageptr.p->word32[ZPOS_EMPTY_LIST] >> ZPOS_PAGE_TYPE_BIT) & 3) == 1) {
+ jam();
+ colPageptr.i = rlPageptr.i;
+ colPageptr.p = rlPageptr.p;
+ checkoverfreelist(signal);
+ }//if
+}//Dbacc::releaseRightlist()
+
+/* --------------------------------------------------------------------------------- */
+/* CHECKOVERFREELIST */
+/* INPUT: COL_PAGEPTR, POINTER OF AN OVERFLOW PAGE RECORD. */
+/* DESCRIPTION: CHECKS IF THE PAGE HAVE TO PUT IN FREE LIST OF OVER FLOW */
+/* PAGES. WHEN IT HAVE TO, AN OVERFLOW REC PTR WILL BE ALLOCATED */
+/* TO KEEP NFORMATION ABOUT THE PAGE. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::checkoverfreelist(Signal* signal)
+{
+ Uint32 tcolTmp;
+
+ if (fragrecptr.p->loadingFlag == ZFALSE) {
+ tcolTmp = colPageptr.p->word32[ZPOS_ALLOC_CONTAINERS];
+ if (tcolTmp <= ZFREE_LIMIT) {
+ if (tcolTmp == 0) {
+ jam();
+ ropPageptr = colPageptr;
+ releaseOverpage(signal);
+ } else {
+ jam();
+ if (colPageptr.p->word32[ZPOS_OVERFLOWREC] == RNIL) {
+ ndbrequire(cfirstfreeoverrec != RNIL);
+ jam();
+ seizeOverRec(signal);
+ sorOverflowRecPtr.p->dirindex = colPageptr.p->word32[ZPOS_PAGE_ID];
+ sorOverflowRecPtr.p->overpage = colPageptr.i;
+ dbgWord32(colPageptr, ZPOS_OVERFLOWREC, sorOverflowRecPtr.i);
+ colPageptr.p->word32[ZPOS_OVERFLOWREC] = sorOverflowRecPtr.i;
+ porOverflowRecPtr = sorOverflowRecPtr;
+ putOverflowRecInFrag(signal);
+ }//if
+ }//if
+ }//if
+ }//if
+}//Dbacc::checkoverfreelist()
+
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* */
+/* END OF DELETE MODULE */
+/* */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* */
+/* COMMIT AND ABORT MODULE */
+/* */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ABORT_OPERATION */
+/*DESCRIPTION: AN OPERATION RECORD CAN BE IN A LOCK QUEUE OF AN ELEMENT OR */
+/*OWNS THE LOCK. BY THIS SUBROUTINE THE LOCK STATE OF THE OPERATION WILL */
+/*BE CHECKED. THE OPERATION RECORD WILL BE REMOVED FROM THE QUEUE IF IT */
+/*BELONGED TO ANY ONE, OTHERWISE THE ELEMENT HEAD WILL BE UPDATED. */
+/* ------------------------------------------------------------------------- */
+void Dbacc::abortOperation(Signal* signal)
+{
+ OperationrecPtr aboOperRecPtr;
+ OperationrecPtr TaboOperRecPtr;
+ Page8Ptr aboPageidptr;
+ Uint32 taboElementptr;
+ Uint32 tmp2Olq;
+
+ if (operationRecPtr.p->lockOwner == ZTRUE) {
+ takeOutLockOwnersList(signal, operationRecPtr);
+ if (operationRecPtr.p->insertIsDone == ZTRUE) {
+ jam();
+ operationRecPtr.p->elementIsDisappeared = ZTRUE;
+ }//if
+ if ((operationRecPtr.p->nextParallelQue != RNIL) ||
+ (operationRecPtr.p->nextSerialQue != RNIL)) {
+ jam();
+ releaselock(signal);
+ } else {
+ /* --------------------------------------------------------------------------------- */
+ /* WE ARE OWNER OF THE LOCK AND NO OTHER OPERATIONS ARE QUEUED. IF INSERT OR STANDBY */
+ /* WE DELETE THE ELEMENT OTHERWISE WE REMOVE THE LOCK FROM THE ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ if (operationRecPtr.p->elementIsDisappeared == ZFALSE) {
+ jam();
+ taboElementptr = operationRecPtr.p->elementPointer;
+ aboPageidptr.i = operationRecPtr.p->elementPage;
+ tmp2Olq = ElementHeader::setUnlocked(operationRecPtr.p->hashvaluePart,
+ operationRecPtr.p->scanBits);
+ ptrCheckGuard(aboPageidptr, cpagesize, page8);
+ dbgWord32(aboPageidptr, taboElementptr, tmp2Olq);
+ arrGuard(taboElementptr, 2048);
+ aboPageidptr.p->word32[taboElementptr] = tmp2Olq;
+ return;
+ } else {
+ jam();
+ commitdelete(signal, false);
+ }//if
+ }//if
+ } else {
+ /* --------------------------------------------------------------- */
+ // We are not the lock owner.
+ /* --------------------------------------------------------------- */
+ jam();
+ takeOutFragWaitQue(signal);
+ if (operationRecPtr.p->prevParallelQue != RNIL) {
+ jam();
+ /* ---------------------------------------------------------------------------------- */
+ /* SINCE WE ARE NOT QUEUE LEADER WE NEED NOT CONSIDER IF THE ELEMENT IS TO BE DELETED.*/
+ /* We will simply remove it from the parallel list without any other rearrangements. */
+ /* ---------------------------------------------------------------------------------- */
+ aboOperRecPtr.i = operationRecPtr.p->prevParallelQue;
+ ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec);
+ aboOperRecPtr.p->nextParallelQue = operationRecPtr.p->nextParallelQue;
+ if (operationRecPtr.p->nextParallelQue != RNIL) {
+ jam();
+ aboOperRecPtr.i = operationRecPtr.p->nextParallelQue;
+ ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec);
+ aboOperRecPtr.p->prevParallelQue = operationRecPtr.p->prevParallelQue;
+ }//if
+ } else if (operationRecPtr.p->prevSerialQue != RNIL) {
+ /* ------------------------------------------------------------------------- */
+ // We are not in the parallel queue owning the lock. Thus we are in another parallel
+ // queue longer down in the serial queue. We are however first since prevParallelQue
+ // == RNIL.
+ /* ------------------------------------------------------------------------- */
+ if (operationRecPtr.p->nextParallelQue != RNIL) {
+ jam();
+ /* ------------------------------------------------------------------------- */
+ // We have an operation in the queue after us. We simply rearrange this parallel queue.
+ // The new leader of this parallel queue will be operation in the serial queue.
+ /* ------------------------------------------------------------------------- */
+ aboOperRecPtr.i = operationRecPtr.p->nextParallelQue;
+ ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec);
+ aboOperRecPtr.p->nextSerialQue = operationRecPtr.p->nextSerialQue;
+ aboOperRecPtr.p->prevSerialQue = operationRecPtr.p->prevSerialQue;
+ aboOperRecPtr.p->prevParallelQue = RNIL; // Queue Leader
+ if (operationRecPtr.p->nextSerialQue != RNIL) {
+ jam();
+ TaboOperRecPtr.i = operationRecPtr.p->nextSerialQue;
+ ptrCheckGuard(TaboOperRecPtr, coprecsize, operationrec);
+ TaboOperRecPtr.p->prevSerialQue = aboOperRecPtr.i;
+ }//if
+ TaboOperRecPtr.i = operationRecPtr.p->prevSerialQue;
+ ptrCheckGuard(TaboOperRecPtr, coprecsize, operationrec);
+ TaboOperRecPtr.p->nextSerialQue = aboOperRecPtr.i;
+ } else {
+ jam();
+ /* ------------------------------------------------------------------------- */
+ // We are the only operation in this parallel queue. We will thus shrink the serial
+ // queue.
+ /* ------------------------------------------------------------------------- */
+ aboOperRecPtr.i = operationRecPtr.p->prevSerialQue;
+ ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec);
+ aboOperRecPtr.p->nextSerialQue = operationRecPtr.p->nextSerialQue;
+ if (operationRecPtr.p->nextSerialQue != RNIL) {
+ jam();
+ aboOperRecPtr.i = operationRecPtr.p->nextSerialQue;
+ ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec);
+ aboOperRecPtr.p->prevSerialQue = operationRecPtr.p->prevSerialQue;
+ }//if
+ }//if
+ }//if
+ }//if
+ /* ------------------------------------------------------------------------- */
+ // If prevParallelQue = RNIL and prevSerialQue = RNIL and we are not owner of the
+ // lock then we cannot be in any lock queue at all.
+ /* ------------------------------------------------------------------------- */
+}//Dbacc::abortOperation()
+
+void Dbacc::commitDeleteCheck()
+{
+ OperationrecPtr opPtr;
+ OperationrecPtr lastOpPtr;
+ OperationrecPtr deleteOpPtr;
+ bool elementDeleted = false;
+ bool deleteCheckOngoing = true;
+ Uint32 hashValue = 0;
+ lastOpPtr = operationRecPtr;
+ opPtr.i = operationRecPtr.p->nextParallelQue;
+ while (opPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(opPtr, coprecsize, operationrec);
+ lastOpPtr = opPtr;
+ opPtr.i = opPtr.p->nextParallelQue;
+ }//while
+ deleteOpPtr = lastOpPtr;
+ do {
+ if (deleteOpPtr.p->operation == ZDELETE) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* IF THE CURRENT OPERATION TO BE COMMITTED IS A DELETE OPERATION DUE TO A */
+ /* SCAN-TAKEOVER THE ACTUAL DELETE WILL BE PERFORMED BY THE PREVIOUS OPERATION (SCAN)*/
+ /* IN THE PARALLEL QUEUE WHICH OWNS THE LOCK.THE PROBLEM IS THAT THE SCAN OPERATION */
+ /* DOES NOT HAVE A HASH VALUE ASSIGNED TO IT SO WE COPY IT FROM THIS OPERATION. */
+ /* */
+ /* WE ASSUME THAT THIS SOLUTION WILL WORK BECAUSE THE ONLY WAY A SCAN CAN PERFORM */
+ /* A DELETE IS BY BEING FOLLOWED BY A NORMAL DELETE-OPERATION THAT HAS A HASH VALUE. */
+ /* --------------------------------------------------------------------------------- */
+ hashValue = deleteOpPtr.p->hashValue;
+ elementDeleted = true;
+ deleteCheckOngoing = false;
+ } else if ((deleteOpPtr.p->operation == ZREAD) ||
+ (deleteOpPtr.p->operation == ZSCAN_OP)) {
+ /* --------------------------------------------------------------------------------- */
+ /* We are trying to find out whether the commit will in the end delete the tuple. */
+ /* Normally the delete will be the last operation in the list of operations on this */
+ /* It is however possible to issue reads and scans in the same savepoint as the */
+ /* delete operation was issued and these can end up after the delete in the list of */
+ /* operations in the parallel queue. Thus if we discover a read or a scan we have to */
+ /* continue scanning the list looking for a delete operation. */
+ /* --------------------------------------------------------------------------------- */
+ deleteOpPtr.i = deleteOpPtr.p->prevParallelQue;
+ if (deleteOpPtr.i == RNIL) {
+ jam();
+ deleteCheckOngoing = false;
+ } else {
+ jam();
+ ptrCheckGuard(deleteOpPtr, coprecsize, operationrec);
+ }//if
+ } else {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* Finding an UPDATE or INSERT before finding a DELETE means we cannot be deleting */
+ /* as the end result of this transaction. */
+ /* --------------------------------------------------------------------------------- */
+ deleteCheckOngoing = false;
+ }//if
+ } while (deleteCheckOngoing);
+ opPtr = lastOpPtr;
+ do {
+ jam();
+ opPtr.p->commitDeleteCheckFlag = ZTRUE;
+ if (elementDeleted) {
+ jam();
+ opPtr.p->elementIsDisappeared = ZTRUE;
+ opPtr.p->hashValue = hashValue;
+ }//if
+ opPtr.i = opPtr.p->prevParallelQue;
+ if (opPtr.i == RNIL) {
+ jam();
+ break;
+ }//if
+ ptrCheckGuard(opPtr, coprecsize, operationrec);
+ } while (true);
+}//Dbacc::commitDeleteCheck()
+
+/* ------------------------------------------------------------------------- */
+/* COMMIT_OPERATION */
+/* INPUT: OPERATION_REC_PTR, POINTER TO AN OPERATION RECORD */
+/* DESCRIPTION: THE OPERATION RECORD WILL BE TAKE OUT OF ANY LOCK QUEUE. */
+/* IF IT OWNS THE ELEMENT LOCK. HEAD OF THE ELEMENT WILL BE UPDATED. */
+/* ------------------------------------------------------------------------- */
+void Dbacc::commitOperation(Signal* signal)
+{
+ OperationrecPtr tolqTmpPtr;
+ Page8Ptr coPageidptr;
+ Uint32 tcoElementptr;
+ Uint32 tmp2Olq;
+
+ if ((operationRecPtr.p->commitDeleteCheckFlag == ZFALSE) &&
+ (operationRecPtr.p->operation != ZSCAN_OP) &&
+ (operationRecPtr.p->operation != ZREAD)) {
+ jam();
+ /* This method is used to check whether the end result of the transaction
+ will be to delete the tuple. In this case all operation will be marked
+ with elementIsDisappeared = true to ensure that the last operation
+ committed will remove the tuple. We only run this once per transaction
+ (commitDeleteCheckFlag = true if performed earlier) and we don't
+ execute this code when committing a scan operation since committing
+ a scan operation only means that the scan is continuing and the scan
+ lock is released.
+ */
+ commitDeleteCheck();
+ }//if
+ if (operationRecPtr.p->lockOwner == ZTRUE) {
+ takeOutLockOwnersList(signal, operationRecPtr);
+ if ((operationRecPtr.p->nextParallelQue == RNIL) &&
+ (operationRecPtr.p->nextSerialQue == RNIL) &&
+ (operationRecPtr.p->elementIsDisappeared == ZFALSE)) {
+ /*
+ This is the normal path through the commit for operations owning the
+ lock without any queues and not a delete operation.
+ */
+ coPageidptr.i = operationRecPtr.p->elementPage;
+ tcoElementptr = operationRecPtr.p->elementPointer;
+ tmp2Olq = ElementHeader::setUnlocked(operationRecPtr.p->hashvaluePart,
+ operationRecPtr.p->scanBits);
+ ptrCheckGuard(coPageidptr, cpagesize, page8);
+ dbgWord32(coPageidptr, tcoElementptr, tmp2Olq);
+ arrGuard(tcoElementptr, 2048);
+ coPageidptr.p->word32[tcoElementptr] = tmp2Olq;
+ return;
+ } else if ((operationRecPtr.p->nextParallelQue != RNIL) ||
+ (operationRecPtr.p->nextSerialQue != RNIL)) {
+ jam();
+ /*
+ The case when there is a queue lined up.
+ Release the lock and pass it to the next operation lined up.
+ */
+ releaselock(signal);
+ return;
+ } else {
+ jam();
+ /*
+ No queue and elementIsDisappeared is true. We perform the actual delete
+ operation.
+ */
+ commitdelete(signal, false);
+ return;
+ }//if
+ } else {
+ /*
+ THE OPERATION DOES NOT OWN THE LOCK. IT MUST BE IN A LOCK QUEUE OF THE
+ ELEMENT.
+ */
+ ndbrequire(operationRecPtr.p->prevParallelQue != RNIL);
+ jam();
+ tolqTmpPtr.i = operationRecPtr.p->prevParallelQue;
+ ptrCheckGuard(tolqTmpPtr, coprecsize, operationrec);
+ tolqTmpPtr.p->nextParallelQue = operationRecPtr.p->nextParallelQue;
+ if (operationRecPtr.p->nextParallelQue != RNIL) {
+ jam();
+ tolqTmpPtr.i = operationRecPtr.p->nextParallelQue;
+ ptrCheckGuard(tolqTmpPtr, coprecsize, operationrec);
+ tolqTmpPtr.p->prevParallelQue = operationRecPtr.p->prevParallelQue;
+ }//if
+ }//if
+}//Dbacc::commitOperation()
+
+/* ------------------------------------------------------------------------- */
+/* RELEASELOCK */
+/* RESETS LOCK OF AN ELEMENT. */
+/* INFORMATION ABOUT THE ELEMENT IS SAVED IN THE OPERATION RECORD */
+/* THESE INFORMATION IS USED TO UPDATE HEADER OF THE ELEMENT */
+/* ------------------------------------------------------------------------- */
+void Dbacc::releaselock(Signal* signal)
+{
+ OperationrecPtr rloOperPtr;
+ OperationrecPtr trlOperPtr;
+ OperationrecPtr trlTmpOperPtr;
+ Uint32 TelementIsDisappeared;
+
+ trlOperPtr.i = RNIL;
+ if (operationRecPtr.p->nextParallelQue != RNIL) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* NEXT OPERATION TAKES OVER THE LOCK. We will simply move the info from the leader */
+ // to the new queue leader.
+ /* --------------------------------------------------------------------------------- */
+ trlOperPtr.i = operationRecPtr.p->nextParallelQue;
+ ptrCheckGuard(trlOperPtr, coprecsize, operationrec);
+ copyInOperPtr = trlOperPtr;
+ copyOperPtr = operationRecPtr;
+ copyOpInfo(signal);
+ trlOperPtr.p->prevParallelQue = RNIL;
+ if (operationRecPtr.p->nextSerialQue != RNIL) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THERE IS A SERIAL QUEUE. MOVE IT FROM RELEASED OP REC TO THE NEW LOCK OWNER. */
+ /* --------------------------------------------------------------------------------- */
+ trlOperPtr.p->nextSerialQue = operationRecPtr.p->nextSerialQue;
+ trlTmpOperPtr.i = trlOperPtr.p->nextSerialQue;
+ ptrCheckGuard(trlTmpOperPtr, coprecsize, operationrec);
+ trlTmpOperPtr.p->prevSerialQue = trlOperPtr.i;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* SINCE THERE ARE STILL ITEMS IN THE PARALLEL QUEUE WE NEED NOT WORRY ABOUT */
+ /* STARTING QUEUED OPERATIONS. THUS WE CAN END HERE. */
+ /* --------------------------------------------------------------------------------- */
+ } else {
+ ndbrequire(operationRecPtr.p->nextSerialQue != RNIL);
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE PARALLEL QUEUE IS EMPTY AND THE SERIAL QUEUE IS NOT EMPTY. WE NEED TO */
+ /* REARRANGE LISTS AND START A NUMBER OF OPERATIONS. */
+ /* --------------------------------------------------------------------------------- */
+ trlOperPtr.i = operationRecPtr.p->nextSerialQue;
+ ptrCheckGuard(trlOperPtr, coprecsize, operationrec);
+ copyOperPtr = operationRecPtr;
+ copyInOperPtr = trlOperPtr;
+ copyOpInfo(signal);
+ trlOperPtr.p->prevSerialQue = RNIL;
+ ndbrequire(trlOperPtr.p->prevParallelQue == RNIL);
+ /* --------------------------------------------------------------------------------- */
+ /* WE HAVE MOVED TO THE NEXT PARALLEL QUEUE. WE MUST START ALL OF THOSE */
+ /* OPERATIONS WHICH UP TILL NOW HAVE BEEN QUEUED WAITING FOR THE LOCK. */
+ /* --------------------------------------------------------------------------------- */
+ rloOperPtr = operationRecPtr;
+ trlTmpOperPtr = trlOperPtr;
+ TelementIsDisappeared = trlOperPtr.p->elementIsDisappeared;
+ Uint32 ThashValue = trlOperPtr.p->hashValue;
+ do {
+ /* --------------------------------------------------------------------------------- */
+ // Ensure that all operations in the queue are assigned with the elementIsDisappeared
+ // to ensure that the element is removed after a previous delete. An insert does
+ // however revert this decision since the element is put back again. Local checkpoints
+ // complicate life here since they do not execute the next operation but simply change
+ // the state on the operation. We need to set-up the variable elementIsDisappeared
+ // properly even when local checkpoints and inserts/writes after deletes occur.
+ /* --------------------------------------------------------------------------------- */
+ trlTmpOperPtr.p->elementIsDisappeared = TelementIsDisappeared;
+ if (TelementIsDisappeared == ZTRUE) {
+ /* --------------------------------------------------------------------------------- */
+ // If the elementIsDisappeared is set then we know that the hashValue is also set
+ // since it always originates from a committing abort or a aborting insert. Scans
+ // do not initialise the hashValue and must have this value initialised if they are
+ // to successfully commit the delete.
+ /* --------------------------------------------------------------------------------- */
+ jam();
+ trlTmpOperPtr.p->hashValue = ThashValue;
+ }//if
+ trlTmpOperPtr.p->localdata[0] = trlOperPtr.p->localdata[0];
+ trlTmpOperPtr.p->localdata[1] = trlOperPtr.p->localdata[1];
+ /* --------------------------------------------------------------------------------- */
+ // Restart the queued operation.
+ /* --------------------------------------------------------------------------------- */
+ operationRecPtr = trlTmpOperPtr;
+ TelementIsDisappeared = executeNextOperation(signal);
+ ThashValue = operationRecPtr.p->hashValue;
+ if (trlTmpOperPtr.p->nextParallelQue != RNIL) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // We will continue with the next operation in the parallel queue and start this as
+ // well.
+ /* --------------------------------------------------------------------------------- */
+ trlTmpOperPtr.i = trlTmpOperPtr.p->nextParallelQue;
+ ptrCheckGuard(trlTmpOperPtr, coprecsize, operationrec);
+ } else {
+ jam();
+ break;
+ }//if
+ } while (1);
+ operationRecPtr = rloOperPtr;
+ }//if
+
+ // Insert the next op into the lock owner list
+ insertLockOwnersList(signal, trlOperPtr);
+ return;
+}//Dbacc::releaselock()
+
+/* --------------------------------------------------------------------------------- */
+/* COPY_OP_INFO */
+/* INPUT: COPY_IN_OPER_PTR AND COPY_OPER_PTR. */
+/* DESCRIPTION:INFORMATION ABOUT THE ELEMENT WILL BE MOVED FROM OPERATION */
+/* REC TO QUEUE OP REC. QUE OP REC TAKES OVER THE LOCK. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::copyOpInfo(Signal* signal)
+{
+ Page8Ptr coiPageidptr;
+
+ copyInOperPtr.p->elementPage = copyOperPtr.p->elementPage;
+ copyInOperPtr.p->elementIsforward = copyOperPtr.p->elementIsforward;
+ copyInOperPtr.p->elementContainer = copyOperPtr.p->elementContainer;
+ copyInOperPtr.p->elementPointer = copyOperPtr.p->elementPointer;
+ copyInOperPtr.p->scanBits = copyOperPtr.p->scanBits;
+ copyInOperPtr.p->hashvaluePart = copyOperPtr.p->hashvaluePart;
+ copyInOperPtr.p->elementIsDisappeared = copyOperPtr.p->elementIsDisappeared;
+ if (copyInOperPtr.p->elementIsDisappeared == ZTRUE) {
+ /* --------------------------------------------------------------------------------- */
+ // If the elementIsDisappeared is set then we know that the hashValue is also set
+ // since it always originates from a committing abort or a aborting insert. Scans
+ // do not initialise the hashValue and must have this value initialised if they are
+ // to successfully commit the delete.
+ /* --------------------------------------------------------------------------------- */
+ jam();
+ copyInOperPtr.p->hashValue = copyOperPtr.p->hashValue;
+ }//if
+ coiPageidptr.i = copyOperPtr.p->elementPage;
+ ptrCheckGuard(coiPageidptr, cpagesize, page8);
+ const Uint32 tmp = ElementHeader::setLocked(copyInOperPtr.i);
+ dbgWord32(coiPageidptr, copyOperPtr.p->elementPointer, tmp);
+ arrGuard(copyOperPtr.p->elementPointer, 2048);
+ coiPageidptr.p->word32[copyOperPtr.p->elementPointer] = tmp;
+ copyInOperPtr.p->localdata[0] = copyOperPtr.p->localdata[0];
+ copyInOperPtr.p->localdata[1] = copyOperPtr.p->localdata[1];
+}//Dbacc::copyOpInfo()
+
+/* ******************--------------------------------------------------------------- */
+/* EXECUTE NEXT OPERATION */
+/* NEXT OPERATION IN A LOCK QUEUE WILL BE EXECUTED. */
+/* --------------------------------------------------------------------------------- */
+Uint32 Dbacc::executeNextOperation(Signal* signal)
+{
+ ndbrequire(operationRecPtr.p->transactionstate == ACTIVE);
+ if (fragrecptr.p->stopQueOp == ZTRUE) {
+ Uint32 TelemDisappeared;
+ jam();
+ TelemDisappeared = operationRecPtr.p->elementIsDisappeared;
+ if ((operationRecPtr.p->elementIsDisappeared == ZTRUE) &&
+ (operationRecPtr.p->prevParallelQue == RNIL) &&
+ ((operationRecPtr.p->operation == ZINSERT) ||
+ (operationRecPtr.p->operation == ZWRITE))) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // In this case we do not wish to change the elementIsDisappeared since that would
+ // create an error the next time this method is called for this operation after local
+ // checkpoint starts up operations again. We must however ensure that operations
+ // that follow in the queue do not get the value ZTRUE when actually an INSERT/WRITE
+ // precedes them (only if the INSERT/WRITE is the first operation).
+ /* --------------------------------------------------------------------------------- */
+ TelemDisappeared = ZFALSE;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* A LOCAL CHECKPOINT HAS STOPPED OPERATIONS. WE MUST NOT START THE OPERATION */
+ /* AT THIS TIME. WE SET THE STATE TO INDICATE THAT WE ARE READY TO START AS */
+ /* SOON AS WE ARE ALLOWED. */
+ /* --------------------------------------------------------------------------------- */
+ operationRecPtr.p->opState = WAIT_EXE_OP;
+ return TelemDisappeared;
+ }//if
+ takeOutFragWaitQue(signal);
+ if (operationRecPtr.p->elementIsDisappeared == ZTRUE) {
+ /* --------------------------------------------------------------------------------- */
+ /* PREVIOUS OPERATION WAS DELETE OPERATION AND THE ELEMENT IS ALREADY DELETED. */
+ /* --------------------------------------------------------------------------------- */
+ if (((operationRecPtr.p->operation != ZINSERT) &&
+ (operationRecPtr.p->operation != ZWRITE)) ||
+ (operationRecPtr.p->prevParallelQue != RNIL)) {
+ if (operationRecPtr.p->operation != ZSCAN_OP ||
+ operationRecPtr.p->isAccLockReq) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // Updates and reads with a previous delete simply aborts with read error indicating
+ // that tuple did not exist. Also inserts and writes not being the first operation.
+ /* --------------------------------------------------------------------------------- */
+ operationRecPtr.p->transactionstate = WAIT_COMMIT_ABORT;
+ signal->theData[0] = operationRecPtr.p->userptr;
+ signal->theData[1] = ZREAD_ERROR;
+ sendSignal(operationRecPtr.p->userblockref, GSN_ACCKEYREF, signal, 2, JBB);
+ return operationRecPtr.p->elementIsDisappeared;
+ } else {
+ /* --------------------------------------------------------------------------------- */
+ /* ABORT OF OPERATION NEEDED BUT THE OPERATION IS A SCAN => SPECIAL TREATMENT. */
+ /* IF THE SCAN WAITS IN QUEUE THEN WE MUST REMOVE THE OPERATION FROM THE SCAN */
+ /* LOCK QUEUE AND IF NO MORE OPERATIONS ARE QUEUED THEN WE SHOULD RESTART THE */
+ /* SCAN PROCESS. OTHERWISE WE SIMPLY RELEASE THE OPERATION AND DECREASE THE */
+ /* NUMBER OF LOCKS HELD. */
+ /* --------------------------------------------------------------------------------- */
+ takeOutScanLockQueue(operationRecPtr.p->scanRecPtr);
+ putReadyScanQueue(signal, operationRecPtr.p->scanRecPtr);
+ return operationRecPtr.p->elementIsDisappeared;
+ }//if
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ // Insert and writes can continue but need to be converted to inserts.
+ /* --------------------------------------------------------------------------------- */
+ jam();
+ operationRecPtr.p->elementIsDisappeared = ZFALSE;
+ operationRecPtr.p->operation = ZINSERT;
+ operationRecPtr.p->insertIsDone = ZTRUE;
+ } else if (operationRecPtr.p->operation == ZINSERT) {
+ bool abortFlag = true;
+ if (operationRecPtr.p->prevParallelQue != RNIL) {
+ OperationrecPtr prevOpPtr;
+ jam();
+ prevOpPtr.i = operationRecPtr.p->prevParallelQue;
+ ptrCheckGuard(prevOpPtr, coprecsize, operationrec);
+ if (prevOpPtr.p->operation == ZDELETE) {
+ jam();
+ abortFlag = false;
+ }//if
+ }//if
+ if (abortFlag) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* ELEMENT STILL REMAINS AND WE ARE TRYING TO INSERT IT AGAIN. THIS IS CLEARLY */
+ /* NOT A GOOD IDEA. */
+ /* --------------------------------------------------------------------------------- */
+ operationRecPtr.p->transactionstate = WAIT_COMMIT_ABORT;
+ signal->theData[0] = operationRecPtr.p->userptr;
+ signal->theData[1] = ZWRITE_ERROR;
+ sendSignal(operationRecPtr.p->userblockref, GSN_ACCKEYREF, signal, 2, JBB);
+ return operationRecPtr.p->elementIsDisappeared;
+ }//if
+ }//if
+ if (operationRecPtr.p->operation == ZSCAN_OP &&
+ ! operationRecPtr.p->isAccLockReq) {
+ jam();
+ takeOutScanLockQueue(operationRecPtr.p->scanRecPtr);
+ putReadyScanQueue(signal, operationRecPtr.p->scanRecPtr);
+ } else {
+ jam();
+ sendAcckeyconf(signal);
+ sendSignal(operationRecPtr.p->userblockref, GSN_ACCKEYCONF, signal, 6, JBB);
+ }//if
+ return operationRecPtr.p->elementIsDisappeared;
+}//Dbacc::executeNextOperation()
+
+/* --------------------------------------------------------------------------------- */
+/* TAKE_OUT_FRAG_WAIT_QUE */
+/* DESCRIPTION: AN OPERATION WHICH OWNS A LOCK OF AN ELEMENT, IS IN A LIST */
+/* OF THE FRAGMENT. THIS LIST IS USED TO STOP THE QUEUE OPERATION */
+/* DURING CREATE CHECK POINT PROSESS FOR STOP AND RESTART OF THE */
+/* OPERATIONS. THIS SUBRUTIN TAKES A OPERATION RECORD OUT OF THE LIST */
+/* -------------------------------------------------------------------------------- */
+void Dbacc::takeOutFragWaitQue(Signal* signal)
+{
+ OperationrecPtr tofwqOperRecPtr;
+
+ if (operationRecPtr.p->opState == WAIT_IN_QUEUE) {
+ if (fragrecptr.p->sentWaitInQueOp == operationRecPtr.i) {
+ jam();
+ fragrecptr.p->sentWaitInQueOp = operationRecPtr.p->nextQueOp;
+ }//if
+ if (operationRecPtr.p->prevQueOp != RNIL) {
+ jam();
+ tofwqOperRecPtr.i = operationRecPtr.p->prevQueOp;
+ ptrCheckGuard(tofwqOperRecPtr, coprecsize, operationrec);
+ tofwqOperRecPtr.p->nextQueOp = operationRecPtr.p->nextQueOp;
+ } else {
+ jam();
+ fragrecptr.p->firstWaitInQueOp = operationRecPtr.p->nextQueOp;
+ }//if
+ if (operationRecPtr.p->nextQueOp != RNIL) {
+ jam();
+ tofwqOperRecPtr.i = operationRecPtr.p->nextQueOp;
+ ptrCheckGuard(tofwqOperRecPtr, coprecsize, operationrec);
+ tofwqOperRecPtr.p->prevQueOp = operationRecPtr.p->prevQueOp;
+ } else {
+ jam();
+ fragrecptr.p->lastWaitInQueOp = operationRecPtr.p->prevQueOp;
+ }//if
+ operationRecPtr.p->opState = FREE_OP;
+ return;
+ } else {
+ ndbrequire(operationRecPtr.p->opState == FREE_OP);
+ }//if
+}//Dbacc::takeOutFragWaitQue()
+
+/**
+ * takeOutLockOwnersList
+ *
+ * Description: Take out an operation from the doubly linked
+ * lock owners list on the fragment.
+ *
+ */
+void Dbacc::takeOutLockOwnersList(Signal* signal,
+ const OperationrecPtr& outOperPtr)
+{
+ const Uint32 Tprev = outOperPtr.p->prevLockOwnerOp;
+ const Uint32 Tnext = outOperPtr.p->nextLockOwnerOp;
+
+#ifdef VM_TRACE
+ // Check that operation is already in the list
+ OperationrecPtr tmpOperPtr;
+ bool inList = false;
+ tmpOperPtr.i = fragrecptr.p->lockOwnersList;
+ while (tmpOperPtr.i != RNIL){
+ ptrCheckGuard(tmpOperPtr, coprecsize, operationrec);
+ if (tmpOperPtr.i == outOperPtr.i)
+ inList = true;
+ tmpOperPtr.i = tmpOperPtr.p->nextLockOwnerOp;
+ }
+ ndbrequire(inList == true);
+#endif
+
+ ndbrequire(outOperPtr.p->lockOwner == ZTRUE);
+ outOperPtr.p->lockOwner = ZFALSE;
+
+ // Fast path through the code for the common case.
+ if ((Tprev == RNIL) && (Tnext == RNIL)) {
+ ndbrequire(fragrecptr.p->lockOwnersList == outOperPtr.i);
+ fragrecptr.p->lockOwnersList = RNIL;
+ return;
+ }
+
+ // Check previous operation
+ if (Tprev != RNIL) {
+ jam();
+ arrGuard(Tprev, coprecsize);
+ operationrec[Tprev].nextLockOwnerOp = Tnext;
+ } else {
+ fragrecptr.p->lockOwnersList = Tnext;
+ }//if
+
+ // Check next operation
+ if (Tnext == RNIL) {
+ return;
+ } else {
+ jam();
+ arrGuard(Tnext, coprecsize);
+ operationrec[Tnext].prevLockOwnerOp = Tprev;
+ }//if
+
+ return;
+}//Dbacc::takeOutLockOwnersList()
+
+/**
+ * insertLockOwnersList
+ *
+ * Description: Insert an operation first in the dubly linked lock owners
+ * list on the fragment.
+ *
+ */
+void Dbacc::insertLockOwnersList(Signal* signal,
+ const OperationrecPtr& insOperPtr)
+{
+ OperationrecPtr tmpOperPtr;
+
+#ifdef VM_TRACE
+ // Check that operation is not already in list
+ tmpOperPtr.i = fragrecptr.p->lockOwnersList;
+ while(tmpOperPtr.i != RNIL){
+ ptrCheckGuard(tmpOperPtr, coprecsize, operationrec);
+ ndbrequire(tmpOperPtr.i != insOperPtr.i);
+ tmpOperPtr.i = tmpOperPtr.p->nextLockOwnerOp;
+ }
+#endif
+
+ ndbrequire(insOperPtr.p->lockOwner == ZFALSE);
+
+ insOperPtr.p->lockOwner = ZTRUE;
+ insOperPtr.p->prevLockOwnerOp = RNIL;
+ tmpOperPtr.i = fragrecptr.p->lockOwnersList;
+ fragrecptr.p->lockOwnersList = insOperPtr.i;
+ insOperPtr.p->nextLockOwnerOp = tmpOperPtr.i;
+ if (tmpOperPtr.i == RNIL) {
+ return;
+ } else {
+ jam();
+ ptrCheckGuard(tmpOperPtr, coprecsize, operationrec);
+ tmpOperPtr.p->prevLockOwnerOp = insOperPtr.i;
+ }//if
+}//Dbacc::insertLockOwnersList()
+
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF COMMIT AND ABORT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* ALLOC_OVERFLOW_PAGE */
+/* DESCRIPTION: */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::allocOverflowPage(Signal* signal)
+{
+ DirRangePtr aopDirRangePtr;
+ DirectoryarrayPtr aopOverflowDirptr;
+ OverflowRecordPtr aopOverflowRecPtr;
+ Uint32 taopTmp1;
+ Uint32 taopTmp2;
+ Uint32 taopTmp3;
+
+ tresult = 0;
+ if ((cfirstfreepage == RNIL) &&
+ (cfreepage >= cpagesize)) {
+ jam();
+ zpagesize_error("Dbacc::allocOverflowPage");
+ tresult = ZPAGESIZE_ERROR;
+ return;
+ }//if
+ if (fragrecptr.p->firstFreeDirindexRec != RNIL) {
+ jam();
+ /* FRAGRECPTR:FIRST_FREE_DIRINDEX_REC POINTS */
+ /* TO THE FIRST ELEMENT IN A FREE LIST OF THE */
+ /* DIRECTORY INDEX WICH HAVE NULL AS PAGE */
+ aopOverflowRecPtr.i = fragrecptr.p->firstFreeDirindexRec;
+ ptrCheckGuard(aopOverflowRecPtr, coverflowrecsize, overflowRecord);
+ troOverflowRecPtr.p = aopOverflowRecPtr.p;
+ takeRecOutOfFreeOverdir(signal);
+ } else if (cfirstfreeoverrec == RNIL) {
+ jam();
+ tresult = ZOVER_REC_ERROR;
+ return;
+ } else if ((cfirstfreedir == RNIL) &&
+ (cdirarraysize <= cdirmemory)) {
+ jam();
+ tresult = ZDIRSIZE_ERROR;
+ return;
+ } else {
+ jam();
+ seizeOverRec(signal);
+ aopOverflowRecPtr = sorOverflowRecPtr;
+ aopOverflowRecPtr.p->dirindex = fragrecptr.p->lastOverIndex;
+ }//if
+ aopOverflowRecPtr.p->nextOverRec = RNIL;
+ aopOverflowRecPtr.p->prevOverRec = RNIL;
+ fragrecptr.p->firstOverflowRec = aopOverflowRecPtr.i;
+ fragrecptr.p->lastOverflowRec = aopOverflowRecPtr.i;
+ taopTmp1 = aopOverflowRecPtr.p->dirindex;
+ aopDirRangePtr.i = fragrecptr.p->overflowdir;
+ taopTmp2 = taopTmp1 >> 8;
+ taopTmp3 = taopTmp1 & 0xff;
+ ptrCheckGuard(aopDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(taopTmp2, 256);
+ if (aopDirRangePtr.p->dirArray[taopTmp2] == RNIL) {
+ jam();
+ seizeDirectory(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ aopDirRangePtr.p->dirArray[taopTmp2] = sdDirptr.i;
+ }//if
+ aopOverflowDirptr.i = aopDirRangePtr.p->dirArray[taopTmp2];
+ seizePage(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ ptrCheckGuard(aopOverflowDirptr, cdirarraysize, directoryarray);
+ aopOverflowDirptr.p->pagep[taopTmp3] = spPageptr.i;
+ tiopPageId = aopOverflowRecPtr.p->dirindex;
+ iopOverflowRecPtr = aopOverflowRecPtr;
+ iopPageptr = spPageptr;
+ initOverpage(signal);
+ aopOverflowRecPtr.p->overpage = spPageptr.i;
+ if (fragrecptr.p->lastOverIndex <= aopOverflowRecPtr.p->dirindex) {
+ jam();
+ ndbrequire(fragrecptr.p->lastOverIndex == aopOverflowRecPtr.p->dirindex);
+ fragrecptr.p->lastOverIndex++;
+ }//if
+}//Dbacc::allocOverflowPage()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* EXPAND/SHRINK MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/*EXPANDCHECK EXPAND BUCKET ORD */
+/* SENDER: ACC, LEVEL B */
+/* INPUT: FRAGRECPTR, POINTS TO A FRAGMENT RECORD. */
+/* DESCRIPTION: A BUCKET OF A FRAGMENT PAGE WILL BE EXPAND INTO TWO BUCKETS */
+/* ACCORDING TO LH3. */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* EXPANDCHECK EXPAND BUCKET ORD */
+/* ******************------------------------------+ */
+/* SENDER: ACC, LEVEL B */
+/* A BUCKET OF THE FRAGMENT WILL */
+/* BE EXPANDED ACORDING TO LH3, */
+/* AND COMMIT TRANSACTION PROCESS */
+/* WILL BE CONTINUED */
+Uint32 Dbacc::checkScanExpand(Signal* signal)
+{
+ Uint32 Ti;
+ Uint32 TreturnCode = 0;
+ Uint32 TPageIndex;
+ Uint32 TDirInd;
+ Uint32 TSplit;
+ Uint32 TreleaseInd = 0;
+ Uint32 TreleaseScanBucket;
+ Uint32 TreleaseScanIndicator[4];
+ DirectoryarrayPtr TDirptr;
+ DirRangePtr TDirRangePtr;
+ Page8Ptr TPageptr;
+ ScanRecPtr TscanPtr;
+ RootfragmentrecPtr Trootfragrecptr;
+
+ Trootfragrecptr.i = fragrecptr.p->myroot;
+ TSplit = fragrecptr.p->p;
+ ptrCheckGuard(Trootfragrecptr, crootfragmentsize, rootfragmentrec);
+ for (Ti = 0; Ti < 4; Ti++) {
+ TreleaseScanIndicator[Ti] = 0;
+ if (Trootfragrecptr.p->scan[Ti] != RNIL) {
+ //-------------------------------------------------------------
+ // A scan is ongoing on this particular local fragment. We have
+ // to check its current state.
+ //-------------------------------------------------------------
+ TscanPtr.i = Trootfragrecptr.p->scan[Ti];
+ ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);
+ if (TscanPtr.p->activeLocalFrag == fragrecptr.i) {
+ if (TscanPtr.p->scanBucketState == ScanRec::FIRST_LAP) {
+ if (TSplit == TscanPtr.p->nextBucketIndex) {
+ jam();
+ //-------------------------------------------------------------
+ // We are currently scanning this bucket. We cannot split it
+ // simultaneously with the scan. We have to pass this offer for
+ // splitting the bucket.
+ //-------------------------------------------------------------
+ TreturnCode = 1;
+ return TreturnCode;
+ } else if (TSplit > TscanPtr.p->nextBucketIndex) {
+ jam();
+ //-------------------------------------------------------------
+ // This bucket has not yet been scanned. We must reset the scanned
+ // bit indicator for this scan on this bucket.
+ //-------------------------------------------------------------
+ TreleaseScanIndicator[Ti] = 1;
+ TreleaseInd = 1;
+ } else {
+ jam();
+ }//if
+ } else if (TscanPtr.p->scanBucketState == ScanRec::SECOND_LAP) {
+ jam();
+ //-------------------------------------------------------------
+ // We are performing a second lap to handle buckets that was
+ // merged during the first lap of scanning. During this second
+ // lap we do not allow any splits or merges.
+ //-------------------------------------------------------------
+ TreturnCode = 1;
+ return TreturnCode;
+ } else {
+ ndbrequire(TscanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED);
+ jam();
+ //-------------------------------------------------------------
+ // The scan is completed and we can thus go ahead and perform
+ // the split.
+ //-------------------------------------------------------------
+ }//if
+ }//if
+ }//if
+ }//for
+ if (TreleaseInd == 1) {
+ TreleaseScanBucket = TSplit;
+ TDirRangePtr.i = fragrecptr.p->directory;
+ TPageIndex = TreleaseScanBucket & ((1 << fragrecptr.p->k) - 1); /* PAGE INDEX OBS K = 6 */
+ TDirInd = TreleaseScanBucket >> fragrecptr.p->k; /* DIRECTORY INDEX OBS K = 6 */
+ ptrCheckGuard(TDirRangePtr, cdirrangesize, dirRange);
+ arrGuard((TDirInd >> 8), 256);
+ TDirptr.i = TDirRangePtr.p->dirArray[TDirInd >> 8];
+ ptrCheckGuard(TDirptr, cdirarraysize, directoryarray);
+ TPageptr.i = TDirptr.p->pagep[TDirInd & 0xff];
+ ptrCheckGuard(TPageptr, cpagesize, page8);
+ for (Ti = 0; Ti < 4; Ti++) {
+ if (TreleaseScanIndicator[Ti] == 1) {
+ jam();
+ scanPtr.i = Trootfragrecptr.p->scan[Ti];
+ ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
+ rsbPageidptr = TPageptr;
+ trsbPageindex = TPageIndex;
+ releaseScanBucket(signal);
+ }//if
+ }//for
+ }//if
+ return TreturnCode;
+}//Dbacc::checkScanExpand()
+
+void Dbacc::execEXPANDCHECK2(Signal* signal)
+{
+ jamEntry();
+
+ if(refToBlock(signal->getSendersBlockRef()) == DBLQH){
+ jam();
+ reenable_expand_after_redo_log_exection_complete(signal);
+ return;
+ }
+
+ DirectoryarrayPtr newDirptr;
+
+ fragrecptr.i = signal->theData[0];
+ tresult = 0; /* 0= FALSE,1= TRUE,> ZLIMIT_OF_ERROR =ERRORCODE */
+ Uint32 tmp = 1;
+ tmp = tmp << 31;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ fragrecptr.p->expandFlag = 0;
+ if (fragrecptr.p->slack < tmp) {
+ jam();
+ /* IT MEANS THAT IF SLACK > ZERO */
+ /*--------------------------------------------------------------*/
+ /* THE SLACK HAS IMPROVED AND IS NOW ACCEPTABLE AND WE */
+ /* CAN FORGET ABOUT THE EXPAND PROCESS. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ if (fragrecptr.p->firstOverflowRec == RNIL) {
+ jam();
+ allocOverflowPage(signal);
+ if (tresult > ZLIMIT_OF_ERROR) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* WE COULD NOT ALLOCATE ANY OVERFLOW PAGE. THUS WE HAVE TO STOP*/
+ /* THE EXPAND SINCE WE CANNOT GUARANTEE ITS COMPLETION. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+ if (cfirstfreepage == RNIL) {
+ if (cfreepage >= cpagesize) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* WE HAVE TO STOP THE EXPAND PROCESS SINCE THERE ARE NO FREE */
+ /* PAGES. THIS MEANS THAT WE COULD BE FORCED TO CRASH SINCE WE */
+ /* CANNOT COMPLETE THE EXPAND. TO AVOID THE CRASH WE EXIT HERE. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+ if (checkScanExpand(signal) == 1) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // A scan state was inconsistent with performing an expand
+ // operation.
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_EXPAND) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // We did not have enough undo log buffers to start up an
+ // expand operation
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+
+ /*--------------------------------------------------------------------------*/
+ /* WE START BY FINDING THE PAGE, THE PAGE INDEX AND THE PAGE DIRECTORY*/
+ /* OF THE NEW BUCKET WHICH SHALL RECEIVE THE ELEMENT WHICH HAVE A 1 IN*/
+ /* THE NEXT HASH BIT. THIS BIT IS USED IN THE SPLIT MECHANISM TO */
+ /* DECIDE WHICH ELEMENT GOES WHERE. */
+ /*--------------------------------------------------------------------------*/
+ expDirRangePtr.i = fragrecptr.p->directory;
+ texpReceivedBucket = (fragrecptr.p->maxp + fragrecptr.p->p) + 1; /* RECEIVED BUCKET */
+ texpDirInd = texpReceivedBucket >> fragrecptr.p->k;
+ newDirptr.i = RNIL;
+ ptrNull(newDirptr);
+ texpDirRangeIndex = texpDirInd >> 8;
+ ptrCheckGuard(expDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(texpDirRangeIndex, 256);
+ expDirptr.i = expDirRangePtr.p->dirArray[texpDirRangeIndex];
+ if (expDirptr.i == RNIL) {
+ jam();
+ seizeDirectory(signal);
+ if (tresult > ZLIMIT_OF_ERROR) {
+ jam();
+ return;
+ } else {
+ jam();
+ newDirptr = sdDirptr;
+ expDirptr = sdDirptr;
+ expDirRangePtr.p->dirArray[texpDirRangeIndex] = sdDirptr.i;
+ }//if
+ } else {
+ ptrCheckGuard(expDirptr, cdirarraysize, directoryarray);
+ }//if
+ texpDirPageIndex = texpDirInd & 0xff;
+ expPageptr.i = expDirptr.p->pagep[texpDirPageIndex];
+ if (expPageptr.i == RNIL) {
+ jam();
+ seizePage(signal);
+ if (tresult > ZLIMIT_OF_ERROR) {
+ jam();
+ if (newDirptr.i != RNIL) {
+ jam();
+ rdDirptr.i = newDirptr.i;
+ releaseDirectory(signal);
+ }//if
+ return;
+ }//if
+ expDirptr.p->pagep[texpDirPageIndex] = spPageptr.i;
+ tipPageId = texpDirInd;
+ inpPageptr = spPageptr;
+ initPage(signal);
+ fragrecptr.p->dirsize++;
+ expPageptr = spPageptr;
+ } else {
+ ptrCheckGuard(expPageptr, cpagesize, page8);
+ }//if
+
+ fragrecptr.p->expReceivePageptr = expPageptr.i;
+ fragrecptr.p->expReceiveIndex = texpReceivedBucket & ((1 << fragrecptr.p->k) - 1);
+ /*--------------------------------------------------------------------------*/
+ /* THE NEXT ACTION IS TO FIND THE PAGE, THE PAGE INDEX AND THE PAGE */
+ /* DIRECTORY OF THE BUCKET TO BE SPLIT. */
+ /*--------------------------------------------------------------------------*/
+ expDirRangePtr.i = fragrecptr.p->directory;
+ cexcPageindex = fragrecptr.p->p & ((1 << fragrecptr.p->k) - 1); /* PAGE INDEX OBS K = 6 */
+ texpDirInd = fragrecptr.p->p >> fragrecptr.p->k; /* DIRECTORY INDEX OBS K = 6 */
+ ptrCheckGuard(expDirRangePtr, cdirrangesize, dirRange);
+ arrGuard((texpDirInd >> 8), 256);
+ expDirptr.i = expDirRangePtr.p->dirArray[texpDirInd >> 8];
+ ptrCheckGuard(expDirptr, cdirarraysize, directoryarray);
+ excPageptr.i = expDirptr.p->pagep[texpDirInd & 0xff];
+ fragrecptr.p->expSenderIndex = cexcPageindex;
+ fragrecptr.p->expSenderPageptr = excPageptr.i;
+ if (excPageptr.i == RNIL) {
+ jam();
+ endofexpLab(signal); /* EMPTY BUCKET */
+ return;
+ }//if
+ fragrecptr.p->expReceiveForward = ZTRUE;
+ ptrCheckGuard(excPageptr, cpagesize, page8);
+ expandcontainer(signal);
+ endofexpLab(signal);
+ return;
+}//Dbacc::execEXPANDCHECK2()
+
+void Dbacc::endofexpLab(Signal* signal)
+{
+ fragrecptr.p->p++;
+ fragrecptr.p->slack += fragrecptr.p->maxloadfactor;
+ fragrecptr.p->expandCounter++;
+ if (fragrecptr.p->p > fragrecptr.p->maxp) {
+ jam();
+ fragrecptr.p->maxp = (fragrecptr.p->maxp << 1) | 1;
+ fragrecptr.p->lhdirbits++;
+ fragrecptr.p->hashcheckbit++;
+ fragrecptr.p->p = 0;
+ }//if
+ Uint32 noOfBuckets = (fragrecptr.p->maxp + 1) + fragrecptr.p->p;
+ Uint32 Thysteres = fragrecptr.p->maxloadfactor - fragrecptr.p->minloadfactor;
+ fragrecptr.p->slackCheck = noOfBuckets * Thysteres;
+ if (fragrecptr.p->slack > (1u << 31)) {
+ jam();
+ /* IT MEANS THAT IF SLACK < ZERO */
+ /* --------------------------------------------------------------------------------- */
+ /* IT IS STILL NECESSARY TO EXPAND THE FRAGMENT EVEN MORE. START IT FROM HERE */
+ /* WITHOUT WAITING FOR NEXT COMMIT ON THE FRAGMENT. */
+ /* --------------------------------------------------------------------------------- */
+ fragrecptr.p->expandFlag = 2;
+ signal->theData[0] = fragrecptr.i;
+ signal->theData[1] = fragrecptr.p->p;
+ signal->theData[2] = fragrecptr.p->maxp;
+ sendSignal(cownBlockref, GSN_EXPANDCHECK2, signal, 3, JBB);
+ }//if
+ return;
+}//Dbacc::endofexpLab()
+
+void Dbacc::reenable_expand_after_redo_log_exection_complete(Signal* signal){
+
+ tabptr.i = signal->theData[0];
+ Uint32 fragId = signal->theData[1];
+
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+ ndbrequire(getrootfragmentrec(signal, rootfragrecptr, fragId));
+#if 0
+ ndbout_c("reenable expand check for table %d fragment: %d",
+ tabptr.i, fragId);
+#endif
+
+ for (Uint32 i = 0; i < 2; i++) {
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[i];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ switch(fragrecptr.p->expandFlag){
+ case 0:
+ /**
+ * Hmm... this means that it's alreay has been reenabled...
+ */
+ ndbassert(false);
+ continue;
+ case 1:
+ /**
+ * Nothing is going on start expand check
+ */
+ case 2:
+ /**
+ * A shrink is running, do expand check anyway
+ * (to reset expandFlag)
+ */
+ fragrecptr.p->expandFlag = 2;
+ signal->theData[0] = fragrecptr.i;
+ signal->theData[1] = fragrecptr.p->p;
+ signal->theData[2] = fragrecptr.p->maxp;
+ sendSignal(cownBlockref, GSN_EXPANDCHECK2, signal, 3, JBB);
+ break;
+ }
+ }
+}
+
+void Dbacc::execDEBUG_SIG(Signal* signal)
+{
+ jamEntry();
+ expPageptr.i = signal->theData[0];
+
+ progError(__LINE__,
+ ERR_SR_UNDOLOG);
+ return;
+}//Dbacc::execDEBUG_SIG()
+
+/* --------------------------------------------------------------------------------- */
+/* EXPANDCONTAINER */
+/* INPUT: EXC_PAGEPTR (POINTER TO THE ACTIVE PAGE RECORD) */
+/* CEXC_PAGEINDEX (INDEX OF THE BUCKET). */
+/* */
+/* DESCRIPTION: THE HASH VALUE OF ALL ELEMENTS IN THE CONTAINER WILL BE */
+/* CHECKED. SOME OF THIS ELEMENTS HAVE TO MOVE TO THE NEW CONTAINER */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::expandcontainer(Signal* signal)
+{
+ Uint32 texcHashvalue;
+ Uint32 texcTmp;
+ Uint32 texcIndex;
+ Uint32 guard20;
+
+ cexcPrevpageptr = RNIL;
+ cexcPrevconptr = 0;
+ cexcForward = ZTRUE;
+ EXP_CONTAINER_LOOP:
+ cexcContainerptr = (cexcPageindex << ZSHIFT_PLUS) - (cexcPageindex << ZSHIFT_MINUS);
+ if (cexcForward == ZTRUE) {
+ jam();
+ cexcContainerptr = cexcContainerptr + ZHEAD_SIZE;
+ cexcElementptr = cexcContainerptr + ZCON_HEAD_SIZE;
+ } else {
+ jam();
+ cexcContainerptr = ((cexcContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - ZCON_HEAD_SIZE;
+ cexcElementptr = cexcContainerptr - 1;
+ }//if
+ arrGuard(cexcContainerptr, 2048);
+ cexcContainerhead = excPageptr.p->word32[cexcContainerptr];
+ cexcContainerlen = cexcContainerhead >> 26;
+ cexcMovedLen = ZCON_HEAD_SIZE;
+ if (cexcContainerlen <= ZCON_HEAD_SIZE) {
+ ndbrequire(cexcContainerlen >= ZCON_HEAD_SIZE);
+ jam();
+ goto NEXT_ELEMENT;
+ }//if
+ NEXT_ELEMENT_LOOP:
+ idrOperationRecPtr.i = RNIL;
+ ptrNull(idrOperationRecPtr);
+ /* --------------------------------------------------------------------------------- */
+ /* CEXC_PAGEINDEX PAGE INDEX OF CURRENT CONTAINER BEING EXAMINED. */
+ /* CEXC_CONTAINERPTR INDEX OF CURRENT CONTAINER BEING EXAMINED. */
+ /* CEXC_ELEMENTPTR INDEX OF CURRENT ELEMENT BEING EXAMINED. */
+ /* EXC_PAGEPTR PAGE WHERE CURRENT ELEMENT RESIDES. */
+ /* CEXC_PREVPAGEPTR PAGE OF PREVIOUS CONTAINER. */
+ /* CEXC_PREVCONPTR INDEX OF PREVIOUS CONTAINER */
+ /* CEXC_FORWARD DIRECTION OF CURRENT CONTAINER */
+ /* --------------------------------------------------------------------------------- */
+ arrGuard(cexcElementptr, 2048);
+ tidrElemhead = excPageptr.p->word32[cexcElementptr];
+ if (ElementHeader::getUnlocked(tidrElemhead)){
+ jam();
+ texcHashvalue = ElementHeader::getHashValuePart(tidrElemhead);
+ } else {
+ jam();
+ idrOperationRecPtr.i = ElementHeader::getOpPtrI(tidrElemhead);
+ ptrCheckGuard(idrOperationRecPtr, coprecsize, operationrec);
+ texcHashvalue = idrOperationRecPtr.p->hashvaluePart;
+ if ((fragrecptr.p->createLcp == ZTRUE) &&
+ (((texcHashvalue >> fragrecptr.p->hashcheckbit) & 1) != 0)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // During local checkpoints we must ensure that we restore the element header in
+ // unlocked state and with the hash value part there with tuple status zeroed.
+ // Otherwise a later insert over the same element will write an UNDO log that will
+ // ensure that the now removed element is restored together with its locked element
+ // header and without the hash value part.
+ /* --------------------------------------------------------------------------------- */
+ const Uint32 hv = idrOperationRecPtr.p->hashvaluePart;
+ const Uint32 eh = ElementHeader::setUnlocked(hv, 0);
+ excPageptr.p->word32[cexcElementptr] = eh;
+ }//if
+ }//if
+ if (((texcHashvalue >> fragrecptr.p->hashcheckbit) & 1) == 0) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THIS ELEMENT IS NOT TO BE MOVED. WE CALCULATE THE WHEREABOUTS OF THE NEXT */
+ /* ELEMENT AND PROCEED WITH THAT OR END THE SEARCH IF THERE ARE NO MORE */
+ /* ELEMENTS IN THIS CONTAINER. */
+ /* --------------------------------------------------------------------------------- */
+ goto NEXT_ELEMENT;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* THE HASH BIT WAS SET AND WE SHALL MOVE THIS ELEMENT TO THE NEW BUCKET. */
+ /* WE START BY READING THE ELEMENT TO BE ABLE TO INSERT IT INTO THE NEW BUCKET.*/
+ /* THEN WE INSERT THE ELEMENT INTO THE NEW BUCKET. THE NEXT STEP IS TO DELETE */
+ /* THE ELEMENT FROM THIS BUCKET. THIS IS PERFORMED BY REPLACING IT WITH THE */
+ /* LAST ELEMENT IN THE BUCKET. IF THIS ELEMENT IS TO BE MOVED WE MOVE IT AND */
+ /* GET THE LAST ELEMENT AGAIN UNTIL WE EITHER FIND ONE THAT STAYS OR THIS */
+ /* ELEMENT IS THE LAST ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ texcTmp = cexcElementptr + cexcForward;
+ guard20 = fragrecptr.p->localkeylen - 1;
+ for (texcIndex = 0; texcIndex <= guard20; texcIndex++) {
+ arrGuard(texcIndex, 2);
+ arrGuard(texcTmp, 2048);
+ clocalkey[texcIndex] = excPageptr.p->word32[texcTmp];
+ texcTmp = texcTmp + cexcForward;
+ }//for
+ tidrPageindex = fragrecptr.p->expReceiveIndex;
+ idrPageptr.i = fragrecptr.p->expReceivePageptr;
+ ptrCheckGuard(idrPageptr, cpagesize, page8);
+ tidrForward = fragrecptr.p->expReceiveForward;
+ insertElement(signal);
+ fragrecptr.p->expReceiveIndex = tidrPageindex;
+ fragrecptr.p->expReceivePageptr = idrPageptr.i;
+ fragrecptr.p->expReceiveForward = tidrForward;
+ REMOVE_LAST_LOOP:
+ jam();
+ lastPageptr.i = excPageptr.i;
+ lastPageptr.p = excPageptr.p;
+ tlastContainerptr = cexcContainerptr;
+ lastPrevpageptr.i = cexcPrevpageptr;
+ ptrCheck(lastPrevpageptr, cpagesize, page8);
+ tlastPrevconptr = cexcPrevconptr;
+ arrGuard(tlastContainerptr, 2048);
+ tlastContainerhead = lastPageptr.p->word32[tlastContainerptr];
+ tlastContainerlen = tlastContainerhead >> 26;
+ tlastForward = cexcForward;
+ tlastPageindex = cexcPageindex;
+ getLastAndRemove(signal);
+ if (excPageptr.i == lastPageptr.i) {
+ if (cexcElementptr == tlastElementptr) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE CURRENT ELEMENT WAS ALSO THE LAST ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* THE CURRENT ELEMENT WAS NOT THE LAST ELEMENT. IF THE LAST ELEMENT SHOULD */
+ /* STAY WE COPY IT TO THE POSITION OF THE CURRENT ELEMENT, OTHERWISE WE INSERT */
+ /* INTO THE NEW BUCKET, REMOVE IT AND TRY WITH THE NEW LAST ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ idrOperationRecPtr.i = RNIL;
+ ptrNull(idrOperationRecPtr);
+ arrGuard(tlastElementptr, 2048);
+ tidrElemhead = lastPageptr.p->word32[tlastElementptr];
+ if (ElementHeader::getUnlocked(tidrElemhead)) {
+ jam();
+ texcHashvalue = ElementHeader::getHashValuePart(tidrElemhead);
+ } else {
+ jam();
+ idrOperationRecPtr.i = ElementHeader::getOpPtrI(tidrElemhead);
+ ptrCheckGuard(idrOperationRecPtr, coprecsize, operationrec);
+ texcHashvalue = idrOperationRecPtr.p->hashvaluePart;
+ if ((fragrecptr.p->createLcp == ZTRUE) &&
+ (((texcHashvalue >> fragrecptr.p->hashcheckbit) & 1) != 0)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // During local checkpoints we must ensure that we restore the element header in
+ // unlocked state and with the hash value part there with tuple status zeroed.
+ // Otherwise a later insert over the same element will write an UNDO log that will
+ // ensure that the now removed element is restored together with its locked element
+ // header and without the hash value part.
+ /* --------------------------------------------------------------------------------- */
+ const Uint32 hv = idrOperationRecPtr.p->hashvaluePart;
+ const Uint32 eh = ElementHeader::setUnlocked(hv, 0);
+ lastPageptr.p->word32[tlastElementptr] = eh;
+ }//if
+ }//if
+ if (((texcHashvalue >> fragrecptr.p->hashcheckbit) & 1) == 0) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE LAST ELEMENT IS NOT TO BE MOVED. WE COPY IT TO THE CURRENT ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ delPageptr = excPageptr;
+ tdelContainerptr = cexcContainerptr;
+ tdelForward = cexcForward;
+ tdelElementptr = cexcElementptr;
+ deleteElement(signal);
+ } else {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE LAST ELEMENT IS ALSO TO BE MOVED. */
+ /* --------------------------------------------------------------------------------- */
+ texcTmp = tlastElementptr + tlastForward;
+ for (texcIndex = 0; texcIndex < fragrecptr.p->localkeylen; texcIndex++) {
+ arrGuard(texcIndex, 2);
+ arrGuard(texcTmp, 2048);
+ clocalkey[texcIndex] = lastPageptr.p->word32[texcTmp];
+ texcTmp = texcTmp + tlastForward;
+ }//for
+ tidrPageindex = fragrecptr.p->expReceiveIndex;
+ idrPageptr.i = fragrecptr.p->expReceivePageptr;
+ ptrCheckGuard(idrPageptr, cpagesize, page8);
+ tidrForward = fragrecptr.p->expReceiveForward;
+ insertElement(signal);
+ fragrecptr.p->expReceiveIndex = tidrPageindex;
+ fragrecptr.p->expReceivePageptr = idrPageptr.i;
+ fragrecptr.p->expReceiveForward = tidrForward;
+ goto REMOVE_LAST_LOOP;
+ }//if
+ NEXT_ELEMENT:
+ arrGuard(cexcContainerptr, 2048);
+ cexcContainerhead = excPageptr.p->word32[cexcContainerptr];
+ cexcMovedLen = cexcMovedLen + fragrecptr.p->elementLength;
+ if ((cexcContainerhead >> 26) > cexcMovedLen) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE HAVE NOT YET MOVED THE COMPLETE CONTAINER. WE PROCEED WITH THE NEXT */
+ /* ELEMENT IN THE CONTAINER. IT IS IMPORTANT TO READ THE CONTAINER LENGTH */
+ /* FROM THE CONTAINER HEADER SINCE IT MIGHT CHANGE BY REMOVING THE LAST */
+ /* ELEMENT IN THE BUCKET. */
+ /* --------------------------------------------------------------------------------- */
+ cexcElementptr = cexcElementptr + (cexcForward * fragrecptr.p->elementLength);
+ goto NEXT_ELEMENT_LOOP;
+ }//if
+ if (((cexcContainerhead >> 7) & 3) != 0) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WE PROCEED TO THE NEXT CONTAINER IN THE BUCKET. */
+ /* --------------------------------------------------------------------------------- */
+ cexcPrevpageptr = excPageptr.i;
+ cexcPrevconptr = cexcContainerptr;
+ nextcontainerinfoExp(signal);
+ goto EXP_CONTAINER_LOOP;
+ }//if
+}//Dbacc::expandcontainer()
+
+/* ******************--------------------------------------------------------------- */
+/* SHRINKCHECK JOIN BUCKET ORD */
+/* SENDER: ACC, LEVEL B */
+/* INPUT: FRAGRECPTR, POINTS TO A FRAGMENT RECORD. */
+/* DESCRIPTION: TWO BUCKET OF A FRAGMENT PAGE WILL BE JOINED TOGETHER */
+/* ACCORDING TO LH3. */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* SHRINKCHECK JOIN BUCKET ORD */
+/* ******************------------------------------+ */
+/* SENDER: ACC, LEVEL B */
+/* TWO BUCKETS OF THE FRAGMENT */
+/* WILL BE JOINED ACORDING TO LH3 */
+/* AND COMMIT TRANSACTION PROCESS */
+/* WILL BE CONTINUED */
+Uint32 Dbacc::checkScanShrink(Signal* signal)
+{
+ Uint32 Ti;
+ Uint32 TreturnCode = 0;
+ Uint32 TPageIndex;
+ Uint32 TDirInd;
+ Uint32 TmergeDest;
+ Uint32 TmergeSource;
+ Uint32 TreleaseScanBucket;
+ Uint32 TreleaseInd = 0;
+ Uint32 TreleaseScanIndicator[4];
+ DirectoryarrayPtr TDirptr;
+ DirRangePtr TDirRangePtr;
+ Page8Ptr TPageptr;
+ ScanRecPtr TscanPtr;
+ RootfragmentrecPtr Trootfragrecptr;
+
+ Trootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(Trootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (fragrecptr.p->p == 0) {
+ jam();
+ TmergeDest = fragrecptr.p->maxp >> 1;
+ } else {
+ jam();
+ TmergeDest = fragrecptr.p->p - 1;
+ }//if
+ TmergeSource = fragrecptr.p->maxp + fragrecptr.p->p;
+ for (Ti = 0; Ti < 4; Ti++) {
+ TreleaseScanIndicator[Ti] = 0;
+ if (Trootfragrecptr.p->scan[Ti] != RNIL) {
+ TscanPtr.i = Trootfragrecptr.p->scan[Ti];
+ ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);
+ if (TscanPtr.p->activeLocalFrag == fragrecptr.i) {
+ //-------------------------------------------------------------
+ // A scan is ongoing on this particular local fragment. We have
+ // to check its current state.
+ //-------------------------------------------------------------
+ if (TscanPtr.p->scanBucketState == ScanRec::FIRST_LAP) {
+ jam();
+ if ((TmergeDest == TscanPtr.p->nextBucketIndex) ||
+ (TmergeSource == TscanPtr.p->nextBucketIndex)) {
+ jam();
+ //-------------------------------------------------------------
+ // We are currently scanning one of the buckets involved in the
+ // merge. We cannot merge while simultaneously performing a scan.
+ // We have to pass this offer for merging the buckets.
+ //-------------------------------------------------------------
+ TreturnCode = 1;
+ return TreturnCode;
+ } else if (TmergeDest < TscanPtr.p->nextBucketIndex) {
+ jam();
+ TreleaseScanIndicator[Ti] = 1;
+ TreleaseInd = 1;
+ }//if
+ } else if (TscanPtr.p->scanBucketState == ScanRec::SECOND_LAP) {
+ jam();
+ //-------------------------------------------------------------
+ // We are performing a second lap to handle buckets that was
+ // merged during the first lap of scanning. During this second
+ // lap we do not allow any splits or merges.
+ //-------------------------------------------------------------
+ TreturnCode = 1;
+ return TreturnCode;
+ } else if (TscanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) {
+ jam();
+ //-------------------------------------------------------------
+ // The scan is completed and we can thus go ahead and perform
+ // the split.
+ //-------------------------------------------------------------
+ } else {
+ jam();
+ sendSystemerror(signal);
+ return TreturnCode;
+ }//if
+ }//if
+ }//if
+ }//for
+ if (TreleaseInd == 1) {
+ jam();
+ TreleaseScanBucket = TmergeSource;
+ TDirRangePtr.i = fragrecptr.p->directory;
+ TPageIndex = TreleaseScanBucket & ((1 << fragrecptr.p->k) - 1); /* PAGE INDEX OBS K = 6 */
+ TDirInd = TreleaseScanBucket >> fragrecptr.p->k; /* DIRECTORY INDEX OBS K = 6 */
+ ptrCheckGuard(TDirRangePtr, cdirrangesize, dirRange);
+ arrGuard((TDirInd >> 8), 256);
+ TDirptr.i = TDirRangePtr.p->dirArray[TDirInd >> 8];
+ ptrCheckGuard(TDirptr, cdirarraysize, directoryarray);
+ TPageptr.i = TDirptr.p->pagep[TDirInd & 0xff];
+ ptrCheckGuard(TPageptr, cpagesize, page8);
+ for (Ti = 0; Ti < 4; Ti++) {
+ if (TreleaseScanIndicator[Ti] == 1) {
+ jam();
+ scanPtr.i = Trootfragrecptr.p->scan[Ti];
+ ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
+ rsbPageidptr.i = TPageptr.i;
+ rsbPageidptr.p = TPageptr.p;
+ trsbPageindex = TPageIndex;
+ releaseScanBucket(signal);
+ if (TmergeDest < scanPtr.p->minBucketIndexToRescan) {
+ jam();
+ //-------------------------------------------------------------
+ // We have to keep track of the starting bucket to Rescan in the
+ // second lap.
+ //-------------------------------------------------------------
+ scanPtr.p->minBucketIndexToRescan = TmergeDest;
+ }//if
+ if (TmergeDest > scanPtr.p->maxBucketIndexToRescan) {
+ jam();
+ //-------------------------------------------------------------
+ // We have to keep track of the ending bucket to Rescan in the
+ // second lap.
+ //-------------------------------------------------------------
+ scanPtr.p->maxBucketIndexToRescan = TmergeDest;
+ }//if
+ }//if
+ }//for
+ }//if
+ return TreturnCode;
+}//Dbacc::checkScanShrink()
+
+void Dbacc::execSHRINKCHECK2(Signal* signal)
+{
+ Uint32 tshrTmp1;
+
+ jamEntry();
+ fragrecptr.i = signal->theData[0];
+ Uint32 oldFlag = signal->theData[3];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ fragrecptr.p->expandFlag = oldFlag;
+ tresult = 0; /* 0= FALSE,1= TRUE,> ZLIMIT_OF_ERROR =ERRORCODE */
+ if (fragrecptr.p->slack <= fragrecptr.p->slackCheck) {
+ jam();
+ /* TIME FOR JOIN BUCKETS PROCESS */
+ /*--------------------------------------------------------------*/
+ /* NO LONGER NECESSARY TO SHRINK THE FRAGMENT. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ if (fragrecptr.p->slack > (1u << 31)) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* THE SLACK IS NEGATIVE, IN THIS CASE WE WILL NOT NEED ANY */
+ /* SHRINK. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ texpDirInd = (fragrecptr.p->maxp + fragrecptr.p->p) >> fragrecptr.p->k;
+ if (((fragrecptr.p->maxp + fragrecptr.p->p) & ((1 << fragrecptr.p->k) - 1)) == 0) {
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (fragrecptr.p->fragState == LCP_SEND_PAGES) {
+ if (fragrecptr.p->lcpMaxDirIndex > texpDirInd) {
+ if (fragrecptr.p->lcpDirIndex <= texpDirInd) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* WE DO NOT ALLOW ANY SHRINKS THAT REMOVE PAGES THAT ARE */
+ /* NEEDED AS PART OF THE LOCAL CHECKPOINT. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+ }//if
+ }//if
+ }//if
+ if (fragrecptr.p->firstOverflowRec == RNIL) {
+ jam();
+ allocOverflowPage(signal);
+ if (tresult > ZLIMIT_OF_ERROR) {
+ jam();
+ return;
+ }//if
+ }//if
+ if (cfirstfreepage == RNIL) {
+ if (cfreepage >= cpagesize) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* WE HAVE TO STOP THE SHRINK PROCESS SINCE THERE ARE NO FREE */
+ /* PAGES. THIS MEANS THAT WE COULD BE FORCED TO CRASH SINCE WE */
+ /* CANNOT COMPLETE THE SHRINK. TO AVOID THE CRASH WE EXIT HERE. */
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+ if (checkScanShrink(signal) == 1) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // A scan state was inconsistent with performing a shrink
+ // operation.
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_EXPAND) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // We did not have enough undo log buffers to start up an
+ // shrink operation
+ /*--------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+ if (fragrecptr.p->p == 0) {
+ jam();
+ fragrecptr.p->maxp = fragrecptr.p->maxp >> 1;
+ fragrecptr.p->p = fragrecptr.p->maxp;
+ fragrecptr.p->lhdirbits--;
+ fragrecptr.p->hashcheckbit--;
+ } else {
+ jam();
+ fragrecptr.p->p--;
+ }//if
+
+ /*--------------------------------------------------------------------------*/
+ /* WE START BY FINDING THE NECESSARY INFORMATION OF THE BUCKET TO BE */
+ /* REMOVED WHICH WILL SEND ITS ELEMENTS TO THE RECEIVING BUCKET. */
+ /*--------------------------------------------------------------------------*/
+ expDirRangePtr.i = fragrecptr.p->directory;
+ cexcPageindex = ((fragrecptr.p->maxp + fragrecptr.p->p) + 1) & ((1 << fragrecptr.p->k) - 1);
+ texpDirInd = ((fragrecptr.p->maxp + fragrecptr.p->p) + 1) >> fragrecptr.p->k;
+ texpDirRangeIndex = texpDirInd >> 8;
+ texpDirPageIndex = texpDirInd & 0xff;
+ ptrCheckGuard(expDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(texpDirRangeIndex, 256);
+ expDirptr.i = expDirRangePtr.p->dirArray[texpDirRangeIndex];
+ ptrCheckGuard(expDirptr, cdirarraysize, directoryarray);
+ excPageptr.i = expDirptr.p->pagep[texpDirPageIndex];
+ fragrecptr.p->expSenderDirptr = expDirptr.i;
+ fragrecptr.p->expSenderIndex = cexcPageindex;
+ fragrecptr.p->expSenderPageptr = excPageptr.i;
+ fragrecptr.p->expSenderDirIndex = texpDirInd;
+ /*--------------------------------------------------------------------------*/
+ /* WE NOW PROCEED BY FINDING THE NECESSARY INFORMATION ABOUT THE */
+ /* RECEIVING BUCKET. */
+ /*--------------------------------------------------------------------------*/
+ expDirRangePtr.i = fragrecptr.p->directory;
+ texpReceivedBucket = fragrecptr.p->p >> fragrecptr.p->k;
+ ptrCheckGuard(expDirRangePtr, cdirrangesize, dirRange);
+ arrGuard((texpReceivedBucket >> 8), 256);
+ expDirptr.i = expDirRangePtr.p->dirArray[texpReceivedBucket >> 8];
+ ptrCheckGuard(expDirptr, cdirarraysize, directoryarray);
+ fragrecptr.p->expReceivePageptr = expDirptr.p->pagep[texpReceivedBucket & 0xff];
+ fragrecptr.p->expReceiveIndex = fragrecptr.p->p & ((1 << fragrecptr.p->k) - 1);
+ fragrecptr.p->expReceiveForward = ZTRUE;
+ if (excPageptr.i == RNIL) {
+ jam();
+ endofshrinkbucketLab(signal); /* EMPTY BUCKET */
+ return;
+ }//if
+ /*--------------------------------------------------------------------------*/
+ /* INITIALISE THE VARIABLES FOR THE SHRINK PROCESS. */
+ /*--------------------------------------------------------------------------*/
+ ptrCheckGuard(excPageptr, cpagesize, page8);
+ cexcForward = ZTRUE;
+ cexcContainerptr = (cexcPageindex << ZSHIFT_PLUS) - (cexcPageindex << ZSHIFT_MINUS);
+ cexcContainerptr = cexcContainerptr + ZHEAD_SIZE;
+ arrGuard(cexcContainerptr, 2048);
+ cexcContainerhead = excPageptr.p->word32[cexcContainerptr];
+ cexcContainerlen = cexcContainerhead >> 26;
+ if (cexcContainerlen <= ZCON_HEAD_SIZE) {
+ ndbrequire(cexcContainerlen == ZCON_HEAD_SIZE);
+ } else {
+ jam();
+ shrinkcontainer(signal);
+ }//if
+ /*--------------------------------------------------------------------------*/
+ /* THIS CONTAINER IS NOT YET EMPTY AND WE REMOVE ALL THE ELEMENTS. */
+ /*--------------------------------------------------------------------------*/
+ if (((cexcContainerhead >> 10) & 1) == 1) {
+ jam();
+ rlPageptr = excPageptr;
+ trlPageindex = cexcPageindex;
+ trlRelCon = ZFALSE;
+ turlIndex = cexcContainerptr + (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ releaseRightlist(signal);
+ }//if
+ tshrTmp1 = ZCON_HEAD_SIZE;
+ tshrTmp1 = tshrTmp1 << 26;
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ datapageptr.p = excPageptr.p;
+ cundoinfolength = 1;
+ cundoElemIndex = cexcContainerptr;
+ undoWritingProcess(signal);
+ }//if
+ dbgWord32(excPageptr, cexcContainerptr, tshrTmp1);
+ arrGuard(cexcContainerptr, 2048);
+ excPageptr.p->word32[cexcContainerptr] = tshrTmp1;
+ if (((cexcContainerhead >> 7) & 0x3) == 0) {
+ jam();
+ endofshrinkbucketLab(signal);
+ return;
+ }//if
+ nextcontainerinfoExp(signal);
+ do {
+ cexcContainerptr = (cexcPageindex << ZSHIFT_PLUS) - (cexcPageindex << ZSHIFT_MINUS);
+ if (cexcForward == ZTRUE) {
+ jam();
+ cexcContainerptr = cexcContainerptr + ZHEAD_SIZE;
+ } else {
+ jam();
+ cexcContainerptr = ((cexcContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - ZCON_HEAD_SIZE;
+ }//if
+ arrGuard(cexcContainerptr, 2048);
+ cexcContainerhead = excPageptr.p->word32[cexcContainerptr];
+ cexcContainerlen = cexcContainerhead >> 26;
+ ndbrequire(cexcContainerlen > ZCON_HEAD_SIZE);
+ /*--------------------------------------------------------------------------*/
+ /* THIS CONTAINER IS NOT YET EMPTY AND WE REMOVE ALL THE ELEMENTS. */
+ /*--------------------------------------------------------------------------*/
+ shrinkcontainer(signal);
+ cexcPrevpageptr = excPageptr.i;
+ cexcPrevpageindex = cexcPageindex;
+ cexcPrevforward = cexcForward;
+ if (((cexcContainerhead >> 7) & 0x3) != 0) {
+ jam();
+ /*--------------------------------------------------------------------------*/
+ /* WE MUST CALL THE NEXT CONTAINER INFO ROUTINE BEFORE WE RELEASE THE */
+ /* CONTAINER SINCE THE RELEASE WILL OVERWRITE THE NEXT POINTER. */
+ /*--------------------------------------------------------------------------*/
+ nextcontainerinfoExp(signal);
+ }//if
+ rlPageptr.i = cexcPrevpageptr;
+ ptrCheckGuard(rlPageptr, cpagesize, page8);
+ trlPageindex = cexcPrevpageindex;
+ if (cexcPrevforward == ZTRUE) {
+ jam();
+ if (((cexcContainerhead >> 10) & 1) == 1) {
+ jam();
+ trlRelCon = ZFALSE;
+ turlIndex = cexcContainerptr + (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ releaseRightlist(signal);
+ }//if
+ trlRelCon = ZTRUE;
+ tullIndex = cexcContainerptr;
+ releaseLeftlist(signal);
+ } else {
+ jam();
+ if (((cexcContainerhead >> 10) & 1) == 1) {
+ jam();
+ trlRelCon = ZFALSE;
+ tullIndex = cexcContainerptr - (ZBUF_SIZE - ZCON_HEAD_SIZE);
+ releaseLeftlist(signal);
+ }//if
+ trlRelCon = ZTRUE;
+ turlIndex = cexcContainerptr;
+ releaseRightlist(signal);
+ }//if
+ } while (((cexcContainerhead >> 7) & 0x3) != 0);
+ endofshrinkbucketLab(signal);
+ return;
+}//Dbacc::execSHRINKCHECK2()
+
+void Dbacc::endofshrinkbucketLab(Signal* signal)
+{
+ fragrecptr.p->expandCounter--;
+ fragrecptr.p->slack -= fragrecptr.p->maxloadfactor;
+ if (fragrecptr.p->expSenderIndex == 0) {
+ jam();
+ fragrecptr.p->dirsize--;
+ if (fragrecptr.p->expSenderPageptr != RNIL) {
+ jam();
+ rpPageptr.i = fragrecptr.p->expSenderPageptr;
+ ptrCheckGuard(rpPageptr, cpagesize, page8);
+ releasePage(signal);
+ expDirptr.i = fragrecptr.p->expSenderDirptr;
+ ptrCheckGuard(expDirptr, cdirarraysize, directoryarray);
+ expDirptr.p->pagep[fragrecptr.p->expSenderDirIndex & 0xff] = RNIL;
+ }//if
+ if (((((fragrecptr.p->p + fragrecptr.p->maxp) + 1) >> fragrecptr.p->k) & 0xff) == 0) {
+ jam();
+ rdDirptr.i = fragrecptr.p->expSenderDirptr;
+ releaseDirectory(signal);
+ expDirRangePtr.i = fragrecptr.p->directory;
+ ptrCheckGuard(expDirRangePtr, cdirrangesize, dirRange);
+ arrGuard((fragrecptr.p->expSenderDirIndex >> 8), 256);
+ expDirRangePtr.p->dirArray[fragrecptr.p->expSenderDirIndex >> 8] = RNIL;
+ }//if
+ }//if
+ if (fragrecptr.p->slack < (1u << 31)) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* THE SLACK IS POSITIVE, IN THIS CASE WE WILL CHECK WHETHER */
+ /* WE WILL CONTINUE PERFORM ANOTHER SHRINK. */
+ /*--------------------------------------------------------------*/
+ Uint32 noOfBuckets = (fragrecptr.p->maxp + 1) + fragrecptr.p->p;
+ Uint32 Thysteresis = fragrecptr.p->maxloadfactor - fragrecptr.p->minloadfactor;
+ fragrecptr.p->slackCheck = noOfBuckets * Thysteresis;
+ if (fragrecptr.p->slack > Thysteresis) {
+ /*--------------------------------------------------------------*/
+ /* IT IS STILL NECESSARY TO SHRINK THE FRAGMENT MORE. THIS*/
+ /* CAN HAPPEN WHEN A NUMBER OF SHRINKS GET REJECTED */
+ /* DURING A LOCAL CHECKPOINT. WE START A NEW SHRINK */
+ /* IMMEDIATELY FROM HERE WITHOUT WAITING FOR A COMMIT TO */
+ /* START IT. */
+ /*--------------------------------------------------------------*/
+ if (fragrecptr.p->expandCounter > 0) {
+ jam();
+ /*--------------------------------------------------------------*/
+ /* IT IS VERY IMPORTANT TO NOT TRY TO SHRINK MORE THAN */
+ /* WAS EXPANDED. IF MAXP IS SET TO A VALUE BELOW 63 THEN */
+ /* WE WILL LOSE RECORDS SINCE GETDIRINDEX CANNOT HANDLE */
+ /* SHRINKING BELOW 2^K - 1 (NOW 63). THIS WAS A BUG THAT */
+ /* WAS REMOVED 2000-05-12. */
+ /*--------------------------------------------------------------*/
+ signal->theData[0] = fragrecptr.i;
+ signal->theData[1] = fragrecptr.p->p;
+ signal->theData[2] = fragrecptr.p->maxp;
+ signal->theData[3] = fragrecptr.p->expandFlag;
+ ndbrequire(fragrecptr.p->expandFlag < 2);
+ fragrecptr.p->expandFlag = 2;
+ sendSignal(cownBlockref, GSN_SHRINKCHECK2, signal, 4, JBB);
+ }//if
+ }//if
+ }//if
+ ndbrequire(fragrecptr.p->maxp >= (Uint32)((1 << fragrecptr.p->k) - 1));
+ return;
+}//Dbacc::endofshrinkbucketLab()
+
+/* --------------------------------------------------------------------------------- */
+/* SHRINKCONTAINER */
+/* INPUT: EXC_PAGEPTR (POINTER TO THE ACTIVE PAGE RECORD) */
+/* CEXC_CONTAINERLEN (LENGTH OF THE CONTAINER). */
+/* CEXC_CONTAINERPTR (ARRAY INDEX OF THE CONTAINER). */
+/* CEXC_FORWARD (CONTAINER FORWARD (+1) OR BACKWARD (-1)) */
+/* */
+/* DESCRIPTION: ALL ELEMENTS OF THE ACTIVE CONTAINER HAVE TO MOVE TO THE NEW */
+/* CONTAINER. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::shrinkcontainer(Signal* signal)
+{
+ Uint32 tshrElementptr;
+ Uint32 tshrRemLen;
+ Uint32 tshrInc;
+ Uint32 tshrTmp;
+ Uint32 tshrIndex;
+ Uint32 guard21;
+
+ tshrRemLen = cexcContainerlen - ZCON_HEAD_SIZE;
+ tshrInc = fragrecptr.p->elementLength;
+ if (cexcForward == ZTRUE) {
+ jam();
+ tshrElementptr = cexcContainerptr + ZCON_HEAD_SIZE;
+ } else {
+ jam();
+ tshrElementptr = cexcContainerptr - 1;
+ }//if
+ SHR_LOOP:
+ idrOperationRecPtr.i = RNIL;
+ ptrNull(idrOperationRecPtr);
+ /* --------------------------------------------------------------------------------- */
+ /* THE CODE BELOW IS ALL USED TO PREPARE FOR THE CALL TO INSERT_ELEMENT AND */
+ /* HANDLE THE RESULT FROM INSERT_ELEMENT. INSERT_ELEMENT INSERTS THE ELEMENT */
+ /* INTO ANOTHER BUCKET. */
+ /* --------------------------------------------------------------------------------- */
+ arrGuard(tshrElementptr, 2048);
+ tidrElemhead = excPageptr.p->word32[tshrElementptr];
+ if (ElementHeader::getLocked(tidrElemhead)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* IF THE ELEMENT IS LOCKED WE MUST UPDATE THE ELEMENT INFO IN THE OPERATION */
+ /* RECORD OWNING THE LOCK. WE DO THIS BY READING THE OPERATION RECORD POINTER */
+ /* FROM THE ELEMENT HEADER. */
+ /* --------------------------------------------------------------------------------- */
+ idrOperationRecPtr.i = ElementHeader::getOpPtrI(tidrElemhead);
+ ptrCheckGuard(idrOperationRecPtr, coprecsize, operationrec);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // During local checkpoints we must ensure that we restore the element header in
+ // unlocked state and with the hash value part there with tuple status zeroed.
+ // Otherwise a later insert over the same element will write an UNDO log that will
+ // ensure that the now removed element is restored together with its locked element
+ // header and without the hash value part.
+ /* --------------------------------------------------------------------------------- */
+ const Uint32 hv = idrOperationRecPtr.p->hashvaluePart;
+ const Uint32 eh = ElementHeader::setUnlocked(hv, 0);
+ excPageptr.p->word32[tshrElementptr] = eh;
+ }//if
+ }//if
+ tshrTmp = tshrElementptr + cexcForward;
+ guard21 = fragrecptr.p->localkeylen - 1;
+ for (tshrIndex = 0; tshrIndex <= guard21; tshrIndex++) {
+ arrGuard(tshrIndex, 2);
+ arrGuard(tshrTmp, 2048);
+ clocalkey[tshrIndex] = excPageptr.p->word32[tshrTmp];
+ tshrTmp = tshrTmp + cexcForward;
+ }//for
+ tidrPageindex = fragrecptr.p->expReceiveIndex;
+ idrPageptr.i = fragrecptr.p->expReceivePageptr;
+ ptrCheckGuard(idrPageptr, cpagesize, page8);
+ tidrForward = fragrecptr.p->expReceiveForward;
+ insertElement(signal);
+ /* --------------------------------------------------------------------------------- */
+ /* TAKE CARE OF RESULT FROM INSERT_ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ fragrecptr.p->expReceiveIndex = tidrPageindex;
+ fragrecptr.p->expReceivePageptr = idrPageptr.i;
+ fragrecptr.p->expReceiveForward = tidrForward;
+ if (tshrRemLen < tshrInc) {
+ jam();
+ sendSystemerror(signal);
+ }//if
+ tshrRemLen = tshrRemLen - tshrInc;
+ if (tshrRemLen != 0) {
+ jam();
+ tshrElementptr = tshrTmp;
+ goto SHR_LOOP;
+ }//if
+}//Dbacc::shrinkcontainer()
+
+/* --------------------------------------------------------------------------------- */
+/* NEXTCONTAINERINFO_EXP */
+/* DESCRIPTION:THE CONTAINER HEAD WILL BE CHECKED TO CALCULATE INFORMATION */
+/* ABOUT NEXT CONTAINER IN THE BUCKET. */
+/* INPUT: CEXC_CONTAINERHEAD */
+/* CEXC_CONTAINERPTR */
+/* EXC_PAGEPTR */
+/* OUTPUT: */
+/* CEXC_PAGEINDEX (INDEX FROM WHICH PAGE INDEX CAN BE CALCULATED. */
+/* EXC_PAGEPTR (PAGE REFERENCE OF NEXT CONTAINER) */
+/* CEXC_FORWARD */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::nextcontainerinfoExp(Signal* signal)
+{
+ tnciNextSamePage = (cexcContainerhead >> 9) & 0x1; /* CHECK BIT FOR CHECKING WHERE */
+ /* THE NEXT CONTAINER IS IN THE SAME PAGE */
+ cexcPageindex = cexcContainerhead & 0x7f; /* NEXT CONTAINER PAGE INDEX 7 BITS */
+ if (((cexcContainerhead >> 7) & 3) == ZLEFT) {
+ jam();
+ cexcForward = ZTRUE;
+ } else if (((cexcContainerhead >> 7) & 3) == ZRIGHT) {
+ jam();
+ cexcForward = cminusOne;
+ } else {
+ jam();
+ sendSystemerror(signal);
+ cexcForward = 0; /* DUMMY FOR COMPILER */
+ }//if
+ if (tnciNextSamePage == ZFALSE) {
+ jam();
+ /* NEXT CONTAINER IS IN AN OVERFLOW PAGE */
+ arrGuard(cexcContainerptr + 1, 2048);
+ tnciTmp = excPageptr.p->word32[cexcContainerptr + 1];
+ nciOverflowrangeptr.i = fragrecptr.p->overflowdir;
+ ptrCheckGuard(nciOverflowrangeptr, cdirrangesize, dirRange);
+ arrGuard((tnciTmp >> 8), 256);
+ nciOverflowDirptr.i = nciOverflowrangeptr.p->dirArray[tnciTmp >> 8];
+ ptrCheckGuard(nciOverflowDirptr, cdirarraysize, directoryarray);
+ excPageptr.i = nciOverflowDirptr.p->pagep[tnciTmp & 0xff];
+ ptrCheckGuard(excPageptr, cpagesize, page8);
+ }//if
+}//Dbacc::nextcontainerinfoExp()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF EXPAND/SHRINK MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* LOCAL CHECKPOINT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* LCP_FRAGIDREQ */
+/* SENDER: LQH, LEVEL B */
+/* ENTER LCP_FRAGIDREQ WITH */
+/* TUSERPTR LQH CONNECTION PTR */
+/* TUSERBLOCKREF, LQH BLOCK REFERENCE */
+/* TCHECKPOINTID, THE CHECKPOINT NUMBER TO USE */
+/* (E.G. 1,2 OR 3) */
+/* TABPTR, TABLE ID = TABLE RECORD POINTER */
+/* TFID ROOT FRAGMENT ID */
+/* CACTIVE_UNDO_FILE_VERSION UNDO FILE VERSION 0,1,2 OR 3. */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* LCP_FRAGIDREQ REQUEST FOR LIST OF STOPED OPERATION */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execLCP_FRAGIDREQ(Signal* signal)
+{
+ jamEntry();
+ tuserptr = signal->theData[0]; /* LQH CONNECTION PTR */
+ tuserblockref = signal->theData[1]; /* LQH BLOCK REFERENCE */
+ tcheckpointid = signal->theData[2]; /* THE CHECKPOINT NUMBER TO USE */
+ /* (E.G. 1,2 OR 3) */
+ tabptr.i = signal->theData[3]; /* TABLE ID = TABLE RECORD POINTER */
+ ptrCheck(tabptr, ctablesize, tabrec);
+ tfid = signal->theData[4]; /* ROOT FRAGMENT ID */
+ cactiveUndoFileVersion = signal->theData[5]; /* UNDO FILE VERSION 0,1,2 OR 3. */
+ tresult = 0;
+ ndbrequire(getrootfragmentrec(signal, rootfragrecptr, tfid));
+ ndbrequire(rootfragrecptr.p->rootState == ACTIVEROOT);
+ seizeLcpConnectRec(signal);
+ initLcpConnRec(signal);
+ lcpConnectptr.p->rootrecptr = rootfragrecptr.i;
+ rootfragrecptr.p->lcpPtr = lcpConnectptr.i;
+ lcpConnectptr.p->localCheckPid = tcheckpointid;
+ lcpConnectptr.p->lcpstate = LCP_ACTIVE;
+ rootfragrecptr.p->rootState = LCP_CREATION;
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ /* D6 AT FSOPENREQ =#010003FF. */
+ tlfrTmp1 = 0x010003ff; /* FILE TYPE = .DATA ,VERSION OF FILENAME = 1 */
+ tlfrTmp2 = 0x301; /* D7 CREATE, WRITE ONLY, TRUNCATE TO ZERO */
+ ndbrequire(cfsFirstfreeconnect != RNIL);
+ seizeFsConnectRec(signal);
+ fsConnectptr.p->fragrecPtr = fragrecptr.i;
+ fsConnectptr.p->fsState = WAIT_OPEN_DATA_FILE_FOR_WRITE;
+ /* ----------- FILENAME (FILESYSTEM)/D3/DBACC/"T"TABID/"F"FRAGID/"S"VERSIONID.DATA ------------ */
+ /* ************************ */
+ /* FSOPENREQ */
+ /* ************************ */
+ signal->theData[0] = cownBlockref;
+ signal->theData[1] = fsConnectptr.i;
+ signal->theData[2] = tabptr.i; /* TABLE IDENTITY */
+ signal->theData[3] = rootfragrecptr.p->fragmentid[0]; /* FRAGMENT IDENTITY */
+ signal->theData[4] = lcpConnectptr.p->localCheckPid; /* CHECKPOINT ID */
+ signal->theData[5] = tlfrTmp1;
+ signal->theData[6] = tlfrTmp2;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ return;
+}//Dbacc::execLCP_FRAGIDREQ()
+
+/* ******************--------------------------------------------------------------- */
+/* FSOPENCONF OPENFILE CONF */
+/* SENDER: FS, LEVEL B */
+/* ENTER FSOPENCONF WITH */
+/* FS_CONNECTPTR, FS_CONNECTION PTR */
+/* TUSERPOINTER, FILE POINTER */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::lcpFsOpenConfLab(Signal* signal)
+{
+ fsConnectptr.p->fsPtr = tuserptr;
+ fragrecptr.i = fsConnectptr.p->fragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ fragrecptr.p->activeDataFilePage = 1; /* ZERO IS KEPT FOR PAGE_ZERO */
+ fragrecptr.p->fsConnPtr = fsConnectptr.i;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ if (rootfragrecptr.p->fragmentptr[0] == fragrecptr.i) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ ptrCheck(fragrecptr, cfragmentsize, fragmentrec);
+ /* ----------- FILENAME (FILESYSTEM)/D3/DBACC/"T"TABID/"F"FRAGID/"S"VERSIONID.DATA ------------ */
+ /* D6 AT FSOPENREQ =#010003FF. */
+ tlfrTmp1 = 0x010003ff; /* FILE TYPE = .DATA ,VERSION OF FILENAME = 1 */
+ tlfrTmp2 = 0x301; /* D7 CREATE, WRITE ONLY, TRUNCATE TO ZERO */
+ ndbrequire(cfsFirstfreeconnect != RNIL);
+ seizeFsConnectRec(signal);
+ fsConnectptr.p->fragrecPtr = fragrecptr.i;
+ fsConnectptr.p->fsState = WAIT_OPEN_DATA_FILE_FOR_WRITE;
+ /* ************************ */
+ /* FSOPENREQ */
+ /* ************************ */
+ signal->theData[0] = cownBlockref;
+ signal->theData[1] = fsConnectptr.i;
+ signal->theData[2] = rootfragrecptr.p->mytabptr; /* TABLE IDENTITY */
+ signal->theData[3] = rootfragrecptr.p->fragmentid[1]; /* FRAGMENT IDENTITY */
+ signal->theData[4] = lcpConnectptr.p->localCheckPid; /* CHECKPOINT ID */
+ signal->theData[5] = tlfrTmp1;
+ signal->theData[6] = tlfrTmp2;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ return;
+ } else {
+ ndbrequire(rootfragrecptr.p->fragmentptr[1] == fragrecptr.i);
+ }//if
+ /*---- BOTH DATA FILES ARE OPEN------*/
+ /* ----IF THE UNDO FILE IS CLOSED , OPEN IT.----- */
+ if (cactiveOpenUndoFsPtr != RNIL) {
+ jam();
+ sendLcpFragidconfLab(signal);
+ return;
+ }//if
+ cactiveUndoFilePage = 0;
+ cprevUndoaddress = cminusOne;
+ cundoposition = 0;
+ clastUndoPageIdWritten = 0;
+ ndbrequire(cfsFirstfreeconnect != RNIL);
+ seizeFsConnectRec(signal);
+ fsConnectptr.p->fsState = WAIT_OPEN_UNDO_LCP;
+ fsConnectptr.p->fsPart = 0; /* FILE INDEX, SECOND FILE IN THE DIRECTORY */
+ cactiveOpenUndoFsPtr = fsConnectptr.i;
+ cactiveRootfrag = rootfragrecptr.i;
+ tlfrTmp1 = 1; /* FILE VERSION */
+ tlfrTmp1 = (tlfrTmp1 << 8) + ZLOCALLOGFILE; /* .LOCLOG = 2 */
+ tlfrTmp1 = (tlfrTmp1 << 8) + 4; /* ROOT DIRECTORY = D4 */
+ tlfrTmp1 = (tlfrTmp1 << 8) + fsConnectptr.p->fsPart; /* P2 */
+ tlfrTmp2 = 0x302; /* D7 CREATE , READ / WRITE , TRUNCATE TO ZERO */
+ /* ---FILE NAME "D4"/"DBACC"/LCP_CONNECTPTR:LOCAL_CHECK_PID/FS_CONNECTPTR:FS_PART".LOCLOG-- */
+ /* ************************ */
+ /* FSOPENREQ */
+ /* ************************ */
+ signal->theData[0] = cownBlockref;
+ signal->theData[1] = fsConnectptr.i;
+ signal->theData[2] = cminusOne; /* #FFFFFFFF */
+ signal->theData[3] = cminusOne; /* #FFFFFFFF */
+ signal->theData[4] = cactiveUndoFileVersion;
+ /* A GROUP OF UNDO FILES WHICH ARE UPDATED */
+ signal->theData[5] = tlfrTmp1;
+ signal->theData[6] = tlfrTmp2;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ return;
+}//Dbacc::lcpFsOpenConfLab()
+
+void Dbacc::lcpOpenUndofileConfLab(Signal* signal)
+{
+ ptrGuard(fsConnectptr);
+ fsConnectptr.p->fsState = WAIT_NOTHING;
+ rootfragrecptr.i = cactiveRootfrag;
+ ptrCheck(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ fsConnectptr.p->fsPtr = tuserptr;
+ sendLcpFragidconfLab(signal);
+ return;
+}//Dbacc::lcpOpenUndofileConfLab()
+
+void Dbacc::sendLcpFragidconfLab(Signal* signal)
+{
+ ptrGuard(rootfragrecptr);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ /* ************************ */
+ /* LCP_FRAGIDCONF */
+ /* ************************ */
+ signal->theData[0] = lcpConnectptr.p->lcpUserptr;
+ signal->theData[1] = lcpConnectptr.i;
+ signal->theData[2] = 2;
+ /* NO OF LOCAL FRAGMENTS */
+ signal->theData[3] = rootfragrecptr.p->fragmentid[0];
+ signal->theData[4] = rootfragrecptr.p->fragmentid[1];
+ signal->theData[5] = RNIL;
+ signal->theData[6] = RNIL;
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_LCP_FRAGIDCONF, signal, 7, JBB);
+ return;
+}//Dbacc::sendLcpFragidconfLab()
+
+/* ******************--------------------------------------------------------------- */
+/* LCP_HOLDOPERATION REQUEST FOR LIST OF STOPED OPERATION */
+/* SENDER: LQH, LEVEL B */
+/* ENTER LCP_HOLDOPREQ WITH */
+/* LCP_CONNECTPTR CONNECTION POINTER */
+/* TFID, LOCAL FRAGMENT ID */
+/* THOLD_PREV_SENT_OP NR OF SENT OPERATIONS AT */
+/* PREVIOUS SIGNALS */
+/* TLQH_POINTER LQH USER POINTER */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* LCP_HOLDOPERATION REQUEST FOR LIST OF STOPED OPERATION */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execLCP_HOLDOPREQ(Signal* signal)
+{
+ Uint32 tholdPrevSentOp;
+
+ jamEntry();
+ lcpConnectptr.i = signal->theData[0]; /* CONNECTION POINTER */
+ tfid = signal->theData[1]; /* LOCAL FRAGMENT ID */
+ tholdPrevSentOp = signal->theData[2]; /* NR OF SENT OPERATIONS AT */
+ /* PREVIOUS SIGNALS */
+ tlqhPointer = signal->theData[3]; /* LQH USER POINTER */
+
+ tresult = 0;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ ndbrequire(lcpConnectptr.p->lcpstate == LCP_ACTIVE);
+ rootfragrecptr.i = lcpConnectptr.p->rootrecptr;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->fragmentid[0] == tfid) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ } else {
+ ndbrequire(rootfragrecptr.p->fragmentid[1] == tfid);
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ }//if
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ fragrecptr.p->lcpLqhPtr = tlqhPointer;
+ if (tholdPrevSentOp != 0) {
+ ndbrequire(fragrecptr.p->fragState == SEND_QUE_OP);
+ } else if (tholdPrevSentOp == 0) {
+ jam();
+ fragrecptr.p->fragState = SEND_QUE_OP;
+ fragrecptr.p->stopQueOp = ZTRUE;
+ fragrecptr.p->sentWaitInQueOp = fragrecptr.p->firstWaitInQueOp;
+ }//if
+ tholdSentOp = 0; /* NR OF OPERATION WHICH ARE SENT THIS TIME */
+ operationRecPtr.i = fragrecptr.p->sentWaitInQueOp;
+
+ /* --------------------------------------------- */
+ /* GO THROUGH ALL OPERATION IN THE WAIT */
+ /* LIST AND SEND THE LQH CONNECTION PTR OF THE */
+ /* OPERATIONS TO THE LQH BLOCK. MAX 23 0PERATION */
+ /* PER SIGNAL */
+ /* --------------------------------------------- */
+ while (operationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ ckeys[tholdSentOp] = operationRecPtr.p->userptr;
+ operationRecPtr.i = operationRecPtr.p->nextQueOp;
+ tholdSentOp++;
+ if ((tholdSentOp >= 23) &&
+ (operationRecPtr.i != RNIL)) {
+ jam();
+ /* ----------------------------------------------- */
+ /* THERE IS MORE THAN 23 WAIT OPERATION. WE */
+ /* HAVE TO SEND THESE 23 AND WAITE FOR NEXT SIGNAL */
+ /* ----------------------------------------------- */
+ tholdMore = ZTRUE; /* SECOUND DATA AT THE CONF SIGNAL , = MORE */
+ fragrecptr.p->sentWaitInQueOp = operationRecPtr.i;
+ sendholdconfsignalLab(signal);
+ return;
+ }//if
+ }//while
+ /* ----------------------------------------------- */
+ /* OPERATION_REC_PTR = RNIL */
+ /* THERE IS NO MORE WAITING OPERATION, STATE OF */
+ /* THE FRAGMENT RRECORD IS CHANGED AND RETURN */
+ /* SIGNAL IS SENT */
+ /* ----------------------------------------------- */
+ fragrecptr.p->sentWaitInQueOp = RNIL;
+ tholdMore = ZFALSE; /* SECOND DATA AT THE CONF SIGNAL , = NOT MORE */
+ fragrecptr.p->fragState = WAIT_ACC_LCPREQ;
+ sendholdconfsignalLab(signal);
+ return;
+}//Dbacc::execLCP_HOLDOPREQ()
+
+void Dbacc::sendholdconfsignalLab(Signal* signal)
+{
+ tholdMore = (tholdMore << 16) + tholdSentOp;
+ /* SECOND SIGNAL DATA, LENGTH + MORE */
+ /* ************************ */
+ /* LCP_HOLDOPCONF */
+ /* ************************ */
+ signal->theData[0] = fragrecptr.p->lcpLqhPtr;
+ signal->theData[1] = tholdMore;
+ signal->theData[2] = ckeys[0];
+ signal->theData[3] = ckeys[1];
+ signal->theData[4] = ckeys[2];
+ signal->theData[5] = ckeys[3];
+ signal->theData[6] = ckeys[4];
+ signal->theData[7] = ckeys[5];
+ signal->theData[8] = ckeys[6];
+ signal->theData[9] = ckeys[7];
+ signal->theData[10] = ckeys[8];
+ signal->theData[11] = ckeys[9];
+ signal->theData[12] = ckeys[10];
+ signal->theData[13] = ckeys[11];
+ signal->theData[14] = ckeys[12];
+ signal->theData[15] = ckeys[13];
+ signal->theData[16] = ckeys[14];
+ signal->theData[17] = ckeys[15];
+ signal->theData[18] = ckeys[16];
+ signal->theData[19] = ckeys[17];
+ signal->theData[20] = ckeys[18];
+ signal->theData[21] = ckeys[19];
+ signal->theData[22] = ckeys[20];
+ signal->theData[23] = ckeys[21];
+ signal->theData[24] = ckeys[22];
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_LCP_HOLDOPCONF, signal, 25, JBA);
+ return;
+}//Dbacc::sendholdconfsignalLab()
+
+/**
+ * execACC_LCPREQ
+ * Perform local checkpoint of a fragment
+ *
+ * SENDER: LQH, LEVEL B
+ * ENTER ACC_LCPREQ WITH
+ * LCP_CONNECTPTR, OPERATION RECORD PTR
+ * TLCP_LQH_CHECK_V, LQH'S LOCAL FRAG CHECK VALUE
+ * TLCP_LOCAL_FRAG_ID, LOCAL FRAG ID
+ *
+ */
+void Dbacc::execACC_LCPREQ(Signal* signal)
+{
+ Uint32 tlcpLocalFragId;
+ Uint32 tlcpLqhCheckV;
+
+ jamEntry();
+ lcpConnectptr.i = signal->theData[0]; // CONNECTION PTR
+ tlcpLqhCheckV = signal->theData[1]; // LQH'S LOCAL FRAG CHECK VALUE
+ tlcpLocalFragId = signal->theData[2]; // LOCAL FRAG ID
+ tresult = 0;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ ndbrequire(lcpConnectptr.p->lcpstate == LCP_ACTIVE);
+
+ rootfragrecptr.i = lcpConnectptr.p->rootrecptr;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->fragmentid[0] == tlcpLocalFragId) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ } else {
+ ndbrequire(rootfragrecptr.p->fragmentid[1] == tlcpLocalFragId);
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ }//if
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ ndbrequire(fragrecptr.p->fragState == WAIT_ACC_LCPREQ);
+ fragrecptr.p->lcpLqhPtr = tlcpLqhCheckV;
+
+ Page8Ptr zeroPagePtr;
+ seizeLcpPage(zeroPagePtr);
+ fragrecptr.p->zeroPagePtr = zeroPagePtr.i;
+ fragrecptr.p->prevUndoposition = cminusOne;
+ initRootFragPageZero(rootfragrecptr, zeroPagePtr);
+ initFragPageZero(fragrecptr, zeroPagePtr);
+ /*-----------------------------------------------------------------*/
+ /* SEIZE ZERO PAGE FIRST AND THEN SEIZE DATA PAGES IN */
+ /* BACKWARDS ORDER. THIS IS TO ENSURE THAT WE GET THE PAGES */
+ /* IN ORDER. ON WINDOWS NT THIS WILL BE A BENEFIT SINCE WE */
+ /* CAN THEN DO 1 WRITE_FILE INSTEAD OF 8. */
+ /* WHEN WE RELEASE THE PAGES WE RELEASE THEM IN THE OPPOSITE */
+ /* ORDER. */
+ /*-----------------------------------------------------------------*/
+ for (Uint32 taspTmp = ZWRITEPAGESIZE - 1; (Uint32)~taspTmp; taspTmp--) {
+ Page8Ptr dataPagePtr;
+ jam();
+ ndbrequire(fragrecptr.p->datapages[taspTmp] == RNIL);
+ seizeLcpPage(dataPagePtr);
+ fragrecptr.p->datapages[taspTmp] = dataPagePtr.i;
+ }//for
+ fragrecptr.p->lcpMaxDirIndex = fragrecptr.p->dirsize;
+ fragrecptr.p->lcpMaxOverDirIndex = fragrecptr.p->lastOverIndex;
+ fragrecptr.p->createLcp = ZTRUE;
+ operationRecPtr.i = fragrecptr.p->lockOwnersList;
+ lcp_write_op_to_undolog(signal);
+}
+
+void
+Dbacc::lcp_write_op_to_undolog(Signal* signal)
+{
+ bool delay_continueb= false;
+ Uint32 i, j;
+ for (i= 0; i < 16; i++) {
+ jam();
+ if (remainingUndoPages() <= ZMIN_UNDO_PAGES_AT_COMMIT) {
+ jam();
+ delay_continueb= true;
+ break;
+ }
+ for (j= 0; j < 32; j++) {
+ if (operationRecPtr.i == RNIL) {
+ jam();
+ break;
+ }
+ jam();
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+
+ if ((operationRecPtr.p->operation == ZINSERT) ||
+ (operationRecPtr.p->elementIsDisappeared == ZTRUE)){
+ /*******************************************************************
+ * Only log inserts and elements that are marked as dissapeared.
+ * All other operations update the element header and that is handled
+ * when pages are written to disk
+ ********************************************************************/
+ undopageptr.i = (cundoposition>>ZUNDOPAGEINDEXBITS) & (cundopagesize-1);
+ ptrAss(undopageptr, undopage);
+ theadundoindex = cundoposition & ZUNDOPAGEINDEX_MASK;
+ tundoindex = theadundoindex + ZUNDOHEADSIZE;
+
+ writeUndoOpInfo(signal);/* THE INFORMATION ABOUT ELEMENT HEADER, STORED*/
+ /* IN OP REC, IS WRITTEN AT UNDO PAGES */
+ cundoElemIndex = 0;/* DEFAULT VALUE USED BY WRITE_UNDO_HEADER SUBROTINE */
+ writeUndoHeader(signal, RNIL, UndoHeader::ZOP_INFO); /* WRITE THE HEAD OF THE UNDO ELEMENT */
+ checkUndoPages(signal); /* SEND UNDO PAGE TO DISK WHEN A GROUP OF */
+ /* UNDO PAGES,CURRENTLY 8, IS FILLED */
+ }
+ operationRecPtr.i = operationRecPtr.p->nextLockOwnerOp;
+ }
+ if (operationRecPtr.i == RNIL) {
+ jam();
+ break;
+ }
+ }
+ if (operationRecPtr.i != RNIL) {
+ jam();
+ signal->theData[0]= ZLCP_OP_WRITE_RT_BREAK;
+ signal->theData[1]= operationRecPtr.i;
+ signal->theData[2]= fragrecptr.i;
+ signal->theData[3]= lcpConnectptr.i;
+ if (delay_continueb) {
+ jam();
+ sendSignalWithDelay(cownBlockref, GSN_CONTINUEB, signal, 10, 4);
+ } else {
+ jam();
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 4, JBB);
+ }
+ return;
+ }
+
+ signal->theData[0] = fragrecptr.p->lcpLqhPtr;
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_ACC_LCPSTARTED,
+ signal, 1, JBA);
+
+ fragrecptr.p->activeDataPage = 0;
+ fragrecptr.p->lcpDirIndex = 0;
+ fragrecptr.p->fragState = LCP_SEND_PAGES;
+
+ signal->theData[0] = lcpConnectptr.i;
+ signal->theData[1] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_ACC_SAVE_PAGES, signal, 2, JBB);
+}
+
+/* ******************--------------------------------------------------------------- */
+/* ACC_SAVE_PAGES A GROUP OF PAGES IS ALLOCATED. THE PAGES AND OVERFLOW */
+/* PAGES OF THE FRAGMENT ARE COPIED IN THEM AND IS SEND TO */
+/* THE DATA FILE OF THE CHECK POINT. */
+/* SENDER: ACC, LEVEL B */
+/* ENTER ACC_SAVE_PAGES WITH */
+/* LCP_CONNECTPTR, CONNECTION RECORD PTR */
+/* FRAGRECPTR FRAGMENT RECORD PTR */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACC_SAVE_PAGES REQUEST TO SEND THE PAGE TO DISK */
+/* ******************------------------------------+ UNDO PAGES */
+/* SENDER: ACC, LEVEL B */
+void Dbacc::execACC_SAVE_PAGES(Signal* signal)
+{
+ jamEntry();
+ lcpConnectptr.i = signal->theData[0];
+ /* CONNECTION RECORD PTR */
+ fragrecptr.i = signal->theData[1];
+ /* FRAGMENT RECORD PTR */
+ tresult = 0;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ if (lcpConnectptr.p->lcpstate != LCP_ACTIVE) {
+ jam();
+ sendSystemerror(signal);
+ return;
+ }//if
+ if (ERROR_INSERTED(3000)) {
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->mytabptr == c_errorInsert3000_TableId){
+ ndbout << "Delay writing of datapages" << endl;
+ // Delay writing of pages
+ jam();
+ sendSignalWithDelay(cownBlockref, GSN_ACC_SAVE_PAGES, signal, 1000, 2);
+ return;
+ }
+ }
+ if (clblPageCounter == 0) {
+ jam();
+ signal->theData[0] = lcpConnectptr.i;
+ signal->theData[1] = fragrecptr.i;
+ sendSignalWithDelay(cownBlockref, GSN_ACC_SAVE_PAGES, signal, 100, 2);
+ return;
+ } else {
+ jam();
+ clblPageCounter = clblPageCounter - 1;
+ }//if
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (fragrecptr.p->fragState == LCP_SEND_PAGES) {
+ jam();
+ savepagesLab(signal);
+ return;
+ } else {
+ if (fragrecptr.p->fragState == LCP_SEND_OVER_PAGES) {
+ jam();
+ saveOverPagesLab(signal);
+ return;
+ } else {
+ ndbrequire(fragrecptr.p->fragState == LCP_SEND_ZERO_PAGE);
+ jam();
+ saveZeroPageLab(signal);
+ return;
+ }//if
+ }//if
+}//Dbacc::execACC_SAVE_PAGES()
+
+void Dbacc::savepagesLab(Signal* signal)
+{
+ DirRangePtr spDirRangePtr;
+ DirectoryarrayPtr spDirptr;
+ Page8Ptr aspPageptr;
+ Page8Ptr aspCopyPageptr;
+ Uint32 taspDirindex;
+ Uint32 taspDirIndex;
+ Uint32 taspIndex;
+
+ if ((fragrecptr.p->lcpDirIndex >= fragrecptr.p->dirsize) ||
+ (fragrecptr.p->lcpDirIndex >= fragrecptr.p->lcpMaxDirIndex)) {
+ jam();
+ endsavepageLab(signal);
+ return;
+ }//if
+ /* SOME EXPAND PROCESSES HAVE BEEN PERFORMED. */
+ /* THE ADDED PAGE ARE NOT SENT TO DISK */
+ arrGuard(fragrecptr.p->activeDataPage, 8);
+ aspCopyPageptr.i = fragrecptr.p->datapages[fragrecptr.p->activeDataPage];
+ ptrCheckGuard(aspCopyPageptr, cpagesize, page8);
+ taspDirindex = fragrecptr.p->lcpDirIndex; /* DIRECTORY OF ACTIVE PAGE */
+ spDirRangePtr.i = fragrecptr.p->directory;
+ taspDirIndex = taspDirindex >> 8;
+ taspIndex = taspDirindex & 0xff;
+ ptrCheckGuard(spDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(taspDirIndex, 256);
+ spDirptr.i = spDirRangePtr.p->dirArray[taspDirIndex];
+ ptrCheckGuard(spDirptr, cdirarraysize, directoryarray);
+ aspPageptr.i = spDirptr.p->pagep[taspIndex];
+ ptrCheckGuard(aspPageptr, cpagesize, page8);
+ ndbrequire(aspPageptr.p->word32[ZPOS_PAGE_ID] == fragrecptr.p->lcpDirIndex);
+ lcnPageptr = aspPageptr;
+ lcnCopyPageptr = aspCopyPageptr;
+ lcpCopyPage(signal);
+ fragrecptr.p->lcpDirIndex++;
+ fragrecptr.p->activeDataPage++;
+ if (fragrecptr.p->activeDataPage < ZWRITEPAGESIZE) {
+ jam();
+ signal->theData[0] = lcpConnectptr.i;
+ signal->theData[1] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_ACC_SAVE_PAGES, signal, 2, JBB);
+ return;
+ }//if
+ senddatapagesLab(signal);
+ return;
+}//Dbacc::savepagesLab()
+
+/* FRAGRECPTR:ACTIVE_DATA_PAGE = ZWRITEPAGESIZE */
+/* SEND A GROUP OF PAGES TO DISK */
+void Dbacc::senddatapagesLab(Signal* signal)
+{
+ fsConnectptr.i = fragrecptr.p->fsConnPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ seizeFsOpRec(signal);
+ initFsOpRec(signal);
+ fsOpptr.p->fsOpstate = WAIT_WRITE_DATA;
+ ndbrequire(fragrecptr.p->activeDataPage <= 8);
+ for (Uint32 i = 0; i < fragrecptr.p->activeDataPage; i++) {
+ signal->theData[i + 6] = fragrecptr.p->datapages[i];
+ }//for
+ signal->theData[fragrecptr.p->activeDataPage + 6] = fragrecptr.p->activeDataFilePage;
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsOpptr.i;
+ signal->theData[3] = 0x2;
+ signal->theData[4] = ZPAGE8_BASE_ADD;
+ signal->theData[5] = fragrecptr.p->activeDataPage;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 15, JBA);
+ return;
+}//Dbacc::senddatapagesLab()
+
+void Dbacc::endsavepageLab(Signal* signal)
+{
+ Page8Ptr espPageidptr;
+
+ espPageidptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(espPageidptr, cpagesize, page8);
+ dbgWord32(espPageidptr, ZPAGEZERO_NO_PAGES, fragrecptr.p->lcpDirIndex);
+ espPageidptr.p->word32[ZPAGEZERO_NO_PAGES] = fragrecptr.p->lcpDirIndex;
+ fragrecptr.p->fragState = LCP_SEND_OVER_PAGES;
+ fragrecptr.p->noOfStoredOverPages = 0;
+ fragrecptr.p->lcpDirIndex = 0;
+ saveOverPagesLab(signal);
+ return;
+}//Dbacc::endsavepageLab()
+
+/* ******************--------------------------------------------------------------- */
+/* ACC_SAVE_OVER_PAGES CONTINUE SAVING THE LEFT OVERPAGES. */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::saveOverPagesLab(Signal* signal)
+{
+ DirRangePtr sopDirRangePtr;
+ DirectoryarrayPtr sopOverflowDirptr;
+ Page8Ptr sopPageptr;
+ Page8Ptr sopCopyPageptr;
+ Uint32 tsopDirindex;
+ Uint32 tsopDirInd;
+ Uint32 tsopIndex;
+
+ if ((fragrecptr.p->lcpDirIndex >= fragrecptr.p->lastOverIndex) ||
+ (fragrecptr.p->lcpDirIndex >= fragrecptr.p->lcpMaxOverDirIndex)) {
+ jam();
+ endsaveoverpageLab(signal);
+ return;
+ }//if
+ arrGuard(fragrecptr.p->activeDataPage, 8);
+ sopCopyPageptr.i = fragrecptr.p->datapages[fragrecptr.p->activeDataPage];
+ ptrCheckGuard(sopCopyPageptr, cpagesize, page8);
+ tsopDirindex = fragrecptr.p->lcpDirIndex;
+ sopDirRangePtr.i = fragrecptr.p->overflowdir;
+ tsopDirInd = tsopDirindex >> 8;
+ tsopIndex = tsopDirindex & 0xff;
+ ptrCheckGuard(sopDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(tsopDirInd, 256);
+ sopOverflowDirptr.i = sopDirRangePtr.p->dirArray[tsopDirInd];
+ ptrCheckGuard(sopOverflowDirptr, cdirarraysize, directoryarray);
+ sopPageptr.i = sopOverflowDirptr.p->pagep[tsopIndex];
+ fragrecptr.p->lcpDirIndex++;
+ if (sopPageptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(sopPageptr, cpagesize, page8);
+ ndbrequire(sopPageptr.p->word32[ZPOS_PAGE_ID] == tsopDirindex);
+ ndbrequire(((sopPageptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) != ZNORMAL_PAGE_TYPE);
+ lcnPageptr = sopPageptr;
+ lcnCopyPageptr = sopCopyPageptr;
+ lcpCopyPage(signal);
+ fragrecptr.p->noOfStoredOverPages++;
+ fragrecptr.p->activeDataPage++;
+ if ((sopPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] == 0)) {
+ //ndbrequire(((sopPageptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) == ZOVERFLOW_PAGE_TYPE);
+ if (((sopPageptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) ==
+ ZOVERFLOW_PAGE_TYPE) {
+ /*--------------------------------------------------------------------------------*/
+ /* THE PAGE IS EMPTY AND WAITING TO BE RELEASED. IT COULD NOT BE RELEASED */
+ /* EARLIER SINCE IT WAS PART OF A LOCAL CHECKPOINT. */
+ /*--------------------------------------------------------------------------------*/
+ jam();
+ ropPageptr = sopPageptr;
+ releaseOverpage(signal);
+ } else {
+ jam();
+ sendSystemerror(signal);
+ }
+ }//if
+ }
+ if (fragrecptr.p->activeDataPage == ZWRITEPAGESIZE) {
+ jam();
+ senddatapagesLab(signal);
+ return;
+ }//if
+ signal->theData[0] = lcpConnectptr.i;
+ signal->theData[1] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_ACC_SAVE_PAGES, signal, 2, JBB);
+ return;
+}//Dbacc::saveOverPagesLab()
+
+void Dbacc::endsaveoverpageLab(Signal* signal)
+{
+ Page8Ptr esoPageidptr;
+
+ esoPageidptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(esoPageidptr, cpagesize, page8);
+ dbgWord32(esoPageidptr, ZPAGEZERO_NO_OVER_PAGE, fragrecptr.p->noOfStoredOverPages);
+ esoPageidptr.p->word32[ZPAGEZERO_NO_OVER_PAGE] = fragrecptr.p->noOfStoredOverPages;
+ fragrecptr.p->fragState = LCP_SEND_ZERO_PAGE;
+ if (fragrecptr.p->activeDataPage != 0) {
+ jam();
+ senddatapagesLab(signal); /* SEND LEFT PAGES TO DISK */
+ return;
+ }//if
+ saveZeroPageLab(signal);
+ return;
+}//Dbacc::endsaveoverpageLab()
+
+/* ******************--------------------------------------------------------------- */
+/* ACC_SAVE_ZERO_PAGE PAGE ZERO IS SENT TO DISK.IT IS THE LAST STAGE AT THE */
+/* CREATION LCP. ACC_LCPCONF IS RETURND. */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::saveZeroPageLab(Signal* signal)
+{
+ Page8Ptr szpPageidptr;
+ Uint32 Tchs;
+ Uint32 Ti;
+
+ fragrecptr.p->createLcp = ZFALSE;
+ fsConnectptr.i = fragrecptr.p->fsConnPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ szpPageidptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(szpPageidptr, cpagesize, page8);
+ dbgWord32(szpPageidptr, ZPAGEZERO_PREV_UNDOP, fragrecptr.p->prevUndoposition);
+ szpPageidptr.p->word32[ZPAGEZERO_PREV_UNDOP] = fragrecptr.p->prevUndoposition;
+ dbgWord32(szpPageidptr, ZPAGEZERO_NEXT_UNDO_FILE, cactiveUndoFileVersion);
+ szpPageidptr.p->word32[ZPAGEZERO_NEXT_UNDO_FILE] = cactiveUndoFileVersion;
+ fragrecptr.p->fragState = WAIT_ZERO_PAGE_STORED;
+
+ /* --------------------------------------------------------------------------------- */
+ // Calculate the checksum and store it for the zero page of the fragment.
+ /* --------------------------------------------------------------------------------- */
+ szpPageidptr.p->word32[ZPOS_CHECKSUM] = 0;
+ Tchs = 0;
+ for (Ti = 0; Ti < 2048; Ti++) {
+ Tchs = Tchs ^ szpPageidptr.p->word32[Ti];
+ }//for
+ szpPageidptr.p->word32[ZPOS_CHECKSUM] = Tchs;
+ dbgWord32(szpPageidptr, ZPOS_CHECKSUM, Tchs);
+
+ seizeFsOpRec(signal);
+ initFsOpRec(signal);
+ fsOpptr.p->fsOpstate = WAIT_WRITE_DATA;
+ if (clblPageCounter > 0) {
+ jam();
+ clblPageCounter = clblPageCounter - 1;
+ } else {
+ jam();
+ clblPageOver = clblPageOver + 1;
+ }//if
+ /* ************************ */
+ /* FSWRITEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsOpptr.i;
+ signal->theData[3] = 0x10;
+ /* FLAG = LIST MEM PAGES, LIST FILE PAGES */
+ /* SYNC FILE AFTER WRITING */
+ signal->theData[4] = ZPAGE8_BASE_ADD;
+ signal->theData[5] = 1;
+ /* NO OF PAGES */
+ signal->theData[6] = fragrecptr.p->zeroPagePtr;
+ /* ZERO PAGE */
+ signal->theData[7] = 0;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+ /* ZERO PAGE AT DATA FILE */
+ return;
+}//Dbacc::saveZeroPageLab()
+
+/* ******************--------------------------------------------------------------- */
+/* FSWRITECONF OPENFILE CONF */
+/* ENTER FSWRITECONF WITH SENDER: FS, LEVEL B */
+/* FS_OPPTR FS_CONNECTION PTR */
+/* ******************--------------------------------------------------------------- */
+void Dbacc::lcpCloseDataFileLab(Signal* signal)
+{
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ fsConnectptr.p->fsState = LCP_CLOSE_DATA;
+ /* ************************ */
+ /* FSCLOSEREQ */
+ /* ************************ */
+ /* CLOSE DATA FILE */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = ZFALSE;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ /* FLAG = 0, DO NOT DELETE FILE */
+ return;
+}//Dbacc::lcpCloseDataFileLab()
+
+void Dbacc::checkSyncUndoPagesLab(Signal* signal)
+{
+ fragrecptr.i = fsConnectptr.p->fragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ releaseFsConnRec(signal);
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ switch (lcpConnectptr.p->syncUndopageState) {
+ case WAIT_NOTHING:
+ jam();
+ lcpConnectptr.p->syncUndopageState = WAIT_ONE_CONF;
+ break;
+ case WAIT_ONE_CONF:
+ jam();
+ lcpConnectptr.p->syncUndopageState = WAIT_TWO_CONF;
+ break;
+ default:
+ jam();
+ sendSystemerror(signal);
+ return;
+ break;
+ }//switch
+
+ /* ACTIVE UNDO PAGE ID */
+ Uint32 tundoPageId = cundoposition >> ZUNDOPAGEINDEXBITS;
+ tmp1 = tundoPageId - (tundoPageId & (ZWRITE_UNDOPAGESIZE - 1));
+ /* START PAGE OF THE LAST UNDO PAGES GROUP */
+ tmp2 = (tundoPageId - tmp1) + 1; /* NO OF LEFT UNDO PAGES */
+ tmp1 = tmp1 & (cundopagesize - 1); /* 1 MBYTE PAGE WINDOW IN MEMORY */
+ fsConnectptr.i = cactiveOpenUndoFsPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ seizeFsOpRec(signal);
+ initFsOpRec(signal);
+ fsOpptr.p->fsOpstate = WAIT_WRITE_UNDO;
+ fsOpptr.p->fsOpMemPage = tundoPageId; /* RECORD MEMORY PAGE WRITTEN */
+ if (clblPageCounter >= (4 * tmp2)) {
+ jam();
+ clblPageCounter = clblPageCounter - (4 * tmp2);
+ } else {
+ jam();
+ clblPageOver = clblPageOver + ((4 * tmp2) - clblPageCounter);
+ clblPageCounter = 0;
+ }//if
+ /* ************************ */
+ /* FSWRITEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsOpptr.i;
+ /* FLAG = START MEM PAGES, START FILE PAGES */
+ /* SYNC FILE AFTER WRITING */
+ signal->theData[3] = 0x11;
+ signal->theData[4] = ZUNDOPAGE_BASE_ADD;
+ /* NO OF UNDO PAGES */
+ signal->theData[5] = tmp2;
+ /* FIRST MEMORY PAGE */
+ signal->theData[6] = tmp1;
+ /* ACTIVE PAGE AT UNDO FILE */
+ signal->theData[7] = cactiveUndoFilePage;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+
+ return;
+}//Dbacc::checkSyncUndoPagesLab()
+
+void Dbacc::checkSendLcpConfLab(Signal* signal)
+{
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ ndbrequire(lcpConnectptr.p->lcpstate == LCP_ACTIVE);
+ switch (lcpConnectptr.p->syncUndopageState) {
+ case WAIT_ONE_CONF:
+ jam();
+ lcpConnectptr.p->syncUndopageState = WAIT_NOTHING;
+ break;
+ case WAIT_TWO_CONF:
+ jam();
+ lcpConnectptr.p->syncUndopageState = WAIT_ONE_CONF;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ lcpConnectptr.p->noOfLcpConf++;
+ ndbrequire(lcpConnectptr.p->noOfLcpConf <= 2);
+ fragrecptr.p->fragState = ACTIVEFRAG;
+ rlpPageptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(rlpPageptr, cpagesize, page8);
+ releaseLcpPage(signal);
+ fragrecptr.p->zeroPagePtr = RNIL;
+ for (Uint32 i = 0; i < ZWRITEPAGESIZE; i++) {
+ jam();
+ if (fragrecptr.p->datapages[i] != RNIL) {
+ jam();
+ rlpPageptr.i = fragrecptr.p->datapages[i];
+ ptrCheckGuard(rlpPageptr, cpagesize, page8);
+ releaseLcpPage(signal);
+ fragrecptr.p->datapages[i] = RNIL;
+ }//if
+ }//for
+ signal->theData[0] = fragrecptr.p->lcpLqhPtr;
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_ACC_LCPCONF, signal, 1, JBB);
+ if (lcpConnectptr.p->noOfLcpConf == 2) {
+ jam();
+ releaseLcpConnectRec(signal);
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ rootfragrecptr.p->rootState = ACTIVEROOT;
+ }//if
+}//Dbacc::checkSendLcpConfLab()
+
+/* ******************--------------------------------------------------------------- */
+/* ACC_CONTOPREQ */
+/* SENDER: LQH, LEVEL B */
+/* ENTER ACC_CONTOPREQ WITH */
+/* LCP_CONNECTPTR */
+/* TMP1 LOCAL FRAG ID */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACC_CONTOPREQ COMMIT TRANSACTION */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execACC_CONTOPREQ(Signal* signal)
+{
+ Uint32 tcorLocalFrag;
+
+ jamEntry();
+ lcpConnectptr.i = signal->theData[0];
+ /* CONNECTION PTR */
+ tcorLocalFrag = signal->theData[1];
+ /* LOCAL FRAG ID */
+ tresult = 0;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ ndbrequire(lcpConnectptr.p->lcpstate == LCP_ACTIVE);
+ rootfragrecptr.i = lcpConnectptr.p->rootrecptr;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->fragmentid[0] == tcorLocalFrag) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ } else {
+ ndbrequire(rootfragrecptr.p->fragmentid[1] == tcorLocalFrag);
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ }//if
+ operationRecPtr.i = fragrecptr.p->firstWaitInQueOp;
+ fragrecptr.p->sentWaitInQueOp = RNIL;
+ fragrecptr.p->stopQueOp = ZFALSE;
+ while (operationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ if (operationRecPtr.p->opState == WAIT_EXE_OP) {
+ jam();
+ //------------------------------------------------------------
+ // Indicate that we are now a normal waiter in the queue. We
+ // will remove the operation from the queue as part of starting
+ // operation again.
+ //------------------------------------------------------------
+ operationRecPtr.p->opState = WAIT_IN_QUEUE;
+ executeNextOperation(signal);
+ }//if
+ operationRecPtr.i = operationRecPtr.p->nextQueOp;
+ }//while
+ signal->theData[0] = fragrecptr.p->lcpLqhPtr;
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_ACC_CONTOPCONF, signal, 1, JBA);
+ return; /* ALL QUEUED OPERATION ARE RESTARTED IF NEEDED. */
+}//Dbacc::execACC_CONTOPREQ()
+
+/* ******************--------------------------------------------------------------- */
+/* END_LCPREQ END OF LOCAL CHECK POINT */
+/* ENTER END_LCPREQ WITH SENDER: LQH, LEVEL B */
+/* CLQH_PTR, LQH PTR */
+/* CLQH_BLOCK_REF LQH BLOCK REF */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* END_LCPREQ PERFORM A LOCAL CHECK POINT */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execEND_LCPREQ(Signal* signal)
+{
+ jamEntry();
+ clqhPtr = signal->theData[0];
+ /* LQH PTR */
+ clqhBlockRef = signal->theData[1];
+ /* LQH BLOCK REF */
+ tresult = 0;
+ fsConnectptr.i = cactiveOpenUndoFsPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ fsConnectptr.p->fsState = WAIT_CLOSE_UNDO; /* CLOSE FILE AFTER WRITTING */
+ /* ************************ */
+ /* FSCLOSEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = ZFALSE;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ /* FLAG = 0, DO NOT DELETE FILE */
+ cactiveUndoFileVersion = RNIL;
+ cactiveOpenUndoFsPtr = RNIL;
+ /* ************************ */
+ /* END_LCPCONF */
+ /* ************************ */
+ signal->theData[0] = clqhPtr;
+ sendSignal(clqhBlockRef, GSN_END_LCPCONF, signal, 1, JBB);
+ return;
+}//Dbacc::execEND_LCPREQ()
+
+/*-----------------------------------------------------------------*/
+/* WHEN WE COPY THE PAGE WE ALSO WRITE THE ELEMENT HEADER AS */
+/* UNLOCKED IF THEY ARE CURRENTLY LOCKED. */
+/*-----------------------------------------------------------------*/
+void Dbacc::lcpCopyPage(Signal* signal)
+{
+ Uint32 tlcnNextContainer;
+ Uint32 tlcnTmp;
+ Uint32 tlcnConIndex;
+ Uint32 tlcnIndex;
+ Uint32 Tmp1;
+ Uint32 Tmp2;
+ Uint32 Tmp3;
+ Uint32 Tmp4;
+ Uint32 Ti;
+ Uint32 Tchs;
+ Uint32 Tlimit;
+
+ Tchs = 0;
+ lupPageptr.p = lcnCopyPageptr.p;
+ lcnPageptr.p->word32[ZPOS_CHECKSUM] = Tchs;
+ for (Ti = 0; Ti < 32 ; Ti++) {
+ Tlimit = 16 + (Ti << 6);
+ for (tlcnTmp = (Ti << 6); tlcnTmp < Tlimit; tlcnTmp ++) {
+ Tmp1 = lcnPageptr.p->word32[tlcnTmp];
+ Tmp2 = lcnPageptr.p->word32[tlcnTmp + 16];
+ Tmp3 = lcnPageptr.p->word32[tlcnTmp + 32];
+ Tmp4 = lcnPageptr.p->word32[tlcnTmp + 48];
+
+ lcnCopyPageptr.p->word32[tlcnTmp] = Tmp1;
+ lcnCopyPageptr.p->word32[tlcnTmp + 16] = Tmp2;
+ lcnCopyPageptr.p->word32[tlcnTmp + 32] = Tmp3;
+ lcnCopyPageptr.p->word32[tlcnTmp + 48] = Tmp4;
+
+ Tchs = Tchs ^ Tmp1;
+ Tchs = Tchs ^ Tmp2;
+ Tchs = Tchs ^ Tmp3;
+ Tchs = Tchs ^ Tmp4;
+ }//for
+ }//for
+ tlcnChecksum = Tchs;
+ if (((lcnCopyPageptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) == ZNORMAL_PAGE_TYPE) {
+ jam();
+ /*-----------------------------------------------------------------*/
+ /* TAKE CARE OF ALL 64 BUFFERS ADDRESSED BY ALGORITHM IN */
+ /* FIRST PAGE. IF THEY ARE EMPTY THEY STILL HAVE A CONTAINER */
+ /* HEADER OF 2 WORDS. */
+ /*-----------------------------------------------------------------*/
+ tlcnConIndex = ZHEAD_SIZE;
+ tlupForward = 1;
+ for (tlcnIndex = 0; tlcnIndex <= ZNO_CONTAINERS - 1; tlcnIndex++) {
+ tlupIndex = tlcnConIndex;
+ tlupElemIndex = tlcnConIndex + ZCON_HEAD_SIZE;
+ lcpUpdatePage(signal);
+ tlcnConIndex = tlcnConIndex + ZBUF_SIZE;
+ }//for
+ }//if
+ /*-----------------------------------------------------------------*/
+ /* TAKE CARE OF ALL USED BUFFERS ON THE LEFT SIDE. */
+ /*-----------------------------------------------------------------*/
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[ZPOS_EMPTY_LIST] >> 23) & 0x7f;
+ while (tlcnNextContainer < ZEMPTYLIST) {
+ tlcnConIndex = (tlcnNextContainer << ZSHIFT_PLUS) - (tlcnNextContainer << ZSHIFT_MINUS);
+ tlcnConIndex = tlcnConIndex + ZHEAD_SIZE;
+ tlupIndex = tlcnConIndex;
+ tlupElemIndex = tlcnConIndex + ZCON_HEAD_SIZE;
+ tlupForward = 1;
+ lcpUpdatePage(signal);
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[tlcnConIndex] >> 11) & 0x7f;
+ }//while
+ if (tlcnNextContainer == ZEMPTYLIST) {
+ jam();
+ /*empty*/;
+ } else {
+ jam();
+ sendSystemerror(signal);
+ return;
+ }//if
+ /*-----------------------------------------------------------------*/
+ /* TAKE CARE OF ALL USED BUFFERS ON THE RIGHT SIDE. */
+ /*-----------------------------------------------------------------*/
+ tlupForward = cminusOne;
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[ZPOS_EMPTY_LIST] >> 16) & 0x7f;
+ while (tlcnNextContainer < ZEMPTYLIST) {
+ tlcnConIndex = (tlcnNextContainer << ZSHIFT_PLUS) - (tlcnNextContainer << ZSHIFT_MINUS);
+ tlcnConIndex = tlcnConIndex + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ tlupIndex = tlcnConIndex;
+ tlupElemIndex = tlcnConIndex - 1;
+ lcpUpdatePage(signal);
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[tlcnConIndex] >> 11) & 0x7f;
+ }//while
+ if (tlcnNextContainer == ZEMPTYLIST) {
+ jam();
+ /*empty*/;
+ } else {
+ jam();
+ sendSystemerror(signal);
+ return;
+ }//if
+ lcnCopyPageptr.p->word32[ZPOS_CHECKSUM] = tlcnChecksum;
+}//Dbacc::lcpCopyPage()
+
+/* --------------------------------------------------------------------------------- */
+/* THIS SUBROUTINE GOES THROUGH ONE CONTAINER TO CHECK FOR LOCKED ELEMENTS AND */
+/* UPDATING THEM TO ENSURE ALL ELEMENTS ARE UNLOCKED ON DISK. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::lcpUpdatePage(Signal* signal)
+{
+ OperationrecPtr lupOperationRecPtr;
+ Uint32 tlupElemHead;
+ Uint32 tlupElemLen;
+ Uint32 tlupElemStep;
+ Uint32 tlupConLen;
+
+ tlupConLen = lupPageptr.p->word32[tlupIndex] >> 26;
+ tlupElemLen = fragrecptr.p->elementLength;
+ tlupElemStep = tlupForward * tlupElemLen;
+ while (tlupConLen > ZCON_HEAD_SIZE) {
+ jam();
+ tlupElemHead = lupPageptr.p->word32[tlupElemIndex];
+ if (ElementHeader::getLocked(tlupElemHead)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* WHEN CHANGING THE ELEMENT HEADER WE ALSO HAVE TO UPDATE THE CHECKSUM. IN */
+ /* DOING THIS WE USE THE FORMULA (A XOR B) XOR B = A WHICH MEANS THAT IF WE */
+ /* XOR SOMETHING TWICE WITH THE SAME OPERAND THEN WE RETURN TO THE ORIGINAL */
+ /* VALUE. THEN WE ALSO HAVE TO USE THE NEW ELEMENT HEADER IN THE CHECKSUM */
+ /* CALCULATION. */
+ /* --------------------------------------------------------------------------------- */
+ tlcnChecksum = tlcnChecksum ^ tlupElemHead;
+ lupOperationRecPtr.i = ElementHeader::getOpPtrI(tlupElemHead);
+ ptrCheckGuard(lupOperationRecPtr, coprecsize, operationrec);
+ const Uint32 hv = lupOperationRecPtr.p->hashvaluePart;
+ tlupElemHead = ElementHeader::setUnlocked(hv , 0);
+ arrGuard(tlupElemIndex, 2048);
+ lupPageptr.p->word32[tlupElemIndex] = tlupElemHead;
+ tlcnChecksum = tlcnChecksum ^ tlupElemHead;
+ }//if
+ tlupConLen = tlupConLen - tlupElemLen;
+ tlupElemIndex = tlupElemIndex + tlupElemStep;
+ }//while
+ if (tlupConLen < ZCON_HEAD_SIZE) {
+ jam();
+ sendSystemerror(signal);
+ }//if
+}//Dbacc::lcpUpdatePage()
+
+/*-----------------------------------------------------------------*/
+// At a system restart we check that the page do not contain any
+// locks that hinder the system restart procedure.
+/*-----------------------------------------------------------------*/
+void Dbacc::srCheckPage(Signal* signal)
+{
+ Uint32 tlcnNextContainer;
+ Uint32 tlcnConIndex;
+ Uint32 tlcnIndex;
+
+ lupPageptr.p = lcnCopyPageptr.p;
+ if (((lcnCopyPageptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) == ZNORMAL_PAGE_TYPE) {
+ jam();
+ /*-----------------------------------------------------------------*/
+ /* TAKE CARE OF ALL 64 BUFFERS ADDRESSED BY ALGORITHM IN */
+ /* FIRST PAGE. IF THEY ARE EMPTY THEY STILL HAVE A CONTAINER */
+ /* HEADER OF 2 WORDS. */
+ /*-----------------------------------------------------------------*/
+ tlcnConIndex = ZHEAD_SIZE;
+ tlupForward = 1;
+ for (tlcnIndex = 0; tlcnIndex <= ZNO_CONTAINERS - 1; tlcnIndex++) {
+ tlupIndex = tlcnConIndex;
+ tlupElemIndex = tlcnConIndex + ZCON_HEAD_SIZE;
+ srCheckContainer(signal);
+ if (tresult != 0) {
+ jam();
+ return;
+ }//if
+ tlcnConIndex = tlcnConIndex + ZBUF_SIZE;
+ }//for
+ }//if
+ /*-----------------------------------------------------------------*/
+ /* TAKE CARE OF ALL USED BUFFERS ON THE LEFT SIDE. */
+ /*-----------------------------------------------------------------*/
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[ZPOS_EMPTY_LIST] >> 23) & 0x7f;
+ while (tlcnNextContainer < ZEMPTYLIST) {
+ tlcnConIndex = (tlcnNextContainer << ZSHIFT_PLUS) - (tlcnNextContainer << ZSHIFT_MINUS);
+ tlcnConIndex = tlcnConIndex + ZHEAD_SIZE;
+ tlupIndex = tlcnConIndex;
+ tlupElemIndex = tlcnConIndex + ZCON_HEAD_SIZE;
+ tlupForward = 1;
+ srCheckContainer(signal);
+ if (tresult != 0) {
+ jam();
+ return;
+ }//if
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[tlcnConIndex] >> 11) & 0x7f;
+ }//while
+ if (tlcnNextContainer == ZEMPTYLIST) {
+ jam();
+ /*empty*/;
+ } else {
+ jam();
+ tresult = 4;
+ return;
+ }//if
+ /*-----------------------------------------------------------------*/
+ /* TAKE CARE OF ALL USED BUFFERS ON THE RIGHT SIDE. */
+ /*-----------------------------------------------------------------*/
+ tlupForward = cminusOne;
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[ZPOS_EMPTY_LIST] >> 16) & 0x7f;
+ while (tlcnNextContainer < ZEMPTYLIST) {
+ tlcnConIndex = (tlcnNextContainer << ZSHIFT_PLUS) - (tlcnNextContainer << ZSHIFT_MINUS);
+ tlcnConIndex = tlcnConIndex + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE);
+ tlupIndex = tlcnConIndex;
+ tlupElemIndex = tlcnConIndex - 1;
+ srCheckContainer(signal);
+ if (tresult != 0) {
+ jam();
+ return;
+ }//if
+ tlcnNextContainer = (lcnCopyPageptr.p->word32[tlcnConIndex] >> 11) & 0x7f;
+ }//while
+ if (tlcnNextContainer == ZEMPTYLIST) {
+ jam();
+ /*empty*/;
+ } else {
+ jam();
+ tresult = 4;
+ return;
+ }//if
+}//Dbacc::srCheckPage()
+
+/* --------------------------------------------------------------------------------- */
+/* THIS SUBROUTINE GOES THROUGH ONE CONTAINER TO CHECK FOR LOCKED ELEMENTS AND */
+/* UPDATING THEM TO ENSURE ALL ELEMENTS ARE UNLOCKED ON DISK. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::srCheckContainer(Signal* signal)
+{
+ Uint32 tlupElemLen;
+ Uint32 tlupElemStep;
+ Uint32 tlupConLen;
+
+ tlupConLen = lupPageptr.p->word32[tlupIndex] >> 26;
+ tlupElemLen = fragrecptr.p->elementLength;
+ tlupElemStep = tlupForward * tlupElemLen;
+ while (tlupConLen > ZCON_HEAD_SIZE) {
+ jam();
+ const Uint32 tlupElemHead = lupPageptr.p->word32[tlupElemIndex];
+ if (ElementHeader::getLocked(tlupElemHead)){
+ jam();
+ //-------------------------------------------------------
+ // This is absolutely undesirable. We have a lock remaining
+ // after the system restart. We send a crash signal that will
+ // enter the trace file.
+ //-------------------------------------------------------
+ tresult = 2;
+ return;
+ }//if
+ tlupConLen = tlupConLen - tlupElemLen;
+ tlupElemIndex = tlupElemIndex + tlupElemStep;
+ }//while
+ if (tlupConLen < ZCON_HEAD_SIZE) {
+ jam();
+ tresult = 3;
+ }//if
+ return;
+}//Dbacc::srCheckContainer()
+
+/* ------------------------------------------------------------------------- */
+/* CHECK_UNDO_PAGES */
+/* DESCRIPTION: CHECKS WHEN A PAGE OR A GROUP OF UNDO PAGES IS FILLED.WHEN */
+/* A PAGE IS FILLED, CUNDOPOSITION WILL BE UPDATE, THE NEW */
+/* POSITION IS THE BEGNING OF THE NEXT UNDO PAGE. */
+/* IN CASE THAT A GROUP IS FILLED THE PAGES ARE SENT TO DISK, */
+/* AND A NEW GROUP IS CHOSEN. */
+/* ------------------------------------------------------------------------- */
+void Dbacc::checkUndoPages(Signal* signal)
+{
+
+ fragrecptr.p->prevUndoposition = cundoposition;
+ cprevUndoaddress = cundoposition;
+
+ // Calculate active undo page id
+ Uint32 tundoPageId = cundoposition >> ZUNDOPAGEINDEXBITS;
+
+ /**
+ * WE WILL WRITE UNTIL WE HAVE ABOUT 8 KBYTE REMAINING ON THE 32 KBYTE
+ * PAGE. THIS IS TO ENSURE THAT WE DO NOT HAVE ANY UNDO LOG RECORDS THAT PASS
+ * A PAGE BOUNDARIE. THIS SIMPLIFIES CODING TRADING SOME INEFFICIENCY.
+ */
+ static const Uint32 ZMAXUNDOPAGEINDEX = 7100;
+ if (tundoindex < ZMAXUNDOPAGEINDEX) {
+ jam();
+ cundoposition = (tundoPageId << ZUNDOPAGEINDEXBITS) + tundoindex;
+ return;
+ }//if
+
+ /**
+ * WE CHECK IF MORE THAN 1 MBYTE OF WRITES ARE OUTSTANDING TO THE UNDO FILE.
+ * IF SO WE HAVE TO CRASH SINCE WE HAVE NO MORE SPACE TO WRITE UNDO LOG
+ * RECORDS IN
+ */
+ Uint16 nextUndoPageId = tundoPageId + 1;
+ updateUndoPositionPage(signal, nextUndoPageId << ZUNDOPAGEINDEXBITS);
+
+ if ((tundoPageId & (ZWRITE_UNDOPAGESIZE - 1)) == (ZWRITE_UNDOPAGESIZE - 1)) {
+ jam();
+ /* ---------- SEND A GROUP OF UNDO PAGES TO DISK --------- */
+ fsConnectptr.i = cactiveOpenUndoFsPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ Uint32 tcupTmp1 = (tundoPageId - ZWRITE_UNDOPAGESIZE) + 1;
+ tcupTmp1 = tcupTmp1 & (cundopagesize - 1); /* 1 MBYTE PAGE WINDOW */
+ seizeFsOpRec(signal);
+ initFsOpRec(signal);
+ fsOpptr.p->fsOpstate = WAIT_WRITE_UNDO_EXIT;
+ fsOpptr.p->fsOpMemPage = tundoPageId;
+ fragrecptr.p->nrWaitWriteUndoExit++;
+ if (clblPageCounter >= 8) {
+ jam();
+ clblPageCounter = clblPageCounter - 8;
+ } else {
+ jam();
+ clblPageOver = clblPageOver + (8 - clblPageCounter);
+ clblPageCounter = 0;
+ }//if
+ /* ************************ */
+ /* FSWRITEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsOpptr.i;
+ signal->theData[3] = 0x1;
+ /* FLAG = START MEM PAGES, START FILE PAGES */
+ signal->theData[4] = ZUNDOPAGE_BASE_ADD;
+ signal->theData[5] = ZWRITE_UNDOPAGESIZE;
+ signal->theData[6] = tcupTmp1;
+ signal->theData[7] = cactiveUndoFilePage;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+ cactiveUndoFilePage = cactiveUndoFilePage + ZWRITE_UNDOPAGESIZE;
+ }//if
+}//Dbacc::checkUndoPages()
+
+/* --------------------------------------------------------------------------------- */
+/* UNDO_WRITING_PROCESS */
+/* INPUT: FRAGRECPTR, CUNDO_ELEM_INDEX, DATAPAGEPTR, CUNDOINFOLENGTH */
+/* DESCRIPTION: WHEN THE PROCESS OF CREATION LOCAL CHECK POINT HAS */
+/* STARTED. IF THE ACTIVE PAGE IS NOT ALREADY SENT TO DISK, THE */
+/* OLD VALUE OF THE ITEM WHICH IS GOING TO BE CHECKED IS STORED ON */
+/* THE ACTIVE UNDO PAGE. INFORMATION ABOUT UNDO PROCESS IN THE */
+/* BLOCK AND IN THE FRAGMENT WILL BE UPDATED. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::undoWritingProcess(Signal* signal)
+{
+ const Uint32 tactivePageDir = datapageptr.p->word32[ZPOS_PAGE_ID];
+ const Uint32 tpageType = (datapageptr.p->word32[ZPOS_EMPTY_LIST] >> ZPOS_PAGE_TYPE_BIT) & 3;
+ if (fragrecptr.p->fragState == LCP_SEND_PAGES) {
+ if (tpageType == ZNORMAL_PAGE_TYPE) {
+ /* --------------------------------------------------------------------------- */
+ /* HANDLING OF LOG OF NORMAL PAGES DURING WRITE OF NORMAL PAGES. */
+ /* --------------------------------------------------------------------------- */
+ if (tactivePageDir < fragrecptr.p->lcpDirIndex) {
+ jam();
+ /* ------------------------------------------------------------------- */
+ /* THIS PAGE HAS ALREADY BEEN WRITTEN IN THE LOCAL CHECKPOINT. */
+ /* ------------------------------------------------------------------- */
+ /*empty*/;
+ } else {
+ if (tactivePageDir >= fragrecptr.p->lcpMaxDirIndex) {
+ jam();
+ /* --------------------------------------------------------------------------- */
+ /* OBVIOUSLY THE FRAGMENT HAS EXPANDED SINCE THE START OF THE LOCAL CHECKPOINT.*/
+ /* WE NEED NOT LOG ANY UPDATES OF PAGES THAT DID NOT EXIST AT START OF LCP. */
+ /* --------------------------------------------------------------------------- */
+ /*empty*/;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------------- */
+ /* IN ALL OTHER CASES WE HAVE TO WRITE TO THE UNDO LOG. */
+ /* --------------------------------------------------------------------------- */
+ undopageptr.i = (cundoposition >> ZUNDOPAGEINDEXBITS) & (cundopagesize - 1);
+ ptrAss(undopageptr, undopage);
+ theadundoindex = cundoposition & ZUNDOPAGEINDEX_MASK;
+ tundoindex = theadundoindex + ZUNDOHEADSIZE;
+ writeUndoHeader(signal, tactivePageDir, UndoHeader::ZPAGE_INFO);
+ tundoElemIndex = cundoElemIndex;
+ writeUndoDataInfo(signal);
+ checkUndoPages(signal);
+ }//if
+ }//if
+ } else if (tpageType == ZOVERFLOW_PAGE_TYPE) {
+ /* --------------------------------------------------------------------------------- */
+ /* OVERFLOW PAGE HANDLING DURING WRITE OF NORMAL PAGES. */
+ /* --------------------------------------------------------------------------------- */
+ if (tactivePageDir >= fragrecptr.p->lcpMaxOverDirIndex) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* OBVIOUSLY THE FRAGMENT HAS EXPANDED THE NUMBER OF OVERFLOW PAGES SINCE THE */
+ /* START OF THE LOCAL CHECKPOINT. WE NEED NOT LOG ANY UPDATES OF PAGES THAT DID*/
+ /* NOT EXIST AT START OF LCP. */
+ /* --------------------------------------------------------------------------------- */
+ /*empty*/;
+ } else {
+ jam();
+ undopageptr.i = (cundoposition >> ZUNDOPAGEINDEXBITS) & (cundopagesize - 1);
+ ptrAss(undopageptr, undopage);
+ theadundoindex = cundoposition & ZUNDOPAGEINDEX_MASK;
+ tundoindex = theadundoindex + ZUNDOHEADSIZE;
+ writeUndoHeader(signal, tactivePageDir, UndoHeader::ZOVER_PAGE_INFO);
+ tundoElemIndex = cundoElemIndex;
+ writeUndoDataInfo(signal);
+ checkUndoPages(signal);
+ }//if
+ } else {
+ jam();
+ /* --------------------------------------------------------------------------- */
+ /* ONLY PAGE INFO AND OVERFLOW PAGE INFO CAN BE LOGGED BY THIS ROUTINE. A */
+ /* SERIOUS ERROR. */
+ /* --------------------------------------------------------------------------- */
+ sendSystemerror(signal);
+ }
+ } else {
+ if (fragrecptr.p->fragState == LCP_SEND_OVER_PAGES) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* DURING WRITE OF OVERFLOW PAGES WE NEED NOT WORRY ANYMORE ABOUT NORMAL PAGES.*/
+ /* --------------------------------------------------------------------------------- */
+ if (tpageType == ZOVERFLOW_PAGE_TYPE) {
+ if (tactivePageDir < fragrecptr.p->lcpDirIndex) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THIS PAGE HAS ALREADY BEEN WRITTEN IN THE LOCAL CHECKPOINT. */
+ /* --------------------------------------------------------------------------------- */
+ /*empty*/;
+ } else {
+ if (tactivePageDir >= fragrecptr.p->lcpMaxOverDirIndex) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* OBVIOUSLY THE FRAGMENT HAS EXPANDED THE NUMBER OF OVERFLOW PAGES SINCE THE */
+ /* START OF THE LOCAL CHECKPOINT. WE NEED NOT LOG ANY UPDATES OF PAGES THAT DID*/
+ /* NOT EXIST AT START OF LCP. */
+ /* --------------------------------------------------------------------------------- */
+ /*empty*/;
+ } else {
+ jam();
+ undopageptr.i = (cundoposition >> ZUNDOPAGEINDEXBITS) & (cundopagesize - 1);
+ ptrAss(undopageptr, undopage);
+ theadundoindex = cundoposition & ZUNDOPAGEINDEX_MASK;
+ tundoindex = theadundoindex + ZUNDOHEADSIZE;
+ writeUndoHeader(signal, tactivePageDir, UndoHeader::ZOVER_PAGE_INFO);
+ tundoElemIndex = cundoElemIndex;
+ writeUndoDataInfo(signal);
+ checkUndoPages(signal);
+ }//if
+ }//if
+ }
+ }//if
+ }//if
+}//Dbacc::undoWritingProcess()
+
+/* --------------------------------------------------------------------------------- */
+/* OTHER STATES MEANS THAT WE HAVE ALREADY WRITTEN ALL PAGES BUT NOT YET RESET */
+/* THE CREATE_LCP FLAG. */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* WRITE_UNDO_DATA_INFO */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::writeUndoDataInfo(Signal* signal)
+{
+ Uint32 twudiIndex;
+ Uint32 guard22;
+
+ guard22 = cundoinfolength;
+ arrGuard((tundoindex + guard22 - 1), 8192);
+ arrGuard((tundoElemIndex + guard22 - 1), 2048);
+ for (twudiIndex = 1; twudiIndex <= guard22; twudiIndex++) {
+ undopageptr.p->undoword[tundoindex] = datapageptr.p->word32[tundoElemIndex];
+ tundoindex++;
+ tundoElemIndex++;
+ }//for
+}//Dbacc::writeUndoDataInfo()
+
+/* --------------------------------------------------------------------------------- */
+/* WRITE_UNDO_HEADER */
+/* THE HEAD OF UNDO ELEMENT IS 24 BYTES AND CONTAINS THE FOLLOWING INFORMATION: */
+/* TABLE IDENTITY 32 BITS */
+/* ROOT FRAGMENT IDENTITY 32 BITS */
+/* LOCAL FRAGMENT IDENTITY 32 BITS */
+/* LENGTH OF ELEMENT INF0 (BIT 31 - 18) 14 BITS */
+/* INFO TYPE (BIT 17 - 14) 4 BITS */
+/* PAGE INDEX OF THE FIRST FIELD IN THE FRAGMENT (BIT 13 - 0) 14 BITS */
+/* DIRECTORY INDEX OF THE PAGE IN THE FRAGMENT 32 BITS */
+/* ADDRESS OF THE PREVIOUS ELEMENT OF THE FRAGMENT 64 BITS */
+/* ADDRESS OF THE PREVIOUS ELEMENT IN THE UNDO PAGES 64 BITS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::writeUndoHeader(Signal* signal,
+ Uint32 logicalPageId,
+ UndoHeader::UndoHeaderType pageType)
+{
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ arrGuard(theadundoindex + 6, 8192);
+
+ // Set the structpointer to point at the undo page at the right address.
+ UndoHeader * const & undoHeaderPtr =
+ (UndoHeader *) &undopageptr.p->undoword[theadundoindex];
+
+ undoHeaderPtr->tableId = rootfragrecptr.p->mytabptr;
+ undoHeaderPtr->rootFragId = rootfragrecptr.p->fragmentid[0] >> 1;
+ undoHeaderPtr->localFragId = fragrecptr.p->myfid;
+ ndbrequire((undoHeaderPtr->localFragId >> 1) == undoHeaderPtr->rootFragId);
+ Uint32 Ttmp = cundoinfolength;
+ Ttmp = (Ttmp << 4) + pageType;
+ Ttmp = Ttmp << 14;
+ undoHeaderPtr->variousInfo = Ttmp + cundoElemIndex;
+ undoHeaderPtr->logicalPageId = logicalPageId;
+ undoHeaderPtr->prevUndoAddressForThisFrag = fragrecptr.p->prevUndoposition;
+ undoHeaderPtr->prevUndoAddress = cprevUndoaddress;
+}//Dbacc::writeUndoHeader()
+
+/* --------------------------------------------------------------------------------- */
+/* WRITE_UNDO_OP_INFO */
+/* FOR A LOCKED ELEMENT, OPERATION TYPE, UNDO OF ELEMENT HEADER AND THE LENGTH OF*/
+/* THE TUPLE KEY HAVE TO BE SAVED IN UNDO PAGES. IN THIS CASE AN UNDO ELEMENT */
+/* INCLUDES THE FLLOWING ITEMS. */
+/* OPERATION TYPE 32 BITS */
+/* HASH VALUE 32 BITS */
+/* LENGTH OF THE TUPLE = N 32 BITS */
+/* TUPLE KEYS N * 32 BITS */
+/* */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::writeUndoOpInfo(Signal* signal)
+{
+ Page8Ptr locPageptr;
+
+ arrGuard((tundoindex + 3), 8192);
+ undopageptr.p->undoword[tundoindex] = operationRecPtr.p->operation;
+ undopageptr.p->undoword[tundoindex + 1] = operationRecPtr.p->hashValue;
+ undopageptr.p->undoword[tundoindex + 2] = operationRecPtr.p->tupkeylen;
+ tundoindex = tundoindex + 3;
+ // log localkey1
+ locPageptr.i = operationRecPtr.p->elementPage;
+ ptrCheckGuard(locPageptr, cpagesize, page8);
+ Uint32 Tforward = operationRecPtr.p->elementIsforward;
+ Uint32 TelemPtr = operationRecPtr.p->elementPointer;
+ TelemPtr += Tforward; // ZELEM_HEAD_SIZE
+ arrGuard(tundoindex+1, 8192);
+ undopageptr.p->undoword[tundoindex] = locPageptr.p->word32[TelemPtr];
+ tundoindex++;
+ cundoinfolength = ZOP_HEAD_INFO_LN + 1;
+}//Dbacc::writeUndoOpInfo()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF LOCAL CHECKPOINT MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* SYSTEM RESTART MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* SR_FRAGIDREQ REQUEST FOR RESTART OF A FRAGMENT */
+/* SENDER: LQH, LEVEL B */
+/* ENTER SR_FRAGIDREQ WITH */
+/* TUSERPTR, LQH CONNECTION PTR */
+/* TUSERBLOCKREF, LQH BLOCK REFERENCE */
+/* TCHECKPOINTID, THE CHECKPOINT NUMBER TO USE */
+/* (E.G. 1,2 OR 3) */
+/* TABPTR, TABLE ID = TABLE RECORD POINTER */
+/* TFID, ROOT FRAGMENT ID */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* SR_FRAGIDREQ REQUEST FOR LIST OF STOPED OPERATION */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execSR_FRAGIDREQ(Signal* signal)
+{
+ jamEntry();
+ tuserptr = signal->theData[0]; /* LQH CONNECTION PTR */
+ tuserblockref = signal->theData[1]; /* LQH BLOCK REFERENCE */
+ tcheckpointid = signal->theData[2]; /* THE CHECKPOINT NUMBER TO USE */
+ /* (E.G. 1,2 OR 3) */
+ tabptr.i = signal->theData[3];
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+ /* TABLE ID = TABLE RECORD POINTER */
+ tfid = signal->theData[4]; /* ROOT FRAGMENT ID */
+ tresult = 0; /* 0= FALSE,1= TRUE,> ZLIMIT_OF_ERROR =ERRORCODE */
+ seizeLcpConnectRec(signal);
+ initLcpConnRec(signal);
+
+ ndbrequire(getrootfragmentrec(signal, rootfragrecptr, tfid));
+ rootfragrecptr.p->lcpPtr = lcpConnectptr.i;
+ lcpConnectptr.p->rootrecptr = rootfragrecptr.i;
+ lcpConnectptr.p->localCheckPid = tcheckpointid;
+ for (Uint32 i = 0; i < 2; i++) {
+ Page8Ptr zeroPagePtr;
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[i];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ seizeLcpPage(zeroPagePtr);
+ fragrecptr.p->zeroPagePtr = zeroPagePtr.i;
+ }//for
+
+ /* ---------------------------OPEN THE DATA FILE WHICH BELONGS TO TFID AND TCHECK POINT ---- */
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ tfid = rootfragrecptr.p->fragmentid[0];
+ tmp = 0;
+ srOpenDataFileLoopLab(signal);
+
+ return;
+}//Dbacc::execSR_FRAGIDREQ()
+
+void Dbacc::srOpenDataFileLoopLab(Signal* signal)
+{
+ /* D6 AT FSOPENREQ. FILE TYPE = .DATA */
+ tmp1 = 0x010003ff; /* VERSION OF FILENAME = 1 */
+ tmp2 = 0x0; /* D7 DON'T CREATE, READ ONLY */
+ ndbrequire(cfsFirstfreeconnect != RNIL);
+ seizeFsConnectRec(signal);
+
+ fragrecptr.p->fsConnPtr = fsConnectptr.i;
+ fsConnectptr.p->fragrecPtr = fragrecptr.i;
+ fsConnectptr.p->fsState = WAIT_OPEN_DATA_FILE_FOR_READ;
+ fsConnectptr.p->activeFragId = tmp; /* LOCAL FRAG INDEX */
+ /* ************************ */
+ /* FSOPENREQ */
+ /* ************************ */
+ signal->theData[0] = cownBlockref;
+ signal->theData[1] = fsConnectptr.i;
+ signal->theData[2] = rootfragrecptr.p->mytabptr; /* TABLE IDENTITY */
+ signal->theData[3] = tfid; /* FRAGMENT IDENTITY */
+ signal->theData[4] = lcpConnectptr.p->localCheckPid; /* CHECKPOINT ID */
+ signal->theData[5] = tmp1;
+ signal->theData[6] = tmp2;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ return;
+}//Dbacc::srOpenDataFileLoopLab()
+
+void Dbacc::srFsOpenConfLab(Signal* signal)
+{
+ fsConnectptr.p->fsState = WAIT_READ_PAGE_ZERO;
+ /* ------------------------ READ ZERO PAGE ---------- */
+ fragrecptr.i = fsConnectptr.p->fragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = 0x0;
+ /* FLAG = LIST MEM PAGES, LIST FILE PAGES */
+ signal->theData[4] = ZPAGE8_BASE_ADD;
+ signal->theData[5] = 1; /* NO OF PAGES */
+ signal->theData[6] = fragrecptr.p->zeroPagePtr; /* ZERO PAGE */
+ signal->theData[7] = 0; /* PAGE ZERO OF THE DATA FILE */
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+ return;
+}//Dbacc::srFsOpenConfLab()
+
+void Dbacc::srReadPageZeroLab(Signal* signal)
+{
+ Page8Ptr srzPageptr;
+
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ fragrecptr.p->activeDataFilePage = 1;
+ srzPageptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(srzPageptr, cpagesize, page8);
+ /* --------------------------------------------------------------------------------- */
+ // Check that the checksum of the zero page is ok.
+ /* --------------------------------------------------------------------------------- */
+ ccoPageptr.p = srzPageptr.p;
+ checksumControl(signal, (Uint32)0);
+ if (tresult > 0) {
+ jam();
+ return; // We will crash through a DEBUG_SIG
+ }//if
+
+ ndbrequire(srzPageptr.p->word32[ZPAGEZERO_FRAGID0] == rootfragrecptr.p->fragmentid[0]);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ if (fsConnectptr.p->activeFragId == 0) {
+ jam();
+ rootfragrecptr.p->fragmentid[1] = srzPageptr.p->word32[ZPAGEZERO_FRAGID1];
+ /* ---------------------------OPEN THE DATA FILE FOR NEXT LOCAL FRAGMENT ----------- ---- */
+ tfid = rootfragrecptr.p->fragmentid[1];
+ tmp = 1; /* LOCAL FRAG INDEX */
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ srOpenDataFileLoopLab(signal);
+ return;
+ } else {
+ jam();
+ lcpConnectptr.p->lcpstate = LCP_ACTIVE;
+ signal->theData[0] = lcpConnectptr.p->lcpUserptr;
+ signal->theData[1] = lcpConnectptr.i;
+ signal->theData[2] = 2; /* NO OF LOCAL FRAGMENTS */
+ signal->theData[3] = srzPageptr.p->word32[ZPAGEZERO_FRAGID0];
+ /* ROOTFRAGRECPTR:FRAGMENTID(0) */
+ signal->theData[4] = srzPageptr.p->word32[ZPAGEZERO_FRAGID1];
+ /* ROOTFRAGRECPTR:FRAGMENTID(1) */
+ signal->theData[5] = RNIL;
+ signal->theData[6] = RNIL;
+ signal->theData[7] = rootfragrecptr.p->fragmentptr[0];
+ signal->theData[8] = rootfragrecptr.p->fragmentptr[1];
+ signal->theData[9] = srzPageptr.p->word32[ZPAGEZERO_HASH_CHECK];
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_SR_FRAGIDCONF, signal, 10, JBB);
+ }//if
+ return;
+}//Dbacc::srReadPageZeroLab()
+
+void Dbacc::initFragAdd(Signal* signal,
+ Uint32 rootFragIndex,
+ Uint32 rootIndex,
+ FragmentrecPtr regFragPtr)
+{
+ const AccFragReq * const req = (AccFragReq*)&signal->theData[0];
+ Uint32 lhFragBits = req->lhFragBits + 1;
+ Uint32 minLoadFactor = (req->minLoadFactor * ZBUF_SIZE) / 100;
+ Uint32 maxLoadFactor = (req->maxLoadFactor * ZBUF_SIZE) / 100;
+ if (minLoadFactor >= maxLoadFactor) {
+ jam();
+ minLoadFactor = maxLoadFactor - 1;
+ }//if
+ regFragPtr.p->fragState = ACTIVEFRAG;
+ // NOTE: next line must match calculation in Dblqh::execLQHFRAGREQ
+ regFragPtr.p->myfid = (req->fragId << 1) | rootFragIndex;
+ regFragPtr.p->myroot = rootIndex;
+ regFragPtr.p->myTableId = req->tableId;
+ ndbrequire(req->kValue == 6);
+ regFragPtr.p->k = req->kValue; /* TK_SIZE = 6 IN THIS VERSION */
+ regFragPtr.p->expandCounter = 0;
+
+ /**
+ * Only allow shrink during SR
+ * - to make sure we don't run out of pages during REDO log execution
+ *
+ * Is later restored to 0 by LQH at end of REDO log execution
+ */
+ regFragPtr.p->expandFlag = (getNodeState().getSystemRestartInProgress()?1:0);
+ regFragPtr.p->p = 0;
+ regFragPtr.p->maxp = (1 << req->kValue) - 1;
+ regFragPtr.p->minloadfactor = minLoadFactor;
+ regFragPtr.p->maxloadfactor = maxLoadFactor;
+ regFragPtr.p->slack = (regFragPtr.p->maxp + 1) * maxLoadFactor;
+ regFragPtr.p->lhfragbits = lhFragBits;
+ regFragPtr.p->lhdirbits = 0;
+ regFragPtr.p->hashcheckbit = 0; //lhFragBits;
+ regFragPtr.p->localkeylen = req->localKeyLen;
+ regFragPtr.p->nodetype = (req->reqInfo >> 4) & 0x3;
+ regFragPtr.p->lastOverIndex = 0;
+ regFragPtr.p->dirsize = 1;
+ regFragPtr.p->loadingFlag = ZFALSE;
+ regFragPtr.p->keyLength = req->keyLength;
+ ndbrequire(req->keyLength != 0);
+ regFragPtr.p->elementLength = ZELEM_HEAD_SIZE + regFragPtr.p->localkeylen;
+ Uint32 Tmp1 = (regFragPtr.p->maxp + 1) + regFragPtr.p->p;
+ Uint32 Tmp2 = regFragPtr.p->maxloadfactor - regFragPtr.p->minloadfactor;
+ Tmp2 = Tmp1 * Tmp2;
+ regFragPtr.p->slackCheck = Tmp2;
+}//Dbacc::initFragAdd()
+
+void Dbacc::initFragGeneral(FragmentrecPtr regFragPtr)
+{
+ regFragPtr.p->directory = RNIL;
+ regFragPtr.p->overflowdir = RNIL;
+ regFragPtr.p->fsConnPtr = RNIL;
+ regFragPtr.p->firstOverflowRec = RNIL;
+ regFragPtr.p->lastOverflowRec = RNIL;
+ regFragPtr.p->firstWaitInQueOp = RNIL;
+ regFragPtr.p->lastWaitInQueOp = RNIL;
+ regFragPtr.p->sentWaitInQueOp = RNIL;
+ regFragPtr.p->lockOwnersList = RNIL;
+ regFragPtr.p->firstFreeDirindexRec = RNIL;
+ regFragPtr.p->zeroPagePtr = RNIL;
+
+ regFragPtr.p->activeDataPage = 0;
+ regFragPtr.p->createLcp = ZFALSE;
+ regFragPtr.p->stopQueOp = ZFALSE;
+ regFragPtr.p->hasCharAttr = ZFALSE;
+ regFragPtr.p->nextAllocPage = 0;
+ regFragPtr.p->nrWaitWriteUndoExit = 0;
+ regFragPtr.p->lastUndoIsStored = ZFALSE;
+ regFragPtr.p->loadingFlag = ZFALSE;
+ regFragPtr.p->fragState = FREEFRAG;
+ for (Uint32 i = 0; i < ZWRITEPAGESIZE; i++) {
+ regFragPtr.p->datapages[i] = RNIL;
+ }//for
+ for (Uint32 j = 0; j < 4; j++) {
+ regFragPtr.p->longKeyPageArray[j] = RNIL;
+ }//for
+}//Dbacc::initFragGeneral()
+
+void Dbacc::initFragSr(FragmentrecPtr regFragPtr, Page8Ptr regPagePtr)
+{
+ regFragPtr.p->prevUndoposition = regPagePtr.p->word32[ZPAGEZERO_PREV_UNDOP];
+ regFragPtr.p->noOfStoredOverPages = regPagePtr.p->word32[ZPAGEZERO_NO_OVER_PAGE];
+ regFragPtr.p->noStoredPages = regPagePtr.p->word32[ZPAGEZERO_NO_PAGES];
+ regFragPtr.p->dirsize = regPagePtr.p->word32[ZPAGEZERO_DIRSIZE];
+ regFragPtr.p->expandCounter = regPagePtr.p->word32[ZPAGEZERO_EXPCOUNTER];
+ regFragPtr.p->slack = regPagePtr.p->word32[ZPAGEZERO_SLACK];
+ regFragPtr.p->hashcheckbit = regPagePtr.p->word32[ZPAGEZERO_HASHCHECKBIT];
+ regFragPtr.p->k = regPagePtr.p->word32[ZPAGEZERO_K];
+ regFragPtr.p->lhfragbits = regPagePtr.p->word32[ZPAGEZERO_LHFRAGBITS];
+ regFragPtr.p->lhdirbits = regPagePtr.p->word32[ZPAGEZERO_LHDIRBITS];
+ regFragPtr.p->localkeylen = regPagePtr.p->word32[ZPAGEZERO_LOCALKEYLEN];
+ regFragPtr.p->maxp = regPagePtr.p->word32[ZPAGEZERO_MAXP];
+ regFragPtr.p->maxloadfactor = regPagePtr.p->word32[ZPAGEZERO_MAXLOADFACTOR];
+ regFragPtr.p->minloadfactor = regPagePtr.p->word32[ZPAGEZERO_MINLOADFACTOR];
+ regFragPtr.p->myfid = regPagePtr.p->word32[ZPAGEZERO_MYFID];
+ regFragPtr.p->lastOverIndex = regPagePtr.p->word32[ZPAGEZERO_LAST_OVER_INDEX];
+ regFragPtr.p->nodetype = regPagePtr.p->word32[ZPAGEZERO_NODETYPE];
+ regFragPtr.p->p = regPagePtr.p->word32[ZPAGEZERO_P];
+ regFragPtr.p->elementLength = regPagePtr.p->word32[ZPAGEZERO_ELEMENT_LENGTH];
+ regFragPtr.p->keyLength = regPagePtr.p->word32[ZPAGEZERO_KEY_LENGTH];
+ regFragPtr.p->slackCheck = regPagePtr.p->word32[ZPAGEZERO_SLACK_CHECK];
+
+ regFragPtr.p->loadingFlag = ZTRUE;
+
+}//Dbacc::initFragSr()
+
+void Dbacc::initFragPageZero(FragmentrecPtr regFragPtr, Page8Ptr regPagePtr)
+{
+ //------------------------------------------------------------------
+ // PREV_UNDOP, NEXT_UNDO_FILE, NO_OVER_PAGE, NO_PAGES
+ // is set at end of copy phase
+ //------------------------------------------------------------------
+ regPagePtr.p->word32[ZPAGEZERO_DIRSIZE] = regFragPtr.p->dirsize;
+ regPagePtr.p->word32[ZPAGEZERO_EXPCOUNTER] = regFragPtr.p->expandCounter;
+ regPagePtr.p->word32[ZPAGEZERO_SLACK] = regFragPtr.p->slack;
+ regPagePtr.p->word32[ZPAGEZERO_HASHCHECKBIT] = regFragPtr.p->hashcheckbit;
+ regPagePtr.p->word32[ZPAGEZERO_K] = regFragPtr.p->k;
+ regPagePtr.p->word32[ZPAGEZERO_LHFRAGBITS] = regFragPtr.p->lhfragbits;
+ regPagePtr.p->word32[ZPAGEZERO_LHDIRBITS] = regFragPtr.p->lhdirbits;
+ regPagePtr.p->word32[ZPAGEZERO_LOCALKEYLEN] = regFragPtr.p->localkeylen;
+ regPagePtr.p->word32[ZPAGEZERO_MAXP] = regFragPtr.p->maxp;
+ regPagePtr.p->word32[ZPAGEZERO_MAXLOADFACTOR] = regFragPtr.p->maxloadfactor;
+ regPagePtr.p->word32[ZPAGEZERO_MINLOADFACTOR] = regFragPtr.p->minloadfactor;
+ regPagePtr.p->word32[ZPAGEZERO_MYFID] = regFragPtr.p->myfid;
+ regPagePtr.p->word32[ZPAGEZERO_LAST_OVER_INDEX] = regFragPtr.p->lastOverIndex;
+ regPagePtr.p->word32[ZPAGEZERO_NODETYPE] = regFragPtr.p->nodetype;
+ regPagePtr.p->word32[ZPAGEZERO_P] = regFragPtr.p->p;
+ regPagePtr.p->word32[ZPAGEZERO_ELEMENT_LENGTH] = regFragPtr.p->elementLength;
+ regPagePtr.p->word32[ZPAGEZERO_KEY_LENGTH] = regFragPtr.p->keyLength;
+ regPagePtr.p->word32[ZPAGEZERO_SLACK_CHECK] = regFragPtr.p->slackCheck;
+}//Dbacc::initFragPageZero()
+
+void Dbacc::initRootFragPageZero(RootfragmentrecPtr rootPtr, Page8Ptr regPagePtr)
+{
+ regPagePtr.p->word32[ZPAGEZERO_TABID] = rootPtr.p->mytabptr;
+ regPagePtr.p->word32[ZPAGEZERO_FRAGID0] = rootPtr.p->fragmentid[0];
+ regPagePtr.p->word32[ZPAGEZERO_FRAGID1] = rootPtr.p->fragmentid[1];
+ regPagePtr.p->word32[ZPAGEZERO_HASH_CHECK] = rootPtr.p->roothashcheck;
+ regPagePtr.p->word32[ZPAGEZERO_NO_OF_ELEMENTS] = rootPtr.p->noOfElements;
+}//Dbacc::initRootFragPageZero()
+
+void Dbacc::initRootFragSr(RootfragmentrecPtr rootPtr, Page8Ptr regPagePtr)
+{
+ rootPtr.p->roothashcheck = regPagePtr.p->word32[ZPAGEZERO_HASH_CHECK];
+ rootPtr.p->noOfElements = regPagePtr.p->word32[ZPAGEZERO_NO_OF_ELEMENTS];
+}//Dbacc::initRootFragSr()
+
+/* ******************--------------------------------------------------------------- */
+/* ACC_SRREQ SYSTEM RESTART OF A LOCAL CHECK POINT */
+/* SENDER: LQH, LEVEL B */
+/* ENTER ACC_SRREQ WITH */
+/* LCP_CONNECTPTR, OPERATION RECORD PTR */
+/* TMP2, LQH'S LOCAL FRAG CHECK VALUE */
+/* TFID, LOCAL FRAG ID */
+/* TMP1, LOCAL CHECKPOINT ID */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACC_SRREQ PERFORM A LOCAL CHECK POINT */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execACC_SRREQ(Signal* signal)
+{
+ Page8Ptr asrPageidptr;
+ jamEntry();
+ lcpConnectptr.i = signal->theData[0];
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ Uint32 lqhPtr = signal->theData[1];
+ Uint32 fragId = signal->theData[2];
+ Uint32 lcpId = signal->theData[3];
+ tresult = 0;
+ ndbrequire(lcpConnectptr.p->lcpstate == LCP_ACTIVE);
+ rootfragrecptr.i = lcpConnectptr.p->rootrecptr;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->fragmentid[0] == fragId) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ } else {
+ ndbrequire(rootfragrecptr.p->fragmentid[1] == fragId);
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ }//if
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ fragrecptr.p->lcpLqhPtr = lqhPtr;
+ fragrecptr.p->localCheckpId = lcpId;
+ asrPageidptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(asrPageidptr, cpagesize, page8);
+ ndbrequire(asrPageidptr.p->word32[ZPAGEZERO_TABID] == rootfragrecptr.p->mytabptr);
+ ndbrequire(asrPageidptr.p->word32[ZPAGEZERO_FRAGID0] == rootfragrecptr.p->fragmentid[0]);
+ ndbrequire(asrPageidptr.p->word32[ZPAGEZERO_FRAGID1] == rootfragrecptr.p->fragmentid[1]);
+ initRootFragSr(rootfragrecptr, asrPageidptr);
+ initFragSr(fragrecptr, asrPageidptr);
+ for (Uint32 i = 0; i < ZMAX_UNDO_VERSION; i++) {
+ jam();
+ if (csrVersList[i] != RNIL) {
+ jam();
+ srVersionPtr.i = csrVersList[i];
+ ptrCheckGuard(srVersionPtr, csrVersionRecSize, srVersionRec);
+ if (fragrecptr.p->localCheckpId == srVersionPtr.p->checkPointId) {
+ jam();
+ ndbrequire(srVersionPtr.p->checkPointId == asrPageidptr.p->word32[ZPAGEZERO_NEXT_UNDO_FILE]);
+ /*--------------------------------------------------------------------------------*/
+ /* SINCE -1 IS THE END OF LOG CODE WE MUST TREAT THIS CODE WITH CARE. WHEN */
+ /* COMPARING IT IS LARGER THAN EVERYTHING ELSE BUT SHOULD BE TREATED AS THE */
+ /* SMALLEST POSSIBLE VALUE, MEANING EMPTY. */
+ /*--------------------------------------------------------------------------------*/
+ if (fragrecptr.p->prevUndoposition != cminusOne) {
+ if (srVersionPtr.p->prevAddress < fragrecptr.p->prevUndoposition) {
+ jam();
+ srVersionPtr.p->prevAddress = fragrecptr.p->prevUndoposition;
+ } else if (srVersionPtr.p->prevAddress == cminusOne) {
+ jam();
+ srVersionPtr.p->prevAddress = fragrecptr.p->prevUndoposition;
+ }//if
+ }//if
+ srAllocPage0011Lab(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ seizeSrVerRec(signal);
+ srVersionPtr.p->checkPointId = fragrecptr.p->localCheckpId;
+ srVersionPtr.p->prevAddress = fragrecptr.p->prevUndoposition;
+ csrVersList[i] = srVersionPtr.i;
+ srAllocPage0011Lab(signal);
+ return;
+ }//if
+ }//for
+ ndbrequire(false);
+}//Dbacc::execACC_SRREQ()
+
+void
+Dbacc::releaseLogicalPage(Fragmentrec * fragP, Uint32 logicalPageId){
+ Ptr<struct DirRange> dirRangePtr;
+ dirRangePtr.i = fragP->directory;
+ ptrCheckGuard(dirRangePtr, cdirrangesize, dirRange);
+
+ const Uint32 lp1 = logicalPageId >> 8;
+ const Uint32 lp2 = logicalPageId & 0xFF;
+ ndbrequire(lp1 < 256);
+
+ Ptr<struct Directoryarray> dirArrPtr;
+ dirArrPtr.i = dirRangePtr.p->dirArray[lp1];
+ ptrCheckGuard(dirArrPtr, cdirarraysize, directoryarray);
+
+ const Uint32 physicalPageId = dirArrPtr.p->pagep[lp2];
+
+ rpPageptr.i = physicalPageId;
+ ptrCheckGuard(rpPageptr, cpagesize, page8);
+ releasePage(0);
+
+ dirArrPtr.p->pagep[lp2] = RNIL;
+}
+
+void Dbacc::srAllocPage0011Lab(Signal* signal)
+{
+ releaseLogicalPage(fragrecptr.p, 0);
+
+#if JONAS
+ ndbrequire(cfirstfreeDirrange != RNIL);
+ seizeDirrange(signal);
+ fragrecptr.p->directory = newDirRangePtr.i;
+ ndbrequire(cfirstfreeDirrange != RNIL);
+ seizeDirrange(signal);
+ fragrecptr.p->overflowdir = newDirRangePtr.i;
+ seizeDirectory(signal);
+ ndbrequire(tresult < ZLIMIT_OF_ERROR);
+ newDirRangePtr.p->dirArray[0] = sdDirptr.i;
+#endif
+
+ fragrecptr.p->nextAllocPage = 0;
+ fragrecptr.p->fragState = SR_READ_PAGES;
+ srReadPagesLab(signal);
+ return;
+}//Dbacc::srAllocPage0011Lab()
+
+void Dbacc::srReadPagesLab(Signal* signal)
+{
+ if (fragrecptr.p->nextAllocPage >= fragrecptr.p->noStoredPages) {
+ /*--------------------------------------------------------------------------------*/
+ /* WE HAVE NOW READ ALL NORMAL PAGES FROM THE FILE. */
+ /*--------------------------------------------------------------------------------*/
+ if (fragrecptr.p->nextAllocPage == fragrecptr.p->dirsize) {
+ jam();
+ /*--------------------------------------------------------------------------------*/
+ /* WE HAVE NOW READ ALL NORMAL PAGES AND ALLOCATED ALL THE NEEDED PAGES. */
+ /*--------------------------------------------------------------------------------*/
+ fragrecptr.p->nextAllocPage = 0; /* THE NEXT OVER FLOW PAGE WHICH WILL BE READ */
+ fragrecptr.p->fragState = SR_READ_OVER_PAGES;
+ srReadOverPagesLab(signal);
+ } else {
+ ndbrequire(fragrecptr.p->nextAllocPage < fragrecptr.p->dirsize);
+ jam();
+ /*--------------------------------------------------------------------------------*/
+ /* WE NEEDED TO ALLOCATE PAGES THAT WERE DEALLOCATED DURING THE LOCAL */
+ /* CHECKPOINT. */
+ /* ALLOCATE THE PAGE AND INITIALISE IT. THEN WE INSERT A REAL-TIME BREAK. */
+ /*--------------------------------------------------------------------------------*/
+ seizePage(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ tipPageId = fragrecptr.p->nextAllocPage;
+ inpPageptr.i = spPageptr.i;
+ ptrCheckGuard(inpPageptr, cpagesize, page8);
+ initPage(signal);
+ fragrecptr.p->noOfExpectedPages = 1;
+ fragrecptr.p->datapages[0] = spPageptr.i;
+ signal->theData[0] = ZSR_READ_PAGES_ALLOC;
+ signal->theData[1] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+ return;
+ }//if
+ Uint32 limitLoop;
+ if ((fragrecptr.p->noStoredPages - fragrecptr.p->nextAllocPage) < ZWRITEPAGESIZE) {
+ jam();
+ limitLoop = fragrecptr.p->noStoredPages - fragrecptr.p->nextAllocPage;
+ } else {
+ jam();
+ limitLoop = ZWRITEPAGESIZE;
+ }//if
+ ndbrequire(limitLoop <= 8);
+ for (Uint32 i = 0; i < limitLoop; i++) {
+ jam();
+ seizePage(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ fragrecptr.p->datapages[i] = spPageptr.i;
+ signal->theData[i + 6] = spPageptr.i;
+ }//for
+ signal->theData[limitLoop + 6] = fragrecptr.p->activeDataFilePage;
+ fragrecptr.p->noOfExpectedPages = limitLoop;
+ /* -----------------SEND READ PAGES SIGNAL TO THE FILE MANAGER --------- */
+ fsConnectptr.i = fragrecptr.p->fsConnPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ fsConnectptr.p->fsState = WAIT_READ_DATA;
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = 2;
+ /* FLAG = LIST MEM PAGES, RANGE OF FILE PAGES */
+ signal->theData[4] = ZPAGE8_BASE_ADD;
+ signal->theData[5] = fragrecptr.p->noOfExpectedPages;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 15, JBA);
+ return;
+}//Dbacc::srReadPagesLab()
+
+void Dbacc::storeDataPageInDirectoryLab(Signal* signal)
+{
+ fragrecptr.p->activeDataFilePage += fragrecptr.p->noOfExpectedPages;
+ srReadPagesAllocLab(signal);
+ return;
+}//Dbacc::storeDataPageInDirectoryLab()
+
+void Dbacc::srReadPagesAllocLab(Signal* signal)
+{
+ DirRangePtr srpDirRangePtr;
+ DirectoryarrayPtr srpDirptr;
+ DirectoryarrayPtr srpOverflowDirptr;
+ Page8Ptr srpPageidptr;
+
+ if (fragrecptr.p->fragState == SR_READ_PAGES) {
+ jam();
+ for (Uint32 i = 0; i < fragrecptr.p->noOfExpectedPages; i++) {
+ jam();
+ tmpP = fragrecptr.p->nextAllocPage;
+ srpDirRangePtr.i = fragrecptr.p->directory;
+ tmpP2 = tmpP >> 8;
+ tmp = tmpP & 0xff;
+ ptrCheckGuard(srpDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(tmpP2, 256);
+ if (srpDirRangePtr.p->dirArray[tmpP2] == RNIL) {
+ seizeDirectory(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ srpDirptr.i = sdDirptr.i;
+ srpDirRangePtr.p->dirArray[tmpP2] = srpDirptr.i;
+ } else {
+ jam();
+ srpDirptr.i = srpDirRangePtr.p->dirArray[tmpP2];
+ }//if
+ ptrCheckGuard(srpDirptr, cdirarraysize, directoryarray);
+ arrGuard(i, 8);
+ srpDirptr.p->pagep[tmp] = fragrecptr.p->datapages[i];
+ srpPageidptr.i = fragrecptr.p->datapages[i];
+ ptrCheckGuard(srpPageidptr, cpagesize, page8);
+ ndbrequire(srpPageidptr.p->word32[ZPOS_PAGE_ID] == fragrecptr.p->nextAllocPage);
+ ndbrequire(((srpPageidptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) == 0);
+ ccoPageptr.p = srpPageidptr.p;
+ checksumControl(signal, (Uint32)1);
+ if (tresult > 0) {
+ jam();
+ return; // We will crash through a DEBUG_SIG
+ }//if
+ dbgWord32(srpPageidptr, ZPOS_OVERFLOWREC, RNIL);
+ srpPageidptr.p->word32[ZPOS_OVERFLOWREC] = RNIL;
+ fragrecptr.p->datapages[i] = RNIL;
+ fragrecptr.p->nextAllocPage++;
+ }//for
+ srReadPagesLab(signal);
+ return;
+ } else {
+ ndbrequire(fragrecptr.p->fragState == SR_READ_OVER_PAGES);
+ for (Uint32 i = 0; i < fragrecptr.p->noOfExpectedPages; i++) {
+ jam();
+ arrGuard(i, 8);
+ srpPageidptr.i = fragrecptr.p->datapages[i];
+ ptrCheckGuard(srpPageidptr, cpagesize, page8);
+ tmpP = srpPageidptr.p->word32[ZPOS_PAGE_ID]; /* DIR INDEX OF THE OVERFLOW PAGE */
+ /*--------------------------------------------------------------------------------*/
+ /* IT IS POSSIBLE THAT WE HAVE LOGICAL PAGES WHICH ARE NOT PART OF THE LOCAL*/
+ /* CHECKPOINT. THUS WE USE THE LOGICAL PAGE ID FROM THE PAGE HERE. */
+ /*--------------------------------------------------------------------------------*/
+ srpDirRangePtr.i = fragrecptr.p->overflowdir;
+ tmpP2 = tmpP >> 8;
+ tmpP = tmpP & 0xff;
+ ptrCheckGuard(srpDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(tmpP2, 256);
+ if (srpDirRangePtr.p->dirArray[tmpP2] == RNIL) {
+ jam();
+ seizeDirectory(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ srpDirRangePtr.p->dirArray[tmpP2] = sdDirptr.i;
+ }//if
+ srpOverflowDirptr.i = srpDirRangePtr.p->dirArray[tmpP2];
+ ndbrequire(((srpPageidptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) != 0);
+ ndbrequire(((srpPageidptr.p->word32[ZPOS_PAGE_TYPE] >> ZPOS_PAGE_TYPE_BIT) & 3) != 3);
+ ptrCheckGuard(srpOverflowDirptr, cdirarraysize, directoryarray);
+ ndbrequire(srpOverflowDirptr.p->pagep[tmpP] == RNIL);
+ srpOverflowDirptr.p->pagep[tmpP] = srpPageidptr.i;
+ ccoPageptr.p = srpPageidptr.p;
+ checksumControl(signal, (Uint32)1);
+ ndbrequire(tresult == 0);
+ dbgWord32(srpPageidptr, ZPOS_OVERFLOWREC, RNIL);
+ srpPageidptr.p->word32[ZPOS_OVERFLOWREC] = RNIL;
+ fragrecptr.p->nextAllocPage++;
+ }//for
+ srReadOverPagesLab(signal);
+ return;
+ }//if
+}//Dbacc::srReadPagesAllocLab()
+
+void Dbacc::srReadOverPagesLab(Signal* signal)
+{
+ if (fragrecptr.p->nextAllocPage >= fragrecptr.p->noOfStoredOverPages) {
+ fragrecptr.p->nextAllocPage = 0;
+ if (fragrecptr.p->prevUndoposition == cminusOne) {
+ jam();
+ /* ************************ */
+ /* ACC_OVER_REC */
+ /* ************************ */
+ /*--------------------------------------------------------------------------------*/
+ /* UPDATE FREE LIST OF OVERFLOW PAGES AS PART OF SYSTEM RESTART AFTER */
+ /* READING PAGES AND EXECUTING THE UNDO LOG. */
+ /*--------------------------------------------------------------------------------*/
+ signal->theData[0] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_ACC_OVER_REC, signal, 1, JBB);
+ } else {
+ jam();
+ srCloseDataFileLab(signal);
+ }//if
+ return;
+ }//if
+ Uint32 limitLoop;
+ if ((fragrecptr.p->noOfStoredOverPages - fragrecptr.p->nextAllocPage) < ZWRITEPAGESIZE) {
+ jam();
+ limitLoop = fragrecptr.p->noOfStoredOverPages - fragrecptr.p->nextAllocPage;
+ } else {
+ jam();
+ limitLoop = ZWRITEPAGESIZE;
+ }//if
+ ndbrequire(limitLoop <= 8);
+ for (Uint32 i = 0; i < limitLoop; i++) {
+ jam();
+ seizePage(signal);
+ ndbrequire(tresult <= ZLIMIT_OF_ERROR);
+ fragrecptr.p->datapages[i] = spPageptr.i;
+ signal->theData[i + 6] = spPageptr.i;
+ }//for
+ fragrecptr.p->noOfExpectedPages = limitLoop;
+ signal->theData[limitLoop + 6] = fragrecptr.p->activeDataFilePage;
+ /* -----------------SEND READ PAGES SIGNAL TO THE FILE MANAGER --------- */
+ fsConnectptr.i = fragrecptr.p->fsConnPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ fsConnectptr.p->fsState = WAIT_READ_DATA;
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = 2;
+ signal->theData[4] = ZPAGE8_BASE_ADD;
+ signal->theData[5] = fragrecptr.p->noOfExpectedPages;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 15, JBA);
+ return;
+}//Dbacc::srReadOverPagesLab()
+
+void Dbacc::srCloseDataFileLab(Signal* signal)
+{
+ fsConnectptr.i = fragrecptr.p->fsConnPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ fsConnectptr.p->fsState = SR_CLOSE_DATA;
+ /* ************************ */
+ /* FSCLOSEREQ */
+ /* ************************ */
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = 0;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ return;
+}//Dbacc::srCloseDataFileLab()
+
+/* ************************ */
+/* ACC_SRCONF */
+/* ************************ */
+void Dbacc::sendaccSrconfLab(Signal* signal)
+{
+ fragrecptr.i = fsConnectptr.p->fragrecPtr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ releaseFsConnRec(signal);
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ lcpConnectptr.i = rootfragrecptr.p->lcpPtr;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ fragrecptr.p->fragState = ACTIVEFRAG;
+ fragrecptr.p->fsConnPtr = RNIL;
+ for (Uint32 i = 0; i < ZWRITEPAGESIZE; i++) {
+ fragrecptr.p->datapages[i] = RNIL;
+ }//for
+ rlpPageptr.i = fragrecptr.p->zeroPagePtr;
+ ptrCheckGuard(rlpPageptr, cpagesize, page8);
+ releaseLcpPage(signal);
+ fragrecptr.p->zeroPagePtr = RNIL;
+ signal->theData[0] = fragrecptr.p->lcpLqhPtr;
+ sendSignal(lcpConnectptr.p->lcpUserblockref, GSN_ACC_SRCONF, signal, 1, JBB);
+ lcpConnectptr.p->noOfLcpConf++;
+ if (lcpConnectptr.p->noOfLcpConf == 2) {
+ jam();
+ releaseLcpConnectRec(signal);
+ rootfragrecptr.p->lcpPtr = RNIL;
+ rootfragrecptr.p->rootState = ACTIVEROOT;
+ }//if
+ return;
+}//Dbacc::sendaccSrconfLab()
+
+/* --------------------------------------------------------------------------------- */
+/* CHECKSUM_CONTROL */
+/* INPUT: CCO_PAGEPTR */
+/* OUTPUT: TRESULT */
+/* */
+/* CHECK THAT CHECKSUM IN PAGE IS CORRECT TO ENSURE THAT NO ONE HAS CORRUPTED */
+/* THE PAGE INFORMATION. WHEN CALCULATING THE CHECKSUM WE REMOVE THE CHECKSUM */
+/* ITSELF FROM THE CHECKSUM BY XOR'ING THE CHECKSUM TWICE. WHEN CALCULATING */
+/* THE CHECKSUM THE CHECKSUM WORD IS ZERO WHICH MEANS NO CHANGE FROM XOR'ING. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::checksumControl(Signal* signal, Uint32 checkPage)
+{
+ Uint32 Tchs;
+ Uint32 tccoIndex;
+ Uint32 Ti;
+ Uint32 Tmp1;
+ Uint32 Tmp2;
+ Uint32 Tmp3;
+ Uint32 Tmp4;
+ Uint32 Tlimit;
+
+ Tchs = 0;
+ for (Ti = 0; Ti < 32 ; Ti++) {
+ Tlimit = 16 + (Ti << 6);
+ for (tccoIndex = (Ti << 6); tccoIndex < Tlimit; tccoIndex ++) {
+ Tmp1 = ccoPageptr.p->word32[tccoIndex];
+ Tmp2 = ccoPageptr.p->word32[tccoIndex + 16];
+ Tmp3 = ccoPageptr.p->word32[tccoIndex + 32];
+ Tmp4 = ccoPageptr.p->word32[tccoIndex + 48];
+
+ Tchs = Tchs ^ Tmp1;
+ Tchs = Tchs ^ Tmp2;
+ Tchs = Tchs ^ Tmp3;
+ Tchs = Tchs ^ Tmp4;
+ }//for
+ }//for
+ if (Tchs == 0) {
+ tresult = 0;
+ if (checkPage != 0) {
+ jam();
+ lcnCopyPageptr.p = ccoPageptr.p;
+ srCheckPage(signal);
+ }//if
+ } else {
+ tresult = 1;
+ }//if
+ if (tresult != 0) {
+ jam();
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ signal->theData[0] = RNIL;
+ signal->theData[1] = rootfragrecptr.p->mytabptr;
+ signal->theData[2] = fragrecptr.p->myfid;
+ signal->theData[3] = ccoPageptr.p->word32[ZPOS_PAGE_ID];
+ signal->theData[4] = tlupElemIndex;
+ signal->theData[5] = ccoPageptr.p->word32[ZPOS_PAGE_TYPE];
+ signal->theData[6] = tresult;
+ sendSignal(cownBlockref, GSN_DEBUG_SIG, signal, 7, JBA);
+ }//if
+}//Dbacc::checksumControl()
+
+/* ******************--------------------------------------------------------------- */
+/* START_RECREQ REQUEST TO START UNDO PROCESS */
+/* SENDER: LQH, LEVEL B */
+/* ENTER START_RECREQ WITH */
+/* CLQH_PTR, LQH CONNECTION PTR */
+/* CLQH_BLOCK_REF, LQH BLOCK REFERENCE */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* START_RECREQ REQUEST TO START UNDO PROCESS */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execSTART_RECREQ(Signal* signal)
+{
+ jamEntry();
+ clqhPtr = signal->theData[0]; /* LQH CONNECTION PTR */
+ clqhBlockRef = signal->theData[1]; /* LQH BLOCK REFERENCE */
+ tresult = 0; /* 0= FALSE,1= TRUE,> ZLIMIT_OF_ERROR =ERRORCODE */
+ for (int i = 0; i < UndoHeader::ZNO_UNDORECORD_TYPES; i++)
+ cSrUndoRecords[i] = 0;
+ startUndoLab(signal);
+ return;
+}//Dbacc::execSTART_RECREQ()
+
+void Dbacc::startUndoLab(Signal* signal)
+{
+ cundoLogActive = ZTRUE;
+ /* ----- OPEN UNDO FILES --------- */
+ for (tmp = 0; tmp <= ZMAX_UNDO_VERSION - 1; tmp++) {
+ jam();
+ if (csrVersList[tmp] != RNIL) {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* SELECT THE NEXT SYSTEM RESTART RECORD WHICH CONTAINS AN UNDO LOG */
+ /* THAT NEEDS TO BE EXECUTED AND SET UP THE DATA TO EXECUTE IT. */
+ /*---------------------------------------------------------------------------*/
+ srVersionPtr.i = csrVersList[tmp];
+ csrVersList[tmp] = RNIL;
+ ptrCheckGuard(srVersionPtr, csrVersionRecSize, srVersionRec);
+ cactiveUndoFilePage = srVersionPtr.p->prevAddress >> 13;
+ cprevUndoaddress = srVersionPtr.p->prevAddress;
+ cactiveCheckpId = srVersionPtr.p->checkPointId;
+
+ releaseSrRec(signal);
+ startActiveUndo(signal);
+ return;
+ }//if
+ }//for
+
+ // Send report of how many undo log records where executed
+ signal->theData[0] = NDB_LE_UNDORecordsExecuted;
+ signal->theData[1] = DBACC; // From block
+ signal->theData[2] = 0; // Total records executed
+ for (int i = 0; i < 10; i++){
+ if (i < UndoHeader::ZNO_UNDORECORD_TYPES){
+ signal->theData[i+3] = cSrUndoRecords[i];
+ signal->theData[2] += cSrUndoRecords[i];
+ }else{
+ signal->theData[i+3] = 0;
+ }
+ }
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 12, JBB);
+
+ /* ******************************< */
+ /* START_RECCONF */
+ /* ******************************< */
+ /*---------------------------------------------------------------------------*/
+ /* REPORT COMPLETION OF UNDO LOG EXECUTION. */
+ /*---------------------------------------------------------------------------*/
+ cundoLogActive = ZFALSE;
+ signal->theData[0] = clqhPtr;
+ sendSignal(clqhBlockRef, GSN_START_RECCONF, signal, 1, JBB);
+ /* LQH CONNECTION PTR */
+ return;
+}//Dbacc::startUndoLab()
+
+/*---------------------------------------------------------------------------*/
+/* START THE UNDO OF AN UNDO LOG FILE BY OPENING THE UNDO LOG FILE. */
+/*---------------------------------------------------------------------------*/
+void Dbacc::startActiveUndo(Signal* signal)
+{
+ if (cprevUndoaddress == cminusOne) {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* THERE WAS NO UNDO LOG INFORMATION IN THIS LOG FILE. WE GET THE NEXT */
+ /* OR REPORT COMPLETION. */
+ /*---------------------------------------------------------------------------*/
+ signal->theData[0] = ZSTART_UNDO;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 1, JBB);
+ } else {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* OPEN THE LOG FILE PERTAINING TO THIS UNDO LOG. */
+ /*---------------------------------------------------------------------------*/
+ if (cfsFirstfreeconnect == RNIL) {
+ jam();
+ sendSystemerror(signal);
+ }//if
+ seizeFsConnectRec(signal);
+ cactiveSrFsPtr = fsConnectptr.i;
+ fsConnectptr.p->fsState = OPEN_UNDO_FILE_SR;
+ fsConnectptr.p->fsPart = 0;
+ tmp1 = 1; /* FILE VERSION ? */
+ tmp1 = (tmp1 << 8) + ZLOCALLOGFILE; /* .LOCLOG = 2 */
+ tmp1 = (tmp1 << 8) + 4; /* ROOT DIRECTORY = D4 */
+ tmp1 = (tmp1 << 8) + fsConnectptr.p->fsPart; /* P2 */
+ tmp2 = 0x0; /* D7 DON'T CREATE , READ ONLY */
+ /* DON'T TRUNCATE TO ZERO */
+ /* ---FILE NAME "D4"/"DBACC"/LCP_CONNECTPTR:LOCAL_CHECK_PID/FS_CONNECTPTR:FS_PART".LOCLOG-- */
+ /* ************************ */
+ /* FSOPENREQ */
+ /* ************************ */
+ signal->theData[0] = cownBlockref;
+ signal->theData[1] = fsConnectptr.i;
+ signal->theData[2] = cminusOne; /* #FFFFFFFF */
+ signal->theData[3] = cminusOne; /* #FFFFFFFF */
+ signal->theData[4] = cactiveCheckpId; /* CHECKPOINT VERSION */
+ signal->theData[5] = tmp1;
+ signal->theData[6] = tmp2;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ }//if
+}//Dbacc::startActiveUndo()
+
+/* ------- READ A GROUP OF UNDO PAGES --------------- */
+void Dbacc::srStartUndoLab(Signal* signal)
+{
+ /*---------------------------------------------------------------------------*/
+ /* ALL LOG FILES HAVE BEEN OPENED. WE CAN NOW READ DATA FROM THE LAST */
+ /* PAGE IN THE LAST LOG FILE AND BACKWARDS UNTIL WE REACH THE VERY */
+ /* FIRST UNDO LOG RECORD. */
+ /*---------------------------------------------------------------------------*/
+ if (cactiveUndoFilePage >= ZWRITE_UNDOPAGESIZE) {
+ jam();
+ tmp1 = ZWRITE_UNDOPAGESIZE; /* NO OF READ UNDO PAGES */
+ cactiveSrUndoPage = ZWRITE_UNDOPAGESIZE - 1; /* LAST PAGE */
+ } else {
+ jam();
+ tmp1 = cactiveUndoFilePage + 1; /* NO OF READ UNDO PAGES */
+ cactiveSrUndoPage = cactiveUndoFilePage;
+ }//if
+ fsConnectptr.i = cactiveSrFsPtr;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ signal->theData[0] = fsConnectptr.p->fsPtr;
+ signal->theData[1] = cownBlockref;
+ signal->theData[2] = fsConnectptr.i;
+ signal->theData[3] = 0;
+ /* FLAG = LIST MEM PAGES, LIST FILE PAGES */
+ signal->theData[4] = ZUNDOPAGE_BASE_ADD;
+ signal->theData[5] = tmp1;
+ signal->theData[6] = 0;
+ signal->theData[7] = (cactiveUndoFilePage - tmp1) + 1;
+ signal->theData[8] = 1;
+ signal->theData[9] = cactiveUndoFilePage;
+
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 10, JBA);
+ if (tmp1 > cactiveUndoFilePage) {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* THIS IS THE LAST READ IN THIS LOG FILE. WE SET THE ACTIVE FILE */
+ /* POINTER. IF IT IS THE FIRST WE SHOULD NEVER ATTEMPT ANY MORE READS */
+ /* SINCE WE SHOULD ENCOUNTER A FIRST LOG RECORD WITH PREVIOUS PAGE ID */
+ /* EQUAL TO RNIL. */
+ /*---------------------------------------------------------------------------*/
+ cactiveSrFsPtr = RNIL;
+ fsConnectptr.p->fsState = READ_UNDO_PAGE_AND_CLOSE;
+ } else {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* WE STILL HAVE MORE INFORMATION IN THIS LOG FILE. WE ONLY MOVE BACK */
+ /* THE FILE PAGE. */
+ /*---------------------------------------------------------------------------*/
+ cactiveUndoFilePage = cactiveUndoFilePage - tmp1;
+ fsConnectptr.p->fsState = READ_UNDO_PAGE;
+ }//if
+ return;
+}//Dbacc::srStartUndoLab()
+
+/* ------- DO UNDO ---------------------------*/
+/* ******************--------------------------------------------------------------- */
+/* NEXTOPERATION ORD FOR EXECUTION OF NEXT OP */
+/* ******************------------------------------+ */
+/* SENDER: ACC, LEVEL B */
+void Dbacc::execNEXTOPERATION(Signal* signal)
+{
+ jamEntry();
+ tresult = 0;
+ srDoUndoLab(signal);
+ return;
+}//Dbacc::execNEXTOPERATION()
+
+void Dbacc::srDoUndoLab(Signal* signal)
+{
+ DirRangePtr souDirRangePtr;
+ DirectoryarrayPtr souDirptr;
+ Page8Ptr souPageidptr;
+ Uint32 tundoPageindex;
+ UndoHeader *undoHeaderPtr;
+ Uint32 tmpindex;
+
+ jam();
+ undopageptr.i = cactiveSrUndoPage;
+ ptrCheckGuard(undopageptr, cundopagesize, undopage);
+ /*---------------------------------------------------------------------------*/
+ /* LAYOUT OF AN UNDO LOG RECORD: */
+ /* ***************************** */
+ /* */
+ /* |----------------------------------------------------| */
+ /* | TABLE ID | */
+ /* |----------------------------------------------------| */
+ /* | ROOT FRAGMENT ID | */
+ /* |----------------------------------------------------| */
+ /* | LOCAL FRAGMENT ID | */
+ /* |----------------------------------------------------| */
+ /* | UNDO INFO LEN 14 b | TYPE 4 b | PAGE INDEX 14 b | */
+ /* |----------------------------------------------------| */
+ /* | INDEX INTO PAGE DIRECTORY (LOGICAL PAGE ID) | */
+ /* |----------------------------------------------------| */
+ /* | PREVIOUS UNDO LOG RECORD FOR THE FRAGMENT | */
+ /* |----------------------------------------------------| */
+ /* | PREVIOUS UNDO LOG RECORD FOR ALL FRAGMENTS | */
+ /* |----------------------------------------------------| */
+ /* | TYPE SPECIFIC PART | */
+ /* |----------------------------------------------------| */
+ /*---------------------------------------------------------------------------*/
+ /*---------------------------------------------------------------------------*/
+ /* SET THE PAGE POINTER. WE ONLY WORK WITH TWO PAGES IN THIS RESTART */
+ /* ACTIVITY. GET THE PAGE POINTER AND THE PAGE INDEX TO READ FROM. */
+ /*---------------------------------------------------------------------------*/
+ tundoindex = cprevUndoaddress & ZUNDOPAGEINDEX_MASK; //0x1fff, 13 bits.
+ undoHeaderPtr = (UndoHeader *) &undopageptr.p->undoword[tundoindex];
+ tundoindex = tundoindex + ZUNDOHEADSIZE;
+
+ /*------------------------------------------------------------------------*/
+ /* READ TABLE ID AND ROOT FRAGMENT ID AND USE THIS TO GET ROOT RECORD. */
+ /*------------------------------------------------------------------------*/
+ arrGuard((tundoindex + 6), 8192);
+
+ // TABLE ID
+ tabptr.i = undoHeaderPtr->tableId;
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+
+ // ROOT FRAGMENT ID
+ tfid = undoHeaderPtr->rootFragId;
+ ndbrequire((undoHeaderPtr->localFragId >> 1) == undoHeaderPtr->rootFragId);
+ if (!getrootfragmentrec(signal, rootfragrecptr, tfid)) {
+ jam();
+ /*---------------------------------------------------------------------*/
+ /* THE ROOT RECORD WAS NOT FOUND. OBVIOUSLY WE ARE NOT RESTARTING THIS */
+ /* FRAGMENT. WE THUS IGNORE THIS LOG RECORD AND PROCEED WITH THE NEXT. */
+ /*---------------------------------------------------------------------*/
+ creadyUndoaddress = cprevUndoaddress;
+ // PREVIOUS UNDO LOG RECORD FOR ALL FRAGMENTS
+ cprevUndoaddress = undoHeaderPtr->prevUndoAddress;
+ undoNext2Lab(signal);
+#ifdef VM_TRACE
+ ndbout_c("ignoring root fid %d", (int)tfid);
+#endif
+ return;
+ }//if
+ /*-----------------------------------------------------------------------*/
+ /* READ THE LOCAL FRAGMENT ID AND VERIFY THAT IT IS CORRECT. */
+ /*-----------------------------------------------------------------------*/
+ if (rootfragrecptr.p->fragmentid[0] == undoHeaderPtr->localFragId) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ } else {
+ if (rootfragrecptr.p->fragmentid[1] == undoHeaderPtr->localFragId) {
+ jam();
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[1];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ } else {
+ jam();
+ progError(__LINE__, 0, "Invalid local fragment id in undo log");
+ return;
+ }//if
+ }//if
+ /*------------------------------------------------------------------------*/
+ /* READ UNDO INFO LENGTH, TYPE OF LOG RECORD AND PAGE INDEX WHERE TO */
+ /* APPLY THIS LOG RECORD. ALSO STEP INDEX TO PREPARE READ OF LOGICAL */
+ /* PAGE ID. SET TMPINDEX TO INDEX THE FIRST WORD IN THE TYPE SPECIFIC */
+ /* PART. */
+ /*------------------------------------------------------------------------*/
+ // UNDO INFO LENGTH 14 b | TYPE 4 b | PAGE INDEX 14 b
+ const Uint32 tmp1 = undoHeaderPtr->variousInfo;
+ cundoinfolength = tmp1 >> 18;
+ const Uint32 tpageType = (tmp1 >> 14) & 0xf;
+ tundoPageindex = tmp1 & 0x3fff;
+
+ // INDEX INTO PAGE DIRECTORY (LOGICAL PAGE ID)
+ tmpP = undoHeaderPtr->logicalPageId ;
+ tmpindex = tundoindex;
+ arrGuard((tmpindex + cundoinfolength - 1), 8192);
+ if (fragrecptr.p->localCheckpId != cactiveCheckpId) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ /* THE FRAGMENT DID EXIST BUT IS NOT AFFECTED BY THIS UNDO LOG */
+ /* EXECUTION. EITHER IT BELONGS TO ANOTHER OR IT IS CREATED AND ONLY IN */
+ /* NEED OF EXECUTION OF REDO LOG RECORDS FROM LQH. */
+ /*-----------------------------------------------------------------------*/
+ creadyUndoaddress = cprevUndoaddress;
+ // PREVIOUS UNDO LOG RECORD FOR ALL FRAGMENTS
+ cprevUndoaddress = undoHeaderPtr->prevUndoAddress;
+
+ undoNext2Lab(signal);
+ return;
+ }//if
+ /*-----------------------------------------------------------------------*/
+ /* VERIFY CONSISTENCY OF UNDO LOG RECORDS. */
+ /*-----------------------------------------------------------------------*/
+ ndbrequire(fragrecptr.p->prevUndoposition == cprevUndoaddress);
+ cSrUndoRecords[tpageType]++;
+ switch(tpageType){
+
+ case UndoHeader::ZPAGE_INFO:{
+ jam();
+ /*----------------------------------------------------------------------*/
+ /* WE HAVE TO UNDO UPDATES IN A NORMAL PAGE. GET THE PAGE POINTER BY */
+ /* USING THE LOGICAL PAGE ID. THEN RESET THE OLD VALUE IN THE PAGE BY */
+ /* USING THE OLD DATA WHICH IS STORED IN THIS UNDO LOG RECORD. */
+ /*----------------------------------------------------------------------*/
+ souDirRangePtr.i = fragrecptr.p->directory;
+ tmpP2 = tmpP >> 8;
+ tmpP = tmpP & 0xff;
+ ptrCheckGuard(souDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(tmpP2, 256);
+ souDirptr.i = souDirRangePtr.p->dirArray[tmpP2];
+ ptrCheckGuard(souDirptr, cdirarraysize, directoryarray);
+ souPageidptr.i = souDirptr.p->pagep[tmpP];
+ ptrCheckGuard(souPageidptr, cpagesize, page8);
+ Uint32 loopLimit = tundoPageindex + cundoinfolength;
+ ndbrequire(loopLimit <= 2048);
+ for (Uint32 tmp = tundoPageindex; tmp < loopLimit; tmp++) {
+ dbgWord32(souPageidptr, tmp, undopageptr.p->undoword[tmpindex]);
+ souPageidptr.p->word32[tmp] = undopageptr.p->undoword[tmpindex];
+ tmpindex = tmpindex + 1;
+ }//for
+ break;
+ }
+
+ case UndoHeader::ZOVER_PAGE_INFO:{
+ jam();
+ /*----------------------------------------------------------------------*/
+ /* WE HAVE TO UNDO UPDATES IN AN OVERFLOW PAGE. GET THE PAGE POINTER BY*/
+ /* USING THE LOGICAL PAGE ID. THEN RESET THE OLD VALUE IN THE PAGE BY */
+ /* USING THE OLD DATA WHICH IS STORED IN THIS UNDO LOG RECORD. */
+ /*----------------------------------------------------------------------*/
+ souDirRangePtr.i = fragrecptr.p->overflowdir;
+ tmpP2 = tmpP >> 8;
+ tmpP = tmpP & 0xff;
+ ptrCheckGuard(souDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(tmpP2, 256);
+ souDirptr.i = souDirRangePtr.p->dirArray[tmpP2];
+ ptrCheckGuard(souDirptr, cdirarraysize, directoryarray);
+ souPageidptr.i = souDirptr.p->pagep[tmpP];
+ ptrCheckGuard(souPageidptr, cpagesize, page8);
+ Uint32 loopLimit = tundoPageindex + cundoinfolength;
+ ndbrequire(loopLimit <= 2048);
+ for (Uint32 tmp = tundoPageindex; tmp < loopLimit; tmp++) {
+ dbgWord32(souPageidptr, tmp, undopageptr.p->undoword[tmpindex]);
+ souPageidptr.p->word32[tmp] = undopageptr.p->undoword[tmpindex];
+ tmpindex = tmpindex + 1;
+ }//for
+ break;
+ }
+
+ case UndoHeader::ZOP_INFO: {
+ jam();
+ /*---------------------------------------------------------------------*/
+ /* AN OPERATION WAS ACTIVE WHEN LOCAL CHECKPOINT WAS EXECUTED. WE NEED */
+ /* TO RESET THE LOCKS IT HAS SET. IF THE OPERATION WAS AN INSERT OR */
+ /* THE ELEMENT WAS MARKED AS DISSAPEARED IT WILL ALSO BE REMOVED */
+ /* FROM THE PAGE */
+ /* */
+ /* BEGIN BY SEARCHING AFTER THE ELEMENT, WHEN FOUND UNDO THE */
+ /* CHANGES ON THE ELEMENT HEADER. IF IT WAS AN INSERT OPERATION OR */
+ /* MARKED AS DISSAPEARED PROCEED BY REMOVING THE ELEMENT. */
+ /*---------------------------------------------------------------------*/
+ seizeOpRec(signal);
+ // Initialise the opRec
+ operationRecPtr.p->transId1 = 0;
+ operationRecPtr.p->transId2 = RNIL;
+ operationRecPtr.p->transactionstate = ACTIVE;
+ operationRecPtr.p->commitDeleteCheckFlag = ZFALSE;
+ operationRecPtr.p->lockMode = 0;
+ operationRecPtr.p->dirtyRead = 0;
+ operationRecPtr.p->nodeType = 0;
+ operationRecPtr.p->fid = fragrecptr.p->myfid;
+ operationRecPtr.p->nextParallelQue = RNIL;
+ operationRecPtr.p->prevParallelQue = RNIL;
+ operationRecPtr.p->nextQueOp = RNIL;
+ operationRecPtr.p->prevQueOp = RNIL;
+ operationRecPtr.p->nextSerialQue = RNIL;
+ operationRecPtr.p->prevSerialQue = RNIL;
+ operationRecPtr.p->elementPage = RNIL;
+ operationRecPtr.p->keyinfoPage = RNIL;
+ operationRecPtr.p->insertIsDone = ZFALSE;
+ operationRecPtr.p->lockOwner = ZFALSE;
+ operationRecPtr.p->elementIsDisappeared = ZFALSE;
+ operationRecPtr.p->insertDeleteLen = fragrecptr.p->elementLength;
+ operationRecPtr.p->longPagePtr = RNIL;
+ operationRecPtr.p->longKeyPageIndex = RNIL;
+ operationRecPtr.p->scanRecPtr = RNIL;
+ operationRecPtr.p->isAccLockReq = ZFALSE;
+ operationRecPtr.p->isUndoLogReq = ZTRUE;
+
+ // Read operation values from undo page
+ operationRecPtr.p->operation = undopageptr.p->undoword[tmpindex];
+ tmpindex++;
+ operationRecPtr.p->hashValue = undopageptr.p->undoword[tmpindex];
+ tmpindex++;
+ const Uint32 tkeylen = undopageptr.p->undoword[tmpindex];
+ tmpindex++;
+ operationRecPtr.p->tupkeylen = tkeylen;
+ operationRecPtr.p->xfrmtupkeylen = 0; // not used
+ operationRecPtr.p->fragptr = fragrecptr.i;
+
+ ndbrequire(fragrecptr.p->keyLength != 0 &&
+ fragrecptr.p->keyLength == tkeylen);
+
+ // Read localkey1 from undo page
+ signal->theData[7 + 0] = undopageptr.p->undoword[tmpindex];
+ tmpindex = tmpindex + 1;
+ arrGuard((tmpindex - 1), 8192);
+ getElement(signal);
+ if (tgeResult != ZTRUE) {
+ jam();
+ signal->theData[0] = RNIL;
+ signal->theData[1] = tabptr.i;
+ signal->theData[2] = cactiveCheckpId;
+ signal->theData[3] = cprevUndoaddress;
+ signal->theData[4] = operationRecPtr.p->operation;
+ signal->theData[5] = operationRecPtr.p->hashValue;
+ signal->theData[6] = operationRecPtr.p->tupkeylen;
+ sendSignal(cownBlockref, GSN_DEBUG_SIG, signal, 11, JBA);
+ return;
+ }//if
+
+ operationRecPtr.p->elementPage = gePageptr.i;
+ operationRecPtr.p->elementContainer = tgeContainerptr;
+ operationRecPtr.p->elementPointer = tgeElementptr;
+ operationRecPtr.p->elementIsforward = tgeForward;
+
+ commitdelete(signal, true);
+ releaseOpRec(signal);
+ break;
+ }
+
+ default:
+ jam();
+ progError(__LINE__, 0, "Invalid pagetype in undo log");
+ break;
+
+ }//switch(tpageType)
+
+ /*----------------------------------------------------------------------*/
+ /* READ THE PAGE ID AND THE PAGE INDEX OF THE PREVIOUS UNDO LOG RECORD */
+ /* FOR THIS FRAGMENT. */
+ /*----------------------------------------------------------------------*/
+ fragrecptr.p->prevUndoposition = undoHeaderPtr->prevUndoAddressForThisFrag;
+ /*----------------------------------------------------------------------*/
+ /* READ THE PAGE ID AND THE PAGE INDEX OF THE PREVIOUS UNDO LOG RECORD */
+ /* FOR THIS UNDO LOG. */
+ /*----------------------------------------------------------------------*/
+ creadyUndoaddress = cprevUndoaddress;
+ cprevUndoaddress = undoHeaderPtr->prevUndoAddress;
+
+ if (fragrecptr.p->prevUndoposition == cminusOne) {
+ jam();
+ /*---------------------------------------------------------------------*/
+ /* WE HAVE NOW EXECUTED ALL UNDO LOG RECORDS FOR THIS FRAGMENT. WE */
+ /* NOW NEED TO UPDATE THE FREE LIST OF OVERFLOW PAGES. */
+ /*---------------------------------------------------------------------*/
+ ndbrequire(fragrecptr.p->nextAllocPage == 0);
+
+ signal->theData[0] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_ACC_OVER_REC, signal, 1, JBB);
+ return;
+ }//if
+ undoNext2Lab(signal);
+ return;
+}//Dbacc::srDoUndoLab()
+
+void Dbacc::undoNext2Lab(Signal* signal)
+{
+ /*---------------------------------------------------------------------------*/
+ /* EXECUTE NEXT UNDO LOG RECORD. */
+ /*---------------------------------------------------------------------------*/
+ if (cprevUndoaddress == cminusOne) {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* WE HAVE EXECUTED THIS UNDO LOG TO COMPLETION. IT IS NOW TIME TO TAKE*/
+ /* OF THE NEXT UNDO LOG OR REPORT COMPLETION OF UNDO LOG EXECUTION. */
+ /*---------------------------------------------------------------------------*/
+ signal->theData[0] = ZSTART_UNDO;
+ sendSignal(cownBlockref, GSN_CONTINUEB, signal, 1, JBB);
+ return;
+ }//if
+ if ((creadyUndoaddress >> 13) != (cprevUndoaddress >> 13)) {
+ /*---------------------------------------------------------------------------*/
+ /* WE ARE CHANGING PAGE. */
+ /*---------------------------------------------------------------------------*/
+ if (cactiveSrUndoPage == 0) {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* WE HAVE READ AND EXECUTED ALL UNDO LOG INFORMATION IN THE CURRENTLY */
+ /* READ PAGES. WE STILL HAVE MORE INFORMATION TO READ FROM FILE SINCE */
+ /* WE HAVEN'T FOUND THE FIRST LOG RECORD IN THE LOG FILE YET. */
+ /*---------------------------------------------------------------------------*/
+ srStartUndoLab(signal);
+ return;
+ } else {
+ jam();
+ /*---------------------------------------------------------------------------*/
+ /* WE HAVE ANOTHER PAGE READ THAT WE NEED TO EXECUTE. */
+ /*---------------------------------------------------------------------------*/
+ cactiveSrUndoPage = cactiveSrUndoPage - 1;
+ }//if
+ }//if
+ /*---------------------------------------------------------------------------*/
+ /* REAL-TIME BREAK */
+ /*---------------------------------------------------------------------------*/
+ /* ******************************< */
+ /* NEXTOPERATION */
+ /* ******************************< */
+ sendSignal(cownBlockref, GSN_NEXTOPERATION, signal, 1, JBB);
+ return;
+}//Dbacc::undoNext2Lab()
+
+/*-----------------------------------------------------------------------------------*/
+/* AFTER COMPLETING THE READING OF DATA PAGES FROM DISK AND EXECUTING THE UNDO */
+/* LOG WE ARE READY TO UPDATE THE FREE LIST OF OVERFLOW PAGES. THIS LIST MUST */
+/* BE BUILT AGAIN SINCE IT IS NOT CHECKPOINTED. WHEN THE PAGES ARE ALLOCATED */
+/* THEY ARE NOT PART OF ANY LIST. PAGES CAN EITHER BE PUT IN FREE LIST, NOT */
+/* IN FREE LIST OR BE PUT INTO LIST OF LONG KEY PAGES. */
+/*-----------------------------------------------------------------------------------*/
+void Dbacc::execACC_OVER_REC(Signal* signal)
+{
+ DirRangePtr pnoDirRangePtr;
+ DirectoryarrayPtr pnoOverflowDirptr;
+ Page8Ptr pnoPageidptr;
+ Uint32 tpnoPageType;
+ Uint32 toverPageCheck;
+
+ jamEntry();
+ fragrecptr.i = signal->theData[0];
+ toverPageCheck = 0;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ ndbrequire((fragrecptr.p->nextAllocPage != 0) ||
+ (fragrecptr.p->firstOverflowRec == RNIL));
+ /*-----------------------------------------------------------------------------------*/
+ /* WHO HAS PUT SOMETHING INTO THE LIST BEFORE WE EVEN STARTED PUTTING THINGS */
+ /* THERE. */
+ /*-----------------------------------------------------------------------------------*/
+ ndbrequire(fragrecptr.p->loadingFlag == ZTRUE);
+ /*---------------------------------------------------------------------------*/
+ /* LOADING HAS STOPPED BEFORE WE HAVE LOADED, SYSTEM ERROR. */
+ /*---------------------------------------------------------------------------*/
+ while (toverPageCheck < ZNO_OF_OP_PER_SIGNAL) {
+ jam();
+ if (fragrecptr.p->nextAllocPage >= fragrecptr.p->lastOverIndex) {
+ jam();
+ fragrecptr.p->loadingFlag = ZFALSE;
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (rootfragrecptr.p->lcpPtr != RNIL) {
+ jam();
+ srCloseDataFileLab(signal);
+ } else {
+ jam();
+ undoNext2Lab(signal);
+ }//if
+ return;
+ }//if
+ tmpP = fragrecptr.p->nextAllocPage;
+ pnoDirRangePtr.i = fragrecptr.p->overflowdir;
+ tmpP2 = tmpP >> 8;
+ tmpP = tmpP & 0xff;
+ arrGuard(tmpP2, 256);
+ ptrCheckGuard(pnoDirRangePtr, cdirrangesize, dirRange);
+ if (pnoDirRangePtr.p->dirArray[tmpP2] == RNIL) {
+ jam();
+ pnoPageidptr.i = RNIL;
+ } else {
+ pnoOverflowDirptr.i = pnoDirRangePtr.p->dirArray[tmpP2];
+ if (pnoOverflowDirptr.i == RNIL) {
+ jam();
+ pnoPageidptr.i = RNIL;
+ } else {
+ jam();
+ ptrCheckGuard(pnoOverflowDirptr, cdirarraysize, directoryarray);
+ pnoPageidptr.i = pnoOverflowDirptr.p->pagep[tmpP];
+ }//if
+ }//if
+ if (pnoPageidptr.i == RNIL) {
+ jam();
+ seizeOverRec(signal);
+ sorOverflowRecPtr.p->dirindex = fragrecptr.p->nextAllocPage;
+ sorOverflowRecPtr.p->overpage = RNIL;
+ priOverflowRecPtr = sorOverflowRecPtr;
+ putRecInFreeOverdir(signal);
+ } else {
+ ptrCheckGuard(pnoPageidptr, cpagesize, page8);
+ tpnoPageType = pnoPageidptr.p->word32[ZPOS_PAGE_TYPE];
+ tpnoPageType = (tpnoPageType >> ZPOS_PAGE_TYPE_BIT) & 3;
+ if (pnoPageidptr.p->word32[ZPOS_ALLOC_CONTAINERS] > ZFREE_LIMIT) {
+ jam();
+ dbgWord32(pnoPageidptr, ZPOS_OVERFLOWREC, RNIL);
+ pnoPageidptr.p->word32[ZPOS_OVERFLOWREC] = RNIL;
+ ndbrequire(pnoPageidptr.p->word32[ZPOS_PAGE_ID] == fragrecptr.p->nextAllocPage);
+ } else {
+ jam();
+ seizeOverRec(signal);
+ sorOverflowRecPtr.p->dirindex = pnoPageidptr.p->word32[ZPOS_PAGE_ID];
+ ndbrequire(sorOverflowRecPtr.p->dirindex == fragrecptr.p->nextAllocPage);
+ dbgWord32(pnoPageidptr, ZPOS_OVERFLOWREC, sorOverflowRecPtr.i);
+ pnoPageidptr.p->word32[ZPOS_OVERFLOWREC] = sorOverflowRecPtr.i;
+ sorOverflowRecPtr.p->overpage = pnoPageidptr.i;
+ porOverflowRecPtr = sorOverflowRecPtr;
+ putOverflowRecInFrag(signal);
+ if (pnoPageidptr.p->word32[ZPOS_ALLOC_CONTAINERS] == 0) {
+ jam();
+ ropPageptr = pnoPageidptr;
+ releaseOverpage(signal);
+ }//if
+ }//if
+ }//if
+ fragrecptr.p->nextAllocPage++;
+ toverPageCheck++;
+ }//while
+ signal->theData[0] = fragrecptr.i;
+ sendSignal(cownBlockref, GSN_ACC_OVER_REC, signal, 1, JBB);
+}//Dbacc::execACC_OVER_REC()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF SYSTEM RESTART MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* SCAN MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACC_SCANREQ START OF A SCAN PROCESS */
+/* SENDER: LQH, LEVEL B */
+/* ENTER ACC_SCANREQ WITH */
+/* TUSERPTR, LQH SCAN_CONNECT POINTER */
+/* TUSERBLOCKREF, LQH BLOCK REFERENCE */
+/* TABPTR, TABLE IDENTITY AND PTR */
+/* TFID ROOT FRAGMENT IDENTITY */
+/* TSCAN_FLAG , = ZCOPY, ZSCAN, ZSCAN_LOCK_ALL */
+/* ZREADLOCK, ZWRITELOCK */
+/* TSCAN_TRID1 , TRANSACTION ID PART 1 */
+/* TSCAN_TRID2 TRANSACTION ID PART 2 */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACC_SCANREQ START OF A SCAN PROCESS */
+/* ******************------------------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execACC_SCANREQ(Signal* signal)
+{
+ jamEntry();
+ AccScanReq * req = (AccScanReq*)&signal->theData[0];
+ tuserptr = req->senderData;
+ tuserblockref = req->senderRef;
+ tabptr.i = req->tableId;
+ tfid = req->fragmentNo;
+ tscanFlag = req->requestInfo;
+ tscanTrid1 = req->transId1;
+ tscanTrid2 = req->transId2;
+
+ tresult = 0;
+ ptrCheckGuard(tabptr, ctablesize, tabrec);
+ ndbrequire(getrootfragmentrec(signal,rootfragrecptr, tfid));
+
+ Uint32 i;
+ for (i = 0; i < MAX_PARALLEL_SCANS_PER_FRAG; i++) {
+ jam();
+ if (rootfragrecptr.p->scan[i] == RNIL) {
+ jam();
+ break;
+ }
+ }
+ ndbrequire(i != MAX_PARALLEL_SCANS_PER_FRAG);
+ ndbrequire(cfirstFreeScanRec != RNIL);
+ seizeScanRec(signal);
+
+ rootfragrecptr.p->scan[i] = scanPtr.i;
+ scanPtr.p->scanBucketState = ScanRec::FIRST_LAP;
+ scanPtr.p->scanLockMode = AccScanReq::getLockMode(tscanFlag);
+ scanPtr.p->scanReadCommittedFlag = AccScanReq::getReadCommittedFlag(tscanFlag);
+
+ /* TWELVE BITS OF THE ELEMENT HEAD ARE SCAN */
+ /* CHECK BITS. THE MASK NOTES WHICH BIT IS */
+ /* ALLOCATED FOR THE ACTIVE SCAN */
+ scanPtr.p->scanMask = 1 << i;
+ scanPtr.p->scanUserptr = tuserptr;
+ scanPtr.p->scanUserblockref = tuserblockref;
+ scanPtr.p->scanTrid1 = tscanTrid1;
+ scanPtr.p->scanTrid2 = tscanTrid2;
+ scanPtr.p->rootPtr = rootfragrecptr.i;
+ scanPtr.p->scanLockHeld = 0;
+ scanPtr.p->scanOpsAllocated = 0;
+ scanPtr.p->scanFirstActiveOp = RNIL;
+ scanPtr.p->scanFirstQueuedOp = RNIL;
+ scanPtr.p->scanLastQueuedOp = RNIL;
+ scanPtr.p->scanFirstLockedOp = RNIL;
+ scanPtr.p->scanLastLockedOp = RNIL;
+ scanPtr.p->scanState = ScanRec::WAIT_NEXT;
+ fragrecptr.i = rootfragrecptr.p->fragmentptr[0];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ initScanFragmentPart(signal);
+
+ /*------------------------------------------------------*/
+ /* We start the timeout loop for the scan process here. */
+ /*------------------------------------------------------*/
+ ndbrequire(scanPtr.p->scanTimer == 0);
+ if (scanPtr.p->scanContinuebCounter == 0) {
+ jam();
+ scanPtr.p->scanContinuebCounter = 1;
+ signal->theData[0] = ZSEND_SCAN_HBREP;
+ signal->theData[1] = scanPtr.i;
+ sendSignalWithDelay(cownBlockref, GSN_CONTINUEB, signal, 100, 2);
+ }//if
+ scanPtr.p->scanTimer = scanPtr.p->scanContinuebCounter;
+ /* ************************ */
+ /* ACC_SCANCONF */
+ /* ************************ */
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] = scanPtr.i;
+ signal->theData[2] = 2;
+ /* NR OF LOCAL FRAGMENT */
+ signal->theData[3] = rootfragrecptr.p->fragmentid[0];
+ signal->theData[4] = rootfragrecptr.p->fragmentid[1];
+ signal->theData[7] = AccScanConf::ZNOT_EMPTY_FRAGMENT;
+ sendSignal(scanPtr.p->scanUserblockref, GSN_ACC_SCANCONF, signal, 8, JBB);
+ /* NOT EMPTY FRAGMENT */
+ return;
+}//Dbacc::execACC_SCANREQ()
+
+/* ******************--------------------------------------------------------------- */
+/* NEXT_SCANREQ REQUEST FOR NEXT ELEMENT OF */
+/* ******************------------------------------+ A FRAGMENT. */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execNEXT_SCANREQ(Signal* signal)
+{
+ Uint32 tscanNextFlag;
+ jamEntry();
+ scanPtr.i = signal->theData[0];
+ operationRecPtr.i = signal->theData[1];
+ tscanNextFlag = signal->theData[2];
+ /* ------------------------------------------ */
+ /* 1 = ZCOPY_NEXT GET NEXT ELEMENT */
+ /* 2 = ZCOPY_NEXT_COMMIT COMMIT THE */
+ /* ACTIVE ELEMENT AND GET THE NEXT ONE */
+ /* 3 = ZCOPY_COMMIT COMMIT THE ACTIVE ELEMENT */
+ /* 4 = ZCOPY_REPEAT GET THE ACTIVE ELEMENT */
+ /* 5 = ZCOPY_ABORT RELOCK THE ACTIVE ELEMENT */
+ /* 6 = ZCOPY_CLOSE THE SCAN PROCESS IS READY */
+ /* ------------------------------------------ */
+ tresult = 0;
+ ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
+ ndbrequire(scanPtr.p->scanState == ScanRec::WAIT_NEXT);
+
+ scanPtr.p->scanTimer = scanPtr.p->scanContinuebCounter;
+ switch (tscanNextFlag) {
+ case ZCOPY_NEXT:
+ jam();
+ /*empty*/;
+ break;
+ case ZCOPY_NEXT_COMMIT:
+ case ZCOPY_COMMIT:
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* COMMIT ACTIVE OPERATION. SEND NEXT SCAN ELEMENT IF IT IS ZCOPY_NEXT_COMMIT. */
+ /* --------------------------------------------------------------------------------- */
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_COMMIT) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // We did not have enough undo log buffers to safely commit an
+ // operation. Try again in 10 milliseconds.
+ /*--------------------------------------------------------------*/
+ sendSignalWithDelay(cownBlockref, GSN_NEXT_SCANREQ, signal, 10, 3);
+ return;
+ }//if
+ }//if
+ commitOperation(signal);
+ }//if
+ takeOutActiveScanOp(signal);
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ if (tscanNextFlag == ZCOPY_COMMIT) {
+ jam();
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ Uint32 blockNo = refToBlock(scanPtr.p->scanUserblockref);
+ EXECUTE_DIRECT(blockNo, GSN_NEXT_SCANCONF, signal, 1);
+ return;
+ }//if
+ break;
+ case ZCOPY_CLOSE:
+ jam();
+ fragrecptr.i = scanPtr.p->activeLocalFrag;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_OPERATION) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // We did not have enough undo log buffers to commit a set of
+ // operations. Try again in 10 milliseconds.
+ /*--------------------------------------------------------------*/
+ sendSignalWithDelay(cownBlockref, GSN_NEXT_SCANREQ, signal, 10, 3);
+ return;
+ }//if
+ }//if
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* THE SCAN PROCESS IS FINISHED. RELOCK ALL LOCKED EL. RELESE ALL INVOLVED REC. */
+ /* --------------------------------------------------------------------------------- */
+ releaseScanLab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZNOT_CHECK_LCP_STOP;
+ execACC_CHECK_SCAN(signal);
+ return;
+}//Dbacc::execNEXT_SCANREQ()
+
+void Dbacc::checkNextBucketLab(Signal* signal)
+{
+ DirRangePtr cscDirRangePtr;
+ DirectoryarrayPtr cscDirptr;
+ DirectoryarrayPtr tnsDirptr;
+ Page8Ptr nsPageptr;
+ Page8Ptr cscPageidptr;
+ Page8Ptr gnsPageidptr;
+ Page8Ptr tnsPageidptr;
+ Uint32 tnsElementptr;
+ Uint32 tnsContainerptr;
+ Uint32 tnsIsLocked;
+ Uint32 tnsTmp1;
+ Uint32 tnsTmp2;
+ Uint32 tnsCopyIndex1;
+ Uint32 tnsCopyIndex2;
+ Uint32 tnsCopyDir;
+
+ tnsCopyDir = scanPtr.p->nextBucketIndex >> fragrecptr.p->k;
+ tnsCopyIndex1 = tnsCopyDir >> 8;
+ tnsCopyIndex2 = tnsCopyDir & 0xff;
+ arrGuard(tnsCopyIndex1, 256);
+ tnsDirptr.i = gnsDirRangePtr.p->dirArray[tnsCopyIndex1];
+ ptrCheckGuard(tnsDirptr, cdirarraysize, directoryarray);
+ tnsPageidptr.i = tnsDirptr.p->pagep[tnsCopyIndex2];
+ ptrCheckGuard(tnsPageidptr, cpagesize, page8);
+ gnsPageidptr.i = tnsPageidptr.i;
+ gnsPageidptr.p = tnsPageidptr.p;
+ tnsTmp1 = (1 << fragrecptr.p->k) - 1;
+ tgsePageindex = scanPtr.p->nextBucketIndex & tnsTmp1;
+ gsePageidptr.i = gnsPageidptr.i;
+ gsePageidptr.p = gnsPageidptr.p;
+ if (!getScanElement(signal)) {
+ scanPtr.p->nextBucketIndex++;
+ if (scanPtr.p->scanBucketState == ScanRec::SECOND_LAP) {
+ if (scanPtr.p->nextBucketIndex > scanPtr.p->maxBucketIndexToRescan) {
+ /* --------------------------------------------------------------------------------- */
+ // We have finished the rescan phase. We are ready to proceed with the next fragment part.
+ /* --------------------------------------------------------------------------------- */
+ jam();
+ checkNextFragmentLab(signal);
+ return;
+ }//if
+ } else if (scanPtr.p->scanBucketState == ScanRec::FIRST_LAP) {
+ if ((fragrecptr.p->p + fragrecptr.p->maxp) < scanPtr.p->nextBucketIndex) {
+ /* --------------------------------------------------------------------------------- */
+ // All buckets have been scanned a first time.
+ /* --------------------------------------------------------------------------------- */
+ if (scanPtr.p->minBucketIndexToRescan == 0xFFFFFFFF) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // We have not had any merges behind the scan. Thus it is not necessary to perform
+ // any rescan any buckets and we can proceed immediately with the next fragment part.
+ /* --------------------------------------------------------------------------------- */
+ checkNextFragmentLab(signal);
+ return;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // Some buckets are in the need of rescanning due to merges that have moved records
+ // from in front of the scan to behind the scan. During the merges we kept track of
+ // which buckets that need a rescan. We start with the minimum and end with maximum.
+ /* --------------------------------------------------------------------------------- */
+ scanPtr.p->nextBucketIndex = scanPtr.p->minBucketIndexToRescan;
+ scanPtr.p->scanBucketState = ScanRec::SECOND_LAP;
+ if (scanPtr.p->maxBucketIndexToRescan > (fragrecptr.p->p + fragrecptr.p->maxp)) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // If we have had so many merges that the maximum is bigger than the number of buckets
+ // then we will simply satisfy ourselves with scanning to the end. This can only happen
+ // after bringing down the total of buckets to less than half and the minimum should
+ // be 0 otherwise there is some problem.
+ /* --------------------------------------------------------------------------------- */
+ if (scanPtr.p->minBucketIndexToRescan != 0) {
+ jam();
+ sendSystemerror(signal);
+ return;
+ }//if
+ scanPtr.p->maxBucketIndexToRescan = fragrecptr.p->p + fragrecptr.p->maxp;
+ }//if
+ }//if
+ }//if
+ }//if
+ if ((scanPtr.p->scanBucketState == ScanRec::FIRST_LAP) &&
+ (scanPtr.p->nextBucketIndex <= scanPtr.p->startNoOfBuckets)) {
+ /* --------------------------------------------------------------------------------- */
+ // We will only reset the scan indicator on the buckets that existed at the start of the
+ // scan. The others will be handled by the split and merge code.
+ /* --------------------------------------------------------------------------------- */
+ tnsTmp2 = (1 << fragrecptr.p->k) - 1;
+ trsbPageindex = scanPtr.p->nextBucketIndex & tnsTmp2;
+ if (trsbPageindex != 0) {
+ jam();
+ rsbPageidptr.i = gnsPageidptr.i;
+ rsbPageidptr.p = gnsPageidptr.p;
+ } else {
+ jam();
+ cscDirRangePtr.i = fragrecptr.p->directory;
+ tmpP = scanPtr.p->nextBucketIndex >> fragrecptr.p->k;
+ tmpP2 = tmpP >> 8;
+ tmpP = tmpP & 0xff;
+ ptrCheckGuard(cscDirRangePtr, cdirrangesize, dirRange);
+ arrGuard(tmpP2, 256);
+ cscDirptr.i = cscDirRangePtr.p->dirArray[tmpP2];
+ ptrCheckGuard(cscDirptr, cdirarraysize, directoryarray);
+ cscPageidptr.i = cscDirptr.p->pagep[tmpP];
+ ptrCheckGuard(cscPageidptr, cpagesize, page8);
+ tmp1 = (1 << fragrecptr.p->k) - 1;
+ trsbPageindex = scanPtr.p->nextBucketIndex & tmp1;
+ rsbPageidptr.i = cscPageidptr.i;
+ rsbPageidptr.p = cscPageidptr.p;
+ }//if
+ releaseScanBucket(signal);
+ }//if
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ }//if
+ /* ----------------------------------------------------------------------- */
+ /* AN ELEMENT WHICH HAVE NOT BEEN SCANNED WAS FOUND. WE WILL PREPARE IT */
+ /* TO BE SENT TO THE LQH BLOCK FOR FURTHER PROCESSING. */
+ /* WE ASSUME THERE ARE OPERATION RECORDS AVAILABLE SINCE LQH SHOULD HAVE*/
+ /* GUARANTEED THAT THROUGH EARLY BOOKING. */
+ /* ----------------------------------------------------------------------- */
+ tnsIsLocked = tgseIsLocked;
+ tnsElementptr = tgseElementptr;
+ tnsContainerptr = tgseContainerptr;
+ nsPageptr.i = gsePageidptr.i;
+ nsPageptr.p = gsePageidptr.p;
+ seizeOpRec(signal);
+ tisoIsforward = tgseIsforward;
+ tisoContainerptr = tnsContainerptr;
+ tisoElementptr = tnsElementptr;
+ isoPageptr.i = nsPageptr.i;
+ isoPageptr.p = nsPageptr.p;
+ initScanOpRec(signal);
+
+ if (!tnsIsLocked){
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ jam();
+ slPageidptr = nsPageptr;
+ tslElementptr = tnsElementptr;
+ setlock(signal);
+ insertLockOwnersList(signal, operationRecPtr);
+ }//if
+ } else {
+ arrGuard(tnsElementptr, 2048);
+ queOperPtr.i =
+ ElementHeader::getOpPtrI(nsPageptr.p->word32[tnsElementptr]);
+ ptrCheckGuard(queOperPtr, coprecsize, operationrec);
+ if (queOperPtr.p->elementIsDisappeared == ZTRUE) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // If the lock owner indicates the element is disappeared then we will not report this
+ // tuple. We will continue with the next tuple.
+ /* --------------------------------------------------------------------------------- */
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ }//if
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ Uint32 return_result;
+ if (scanPtr.p->scanLockMode == ZREADLOCK) {
+ jam();
+ priPageptr = nsPageptr;
+ tpriElementptr = tnsElementptr;
+ return_result = placeReadInLockQueue(signal);
+ } else {
+ jam();
+ pwiPageptr = nsPageptr;
+ tpwiElementptr = tnsElementptr;
+ return_result = placeWriteInLockQueue(signal);
+ }//if
+ if (return_result == ZSERIAL_QUEUE) {
+ /* --------------------------------------------------------------------------------- */
+ /* WE PLACED THE OPERATION INTO A SERIAL QUEUE AND THUS WE HAVE TO WAIT FOR */
+ /* THE LOCK TO BE RELEASED. WE CONTINUE WITH THE NEXT ELEMENT. */
+ /* --------------------------------------------------------------------------------- */
+ putOpScanLockQue(); /* PUT THE OP IN A QUE IN THE SCAN REC */
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ } else if (return_result == ZWRITE_ERROR) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // The tuple is either not committed yet or a delete in the same transaction (not
+ // possible here since we are a scan). Thus we simply continue with the next tuple.
+ /* --------------------------------------------------------------------------------- */
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ }//if
+ ndbassert(return_result == ZPARALLEL_QUEUE);
+ }//if
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ // Committed read proceed without caring for locks immediately down here except when
+ // the tuple was deleted permanently and no new operation has inserted it again.
+ /* --------------------------------------------------------------------------------- */
+ putActiveScanOp(signal);
+ sendNextScanConf(signal);
+ return;
+}//Dbacc::checkNextBucketLab()
+
+
+void Dbacc::checkNextFragmentLab(Signal* signal)
+{
+ RootfragmentrecPtr cnfRootfragrecptr;
+
+ cnfRootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(cnfRootfragrecptr, crootfragmentsize, rootfragmentrec);
+ if (scanPtr.p->activeLocalFrag == cnfRootfragrecptr.p->fragmentptr[0]) {
+ jam();
+ fragrecptr.i = cnfRootfragrecptr.p->fragmentptr[1];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ initScanFragmentPart(signal);
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ } else {
+ if (scanPtr.p->activeLocalFrag == cnfRootfragrecptr.p->fragmentptr[1]) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ // Both fragments have completed their scan part and we can indicate that the scan is
+ // now completed.
+ /* --------------------------------------------------------------------------------- */
+ scanPtr.p->scanBucketState = ScanRec::SCAN_COMPLETED;
+ /*empty*/;
+ } else {
+ jam();
+ /* ALL ELEMENTS ARE SENT */
+ sendSystemerror(signal);
+ }//if
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ // The scan is completed. ACC_CHECK_SCAN will perform all the necessary checks to see
+ // what the next step is.
+ /* --------------------------------------------------------------------------------- */
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ execACC_CHECK_SCAN(signal);
+ return;
+}//Dbacc::checkNextFragmentLab()
+
+void Dbacc::initScanFragmentPart(Signal* signal)
+{
+ DirRangePtr cnfDirRangePtr;
+ DirectoryarrayPtr cnfDirptr;
+ Page8Ptr cnfPageidptr;
+ /* --------------------------------------------------------------------------------- */
+ // Set the active fragment part.
+ // Set the current bucket scanned to the first.
+ // Start with the first lap.
+ // Remember the number of buckets at start of the scan.
+ // Set the minimum and maximum to values that will always be smaller and larger than.
+ // Reset the scan indicator on the first bucket.
+ /* --------------------------------------------------------------------------------- */
+ scanPtr.p->activeLocalFrag = fragrecptr.i;
+ scanPtr.p->nextBucketIndex = 0; /* INDEX OF SCAN BUCKET */
+ scanPtr.p->scanBucketState = ScanRec::FIRST_LAP;
+ scanPtr.p->startNoOfBuckets = fragrecptr.p->p + fragrecptr.p->maxp;
+ scanPtr.p->minBucketIndexToRescan = 0xFFFFFFFF;
+ scanPtr.p->maxBucketIndexToRescan = 0;
+ cnfDirRangePtr.i = fragrecptr.p->directory;
+ ptrCheckGuard(cnfDirRangePtr, cdirrangesize, dirRange);
+ cnfDirptr.i = cnfDirRangePtr.p->dirArray[0];
+ ptrCheckGuard(cnfDirptr, cdirarraysize, directoryarray);
+ cnfPageidptr.i = cnfDirptr.p->pagep[0];
+ ptrCheckGuard(cnfPageidptr, cpagesize, page8);
+ trsbPageindex = scanPtr.p->nextBucketIndex & ((1 << fragrecptr.p->k) - 1);
+ rsbPageidptr.i = cnfPageidptr.i;
+ rsbPageidptr.p = cnfPageidptr.p;
+ releaseScanBucket(signal);
+}//Dbacc::initScanFragmentPart()
+
+/* --------------------------------------------------------------------------------- */
+/* FLAG = 6 = ZCOPY_CLOSE THE SCAN PROCESS IS READY OR ABORTED. ALL OPERATION IN THE */
+/* ACTIVE OR WAIT QUEUE ARE RELEASED, SCAN FLAG OF ROOT FRAG IS RESET AND THE SCAN */
+/* RECORD IS RELEASED. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseScanLab(Signal* signal)
+{
+ releaseAndCommitActiveOps(signal);
+ releaseAndCommitQueuedOps(signal);
+ releaseAndAbortLockedOps(signal);
+
+ rootfragrecptr.i = scanPtr.p->rootPtr;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ for (tmp = 0; tmp < MAX_PARALLEL_SCANS_PER_FRAG; tmp++) {
+ jam();
+ if (rootfragrecptr.p->scan[tmp] == scanPtr.i) {
+ jam();
+ rootfragrecptr.p->scan[tmp] = RNIL;
+ }//if
+ }//for
+ // Stops the heartbeat.
+ scanPtr.p->scanTimer = 0;
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] = RNIL;
+ signal->theData[2] = RNIL;
+ sendSignal(scanPtr.p->scanUserblockref, GSN_NEXT_SCANCONF, signal, 3, JBB);
+ releaseScanRec(signal);
+ return;
+}//Dbacc::releaseScanLab()
+
+
+void Dbacc::releaseAndCommitActiveOps(Signal* signal)
+{
+ OperationrecPtr trsoOperPtr;
+ operationRecPtr.i = scanPtr.p->scanFirstActiveOp;
+ while (operationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ trsoOperPtr.i = operationRecPtr.p->nextOp;
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ jam();
+ commitOperation(signal);
+ }//if
+ takeOutActiveScanOp(signal);
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ operationRecPtr.i = trsoOperPtr.i;
+ }//if
+}//Dbacc::releaseAndCommitActiveOps()
+
+
+void Dbacc::releaseAndCommitQueuedOps(Signal* signal)
+{
+ OperationrecPtr trsoOperPtr;
+ operationRecPtr.i = scanPtr.p->scanFirstQueuedOp;
+ while (operationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ trsoOperPtr.i = operationRecPtr.p->nextOp;
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ jam();
+ commitOperation(signal);
+ }//if
+ takeOutReadyScanQueue(signal);
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ operationRecPtr.i = trsoOperPtr.i;
+ }//if
+}//Dbacc::releaseAndCommitQueuedOps()
+
+void Dbacc::releaseAndAbortLockedOps(Signal* signal) {
+
+ OperationrecPtr trsoOperPtr;
+ operationRecPtr.i = scanPtr.p->scanFirstLockedOp;
+ while (operationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ trsoOperPtr.i = operationRecPtr.p->nextOp;
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (!scanPtr.p->scanReadCommittedFlag) {
+ jam();
+ abortOperation(signal);
+ }//if
+ takeOutScanLockQueue(scanPtr.i);
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ operationRecPtr.i = trsoOperPtr.i;
+ }//if
+}//Dbacc::releaseAndAbortLockedOps()
+
+/* 3.18.3 ACC_CHECK_SCAN */
+/* ******************--------------------------------------------------------------- */
+/* ACC_CHECK_SCAN */
+/* ENTER ACC_CHECK_SCAN WITH */
+/* SCAN_PTR */
+/* ******************--------------------------------------------------------------- */
+/* ******************--------------------------------------------------------------- */
+/* ACC_CHECK_SCAN */
+/* ******************------------------------------+ */
+void Dbacc::execACC_CHECK_SCAN(Signal* signal)
+{
+ Uint32 TcheckLcpStop;
+ jamEntry();
+ scanPtr.i = signal->theData[0];
+ TcheckLcpStop = signal->theData[1];
+ ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
+ while (scanPtr.p->scanFirstQueuedOp != RNIL) {
+ jam();
+ //----------------------------------------------------------------------------
+ // An operation has been released from the lock queue. We are in the parallel
+ // queue of this tuple. We are ready to report the tuple now.
+ //----------------------------------------------------------------------------
+ operationRecPtr.i = scanPtr.p->scanFirstQueuedOp;
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ takeOutReadyScanQueue(signal);
+ if (operationRecPtr.p->elementIsDisappeared == ZTRUE) {
+ jam();
+ fragrecptr.i = operationRecPtr.p->fragptr;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ if (fragrecptr.p->createLcp == ZTRUE) {
+ if (remainingUndoPages() < ZMIN_UNDO_PAGES_AT_COMMIT) {
+ jam();
+ /*--------------------------------------------------------------*/
+ // We did not have enough undo log buffers to safely abort an
+ // operation. Try again in 10 milliseconds.
+ /*--------------------------------------------------------------*/
+ sendSignalWithDelay(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 10, 2);
+ return;
+ }//if
+ }//if
+ abortOperation(signal);
+ releaseOpRec(signal);
+ scanPtr.p->scanOpsAllocated--;
+ continue;
+ }//if
+ putActiveScanOp(signal);
+ sendNextScanConf(signal);
+ return;
+ }//while
+
+
+ if ((scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) &&
+ (scanPtr.p->scanLockHeld == 0)) {
+ jam();
+ //----------------------------------------------------------------------------
+ // The scan is now completed and there are no more locks outstanding. Thus we
+ // we will report the scan as completed to LQH.
+ //----------------------------------------------------------------------------
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] = RNIL;
+ signal->theData[2] = RNIL;
+ sendSignal(scanPtr.p->scanUserblockref, GSN_NEXT_SCANCONF, signal, 3, JBB);
+ return;
+ }//if
+ if (TcheckLcpStop == AccCheckScan::ZCHECK_LCP_STOP) {
+ //---------------------------------------------------------------------------
+ // To ensure that the block of the fragment occurring at the start of a local
+ // checkpoint is not held for too long we insert a release and reacquiring of
+ // that lock here. This is performed in LQH. If we are blocked or if we have
+ // requested a sleep then we will receive RNIL in the returning signal word.
+ //---------------------------------------------------------------------------
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] =
+ ((scanPtr.p->scanLockHeld >= ZSCAN_MAX_LOCK) ||
+ (scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED));
+ EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2);
+ jamEntry();
+ if (signal->theData[0] == RNIL) {
+ jam();
+ return;
+ }//if
+ }//if
+ /**
+ * If we have more than max locks held OR
+ * scan is completed AND at least one lock held
+ * - Inform LQH about this condition
+ */
+ if ((scanPtr.p->scanLockHeld >= ZSCAN_MAX_LOCK) ||
+ (cfreeopRec == RNIL) ||
+ ((scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) &&
+ (scanPtr.p->scanLockHeld > 0))) {
+ jam();
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] = RNIL; // No operation is returned
+ signal->theData[2] = 512; // MASV
+ sendSignal(scanPtr.p->scanUserblockref, GSN_NEXT_SCANCONF, signal, 3, JBB);
+ return;
+ }
+ if (scanPtr.p->scanBucketState == ScanRec::SCAN_COMPLETED) {
+ jam();
+ signal->theData[0] = scanPtr.i;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ execACC_CHECK_SCAN(signal);
+ return;
+ }//if
+
+ scanPtr.p->scanTimer = scanPtr.p->scanContinuebCounter;
+
+ fragrecptr.i = scanPtr.p->activeLocalFrag;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ gnsDirRangePtr.i = fragrecptr.p->directory;
+ ptrCheckGuard(gnsDirRangePtr, cdirrangesize, dirRange);
+ checkNextBucketLab(signal);
+ return;
+}//Dbacc::execACC_CHECK_SCAN()
+
+/* ******************---------------------------------------------------- */
+/* ACC_TO_REQ PERFORM A TAKE OVER */
+/* ******************-------------------+ */
+/* SENDER: LQH, LEVEL B */
+void Dbacc::execACC_TO_REQ(Signal* signal)
+{
+ OperationrecPtr tatrOpPtr;
+
+ jamEntry();
+ tatrOpPtr.i = signal->theData[1]; /* OPER PTR OF ACC */
+ ptrCheckGuard(tatrOpPtr, coprecsize, operationrec);
+ if (tatrOpPtr.p->operation == ZSCAN_OP) {
+ tatrOpPtr.p->transId1 = signal->theData[2];
+ tatrOpPtr.p->transId2 = signal->theData[3];
+ } else {
+ jam();
+ signal->theData[0] = cminusOne;
+ signal->theData[1] = ZTO_OP_STATE_ERROR;
+ }//if
+ return;
+}//Dbacc::execACC_TO_REQ()
+
+/* --------------------------------------------------------------------------------- */
+/* CONTAINERINFO */
+/* INPUT: */
+/* CI_PAGEIDPTR (PAGE POINTER WHERE CONTAINER RESIDES) */
+/* TCI_PAGEINDEX (INDEX OF CONTAINER, USED TO CALCULATE PAGE INDEX) */
+/* TCI_ISFORWARD (DIRECTION OF CONTAINER FORWARD OR BACKWARD) */
+/* */
+/* OUTPUT: */
+/* TCI_CONTAINERPTR (A POINTER TO THE HEAD OF THE CONTAINER) */
+/* TCI_CONTAINERLEN (LENGTH OF THE CONTAINER */
+/* TCI_CONTAINERHEAD (THE HEADER OF THE CONTAINER) */
+/* */
+/* DESCRIPTION: THE ADDRESS OF THE CONTAINER WILL BE CALCULATED AND */
+/* ALL INFORMATION ABOUT THE CONTAINER WILL BE READ */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::containerinfo(Signal* signal)
+{
+ tciContainerptr = (tciPageindex << ZSHIFT_PLUS) - (tciPageindex << ZSHIFT_MINUS);
+ if (tciIsforward == ZTRUE) {
+ jam();
+ tciContainerptr = tciContainerptr + ZHEAD_SIZE;
+ } else {
+ jam();
+ tciContainerptr = ((tciContainerptr + ZHEAD_SIZE) + ZBUF_SIZE) - ZCON_HEAD_SIZE;
+ }//if
+ arrGuard(tciContainerptr, 2048);
+ tciContainerhead = ciPageidptr.p->word32[tciContainerptr];
+ tciContainerlen = tciContainerhead >> 26;
+}//Dbacc::containerinfo()
+
+/* --------------------------------------------------------------------------------- */
+/* GET_SCAN_ELEMENT */
+/* INPUT: GSE_PAGEIDPTR */
+/* TGSE_PAGEINDEX */
+/* OUTPUT: TGSE_IS_LOCKED (IF TRESULT /= ZFALSE) */
+/* GSE_PAGEIDPTR */
+/* TGSE_PAGEINDEX */
+/* --------------------------------------------------------------------------------- */
+bool Dbacc::getScanElement(Signal* signal)
+{
+ tgseIsforward = ZTRUE;
+ NEXTSEARCH_SCAN_LOOP:
+ ciPageidptr.i = gsePageidptr.i;
+ ciPageidptr.p = gsePageidptr.p;
+ tciPageindex = tgsePageindex;
+ tciIsforward = tgseIsforward;
+ containerinfo(signal);
+ sscPageidptr.i = gsePageidptr.i;
+ sscPageidptr.p = gsePageidptr.p;
+ tsscContainerlen = tciContainerlen;
+ tsscContainerptr = tciContainerptr;
+ tsscIsforward = tciIsforward;
+ if (searchScanContainer(signal)) {
+ jam();
+ tgseIsLocked = tsscIsLocked;
+ tgseElementptr = tsscElementptr;
+ tgseContainerptr = tsscContainerptr;
+ return true;
+ }//if
+ if (((tciContainerhead >> 7) & 0x3) != 0) {
+ jam();
+ nciPageidptr.i = gsePageidptr.i;
+ nciPageidptr.p = gsePageidptr.p;
+ tnciContainerhead = tciContainerhead;
+ tnciContainerptr = tciContainerptr;
+ nextcontainerinfo(signal);
+ tgsePageindex = tnciPageindex;
+ gsePageidptr.i = nciPageidptr.i;
+ gsePageidptr.p = nciPageidptr.p;
+ tgseIsforward = tnciIsforward;
+ goto NEXTSEARCH_SCAN_LOOP;
+ }//if
+ return false;
+}//Dbacc::getScanElement()
+
+/* --------------------------------------------------------------------------------- */
+/* INIT_SCAN_OP_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initScanOpRec(Signal* signal)
+{
+ Uint32 tisoTmp;
+ Uint32 tisoLocalPtr;
+ Uint32 guard24;
+
+ scanPtr.p->scanOpsAllocated++;
+
+ operationRecPtr.p->scanRecPtr = scanPtr.i;
+ operationRecPtr.p->operation = ZSCAN_OP;
+ operationRecPtr.p->transactionstate = ACTIVE;
+ operationRecPtr.p->commitDeleteCheckFlag = ZFALSE;
+ operationRecPtr.p->lockMode = scanPtr.p->scanLockMode;
+ operationRecPtr.p->fid = fragrecptr.p->myfid;
+ operationRecPtr.p->fragptr = fragrecptr.i;
+ operationRecPtr.p->elementIsDisappeared = ZFALSE;
+ operationRecPtr.p->nextParallelQue = RNIL;
+ operationRecPtr.p->prevParallelQue = RNIL;
+ operationRecPtr.p->nextSerialQue = RNIL;
+ operationRecPtr.p->prevSerialQue = RNIL;
+ operationRecPtr.p->prevQueOp = RNIL;
+ operationRecPtr.p->nextQueOp = RNIL;
+ operationRecPtr.p->keyinfoPage = RNIL; // Safety precaution
+ operationRecPtr.p->transId1 = scanPtr.p->scanTrid1;
+ operationRecPtr.p->transId2 = scanPtr.p->scanTrid2;
+ operationRecPtr.p->lockOwner = ZFALSE;
+ operationRecPtr.p->dirtyRead = 0;
+ operationRecPtr.p->nodeType = 0; // Not a stand-by node
+ operationRecPtr.p->elementIsforward = tisoIsforward;
+ operationRecPtr.p->elementContainer = tisoContainerptr;
+ operationRecPtr.p->elementPointer = tisoElementptr;
+ operationRecPtr.p->elementPage = isoPageptr.i;
+ operationRecPtr.p->isAccLockReq = ZFALSE;
+ operationRecPtr.p->isUndoLogReq = ZFALSE;
+ tisoLocalPtr = tisoElementptr + tisoIsforward;
+ guard24 = fragrecptr.p->localkeylen - 1;
+ for (tisoTmp = 0; tisoTmp <= guard24; tisoTmp++) {
+ arrGuard(tisoTmp, 2);
+ arrGuard(tisoLocalPtr, 2048);
+ operationRecPtr.p->localdata[tisoTmp] = isoPageptr.p->word32[tisoLocalPtr];
+ tisoLocalPtr = tisoLocalPtr + tisoIsforward;
+ }//for
+ arrGuard(tisoLocalPtr, 2048);
+ operationRecPtr.p->keydata[0] = isoPageptr.p->word32[tisoLocalPtr];
+ operationRecPtr.p->tupkeylen = fragrecptr.p->keyLength;
+ operationRecPtr.p->xfrmtupkeylen = 0; // not used
+}//Dbacc::initScanOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* NEXTCONTAINERINFO */
+/* DESCRIPTION:THE CONTAINER HEAD WILL BE CHECKED TO CALCULATE INFORMATION */
+/* ABOUT NEXT CONTAINER IN THE BUCKET. */
+/* INPUT: TNCI_CONTAINERHEAD */
+/* NCI_PAGEIDPTR */
+/* TNCI_CONTAINERPTR */
+/* OUTPUT: */
+/* TNCI_PAGEINDEX (INDEX FROM WHICH PAGE INDEX CAN BE CALCULATED). */
+/* TNCI_ISFORWARD (IS THE NEXT CONTAINER FORWARD (+1) OR BACKWARD (-1) */
+/* NCI_PAGEIDPTR (PAGE REFERENCE OF NEXT CONTAINER) */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::nextcontainerinfo(Signal* signal)
+{
+ tnciNextSamePage = (tnciContainerhead >> 9) & 0x1; /* CHECK BIT FOR CHECKING WHERE */
+ /* THE NEXT CONTAINER IS IN THE SAME PAGE */
+ tnciPageindex = tnciContainerhead & 0x7f; /* NEXT CONTAINER PAGE INDEX 7 BITS */
+ if (((tnciContainerhead >> 7) & 3) == ZLEFT) {
+ jam();
+ tnciIsforward = ZTRUE;
+ } else {
+ jam();
+ tnciIsforward = cminusOne;
+ }//if
+ if (tnciNextSamePage == ZFALSE) {
+ jam();
+ /* NEXT CONTAINER IS IN AN OVERFLOW PAGE */
+ arrGuard(tnciContainerptr + 1, 2048);
+ tnciTmp = nciPageidptr.p->word32[tnciContainerptr + 1];
+ nciOverflowrangeptr.i = fragrecptr.p->overflowdir;
+ ptrCheckGuard(nciOverflowrangeptr, cdirrangesize, dirRange);
+ arrGuard((tnciTmp >> 8), 256);
+ nciOverflowDirptr.i = nciOverflowrangeptr.p->dirArray[tnciTmp >> 8];
+ ptrCheckGuard(nciOverflowDirptr, cdirarraysize, directoryarray);
+ nciPageidptr.i = nciOverflowDirptr.p->pagep[tnciTmp & 0xff];
+ ptrCheckGuard(nciPageidptr, cpagesize, page8);
+ }//if
+}//Dbacc::nextcontainerinfo()
+
+/* --------------------------------------------------------------------------------- */
+/* PUT_ACTIVE_SCAN_OP */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::putActiveScanOp(Signal* signal)
+{
+ OperationrecPtr pasOperationRecPtr;
+ pasOperationRecPtr.i = scanPtr.p->scanFirstActiveOp;
+ if (pasOperationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(pasOperationRecPtr, coprecsize, operationrec);
+ pasOperationRecPtr.p->prevOp = operationRecPtr.i;
+ }//if
+ operationRecPtr.p->nextOp = pasOperationRecPtr.i;
+ operationRecPtr.p->prevOp = RNIL;
+ scanPtr.p->scanFirstActiveOp = operationRecPtr.i;
+}//Dbacc::putActiveScanOp()
+
+/**
+ * putOpScanLockQueue
+ *
+ * Description: Put an operation in the doubly linked
+ * lock list on a scan record. The list is used to
+ * keep track of which operations belonging
+ * to the scan are put in serial lock list of another
+ * operation
+ *
+ * @note Use takeOutScanLockQueue to remove an operation
+ * from the list
+ *
+ */
+void Dbacc::putOpScanLockQue()
+{
+
+#ifdef VM_TRACE
+ // DEBUG CODE
+ // Check that there are as many operations in the lockqueue as
+ // scanLockHeld indicates
+ OperationrecPtr tmpOp;
+ int numLockedOpsBefore = 0;
+ tmpOp.i = scanPtr.p->scanFirstLockedOp;
+ while(tmpOp.i != RNIL){
+ numLockedOpsBefore++;
+ ptrCheckGuard(tmpOp, coprecsize, operationrec);
+ if (tmpOp.p->nextOp == RNIL)
+ ndbrequire(tmpOp.i == scanPtr.p->scanLastLockedOp);
+ tmpOp.i = tmpOp.p->nextOp;
+ }
+ ndbrequire(numLockedOpsBefore==scanPtr.p->scanLockHeld);
+#endif
+
+ OperationrecPtr pslOperationRecPtr;
+ ScanRec theScanRec;
+ theScanRec = *scanPtr.p;
+
+ pslOperationRecPtr.i = scanPtr.p->scanLastLockedOp;
+ operationRecPtr.p->prevOp = pslOperationRecPtr.i;
+ operationRecPtr.p->nextOp = RNIL;
+ if (pslOperationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(pslOperationRecPtr, coprecsize, operationrec);
+ pslOperationRecPtr.p->nextOp = operationRecPtr.i;
+ } else {
+ jam();
+ scanPtr.p->scanFirstLockedOp = operationRecPtr.i;
+ }//if
+ scanPtr.p->scanLastLockedOp = operationRecPtr.i;
+ scanPtr.p->scanLockHeld++;
+
+}//Dbacc::putOpScanLockQue()
+
+/* --------------------------------------------------------------------------------- */
+/* PUT_READY_SCAN_QUEUE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::putReadyScanQueue(Signal* signal, Uint32 scanRecIndex)
+{
+ OperationrecPtr prsOperationRecPtr;
+ ScanRecPtr TscanPtr;
+
+ TscanPtr.i = scanRecIndex;
+ ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);
+
+ prsOperationRecPtr.i = TscanPtr.p->scanLastQueuedOp;
+ operationRecPtr.p->prevOp = prsOperationRecPtr.i;
+ operationRecPtr.p->nextOp = RNIL;
+ TscanPtr.p->scanLastQueuedOp = operationRecPtr.i;
+ if (prsOperationRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(prsOperationRecPtr, coprecsize, operationrec);
+ prsOperationRecPtr.p->nextOp = operationRecPtr.i;
+ } else {
+ jam();
+ TscanPtr.p->scanFirstQueuedOp = operationRecPtr.i;
+ }//if
+}//Dbacc::putReadyScanQueue()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_SCAN_BUCKET */
+// Input:
+// rsbPageidptr.i Index to page where buckets starts
+// rsbPageidptr.p Pointer to page where bucket starts
+// trsbPageindex Page index of starting container in bucket
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseScanBucket(Signal* signal)
+{
+ Uint32 trsbIsforward;
+
+ trsbIsforward = ZTRUE;
+ NEXTRELEASESCANLOOP:
+ ciPageidptr.i = rsbPageidptr.i;
+ ciPageidptr.p = rsbPageidptr.p;
+ tciPageindex = trsbPageindex;
+ tciIsforward = trsbIsforward;
+ containerinfo(signal);
+ rscPageidptr.i = rsbPageidptr.i;
+ rscPageidptr.p = rsbPageidptr.p;
+ trscContainerlen = tciContainerlen;
+ trscContainerptr = tciContainerptr;
+ trscIsforward = trsbIsforward;
+ releaseScanContainer(signal);
+ if (((tciContainerhead >> 7) & 0x3) != 0) {
+ jam();
+ nciPageidptr.i = rsbPageidptr.i;
+ nciPageidptr.p = rsbPageidptr.p;
+ tnciContainerhead = tciContainerhead;
+ tnciContainerptr = tciContainerptr;
+ nextcontainerinfo(signal);
+ rsbPageidptr.i = nciPageidptr.i;
+ rsbPageidptr.p = nciPageidptr.p;
+ trsbPageindex = tnciPageindex;
+ trsbIsforward = tnciIsforward;
+ goto NEXTRELEASESCANLOOP;
+ }//if
+}//Dbacc::releaseScanBucket()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_SCAN_CONTAINER */
+/* INPUT: TRSC_CONTAINERLEN */
+/* RSC_PAGEIDPTR */
+/* TRSC_CONTAINERPTR */
+/* TRSC_ISFORWARD */
+/* SCAN_PTR */
+/* */
+/* DESCRIPTION: SEARCHS IN A CONTAINER, AND THE SCAN BIT OF THE ELEMENTS */
+/* OF THE CONTAINER IS RESET */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseScanContainer(Signal* signal)
+{
+ OperationrecPtr rscOperPtr;
+ Uint32 trscElemStep;
+ Uint32 trscElementptr;
+ Uint32 trscElemlens;
+ Uint32 trscElemlen;
+
+ if (trscContainerlen < 4) {
+ if (trscContainerlen != ZCON_HEAD_SIZE) {
+ jam();
+ sendSystemerror(signal);
+ }//if
+ return; /* 2 IS THE MINIMUM SIZE OF THE ELEMENT */
+ }//if
+ trscElemlens = trscContainerlen - ZCON_HEAD_SIZE;
+ trscElemlen = fragrecptr.p->elementLength;
+ if (trscIsforward == 1) {
+ jam();
+ trscElementptr = trscContainerptr + ZCON_HEAD_SIZE;
+ trscElemStep = trscElemlen;
+ } else {
+ jam();
+ trscElementptr = trscContainerptr - 1;
+ trscElemStep = 0 - trscElemlen;
+ }//if
+ do {
+ arrGuard(trscElementptr, 2048);
+ const Uint32 eh = rscPageidptr.p->word32[trscElementptr];
+ const Uint32 scanMask = scanPtr.p->scanMask;
+ if (ElementHeader::getUnlocked(eh)) {
+ jam();
+ const Uint32 tmp = ElementHeader::clearScanBit(eh, scanMask);
+ dbgWord32(rscPageidptr, trscElementptr, tmp);
+ rscPageidptr.p->word32[trscElementptr] = tmp;
+ } else {
+ jam();
+ rscOperPtr.i = ElementHeader::getOpPtrI(eh);
+ ptrCheckGuard(rscOperPtr, coprecsize, operationrec);
+ rscOperPtr.p->scanBits &= ~scanMask;
+ }//if
+ trscElemlens = trscElemlens - trscElemlen;
+ trscElementptr = trscElementptr + trscElemStep;
+ } while (trscElemlens > 1);
+ if (trscElemlens != 0) {
+ jam();
+ sendSystemerror(signal);
+ }//if
+}//Dbacc::releaseScanContainer()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_SCAN_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseScanRec(Signal* signal)
+{
+ // Check that all ops this scan has allocated have been
+ // released
+ ndbrequire(scanPtr.p->scanOpsAllocated==0);
+
+ // Check that all locks this scan might have aquired
+ // have been properly released
+ ndbrequire(scanPtr.p->scanLockHeld == 0);
+ ndbrequire(scanPtr.p->scanFirstLockedOp == RNIL);
+ ndbrequire(scanPtr.p->scanLastLockedOp == RNIL);
+
+ // Check that all active operations have been
+ // properly released
+ ndbrequire(scanPtr.p->scanFirstActiveOp == RNIL);
+
+ // Check that all queued operations have been
+ // properly released
+ ndbrequire(scanPtr.p->scanFirstQueuedOp == RNIL);
+ ndbrequire(scanPtr.p->scanLastQueuedOp == RNIL);
+
+ // Put scan record in free list
+ scanPtr.p->scanNextfreerec = cfirstFreeScanRec;
+ scanPtr.p->scanState = ScanRec::SCAN_DISCONNECT;
+ cfirstFreeScanRec = scanPtr.i;
+
+}//Dbacc::releaseScanRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEARCH_SCAN_CONTAINER */
+/* INPUT: TSSC_CONTAINERLEN */
+/* TSSC_CONTAINERPTR */
+/* TSSC_ISFORWARD */
+/* SSC_PAGEIDPTR */
+/* SCAN_PTR */
+/* OUTPUT: TSSC_IS_LOCKED */
+/* */
+/* DESCRIPTION: SEARCH IN A CONTAINER TO FIND THE NEXT SCAN ELEMENT. */
+/* TO DO THIS THE SCAN BIT OF THE ELEMENT HEADER IS CHECKED. IF */
+/* THIS BIT IS ZERO, IT IS SET TO ONE AND THE ELEMENT IS RETURNED.*/
+/* --------------------------------------------------------------------------------- */
+bool Dbacc::searchScanContainer(Signal* signal)
+{
+ OperationrecPtr sscOperPtr;
+ Uint32 tsscScanBits;
+ Uint32 tsscElemlens;
+ Uint32 tsscElemlen;
+ Uint32 tsscElemStep;
+
+ if (tsscContainerlen < 4) {
+ jam();
+ return false; /* 2 IS THE MINIMUM SIZE OF THE ELEMENT */
+ }//if
+ tsscElemlens = tsscContainerlen - ZCON_HEAD_SIZE;
+ tsscElemlen = fragrecptr.p->elementLength;
+ /* LENGTH OF THE ELEMENT */
+ if (tsscIsforward == 1) {
+ jam();
+ tsscElementptr = tsscContainerptr + ZCON_HEAD_SIZE;
+ tsscElemStep = tsscElemlen;
+ } else {
+ jam();
+ tsscElementptr = tsscContainerptr - 1;
+ tsscElemStep = 0 - tsscElemlen;
+ }//if
+ SCANELEMENTLOOP001:
+ arrGuard(tsscElementptr, 2048);
+ const Uint32 eh = sscPageidptr.p->word32[tsscElementptr];
+ tsscIsLocked = ElementHeader::getLocked(eh);
+ if (!tsscIsLocked){
+ jam();
+ tsscScanBits = ElementHeader::getScanBits(eh);
+ if ((scanPtr.p->scanMask & tsscScanBits) == 0) {
+ jam();
+ const Uint32 tmp = ElementHeader::setScanBit(eh, scanPtr.p->scanMask);
+ dbgWord32(sscPageidptr, tsscElementptr, tmp);
+ sscPageidptr.p->word32[tsscElementptr] = tmp;
+ return true;
+ }//if
+ } else {
+ jam();
+ sscOperPtr.i = ElementHeader::getOpPtrI(eh);
+ ptrCheckGuard(sscOperPtr, coprecsize, operationrec);
+ if ((sscOperPtr.p->scanBits & scanPtr.p->scanMask) == 0) {
+ jam();
+ sscOperPtr.p->scanBits |= scanPtr.p->scanMask;
+ return true;
+ }//if
+ }//if
+ /* THE ELEMENT IS ALREADY SENT. */
+ /* SEARCH FOR NEXT ONE */
+ tsscElemlens = tsscElemlens - tsscElemlen;
+ if (tsscElemlens > 1) {
+ jam();
+ tsscElementptr = tsscElementptr + tsscElemStep;
+ goto SCANELEMENTLOOP001;
+ }//if
+ return false;
+}//Dbacc::searchScanContainer()
+
+/* --------------------------------------------------------------------------------- */
+/* SEND THE RESPONSE NEXT_SCANCONF AND POSSIBLE KEYINFO SIGNALS AS WELL. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::sendNextScanConf(Signal* signal)
+{
+ scanPtr.p->scanTimer = scanPtr.p->scanContinuebCounter;
+ Uint32 blockNo = refToBlock(scanPtr.p->scanUserblockref);
+ jam();
+ /** ---------------------------------------------------------------------
+ * LQH WILL NOT HAVE ANY USE OF THE TUPLE KEY LENGTH IN THIS CASE AND
+ * SO WE DO NOT PROVIDE IT. IN THIS CASE THESE VALUES ARE UNDEFINED.
+ * ---------------------------------------------------------------------- */
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] = operationRecPtr.i;
+ signal->theData[2] = operationRecPtr.p->fid;
+ signal->theData[3] = operationRecPtr.p->localdata[0];
+ signal->theData[4] = operationRecPtr.p->localdata[1];
+ signal->theData[5] = fragrecptr.p->localkeylen;
+ EXECUTE_DIRECT(blockNo, GSN_NEXT_SCANCONF, signal, 6);
+ return;
+}//Dbacc::sendNextScanConf()
+
+/*---------------------------------------------------------------------------
+ * sendScanHbRep
+ * Description: Using Dispatcher::execute() to send a heartbeat to DBTC
+ * from DBLQH telling the scan is alive. We use the sendScanHbRep()
+ * in DBLQH, this needs to be done here in DBACC since it can take
+ * a while before LQH receives an answer the normal way from ACC.
+ *--------------------------------------------------------------------------*/
+void Dbacc::sendScanHbRep(Signal* signal, Uint32 scanPtrIndex)
+{
+ scanPtr.i = scanPtrIndex;
+ ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
+
+ // If the timer status is on we continue with a new heartbeat in one second,
+ // else the loop stops and we will not send a new CONTINUEB
+ if (scanPtr.p->scanTimer != 0){
+ if (scanPtr.p->scanTimer == scanPtr.p->scanContinuebCounter){
+ jam();
+ ndbrequire(scanPtr.p->scanState != ScanRec::SCAN_DISCONNECT);
+
+ signal->theData[0] = scanPtr.p->scanUserptr;
+ signal->theData[1] = scanPtr.p->scanTrid1;
+ signal->theData[2] = scanPtr.p->scanTrid2;
+ EXECUTE_DIRECT(DBLQH, GSN_SCAN_HBREP, signal, 3);
+ jamEntry();
+ }//if
+ scanPtr.p->scanContinuebCounter++;
+ signal->theData[0] = ZSEND_SCAN_HBREP;
+ signal->theData[1] = scanPtr.i;
+ sendSignalWithDelay(cownBlockref, GSN_CONTINUEB, signal, 100, 2);
+ } else {
+ jam();
+ scanPtr.p->scanContinuebCounter = 0;
+ }//if
+}//Dbacc::sendScanHbRep()
+
+/* --------------------------------------------------------------------------------- */
+/* SETLOCK */
+/* DESCRIPTION:SETS LOCK ON AN ELEMENT. INFORMATION ABOUT THE ELEMENT IS */
+/* SAVED IN THE ELEMENT HEAD.A COPY OF THIS INFORMATION WILL */
+/* BE PUT IN THE OPERATION RECORD. A FIELD IN THE HEADER OF */
+/* THE ELEMENT POINTS TO THE OPERATION RECORD. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::setlock(Signal* signal)
+{
+ Uint32 tselTmp1;
+
+ arrGuard(tslElementptr, 2048);
+ tselTmp1 = slPageidptr.p->word32[tslElementptr];
+ operationRecPtr.p->scanBits = ElementHeader::getScanBits(tselTmp1);
+ operationRecPtr.p->hashvaluePart = ElementHeader::getHashValuePart(tselTmp1);
+
+ tselTmp1 = ElementHeader::setLocked(operationRecPtr.i);
+ dbgWord32(slPageidptr, tslElementptr, tselTmp1);
+ slPageidptr.p->word32[tslElementptr] = tselTmp1;
+}//Dbacc::setlock()
+
+/* --------------------------------------------------------------------------------- */
+/* TAKE_OUT_ACTIVE_SCAN_OP */
+/* DESCRIPTION: AN ACTIVE SCAN OPERATION IS BELOGED TO AN ACTIVE LIST OF THE */
+/* SCAN RECORD. BY THIS SUBRUTIN THE LIST IS UPDATED. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::takeOutActiveScanOp(Signal* signal)
+{
+ OperationrecPtr tasOperationRecPtr;
+
+ if (operationRecPtr.p->prevOp != RNIL) {
+ jam();
+ tasOperationRecPtr.i = operationRecPtr.p->prevOp;
+ ptrCheckGuard(tasOperationRecPtr, coprecsize, operationrec);
+ tasOperationRecPtr.p->nextOp = operationRecPtr.p->nextOp;
+ } else {
+ jam();
+ scanPtr.p->scanFirstActiveOp = operationRecPtr.p->nextOp;
+ }//if
+ if (operationRecPtr.p->nextOp != RNIL) {
+ jam();
+ tasOperationRecPtr.i = operationRecPtr.p->nextOp;
+ ptrCheckGuard(tasOperationRecPtr, coprecsize, operationrec);
+ tasOperationRecPtr.p->prevOp = operationRecPtr.p->prevOp;
+ }//if
+}//Dbacc::takeOutActiveScanOp()
+
+/**
+ * takeOutScanLockQueue
+ *
+ * Description: Take out an operation from the doubly linked
+ * lock list on a scan record.
+ *
+ * @note Use putOpScanLockQue to insert a operation in
+ * the list
+ *
+ */
+void Dbacc::takeOutScanLockQueue(Uint32 scanRecIndex)
+{
+ OperationrecPtr tslOperationRecPtr;
+ ScanRecPtr TscanPtr;
+
+ TscanPtr.i = scanRecIndex;
+ ptrCheckGuard(TscanPtr, cscanRecSize, scanRec);
+
+ if (operationRecPtr.p->prevOp != RNIL) {
+ jam();
+ tslOperationRecPtr.i = operationRecPtr.p->prevOp;
+ ptrCheckGuard(tslOperationRecPtr, coprecsize, operationrec);
+ tslOperationRecPtr.p->nextOp = operationRecPtr.p->nextOp;
+ } else {
+ jam();
+ // Check that first are pointing at operation to take out
+ ndbrequire(TscanPtr.p->scanFirstLockedOp==operationRecPtr.i);
+ TscanPtr.p->scanFirstLockedOp = operationRecPtr.p->nextOp;
+ }//if
+ if (operationRecPtr.p->nextOp != RNIL) {
+ jam();
+ tslOperationRecPtr.i = operationRecPtr.p->nextOp;
+ ptrCheckGuard(tslOperationRecPtr, coprecsize, operationrec);
+ tslOperationRecPtr.p->prevOp = operationRecPtr.p->prevOp;
+ } else {
+ jam();
+ // Check that last are pointing at operation to take out
+ ndbrequire(TscanPtr.p->scanLastLockedOp==operationRecPtr.i);
+ TscanPtr.p->scanLastLockedOp = operationRecPtr.p->prevOp;
+ }//if
+ TscanPtr.p->scanLockHeld--;
+
+#ifdef VM_TRACE
+ // DEBUG CODE
+ // Check that there are as many operations in the lockqueue as
+ // scanLockHeld indicates
+ OperationrecPtr tmpOp;
+ int numLockedOps = 0;
+ tmpOp.i = TscanPtr.p->scanFirstLockedOp;
+ while(tmpOp.i != RNIL){
+ numLockedOps++;
+ ptrCheckGuard(tmpOp, coprecsize, operationrec);
+ if (tmpOp.p->nextOp == RNIL)
+ ndbrequire(tmpOp.i == TscanPtr.p->scanLastLockedOp);
+ tmpOp.i = tmpOp.p->nextOp;
+ }
+ ndbrequire(numLockedOps==TscanPtr.p->scanLockHeld);
+#endif
+}//Dbacc::takeOutScanLockQueue()
+
+/* --------------------------------------------------------------------------------- */
+/* TAKE_OUT_READY_SCAN_QUEUE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::takeOutReadyScanQueue(Signal* signal)
+{
+ OperationrecPtr trsOperationRecPtr;
+
+ if (operationRecPtr.p->prevOp != RNIL) {
+ jam();
+ trsOperationRecPtr.i = operationRecPtr.p->prevOp;
+ ptrCheckGuard(trsOperationRecPtr, coprecsize, operationrec);
+ trsOperationRecPtr.p->nextOp = operationRecPtr.p->nextOp;
+ } else {
+ jam();
+ scanPtr.p->scanFirstQueuedOp = operationRecPtr.p->nextOp;
+ }//if
+ if (operationRecPtr.p->nextOp != RNIL) {
+ jam();
+ trsOperationRecPtr.i = operationRecPtr.p->nextOp;
+ ptrCheckGuard(trsOperationRecPtr, coprecsize, operationrec);
+ trsOperationRecPtr.p->prevOp = operationRecPtr.p->prevOp;
+ } else {
+ jam();
+ scanPtr.p->scanLastQueuedOp = operationRecPtr.p->nextOp;
+ }//if
+}//Dbacc::takeOutReadyScanQueue()
+
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+/* */
+/* END OF SCAN MODULE */
+/* */
+/* --------------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------------- */
+
+bool Dbacc::getrootfragmentrec(Signal* signal, RootfragmentrecPtr& rootPtr, Uint32 fid)
+{
+ for (Uint32 i = 0; i < MAX_FRAG_PER_NODE; i++) {
+ jam();
+ if (tabptr.p->fragholder[i] == fid) {
+ jam();
+ rootPtr.i = tabptr.p->fragptrholder[i];
+ ptrCheckGuard(rootPtr, crootfragmentsize, rootfragmentrec);
+ return true;
+ }//if
+ }//for
+ return false;
+}//Dbacc::getrootfragmentrec()
+
+/* --------------------------------------------------------------------------------- */
+/* INIT_FS_OP_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initFsOpRec(Signal* signal)
+{
+ fsOpptr.p->fsOpfragrecPtr = fragrecptr.i;
+ fsOpptr.p->fsConptr = fsConnectptr.i;
+}//Dbacc::initFsOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INIT_LCP_CONN_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initLcpConnRec(Signal* signal)
+{
+ lcpConnectptr.p->lcpUserblockref = tuserblockref;
+ lcpConnectptr.p->lcpUserptr = tuserptr;
+ lcpConnectptr.p->noOfLcpConf = 0; /* NO OF RETUREND CONF SIGNALS */
+ lcpConnectptr.p->syncUndopageState = WAIT_NOTHING;
+}//Dbacc::initLcpConnRec()
+
+/* --------------------------------------------------------------------------------- */
+/* INIT_OVERPAGE */
+/* INPUT. IOP_PAGEPTR, POINTER TO AN OVERFLOW PAGE RECORD */
+/* DESCRIPTION: CONTAINERS AND FREE LISTS OF THE PAGE, GET INITIALE VALUE */
+/* ACCORDING TO LH3 AND PAGE STRUCTOR DESCRIPTION OF NDBACC BLOCK */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initOverpage(Signal* signal)
+{
+ Uint32 tiopTmp;
+ Uint32 tiopPrevFree;
+ Uint32 tiopNextFree;
+
+ for (tiopIndex = 0; tiopIndex <= 2047; tiopIndex++) {
+ iopPageptr.p->word32[tiopIndex] = 0;
+ }//for
+ iopPageptr.p->word32[ZPOS_OVERFLOWREC] = iopOverflowRecPtr.i;
+ iopPageptr.p->word32[ZPOS_CHECKSUM] = 0;
+ iopPageptr.p->word32[ZPOS_PAGE_ID] = tiopPageId;
+ iopPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = 0;
+ tiopTmp = ZEMPTYLIST;
+ tiopTmp = (tiopTmp << 16) + (tiopTmp << 23);
+ iopPageptr.p->word32[ZPOS_EMPTY_LIST] = tiopTmp + (1 << ZPOS_PAGE_TYPE_BIT);
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS. */
+ /* --------------------------------------------------------------------------------- */
+ tiopIndex = ZHEAD_SIZE + 1;
+ iopPageptr.p->word32[tiopIndex] = ZEMPTYLIST;
+ for (tiopPrevFree = 0; tiopPrevFree <= ZEMPTYLIST - 2; tiopPrevFree++) {
+ tiopIndex = tiopIndex + ZBUF_SIZE;
+ iopPageptr.p->word32[tiopIndex] = tiopPrevFree;
+ }//for
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS. */
+ /* --------------------------------------------------------------------------------- */
+ tiopIndex = ZHEAD_SIZE;
+ for (tiopNextFree = 1; tiopNextFree <= ZEMPTYLIST - 1; tiopNextFree++) {
+ iopPageptr.p->word32[tiopIndex] = tiopNextFree;
+ tiopIndex = tiopIndex + ZBUF_SIZE;
+ }//for
+ iopPageptr.p->word32[tiopIndex] = ZEMPTYLIST; /* LEFT_LIST IS UPDATED */
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS. */
+ /* --------------------------------------------------------------------------------- */
+ tiopIndex = (ZBUF_SIZE + ZHEAD_SIZE) - 1;
+ iopPageptr.p->word32[tiopIndex] = ZEMPTYLIST;
+ for (tiopPrevFree = 0; tiopPrevFree <= ZEMPTYLIST - 2; tiopPrevFree++) {
+ tiopIndex = tiopIndex + ZBUF_SIZE;
+ iopPageptr.p->word32[tiopIndex] = tiopPrevFree;
+ }//for
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS. */
+ /* --------------------------------------------------------------------------------- */
+ tiopIndex = (ZBUF_SIZE + ZHEAD_SIZE) - 2;
+ for (tiopNextFree = 1; tiopNextFree <= ZEMPTYLIST - 1; tiopNextFree++) {
+ iopPageptr.p->word32[tiopIndex] = tiopNextFree;
+ tiopIndex = tiopIndex + ZBUF_SIZE;
+ }//for
+ iopPageptr.p->word32[tiopIndex] = ZEMPTYLIST; /* RIGHT_LIST IS UPDATED */
+}//Dbacc::initOverpage()
+
+/* --------------------------------------------------------------------------------- */
+/* INIT_PAGE */
+/* INPUT. INP_PAGEPTR, POINTER TO A PAGE RECORD */
+/* DESCRIPTION: CONTAINERS AND FREE LISTS OF THE PAGE, GET INITIALE VALUE */
+/* ACCORDING TO LH3 AND PAGE STRUCTOR DISACRIPTION OF NDBACC BLOCK */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::initPage(Signal* signal)
+{
+ Uint32 tinpTmp1;
+ Uint32 tinpIndex;
+ Uint32 tinpTmp;
+ Uint32 tinpPrevFree;
+ Uint32 tinpNextFree;
+
+ for (tiopIndex = 0; tiopIndex <= 2047; tiopIndex++) {
+ inpPageptr.p->word32[tiopIndex] = 0;
+ }//for
+ /* --------------------------------------------------------------------------------- */
+ /* SET PAGE ID FOR USE OF CHECKPOINTER. */
+ /* PREPARE CONTAINER HEADERS INDICATING EMPTY CONTAINERS WITHOUT NEXT. */
+ /* --------------------------------------------------------------------------------- */
+ inpPageptr.p->word32[ZPOS_PAGE_ID] = tipPageId;
+ tinpTmp1 = ZCON_HEAD_SIZE;
+ tinpTmp1 = tinpTmp1 << 26;
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE ZNO_CONTAINERS PREDEFINED HEADERS ON LEFT SIZE. */
+ /* --------------------------------------------------------------------------------- */
+ tinpIndex = ZHEAD_SIZE;
+ for (tinpTmp = 0; tinpTmp <= ZNO_CONTAINERS - 1; tinpTmp++) {
+ inpPageptr.p->word32[tinpIndex] = tinpTmp1;
+ tinpIndex = tinpIndex + ZBUF_SIZE;
+ }//for
+ /* WORD32(ZPOS_EMPTY_LIST) DATA STRUCTURE:*/
+ /*--------------------------------------- */
+ /*| PAGE TYPE|LEFT FREE|RIGHT FREE */
+ /*| 1 | LIST | LIST */
+ /*| BIT | 7 BITS | 7 BITS */
+ /*--------------------------------------- */
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE FIRST POINTER TO DOUBLY LINKED LIST OF FREE CONTAINERS. */
+ /* INITIALISE EMPTY LISTS OF USED CONTAINERS. */
+ /* INITIALISE LEFT FREE LIST TO 64 AND RIGHT FREE LIST TO ZERO. */
+ /* ALSO INITIALISE PAGE TYPE TO NOT OVERFLOW PAGE. */
+ /* --------------------------------------------------------------------------------- */
+ tinpTmp = ZEMPTYLIST;
+ tinpTmp = (tinpTmp << 16) + (tinpTmp << 23);
+ tinpTmp = tinpTmp + (ZNO_CONTAINERS << 7);
+ inpPageptr.p->word32[ZPOS_EMPTY_LIST] = tinpTmp;
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS. */
+ /* --------------------------------------------------------------------------------- */
+ tinpIndex = (ZHEAD_SIZE + ZBUF_SIZE) - 1;
+ inpPageptr.p->word32[tinpIndex] = ZEMPTYLIST;
+ for (tinpPrevFree = 0; tinpPrevFree <= ZEMPTYLIST - 2; tinpPrevFree++) {
+ tinpIndex = tinpIndex + ZBUF_SIZE;
+ inpPageptr.p->word32[tinpIndex] = tinpPrevFree;
+ }//for
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR RIGHT CONTAINERS. */
+ /* --------------------------------------------------------------------------------- */
+ tinpIndex = (ZHEAD_SIZE + ZBUF_SIZE) - 2;
+ for (tinpNextFree = 1; tinpNextFree <= ZEMPTYLIST - 1; tinpNextFree++) {
+ inpPageptr.p->word32[tinpIndex] = tinpNextFree;
+ tinpIndex = tinpIndex + ZBUF_SIZE;
+ }//for
+ inpPageptr.p->word32[tinpIndex] = ZEMPTYLIST;
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE PREVIOUS PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS. */
+ /* THE FIRST ZNO_CONTAINERS ARE NOT PUT INTO FREE LIST SINCE THEY ARE */
+ /* PREDEFINED AS OCCUPIED. */
+ /* --------------------------------------------------------------------------------- */
+ tinpIndex = (ZNO_CONTAINERS * ZBUF_SIZE) + ZHEAD_SIZE;
+ for (tinpNextFree = ZNO_CONTAINERS + 1; tinpNextFree <= ZEMPTYLIST - 1; tinpNextFree++) {
+ inpPageptr.p->word32[tinpIndex] = tinpNextFree;
+ tinpIndex = tinpIndex + ZBUF_SIZE;
+ }//for
+ inpPageptr.p->word32[tinpIndex] = ZEMPTYLIST;
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE NEXT PART OF DOUBLY LINKED LIST FOR LEFT CONTAINERS. */
+ /* THE FIRST ZNO_CONTAINERS ARE NOT PUT INTO FREE LIST SINCE THEY ARE */
+ /* PREDEFINED AS OCCUPIED. */
+ /* --------------------------------------------------------------------------------- */
+ tinpIndex = ((ZNO_CONTAINERS * ZBUF_SIZE) + ZHEAD_SIZE) + 1;
+ inpPageptr.p->word32[tinpIndex] = ZEMPTYLIST;
+ for (tinpPrevFree = ZNO_CONTAINERS; tinpPrevFree <= ZEMPTYLIST - 2; tinpPrevFree++) {
+ tinpIndex = tinpIndex + ZBUF_SIZE;
+ inpPageptr.p->word32[tinpIndex] = tinpPrevFree;
+ }//for
+ /* --------------------------------------------------------------------------------- */
+ /* INITIALISE HEADER POSITIONS NOT CURRENTLY USED AND ENSURE USE OF OVERFLOW */
+ /* RECORD POINTER ON THIS PAGE LEADS TO ERROR. */
+ /* --------------------------------------------------------------------------------- */
+ inpPageptr.p->word32[ZPOS_CHECKSUM] = 0;
+ inpPageptr.p->word32[ZPOS_ALLOC_CONTAINERS] = 0;
+ inpPageptr.p->word32[ZPOS_OVERFLOWREC] = RNIL;
+}//Dbacc::initPage()
+
+/* --------------------------------------------------------------------------------- */
+/* PUT_OP_IN_FRAG_WAIT_QUE */
+/* DESCRIPTION: AN OPERATION WHICH OWNS A LOCK OF AN ELEMENT, IS PUT IN A */
+/* LIST OF THE FRAGMENT. THIS LIST IS USED TO STOP THE QUEUE */
+/* OPERATION DURING CREATE CHECK POINT PROSESS FOR STOP AND */
+/* RESTART OF THE OPERATIONS. */
+/* */
+/* IF CONTINUEB SIGNALS ARE INTRODUCED AFTER STARTING TO EXECUTE ACCKEYREQ WE */
+/* MUST PUT IT IN THIS LIST BEFORE EXITING TO ENSURE THAT WE ARE NOT BEING */
+/* LOCKED AFTER THAT LQH HAS RECEIVED ALL LCP_HOLDOP'S. THEN THE LCP WILL NEVER*/
+/* PROCEED. WE ALSO PUT IT INTO THIS LIST WHEN WAITING FOR LONG KEYS. THIS IS */
+/* ONLY NEEDED IF SIGNALS CAN ENTER BETWEEN THE KEYDATA CARRYING SIGNALS. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::putOpInFragWaitQue(Signal* signal)
+{
+ OperationrecPtr tpiwOperRecPtr;
+
+ if (operationRecPtr.p->operation != ZSCAN_OP) {
+ if (fragrecptr.p->firstWaitInQueOp == RNIL) {
+ jam();
+ fragrecptr.p->firstWaitInQueOp = operationRecPtr.i;
+ } else {
+ jam();
+ tpiwOperRecPtr.i = fragrecptr.p->lastWaitInQueOp;
+ ptrCheckGuard(tpiwOperRecPtr, coprecsize, operationrec);
+ tpiwOperRecPtr.p->nextQueOp = operationRecPtr.i;
+ }//if
+ operationRecPtr.p->opState = WAIT_IN_QUEUE;
+ operationRecPtr.p->nextQueOp = RNIL;
+ operationRecPtr.p->prevQueOp = fragrecptr.p->lastWaitInQueOp;
+ fragrecptr.p->lastWaitInQueOp = operationRecPtr.i;
+ }//if
+}//Dbacc::putOpInFragWaitQue()
+
+/* --------------------------------------------------------------------------------- */
+/* PUT_OVERFLOW_REC_IN_FRAG */
+/* DESCRIPTION: AN OVERFLOW RECORD WITCH IS USED TO KEEP INFORMATION ABOUT */
+/* OVERFLOW PAGE WILL BE PUT IN A LIST OF OVERFLOW RECORDS IN */
+/* THE FRAGMENT RECORD. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::putOverflowRecInFrag(Signal* signal)
+{
+ OverflowRecordPtr tpifNextOverrecPtr;
+ OverflowRecordPtr tpifPrevOverrecPtr;
+
+ tpifNextOverrecPtr.i = fragrecptr.p->firstOverflowRec;
+ tpifPrevOverrecPtr.i = RNIL;
+ while (tpifNextOverrecPtr.i != RNIL) {
+ ptrCheckGuard(tpifNextOverrecPtr, coverflowrecsize, overflowRecord);
+ if (tpifNextOverrecPtr.p->dirindex < porOverflowRecPtr.p->dirindex) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* PROCEED IN LIST TO THE NEXT IN THE LIST SINCE THE ENTRY HAD A LOWER PAGE ID.*/
+ /* WE WANT TO ENSURE THAT LOWER PAGE ID'S ARE KEPT FULL RATHER THAN THE */
+ /* OPPOSITE TO ENSURE THAT HIGH PAGE ID'S CAN BE REMOVED WHEN SHRINKS ARE */
+ /* PERFORMED. */
+ /* --------------------------------------------------------------------------------- */
+ tpifPrevOverrecPtr = tpifNextOverrecPtr;
+ tpifNextOverrecPtr.i = tpifNextOverrecPtr.p->nextOverRec;
+ } else {
+ jam();
+ ndbrequire(tpifNextOverrecPtr.p->dirindex != porOverflowRecPtr.p->dirindex);
+ /* --------------------------------------------------------------------------------- */
+ /* TRYING TO INSERT THE SAME PAGE TWICE. SYSTEM ERROR. */
+ /* --------------------------------------------------------------------------------- */
+ break;
+ }//if
+ }//while
+ if (tpifNextOverrecPtr.i == RNIL) {
+ jam();
+ fragrecptr.p->lastOverflowRec = porOverflowRecPtr.i;
+ } else {
+ jam();
+ tpifNextOverrecPtr.p->prevOverRec = porOverflowRecPtr.i;
+ }//if
+ if (tpifPrevOverrecPtr.i == RNIL) {
+ jam();
+ fragrecptr.p->firstOverflowRec = porOverflowRecPtr.i;
+ } else {
+ jam();
+ tpifPrevOverrecPtr.p->nextOverRec = porOverflowRecPtr.i;
+ }//if
+ porOverflowRecPtr.p->prevOverRec = tpifPrevOverrecPtr.i;
+ porOverflowRecPtr.p->nextOverRec = tpifNextOverrecPtr.i;
+}//Dbacc::putOverflowRecInFrag()
+
+/* --------------------------------------------------------------------------------- */
+/* PUT_REC_IN_FREE_OVERDIR */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::putRecInFreeOverdir(Signal* signal)
+{
+ OverflowRecordPtr tpfoNextOverrecPtr;
+ OverflowRecordPtr tpfoPrevOverrecPtr;
+
+ tpfoNextOverrecPtr.i = fragrecptr.p->firstFreeDirindexRec;
+ tpfoPrevOverrecPtr.i = RNIL;
+ while (tpfoNextOverrecPtr.i != RNIL) {
+ ptrCheckGuard(tpfoNextOverrecPtr, coverflowrecsize, overflowRecord);
+ if (tpfoNextOverrecPtr.p->dirindex < priOverflowRecPtr.p->dirindex) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* PROCEED IN LIST TO THE NEXT IN THE LIST SINCE THE ENTRY HAD A LOWER PAGE ID.*/
+ /* WE WANT TO ENSURE THAT LOWER PAGE ID'S ARE KEPT FULL RATHER THAN THE */
+ /* OPPOSITE TO ENSURE THAT HIGH PAGE ID'S CAN BE REMOVED WHEN SHRINKS ARE */
+ /* PERFORMED. */
+ /* --------------------------------------------------------------------------------- */
+ tpfoPrevOverrecPtr = tpfoNextOverrecPtr;
+ tpfoNextOverrecPtr.i = tpfoNextOverrecPtr.p->nextOverList;
+ } else {
+ jam();
+ ndbrequire(tpfoNextOverrecPtr.p->dirindex != priOverflowRecPtr.p->dirindex);
+ /* --------------------------------------------------------------------------------- */
+ /* ENSURE WE ARE NOT TRYING TO INSERT THE SAME PAGE TWICE. */
+ /* --------------------------------------------------------------------------------- */
+ break;
+ }//if
+ }//while
+ if (tpfoNextOverrecPtr.i != RNIL) {
+ jam();
+ tpfoNextOverrecPtr.p->prevOverList = priOverflowRecPtr.i;
+ }//if
+ if (tpfoPrevOverrecPtr.i == RNIL) {
+ jam();
+ fragrecptr.p->firstFreeDirindexRec = priOverflowRecPtr.i;
+ } else {
+ jam();
+ tpfoPrevOverrecPtr.p->nextOverList = priOverflowRecPtr.i;
+ }//if
+ priOverflowRecPtr.p->prevOverList = tpfoPrevOverrecPtr.i;
+ priOverflowRecPtr.p->nextOverList = tpfoNextOverrecPtr.i;
+}//Dbacc::putRecInFreeOverdir()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_DIRECTORY */
+/* --------------------------------------- ----------------------------------------- */
+void Dbacc::releaseDirectory(Signal* signal)
+{
+ ptrCheckGuard(rdDirptr, cdirarraysize, directoryarray);
+ rdDirptr.p->pagep[0] = cfirstfreedir;
+ cfirstfreedir = rdDirptr.i;
+}//Dbacc::releaseDirectory()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_DIRRANGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseDirrange(Signal* signal)
+{
+ ptrCheckGuard(rdDirRangePtr, cdirrangesize, dirRange);
+ rdDirRangePtr.p->dirArray[0] = cfirstfreeDirrange;
+ cfirstfreeDirrange = rdDirRangePtr.i;
+}//Dbacc::releaseDirrange()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_FS_CONN_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseFsConnRec(Signal* signal)
+{
+ fsConnectptr.p->fsNext = cfsFirstfreeconnect;
+ cfsFirstfreeconnect = fsConnectptr.i;
+ fsConnectptr.p->fsState = WAIT_NOTHING;
+}//Dbacc::releaseFsConnRec()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_FS_OP_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseFsOpRec(Signal* signal)
+{
+ fsOpptr.p->fsOpnext = cfsFirstfreeop;
+ cfsFirstfreeop = fsOpptr.i;
+ fsOpptr.p->fsOpstate = WAIT_NOTHING;
+}//Dbacc::releaseFsOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_LCP_CONNECT_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseLcpConnectRec(Signal* signal)
+{
+ lcpConnectptr.p->lcpstate = LCP_FREE;
+ lcpConnectptr.p->nextLcpConn = cfirstfreelcpConnect;
+ lcpConnectptr.p->lcpstate = LCP_FREE;
+ cfirstfreelcpConnect = lcpConnectptr.i;
+}//Dbacc::releaseLcpConnectRec()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE OP RECORD */
+/* PUT A FREE OPERATION IN A FREE LIST OF THE OPERATIONS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseOpRec(Signal* signal)
+{
+#if 0
+ // DEBUG CODE
+ // Check that the operation to be released isn't
+ // already in the list of free operations
+ // Since this code loops through the entire list of free operations
+ // it's only enabled in VM_TRACE mode
+ OperationrecPtr opRecPtr;
+ bool opInList = false;
+ opRecPtr.i = cfreeopRec;
+ while (opRecPtr.i != RNIL){
+ if (opRecPtr.i == operationRecPtr.i){
+ opInList = true;
+ break;
+ }
+ ptrCheckGuard(opRecPtr, coprecsize, operationrec);
+ opRecPtr.i = opRecPtr.p->nextOp;
+ }
+ ndbrequire(opInList == false);
+#endif
+ ndbrequire(operationRecPtr.p->lockOwner == ZFALSE);
+
+ operationRecPtr.p->nextOp = cfreeopRec;
+ cfreeopRec = operationRecPtr.i; /* UPDATE FREE LIST OF OP RECORDS */
+ operationRecPtr.p->prevOp = RNIL;
+ operationRecPtr.p->opState = FREE_OP;
+ operationRecPtr.p->transactionstate = IDLE;
+ operationRecPtr.p->operation = ZUNDEFINED_OP;
+}//Dbacc::releaseOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_OVERFLOW_REC */
+/* PUT A FREE OVERFLOW REC IN A FREE LIST OF THE OVERFLOW RECORDS */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseOverflowRec(Signal* signal)
+{
+ rorOverflowRecPtr.p->nextfreeoverrec = cfirstfreeoverrec;
+ cfirstfreeoverrec = rorOverflowRecPtr.i;
+}//Dbacc::releaseOverflowRec()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_OVERPAGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseOverpage(Signal* signal)
+{
+ DirRangePtr ropOverflowrangeptr;
+ DirectoryarrayPtr ropOverflowDirptr;
+ OverflowRecordPtr ropOverflowRecPtr;
+ OverflowRecordPtr tuodOverflowRecPtr;
+ Uint32 tropTmp;
+ Uint32 tropTmp1;
+ Uint32 tropTmp2;
+
+ ropOverflowRecPtr.i = ropPageptr.p->word32[ZPOS_OVERFLOWREC];
+ ndbrequire(ropOverflowRecPtr.i != RNIL);
+ /* THE OVERFLOW REC WILL BE TAKEN OUT OF THE */
+ /* FREELIST OF OVERFLOW PAGE WITH FREE */
+ /* CONTAINER AND WILL BE PUT IN THE FREE LIST */
+ /* OF THE FREE DIRECTORY INDEXES. */
+ if ((fragrecptr.p->lastOverflowRec == ropOverflowRecPtr.i) &&
+ (fragrecptr.p->firstOverflowRec == ropOverflowRecPtr.i)) {
+ jam();
+ return; /* THERE IS ONLY ONE OVERFLOW PAGE */
+ }//if
+ if ((fragrecptr.p->createLcp == ZTRUE) &&
+ (fragrecptr.p->lcpMaxOverDirIndex > ropPageptr.p->word32[ZPOS_PAGE_ID])) {
+ /* --------------------------------------------------------------------------------- */
+ /* THE PAGE PARTICIPATES IN THE LOCAL CHECKPOINT. */
+ /* --------------------------------------------------------------------------------- */
+ if (fragrecptr.p->fragState == LCP_SEND_PAGES) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* THE PAGE PARTICIPATES IN THE LOCAL CHECKPOINT AND THE WRITE TO DISK HAS NOT */
+ /* YET BEEN COMPLETED. WE MUST KEEP IT A WHILE LONGER SINCE AN EMPTY PAGE IS */
+ /* NOT EQUIVALENT TO AN INITIALISED PAGE SINCE THE FREE LISTS CAN DIFFER. */
+ /* --------------------------------------------------------------------------------- */
+ return;
+ } else {
+ if ((fragrecptr.p->fragState == LCP_SEND_OVER_PAGES) &&
+ (fragrecptr.p->lcpDirIndex <= ropPageptr.p->word32[ZPOS_PAGE_ID])) {
+ jam();
+ /* --------------------------------------------------------------------------------- */
+ /* SEE COMMENT ABOVE */
+ /* --------------------------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//if
+#if kalle
+ logicalPage = 0;
+
+ i = fragrecptr.p->directory;
+ p = dirRange.getPtr(i);
+
+ i1 = logicalPage >> 8;
+ i2 = logicalPage & 0xFF;
+
+ ndbrequire(i1 < 256);
+
+ i = p->dirArray[i1];
+ p = directoryarray.getPtr(i);
+
+ physicPageId = p->pagep[i2];
+ physicPageP = page8.getPtr(physicPageId);
+
+ p->pagep[i2] = RNIL;
+ rpPageptr = { physicPageId, physicPageP };
+ releasePage(signal);
+
+#endif
+
+ /* --------------------------------------------------------------------------------- */
+ /* IT WAS OK TO RELEASE THE PAGE. */
+ /* --------------------------------------------------------------------------------- */
+ ptrCheckGuard(ropOverflowRecPtr, coverflowrecsize, overflowRecord);
+ tfoOverflowRecPtr = ropOverflowRecPtr;
+ takeRecOutOfFreeOverpage(signal);
+ ropOverflowRecPtr.p->overpage = RNIL;
+ priOverflowRecPtr = ropOverflowRecPtr;
+ putRecInFreeOverdir(signal);
+ tropTmp = ropPageptr.p->word32[ZPOS_PAGE_ID];
+ ropOverflowrangeptr.i = fragrecptr.p->overflowdir;
+ tropTmp1 = tropTmp >> 8;
+ tropTmp2 = tropTmp & 0xff;
+ ptrCheckGuard(ropOverflowrangeptr, cdirrangesize, dirRange);
+ arrGuard(tropTmp1, 256);
+ ropOverflowDirptr.i = ropOverflowrangeptr.p->dirArray[tropTmp1];
+ ptrCheckGuard(ropOverflowDirptr, cdirarraysize, directoryarray);
+ ropOverflowDirptr.p->pagep[tropTmp2] = RNIL;
+ rpPageptr = ropPageptr;
+ releasePage(signal);
+ if (ropOverflowRecPtr.p->dirindex != (fragrecptr.p->lastOverIndex - 1)) {
+ jam();
+ return;
+ }//if
+ /* --------------------------------------------------------------------------------- */
+ /* THE LAST PAGE IN THE DIRECTORY WAS RELEASED IT IS NOW NECESSARY TO REMOVE */
+ /* ALL RELEASED OVERFLOW DIRECTORIES AT THE END OF THE LIST. */
+ /* --------------------------------------------------------------------------------- */
+ do {
+ fragrecptr.p->lastOverIndex--;
+ if (tropTmp2 == 0) {
+ jam();
+ ndbrequire(tropTmp1 != 0);
+ ropOverflowrangeptr.p->dirArray[tropTmp1] = RNIL;
+ rdDirptr.i = ropOverflowDirptr.i;
+ releaseDirectory(signal);
+ tropTmp1--;
+ tropTmp2 = 255;
+ } else {
+ jam();
+ tropTmp2--;
+ }//if
+ ropOverflowDirptr.i = ropOverflowrangeptr.p->dirArray[tropTmp1];
+ ptrCheckGuard(ropOverflowDirptr, cdirarraysize, directoryarray);
+ } while (ropOverflowDirptr.p->pagep[tropTmp2] == RNIL);
+ /* --------------------------------------------------------------------------------- */
+ /* RELEASE ANY OVERFLOW RECORDS THAT ARE PART OF THE FREE INDEX LIST WHICH */
+ /* DIRECTORY INDEX NOW HAS BEEN RELEASED. */
+ /* --------------------------------------------------------------------------------- */
+ tuodOverflowRecPtr.i = fragrecptr.p->firstFreeDirindexRec;
+ jam();
+ while (tuodOverflowRecPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(tuodOverflowRecPtr, coverflowrecsize, overflowRecord);
+ if (tuodOverflowRecPtr.p->dirindex >= fragrecptr.p->lastOverIndex) {
+ jam();
+ rorOverflowRecPtr = tuodOverflowRecPtr;
+ troOverflowRecPtr.p = tuodOverflowRecPtr.p;
+ tuodOverflowRecPtr.i = troOverflowRecPtr.p->nextOverList;
+ takeRecOutOfFreeOverdir(signal);
+ releaseOverflowRec(signal);
+ } else {
+ jam();
+ tuodOverflowRecPtr.i = tuodOverflowRecPtr.p->nextOverList;
+ }//if
+ }//while
+}//Dbacc::releaseOverpage()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_PAGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releasePage(Signal* signal)
+{
+#ifdef VM_TRACE
+ bool inList = false;
+ Uint32 numInList = 0;
+ Page8Ptr tmpPagePtr;
+ tmpPagePtr.i = cfirstfreepage;
+ while (tmpPagePtr.i != RNIL){
+ ptrCheckGuard(tmpPagePtr, cpagesize, page8);
+ if (tmpPagePtr.i == rpPageptr.i){
+ jam(); inList = true;
+ break;
+ }
+ numInList++;
+ tmpPagePtr.i = tmpPagePtr.p->word32[0];
+ }
+ ndbrequire(inList == false);
+ // ndbrequire(numInList == cnoOfAllocatedPages);
+#endif
+ rpPageptr.p->word32[0] = cfirstfreepage;
+ cfirstfreepage = rpPageptr.i;
+ cnoOfAllocatedPages--;
+}//Dbacc::releasePage()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_LCP_PAGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseLcpPage(Signal* signal)
+{
+ rlpPageptr.p->word32[0] = cfirstfreeLcpPage;
+ cfirstfreeLcpPage = rlpPageptr.i;
+}//Dbacc::releaseLcpPage()
+
+/* --------------------------------------------------------------------------------- */
+/* RELEASE_SR_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::releaseSrRec(Signal* signal)
+{
+ srVersionPtr.p->nextFreeSr = cfirstFreeSrVersionRec;
+ cfirstFreeSrVersionRec = srVersionPtr.i;
+}//Dbacc::releaseSrRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_DIRECTORY */
+/* DESCRIPTION: A DIRECTORY BLOCK (ZDIRBLOCKSIZE NUMBERS OF DIRECTORY */
+/* RECORDS WILL BE ALLOCATED AND RETURNED. */
+/* SIZE OF DIRECTORY ERROR_CODE, WILL BE RETURNED IF THERE IS NO ANY */
+/* FREE BLOCK */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeDirectory(Signal* signal)
+{
+ Uint32 tsdyIndex;
+
+ if (cfirstfreedir == RNIL) {
+ jam();
+ if (cdirarraysize <= cdirmemory) {
+ jam();
+ tresult = ZDIRSIZE_ERROR;
+ return;
+ } else {
+ jam();
+ sdDirptr.i = cdirmemory;
+ ptrCheckGuard(sdDirptr, cdirarraysize, directoryarray);
+ cdirmemory = cdirmemory + 1;
+ }//if
+ } else {
+ jam();
+ sdDirptr.i = cfirstfreedir;
+ ptrCheckGuard(sdDirptr, cdirarraysize, directoryarray);
+ cfirstfreedir = sdDirptr.p->pagep[0];
+ sdDirptr.p->pagep[0] = RNIL;
+ }//if
+ for (tsdyIndex = 0; tsdyIndex <= 255; tsdyIndex++) {
+ sdDirptr.p->pagep[tsdyIndex] = RNIL;
+ }//for
+}//Dbacc::seizeDirectory()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_DIRRANGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeDirrange(Signal* signal)
+{
+ Uint32 tsdeIndex;
+
+ newDirRangePtr.i = cfirstfreeDirrange;
+ ptrCheckGuard(newDirRangePtr, cdirrangesize, dirRange);
+ cfirstfreeDirrange = newDirRangePtr.p->dirArray[0];
+ for (tsdeIndex = 0; tsdeIndex <= 255; tsdeIndex++) {
+ newDirRangePtr.p->dirArray[tsdeIndex] = RNIL;
+ }//for
+}//Dbacc::seizeDirrange()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE FRAGREC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeFragrec(Signal* signal)
+{
+ fragrecptr.i = cfirstfreefrag;
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ cfirstfreefrag = fragrecptr.p->nextfreefrag;
+ fragrecptr.p->nextfreefrag = RNIL;
+}//Dbacc::seizeFragrec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_FS_CONNECT_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeFsConnectRec(Signal* signal)
+{
+ fsConnectptr.i = cfsFirstfreeconnect;
+ ptrCheckGuard(fsConnectptr, cfsConnectsize, fsConnectrec);
+ cfsFirstfreeconnect = fsConnectptr.p->fsNext;
+ fsConnectptr.p->fsNext = RNIL;
+ fsConnectptr.p->fsState = WAIT_NOTHING;
+}//Dbacc::seizeFsConnectRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_FS_OP_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeFsOpRec(Signal* signal)
+{
+ fsOpptr.i = cfsFirstfreeop;
+ ptrCheckGuard(fsOpptr, cfsOpsize, fsOprec);
+ cfsFirstfreeop = fsOpptr.p->fsOpnext;
+ fsOpptr.p->fsOpnext = RNIL;
+}//Dbacc::seizeFsOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_LCP_CONNECT_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeLcpConnectRec(Signal* signal)
+{
+ lcpConnectptr.i = cfirstfreelcpConnect;
+ ptrCheckGuard(lcpConnectptr, clcpConnectsize, lcpConnectrec);
+ cfirstfreelcpConnect = lcpConnectptr.p->nextLcpConn;
+ lcpConnectptr.p->nextLcpConn = RNIL;
+}//Dbacc::seizeLcpConnectRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_OP_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeOpRec(Signal* signal)
+{
+ operationRecPtr.i = cfreeopRec;
+ ptrCheckGuard(operationRecPtr, coprecsize, operationrec);
+ cfreeopRec = operationRecPtr.p->nextOp; /* UPDATE FREE LIST OF OP RECORDS */
+ /* PUTS OPERTION RECORD PTR IN THE LIST */
+ /* OF OPERATION IN CONNECTION RECORD */
+ operationRecPtr.p->nextOp = RNIL;
+}//Dbacc::seizeOpRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE OVERFLOW RECORD */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeOverRec(Signal* signal) {
+ sorOverflowRecPtr.i = cfirstfreeoverrec;
+ ptrCheckGuard(sorOverflowRecPtr, coverflowrecsize, overflowRecord);
+ cfirstfreeoverrec = sorOverflowRecPtr.p->nextfreeoverrec;
+ sorOverflowRecPtr.p->nextfreeoverrec = RNIL;
+ sorOverflowRecPtr.p->prevOverRec = RNIL;
+ sorOverflowRecPtr.p->nextOverRec = RNIL;
+}//Dbacc::seizeOverRec()
+
+
+/**
+ * A ZPAGESIZE_ERROR has occured, out of index pages
+ * Print some debug info if debug compiled
+ */
+void Dbacc::zpagesize_error(const char* where){
+ DEBUG(where << endl
+ << " ZPAGESIZE_ERROR" << endl
+ << " cfirstfreepage=" << cfirstfreepage << endl
+ << " cfreepage=" <<cfreepage<<endl
+ << " cpagesize=" <<cpagesize<<endl
+ << " cnoOfAllocatedPages="<<cnoOfAllocatedPages);
+}
+
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_PAGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizePage(Signal* signal)
+{
+ tresult = 0;
+ if (cfirstfreepage == RNIL) {
+ if (cfreepage < cpagesize) {
+ jam();
+ spPageptr.i = cfreepage;
+ ptrCheckGuard(spPageptr, cpagesize, page8);
+ cfreepage++;
+ cnoOfAllocatedPages++;
+ } else {
+ jam();
+ zpagesize_error("Dbacc::seizePage");
+ tresult = ZPAGESIZE_ERROR;
+ }//if
+ } else {
+ jam();
+ spPageptr.i = cfirstfreepage;
+ ptrCheckGuard(spPageptr, cpagesize, page8);
+ cfirstfreepage = spPageptr.p->word32[0];
+ cnoOfAllocatedPages++;
+ }//if
+}//Dbacc::seizePage()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_PAGE */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeLcpPage(Page8Ptr& regPagePtr)
+{
+ regPagePtr.i = cfirstfreeLcpPage;
+ ptrCheckGuard(regPagePtr, cpagesize, page8);
+ cfirstfreeLcpPage = regPagePtr.p->word32[0];
+}//Dbacc::seizeLcpPage()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_ROOTFRAGREC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeRootfragrec(Signal* signal)
+{
+ rootfragrecptr.i = cfirstfreerootfrag;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ cfirstfreerootfrag = rootfragrecptr.p->nextroot;
+ rootfragrecptr.p->nextroot = RNIL;
+}//Dbacc::seizeRootfragrec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_SCAN_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeScanRec(Signal* signal)
+{
+ scanPtr.i = cfirstFreeScanRec;
+ ptrCheckGuard(scanPtr, cscanRecSize, scanRec);
+ ndbrequire(scanPtr.p->scanState == ScanRec::SCAN_DISCONNECT);
+ cfirstFreeScanRec = scanPtr.p->scanNextfreerec;
+}//Dbacc::seizeScanRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEIZE_SR_VERSION_REC */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::seizeSrVerRec(Signal* signal)
+{
+ srVersionPtr.i = cfirstFreeSrVersionRec;
+ ptrCheckGuard(srVersionPtr, csrVersionRecSize, srVersionRec);
+ cfirstFreeSrVersionRec = srVersionPtr.p->nextFreeSr;
+}//Dbacc::seizeSrVerRec()
+
+/* --------------------------------------------------------------------------------- */
+/* SEND_SYSTEMERROR */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::sendSystemerror(Signal* signal)
+{
+ progError(0, 0);
+}//Dbacc::sendSystemerror()
+
+/* --------------------------------------------------------------------------------- */
+/* TAKE_REC_OUT_OF_FREE_OVERDIR */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::takeRecOutOfFreeOverdir(Signal* signal)
+{
+ OverflowRecordPtr tofoOverrecPtr;
+ if (troOverflowRecPtr.p->nextOverList != RNIL) {
+ jam();
+ tofoOverrecPtr.i = troOverflowRecPtr.p->nextOverList;
+ ptrCheckGuard(tofoOverrecPtr, coverflowrecsize, overflowRecord);
+ tofoOverrecPtr.p->prevOverList = troOverflowRecPtr.p->prevOverList;
+ }//if
+ if (troOverflowRecPtr.p->prevOverList != RNIL) {
+ jam();
+ tofoOverrecPtr.i = troOverflowRecPtr.p->prevOverList;
+ ptrCheckGuard(tofoOverrecPtr, coverflowrecsize, overflowRecord);
+ tofoOverrecPtr.p->nextOverList = troOverflowRecPtr.p->nextOverList;
+ } else {
+ jam();
+ fragrecptr.p->firstFreeDirindexRec = troOverflowRecPtr.p->nextOverList;
+ }//if
+}//Dbacc::takeRecOutOfFreeOverdir()
+
+/* --------------------------------------------------------------------------------- */
+/* TAKE_REC_OUT_OF_FREE_OVERPAGE */
+/* DESCRIPTION: AN OVERFLOW PAGE WHICH IS EMPTY HAVE TO BE TAKE OUT OF THE */
+/* FREE LIST OF OVERFLOW PAGE. BY THIS SUBROUTINE THIS LIST */
+/* WILL BE UPDATED. */
+/* --------------------------------------------------------------------------------- */
+void Dbacc::takeRecOutOfFreeOverpage(Signal* signal)
+{
+ OverflowRecordPtr tfoNextOverflowRecPtr;
+ OverflowRecordPtr tfoPrevOverflowRecPtr;
+
+ if (tfoOverflowRecPtr.p->nextOverRec != RNIL) {
+ jam();
+ tfoNextOverflowRecPtr.i = tfoOverflowRecPtr.p->nextOverRec;
+ ptrCheckGuard(tfoNextOverflowRecPtr, coverflowrecsize, overflowRecord);
+ tfoNextOverflowRecPtr.p->prevOverRec = tfoOverflowRecPtr.p->prevOverRec;
+ } else {
+ ndbrequire(fragrecptr.p->lastOverflowRec == tfoOverflowRecPtr.i);
+ jam();
+ fragrecptr.p->lastOverflowRec = tfoOverflowRecPtr.p->prevOverRec;
+ }//if
+ if (tfoOverflowRecPtr.p->prevOverRec != RNIL) {
+ jam();
+ tfoPrevOverflowRecPtr.i = tfoOverflowRecPtr.p->prevOverRec;
+ ptrCheckGuard(tfoPrevOverflowRecPtr, coverflowrecsize, overflowRecord);
+ tfoPrevOverflowRecPtr.p->nextOverRec = tfoOverflowRecPtr.p->nextOverRec;
+ } else {
+ ndbrequire(fragrecptr.p->firstOverflowRec == tfoOverflowRecPtr.i);
+ jam();
+ fragrecptr.p->firstOverflowRec = tfoOverflowRecPtr.p->nextOverRec;
+ }//if
+}//Dbacc::takeRecOutOfFreeOverpage()
+
+void
+Dbacc::reportMemoryUsage(Signal* signal, int gth){
+ signal->theData[0] = NDB_LE_MemoryUsage;
+ signal->theData[1] = gth;
+ signal->theData[2] = sizeof(* rpPageptr.p);
+ signal->theData[3] = cnoOfAllocatedPages;
+ signal->theData[4] = cpagesize;
+ signal->theData[5] = DBACC;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 6, JBB);
+}
+
+void
+Dbacc::execDUMP_STATE_ORD(Signal* signal)
+{
+ DumpStateOrd * const dumpState = (DumpStateOrd *)&signal->theData[0];
+ if (dumpState->args[0] == DumpStateOrd::AccDumpOneScanRec){
+ Uint32 recordNo = RNIL;
+ if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ if (recordNo >= cscanRecSize)
+ return;
+
+ scanPtr.i = recordNo;
+ ptrAss(scanPtr, scanRec);
+ infoEvent("Dbacc::ScanRec[%d]: state=%d, transid(0x%x, 0x%x)",
+ scanPtr.i, scanPtr.p->scanState,scanPtr.p->scanTrid1,
+ scanPtr.p->scanTrid2);
+ infoEvent(" timer=%d, continueBCount=%d, "
+ "activeLocalFrag=%d, root=%d, nextBucketIndex=%d",
+ scanPtr.p->scanTimer,
+ scanPtr.p->scanContinuebCounter,
+ scanPtr.p->activeLocalFrag,
+ scanPtr.p->rootPtr,
+ scanPtr.p->nextBucketIndex);
+ infoEvent(" scanNextfreerec=%d firstActOp=%d firstLockedOp=%d, "
+ "scanLastLockedOp=%d firstQOp=%d lastQOp=%d",
+ scanPtr.p->scanNextfreerec,
+ scanPtr.p->scanFirstActiveOp,
+ scanPtr.p->scanFirstLockedOp,
+ scanPtr.p->scanLastLockedOp,
+ scanPtr.p->scanFirstQueuedOp,
+ scanPtr.p->scanLastQueuedOp);
+ infoEvent(" scanUserP=%d, startNoBuck=%d, minBucketIndexToRescan=%d, "
+ "maxBucketIndexToRescan=%d",
+ scanPtr.p->scanUserptr,
+ scanPtr.p->startNoOfBuckets,
+ scanPtr.p->minBucketIndexToRescan,
+ scanPtr.p->maxBucketIndexToRescan);
+ infoEvent(" scanBucketState=%d, scanLockHeld=%d, userBlockRef=%d, "
+ "scanMask=%d scanLockMode=%d",
+ scanPtr.p->scanBucketState,
+ scanPtr.p->scanLockHeld,
+ scanPtr.p->scanUserblockref,
+ scanPtr.p->scanMask,
+ scanPtr.p->scanLockMode);
+ return;
+ }
+
+ // Dump all ScanRec(ords)
+ if (dumpState->args[0] == DumpStateOrd::AccDumpAllScanRec){
+ Uint32 recordNo = 0;
+ if (signal->length() == 1)
+ infoEvent("ACC: Dump all ScanRec - size: %d",
+ cscanRecSize);
+ else if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ dumpState->args[0] = DumpStateOrd::AccDumpOneScanRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+
+ if (recordNo < cscanRecSize-1){
+ dumpState->args[0] = DumpStateOrd::AccDumpAllScanRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ return;
+ }
+
+ // Dump all active ScanRec(ords)
+ if (dumpState->args[0] == DumpStateOrd::AccDumpAllActiveScanRec){
+ Uint32 recordNo = 0;
+ if (signal->length() == 1)
+ infoEvent("ACC: Dump active ScanRec - size: %d",
+ cscanRecSize);
+ else if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ ScanRecPtr sp;
+ sp.i = recordNo;
+ ptrAss(sp, scanRec);
+ if (sp.p->scanState != ScanRec::SCAN_DISCONNECT){
+ dumpState->args[0] = DumpStateOrd::AccDumpOneScanRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+ }
+
+ if (recordNo < cscanRecSize-1){
+ dumpState->args[0] = DumpStateOrd::AccDumpAllActiveScanRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ return;
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::DumpPageMemory){
+ reportMemoryUsage(signal, 0);
+ return;
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::EnableUndoDelayDataWrite){
+ ndbout << "Dbacc:: delay write of datapages for table = "
+ << dumpState->args[1]<< endl;
+ c_errorInsert3000_TableId = dumpState->args[1];
+ SET_ERROR_INSERT_VALUE(3000);
+ return;
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::AccDumpOneOperationRec){
+ Uint32 recordNo = RNIL;
+ if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ if (recordNo >= coprecsize)
+ return;
+
+ OperationrecPtr tmpOpPtr;
+ tmpOpPtr.i = recordNo;
+ ptrAss(tmpOpPtr, operationrec);
+ infoEvent("Dbacc::operationrec[%d]: opState=%d, transid(0x%x, 0x%x)",
+ tmpOpPtr.i, tmpOpPtr.p->opState, tmpOpPtr.p->transId1,
+ tmpOpPtr.p->transId2);
+ infoEvent("elementIsforward=%d, elementPage=%d, elementPointer=%d ",
+ tmpOpPtr.p->elementIsforward, tmpOpPtr.p->elementPage,
+ tmpOpPtr.p->elementPointer);
+ infoEvent("fid=%d, fragptr=%d, hashvaluePart=%d ",
+ tmpOpPtr.p->fid, tmpOpPtr.p->fragptr,
+ tmpOpPtr.p->hashvaluePart);
+ infoEvent("hashValue=%d, insertDeleteLen=%d, keyinfoPage=%d ",
+ tmpOpPtr.p->hashValue, tmpOpPtr.p->insertDeleteLen,
+ tmpOpPtr.p->keyinfoPage);
+ infoEvent("nextLockOwnerOp=%d, nextOp=%d, nextParallelQue=%d ",
+ tmpOpPtr.p->nextLockOwnerOp, tmpOpPtr.p->nextOp,
+ tmpOpPtr.p->nextParallelQue);
+ infoEvent("nextQueOp=%d, nextSerialQue=%d, prevOp=%d ",
+ tmpOpPtr.p->nextQueOp, tmpOpPtr.p->nextSerialQue,
+ tmpOpPtr.p->prevOp);
+ infoEvent("prevLockOwnerOp=%d, prevParallelQue=%d, prevQueOp=%d ",
+ tmpOpPtr.p->prevLockOwnerOp, tmpOpPtr.p->nextParallelQue,
+ tmpOpPtr.p->prevQueOp);
+ infoEvent("prevSerialQue=%d, scanRecPtr=%d, longPagePtr=%d ",
+ tmpOpPtr.p->prevSerialQue, tmpOpPtr.p->scanRecPtr,
+ tmpOpPtr.p->longPagePtr);
+ infoEvent("transactionstate=%d, elementIsDisappeared=%d, insertIsDone=%d ",
+ tmpOpPtr.p->transactionstate, tmpOpPtr.p->elementIsDisappeared,
+ tmpOpPtr.p->insertIsDone);
+ infoEvent("lockMode=%d, lockOwner=%d, nodeType=%d ",
+ tmpOpPtr.p->lockMode, tmpOpPtr.p->lockOwner,
+ tmpOpPtr.p->nodeType);
+ infoEvent("operation=%d, opSimple=%d, dirtyRead=%d,scanBits=%d ",
+ tmpOpPtr.p->operation, tmpOpPtr.p->opSimple,
+ tmpOpPtr.p->dirtyRead, tmpOpPtr.p->scanBits);
+ return;
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::AccDumpNumOpRecs){
+
+ Uint32 freeOpRecs = 0;
+ OperationrecPtr opRecPtr;
+ opRecPtr.i = cfreeopRec;
+ while (opRecPtr.i != RNIL){
+ freeOpRecs++;
+ ptrCheckGuard(opRecPtr, coprecsize, operationrec);
+ opRecPtr.i = opRecPtr.p->nextOp;
+ }
+
+ infoEvent("Dbacc::OperationRecords: num=%d, free=%d",
+ coprecsize, freeOpRecs);
+
+ return;
+ }
+ if(dumpState->args[0] == DumpStateOrd::AccDumpFreeOpRecs){
+
+ OperationrecPtr opRecPtr;
+ opRecPtr.i = cfreeopRec;
+ while (opRecPtr.i != RNIL){
+
+ dumpState->args[0] = DumpStateOrd::AccDumpOneOperationRec;
+ dumpState->args[1] = opRecPtr.i;
+ execDUMP_STATE_ORD(signal);
+
+ ptrCheckGuard(opRecPtr, coprecsize, operationrec);
+ opRecPtr.i = opRecPtr.p->nextOp;
+ }
+
+
+ return;
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::AccDumpNotFreeOpRecs){
+ Uint32 recordStart = RNIL;
+ if (signal->length() == 2)
+ recordStart = dumpState->args[1];
+ else
+ return;
+
+ if (recordStart >= coprecsize)
+ return;
+
+ for (Uint32 i = recordStart; i < coprecsize; i++){
+
+ bool inFreeList = false;
+ OperationrecPtr opRecPtr;
+ opRecPtr.i = cfreeopRec;
+ while (opRecPtr.i != RNIL){
+ if (opRecPtr.i == i){
+ inFreeList = true;
+ break;
+ }
+ ptrCheckGuard(opRecPtr, coprecsize, operationrec);
+ opRecPtr.i = opRecPtr.p->nextOp;
+ }
+ if (inFreeList == false){
+ dumpState->args[0] = DumpStateOrd::AccDumpOneOperationRec;
+ dumpState->args[1] = i;
+ execDUMP_STATE_ORD(signal);
+ }
+ }
+ return;
+ }
+
+#if 0
+ if (type == 100) {
+ RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
+ req->primaryTableId = 2;
+ req->secondaryTableId = RNIL;
+ req->userPtr = 2;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownBlockref, GSN_REL_TABMEMREQ, signal,
+ RelTabMemReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 101) {
+ RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
+ req->primaryTableId = 4;
+ req->secondaryTableId = 5;
+ req->userPtr = 4;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownBlockref, GSN_REL_TABMEMREQ, signal,
+ RelTabMemReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 102) {
+ RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
+ req->primaryTableId = 6;
+ req->secondaryTableId = 8;
+ req->userPtr = 6;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownBlockref, GSN_REL_TABMEMREQ, signal,
+ RelTabMemReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 103) {
+ DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
+ req->primaryTableId = 2;
+ req->secondaryTableId = RNIL;
+ req->userPtr = 2;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownBlockref, GSN_DROP_TABFILEREQ, signal,
+ DropTabFileReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 104) {
+ DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
+ req->primaryTableId = 4;
+ req->secondaryTableId = 5;
+ req->userPtr = 4;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownBlockref, GSN_DROP_TABFILEREQ, signal,
+ DropTabFileReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 105) {
+ DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
+ req->primaryTableId = 6;
+ req->secondaryTableId = 8;
+ req->userPtr = 6;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownBlockref, GSN_DROP_TABFILEREQ, signal,
+ DropTabFileReq::SignalLength, JBB);
+ return;
+ }//if
+#endif
+}//Dbacc::execDUMP_STATE_ORD()
+
+void Dbacc::execSET_VAR_REQ(Signal* signal)
+{
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+ int val = setVarReq->value();
+
+
+ switch (var) {
+
+ case NoOfDiskPagesToDiskAfterRestartACC:
+ clblPagesPerTick = val;
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case NoOfDiskPagesToDiskDuringRestartACC:
+ // Valid only during start so value not set.
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ } // switch
+#endif
+
+}//execSET_VAR_REQ()
+
+void
+Dbacc::execREAD_PSUEDO_REQ(Signal* signal){
+ jamEntry();
+ fragrecptr.i = signal->theData[0];
+ Uint32 attrId = signal->theData[1];
+ ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec);
+ rootfragrecptr.i = fragrecptr.p->myroot;
+ ptrCheckGuard(rootfragrecptr, crootfragmentsize, rootfragmentrec);
+ Uint64 tmp;
+ switch(attrId){
+ case AttributeHeader::ROW_COUNT:
+ tmp = rootfragrecptr.p->noOfElements;
+ break;
+ case AttributeHeader::COMMIT_COUNT:
+ tmp = rootfragrecptr.p->m_commit_count;
+ break;
+ default:
+ tmp = 0;
+ }
+ memcpy(signal->theData, &tmp, 8); /* must be memcpy, gives strange results on
+ * ithanium gcc (GCC) 3.4.1 smp linux 2.4
+ * otherwise
+ */
+ // Uint32 * src = (Uint32*)&tmp;
+ // signal->theData[0] = src[0];
+ // signal->theData[1] = src[1];
+}
+
diff --git a/storage/ndb/src/kernel/blocks/dbacc/Makefile.am b/storage/ndb/src/kernel/blocks/dbacc/Makefile.am
new file mode 100644
index 00000000000..ca1b1efac37
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbacc/Makefile.am
@@ -0,0 +1,26 @@
+
+noinst_LIBRARIES = libdbacc.a
+
+libdbacc_a_SOURCES = DbaccInit.cpp DbaccMain.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/kernel/blocks/dbtup
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbacc.dsp
+
+libdbacc.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbacc_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbdict/CreateIndex.txt b/storage/ndb/src/kernel/blocks/dbdict/CreateIndex.txt
new file mode 100644
index 00000000000..3d11e501c07
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/CreateIndex.txt
@@ -0,0 +1,152 @@
+Unique Hash Index
+=================
+
+unique hash index X on T(A1,...,An) becomes:
+table X with primary key A1,...,An and extra attribute NDB$PK
+
+NDB$PK is primary key of T concatenated at 4-byte boundaries
+
+Protocols:
+
+U - user, initiator of protocol
+C - coordinator
+P - participants, including coordinator node
+
+RT_ - request type, current state
+
+P always replies to C with current RT_ (initially RT_DICT_PREPARE)
+C replies to U at the end
+
+CREATE INDEX
+------------
+
+U: RT_USER
+
+C: forward request to P's
+P: check and reply
+
+C: invoke CREATE TABLE for index table
+
+C: invoke ALTER INDEX online
+
+C: send RT_DICT_COMMIT to P's
+P: reply
+
+C: reply to U
+
+DROP INDEX
+----------
+
+[ todo ]
+
+ALTER INDEX online
+------------------
+
+U: RT_USER, RT_CREATE_INDEX, RT_NODERESTART, RT_SYSTEMRESTART
+
+C: forward request to P's
+P: check and reply
+
+C: send RT_DICT_TC to P's
+P: create index in local TC, and reply
+
+C: invoke CREATE TRIGGER for insert/update/delete triggers
+
+C: invoke BUILD INDEX
+
+C: send RT_DICT_COMMIT to P's
+P: reply
+
+C: reply to U
+
+ALTER INDEX offline
+-------------------
+
+[ todo ]
+
+BUILD INDEX
+-----------
+
+U: RT_USER, RT_ALTER_INDEX
+
+C: forward request to P's
+P: check and reply
+
+C: invoke CREATE TRIGGER for read-only constraint on NDB$PK
+
+C: send RT_DICT_TRIX to P's
+P: build index via local TRIX, and reply
+
+C: invoke DROP TRIGGER for read-only constraint on NDB$PK
+
+C: send RT_DICT_TC to P's
+P: online index in local TC, and reply
+
+CREATE TRIGGER
+--------------
+
+U: RT_USER, RT_ALTER_INDEX, RT_BUILD_INDEX
+
+C: forward request to P's
+P: check and reply
+
+C: seize trigger id and send RT_DICT_CREATE to P's
+P: create trigger in DICT (also connect to index record), and reply
+
+C: invoke ALTER TRIGGER online [ not if subscription trigger ]
+
+C: send RT_DICT_COMMIT to P's
+P: reply
+
+C: reply to U
+
+DROP TRIGGER
+------------
+
+[ todo ]
+
+ALTER TRIGGER online
+--------------------
+
+U: RT_USER, RT_CREATE_TRIGGER
+
+C: forward request to P's
+P: check and reply
+
+C: send RT_DICT_TC to P's
+P: create trigger in local TC, and reply
+
+C: send RT_DICT_LQH to P's
+P: create trigger in local LQH (which just forwards to TUP), and reply
+
+C: send RT_DICT_COMMIT to P's
+P: reply
+
+C: reply to U
+
+ALTER TRIGGER offline
+---------------------
+
+[ todo ]
+
+Ordered Index << under work >>
+=============
+
+created as DICT table, as before, to reuse the code
+
+keep NDB$PK as last attribute (not used but logically correct)
+
+create fragments and attributes must be modified
+
+global metadata? implemented but will use signals anyway
+
+create (after-) insert/update/delete triggers as DICT objects, as before
+
+skip following:
+- create index in TC
+- create triggers in TC
+- read-only constraint on NDB$PK
+
+create (before-) commit trigger in TUP
+
+alter online (in TUX, instead of TC) is needed
diff --git a/storage/ndb/src/kernel/blocks/dbdict/CreateTable.new.txt b/storage/ndb/src/kernel/blocks/dbdict/CreateTable.new.txt
new file mode 100644
index 00000000000..d37732dcda1
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/CreateTable.new.txt
@@ -0,0 +1,29 @@
+
+1) Receive from client (sequence of DICTTABINFO)
+
+2) CREATE_FRAGMENTATION_REQ -> local DIH
+ Returns all fragments for table + some other stuff
+ NOTE without side effects in DIH
+
+3) Pack table description
+
+4) CREATE_TAB -> all DICTs (including table data)
+ 1) Write schema file (ADD_STARTED)
+ 2) Write table descriptor to file
+ 3) CREATE_TAB (DIADDTABREQ) -> local DIH (including fragment info)
+ 4) DIH
+ 1) write table descriptor
+ 2) For each local fragment
+ ADD_FRAG -> local DICT
+ LQHFRAGREQ -> local LQH
+ LQHADDATTREQ -> local LQH
+ 5) TAB_COMMITREQ -> local LQH
+
+5) WAIT_GCP
+
+6) ALTER_TAB (activate) -> all DICTs
+ 1) Write schema file (CREATED)
+ 2) TAB_COMMITREQ -> local DIH
+ 3) TC_SCHVERREQ -> local TC
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbdict/CreateTable.txt b/storage/ndb/src/kernel/blocks/dbdict/CreateTable.txt
new file mode 100644
index 00000000000..0b37e5d767f
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/CreateTable.txt
@@ -0,0 +1,35 @@
+
+1) Receive from client (sequence of DICTTABINFO)
+
+2) DICT_SCHEMAREQ -> all DICTs
+ Write ADD_STARTED in schema file
+
+3) Pack table description
+
+4) DICTTABINFO -> all DICTs (but self) (containing packed table info)
+ self -> Write 2 file
+ 1) Write 2 file
+
+5) DICT_SCHEMAREQ -> all DICTs
+ Write UPDATE_PAGE_COUNT in schema file
+
+6) DIADDTABREQ -> local DIH
+ 1) Create fragments
+ 2) For each fragment
+ DIHADDFRAGREQ -> all DIH
+ 3) For each fragment
+ DICTFRAGSREQ -> local DICT
+ 1) LQHFRAGREQ -> concerned LQH
+ 2) For each attribute
+ LQHADDATTREQ -> concerned LQH
+
+7) WAIT_GCP -> local DIH
+
+8) DICT_SCHEMAREQ -> all DICTs
+ Write TABLE_ADD_COMMITTED in schema file
+
+9) TAB_COMMITREQ -> all LQH & DIH
+
+10) TC_SCHVERREQ -> all TC
+
+11) UNBLO_DICTREQ -> all DICT
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
new file mode 100644
index 00000000000..4bc5b127a8f
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
@@ -0,0 +1,11884 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_sys.h>
+
+#define DBDICT_C
+#include "Dbdict.hpp"
+
+#include <ndb_limits.h>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <Configuration.hpp>
+#include <SectionReader.hpp>
+#include <SimpleProperties.hpp>
+#include <AttributeHeader.hpp>
+#include <signaldata/DictSchemaInfo.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/DropTabFile.hpp>
+
+#include <signaldata/EventReport.hpp>
+#include <signaldata/FsCloseReq.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsOpenReq.hpp>
+#include <signaldata/FsReadWriteReq.hpp>
+#include <signaldata/FsRef.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/GetTableId.hpp>
+#include <signaldata/HotSpareRep.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/RelTabMem.hpp>
+#include <signaldata/WaitGCP.hpp>
+#include <signaldata/ListTables.hpp>
+
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/AlterTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/CreateIndx.hpp>
+#include <signaldata/DropIndx.hpp>
+#include <signaldata/BuildIndx.hpp>
+
+#include <signaldata/CreateEvnt.hpp>
+#include <signaldata/UtilPrepare.hpp>
+#include <signaldata/UtilExecute.hpp>
+#include <signaldata/UtilRelease.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <GrepError.hpp>
+//#include <signaldata/DropEvnt.hpp>
+
+#include <signaldata/LqhFrag.hpp>
+
+#include <signaldata/DiAddTab.hpp>
+#include <signaldata/DihStartTab.hpp>
+
+#include <signaldata/DropTable.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/PrepDropTab.hpp>
+
+#include <signaldata/CreateTable.hpp>
+#include <signaldata/AlterTable.hpp>
+#include <signaldata/AlterTab.hpp>
+#include <signaldata/CreateFragmentation.hpp>
+#include <signaldata/CreateTab.hpp>
+#include <NdbSleep.h>
+
+#define ZNOT_FOUND 626
+#define ZALREADYEXIST 630
+
+//#define EVENT_PH2_DEBUG
+//#define EVENT_PH3_DEBUG
+//#define EVENT_DEBUG
+
+#define EVENT_TRACE \
+// ndbout_c("Event debug trace: File: %s Line: %u", __FILE__, __LINE__)
+
+#define DIV(x,y) (((x)+(y)-1)/(y))
+#include <ndb_version.h>
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: GENERAL MODULE -------------------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains general stuff. Mostly debug signals and */
+/* general signals that go into a specific module after checking a */
+/* state variable. Also general subroutines used by many. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+/* ---------------------------------------------------------------- */
+// This signal is used to dump states of various variables in the
+// block by command.
+/* ---------------------------------------------------------------- */
+void
+Dbdict::execDUMP_STATE_ORD(Signal* signal)
+{
+ jamEntry();
+
+#ifdef VM_TRACE
+ if(signal->theData[0] == 1222){
+ const Uint32 tab = signal->theData[1];
+ PrepDropTabReq* req = (PrepDropTabReq*)signal->getDataPtr();
+ req->senderRef = reference();
+ req->senderData = 1222;
+ req->tableId = tab;
+ sendSignal(DBLQH_REF, GSN_PREP_DROP_TAB_REQ, signal,
+ PrepDropTabReq::SignalLength, JBB);
+ }
+
+ if(signal->theData[0] == 1223){
+ const Uint32 tab = signal->theData[1];
+ PrepDropTabReq* req = (PrepDropTabReq*)signal->getDataPtr();
+ req->senderRef = reference();
+ req->senderData = 1222;
+ req->tableId = tab;
+ sendSignal(DBTC_REF, GSN_PREP_DROP_TAB_REQ, signal,
+ PrepDropTabReq::SignalLength, JBB);
+ }
+
+ if(signal->theData[0] == 1224){
+ const Uint32 tab = signal->theData[1];
+ PrepDropTabReq* req = (PrepDropTabReq*)signal->getDataPtr();
+ req->senderRef = reference();
+ req->senderData = 1222;
+ req->tableId = tab;
+ sendSignal(DBDIH_REF, GSN_PREP_DROP_TAB_REQ, signal,
+ PrepDropTabReq::SignalLength, JBB);
+ }
+
+ if(signal->theData[0] == 1225){
+ const Uint32 tab = signal->theData[1];
+ const Uint32 ver = signal->theData[2];
+ TableRecordPtr tabRecPtr;
+ c_tableRecordPool.getPtr(tabRecPtr, tab);
+ DropTableReq * req = (DropTableReq*)signal->getDataPtr();
+ req->senderData = 1225;
+ req->senderRef = numberToRef(1,1);
+ req->tableId = tab;
+ req->tableVersion = tabRecPtr.p->tableVersion + ver;
+ sendSignal(DBDICT_REF, GSN_DROP_TABLE_REQ, signal,
+ DropTableReq::SignalLength, JBB);
+ }
+#endif
+
+ return;
+}//Dbdict::execDUMP_STATE_ORD()
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+// CONTINUEB is used when a real-time break is needed for long
+// processes.
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+void Dbdict::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ switch (signal->theData[0]) {
+ case ZPACK_TABLE_INTO_PAGES :
+ jam();
+ packTableIntoPages(signal, signal->theData[1], signal->theData[2]);
+ break;
+
+ case ZSEND_GET_TAB_RESPONSE :
+ jam();
+ sendGetTabResponse(signal);
+ break;
+
+ default :
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//execCONTINUEB()
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+// Routine to handle pack table into pages.
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+
+void Dbdict::packTableIntoPages(Signal* signal, Uint32 tableId, Uint32 pageId)
+{
+
+ PageRecordPtr pagePtr;
+ TableRecordPtr tablePtr;
+ c_pageRecordArray.getPtr(pagePtr, pageId);
+
+ memset(&pagePtr.p->word[0], 0, 4 * ZPAGE_HEADER_SIZE);
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ LinearWriter w(&pagePtr.p->word[ZPAGE_HEADER_SIZE],
+ 8 * ZSIZE_OF_PAGES_IN_WORDS);
+
+ w.first();
+ packTableIntoPagesImpl(w, tablePtr, signal);
+
+ Uint32 wordsOfTable = w.getWordsUsed();
+ Uint32 pagesUsed =
+ DIV(wordsOfTable + ZPAGE_HEADER_SIZE, ZSIZE_OF_PAGES_IN_WORDS);
+ pagePtr.p->word[ZPOS_CHECKSUM] =
+ computeChecksum(&pagePtr.p->word[0], pagesUsed * ZSIZE_OF_PAGES_IN_WORDS);
+
+ switch (c_packTable.m_state) {
+ case PackTable::PTS_IDLE:
+ case PackTable::PTS_ADD_TABLE_MASTER:
+ case PackTable::PTS_ADD_TABLE_SLAVE:
+ case PackTable::PTS_RESTART:
+ ndbrequire(false);
+ break;
+ case PackTable::PTS_GET_TAB:
+ jam();
+ c_retrieveRecord.retrievedNoOfPages = pagesUsed;
+ c_retrieveRecord.retrievedNoOfWords = wordsOfTable;
+ sendGetTabResponse(signal);
+ return;
+ break;
+ }//switch
+ ndbrequire(false);
+ return;
+}//packTableIntoPages()
+
+void
+Dbdict::packTableIntoPagesImpl(SimpleProperties::Writer & w,
+ TableRecordPtr tablePtr,
+ Signal* signal){
+
+ w.add(DictTabInfo::TableName, tablePtr.p->tableName);
+ w.add(DictTabInfo::TableId, tablePtr.i);
+#ifdef HAVE_TABLE_REORG
+ w.add(DictTabInfo::SecondTableId, tablePtr.p->secondTable);
+#else
+ w.add(DictTabInfo::SecondTableId, (Uint32)0);
+#endif
+ w.add(DictTabInfo::TableVersion, tablePtr.p->tableVersion);
+ w.add(DictTabInfo::NoOfKeyAttr, tablePtr.p->noOfPrimkey);
+ w.add(DictTabInfo::NoOfAttributes, tablePtr.p->noOfAttributes);
+ w.add(DictTabInfo::NoOfNullable, tablePtr.p->noOfNullAttr);
+ w.add(DictTabInfo::NoOfVariable, (Uint32)0);
+ w.add(DictTabInfo::KeyLength, tablePtr.p->tupKeyLength);
+
+ w.add(DictTabInfo::TableLoggedFlag, tablePtr.p->storedTable);
+ w.add(DictTabInfo::MinLoadFactor, tablePtr.p->minLoadFactor);
+ w.add(DictTabInfo::MaxLoadFactor, tablePtr.p->maxLoadFactor);
+ w.add(DictTabInfo::TableKValue, tablePtr.p->kValue);
+ w.add(DictTabInfo::FragmentTypeVal, tablePtr.p->fragmentType);
+ w.add(DictTabInfo::TableTypeVal, tablePtr.p->tableType);
+
+ if(!signal)
+ {
+ w.add(DictTabInfo::FragmentCount, tablePtr.p->fragmentCount);
+ }
+ else
+ {
+ Uint32 * theData = signal->getDataPtrSend();
+ CreateFragmentationReq * const req = (CreateFragmentationReq*)theData;
+ req->senderRef = 0;
+ req->senderData = RNIL;
+ req->fragmentationType = tablePtr.p->fragmentType;
+ req->noOfFragments = 0;
+ req->fragmentNode = 0;
+ req->primaryTableId = tablePtr.i;
+ EXECUTE_DIRECT(DBDIH, GSN_CREATE_FRAGMENTATION_REQ, signal,
+ CreateFragmentationReq::SignalLength);
+ if(signal->theData[0] == 0)
+ {
+ Uint16 *data = (Uint16*)&signal->theData[25];
+ Uint32 count = 2 + data[0] * data[1];
+ w.add(DictTabInfo::FragmentDataLen, 2*count);
+ w.add(DictTabInfo::FragmentData, data, 2*count);
+ }
+ }
+
+ if (tablePtr.p->primaryTableId != RNIL){
+ TableRecordPtr primTab;
+ c_tableRecordPool.getPtr(primTab, tablePtr.p->primaryTableId);
+ w.add(DictTabInfo::PrimaryTable, primTab.p->tableName);
+ w.add(DictTabInfo::PrimaryTableId, tablePtr.p->primaryTableId);
+ w.add(DictTabInfo::IndexState, tablePtr.p->indexState);
+ w.add(DictTabInfo::InsertTriggerId, tablePtr.p->insertTriggerId);
+ w.add(DictTabInfo::UpdateTriggerId, tablePtr.p->updateTriggerId);
+ w.add(DictTabInfo::DeleteTriggerId, tablePtr.p->deleteTriggerId);
+ w.add(DictTabInfo::CustomTriggerId, tablePtr.p->customTriggerId);
+ }
+ w.add(DictTabInfo::FrmLen, tablePtr.p->frmLen);
+ w.add(DictTabInfo::FrmData, tablePtr.p->frmData, tablePtr.p->frmLen);
+
+ Uint32 nextAttribute = tablePtr.p->firstAttribute;
+ AttributeRecordPtr attrPtr;
+ do {
+ jam();
+ c_attributeRecordPool.getPtr(attrPtr, nextAttribute);
+
+ w.add(DictTabInfo::AttributeName, attrPtr.p->attributeName);
+ w.add(DictTabInfo::AttributeId, attrPtr.p->attributeId);
+ w.add(DictTabInfo::AttributeKeyFlag, attrPtr.p->tupleKey > 0);
+
+ const Uint32 desc = attrPtr.p->attributeDescriptor;
+ const Uint32 attrType = AttributeDescriptor::getType(desc);
+ const Uint32 attrSize = AttributeDescriptor::getSize(desc);
+ const Uint32 arraySize = AttributeDescriptor::getArraySize(desc);
+ const Uint32 nullable = AttributeDescriptor::getNullable(desc);
+ const Uint32 DKey = AttributeDescriptor::getDKey(desc);
+
+ // AttributeType deprecated
+ w.add(DictTabInfo::AttributeSize, attrSize);
+ w.add(DictTabInfo::AttributeArraySize, arraySize);
+ w.add(DictTabInfo::AttributeNullableFlag, nullable);
+ w.add(DictTabInfo::AttributeDKey, DKey);
+ w.add(DictTabInfo::AttributeExtType, attrType);
+ w.add(DictTabInfo::AttributeExtPrecision, attrPtr.p->extPrecision);
+ w.add(DictTabInfo::AttributeExtScale, attrPtr.p->extScale);
+ w.add(DictTabInfo::AttributeExtLength, attrPtr.p->extLength);
+ w.add(DictTabInfo::AttributeAutoIncrement,
+ (Uint32)attrPtr.p->autoIncrement);
+ w.add(DictTabInfo::AttributeDefaultValue, attrPtr.p->defaultValue);
+
+ w.add(DictTabInfo::AttributeEnd, 1);
+ nextAttribute = attrPtr.p->nextAttrInTable;
+ } while (nextAttribute != RNIL);
+
+ w.add(DictTabInfo::TableEnd, 1);
+}
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+// The routines to handle responses from file system.
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+
+/* ---------------------------------------------------------------- */
+// A file was successfully closed.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSCLOSECONF(Signal* signal)
+{
+ FsConnectRecordPtr fsPtr;
+ FsConf * const fsConf = (FsConf *)&signal->theData[0];
+ jamEntry();
+ c_fsConnectRecordPool.getPtr(fsPtr, fsConf->userPointer);
+ switch (fsPtr.p->fsState) {
+ case FsConnectRecord::CLOSE_WRITE_SCHEMA:
+ jam();
+ closeWriteSchemaConf(signal, fsPtr);
+ break;
+ case FsConnectRecord::CLOSE_READ_SCHEMA:
+ jam();
+ closeReadSchemaConf(signal, fsPtr);
+ break;
+ case FsConnectRecord::CLOSE_READ_TAB_FILE:
+ jam();
+ closeReadTableConf(signal, fsPtr);
+ break;
+ case FsConnectRecord::CLOSE_WRITE_TAB_FILE:
+ jam();
+ closeWriteTableConf(signal, fsPtr);
+ break;
+ default:
+ jamLine((fsPtr.p->fsState & 0xFFF));
+ ndbrequire(false);
+ break;
+ }//switch
+}//execFSCLOSECONF()
+
+/* ---------------------------------------------------------------- */
+// A close file was refused.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSCLOSEREF(Signal* signal)
+{
+ jamEntry();
+ progError(0, 0);
+}//execFSCLOSEREF()
+
+/* ---------------------------------------------------------------- */
+// A file was successfully opened.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSOPENCONF(Signal* signal)
+{
+ FsConnectRecordPtr fsPtr;
+ jamEntry();
+ FsConf * const fsConf = (FsConf *)&signal->theData[0];
+ c_fsConnectRecordPool.getPtr(fsPtr, fsConf->userPointer);
+
+ Uint32 filePointer = fsConf->filePointer;
+ fsPtr.p->filePtr = filePointer;
+ switch (fsPtr.p->fsState) {
+ case FsConnectRecord::OPEN_WRITE_SCHEMA:
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::WRITE_SCHEMA;
+ writeSchemaFile(signal, filePointer, fsPtr.i);
+ break;
+ case FsConnectRecord::OPEN_READ_SCHEMA1:
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::READ_SCHEMA1;
+ readSchemaFile(signal, filePointer, fsPtr.i);
+ break;
+ case FsConnectRecord::OPEN_READ_SCHEMA2:
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::READ_SCHEMA2;
+ readSchemaFile(signal, filePointer, fsPtr.i);
+ break;
+ case FsConnectRecord::OPEN_READ_TAB_FILE1:
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::READ_TAB_FILE1;
+ readTableFile(signal, filePointer, fsPtr.i);
+ break;
+ case FsConnectRecord::OPEN_READ_TAB_FILE2:
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::READ_TAB_FILE2;
+ readTableFile(signal, filePointer, fsPtr.i);
+ break;
+ case FsConnectRecord::OPEN_WRITE_TAB_FILE:
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::WRITE_TAB_FILE;
+ writeTableFile(signal, filePointer, fsPtr.i);
+ break;
+ default:
+ jamLine((fsPtr.p->fsState & 0xFFF));
+ ndbrequire(false);
+ break;
+ }//switch
+}//execFSOPENCONF()
+
+/* ---------------------------------------------------------------- */
+// An open file was refused.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSOPENREF(Signal* signal)
+{
+ jamEntry();
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.getPtr(fsPtr, fsRef->userPointer);
+ switch (fsPtr.p->fsState) {
+ case FsConnectRecord::OPEN_READ_SCHEMA1:
+ openReadSchemaRef(signal, fsPtr);
+ break;
+ case FsConnectRecord::OPEN_READ_TAB_FILE1:
+ jam();
+ openReadTableRef(signal, fsPtr);
+ break;
+ default:
+ jamLine((fsPtr.p->fsState & 0xFFF));
+ ndbrequire(false);
+ break;
+ }//switch
+}//execFSOPENREF()
+
+/* ---------------------------------------------------------------- */
+// A file was successfully read.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSREADCONF(Signal* signal)
+{
+ jamEntry();
+ FsConf * const fsConf = (FsConf *)&signal->theData[0];
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.getPtr(fsPtr, fsConf->userPointer);
+ switch (fsPtr.p->fsState) {
+ case FsConnectRecord::READ_SCHEMA1:
+ case FsConnectRecord::READ_SCHEMA2:
+ readSchemaConf(signal ,fsPtr);
+ break;
+ case FsConnectRecord::READ_TAB_FILE1:
+ case FsConnectRecord::READ_TAB_FILE2:
+ jam();
+ readTableConf(signal ,fsPtr);
+ break;
+ default:
+ jamLine((fsPtr.p->fsState & 0xFFF));
+ ndbrequire(false);
+ break;
+ }//switch
+}//execFSREADCONF()
+
+/* ---------------------------------------------------------------- */
+// A read file was refused.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSREADREF(Signal* signal)
+{
+ jamEntry();
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.getPtr(fsPtr, fsRef->userPointer);
+ switch (fsPtr.p->fsState) {
+ case FsConnectRecord::READ_SCHEMA1:
+ readSchemaRef(signal, fsPtr);
+ break;
+ case FsConnectRecord::READ_TAB_FILE1:
+ jam();
+ readTableRef(signal, fsPtr);
+ break;
+ default:
+ jamLine((fsPtr.p->fsState & 0xFFF));
+ ndbrequire(false);
+ break;
+ }//switch
+}//execFSREADREF()
+
+/* ---------------------------------------------------------------- */
+// A file was successfully written.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSWRITECONF(Signal* signal)
+{
+ FsConf * const fsConf = (FsConf *)&signal->theData[0];
+ FsConnectRecordPtr fsPtr;
+ jamEntry();
+ c_fsConnectRecordPool.getPtr(fsPtr, fsConf->userPointer);
+ switch (fsPtr.p->fsState) {
+ case FsConnectRecord::WRITE_TAB_FILE:
+ writeTableConf(signal, fsPtr);
+ break;
+ case FsConnectRecord::WRITE_SCHEMA:
+ jam();
+ writeSchemaConf(signal, fsPtr);
+ break;
+ default:
+ jamLine((fsPtr.p->fsState & 0xFFF));
+ ndbrequire(false);
+ break;
+ }//switch
+}//execFSWRITECONF()
+
+/* ---------------------------------------------------------------- */
+// A write file was refused.
+/* ---------------------------------------------------------------- */
+void Dbdict::execFSWRITEREF(Signal* signal)
+{
+ jamEntry();
+ progError(0, 0);
+}//execFSWRITEREF()
+
+/* ---------------------------------------------------------------- */
+// Routines to handle Read/Write of Table Files
+/* ---------------------------------------------------------------- */
+void
+Dbdict::writeTableFile(Signal* signal, Uint32 tableId,
+ SegmentedSectionPtr tabInfoPtr, Callback* callback){
+
+ ndbrequire(c_writeTableRecord.tableWriteState == WriteTableRecord::IDLE);
+
+ Uint32 sz = tabInfoPtr.sz + ZPAGE_HEADER_SIZE;
+
+ c_writeTableRecord.noOfPages = DIV(sz, ZSIZE_OF_PAGES_IN_WORDS);
+ c_writeTableRecord.tableWriteState = WriteTableRecord::TWR_CALLBACK;
+ c_writeTableRecord.m_callback = * callback;
+
+ c_writeTableRecord.pageId = 0;
+ ndbrequire(c_writeTableRecord.noOfPages < 8);
+
+ PageRecordPtr pageRecPtr;
+ c_pageRecordArray.getPtr(pageRecPtr, c_writeTableRecord.pageId);
+ copy(&pageRecPtr.p->word[ZPAGE_HEADER_SIZE], tabInfoPtr);
+
+ memset(&pageRecPtr.p->word[0], 0, 4 * ZPAGE_HEADER_SIZE);
+ pageRecPtr.p->word[ZPOS_CHECKSUM] =
+ computeChecksum(&pageRecPtr.p->word[0],
+ c_writeTableRecord.noOfPages * ZSIZE_OF_PAGES_IN_WORDS);
+
+ startWriteTableFile(signal, tableId);
+
+}
+
+void Dbdict::startWriteTableFile(Signal* signal, Uint32 tableId)
+{
+ FsConnectRecordPtr fsPtr;
+ c_writeTableRecord.tableId = tableId;
+ c_fsConnectRecordPool.getPtr(fsPtr, getFsConnRecord());
+ fsPtr.p->fsState = FsConnectRecord::OPEN_WRITE_TAB_FILE;
+ openTableFile(signal, 0, fsPtr.i, tableId, true);
+ c_writeTableRecord.noOfTableFilesHandled = 0;
+}//Dbdict::startWriteTableFile()
+
+void Dbdict::openTableFile(Signal* signal,
+ Uint32 fileNo,
+ Uint32 fsConPtr,
+ Uint32 tableId,
+ bool writeFlag)
+{
+ TableRecordPtr tablePtr;
+ FsOpenReq * const fsOpenReq = (FsOpenReq *)&signal->theData[0];
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+
+ fsOpenReq->userReference = reference();
+ fsOpenReq->userPointer = fsConPtr;
+ if (writeFlag) {
+ jam();
+ fsOpenReq->fileFlags =
+ FsOpenReq::OM_WRITEONLY |
+ FsOpenReq::OM_TRUNCATE |
+ FsOpenReq::OM_CREATE |
+ FsOpenReq::OM_SYNC;
+ } else {
+ jam();
+ fsOpenReq->fileFlags = FsOpenReq::OM_READONLY;
+ }//if
+ ndbrequire(tablePtr.p->tableVersion < ZNIL);
+ fsOpenReq->fileNumber[3] = 0; // Initialise before byte changes
+ FsOpenReq::setVersion(fsOpenReq->fileNumber, 1);
+ FsOpenReq::setSuffix(fsOpenReq->fileNumber, FsOpenReq::S_TABLELIST);
+ FsOpenReq::v1_setDisk(fsOpenReq->fileNumber, (fileNo + 1));
+ FsOpenReq::v1_setTable(fsOpenReq->fileNumber, tableId);
+ FsOpenReq::v1_setFragment(fsOpenReq->fileNumber, (Uint32)-1);
+ FsOpenReq::v1_setS(fsOpenReq->fileNumber, tablePtr.p->tableVersion);
+ FsOpenReq::v1_setP(fsOpenReq->fileNumber, 255);
+/* ---------------------------------------------------------------- */
+// File name : D1/DBDICT/T0/S1.TableList
+// D1 means Disk 1 (set by fileNo + 1)
+// T0 means table id = 0
+// S1 means tableVersion 1
+// TableList indicates that this is a file for a table description.
+/* ---------------------------------------------------------------- */
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, FsOpenReq::SignalLength, JBA);
+}//openTableFile()
+
+void Dbdict::writeTableFile(Signal* signal, Uint32 filePtr, Uint32 fsConPtr)
+{
+ FsReadWriteReq * const fsRWReq = (FsReadWriteReq *)&signal->theData[0];
+
+ fsRWReq->filePointer = filePtr;
+ fsRWReq->userReference = reference();
+ fsRWReq->userPointer = fsConPtr;
+ fsRWReq->operationFlag = 0; // Initialise before bit changes
+ FsReadWriteReq::setSyncFlag(fsRWReq->operationFlag, 1);
+ FsReadWriteReq::setFormatFlag(fsRWReq->operationFlag,
+ FsReadWriteReq::fsFormatArrayOfPages);
+ fsRWReq->varIndex = ZALLOCATE;
+ fsRWReq->numberOfPages = c_writeTableRecord.noOfPages;
+ fsRWReq->data.arrayOfPages.varIndex = c_writeTableRecord.pageId;
+ fsRWReq->data.arrayOfPages.fileOffset = 0; // Write to file page 0
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+}//writeTableFile()
+
+void Dbdict::writeTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ fsPtr.p->fsState = FsConnectRecord::CLOSE_WRITE_TAB_FILE;
+ closeFile(signal, fsPtr.p->filePtr, fsPtr.i);
+ return;
+}//Dbdict::writeTableConf()
+
+void Dbdict::closeWriteTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ c_writeTableRecord.noOfTableFilesHandled++;
+ if (c_writeTableRecord.noOfTableFilesHandled < 2) {
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::OPEN_WRITE_TAB_FILE;
+ openTableFile(signal, 1, fsPtr.i, c_writeTableRecord.tableId, true);
+ return;
+ }
+ ndbrequire(c_writeTableRecord.noOfTableFilesHandled == 2);
+ c_fsConnectRecordPool.release(fsPtr);
+ WriteTableRecord::TableWriteState state = c_writeTableRecord.tableWriteState;
+ c_writeTableRecord.tableWriteState = WriteTableRecord::IDLE;
+ switch (state) {
+ case WriteTableRecord::IDLE:
+ case WriteTableRecord::WRITE_ADD_TABLE_MASTER :
+ case WriteTableRecord::WRITE_ADD_TABLE_SLAVE :
+ case WriteTableRecord::WRITE_RESTART_FROM_MASTER :
+ case WriteTableRecord::WRITE_RESTART_FROM_OWN :
+ ndbrequire(false);
+ break;
+ case WriteTableRecord::TWR_CALLBACK:
+ jam();
+ execute(signal, c_writeTableRecord.m_callback, 0);
+ return;
+ }
+ ndbrequire(false);
+}//Dbdict::closeWriteTableConf()
+
+void Dbdict::startReadTableFile(Signal* signal, Uint32 tableId)
+{
+ //globalSignalLoggers.log(number(), "startReadTableFile");
+ ndbrequire(!c_readTableRecord.inUse);
+
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.getPtr(fsPtr, getFsConnRecord());
+ c_readTableRecord.inUse = true;
+ c_readTableRecord.tableId = tableId;
+ fsPtr.p->fsState = FsConnectRecord::OPEN_READ_TAB_FILE1;
+ openTableFile(signal, 0, fsPtr.i, tableId, false);
+}//Dbdict::startReadTableFile()
+
+void Dbdict::openReadTableRef(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ fsPtr.p->fsState = FsConnectRecord::OPEN_READ_TAB_FILE2;
+ openTableFile(signal, 1, fsPtr.i, c_readTableRecord.tableId, false);
+ return;
+}//Dbdict::openReadTableConf()
+
+void Dbdict::readTableFile(Signal* signal, Uint32 filePtr, Uint32 fsConPtr)
+{
+ FsReadWriteReq * const fsRWReq = (FsReadWriteReq *)&signal->theData[0];
+
+ fsRWReq->filePointer = filePtr;
+ fsRWReq->userReference = reference();
+ fsRWReq->userPointer = fsConPtr;
+ fsRWReq->operationFlag = 0; // Initialise before bit changes
+ FsReadWriteReq::setSyncFlag(fsRWReq->operationFlag, 0);
+ FsReadWriteReq::setFormatFlag(fsRWReq->operationFlag,
+ FsReadWriteReq::fsFormatArrayOfPages);
+ fsRWReq->varIndex = ZALLOCATE;
+ fsRWReq->numberOfPages = c_readTableRecord.noOfPages;
+ fsRWReq->data.arrayOfPages.varIndex = c_readTableRecord.pageId;
+ fsRWReq->data.arrayOfPages.fileOffset = 0; // Write to file page 0
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+}//readTableFile()
+
+void Dbdict::readTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ /* ---------------------------------------------------------------- */
+ // Verify the data read from disk
+ /* ---------------------------------------------------------------- */
+ bool crashInd;
+ if (fsPtr.p->fsState == FsConnectRecord::READ_TAB_FILE1) {
+ jam();
+ crashInd = false;
+ } else {
+ jam();
+ crashInd = true;
+ }//if
+
+ PageRecordPtr tmpPagePtr;
+ c_pageRecordArray.getPtr(tmpPagePtr, c_readTableRecord.pageId);
+ Uint32 sz = c_readTableRecord.noOfPages * ZSIZE_OF_PAGES_IN_WORDS;
+ Uint32 chk = computeChecksum((const Uint32*)tmpPagePtr.p, sz);
+
+ ndbrequire((chk == 0) || !crashInd);
+ if(chk != 0){
+ jam();
+ ndbrequire(fsPtr.p->fsState == FsConnectRecord::READ_TAB_FILE1);
+ readTableRef(signal, fsPtr);
+ return;
+ }//if
+
+ fsPtr.p->fsState = FsConnectRecord::CLOSE_READ_TAB_FILE;
+ closeFile(signal, fsPtr.p->filePtr, fsPtr.i);
+ return;
+}//Dbdict::readTableConf()
+
+void Dbdict::readTableRef(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ fsPtr.p->fsState = FsConnectRecord::OPEN_READ_TAB_FILE2;
+ openTableFile(signal, 1, fsPtr.i, c_readTableRecord.tableId, false);
+ return;
+}//Dbdict::readTableRef()
+
+void Dbdict::closeReadTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ c_fsConnectRecordPool.release(fsPtr);
+ c_readTableRecord.inUse = false;
+
+ execute(signal, c_readTableRecord.m_callback, 0);
+ return;
+}//Dbdict::closeReadTableConf()
+
+/* ---------------------------------------------------------------- */
+// Routines to handle Read/Write of Schema Files
+/* ---------------------------------------------------------------- */
+void
+Dbdict::updateSchemaState(Signal* signal, Uint32 tableId,
+ SchemaFile::TableEntry* te, Callback* callback){
+
+ jam();
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+
+ ndbrequire(tableId < c_tableRecordPool.getSize());
+ SchemaFile::TableEntry * tableEntry = getTableEntry(pagePtr.p, tableId);
+
+ SchemaFile::TableState newState =
+ (SchemaFile::TableState)te->m_tableState;
+ SchemaFile::TableState oldState =
+ (SchemaFile::TableState)tableEntry->m_tableState;
+
+ Uint32 newVersion = te->m_tableVersion;
+ Uint32 oldVersion = tableEntry->m_tableVersion;
+
+ bool ok = false;
+ switch(newState){
+ case SchemaFile::ADD_STARTED:
+ jam();
+ ok = true;
+ ndbrequire((oldVersion + 1) == newVersion);
+ ndbrequire(oldState == SchemaFile::INIT ||
+ oldState == SchemaFile::DROP_TABLE_COMMITTED);
+ break;
+ case SchemaFile::TABLE_ADD_COMMITTED:
+ jam();
+ ok = true;
+ ndbrequire(newVersion == oldVersion);
+ ndbrequire(oldState == SchemaFile::ADD_STARTED);
+ break;
+ case SchemaFile::ALTER_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ ndbrequire((oldVersion + 1) == newVersion);
+ ndbrequire(oldState == SchemaFile::TABLE_ADD_COMMITTED ||
+ oldState == SchemaFile::ALTER_TABLE_COMMITTED);
+ break;
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ ndbrequire(false);
+ break;
+ case SchemaFile::INIT:
+ jam();
+ ok = true;
+ ndbrequire((oldState == SchemaFile::ADD_STARTED));
+ }//if
+ ndbrequire(ok);
+
+ * tableEntry = * te;
+ computeChecksum((SchemaFile*)pagePtr.p);
+
+ ndbrequire(c_writeSchemaRecord.inUse == false);
+ c_writeSchemaRecord.inUse = true;
+
+ c_writeSchemaRecord.pageId = c_schemaRecord.schemaPage;
+ c_writeSchemaRecord.m_callback = * callback;
+
+ startWriteSchemaFile(signal);
+}
+
+void Dbdict::startWriteSchemaFile(Signal* signal)
+{
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.getPtr(fsPtr, getFsConnRecord());
+ fsPtr.p->fsState = FsConnectRecord::OPEN_WRITE_SCHEMA;
+ openSchemaFile(signal, 0, fsPtr.i, true);
+ c_writeSchemaRecord.noOfSchemaFilesHandled = 0;
+}//Dbdict::startWriteSchemaFile()
+
+void Dbdict::openSchemaFile(Signal* signal,
+ Uint32 fileNo,
+ Uint32 fsConPtr,
+ bool writeFlag)
+{
+ FsOpenReq * const fsOpenReq = (FsOpenReq *)&signal->theData[0];
+ fsOpenReq->userReference = reference();
+ fsOpenReq->userPointer = fsConPtr;
+ if (writeFlag) {
+ jam();
+ fsOpenReq->fileFlags =
+ FsOpenReq::OM_WRITEONLY |
+ FsOpenReq::OM_TRUNCATE |
+ FsOpenReq::OM_CREATE |
+ FsOpenReq::OM_SYNC;
+ } else {
+ jam();
+ fsOpenReq->fileFlags = FsOpenReq::OM_READONLY;
+ }//if
+ fsOpenReq->fileNumber[3] = 0; // Initialise before byte changes
+ FsOpenReq::setVersion(fsOpenReq->fileNumber, 1);
+ FsOpenReq::setSuffix(fsOpenReq->fileNumber, FsOpenReq::S_SCHEMALOG);
+ FsOpenReq::v1_setDisk(fsOpenReq->fileNumber, (fileNo + 1));
+ FsOpenReq::v1_setTable(fsOpenReq->fileNumber, (Uint32)-1);
+ FsOpenReq::v1_setFragment(fsOpenReq->fileNumber, (Uint32)-1);
+ FsOpenReq::v1_setS(fsOpenReq->fileNumber, (Uint32)-1);
+ FsOpenReq::v1_setP(fsOpenReq->fileNumber, 0);
+/* ---------------------------------------------------------------- */
+// File name : D1/DBDICT/P0.SchemaLog
+// D1 means Disk 1 (set by fileNo + 1). Writes to both D1 and D2
+// SchemaLog indicates that this is a file giving a list of current tables.
+/* ---------------------------------------------------------------- */
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, FsOpenReq::SignalLength, JBA);
+}//openSchemaFile()
+
+void Dbdict::writeSchemaFile(Signal* signal, Uint32 filePtr, Uint32 fsConPtr)
+{
+ FsReadWriteReq * const fsRWReq = (FsReadWriteReq *)&signal->theData[0];
+
+ fsRWReq->filePointer = filePtr;
+ fsRWReq->userReference = reference();
+ fsRWReq->userPointer = fsConPtr;
+ fsRWReq->operationFlag = 0; // Initialise before bit changes
+ FsReadWriteReq::setSyncFlag(fsRWReq->operationFlag, 1);
+ FsReadWriteReq::setFormatFlag(fsRWReq->operationFlag,
+ FsReadWriteReq::fsFormatArrayOfPages);
+ fsRWReq->varIndex = ZALLOCATE;
+ fsRWReq->numberOfPages = 1;
+// Write from memory page
+ fsRWReq->data.arrayOfPages.varIndex = c_writeSchemaRecord.pageId;
+ fsRWReq->data.arrayOfPages.fileOffset = 0; // Write to file page 0
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+}//writeSchemaFile()
+
+void Dbdict::writeSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ fsPtr.p->fsState = FsConnectRecord::CLOSE_WRITE_SCHEMA;
+ closeFile(signal, fsPtr.p->filePtr, fsPtr.i);
+ return;
+}//Dbdict::writeSchemaConf()
+
+void Dbdict::closeFile(Signal* signal, Uint32 filePtr, Uint32 fsConPtr)
+{
+ FsCloseReq * const fsCloseReq = (FsCloseReq *)&signal->theData[0];
+ fsCloseReq->filePointer = filePtr;
+ fsCloseReq->userReference = reference();
+ fsCloseReq->userPointer = fsConPtr;
+ FsCloseReq::setRemoveFileFlag(fsCloseReq->fileFlag, false);
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, FsCloseReq::SignalLength, JBA);
+ return;
+}//closeFile()
+
+void Dbdict::closeWriteSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ c_writeSchemaRecord.noOfSchemaFilesHandled++;
+ if (c_writeSchemaRecord.noOfSchemaFilesHandled < 2) {
+ jam();
+ fsPtr.p->fsState = FsConnectRecord::OPEN_WRITE_SCHEMA;
+ openSchemaFile(signal, 1, fsPtr.i, true);
+ return;
+ }
+ ndbrequire(c_writeSchemaRecord.noOfSchemaFilesHandled == 2);
+
+ c_fsConnectRecordPool.release(fsPtr);
+
+ c_writeSchemaRecord.inUse = false;
+ execute(signal, c_writeSchemaRecord.m_callback, 0);
+ return;
+}//Dbdict::closeWriteSchemaConf()
+
+void Dbdict::startReadSchemaFile(Signal* signal)
+{
+ //globalSignalLoggers.log(number(), "startReadSchemaFile");
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.getPtr(fsPtr, getFsConnRecord());
+ fsPtr.p->fsState = FsConnectRecord::OPEN_READ_SCHEMA1;
+ openSchemaFile(signal, 0, fsPtr.i, false);
+}//Dbdict::startReadSchemaFile()
+
+void Dbdict::openReadSchemaRef(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ fsPtr.p->fsState = FsConnectRecord::OPEN_READ_SCHEMA2;
+ openSchemaFile(signal, 1, fsPtr.i, false);
+}//Dbdict::openReadSchemaRef()
+
+void Dbdict::readSchemaFile(Signal* signal, Uint32 filePtr, Uint32 fsConPtr)
+{
+ FsReadWriteReq * const fsRWReq = (FsReadWriteReq *)&signal->theData[0];
+
+ fsRWReq->filePointer = filePtr;
+ fsRWReq->userReference = reference();
+ fsRWReq->userPointer = fsConPtr;
+ fsRWReq->operationFlag = 0; // Initialise before bit changes
+ FsReadWriteReq::setSyncFlag(fsRWReq->operationFlag, 0);
+ FsReadWriteReq::setFormatFlag(fsRWReq->operationFlag,
+ FsReadWriteReq::fsFormatArrayOfPages);
+ fsRWReq->varIndex = ZALLOCATE;
+ fsRWReq->numberOfPages = 1;
+ fsRWReq->data.arrayOfPages.varIndex = c_readSchemaRecord.pageId;
+ fsRWReq->data.arrayOfPages.fileOffset = 0;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+}//readSchemaFile()
+
+void Dbdict::readSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+/* ---------------------------------------------------------------- */
+// Verify the data read from disk
+/* ---------------------------------------------------------------- */
+ bool crashInd;
+ if (fsPtr.p->fsState == FsConnectRecord::READ_SCHEMA1) {
+ jam();
+ crashInd = false;
+ } else {
+ jam();
+ crashInd = true;
+ }//if
+ PageRecordPtr tmpPagePtr;
+ c_pageRecordArray.getPtr(tmpPagePtr, c_readSchemaRecord.pageId);
+
+ Uint32 sz = ZSIZE_OF_PAGES_IN_WORDS;
+ Uint32 chk = computeChecksum((const Uint32*)tmpPagePtr.p, sz);
+
+ ndbrequire((chk == 0) || !crashInd);
+
+ if (chk != 0){
+ jam();
+ ndbrequire(fsPtr.p->fsState == FsConnectRecord::READ_SCHEMA1);
+ readSchemaRef(signal, fsPtr);
+ return;
+ }//if
+ fsPtr.p->fsState = FsConnectRecord::CLOSE_READ_SCHEMA;
+ closeFile(signal, fsPtr.p->filePtr, fsPtr.i);
+ return;
+}//Dbdict::readSchemaConf()
+
+void Dbdict::readSchemaRef(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ fsPtr.p->fsState = FsConnectRecord::OPEN_READ_SCHEMA2;
+ openSchemaFile(signal, 1, fsPtr.i, false);
+ return;
+}//Dbdict::readSchemaRef()
+
+void Dbdict::closeReadSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr)
+{
+ c_fsConnectRecordPool.release(fsPtr);
+ ReadSchemaRecord::SchemaReadState state = c_readSchemaRecord.schemaReadState;
+ c_readSchemaRecord.schemaReadState = ReadSchemaRecord::IDLE;
+
+ switch(state) {
+ case ReadSchemaRecord::INITIAL_READ :
+ jam();
+ sendNDB_STTORRY(signal);
+ break;
+
+ default :
+ ndbrequire(false);
+ break;
+
+ }//switch
+}//Dbdict::closeReadSchemaConf()
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: INITIALISATION MODULE ------------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains initialisation of data at start/restart. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+Dbdict::Dbdict(const class Configuration & conf):
+ SimulatedBlock(DBDICT, conf),
+ c_tableRecordHash(c_tableRecordPool),
+ c_attributeRecordHash(c_attributeRecordPool),
+ c_triggerRecordHash(c_triggerRecordPool),
+ c_opCreateTable(c_opRecordPool),
+ c_opDropTable(c_opRecordPool),
+ c_opCreateIndex(c_opRecordPool),
+ c_opDropIndex(c_opRecordPool),
+ c_opAlterIndex(c_opRecordPool),
+ c_opBuildIndex(c_opRecordPool),
+ c_opCreateEvent(c_opRecordPool),
+ c_opSubEvent(c_opRecordPool),
+ c_opDropEvent(c_opRecordPool),
+ c_opSignalUtil(c_opRecordPool),
+ c_opCreateTrigger(c_opRecordPool),
+ c_opDropTrigger(c_opRecordPool),
+ c_opAlterTrigger(c_opRecordPool),
+ c_opRecordSequence(0)
+{
+ BLOCK_CONSTRUCTOR(Dbdict);
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_TRIGGERS, &c_maxNoOfTriggers);
+ // Transit signals
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbdict::execDUMP_STATE_ORD);
+ addRecSignal(GSN_GET_TABINFOREQ, &Dbdict::execGET_TABINFOREQ);
+ addRecSignal(GSN_GET_TABLEID_REQ, &Dbdict::execGET_TABLEDID_REQ);
+ addRecSignal(GSN_GET_TABINFO_CONF, &Dbdict::execGET_TABINFO_CONF);
+ addRecSignal(GSN_CONTINUEB, &Dbdict::execCONTINUEB);
+
+ addRecSignal(GSN_CREATE_TABLE_REQ, &Dbdict::execCREATE_TABLE_REQ);
+ addRecSignal(GSN_CREATE_TAB_REQ, &Dbdict::execCREATE_TAB_REQ);
+ addRecSignal(GSN_CREATE_TAB_REF, &Dbdict::execCREATE_TAB_REF);
+ addRecSignal(GSN_CREATE_TAB_CONF, &Dbdict::execCREATE_TAB_CONF);
+ addRecSignal(GSN_CREATE_FRAGMENTATION_REF, &Dbdict::execCREATE_FRAGMENTATION_REF);
+ addRecSignal(GSN_CREATE_FRAGMENTATION_CONF, &Dbdict::execCREATE_FRAGMENTATION_CONF);
+ addRecSignal(GSN_DIADDTABCONF, &Dbdict::execDIADDTABCONF);
+ addRecSignal(GSN_DIADDTABREF, &Dbdict::execDIADDTABREF);
+ addRecSignal(GSN_ADD_FRAGREQ, &Dbdict::execADD_FRAGREQ);
+ addRecSignal(GSN_TAB_COMMITCONF, &Dbdict::execTAB_COMMITCONF);
+ addRecSignal(GSN_TAB_COMMITREF, &Dbdict::execTAB_COMMITREF);
+ addRecSignal(GSN_ALTER_TABLE_REQ, &Dbdict::execALTER_TABLE_REQ);
+ addRecSignal(GSN_ALTER_TAB_REQ, &Dbdict::execALTER_TAB_REQ);
+ addRecSignal(GSN_ALTER_TAB_REF, &Dbdict::execALTER_TAB_REF);
+ addRecSignal(GSN_ALTER_TAB_CONF, &Dbdict::execALTER_TAB_CONF);
+
+ // Index signals
+ addRecSignal(GSN_CREATE_INDX_REQ, &Dbdict::execCREATE_INDX_REQ);
+ addRecSignal(GSN_CREATE_INDX_CONF, &Dbdict::execCREATE_INDX_CONF);
+ addRecSignal(GSN_CREATE_INDX_REF, &Dbdict::execCREATE_INDX_REF);
+
+ addRecSignal(GSN_ALTER_INDX_REQ, &Dbdict::execALTER_INDX_REQ);
+ addRecSignal(GSN_ALTER_INDX_CONF, &Dbdict::execALTER_INDX_CONF);
+ addRecSignal(GSN_ALTER_INDX_REF, &Dbdict::execALTER_INDX_REF);
+
+ addRecSignal(GSN_CREATE_TABLE_CONF, &Dbdict::execCREATE_TABLE_CONF);
+ addRecSignal(GSN_CREATE_TABLE_REF, &Dbdict::execCREATE_TABLE_REF);
+
+ addRecSignal(GSN_DROP_INDX_REQ, &Dbdict::execDROP_INDX_REQ);
+ addRecSignal(GSN_DROP_INDX_CONF, &Dbdict::execDROP_INDX_CONF);
+ addRecSignal(GSN_DROP_INDX_REF, &Dbdict::execDROP_INDX_REF);
+
+ addRecSignal(GSN_DROP_TABLE_CONF, &Dbdict::execDROP_TABLE_CONF);
+ addRecSignal(GSN_DROP_TABLE_REF, &Dbdict::execDROP_TABLE_REF);
+
+ addRecSignal(GSN_BUILDINDXREQ, &Dbdict::execBUILDINDXREQ);
+ addRecSignal(GSN_BUILDINDXCONF, &Dbdict::execBUILDINDXCONF);
+ addRecSignal(GSN_BUILDINDXREF, &Dbdict::execBUILDINDXREF);
+
+ // Util signals
+ addRecSignal(GSN_UTIL_PREPARE_CONF, &Dbdict::execUTIL_PREPARE_CONF);
+ addRecSignal(GSN_UTIL_PREPARE_REF, &Dbdict::execUTIL_PREPARE_REF);
+
+ addRecSignal(GSN_UTIL_EXECUTE_CONF, &Dbdict::execUTIL_EXECUTE_CONF);
+ addRecSignal(GSN_UTIL_EXECUTE_REF, &Dbdict::execUTIL_EXECUTE_REF);
+
+ addRecSignal(GSN_UTIL_RELEASE_CONF, &Dbdict::execUTIL_RELEASE_CONF);
+ addRecSignal(GSN_UTIL_RELEASE_REF, &Dbdict::execUTIL_RELEASE_REF);
+
+ // Event signals
+ addRecSignal(GSN_CREATE_EVNT_REQ, &Dbdict::execCREATE_EVNT_REQ);
+ addRecSignal(GSN_CREATE_EVNT_CONF, &Dbdict::execCREATE_EVNT_CONF);
+ addRecSignal(GSN_CREATE_EVNT_REF, &Dbdict::execCREATE_EVNT_REF);
+
+ addRecSignal(GSN_CREATE_SUBID_CONF, &Dbdict::execCREATE_SUBID_CONF);
+ addRecSignal(GSN_CREATE_SUBID_REF, &Dbdict::execCREATE_SUBID_REF);
+
+ addRecSignal(GSN_SUB_CREATE_CONF, &Dbdict::execSUB_CREATE_CONF);
+ addRecSignal(GSN_SUB_CREATE_REF, &Dbdict::execSUB_CREATE_REF);
+
+ addRecSignal(GSN_SUB_START_REQ, &Dbdict::execSUB_START_REQ);
+ addRecSignal(GSN_SUB_START_CONF, &Dbdict::execSUB_START_CONF);
+ addRecSignal(GSN_SUB_START_REF, &Dbdict::execSUB_START_REF);
+
+ addRecSignal(GSN_SUB_STOP_REQ, &Dbdict::execSUB_STOP_REQ);
+ addRecSignal(GSN_SUB_STOP_CONF, &Dbdict::execSUB_STOP_CONF);
+ addRecSignal(GSN_SUB_STOP_REF, &Dbdict::execSUB_STOP_REF);
+
+ addRecSignal(GSN_SUB_SYNC_CONF, &Dbdict::execSUB_SYNC_CONF);
+ addRecSignal(GSN_SUB_SYNC_REF, &Dbdict::execSUB_SYNC_REF);
+
+ addRecSignal(GSN_DROP_EVNT_REQ, &Dbdict::execDROP_EVNT_REQ);
+
+ addRecSignal(GSN_SUB_REMOVE_REQ, &Dbdict::execSUB_REMOVE_REQ);
+ addRecSignal(GSN_SUB_REMOVE_CONF, &Dbdict::execSUB_REMOVE_CONF);
+ addRecSignal(GSN_SUB_REMOVE_REF, &Dbdict::execSUB_REMOVE_REF);
+
+ // Trigger signals
+ addRecSignal(GSN_CREATE_TRIG_REQ, &Dbdict::execCREATE_TRIG_REQ);
+ addRecSignal(GSN_CREATE_TRIG_CONF, &Dbdict::execCREATE_TRIG_CONF);
+ addRecSignal(GSN_CREATE_TRIG_REF, &Dbdict::execCREATE_TRIG_REF);
+ addRecSignal(GSN_ALTER_TRIG_REQ, &Dbdict::execALTER_TRIG_REQ);
+ addRecSignal(GSN_ALTER_TRIG_CONF, &Dbdict::execALTER_TRIG_CONF);
+ addRecSignal(GSN_ALTER_TRIG_REF, &Dbdict::execALTER_TRIG_REF);
+ addRecSignal(GSN_DROP_TRIG_REQ, &Dbdict::execDROP_TRIG_REQ);
+ addRecSignal(GSN_DROP_TRIG_CONF, &Dbdict::execDROP_TRIG_CONF);
+ addRecSignal(GSN_DROP_TRIG_REF, &Dbdict::execDROP_TRIG_REF);
+
+ // Received signals
+ addRecSignal(GSN_HOT_SPAREREP, &Dbdict::execHOT_SPAREREP);
+ addRecSignal(GSN_GET_SCHEMA_INFOREQ, &Dbdict::execGET_SCHEMA_INFOREQ);
+ addRecSignal(GSN_SCHEMA_INFO, &Dbdict::execSCHEMA_INFO);
+ addRecSignal(GSN_SCHEMA_INFOCONF, &Dbdict::execSCHEMA_INFOCONF);
+ addRecSignal(GSN_DICTSTARTREQ, &Dbdict::execDICTSTARTREQ);
+ addRecSignal(GSN_READ_NODESCONF, &Dbdict::execREAD_NODESCONF);
+ addRecSignal(GSN_FSOPENCONF, &Dbdict::execFSOPENCONF);
+ addRecSignal(GSN_FSOPENREF, &Dbdict::execFSOPENREF);
+ addRecSignal(GSN_FSCLOSECONF, &Dbdict::execFSCLOSECONF);
+ addRecSignal(GSN_FSCLOSEREF, &Dbdict::execFSCLOSEREF);
+ addRecSignal(GSN_FSWRITECONF, &Dbdict::execFSWRITECONF);
+ addRecSignal(GSN_FSWRITEREF, &Dbdict::execFSWRITEREF);
+ addRecSignal(GSN_FSREADCONF, &Dbdict::execFSREADCONF);
+ addRecSignal(GSN_FSREADREF, &Dbdict::execFSREADREF);
+ addRecSignal(GSN_LQHFRAGCONF, &Dbdict::execLQHFRAGCONF);
+ addRecSignal(GSN_LQHADDATTCONF, &Dbdict::execLQHADDATTCONF);
+ addRecSignal(GSN_LQHADDATTREF, &Dbdict::execLQHADDATTREF);
+ addRecSignal(GSN_LQHFRAGREF, &Dbdict::execLQHFRAGREF);
+ addRecSignal(GSN_NDB_STTOR, &Dbdict::execNDB_STTOR);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbdict::execREAD_CONFIG_REQ, true);
+ addRecSignal(GSN_STTOR, &Dbdict::execSTTOR);
+ addRecSignal(GSN_TC_SCHVERCONF, &Dbdict::execTC_SCHVERCONF);
+ addRecSignal(GSN_NODE_FAILREP, &Dbdict::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Dbdict::execINCL_NODEREQ);
+ addRecSignal(GSN_API_FAILREQ, &Dbdict::execAPI_FAILREQ);
+
+ addRecSignal(GSN_WAIT_GCP_REF, &Dbdict::execWAIT_GCP_REF);
+ addRecSignal(GSN_WAIT_GCP_CONF, &Dbdict::execWAIT_GCP_CONF);
+
+ addRecSignal(GSN_LIST_TABLES_REQ, &Dbdict::execLIST_TABLES_REQ);
+
+ addRecSignal(GSN_DROP_TABLE_REQ, &Dbdict::execDROP_TABLE_REQ);
+
+ addRecSignal(GSN_PREP_DROP_TAB_REQ, &Dbdict::execPREP_DROP_TAB_REQ);
+ addRecSignal(GSN_PREP_DROP_TAB_REF, &Dbdict::execPREP_DROP_TAB_REF);
+ addRecSignal(GSN_PREP_DROP_TAB_CONF, &Dbdict::execPREP_DROP_TAB_CONF);
+
+ addRecSignal(GSN_DROP_TAB_REQ, &Dbdict::execDROP_TAB_REQ);
+ addRecSignal(GSN_DROP_TAB_REF, &Dbdict::execDROP_TAB_REF);
+ addRecSignal(GSN_DROP_TAB_CONF, &Dbdict::execDROP_TAB_CONF);
+}//Dbdict::Dbdict()
+
+Dbdict::~Dbdict()
+{
+}//Dbdict::~Dbdict()
+
+BLOCK_FUNCTIONS(Dbdict)
+
+void Dbdict::initCommonData()
+{
+/* ---------------------------------------------------------------- */
+// Initialise all common variables.
+/* ---------------------------------------------------------------- */
+ initRetrieveRecord(0, 0, 0);
+ initSchemaRecord();
+ initRestartRecord();
+ initSendSchemaRecord();
+ initReadTableRecord();
+ initWriteTableRecord();
+ initReadSchemaRecord();
+ initWriteSchemaRecord();
+
+ c_masterNodeId = ZNIL;
+ c_numberNode = 0;
+ c_noNodesFailed = 0;
+ c_failureNr = 0;
+ c_blockState = BS_IDLE;
+ c_packTable.m_state = PackTable::PTS_IDLE;
+ c_startPhase = 0;
+ c_restartType = 255; //Ensure not used restartType
+ c_tabinfoReceived = 0;
+ c_initialStart = false;
+ c_systemRestart = false;
+ c_initialNodeRestart = false;
+ c_nodeRestart = false;
+}//Dbdict::initCommonData()
+
+void Dbdict::initRecords()
+{
+ initNodeRecords();
+ initPageRecords();
+ initTableRecords();
+ initTriggerRecords();
+}//Dbdict::initRecords()
+
+void Dbdict::initSendSchemaRecord()
+{
+ c_sendSchemaRecord.noOfWords = (Uint32)-1;
+ c_sendSchemaRecord.pageId = RNIL;
+ c_sendSchemaRecord.noOfWordsCurrentlySent = 0;
+ c_sendSchemaRecord.noOfSignalsSentSinceDelay = 0;
+ c_sendSchemaRecord.inUse = false;
+ //c_sendSchemaRecord.sendSchemaState = SendSchemaRecord::IDLE;
+}//initSendSchemaRecord()
+
+void Dbdict::initReadTableRecord()
+{
+ c_readTableRecord.noOfPages = (Uint32)-1;
+ c_readTableRecord.pageId = RNIL;
+ c_readTableRecord.tableId = ZNIL;
+ c_readTableRecord.inUse = false;
+}//initReadTableRecord()
+
+void Dbdict::initWriteTableRecord()
+{
+ c_writeTableRecord.noOfPages = (Uint32)-1;
+ c_writeTableRecord.pageId = RNIL;
+ c_writeTableRecord.noOfTableFilesHandled = 3;
+ c_writeTableRecord.tableId = ZNIL;
+ c_writeTableRecord.tableWriteState = WriteTableRecord::IDLE;
+}//initWriteTableRecord()
+
+void Dbdict::initReadSchemaRecord()
+{
+ c_readSchemaRecord.pageId = RNIL;
+ c_readSchemaRecord.schemaReadState = ReadSchemaRecord::IDLE;
+}//initReadSchemaRecord()
+
+void Dbdict::initWriteSchemaRecord()
+{
+ c_writeSchemaRecord.inUse = false;
+ c_writeSchemaRecord.pageId = RNIL;
+ c_writeSchemaRecord.noOfSchemaFilesHandled = 3;
+}//initWriteSchemaRecord()
+
+void Dbdict::initRetrieveRecord(Signal* signal, Uint32 i, Uint32 returnCode)
+{
+ c_retrieveRecord.busyState = false;
+ c_retrieveRecord.blockRef = 0;
+ c_retrieveRecord.m_senderData = RNIL;
+ c_retrieveRecord.tableId = RNIL;
+ c_retrieveRecord.currentSent = 0;
+ c_retrieveRecord.retrievedNoOfPages = 0;
+ c_retrieveRecord.retrievedNoOfWords = 0;
+ c_retrieveRecord.m_useLongSig = false;
+}//initRetrieveRecord()
+
+void Dbdict::initSchemaRecord()
+{
+ c_schemaRecord.schemaPage = RNIL;
+}//Dbdict::initSchemaRecord()
+
+void Dbdict::initRestartRecord()
+{
+ c_restartRecord.gciToRestart = 0;
+ c_restartRecord.activeTable = ZNIL;
+}//Dbdict::initRestartRecord()
+
+void Dbdict::initNodeRecords()
+{
+ jam();
+ for (unsigned i = 1; i < MAX_NODES; i++) {
+ NodeRecordPtr nodePtr;
+ c_nodes.getPtr(nodePtr, i);
+ nodePtr.p->hotSpare = false;
+ nodePtr.p->nodeState = NodeRecord::API_NODE;
+ }//for
+}//Dbdict::initNodeRecords()
+
+void Dbdict::initPageRecords()
+{
+ c_schemaRecord.schemaPage = ZMAX_PAGES_OF_TABLE_DEFINITION;
+ c_schemaRecord.oldSchemaPage = ZMAX_PAGES_OF_TABLE_DEFINITION + 1;
+ c_retrieveRecord.retrievePage = ZMAX_PAGES_OF_TABLE_DEFINITION + 2;
+ ndbrequire(ZNUMBER_OF_PAGES >= (2 * ZMAX_PAGES_OF_TABLE_DEFINITION + 2));
+}//Dbdict::initPageRecords()
+
+void Dbdict::initTableRecords()
+{
+ TableRecordPtr tablePtr;
+ while (1) {
+ jam();
+ refresh_watch_dog();
+ c_tableRecordPool.seize(tablePtr);
+ if (tablePtr.i == RNIL) {
+ jam();
+ break;
+ }//if
+ initialiseTableRecord(tablePtr);
+ }//while
+}//Dbdict::initTableRecords()
+
+void Dbdict::initialiseTableRecord(TableRecordPtr tablePtr)
+{
+ tablePtr.p->activePage = RNIL;
+ tablePtr.p->filePtr[0] = RNIL;
+ tablePtr.p->filePtr[1] = RNIL;
+ tablePtr.p->firstAttribute = RNIL;
+ tablePtr.p->firstPage = RNIL;
+ tablePtr.p->lastAttribute = RNIL;
+ tablePtr.p->tableId = tablePtr.i;
+ tablePtr.p->tableVersion = (Uint32)-1;
+ tablePtr.p->tabState = TableRecord::NOT_DEFINED;
+ tablePtr.p->tabReturnState = TableRecord::TRS_IDLE;
+ tablePtr.p->myConnect = RNIL;
+ tablePtr.p->fragmentType = DictTabInfo::AllNodesSmallTable;
+ memset(tablePtr.p->tableName, 0, sizeof(tablePtr.p->tableName));
+ tablePtr.p->gciTableCreated = 0;
+ tablePtr.p->noOfAttributes = ZNIL;
+ tablePtr.p->noOfNullAttr = 0;
+ tablePtr.p->frmLen = 0;
+ memset(tablePtr.p->frmData, 0, sizeof(tablePtr.p->frmData));
+ /*
+ tablePtr.p->lh3PageIndexBits = 0;
+ tablePtr.p->lh3DistrBits = 0;
+ tablePtr.p->lh3PageBits = 6;
+ */
+ tablePtr.p->kValue = 6;
+ tablePtr.p->localKeyLen = 1;
+ tablePtr.p->maxLoadFactor = 80;
+ tablePtr.p->minLoadFactor = 70;
+ tablePtr.p->noOfPrimkey = 1;
+ tablePtr.p->tupKeyLength = 1;
+ tablePtr.p->storedTable = true;
+ tablePtr.p->tableType = DictTabInfo::UserTable;
+ tablePtr.p->primaryTableId = RNIL;
+ // volatile elements
+ tablePtr.p->indexState = TableRecord::IS_UNDEFINED;
+ tablePtr.p->insertTriggerId = RNIL;
+ tablePtr.p->updateTriggerId = RNIL;
+ tablePtr.p->deleteTriggerId = RNIL;
+ tablePtr.p->customTriggerId = RNIL;
+ tablePtr.p->buildTriggerId = RNIL;
+ tablePtr.p->indexLocal = 0;
+}//Dbdict::initialiseTableRecord()
+
+void Dbdict::initTriggerRecords()
+{
+ TriggerRecordPtr triggerPtr;
+ while (1) {
+ jam();
+ refresh_watch_dog();
+ c_triggerRecordPool.seize(triggerPtr);
+ if (triggerPtr.i == RNIL) {
+ jam();
+ break;
+ }//if
+ initialiseTriggerRecord(triggerPtr);
+ }//while
+}
+
+void Dbdict::initialiseTriggerRecord(TriggerRecordPtr triggerPtr)
+{
+ triggerPtr.p->triggerState = TriggerRecord::TS_NOT_DEFINED;
+ triggerPtr.p->triggerLocal = 0;
+ memset(triggerPtr.p->triggerName, 0, sizeof(triggerPtr.p->triggerName));
+ triggerPtr.p->triggerId = RNIL;
+ triggerPtr.p->tableId = RNIL;
+ triggerPtr.p->triggerType = (TriggerType::Value)~0;
+ triggerPtr.p->triggerActionTime = (TriggerActionTime::Value)~0;
+ triggerPtr.p->triggerEvent = (TriggerEvent::Value)~0;
+ triggerPtr.p->monitorReplicas = false;
+ triggerPtr.p->monitorAllAttributes = false;
+ triggerPtr.p->attributeMask.clear();
+ triggerPtr.p->indexId = RNIL;
+}
+
+Uint32 Dbdict::getFsConnRecord()
+{
+ FsConnectRecordPtr fsPtr;
+ c_fsConnectRecordPool.seize(fsPtr);
+ ndbrequire(fsPtr.i != RNIL);
+ fsPtr.p->filePtr = (Uint32)-1;
+ fsPtr.p->ownerPtr = RNIL;
+ fsPtr.p->fsState = FsConnectRecord::IDLE;
+ return fsPtr.i;
+}//Dbdict::getFsConnRecord()
+
+Uint32 Dbdict::getFreeTableRecord(Uint32 primaryTableId)
+{
+ Uint32 minId = (primaryTableId == RNIL ? 0 : primaryTableId + 1);
+ TableRecordPtr tablePtr;
+ TableRecordPtr firstTablePtr;
+ bool firstFound = false;
+ Uint32 tabSize = c_tableRecordPool.getSize();
+ for (tablePtr.i = minId; tablePtr.i < tabSize ; tablePtr.i++) {
+ jam();
+ c_tableRecordPool.getPtr(tablePtr);
+ if (tablePtr.p->tabState == TableRecord::NOT_DEFINED) {
+ jam();
+ initialiseTableRecord(tablePtr);
+ tablePtr.p->tabState = TableRecord::DEFINING;
+ firstFound = true;
+ firstTablePtr.i = tablePtr.i;
+ firstTablePtr.p = tablePtr.p;
+ break;
+ }//if
+ }//for
+ if (!firstFound) {
+ jam();
+ return RNIL;
+ }//if
+#ifdef HAVE_TABLE_REORG
+ bool secondFound = false;
+ for (tablePtr.i = firstTablePtr.i + 1; tablePtr.i < tabSize ; tablePtr.i++) {
+ jam();
+ c_tableRecordPool.getPtr(tablePtr);
+ if (tablePtr.p->tabState == TableRecord::NOT_DEFINED) {
+ jam();
+ initialiseTableRecord(tablePtr);
+ tablePtr.p->tabState = TableRecord::REORG_TABLE_PREPARED;
+ tablePtr.p->secondTable = firstTablePtr.i;
+ firstTablePtr.p->secondTable = tablePtr.i;
+ secondFound = true;
+ break;
+ }//if
+ }//for
+ if (!secondFound) {
+ jam();
+ firstTablePtr.p->tabState = TableRecord::NOT_DEFINED;
+ return RNIL;
+ }//if
+#endif
+ return firstTablePtr.i;
+}//Dbdict::getFreeTableRecord()
+
+Uint32 Dbdict::getFreeTriggerRecord()
+{
+ const Uint32 size = c_triggerRecordPool.getSize();
+ TriggerRecordPtr triggerPtr;
+ for (triggerPtr.i = 0; triggerPtr.i < size; triggerPtr.i++) {
+ jam();
+ c_triggerRecordPool.getPtr(triggerPtr);
+ if (triggerPtr.p->triggerState == TriggerRecord::TS_NOT_DEFINED) {
+ jam();
+ initialiseTriggerRecord(triggerPtr);
+ return triggerPtr.i;
+ }
+ }
+ return RNIL;
+}
+
+bool
+Dbdict::getNewAttributeRecord(TableRecordPtr tablePtr,
+ AttributeRecordPtr & attrPtr)
+{
+ c_attributeRecordPool.seize(attrPtr);
+ if(attrPtr.i == RNIL){
+ return false;
+ }
+
+ memset(attrPtr.p->attributeName, 0, sizeof(attrPtr.p->attributeName));
+ attrPtr.p->attributeDescriptor = 0x00012255; //Default value
+ attrPtr.p->attributeId = ZNIL;
+ attrPtr.p->nextAttrInTable = RNIL;
+ attrPtr.p->tupleKey = 0;
+ memset(attrPtr.p->defaultValue, 0, sizeof(attrPtr.p->defaultValue));
+
+ /* ---------------------------------------------------------------- */
+ // A free attribute record has been acquired. We will now link it
+ // to the table record.
+ /* ---------------------------------------------------------------- */
+ if (tablePtr.p->lastAttribute == RNIL) {
+ jam();
+ tablePtr.p->firstAttribute = attrPtr.i;
+ } else {
+ jam();
+ AttributeRecordPtr lastAttrPtr;
+ c_attributeRecordPool.getPtr(lastAttrPtr, tablePtr.p->lastAttribute);
+ lastAttrPtr.p->nextAttrInTable = attrPtr.i;
+ }//if
+ tablePtr.p->lastAttribute = attrPtr.i;
+ return true;
+}//Dbdict::getNewAttributeRecord()
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: START/RESTART HANDLING ------------------------ */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains the code that is common for all */
+/* start/restart types. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+/* ---------------------------------------------------------------- */
+// This is sent as the first signal during start/restart.
+/* ---------------------------------------------------------------- */
+void Dbdict::execSTTOR(Signal* signal)
+{
+ jamEntry();
+ c_startPhase = signal->theData[1];
+ switch (c_startPhase) {
+ case 1:
+ break;
+ case 3:
+ c_restartType = signal->theData[7]; /* valid if 3 */
+ ndbrequire(c_restartType == NodeState::ST_INITIAL_START ||
+ c_restartType == NodeState::ST_SYSTEM_RESTART ||
+ c_restartType == NodeState::ST_INITIAL_NODE_RESTART ||
+ c_restartType == NodeState::ST_NODE_RESTART);
+ break;
+ }
+ sendSTTORRY(signal);
+}//execSTTOR()
+
+void Dbdict::sendSTTORRY(Signal* signal)
+{
+ signal->theData[0] = 0; /* garbage SIGNAL KEY */
+ signal->theData[1] = 0; /* garbage SIGNAL VERSION NUMBER */
+ signal->theData[2] = 0; /* garbage */
+ signal->theData[3] = 1; /* first wanted start phase */
+ signal->theData[4] = 3; /* get type of start */
+ signal->theData[5] = ZNOMOREPHASES;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 6, JBB);
+}
+
+/* ---------------------------------------------------------------- */
+// We receive information about sizes of records.
+/* ---------------------------------------------------------------- */
+void Dbdict::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ jamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ Uint32 attributesize, tablerecSize;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DICT_ATTRIBUTE,&attributesize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DICT_TABLE, &tablerecSize));
+
+ c_attributeRecordPool.setSize(attributesize);
+ c_attributeRecordHash.setSize(64);
+ c_fsConnectRecordPool.setSize(ZFS_CONNECT_SIZE);
+ c_nodes.setSize(MAX_NODES);
+ c_pageRecordArray.setSize(ZNUMBER_OF_PAGES);
+ c_tableRecordPool.setSize(tablerecSize);
+ c_tableRecordHash.setSize(tablerecSize);
+ c_triggerRecordPool.setSize(c_maxNoOfTriggers);
+ c_triggerRecordHash.setSize(c_maxNoOfTriggers);
+ c_opRecordPool.setSize(256); // XXX need config params
+ c_opCreateTable.setSize(8);
+ c_opDropTable.setSize(8);
+ c_opCreateIndex.setSize(8);
+ c_opCreateEvent.setSize(8);
+ c_opSubEvent.setSize(8);
+ c_opDropEvent.setSize(8);
+ c_opSignalUtil.setSize(8);
+ c_opDropIndex.setSize(8);
+ c_opAlterIndex.setSize(8);
+ c_opBuildIndex.setSize(8);
+ c_opCreateTrigger.setSize(8);
+ c_opDropTrigger.setSize(8);
+ c_opAlterTrigger.setSize(8);
+
+ // Initialize BAT for interface to file system
+ PageRecordPtr pageRecPtr;
+ c_pageRecordArray.getPtr(pageRecPtr, 0);
+ NewVARIABLE* bat = allocateBat(2);
+ bat[1].WA = &pageRecPtr.p->word[0];
+ bat[1].nrr = ZNUMBER_OF_PAGES;
+ bat[1].ClusterSize = ZSIZE_OF_PAGES_IN_WORDS * 4;
+ bat[1].bits.q = ZLOG_SIZE_OF_PAGES_IN_WORDS; // 2**13 = 8192 elements
+ bat[1].bits.v = 5; // 32 bits per element
+
+ initCommonData();
+ initRecords();
+
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ sendSignal(ref, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+}//execSIZEALT_REP()
+
+/* ---------------------------------------------------------------- */
+// Start phase signals sent by CNTR. We reply with NDB_STTORRY when
+// we completed this phase.
+/* ---------------------------------------------------------------- */
+void Dbdict::execNDB_STTOR(Signal* signal)
+{
+ jamEntry();
+ c_startPhase = signal->theData[2];
+ const Uint32 restartType = signal->theData[3];
+ if (restartType == NodeState::ST_INITIAL_START) {
+ jam();
+ c_initialStart = true;
+ } else if (restartType == NodeState::ST_SYSTEM_RESTART) {
+ jam();
+ c_systemRestart = true;
+ } else if (restartType == NodeState::ST_INITIAL_NODE_RESTART) {
+ jam();
+ c_initialNodeRestart = true;
+ } else if (restartType == NodeState::ST_NODE_RESTART) {
+ jam();
+ c_nodeRestart = true;
+ } else {
+ ndbrequire(false);
+ }//if
+ switch (c_startPhase) {
+ case 1:
+ jam();
+ initSchemaFile(signal);
+ break;
+ case 3:
+ jam();
+ signal->theData[0] = reference();
+ sendSignal(NDBCNTR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+ break;
+ case 6:
+ jam();
+ c_initialStart = false;
+ c_systemRestart = false;
+ c_initialNodeRestart = false;
+ c_nodeRestart = false;
+ sendNDB_STTORRY(signal);
+ break;
+ case 7:
+ // uses c_restartType
+ if(restartType == NodeState::ST_SYSTEM_RESTART &&
+ c_masterNodeId == getOwnNodeId()){
+ rebuildIndexes(signal, 0);
+ return;
+ }
+ sendNDB_STTORRY(signal);
+ break;
+ default:
+ jam();
+ sendNDB_STTORRY(signal);
+ break;
+ }//switch
+}//execNDB_STTOR()
+
+void Dbdict::sendNDB_STTORRY(Signal* signal)
+{
+ signal->theData[0] = reference();
+ sendSignal(NDBCNTR_REF, GSN_NDB_STTORRY, signal, 1, JBB);
+ return;
+}//sendNDB_STTORRY()
+
+/* ---------------------------------------------------------------- */
+// We receive the information about which nodes that are up and down.
+/* ---------------------------------------------------------------- */
+void Dbdict::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+
+ ReadNodesConf * const readNodes = (ReadNodesConf *)&signal->theData[0];
+ c_numberNode = readNodes->noOfNodes;
+ c_masterNodeId = readNodes->masterNodeId;
+
+ c_noNodesFailed = 0;
+ c_aliveNodes.clear();
+ for (unsigned i = 1; i < MAX_NDB_NODES; i++) {
+ jam();
+ NodeRecordPtr nodePtr;
+ c_nodes.getPtr(nodePtr, i);
+
+ if (NodeBitmask::get(readNodes->allNodes, i)) {
+ jam();
+ nodePtr.p->nodeState = NodeRecord::NDB_NODE_ALIVE;
+ if (NodeBitmask::get(readNodes->inactiveNodes, i)) {
+ jam();
+ /**-------------------------------------------------------------------
+ *
+ * THIS NODE IS DEFINED IN THE CLUSTER BUT IS NOT ALIVE CURRENTLY.
+ * WE ADD THE NODE TO THE SET OF FAILED NODES AND ALSO SET THE
+ * BLOCKSTATE TO BUSY TO AVOID ADDING TABLES WHILE NOT ALL NODES ARE
+ * ALIVE.
+ *------------------------------------------------------------------*/
+ nodePtr.p->nodeState = NodeRecord::NDB_NODE_DEAD;
+ c_noNodesFailed++;
+ } else {
+ c_aliveNodes.set(i);
+ }
+ }//if
+ }//for
+ sendNDB_STTORRY(signal);
+}//execREAD_NODESCONF()
+
+/* ---------------------------------------------------------------- */
+// HOT_SPAREREP informs DBDICT about which nodes that have become
+// hot spare nodes.
+/* ---------------------------------------------------------------- */
+void Dbdict::execHOT_SPAREREP(Signal* signal)
+{
+ Uint32 hotSpareNodes = 0;
+ jamEntry();
+ HotSpareRep * const hotSpare = (HotSpareRep*)&signal->theData[0];
+ for (unsigned i = 1; i < MAX_NDB_NODES; i++) {
+ if (NodeBitmask::get(hotSpare->theHotSpareNodes, i)) {
+ NodeRecordPtr nodePtr;
+ c_nodes.getPtr(nodePtr, i);
+ nodePtr.p->hotSpare = true;
+ hotSpareNodes++;
+ }//if
+ }//for
+ ndbrequire(hotSpareNodes == hotSpare->noHotSpareNodes);
+ c_noHotSpareNodes = hotSpareNodes;
+ return;
+}//execHOT_SPAREREP()
+
+void Dbdict::initSchemaFile(Signal* signal)
+{
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+ SchemaFile * schemaFile = (SchemaFile *)pagePtr.p;
+ initSchemaFile(schemaFile, 4 * ZSIZE_OF_PAGES_IN_WORDS);
+
+ if (c_initialStart || c_initialNodeRestart) {
+ jam();
+ ndbrequire(c_writeSchemaRecord.inUse == false);
+ c_writeSchemaRecord.inUse = true;
+ c_writeSchemaRecord.pageId = c_schemaRecord.schemaPage;
+
+ c_writeSchemaRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::initSchemaFile_conf);
+
+ startWriteSchemaFile(signal);
+ } else if (c_systemRestart || c_nodeRestart) {
+ jam();
+ ndbrequire(c_readSchemaRecord.schemaReadState == ReadSchemaRecord::IDLE);
+ c_readSchemaRecord.pageId = c_schemaRecord.oldSchemaPage;
+ c_readSchemaRecord.schemaReadState = ReadSchemaRecord::INITIAL_READ;
+ startReadSchemaFile(signal);
+ } else {
+ ndbrequire(false);
+ }//if
+}//Dbdict::initSchemaFile()
+
+void
+Dbdict::initSchemaFile_conf(Signal* signal, Uint32 callbackData, Uint32 rv){
+ jam();
+ sendNDB_STTORRY(signal);
+}
+
+void
+Dbdict::activateIndexes(Signal* signal, Uint32 i)
+{
+ AlterIndxReq* req = (AlterIndxReq*)signal->getDataPtrSend();
+ TableRecordPtr tablePtr;
+ for (; i < c_tableRecordPool.getSize(); i++) {
+ tablePtr.i = i;
+ c_tableRecordPool.getPtr(tablePtr);
+ if (tablePtr.p->tabState != TableRecord::DEFINED)
+ continue;
+ if (! tablePtr.p->isIndex())
+ continue;
+ jam();
+ req->setUserRef(reference());
+ req->setConnectionPtr(i);
+ req->setTableId(tablePtr.p->primaryTableId);
+ req->setIndexId(tablePtr.i);
+ req->setIndexVersion(tablePtr.p->tableVersion);
+ req->setOnline(true);
+ if (c_restartType == NodeState::ST_SYSTEM_RESTART) {
+ if (c_masterNodeId != getOwnNodeId())
+ continue;
+ // from file index state is not defined currently
+ req->setRequestType(AlterIndxReq::RT_SYSTEMRESTART);
+ req->addRequestFlag((Uint32)RequestFlag::RF_NOBUILD);
+ }
+ else if (
+ c_restartType == NodeState::ST_NODE_RESTART ||
+ c_restartType == NodeState::ST_INITIAL_NODE_RESTART) {
+ // from master index must be online
+ if (tablePtr.p->indexState != TableRecord::IS_ONLINE)
+ continue;
+ req->setRequestType(AlterIndxReq::RT_NODERESTART);
+ // activate locally, rebuild not needed
+ req->addRequestFlag((Uint32)RequestFlag::RF_LOCAL);
+ req->addRequestFlag((Uint32)RequestFlag::RF_NOBUILD);
+ } else {
+ ndbrequire(false);
+ }
+ sendSignal(reference(), GSN_ALTER_INDX_REQ,
+ signal, AlterIndxReq::SignalLength, JBB);
+ return;
+ }
+ signal->theData[0] = reference();
+ sendSignal(c_restartRecord.returnBlockRef, GSN_DICTSTARTCONF,
+ signal, 1, JBB);
+}
+
+void
+Dbdict::rebuildIndexes(Signal* signal, Uint32 i){
+ BuildIndxReq* const req = (BuildIndxReq*)signal->getDataPtrSend();
+
+ TableRecordPtr indexPtr;
+ for (; i < c_tableRecordPool.getSize(); i++) {
+ indexPtr.i = i;
+ c_tableRecordPool.getPtr(indexPtr);
+ if (indexPtr.p->tabState != TableRecord::DEFINED)
+ continue;
+ if (! indexPtr.p->isIndex())
+ continue;
+
+ jam();
+
+ req->setUserRef(reference());
+ req->setConnectionPtr(i);
+ req->setRequestType(BuildIndxReq::RT_SYSTEMRESTART);
+ req->setBuildId(0); // not used
+ req->setBuildKey(0); // not used
+ req->setIndexType(indexPtr.p->tableType);
+ req->setIndexId(indexPtr.i);
+ req->setTableId(indexPtr.p->primaryTableId);
+ req->setParallelism(16);
+
+ // from file index state is not defined currently
+ if (indexPtr.p->storedTable) {
+ // rebuild not needed
+ req->addRequestFlag((Uint32)RequestFlag::RF_NOBUILD);
+ }
+
+ // send
+ sendSignal(reference(), GSN_BUILDINDXREQ,
+ signal, BuildIndxReq::SignalLength, JBB);
+ return;
+ }
+ sendNDB_STTORRY(signal);
+}
+
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: SYSTEM RESTART MODULE ------------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains code specific for system restart */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+/* ---------------------------------------------------------------- */
+// DIH asks DICT to read in table data from disk during system
+// restart. DIH also asks DICT to send information about which
+// tables that should be started as part of this system restart.
+// DICT will also activate the tables in TC as part of this process.
+/* ---------------------------------------------------------------- */
+void Dbdict::execDICTSTARTREQ(Signal* signal)
+{
+ jamEntry();
+ c_restartRecord.gciToRestart = signal->theData[0];
+ c_restartRecord.returnBlockRef = signal->theData[1];
+ if (c_nodeRestart || c_initialNodeRestart) {
+ jam();
+
+ CRASH_INSERTION(6000);
+
+ BlockReference dictRef = calcDictBlockRef(c_masterNodeId);
+ signal->theData[0] = getOwnNodeId();
+ sendSignal(dictRef, GSN_GET_SCHEMA_INFOREQ, signal, 1, JBB);
+ return;
+ }
+ ndbrequire(c_systemRestart);
+ ndbrequire(c_masterNodeId == getOwnNodeId());
+
+ c_schemaRecord.m_callback.m_callbackData = 0;
+ c_schemaRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::masterRestart_checkSchemaStatusComplete);
+
+ c_restartRecord.activeTable = 0;
+ c_schemaRecord.schemaPage = c_schemaRecord.oldSchemaPage;
+ checkSchemaStatus(signal);
+}//execDICTSTARTREQ()
+
+void
+Dbdict::masterRestart_checkSchemaStatusComplete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+
+ c_schemaRecord.schemaPage = ZMAX_PAGES_OF_TABLE_DEFINITION;
+
+ LinearSectionPtr ptr[3];
+
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.oldSchemaPage);
+
+ ptr[0].p = &pagePtr.p->word[0];
+ ptr[0].sz = ZSIZE_OF_PAGES_IN_WORDS;
+
+ c_sendSchemaRecord.m_SCHEMAINFO_Counter = c_aliveNodes;
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+
+ rg.m_nodes.clear(getOwnNodeId());
+ Callback c = { 0, 0 };
+ sendFragmentedSignal(rg,
+ GSN_SCHEMA_INFO,
+ signal,
+ 1, //SchemaInfo::SignalLength,
+ JBB,
+ ptr,
+ 1,
+ c);
+
+ PageRecordPtr newPagePtr;
+ c_pageRecordArray.getPtr(newPagePtr, c_schemaRecord.schemaPage);
+ memcpy(&newPagePtr.p->word[0], &pagePtr.p->word[0],
+ 4 * ZSIZE_OF_PAGES_IN_WORDS);
+
+ signal->theData[0] = getOwnNodeId();
+ sendSignal(reference(), GSN_SCHEMA_INFOCONF, signal, 1, JBB);
+}
+
+void
+Dbdict::execGET_SCHEMA_INFOREQ(Signal* signal){
+
+ const Uint32 ref = signal->getSendersBlockRef();
+ //const Uint32 senderData = signal->theData[0];
+
+ ndbrequire(c_sendSchemaRecord.inUse == false);
+ c_sendSchemaRecord.inUse = true;
+
+ LinearSectionPtr ptr[3];
+
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+
+ ptr[0].p = &pagePtr.p->word[0];
+ ptr[0].sz = ZSIZE_OF_PAGES_IN_WORDS;
+
+ Callback c = { safe_cast(&Dbdict::sendSchemaComplete), 0 };
+ sendFragmentedSignal(ref,
+ GSN_SCHEMA_INFO,
+ signal,
+ 1, //GetSchemaInfoConf::SignalLength,
+ JBB,
+ ptr,
+ 1,
+ c);
+}//Dbdict::execGET_SCHEMA_INFOREQ()
+
+void
+Dbdict::sendSchemaComplete(Signal * signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ ndbrequire(c_sendSchemaRecord.inUse == true);
+ c_sendSchemaRecord.inUse = false;
+
+}
+
+
+/* ---------------------------------------------------------------- */
+// We receive the schema info from master as part of all restarts
+// except the initial start where no tables exists.
+/* ---------------------------------------------------------------- */
+void Dbdict::execSCHEMA_INFO(Signal* signal)
+{
+ jamEntry();
+ if(!assembleFragments(signal)){
+ jam();
+ return;
+ }
+
+ if(getNodeState().getNodeRestartInProgress()){
+ CRASH_INSERTION(6001);
+ }
+
+ SegmentedSectionPtr schemaDataPtr;
+ signal->getSection(schemaDataPtr, 0);
+
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+ copy(&pagePtr.p->word[0], schemaDataPtr);
+ releaseSections(signal);
+
+ validateChecksum((SchemaFile*)pagePtr.p);
+
+ ndbrequire(signal->getSendersBlockRef() != reference());
+
+ /* ---------------------------------------------------------------- */
+ // Synchronise our view on data with other nodes in the cluster.
+ // This is an important part of restart handling where we will handle
+ // cases where the table have been added but only partially, where
+ // tables have been deleted but not completed the deletion yet and
+ // other scenarios needing synchronisation.
+ /* ---------------------------------------------------------------- */
+ c_schemaRecord.m_callback.m_callbackData = 0;
+ c_schemaRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::restart_checkSchemaStatusComplete);
+ c_restartRecord.activeTable = 0;
+ checkSchemaStatus(signal);
+}//execSCHEMA_INFO()
+
+void
+Dbdict::restart_checkSchemaStatusComplete(Signal * signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+
+ ndbrequire(c_writeSchemaRecord.inUse == false);
+ c_writeSchemaRecord.inUse = true;
+ c_writeSchemaRecord.pageId = c_schemaRecord.schemaPage;
+ c_writeSchemaRecord.m_callback.m_callbackData = 0;
+ c_writeSchemaRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::restart_writeSchemaConf);
+
+ startWriteSchemaFile(signal);
+}
+
+void
+Dbdict::restart_writeSchemaConf(Signal * signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+
+ if(c_systemRestart){
+ jam();
+ signal->theData[0] = getOwnNodeId();
+ sendSignal(calcDictBlockRef(c_masterNodeId), GSN_SCHEMA_INFOCONF,
+ signal, 1, JBB);
+ return;
+ }
+
+ ndbrequire(c_nodeRestart || c_initialNodeRestart);
+ c_blockState = BS_IDLE;
+ activateIndexes(signal, 0);
+ return;
+}
+
+void Dbdict::execSCHEMA_INFOCONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+
+/* ---------------------------------------------------------------- */
+// This signal is received in the master as part of system restart
+// from all nodes (including the master) after they have synchronised
+// their data with the master node's schema information.
+/* ---------------------------------------------------------------- */
+ const Uint32 nodeId = signal->theData[0];
+ c_sendSchemaRecord.m_SCHEMAINFO_Counter.clearWaitingFor(nodeId);
+
+ if (!c_sendSchemaRecord.m_SCHEMAINFO_Counter.done()){
+ jam();
+ return;
+ }//if
+ activateIndexes(signal, 0);
+}//execSCHEMA_INFOCONF()
+
+void Dbdict::checkSchemaStatus(Signal* signal)
+{
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+
+ PageRecordPtr oldPagePtr;
+ c_pageRecordArray.getPtr(oldPagePtr, c_schemaRecord.oldSchemaPage);
+
+ for (; c_restartRecord.activeTable < MAX_TABLES;
+ c_restartRecord.activeTable++) {
+ jam();
+
+ Uint32 tableId = c_restartRecord.activeTable;
+ SchemaFile::TableEntry *newEntry = getTableEntry(pagePtr.p, tableId);
+ SchemaFile::TableEntry *oldEntry = getTableEntry(oldPagePtr.p, tableId,
+ true);
+ SchemaFile::TableState schemaState =
+ (SchemaFile::TableState)newEntry->m_tableState;
+ SchemaFile::TableState oldSchemaState =
+ (SchemaFile::TableState)oldEntry->m_tableState;
+
+ if (c_restartRecord.activeTable >= c_tableRecordPool.getSize()) {
+ jam();
+ ndbrequire(schemaState == SchemaFile::INIT);
+ ndbrequire(oldSchemaState == SchemaFile::INIT);
+ continue;
+ }//if
+
+ switch(schemaState){
+ case SchemaFile::INIT:{
+ jam();
+ bool ok = false;
+ switch(oldSchemaState) {
+ case SchemaFile::INIT:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ jam();
+ break;
+
+ case SchemaFile::ADD_STARTED:
+ jam();
+ case SchemaFile::TABLE_ADD_COMMITTED:
+ jam();
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::ALTER_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ jam();
+ newEntry->m_tableState = SchemaFile::INIT;
+ restartDropTab(signal, tableId);
+ return;
+ }//switch
+ ndbrequire(ok);
+ break;
+ }
+ case SchemaFile::ADD_STARTED:{
+ jam();
+ bool ok = false;
+ switch(oldSchemaState) {
+ case SchemaFile::INIT:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ break;
+ case SchemaFile::ADD_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::TABLE_ADD_COMMITTED:
+ jam();
+ case SchemaFile::ALTER_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ //------------------------------------------------------------------
+ // Add Table was started but not completed. Will be dropped in all
+ // nodes. Update schema information (restore table version).
+ //------------------------------------------------------------------
+ newEntry->m_tableState = SchemaFile::INIT;
+ restartDropTab(signal, tableId);
+ return;
+ }
+ ndbrequire(ok);
+ break;
+ }
+ case SchemaFile::TABLE_ADD_COMMITTED:{
+ jam();
+ bool ok = false;
+ switch(oldSchemaState) {
+ case SchemaFile::INIT:
+ jam();
+ case SchemaFile::ADD_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ //------------------------------------------------------------------
+ // Table was added in the master node but not in our node. We can
+ // retrieve the table definition from the master.
+ //------------------------------------------------------------------
+ restartCreateTab(signal, tableId, oldEntry, false);
+ return;
+ break;
+ case SchemaFile::TABLE_ADD_COMMITTED:
+ jam();
+ case SchemaFile::ALTER_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ //------------------------------------------------------------------
+ // Table was added in both our node and the master node. We can
+ // retrieve the table definition from our own disk.
+ //------------------------------------------------------------------
+ if(* newEntry == * oldEntry){
+ jam();
+
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ tablePtr.p->tableVersion = oldEntry->m_tableVersion;
+ tablePtr.p->tableType = (DictTabInfo::TableType)oldEntry->m_tableType;
+
+ // On NR get index from master because index state is not on file
+ const bool file = c_systemRestart || tablePtr.p->isTable();
+ restartCreateTab(signal, tableId, oldEntry, file);
+
+ return;
+ } else {
+ //------------------------------------------------------------------
+ // Must be a new version of the table if anything differs. Both table
+ // version and global checkpoint must be different.
+ // This should not happen for the master node. This can happen after
+ // drop table followed by add table or after change table.
+ // Not supported in this version.
+ //------------------------------------------------------------------
+ ndbrequire(c_masterNodeId != getOwnNodeId());
+ ndbrequire(newEntry->m_tableVersion != oldEntry->m_tableVersion);
+ jam();
+
+ restartCreateTab(signal, tableId, oldEntry, false);
+ return;
+ }//if
+ }
+ ndbrequire(ok);
+ break;
+ }
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:{
+ jam();
+ bool ok = false;
+ switch(oldSchemaState){
+ case SchemaFile::INIT:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:
+ jam();
+ ok = true;
+ break;
+ case SchemaFile::ADD_STARTED:
+ jam();
+ case SchemaFile::TABLE_ADD_COMMITTED:
+ jam();
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::ALTER_TABLE_COMMITTED:
+ jam();
+ newEntry->m_tableState = SchemaFile::INIT;
+ restartDropTab(signal, tableId);
+ return;
+ }
+ ndbrequire(ok);
+ break;
+ }
+ case SchemaFile::ALTER_TABLE_COMMITTED: {
+ jam();
+ bool ok = false;
+ switch(oldSchemaState) {
+ case SchemaFile::INIT:
+ jam();
+ case SchemaFile::ADD_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_STARTED:
+ jam();
+ case SchemaFile::DROP_TABLE_COMMITTED:
+ jam();
+ case SchemaFile::TABLE_ADD_COMMITTED:
+ jam();
+ ok = true;
+ //------------------------------------------------------------------
+ // Table was altered in the master node but not in our node. We can
+ // retrieve the altered table definition from the master.
+ //------------------------------------------------------------------
+ restartCreateTab(signal, tableId, oldEntry, false);
+ return;
+ break;
+ case SchemaFile::ALTER_TABLE_COMMITTED:
+ jam();
+ ok = true;
+
+ //------------------------------------------------------------------
+ // Table was altered in both our node and the master node. We can
+ // retrieve the table definition from our own disk.
+ //------------------------------------------------------------------
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ tablePtr.p->tableVersion = oldEntry->m_tableVersion;
+ tablePtr.p->tableType = (DictTabInfo::TableType)oldEntry->m_tableType;
+
+ // On NR get index from master because index state is not on file
+ const bool file = c_systemRestart || tablePtr.p->isTable();
+ restartCreateTab(signal, tableId, oldEntry, file);
+
+ return;
+ }
+ ndbrequire(ok);
+ break;
+ }
+ }
+ }
+
+ execute(signal, c_schemaRecord.m_callback, 0);
+}//checkSchemaStatus()
+
+void
+Dbdict::restartCreateTab(Signal* signal, Uint32 tableId,
+ const SchemaFile::TableEntry * te, bool file){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ c_opCreateTable.seize(createTabPtr);
+ ndbrequire(!createTabPtr.isNull());
+
+ createTabPtr.p->key = ++c_opRecordSequence;
+ c_opCreateTable.add(createTabPtr);
+
+ createTabPtr.p->m_errorCode = 0;
+ createTabPtr.p->m_tablePtrI = tableId;
+ createTabPtr.p->m_coordinatorRef = reference();
+ createTabPtr.p->m_senderRef = 0;
+ createTabPtr.p->m_senderData = RNIL;
+ createTabPtr.p->m_tabInfoPtrI = RNIL;
+ createTabPtr.p->m_dihAddFragPtr = RNIL;
+
+ if(file && !ERROR_INSERTED(6002)){
+ jam();
+
+ c_readTableRecord.noOfPages = te->m_noOfPages;
+ c_readTableRecord.pageId = 0;
+ c_readTableRecord.m_callback.m_callbackData = createTabPtr.p->key;
+ c_readTableRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::restartCreateTab_readTableConf);
+
+ startReadTableFile(signal, tableId);
+ return;
+ } else {
+
+ ndbrequire(c_masterNodeId != getOwnNodeId());
+
+ /**
+ * Get from master
+ */
+ GetTabInfoReq * const req = (GetTabInfoReq *)&signal->theData[0];
+ req->senderRef = reference();
+ req->senderData = createTabPtr.p->key;
+ req->requestType = GetTabInfoReq::RequestById |
+ GetTabInfoReq::LongSignalConf;
+ req->tableId = tableId;
+ sendSignal(calcDictBlockRef(c_masterNodeId), GSN_GET_TABINFOREQ, signal,
+ GetTabInfoReq::SignalLength, JBB);
+
+ if(ERROR_INSERTED(6002)){
+ NdbSleep_MilliSleep(10);
+ CRASH_INSERTION(6002);
+ }
+ }
+}
+
+void
+Dbdict::restartCreateTab_readTableConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ PageRecordPtr pageRecPtr;
+ c_pageRecordArray.getPtr(pageRecPtr, c_readTableRecord.pageId);
+
+ ParseDictTabInfoRecord parseRecord;
+ parseRecord.requestType = DictTabInfo::GetTabInfoConf;
+ parseRecord.errorCode = 0;
+
+ Uint32 sz = c_readTableRecord.noOfPages * ZSIZE_OF_PAGES_IN_WORDS;
+ SimplePropertiesLinearReader r(&pageRecPtr.p->word[0], sz);
+ handleTabInfoInit(r, &parseRecord);
+ ndbrequire(parseRecord.errorCode == 0);
+
+ /* ---------------------------------------------------------------- */
+ // We have read the table description from disk as part of system restart.
+ // We will also write it back again to ensure that both copies are ok.
+ /* ---------------------------------------------------------------- */
+ ndbrequire(c_writeTableRecord.tableWriteState == WriteTableRecord::IDLE);
+ c_writeTableRecord.noOfPages = c_readTableRecord.noOfPages;
+ c_writeTableRecord.pageId = c_readTableRecord.pageId;
+ c_writeTableRecord.tableWriteState = WriteTableRecord::TWR_CALLBACK;
+ c_writeTableRecord.m_callback.m_callbackData = callbackData;
+ c_writeTableRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::restartCreateTab_writeTableConf);
+ startWriteTableFile(signal, c_readTableRecord.tableId);
+}
+
+void
+Dbdict::execGET_TABINFO_CONF(Signal* signal){
+ jamEntry();
+
+ if(!assembleFragments(signal)){
+ jam();
+ return;
+ }
+
+ GetTabInfoConf * const conf = (GetTabInfoConf*)signal->getDataPtr();
+
+ const Uint32 tableId = conf->tableId;
+ const Uint32 senderData = conf->senderData;
+
+ SegmentedSectionPtr tabInfoPtr;
+ signal->getSection(tabInfoPtr, GetTabInfoConf::DICT_TAB_INFO);
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, senderData));
+ ndbrequire(!createTabPtr.isNull());
+ ndbrequire(createTabPtr.p->m_tablePtrI == tableId);
+
+ /**
+ * Put data into table record
+ */
+ ParseDictTabInfoRecord parseRecord;
+ parseRecord.requestType = DictTabInfo::GetTabInfoConf;
+ parseRecord.errorCode = 0;
+
+ SimplePropertiesSectionReader r(tabInfoPtr, getSectionSegmentPool());
+ handleTabInfoInit(r, &parseRecord);
+ ndbrequire(parseRecord.errorCode == 0);
+
+ Callback callback;
+ callback.m_callbackData = createTabPtr.p->key;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::restartCreateTab_writeTableConf);
+
+ signal->header.m_noOfSections = 0;
+ writeTableFile(signal, createTabPtr.p->m_tablePtrI, tabInfoPtr, &callback);
+ signal->setSection(tabInfoPtr, 0);
+ releaseSections(signal);
+}
+
+void
+Dbdict::restartCreateTab_writeTableConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ Callback callback;
+ callback.m_callbackData = callbackData;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::restartCreateTab_dihComplete);
+
+ SegmentedSectionPtr fragDataPtr;
+ fragDataPtr.sz = 0;
+ fragDataPtr.setNull();
+ createTab_dih(signal, createTabPtr, fragDataPtr, &callback);
+}
+
+void
+Dbdict::restartCreateTab_dihComplete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ //@todo check error
+ ndbrequire(createTabPtr.p->m_errorCode == 0);
+
+ Callback callback;
+ callback.m_callbackData = callbackData;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::restartCreateTab_activateComplete);
+
+ alterTab_activate(signal, createTabPtr, &callback);
+}
+
+void
+Dbdict::restartCreateTab_activateComplete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+ tabPtr.p->tabState = TableRecord::DEFINED;
+
+ c_opCreateTable.release(createTabPtr);
+
+ c_restartRecord.activeTable++;
+ checkSchemaStatus(signal);
+}
+
+void
+Dbdict::restartDropTab(Signal* signal, Uint32 tableId){
+
+ const Uint32 key = ++c_opRecordSequence;
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.seize(dropTabPtr));
+
+ dropTabPtr.p->key = key;
+ c_opDropTable.add(dropTabPtr);
+
+ dropTabPtr.p->m_errorCode = 0;
+ dropTabPtr.p->m_request.tableId = tableId;
+ dropTabPtr.p->m_coordinatorRef = 0;
+ dropTabPtr.p->m_requestType = DropTabReq::RestartDropTab;
+ dropTabPtr.p->m_participantData.m_gsn = GSN_DROP_TAB_REQ;
+
+
+ dropTabPtr.p->m_participantData.m_block = 0;
+ dropTabPtr.p->m_participantData.m_callback.m_callbackData = key;
+ dropTabPtr.p->m_participantData.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::restartDropTab_complete);
+ dropTab_nextStep(signal, dropTabPtr);
+}
+
+void
+Dbdict::restartDropTab_complete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, callbackData));
+
+ //@todo check error
+
+ c_opDropTable.release(dropTabPtr);
+
+ c_restartRecord.activeTable++;
+ checkSchemaStatus(signal);
+}
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: NODE FAILURE HANDLING ------------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains the code that is used when nodes */
+/* (kernel/api) fails. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+/* ---------------------------------------------------------------- */
+// We receive a report of an API that failed.
+/* ---------------------------------------------------------------- */
+void Dbdict::execAPI_FAILREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 failedApiNode = signal->theData[0];
+ BlockReference retRef = signal->theData[1];
+
+#if 0
+ Uint32 userNode = refToNode(c_connRecord.userBlockRef);
+ if (userNode == failedApiNode) {
+ jam();
+ c_connRecord.userBlockRef = (Uint32)-1;
+ }//if
+#endif
+
+ signal->theData[0] = failedApiNode;
+ signal->theData[1] = reference();
+ sendSignal(retRef, GSN_API_FAILCONF, signal, 2, JBB);
+}//execAPI_FAILREQ()
+
+/* ---------------------------------------------------------------- */
+// We receive a report of one or more node failures of kernel nodes.
+/* ---------------------------------------------------------------- */
+void Dbdict::execNODE_FAILREP(Signal* signal)
+{
+ jamEntry();
+ NodeFailRep * const nodeFail = (NodeFailRep *)&signal->theData[0];
+
+ c_failureNr = nodeFail->failNo;
+ const Uint32 numberOfFailedNodes = nodeFail->noOfNodes;
+ const bool masterFailed = (c_masterNodeId != nodeFail->masterNodeId);
+ c_masterNodeId = nodeFail->masterNodeId;
+
+ c_noNodesFailed += numberOfFailedNodes;
+ Uint32 theFailedNodes[NodeBitmask::Size];
+ memcpy(theFailedNodes, nodeFail->theNodes, sizeof(theFailedNodes));
+
+ c_counterMgr.execNODE_FAILREP(signal);
+
+ bool ok = false;
+ switch(c_blockState){
+ case BS_IDLE:
+ jam();
+ ok = true;
+ if(c_opRecordPool.getSize() != c_opRecordPool.getNoOfFree()){
+ jam();
+ c_blockState = BS_NODE_FAILURE;
+ }
+ break;
+ case BS_CREATE_TAB:
+ jam();
+ ok = true;
+ if(!masterFailed)
+ break;
+ // fall through
+ case BS_BUSY:
+ case BS_NODE_FAILURE:
+ jam();
+ c_blockState = BS_NODE_FAILURE;
+ ok = true;
+ break;
+ }
+ ndbrequire(ok);
+
+ for(unsigned i = 1; i < MAX_NDB_NODES; i++) {
+ jam();
+ if(NodeBitmask::get(theFailedNodes, i)) {
+ jam();
+ NodeRecordPtr nodePtr;
+ c_nodes.getPtr(nodePtr, i);
+
+ nodePtr.p->nodeState = NodeRecord::NDB_NODE_DEAD;
+ NFCompleteRep * const nfCompRep = (NFCompleteRep *)&signal->theData[0];
+ nfCompRep->blockNo = DBDICT;
+ nfCompRep->nodeId = getOwnNodeId();
+ nfCompRep->failedNodeId = nodePtr.i;
+ sendSignal(DBDIH_REF, GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+
+ c_aliveNodes.clear(i);
+ }//if
+ }//for
+
+}//execNODE_FAILREP()
+
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: NODE START HANDLING --------------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains the code that is used when kernel nodes */
+/* starts. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+/* ---------------------------------------------------------------- */
+// Include a starting node in list of nodes to be part of adding
+// and dropping tables.
+/* ---------------------------------------------------------------- */
+void Dbdict::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+ NodeRecordPtr nodePtr;
+ BlockReference retRef = signal->theData[0];
+ nodePtr.i = signal->theData[1];
+
+ ndbrequire(c_noNodesFailed > 0);
+ c_noNodesFailed--;
+
+ c_nodes.getPtr(nodePtr);
+ ndbrequire(nodePtr.p->nodeState == NodeRecord::NDB_NODE_DEAD);
+ nodePtr.p->nodeState = NodeRecord::NDB_NODE_ALIVE;
+ signal->theData[0] = reference();
+ sendSignal(retRef, GSN_INCL_NODECONF, signal, 1, JBB);
+
+ c_aliveNodes.set(nodePtr.i);
+}//execINCL_NODEREQ()
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: ADD TABLE HANDLING ---------------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains the code that is used when adding a table. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+/* ---------------------------------------------------------------- */
+// This signal receives information about a table from either:
+// API, Ndbcntr or from other DICT.
+/* ---------------------------------------------------------------- */
+void
+Dbdict::execCREATE_TABLE_REQ(Signal* signal){
+ jamEntry();
+ if(!assembleFragments(signal)){
+ return;
+ }
+
+ CreateTableReq* const req = (CreateTableReq*)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+
+ ParseDictTabInfoRecord parseRecord;
+ do {
+ if(getOwnNodeId() != c_masterNodeId){
+ jam();
+ parseRecord.errorCode = CreateTableRef::NotMaster;
+ break;
+ }
+
+ if (c_blockState != BS_IDLE){
+ jam();
+ parseRecord.errorCode = CreateTableRef::Busy;
+ break;
+ }
+
+ CreateTableRecordPtr createTabPtr;
+ c_opCreateTable.seize(createTabPtr);
+
+ if(createTabPtr.isNull()){
+ jam();
+ parseRecord.errorCode = CreateTableRef::Busy;
+ break;
+ }
+
+ parseRecord.requestType = DictTabInfo::CreateTableFromAPI;
+ parseRecord.errorCode = 0;
+
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, CreateTableReq::DICT_TAB_INFO);
+ SimplePropertiesSectionReader r(ptr, getSectionSegmentPool());
+
+ handleTabInfoInit(r, &parseRecord);
+ releaseSections(signal);
+
+ if(parseRecord.errorCode != 0){
+ jam();
+ c_opCreateTable.release(createTabPtr);
+ break;
+ }
+
+ createTabPtr.p->key = ++c_opRecordSequence;
+ c_opCreateTable.add(createTabPtr);
+ createTabPtr.p->m_errorCode = 0;
+ createTabPtr.p->m_senderRef = senderRef;
+ createTabPtr.p->m_senderData = senderData;
+ createTabPtr.p->m_tablePtrI = parseRecord.tablePtr.i;
+ createTabPtr.p->m_coordinatorRef = reference();
+ createTabPtr.p->m_fragmentsPtrI = RNIL;
+ createTabPtr.p->m_dihAddFragPtr = RNIL;
+
+ Uint32 * theData = signal->getDataPtrSend();
+ CreateFragmentationReq * const req = (CreateFragmentationReq*)theData;
+ req->senderRef = reference();
+ req->senderData = createTabPtr.p->key;
+ req->fragmentationType = parseRecord.tablePtr.p->fragmentType;
+ req->noOfFragments = 0;
+ req->fragmentNode = 0;
+ req->primaryTableId = RNIL;
+ if (parseRecord.tablePtr.p->isOrderedIndex()) {
+ // ordered index has same fragmentation as the table
+ const Uint32 primaryTableId = parseRecord.tablePtr.p->primaryTableId;
+ TableRecordPtr primaryTablePtr;
+ c_tableRecordPool.getPtr(primaryTablePtr, primaryTableId);
+ // fragmentationType must be consistent
+ req->fragmentationType = primaryTablePtr.p->fragmentType;
+ req->primaryTableId = primaryTableId;
+ }
+ sendSignal(DBDIH_REF, GSN_CREATE_FRAGMENTATION_REQ, signal,
+ CreateFragmentationReq::SignalLength, JBB);
+
+ c_blockState = BS_CREATE_TAB;
+ return;
+ } while(0);
+
+ /**
+ * Something went wrong
+ */
+ releaseSections(signal);
+
+ CreateTableRef * ref = (CreateTableRef*)signal->getDataPtrSend();
+ ref->senderData = senderData;
+ ref->senderRef = reference();
+ ref->masterNodeId = c_masterNodeId;
+ ref->errorCode = parseRecord.errorCode;
+ ref->errorLine = parseRecord.errorLine;
+ ref->errorKey = parseRecord.errorKey;
+ ref->status = parseRecord.status;
+ sendSignal(senderRef, GSN_CREATE_TABLE_REF, signal,
+ CreateTableRef::SignalLength, JBB);
+}
+
+void
+Dbdict::execALTER_TABLE_REQ(Signal* signal)
+{
+ // Received by master
+ jamEntry();
+ if(!assembleFragments(signal)){
+ return;
+ }
+ AlterTableReq* const req = (AlterTableReq*)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 changeMask = req->changeMask;
+ const Uint32 tableId = req->tableId;
+ const Uint32 tableVersion = req->tableVersion;
+ ParseDictTabInfoRecord* aParseRecord;
+
+ // Get table definition
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId, false);
+ if(tablePtr.isNull()){
+ jam();
+ alterTableRef(signal, req, AlterTableRef::NoSuchTable);
+ return;
+ }
+
+ if(getOwnNodeId() != c_masterNodeId){
+ jam();
+ alterTableRef(signal, req, AlterTableRef::NotMaster);
+ return;
+ }
+
+ if(c_blockState != BS_IDLE){
+ jam();
+ alterTableRef(signal, req, AlterTableRef::Busy);
+ return;
+ }
+
+ const TableRecord::TabState tabState = tablePtr.p->tabState;
+ bool ok = false;
+ switch(tabState){
+ case TableRecord::NOT_DEFINED:
+ case TableRecord::REORG_TABLE_PREPARED:
+ case TableRecord::DEFINING:
+ case TableRecord::CHECKED:
+ jam();
+ alterTableRef(signal, req, AlterTableRef::NoSuchTable);
+ return;
+ case TableRecord::DEFINED:
+ ok = true;
+ jam();
+ break;
+ case TableRecord::PREPARE_DROPPING:
+ case TableRecord::DROPPING:
+ jam();
+ alterTableRef(signal, req, AlterTableRef::DropInProgress);
+ return;
+ }
+ ndbrequire(ok);
+
+ if(tablePtr.p->tableVersion != tableVersion){
+ jam();
+ alterTableRef(signal, req, AlterTableRef::InvalidTableVersion);
+ return;
+ }
+ // Parse new table defintion
+ ParseDictTabInfoRecord parseRecord;
+ aParseRecord = &parseRecord;
+
+ CreateTableRecordPtr alterTabPtr; // Reuse create table records
+ c_opCreateTable.seize(alterTabPtr);
+ CreateTableRecord * regAlterTabPtr = alterTabPtr.p;
+
+ if(alterTabPtr.isNull()){
+ jam();
+ alterTableRef(signal, req, AlterTableRef::Busy);
+ return;
+ }
+
+ regAlterTabPtr->m_changeMask = changeMask;
+ parseRecord.requestType = DictTabInfo::AlterTableFromAPI;
+ parseRecord.errorCode = 0;
+
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, AlterTableReq::DICT_TAB_INFO);
+ SimplePropertiesSectionReader r(ptr, getSectionSegmentPool());
+
+ handleTabInfoInit(r, &parseRecord, false); // Will not save info
+
+ if(parseRecord.errorCode != 0){
+ jam();
+ c_opCreateTable.release(alterTabPtr);
+ alterTableRef(signal, req,
+ (AlterTableRef::ErrorCode) parseRecord.errorCode,
+ aParseRecord);
+ return;
+ }
+
+ releaseSections(signal);
+ regAlterTabPtr->key = ++c_opRecordSequence;
+ c_opCreateTable.add(alterTabPtr);
+ ndbrequire(c_opCreateTable.find(alterTabPtr, regAlterTabPtr->key));
+ regAlterTabPtr->m_errorCode = 0;
+ regAlterTabPtr->m_senderRef = senderRef;
+ regAlterTabPtr->m_senderData = senderData;
+ regAlterTabPtr->m_tablePtrI = parseRecord.tablePtr.i;
+ regAlterTabPtr->m_alterTableFailed = false;
+ regAlterTabPtr->m_coordinatorRef = reference();
+ regAlterTabPtr->m_fragmentsPtrI = RNIL;
+ regAlterTabPtr->m_dihAddFragPtr = RNIL;
+
+ // Alter table on all nodes
+ c_blockState = BS_BUSY;
+
+ // Send prepare request to all alive nodes
+ SimplePropertiesSectionWriter w(getSectionSegmentPool());
+ packTableIntoPagesImpl(w, parseRecord.tablePtr);
+
+ SegmentedSectionPtr tabInfoPtr;
+ w.getPtr(tabInfoPtr);
+ signal->setSection(tabInfoPtr, AlterTabReq::DICT_TAB_INFO);
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ regAlterTabPtr->m_coordinatorData.m_gsn = GSN_ALTER_TAB_REQ;
+ SafeCounter safeCounter(c_counterMgr, regAlterTabPtr->m_coordinatorData.m_counter);
+ safeCounter.init<AlterTabRef>(rg, regAlterTabPtr->key);
+
+ AlterTabReq * const lreq = (AlterTabReq*)signal->getDataPtrSend();
+ lreq->senderRef = reference();
+ lreq->senderData = regAlterTabPtr->key;
+ lreq->clientRef = regAlterTabPtr->m_senderRef;
+ lreq->clientData = regAlterTabPtr->m_senderData;
+ lreq->changeMask = changeMask;
+ lreq->tableId = tableId;
+ lreq->tableVersion = tableVersion + 1;
+ lreq->gci = tablePtr.p->gciTableCreated;
+ lreq->requestType = AlterTabReq::AlterTablePrepare;
+
+ sendSignal(rg, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+
+}
+
+void Dbdict::alterTableRef(Signal * signal,
+ AlterTableReq * req,
+ AlterTableRef::ErrorCode errCode,
+ ParseDictTabInfoRecord* parseRecord)
+{
+ jam();
+ releaseSections(signal);
+ AlterTableRef * ref = (AlterTableRef*)signal->getDataPtrSend();
+ Uint32 senderRef = req->senderRef;
+ ref->senderData = req->senderData;
+ ref->senderRef = reference();
+ ref->masterNodeId = c_masterNodeId;
+ if (parseRecord) {
+ ref->errorCode = parseRecord->errorCode;
+ ref->errorLine = parseRecord->errorLine;
+ ref->errorKey = parseRecord->errorKey;
+ ref->status = parseRecord->status;
+ }
+ else {
+ ref->errorCode = errCode;
+ ref->errorLine = 0;
+ ref->errorKey = 0;
+ ref->status = 0;
+ }
+ sendSignal(senderRef, GSN_ALTER_TABLE_REF, signal,
+ AlterTableRef::SignalLength, JBB);
+}
+
+void
+Dbdict::execALTER_TAB_REQ(Signal * signal)
+{
+ // Received in all nodes to handle change locally
+ jamEntry();
+
+ if(!assembleFragments(signal)){
+ return;
+ }
+ AlterTabReq* const req = (AlterTabReq*)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 changeMask = req->changeMask;
+ const Uint32 tableId = req->tableId;
+ const Uint32 tableVersion = req->tableVersion;
+ const Uint32 gci = req->gci;
+ AlterTabReq::RequestType requestType =
+ (AlterTabReq::RequestType) req->requestType;
+
+ SegmentedSectionPtr tabInfoPtr;
+ signal->getSection(tabInfoPtr, AlterTabReq::DICT_TAB_INFO);
+
+ CreateTableRecordPtr alterTabPtr; // Reuse create table records
+
+ if (senderRef != reference()) {
+ jam();
+ c_blockState = BS_BUSY;
+ }
+ if ((requestType == AlterTabReq::AlterTablePrepare)
+ && (senderRef != reference())) {
+ jam();
+ c_opCreateTable.seize(alterTabPtr);
+ if(!alterTabPtr.isNull())
+ alterTabPtr.p->m_changeMask = changeMask;
+ }
+ else {
+ jam();
+ ndbrequire(c_opCreateTable.find(alterTabPtr, senderData));
+ }
+ if(alterTabPtr.isNull()){
+ jam();
+ alterTabRef(signal, req, AlterTableRef::Busy);
+ return;
+ }
+ CreateTableRecord * regAlterTabPtr = alterTabPtr.p;
+ regAlterTabPtr->m_alterTableId = tableId;
+ regAlterTabPtr->m_coordinatorRef = senderRef;
+
+ // Get table definition
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId, false);
+ if(tablePtr.isNull()){
+ jam();
+ alterTabRef(signal, req, AlterTableRef::NoSuchTable);
+ return;
+ }
+
+ switch(requestType) {
+ case(AlterTabReq::AlterTablePrepare): {
+ ParseDictTabInfoRecord* aParseRecord;
+
+ const TableRecord::TabState tabState = tablePtr.p->tabState;
+ bool ok = false;
+ switch(tabState){
+ case TableRecord::NOT_DEFINED:
+ case TableRecord::REORG_TABLE_PREPARED:
+ case TableRecord::DEFINING:
+ case TableRecord::CHECKED:
+ jam();
+ alterTabRef(signal, req, AlterTableRef::NoSuchTable);
+ return;
+ case TableRecord::DEFINED:
+ ok = true;
+ jam();
+ break;
+ case TableRecord::PREPARE_DROPPING:
+ case TableRecord::DROPPING:
+ jam();
+ alterTabRef(signal, req, AlterTableRef::DropInProgress);
+ return;
+ }
+ ndbrequire(ok);
+
+ if(tablePtr.p->tableVersion + 1 != tableVersion){
+ jam();
+ alterTabRef(signal, req, AlterTableRef::InvalidTableVersion);
+ return;
+ }
+ TableRecordPtr newTablePtr;
+ if (senderRef != reference()) {
+ jam();
+ // Parse altered table defintion
+ ParseDictTabInfoRecord parseRecord;
+ aParseRecord = &parseRecord;
+
+ parseRecord.requestType = DictTabInfo::AlterTableFromAPI;
+ parseRecord.errorCode = 0;
+
+ SimplePropertiesSectionReader r(tabInfoPtr, getSectionSegmentPool());
+
+ handleTabInfoInit(r, &parseRecord, false); // Will not save info
+
+ if(parseRecord.errorCode != 0){
+ jam();
+ c_opCreateTable.release(alterTabPtr);
+ alterTabRef(signal, req,
+ (AlterTableRef::ErrorCode) parseRecord.errorCode,
+ aParseRecord);
+ return;
+ }
+ regAlterTabPtr->key = senderData;
+ c_opCreateTable.add(alterTabPtr);
+ regAlterTabPtr->m_errorCode = 0;
+ regAlterTabPtr->m_senderRef = senderRef;
+ regAlterTabPtr->m_senderData = senderData;
+ regAlterTabPtr->m_tablePtrI = parseRecord.tablePtr.i;
+ regAlterTabPtr->m_fragmentsPtrI = RNIL;
+ regAlterTabPtr->m_dihAddFragPtr = RNIL;
+ newTablePtr = parseRecord.tablePtr;
+ newTablePtr.p->tableVersion = tableVersion;
+ }
+ else { // (req->senderRef == reference())
+ jam();
+ c_tableRecordPool.getPtr(newTablePtr, regAlterTabPtr->m_tablePtrI);
+ newTablePtr.p->tableVersion = tableVersion;
+ }
+ if (handleAlterTab(req, regAlterTabPtr, tablePtr, newTablePtr) == -1) {
+ jam();
+ c_opCreateTable.release(alterTabPtr);
+ alterTabRef(signal, req, AlterTableRef::UnsupportedChange);
+ return;
+ }
+ releaseSections(signal);
+ // Propagate alter table to other local blocks
+ AlterTabReq * req = (AlterTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = senderData;
+ req->changeMask = changeMask;
+ req->tableId = tableId;
+ req->tableVersion = tableVersion;
+ req->gci = gci;
+ req->requestType = requestType;
+ sendSignal(DBLQH_REF, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+ return;
+ }
+ case(AlterTabReq::AlterTableCommit): {
+ jam();
+ // Write schema for altered table to disk
+ SegmentedSectionPtr tabInfoPtr;
+ signal->getSection(tabInfoPtr, AlterTabReq::DICT_TAB_INFO);
+ regAlterTabPtr->m_tabInfoPtrI = tabInfoPtr.i;
+
+ signal->header.m_noOfSections = 0;
+
+ // Update table record
+ tablePtr.p->packedSize = tabInfoPtr.sz;
+ tablePtr.p->tableVersion = tableVersion;
+ tablePtr.p->gciTableCreated = gci;
+
+ SchemaFile::TableEntry tabEntry;
+ tabEntry.m_tableVersion = tableVersion;
+ tabEntry.m_tableType = tablePtr.p->tableType;
+ tabEntry.m_tableState = SchemaFile::ALTER_TABLE_COMMITTED;
+ tabEntry.m_gcp = gci;
+ tabEntry.m_noOfPages =
+ DIV(tabInfoPtr.sz + ZPAGE_HEADER_SIZE, ZSIZE_OF_PAGES_IN_WORDS);
+
+ Callback callback;
+ callback.m_callbackData = senderData;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::alterTab_writeSchemaConf);
+
+ updateSchemaState(signal, tableId, &tabEntry, &callback);
+ break;
+ }
+ case(AlterTabReq::AlterTableRevert): {
+ jam();
+ // Revert failed alter table
+ revertAlterTable(signal, changeMask, tableId, regAlterTabPtr);
+ // Acknowledge the reverted alter table
+ AlterTabConf * conf = (AlterTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->changeMask = changeMask;
+ conf->tableId = tableId;
+ conf->tableVersion = tableVersion;
+ conf->gci = gci;
+ conf->requestType = requestType;
+ sendSignal(senderRef, GSN_ALTER_TAB_CONF, signal,
+ AlterTabConf::SignalLength, JBB);
+ break;
+ }
+ default: ndbrequire(false);
+ }
+}
+
+void Dbdict::alterTabRef(Signal * signal,
+ AlterTabReq * req,
+ AlterTableRef::ErrorCode errCode,
+ ParseDictTabInfoRecord* parseRecord)
+{
+ jam();
+ releaseSections(signal);
+ AlterTabRef * ref = (AlterTabRef*)signal->getDataPtrSend();
+ Uint32 senderRef = req->senderRef;
+ ref->senderData = req->senderData;
+ ref->senderRef = reference();
+ if (parseRecord) {
+ jam();
+ ref->errorCode = parseRecord->errorCode;
+ ref->errorLine = parseRecord->errorLine;
+ ref->errorKey = parseRecord->errorKey;
+ ref->errorStatus = parseRecord->status;
+ }
+ else {
+ jam();
+ ref->errorCode = errCode;
+ ref->errorLine = 0;
+ ref->errorKey = 0;
+ ref->errorStatus = 0;
+ }
+ sendSignal(senderRef, GSN_ALTER_TAB_REF, signal,
+ AlterTabRef::SignalLength, JBB);
+
+ c_blockState = BS_IDLE;
+}
+
+void Dbdict::execALTER_TAB_REF(Signal * signal){
+ jamEntry();
+
+ AlterTabRef * ref = (AlterTabRef*)signal->getDataPtr();
+
+ Uint32 senderRef = ref->senderRef;
+ Uint32 senderData = ref->senderData;
+ Uint32 errorCode = ref->errorCode;
+ Uint32 errorLine = ref->errorLine;
+ Uint32 errorKey = ref->errorKey;
+ Uint32 errorStatus = ref->errorStatus;
+ AlterTabReq::RequestType requestType =
+ (AlterTabReq::RequestType) ref->requestType;
+ CreateTableRecordPtr alterTabPtr;
+ ndbrequire(c_opCreateTable.find(alterTabPtr, senderData));
+ CreateTableRecord * regAlterTabPtr = alterTabPtr.p;
+ Uint32 changeMask = regAlterTabPtr->m_changeMask;
+ SafeCounter safeCounter(c_counterMgr, regAlterTabPtr->m_coordinatorData.m_counter);
+ safeCounter.clearWaitingFor(refToNode(senderRef));
+ switch (requestType) {
+ case(AlterTabReq::AlterTablePrepare): {
+ if (safeCounter.done()) {
+ jam();
+ // Send revert request to all alive nodes
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, regAlterTabPtr->m_alterTableId);
+ Uint32 tableId = tablePtr.p->tableId;
+ Uint32 tableVersion = tablePtr.p->tableVersion;
+ Uint32 gci = tablePtr.p->gciTableCreated;
+ SimplePropertiesSectionWriter w(getSectionSegmentPool());
+ packTableIntoPagesImpl(w, tablePtr);
+ SegmentedSectionPtr spDataPtr;
+ w.getPtr(spDataPtr);
+ signal->setSection(spDataPtr, AlterTabReq::DICT_TAB_INFO);
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ regAlterTabPtr->m_coordinatorData.m_gsn = GSN_ALTER_TAB_REQ;
+ safeCounter.init<AlterTabRef>(rg, regAlterTabPtr->key);
+
+ AlterTabReq * const lreq = (AlterTabReq*)signal->getDataPtrSend();
+ lreq->senderRef = reference();
+ lreq->senderData = regAlterTabPtr->key;
+ lreq->clientRef = regAlterTabPtr->m_senderRef;
+ lreq->clientData = regAlterTabPtr->m_senderData;
+ lreq->changeMask = changeMask;
+ lreq->tableId = tableId;
+ lreq->tableVersion = tableVersion;
+ lreq->gci = gci;
+ lreq->requestType = AlterTabReq::AlterTableRevert;
+
+ sendSignal(rg, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+ }
+ else {
+ jam();
+ regAlterTabPtr->m_alterTableFailed = true;
+ }
+ break;
+ }
+ case(AlterTabReq::AlterTableCommit):
+ jam();
+ case(AlterTabReq::AlterTableRevert): {
+ AlterTableRef * apiRef = (AlterTableRef*)signal->getDataPtrSend();
+
+ apiRef->senderData = senderData;
+ apiRef->senderRef = reference();
+ apiRef->masterNodeId = c_masterNodeId;
+ apiRef->errorCode = errorCode;
+ apiRef->errorLine = errorLine;
+ apiRef->errorKey = errorKey;
+ apiRef->status = errorStatus;
+ if (safeCounter.done()) {
+ jam();
+ sendSignal(senderRef, GSN_ALTER_TABLE_REF, signal,
+ AlterTableRef::SignalLength, JBB);
+ c_blockState = BS_IDLE;
+ }
+ else {
+ jam();
+ regAlterTabPtr->m_alterTableFailed = true;
+ regAlterTabPtr->m_alterTableRef = *apiRef;
+ }
+ break;
+ }
+ default: ndbrequire(false);
+ }
+}
+
+void
+Dbdict::execALTER_TAB_CONF(Signal * signal){
+ jamEntry();
+ AlterTabConf * const conf = (AlterTabConf*)signal->getDataPtr();
+ Uint32 senderRef = conf->senderRef;
+ Uint32 senderData = conf->senderData;
+ Uint32 changeMask = conf->changeMask;
+ Uint32 tableId = conf->tableId;
+ Uint32 tableVersion = conf->tableVersion;
+ Uint32 gci = conf->gci;
+ AlterTabReq::RequestType requestType =
+ (AlterTabReq::RequestType) conf->requestType;
+ CreateTableRecordPtr alterTabPtr;
+ ndbrequire(c_opCreateTable.find(alterTabPtr, senderData));
+ CreateTableRecord * regAlterTabPtr = alterTabPtr.p;
+
+ switch (requestType) {
+ case(AlterTabReq::AlterTablePrepare): {
+ switch(refToBlock(signal->getSendersBlockRef())) {
+ case DBLQH: {
+ jam();
+ AlterTabReq * req = (AlterTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = senderData;
+ req->changeMask = changeMask;
+ req->tableId = tableId;
+ req->tableVersion = tableVersion;
+ req->gci = gci;
+ req->requestType = requestType;
+ sendSignal(DBDIH_REF, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+ return;
+ }
+ case DBDIH: {
+ jam();
+ AlterTabReq * req = (AlterTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = senderData;
+ req->changeMask = changeMask;
+ req->tableId = tableId;
+ req->tableVersion = tableVersion;
+ req->gci = gci;
+ req->requestType = requestType;
+ sendSignal(DBTC_REF, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+ return;
+ }
+ case DBTC: {
+ jam();
+ // Participant is done with prepare phase, send conf to coordinator
+ AlterTabConf * conf = (AlterTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->changeMask = changeMask;
+ conf->tableId = tableId;
+ conf->tableVersion = tableVersion;
+ conf->gci = gci;
+ conf->requestType = requestType;
+ sendSignal(regAlterTabPtr->m_coordinatorRef, GSN_ALTER_TAB_CONF, signal,
+ AlterTabConf::SignalLength, JBB);
+ return;
+ }
+ default :break;
+ }
+ // Coordinator only
+ SafeCounter safeCounter(c_counterMgr, regAlterTabPtr->m_coordinatorData.m_counter);
+ safeCounter.clearWaitingFor(refToNode(senderRef));
+ if (safeCounter.done()) {
+ jam();
+ // We have received all local confirmations
+ if (regAlterTabPtr->m_alterTableFailed) {
+ jam();
+ // Send revert request to all alive nodes
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, regAlterTabPtr->m_alterTableId);
+ Uint32 tableId = tablePtr.p->tableId;
+ Uint32 tableVersion = tablePtr.p->tableVersion;
+ Uint32 gci = tablePtr.p->gciTableCreated;
+ SimplePropertiesSectionWriter w(getSectionSegmentPool());
+ packTableIntoPagesImpl(w, tablePtr);
+ SegmentedSectionPtr spDataPtr;
+ w.getPtr(spDataPtr);
+ signal->setSection(spDataPtr, AlterTabReq::DICT_TAB_INFO);
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ regAlterTabPtr->m_coordinatorData.m_gsn = GSN_ALTER_TAB_REQ;
+ safeCounter.init<AlterTabRef>(rg, regAlterTabPtr->key);
+
+ AlterTabReq * const lreq = (AlterTabReq*)signal->getDataPtrSend();
+ lreq->senderRef = reference();
+ lreq->senderData = regAlterTabPtr->key;
+ lreq->clientRef = regAlterTabPtr->m_senderRef;
+ lreq->clientData = regAlterTabPtr->m_senderData;
+ lreq->changeMask = changeMask;
+ lreq->tableId = tableId;
+ lreq->tableVersion = tableVersion;
+ lreq->gci = gci;
+ lreq->requestType = AlterTabReq::AlterTableRevert;
+
+ sendSignal(rg, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+ }
+ else {
+ jam();
+ // Send commit request to all alive nodes
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ SimplePropertiesSectionWriter w(getSectionSegmentPool());
+ packTableIntoPagesImpl(w, tablePtr);
+ SegmentedSectionPtr spDataPtr;
+ w.getPtr(spDataPtr);
+ signal->setSection(spDataPtr, AlterTabReq::DICT_TAB_INFO);
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ regAlterTabPtr->m_coordinatorData.m_gsn = GSN_ALTER_TAB_REQ;
+ safeCounter.init<AlterTabRef>(rg, regAlterTabPtr->key);
+
+ AlterTabReq * const lreq = (AlterTabReq*)signal->getDataPtrSend();
+ lreq->senderRef = reference();
+ lreq->senderData = regAlterTabPtr->key;
+ lreq->clientRef = regAlterTabPtr->m_senderRef;
+ lreq->clientData = regAlterTabPtr->m_senderData;
+ lreq->changeMask = changeMask;
+ lreq->tableId = tableId;
+ lreq->tableVersion = tableVersion;
+ lreq->gci = gci;
+ lreq->requestType = AlterTabReq::AlterTableCommit;
+
+ sendSignal(rg, GSN_ALTER_TAB_REQ, signal,
+ AlterTabReq::SignalLength, JBB);
+ }
+ }
+ else {
+ // (!safeCounter.done())
+ jam();
+ }
+ break;
+ }
+ case(AlterTabReq::AlterTableRevert):
+ jam();
+ case(AlterTabReq::AlterTableCommit): {
+ SafeCounter safeCounter(c_counterMgr, regAlterTabPtr->m_coordinatorData.m_counter);
+ safeCounter.clearWaitingFor(refToNode(senderRef));
+ if (safeCounter.done()) {
+ jam();
+ // We have received all local confirmations
+ releaseSections(signal);
+ if (regAlterTabPtr->m_alterTableFailed) {
+ jam();
+ AlterTableRef * apiRef =
+ (AlterTableRef*)signal->getDataPtrSend();
+ *apiRef = regAlterTabPtr->m_alterTableRef;
+ sendSignal(regAlterTabPtr->m_senderRef, GSN_ALTER_TABLE_REF, signal,
+ AlterTableRef::SignalLength, JBB);
+ }
+ else {
+ jam();
+ // Alter table completed, inform API
+ AlterTableConf * const apiConf =
+ (AlterTableConf*)signal->getDataPtrSend();
+ apiConf->senderRef = reference();
+ apiConf->senderData = regAlterTabPtr->m_senderData;
+ apiConf->tableId = tableId;
+ apiConf->tableVersion = tableVersion;
+
+ //@todo check api failed
+ sendSignal(regAlterTabPtr->m_senderRef, GSN_ALTER_TABLE_CONF, signal,
+ AlterTableConf::SignalLength, JBB);
+ }
+
+ // Release resources
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, regAlterTabPtr->m_tablePtrI);
+ releaseTableObject(tabPtr.i, false);
+ c_opCreateTable.release(alterTabPtr);
+ c_blockState = BS_IDLE;
+ }
+ else {
+ // (!safeCounter.done())
+ jam();
+ }
+ break;
+ }
+ default: ndbrequire(false);
+ }
+}
+
+// For debugging
+inline
+void Dbdict::printTables()
+{
+ DLHashTable<TableRecord>::Iterator iter;
+ bool moreTables = c_tableRecordHash.first(iter);
+ printf("TABLES IN DICT:\n");
+ while (moreTables) {
+ TableRecordPtr tablePtr = iter.curr;
+ printf("%s ", tablePtr.p->tableName);
+ moreTables = c_tableRecordHash.next(iter);
+ }
+ printf("\n");
+}
+
+int Dbdict::handleAlterTab(AlterTabReq * req,
+ CreateTableRecord * regAlterTabPtr,
+ TableRecordPtr origTablePtr,
+ TableRecordPtr newTablePtr)
+{
+ Uint32 changeMask = req->changeMask;
+
+ if (AlterTableReq::getNameFlag(changeMask)) {
+ jam();
+ // Table rename
+ // Remove from hashtable
+#ifdef VM_TRACE
+ TableRecordPtr tmp;
+ ndbrequire(c_tableRecordHash.find(tmp, *origTablePtr.p));
+#endif
+ c_tableRecordHash.remove(origTablePtr);
+ strcpy(regAlterTabPtr->previousTableName, origTablePtr.p->tableName);
+ strcpy(origTablePtr.p->tableName, newTablePtr.p->tableName);
+ // Set new schema version
+ origTablePtr.p->tableVersion = newTablePtr.p->tableVersion;
+ // Put it back
+#ifdef VM_TRACE
+ ndbrequire(!c_tableRecordHash.find(tmp, *origTablePtr.p));
+#endif
+ c_tableRecordHash.add(origTablePtr);
+
+ return 0;
+ }
+ jam();
+ return -1;
+}
+
+void Dbdict::revertAlterTable(Signal * signal,
+ Uint32 changeMask,
+ Uint32 tableId,
+ CreateTableRecord * regAlterTabPtr)
+{
+ if (AlterTableReq::getNameFlag(changeMask)) {
+ jam();
+ // Table rename
+ // Restore previous name
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ // Remove from hashtable
+#ifdef VM_TRACE
+ TableRecordPtr tmp;
+ ndbrequire(c_tableRecordHash.find(tmp, * tablePtr.p));
+#endif
+ c_tableRecordHash.remove(tablePtr);
+ // Restore name
+ strcpy(tablePtr.p->tableName, regAlterTabPtr->previousTableName);
+ // Revert schema version
+ tablePtr.p->tableVersion = tablePtr.p->tableVersion - 1;
+ // Put it back
+#ifdef VM_TRACE
+ ndbrequire(!c_tableRecordHash.find(tmp, * tablePtr.p));
+#endif
+ c_tableRecordHash.add(tablePtr);
+
+ return;
+ }
+
+ ndbrequire(false);
+}
+
+void
+Dbdict::alterTab_writeSchemaConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ Uint32 key = callbackData;
+ CreateTableRecordPtr alterTabPtr;
+ ndbrequire(c_opCreateTable.find(alterTabPtr, key));
+ CreateTableRecord * regAlterTabPtr = alterTabPtr.p;
+ Uint32 tableId = regAlterTabPtr->m_alterTableId;
+
+ Callback callback;
+ callback.m_callbackData = regAlterTabPtr->key;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::alterTab_writeTableConf);
+
+ SegmentedSectionPtr tabInfoPtr;
+ getSection(tabInfoPtr, regAlterTabPtr->m_tabInfoPtrI);
+
+ writeTableFile(signal, tableId, tabInfoPtr, &callback);
+
+ signal->setSection(tabInfoPtr, 0);
+ releaseSections(signal);
+}
+
+void
+Dbdict::alterTab_writeTableConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ CreateTableRecordPtr alterTabPtr;
+ ndbrequire(c_opCreateTable.find(alterTabPtr, callbackData));
+ CreateTableRecord * regAlterTabPtr = alterTabPtr.p;
+ Uint32 coordinatorRef = regAlterTabPtr->m_coordinatorRef;
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, regAlterTabPtr->m_alterTableId);
+
+ // Alter table commit request handled successfully
+ AlterTabConf * conf = (AlterTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = callbackData;
+ conf->tableId = tabPtr.p->tableId;
+ conf->tableVersion = tabPtr.p->tableVersion;
+ conf->gci = tabPtr.p->gciTableCreated;
+ conf->requestType = AlterTabReq::AlterTableCommit;
+ sendSignal(coordinatorRef, GSN_ALTER_TAB_CONF, signal,
+ AlterTabConf::SignalLength, JBB);
+ if(coordinatorRef != reference()) {
+ jam();
+ // Release resources
+ c_tableRecordPool.getPtr(tabPtr, regAlterTabPtr->m_tablePtrI);
+ releaseTableObject(tabPtr.i, false);
+ c_opCreateTable.release(alterTabPtr);
+ c_blockState = BS_IDLE;
+ }
+}
+
+void
+Dbdict::execCREATE_FRAGMENTATION_REF(Signal * signal){
+ jamEntry();
+ const Uint32 * theData = signal->getDataPtr();
+ CreateFragmentationRef * const ref = (CreateFragmentationRef*)theData;
+ (void)ref;
+ ndbrequire(false);
+}
+
+void
+Dbdict::execCREATE_FRAGMENTATION_CONF(Signal* signal){
+ jamEntry();
+ const Uint32 * theData = signal->getDataPtr();
+ CreateFragmentationConf * const conf = (CreateFragmentationConf*)theData;
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, conf->senderData));
+
+ ndbrequire(signal->getNoOfSections() == 1);
+
+ SegmentedSectionPtr fragDataPtr;
+ signal->getSection(fragDataPtr, CreateFragmentationConf::FRAGMENTS);
+ signal->header.m_noOfSections = 0;
+
+ /**
+ * Get table
+ */
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+
+ /**
+ * Save fragment count
+ */
+ tabPtr.p->fragmentCount = conf->noOfFragments;
+
+ /**
+ * Update table version
+ */
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+ SchemaFile::TableEntry * tabEntry = getTableEntry(pagePtr.p, tabPtr.i);
+
+ tabPtr.p->tableVersion = tabEntry->m_tableVersion + 1;
+
+ /**
+ * Pack
+ */
+ SimplePropertiesSectionWriter w(getSectionSegmentPool());
+ packTableIntoPagesImpl(w, tabPtr);
+
+ SegmentedSectionPtr spDataPtr;
+ w.getPtr(spDataPtr);
+
+ signal->setSection(spDataPtr, CreateTabReq::DICT_TAB_INFO);
+ signal->setSection(fragDataPtr, CreateTabReq::FRAGMENTATION);
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ SafeCounter tmp(c_counterMgr, createTabPtr.p->m_coordinatorData.m_counter);
+ createTabPtr.p->m_coordinatorData.m_gsn = GSN_CREATE_TAB_REQ;
+ createTabPtr.p->m_coordinatorData.m_requestType = CreateTabReq::CreateTablePrepare;
+ tmp.init<CreateTabRef>(rg, GSN_CREATE_TAB_REF, createTabPtr.p->key);
+
+ CreateTabReq * const req = (CreateTabReq*)theData;
+ req->senderRef = reference();
+ req->senderData = createTabPtr.p->key;
+ req->clientRef = createTabPtr.p->m_senderRef;
+ req->clientData = createTabPtr.p->m_senderData;
+ req->requestType = CreateTabReq::CreateTablePrepare;
+
+ req->gci = 0;
+ req->tableId = tabPtr.i;
+ req->tableVersion = tabEntry->m_tableVersion + 1;
+
+ sendFragmentedSignal(rg, GSN_CREATE_TAB_REQ, signal,
+ CreateTabReq::SignalLength, JBB);
+
+ return;
+}
+
+void
+Dbdict::execCREATE_TAB_REF(Signal* signal){
+ jamEntry();
+
+ CreateTabRef * const ref = (CreateTabRef*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, ref->senderData));
+
+ ndbrequire(createTabPtr.p->m_coordinatorRef == reference());
+ ndbrequire(createTabPtr.p->m_coordinatorData.m_gsn == GSN_CREATE_TAB_REQ);
+
+ if(ref->errorCode != CreateTabRef::NF_FakeErrorREF){
+ createTabPtr.p->setErrorCode(ref->errorCode);
+ }
+ createTab_reply(signal, createTabPtr, refToNode(ref->senderRef));
+}
+
+void
+Dbdict::execCREATE_TAB_CONF(Signal* signal){
+ jamEntry();
+
+ ndbrequire(signal->getNoOfSections() == 0);
+
+ CreateTabConf * const conf = (CreateTabConf*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, conf->senderData));
+
+ ndbrequire(createTabPtr.p->m_coordinatorRef == reference());
+ ndbrequire(createTabPtr.p->m_coordinatorData.m_gsn == GSN_CREATE_TAB_REQ);
+
+ createTab_reply(signal, createTabPtr, refToNode(conf->senderRef));
+}
+
+void
+Dbdict::createTab_reply(Signal* signal,
+ CreateTableRecordPtr createTabPtr,
+ Uint32 nodeId)
+{
+
+ SafeCounter tmp(c_counterMgr, createTabPtr.p->m_coordinatorData.m_counter);
+ if(!tmp.clearWaitingFor(nodeId)){
+ jam();
+ return;
+ }
+
+ switch(createTabPtr.p->m_coordinatorData.m_requestType){
+ case CreateTabReq::CreateTablePrepare:{
+
+ if(createTabPtr.p->m_errorCode != 0){
+ jam();
+ /**
+ * Failed to prepare on atleast one node -> abort on all
+ */
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ createTabPtr.p->m_coordinatorData.m_gsn = GSN_CREATE_TAB_REQ;
+ createTabPtr.p->m_coordinatorData.m_requestType = CreateTabReq::CreateTableDrop;
+ ndbrequire(tmp.init<CreateTabRef>(rg, createTabPtr.p->key));
+
+ CreateTabReq * const req = (CreateTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = createTabPtr.p->key;
+ req->requestType = CreateTabReq::CreateTableDrop;
+
+ sendSignal(rg, GSN_CREATE_TAB_REQ, signal,
+ CreateTabReq::SignalLength, JBB);
+ return;
+ }
+
+ /**
+ * Lock mutex before commiting table
+ */
+ Mutex mutex(signal, c_mutexMgr, createTabPtr.p->m_startLcpMutex);
+ Callback c = { safe_cast(&Dbdict::createTab_startLcpMutex_locked),
+ createTabPtr.p->key};
+
+ ndbrequire(mutex.lock(c));
+ return;
+ }
+ case CreateTabReq::CreateTableCommit:{
+ jam();
+ ndbrequire(createTabPtr.p->m_errorCode == 0);
+
+ /**
+ * Unlock mutex before commiting table
+ */
+ Mutex mutex(signal, c_mutexMgr, createTabPtr.p->m_startLcpMutex);
+ Callback c = { safe_cast(&Dbdict::createTab_startLcpMutex_unlocked),
+ createTabPtr.p->key};
+ mutex.unlock(c);
+ return;
+ }
+ case CreateTabReq::CreateTableDrop:{
+ jam();
+ CreateTableRef * const ref = (CreateTableRef*)signal->getDataPtr();
+ ref->senderRef = reference();
+ ref->senderData = createTabPtr.p->m_senderData;
+ ref->errorCode = createTabPtr.p->m_errorCode;
+ ref->masterNodeId = c_masterNodeId;
+ ref->status = 0;
+ ref->errorKey = 0;
+ ref->errorLine = 0;
+
+ //@todo check api failed
+ sendSignal(createTabPtr.p->m_senderRef, GSN_CREATE_TABLE_REF, signal,
+ CreateTableRef::SignalLength, JBB);
+ c_opCreateTable.release(createTabPtr);
+ c_blockState = BS_IDLE;
+ return;
+ }
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::createTab_startLcpMutex_locked(Signal* signal,
+ Uint32 callbackData,
+ Uint32 retValue){
+ jamEntry();
+
+ ndbrequire(retValue == 0);
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ createTabPtr.p->m_coordinatorData.m_gsn = GSN_CREATE_TAB_REQ;
+ createTabPtr.p->m_coordinatorData.m_requestType = CreateTabReq::CreateTableCommit;
+ SafeCounter tmp(c_counterMgr, createTabPtr.p->m_coordinatorData.m_counter);
+ tmp.init<CreateTabRef>(rg, GSN_CREATE_TAB_REF, createTabPtr.p->key);
+
+ CreateTabReq * const req = (CreateTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = createTabPtr.p->key;
+ req->requestType = CreateTabReq::CreateTableCommit;
+
+ sendSignal(rg, GSN_CREATE_TAB_REQ, signal,
+ CreateTabReq::SignalLength, JBB);
+}
+
+void
+Dbdict::createTab_startLcpMutex_unlocked(Signal* signal,
+ Uint32 callbackData,
+ Uint32 retValue){
+ jamEntry();
+
+ ndbrequire(retValue == 0);
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ createTabPtr.p->m_startLcpMutex.release(c_mutexMgr);
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+
+ CreateTableConf * const conf = (CreateTableConf*)signal->getDataPtr();
+ conf->senderRef = reference();
+ conf->senderData = createTabPtr.p->m_senderData;
+ conf->tableId = createTabPtr.p->m_tablePtrI;
+ conf->tableVersion = tabPtr.p->tableVersion;
+
+ //@todo check api failed
+ sendSignal(createTabPtr.p->m_senderRef, GSN_CREATE_TABLE_CONF, signal,
+ CreateTableConf::SignalLength, JBB);
+ c_opCreateTable.release(createTabPtr);
+ c_blockState = BS_IDLE;
+ return;
+}
+
+/***********************************************************
+ * CreateTable participant code
+ **********************************************************/
+void
+Dbdict::execCREATE_TAB_REQ(Signal* signal){
+ jamEntry();
+
+ if(!assembleFragments(signal)){
+ jam();
+ return;
+ }
+
+ CreateTabReq * const req = (CreateTabReq*)signal->getDataPtr();
+
+ CreateTabReq::RequestType rt = (CreateTabReq::RequestType)req->requestType;
+ switch(rt){
+ case CreateTabReq::CreateTablePrepare:
+ CRASH_INSERTION2(6003, getOwnNodeId() != c_masterNodeId);
+ createTab_prepare(signal, req);
+ return;
+ case CreateTabReq::CreateTableCommit:
+ CRASH_INSERTION2(6004, getOwnNodeId() != c_masterNodeId);
+ createTab_commit(signal, req);
+ return;
+ case CreateTabReq::CreateTableDrop:
+ CRASH_INSERTION2(6005, getOwnNodeId() != c_masterNodeId);
+ createTab_drop(signal, req);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::createTab_prepare(Signal* signal, CreateTabReq * req){
+
+ const Uint32 gci = req->gci;
+ const Uint32 tableId = req->tableId;
+ const Uint32 tableVersion = req->tableVersion;
+
+ SegmentedSectionPtr tabInfoPtr;
+ signal->getSection(tabInfoPtr, CreateTabReq::DICT_TAB_INFO);
+
+ CreateTableRecordPtr createTabPtr;
+ if(req->senderRef == reference()){
+ jam();
+ ndbrequire(c_opCreateTable.find(createTabPtr, req->senderData));
+ } else {
+ jam();
+ c_opCreateTable.seize(createTabPtr);
+
+ ndbrequire(!createTabPtr.isNull());
+
+ createTabPtr.p->key = req->senderData;
+ c_opCreateTable.add(createTabPtr);
+ createTabPtr.p->m_errorCode = 0;
+ createTabPtr.p->m_tablePtrI = tableId;
+ createTabPtr.p->m_coordinatorRef = req->senderRef;
+ createTabPtr.p->m_senderRef = req->clientRef;
+ createTabPtr.p->m_senderData = req->clientData;
+ createTabPtr.p->m_dihAddFragPtr = RNIL;
+
+ /**
+ * Put data into table record
+ */
+ ParseDictTabInfoRecord parseRecord;
+ parseRecord.requestType = DictTabInfo::AddTableFromDict;
+ parseRecord.errorCode = 0;
+
+ SimplePropertiesSectionReader r(tabInfoPtr, getSectionSegmentPool());
+
+ handleTabInfoInit(r, &parseRecord);
+
+ ndbrequire(parseRecord.errorCode == 0);
+ }
+
+ ndbrequire(!createTabPtr.isNull());
+
+ SegmentedSectionPtr fragPtr;
+ signal->getSection(fragPtr, CreateTabReq::FRAGMENTATION);
+
+ createTabPtr.p->m_tabInfoPtrI = tabInfoPtr.i;
+ createTabPtr.p->m_fragmentsPtrI = fragPtr.i;
+
+ signal->header.m_noOfSections = 0;
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, tableId);
+ tabPtr.p->packedSize = tabInfoPtr.sz;
+ tabPtr.p->tableVersion = tableVersion;
+ tabPtr.p->gciTableCreated = gci;
+
+ SchemaFile::TableEntry tabEntry;
+ tabEntry.m_tableVersion = tableVersion;
+ tabEntry.m_tableType = tabPtr.p->tableType;
+ tabEntry.m_tableState = SchemaFile::ADD_STARTED;
+ tabEntry.m_gcp = gci;
+ tabEntry.m_noOfPages =
+ DIV(tabInfoPtr.sz + ZPAGE_HEADER_SIZE, ZSIZE_OF_PAGES_IN_WORDS);
+
+ Callback callback;
+ callback.m_callbackData = createTabPtr.p->key;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::createTab_writeSchemaConf1);
+
+ updateSchemaState(signal, tableId, &tabEntry, &callback);
+}
+
+void getSection(SegmentedSectionPtr & ptr, Uint32 i);
+
+void
+Dbdict::createTab_writeSchemaConf1(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ Callback callback;
+ callback.m_callbackData = createTabPtr.p->key;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::createTab_writeTableConf);
+
+ SegmentedSectionPtr tabInfoPtr;
+ getSection(tabInfoPtr, createTabPtr.p->m_tabInfoPtrI);
+ writeTableFile(signal, createTabPtr.p->m_tablePtrI, tabInfoPtr, &callback);
+
+ createTabPtr.p->m_tabInfoPtrI = RNIL;
+ signal->setSection(tabInfoPtr, 0);
+ releaseSections(signal);
+}
+
+void
+Dbdict::createTab_writeTableConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ SegmentedSectionPtr fragDataPtr;
+ getSection(fragDataPtr, createTabPtr.p->m_fragmentsPtrI);
+
+ Callback callback;
+ callback.m_callbackData = callbackData;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::createTab_dihComplete);
+
+ createTab_dih(signal, createTabPtr, fragDataPtr, &callback);
+}
+
+void
+Dbdict::createTab_dih(Signal* signal,
+ CreateTableRecordPtr createTabPtr,
+ SegmentedSectionPtr fragDataPtr,
+ Callback * c){
+ jam();
+
+ createTabPtr.p->m_callback = * c;
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+
+ DiAddTabReq * req = (DiAddTabReq*)signal->getDataPtrSend();
+ req->connectPtr = createTabPtr.p->key;
+ req->tableId = tabPtr.i;
+ req->fragType = tabPtr.p->fragmentType;
+ req->kValue = tabPtr.p->kValue;
+ req->noOfReplicas = 0;
+ req->storedTable = tabPtr.p->storedTable;
+ req->tableType = tabPtr.p->tableType;
+ req->schemaVersion = tabPtr.p->tableVersion;
+ req->primaryTableId = tabPtr.p->primaryTableId;
+
+ if(!fragDataPtr.isNull()){
+ signal->setSection(fragDataPtr, DiAddTabReq::FRAGMENTATION);
+ }
+
+ sendSignal(DBDIH_REF, GSN_DIADDTABREQ, signal,
+ DiAddTabReq::SignalLength, JBB);
+}
+
+static
+void
+calcLHbits(Uint32 * lhPageBits, Uint32 * lhDistrBits,
+ Uint32 fid, Uint32 totalFragments)
+{
+ Uint32 distrBits = 0;
+ Uint32 pageBits = 0;
+
+ Uint32 tmp = 1;
+ while (tmp < totalFragments) {
+ jam();
+ tmp <<= 1;
+ distrBits++;
+ }//while
+ if (tmp != totalFragments) {
+ tmp >>= 1;
+ if ((fid >= (totalFragments - tmp)) && (fid < (tmp - 1))) {
+ distrBits--;
+ }//if
+ }//if
+ * lhPageBits = pageBits;
+ * lhDistrBits = distrBits;
+
+}//calcLHbits()
+
+
+void
+Dbdict::execADD_FRAGREQ(Signal* signal) {
+ jamEntry();
+
+ AddFragReq * const req = (AddFragReq*)signal->getDataPtr();
+
+ Uint32 dihPtr = req->dihPtr;
+ Uint32 senderData = req->senderData;
+ Uint32 tableId = req->tableId;
+ Uint32 fragId = req->fragmentId;
+ Uint32 node = req->nodeId;
+ Uint32 lcpNo = req->nextLCP;
+ Uint32 fragCount = req->totalFragments;
+ Uint32 requestInfo = req->requestInfo;
+ Uint32 startGci = req->startGci;
+
+ ndbrequire(node == getOwnNodeId());
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, senderData));
+
+ createTabPtr.p->m_dihAddFragPtr = dihPtr;
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, tableId);
+
+#if 0
+ tabPtr.p->gciTableCreated = (startGci > tabPtr.p->gciTableCreated ? startGci:
+ startGci > tabPtr.p->gciTableCreated);
+#endif
+
+ /**
+ * Calc lh3PageBits
+ */
+ Uint32 lhDistrBits = 0;
+ Uint32 lhPageBits = 0;
+ ::calcLHbits(&lhPageBits, &lhDistrBits, fragId, fragCount);
+
+ {
+ LqhFragReq* req = (LqhFragReq*)signal->getDataPtrSend();
+ req->senderData = senderData;
+ req->senderRef = reference();
+ req->fragmentId = fragId;
+ req->requestInfo = requestInfo;
+ req->tableId = tableId;
+ req->localKeyLength = tabPtr.p->localKeyLen;
+ req->maxLoadFactor = tabPtr.p->maxLoadFactor;
+ req->minLoadFactor = tabPtr.p->minLoadFactor;
+ req->kValue = tabPtr.p->kValue;
+ req->lh3DistrBits = 0; //lhDistrBits;
+ req->lh3PageBits = 0; //lhPageBits;
+ req->noOfAttributes = tabPtr.p->noOfAttributes;
+ req->noOfNullAttributes = tabPtr.p->noOfNullBits;
+ req->noOfPagesToPreAllocate = 0;
+ req->schemaVersion = tabPtr.p->tableVersion;
+ Uint32 keyLen = tabPtr.p->tupKeyLength;
+ req->keyLength = keyLen; // wl-2066 no more "long keys"
+ req->nextLCP = lcpNo;
+
+ req->noOfKeyAttr = tabPtr.p->noOfPrimkey;
+ req->noOfNewAttr = 0;
+ // noOfCharsets passed to TUP in upper half
+ req->noOfNewAttr |= (tabPtr.p->noOfCharsets << 16);
+ req->checksumIndicator = 1;
+ req->noOfAttributeGroups = 1;
+ req->GCPIndicator = 0;
+ req->startGci = startGci;
+ req->tableType = tabPtr.p->tableType;
+ req->primaryTableId = tabPtr.p->primaryTableId;
+ sendSignal(DBLQH_REF, GSN_LQHFRAGREQ, signal,
+ LqhFragReq::SignalLength, JBB);
+ }
+}
+
+void
+Dbdict::execLQHFRAGREF(Signal * signal){
+ jamEntry();
+ LqhFragRef * const ref = (LqhFragRef*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, ref->senderData));
+
+ createTabPtr.p->setErrorCode(ref->errorCode);
+
+ {
+ AddFragRef * const ref = (AddFragRef*)signal->getDataPtr();
+ ref->dihPtr = createTabPtr.p->m_dihAddFragPtr;
+ sendSignal(DBDIH_REF, GSN_ADD_FRAGREF, signal,
+ AddFragRef::SignalLength, JBB);
+ }
+}
+
+void
+Dbdict::execLQHFRAGCONF(Signal * signal){
+ jamEntry();
+ LqhFragConf * const conf = (LqhFragConf*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, conf->senderData));
+
+ createTabPtr.p->m_lqhFragPtr = conf->lqhFragPtr;
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+ sendLQHADDATTRREQ(signal, createTabPtr, tabPtr.p->firstAttribute);
+}
+
+void
+Dbdict::sendLQHADDATTRREQ(Signal* signal,
+ CreateTableRecordPtr createTabPtr,
+ Uint32 attributePtrI){
+ jam();
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+ LqhAddAttrReq * const req = (LqhAddAttrReq*)signal->getDataPtrSend();
+ Uint32 i = 0;
+ for(i = 0; i<LqhAddAttrReq::MAX_ATTRIBUTES && attributePtrI != RNIL; i++){
+ jam();
+ AttributeRecordPtr attrPtr;
+ c_attributeRecordPool.getPtr(attrPtr, attributePtrI);
+ LqhAddAttrReq::Entry& entry = req->attributes[i];
+ entry.attrId = attrPtr.p->attributeId;
+ entry.attrDescriptor = attrPtr.p->attributeDescriptor;
+ entry.extTypeInfo = 0;
+ // charset number passed to TUP, TUX in upper half
+ entry.extTypeInfo |= (attrPtr.p->extPrecision & ~0xFFFF);
+ if (tabPtr.p->isIndex()) {
+ Uint32 primaryAttrId;
+ if (attrPtr.p->nextAttrInTable != RNIL) {
+ getIndexAttr(tabPtr, attributePtrI, &primaryAttrId);
+ } else {
+ primaryAttrId = ZNIL;
+ if (tabPtr.p->isOrderedIndex())
+ entry.attrId = 0; // attribute goes to TUP
+ }
+ entry.attrId |= (primaryAttrId << 16);
+ }
+ attributePtrI = attrPtr.p->nextAttrInTable;
+ }
+ req->lqhFragPtr = createTabPtr.p->m_lqhFragPtr;
+ req->senderData = createTabPtr.p->key;
+ req->senderAttrPtr = attributePtrI;
+ req->noOfAttributes = i;
+
+ sendSignal(DBLQH_REF, GSN_LQHADDATTREQ, signal,
+ LqhAddAttrReq::HeaderLength + LqhAddAttrReq::EntryLength * i, JBB);
+}
+
+void
+Dbdict::execLQHADDATTREF(Signal * signal){
+ jamEntry();
+ LqhAddAttrRef * const ref = (LqhAddAttrRef*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, ref->senderData));
+
+ createTabPtr.p->setErrorCode(ref->errorCode);
+
+ {
+ AddFragRef * const ref = (AddFragRef*)signal->getDataPtr();
+ ref->dihPtr = createTabPtr.p->m_dihAddFragPtr;
+ sendSignal(DBDIH_REF, GSN_ADD_FRAGREF, signal,
+ AddFragRef::SignalLength, JBB);
+ }
+
+}
+
+void
+Dbdict::execLQHADDATTCONF(Signal * signal){
+ jamEntry();
+ LqhAddAttrConf * const conf = (LqhAddAttrConf*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, conf->senderData));
+
+ const Uint32 fragId = conf->fragId;
+ const Uint32 nextAttrPtr = conf->senderAttrPtr;
+ if(nextAttrPtr != RNIL){
+ jam();
+ sendLQHADDATTRREQ(signal, createTabPtr, nextAttrPtr);
+ return;
+ }
+
+ {
+ AddFragConf * const conf = (AddFragConf*)signal->getDataPtr();
+ conf->dihPtr = createTabPtr.p->m_dihAddFragPtr;
+ conf->fragId = fragId;
+ sendSignal(DBDIH_REF, GSN_ADD_FRAGCONF, signal,
+ AddFragConf::SignalLength, JBB);
+ }
+}
+
+void
+Dbdict::execDIADDTABREF(Signal* signal){
+ jam();
+
+ DiAddTabRef * const ref = (DiAddTabRef*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, ref->senderData));
+
+ createTabPtr.p->setErrorCode(ref->errorCode);
+ execute(signal, createTabPtr.p->m_callback, 0);
+}
+
+void
+Dbdict::execDIADDTABCONF(Signal* signal){
+ jam();
+
+ DiAddTabConf * const conf = (DiAddTabConf*)signal->getDataPtr();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, conf->senderData));
+
+ signal->theData[0] = createTabPtr.p->key;
+ signal->theData[1] = reference();
+ signal->theData[2] = createTabPtr.p->m_tablePtrI;
+
+ if(createTabPtr.p->m_dihAddFragPtr != RNIL){
+ jam();
+
+ /**
+ * We did perform at least one LQHFRAGREQ
+ */
+ sendSignal(DBLQH_REF, GSN_TAB_COMMITREQ, signal, 3, JBB);
+ return;
+ } else {
+ /**
+ * No local fragment (i.e. no LQHFRAGREQ)
+ */
+ execute(signal, createTabPtr.p->m_callback, 0);
+ return;
+ //sendSignal(DBDIH_REF, GSN_TAB_COMMITREQ, signal, 3, JBB);
+ }
+}
+
+void
+Dbdict::execTAB_COMMITREF(Signal* signal) {
+ jamEntry();
+ ndbrequire(false);
+}//execTAB_COMMITREF()
+
+void
+Dbdict::execTAB_COMMITCONF(Signal* signal){
+ jamEntry();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, signal->theData[0]));
+
+ if(refToBlock(signal->getSendersBlockRef()) == DBLQH){
+
+ execute(signal, createTabPtr.p->m_callback, 0);
+ return;
+ }
+
+ if(refToBlock(signal->getSendersBlockRef()) == DBDIH){
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+
+ signal->theData[0] = tabPtr.i;
+ signal->theData[1] = tabPtr.p->tableVersion;
+ signal->theData[2] = (Uint32)tabPtr.p->storedTable;
+ signal->theData[3] = reference();
+ signal->theData[4] = (Uint32)tabPtr.p->tableType;
+ signal->theData[5] = createTabPtr.p->key;
+ signal->theData[6] = (Uint32)tabPtr.p->noOfPrimkey;
+
+ Uint32 buf[2 * MAX_ATTRIBUTES_IN_INDEX];
+ Uint32 sz = 0;
+ Uint32 tAttr = tabPtr.p->firstAttribute;
+ while (tAttr != RNIL) {
+ jam();
+ AttributeRecord* aRec = c_attributeRecordPool.getPtr(tAttr);
+ if (aRec->tupleKey) {
+ buf[sz++] = aRec->attributeDescriptor;
+ buf[sz++] = (aRec->extPrecision >> 16); // charset number
+ }
+ tAttr = aRec->nextAttrInTable;
+ }
+ ndbrequire((int)sz == 2 * tabPtr.p->noOfPrimkey);
+
+ LinearSectionPtr lsPtr[3];
+ lsPtr[0].p = buf;
+ lsPtr[0].sz = sz;
+ // note: ACC does not reply
+ if (tabPtr.p->isTable() || tabPtr.p->isHashIndex())
+ sendSignal(DBACC_REF, GSN_TC_SCHVERREQ, signal, 7, JBB, lsPtr, 1);
+ sendSignal(DBTC_REF, GSN_TC_SCHVERREQ, signal, 7, JBB, lsPtr, 1);
+ return;
+ }
+
+ ndbrequire(false);
+}
+
+void
+Dbdict::createTab_dihComplete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ //@todo check for master failed
+
+ if(createTabPtr.p->m_errorCode == 0){
+ jam();
+
+ CreateTabConf * const conf = (CreateTabConf*)signal->getDataPtr();
+ conf->senderRef = reference();
+ conf->senderData = createTabPtr.p->key;
+ sendSignal(createTabPtr.p->m_coordinatorRef, GSN_CREATE_TAB_CONF,
+ signal, CreateTabConf::SignalLength, JBB);
+ return;
+ }
+
+ CreateTabRef * const ref = (CreateTabRef*)signal->getDataPtr();
+ ref->senderRef = reference();
+ ref->senderData = createTabPtr.p->key;
+ ref->errorCode = createTabPtr.p->m_errorCode;
+ ref->errorLine = 0;
+ ref->errorKey = 0;
+ ref->errorStatus = 0;
+
+ sendSignal(createTabPtr.p->m_coordinatorRef, GSN_CREATE_TAB_REF,
+ signal, CreateTabRef::SignalLength, JBB);
+}
+
+void
+Dbdict::createTab_commit(Signal * signal, CreateTabReq * req){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, req->senderData));
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+
+ SchemaFile::TableEntry tabEntry;
+ tabEntry.m_tableVersion = tabPtr.p->tableVersion;
+ tabEntry.m_tableType = tabPtr.p->tableType;
+ tabEntry.m_tableState = SchemaFile::TABLE_ADD_COMMITTED;
+ tabEntry.m_gcp = tabPtr.p->gciTableCreated;
+ tabEntry.m_noOfPages =
+ DIV(tabPtr.p->packedSize + ZPAGE_HEADER_SIZE, ZSIZE_OF_PAGES_IN_WORDS);
+
+ Callback callback;
+ callback.m_callbackData = createTabPtr.p->key;
+ callback.m_callbackFunction =
+ safe_cast(&Dbdict::createTab_writeSchemaConf2);
+
+ updateSchemaState(signal, tabPtr.i, &tabEntry, &callback);
+}
+
+void
+Dbdict::createTab_writeSchemaConf2(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ Callback c;
+ c.m_callbackData = callbackData;
+ c.m_callbackFunction = safe_cast(&Dbdict::createTab_alterComplete);
+ alterTab_activate(signal, createTabPtr, &c);
+}
+
+void
+Dbdict::createTab_alterComplete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+ tabPtr.p->tabState = TableRecord::DEFINED;
+
+ //@todo check error
+ //@todo check master failed
+
+ CreateTabConf * const conf = (CreateTabConf*)signal->getDataPtr();
+ conf->senderRef = reference();
+ conf->senderData = createTabPtr.p->key;
+ sendSignal(createTabPtr.p->m_coordinatorRef, GSN_CREATE_TAB_CONF,
+ signal, CreateTabConf::SignalLength, JBB);
+
+ if(createTabPtr.p->m_coordinatorRef != reference()){
+ jam();
+ c_opCreateTable.release(createTabPtr);
+ }
+}
+
+void
+Dbdict::createTab_drop(Signal* signal, CreateTabReq * req){
+ jam();
+
+ const Uint32 key = req->senderData;
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, key));
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+ tabPtr.p->tabState = TableRecord::DROPPING;
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.seize(dropTabPtr));
+
+ dropTabPtr.p->key = key;
+ c_opDropTable.add(dropTabPtr);
+
+ dropTabPtr.p->m_errorCode = 0;
+ dropTabPtr.p->m_request.tableId = createTabPtr.p->m_tablePtrI;
+ dropTabPtr.p->m_requestType = DropTabReq::CreateTabDrop;
+ dropTabPtr.p->m_coordinatorRef = createTabPtr.p->m_coordinatorRef;
+ dropTabPtr.p->m_participantData.m_gsn = GSN_DROP_TAB_REQ;
+
+ dropTabPtr.p->m_participantData.m_block = 0;
+ dropTabPtr.p->m_participantData.m_callback.m_callbackData = req->senderData;
+ dropTabPtr.p->m_participantData.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::createTab_dropComplete);
+ dropTab_nextStep(signal, dropTabPtr);
+}
+
+void
+Dbdict::createTab_dropComplete(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode){
+ jam();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, callbackData));
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, callbackData));
+
+ TableRecordPtr tabPtr;
+ c_tableRecordPool.getPtr(tabPtr, createTabPtr.p->m_tablePtrI);
+
+ releaseTableObject(tabPtr.i);
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+
+ SchemaFile::TableEntry * tableEntry = getTableEntry(pagePtr.p, tabPtr.i);
+ tableEntry->m_tableState = SchemaFile::DROP_TABLE_COMMITTED;
+
+ //@todo check error
+ //@todo check master failed
+
+ CreateTabConf * const conf = (CreateTabConf*)signal->getDataPtr();
+ conf->senderRef = reference();
+ conf->senderData = createTabPtr.p->key;
+ sendSignal(createTabPtr.p->m_coordinatorRef, GSN_CREATE_TAB_CONF,
+ signal, CreateTabConf::SignalLength, JBB);
+
+ if(createTabPtr.p->m_coordinatorRef != reference()){
+ jam();
+ c_opCreateTable.release(createTabPtr);
+ }
+
+ c_opDropTable.release(dropTabPtr);
+}
+
+void
+Dbdict::alterTab_activate(Signal* signal, CreateTableRecordPtr createTabPtr,
+ Callback * c){
+
+ createTabPtr.p->m_callback = * c;
+
+ signal->theData[0] = createTabPtr.p->key;
+ signal->theData[1] = reference();
+ signal->theData[2] = createTabPtr.p->m_tablePtrI;
+ sendSignal(DBDIH_REF, GSN_TAB_COMMITREQ, signal, 3, JBB);
+}
+
+void
+Dbdict::execTC_SCHVERCONF(Signal* signal){
+ jamEntry();
+
+ CreateTableRecordPtr createTabPtr;
+ ndbrequire(c_opCreateTable.find(createTabPtr, signal->theData[1]));
+
+ execute(signal, createTabPtr.p->m_callback, 0);
+}
+
+#define tabRequire(cond, error) \
+ if (!(cond)) { \
+ jam(); \
+ parseP->errorCode = error; parseP->errorLine = __LINE__; \
+ parseP->errorKey = it.getKey(); \
+ return; \
+ }//if
+
+// handleAddTableFailure(signal, __LINE__, allocatedTable);
+
+void Dbdict::handleTabInfoInit(SimpleProperties::Reader & it,
+ ParseDictTabInfoRecord * parseP,
+ bool checkExist)
+{
+/* ---------------------------------------------------------------- */
+// We always start by handling table name since this must be the first
+// item in the list. Through the table name we can derive if it is a
+// correct name, a new name or an already existing table.
+/* ---------------------------------------------------------------- */
+
+ it.first();
+
+ SimpleProperties::UnpackStatus status;
+ DictTabInfo::Table tableDesc; tableDesc.init();
+ status = SimpleProperties::unpack(it, &tableDesc,
+ DictTabInfo::TableMapping,
+ DictTabInfo::TableMappingSize,
+ true, true);
+
+ if(status != SimpleProperties::Break){
+ parseP->errorCode = CreateTableRef::InvalidFormat;
+ parseP->status = status;
+ parseP->errorKey = it.getKey();
+ parseP->errorLine = __LINE__;
+ return;
+ }
+
+ if(parseP->requestType == DictTabInfo::AlterTableFromAPI)
+ {
+ ndbrequire(!checkExist);
+ }
+ if(!checkExist)
+ {
+ ndbrequire(parseP->requestType == DictTabInfo::AlterTableFromAPI);
+ }
+
+ /* ---------------------------------------------------------------- */
+ // Verify that table name is an allowed table name.
+ // TODO
+ /* ---------------------------------------------------------------- */
+ const Uint32 tableNameLength = strlen(tableDesc.TableName) + 1;
+
+ TableRecord keyRecord;
+ tabRequire(tableNameLength <= sizeof(keyRecord.tableName),
+ CreateTableRef::TableNameTooLong);
+ strcpy(keyRecord.tableName, tableDesc.TableName);
+
+ TableRecordPtr tablePtr;
+ c_tableRecordHash.find(tablePtr, keyRecord);
+
+ if (checkExist){
+ jam();
+ /* ---------------------------------------------------------------- */
+ // Check if table already existed.
+ /* ---------------------------------------------------------------- */
+ tabRequire(tablePtr.i == RNIL, CreateTableRef::TableAlreadyExist);
+ }
+
+ switch (parseP->requestType) {
+ case DictTabInfo::CreateTableFromAPI: {
+ jam();
+ }
+ case DictTabInfo::AlterTableFromAPI:{
+ jam();
+ tablePtr.i = getFreeTableRecord(tableDesc.PrimaryTableId);
+ /* ---------------------------------------------------------------- */
+ // Check if no free tables existed.
+ /* ---------------------------------------------------------------- */
+ tabRequire(tablePtr.i != RNIL, CreateTableRef::NoMoreTableRecords);
+
+ c_tableRecordPool.getPtr(tablePtr);
+ break;
+ }
+ case DictTabInfo::AddTableFromDict:
+ case DictTabInfo::ReadTableFromDiskSR:
+ case DictTabInfo::GetTabInfoConf:
+ {
+/* ---------------------------------------------------------------- */
+// Get table id and check that table doesn't already exist
+/* ---------------------------------------------------------------- */
+ tablePtr.i = tableDesc.TableId;
+
+ if (parseP->requestType == DictTabInfo::ReadTableFromDiskSR) {
+ ndbrequire(tablePtr.i == c_restartRecord.activeTable);
+ }//if
+ if (parseP->requestType == DictTabInfo::GetTabInfoConf) {
+ ndbrequire(tablePtr.i == c_restartRecord.activeTable);
+ }//if
+
+ c_tableRecordPool.getPtr(tablePtr);
+ ndbrequire(tablePtr.p->tabState == TableRecord::NOT_DEFINED);
+
+ //Uint32 oldTableVersion = tablePtr.p->tableVersion;
+ initialiseTableRecord(tablePtr);
+ if (parseP->requestType == DictTabInfo::AddTableFromDict) {
+ jam();
+ tablePtr.p->tabState = TableRecord::DEFINING;
+ }//if
+#ifdef HAVE_TABLE_REORG
+/* ---------------------------------------------------------------- */
+// Get id of second table id and check that table doesn't already exist
+// and set up links between first and second table.
+/* ---------------------------------------------------------------- */
+ TableRecordPtr secondTablePtr;
+ secondTablePtr.i = tableDesc.SecondTableId;
+ c_tableRecordPool.getPtr(secondTablePtr);
+ ndbrequire(secondTablePtr.p->tabState == TableRecord::NOT_DEFINED);
+
+ initialiseTableRecord(secondTablePtr);
+ secondTablePtr.p->tabState = TableRecord::REORG_TABLE_PREPARED;
+ secondTablePtr.p->secondTable = tablePtr.i;
+ tablePtr.p->secondTable = secondTablePtr.i;
+#endif
+/* ---------------------------------------------------------------- */
+// Set table version
+/* ---------------------------------------------------------------- */
+ Uint32 tableVersion = tableDesc.TableVersion;
+ tablePtr.p->tableVersion = tableVersion;
+
+ break;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ parseP->tablePtr = tablePtr;
+
+ strcpy(tablePtr.p->tableName, keyRecord.tableName);
+ if (parseP->requestType != DictTabInfo::AlterTableFromAPI) {
+ jam();
+#ifdef VM_TRACE
+ ndbout_c("Dbdict: name=%s,id=%u", tablePtr.p->tableName, tablePtr.i);
+ TableRecordPtr tmp;
+ ndbrequire(!c_tableRecordHash.find(tmp, * tablePtr.p));
+#endif
+ c_tableRecordHash.add(tablePtr);
+ }
+
+ //tablePtr.p->noOfPrimkey = tableDesc.NoOfKeyAttr;
+ //tablePtr.p->noOfNullAttr = tableDesc.NoOfNullable;
+ //tablePtr.p->tupKeyLength = tableDesc.KeyLength;
+ tablePtr.p->noOfAttributes = tableDesc.NoOfAttributes;
+ tablePtr.p->storedTable = tableDesc.TableLoggedFlag;
+ tablePtr.p->minLoadFactor = tableDesc.MinLoadFactor;
+ tablePtr.p->maxLoadFactor = tableDesc.MaxLoadFactor;
+ tablePtr.p->fragmentType = (DictTabInfo::FragmentType)tableDesc.FragmentType;
+ tablePtr.p->tableType = (DictTabInfo::TableType)tableDesc.TableType;
+ tablePtr.p->kValue = tableDesc.TableKValue;
+ tablePtr.p->fragmentCount = tableDesc.FragmentCount;
+
+ tablePtr.p->frmLen = tableDesc.FrmLen;
+ memcpy(tablePtr.p->frmData, tableDesc.FrmData, tableDesc.FrmLen);
+
+ if(tableDesc.PrimaryTableId != RNIL) {
+
+ tablePtr.p->primaryTableId = tableDesc.PrimaryTableId;
+ tablePtr.p->indexState = (TableRecord::IndexState)tableDesc.IndexState;
+ tablePtr.p->insertTriggerId = tableDesc.InsertTriggerId;
+ tablePtr.p->updateTriggerId = tableDesc.UpdateTriggerId;
+ tablePtr.p->deleteTriggerId = tableDesc.DeleteTriggerId;
+ tablePtr.p->customTriggerId = tableDesc.CustomTriggerId;
+ } else {
+ tablePtr.p->primaryTableId = RNIL;
+ tablePtr.p->indexState = TableRecord::IS_UNDEFINED;
+ tablePtr.p->insertTriggerId = RNIL;
+ tablePtr.p->updateTriggerId = RNIL;
+ tablePtr.p->deleteTriggerId = RNIL;
+ tablePtr.p->customTriggerId = RNIL;
+ }
+ tablePtr.p->buildTriggerId = RNIL;
+ tablePtr.p->indexLocal = 0;
+
+ handleTabInfo(it, parseP);
+
+ if(parseP->errorCode != 0)
+ {
+ /**
+ * Release table
+ */
+ releaseTableObject(tablePtr.i, checkExist);
+ }
+}//handleTabInfoInit()
+
+void Dbdict::handleTabInfo(SimpleProperties::Reader & it,
+ ParseDictTabInfoRecord * parseP)
+{
+ TableRecordPtr tablePtr = parseP->tablePtr;
+
+ SimpleProperties::UnpackStatus status;
+
+ Uint32 keyCount = 0;
+ Uint32 keyLength = 0;
+ Uint32 attrCount = tablePtr.p->noOfAttributes;
+ Uint32 nullCount = 0;
+ Uint32 nullBits = 0;
+ Uint32 noOfCharsets = 0;
+ Uint16 charsets[128];
+ Uint32 recordLength = 0;
+ AttributeRecordPtr attrPtr;
+ c_attributeRecordHash.removeAll();
+
+ for(Uint32 i = 0; i<attrCount; i++){
+ /**
+ * Attribute Name
+ */
+ DictTabInfo::Attribute attrDesc; attrDesc.init();
+ status = SimpleProperties::unpack(it, &attrDesc,
+ DictTabInfo::AttributeMapping,
+ DictTabInfo::AttributeMappingSize,
+ true, true);
+ if(status != SimpleProperties::Break){
+ parseP->errorCode = CreateTableRef::InvalidFormat;
+ parseP->status = status;
+ parseP->errorKey = it.getKey();
+ parseP->errorLine = __LINE__;
+ return;
+ }
+
+ /**
+ * Check that attribute is not defined twice
+ */
+ AttributeRecord tmpAttr;
+ {
+ strcpy(tmpAttr.attributeName, attrDesc.AttributeName);
+
+ AttributeRecordPtr attrPtr;
+ c_attributeRecordHash.find(attrPtr, tmpAttr);
+
+ if(attrPtr.i != RNIL){
+ parseP->errorCode = CreateTableRef::AttributeNameTwice;
+ return;
+ }
+ }
+
+ if(!getNewAttributeRecord(tablePtr, attrPtr)){
+ jam();
+ parseP->errorCode = CreateTableRef::NoMoreAttributeRecords;
+ return;
+ }
+
+ /**
+ * TmpAttrib to Attribute mapping
+ */
+ strcpy(attrPtr.p->attributeName, attrDesc.AttributeName);
+ attrPtr.p->attributeId = attrDesc.AttributeId;
+ attrPtr.p->tupleKey = (keyCount + 1) * attrDesc.AttributeKeyFlag;
+
+ attrPtr.p->extPrecision = attrDesc.AttributeExtPrecision;
+ attrPtr.p->extScale = attrDesc.AttributeExtScale;
+ attrPtr.p->extLength = attrDesc.AttributeExtLength;
+ // charset in upper half of precision
+ unsigned csNumber = (attrPtr.p->extPrecision >> 16);
+ if (csNumber != 0) {
+ /*
+ * A new charset is first accessed here on this node.
+ * TODO use separate thread (e.g. via NDBFS) if need to load from file
+ */
+ CHARSET_INFO* cs = get_charset(csNumber, MYF(0));
+ if (cs == NULL) {
+ parseP->errorCode = CreateTableRef::InvalidCharset;
+ parseP->errorLine = __LINE__;
+ return;
+ }
+ // XXX should be done somewhere in mysql
+ all_charsets[cs->number] = cs;
+ unsigned i = 0;
+ while (i < noOfCharsets) {
+ if (charsets[i] == csNumber)
+ break;
+ i++;
+ }
+ if (i == noOfCharsets) {
+ noOfCharsets++;
+ if (noOfCharsets > sizeof(charsets)/sizeof(charsets[0])) {
+ parseP->errorCode = CreateTableRef::InvalidFormat;
+ parseP->errorLine = __LINE__;
+ return;
+ }
+ charsets[i] = csNumber;
+ }
+ }
+
+ // compute attribute size and array size
+ bool translateOk = attrDesc.translateExtType();
+ tabRequire(translateOk, CreateTableRef::Inconsistency);
+
+ if(attrDesc.AttributeArraySize > 65535){
+ parseP->errorCode = CreateTableRef::ArraySizeTooBig;
+ parseP->status = status;
+ parseP->errorKey = it.getKey();
+ parseP->errorLine = __LINE__;
+ return;
+ }
+
+ Uint32 desc = 0;
+ AttributeDescriptor::setType(desc, attrDesc.AttributeExtType);
+ AttributeDescriptor::setSize(desc, attrDesc.AttributeSize);
+ AttributeDescriptor::setArray(desc, attrDesc.AttributeArraySize);
+ AttributeDescriptor::setNullable(desc, attrDesc.AttributeNullableFlag);
+ AttributeDescriptor::setDKey(desc, attrDesc.AttributeDKey);
+ AttributeDescriptor::setPrimaryKey(desc, attrDesc.AttributeKeyFlag);
+ attrPtr.p->attributeDescriptor = desc;
+ attrPtr.p->autoIncrement = attrDesc.AttributeAutoIncrement;
+ strcpy(attrPtr.p->defaultValue, attrDesc.AttributeDefaultValue);
+
+ tabRequire(attrDesc.AttributeId == i, CreateTableRef::InvalidFormat);
+
+ attrCount ++;
+ keyCount += attrDesc.AttributeKeyFlag;
+ nullCount += attrDesc.AttributeNullableFlag;
+
+ const Uint32 aSz = (1 << attrDesc.AttributeSize);
+ Uint32 sz;
+ if(aSz != 1)
+ {
+ sz = ((aSz * attrDesc.AttributeArraySize) + 31) >> 5;
+ }
+ else
+ {
+ sz = 0;
+ nullBits += attrDesc.AttributeArraySize;
+ }
+
+ if(attrDesc.AttributeArraySize == 0)
+ {
+ parseP->errorCode = CreateTableRef::InvalidArraySize;
+ parseP->status = status;
+ parseP->errorKey = it.getKey();
+ parseP->errorLine = __LINE__;
+ return;
+ }
+
+ recordLength += sz;
+ if(attrDesc.AttributeKeyFlag){
+ keyLength += sz;
+
+ if(attrDesc.AttributeNullableFlag){
+ parseP->errorCode = CreateTableRef::NullablePrimaryKey;
+ parseP->status = status;
+ parseP->errorKey = it.getKey();
+ parseP->errorLine = __LINE__;
+ return;
+ }
+ }
+
+ if (parseP->requestType != DictTabInfo::AlterTableFromAPI)
+ c_attributeRecordHash.add(attrPtr);
+
+ if(!it.next())
+ break;
+
+ if(it.getKey() != DictTabInfo::AttributeName)
+ break;
+ }//while
+
+ tablePtr.p->noOfPrimkey = keyCount;
+ tablePtr.p->noOfNullAttr = nullCount;
+ tablePtr.p->noOfCharsets = noOfCharsets;
+ tablePtr.p->tupKeyLength = keyLength;
+ tablePtr.p->noOfNullBits = nullCount + nullBits;
+
+ tabRequire(recordLength<= MAX_TUPLE_SIZE_IN_WORDS,
+ CreateTableRef::RecordTooBig);
+ tabRequire(keyLength <= MAX_KEY_SIZE_IN_WORDS,
+ CreateTableRef::InvalidPrimaryKeySize);
+ tabRequire(keyLength > 0,
+ CreateTableRef::InvalidPrimaryKeySize);
+
+}//handleTabInfo()
+
+
+/* ---------------------------------------------------------------- */
+// DICTTABCONF is sent when participants have received all DICTTABINFO
+// and successfully handled it.
+// Also sent to self (DICT master) when index table creation ready.
+/* ---------------------------------------------------------------- */
+void Dbdict::execCREATE_TABLE_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+
+ CreateTableConf * const conf = (CreateTableConf *)signal->getDataPtr();
+ // assume part of create index operation
+ OpCreateIndexPtr opPtr;
+ c_opCreateIndex.find(opPtr, conf->senderData);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->m_request.setIndexId(conf->tableId);
+ opPtr.p->m_request.setIndexVersion(conf->tableVersion);
+ createIndex_fromCreateTable(signal, opPtr);
+}//execCREATE_TABLE_CONF()
+
+void Dbdict::execCREATE_TABLE_REF(Signal* signal)
+{
+ jamEntry();
+
+ CreateTableRef * const ref = (CreateTableRef *)signal->getDataPtr();
+ // assume part of create index operation
+ OpCreateIndexPtr opPtr;
+ c_opCreateIndex.find(opPtr, ref->senderData);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ createIndex_fromCreateTable(signal, opPtr);
+}//execCREATE_TABLE_REF()
+
+/* ---------------------------------------------------------------- */
+// New global checkpoint created.
+/* ---------------------------------------------------------------- */
+void Dbdict::execWAIT_GCP_CONF(Signal* signal)
+{
+#if 0
+ TableRecordPtr tablePtr;
+ jamEntry();
+ WaitGCPConf* const conf = (WaitGCPConf*)&signal->theData[0];
+ c_tableRecordPool.getPtr(tablePtr, c_connRecord.connTableId);
+ tablePtr.p->gciTableCreated = conf->gcp;
+ sendUpdateSchemaState(signal,
+ tablePtr.i,
+ SchemaFile::TABLE_ADD_COMMITTED,
+ c_connRecord.noOfPagesForTable,
+ conf->gcp);
+#endif
+}//execWAIT_GCP_CONF()
+
+/* ---------------------------------------------------------------- */
+// Refused new global checkpoint.
+/* ---------------------------------------------------------------- */
+void Dbdict::execWAIT_GCP_REF(Signal* signal)
+{
+ jamEntry();
+ WaitGCPRef* const ref = (WaitGCPRef*)&signal->theData[0];
+/* ---------------------------------------------------------------- */
+// Error Handling code needed
+/* ---------------------------------------------------------------- */
+ progError(ref->errorCode, 0);
+}//execWAIT_GCP_REF()
+
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: DROP TABLE -------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains the code used to drop a table. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+void
+Dbdict::execDROP_TABLE_REQ(Signal* signal){
+ jamEntry();
+ DropTableReq* req = (DropTableReq*)signal->getDataPtr();
+
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, req->tableId, false);
+ if(tablePtr.isNull()){
+ jam();
+ dropTableRef(signal, req, DropTableRef::NoSuchTable);
+ return;
+ }
+
+ if(getOwnNodeId() != c_masterNodeId){
+ jam();
+ dropTableRef(signal, req, DropTableRef::NotMaster);
+ return;
+ }
+
+ if(c_blockState != BS_IDLE){
+ jam();
+ dropTableRef(signal, req, DropTableRef::Busy);
+ return;
+ }
+
+ const TableRecord::TabState tabState = tablePtr.p->tabState;
+ bool ok = false;
+ switch(tabState){
+ case TableRecord::NOT_DEFINED:
+ case TableRecord::REORG_TABLE_PREPARED:
+ case TableRecord::DEFINING:
+ case TableRecord::CHECKED:
+ jam();
+ dropTableRef(signal, req, DropTableRef::NoSuchTable);
+ return;
+ case TableRecord::DEFINED:
+ ok = true;
+ jam();
+ break;
+ case TableRecord::PREPARE_DROPPING:
+ case TableRecord::DROPPING:
+ jam();
+ dropTableRef(signal, req, DropTableRef::DropInProgress);
+ return;
+ }
+ ndbrequire(ok);
+
+ if(tablePtr.p->tableVersion != req->tableVersion){
+ jam();
+ dropTableRef(signal, req, DropTableRef::InvalidTableVersion);
+ return;
+ }
+
+ /**
+ * Seems ok
+ */
+ DropTableRecordPtr dropTabPtr;
+ c_opDropTable.seize(dropTabPtr);
+
+ if(dropTabPtr.isNull()){
+ jam();
+ dropTableRef(signal, req, DropTableRef::NoDropTableRecordAvailable);
+ return;
+ }
+
+ c_blockState = BS_BUSY;
+
+ dropTabPtr.p->key = ++c_opRecordSequence;
+ c_opDropTable.add(dropTabPtr);
+
+ tablePtr.p->tabState = TableRecord::PREPARE_DROPPING;
+
+ dropTabPtr.p->m_request = * req;
+ dropTabPtr.p->m_errorCode = 0;
+ dropTabPtr.p->m_requestType = DropTabReq::OnlineDropTab;
+ dropTabPtr.p->m_coordinatorRef = reference();
+ dropTabPtr.p->m_coordinatorData.m_gsn = GSN_PREP_DROP_TAB_REQ;
+ dropTabPtr.p->m_coordinatorData.m_block = 0;
+ prepDropTab_nextStep(signal, dropTabPtr);
+}
+
+void
+Dbdict::dropTableRef(Signal * signal,
+ DropTableReq * req, DropTableRef::ErrorCode errCode){
+
+ Uint32 tableId = req->tableId;
+ Uint32 tabVersion = req->tableVersion;
+ Uint32 senderData = req->senderData;
+ Uint32 senderRef = req->senderRef;
+
+ DropTableRef * ref = (DropTableRef*)signal->getDataPtrSend();
+ ref->tableId = tableId;
+ ref->tableVersion = tabVersion;
+ ref->senderData = senderData;
+ ref->senderRef = reference();
+ ref->errorCode = errCode;
+ ref->masterNodeId = c_masterNodeId;
+ sendSignal(senderRef, GSN_DROP_TABLE_REF, signal,
+ DropTableRef::SignalLength, JBB);
+}
+
+void
+Dbdict::prepDropTab_nextStep(Signal* signal, DropTableRecordPtr dropTabPtr){
+
+ /**
+ * No errors currently allowed
+ */
+ ndbrequire(dropTabPtr.p->m_errorCode == 0);
+
+ Uint32 block = 0;
+ switch(dropTabPtr.p->m_coordinatorData.m_block){
+ case 0:
+ jam();
+ block = dropTabPtr.p->m_coordinatorData.m_block = DBDICT;
+ break;
+ case DBDICT:
+ jam();
+ block = dropTabPtr.p->m_coordinatorData.m_block = DBLQH;
+ break;
+ case DBLQH:
+ jam();
+ block = dropTabPtr.p->m_coordinatorData.m_block = DBTC;
+ break;
+ case DBTC:
+ jam();
+ block = dropTabPtr.p->m_coordinatorData.m_block = DBDIH;
+ break;
+ case DBDIH:
+ jam();
+ prepDropTab_complete(signal, dropTabPtr);
+ return;
+ default:
+ ndbrequire(false);
+ }
+
+ PrepDropTabReq * prep = (PrepDropTabReq*)signal->getDataPtrSend();
+ prep->senderRef = reference();
+ prep->senderData = dropTabPtr.p->key;
+ prep->tableId = dropTabPtr.p->m_request.tableId;
+ prep->requestType = dropTabPtr.p->m_requestType;
+
+ dropTabPtr.p->m_coordinatorData.m_signalCounter = c_aliveNodes;
+ NodeReceiverGroup rg(block, c_aliveNodes);
+ sendSignal(rg, GSN_PREP_DROP_TAB_REQ, signal,
+ PrepDropTabReq::SignalLength, JBB);
+
+#if 0
+ for (Uint32 i = 1; i < MAX_NDB_NODES; i++){
+ if(c_aliveNodes.get(i)){
+ jam();
+ BlockReference ref = numberToRef(block, i);
+
+ dropTabPtr.p->m_coordinatorData.m_signalCounter.setWaitingFor(i);
+ }
+ }
+#endif
+}
+
+void
+Dbdict::execPREP_DROP_TAB_CONF(Signal * signal){
+ jamEntry();
+
+ PrepDropTabConf * prep = (PrepDropTabConf*)signal->getDataPtr();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, prep->senderData));
+
+ ndbrequire(dropTabPtr.p->m_coordinatorRef == reference());
+ ndbrequire(dropTabPtr.p->m_request.tableId == prep->tableId);
+ ndbrequire(dropTabPtr.p->m_coordinatorData.m_gsn == GSN_PREP_DROP_TAB_REQ);
+
+ Uint32 nodeId = refToNode(prep->senderRef);
+ dropTabPtr.p->m_coordinatorData.m_signalCounter.clearWaitingFor(nodeId);
+
+ if(!dropTabPtr.p->m_coordinatorData.m_signalCounter.done()){
+ jam();
+ return;
+ }
+ prepDropTab_nextStep(signal, dropTabPtr);
+}
+
+void
+Dbdict::execPREP_DROP_TAB_REF(Signal* signal){
+ jamEntry();
+
+ PrepDropTabRef * prep = (PrepDropTabRef*)signal->getDataPtr();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, prep->senderData));
+
+ ndbrequire(dropTabPtr.p->m_coordinatorRef == reference());
+ ndbrequire(dropTabPtr.p->m_request.tableId == prep->tableId);
+ ndbrequire(dropTabPtr.p->m_coordinatorData.m_gsn == GSN_PREP_DROP_TAB_REQ);
+
+ Uint32 nodeId = refToNode(prep->senderRef);
+ dropTabPtr.p->m_coordinatorData.m_signalCounter.clearWaitingFor(nodeId);
+
+ Uint32 block = refToBlock(prep->senderRef);
+ if((prep->errorCode == PrepDropTabRef::NoSuchTable && block == DBLQH) ||
+ (prep->errorCode == PrepDropTabRef::NF_FakeErrorREF)){
+ jam();
+ /**
+ * Ignore errors:
+ * 1) no such table and LQH, it might not exists in different LQH's
+ * 2) node failure...
+ */
+ } else {
+ dropTabPtr.p->setErrorCode((Uint32)prep->errorCode);
+ }
+
+ if(!dropTabPtr.p->m_coordinatorData.m_signalCounter.done()){
+ jam();
+ return;
+ }
+ prepDropTab_nextStep(signal, dropTabPtr);
+}
+
+void
+Dbdict::prepDropTab_complete(Signal* signal, DropTableRecordPtr dropTabPtr){
+ jam();
+
+ dropTabPtr.p->m_coordinatorData.m_gsn = GSN_DROP_TAB_REQ;
+ dropTabPtr.p->m_coordinatorData.m_block = DBDICT;
+
+ DropTabReq * req = (DropTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = dropTabPtr.p->key;
+ req->tableId = dropTabPtr.p->m_request.tableId;
+ req->requestType = dropTabPtr.p->m_requestType;
+
+ dropTabPtr.p->m_coordinatorData.m_signalCounter = c_aliveNodes;
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_DROP_TAB_REQ, signal,
+ DropTabReq::SignalLength, JBB);
+}
+
+void
+Dbdict::execDROP_TAB_REF(Signal* signal){
+ jamEntry();
+
+ DropTabRef * const req = (DropTabRef*)signal->getDataPtr();
+
+ Uint32 block = refToBlock(req->senderRef);
+ ndbrequire(req->errorCode == DropTabRef::NF_FakeErrorREF ||
+ (req->errorCode == DropTabRef::NoSuchTable &&
+ (block == DBTUP || block == DBACC || block == DBLQH)));
+
+ if(block != DBDICT){
+ jam();
+ ndbrequire(refToNode(req->senderRef) == getOwnNodeId());
+ dropTab_localDROP_TAB_CONF(signal);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::execDROP_TAB_CONF(Signal* signal){
+ jamEntry();
+
+ DropTabConf * const req = (DropTabConf*)signal->getDataPtr();
+
+ if(refToBlock(req->senderRef) != DBDICT){
+ jam();
+ ndbrequire(refToNode(req->senderRef) == getOwnNodeId());
+ dropTab_localDROP_TAB_CONF(signal);
+ return;
+ }
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, req->senderData));
+
+ ndbrequire(dropTabPtr.p->m_coordinatorRef == reference());
+ ndbrequire(dropTabPtr.p->m_request.tableId == req->tableId);
+ ndbrequire(dropTabPtr.p->m_coordinatorData.m_gsn == GSN_DROP_TAB_REQ);
+
+ Uint32 nodeId = refToNode(req->senderRef);
+ dropTabPtr.p->m_coordinatorData.m_signalCounter.clearWaitingFor(nodeId);
+
+ if(!dropTabPtr.p->m_coordinatorData.m_signalCounter.done()){
+ jam();
+ return;
+ }
+
+ DropTableConf* conf = (DropTableConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = dropTabPtr.p->m_request.senderData;
+ conf->tableId = dropTabPtr.p->m_request.tableId;
+ conf->tableVersion = dropTabPtr.p->m_request.tableVersion;
+
+ Uint32 ref = dropTabPtr.p->m_request.senderRef;
+ sendSignal(ref, GSN_DROP_TABLE_CONF, signal,
+ DropTableConf::SignalLength, JBB);
+
+ c_opDropTable.release(dropTabPtr);
+ c_blockState = BS_IDLE;
+}
+
+/**
+ * DROP TABLE PARTICIPANT CODE
+ */
+void
+Dbdict::execPREP_DROP_TAB_REQ(Signal* signal){
+ jamEntry();
+ PrepDropTabReq * prep = (PrepDropTabReq*)signal->getDataPtrSend();
+
+ DropTableRecordPtr dropTabPtr;
+ if(prep->senderRef == reference()){
+ jam();
+ ndbrequire(c_opDropTable.find(dropTabPtr, prep->senderData));
+ ndbrequire(dropTabPtr.p->m_requestType == prep->requestType);
+ } else {
+ jam();
+ c_opDropTable.seize(dropTabPtr);
+ if(!dropTabPtr.isNull()){
+ dropTabPtr.p->key = prep->senderData;
+ c_opDropTable.add(dropTabPtr);
+ }
+ }
+
+ ndbrequire(!dropTabPtr.isNull());
+
+ dropTabPtr.p->m_errorCode = 0;
+ dropTabPtr.p->m_request.tableId = prep->tableId;
+ dropTabPtr.p->m_requestType = prep->requestType;
+ dropTabPtr.p->m_coordinatorRef = prep->senderRef;
+ dropTabPtr.p->m_participantData.m_gsn = GSN_PREP_DROP_TAB_REQ;
+
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, prep->tableId);
+ tablePtr.p->tabState = TableRecord::PREPARE_DROPPING;
+
+ /**
+ * Modify schema
+ */
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+
+ SchemaFile::TableEntry * tableEntry = getTableEntry(pagePtr.p, tablePtr.i);
+ SchemaFile::TableState tabState =
+ (SchemaFile::TableState)tableEntry->m_tableState;
+ ndbrequire(tabState == SchemaFile::TABLE_ADD_COMMITTED ||
+ tabState == SchemaFile::ALTER_TABLE_COMMITTED);
+ tableEntry->m_tableState = SchemaFile::DROP_TABLE_STARTED;
+ computeChecksum((SchemaFile*)pagePtr.p);
+
+ ndbrequire(c_writeSchemaRecord.inUse == false);
+ c_writeSchemaRecord.inUse = true;
+
+ c_writeSchemaRecord.pageId = c_schemaRecord.schemaPage;
+ c_writeSchemaRecord.m_callback.m_callbackData = dropTabPtr.p->key;
+ c_writeSchemaRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::prepDropTab_writeSchemaConf);
+ startWriteSchemaFile(signal);
+}
+
+void
+Dbdict::prepDropTab_writeSchemaConf(Signal* signal,
+ Uint32 dropTabPtrI,
+ Uint32 returnCode){
+ jam();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, dropTabPtrI));
+
+ ndbrequire(dropTabPtr.p->m_participantData.m_gsn == GSN_PREP_DROP_TAB_REQ);
+
+ /**
+ * There probably should be node fail handlign here
+ *
+ * To check that coordinator hasn't died
+ */
+
+ PrepDropTabConf * prep = (PrepDropTabConf*)signal->getDataPtr();
+ prep->senderRef = reference();
+ prep->senderData = dropTabPtrI;
+ prep->tableId = dropTabPtr.p->m_request.tableId;
+
+ dropTabPtr.p->m_participantData.m_gsn = GSN_PREP_DROP_TAB_CONF;
+ sendSignal(dropTabPtr.p->m_coordinatorRef, GSN_PREP_DROP_TAB_CONF, signal,
+ PrepDropTabConf::SignalLength, JBB);
+}
+
+void
+Dbdict::execDROP_TAB_REQ(Signal* signal){
+ jamEntry();
+ DropTabReq * req = (DropTabReq*)signal->getDataPtrSend();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, req->senderData));
+
+ ndbrequire(dropTabPtr.p->m_participantData.m_gsn == GSN_PREP_DROP_TAB_CONF);
+ dropTabPtr.p->m_participantData.m_gsn = GSN_DROP_TAB_REQ;
+
+ ndbrequire(dropTabPtr.p->m_requestType == req->requestType);
+
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, dropTabPtr.p->m_request.tableId);
+ tablePtr.p->tabState = TableRecord::DROPPING;
+
+ dropTabPtr.p->m_participantData.m_block = 0;
+ dropTabPtr.p->m_participantData.m_callback.m_callbackData = dropTabPtr.p->key;
+ dropTabPtr.p->m_participantData.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::dropTab_complete);
+ dropTab_nextStep(signal, dropTabPtr);
+}
+
+#include <DebuggerNames.hpp>
+
+void
+Dbdict::dropTab_nextStep(Signal* signal, DropTableRecordPtr dropTabPtr){
+
+ /**
+ * No errors currently allowed
+ */
+ ndbrequire(dropTabPtr.p->m_errorCode == 0);
+
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, dropTabPtr.p->m_request.tableId);
+
+ Uint32 block = 0;
+ switch(dropTabPtr.p->m_participantData.m_block){
+ case 0:
+ jam();
+ block = DBTC;
+ break;
+ case DBTC:
+ jam();
+ if (tablePtr.p->isTable() || tablePtr.p->isHashIndex())
+ block = DBACC;
+ if (tablePtr.p->isOrderedIndex())
+ block = DBTUP;
+ break;
+ case DBACC:
+ jam();
+ block = DBTUP;
+ break;
+ case DBTUP:
+ jam();
+ if (tablePtr.p->isTable() || tablePtr.p->isHashIndex())
+ block = DBLQH;
+ if (tablePtr.p->isOrderedIndex())
+ block = DBTUX;
+ break;
+ case DBTUX:
+ jam();
+ block = DBLQH;
+ break;
+ case DBLQH:
+ jam();
+ block = DBDIH;
+ break;
+ case DBDIH:
+ jam();
+ execute(signal, dropTabPtr.p->m_participantData.m_callback, 0);
+ return;
+ }
+ ndbrequire(block != 0);
+ dropTabPtr.p->m_participantData.m_block = block;
+
+ DropTabReq * req = (DropTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = dropTabPtr.p->key;
+ req->tableId = dropTabPtr.p->m_request.tableId;
+ req->requestType = dropTabPtr.p->m_requestType;
+
+ const Uint32 nodeId = getOwnNodeId();
+ dropTabPtr.p->m_participantData.m_signalCounter.clearWaitingFor();
+ dropTabPtr.p->m_participantData.m_signalCounter.setWaitingFor(nodeId);
+ BlockReference ref = numberToRef(block, 0);
+ sendSignal(ref, GSN_DROP_TAB_REQ, signal, DropTabReq::SignalLength, JBB);
+}
+
+void
+Dbdict::dropTab_localDROP_TAB_CONF(Signal* signal){
+ jamEntry();
+
+ DropTabConf * conf = (DropTabConf*)signal->getDataPtr();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, conf->senderData));
+
+ ndbrequire(dropTabPtr.p->m_request.tableId == conf->tableId);
+ ndbrequire(dropTabPtr.p->m_participantData.m_gsn == GSN_DROP_TAB_REQ);
+
+ Uint32 nodeId = refToNode(conf->senderRef);
+ dropTabPtr.p->m_participantData.m_signalCounter.clearWaitingFor(nodeId);
+
+ if(!dropTabPtr.p->m_participantData.m_signalCounter.done()){
+ jam();
+ ndbrequire(false);
+ return;
+ }
+ dropTab_nextStep(signal, dropTabPtr);
+}
+
+void
+Dbdict::dropTab_complete(Signal* signal,
+ Uint32 dropTabPtrI,
+ Uint32 returnCode){
+ jam();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, dropTabPtrI));
+
+ Uint32 tableId = dropTabPtr.p->m_request.tableId;
+
+ /**
+ * Write to schema file
+ */
+ PageRecordPtr pagePtr;
+ c_pageRecordArray.getPtr(pagePtr, c_schemaRecord.schemaPage);
+
+ SchemaFile::TableEntry * tableEntry = getTableEntry(pagePtr.p, tableId);
+ SchemaFile::TableState tabState =
+ (SchemaFile::TableState)tableEntry->m_tableState;
+ ndbrequire(tabState == SchemaFile::DROP_TABLE_STARTED);
+ tableEntry->m_tableState = SchemaFile::DROP_TABLE_COMMITTED;
+ computeChecksum((SchemaFile*)pagePtr.p);
+
+ ndbrequire(c_writeSchemaRecord.inUse == false);
+ c_writeSchemaRecord.inUse = true;
+
+ c_writeSchemaRecord.pageId = c_schemaRecord.schemaPage;
+ c_writeSchemaRecord.m_callback.m_callbackData = dropTabPtr.p->key;
+ c_writeSchemaRecord.m_callback.m_callbackFunction =
+ safe_cast(&Dbdict::dropTab_writeSchemaConf);
+ startWriteSchemaFile(signal);
+}
+
+void
+Dbdict::dropTab_writeSchemaConf(Signal* signal,
+ Uint32 dropTabPtrI,
+ Uint32 returnCode){
+ jam();
+
+ DropTableRecordPtr dropTabPtr;
+ ndbrequire(c_opDropTable.find(dropTabPtr, dropTabPtrI));
+
+ ndbrequire(dropTabPtr.p->m_participantData.m_gsn == GSN_DROP_TAB_REQ);
+
+ dropTabPtr.p->m_participantData.m_gsn = GSN_DROP_TAB_CONF;
+
+ releaseTableObject(dropTabPtr.p->m_request.tableId);
+
+ DropTabConf * conf = (DropTabConf*)signal->getDataPtr();
+ conf->senderRef = reference();
+ conf->senderData = dropTabPtrI;
+ conf->tableId = dropTabPtr.p->m_request.tableId;
+
+ dropTabPtr.p->m_participantData.m_gsn = GSN_DROP_TAB_CONF;
+ sendSignal(dropTabPtr.p->m_coordinatorRef, GSN_DROP_TAB_CONF, signal,
+ DropTabConf::SignalLength, JBB);
+
+ if(dropTabPtr.p->m_coordinatorRef != reference()){
+ c_opDropTable.release(dropTabPtr);
+ }
+}
+
+void Dbdict::releaseTableObject(Uint32 tableId, bool removeFromHash)
+{
+ TableRecordPtr tablePtr;
+ AttributeRecordPtr attrPtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ if (removeFromHash)
+ {
+#ifdef VM_TRACE
+ TableRecordPtr tmp;
+ ndbrequire(c_tableRecordHash.find(tmp, * tablePtr.p));
+#endif
+ c_tableRecordHash.remove(tablePtr);
+ }
+ tablePtr.p->tabState = TableRecord::NOT_DEFINED;
+
+ Uint32 nextAttrRecord = tablePtr.p->firstAttribute;
+ while (nextAttrRecord != RNIL) {
+ jam();
+/* ---------------------------------------------------------------- */
+// Release all attribute records
+/* ---------------------------------------------------------------- */
+ c_attributeRecordPool.getPtr(attrPtr, nextAttrRecord);
+ nextAttrRecord = attrPtr.p->nextAttrInTable;
+ c_attributeRecordPool.release(attrPtr);
+ }//if
+#ifdef HAVE_TABLE_REORG
+ Uint32 secondTableId = tablePtr.p->secondTable;
+ initialiseTableRecord(tablePtr);
+ c_tableRecordPool.getPtr(tablePtr, secondTableId);
+ initialiseTableRecord(tablePtr);
+#endif
+ return;
+}//releaseTableObject()
+
+/**
+ * DICT receives these on index create and drop.
+ */
+void Dbdict::execDROP_TABLE_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+
+ DropTableConf * const conf = (DropTableConf *)signal->getDataPtr();
+ // assume part of drop index operation
+ OpDropIndexPtr opPtr;
+ c_opDropIndex.find(opPtr, conf->senderData);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_request.getIndexId() == conf->tableId);
+ ndbrequire(opPtr.p->m_request.getIndexVersion() == conf->tableVersion);
+ dropIndex_fromDropTable(signal, opPtr);
+}
+
+void Dbdict::execDROP_TABLE_REF(Signal* signal)
+{
+ jamEntry();
+
+ DropTableRef * const ref = (DropTableRef *)signal->getDataPtr();
+ // assume part of drop index operation
+ OpDropIndexPtr opPtr;
+ c_opDropIndex.find(opPtr, ref->senderData);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ opPtr.p->m_errorLine = __LINE__;
+ dropIndex_fromDropTable(signal, opPtr);
+}
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: EXTERNAL INTERFACE TO DATA -------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* This module contains the code that is used by other modules to. */
+/* access the data within DBDICT. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+void Dbdict::execGET_TABLEDID_REQ(Signal * signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 1);
+ GetTableIdReq const * req = (GetTableIdReq *)signal->getDataPtr();
+ Uint32 senderData = req->senderData;
+ Uint32 senderRef = req->senderRef;
+ Uint32 len = req->len;
+
+ if(len>MAX_TAB_NAME_SIZE)
+ {
+ jam();
+ sendGET_TABLEID_REF((Signal*)signal,
+ (GetTableIdReq *)req,
+ GetTableIdRef::TableNameTooLong);
+ return;
+ }
+
+ char tableName[MAX_TAB_NAME_SIZE];
+ TableRecord keyRecord;
+ SegmentedSectionPtr ssPtr;
+ signal->getSection(ssPtr,GetTableIdReq::TABLE_NAME);
+ copy((Uint32*)tableName, ssPtr);
+ strcpy(keyRecord.tableName, tableName);
+ releaseSections(signal);
+
+ if(len > sizeof(keyRecord.tableName)){
+ jam();
+ sendGET_TABLEID_REF((Signal*)signal,
+ (GetTableIdReq *)req,
+ GetTableIdRef::TableNameTooLong);
+ return;
+ }
+
+ TableRecordPtr tablePtr;
+ if(!c_tableRecordHash.find(tablePtr, keyRecord)) {
+ jam();
+ sendGET_TABLEID_REF((Signal*)signal,
+ (GetTableIdReq *)req,
+ GetTableIdRef::TableNotDefined);
+ return;
+ }
+ GetTableIdConf * conf = (GetTableIdConf *)req;
+ conf->tableId = tablePtr.p->tableId;
+ conf->schemaVersion = tablePtr.p->tableVersion;
+ conf->senderData = senderData;
+ sendSignal(senderRef, GSN_GET_TABLEID_CONF, signal,
+ GetTableIdConf::SignalLength, JBB);
+
+}
+
+
+void Dbdict::sendGET_TABLEID_REF(Signal* signal,
+ GetTableIdReq * req,
+ GetTableIdRef::ErrorCode errorCode)
+{
+ GetTableIdRef * const ref = (GetTableIdRef *)req;
+ /**
+ * The format of GetTabInfo Req/Ref is the same
+ */
+ BlockReference retRef = req->senderRef;
+ ref->err = errorCode;
+ sendSignal(retRef, GSN_GET_TABLEID_REF, signal,
+ GetTableIdRef::SignalLength, JBB);
+}//sendGET_TABINFOREF()
+
+/* ---------------------------------------------------------------- */
+// Get a full table description.
+/* ---------------------------------------------------------------- */
+void Dbdict::execGET_TABINFOREQ(Signal* signal)
+{
+ jamEntry();
+ if(!assembleFragments(signal))
+ {
+ return;
+ }
+
+ GetTabInfoReq * const req = (GetTabInfoReq *)&signal->theData[0];
+
+ /**
+ * If I get a GET_TABINFO_REQ from myself
+ * it's is a one from the time queue
+ */
+ bool fromTimeQueue = (signal->senderBlockRef() == reference());
+
+ if (c_retrieveRecord.busyState && fromTimeQueue == true) {
+ jam();
+
+ sendSignalWithDelay(reference(), GSN_GET_TABINFOREQ, signal, 30,
+ signal->length());
+ return;
+ }//if
+
+ const Uint32 MAX_WAITERS = 5;
+
+ if(c_retrieveRecord.busyState && fromTimeQueue == false){
+ jam();
+ if(c_retrieveRecord.noOfWaiters < MAX_WAITERS){
+ jam();
+ c_retrieveRecord.noOfWaiters++;
+
+ sendSignalWithDelay(reference(), GSN_GET_TABINFOREQ, signal, 30,
+ signal->length());
+ return;
+ }
+
+ sendGET_TABINFOREF(signal, req, GetTabInfoRef::Busy);
+ return;
+ }
+
+ if(fromTimeQueue){
+ jam();
+ c_retrieveRecord.noOfWaiters--;
+ }
+
+ const bool useLongSig = (req->requestType & GetTabInfoReq::LongSignalConf);
+ const Uint32 reqType = req->requestType & (~GetTabInfoReq::LongSignalConf);
+
+ TableRecordPtr tablePtr;
+ if(reqType == GetTabInfoReq::RequestByName){
+ jam();
+ ndbrequire(signal->getNoOfSections() == 1);
+ const Uint32 len = req->tableNameLen;
+
+ TableRecord keyRecord;
+ if(len > sizeof(keyRecord.tableName)){
+ jam();
+ releaseSections(signal);
+ sendGET_TABINFOREF(signal, req, GetTabInfoRef::TableNameTooLong);
+ return;
+ }
+
+ char tableName[MAX_TAB_NAME_SIZE];
+ SegmentedSectionPtr ssPtr;
+ signal->getSection(ssPtr,GetTabInfoReq::TABLE_NAME);
+ SimplePropertiesSectionReader r0(ssPtr, getSectionSegmentPool());
+ r0.reset(); // undo implicit first()
+ if(r0.getWords((Uint32*)tableName, ((len + 3)/4)))
+ memcpy(keyRecord.tableName, tableName, len);
+ else {
+ jam();
+ releaseSections(signal);
+ sendGET_TABINFOREF(signal, req, GetTabInfoRef::TableNotDefined);
+ return;
+ }
+ releaseSections(signal);
+ // memcpy(keyRecord.tableName, req->tableName, len);
+ //ntohS(&keyRecord.tableName[0], len);
+
+ c_tableRecordHash.find(tablePtr, keyRecord);
+ } else {
+ jam();
+ c_tableRecordPool.getPtr(tablePtr, req->tableId, false);
+ }
+
+ // The table seached for was not found
+ if(tablePtr.i == RNIL){
+ jam();
+ sendGET_TABINFOREF(signal, req, GetTabInfoRef::InvalidTableId);
+ return;
+ }//if
+
+ if (tablePtr.p->tabState != TableRecord::DEFINED) {
+ jam();
+ sendGET_TABINFOREF(signal, req, GetTabInfoRef::TableNotDefined);
+ return;
+ }//if
+
+ c_retrieveRecord.busyState = true;
+ c_retrieveRecord.blockRef = req->senderRef;
+ c_retrieveRecord.m_senderData = req->senderData;
+ c_retrieveRecord.tableId = tablePtr.i;
+ c_retrieveRecord.currentSent = 0;
+ c_retrieveRecord.m_useLongSig = useLongSig;
+
+ c_packTable.m_state = PackTable::PTS_GET_TAB;
+
+ signal->theData[0] = ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tablePtr.i;
+ signal->theData[2] = c_retrieveRecord.retrievePage;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+}//execGET_TABINFOREQ()
+
+void Dbdict::sendGetTabResponse(Signal* signal)
+{
+ PageRecordPtr pagePtr;
+ DictTabInfo * const conf = (DictTabInfo *)&signal->theData[0];
+ conf->senderRef = reference();
+ conf->senderData = c_retrieveRecord.m_senderData;
+ conf->requestType = DictTabInfo::GetTabInfoConf;
+ conf->totalLen = c_retrieveRecord.retrievedNoOfWords;
+
+ c_pageRecordArray.getPtr(pagePtr, c_retrieveRecord.retrievePage);
+ Uint32* pagePointer = (Uint32*)&pagePtr.p->word[0] + ZPAGE_HEADER_SIZE;
+
+ if(c_retrieveRecord.m_useLongSig){
+ jam();
+ GetTabInfoConf* conf = (GetTabInfoConf*)signal->getDataPtr();
+ conf->gci = 0;
+ conf->tableId = c_retrieveRecord.tableId;
+ conf->senderData = c_retrieveRecord.m_senderData;
+ conf->totalLen = c_retrieveRecord.retrievedNoOfWords;
+
+ Callback c = { safe_cast(&Dbdict::initRetrieveRecord), 0 };
+ LinearSectionPtr ptr[3];
+ ptr[0].p = pagePointer;
+ ptr[0].sz = c_retrieveRecord.retrievedNoOfWords;
+ sendFragmentedSignal(c_retrieveRecord.blockRef,
+ GSN_GET_TABINFO_CONF,
+ signal,
+ GetTabInfoConf::SignalLength,
+ JBB,
+ ptr,
+ 1,
+ c);
+ return;
+ }
+
+ ndbrequire(false);
+}//sendGetTabResponse()
+
+void Dbdict::sendGET_TABINFOREF(Signal* signal,
+ GetTabInfoReq * req,
+ GetTabInfoRef::ErrorCode errorCode)
+{
+ jamEntry();
+ GetTabInfoRef * const ref = (GetTabInfoRef *)&signal->theData[0];
+ /**
+ * The format of GetTabInfo Req/Ref is the same
+ */
+ BlockReference retRef = req->senderRef;
+ ref->errorCode = errorCode;
+
+ sendSignal(retRef, GSN_GET_TABINFOREF, signal, signal->length(), JBB);
+}//sendGET_TABINFOREF()
+
+Uint32 convertEndian(Uint32 in) {
+#ifdef WORDS_BIGENDIAN
+ Uint32 ut = 0;
+ ut += ((in >> 24) & 255);
+ ut += (((in >> 16) & 255) << 8);
+ ut += (((in >> 8) & 255) << 16);
+ ut += ((in & 255) << 24);
+ return ut;
+#else
+ return in;
+#endif
+}
+void
+Dbdict::execLIST_TABLES_REQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 i;
+ ListTablesReq * req = (ListTablesReq*)signal->getDataPtr();
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+ // save req flags
+ const Uint32 reqTableId = req->getTableId();
+ const Uint32 reqTableType = req->getTableType();
+ const bool reqListNames = req->getListNames();
+ const bool reqListIndexes = req->getListIndexes();
+ // init the confs
+ ListTablesConf * conf = (ListTablesConf *)signal->getDataPtrSend();
+ conf->senderData = senderData;
+ conf->counter = 0;
+ Uint32 pos = 0;
+ for (i = 0; i < c_tableRecordPool.getSize(); i++) {
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, i);
+ // filter
+ if (tablePtr.p->tabState == TableRecord::NOT_DEFINED ||
+ tablePtr.p->tabState == TableRecord::REORG_TABLE_PREPARED)
+ continue;
+
+
+ if ((reqTableType != (Uint32)0) && (reqTableType != (unsigned)tablePtr.p->tableType))
+ continue;
+ if (reqListIndexes && reqTableId != tablePtr.p->primaryTableId)
+ continue;
+ conf->tableData[pos] = 0;
+ // id
+ conf->setTableId(pos, tablePtr.i);
+ // type
+ conf->setTableType(pos, tablePtr.p->tableType);
+ // state
+ if (tablePtr.p->isTable()) {
+ switch (tablePtr.p->tabState) {
+ case TableRecord::DEFINING:
+ case TableRecord::CHECKED:
+ conf->setTableState(pos, DictTabInfo::StateBuilding);
+ break;
+ case TableRecord::PREPARE_DROPPING:
+ case TableRecord::DROPPING:
+ conf->setTableState(pos, DictTabInfo::StateDropping);
+ break;
+ case TableRecord::DEFINED:
+ conf->setTableState(pos, DictTabInfo::StateOnline);
+ break;
+ default:
+ conf->setTableState(pos, DictTabInfo::StateBroken);
+ break;
+ }
+ }
+ if (tablePtr.p->isIndex()) {
+ switch (tablePtr.p->indexState) {
+ case TableRecord::IS_OFFLINE:
+ conf->setTableState(pos, DictTabInfo::StateOffline);
+ break;
+ case TableRecord::IS_BUILDING:
+ conf->setTableState(pos, DictTabInfo::StateBuilding);
+ break;
+ case TableRecord::IS_DROPPING:
+ conf->setTableState(pos, DictTabInfo::StateDropping);
+ break;
+ case TableRecord::IS_ONLINE:
+ conf->setTableState(pos, DictTabInfo::StateOnline);
+ break;
+ default:
+ conf->setTableState(pos, DictTabInfo::StateBroken);
+ break;
+ }
+ }
+ // store
+ if (! tablePtr.p->storedTable) {
+ conf->setTableStore(pos, DictTabInfo::StoreTemporary);
+ } else {
+ conf->setTableStore(pos, DictTabInfo::StorePermanent);
+ }
+ pos++;
+ if (pos >= ListTablesConf::DataLength) {
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::SignalLength, JBB);
+ conf->counter++;
+ pos = 0;
+ }
+ if (! reqListNames)
+ continue;
+ const Uint32 size = strlen(tablePtr.p->tableName) + 1;
+ conf->tableData[pos] = size;
+ pos++;
+ if (pos >= ListTablesConf::DataLength) {
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::SignalLength, JBB);
+ conf->counter++;
+ pos = 0;
+ }
+ Uint32 k = 0;
+ while (k < size) {
+ char* p = (char*)&conf->tableData[pos];
+ for (Uint32 j = 0; j < 4; j++) {
+ if (k < size)
+ *p++ = tablePtr.p->tableName[k++];
+ else
+ *p++ = 0;
+ }
+ pos++;
+ if (pos >= ListTablesConf::DataLength) {
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::SignalLength, JBB);
+ conf->counter++;
+ pos = 0;
+ }
+ }
+ }
+ // XXX merge with above somehow
+ for (i = 0; i < c_triggerRecordPool.getSize(); i++) {
+ if (reqListIndexes)
+ break;
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, i);
+ if (triggerPtr.p->triggerState == TriggerRecord::TS_NOT_DEFINED)
+ continue;
+ // constant 10 hardcoded
+ Uint32 type = 10 + triggerPtr.p->triggerType;
+ if (reqTableType != 0 && reqTableType != type)
+ continue;
+ conf->tableData[pos] = 0;
+ conf->setTableId(pos, triggerPtr.i);
+ conf->setTableType(pos, type);
+ switch (triggerPtr.p->triggerState) {
+ case TriggerRecord::TS_OFFLINE:
+ conf->setTableState(pos, DictTabInfo::StateOffline);
+ break;
+ case TriggerRecord::TS_ONLINE:
+ conf->setTableState(pos, DictTabInfo::StateOnline);
+ break;
+ default:
+ conf->setTableState(pos, DictTabInfo::StateBroken);
+ break;
+ }
+ conf->setTableStore(pos, DictTabInfo::StoreTemporary);
+ pos++;
+ if (pos >= ListTablesConf::DataLength) {
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::SignalLength, JBB);
+ conf->counter++;
+ pos = 0;
+ }
+ if (! reqListNames)
+ continue;
+ const Uint32 size = strlen(triggerPtr.p->triggerName) + 1;
+ conf->tableData[pos] = size;
+ pos++;
+ if (pos >= ListTablesConf::DataLength) {
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::SignalLength, JBB);
+ conf->counter++;
+ pos = 0;
+ }
+ Uint32 k = 0;
+ while (k < size) {
+ char* p = (char*)&conf->tableData[pos];
+ for (Uint32 j = 0; j < 4; j++) {
+ if (k < size)
+ *p++ = triggerPtr.p->triggerName[k++];
+ else
+ *p++ = 0;
+ }
+ pos++;
+ if (pos >= ListTablesConf::DataLength) {
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::SignalLength, JBB);
+ conf->counter++;
+ pos = 0;
+ }
+ }
+ }
+ // last signal must have less than max length
+ sendSignal(senderRef, GSN_LIST_TABLES_CONF, signal,
+ ListTablesConf::HeaderLength + pos, JBB);
+}
+
+/**
+ * MODULE: Create index
+ *
+ * Create index in DICT via create table operation. Then invoke alter
+ * index opearation to online the index.
+ *
+ * Request type in CREATE_INDX signals:
+ *
+ * RT_USER - from API to DICT master
+ * RT_DICT_PREPARE - prepare participants
+ * RT_DICT_COMMIT - commit participants
+ * RT_TC - create index in TC (part of alter index operation)
+ */
+
+void
+Dbdict::execCREATE_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ CreateIndxReq* const req = (CreateIndxReq*)signal->getDataPtrSend();
+ OpCreateIndexPtr opPtr;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const CreateIndxReq::RequestType requestType = req->getRequestType();
+ if (requestType == CreateIndxReq::RT_USER) {
+ jam();
+ if (! assembleFragments(signal)) {
+ jam();
+ return;
+ }
+ if (signal->getLength() == CreateIndxReq::SignalLength) {
+ jam();
+ if (getOwnNodeId() != c_masterNodeId) {
+ jam();
+
+ releaseSections(signal);
+ OpCreateIndex opBusy;
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = 0;
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_PREPARE;
+ opPtr.p->m_errorCode = CreateIndxRef::NotMaster;
+ opPtr.p->m_errorLine = __LINE__;
+ opPtr.p->m_errorNode = c_masterNodeId;
+ createIndex_sendReply(signal, opPtr, true);
+ return;
+ }
+
+ // forward initial request plus operation key to all
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_CREATE_INDX_REQ,
+ signal, CreateIndxReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == CreateIndxReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpCreateIndex opBusy;
+ if (! c_opCreateIndex.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ releaseSections(signal);
+ createIndex_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opCreateIndex.add(opPtr);
+ // save attribute list
+ SegmentedSectionPtr ssPtr;
+ signal->getSection(ssPtr, CreateIndxReq::ATTRIBUTE_LIST_SECTION);
+ SimplePropertiesSectionReader r0(ssPtr, getSectionSegmentPool());
+ r0.reset(); // undo implicit first()
+ if (! r0.getWord(&opPtr.p->m_attrList.sz) ||
+ ! r0.getWords(opPtr.p->m_attrList.id, opPtr.p->m_attrList.sz)) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidName;
+ opPtr.p->m_errorLine = __LINE__;
+ releaseSections(signal);
+ createIndex_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ // save name and index table properties
+ signal->getSection(ssPtr, CreateIndxReq::INDEX_NAME_SECTION);
+ SimplePropertiesSectionReader r1(ssPtr, getSectionSegmentPool());
+ DictTabInfo::Table tableDesc;
+ tableDesc.init();
+ SimpleProperties::UnpackStatus status = SimpleProperties::unpack(
+ r1, &tableDesc,
+ DictTabInfo::TableMapping, DictTabInfo::TableMappingSize,
+ true, true);
+ if (status != SimpleProperties::Eof) {
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidName;
+ opPtr.p->m_errorLine = __LINE__;
+ releaseSections(signal);
+ createIndex_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ memcpy(opPtr.p->m_indexName, tableDesc.TableName, MAX_TAB_NAME_SIZE);
+ opPtr.p->m_storedIndex = tableDesc.TableLoggedFlag;
+ releaseSections(signal);
+ // master expects to hear from all
+ if (opPtr.p->m_isMaster)
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ createIndex_slavePrepare(signal, opPtr);
+ createIndex_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opCreateIndex.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == CreateIndxReq::RT_DICT_COMMIT ||
+ requestType == CreateIndxReq::RT_DICT_ABORT) {
+ jam();
+ if (requestType == CreateIndxReq::RT_DICT_COMMIT) {
+ opPtr.p->m_request.setIndexId(req->getIndexId());
+ opPtr.p->m_request.setIndexVersion(req->getIndexVersion());
+ createIndex_slaveCommit(signal, opPtr);
+ } else {
+ createIndex_slaveAbort(signal, opPtr);
+ }
+ createIndex_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opCreateIndex.release(opPtr);
+ return;
+ }
+ }
+ jam();
+ // return to sender
+ releaseSections(signal);
+ OpCreateIndex opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = CreateIndxRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ createIndex_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::execCREATE_INDX_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+ CreateIndxConf* conf = (CreateIndxConf*)signal->getDataPtrSend();
+ createIndex_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execCREATE_INDX_REF(Signal* signal)
+{
+ jamEntry();
+ CreateIndxRef* ref = (CreateIndxRef*)signal->getDataPtrSend();
+ createIndex_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::createIndex_recvReply(Signal* signal, const CreateIndxConf* conf,
+ const CreateIndxRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const CreateIndxReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == CreateIndxReq::RT_TC) {
+ jam();
+ // part of alter index operation
+ OpAlterIndexPtr opPtr;
+ c_opAlterIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterIndex_fromCreateTc(signal, opPtr);
+ return;
+ }
+ OpCreateIndexPtr opPtr;
+ c_opCreateIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ opPtr.p->setError(ref);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == CreateIndxReq::RT_DICT_COMMIT ||
+ requestType == CreateIndxReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ createIndex_sendReply(signal, opPtr, true);
+ c_opCreateIndex.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_ABORT;
+ createIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == CreateIndxReq::RT_DICT_PREPARE) {
+ jam();
+ // start index table create
+ createIndex_toCreateTable(signal, opPtr);
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_ABORT;
+ createIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::createIndex_slavePrepare(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+}
+
+void
+Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ Uint32 attrid_map[MAX_ATTRIBUTES_IN_INDEX];
+ Uint32 k;
+ jam();
+ const CreateIndxReq* const req = &opPtr.p->m_request;
+ // signal data writer
+ Uint32* wbuffer = &c_indexPage.word[0];
+ LinearWriter w(wbuffer, sizeof(c_indexPage) >> 2);
+ w.first();
+ // get table being indexed
+ if (! (req->getTableId() < c_tableRecordPool.getSize())) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidPrimaryTable;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, req->getTableId());
+ if (tablePtr.p->tabState != TableRecord::DEFINED) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidPrimaryTable;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ if (! tablePtr.p->isTable()) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidPrimaryTable;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ // compute index table record
+ TableRecord indexRec;
+ TableRecordPtr indexPtr;
+ indexPtr.i = RNIL; // invalid
+ indexPtr.p = &indexRec;
+ initialiseTableRecord(indexPtr);
+ if (req->getIndexType() == DictTabInfo::UniqueHashIndex) {
+ indexPtr.p->storedTable = opPtr.p->m_storedIndex;
+ indexPtr.p->fragmentType = tablePtr.p->fragmentType;
+ } else if (req->getIndexType() == DictTabInfo::OrderedIndex) {
+ // first version will not supported logging
+ if (opPtr.p->m_storedIndex) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidIndexType;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ indexPtr.p->storedTable = false;
+ // follows table fragmentation
+ indexPtr.p->fragmentType = tablePtr.p->fragmentType;
+ } else {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidIndexType;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ indexPtr.p->tableType = (DictTabInfo::TableType)req->getIndexType();
+ indexPtr.p->primaryTableId = req->getTableId();
+ indexPtr.p->noOfAttributes = opPtr.p->m_attrList.sz;
+ indexPtr.p->tupKeyLength = 0;
+ if (indexPtr.p->noOfAttributes == 0) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::InvalidIndexType;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ // tree node size in words (make configurable later)
+ indexPtr.p->tupKeyLength = MAX_TTREE_NODE_SIZE;
+ }
+
+ AttributeMask mask;
+ mask.clear();
+ for (k = 0; k < opPtr.p->m_attrList.sz; k++) {
+ jam();
+ unsigned current_id= opPtr.p->m_attrList.id[k];
+ AttributeRecord* aRec= NULL;
+ Uint32 tAttr= tablePtr.p->firstAttribute;
+ for (; tAttr != RNIL; tAttr= aRec->nextAttrInTable)
+ {
+ aRec = c_attributeRecordPool.getPtr(tAttr);
+ if (aRec->attributeId != current_id)
+ continue;
+ jam();
+ break;
+ }
+ if (tAttr == RNIL) {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ if (mask.get(current_id))
+ {
+ jam();
+ opPtr.p->m_errorCode = CreateIndxRef::DuplicateAttributes;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ mask.set(current_id);
+
+ const Uint32 a = aRec->attributeDescriptor;
+ unsigned kk= k;
+ if (indexPtr.p->isHashIndex()) {
+ const Uint32 s1 = AttributeDescriptor::getSize(a);
+ const Uint32 s2 = AttributeDescriptor::getArraySize(a);
+ indexPtr.p->tupKeyLength += ((1 << s1) * s2 + 31) >> 5;
+ // reorder the attributes according to the tableid order
+ // for unque indexes
+ for (; kk > 0 && current_id < attrid_map[kk-1]>>16; kk--)
+ attrid_map[kk]= attrid_map[kk-1];
+ }
+ attrid_map[kk]= k | (current_id << 16);
+ }
+ indexPtr.p->noOfPrimkey = indexPtr.p->noOfAttributes;
+ // plus concatenated primary table key attribute
+ indexPtr.p->noOfAttributes += 1;
+ indexPtr.p->noOfNullAttr = 0;
+ // write index table
+ w.add(DictTabInfo::TableName, opPtr.p->m_indexName);
+ w.add(DictTabInfo::TableLoggedFlag, indexPtr.p->storedTable);
+ w.add(DictTabInfo::FragmentTypeVal, indexPtr.p->fragmentType);
+ w.add(DictTabInfo::TableTypeVal, indexPtr.p->tableType);
+ w.add(DictTabInfo::PrimaryTable, tablePtr.p->tableName);
+ w.add(DictTabInfo::PrimaryTableId, tablePtr.i);
+ w.add(DictTabInfo::NoOfAttributes, indexPtr.p->noOfAttributes);
+ w.add(DictTabInfo::NoOfKeyAttr, indexPtr.p->noOfPrimkey);
+ w.add(DictTabInfo::NoOfNullable, indexPtr.p->noOfNullAttr);
+ w.add(DictTabInfo::KeyLength, indexPtr.p->tupKeyLength);
+ // write index key attributes
+ AttributeRecordPtr aRecPtr;
+ c_attributeRecordPool.getPtr(aRecPtr, tablePtr.p->firstAttribute);
+ for (k = 0; k < opPtr.p->m_attrList.sz; k++) {
+ // insert the attributes in the order decided above in attrid_map
+ // k is new order, current_id is in previous order
+ // ToDo: make sure "current_id" is stored with the table and
+ // passed up to NdbDictionary
+ unsigned current_id= opPtr.p->m_attrList.id[attrid_map[k] & 0xffff];
+ jam();
+ for (Uint32 tAttr = tablePtr.p->firstAttribute; tAttr != RNIL; ) {
+ AttributeRecord* aRec = c_attributeRecordPool.getPtr(tAttr);
+ tAttr = aRec->nextAttrInTable;
+ if (aRec->attributeId != current_id)
+ continue;
+ jam();
+ const Uint32 a = aRec->attributeDescriptor;
+ bool isNullable = AttributeDescriptor::getNullable(a);
+ Uint32 attrType = AttributeDescriptor::getType(a);
+ w.add(DictTabInfo::AttributeName, aRec->attributeName);
+ w.add(DictTabInfo::AttributeId, k);
+ if (indexPtr.p->isHashIndex()) {
+ w.add(DictTabInfo::AttributeKeyFlag, (Uint32)true);
+ w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false);
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ w.add(DictTabInfo::AttributeKeyFlag, (Uint32)false);
+ w.add(DictTabInfo::AttributeNullableFlag, (Uint32)isNullable);
+ }
+ w.add(DictTabInfo::AttributeExtType, attrType);
+ w.add(DictTabInfo::AttributeExtPrecision, aRec->extPrecision);
+ w.add(DictTabInfo::AttributeExtScale, aRec->extScale);
+ w.add(DictTabInfo::AttributeExtLength, aRec->extLength);
+ w.add(DictTabInfo::AttributeEnd, (Uint32)true);
+ }
+ }
+ if (indexPtr.p->isHashIndex()) {
+ jam();
+ // write concatenated primary table key attribute
+ w.add(DictTabInfo::AttributeName, "NDB$PK");
+ w.add(DictTabInfo::AttributeId, opPtr.p->m_attrList.sz);
+ w.add(DictTabInfo::AttributeKeyFlag, (Uint32)false);
+ w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false);
+ w.add(DictTabInfo::AttributeExtType, (Uint32)DictTabInfo::ExtUnsigned);
+ w.add(DictTabInfo::AttributeExtLength, tablePtr.p->tupKeyLength);
+ w.add(DictTabInfo::AttributeEnd, (Uint32)true);
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ jam();
+ // write index tree node as Uint32 array attribute
+ w.add(DictTabInfo::AttributeName, "NDB$TNODE");
+ w.add(DictTabInfo::AttributeId, opPtr.p->m_attrList.sz);
+ w.add(DictTabInfo::AttributeKeyFlag, (Uint32)true);
+ w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false);
+ w.add(DictTabInfo::AttributeExtType, (Uint32)DictTabInfo::ExtUnsigned);
+ w.add(DictTabInfo::AttributeExtLength, indexPtr.p->tupKeyLength);
+ w.add(DictTabInfo::AttributeEnd, (Uint32)true);
+ }
+ // finish
+ w.add(DictTabInfo::TableEnd, (Uint32)true);
+ // remember to...
+ releaseSections(signal);
+ // send create index table request
+ CreateTableReq * const cre = (CreateTableReq*)signal->getDataPtrSend();
+ cre->senderRef = reference();
+ cre->senderData = opPtr.p->key;
+ LinearSectionPtr lsPtr[3];
+ lsPtr[0].p = wbuffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(DBDICT_REF, GSN_CREATE_TABLE_REQ,
+ signal, CreateTableReq::SignalLength, JBB, lsPtr, 1);
+}
+
+void
+Dbdict::createIndex_fromCreateTable(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_ABORT;
+ createIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (! opPtr.p->m_request.getOnline()) {
+ jam();
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_COMMIT;
+ createIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ createIndex_toAlterIndex(signal, opPtr);
+}
+
+void
+Dbdict::createIndex_toAlterIndex(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+ AlterIndxReq* const req = (AlterIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(AlterIndxReq::RT_CREATE_INDEX);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ req->setTableId(opPtr.p->m_request.getTableId());
+ req->setIndexId(opPtr.p->m_request.getIndexId());
+ req->setIndexVersion(opPtr.p->m_request.getIndexVersion());
+ req->setOnline(true);
+ sendSignal(reference(), GSN_ALTER_INDX_REQ,
+ signal, AlterIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::createIndex_fromAlterIndex(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_ABORT;
+ createIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = CreateIndxReq::RT_DICT_COMMIT;
+ createIndex_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::createIndex_slaveCommit(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+ const Uint32 indexId = opPtr.p->m_request.getIndexId();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, indexId);
+ if (! opPtr.p->m_request.getOnline()) {
+ ndbrequire(indexPtr.p->indexState == TableRecord::IS_UNDEFINED);
+ indexPtr.p->indexState = TableRecord::IS_OFFLINE;
+ } else {
+ ndbrequire(indexPtr.p->indexState == TableRecord::IS_ONLINE);
+ }
+}
+
+void
+Dbdict::createIndex_slaveAbort(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+ CreateIndxReq* const req = &opPtr.p->m_request;
+ const Uint32 indexId = req->getIndexId();
+ if (indexId >= c_tableRecordPool.getSize()) {
+ jam();
+ return;
+ }
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, indexId);
+ if (! indexPtr.p->isIndex()) {
+ jam();
+ return;
+ }
+ indexPtr.p->indexState = TableRecord::IS_BROKEN;
+}
+
+void
+Dbdict::createIndex_sendSlaveReq(Signal* signal, OpCreateIndexPtr opPtr)
+{
+ jam();
+ CreateIndxReq* const req = (CreateIndxReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_CREATE_INDX_REQ,
+ signal, CreateIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::createIndex_sendReply(Signal* signal, OpCreateIndexPtr opPtr,
+ bool toUser)
+{
+ CreateIndxRef* rep = (CreateIndxRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_CREATE_INDX_CONF;
+ Uint32 length = CreateIndxConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == CreateIndxReq::RT_DICT_ABORT)
+ sendRef = false;
+ } else {
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = CreateIndxConf::SignalLength;
+ }
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setIndexId(opPtr.p->m_request.getIndexId());
+ rep->setIndexVersion(opPtr.p->m_request.getIndexVersion());
+ if (sendRef) {
+ if (opPtr.p->m_errorNode == 0)
+ opPtr.p->m_errorNode = getOwnNodeId();
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->setErrorLine(opPtr.p->m_errorLine);
+ rep->setErrorNode(opPtr.p->m_errorNode);
+ gsn = GSN_CREATE_INDX_REF;
+ length = CreateIndxRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/**
+ * MODULE: Drop index.
+ *
+ * Drop index. First alters the index offline (i.e. drops metadata in
+ * other blocks) and then drops the index table.
+ */
+
+void
+Dbdict::execDROP_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ DropIndxReq* const req = (DropIndxReq*)signal->getDataPtrSend();
+ OpDropIndexPtr opPtr;
+
+ int err = DropIndxRef::BadRequestType;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const DropIndxReq::RequestType requestType = req->getRequestType();
+ if (requestType == DropIndxReq::RT_USER) {
+ jam();
+ if (signal->getLength() == DropIndxReq::SignalLength) {
+ jam();
+ if (getOwnNodeId() != c_masterNodeId) {
+ jam();
+
+ err = DropIndxRef::NotMaster;
+ goto error;
+ }
+ // forward initial request plus operation key to all
+ Uint32 indexId= req->getIndexId();
+ Uint32 indexVersion= req->getIndexVersion();
+ TableRecordPtr tmp;
+ int res = getMetaTablePtr(tmp, indexId, indexVersion);
+ switch(res){
+ case MetaData::InvalidArgument:
+ err = DropIndxRef::IndexNotFound;
+ goto error;
+ case MetaData::TableNotFound:
+ case MetaData::InvalidTableVersion:
+ err = DropIndxRef::InvalidIndexVersion;
+ goto error;
+ }
+
+ if (! tmp.p->isIndex()) {
+ jam();
+ err = DropIndxRef::NotAnIndex;
+ goto error;
+ }
+
+ if (tmp.p->indexState == TableRecord::IS_DROPPING){
+ jam();
+ err = DropIndxRef::IndexNotFound;
+ goto error;
+ }
+
+ tmp.p->indexState = TableRecord::IS_DROPPING;
+
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_DROP_INDX_REQ,
+ signal, DropIndxReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == DropIndxReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpDropIndex opBusy;
+ if (! c_opDropIndex.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = DropIndxReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = DropIndxRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ dropIndex_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opDropIndex.add(opPtr);
+ // master expects to hear from all
+ if (opPtr.p->m_isMaster)
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ dropIndex_slavePrepare(signal, opPtr);
+ dropIndex_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opDropIndex.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == DropIndxReq::RT_DICT_COMMIT ||
+ requestType == DropIndxReq::RT_DICT_ABORT) {
+ jam();
+ if (requestType == DropIndxReq::RT_DICT_COMMIT)
+ dropIndex_slaveCommit(signal, opPtr);
+ else
+ dropIndex_slaveAbort(signal, opPtr);
+ dropIndex_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opDropIndex.release(opPtr);
+ return;
+ }
+ }
+error:
+ jam();
+ // return to sender
+ OpDropIndex opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = (DropIndxRef::ErrorCode)err;
+ opPtr.p->m_errorLine = __LINE__;
+ opPtr.p->m_errorNode = c_masterNodeId;
+ dropIndex_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::execDROP_INDX_CONF(Signal* signal)
+{
+ jamEntry();
+ DropIndxConf* conf = (DropIndxConf*)signal->getDataPtrSend();
+ dropIndex_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execDROP_INDX_REF(Signal* signal)
+{
+ jamEntry();
+ DropIndxRef* ref = (DropIndxRef*)signal->getDataPtrSend();
+ dropIndex_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::dropIndex_recvReply(Signal* signal, const DropIndxConf* conf,
+ const DropIndxRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const DropIndxReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == DropIndxReq::RT_TC) {
+ jam();
+ // part of alter index operation
+ OpAlterIndexPtr opPtr;
+ c_opAlterIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterIndex_fromDropTc(signal, opPtr);
+ return;
+ }
+ OpDropIndexPtr opPtr;
+ c_opDropIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ opPtr.p->setError(ref);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == DropIndxReq::RT_DICT_COMMIT ||
+ requestType == DropIndxReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ dropIndex_sendReply(signal, opPtr, true);
+ c_opDropIndex.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = DropIndxReq::RT_DICT_ABORT;
+ dropIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == DropIndxReq::RT_DICT_PREPARE) {
+ jam();
+ // start alter offline
+ dropIndex_toAlterIndex(signal, opPtr);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::dropIndex_slavePrepare(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+ DropIndxReq* const req = &opPtr.p->m_request;
+ // check index exists
+ TableRecordPtr indexPtr;
+ if (! (req->getIndexId() < c_tableRecordPool.getSize())) {
+ jam();
+ opPtr.p->m_errorCode = DropIndxRef::IndexNotFound;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ c_tableRecordPool.getPtr(indexPtr, req->getIndexId());
+ if (indexPtr.p->tabState != TableRecord::DEFINED) {
+ jam();
+ opPtr.p->m_errorCode = DropIndxRef::IndexNotFound;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ if (! indexPtr.p->isIndex()) {
+ jam();
+ opPtr.p->m_errorCode = DropIndxRef::NotAnIndex;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ // ignore incoming primary table id
+ req->setTableId(indexPtr.p->primaryTableId);
+}
+
+void
+Dbdict::dropIndex_toAlterIndex(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+ AlterIndxReq* const req = (AlterIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(AlterIndxReq::RT_DROP_INDEX);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ req->setTableId(opPtr.p->m_request.getTableId());
+ req->setIndexId(opPtr.p->m_request.getIndexId());
+ req->setIndexVersion(opPtr.p->m_request.getIndexVersion());
+ req->setOnline(false);
+ sendSignal(reference(), GSN_ALTER_INDX_REQ,
+ signal, AlterIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::dropIndex_fromAlterIndex(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = DropIndxReq::RT_DICT_ABORT;
+ dropIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ dropIndex_toDropTable(signal, opPtr);
+}
+
+void
+Dbdict::dropIndex_toDropTable(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+ DropTableReq* const req = (DropTableReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = opPtr.p->key;
+ req->tableId = opPtr.p->m_request.getIndexId();
+ req->tableVersion = opPtr.p->m_request.getIndexVersion();
+ sendSignal(reference(), GSN_DROP_TABLE_REQ,
+ signal,DropTableReq::SignalLength, JBB);
+}
+
+void
+Dbdict::dropIndex_fromDropTable(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = DropIndxReq::RT_DICT_ABORT;
+ dropIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = DropIndxReq::RT_DICT_COMMIT;
+ dropIndex_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::dropIndex_slaveCommit(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+}
+
+void
+Dbdict::dropIndex_slaveAbort(Signal* signal, OpDropIndexPtr opPtr)
+{
+ jam();
+ DropIndxReq* const req = &opPtr.p->m_request;
+ const Uint32 indexId = req->getIndexId();
+ if (indexId >= c_tableRecordPool.getSize()) {
+ jam();
+ return;
+ }
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, indexId);
+ indexPtr.p->indexState = TableRecord::IS_BROKEN;
+}
+
+void
+Dbdict::dropIndex_sendSlaveReq(Signal* signal, OpDropIndexPtr opPtr)
+{
+ DropIndxReq* const req = (DropIndxReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_DROP_INDX_REQ,
+ signal, DropIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::dropIndex_sendReply(Signal* signal, OpDropIndexPtr opPtr,
+ bool toUser)
+{
+ DropIndxRef* rep = (DropIndxRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_DROP_INDX_CONF;
+ Uint32 length = DropIndxConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == DropIndxReq::RT_DICT_ABORT)
+ sendRef = false;
+ } else {
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = DropIndxConf::SignalLength;
+ }
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setIndexId(opPtr.p->m_request.getIndexId());
+ rep->setIndexVersion(opPtr.p->m_request.getIndexVersion());
+ if (sendRef) {
+ if (opPtr.p->m_errorNode == 0)
+ opPtr.p->m_errorNode = getOwnNodeId();
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->setErrorLine(opPtr.p->m_errorLine);
+ rep->setErrorNode(opPtr.p->m_errorNode);
+ gsn = GSN_DROP_INDX_REF;
+ length = DropIndxRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/*****************************************************
+ *
+ * Util signalling
+ *
+ *****************************************************/
+
+int
+Dbdict::sendSignalUtilReq(Callback *pcallback,
+ BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections)
+{
+ jam();
+ EVENT_TRACE;
+ OpSignalUtilPtr utilRecPtr;
+
+ // Seize a Util Send record
+ if (!c_opSignalUtil.seize(utilRecPtr)) {
+ // Failed to allocate util record
+ return -1;
+ }
+ utilRecPtr.p->m_callback = *pcallback;
+
+ // should work for all util signal classes
+ UtilPrepareReq *req = (UtilPrepareReq*)signal->getDataPtrSend();
+ utilRecPtr.p->m_userData = req->getSenderData();
+ req->setSenderData(utilRecPtr.i);
+
+ if (ptr) {
+ jam();
+ sendSignal(ref, gsn, signal, length, jbuf, ptr, noOfSections);
+ } else {
+ jam();
+ sendSignal(ref, gsn, signal, length, jbuf);
+ }
+
+ return 0;
+}
+
+int
+Dbdict::recvSignalUtilReq(Signal* signal, Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ UtilPrepareConf * const req = (UtilPrepareConf*)signal->getDataPtr();
+ OpSignalUtilPtr utilRecPtr;
+ utilRecPtr.i = req->getSenderData();
+ if ((utilRecPtr.p = c_opSignalUtil.getPtr(utilRecPtr.i)) == NULL) {
+ jam();
+ return -1;
+ }
+
+ req->setSenderData(utilRecPtr.p->m_userData);
+ Callback c = utilRecPtr.p->m_callback;
+ c_opSignalUtil.release(utilRecPtr);
+
+ execute(signal, c, returnCode);
+ return 0;
+}
+
+void Dbdict::execUTIL_PREPARE_CONF(Signal *signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ ndbrequire(recvSignalUtilReq(signal, 0) == 0);
+}
+
+void
+Dbdict::execUTIL_PREPARE_REF(Signal *signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ ndbrequire(recvSignalUtilReq(signal, 1) == 0);
+}
+
+void Dbdict::execUTIL_EXECUTE_CONF(Signal *signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ ndbrequire(recvSignalUtilReq(signal, 0) == 0);
+}
+
+void Dbdict::execUTIL_EXECUTE_REF(Signal *signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+
+#ifdef EVENT_DEBUG
+ UtilExecuteRef * ref = (UtilExecuteRef *)signal->getDataPtrSend();
+
+ ndbout_c("execUTIL_EXECUTE_REF");
+ ndbout_c("senderData %u",ref->getSenderData());
+ ndbout_c("errorCode %u",ref->getErrorCode());
+ ndbout_c("TCErrorCode %u",ref->getTCErrorCode());
+#endif
+
+ ndbrequire(recvSignalUtilReq(signal, 1) == 0);
+}
+void Dbdict::execUTIL_RELEASE_CONF(Signal *signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ ndbrequire(false);
+ ndbrequire(recvSignalUtilReq(signal, 0) == 0);
+}
+void Dbdict::execUTIL_RELEASE_REF(Signal *signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ ndbrequire(false);
+ ndbrequire(recvSignalUtilReq(signal, 1) == 0);
+}
+
+/**
+ * MODULE: Create event
+ *
+ * Create event in DICT.
+ *
+ *
+ * Request type in CREATE_EVNT signals:
+ *
+ * Signalflow see Dbdict.txt
+ *
+ */
+
+/*****************************************************************
+ *
+ * Systable stuff
+ *
+ */
+
+const Uint32 Dbdict::sysTab_NDBEVENTS_0_szs[EVENT_SYSTEM_TABLE_LENGTH] = {
+ sizeof(((sysTab_NDBEVENTS_0*)0)->NAME),
+ sizeof(((sysTab_NDBEVENTS_0*)0)->EVENT_TYPE),
+ sizeof(((sysTab_NDBEVENTS_0*)0)->TABLE_NAME),
+ sizeof(((sysTab_NDBEVENTS_0*)0)->ATTRIBUTE_MASK),
+ sizeof(((sysTab_NDBEVENTS_0*)0)->SUBID),
+ sizeof(((sysTab_NDBEVENTS_0*)0)->SUBKEY)
+};
+
+void
+Dbdict::prepareTransactionEventSysTable (Callback *pcallback,
+ Signal* signal,
+ Uint32 senderData,
+ UtilPrepareReq::OperationTypeValue prepReq)
+{
+ // find table id for event system table
+ TableRecord keyRecord;
+ strcpy(keyRecord.tableName, EVENT_SYSTEM_TABLE_NAME);
+
+ TableRecordPtr tablePtr;
+ c_tableRecordHash.find(tablePtr, keyRecord);
+
+ ndbrequire(tablePtr.i != RNIL); // system table must exist
+
+ Uint32 tableId = tablePtr.p->tableId; /* System table */
+ Uint32 noAttr = tablePtr.p->noOfAttributes;
+ ndbrequire(noAttr == EVENT_SYSTEM_TABLE_LENGTH);
+
+ switch (prepReq) {
+ case UtilPrepareReq::Update:
+ case UtilPrepareReq::Insert:
+ case UtilPrepareReq::Write:
+ case UtilPrepareReq::Read:
+ jam();
+ break;
+ case UtilPrepareReq::Delete:
+ jam();
+ noAttr = 1; // only involves Primary key which should be the first
+ break;
+ }
+ prepareUtilTransaction(pcallback, signal, senderData, tableId, NULL,
+ prepReq, noAttr, NULL, NULL);
+}
+
+void
+Dbdict::prepareUtilTransaction(Callback *pcallback,
+ Signal* signal,
+ Uint32 senderData,
+ Uint32 tableId,
+ const char* tableName,
+ UtilPrepareReq::OperationTypeValue prepReq,
+ Uint32 noAttr,
+ Uint32 attrIds[],
+ const char *attrNames[])
+{
+ jam();
+ EVENT_TRACE;
+
+ UtilPrepareReq * utilPrepareReq =
+ (UtilPrepareReq *)signal->getDataPtrSend();
+
+ utilPrepareReq->setSenderRef(reference());
+ utilPrepareReq->setSenderData(senderData);
+
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, prepReq);
+ if (tableName) {
+ jam();
+ w.add(UtilPrepareReq::TableName, tableName);
+ } else {
+ jam();
+ w.add(UtilPrepareReq::TableId, tableId);
+ }
+ for(Uint32 i = 0; i < noAttr; i++)
+ if (tableName) {
+ jam();
+ w.add(UtilPrepareReq::AttributeName, attrNames[i]);
+ } else {
+ if (attrIds) {
+ jam();
+ w.add(UtilPrepareReq::AttributeId, attrIds[i]);
+ } else {
+ jam();
+ w.add(UtilPrepareReq::AttributeId, i);
+ }
+ }
+#ifdef EVENT_DEBUG
+ // Debugging
+ SimplePropertiesLinearReader reader(propPage, w.getWordsUsed());
+ printf("Dict::prepareInsertTransactions: Sent SimpleProperties:\n");
+ reader.printAll(ndbout);
+#endif
+
+ struct LinearSectionPtr sectionsPtr[UtilPrepareReq::NoOfSections];
+ sectionsPtr[UtilPrepareReq::PROPERTIES_SECTION].p = propPage;
+ sectionsPtr[UtilPrepareReq::PROPERTIES_SECTION].sz = w.getWordsUsed();
+
+ sendSignalUtilReq(pcallback, DBUTIL_REF, GSN_UTIL_PREPARE_REQ, signal,
+ UtilPrepareReq::SignalLength, JBB,
+ sectionsPtr, UtilPrepareReq::NoOfSections);
+}
+
+/*****************************************************************
+ *
+ * CREATE_EVNT_REQ has three types RT_CREATE, RT_GET (from user)
+ * and RT_DICT_AFTER_GET send from master DICT to slaves
+ *
+ * This function just dscpaches these to
+ *
+ * createEvent_RT_USER_CREATE
+ * createEvent_RT_USER_GET
+ * createEvent_RT_DICT_AFTER_GET
+ *
+ * repectively
+ *
+ */
+
+void
+Dbdict::execCREATE_EVNT_REQ(Signal* signal)
+{
+ jamEntry();
+
+#if 0
+ {
+ SafeCounterHandle handle;
+ {
+ SafeCounter tmp(c_counterMgr, handle);
+ tmp.init<CreateEvntRef>(CMVMI, GSN_DUMP_STATE_ORD, /* senderData */ 13);
+ tmp.clearWaitingFor();
+ tmp.setWaitingFor(3);
+ ndbrequire(!tmp.done());
+ ndbout_c("Allocted");
+ }
+ ndbrequire(!handle.done());
+ {
+ SafeCounter tmp(c_counterMgr, handle);
+ tmp.clearWaitingFor(3);
+ ndbrequire(tmp.done());
+ ndbout_c("Deallocted");
+ }
+ ndbrequire(handle.done());
+ }
+ {
+ NodeBitmask nodes;
+ nodes.clear();
+
+ nodes.set(2);
+ nodes.set(3);
+ nodes.set(4);
+ nodes.set(5);
+
+ {
+ Uint32 i = 0;
+ while((i = nodes.find(i)) != NodeBitmask::NotFound){
+ ndbout_c("1 Node id = %u", i);
+ i++;
+ }
+ }
+
+ NodeReceiverGroup rg(DBDICT, nodes);
+ RequestTracker rt2;
+ ndbrequire(rt2.done());
+ ndbrequire(!rt2.hasRef());
+ ndbrequire(!rt2.hasConf());
+ rt2.init<CreateEvntRef>(c_counterMgr, rg, GSN_CREATE_EVNT_REF, 13);
+
+ RequestTracker rt3;
+ rt3.init<CreateEvntRef>(c_counterMgr, rg, GSN_CREATE_EVNT_REF, 13);
+
+ ndbrequire(!rt2.done());
+ ndbrequire(!rt3.done());
+
+ rt2.reportRef(c_counterMgr, 2);
+ rt3.reportConf(c_counterMgr, 2);
+
+ ndbrequire(!rt2.done());
+ ndbrequire(!rt3.done());
+
+ rt2.reportConf(c_counterMgr, 3);
+ rt3.reportConf(c_counterMgr, 3);
+
+ ndbrequire(!rt2.done());
+ ndbrequire(!rt3.done());
+
+ rt2.reportConf(c_counterMgr, 4);
+ rt3.reportConf(c_counterMgr, 4);
+
+ ndbrequire(!rt2.done());
+ ndbrequire(!rt3.done());
+
+ rt2.reportConf(c_counterMgr, 5);
+ rt3.reportConf(c_counterMgr, 5);
+
+ ndbrequire(rt2.done());
+ ndbrequire(rt3.done());
+ }
+#endif
+
+ if (! assembleFragments(signal)) {
+ jam();
+ return;
+ }
+
+ CreateEvntReq *req = (CreateEvntReq*)signal->getDataPtr();
+ const CreateEvntReq::RequestType requestType = req->getRequestType();
+ const Uint32 requestFlag = req->getRequestFlag();
+
+ OpCreateEventPtr evntRecPtr;
+ // Seize a Create Event record
+ if (!c_opCreateEvent.seize(evntRecPtr)) {
+ // Failed to allocate event record
+ jam();
+ releaseSections(signal);
+
+ CreateEvntRef * ret = (CreateEvntRef *)signal->getDataPtrSend();
+ ret->senderRef = reference();
+ ret->setErrorCode(CreateEvntRef::SeizeError);
+ ret->setErrorLine(__LINE__);
+ ret->setErrorNode(reference());
+ sendSignal(signal->senderBlockRef(), GSN_CREATE_EVNT_REF, signal,
+ CreateEvntRef::SignalLength, JBB);
+ return;
+ }
+
+#ifdef EVENT_DEBUG
+ ndbout_c("DBDICT::execCREATE_EVNT_REQ from %u evntRecId = (%d)", refToNode(signal->getSendersBlockRef()), evntRecPtr.i);
+#endif
+
+ ndbrequire(req->getUserRef() == signal->getSendersBlockRef());
+
+ evntRecPtr.p->init(req,this);
+
+ if (requestFlag & (Uint32)CreateEvntReq::RT_DICT_AFTER_GET) {
+ jam();
+ EVENT_TRACE;
+ createEvent_RT_DICT_AFTER_GET(signal, evntRecPtr);
+ return;
+ }
+ if (requestType == CreateEvntReq::RT_USER_GET) {
+ jam();
+ EVENT_TRACE;
+ createEvent_RT_USER_GET(signal, evntRecPtr);
+ return;
+ }
+ if (requestType == CreateEvntReq::RT_USER_CREATE) {
+ jam();
+ EVENT_TRACE;
+ createEvent_RT_USER_CREATE(signal, evntRecPtr);
+ return;
+ }
+
+#ifdef EVENT_DEBUG
+ ndbout << "Dbdict.cpp: Dbdict::execCREATE_EVNT_REQ other" << endl;
+#endif
+ jam();
+ releaseSections(signal);
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+/********************************************************************
+ *
+ * Event creation
+ *
+ *****************************************************************/
+
+void
+Dbdict::createEvent_RT_USER_CREATE(Signal* signal, OpCreateEventPtr evntRecPtr){
+ jam();
+ evntRecPtr.p->m_request.setUserRef(signal->senderBlockRef());
+
+#ifdef EVENT_DEBUG
+ ndbout << "Dbdict.cpp: Dbdict::execCREATE_EVNT_REQ RT_USER" << endl;
+ char buf[128] = {0};
+ AttributeMask mask = evntRecPtr.p->m_request.getAttrListBitmask();
+ mask.getText(buf);
+ ndbout_c("mask = %s", buf);
+#endif
+
+ // Interpret the long signal
+
+ SegmentedSectionPtr ssPtr;
+ // save name and event properties
+ signal->getSection(ssPtr, CreateEvntReq::EVENT_NAME_SECTION);
+
+ SimplePropertiesSectionReader r0(ssPtr, getSectionSegmentPool());
+#ifdef EVENT_DEBUG
+ r0.printAll(ndbout);
+#endif
+ // event name
+ if ((!r0.first()) ||
+ (r0.getValueType() != SimpleProperties::StringValue) ||
+ (r0.getValueLen() <= 0)) {
+ jam();
+ releaseSections(signal);
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+ r0.getString(evntRecPtr.p->m_eventRec.NAME);
+ {
+ int len = strlen(evntRecPtr.p->m_eventRec.NAME);
+ memset(evntRecPtr.p->m_eventRec.NAME+len, 0, MAX_TAB_NAME_SIZE-len);
+#ifdef EVENT_DEBUG
+ printf("CreateEvntReq::RT_USER_CREATE; EventName %s, len %u\n",
+ evntRecPtr.p->m_eventRec.NAME, len);
+ for(int i = 0; i < MAX_TAB_NAME_SIZE/4; i++)
+ printf("H'%.8x ", ((Uint32*)evntRecPtr.p->m_eventRec.NAME)[i]);
+ printf("\n");
+#endif
+ }
+ // table name
+ if ((!r0.next()) ||
+ (r0.getValueType() != SimpleProperties::StringValue) ||
+ (r0.getValueLen() <= 0)) {
+ jam();
+ releaseSections(signal);
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+ r0.getString(evntRecPtr.p->m_eventRec.TABLE_NAME);
+ {
+ int len = strlen(evntRecPtr.p->m_eventRec.TABLE_NAME);
+ memset(evntRecPtr.p->m_eventRec.TABLE_NAME+len, 0, MAX_TAB_NAME_SIZE-len);
+ }
+
+#ifdef EVENT_DEBUG
+ ndbout_c("event name: %s",evntRecPtr.p->m_eventRec.NAME);
+ ndbout_c("table name: %s",evntRecPtr.p->m_eventRec.TABLE_NAME);
+#endif
+
+ releaseSections(signal);
+
+ // Send request to SUMA
+
+ CreateSubscriptionIdReq * sumaIdReq =
+ (CreateSubscriptionIdReq *)signal->getDataPtrSend();
+
+ // make sure we save the original sender for later
+ sumaIdReq->senderData = evntRecPtr.i;
+#ifdef EVENT_DEBUG
+ ndbout << "sumaIdReq->senderData = " << sumaIdReq->senderData << endl;
+#endif
+ sendSignal(SUMA_REF, GSN_CREATE_SUBID_REQ, signal,
+ CreateSubscriptionIdReq::SignalLength, JBB);
+ // we should now return in either execCREATE_SUBID_CONF
+ // or execCREATE_SUBID_REF
+}
+
+void Dbdict::execCREATE_SUBID_REF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ CreateSubscriptionIdRef * const ref =
+ (CreateSubscriptionIdRef *)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+
+ evntRecPtr.i = ref->senderData;
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+void Dbdict::execCREATE_SUBID_CONF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+
+ CreateSubscriptionIdConf const * sumaIdConf =
+ (CreateSubscriptionIdConf *)signal->getDataPtr();
+
+ Uint32 evntRecId = sumaIdConf->senderData;
+ OpCreateEvent *evntRec;
+
+ ndbrequire((evntRec = c_opCreateEvent.getPtr(evntRecId)) != NULL);
+
+ evntRec->m_request.setEventId(sumaIdConf->subscriptionId);
+ evntRec->m_request.setEventKey(sumaIdConf->subscriptionKey);
+
+ releaseSections(signal);
+
+ Callback c = { safe_cast(&Dbdict::createEventUTIL_PREPARE), 0 };
+
+ prepareTransactionEventSysTable(&c, signal, evntRecId,
+ UtilPrepareReq::Insert);
+}
+
+void
+Dbdict::createEventComplete_RT_USER_CREATE(Signal* signal,
+ OpCreateEventPtr evntRecPtr){
+ jam();
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+/*********************************************************************
+ *
+ * UTIL_PREPARE, UTIL_EXECUTE
+ *
+ * insert or read systable NDB$EVENTS_0
+ */
+
+void interpretUtilPrepareErrorCode(UtilPrepareRef::ErrorCode errorCode,
+ bool& temporary, Uint32& line)
+{
+ switch (errorCode) {
+ case UtilPrepareRef::NO_ERROR:
+ jam();
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ case UtilPrepareRef::PREPARE_SEIZE_ERROR:
+ jam();
+ temporary = true;
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ case UtilPrepareRef::PREPARE_PAGES_SEIZE_ERROR:
+ jam();
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ case UtilPrepareRef::PREPARED_OPERATION_SEIZE_ERROR:
+ jam();
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ case UtilPrepareRef::DICT_TAB_INFO_ERROR:
+ jam();
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ case UtilPrepareRef::MISSING_PROPERTIES_SECTION:
+ jam();
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ default:
+ jam();
+ line = __LINE__;
+ EVENT_TRACE;
+ break;
+ }
+}
+
+void
+Dbdict::createEventUTIL_PREPARE(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ if (returnCode == 0) {
+ UtilPrepareConf* const req = (UtilPrepareConf*)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+ jam();
+ evntRecPtr.i = req->getSenderData();
+ const Uint32 prepareId = req->getPrepareId();
+
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ Callback c = { safe_cast(&Dbdict::createEventUTIL_EXECUTE), 0 };
+
+ switch (evntRecPtr.p->m_requestType) {
+ case CreateEvntReq::RT_USER_GET:
+#ifdef EVENT_DEBUG
+ printf("get type = %d\n", CreateEvntReq::RT_USER_GET);
+#endif
+ jam();
+ executeTransEventSysTable(&c, signal,
+ evntRecPtr.i, evntRecPtr.p->m_eventRec,
+ prepareId, UtilPrepareReq::Read);
+ break;
+ case CreateEvntReq::RT_USER_CREATE:
+#ifdef EVENT_DEBUG
+ printf("create type = %d\n", CreateEvntReq::RT_USER_CREATE);
+#endif
+ {
+ evntRecPtr.p->m_eventRec.EVENT_TYPE = evntRecPtr.p->m_request.getEventType();
+ AttributeMask m = evntRecPtr.p->m_request.getAttrListBitmask();
+ memcpy(evntRecPtr.p->m_eventRec.ATTRIBUTE_MASK, &m,
+ sizeof(evntRecPtr.p->m_eventRec.ATTRIBUTE_MASK));
+ evntRecPtr.p->m_eventRec.SUBID = evntRecPtr.p->m_request.getEventId();
+ evntRecPtr.p->m_eventRec.SUBKEY = evntRecPtr.p->m_request.getEventKey();
+ }
+ jam();
+ executeTransEventSysTable(&c, signal,
+ evntRecPtr.i, evntRecPtr.p->m_eventRec,
+ prepareId, UtilPrepareReq::Insert);
+ break;
+ default:
+#ifdef EVENT_DEBUG
+ printf("type = %d\n", evntRecPtr.p->m_requestType);
+ printf("bet type = %d\n", CreateEvntReq::RT_USER_GET);
+ printf("create type = %d\n", CreateEvntReq::RT_USER_CREATE);
+#endif
+ ndbrequire(false);
+ }
+ } else { // returnCode != 0
+ UtilPrepareRef* const ref = (UtilPrepareRef*)signal->getDataPtr();
+
+ const UtilPrepareRef::ErrorCode errorCode =
+ (UtilPrepareRef::ErrorCode)ref->getErrorCode();
+
+ OpCreateEventPtr evntRecPtr;
+ evntRecPtr.i = ref->getSenderData();
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ bool temporary = false;
+ interpretUtilPrepareErrorCode(errorCode,
+ temporary, evntRecPtr.p->m_errorLine);
+ if (temporary) {
+ evntRecPtr.p->m_errorCode =
+ CreateEvntRef::makeTemporary(CreateEvntRef::Undefined);
+ }
+
+ if (evntRecPtr.p->m_errorCode == 0) {
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ }
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+ }
+}
+
+void Dbdict::executeTransEventSysTable(Callback *pcallback, Signal *signal,
+ const Uint32 ptrI,
+ sysTab_NDBEVENTS_0& m_eventRec,
+ const Uint32 prepareId,
+ UtilPrepareReq::OperationTypeValue prepReq)
+{
+ jam();
+ const Uint32 noAttr = EVENT_SYSTEM_TABLE_LENGTH;
+ Uint32 total_len = 0;
+
+ Uint32* attrHdr = signal->theData + 25;
+ Uint32* attrPtr = attrHdr;
+
+ Uint32 id=0;
+ // attribute 0 event name: Primary Key
+ {
+ AttributeHeader::init(attrPtr, id, sysTab_NDBEVENTS_0_szs[id]/4);
+ total_len += sysTab_NDBEVENTS_0_szs[id];
+ attrPtr++; id++;
+ }
+
+ switch (prepReq) {
+ case UtilPrepareReq::Read:
+ jam();
+ EVENT_TRACE;
+ // no more
+ while ( id < noAttr )
+ AttributeHeader::init(attrPtr++, id++, 0);
+ ndbrequire(id == (Uint32) noAttr);
+ break;
+ case UtilPrepareReq::Insert:
+ jam();
+ EVENT_TRACE;
+ while ( id < noAttr ) {
+ AttributeHeader::init(attrPtr, id, sysTab_NDBEVENTS_0_szs[id]/4);
+ total_len += sysTab_NDBEVENTS_0_szs[id];
+ attrPtr++; id++;
+ }
+ ndbrequire(id == (Uint32) noAttr);
+ break;
+ case UtilPrepareReq::Delete:
+ ndbrequire(id == 1);
+ break;
+ default:
+ ndbrequire(false);
+ }
+
+ LinearSectionPtr headerPtr;
+ LinearSectionPtr dataPtr;
+
+ headerPtr.p = attrHdr;
+ headerPtr.sz = noAttr;
+
+ dataPtr.p = (Uint32*)&m_eventRec;
+ dataPtr.sz = total_len/4;
+
+ ndbrequire((total_len == sysTab_NDBEVENTS_0_szs[0]) ||
+ (total_len == sizeof(sysTab_NDBEVENTS_0)));
+
+#if 0
+ printf("Header size %u\n", headerPtr.sz);
+ for(int i = 0; i < (int)headerPtr.sz; i++)
+ printf("H'%.8x ", attrHdr[i]);
+ printf("\n");
+
+ printf("Data size %u\n", dataPtr.sz);
+ for(int i = 0; i < (int)dataPtr.sz; i++)
+ printf("H'%.8x ", dataPage[i]);
+ printf("\n");
+#endif
+
+ executeTransaction(pcallback, signal,
+ ptrI,
+ prepareId,
+ id,
+ headerPtr,
+ dataPtr);
+}
+
+void Dbdict::executeTransaction(Callback *pcallback,
+ Signal* signal,
+ Uint32 senderData,
+ Uint32 prepareId,
+ Uint32 noAttr,
+ LinearSectionPtr headerPtr,
+ LinearSectionPtr dataPtr)
+{
+ jam();
+ EVENT_TRACE;
+
+ UtilExecuteReq * utilExecuteReq =
+ (UtilExecuteReq *)signal->getDataPtrSend();
+
+ utilExecuteReq->setSenderRef(reference());
+ utilExecuteReq->setSenderData(senderData);
+ utilExecuteReq->setPrepareId(prepareId);
+ utilExecuteReq->setReleaseFlag(); // must be done after setting prepareId
+
+#if 0
+ printf("Header size %u\n", headerPtr.sz);
+ for(int i = 0; i < (int)headerPtr.sz; i++)
+ printf("H'%.8x ", headerBuffer[i]);
+ printf("\n");
+
+ printf("Data size %u\n", dataPtr.sz);
+ for(int i = 0; i < (int)dataPtr.sz; i++)
+ printf("H'%.8x ", dataBuffer[i]);
+ printf("\n");
+#endif
+
+ struct LinearSectionPtr sectionsPtr[UtilExecuteReq::NoOfSections];
+ sectionsPtr[UtilExecuteReq::HEADER_SECTION].p = headerPtr.p;
+ sectionsPtr[UtilExecuteReq::HEADER_SECTION].sz = noAttr;
+ sectionsPtr[UtilExecuteReq::DATA_SECTION].p = dataPtr.p;
+ sectionsPtr[UtilExecuteReq::DATA_SECTION].sz = dataPtr.sz;
+
+ sendSignalUtilReq(pcallback, DBUTIL_REF, GSN_UTIL_EXECUTE_REQ, signal,
+ UtilExecuteReq::SignalLength, JBB,
+ sectionsPtr, UtilExecuteReq::NoOfSections);
+}
+
+void Dbdict::parseReadEventSys(Signal* signal, sysTab_NDBEVENTS_0& m_eventRec)
+{
+ SegmentedSectionPtr headerPtr, dataPtr;
+ jam();
+ signal->getSection(headerPtr, UtilExecuteReq::HEADER_SECTION);
+ SectionReader headerReader(headerPtr, getSectionSegmentPool());
+
+ signal->getSection(dataPtr, UtilExecuteReq::DATA_SECTION);
+ SectionReader dataReader(dataPtr, getSectionSegmentPool());
+
+ AttributeHeader header;
+ Uint32 *dst = (Uint32*)&m_eventRec;
+
+ for (int i = 0; i < EVENT_SYSTEM_TABLE_LENGTH; i++) {
+ headerReader.getWord((Uint32 *)&header);
+ int sz = header.getDataSize();
+ for (int i=0; i < sz; i++)
+ dataReader.getWord(dst++);
+ }
+
+ ndbrequire( ((char*)dst-(char*)&m_eventRec) == sizeof(m_eventRec) );
+
+ releaseSections(signal);
+}
+
+void Dbdict::createEventUTIL_EXECUTE(Signal *signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ if (returnCode == 0) {
+ // Entry into system table all set
+ UtilExecuteConf* const conf = (UtilExecuteConf*)signal->getDataPtr();
+ jam();
+ OpCreateEventPtr evntRecPtr;
+ evntRecPtr.i = conf->getSenderData();
+
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+ OpCreateEvent *evntRec = evntRecPtr.p;
+
+ switch (evntRec->m_requestType) {
+ case CreateEvntReq::RT_USER_GET: {
+#ifdef EVENT_DEBUG
+ printf("get type = %d\n", CreateEvntReq::RT_USER_GET);
+#endif
+ parseReadEventSys(signal, evntRecPtr.p->m_eventRec);
+
+ evntRec->m_request.setEventType(evntRecPtr.p->m_eventRec.EVENT_TYPE);
+ evntRec->m_request.setAttrListBitmask(*(AttributeMask*)evntRecPtr.p->m_eventRec.ATTRIBUTE_MASK);
+ evntRec->m_request.setEventId(evntRecPtr.p->m_eventRec.SUBID);
+ evntRec->m_request.setEventKey(evntRecPtr.p->m_eventRec.SUBKEY);
+
+#ifdef EVENT_DEBUG
+ printf("EventName: %s\n", evntRec->m_eventRec.NAME);
+ printf("TableName: %s\n", evntRec->m_eventRec.TABLE_NAME);
+#endif
+
+ // find table id for event table
+ TableRecord keyRecord;
+ strcpy(keyRecord.tableName, evntRecPtr.p->m_eventRec.TABLE_NAME);
+
+ TableRecordPtr tablePtr;
+ c_tableRecordHash.find(tablePtr, keyRecord);
+
+ if (tablePtr.i == RNIL) {
+ jam();
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+
+ evntRec->m_request.setTableId(tablePtr.p->tableId);
+
+ createEventComplete_RT_USER_GET(signal, evntRecPtr);
+ return;
+ }
+ case CreateEvntReq::RT_USER_CREATE: {
+#ifdef EVENT_DEBUG
+ printf("create type = %d\n", CreateEvntReq::RT_USER_CREATE);
+#endif
+ jam();
+ createEventComplete_RT_USER_CREATE(signal, evntRecPtr);
+ return;
+ }
+ break;
+ default:
+ ndbrequire(false);
+ }
+ } else { // returnCode != 0
+ UtilExecuteRef * const ref = (UtilExecuteRef *)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+ evntRecPtr.i = ref->getSenderData();
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+ jam();
+ evntRecPtr.p->m_errorNode = reference();
+ evntRecPtr.p->m_errorLine = __LINE__;
+
+ switch (ref->getErrorCode()) {
+ case UtilExecuteRef::TCError:
+ switch (ref->getTCErrorCode()) {
+ case ZNOT_FOUND:
+ jam();
+ evntRecPtr.p->m_errorCode = CreateEvntRef::EventNotFound;
+ break;
+ case ZALREADYEXIST:
+ jam();
+ evntRecPtr.p->m_errorCode = CreateEvntRef::EventNameExists;
+ break;
+ default:
+ jam();
+ evntRecPtr.p->m_errorCode = CreateEvntRef::UndefinedTCError;
+ break;
+ }
+ break;
+ default:
+ jam();
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ break;
+ }
+
+ createEvent_sendReply(signal, evntRecPtr);
+ }
+}
+
+/***********************************************************************
+ *
+ * NdbEventOperation, reading systable, creating event in suma
+ *
+ */
+
+void
+Dbdict::createEvent_RT_USER_GET(Signal* signal, OpCreateEventPtr evntRecPtr){
+ jam();
+ EVENT_TRACE;
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Coordinator) got GSN_CREATE_EVNT_REQ::RT_USER_GET evntRecPtr.i = (%d), ref = %u", evntRecPtr.i, evntRecPtr.p->m_request.getUserRef());
+#endif
+
+ SegmentedSectionPtr ssPtr;
+
+ signal->getSection(ssPtr, 0);
+
+ SimplePropertiesSectionReader r0(ssPtr, getSectionSegmentPool());
+#ifdef EVENT_DEBUG
+ r0.printAll(ndbout);
+#endif
+ if ((!r0.first()) ||
+ (r0.getValueType() != SimpleProperties::StringValue) ||
+ (r0.getValueLen() <= 0)) {
+ jam();
+ releaseSections(signal);
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+
+ r0.getString(evntRecPtr.p->m_eventRec.NAME);
+ int len = strlen(evntRecPtr.p->m_eventRec.NAME);
+ memset(evntRecPtr.p->m_eventRec.NAME+len, 0, MAX_TAB_NAME_SIZE-len);
+
+ releaseSections(signal);
+
+ Callback c = { safe_cast(&Dbdict::createEventUTIL_PREPARE), 0 };
+
+ prepareTransactionEventSysTable(&c, signal, evntRecPtr.i,
+ UtilPrepareReq::Read);
+ /*
+ * Will read systable and fill an OpCreateEventPtr
+ * and return below
+ */
+}
+
+void
+Dbdict::createEventComplete_RT_USER_GET(Signal* signal,
+ OpCreateEventPtr evntRecPtr){
+ jam();
+
+ // Send to oneself and the other DICT's
+ CreateEvntReq * req = (CreateEvntReq *)signal->getDataPtrSend();
+
+ *req = evntRecPtr.p->m_request;
+ req->senderRef = reference();
+ req->senderData = evntRecPtr.i;
+
+ req->addRequestFlag(CreateEvntReq::RT_DICT_AFTER_GET);
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Coordinator) sending GSN_CREATE_EVNT_REQ::RT_DICT_AFTER_GET to DBDICT participants evntRecPtr.i = (%d)", evntRecPtr.i);
+#endif
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ RequestTracker & p = evntRecPtr.p->m_reqTracker;
+ p.init<CreateEvntRef>(c_counterMgr, rg, GSN_CREATE_EVNT_REF, evntRecPtr.i);
+
+ sendSignal(rg, GSN_CREATE_EVNT_REQ, signal, CreateEvntReq::SignalLength, JBB);
+}
+
+void
+Dbdict::createEvent_nodeFailCallback(Signal* signal, Uint32 eventRecPtrI,
+ Uint32 returnCode){
+ OpCreateEventPtr evntRecPtr;
+ c_opCreateEvent.getPtr(evntRecPtr, eventRecPtrI);
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+void Dbdict::execCREATE_EVNT_REF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ CreateEvntRef * const ref = (CreateEvntRef *)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+
+ evntRecPtr.i = ref->getUserData();
+
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Coordinator) got GSN_CREATE_EVNT_REF evntRecPtr.i = (%d)", evntRecPtr.i);
+#endif
+
+ if (ref->errorCode == CreateEvntRef::NF_FakeErrorREF){
+ jam();
+ evntRecPtr.p->m_reqTracker.ignoreRef(c_counterMgr, refToNode(ref->senderRef));
+ } else {
+ jam();
+ evntRecPtr.p->m_reqTracker.reportRef(c_counterMgr, refToNode(ref->senderRef));
+ }
+ createEvent_sendReply(signal, evntRecPtr);
+
+ return;
+}
+
+void Dbdict::execCREATE_EVNT_CONF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ CreateEvntConf * const conf = (CreateEvntConf *)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+
+ evntRecPtr.i = conf->getUserData();
+
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Coordinator) got GSN_CREATE_EVNT_CONF evntRecPtr.i = (%d)", evntRecPtr.i);
+#endif
+
+ evntRecPtr.p->m_reqTracker.reportConf(c_counterMgr, refToNode(conf->senderRef));
+
+ // we will only have a valid tablename if it the master DICT sending this
+ // but that's ok
+ LinearSectionPtr ptr[1];
+ ptr[0].p = (Uint32 *)evntRecPtr.p->m_eventRec.TABLE_NAME;
+ ptr[0].sz =
+ (strlen(evntRecPtr.p->m_eventRec.TABLE_NAME)+4)/4; // to make sure we have a null
+
+ createEvent_sendReply(signal, evntRecPtr, ptr, 1);
+
+ return;
+}
+
+/************************************************
+ *
+ * Participant stuff
+ *
+ */
+
+void
+Dbdict::createEvent_RT_DICT_AFTER_GET(Signal* signal, OpCreateEventPtr evntRecPtr){
+ jam();
+ evntRecPtr.p->m_request.setUserRef(signal->senderBlockRef());
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Participant) got CREATE_EVNT_REQ::RT_DICT_AFTER_GET evntRecPtr.i = (%d)", evntRecPtr.i);
+#endif
+
+ // the signal comes from the DICT block that got the first user request!
+ // This code runs on all DICT nodes, including oneself
+
+ // Seize a Create Event record, the Coordinator will now have two seized
+ // but that's ok, it's like a recursion
+
+ SubCreateReq * sumaReq = (SubCreateReq *)signal->getDataPtrSend();
+
+ sumaReq->subscriberRef = reference(); // reference to DICT
+ sumaReq->subscriberData = evntRecPtr.i;
+ sumaReq->subscriptionId = evntRecPtr.p->m_request.getEventId();
+ sumaReq->subscriptionKey = evntRecPtr.p->m_request.getEventKey();
+ sumaReq->subscriptionType = SubCreateReq::TableEvent;
+ sumaReq->tableId = evntRecPtr.p->m_request.getTableId();
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("sending GSN_SUB_CREATE_REQ");
+#endif
+
+ sendSignal(SUMA_REF, GSN_SUB_CREATE_REQ, signal,
+ SubCreateReq::SignalLength+1 /*to get table Id*/, JBB);
+}
+
+void Dbdict::execSUB_CREATE_REF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ SubCreateRef * const ref = (SubCreateRef *)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+
+ evntRecPtr.i = ref->subscriberData;
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Participant) got SUB_CREATE_REF evntRecPtr.i = (%d)", evntRecPtr.i);
+#endif
+
+ if (ref->err == GrepError::SUBSCRIPTION_ID_NOT_UNIQUE) {
+ jam();
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("SUBSCRIPTION_ID_NOT_UNIQUE");
+#endif
+ createEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("Other error");
+#endif
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+void Dbdict::execSUB_CREATE_CONF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+
+ SubCreateConf * const sumaConf = (SubCreateConf *)signal->getDataPtr();
+
+ const Uint32 subscriptionId = sumaConf->subscriptionId;
+ const Uint32 subscriptionKey = sumaConf->subscriptionKey;
+ const Uint32 evntRecId = sumaConf->subscriberData;
+
+ OpCreateEvent *evntRec;
+ ndbrequire((evntRec = c_opCreateEvent.getPtr(evntRecId)) != NULL);
+
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT(Participant) got SUB_CREATE_CONF evntRecPtr.i = (%d)", evntRecId);
+#endif
+
+ SubSyncReq *sumaSync = (SubSyncReq *)signal->getDataPtrSend();
+
+ sumaSync->subscriptionId = subscriptionId;
+ sumaSync->subscriptionKey = subscriptionKey;
+ sumaSync->part = (Uint32) SubscriptionData::MetaData;
+ sumaSync->subscriberData = evntRecId;
+
+ sendSignal(SUMA_REF, GSN_SUB_SYNC_REQ, signal,
+ SubSyncReq::SignalLength, JBB);
+}
+
+void Dbdict::execSUB_SYNC_REF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+ SubSyncRef * const ref = (SubSyncRef *)signal->getDataPtr();
+ OpCreateEventPtr evntRecPtr;
+
+ evntRecPtr.i = ref->subscriberData;
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+void Dbdict::execSUB_SYNC_CONF(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+
+ SubSyncConf * const sumaSyncConf = (SubSyncConf *)signal->getDataPtr();
+
+ // Uint32 subscriptionId = sumaSyncConf->subscriptionId;
+ // Uint32 subscriptionKey = sumaSyncConf->subscriptionKey;
+ OpCreateEventPtr evntRecPtr;
+
+ evntRecPtr.i = sumaSyncConf->subscriberData;
+ ndbrequire((evntRecPtr.p = c_opCreateEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ ndbrequire(sumaSyncConf->part == (Uint32)SubscriptionData::MetaData);
+
+ createEvent_sendReply(signal, evntRecPtr);
+}
+
+/****************************************************
+ *
+ * common create reply method
+ *
+ *******************************************************/
+
+void Dbdict::createEvent_sendReply(Signal* signal,
+ OpCreateEventPtr evntRecPtr,
+ LinearSectionPtr *ptr, int noLSP)
+{
+ jam();
+ EVENT_TRACE;
+
+ // check if we're ready to sent reply
+ // if we are the master dict we might be waiting for conf/ref
+
+ if (!evntRecPtr.p->m_reqTracker.done()) {
+ jam();
+ return; // there's more to come
+ }
+
+ if (evntRecPtr.p->m_reqTracker.hasRef()) {
+ ptr = NULL; // we don't want to return anything if there's an error
+ if (!evntRecPtr.p->hasError()) {
+ evntRecPtr.p->m_errorCode = CreateEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+ jam();
+ } else
+ jam();
+ }
+
+ // reference to API if master DICT
+ // else reference to master DICT
+ Uint32 senderRef = evntRecPtr.p->m_request.getUserRef();
+ Uint32 signalLength;
+ Uint32 gsn;
+
+ if (evntRecPtr.p->hasError()) {
+ jam();
+ EVENT_TRACE;
+ CreateEvntRef * ret = (CreateEvntRef *)signal->getDataPtrSend();
+
+ ret->setEventId(evntRecPtr.p->m_request.getEventId());
+ ret->setEventKey(evntRecPtr.p->m_request.getEventKey());
+ ret->setUserData(evntRecPtr.p->m_request.getUserData());
+ ret->senderRef = reference();
+ ret->setTableId(evntRecPtr.p->m_request.getTableId());
+ ret->setEventType(evntRecPtr.p->m_request.getEventType());
+ ret->setRequestType(evntRecPtr.p->m_request.getRequestType());
+
+ ret->setErrorCode(evntRecPtr.p->m_errorCode);
+ ret->setErrorLine(evntRecPtr.p->m_errorLine);
+ ret->setErrorNode(evntRecPtr.p->m_errorNode);
+
+ signalLength = CreateEvntRef::SignalLength;
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT sending GSN_CREATE_EVNT_REF to evntRecPtr.i = (%d) node = %u ref = %u", evntRecPtr.i, refToNode(senderRef), senderRef);
+ ndbout_c("errorCode = %u", evntRecPtr.p->m_errorCode);
+ ndbout_c("errorLine = %u", evntRecPtr.p->m_errorLine);
+#endif
+ gsn = GSN_CREATE_EVNT_REF;
+
+ } else {
+ jam();
+ EVENT_TRACE;
+ CreateEvntConf * evntConf = (CreateEvntConf *)signal->getDataPtrSend();
+
+ evntConf->setEventId(evntRecPtr.p->m_request.getEventId());
+ evntConf->setEventKey(evntRecPtr.p->m_request.getEventKey());
+ evntConf->setUserData(evntRecPtr.p->m_request.getUserData());
+ evntConf->senderRef = reference();
+ evntConf->setTableId(evntRecPtr.p->m_request.getTableId());
+ evntConf->setAttrListBitmask(evntRecPtr.p->m_request.getAttrListBitmask());
+ evntConf->setEventType(evntRecPtr.p->m_request.getEventType());
+ evntConf->setRequestType(evntRecPtr.p->m_request.getRequestType());
+
+ signalLength = CreateEvntConf::SignalLength;
+#ifdef EVENT_PH2_DEBUG
+ ndbout_c("DBDICT sending GSN_CREATE_EVNT_CONF to evntRecPtr.i = (%d) node = %u ref = %u", evntRecPtr.i, refToNode(senderRef), senderRef);
+#endif
+ gsn = GSN_CREATE_EVNT_CONF;
+ }
+
+ if (ptr) {
+ jam();
+ sendSignal(senderRef, gsn, signal, signalLength, JBB, ptr, noLSP);
+ } else {
+ jam();
+ sendSignal(senderRef, gsn, signal, signalLength, JBB);
+ }
+
+ c_opCreateEvent.release(evntRecPtr);
+}
+
+/*************************************************************/
+
+/********************************************************************
+ *
+ * Start event
+ *
+ *******************************************************************/
+
+void Dbdict::execSUB_START_REQ(Signal* signal)
+{
+ jamEntry();
+
+ Uint32 origSenderRef = signal->senderBlockRef();
+
+ OpSubEventPtr subbPtr;
+ if (!c_opSubEvent.seize(subbPtr)) {
+ SubStartRef * ref = (SubStartRef *)signal->getDataPtrSend();
+ { // fix
+ Uint32 subcriberRef = ((SubStartReq*)signal->getDataPtr())->subscriberRef;
+ ref->subscriberRef = subcriberRef;
+ }
+ jam();
+ // ret->setErrorCode(SubStartRef::SeizeError);
+ // ret->setErrorLine(__LINE__);
+ // ret->setErrorNode(reference());
+ ref->senderRef = reference();
+ ref->setTemporary(SubStartRef::Busy);
+
+ sendSignal(origSenderRef, GSN_SUB_START_REF, signal,
+ SubStartRef::SignalLength2, JBB);
+ return;
+ }
+
+ {
+ const SubStartReq* req = (SubStartReq*) signal->getDataPtr();
+ subbPtr.p->m_senderRef = req->senderRef;
+ subbPtr.p->m_senderData = req->senderData;
+ subbPtr.p->m_errorCode = 0;
+ }
+
+ if (refToBlock(origSenderRef) != DBDICT) {
+ /*
+ * Coordinator
+ */
+ jam();
+
+ subbPtr.p->m_senderRef = origSenderRef; // not sure if API sets correctly
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ RequestTracker & p = subbPtr.p->m_reqTracker;
+ p.init<SubStartRef>(c_counterMgr, rg, GSN_SUB_START_REF, subbPtr.i);
+
+ SubStartReq* req = (SubStartReq*) signal->getDataPtrSend();
+
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("DBDICT(Coordinator) sending GSN_SUB_START_REQ to DBDICT participants subbPtr.i = (%d)", subbPtr.i);
+#endif
+
+ sendSignal(rg, GSN_SUB_START_REQ, signal, SubStartReq::SignalLength2, JBB);
+ return;
+ }
+ /*
+ * Participant
+ */
+ ndbrequire(refToBlock(origSenderRef) == DBDICT);
+
+ {
+ SubStartReq* req = (SubStartReq*) signal->getDataPtrSend();
+
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("DBDICT(Participant) sending GSN_SUB_START_REQ to SUMA subbPtr.i = (%d)", subbPtr.i);
+#endif
+ sendSignal(SUMA_REF, GSN_SUB_START_REQ, signal, SubStartReq::SignalLength2, JBB);
+ }
+}
+
+void Dbdict::execSUB_START_REF(Signal* signal)
+{
+ jamEntry();
+
+ const SubStartRef* ref = (SubStartRef*) signal->getDataPtr();
+ Uint32 senderRef = ref->senderRef;
+
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, ref->senderData);
+
+ if (refToBlock(senderRef) == SUMA) {
+ /*
+ * Participant
+ */
+ jam();
+
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("DBDICT(Participant) got GSN_SUB_START_REF = (%d)", subbPtr.i);
+#endif
+
+ if (ref->isTemporary()){
+ jam();
+ SubStartReq* req = (SubStartReq*)signal->getDataPtrSend();
+ { // fix
+ Uint32 subscriberRef = ref->subscriberRef;
+ req->subscriberRef = subscriberRef;
+ }
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+ sendSignal(SUMA_REF, GSN_SUB_START_REQ,
+ signal, SubStartReq::SignalLength2, JBB);
+ } else {
+ jam();
+
+ SubStartRef* ref = (SubStartRef*) signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = subbPtr.p->m_senderData;
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_START_REF,
+ signal, SubStartRef::SignalLength2, JBB);
+ c_opSubEvent.release(subbPtr);
+ }
+ return;
+ }
+ /*
+ * Coordinator
+ */
+ ndbrequire(refToBlock(senderRef) == DBDICT);
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("DBDICT(Coordinator) got GSN_SUB_START_REF = (%d)", subbPtr.i);
+#endif
+ if (ref->errorCode == SubStartRef::NF_FakeErrorREF){
+ jam();
+ subbPtr.p->m_reqTracker.ignoreRef(c_counterMgr, refToNode(senderRef));
+ } else {
+ jam();
+ subbPtr.p->m_reqTracker.reportRef(c_counterMgr, refToNode(senderRef));
+ }
+ completeSubStartReq(signal,subbPtr.i,0);
+}
+
+void Dbdict::execSUB_START_CONF(Signal* signal)
+{
+ jamEntry();
+
+ const SubStartConf* conf = (SubStartConf*) signal->getDataPtr();
+ Uint32 senderRef = conf->senderRef;
+
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, conf->senderData);
+
+ if (refToBlock(senderRef) == SUMA) {
+ /*
+ * Participant
+ */
+ jam();
+ SubStartConf* conf = (SubStartConf*) signal->getDataPtrSend();
+
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("DBDICT(Participant) got GSN_SUB_START_CONF = (%d)", subbPtr.i);
+#endif
+
+ conf->senderRef = reference();
+ conf->senderData = subbPtr.p->m_senderData;
+
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_START_CONF,
+ signal, SubStartConf::SignalLength2, JBB);
+ c_opSubEvent.release(subbPtr);
+ return;
+ }
+ /*
+ * Coordinator
+ */
+ ndbrequire(refToBlock(senderRef) == DBDICT);
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("DBDICT(Coordinator) got GSN_SUB_START_CONF = (%d)", subbPtr.i);
+#endif
+ subbPtr.p->m_reqTracker.reportConf(c_counterMgr, refToNode(senderRef));
+ completeSubStartReq(signal,subbPtr.i,0);
+}
+
+/*
+ * Coordinator
+ */
+void Dbdict::completeSubStartReq(Signal* signal,
+ Uint32 ptrI,
+ Uint32 returnCode){
+ jam();
+
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, ptrI);
+
+ if (!subbPtr.p->m_reqTracker.done()){
+ jam();
+ return;
+ }
+
+ if (subbPtr.p->m_reqTracker.hasRef()) {
+ jam();
+#ifdef EVENT_DEBUG
+ ndbout_c("SUB_START_REF");
+#endif
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_START_REF,
+ signal, SubStartRef::SignalLength, JBB);
+ if (subbPtr.p->m_reqTracker.hasConf()) {
+ // stopStartedNodes(signal);
+ }
+ c_opSubEvent.release(subbPtr);
+ return;
+ }
+#ifdef EVENT_DEBUG
+ ndbout_c("SUB_START_CONF");
+#endif
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_START_CONF,
+ signal, SubStartConf::SignalLength, JBB);
+ c_opSubEvent.release(subbPtr);
+}
+
+/********************************************************************
+ *
+ * Stop event
+ *
+ *******************************************************************/
+
+void Dbdict::execSUB_STOP_REQ(Signal* signal)
+{
+ jamEntry();
+
+ Uint32 origSenderRef = signal->senderBlockRef();
+
+ OpSubEventPtr subbPtr;
+ if (!c_opSubEvent.seize(subbPtr)) {
+ SubStopRef * ref = (SubStopRef *)signal->getDataPtrSend();
+ jam();
+ // ret->setErrorCode(SubStartRef::SeizeError);
+ // ret->setErrorLine(__LINE__);
+ // ret->setErrorNode(reference());
+ ref->senderRef = reference();
+ ref->setTemporary(SubStopRef::Busy);
+
+ sendSignal(origSenderRef, GSN_SUB_STOP_REF, signal,
+ SubStopRef::SignalLength, JBB);
+ return;
+ }
+
+ {
+ const SubStopReq* req = (SubStopReq*) signal->getDataPtr();
+ subbPtr.p->m_senderRef = req->senderRef;
+ subbPtr.p->m_senderData = req->senderData;
+ subbPtr.p->m_errorCode = 0;
+ }
+
+ if (refToBlock(origSenderRef) != DBDICT) {
+ /*
+ * Coordinator
+ */
+ jam();
+#ifdef EVENT_DEBUG
+ ndbout_c("SUB_STOP_REQ 1");
+#endif
+ subbPtr.p->m_senderRef = origSenderRef; // not sure if API sets correctly
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ RequestTracker & p = subbPtr.p->m_reqTracker;
+ p.init<SubStopRef>(c_counterMgr, rg, GSN_SUB_STOP_REF, subbPtr.i);
+
+ SubStopReq* req = (SubStopReq*) signal->getDataPtrSend();
+
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+
+ sendSignal(rg, GSN_SUB_STOP_REQ, signal, SubStopReq::SignalLength, JBB);
+ return;
+ }
+ /*
+ * Participant
+ */
+#ifdef EVENT_DEBUG
+ ndbout_c("SUB_STOP_REQ 2");
+#endif
+ ndbrequire(refToBlock(origSenderRef) == DBDICT);
+ {
+ SubStopReq* req = (SubStopReq*) signal->getDataPtrSend();
+
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+
+ sendSignal(SUMA_REF, GSN_SUB_STOP_REQ, signal, SubStopReq::SignalLength, JBB);
+ }
+}
+
+void Dbdict::execSUB_STOP_REF(Signal* signal)
+{
+ jamEntry();
+ const SubStopRef* ref = (SubStopRef*) signal->getDataPtr();
+ Uint32 senderRef = ref->senderRef;
+
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, ref->senderData);
+
+ if (refToBlock(senderRef) == SUMA) {
+ /*
+ * Participant
+ */
+ jam();
+ if (ref->isTemporary()){
+ jam();
+ SubStopReq* req = (SubStopReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+ sendSignal(SUMA_REF, GSN_SUB_STOP_REQ,
+ signal, SubStopReq::SignalLength, JBB);
+ } else {
+ jam();
+ SubStopRef* ref = (SubStopRef*) signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = subbPtr.p->m_senderData;
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_STOP_REF,
+ signal, SubStopRef::SignalLength, JBB);
+ c_opSubEvent.release(subbPtr);
+ }
+ return;
+ }
+ /*
+ * Coordinator
+ */
+ ndbrequire(refToBlock(senderRef) == DBDICT);
+ if (ref->errorCode == SubStopRef::NF_FakeErrorREF){
+ jam();
+ subbPtr.p->m_reqTracker.ignoreRef(c_counterMgr, refToNode(senderRef));
+ } else {
+ jam();
+ subbPtr.p->m_reqTracker.reportRef(c_counterMgr, refToNode(senderRef));
+ }
+ completeSubStopReq(signal,subbPtr.i,0);
+}
+
+void Dbdict::execSUB_STOP_CONF(Signal* signal)
+{
+ jamEntry();
+
+ const SubStopConf* conf = (SubStopConf*) signal->getDataPtr();
+ Uint32 senderRef = conf->senderRef;
+
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, conf->senderData);
+
+ if (refToBlock(senderRef) == SUMA) {
+ /*
+ * Participant
+ */
+ jam();
+ SubStopConf* conf = (SubStopConf*) signal->getDataPtrSend();
+
+ conf->senderRef = reference();
+ conf->senderData = subbPtr.p->m_senderData;
+
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_STOP_CONF,
+ signal, SubStopConf::SignalLength, JBB);
+ c_opSubEvent.release(subbPtr);
+ return;
+ }
+ /*
+ * Coordinator
+ */
+ ndbrequire(refToBlock(senderRef) == DBDICT);
+ subbPtr.p->m_reqTracker.reportConf(c_counterMgr, refToNode(senderRef));
+ completeSubStopReq(signal,subbPtr.i,0);
+}
+
+/*
+ * Coordinator
+ */
+void Dbdict::completeSubStopReq(Signal* signal,
+ Uint32 ptrI,
+ Uint32 returnCode){
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, ptrI);
+
+ if (!subbPtr.p->m_reqTracker.done()){
+ jam();
+ return;
+ }
+
+ if (subbPtr.p->m_reqTracker.hasRef()) {
+ jam();
+#ifdef EVENT_DEBUG
+ ndbout_c("SUB_STOP_REF");
+#endif
+ SubStopRef* ref = (SubStopRef*)signal->getDataPtrSend();
+
+ ref->senderRef = reference();
+ ref->senderData = subbPtr.p->m_senderData;
+ /*
+ ref->subscriptionId = subbPtr.p->m_senderData;
+ ref->subscriptionKey = subbPtr.p->m_senderData;
+ ref->part = subbPtr.p->m_part; // SubscriptionData::Part
+ ref->subscriberData = subbPtr.p->m_subscriberData;
+ ref->subscriberRef = subbPtr.p->m_subscriberRef;
+ */
+ ref->errorCode = subbPtr.p->m_errorCode;
+
+
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_STOP_REF,
+ signal, SubStopRef::SignalLength, JBB);
+ if (subbPtr.p->m_reqTracker.hasConf()) {
+ // stopStartedNodes(signal);
+ }
+ c_opSubEvent.release(subbPtr);
+ return;
+ }
+#ifdef EVENT_DEBUG
+ ndbout_c("SUB_STOP_CONF");
+#endif
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_STOP_CONF,
+ signal, SubStopConf::SignalLength, JBB);
+ c_opSubEvent.release(subbPtr);
+}
+
+/***************************************************************
+ * MODULE: Drop event.
+ *
+ * Drop event.
+ *
+ * TODO
+ */
+
+void
+Dbdict::execDROP_EVNT_REQ(Signal* signal)
+{
+ jamEntry();
+ EVENT_TRACE;
+
+ DropEvntReq *req = (DropEvntReq*)signal->getDataPtr();
+ const Uint32 senderRef = signal->senderBlockRef();
+ OpDropEventPtr evntRecPtr;
+
+ // Seize a Create Event record
+ if (!c_opDropEvent.seize(evntRecPtr)) {
+ // Failed to allocate event record
+ jam();
+ releaseSections(signal);
+
+ DropEvntRef * ret = (DropEvntRef *)signal->getDataPtrSend();
+ ret->setErrorCode(DropEvntRef::SeizeError);
+ ret->setErrorLine(__LINE__);
+ ret->setErrorNode(reference());
+ sendSignal(senderRef, GSN_DROP_EVNT_REF, signal,
+ DropEvntRef::SignalLength, JBB);
+ return;
+ }
+
+#ifdef EVENT_DEBUG
+ ndbout_c("DBDICT::execDROP_EVNT_REQ evntRecId = (%d)", evntRecPtr.i);
+#endif
+
+ OpDropEvent* evntRec = evntRecPtr.p;
+ evntRec->init(req);
+
+ SegmentedSectionPtr ssPtr;
+
+ signal->getSection(ssPtr, 0);
+
+ SimplePropertiesSectionReader r0(ssPtr, getSectionSegmentPool());
+#ifdef EVENT_DEBUG
+ r0.printAll(ndbout);
+#endif
+ // event name
+ if ((!r0.first()) ||
+ (r0.getValueType() != SimpleProperties::StringValue) ||
+ (r0.getValueLen() <= 0)) {
+ jam();
+ releaseSections(signal);
+
+ evntRecPtr.p->m_errorCode = DropEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorNode = reference();
+
+ dropEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+ r0.getString(evntRecPtr.p->m_eventRec.NAME);
+ {
+ int len = strlen(evntRecPtr.p->m_eventRec.NAME);
+ memset(evntRecPtr.p->m_eventRec.NAME+len, 0, MAX_TAB_NAME_SIZE-len);
+#ifdef EVENT_DEBUG
+ printf("DropEvntReq; EventName %s, len %u\n",
+ evntRecPtr.p->m_eventRec.NAME, len);
+ for(int i = 0; i < MAX_TAB_NAME_SIZE/4; i++)
+ printf("H'%.8x ", ((Uint32*)evntRecPtr.p->m_eventRec.NAME)[i]);
+ printf("\n");
+#endif
+ }
+
+ releaseSections(signal);
+
+ Callback c = { safe_cast(&Dbdict::dropEventUTIL_PREPARE_READ), 0 };
+
+ prepareTransactionEventSysTable(&c, signal, evntRecPtr.i,
+ UtilPrepareReq::Read);
+}
+
+void
+Dbdict::dropEventUTIL_PREPARE_READ(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ if (returnCode != 0) {
+ EVENT_TRACE;
+ dropEventUtilPrepareRef(signal, callbackData, returnCode);
+ return;
+ }
+
+ UtilPrepareConf* const req = (UtilPrepareConf*)signal->getDataPtr();
+ OpDropEventPtr evntRecPtr;
+ evntRecPtr.i = req->getSenderData();
+ const Uint32 prepareId = req->getPrepareId();
+
+ ndbrequire((evntRecPtr.p = c_opDropEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ Callback c = { safe_cast(&Dbdict::dropEventUTIL_EXECUTE_READ), 0 };
+
+ executeTransEventSysTable(&c, signal,
+ evntRecPtr.i, evntRecPtr.p->m_eventRec,
+ prepareId, UtilPrepareReq::Read);
+}
+
+void
+Dbdict::dropEventUTIL_EXECUTE_READ(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ if (returnCode != 0) {
+ EVENT_TRACE;
+ dropEventUtilExecuteRef(signal, callbackData, returnCode);
+ return;
+ }
+
+ OpDropEventPtr evntRecPtr;
+ UtilExecuteConf * const ref = (UtilExecuteConf *)signal->getDataPtr();
+ jam();
+ evntRecPtr.i = ref->getSenderData();
+ ndbrequire((evntRecPtr.p = c_opDropEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ parseReadEventSys(signal, evntRecPtr.p->m_eventRec);
+
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ RequestTracker & p = evntRecPtr.p->m_reqTracker;
+ p.init<SubRemoveRef>(c_counterMgr, rg, GSN_SUB_REMOVE_REF,
+ evntRecPtr.i);
+
+ SubRemoveReq* req = (SubRemoveReq*) signal->getDataPtrSend();
+
+ req->senderRef = reference();
+ req->senderData = evntRecPtr.i;
+ req->subscriptionId = evntRecPtr.p->m_eventRec.SUBID;
+ req->subscriptionKey = evntRecPtr.p->m_eventRec.SUBKEY;
+
+ sendSignal(rg, GSN_SUB_REMOVE_REQ, signal, SubRemoveReq::SignalLength, JBB);
+}
+
+/*
+ * Participant
+ */
+
+void
+Dbdict::execSUB_REMOVE_REQ(Signal* signal)
+{
+ jamEntry();
+
+ Uint32 origSenderRef = signal->senderBlockRef();
+
+ OpSubEventPtr subbPtr;
+ if (!c_opSubEvent.seize(subbPtr)) {
+ SubRemoveRef * ref = (SubRemoveRef *)signal->getDataPtrSend();
+ jam();
+ ref->senderRef = reference();
+ ref->setTemporary(SubRemoveRef::Busy);
+
+ sendSignal(origSenderRef, GSN_SUB_REMOVE_REF, signal,
+ SubRemoveRef::SignalLength, JBB);
+ return;
+ }
+
+ {
+ const SubRemoveReq* req = (SubRemoveReq*) signal->getDataPtr();
+ subbPtr.p->m_senderRef = req->senderRef;
+ subbPtr.p->m_senderData = req->senderData;
+ subbPtr.p->m_errorCode = 0;
+ }
+
+ SubRemoveReq* req = (SubRemoveReq*) signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+
+ sendSignal(SUMA_REF, GSN_SUB_REMOVE_REQ, signal, SubRemoveReq::SignalLength, JBB);
+}
+
+/*
+ * Coordintor/Participant
+ */
+
+void
+Dbdict::execSUB_REMOVE_REF(Signal* signal)
+{
+ jamEntry();
+ const SubRemoveRef* ref = (SubRemoveRef*) signal->getDataPtr();
+ Uint32 senderRef = ref->senderRef;
+
+ if (refToBlock(senderRef) == SUMA) {
+ /*
+ * Participant
+ */
+ jam();
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, ref->senderData);
+ if (ref->errorCode == (Uint32) GrepError::SUBSCRIPTION_ID_NOT_FOUND) {
+ // conf this since this may occur if a nodefailiure has occured
+ // earlier so that the systable was not cleared
+ SubRemoveConf* conf = (SubRemoveConf*) signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = subbPtr.p->m_senderData;
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_REMOVE_CONF,
+ signal, SubRemoveConf::SignalLength, JBB);
+ } else {
+ SubRemoveRef* ref = (SubRemoveRef*) signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = subbPtr.p->m_senderData;
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_REMOVE_REF,
+ signal, SubRemoveRef::SignalLength, JBB);
+ }
+ c_opSubEvent.release(subbPtr);
+ return;
+ }
+ /*
+ * Coordinator
+ */
+ ndbrequire(refToBlock(senderRef) == DBDICT);
+ OpDropEventPtr eventRecPtr;
+ c_opDropEvent.getPtr(eventRecPtr, ref->senderData);
+ if (ref->errorCode == SubRemoveRef::NF_FakeErrorREF){
+ jam();
+ eventRecPtr.p->m_reqTracker.ignoreRef(c_counterMgr, refToNode(senderRef));
+ } else {
+ jam();
+ eventRecPtr.p->m_reqTracker.reportRef(c_counterMgr, refToNode(senderRef));
+ }
+ completeSubRemoveReq(signal,eventRecPtr.i,0);
+}
+
+void
+Dbdict::execSUB_REMOVE_CONF(Signal* signal)
+{
+ jamEntry();
+ const SubRemoveConf* conf = (SubRemoveConf*) signal->getDataPtr();
+ Uint32 senderRef = conf->senderRef;
+
+ if (refToBlock(senderRef) == SUMA) {
+ /*
+ * Participant
+ */
+ jam();
+ OpSubEventPtr subbPtr;
+ c_opSubEvent.getPtr(subbPtr, conf->senderData);
+ SubRemoveConf* conf = (SubRemoveConf*) signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = subbPtr.p->m_senderData;
+ sendSignal(subbPtr.p->m_senderRef, GSN_SUB_REMOVE_CONF,
+ signal, SubRemoveConf::SignalLength, JBB);
+ c_opSubEvent.release(subbPtr);
+ return;
+ }
+ /*
+ * Coordinator
+ */
+ ndbrequire(refToBlock(senderRef) == DBDICT);
+ OpDropEventPtr eventRecPtr;
+ c_opDropEvent.getPtr(eventRecPtr, conf->senderData);
+ eventRecPtr.p->m_reqTracker.reportConf(c_counterMgr, refToNode(senderRef));
+ completeSubRemoveReq(signal,eventRecPtr.i,0);
+}
+
+void
+Dbdict::completeSubRemoveReq(Signal* signal, Uint32 ptrI, Uint32 xxx)
+{
+ OpDropEventPtr evntRecPtr;
+ c_opDropEvent.getPtr(evntRecPtr, ptrI);
+
+ if (!evntRecPtr.p->m_reqTracker.done()){
+ jam();
+ return;
+ }
+
+ if (evntRecPtr.p->m_reqTracker.hasRef()) {
+ jam();
+ evntRecPtr.p->m_errorNode = reference();
+ evntRecPtr.p->m_errorLine = __LINE__;
+ evntRecPtr.p->m_errorCode = DropEvntRef::Undefined;
+ dropEvent_sendReply(signal, evntRecPtr);
+ return;
+ }
+
+ Callback c = { safe_cast(&Dbdict::dropEventUTIL_PREPARE_DELETE), 0 };
+
+ prepareTransactionEventSysTable(&c, signal, evntRecPtr.i,
+ UtilPrepareReq::Delete);
+}
+
+void
+Dbdict::dropEventUTIL_PREPARE_DELETE(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ if (returnCode != 0) {
+ EVENT_TRACE;
+ dropEventUtilPrepareRef(signal, callbackData, returnCode);
+ return;
+ }
+
+ UtilPrepareConf* const req = (UtilPrepareConf*)signal->getDataPtr();
+ OpDropEventPtr evntRecPtr;
+ jam();
+ evntRecPtr.i = req->getSenderData();
+ const Uint32 prepareId = req->getPrepareId();
+
+ ndbrequire((evntRecPtr.p = c_opDropEvent.getPtr(evntRecPtr.i)) != NULL);
+#ifdef EVENT_DEBUG
+ printf("DropEvntUTIL_PREPARE; evntRecPtr.i len %u\n",evntRecPtr.i);
+#endif
+
+ Callback c = { safe_cast(&Dbdict::dropEventUTIL_EXECUTE_DELETE), 0 };
+
+ executeTransEventSysTable(&c, signal,
+ evntRecPtr.i, evntRecPtr.p->m_eventRec,
+ prepareId, UtilPrepareReq::Delete);
+}
+
+void
+Dbdict::dropEventUTIL_EXECUTE_DELETE(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ if (returnCode != 0) {
+ EVENT_TRACE;
+ dropEventUtilExecuteRef(signal, callbackData, returnCode);
+ return;
+ }
+
+ OpDropEventPtr evntRecPtr;
+ UtilExecuteConf * const ref = (UtilExecuteConf *)signal->getDataPtr();
+ jam();
+ evntRecPtr.i = ref->getSenderData();
+ ndbrequire((evntRecPtr.p = c_opDropEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ dropEvent_sendReply(signal, evntRecPtr);
+}
+
+void
+Dbdict::dropEventUtilPrepareRef(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ UtilPrepareRef * const ref = (UtilPrepareRef *)signal->getDataPtr();
+ OpDropEventPtr evntRecPtr;
+ evntRecPtr.i = ref->getSenderData();
+ ndbrequire((evntRecPtr.p = c_opDropEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ bool temporary = false;
+ interpretUtilPrepareErrorCode((UtilPrepareRef::ErrorCode)ref->getErrorCode(),
+ temporary, evntRecPtr.p->m_errorLine);
+ if (temporary) {
+ evntRecPtr.p->m_errorCode = (DropEvntRef::ErrorCode)
+ ((Uint32) DropEvntRef::Undefined | (Uint32) DropEvntRef::Temporary);
+ }
+
+ if (evntRecPtr.p->m_errorCode == 0) {
+ evntRecPtr.p->m_errorCode = DropEvntRef::Undefined;
+ evntRecPtr.p->m_errorLine = __LINE__;
+ }
+ evntRecPtr.p->m_errorNode = reference();
+
+ dropEvent_sendReply(signal, evntRecPtr);
+}
+
+void
+Dbdict::dropEventUtilExecuteRef(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode)
+{
+ jam();
+ EVENT_TRACE;
+ OpDropEventPtr evntRecPtr;
+ UtilExecuteRef * const ref = (UtilExecuteRef *)signal->getDataPtr();
+ jam();
+ evntRecPtr.i = ref->getSenderData();
+ ndbrequire((evntRecPtr.p = c_opDropEvent.getPtr(evntRecPtr.i)) != NULL);
+
+ evntRecPtr.p->m_errorNode = reference();
+ evntRecPtr.p->m_errorLine = __LINE__;
+
+ switch (ref->getErrorCode()) {
+ case UtilExecuteRef::TCError:
+ switch (ref->getTCErrorCode()) {
+ case ZNOT_FOUND:
+ jam();
+ evntRecPtr.p->m_errorCode = DropEvntRef::EventNotFound;
+ break;
+ default:
+ jam();
+ evntRecPtr.p->m_errorCode = DropEvntRef::UndefinedTCError;
+ break;
+ }
+ break;
+ default:
+ jam();
+ evntRecPtr.p->m_errorCode = DropEvntRef::Undefined;
+ break;
+ }
+ dropEvent_sendReply(signal, evntRecPtr);
+}
+
+void Dbdict::dropEvent_sendReply(Signal* signal,
+ OpDropEventPtr evntRecPtr)
+{
+ jam();
+ EVENT_TRACE;
+ Uint32 senderRef = evntRecPtr.p->m_request.getUserRef();
+
+ if (evntRecPtr.p->hasError()) {
+ jam();
+ DropEvntRef * ret = (DropEvntRef *)signal->getDataPtrSend();
+
+ ret->setUserData(evntRecPtr.p->m_request.getUserData());
+ ret->setUserRef(evntRecPtr.p->m_request.getUserRef());
+
+ ret->setErrorCode(evntRecPtr.p->m_errorCode);
+ ret->setErrorLine(evntRecPtr.p->m_errorLine);
+ ret->setErrorNode(evntRecPtr.p->m_errorNode);
+
+ sendSignal(senderRef, GSN_DROP_EVNT_REF, signal,
+ DropEvntRef::SignalLength, JBB);
+ } else {
+ jam();
+ DropEvntConf * evntConf = (DropEvntConf *)signal->getDataPtrSend();
+
+ evntConf->setUserData(evntRecPtr.p->m_request.getUserData());
+ evntConf->setUserRef(evntRecPtr.p->m_request.getUserRef());
+
+ sendSignal(senderRef, GSN_DROP_EVNT_CONF, signal,
+ DropEvntConf::SignalLength, JBB);
+ }
+
+ c_opDropEvent.release(evntRecPtr);
+}
+
+/**
+ * MODULE: Alter index
+ *
+ * Alter index state. Alter online creates the index in each TC and
+ * then invokes create trigger and alter trigger protocols to activate
+ * the 3 triggers. Alter offline does the opposite.
+ *
+ * Request type received in REQ and returned in CONF/REF:
+ *
+ * RT_USER - from API to DICT master
+ * RT_CREATE_INDEX - part of create index operation
+ * RT_DROP_INDEX - part of drop index operation
+ * RT_NODERESTART - node restart, activate locally only
+ * RT_SYSTEMRESTART - system restart, activate and build if not logged
+ * RT_DICT_PREPARE - prepare participants
+ * RT_DICT_TC - to local TC via each participant
+ * RT_DICT_COMMIT - commit in each participant
+ */
+
+void
+Dbdict::execALTER_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ AlterIndxReq* const req = (AlterIndxReq*)signal->getDataPtrSend();
+ OpAlterIndexPtr opPtr;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const AlterIndxReq::RequestType requestType = req->getRequestType();
+ if (requestType == AlterIndxReq::RT_USER ||
+ requestType == AlterIndxReq::RT_CREATE_INDEX ||
+ requestType == AlterIndxReq::RT_DROP_INDEX ||
+ requestType == AlterIndxReq::RT_NODERESTART ||
+ requestType == AlterIndxReq::RT_SYSTEMRESTART) {
+ jam();
+ const bool isLocal = req->getRequestFlag() & RequestFlag::RF_LOCAL;
+ NdbNodeBitmask receiverNodes = c_aliveNodes;
+ if (isLocal) {
+ receiverNodes.clear();
+ receiverNodes.set(getOwnNodeId());
+ }
+ if (signal->getLength() == AlterIndxReq::SignalLength) {
+ jam();
+ if (! isLocal && getOwnNodeId() != c_masterNodeId) {
+ jam();
+
+ releaseSections(signal);
+ OpAlterIndex opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = AlterIndxRef::NotMaster;
+ opPtr.p->m_errorLine = __LINE__;
+ opPtr.p->m_errorNode = c_masterNodeId;
+ alterIndex_sendReply(signal, opPtr, true);
+ return;
+ }
+ // forward initial request plus operation key to all
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, receiverNodes);
+ sendSignal(rg, GSN_ALTER_INDX_REQ,
+ signal, AlterIndxReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == AlterIndxReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpAlterIndex opBusy;
+ if (! c_opAlterIndex.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = AlterIndxRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ alterIndex_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opAlterIndex.add(opPtr);
+ // master expects to hear from all
+ if (opPtr.p->m_isMaster)
+ opPtr.p->m_signalCounter = receiverNodes;
+ // check request in all participants
+ alterIndex_slavePrepare(signal, opPtr);
+ alterIndex_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opAlterIndex.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == AlterIndxReq::RT_DICT_TC) {
+ jam();
+ if (opPtr.p->m_request.getOnline())
+ alterIndex_toCreateTc(signal, opPtr);
+ else
+ alterIndex_toDropTc(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_DICT_COMMIT ||
+ requestType == AlterIndxReq::RT_DICT_ABORT) {
+ jam();
+ if (requestType == AlterIndxReq::RT_DICT_COMMIT)
+ alterIndex_slaveCommit(signal, opPtr);
+ else
+ alterIndex_slaveAbort(signal, opPtr);
+ alterIndex_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opAlterIndex.release(opPtr);
+ return;
+ }
+ }
+ jam();
+ // return to sender
+ OpAlterIndex opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = AlterIndxRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ alterIndex_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::execALTER_INDX_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+ AlterIndxConf* conf = (AlterIndxConf*)signal->getDataPtrSend();
+ alterIndex_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execALTER_INDX_REF(Signal* signal)
+{
+ jamEntry();
+ AlterIndxRef* ref = (AlterIndxRef*)signal->getDataPtrSend();
+ alterIndex_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::alterIndex_recvReply(Signal* signal, const AlterIndxConf* conf,
+ const AlterIndxRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const AlterIndxReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == AlterIndxReq::RT_CREATE_INDEX) {
+ jam();
+ // part of create index operation
+ OpCreateIndexPtr opPtr;
+ c_opCreateIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ createIndex_fromAlterIndex(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_DROP_INDEX) {
+ jam();
+ // part of drop index operation
+ OpDropIndexPtr opPtr;
+ c_opDropIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ dropIndex_fromAlterIndex(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_TC ||
+ requestType == AlterIndxReq::RT_TUX) {
+ jam();
+ // part of build index operation
+ OpBuildIndexPtr opPtr;
+ c_opBuildIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ buildIndex_fromOnline(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_NODERESTART) {
+ jam();
+ if (ref == 0) {
+ infoEvent("DICT: index %u activated", (unsigned)key);
+ } else {
+ warningEvent("DICT: index %u activation failed: code=%d line=%d",
+ (unsigned)key,
+ ref->getErrorCode(), ref->getErrorLine());
+ }
+ activateIndexes(signal, key + 1);
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_SYSTEMRESTART) {
+ jam();
+ if (ref == 0) {
+ infoEvent("DICT: index %u activated done", (unsigned)key);
+ } else {
+ warningEvent("DICT: index %u activated failed: code=%d line=%d node=%d",
+ (unsigned)key,
+ ref->getErrorCode(), ref->getErrorLine(), ref->getErrorNode());
+ }
+ activateIndexes(signal, key + 1);
+ return;
+ }
+ OpAlterIndexPtr opPtr;
+ c_opAlterIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ opPtr.p->setError(ref);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_DICT_COMMIT ||
+ requestType == AlterIndxReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ alterIndex_sendReply(signal, opPtr, true);
+ c_opAlterIndex.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_ABORT;
+ alterIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ if (indexPtr.p->isHashIndex()) {
+ if (requestType == AlterIndxReq::RT_DICT_PREPARE) {
+ jam();
+ if (opPtr.p->m_request.getOnline()) {
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_TC;
+ alterIndex_sendSlaveReq(signal, opPtr);
+ } else {
+ // start drop triggers
+ alterIndex_toDropTrigger(signal, opPtr);
+ }
+ return;
+ }
+ if (requestType == AlterIndxReq::RT_DICT_TC) {
+ jam();
+ if (opPtr.p->m_request.getOnline()) {
+ // start create triggers
+ alterIndex_toCreateTrigger(signal, opPtr);
+ } else {
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_COMMIT;
+ alterIndex_sendSlaveReq(signal, opPtr);
+ }
+ return;
+ }
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ if (requestType == AlterIndxReq::RT_DICT_PREPARE) {
+ jam();
+ if (opPtr.p->m_request.getOnline()) {
+ // start create triggers
+ alterIndex_toCreateTrigger(signal, opPtr);
+ } else {
+ // start drop triggers
+ alterIndex_toDropTrigger(signal, opPtr);
+ }
+ return;
+ }
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::alterIndex_slavePrepare(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ const AlterIndxReq* const req = &opPtr.p->m_request;
+ if (! (req->getIndexId() < c_tableRecordPool.getSize())) {
+ jam();
+ opPtr.p->m_errorCode = AlterIndxRef::Inconsistency;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, req->getIndexId());
+ if (indexPtr.p->tabState != TableRecord::DEFINED) {
+ jam();
+ opPtr.p->m_errorCode = AlterIndxRef::IndexNotFound;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ if (! indexPtr.p->isIndex()) {
+ jam();
+ opPtr.p->m_errorCode = AlterIndxRef::NotAnIndex;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ if (req->getOnline())
+ indexPtr.p->indexState = TableRecord::IS_BUILDING;
+ else
+ indexPtr.p->indexState = TableRecord::IS_DROPPING;
+}
+
+void
+Dbdict::alterIndex_toCreateTc(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ // request to create index in local TC
+ CreateIndxReq* const req = (CreateIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(CreateIndxReq::RT_TC);
+ req->setIndexType(indexPtr.p->tableType);
+ req->setTableId(indexPtr.p->primaryTableId);
+ req->setIndexId(indexPtr.i);
+ req->setOnline(true);
+ getIndexAttrList(indexPtr, opPtr.p->m_attrList);
+ // send
+ LinearSectionPtr lsPtr[3];
+ lsPtr[0].p = (Uint32*)&opPtr.p->m_attrList;
+ lsPtr[0].sz = 1 + opPtr.p->m_attrList.sz;
+ sendSignal(calcTcBlockRef(getOwnNodeId()), GSN_CREATE_INDX_REQ,
+ signal, CreateIndxReq::SignalLength, JBB, lsPtr, 1);
+}
+
+void
+Dbdict::alterIndex_fromCreateTc(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ // mark created in local TC
+ if (! opPtr.p->hasError()) {
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ indexPtr.p->indexLocal |= TableRecord::IL_CREATED_TC;
+ }
+ // forward CONF or REF to master
+ ndbrequire(opPtr.p->m_requestType == AlterIndxReq::RT_DICT_TC);
+ alterIndex_sendReply(signal, opPtr, false);
+}
+
+void
+Dbdict::alterIndex_toDropTc(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ // broken index
+ if (! (indexPtr.p->indexLocal & TableRecord::IL_CREATED_TC)) {
+ jam();
+ alterIndex_sendReply(signal, opPtr, false);
+ return;
+ }
+ // request to drop in local TC
+ DropIndxReq* const req = (DropIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(DropIndxReq::RT_TC);
+ req->setTableId(indexPtr.p->primaryTableId);
+ req->setIndexId(indexPtr.i);
+ req->setIndexVersion(indexPtr.p->tableVersion);
+ // send
+ sendSignal(calcTcBlockRef(getOwnNodeId()), GSN_DROP_INDX_REQ,
+ signal, DropIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::alterIndex_fromDropTc(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ ndbrequire(opPtr.p->m_requestType == AlterIndxReq::RT_DICT_TC);
+ if (! opPtr.p->hasError()) {
+ // mark dropped in local TC
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ indexPtr.p->indexLocal &= ~TableRecord::IL_CREATED_TC;
+ }
+ // forward CONF or REF to master
+ alterIndex_sendReply(signal, opPtr, false);
+}
+
+void
+Dbdict::alterIndex_toCreateTrigger(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ // start creation of index triggers
+ CreateTrigReq* const req = (CreateTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(CreateTrigReq::RT_ALTER_INDEX);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ req->setTableId(opPtr.p->m_request.getTableId());
+ req->setIndexId(opPtr.p->m_request.getIndexId());
+ req->setTriggerId(RNIL);
+ req->setTriggerActionTime(TriggerActionTime::TA_AFTER);
+ req->setMonitorAllAttributes(false);
+ req->setOnline(true); // alter online after create
+ req->setReceiverRef(0); // implicit for index triggers
+ getIndexAttrMask(indexPtr, req->getAttributeMask());
+ // name section
+ char triggerName[MAX_TAB_NAME_SIZE];
+ Uint32 buffer[2 + ((MAX_TAB_NAME_SIZE + 3) >> 2)]; // SP string
+ LinearWriter w(buffer, sizeof(buffer) >> 2);
+ LinearSectionPtr lsPtr[3];
+ if (indexPtr.p->isHashIndex()) {
+ req->setTriggerType(TriggerType::SECONDARY_INDEX);
+ req->setMonitorReplicas(false);
+ // insert
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL)
+ req->setTriggerId(indexPtr.p->insertTriggerId);
+ req->setTriggerEvent(TriggerEvent::TE_INSERT);
+ sprintf(triggerName, "NDB$INDEX_%u_INSERT", opPtr.p->m_request.getIndexId());
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = buffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(reference(), GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB, lsPtr, 1);
+ // update
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL)
+ req->setTriggerId(indexPtr.p->updateTriggerId);
+ req->setTriggerEvent(TriggerEvent::TE_UPDATE);
+ sprintf(triggerName, "NDB$INDEX_%u_UPDATE", opPtr.p->m_request.getIndexId());
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = buffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(reference(), GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB, lsPtr, 1);
+ // delete
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL)
+ req->setTriggerId(indexPtr.p->deleteTriggerId);
+ req->setTriggerEvent(TriggerEvent::TE_DELETE);
+ sprintf(triggerName, "NDB$INDEX_%u_DELETE", opPtr.p->m_request.getIndexId());
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = buffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(reference(), GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB, lsPtr, 1);
+ // triggers left to create
+ opPtr.p->m_triggerCounter = 3;
+ return;
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ req->addRequestFlag(RequestFlag::RF_NOTCTRIGGER);
+ req->setTriggerType(TriggerType::ORDERED_INDEX);
+ req->setTriggerActionTime(TriggerActionTime::TA_CUSTOM);
+ req->setMonitorReplicas(true);
+ // one trigger for 5 events (insert, update, delete, commit, abort)
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL)
+ req->setTriggerId(indexPtr.p->customTriggerId);
+ req->setTriggerEvent(TriggerEvent::TE_CUSTOM);
+ sprintf(triggerName, "NDB$INDEX_%u_CUSTOM", opPtr.p->m_request.getIndexId());
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = buffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(reference(), GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB, lsPtr, 1);
+ // triggers left to create
+ opPtr.p->m_triggerCounter = 1;
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::alterIndex_fromCreateTrigger(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ ndbrequire(opPtr.p->m_triggerCounter != 0);
+ if (--opPtr.p->m_triggerCounter != 0) {
+ jam();
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_ABORT;
+ alterIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if(opPtr.p->m_requestType != AlterIndxReq::RT_SYSTEMRESTART){
+ // send build request
+ alterIndex_toBuildIndex(signal, opPtr);
+ return;
+ }
+
+ /**
+ * During system restart,
+ * leave index in activated but not build state.
+ *
+ * Build a bit later when REDO has been run
+ */
+ alterIndex_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::alterIndex_toDropTrigger(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ // start drop of index triggers
+ DropTrigReq* const req = (DropTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(DropTrigReq::RT_ALTER_INDEX);
+ req->setTableId(opPtr.p->m_request.getTableId());
+ req->setIndexId(opPtr.p->m_request.getIndexId());
+ req->setTriggerInfo(0); // not used
+ opPtr.p->m_triggerCounter = 0;
+ // insert
+ if (indexPtr.p->insertTriggerId != RNIL) {
+ req->setTriggerId(indexPtr.p->insertTriggerId);
+ sendSignal(reference(), GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ opPtr.p->m_triggerCounter++;
+ }
+ // update
+ if (indexPtr.p->updateTriggerId != RNIL) {
+ req->setTriggerId(indexPtr.p->updateTriggerId);
+ sendSignal(reference(), GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ opPtr.p->m_triggerCounter++;
+ }
+ // delete
+ if (indexPtr.p->deleteTriggerId != RNIL) {
+ req->setTriggerId(indexPtr.p->deleteTriggerId);
+ sendSignal(reference(), GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ opPtr.p->m_triggerCounter++;
+ }
+ // custom
+ if (indexPtr.p->customTriggerId != RNIL) {
+ req->setTriggerId(indexPtr.p->customTriggerId);
+ sendSignal(reference(), GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ opPtr.p->m_triggerCounter++;
+ }
+ // build
+ if (indexPtr.p->buildTriggerId != RNIL) {
+ req->setTriggerId(indexPtr.p->buildTriggerId);
+ sendSignal(reference(), GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ opPtr.p->m_triggerCounter++;
+ }
+ if (opPtr.p->m_triggerCounter == 0) {
+ // drop in each TC
+ jam();
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_TC;
+ alterIndex_sendSlaveReq(signal, opPtr);
+ }
+}
+
+void
+Dbdict::alterIndex_fromDropTrigger(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ ndbrequire(opPtr.p->m_triggerCounter != 0);
+ if (--opPtr.p->m_triggerCounter != 0) {
+ jam();
+ return;
+ }
+ // finally drop index in each TC
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ const bool isHashIndex = indexPtr.p->isHashIndex();
+ const bool isOrderedIndex = indexPtr.p->isOrderedIndex();
+ ndbrequire(isHashIndex != isOrderedIndex); // xor
+ if (isHashIndex)
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_TC;
+ if (isOrderedIndex)
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_COMMIT;
+ alterIndex_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::alterIndex_toBuildIndex(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ // get index and table records
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, indexPtr.p->primaryTableId);
+ // build request to self (short signal)
+ BuildIndxReq* const req = (BuildIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(BuildIndxReq::RT_ALTER_INDEX);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ req->setBuildId(0); // not used
+ req->setBuildKey(0); // not used
+ req->setIndexType(indexPtr.p->tableType);
+ req->setIndexId(indexPtr.i);
+ req->setTableId(indexPtr.p->primaryTableId);
+ req->setParallelism(16);
+ // send
+ sendSignal(reference(), GSN_BUILDINDXREQ,
+ signal, BuildIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::alterIndex_fromBuildIndex(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_ABORT;
+ alterIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = AlterIndxReq::RT_DICT_COMMIT;
+ alterIndex_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::alterIndex_slaveCommit(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ // get index record
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ indexPtr.p->indexState = TableRecord::IS_ONLINE;
+}
+
+void
+Dbdict::alterIndex_slaveAbort(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ jam();
+ // find index record
+ const Uint32 indexId = opPtr.p->m_request.getIndexId();
+ if (indexId >= c_tableRecordPool.getSize())
+ return;
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, indexId);
+ if (! indexPtr.p->isIndex())
+ return;
+ // mark broken
+ indexPtr.p->indexState = TableRecord::IS_BROKEN;
+}
+
+void
+Dbdict::alterIndex_sendSlaveReq(Signal* signal, OpAlterIndexPtr opPtr)
+{
+ AlterIndxReq* const req = (AlterIndxReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ NdbNodeBitmask receiverNodes = c_aliveNodes;
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL) {
+ receiverNodes.clear();
+ receiverNodes.set(getOwnNodeId());
+ }
+ opPtr.p->m_signalCounter = receiverNodes;
+ NodeReceiverGroup rg(DBDICT, receiverNodes);
+ sendSignal(rg, GSN_ALTER_INDX_REQ,
+ signal, AlterIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::alterIndex_sendReply(Signal* signal, OpAlterIndexPtr opPtr,
+ bool toUser)
+{
+ AlterIndxRef* rep = (AlterIndxRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_ALTER_INDX_CONF;
+ Uint32 length = AlterIndxConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == AlterIndxReq::RT_DICT_ABORT)
+ sendRef = false;
+ } else {
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = AlterIndxConf::SignalLength;
+ }
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setIndexId(opPtr.p->m_request.getIndexId());
+ if (sendRef) {
+ if (opPtr.p->m_errorNode == 0)
+ opPtr.p->m_errorNode = getOwnNodeId();
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->setErrorLine(opPtr.p->m_errorLine);
+ rep->setErrorNode(opPtr.p->m_errorNode);
+ gsn = GSN_ALTER_INDX_REF;
+ length = AlterIndxRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/**
+ * MODULE: Build index
+ *
+ * Build index or all indexes on a table. Request type:
+ *
+ * RT_USER - normal user request, not yet used
+ * RT_ALTER_INDEX - from alter index
+ * RT_SYSTEM_RESTART -
+ * RT_DICT_PREPARE - prepare participants
+ * RT_DICT_TRIX - to participant on way to local TRIX
+ * RT_DICT_COMMIT - commit in each participant
+ * RT_DICT_ABORT - abort
+ * RT_TRIX - to local TRIX
+ */
+
+void
+Dbdict::execBUILDINDXREQ(Signal* signal)
+{
+ jamEntry();
+ BuildIndxReq* const req = (BuildIndxReq*)signal->getDataPtrSend();
+ OpBuildIndexPtr opPtr;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const BuildIndxReq::RequestType requestType = req->getRequestType();
+ if (requestType == BuildIndxReq::RT_USER ||
+ requestType == BuildIndxReq::RT_ALTER_INDEX ||
+ requestType == BuildIndxReq::RT_SYSTEMRESTART) {
+ jam();
+ if (signal->getLength() == BuildIndxReq::SignalLength) {
+ jam();
+ if (getOwnNodeId() != c_masterNodeId) {
+ jam();
+
+ releaseSections(signal);
+ OpBuildIndex opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = BuildIndxRef::NotMaster;
+ opPtr.p->m_errorLine = __LINE__;
+ opPtr.p->m_errorNode = c_masterNodeId;
+ buildIndex_sendReply(signal, opPtr, true);
+ return;
+ }
+ // forward initial request plus operation key to all
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_BUILDINDXREQ,
+ signal, BuildIndxReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == BuildIndxReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpBuildIndex opBusy;
+ if (! c_opBuildIndex.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = BuildIndxRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ buildIndex_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opBuildIndex.add(opPtr);
+ // master expects to hear from all
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ buildIndex_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opBuildIndex.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == BuildIndxReq::RT_DICT_TRIX) {
+ jam();
+ buildIndex_buildTrix(signal, opPtr);
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_TC ||
+ requestType == BuildIndxReq::RT_DICT_TUX) {
+ jam();
+ buildIndex_toOnline(signal, opPtr);
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_COMMIT ||
+ requestType == BuildIndxReq::RT_DICT_ABORT) {
+ jam();
+ buildIndex_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opBuildIndex.release(opPtr);
+ return;
+ }
+ }
+ jam();
+ // return to sender
+ OpBuildIndex opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = BuildIndxRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ buildIndex_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::execBUILDINDXCONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+ BuildIndxConf* conf = (BuildIndxConf*)signal->getDataPtrSend();
+ buildIndex_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execBUILDINDXREF(Signal* signal)
+{
+ jamEntry();
+ BuildIndxRef* ref = (BuildIndxRef*)signal->getDataPtrSend();
+ buildIndex_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::buildIndex_recvReply(Signal* signal, const BuildIndxConf* conf,
+ const BuildIndxRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const BuildIndxReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == BuildIndxReq::RT_ALTER_INDEX) {
+ jam();
+ // part of alter index operation
+ OpAlterIndexPtr opPtr;
+ c_opAlterIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterIndex_fromBuildIndex(signal, opPtr);
+ return;
+ }
+
+ if (requestType == BuildIndxReq::RT_SYSTEMRESTART) {
+ jam();
+ if (ref == 0) {
+ infoEvent("DICT: index %u rebuild done", (unsigned)key);
+ } else {
+ warningEvent("DICT: index %u rebuild failed: code=%d line=%d node=%d",
+ (unsigned)key, ref->getErrorCode());
+ }
+ rebuildIndexes(signal, key + 1);
+ return;
+ }
+
+ OpBuildIndexPtr opPtr;
+ c_opBuildIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ if (requestType == BuildIndxReq::RT_TRIX) {
+ jam();
+ // forward to master
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TRIX;
+ buildIndex_sendReply(signal, opPtr, false);
+ return;
+ }
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_COMMIT ||
+ requestType == BuildIndxReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ buildIndex_sendReply(signal, opPtr, true);
+ c_opBuildIndex.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_ABORT;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ if (indexPtr.p->isHashIndex()) {
+ if (requestType == BuildIndxReq::RT_DICT_PREPARE) {
+ jam();
+ if (! (opPtr.p->m_requestFlag & RequestFlag::RF_NOBUILD)) {
+ buildIndex_toCreateConstr(signal, opPtr);
+ } else {
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TC;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ }
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_TRIX) {
+ jam();
+ ndbrequire(! (opPtr.p->m_requestFlag & RequestFlag::RF_NOBUILD));
+ buildIndex_toDropConstr(signal, opPtr);
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_TC) {
+ jam();
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_COMMIT;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ if (requestType == BuildIndxReq::RT_DICT_PREPARE) {
+ jam();
+ if (! (opPtr.p->m_requestFlag & RequestFlag::RF_NOBUILD)) {
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TRIX;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ } else {
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TUX;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ }
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_TRIX) {
+ jam();
+ ndbrequire(! (opPtr.p->m_requestFlag & RequestFlag::RF_NOBUILD));
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TUX;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == BuildIndxReq::RT_DICT_TUX) {
+ jam();
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_COMMIT;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::buildIndex_toCreateConstr(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ // request to create constraint trigger
+ CreateTrigReq* req = (CreateTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(CreateTrigReq::RT_BUILD_INDEX);
+ req->addRequestFlag(0); // none
+ req->setTableId(indexPtr.i);
+ req->setIndexId(RNIL);
+ req->setTriggerId(RNIL);
+ req->setTriggerType(TriggerType::READ_ONLY_CONSTRAINT);
+ req->setTriggerActionTime(TriggerActionTime::TA_AFTER);
+ req->setTriggerEvent(TriggerEvent::TE_UPDATE);
+ req->setMonitorReplicas(false);
+ req->setMonitorAllAttributes(false);
+ req->setOnline(true); // alter online after create
+ req->setReceiverRef(0); // no receiver, REF-ed by TUP
+ req->getAttributeMask().clear();
+ // NDB$PK is last attribute
+ req->getAttributeMask().set(indexPtr.p->noOfAttributes - 1);
+ // name section
+ char triggerName[MAX_TAB_NAME_SIZE];
+ Uint32 buffer[2 + ((MAX_TAB_NAME_SIZE + 3) >> 2)]; // SP string
+ LinearWriter w(buffer, sizeof(buffer) >> 2);
+ LinearSectionPtr lsPtr[3];
+ sprintf(triggerName, "NDB$INDEX_%u_BUILD", indexPtr.i);
+ w.reset();
+ w.add(CreateTrigReq::TriggerNameKey, triggerName);
+ lsPtr[0].p = buffer;
+ lsPtr[0].sz = w.getWordsUsed();
+ sendSignal(reference(), GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB, lsPtr, 1);
+}
+
+void
+Dbdict::buildIndex_fromCreateConstr(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_ABORT;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TRIX;
+ buildIndex_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::buildIndex_buildTrix(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, indexPtr.p->primaryTableId);
+ // build request
+ BuildIndxReq* const req = (BuildIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(BuildIndxReq::RT_TRIX);
+ req->setBuildId(0); // not yet..
+ req->setBuildKey(0); // ..in use
+ req->setIndexType(indexPtr.p->tableType);
+ req->setIndexId(indexPtr.i);
+ req->setTableId(indexPtr.p->primaryTableId);
+ req->setParallelism(16);
+ if (indexPtr.p->isHashIndex()) {
+ jam();
+ getIndexAttrList(indexPtr, opPtr.p->m_attrList);
+ getTableKeyList(tablePtr, opPtr.p->m_tableKeyList);
+ // send
+ LinearSectionPtr lsPtr[3];
+ lsPtr[0].sz = opPtr.p->m_attrList.sz;
+ lsPtr[0].p = opPtr.p->m_attrList.id;
+ lsPtr[1].sz = opPtr.p->m_tableKeyList.sz;
+ lsPtr[1].p = opPtr.p->m_tableKeyList.id;
+ sendSignal(calcTrixBlockRef(getOwnNodeId()), GSN_BUILDINDXREQ,
+ signal, BuildIndxReq::SignalLength, JBB, lsPtr, 2);
+ return;
+ }
+ if (indexPtr.p->isOrderedIndex()) {
+ jam();
+ sendSignal(calcTupBlockRef(getOwnNodeId()), GSN_BUILDINDXREQ,
+ signal, BuildIndxReq::SignalLength, JBB);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::buildIndex_toDropConstr(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ // request to drop constraint trigger
+ DropTrigReq* req = (DropTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(DropTrigReq::RT_BUILD_INDEX);
+ req->addRequestFlag(0); // none
+ req->setTableId(indexPtr.i);
+ req->setIndexId(RNIL);
+ req->setTriggerId(opPtr.p->m_constrTriggerId);
+ req->setTriggerInfo(0); // not used
+ sendSignal(reference(), GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::buildIndex_fromDropConstr(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_ABORT;
+ buildIndex_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = BuildIndxReq::RT_DICT_TC;
+ buildIndex_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::buildIndex_toOnline(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, opPtr.p->m_request.getIndexId());
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, indexPtr.p->primaryTableId);
+ // request to set index online in TC or TUX
+ AlterIndxReq* const req = (AlterIndxReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ if (opPtr.p->m_requestType == BuildIndxReq::RT_DICT_TC) {
+ req->setRequestType(AlterIndxReq::RT_TC);
+ } else if (opPtr.p->m_requestType == BuildIndxReq::RT_DICT_TUX) {
+ req->setRequestType(AlterIndxReq::RT_TUX);
+ } else {
+ ndbrequire(false);
+ }
+ req->setTableId(tablePtr.i);
+ req->setIndexId(indexPtr.i);
+ req->setIndexVersion(indexPtr.p->tableVersion);
+ req->setOnline(true);
+ BlockReference blockRef = 0;
+ if (opPtr.p->m_requestType == BuildIndxReq::RT_DICT_TC) {
+ blockRef = calcTcBlockRef(getOwnNodeId());
+ } else if (opPtr.p->m_requestType == BuildIndxReq::RT_DICT_TUX) {
+ blockRef = calcTuxBlockRef(getOwnNodeId());
+ } else {
+ ndbrequire(false);
+ }
+ // send
+ sendSignal(blockRef, GSN_ALTER_INDX_REQ,
+ signal, BuildIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::buildIndex_fromOnline(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ jam();
+ // forward to master
+ buildIndex_sendReply(signal, opPtr, false);
+}
+
+void
+Dbdict::buildIndex_sendSlaveReq(Signal* signal, OpBuildIndexPtr opPtr)
+{
+ BuildIndxReq* const req = (BuildIndxReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_BUILDINDXREQ,
+ signal, BuildIndxReq::SignalLength, JBB);
+}
+
+void
+Dbdict::buildIndex_sendReply(Signal* signal, OpBuildIndexPtr opPtr,
+ bool toUser)
+{
+ BuildIndxRef* rep = (BuildIndxRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_BUILDINDXCONF;
+ Uint32 length = BuildIndxConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == BuildIndxReq::RT_DICT_ABORT)
+ sendRef = false;
+ } else {
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = BuildIndxConf::SignalLength;
+ }
+ rep->setIndexType(opPtr.p->m_request.getIndexType());
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setIndexId(opPtr.p->m_request.getIndexId());
+ if (sendRef) {
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->masterNodeId = opPtr.p->m_errorNode;
+ gsn = GSN_BUILDINDXREF;
+ length = BuildIndxRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/**
+ * MODULE: Create trigger
+ *
+ * Create trigger in all DICT blocks. Optionally start alter trigger
+ * operation to set the trigger online.
+ *
+ * Request type received in REQ and returned in CONF/REF:
+ *
+ * RT_USER - normal user e.g. BACKUP
+ * RT_ALTER_INDEX - from alter index online
+ * RT_DICT_PREPARE - seize operation in each DICT
+ * RT_DICT_COMMIT - commit create in each DICT
+ * RT_TC - sending to TC (operation alter trigger)
+ * RT_LQH - sending to LQH (operation alter trigger)
+ */
+
+void
+Dbdict::execCREATE_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ CreateTrigReq* const req = (CreateTrigReq*)signal->getDataPtrSend();
+ OpCreateTriggerPtr opPtr;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const CreateTrigReq::RequestType requestType = req->getRequestType();
+ if (requestType == CreateTrigReq::RT_USER ||
+ requestType == CreateTrigReq::RT_ALTER_INDEX ||
+ requestType == CreateTrigReq::RT_BUILD_INDEX) {
+ jam();
+ if (! assembleFragments(signal)) {
+ jam();
+ return;
+ }
+ const bool isLocal = req->getRequestFlag() & RequestFlag::RF_LOCAL;
+ NdbNodeBitmask receiverNodes = c_aliveNodes;
+ if (isLocal) {
+ receiverNodes.clear();
+ receiverNodes.set(getOwnNodeId());
+ }
+ if (signal->getLength() == CreateTrigReq::SignalLength) {
+ jam();
+ if (! isLocal && getOwnNodeId() != c_masterNodeId) {
+ jam();
+
+ releaseSections(signal);
+ OpCreateTrigger opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = CreateTrigRef::NotMaster;
+ opPtr.p->m_errorLine = __LINE__;
+ opPtr.p->m_errorNode = c_masterNodeId;
+ createTrigger_sendReply(signal, opPtr, true);
+ return;
+ }
+ // forward initial request plus operation key to all
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, receiverNodes);
+ sendSignal(rg, GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == CreateTrigReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpCreateTrigger opBusy;
+ if (! c_opCreateTrigger.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ releaseSections(signal);
+ createTrigger_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opCreateTrigger.add(opPtr);
+ {
+ // save name
+ SegmentedSectionPtr ssPtr;
+ signal->getSection(ssPtr, CreateTrigReq::TRIGGER_NAME_SECTION);
+ SimplePropertiesSectionReader ssReader(ssPtr, getSectionSegmentPool());
+ if (ssReader.getKey() != CreateTrigReq::TriggerNameKey ||
+ ! ssReader.getString(opPtr.p->m_triggerName)) {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::InvalidName;
+ opPtr.p->m_errorLine = __LINE__;
+ releaseSections(signal);
+ createTrigger_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ }
+ releaseSections(signal);
+ {
+ // check that trigger name is unique
+ TriggerRecordPtr triggerPtr;
+ TriggerRecord keyRecord;
+ strcpy(keyRecord.triggerName, opPtr.p->m_triggerName);
+ c_triggerRecordHash.find(triggerPtr, keyRecord);
+ if (triggerPtr.i != RNIL) {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::TriggerExists;
+ opPtr.p->m_errorLine = __LINE__;
+ createTrigger_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ }
+
+ // master expects to hear from all
+ if (opPtr.p->m_isMaster)
+ opPtr.p->m_signalCounter = receiverNodes;
+ // check request in all participants
+ createTrigger_slavePrepare(signal, opPtr);
+ createTrigger_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opCreateTrigger.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == CreateTrigReq::RT_DICT_CREATE) {
+ jam();
+ // master has set trigger id
+ opPtr.p->m_request.setTriggerId(req->getTriggerId());
+ createTrigger_slaveCreate(signal, opPtr);
+ createTrigger_sendReply(signal, opPtr, false);
+ return;
+ }
+ if (requestType == CreateTrigReq::RT_DICT_COMMIT ||
+ requestType == CreateTrigReq::RT_DICT_ABORT) {
+ jam();
+ if (requestType == CreateTrigReq::RT_DICT_COMMIT)
+ createTrigger_slaveCommit(signal, opPtr);
+ else
+ createTrigger_slaveAbort(signal, opPtr);
+ createTrigger_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opCreateTrigger.release(opPtr);
+ return;
+ }
+ }
+ jam();
+ // return to sender
+ releaseSections(signal);
+ OpCreateTrigger opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = CreateTrigRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ createTrigger_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::execCREATE_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(signal->getNoOfSections() == 0);
+ CreateTrigConf* conf = (CreateTrigConf*)signal->getDataPtrSend();
+ createTrigger_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execCREATE_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+ CreateTrigRef* ref = (CreateTrigRef*)signal->getDataPtrSend();
+ createTrigger_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::createTrigger_recvReply(Signal* signal, const CreateTrigConf* conf,
+ const CreateTrigRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const CreateTrigReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == CreateTrigReq::RT_ALTER_INDEX) {
+ jam();
+ // part of alter index operation
+ OpAlterIndexPtr opPtr;
+ c_opAlterIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterIndex_fromCreateTrigger(signal, opPtr);
+ return;
+ }
+ if (requestType == CreateTrigReq::RT_BUILD_INDEX) {
+ jam();
+ // part of build index operation
+ OpBuildIndexPtr opPtr;
+ c_opBuildIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ // fill in trigger id
+ opPtr.p->m_constrTriggerId = conf->getTriggerId();
+ buildIndex_fromCreateConstr(signal, opPtr);
+ return;
+ }
+ if (requestType == CreateTrigReq::RT_TC ||
+ requestType == CreateTrigReq::RT_LQH) {
+ jam();
+ // part of alter trigger operation
+ OpAlterTriggerPtr opPtr;
+ c_opAlterTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterTrigger_fromCreateLocal(signal, opPtr);
+ return;
+ }
+ OpCreateTriggerPtr opPtr;
+ c_opCreateTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ opPtr.p->setError(ref);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == CreateTrigReq::RT_DICT_COMMIT ||
+ requestType == CreateTrigReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ createTrigger_sendReply(signal, opPtr, true);
+ c_opCreateTrigger.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_ABORT;
+ createTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == CreateTrigReq::RT_DICT_PREPARE) {
+ jam();
+ // seize trigger id in master
+ createTrigger_masterSeize(signal, opPtr);
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_ABORT;
+ createTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_CREATE;
+ createTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == CreateTrigReq::RT_DICT_CREATE) {
+ jam();
+ if (opPtr.p->m_request.getOnline()) {
+ jam();
+ // start alter online
+ createTrigger_toAlterTrigger(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_COMMIT;
+ createTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::createTrigger_slavePrepare(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ jam();
+ const CreateTrigReq* const req = &opPtr.p->m_request;
+ // check trigger type
+ if (req->getRequestType() == CreateTrigReq::RT_USER &&
+ req->getTriggerType() == TriggerType::SUBSCRIPTION ||
+ req->getRequestType() == CreateTrigReq::RT_ALTER_INDEX &&
+ req->getTriggerType() == TriggerType::SECONDARY_INDEX ||
+ req->getRequestType() == CreateTrigReq::RT_ALTER_INDEX &&
+ req->getTriggerType() == TriggerType::ORDERED_INDEX ||
+ req->getRequestType() == CreateTrigReq::RT_BUILD_INDEX &&
+ req->getTriggerType() == TriggerType::READ_ONLY_CONSTRAINT) {
+ ;
+ } else {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::UnsupportedTriggerType;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ // check the table
+ const Uint32 tableId = req->getTableId();
+ if (! (tableId < c_tableRecordPool.getSize())) {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::InvalidTable;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ if (tablePtr.p->tabState != TableRecord::DEFINED) {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::InvalidTable;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+}
+
+void
+Dbdict::createTrigger_masterSeize(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ TriggerRecordPtr triggerPtr;
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL) {
+ triggerPtr.i = opPtr.p->m_request.getTriggerId();
+ } else {
+ triggerPtr.i = getFreeTriggerRecord();
+ if (triggerPtr.i == RNIL) {
+ jam();
+ opPtr.p->m_errorCode = CreateTrigRef::TooManyTriggers;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ }
+ c_triggerRecordPool.getPtr(triggerPtr);
+ initialiseTriggerRecord(triggerPtr);
+ triggerPtr.p->triggerState = TriggerRecord::TS_DEFINING;
+ opPtr.p->m_request.setTriggerId(triggerPtr.i);
+}
+
+void
+Dbdict::createTrigger_slaveCreate(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ jam();
+ const CreateTrigReq* const req = &opPtr.p->m_request;
+ // get the trigger record
+ const Uint32 triggerId = req->getTriggerId();
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, triggerId);
+ initialiseTriggerRecord(triggerPtr);
+ // fill in trigger data
+ strcpy(triggerPtr.p->triggerName, opPtr.p->m_triggerName);
+ triggerPtr.p->triggerId = triggerId;
+ triggerPtr.p->tableId = req->getTableId();
+ triggerPtr.p->indexId = RNIL;
+ triggerPtr.p->triggerType = req->getTriggerType();
+ triggerPtr.p->triggerActionTime = req->getTriggerActionTime();
+ triggerPtr.p->triggerEvent = req->getTriggerEvent();
+ triggerPtr.p->monitorReplicas = req->getMonitorReplicas();
+ triggerPtr.p->monitorAllAttributes = req->getMonitorAllAttributes();
+ triggerPtr.p->attributeMask = req->getAttributeMask();
+ triggerPtr.p->triggerState = TriggerRecord::TS_OFFLINE;
+ // add to hash table
+ // ndbout_c("++++++++++++ Adding trigger id %u, %s", triggerPtr.p->triggerId, triggerPtr.p->triggerName);
+ c_triggerRecordHash.add(triggerPtr);
+ if (triggerPtr.p->triggerType == TriggerType::SECONDARY_INDEX ||
+ triggerPtr.p->triggerType == TriggerType::ORDERED_INDEX) {
+ jam();
+ // connect to index record XXX should be done in caller instead
+ triggerPtr.p->indexId = req->getIndexId();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, triggerPtr.p->indexId);
+ switch (triggerPtr.p->triggerEvent) {
+ case TriggerEvent::TE_INSERT:
+ indexPtr.p->insertTriggerId = triggerPtr.p->triggerId;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ indexPtr.p->updateTriggerId = triggerPtr.p->triggerId;
+ break;
+ case TriggerEvent::TE_DELETE:
+ indexPtr.p->deleteTriggerId = triggerPtr.p->triggerId;
+ break;
+ case TriggerEvent::TE_CUSTOM:
+ indexPtr.p->customTriggerId = triggerPtr.p->triggerId;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ }
+ if (triggerPtr.p->triggerType == TriggerType::READ_ONLY_CONSTRAINT) {
+ jam();
+ // connect to index record XXX should be done in caller instead
+ triggerPtr.p->indexId = req->getTableId();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, triggerPtr.p->indexId);
+ indexPtr.p->buildTriggerId = triggerPtr.p->triggerId;
+ }
+}
+
+void
+Dbdict::createTrigger_toAlterTrigger(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ jam();
+ AlterTrigReq* req = (AlterTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(AlterTrigReq::RT_CREATE_TRIGGER);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ req->setTableId(opPtr.p->m_request.getTableId());
+ req->setTriggerId(opPtr.p->m_request.getTriggerId());
+ req->setTriggerInfo(0); // not used
+ req->setOnline(true);
+ req->setReceiverRef(opPtr.p->m_request.getReceiverRef());
+ sendSignal(reference(), GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::createTrigger_fromAlterTrigger(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ jam();
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_ABORT;
+ createTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ opPtr.p->m_requestType = CreateTrigReq::RT_DICT_COMMIT;
+ createTrigger_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::createTrigger_slaveCommit(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ jam();
+ const CreateTrigReq* const req = &opPtr.p->m_request;
+ // get the trigger record
+ const Uint32 triggerId = req->getTriggerId();
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, triggerId);
+ if (! req->getOnline()) {
+ triggerPtr.p->triggerState = TriggerRecord::TS_OFFLINE;
+ } else {
+ ndbrequire(triggerPtr.p->triggerState == TriggerRecord::TS_ONLINE);
+ }
+}
+
+void
+Dbdict::createTrigger_slaveAbort(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ jam();
+}
+
+void
+Dbdict::createTrigger_sendSlaveReq(Signal* signal, OpCreateTriggerPtr opPtr)
+{
+ CreateTrigReq* const req = (CreateTrigReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ NdbNodeBitmask receiverNodes = c_aliveNodes;
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL) {
+ receiverNodes.clear();
+ receiverNodes.set(getOwnNodeId());
+ }
+ opPtr.p->m_signalCounter = receiverNodes;
+ NodeReceiverGroup rg(DBDICT, receiverNodes);
+ sendSignal(rg, GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::createTrigger_sendReply(Signal* signal, OpCreateTriggerPtr opPtr,
+ bool toUser)
+{
+ CreateTrigRef* rep = (CreateTrigRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_CREATE_TRIG_CONF;
+ Uint32 length = CreateTrigConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == CreateTrigReq::RT_DICT_ABORT)
+ sendRef = false;
+ } else {
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = CreateTrigConf::SignalLength;
+ }
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setIndexId(opPtr.p->m_request.getIndexId());
+ rep->setTriggerId(opPtr.p->m_request.getTriggerId());
+ rep->setTriggerInfo(opPtr.p->m_request.getTriggerInfo());
+ if (sendRef) {
+ if (opPtr.p->m_errorNode == 0)
+ opPtr.p->m_errorNode = getOwnNodeId();
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->setErrorLine(opPtr.p->m_errorLine);
+ rep->setErrorNode(opPtr.p->m_errorNode);
+ gsn = GSN_CREATE_TRIG_REF;
+ length = CreateTrigRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/**
+ * MODULE: Drop trigger.
+ */
+
+void
+Dbdict::execDROP_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ DropTrigReq* const req = (DropTrigReq*)signal->getDataPtrSend();
+ OpDropTriggerPtr opPtr;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const DropTrigReq::RequestType requestType = req->getRequestType();
+
+ if (signal->getNoOfSections() > 0) {
+ ndbrequire(signal->getNoOfSections() == 1);
+ jam();
+ TriggerRecord keyRecord;
+ OpDropTrigger opTmp;
+ opPtr.p=&opTmp;
+
+ SegmentedSectionPtr ssPtr;
+ signal->getSection(ssPtr, DropTrigReq::TRIGGER_NAME_SECTION);
+ SimplePropertiesSectionReader ssReader(ssPtr, getSectionSegmentPool());
+ if (ssReader.getKey() != DropTrigReq::TriggerNameKey ||
+ ! ssReader.getString(keyRecord.triggerName)) {
+ jam();
+ opPtr.p->m_errorCode = DropTrigRef::InvalidName;
+ opPtr.p->m_errorLine = __LINE__;
+ releaseSections(signal);
+ dropTrigger_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ releaseSections(signal);
+
+ TriggerRecordPtr triggerPtr;
+
+ // ndbout_c("++++++++++++++ Looking for trigger %s", keyRecord.triggerName);
+ c_triggerRecordHash.find(triggerPtr, keyRecord);
+ if (triggerPtr.i == RNIL) {
+ jam();
+ req->setTriggerId(RNIL);
+ } else {
+ jam();
+ // ndbout_c("++++++++++ Found trigger %s", triggerPtr.p->triggerName);
+ req->setTriggerId(triggerPtr.p->triggerId);
+ req->setTableId(triggerPtr.p->tableId);
+ }
+ }
+ if (requestType == DropTrigReq::RT_USER ||
+ requestType == DropTrigReq::RT_ALTER_INDEX ||
+ requestType == DropTrigReq::RT_BUILD_INDEX) {
+ jam();
+ if (signal->getLength() == DropTrigReq::SignalLength) {
+ if (getOwnNodeId() != c_masterNodeId) {
+ jam();
+ // forward to DICT master
+ sendSignal(calcDictBlockRef(c_masterNodeId), GSN_DROP_TRIG_REQ,
+ signal, signal->getLength(), JBB);
+ return;
+ }
+ if (!c_triggerRecordPool.findId(req->getTriggerId())) {
+ jam();
+ // return to sender
+ OpDropTrigger opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = DropTrigRef::TriggerNotFound;
+ opPtr.p->m_errorLine = __LINE__;
+ dropTrigger_sendReply(signal, opPtr, true);
+ return;
+ }
+ // forward initial request plus operation key to all
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == DropTrigReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpDropTrigger opBusy;
+ if (! c_opDropTrigger.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = DropTrigReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = DropTrigRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ dropTrigger_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opDropTrigger.add(opPtr);
+ // master expects to hear from all
+ if (opPtr.p->m_isMaster)
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ dropTrigger_slavePrepare(signal, opPtr);
+ dropTrigger_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opDropTrigger.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == DropTrigReq::RT_DICT_COMMIT ||
+ requestType == DropTrigReq::RT_DICT_ABORT) {
+ jam();
+ if (requestType == DropTrigReq::RT_DICT_COMMIT)
+ dropTrigger_slaveCommit(signal, opPtr);
+ else
+ dropTrigger_slaveAbort(signal, opPtr);
+ dropTrigger_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opDropTrigger.release(opPtr);
+ return;
+ }
+ }
+ jam();
+ // return to sender
+ OpDropTrigger opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = DropTrigRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ dropTrigger_sendReply(signal, opPtr, true);
+}
+
+void
+Dbdict::execDROP_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+ DropTrigConf* conf = (DropTrigConf*)signal->getDataPtrSend();
+ dropTrigger_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execDROP_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+ DropTrigRef* ref = (DropTrigRef*)signal->getDataPtrSend();
+ dropTrigger_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::dropTrigger_recvReply(Signal* signal, const DropTrigConf* conf,
+ const DropTrigRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const DropTrigReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == DropTrigReq::RT_ALTER_INDEX) {
+ jam();
+ // part of alter index operation
+ OpAlterIndexPtr opPtr;
+ c_opAlterIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterIndex_fromDropTrigger(signal, opPtr);
+ return;
+ }
+ if (requestType == DropTrigReq::RT_BUILD_INDEX) {
+ jam();
+ // part of build index operation
+ OpBuildIndexPtr opPtr;
+ c_opBuildIndex.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ buildIndex_fromDropConstr(signal, opPtr);
+ return;
+ }
+ if (requestType == DropTrigReq::RT_TC ||
+ requestType == DropTrigReq::RT_LQH) {
+ jam();
+ // part of alter trigger operation
+ OpAlterTriggerPtr opPtr;
+ c_opAlterTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ alterTrigger_fromDropLocal(signal, opPtr);
+ return;
+ }
+ OpDropTriggerPtr opPtr;
+ c_opDropTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ opPtr.p->setError(ref);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == DropTrigReq::RT_DICT_COMMIT ||
+ requestType == DropTrigReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ dropTrigger_sendReply(signal, opPtr, true);
+ c_opDropTrigger.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = DropTrigReq::RT_DICT_ABORT;
+ dropTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == DropTrigReq::RT_DICT_PREPARE) {
+ jam();
+ // start alter offline
+ dropTrigger_toAlterTrigger(signal, opPtr);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::dropTrigger_slavePrepare(Signal* signal, OpDropTriggerPtr opPtr)
+{
+ jam();
+}
+
+void
+Dbdict::dropTrigger_toAlterTrigger(Signal* signal, OpDropTriggerPtr opPtr)
+{
+ jam();
+ AlterTrigReq* req = (AlterTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(AlterTrigReq::RT_DROP_TRIGGER);
+ req->setTableId(opPtr.p->m_request.getTableId());
+ req->setTriggerId(opPtr.p->m_request.getTriggerId());
+ req->setTriggerInfo(0); // not used
+ req->setOnline(false);
+ req->setReceiverRef(0);
+ sendSignal(reference(), GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::dropTrigger_fromAlterTrigger(Signal* signal, OpDropTriggerPtr opPtr)
+{
+ jam();
+ // remove in all
+ opPtr.p->m_requestType = DropTrigReq::RT_DICT_COMMIT;
+ dropTrigger_sendSlaveReq(signal, opPtr);
+}
+
+void
+Dbdict::dropTrigger_sendSlaveReq(Signal* signal, OpDropTriggerPtr opPtr)
+{
+ DropTrigReq* const req = (DropTrigReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ opPtr.p->m_signalCounter = c_aliveNodes;
+ NodeReceiverGroup rg(DBDICT, c_aliveNodes);
+ sendSignal(rg, GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::dropTrigger_slaveCommit(Signal* signal, OpDropTriggerPtr opPtr)
+{
+ jam();
+ const DropTrigReq* const req = &opPtr.p->m_request;
+ // get trigger record
+ const Uint32 triggerId = req->getTriggerId();
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, triggerId);
+ if (triggerPtr.p->triggerType == TriggerType::SECONDARY_INDEX ||
+ triggerPtr.p->triggerType == TriggerType::ORDERED_INDEX) {
+ jam();
+ // disconnect from index if index trigger XXX move to drop index
+ triggerPtr.p->indexId = req->getIndexId();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, triggerPtr.p->indexId);
+ ndbrequire(! indexPtr.isNull());
+ switch (triggerPtr.p->triggerEvent) {
+ case TriggerEvent::TE_INSERT:
+ indexPtr.p->insertTriggerId = RNIL;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ indexPtr.p->updateTriggerId = RNIL;
+ break;
+ case TriggerEvent::TE_DELETE:
+ indexPtr.p->deleteTriggerId = RNIL;
+ break;
+ case TriggerEvent::TE_CUSTOM:
+ indexPtr.p->customTriggerId = RNIL;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ }
+ if (triggerPtr.p->triggerType == TriggerType::READ_ONLY_CONSTRAINT) {
+ jam();
+ // disconnect from index record XXX should be done in caller instead
+ triggerPtr.p->indexId = req->getTableId();
+ TableRecordPtr indexPtr;
+ c_tableRecordPool.getPtr(indexPtr, triggerPtr.p->indexId);
+ indexPtr.p->buildTriggerId = RNIL;
+ }
+ // remove trigger
+ // ndbout_c("++++++++++++ Removing trigger id %u, %s", triggerPtr.p->triggerId, triggerPtr.p->triggerName);
+ c_triggerRecordHash.remove(triggerPtr);
+ triggerPtr.p->triggerState = TriggerRecord::TS_NOT_DEFINED;
+}
+
+void
+Dbdict::dropTrigger_slaveAbort(Signal* signal, OpDropTriggerPtr opPtr)
+{
+ jam();
+}
+
+void
+Dbdict::dropTrigger_sendReply(Signal* signal, OpDropTriggerPtr opPtr,
+ bool toUser)
+{
+ DropTrigRef* rep = (DropTrigRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_DROP_TRIG_CONF;
+ Uint32 length = DropTrigConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == DropTrigReq::RT_DICT_ABORT)
+ sendRef = false;
+ } else {
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = DropTrigConf::SignalLength;
+ }
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setIndexId(opPtr.p->m_request.getIndexId());
+ rep->setTriggerId(opPtr.p->m_request.getTriggerId());
+ if (sendRef) {
+ if (opPtr.p->m_errorNode == 0)
+ opPtr.p->m_errorNode = getOwnNodeId();
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->setErrorLine(opPtr.p->m_errorLine);
+ rep->setErrorNode(opPtr.p->m_errorNode);
+ gsn = GSN_DROP_TRIG_REF;
+ length = CreateTrigRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/**
+ * MODULE: Alter trigger.
+ *
+ * Alter trigger state. Alter online creates the trigger first in all
+ * TC (if index trigger) and then in all LQH-TUP.
+ *
+ * Request type received in REQ and returned in CONF/REF:
+ *
+ * RT_USER - normal user e.g. BACKUP
+ * RT_CREATE_TRIGGER - from create trigger
+ * RT_DROP_TRIGGER - from drop trigger
+ * RT_DICT_PREPARE - seize operations and check request
+ * RT_DICT_TC - master to each DICT on way to TC
+ * RT_DICT_LQH - master to each DICT on way to LQH-TUP
+ * RT_DICT_COMMIT - commit state change in each DICT (no reply)
+ */
+
+void
+Dbdict::execALTER_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ AlterTrigReq* const req = (AlterTrigReq*)signal->getDataPtrSend();
+ OpAlterTriggerPtr opPtr;
+ const Uint32 senderRef = signal->senderBlockRef();
+ const AlterTrigReq::RequestType requestType = req->getRequestType();
+ if (requestType == AlterTrigReq::RT_USER ||
+ requestType == AlterTrigReq::RT_CREATE_TRIGGER ||
+ requestType == AlterTrigReq::RT_DROP_TRIGGER) {
+ jam();
+ const bool isLocal = req->getRequestFlag() & RequestFlag::RF_LOCAL;
+ NdbNodeBitmask receiverNodes = c_aliveNodes;
+ if (isLocal) {
+ receiverNodes.clear();
+ receiverNodes.set(getOwnNodeId());
+ }
+ if (signal->getLength() == AlterTrigReq::SignalLength) {
+ jam();
+ if (! isLocal && getOwnNodeId() != c_masterNodeId) {
+ jam();
+ // forward to DICT master
+ sendSignal(calcDictBlockRef(c_masterNodeId), GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+ return;
+ }
+ // forward initial request plus operation key to all
+ req->setOpKey(++c_opRecordSequence);
+ NodeReceiverGroup rg(DBDICT, receiverNodes);
+ sendSignal(rg, GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength + 1, JBB);
+ return;
+ }
+ // seize operation record
+ ndbrequire(signal->getLength() == AlterTrigReq::SignalLength + 1);
+ const Uint32 opKey = req->getOpKey();
+ OpAlterTrigger opBusy;
+ if (! c_opAlterTrigger.seize(opPtr))
+ opPtr.p = &opBusy;
+ opPtr.p->save(req);
+ opPtr.p->m_coordinatorRef = senderRef;
+ opPtr.p->m_isMaster = (senderRef == reference());
+ opPtr.p->key = opKey;
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_PREPARE;
+ if (opPtr.p == &opBusy) {
+ jam();
+ opPtr.p->m_errorCode = AlterTrigRef::Busy;
+ opPtr.p->m_errorLine = __LINE__;
+ alterTrigger_sendReply(signal, opPtr, opPtr.p->m_isMaster);
+ return;
+ }
+ c_opAlterTrigger.add(opPtr);
+ // master expects to hear from all
+ if (opPtr.p->m_isMaster) {
+ opPtr.p->m_nodes = receiverNodes;
+ opPtr.p->m_signalCounter = receiverNodes;
+ }
+ alterTrigger_slavePrepare(signal, opPtr);
+ alterTrigger_sendReply(signal, opPtr, false);
+ return;
+ }
+ c_opAlterTrigger.find(opPtr, req->getConnectionPtr());
+ if (! opPtr.isNull()) {
+ opPtr.p->m_requestType = requestType;
+ if (requestType == AlterTrigReq::RT_DICT_TC ||
+ requestType == AlterTrigReq::RT_DICT_LQH) {
+ jam();
+ if (req->getOnline())
+ alterTrigger_toCreateLocal(signal, opPtr);
+ else
+ alterTrigger_toDropLocal(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterTrigReq::RT_DICT_COMMIT ||
+ requestType == AlterTrigReq::RT_DICT_ABORT) {
+ jam();
+ if (requestType == AlterTrigReq::RT_DICT_COMMIT)
+ alterTrigger_slaveCommit(signal, opPtr);
+ else
+ alterTrigger_slaveAbort(signal, opPtr);
+ alterTrigger_sendReply(signal, opPtr, false);
+ // done in slave
+ if (! opPtr.p->m_isMaster)
+ c_opAlterTrigger.release(opPtr);
+ return;
+ }
+ }
+ jam();
+ // return to sender
+ OpAlterTrigger opBad;
+ opPtr.p = &opBad;
+ opPtr.p->save(req);
+ opPtr.p->m_errorCode = AlterTrigRef::BadRequestType;
+ opPtr.p->m_errorLine = __LINE__;
+ alterTrigger_sendReply(signal, opPtr, true);
+ return;
+}
+
+void
+Dbdict::execALTER_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+ AlterTrigConf* conf = (AlterTrigConf*)signal->getDataPtrSend();
+ alterTrigger_recvReply(signal, conf, 0);
+}
+
+void
+Dbdict::execALTER_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+ AlterTrigRef* ref = (AlterTrigRef*)signal->getDataPtrSend();
+ alterTrigger_recvReply(signal, ref->getConf(), ref);
+}
+
+void
+Dbdict::alterTrigger_recvReply(Signal* signal, const AlterTrigConf* conf,
+ const AlterTrigRef* ref)
+{
+ jam();
+ const Uint32 senderRef = signal->senderBlockRef();
+ const AlterTrigReq::RequestType requestType = conf->getRequestType();
+ const Uint32 key = conf->getConnectionPtr();
+ if (requestType == AlterTrigReq::RT_CREATE_TRIGGER) {
+ jam();
+ // part of create trigger operation
+ OpCreateTriggerPtr opPtr;
+ c_opCreateTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ createTrigger_fromAlterTrigger(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterTrigReq::RT_DROP_TRIGGER) {
+ jam();
+ // part of drop trigger operation
+ OpDropTriggerPtr opPtr;
+ c_opDropTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ opPtr.p->setError(ref);
+ dropTrigger_fromAlterTrigger(signal, opPtr);
+ return;
+ }
+ OpAlterTriggerPtr opPtr;
+ c_opAlterTrigger.find(opPtr, key);
+ ndbrequire(! opPtr.isNull());
+ ndbrequire(opPtr.p->m_isMaster);
+ ndbrequire(opPtr.p->m_requestType == requestType);
+ /*
+ * If refuse on drop trig, because of non-existent trigger,
+ * comes from anyone but the master node - ignore it and
+ * remove the node from forter ALTER_TRIG communication
+ * This will happen if a new node has started since the
+ * trigger whas created.
+ */
+ if (ref &&
+ refToNode(senderRef) != refToNode(reference()) &&
+ opPtr.p->m_request.getRequestType() == AlterTrigReq::RT_DROP_TRIGGER &&
+ ref->getErrorCode() == AlterTrigRef::TriggerNotFound) {
+ jam();
+ ref = 0; // ignore this error
+ opPtr.p->m_nodes.clear(refToNode(senderRef)); // remove this from group
+ }
+ opPtr.p->setError(ref);
+ opPtr.p->m_signalCounter.clearWaitingFor(refToNode(senderRef));
+ if (! opPtr.p->m_signalCounter.done()) {
+ jam();
+ return;
+ }
+ if (requestType == AlterTrigReq::RT_DICT_COMMIT ||
+ requestType == AlterTrigReq::RT_DICT_ABORT) {
+ jam();
+ // send reply to user
+ alterTrigger_sendReply(signal, opPtr, true);
+ c_opAlterTrigger.release(opPtr);
+ return;
+ }
+ if (opPtr.p->hasError()) {
+ jam();
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_ABORT;
+ alterTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (! (opPtr.p->m_request.getRequestFlag() & RequestFlag::RF_NOTCTRIGGER)) {
+ if (requestType == AlterTrigReq::RT_DICT_PREPARE) {
+ jam();
+ if (opPtr.p->m_request.getOnline())
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_TC;
+ else
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_LQH;
+ alterTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterTrigReq::RT_DICT_TC) {
+ jam();
+ if (opPtr.p->m_request.getOnline())
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_LQH;
+ else
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_COMMIT;
+ alterTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterTrigReq::RT_DICT_LQH) {
+ jam();
+ if (opPtr.p->m_request.getOnline())
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_COMMIT;
+ else
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_TC;
+ alterTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ } else {
+ if (requestType == AlterTrigReq::RT_DICT_PREPARE) {
+ jam();
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_LQH;
+ alterTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ if (requestType == AlterTrigReq::RT_DICT_LQH) {
+ jam();
+ opPtr.p->m_requestType = AlterTrigReq::RT_DICT_COMMIT;
+ alterTrigger_sendSlaveReq(signal, opPtr);
+ return;
+ }
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::alterTrigger_slavePrepare(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+ const AlterTrigReq* const req = &opPtr.p->m_request;
+ const Uint32 triggerId = req->getTriggerId();
+ TriggerRecordPtr triggerPtr;
+ if (! (triggerId < c_triggerRecordPool.getSize())) {
+ jam();
+ opPtr.p->m_errorCode = AlterTrigRef::TriggerNotFound;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+ c_triggerRecordPool.getPtr(triggerPtr, triggerId);
+ if (triggerPtr.p->triggerState == TriggerRecord::TS_NOT_DEFINED) {
+ jam();
+ opPtr.p->m_errorCode = AlterTrigRef::TriggerNotFound;
+ opPtr.p->m_errorLine = __LINE__;
+ return;
+ }
+}
+
+void
+Dbdict::alterTrigger_toCreateLocal(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+ // find trigger record
+ const Uint32 triggerId = opPtr.p->m_request.getTriggerId();
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, triggerId);
+ CreateTrigReq* const req = (CreateTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_TC) {
+ req->setRequestType(CreateTrigReq::RT_TC);
+ } else if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_LQH) {
+ req->setRequestType(CreateTrigReq::RT_LQH);
+ } else {
+ ndbassert(false);
+ }
+ req->setTableId(triggerPtr.p->tableId);
+ req->setIndexId(triggerPtr.p->indexId);
+ req->setTriggerId(triggerPtr.i);
+ req->setTriggerType(triggerPtr.p->triggerType);
+ req->setTriggerActionTime(triggerPtr.p->triggerActionTime);
+ req->setTriggerEvent(triggerPtr.p->triggerEvent);
+ req->setMonitorReplicas(triggerPtr.p->monitorReplicas);
+ req->setMonitorAllAttributes(triggerPtr.p->monitorAllAttributes);
+ req->setOnline(true);
+ req->setReceiverRef(opPtr.p->m_request.getReceiverRef());
+ BlockReference blockRef = 0;
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_TC) {
+ blockRef = calcTcBlockRef(getOwnNodeId());
+ } else if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_LQH) {
+ blockRef = calcLqhBlockRef(getOwnNodeId());
+ } else {
+ ndbassert(false);
+ }
+ req->setAttributeMask(triggerPtr.p->attributeMask);
+ sendSignal(blockRef, GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::alterTrigger_fromCreateLocal(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+ if (! opPtr.p->hasError()) {
+ // mark created locally
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, opPtr.p->m_request.getTriggerId());
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_TC) {
+ triggerPtr.p->triggerLocal |= TriggerRecord::TL_CREATED_TC;
+ } else if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_LQH) {
+ triggerPtr.p->triggerLocal |= TriggerRecord::TL_CREATED_LQH;
+ } else {
+ ndbrequire(false);
+ }
+ }
+ // forward CONF or REF to master
+ alterTrigger_sendReply(signal, opPtr, false);
+}
+
+void
+Dbdict::alterTrigger_toDropLocal(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, opPtr.p->m_request.getTriggerId());
+ DropTrigReq* const req = (DropTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(reference());
+ req->setConnectionPtr(opPtr.p->key);
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_TC) {
+ // broken trigger
+ if (! (triggerPtr.p->triggerLocal & TriggerRecord::TL_CREATED_TC)) {
+ jam();
+ alterTrigger_sendReply(signal, opPtr, false);
+ return;
+ }
+ req->setRequestType(DropTrigReq::RT_TC);
+ } else if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_LQH) {
+ // broken trigger
+ if (! (triggerPtr.p->triggerLocal & TriggerRecord::TL_CREATED_LQH)) {
+ jam();
+ alterTrigger_sendReply(signal, opPtr, false);
+ return;
+ }
+ req->setRequestType(DropTrigReq::RT_LQH);
+ } else {
+ ndbassert(false);
+ }
+ req->setTableId(triggerPtr.p->tableId);
+ req->setIndexId(triggerPtr.p->indexId);
+ req->setTriggerId(triggerPtr.i);
+ req->setTriggerType(triggerPtr.p->triggerType);
+ req->setTriggerActionTime(triggerPtr.p->triggerActionTime);
+ req->setTriggerEvent(triggerPtr.p->triggerEvent);
+ req->setMonitorReplicas(triggerPtr.p->monitorReplicas);
+ req->setMonitorAllAttributes(triggerPtr.p->monitorAllAttributes);
+ BlockReference blockRef = 0;
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_TC) {
+ blockRef = calcTcBlockRef(getOwnNodeId());
+ } else if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_LQH) {
+ blockRef = calcLqhBlockRef(getOwnNodeId());
+ } else {
+ ndbassert(false);
+ }
+ sendSignal(blockRef, GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::alterTrigger_fromDropLocal(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+ if (! opPtr.p->hasError()) {
+ // mark dropped locally
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, opPtr.p->m_request.getTriggerId());
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_TC) {
+ triggerPtr.p->triggerLocal &= ~TriggerRecord::TL_CREATED_TC;
+ } else if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_LQH) {
+ triggerPtr.p->triggerLocal &= ~TriggerRecord::TL_CREATED_LQH;
+ } else {
+ ndbrequire(false);
+ }
+ }
+ // forward CONF or REF to master
+ alterTrigger_sendReply(signal, opPtr, false);
+}
+
+void
+Dbdict::alterTrigger_slaveCommit(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+ TriggerRecordPtr triggerPtr;
+ c_triggerRecordPool.getPtr(triggerPtr, opPtr.p->m_request.getTriggerId());
+ // set state
+ triggerPtr.p->triggerState = TriggerRecord::TS_ONLINE;
+}
+
+void
+Dbdict::alterTrigger_slaveAbort(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ jam();
+}
+
+void
+Dbdict::alterTrigger_sendSlaveReq(Signal* signal, OpAlterTriggerPtr opPtr)
+{
+ AlterTrigReq* const req = (AlterTrigReq*)signal->getDataPtrSend();
+ *req = opPtr.p->m_request;
+ req->setUserRef(opPtr.p->m_coordinatorRef);
+ req->setConnectionPtr(opPtr.p->key);
+ req->setRequestType(opPtr.p->m_requestType);
+ req->addRequestFlag(opPtr.p->m_requestFlag);
+ NdbNodeBitmask receiverNodes = c_aliveNodes;
+ if (opPtr.p->m_requestFlag & RequestFlag::RF_LOCAL) {
+ receiverNodes.clear();
+ receiverNodes.set(getOwnNodeId());
+ } else {
+ opPtr.p->m_nodes.bitAND(receiverNodes);
+ receiverNodes = opPtr.p->m_nodes;
+ }
+ opPtr.p->m_signalCounter = receiverNodes;
+ NodeReceiverGroup rg(DBDICT, receiverNodes);
+ sendSignal(rg, GSN_ALTER_TRIG_REQ,
+ signal, AlterTrigReq::SignalLength, JBB);
+}
+
+void
+Dbdict::alterTrigger_sendReply(Signal* signal, OpAlterTriggerPtr opPtr,
+ bool toUser)
+{
+ jam();
+ AlterTrigRef* rep = (AlterTrigRef*)signal->getDataPtrSend();
+ Uint32 gsn = GSN_ALTER_TRIG_CONF;
+ Uint32 length = AlterTrigConf::InternalLength;
+ bool sendRef = opPtr.p->hasError();
+ if (! toUser) {
+ rep->setUserRef(opPtr.p->m_coordinatorRef);
+ rep->setConnectionPtr(opPtr.p->key);
+ rep->setRequestType(opPtr.p->m_requestType);
+ if (opPtr.p->m_requestType == AlterTrigReq::RT_DICT_ABORT) {
+ jam();
+ sendRef = false;
+ } else {
+ jam();
+ }
+ } else {
+ jam();
+ rep->setUserRef(opPtr.p->m_request.getUserRef());
+ rep->setConnectionPtr(opPtr.p->m_request.getConnectionPtr());
+ rep->setRequestType(opPtr.p->m_request.getRequestType());
+ length = AlterTrigConf::SignalLength;
+ }
+ rep->setTableId(opPtr.p->m_request.getTableId());
+ rep->setTriggerId(opPtr.p->m_request.getTriggerId());
+ if (sendRef) {
+ if (opPtr.p->m_errorNode == 0) {
+ jam();
+ opPtr.p->m_errorNode = getOwnNodeId();
+ } else {
+ jam();
+ }
+ rep->setErrorCode(opPtr.p->m_errorCode);
+ rep->setErrorLine(opPtr.p->m_errorLine);
+ rep->setErrorNode(opPtr.p->m_errorNode);
+ gsn = GSN_ALTER_TRIG_REF;
+ length = AlterTrigRef::SignalLength;
+ }
+ sendSignal(rep->getUserRef(), gsn, signal, length, JBB);
+}
+
+/**
+ * MODULE: Support routines for index and trigger.
+ */
+
+void
+Dbdict::getTableKeyList(TableRecordPtr tablePtr, AttributeList& list)
+{
+ jam();
+ list.sz = 0;
+ for (Uint32 tAttr = tablePtr.p->firstAttribute; tAttr != RNIL; ) {
+ AttributeRecord* aRec = c_attributeRecordPool.getPtr(tAttr);
+ if (aRec->tupleKey)
+ list.id[list.sz++] = aRec->attributeId;
+ tAttr = aRec->nextAttrInTable;
+ }
+}
+
+// XXX should store the primary attribute id
+void
+Dbdict::getIndexAttr(TableRecordPtr indexPtr, Uint32 itAttr, Uint32* id)
+{
+ jam();
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, indexPtr.p->primaryTableId);
+ AttributeRecord* iaRec = c_attributeRecordPool.getPtr(itAttr);
+ for (Uint32 tAttr = tablePtr.p->firstAttribute; tAttr != RNIL; ) {
+ AttributeRecord* aRec = c_attributeRecordPool.getPtr(tAttr);
+ if (iaRec->equal(*aRec)) {
+ id[0] = aRec->attributeId;
+ return;
+ }
+ tAttr = aRec->nextAttrInTable;
+ }
+ ndbrequire(false);
+}
+
+void
+Dbdict::getIndexAttrList(TableRecordPtr indexPtr, AttributeList& list)
+{
+ jam();
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, indexPtr.p->primaryTableId);
+ list.sz = 0;
+ memset(list.id, 0, sizeof(list.id));
+ ndbrequire(indexPtr.p->noOfAttributes >= 2);
+ Uint32 itAttr = indexPtr.p->firstAttribute;
+ for (Uint32 i = 0; i < (Uint32)indexPtr.p->noOfAttributes - 1; i++) {
+ getIndexAttr(indexPtr, itAttr, &list.id[list.sz++]);
+ AttributeRecord* iaRec = c_attributeRecordPool.getPtr(itAttr);
+ itAttr = iaRec->nextAttrInTable;
+ }
+}
+
+void
+Dbdict::getIndexAttrMask(TableRecordPtr indexPtr, AttributeMask& mask)
+{
+ jam();
+ TableRecordPtr tablePtr;
+ c_tableRecordPool.getPtr(tablePtr, indexPtr.p->primaryTableId);
+ mask.clear();
+ ndbrequire(indexPtr.p->noOfAttributes >= 2);
+ Uint32 itAttr = indexPtr.p->firstAttribute;
+ for (Uint32 i = 0; i < (Uint32)indexPtr.p->noOfAttributes - 1; i++) {
+ Uint32 id;
+ getIndexAttr(indexPtr, itAttr, &id);
+ mask.set(id);
+ AttributeRecord* iaRec = c_attributeRecordPool.getPtr(itAttr);
+ itAttr = iaRec->nextAttrInTable;
+ }
+}
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* MODULE: STORE/RESTORE SCHEMA FILE---------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* General module used to store the schema file on disk and */
+/* similar function to restore it from disk. */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+void
+Dbdict::initSchemaFile(SchemaFile * sf, Uint32 fileSz){
+ memcpy(sf->Magic, "NDBSCHMA", sizeof(sf->Magic));
+ sf->ByteOrder = 0x12345678;
+ sf->NdbVersion = NDB_VERSION;
+ sf->FileSize = fileSz;
+ sf->CheckSum = 0;
+
+ Uint32 headSz = (sizeof(SchemaFile)-sizeof(SchemaFile::TableEntry));
+ Uint32 noEntries = (fileSz - headSz) / sizeof(SchemaFile::TableEntry);
+ Uint32 slack = (fileSz - headSz) - noEntries * sizeof(SchemaFile::TableEntry);
+
+ ndbrequire(noEntries > MAX_TABLES);
+
+ sf->NoOfTableEntries = noEntries;
+ memset(sf->TableEntries, 0, noEntries*sizeof(SchemaFile::TableEntry));
+ memset(&(sf->TableEntries[noEntries]), 0, slack);
+ computeChecksum(sf);
+}
+
+void
+Dbdict::computeChecksum(SchemaFile * sf){
+ sf->CheckSum = 0;
+ sf->CheckSum = computeChecksum((const Uint32*)sf, sf->FileSize/4);
+}
+
+bool
+Dbdict::validateChecksum(const SchemaFile * sf){
+
+ Uint32 c = computeChecksum((const Uint32*)sf, sf->FileSize/4);
+ return c == 0;
+}
+
+Uint32
+Dbdict::computeChecksum(const Uint32 * src, Uint32 len){
+ Uint32 ret = 0;
+ for(Uint32 i = 0; i<len; i++)
+ ret ^= src[i];
+ return ret;
+}
+
+SchemaFile::TableEntry *
+Dbdict::getTableEntry(void * p, Uint32 tableId, bool allowTooBig){
+ SchemaFile * sf = (SchemaFile*)p;
+
+ ndbrequire(allowTooBig || tableId < sf->NoOfTableEntries);
+ return &sf->TableEntries[tableId];
+}
+
+// global metadata support
+
+int
+Dbdict::getMetaTablePtr(TableRecordPtr& tablePtr, Uint32 tableId, Uint32 tableVersion)
+{
+ if (tableId >= c_tableRecordPool.getSize()) {
+ return MetaData::InvalidArgument;
+ }
+ c_tableRecordPool.getPtr(tablePtr, tableId);
+ if (tablePtr.p->tabState == TableRecord::NOT_DEFINED) {
+ return MetaData::TableNotFound;
+ }
+ if (tablePtr.p->tableVersion != tableVersion) {
+ return MetaData::InvalidTableVersion;
+ }
+ // online flag is not maintained by DICT
+ tablePtr.p->online =
+ tablePtr.p->isTable() && tablePtr.p->tabState == TableRecord::DEFINED ||
+ tablePtr.p->isIndex() && tablePtr.p->indexState == TableRecord::IS_ONLINE;
+ return 0;
+}
+
+int
+Dbdict::getMetaTable(MetaData::Table& table, Uint32 tableId, Uint32 tableVersion)
+{
+ int ret;
+ TableRecordPtr tablePtr;
+ if ((ret = getMetaTablePtr(tablePtr, tableId, tableVersion)) < 0) {
+ return ret;
+ }
+ new (&table) MetaData::Table(*tablePtr.p);
+ return 0;
+}
+
+int
+Dbdict::getMetaTable(MetaData::Table& table, const char* tableName)
+{
+ int ret;
+ TableRecordPtr tablePtr;
+ if (strlen(tableName) + 1 > MAX_TAB_NAME_SIZE) {
+ return MetaData::InvalidArgument;
+ }
+ TableRecord keyRecord;
+ strcpy(keyRecord.tableName, tableName);
+ c_tableRecordHash.find(tablePtr, keyRecord);
+ if (tablePtr.i == RNIL) {
+ return MetaData::TableNotFound;
+ }
+ if ((ret = getMetaTablePtr(tablePtr, tablePtr.i, tablePtr.p->tableVersion)) < 0) {
+ return ret;
+ }
+ new (&table) MetaData::Table(*tablePtr.p);
+ return 0;
+}
+
+int
+Dbdict::getMetaAttribute(MetaData::Attribute& attr, const MetaData::Table& table, Uint32 attributeId)
+{
+ int ret;
+ TableRecordPtr tablePtr;
+ if ((ret = getMetaTablePtr(tablePtr, table.tableId, table.tableVersion)) < 0) {
+ return ret;
+ }
+ AttributeRecordPtr attrPtr;
+ attrPtr.i = tablePtr.p->firstAttribute;
+ while (attrPtr.i != RNIL) {
+ c_attributeRecordPool.getPtr(attrPtr);
+ if (attrPtr.p->attributeId == attributeId)
+ break;
+ attrPtr.i = attrPtr.p->nextAttrInTable;
+ }
+ if (attrPtr.i == RNIL) {
+ return MetaData::AttributeNotFound;
+ }
+ new (&attr) MetaData::Attribute(*attrPtr.p);
+ return 0;
+}
+
+int
+Dbdict::getMetaAttribute(MetaData::Attribute& attr, const MetaData::Table& table, const char* attributeName)
+{
+ int ret;
+ TableRecordPtr tablePtr;
+ if ((ret = getMetaTablePtr(tablePtr, table.tableId, table.tableVersion)) < 0) {
+ return ret;
+ }
+ AttributeRecordPtr attrPtr;
+ attrPtr.i = tablePtr.p->firstAttribute;
+ while (attrPtr.i != RNIL) {
+ c_attributeRecordPool.getPtr(attrPtr);
+ if (strcmp(attrPtr.p->attributeName, attributeName) == 0)
+ break;
+ attrPtr.i = attrPtr.p->nextAttrInTable;
+ }
+ if (attrPtr.i == RNIL) {
+ return MetaData::AttributeNotFound;
+ }
+ new (&attr) MetaData::Attribute(*attrPtr.p);
+ return 0;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
new file mode 100644
index 00000000000..73fbdcc8e16
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
@@ -0,0 +1,1990 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBDICT_H
+#define DBDICT_H
+
+/**
+ * Dict : Dictionary Block
+ */
+
+#include <ndb_limits.h>
+#include <trigger_definitions.h>
+#include <pc.hpp>
+#include <ArrayList.hpp>
+#include <DLHashTable.hpp>
+#include <CArray.hpp>
+#include <KeyTable2.hpp>
+#include <SimulatedBlock.hpp>
+#include <SimpleProperties.hpp>
+#include <SignalCounter.hpp>
+#include <Bitmask.hpp>
+#include <AttributeList.hpp>
+#include <signaldata/GetTableId.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/CreateTable.hpp>
+#include <signaldata/CreateTab.hpp>
+#include <signaldata/DropTable.hpp>
+#include <signaldata/AlterTable.hpp>
+#include <signaldata/AlterTab.hpp>
+#include <signaldata/CreateIndx.hpp>
+#include <signaldata/DropIndx.hpp>
+#include <signaldata/AlterIndx.hpp>
+#include <signaldata/BuildIndx.hpp>
+#include <signaldata/UtilPrepare.hpp>
+#include <signaldata/CreateEvnt.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/AlterTrig.hpp>
+#include "SchemaFile.hpp"
+#include <blocks/mutexes.hpp>
+#include <SafeCounter.hpp>
+#include <RequestTracker.hpp>
+
+#ifdef DBDICT_C
+// Debug Macros
+
+/*--------------------------------------------------------------*/
+// Constants for CONTINUEB
+/*--------------------------------------------------------------*/
+#define ZPACK_TABLE_INTO_PAGES 0
+#define ZSEND_GET_TAB_RESPONSE 3
+
+
+/*--------------------------------------------------------------*/
+// Other constants in alphabetical order
+/*--------------------------------------------------------------*/
+#define ZNOMOREPHASES 255
+
+/*--------------------------------------------------------------*/
+// Schema file defines
+/*--------------------------------------------------------------*/
+#define ZSCHEMA_WORDS 4
+
+/*--------------------------------------------------------------*/
+// Page constants
+/*--------------------------------------------------------------*/
+#define ZALLOCATE 1 //Variable number of page for NDBFS
+#define ZPAGE_HEADER_SIZE 32
+#define ZPOS_PAGE_SIZE 16
+#define ZPOS_CHECKSUM 17
+#define ZPOS_VERSION 18
+#define ZPOS_PAGE_HEADER_SIZE 19
+
+/*--------------------------------------------------------------*/
+// Size constants
+/*--------------------------------------------------------------*/
+#define ZFS_CONNECT_SIZE 4
+#define ZSIZE_OF_PAGES_IN_WORDS 8192
+#define ZLOG_SIZE_OF_PAGES_IN_WORDS 13
+#define ZMAX_PAGES_OF_TABLE_DEFINITION 8
+#define ZNUMBER_OF_PAGES (2 * ZMAX_PAGES_OF_TABLE_DEFINITION + 2)
+#define ZNO_OF_FRAGRECORD 5
+
+/*--------------------------------------------------------------*/
+// Error codes
+/*--------------------------------------------------------------*/
+#define ZNODE_FAILURE_ERROR 704
+#endif
+
+/**
+ * Systable NDB$EVENTS_0
+ */
+
+#define EVENT_SYSTEM_TABLE_NAME "sys/def/NDB$EVENTS_0"
+#define EVENT_SYSTEM_TABLE_LENGTH 6
+
+struct sysTab_NDBEVENTS_0 {
+ char NAME[MAX_TAB_NAME_SIZE];
+ Uint32 EVENT_TYPE;
+ char TABLE_NAME[MAX_TAB_NAME_SIZE];
+ Uint32 ATTRIBUTE_MASK[MAXNROFATTRIBUTESINWORDS];
+ Uint32 SUBID;
+ Uint32 SUBKEY;
+};
+
+/**
+ * DICT - This blocks handles all metadata
+ */
+class Dbdict: public SimulatedBlock {
+public:
+ /*
+ * 2.3 RECORD AND FILESIZES
+ */
+ /**
+ * Shared table / index record. Most of this is permanent data stored
+ * on disk. Index trigger ids are volatile.
+ */
+ struct TableRecord : public MetaData::Table {
+ /****************************************************
+ * Support variables for table handling
+ ****************************************************/
+
+ /* Active page which is sent to disk */
+ Uint32 activePage;
+
+ /** File pointer received from disk */
+ Uint32 filePtr[2];
+
+ /** Pointer to first attribute in table */
+ Uint32 firstAttribute;
+
+ /* Pointer to first page of table description */
+ Uint32 firstPage;
+
+ /** Pointer to last attribute in table */
+ Uint32 lastAttribute;
+
+ /* Temporary record used during add/drop table */
+ Uint32 myConnect;
+#ifdef HAVE_TABLE_REORG
+ /* Second table used by this table (for table reorg) */
+ Uint32 secondTable;
+#endif
+ /* Next record in Pool */
+ Uint32 nextPool;
+
+ /* Next record in hash table */
+ Uint32 nextHash;
+
+ /* Previous record in Pool */
+ Uint32 prevPool;
+
+ /* Previous record in hash table */
+ Uint32 prevHash;
+
+ enum TabState {
+ NOT_DEFINED = 0,
+ REORG_TABLE_PREPARED = 1,
+ DEFINING = 2,
+ CHECKED = 3,
+ DEFINED = 4,
+ PREPARE_DROPPING = 5,
+ DROPPING = 6
+ };
+ TabState tabState;
+
+ /* State when returning from TC_SCHVERREQ */
+ enum TabReturnState {
+ TRS_IDLE = 0,
+ ADD_TABLE = 1,
+ SLAVE_SYSTEM_RESTART = 2,
+ MASTER_SYSTEM_RESTART = 3
+ };
+ TabReturnState tabReturnState;
+
+ /** Number of words */
+ Uint32 packedSize;
+
+ /** Index state (volatile data) */
+ enum IndexState {
+ IS_UNDEFINED = 0, // initial
+ IS_OFFLINE = 1, // index table created
+ IS_BUILDING = 2, // building (local state)
+ IS_DROPPING = 3, // dropping (local state)
+ IS_ONLINE = 4, // online
+ IS_BROKEN = 9 // build or drop aborted
+ };
+ IndexState indexState;
+
+ /** Trigger ids of index (volatile data) */
+ Uint32 insertTriggerId;
+ Uint32 updateTriggerId;
+ Uint32 deleteTriggerId;
+ Uint32 customTriggerId; // ordered index
+ Uint32 buildTriggerId; // temp during build
+
+ /** Index state in other blocks on this node */
+ enum IndexLocal {
+ IL_CREATED_TC = 1 << 0 // created in TC
+ };
+ Uint32 indexLocal;
+
+ Uint32 noOfNullBits;
+
+ inline bool equal(TableRecord & rec) const {
+ return strcmp(tableName, rec.tableName) == 0;
+ }
+
+ inline Uint32 hashValue() const {
+ Uint32 h = 0;
+ for (const char* p = tableName; *p != 0; p++)
+ h = (h << 5) + h + (*p);
+ return h;
+ }
+
+ /** frm data for this table */
+ /** TODO Could preferrably be made dynamic size */
+ Uint32 frmLen;
+ char frmData[MAX_FRM_DATA_SIZE];
+
+ Uint32 fragmentCount;
+ };
+
+ typedef Ptr<TableRecord> TableRecordPtr;
+ ArrayPool<TableRecord> c_tableRecordPool;
+ DLHashTable<TableRecord> c_tableRecordHash;
+
+ /**
+ * Table attributes. Permanent data.
+ *
+ * Indexes have an attribute list which duplicates primary table
+ * attributes. This is wrong but convenient.
+ */
+ struct AttributeRecord : public MetaData::Attribute {
+ union {
+ /** Pointer to the next attribute used by ArrayPool */
+ Uint32 nextPool;
+
+ /** Pointer to the next attribute used by DLHash */
+ Uint32 nextHash;
+ };
+
+ /** Pointer to the previous attribute used by DLHash */
+ Uint32 prevHash;
+
+ /** Pointer to the next attribute in table */
+ Uint32 nextAttrInTable;
+
+ inline bool equal(AttributeRecord & rec) const {
+ return strcmp(attributeName, rec.attributeName) == 0;
+ }
+
+ inline Uint32 hashValue() const {
+ Uint32 h = 0;
+ for (const char* p = attributeName; *p != 0; p++)
+ h = (h << 5) + h + (*p);
+ return h;
+ }
+ };
+
+ typedef Ptr<AttributeRecord> AttributeRecordPtr;
+ ArrayPool<AttributeRecord> c_attributeRecordPool;
+ DLHashTable<AttributeRecord> c_attributeRecordHash;
+
+ /**
+ * Triggers. This is volatile data not saved on disk. Setting a
+ * trigger online creates the trigger in TC (if index) and LQH-TUP.
+ */
+ struct TriggerRecord {
+
+ /** Trigger state */
+ enum TriggerState {
+ TS_NOT_DEFINED = 0,
+ TS_DEFINING = 1,
+ TS_OFFLINE = 2, // created globally in DICT
+ TS_BUILDING = 3,
+ TS_DROPPING = 4,
+ TS_ONLINE = 5 // activated globally
+ };
+ TriggerState triggerState;
+
+ /** Trigger state in other blocks on this node */
+ enum IndexLocal {
+ TL_CREATED_TC = 1 << 0, // created in TC
+ TL_CREATED_LQH = 1 << 1 // created in LQH-TUP
+ };
+ Uint32 triggerLocal;
+
+ /** Trigger name, used by DICT to identify the trigger */
+ char triggerName[MAX_TAB_NAME_SIZE];
+
+ /** Trigger id, used by TRIX, TC, LQH, and TUP to identify the trigger */
+ Uint32 triggerId;
+
+ /** Table id, the table the trigger is defined on */
+ Uint32 tableId;
+
+ /** Trigger type, defines what the trigger is used for */
+ TriggerType::Value triggerType;
+
+ /** Trigger action time, defines when the trigger should fire */
+ TriggerActionTime::Value triggerActionTime;
+
+ /** Trigger event, defines what events the trigger should monitor */
+ TriggerEvent::Value triggerEvent;
+
+ /** Monitor all replicas */
+ bool monitorReplicas;
+
+ /** Monitor all, the trigger monitors changes of all attributes in table */
+ bool monitorAllAttributes;
+
+ /**
+ * Attribute mask, defines what attributes are to be monitored.
+ * Can be seen as a compact representation of SQL column name list.
+ */
+ AttributeMask attributeMask;
+
+ /** Index id, only used by secondary_index triggers */
+ Uint32 indexId;
+
+ union {
+ /** Pointer to the next attribute used by ArrayPool */
+ Uint32 nextPool;
+
+ /** Next record in hash table */
+ Uint32 nextHash;
+ };
+
+ /** Previous record in hash table */
+ Uint32 prevHash;
+
+ /** Equal function, used by DLHashTable */
+ inline bool equal(TriggerRecord & rec) const {
+ return strcmp(triggerName, rec.triggerName) == 0;
+ }
+
+ /** Hash value function, used by DLHashTable */
+ inline Uint32 hashValue() const {
+ Uint32 h = 0;
+ for (const char* p = triggerName; *p != 0; p++)
+ h = (h << 5) + h + (*p);
+ return h;
+ }
+ };
+
+ Uint32 c_maxNoOfTriggers;
+ typedef Ptr<TriggerRecord> TriggerRecordPtr;
+ ArrayPool<TriggerRecord> c_triggerRecordPool;
+ DLHashTable<TriggerRecord> c_triggerRecordHash;
+
+ /**
+ * Information for each FS connection.
+ ****************************************************************************/
+ struct FsConnectRecord {
+ enum FsState {
+ IDLE = 0,
+ OPEN_WRITE_SCHEMA = 1,
+ WRITE_SCHEMA = 2,
+ CLOSE_WRITE_SCHEMA = 3,
+ OPEN_READ_SCHEMA1 = 4,
+ OPEN_READ_SCHEMA2 = 5,
+ READ_SCHEMA1 = 6,
+ READ_SCHEMA2 = 7,
+ CLOSE_READ_SCHEMA = 8,
+ OPEN_READ_TAB_FILE1 = 9,
+ OPEN_READ_TAB_FILE2 = 10,
+ READ_TAB_FILE1 = 11,
+ READ_TAB_FILE2 = 12,
+ CLOSE_READ_TAB_FILE = 13,
+ OPEN_WRITE_TAB_FILE = 14,
+ WRITE_TAB_FILE = 15,
+ CLOSE_WRITE_TAB_FILE = 16
+ };
+ /** File Pointer for this file system connection */
+ Uint32 filePtr;
+
+ /** Reference of owner record */
+ Uint32 ownerPtr;
+
+ /** State of file system connection */
+ FsState fsState;
+
+ /** Used by Array Pool for free list handling */
+ Uint32 nextPool;
+ };
+
+ typedef Ptr<FsConnectRecord> FsConnectRecordPtr;
+ ArrayPool<FsConnectRecord> c_fsConnectRecordPool;
+
+ /**
+ * This record stores all the information about a node and all its attributes
+ ****************************************************************************/
+ struct NodeRecord {
+ enum NodeState {
+ API_NODE = 0,
+ NDB_NODE_ALIVE = 1,
+ NDB_NODE_DEAD = 2
+ };
+ bool hotSpare;
+ NodeState nodeState;
+ };
+
+ typedef Ptr<NodeRecord> NodeRecordPtr;
+ CArray<NodeRecord> c_nodes;
+ NdbNodeBitmask c_aliveNodes;
+
+ /**
+ * This record stores all the information about a table and all its attributes
+ ****************************************************************************/
+ struct PageRecord {
+ Uint32 word[8192];
+ };
+
+ typedef Ptr<PageRecord> PageRecordPtr;
+ CArray<PageRecord> c_pageRecordArray;
+
+ /**
+ * A page for create index table signal.
+ */
+ PageRecord c_indexPage;
+
+public:
+ Dbdict(const class Configuration &);
+ virtual ~Dbdict();
+
+private:
+ BLOCK_DEFINES(Dbdict);
+
+ // Signal receivers
+ void execDICTSTARTREQ(Signal* signal);
+
+ void execGET_TABINFOREQ(Signal* signal);
+ void execGET_TABLEDID_REQ(Signal* signal);
+ void execGET_TABINFO_REF(Signal* signal);
+ void execGET_TABINFO_CONF(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execHOT_SPAREREP(Signal* signal);
+ void execDIADDTABCONF(Signal* signal);
+ void execDIADDTABREF(Signal* signal);
+ void execTAB_COMMITCONF(Signal* signal);
+ void execTAB_COMMITREF(Signal* signal);
+ void execGET_SCHEMA_INFOREQ(Signal* signal);
+ void execSCHEMA_INFO(Signal* signal);
+ void execSCHEMA_INFOCONF(Signal* signal);
+ void execREAD_NODESCONF(Signal* signal);
+ void execFSCLOSECONF(Signal* signal);
+ void execFSCLOSEREF(Signal* signal);
+ void execFSOPENCONF(Signal* signal);
+ void execFSOPENREF(Signal* signal);
+ void execFSREADCONF(Signal* signal);
+ void execFSREADREF(Signal* signal);
+ void execFSWRITECONF(Signal* signal);
+ void execFSWRITEREF(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execTC_SCHVERCONF(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execINCL_NODEREQ(Signal* signal);
+ void execAPI_FAILREQ(Signal* signal);
+
+ void execWAIT_GCP_REF(Signal* signal);
+ void execWAIT_GCP_CONF(Signal* signal);
+
+ void execLIST_TABLES_REQ(Signal* signal);
+
+ // Index signals
+ void execCREATE_INDX_REQ(Signal* signal);
+ void execCREATE_INDX_CONF(Signal* signal);
+ void execCREATE_INDX_REF(Signal* signal);
+
+ void execALTER_INDX_REQ(Signal* signal);
+ void execALTER_INDX_CONF(Signal* signal);
+ void execALTER_INDX_REF(Signal* signal);
+
+ void execCREATE_TABLE_CONF(Signal* signal);
+ void execCREATE_TABLE_REF(Signal* signal);
+
+ void execDROP_INDX_REQ(Signal* signal);
+ void execDROP_INDX_CONF(Signal* signal);
+ void execDROP_INDX_REF(Signal* signal);
+
+ void execDROP_TABLE_CONF(Signal* signal);
+ void execDROP_TABLE_REF(Signal* signal);
+
+ void execBUILDINDXREQ(Signal* signal);
+ void execBUILDINDXCONF(Signal* signal);
+ void execBUILDINDXREF(Signal* signal);
+
+ // Util signals used by Event code
+ void execUTIL_PREPARE_CONF(Signal* signal);
+ void execUTIL_PREPARE_REF (Signal* signal);
+ void execUTIL_EXECUTE_CONF(Signal* signal);
+ void execUTIL_EXECUTE_REF (Signal* signal);
+ void execUTIL_RELEASE_CONF(Signal* signal);
+ void execUTIL_RELEASE_REF (Signal* signal);
+
+
+ // Event signals from API
+ void execCREATE_EVNT_REQ (Signal* signal);
+ void execCREATE_EVNT_CONF(Signal* signal);
+ void execCREATE_EVNT_REF (Signal* signal);
+
+ void execDROP_EVNT_REQ (Signal* signal);
+
+ void execSUB_START_REQ (Signal* signal);
+ void execSUB_START_CONF (Signal* signal);
+ void execSUB_START_REF (Signal* signal);
+
+ void execSUB_STOP_REQ (Signal* signal);
+ void execSUB_STOP_CONF (Signal* signal);
+ void execSUB_STOP_REF (Signal* signal);
+
+ // Event signals from SUMA
+
+ void execCREATE_SUBID_CONF(Signal* signal);
+ void execCREATE_SUBID_REF (Signal* signal);
+
+ void execSUB_CREATE_CONF(Signal* signal);
+ void execSUB_CREATE_REF (Signal* signal);
+
+ void execSUB_SYNC_CONF(Signal* signal);
+ void execSUB_SYNC_REF (Signal* signal);
+
+ void execSUB_REMOVE_REQ(Signal* signal);
+ void execSUB_REMOVE_CONF(Signal* signal);
+ void execSUB_REMOVE_REF(Signal* signal);
+
+ // Trigger signals
+ void execCREATE_TRIG_REQ(Signal* signal);
+ void execCREATE_TRIG_CONF(Signal* signal);
+ void execCREATE_TRIG_REF(Signal* signal);
+ void execALTER_TRIG_REQ(Signal* signal);
+ void execALTER_TRIG_CONF(Signal* signal);
+ void execALTER_TRIG_REF(Signal* signal);
+ void execDROP_TRIG_REQ(Signal* signal);
+ void execDROP_TRIG_CONF(Signal* signal);
+ void execDROP_TRIG_REF(Signal* signal);
+
+ void execDROP_TABLE_REQ(Signal* signal);
+
+ void execPREP_DROP_TAB_REQ(Signal* signal);
+ void execPREP_DROP_TAB_REF(Signal* signal);
+ void execPREP_DROP_TAB_CONF(Signal* signal);
+
+ void execDROP_TAB_REQ(Signal* signal);
+ void execDROP_TAB_REF(Signal* signal);
+ void execDROP_TAB_CONF(Signal* signal);
+
+ void execCREATE_TABLE_REQ(Signal* signal);
+ void execALTER_TABLE_REQ(Signal* signal);
+ void execCREATE_FRAGMENTATION_REF(Signal*);
+ void execCREATE_FRAGMENTATION_CONF(Signal*);
+ void execCREATE_TAB_REQ(Signal* signal);
+ void execADD_FRAGREQ(Signal* signal);
+ void execLQHFRAGREF(Signal* signal);
+ void execLQHFRAGCONF(Signal* signal);
+ void execLQHADDATTREF(Signal* signal);
+ void execLQHADDATTCONF(Signal* signal);
+ void execCREATE_TAB_REF(Signal* signal);
+ void execCREATE_TAB_CONF(Signal* signal);
+ void execALTER_TAB_REQ(Signal* signal);
+ void execALTER_TAB_REF(Signal* signal);
+ void execALTER_TAB_CONF(Signal* signal);
+
+ /*
+ * 2.4 COMMON STORED VARIABLES
+ */
+
+ /**
+ * This record stores all the state needed
+ * when the schema page is being sent to other nodes
+ ***************************************************************************/
+ struct SendSchemaRecord {
+ /** Number of words of schema data */
+ Uint32 noOfWords;
+ /** Page Id of schema data */
+ Uint32 pageId;
+
+ Uint32 nodeId;
+ SignalCounter m_SCHEMAINFO_Counter;
+
+ Uint32 noOfWordsCurrentlySent;
+ Uint32 noOfSignalsSentSinceDelay;
+
+ bool inUse;
+ };
+ SendSchemaRecord c_sendSchemaRecord;
+
+ /**
+ * This record stores all the state needed
+ * when a table file is being read from disk
+ ****************************************************************************/
+ struct ReadTableRecord {
+ /** Number of Pages */
+ Uint32 noOfPages;
+ /** Page Id*/
+ Uint32 pageId;
+ /** Table Id of read table */
+ Uint32 tableId;
+
+ bool inUse;
+ Callback m_callback;
+ };
+ ReadTableRecord c_readTableRecord;
+
+ /**
+ * This record stores all the state needed
+ * when a table file is being written to disk
+ ****************************************************************************/
+ struct WriteTableRecord {
+ /** Number of Pages */
+ Uint32 noOfPages;
+ /** Page Id*/
+ Uint32 pageId;
+ /** Table Files Handled, local state variable */
+ Uint32 noOfTableFilesHandled;
+ /** Table Id of written table */
+ Uint32 tableId;
+ /** State, indicates from where it was called */
+ enum TableWriteState {
+ IDLE = 0,
+ WRITE_ADD_TABLE_MASTER = 1,
+ WRITE_ADD_TABLE_SLAVE = 2,
+ WRITE_RESTART_FROM_MASTER = 3,
+ WRITE_RESTART_FROM_OWN = 4,
+ TWR_CALLBACK = 5
+ };
+ TableWriteState tableWriteState;
+ Callback m_callback;
+ };
+ WriteTableRecord c_writeTableRecord;
+
+ /**
+ * This record stores all the state needed
+ * when a schema file is being read from disk
+ ****************************************************************************/
+ struct ReadSchemaRecord {
+ /** Page Id of schema page */
+ Uint32 pageId;
+ /** State, indicates from where it was called */
+ enum SchemaReadState {
+ IDLE = 0,
+ INITIAL_READ = 1
+ };
+ SchemaReadState schemaReadState;
+ };
+ ReadSchemaRecord c_readSchemaRecord;
+
+private:
+ /**
+ * This record stores all the state needed
+ * when a schema file is being written to disk
+ ****************************************************************************/
+ struct WriteSchemaRecord {
+ /** Page Id of schema page */
+ Uint32 pageId;
+ /** Schema Files Handled, local state variable */
+ Uint32 noOfSchemaFilesHandled;
+
+ bool inUse;
+ Callback m_callback;
+ };
+ WriteSchemaRecord c_writeSchemaRecord;
+
+ /**
+ * This record stores all the information needed
+ * when a file is being read from disk
+ ****************************************************************************/
+ struct RestartRecord {
+ /** Global check point identity */
+ Uint32 gciToRestart;
+
+ /** The active table at restart process */
+ Uint32 activeTable;
+
+ /** The active table at restart process */
+ BlockReference returnBlockRef;
+ };
+ RestartRecord c_restartRecord;
+
+ /**
+ * This record stores all the information needed
+ * when a file is being read from disk
+ ****************************************************************************/
+ struct RetrieveRecord {
+ RetrieveRecord(){ noOfWaiters = 0;}
+
+ /** Only one retrieve table definition at a time */
+ bool busyState;
+
+ /**
+ * No of waiting in time queue
+ */
+ Uint32 noOfWaiters;
+
+ /** Block Reference of retriever */
+ BlockReference blockRef;
+
+ /** Id of retriever */
+ Uint32 m_senderData;
+
+ /** Table id of retrieved table */
+ Uint32 tableId;
+
+ /** Starting page to retrieve data from */
+ Uint32 retrievePage;
+
+ /** Number of pages retrieved */
+ Uint32 retrievedNoOfPages;
+
+ /** Number of words retrieved */
+ Uint32 retrievedNoOfWords;
+
+ /** Number of words sent currently */
+ Uint32 currentSent;
+
+ /**
+ * Long signal stuff
+ */
+ bool m_useLongSig;
+ };
+ RetrieveRecord c_retrieveRecord;
+
+ /**
+ * This record stores all the information needed
+ * when a file is being read from disk
+ *
+ * This is the info stored in one entry of the schema
+ * page. Each table has 4 words of info.
+ * Word 1: Schema version (upper 16 bits)
+ * Table State (lower 16 bits)
+ * Word 2: Number of pages of table description
+ * Word 3: Global checkpoint id table was created
+ * Word 4: Currently zero
+ ****************************************************************************/
+ struct SchemaRecord {
+ /** Schema page */
+ Uint32 schemaPage;
+
+ /** Old Schema page (used at node restart) */
+ Uint32 oldSchemaPage;
+
+ Callback m_callback;
+ };
+ SchemaRecord c_schemaRecord;
+
+ void initSchemaFile(SchemaFile *, Uint32 sz);
+ void computeChecksum(SchemaFile *);
+ bool validateChecksum(const SchemaFile *);
+ SchemaFile::TableEntry * getTableEntry(void * buf, Uint32 tableId,
+ bool allowTooBig = false);
+
+ Uint32 computeChecksum(const Uint32 * src, Uint32 len);
+
+
+ /* ----------------------------------------------------------------------- */
+ // Node References
+ /* ----------------------------------------------------------------------- */
+ Uint16 c_masterNodeId;
+
+ /* ----------------------------------------------------------------------- */
+ // Various current system properties
+ /* ----------------------------------------------------------------------- */
+ Uint16 c_numberNode;
+ Uint16 c_noHotSpareNodes;
+ Uint16 c_noNodesFailed;
+ Uint32 c_failureNr;
+
+ /* ----------------------------------------------------------------------- */
+ // State variables
+ /* ----------------------------------------------------------------------- */
+
+ enum BlockState {
+ BS_IDLE = 0,
+ BS_CREATE_TAB = 1,
+ BS_BUSY = 2,
+ BS_NODE_FAILURE = 3
+ };
+ BlockState c_blockState;
+
+ struct PackTable {
+
+ enum PackTableState {
+ PTS_IDLE = 0,
+ PTS_ADD_TABLE_MASTER = 1,
+ PTS_ADD_TABLE_SLAVE = 2,
+ PTS_GET_TAB = 3,
+ PTS_RESTART = 4
+ } m_state;
+
+ } c_packTable;
+
+ Uint32 c_startPhase;
+ Uint32 c_restartType;
+ bool c_initialStart;
+ bool c_systemRestart;
+ bool c_nodeRestart;
+ bool c_initialNodeRestart;
+ Uint32 c_tabinfoReceived;
+
+ /**
+ * Temporary structure used when parsing table info
+ */
+ struct ParseDictTabInfoRecord {
+ DictTabInfo::RequestType requestType;
+ Uint32 errorCode;
+ Uint32 errorLine;
+
+ SimpleProperties::UnpackStatus status;
+ Uint32 errorKey;
+ TableRecordPtr tablePtr;
+ };
+
+ // Operation records
+
+ /**
+ * Common part of operation records. Uses KeyTable2. Note that each
+ * seize/release invokes ctor/dtor automatically.
+ */
+ struct OpRecordCommon {
+ Uint32 key; // key shared between master and slaves
+ Uint32 nextHash;
+ Uint32 prevHash;
+ Uint32 hashValue() const {
+ return key;
+ }
+ bool equal(const OpRecordCommon& rec) const {
+ return key == rec.key;
+ }
+ };
+
+ /**
+ * Create table record
+ */
+ struct CreateTableRecord : OpRecordCommon {
+ Uint32 m_senderRef;
+ Uint32 m_senderData;
+ Uint32 m_coordinatorRef;
+
+ Uint32 m_errorCode;
+ void setErrorCode(Uint32 c){ if(m_errorCode == 0) m_errorCode = c;}
+
+ // For alter table
+ Uint32 m_changeMask;
+ bool m_alterTableFailed;
+ AlterTableRef m_alterTableRef;
+ Uint32 m_alterTableId;
+
+ /* Previous table name (used for reverting failed table rename) */
+ char previousTableName[MAX_TAB_NAME_SIZE];
+
+ Uint32 m_tablePtrI;
+ Uint32 m_tabInfoPtrI;
+ Uint32 m_fragmentsPtrI;
+
+ Uint32 m_dihAddFragPtr; // Connect ptr towards DIH
+ Uint32 m_lqhFragPtr; // Connect ptr towards LQH
+
+ Callback m_callback; // Who's using local create tab
+ MutexHandle2<DIH_START_LCP_MUTEX> m_startLcpMutex;
+
+ struct CoordinatorData {
+ Uint32 m_gsn;
+ SafeCounterHandle m_counter;
+ CreateTabReq::RequestType m_requestType;
+ } m_coordinatorData;
+ };
+ typedef Ptr<CreateTableRecord> CreateTableRecordPtr;
+
+ /**
+ * Drop table record
+ */
+ struct DropTableRecord : OpRecordCommon {
+ DropTableReq m_request;
+
+ Uint32 m_requestType;
+ Uint32 m_coordinatorRef;
+
+ Uint32 m_errorCode;
+ void setErrorCode(Uint32 c){ if(m_errorCode == 0) m_errorCode = c;}
+
+ /**
+ * When sending stuff around
+ */
+ struct CoordinatorData {
+ Uint32 m_gsn;
+ Uint32 m_block;
+ SignalCounter m_signalCounter;
+ } m_coordinatorData;
+
+ struct ParticipantData {
+ Uint32 m_gsn;
+ Uint32 m_block;
+ SignalCounter m_signalCounter;
+
+ Callback m_callback;
+ } m_participantData;
+ };
+ typedef Ptr<DropTableRecord> DropTableRecordPtr;
+
+ /**
+ * Request flags passed in signals along with request type and
+ * propagated across operations.
+ */
+ struct RequestFlag {
+ enum {
+ RF_LOCAL = 1 << 0, // create on local node only
+ RF_NOBUILD = 1 << 1, // no need to build index
+ RF_NOTCTRIGGER = 1 << 2 // alter trigger: no trigger in TC
+ };
+ };
+
+ /**
+ * Operation record for create index.
+ */
+ struct OpCreateIndex : OpRecordCommon {
+ // original request (index id will be added)
+ CreateIndxReq m_request;
+ AttributeList m_attrList;
+ char m_indexName[MAX_TAB_NAME_SIZE];
+ bool m_storedIndex;
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ CreateIndxReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ CreateIndxRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ // ctor
+ OpCreateIndex() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = CreateIndxReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = CreateIndxRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void save(const CreateIndxReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != CreateIndxRef::NoError;
+ }
+ void setError(const CreateIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const CreateTableRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ switch (ref->getErrorCode()) {
+ case CreateTableRef::TableAlreadyExist:
+ m_errorCode = CreateIndxRef::IndexExists;
+ break;
+ default:
+ m_errorCode = (CreateIndxRef::ErrorCode)ref->getErrorCode();
+ break;
+ }
+ m_errorLine = ref->getErrorLine();
+ }
+ }
+ void setError(const AlterIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (CreateIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpCreateIndex> OpCreateIndexPtr;
+
+ /**
+ * Operation record for drop index.
+ */
+ struct OpDropIndex : OpRecordCommon {
+ // original request
+ DropIndxReq m_request;
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ DropIndxReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ DropIndxRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ // ctor
+ OpDropIndex() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = DropIndxReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = DropIndxRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void save(const DropIndxReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != DropIndxRef::NoError;
+ }
+ void setError(const DropIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const AlterIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (DropIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const DropTableRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ switch(ref->errorCode) {
+ case(DropTableRef::Busy):
+ m_errorCode = DropIndxRef::Busy;
+ break;
+ case(DropTableRef::NoSuchTable):
+ m_errorCode = DropIndxRef::IndexNotFound;
+ break;
+ case(DropTableRef::DropInProgress):
+ m_errorCode = DropIndxRef::Busy;
+ break;
+ case(DropTableRef::NoDropTableRecordAvailable):
+ m_errorCode = DropIndxRef::Busy;
+ break;
+ default:
+ m_errorCode = (DropIndxRef::ErrorCode)ref->errorCode;
+ break;
+ }
+ //m_errorLine = ref->getErrorLine();
+ //m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpDropIndex> OpDropIndexPtr;
+
+ /**
+ * Operation record for alter index.
+ */
+ struct OpAlterIndex : OpRecordCommon {
+ // original request plus buffer for attribute lists
+ AlterIndxReq m_request;
+ AttributeList m_attrList;
+ AttributeList m_tableKeyList;
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ AlterIndxReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ AlterIndxRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ Uint32 m_triggerCounter;
+ // ctor
+ OpAlterIndex() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = AlterIndxReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = AlterIndxRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ m_triggerCounter = 0;
+ }
+ void save(const AlterIndxReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != AlterIndxRef::NoError;
+ }
+ void setError(const AlterIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const CreateIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const DropIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const BuildIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterIndxRef::ErrorCode)ref->getErrorCode();
+ }
+ }
+ void setError(const CreateTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const DropTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpAlterIndex> OpAlterIndexPtr;
+
+ /**
+ * Operation record for build index.
+ */
+ struct OpBuildIndex : OpRecordCommon {
+ // original request plus buffer for attribute lists
+ BuildIndxReq m_request;
+ AttributeList m_attrList;
+ AttributeList m_tableKeyList;
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ BuildIndxReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ Uint32 m_constrTriggerId;
+ // error info
+ BuildIndxRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ // ctor
+ OpBuildIndex() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = BuildIndxReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+// Uint32 m_constrTriggerId = RNIL;
+ m_errorCode = BuildIndxRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void save(const BuildIndxReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != BuildIndxRef::NoError;
+ }
+ void setError(const BuildIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ }
+ }
+ void setError(const AlterIndxRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (BuildIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const CreateTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (BuildIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const DropTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (BuildIndxRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpBuildIndex> OpBuildIndexPtr;
+
+ /**
+ * Operation record for Util Signals.
+ */
+ struct OpSignalUtil : OpRecordCommon{
+ Callback m_callback;
+ Uint32 m_userData;
+ };
+ typedef Ptr<OpSignalUtil> OpSignalUtilPtr;
+
+ /**
+ * Operation record for subscribe-start-stop
+ */
+ struct OpSubEvent : OpRecordCommon {
+ Uint32 m_senderRef;
+ Uint32 m_senderData;
+ Uint32 m_errorCode;
+ RequestTracker m_reqTracker;
+ };
+ typedef Ptr<OpSubEvent> OpSubEventPtr;
+
+ static const Uint32 sysTab_NDBEVENTS_0_szs[];
+
+ /**
+ * Operation record for create event.
+ */
+ struct OpCreateEvent : OpRecordCommon {
+ // original request (event id will be added)
+ CreateEvntReq m_request;
+ //AttributeMask m_attrListBitmask;
+ // AttributeList m_attrList;
+ sysTab_NDBEVENTS_0 m_eventRec;
+ // char m_eventName[MAX_TAB_NAME_SIZE];
+ // char m_tableName[MAX_TAB_NAME_SIZE];
+
+ // coordinator DICT
+ RequestTracker m_reqTracker;
+ // state info
+ CreateEvntReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ CreateEvntRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // ctor
+ OpCreateEvent() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_requestType = CreateEvntReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = CreateEvntRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void init(const CreateEvntReq* req, Dbdict* dp) {
+ m_request = *req;
+ m_errorCode = CreateEvntRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != CreateEvntRef::NoError;
+ }
+ void setError(const CreateEvntRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+
+ };
+ typedef Ptr<OpCreateEvent> OpCreateEventPtr;
+
+ /**
+ * Operation record for drop event.
+ */
+ struct OpDropEvent : OpRecordCommon {
+ // original request
+ DropEvntReq m_request;
+ // char m_eventName[MAX_TAB_NAME_SIZE];
+ sysTab_NDBEVENTS_0 m_eventRec;
+ RequestTracker m_reqTracker;
+ // error info
+ DropEvntRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // ctor
+ OpDropEvent() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_errorCode = DropEvntRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void init(const DropEvntReq* req) {
+ m_request = *req;
+ m_errorCode = DropEvntRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ bool hasError() {
+ return m_errorCode != DropEvntRef::NoError;
+ }
+ void setError(const DropEvntRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpDropEvent> OpDropEventPtr;
+
+ /**
+ * Operation record for create trigger.
+ */
+ struct OpCreateTrigger : OpRecordCommon {
+ // original request (trigger id will be added)
+ CreateTrigReq m_request;
+ char m_triggerName[MAX_TAB_NAME_SIZE];
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ CreateTrigReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ CreateTrigRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ // ctor
+ OpCreateTrigger() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = CreateTrigReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = CreateTrigRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void save(const CreateTrigReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != CreateTrigRef::NoError;
+ }
+ void setError(const CreateTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const AlterTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (CreateTrigRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpCreateTrigger> OpCreateTriggerPtr;
+
+ /**
+ * Operation record for drop trigger.
+ */
+ struct OpDropTrigger : OpRecordCommon {
+ // original request
+ DropTrigReq m_request;
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ DropTrigReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ DropTrigRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ // ctor
+ OpDropTrigger() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = DropTrigReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = DropTrigRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void save(const DropTrigReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != DropTrigRef::NoError;
+ }
+ void setError(const DropTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const AlterTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (DropTrigRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpDropTrigger> OpDropTriggerPtr;
+
+ /**
+ * Operation record for alter trigger.
+ */
+ struct OpAlterTrigger : OpRecordCommon {
+ // original request
+ AlterTrigReq m_request;
+ // nodes participating in operation
+ NdbNodeBitmask m_nodes;
+ // coordinator DICT
+ Uint32 m_coordinatorRef;
+ bool m_isMaster;
+ // state info
+ AlterTrigReq::RequestType m_requestType;
+ Uint32 m_requestFlag;
+ // error info
+ AlterTrigRef::ErrorCode m_errorCode;
+ Uint32 m_errorLine;
+ Uint32 m_errorNode;
+ // counters
+ SignalCounter m_signalCounter;
+ // ctor
+ OpAlterTrigger() {
+ memset(&m_request, 0, sizeof(m_request));
+ m_coordinatorRef = 0;
+ m_requestType = AlterTrigReq::RT_UNDEFINED;
+ m_requestFlag = 0;
+ m_errorCode = AlterTrigRef::NoError;
+ m_errorLine = 0;
+ m_errorNode = 0;
+ }
+ void save(const AlterTrigReq* req) {
+ m_request = *req;
+ m_requestType = req->getRequestType();
+ m_requestFlag = req->getRequestFlag();
+ }
+ bool hasError() {
+ return m_errorCode != AlterTrigRef::NoError;
+ }
+ void setError(const AlterTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterTrigRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const CreateTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterTrigRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ void setError(const DropTrigRef* ref) {
+ if (ref != 0 && ! hasError()) {
+ m_errorCode = (AlterTrigRef::ErrorCode)ref->getErrorCode();
+ m_errorLine = ref->getErrorLine();
+ m_errorNode = ref->getErrorNode();
+ }
+ }
+ };
+ typedef Ptr<OpAlterTrigger> OpAlterTriggerPtr;
+
+ // Common operation record pool
+public:
+ STATIC_CONST( opCreateTableSize = sizeof(CreateTableRecord) );
+ STATIC_CONST( opDropTableSize = sizeof(DropTableRecord) );
+ STATIC_CONST( opCreateIndexSize = sizeof(OpCreateIndex) );
+ STATIC_CONST( opDropIndexSize = sizeof(OpDropIndex) );
+ STATIC_CONST( opAlterIndexSize = sizeof(OpAlterIndex) );
+ STATIC_CONST( opBuildIndexSize = sizeof(OpBuildIndex) );
+ STATIC_CONST( opCreateEventSize = sizeof(OpCreateEvent) );
+ STATIC_CONST( opSubEventSize = sizeof(OpSubEvent) );
+ STATIC_CONST( opDropEventSize = sizeof(OpDropEvent) );
+ STATIC_CONST( opSignalUtilSize = sizeof(OpSignalUtil) );
+ STATIC_CONST( opCreateTriggerSize = sizeof(OpCreateTrigger) );
+ STATIC_CONST( opDropTriggerSize = sizeof(OpDropTrigger) );
+ STATIC_CONST( opAlterTriggerSize = sizeof(OpAlterTrigger) );
+private:
+#define PTR_ALIGN(n) ((((n)+sizeof(void*)-1)>>2)&~((sizeof(void*)-1)>>2))
+ union OpRecordUnion {
+ Uint32 u_opCreateTable [PTR_ALIGN(opCreateTableSize)];
+ Uint32 u_opDropTable [PTR_ALIGN(opDropTableSize)];
+ Uint32 u_opCreateIndex [PTR_ALIGN(opCreateIndexSize)];
+ Uint32 u_opDropIndex [PTR_ALIGN(opDropIndexSize)];
+ Uint32 u_opCreateEvent [PTR_ALIGN(opCreateEventSize)];
+ Uint32 u_opSubEvent [PTR_ALIGN(opSubEventSize)];
+ Uint32 u_opDropEvent [PTR_ALIGN(opDropEventSize)];
+ Uint32 u_opSignalUtil [PTR_ALIGN(opSignalUtilSize)];
+ Uint32 u_opAlterIndex [PTR_ALIGN(opAlterIndexSize)];
+ Uint32 u_opBuildIndex [PTR_ALIGN(opBuildIndexSize)];
+ Uint32 u_opCreateTrigger[PTR_ALIGN(opCreateTriggerSize)];
+ Uint32 u_opDropTrigger [PTR_ALIGN(opDropTriggerSize)];
+ Uint32 u_opAlterTrigger [PTR_ALIGN(opAlterTriggerSize)];
+ Uint32 nextPool;
+ };
+ ArrayPool<OpRecordUnion> c_opRecordPool;
+
+ // Operation records
+ KeyTable2<CreateTableRecord, OpRecordUnion> c_opCreateTable;
+ KeyTable2<DropTableRecord, OpRecordUnion> c_opDropTable;
+ KeyTable2<OpCreateIndex, OpRecordUnion> c_opCreateIndex;
+ KeyTable2<OpDropIndex, OpRecordUnion> c_opDropIndex;
+ KeyTable2<OpAlterIndex, OpRecordUnion> c_opAlterIndex;
+ KeyTable2<OpBuildIndex, OpRecordUnion> c_opBuildIndex;
+ KeyTable2<OpCreateEvent, OpRecordUnion> c_opCreateEvent;
+ KeyTable2<OpSubEvent, OpRecordUnion> c_opSubEvent;
+ KeyTable2<OpDropEvent, OpRecordUnion> c_opDropEvent;
+ KeyTable2<OpSignalUtil, OpRecordUnion> c_opSignalUtil;
+ KeyTable2<OpCreateTrigger, OpRecordUnion> c_opCreateTrigger;
+ KeyTable2<OpDropTrigger, OpRecordUnion> c_opDropTrigger;
+ KeyTable2<OpAlterTrigger, OpRecordUnion> c_opAlterTrigger;
+
+ // Unique key for operation XXX move to some system table
+ Uint32 c_opRecordSequence;
+
+ // Statement blocks
+
+ /* ------------------------------------------------------------ */
+ // Start/Restart Handling
+ /* ------------------------------------------------------------ */
+ void sendSTTORRY(Signal* signal);
+ void sendNDB_STTORRY(Signal* signal);
+ void initSchemaFile(Signal* signal);
+
+ /* ------------------------------------------------------------ */
+ // Drop Table Handling
+ /* ------------------------------------------------------------ */
+ void releaseTableObject(Uint32 tableId, bool removeFromHash = true);
+
+ /* ------------------------------------------------------------ */
+ // General Stuff
+ /* ------------------------------------------------------------ */
+ Uint32 getFreeTableRecord(Uint32 primaryTableId);
+ Uint32 getFreeTriggerRecord();
+ bool getNewAttributeRecord(TableRecordPtr tablePtr,
+ AttributeRecordPtr & attrPtr);
+ void packTableIntoPages(Signal* signal, Uint32 tableId, Uint32 pageId);
+ void packTableIntoPagesImpl(SimpleProperties::Writer &, TableRecordPtr,
+ Signal* signal= 0);
+
+ void sendGET_TABINFOREQ(Signal* signal,
+ Uint32 tableId);
+ void sendTC_SCHVERREQ(Signal* signal,
+ Uint32 tableId,
+ BlockReference tcRef);
+
+ /* ------------------------------------------------------------ */
+ // System Restart Handling
+ /* ------------------------------------------------------------ */
+ void initSendSchemaData(Signal* signal);
+ void sendSchemaData(Signal* signal);
+ Uint32 sendSCHEMA_INFO(Signal* signal, Uint32 nodeId, Uint32* pagePointer);
+ void checkSchemaStatus(Signal* signal);
+ void sendDIHSTARTTAB_REQ(Signal* signal);
+
+ /* ------------------------------------------------------------ */
+ // Receive Table Handling
+ /* ------------------------------------------------------------ */
+ void handleTabInfoInit(SimpleProperties::Reader &,
+ ParseDictTabInfoRecord *,
+ bool checkExist = true);
+ void handleTabInfo(SimpleProperties::Reader & it, ParseDictTabInfoRecord *);
+
+ void handleAddTableFailure(Signal* signal,
+ Uint32 failureLine,
+ Uint32 tableId);
+ bool verifyTableCorrect(Signal* signal, Uint32 tableId);
+
+ /* ------------------------------------------------------------ */
+ // Add Table Handling
+ /* ------------------------------------------------------------ */
+
+ /* ------------------------------------------------------------ */
+ // Add Fragment Handling
+ /* ------------------------------------------------------------ */
+ void sendLQHADDATTRREQ(Signal*, CreateTableRecordPtr, Uint32 attributePtrI);
+
+ /* ------------------------------------------------------------ */
+ // Read/Write Schema and Table files
+ /* ------------------------------------------------------------ */
+ void updateSchemaState(Signal* signal, Uint32 tableId,
+ SchemaFile::TableEntry*, Callback*);
+ void startWriteSchemaFile(Signal* signal);
+ void openSchemaFile(Signal* signal,
+ Uint32 fileNo,
+ Uint32 fsPtr,
+ bool writeFlag);
+ void writeSchemaFile(Signal* signal, Uint32 filePtr, Uint32 fsPtr);
+ void writeSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void closeFile(Signal* signal, Uint32 filePtr, Uint32 fsPtr);
+ void closeWriteSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void initSchemaFile_conf(Signal* signal, Uint32 i, Uint32 returnCode);
+
+ void writeTableFile(Signal* signal, Uint32 tableId,
+ SegmentedSectionPtr tabInfo, Callback*);
+ void startWriteTableFile(Signal* signal, Uint32 tableId);
+ void openTableFile(Signal* signal,
+ Uint32 fileNo,
+ Uint32 fsPtr,
+ Uint32 tableId,
+ bool writeFlag);
+ void writeTableFile(Signal* signal, Uint32 filePtr, Uint32 fsPtr);
+ void writeTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void closeWriteTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+
+ void startReadTableFile(Signal* signal, Uint32 tableId);
+ void openReadTableRef(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void readTableFile(Signal* signal, Uint32 filePtr, Uint32 fsPtr);
+ void readTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void readTableRef(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void closeReadTableConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+
+ void startReadSchemaFile(Signal* signal);
+ void openReadSchemaRef(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+ void readSchemaFile(Signal* signal, Uint32 filePtr, Uint32 fsPtr);
+ void readSchemaConf(Signal* signal, FsConnectRecordPtr fsPtr);
+ void readSchemaRef(Signal* signal, FsConnectRecordPtr fsPtr);
+ void closeReadSchemaConf(Signal* signal,
+ FsConnectRecordPtr fsPtr);
+
+ /* ------------------------------------------------------------ */
+ // Get table definitions
+ /* ------------------------------------------------------------ */
+ void sendGET_TABINFOREF(Signal* signal,
+ GetTabInfoReq*,
+ GetTabInfoRef::ErrorCode errorCode);
+
+ void sendGET_TABLEID_REF(Signal* signal,
+ GetTableIdReq * req,
+ GetTableIdRef::ErrorCode errorCode);
+
+ void sendGetTabResponse(Signal* signal);
+
+ /* ------------------------------------------------------------ */
+ // Indexes and triggers
+ /* ------------------------------------------------------------ */
+
+ // reactivate and rebuild indexes on start up
+ void activateIndexes(Signal* signal, Uint32 i);
+ void rebuildIndexes(Signal* signal, Uint32 i);
+
+ // create index
+ void createIndex_recvReply(Signal* signal, const CreateIndxConf* conf,
+ const CreateIndxRef* ref);
+ void createIndex_slavePrepare(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_fromCreateTable(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_toAlterIndex(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_fromAlterIndex(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_slaveCommit(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_slaveAbort(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_sendSlaveReq(Signal* signal, OpCreateIndexPtr opPtr);
+ void createIndex_sendReply(Signal* signal, OpCreateIndexPtr opPtr, bool);
+ // drop index
+ void dropIndex_recvReply(Signal* signal, const DropIndxConf* conf,
+ const DropIndxRef* ref);
+ void dropIndex_slavePrepare(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_toAlterIndex(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_fromAlterIndex(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_toDropTable(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_fromDropTable(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_slaveCommit(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_slaveAbort(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_sendSlaveReq(Signal* signal, OpDropIndexPtr opPtr);
+ void dropIndex_sendReply(Signal* signal, OpDropIndexPtr opPtr, bool);
+ // alter index
+ void alterIndex_recvReply(Signal* signal, const AlterIndxConf* conf,
+ const AlterIndxRef* ref);
+ void alterIndex_slavePrepare(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_toCreateTc(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_fromCreateTc(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_toDropTc(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_fromDropTc(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_toCreateTrigger(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_fromCreateTrigger(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_toDropTrigger(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_fromDropTrigger(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_toBuildIndex(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_fromBuildIndex(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_slaveCommit(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_slaveAbort(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_sendSlaveReq(Signal* signal, OpAlterIndexPtr opPtr);
+ void alterIndex_sendReply(Signal* signal, OpAlterIndexPtr opPtr, bool);
+ // build index
+ void buildIndex_recvReply(Signal* signal, const BuildIndxConf* conf,
+ const BuildIndxRef* ref);
+ void buildIndex_toCreateConstr(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_fromCreateConstr(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_buildTrix(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_toDropConstr(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_fromDropConstr(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_toOnline(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_fromOnline(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_sendSlaveReq(Signal* signal, OpBuildIndexPtr opPtr);
+ void buildIndex_sendReply(Signal* signal, OpBuildIndexPtr opPtr, bool);
+
+ // Events
+ void
+ createEventUTIL_PREPARE(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ createEventUTIL_EXECUTE(Signal *signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ dropEventUTIL_PREPARE_READ(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ dropEventUTIL_EXECUTE_READ(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ dropEventUTIL_PREPARE_DELETE(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ dropEventUTIL_EXECUTE_DELETE(Signal *signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ dropEventUtilPrepareRef(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void
+ dropEventUtilExecuteRef(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ int
+ sendSignalUtilReq(Callback *c,
+ BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections);
+ int
+ recvSignalUtilReq(Signal* signal, Uint32 returnCode);
+
+ void completeSubStartReq(Signal* signal, Uint32 ptrI, Uint32 returnCode);
+ void completeSubStopReq(Signal* signal, Uint32 ptrI, Uint32 returnCode);
+ void completeSubRemoveReq(Signal* signal, Uint32 ptrI, Uint32 returnCode);
+
+ void dropEvent_sendReply(Signal* signal,
+ OpDropEventPtr evntRecPtr);
+
+ void createEvent_RT_USER_CREATE(Signal* signal, OpCreateEventPtr evntRecPtr);
+ void createEventComplete_RT_USER_CREATE(Signal* signal,
+ OpCreateEventPtr evntRecPtr);
+ void createEvent_RT_USER_GET(Signal* signal, OpCreateEventPtr evntRecPtr);
+ void createEventComplete_RT_USER_GET(Signal* signal, OpCreateEventPtr evntRecPtr);
+
+ void createEvent_RT_DICT_AFTER_GET(Signal* signal, OpCreateEventPtr evntRecPtr);
+
+ void createEvent_nodeFailCallback(Signal* signal, Uint32 eventRecPtrI,
+ Uint32 returnCode);
+ void createEvent_sendReply(Signal* signal, OpCreateEventPtr evntRecPtr,
+ LinearSectionPtr *ptr = NULL, int noLSP = 0);
+
+ void prepareTransactionEventSysTable (Callback *c,
+ Signal* signal,
+ Uint32 senderData,
+ UtilPrepareReq::OperationTypeValue prepReq);
+ void prepareUtilTransaction(Callback *c,
+ Signal* signal,
+ Uint32 senderData,
+ Uint32 tableId,
+ const char *tableName,
+ UtilPrepareReq::OperationTypeValue prepReq,
+ Uint32 noAttr,
+ Uint32 attrIds[],
+ const char *attrNames[]);
+
+ void executeTransEventSysTable(Callback *c,
+ Signal *signal,
+ const Uint32 ptrI,
+ sysTab_NDBEVENTS_0& m_eventRec,
+ const Uint32 prepareId,
+ UtilPrepareReq::OperationTypeValue prepReq);
+ void executeTransaction(Callback *c,
+ Signal* signal,
+ Uint32 senderData,
+ Uint32 prepareId,
+ Uint32 noAttr,
+ LinearSectionPtr headerPtr,
+ LinearSectionPtr dataPtr);
+
+ void parseReadEventSys(Signal *signal, sysTab_NDBEVENTS_0& m_eventRec);
+
+ // create trigger
+ void createTrigger_recvReply(Signal* signal, const CreateTrigConf* conf,
+ const CreateTrigRef* ref);
+ void createTrigger_slavePrepare(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_masterSeize(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_slaveCreate(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_toAlterTrigger(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_fromAlterTrigger(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_slaveCommit(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_slaveAbort(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_sendSlaveReq(Signal* signal, OpCreateTriggerPtr opPtr);
+ void createTrigger_sendReply(Signal* signal, OpCreateTriggerPtr opPtr, bool);
+ // drop trigger
+ void dropTrigger_recvReply(Signal* signal, const DropTrigConf* conf,
+ const DropTrigRef* ref);
+ void dropTrigger_slavePrepare(Signal* signal, OpDropTriggerPtr opPtr);
+ void dropTrigger_toAlterTrigger(Signal* signal, OpDropTriggerPtr opPtr);
+ void dropTrigger_fromAlterTrigger(Signal* signal, OpDropTriggerPtr opPtr);
+ void dropTrigger_slaveCommit(Signal* signal, OpDropTriggerPtr opPtr);
+ void dropTrigger_slaveAbort(Signal* signal, OpDropTriggerPtr opPtr);
+ void dropTrigger_sendSlaveReq(Signal* signal, OpDropTriggerPtr opPtr);
+ void dropTrigger_sendReply(Signal* signal, OpDropTriggerPtr opPtr, bool);
+ // alter trigger
+ void alterTrigger_recvReply(Signal* signal, const AlterTrigConf* conf,
+ const AlterTrigRef* ref);
+ void alterTrigger_slavePrepare(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_toCreateLocal(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_fromCreateLocal(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_toDropLocal(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_fromDropLocal(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_slaveCommit(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_slaveAbort(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_sendSlaveReq(Signal* signal, OpAlterTriggerPtr opPtr);
+ void alterTrigger_sendReply(Signal* signal, OpAlterTriggerPtr opPtr, bool);
+ // support
+ void getTableKeyList(TableRecordPtr tablePtr, AttributeList& list);
+ void getIndexAttr(TableRecordPtr indexPtr, Uint32 itAttr, Uint32* id);
+ void getIndexAttrList(TableRecordPtr indexPtr, AttributeList& list);
+ void getIndexAttrMask(TableRecordPtr indexPtr, AttributeMask& mask);
+
+ /* ------------------------------------------------------------ */
+ // Initialisation
+ /* ------------------------------------------------------------ */
+ void initCommonData();
+ void initRecords();
+ void initConnectRecord();
+ void initRetrieveRecord(Signal*, Uint32, Uint32 returnCode);
+ void initSchemaRecord();
+ void initRestartRecord();
+ void initSendSchemaRecord();
+ void initReadTableRecord();
+ void initWriteTableRecord();
+ void initReadSchemaRecord();
+ void initWriteSchemaRecord();
+
+ void initNodeRecords();
+ void initTableRecords();
+ void initialiseTableRecord(TableRecordPtr tablePtr);
+ void initTriggerRecords();
+ void initialiseTriggerRecord(TriggerRecordPtr triggerPtr);
+ void initPageRecords();
+
+ Uint32 getFsConnRecord();
+
+ bool getIsFailed(Uint32 nodeId) const;
+
+ void dropTableRef(Signal * signal, DropTableReq *, DropTableRef::ErrorCode);
+ void printTables(); // For debugging only
+ int handleAlterTab(AlterTabReq * req,
+ CreateTableRecord * regAlterTabPtr,
+ TableRecordPtr origTablePtr,
+ TableRecordPtr newTablePtr);
+ void revertAlterTable(Signal * signal,
+ Uint32 changeMask,
+ Uint32 tableId,
+ CreateTableRecord * regAlterTabPtr);
+ void alterTableRef(Signal * signal,
+ AlterTableReq *, AlterTableRef::ErrorCode,
+ ParseDictTabInfoRecord* parseRecord = NULL);
+ void alterTabRef(Signal * signal,
+ AlterTabReq *, AlterTableRef::ErrorCode,
+ ParseDictTabInfoRecord* parseRecord = NULL);
+ void alterTab_writeSchemaConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ void alterTab_writeTableConf(Signal* signal,
+ Uint32 callbackData,
+ Uint32 returnCode);
+
+ void prepDropTab_nextStep(Signal* signal, DropTableRecordPtr);
+ void prepDropTab_complete(Signal* signal, DropTableRecordPtr);
+ void prepDropTab_writeSchemaConf(Signal* signal, Uint32 dropTabPtrI, Uint32);
+
+ void dropTab_localDROP_TAB_CONF(Signal* signal);
+ void dropTab_nextStep(Signal* signal, DropTableRecordPtr);
+ void dropTab_complete(Signal* signal, Uint32 dropTabPtrI, Uint32);
+ void dropTab_writeSchemaConf(Signal* signal, Uint32 dropTabPtrI, Uint32);
+
+ void createTab_prepare(Signal* signal, CreateTabReq * req);
+ void createTab_writeSchemaConf1(Signal* signal, Uint32 callback, Uint32);
+ void createTab_writeTableConf(Signal* signal, Uint32 callbackData, Uint32);
+ void createTab_dih(Signal*, CreateTableRecordPtr,
+ SegmentedSectionPtr, Callback*);
+ void createTab_dihComplete(Signal* signal, Uint32 callbackData, Uint32);
+
+ void createTab_startLcpMutex_locked(Signal* signal, Uint32, Uint32);
+ void createTab_startLcpMutex_unlocked(Signal* signal, Uint32, Uint32);
+
+ void createTab_commit(Signal* signal, CreateTabReq * req);
+ void createTab_writeSchemaConf2(Signal* signal, Uint32 callbackData, Uint32);
+ void createTab_alterComplete(Signal*, Uint32 callbackData, Uint32);
+
+ void createTab_drop(Signal* signal, CreateTabReq * req);
+ void createTab_dropComplete(Signal* signal, Uint32 callbackData, Uint32);
+
+ void createTab_reply(Signal* signal, CreateTableRecordPtr, Uint32 nodeId);
+ void alterTab_activate(Signal*, CreateTableRecordPtr, Callback*);
+
+ void restartCreateTab(Signal*, Uint32, const SchemaFile::TableEntry *, bool);
+ void restartCreateTab_readTableConf(Signal* signal, Uint32 callback, Uint32);
+ void restartCreateTab_writeTableConf(Signal* signal, Uint32 callback, Uint32);
+ void restartCreateTab_dihComplete(Signal* signal, Uint32 callback, Uint32);
+ void restartCreateTab_activateComplete(Signal*, Uint32 callback, Uint32);
+
+ void restartDropTab(Signal* signal, Uint32 tableId);
+ void restartDropTab_complete(Signal*, Uint32 callback, Uint32);
+
+ void restart_checkSchemaStatusComplete(Signal*, Uint32 callback, Uint32);
+ void restart_writeSchemaConf(Signal*, Uint32 callbackData, Uint32);
+ void masterRestart_checkSchemaStatusComplete(Signal*, Uint32, Uint32);
+
+ void sendSchemaComplete(Signal*, Uint32 callbackData, Uint32);
+
+ // global metadata support
+ friend class MetaData;
+ int getMetaTablePtr(TableRecordPtr& tablePtr, Uint32 tableId, Uint32 tableVersion);
+ int getMetaTable(MetaData::Table& table, Uint32 tableId, Uint32 tableVersion);
+ int getMetaTable(MetaData::Table& table, const char* tableName);
+ int getMetaAttribute(MetaData::Attribute& attribute, const MetaData::Table& table, Uint32 attributeId);
+ int getMetaAttribute(MetaData::Attribute& attribute, const MetaData::Table& table, const char* attributeName);
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.txt b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.txt
new file mode 100644
index 00000000000..8d4267a1c42
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.txt
@@ -0,0 +1,88 @@
+
+Event creation
+
+USER DICT(Master) UTIL SUMA
+================================================================================
+CREATE_EVENT_REQ::create
+-------------------------->
+ - Get ID
+ CREATE_SUBID
+ ----------------------------------------------->
+ <-----------------------------------------------
+ - insert into system table
+ UTIL_PREPARE::insert
+ ------------------------>
+ <------------------------
+ UTIL_EXECUTE
+ ------------------------>
+ <------------------------
+CREATE_EVENT_CONF
+<--------------------------
+
+
+Event dropping
+
+USER DICT(Master) UTIL SUMA
+================================================================================
+DROP_EVENT_REQ
+-------------------------->
+ - remove from system table
+ UTIL_PREPARE::delete
+ ------------------------>
+ <------------------------
+ UTIL_EXECUTE
+ ------------------------>
+ <------------------------
+DROP_EVENT_CONF
+<--------------------------
+
+
+
+create NdbEventOperation
+
+USER DICT(Master) (Slaves) UTIL
+=======================================================================
+CREATE_EVENT_REQ::get
+-------------------------->
+ - read from system table
+ UTIL_PREPARE::read
+ ---------------------------------------->
+ <----------------------------------------
+ UTIL_EXECUTE
+ ---------------------------------------->
+ <----------------------------------------
+ SUMA
+ CREATE_EVENT_REQ::after_get ======
+ ---------------------->
+ SUB_CREATE
+ ------------------>
+ <------------------
+ SUB_SYNC
+ ------------------>
+ <------------------
+ CREATE_EVENT_CONF
+ <----------------------
+CREATE_EVENT_CONF
+<-------------------------
+
+
+
+USER DICT(Master) (Slaves) SUMA
+=======================================================================
+SUB_START_REQ
+-------------------------->
+ SUB_START_REQ
+ ---------------------->
+ SUB_START
+ ------------------>
+ <------------------
+ SUB_START_CONF
+ <----------------------
+SUB_START_CONF
+<-------------------------
+
+
+SUB_STOP analogous to SUB_STOP
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbdict/DropTable.txt b/storage/ndb/src/kernel/blocks/dbdict/DropTable.txt
new file mode 100644
index 00000000000..8d364d15c57
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/DropTable.txt
@@ -0,0 +1,140 @@
+DROP TABLE DESCRIPTION
+----------------------
+
+Drop table is controlled by DICT.
+
+Drop table is used in the following cases in some sort.
+ - Drop Table
+ - Abort Add Table
+ - Drop table in node restart
+ - Drop table in system restart
+
+Sequence of Drop Table:
+-----------------------
+
+1) PREP_DROP_TAB_REQ -> all DICT
+ Update schema files on disk
+ Table status = DROPPING
+
+2) Controlling DICT only
+ Report Table Dropped secured but not yet completed.
+
+------ PREP DROP
+
+4) PREP_DROP_TAB_REQ -> all LQHs
+
+5) PREP_DROP_TAB_REQ -> all TCs
+
+6) PREP_DROP_TAB_REQ -> all DIHs
+
+
+--- LQH::PREP_DROP_TAB_REQ
+
+*) Mark the table so that no new operations will start
+*) Mark all fragments so that new LCP_FRAG_ORD gets replied directly
+ w.o actually checkpointing the fragment
+2) Start waiting for completion
+3) Reply PREP_DROP_TAB_CONF
+
+- After this LQH accepts WAIT_DROP_TAB_REQ
+
+--- TC::PREP_DROP_TAB_REQ
+
+1) Mark the table so that no new transactions will start on the table
+2) Send WAIT_DROP_TAB_REQ -> all connected LQH's
+3) Wait for CONF (including NF-handling) from LQH:s
+4) Reply PREP_DROP_TAB_CONF
+
+--- DIH::PREP_DROP_TAB_REQ
+
+1) Mark the table so that no new LCP will start on the table
+2) If master (unlink any queued LCP_FRAG_ORD)
+3) Send WAIT_DROP_TAB_REQ -> all connected LQH's
+4) Wait for CONF (including NF-handling) from LQH:s
+5) Reply PREP_DROP_TAB_CONF
+
+--- LQH::WAIT_DROP_TAB_REQ
+
+1) Wait for running operations
+ Wait for running LCP
+
+2) Reply
+
+------ PREP_DROP
+
+7) DROP_TAB_REQ -> all DICT's
+ *) DROP_TAB_REQ -> TC
+ *) DROP_TAB_REQ -> ACC
+ *) DROP_TAB_REQ -> TUP
+ *) DROP_TAB_REQ -> DIH
+ *) DROP_TAB_REQ -> LQH
+ *) Update schema files on disk DROPPED
+
+8) DICT_SCHEMAREQ -> all DICT
+ Table status = DROPPED
+
+---------------------------------
+
+Sequence of Drop table in node/system restart
+---------------------------------------------
+
+In both node and system restart the node receives the schema information from
+the master. If the table is in a state where it needs to complete the drop
+table activity then DBACC, DBTUP, DBDIH, DBDICT is contacted to drop all files
+related to the table. After this the schema information is updated with the new
+state. Since all nodes receive the same schema information there is no risk of
+different behaviour in the various NDB nodes.
+
+API Requirements for Drop Table
+-------------------------------
+Definition:
+
+ Two tables are NOT the same if they were created with two create
+ tables at different points in time, even if the two create tables
+ had exactly the same definition.
+
+Requirements:
+
+1. Each operation in a transaction refering to a table (by name or by id)
+ should operate on the same table. (This is probably necessary.)
+
+2. Each operation in a transaction refering to a table (by name or by
+ id) should operate on the same table as were defined at the
+ startTransaction timepoint. (This is not strictly necessary for
+ API consistency.)
+
+ Example 1:
+
+ startTransaction()
+
+ drop("TableName1")
+ create("TableName1")
+
+ getNdbOperation("TableName1")
+
+ execute(commit)
+
+ - If both requirements 1 and 2 are fulfilled, then this should lead
+ to "Error: Invalid Schema Version" or similar error
+
+ - If only requirement 1 is fulfilled, then this may be executed
+ without any errors.
+
+
+ Example 2:
+
+ startTransaction()
+
+ getNdbOperation("TableName1")
+ execute(NoCommit)
+
+ drop("TableName1")
+ create("TableName1")
+
+ getNdbOperation("TableName1")
+
+ execute(commit)
+
+ - This should always lead to "Error: Invalid Schema Version" or
+ similar error.
+
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Event.txt b/storage/ndb/src/kernel/blocks/dbdict/Event.txt
new file mode 100644
index 00000000000..553c915d9c5
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Event.txt
@@ -0,0 +1,102 @@
+
+Event creation
+
+USER DICT(Master) UTIL SUMA
+================================================================================
+CREATE_EVENT_REQ::create
+-------------------------->
+ - Get ID
+ CREATE_SUBID
+ ----------------------------------------------->
+ <-----------------------------------------------
+ - insert into system table
+ UTIL_PREPARE::insert
+ ------------------------>
+ <------------------------
+ UTIL_EXECUTE
+ ------------------------>
+ <------------------------
+CREATE_EVENT_CONF
+<--------------------------
+
+
+Event dropping
+
+USER DICT(Master) (Slaves) UTIL SUMA
+================================================================================
+DROP_EVENT_REQ
+-------------------------->
+ - read from system table
+ UTIL_PREPARE::read
+ ------------------------------------>
+ <------------------------------------
+ UTIL_EXECUTE
+ ------------------------------------>
+ <------------------------------------
+ SUB_REMOVE_REQ
+ -------------------->
+ SUB_REMOVE
+ ------------------------------>
+ <------------------------------
+ SUB_REMOVE_CONF
+ <--------------------
+ - remove from system table
+ UTIL_PREPARE::delete
+ ------------------------------------>
+ <------------------------------------
+ UTIL_EXECUTE
+ ------------------------------------>
+ <------------------------------------
+DROP_EVENT_CONF
+<--------------------------
+
+
+
+create NdbEventOperation
+
+USER DICT(Master) (Slaves) UTIL
+=======================================================================
+CREATE_EVENT_REQ::get
+-------------------------->
+ - read from system table
+ UTIL_PREPARE::read
+ ---------------------------------------->
+ <----------------------------------------
+ UTIL_EXECUTE
+ ---------------------------------------->
+ <----------------------------------------
+ SUMA
+ CREATE_EVENT_REQ::after_get ======
+ ---------------------->
+ SUB_CREATE
+ ------------------>
+ <------------------
+ SUB_SYNC
+ ------------------>
+ <------------------
+ CREATE_EVENT_CONF
+ <----------------------
+CREATE_EVENT_CONF
+<-------------------------
+
+
+
+USER DICT(Master) (Slaves) SUMA
+=======================================================================
+SUB_START_REQ
+-------------------------->
+ SUB_START_REQ
+ ---------------------->
+ SUB_START
+ ------------------>
+ <------------------
+ SUB_START_CONF
+ <----------------------
+SUB_START_CONF
+<-------------------------
+
+
+SUB_STOP analogous to SUB_STOP
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Makefile.am b/storage/ndb/src/kernel/blocks/dbdict/Makefile.am
new file mode 100644
index 00000000000..9a0d68f8148
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Makefile.am
@@ -0,0 +1,25 @@
+#SUBDIRS = printSchemafile
+
+noinst_LIBRARIES = libdbdict.a
+
+libdbdict_a_SOURCES = Dbdict.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbdict.dsp
+
+libdbdict.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbdict_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Master_AddTable.sfl b/storage/ndb/src/kernel/blocks/dbdict/Master_AddTable.sfl
new file mode 100644
index 00000000000..1bcec156ef7
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Master_AddTable.sfl
@@ -0,0 +1,751 @@
+// ---------------------------------------------------------------------------
+// This file contains a signal log trace for DBDICT at the master for a
+// create table. Another file contains the signal log for the participant
+// node. Master node is 2, participant node 4 and api node is 3.
+//
+
+// ---------------------------------------------------------------------------
+// First arrives the table description in a number of DICTTABINFO signals.
+// These have a header of 5 words (see DictTabInfo.hpp for details) and
+// upto 20 words of property data per signal. The property data is packed
+// by the SimpleProperties class.
+// ---------------------------------------------------------------------------
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57069 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 0 "API", s.proc: 3, s.sigId: 940284 length: 25 trace: 0
+ H'00010003 H'00047700 H'00000001 H'00000042 H'00000000 H'4e444250 H'524f5053
+ H'00010000 H'00000000 H'1c0a1203 H'524f4c46 H'00020001 H'0000000a H'56504e5f
+ H'55534552 H'53000000 H'0001000a H'0000004b H'000203e8 H'00000007 H'56504e5f
+ H'49440000 H'000103ee H'00000001 H'000203e8
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57069 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 0 "API", s.proc: 3, s.sigId: 940284 length: 25 trace: 0
+ H'00010003 H'00047700 H'00000001 H'00000042 H'00000014 H'00000007 H'56504e5f
+ H'4e420000 H'000103ee H'00000001 H'000203e8 H'0000000d H'44495245 H'43544f52
+ H'595f4e42 H'00000000 H'000103eb H'00000003 H'000103ed H'0000000a H'000103ec
+ H'00000002 H'000203e8 H'00000010 H'4c415354
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57069 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 0 "API", s.proc: 3, s.sigId: 940284 length: 25 trace: 0
+ H'00010003 H'00047700 H'00000001 H'00000042 H'00000028 H'5f43414c H'4c5f5041
+ H'52545900 H'000103eb H'00000003 H'000103ed H'0000000a H'000103ec H'00000002
+ H'000203e8 H'00000006 H'44455343 H'52000000 H'000103eb H'00000003 H'000103ed
+ H'00000064 H'000103ec H'00000002 H'00010005
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57069 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 0 "API", s.proc: 3, s.sigId: 940284 length: 11 trace: 0
+ H'00010003 H'00047700 H'00000001 H'00000042 H'0000003c H'00000002 H'00010006
+ H'00000005 H'0001000c H'00000002 H'0000ffff
+
+// ---------------------------------------------------------------------------
+// Send DICT_SCHEMAREQ to all nodes including ourselves to write the state
+// ADD_STARTED in the schema file for the new table.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57069 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000000 H'00000000 H'00000001
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57069 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000000 H'00000000 H'00000001
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57069 gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57077 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000000 H'00000000 H'00000001
+
+// ---------------------------------------------------------------------------
+// Write both schema files with new state of table added.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57069 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050100
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57081 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57082 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 99
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57081 length: 8 trace: 0
+ FilePointer: 99
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57090 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57091 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57090 length: 4 trace: 0
+ FilePointer: 99
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57099 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57100 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57099 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050200
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57111 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57112 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 100
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57111 length: 8 trace: 0
+ FilePointer: 100
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57123 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57124 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57123 length: 4 trace: 0
+ FilePointer: 100
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57133 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 1 trace: 0
+ H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57135 length: 1 trace: 0
+ H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, s.sigId: 46718 length: 1 trace: 0
+ H'00000004
+
+// ---------------------------------------------------------------------------
+// Pack Table description into pages in DICT using SimpleProperties class.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 3 trace: 0
+ H'00000001 H'00000002 H'00000000
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57140 length: 3 trace: 0
+ H'00000001 H'00000002 H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57141 length: 2 trace: 0
+ H'00000002 H'00000002
+
+// ---------------------------------------------------------------------------
+// Send the table description over to the other NDB nodes.
+// A CONTINUEB is sent for each signal sent to avoid overloading the
+// transporters.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000000 H'4e444250 H'524f5053
+ H'00002000 H'0000001c H'1c0a1203 H'524f4c46 H'00020001 H'0000000a H'56504e5f
+ H'55534552 H'53000000 H'0001000a H'0000004b H'000203e8 H'00000007 H'56504e5f
+ H'49440000 H'1cc03924 H'00000001 H'000203e8
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57142 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000014 H'00000007 H'56504e5f
+ H'4e420000 H'000103ee H'00000001 H'000203e8 H'0000000d H'44495245 H'43544f52
+ H'595f4e42 H'00000000 H'000103eb H'00000003 H'524f4c46 H'00020001 H'0000000a
+ H'56504e5f H'55534552 H'53000010 H'00010002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57143 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000028 H'00000002 H'00010011
+ H'00000003 H'00010003 H'00000001 H'00010005 H'00000002 H'00010006 H'00000005
+ H'0001000a H'0000004b H'0001000c H'00000002 H'000203e8 H'00000007 H'56504e5f
+ H'49440064 H'000103e9 H'00000000 H'000103ee
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57144 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'0000003c H'00000001 H'000203e8
+ H'00000007 H'56504e5f H'4e420002 H'000103e9 H'00000001 H'000103ee H'00000001
+ H'000203e8 H'0000000d H'44495245 H'43544f52 H'595f4e42 H'00000000 H'000103e9
+ H'00000002 H'000103eb H'00000003 H'000103ec
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57145 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000050 H'00000002 H'000103ed
+ H'0000000a H'000203e8 H'00000010 H'4c415354 H'5f43414c H'4c5f5041 H'52545900
+ H'000103e9 H'00000003 H'000103eb H'00000003 H'000103ec H'00000002 H'000103ed
+ H'0000000a H'000203e8 H'00000006 H'44455343
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57132 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57146 length: 2 trace: 0
+ H'00000002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 15 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000064 H'52000000 H'000103e9
+ H'00000004 H'000103eb H'00000003 H'000103ec H'00000002 H'000103ed H'00000064
+ H'0000ffff
+
+// ---------------------------------------------------------------------------
+// In parallel with sending the table description to other nodes we will also
+// write the table description to our local file system.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57132 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'00000002 H'ffffffff H'00000001 H'010401ff
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57165 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57166 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 101
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57165 length: 8 trace: 0
+ FilePointer: 101
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000000, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57177 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57178 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57177 length: 4 trace: 0
+ FilePointer: 101
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57186 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57187 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57186 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'00000002 H'ffffffff H'00000001 H'010402ff
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57195 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57196 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 102
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57195 length: 8 trace: 0
+ FilePointer: 102
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000000, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57204 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57205 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57204 length: 4 trace: 0
+ FilePointer: 102
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57218 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57219 length: 1 trace: 0
+ UserPointer: H'00000000
+
+// ---------------------------------------------------------------------------
+// Completed writing to our file system the table description.
+// ---------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57229 gsn: 24 "DICTTABCONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, s.sigId: 46803 length: 2 trace: 0
+ H'00000002 H'00000004
+
+// ---------------------------------------------------------------------------
+// Also the participant have completed writing the table description to file.
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Write the state UPDATE_PAGE_COUNT to schema file for the new table.
+// This also contains the number of pages used for the table description.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57229 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'00000000 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57229 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'00000000 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57229 gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57234 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'00000000 H'00000002
+
+// ---------------------------------------------------------------------------
+// Write schema file to disk
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57229 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050100
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57238 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57239 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 103
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57238 length: 8 trace: 0
+ FilePointer: 103
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57247 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57248 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57247 length: 4 trace: 0
+ FilePointer: 103
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57257 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57258 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57257 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050200
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57267 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57268 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 104
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57267 length: 8 trace: 0
+ FilePointer: 104
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57279 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57283 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57279 length: 4 trace: 0
+ FilePointer: 104
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57290 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 57291 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57290 length: 1 trace: 0
+ H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57290 gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57293 length: 1 trace: 0
+ H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57299 gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, s.sigId: 46860 length: 1 trace: 0
+ H'00000004
+
+// ---------------------------------------------------------------------------
+// All schema files in the system have been updated.
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Now control is given to DIH for adding the fragments needed by this table.
+// We first seize a record in DIH and then we send the add table request with
+// the needed table parameters.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 2, gsn: 238 "DISEIZEREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57299 length: 2 trace: 0
+ H'00000000 H'00fa0002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57299 gsn: 236 "DISEIZECONF" prio: 1
+s.bn: 246 "DBDIH", s.proc: 2, s.sigId: 57304 length: 2 trace: 0
+ H'00000000 H'00000210
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 2, gsn: 187 "DIADDTABREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57299 length: 6 trace: 0
+ H'00000210 H'00000002 H'00000000 H'00000006 H'00000000 H'00000001
+
+// ---------------------------------------------------------------------------
+// DIH requests us to add a certain fragment replica.
+// ---------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57400 gsn: 195 "DICTFRAGSREQ" prio: 1
+s.bn: 246 "DBDIH", s.proc: 2, s.sigId: 57418 length: 7 trace: 0
+ H'00000000 H'00000000 H'00000000 H'00000002 H'00150040 H'00000001 H'00000002
+
+// ---------------------------------------------------------------------------
+// We add the fragment by contacting LQH through sending a LQHFRAGREQ and
+// a number of LQHADDATTREQ (in this case only one since not more than 8
+// attributes).
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 247 "DBLQH", r.proc: 2, gsn: 313 "LQHFRAGREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57400 length: 17 trace: 0
+ H'00000000 H'00fa0002 H'00000000 H'00000000 H'00000002 H'00000001 H'00000050
+ H'0000004b H'00000006 H'00000001 H'00000000 H'00000005 H'00000000 H'00000000
+ H'00000001 H'00000002 H'00000000
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57400 gsn: 311 "LQHFRAGCONF" prio: 1
+s.bn: 247 "DBLQH", s.proc: 2, s.sigId: 57428 length: 2 trace: 0
+ H'00000000 H'00000000
+---- Send ----- Signal ----------------
+r.bn: 247 "DBLQH", r.proc: 2, gsn: 310 "LQHADDATTREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57400 length: 12 trace: 0
+ H'00000000 H'00000005 H'00000000 H'00012255 H'00000001 H'00012255 H'00000002
+ H'000a2236 H'00000003 H'000a2236 H'00000004 H'00642236
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57400 gsn: 308 "LQHADDATTCONF" prio: 1
+s.bn: 247 "DBLQH", s.proc: 2, s.sigId: 57450 length: 1 trace: 0
+ H'00000000
+
+// ---------------------------------------------------------------------------
+// When we have completed adding the fragment we send DINEXTNODEREQ (should
+// change name to DICTFRAGSCONF) to DIH indicate we have completed the task.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 2, gsn: 231 "DINEXTNODEREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57400 length: 4 trace: 0
+ H'00000210 H'00000000 H'00000001 H'00000000
+
+// ---------------------------------------------------------------------------
+// We continue by performing the same task again for the next fragment replica.
+// We skip this from this log since they contain no more interesting stuff.
+// ---------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 57618 gsn: 185 "DIADDTABCONF" prio: 1
+s.bn: 246 "DBDIH", s.proc: 2, s.sigId: 57655 length: 2 trace: 0
+ H'00000000 H'00000002
+
+// ---------------------------------------------------------------------------
+// Now that we have added all fragments DIH gives back control to DICT by
+// sending DIADDTABCONF.
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// It is now time to decide which global checkpoint this table will be born.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 2, gsn: 499 "WAIT_GCP_REQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 57618 length: 3 trace: 0
+ H'00fa0002 H'00000000 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58288 gsn: 501 "WAIT_GCP_CONF" prio: 1
+s.bn: 246 "DBDIH", s.proc: 2, s.sigId: 58296 length: 2 trace: 0
+ H'00000000 H'0000000c
+
+// ---------------------------------------------------------------------------
+// We can update all schema files in the system with this global checkpoint
+// number. We are certain that no transaction will be performed on the table
+// before this global checkpoint.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58288 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'0000000c H'00000003
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58288 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'0000000c H'00000003
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58288 gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 58298 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'0000000c H'00000003
+
+// ---------------------------------------------------------------------------
+// Write schema files as usual when updating schema file state.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58288 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050100
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58304 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 58305 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 117
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58304 length: 8 trace: 0
+ FilePointer: 117
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58315 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 58316 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58315 length: 4 trace: 0
+ FilePointer: 117
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58326 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 58327 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58326 length: 7 trace: 0
+ UserReference: H'00fa0002, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050200
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58339 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 58340 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 118
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58339 length: 8 trace: 0
+ FilePointer: 118
+ UserReference: H'00fa0002, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58348 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 58349 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 2, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58348 length: 4 trace: 0
+ FilePointer: 118
+ UserReference: H'00fa0002, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 2, s.sigId: 58360 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 1 trace: 0
+ H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 58364 length: 1 trace: 0
+ H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, s.sigId: 47846 length: 1 trace: 0
+ H'00000004
+
+// ---------------------------------------------------------------------------
+// Commit the table for usage in DIH and LQH in all nodes.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 247 "DBLQH", r.proc: 2, gsn: 398 "TAB_COMMITREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 3 trace: 0
+ H'00000000 H'00fa0002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 2, gsn: 398 "TAB_COMMITREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 3 trace: 0
+ H'00000001 H'00fa0002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 247 "DBLQH", r.proc: 4, gsn: 398 "TAB_COMMITREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 3 trace: 0
+ H'00000000 H'00fa0002 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 4, gsn: 398 "TAB_COMMITREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 3 trace: 0
+ H'00000001 H'00fa0002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 396 "TAB_COMMITCONF" prio: 1
+s.bn: 247 "DBLQH", s.proc: 2, s.sigId: 58370 length: 3 trace: 0
+ H'00000000 H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 396 "TAB_COMMITCONF" prio: 1
+s.bn: 246 "DBDIH", s.proc: 2, s.sigId: 58371 length: 3 trace: 0
+ H'00000001 H'00000002 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 396 "TAB_COMMITCONF" prio: 1
+s.bn: 247 "DBLQH", s.proc: 4, s.sigId: 47846 length: 3 trace: 0
+ H'00000000 H'00000004 H'00000002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 396 "TAB_COMMITCONF" prio: 1
+s.bn: 246 "DBDIH", s.proc: 4, s.sigId: 47846 length: 3 trace: 0
+ H'00000001 H'00000004 H'00000002
+
+// ---------------------------------------------------------------------------
+// Finally also open the table for usage from TC in all nodes.
+// After this signal is received in TC it is ok to execute transactions on
+// this new empty table.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 245 "DBTC", r.proc: 2, gsn: 404 "TC_SCHVERREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 5 trace: 0
+ H'00000002 H'00000001 H'00000001 H'00fa0002 H'00000000
+---- Send ----- Signal ----------------
+r.bn: 245 "DBTC", r.proc: 4, gsn: 404 "TC_SCHVERREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 5 trace: 0
+ H'00000002 H'00000001 H'00000001 H'00fa0002 H'00000000
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 403 "TC_SCHVERCONF" prio: 1
+s.bn: 245 "DBTC", s.proc: 2, s.sigId: 58376 length: 2 trace: 0
+ H'00000002 H'00000000
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 403 "TC_SCHVERCONF" prio: 1
+s.bn: 245 "DBTC", s.proc: 4, s.sigId: 47846 length: 2 trace: 0
+ H'00000002 H'00000001
+
+// ---------------------------------------------------------------------------
+// Unblock dictionary to allow for another add table.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 444 "UNBLO_DICTREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 1 trace: 0
+ H'00fa0002
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 444 "UNBLO_DICTREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 1 trace: 0
+ H'00fa0002
+
+// ---------------------------------------------------------------------------
+// Send the confirmation to the requesting application process.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 1 "API", r.proc: 3, gsn: 24 "DICTTABCONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 3 trace: 0
+ H'00047700 H'00000002 H'00000001
+
+// ---------------------------------------------------------------------------
+// Also release the connection in DIH that was previously established.
+// ---------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 246 "DBDIH", r.proc: 2, gsn: 234 "DIRELEASEREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, sigId: 58359 length: 3 trace: 0
+ H'00000210 H'00000000 H'00fa0002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 444 "UNBLO_DICTREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 58378 length: 1 trace: 0
+ H'00fa0002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, sigId: 58359 gsn: 232 "DIRELEASECONF" prio: 1
+s.bn: 246 "DBDIH", s.proc: 2, s.sigId: 58380 length: 1 trace: 0
+ H'00000000
+
+// ---------------------------------------------------------------------------
+// Now all actions regarding this add table have completed.
+// ---------------------------------------------------------------------------
diff --git a/storage/ndb/src/kernel/blocks/dbdict/SchemaFile.hpp b/storage/ndb/src/kernel/blocks/dbdict/SchemaFile.hpp
new file mode 100644
index 00000000000..7c3223d3d14
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/SchemaFile.hpp
@@ -0,0 +1,57 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBDICT_SCHEMA_FILE_HPP
+#define DBDICT_SCHEMA_FILE_HPP
+
+#include <ndb_types.h>
+#include <string.h>
+
+struct SchemaFile {
+ char Magic[8];
+ Uint32 ByteOrder;
+ Uint32 NdbVersion;
+ Uint32 FileSize; // In bytes
+ Uint32 Unused;
+
+ Uint32 CheckSum;
+
+ enum TableState {
+ INIT = 0,
+ ADD_STARTED = 1,
+ TABLE_ADD_COMMITTED = 2,
+ DROP_TABLE_STARTED = 3,
+ DROP_TABLE_COMMITTED = 4,
+ ALTER_TABLE_COMMITTED = 5
+ };
+
+ struct TableEntry {
+ Uint32 m_tableState;
+ Uint32 m_tableVersion;
+ Uint32 m_tableType;
+ Uint32 m_noOfPages;
+ Uint32 m_gcp;
+
+ bool operator==(const TableEntry& o) const {
+ return memcmp(this, &o, sizeof(* this))== 0;
+ }
+ };
+
+ Uint32 NoOfTableEntries;
+ TableEntry TableEntries[1];
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Slave_AddTable.sfl b/storage/ndb/src/kernel/blocks/dbdict/Slave_AddTable.sfl
new file mode 100644
index 00000000000..8740be9595d
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/Slave_AddTable.sfl
@@ -0,0 +1,416 @@
+// ---------------------------------------------------------------------------
+// This file contains a signal log trace for DBDICT at the participant for a
+// add table. Another file contains the signal log for the master
+// node. Master node is 2, participant node 4 and api node is 3.
+//
+
+// ---------------------------------------------------------------------------
+//--------------------------------------------------------------------------
+// Master requests us to save a new state of the table in the schema file
+// == ADD_STARTED
+//--------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46661 gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57069 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000000 H'00000000 H'00000001
+
+//--------------------------------------------------------------------------
+// Write the new state to the schema files.
+//--------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46661 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050100
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46669 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46670 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 99
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46669 length: 8 trace: 0
+ FilePointer: 99
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46679 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46680 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46679 length: 4 trace: 0
+ FilePointer: 99
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46690 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46691 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46690 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050200
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46700 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46701 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 100
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46700 length: 8 trace: 0
+ FilePointer: 100
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46709 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46710 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46709 length: 4 trace: 0
+ FilePointer: 100
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46719 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46718 length: 1 trace: 0
+ H'00000004
+
+//--------------------------------------------------------------------------
+// We receive the table description from the master node.
+// We set the data in the DICT block. (table and attribute records).
+//--------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000000 H'4e444250 H'524f5053
+ H'00002000 H'0000001c H'1c0a1203 H'524f4c46 H'00020001 H'0000000a H'56504e5f
+ H'55534552 H'53000000 H'0001000a H'0000004b H'000203e8 H'00000007 H'56504e5f
+ H'49440000 H'1cc03924 H'00000001 H'000203e8
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000014 H'00000007 H'56504e5f
+ H'4e420000 H'000103ee H'00000001 H'000203e8 H'0000000d H'44495245 H'43544f52
+ H'595f4e42 H'00000000 H'000103eb H'00000003 H'524f4c46 H'00020001 H'0000000a
+ H'56504e5f H'55534552 H'53000010 H'00010002
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000028 H'00000002 H'00010011
+ H'00000003 H'00010003 H'00000001 H'00010005 H'00000002 H'00010006 H'00000005
+ H'0001000a H'0000004b H'0001000c H'00000002 H'000203e8 H'00000007 H'56504e5f
+ H'49440064 H'000103e9 H'00000000 H'000103ee
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'0000003c H'00000001 H'000203e8
+ H'00000007 H'56504e5f H'4e420002 H'000103e9 H'00000001 H'000103ee H'00000001
+ H'000203e8 H'0000000d H'44495245 H'43544f52 H'595f4e42 H'00000000 H'000103e9
+ H'00000002 H'000103eb H'00000003 H'000103ec
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57132 length: 25 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000050 H'00000002 H'000103ed
+ H'0000000a H'000203e8 H'00000010 H'4c415354 H'5f43414c H'4c5f5041 H'52545900
+ H'000103e9 H'00000003 H'000103eb H'00000003 H'000103ec H'00000002 H'000103ed
+ H'0000000a H'000203e8 H'00000006 H'44455343
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 204 "DICTTABINFO" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57132 length: 15 trace: 0
+ H'00fa0002 H'00000000 H'00000002 H'0000006e H'00000064 H'52000000 H'000103e9
+ H'00000004 H'000103eb H'00000003 H'000103ec H'00000002 H'000103ed H'00000064
+ H'0000ffff
+
+//--------------------------------------------------------------------------
+// Pack the table description into pages.
+//--------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46718 length: 3 trace: 0
+ H'00000001 H'00000002 H'00000000
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46718 gsn: 164 "CONTINUEB" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, s.sigId: 46730 length: 3 trace: 0
+ H'00000001 H'00000002 H'00000000
+
+//--------------------------------------------------------------------------
+// Write the pages of the table description to disk.
+//--------------------------------------------------------------------------
+
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46718 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'00000002 H'ffffffff H'00000001 H'010401ff
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46748 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46749 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 101
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46748 length: 8 trace: 0
+ FilePointer: 101
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000000, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46757 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46758 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46757 length: 4 trace: 0
+ FilePointer: 101
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46766 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46767 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46766 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'00000002 H'ffffffff H'00000001 H'010402ff
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46783 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46784 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 102
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46783 length: 8 trace: 0
+ FilePointer: 102
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000000, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46794 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46795 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46794 length: 4 trace: 0
+ FilePointer: 102
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46803 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46804 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 24 "DICTTABCONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46803 length: 2 trace: 0
+ H'00000002 H'00000004
+
+//--------------------------------------------------------------------------
+// Update schema file ín memory and on disk to UPDATE_PAGE_COUNT.
+//--------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46803 gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 57229 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'00000000 H'00000002
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46803 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050100
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46813 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46814 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 103
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46813 length: 8 trace: 0
+ FilePointer: 103
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46823 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46824 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46823 length: 4 trace: 0
+ FilePointer: 103
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46833 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46834 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46833 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050200
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46842 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46843 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 104
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46842 length: 8 trace: 0
+ FilePointer: 104
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46851 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46852 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46851 length: 4 trace: 0
+ FilePointer: 104
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 46860 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 46861 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 46860 length: 1 trace: 0
+ H'00000004
+
+//--------------------------------------------------------------------------
+// Update schema file with information about the starting global checkpoint
+// identity.
+//--------------------------------------------------------------------------
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47782 gsn: 132 "DICT_SCHEMAREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 58288 length: 7 trace: 0
+ H'00010003 H'00047700 H'00000002 H'00000001 H'00000001 H'0000000c H'00000003
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47782 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050100
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47793 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 47794 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 117
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47793 length: 8 trace: 0
+ FilePointer: 117
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47804 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 47805 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47804 length: 4 trace: 0
+ FilePointer: 117
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47817 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 47818 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 261 "FSOPENREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47817 length: 7 trace: 0
+ UserReference: H'00fa0004, userPointer: H'00000000
+ FileNumber[1-4]: H'ffffffff H'ffffffff H'ffffffff H'01050200
+ FileFlags: H'00000311 Open write only, Create new file, Truncate existing file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47826 gsn: 259 "FSOPENCONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 47827 length: 3 trace: 0
+ UserPointer: H'00000000
+ FilePointer: 118
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 272 "FSWRITEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47826 length: 8 trace: 0
+ FilePointer: 118
+ UserReference: H'00fa0004, UserPointer: H'00000000
+ Operation flag: H'00000011, Sync, Format=Array of pages
+ varIndex: 1
+ numberOfPages: 1
+ pageData: H'00000008, H'00000000
+
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47836 gsn: 270 "FSWRITECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 47837 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 253 "NDBFS", r.proc: 4, gsn: 257 "FSCLOSEREQ" prio: 0
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47836 length: 4 trace: 0
+ FilePointer: 118
+ UserReference: H'00fa0004, userPointer: H'00000000
+ Flags: H'00000000, Don't remove file
+---- Received - Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47846 gsn: 255 "FSCLOSECONF" prio: 1
+s.bn: 253 "NDBFS", s.proc: 4, s.sigId: 47847 length: 1 trace: 0
+ UserPointer: H'00000000
+---- Send ----- Signal ----------------
+r.bn: 250 "DBDICT", r.proc: 2, gsn: 133 "DICT_SCHEMACONF" prio: 1
+s.bn: 250 "DBDICT", s.proc: 4, sigId: 47846 length: 1 trace: 0
+ H'00000004
+---- Received - Signal ----------------
+
+//--------------------------------------------------------------------------
+// Finally unblock the DICT block so that it can handle add table as master
+// if it becomes master in the future.
+//--------------------------------------------------------------------------
+
+r.bn: 250 "DBDICT", r.proc: 4, sigId: 47846 gsn: 444 "UNBLO_DICTREQ" prio: 1
+s.bn: 250 "DBDICT", s.proc: 2, s.sigId: 58359 length: 1 trace: 0
+ H'00fa0002
+
+//--------------------------------------------------------------------------
+// We completed the add table operation.
+//--------------------------------------------------------------------------
+
diff --git a/storage/ndb/src/kernel/blocks/dbdict/printSchemaFile.cpp b/storage/ndb/src/kernel/blocks/dbdict/printSchemaFile.cpp
new file mode 100644
index 00000000000..0ba52878b7c
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdict/printSchemaFile.cpp
@@ -0,0 +1,112 @@
+#if 0
+make -f Makefile -f - printSchemaFile <<'_eof_'
+printSchemaFile: printSchemaFile.cpp
+ $(CXXCOMPILE) -o $@ $@.cpp -L../../../common/util/.libs -lgeneral
+_eof_
+exit $?
+#endif
+
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <SchemaFile.hpp>
+
+void
+usage(const char * prg){
+ ndbout << "Usage " << prg
+ << " P0.SchemaLog" << endl;
+}
+
+void
+fill(const char * buf, int mod){
+ int len = strlen(buf)+1;
+ ndbout << buf << " ";
+ while((len % mod) != 0){
+ ndbout << " ";
+ len++;
+ }
+}
+
+void
+print(const char * filename, const SchemaFile * file){
+ ndbout << "----- Schemafile: " << filename << " -----" << endl;
+ ndbout_c("Magic: %.*s ByteOrder: %.8x NdbVersion: %d FileSize: %d",
+ sizeof(file->Magic), file->Magic,
+ file->ByteOrder,
+ file->NdbVersion,
+ file->FileSize);
+
+ for(Uint32 i = 0; i<file->NoOfTableEntries; i++){
+ SchemaFile::TableEntry te = file->TableEntries[i];
+ if(te.m_tableState != SchemaFile::INIT){
+ ndbout << "Table " << i << ": State = " << te.m_tableState
+ << " version = " << te.m_tableVersion
+ << " type = " << te.m_tableType
+ << " noOfPages = " << te.m_noOfPages
+ << " gcp: " << te.m_gcp << endl;
+ }
+ }
+}
+
+NDB_COMMAND(printSchemafile,
+ "printSchemafile", "printSchemafile", "Prints a schemafile", 16384){
+ if(argc < 2){
+ usage(argv[0]);
+ return 0;
+ }
+
+ const char * filename = argv[1];
+
+ struct stat sbuf;
+ const int res = stat(filename, &sbuf);
+ if(res != 0){
+ ndbout << "Could not find file: \"" << filename << "\"" << endl;
+ return 0;
+ }
+ const Uint32 bytes = sbuf.st_size;
+
+ Uint32 * buf = new Uint32[bytes/4+1];
+
+ FILE * f = fopen(filename, "rb");
+ if(f == 0){
+ ndbout << "Failed to open file" << endl;
+ delete [] buf;
+ return 0;
+ }
+ Uint32 sz = fread(buf, 1, bytes, f);
+ fclose(f);
+ if(sz != bytes){
+ ndbout << "Failure while reading file" << endl;
+ delete [] buf;
+ return 0;
+ }
+
+ print(filename, (SchemaFile *)&buf[0]);
+
+ Uint32 chk = 0, i;
+ for (i = 0; i < bytes/4; i++)
+ chk ^= buf[i];
+ if (chk != 0)
+ ndbout << "Invalid checksum!" << endl;
+
+ delete [] buf;
+ return 0;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp b/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
new file mode 100644
index 00000000000..ee67bf47d7b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
@@ -0,0 +1,1603 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBDIH_H
+#define DBDIH_H
+
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+#include "Sysfile.hpp"
+#include <ArrayList.hpp>
+#include <SignalCounter.hpp>
+
+#include <signaldata/MasterLCP.hpp>
+#include <signaldata/CopyGCIReq.hpp>
+#include <blocks/mutexes.hpp>
+
+#ifdef DBDIH_C
+
+/*###################*/
+/* FILE SYSTEM FLAGS */
+/*###################*/
+#define ZLIST_OF_PAIRS 0
+#define ZLIST_OF_PAIRS_SYNCH 16
+#define ZOPEN_READ_WRITE 2
+#define ZCREATE_READ_WRITE 0x302
+#define ZCLOSE_NO_DELETE 0
+#define ZCLOSE_DELETE 1
+
+/*###############*/
+/* NODE STATES */
+/*###############*/
+#define ZIDLE 0
+#define ZACTIVE 1
+
+/*#########*/
+/* GENERAL */
+/*#########*/
+#define ZVAR_NO_WORD 1
+#define ZVAR_NO_CRESTART_INFO 20
+#define ZVAR_NO_CRESTART_INFO_TO_FILE 21
+#define ZVALID 1
+#define ZINVALID 2
+
+/*###############*/
+/* ERROR CODES */
+/*###############*/
+// ------------------------------------------
+// Error Codes for Transactions (None sofar)
+// ------------------------------------------
+
+// --------------------------------------
+// Error Codes for Add Table
+// --------------------------------------
+#define ZREPLERROR1 306
+#define ZNOTIMPLEMENTED 307
+#define ZTABLEINSTALLED 310
+// --------------------------------------
+// Error Codes for Scan Table
+// --------------------------------------
+#define ZERRONOUSSTATE 308
+
+// --------------------------------------
+// Crash Codes
+// --------------------------------------
+#define ZCOULD_NOT_OCCUR_ERROR 300
+#define ZNOT_MASTER_ERROR 301
+#define ZWRONG_FAILURE_NUMBER_ERROR 302
+#define ZWRONG_START_NODE_ERROR 303
+#define ZNO_REPLICA_FOUND_ERROR 304
+#define ZNODE_ALREADY_STARTING_ERROR 305
+#define ZNODE_START_DISALLOWED_ERROR 309
+
+// --------------------------------------
+// Codes from LQH
+// --------------------------------------
+#define ZNODE_FAILURE_ERROR 400
+
+
+/*#########*/
+/* PHASES */
+/*#########*/
+#define ZNDB_SPH1 1
+#define ZNDB_SPH2 2
+#define ZNDB_SPH3 3
+#define ZNDB_SPH4 4
+#define ZNDB_SPH5 5
+#define ZNDB_SPH6 6
+#define ZNDB_SPH7 7
+#define ZNDB_SPH8 8
+/*#########*/
+/* SIZES */
+/*#########*/
+#define ZPAGEREC 100
+#define ZCREATE_REPLICA_FILE_SIZE 4
+#define ZPROXY_MASTER_FILE_SIZE 10
+#define ZPROXY_FILE_SIZE 10
+#endif
+
+class Dbdih: public SimulatedBlock {
+public:
+
+ // Records
+
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
+ * THE API CONNECT RECORD IS THE SAME RECORD POINTER AS USED IN THE TC BLOCK
+ *
+ * IT KEEPS TRACK OF ALL THE OPERATIONS CONNECTED TO THIS TRANSACTION.
+ * IT IS LINKED INTO A QUEUE IN CASE THE GLOBAL CHECKPOINT IS CURRENTLY
+ * ONGOING */
+ struct ApiConnectRecord {
+ Uint32 apiGci;
+ Uint32 nextApi;
+ };
+ typedef Ptr<ApiConnectRecord> ApiConnectRecordPtr;
+
+ /*############## CONNECT_RECORD ##############*/
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* THE CONNECT RECORD IS CREATED WHEN A TRANSACTION HAS TO START. IT KEEPS
+ ALL INTERMEDIATE INFORMATION NECESSARY FOR THE TRANSACTION FROM THE
+ DISTRIBUTED MANAGER. THE RECORD KEEPS INFORMATION ABOUT THE
+ OPERATIONS THAT HAVE TO BE CARRIED OUT BY THE TRANSACTION AND
+ ALSO THE TRAIL OF NODES FOR EACH OPERATION IN THE THE
+ TRANSACTION.
+ */
+ struct ConnectRecord {
+ enum ConnectState {
+ INUSE = 0,
+ FREE = 1,
+ STARTED = 2
+ };
+ Uint32 nodes[MAX_REPLICAS];
+ ConnectState connectState;
+ Uint32 nfConnect;
+ Uint32 table;
+ Uint32 userpointer;
+ BlockReference userblockref;
+ };
+ typedef Ptr<ConnectRecord> ConnectRecordPtr;
+
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* THESE RECORDS ARE USED WHEN CREATING REPLICAS DURING SYSTEM */
+ /* RESTART. I NEED A COMPLEX DATA STRUCTURE DESCRIBING THE REPLICAS */
+ /* I WILL TRY TO CREATE FOR EACH FRAGMENT. */
+ /* */
+ /* I STORE A REFERENCE TO THE FOUR POSSIBLE CREATE REPLICA RECORDS */
+ /* IN A COMMON STORED VARIABLE. I ALLOW A MAXIMUM OF 4 REPLICAS TO */
+ /* BE RESTARTED PER FRAGMENT. */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ struct CreateReplicaRecord {
+ Uint32 logStartGci[MAX_LOG_EXEC];
+ Uint32 logStopGci[MAX_LOG_EXEC];
+ Uint16 logNodeId[MAX_LOG_EXEC];
+ Uint32 createLcpId;
+
+ bool hotSpareUse;
+ Uint32 replicaRec;
+ Uint16 dataNodeId;
+ Uint16 lcpNo;
+ Uint16 noLogNodes;
+ };
+ typedef Ptr<CreateReplicaRecord> CreateReplicaRecordPtr;
+
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* THIS RECORD CONTAINS A FILE DESCRIPTION. THERE ARE TWO */
+ /* FILES PER TABLE TO RAISE SECURITY LEVEL AGAINST DISK CRASHES. */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ struct FileRecord {
+ enum FileStatus {
+ CLOSED = 0,
+ CRASHED = 1,
+ OPEN = 2
+ };
+ enum FileType {
+ TABLE_FILE = 0,
+ GCP_FILE = 1
+ };
+ enum ReqStatus {
+ IDLE = 0,
+ CREATING_GCP = 1,
+ OPENING_GCP = 2,
+ OPENING_COPY_GCI = 3,
+ WRITING_COPY_GCI = 4,
+ CREATING_COPY_GCI = 5,
+ OPENING_TABLE = 6,
+ READING_GCP = 7,
+ READING_TABLE = 8,
+ WRITE_INIT_GCP = 9,
+ TABLE_CREATE = 10,
+ TABLE_WRITE = 11,
+ TABLE_CLOSE = 12,
+ CLOSING_GCP = 13,
+ CLOSING_TABLE_CRASH = 14,
+ CLOSING_TABLE_SR = 15,
+ CLOSING_GCP_CRASH = 16,
+ TABLE_OPEN_FOR_DELETE = 17,
+ TABLE_CLOSE_DELETE = 18
+ };
+ Uint32 fileName[4];
+ Uint32 fileRef;
+ FileStatus fileStatus;
+ FileType fileType;
+ Uint32 nextFile;
+ ReqStatus reqStatus;
+ Uint32 tabRef;
+ };
+ typedef Ptr<FileRecord> FileRecordPtr;
+
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* THIS RECORD KEEPS THE STORAGE AND DECISIONS INFORMATION OF A FRAGMENT */
+ /* AND ITS REPLICAS. IF FRAGMENT HAS MORE THAN ONE BACK UP */
+ /* REPLICA THEN A LIST OF MORE NODES IS ATTACHED TO THIS RECORD. */
+ /* EACH RECORD IN MORE LIST HAS INFORMATION ABOUT ONE BACKUP. THIS RECORD */
+ /* ALSO HAVE THE STATUS OF THE FRAGMENT. */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* */
+ /* FRAGMENTSTORE RECORD ALIGNED TO BE 64 BYTES */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ struct Fragmentstore {
+ Uint16 activeNodes[MAX_REPLICAS];
+ Uint32 preferredPrimary;
+
+ Uint32 oldStoredReplicas; /* "DEAD" STORED REPLICAS */
+ Uint32 storedReplicas; /* "ALIVE" STORED REPLICAS */
+ Uint32 nextFragmentChunk;
+
+ Uint8 distributionKey;
+ Uint8 fragReplicas;
+ Uint8 noOldStoredReplicas; /* NUMBER OF "DEAD" STORED REPLICAS */
+ Uint8 noStoredReplicas; /* NUMBER OF "ALIVE" STORED REPLICAS*/
+ Uint8 noLcpReplicas; ///< No of replicas remaining to be LCP:ed
+ };
+ typedef Ptr<Fragmentstore> FragmentstorePtr;
+
+ /*########### PAGE RECORD ############*/
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* THIS RECORD KEEPS INFORMATION ABOUT NODE GROUPS. */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ struct NodeGroupRecord {
+ Uint32 nodesInGroup[MAX_REPLICAS + 1];
+ Uint32 nextReplicaNode;
+ Uint32 nodeCount;
+ bool activeTakeOver;
+ };
+ typedef Ptr<NodeGroupRecord> NodeGroupRecordPtr;
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* THIS RECORD KEEPS INFORMATION ABOUT NODES. */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ /* RECORD ALIGNED TO BE 64 BYTES. */
+ /*¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤*/
+ enum NodefailHandlingStep {
+ NF_REMOVE_NODE_FROM_TABLE = 1,
+ NF_GCP_TAKE_OVER = 2,
+ NF_LCP_TAKE_OVER = 4
+ };
+
+ struct NodeRecord {
+ NodeRecord();
+
+ enum NodeStatus {
+ NOT_IN_CLUSTER = 0,
+ ALIVE = 1,
+ STARTING = 2,
+ DIED_NOW = 3,
+ DYING = 4,
+ DEAD = 5
+ };
+
+ struct FragmentCheckpointInfo {
+ Uint32 tableId;
+ Uint32 fragId;
+ Uint32 replicaPtr;
+ };
+
+ enum GcpState {
+ READY = 0,
+ PREPARE_SENT = 1,
+ PREPARE_RECEIVED = 2,
+ COMMIT_SENT = 3,
+ NODE_FINISHED = 4,
+ SAVE_REQ_SENT = 5,
+ SAVE_RECEIVED = 6,
+ COPY_GCI_SENT = 7
+ };
+
+ GcpState gcpstate;
+ Sysfile::ActiveStatus activeStatus;
+
+ NodeStatus nodeStatus;
+ bool useInTransactions;
+ bool allowNodeStart;
+ bool copyCompleted;
+ bool m_inclDihLcp;
+
+ FragmentCheckpointInfo startedChkpt[2];
+ FragmentCheckpointInfo queuedChkpt[2];
+
+ Bitmask<1> m_nodefailSteps;
+ Uint32 activeTabptr;
+ Uint32 nextNode;
+ Uint32 nodeGroup;
+
+ SignalCounter m_NF_COMPLETE_REP;
+
+ Uint8 dbtcFailCompleted;
+ Uint8 dblqhFailCompleted;
+ Uint8 dbdihFailCompleted;
+ Uint8 dbdictFailCompleted;
+ Uint8 recNODE_FAILREP;
+
+ Uint8 noOfQueuedChkpt;
+ Uint8 noOfStartedChkpt;
+
+ MasterLCPConf::State lcpStateAtTakeOver;
+ };
+ typedef Ptr<NodeRecord> NodeRecordPtr;
+ /**********************************************************************/
+ /* THIS RECORD KEEPS THE INFORMATION ABOUT A TABLE AND ITS FRAGMENTS */
+ /**********************************************************************/
+ struct PageRecord {
+ Uint32 word[2048];
+ /* 8 KBYTE PAGE*/
+ Uint32 nextfreepage;
+ };
+ typedef Ptr<PageRecord> PageRecordPtr;
+
+ /************ REPLICA RECORD *************/
+ /**********************************************************************/
+ /* THIS RECORD KEEPS THE INFORMATION ABOUT A REPLICA OF A FRAGMENT */
+ /**********************************************************************/
+ struct ReplicaRecord {
+ /* -------------------------------------------------------------------- */
+ /* THE GLOBAL CHECKPOINT IDENTITY WHEN THIS REPLICA WAS CREATED. */
+ /* THERE IS ONE INDEX PER REPLICA. A REPLICA INDEX IS CREATED WHEN ANODE*/
+ /* CRASH OCCURS. */
+ /* -------------------------------------------------------------------- */
+ Uint32 createGci[8];
+ /* -------------------------------------------------------------------- */
+ /* THE LAST GLOBAL CHECKPOINT IDENTITY WHICH HAS BEEN SAVED ON DISK. */
+ /* THIS VARIABLE IS ONLY VALID FOR REPLICAS WHICH HAVE "DIED". A REPLICA*/
+ /* "DIES" EITHER WHEN THE NODE CRASHES THAT KEPT THE REPLICA OR BY BEING*/
+ /* STOPPED IN A CONTROLLED MANNER. */
+ /* THERE IS ONE INDEX PER REPLICA. A REPLICA INDEX IS CREATED WHEN ANODE*/
+ /* CRASH OCCURS. */
+ /* -------------------------------------------------------------------- */
+ Uint32 replicaLastGci[8];
+ /* -------------------------------------------------------------------- */
+ /* THE LOCAL CHECKPOINT IDENTITY OF A LOCAL CHECKPOINT. */
+ /* -------------------------------------------------------------------- */
+ Uint32 lcpId[MAX_LCP_STORED];
+ /* -------------------------------------------------------------------- */
+ /* THIS VARIABLE KEEPS TRACK OF THE MAXIMUM GLOBAL CHECKPOINT COMPLETED */
+ /* FOR EACH OF THE LOCAL CHECKPOINTS IN THIS FRAGMENT REPLICA. */
+ /* -------------------------------------------------------------------- */
+ Uint32 maxGciCompleted[MAX_LCP_STORED];
+ /* -------------------------------------------------------------------- */
+ /* THIS VARIABLE KEEPS TRACK OF THE MINIMUM GLOBAL CHECKPOINT STARTEDFOR*/
+ /* EACH OF THE LOCAL CHECKPOINTS IN THIS FRAGMENT REPLICA. */
+ /* -------------------------------------------------------------------- */
+ Uint32 maxGciStarted[MAX_LCP_STORED];
+ /* -------------------------------------------------------------------- */
+ /* THE GLOBAL CHECKPOINT IDENTITY WHEN THE TABLE WAS CREATED. */
+ /* -------------------------------------------------------------------- */
+ Uint32 initialGci;
+
+ /* -------------------------------------------------------------------- */
+ /* THE REFERENCE TO THE NEXT REPLICA. EITHER IT REFERS TO THE NEXT IN */
+ /* THE FREE LIST OR IT REFERS TO THE NEXT IN A LIST OF REPLICAS ON A */
+ /* FRAGMENT. */
+ /* -------------------------------------------------------------------- */
+ Uint32 nextReplica;
+
+ /* -------------------------------------------------------------------- */
+ /* THE NODE ID WHERE THIS REPLICA IS STORED. */
+ /* -------------------------------------------------------------------- */
+ Uint16 procNode;
+
+ /* -------------------------------------------------------------------- */
+ /* The last local checkpoint id started or queued on this replica. */
+ /* -------------------------------------------------------------------- */
+ Uint32 lcpIdStarted; // Started or queued
+
+ /* -------------------------------------------------------------------- */
+ /* THIS VARIABLE SPECIFIES WHAT THE STATUS OF THE LOCAL CHECKPOINT IS.IT*/
+ /* CAN EITHER BE VALID OR INVALID. AT CREATION OF A FRAGMENT REPLICA ALL*/
+ /* LCP'S ARE INVALID. ALSO IF IF INDEX >= NO_LCP THEN THELOCALCHECKPOINT*/
+ /* IS ALWAYS INVALID. IF THE LCP BEFORE THE NEXT_LCP HAS LCP_ID THAT */
+ /* DIFFERS FROM THE LATEST LCP_ID STARTED THEN THE NEXT_LCP IS ALSO */
+ /* INVALID */
+ /* -------------------------------------------------------------------- */
+ Uint8 lcpStatus[MAX_LCP_STORED];
+
+ /* -------------------------------------------------------------------- */
+ /* THE NEXT LOCAL CHECKPOINT TO EXECUTE IN THIS FRAGMENT REPLICA. */
+ /* -------------------------------------------------------------------- */
+ Uint8 nextLcp;
+
+ /* -------------------------------------------------------------------- */
+ /* THE NUMBER OF CRASHED REPLICAS IN THIS REPLICAS SO FAR. */
+ /* -------------------------------------------------------------------- */
+ Uint8 noCrashedReplicas;
+
+ /**
+ * Is a LCP currently ongoing on fragment
+ */
+ Uint8 lcpOngoingFlag;
+ };
+ typedef Ptr<ReplicaRecord> ReplicaRecordPtr;
+
+ /*************************************************************************
+ * TAB_DESCRIPTOR IS A DESCRIPTOR OF THE LOCATION OF THE FRAGMENTS BELONGING
+ * TO THE TABLE.THE INFORMATION ABOUT FRAGMENTS OF A TABLE ARE STORED IN
+ * CHUNKS OF FRAGMENTSTORE RECORDS.
+ * THIS RECORD ALSO HAS THE NECESSARY INFORMATION TO LOCATE A FRAGMENT AND
+ * TO LOCATE A FRAGMENT AND TO TRANSLATE A KEY OF A TUPLE TO THE FRAGMENT IT
+ * BELONGS
+ */
+ struct TabRecord {
+ /**
+ * State for copying table description into pages
+ */
+ enum CopyStatus {
+ CS_IDLE,
+ CS_SR_PHASE1_READ_PAGES,
+ CS_SR_PHASE2_READ_TABLE,
+ CS_SR_PHASE3_COPY_TABLE,
+ CS_REMOVE_NODE,
+ CS_LCP_READ_TABLE,
+ CS_COPY_TAB_REQ,
+ CS_COPY_NODE_STATE,
+ CS_ADD_TABLE_MASTER,
+ CS_ADD_TABLE_SLAVE,
+ CS_INVALIDATE_NODE_LCP
+ };
+ /**
+ * State for copying pages to disk
+ */
+ enum UpdateState {
+ US_IDLE,
+ US_LOCAL_CHECKPOINT,
+ US_REMOVE_NODE,
+ US_COPY_TAB_REQ,
+ US_ADD_TABLE_MASTER,
+ US_ADD_TABLE_SLAVE,
+ US_INVALIDATE_NODE_LCP
+ };
+ enum TabLcpStatus {
+ TLS_ACTIVE = 1,
+ TLS_WRITING_TO_FILE = 2,
+ TLS_COMPLETED = 3
+ };
+ enum TabStatus {
+ TS_IDLE = 0,
+ TS_ACTIVE = 1,
+ TS_CREATING = 2,
+ TS_DROPPING = 3
+ };
+ enum Method {
+ HASH = 0,
+ NOTDEFINED = 1
+ };
+ CopyStatus tabCopyStatus;
+ UpdateState tabUpdateState;
+ TabLcpStatus tabLcpStatus;
+ TabStatus tabStatus;
+ Method method;
+
+ Uint32 pageRef[8];
+//-----------------------------------------------------------------------------
+// Each entry in this array contains a reference to 16 fragment records in a
+// row. Thus finding the correct record is very quick provided the fragment id.
+//-----------------------------------------------------------------------------
+ Uint32 startFid[MAX_NDB_NODES];
+
+ Uint32 tabFile[2];
+ Uint32 connectrec;
+ Uint32 hashpointer;
+ Uint32 mask;
+ Uint32 noOfWords;
+ Uint32 schemaVersion;
+ Uint32 tabRemoveNode;
+ Uint32 totalfragments;
+ Uint32 noOfFragChunks;
+ Uint32 tabErrorCode;
+ struct {
+ Uint32 tabUserRef;
+ Uint32 tabUserPtr;
+ } m_dropTab;
+
+ struct DropTable {
+ Uint32 senderRef;
+ Uint32 senderData;
+ SignalCounter waitDropTabCount;
+ } m_prepDropTab;
+
+ Uint8 kvalue;
+ Uint8 noOfBackups;
+ Uint8 noPages;
+ Uint8 storedTable; /* 0 IF THE TABLE IS A TEMPORARY TABLE */
+ Uint16 tableType;
+ Uint16 primaryTableId;
+ };
+ typedef Ptr<TabRecord> TabRecordPtr;
+
+ /***************************************************************************/
+ /* THIS RECORD IS USED TO KEEP TRACK OF TAKE OVER AND STARTING A NODE. */
+ /* WE KEEP IT IN A RECORD TO ENABLE IT TO BE PARALLELISED IN THE FUTURE. */
+ /**************************************************************************/
+ struct TakeOverRecord {
+ enum ToMasterStatus {
+ IDLE = 0,
+ TO_WAIT_START_TAKE_OVER = 1,
+ TO_START_COPY = 2,
+ TO_START_COPY_ONGOING = 3,
+ TO_WAIT_START = 4,
+ STARTING = 5,
+ SELECTING_NEXT = 6,
+ TO_WAIT_PREPARE_CREATE = 9,
+ PREPARE_CREATE = 10,
+ COPY_FRAG = 11,
+ TO_WAIT_UPDATE_TO = 12,
+ TO_UPDATE_TO = 13,
+ COPY_ACTIVE = 14,
+ TO_WAIT_COMMIT_CREATE = 15,
+ LOCK_MUTEX = 23,
+ COMMIT_CREATE = 16,
+ TO_COPY_COMPLETED = 17,
+ WAIT_LCP = 18,
+ TO_END_COPY = 19,
+ TO_END_COPY_ONGOING = 20,
+ TO_WAIT_ENDING = 21,
+ ENDING = 22
+ };
+ enum ToSlaveStatus {
+ TO_SLAVE_IDLE = 0,
+ TO_SLAVE_STARTED = 1,
+ TO_SLAVE_CREATE_PREPARE = 2,
+ TO_SLAVE_COPY_FRAG_COMPLETED = 3,
+ TO_SLAVE_CREATE_COMMIT = 4,
+ TO_SLAVE_COPY_COMPLETED = 5
+ };
+ Uint32 startGci;
+ Uint32 toCopyNode;
+ Uint32 toCurrentFragid;
+ Uint32 toCurrentReplica;
+ Uint32 toCurrentTabref;
+ Uint32 toFailedNode;
+ Uint32 toStartingNode;
+ Uint32 nextTakeOver;
+ Uint32 prevTakeOver;
+ bool toNodeRestart;
+ ToMasterStatus toMasterStatus;
+ ToSlaveStatus toSlaveStatus;
+ MutexHandle2<DIH_SWITCH_PRIMARY_MUTEX> m_switchPrimaryMutexHandle;
+ };
+ typedef Ptr<TakeOverRecord> TakeOverRecordPtr;
+
+public:
+ Dbdih(const class Configuration &);
+ virtual ~Dbdih();
+
+ struct RWFragment {
+ Uint32 pageIndex;
+ Uint32 wordIndex;
+ Uint32 fragId;
+ TabRecordPtr rwfTabPtr;
+ PageRecordPtr rwfPageptr;
+ };
+ struct CopyTableNode {
+ Uint32 pageIndex;
+ Uint32 wordIndex;
+ Uint32 noOfWords;
+ TabRecordPtr ctnTabPtr;
+ PageRecordPtr ctnPageptr;
+ };
+
+private:
+ BLOCK_DEFINES(Dbdih);
+
+ void execDUMP_STATE_ORD(Signal *);
+ void execNDB_TAMPER(Signal *);
+ void execDEBUG_SIG(Signal *);
+ void execEMPTY_LCP_CONF(Signal *);
+ void execMASTER_GCPREF(Signal *);
+ void execMASTER_GCPREQ(Signal *);
+ void execMASTER_GCPCONF(Signal *);
+ void execMASTER_LCPREF(Signal *);
+ void execMASTER_LCPREQ(Signal *);
+ void execMASTER_LCPCONF(Signal *);
+ void execNF_COMPLETEREP(Signal *);
+ void execSTART_PERMREQ(Signal *);
+ void execSTART_PERMCONF(Signal *);
+ void execSTART_PERMREF(Signal *);
+ void execINCL_NODEREQ(Signal *);
+ void execINCL_NODECONF(Signal *);
+ void execEND_TOREQ(Signal *);
+ void execEND_TOCONF(Signal *);
+ void execSTART_TOREQ(Signal *);
+ void execSTART_TOCONF(Signal *);
+ void execSTART_MEREQ(Signal *);
+ void execSTART_MECONF(Signal *);
+ void execSTART_MEREF(Signal *);
+ void execSTART_COPYREQ(Signal *);
+ void execSTART_COPYCONF(Signal *);
+ void execSTART_COPYREF(Signal *);
+ void execCREATE_FRAGREQ(Signal *);
+ void execCREATE_FRAGCONF(Signal *);
+ void execDIVERIFYREQ(Signal *);
+ void execGCP_SAVECONF(Signal *);
+ void execGCP_PREPARECONF(Signal *);
+ void execGCP_PREPARE(Signal *);
+ void execGCP_NODEFINISH(Signal *);
+ void execGCP_COMMIT(Signal *);
+ void execDIHNDBTAMPER(Signal *);
+ void execCONTINUEB(Signal *);
+ void execCOPY_GCIREQ(Signal *);
+ void execCOPY_GCICONF(Signal *);
+ void execCOPY_TABREQ(Signal *);
+ void execCOPY_TABCONF(Signal *);
+ void execTCGETOPSIZECONF(Signal *);
+ void execTC_CLOPSIZECONF(Signal *);
+
+ void execLCP_FRAG_REP(Signal *);
+ void execLCP_COMPLETE_REP(Signal *);
+ void execSTART_LCP_REQ(Signal *);
+ void execSTART_LCP_CONF(Signal *);
+ MutexHandle2<DIH_START_LCP_MUTEX> c_startLcpMutexHandle;
+ void startLcpMutex_locked(Signal* signal, Uint32, Uint32);
+ void startLcpMutex_unlocked(Signal* signal, Uint32, Uint32);
+
+ MutexHandle2<DIH_SWITCH_PRIMARY_MUTEX> c_switchPrimaryMutexHandle;
+ void switchPrimaryMutex_locked(Signal* signal, Uint32, Uint32);
+ void switchPrimaryMutex_unlocked(Signal* signal, Uint32, Uint32);
+ void switch_primary_stop_node(Signal* signal, Uint32, Uint32);
+
+ void execBLOCK_COMMIT_ORD(Signal *);
+ void execUNBLOCK_COMMIT_ORD(Signal *);
+
+ void execDIH_SWITCH_REPLICA_REQ(Signal *);
+ void execDIH_SWITCH_REPLICA_REF(Signal *);
+ void execDIH_SWITCH_REPLICA_CONF(Signal *);
+
+ void execSTOP_PERM_REQ(Signal *);
+ void execSTOP_PERM_REF(Signal *);
+ void execSTOP_PERM_CONF(Signal *);
+
+ void execSTOP_ME_REQ(Signal *);
+ void execSTOP_ME_REF(Signal *);
+ void execSTOP_ME_CONF(Signal *);
+
+ void execREAD_CONFIG_REQ(Signal *);
+ void execUNBLO_DICTCONF(Signal *);
+ void execCOPY_ACTIVECONF(Signal *);
+ void execTAB_COMMITREQ(Signal *);
+ void execNODE_FAILREP(Signal *);
+ void execCOPY_FRAGCONF(Signal *);
+ void execCOPY_FRAGREF(Signal *);
+ void execDIADDTABREQ(Signal *);
+ void execDIGETNODESREQ(Signal *);
+ void execDIRELEASEREQ(Signal *);
+ void execDISEIZEREQ(Signal *);
+ void execSTTOR(Signal *);
+ void execDI_FCOUNTREQ(Signal *);
+ void execDIGETPRIMREQ(Signal *);
+ void execGCP_SAVEREF(Signal *);
+ void execGCP_TCFINISHED(Signal *);
+ void execREAD_NODESCONF(Signal *);
+ void execNDB_STTOR(Signal *);
+ void execDICTSTARTCONF(Signal *);
+ void execNDB_STARTREQ(Signal *);
+ void execGETGCIREQ(Signal *);
+ void execDIH_RESTARTREQ(Signal *);
+ void execSTART_RECCONF(Signal *);
+ void execSTART_FRAGCONF(Signal *);
+ void execADD_FRAGCONF(Signal *);
+ void execADD_FRAGREF(Signal *);
+ void execFSOPENCONF(Signal *);
+ void execFSOPENREF(Signal *);
+ void execFSCLOSECONF(Signal *);
+ void execFSCLOSEREF(Signal *);
+ void execFSREADCONF(Signal *);
+ void execFSREADREF(Signal *);
+ void execFSWRITECONF(Signal *);
+ void execFSWRITEREF(Signal *);
+ void execSET_VAR_REQ(Signal *);
+ void execCHECKNODEGROUPSREQ(Signal *);
+ void execSTART_INFOREQ(Signal*);
+ void execSTART_INFOREF(Signal*);
+ void execSTART_INFOCONF(Signal*);
+ void execWAIT_GCP_REQ(Signal* signal);
+ void execWAIT_GCP_REF(Signal* signal);
+ void execWAIT_GCP_CONF(Signal* signal);
+ void execUPDATE_TOREQ(Signal* signal);
+ void execUPDATE_TOCONF(Signal* signal);
+
+ void execPREP_DROP_TAB_REQ(Signal* signal);
+ void execWAIT_DROP_TAB_REF(Signal* signal);
+ void execWAIT_DROP_TAB_CONF(Signal* signal);
+ void execDROP_TAB_REQ(Signal* signal);
+
+ void execALTER_TAB_REQ(Signal* signal);
+
+ void execCREATE_FRAGMENTATION_REQ(Signal*);
+
+ void waitDropTabWritingToFile(Signal *, TabRecordPtr tabPtr);
+ void checkPrepDropTabComplete(Signal *, TabRecordPtr tabPtr);
+ void checkWaitDropTabFailedLqh(Signal *, Uint32 nodeId, Uint32 tableId);
+
+ // Statement blocks
+//------------------------------------
+// Methods that send signals
+//------------------------------------
+ void nullRoutine(Signal *, Uint32 nodeId);
+ void sendCOPY_GCIREQ(Signal *, Uint32 nodeId);
+ void sendDIH_SWITCH_REPLICA_REQ(Signal *, Uint32 nodeId);
+ void sendEMPTY_LCP_REQ(Signal *, Uint32 nodeId);
+ void sendEND_TOREQ(Signal *, Uint32 nodeId);
+ void sendGCP_COMMIT(Signal *, Uint32 nodeId);
+ void sendGCP_PREPARE(Signal *, Uint32 nodeId);
+ void sendGCP_SAVEREQ(Signal *, Uint32 nodeId);
+ void sendINCL_NODEREQ(Signal *, Uint32 nodeId);
+ void sendMASTER_GCPREQ(Signal *, Uint32 nodeId);
+ void sendMASTER_LCPREQ(Signal *, Uint32 nodeId);
+ void sendMASTER_LCPCONF(Signal * signal);
+ void sendSTART_RECREQ(Signal *, Uint32 nodeId);
+ void sendSTART_INFOREQ(Signal *, Uint32 nodeId);
+ void sendSTART_TOREQ(Signal *, Uint32 nodeId);
+ void sendSTOP_ME_REQ(Signal *, Uint32 nodeId);
+ void sendTC_CLOPSIZEREQ(Signal *, Uint32 nodeId);
+ void sendTCGETOPSIZEREQ(Signal *, Uint32 nodeId);
+ void sendUPDATE_TOREQ(Signal *, Uint32 nodeId);
+ void sendSTART_LCP_REQ(Signal *, Uint32 nodeId);
+
+ void sendLCP_FRAG_ORD(Signal*, NodeRecord::FragmentCheckpointInfo info);
+ void sendLastLCP_FRAG_ORD(Signal *);
+
+ void sendCopyTable(Signal *, CopyTableNode* ctn,
+ BlockReference ref, Uint32 reqinfo);
+ void sendCreateFragReq(Signal *,
+ Uint32 startGci,
+ Uint32 storedType,
+ Uint32 takeOverPtr);
+ void sendDihfragreq(Signal *,
+ TabRecordPtr regTabPtr,
+ Uint32 fragId);
+ void sendStartFragreq(Signal *,
+ TabRecordPtr regTabPtr,
+ Uint32 fragId);
+ void sendHOT_SPAREREP(Signal *);
+ void sendAddFragreq(Signal *,
+ TabRecordPtr regTabPtr,
+ Uint32 fragId,
+ Uint32 lcpNo,
+ Uint32 param);
+
+ void sendAddFragreq(Signal*, ConnectRecordPtr, TabRecordPtr, Uint32 fragId);
+ void addTable_closeConf(Signal* signal, Uint32 tabPtrI);
+ void resetReplicaSr(TabRecordPtr tabPtr);
+ void resetReplicaLcp(ReplicaRecord * replicaP, Uint32 stopGci);
+
+//------------------------------------
+// Methods for LCP functionality
+//------------------------------------
+ void checkKeepGci(Uint32 replicaStartIndex);
+ void checkLcpStart(Signal *, Uint32 lineNo);
+ void checkStartMoreLcp(Signal *, Uint32 nodeId);
+ bool reportLcpCompletion(const class LcpFragRep *);
+ void sendLCP_COMPLETE_REP(Signal *);
+
+//------------------------------------
+// Methods for Delete Table Files
+//------------------------------------
+ void startDeleteFile(Signal* signal, TabRecordPtr tabPtr);
+ void openTableFileForDelete(Signal* signal, Uint32 fileIndex);
+ void tableOpenLab(Signal* signal, FileRecordPtr regFilePtr);
+ void tableDeleteLab(Signal* signal, FileRecordPtr regFilePtr);
+
+//------------------------------------
+// File Record specific methods
+//------------------------------------
+ void closeFile(Signal *, FileRecordPtr regFilePtr);
+ void closeFileDelete(Signal *, FileRecordPtr regFilePtr);
+ void createFileRw(Signal *, FileRecordPtr regFilePtr);
+ void openFileRw(Signal *, FileRecordPtr regFilePtr);
+ void openFileRo(Signal *, FileRecordPtr regFilePtr);
+ void seizeFile(FileRecordPtr& regFilePtr);
+ void releaseFile(Uint32 fileIndex);
+
+//------------------------------------
+// Methods called when completing file
+// operation.
+//------------------------------------
+ void creatingGcpLab(Signal *, FileRecordPtr regFilePtr);
+ void openingGcpLab(Signal *, FileRecordPtr regFilePtr);
+ void openingTableLab(Signal *, FileRecordPtr regFilePtr);
+ void tableCreateLab(Signal *, FileRecordPtr regFilePtr);
+ void creatingGcpErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void openingCopyGciErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void creatingCopyGciErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void openingGcpErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void openingTableErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void tableCreateErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void closingGcpLab(Signal *, FileRecordPtr regFilePtr);
+ void closingGcpCrashLab(Signal *, FileRecordPtr regFilePtr);
+ void closingTableCrashLab(Signal *, FileRecordPtr regFilePtr);
+ void closingTableSrLab(Signal *, FileRecordPtr regFilePtr);
+ void tableCloseLab(Signal *, FileRecordPtr regFilePtr);
+ void tableCloseErrorLab(FileRecordPtr regFilePtr);
+ void readingGcpLab(Signal *, FileRecordPtr regFilePtr);
+ void readingTableLab(Signal *, FileRecordPtr regFilePtr);
+ void readingGcpErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void readingTableErrorLab(Signal *, FileRecordPtr regFilePtr);
+ void writingCopyGciLab(Signal *, FileRecordPtr regFilePtr);
+ void writeInitGcpLab(Signal *, FileRecordPtr regFilePtr);
+ void tableWriteLab(Signal *, FileRecordPtr regFilePtr);
+ void writeInitGcpErrorLab(Signal *, FileRecordPtr regFilePtr);
+
+
+ void calculateHotSpare();
+ void checkEscalation();
+ void clearRestartInfoBits(Signal *);
+ void invalidateLcpInfoAfterSr();
+
+ bool isMaster();
+ bool isActiveMaster();
+
+ void emptyverificbuffer(Signal *, bool aContintueB);
+ Uint32 findHotSpare();
+ void handleGcpStateInMaster(Signal *, NodeRecordPtr failedNodeptr);
+ void initRestartInfo();
+ void initRestorableGciFiles();
+ void makeNodeGroups(Uint32 nodeArray[]);
+ void makePrnList(class ReadNodesConf * readNodes, Uint32 nodeArray[]);
+ void nodeResetStart();
+ void releaseTabPages(Uint32 tableId);
+ void replication(Uint32 noOfReplicas,
+ NodeGroupRecordPtr NGPtr,
+ FragmentstorePtr regFragptr);
+ void selectMasterCandidateAndSend(Signal *);
+ void setInitialActiveStatus();
+ void setLcpActiveStatusEnd();
+ void setLcpActiveStatusStart(Signal *);
+ void setNodeActiveStatus();
+ void setNodeGroups();
+ void setNodeInfo(Signal *);
+ void setNodeLcpActiveStatus();
+ void setNodeRestartInfoBits();
+ void startGcp(Signal *);
+
+ void readFragment(RWFragment* rf, FragmentstorePtr regFragptr);
+ Uint32 readPageWord(RWFragment* rf);
+ void readReplica(RWFragment* rf, ReplicaRecordPtr readReplicaPtr);
+ void readReplicas(RWFragment* rf, FragmentstorePtr regFragptr);
+ void readRestorableGci(Signal *, FileRecordPtr regFilePtr);
+ void readTabfile(Signal *, TabRecord* tab, FileRecordPtr regFilePtr);
+ void writeFragment(RWFragment* wf, FragmentstorePtr regFragptr);
+ void writePageWord(RWFragment* wf, Uint32 dataWord);
+ void writeReplicas(RWFragment* wf, Uint32 replicaStartIndex);
+ void writeRestorableGci(Signal *, FileRecordPtr regFilePtr);
+ void writeTabfile(Signal *, TabRecord* tab, FileRecordPtr regFilePtr);
+ void copyTabReq_complete(Signal* signal, TabRecordPtr tabPtr);
+
+ void gcpcommitreqLab(Signal *);
+ void gcpsavereqLab(Signal *);
+ void copyGciLab(Signal *, CopyGCIReq::CopyReason reason);
+ void storeNewLcpIdLab(Signal *);
+ void startLcpRoundLoopLab(Signal *, Uint32 startTableId, Uint32 startFragId);
+
+ void nodeFailCompletedCheckLab(Signal*, NodeRecordPtr failedNodePtr);
+
+ /**
+ *
+ */
+ void setLocalNodefailHandling(Signal*, Uint32 failedNodeId,
+ NodefailHandlingStep step);
+ void checkLocalNodefailComplete(Signal*, Uint32 failedNodeId,
+ NodefailHandlingStep step);
+
+ void ndbsttorry10Lab(Signal *, Uint32 _line);
+ void createMutexes(Signal* signal, Uint32 no);
+ void createMutex_done(Signal* signal, Uint32 no, Uint32 retVal);
+ void crashSystemAtGcpStop(Signal *);
+ void sendFirstDictfragsreq(Signal *, TabRecordPtr regTabPtr);
+ void addtabrefuseLab(Signal *, ConnectRecordPtr regConnectPtr, Uint32 errorCode);
+ void GCP_SAVEhandling(Signal *, Uint32 nodeId);
+ void packTableIntoPagesLab(Signal *, Uint32 tableId);
+ void readPagesIntoTableLab(Signal *, Uint32 tableId);
+ void readPagesIntoFragLab(Signal *, RWFragment* rf);
+ void readTabDescriptionLab(Signal *, Uint32 tableId);
+ void copyTableLab(Signal *, Uint32 tableId);
+ void breakCopyTableLab(Signal *,
+ TabRecordPtr regTabPtr,
+ Uint32 nodeId);
+ void checkAddfragCompletedLab(Signal *,
+ TabRecordPtr regTabPtr,
+ Uint32 fragId);
+ void completeRestartLab(Signal *);
+ void readTableFromPagesLab(Signal *, TabRecordPtr regTabPtr);
+ void srPhase2ReadTableLab(Signal *, TabRecordPtr regTabPtr);
+ void checkTcCounterLab(Signal *);
+ void calculateKeepGciLab(Signal *, Uint32 tableId, Uint32 fragId);
+ void tableUpdateLab(Signal *, TabRecordPtr regTabPtr);
+ void checkLcpCompletedLab(Signal *);
+ void initLcpLab(Signal *, Uint32 masterRef, Uint32 tableId);
+ void startGcpLab(Signal *, Uint32 aWaitTime);
+ void checkGcpStopLab(Signal *);
+ void MASTER_GCPhandling(Signal *, Uint32 failedNodeId);
+ void MASTER_LCPhandling(Signal *, Uint32 failedNodeId);
+ void rnfTableNotReadyLab(Signal *, TabRecordPtr regTabPtr, Uint32 removeNodeId);
+ void startLcpTakeOverLab(Signal *, Uint32 failedNodeId);
+
+ void startLcpMasterTakeOver(Signal *, Uint32 failedNodeId);
+ void startGcpMasterTakeOver(Signal *, Uint32 failedNodeId);
+ void checkGcpOutstanding(Signal*, Uint32 failedNodeId);
+
+ void checkEmptyLcpComplete(Signal *);
+ void lcpBlockedLab(Signal *);
+ void breakCheckTabCompletedLab(Signal *, TabRecordPtr regTabptr);
+ void readGciFileLab(Signal *);
+ void openingCopyGciSkipInitLab(Signal *, FileRecordPtr regFilePtr);
+ void startLcpRoundLab(Signal *);
+ void gcpBlockedLab(Signal *);
+ void initialStartCompletedLab(Signal *);
+ void allNodesLcpCompletedLab(Signal *);
+ void nodeRestartPh2Lab(Signal *);
+ void initGciFilesLab(Signal *);
+ void dictStartConfLab(Signal *);
+ void nodeDictStartConfLab(Signal *);
+ void ndbStartReqLab(Signal *, BlockReference ref);
+ void nodeRestartStartRecConfLab(Signal *);
+ void dihCopyCompletedLab(Signal *);
+ void release_connect(ConnectRecordPtr ptr);
+ void copyTableNode(Signal *,
+ CopyTableNode* ctn,
+ NodeRecordPtr regNodePtr);
+ void startFragment(Signal *, Uint32 tableId, Uint32 fragId);
+ bool checkLcpAllTablesDoneInLqh();
+
+ void lcpStateAtNodeFailureLab(Signal *, Uint32 nodeId);
+ void copyNodeLab(Signal *, Uint32 tableId);
+ void copyGciReqLab(Signal *);
+ void allLab(Signal *,
+ ConnectRecordPtr regConnectPtr,
+ TabRecordPtr regTabPtr);
+ void tableCopyNodeLab(Signal *, TabRecordPtr regTabPtr);
+
+ void removeNodeFromTables(Signal *, Uint32 tableId, Uint32 nodeId);
+ void removeNodeFromTable(Signal *, Uint32 tableId, TabRecordPtr tabPtr);
+ void removeNodeFromTablesComplete(Signal* signal, Uint32 nodeId);
+
+ void packFragIntoPagesLab(Signal *, RWFragment* wf);
+ void startNextChkpt(Signal *);
+ void failedNodeLcpHandling(Signal*, NodeRecordPtr failedNodePtr);
+ void failedNodeSynchHandling(Signal *, NodeRecordPtr failedNodePtr);
+ void checkCopyTab(NodeRecordPtr failedNodePtr);
+
+ void initCommonData();
+ void initialiseRecordsLab(Signal *, Uint32 stepNo, Uint32, Uint32);
+
+ void findReplica(ReplicaRecordPtr& regReplicaPtr,
+ Fragmentstore* fragPtrP, Uint32 nodeId);
+//------------------------------------
+// Node failure handling methods
+//------------------------------------
+ void startRemoveFailedNode(Signal *, NodeRecordPtr failedNodePtr);
+ void handleGcpTakeOver(Signal *, NodeRecordPtr failedNodePtr);
+ void handleLcpTakeOver(Signal *, NodeRecordPtr failedNodePtr);
+ void handleNewMaster(Signal *, NodeRecordPtr failedNodePtr);
+ void checkTakeOverInMasterAllNodeFailure(Signal*, NodeRecordPtr failedNode);
+ void checkTakeOverInMasterCopyNodeFailure(Signal*, Uint32 failedNodeId);
+ void checkTakeOverInMasterStartNodeFailure(Signal*, Uint32 takeOverPtr);
+ void checkTakeOverInNonMasterStartNodeFailure(Signal*, Uint32 takeOverPtr);
+ void handleLcpMasterTakeOver(Signal *, Uint32 nodeId);
+
+//------------------------------------
+// Replica record specific methods
+//------------------------------------
+ Uint32 findLogInterval(ConstPtr<ReplicaRecord> regReplicaPtr,
+ Uint32 startGci);
+ void findMinGci(ReplicaRecordPtr fmgReplicaPtr,
+ Uint32& keeGci,
+ Uint32& oldestRestorableGci);
+ bool findStartGci(ConstPtr<ReplicaRecord> fstReplicaPtr,
+ Uint32 tfstStopGci,
+ Uint32& tfstStartGci,
+ Uint32& tfstLcp);
+ void newCrashedReplica(Uint32 nodeId, ReplicaRecordPtr ncrReplicaPtr);
+ void packCrashedReplicas(ReplicaRecordPtr pcrReplicaPtr);
+ void releaseReplicas(Uint32 replicaPtr);
+ void removeOldCrashedReplicas(ReplicaRecordPtr rocReplicaPtr);
+ void removeTooNewCrashedReplicas(ReplicaRecordPtr rtnReplicaPtr);
+ void seizeReplicaRec(ReplicaRecordPtr& replicaPtr);
+
+//------------------------------------
+// Methods operating on a fragment and
+// its connected replicas and nodes.
+//------------------------------------
+ void allocStoredReplica(FragmentstorePtr regFragptr,
+ ReplicaRecordPtr& newReplicaPtr,
+ Uint32 nodeId);
+ Uint32 extractNodeInfo(const Fragmentstore * fragPtr, Uint32 nodes[]);
+ bool findBestLogNode(CreateReplicaRecord* createReplica,
+ FragmentstorePtr regFragptr,
+ Uint32 startGci,
+ Uint32 stopGci,
+ Uint32 logNode,
+ Uint32& fblStopGci);
+ bool findLogNodes(CreateReplicaRecord* createReplica,
+ FragmentstorePtr regFragptr,
+ Uint32 startGci,
+ Uint32 stopGci);
+ void findToReplica(TakeOverRecord* regTakeOver,
+ Uint32 replicaType,
+ FragmentstorePtr regFragptr,
+ ReplicaRecordPtr& ftrReplicaPtr);
+ void initFragstore(FragmentstorePtr regFragptr);
+ void insertBackup(FragmentstorePtr regFragptr, Uint32 nodeId);
+ void insertfraginfo(FragmentstorePtr regFragptr,
+ Uint32 noOfBackups,
+ Uint32* nodeArray);
+ void linkOldStoredReplica(FragmentstorePtr regFragptr,
+ ReplicaRecordPtr replicaPtr);
+ void linkStoredReplica(FragmentstorePtr regFragptr,
+ ReplicaRecordPtr replicaPtr);
+ void prepareReplicas(FragmentstorePtr regFragptr);
+ void removeNodeFromStored(Uint32 nodeId,
+ FragmentstorePtr regFragptr,
+ ReplicaRecordPtr replicaPtr);
+ void removeOldStoredReplica(FragmentstorePtr regFragptr,
+ ReplicaRecordPtr replicaPtr);
+ void removeStoredReplica(FragmentstorePtr regFragptr,
+ ReplicaRecordPtr replicaPtr);
+ void searchStoredReplicas(FragmentstorePtr regFragptr);
+ void updateNodeInfo(FragmentstorePtr regFragptr);
+
+//------------------------------------
+// Fragment allocation, deallocation and
+// find methods
+//------------------------------------
+ void allocFragments(Uint32 noOfFragments, TabRecordPtr regTabPtr);
+ void releaseFragments(TabRecordPtr regTabPtr);
+ void getFragstore(TabRecord *, Uint32 fragNo, FragmentstorePtr & ptr);
+ void initialiseFragstore();
+
+//------------------------------------
+// Page Record specific methods
+//------------------------------------
+ void allocpage(PageRecordPtr& regPagePtr);
+ void releasePage(Uint32 pageIndex);
+
+//------------------------------------
+// Table Record specific methods
+//------------------------------------
+ void initTable(TabRecordPtr regTabPtr);
+ void initTableFile(TabRecordPtr regTabPtr);
+ void releaseTable(TabRecordPtr tabPtr);
+ Uint32 findTakeOver(Uint32 failedNodeId);
+ void handleTakeOverMaster(Signal *, Uint32 takeOverPtr);
+ void handleTakeOverNewMaster(Signal *, Uint32 takeOverPtr);
+
+//------------------------------------
+// TakeOver Record specific methods
+//------------------------------------
+ void initTakeOver(TakeOverRecordPtr regTakeOverptr);
+ void seizeTakeOver(TakeOverRecordPtr& regTakeOverptr);
+ void allocateTakeOver(TakeOverRecordPtr& regTakeOverptr);
+ void releaseTakeOver(Uint32 takeOverPtr);
+ bool anyActiveTakeOver();
+ void checkToCopy();
+ void checkToCopyCompleted(Signal *);
+ bool checkToInterrupted(TakeOverRecordPtr& regTakeOverptr);
+ Uint32 getStartNode(Uint32 takeOverPtr);
+
+//------------------------------------
+// Methods for take over functionality
+//------------------------------------
+ void changeNodeGroups(Uint32 startNode, Uint32 nodeTakenOver);
+ void endTakeOver(Uint32 takeOverPtr);
+ void initStartTakeOver(const class StartToReq *,
+ TakeOverRecordPtr regTakeOverPtr);
+
+ void nodeRestartTakeOver(Signal *, Uint32 startNodeId);
+ void systemRestartTakeOverLab(Signal *);
+ void startTakeOver(Signal *,
+ Uint32 takeOverPtr,
+ Uint32 startNode,
+ Uint32 toNode);
+ void sendStartTo(Signal *, Uint32 takeOverPtr);
+ void startNextCopyFragment(Signal *, Uint32 takeOverPtr);
+ void toCopyFragLab(Signal *, Uint32 takeOverPtr);
+ void startHsAddFragConfLab(Signal *);
+ void prepareSendCreateFragReq(Signal *, Uint32 takeOverPtr);
+ void sendUpdateTo(Signal *, Uint32 takeOverPtr, Uint32 updateState);
+ void toCopyCompletedLab(Signal *, TakeOverRecordPtr regTakeOverptr);
+ void takeOverCompleted(Uint32 aNodeId);
+ void sendEndTo(Signal *, Uint32 takeOverPtr);
+
+//------------------------------------
+// Node Record specific methods
+//------------------------------------
+ void checkStartTakeOver(Signal *);
+ void insertAlive(NodeRecordPtr newNodePtr);
+ void insertDeadNode(NodeRecordPtr removeNodePtr);
+ void removeAlive(NodeRecordPtr removeNodePtr);
+ void removeDeadNode(NodeRecordPtr removeNodePtr);
+
+ NodeRecord::NodeStatus getNodeStatus(Uint32 nodeId);
+ void setNodeStatus(Uint32 nodeId, NodeRecord::NodeStatus);
+ Sysfile::ActiveStatus getNodeActiveStatus(Uint32 nodeId);
+ void setNodeActiveStatus(Uint32 nodeId, Sysfile::ActiveStatus newStatus);
+ void setNodeLcpActiveStatus(Uint32 nodeId, bool newState);
+ bool getNodeLcpActiveStatus(Uint32 nodeId);
+ bool getAllowNodeStart(Uint32 nodeId);
+ void setAllowNodeStart(Uint32 nodeId, bool newState);
+ bool getNodeCopyCompleted(Uint32 nodeId);
+ void setNodeCopyCompleted(Uint32 nodeId, bool newState);
+ bool checkNodeAlive(Uint32 nodeId);
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ // Variables to support record structures and their free lists
+
+ ApiConnectRecord *apiConnectRecord;
+ Uint32 capiConnectFileSize;
+
+ ConnectRecord *connectRecord;
+ Uint32 cfirstconnect;
+ Uint32 cconnectFileSize;
+
+ CreateReplicaRecord *createReplicaRecord;
+ Uint32 cnoOfCreateReplicas;
+
+ FileRecord *fileRecord;
+ Uint32 cfirstfreeFile;
+ Uint32 cfileFileSize;
+
+ Fragmentstore *fragmentstore;
+ Uint32 cfirstfragstore;
+ Uint32 cfragstoreFileSize;
+
+ Uint32 c_nextNodeGroup;
+ NodeGroupRecord *nodeGroupRecord;
+
+ NodeRecord *nodeRecord;
+
+ PageRecord *pageRecord;
+ Uint32 cfirstfreepage;
+ Uint32 cpageFileSize;
+
+ ReplicaRecord *replicaRecord;
+ Uint32 cfirstfreeReplica;
+ Uint32 cnoFreeReplicaRec;
+ Uint32 creplicaFileSize;
+
+ TabRecord *tabRecord;
+ Uint32 ctabFileSize;
+
+ TakeOverRecord *takeOverRecord;
+ Uint32 cfirstfreeTakeOver;
+
+ /*
+ 2.4 C O M M O N S T O R E D V A R I A B L E S
+ ----------------------------------------------------
+ */
+ Uint32 cfirstVerifyQueue;
+ Uint32 clastVerifyQueue;
+ Uint32 cverifyQueueCounter;
+
+ /*------------------------------------------------------------------------*/
+ /* THIS VARIABLE KEEPS THE REFERENCES TO FILE RECORDS THAT DESCRIBE */
+ /* THE TWO FILES THAT ARE USED TO STORE THE VARIABLE CRESTART_INFO */
+ /* ON DISK. */
+ /*------------------------------------------------------------------------*/
+ Uint32 crestartInfoFile[2];
+ /*------------------------------------------------------------------------*/
+ /* THIS VARIABLE KEEPS TRACK OF THE STATUS OF A GLOBAL CHECKPOINT */
+ /* PARTICIPANT. THIS IS NEEDED TO HANDLE A NODE FAILURE. WHEN A NODE*/
+ /* FAILURE OCCURS IT IS EASY THAT THE PROTOCOL STOPS IF NO ACTION IS*/
+ /* TAKEN TO PREVENT THIS. THIS VARIABLE ENSURES SUCH ACTION CAN BE */
+ /* TAKEN. */
+ /*------------------------------------------------------------------------*/
+ enum GcpParticipantState {
+ GCP_PARTICIPANT_READY = 0,
+ GCP_PARTICIPANT_PREPARE_RECEIVED = 1,
+ GCP_PARTICIPANT_COMMIT_RECEIVED = 2,
+ GCP_PARTICIPANT_TC_FINISHED = 3,
+ GCP_PARTICIPANT_COPY_GCI_RECEIVED = 4
+ };
+ GcpParticipantState cgcpParticipantState;
+ /*------------------------------------------------------------------------*/
+ /* THESE VARIABLES ARE USED TO CONTROL THAT GCP PROCESSING DO NOT */
+ /*STOP FOR SOME REASON. */
+ /*------------------------------------------------------------------------*/
+ enum GcpStatus {
+ GCP_READY = 0,
+ GCP_PREPARE_SENT = 1,
+ GCP_COMMIT_SENT = 2,
+ GCP_NODE_FINISHED = 3,
+ GCP_SAVE_LQH_FINISHED = 4
+ };
+ GcpStatus cgcpStatus;
+ Uint32 cgcpStartCounter;
+ Uint32 coldGcpStatus;
+ Uint32 coldGcpId;
+ /*------------------------------------------------------------------------*/
+ /* THIS VARIABLE KEEPS TRACK OF THE STATE OF THIS NODE AS MASTER. */
+ /*------------------------------------------------------------------------*/
+ enum MasterState {
+ MASTER_IDLE = 0,
+ MASTER_ACTIVE = 1,
+ MASTER_TAKE_OVER_GCP = 2
+ };
+ MasterState cmasterState;
+ Uint16 cmasterTakeOverNode;
+ /* NODE IS NOT MASTER */
+ /* NODE IS ACTIVE AS MASTER */
+ /* NODE IS TAKING OVER AS MASTER */
+
+ struct CopyGCIMaster {
+ CopyGCIMaster(){ m_copyReason = m_waiting = CopyGCIReq::IDLE;}
+ /*------------------------------------------------------------------------*/
+ /* THIS STATE VARIABLE IS USED TO INDICATE IF COPYING OF RESTART */
+ /* INFO WAS STARTED BY A LOCAL CHECKPOINT OR AS PART OF A SYSTEM */
+ /* RESTART. */
+ /*------------------------------------------------------------------------*/
+ CopyGCIReq::CopyReason m_copyReason;
+
+ /*------------------------------------------------------------------------*/
+ /* COPYING RESTART INFO CAN BE STARTED BY LOCAL CHECKPOINTS AND BY */
+ /* GLOBAL CHECKPOINTS. WE CAN HOWEVER ONLY HANDLE ONE SUCH COPY AT */
+ /* THE TIME. THUS WE HAVE TO KEEP WAIT INFORMATION IN THIS VARIABLE.*/
+ /*------------------------------------------------------------------------*/
+ CopyGCIReq::CopyReason m_waiting;
+ } c_copyGCIMaster;
+
+ struct CopyGCISlave {
+ CopyGCISlave(){ m_copyReason = CopyGCIReq::IDLE; m_expectedNextWord = 0;}
+ /*------------------------------------------------------------------------*/
+ /* THIS STATE VARIABLE IS USED TO INDICATE IF COPYING OF RESTART */
+ /* INFO WAS STARTED BY A LOCAL CHECKPOINT OR AS PART OF A SYSTEM */
+ /* RESTART. THIS VARIABLE IS USED BY THE NODE THAT RECEIVES */
+ /* COPY_GCI_REQ. */
+ /*------------------------------------------------------------------------*/
+ Uint32 m_senderData;
+ BlockReference m_senderRef;
+ CopyGCIReq::CopyReason m_copyReason;
+
+ Uint32 m_expectedNextWord;
+ } c_copyGCISlave;
+
+ /*------------------------------------------------------------------------*/
+ /* THIS VARIABLE IS USED TO KEEP TRACK OF THE STATE OF LOCAL */
+ /* CHECKPOINTS. */
+ /*------------------------------------------------------------------------*/
+public:
+ enum LcpStatus {
+ LCP_STATUS_IDLE = 0,
+ LCP_TCGET = 1, // Only master
+ LCP_STATUS_ACTIVE = 2,
+ LCP_CALCULATE_KEEP_GCI = 4, // Only master
+ LCP_COPY_GCI = 5,
+ LCP_INIT_TABLES = 6,
+ LCP_TC_CLOPSIZE = 7, // Only master
+ LCP_START_LCP_ROUND = 8,
+ LCP_TAB_COMPLETED = 9,
+ LCP_TAB_SAVED = 10
+ };
+private:
+
+ struct LcpState {
+ LcpStatus lcpStatus;
+ Uint32 lcpStatusUpdatedPlace;
+
+ void setLcpStatus(LcpStatus status, Uint32 line){
+ lcpStatus = status;
+ lcpStatusUpdatedPlace = line;
+ }
+
+ Uint32 lcpStart;
+ Uint32 lcpStartGcp;
+ Uint32 keepGci; /* USED TO CALCULATE THE GCI TO KEEP AFTER A LCP */
+ Uint32 oldestRestorableGci;
+
+ struct CurrentFragment {
+ Uint32 tableId;
+ Uint32 fragmentId;
+ } currentFragment;
+
+ Uint32 noOfLcpFragRepOutstanding;
+
+ /*------------------------------------------------------------------------*/
+ /* USED TO ENSURE THAT LCP'S ARE EXECUTED WITH CERTAIN TIMEINTERVALS*/
+ /* EVEN WHEN SYSTEM IS NOT DOING ANYTHING. */
+ /*------------------------------------------------------------------------*/
+ Uint32 ctimer;
+ Uint32 ctcCounter;
+ Uint32 clcpDelay; /* MAX. 2^(CLCP_DELAY - 2) SEC BETWEEN LCP'S */
+
+ /*------------------------------------------------------------------------*/
+ /* THIS STATE IS USED TO TELL IF THE FIRST LCP AFTER START/RESTART */
+ /* HAS BEEN RUN. AFTER A NODE RESTART THE NODE DOES NOT ENTER */
+ /* STARTED STATE BEFORE THIS IS DONE. */
+ /*------------------------------------------------------------------------*/
+ bool immediateLcpStart;
+ bool m_LCP_COMPLETE_REP_From_Master_Received;
+ SignalCounter m_LCP_COMPLETE_REP_Counter_DIH;
+ SignalCounter m_LCP_COMPLETE_REP_Counter_LQH;
+ SignalCounter m_LAST_LCP_FRAG_ORD;
+ NdbNodeBitmask m_participatingLQH;
+ NdbNodeBitmask m_participatingDIH;
+
+ Uint32 m_masterLcpDihRef;
+ bool m_MASTER_LCPREQ_Received;
+ Uint32 m_MASTER_LCPREQ_FailedNodeId;
+ } c_lcpState;
+
+ /*------------------------------------------------------------------------*/
+ /* THIS VARIABLE KEEPS TRACK OF HOW MANY TABLES ARE ACTIVATED WHEN */
+ /* STARTING A LOCAL CHECKPOINT WE SHOULD AVOID STARTING A CHECKPOINT*/
+ /* WHEN NO TABLES ARE ACTIVATED. */
+ /*------------------------------------------------------------------------*/
+ Uint32 cnoOfActiveTables;
+ Uint32 cgcpDelay; /* Delay between global checkpoints */
+
+ BlockReference cdictblockref; /* DICTIONARY BLOCK REFERENCE */
+ Uint32 cfailurenr; /* EVERY TIME WHEN A NODE FAILURE IS REPORTED
+ THIS NUMBER IS INCREMENTED. AT THE START OF
+ THE SYSTEM THIS NUMBER MUST BE INITIATED TO
+ ZERO */
+ bool cgckptflag; /* A FLAG WHICH IS SET WHILE A NEW GLOBAL CHECK
+ POINT IS BEING CREATED. NO VERIFICATION IS ALLOWED
+ IF THE FLAG IS SET*/
+ Uint32 cgcpOrderBlocked;
+ BlockReference clocallqhblockref;
+ BlockReference clocaltcblockref;
+ BlockReference cmasterdihref;
+ Uint16 cownNodeId;
+ Uint32 cnewgcp;
+ BlockReference cndbStartReqBlockref;
+ BlockReference cntrlblockref;
+ Uint32 cgcpSameCounter;
+ Uint32 coldgcp;
+ Uint32 con_lineNodes;
+ Uint32 creceivedfrag;
+ Uint32 cremainingfrags;
+ Uint32 cstarttype;
+ Uint32 csystemnodes;
+ Uint32 currentgcp;
+
+ enum GcpMasterTakeOverState {
+ GMTOS_IDLE = 0,
+ GMTOS_INITIAL = 1,
+ ALL_READY = 2,
+ ALL_PREPARED = 3,
+ COMMIT_STARTED_NOT_COMPLETED = 4,
+ COMMIT_COMPLETED = 5,
+ PREPARE_STARTED_NOT_COMMITTED = 6,
+ SAVE_STARTED_NOT_COMPLETED = 7
+ };
+ GcpMasterTakeOverState cgcpMasterTakeOverState;
+
+public:
+ enum LcpMasterTakeOverState {
+ LMTOS_IDLE = 0,
+ LMTOS_WAIT_EMPTY_LCP = 1, // Currently doing empty LCP
+ LMTOS_WAIT_LCP_FRAG_REP = 2,// Currently waiting for outst. LCP_FRAG_REP
+ LMTOS_INITIAL = 3,
+ LMTOS_ALL_IDLE = 4,
+ LMTOS_ALL_ACTIVE = 5,
+ LMTOS_LCP_CONCLUDING = 6,
+ LMTOS_COPY_ONGOING = 7
+ };
+private:
+ class MasterTakeOverState {
+ public:
+ void set(LcpMasterTakeOverState s, Uint32 line) {
+ state = s; updatePlace = line;
+ }
+
+ LcpMasterTakeOverState state;
+ Uint32 updatePlace;
+
+ Uint32 minTableId;
+ Uint32 minFragId;
+ Uint32 failedNodeId;
+ } c_lcpMasterTakeOverState;
+
+ Uint16 cmasterNodeId;
+ Uint8 cnoHotSpare;
+
+ struct NodeStartMasterRecord {
+ Uint32 startNode;
+ Uint32 wait;
+ Uint32 failNr;
+ bool activeState;
+ bool blockLcp;
+ bool blockGcp;
+ Uint32 startInfoErrorCode;
+ Uint32 m_outstandingGsn;
+ };
+ NodeStartMasterRecord c_nodeStartMaster;
+
+ struct NodeStartSlaveRecord {
+ NodeStartSlaveRecord() { nodeId = 0;}
+
+ Uint32 nodeId;
+ };
+ NodeStartSlaveRecord c_nodeStartSlave;
+
+ Uint32 cfirstAliveNode;
+ Uint32 cfirstDeadNode;
+ Uint32 cstartPhase;
+ Uint32 cnoReplicas;
+
+ Uint32 c_startToLock;
+ Uint32 c_endToLock;
+ Uint32 c_createFragmentLock;
+ Uint32 c_updateToLock;
+
+ bool cwaitLcpSr;
+ Uint32 cnoOfNodeGroups;
+ bool cstartGcpNow;
+
+ Uint32 crestartGci; /* VALUE OF GCI WHEN SYSTEM RESTARTED OR STARTED */
+ Uint32 cminHotSpareNodes;
+
+ /**
+ * Counter variables keeping track of the number of outstanding signals
+ * for particular signals in various protocols.
+ */
+ SignalCounter c_COPY_GCIREQ_Counter;
+ SignalCounter c_COPY_TABREQ_Counter;
+ SignalCounter c_CREATE_FRAGREQ_Counter;
+ SignalCounter c_DIH_SWITCH_REPLICA_REQ_Counter;
+ SignalCounter c_EMPTY_LCP_REQ_Counter;
+ SignalCounter c_END_TOREQ_Counter;
+ SignalCounter c_GCP_COMMIT_Counter;
+ SignalCounter c_GCP_PREPARE_Counter;
+ SignalCounter c_GCP_SAVEREQ_Counter;
+ SignalCounter c_INCL_NODEREQ_Counter;
+ SignalCounter c_MASTER_GCPREQ_Counter;
+ SignalCounter c_MASTER_LCPREQ_Counter;
+ SignalCounter c_START_INFOREQ_Counter;
+ SignalCounter c_START_RECREQ_Counter;
+ SignalCounter c_START_TOREQ_Counter;
+ SignalCounter c_STOP_ME_REQ_Counter;
+ SignalCounter c_TC_CLOPSIZEREQ_Counter;
+ SignalCounter c_TCGETOPSIZEREQ_Counter;
+ SignalCounter c_UPDATE_TOREQ_Counter;
+ SignalCounter c_START_LCP_REQ_Counter;
+
+ bool c_blockCommit;
+ Uint32 c_blockCommitNo;
+
+ bool getBlockCommit() const {
+ return c_blockCommit || cgckptflag;
+ }
+
+ /**
+ * SwitchReplicaRecord - Should only be used by master
+ */
+ struct SwitchReplicaRecord {
+ void clear(){}
+
+ Uint32 nodeId;
+ Uint32 tableId;
+ Uint32 fragNo;
+ };
+ SwitchReplicaRecord c_switchReplicas;
+
+ struct StopPermProxyRecord {
+ StopPermProxyRecord() { clientRef = 0; }
+
+ Uint32 clientData;
+ BlockReference clientRef;
+ BlockReference masterRef;
+ };
+
+ struct StopPermMasterRecord {
+ StopPermMasterRecord() { clientRef = 0;}
+
+ Uint32 returnValue;
+
+ Uint32 clientData;
+ BlockReference clientRef;
+ };
+
+ StopPermProxyRecord c_stopPermProxy;
+ StopPermMasterRecord c_stopPermMaster;
+
+ void checkStopPermProxy(Signal*, NodeId failedNodeId);
+ void checkStopPermMaster(Signal*, NodeRecordPtr failedNodePtr);
+
+ void switchReplica(Signal*,
+ Uint32 nodeId,
+ Uint32 tableId,
+ Uint32 fragNo);
+
+ void switchReplicaReply(Signal*, NodeId nodeId);
+
+ /**
+ * Wait GCP (proxy)
+ */
+ struct WaitGCPProxyRecord {
+ WaitGCPProxyRecord() { clientRef = 0;}
+
+ Uint32 clientData;
+ BlockReference clientRef;
+ BlockReference masterRef;
+
+ union { Uint32 nextPool; Uint32 nextList; };
+ Uint32 prevList;
+ };
+ typedef Ptr<WaitGCPProxyRecord> WaitGCPProxyPtr;
+
+ /**
+ * Wait GCP (master)
+ */
+ struct WaitGCPMasterRecord {
+ WaitGCPMasterRecord() { clientRef = 0;}
+ Uint32 clientData;
+ BlockReference clientRef;
+
+ union { Uint32 nextPool; Uint32 nextList; };
+ Uint32 prevList;
+ };
+ typedef Ptr<WaitGCPMasterRecord> WaitGCPMasterPtr;
+
+ /**
+ * Pool/list of WaitGCPProxyRecord record
+ */
+ ArrayPool<WaitGCPProxyRecord> waitGCPProxyPool;
+ ArrayList<WaitGCPProxyRecord> c_waitGCPProxyList;
+
+ /**
+ * Pool/list of WaitGCPMasterRecord record
+ */
+ ArrayPool<WaitGCPMasterRecord> waitGCPMasterPool;
+ ArrayList<WaitGCPMasterRecord> c_waitGCPMasterList;
+
+ void checkWaitGCPProxy(Signal*, NodeId failedNodeId);
+ void checkWaitGCPMaster(Signal*, NodeId failedNodeId);
+ void emptyWaitGCPMasterQueue(Signal*);
+
+ /**
+ * Stop me
+ */
+ struct StopMeRecord {
+ StopMeRecord() { clientRef = 0;}
+
+ BlockReference clientRef;
+ Uint32 clientData;
+ };
+ StopMeRecord c_stopMe;
+
+ void checkStopMe(Signal *, NodeRecordPtr failedNodePtr);
+
+#define DIH_CDATA_SIZE 128
+ /**
+ * This variable must be atleast the size of Sysfile::SYSFILE_SIZE32
+ */
+ Uint32 cdata[DIH_CDATA_SIZE]; /* TEMPORARY ARRAY VARIABLE */
+
+ /**
+ * Sys file data
+ */
+ Uint32 sysfileData[DIH_CDATA_SIZE];
+ Uint32 sysfileDataToFile[DIH_CDATA_SIZE];
+
+ /**
+ * When a node comes up without filesystem
+ * we have to clear all LCP for that node
+ */
+ void invalidateNodeLCP(Signal *, Uint32 nodeId, Uint32 tableId);
+ void invalidateNodeLCP(Signal *, Uint32 nodeId, TabRecordPtr);
+
+ /**
+ * Reply from nodeId
+ */
+ void startInfoReply(Signal *, Uint32 nodeId);
+};
+
+#if (DIH_CDATA_SIZE < _SYSFILE_SIZE32)
+#error "cdata is to small compared to Sysfile size"
+#endif
+
+#endif
+
diff --git a/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp b/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp
new file mode 100644
index 00000000000..9a5efebc56e
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp
@@ -0,0 +1,319 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBDIH_C
+#include "Dbdih.hpp"
+#include <ndb_limits.h>
+
+#define DEBUG(x) { ndbout << "DIH::" << x << endl; }
+
+void Dbdih::initData()
+{
+ cpageFileSize = ZPAGEREC;
+
+ apiConnectRecord = 0;
+ connectRecord = 0;
+ fileRecord = 0;
+ fragmentstore = 0;
+ pageRecord = 0;
+ replicaRecord = 0;
+ tabRecord = 0;
+ takeOverRecord = 0;
+ createReplicaRecord = 0;
+ nodeGroupRecord = 0;
+ nodeRecord = 0;
+ c_nextNodeGroup = 0;
+
+ // Records with constant sizes
+ createReplicaRecord = (CreateReplicaRecord*)
+ allocRecord("CreateReplicaRecord", sizeof(CreateReplicaRecord),
+ ZCREATE_REPLICA_FILE_SIZE);
+
+ nodeGroupRecord = (NodeGroupRecord*)
+ allocRecord("NodeGroupRecord", sizeof(NodeGroupRecord), MAX_NDB_NODES);
+
+ nodeRecord = (NodeRecord*)
+ allocRecord("NodeRecord", sizeof(NodeRecord), MAX_NDB_NODES);
+
+ Uint32 i;
+ for(i = 0; i<MAX_NDB_NODES; i++){
+ new (&nodeRecord[i]) NodeRecord();
+ }
+
+ takeOverRecord = (TakeOverRecord*)allocRecord("TakeOverRecord",
+ sizeof(TakeOverRecord),
+ MAX_NDB_NODES);
+ for(i = 0; i<MAX_NDB_NODES; i++)
+ new (&takeOverRecord[i]) TakeOverRecord();
+
+ for(i = 0; i<MAX_NDB_NODES; i++)
+ new (&takeOverRecord[i]) TakeOverRecord();
+
+ waitGCPProxyPool.setSize(ZPROXY_FILE_SIZE);
+ waitGCPMasterPool.setSize(ZPROXY_MASTER_FILE_SIZE);
+
+ cgcpOrderBlocked = 0;
+ c_lcpState.ctcCounter = 0;
+ cwaitLcpSr = false;
+ c_blockCommit = false;
+ c_blockCommitNo = 1;
+}//Dbdih::initData()
+
+void Dbdih::initRecords()
+{
+ // Records with dynamic sizes
+ apiConnectRecord = (ApiConnectRecord*)
+ allocRecord("ApiConnectRecord",
+ sizeof(ApiConnectRecord),
+ capiConnectFileSize);
+
+ connectRecord = (ConnectRecord*)allocRecord("ConnectRecord",
+ sizeof(ConnectRecord),
+ cconnectFileSize);
+
+ fileRecord = (FileRecord*)allocRecord("FileRecord",
+ sizeof(FileRecord),
+ cfileFileSize);
+
+ fragmentstore = (Fragmentstore*)allocRecord("Fragmentstore",
+ sizeof(Fragmentstore),
+ cfragstoreFileSize);
+
+ pageRecord = (PageRecord*)allocRecord("PageRecord",
+ sizeof(PageRecord),
+ cpageFileSize);
+
+ replicaRecord = (ReplicaRecord*)allocRecord("ReplicaRecord",
+ sizeof(ReplicaRecord),
+ creplicaFileSize);
+
+ tabRecord = (TabRecord*)allocRecord("TabRecord",
+ sizeof(TabRecord),
+ ctabFileSize);
+
+ // Initialize BAT for interface to file system
+ NewVARIABLE* bat = allocateBat(22);
+ bat[1].WA = &pageRecord->word[0];
+ bat[1].nrr = cpageFileSize;
+ bat[1].ClusterSize = sizeof(PageRecord);
+ bat[1].bits.q = 11;
+ bat[1].bits.v = 5;
+ bat[20].WA = &sysfileData[0];
+ bat[20].nrr = 1;
+ bat[20].ClusterSize = sizeof(sysfileData);
+ bat[20].bits.q = 7;
+ bat[20].bits.v = 5;
+ bat[21].WA = &sysfileDataToFile[0];
+ bat[21].nrr = 1;
+ bat[21].ClusterSize = sizeof(sysfileDataToFile);
+ bat[21].bits.q = 7;
+ bat[21].bits.v = 5;
+}//Dbdih::initRecords()
+
+Dbdih::Dbdih(const class Configuration & config):
+ SimulatedBlock(DBDIH, config),
+ c_waitGCPProxyList(waitGCPProxyPool),
+ c_waitGCPMasterList(waitGCPMasterPool)
+{
+ BLOCK_CONSTRUCTOR(Dbdih);
+
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbdih::execDUMP_STATE_ORD);
+ addRecSignal(GSN_NDB_TAMPER, &Dbdih::execNDB_TAMPER, true);
+ addRecSignal(GSN_DEBUG_SIG, &Dbdih::execDEBUG_SIG);
+ addRecSignal(GSN_MASTER_GCPREQ, &Dbdih::execMASTER_GCPREQ);
+ addRecSignal(GSN_MASTER_GCPREF, &Dbdih::execMASTER_GCPREF);
+ addRecSignal(GSN_MASTER_GCPCONF, &Dbdih::execMASTER_GCPCONF);
+ addRecSignal(GSN_EMPTY_LCP_CONF, &Dbdih::execEMPTY_LCP_CONF);
+ addRecSignal(GSN_MASTER_LCPREQ, &Dbdih::execMASTER_LCPREQ);
+ addRecSignal(GSN_MASTER_LCPREF, &Dbdih::execMASTER_LCPREF);
+ addRecSignal(GSN_MASTER_LCPCONF, &Dbdih::execMASTER_LCPCONF);
+ addRecSignal(GSN_NF_COMPLETEREP, &Dbdih::execNF_COMPLETEREP);
+ addRecSignal(GSN_START_PERMREQ, &Dbdih::execSTART_PERMREQ);
+ addRecSignal(GSN_START_PERMCONF, &Dbdih::execSTART_PERMCONF);
+ addRecSignal(GSN_START_PERMREF, &Dbdih::execSTART_PERMREF);
+ addRecSignal(GSN_INCL_NODEREQ, &Dbdih::execINCL_NODEREQ);
+ addRecSignal(GSN_INCL_NODECONF, &Dbdih::execINCL_NODECONF);
+ addRecSignal(GSN_END_TOREQ, &Dbdih::execEND_TOREQ);
+ addRecSignal(GSN_END_TOCONF, &Dbdih::execEND_TOCONF);
+ addRecSignal(GSN_START_TOREQ, &Dbdih::execSTART_TOREQ);
+ addRecSignal(GSN_START_TOCONF, &Dbdih::execSTART_TOCONF);
+ addRecSignal(GSN_START_MEREQ, &Dbdih::execSTART_MEREQ);
+ addRecSignal(GSN_START_MECONF, &Dbdih::execSTART_MECONF);
+ addRecSignal(GSN_START_MEREF, &Dbdih::execSTART_MEREF);
+ addRecSignal(GSN_START_COPYREQ, &Dbdih::execSTART_COPYREQ);
+ addRecSignal(GSN_START_COPYCONF, &Dbdih::execSTART_COPYCONF);
+ addRecSignal(GSN_START_COPYREF, &Dbdih::execSTART_COPYREF);
+ addRecSignal(GSN_CREATE_FRAGREQ, &Dbdih::execCREATE_FRAGREQ);
+ addRecSignal(GSN_CREATE_FRAGCONF, &Dbdih::execCREATE_FRAGCONF);
+ addRecSignal(GSN_DIVERIFYREQ, &Dbdih::execDIVERIFYREQ);
+ addRecSignal(GSN_GCP_SAVECONF, &Dbdih::execGCP_SAVECONF);
+ addRecSignal(GSN_GCP_PREPARECONF, &Dbdih::execGCP_PREPARECONF);
+ addRecSignal(GSN_GCP_PREPARE, &Dbdih::execGCP_PREPARE);
+ addRecSignal(GSN_GCP_NODEFINISH, &Dbdih::execGCP_NODEFINISH);
+ addRecSignal(GSN_GCP_COMMIT, &Dbdih::execGCP_COMMIT);
+ addRecSignal(GSN_DIHNDBTAMPER, &Dbdih::execDIHNDBTAMPER);
+ addRecSignal(GSN_CONTINUEB, &Dbdih::execCONTINUEB);
+ addRecSignal(GSN_COPY_GCIREQ, &Dbdih::execCOPY_GCIREQ);
+ addRecSignal(GSN_COPY_GCICONF, &Dbdih::execCOPY_GCICONF);
+ addRecSignal(GSN_COPY_TABREQ, &Dbdih::execCOPY_TABREQ);
+ addRecSignal(GSN_COPY_TABCONF, &Dbdih::execCOPY_TABCONF);
+ addRecSignal(GSN_TCGETOPSIZECONF, &Dbdih::execTCGETOPSIZECONF);
+ addRecSignal(GSN_TC_CLOPSIZECONF, &Dbdih::execTC_CLOPSIZECONF);
+
+ addRecSignal(GSN_LCP_COMPLETE_REP, &Dbdih::execLCP_COMPLETE_REP);
+ addRecSignal(GSN_LCP_FRAG_REP, &Dbdih::execLCP_FRAG_REP);
+ addRecSignal(GSN_START_LCP_REQ, &Dbdih::execSTART_LCP_REQ);
+ addRecSignal(GSN_START_LCP_CONF, &Dbdih::execSTART_LCP_CONF);
+
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbdih::execREAD_CONFIG_REQ, true);
+ addRecSignal(GSN_UNBLO_DICTCONF, &Dbdih::execUNBLO_DICTCONF);
+ addRecSignal(GSN_COPY_ACTIVECONF, &Dbdih::execCOPY_ACTIVECONF);
+ addRecSignal(GSN_TAB_COMMITREQ, &Dbdih::execTAB_COMMITREQ);
+ addRecSignal(GSN_NODE_FAILREP, &Dbdih::execNODE_FAILREP);
+ addRecSignal(GSN_COPY_FRAGCONF, &Dbdih::execCOPY_FRAGCONF);
+ addRecSignal(GSN_COPY_FRAGREF, &Dbdih::execCOPY_FRAGREF);
+ addRecSignal(GSN_DIADDTABREQ, &Dbdih::execDIADDTABREQ);
+ addRecSignal(GSN_DIGETNODESREQ, &Dbdih::execDIGETNODESREQ);
+ addRecSignal(GSN_DIRELEASEREQ, &Dbdih::execDIRELEASEREQ);
+ addRecSignal(GSN_DISEIZEREQ, &Dbdih::execDISEIZEREQ);
+ addRecSignal(GSN_STTOR, &Dbdih::execSTTOR);
+ addRecSignal(GSN_DI_FCOUNTREQ, &Dbdih::execDI_FCOUNTREQ);
+ addRecSignal(GSN_DIGETPRIMREQ, &Dbdih::execDIGETPRIMREQ);
+ addRecSignal(GSN_GCP_SAVEREF, &Dbdih::execGCP_SAVEREF);
+ addRecSignal(GSN_GCP_TCFINISHED, &Dbdih::execGCP_TCFINISHED);
+ addRecSignal(GSN_READ_NODESCONF, &Dbdih::execREAD_NODESCONF);
+ addRecSignal(GSN_NDB_STTOR, &Dbdih::execNDB_STTOR);
+ addRecSignal(GSN_DICTSTARTCONF, &Dbdih::execDICTSTARTCONF);
+ addRecSignal(GSN_NDB_STARTREQ, &Dbdih::execNDB_STARTREQ);
+ addRecSignal(GSN_GETGCIREQ, &Dbdih::execGETGCIREQ);
+ addRecSignal(GSN_DIH_RESTARTREQ, &Dbdih::execDIH_RESTARTREQ);
+ addRecSignal(GSN_START_RECCONF, &Dbdih::execSTART_RECCONF);
+ addRecSignal(GSN_START_FRAGCONF, &Dbdih::execSTART_FRAGCONF);
+ addRecSignal(GSN_ADD_FRAGCONF, &Dbdih::execADD_FRAGCONF);
+ addRecSignal(GSN_ADD_FRAGREF, &Dbdih::execADD_FRAGREF);
+ addRecSignal(GSN_FSOPENCONF, &Dbdih::execFSOPENCONF);
+ addRecSignal(GSN_FSOPENREF, &Dbdih::execFSOPENREF);
+ addRecSignal(GSN_FSCLOSECONF, &Dbdih::execFSCLOSECONF);
+ addRecSignal(GSN_FSCLOSEREF, &Dbdih::execFSCLOSEREF);
+ addRecSignal(GSN_FSREADCONF, &Dbdih::execFSREADCONF);
+ addRecSignal(GSN_FSREADREF, &Dbdih::execFSREADREF);
+ addRecSignal(GSN_FSWRITECONF, &Dbdih::execFSWRITECONF);
+ addRecSignal(GSN_FSWRITEREF, &Dbdih::execFSWRITEREF);
+ addRecSignal(GSN_SET_VAR_REQ, &Dbdih::execSET_VAR_REQ);
+
+ addRecSignal(GSN_START_INFOREQ,
+ &Dbdih::execSTART_INFOREQ);
+ addRecSignal(GSN_START_INFOREF,
+ &Dbdih::execSTART_INFOREF);
+ addRecSignal(GSN_START_INFOCONF,
+ &Dbdih::execSTART_INFOCONF);
+
+ addRecSignal(GSN_CHECKNODEGROUPSREQ, &Dbdih::execCHECKNODEGROUPSREQ);
+
+ addRecSignal(GSN_BLOCK_COMMIT_ORD,
+ &Dbdih::execBLOCK_COMMIT_ORD);
+ addRecSignal(GSN_UNBLOCK_COMMIT_ORD,
+ &Dbdih::execUNBLOCK_COMMIT_ORD);
+
+ addRecSignal(GSN_DIH_SWITCH_REPLICA_REQ,
+ &Dbdih::execDIH_SWITCH_REPLICA_REQ);
+
+ addRecSignal(GSN_DIH_SWITCH_REPLICA_REF,
+ &Dbdih::execDIH_SWITCH_REPLICA_REF);
+
+ addRecSignal(GSN_DIH_SWITCH_REPLICA_CONF,
+ &Dbdih::execDIH_SWITCH_REPLICA_CONF);
+
+ addRecSignal(GSN_STOP_PERM_REQ, &Dbdih::execSTOP_PERM_REQ);
+ addRecSignal(GSN_STOP_PERM_REF, &Dbdih::execSTOP_PERM_REF);
+ addRecSignal(GSN_STOP_PERM_CONF, &Dbdih::execSTOP_PERM_CONF);
+
+ addRecSignal(GSN_STOP_ME_REQ, &Dbdih::execSTOP_ME_REQ);
+ addRecSignal(GSN_STOP_ME_REF, &Dbdih::execSTOP_ME_REF);
+ addRecSignal(GSN_STOP_ME_CONF, &Dbdih::execSTOP_ME_CONF);
+
+ addRecSignal(GSN_WAIT_GCP_REQ, &Dbdih::execWAIT_GCP_REQ);
+ addRecSignal(GSN_WAIT_GCP_REF, &Dbdih::execWAIT_GCP_REF);
+ addRecSignal(GSN_WAIT_GCP_CONF, &Dbdih::execWAIT_GCP_CONF);
+
+ addRecSignal(GSN_UPDATE_TOREQ, &Dbdih::execUPDATE_TOREQ);
+ addRecSignal(GSN_UPDATE_TOCONF, &Dbdih::execUPDATE_TOCONF);
+
+ addRecSignal(GSN_PREP_DROP_TAB_REQ, &Dbdih::execPREP_DROP_TAB_REQ);
+ addRecSignal(GSN_WAIT_DROP_TAB_REF, &Dbdih::execWAIT_DROP_TAB_REF);
+ addRecSignal(GSN_WAIT_DROP_TAB_CONF, &Dbdih::execWAIT_DROP_TAB_CONF);
+ addRecSignal(GSN_DROP_TAB_REQ, &Dbdih::execDROP_TAB_REQ);
+
+ addRecSignal(GSN_ALTER_TAB_REQ, &Dbdih::execALTER_TAB_REQ);
+
+ addRecSignal(GSN_CREATE_FRAGMENTATION_REQ,
+ &Dbdih::execCREATE_FRAGMENTATION_REQ);
+
+ initData();
+}//Dbdih::Dbdih()
+
+Dbdih::~Dbdih()
+{
+ deallocRecord((void **)&apiConnectRecord, "ApiConnectRecord",
+ sizeof(ApiConnectRecord),
+ capiConnectFileSize);
+
+ deallocRecord((void **)&connectRecord, "ConnectRecord",
+ sizeof(ConnectRecord),
+ cconnectFileSize);
+
+ deallocRecord((void **)&fileRecord, "FileRecord",
+ sizeof(FileRecord),
+ cfileFileSize);
+
+ deallocRecord((void **)&fragmentstore, "Fragmentstore",
+ sizeof(Fragmentstore),
+ cfragstoreFileSize);
+
+ deallocRecord((void **)&pageRecord, "PageRecord",
+ sizeof(PageRecord),
+ cpageFileSize);
+
+ deallocRecord((void **)&replicaRecord, "ReplicaRecord",
+ sizeof(ReplicaRecord),
+ creplicaFileSize);
+
+ deallocRecord((void **)&tabRecord, "TabRecord",
+ sizeof(TabRecord),
+ ctabFileSize);
+
+ // Records with constant sizes
+ deallocRecord((void **)&createReplicaRecord,
+ "CreateReplicaRecord", sizeof(CreateReplicaRecord),
+ ZCREATE_REPLICA_FILE_SIZE);
+
+ deallocRecord((void **)&nodeGroupRecord, "NodeGroupRecord",
+ sizeof(NodeGroupRecord), MAX_NDB_NODES);
+
+ deallocRecord((void **)&nodeRecord, "NodeRecord",
+ sizeof(NodeRecord), MAX_NDB_NODES);
+
+ deallocRecord((void **)&takeOverRecord, "TakeOverRecord",
+ sizeof(TakeOverRecord),
+ MAX_NDB_NODES);
+
+}//Dbdih::~Dbdih()
+
+BLOCK_FUNCTIONS(Dbdih)
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
new file mode 100644
index 00000000000..af75707560a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
@@ -0,0 +1,14272 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBDIH_C
+#include <ndb_limits.h>
+#include <ndb_version.h>
+#include <NdbOut.hpp>
+
+#include "Dbdih.hpp"
+#include "Configuration.hpp"
+
+#include <signaldata/BlockCommitOrd.hpp>
+#include <signaldata/CheckNodeGroups.hpp>
+#include <signaldata/CreateFrag.hpp>
+#include <signaldata/CopyActive.hpp>
+#include <signaldata/CopyFrag.hpp>
+#include <signaldata/CopyGCIReq.hpp>
+#include <signaldata/DiAddTab.hpp>
+#include <signaldata/DictStart.hpp>
+#include <signaldata/DiGetNodes.hpp>
+#include <signaldata/DihContinueB.hpp>
+#include <signaldata/DihSwitchReplica.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/EmptyLcp.hpp>
+#include <signaldata/EndTo.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/GCPSave.hpp>
+#include <signaldata/HotSpareRep.hpp>
+#include <signaldata/MasterGCP.hpp>
+#include <signaldata/MasterLCP.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/StartFragReq.hpp>
+#include <signaldata/StartInfo.hpp>
+#include <signaldata/StartMe.hpp>
+#include <signaldata/StartPerm.hpp>
+#include <signaldata/StartRec.hpp>
+#include <signaldata/StartTo.hpp>
+#include <signaldata/StopPerm.hpp>
+#include <signaldata/StopMe.hpp>
+#include <signaldata/TestOrd.hpp>
+#include <signaldata/UpdateTo.hpp>
+#include <signaldata/WaitGCP.hpp>
+#include <signaldata/DihStartTab.hpp>
+#include <signaldata/LCP.hpp>
+#include <signaldata/SystemError.hpp>
+
+#include <signaldata/DropTab.hpp>
+#include <signaldata/AlterTab.hpp>
+#include <signaldata/PrepDropTab.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/CreateFragmentation.hpp>
+#include <signaldata/LqhFrag.hpp>
+#include <signaldata/FsOpenReq.hpp>
+#include <DebuggerNames.hpp>
+
+#define SYSFILE ((Sysfile *)&sysfileData[0])
+
+#define RETURN_IF_NODE_NOT_ALIVE(node) \
+ if (!checkNodeAlive((node))) { \
+ jam(); \
+ return; \
+ } \
+
+#define RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverIndex, regTOPtr) \
+ regTOPtr.i = takeOverIndex; \
+ ptrCheckGuard(regTOPtr, MAX_NDB_NODES, takeOverRecord); \
+ if (checkToInterrupted(regTOPtr)) { \
+ jam(); \
+ return; \
+ } \
+
+#define receiveLoopMacro(sigName, receiveNodeId)\
+{ \
+ c_##sigName##_Counter.clearWaitingFor(receiveNodeId); \
+ if(c_##sigName##_Counter.done() == false){ \
+ jam(); \
+ return; \
+ } \
+}
+
+#define sendLoopMacro(sigName, signalRoutine) \
+{ \
+ c_##sigName##_Counter.clearWaitingFor(); \
+ NodeRecordPtr specNodePtr; \
+ specNodePtr.i = cfirstAliveNode; \
+ do { \
+ jam(); \
+ ptrCheckGuard(specNodePtr, MAX_NDB_NODES, nodeRecord); \
+ c_##sigName##_Counter.setWaitingFor(specNodePtr.i); \
+ signalRoutine(signal, specNodePtr.i); \
+ specNodePtr.i = specNodePtr.p->nextNode; \
+ } while (specNodePtr.i != RNIL); \
+}
+
+static
+Uint32
+prevLcpNo(Uint32 lcpNo){
+ if(lcpNo == 0)
+ return MAX_LCP_STORED - 1;
+ return lcpNo - 1;
+}
+
+static
+Uint32
+nextLcpNo(Uint32 lcpNo){
+ lcpNo++;
+ if(lcpNo == MAX_LCP_STORED)
+ return 0;
+ return lcpNo;
+}
+
+#define gth(x, y) ndbrequire(((int)x)>((int)y))
+
+void Dbdih::nullRoutine(Signal* signal, Uint32 nodeId)
+{
+}//Dbdih::nullRoutine()
+
+void Dbdih::sendCOPY_GCIREQ(Signal* signal, Uint32 nodeId)
+{
+ ndbrequire(c_copyGCIMaster.m_copyReason != CopyGCIReq::IDLE);
+
+ const BlockReference ref = calcDihBlockRef(nodeId);
+ const Uint32 wordPerSignal = CopyGCIReq::DATA_SIZE;
+ const Uint32 noOfSignals = ((Sysfile::SYSFILE_SIZE32 + (wordPerSignal - 1)) /
+ wordPerSignal);
+
+ CopyGCIReq * const copyGCI = (CopyGCIReq *)&signal->theData[0];
+ copyGCI->anyData = nodeId;
+ copyGCI->copyReason = c_copyGCIMaster.m_copyReason;
+ copyGCI->startWord = 0;
+
+ for(Uint32 i = 0; i < noOfSignals; i++) {
+ jam();
+ { // Do copy
+ const int startWord = copyGCI->startWord;
+ for(Uint32 j = 0; j < wordPerSignal; j++) {
+ copyGCI->data[j] = sysfileData[j+startWord];
+ }//for
+ }
+ sendSignal(ref, GSN_COPY_GCIREQ, signal, 25, JBB);
+ copyGCI->startWord += wordPerSignal;
+ }//for
+}//Dbdih::sendCOPY_GCIREQ()
+
+
+void Dbdih::sendDIH_SWITCH_REPLICA_REQ(Signal* signal, Uint32 nodeId)
+{
+ const BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_DIH_SWITCH_REPLICA_REQ, signal,
+ DihSwitchReplicaReq::SignalLength, JBB);
+}//Dbdih::sendDIH_SWITCH_REPLICA_REQ()
+
+void Dbdih::sendEMPTY_LCP_REQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcLqhBlockRef(nodeId);
+ sendSignal(ref, GSN_EMPTY_LCP_REQ, signal, EmptyLcpReq::SignalLength, JBB);
+}//Dbdih::sendEMPTY_LCPREQ()
+
+void Dbdih::sendEND_TOREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_END_TOREQ, signal, EndToReq::SignalLength, JBB);
+}//Dbdih::sendEND_TOREQ()
+
+void Dbdih::sendGCP_COMMIT(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcDihBlockRef(nodeId);
+ signal->theData[0] = cownNodeId;
+ signal->theData[1] = cnewgcp;
+ sendSignal(ref, GSN_GCP_COMMIT, signal, 2, JBA);
+}//Dbdih::sendGCP_COMMIT()
+
+void Dbdih::sendGCP_PREPARE(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcDihBlockRef(nodeId);
+ signal->theData[0] = cownNodeId;
+ signal->theData[1] = cnewgcp;
+ sendSignal(ref, GSN_GCP_PREPARE, signal, 2, JBA);
+}//Dbdih::sendGCP_PREPARE()
+
+void Dbdih::sendGCP_SAVEREQ(Signal* signal, Uint32 nodeId)
+{
+ GCPSaveReq * const saveReq = (GCPSaveReq*)&signal->theData[0];
+ BlockReference ref = calcLqhBlockRef(nodeId);
+ saveReq->dihBlockRef = reference();
+ saveReq->dihPtr = nodeId;
+ saveReq->gci = coldgcp;
+ sendSignal(ref, GSN_GCP_SAVEREQ, signal, GCPSaveReq::SignalLength, JBB);
+}//Dbdih::sendGCP_SAVEREQ()
+
+void Dbdih::sendINCL_NODEREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference nodeDihRef = calcDihBlockRef(nodeId);
+ signal->theData[0] = reference();
+ signal->theData[1] = c_nodeStartMaster.startNode;
+ signal->theData[2] = c_nodeStartMaster.failNr;
+ signal->theData[3] = 0;
+ signal->theData[4] = currentgcp;
+ sendSignal(nodeDihRef, GSN_INCL_NODEREQ, signal, 5, JBB);
+}//Dbdih::sendINCL_NODEREQ()
+
+void Dbdih::sendMASTER_GCPREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_MASTER_GCPREQ, signal, MasterGCPReq::SignalLength, JBB);
+}//Dbdih::sendMASTER_GCPREQ()
+
+void Dbdih::sendMASTER_LCPREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_MASTER_LCPREQ, signal, MasterLCPReq::SignalLength, JBB);
+}//Dbdih::sendMASTER_LCPREQ()
+
+void Dbdih::sendSTART_INFOREQ(Signal* signal, Uint32 nodeId)
+{
+ const BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_START_INFOREQ, signal, StartInfoReq::SignalLength, JBB);
+}//sendSTART_INFOREQ()
+
+void Dbdih::sendSTART_RECREQ(Signal* signal, Uint32 nodeId)
+{
+ StartRecReq * const req = (StartRecReq*)&signal->theData[0];
+ BlockReference ref = calcLqhBlockRef(nodeId);
+ req->receivingNodeId = nodeId;
+ req->senderRef = reference();
+ req->keepGci = SYSFILE->keepGCI;
+ req->lastCompletedGci = SYSFILE->lastCompletedGCI[nodeId];
+ req->newestGci = SYSFILE->newestRestorableGCI;
+ sendSignal(ref, GSN_START_RECREQ, signal, StartRecReq::SignalLength, JBB);
+
+ signal->theData[0] = NDB_LE_StartREDOLog;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = SYSFILE->keepGCI;
+ signal->theData[3] = SYSFILE->lastCompletedGCI[nodeId];
+ signal->theData[4] = SYSFILE->newestRestorableGCI;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 5, JBB);
+}//Dbdih::sendSTART_RECREQ()
+
+void Dbdih::sendSTART_TOREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_START_TOREQ, signal, StartToReq::SignalLength, JBB);
+}//Dbdih::sendSTART_TOREQ()
+
+void Dbdih::sendSTOP_ME_REQ(Signal* signal, Uint32 nodeId)
+{
+ if (nodeId != getOwnNodeId()) {
+ jam();
+ const BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_STOP_ME_REQ, signal, StopMeReq::SignalLength, JBB);
+ }//if
+}//Dbdih::sendSTOP_ME_REQ()
+
+void Dbdih::sendTC_CLOPSIZEREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcTcBlockRef(nodeId);
+ signal->theData[0] = nodeId;
+ signal->theData[1] = reference();
+ sendSignal(ref, GSN_TC_CLOPSIZEREQ, signal, 2, JBB);
+}//Dbdih::sendTC_CLOPSIZEREQ()
+
+void Dbdih::sendTCGETOPSIZEREQ(Signal* signal, Uint32 nodeId)
+{
+ BlockReference ref = calcTcBlockRef(nodeId);
+ signal->theData[0] = nodeId;
+ signal->theData[1] = reference();
+ sendSignal(ref, GSN_TCGETOPSIZEREQ, signal, 2, JBB);
+}//Dbdih::sendTCGETOPSIZEREQ()
+
+void Dbdih::sendUPDATE_TOREQ(Signal* signal, Uint32 nodeId)
+{
+ const BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_UPDATE_TOREQ, signal, UpdateToReq::SignalLength, JBB);
+}//sendUPDATE_TOREQ()
+
+void Dbdih::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ switch ((DihContinueB::Type)signal->theData[0]) {
+ case DihContinueB::ZPACK_TABLE_INTO_PAGES:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ packTableIntoPagesLab(signal, tableId);
+ return;
+ break;
+ }
+ case DihContinueB::ZPACK_FRAG_INTO_PAGES:
+ {
+ RWFragment wf;
+ jam();
+ wf.rwfTabPtr.i = signal->theData[1];
+ ptrCheckGuard(wf.rwfTabPtr, ctabFileSize, tabRecord);
+ wf.fragId = signal->theData[2];
+ wf.pageIndex = signal->theData[3];
+ wf.wordIndex = signal->theData[4];
+ packFragIntoPagesLab(signal, &wf);
+ return;
+ break;
+ }
+ case DihContinueB::ZREAD_PAGES_INTO_TABLE:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ readPagesIntoTableLab(signal, tableId);
+ return;
+ break;
+ }
+ case DihContinueB::ZREAD_PAGES_INTO_FRAG:
+ {
+ RWFragment rf;
+ jam();
+ rf.rwfTabPtr.i = signal->theData[1];
+ ptrCheckGuard(rf.rwfTabPtr, ctabFileSize, tabRecord);
+ rf.fragId = signal->theData[2];
+ rf.pageIndex = signal->theData[3];
+ rf.wordIndex = signal->theData[4];
+ readPagesIntoFragLab(signal, &rf);
+ return;
+ break;
+ }
+ case DihContinueB::ZCOPY_TABLE:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ copyTableLab(signal, tableId);
+ return;
+ }
+ case DihContinueB::ZCOPY_TABLE_NODE:
+ {
+ NodeRecordPtr nodePtr;
+ CopyTableNode ctn;
+ jam();
+ ctn.ctnTabPtr.i = signal->theData[1];
+ ptrCheckGuard(ctn.ctnTabPtr, ctabFileSize, tabRecord);
+ nodePtr.i = signal->theData[2];
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ ctn.pageIndex = signal->theData[3];
+ ctn.wordIndex = signal->theData[4];
+ ctn.noOfWords = signal->theData[5];
+ copyTableNode(signal, &ctn, nodePtr);
+ return;
+ }
+ case DihContinueB::ZSTART_FRAGMENT:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ Uint32 fragId = signal->theData[2];
+ startFragment(signal, tableId, fragId);
+ return;
+ }
+ case DihContinueB::ZCOMPLETE_RESTART:
+ jam();
+ completeRestartLab(signal);
+ return;
+ case DihContinueB::ZREAD_TABLE_FROM_PAGES:
+ {
+ TabRecordPtr tabPtr;
+ jam();
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ readTableFromPagesLab(signal, tabPtr);
+ return;
+ }
+ case DihContinueB::ZSR_PHASE2_READ_TABLE:
+ {
+ TabRecordPtr tabPtr;
+ jam();
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ srPhase2ReadTableLab(signal, tabPtr);
+ return;
+ }
+ case DihContinueB::ZCHECK_TC_COUNTER:
+ jam();
+#ifndef NO_LCP
+ checkTcCounterLab(signal);
+#endif
+ return;
+ case DihContinueB::ZCALCULATE_KEEP_GCI:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ Uint32 fragId = signal->theData[2];
+ calculateKeepGciLab(signal, tableId, fragId);
+ return;
+ }
+ case DihContinueB::ZSTORE_NEW_LCP_ID:
+ jam();
+ storeNewLcpIdLab(signal);
+ return;
+ case DihContinueB::ZTABLE_UPDATE:
+ {
+ TabRecordPtr tabPtr;
+ jam();
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tableUpdateLab(signal, tabPtr);
+ return;
+ }
+ case DihContinueB::ZCHECK_LCP_COMPLETED:
+ {
+ jam();
+ checkLcpCompletedLab(signal);
+ return;
+ }
+ case DihContinueB::ZINIT_LCP:
+ {
+ jam();
+ Uint32 senderRef = signal->theData[1];
+ Uint32 tableId = signal->theData[2];
+ initLcpLab(signal, senderRef, tableId);
+ return;
+ }
+ case DihContinueB::ZADD_TABLE_MASTER_PAGES:
+ {
+ TabRecordPtr tabPtr;
+ jam();
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tabPtr.p->tabUpdateState = TabRecord::US_ADD_TABLE_MASTER;
+ tableUpdateLab(signal, tabPtr);
+ return;
+ break;
+ }
+ case DihContinueB::ZDIH_ADD_TABLE_MASTER:
+ {
+ jam();
+ addTable_closeConf(signal, signal->theData[1]);
+ return;
+ }
+ case DihContinueB::ZADD_TABLE_SLAVE_PAGES:
+ {
+ TabRecordPtr tabPtr;
+ jam();
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tabPtr.p->tabUpdateState = TabRecord::US_ADD_TABLE_SLAVE;
+ tableUpdateLab(signal, tabPtr);
+ return;
+ }
+ case DihContinueB::ZDIH_ADD_TABLE_SLAVE:
+ {
+ ndbrequire(false);
+ return;
+ }
+ case DihContinueB::ZSTART_GCP:
+ jam();
+#ifndef NO_GCP
+ startGcpLab(signal, signal->theData[1]);
+#endif
+ return;
+ break;
+ case DihContinueB::ZCOPY_GCI:{
+ jam();
+ CopyGCIReq::CopyReason reason = (CopyGCIReq::CopyReason)signal->theData[1];
+ ndbrequire(c_copyGCIMaster.m_copyReason == reason);
+ sendLoopMacro(COPY_GCIREQ, sendCOPY_GCIREQ);
+ return;
+ }
+ break;
+ case DihContinueB::ZEMPTY_VERIFY_QUEUE:
+ jam();
+ emptyverificbuffer(signal, true);
+ return;
+ break;
+ case DihContinueB::ZCHECK_GCP_STOP:
+ jam();
+#ifndef NO_GCP
+ checkGcpStopLab(signal);
+#endif
+ return;
+ break;
+ case DihContinueB::ZREMOVE_NODE_FROM_TABLE:
+ {
+ jam();
+ Uint32 nodeId = signal->theData[1];
+ Uint32 tableId = signal->theData[2];
+ removeNodeFromTables(signal, nodeId, tableId);
+ return;
+ }
+ case DihContinueB::ZCOPY_NODE:
+ {
+ jam();
+ Uint32 tableId = signal->theData[1];
+ copyNodeLab(signal, tableId);
+ return;
+ }
+ case DihContinueB::ZSTART_TAKE_OVER:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ Uint32 startNode = signal->theData[2];
+ Uint32 toNode = signal->theData[3];
+ startTakeOver(signal, takeOverPtrI, startNode, toNode);
+ return;
+ break;
+ }
+ case DihContinueB::ZCHECK_START_TAKE_OVER:
+ jam();
+ checkStartTakeOver(signal);
+ break;
+ case DihContinueB::ZTO_START_COPY_FRAG:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ startNextCopyFragment(signal, takeOverPtrI);
+ return;
+ }
+ case DihContinueB::ZINVALIDATE_NODE_LCP:
+ {
+ jam();
+ const Uint32 nodeId = signal->theData[1];
+ const Uint32 tableId = signal->theData[2];
+ invalidateNodeLCP(signal, nodeId, tableId);
+ return;
+ }
+ case DihContinueB::ZINITIALISE_RECORDS:
+ jam();
+ initialiseRecordsLab(signal,
+ signal->theData[1],
+ signal->theData[2],
+ signal->theData[3]);
+ return;
+ break;
+ case DihContinueB::ZSTART_PERMREQ_AGAIN:
+ jam();
+ nodeRestartPh2Lab(signal);
+ return;
+ break;
+ case DihContinueB::SwitchReplica:
+ {
+ jam();
+ const Uint32 nodeId = signal->theData[1];
+ const Uint32 tableId = signal->theData[2];
+ const Uint32 fragNo = signal->theData[3];
+ switchReplica(signal, nodeId, tableId, fragNo);
+ return;
+ }
+ case DihContinueB::ZSEND_START_TO:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ sendStartTo(signal, takeOverPtrI);
+ return;
+ }
+ case DihContinueB::ZSEND_ADD_FRAG:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ toCopyFragLab(signal, takeOverPtrI);
+ return;
+ }
+ case DihContinueB::ZSEND_UPDATE_TO:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ Uint32 updateState = signal->theData[4];
+ sendUpdateTo(signal, takeOverPtrI, updateState);
+ return;
+ }
+ case DihContinueB::ZSEND_END_TO:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ sendEndTo(signal, takeOverPtrI);
+ return;
+ }
+ case DihContinueB::ZSEND_CREATE_FRAG:
+ {
+ jam();
+ Uint32 takeOverPtrI = signal->theData[1];
+ Uint32 storedType = signal->theData[2];
+ Uint32 startGci = signal->theData[3];
+ sendCreateFragReq(signal, startGci, storedType, takeOverPtrI);
+ return;
+ }
+ case DihContinueB::WAIT_DROP_TAB_WRITING_TO_FILE:{
+ jam();
+ TabRecordPtr tabPtr;
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ waitDropTabWritingToFile(signal, tabPtr);
+ return;
+ }
+ case DihContinueB::CHECK_WAIT_DROP_TAB_FAILED_LQH:{
+ jam();
+ Uint32 nodeId = signal->theData[1];
+ Uint32 tableId = signal->theData[2];
+ checkWaitDropTabFailedLqh(signal, nodeId, tableId);
+ return;
+ }
+ }//switch
+
+ ndbrequire(false);
+ return;
+}//Dbdih::execCONTINUEB()
+
+void Dbdih::execCOPY_GCIREQ(Signal* signal)
+{
+ CopyGCIReq * const copyGCI = (CopyGCIReq *)&signal->theData[0];
+ jamEntry();
+ CopyGCIReq::CopyReason reason = (CopyGCIReq::CopyReason)copyGCI->copyReason;
+ const Uint32 tstart = copyGCI->startWord;
+
+ ndbrequire(cmasterdihref == signal->senderBlockRef()) ;
+ ndbrequire(c_copyGCISlave.m_copyReason == CopyGCIReq::IDLE);
+ ndbrequire(c_copyGCISlave.m_expectedNextWord == tstart);
+ ndbrequire(reason != CopyGCIReq::IDLE);
+
+ arrGuard(tstart + CopyGCIReq::DATA_SIZE, sizeof(sysfileData)/4);
+ for(Uint32 i = 0; i<CopyGCIReq::DATA_SIZE; i++)
+ cdata[tstart+i] = copyGCI->data[i];
+
+ if ((tstart + CopyGCIReq::DATA_SIZE) >= Sysfile::SYSFILE_SIZE32) {
+ jam();
+ c_copyGCISlave.m_expectedNextWord = 0;
+ } else {
+ jam();
+ c_copyGCISlave.m_expectedNextWord += CopyGCIReq::DATA_SIZE;
+ return;
+ }//if
+
+ memcpy(sysfileData, cdata, sizeof(sysfileData));
+
+ c_copyGCISlave.m_copyReason = reason;
+ c_copyGCISlave.m_senderRef = signal->senderBlockRef();
+ c_copyGCISlave.m_senderData = copyGCI->anyData;
+
+ CRASH_INSERTION2(7020, reason==CopyGCIReq::LOCAL_CHECKPOINT);
+ CRASH_INSERTION2(7008, reason==CopyGCIReq::GLOBAL_CHECKPOINT);
+
+ /* -------------------------------------------------------------------------*/
+ /* WE SET THE REQUESTER OF THE COPY GCI TO THE CURRENT MASTER. IF THE */
+ /* CURRENT MASTER WE DO NOT WANT THE NEW MASTER TO RECEIVE CONFIRM OF */
+ /* SOMETHING HE HAS NOT SENT. THE TAKE OVER MUST BE CAREFUL. */
+ /* -------------------------------------------------------------------------*/
+ bool ok = false;
+ switch(reason){
+ case CopyGCIReq::IDLE:
+ ok = true;
+ jam();
+ ndbrequire(false);
+ break;
+ case CopyGCIReq::LOCAL_CHECKPOINT: {
+ ok = true;
+ jam();
+ c_lcpState.setLcpStatus(LCP_COPY_GCI, __LINE__);
+ c_lcpState.m_masterLcpDihRef = cmasterdihref;
+ setNodeInfo(signal);
+ break;
+ }
+ case CopyGCIReq::RESTART: {
+ ok = true;
+ jam();
+ coldgcp = SYSFILE->newestRestorableGCI;
+ crestartGci = SYSFILE->newestRestorableGCI;
+ Sysfile::setRestartOngoing(SYSFILE->systemRestartBits);
+ currentgcp = coldgcp + 1;
+ cnewgcp = coldgcp + 1;
+ setNodeInfo(signal);
+ if ((Sysfile::getLCPOngoing(SYSFILE->systemRestartBits))) {
+ jam();
+ /* -------------------------------------------------------------------- */
+ // IF THERE WAS A LOCAL CHECKPOINT ONGOING AT THE CRASH MOMENT WE WILL
+ // INVALIDATE THAT LOCAL CHECKPOINT.
+ /* -------------------------------------------------------------------- */
+ invalidateLcpInfoAfterSr();
+ }//if
+ break;
+ }
+ case CopyGCIReq::GLOBAL_CHECKPOINT: {
+ ok = true;
+ jam();
+ cgcpParticipantState = GCP_PARTICIPANT_COPY_GCI_RECEIVED;
+ setNodeInfo(signal);
+ break;
+ }//if
+ case CopyGCIReq::INITIAL_START_COMPLETED:
+ ok = true;
+ jam();
+ break;
+ }
+ ndbrequire(ok);
+
+ /* ----------------------------------------------------------------------- */
+ /* WE START BY TRYING TO OPEN THE FIRST RESTORABLE GCI FILE. */
+ /* ----------------------------------------------------------------------- */
+ FileRecordPtr filePtr;
+ filePtr.i = crestartInfoFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ if (filePtr.p->fileStatus == FileRecord::OPEN) {
+ jam();
+ openingCopyGciSkipInitLab(signal, filePtr);
+ return;
+ }//if
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_COPY_GCI;
+ return;
+}//Dbdih::execCOPY_GCIREQ()
+
+void Dbdih::execDICTSTARTCONF(Signal* signal)
+{
+ jamEntry();
+ Uint32 nodeId = refToNode(signal->getSendersBlockRef());
+ if (nodeId != getOwnNodeId()) {
+ jam();
+ nodeDictStartConfLab(signal);
+ } else {
+ jam();
+ dictStartConfLab(signal);
+ }//if
+}//Dbdih::execDICTSTARTCONF()
+
+void Dbdih::execFSCLOSECONF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ filePtr.p->fileStatus = FileRecord::CLOSED;
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::CLOSING_GCP:
+ jam();
+ closingGcpLab(signal, filePtr);
+ break;
+ case FileRecord::CLOSING_GCP_CRASH:
+ jam();
+ closingGcpCrashLab(signal, filePtr);
+ break;
+ case FileRecord::CLOSING_TABLE_CRASH:
+ jam();
+ closingTableCrashLab(signal, filePtr);
+ break;
+ case FileRecord::CLOSING_TABLE_SR:
+ jam();
+ closingTableSrLab(signal, filePtr);
+ break;
+ case FileRecord::TABLE_CLOSE:
+ jam();
+ tableCloseLab(signal, filePtr);
+ break;
+ case FileRecord::TABLE_CLOSE_DELETE:
+ jam();
+ tableDeleteLab(signal, filePtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSCLOSECONF()
+
+void Dbdih::execFSCLOSEREF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::CLOSING_GCP:
+ ndbrequire(false);
+ break;
+ case FileRecord::CLOSING_GCP_CRASH:
+ jam();
+ closingGcpCrashLab(signal, filePtr);
+ break;
+ case FileRecord::CLOSING_TABLE_CRASH:
+ jam();
+ closingTableCrashLab(signal, filePtr);
+ break;
+ case FileRecord::CLOSING_TABLE_SR:
+ ndbrequire(false);
+ break;
+ case FileRecord::TABLE_CLOSE:
+ ndbrequire(false);
+ break;
+ case FileRecord::TABLE_CLOSE_DELETE:
+ ndbrequire(false);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSCLOSEREF()
+
+void Dbdih::execFSOPENCONF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ filePtr.p->fileRef = signal->theData[1];
+ filePtr.p->fileStatus = FileRecord::OPEN;
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::CREATING_GCP:
+ jam();
+ creatingGcpLab(signal, filePtr);
+ break;
+ case FileRecord::OPENING_COPY_GCI:
+ jam();
+ openingCopyGciSkipInitLab(signal, filePtr);
+ break;
+ case FileRecord::CREATING_COPY_GCI:
+ jam();
+ openingCopyGciSkipInitLab(signal, filePtr);
+ break;
+ case FileRecord::OPENING_GCP:
+ jam();
+ openingGcpLab(signal, filePtr);
+ break;
+ case FileRecord::OPENING_TABLE:
+ jam();
+ openingTableLab(signal, filePtr);
+ break;
+ case FileRecord::TABLE_CREATE:
+ jam();
+ tableCreateLab(signal, filePtr);
+ break;
+ case FileRecord::TABLE_OPEN_FOR_DELETE:
+ jam();
+ tableOpenLab(signal, filePtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSOPENCONF()
+
+void Dbdih::execFSOPENREF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::CREATING_GCP:
+ /* --------------------------------------------------------------------- */
+ /* WE DID NOT MANAGE TO CREATE A GLOBAL CHECKPOINT FILE. SERIOUS ERROR */
+ /* WHICH CAUSES A SYSTEM RESTART. */
+ /* --------------------------------------------------------------------- */
+ ndbrequire(false);
+ break;
+ case FileRecord::OPENING_COPY_GCI:
+ jam();
+ openingCopyGciErrorLab(signal, filePtr);
+ break;
+ case FileRecord::CREATING_COPY_GCI:
+ ndbrequire(false);
+ break;
+ case FileRecord::OPENING_GCP:
+ jam();
+ openingGcpErrorLab(signal, filePtr);
+ break;
+ case FileRecord::OPENING_TABLE:
+ jam();
+ openingTableErrorLab(signal, filePtr);
+ break;
+ case FileRecord::TABLE_CREATE:
+ ndbrequire(false);
+ break;
+ case FileRecord::TABLE_OPEN_FOR_DELETE:
+ jam();
+ tableDeleteLab(signal, filePtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSOPENREF()
+
+void Dbdih::execFSREADCONF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::READING_GCP:
+ jam();
+ readingGcpLab(signal, filePtr);
+ break;
+ case FileRecord::READING_TABLE:
+ jam();
+ readingTableLab(signal, filePtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSREADCONF()
+
+void Dbdih::execFSREADREF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::READING_GCP:
+ jam();
+ readingGcpErrorLab(signal, filePtr);
+ break;
+ case FileRecord::READING_TABLE:
+ jam();
+ readingTableErrorLab(signal, filePtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSREADREF()
+
+void Dbdih::execFSWRITECONF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::WRITING_COPY_GCI:
+ jam();
+ writingCopyGciLab(signal, filePtr);
+ break;
+ case FileRecord::WRITE_INIT_GCP:
+ jam();
+ writeInitGcpLab(signal, filePtr);
+ break;
+ case FileRecord::TABLE_WRITE:
+ jam();
+ tableWriteLab(signal, filePtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSWRITECONF()
+
+void Dbdih::execFSWRITEREF(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ jamEntry();
+ filePtr.i = signal->theData[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ FileRecord::ReqStatus status = filePtr.p->reqStatus;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ switch (status) {
+ case FileRecord::WRITING_COPY_GCI:
+ /* --------------------------------------------------------------------- */
+ /* EVEN CREATING THE FILE DID NOT WORK. WE WILL THEN CRASH. */
+ /* ERROR IN WRITING FILE. WE WILL NOT CONTINUE FROM HERE. */
+ /* --------------------------------------------------------------------- */
+ ndbrequire(false);
+ break;
+ case FileRecord::WRITE_INIT_GCP:
+ /* --------------------------------------------------------------------- */
+ /* AN ERROR OCCURRED IN WRITING A GCI FILE WHICH IS A SERIOUS ERROR */
+ /* THAT CAUSE A SYSTEM RESTART. */
+ /* --------------------------------------------------------------------- */
+ ndbrequire(false);
+ break;
+ case FileRecord::TABLE_WRITE:
+ ndbrequire(false);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execFSWRITEREF()
+
+void Dbdih::execGETGCIREQ(Signal* signal)
+{
+
+ jamEntry();
+ Uint32 userPtr = signal->theData[0];
+ BlockReference userRef = signal->theData[1];
+
+ signal->theData[0] = userPtr;
+ signal->theData[1] = SYSFILE->newestRestorableGCI;
+ sendSignal(userRef, GSN_GETGCICONF, signal, 2, JBB);
+}//Dbdih::execGETGCIREQ()
+
+void Dbdih::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ jamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DIH_API_CONNECT,
+ &capiConnectFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DIH_CONNECT,&cconnectFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DIH_FRAG_CONNECT,
+ &cfragstoreFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DIH_REPLICAS,
+ &creplicaFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DIH_TABLE, &ctabFileSize))
+ cfileFileSize = (2 * ctabFileSize) + 2;
+ initRecords();
+ initialiseRecordsLab(signal, 0, ref, senderData);
+ return;
+}//Dbdih::execSIZEALT_REP()
+
+void Dbdih::execSTART_COPYREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dbdih::execSTART_COPYREF()
+
+void Dbdih::execSTART_FRAGCONF(Signal* signal)
+{
+ (void)signal; // Don't want compiler warning
+ /* ********************************************************************* */
+ /* If anyone wants to add functionality in this method, be aware that */
+ /* for temporary tables no START_FRAGREQ is sent and therefore no */
+ /* START_FRAGCONF signal will be received for those tables!! */
+ /* ********************************************************************* */
+ jamEntry();
+ return;
+}//Dbdih::execSTART_FRAGCONF()
+
+void Dbdih::execSTART_MEREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dbdih::execSTART_MEREF()
+
+void Dbdih::execTAB_COMMITREQ(Signal* signal)
+{
+ TabRecordPtr tabPtr;
+ jamEntry();
+ Uint32 tdictPtr = signal->theData[0];
+ BlockReference tdictBlockref = signal->theData[1];
+ tabPtr.i = signal->theData[2];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_CREATING);
+ tabPtr.p->tabStatus = TabRecord::TS_ACTIVE;
+ signal->theData[0] = tdictPtr;
+ signal->theData[1] = cownNodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(tdictBlockref, GSN_TAB_COMMITCONF, signal, 3, JBB);
+ return;
+}//Dbdih::execTAB_COMMITREQ()
+
+/*
+ 3.2 S T A N D A R D S U B P R O G R A M S I N P L E X
+ *************************************************************
+ */
+/*
+ 3.2.1 S T A R T / R E S T A R T
+ **********************************
+ */
+/*****************************************************************************/
+/* ********** START / RESTART MODULE *************/
+/*****************************************************************************/
+/*
+ 3.2.1.1 LOADING O W N B L O C K R E F E R E N C E (ABSOLUTE PHASE 1)
+ *****************************************************************************
+ */
+void Dbdih::execDIH_RESTARTREQ(Signal* signal)
+{
+ jamEntry();
+ cntrlblockref = signal->theData[0];
+ if(theConfiguration.getInitialStart()){
+ sendSignal(cntrlblockref, GSN_DIH_RESTARTREF, signal, 1, JBB);
+ } else {
+ readGciFileLab(signal);
+ }
+ return;
+}//Dbdih::execDIH_RESTARTREQ()
+
+void Dbdih::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ signal->theData[0] = 0;
+ signal->theData[1] = 0;
+ signal->theData[2] = 0;
+ signal->theData[3] = 1; // Next start phase
+ signal->theData[4] = 255; // Next start phase
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+ return;
+}//Dbdih::execSTTOR()
+
+void Dbdih::initialStartCompletedLab(Signal* signal)
+{
+ /*-------------------------------------------------------------------------*/
+ /* NOW THAT (RE)START IS COMPLETED WE CAN START THE LCP.*/
+ /*-------------------------------------------------------------------------*/
+ return;
+}//Dbdih::initialStartCompletedLab()
+
+/*
+ * ***************************************************************************
+ * S E N D I N G R E P L Y T O S T A R T / R E S T A R T R E Q U E S T S
+ * ****************************************************************************
+ */
+void Dbdih::ndbsttorry10Lab(Signal* signal, Uint32 _line)
+{
+ /*-------------------------------------------------------------------------*/
+ // AN NDB START PHASE HAS BEEN COMPLETED. WHEN START PHASE 6 IS COMPLETED WE
+ // RECORD THAT THE SYSTEM IS RUNNING.
+ /*-------------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ sendSignal(cntrlblockref, GSN_NDB_STTORRY, signal, 1, JBB);
+ return;
+}//Dbdih::ndbsttorry10Lab()
+
+/*
+****************************************
+I N T E R N A L P H A S E S
+****************************************
+*/
+/*---------------------------------------------------------------------------*/
+/*NDB_STTOR START SIGNAL AT START/RESTART */
+/*---------------------------------------------------------------------------*/
+void Dbdih::execNDB_STTOR(Signal* signal)
+{
+ jamEntry();
+ BlockReference cntrRef = signal->theData[0]; /* SENDERS BLOCK REFERENCE */
+ Uint32 ownNodeId = signal->theData[1]; /* OWN PROCESSOR ID*/
+ Uint32 phase = signal->theData[2]; /* INTERNAL START PHASE*/
+ Uint32 typestart = signal->theData[3];
+
+ cstarttype = typestart;
+ cstartPhase = phase;
+
+ switch (phase){
+ case ZNDB_SPH1:
+ jam();
+ /*----------------------------------------------------------------------*/
+ /* Set the delay between local checkpoints in ndb startphase 1. */
+ /*----------------------------------------------------------------------*/
+ cownNodeId = ownNodeId;
+ /*-----------------------------------------------------------------------*/
+ // Compute all static block references in this node as part of
+ // ndb start phase 1.
+ /*-----------------------------------------------------------------------*/
+ cntrlblockref = cntrRef;
+ clocaltcblockref = calcTcBlockRef(ownNodeId);
+ clocallqhblockref = calcLqhBlockRef(ownNodeId);
+ cdictblockref = calcDictBlockRef(ownNodeId);
+ ndbsttorry10Lab(signal, __LINE__);
+ break;
+
+ case ZNDB_SPH2:
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // Set the number of replicas, maximum is 4 replicas.
+ // Read the ndb nodes from the configuration.
+ /*-----------------------------------------------------------------------*/
+
+ /*-----------------------------------------------------------------------*/
+ // For node restarts we will also add a request for permission
+ // to continue the system restart.
+ // The permission is given by the master node in the alive set.
+ /*-----------------------------------------------------------------------*/
+ createMutexes(signal, 0);
+ break;
+
+ case ZNDB_SPH3:
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // Non-master nodes performing an initial start will execute
+ // the start request here since the
+ // initial start do not synchronise so much from the master.
+ // In the master nodes the start
+ // request will be sent directly to dih (in ndb_startreq) when all
+ // nodes have completed phase 3 of the start.
+ /*-----------------------------------------------------------------------*/
+ cmasterState = MASTER_IDLE;
+ if(cstarttype == NodeState::ST_INITIAL_START ||
+ cstarttype == NodeState::ST_SYSTEM_RESTART){
+ jam();
+ cmasterState = isMaster() ? MASTER_ACTIVE : MASTER_IDLE;
+ }
+ if (!isMaster() && cstarttype == NodeState::ST_INITIAL_START) {
+ jam();
+ ndbStartReqLab(signal, cntrRef);
+ return;
+ }//if
+ ndbsttorry10Lab(signal, __LINE__);
+ break;
+
+ case ZNDB_SPH4:
+ jam();
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+ cmasterTakeOverNode = ZNIL;
+ switch(typestart){
+ case NodeState::ST_INITIAL_START:
+ jam();
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ case NodeState::ST_SYSTEM_RESTART:
+ jam();
+ if (isMaster()) {
+ jam();
+ systemRestartTakeOverLab(signal);
+ if (anyActiveTakeOver() && false) {
+ jam();
+ ndbout_c("1 - anyActiveTakeOver == true");
+ return;
+ }
+ }
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ case NodeState::ST_NODE_RESTART:
+ jam();
+ /***********************************************************************
+ * When starting nodes while system is operational we must be controlled
+ * by the master since only one node restart is allowed at a time.
+ * When this signal is confirmed the master has also copied the
+ * dictionary and the distribution information.
+ */
+ StartMeReq * req = (StartMeReq*)&signal->theData[0];
+ req->startingRef = reference();
+ req->startingVersion = 0; // Obsolete
+ sendSignal(cmasterdihref, GSN_START_MEREQ, signal,
+ StartMeReq::SignalLength, JBB);
+ return;
+ }
+ ndbrequire(false);
+ break;
+ case ZNDB_SPH5:
+ jam();
+ switch(typestart){
+ case NodeState::ST_INITIAL_START:
+ case NodeState::ST_SYSTEM_RESTART:
+ jam();
+ jam();
+ /*---------------------------------------------------------------------*/
+ // WE EXECUTE A LOCAL CHECKPOINT AS A PART OF A SYSTEM RESTART.
+ // THE IDEA IS THAT WE NEED TO
+ // ENSURE THAT WE CAN RECOVER FROM PROBLEMS CAUSED BY MANY NODE
+ // CRASHES THAT CAUSES THE LOG
+ // TO GROW AND THE NUMBER OF LOG ROUNDS TO EXECUTE TO GROW.
+ // THIS CAN OTHERWISE GET US INTO
+ // A SITUATION WHICH IS UNREPAIRABLE. THUS WE EXECUTE A CHECKPOINT
+ // BEFORE ALLOWING ANY TRANSACTIONS TO START.
+ /*---------------------------------------------------------------------*/
+ if (!isMaster()) {
+ jam();
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ }//if
+
+ c_lcpState.immediateLcpStart = true;
+ cwaitLcpSr = true;
+ checkLcpStart(signal, __LINE__);
+ return;
+ case NodeState::ST_NODE_RESTART:
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ jam();
+ signal->theData[0] = cownNodeId;
+ signal->theData[1] = reference();
+ sendSignal(cmasterdihref, GSN_START_COPYREQ, signal, 2, JBB);
+ return;
+ }
+ ndbrequire(false);
+ case ZNDB_SPH6:
+ jam();
+ switch(typestart){
+ case NodeState::ST_INITIAL_START:
+ case NodeState::ST_SYSTEM_RESTART:
+ jam();
+ if(isMaster()){
+ jam();
+ startGcp(signal);
+ }
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ case NodeState::ST_NODE_RESTART:
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ }
+ ndbrequire(false);
+ break;
+ default:
+ jam();
+ ndbsttorry10Lab(signal, __LINE__);
+ break;
+ }//switch
+}//Dbdih::execNDB_STTOR()
+
+void
+Dbdih::createMutexes(Signal * signal, Uint32 count){
+ Callback c = { safe_cast(&Dbdih::createMutex_done), count };
+
+ switch(count){
+ case 0:{
+ Mutex mutex(signal, c_mutexMgr, c_startLcpMutexHandle);
+ mutex.create(c);
+ return;
+ }
+ case 1:{
+ Mutex mutex(signal, c_mutexMgr, c_switchPrimaryMutexHandle);
+ mutex.create(c);
+ return;
+ }
+ }
+
+ signal->theData[0] = reference();
+ sendSignal(cntrlblockref, GSN_READ_NODESREQ, signal, 1, JBB);
+}
+
+void
+Dbdih::createMutex_done(Signal* signal, Uint32 senderData, Uint32 retVal){
+ jamEntry();
+ ndbrequire(retVal == 0);
+
+ switch(senderData){
+ case 0:{
+ Mutex mutex(signal, c_mutexMgr, c_startLcpMutexHandle);
+ mutex.release();
+ }
+ case 1:{
+ Mutex mutex(signal, c_mutexMgr, c_switchPrimaryMutexHandle);
+ mutex.release();
+ }
+ }
+
+ createMutexes(signal, senderData + 1);
+}
+
+/*****************************************************************************/
+/* ------------------------------------------------------------------------- */
+/* WE HAVE BEEN REQUESTED BY NDBCNTR TO PERFORM A RESTART OF THE */
+/* DATABASE TABLES. */
+/* THIS SIGNAL IS SENT AFTER COMPLETING PHASE 3 IN ALL BLOCKS IN A */
+/* SYSTEM RESTART. WE WILL ALSO JUMP TO THIS LABEL FROM PHASE 3 IN AN */
+/* INITIAL START. */
+/* ------------------------------------------------------------------------- */
+/*****************************************************************************/
+void Dbdih::execNDB_STARTREQ(Signal* signal)
+{
+ jamEntry();
+ BlockReference ref = signal->theData[0];
+ cstarttype = signal->theData[1];
+ ndbStartReqLab(signal, ref);
+}//Dbdih::execNDB_STARTREQ()
+
+void Dbdih::ndbStartReqLab(Signal* signal, BlockReference ref)
+{
+ cndbStartReqBlockref = ref;
+ if (cstarttype == NodeState::ST_INITIAL_START) {
+ jam();
+ initRestartInfo();
+ initGciFilesLab(signal);
+ return;
+ }
+
+ ndbrequire(isMaster());
+ copyGciLab(signal, CopyGCIReq::RESTART); // We have already read the file!
+}//Dbdih::ndbStartReqLab()
+
+void Dbdih::execREAD_NODESCONF(Signal* signal)
+{
+ unsigned i;
+ ReadNodesConf * const readNodes = (ReadNodesConf *)&signal->theData[0];
+ jamEntry();
+ Uint32 nodeArray[MAX_NDB_NODES];
+
+ csystemnodes = readNodes->noOfNodes;
+ cmasterNodeId = readNodes->masterNodeId;
+ int index = 0;
+ NdbNodeBitmask tmp; tmp.assign(2, readNodes->allNodes);
+ for (i = 1; i < MAX_NDB_NODES; i++){
+ jam();
+ if(tmp.get(i)){
+ jam();
+ nodeArray[index] = i;
+ if(NodeBitmask::get(readNodes->inactiveNodes, i) == false){
+ jam();
+ con_lineNodes++;
+ }//if
+ index++;
+ }//if
+ }//for
+
+ if(cstarttype == NodeState::ST_SYSTEM_RESTART ||
+ cstarttype == NodeState::ST_NODE_RESTART){
+
+ for(i = 1; i<MAX_NDB_NODES; i++){
+ const Uint32 stat = Sysfile::getNodeStatus(i, SYSFILE->nodeStatus);
+ if(stat == Sysfile::NS_NotDefined && !tmp.get(i)){
+ jam();
+ continue;
+ }
+
+ if(tmp.get(i) && stat != Sysfile::NS_NotDefined){
+ jam();
+ continue;
+ }
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf),
+ "Illegal configuration change."
+ " Initial start needs to be performed "
+ " when changing no of storage nodes (node %d)", i);
+ progError(__LINE__,
+ ERR_INVALID_CONFIG,
+ buf);
+ }
+ }
+
+ ndbrequire(csystemnodes >= 1 && csystemnodes < MAX_NDB_NODES);
+ if (cstarttype == NodeState::ST_INITIAL_START) {
+ jam();
+ ndbrequire(cnoReplicas <= csystemnodes);
+ calculateHotSpare();
+ ndbrequire(cnoReplicas <= (csystemnodes - cnoHotSpare));
+ }//if
+
+ cmasterdihref = calcDihBlockRef(cmasterNodeId);
+ /*-------------------------------------------------------------------------*/
+ /* MAKE THE LIST OF PRN-RECORD WHICH IS ONE OF THE NODES-LIST IN THIS BLOCK*/
+ /*-------------------------------------------------------------------------*/
+ makePrnList(readNodes, nodeArray);
+ if (cstarttype == NodeState::ST_INITIAL_START) {
+ jam();
+ /**----------------------------------------------------------------------
+ * WHEN WE INITIALLY START A DATABASE WE WILL CREATE NODE GROUPS.
+ * ALL NODES ARE PUT INTO NODE GROUPS ALTHOUGH HOT SPARE NODES ARE PUT
+ * INTO A SPECIAL NODE GROUP. IN EACH NODE GROUP WE HAVE THE SAME AMOUNT
+ * OF NODES AS THERE ARE NUMBER OF REPLICAS.
+ * ONE POSSIBLE USAGE OF NODE GROUPS ARE TO MAKE A NODE GROUP A COMPLETE
+ * FRAGMENT OF THE DATABASE. THIS MEANS THAT ALL REPLICAS WILL BE STORED
+ * IN THE NODE GROUP.
+ *-----------------------------------------------------------------------*/
+ makeNodeGroups(nodeArray);
+ }//if
+ ndbrequire(checkNodeAlive(cmasterNodeId));
+ if (cstarttype == NodeState::ST_INITIAL_START) {
+ jam();
+ /**-----------------------------------------------------------------------
+ * INITIALISE THE SECOND NODE-LIST AND SET NODE BITS AND SOME NODE STATUS.
+ * VERY CONNECTED WITH MAKE_NODE_GROUPS. CHANGING ONE WILL AFFECT THE
+ * OTHER AS WELL.
+ *-----------------------------------------------------------------------*/
+ setInitialActiveStatus();
+ } else if (cstarttype == NodeState::ST_SYSTEM_RESTART) {
+ jam();
+ /*empty*/;
+ } else if ((cstarttype == NodeState::ST_NODE_RESTART) ||
+ (cstarttype == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ nodeRestartPh2Lab(signal);
+ return;
+ } else {
+ ndbrequire(false);
+ }//if
+ /**------------------------------------------------------------------------
+ * ESTABLISH CONNECTIONS WITH THE OTHER DIH BLOCKS AND INITIALISE THIS
+ * NODE-LIST THAT HANDLES CONNECTION WITH OTHER DIH BLOCKS.
+ *-------------------------------------------------------------------------*/
+ ndbsttorry10Lab(signal, __LINE__);
+}//Dbdih::execREAD_NODESCONF()
+
+/*---------------------------------------------------------------------------*/
+/* START NODE LOGIC FOR NODE RESTART */
+/*---------------------------------------------------------------------------*/
+void Dbdih::nodeRestartPh2Lab(Signal* signal)
+{
+ /*------------------------------------------------------------------------*/
+ // REQUEST FOR PERMISSION FROM MASTER TO START A NODE IN AN ALREADY
+ // RUNNING SYSTEM.
+ /*------------------------------------------------------------------------*/
+ StartPermReq * const req = (StartPermReq *)&signal->theData[0];
+
+ req->blockRef = reference();
+ req->nodeId = cownNodeId;
+ req->startType = cstarttype;
+ sendSignal(cmasterdihref, GSN_START_PERMREQ, signal, 3, JBB);
+}//Dbdih::nodeRestartPh2Lab()
+
+void Dbdih::execSTART_PERMCONF(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(7121);
+ Uint32 nodeId = signal->theData[0];
+ cfailurenr = signal->theData[1];
+ ndbrequire(nodeId == cownNodeId);
+ ndbsttorry10Lab(signal, __LINE__);
+}//Dbdih::execSTART_PERMCONF()
+
+void Dbdih::execSTART_PERMREF(Signal* signal)
+{
+ jamEntry();
+ Uint32 errorCode = signal->theData[1];
+ if (errorCode == ZNODE_ALREADY_STARTING_ERROR) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // The master was busy adding another node. We will wait for a second and
+ // try again.
+ /*-----------------------------------------------------------------------*/
+ signal->theData[0] = DihContinueB::ZSTART_PERMREQ_AGAIN;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 3000, 1);
+ return;
+ }//if
+ /*------------------------------------------------------------------------*/
+ // Some node process in another node involving our node was still active. We
+ // will recover from this by crashing here.
+ // This is controlled restart using the
+ // already existing features of node crashes. It is not a bug getting here.
+ /*-------------------------------------------------------------------------*/
+ ndbrequire(false);
+ return;
+}//Dbdih::execSTART_PERMREF()
+
+/*---------------------------------------------------------------------------*/
+/* THIS SIGNAL IS RECEIVED IN THE STARTING NODE WHEN THE START_MEREQ */
+/* HAS BEEN EXECUTED IN THE MASTER NODE. */
+/*---------------------------------------------------------------------------*/
+void Dbdih::execSTART_MECONF(Signal* signal)
+{
+ jamEntry();
+ StartMeConf * const startMe = (StartMeConf *)&signal->theData[0];
+ Uint32 nodeId = startMe->startingNodeId;
+ const Uint32 startWord = startMe->startWord;
+ Uint32 i;
+
+ CRASH_INSERTION(7130);
+ ndbrequire(nodeId == cownNodeId);
+ arrGuard(startWord + StartMeConf::DATA_SIZE, sizeof(cdata)/4);
+ for(i = 0; i < StartMeConf::DATA_SIZE; i++)
+ cdata[startWord+i] = startMe->data[i];
+
+ if(startWord + StartMeConf::DATA_SIZE < Sysfile::SYSFILE_SIZE32){
+ jam();
+ /**
+ * We are still waiting for data
+ */
+ return;
+ }
+ jam();
+
+ /**
+ * Copy into sysfile
+ *
+ * But dont copy lastCompletedGCI:s
+ */
+ Uint32 tempGCP[MAX_NDB_NODES];
+ for(i = 0; i < MAX_NDB_NODES; i++)
+ tempGCP[i] = SYSFILE->lastCompletedGCI[i];
+
+ for(i = 0; i < Sysfile::SYSFILE_SIZE32; i++)
+ sysfileData[i] = cdata[i];
+ for(i = 0; i < MAX_NDB_NODES; i++)
+ SYSFILE->lastCompletedGCI[i] = tempGCP[i];
+
+ setNodeActiveStatus();
+ setNodeGroups();
+ ndbsttorry10Lab(signal, __LINE__);
+}//Dbdih::execSTART_MECONF()
+
+void Dbdih::execSTART_COPYCONF(Signal* signal)
+{
+ jamEntry();
+ Uint32 nodeId = signal->theData[0];
+ ndbrequire(nodeId == cownNodeId);
+ CRASH_INSERTION(7132);
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+}//Dbdih::execSTART_COPYCONF()
+
+/*---------------------------------------------------------------------------*/
+/* MASTER LOGIC FOR NODE RESTART */
+/*---------------------------------------------------------------------------*/
+/* NODE RESTART PERMISSION REQUEST */
+/*---------------------------------------------------------------------------*/
+// A REQUEST FROM A STARTING NODE TO PERFORM A NODE RESTART. IF NO OTHER NODE
+// IS ACTIVE IN PERFORMING A NODE RESTART AND THERE ARE NO ACTIVE PROCESSES IN
+// THIS NODE INVOLVING THE STARTING NODE THIS REQUEST WILL BE GRANTED.
+/*---------------------------------------------------------------------------*/
+void Dbdih::execSTART_PERMREQ(Signal* signal)
+{
+ StartPermReq * const req = (StartPermReq*)&signal->theData[0];
+ jamEntry();
+ const BlockReference retRef = req->blockRef;
+ const Uint32 nodeId = req->nodeId;
+ const Uint32 typeStart = req->startType;
+
+ CRASH_INSERTION(7122);
+ ndbrequire(isMaster());
+ ndbrequire(refToNode(retRef) == nodeId);
+ if ((c_nodeStartMaster.activeState) ||
+ (c_nodeStartMaster.wait != ZFALSE)) {
+ jam();
+ signal->theData[0] = nodeId;
+ signal->theData[1] = ZNODE_ALREADY_STARTING_ERROR;
+ sendSignal(retRef, GSN_START_PERMREF, signal, 2, JBB);
+ return;
+ }//if
+ if (getNodeStatus(nodeId) != NodeRecord::DEAD){
+ ndbout << "nodeStatus in START_PERMREQ = "
+ << (Uint32) getNodeStatus(nodeId) << endl;
+ ndbrequire(false);
+ }//if
+
+ /*----------------------------------------------------------------------
+ * WE START THE INCLUSION PROCEDURE
+ * ---------------------------------------------------------------------*/
+ c_nodeStartMaster.failNr = cfailurenr;
+ c_nodeStartMaster.wait = ZFALSE;
+ c_nodeStartMaster.startInfoErrorCode = 0;
+ c_nodeStartMaster.startNode = nodeId;
+ c_nodeStartMaster.activeState = true;
+ c_nodeStartMaster.m_outstandingGsn = GSN_START_INFOREQ;
+
+ setNodeStatus(nodeId, NodeRecord::STARTING);
+ /**
+ * But if it's a NodeState::ST_INITIAL_NODE_RESTART
+ *
+ * We first have to clear LCP's
+ * For normal node restart we simply ensure that all nodes
+ * are informed of the node restart
+ */
+ StartInfoReq *const r =(StartInfoReq*)&signal->theData[0];
+ r->startingNodeId = nodeId;
+ r->typeStart = typeStart;
+ r->systemFailureNo = cfailurenr;
+ sendLoopMacro(START_INFOREQ, sendSTART_INFOREQ);
+}//Dbdih::execSTART_PERMREQ()
+
+void Dbdih::execSTART_INFOREF(Signal* signal)
+{
+ StartInfoRef * ref = (StartInfoRef*)&signal->theData[0];
+ if (getNodeStatus(ref->startingNodeId) != NodeRecord::STARTING) {
+ jam();
+ return;
+ }//if
+ ndbrequire(c_nodeStartMaster.startNode == ref->startingNodeId);
+ c_nodeStartMaster.startInfoErrorCode = ref->errorCode;
+ startInfoReply(signal, ref->sendingNodeId);
+}//Dbdih::execSTART_INFOREF()
+
+void Dbdih::execSTART_INFOCONF(Signal* signal)
+{
+ jamEntry();
+ StartInfoConf * conf = (StartInfoConf*)&signal->theData[0];
+ if (getNodeStatus(conf->startingNodeId) != NodeRecord::STARTING) {
+ jam();
+ return;
+ }//if
+ ndbrequire(c_nodeStartMaster.startNode == conf->startingNodeId);
+ startInfoReply(signal, conf->sendingNodeId);
+}//Dbdih::execSTART_INFOCONF()
+
+void Dbdih::startInfoReply(Signal* signal, Uint32 nodeId)
+{
+ receiveLoopMacro(START_INFOREQ, nodeId);
+ /**
+ * We're finished with the START_INFOREQ's
+ */
+ if (c_nodeStartMaster.startInfoErrorCode == 0) {
+ jam();
+ /**
+ * Everything has been a success so far
+ */
+ StartPermConf * conf = (StartPermConf*)&signal->theData[0];
+ conf->startingNodeId = c_nodeStartMaster.startNode;
+ conf->systemFailureNo = cfailurenr;
+ sendSignal(calcDihBlockRef(c_nodeStartMaster.startNode),
+ GSN_START_PERMCONF, signal, StartPermConf::SignalLength, JBB);
+ c_nodeStartMaster.m_outstandingGsn = GSN_START_PERMCONF;
+ } else {
+ jam();
+ StartPermRef * ref = (StartPermRef*)&signal->theData[0];
+ ref->startingNodeId = c_nodeStartMaster.startNode;
+ ref->errorCode = c_nodeStartMaster.startInfoErrorCode;
+ sendSignal(calcDihBlockRef(c_nodeStartMaster.startNode),
+ GSN_START_PERMREF, signal, StartPermRef::SignalLength, JBB);
+ nodeResetStart();
+ }//if
+}//Dbdih::startInfoReply()
+
+/*---------------------------------------------------------------------------*/
+/* NODE RESTART CONTINUE REQUEST */
+/*---------------------------------------------------------------------------*/
+// THIS SIGNAL AND THE CODE BELOW IS EXECUTED BY THE MASTER WHEN IT HAS BEEN
+// REQUESTED TO START UP A NEW NODE. The master instructs the starting node
+// how to set up its log for continued execution.
+/*---------------------------------------------------------------------------*/
+void Dbdih::execSTART_MEREQ(Signal* signal)
+{
+ StartMeReq * req = (StartMeReq*)&signal->theData[0];
+ jamEntry();
+ const BlockReference Tblockref = req->startingRef;
+ const Uint32 Tnodeid = refToNode(Tblockref);
+
+ ndbrequire(isMaster());
+ ndbrequire(c_nodeStartMaster.startNode == Tnodeid);
+ ndbrequire(getNodeStatus(Tnodeid) == NodeRecord::STARTING);
+
+ sendSTART_RECREQ(signal, Tnodeid);
+}//Dbdih::execSTART_MEREQ()
+
+void Dbdih::nodeRestartStartRecConfLab(Signal* signal)
+{
+ c_nodeStartMaster.blockLcp = true;
+ if ((c_lcpState.lcpStatus != LCP_STATUS_IDLE) &&
+ (c_lcpState.lcpStatus != LCP_TCGET)) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // WE WILL NOT ALLOW A NODE RESTART TO COME IN WHEN A LOCAL CHECKPOINT IS
+ // ONGOING. IT WOULD COMPLICATE THE LCP PROTOCOL TOO MUCH. WE WILL ADD THIS
+ // LATER.
+ /*-----------------------------------------------------------------------*/
+ return;
+ }//if
+ lcpBlockedLab(signal);
+}//Dbdih::nodeRestartStartRecConfLab()
+
+void Dbdih::lcpBlockedLab(Signal* signal)
+{
+ ndbrequire(getNodeStatus(c_nodeStartMaster.startNode)==NodeRecord::STARTING);
+ /*------------------------------------------------------------------------*/
+ // NOW WE HAVE COPIED ALL INFORMATION IN DICT WE ARE NOW READY TO COPY ALL
+ // INFORMATION IN DIH TO THE NEW NODE.
+ /*------------------------------------------------------------------------*/
+ c_nodeStartMaster.wait = 10;
+ signal->theData[0] = DihContinueB::ZCOPY_NODE;
+ signal->theData[1] = 0;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ c_nodeStartMaster.m_outstandingGsn = GSN_COPY_TABREQ;
+}//Dbdih::lcpBlockedLab()
+
+void Dbdih::nodeDictStartConfLab(Signal* signal)
+{
+ /*-------------------------------------------------------------------------*/
+ // NOW WE HAVE COPIED BOTH DIH AND DICT INFORMATION. WE ARE NOW READY TO
+ // INTEGRATE THE NODE INTO THE LCP AND GCP PROTOCOLS AND TO ALLOW UPDATES OF
+ // THE DICTIONARY AGAIN.
+ /*-------------------------------------------------------------------------*/
+ c_nodeStartMaster.wait = ZFALSE;
+ c_nodeStartMaster.blockGcp = true;
+ if (cgcpStatus != GCP_READY) {
+ /*-----------------------------------------------------------------------*/
+ // The global checkpoint is executing. Wait until it is completed before we
+ // continue processing the node recovery.
+ /*-----------------------------------------------------------------------*/
+ jam();
+ return;
+ }//if
+ gcpBlockedLab(signal);
+
+ /*-----------------------------------------------------------------*/
+ // Report that node restart has completed copy of dictionary.
+ /*-----------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_NR_CopyDict;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
+}//Dbdih::nodeDictStartConfLab()
+
+void Dbdih::dihCopyCompletedLab(Signal* signal)
+{
+ BlockReference ref = calcDictBlockRef(c_nodeStartMaster.startNode);
+ DictStartReq * req = (DictStartReq*)&signal->theData[0];
+ req->restartGci = cnewgcp;
+ req->senderRef = reference();
+ sendSignal(ref, GSN_DICTSTARTREQ,
+ signal, DictStartReq::SignalLength, JBB);
+ c_nodeStartMaster.m_outstandingGsn = GSN_DICTSTARTREQ;
+ c_nodeStartMaster.wait = 0;
+}//Dbdih::dihCopyCompletedLab()
+
+void Dbdih::gcpBlockedLab(Signal* signal)
+{
+ /*-----------------------------------------------------------------*/
+ // Report that node restart has completed copy of distribution info.
+ /*-----------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_NR_CopyDistr;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
+
+ /**
+ * The node DIH will be part of LCP
+ */
+ NodeRecordPtr nodePtr;
+ nodePtr.i = c_nodeStartMaster.startNode;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->m_inclDihLcp = true;
+
+ /*-------------------------------------------------------------------------*/
+ // NOW IT IS TIME TO INFORM ALL OTHER NODES IN THE CLUSTER OF THE STARTED
+ // NODE SUCH THAT THEY ALSO INCLUDE THE NODE IN THE NODE LISTS AND SO FORTH.
+ /*------------------------------------------------------------------------*/
+ sendLoopMacro(INCL_NODEREQ, sendINCL_NODEREQ);
+ /*-------------------------------------------------------------------------*/
+ // We also need to send to the starting node to ensure he is aware of the
+ // global checkpoint id and the correct state. We do not wait for any reply
+ // since the starting node will not send any.
+ /*-------------------------------------------------------------------------*/
+ sendINCL_NODEREQ(signal, c_nodeStartMaster.startNode);
+}//Dbdih::gcpBlockedLab()
+
+/*---------------------------------------------------------------------------*/
+// THIS SIGNAL IS EXECUTED IN BOTH SLAVES AND IN THE MASTER
+/*---------------------------------------------------------------------------*/
+void Dbdih::execINCL_NODECONF(Signal* signal)
+{
+ Uint32 TsendNodeId;
+ Uint32 TstartNode_or_blockref;
+
+ jamEntry();
+ TstartNode_or_blockref = signal->theData[0];
+ TsendNodeId = signal->theData[1];
+
+ if (TstartNode_or_blockref == clocallqhblockref) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // THIS SIGNAL CAME FROM THE LOCAL LQH BLOCK.
+ // WE WILL NOW SEND INCLUDE TO THE TC BLOCK.
+ /*-----------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ signal->theData[1] = c_nodeStartSlave.nodeId;
+ sendSignal(clocaltcblockref, GSN_INCL_NODEREQ, signal, 2, JBB);
+ return;
+ }//if
+ if (TstartNode_or_blockref == clocaltcblockref) {
+ jam();
+ /*----------------------------------------------------------------------*/
+ // THIS SIGNAL CAME FROM THE LOCAL LQH BLOCK.
+ // WE WILL NOW SEND INCLUDE TO THE DICT BLOCK.
+ /*----------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ signal->theData[1] = c_nodeStartSlave.nodeId;
+ sendSignal(cdictblockref, GSN_INCL_NODEREQ, signal, 2, JBB);
+ return;
+ }//if
+ if (TstartNode_or_blockref == cdictblockref) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // THIS SIGNAL CAME FROM THE LOCAL DICT BLOCK. WE WILL NOW SEND CONF TO THE
+ // BACKUP.
+ /*-----------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ signal->theData[1] = c_nodeStartSlave.nodeId;
+ sendSignal(BACKUP_REF, GSN_INCL_NODEREQ, signal, 2, JBB);
+
+ // Suma will not send response to this for now, later...
+ sendSignal(SUMA_REF, GSN_INCL_NODEREQ, signal, 2, JBB);
+ // Grep will not send response to this for now, later...
+ sendSignal(GREP_REF, GSN_INCL_NODEREQ, signal, 2, JBB);
+ return;
+ }//if
+ if (TstartNode_or_blockref == numberToRef(BACKUP, getOwnNodeId())){
+ jam();
+ signal->theData[0] = c_nodeStartSlave.nodeId;
+ signal->theData[1] = cownNodeId;
+ sendSignal(cmasterdihref, GSN_INCL_NODECONF, signal, 2, JBB);
+ c_nodeStartSlave.nodeId = 0;
+ return;
+ }
+
+ ndbrequire(cmasterdihref = reference());
+ receiveLoopMacro(INCL_NODEREQ, TsendNodeId);
+
+ CRASH_INSERTION(7128);
+ /*-------------------------------------------------------------------------*/
+ // Now that we have included the starting node in the node lists in the
+ // various blocks we are ready to start the global checkpoint protocol
+ /*------------------------------------------------------------------------*/
+ c_nodeStartMaster.wait = 11;
+ c_nodeStartMaster.blockGcp = false;
+
+ signal->theData[0] = reference();
+ sendSignal(reference(), GSN_UNBLO_DICTCONF, signal, 1, JBB);
+}//Dbdih::execINCL_NODECONF()
+
+void Dbdih::execUNBLO_DICTCONF(Signal* signal)
+{
+ jamEntry();
+ c_nodeStartMaster.wait = ZFALSE;
+ if (!c_nodeStartMaster.activeState) {
+ jam();
+ return;
+ }//if
+
+ CRASH_INSERTION(7129);
+ /**-----------------------------------------------------------------------
+ * WE HAVE NOW PREPARED IT FOR INCLUSION IN THE LCP PROTOCOL.
+ * WE CAN NOW START THE LCP PROTOCOL AGAIN.
+ * WE HAVE ALSO MADE THIS FOR THE GCP PROTOCOL.
+ * WE ARE READY TO START THE PROTOCOLS AND RESPOND TO THE START REQUEST
+ * FROM THE STARTING NODE.
+ *------------------------------------------------------------------------*/
+
+ StartMeConf * const startMe = (StartMeConf *)&signal->theData[0];
+
+ const Uint32 wordPerSignal = StartMeConf::DATA_SIZE;
+ const int noOfSignals = ((Sysfile::SYSFILE_SIZE32 + (wordPerSignal - 1)) /
+ wordPerSignal);
+
+ startMe->startingNodeId = c_nodeStartMaster.startNode;
+ startMe->startWord = 0;
+
+ const Uint32 ref = calcDihBlockRef(c_nodeStartMaster.startNode);
+ for(int i = 0; i < noOfSignals; i++){
+ jam();
+ { // Do copy
+ const int startWord = startMe->startWord;
+ for(Uint32 j = 0; j < wordPerSignal; j++){
+ startMe->data[j] = sysfileData[j+startWord];
+ }
+ }
+ sendSignal(ref, GSN_START_MECONF, signal, StartMeConf::SignalLength, JBB);
+ startMe->startWord += wordPerSignal;
+ }//for
+ c_nodeStartMaster.m_outstandingGsn = GSN_START_MECONF;
+}//Dbdih::execUNBLO_DICTCONF()
+
+/*---------------------------------------------------------------------------*/
+/* NODE RESTART COPY REQUEST */
+/*---------------------------------------------------------------------------*/
+// A NODE RESTART HAS REACHED ITS FINAL PHASE WHEN THE DATA IS TO BE COPIED
+// TO THE NODE. START_COPYREQ IS EXECUTED BY THE MASTER NODE.
+/*---------------------------------------------------------------------------*/
+void Dbdih::execSTART_COPYREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 startNodeId = signal->theData[0];
+ //BlockReference startingRef = signal->theData[1];
+ ndbrequire(c_nodeStartMaster.startNode == startNodeId);
+ /*-------------------------------------------------------------------------*/
+ // REPORT Copy process of node restart is now about to start up.
+ /*-------------------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_NR_CopyFragsStarted;
+ signal->theData[1] = startNodeId;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ CRASH_INSERTION(7131);
+ nodeRestartTakeOver(signal, startNodeId);
+ // BlockReference ref = calcQmgrBlockRef(startNodeId);
+ // signal->theData[0] = cownNodeId;
+ // Remove comments as soon as I open up the Qmgr block
+ // TODO_RONM
+ // sendSignal(ref, GSN_ALLOW_NODE_CRASHORD, signal, 1, JBB);
+}//Dbdih::execSTART_COPYREQ()
+
+/*---------------------------------------------------------------------------*/
+/* SLAVE LOGIC FOR NODE RESTART */
+/*---------------------------------------------------------------------------*/
+void Dbdih::execSTART_INFOREQ(Signal* signal)
+{
+ jamEntry();
+ StartInfoReq *const req =(StartInfoReq*)&signal->theData[0];
+ Uint32 startNode = req->startingNodeId;
+ if (cfailurenr != req->systemFailureNo) {
+ jam();
+ //---------------------------------------------------------------
+ // A failure occurred since master sent this request. We will ignore
+ // this request since the node is already dead that is starting.
+ //---------------------------------------------------------------
+ return;
+ }//if
+ CRASH_INSERTION(7123);
+ if (isMaster()) {
+ jam();
+ ndbrequire(getNodeStatus(startNode) == NodeRecord::STARTING);
+ } else {
+ jam();
+ ndbrequire(getNodeStatus(startNode) == NodeRecord::DEAD);
+ }//if
+ if ((!getAllowNodeStart(startNode)) ||
+ (c_nodeStartSlave.nodeId != 0) ||
+ (ERROR_INSERTED(7124))) {
+ jam();
+ StartInfoRef *const ref =(StartInfoRef*)&signal->theData[0];
+ ref->startingNodeId = startNode;
+ ref->sendingNodeId = cownNodeId;
+ ref->errorCode = ZNODE_START_DISALLOWED_ERROR;
+ sendSignal(cmasterdihref, GSN_START_INFOREF, signal,
+ StartInfoRef::SignalLength, JBB);
+ return;
+ }//if
+ setNodeStatus(startNode, NodeRecord::STARTING);
+ if (req->typeStart == NodeState::ST_INITIAL_NODE_RESTART) {
+ jam();
+ setAllowNodeStart(startNode, false);
+ invalidateNodeLCP(signal, startNode, 0);
+ } else {
+ jam();
+ StartInfoConf * c = (StartInfoConf*)&signal->theData[0];
+ c->sendingNodeId = cownNodeId;
+ c->startingNodeId = startNode;
+ sendSignal(cmasterdihref, GSN_START_INFOCONF, signal,
+ StartInfoConf::SignalLength, JBB);
+ return;
+ }//if
+}//Dbdih::execSTART_INFOREQ()
+
+void Dbdih::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 retRef = signal->theData[0];
+ Uint32 nodeId = signal->theData[1];
+ Uint32 tnodeStartFailNr = signal->theData[2];
+ currentgcp = signal->theData[4];
+ CRASH_INSERTION(7127);
+ cnewgcp = currentgcp;
+ coldgcp = currentgcp - 1;
+ if (!isMaster()) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // We don't want to change the state of the master since he can be in the
+ // state LCP_TCGET at this time.
+ /*-----------------------------------------------------------------------*/
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+ }//if
+
+ /*-------------------------------------------------------------------------*/
+ // When a node is restarted we must ensure that a lcp will be run
+ // as soon as possible and the reset the delay according to the original
+ // configuration.
+ // Without an initial local checkpoint the new node will not be available.
+ /*-------------------------------------------------------------------------*/
+ if (getOwnNodeId() == nodeId) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // We are the starting node. We came here only to set the global checkpoint
+ // id's and the lcp status.
+ /*-----------------------------------------------------------------------*/
+ CRASH_INSERTION(7171);
+ return;
+ }//if
+ if (getNodeStatus(nodeId) != NodeRecord::STARTING) {
+ jam();
+ return;
+ }//if
+ ndbrequire(cfailurenr == tnodeStartFailNr);
+ ndbrequire (c_nodeStartSlave.nodeId == 0);
+ c_nodeStartSlave.nodeId = nodeId;
+
+ ndbrequire (retRef == cmasterdihref);
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ Sysfile::ActiveStatus TsaveState = nodePtr.p->activeStatus;
+ Uint32 TnodeGroup = nodePtr.p->nodeGroup;
+
+ new (nodePtr.p) NodeRecord();
+ nodePtr.p->nodeGroup = TnodeGroup;
+ nodePtr.p->activeStatus = TsaveState;
+ nodePtr.p->nodeStatus = NodeRecord::ALIVE;
+ nodePtr.p->useInTransactions = true;
+ nodePtr.p->m_inclDihLcp = true;
+
+ removeDeadNode(nodePtr);
+ insertAlive(nodePtr);
+ con_lineNodes++;
+
+ /*-------------------------------------------------------------------------*/
+ // WE WILL ALSO SEND THE INCLUDE NODE REQUEST TO THE LOCAL LQH BLOCK.
+ /*-------------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ signal->theData[1] = nodeId;
+ signal->theData[2] = currentgcp;
+ sendSignal(clocallqhblockref, GSN_INCL_NODEREQ, signal, 3, JBB);
+}//Dbdih::execINCL_NODEREQ()
+
+/* ------------------------------------------------------------------------- */
+// execINCL_NODECONF() is found in the master logic part since it is used by
+// both the master and the slaves.
+/* ------------------------------------------------------------------------- */
+
+/*****************************************************************************/
+/*********** TAKE OVER DECISION MODULE *************/
+/*****************************************************************************/
+// This module contains the subroutines that take the decision whether to take
+// over a node now or not.
+/* ------------------------------------------------------------------------- */
+/* MASTER LOGIC FOR SYSTEM RESTART */
+/* ------------------------------------------------------------------------- */
+// WE ONLY COME HERE IF WE ARE THE MASTER AND WE ARE PERFORMING A SYSTEM
+// RESTART. WE ALSO COME HERE DURING THIS SYSTEM RESTART ONE TIME PER NODE
+// THAT NEEDS TAKE OVER.
+/*---------------------------------------------------------------------------*/
+// WE CHECK IF ANY NODE NEEDS TO BE TAKEN OVER AND THE TAKE OVER HAS NOT YET
+// BEEN STARTED OR COMPLETED.
+/*---------------------------------------------------------------------------*/
+void
+Dbdih::systemRestartTakeOverLab(Signal* signal)
+{
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ break;
+ /*---------------------------------------------------------------------*/
+ // WE HAVE NOT REACHED A STATE YET WHERE THIS NODE NEEDS TO BE TAKEN OVER
+ /*---------------------------------------------------------------------*/
+ case Sysfile::NS_ActiveMissed_2:
+ case Sysfile::NS_NotActive_NotTakenOver:
+ jam();
+ /*---------------------------------------------------------------------*/
+ // THIS NODE IS IN TROUBLE.
+ // WE MUST SUCCEED WITH A LOCAL CHECKPOINT WITH THIS NODE TO REMOVE THE
+ // DANGER. IF THE NODE IS NOT ALIVE THEN THIS WILL NOT BE
+ // POSSIBLE AND WE CAN START THE TAKE OVER IMMEDIATELY IF WE HAVE ANY
+ // NODES THAT CAN PERFORM A TAKE OVER.
+ /*---------------------------------------------------------------------*/
+ if (nodePtr.p->nodeStatus != NodeRecord::ALIVE) {
+ jam();
+ Uint32 ThotSpareNode = findHotSpare();
+ if (ThotSpareNode != RNIL) {
+ jam();
+ startTakeOver(signal, RNIL, ThotSpareNode, nodePtr.i);
+ }//if
+ } else if(nodePtr.p->activeStatus == Sysfile::NS_NotActive_NotTakenOver){
+ jam();
+ /*-------------------------------------------------------------------*/
+ // NOT ACTIVE NODES THAT HAVE NOT YET BEEN TAKEN OVER NEEDS TAKE OVER
+ // IMMEDIATELY. IF WE ARE ALIVE WE TAKE OVER OUR OWN NODE.
+ /*-------------------------------------------------------------------*/
+ startTakeOver(signal, RNIL, nodePtr.i, nodePtr.i);
+ }//if
+ break;
+ case Sysfile::NS_TakeOver:
+ /**-------------------------------------------------------------------
+ * WE MUST HAVE FAILED IN THE MIDDLE OF THE TAKE OVER PROCESS.
+ * WE WILL CONCLUDE THE TAKE OVER PROCESS NOW.
+ *-------------------------------------------------------------------*/
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ Uint32 takeOverNode = Sysfile::getTakeOverNode(nodePtr.i,
+ SYSFILE->takeOver);
+ if(takeOverNode == 0){
+ jam();
+ warningEvent("Bug in take-over code restarting");
+ takeOverNode = nodePtr.i;
+ }
+ startTakeOver(signal, RNIL, nodePtr.i, takeOverNode);
+ } else {
+ jam();
+ /**-------------------------------------------------------------------
+ * We are not currently taking over, change our active status.
+ *-------------------------------------------------------------------*/
+ nodePtr.p->activeStatus = Sysfile::NS_NotActive_NotTakenOver;
+ setNodeRestartInfoBits();
+ }//if
+ break;
+ case Sysfile::NS_HotSpare:
+ jam();
+ break;
+ /*---------------------------------------------------------------------*/
+ // WE NEED NOT TAKE OVER NODES THAT ARE HOT SPARE.
+ /*---------------------------------------------------------------------*/
+ case Sysfile::NS_NotDefined:
+ jam();
+ break;
+ /*---------------------------------------------------------------------*/
+ // WE NEED NOT TAKE OVER NODES THAT DO NOT EVEN EXIST IN THE CLUSTER.
+ /*---------------------------------------------------------------------*/
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ }//for
+ /*-------------------------------------------------------------------------*/
+ /* NO TAKE OVER HAS BEEN INITIATED. */
+ /*-------------------------------------------------------------------------*/
+}//Dbdih::systemRestartTakeOverLab()
+
+/*---------------------------------------------------------------------------*/
+// This subroutine is called as part of node restart in the master node.
+/*---------------------------------------------------------------------------*/
+void Dbdih::nodeRestartTakeOver(Signal* signal, Uint32 startNodeId)
+{
+ switch (getNodeActiveStatus(startNodeId)) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // AN ACTIVE NODE HAS BEEN STARTED. THE ACTIVE NODE MUST THEN GET ALL DATA
+ // IT HAD BEFORE ITS CRASH. WE START THE TAKE OVER IMMEDIATELY.
+ // SINCE WE ARE AN ACTIVE NODE WE WILL TAKE OVER OUR OWN NODE THAT
+ // PREVIOUSLY CRASHED.
+ /*-----------------------------------------------------------------------*/
+ startTakeOver(signal, RNIL, startNodeId, startNodeId);
+ break;
+ case Sysfile::NS_HotSpare:{
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // WHEN STARTING UP A HOT SPARE WE WILL CHECK IF ANY NODE NEEDS TO TAKEN
+ // OVER. IF SO THEN WE WILL START THE TAKE OVER.
+ /*-----------------------------------------------------------------------*/
+ bool takeOverStarted = false;
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->activeStatus == Sysfile::NS_NotActive_NotTakenOver) {
+ jam();
+ takeOverStarted = true;
+ startTakeOver(signal, RNIL, startNodeId, nodePtr.i);
+ }//if
+ }//for
+ if (!takeOverStarted) {
+ jam();
+ /*-------------------------------------------------------------------*/
+ // NO TAKE OVER WAS NEEDED AT THE MOMENT WE START-UP AND WAIT UNTIL A
+ // TAKE OVER IS NEEDED.
+ /*-------------------------------------------------------------------*/
+ BlockReference ref = calcDihBlockRef(startNodeId);
+ signal->theData[0] = startNodeId;
+ sendSignal(ref, GSN_START_COPYCONF, signal, 1, JBB);
+ }//if
+ break;
+ }
+ case Sysfile::NS_NotActive_NotTakenOver:
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // ALL DATA IN THE NODE IS LOST BUT WE HAVE NOT TAKEN OVER YET. WE WILL
+ // TAKE OVER OUR OWN NODE
+ /*-----------------------------------------------------------------------*/
+ startTakeOver(signal, RNIL, startNodeId, startNodeId);
+ break;
+ case Sysfile::NS_TakeOver:{
+ jam();
+ /*--------------------------------------------------------------------
+ * We were in the process of taking over but it was not completed.
+ * We will complete it now instead.
+ *--------------------------------------------------------------------*/
+ Uint32 takeOverNode = Sysfile::getTakeOverNode(startNodeId,
+ SYSFILE->takeOver);
+ startTakeOver(signal, RNIL, startNodeId, takeOverNode);
+ break;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ nodeResetStart();
+}//Dbdih::nodeRestartTakeOver()
+
+/*************************************************************************/
+// Ths routine is called when starting a local checkpoint.
+/*************************************************************************/
+void Dbdih::checkStartTakeOver(Signal* signal)
+{
+ NodeRecordPtr csoNodeptr;
+ Uint32 tcsoHotSpareNode;
+ Uint32 tcsoTakeOverNode;
+ if (isMaster()) {
+ /*-----------------------------------------------------------------*/
+ /* WE WILL ONLY START TAKE OVER IF WE ARE MASTER. */
+ /*-----------------------------------------------------------------*/
+ /* WE WILL ONLY START THE TAKE OVER IF THERE WERE A NEED OF */
+ /* A TAKE OVER. */
+ /*-----------------------------------------------------------------*/
+ /* WE CAN ONLY PERFORM THE TAKE OVER IF WE HAVE A HOT SPARE */
+ /* AVAILABLE. */
+ /*-----------------------------------------------------------------*/
+ tcsoTakeOverNode = 0;
+ tcsoHotSpareNode = 0;
+ for (csoNodeptr.i = 1; csoNodeptr.i < MAX_NDB_NODES; csoNodeptr.i++) {
+ ptrAss(csoNodeptr, nodeRecord);
+ if (csoNodeptr.p->activeStatus == Sysfile::NS_NotActive_NotTakenOver) {
+ jam();
+ tcsoTakeOverNode = csoNodeptr.i;
+ } else {
+ jam();
+ if (csoNodeptr.p->activeStatus == Sysfile::NS_HotSpare) {
+ jam();
+ tcsoHotSpareNode = csoNodeptr.i;
+ }//if
+ }//if
+ }//for
+ if ((tcsoTakeOverNode != 0) &&
+ (tcsoHotSpareNode != 0)) {
+ jam();
+ startTakeOver(signal, RNIL, tcsoHotSpareNode, tcsoTakeOverNode);
+ }//if
+ }//if
+}//Dbdih::checkStartTakeOver()
+
+/*****************************************************************************/
+/*********** NODE ADDING MODULE *************/
+/*********** CODE TO HANDLE TAKE OVER *************/
+/*****************************************************************************/
+// A take over can be initiated by a number of things:
+// 1) A node restart, usually the node takes over itself but can also take
+// over somebody else if its own data was already taken over
+// 2) At system restart it is necessary to use the take over code to recover
+// nodes which had too old checkpoints to be restorable by the usual
+// restoration from disk.
+// 3) When a node has missed too many local checkpoints and is decided by the
+// master to be taken over by a hot spare node that sits around waiting
+// for this to happen.
+//
+// To support multiple node failures efficiently the code is written such that
+// only one take over can handle transitions in state but during a copy
+// fragment other take over's can perform state transitions.
+/*****************************************************************************/
+void Dbdih::startTakeOver(Signal* signal,
+ Uint32 takeOverPtrI,
+ Uint32 startNode,
+ Uint32 nodeTakenOver)
+{
+ NodeRecordPtr toNodePtr;
+ NodeGroupRecordPtr NGPtr;
+ toNodePtr.i = nodeTakenOver;
+ ptrCheckGuard(toNodePtr, MAX_NDB_NODES, nodeRecord);
+ NGPtr.i = toNodePtr.p->nodeGroup;
+ ptrCheckGuard(NGPtr, MAX_NDB_NODES, nodeGroupRecord);
+ TakeOverRecordPtr takeOverPtr;
+ if (takeOverPtrI == RNIL) {
+ jam();
+ setAllowNodeStart(startNode, false);
+ seizeTakeOver(takeOverPtr);
+ if (startNode == c_nodeStartMaster.startNode) {
+ jam();
+ takeOverPtr.p->toNodeRestart = true;
+ }//if
+ takeOverPtr.p->toStartingNode = startNode;
+ takeOverPtr.p->toFailedNode = nodeTakenOver;
+ } else {
+ jam();
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ ndbrequire(takeOverPtr.p->toStartingNode == startNode);
+ ndbrequire(takeOverPtr.p->toFailedNode == nodeTakenOver);
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::TO_WAIT_START_TAKE_OVER);
+ }//if
+ if ((NGPtr.p->activeTakeOver) || (ERROR_INSERTED(7157))) {
+ jam();
+ /**------------------------------------------------------------------------
+ * A take over is already active in this node group. We only allow one
+ * take over per node group. Otherwise we will overload the node group and
+ * also we will require much more checks when starting up copying of
+ * fragments. The parallelism for take over is mainly to ensure that we
+ * can handle take over efficiently in large systems with 4 nodes and above
+ * A typical case is a 8 node system executing on two 8-cpu boxes.
+ * A box crash in one of the boxes will mean 4 nodes crashes.
+ * We want to be able to restart those four nodes to some
+ * extent in parallel.
+ *
+ * We will wait for a few seconds and then try again.
+ */
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_WAIT_START_TAKE_OVER;
+ signal->theData[0] = DihContinueB::ZSTART_TAKE_OVER;
+ signal->theData[1] = takeOverPtr.i;
+ signal->theData[2] = startNode;
+ signal->theData[3] = nodeTakenOver;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 5000, 4);
+ return;
+ }//if
+ NGPtr.p->activeTakeOver = true;
+ if (startNode == nodeTakenOver) {
+ jam();
+ switch (getNodeActiveStatus(nodeTakenOver)) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ break;
+ case Sysfile::NS_NotActive_NotTakenOver:
+ case Sysfile::NS_TakeOver:
+ jam();
+ setNodeActiveStatus(nodeTakenOver, Sysfile::NS_TakeOver);
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+ } else {
+ jam();
+ setNodeActiveStatus(nodeTakenOver, Sysfile::NS_HotSpare);
+ setNodeActiveStatus(startNode, Sysfile::NS_TakeOver);
+ changeNodeGroups(startNode, nodeTakenOver);
+ }//if
+ setNodeRestartInfoBits();
+ /* ---------------------------------------------------------------------- */
+ /* WE SET THE RESTART INFORMATION TO INDICATE THAT WE ARE ABOUT TO TAKE */
+ /* OVER THE FAILED NODE. WE SET THIS INFORMATION AND WAIT UNTIL THE */
+ /* GLOBAL CHECKPOINT HAS WRITTEN THE RESTART INFORMATION. */
+ /* ---------------------------------------------------------------------- */
+ Sysfile::setTakeOverNode(takeOverPtr.p->toFailedNode, SYSFILE->takeOver,
+ startNode);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_START_COPY;
+
+ cstartGcpNow = true;
+}//Dbdih::startTakeOver()
+
+void Dbdih::changeNodeGroups(Uint32 startNode, Uint32 nodeTakenOver)
+{
+ NodeRecordPtr startNodePtr;
+ NodeRecordPtr toNodePtr;
+ startNodePtr.i = startNode;
+ ptrCheckGuard(startNodePtr, MAX_NDB_NODES, nodeRecord);
+ toNodePtr.i = nodeTakenOver;
+ ptrCheckGuard(toNodePtr, MAX_NDB_NODES, nodeRecord);
+ ndbrequire(startNodePtr.p->nodeGroup == ZNIL);
+ NodeGroupRecordPtr NGPtr;
+
+ NGPtr.i = toNodePtr.p->nodeGroup;
+ ptrCheckGuard(NGPtr, MAX_NDB_NODES, nodeGroupRecord);
+ bool nodeFound = false;
+ for (Uint32 i = 0; i < NGPtr.p->nodeCount; i++) {
+ jam();
+ if (NGPtr.p->nodesInGroup[i] == nodeTakenOver) {
+ jam();
+ NGPtr.p->nodesInGroup[i] = startNode;
+ nodeFound = true;
+ }//if
+ }//for
+ ndbrequire(nodeFound);
+ Sysfile::setNodeGroup(startNodePtr.i, SYSFILE->nodeGroups, toNodePtr.p->nodeGroup);
+ startNodePtr.p->nodeGroup = toNodePtr.p->nodeGroup;
+ Sysfile::setNodeGroup(toNodePtr.i, SYSFILE->nodeGroups, NO_NODE_GROUP_ID);
+ toNodePtr.p->nodeGroup = ZNIL;
+}//Dbdih::changeNodeGroups()
+
+void Dbdih::checkToCopy()
+{
+ TakeOverRecordPtr takeOverPtr;
+ for (takeOverPtr.i = 0;takeOverPtr.i < MAX_NDB_NODES; takeOverPtr.i++) {
+ ptrAss(takeOverPtr, takeOverRecord);
+ /*----------------------------------------------------------------------*/
+ // TAKE OVER HANDLING WRITES RESTART INFORMATION THROUGH
+ // THE GLOBAL CHECKPOINT
+ // PROTOCOL. WE CHECK HERE BEFORE STARTING A WRITE OF THE RESTART
+ // INFORMATION.
+ /*-----------------------------------------------------------------------*/
+ if (takeOverPtr.p->toMasterStatus == TakeOverRecord::TO_START_COPY) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_START_COPY_ONGOING;
+ } else if (takeOverPtr.p->toMasterStatus == TakeOverRecord::TO_END_COPY) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_END_COPY_ONGOING;
+ }//if
+ }//for
+}//Dbdih::checkToCopy()
+
+void Dbdih::checkToCopyCompleted(Signal* signal)
+{
+ /* ------------------------------------------------------------------------*/
+ /* WE CHECK HERE IF THE WRITING OF TAKE OVER INFORMATION ALSO HAS BEEN */
+ /* COMPLETED. */
+ /* ------------------------------------------------------------------------*/
+ TakeOverRecordPtr toPtr;
+ for (toPtr.i = 0; toPtr.i < MAX_NDB_NODES; toPtr.i++) {
+ ptrAss(toPtr, takeOverRecord);
+ if (toPtr.p->toMasterStatus == TakeOverRecord::TO_START_COPY_ONGOING){
+ jam();
+ sendStartTo(signal, toPtr.i);
+ } else if (toPtr.p->toMasterStatus == TakeOverRecord::TO_END_COPY_ONGOING){
+ jam();
+ sendEndTo(signal, toPtr.i);
+ } else {
+ jam();
+ }//if
+ }//for
+}//Dbdih::checkToCopyCompleted()
+
+bool Dbdih::checkToInterrupted(TakeOverRecordPtr& takeOverPtr)
+{
+ if (checkNodeAlive(takeOverPtr.p->toStartingNode)) {
+ jam();
+ return false;
+ } else {
+ jam();
+ endTakeOver(takeOverPtr.i);
+ return true;
+ }//if
+}//Dbdih::checkToInterrupted()
+
+void Dbdih::sendStartTo(Signal* signal, Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ CRASH_INSERTION(7155);
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ if ((c_startToLock != RNIL) || (ERROR_INSERTED(7158))) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_WAIT_START;
+ signal->theData[0] = DihContinueB::ZSEND_START_TO;
+ signal->theData[1] = takeOverPtrI;
+ signal->theData[2] = takeOverPtr.p->toStartingNode;
+ signal->theData[3] = takeOverPtr.p->toFailedNode;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 30, 4);
+ return;
+ }//if
+ c_startToLock = takeOverPtrI;
+ StartToReq * const req = (StartToReq *)&signal->theData[0];
+ req->userPtr = takeOverPtr.i;
+ req->userRef = reference();
+ req->startingNodeId = takeOverPtr.p->toStartingNode;
+ req->nodeTakenOver = takeOverPtr.p->toFailedNode;
+ req->nodeRestart = takeOverPtr.p->toNodeRestart;
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::STARTING;
+ sendLoopMacro(START_TOREQ, sendSTART_TOREQ);
+}//Dbdih::sendStartTo()
+
+void Dbdih::execSTART_TOREQ(Signal* signal)
+{
+ TakeOverRecordPtr takeOverPtr;
+ jamEntry();
+ const StartToReq * const req = (StartToReq *)&signal->theData[0];
+ takeOverPtr.i = req->userPtr;
+ BlockReference ref = req->userRef;
+ Uint32 startingNode = req->startingNodeId;
+
+ CRASH_INSERTION(7133);
+ RETURN_IF_NODE_NOT_ALIVE(req->startingNodeId);
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ allocateTakeOver(takeOverPtr);
+ initStartTakeOver(req, takeOverPtr);
+
+ StartToConf * const conf = (StartToConf *)&signal->theData[0];
+ conf->userPtr = takeOverPtr.i;
+ conf->sendingNodeId = cownNodeId;
+ conf->startingNodeId = startingNode;
+ sendSignal(ref, GSN_START_TOCONF, signal, StartToConf::SignalLength, JBB);
+}//Dbdih::execSTART_TOREQ()
+
+void Dbdih::execSTART_TOCONF(Signal* signal)
+{
+ TakeOverRecordPtr takeOverPtr;
+ jamEntry();
+ const StartToConf * const conf = (StartToConf *)&signal->theData[0];
+
+ CRASH_INSERTION(7147);
+
+ RETURN_IF_NODE_NOT_ALIVE(conf->startingNodeId);
+
+ takeOverPtr.i = conf->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::STARTING);
+ ndbrequire(takeOverPtr.p->toStartingNode == conf->startingNodeId);
+ receiveLoopMacro(START_TOREQ, conf->sendingNodeId);
+ CRASH_INSERTION(7134);
+ c_startToLock = RNIL;
+
+ startNextCopyFragment(signal, takeOverPtr.i);
+}//Dbdih::execSTART_TOCONF()
+
+void Dbdih::initStartTakeOver(const StartToReq * req,
+ TakeOverRecordPtr takeOverPtr)
+{
+ takeOverPtr.p->toCurrentTabref = 0;
+ takeOverPtr.p->toCurrentFragid = 0;
+ takeOverPtr.p->toStartingNode = req->startingNodeId;
+ takeOverPtr.p->toFailedNode = req->nodeTakenOver;
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_STARTED;
+ takeOverPtr.p->toCopyNode = RNIL;
+ takeOverPtr.p->toCurrentReplica = RNIL;
+ takeOverPtr.p->toNodeRestart = req->nodeRestart;
+}//Dbdih::initStartTakeOver()
+
+void Dbdih::startNextCopyFragment(Signal* signal, Uint32 takeOverPtrI)
+{
+ TabRecordPtr tabPtr;
+ TakeOverRecordPtr takeOverPtr;
+ Uint32 loopCount;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::SELECTING_NEXT;
+ loopCount = 0;
+ if (ERROR_INSERTED(7159)) {
+ loopCount = 100;
+ }//if
+ while (loopCount++ < 100) {
+ tabPtr.i = takeOverPtr.p->toCurrentTabref;
+ if (tabPtr.i >= ctabFileSize) {
+ jam();
+ CRASH_INSERTION(7136);
+ sendUpdateTo(signal, takeOverPtr.i, UpdateToReq::TO_COPY_COMPLETED);
+ return;
+ }//if
+ ptrAss(tabPtr, tabRecord);
+ if (tabPtr.p->tabStatus != TabRecord::TS_ACTIVE){
+ jam();
+ takeOverPtr.p->toCurrentFragid = 0;
+ takeOverPtr.p->toCurrentTabref++;
+ continue;
+ }//if
+ Uint32 fragId = takeOverPtr.p->toCurrentFragid;
+ if (fragId >= tabPtr.p->totalfragments) {
+ jam();
+ takeOverPtr.p->toCurrentFragid = 0;
+ takeOverPtr.p->toCurrentTabref++;
+ if (ERROR_INSERTED(7135)) {
+ if (takeOverPtr.p->toCurrentTabref == 1) {
+ ndbrequire(false);
+ }//if
+ }//if
+ continue;
+ }//if
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ ReplicaRecordPtr loopReplicaPtr;
+ loopReplicaPtr.i = fragPtr.p->oldStoredReplicas;
+ while (loopReplicaPtr.i != RNIL) {
+ ptrCheckGuard(loopReplicaPtr, creplicaFileSize, replicaRecord);
+ if (loopReplicaPtr.p->procNode == takeOverPtr.p->toFailedNode) {
+ jam();
+ /* ----------------------------------------------------------------- */
+ /* WE HAVE FOUND A REPLICA THAT BELONGED THE FAILED NODE THAT NEEDS */
+ /* TAKE OVER. WE TAKE OVER THIS REPLICA TO THE NEW NODE. */
+ /* ----------------------------------------------------------------- */
+ takeOverPtr.p->toCurrentReplica = loopReplicaPtr.i;
+ toCopyFragLab(signal, takeOverPtr.i);
+ return;
+ } else if (loopReplicaPtr.p->procNode == takeOverPtr.p->toStartingNode) {
+ jam();
+ /* ----------------------------------------------------------------- */
+ /* WE HAVE OBVIOUSLY STARTED TAKING OVER THIS WITHOUT COMPLETING IT. */
+ /* WE */
+ /* NEED TO COMPLETE THE TAKE OVER OF THIS REPLICA. */
+ /* ----------------------------------------------------------------- */
+ takeOverPtr.p->toCurrentReplica = loopReplicaPtr.i;
+ toCopyFragLab(signal, takeOverPtr.i);
+ return;
+ } else {
+ jam();
+ loopReplicaPtr.i = loopReplicaPtr.p->nextReplica;
+ }//if
+ }//while
+ takeOverPtr.p->toCurrentFragid++;
+ }//while
+ signal->theData[0] = DihContinueB::ZTO_START_COPY_FRAG;
+ signal->theData[1] = takeOverPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+}//Dbdih::startNextCopyFragment()
+
+void Dbdih::toCopyFragLab(Signal* signal,
+ Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+
+ CreateReplicaRecordPtr createReplicaPtr;
+ createReplicaPtr.i = 0;
+ ptrAss(createReplicaPtr, createReplicaRecord);
+
+ ReplicaRecordPtr replicaPtr;
+ replicaPtr.i = takeOverPtr.p->toCurrentReplica;
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = takeOverPtr.p->toCurrentTabref;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE FOUND A REPLICA THAT NEEDS TAKE OVER. WE WILL START THIS TAKE */
+ /* OVER BY ADDING THE FRAGMENT WHEREAFTER WE WILL ORDER THE PRIMARY */
+ /* REPLICA TO COPY ITS CONTENT TO THE NEW STARTING REPLICA. */
+ /* THIS OPERATION IS A SINGLE USER OPERATION UNTIL WE HAVE SENT */
+ /* COPY_FRAGREQ. AFTER SENDING COPY_FRAGREQ WE ARE READY TO START A NEW */
+ /* FRAGMENT REPLICA. WE WILL NOT IMPLEMENT THIS IN THE FIRST PHASE. */
+ /* ----------------------------------------------------------------------- */
+ cnoOfCreateReplicas = 1;
+ createReplicaPtr.p->hotSpareUse = true;
+ createReplicaPtr.p->dataNodeId = takeOverPtr.p->toStartingNode;
+
+ prepareSendCreateFragReq(signal, takeOverPtrI);
+}//Dbdih::toCopyFragLab()
+
+void Dbdih::prepareSendCreateFragReq(Signal* signal, Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = takeOverPtr.p->toCurrentTabref;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ FragmentstorePtr fragPtr;
+
+ getFragstore(tabPtr.p, takeOverPtr.p->toCurrentFragid, fragPtr);
+ Uint32 nodes[MAX_REPLICAS];
+ extractNodeInfo(fragPtr.p, nodes);
+ takeOverPtr.p->toCopyNode = nodes[0];
+ sendCreateFragReq(signal, 0, CreateFragReq::STORED, takeOverPtr.i);
+}//Dbdih::prepareSendCreateFragReq()
+
+void Dbdih::sendCreateFragReq(Signal* signal,
+ Uint32 startGci,
+ Uint32 replicaType,
+ Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ if ((c_createFragmentLock != RNIL) ||
+ ((ERROR_INSERTED(7161))&&(replicaType == CreateFragReq::STORED)) ||
+ ((ERROR_INSERTED(7162))&&(replicaType == CreateFragReq::COMMIT_STORED))){
+ if (replicaType == CreateFragReq::STORED) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_WAIT_PREPARE_CREATE;
+ } else {
+ ndbrequire(replicaType == CreateFragReq::COMMIT_STORED);
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_WAIT_COMMIT_CREATE;
+ }//if
+ signal->theData[0] = DihContinueB::ZSEND_CREATE_FRAG;
+ signal->theData[1] = takeOverPtr.i;
+ signal->theData[2] = replicaType;
+ signal->theData[3] = startGci;
+ signal->theData[4] = takeOverPtr.p->toStartingNode;
+ signal->theData[5] = takeOverPtr.p->toFailedNode;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 50, 6);
+ return;
+ }//if
+ c_createFragmentLock = takeOverPtr.i;
+ sendLoopMacro(CREATE_FRAGREQ, nullRoutine);
+
+ CreateFragReq * const req = (CreateFragReq *)&signal->theData[0];
+ req->userPtr = takeOverPtr.i;
+ req->userRef = reference();
+ req->tableId = takeOverPtr.p->toCurrentTabref;
+ req->fragId = takeOverPtr.p->toCurrentFragid;
+ req->startingNodeId = takeOverPtr.p->toStartingNode;
+ req->copyNodeId = takeOverPtr.p->toCopyNode;
+ req->startGci = startGci;
+ req->replicaType = replicaType;
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = cfirstAliveNode;
+ do {
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ BlockReference ref = calcDihBlockRef(nodePtr.i);
+ sendSignal(ref, GSN_CREATE_FRAGREQ, signal,
+ CreateFragReq::SignalLength, JBB);
+ nodePtr.i = nodePtr.p->nextNode;
+ } while (nodePtr.i != RNIL);
+
+ if (replicaType == CreateFragReq::STORED) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::PREPARE_CREATE;
+ } else {
+ ndbrequire(replicaType == CreateFragReq::COMMIT_STORED);
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::COMMIT_CREATE;
+ }
+}//Dbdih::sendCreateFragReq()
+
+/* --------------------------------------------------------------------------*/
+/* AN ORDER TO START OR COMMIT THE REPLICA CREATION ARRIVED FROM THE */
+/* MASTER. */
+/* --------------------------------------------------------------------------*/
+void Dbdih::execCREATE_FRAGREQ(Signal* signal)
+{
+ jamEntry();
+ CreateFragReq * const req = (CreateFragReq *)&signal->theData[0];
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = req->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ BlockReference retRef = req->userRef;
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ Uint32 fragId = req->fragId;
+ Uint32 tdestNodeid = req->startingNodeId;
+ Uint32 tsourceNodeid = req->copyNodeId;
+ Uint32 startGci = req->startGci;
+ Uint32 replicaType = req->replicaType;
+
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ RETURN_IF_NODE_NOT_ALIVE(tdestNodeid);
+ ReplicaRecordPtr frReplicaPtr;
+ findToReplica(takeOverPtr.p, replicaType, fragPtr, frReplicaPtr);
+ ndbrequire(frReplicaPtr.i != RNIL);
+
+ switch (replicaType) {
+ case CreateFragReq::STORED:
+ jam();
+ CRASH_INSERTION(7138);
+ /* ----------------------------------------------------------------------*/
+ /* HERE WE ARE INSERTING THE NEW BACKUP NODE IN THE EXECUTION OF ALL */
+ /* OPERATIONS. FROM HERE ON ALL OPERATIONS ON THIS FRAGMENT WILL INCLUDE*/
+ /* USE OF THE NEW REPLICA. */
+ /* --------------------------------------------------------------------- */
+ insertBackup(fragPtr, tdestNodeid);
+ takeOverPtr.p->toCopyNode = tsourceNodeid;
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_CREATE_PREPARE;
+
+ fragPtr.p->distributionKey++;
+ fragPtr.p->distributionKey &= 255;
+ break;
+ case CreateFragReq::COMMIT_STORED:
+ jam();
+ CRASH_INSERTION(7139);
+ /* ----------------------------------------------------------------------*/
+ /* HERE WE ARE MOVING THE REPLICA TO THE STORED SECTION SINCE IT IS NOW */
+ /* FULLY LOADED WITH ALL DATA NEEDED. */
+ // We also update the order of the replicas here so that if the new
+ // replica is the desired primary we insert it as primary.
+ /* ----------------------------------------------------------------------*/
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_CREATE_COMMIT;
+ removeOldStoredReplica(fragPtr, frReplicaPtr);
+ linkStoredReplica(fragPtr, frReplicaPtr);
+ updateNodeInfo(fragPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+
+ /* ------------------------------------------------------------------------*/
+ /* THE NEW NODE OF THIS REPLICA IS THE STARTING NODE. */
+ /* ------------------------------------------------------------------------*/
+ if (frReplicaPtr.p->procNode != takeOverPtr.p->toStartingNode) {
+ jam();
+ /* ---------------------------------------------------------------------*/
+ /* IF WE ARE STARTING A TAKE OVER NODE WE MUST INVALIDATE ALL LCP'S. */
+ /* OTHERWISE WE WILL TRY TO START LCP'S THAT DO NOT EXIST. */
+ /* ---------------------------------------------------------------------*/
+ frReplicaPtr.p->procNode = takeOverPtr.p->toStartingNode;
+ frReplicaPtr.p->noCrashedReplicas = 0;
+ frReplicaPtr.p->createGci[0] = startGci;
+ ndbrequire(startGci != 0xF1F1F1F1);
+ frReplicaPtr.p->replicaLastGci[0] = (Uint32)-1;
+ for (Uint32 i = 0; i < MAX_LCP_STORED; i++) {
+ frReplicaPtr.p->lcpStatus[i] = ZINVALID;
+ }//for
+ } else {
+ jam();
+ const Uint32 noCrashed = frReplicaPtr.p->noCrashedReplicas;
+ arrGuard(noCrashed, 8);
+ frReplicaPtr.p->createGci[noCrashed] = startGci;
+ ndbrequire(startGci != 0xF1F1F1F1);
+ frReplicaPtr.p->replicaLastGci[noCrashed] = (Uint32)-1;
+ }//if
+ takeOverPtr.p->toCurrentTabref = tabPtr.i;
+ takeOverPtr.p->toCurrentFragid = fragId;
+ CreateFragConf * const conf = (CreateFragConf *)&signal->theData[0];
+ conf->userPtr = takeOverPtr.i;
+ conf->tableId = tabPtr.i;
+ conf->fragId = fragId;
+ conf->sendingNodeId = cownNodeId;
+ conf->startingNodeId = tdestNodeid;
+ sendSignal(retRef, GSN_CREATE_FRAGCONF, signal,
+ CreateFragConf::SignalLength, JBB);
+}//Dbdih::execCREATE_FRAGREQ()
+
+void Dbdih::execCREATE_FRAGCONF(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(7148);
+ const CreateFragConf * const conf = (CreateFragConf *)&signal->theData[0];
+ Uint32 fragId = conf->fragId;
+
+ RETURN_IF_NODE_NOT_ALIVE(conf->startingNodeId);
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = conf->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = conf->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ ndbrequire(tabPtr.i == takeOverPtr.p->toCurrentTabref);
+ ndbrequire(fragId == takeOverPtr.p->toCurrentFragid);
+ receiveLoopMacro(CREATE_FRAGREQ, conf->sendingNodeId);
+ c_createFragmentLock = RNIL;
+
+ if (takeOverPtr.p->toMasterStatus == TakeOverRecord::PREPARE_CREATE) {
+ jam();
+ CRASH_INSERTION(7140);
+ /* --------------------------------------------------------------------- */
+ /* ALL NODES HAVE PREPARED THE INTRODUCTION OF THIS NEW NODE AND IT IS */
+ /* ALREADY IN USE. WE CAN NOW START COPYING THE FRAGMENT. */
+ /*---------------------------------------------------------------------- */
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::COPY_FRAG;
+ BlockReference ref = calcLqhBlockRef(takeOverPtr.p->toCopyNode);
+ CopyFragReq * const copyFragReq = (CopyFragReq *)&signal->theData[0];
+ copyFragReq->userPtr = takeOverPtr.i;
+ copyFragReq->userRef = reference();
+ copyFragReq->tableId = tabPtr.i;
+ copyFragReq->fragId = fragId;
+ copyFragReq->nodeId = takeOverPtr.p->toStartingNode;
+ copyFragReq->schemaVersion = tabPtr.p->schemaVersion;
+ copyFragReq->distributionKey = fragPtr.p->distributionKey;
+ sendSignal(ref, GSN_COPY_FRAGREQ, signal, CopyFragReq::SignalLength, JBB);
+ } else {
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::COMMIT_CREATE);
+ jam();
+ CRASH_INSERTION(7141);
+ /* --------------------------------------------------------------------- */
+ // REPORT that copy of fragment has been completed.
+ /* --------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NR_CopyFragDone;
+ signal->theData[1] = takeOverPtr.p->toStartingNode;
+ signal->theData[2] = tabPtr.i;
+ signal->theData[3] = takeOverPtr.p->toCurrentFragid;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+ /* --------------------------------------------------------------------- */
+ /* WE HAVE NOW CREATED THIS NEW REPLICA AND WE ARE READY TO TAKE THE */
+ /* THE NEXT REPLICA. */
+ /* --------------------------------------------------------------------- */
+
+ Mutex mutex(signal, c_mutexMgr, takeOverPtr.p->m_switchPrimaryMutexHandle);
+ mutex.unlock(); // ignore result
+
+ takeOverPtr.p->toCurrentFragid++;
+ startNextCopyFragment(signal, takeOverPtr.i);
+ }//if
+}//Dbdih::execCREATE_FRAGCONF()
+
+void Dbdih::execCOPY_FRAGREF(Signal* signal)
+{
+ const CopyFragRef * const ref = (CopyFragRef *)&signal->theData[0];
+ jamEntry();
+ Uint32 takeOverPtrI = ref->userPtr;
+ Uint32 startingNodeId = ref->startingNodeId;
+ Uint32 errorCode = ref->errorCode;
+
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ ndbrequire(errorCode != ZNODE_FAILURE_ERROR);
+ ndbrequire(ref->tableId == takeOverPtr.p->toCurrentTabref);
+ ndbrequire(ref->fragId == takeOverPtr.p->toCurrentFragid);
+ ndbrequire(ref->startingNodeId == takeOverPtr.p->toStartingNode);
+ ndbrequire(ref->sendingNodeId == takeOverPtr.p->toCopyNode);
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::COPY_FRAG);
+ endTakeOver(takeOverPtrI);
+ //--------------------------------------------------------------------------
+ // For some reason we did not succeed in copying a fragment. We treat this
+ // as a serious failure and crash the starting node.
+ //--------------------------------------------------------------------------
+ BlockReference cntrRef = calcNdbCntrBlockRef(startingNodeId);
+ SystemError * const sysErr = (SystemError*)&signal->theData[0];
+ sysErr->errorCode = SystemError::CopyFragRefError;
+ sysErr->errorRef = reference();
+ sysErr->data1 = errorCode;
+ sysErr->data2 = 0;
+ sendSignal(cntrRef, GSN_SYSTEM_ERROR, signal,
+ SystemError::SignalLength, JBB);
+ return;
+}//Dbdih::execCOPY_FRAGREF()
+
+void Dbdih::execCOPY_FRAGCONF(Signal* signal)
+{
+ const CopyFragConf * const conf = (CopyFragConf *)&signal->theData[0];
+ jamEntry();
+ CRASH_INSERTION(7142);
+
+ TakeOverRecordPtr takeOverPtr;
+ Uint32 takeOverPtrI = conf->userPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+
+ ndbrequire(conf->tableId == takeOverPtr.p->toCurrentTabref);
+ ndbrequire(conf->fragId == takeOverPtr.p->toCurrentFragid);
+ ndbrequire(conf->startingNodeId == takeOverPtr.p->toStartingNode);
+ ndbrequire(conf->sendingNodeId == takeOverPtr.p->toCopyNode);
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::COPY_FRAG);
+ sendUpdateTo(signal, takeOverPtr.i,
+ (Uint32)UpdateToReq::TO_COPY_FRAG_COMPLETED);
+}//Dbdih::execCOPY_FRAGCONF()
+
+void Dbdih::sendUpdateTo(Signal* signal,
+ Uint32 takeOverPtrI, Uint32 updateState)
+{
+ TakeOverRecordPtr takeOverPtr;
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ if ((c_updateToLock != RNIL) ||
+ ((ERROR_INSERTED(7163)) &&
+ (updateState == UpdateToReq::TO_COPY_FRAG_COMPLETED)) ||
+ ((ERROR_INSERTED(7169)) &&
+ (updateState == UpdateToReq::TO_COPY_COMPLETED))) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_WAIT_UPDATE_TO;
+ signal->theData[0] = DihContinueB::ZSEND_UPDATE_TO;
+ signal->theData[1] = takeOverPtrI;
+ signal->theData[2] = takeOverPtr.p->toStartingNode;
+ signal->theData[3] = takeOverPtr.p->toFailedNode;
+ signal->theData[4] = updateState;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 30, 5);
+ return;
+ }//if
+ c_updateToLock = takeOverPtrI;
+ if (updateState == UpdateToReq::TO_COPY_FRAG_COMPLETED) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_UPDATE_TO;
+ } else {
+ jam();
+ ndbrequire(updateState == UpdateToReq::TO_COPY_COMPLETED);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_COPY_COMPLETED;
+ }//if
+
+ UpdateToReq * const req = (UpdateToReq *)&signal->theData[0];
+ req->userPtr = takeOverPtr.i;
+ req->userRef = reference();
+ req->updateState = (UpdateToReq::UpdateState)updateState;
+ req->startingNodeId = takeOverPtr.p->toStartingNode;
+ req->tableId = takeOverPtr.p->toCurrentTabref;
+ req->fragmentNo = takeOverPtr.p->toCurrentFragid;
+ sendLoopMacro(UPDATE_TOREQ, sendUPDATE_TOREQ);
+}//Dbdih::sendUpdateTo()
+
+void Dbdih::execUPDATE_TOREQ(Signal* signal)
+{
+ jamEntry();
+ const UpdateToReq * const req = (UpdateToReq *)&signal->theData[0];
+ BlockReference ref = req->userRef;
+ ndbrequire(cmasterdihref == ref);
+
+ CRASH_INSERTION(7154);
+ RETURN_IF_NODE_NOT_ALIVE(req->startingNodeId);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = req->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ ndbrequire(req->startingNodeId == takeOverPtr.p->toStartingNode);
+ if (req->updateState == UpdateToReq::TO_COPY_FRAG_COMPLETED) {
+ jam();
+ ndbrequire(takeOverPtr.p->toSlaveStatus == TakeOverRecord::TO_SLAVE_CREATE_PREPARE);
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_COPY_FRAG_COMPLETED;
+ takeOverPtr.p->toCurrentTabref = req->tableId;
+ takeOverPtr.p->toCurrentFragid = req->fragmentNo;
+ } else {
+ jam();
+ ndbrequire(req->updateState == UpdateToReq::TO_COPY_COMPLETED);
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_COPY_COMPLETED;
+ setNodeCopyCompleted(takeOverPtr.p->toStartingNode, true);
+ }//if
+
+
+ UpdateToConf * const conf = (UpdateToConf *)&signal->theData[0];
+ conf->userPtr = takeOverPtr.i;
+ conf->sendingNodeId = cownNodeId;
+ conf->startingNodeId = takeOverPtr.p->toStartingNode;
+ sendSignal(ref, GSN_UPDATE_TOCONF, signal, UpdateToConf::SignalLength, JBB);
+}//Dbdih::execUPDATE_TOREQ()
+
+void Dbdih::execUPDATE_TOCONF(Signal* signal)
+{
+ const UpdateToConf * const conf = (UpdateToConf *)&signal->theData[0];
+ CRASH_INSERTION(7152);
+
+ RETURN_IF_NODE_NOT_ALIVE(conf->startingNodeId);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = conf->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ receiveLoopMacro(UPDATE_TOREQ, conf->sendingNodeId);
+ CRASH_INSERTION(7153);
+ c_updateToLock = RNIL;
+
+ if (takeOverPtr.p->toMasterStatus == TakeOverRecord::TO_COPY_COMPLETED) {
+ jam();
+ toCopyCompletedLab(signal, takeOverPtr);
+ return;
+ } else {
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::TO_UPDATE_TO);
+ }//if
+ TabRecordPtr tabPtr;
+ tabPtr.i = takeOverPtr.p->toCurrentTabref;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, takeOverPtr.p->toCurrentFragid, fragPtr);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::COPY_ACTIVE;
+ BlockReference lqhRef = calcLqhBlockRef(takeOverPtr.p->toStartingNode);
+ CopyActiveReq * const req = (CopyActiveReq *)&signal->theData[0];
+ req->userPtr = takeOverPtr.i;
+ req->userRef = reference();
+ req->tableId = takeOverPtr.p->toCurrentTabref;
+ req->fragId = takeOverPtr.p->toCurrentFragid;
+ req->distributionKey = fragPtr.p->distributionKey;
+
+ sendSignal(lqhRef, GSN_COPY_ACTIVEREQ, signal,
+ CopyActiveReq::SignalLength, JBB);
+}//Dbdih::execUPDATE_TOCONF()
+
+void Dbdih::execCOPY_ACTIVECONF(Signal* signal)
+{
+ const CopyActiveConf * const conf = (CopyActiveConf *)&signal->theData[0];
+ jamEntry();
+ CRASH_INSERTION(7143);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = conf->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ ndbrequire(conf->tableId == takeOverPtr.p->toCurrentTabref);
+ ndbrequire(conf->fragId == takeOverPtr.p->toCurrentFragid);
+ ndbrequire(checkNodeAlive(conf->startingNodeId));
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::COPY_ACTIVE);
+
+ takeOverPtr.p->startGci = conf->startGci;
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::LOCK_MUTEX;
+
+ Mutex mutex(signal, c_mutexMgr, takeOverPtr.p->m_switchPrimaryMutexHandle);
+ Callback c = { safe_cast(&Dbdih::switchPrimaryMutex_locked), takeOverPtr.i };
+ ndbrequire(mutex.lock(c));
+}//Dbdih::execCOPY_ACTIVECONF()
+
+void
+Dbdih::switchPrimaryMutex_locked(Signal* signal, Uint32 toPtrI, Uint32 retVal){
+ jamEntry();
+ ndbrequire(retVal == 0);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = toPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::LOCK_MUTEX);
+
+ if (!checkNodeAlive((takeOverPtr.p->toStartingNode))) {
+ // We have mutex
+ Mutex mutex(signal, c_mutexMgr, takeOverPtr.p->m_switchPrimaryMutexHandle);
+ mutex.unlock(); // Ignore result
+
+ c_createFragmentLock = RNIL;
+ c_CREATE_FRAGREQ_Counter.clearWaitingFor();
+ endTakeOver(takeOverPtr.i);
+ return;
+ }
+
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::COMMIT_CREATE;
+ sendCreateFragReq(signal, takeOverPtr.p->startGci,
+ CreateFragReq::COMMIT_STORED, takeOverPtr.i);
+}
+
+void Dbdih::toCopyCompletedLab(Signal * signal, TakeOverRecordPtr takeOverPtr)
+{
+ signal->theData[0] = NDB_LE_NR_CopyFragsCompleted;
+ signal->theData[1] = takeOverPtr.p->toStartingNode;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ c_lcpState.immediateLcpStart = true;
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::WAIT_LCP;
+
+ /*-----------------------------------------------------------------------*/
+ /* NOW WE CAN ALLOW THE NEW NODE TO PARTICIPATE IN LOCAL CHECKPOINTS. */
+ /* WHEN THE FIRST LOCAL CHECKPOINT IS READY WE DECLARE THE TAKE OVER AS */
+ /* COMPLETED. SINCE LOCAL CHECKPOINTS HAVE BEEN BLOCKED DURING THE COPY */
+ /* PROCESS WE MUST ALSO START A NEW LOCAL CHECKPOINT PROCESS BY ENSURING */
+ /* THAT IT LOOKS LIKE IT IS TIME FOR A NEW LOCAL CHECKPOINT AND BY */
+ /* UNBLOCKING THE LOCAL CHECKPOINT AGAIN. */
+ /* --------------------------------------------------------------------- */
+}//Dbdih::toCopyCompletedLab()
+
+void Dbdih::sendEndTo(Signal* signal, Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ CRASH_INSERTION(7156);
+ RETURN_IF_TAKE_OVER_INTERRUPTED(takeOverPtrI, takeOverPtr);
+ if ((c_endToLock != RNIL) || (ERROR_INSERTED(7164))) {
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_WAIT_ENDING;
+ signal->theData[0] = DihContinueB::ZSEND_END_TO;
+ signal->theData[1] = takeOverPtrI;
+ signal->theData[2] = takeOverPtr.p->toStartingNode;
+ signal->theData[3] = takeOverPtr.p->toFailedNode;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 30, 4);
+ return;
+ }//if
+ c_endToLock = takeOverPtr.i;
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::ENDING;
+ EndToReq * const req = (EndToReq *)&signal->theData[0];
+ req->userPtr = takeOverPtr.i;
+ req->userRef = reference();
+ req->startingNodeId = takeOverPtr.p->toStartingNode;
+ sendLoopMacro(END_TOREQ, sendEND_TOREQ);
+}//Dbdih::sendStartTo()
+
+void Dbdih::execEND_TOREQ(Signal* signal)
+{
+ jamEntry();
+ const EndToReq * const req = (EndToReq *)&signal->theData[0];
+ BlockReference ref = req->userRef;
+ Uint32 startingNodeId = req->startingNodeId;
+
+ CRASH_INSERTION(7144);
+ RETURN_IF_NODE_NOT_ALIVE(startingNodeId);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = req->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ ndbrequire(startingNodeId == takeOverPtr.p->toStartingNode);
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_IDLE;
+
+ if (!isMaster()) {
+ jam();
+ endTakeOver(takeOverPtr.i);
+ }//if
+
+ EndToConf * const conf = (EndToConf *)&signal->theData[0];
+ conf->userPtr = takeOverPtr.i;
+ conf->sendingNodeId = cownNodeId;
+ conf->startingNodeId = startingNodeId;
+ sendSignal(ref, GSN_END_TOCONF, signal, EndToConf::SignalLength, JBB);
+}//Dbdih::execEND_TOREQ()
+
+void Dbdih::execEND_TOCONF(Signal* signal)
+{
+ const EndToConf * const conf = (EndToConf *)&signal->theData[0];
+ jamEntry();
+
+ const Uint32 nodeId = conf->startingNodeId;
+ CRASH_INSERTION(7145);
+
+ RETURN_IF_NODE_NOT_ALIVE(nodeId);
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = conf->userPtr;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ ndbrequire(takeOverPtr.p->toMasterStatus == TakeOverRecord::ENDING);
+ ndbrequire(nodeId == takeOverPtr.p->toStartingNode);
+
+ receiveLoopMacro(END_TOREQ, conf->sendingNodeId);
+ CRASH_INSERTION(7146);
+ c_endToLock = RNIL;
+
+ /* -----------------------------------------------------------------------*/
+ /* WE HAVE FINALLY COMPLETED THE TAKE OVER. WE RESET THE STATUS AND CHECK*/
+ /* IF ANY MORE TAKE OVERS ARE NEEDED AT THE MOMENT. */
+ /* FIRST WE CHECK IF A RESTART IS ONGOING. IN THAT CASE WE RESTART PHASE */
+ /* 4 AND CHECK IF ANY MORE TAKE OVERS ARE NEEDED BEFORE WE START NDB */
+ /* CLUSTER. THIS CAN ONLY HAPPEN IN A SYSTEM RESTART. */
+ /* ---------------------------------------------------------------------- */
+ if (takeOverPtr.p->toNodeRestart) {
+ jam();
+ /* ----------------------------------------------------------------------*/
+ /* THE TAKE OVER NODE WAS A STARTING NODE. WE WILL SEND START_COPYCONF */
+ /* TO THE STARTING NODE SUCH THAT THE NODE CAN COMPLETE THE START-UP. */
+ /* --------------------------------------------------------------------- */
+ BlockReference ref = calcDihBlockRef(takeOverPtr.p->toStartingNode);
+ signal->theData[0] = takeOverPtr.p->toStartingNode;
+ sendSignal(ref, GSN_START_COPYCONF, signal, 1,JBB);
+ }//if
+ endTakeOver(takeOverPtr.i);
+
+ ndbout_c("2 - endTakeOver");
+ if (cstartPhase == ZNDB_SPH4) {
+ jam();
+ ndbrequire(false);
+ if (anyActiveTakeOver()) {
+ jam();
+ ndbout_c("4 - anyActiveTakeOver == true");
+ return;
+ }//if
+ ndbout_c("5 - anyActiveTakeOver == false -> ndbsttorry10Lab");
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ }//if
+ checkStartTakeOver(signal);
+}//Dbdih::execEND_TOCONF()
+
+void Dbdih::allocateTakeOver(TakeOverRecordPtr& takeOverPtr)
+{
+ if (isMaster()) {
+ jam();
+ //--------------------------------------------
+ // Master already seized the take over record.
+ //--------------------------------------------
+ return;
+ }//if
+ if (takeOverPtr.i == cfirstfreeTakeOver) {
+ jam();
+ seizeTakeOver(takeOverPtr);
+ } else {
+ TakeOverRecordPtr nextTakeOverptr;
+ TakeOverRecordPtr prevTakeOverptr;
+ nextTakeOverptr.i = takeOverPtr.p->nextTakeOver;
+ prevTakeOverptr.i = takeOverPtr.p->prevTakeOver;
+ if (prevTakeOverptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(prevTakeOverptr, MAX_NDB_NODES, takeOverRecord);
+ prevTakeOverptr.p->nextTakeOver = nextTakeOverptr.i;
+ }//if
+ if (nextTakeOverptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(nextTakeOverptr, MAX_NDB_NODES, takeOverRecord);
+ nextTakeOverptr.p->prevTakeOver = prevTakeOverptr.i;
+ }//if
+ }//if
+}//Dbdih::allocateTakeOver()
+
+void Dbdih::seizeTakeOver(TakeOverRecordPtr& takeOverPtr)
+{
+ TakeOverRecordPtr nextTakeOverptr;
+ ndbrequire(cfirstfreeTakeOver != RNIL);
+ takeOverPtr.i = cfirstfreeTakeOver;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ cfirstfreeTakeOver = takeOverPtr.p->nextTakeOver;
+ nextTakeOverptr.i = takeOverPtr.p->nextTakeOver;
+ if (nextTakeOverptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(nextTakeOverptr, MAX_NDB_NODES, takeOverRecord);
+ nextTakeOverptr.p->prevTakeOver = RNIL;
+ }//if
+ takeOverPtr.p->nextTakeOver = RNIL;
+ takeOverPtr.p->prevTakeOver = RNIL;
+}//Dbdih::seizeTakeOver()
+
+void Dbdih::endTakeOver(Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = takeOverPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ releaseTakeOver(takeOverPtrI);
+ if ((takeOverPtr.p->toMasterStatus != TakeOverRecord::IDLE) &&
+ (takeOverPtr.p->toMasterStatus != TakeOverRecord::TO_WAIT_START_TAKE_OVER)) {
+ jam();
+ NodeGroupRecordPtr NGPtr;
+ NodeRecordPtr nodePtr;
+ nodePtr.i = takeOverPtr.p->toStartingNode;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ NGPtr.i = nodePtr.p->nodeGroup;
+ ptrCheckGuard(NGPtr, MAX_NDB_NODES, nodeGroupRecord);
+ NGPtr.p->activeTakeOver = false;
+ }//if
+ setAllowNodeStart(takeOverPtr.p->toStartingNode, true);
+ initTakeOver(takeOverPtr);
+}//Dbdih::endTakeOver()
+
+void Dbdih::releaseTakeOver(Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = takeOverPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ takeOverPtr.p->nextTakeOver = cfirstfreeTakeOver;
+ cfirstfreeTakeOver = takeOverPtr.i;
+}//Dbdih::releaseTakeOver()
+
+void Dbdih::initTakeOver(TakeOverRecordPtr takeOverPtr)
+{
+ takeOverPtr.p->toCopyNode = RNIL;
+ takeOverPtr.p->toCurrentFragid = RNIL;
+ takeOverPtr.p->toCurrentReplica = RNIL;
+ takeOverPtr.p->toCurrentTabref = RNIL;
+ takeOverPtr.p->toFailedNode = RNIL;
+ takeOverPtr.p->toStartingNode = RNIL;
+ takeOverPtr.p->prevTakeOver = RNIL;
+ takeOverPtr.p->nextTakeOver = RNIL;
+ takeOverPtr.p->toNodeRestart = false;
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::IDLE;
+ takeOverPtr.p->toSlaveStatus = TakeOverRecord::TO_SLAVE_IDLE;
+}//Dbdih::initTakeOver()
+
+bool Dbdih::anyActiveTakeOver()
+{
+ TakeOverRecordPtr takeOverPtr;
+ for (takeOverPtr.i = 0; takeOverPtr.i < MAX_NDB_NODES; takeOverPtr.i++) {
+ ptrAss(takeOverPtr, takeOverRecord);
+ if (takeOverPtr.p->toMasterStatus != TakeOverRecord::IDLE) {
+ jam();
+ return true;
+ }//if
+ }//for
+ return false;
+}//Dbdih::anyActiveTakeOver()
+
+/*****************************************************************************/
+/* ------------------------------------------------------------------------- */
+/* WE HAVE BEEN REQUESTED TO PERFORM A SYSTEM RESTART. WE START BY */
+/* READING THE GCI FILES. THIS REQUEST WILL ONLY BE SENT TO THE MASTER */
+/* DIH. THAT MEANS WE HAVE TO REPLICATE THE INFORMATION WE READ FROM */
+/* OUR FILES TO ENSURE THAT ALL NODES HAVE THE SAME DISTRIBUTION */
+/* INFORMATION. */
+/* ------------------------------------------------------------------------- */
+/*****************************************************************************/
+void Dbdih::readGciFileLab(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ filePtr.i = crestartInfoFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ filePtr.p->reqStatus = FileRecord::OPENING_GCP;
+
+ openFileRo(signal, filePtr);
+}//Dbdih::readGciFileLab()
+
+void Dbdih::openingGcpLab(Signal* signal, FileRecordPtr filePtr)
+{
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE SUCCESSFULLY OPENED A FILE CONTAINING INFORMATION ABOUT */
+ /* THE GLOBAL CHECKPOINTS THAT ARE POSSIBLE TO RESTART. */
+ /* ----------------------------------------------------------------------- */
+ readRestorableGci(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::READING_GCP;
+}//Dbdih::openingGcpLab()
+
+void Dbdih::readingGcpLab(Signal* signal, FileRecordPtr filePtr)
+{
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE NOW SUCCESSFULLY MANAGED TO READ IN THE GLOBAL CHECKPOINT */
+ /* INFORMATION FROM FILE. LATER WE WILL ADD SOME FUNCTIONALITY THAT */
+ /* CHECKS THE RESTART TIMERS TO DEDUCE FROM WHERE TO RESTART. */
+ /* NOW WE WILL SIMPLY RESTART FROM THE NEWEST GLOBAL CHECKPOINT */
+ /* POSSIBLE TO RESTORE. */
+ /* */
+ /* BEFORE WE INVOKE DICT WE NEED TO COPY CRESTART_INFO TO ALL NODES. */
+ /* WE ALSO COPY TO OUR OWN NODE. TO ENABLE US TO DO THIS PROPERLY WE */
+ /* START BY CLOSING THIS FILE. */
+ /* ----------------------------------------------------------------------- */
+ closeFile(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::CLOSING_GCP;
+}//Dbdih::readingGcpLab()
+
+void Dbdih::closingGcpLab(Signal* signal, FileRecordPtr filePtr)
+{
+ if (Sysfile::getInitialStartOngoing(SYSFILE->systemRestartBits) == false){
+ jam();
+ selectMasterCandidateAndSend(signal);
+ return;
+ } else {
+ jam();
+ sendSignal(cntrlblockref, GSN_DIH_RESTARTREF, signal, 1, JBB);
+ return;
+ }//if
+}//Dbdih::closingGcpLab()
+
+/* ------------------------------------------------------------------------- */
+/* SELECT THE MASTER CANDIDATE TO BE USED IN SYSTEM RESTARTS. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::selectMasterCandidateAndSend(Signal* signal)
+{
+ Uint32 gci = 0;
+ Uint32 masterCandidateId = 0;
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (SYSFILE->lastCompletedGCI[nodePtr.i] > gci) {
+ jam();
+ masterCandidateId = nodePtr.i;
+ gci = SYSFILE->lastCompletedGCI[nodePtr.i];
+ }//if
+ }//for
+ ndbrequire(masterCandidateId != 0);
+ setNodeGroups();
+ signal->theData[0] = masterCandidateId;
+ signal->theData[1] = gci;
+ sendSignal(cntrlblockref, GSN_DIH_RESTARTCONF, signal, 2, JBB);
+
+ Uint32 node_groups[MAX_NDB_NODES];
+ memset(node_groups, 0, sizeof(node_groups));
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ const Uint32 ng = Sysfile::getNodeGroup(nodePtr.i, SYSFILE->nodeGroups);
+ if(ng != NO_NODE_GROUP_ID){
+ ndbrequire(ng < MAX_NDB_NODES);
+ node_groups[ng]++;
+ }
+ }
+
+ for (nodePtr.i = 0; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ Uint32 count = node_groups[nodePtr.i];
+ if(count != 0 && count != cnoReplicas){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf),
+ "Illegal configuration change."
+ " Initial start needs to be performed "
+ " when changing no of replicas (%d != %d)",
+ node_groups[nodePtr.i], cnoReplicas);
+ progError(__LINE__,
+ ERR_INVALID_CONFIG,
+ buf);
+ }
+ }
+}//Dbdih::selectMasterCandidate()
+
+/* ------------------------------------------------------------------------- */
+/* ERROR HANDLING DURING READING RESTORABLE GCI FROM FILE. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::openingGcpErrorLab(Signal* signal, FileRecordPtr filePtr)
+{
+ filePtr.p->fileStatus = FileRecord::CRASHED;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ if (crestartInfoFile[0] == filePtr.i) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* THE FIRST FILE WAS NOT ABLE TO BE OPENED. SET STATUS TO CRASHED AND */
+ /* TRY OPEN THE NEXT FILE. */
+ /* --------------------------------------------------------------------- */
+ filePtr.i = crestartInfoFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ openFileRo(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_GCP;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* WE FAILED IN OPENING THE SECOND FILE. BOTH FILES WERE CORRUPTED. WE */
+ /* CANNOT CONTINUE THE RESTART IN THIS CASE. TELL NDBCNTR OF OUR */
+ /* FAILURE. */
+ /*---------------------------------------------------------------------- */
+ sendSignal(cntrlblockref, GSN_DIH_RESTARTREF, signal, 1, JBB);
+ return;
+ }//if
+}//Dbdih::openingGcpErrorLab()
+
+void Dbdih::readingGcpErrorLab(Signal* signal, FileRecordPtr filePtr)
+{
+ filePtr.p->fileStatus = FileRecord::CRASHED;
+ /* ----------------------------------------------------------------------- */
+ /* WE FAILED IN READING THE FILE AS WELL. WE WILL CLOSE THIS FILE. */
+ /* ----------------------------------------------------------------------- */
+ closeFile(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::CLOSING_GCP_CRASH;
+}//Dbdih::readingGcpErrorLab()
+
+void Dbdih::closingGcpCrashLab(Signal* signal, FileRecordPtr filePtr)
+{
+ if (crestartInfoFile[0] == filePtr.i) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* ERROR IN FIRST FILE, TRY THE SECOND FILE. */
+ /* --------------------------------------------------------------------- */
+ filePtr.i = crestartInfoFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_GCP;
+ return;
+ }//if
+ /* ----------------------------------------------------------------------- */
+ /* WE DISCOVERED A FAILURE WITH THE SECOND FILE AS WELL. THIS IS A */
+ /* SERIOUS PROBLEM. REPORT FAILURE TO NDBCNTR. */
+ /* ----------------------------------------------------------------------- */
+ sendSignal(cntrlblockref, GSN_DIH_RESTARTREF, signal, 1, JBB);
+}//Dbdih::closingGcpCrashLab()
+
+/*****************************************************************************/
+/* ------------------------------------------------------------------------- */
+/* THIS IS AN INITIAL RESTART. WE WILL CREATE THE TWO FILES DESCRIBING */
+/* THE GLOBAL CHECKPOINTS THAT ARE RESTORABLE. */
+/* ------------------------------------------------------------------------- */
+/*****************************************************************************/
+void Dbdih::initGciFilesLab(Signal* signal)
+{
+ FileRecordPtr filePtr;
+ filePtr.i = crestartInfoFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ createFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::CREATING_GCP;
+}//Dbdih::initGciFilesLab()
+
+/* ------------------------------------------------------------------------- */
+/* GLOBAL CHECKPOINT FILE HAVE BEEN SUCCESSFULLY CREATED. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::creatingGcpLab(Signal* signal, FileRecordPtr filePtr)
+{
+ if (filePtr.i == crestartInfoFile[0]) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* IF CREATED FIRST THEN ALSO CREATE THE SECOND FILE. */
+ /* --------------------------------------------------------------------- */
+ filePtr.i = crestartInfoFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ createFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::CREATING_GCP;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* BOTH FILES HAVE BEEN CREATED. NOW WRITE THE INITIAL DATA TO BOTH */
+ /* OF THE FILES. */
+ /* --------------------------------------------------------------------- */
+ filePtr.i = crestartInfoFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ writeRestorableGci(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::WRITE_INIT_GCP;
+ }//if
+}//Dbdih::creatingGcpLab()
+
+/* ------------------------------------------------------------------------- */
+/* WE HAVE SUCCESSFULLY WRITTEN A GCI FILE. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::writeInitGcpLab(Signal* signal, FileRecordPtr filePtr)
+{
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ if (filePtr.i == crestartInfoFile[0]) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* WE HAVE WRITTEN THE FIRST FILE NOW ALSO WRITE THE SECOND FILE. */
+ /* --------------------------------------------------------------------- */
+ filePtr.i = crestartInfoFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ writeRestorableGci(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::WRITE_INIT_GCP;
+ } else {
+ /* --------------------------------------------------------------------- */
+ /* WE HAVE WRITTEN BOTH FILES. LEAVE BOTH FILES OPEN AND CONFIRM OUR */
+ /* PART OF THE INITIAL START. */
+ /* --------------------------------------------------------------------- */
+ if (isMaster()) {
+ jam();
+ /*---------------------------------------------------------------------*/
+ // IN MASTER NODES THE START REQUEST IS RECEIVED FROM NDBCNTR AND WE MUST
+ // RESPOND WHEN COMPLETED.
+ /*---------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ sendSignal(cndbStartReqBlockref, GSN_NDB_STARTCONF, signal, 1, JBB);
+ } else {
+ jam();
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ }//if
+ }//if
+}//Dbdih::writeInitGcpLab()
+
+/*****************************************************************************/
+/* ********** NODES DELETION MODULE *************/
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/* LOGIC FOR NODE FAILURE */
+/*---------------------------------------------------------------------------*/
+void Dbdih::execNODE_FAILREP(Signal* signal)
+{
+ Uint32 i;
+ Uint32 failedNodes[MAX_NDB_NODES];
+ jamEntry();
+ NodeFailRep * const nodeFail = (NodeFailRep *)&signal->theData[0];
+
+ cfailurenr = nodeFail->failNo;
+ Uint32 newMasterId = nodeFail->masterNodeId;
+ const Uint32 noOfFailedNodes = nodeFail->noOfNodes;
+
+ /*-------------------------------------------------------------------------*/
+ // The first step is to convert from a bit mask to an array of failed nodes.
+ /*-------------------------------------------------------------------------*/
+ Uint32 index = 0;
+ for (i = 1; i < MAX_NDB_NODES; i++) {
+ jam();
+ if(NodeBitmask::get(nodeFail->theNodes, i)){
+ jam();
+ failedNodes[index] = i;
+ index++;
+ }//if
+ }//for
+ ndbrequire(noOfFailedNodes == index);
+ ndbrequire(noOfFailedNodes - 1 < MAX_NDB_NODES);
+
+ /*-------------------------------------------------------------------------*/
+ // The second step is to update the node status of the failed nodes, remove
+ // them from the alive node list and put them into the dead node list. Also
+ // update the number of nodes on-line.
+ // We also set certain state variables ensuring that the node no longer is
+ // used in transactions and also mark that we received this signal.
+ /*-------------------------------------------------------------------------*/
+ for (i = 0; i < noOfFailedNodes; i++) {
+ jam();
+ NodeRecordPtr TNodePtr;
+ TNodePtr.i = failedNodes[i];
+ ptrCheckGuard(TNodePtr, MAX_NDB_NODES, nodeRecord);
+ TNodePtr.p->useInTransactions = false;
+ TNodePtr.p->m_inclDihLcp = false;
+ TNodePtr.p->recNODE_FAILREP = ZTRUE;
+ if (TNodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ con_lineNodes--;
+ TNodePtr.p->nodeStatus = NodeRecord::DIED_NOW;
+ removeAlive(TNodePtr);
+ insertDeadNode(TNodePtr);
+ }//if
+ }//for
+
+ /*-------------------------------------------------------------------------*/
+ // Verify that we can continue to operate the cluster. If we cannot we will
+ // not return from checkEscalation.
+ /*-------------------------------------------------------------------------*/
+ checkEscalation();
+
+ /*------------------------------------------------------------------------*/
+ // Verify that a starting node has also crashed. Reset the node start record.
+ /*-------------------------------------------------------------------------*/
+ if (c_nodeStartMaster.startNode != RNIL) {
+ ndbrequire(getNodeStatus(c_nodeStartMaster.startNode)!= NodeRecord::ALIVE);
+ }//if
+
+ /*--------------------------------------------------*/
+ /* */
+ /* WE CHANGE THE REFERENCE TO MASTER DIH */
+ /* BLOCK AND POINTER AT THIS PLACE IN THE CODE*/
+ /*--------------------------------------------------*/
+ Uint32 oldMasterId = cmasterNodeId;
+ BlockReference oldMasterRef = cmasterdihref;
+ cmasterdihref = calcDihBlockRef(newMasterId);
+ cmasterNodeId = newMasterId;
+
+ const bool masterTakeOver = (oldMasterId != newMasterId);
+
+ for(i = 0; i < noOfFailedNodes; i++) {
+ NodeRecordPtr failedNodePtr;
+ failedNodePtr.i = failedNodes[i];
+ ptrCheckGuard(failedNodePtr, MAX_NDB_NODES, nodeRecord);
+ Uint32 activeTakeOverPtr = findTakeOver(failedNodes[i]);
+ if (oldMasterRef == reference()) {
+ /*-------------------------------------------------------*/
+ // Functions that need to be called only for master nodes.
+ /*-------------------------------------------------------*/
+ checkCopyTab(failedNodePtr);
+ checkStopPermMaster(signal, failedNodePtr);
+ checkWaitGCPMaster(signal, failedNodes[i]);
+ checkTakeOverInMasterAllNodeFailure(signal, failedNodePtr);
+ checkTakeOverInMasterCopyNodeFailure(signal, failedNodePtr.i);
+ checkTakeOverInMasterStartNodeFailure(signal, activeTakeOverPtr);
+ checkGcpOutstanding(signal, failedNodePtr.i);
+ } else {
+ jam();
+ /*-----------------------------------------------------------*/
+ // Functions that need to be called only for nodes that were
+ // not master before these failures.
+ /*-----------------------------------------------------------*/
+ checkStopPermProxy(signal, failedNodes[i]);
+ checkWaitGCPProxy(signal, failedNodes[i]);
+ if (isMaster()) {
+ /*-----------------------------------------------------------*/
+ // We take over as master since old master has failed
+ /*-----------------------------------------------------------*/
+ handleTakeOverNewMaster(signal, activeTakeOverPtr);
+ } else {
+ /*-----------------------------------------------------------*/
+ // We are not master and will not become master.
+ /*-----------------------------------------------------------*/
+ checkTakeOverInNonMasterStartNodeFailure(signal, activeTakeOverPtr);
+ }//if
+ }//if
+ /*--------------------------------------------------*/
+ // Functions that need to be called for all nodes.
+ /*--------------------------------------------------*/
+ checkStopMe(signal, failedNodePtr);
+ failedNodeLcpHandling(signal, failedNodePtr);
+ checkWaitDropTabFailedLqh(signal, failedNodePtr.i, 0); // 0 = start w/ tab 0
+ startRemoveFailedNode(signal, failedNodePtr);
+
+ /**
+ * This is the last function called
+ * It modifies failedNodePtr.p->nodeStatus
+ */
+ failedNodeSynchHandling(signal, failedNodePtr);
+ }//for
+
+ if(masterTakeOver){
+ jam();
+ startLcpMasterTakeOver(signal, oldMasterId);
+ startGcpMasterTakeOver(signal, oldMasterId);
+
+ if(getNodeState().getNodeRestartInProgress()){
+ jam();
+ progError(__LINE__,
+ ERR_SYSTEM_ERROR,
+ "Unhandle master failure during node restart");
+ }
+ }
+
+
+ if (isMaster()) {
+ jam();
+ setNodeRestartInfoBits();
+ }//if
+}//Dbdih::execNODE_FAILREP()
+
+void Dbdih::checkCopyTab(NodeRecordPtr failedNodePtr)
+{
+ jam();
+
+ if(c_nodeStartMaster.startNode != failedNodePtr.i){
+ jam();
+ return;
+ }
+
+ switch(c_nodeStartMaster.m_outstandingGsn){
+ case GSN_COPY_TABREQ:
+ jam();
+ ndbrequire(c_COPY_TABREQ_Counter.isWaitingFor(failedNodePtr.i));
+ releaseTabPages(failedNodePtr.p->activeTabptr);
+ c_COPY_TABREQ_Counter.clearWaitingFor(failedNodePtr.i);
+ c_nodeStartMaster.wait = ZFALSE;
+ break;
+ case GSN_START_INFOREQ:
+ case GSN_START_PERMCONF:
+ case GSN_DICTSTARTREQ:
+ case GSN_START_MECONF:
+ jam();
+ break;
+ default:
+ ndbout_c("outstanding gsn: %s(%d)",
+ getSignalName(c_nodeStartMaster.m_outstandingGsn),
+ c_nodeStartMaster.m_outstandingGsn);
+ ndbrequire(false);
+ }
+
+ nodeResetStart();
+}//Dbdih::checkCopyTab()
+
+void Dbdih::checkStopMe(Signal* signal, NodeRecordPtr failedNodePtr)
+{
+ jam();
+ if (c_STOP_ME_REQ_Counter.isWaitingFor(failedNodePtr.i)){
+ jam();
+ ndbrequire(c_stopMe.clientRef != 0);
+ StopMeConf * const stopMeConf = (StopMeConf *)&signal->theData[0];
+ stopMeConf->senderRef = calcDihBlockRef(failedNodePtr.i);
+ stopMeConf->senderData = c_stopMe.clientData;
+ sendSignal(reference(), GSN_STOP_ME_CONF, signal,
+ StopMeConf::SignalLength, JBB);
+ }//if
+}//Dbdih::checkStopMe()
+
+void Dbdih::checkStopPermMaster(Signal* signal, NodeRecordPtr failedNodePtr)
+{
+ DihSwitchReplicaRef* const ref = (DihSwitchReplicaRef*)&signal->theData[0];
+ jam();
+ if (c_DIH_SWITCH_REPLICA_REQ_Counter.isWaitingFor(failedNodePtr.i)){
+ jam();
+ ndbrequire(c_stopPermMaster.clientRef != 0);
+ ref->senderNode = failedNodePtr.i;
+ ref->errorCode = StopPermRef::NF_CausedAbortOfStopProcedure;
+ sendSignal(reference(), GSN_DIH_SWITCH_REPLICA_REF, signal,
+ DihSwitchReplicaRef::SignalLength, JBB);
+ return;
+ }//if
+}//Dbdih::checkStopPermMaster()
+
+void Dbdih::checkStopPermProxy(Signal* signal, NodeId failedNodeId)
+{
+ jam();
+ if(c_stopPermProxy.clientRef != 0 &&
+ refToNode(c_stopPermProxy.masterRef) == failedNodeId){
+
+ /**
+ * The master has failed report to proxy-client
+ */
+ jam();
+ StopPermRef* const ref = (StopPermRef*)&signal->theData[0];
+
+ ref->senderData = c_stopPermProxy.clientData;
+ ref->errorCode = StopPermRef::NF_CausedAbortOfStopProcedure;
+ sendSignal(c_stopPermProxy.clientRef, GSN_STOP_PERM_REF, signal, 2, JBB);
+ c_stopPermProxy.clientRef = 0;
+ }//if
+}//Dbdih::checkStopPermProxy()
+
+void
+Dbdih::checkTakeOverInMasterAllNodeFailure(Signal* signal,
+ NodeRecordPtr failedNodePtr)
+{
+ //------------------------------------------------------------------------
+ // This code is used to handle the failure of "all" nodes during the
+ // take over when "all" nodes are informed about state changes in
+ // the take over protocol.
+ //--------------------------------------------------------------------------
+ if (c_START_TOREQ_Counter.isWaitingFor(failedNodePtr.i)){
+ jam();
+ StartToConf * const conf = (StartToConf *)&signal->theData[0];
+ conf->userPtr = c_startToLock;
+ conf->sendingNodeId = failedNodePtr.i;
+ conf->startingNodeId = getStartNode(c_startToLock);
+ sendSignal(reference(), GSN_START_TOCONF, signal,
+ StartToConf::SignalLength, JBB);
+ }//if
+ if (c_CREATE_FRAGREQ_Counter.isWaitingFor(failedNodePtr.i)){
+ jam();
+ CreateFragConf * const conf = (CreateFragConf *)&signal->theData[0];
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = c_createFragmentLock;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ conf->userPtr = takeOverPtr.i;
+ conf->tableId = takeOverPtr.p->toCurrentTabref;
+ conf->fragId = takeOverPtr.p->toCurrentFragid;
+ conf->sendingNodeId = failedNodePtr.i;
+ conf->startingNodeId = takeOverPtr.p->toStartingNode;
+ sendSignal(reference(), GSN_CREATE_FRAGCONF, signal,
+ CreateFragConf::SignalLength, JBB);
+ }//if
+ if (c_UPDATE_TOREQ_Counter.isWaitingFor(failedNodePtr.i)){
+ jam();
+ UpdateToConf * const conf = (UpdateToConf *)&signal->theData[0];
+ conf->userPtr = c_updateToLock;
+ conf->sendingNodeId = failedNodePtr.i;
+ conf->startingNodeId = getStartNode(c_updateToLock);
+ sendSignal(reference(), GSN_UPDATE_TOCONF, signal,
+ UpdateToConf::SignalLength, JBB);
+ }//if
+
+ if (c_END_TOREQ_Counter.isWaitingFor(failedNodePtr.i)){
+ jam();
+ EndToConf * const conf = (EndToConf *)&signal->theData[0];
+ conf->userPtr = c_endToLock;
+ conf->sendingNodeId = failedNodePtr.i;
+ conf->startingNodeId = getStartNode(c_endToLock);
+ sendSignal(reference(), GSN_END_TOCONF, signal,
+ EndToConf::SignalLength, JBB);
+ }//if
+}//Dbdih::checkTakeOverInMasterAllNodeFailure()
+
+void Dbdih::checkTakeOverInMasterCopyNodeFailure(Signal* signal,
+ Uint32 failedNodeId)
+{
+ //---------------------------------------------------------------------------
+ // This code is used to handle failure of the copying node during a take over
+ //---------------------------------------------------------------------------
+ TakeOverRecordPtr takeOverPtr;
+ for (Uint32 i = 0; i < MAX_NDB_NODES; i++) {
+ jam();
+ takeOverPtr.i = i;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ if ((takeOverPtr.p->toMasterStatus == TakeOverRecord::COPY_FRAG) &&
+ (takeOverPtr.p->toCopyNode == failedNodeId)) {
+ jam();
+ /**
+ * The copying node failed but the system is still operational.
+ * We restart the copy process by selecting a new copy node.
+ * We do not need to add a fragment however since it is already added.
+ * We start again from the prepare create fragment phase.
+ */
+ prepareSendCreateFragReq(signal, takeOverPtr.i);
+ }//if
+ }//for
+}//Dbdih::checkTakeOverInMasterCopyNodeFailure()
+
+void Dbdih::checkTakeOverInMasterStartNodeFailure(Signal* signal,
+ Uint32 takeOverPtrI)
+{
+ jam();
+ if (takeOverPtrI == RNIL) {
+ jam();
+ return;
+ }
+ //-----------------------------------------------------------------------
+ // We are the master and the starting node has failed during a take over.
+ // We need to handle this failure in different ways depending on the state.
+ //-----------------------------------------------------------------------
+
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = takeOverPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+
+ bool ok = false;
+ switch (takeOverPtr.p->toMasterStatus) {
+ case TakeOverRecord::IDLE:
+ //-----------------------------------------------------------------------
+ // The state cannot be idle when it has a starting node.
+ //-----------------------------------------------------------------------
+ ndbrequire(false);
+ break;
+ case TakeOverRecord::TO_WAIT_START_TAKE_OVER:
+ jam();
+ case TakeOverRecord::TO_START_COPY:
+ jam();
+ case TakeOverRecord::TO_START_COPY_ONGOING:
+ jam();
+ case TakeOverRecord::TO_WAIT_START:
+ jam();
+ case TakeOverRecord::TO_WAIT_PREPARE_CREATE:
+ jam();
+ case TakeOverRecord::TO_WAIT_UPDATE_TO:
+ jam();
+ case TakeOverRecord::TO_WAIT_COMMIT_CREATE:
+ jam();
+ case TakeOverRecord::TO_END_COPY:
+ jam();
+ case TakeOverRecord::TO_END_COPY_ONGOING:
+ jam();
+ case TakeOverRecord::TO_WAIT_ENDING:
+ jam();
+ //-----------------------------------------------------------------------
+ // We will not do anything since an internal signal process is outstanding.
+ // When the signal arrives the take over will be released.
+ //-----------------------------------------------------------------------
+ ok = true;
+ break;
+ case TakeOverRecord::STARTING:
+ jam();
+ ok = true;
+ c_startToLock = RNIL;
+ c_START_TOREQ_Counter.clearWaitingFor();
+ endTakeOver(takeOverPtr.i);
+ break;
+ case TakeOverRecord::TO_UPDATE_TO:
+ jam();
+ ok = true;
+ c_updateToLock = RNIL;
+ c_UPDATE_TOREQ_Counter.clearWaitingFor();
+ endTakeOver(takeOverPtr.i);
+ break;
+ case TakeOverRecord::ENDING:
+ jam();
+ ok = true;
+ c_endToLock = RNIL;
+ c_END_TOREQ_Counter.clearWaitingFor();
+ endTakeOver(takeOverPtr.i);
+ break;
+ case TakeOverRecord::COMMIT_CREATE:
+ ok = true;
+ jam();
+ {// We have mutex
+ Mutex m(signal, c_mutexMgr, takeOverPtr.p->m_switchPrimaryMutexHandle);
+ m.unlock(); // Ignore result
+ }
+ // Fall through
+ case TakeOverRecord::PREPARE_CREATE:
+ ok = true;
+ jam();
+ c_createFragmentLock = RNIL;
+ c_CREATE_FRAGREQ_Counter.clearWaitingFor();
+ endTakeOver(takeOverPtr.i);
+ break;
+ case TakeOverRecord::LOCK_MUTEX:
+ ok = true;
+ jam();
+ // Lock mutex will return and do endTakeOver
+ break;
+
+ //-----------------------------------------------------------------------
+ // Signals are outstanding to external nodes. These signals carry the node
+ // id of the starting node and will not use the take over record if the
+ // starting node has failed.
+ //-----------------------------------------------------------------------
+ case TakeOverRecord::COPY_FRAG:
+ ok = true;
+ jam();
+ //-----------------------------------------------------------------------
+ // The starting node will discover the problem. We will receive either
+ // COPY_FRAGREQ or COPY_FRAGCONF and then we can release the take over
+ // record and end the process. If the copying node should also die then
+ // we will try to send prepare create fragment and will then discover
+ // that the starting node has failed.
+ //-----------------------------------------------------------------------
+ break;
+ case TakeOverRecord::COPY_ACTIVE:
+ ok = true;
+ jam();
+ //-----------------------------------------------------------------------
+ // In this we are waiting for a signal from the starting node. Thus we
+ // can release the take over record and end the process.
+ //-----------------------------------------------------------------------
+ endTakeOver(takeOverPtr.i);
+ break;
+ case TakeOverRecord::WAIT_LCP:
+ ok = true;
+ jam();
+ //-----------------------------------------------------------------------
+ //-----------------------------------------------------------------------
+ endTakeOver(takeOverPtr.i);
+ break;
+ /**
+ * The following are states that it should not be possible to "be" in
+ */
+ case TakeOverRecord::SELECTING_NEXT:
+ jam();
+ case TakeOverRecord::TO_COPY_COMPLETED:
+ jam();
+ ndbrequire(false);
+ }
+ if(!ok){
+ jamLine(takeOverPtr.p->toSlaveStatus);
+ ndbrequire(ok);
+ }
+}//Dbdih::checkTakeOverInMasterStartNodeFailure()
+
+void Dbdih::checkTakeOverInNonMasterStartNodeFailure(Signal* signal,
+ Uint32 takeOverPtrI)
+{
+ jam();
+ if (takeOverPtrI == RNIL) {
+ jam();
+ return;
+ }
+ //-----------------------------------------------------------------------
+ // We are not master and not taking over as master. A take over was ongoing
+ // but the starting node has now failed. Handle it according to the state
+ // of the take over.
+ //-----------------------------------------------------------------------
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = takeOverPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ bool ok = false;
+ switch (takeOverPtr.p->toSlaveStatus) {
+ case TakeOverRecord::TO_SLAVE_IDLE:
+ ndbrequire(false);
+ break;
+ case TakeOverRecord::TO_SLAVE_STARTED:
+ jam();
+ case TakeOverRecord::TO_SLAVE_CREATE_PREPARE:
+ jam();
+ case TakeOverRecord::TO_SLAVE_COPY_FRAG_COMPLETED:
+ jam();
+ case TakeOverRecord::TO_SLAVE_CREATE_COMMIT:
+ jam();
+ case TakeOverRecord::TO_SLAVE_COPY_COMPLETED:
+ jam();
+ ok = true;
+ endTakeOver(takeOverPtr.i);
+ break;
+ }//switch
+ if(!ok){
+ jamLine(takeOverPtr.p->toSlaveStatus);
+ ndbrequire(ok);
+ }
+}//Dbdih::checkTakeOverInNonMasterStartNodeFailure()
+
+void Dbdih::failedNodeSynchHandling(Signal* signal,
+ NodeRecordPtr failedNodePtr)
+{
+ jam();
+ /*----------------------------------------------------*/
+ /* INITIALISE THE VARIABLES THAT KEEP TRACK OF */
+ /* WHEN A NODE FAILURE IS COMPLETED. */
+ /*----------------------------------------------------*/
+ failedNodePtr.p->dbdictFailCompleted = ZFALSE;
+ failedNodePtr.p->dbtcFailCompleted = ZFALSE;
+ failedNodePtr.p->dbdihFailCompleted = ZFALSE;
+ failedNodePtr.p->dblqhFailCompleted = ZFALSE;
+
+ failedNodePtr.p->m_NF_COMPLETE_REP.clearWaitingFor();
+
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ /**
+ * We'r waiting for nodePtr.i to complete
+ * handling of failedNodePtr.i's death
+ */
+
+ failedNodePtr.p->m_NF_COMPLETE_REP.setWaitingFor(nodePtr.i);
+ } else {
+ jam();
+ if ((nodePtr.p->nodeStatus == NodeRecord::DYING) &&
+ (nodePtr.p->m_NF_COMPLETE_REP.isWaitingFor(failedNodePtr.i))){
+ jam();
+ /*----------------------------------------------------*/
+ /* THE NODE FAILED BEFORE REPORTING THE FAILURE */
+ /* HANDLING COMPLETED ON THIS FAILED NODE. */
+ /* REPORT THAT NODE FAILURE HANDLING WAS */
+ /* COMPLETED ON THE NEW FAILED NODE FOR THIS */
+ /* PARTICULAR OLD FAILED NODE. */
+ /*----------------------------------------------------*/
+ NFCompleteRep * const nf = (NFCompleteRep *)&signal->theData[0];
+ nf->blockNo = 0;
+ nf->nodeId = failedNodePtr.i;
+ nf->failedNodeId = nodePtr.i;
+ nf->from = __LINE__;
+ sendSignal(reference(), GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+ }//if
+ }//if
+ }//for
+ if (failedNodePtr.p->nodeStatus == NodeRecord::DIED_NOW) {
+ jam();
+ failedNodePtr.p->nodeStatus = NodeRecord::DYING;
+ } else {
+ jam();
+ /*----------------------------------------------------*/
+ // No more processing needed when node not even started
+ // yet. We give the node status to DEAD since we do not
+ // care whether all nodes complete the node failure
+ // handling. The node have not been included in the
+ // node failure protocols.
+ /*----------------------------------------------------*/
+ failedNodePtr.p->nodeStatus = NodeRecord::DEAD;
+ /**-----------------------------------------------------------------------
+ * WE HAVE COMPLETED HANDLING THE NODE FAILURE IN DIH. WE CAN REPORT THIS
+ * TO DIH THAT WAIT FOR THE OTHER BLOCKS TO BE CONCLUDED AS WELL.
+ *-----------------------------------------------------------------------*/
+ NFCompleteRep * const nf = (NFCompleteRep *)&signal->theData[0];
+ nf->blockNo = DBDIH;
+ nf->nodeId = cownNodeId;
+ nf->failedNodeId = failedNodePtr.i;
+ nf->from = __LINE__;
+ sendSignal(reference(), GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+ }//if
+}//Dbdih::failedNodeSynchHandling()
+
+Uint32 Dbdih::findTakeOver(Uint32 failedNodeId)
+{
+ for (Uint32 i = 0; i < MAX_NDB_NODES; i++) {
+ jam();
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = i;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ if (takeOverPtr.p->toStartingNode == failedNodeId) {
+ jam();
+ return i;
+ }//if
+ }//for
+ return RNIL;
+}//Dbdih::findTakeOver()
+
+Uint32 Dbdih::getStartNode(Uint32 takeOverPtrI)
+{
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = takeOverPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ return takeOverPtr.p->toStartingNode;
+}//Dbdih::getStartNode()
+
+void Dbdih::failedNodeLcpHandling(Signal* signal, NodeRecordPtr failedNodePtr)
+{
+ jam();
+ const Uint32 nodeId = failedNodePtr.i;
+
+ if (c_lcpState.m_participatingLQH.get(failedNodePtr.i)){
+ /*----------------------------------------------------*/
+ /* THE NODE WAS INVOLVED IN A LOCAL CHECKPOINT. WE */
+ /* MUST UPDATE THE ACTIVE STATUS TO INDICATE THAT */
+ /* THE NODE HAVE MISSED A LOCAL CHECKPOINT. */
+ /*----------------------------------------------------*/
+ switch (failedNodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ jam();
+ failedNodePtr.p->activeStatus = Sysfile::NS_ActiveMissed_1;
+ break;
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ failedNodePtr.p->activeStatus = Sysfile::NS_ActiveMissed_2;
+ break;
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ failedNodePtr.p->activeStatus = Sysfile::NS_NotActive_NotTakenOver;
+ break;
+ case Sysfile::NS_TakeOver:
+ jam();
+ failedNodePtr.p->activeStatus = Sysfile::NS_NotActive_NotTakenOver;
+ break;
+ default:
+ ndbout << "activeStatus = " << (Uint32) failedNodePtr.p->activeStatus;
+ ndbout << " at failure after NODE_FAILREP of node = ";
+ ndbout << failedNodePtr.i << endl;
+ ndbrequire(false);
+ break;
+ }//switch
+ }//if
+
+ c_lcpState.m_participatingDIH.clear(failedNodePtr.i);
+ c_lcpState.m_participatingLQH.clear(failedNodePtr.i);
+
+ if(c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH.isWaitingFor(failedNodePtr.i)){
+ jam();
+ LcpCompleteRep * rep = (LcpCompleteRep*)signal->getDataPtrSend();
+ rep->nodeId = failedNodePtr.i;
+ rep->lcpId = SYSFILE->latestLCP_ID;
+ rep->blockNo = DBDIH;
+ sendSignal(reference(), GSN_LCP_COMPLETE_REP, signal,
+ LcpCompleteRep::SignalLength, JBB);
+ }
+
+ /**
+ * Check if we'r waiting for the failed node's LQH to complete
+ *
+ * Note that this is ran "before" LCP master take over
+ */
+ if(c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH.isWaitingFor(nodeId)){
+ jam();
+
+ LcpCompleteRep * rep = (LcpCompleteRep*)signal->getDataPtrSend();
+ rep->nodeId = nodeId;
+ rep->lcpId = SYSFILE->latestLCP_ID;
+ rep->blockNo = DBLQH;
+ sendSignal(reference(), GSN_LCP_COMPLETE_REP, signal,
+ LcpCompleteRep::SignalLength, JBB);
+
+ if(c_lcpState.m_LAST_LCP_FRAG_ORD.isWaitingFor(nodeId)){
+ jam();
+ /**
+ * Make sure we're ready to accept it
+ */
+ c_lcpState.m_LAST_LCP_FRAG_ORD.clearWaitingFor(nodeId);
+ }
+ }
+
+ if (c_TCGETOPSIZEREQ_Counter.isWaitingFor(failedNodePtr.i)) {
+ jam();
+ signal->theData[0] = failedNodePtr.i;
+ signal->theData[1] = 0;
+ sendSignal(reference(), GSN_TCGETOPSIZECONF, signal, 2, JBB);
+ }//if
+
+ if (c_TC_CLOPSIZEREQ_Counter.isWaitingFor(failedNodePtr.i)) {
+ jam();
+ signal->theData[0] = failedNodePtr.i;
+ sendSignal(reference(), GSN_TC_CLOPSIZECONF, signal, 1, JBB);
+ }//if
+
+ if (c_START_LCP_REQ_Counter.isWaitingFor(failedNodePtr.i)) {
+ jam();
+ StartLcpConf * conf = (StartLcpConf*)signal->getDataPtrSend();
+ conf->senderRef = numberToRef(DBLQH, failedNodePtr.i);
+ conf->lcpId = SYSFILE->latestLCP_ID;
+ sendSignal(reference(), GSN_START_LCP_CONF, signal,
+ StartLcpConf::SignalLength, JBB);
+ }//if
+
+ if (c_EMPTY_LCP_REQ_Counter.isWaitingFor(failedNodePtr.i)) {
+ jam();
+ EmptyLcpConf * const rep = (EmptyLcpConf *)&signal->theData[0];
+ rep->senderNodeId = failedNodePtr.i;
+ rep->tableId = ~0;
+ rep->fragmentId = ~0;
+ rep->lcpNo = 0;
+ rep->lcpId = SYSFILE->latestLCP_ID;
+ rep->idle = true;
+ sendSignal(reference(), GSN_EMPTY_LCP_CONF, signal,
+ EmptyLcpConf::SignalLength, JBB);
+ }//if
+
+ if (c_MASTER_LCPREQ_Counter.isWaitingFor(failedNodePtr.i)) {
+ jam();
+ MasterLCPRef * const ref = (MasterLCPRef *)&signal->theData[0];
+ ref->senderNodeId = failedNodePtr.i;
+ ref->failedNodeId = cmasterTakeOverNode;
+ sendSignal(reference(), GSN_MASTER_LCPREF, signal,
+ MasterLCPRef::SignalLength, JBB);
+ }//if
+
+}//Dbdih::failedNodeLcpHandling()
+
+void Dbdih::checkGcpOutstanding(Signal* signal, Uint32 failedNodeId){
+ if (c_GCP_PREPARE_Counter.isWaitingFor(failedNodeId)){
+ jam();
+ signal->theData[0] = failedNodeId;
+ signal->theData[1] = cnewgcp;
+ sendSignal(reference(), GSN_GCP_PREPARECONF, signal, 2, JBB);
+ }//if
+
+ if (c_GCP_COMMIT_Counter.isWaitingFor(failedNodeId)) {
+ jam();
+ signal->theData[0] = failedNodeId;
+ signal->theData[1] = coldgcp;
+ signal->theData[2] = cfailurenr;
+ sendSignal(reference(), GSN_GCP_NODEFINISH, signal, 3, JBB);
+ }//if
+
+ if (c_GCP_SAVEREQ_Counter.isWaitingFor(failedNodeId)) {
+ jam();
+ GCPSaveRef * const saveRef = (GCPSaveRef*)&signal->theData[0];
+ saveRef->dihPtr = failedNodeId;
+ saveRef->nodeId = failedNodeId;
+ saveRef->gci = coldgcp;
+ saveRef->errorCode = GCPSaveRef::FakedSignalDueToNodeFailure;
+ sendSignal(reference(), GSN_GCP_SAVEREF, signal,
+ GCPSaveRef::SignalLength, JBB);
+ }//if
+
+ if (c_COPY_GCIREQ_Counter.isWaitingFor(failedNodeId)) {
+ jam();
+ signal->theData[0] = failedNodeId;
+ sendSignal(reference(), GSN_COPY_GCICONF, signal, 1, JBB);
+ }//if
+
+ if (c_MASTER_GCPREQ_Counter.isWaitingFor(failedNodeId)){
+ jam();
+ MasterGCPRef * const ref = (MasterGCPRef *)&signal->theData[0];
+ ref->senderNodeId = failedNodeId;
+ ref->failedNodeId = cmasterTakeOverNode;
+ sendSignal(reference(), GSN_MASTER_GCPREF, signal,
+ MasterGCPRef::SignalLength, JBB);
+ }//if
+}//Dbdih::handleGcpStateInMaster()
+
+
+void
+Dbdih::startLcpMasterTakeOver(Signal* signal, Uint32 nodeId){
+ jam();
+
+ c_lcpMasterTakeOverState.minTableId = ~0;
+ c_lcpMasterTakeOverState.minFragId = ~0;
+ c_lcpMasterTakeOverState.failedNodeId = nodeId;
+
+ c_lcpMasterTakeOverState.set(LMTOS_WAIT_EMPTY_LCP, __LINE__);
+
+ if(c_EMPTY_LCP_REQ_Counter.done()){
+ jam();
+ c_lcpState.m_LAST_LCP_FRAG_ORD.clearWaitingFor();
+
+ EmptyLcpReq* req = (EmptyLcpReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ sendLoopMacro(EMPTY_LCP_REQ, sendEMPTY_LCP_REQ);
+ ndbrequire(!c_EMPTY_LCP_REQ_Counter.done());
+ } else {
+ /**
+ * Node failure during master take over...
+ */
+ ndbout_c("Nodefail during master take over");
+ }
+
+ setLocalNodefailHandling(signal, nodeId, NF_LCP_TAKE_OVER);
+}
+
+void Dbdih::startGcpMasterTakeOver(Signal* signal, Uint32 oldMasterId){
+ jam();
+ /*--------------------------------------------------*/
+ /* */
+ /* THE MASTER HAVE FAILED AND WE WERE ELECTED */
+ /* TO BE THE NEW MASTER NODE. WE NEED TO QUERY*/
+ /* ALL THE OTHER NODES ABOUT THEIR STATUS IN */
+ /* ORDER TO BE ABLE TO TAKE OVER CONTROL OF */
+ /* THE GLOBAL CHECKPOINT PROTOCOL AND THE */
+ /* LOCAL CHECKPOINT PROTOCOL. */
+ /*--------------------------------------------------*/
+ if(!isMaster()){
+ jam();
+ return;
+ }
+ cmasterState = MASTER_TAKE_OVER_GCP;
+ cmasterTakeOverNode = oldMasterId;
+ MasterGCPReq * const req = (MasterGCPReq *)&signal->theData[0];
+ req->masterRef = reference();
+ req->failedNodeId = oldMasterId;
+ sendLoopMacro(MASTER_GCPREQ, sendMASTER_GCPREQ);
+ cgcpMasterTakeOverState = GMTOS_INITIAL;
+
+ signal->theData[0] = NDB_LE_GCP_TakeoverStarted;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
+
+ setLocalNodefailHandling(signal, oldMasterId, NF_GCP_TAKE_OVER);
+}//Dbdih::handleNewMaster()
+
+void Dbdih::handleTakeOverNewMaster(Signal* signal, Uint32 takeOverPtrI)
+{
+ jam();
+ if (takeOverPtrI != RNIL) {
+ jam();
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = takeOverPtrI;
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ bool ok = false;
+ switch (takeOverPtr.p->toSlaveStatus) {
+ case TakeOverRecord::TO_SLAVE_IDLE:
+ ndbrequire(false);
+ break;
+ case TakeOverRecord::TO_SLAVE_STARTED:
+ jam();
+ case TakeOverRecord::TO_SLAVE_CREATE_PREPARE:
+ jam();
+ case TakeOverRecord::TO_SLAVE_COPY_FRAG_COMPLETED:
+ jam();
+ case TakeOverRecord::TO_SLAVE_CREATE_COMMIT:
+ jam();
+ ok = true;
+ infoEvent("Unhandled MasterTO of TO slaveStatus=%d killing node %d",
+ takeOverPtr.p->toSlaveStatus,
+ takeOverPtr.p->toStartingNode);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::COPY_ACTIVE;
+
+ {
+ BlockReference cntrRef = calcNdbCntrBlockRef(takeOverPtr.p->toStartingNode);
+ SystemError * const sysErr = (SystemError*)&signal->theData[0];
+ sysErr->errorCode = SystemError::CopyFragRefError;
+ sysErr->errorRef = reference();
+ sysErr->data1= 0;
+ sysErr->data2= __LINE__;
+ sendSignal(cntrRef, GSN_SYSTEM_ERROR, signal,
+ SystemError::SignalLength, JBB);
+ }
+ break;
+ case TakeOverRecord::TO_SLAVE_COPY_COMPLETED:
+ ok = true;
+ jam();
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::WAIT_LCP;
+ break;
+ }
+ ndbrequire(ok);
+ }//if
+}//Dbdih::handleTakeOverNewMaster()
+
+void Dbdih::startRemoveFailedNode(Signal* signal, NodeRecordPtr failedNodePtr)
+{
+ Uint32 nodeId = failedNodePtr.i;
+ if(failedNodePtr.p->nodeStatus != NodeRecord::DIED_NOW){
+ jam();
+ /**
+ * Is node isn't alive. It can't be part of LCP
+ */
+ ndbrequire(!c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH.isWaitingFor(nodeId));
+
+ /**
+ * And there is no point in removing any replicas
+ * It's dead...
+ */
+ return;
+ }
+
+ jam();
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = failedNodePtr.i;
+ signal->theData[2] = 0; // Tab id
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+
+ setLocalNodefailHandling(signal, failedNodePtr.i, NF_REMOVE_NODE_FROM_TABLE);
+}//Dbdih::startRemoveFailedNode()
+
+/*--------------------------------------------------*/
+/* THE MASTER HAS FAILED AND THE NEW MASTER IS*/
+/* QUERYING THIS NODE ABOUT THE STATE OF THE */
+/* GLOBAL CHECKPOINT PROTOCOL */
+/*--------------------------------------------------*/
+void Dbdih::execMASTER_GCPREQ(Signal* signal)
+{
+ NodeRecordPtr failedNodePtr;
+ MasterGCPReq * const masterGCPReq = (MasterGCPReq *)&signal->theData[0];
+ jamEntry();
+ const BlockReference newMasterBlockref = masterGCPReq->masterRef;
+ const Uint32 failedNodeId = masterGCPReq->failedNodeId;
+ if (c_copyGCISlave.m_copyReason != CopyGCIReq::IDLE) {
+ jam();
+ /*--------------------------------------------------*/
+ /* WE ARE CURRENTLY WRITING THE RESTART INFO */
+ /* IN THIS NODE. SINCE ONLY ONE PROCESS IS */
+ /* ALLOWED TO DO THIS AT A TIME WE MUST ENSURE*/
+ /* THAT THIS IS NOT ONGOING WHEN THE NEW */
+ /* MASTER TAKES OVER CONTROL. IF NOT ALL NODES*/
+ /* RECEIVE THE SAME RESTART INFO DUE TO THE */
+ /* FAILURE OF THE MASTER IT IS TAKEN CARE OF */
+ /* BY THE NEW MASTER. */
+ /*--------------------------------------------------*/
+ sendSignalWithDelay(reference(), GSN_MASTER_GCPREQ,
+ signal, 10, MasterGCPReq::SignalLength);
+ return;
+ }//if
+ failedNodePtr.i = failedNodeId;
+ ptrCheckGuard(failedNodePtr, MAX_NDB_NODES, nodeRecord);
+ if (failedNodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ /*--------------------------------------------------*/
+ /* ENSURE THAT WE HAVE PROCESSED THE SIGNAL */
+ /* NODE_FAILURE BEFORE WE PROCESS THIS REQUEST*/
+ /* FROM THE NEW MASTER. THIS ENSURES THAT WE */
+ /* HAVE REMOVED THE FAILED NODE FROM THE LIST */
+ /* OF ACTIVE NODES AND SO FORTH. */
+ /*--------------------------------------------------*/
+ sendSignalWithDelay(reference(), GSN_MASTER_GCPREQ,
+ signal, 10, MasterGCPReq::SignalLength);
+ return;
+ } else {
+ ndbrequire(failedNodePtr.p->nodeStatus == NodeRecord::DYING);
+ }//if
+ MasterGCPConf::State gcpState;
+ switch (cgcpParticipantState) {
+ case GCP_PARTICIPANT_READY:
+ jam();
+ /*--------------------------------------------------*/
+ /* THE GLOBAL CHECKPOINT IS NOT ACTIVE SINCE */
+ /* THE PREVIOUS GLOBAL CHECKPOINT IS COMPLETED*/
+ /* AND THE NEW HAVE NOT STARTED YET. */
+ /*--------------------------------------------------*/
+ gcpState = MasterGCPConf::GCP_READY;
+ break;
+ case GCP_PARTICIPANT_PREPARE_RECEIVED:
+ jam();
+ /*--------------------------------------------------*/
+ /* GCP_PREPARE HAVE BEEN RECEIVED AND RESPONSE*/
+ /* HAVE BEEN SENT. */
+ /*--------------------------------------------------*/
+ gcpState = MasterGCPConf::GCP_PREPARE_RECEIVED;
+ break;
+ case GCP_PARTICIPANT_COMMIT_RECEIVED:
+ jam();
+ /*------------------------------------------------*/
+ /* GCP_COMMIT HAVE BEEN RECEIVED BUT NOT YET*/
+ /* GCP_TCFINISHED FROM LOCAL TC. */
+ /*------------------------------------------------*/
+ gcpState = MasterGCPConf::GCP_COMMIT_RECEIVED;
+ break;
+ case GCP_PARTICIPANT_TC_FINISHED:
+ jam();
+ /*------------------------------------------------*/
+ /* GCP_COMMIT HAS BEEN RECEIVED AND ALSO */
+ /* GCP_TCFINISHED HAVE BEEN RECEIVED. */
+ /*------------------------------------------------*/
+ gcpState = MasterGCPConf::GCP_TC_FINISHED;
+ break;
+ case GCP_PARTICIPANT_COPY_GCI_RECEIVED:
+ /*--------------------------------------------------*/
+ /* COPY RESTART INFORMATION HAS BEEN RECEIVED */
+ /* BUT NOT YET COMPLETED. */
+ /*--------------------------------------------------*/
+ ndbrequire(false);
+ gcpState= MasterGCPConf::GCP_READY; // remove warning
+ break;
+ default:
+ /*------------------------------------------------*/
+ /* */
+ /* THIS SHOULD NOT OCCUR SINCE THE ABOVE */
+ /* STATES ARE THE ONLY POSSIBLE STATES AT A */
+ /* NODE WHICH WAS NOT A MASTER NODE. */
+ /*------------------------------------------------*/
+ ndbrequire(false);
+ gcpState= MasterGCPConf::GCP_READY; // remove warning
+ break;
+ }//switch
+ MasterGCPConf * const masterGCPConf = (MasterGCPConf *)&signal->theData[0];
+ masterGCPConf->gcpState = gcpState;
+ masterGCPConf->senderNodeId = cownNodeId;
+ masterGCPConf->failedNodeId = failedNodeId;
+ masterGCPConf->newGCP = cnewgcp;
+ masterGCPConf->latestLCP = SYSFILE->latestLCP_ID;
+ masterGCPConf->oldestRestorableGCI = SYSFILE->oldestRestorableGCI;
+ masterGCPConf->keepGCI = SYSFILE->keepGCI;
+ for(Uint32 i = 0; i < NdbNodeBitmask::Size; i++)
+ masterGCPConf->lcpActive[i] = SYSFILE->lcpActive[i];
+ sendSignal(newMasterBlockref, GSN_MASTER_GCPCONF, signal,
+ MasterGCPConf::SignalLength, JBB);
+}//Dbdih::execMASTER_GCPREQ()
+
+void Dbdih::execMASTER_GCPCONF(Signal* signal)
+{
+ NodeRecordPtr senderNodePtr;
+ MasterGCPConf * const masterGCPConf = (MasterGCPConf *)&signal->theData[0];
+ jamEntry();
+ senderNodePtr.i = masterGCPConf->senderNodeId;
+ ptrCheckGuard(senderNodePtr, MAX_NDB_NODES, nodeRecord);
+
+ MasterGCPConf::State gcpState = (MasterGCPConf::State)masterGCPConf->gcpState;
+ const Uint32 failedNodeId = masterGCPConf->failedNodeId;
+ const Uint32 newGcp = masterGCPConf->newGCP;
+ const Uint32 latestLcpId = masterGCPConf->latestLCP;
+ const Uint32 oldestRestorableGci = masterGCPConf->oldestRestorableGCI;
+ const Uint32 oldestKeepGci = masterGCPConf->keepGCI;
+ if (latestLcpId > SYSFILE->latestLCP_ID) {
+ jam();
+#if 0
+ ndbout_c("Dbdih: Setting SYSFILE->latestLCP_ID to %d", latestLcpId);
+ SYSFILE->latestLCP_ID = latestLcpId;
+#endif
+ SYSFILE->keepGCI = oldestKeepGci;
+ SYSFILE->oldestRestorableGCI = oldestRestorableGci;
+ for(Uint32 i = 0; i < NdbNodeBitmask::Size; i++)
+ SYSFILE->lcpActive[i] = masterGCPConf->lcpActive[i];
+ }//if
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ jam();
+ senderNodePtr.p->gcpstate = NodeRecord::READY;
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ senderNodePtr.p->gcpstate = NodeRecord::PREPARE_RECEIVED;
+ cnewgcp = newGcp;
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ jam();
+ senderNodePtr.p->gcpstate = NodeRecord::COMMIT_SENT;
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ senderNodePtr.p->gcpstate = NodeRecord::NODE_FINISHED;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ switch (cgcpMasterTakeOverState) {
+ case GMTOS_INITIAL:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ jam();
+ cgcpMasterTakeOverState = ALL_READY;
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ cgcpMasterTakeOverState = ALL_PREPARED;
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ jam();
+ cgcpMasterTakeOverState = COMMIT_STARTED_NOT_COMPLETED;
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ cgcpMasterTakeOverState = COMMIT_COMPLETED;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ case ALL_READY:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ jam();
+ /*empty*/;
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ cgcpMasterTakeOverState = PREPARE_STARTED_NOT_COMMITTED;
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ ndbrequire(false);
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ cgcpMasterTakeOverState = SAVE_STARTED_NOT_COMPLETED;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ case PREPARE_STARTED_NOT_COMMITTED:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ jam();
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ ndbrequire(false);
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ ndbrequire(false);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ case ALL_PREPARED:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ jam();
+ cgcpMasterTakeOverState = PREPARE_STARTED_NOT_COMMITTED;
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ jam();
+ cgcpMasterTakeOverState = COMMIT_STARTED_NOT_COMPLETED;
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ cgcpMasterTakeOverState = COMMIT_STARTED_NOT_COMPLETED;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ case COMMIT_STARTED_NOT_COMPLETED:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ ndbrequire(false);
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ jam();
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ case COMMIT_COMPLETED:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ cgcpMasterTakeOverState = SAVE_STARTED_NOT_COMPLETED;
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ jam();
+ cgcpMasterTakeOverState = COMMIT_STARTED_NOT_COMPLETED;
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ jam();
+ cgcpMasterTakeOverState = COMMIT_STARTED_NOT_COMPLETED;
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ case SAVE_STARTED_NOT_COMPLETED:
+ switch (gcpState) {
+ case MasterGCPConf::GCP_READY:
+ jam();
+ break;
+ case MasterGCPConf::GCP_PREPARE_RECEIVED:
+ ndbrequire(false);
+ break;
+ case MasterGCPConf::GCP_COMMIT_RECEIVED:
+ ndbrequire(false);
+ break;
+ case MasterGCPConf::GCP_TC_FINISHED:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ receiveLoopMacro(MASTER_GCPREQ, senderNodePtr.i);
+ /*-------------------------------------------------------------------------*/
+ // We have now received all responses and are ready to take over the GCP
+ // protocol as master.
+ /*-------------------------------------------------------------------------*/
+ MASTER_GCPhandling(signal, failedNodeId);
+ return;
+}//Dbdih::execMASTER_GCPCONF()
+
+void Dbdih::execMASTER_GCPREF(Signal* signal)
+{
+ const MasterGCPRef * const ref = (MasterGCPRef *)&signal->theData[0];
+ jamEntry();
+ receiveLoopMacro(MASTER_GCPREQ, ref->senderNodeId);
+ /*-------------------------------------------------------------------------*/
+ // We have now received all responses and are ready to take over the GCP
+ // protocol as master.
+ /*-------------------------------------------------------------------------*/
+ MASTER_GCPhandling(signal, ref->failedNodeId);
+}//Dbdih::execMASTER_GCPREF()
+
+void Dbdih::MASTER_GCPhandling(Signal* signal, Uint32 failedNodeId)
+{
+ NodeRecordPtr failedNodePtr;
+ cmasterState = MASTER_ACTIVE;
+ /*----------------------------------------------------------*/
+ /* REMOVE ALL ACTIVE STATUS ON ALREADY FAILED NODES */
+ /* THIS IS PERFORMED HERE SINCE WE GET THE LCP ACTIVE */
+ /* STATUS AS PART OF THE COPY RESTART INFO AND THIS IS*/
+ /* HANDLED BY THE MASTER GCP TAKE OVER PROTOCOL. */
+ /*----------------------------------------------------------*/
+
+ failedNodePtr.i = failedNodeId;
+ ptrCheckGuard(failedNodePtr, MAX_NDB_NODES, nodeRecord);
+ switch (cgcpMasterTakeOverState) {
+ case ALL_READY:
+ jam();
+ startGcp(signal);
+ break;
+ case PREPARE_STARTED_NOT_COMMITTED:
+ {
+ NodeRecordPtr nodePtr;
+ jam();
+ c_GCP_PREPARE_Counter.clearWaitingFor();
+ nodePtr.i = cfirstAliveNode;
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->gcpstate == NodeRecord::READY) {
+ jam();
+ c_GCP_PREPARE_Counter.setWaitingFor(nodePtr.i);
+ sendGCP_PREPARE(signal, nodePtr.i);
+ }//if
+ nodePtr.i = nodePtr.p->nextNode;
+ } while(nodePtr.i != RNIL);
+ if (c_GCP_PREPARE_Counter.done()) {
+ jam();
+ gcpcommitreqLab(signal);
+ }//if
+ break;
+ }
+ case ALL_PREPARED:
+ jam();
+ gcpcommitreqLab(signal);
+ break;
+ case COMMIT_STARTED_NOT_COMPLETED:
+ {
+ NodeRecordPtr nodePtr;
+ jam();
+ c_GCP_COMMIT_Counter.clearWaitingFor();
+ nodePtr.i = cfirstAliveNode;
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->gcpstate == NodeRecord::PREPARE_RECEIVED) {
+ jam();
+ sendGCP_COMMIT(signal, nodePtr.i);
+ c_GCP_COMMIT_Counter.setWaitingFor(nodePtr.i);
+ } else {
+ ndbrequire((nodePtr.p->gcpstate == NodeRecord::NODE_FINISHED) ||
+ (nodePtr.p->gcpstate == NodeRecord::COMMIT_SENT));
+ }//if
+ nodePtr.i = nodePtr.p->nextNode;
+ } while(nodePtr.i != RNIL);
+ if (c_GCP_COMMIT_Counter.done()){
+ jam();
+ gcpsavereqLab(signal);
+ }//if
+ break;
+ }
+ case COMMIT_COMPLETED:
+ jam();
+ gcpsavereqLab(signal);
+ break;
+ case SAVE_STARTED_NOT_COMPLETED:
+ {
+ NodeRecordPtr nodePtr;
+ jam();
+ SYSFILE->newestRestorableGCI = coldgcp;
+ nodePtr.i = cfirstAliveNode;
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ SYSFILE->lastCompletedGCI[nodePtr.i] = coldgcp;
+ nodePtr.i = nodePtr.p->nextNode;
+ } while (nodePtr.i != RNIL);
+ /**-------------------------------------------------------------------
+ * THE FAILED NODE DID ALSO PARTICIPATE IN THIS GLOBAL CHECKPOINT
+ * WHICH IS RECORDED.
+ *-------------------------------------------------------------------*/
+ SYSFILE->lastCompletedGCI[failedNodeId] = coldgcp;
+ copyGciLab(signal, CopyGCIReq::GLOBAL_CHECKPOINT);
+ break;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+
+ signal->theData[0] = NDB_LE_GCP_TakeoverCompleted;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
+
+ /*--------------------------------------------------*/
+ /* WE SEPARATE HANDLING OF GLOBAL CHECKPOINTS */
+ /* AND LOCAL CHECKPOINTS HERE. LCP'S HAVE TO */
+ /* REMOVE ALL FAILED FRAGMENTS BEFORE WE CAN */
+ /* HANDLE THE LCP PROTOCOL. */
+ /*--------------------------------------------------*/
+ checkLocalNodefailComplete(signal, failedNodeId, NF_GCP_TAKE_OVER);
+
+ return;
+}//Dbdih::masterGcpConfFromFailedLab()
+
+void
+Dbdih::invalidateNodeLCP(Signal* signal, Uint32 nodeId, Uint32 tableId)
+{
+ jamEntry();
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ const Uint32 RT_BREAK = 64;
+ if (ERROR_INSERTED(7125)) {
+ return;
+ }//if
+ for (Uint32 i = 0; i<RT_BREAK; i++) {
+ jam();
+ if (tabPtr.i >= ctabFileSize){
+ jam();
+ /**
+ * Ready with entire loop
+ * Return to master
+ */
+ setAllowNodeStart(nodeId, true);
+ if (getNodeStatus(nodeId) == NodeRecord::STARTING) {
+ jam();
+ StartInfoConf * conf = (StartInfoConf*)&signal->theData[0];
+ conf->sendingNodeId = cownNodeId;
+ conf->startingNodeId = nodeId;
+ sendSignal(cmasterdihref, GSN_START_INFOCONF, signal,
+ StartInfoConf::SignalLength, JBB);
+ }//if
+ return;
+ }//if
+ ptrAss(tabPtr, tabRecord);
+ if (tabPtr.p->tabStatus == TabRecord::TS_ACTIVE) {
+ jam();
+ invalidateNodeLCP(signal, nodeId, tabPtr);
+ return;
+ }//if
+ tabPtr.i++;
+ }//for
+ signal->theData[0] = DihContinueB::ZINVALIDATE_NODE_LCP;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+}//Dbdih::invalidateNodeLCP()
+
+void
+Dbdih::invalidateNodeLCP(Signal* signal, Uint32 nodeId, TabRecordPtr tabPtr)
+{
+ /**
+ * Check so that no one else is using the tab descriptior
+ */
+ if (tabPtr.p->tabCopyStatus != TabRecord::CS_IDLE) {
+ jam();
+ signal->theData[0] = DihContinueB::ZINVALIDATE_NODE_LCP;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 20, 3);
+ return;
+ }//if
+
+ /**
+ * For each fragment
+ */
+ bool modified = false;
+ FragmentstorePtr fragPtr;
+ for(Uint32 fragNo = 0; fragNo < tabPtr.p->totalfragments; fragNo++){
+ jam();
+ getFragstore(tabPtr.p, fragNo, fragPtr);
+ /**
+ * For each of replica record
+ */
+ ReplicaRecordPtr replicaPtr;
+ for(replicaPtr.i = fragPtr.p->oldStoredReplicas; replicaPtr.i != RNIL;
+ replicaPtr.i = replicaPtr.p->nextReplica) {
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if(replicaPtr.p->procNode == nodeId){
+ jam();
+ /**
+ * Found one with correct node id
+ */
+ /**
+ * Invalidate all LCP's
+ */
+ modified = true;
+ for(int i = 0; i < MAX_LCP_STORED; i++) {
+ replicaPtr.p->lcpStatus[i] = ZINVALID;
+ }//if
+ /**
+ * And reset nextLcp
+ */
+ replicaPtr.p->nextLcp = 0;
+ }//if
+ }//for
+ }//for
+
+ if (modified) {
+ jam();
+ /**
+ * Save table description to disk
+ */
+ tabPtr.p->tabCopyStatus = TabRecord::CS_INVALIDATE_NODE_LCP;
+ tabPtr.p->tabUpdateState = TabRecord::US_INVALIDATE_NODE_LCP;
+ tabPtr.p->tabRemoveNode = nodeId;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }
+
+ jam();
+ /**
+ * Move to next table
+ */
+ tabPtr.i++;
+ signal->theData[0] = DihContinueB::ZINVALIDATE_NODE_LCP;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+}//Dbdih::invalidateNodeLCP()
+
+/*------------------------------------------------*/
+/* INPUT: TABPTR */
+/* TNODEID */
+/*------------------------------------------------*/
+void Dbdih::removeNodeFromTables(Signal* signal,
+ Uint32 nodeId, Uint32 tableId)
+{
+ jamEntry();
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ const Uint32 RT_BREAK = 64;
+ for (Uint32 i = 0; i<RT_BREAK; i++) {
+ jam();
+ if (tabPtr.i >= ctabFileSize){
+ jam();
+ removeNodeFromTablesComplete(signal, nodeId);
+ return;
+ }//if
+
+ ptrAss(tabPtr, tabRecord);
+ if (tabPtr.p->tabStatus == TabRecord::TS_ACTIVE) {
+ jam();
+ removeNodeFromTable(signal, nodeId, tabPtr);
+ return;
+ }//if
+ tabPtr.i++;
+ }//for
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+}
+
+void Dbdih::removeNodeFromTable(Signal* signal,
+ Uint32 nodeId, TabRecordPtr tabPtr){
+
+ /**
+ * Check so that no one else is using the tab descriptior
+ */
+ if (tabPtr.p->tabCopyStatus != TabRecord::CS_IDLE) {
+ jam();
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 20, 3);
+ return;
+ }//if
+
+ /**
+ * For each fragment
+ */
+ Uint32 noOfRemovedReplicas = 0; // No of replicas removed
+ Uint32 noOfRemovedLcpReplicas = 0; // No of replicas in LCP removed
+ Uint32 noOfRemainingLcpReplicas = 0;// No of replicas in LCP remaining
+
+ //const Uint32 lcpId = SYSFILE->latestLCP_ID;
+ const bool lcpOngoingFlag = (tabPtr.p->tabLcpStatus== TabRecord::TLS_ACTIVE);
+
+ FragmentstorePtr fragPtr;
+ for(Uint32 fragNo = 0; fragNo < tabPtr.p->totalfragments; fragNo++){
+ jam();
+ getFragstore(tabPtr.p, fragNo, fragPtr);
+
+ /**
+ * For each of replica record
+ */
+ Uint32 replicaNo = 0;
+ ReplicaRecordPtr replicaPtr;
+ for(replicaPtr.i = fragPtr.p->storedReplicas; replicaPtr.i != RNIL;
+ replicaPtr.i = replicaPtr.p->nextReplica, replicaNo++) {
+ jam();
+
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if(replicaPtr.p->procNode == nodeId){
+ jam();
+ noOfRemovedReplicas++;
+ removeNodeFromStored(nodeId, fragPtr, replicaPtr);
+ if(replicaPtr.p->lcpOngoingFlag){
+ jam();
+ /**
+ * This replica is currently LCP:ed
+ */
+ ndbrequire(fragPtr.p->noLcpReplicas > 0);
+ fragPtr.p->noLcpReplicas --;
+
+ noOfRemovedLcpReplicas ++;
+ replicaPtr.p->lcpOngoingFlag = false;
+ }
+ }
+ }
+ noOfRemainingLcpReplicas += fragPtr.p->noLcpReplicas;
+ }
+
+ if(noOfRemovedReplicas == 0){
+ jam();
+ /**
+ * The table had no replica on the failed node
+ * continue with next table
+ */
+ tabPtr.i++;
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }
+
+ /**
+ * We did remove at least one replica
+ */
+ bool ok = false;
+ switch(tabPtr.p->tabLcpStatus){
+ case TabRecord::TLS_COMPLETED:
+ ok = true;
+ jam();
+ /**
+ * WE WILL WRITE THE TABLE DESCRIPTION TO DISK AT THIS TIME
+ * INDEPENDENT OF WHAT THE LOCAL CHECKPOINT NEEDED.
+ * THIS IS TO ENSURE THAT THE FAILED NODES ARE ALSO UPDATED ON DISK
+ * IN THE DIH DATA STRUCTURES BEFORE WE COMPLETE HANDLING OF THE
+ * NODE FAILURE.
+ */
+ ndbrequire(noOfRemovedLcpReplicas == 0);
+
+ tabPtr.p->tabCopyStatus = TabRecord::CS_REMOVE_NODE;
+ tabPtr.p->tabUpdateState = TabRecord::US_REMOVE_NODE;
+ tabPtr.p->tabRemoveNode = nodeId;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::TLS_ACTIVE:
+ ok = true;
+ jam();
+ /**
+ * The table is participating in an LCP currently
+ */
+ // Fall through
+ break;
+ case TabRecord::TLS_WRITING_TO_FILE:
+ ok = true;
+ jam();
+ /**
+ * This should never happen since we in the beginning of this function
+ * checks the tabCopyStatus
+ */
+ ndbrequire(lcpOngoingFlag);
+ ndbrequire(false);
+ break;
+ }
+ ndbrequire(ok);
+
+ /**
+ * The table is participating in an LCP currently
+ * and we removed some replicas that should have been checkpointed
+ */
+ ndbrequire(c_lcpState.lcpStatus != LCP_STATUS_IDLE);
+ ndbrequire(tabPtr.p->tabLcpStatus == TabRecord::TLS_ACTIVE);
+
+ /**
+ * Save the table
+ */
+ tabPtr.p->tabCopyStatus = TabRecord::CS_REMOVE_NODE;
+ tabPtr.p->tabUpdateState = TabRecord::US_REMOVE_NODE;
+ tabPtr.p->tabRemoveNode = nodeId;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+
+ if(noOfRemainingLcpReplicas == 0){
+ jam();
+ /**
+ * The removal on the failed node made the LCP complete
+ */
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_WRITING_TO_FILE;
+ checkLcpAllTablesDoneInLqh();
+ }
+}
+
+void
+Dbdih::removeNodeFromTablesComplete(Signal* signal, Uint32 nodeId){
+ jam();
+
+ /**
+ * Check if we "accidently" completed a LCP
+ */
+ checkLcpCompletedLab(signal);
+
+ /**
+ * Check if we (DIH) are finished with node fail handling
+ */
+ checkLocalNodefailComplete(signal, nodeId, NF_REMOVE_NODE_FROM_TABLE);
+}
+
+void
+Dbdih::checkLocalNodefailComplete(Signal* signal, Uint32 failedNodeId,
+ NodefailHandlingStep step){
+ jam();
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = failedNodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ ndbrequire(nodePtr.p->m_nodefailSteps.get(step));
+ nodePtr.p->m_nodefailSteps.clear(step);
+
+ if(nodePtr.p->m_nodefailSteps.count() > 0){
+ jam();
+ return;
+ }
+
+ NFCompleteRep * const nf = (NFCompleteRep *)&signal->theData[0];
+ nf->blockNo = DBDIH;
+ nf->nodeId = cownNodeId;
+ nf->failedNodeId = failedNodeId;
+ nf->from = __LINE__;
+ sendSignal(reference(), GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+}
+
+
+void
+Dbdih::setLocalNodefailHandling(Signal* signal, Uint32 failedNodeId,
+ NodefailHandlingStep step){
+ jam();
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = failedNodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ ndbrequire(!nodePtr.p->m_nodefailSteps.get(step));
+ nodePtr.p->m_nodefailSteps.set(step);
+}
+
+void Dbdih::startLcpTakeOverLab(Signal* signal, Uint32 failedNodeId)
+{
+ /*--------------------------------------------------------------------*/
+ // Start LCP master take over process. Consists of the following steps.
+ // 1) Ensure that all LQH's have reported all fragments they have been
+ // told to checkpoint. Can be a fairly long step time-wise.
+ // 2) Query all nodes about their LCP status.
+ // During the query process we do not want our own state to change.
+ // This can change due to delayed reception of LCP_REPORT, completed
+ // save of table on disk or reception of DIH_LCPCOMPLETE from other
+ // node.
+ /*--------------------------------------------------------------------*/
+}//Dbdih::startLcpTakeOver()
+
+void Dbdih::execEMPTY_LCP_CONF(Signal* signal)
+{
+ jamEntry();
+
+ ndbrequire(c_lcpMasterTakeOverState.state == LMTOS_WAIT_EMPTY_LCP);
+
+ const EmptyLcpConf * const conf = (EmptyLcpConf *)&signal->theData[0];
+ Uint32 nodeId = conf->senderNodeId;
+
+ if(!conf->idle){
+ jam();
+ if (conf->tableId < c_lcpMasterTakeOverState.minTableId) {
+ jam();
+ c_lcpMasterTakeOverState.minTableId = conf->tableId;
+ c_lcpMasterTakeOverState.minFragId = conf->fragmentId;
+ } else if (conf->tableId == c_lcpMasterTakeOverState.minTableId &&
+ conf->fragmentId < c_lcpMasterTakeOverState.minFragId) {
+ jam();
+ c_lcpMasterTakeOverState.minFragId = conf->fragmentId;
+ }//if
+ if(isMaster()){
+ jam();
+ c_lcpState.m_LAST_LCP_FRAG_ORD.setWaitingFor(nodeId);
+ }
+ }
+
+ receiveLoopMacro(EMPTY_LCP_REQ, nodeId);
+ /*--------------------------------------------------------------------*/
+ // Received all EMPTY_LCPCONF. We can continue with next phase of the
+ // take over LCP master process.
+ /*--------------------------------------------------------------------*/
+ c_lcpMasterTakeOverState.set(LMTOS_WAIT_LCP_FRAG_REP, __LINE__);
+ checkEmptyLcpComplete(signal);
+ return;
+}//Dbdih::execEMPTY_LCPCONF()
+
+void
+Dbdih::checkEmptyLcpComplete(Signal *signal){
+
+ ndbrequire(c_lcpMasterTakeOverState.state == LMTOS_WAIT_LCP_FRAG_REP);
+
+ if(c_lcpState.noOfLcpFragRepOutstanding > 0){
+ jam();
+ return;
+ }
+
+ if(isMaster()){
+ jam();
+
+ signal->theData[0] = NDB_LE_LCP_TakeoverStarted;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
+
+ signal->theData[0] = 7012;
+ execDUMP_STATE_ORD(signal);
+
+ c_lcpMasterTakeOverState.set(LMTOS_INITIAL, __LINE__);
+ MasterLCPReq * const req = (MasterLCPReq *)&signal->theData[0];
+ req->masterRef = reference();
+ req->failedNodeId = c_lcpMasterTakeOverState.failedNodeId;
+ sendLoopMacro(MASTER_LCPREQ, sendMASTER_LCPREQ);
+ } else {
+ sendMASTER_LCPCONF(signal);
+ }
+}
+
+/*--------------------------------------------------*/
+/* THE MASTER HAS FAILED AND THE NEW MASTER IS*/
+/* QUERYING THIS NODE ABOUT THE STATE OF THE */
+/* LOCAL CHECKPOINT PROTOCOL. */
+/*--------------------------------------------------*/
+void Dbdih::execMASTER_LCPREQ(Signal* signal)
+{
+ const MasterLCPReq * const req = (MasterLCPReq *)&signal->theData[0];
+ jamEntry();
+ const BlockReference newMasterBlockref = req->masterRef;
+
+ Uint32 failedNodeId = req->failedNodeId;
+
+ /**
+ * There can be no take over with the same master
+ */
+ ndbrequire(c_lcpState.m_masterLcpDihRef != newMasterBlockref);
+ c_lcpState.m_masterLcpDihRef = newMasterBlockref;
+ c_lcpState.m_MASTER_LCPREQ_Received = true;
+ c_lcpState.m_MASTER_LCPREQ_FailedNodeId = failedNodeId;
+
+ if(newMasterBlockref != cmasterdihref){
+ jam();
+ ndbrequire(0);
+ }
+
+ sendMASTER_LCPCONF(signal);
+}//Dbdih::execMASTER_LCPREQ()
+
+void
+Dbdih::sendMASTER_LCPCONF(Signal * signal){
+
+ if(!c_EMPTY_LCP_REQ_Counter.done()){
+ /**
+ * Have not received all EMPTY_LCP_REP
+ * dare not answer MASTER_LCP_CONF yet
+ */
+ jam();
+ return;
+ }
+
+ if(!c_lcpState.m_MASTER_LCPREQ_Received){
+ jam();
+ /**
+ * Has not received MASTER_LCPREQ yet
+ */
+ return;
+ }
+
+ if(c_lcpState.lcpStatus == LCP_INIT_TABLES){
+ jam();
+ /**
+ * Still aborting old initLcpLab
+ */
+ return;
+ }
+
+ if(c_lcpState.lcpStatus == LCP_COPY_GCI){
+ jam();
+ /**
+ * Restart it
+ */
+ //Uint32 lcpId = SYSFILE->latestLCP_ID;
+ SYSFILE->latestLCP_ID--;
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+#if 0
+ if(c_copyGCISlave.m_copyReason == CopyGCIReq::LOCAL_CHECKPOINT){
+ ndbout_c("Dbdih: Also resetting c_copyGCISlave");
+ c_copyGCISlave.m_copyReason = CopyGCIReq::IDLE;
+ c_copyGCISlave.m_expectedNextWord = 0;
+ }
+#endif
+ }
+
+ bool ok = false;
+ MasterLCPConf::State lcpState;
+ switch (c_lcpState.lcpStatus) {
+ case LCP_STATUS_IDLE:
+ ok = true;
+ jam();
+ /*------------------------------------------------*/
+ /* LOCAL CHECKPOINT IS CURRENTLY NOT ACTIVE */
+ /* SINCE NO COPY OF RESTART INFORMATION HAVE*/
+ /* BEEN RECEIVED YET. ALSO THE PREVIOUS */
+ /* CHECKPOINT HAVE BEEN FULLY COMPLETED. */
+ /*------------------------------------------------*/
+ lcpState = MasterLCPConf::LCP_STATUS_IDLE;
+ break;
+ case LCP_STATUS_ACTIVE:
+ ok = true;
+ jam();
+ /*--------------------------------------------------*/
+ /* COPY OF RESTART INFORMATION HAS BEEN */
+ /* PERFORMED AND ALSO RESPONSE HAVE BEEN SENT.*/
+ /*--------------------------------------------------*/
+ lcpState = MasterLCPConf::LCP_STATUS_ACTIVE;
+ break;
+ case LCP_TAB_COMPLETED:
+ ok = true;
+ jam();
+ /*--------------------------------------------------------*/
+ /* ALL LCP_REPORT'S HAVE BEEN COMPLETED FOR */
+ /* ALL TABLES. SAVE OF AT LEAST ONE TABLE IS */
+ /* ONGOING YET. */
+ /*--------------------------------------------------------*/
+ lcpState = MasterLCPConf::LCP_TAB_COMPLETED;
+ break;
+ case LCP_TAB_SAVED:
+ ok = true;
+ jam();
+ /*--------------------------------------------------------*/
+ /* ALL LCP_REPORT'S HAVE BEEN COMPLETED FOR */
+ /* ALL TABLES. ALL TABLES HAVE ALSO BEEN SAVED */
+ /* ALL OTHER NODES ARE NOT YET FINISHED WITH */
+ /* THE LOCAL CHECKPOINT. */
+ /*--------------------------------------------------------*/
+ lcpState = MasterLCPConf::LCP_TAB_SAVED;
+ break;
+ case LCP_TCGET:
+ case LCP_CALCULATE_KEEP_GCI:
+ case LCP_TC_CLOPSIZE:
+ case LCP_START_LCP_ROUND:
+ /**
+ * These should only exists on the master
+ * but since this is master take over
+ * it not allowed
+ */
+ ndbrequire(false);
+ lcpState= MasterLCPConf::LCP_STATUS_IDLE; // remove warning
+ break;
+ case LCP_COPY_GCI:
+ case LCP_INIT_TABLES:
+ ok = true;
+ /**
+ * These two states are handled by if statements above
+ */
+ ndbrequire(false);
+ lcpState= MasterLCPConf::LCP_STATUS_IDLE; // remove warning
+ break;
+ }//switch
+ ndbrequire(ok);
+
+ Uint32 failedNodeId = c_lcpState.m_MASTER_LCPREQ_FailedNodeId;
+ MasterLCPConf * const conf = (MasterLCPConf *)&signal->theData[0];
+ conf->senderNodeId = cownNodeId;
+ conf->lcpState = lcpState;
+ conf->failedNodeId = failedNodeId;
+ sendSignal(c_lcpState.m_masterLcpDihRef, GSN_MASTER_LCPCONF,
+ signal, MasterLCPConf::SignalLength, JBB);
+
+ // Answer to MASTER_LCPREQ sent, reset flag so
+ // that it's not sent again before another request comes in
+ c_lcpState.m_MASTER_LCPREQ_Received = false;
+
+ if(c_lcpState.lcpStatus == LCP_TAB_SAVED){
+#ifdef VM_TRACE
+ ndbout_c("Sending extra GSN_LCP_COMPLETE_REP to new master");
+#endif
+ sendLCP_COMPLETE_REP(signal);
+ }
+
+ if(!isMaster()){
+ c_lcpMasterTakeOverState.set(LMTOS_IDLE, __LINE__);
+ checkLocalNodefailComplete(signal, failedNodeId, NF_LCP_TAKE_OVER);
+ }
+
+ return;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbdih::LcpMasterTakeOverState state){
+ switch(state){
+ case Dbdih::LMTOS_IDLE:
+ out << "LMTOS_IDLE";
+ break;
+ case Dbdih::LMTOS_WAIT_EMPTY_LCP:
+ out << "LMTOS_WAIT_EMPTY_LCP";
+ break;
+ case Dbdih::LMTOS_WAIT_LCP_FRAG_REP:
+ out << "LMTOS_WAIT_EMPTY_LCP";
+ break;
+ case Dbdih::LMTOS_INITIAL:
+ out << "LMTOS_INITIAL";
+ break;
+ case Dbdih::LMTOS_ALL_IDLE:
+ out << "LMTOS_ALL_IDLE";
+ break;
+ case Dbdih::LMTOS_ALL_ACTIVE:
+ out << "LMTOS_ALL_ACTIVE";
+ break;
+ case Dbdih::LMTOS_LCP_CONCLUDING:
+ out << "LMTOS_LCP_CONCLUDING";
+ break;
+ case Dbdih::LMTOS_COPY_ONGOING:
+ out << "LMTOS_COPY_ONGOING";
+ break;
+ }
+ return out;
+}
+
+struct MASTERLCP_StateTransitions {
+ Dbdih::LcpMasterTakeOverState CurrentState;
+ MasterLCPConf::State ParticipantState;
+ Dbdih::LcpMasterTakeOverState NewState;
+};
+
+static const
+MASTERLCP_StateTransitions g_masterLCPTakeoverStateTransitions[] = {
+ /**
+ * Current = LMTOS_INITIAL
+ */
+ { Dbdih::LMTOS_INITIAL,
+ MasterLCPConf::LCP_STATUS_IDLE,
+ Dbdih::LMTOS_ALL_IDLE },
+
+ { Dbdih::LMTOS_INITIAL,
+ MasterLCPConf::LCP_STATUS_ACTIVE,
+ Dbdih::LMTOS_ALL_ACTIVE },
+
+ { Dbdih::LMTOS_INITIAL,
+ MasterLCPConf::LCP_TAB_COMPLETED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ { Dbdih::LMTOS_INITIAL,
+ MasterLCPConf::LCP_TAB_SAVED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ /**
+ * Current = LMTOS_ALL_IDLE
+ */
+ { Dbdih::LMTOS_ALL_IDLE,
+ MasterLCPConf::LCP_STATUS_IDLE,
+ Dbdih::LMTOS_ALL_IDLE },
+
+ { Dbdih::LMTOS_ALL_IDLE,
+ MasterLCPConf::LCP_STATUS_ACTIVE,
+ Dbdih::LMTOS_COPY_ONGOING },
+
+ { Dbdih::LMTOS_ALL_IDLE,
+ MasterLCPConf::LCP_TAB_COMPLETED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ { Dbdih::LMTOS_ALL_IDLE,
+ MasterLCPConf::LCP_TAB_SAVED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ /**
+ * Current = LMTOS_COPY_ONGOING
+ */
+ { Dbdih::LMTOS_COPY_ONGOING,
+ MasterLCPConf::LCP_STATUS_IDLE,
+ Dbdih::LMTOS_COPY_ONGOING },
+
+ { Dbdih::LMTOS_COPY_ONGOING,
+ MasterLCPConf::LCP_STATUS_ACTIVE,
+ Dbdih::LMTOS_COPY_ONGOING },
+
+ /**
+ * Current = LMTOS_ALL_ACTIVE
+ */
+ { Dbdih::LMTOS_ALL_ACTIVE,
+ MasterLCPConf::LCP_STATUS_IDLE,
+ Dbdih::LMTOS_COPY_ONGOING },
+
+ { Dbdih::LMTOS_ALL_ACTIVE,
+ MasterLCPConf::LCP_STATUS_ACTIVE,
+ Dbdih::LMTOS_ALL_ACTIVE },
+
+ { Dbdih::LMTOS_ALL_ACTIVE,
+ MasterLCPConf::LCP_TAB_COMPLETED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ { Dbdih::LMTOS_ALL_ACTIVE,
+ MasterLCPConf::LCP_TAB_SAVED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ /**
+ * Current = LMTOS_LCP_CONCLUDING
+ */
+ { Dbdih::LMTOS_LCP_CONCLUDING,
+ MasterLCPConf::LCP_STATUS_IDLE,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ { Dbdih::LMTOS_LCP_CONCLUDING,
+ MasterLCPConf::LCP_STATUS_ACTIVE,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ { Dbdih::LMTOS_LCP_CONCLUDING,
+ MasterLCPConf::LCP_TAB_COMPLETED,
+ Dbdih::LMTOS_LCP_CONCLUDING },
+
+ { Dbdih::LMTOS_LCP_CONCLUDING,
+ MasterLCPConf::LCP_TAB_SAVED,
+ Dbdih::LMTOS_LCP_CONCLUDING }
+};
+
+const Uint32 g_masterLCPTakeoverStateTransitionsRows =
+sizeof(g_masterLCPTakeoverStateTransitions) / sizeof(struct MASTERLCP_StateTransitions);
+
+void Dbdih::execMASTER_LCPCONF(Signal* signal)
+{
+ const MasterLCPConf * const conf = (MasterLCPConf *)&signal->theData[0];
+ jamEntry();
+ Uint32 senderNodeId = conf->senderNodeId;
+ MasterLCPConf::State lcpState = (MasterLCPConf::State)conf->lcpState;
+ const Uint32 failedNodeId = conf->failedNodeId;
+ NodeRecordPtr nodePtr;
+ nodePtr.i = senderNodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->lcpStateAtTakeOver = lcpState;
+
+#ifdef VM_TRACE
+ ndbout_c("MASTER_LCPCONF");
+ printMASTER_LCP_CONF(stdout, &signal->theData[0], 0, 0);
+#endif
+
+ bool found = false;
+ for(Uint32 i = 0; i<g_masterLCPTakeoverStateTransitionsRows; i++){
+ const struct MASTERLCP_StateTransitions * valid =
+ &g_masterLCPTakeoverStateTransitions[i];
+
+ if(valid->CurrentState == c_lcpMasterTakeOverState.state &&
+ valid->ParticipantState == lcpState){
+ jam();
+ found = true;
+ c_lcpMasterTakeOverState.set(valid->NewState, __LINE__);
+ break;
+ }
+ }
+ ndbrequire(found);
+
+ bool ok = false;
+ switch(lcpState){
+ case MasterLCPConf::LCP_STATUS_IDLE:
+ ok = true;
+ break;
+ case MasterLCPConf::LCP_STATUS_ACTIVE:
+ case MasterLCPConf::LCP_TAB_COMPLETED:
+ case MasterLCPConf::LCP_TAB_SAVED:
+ ok = true;
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH.setWaitingFor(nodePtr.i);
+ break;
+ }
+ ndbrequire(ok);
+
+ receiveLoopMacro(MASTER_LCPREQ, senderNodeId);
+ /*-------------------------------------------------------------------------*/
+ // We have now received all responses and are ready to take over the LCP
+ // protocol as master.
+ /*-------------------------------------------------------------------------*/
+ MASTER_LCPhandling(signal, failedNodeId);
+}//Dbdih::execMASTER_LCPCONF()
+
+void Dbdih::execMASTER_LCPREF(Signal* signal)
+{
+ const MasterLCPRef * const ref = (MasterLCPRef *)&signal->theData[0];
+ jamEntry();
+ receiveLoopMacro(MASTER_LCPREQ, ref->senderNodeId);
+ /*-------------------------------------------------------------------------*/
+ // We have now received all responses and are ready to take over the LCP
+ // protocol as master.
+ /*-------------------------------------------------------------------------*/
+ MASTER_LCPhandling(signal, ref->failedNodeId);
+}//Dbdih::execMASTER_LCPREF()
+
+void Dbdih::MASTER_LCPhandling(Signal* signal, Uint32 failedNodeId)
+{
+ /*-------------------------------------------------------------------------
+ *
+ * WE ARE NOW READY TO CONCLUDE THE TAKE OVER AS MASTER.
+ * WE HAVE ENOUGH INFO TO START UP ACTIVITIES IN THE PROPER PLACE.
+ * ALSO SET THE PROPER STATE VARIABLES.
+ *------------------------------------------------------------------------*/
+ c_lcpState.currentFragment.tableId = c_lcpMasterTakeOverState.minTableId;
+ c_lcpState.currentFragment.fragmentId = c_lcpMasterTakeOverState.minFragId;
+ c_lcpState.m_LAST_LCP_FRAG_ORD = c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH;
+
+ NodeRecordPtr failedNodePtr;
+ failedNodePtr.i = failedNodeId;
+ ptrCheckGuard(failedNodePtr, MAX_NDB_NODES, nodeRecord);
+
+ switch (c_lcpMasterTakeOverState.state) {
+ case LMTOS_ALL_IDLE:
+ jam();
+ /* --------------------------------------------------------------------- */
+ // All nodes were idle in the LCP protocol. Start checking for start of LCP
+ // protocol.
+ /* --------------------------------------------------------------------- */
+#ifdef VM_TRACE
+ ndbout_c("MASTER_LCPhandling:: LMTOS_ALL_IDLE -> checkLcpStart");
+#endif
+ checkLcpStart(signal, __LINE__);
+ break;
+ case LMTOS_COPY_ONGOING:
+ jam();
+ /* --------------------------------------------------------------------- */
+ // We were in the starting process of the LCP protocol. We will restart the
+ // protocol by calculating the keep gci and storing the new lcp id.
+ /* --------------------------------------------------------------------- */
+#ifdef VM_TRACE
+ ndbout_c("MASTER_LCPhandling:: LMTOS_COPY_ONGOING -> storeNewLcpId");
+#endif
+ if (c_lcpState.lcpStatus == LCP_STATUS_ACTIVE) {
+ jam();
+ /*---------------------------------------------------------------------*/
+ /* WE NEED TO DECREASE THE LATEST LCP ID SINCE WE HAVE ALREADY */
+ /* STARTED THIS */
+ /* LOCAL CHECKPOINT. */
+ /*---------------------------------------------------------------------*/
+ Uint32 lcpId = SYSFILE->latestLCP_ID;
+#ifdef VM_TRACE
+ ndbout_c("Decreasing latestLCP_ID from %d to %d", lcpId, lcpId - 1);
+#endif
+ SYSFILE->latestLCP_ID--;
+ }//if
+ storeNewLcpIdLab(signal);
+ break;
+ case LMTOS_ALL_ACTIVE:
+ {
+ jam();
+ /* -------------------------------------------------------------------
+ * Everybody was in the active phase. We will restart sending
+ * LCP_FRAGORD to the nodes from the new master.
+ * We also need to set dihLcpStatus to ZACTIVE
+ * in the master node since the master will wait for all nodes to
+ * complete before finalising the LCP process.
+ * ------------------------------------------------------------------ */
+#ifdef VM_TRACE
+ ndbout_c("MASTER_LCPhandling:: LMTOS_ALL_ACTIVE -> "
+ "startLcpRoundLoopLab(table=%u, fragment=%u)",
+ c_lcpMasterTakeOverState.minTableId,
+ c_lcpMasterTakeOverState.minFragId);
+#endif
+
+ c_lcpState.keepGci = SYSFILE->keepGCI;
+ c_lcpState.setLcpStatus(LCP_START_LCP_ROUND, __LINE__);
+ startLcpRoundLoopLab(signal, 0, 0);
+ break;
+ }
+ case LMTOS_LCP_CONCLUDING:
+ {
+ jam();
+ /* ------------------------------------------------------------------- */
+ // The LCP process is in the finalisation phase. We simply wait for it to
+ // complete with signals arriving in. We need to check also if we should
+ // change state due to table write completion during state
+ // collection phase.
+ /* ------------------------------------------------------------------- */
+ ndbrequire(c_lcpState.lcpStatus != LCP_STATUS_IDLE);
+ startLcpRoundLoopLab(signal, 0, 0);
+ break;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ signal->theData[0] = NDB_LE_LCP_TakeoverCompleted;
+ signal->theData[1] = c_lcpMasterTakeOverState.state;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ signal->theData[0] = 7012;
+ execDUMP_STATE_ORD(signal);
+
+ signal->theData[0] = 7015;
+ execDUMP_STATE_ORD(signal);
+
+ c_lcpMasterTakeOverState.set(LMTOS_IDLE, __LINE__);
+
+ checkLocalNodefailComplete(signal, failedNodePtr.i, NF_LCP_TAKE_OVER);
+}
+
+/* ------------------------------------------------------------------------- */
+/* A BLOCK OR A NODE HAS COMPLETED THE HANDLING OF THE NODE FAILURE. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::execNF_COMPLETEREP(Signal* signal)
+{
+ NodeRecordPtr failedNodePtr;
+ NFCompleteRep * const nfCompleteRep = (NFCompleteRep *)&signal->theData[0];
+ jamEntry();
+ const Uint32 blockNo = nfCompleteRep->blockNo;
+ Uint32 nodeId = nfCompleteRep->nodeId;
+ failedNodePtr.i = nfCompleteRep->failedNodeId;
+
+ ptrCheckGuard(failedNodePtr, MAX_NDB_NODES, nodeRecord);
+ switch (blockNo) {
+ case DBTC:
+ jam();
+ ndbrequire(failedNodePtr.p->dbtcFailCompleted == ZFALSE);
+ /* -------------------------------------------------------------------- */
+ // Report the event that DBTC completed node failure handling.
+ /* -------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NodeFailCompleted;
+ signal->theData[1] = DBTC;
+ signal->theData[2] = failedNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+
+ failedNodePtr.p->dbtcFailCompleted = ZTRUE;
+ break;
+ case DBDICT:
+ jam();
+ ndbrequire(failedNodePtr.p->dbdictFailCompleted == ZFALSE);
+ /* --------------------------------------------------------------------- */
+ // Report the event that DBDICT completed node failure handling.
+ /* --------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NodeFailCompleted;
+ signal->theData[1] = DBDICT;
+ signal->theData[2] = failedNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+
+ failedNodePtr.p->dbdictFailCompleted = ZTRUE;
+ break;
+ case DBDIH:
+ jam();
+ ndbrequire(failedNodePtr.p->dbdihFailCompleted == ZFALSE);
+ /* --------------------------------------------------------------------- */
+ // Report the event that DBDIH completed node failure handling.
+ /* --------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NodeFailCompleted;
+ signal->theData[1] = DBDIH;
+ signal->theData[2] = failedNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+
+ failedNodePtr.p->dbdihFailCompleted = ZTRUE;
+ break;
+ case DBLQH:
+ jam();
+ ndbrequire(failedNodePtr.p->dblqhFailCompleted == ZFALSE);
+ /* --------------------------------------------------------------------- */
+ // Report the event that DBDIH completed node failure handling.
+ /* --------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NodeFailCompleted;
+ signal->theData[1] = DBLQH;
+ signal->theData[2] = failedNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+
+ failedNodePtr.p->dblqhFailCompleted = ZTRUE;
+ break;
+ case 0: /* Node has finished */
+ jam();
+ ndbrequire(nodeId < MAX_NDB_NODES);
+
+ if (failedNodePtr.p->recNODE_FAILREP == ZFALSE) {
+ jam();
+ /* ------------------------------------------------------------------- */
+ // We received a report about completion of node failure before we
+ // received the message about the NODE failure ourselves.
+ // We will send the signal to ourselves with a small delay
+ // (10 milliseconds).
+ /* ------------------------------------------------------------------- */
+ //nf->from = __LINE__;
+ sendSignalWithDelay(reference(), GSN_NF_COMPLETEREP, signal, 10,
+ signal->length());
+ return;
+ }//if
+
+ if (!failedNodePtr.p->m_NF_COMPLETE_REP.isWaitingFor(nodeId)){
+ jam();
+ return;
+ }
+
+ failedNodePtr.p->m_NF_COMPLETE_REP.clearWaitingFor(nodeId);;
+
+ /* -------------------------------------------------------------------- */
+ // Report the event that nodeId has completed node failure handling.
+ /* -------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NodeFailCompleted;
+ signal->theData[1] = 0;
+ signal->theData[2] = failedNodePtr.i;
+ signal->theData[3] = nodeId;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+
+ nodeFailCompletedCheckLab(signal, failedNodePtr);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ if (failedNodePtr.p->dbtcFailCompleted == ZFALSE) {
+ jam();
+ return;
+ }//if
+ if (failedNodePtr.p->dbdictFailCompleted == ZFALSE) {
+ jam();
+ return;
+ }//if
+ if (failedNodePtr.p->dbdihFailCompleted == ZFALSE) {
+ jam();
+ return;
+ }//if
+ if (failedNodePtr.p->dblqhFailCompleted == ZFALSE) {
+ jam();
+ return;
+ }//if
+ /* ----------------------------------------------------------------------- */
+ /* ALL BLOCKS IN THIS NODE HAVE COMPLETED THEIR PART OF HANDLING THE */
+ /* NODE FAILURE. WE CAN NOW REPORT THIS COMPLETION TO ALL OTHER NODES. */
+ /* ----------------------------------------------------------------------- */
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ BlockReference ref = calcDihBlockRef(nodePtr.i);
+ NFCompleteRep * const nf = (NFCompleteRep *)&signal->theData[0];
+ nf->blockNo = 0;
+ nf->nodeId = cownNodeId;
+ nf->failedNodeId = failedNodePtr.i;
+ nf->from = __LINE__;
+ sendSignal(ref, GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+ }//if
+ }//for
+ return;
+}//Dbdih::execNF_COMPLETEREP()
+
+void Dbdih::nodeFailCompletedCheckLab(Signal* signal,
+ NodeRecordPtr failedNodePtr)
+{
+ jam();
+ if (!failedNodePtr.p->m_NF_COMPLETE_REP.done()){
+ jam();
+ return;
+ }//if
+ /* ---------------------------------------------------------------------- */
+ /* ALL BLOCKS IN ALL NODES HAVE NOW REPORTED COMPLETION OF THE NODE */
+ /* FAILURE HANDLING. WE ARE NOW READY TO ACCEPT THAT THIS NODE STARTS */
+ /* AGAIN. */
+ /* ---------------------------------------------------------------------- */
+ jam();
+ failedNodePtr.p->nodeStatus = NodeRecord::DEAD;
+ failedNodePtr.p->recNODE_FAILREP = ZFALSE;
+
+ /* ---------------------------------------------------------------------- */
+ // Report the event that all nodes completed node failure handling.
+ /* ---------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_NodeFailCompleted;
+ signal->theData[1] = 0;
+ signal->theData[2] = failedNodePtr.i;
+ signal->theData[3] = 0;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+
+ /* ---------------------------------------------------------------------- */
+ // Report to QMGR that we have concluded recovery handling of this node.
+ /* ---------------------------------------------------------------------- */
+ signal->theData[0] = failedNodePtr.i;
+ sendSignal(QMGR_REF, GSN_NDB_FAILCONF, signal, 1, JBB);
+
+ if (isMaster()) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* IF WE ARE MASTER WE MUST CHECK IF COPY FRAGMENT WAS INTERRUPTED */
+ /* BY THE FAILED NODES. */
+ /* --------------------------------------------------------------------- */
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = 0;
+ ptrAss(takeOverPtr, takeOverRecord);
+ if ((takeOverPtr.p->toMasterStatus == TakeOverRecord::COPY_FRAG) &&
+ (failedNodePtr.i == takeOverPtr.p->toCopyNode)) {
+ jam();
+#ifdef VM_TRACE
+ ndbrequire("Tell jonas" == 0);
+#endif
+ /*------------------------------------------------------------------*/
+ /* WE ARE CURRENTLY IN THE PROCESS OF COPYING A FRAGMENT. WE */
+ /* WILL CHECK IF THE COPY NODE HAVE FAILED. */
+ /*------------------------------------------------------------------*/
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::SELECTING_NEXT;
+ startNextCopyFragment(signal, takeOverPtr.i);
+ return;
+ }//if
+ checkStartTakeOver(signal);
+ }//if
+ return;
+}//Dbdih::nodeFailCompletedCheckLab()
+
+/*****************************************************************************/
+/* ********** SEIZING / RELEASING MODULE *************/
+/*****************************************************************************/
+/*
+ 3.4 L O C A L N O D E S E I Z E
+ ************************************
+ */
+/*
+ 3.4.1 L O C A L N O D E S E I Z E R E Q U E S T
+ ******************************************************
+ */
+void Dbdih::execDISEIZEREQ(Signal* signal)
+{
+ ConnectRecordPtr connectPtr;
+ jamEntry();
+ Uint32 userPtr = signal->theData[0];
+ BlockReference userRef = signal->theData[1];
+ ndbrequire(cfirstconnect != RNIL);
+ connectPtr.i = cfirstconnect;
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+ cfirstconnect = connectPtr.p->nfConnect;
+ connectPtr.p->nfConnect = RNIL;
+ connectPtr.p->userpointer = userPtr;
+ connectPtr.p->userblockref = userRef;
+ connectPtr.p->connectState = ConnectRecord::INUSE;
+ signal->theData[0] = connectPtr.p->userpointer;
+ signal->theData[1] = connectPtr.i;
+ sendSignal(userRef, GSN_DISEIZECONF, signal, 2, JBB);
+}//Dbdih::execDISEIZEREQ()
+
+/*
+ 3.5 L O C A L N O D E R E L E A S E
+ ****************************************
+ */
+/*
+ 3.5.1 L O C A L N O D E R E L E A S E R E Q U E S T
+ *******************************************************=
+ */
+void Dbdih::execDIRELEASEREQ(Signal* signal)
+{
+ ConnectRecordPtr connectPtr;
+ jamEntry();
+ connectPtr.i = signal->theData[0];
+ Uint32 userRef = signal->theData[2];
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+ ndbrequire(connectPtr.p->connectState != ConnectRecord::FREE);
+ ndbrequire(connectPtr.p->userblockref == userRef);
+ signal->theData[0] = connectPtr.p->userpointer;
+ sendSignal(connectPtr.p->userblockref, GSN_DIRELEASECONF, signal, 1, JBB);
+ release_connect(connectPtr);
+}//Dbdih::execDIRELEASEREQ()
+
+/*
+ 3.7 A D D T A B L E
+ **********************=
+ */
+/*****************************************************************************/
+/* ********** TABLE ADDING MODULE *************/
+/*****************************************************************************/
+/*
+ 3.7.1 A D D T A B L E M A I N L Y
+ ***************************************
+ */
+void Dbdih::execCREATE_FRAGMENTATION_REQ(Signal * signal){
+ jamEntry();
+ CreateFragmentationReq * const req =
+ (CreateFragmentationReq*)signal->getDataPtr();
+
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 fragmentNode = req->fragmentNode;
+ const Uint32 fragmentType = req->fragmentationType;
+ //const Uint32 fragmentCount = req->noOfFragments;
+ const Uint32 primaryTableId = req->primaryTableId;
+
+ Uint32 err = 0;
+
+ do {
+ Uint32 noOfFragments = 0;
+ Uint32 noOfReplicas = cnoReplicas;
+ switch(fragmentType){
+ case DictTabInfo::AllNodesSmallTable:
+ jam();
+ noOfFragments = csystemnodes;
+ break;
+ case DictTabInfo::AllNodesMediumTable:
+ jam();
+ noOfFragments = 2 * csystemnodes;
+ break;
+ case DictTabInfo::AllNodesLargeTable:
+ jam();
+ noOfFragments = 4 * csystemnodes;
+ break;
+ case DictTabInfo::SingleFragment:
+ jam();
+ noOfFragments = 1;
+ break;
+#if 0
+ case DictTabInfo::SpecifiedFragmentCount:
+ noOfFragments = (fragmentCount == 0 ? 1 : (fragmentCount + 1)/ 2);
+ break;
+#endif
+ default:
+ jam();
+ err = CreateFragmentationRef::InvalidFragmentationType;
+ break;
+ }
+ if(err)
+ break;
+
+ NodeGroupRecordPtr NGPtr;
+ TabRecordPtr primTabPtr;
+ if (primaryTableId == RNIL) {
+ if(fragmentNode == 0){
+ jam();
+ NGPtr.i = 0;
+ if(noOfFragments < csystemnodes)
+ {
+ NGPtr.i = c_nextNodeGroup;
+ c_nextNodeGroup = (NGPtr.i + 1 == cnoOfNodeGroups ? 0 : NGPtr.i + 1);
+ }
+ } else if(! (fragmentNode < MAX_NDB_NODES)) {
+ jam();
+ err = CreateFragmentationRef::InvalidNodeId;
+ } else {
+ jam();
+ const Uint32 stat = Sysfile::getNodeStatus(fragmentNode,
+ SYSFILE->nodeStatus);
+ switch (stat) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:
+ case Sysfile::NS_TakeOver:
+ jam();
+ break;
+ case Sysfile::NS_NotActive_NotTakenOver:
+ jam();
+ break;
+ case Sysfile::NS_HotSpare:
+ jam();
+ case Sysfile::NS_NotDefined:
+ jam();
+ default:
+ jam();
+ err = CreateFragmentationRef::InvalidNodeType;
+ break;
+ }
+ if(err)
+ break;
+ NGPtr.i = Sysfile::getNodeGroup(fragmentNode,
+ SYSFILE->nodeGroups);
+ break;
+ }
+ } else {
+ if (primaryTableId >= ctabFileSize) {
+ jam();
+ err = CreateFragmentationRef::InvalidPrimaryTable;
+ break;
+ }
+ primTabPtr.i = primaryTableId;
+ ptrAss(primTabPtr, tabRecord);
+ if (primTabPtr.p->tabStatus != TabRecord::TS_ACTIVE) {
+ jam();
+ err = CreateFragmentationRef::InvalidPrimaryTable;
+ break;
+ }
+ if (noOfFragments != primTabPtr.p->totalfragments) {
+ jam();
+ err = CreateFragmentationRef::InvalidFragmentationType;
+ break;
+ }
+ }
+
+ Uint32 count = 2;
+ Uint16 *fragments = (Uint16*)(signal->theData+25);
+ if (primaryTableId == RNIL) {
+ jam();
+ Uint8 next_replica_node[MAX_NDB_NODES];
+ memset(next_replica_node,0,sizeof(next_replica_node));
+ for(Uint32 fragNo = 0; fragNo<noOfFragments; fragNo++){
+ jam();
+ ptrCheckGuard(NGPtr, MAX_NDB_NODES, nodeGroupRecord);
+ const Uint32 max = NGPtr.p->nodeCount;
+
+ Uint32 tmp= next_replica_node[NGPtr.i];
+ for(Uint32 replicaNo = 0; replicaNo<noOfReplicas; replicaNo++)
+ {
+ jam();
+ const Uint32 nodeId = NGPtr.p->nodesInGroup[tmp++];
+ fragments[count++] = nodeId;
+ tmp = (tmp >= max ? 0 : tmp);
+ }
+ tmp++;
+ next_replica_node[NGPtr.i]= (tmp >= max ? 0 : tmp);
+
+ /**
+ * Next node group for next fragment
+ */
+ NGPtr.i++;
+ NGPtr.i = (NGPtr.i == cnoOfNodeGroups ? 0 : NGPtr.i);
+ }
+ } else {
+ for (Uint32 fragNo = 0;
+ fragNo < primTabPtr.p->totalfragments; fragNo++) {
+ jam();
+ FragmentstorePtr fragPtr;
+ ReplicaRecordPtr replicaPtr;
+ getFragstore(primTabPtr.p, fragNo, fragPtr);
+ fragments[count++] = fragPtr.p->preferredPrimary;
+ for (replicaPtr.i = fragPtr.p->storedReplicas;
+ replicaPtr.i != RNIL;
+ replicaPtr.i = replicaPtr.p->nextReplica) {
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if (replicaPtr.p->procNode != fragPtr.p->preferredPrimary) {
+ jam();
+ fragments[count++] = replicaPtr.p->procNode;
+ }//if
+ }//for
+ for (replicaPtr.i = fragPtr.p->oldStoredReplicas;
+ replicaPtr.i != RNIL;
+ replicaPtr.i = replicaPtr.p->nextReplica) {
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if (replicaPtr.p->procNode != fragPtr.p->preferredPrimary) {
+ jam();
+ fragments[count++] = replicaPtr.p->procNode;
+ }//if
+ }//for
+ }
+ }
+ ndbrequire(count == (2 + noOfReplicas * noOfFragments));
+
+ CreateFragmentationConf * const conf =
+ (CreateFragmentationConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->noOfReplicas = noOfReplicas;
+ conf->noOfFragments = noOfFragments;
+
+ fragments[0] = noOfReplicas;
+ fragments[1] = noOfFragments;
+
+ if(senderRef != 0)
+ {
+ LinearSectionPtr ptr[3];
+ ptr[0].p = (Uint32*)&fragments[0];
+ ptr[0].sz = (count + 1) / 2;
+ sendSignal(senderRef,
+ GSN_CREATE_FRAGMENTATION_CONF,
+ signal,
+ CreateFragmentationConf::SignalLength,
+ JBB,
+ ptr,
+ 1);
+ }
+ else
+ {
+ // Execute direct
+ signal->theData[0] = 0;
+ }
+ return;
+ } while(false);
+
+ if(senderRef != 0)
+ {
+ CreateFragmentationRef * const ref =
+ (CreateFragmentationRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->errorCode = err;
+ sendSignal(senderRef, GSN_CREATE_FRAGMENTATION_REF, signal,
+ CreateFragmentationRef::SignalLength, JBB);
+ }
+ else
+ {
+ // Execute direct
+ signal->theData[0] = err;
+ }
+}
+
+void Dbdih::execDIADDTABREQ(Signal* signal)
+{
+ jamEntry();
+
+ DiAddTabReq * const req = (DiAddTabReq*)signal->getDataPtr();
+
+ // Seize connect record
+ ndbrequire(cfirstconnect != RNIL);
+ ConnectRecordPtr connectPtr;
+ connectPtr.i = cfirstconnect;
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+ cfirstconnect = connectPtr.p->nfConnect;
+
+ const Uint32 userPtr = req->connectPtr;
+ const BlockReference userRef = signal->getSendersBlockRef();
+ connectPtr.p->nfConnect = RNIL;
+ connectPtr.p->userpointer = userPtr;
+ connectPtr.p->userblockref = userRef;
+ connectPtr.p->connectState = ConnectRecord::INUSE;
+ connectPtr.p->table = req->tableId;
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tabPtr.p->connectrec = connectPtr.i;
+ tabPtr.p->tableType = req->tableType;
+ tabPtr.p->schemaVersion = req->schemaVersion;
+ tabPtr.p->primaryTableId = req->primaryTableId;
+
+ if(tabPtr.p->tabStatus == TabRecord::TS_ACTIVE){
+ jam();
+ tabPtr.p->tabStatus = TabRecord::TS_CREATING;
+ sendAddFragreq(signal, connectPtr, tabPtr, 0);
+ return;
+ }
+
+ if(getNodeState().getSystemRestartInProgress() &&
+ tabPtr.p->tabStatus == TabRecord::TS_IDLE){
+ jam();
+
+ ndbrequire(cmasterNodeId == getOwnNodeId());
+ tabPtr.p->tabStatus = TabRecord::TS_CREATING;
+
+ initTableFile(tabPtr);
+ FileRecordPtr filePtr;
+ filePtr.i = tabPtr.p->tabFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_TABLE;
+ return;
+ }
+
+ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+ /* AT THE TIME OF INITIATING THE FILE OF TABLE */
+ /* DESCRIPTION IS CREATED FOR APPROPRIATE SIZE. EACH */
+ /* EACH RECORD IN THIS FILE HAS THE INFORMATION ABOUT */
+ /* ONE TABLE. THE POINTER TO THIS RECORD IS THE TABLE */
+ /* REFERENCE. IN THE BEGINNING ALL RECORDS ARE CREATED */
+ /* BUT THEY DO NOT HAVE ANY INFORMATION ABOUT ANY TABLE*/
+ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+ tabPtr.p->tabStatus = TabRecord::TS_CREATING;
+ tabPtr.p->storedTable = req->storedTable;
+ tabPtr.p->method = TabRecord::HASH;
+ tabPtr.p->kvalue = req->kValue;
+
+ union {
+ Uint16 fragments[2 + MAX_FRAG_PER_NODE*MAX_REPLICAS*MAX_NDB_NODES];
+ Uint32 align;
+ };
+ SegmentedSectionPtr fragDataPtr;
+ signal->getSection(fragDataPtr, DiAddTabReq::FRAGMENTATION);
+ copy((Uint32*)fragments, fragDataPtr);
+ releaseSections(signal);
+
+ const Uint32 noReplicas = fragments[0];
+ const Uint32 noFragments = fragments[1];
+
+ tabPtr.p->noOfBackups = noReplicas - 1;
+ tabPtr.p->totalfragments = noFragments;
+ ndbrequire(noReplicas == cnoReplicas); // Only allowed
+
+ if (ERROR_INSERTED(7173)) {
+ addtabrefuseLab(signal, connectPtr, ZREPLERROR1);
+ return;
+ }
+ if ((noReplicas * noFragments) > cnoFreeReplicaRec) {
+ jam();
+ addtabrefuseLab(signal, connectPtr, ZREPLERROR1);
+ return;
+ }//if
+ if (noFragments > cremainingfrags) {
+ jam();
+ addtabrefuseLab(signal, connectPtr, ZREPLERROR1);
+ return;
+ }//if
+
+ Uint32 logTotalFragments = 1;
+ while (logTotalFragments <= tabPtr.p->totalfragments) {
+ jam();
+ logTotalFragments <<= 1;
+ }
+ logTotalFragments >>= 1;
+ tabPtr.p->mask = logTotalFragments - 1;
+ tabPtr.p->hashpointer = tabPtr.p->totalfragments - logTotalFragments;
+ allocFragments(tabPtr.p->totalfragments, tabPtr);
+
+ Uint32 index = 2;
+ for (Uint32 fragId = 0; fragId < noFragments; fragId++) {
+ jam();
+ FragmentstorePtr fragPtr;
+ Uint32 activeIndex = 0;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ fragPtr.p->preferredPrimary = fragments[index];
+ for (Uint32 i = 0; i<noReplicas; i++) {
+ const Uint32 nodeId = fragments[index++];
+ ReplicaRecordPtr replicaPtr;
+ allocStoredReplica(fragPtr, replicaPtr, nodeId);
+ if (getNodeStatus(nodeId) == NodeRecord::ALIVE) {
+ jam();
+ ndbrequire(activeIndex < MAX_REPLICAS);
+ fragPtr.p->activeNodes[activeIndex] = nodeId;
+ activeIndex++;
+ } else {
+ jam();
+ removeStoredReplica(fragPtr, replicaPtr);
+ linkOldStoredReplica(fragPtr, replicaPtr);
+ }//if
+ }//for
+ fragPtr.p->fragReplicas = activeIndex;
+ ndbrequire(activeIndex > 0 && fragPtr.p->storedReplicas != RNIL);
+ }
+ initTableFile(tabPtr);
+ tabPtr.p->tabCopyStatus = TabRecord::CS_ADD_TABLE_MASTER;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+}
+
+void
+Dbdih::addTable_closeConf(Signal * signal, Uint32 tabPtrI){
+ TabRecordPtr tabPtr;
+ tabPtr.i = tabPtrI;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ConnectRecordPtr connectPtr;
+ connectPtr.i = tabPtr.p->connectrec;
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+
+ sendAddFragreq(signal, connectPtr, tabPtr, 0);
+}
+
+void
+Dbdih::sendAddFragreq(Signal* signal, ConnectRecordPtr connectPtr,
+ TabRecordPtr tabPtr, Uint32 fragId){
+ jam();
+ const Uint32 fragCount = tabPtr.p->totalfragments;
+ ReplicaRecordPtr replicaPtr; replicaPtr.i = RNIL;
+ for(; fragId<fragCount; fragId++){
+ jam();
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+
+ replicaPtr.i = fragPtr.p->storedReplicas;
+ while(replicaPtr.i != RNIL){
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if(replicaPtr.p->procNode == getOwnNodeId()){
+ break;
+ }
+ replicaPtr.i = replicaPtr.p->nextReplica;
+ }
+
+ if(replicaPtr.i != RNIL){
+ jam();
+ break;
+ }
+
+ replicaPtr.i = fragPtr.p->oldStoredReplicas;
+ while(replicaPtr.i != RNIL){
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if(replicaPtr.p->procNode == getOwnNodeId()){
+ break;
+ }
+ replicaPtr.i = replicaPtr.p->nextReplica;
+ }
+
+ if(replicaPtr.i != RNIL){
+ jam();
+ break;
+ }
+ }
+
+ if(replicaPtr.i != RNIL){
+ jam();
+ ndbrequire(fragId < fragCount);
+ ndbrequire(replicaPtr.p->procNode == getOwnNodeId());
+
+ Uint32 requestInfo = 0;
+ if(!tabPtr.p->storedTable){
+ requestInfo |= LqhFragReq::TemporaryTable;
+ }
+
+ if(getNodeState().getNodeRestartInProgress()){
+ requestInfo |= LqhFragReq::CreateInRunning;
+ }
+
+ AddFragReq* const req = (AddFragReq*)signal->getDataPtr();
+ req->dihPtr = connectPtr.i;
+ req->senderData = connectPtr.p->userpointer;
+ req->fragmentId = fragId;
+ req->requestInfo = requestInfo;
+ req->tableId = tabPtr.i;
+ req->nextLCP = 0;
+ req->nodeId = getOwnNodeId();
+ req->totalFragments = fragCount;
+ req->startGci = SYSFILE->newestRestorableGCI;
+ sendSignal(DBDICT_REF, GSN_ADD_FRAGREQ, signal,
+ AddFragReq::SignalLength, JBB);
+ return;
+ }
+
+ // Done
+ DiAddTabConf * const conf = (DiAddTabConf*)signal->getDataPtr();
+ conf->senderData = connectPtr.p->userpointer;
+ sendSignal(connectPtr.p->userblockref, GSN_DIADDTABCONF, signal,
+ DiAddTabConf::SignalLength, JBB);
+
+ // Release
+ release_connect(connectPtr);
+}
+void
+Dbdih::release_connect(ConnectRecordPtr ptr)
+{
+ ptr.p->userblockref = ZNIL;
+ ptr.p->userpointer = RNIL;
+ ptr.p->connectState = ConnectRecord::FREE;
+ ptr.p->nfConnect = cfirstconnect;
+ cfirstconnect = ptr.i;
+}
+
+void
+Dbdih::execADD_FRAGCONF(Signal* signal){
+ jamEntry();
+ AddFragConf * const conf = (AddFragConf*)signal->getDataPtr();
+
+ ConnectRecordPtr connectPtr;
+ connectPtr.i = conf->dihPtr;
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = connectPtr.p->table;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ sendAddFragreq(signal, connectPtr, tabPtr, conf->fragId + 1);
+}
+
+void
+Dbdih::execADD_FRAGREF(Signal* signal){
+ jamEntry();
+ AddFragRef * const ref = (AddFragRef*)signal->getDataPtr();
+
+ ConnectRecordPtr connectPtr;
+ connectPtr.i = ref->dihPtr;
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+
+ {
+ DiAddTabRef * const ref = (DiAddTabRef*)signal->getDataPtr();
+ ref->senderData = connectPtr.p->userpointer;
+ ref->errorCode = ~0;
+ sendSignal(connectPtr.p->userblockref, GSN_DIADDTABREF, signal,
+ DiAddTabRef::SignalLength, JBB);
+ }
+
+ // Release
+ release_connect(connectPtr);
+}
+
+/*
+ 3.7.1.3 R E F U S E
+ *********************
+ */
+void Dbdih::addtabrefuseLab(Signal* signal, ConnectRecordPtr connectPtr, Uint32 errorCode)
+{
+ signal->theData[0] = connectPtr.p->userpointer;
+ signal->theData[1] = errorCode;
+ sendSignal(connectPtr.p->userblockref, GSN_DIADDTABREF, signal, 2, JBB);
+ release_connect(connectPtr);
+ return;
+}//Dbdih::addtabrefuseLab()
+
+/*
+ 3.7.2 A D D T A B L E D U P L I C A T I O N
+ *************************************************
+ */
+/*
+ 3.7.2.1 A D D T A B L E D U P L I C A T I O N R E Q U E S T
+ *******************************************************************=
+ */
+
+/*
+ D E L E T E T A B L E
+ **********************=
+ */
+/*****************************************************************************/
+/*********** DELETE TABLE MODULE *************/
+/*****************************************************************************/
+void
+Dbdih::execDROP_TAB_REQ(Signal* signal){
+ jamEntry();
+ DropTabReq* req = (DropTabReq*)signal->getDataPtr();
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ tabPtr.p->m_dropTab.tabUserRef = req->senderRef;
+ tabPtr.p->m_dropTab.tabUserPtr = req->senderData;
+
+ DropTabReq::RequestType rt = (DropTabReq::RequestType)req->requestType;
+
+ switch(rt){
+ case DropTabReq::OnlineDropTab:
+ jam();
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_DROPPING);
+ releaseTable(tabPtr);
+ break;
+ case DropTabReq::CreateTabDrop:
+ jam();
+ releaseTable(tabPtr);
+ break;
+ case DropTabReq::RestartDropTab:
+ break;
+ }
+
+ startDeleteFile(signal, tabPtr);
+}
+
+void Dbdih::startDeleteFile(Signal* signal, TabRecordPtr tabPtr)
+{
+ if (tabPtr.p->tabFile[0] == RNIL) {
+ jam();
+ initTableFile(tabPtr);
+ }//if
+ openTableFileForDelete(signal, tabPtr.p->tabFile[0]);
+}//Dbdih::startDeleteFile()
+
+void Dbdih::openTableFileForDelete(Signal* signal, Uint32 fileIndex)
+{
+ FileRecordPtr filePtr;
+ filePtr.i = fileIndex;
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::TABLE_OPEN_FOR_DELETE;
+}//Dbdih::openTableFileForDelete()
+
+void Dbdih::tableOpenLab(Signal* signal, FileRecordPtr filePtr)
+{
+ closeFileDelete(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::TABLE_CLOSE_DELETE;
+ return;
+}//Dbdih::tableOpenLab()
+
+void Dbdih::tableDeleteLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if (filePtr.i == tabPtr.p->tabFile[0]) {
+ jam();
+ openTableFileForDelete(signal, tabPtr.p->tabFile[1]);
+ return;
+ }//if
+ ndbrequire(filePtr.i == tabPtr.p->tabFile[1]);
+
+ releaseFile(tabPtr.p->tabFile[0]);
+ releaseFile(tabPtr.p->tabFile[1]);
+ tabPtr.p->tabFile[0] = tabPtr.p->tabFile[1] = RNIL;
+
+ tabPtr.p->tabStatus = TabRecord::TS_IDLE;
+
+ DropTabConf * const dropConf = (DropTabConf *)signal->getDataPtrSend();
+ dropConf->senderRef = reference();
+ dropConf->senderData = tabPtr.p->m_dropTab.tabUserPtr;
+ dropConf->tableId = tabPtr.i;
+ sendSignal(tabPtr.p->m_dropTab.tabUserRef, GSN_DROP_TAB_CONF,
+ signal, DropTabConf::SignalLength, JBB);
+
+ tabPtr.p->m_dropTab.tabUserPtr = RNIL;
+ tabPtr.p->m_dropTab.tabUserRef = 0;
+}//Dbdih::tableDeleteLab()
+
+
+void Dbdih::releaseTable(TabRecordPtr tabPtr)
+{
+ FragmentstorePtr fragPtr;
+ if (tabPtr.p->noOfFragChunks > 0) {
+ for (Uint32 fragId = 0; fragId < tabPtr.p->totalfragments; fragId++) {
+ jam();
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ releaseReplicas(fragPtr.p->storedReplicas);
+ releaseReplicas(fragPtr.p->oldStoredReplicas);
+ }//for
+ releaseFragments(tabPtr);
+ }
+ if (tabPtr.p->tabFile[0] != RNIL) {
+ jam();
+ releaseFile(tabPtr.p->tabFile[0]);
+ releaseFile(tabPtr.p->tabFile[1]);
+ tabPtr.p->tabFile[0] = tabPtr.p->tabFile[1] = RNIL;
+ }//if
+}//Dbdih::releaseTable()
+
+void Dbdih::releaseReplicas(Uint32 replicaPtrI)
+{
+ ReplicaRecordPtr replicaPtr;
+ replicaPtr.i = replicaPtrI;
+ jam();
+ while (replicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ Uint32 tmp = replicaPtr.p->nextReplica;
+ replicaPtr.p->nextReplica = cfirstfreeReplica;
+ cfirstfreeReplica = replicaPtr.i;
+ replicaPtr.i = tmp;
+ cnoFreeReplicaRec++;
+ }//while
+}//Dbdih::releaseReplicas()
+
+void Dbdih::seizeReplicaRec(ReplicaRecordPtr& replicaPtr)
+{
+ replicaPtr.i = cfirstfreeReplica;
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ cfirstfreeReplica = replicaPtr.p->nextReplica;
+ cnoFreeReplicaRec--;
+ replicaPtr.p->nextReplica = RNIL;
+}//Dbdih::seizeReplicaRec()
+
+void Dbdih::releaseFile(Uint32 fileIndex)
+{
+ FileRecordPtr filePtr;
+ filePtr.i = fileIndex;
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ filePtr.p->nextFile = cfirstfreeFile;
+ cfirstfreeFile = filePtr.i;
+}//Dbdih::releaseFile()
+
+
+void Dbdih::execALTER_TAB_REQ(Signal * signal)
+{
+ AlterTabReq* const req = (AlterTabReq*)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 changeMask = req->changeMask;
+ const Uint32 tableId = req->tableId;
+ const Uint32 tableVersion = req->tableVersion;
+ const Uint32 gci = req->gci;
+ AlterTabReq::RequestType requestType =
+ (AlterTabReq::RequestType) req->requestType;
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tabPtr.p->schemaVersion = tableVersion;
+
+ // Request handled successfully
+ AlterTabConf * conf = (AlterTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->changeMask = changeMask;
+ conf->tableId = tableId;
+ conf->tableVersion = tableVersion;
+ conf->gci = gci;
+ conf->requestType = requestType;
+ sendSignal(senderRef, GSN_ALTER_TAB_CONF, signal,
+ AlterTabConf::SignalLength, JBB);
+}
+
+/*
+ G E T N O D E S
+ **********************=
+ */
+/*****************************************************************************/
+/* ********** TRANSACTION HANDLING MODULE *************/
+/*****************************************************************************/
+/*
+ 3.8.1 G E T N O D E S R E Q U E S T
+ ******************************************
+ Asks what nodes should be part of a transaction.
+*/
+void Dbdih::execDIGETNODESREQ(Signal* signal)
+{
+ const DiGetNodesReq * const req = (DiGetNodesReq *)&signal->theData[0];
+ FragmentstorePtr fragPtr;
+ TabRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ Uint32 hashValue = req->hashValue;
+ Uint32 ttabFileSize = ctabFileSize;
+ TabRecord* regTabDesc = tabRecord;
+ jamEntry();
+ ptrCheckGuard(tabPtr, ttabFileSize, regTabDesc);
+ Uint32 fragId = hashValue & tabPtr.p->mask;
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_ACTIVE);
+ if (fragId < tabPtr.p->hashpointer) {
+ jam();
+ fragId = hashValue & ((tabPtr.p->mask << 1) + 1);
+ }//if
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ DiGetNodesConf * const conf = (DiGetNodesConf *)&signal->theData[0];
+ Uint32 nodeCount = extractNodeInfo(fragPtr.p, conf->nodes);
+ Uint32 sig2 = (nodeCount - 1) +
+ (fragPtr.p->distributionKey << 16);
+ conf->zero = 0;
+ conf->reqinfo = sig2;
+ conf->fragId = fragId;
+}//Dbdih::execDIGETNODESREQ()
+
+Uint32 Dbdih::extractNodeInfo(const Fragmentstore * fragPtr, Uint32 nodes[])
+{
+ Uint32 nodeCount = 0;
+ for (Uint32 i = 0; i < fragPtr->fragReplicas; i++) {
+ jam();
+ NodeRecordPtr nodePtr;
+ ndbrequire(i < MAX_REPLICAS);
+ nodePtr.i = fragPtr->activeNodes[i];
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->useInTransactions) {
+ jam();
+ nodes[nodeCount] = nodePtr.i;
+ nodeCount++;
+ }//if
+ }//for
+ ndbrequire(nodeCount > 0);
+ return nodeCount;
+}//Dbdih::extractNodeInfo()
+
+void
+Dbdih::getFragstore(TabRecord * tab, //In parameter
+ Uint32 fragNo, //In parameter
+ FragmentstorePtr & fragptr) //Out parameter
+{
+ FragmentstorePtr fragPtr;
+ Uint32 chunkNo = fragNo >> LOG_NO_OF_FRAGS_PER_CHUNK;
+ Uint32 chunkIndex = fragNo & (NO_OF_FRAGS_PER_CHUNK - 1);
+ Uint32 TfragstoreFileSize = cfragstoreFileSize;
+ Fragmentstore* TfragStore = fragmentstore;
+ if (chunkNo < MAX_NDB_NODES) {
+ fragPtr.i = tab->startFid[chunkNo] + chunkIndex;
+ ptrCheckGuard(fragPtr, TfragstoreFileSize, TfragStore);
+ fragptr = fragPtr;
+ return;
+ }//if
+ ndbrequire(false);
+}//Dbdih::getFragstore()
+
+void Dbdih::allocFragments(Uint32 noOfFragments, TabRecordPtr tabPtr)
+{
+ FragmentstorePtr fragPtr;
+ Uint32 noOfChunks = (noOfFragments + (NO_OF_FRAGS_PER_CHUNK - 1)) >> LOG_NO_OF_FRAGS_PER_CHUNK;
+ ndbrequire(cremainingfrags >= noOfFragments);
+ for (Uint32 i = 0; i < noOfChunks; i++) {
+ jam();
+ Uint32 baseFrag = cfirstfragstore;
+ tabPtr.p->startFid[i] = baseFrag;
+ fragPtr.i = baseFrag;
+ ptrCheckGuard(fragPtr, cfragstoreFileSize, fragmentstore);
+ cfirstfragstore = fragPtr.p->nextFragmentChunk;
+ cremainingfrags -= NO_OF_FRAGS_PER_CHUNK;
+ for (Uint32 j = 0; j < NO_OF_FRAGS_PER_CHUNK; j++) {
+ jam();
+ fragPtr.i = baseFrag + j;
+ ptrCheckGuard(fragPtr, cfragstoreFileSize, fragmentstore);
+ initFragstore(fragPtr);
+ }//if
+ }//for
+ tabPtr.p->noOfFragChunks = noOfChunks;
+}//Dbdih::allocFragments()
+
+void Dbdih::releaseFragments(TabRecordPtr tabPtr)
+{
+ FragmentstorePtr fragPtr;
+ for (Uint32 i = 0; i < tabPtr.p->noOfFragChunks; i++) {
+ jam();
+ Uint32 baseFrag = tabPtr.p->startFid[i];
+ fragPtr.i = baseFrag;
+ ptrCheckGuard(fragPtr, cfragstoreFileSize, fragmentstore);
+ fragPtr.p->nextFragmentChunk = cfirstfragstore;
+ cfirstfragstore = baseFrag;
+ tabPtr.p->startFid[i] = RNIL;
+ cremainingfrags += NO_OF_FRAGS_PER_CHUNK;
+ }//for
+ tabPtr.p->noOfFragChunks = 0;
+}//Dbdih::releaseFragments()
+
+void Dbdih::initialiseFragstore()
+{
+ Uint32 i;
+ FragmentstorePtr fragPtr;
+ for (i = 0; i < cfragstoreFileSize; i++) {
+ fragPtr.i = i;
+ ptrCheckGuard(fragPtr, cfragstoreFileSize, fragmentstore);
+ initFragstore(fragPtr);
+ }//for
+ Uint32 noOfChunks = cfragstoreFileSize >> LOG_NO_OF_FRAGS_PER_CHUNK;
+ fragPtr.i = 0;
+ cfirstfragstore = RNIL;
+ cremainingfrags = 0;
+ for (i = 0; i < noOfChunks; i++) {
+ refresh_watch_dog();
+ ptrCheckGuard(fragPtr, cfragstoreFileSize, fragmentstore);
+ fragPtr.p->nextFragmentChunk = cfirstfragstore;
+ cfirstfragstore = fragPtr.i;
+ fragPtr.i += NO_OF_FRAGS_PER_CHUNK;
+ cremainingfrags += NO_OF_FRAGS_PER_CHUNK;
+ }//for
+}//Dbdih::initialiseFragstore()
+
+/*
+ 3.9 V E R I F I C A T I O N
+ ****************************=
+ */
+/****************************************************************************/
+/* ********** VERIFICATION SUB-MODULE *************/
+/****************************************************************************/
+/*
+ 3.9.1 R E C E I V I N G O F V E R I F I C A T I O N R E Q U E S T
+ *************************************************************************
+ */
+void Dbdih::execDIVERIFYREQ(Signal* signal)
+{
+
+ jamEntry();
+ if ((getBlockCommit() == false) &&
+ (cfirstVerifyQueue == RNIL)) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // We are not blocked and the verify queue was empty currently so we can
+ // simply reply back to TC immediately. The method was called with
+ // EXECUTE_DIRECT so we reply back by setting signal data and returning.
+ // theData[0] already contains the correct information so
+ // we need not touch it.
+ /*-----------------------------------------------------------------------*/
+ signal->theData[1] = currentgcp;
+ signal->theData[2] = 0;
+ return;
+ }//if
+ /*-------------------------------------------------------------------------*/
+ // Since we are blocked we need to put this operation last in the verify
+ // queue to ensure that operation starts up in the correct order.
+ /*-------------------------------------------------------------------------*/
+ ApiConnectRecordPtr tmpApiConnectptr;
+ ApiConnectRecordPtr localApiConnectptr;
+
+ cverifyQueueCounter++;
+ localApiConnectptr.i = signal->theData[0];
+ tmpApiConnectptr.i = clastVerifyQueue;
+ ptrCheckGuard(localApiConnectptr, capiConnectFileSize, apiConnectRecord);
+ localApiConnectptr.p->apiGci = cnewgcp;
+ localApiConnectptr.p->nextApi = RNIL;
+ clastVerifyQueue = localApiConnectptr.i;
+ if (tmpApiConnectptr.i == RNIL) {
+ jam();
+ cfirstVerifyQueue = localApiConnectptr.i;
+ } else {
+ jam();
+ ptrCheckGuard(tmpApiConnectptr, capiConnectFileSize, apiConnectRecord);
+ tmpApiConnectptr.p->nextApi = localApiConnectptr.i;
+ }//if
+ emptyverificbuffer(signal, false);
+ signal->theData[2] = 1; // Indicate no immediate return
+ return;
+}//Dbdih::execDIVERIFYREQ()
+
+void Dbdih::execDI_FCOUNTREQ(Signal* signal)
+{
+ ConnectRecordPtr connectPtr;
+ TabRecordPtr tabPtr;
+ jamEntry();
+ connectPtr.i = signal->theData[0];
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_ACTIVE);
+
+ if(connectPtr.i != RNIL){
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+ if (connectPtr.p->connectState == ConnectRecord::INUSE) {
+ jam();
+ signal->theData[0] = connectPtr.p->userpointer;
+ signal->theData[1] = tabPtr.p->totalfragments;
+ sendSignal(connectPtr.p->userblockref, GSN_DI_FCOUNTCONF, signal,2, JBB);
+ return;
+ }//if
+ signal->theData[0] = connectPtr.p->userpointer;
+ signal->theData[1] = ZERRONOUSSTATE;
+ sendSignal(connectPtr.p->userblockref, GSN_DI_FCOUNTREF, signal, 2, JBB);
+ return;
+ }//if
+
+ //connectPtr.i == RNIL -> question without connect record
+ const Uint32 senderData = signal->theData[2];
+ const BlockReference senderRef = signal->senderBlockRef();
+ signal->theData[0] = RNIL;
+ signal->theData[1] = tabPtr.p->totalfragments;
+ signal->theData[2] = tabPtr.i;
+ signal->theData[3] = senderData;
+ signal->theData[4] = tabPtr.p->noOfBackups;
+ sendSignal(senderRef, GSN_DI_FCOUNTCONF, signal, 5, JBB);
+}//Dbdih::execDI_FCOUNTREQ()
+
+void Dbdih::execDIGETPRIMREQ(Signal* signal)
+{
+ FragmentstorePtr fragPtr;
+ ConnectRecordPtr connectPtr;
+ TabRecordPtr tabPtr;
+ jamEntry();
+ Uint32 passThrough = signal->theData[1];
+ tabPtr.i = signal->theData[2];
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if (DictTabInfo::isOrderedIndex(tabPtr.p->tableType)) {
+ jam();
+ tabPtr.i = tabPtr.p->primaryTableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ }
+ Uint32 fragId = signal->theData[3];
+
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_ACTIVE);
+ connectPtr.i = signal->theData[0];
+ if(connectPtr.i != RNIL)
+ {
+ jam();
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+ signal->theData[0] = connectPtr.p->userpointer;
+ }
+ else
+ {
+ jam();
+ signal->theData[0] = RNIL;
+ }
+
+ Uint32 nodes[MAX_REPLICAS];
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ Uint32 count = extractNodeInfo(fragPtr.p, nodes);
+
+ signal->theData[1] = passThrough;
+ signal->theData[2] = nodes[0];
+ signal->theData[3] = nodes[1];
+ signal->theData[4] = nodes[2];
+ signal->theData[5] = nodes[3];
+ signal->theData[6] = count;
+ signal->theData[7] = tabPtr.i;
+ signal->theData[8] = fragId;
+
+ const BlockReference senderRef = signal->senderBlockRef();
+ sendSignal(senderRef, GSN_DIGETPRIMCONF, signal, 9, JBB);
+}//Dbdih::execDIGETPRIMREQ()
+
+/****************************************************************************/
+/* ********** GLOBAL-CHECK-POINT HANDLING MODULE *************/
+/****************************************************************************/
+/*
+ 3.10 G L O B A L C H E C K P O I N T ( IN M A S T E R R O L E)
+ *******************************************************************
+ */
+void Dbdih::checkGcpStopLab(Signal* signal)
+{
+ Uint32 tgcpStatus;
+
+ tgcpStatus = cgcpStatus;
+ if (tgcpStatus == coldGcpStatus) {
+ jam();
+ if (coldGcpId == cnewgcp) {
+ jam();
+ if (cgcpStatus != GCP_READY) {
+ jam();
+ cgcpSameCounter++;
+ if (cgcpSameCounter == 1200) {
+ jam();
+#ifdef VM_TRACE
+ ndbout << "System crash due to GCP Stop in state = ";
+ ndbout << (Uint32) cgcpStatus << endl;
+#endif
+ crashSystemAtGcpStop(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ if (cgcpOrderBlocked == 0) {
+ jam();
+ cgcpSameCounter++;
+ if (cgcpSameCounter == 1200) {
+ jam();
+#ifdef VM_TRACE
+ ndbout << "System crash due to GCP Stop in state = ";
+ ndbout << (Uint32) cgcpStatus << endl;
+#endif
+ crashSystemAtGcpStop(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ cgcpSameCounter = 0;
+ }//if
+ }//if
+ } else {
+ jam();
+ cgcpSameCounter = 0;
+ }//if
+ } else {
+ jam();
+ cgcpSameCounter = 0;
+ }//if
+ signal->theData[0] = DihContinueB::ZCHECK_GCP_STOP;
+ signal->theData[1] = coldGcpStatus;
+ signal->theData[2] = cgcpStatus;
+ signal->theData[3] = coldGcpId;
+ signal->theData[4] = cnewgcp;
+ signal->theData[5] = cgcpSameCounter;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 6);
+ coldGcpStatus = cgcpStatus;
+ coldGcpId = cnewgcp;
+ return;
+}//Dbdih::checkGcpStopLab()
+
+void Dbdih::startGcpLab(Signal* signal, Uint32 aWaitTime)
+{
+ if ((cgcpOrderBlocked == 1) ||
+ (c_nodeStartMaster.blockGcp == true) ||
+ (cfirstVerifyQueue != RNIL)) {
+ /*************************************************************************/
+ // 1: Global Checkpoint has been stopped by management command
+ // 2: Global Checkpoint is blocked by node recovery activity
+ // 3: Previous global checkpoint is not yet completed.
+ // All this means that global checkpoint cannot start now.
+ /*************************************************************************/
+ jam();
+ cgcpStartCounter++;
+ signal->theData[0] = DihContinueB::ZSTART_GCP;
+ signal->theData[1] = aWaitTime > 100 ? (aWaitTime - 100) : 0;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 2);
+ return;
+ }//if
+ if (cstartGcpNow == false && aWaitTime > 100){
+ /*************************************************************************/
+ // We still have more than 100 milliseconds before we start the next and
+ // nobody has ordered immediate start of a global checkpoint.
+ // During initial start we will use continuos global checkpoints to
+ // speed it up since we need to complete a global checkpoint after
+ // inserting a lot of records.
+ /*************************************************************************/
+ jam();
+ cgcpStartCounter++;
+ signal->theData[0] = DihContinueB::ZSTART_GCP;
+ signal->theData[1] = (aWaitTime - 100);
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 2);
+ return;
+ }//if
+ cgcpStartCounter = 0;
+ cstartGcpNow = false;
+ /***************************************************************************/
+ // Report the event that a global checkpoint has started.
+ /***************************************************************************/
+ signal->theData[0] = NDB_LE_GlobalCheckpointStarted; //Event type
+ signal->theData[1] = cnewgcp;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ CRASH_INSERTION(7000);
+ cnewgcp++;
+ signal->setTrace(TestOrd::TraceGlobalCheckpoint);
+ sendLoopMacro(GCP_PREPARE, sendGCP_PREPARE);
+ cgcpStatus = GCP_PREPARE_SENT;
+}//Dbdih::startGcpLab()
+
+void Dbdih::execGCP_PREPARECONF(Signal* signal)
+{
+ jamEntry();
+ Uint32 senderNodeId = signal->theData[0];
+ Uint32 gci = signal->theData[1];
+ ndbrequire(gci == cnewgcp);
+ receiveLoopMacro(GCP_PREPARE, senderNodeId);
+ //-------------------------------------------------------------
+ // We have now received all replies. We are ready to continue
+ // with committing the global checkpoint.
+ //-------------------------------------------------------------
+ gcpcommitreqLab(signal);
+}//Dbdih::execGCP_PREPARECONF()
+
+void Dbdih::gcpcommitreqLab(Signal* signal)
+{
+ CRASH_INSERTION(7001);
+ sendLoopMacro(GCP_COMMIT, sendGCP_COMMIT);
+ cgcpStatus = GCP_COMMIT_SENT;
+ return;
+}//Dbdih::gcpcommitreqLab()
+
+void Dbdih::execGCP_NODEFINISH(Signal* signal)
+{
+ jamEntry();
+ const Uint32 senderNodeId = signal->theData[0];
+ const Uint32 gci = signal->theData[1];
+ const Uint32 failureNr = signal->theData[2];
+ if (!isMaster()) {
+ jam();
+ ndbrequire(failureNr > cfailurenr);
+ //-------------------------------------------------------------
+ // Another node thinks we are master. This could happen when he
+ // has heard of a node failure which I have not heard of. Ignore
+ // signal in this case since we will discover it by sending
+ // MASTER_GCPREQ to the node.
+ //-------------------------------------------------------------
+ return;
+ } else if (cmasterState == MASTER_TAKE_OVER_GCP) {
+ jam();
+ //-------------------------------------------------------------
+ // We are currently taking over as master. We will delay the
+ // signal until we have completed the take over gcp handling.
+ //-------------------------------------------------------------
+ sendSignalWithDelay(reference(), GSN_GCP_NODEFINISH, signal, 20, 3);
+ return;
+ } else {
+ ndbrequire(cmasterState == MASTER_ACTIVE);
+ }//if
+ ndbrequire(gci == coldgcp);
+ receiveLoopMacro(GCP_COMMIT, senderNodeId);
+ //-------------------------------------------------------------
+ // We have now received all replies. We are ready to continue
+ // with saving the global checkpoint to disk.
+ //-------------------------------------------------------------
+ CRASH_INSERTION(7002);
+ gcpsavereqLab(signal);
+ return;
+}//Dbdih::execGCP_NODEFINISH()
+
+void Dbdih::gcpsavereqLab(Signal* signal)
+{
+ sendLoopMacro(GCP_SAVEREQ, sendGCP_SAVEREQ);
+ cgcpStatus = GCP_NODE_FINISHED;
+}//Dbdih::gcpsavereqLab()
+
+void Dbdih::execGCP_SAVECONF(Signal* signal)
+{
+ jamEntry();
+ const GCPSaveConf * const saveConf = (GCPSaveConf*)&signal->theData[0];
+ ndbrequire(saveConf->gci == coldgcp);
+ ndbrequire(saveConf->nodeId == saveConf->dihPtr);
+ SYSFILE->lastCompletedGCI[saveConf->nodeId] = saveConf->gci;
+ GCP_SAVEhandling(signal, saveConf->nodeId);
+}//Dbdih::execGCP_SAVECONF()
+
+void Dbdih::execGCP_SAVEREF(Signal* signal)
+{
+ jamEntry();
+ const GCPSaveRef * const saveRef = (GCPSaveRef*)&signal->theData[0];
+ ndbrequire(saveRef->gci == coldgcp);
+ ndbrequire(saveRef->nodeId == saveRef->dihPtr);
+ /**
+ * Only allow reason not to save
+ */
+ ndbrequire(saveRef->errorCode == GCPSaveRef::NodeShutdownInProgress ||
+ saveRef->errorCode == GCPSaveRef::FakedSignalDueToNodeFailure ||
+ saveRef->errorCode == GCPSaveRef::NodeRestartInProgress);
+ GCP_SAVEhandling(signal, saveRef->nodeId);
+}//Dbdih::execGCP_SAVEREF()
+
+void Dbdih::GCP_SAVEhandling(Signal* signal, Uint32 nodeId)
+{
+ receiveLoopMacro(GCP_SAVEREQ, nodeId);
+ /*-------------------------------------------------------------------------*/
+ // All nodes have replied. We are ready to update the system file.
+ /*-------------------------------------------------------------------------*/
+ cgcpStatus = GCP_SAVE_LQH_FINISHED;
+ CRASH_INSERTION(7003);
+ checkToCopy();
+ /**------------------------------------------------------------------------
+ * SET NEW RECOVERABLE GCI. ALSO RESET RESTART COUNTER TO ZERO.
+ * THIS INDICATES THAT THE SYSTEM HAS BEEN RECOVERED AND SURVIVED AT
+ * LEAST ONE GLOBAL CHECKPOINT PERIOD. WE WILL USE THIS PARAMETER TO
+ * SET BACK THE RESTART GCI IF WE ENCOUNTER MORE THAN ONE UNSUCCESSFUL
+ * RESTART.
+ *------------------------------------------------------------------------*/
+ SYSFILE->newestRestorableGCI = coldgcp;
+ if(Sysfile::getInitialStartOngoing(SYSFILE->systemRestartBits) &&
+ getNodeState().startLevel == NodeState::SL_STARTED){
+ jam();
+#if 0
+ ndbout_c("Dbdih: Clearing initial start ongoing");
+#endif
+ Sysfile::clearInitialStartOngoing(SYSFILE->systemRestartBits);
+ }
+ copyGciLab(signal, CopyGCIReq::GLOBAL_CHECKPOINT);
+}//Dbdih::GCP_SAVEhandling()
+
+/*
+ 3.11 G L O B A L C H E C K P O I N T (N O T - M A S T E R)
+ *************************************************************
+ */
+void Dbdih::execGCP_PREPARE(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(7005);
+ Uint32 masterNodeId = signal->theData[0];
+ Uint32 gci = signal->theData[1];
+ BlockReference retRef = calcDihBlockRef(masterNodeId);
+
+ ndbrequire (cmasterdihref == retRef);
+ ndbrequire (cgcpParticipantState == GCP_PARTICIPANT_READY);
+ ndbrequire (gci == (currentgcp + 1));
+
+ cgckptflag = true;
+ cgcpParticipantState = GCP_PARTICIPANT_PREPARE_RECEIVED;
+ cnewgcp = gci;
+
+ signal->theData[0] = cownNodeId;
+ signal->theData[1] = gci;
+ sendSignal(retRef, GSN_GCP_PREPARECONF, signal, 2, JBA);
+ return;
+}//Dbdih::execGCP_PREPARE()
+
+void Dbdih::execGCP_COMMIT(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(7006);
+ Uint32 masterNodeId = signal->theData[0];
+ Uint32 gci = signal->theData[1];
+
+ ndbrequire(gci == (currentgcp + 1));
+ ndbrequire(masterNodeId = cmasterNodeId);
+ ndbrequire(cgcpParticipantState == GCP_PARTICIPANT_PREPARE_RECEIVED);
+
+ coldgcp = currentgcp;
+ currentgcp = cnewgcp;
+ cgckptflag = false;
+ emptyverificbuffer(signal, true);
+ cgcpParticipantState = GCP_PARTICIPANT_COMMIT_RECEIVED;
+ signal->theData[1] = coldgcp;
+ sendSignal(clocaltcblockref, GSN_GCP_NOMORETRANS, signal, 2, JBB);
+ return;
+}//Dbdih::execGCP_COMMIT()
+
+void Dbdih::execGCP_TCFINISHED(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(7007);
+ Uint32 gci = signal->theData[1];
+ ndbrequire(gci == coldgcp);
+
+ cgcpParticipantState = GCP_PARTICIPANT_TC_FINISHED;
+ signal->theData[0] = cownNodeId;
+ signal->theData[1] = coldgcp;
+ signal->theData[2] = cfailurenr;
+ sendSignal(cmasterdihref, GSN_GCP_NODEFINISH, signal, 3, JBB);
+}//Dbdih::execGCP_TCFINISHED()
+
+/*****************************************************************************/
+//****** RECEIVING TAMPER REQUEST FROM NDBAPI ******
+/*****************************************************************************/
+void Dbdih::execDIHNDBTAMPER(Signal* signal)
+{
+ jamEntry();
+ Uint32 tcgcpblocked = signal->theData[0];
+ /* ACTION TO BE TAKEN BY DIH */
+ Uint32 tuserpointer = signal->theData[1];
+ BlockReference tuserblockref = signal->theData[2];
+ switch (tcgcpblocked) {
+ case 1:
+ jam();
+ if (isMaster()) {
+ jam();
+ cgcpOrderBlocked = 1;
+ } else {
+ jam();
+ /* TRANSFER THE REQUEST */
+ /* TO MASTER*/
+ signal->theData[0] = tcgcpblocked;
+ signal->theData[1] = tuserpointer;
+ signal->theData[2] = tuserblockref;
+ sendSignal(cmasterdihref, GSN_DIHNDBTAMPER, signal, 3, JBB);
+ }//if
+ break;
+ case 2:
+ jam();
+ if (isMaster()) {
+ jam();
+ cgcpOrderBlocked = 0;
+ } else {
+ jam();
+ /* TRANSFER THE REQUEST */
+ /* TO MASTER*/
+ signal->theData[0] = tcgcpblocked;
+ signal->theData[1] = tuserpointer;
+ signal->theData[2] = tuserblockref;
+ sendSignal(cmasterdihref, GSN_DIHNDBTAMPER, signal, 3, JBB);
+ }//if
+ break;
+ case 3:
+ ndbrequire(false);
+ return;
+ break;
+ case 4:
+ jam();
+ signal->theData[0] = tuserpointer;
+ signal->theData[1] = crestartGci;
+ sendSignal(tuserblockref, GSN_DIHNDBTAMPER, signal, 2, JBB);
+ break;
+#ifdef ERROR_INSERT
+ case 5:
+ jam();
+ if(tuserpointer == 0)
+ {
+ jam();
+ signal->theData[0] = 0;
+ sendSignal(QMGR_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(NDBCNTR_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(NDBFS_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBACC_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBTUP_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBLQH_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBDICT_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBDIH_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBTC_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(CMVMI_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ return;
+ }
+ /*----------------------------------------------------------------------*/
+ // Insert errors.
+ /*----------------------------------------------------------------------*/
+ if (tuserpointer < 1000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into QMGR.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = QMGR_REF;
+ } else if (tuserpointer < 2000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into NDBCNTR.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = NDBCNTR_REF;
+ } else if (tuserpointer < 3000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into NDBFS.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = NDBFS_REF;
+ } else if (tuserpointer < 4000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into DBACC.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = DBACC_REF;
+ } else if (tuserpointer < 5000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into DBTUP.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = DBTUP_REF;
+ } else if (tuserpointer < 6000) {
+ /*---------------------------------------------------------------------*/
+ // Insert errors into DBLQH.
+ /*---------------------------------------------------------------------*/
+ jam();
+ tuserblockref = DBLQH_REF;
+ } else if (tuserpointer < 7000) {
+ /*---------------------------------------------------------------------*/
+ // Insert errors into DBDICT.
+ /*---------------------------------------------------------------------*/
+ jam();
+ tuserblockref = DBDICT_REF;
+ } else if (tuserpointer < 8000) {
+ /*---------------------------------------------------------------------*/
+ // Insert errors into DBDIH.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = DBDIH_REF;
+ } else if (tuserpointer < 9000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into DBTC.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = DBTC_REF;
+ } else if (tuserpointer < 10000) {
+ /*--------------------------------------------------------------------*/
+ // Insert errors into CMVMI.
+ /*--------------------------------------------------------------------*/
+ jam();
+ tuserblockref = CMVMI_REF;
+ } else if (tuserpointer < 11000) {
+ jam();
+ tuserblockref = BACKUP_REF;
+ } else if (tuserpointer < 12000) {
+ // DBUTIL_REF ?
+ jam();
+ } else if (tuserpointer < 13000) {
+ jam();
+ tuserblockref = DBTUX_REF;
+ } else if (tuserpointer < 14000) {
+ jam();
+ tuserblockref = SUMA_REF;
+ } else if (tuserpointer < 15000) {
+ jam();
+ tuserblockref = DBDICT_REF;
+ } else if (tuserpointer < 30000) {
+ /*--------------------------------------------------------------------*/
+ // Ignore errors in the 20000-range.
+ /*--------------------------------------------------------------------*/
+ jam();
+ return;
+ } else if (tuserpointer < 40000) {
+ jam();
+ /*--------------------------------------------------------------------*/
+ // Redirect errors to master DIH in the 30000-range.
+ /*--------------------------------------------------------------------*/
+ tuserblockref = cmasterdihref;
+ tuserpointer -= 30000;
+ signal->theData[0] = 5;
+ signal->theData[1] = tuserpointer;
+ signal->theData[2] = tuserblockref;
+ sendSignal(tuserblockref, GSN_DIHNDBTAMPER, signal, 3, JBB);
+ return;
+ } else if (tuserpointer < 50000) {
+ NodeRecordPtr localNodeptr;
+ Uint32 Tfound = 0;
+ jam();
+ /*--------------------------------------------------------------------*/
+ // Redirect errors to non-master DIH in the 40000-range.
+ /*--------------------------------------------------------------------*/
+ tuserpointer -= 40000;
+ for (localNodeptr.i = 1;
+ localNodeptr.i < MAX_NDB_NODES;
+ localNodeptr.i++) {
+ jam();
+ ptrAss(localNodeptr, nodeRecord);
+ if ((localNodeptr.p->nodeStatus == NodeRecord::ALIVE) &&
+ (localNodeptr.i != cmasterNodeId)) {
+ jam();
+ tuserblockref = calcDihBlockRef(localNodeptr.i);
+ Tfound = 1;
+ break;
+ }//if
+ }//for
+ if (Tfound == 0) {
+ jam();
+ /*-------------------------------------------------------------------*/
+ // Ignore since no non-master node existed.
+ /*-------------------------------------------------------------------*/
+ return;
+ }//if
+ signal->theData[0] = 5;
+ signal->theData[1] = tuserpointer;
+ signal->theData[2] = tuserblockref;
+ sendSignal(tuserblockref, GSN_DIHNDBTAMPER, signal, 3, JBB);
+ return;
+ } else {
+ jam();
+ return;
+ }//if
+ signal->theData[0] = tuserpointer;
+ if (tuserpointer != 0) {
+ sendSignal(tuserblockref, GSN_NDB_TAMPER, signal, 1, JBB);
+ } else {
+ sendSignal(QMGR_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(NDBCNTR_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(NDBFS_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBACC_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBTUP_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBLQH_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBDICT_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBDIH_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(DBTC_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ sendSignal(CMVMI_REF, GSN_NDB_TAMPER, signal, 1, JBB);
+ }//if
+ break;
+#endif
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbdih::execDIHNDBTAMPER()
+
+/*****************************************************************************/
+/* ********** FILE HANDLING MODULE *************/
+/*****************************************************************************/
+void Dbdih::copyGciLab(Signal* signal, CopyGCIReq::CopyReason reason)
+{
+ if(c_copyGCIMaster.m_copyReason != CopyGCIReq::IDLE){
+ /**
+ * There can currently only be one waiting
+ */
+ ndbrequire(c_copyGCIMaster.m_waiting == CopyGCIReq::IDLE);
+ c_copyGCIMaster.m_waiting = reason;
+ return;
+ }
+ c_copyGCIMaster.m_copyReason = reason;
+ sendLoopMacro(COPY_GCIREQ, sendCOPY_GCIREQ);
+
+}//Dbdih::copyGciLab()
+
+/* ------------------------------------------------------------------------- */
+/* COPY_GCICONF RESPONSE TO COPY_GCIREQ */
+/* ------------------------------------------------------------------------- */
+void Dbdih::execCOPY_GCICONF(Signal* signal)
+{
+ jamEntry();
+ NodeRecordPtr senderNodePtr;
+ senderNodePtr.i = signal->theData[0];
+ receiveLoopMacro(COPY_GCIREQ, senderNodePtr.i);
+
+ CopyGCIReq::CopyReason waiting = c_copyGCIMaster.m_waiting;
+ CopyGCIReq::CopyReason current = c_copyGCIMaster.m_copyReason;
+
+ c_copyGCIMaster.m_copyReason = CopyGCIReq::IDLE;
+ c_copyGCIMaster.m_waiting = CopyGCIReq::IDLE;
+
+ bool ok = false;
+ switch(current){
+ case CopyGCIReq::RESTART:{
+ ok = true;
+ jam();
+ DictStartReq * req = (DictStartReq*)&signal->theData[0];
+ req->restartGci = SYSFILE->newestRestorableGCI;
+ req->senderRef = reference();
+ sendSignal(cdictblockref, GSN_DICTSTARTREQ,
+ signal, DictStartReq::SignalLength, JBB);
+ break;
+ }
+ case CopyGCIReq::LOCAL_CHECKPOINT:{
+ ok = true;
+ jam();
+ startLcpRoundLab(signal);
+ break;
+ }
+ case CopyGCIReq::GLOBAL_CHECKPOINT:
+ ok = true;
+ jam();
+ checkToCopyCompleted(signal);
+
+ /************************************************************************/
+ // Report the event that a global checkpoint has completed.
+ /************************************************************************/
+ signal->setTrace(0);
+ signal->theData[0] = NDB_LE_GlobalCheckpointCompleted; //Event type
+ signal->theData[1] = coldgcp;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ CRASH_INSERTION(7004);
+ emptyWaitGCPMasterQueue(signal);
+ cgcpStatus = GCP_READY;
+ signal->theData[0] = DihContinueB::ZSTART_GCP;
+ signal->theData[1] = cgcpDelay;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 2);
+ if (c_nodeStartMaster.blockGcp == true) {
+ jam();
+ /* ------------------------------------------------------------------ */
+ /* A NEW NODE WANTS IN AND WE MUST ALLOW IT TO COME IN NOW SINCE THE */
+ /* GCP IS COMPLETED. */
+ /* ------------------------------------------------------------------ */
+ gcpBlockedLab(signal);
+ }//if
+ break;
+ case CopyGCIReq::INITIAL_START_COMPLETED:
+ ok = true;
+ jam();
+ initialStartCompletedLab(signal);
+ break;
+ case CopyGCIReq::IDLE:
+ ok = false;
+ jam();
+ }
+ ndbrequire(ok);
+
+ /**
+ * Pop queue
+ */
+ if(waiting != CopyGCIReq::IDLE){
+ c_copyGCIMaster.m_copyReason = waiting;
+ signal->theData[0] = DihContinueB::ZCOPY_GCI;
+ signal->theData[1] = waiting;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ }
+}//Dbdih::execCOPY_GCICONF()
+
+void Dbdih::invalidateLcpInfoAfterSr()
+{
+ NodeRecordPtr nodePtr;
+ SYSFILE->latestLCP_ID--;
+ Sysfile::clearLCPOngoing(SYSFILE->systemRestartBits);
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (!NdbNodeBitmask::get(SYSFILE->lcpActive, nodePtr.i)){
+ jam();
+ /* ------------------------------------------------------------------- */
+ // The node was not active in the local checkpoint.
+ // To avoid that we step the active status too fast to not
+ // active we step back one step from Sysfile::NS_ActiveMissed_x.
+ /* ------------------------------------------------------------------- */
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ /* ----------------------------------------------------------------- */
+ // When not active in ongoing LCP and still active is a contradiction.
+ /* ----------------------------------------------------------------- */
+ ndbrequire(false);
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ nodePtr.p->activeStatus = Sysfile::NS_Active;
+ break;
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ nodePtr.p->activeStatus = Sysfile::NS_ActiveMissed_1;
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ }//if
+ }//for
+ setNodeRestartInfoBits();
+}//Dbdih::invalidateLcpInfoAfterSr()
+
+/* ------------------------------------------------------------------------- */
+/* THE NEXT STEP IS TO WRITE THE FILE. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::openingCopyGciSkipInitLab(Signal* signal, FileRecordPtr filePtr)
+{
+ writeRestorableGci(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::WRITING_COPY_GCI;
+ return;
+}//Dbdih::openingCopyGciSkipInitLab()
+
+void Dbdih::writingCopyGciLab(Signal* signal, FileRecordPtr filePtr)
+{
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE NOW WRITTEN THIS FILE. WRITE ALSO NEXT FILE IF THIS IS NOT */
+ /* ALREADY THE LAST. */
+ /* ----------------------------------------------------------------------- */
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ if (filePtr.i == crestartInfoFile[0]) {
+ jam();
+ filePtr.i = crestartInfoFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ if (filePtr.p->fileStatus == FileRecord::OPEN) {
+ jam();
+ openingCopyGciSkipInitLab(signal, filePtr);
+ return;
+ }//if
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_COPY_GCI;
+ return;
+ }//if
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE COMPLETED WRITING BOTH FILES SUCCESSFULLY. NOW REPORT OUR */
+ /* SUCCESS TO THE MASTER DIH. BUT FIRST WE NEED TO RESET A NUMBER OF */
+ /* VARIABLES USED BY THE LOCAL CHECKPOINT PROCESS (ONLY IF TRIGGERED */
+ /* BY LOCAL CHECKPOINT PROCESS. */
+ /* ----------------------------------------------------------------------- */
+ CopyGCIReq::CopyReason reason = c_copyGCISlave.m_copyReason;
+
+ if (reason == CopyGCIReq::GLOBAL_CHECKPOINT) {
+ jam();
+ cgcpParticipantState = GCP_PARTICIPANT_READY;
+
+ SubGcpCompleteRep * const rep = (SubGcpCompleteRep*)signal->getDataPtr();
+ rep->gci = coldgcp;
+ rep->senderData = 0;
+ sendSignal(SUMA_REF, GSN_SUB_GCP_COMPLETE_REP, signal,
+ SubGcpCompleteRep::SignalLength, JBB);
+ }
+
+ jam();
+ c_copyGCISlave.m_copyReason = CopyGCIReq::IDLE;
+
+ if(c_copyGCISlave.m_senderRef == cmasterdihref){
+ jam();
+ /**
+ * Only if same master
+ */
+ signal->theData[0] = c_copyGCISlave.m_senderData;
+ sendSignal(c_copyGCISlave.m_senderRef, GSN_COPY_GCICONF, signal, 1, JBB);
+
+ }
+ return;
+}//Dbdih::writingCopyGciLab()
+
+void Dbdih::execSTART_LCP_REQ(Signal* signal){
+ StartLcpReq * req = (StartLcpReq*)signal->getDataPtr();
+
+ CRASH_INSERTION2(7021, isMaster());
+ CRASH_INSERTION2(7022, !isMaster());
+
+ ndbrequire(c_lcpState.m_masterLcpDihRef = req->senderRef);
+ c_lcpState.m_participatingDIH = req->participatingDIH;
+ c_lcpState.m_participatingLQH = req->participatingLQH;
+
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH = req->participatingLQH;
+ if(isMaster()){
+ jam();
+ ndbrequire(isActiveMaster());
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH = req->participatingDIH;
+
+ } else {
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH.clearWaitingFor();
+ }
+
+ c_lcpState.m_LCP_COMPLETE_REP_From_Master_Received = false;
+
+ c_lcpState.setLcpStatus(LCP_INIT_TABLES, __LINE__);
+
+ signal->theData[0] = DihContinueB::ZINIT_LCP;
+ signal->theData[1] = c_lcpState.m_masterLcpDihRef;
+ signal->theData[2] = 0;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+}
+
+void Dbdih::initLcpLab(Signal* signal, Uint32 senderRef, Uint32 tableId)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+
+ if(c_lcpState.m_masterLcpDihRef != senderRef){
+ jam();
+ /**
+ * This is LCP master takeover
+ */
+#ifdef VM_TRACE
+ ndbout_c("initLcpLab aborted due to LCP master takeover - 1");
+#endif
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+ sendMASTER_LCPCONF(signal);
+ return;
+ }
+
+ if(c_lcpState.m_masterLcpDihRef != cmasterdihref){
+ jam();
+ /**
+ * Master take over but has not yet received MASTER_LCPREQ
+ */
+#ifdef VM_TRACE
+ ndbout_c("initLcpLab aborted due to LCP master takeover - 2");
+#endif
+ return;
+ }
+
+ //const Uint32 lcpId = SYSFILE->latestLCP_ID;
+
+ for(; tabPtr.i < ctabFileSize; tabPtr.i++){
+
+ ptrAss(tabPtr, tabRecord);
+
+ if (tabPtr.p->tabStatus != TabRecord::TS_ACTIVE) {
+ jam();
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_COMPLETED;
+ continue;
+ }
+
+ if (tabPtr.p->storedTable == 0) {
+ /**
+ * Temporary table
+ */
+ jam();
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_COMPLETED;
+ continue;
+ }
+
+ if (tabPtr.p->tabCopyStatus != TabRecord::CS_IDLE) {
+ /* ----------------------------------------------------------------- */
+ // We protect the updates of table data structures by this variable.
+ /* ----------------------------------------------------------------- */
+ jam();
+ signal->theData[0] = DihContinueB::ZINIT_LCP;
+ signal->theData[1] = senderRef;
+ signal->theData[2] = tabPtr.i;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 20, 3);
+ return;
+ }//if
+
+ /**
+ * Found a table
+ */
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_ACTIVE;
+
+ /**
+ * For each fragment
+ */
+ for (Uint32 fragId = 0; fragId < tabPtr.p->totalfragments; fragId++) {
+ jam();
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+
+ /**
+ * For each of replica record
+ */
+ Uint32 replicaCount = 0;
+ ReplicaRecordPtr replicaPtr;
+ for(replicaPtr.i = fragPtr.p->storedReplicas; replicaPtr.i != RNIL;
+ replicaPtr.i = replicaPtr.p->nextReplica) {
+ jam();
+
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ Uint32 nodeId = replicaPtr.p->procNode;
+ if(c_lcpState.m_participatingLQH.get(nodeId)){
+ jam();
+ replicaCount++;
+ replicaPtr.p->lcpOngoingFlag = true;
+ }
+ }
+
+ fragPtr.p->noLcpReplicas = replicaCount;
+ }//for
+
+ signal->theData[0] = DihContinueB::ZINIT_LCP;
+ signal->theData[1] = senderRef;
+ signal->theData[2] = tabPtr.i + 1;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }
+
+ /**
+ * No more tables
+ */
+ jam();
+
+ if (c_lcpState.m_masterLcpDihRef != reference()){
+ jam();
+ ndbrequire(!isMaster());
+ c_lcpState.setLcpStatus(LCP_STATUS_ACTIVE, __LINE__);
+ } else {
+ jam();
+ ndbrequire(isMaster());
+ }
+
+ CRASH_INSERTION2(7023, isMaster());
+ CRASH_INSERTION2(7024, !isMaster());
+
+ jam();
+ StartLcpConf * conf = (StartLcpConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ sendSignal(c_lcpState.m_masterLcpDihRef, GSN_START_LCP_CONF, signal,
+ StartLcpConf::SignalLength, JBB);
+ return;
+}//Dbdih::initLcpLab()
+
+/* ------------------------------------------------------------------------- */
+/* ERROR HANDLING FOR COPY RESTORABLE GCI FILE. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::openingCopyGciErrorLab(Signal* signal, FileRecordPtr filePtr)
+{
+ createFileRw(signal, filePtr);
+ /* ------------------------------------------------------------------------- */
+ /* ERROR IN OPENING FILE. WE WILL TRY BY CREATING FILE INSTEAD. */
+ /* ------------------------------------------------------------------------- */
+ filePtr.p->reqStatus = FileRecord::CREATING_COPY_GCI;
+ return;
+}//Dbdih::openingCopyGciErrorLab()
+
+/* ------------------------------------------------------------------------- */
+/* ENTER DICTSTARTCONF WITH */
+/* TBLOCKREF */
+/* ------------------------------------------------------------------------- */
+void Dbdih::dictStartConfLab(Signal* signal)
+{
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE NOW RECEIVED ALL THE TABLES TO RESTART. */
+ /* ----------------------------------------------------------------------- */
+ signal->theData[0] = DihContinueB::ZSTART_FRAGMENT;
+ signal->theData[1] = 0; /* START WITH TABLE 0 */
+ signal->theData[2] = 0; /* AND FRAGMENT 0 */
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+}//Dbdih::dictStartConfLab()
+
+
+void Dbdih::openingTableLab(Signal* signal, FileRecordPtr filePtr)
+{
+ /* ---------------------------------------------------------------------- */
+ /* SUCCESSFULLY OPENED A FILE. READ THE FIRST PAGE OF THIS FILE. */
+ /* ---------------------------------------------------------------------- */
+ TabRecordPtr tabPtr;
+ PageRecordPtr pagePtr;
+
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tabPtr.p->noPages = 1;
+ allocpage(pagePtr);
+ tabPtr.p->pageRef[0] = pagePtr.i;
+ readTabfile(signal, tabPtr.p, filePtr);
+ filePtr.p->reqStatus = FileRecord::READING_TABLE;
+ return;
+}//Dbdih::openingTableLab()
+
+void Dbdih::openingTableErrorLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ /* ---------------------------------------------------------------------- */
+ /* WE FAILED IN OPENING A FILE. IF THE FIRST FILE THEN TRY WITH THE */
+ /* DUPLICATE FILE, OTHERWISE WE REPORT AN ERROR IN THE SYSTEM RESTART. */
+ /* ---------------------------------------------------------------------- */
+ ndbrequire(filePtr.i == tabPtr.p->tabFile[0]);
+ filePtr.i = tabPtr.p->tabFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_TABLE;
+}//Dbdih::openingTableErrorLab()
+
+void Dbdih::readingTableLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ PageRecordPtr pagePtr;
+ /* ---------------------------------------------------------------------- */
+ /* WE HAVE SUCCESSFULLY READ A NUMBER OF PAGES IN THE TABLE FILE. IF */
+ /* MORE PAGES EXIST IN THE FILE THEN READ ALL PAGES IN THE FILE. */
+ /* ---------------------------------------------------------------------- */
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ pagePtr.i = tabPtr.p->pageRef[0];
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+ Uint32 noOfStoredPages = pagePtr.p->word[33];
+ if (tabPtr.p->noPages < noOfStoredPages) {
+ jam();
+ ndbrequire(noOfStoredPages <= 8);
+ for (Uint32 i = tabPtr.p->noPages; i < noOfStoredPages; i++) {
+ jam();
+ allocpage(pagePtr);
+ tabPtr.p->pageRef[i] = pagePtr.i;
+ }//for
+ tabPtr.p->noPages = noOfStoredPages;
+ readTabfile(signal, tabPtr.p, filePtr);
+ filePtr.p->reqStatus = FileRecord::READING_TABLE;
+ } else {
+ ndbrequire(tabPtr.p->noPages == pagePtr.p->word[33]);
+ ndbrequire(tabPtr.p->tabCopyStatus == TabRecord::CS_IDLE);
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* WE HAVE READ ALL PAGES. NOW READ FROM PAGES INTO TABLE AND FRAGMENT */
+ /* DATA STRUCTURES. */
+ /* --------------------------------------------------------------------- */
+ tabPtr.p->tabCopyStatus = TabRecord::CS_SR_PHASE1_READ_PAGES;
+ signal->theData[0] = DihContinueB::ZREAD_PAGES_INTO_TABLE;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+ return;
+}//Dbdih::readingTableLab()
+
+void Dbdih::readTableFromPagesLab(Signal* signal, TabRecordPtr tabPtr)
+{
+ FileRecordPtr filePtr;
+ filePtr.i = tabPtr.p->tabFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ /* ---------------------------------------------------------------------- */
+ /* WE HAVE NOW COPIED TO OUR NODE. WE HAVE NOW COMPLETED RESTORING */
+ /* THIS TABLE. CONTINUE WITH THE NEXT TABLE. */
+ /* WE ALSO NEED TO CLOSE THE TABLE FILE. */
+ /* ---------------------------------------------------------------------- */
+ if (filePtr.p->fileStatus != FileRecord::OPEN) {
+ jam();
+ filePtr.i = tabPtr.p->tabFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ }//if
+ closeFile(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::CLOSING_TABLE_SR;
+ return;
+}//Dbdih::readTableFromPagesLab()
+
+void Dbdih::closingTableSrLab(Signal* signal, FileRecordPtr filePtr)
+{
+ /**
+ * Update table/fragment info
+ */
+ TabRecordPtr tabPtr;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ resetReplicaSr(tabPtr);
+
+ signal->theData[0] = DihContinueB::ZCOPY_TABLE;
+ signal->theData[1] = filePtr.p->tabRef;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+
+ return;
+}//Dbdih::closingTableSrLab()
+
+void
+Dbdih::resetReplicaSr(TabRecordPtr tabPtr){
+
+ const Uint32 newestRestorableGCI = SYSFILE->newestRestorableGCI;
+
+ for(Uint32 i = 0; i<tabPtr.p->totalfragments; i++){
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, i, fragPtr);
+
+ /**
+ * 1) Start by moving all replicas into oldStoredReplicas
+ */
+ prepareReplicas(fragPtr);
+
+ /**
+ * 2) Move all "alive" replicas into storedReplicas
+ * + update noCrashedReplicas...
+ */
+ ReplicaRecordPtr replicaPtr;
+ replicaPtr.i = fragPtr.p->oldStoredReplicas;
+ while (replicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ const Uint32 nextReplicaPtrI = replicaPtr.p->nextReplica;
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = replicaPtr.p->procNode;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ const Uint32 noCrashedReplicas = replicaPtr.p->noCrashedReplicas;
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:{
+ jam();
+ /* --------------------------------------------------------------- */
+ /* THE NODE IS ALIVE AND KICKING AND ACTIVE, LET'S USE IT. */
+ /* --------------------------------------------------------------- */
+ arrGuard(noCrashedReplicas, 8);
+ Uint32 lastGci = replicaPtr.p->replicaLastGci[noCrashedReplicas];
+ if(lastGci >= newestRestorableGCI){
+ jam();
+ /** -------------------------------------------------------------
+ * THE REPLICA WAS ALIVE AT THE SYSTEM FAILURE. WE WILL SET THE
+ * LAST REPLICA GCI TO MINUS ONE SINCE IT HASN'T FAILED YET IN THE
+ * NEW SYSTEM.
+ *-------------------------------------------------------------- */
+ replicaPtr.p->replicaLastGci[noCrashedReplicas] = (Uint32)-1;
+ } else {
+ jam();
+ /*--------------------------------------------------------------
+ * SINCE IT WAS NOT ALIVE AT THE TIME OF THE SYSTEM CRASH THIS IS
+ * A COMPLETELY NEW REPLICA. WE WILL SET THE CREATE GCI TO BE THE
+ * NEXT GCI TO BE EXECUTED.
+ *--------_----------------------------------------------------- */
+ const Uint32 nextCrashed = noCrashedReplicas + 1;
+ replicaPtr.p->noCrashedReplicas = nextCrashed;
+ arrGuard(nextCrashed, 8);
+ replicaPtr.p->createGci[nextCrashed] = newestRestorableGCI + 1;
+ ndbrequire(newestRestorableGCI + 1 != 0xF1F1F1F1);
+ replicaPtr.p->replicaLastGci[nextCrashed] = (Uint32)-1;
+ }//if
+
+ resetReplicaLcp(replicaPtr.p, newestRestorableGCI);
+
+ /* -----------------------------------------------------------------
+ * LINK THE REPLICA INTO THE STORED REPLICA LIST. WE WILL USE THIS
+ * NODE AS A STORED REPLICA.
+ * WE MUST FIRST LINK IT OUT OF THE LIST OF OLD STORED REPLICAS.
+ * --------------------------------------------------------------- */
+ removeOldStoredReplica(fragPtr, replicaPtr);
+ linkStoredReplica(fragPtr, replicaPtr);
+
+ }
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }
+ }
+ replicaPtr.i = nextReplicaPtrI;
+ }//while
+ }
+}
+
+void
+Dbdih::resetReplicaLcp(ReplicaRecord * replicaP, Uint32 stopGci){
+
+ Uint32 lcpNo = replicaP->nextLcp;
+ const Uint32 startLcpNo = lcpNo;
+ do {
+ lcpNo = prevLcpNo(lcpNo);
+ ndbrequire(lcpNo < MAX_LCP_STORED);
+ if (replicaP->lcpStatus[lcpNo] == ZVALID) {
+ if (replicaP->maxGciStarted[lcpNo] < stopGci) {
+ jam();
+ /* ----------------------------------------------------------------- */
+ /* WE HAVE FOUND A USEFUL LOCAL CHECKPOINT THAT CAN BE USED FOR */
+ /* RESTARTING THIS FRAGMENT REPLICA. */
+ /* ----------------------------------------------------------------- */
+ return ;
+ }//if
+ }//if
+
+ /**
+ * WE COULD NOT USE THIS LOCAL CHECKPOINT. IT WAS TOO
+ * RECENT OR SIMPLY NOT A VALID CHECKPOINT.
+ * WE SHOULD THUS REMOVE THIS LOCAL CHECKPOINT SINCE IT WILL NEVER
+ * AGAIN BE USED. SET LCP_STATUS TO INVALID.
+ */
+ replicaP->nextLcp = lcpNo;
+ replicaP->lcpId[lcpNo] = 0;
+ replicaP->lcpStatus[lcpNo] = ZINVALID;
+ } while (lcpNo != startLcpNo);
+
+ replicaP->nextLcp = 0;
+}
+
+void Dbdih::readingTableErrorLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ /* ---------------------------------------------------------------------- */
+ /* READING THIS FILE FAILED. CLOSE IT AFTER RELEASING ALL PAGES. */
+ /* ---------------------------------------------------------------------- */
+ ndbrequire(tabPtr.p->noPages <= 8);
+ for (Uint32 i = 0; i < tabPtr.p->noPages; i++) {
+ jam();
+ releasePage(tabPtr.p->pageRef[i]);
+ }//for
+ closeFile(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::CLOSING_TABLE_CRASH;
+ return;
+}//Dbdih::readingTableErrorLab()
+
+void Dbdih::closingTableCrashLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ /* ---------------------------------------------------------------------- */
+ /* WE HAVE NOW CLOSED A FILE WHICH WE HAD A READ ERROR WITH. PROCEED */
+ /* WITH NEXT FILE IF NOT THE LAST OTHERWISE REPORT ERROR. */
+ /* ---------------------------------------------------------------------- */
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ ndbrequire(filePtr.i == tabPtr.p->tabFile[0]);
+ filePtr.i = tabPtr.p->tabFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ openFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::OPENING_TABLE;
+}//Dbdih::closingTableCrashLab()
+
+/*****************************************************************************/
+/* ********** COPY TABLE MODULE *************/
+/*****************************************************************************/
+void Dbdih::execCOPY_TABREQ(Signal* signal)
+{
+ CRASH_INSERTION(7172);
+
+ TabRecordPtr tabPtr;
+ PageRecordPtr pagePtr;
+ jamEntry();
+ BlockReference ref = signal->theData[0];
+ Uint32 reqinfo = signal->theData[1];
+ tabPtr.i = signal->theData[2];
+ Uint32 schemaVersion = signal->theData[3];
+ Uint32 noOfWords = signal->theData[4];
+ ndbrequire(ref == cmasterdihref);
+ ndbrequire(!isMaster());
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if (reqinfo == 1) {
+ jam();
+ tabPtr.p->schemaVersion = schemaVersion;
+ initTableFile(tabPtr);
+ }//if
+ ndbrequire(tabPtr.p->noPages < 8);
+ if (tabPtr.p->noOfWords == 0) {
+ jam();
+ allocpage(pagePtr);
+ tabPtr.p->pageRef[tabPtr.p->noPages] = pagePtr.i;
+ tabPtr.p->noPages++;
+ } else {
+ jam();
+ pagePtr.i = tabPtr.p->pageRef[tabPtr.p->noPages - 1];
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+ }//if
+ ndbrequire(tabPtr.p->noOfWords + 15 < 2048);
+ ndbrequire(tabPtr.p->noOfWords < 2048);
+ MEMCOPY_NO_WORDS(&pagePtr.p->word[tabPtr.p->noOfWords], &signal->theData[5], 16);
+ tabPtr.p->noOfWords += 16;
+ if (tabPtr.p->noOfWords == 2048) {
+ jam();
+ tabPtr.p->noOfWords = 0;
+ }//if
+ if (noOfWords > 16) {
+ jam();
+ return;
+ }//if
+ tabPtr.p->noOfWords = 0;
+ ndbrequire(tabPtr.p->tabCopyStatus == TabRecord::CS_IDLE);
+ tabPtr.p->tabCopyStatus = TabRecord::CS_COPY_TAB_REQ;
+ signal->theData[0] = DihContinueB::ZREAD_PAGES_INTO_TABLE;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+}//Dbdih::execCOPY_TABREQ()
+
+void
+Dbdih::copyTabReq_complete(Signal* signal, TabRecordPtr tabPtr){
+ if (!isMaster()) {
+ jam();
+ //----------------------------------------------------------------------------
+ // In this particular case we do not release table pages if we are master. The
+ // reason is that the master could still be sending the table info to another
+ // node.
+ //----------------------------------------------------------------------------
+ releaseTabPages(tabPtr.i);
+ tabPtr.p->tabStatus = TabRecord::TS_ACTIVE;
+ for (Uint32 fragId = 0; fragId < tabPtr.p->totalfragments; fragId++) {
+ jam();
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ updateNodeInfo(fragPtr);
+ }//for
+ }//if
+ signal->theData[0] = cownNodeId;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(cmasterdihref, GSN_COPY_TABCONF, signal, 2, JBB);
+}
+
+/*****************************************************************************/
+/* ****** READ FROM A NUMBER OF PAGES INTO THE TABLE DATA STRUCTURES ********/
+/*****************************************************************************/
+void Dbdih::readPagesIntoTableLab(Signal* signal, Uint32 tableId)
+{
+ RWFragment rf;
+ rf.wordIndex = 35;
+ rf.pageIndex = 0;
+ rf.rwfTabPtr.i = tableId;
+ ptrCheckGuard(rf.rwfTabPtr, ctabFileSize, tabRecord);
+ rf.rwfPageptr.i = rf.rwfTabPtr.p->pageRef[0];
+ ptrCheckGuard(rf.rwfPageptr, cpageFileSize, pageRecord);
+ rf.rwfTabPtr.p->totalfragments = readPageWord(&rf);
+ rf.rwfTabPtr.p->noOfBackups = readPageWord(&rf);
+ rf.rwfTabPtr.p->hashpointer = readPageWord(&rf);
+ rf.rwfTabPtr.p->kvalue = readPageWord(&rf);
+ rf.rwfTabPtr.p->mask = readPageWord(&rf);
+ ndbrequire(readPageWord(&rf) == TabRecord::HASH);
+ rf.rwfTabPtr.p->method = TabRecord::HASH;
+ /* ---------------------------------- */
+ /* Type of table, 2 = temporary table */
+ /* ---------------------------------- */
+ rf.rwfTabPtr.p->storedTable = readPageWord(&rf);
+
+ Uint32 noOfFrags = rf.rwfTabPtr.p->totalfragments;
+ ndbrequire(noOfFrags > 0);
+ ndbrequire((noOfFrags * (rf.rwfTabPtr.p->noOfBackups + 1)) <= cnoFreeReplicaRec);
+ allocFragments(noOfFrags, rf.rwfTabPtr);
+
+ signal->theData[0] = DihContinueB::ZREAD_PAGES_INTO_FRAG;
+ signal->theData[1] = rf.rwfTabPtr.i;
+ signal->theData[2] = 0;
+ signal->theData[3] = rf.pageIndex;
+ signal->theData[4] = rf.wordIndex;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 5, JBB);
+ return;
+}//Dbdih::readPagesIntoTableLab()
+
+void Dbdih::readPagesIntoFragLab(Signal* signal, RWFragment* rf)
+{
+ ndbrequire(rf->pageIndex < 8);
+ rf->rwfPageptr.i = rf->rwfTabPtr.p->pageRef[rf->pageIndex];
+ ptrCheckGuard(rf->rwfPageptr, cpageFileSize, pageRecord);
+ FragmentstorePtr fragPtr;
+ getFragstore(rf->rwfTabPtr.p, rf->fragId, fragPtr);
+ readFragment(rf, fragPtr);
+ readReplicas(rf, fragPtr);
+ rf->fragId++;
+ if (rf->fragId == rf->rwfTabPtr.p->totalfragments) {
+ jam();
+ switch (rf->rwfTabPtr.p->tabCopyStatus) {
+ case TabRecord::CS_SR_PHASE1_READ_PAGES:
+ jam();
+ releaseTabPages(rf->rwfTabPtr.i);
+ rf->rwfTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ signal->theData[0] = DihContinueB::ZREAD_TABLE_FROM_PAGES;
+ signal->theData[1] = rf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::CS_COPY_TAB_REQ:
+ jam();
+ rf->rwfTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ if(getNodeState().getSystemRestartInProgress()){
+ jam();
+ copyTabReq_complete(signal, rf->rwfTabPtr);
+ return;
+ }
+ rf->rwfTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ rf->rwfTabPtr.p->tabUpdateState = TabRecord::US_COPY_TAB_REQ;
+ signal->theData[0] = DihContinueB::ZTABLE_UPDATE;
+ signal->theData[1] = rf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ } else {
+ jam();
+ signal->theData[0] = DihContinueB::ZREAD_PAGES_INTO_FRAG;
+ signal->theData[1] = rf->rwfTabPtr.i;
+ signal->theData[2] = rf->fragId;
+ signal->theData[3] = rf->pageIndex;
+ signal->theData[4] = rf->wordIndex;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 5, JBB);
+ }//if
+ return;
+}//Dbdih::readPagesIntoFragLab()
+
+/*****************************************************************************/
+/***** WRITING FROM TABLE DATA STRUCTURES INTO A SET OF PAGES ******/
+// execCONTINUEB(ZPACK_TABLE_INTO_PAGES)
+/*****************************************************************************/
+void Dbdih::packTableIntoPagesLab(Signal* signal, Uint32 tableId)
+{
+ RWFragment wf;
+ TabRecordPtr tabPtr;
+ allocpage(wf.rwfPageptr);
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ tabPtr.p->pageRef[0] = wf.rwfPageptr.i;
+ tabPtr.p->noPages = 1;
+ wf.wordIndex = 35;
+ wf.pageIndex = 0;
+ writePageWord(&wf, tabPtr.p->totalfragments);
+ writePageWord(&wf, tabPtr.p->noOfBackups);
+ writePageWord(&wf, tabPtr.p->hashpointer);
+ writePageWord(&wf, tabPtr.p->kvalue);
+ writePageWord(&wf, tabPtr.p->mask);
+ writePageWord(&wf, TabRecord::HASH);
+ writePageWord(&wf, tabPtr.p->storedTable);
+
+ signal->theData[0] = DihContinueB::ZPACK_FRAG_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = 0;
+ signal->theData[3] = wf.pageIndex;
+ signal->theData[4] = wf.wordIndex;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 5, JBB);
+}//Dbdih::packTableIntoPagesLab()
+
+/*****************************************************************************/
+// execCONTINUEB(ZPACK_FRAG_INTO_PAGES)
+/*****************************************************************************/
+void Dbdih::packFragIntoPagesLab(Signal* signal, RWFragment* wf)
+{
+ ndbrequire(wf->pageIndex < 8);
+ wf->rwfPageptr.i = wf->rwfTabPtr.p->pageRef[wf->pageIndex];
+ ptrCheckGuard(wf->rwfPageptr, cpageFileSize, pageRecord);
+ FragmentstorePtr fragPtr;
+ getFragstore(wf->rwfTabPtr.p, wf->fragId, fragPtr);
+ writeFragment(wf, fragPtr);
+ writeReplicas(wf, fragPtr.p->storedReplicas);
+ writeReplicas(wf, fragPtr.p->oldStoredReplicas);
+ wf->fragId++;
+ if (wf->fragId == wf->rwfTabPtr.p->totalfragments) {
+ jam();
+ PageRecordPtr pagePtr;
+ pagePtr.i = wf->rwfTabPtr.p->pageRef[0];
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+ pagePtr.p->word[33] = wf->rwfTabPtr.p->noPages;
+ pagePtr.p->word[34] = ((wf->rwfTabPtr.p->noPages - 1) * 2048) + wf->wordIndex;
+ switch (wf->rwfTabPtr.p->tabCopyStatus) {
+ case TabRecord::CS_SR_PHASE2_READ_TABLE:
+ /* -------------------------------------------------------------------*/
+ // We are performing a system restart and we are now ready to copy the
+ // table from this node (the master) to all other nodes.
+ /* -------------------------------------------------------------------*/
+ jam();
+ wf->rwfTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ signal->theData[0] = DihContinueB::ZSR_PHASE2_READ_TABLE;
+ signal->theData[1] = wf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::CS_COPY_NODE_STATE:
+ jam();
+ tableCopyNodeLab(signal, wf->rwfTabPtr);
+ return;
+ break;
+ case TabRecord::CS_LCP_READ_TABLE:
+ jam();
+ signal->theData[0] = DihContinueB::ZTABLE_UPDATE;
+ signal->theData[1] = wf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::CS_REMOVE_NODE:
+ case TabRecord::CS_INVALIDATE_NODE_LCP:
+ jam();
+ signal->theData[0] = DihContinueB::ZTABLE_UPDATE;
+ signal->theData[1] = wf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::CS_ADD_TABLE_MASTER:
+ jam();
+ wf->rwfTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ signal->theData[0] = DihContinueB::ZADD_TABLE_MASTER_PAGES;
+ signal->theData[1] = wf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::CS_ADD_TABLE_SLAVE:
+ jam();
+ wf->rwfTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ signal->theData[0] = DihContinueB::ZADD_TABLE_SLAVE_PAGES;
+ signal->theData[1] = wf->rwfTabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ } else {
+ jam();
+ signal->theData[0] = DihContinueB::ZPACK_FRAG_INTO_PAGES;
+ signal->theData[1] = wf->rwfTabPtr.i;
+ signal->theData[2] = wf->fragId;
+ signal->theData[3] = wf->pageIndex;
+ signal->theData[4] = wf->wordIndex;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 5, JBB);
+ }//if
+ return;
+}//Dbdih::packFragIntoPagesLab()
+
+/*****************************************************************************/
+/* ********** START FRAGMENT MODULE *************/
+/*****************************************************************************/
+void Dbdih::startFragment(Signal* signal, Uint32 tableId, Uint32 fragId)
+{
+ Uint32 TloopCount = 0;
+ TabRecordPtr tabPtr;
+ while (true) {
+ if (TloopCount > 100) {
+ jam();
+ signal->theData[0] = DihContinueB::ZSTART_FRAGMENT;
+ signal->theData[1] = tableId;
+ signal->theData[2] = 0;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }
+
+ if (tableId >= ctabFileSize) {
+ jam();
+ signal->theData[0] = DihContinueB::ZCOMPLETE_RESTART;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 1, JBB);
+ return;
+ }//if
+
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if (tabPtr.p->tabStatus != TabRecord::TS_ACTIVE){
+ jam();
+ TloopCount++;
+ tableId++;
+ fragId = 0;
+ continue;
+ }
+
+ if(tabPtr.p->storedTable == 0){
+ jam();
+ TloopCount++;
+ tableId++;
+ fragId = 0;
+ continue;
+ }
+
+ jam();
+ break;
+ }//while
+
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ /* ----------------------------------------------------------------------- */
+ /* WE NEED TO RESET THE REPLICA DATA STRUCTURES. THIS MEANS THAT WE */
+ /* MUST REMOVE REPLICAS THAT WAS NOT STARTED AT THE GCI TO RESTORE. WE */
+ /* NEED TO PUT ALL STORED REPLICAS ON THE LIST OF OLD STORED REPLICAS */
+ /* RESET THE NUMBER OF REPLICAS TO CREATE. */
+ /* ----------------------------------------------------------------------- */
+ cnoOfCreateReplicas = 0;
+ /* ----------------------------------------------------------------------- */
+ /* WE WILL NEVER START MORE THAN FOUR FRAGMENT REPLICAS WHATEVER THE */
+ /* DESIRED REPLICATION IS. */
+ /* ----------------------------------------------------------------------- */
+ ndbrequire(tabPtr.p->noOfBackups < 4);
+ /* ----------------------------------------------------------------------- */
+ /* SEARCH FOR STORED REPLICAS THAT CAN BE USED TO RESTART THE SYSTEM. */
+ /* ----------------------------------------------------------------------- */
+ searchStoredReplicas(fragPtr);
+ if (cnoOfCreateReplicas == 0) {
+ /* --------------------------------------------------------------------- */
+ /* THERE WERE NO STORED REPLICAS AVAILABLE THAT CAN SERVE AS REPLICA TO*/
+ /* RESTART THE SYSTEM FROM. IN A LATER RELEASE WE WILL ADD */
+ /* FUNCTIONALITY TO CHECK IF THERE ARE ANY STANDBY NODES THAT COULD DO */
+ /* THIS TASK INSTEAD IN THIS IMPLEMENTATION WE SIMPLY CRASH THE SYSTEM.*/
+ /* THIS WILL DECREASE THE GCI TO RESTORE WHICH HOPEFULLY WILL MAKE IT */
+ /* POSSIBLE TO RESTORE THE SYSTEM. */
+ /* --------------------------------------------------------------------- */
+ char buf[100];
+ BaseString::snprintf(buf, sizeof(buf),
+ "Unable to find restorable replica for "
+ "table: %d fragment: %d gci: %d",
+ tableId, fragId, SYSFILE->newestRestorableGCI);
+ progError(__LINE__,
+ ERR_SYSTEM_ERROR,
+ buf);
+ ndbrequire(false);
+ return;
+ }//if
+
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE CHANGED THE NODE TO BE PRIMARY REPLICA AND THE NODES TO BE */
+ /* BACKUP NODES. WE MUST UPDATE THIS NODES DATA STRUCTURE SINCE WE */
+ /* WILL NOT COPY THE TABLE DATA TO OURSELF. */
+ /* ----------------------------------------------------------------------- */
+ updateNodeInfo(fragPtr);
+ /* ----------------------------------------------------------------------- */
+ /* NOW WE HAVE COLLECTED ALL THE REPLICAS WE COULD GET. WE WILL NOW */
+ /* RESTART THE FRAGMENT REPLICAS WE HAVE FOUND IRRESPECTIVE OF IF THERE*/
+ /* ARE ENOUGH ACCORDING TO THE DESIRED REPLICATION. */
+ /* ----------------------------------------------------------------------- */
+ /* WE START BY SENDING ADD_FRAGREQ FOR THOSE REPLICAS THAT NEED IT. */
+ /* ----------------------------------------------------------------------- */
+ CreateReplicaRecordPtr createReplicaPtr;
+ for (createReplicaPtr.i = 0;
+ createReplicaPtr.i < cnoOfCreateReplicas;
+ createReplicaPtr.i++) {
+ jam();
+ ptrCheckGuard(createReplicaPtr, 4, createReplicaRecord);
+ createReplicaPtr.p->hotSpareUse = false;
+ }//for
+
+ sendStartFragreq(signal, tabPtr, fragId);
+
+ /**
+ * Don't wait for START_FRAGCONF
+ */
+ fragId++;
+ if (fragId >= tabPtr.p->totalfragments) {
+ jam();
+ tabPtr.i++;
+ fragId = 0;
+ }//if
+ signal->theData[0] = DihContinueB::ZSTART_FRAGMENT;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = fragId;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+
+ return;
+}//Dbdih::startFragmentLab()
+
+
+/*****************************************************************************/
+/* ********** COMPLETE RESTART MODULE *************/
+/*****************************************************************************/
+void Dbdih::completeRestartLab(Signal* signal)
+{
+ sendLoopMacro(START_RECREQ, sendSTART_RECREQ);
+}//completeRestartLab()
+
+/* ------------------------------------------------------------------------- */
+// SYSTEM RESTART:
+/* A NODE HAS COMPLETED RESTORING ALL DATABASE FRAGMENTS. */
+// NODE RESTART:
+// THE STARTING NODE HAS PREPARED ITS LOG FILES TO ENABLE EXECUTION
+// OF TRANSACTIONS.
+// Precondition:
+// This signal must be received by the master node.
+/* ------------------------------------------------------------------------- */
+void Dbdih::execSTART_RECCONF(Signal* signal)
+{
+ jamEntry();
+ Uint32 senderNodeId = signal->theData[0];
+ ndbrequire(isMaster());
+ if (getNodeState().startLevel >= NodeState::SL_STARTED){
+ /* --------------------------------------------------------------------- */
+ // Since our node is already up and running this must be a node restart.
+ // This means that we should be the master node,
+ // otherwise we have a problem.
+ /* --------------------------------------------------------------------- */
+ jam();
+ ndbrequire(senderNodeId == c_nodeStartMaster.startNode);
+ nodeRestartStartRecConfLab(signal);
+ return;
+ } else {
+ /* --------------------------------------------------------------------- */
+ // This was the system restart case. We set the state indicating that the
+ // node has completed restoration of all fragments.
+ /* --------------------------------------------------------------------- */
+ receiveLoopMacro(START_RECREQ, senderNodeId);
+
+ signal->theData[0] = reference();
+ sendSignal(cntrlblockref, GSN_NDB_STARTCONF, signal, 1, JBB);
+ return;
+ }//if
+}//Dbdih::execSTART_RECCONF()
+
+void Dbdih::copyNodeLab(Signal* signal, Uint32 tableId)
+{
+ /* ----------------------------------------------------------------------- */
+ // This code is executed by the master to assist a node restart in receiving
+ // the data in the master.
+ /* ----------------------------------------------------------------------- */
+ Uint32 TloopCount = 0;
+
+ if (!c_nodeStartMaster.activeState) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ // Obviously the node crashed in the middle of its node restart. We will
+ // stop this process simply by returning after resetting the wait indicator.
+ /* ---------------------------------------------------------------------- */
+ c_nodeStartMaster.wait = ZFALSE;
+ return;
+ }//if
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ while (tabPtr.i < ctabFileSize) {
+ ptrAss(tabPtr, tabRecord);
+ if (tabPtr.p->tabStatus == TabRecord::TS_ACTIVE) {
+ /* -------------------------------------------------------------------- */
+ // The table is defined. We will start by packing the table into pages.
+ // The tabCopyStatus indicates to the CONTINUEB(ZPACK_TABLE_INTO_PAGES)
+ // who called it. After packing the table into page(s) it will be sent to
+ // the starting node by COPY_TABREQ signals. After returning from the
+ // starting node we will return to this subroutine and continue
+ // with the next table.
+ /* -------------------------------------------------------------------- */
+ ndbrequire(tabPtr.p->tabCopyStatus == TabRecord::CS_IDLE);
+ tabPtr.p->tabCopyStatus = TabRecord::CS_COPY_NODE_STATE;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ } else {
+ jam();
+ if (TloopCount > 100) {
+ /* ------------------------------------------------------------------ */
+ // Introduce real-time break after looping through 100 not copied tables
+ /* ----------------------------------------------------------------- */
+ jam();
+ signal->theData[0] = DihContinueB::ZCOPY_NODE;
+ signal->theData[1] = tabPtr.i + 1;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ } else {
+ jam();
+ TloopCount++;
+ tabPtr.i++;
+ }//if
+ }//if
+ }//while
+ dihCopyCompletedLab(signal);
+ return;
+}//Dbdih::copyNodeLab()
+
+void Dbdih::tableCopyNodeLab(Signal* signal, TabRecordPtr tabPtr)
+{
+ /* ----------------------------------------------------------------------- */
+ /* COPY PAGES READ TO STARTING NODE. */
+ /* ----------------------------------------------------------------------- */
+ if (!c_nodeStartMaster.activeState) {
+ jam();
+ releaseTabPages(tabPtr.i);
+ c_nodeStartMaster.wait = ZFALSE;
+ return;
+ }//if
+ NodeRecordPtr copyNodePtr;
+ PageRecordPtr pagePtr;
+ copyNodePtr.i = c_nodeStartMaster.startNode;
+ ptrCheckGuard(copyNodePtr, MAX_NDB_NODES, nodeRecord);
+
+ copyNodePtr.p->activeTabptr = tabPtr.i;
+ pagePtr.i = tabPtr.p->pageRef[0];
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+
+ signal->theData[0] = DihContinueB::ZCOPY_TABLE_NODE;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = copyNodePtr.i;
+ signal->theData[3] = 0;
+ signal->theData[4] = 0;
+ signal->theData[5] = pagePtr.p->word[34];
+ sendSignal(reference(), GSN_CONTINUEB, signal, 6, JBB);
+}//Dbdih::tableCopyNodeLab()
+
+/* ------------------------------------------------------------------------- */
+// execCONTINUEB(ZCOPY_TABLE)
+// This routine is used to copy the table descriptions from the master to
+// other nodes. It is used in the system restart to copy from master to all
+// starting nodes.
+/* ------------------------------------------------------------------------- */
+void Dbdih::copyTableLab(Signal* signal, Uint32 tableId)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrAss(tabPtr, tabRecord);
+
+ ndbrequire(tabPtr.p->tabCopyStatus == TabRecord::CS_IDLE);
+ tabPtr.p->tabCopyStatus = TabRecord::CS_SR_PHASE2_READ_TABLE;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//Dbdih::copyTableLab()
+
+/* ------------------------------------------------------------------------- */
+// execCONTINUEB(ZSR_PHASE2_READ_TABLE)
+/* ------------------------------------------------------------------------- */
+void Dbdih::srPhase2ReadTableLab(Signal* signal, TabRecordPtr tabPtr)
+{
+ /* ----------------------------------------------------------------------- */
+ // We set the sendCOPY_TABREQState to ZACTIVE for all nodes since it is a long
+ // process to send off all table descriptions. Thus we ensure that we do
+ // not encounter race conditions where one node is completed before the
+ // sending process is completed. This could lead to that we start off the
+ // system before we actually finished all copying of table descriptions
+ // and could lead to strange errors.
+ /* ----------------------------------------------------------------------- */
+
+ //sendLoopMacro(COPY_TABREQ, nullRoutine);
+
+ breakCopyTableLab(signal, tabPtr, cfirstAliveNode);
+ return;
+}//Dbdih::srPhase2ReadTableLab()
+
+/* ------------------------------------------------------------------------- */
+/* COPY PAGES READ TO ALL NODES. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::breakCopyTableLab(Signal* signal, TabRecordPtr tabPtr, Uint32 nodeId)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ while (nodePtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.i == getOwnNodeId()){
+ jam();
+ /* ------------------------------------------------------------------- */
+ /* NOT NECESSARY TO COPY TO MY OWN NODE. I ALREADY HAVE THE PAGES. */
+ /* I DO HOWEVER NEED TO STORE THE TABLE DESCRIPTION ONTO DISK. */
+ /* ------------------------------------------------------------------- */
+ /* IF WE ARE MASTER WE ONLY NEED TO SAVE THE TABLE ON DISK. WE ALREADY */
+ /* HAVE THE TABLE DESCRIPTION IN THE DATA STRUCTURES. */
+ // AFTER COMPLETING THE WRITE TO DISK THE MASTER WILL ALSO SEND
+ // COPY_TABCONF AS ALL THE OTHER NODES.
+ /* ------------------------------------------------------------------- */
+ c_COPY_TABREQ_Counter.setWaitingFor(nodePtr.i);
+ tabPtr.p->tabUpdateState = TabRecord::US_COPY_TAB_REQ;
+ signal->theData[0] = DihContinueB::ZTABLE_UPDATE;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ nodePtr.i = nodePtr.p->nextNode;
+ } else {
+ PageRecordPtr pagePtr;
+ /* -------------------------------------------------------------------- */
+ // RATHER THAN SENDING ALL COPY_TABREQ IN PARALLEL WE WILL SERIALISE THIS
+ // ACTIVITY AND WILL THUS CALL breakCopyTableLab AGAIN WHEN COMPLETED THE
+ // SENDING OF COPY_TABREQ'S.
+ /* -------------------------------------------------------------------- */
+ jam();
+ tabPtr.p->tabCopyStatus = TabRecord::CS_SR_PHASE3_COPY_TABLE;
+ pagePtr.i = tabPtr.p->pageRef[0];
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+ signal->theData[0] = DihContinueB::ZCOPY_TABLE_NODE;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = nodePtr.i;
+ signal->theData[3] = 0;
+ signal->theData[4] = 0;
+ signal->theData[5] = pagePtr.p->word[34];
+ sendSignal(reference(), GSN_CONTINUEB, signal, 6, JBB);
+ return;
+ }//if
+ }//while
+ /* ----------------------------------------------------------------------- */
+ /* WE HAVE NOW SENT THE TABLE PAGES TO ALL NODES. EXIT AND WAIT FOR ALL */
+ /* REPLIES. */
+ /* ----------------------------------------------------------------------- */
+ return;
+}//Dbdih::breakCopyTableLab()
+
+/* ------------------------------------------------------------------------- */
+// execCONTINUEB(ZCOPY_TABLE_NODE)
+/* ------------------------------------------------------------------------- */
+void Dbdih::copyTableNode(Signal* signal,
+ CopyTableNode* ctn, NodeRecordPtr nodePtr)
+{
+ if (getNodeState().startLevel >= NodeState::SL_STARTED){
+ /* --------------------------------------------------------------------- */
+ // We are in the process of performing a node restart and are copying a
+ // table description to a starting node. We will check that no nodes have
+ // crashed in this process.
+ /* --------------------------------------------------------------------- */
+ if (!c_nodeStartMaster.activeState) {
+ jam();
+ /** ------------------------------------------------------------------
+ * The starting node crashed. We will release table pages and stop this
+ * copy process and allow new node restarts to start.
+ * ------------------------------------------------------------------ */
+ releaseTabPages(ctn->ctnTabPtr.i);
+ c_nodeStartMaster.wait = ZFALSE;
+ return;
+ }//if
+ }//if
+ ndbrequire(ctn->pageIndex < 8);
+ ctn->ctnPageptr.i = ctn->ctnTabPtr.p->pageRef[ctn->pageIndex];
+ ptrCheckGuard(ctn->ctnPageptr, cpageFileSize, pageRecord);
+ /**
+ * If first page & firstWord reqinfo = 1 (first signal)
+ */
+ Uint32 reqinfo = (ctn->pageIndex == 0) && (ctn->wordIndex == 0);
+ if(reqinfo == 1){
+ c_COPY_TABREQ_Counter.setWaitingFor(nodePtr.i);
+ }
+
+ for (Uint32 i = 0; i < 16; i++) {
+ jam();
+ sendCopyTable(signal, ctn, calcDihBlockRef(nodePtr.i), reqinfo);
+ reqinfo = 0;
+ if (ctn->noOfWords <= 16) {
+ jam();
+ switch (ctn->ctnTabPtr.p->tabCopyStatus) {
+ case TabRecord::CS_SR_PHASE3_COPY_TABLE:
+ /* ------------------------------------------------------------------ */
+ // We have copied the table description to this node.
+ // We will now proceed
+ // with sending the table description to the next node in the node list.
+ /* ------------------------------------------------------------------ */
+ jam();
+ ctn->ctnTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ breakCopyTableLab(signal, ctn->ctnTabPtr, nodePtr.p->nextNode);
+ return;
+ break;
+ case TabRecord::CS_COPY_NODE_STATE:
+ jam();
+ ctn->ctnTabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ } else {
+ jam();
+ ctn->wordIndex += 16;
+ if (ctn->wordIndex == 2048) {
+ jam();
+ ctn->wordIndex = 0;
+ ctn->pageIndex++;
+ ndbrequire(ctn->pageIndex < 8);
+ ctn->ctnPageptr.i = ctn->ctnTabPtr.p->pageRef[ctn->pageIndex];
+ ptrCheckGuard(ctn->ctnPageptr, cpageFileSize, pageRecord);
+ }//if
+ ctn->noOfWords -= 16;
+ }//if
+ }//for
+ signal->theData[0] = DihContinueB::ZCOPY_TABLE_NODE;
+ signal->theData[1] = ctn->ctnTabPtr.i;
+ signal->theData[2] = nodePtr.i;
+ signal->theData[3] = ctn->pageIndex;
+ signal->theData[4] = ctn->wordIndex;
+ signal->theData[5] = ctn->noOfWords;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 6, JBB);
+}//Dbdih::copyTableNodeLab()
+
+void Dbdih::sendCopyTable(Signal* signal, CopyTableNode* ctn,
+ BlockReference ref, Uint32 reqinfo)
+{
+ signal->theData[0] = reference();
+ signal->theData[1] = reqinfo;
+ signal->theData[2] = ctn->ctnTabPtr.i;
+ signal->theData[3] = ctn->ctnTabPtr.p->schemaVersion;
+ signal->theData[4] = ctn->noOfWords;
+ ndbrequire(ctn->wordIndex + 15 < 2048);
+ MEMCOPY_NO_WORDS(&signal->theData[5], &ctn->ctnPageptr.p->word[ctn->wordIndex], 16);
+ sendSignal(ref, GSN_COPY_TABREQ, signal, 21, JBB);
+}//Dbdih::sendCopyTable()
+
+void Dbdih::execCOPY_TABCONF(Signal* signal)
+{
+ NodeRecordPtr nodePtr;
+ jamEntry();
+ nodePtr.i = signal->theData[0];
+ Uint32 tableId = signal->theData[1];
+ if (getNodeState().startLevel >= NodeState::SL_STARTED){
+ /* --------------------------------------------------------------------- */
+ // We are in the process of performing a node restart. Continue by copying
+ // the next table to the starting node.
+ /* --------------------------------------------------------------------- */
+ jam();
+ NodeRecordPtr nodePtr;
+ nodePtr.i = signal->theData[0];
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ c_COPY_TABREQ_Counter.clearWaitingFor(nodePtr.i);
+
+ releaseTabPages(tableId);
+ signal->theData[0] = DihContinueB::ZCOPY_NODE;
+ signal->theData[1] = tableId + 1;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ } else {
+ /* --------------------------------------------------------------------- */
+ // We are in the process of performing a system restart. Check if all nodes
+ // have saved the new table description to file and then continue with the
+ // next table.
+ /* --------------------------------------------------------------------- */
+ receiveLoopMacro(COPY_TABREQ, nodePtr.i);
+ /* --------------------------------------------------------------------- */
+ /* WE HAVE NOW COPIED TO ALL NODES. WE HAVE NOW COMPLETED RESTORING */
+ /* THIS TABLE. CONTINUE WITH THE NEXT TABLE. */
+ /* WE NEED TO RELEASE THE PAGES IN THE TABLE IN THIS NODE HERE. */
+ /* WE ALSO NEED TO CLOSE THE TABLE FILE. */
+ /* --------------------------------------------------------------------- */
+ releaseTabPages(tableId);
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ConnectRecordPtr connectPtr;
+ connectPtr.i = tabPtr.p->connectrec;
+ ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
+
+ sendAddFragreq(signal, connectPtr, tabPtr, 0);
+ return;
+ }//if
+}//Dbdih::execCOPY_TABCONF()
+
+/*
+ 3.13 L O C A L C H E C K P O I N T (M A S T E R)
+ ****************************************************
+ */
+/*****************************************************************************/
+/* ********** LOCAL-CHECK-POINT-HANDLING MODULE *************/
+/*****************************************************************************/
+/* ------------------------------------------------------------------------- */
+/* IT IS TIME TO CHECK IF IT IS TIME TO START A LOCAL CHECKPOINT. */
+/* WE WILL EITHER START AFTER 1 MILLION WORDS HAVE ARRIVED OR WE WILL */
+/* EXECUTE AFTER ABOUT 16 MINUTES HAVE PASSED BY. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::checkTcCounterLab(Signal* signal)
+{
+ CRASH_INSERTION(7009);
+ if (c_lcpState.lcpStatus != LCP_STATUS_IDLE) {
+ ndbout << "lcpStatus = " << (Uint32) c_lcpState.lcpStatus;
+ ndbout << "lcpStatusUpdatedPlace = " <<
+ c_lcpState.lcpStatusUpdatedPlace << endl;
+ ndbrequire(false);
+ return;
+ }//if
+ c_lcpState.ctimer += 32;
+ if ((c_nodeStartMaster.blockLcp == true) ||
+ ((c_lcpState.lcpStartGcp + 1) > currentgcp)) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ // No reason to start juggling the states and checking for start of LCP if
+ // we are blocked to start an LCP anyway.
+ // We also block LCP start if we have not completed one global checkpoints
+ // before starting another local checkpoint.
+ /* --------------------------------------------------------------------- */
+ signal->theData[0] = DihContinueB::ZCHECK_TC_COUNTER;
+ signal->theData[1] = __LINE__;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 1 * 100, 2);
+ return;
+ }//if
+ c_lcpState.setLcpStatus(LCP_TCGET, __LINE__);
+
+ c_lcpState.ctcCounter = c_lcpState.ctimer;
+ sendLoopMacro(TCGETOPSIZEREQ, sendTCGETOPSIZEREQ);
+}//Dbdih::checkTcCounterLab()
+
+void Dbdih::checkLcpStart(Signal* signal, Uint32 lineNo)
+{
+ /* ----------------------------------------------------------------------- */
+ // Verify that we are not attempting to start another instance of the LCP
+ // when it is not alright to do so.
+ /* ----------------------------------------------------------------------- */
+ ndbrequire(c_lcpState.lcpStart == ZIDLE);
+ c_lcpState.lcpStart = ZACTIVE;
+ signal->theData[0] = DihContinueB::ZCHECK_TC_COUNTER;
+ signal->theData[1] = lineNo;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 1000, 2);
+}//Dbdih::checkLcpStart()
+
+/* ------------------------------------------------------------------------- */
+/*TCGETOPSIZECONF HOW MUCH OPERATION SIZE HAVE BEEN EXECUTED BY TC */
+/* ------------------------------------------------------------------------- */
+void Dbdih::execTCGETOPSIZECONF(Signal* signal)
+{
+ jamEntry();
+ Uint32 senderNodeId = signal->theData[0];
+ c_lcpState.ctcCounter += signal->theData[1];
+
+ receiveLoopMacro(TCGETOPSIZEREQ, senderNodeId);
+
+ ndbrequire(c_lcpState.lcpStatus == LCP_TCGET);
+ ndbrequire(c_lcpState.lcpStart == ZACTIVE);
+ /* ----------------------------------------------------------------------- */
+ // We are not actively starting another LCP, still we receive this signal.
+ // This is not ok.
+ /* ---------------------------------------------------------------------- */
+ /* ALL TC'S HAVE RESPONDED NOW. NOW WE WILL CHECK IF ENOUGH OPERATIONS */
+ /* HAVE EXECUTED TO ENABLE US TO START A NEW LOCAL CHECKPOINT. */
+ /* WHILE COPYING DICTIONARY AND DISTRIBUTION INFO TO A STARTING NODE */
+ /* WE WILL ALSO NOT ALLOW THE LOCAL CHECKPOINT TO PROCEED. */
+ /*----------------------------------------------------------------------- */
+ if (c_lcpState.immediateLcpStart == false) {
+ if ((c_lcpState.ctcCounter <
+ ((Uint32)1 << c_lcpState.clcpDelay)) ||
+ (c_nodeStartMaster.blockLcp == true)) {
+ jam();
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+
+ signal->theData[0] = DihContinueB::ZCHECK_TC_COUNTER;
+ signal->theData[1] = __LINE__;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 1 * 100, 2);
+ return;
+ }//if
+ }//if
+ c_lcpState.lcpStart = ZIDLE;
+ c_lcpState.immediateLcpStart = false;
+ /* -----------------------------------------------------------------------
+ * Now the initial lcp is started,
+ * we can reset the delay to its orginal value
+ * --------------------------------------------------------------------- */
+ CRASH_INSERTION(7010);
+ /* ----------------------------------------------------------------------- */
+ /* IF MORE THAN 1 MILLION WORDS PASSED THROUGH THE TC'S THEN WE WILL */
+ /* START A NEW LOCAL CHECKPOINT. CLEAR CTIMER. START CHECKPOINT */
+ /* ACTIVITY BY CALCULATING THE KEEP GLOBAL CHECKPOINT. */
+ // Also remember the current global checkpoint to ensure that we run at least
+ // one global checkpoints between each local checkpoint that we start up.
+ /* ----------------------------------------------------------------------- */
+ c_lcpState.ctimer = 0;
+ c_lcpState.keepGci = coldgcp;
+ c_lcpState.lcpStartGcp = currentgcp;
+ /* ----------------------------------------------------------------------- */
+ /* UPDATE THE NEW LATEST LOCAL CHECKPOINT ID. */
+ /* ----------------------------------------------------------------------- */
+ cnoOfActiveTables = 0;
+ c_lcpState.setLcpStatus(LCP_CALCULATE_KEEP_GCI, __LINE__);
+ c_lcpState.oldestRestorableGci = SYSFILE->oldestRestorableGCI;
+ ndbrequire(((int)c_lcpState.oldestRestorableGci) > 0);
+
+ if (ERROR_INSERTED(7011)) {
+ signal->theData[0] = NDB_LE_LCPStoppedInCalcKeepGci;
+ signal->theData[1] = 0;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+ return;
+ }//if
+ signal->theData[0] = DihContinueB::ZCALCULATE_KEEP_GCI;
+ signal->theData[1] = 0; /* TABLE ID = 0 */
+ signal->theData[2] = 0; /* FRAGMENT ID = 0 */
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+}//Dbdih::execTCGETOPSIZECONF()
+
+/* ------------------------------------------------------------------------- */
+/* WE NEED TO CALCULATE THE OLDEST GLOBAL CHECKPOINT THAT WILL BE */
+/* COMPLETELY RESTORABLE AFTER EXECUTING THIS LOCAL CHECKPOINT. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::calculateKeepGciLab(Signal* signal, Uint32 tableId, Uint32 fragId)
+{
+ TabRecordPtr tabPtr;
+ Uint32 TloopCount = 1;
+ tabPtr.i = tableId;
+ do {
+ if (tabPtr.i >= ctabFileSize) {
+ if (cnoOfActiveTables > 0) {
+ jam();
+ signal->theData[0] = DihContinueB::ZSTORE_NEW_LCP_ID;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 1, JBB);
+ return;
+ } else {
+ jam();
+ /* ------------------------------------------------------------------ */
+ /* THERE ARE NO TABLES TO CHECKPOINT. WE STOP THE CHECKPOINT ALREADY */
+ /* HERE TO AVOID STRANGE PROBLEMS LATER. */
+ /* ------------------------------------------------------------------ */
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+ checkLcpStart(signal, __LINE__);
+ return;
+ }//if
+ }//if
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if (tabPtr.p->tabStatus != TabRecord::TS_ACTIVE ||
+ tabPtr.p->storedTable == 0) {
+ if (TloopCount > 100) {
+ jam();
+ signal->theData[0] = DihContinueB::ZCALCULATE_KEEP_GCI;
+ signal->theData[1] = tabPtr.i + 1;
+ signal->theData[2] = 0;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ } else {
+ jam();
+ TloopCount++;
+ tabPtr.i++;
+ }//if
+ } else {
+ jam();
+ TloopCount = 0;
+ }//if
+ } while (TloopCount != 0);
+ cnoOfActiveTables++;
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ checkKeepGci(fragPtr.p->storedReplicas);
+ fragId++;
+ if (fragId >= tabPtr.p->totalfragments) {
+ jam();
+ tabPtr.i++;
+ fragId = 0;
+ }//if
+ signal->theData[0] = DihContinueB::ZCALCULATE_KEEP_GCI;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = fragId;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+}//Dbdih::calculateKeepGciLab()
+
+/* ------------------------------------------------------------------------- */
+/* WE NEED TO STORE ON DISK THE FACT THAT WE ARE STARTING THIS LOCAL */
+/* CHECKPOINT ROUND. THIS WILL INVALIDATE ALL THE LOCAL CHECKPOINTS */
+/* THAT WILL EVENTUALLY BE OVERWRITTEN AS PART OF THIS LOCAL CHECKPOINT*/
+/* ------------------------------------------------------------------------- */
+void Dbdih::storeNewLcpIdLab(Signal* signal)
+{
+ /***************************************************************************/
+ // Report the event that a local checkpoint has started.
+ /***************************************************************************/
+ signal->theData[0] = NDB_LE_LocalCheckpointStarted; //Event type
+ signal->theData[1] = SYSFILE->latestLCP_ID + 1;
+ signal->theData[2] = c_lcpState.keepGci;
+ signal->theData[3] = c_lcpState.oldestRestorableGci;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+
+ signal->setTrace(TestOrd::TraceLocalCheckpoint);
+
+ CRASH_INSERTION(7013);
+ SYSFILE->keepGCI = c_lcpState.keepGci;
+ //Uint32 lcpId = SYSFILE->latestLCP_ID;
+ SYSFILE->latestLCP_ID++;
+ SYSFILE->oldestRestorableGCI = c_lcpState.oldestRestorableGci;
+
+ const Uint32 oldestRestorableGCI = SYSFILE->oldestRestorableGCI;
+ //const Uint32 newestRestorableGCI = SYSFILE->newestRestorableGCI;
+ //ndbrequire(newestRestorableGCI >= oldestRestorableGCI);
+
+ Int32 val = oldestRestorableGCI;
+ ndbrequire(val > 0);
+
+ /* ----------------------------------------------------------------------- */
+ /* SET BIT INDICATING THAT LOCAL CHECKPOINT IS ONGOING. THIS IS CLEARED */
+ /* AT THE END OF A LOCAL CHECKPOINT. */
+ /* ----------------------------------------------------------------------- */
+ SYSFILE->setLCPOngoing(SYSFILE->systemRestartBits);
+ /* ---------------------------------------------------------------------- */
+ /* CHECK IF ANY NODE MUST BE TAKEN OUT OF SERVICE AND REFILLED WITH */
+ /* NEW FRESH DATA FROM AN ACTIVE NODE. */
+ /* ---------------------------------------------------------------------- */
+ setLcpActiveStatusStart(signal);
+ c_lcpState.setLcpStatus(LCP_COPY_GCI, __LINE__);
+ //#ifdef VM_TRACE
+ // infoEvent("LocalCheckpoint %d started", SYSFILE->latestLCP_ID);
+ // signal->theData[0] = 7012;
+ // execDUMP_STATE_ORD(signal);
+ //#endif
+
+ copyGciLab(signal, CopyGCIReq::LOCAL_CHECKPOINT);
+}//Dbdih::storeNewLcpIdLab()
+
+void Dbdih::startLcpRoundLab(Signal* signal) {
+ jam();
+
+ Mutex mutex(signal, c_mutexMgr, c_startLcpMutexHandle);
+ Callback c = { safe_cast(&Dbdih::startLcpMutex_locked), 0 };
+ ndbrequire(mutex.lock(c));
+}
+
+void
+Dbdih::startLcpMutex_locked(Signal* signal, Uint32 senderData, Uint32 retVal){
+ jamEntry();
+ ndbrequire(retVal == 0);
+
+ StartLcpReq* req = (StartLcpReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->lcpId = SYSFILE->latestLCP_ID;
+ req->participatingLQH = c_lcpState.m_participatingLQH;
+ req->participatingDIH = c_lcpState.m_participatingDIH;
+ sendLoopMacro(START_LCP_REQ, sendSTART_LCP_REQ);
+}
+void
+Dbdih::sendSTART_LCP_REQ(Signal* signal, Uint32 nodeId){
+ BlockReference ref = calcDihBlockRef(nodeId);
+ sendSignal(ref, GSN_START_LCP_REQ, signal, StartLcpReq::SignalLength, JBB);
+}
+
+void
+Dbdih::execSTART_LCP_CONF(Signal* signal){
+ StartLcpConf * conf = (StartLcpConf*)signal->getDataPtr();
+
+ Uint32 nodeId = refToNode(conf->senderRef);
+ receiveLoopMacro(START_LCP_REQ, nodeId);
+
+ Mutex mutex(signal, c_mutexMgr, c_startLcpMutexHandle);
+ Callback c = { safe_cast(&Dbdih::startLcpMutex_unlocked), 0 };
+ mutex.unlock(c);
+}
+
+void
+Dbdih::startLcpMutex_unlocked(Signal* signal, Uint32 data, Uint32 retVal){
+ jamEntry();
+ ndbrequire(retVal == 0);
+
+ Mutex mutex(signal, c_mutexMgr, c_startLcpMutexHandle);
+ mutex.release();
+
+ CRASH_INSERTION(7014);
+ c_lcpState.setLcpStatus(LCP_TC_CLOPSIZE, __LINE__);
+ sendLoopMacro(TC_CLOPSIZEREQ, sendTC_CLOPSIZEREQ);
+}
+
+void Dbdih::execTC_CLOPSIZECONF(Signal* signal) {
+ jamEntry();
+ Uint32 senderNodeId = signal->theData[0];
+ receiveLoopMacro(TC_CLOPSIZEREQ, senderNodeId);
+
+ ndbrequire(c_lcpState.lcpStatus == LCP_TC_CLOPSIZE);
+ /* ----------------------------------------------------------------------- */
+ /* ALL TC'S HAVE CLEARED THEIR OPERATION SIZE COUNTERS. NOW PROCEED BY */
+ /* STARTING THE LOCAL CHECKPOINT IN EACH LQH. */
+ /* ----------------------------------------------------------------------- */
+ c_lcpState.m_LAST_LCP_FRAG_ORD = c_lcpState.m_participatingLQH;
+
+ CRASH_INSERTION(7015);
+ c_lcpState.setLcpStatus(LCP_START_LCP_ROUND, __LINE__);
+ startLcpRoundLoopLab(signal, 0, 0);
+}//Dbdih::execTC_CLOPSIZECONF()
+
+void Dbdih::startLcpRoundLoopLab(Signal* signal,
+ Uint32 startTableId, Uint32 startFragId)
+{
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ ndbrequire(nodePtr.p->noOfStartedChkpt == 0);
+ ndbrequire(nodePtr.p->noOfQueuedChkpt == 0);
+ }//if
+ }//if
+ c_lcpState.currentFragment.tableId = startTableId;
+ c_lcpState.currentFragment.fragmentId = startFragId;
+ startNextChkpt(signal);
+}//Dbdih::startLcpRoundLoopLab()
+
+void Dbdih::startNextChkpt(Signal* signal)
+{
+ Uint32 lcpId = SYSFILE->latestLCP_ID;
+
+ NdbNodeBitmask busyNodes;
+ busyNodes.clear();
+ const Uint32 lcpNodes = c_lcpState.m_participatingLQH.count();
+
+ bool save = true;
+ LcpState::CurrentFragment curr = c_lcpState.currentFragment;
+
+ while (curr.tableId < ctabFileSize) {
+ TabRecordPtr tabPtr;
+ tabPtr.i = curr.tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if ((tabPtr.p->tabStatus != TabRecord::TS_ACTIVE) ||
+ (tabPtr.p->tabLcpStatus != TabRecord::TLS_ACTIVE)) {
+ curr.tableId++;
+ curr.fragmentId = 0;
+ continue;
+ }//if
+
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, curr.fragmentId, fragPtr);
+
+ ReplicaRecordPtr replicaPtr;
+ for(replicaPtr.i = fragPtr.p->storedReplicas;
+ replicaPtr.i != RNIL ;
+ replicaPtr.i = replicaPtr.p->nextReplica){
+
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = replicaPtr.p->procNode;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ if (replicaPtr.p->lcpOngoingFlag &&
+ replicaPtr.p->lcpIdStarted < lcpId) {
+ jam();
+ //-------------------------------------------------------------------
+ // We have found a replica on a node that performs local checkpoint
+ // that is alive and that have not yet been started.
+ //-------------------------------------------------------------------
+
+ if (nodePtr.p->noOfStartedChkpt < 2) {
+ jam();
+ /**
+ * Send LCP_FRAG_ORD to LQH
+ */
+
+ /**
+ * Mark the replica so with lcpIdStarted == true
+ */
+ replicaPtr.p->lcpIdStarted = lcpId;
+
+ Uint32 i = nodePtr.p->noOfStartedChkpt;
+ nodePtr.p->startedChkpt[i].tableId = tabPtr.i;
+ nodePtr.p->startedChkpt[i].fragId = curr.fragmentId;
+ nodePtr.p->startedChkpt[i].replicaPtr = replicaPtr.i;
+ nodePtr.p->noOfStartedChkpt = i + 1;
+
+ sendLCP_FRAG_ORD(signal, nodePtr.p->startedChkpt[i]);
+ } else if (nodePtr.p->noOfQueuedChkpt < 2) {
+ jam();
+ /**
+ * Put LCP_FRAG_ORD "in queue"
+ */
+
+ /**
+ * Mark the replica so with lcpIdStarted == true
+ */
+ replicaPtr.p->lcpIdStarted = lcpId;
+
+ Uint32 i = nodePtr.p->noOfQueuedChkpt;
+ nodePtr.p->queuedChkpt[i].tableId = tabPtr.i;
+ nodePtr.p->queuedChkpt[i].fragId = curr.fragmentId;
+ nodePtr.p->queuedChkpt[i].replicaPtr = replicaPtr.i;
+ nodePtr.p->noOfQueuedChkpt = i + 1;
+ } else {
+ jam();
+
+ if(save){
+ /**
+ * Stop increasing value on first that was "full"
+ */
+ c_lcpState.currentFragment = curr;
+ save = false;
+ }
+
+ busyNodes.set(nodePtr.i);
+ if(busyNodes.count() == lcpNodes){
+ /**
+ * There were no possibility to start the local checkpoint
+ * and it was not possible to queue it up. In this case we
+ * stop the start of local checkpoints until the nodes with a
+ * backlog have performed more checkpoints. We will return and
+ * will not continue the process of starting any more checkpoints.
+ */
+ return;
+ }//if
+ }//if
+ }
+ }//while
+ curr.fragmentId++;
+ if (curr.fragmentId >= tabPtr.p->totalfragments) {
+ jam();
+ curr.fragmentId = 0;
+ curr.tableId++;
+ }//if
+ }//while
+
+ sendLastLCP_FRAG_ORD(signal);
+}//Dbdih::startNextChkpt()
+
+void Dbdih::sendLastLCP_FRAG_ORD(Signal* signal)
+{
+ LcpFragOrd * const lcpFragOrd = (LcpFragOrd *)&signal->theData[0];
+ lcpFragOrd->tableId = RNIL;
+ lcpFragOrd->fragmentId = 0;
+ lcpFragOrd->lcpId = SYSFILE->latestLCP_ID;
+ lcpFragOrd->lcpNo = 0;
+ lcpFragOrd->keepGci = c_lcpState.keepGci;
+ lcpFragOrd->lastFragmentFlag = true;
+
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+
+ if(nodePtr.p->noOfQueuedChkpt == 0 &&
+ nodePtr.p->noOfStartedChkpt == 0 &&
+ c_lcpState.m_LAST_LCP_FRAG_ORD.isWaitingFor(nodePtr.i)){
+ jam();
+
+ CRASH_INSERTION(7028);
+
+ /**
+ * Nothing queued or started <=> Complete on that node
+ *
+ */
+ c_lcpState.m_LAST_LCP_FRAG_ORD.clearWaitingFor(nodePtr.i);
+ if(ERROR_INSERTED(7075)){
+ continue;
+ }
+ BlockReference ref = calcLqhBlockRef(nodePtr.i);
+ sendSignal(ref, GSN_LCP_FRAG_ORD, signal,LcpFragOrd::SignalLength, JBB);
+ }
+ }
+ if(ERROR_INSERTED(7075)){
+ if(c_lcpState.m_LAST_LCP_FRAG_ORD.done())
+ CRASH_INSERTION(7075);
+ }
+}//Dbdih::sendLastLCP_FRAGORD()
+
+/* ------------------------------------------------------------------------- */
+/* A FRAGMENT REPLICA HAS COMPLETED EXECUTING ITS LOCAL CHECKPOINT. */
+/* CHECK IF ALL REPLICAS IN THE TABLE HAVE COMPLETED. IF SO STORE THE */
+/* THE TABLE DISTRIBUTION ON DISK. ALSO SEND LCP_REPORT TO ALL OTHER */
+/* NODES SO THAT THEY CAN STORE THE TABLE ONTO DISK AS WELL. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::execLCP_FRAG_REP(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(c_lcpState.lcpStatus != LCP_STATUS_IDLE);
+
+#if 0
+ printLCP_FRAG_REP(stdout,
+ signal->getDataPtr(),
+ signal->length(), number());
+#endif
+
+ LcpFragRep * const lcpReport = (LcpFragRep *)&signal->theData[0];
+ Uint32 nodeId = lcpReport->nodeId;
+ Uint32 tableId = lcpReport->tableId;
+ Uint32 fragId = lcpReport->fragId;
+
+ jamEntry();
+
+ CRASH_INSERTION2(7025, isMaster());
+ CRASH_INSERTION2(7016, !isMaster());
+
+ bool fromTimeQueue = (signal->senderBlockRef() == reference());
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if(tabPtr.p->tabCopyStatus != TabRecord::CS_IDLE) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ // If the table is currently copied to disk we also
+ // stop already here to avoid strange half-way updates
+ // of the table data structures.
+ /*-----------------------------------------------------------------------*/
+ /*
+ We need to send this signal without a delay since we have discovered
+ that we have run out of space in the short time queue. This problem
+ is very erunlikely to happen but it has and it results in a node crash.
+ This should be considered a "quick fix" and not a permanent solution.
+ A cleaner/better way would be to check the time queue if it is full or
+ not before sending this signal.
+ */
+ sendSignal(reference(), GSN_LCP_FRAG_REP, signal, signal->length(), JBB);
+ /* Kept here for reference
+ sendSignalWithDelay(reference(), GSN_LCP_FRAG_REP,
+ signal, 20, signal->length());
+ */
+
+ if(!fromTimeQueue){
+ c_lcpState.noOfLcpFragRepOutstanding++;
+ }
+
+ return;
+ }//if
+
+ if(fromTimeQueue){
+ jam();
+
+ ndbrequire(c_lcpState.noOfLcpFragRepOutstanding > 0);
+ c_lcpState.noOfLcpFragRepOutstanding--;
+ }
+
+ bool tableDone = reportLcpCompletion(lcpReport);
+
+ if(tableDone){
+ jam();
+
+ if(tabPtr.p->tabStatus == TabRecord::TS_DROPPING){
+ jam();
+ ndbout_c("TS_DROPPING - Neglecting to save Table: %d Frag: %d - ",
+ tableId,
+ fragId);
+ } else {
+ jam();
+ /**
+ * Write table description to file
+ */
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_WRITING_TO_FILE;
+ tabPtr.p->tabCopyStatus = TabRecord::CS_LCP_READ_TABLE;
+ tabPtr.p->tabUpdateState = TabRecord::US_LOCAL_CHECKPOINT;
+ signal->theData[0] = DihContinueB::ZPACK_TABLE_INTO_PAGES;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+
+ checkLcpAllTablesDoneInLqh();
+ }
+ }
+
+#ifdef VM_TRACE
+ /* --------------------------------------------------------------------- */
+ // REPORT that local checkpoint have completed this fragment.
+ /* --------------------------------------------------------------------- */
+ signal->theData[0] = NDB_LE_LCPFragmentCompleted;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tableId;
+ signal->theData[3] = fragId;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+#endif
+
+ bool ok = false;
+ switch(c_lcpMasterTakeOverState.state){
+ case LMTOS_IDLE:
+ ok = true;
+ jam();
+ /**
+ * Fall through
+ */
+ break;
+ case LMTOS_WAIT_EMPTY_LCP: // LCP Take over waiting for EMPTY_LCPCONF
+ jam();
+ return;
+ case LMTOS_WAIT_LCP_FRAG_REP:
+ jam();
+ checkEmptyLcpComplete(signal);
+ return;
+ case LMTOS_INITIAL:
+ case LMTOS_ALL_IDLE:
+ case LMTOS_ALL_ACTIVE:
+ case LMTOS_LCP_CONCLUDING:
+ case LMTOS_COPY_ONGOING:
+ ndbrequire(false);
+ }
+ ndbrequire(ok);
+
+ /* ----------------------------------------------------------------------- */
+ // Check if there are more LCP's to start up.
+ /* ----------------------------------------------------------------------- */
+ if(isMaster()){
+ jam();
+
+ /**
+ * Remove from "running" array
+ */
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ const Uint32 outstanding = nodePtr.p->noOfStartedChkpt;
+ ndbrequire(outstanding > 0);
+ if(nodePtr.p->startedChkpt[0].tableId != tableId ||
+ nodePtr.p->startedChkpt[0].fragId != fragId){
+ jam();
+ ndbrequire(outstanding > 1);
+ ndbrequire(nodePtr.p->startedChkpt[1].tableId == tableId);
+ ndbrequire(nodePtr.p->startedChkpt[1].fragId == fragId);
+ } else {
+ jam();
+ nodePtr.p->startedChkpt[0] = nodePtr.p->startedChkpt[1];
+ }
+ nodePtr.p->noOfStartedChkpt--;
+ checkStartMoreLcp(signal, nodeId);
+ }
+}
+
+bool
+Dbdih::checkLcpAllTablesDoneInLqh(){
+ TabRecordPtr tabPtr;
+
+ /**
+ * Check if finished with all tables
+ */
+ for (tabPtr.i = 0; tabPtr.i < ctabFileSize; tabPtr.i++) {
+ jam();
+ ptrAss(tabPtr, tabRecord);
+ if ((tabPtr.p->tabStatus == TabRecord::TS_ACTIVE) &&
+ (tabPtr.p->tabLcpStatus == TabRecord::TLS_ACTIVE)) {
+ jam();
+ /**
+ * Nope, not finished with all tables
+ */
+ return false;
+ }//if
+ }//for
+
+ CRASH_INSERTION2(7026, isMaster());
+ CRASH_INSERTION2(7017, !isMaster());
+
+ c_lcpState.setLcpStatus(LCP_TAB_COMPLETED, __LINE__);
+ return true;
+}
+
+void Dbdih::findReplica(ReplicaRecordPtr& replicaPtr,
+ Fragmentstore* fragPtrP, Uint32 nodeId)
+{
+ replicaPtr.i = fragPtrP->storedReplicas;
+ while(replicaPtr.i != RNIL){
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if (replicaPtr.p->procNode == nodeId) {
+ jam();
+ return;
+ } else {
+ jam();
+ replicaPtr.i = replicaPtr.p->nextReplica;
+ }//if
+ };
+
+#ifdef VM_TRACE
+ ndbout_c("Fragment Replica(node=%d) not found", nodeId);
+ replicaPtr.i = fragPtrP->oldStoredReplicas;
+ while(replicaPtr.i != RNIL){
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ if (replicaPtr.p->procNode == nodeId) {
+ jam();
+ break;
+ } else {
+ jam();
+ replicaPtr.i = replicaPtr.p->nextReplica;
+ }//if
+ };
+ if(replicaPtr.i != RNIL){
+ ndbout_c("...But was found in oldStoredReplicas");
+ } else {
+ ndbout_c("...And wasn't found in oldStoredReplicas");
+ }
+#endif
+ ndbrequire(false);
+}//Dbdih::findReplica()
+
+/**
+ * Return true if table is all fragment replicas have been checkpointed
+ * to disk (in all LQHs)
+ * false otherwise
+ */
+bool
+Dbdih::reportLcpCompletion(const LcpFragRep* lcpReport)
+{
+ Uint32 lcpNo = lcpReport->lcpNo;
+ Uint32 lcpId = lcpReport->lcpId;
+ Uint32 maxGciStarted = lcpReport->maxGciStarted;
+ Uint32 maxGciCompleted = lcpReport->maxGciCompleted;
+ Uint32 tableId = lcpReport->tableId;
+ Uint32 fragId = lcpReport->fragId;
+ Uint32 nodeId = lcpReport->nodeId;
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+
+ ReplicaRecordPtr replicaPtr;
+ findReplica(replicaPtr, fragPtr.p, nodeId);
+
+ ndbrequire(replicaPtr.p->lcpOngoingFlag == true);
+ if(lcpNo != replicaPtr.p->nextLcp){
+ ndbout_c("lcpNo = %d replicaPtr.p->nextLcp = %d",
+ lcpNo, replicaPtr.p->nextLcp);
+ ndbrequire(false);
+ }
+ ndbrequire(lcpNo == replicaPtr.p->nextLcp);
+ ndbrequire(lcpNo < MAX_LCP_STORED);
+ ndbrequire(replicaPtr.p->lcpId[lcpNo] != lcpId);
+
+ replicaPtr.p->lcpIdStarted = lcpId;
+ replicaPtr.p->lcpOngoingFlag = false;
+
+ removeOldCrashedReplicas(replicaPtr);
+ replicaPtr.p->lcpId[lcpNo] = lcpId;
+ replicaPtr.p->lcpStatus[lcpNo] = ZVALID;
+ replicaPtr.p->maxGciStarted[lcpNo] = maxGciStarted;
+ gth(maxGciStarted + 1, 0);
+ replicaPtr.p->maxGciCompleted[lcpNo] = maxGciCompleted;
+ replicaPtr.p->nextLcp = nextLcpNo(replicaPtr.p->nextLcp);
+
+ ndbrequire(fragPtr.p->noLcpReplicas > 0);
+ fragPtr.p->noLcpReplicas --;
+
+ if(fragPtr.p->noLcpReplicas > 0){
+ jam();
+ return false;
+ }
+
+ for (Uint32 fid = 0; fid < tabPtr.p->totalfragments; fid++) {
+ jam();
+ getFragstore(tabPtr.p, fid, fragPtr);
+ if (fragPtr.p->noLcpReplicas > 0){
+ jam();
+ /* ----------------------------------------------------------------- */
+ // Not all fragments in table have been checkpointed.
+ /* ----------------------------------------------------------------- */
+ if(0)
+ ndbout_c("reportLcpCompletion: fragment %d not ready", fid);
+ return false;
+ }//if
+ }//for
+ return true;
+}//Dbdih::reportLcpCompletion()
+
+void Dbdih::checkStartMoreLcp(Signal* signal, Uint32 nodeId)
+{
+ ndbrequire(isMaster());
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ ndbrequire(nodePtr.p->noOfStartedChkpt < 2);
+
+ if (nodePtr.p->noOfQueuedChkpt > 0) {
+ jam();
+ nodePtr.p->noOfQueuedChkpt--;
+ Uint32 i = nodePtr.p->noOfStartedChkpt;
+ nodePtr.p->startedChkpt[i] = nodePtr.p->queuedChkpt[0];
+ nodePtr.p->queuedChkpt[0] = nodePtr.p->queuedChkpt[1];
+ //-------------------------------------------------------------------
+ // We can send a LCP_FRAGORD to the node ordering it to perform a
+ // local checkpoint on this fragment replica.
+ //-------------------------------------------------------------------
+ nodePtr.p->noOfStartedChkpt = i + 1;
+
+ sendLCP_FRAG_ORD(signal, nodePtr.p->startedChkpt[i]);
+ }
+
+ /* ----------------------------------------------------------------------- */
+ // When there are no more outstanding LCP reports and there are no one queued
+ // in at least one node, then we are ready to make sure all nodes have at
+ // least two outstanding LCP requests per node and at least two queued for
+ // sending.
+ /* ----------------------------------------------------------------------- */
+ startNextChkpt(signal);
+}//Dbdih::checkStartMoreLcp()
+
+void
+Dbdih::sendLCP_FRAG_ORD(Signal* signal,
+ NodeRecord::FragmentCheckpointInfo info){
+
+ ReplicaRecordPtr replicaPtr;
+ replicaPtr.i = info.replicaPtr;
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+
+ BlockReference ref = calcLqhBlockRef(replicaPtr.p->procNode);
+
+ LcpFragOrd * const lcpFragOrd = (LcpFragOrd *)&signal->theData[0];
+ lcpFragOrd->tableId = info.tableId;
+ lcpFragOrd->fragmentId = info.fragId;
+ lcpFragOrd->lcpId = SYSFILE->latestLCP_ID;
+ lcpFragOrd->lcpNo = replicaPtr.p->nextLcp;
+ lcpFragOrd->keepGci = c_lcpState.keepGci;
+ lcpFragOrd->lastFragmentFlag = false;
+ sendSignal(ref, GSN_LCP_FRAG_ORD, signal, LcpFragOrd::SignalLength, JBB);
+}
+
+void Dbdih::checkLcpCompletedLab(Signal* signal)
+{
+ if(c_lcpState.lcpStatus < LCP_TAB_COMPLETED){
+ jam();
+ return;
+ }
+
+ TabRecordPtr tabPtr;
+ for (tabPtr.i = 0; tabPtr.i < ctabFileSize; tabPtr.i++) {
+ jam();
+ ptrAss(tabPtr, tabRecord);
+ if (tabPtr.p->tabStatus == TabRecord::TS_ACTIVE) {
+ if (tabPtr.p->tabLcpStatus != TabRecord::TLS_COMPLETED) {
+ jam();
+ return;
+ }//if
+ }//if
+ }//for
+
+ CRASH_INSERTION2(7027, isMaster());
+ CRASH_INSERTION2(7018, !isMaster());
+
+ if(c_lcpState.lcpStatus == LCP_TAB_COMPLETED){
+ /**
+ * We'r done
+ */
+ c_lcpState.setLcpStatus(LCP_TAB_SAVED, __LINE__);
+ sendLCP_COMPLETE_REP(signal);
+ return;
+ }
+
+ ndbrequire(c_lcpState.lcpStatus == LCP_TAB_SAVED);
+ allNodesLcpCompletedLab(signal);
+ return;
+}//Dbdih::checkLcpCompletedLab()
+
+void
+Dbdih::sendLCP_COMPLETE_REP(Signal* signal){
+ jam();
+ LcpCompleteRep * rep = (LcpCompleteRep*)signal->getDataPtrSend();
+ rep->nodeId = getOwnNodeId();
+ rep->lcpId = SYSFILE->latestLCP_ID;
+ rep->blockNo = DBDIH;
+
+ sendSignal(c_lcpState.m_masterLcpDihRef, GSN_LCP_COMPLETE_REP, signal,
+ LcpCompleteRep::SignalLength, JBB);
+}
+
+/*-------------------------------------------------------------------------- */
+/* COMP_LCP_ROUND A LQH HAS COMPLETED A LOCAL CHECKPOINT */
+/*------------------------------------------------------------------------- */
+void Dbdih::execLCP_COMPLETE_REP(Signal* signal)
+{
+ jamEntry();
+
+#if 0
+ ndbout_c("LCP_COMPLETE_REP");
+ printLCP_COMPLETE_REP(stdout,
+ signal->getDataPtr(),
+ signal->length(), number());
+#endif
+
+ LcpCompleteRep * rep = (LcpCompleteRep*)signal->getDataPtr();
+ Uint32 lcpId = rep->lcpId;
+ Uint32 nodeId = rep->nodeId;
+ Uint32 blockNo = rep->blockNo;
+
+ if(c_lcpMasterTakeOverState.state > LMTOS_WAIT_LCP_FRAG_REP){
+ jam();
+ /**
+ * Don't allow LCP_COMPLETE_REP to arrive during
+ * LCP master take over
+ */
+ ndbrequire(isMaster());
+ ndbrequire(blockNo == DBDIH);
+ sendSignalWithDelay(reference(), GSN_LCP_COMPLETE_REP, signal, 100,
+ signal->length());
+ return;
+ }
+
+ ndbrequire(c_lcpState.lcpStatus != LCP_STATUS_IDLE);
+
+ switch(blockNo){
+ case DBLQH:
+ jam();
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH.clearWaitingFor(nodeId);
+ ndbrequire(!c_lcpState.m_LAST_LCP_FRAG_ORD.isWaitingFor(nodeId));
+ break;
+ case DBDIH:
+ jam();
+ ndbrequire(isMaster());
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH.clearWaitingFor(nodeId);
+ break;
+ case 0:
+ jam();
+ ndbrequire(!isMaster());
+ ndbrequire(c_lcpState.m_LCP_COMPLETE_REP_From_Master_Received == false);
+ c_lcpState.m_LCP_COMPLETE_REP_From_Master_Received = true;
+ break;
+ default:
+ ndbrequire(false);
+ }
+ ndbrequire(lcpId == SYSFILE->latestLCP_ID);
+
+ allNodesLcpCompletedLab(signal);
+ return;
+}
+
+void Dbdih::allNodesLcpCompletedLab(Signal* signal)
+{
+ jam();
+
+ if (c_lcpState.lcpStatus != LCP_TAB_SAVED) {
+ jam();
+ /**
+ * We have not sent LCP_COMPLETE_REP to master DIH yet
+ */
+ return;
+ }//if
+
+ if (!c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH.done()){
+ jam();
+ return;
+ }
+
+ if (!c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH.done()){
+ jam();
+ return;
+ }
+
+ if (!isMaster() &&
+ c_lcpState.m_LCP_COMPLETE_REP_From_Master_Received == false){
+ jam();
+ /**
+ * Wait until master DIH has signaled lcp is complete
+ */
+ return;
+ }
+
+ if(c_lcpMasterTakeOverState.state != LMTOS_IDLE){
+ jam();
+#ifdef VM_TRACE
+ ndbout_c("Exiting from allNodesLcpCompletedLab");
+#endif
+ return;
+ }
+
+
+ /*------------------------------------------------------------------------ */
+ /* WE HAVE NOW COMPLETED A LOCAL CHECKPOINT. WE ARE NOW READY TO WAIT */
+ /* FOR THE NEXT LOCAL CHECKPOINT. SEND WITHOUT TIME-OUT SINCE IT MIGHT */
+ /* BE TIME TO START THE NEXT LOCAL CHECKPOINT IMMEDIATELY. */
+ /* CLEAR BIT 3 OF SYSTEM RESTART BITS TO INDICATE THAT THERE IS NO */
+ /* LOCAL CHECKPOINT ONGOING. THIS WILL BE WRITTEN AT SOME LATER TIME */
+ /* DURING A GLOBAL CHECKPOINT. IT IS NOT NECESSARY TO WRITE IT */
+ /* IMMEDIATELY. WE WILL ALSO CLEAR BIT 2 OF SYSTEM RESTART BITS IF ALL */
+ /* CURRENTLY ACTIVE NODES COMPLETED THE LOCAL CHECKPOINT. */
+ /*------------------------------------------------------------------------ */
+ CRASH_INSERTION(7019);
+ signal->setTrace(0);
+
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+ setLcpActiveStatusEnd();
+ Sysfile::clearLCPOngoing(SYSFILE->systemRestartBits);
+
+ if(!isMaster()){
+ jam();
+ /**
+ * We're not master, be content
+ */
+ return;
+ }
+
+ // Send LCP_COMPLETE_REP to all other nodes
+ // allowing them to set their lcpStatus to LCP_STATUS_IDLE
+ LcpCompleteRep * rep = (LcpCompleteRep*)signal->getDataPtrSend();
+ rep->nodeId = getOwnNodeId();
+ rep->lcpId = SYSFILE->latestLCP_ID;
+ rep->blockNo = 0; // 0 = Sent from master
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = cfirstAliveNode;
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.i != cownNodeId){
+ BlockReference ref = calcDihBlockRef(nodePtr.i);
+ sendSignal(ref, GSN_LCP_COMPLETE_REP, signal,
+ LcpCompleteRep::SignalLength, JBB);
+ }
+ nodePtr.i = nodePtr.p->nextNode;
+ } while (nodePtr.i != RNIL);
+
+
+ jam();
+ /***************************************************************************/
+ // Report the event that a local checkpoint has completed.
+ /***************************************************************************/
+ signal->theData[0] = NDB_LE_LocalCheckpointCompleted; //Event type
+ signal->theData[1] = SYSFILE->latestLCP_ID;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ /**
+ * Start checking for next LCP
+ */
+ checkLcpStart(signal, __LINE__);
+
+ if (cwaitLcpSr == true) {
+ jam();
+ cwaitLcpSr = false;
+ ndbsttorry10Lab(signal, __LINE__);
+ return;
+ }//if
+
+ if (c_nodeStartMaster.blockLcp == true) {
+ jam();
+ lcpBlockedLab(signal);
+ return;
+ }//if
+ return;
+}//Dbdih::allNodesLcpCompletedLab()
+
+/******************************************************************************/
+/* ********** TABLE UPDATE MODULE *************/
+/* ****************************************************************************/
+/* ------------------------------------------------------------------------- */
+/* THIS MODULE IS USED TO UPDATE THE TABLE DESCRIPTION. IT STARTS BY */
+/* CREATING THE FIRST TABLE FILE, THEN UPDATES THIS FILE AND CLOSES IT.*/
+/* AFTER THAT THE SAME HAPPENS WITH THE SECOND FILE. AFTER THAT THE */
+/* TABLE DISTRIBUTION HAS BEEN UPDATED. */
+/* */
+/* THE REASON FOR CREATING THE FILE AND NOT OPENING IT IS TO ENSURE */
+/* THAT WE DO NOT GET A MIX OF OLD AND NEW INFORMATION IN THE FILE IN */
+/* ERROR SITUATIONS. */
+/* ------------------------------------------------------------------------- */
+void Dbdih::tableUpdateLab(Signal* signal, TabRecordPtr tabPtr) {
+ FileRecordPtr filePtr;
+ filePtr.i = tabPtr.p->tabFile[0];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ createFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::TABLE_CREATE;
+ return;
+}//Dbdih::tableUpdateLab()
+
+void Dbdih::tableCreateLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ writeTabfile(signal, tabPtr.p, filePtr);
+ filePtr.p->reqStatus = FileRecord::TABLE_WRITE;
+ return;
+}//Dbdih::tableCreateLab()
+
+void Dbdih::tableWriteLab(Signal* signal, FileRecordPtr filePtr)
+{
+ closeFile(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::TABLE_CLOSE;
+ return;
+}//Dbdih::tableWriteLab()
+
+void Dbdih::tableCloseLab(Signal* signal, FileRecordPtr filePtr)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = filePtr.p->tabRef;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ if (filePtr.i == tabPtr.p->tabFile[0]) {
+ jam();
+ filePtr.i = tabPtr.p->tabFile[1];
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ createFileRw(signal, filePtr);
+ filePtr.p->reqStatus = FileRecord::TABLE_CREATE;
+ return;
+ }//if
+ switch (tabPtr.p->tabUpdateState) {
+ case TabRecord::US_LOCAL_CHECKPOINT:
+ jam();
+ releaseTabPages(tabPtr.i);
+ signal->theData[0] = DihContinueB::ZCHECK_LCP_COMPLETED;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 1, JBB);
+
+ tabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_COMPLETED;
+ return;
+ break;
+ case TabRecord::US_REMOVE_NODE:
+ jam();
+ releaseTabPages(tabPtr.i);
+ for (Uint32 fragId = 0; fragId < tabPtr.p->totalfragments; fragId++) {
+ jam();
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragId, fragPtr);
+ updateNodeInfo(fragPtr);
+ }//for
+ tabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+ if (tabPtr.p->tabLcpStatus == TabRecord::TLS_WRITING_TO_FILE) {
+ jam();
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_COMPLETED;
+ signal->theData[0] = DihContinueB::ZCHECK_LCP_COMPLETED;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 1, JBB);
+ }//if
+ signal->theData[0] = DihContinueB::ZREMOVE_NODE_FROM_TABLE;
+ signal->theData[1] = tabPtr.p->tabRemoveNode;
+ signal->theData[2] = tabPtr.i + 1;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ break;
+ case TabRecord::US_INVALIDATE_NODE_LCP:
+ jam();
+ releaseTabPages(tabPtr.i);
+ tabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+
+ signal->theData[0] = DihContinueB::ZINVALIDATE_NODE_LCP;
+ signal->theData[1] = tabPtr.p->tabRemoveNode;
+ signal->theData[2] = tabPtr.i + 1;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ case TabRecord::US_COPY_TAB_REQ:
+ jam();
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+ copyTabReq_complete(signal, tabPtr);
+ return;
+ break;
+ case TabRecord::US_ADD_TABLE_MASTER:
+ jam();
+ releaseTabPages(tabPtr.i);
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+ signal->theData[0] = DihContinueB::ZDIH_ADD_TABLE_MASTER;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ case TabRecord::US_ADD_TABLE_SLAVE:
+ jam();
+ releaseTabPages(tabPtr.i);
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+ signal->theData[0] = DihContinueB::ZDIH_ADD_TABLE_SLAVE;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+}//Dbdih::tableCloseLab()
+
+/**
+ * GCP stop detected,
+ * send SYSTEM_ERROR to all other alive nodes
+ */
+void Dbdih::crashSystemAtGcpStop(Signal* signal){
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ const BlockReference ref =
+ numberToRef(refToBlock(cntrlblockref), nodePtr.i);
+ SystemError * const sysErr = (SystemError*)&signal->theData[0];
+ sysErr->errorCode = SystemError::GCPStopDetected;
+ sysErr->errorRef = reference();
+ sysErr->data1 = cgcpStatus;
+ sysErr->data2 = cgcpOrderBlocked;
+ sendSignal(ref, GSN_SYSTEM_ERROR, signal,
+ SystemError::SignalLength, JBA);
+ }//if
+ }//for
+ return;
+}//Dbdih::crashSystemAtGcpStop()
+
+/*************************************************************************/
+/* */
+/* MODULE: ALLOCPAGE */
+/* DESCRIPTION: THE SUBROUTINE IS CALLED WITH POINTER TO PAGE */
+/* RECORD. A PAGE RECORD IS TAKEN FROM */
+/* THE FREE PAGE LIST */
+/*************************************************************************/
+void Dbdih::allocpage(PageRecordPtr& pagePtr)
+{
+ ndbrequire(cfirstfreepage != RNIL);
+ pagePtr.i = cfirstfreepage;
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+ cfirstfreepage = pagePtr.p->nextfreepage;
+ pagePtr.p->nextfreepage = RNIL;
+}//Dbdih::allocpage()
+
+/*************************************************************************/
+/* */
+/* MODULE: ALLOC_STORED_REPLICA */
+/* DESCRIPTION: THE SUBROUTINE IS CALLED TO GET A REPLICA RECORD, */
+/* TO INITIALISE IT AND TO LINK IT INTO THE FRAGMENT */
+/* STORE RECORD. USED FOR STORED REPLICAS. */
+/*************************************************************************/
+void Dbdih::allocStoredReplica(FragmentstorePtr fragPtr,
+ ReplicaRecordPtr& newReplicaPtr,
+ Uint32 nodeId)
+{
+ Uint32 i;
+ ReplicaRecordPtr arrReplicaPtr;
+ ReplicaRecordPtr arrPrevReplicaPtr;
+
+ seizeReplicaRec(newReplicaPtr);
+ for (i = 0; i < MAX_LCP_STORED; i++) {
+ newReplicaPtr.p->maxGciCompleted[i] = 0;
+ newReplicaPtr.p->maxGciStarted[i] = 0;
+ newReplicaPtr.p->lcpId[i] = 0;
+ newReplicaPtr.p->lcpStatus[i] = ZINVALID;
+ }//for
+ newReplicaPtr.p->noCrashedReplicas = 0;
+ newReplicaPtr.p->initialGci = currentgcp;
+ for (i = 0; i < 8; i++) {
+ newReplicaPtr.p->replicaLastGci[i] = (Uint32)-1;
+ newReplicaPtr.p->createGci[i] = 0;
+ }//for
+ newReplicaPtr.p->createGci[0] = currentgcp;
+ ndbrequire(currentgcp != 0xF1F1F1F1);
+ newReplicaPtr.p->nextLcp = 0;
+ newReplicaPtr.p->procNode = nodeId;
+ newReplicaPtr.p->lcpOngoingFlag = false;
+ newReplicaPtr.p->lcpIdStarted = 0;
+
+ arrPrevReplicaPtr.i = RNIL;
+ arrReplicaPtr.i = fragPtr.p->storedReplicas;
+ while (arrReplicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(arrReplicaPtr, creplicaFileSize, replicaRecord);
+ arrPrevReplicaPtr = arrReplicaPtr;
+ arrReplicaPtr.i = arrReplicaPtr.p->nextReplica;
+ }//while
+ if (arrPrevReplicaPtr.i == RNIL) {
+ jam();
+ fragPtr.p->storedReplicas = newReplicaPtr.i;
+ } else {
+ jam();
+ arrPrevReplicaPtr.p->nextReplica = newReplicaPtr.i;
+ }//if
+ fragPtr.p->noStoredReplicas++;
+}//Dbdih::allocStoredReplica()
+
+/*************************************************************************/
+/* CALCULATE HOW MANY HOT SPARES THAT ARE TO BE ASSIGNED IN THIS SYSTEM */
+/*************************************************************************/
+void Dbdih::calculateHotSpare()
+{
+ Uint32 tchsTmp;
+ Uint32 tchsNoNodes;
+
+ switch (cnoReplicas) {
+ case 1:
+ jam();
+ cnoHotSpare = 0;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ jam();
+ if (csystemnodes > cnoReplicas) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* WITH MORE NODES THAN REPLICAS WE WILL ALWAYS USE AT LEAST ONE HOT */
+ /* SPARE IF THAT HAVE BEEN REQUESTED BY THE CONFIGURATION FILE. THE */
+ /* NUMBER OF NODES TO BE USED FOR NORMAL OPERATION IS ALWAYS */
+ /* A MULTIPLE OF THE NUMBER OF REPLICAS SINCE WE WILL ORGANISE NODES */
+ /* INTO NODE GROUPS. THE REMAINING NODES WILL BE HOT SPARE NODES. */
+ /* --------------------------------------------------------------------- */
+ if ((csystemnodes - cnoReplicas) >= cminHotSpareNodes) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ // We set the minimum number of hot spares according to users request
+ // through the configuration file.
+ /* --------------------------------------------------------------------- */
+ tchsNoNodes = csystemnodes - cminHotSpareNodes;
+ cnoHotSpare = cminHotSpareNodes;
+ } else if (cminHotSpareNodes > 0) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ // The user requested at least one hot spare node and we will support him
+ // in that.
+ /* --------------------------------------------------------------------- */
+ tchsNoNodes = csystemnodes - 1;
+ cnoHotSpare = 1;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------- */
+ // The user did not request any hot spare nodes so in this case we will
+ // only use hot spare nodes if the number of nodes is such that we cannot
+ // use all nodes as normal nodes.
+ /* --------------------------------------------------------------------- */
+ tchsNoNodes = csystemnodes;
+ cnoHotSpare = 0;
+ }//if
+ } else {
+ jam();
+ /* --------------------------------------------------------------------- */
+ // We only have enough to support the replicas. We will not have any hot
+ // spares.
+ /* --------------------------------------------------------------------- */
+ tchsNoNodes = csystemnodes;
+ cnoHotSpare = 0;
+ }//if
+ tchsTmp = tchsNoNodes - (cnoReplicas * (tchsNoNodes / cnoReplicas));
+ cnoHotSpare = cnoHotSpare + tchsTmp;
+ break;
+ default:
+ jam();
+ progError(0, 0);
+ break;
+ }//switch
+}//Dbdih::calculateHotSpare()
+
+/*************************************************************************/
+/* CHECK IF THE NODE CRASH IS TO ESCALATE INTO A SYSTEM CRASH. WE COULD */
+/* DO THIS BECAUSE ALL REPLICAS OF SOME FRAGMENT ARE LOST. WE COULD ALSO */
+/* DO IT AFTER MANY NODE FAILURES THAT MAKE IT VERY DIFFICULT TO RESTORE */
+/* DATABASE AFTER A SYSTEM CRASH. IT MIGHT EVEN BE IMPOSSIBLE AND THIS */
+/* MUST BE AVOIDED EVEN MORE THAN AVOIDING SYSTEM CRASHES. */
+/*************************************************************************/
+void Dbdih::checkEscalation()
+{
+ Uint32 TnodeGroup[MAX_NDB_NODES];
+ NodeRecordPtr nodePtr;
+ Uint32 i;
+ for (i = 0; i < MAX_NDB_NODES; i++) {
+ TnodeGroup[i] = ZFALSE;
+ }//for
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE &&
+ nodePtr.p->activeStatus == Sysfile::NS_Active){
+ ndbrequire(nodePtr.p->nodeGroup < MAX_NDB_NODES);
+ TnodeGroup[nodePtr.p->nodeGroup] = ZTRUE;
+ }
+ }
+ for (i = 0; i < cnoOfNodeGroups; i++) {
+ jam();
+ if (TnodeGroup[i] == ZFALSE) {
+ jam();
+ progError(__LINE__, ERR_SYSTEM_ERROR, "Lost node group");
+ }//if
+ }//for
+}//Dbdih::checkEscalation()
+
+/*************************************************************************/
+/* */
+/* MODULE: CHECK_KEEP_GCI */
+/* DESCRIPTION: CHECK FOR MINIMUM GCI RESTORABLE WITH NEW LOCAL */
+/* CHECKPOINT. */
+/*************************************************************************/
+void Dbdih::checkKeepGci(Uint32 replicaStartIndex)
+{
+ ReplicaRecordPtr ckgReplicaPtr;
+ ckgReplicaPtr.i = replicaStartIndex;
+ while (ckgReplicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(ckgReplicaPtr, creplicaFileSize, replicaRecord);
+ Uint32 keepGci;
+ Uint32 oldestRestorableGci;
+ findMinGci(ckgReplicaPtr, keepGci, oldestRestorableGci);
+ if (keepGci < c_lcpState.keepGci) {
+ jam();
+ /* ------------------------------------------------------------------- */
+ /* WE MUST KEEP LOG RECORDS SO THAT WE CAN USE ALL LOCAL CHECKPOINTS */
+ /* THAT ARE AVAILABLE. THUS WE NEED TO CALCULATE THE MINIMUM OVER ALL */
+ /* FRAGMENTS. */
+ /* ------------------------------------------------------------------- */
+ c_lcpState.keepGci = keepGci;
+ }//if
+ if (oldestRestorableGci > c_lcpState.oldestRestorableGci) {
+ jam();
+ c_lcpState.oldestRestorableGci = oldestRestorableGci;
+ ndbrequire(((int)c_lcpState.oldestRestorableGci) >= 0);
+ }//if
+ ckgReplicaPtr.i = ckgReplicaPtr.p->nextReplica;
+ }//while
+}//Dbdih::checkKeepGci()
+
+void Dbdih::closeFile(Signal* signal, FileRecordPtr filePtr)
+{
+ signal->theData[0] = filePtr.p->fileRef;
+ signal->theData[1] = reference();
+ signal->theData[2] = filePtr.i;
+ signal->theData[3] = ZCLOSE_NO_DELETE;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+}//Dbdih::closeFile()
+
+void Dbdih::closeFileDelete(Signal* signal, FileRecordPtr filePtr)
+{
+ signal->theData[0] = filePtr.p->fileRef;
+ signal->theData[1] = reference();
+ signal->theData[2] = filePtr.i;
+ signal->theData[3] = ZCLOSE_DELETE;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+}//Dbdih::closeFileDelete()
+
+void Dbdih::createFileRw(Signal* signal, FileRecordPtr filePtr)
+{
+ signal->theData[0] = reference();
+ signal->theData[1] = filePtr.i;
+ signal->theData[2] = filePtr.p->fileName[0];
+ signal->theData[3] = filePtr.p->fileName[1];
+ signal->theData[4] = filePtr.p->fileName[2];
+ signal->theData[5] = filePtr.p->fileName[3];
+ signal->theData[6] = ZCREATE_READ_WRITE;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+}//Dbdih::createFileRw()
+
+void Dbdih::emptyverificbuffer(Signal* signal, bool aContinueB)
+{
+ if(cfirstVerifyQueue == RNIL){
+ jam();
+ return;
+ }//if
+ ApiConnectRecordPtr localApiConnectptr;
+ if(getBlockCommit() == false){
+ jam();
+ ndbrequire(cverifyQueueCounter > 0);
+ cverifyQueueCounter--;
+ localApiConnectptr.i = cfirstVerifyQueue;
+ ptrCheckGuard(localApiConnectptr, capiConnectFileSize, apiConnectRecord);
+ ndbrequire(localApiConnectptr.p->apiGci <= currentgcp);
+ cfirstVerifyQueue = localApiConnectptr.p->nextApi;
+ if (cfirstVerifyQueue == RNIL) {
+ jam();
+ ndbrequire(cverifyQueueCounter == 0);
+ clastVerifyQueue = RNIL;
+ }//if
+ signal->theData[0] = localApiConnectptr.i;
+ signal->theData[1] = currentgcp;
+ sendSignal(clocaltcblockref, GSN_DIVERIFYCONF, signal, 2, JBB);
+ if (aContinueB == true) {
+ jam();
+ //-----------------------------------------------------------------------
+ // This emptying happened as part of a take-out process by continueb signals.
+ // This ensures that we will empty the queue eventually. We will also empty
+ // one item every time we insert one item to ensure that the list doesn't
+ // grow when it is not blocked.
+ //-----------------------------------------------------------------------
+ signal->theData[0] = DihContinueB::ZEMPTY_VERIFY_QUEUE;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 1, JBB);
+ }//if
+ } else {
+ jam();
+ //-----------------------------------------------------------------------
+ // We are blocked so it is no use in continuing the emptying of the
+ // verify buffer. Whenever the block is removed the emptying will
+ // restart.
+ //-----------------------------------------------------------------------
+ }
+ return;
+}//Dbdih::emptyverificbuffer()
+
+/*----------------------------------------------------------------*/
+/* FIND A FREE HOT SPARE IF AVAILABLE AND ALIVE. */
+/*----------------------------------------------------------------*/
+Uint32 Dbdih::findHotSpare()
+{
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ if (nodePtr.p->activeStatus == Sysfile::NS_HotSpare) {
+ jam();
+ return nodePtr.i;
+ }//if
+ }//if
+ }//for
+ return RNIL;
+}//Dbdih::findHotSpare()
+
+/*************************************************************************/
+/* FIND THE NODES FROM WHICH WE CAN EXECUTE THE LOG TO RESTORE THE */
+/* DATA NODE IN A SYSTEM RESTART. */
+/*************************************************************************/
+bool Dbdih::findLogNodes(CreateReplicaRecord* createReplica,
+ FragmentstorePtr fragPtr,
+ Uint32 startGci,
+ Uint32 stopGci)
+{
+ ConstPtr<ReplicaRecord> flnReplicaPtr;
+ flnReplicaPtr.i = createReplica->replicaRec;
+ ptrCheckGuard(flnReplicaPtr, creplicaFileSize, replicaRecord);
+ /* --------------------------------------------------------------------- */
+ /* WE START BY CHECKING IF THE DATA NODE CAN HANDLE THE LOG ALL BY */
+ /* ITSELF. THIS IS THE DESIRED BEHAVIOUR. IF THIS IS NOT POSSIBLE */
+ /* THEN WE SEARCH FOR THE BEST POSSIBLE NODES AMONG THE NODES THAT */
+ /* ARE PART OF THIS SYSTEM RESTART. */
+ /* THIS CAN ONLY BE HANDLED BY THE LAST CRASHED REPLICA. */
+ /* The condition is that the replica was created before or at the */
+ /* time of the starting gci, in addition it must have been alive */
+ /* at the time of the stopping gci. This is checked by two */
+ /* conditions, the first checks replicaLastGci and the second */
+ /* checks that it is also smaller than the last gci the node was */
+ /* involved in. This is necessary to check since createGci is set */
+ /* Last + 1 and sometimes startGci = stopGci + 1 and in that case */
+ /* it could happen that replicaLastGci is set to -1 with CreateGci */
+ /* set to LastGci + 1. */
+ /* --------------------------------------------------------------------- */
+ arrGuard(flnReplicaPtr.p->noCrashedReplicas, 8);
+ const Uint32 noCrashed = flnReplicaPtr.p->noCrashedReplicas;
+
+ if (!(ERROR_INSERTED(7073) || ERROR_INSERTED(7074))&&
+ (startGci >= flnReplicaPtr.p->createGci[noCrashed]) &&
+ (stopGci <= flnReplicaPtr.p->replicaLastGci[noCrashed]) &&
+ (stopGci <= SYSFILE->lastCompletedGCI[flnReplicaPtr.p->procNode])) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* WE FOUND ALL THE LOG RECORDS NEEDED IN THE DATA NODE. WE WILL */
+ /* USE THOSE. */
+ /* --------------------------------------------------------------------- */
+ createReplica->noLogNodes = 1;
+ createReplica->logStartGci[0] = startGci;
+ createReplica->logStopGci[0] = stopGci;
+ createReplica->logNodeId[0] = flnReplicaPtr.p->procNode;
+ return true;
+ }//if
+ Uint32 logNode = 0;
+ do {
+ Uint32 fblStopGci;
+ jam();
+ if(!findBestLogNode(createReplica,
+ fragPtr,
+ startGci,
+ stopGci,
+ logNode,
+ fblStopGci)){
+ jam();
+ return false;
+ }
+
+ logNode++;
+ if (fblStopGci >= stopGci) {
+ jam();
+ createReplica->noLogNodes = logNode;
+ return true;
+ }//if
+ startGci = fblStopGci + 1;
+ if (logNode >= 4) { // Why??
+ jam();
+ break;
+ }//if
+ } while (1);
+ /* --------------------------------------------------------------------- */
+ /* IT WAS NOT POSSIBLE TO RESTORE THE REPLICA. THIS CAN EITHER BE */
+ /* BECAUSE OF LACKING NODES OR BECAUSE OF A REALLY SERIOUS PROBLEM.*/
+ /* --------------------------------------------------------------------- */
+ return false;
+}//Dbdih::findLogNodes()
+
+/*************************************************************************/
+/* FIND THE BEST POSSIBLE LOG NODE TO EXECUTE THE LOG AS SPECIFIED */
+/* BY THE INPUT PARAMETERS. WE SCAN THROUGH ALL ALIVE REPLICAS. */
+/* THIS MEANS STORED, OLD_STORED */
+/*************************************************************************/
+bool
+Dbdih::findBestLogNode(CreateReplicaRecord* createReplica,
+ FragmentstorePtr fragPtr,
+ Uint32 startGci,
+ Uint32 stopGci,
+ Uint32 logNode,
+ Uint32& fblStopGci)
+{
+ ConstPtr<ReplicaRecord> fblFoundReplicaPtr;
+ ConstPtr<ReplicaRecord> fblReplicaPtr;
+
+ /* --------------------------------------------------------------------- */
+ /* WE START WITH ZERO AS FOUND TO ENSURE THAT FIRST HIT WILL BE */
+ /* BETTER. */
+ /* --------------------------------------------------------------------- */
+ fblStopGci = 0;
+ fblReplicaPtr.i = fragPtr.p->storedReplicas;
+ while (fblReplicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(fblReplicaPtr, creplicaFileSize, replicaRecord);
+ if (checkNodeAlive(fblReplicaPtr.p->procNode)) {
+ jam();
+ Uint32 fliStopGci = findLogInterval(fblReplicaPtr, startGci);
+ if (fliStopGci > fblStopGci) {
+ jam();
+ fblStopGci = fliStopGci;
+ fblFoundReplicaPtr = fblReplicaPtr;
+ }//if
+ }//if
+ fblReplicaPtr.i = fblReplicaPtr.p->nextReplica;
+ }//while
+ fblReplicaPtr.i = fragPtr.p->oldStoredReplicas;
+ while (fblReplicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(fblReplicaPtr, creplicaFileSize, replicaRecord);
+ if (checkNodeAlive(fblReplicaPtr.p->procNode)) {
+ jam();
+ Uint32 fliStopGci = findLogInterval(fblReplicaPtr, startGci);
+ if (fliStopGci > fblStopGci) {
+ jam();
+ fblStopGci = fliStopGci;
+ fblFoundReplicaPtr = fblReplicaPtr;
+ }//if
+ }//if
+ fblReplicaPtr.i = fblReplicaPtr.p->nextReplica;
+ }//while
+ if (fblStopGci != 0) {
+ jam();
+ ndbrequire(logNode < MAX_LOG_EXEC);
+ createReplica->logNodeId[logNode] = fblFoundReplicaPtr.p->procNode;
+ createReplica->logStartGci[logNode] = startGci;
+ if (fblStopGci >= stopGci) {
+ jam();
+ createReplica->logStopGci[logNode] = stopGci;
+ } else {
+ jam();
+ createReplica->logStopGci[logNode] = fblStopGci;
+ }//if
+ }//if
+
+ return fblStopGci != 0;
+}//Dbdih::findBestLogNode()
+
+Uint32 Dbdih::findLogInterval(ConstPtr<ReplicaRecord> replicaPtr,
+ Uint32 startGci)
+{
+ ndbrequire(replicaPtr.p->noCrashedReplicas <= 8);
+ Uint32 loopLimit = replicaPtr.p->noCrashedReplicas + 1;
+ for (Uint32 i = 0; i < loopLimit; i++) {
+ jam();
+ if (replicaPtr.p->createGci[i] <= startGci) {
+ if (replicaPtr.p->replicaLastGci[i] >= startGci) {
+ jam();
+ return replicaPtr.p->replicaLastGci[i];
+ }//if
+ }//if
+ }//for
+ return 0;
+}//Dbdih::findLogInterval()
+
+/*************************************************************************/
+/* */
+/* MODULE: FIND THE MINIMUM GCI THAT THIS NODE HAS LOG RECORDS FOR.*/
+/*************************************************************************/
+void Dbdih::findMinGci(ReplicaRecordPtr fmgReplicaPtr,
+ Uint32& keepGci,
+ Uint32& oldestRestorableGci)
+{
+ Uint32 nextLcpNo;
+ Uint32 lcpNo;
+ for (Uint32 i = 0; i < MAX_LCP_STORED; i++) {
+ jam();
+ if ((fmgReplicaPtr.p->lcpStatus[i] == ZVALID) &&
+ ((fmgReplicaPtr.p->lcpId[i] + MAX_LCP_STORED) <= (SYSFILE->latestLCP_ID + 1))) {
+ jam();
+ /*--------------------------------------------------------------------*/
+ // We invalidate the checkpoint we are preparing to overwrite.
+ // The LCP id is still the old lcp id,
+ // this is the reason of comparing with lcpId + 1.
+ /*---------------------------------------------------------------------*/
+ fmgReplicaPtr.p->lcpStatus[i] = ZINVALID;
+ }//if
+ }//for
+ keepGci = (Uint32)-1;
+ oldestRestorableGci = 0;
+ nextLcpNo = fmgReplicaPtr.p->nextLcp;
+ lcpNo = fmgReplicaPtr.p->nextLcp;
+ do {
+ ndbrequire(lcpNo < MAX_LCP_STORED);
+ if (fmgReplicaPtr.p->lcpStatus[lcpNo] == ZVALID) {
+ jam();
+ keepGci = fmgReplicaPtr.p->maxGciCompleted[lcpNo];
+ oldestRestorableGci = fmgReplicaPtr.p->maxGciStarted[lcpNo];
+ ndbrequire(((int)oldestRestorableGci) >= 0);
+ return;
+ } else {
+ jam();
+ ndbrequire(fmgReplicaPtr.p->lcpStatus[lcpNo] == ZINVALID);
+ if (fmgReplicaPtr.p->createGci[0] == fmgReplicaPtr.p->initialGci) {
+ jam();
+ /*-------------------------------------------------------------------
+ * WE CAN STILL RESTORE THIS REPLICA WITHOUT ANY LOCAL CHECKPOINTS BY
+ * ONLY USING THE LOG. IF THIS IS NOT POSSIBLE THEN WE REPORT THE LAST
+ * VALID LOCAL CHECKPOINT AS THE MINIMUM GCI RECOVERABLE.
+ *-----------------------------------------------------------------*/
+ keepGci = fmgReplicaPtr.p->createGci[0];
+ }//if
+ }//if
+ lcpNo = prevLcpNo(lcpNo);
+ } while (lcpNo != nextLcpNo);
+ return;
+}//Dbdih::findMinGci()
+
+bool Dbdih::findStartGci(ConstPtr<ReplicaRecord> replicaPtr,
+ Uint32 stopGci,
+ Uint32& startGci,
+ Uint32& lcpNo)
+{
+ lcpNo = replicaPtr.p->nextLcp;
+ const Uint32 startLcpNo = lcpNo;
+ do {
+ lcpNo = prevLcpNo(lcpNo);
+ ndbrequire(lcpNo < MAX_LCP_STORED);
+ if (replicaPtr.p->lcpStatus[lcpNo] == ZVALID) {
+ if (replicaPtr.p->maxGciStarted[lcpNo] < stopGci) {
+ jam();
+ /* ----------------------------------------------------------------- */
+ /* WE HAVE FOUND A USEFUL LOCAL CHECKPOINT THAT CAN BE USED FOR */
+ /* RESTARTING THIS FRAGMENT REPLICA. */
+ /* ----------------------------------------------------------------- */
+ startGci = replicaPtr.p->maxGciCompleted[lcpNo] + 1;
+ return true;
+ }
+ }
+ } while (lcpNo != startLcpNo);
+ /* --------------------------------------------------------------------- */
+ /* NO VALID LOCAL CHECKPOINT WAS AVAILABLE. WE WILL ADD THE */
+ /* FRAGMENT. THUS THE NEXT LCP MUST BE SET TO ZERO. */
+ /* WE MUST EXECUTE THE LOG FROM THE INITIAL GLOBAL CHECKPOINT WHEN */
+ /* THE TABLE WAS CREATED. */
+ /* --------------------------------------------------------------------- */
+ startGci = replicaPtr.p->initialGci;
+ ndbrequire(replicaPtr.p->nextLcp == 0);
+ return false;
+}//Dbdih::findStartGci()
+
+/**************************************************************************/
+/* ---------------------------------------------------------------------- */
+/* FIND A TAKE OVER REPLICA WHICH IS TO BE STARTED OR COMMITTED WHEN*/
+/* TAKING OVER A FAILED NODE. */
+/* ---------------------------------------------------------------------- */
+/*************************************************************************/
+void Dbdih::findToReplica(TakeOverRecord* regTakeOver,
+ Uint32 replicaType,
+ FragmentstorePtr fragPtr,
+ ReplicaRecordPtr& ftrReplicaPtr)
+{
+ switch (replicaType) {
+ case CreateFragReq::STORED:
+ case CreateFragReq::COMMIT_STORED:
+ /* ----------------------------------------------------------------------*/
+ /* HERE WE SEARCH FOR STORED REPLICAS. THE REPLICA MUST BE STORED IN THE */
+ /* SECTION FOR OLD STORED REPLICAS SINCE WE HAVE NOT TAKEN OVER YET. */
+ /* ----------------------------------------------------------------------*/
+ ftrReplicaPtr.i = fragPtr.p->oldStoredReplicas;
+ while (ftrReplicaPtr.i != RNIL) {
+ ptrCheckGuard(ftrReplicaPtr, creplicaFileSize, replicaRecord);
+ if (ftrReplicaPtr.p->procNode == regTakeOver->toStartingNode) {
+ jam();
+ return;
+ } else {
+ if (ftrReplicaPtr.p->procNode == regTakeOver->toFailedNode) {
+ jam();
+ return;
+ } else {
+ jam();
+ ftrReplicaPtr.i = ftrReplicaPtr.p->nextReplica;
+ }//if
+ }//if
+ }//while
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbdih::findToReplica()
+
+void Dbdih::initCommonData()
+{
+ c_blockCommit = false;
+ c_blockCommitNo = 0;
+ c_createFragmentLock = RNIL;
+ c_endToLock = RNIL;
+ cfailurenr = 1;
+ cfirstAliveNode = RNIL;
+ cfirstDeadNode = RNIL;
+ cfirstVerifyQueue = RNIL;
+ cgckptflag = false;
+ cgcpDelay = 0;
+ cgcpMasterTakeOverState = GMTOS_IDLE;
+ cgcpOrderBlocked = 0;
+ cgcpParticipantState = GCP_PARTICIPANT_READY;
+ cgcpSameCounter = 0;
+ cgcpStartCounter = 0;
+ cgcpStatus = GCP_READY;
+
+ clastVerifyQueue = RNIL;
+ c_lcpMasterTakeOverState.set(LMTOS_IDLE, __LINE__);
+
+ c_lcpState.clcpDelay = 0;
+ c_lcpState.lcpStart = ZIDLE;
+ c_lcpState.lcpStartGcp = 0;
+ c_lcpState.setLcpStatus(LCP_STATUS_IDLE, __LINE__);
+ c_lcpState.currentFragment.tableId = 0;
+ c_lcpState.currentFragment.fragmentId = 0;
+ c_lcpState.noOfLcpFragRepOutstanding = 0;
+ c_lcpState.keepGci = 0;
+ c_lcpState.oldestRestorableGci = 0;
+ c_lcpState.ctcCounter = 0;
+ c_lcpState.ctimer = 0;
+ c_lcpState.immediateLcpStart = false;
+ c_lcpState.m_MASTER_LCPREQ_Received = false;
+
+ cmasterdihref = 0;
+ cmasterNodeId = 0;
+ cmasterState = MASTER_IDLE;
+ cmasterTakeOverNode = 0;
+ cnewgcp = 0;
+ cnoHotSpare = 0;
+ cnoOfActiveTables = 0;
+ cnoOfNodeGroups = 0;
+ cnoReplicas = 0;
+ coldgcp = 0;
+ coldGcpId = 0;
+ coldGcpStatus = cgcpStatus;
+ con_lineNodes = 0;
+ creceivedfrag = 0;
+ crestartGci = 0;
+ crestartInfoFile[0] = RNIL;
+ crestartInfoFile[1] = RNIL;
+ cstartGcpNow = false;
+ cstartPhase = 0;
+ c_startToLock = RNIL;
+ cstarttype = (Uint32)-1;
+ csystemnodes = 0;
+ c_updateToLock = RNIL;
+ currentgcp = 0;
+ cverifyQueueCounter = 0;
+ cwaitLcpSr = false;
+
+ nodeResetStart();
+ c_nodeStartMaster.wait = ZFALSE;
+
+ memset(&sysfileData[0], 0, sizeof(sysfileData));
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ c_lcpState.clcpDelay = 20;
+ ndb_mgm_get_int_parameter(p, CFG_DB_LCP_INTERVAL, &c_lcpState.clcpDelay);
+ c_lcpState.clcpDelay = c_lcpState.clcpDelay > 31 ? 31 : c_lcpState.clcpDelay;
+
+ cminHotSpareNodes = 0;
+ //ndb_mgm_get_int_parameter(p, CFG_DB_MIN_HOT_SPARES, &cminHotSpareNodes);
+ cminHotSpareNodes = cminHotSpareNodes > 2 ? 2 : cminHotSpareNodes;
+
+ cnoReplicas = 1;
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_REPLICAS, &cnoReplicas);
+ cnoReplicas = cnoReplicas > 4 ? 4 : cnoReplicas;
+
+ cgcpDelay = 2000;
+ ndb_mgm_get_int_parameter(p, CFG_DB_GCP_INTERVAL, &cgcpDelay);
+ cgcpDelay = cgcpDelay > 60000 ? 60000 : (cgcpDelay < 10 ? 10 : cgcpDelay);
+}//Dbdih::initCommonData()
+
+void Dbdih::initFragstore(FragmentstorePtr fragPtr)
+{
+ fragPtr.p->storedReplicas = RNIL;
+ fragPtr.p->oldStoredReplicas = RNIL;
+
+ fragPtr.p->noStoredReplicas = 0;
+ fragPtr.p->noOldStoredReplicas = 0;
+ fragPtr.p->fragReplicas = 0;
+ fragPtr.p->preferredPrimary = 0;
+
+ for (Uint32 i = 0; i < MAX_REPLICAS; i++)
+ fragPtr.p->activeNodes[i] = 0;
+
+ fragPtr.p->noLcpReplicas = 0;
+ fragPtr.p->distributionKey = 0;
+}//Dbdih::initFragstore()
+
+/*************************************************************************/
+/* */
+/* MODULE: INIT_RESTART_INFO */
+/* DESCRIPTION: INITIATE RESTART INFO VARIABLE AND VARIABLES FOR */
+/* GLOBAL CHECKPOINTS. */
+/*************************************************************************/
+void Dbdih::initRestartInfo()
+{
+ Uint32 i;
+ for (i = 0; i < MAX_NDB_NODES; i++) {
+ SYSFILE->lastCompletedGCI[i] = 0;
+ }//for
+ NodeRecordPtr nodePtr;
+ nodePtr.i = cfirstAliveNode;
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ SYSFILE->lastCompletedGCI[nodePtr.i] = 1;
+ /* FIRST GCP = 1 ALREADY SET BY LQH */
+ nodePtr.i = nodePtr.p->nextNode;
+ } while (nodePtr.i != RNIL);
+ coldgcp = 1;
+ currentgcp = 2;
+ cnewgcp = 2;
+ crestartGci = 1;
+
+ SYSFILE->keepGCI = 1;
+ SYSFILE->oldestRestorableGCI = 1;
+ SYSFILE->newestRestorableGCI = 1;
+ SYSFILE->systemRestartBits = 0;
+ for (i = 0; i < NodeBitmask::Size; i++) {
+ SYSFILE->lcpActive[0] = 0;
+ }//for
+ for (i = 0; i < Sysfile::TAKE_OVER_SIZE; i++) {
+ SYSFILE->takeOver[i] = 0;
+ }//for
+ Sysfile::setInitialStartOngoing(SYSFILE->systemRestartBits);
+}//Dbdih::initRestartInfo()
+
+/*--------------------------------------------------------------------*/
+/* NODE GROUP BITS ARE INITIALISED BEFORE THIS. */
+/* NODE ACTIVE BITS ARE INITIALISED BEFORE THIS. */
+/*--------------------------------------------------------------------*/
+/*************************************************************************/
+/* */
+/* MODULE: INIT_RESTORABLE_GCI_FILES */
+/* DESCRIPTION: THE SUBROUTINE SETS UP THE FILES THAT REFERS TO THE*/
+/* FILES THAT KEEP THE VARIABLE CRESTART_INFO */
+/*************************************************************************/
+void Dbdih::initRestorableGciFiles()
+{
+ Uint32 tirgTmp;
+ FileRecordPtr filePtr;
+ seizeFile(filePtr);
+ filePtr.p->tabRef = RNIL;
+ filePtr.p->fileType = FileRecord::GCP_FILE;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ filePtr.p->fileStatus = FileRecord::CLOSED;
+ crestartInfoFile[0] = filePtr.i;
+ filePtr.p->fileName[0] = (Uint32)-1; /* T DIRECTORY NOT USED */
+ filePtr.p->fileName[1] = (Uint32)-1; /* F DIRECTORY NOT USED */
+ filePtr.p->fileName[2] = (Uint32)-1; /* S PART IGNORED */
+ tirgTmp = 1; /* FILE NAME VERSION 1 */
+ tirgTmp = (tirgTmp << 8) + 6; /* .SYSFILE */
+ tirgTmp = (tirgTmp << 8) + 1; /* D1 DIRECTORY */
+ tirgTmp = (tirgTmp << 8) + 0; /* P0 FILE NAME */
+ filePtr.p->fileName[3] = tirgTmp;
+ /* --------------------------------------------------------------------- */
+ /* THE NAME BECOMES /D1/DBDICT/S0.SYSFILE */
+ /* --------------------------------------------------------------------- */
+ seizeFile(filePtr);
+ filePtr.p->tabRef = RNIL;
+ filePtr.p->fileType = FileRecord::GCP_FILE;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ filePtr.p->fileStatus = FileRecord::CLOSED;
+ crestartInfoFile[1] = filePtr.i;
+ filePtr.p->fileName[0] = (Uint32)-1; /* T DIRECTORY NOT USED */
+ filePtr.p->fileName[1] = (Uint32)-1; /* F DIRECTORY NOT USED */
+ filePtr.p->fileName[2] = (Uint32)-1; /* S PART IGNORED */
+ tirgTmp = 1; /* FILE NAME VERSION 1 */
+ tirgTmp = (tirgTmp << 8) + 6; /* .SYSFILE */
+ tirgTmp = (tirgTmp << 8) + 2; /* D1 DIRECTORY */
+ tirgTmp = (tirgTmp << 8) + 0; /* P0 FILE NAME */
+ filePtr.p->fileName[3] = tirgTmp;
+ /* --------------------------------------------------------------------- */
+ /* THE NAME BECOMES /D2/DBDICT/P0.SYSFILE */
+ /* --------------------------------------------------------------------- */
+}//Dbdih::initRestorableGciFiles()
+
+void Dbdih::initTable(TabRecordPtr tabPtr)
+{
+ tabPtr.p->noOfFragChunks = 0;
+ tabPtr.p->method = TabRecord::NOTDEFINED;
+ tabPtr.p->tabStatus = TabRecord::TS_IDLE;
+ tabPtr.p->noOfWords = 0;
+ tabPtr.p->noPages = 0;
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_COMPLETED;
+ tabPtr.p->tabCopyStatus = TabRecord::CS_IDLE;
+ tabPtr.p->tabUpdateState = TabRecord::US_IDLE;
+ tabPtr.p->noOfBackups = 0;
+ tabPtr.p->kvalue = 0;
+ tabPtr.p->hashpointer = (Uint32)-1;
+ tabPtr.p->mask = 0;
+ tabPtr.p->storedTable = 1;
+ tabPtr.p->tabErrorCode = 0;
+ tabPtr.p->schemaVersion = (Uint32)-1;
+ tabPtr.p->tabRemoveNode = RNIL;
+ tabPtr.p->totalfragments = (Uint32)-1;
+ tabPtr.p->connectrec = RNIL;
+ tabPtr.p->tabFile[0] = RNIL;
+ tabPtr.p->tabFile[1] = RNIL;
+ tabPtr.p->m_dropTab.tabUserRef = 0;
+ tabPtr.p->m_dropTab.tabUserPtr = RNIL;
+ Uint32 i;
+ for (i = 0; i < MAX_NDB_NODES; i++) {
+ tabPtr.p->startFid[i] = RNIL;
+ }//for
+ for (i = 0; i < 8; i++) {
+ tabPtr.p->pageRef[i] = RNIL;
+ }//for
+ tabPtr.p->tableType = DictTabInfo::UndefTableType;
+}//Dbdih::initTable()
+
+/*************************************************************************/
+/* */
+/* MODULE: INIT_TABLE_FILES */
+/* DESCRIPTION: THE SUBROUTINE SETS UP THE FILES THAT REFERS TO THE*/
+/* FILES THAT KEEP THE TABLE FRAGMENTATION DESCRIPTION. */
+/*************************************************************************/
+void Dbdih::initTableFile(TabRecordPtr tabPtr)
+{
+ Uint32 titfTmp;
+ FileRecordPtr filePtr;
+ seizeFile(filePtr);
+ filePtr.p->tabRef = tabPtr.i;
+ filePtr.p->fileType = FileRecord::TABLE_FILE;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ filePtr.p->fileStatus = FileRecord::CLOSED;
+ tabPtr.p->tabFile[0] = filePtr.i;
+ filePtr.p->fileName[0] = (Uint32)-1; /* T DIRECTORY NOT USED */
+ filePtr.p->fileName[1] = (Uint32)-1; /* F DIRECTORY NOT USED */
+ filePtr.p->fileName[2] = tabPtr.i; /* Stid FILE NAME */
+ titfTmp = 1; /* FILE NAME VERSION 1 */
+ titfTmp = (titfTmp << 8) + 3; /* .FRAGLIST */
+ titfTmp = (titfTmp << 8) + 1; /* D1 DIRECTORY */
+ titfTmp = (titfTmp << 8) + 255; /* P PART IGNORED */
+ filePtr.p->fileName[3] = titfTmp;
+ /* --------------------------------------------------------------------- */
+ /* THE NAME BECOMES /D1/DBDICT/Stid.FRAGLIST */
+ /* --------------------------------------------------------------------- */
+ seizeFile(filePtr);
+ filePtr.p->tabRef = tabPtr.i;
+ filePtr.p->fileType = FileRecord::TABLE_FILE;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ filePtr.p->fileStatus = FileRecord::CLOSED;
+ tabPtr.p->tabFile[1] = filePtr.i;
+ filePtr.p->fileName[0] = (Uint32)-1; /* T DIRECTORY NOT USED */
+ filePtr.p->fileName[1] = (Uint32)-1; /* F DIRECTORY NOT USED */
+ filePtr.p->fileName[2] = tabPtr.i; /* Stid FILE NAME */
+ titfTmp = 1; /* FILE NAME VERSION 1 */
+ titfTmp = (titfTmp << 8) + 3; /* .FRAGLIST */
+ titfTmp = (titfTmp << 8) + 2; /* D2 DIRECTORY */
+ titfTmp = (titfTmp << 8) + 255; /* P PART IGNORED */
+ filePtr.p->fileName[3] = titfTmp;
+ /* --------------------------------------------------------------------- */
+ /* THE NAME BECOMES /D2/DBDICT/Stid.FRAGLIST */
+ /* --------------------------------------------------------------------- */
+}//Dbdih::initTableFile()
+
+void Dbdih::initialiseRecordsLab(Signal* signal,
+ Uint32 stepNo, Uint32 retRef, Uint32 retData)
+{
+ switch (stepNo) {
+ case 0:
+ jam();
+ initCommonData();
+ break;
+ case 1:{
+ ApiConnectRecordPtr apiConnectptr;
+ jam();
+ /******** INTIALIZING API CONNECT RECORDS ********/
+ for (apiConnectptr.i = 0; apiConnectptr.i < capiConnectFileSize; apiConnectptr.i++) {
+ refresh_watch_dog();
+ ptrAss(apiConnectptr, apiConnectRecord);
+ apiConnectptr.p->nextApi = RNIL;
+ }//for
+ jam();
+ break;
+ }
+ case 2:{
+ ConnectRecordPtr connectPtr;
+ jam();
+ /****** CONNECT ******/
+ for (connectPtr.i = 0; connectPtr.i < cconnectFileSize; connectPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(connectPtr, connectRecord);
+ connectPtr.p->userpointer = RNIL;
+ connectPtr.p->userblockref = ZNIL;
+ connectPtr.p->connectState = ConnectRecord::FREE;
+ connectPtr.p->table = RNIL;
+ connectPtr.p->nfConnect = connectPtr.i + 1;
+ }//for
+ connectPtr.i = cconnectFileSize - 1;
+ ptrAss(connectPtr, connectRecord);
+ connectPtr.p->nfConnect = RNIL;
+ cfirstconnect = 0;
+ break;
+ }
+ case 3:
+ {
+ FileRecordPtr filePtr;
+ jam();
+ /******** INTIALIZING FILE RECORDS ********/
+ for (filePtr.i = 0; filePtr.i < cfileFileSize; filePtr.i++) {
+ ptrAss(filePtr, fileRecord);
+ filePtr.p->nextFile = filePtr.i + 1;
+ filePtr.p->fileStatus = FileRecord::CLOSED;
+ filePtr.p->reqStatus = FileRecord::IDLE;
+ }//for
+ filePtr.i = cfileFileSize - 1;
+ ptrAss(filePtr, fileRecord);
+ filePtr.p->nextFile = RNIL;
+ cfirstfreeFile = 0;
+ initRestorableGciFiles();
+ break;
+ }
+ case 4:
+ jam();
+ initialiseFragstore();
+ break;
+ case 5:
+ {
+ jam();
+ /******* NODE GROUP RECORD ******/
+ /******* NODE RECORD ******/
+ NodeGroupRecordPtr loopNGPtr;
+ for (loopNGPtr.i = 0; loopNGPtr.i < MAX_NDB_NODES; loopNGPtr.i++) {
+ ptrAss(loopNGPtr, nodeGroupRecord);
+ loopNGPtr.p->nodesInGroup[0] = RNIL;
+ loopNGPtr.p->nodesInGroup[1] = RNIL;
+ loopNGPtr.p->nodesInGroup[2] = RNIL;
+ loopNGPtr.p->nodesInGroup[3] = RNIL;
+ loopNGPtr.p->nextReplicaNode = 0;
+ loopNGPtr.p->nodeCount = 0;
+ loopNGPtr.p->activeTakeOver = false;
+ }//for
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 0; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRecord);
+ new (nodePtr.p) NodeRecord();
+ }//for
+ break;
+ }
+ case 6:
+ {
+ PageRecordPtr pagePtr;
+ jam();
+ /******* PAGE RECORD ******/
+ for (pagePtr.i = 0; pagePtr.i < cpageFileSize; pagePtr.i++) {
+ refresh_watch_dog();
+ ptrAss(pagePtr, pageRecord);
+ pagePtr.p->nextfreepage = pagePtr.i + 1;
+ }//for
+ pagePtr.i = cpageFileSize - 1;
+ ptrAss(pagePtr, pageRecord);
+ pagePtr.p->nextfreepage = RNIL;
+ cfirstfreepage = 0;
+ break;
+ }
+ case 7:
+ {
+ ReplicaRecordPtr initReplicaPtr;
+ jam();
+ /******* REPLICA RECORD ******/
+ for (initReplicaPtr.i = 0; initReplicaPtr.i < creplicaFileSize;
+ initReplicaPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(initReplicaPtr, replicaRecord);
+ initReplicaPtr.p->lcpIdStarted = 0;
+ initReplicaPtr.p->lcpOngoingFlag = false;
+ initReplicaPtr.p->nextReplica = initReplicaPtr.i + 1;
+ }//for
+ initReplicaPtr.i = creplicaFileSize - 1;
+ ptrAss(initReplicaPtr, replicaRecord);
+ initReplicaPtr.p->nextReplica = RNIL;
+ cnoFreeReplicaRec = creplicaFileSize;
+ cfirstfreeReplica = 0;
+ break;
+ }
+ case 8:
+ {
+ TabRecordPtr loopTabptr;
+ jam();
+ /********* TAB-DESCRIPTOR ********/
+ for (loopTabptr.i = 0; loopTabptr.i < ctabFileSize; loopTabptr.i++) {
+ ptrAss(loopTabptr, tabRecord);
+ refresh_watch_dog();
+ initTable(loopTabptr);
+ }//for
+ break;
+ }
+ case 9:
+ {
+ TakeOverRecordPtr takeOverPtr;
+ jam();
+ cfirstfreeTakeOver = RNIL;
+ for (takeOverPtr.i = 0; takeOverPtr.i < MAX_NDB_NODES; takeOverPtr.i++) {
+ ptrAss(takeOverPtr, takeOverRecord);
+ initTakeOver(takeOverPtr);
+ releaseTakeOver(takeOverPtr.i);
+ }//for
+
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = retData;
+ sendSignal(retRef, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+ return;
+ break;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ jam();
+ /* ---------------------------------------------------------------------- */
+ /* SEND REAL-TIME BREAK DURING INIT OF VARIABLES DURING SYSTEM RESTART. */
+ /* ---------------------------------------------------------------------- */
+ signal->theData[0] = DihContinueB::ZINITIALISE_RECORDS;
+ signal->theData[1] = stepNo + 1;
+ signal->theData[2] = retRef;
+ signal->theData[3] = retData;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 4, JBB);
+}//Dbdih::initialiseRecordsLab()
+
+/*************************************************************************/
+/* INSERT THE NODE INTO THE LINKED LIST OF NODES INVOLVED ALL */
+/* DISTRIBUTED PROTOCOLS (EXCEPT GCP PROTOCOL THAT USES THE DIH */
+/* LINKED LIST INSTEAD). */
+/*************************************************************************/
+void Dbdih::insertAlive(NodeRecordPtr newNodePtr)
+{
+ NodeRecordPtr nodePtr;
+
+ nodePtr.i = cfirstAliveNode;
+ if (nodePtr.i == RNIL) {
+ jam();
+ cfirstAliveNode = newNodePtr.i;
+ } else {
+ do {
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->nextNode == RNIL) {
+ jam();
+ nodePtr.p->nextNode = newNodePtr.i;
+ break;
+ } else {
+ jam();
+ nodePtr.i = nodePtr.p->nextNode;
+ }//if
+ } while (1);
+ }//if
+ newNodePtr.p->nextNode = RNIL;
+}//Dbdih::insertAlive()
+
+void Dbdih::insertBackup(FragmentstorePtr fragPtr, Uint32 nodeId)
+{
+ for (Uint32 i = fragPtr.p->fragReplicas; i > 1; i--) {
+ jam();
+ ndbrequire(i < MAX_REPLICAS && i > 0);
+ fragPtr.p->activeNodes[i] = fragPtr.p->activeNodes[i - 1];
+ }//for
+ fragPtr.p->activeNodes[1] = nodeId;
+ fragPtr.p->fragReplicas++;
+}//Dbdih::insertBackup()
+
+void Dbdih::insertDeadNode(NodeRecordPtr newNodePtr)
+{
+ NodeRecordPtr nodePtr;
+
+ nodePtr.i = cfirstDeadNode;
+ if (nodePtr.i == RNIL) {
+ jam();
+ cfirstDeadNode = newNodePtr.i;
+ } else {
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->nextNode == RNIL) {
+ jam();
+ nodePtr.p->nextNode = newNodePtr.i;
+ break;
+ } else {
+ jam();
+ nodePtr.i = nodePtr.p->nextNode;
+ }//if
+ } while (1);
+ }//if
+ newNodePtr.p->nextNode = RNIL;
+}//Dbdih::insertDeadNode()
+
+void Dbdih::linkOldStoredReplica(FragmentstorePtr fragPtr,
+ ReplicaRecordPtr replicatePtr)
+{
+ ReplicaRecordPtr losReplicaPtr;
+
+ replicatePtr.p->nextReplica = RNIL;
+ fragPtr.p->noOldStoredReplicas++;
+ losReplicaPtr.i = fragPtr.p->oldStoredReplicas;
+ if (losReplicaPtr.i == RNIL) {
+ jam();
+ fragPtr.p->oldStoredReplicas = replicatePtr.i;
+ return;
+ }//if
+ ptrCheckGuard(losReplicaPtr, creplicaFileSize, replicaRecord);
+ while (losReplicaPtr.p->nextReplica != RNIL) {
+ jam();
+ losReplicaPtr.i = losReplicaPtr.p->nextReplica;
+ ptrCheckGuard(losReplicaPtr, creplicaFileSize, replicaRecord);
+ }//if
+ losReplicaPtr.p->nextReplica = replicatePtr.i;
+}//Dbdih::linkOldStoredReplica()
+
+void Dbdih::linkStoredReplica(FragmentstorePtr fragPtr,
+ ReplicaRecordPtr replicatePtr)
+{
+ ReplicaRecordPtr lsrReplicaPtr;
+
+ fragPtr.p->noStoredReplicas++;
+ replicatePtr.p->nextReplica = RNIL;
+ lsrReplicaPtr.i = fragPtr.p->storedReplicas;
+ if (fragPtr.p->storedReplicas == RNIL) {
+ jam();
+ fragPtr.p->storedReplicas = replicatePtr.i;
+ return;
+ }//if
+ ptrCheckGuard(lsrReplicaPtr, creplicaFileSize, replicaRecord);
+ while (lsrReplicaPtr.p->nextReplica != RNIL) {
+ jam();
+ lsrReplicaPtr.i = lsrReplicaPtr.p->nextReplica;
+ ptrCheckGuard(lsrReplicaPtr, creplicaFileSize, replicaRecord);
+ }//if
+ lsrReplicaPtr.p->nextReplica = replicatePtr.i;
+}//Dbdih::linkStoredReplica()
+
+/*************************************************************************/
+/* MAKE NODE GROUPS BASED ON THE LIST OF NODES RECEIVED FROM CNTR */
+/*************************************************************************/
+void Dbdih::makeNodeGroups(Uint32 nodeArray[])
+{
+ NodeRecordPtr mngNodeptr;
+ Uint32 tmngNode;
+ Uint32 tmngNodeGroup;
+ Uint32 tmngLimit;
+ Uint32 i;
+
+ /**-----------------------------------------------------------------------
+ * ASSIGN ALL ACTIVE NODES INTO NODE GROUPS. HOT SPARE NODES ARE ASSIGNED
+ * TO NODE GROUP ZNIL
+ *-----------------------------------------------------------------------*/
+ tmngNodeGroup = 0;
+ tmngLimit = csystemnodes - cnoHotSpare;
+ ndbrequire(tmngLimit < MAX_NDB_NODES);
+ for (i = 0; i < tmngLimit; i++) {
+ NodeGroupRecordPtr NGPtr;
+ jam();
+ tmngNode = nodeArray[i];
+ mngNodeptr.i = tmngNode;
+ ptrCheckGuard(mngNodeptr, MAX_NDB_NODES, nodeRecord);
+ mngNodeptr.p->nodeGroup = tmngNodeGroup;
+ NGPtr.i = tmngNodeGroup;
+ ptrCheckGuard(NGPtr, MAX_NDB_NODES, nodeGroupRecord);
+ arrGuard(NGPtr.p->nodeCount, MAX_REPLICAS);
+ NGPtr.p->nodesInGroup[NGPtr.p->nodeCount++] = mngNodeptr.i;
+ if (NGPtr.p->nodeCount == cnoReplicas) {
+ jam();
+ tmngNodeGroup++;
+ }//if
+ }//for
+ cnoOfNodeGroups = tmngNodeGroup;
+ ndbrequire(csystemnodes < MAX_NDB_NODES);
+ for (i = tmngLimit + 1; i < csystemnodes; i++) {
+ jam();
+ tmngNode = nodeArray[i];
+ mngNodeptr.i = tmngNode;
+ ptrCheckGuard(mngNodeptr, MAX_NDB_NODES, nodeRecord);
+ mngNodeptr.p->nodeGroup = ZNIL;
+ }//for
+ for(i = 0; i < MAX_NDB_NODES; i++){
+ jam();
+ Sysfile::setNodeGroup(i, SYSFILE->nodeGroups, NO_NODE_GROUP_ID);
+ }//for
+ for (mngNodeptr.i = 1; mngNodeptr.i < MAX_NDB_NODES; mngNodeptr.i++) {
+ jam();
+ ptrAss(mngNodeptr, nodeRecord);
+ if (mngNodeptr.p->nodeGroup != ZNIL) {
+ jam();
+ Sysfile::setNodeGroup(mngNodeptr.i, SYSFILE->nodeGroups, mngNodeptr.p->nodeGroup);
+ }//if
+ }//for
+}//Dbdih::makeNodeGroups()
+
+/**
+ * On node failure QMGR asks DIH about node groups. This is
+ * a direct signal (function call in same process). Input is
+ * bitmask of surviving nodes. The routine is not concerned
+ * about node count. Reply is one of:
+ * 1) win - we can survive, and nobody else can
+ * 2) lose - we cannot survive
+ * 3) partition - we can survive but there could be others
+ */
+void Dbdih::execCHECKNODEGROUPSREQ(Signal* signal)
+{
+ jamEntry();
+ CheckNodeGroups* sd = (CheckNodeGroups*)&signal->theData[0];
+
+ bool direct = (sd->requestType & CheckNodeGroups::Direct);
+ bool ok = false;
+ switch(sd->requestType & ~CheckNodeGroups::Direct){
+ case CheckNodeGroups::ArbitCheck:{
+ ok = true;
+ jam();
+ unsigned missall = 0;
+ unsigned haveall = 0;
+ for (Uint32 i = 0; i < cnoOfNodeGroups; i++) {
+ jam();
+ NodeGroupRecordPtr ngPtr;
+ ngPtr.i = i;
+ ptrAss(ngPtr, nodeGroupRecord);
+ Uint32 count = 0;
+ for (Uint32 j = 0; j < ngPtr.p->nodeCount; j++) {
+ jam();
+ Uint32 nodeId = ngPtr.p->nodesInGroup[j];
+ if (sd->mask.get(nodeId)) {
+ jam();
+ count++;
+ }//if
+ }//for
+ if (count == 0) {
+ jam();
+ missall++;
+ }//if
+ if (count == ngPtr.p->nodeCount) {
+ haveall++;
+ }//if
+ }//for
+
+ if (missall) {
+ jam();
+ sd->output = CheckNodeGroups::Lose;
+ } else if (haveall) {
+ jam();
+ sd->output = CheckNodeGroups::Win;
+ } else {
+ jam();
+ sd->output = CheckNodeGroups::Partitioning;
+ }//if
+ }
+ break;
+ case CheckNodeGroups::GetNodeGroup:
+ ok = true;
+ sd->output = Sysfile::getNodeGroup(getOwnNodeId(), SYSFILE->nodeGroups);
+ break;
+ case CheckNodeGroups::GetNodeGroupMembers: {
+ ok = true;
+ Uint32 ownNodeGoup =
+ Sysfile::getNodeGroup(sd->nodeId, SYSFILE->nodeGroups);
+
+ sd->output = ownNodeGoup;
+ sd->mask.clear();
+
+ NodeGroupRecordPtr ngPtr;
+ ngPtr.i = ownNodeGoup;
+ ptrAss(ngPtr, nodeGroupRecord);
+ for (Uint32 j = 0; j < ngPtr.p->nodeCount; j++) {
+ jam();
+ sd->mask.set(ngPtr.p->nodesInGroup[j]);
+ }
+#if 0
+ for (int i = 0; i < MAX_NDB_NODES; i++) {
+ if (ownNodeGoup ==
+ Sysfile::getNodeGroup(i, SYSFILE->nodeGroups)) {
+ sd->mask.set(i);
+ }
+ }
+#endif
+ }
+ break;
+ }
+ ndbrequire(ok);
+
+ if (!direct)
+ sendSignal(sd->blockRef, GSN_CHECKNODEGROUPSCONF, signal,
+ CheckNodeGroups::SignalLength, JBB);
+}//Dbdih::execCHECKNODEGROUPSREQ()
+
+void Dbdih::makePrnList(ReadNodesConf * readNodes, Uint32 nodeArray[])
+{
+ cfirstAliveNode = RNIL;
+ ndbrequire(con_lineNodes > 0);
+ ndbrequire(csystemnodes < MAX_NDB_NODES);
+ for (Uint32 i = 0; i < csystemnodes; i++) {
+ NodeRecordPtr nodePtr;
+ jam();
+ nodePtr.i = nodeArray[i];
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ new (nodePtr.p) NodeRecord();
+ if (NodeBitmask::get(readNodes->inactiveNodes, nodePtr.i) == false){
+ jam();
+ nodePtr.p->nodeStatus = NodeRecord::ALIVE;
+ nodePtr.p->useInTransactions = true;
+ nodePtr.p->copyCompleted = true;
+ nodePtr.p->m_inclDihLcp = true;
+ insertAlive(nodePtr);
+ } else {
+ jam();
+ nodePtr.p->nodeStatus = NodeRecord::DEAD;
+ insertDeadNode(nodePtr);
+ }//if
+ }//for
+}//Dbdih::makePrnList()
+
+/*************************************************************************/
+/* A NEW CRASHED REPLICA IS ADDED BY A NODE FAILURE. */
+/*************************************************************************/
+void Dbdih::newCrashedReplica(Uint32 nodeId, ReplicaRecordPtr ncrReplicaPtr)
+{
+ /*----------------------------------------------------------------------*/
+ /* SET THE REPLICA_LAST_GCI OF THE CRASHED REPLICA TO LAST GCI */
+ /* EXECUTED BY THE FAILED NODE. */
+ /*----------------------------------------------------------------------*/
+ /* WE HAVE A NEW CRASHED REPLICA. INITIATE CREATE GCI TO INDICATE */
+ /* THAT THE NEW REPLICA IS NOT STARTED YET AND REPLICA_LAST_GCI IS*/
+ /* SET TO -1 TO INDICATE THAT IT IS NOT DEAD YET. */
+ /*----------------------------------------------------------------------*/
+ arrGuard(ncrReplicaPtr.p->noCrashedReplicas + 1, 8);
+ ncrReplicaPtr.p->replicaLastGci[ncrReplicaPtr.p->noCrashedReplicas] =
+ SYSFILE->lastCompletedGCI[nodeId];
+ ncrReplicaPtr.p->noCrashedReplicas = ncrReplicaPtr.p->noCrashedReplicas + 1;
+ ncrReplicaPtr.p->createGci[ncrReplicaPtr.p->noCrashedReplicas] = 0;
+ ncrReplicaPtr.p->replicaLastGci[ncrReplicaPtr.p->noCrashedReplicas] =
+ (Uint32)-1;
+}//Dbdih::newCrashedReplica()
+
+/*************************************************************************/
+/* AT NODE FAILURE DURING START OF A NEW NODE WE NEED TO RESET A */
+/* SET OF VARIABLES CONTROLLING THE START AND INDICATING ONGOING */
+/* START OF A NEW NODE. */
+/*************************************************************************/
+void Dbdih::nodeResetStart()
+{
+ jam();
+ c_nodeStartMaster.startNode = RNIL;
+ c_nodeStartMaster.failNr = cfailurenr;
+ c_nodeStartMaster.activeState = false;
+ c_nodeStartMaster.blockGcp = false;
+ c_nodeStartMaster.blockLcp = false;
+ c_nodeStartMaster.m_outstandingGsn = 0;
+}//Dbdih::nodeResetStart()
+
+void Dbdih::openFileRw(Signal* signal, FileRecordPtr filePtr)
+{
+ signal->theData[0] = reference();
+ signal->theData[1] = filePtr.i;
+ signal->theData[2] = filePtr.p->fileName[0];
+ signal->theData[3] = filePtr.p->fileName[1];
+ signal->theData[4] = filePtr.p->fileName[2];
+ signal->theData[5] = filePtr.p->fileName[3];
+ signal->theData[6] = FsOpenReq::OM_READWRITE;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+}//Dbdih::openFileRw()
+
+void Dbdih::openFileRo(Signal* signal, FileRecordPtr filePtr)
+{
+ signal->theData[0] = reference();
+ signal->theData[1] = filePtr.i;
+ signal->theData[2] = filePtr.p->fileName[0];
+ signal->theData[3] = filePtr.p->fileName[1];
+ signal->theData[4] = filePtr.p->fileName[2];
+ signal->theData[5] = filePtr.p->fileName[3];
+ signal->theData[6] = FsOpenReq::OM_READONLY;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+}//Dbdih::openFileRw()
+
+/*************************************************************************/
+/* REMOVE A CRASHED REPLICA BY PACKING THE ARRAY OF CREATED GCI AND*/
+/* THE LAST GCI OF THE CRASHED REPLICA. */
+/*************************************************************************/
+void Dbdih::packCrashedReplicas(ReplicaRecordPtr replicaPtr)
+{
+ ndbrequire(replicaPtr.p->noCrashedReplicas > 0);
+ ndbrequire(replicaPtr.p->noCrashedReplicas <= 8);
+ for (Uint32 i = 0; i < replicaPtr.p->noCrashedReplicas; i++) {
+ jam();
+ replicaPtr.p->createGci[i] = replicaPtr.p->createGci[i + 1];
+ replicaPtr.p->replicaLastGci[i] = replicaPtr.p->replicaLastGci[i + 1];
+ }//for
+ replicaPtr.p->noCrashedReplicas--;
+
+#ifdef VM_TRACE
+ for (Uint32 i = 0; i < replicaPtr.p->noCrashedReplicas; i++) {
+ jam();
+ ndbrequire(replicaPtr.p->createGci[i] != 0xF1F1F1F1);
+ ndbrequire(replicaPtr.p->replicaLastGci[i] != 0xF1F1F1F1);
+ }//for
+#endif
+}//Dbdih::packCrashedReplicas()
+
+void Dbdih::prepareReplicas(FragmentstorePtr fragPtr)
+{
+ ReplicaRecordPtr prReplicaPtr;
+ Uint32 prevReplica = RNIL;
+
+ /* --------------------------------------------------------------------- */
+ /* BEGIN BY LINKING ALL REPLICA RECORDS ONTO THE OLD STORED REPLICA*/
+ /* LIST. */
+ /* AT A SYSTEM RESTART OBVIOUSLY ALL NODES ARE OLD. */
+ /* --------------------------------------------------------------------- */
+ prReplicaPtr.i = fragPtr.p->storedReplicas;
+ while (prReplicaPtr.i != RNIL) {
+ jam();
+ prevReplica = prReplicaPtr.i;
+ ptrCheckGuard(prReplicaPtr, creplicaFileSize, replicaRecord);
+ prReplicaPtr.i = prReplicaPtr.p->nextReplica;
+ }//while
+ /* --------------------------------------------------------------------- */
+ /* LIST OF STORED REPLICAS WILL BE EMPTY NOW. */
+ /* --------------------------------------------------------------------- */
+ if (prevReplica != RNIL) {
+ prReplicaPtr.i = prevReplica;
+ ptrCheckGuard(prReplicaPtr, creplicaFileSize, replicaRecord);
+ prReplicaPtr.p->nextReplica = fragPtr.p->oldStoredReplicas;
+ fragPtr.p->oldStoredReplicas = fragPtr.p->storedReplicas;
+ fragPtr.p->storedReplicas = RNIL;
+ fragPtr.p->noOldStoredReplicas += fragPtr.p->noStoredReplicas;
+ fragPtr.p->noStoredReplicas = 0;
+ }//if
+}//Dbdih::prepareReplicas()
+
+void Dbdih::readFragment(RWFragment* rf, FragmentstorePtr fragPtr)
+{
+ Uint32 TreadFid = readPageWord(rf);
+ fragPtr.p->preferredPrimary = readPageWord(rf);
+ fragPtr.p->noStoredReplicas = readPageWord(rf);
+ fragPtr.p->noOldStoredReplicas = readPageWord(rf);
+ Uint32 TdistKey = readPageWord(rf);
+
+ ndbrequire(fragPtr.p->noStoredReplicas > 0);
+ ndbrequire(TreadFid == rf->fragId);
+ ndbrequire(TdistKey < 256);
+ if ((cstarttype == NodeState::ST_NODE_RESTART) ||
+ (cstarttype == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ fragPtr.p->distributionKey = TdistKey;
+ }//if
+}//Dbdih::readFragment()
+
+Uint32 Dbdih::readPageWord(RWFragment* rf)
+{
+ if (rf->wordIndex >= 2048) {
+ jam();
+ ndbrequire(rf->wordIndex == 2048);
+ rf->pageIndex++;
+ ndbrequire(rf->pageIndex < 8);
+ rf->rwfPageptr.i = rf->rwfTabPtr.p->pageRef[rf->pageIndex];
+ ptrCheckGuard(rf->rwfPageptr, cpageFileSize, pageRecord);
+ rf->wordIndex = 32;
+ }//if
+ Uint32 dataWord = rf->rwfPageptr.p->word[rf->wordIndex];
+ rf->wordIndex++;
+ return dataWord;
+}//Dbdih::readPageWord()
+
+void Dbdih::readReplica(RWFragment* rf, ReplicaRecordPtr readReplicaPtr)
+{
+ Uint32 i;
+ readReplicaPtr.p->procNode = readPageWord(rf);
+ readReplicaPtr.p->initialGci = readPageWord(rf);
+ readReplicaPtr.p->noCrashedReplicas = readPageWord(rf);
+ readReplicaPtr.p->nextLcp = readPageWord(rf);
+
+ for (i = 0; i < MAX_LCP_STORED; i++) {
+ readReplicaPtr.p->maxGciCompleted[i] = readPageWord(rf);
+ readReplicaPtr.p->maxGciStarted[i] = readPageWord(rf);
+ readReplicaPtr.p->lcpId[i] = readPageWord(rf);
+ readReplicaPtr.p->lcpStatus[i] = readPageWord(rf);
+ }//for
+ const Uint32 noCrashedReplicas = readReplicaPtr.p->noCrashedReplicas;
+ ndbrequire(noCrashedReplicas < 8);
+ for (i = 0; i < noCrashedReplicas; i++) {
+ readReplicaPtr.p->createGci[i] = readPageWord(rf);
+ readReplicaPtr.p->replicaLastGci[i] = readPageWord(rf);
+ ndbrequire(readReplicaPtr.p->createGci[i] != 0xF1F1F1F1);
+ ndbrequire(readReplicaPtr.p->replicaLastGci[i] != 0xF1F1F1F1);
+ }//for
+ for(i = noCrashedReplicas; i<8; i++){
+ readReplicaPtr.p->createGci[i] = readPageWord(rf);
+ readReplicaPtr.p->replicaLastGci[i] = readPageWord(rf);
+ // They are not initialized...
+ readReplicaPtr.p->createGci[i] = 0;
+ readReplicaPtr.p->replicaLastGci[i] = ~0;
+ }
+ /* ---------------------------------------------------------------------- */
+ /* IF THE LAST COMPLETED LOCAL CHECKPOINT IS VALID AND LARGER THAN */
+ /* THE LAST COMPLETED CHECKPOINT THEN WE WILL INVALIDATE THIS LOCAL */
+ /* CHECKPOINT FOR THIS REPLICA. */
+ /* ---------------------------------------------------------------------- */
+ Uint32 trraLcp = prevLcpNo(readReplicaPtr.p->nextLcp);
+ ndbrequire(trraLcp < MAX_LCP_STORED);
+ if ((readReplicaPtr.p->lcpStatus[trraLcp] == ZVALID) &&
+ (readReplicaPtr.p->lcpId[trraLcp] > SYSFILE->latestLCP_ID)) {
+ jam();
+ readReplicaPtr.p->lcpStatus[trraLcp] = ZINVALID;
+ }//if
+ /* ---------------------------------------------------------------------- */
+ /* WE ALSO HAVE TO INVALIDATE ANY LOCAL CHECKPOINTS THAT HAVE BEEN */
+ /* INVALIDATED BY MOVING BACK THE RESTART GCI. */
+ /* ---------------------------------------------------------------------- */
+ for (i = 0; i < MAX_LCP_STORED; i++) {
+ jam();
+ if ((readReplicaPtr.p->lcpStatus[i] == ZVALID) &&
+ (readReplicaPtr.p->maxGciStarted[i] > SYSFILE->newestRestorableGCI)) {
+ jam();
+ readReplicaPtr.p->lcpStatus[i] = ZINVALID;
+ }//if
+ }//for
+ /* ---------------------------------------------------------------------- */
+ /* WE WILL REMOVE ANY OCCURRENCES OF REPLICAS THAT HAVE CRASHED */
+ /* THAT ARE NO LONGER VALID DUE TO MOVING RESTART GCI BACKWARDS. */
+ /* ---------------------------------------------------------------------- */
+ removeTooNewCrashedReplicas(readReplicaPtr);
+ /* ---------------------------------------------------------------------- */
+ /* WE WILL REMOVE ANY OCCURRENCES OF REPLICAS THAT HAVE CRASHED */
+ /* THAT ARE NO LONGER VALID SINCE THEY ARE NO LONGER RESTORABLE. */
+ /* ---------------------------------------------------------------------- */
+ removeOldCrashedReplicas(readReplicaPtr);
+ /* --------------------------------------------------------------------- */
+ // We set the last GCI of the replica that was alive before the node
+ // crashed last time. We set it to the last GCI which the node participated in.
+ /* --------------------------------------------------------------------- */
+ ndbrequire(readReplicaPtr.p->noCrashedReplicas < 8);
+ readReplicaPtr.p->replicaLastGci[readReplicaPtr.p->noCrashedReplicas] =
+ SYSFILE->lastCompletedGCI[readReplicaPtr.p->procNode];
+ /* ---------------------------------------------------------------------- */
+ /* FIND PROCESSOR RECORD */
+ /* ---------------------------------------------------------------------- */
+}//Dbdih::readReplica()
+
+void Dbdih::readReplicas(RWFragment* rf, FragmentstorePtr fragPtr)
+{
+ Uint32 i;
+ ReplicaRecordPtr newReplicaPtr;
+ Uint32 noStoredReplicas = fragPtr.p->noStoredReplicas;
+ Uint32 noOldStoredReplicas = fragPtr.p->noOldStoredReplicas;
+ /* ----------------------------------------------------------------------- */
+ /* WE CLEAR THE NUMBER OF STORED REPLICAS SINCE IT WILL BE CALCULATED */
+ /* BY THE LINKING SUBROUTINES. */
+ /* ----------------------------------------------------------------------- */
+ fragPtr.p->noStoredReplicas = 0;
+ fragPtr.p->noOldStoredReplicas = 0;
+ Uint32 replicaIndex = 0;
+ ndbrequire(noStoredReplicas + noOldStoredReplicas <= MAX_REPLICAS);
+ for (i = 0; i < noStoredReplicas; i++) {
+ seizeReplicaRec(newReplicaPtr);
+ readReplica(rf, newReplicaPtr);
+ if (checkNodeAlive(newReplicaPtr.p->procNode)) {
+ jam();
+ ndbrequire(replicaIndex < MAX_REPLICAS);
+ fragPtr.p->activeNodes[replicaIndex] = newReplicaPtr.p->procNode;
+ replicaIndex++;
+ linkStoredReplica(fragPtr, newReplicaPtr);
+ } else {
+ jam();
+ linkOldStoredReplica(fragPtr, newReplicaPtr);
+ }//if
+ }//for
+ fragPtr.p->fragReplicas = noStoredReplicas;
+ for (i = 0; i < noOldStoredReplicas; i++) {
+ jam();
+ seizeReplicaRec(newReplicaPtr);
+ readReplica(rf, newReplicaPtr);
+ linkOldStoredReplica(fragPtr, newReplicaPtr);
+ }//for
+}//Dbdih::readReplicas()
+
+void Dbdih::readRestorableGci(Signal* signal, FileRecordPtr filePtr)
+{
+ signal->theData[0] = filePtr.p->fileRef;
+ signal->theData[1] = reference();
+ signal->theData[2] = filePtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS;
+ signal->theData[4] = ZVAR_NO_CRESTART_INFO;
+ signal->theData[5] = 1;
+ signal->theData[6] = 0;
+ signal->theData[7] = 0;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+}//Dbdih::readRestorableGci()
+
+void Dbdih::readTabfile(Signal* signal, TabRecord* tab, FileRecordPtr filePtr)
+{
+ signal->theData[0] = filePtr.p->fileRef;
+ signal->theData[1] = reference();
+ signal->theData[2] = filePtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS;
+ signal->theData[4] = ZVAR_NO_WORD;
+ signal->theData[5] = tab->noPages;
+ for (Uint32 i = 0; i < tab->noPages; i++) {
+ signal->theData[6 + (2 * i)] = tab->pageRef[i];
+ signal->theData[7 + (2 * i)] = i;
+ }//for
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 22, JBA);
+}//Dbdih::readTabfile()
+
+void Dbdih::releasePage(Uint32 pageIndex)
+{
+ PageRecordPtr pagePtr;
+ pagePtr.i = pageIndex;
+ ptrCheckGuard(pagePtr, cpageFileSize, pageRecord);
+ pagePtr.p->nextfreepage = cfirstfreepage;
+ cfirstfreepage = pagePtr.i;
+}//Dbdih::releasePage()
+
+void Dbdih::releaseTabPages(Uint32 tableId)
+{
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+ ndbrequire(tabPtr.p->noPages <= 8);
+ for (Uint32 i = 0; i < tabPtr.p->noPages; i++) {
+ jam();
+ releasePage(tabPtr.p->pageRef[i]);
+ }//for
+ tabPtr.p->noPages = 0;
+}//Dbdih::releaseTabPages()
+
+/*************************************************************************/
+/* REMOVE NODE FROM SET OF ALIVE NODES. */
+/*************************************************************************/
+void Dbdih::removeAlive(NodeRecordPtr removeNodePtr)
+{
+ NodeRecordPtr nodePtr;
+
+ nodePtr.i = cfirstAliveNode;
+ if (nodePtr.i == removeNodePtr.i) {
+ jam();
+ cfirstAliveNode = removeNodePtr.p->nextNode;
+ return;
+ }//if
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->nextNode == removeNodePtr.i) {
+ jam();
+ nodePtr.p->nextNode = removeNodePtr.p->nextNode;
+ break;
+ } else {
+ jam();
+ nodePtr.i = nodePtr.p->nextNode;
+ }//if
+ } while (1);
+}//Dbdih::removeAlive()
+
+/*************************************************************************/
+/* REMOVE NODE FROM SET OF DEAD NODES. */
+/*************************************************************************/
+void Dbdih::removeDeadNode(NodeRecordPtr removeNodePtr)
+{
+ NodeRecordPtr nodePtr;
+
+ nodePtr.i = cfirstDeadNode;
+ if (nodePtr.i == removeNodePtr.i) {
+ jam();
+ cfirstDeadNode = removeNodePtr.p->nextNode;
+ return;
+ }//if
+ do {
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->nextNode == removeNodePtr.i) {
+ jam();
+ nodePtr.p->nextNode = removeNodePtr.p->nextNode;
+ break;
+ } else {
+ jam();
+ nodePtr.i = nodePtr.p->nextNode;
+ }//if
+ } while (1);
+}//Dbdih::removeDeadNode()
+
+/*---------------------------------------------------------------*/
+/* REMOVE REPLICAS OF A FAILED NODE FROM LIST OF STORED */
+/* REPLICAS AND MOVE IT TO THE LIST OF OLD STORED REPLICAS.*/
+/* ALSO UPDATE THE CRASHED REPLICA INFORMATION. */
+/*---------------------------------------------------------------*/
+void Dbdih::removeNodeFromStored(Uint32 nodeId,
+ FragmentstorePtr fragPtr,
+ ReplicaRecordPtr replicatePtr)
+{
+ newCrashedReplica(nodeId, replicatePtr);
+ removeStoredReplica(fragPtr, replicatePtr);
+ linkOldStoredReplica(fragPtr, replicatePtr);
+ ndbrequire(fragPtr.p->storedReplicas != RNIL);
+}//Dbdih::removeNodeFromStored()
+
+/*************************************************************************/
+/* REMOVE ANY OLD CRASHED REPLICAS THAT ARE NOT RESTORABLE ANY MORE*/
+/*************************************************************************/
+void Dbdih::removeOldCrashedReplicas(ReplicaRecordPtr rocReplicaPtr)
+{
+ while (rocReplicaPtr.p->noCrashedReplicas > 0) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* ONLY IF THERE IS AT LEAST ONE REPLICA THEN CAN WE REMOVE ANY. */
+ /* --------------------------------------------------------------------- */
+ if (rocReplicaPtr.p->replicaLastGci[0] < SYSFILE->oldestRestorableGCI){
+ jam();
+ /* ------------------------------------------------------------------- */
+ /* THIS CRASHED REPLICA HAS BECOME EXTINCT AND MUST BE REMOVED TO */
+ /* GIVE SPACE FOR NEW CRASHED REPLICAS. */
+ /* ------------------------------------------------------------------- */
+ packCrashedReplicas(rocReplicaPtr);
+ } else {
+ break;
+ }//if
+ }//while
+ if (rocReplicaPtr.p->createGci[0] < SYSFILE->keepGCI){
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* MOVE FORWARD THE CREATE GCI TO A GCI THAT CAN BE USED. WE HAVE */
+ /* NO CERTAINTY IN FINDING ANY LOG RECORDS FROM OLDER GCI'S. */
+ /* --------------------------------------------------------------------- */
+ rocReplicaPtr.p->createGci[0] = SYSFILE->keepGCI;
+ ndbrequire(SYSFILE->keepGCI != 0xF1F1F1F1);
+ }//if
+}//Dbdih::removeOldCrashedReplicas()
+
+void Dbdih::removeOldStoredReplica(FragmentstorePtr fragPtr,
+ ReplicaRecordPtr replicatePtr)
+{
+ ReplicaRecordPtr rosTmpReplicaPtr;
+ ReplicaRecordPtr rosPrevReplicaPtr;
+
+ fragPtr.p->noOldStoredReplicas--;
+ if (fragPtr.p->oldStoredReplicas == replicatePtr.i) {
+ jam();
+ fragPtr.p->oldStoredReplicas = replicatePtr.p->nextReplica;
+ } else {
+ rosPrevReplicaPtr.i = fragPtr.p->oldStoredReplicas;
+ ptrCheckGuard(rosPrevReplicaPtr, creplicaFileSize, replicaRecord);
+ rosTmpReplicaPtr.i = rosPrevReplicaPtr.p->nextReplica;
+ while (rosTmpReplicaPtr.i != replicatePtr.i) {
+ jam();
+ rosPrevReplicaPtr.i = rosTmpReplicaPtr.i;
+ ptrCheckGuard(rosPrevReplicaPtr, creplicaFileSize, replicaRecord);
+ ptrCheckGuard(rosTmpReplicaPtr, creplicaFileSize, replicaRecord);
+ rosTmpReplicaPtr.i = rosTmpReplicaPtr.p->nextReplica;
+ }//if
+ rosPrevReplicaPtr.p->nextReplica = replicatePtr.p->nextReplica;
+ }//if
+}//Dbdih::removeOldStoredReplica()
+
+void Dbdih::removeStoredReplica(FragmentstorePtr fragPtr,
+ ReplicaRecordPtr replicatePtr)
+{
+ ReplicaRecordPtr rsrTmpReplicaPtr;
+ ReplicaRecordPtr rsrPrevReplicaPtr;
+
+ fragPtr.p->noStoredReplicas--;
+ if (fragPtr.p->storedReplicas == replicatePtr.i) {
+ jam();
+ fragPtr.p->storedReplicas = replicatePtr.p->nextReplica;
+ } else {
+ jam();
+ rsrPrevReplicaPtr.i = fragPtr.p->storedReplicas;
+ rsrTmpReplicaPtr.i = fragPtr.p->storedReplicas;
+ ptrCheckGuard(rsrTmpReplicaPtr, creplicaFileSize, replicaRecord);
+ rsrTmpReplicaPtr.i = rsrTmpReplicaPtr.p->nextReplica;
+ while (rsrTmpReplicaPtr.i != replicatePtr.i) {
+ jam();
+ rsrPrevReplicaPtr.i = rsrTmpReplicaPtr.i;
+ ptrCheckGuard(rsrTmpReplicaPtr, creplicaFileSize, replicaRecord);
+ rsrTmpReplicaPtr.i = rsrTmpReplicaPtr.p->nextReplica;
+ }//while
+ ptrCheckGuard(rsrPrevReplicaPtr, creplicaFileSize, replicaRecord);
+ rsrPrevReplicaPtr.p->nextReplica = replicatePtr.p->nextReplica;
+ }//if
+}//Dbdih::removeStoredReplica()
+
+/*************************************************************************/
+/* REMOVE ALL TOO NEW CRASHED REPLICAS THAT IS IN THIS REPLICA. */
+/*************************************************************************/
+void Dbdih::removeTooNewCrashedReplicas(ReplicaRecordPtr rtnReplicaPtr)
+{
+ while (rtnReplicaPtr.p->noCrashedReplicas > 0) {
+ jam();
+ /* --------------------------------------------------------------------- */
+ /* REMOVE ALL REPLICAS THAT ONLY LIVED IN A PERIOD THAT HAVE BEEN */
+ /* REMOVED FROM THE RESTART INFORMATION SINCE THE RESTART FAILED */
+ /* TOO MANY TIMES. */
+ /* --------------------------------------------------------------------- */
+ arrGuard(rtnReplicaPtr.p->noCrashedReplicas - 1, 8);
+ if (rtnReplicaPtr.p->createGci[rtnReplicaPtr.p->noCrashedReplicas - 1] >
+ SYSFILE->newestRestorableGCI){
+ jam();
+ rtnReplicaPtr.p->createGci[rtnReplicaPtr.p->noCrashedReplicas - 1] =
+ (Uint32)-1;
+ rtnReplicaPtr.p->replicaLastGci[rtnReplicaPtr.p->noCrashedReplicas - 1] =
+ (Uint32)-1;
+ rtnReplicaPtr.p->noCrashedReplicas--;
+ } else {
+ break;
+ }//if
+ }//while
+}//Dbdih::removeTooNewCrashedReplicas()
+
+/*************************************************************************/
+/* */
+/* MODULE: SEARCH FOR POSSIBLE REPLICAS THAT CAN HANDLE THE GLOBAL */
+/* CHECKPOINT WITHOUT NEEDING ANY EXTRA LOGGING FACILITIES.*/
+/* A MAXIMUM OF FOUR NODES IS RETRIEVED. */
+/*************************************************************************/
+void Dbdih::searchStoredReplicas(FragmentstorePtr fragPtr)
+{
+ Uint32 nextReplicaPtrI;
+ ConstPtr<ReplicaRecord> replicaPtr;
+
+ replicaPtr.i = fragPtr.p->storedReplicas;
+ while (replicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ nextReplicaPtrI = replicaPtr.p->nextReplica;
+ NodeRecordPtr nodePtr;
+ nodePtr.i = replicaPtr.p->procNode;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->nodeStatus == NodeRecord::ALIVE) {
+ jam();
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:{
+ /* ----------------------------------------------------------------- */
+ /* INITIALISE THE CREATE REPLICA STRUCTURE THAT IS USED FOR SENDING*/
+ /* TO LQH START_FRAGREQ. */
+ /* SET THE DATA NODE WHERE THE LOCAL CHECKPOINT IS FOUND. ALSO */
+ /* SET A REFERENCE TO THE REPLICA POINTER OF THAT. */
+ /* ----------------------------------------------------------------- */
+ CreateReplicaRecordPtr createReplicaPtr;
+ createReplicaPtr.i = cnoOfCreateReplicas;
+ ptrCheckGuard(createReplicaPtr, 4, createReplicaRecord);
+ cnoOfCreateReplicas++;
+ createReplicaPtr.p->dataNodeId = replicaPtr.p->procNode;
+ createReplicaPtr.p->replicaRec = replicaPtr.i;
+ /* ----------------------------------------------------------------- */
+ /* WE NEED TO SEARCH FOR A PROPER LOCAL CHECKPOINT TO USE FOR THE */
+ /* SYSTEM RESTART. */
+ /* ----------------------------------------------------------------- */
+ Uint32 startGci;
+ Uint32 startLcpNo;
+ Uint32 stopGci = SYSFILE->newestRestorableGCI;
+ bool result = findStartGci(replicaPtr,
+ stopGci,
+ startGci,
+ startLcpNo);
+ if (!result) {
+ jam();
+ /* --------------------------------------------------------------- */
+ /* WE COULD NOT FIND ANY LOCAL CHECKPOINT. THE FRAGMENT THUS DO NOT*/
+ /* CONTAIN ANY VALID LOCAL CHECKPOINT. IT DOES HOWEVER CONTAIN A */
+ /* VALID FRAGMENT LOG. THUS BY FIRST CREATING THE FRAGMENT AND THEN*/
+ /* EXECUTING THE FRAGMENT LOG WE CAN CREATE THE FRAGMENT AS */
+ /* DESIRED. THIS SHOULD ONLY OCCUR AFTER CREATING A FRAGMENT. */
+ /* */
+ /* TO INDICATE THAT NO LOCAL CHECKPOINT IS TO BE USED WE SET THE */
+ /* LOCAL CHECKPOINT TO ZNIL. */
+ /* --------------------------------------------------------------- */
+ createReplicaPtr.p->lcpNo = ZNIL;
+ } else {
+ jam();
+ /* --------------------------------------------------------------- */
+ /* WE FOUND A PROPER LOCAL CHECKPOINT TO RESTART FROM. */
+ /* SET LOCAL CHECKPOINT ID AND LOCAL CHECKPOINT NUMBER. */
+ /* --------------------------------------------------------------- */
+ createReplicaPtr.p->lcpNo = startLcpNo;
+ arrGuard(startLcpNo, MAX_LCP_STORED);
+ createReplicaPtr.p->createLcpId = replicaPtr.p->lcpId[startLcpNo];
+ }//if
+
+ if(ERROR_INSERTED(7073) || ERROR_INSERTED(7074)){
+ jam();
+ nodePtr.p->nodeStatus = NodeRecord::DEAD;
+ }
+
+ /* ----------------------------------------------------------------- */
+ /* WE HAVE EITHER FOUND A LOCAL CHECKPOINT OR WE ARE PLANNING TO */
+ /* EXECUTE THE LOG FROM THE INITIAL CREATION OF THE TABLE. IN BOTH */
+ /* CASES WE NEED TO FIND A SET OF LOGS THAT CAN EXECUTE SUCH THAT */
+ /* WE RECOVER TO THE SYSTEM RESTART GLOBAL CHECKPOINT. */
+ /* -_--------------------------------------------------------------- */
+ if (!findLogNodes(createReplicaPtr.p, fragPtr, startGci, stopGci)) {
+ jam();
+ /* --------------------------------------------------------------- */
+ /* WE WERE NOT ABLE TO FIND ANY WAY OF RESTORING THIS REPLICA. */
+ /* THIS IS A POTENTIAL SYSTEM ERROR. */
+ /* --------------------------------------------------------------- */
+ cnoOfCreateReplicas--;
+ return;
+ }//if
+
+ if(ERROR_INSERTED(7073) || ERROR_INSERTED(7074)){
+ jam();
+ nodePtr.p->nodeStatus = NodeRecord::ALIVE;
+ }
+
+ break;
+ }
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }//switch
+ }
+ replicaPtr.i = nextReplicaPtrI;
+ }//while
+}//Dbdih::searchStoredReplicas()
+
+/*************************************************************************/
+/* */
+/* MODULE: SEIZE_FILE */
+/* DESCRIPTION: THE SUBROUTINE SEIZES A FILE RECORD FROM THE */
+/* FREE LIST. */
+/*************************************************************************/
+void Dbdih::seizeFile(FileRecordPtr& filePtr)
+{
+ filePtr.i = cfirstfreeFile;
+ ptrCheckGuard(filePtr, cfileFileSize, fileRecord);
+ cfirstfreeFile = filePtr.p->nextFile;
+ filePtr.p->nextFile = RNIL;
+}//Dbdih::seizeFile()
+
+/*************************************************************************/
+/* SEND CREATE_FRAGREQ TO ALL NODES IN THE NDB CLUSTER. */
+/*************************************************************************/
+/*************************************************************************/
+/* */
+/* MODULE: FIND THE START GCI AND LOCAL CHECKPOINT TO USE. */
+/*************************************************************************/
+void Dbdih::sendStartFragreq(Signal* signal,
+ TabRecordPtr tabPtr, Uint32 fragId)
+{
+ CreateReplicaRecordPtr replicaPtr;
+ for (replicaPtr.i = 0; replicaPtr.i < cnoOfCreateReplicas; replicaPtr.i++) {
+ jam();
+ ptrAss(replicaPtr, createReplicaRecord);
+ BlockReference ref = calcLqhBlockRef(replicaPtr.p->dataNodeId);
+ StartFragReq * const startFragReq = (StartFragReq *)&signal->theData[0];
+ startFragReq->userPtr = replicaPtr.p->replicaRec;
+ startFragReq->userRef = reference();
+ startFragReq->lcpNo = replicaPtr.p->lcpNo;
+ startFragReq->lcpId = replicaPtr.p->createLcpId;
+ startFragReq->tableId = tabPtr.i;
+ startFragReq->fragId = fragId;
+
+ if(ERROR_INSERTED(7072) || ERROR_INSERTED(7074)){
+ jam();
+ const Uint32 noNodes = replicaPtr.p->noLogNodes;
+ Uint32 start = replicaPtr.p->logStartGci[noNodes - 1];
+ const Uint32 stop = replicaPtr.p->logStopGci[noNodes - 1];
+
+ for(Uint32 i = noNodes; i < 4 && (stop - start) > 0; i++){
+ replicaPtr.p->noLogNodes++;
+ replicaPtr.p->logStopGci[i - 1] = start;
+
+ replicaPtr.p->logNodeId[i] = replicaPtr.p->logNodeId[i-1];
+ replicaPtr.p->logStartGci[i] = start + 1;
+ replicaPtr.p->logStopGci[i] = stop;
+ start += 1;
+ }
+ }
+
+ startFragReq->noOfLogNodes = replicaPtr.p->noLogNodes;
+
+ for (Uint32 i = 0; i < 4 ; i++) {
+ startFragReq->lqhLogNode[i] = replicaPtr.p->logNodeId[i];
+ startFragReq->startGci[i] = replicaPtr.p->logStartGci[i];
+ startFragReq->lastGci[i] = replicaPtr.p->logStopGci[i];
+ }//for
+
+ sendSignal(ref, GSN_START_FRAGREQ, signal,
+ StartFragReq::SignalLength, JBB);
+ }//for
+}//Dbdih::sendStartFragreq()
+
+/*************************************************************************/
+/* SET THE INITIAL ACTIVE STATUS ON ALL NODES AND PUT INTO LISTS. */
+/*************************************************************************/
+void Dbdih::setInitialActiveStatus()
+{
+ NodeRecordPtr siaNodeptr;
+ Uint32 tsiaNodeActiveStatus;
+ Uint32 tsiaNoActiveNodes;
+
+ tsiaNoActiveNodes = csystemnodes - cnoHotSpare;
+ for(Uint32 i = 0; i<Sysfile::NODE_STATUS_SIZE; i++)
+ SYSFILE->nodeStatus[i] = 0;
+ for (siaNodeptr.i = 1; siaNodeptr.i < MAX_NDB_NODES; siaNodeptr.i++) {
+ ptrAss(siaNodeptr, nodeRecord);
+ if (siaNodeptr.p->nodeStatus == NodeRecord::ALIVE) {
+ if (tsiaNoActiveNodes == 0) {
+ jam();
+ siaNodeptr.p->activeStatus = Sysfile::NS_HotSpare;
+ } else {
+ jam();
+ tsiaNoActiveNodes = tsiaNoActiveNodes - 1;
+ siaNodeptr.p->activeStatus = Sysfile::NS_Active;
+ }//if
+ } else {
+ jam();
+ siaNodeptr.p->activeStatus = Sysfile::NS_NotDefined;
+ }//if
+ switch (siaNodeptr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ jam();
+ tsiaNodeActiveStatus = Sysfile::NS_Active;
+ break;
+ case Sysfile::NS_HotSpare:
+ jam();
+ tsiaNodeActiveStatus = Sysfile::NS_HotSpare;
+ break;
+ case Sysfile::NS_NotDefined:
+ jam();
+ tsiaNodeActiveStatus = Sysfile::NS_NotDefined;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ Sysfile::setNodeStatus(siaNodeptr.i, SYSFILE->nodeStatus,
+ tsiaNodeActiveStatus);
+ }//for
+}//Dbdih::setInitialActiveStatus()
+
+/*************************************************************************/
+/* SET LCP ACTIVE STATUS AT THE END OF A LOCAL CHECKPOINT. */
+/*************************************************************************/
+void Dbdih::setLcpActiveStatusEnd()
+{
+ NodeRecordPtr nodePtr;
+
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (c_lcpState.m_participatingLQH.get(nodePtr.i)){
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ /*-------------------------------------------------------------------*/
+ /* THE NODE PARTICIPATED IN THIS CHECKPOINT.
+ * WE CAN SET ITS STATUS TO ACTIVE */
+ /*-------------------------------------------------------------------*/
+ nodePtr.p->activeStatus = Sysfile::NS_Active;
+ takeOverCompleted(nodePtr.i);
+ break;
+ case Sysfile::NS_TakeOver:
+ jam();
+ /*-------------------------------------------------------------------*/
+ /* THE NODE HAS COMPLETED A CHECKPOINT AFTER TAKE OVER. WE CAN NOW */
+ /* SET ITS STATUS TO ACTIVE. WE CAN ALSO COMPLETE THE TAKE OVER */
+ /* AND ALSO WE CLEAR THE TAKE OVER NODE IN THE RESTART INFO. */
+ /*-------------------------------------------------------------------*/
+ nodePtr.p->activeStatus = Sysfile::NS_Active;
+ takeOverCompleted(nodePtr.i);
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ }//if
+ }//for
+
+ if(getNodeState().getNodeRestartInProgress()){
+ jam();
+ if(c_lcpState.m_participatingLQH.get(getOwnNodeId())){
+ nodePtr.i = getOwnNodeId();
+ ptrAss(nodePtr, nodeRecord);
+ ndbrequire(nodePtr.p->activeStatus == Sysfile::NS_Active);
+ ndbout_c("NR: setLcpActiveStatusEnd - m_participatingLQH");
+ } else {
+ ndbout_c("NR: setLcpActiveStatusEnd - !m_participatingLQH");
+ }
+ }
+
+ c_lcpState.m_participatingDIH.clear();
+ c_lcpState.m_participatingLQH.clear();
+ if (isMaster()) {
+ jam();
+ setNodeRestartInfoBits();
+ }//if
+}//Dbdih::setLcpActiveStatusEnd()
+
+void Dbdih::takeOverCompleted(Uint32 aNodeId)
+{
+ TakeOverRecordPtr takeOverPtr;
+ takeOverPtr.i = findTakeOver(aNodeId);
+ if (takeOverPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ if (takeOverPtr.p->toMasterStatus != TakeOverRecord::WAIT_LCP) {
+ jam();
+ ndbrequire(!isMaster());
+ return;
+ }//if
+ ndbrequire(isMaster());
+ Sysfile::setTakeOverNode(aNodeId, SYSFILE->takeOver, 0);
+ takeOverPtr.p->toMasterStatus = TakeOverRecord::TO_END_COPY;
+ cstartGcpNow = true;
+ }//if
+}//Dbdih::takeOverCompleted()
+
+/*************************************************************************/
+/* SET LCP ACTIVE STATUS BEFORE STARTING A LOCAL CHECKPOINT. */
+/*************************************************************************/
+void Dbdih::setLcpActiveStatusStart(Signal* signal)
+{
+ NodeRecordPtr nodePtr;
+
+ c_lcpState.m_participatingLQH.clear();
+ c_lcpState.m_participatingDIH.clear();
+
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRecord);
+#if 0
+ if(nodePtr.p->nodeStatus != NodeRecord::NOT_IN_CLUSTER){
+ infoEvent("Node %d nodeStatus=%d activeStatus=%d copyCompleted=%d lcp=%d",
+ nodePtr.i,
+ nodePtr.p->nodeStatus,
+ nodePtr.p->activeStatus,
+ nodePtr.p->copyCompleted,
+ nodePtr.p->m_inclDihLcp);
+ }
+#endif
+ if(nodePtr.p->nodeStatus == NodeRecord::ALIVE && nodePtr.p->m_inclDihLcp){
+ jam();
+ c_lcpState.m_participatingDIH.set(nodePtr.i);
+ }
+
+ if ((nodePtr.p->nodeStatus == NodeRecord::ALIVE) &&
+ (nodePtr.p->copyCompleted)) {
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ jam();
+ /*-------------------------------------------------------------------*/
+ // The normal case. Starting a LCP for a started node which hasn't
+ // missed the previous LCP.
+ /*-------------------------------------------------------------------*/
+ c_lcpState.m_participatingLQH.set(nodePtr.i);
+ break;
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ /*-------------------------------------------------------------------*/
+ // The node is starting up and is participating in a local checkpoint
+ // as the final phase of the start-up. We can still use the checkpoints
+ // on the node after a system restart.
+ /*-------------------------------------------------------------------*/
+ c_lcpState.m_participatingLQH.set(nodePtr.i);
+ break;
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ /*-------------------------------------------------------------------*/
+ // The node is starting up and is participating in a local checkpoint
+ // as the final phase of the start-up. We have missed so
+ // many checkpoints that we no longer can use this node to
+ // recreate fragments from disk.
+ // It must be taken over with the copy fragment process after a system
+ // crash. We indicate this by setting the active status to TAKE_OVER.
+ /*-------------------------------------------------------------------*/
+ nodePtr.p->activeStatus = Sysfile::NS_TakeOver;
+ //break; // Fall through
+ case Sysfile::NS_TakeOver:{
+ TakeOverRecordPtr takeOverPtr;
+ jam();
+ /*-------------------------------------------------------------------*/
+ /* THIS NODE IS CURRENTLY TAKING OVER A FAILED NODE. */
+ /*-------------------------------------------------------------------*/
+ takeOverPtr.i = findTakeOver(nodePtr.i);
+ if (takeOverPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord);
+ if (takeOverPtr.p->toMasterStatus == TakeOverRecord::WAIT_LCP) {
+ jam();
+ /*---------------------------------------------------------------
+ * ALL THE INFORMATION HAVE BEEN REPLICATED TO THE NEW
+ * NODE AND WE ARE ONLY WAITING FOR A LOCAL CHECKPOINT TO BE
+ * PERFORMED ON THE NODE TO SET ITS STATUS TO ACTIVE.
+ */
+ infoEvent("Node %d is WAIT_LCP including in LCP", nodePtr.i);
+ c_lcpState.m_participatingLQH.set(nodePtr.i);
+ }//if
+ }//if
+ break;
+ }
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }//switch
+ } else {
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ jam();
+ nodePtr.p->activeStatus = Sysfile::NS_ActiveMissed_1;
+ break;
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ nodePtr.p->activeStatus = Sysfile::NS_ActiveMissed_2;
+ break;
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ if ((nodePtr.p->nodeStatus == NodeRecord::ALIVE) &&
+ (!nodePtr.p->copyCompleted)) {
+ jam();
+ /*-----------------------------------------------------------------*/
+ // The node is currently starting up and has not completed the
+ // copy phase.
+ // It will thus be in the TAKE_OVER state.
+ /*-----------------------------------------------------------------*/
+ ndbrequire(findTakeOver(nodePtr.i) != RNIL);
+ nodePtr.p->activeStatus = Sysfile::NS_TakeOver;
+ } else {
+ jam();
+ /*-----------------------------------------------------------------*/
+ /* THE NODE IS ACTIVE AND HAS NOT COMPLETED ANY OF THE LAST 3
+ * CHECKPOINTS */
+ /* WE MUST TAKE IT OUT OF ACTION AND START A NEW NODE TO TAKE OVER.*/
+ /*-----------------------------------------------------------------*/
+ nodePtr.p->activeStatus = Sysfile::NS_NotActive_NotTakenOver;
+ }//if
+ break;
+ case Sysfile::NS_TakeOver:
+ jam();
+ break;
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }//switch
+ }//if
+ }//for
+ if (isMaster()) {
+ jam();
+ checkStartTakeOver(signal);
+ setNodeRestartInfoBits();
+ }//if
+}//Dbdih::setLcpActiveStatusStart()
+
+/*************************************************************************/
+/* SET NODE ACTIVE STATUS AT SYSTEM RESTART AND WHEN UPDATED BY MASTER */
+/*************************************************************************/
+void Dbdih::setNodeActiveStatus()
+{
+ NodeRecordPtr snaNodeptr;
+
+ for (snaNodeptr.i = 1; snaNodeptr.i < MAX_NDB_NODES; snaNodeptr.i++) {
+ ptrAss(snaNodeptr, nodeRecord);
+ const Uint32 tsnaNodeBits = Sysfile::getNodeStatus(snaNodeptr.i,
+ SYSFILE->nodeStatus);
+ switch (tsnaNodeBits) {
+ case Sysfile::NS_Active:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_Active;
+ break;
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_ActiveMissed_1;
+ break;
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_ActiveMissed_2;
+ break;
+ case Sysfile::NS_TakeOver:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_TakeOver;
+ break;
+ case Sysfile::NS_HotSpare:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_HotSpare;
+ break;
+ case Sysfile::NS_NotActive_NotTakenOver:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_NotActive_NotTakenOver;
+ break;
+ case Sysfile::NS_NotDefined:
+ jam();
+ snaNodeptr.p->activeStatus = Sysfile::NS_NotDefined;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ }//for
+}//Dbdih::setNodeActiveStatus()
+
+/***************************************************************************/
+/* SET THE NODE GROUP BASED ON THE RESTART INFORMATION OR AS SET BY MASTER */
+/***************************************************************************/
+void Dbdih::setNodeGroups()
+{
+ NodeGroupRecordPtr NGPtr;
+ NodeRecordPtr sngNodeptr;
+ Uint32 Ti;
+
+ for (Ti = 0; Ti < MAX_NDB_NODES; Ti++) {
+ NGPtr.i = Ti;
+ ptrAss(NGPtr, nodeGroupRecord);
+ NGPtr.p->nodeCount = 0;
+ }//for
+ for (sngNodeptr.i = 1; sngNodeptr.i < MAX_NDB_NODES; sngNodeptr.i++) {
+ ptrAss(sngNodeptr, nodeRecord);
+ Sysfile::ActiveStatus s =
+ (Sysfile::ActiveStatus)Sysfile::getNodeStatus(sngNodeptr.i,
+ SYSFILE->nodeStatus);
+ switch (s){
+ case Sysfile::NS_Active:
+ case Sysfile::NS_ActiveMissed_1:
+ case Sysfile::NS_ActiveMissed_2:
+ case Sysfile::NS_NotActive_NotTakenOver:
+ case Sysfile::NS_TakeOver:
+ jam();
+ sngNodeptr.p->nodeGroup = Sysfile::getNodeGroup(sngNodeptr.i,
+ SYSFILE->nodeGroups);
+ NGPtr.i = sngNodeptr.p->nodeGroup;
+ ptrCheckGuard(NGPtr, MAX_NDB_NODES, nodeGroupRecord);
+ NGPtr.p->nodesInGroup[NGPtr.p->nodeCount] = sngNodeptr.i;
+ NGPtr.p->nodeCount++;
+ break;
+ case Sysfile::NS_HotSpare:
+ case Sysfile::NS_NotDefined:
+ jam();
+ sngNodeptr.p->nodeGroup = ZNIL;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ }//for
+ cnoOfNodeGroups = 0;
+ for (Ti = 0; Ti < MAX_NDB_NODES; Ti++) {
+ jam();
+ NGPtr.i = Ti;
+ ptrAss(NGPtr, nodeGroupRecord);
+ if (NGPtr.p->nodeCount != 0) {
+ jam();
+ cnoOfNodeGroups++;
+ }//if
+ }//for
+ cnoHotSpare = csystemnodes - (cnoOfNodeGroups * cnoReplicas);
+}//Dbdih::setNodeGroups()
+
+/*************************************************************************/
+/* SET NODE INFORMATION AFTER RECEIVING RESTART INFORMATION FROM MASTER. */
+/* WE TAKE THE OPPORTUNITY TO SYNCHRONISE OUR DATA WITH THE MASTER. IT */
+/* IS ONLY THE MASTER THAT WILL ACT ON THIS DATA. WE WILL KEEP THEM */
+/* UPDATED FOR THE CASE WHEN WE HAVE TO BECOME MASTER. */
+/*************************************************************************/
+void Dbdih::setNodeInfo(Signal* signal)
+{
+ setNodeActiveStatus();
+ setNodeGroups();
+ sendHOT_SPAREREP(signal);
+}//Dbdih::setNodeInfo()
+
+/*************************************************************************/
+// Keep also DBDICT informed about the Hot Spare situation in the cluster.
+/*************************************************************************/
+void Dbdih::sendHOT_SPAREREP(Signal* signal)
+{
+ NodeRecordPtr locNodeptr;
+ Uint32 Ti = 0;
+ HotSpareRep * const hotSpare = (HotSpareRep*)&signal->theData[0];
+ NodeBitmask::clear(hotSpare->theHotSpareNodes);
+ for (locNodeptr.i = 1; locNodeptr.i < MAX_NDB_NODES; locNodeptr.i++) {
+ ptrAss(locNodeptr, nodeRecord);
+ switch (locNodeptr.p->activeStatus) {
+ case Sysfile::NS_HotSpare:
+ jam();
+ NodeBitmask::set(hotSpare->theHotSpareNodes, locNodeptr.i);
+ Ti++;
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ }//for
+ hotSpare->noHotSpareNodes = Ti;
+ sendSignal(DBDICT_REF, GSN_HOT_SPAREREP,
+ signal, HotSpareRep::SignalLength, JBB);
+}//Dbdih::sendHOT_SPAREREP()
+
+/*************************************************************************/
+/* SET LCP ACTIVE STATUS FOR ALL NODES BASED ON THE INFORMATION IN */
+/* THE RESTART INFORMATION. */
+/*************************************************************************/
+#if 0
+void Dbdih::setNodeLcpActiveStatus()
+{
+ c_lcpState.m_lcpActiveStatus.clear();
+ for (Uint32 i = 1; i < MAX_NDB_NODES; i++) {
+ if (NodeBitmask::get(SYSFILE->lcpActive, i)) {
+ jam();
+ c_lcpState.m_lcpActiveStatus.set(i);
+ }//if
+ }//for
+}//Dbdih::setNodeLcpActiveStatus()
+#endif
+
+/*************************************************************************/
+/* SET THE RESTART INFO BITS BASED ON THE NODES ACTIVE STATUS. */
+/*************************************************************************/
+void Dbdih::setNodeRestartInfoBits()
+{
+ NodeRecordPtr nodePtr;
+ Uint32 tsnrNodeGroup;
+ Uint32 tsnrNodeActiveStatus;
+ Uint32 i;
+ for(i = 1; i < MAX_NDB_NODES; i++){
+ Sysfile::setNodeStatus(i, SYSFILE->nodeStatus, Sysfile::NS_Active);
+ }//for
+ for(i = 1; i < Sysfile::NODE_GROUPS_SIZE; i++){
+ SYSFILE->nodeGroups[i] = 0;
+ }//for
+ NdbNodeBitmask::clear(SYSFILE->lcpActive);
+
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRecord);
+ switch (nodePtr.p->activeStatus) {
+ case Sysfile::NS_Active:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_Active;
+ break;
+ case Sysfile::NS_ActiveMissed_1:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_ActiveMissed_1;
+ break;
+ case Sysfile::NS_ActiveMissed_2:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_ActiveMissed_2;
+ break;
+ case Sysfile::NS_HotSpare:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_HotSpare;
+ break;
+ case Sysfile::NS_TakeOver:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_TakeOver;
+ break;
+ case Sysfile::NS_NotActive_NotTakenOver:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_NotActive_NotTakenOver;
+ break;
+ case Sysfile::NS_NotDefined:
+ jam();
+ tsnrNodeActiveStatus = Sysfile::NS_NotDefined;
+ break;
+ default:
+ ndbrequire(false);
+ tsnrNodeActiveStatus = Sysfile::NS_NotDefined; // remove warning
+ break;
+ }//switch
+ Sysfile::setNodeStatus(nodePtr.i, SYSFILE->nodeStatus,
+ tsnrNodeActiveStatus);
+ if (nodePtr.p->nodeGroup == ZNIL) {
+ jam();
+ tsnrNodeGroup = NO_NODE_GROUP_ID;
+ } else {
+ jam();
+ tsnrNodeGroup = nodePtr.p->nodeGroup;
+ }//if
+ Sysfile::setNodeGroup(nodePtr.i, SYSFILE->nodeGroups, tsnrNodeGroup);
+ if (c_lcpState.m_participatingLQH.get(nodePtr.i)){
+ jam();
+ NodeBitmask::set(SYSFILE->lcpActive, nodePtr.i);
+ }//if
+ }//for
+}//Dbdih::setNodeRestartInfoBits()
+
+/*************************************************************************/
+/* START THE GLOBAL CHECKPOINT PROTOCOL IN MASTER AT START-UP */
+/*************************************************************************/
+void Dbdih::startGcp(Signal* signal)
+{
+ cgcpStatus = GCP_READY;
+ coldGcpStatus = cgcpStatus;
+ coldGcpId = cnewgcp;
+ cgcpSameCounter = 0;
+ signal->theData[0] = DihContinueB::ZSTART_GCP;
+ signal->theData[1] = 0;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+ signal->theData[0] = DihContinueB::ZCHECK_GCP_STOP;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 1);
+}//Dbdih::startGcp()
+
+void Dbdih::updateNodeInfo(FragmentstorePtr fragPtr)
+{
+ ReplicaRecordPtr replicatePtr;
+ Uint32 index = 0;
+ replicatePtr.i = fragPtr.p->storedReplicas;
+ do {
+ jam();
+ ptrCheckGuard(replicatePtr, creplicaFileSize, replicaRecord);
+ ndbrequire(index < MAX_REPLICAS);
+ fragPtr.p->activeNodes[index] = replicatePtr.p->procNode;
+ index++;
+ replicatePtr.i = replicatePtr.p->nextReplica;
+ } while (replicatePtr.i != RNIL);
+ fragPtr.p->fragReplicas = index;
+
+ /* ----------------------------------------------------------------------- */
+ // We switch primary to the preferred primary if the preferred primary is
+ // in the list.
+ /* ----------------------------------------------------------------------- */
+ const Uint32 prefPrim = fragPtr.p->preferredPrimary;
+ for (Uint32 i = 1; i < index; i++) {
+ jam();
+ ndbrequire(i < MAX_REPLICAS);
+ if (fragPtr.p->activeNodes[i] == prefPrim){
+ jam();
+ Uint32 switchNode = fragPtr.p->activeNodes[0];
+ fragPtr.p->activeNodes[0] = prefPrim;
+ fragPtr.p->activeNodes[i] = switchNode;
+ break;
+ }//if
+ }//for
+}//Dbdih::updateNodeInfo()
+
+void Dbdih::writeFragment(RWFragment* wf, FragmentstorePtr fragPtr)
+{
+ writePageWord(wf, wf->fragId);
+ writePageWord(wf, fragPtr.p->preferredPrimary);
+ writePageWord(wf, fragPtr.p->noStoredReplicas);
+ writePageWord(wf, fragPtr.p->noOldStoredReplicas);
+ writePageWord(wf, fragPtr.p->distributionKey);
+}//Dbdih::writeFragment()
+
+void Dbdih::writePageWord(RWFragment* wf, Uint32 dataWord)
+{
+ if (wf->wordIndex >= 2048) {
+ jam();
+ ndbrequire(wf->wordIndex == 2048);
+ allocpage(wf->rwfPageptr);
+ wf->wordIndex = 32;
+ wf->pageIndex++;
+ ndbrequire(wf->pageIndex < 8);
+ wf->rwfTabPtr.p->pageRef[wf->pageIndex] = wf->rwfPageptr.i;
+ wf->rwfTabPtr.p->noPages++;
+ }//if
+ wf->rwfPageptr.p->word[wf->wordIndex] = dataWord;
+ wf->wordIndex++;
+}//Dbdih::writePageWord()
+
+void Dbdih::writeReplicas(RWFragment* wf, Uint32 replicaStartIndex)
+{
+ ReplicaRecordPtr wfReplicaPtr;
+ wfReplicaPtr.i = replicaStartIndex;
+ while (wfReplicaPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(wfReplicaPtr, creplicaFileSize, replicaRecord);
+ writePageWord(wf, wfReplicaPtr.p->procNode);
+ writePageWord(wf, wfReplicaPtr.p->initialGci);
+ writePageWord(wf, wfReplicaPtr.p->noCrashedReplicas);
+ writePageWord(wf, wfReplicaPtr.p->nextLcp);
+ Uint32 i;
+ for (i = 0; i < MAX_LCP_STORED; i++) {
+ writePageWord(wf, wfReplicaPtr.p->maxGciCompleted[i]);
+ writePageWord(wf, wfReplicaPtr.p->maxGciStarted[i]);
+ writePageWord(wf, wfReplicaPtr.p->lcpId[i]);
+ writePageWord(wf, wfReplicaPtr.p->lcpStatus[i]);
+ }//if
+ for (i = 0; i < 8; i++) {
+ writePageWord(wf, wfReplicaPtr.p->createGci[i]);
+ writePageWord(wf, wfReplicaPtr.p->replicaLastGci[i]);
+ }//if
+
+ wfReplicaPtr.i = wfReplicaPtr.p->nextReplica;
+ }//while
+}//Dbdih::writeReplicas()
+
+void Dbdih::writeRestorableGci(Signal* signal, FileRecordPtr filePtr)
+{
+ for (Uint32 i = 0; i < Sysfile::SYSFILE_SIZE32; i++) {
+ sysfileDataToFile[i] = sysfileData[i];
+ }//for
+ signal->theData[0] = filePtr.p->fileRef;
+ signal->theData[1] = reference();
+ signal->theData[2] = filePtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS_SYNCH;
+ signal->theData[4] = ZVAR_NO_CRESTART_INFO_TO_FILE;
+ signal->theData[5] = 1; /* AMOUNT OF PAGES */
+ signal->theData[6] = 0; /* MEMORY PAGE = 0 SINCE COMMON STORED VARIABLE */
+ signal->theData[7] = 0;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+}//Dbdih::writeRestorableGci()
+
+void Dbdih::writeTabfile(Signal* signal, TabRecord* tab, FileRecordPtr filePtr)
+{
+ signal->theData[0] = filePtr.p->fileRef;
+ signal->theData[1] = reference();
+ signal->theData[2] = filePtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS;
+ signal->theData[4] = ZVAR_NO_WORD;
+ signal->theData[5] = tab->noPages;
+ for (Uint32 i = 0; i < tab->noPages; i++) {
+ jam();
+ signal->theData[6 + (2 * i)] = tab->pageRef[i];
+ signal->theData[7 + (2 * i)] = i;
+ }//for
+ Uint32 length = 6 + (2 * tab->noPages);
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, length, JBA);
+}//Dbdih::writeTabfile()
+
+void Dbdih::execDEBUG_SIG(Signal* signal)
+{
+ signal = signal; //Avoid compiler warnings
+}//Dbdih::execDEBUG_SIG()
+
+void
+Dbdih::execDUMP_STATE_ORD(Signal* signal)
+{
+ DumpStateOrd * const & dumpState = (DumpStateOrd *)&signal->theData[0];
+ if (dumpState->args[0] == DumpStateOrd::DihDumpNodeRestartInfo) {
+ infoEvent("c_nodeStartMaster.blockLcp = %d, c_nodeStartMaster.blockGcp = %d, c_nodeStartMaster.wait = %d",
+ c_nodeStartMaster.blockLcp, c_nodeStartMaster.blockGcp, c_nodeStartMaster.wait);
+ infoEvent("cstartGcpNow = %d, cgcpStatus = %d",
+ cstartGcpNow, cgcpStatus);
+ infoEvent("cfirstVerifyQueue = %d, cverifyQueueCounter = %d",
+ cfirstVerifyQueue, cverifyQueueCounter);
+ infoEvent("cgcpOrderBlocked = %d, cgcpStartCounter = %d",
+ cgcpOrderBlocked, cgcpStartCounter);
+ }//if
+ if (dumpState->args[0] == DumpStateOrd::DihDumpNodeStatusInfo) {
+ NodeRecordPtr localNodePtr;
+ infoEvent("Printing nodeStatus of all nodes");
+ for (localNodePtr.i = 1; localNodePtr.i < MAX_NDB_NODES; localNodePtr.i++) {
+ ptrAss(localNodePtr, nodeRecord);
+ if (localNodePtr.p->nodeStatus != NodeRecord::NOT_IN_CLUSTER) {
+ infoEvent("Node = %d has status = %d",
+ localNodePtr.i, localNodePtr.p->nodeStatus);
+ }//if
+ }//for
+ }//if
+
+ if (dumpState->args[0] == DumpStateOrd::DihPrintFragmentation){
+ infoEvent("Printing fragmentation of all tables --");
+ for(Uint32 i = 0; i<ctabFileSize; i++){
+ TabRecordPtr tabPtr;
+ tabPtr.i = i;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ if(tabPtr.p->tabStatus != TabRecord::TS_ACTIVE)
+ continue;
+
+ for(Uint32 j = 0; j < tabPtr.p->totalfragments; j++){
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, j, fragPtr);
+
+ Uint32 nodeOrder[MAX_REPLICAS];
+ const Uint32 noOfReplicas = extractNodeInfo(fragPtr.p, nodeOrder);
+ char buf[100];
+ BaseString::snprintf(buf, sizeof(buf), " Table %d Fragment %d - ", tabPtr.i, j);
+ for(Uint32 k = 0; k < noOfReplicas; k++){
+ char tmp[100];
+ BaseString::snprintf(tmp, sizeof(tmp), "%d ", nodeOrder[k]);
+ strcat(buf, tmp);
+ }
+ infoEvent(buf);
+ }
+ }
+ }
+
+ if (signal->theData[0] == 7000) {
+ infoEvent("ctimer = %d, cgcpParticipantState = %d, cgcpStatus = %d",
+ c_lcpState.ctimer, cgcpParticipantState, cgcpStatus);
+ infoEvent("coldGcpStatus = %d, coldGcpId = %d, cmasterState = %d",
+ coldGcpStatus, coldGcpId, cmasterState);
+ infoEvent("cmasterTakeOverNode = %d, ctcCounter = %d",
+ cmasterTakeOverNode, c_lcpState.ctcCounter);
+ }//if
+ if (signal->theData[0] == 7001) {
+ infoEvent("c_lcpState.keepGci = %d",
+ c_lcpState.keepGci);
+ infoEvent("c_lcpState.lcpStatus = %d, clcpStartGcp = %d",
+ c_lcpState.lcpStatus,
+ c_lcpState.lcpStartGcp);
+ infoEvent("cgcpStartCounter = %d, cimmediateLcpStart = %d",
+ cgcpStartCounter, c_lcpState.immediateLcpStart);
+ }//if
+ if (signal->theData[0] == 7002) {
+ infoEvent("cnoOfActiveTables = %d, cgcpDelay = %d",
+ cnoOfActiveTables, cgcpDelay);
+ infoEvent("cdictblockref = %d, cfailurenr = %d",
+ cdictblockref, cfailurenr);
+ infoEvent("con_lineNodes = %d, reference() = %d, creceivedfrag = %d",
+ con_lineNodes, reference(), creceivedfrag);
+ }//if
+ if (signal->theData[0] == 7003) {
+ infoEvent("cfirstAliveNode = %d, cgckptflag = %d",
+ cfirstAliveNode, cgckptflag);
+ infoEvent("clocallqhblockref = %d, clocaltcblockref = %d, cgcpOrderBlocked = %d",
+ clocallqhblockref, clocaltcblockref, cgcpOrderBlocked);
+ infoEvent("cstarttype = %d, csystemnodes = %d, currentgcp = %d",
+ cstarttype, csystemnodes, currentgcp);
+ }//if
+ if (signal->theData[0] == 7004) {
+ infoEvent("cmasterdihref = %d, cownNodeId = %d, cnewgcp = %d",
+ cmasterdihref, cownNodeId, cnewgcp);
+ infoEvent("cndbStartReqBlockref = %d, cremainingfrags = %d",
+ cndbStartReqBlockref, cremainingfrags);
+ infoEvent("cntrlblockref = %d, cgcpSameCounter = %d, coldgcp = %d",
+ cntrlblockref, cgcpSameCounter, coldgcp);
+ }//if
+ if (signal->theData[0] == 7005) {
+ infoEvent("crestartGci = %d",
+ crestartGci);
+ }//if
+ if (signal->theData[0] == 7006) {
+ infoEvent("clcpDelay = %d, cgcpMasterTakeOverState = %d",
+ c_lcpState.clcpDelay, cgcpMasterTakeOverState);
+ infoEvent("cmasterNodeId = %d", cmasterNodeId);
+ infoEvent("cnoHotSpare = %d, c_nodeStartMaster.startNode = %d, c_nodeStartMaster.wait = %d",
+ cnoHotSpare, c_nodeStartMaster.startNode, c_nodeStartMaster.wait);
+ }//if
+ if (signal->theData[0] == 7007) {
+ infoEvent("c_nodeStartMaster.failNr = %d", c_nodeStartMaster.failNr);
+ infoEvent("c_nodeStartMaster.startInfoErrorCode = %d",
+ c_nodeStartMaster.startInfoErrorCode);
+ infoEvent("c_nodeStartMaster.blockLcp = %d, c_nodeStartMaster.blockGcp = %d",
+ c_nodeStartMaster.blockLcp, c_nodeStartMaster.blockGcp);
+ }//if
+ if (signal->theData[0] == 7008) {
+ infoEvent("cfirstDeadNode = %d, cstartPhase = %d, cnoReplicas = %d",
+ cfirstDeadNode, cstartPhase, cnoReplicas);
+ infoEvent("cwaitLcpSr = %d",cwaitLcpSr);
+ }//if
+ if (signal->theData[0] == 7009) {
+ infoEvent("ccalcOldestRestorableGci = %d, cnoOfNodeGroups = %d",
+ c_lcpState.oldestRestorableGci, cnoOfNodeGroups);
+ infoEvent("cstartGcpNow = %d",
+ cstartGcpNow);
+ infoEvent("crestartGci = %d",
+ crestartGci);
+ }//if
+ if (signal->theData[0] == 7010) {
+ infoEvent("cminHotSpareNodes = %d, c_lcpState.lcpStatusUpdatedPlace = %d, cLcpStart = %d",
+ cminHotSpareNodes, c_lcpState.lcpStatusUpdatedPlace, c_lcpState.lcpStart);
+ infoEvent("c_blockCommit = %d, c_blockCommitNo = %d",
+ c_blockCommit, c_blockCommitNo);
+ }//if
+ if (signal->theData[0] == 7011){
+ infoEvent("c_COPY_GCIREQ_Counter = %s",
+ c_COPY_GCIREQ_Counter.getText());
+ infoEvent("c_COPY_TABREQ_Counter = %s",
+ c_COPY_TABREQ_Counter.getText());
+ infoEvent("c_CREATE_FRAGREQ_Counter = %s",
+ c_CREATE_FRAGREQ_Counter.getText());
+ infoEvent("c_DIH_SWITCH_REPLICA_REQ_Counter = %s",
+ c_DIH_SWITCH_REPLICA_REQ_Counter.getText());
+ infoEvent("c_EMPTY_LCP_REQ_Counter = %s",c_EMPTY_LCP_REQ_Counter.getText());
+ infoEvent("c_END_TOREQ_Counter = %s", c_END_TOREQ_Counter.getText());
+ infoEvent("c_GCP_COMMIT_Counter = %s", c_GCP_COMMIT_Counter.getText());
+ infoEvent("c_GCP_PREPARE_Counter = %s", c_GCP_PREPARE_Counter.getText());
+ infoEvent("c_GCP_SAVEREQ_Counter = %s", c_GCP_SAVEREQ_Counter.getText());
+ infoEvent("c_INCL_NODEREQ_Counter = %s", c_INCL_NODEREQ_Counter.getText());
+ infoEvent("c_MASTER_GCPREQ_Counter = %s",
+ c_MASTER_GCPREQ_Counter.getText());
+ infoEvent("c_MASTER_LCPREQ_Counter = %s",
+ c_MASTER_LCPREQ_Counter.getText());
+ infoEvent("c_START_INFOREQ_Counter = %s",
+ c_START_INFOREQ_Counter.getText());
+ infoEvent("c_START_RECREQ_Counter = %s", c_START_RECREQ_Counter.getText());
+ infoEvent("c_START_TOREQ_Counter = %s", c_START_TOREQ_Counter.getText());
+ infoEvent("c_STOP_ME_REQ_Counter = %s", c_STOP_ME_REQ_Counter.getText());
+ infoEvent("c_TC_CLOPSIZEREQ_Counter = %s",
+ c_TC_CLOPSIZEREQ_Counter.getText());
+ infoEvent("c_TCGETOPSIZEREQ_Counter = %s",
+ c_TCGETOPSIZEREQ_Counter.getText());
+ infoEvent("c_UPDATE_TOREQ_Counter = %s", c_UPDATE_TOREQ_Counter.getText());
+ }
+
+ if(signal->theData[0] == 7012){
+ char buf[8*_NDB_NODE_BITMASK_SIZE+1];
+ infoEvent("ParticipatingDIH = %s", c_lcpState.m_participatingDIH.getText(buf));
+ infoEvent("ParticipatingLQH = %s", c_lcpState.m_participatingLQH.getText(buf));
+ infoEvent("m_LCP_COMPLETE_REP_Counter_DIH = %s",
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_DIH.getText());
+ infoEvent("m_LCP_COMPLETE_REP_Counter_LQH = %s",
+ c_lcpState.m_LCP_COMPLETE_REP_Counter_LQH.getText());
+ infoEvent("m_LAST_LCP_FRAG_ORD = %s",
+ c_lcpState.m_LAST_LCP_FRAG_ORD.getText());
+ infoEvent("m_LCP_COMPLETE_REP_From_Master_Received = %d",
+ c_lcpState.m_LCP_COMPLETE_REP_From_Master_Received);
+
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if(nodePtr.p->nodeStatus == NodeRecord::ALIVE){
+ Uint32 i;
+ for(i = 0; i<nodePtr.p->noOfStartedChkpt; i++){
+ infoEvent("Node %d: started: table=%d fragment=%d replica=%d",
+ nodePtr.i,
+ nodePtr.p->startedChkpt[i].tableId,
+ nodePtr.p->startedChkpt[i].fragId,
+ nodePtr.p->startedChkpt[i].replicaPtr);
+ }
+
+ for(i = 0; i<nodePtr.p->noOfQueuedChkpt; i++){
+ infoEvent("Node %d: queued: table=%d fragment=%d replica=%d",
+ nodePtr.i,
+ nodePtr.p->queuedChkpt[i].tableId,
+ nodePtr.p->queuedChkpt[i].fragId,
+ nodePtr.p->queuedChkpt[i].replicaPtr);
+ }
+ }
+ }
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::DihDumpLCPState){
+ infoEvent("-- Node %d LCP STATE --", getOwnNodeId());
+ infoEvent("lcpStatus = %d (update place = %d) ",
+ c_lcpState.lcpStatus, c_lcpState.lcpStatusUpdatedPlace);
+ infoEvent
+ ("lcpStart = %d lcpStartGcp = %d keepGci = %d oldestRestorable = %d",
+ c_lcpState.lcpStart, c_lcpState.lcpStartGcp,
+ c_lcpState.keepGci, c_lcpState.oldestRestorableGci);
+
+ infoEvent
+ ("immediateLcpStart = %d masterLcpNodeId = %d",
+ c_lcpState.immediateLcpStart,
+ refToNode(c_lcpState.m_masterLcpDihRef));
+ infoEvent("-- Node %d LCP STATE --", getOwnNodeId());
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::DihDumpLCPMasterTakeOver){
+ infoEvent("-- Node %d LCP MASTER TAKE OVER STATE --", getOwnNodeId());
+ infoEvent
+ ("c_lcpMasterTakeOverState.state = %d updatePlace = %d failedNodeId = %d",
+ c_lcpMasterTakeOverState.state,
+ c_lcpMasterTakeOverState.updatePlace,
+ c_lcpMasterTakeOverState.failedNodeId);
+
+ infoEvent("c_lcpMasterTakeOverState.minTableId = %u minFragId = %u",
+ c_lcpMasterTakeOverState.minTableId,
+ c_lcpMasterTakeOverState.minFragId);
+
+ infoEvent("-- Node %d LCP MASTER TAKE OVER STATE --", getOwnNodeId());
+ }
+
+ if (signal->theData[0] == 7015){
+ for(Uint32 i = 0; i<ctabFileSize; i++){
+ TabRecordPtr tabPtr;
+ tabPtr.i = i;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ if(tabPtr.p->tabStatus != TabRecord::TS_ACTIVE)
+ continue;
+
+ infoEvent
+ ("Table %d: TabCopyStatus: %d TabUpdateStatus: %d TabLcpStatus: %d",
+ tabPtr.i,
+ tabPtr.p->tabCopyStatus,
+ tabPtr.p->tabUpdateState,
+ tabPtr.p->tabLcpStatus);
+
+ FragmentstorePtr fragPtr;
+ for (Uint32 fid = 0; fid < tabPtr.p->totalfragments; fid++) {
+ jam();
+ getFragstore(tabPtr.p, fid, fragPtr);
+
+ char buf[100], buf2[100];
+ BaseString::snprintf(buf, sizeof(buf), " Fragment %d: noLcpReplicas==%d ",
+ fid, fragPtr.p->noLcpReplicas);
+
+ Uint32 num=0;
+ ReplicaRecordPtr replicaPtr;
+ replicaPtr.i = fragPtr.p->storedReplicas;
+ do {
+ ptrCheckGuard(replicaPtr, creplicaFileSize, replicaRecord);
+ BaseString::snprintf(buf2, sizeof(buf2), "%s %d(on %d)=%d(%s)",
+ buf, num,
+ replicaPtr.p->procNode,
+ replicaPtr.p->lcpIdStarted,
+ replicaPtr.p->lcpOngoingFlag ? "Ongoing" : "Idle");
+ BaseString::snprintf(buf, sizeof(buf), "%s", buf2);
+
+ num++;
+ replicaPtr.i = replicaPtr.p->nextReplica;
+ } while (replicaPtr.i != RNIL);
+ infoEvent(buf);
+ }
+ }
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::EnableUndoDelayDataWrite){
+ ndbout << "Dbdih:: delay write of datapages for table = "
+ << dumpState->args[1]<< endl;
+ // Send this dump to ACC and TUP
+ EXECUTE_DIRECT(DBACC, GSN_DUMP_STATE_ORD, signal, 2);
+ EXECUTE_DIRECT(DBTUP, GSN_DUMP_STATE_ORD, signal, 2);
+
+ // Start immediate LCP
+ c_lcpState.ctimer += (1 << c_lcpState.clcpDelay);
+ return;
+ }
+
+ if (signal->theData[0] == DumpStateOrd::DihAllAllowNodeStart) {
+ for (Uint32 i = 1; i < MAX_NDB_NODES; i++)
+ setAllowNodeStart(i, true);
+ return;
+ }//if
+ if (signal->theData[0] == DumpStateOrd::DihMinTimeBetweenLCP) {
+ // Set time between LCP to min value
+ ndbout << "Set time between LCP to min value" << endl;
+ c_lcpState.clcpDelay = 0; // TimeBetweenLocalCheckpoints.min
+ return;
+ }
+ if (signal->theData[0] == DumpStateOrd::DihMaxTimeBetweenLCP) {
+ // Set time between LCP to max value
+ ndbout << "Set time between LCP to max value" << endl;
+ c_lcpState.clcpDelay = 31; // TimeBetweenLocalCheckpoints.max
+ return;
+ }
+
+ if(dumpState->args[0] == 7098){
+ if(signal->length() == 3){
+ jam();
+ infoEvent("startLcpRoundLoopLab(tabel=%d, fragment=%d)",
+ signal->theData[1], signal->theData[2]);
+ startLcpRoundLoopLab(signal, signal->theData[1], signal->theData[2]);
+ return;
+ } else {
+ infoEvent("Invalid no of arguments to 7098 - startLcpRoundLoopLab -"
+ " expected 2 (tableId, fragmentId)");
+ }
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::DihStartLcpImmediately){
+ c_lcpState.ctimer += (1 << c_lcpState.clcpDelay);
+ return;
+ }
+}//Dbdih::execDUMP_STATE_ORD()
+
+void
+Dbdih::execPREP_DROP_TAB_REQ(Signal* signal){
+ jamEntry();
+
+ PrepDropTabReq* req = (PrepDropTabReq*)signal->getDataPtr();
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+
+ PrepDropTabRef::ErrorCode err = PrepDropTabRef::OK;
+ { /**
+ * Check table state
+ */
+ bool ok = false;
+ switch(tabPtr.p->tabStatus){
+ case TabRecord::TS_IDLE:
+ ok = true;
+ jam();
+ err = PrepDropTabRef::NoSuchTable;
+ break;
+ case TabRecord::TS_DROPPING:
+ ok = true;
+ jam();
+ err = PrepDropTabRef::PrepDropInProgress;
+ break;
+ case TabRecord::TS_CREATING:
+ jam();
+ ok = true;
+ break;
+ case TabRecord::TS_ACTIVE:
+ ok = true;
+ jam();
+ break;
+ }
+ ndbrequire(ok);
+ }
+
+ if(err != PrepDropTabRef::OK){
+ jam();
+ PrepDropTabRef* ref = (PrepDropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = err;
+ sendSignal(senderRef, GSN_PREP_DROP_TAB_REF, signal,
+ PrepDropTabRef::SignalLength, JBB);
+ return;
+ }
+
+ tabPtr.p->tabStatus = TabRecord::TS_DROPPING;
+ tabPtr.p->m_prepDropTab.senderRef = senderRef;
+ tabPtr.p->m_prepDropTab.senderData = senderData;
+
+ if(isMaster()){
+ /**
+ * Remove from queue
+ */
+ NodeRecordPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRecord);
+ if (c_lcpState.m_participatingLQH.get(nodePtr.i)){
+
+ Uint32 index = 0;
+ Uint32 count = nodePtr.p->noOfQueuedChkpt;
+ while(index < count){
+ if(nodePtr.p->queuedChkpt[index].tableId == tabPtr.i){
+ jam();
+ // ndbout_c("Unqueuing %d", index);
+
+ count--;
+ for(Uint32 i = index; i<count; i++){
+ jam();
+ nodePtr.p->queuedChkpt[i] = nodePtr.p->queuedChkpt[i + 1];
+ }
+ } else {
+ index++;
+ }
+ }
+ nodePtr.p->noOfQueuedChkpt = count;
+ }
+ }
+ }
+
+ { /**
+ * Check table lcp state
+ */
+
+ bool ok = false;
+ switch(tabPtr.p->tabLcpStatus){
+ case TabRecord::TLS_COMPLETED:
+ case TabRecord::TLS_WRITING_TO_FILE:
+ ok = true;
+ jam();
+ break;
+ return;
+ case TabRecord::TLS_ACTIVE:
+ ok = true;
+ jam();
+
+ tabPtr.p->tabLcpStatus = TabRecord::TLS_COMPLETED;
+
+ /**
+ * First check if all fragments are done
+ */
+ if(checkLcpAllTablesDoneInLqh()){
+ jam();
+
+ ndbout_c("This is the last table");
+
+ /**
+ * Then check if saving of tab info is done for all tables
+ */
+ LcpStatus a = c_lcpState.lcpStatus;
+ checkLcpCompletedLab(signal);
+
+ if(a != c_lcpState.lcpStatus){
+ ndbout_c("And all tables are written to already written disk");
+ }
+ }
+ break;
+ }
+ ndbrequire(ok);
+ }
+
+ { /**
+ * Send WaitDropTabReq to all LQH
+ */
+ WaitDropTabReq * req = (WaitDropTabReq*)signal->getDataPtrSend();
+ req->tableId = tabPtr.i;
+ req->senderRef = reference();
+
+ NodeRecordPtr nodePtr;
+ nodePtr.i = cfirstAliveNode;
+ tabPtr.p->m_prepDropTab.waitDropTabCount.clearWaitingFor();
+ while(nodePtr.i != RNIL){
+ jam();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+
+ tabPtr.p->m_prepDropTab.waitDropTabCount.setWaitingFor(nodePtr.i);
+ sendSignal(calcLqhBlockRef(nodePtr.i), GSN_WAIT_DROP_TAB_REQ,
+ signal, WaitDropTabReq::SignalLength, JBB);
+
+ nodePtr.i = nodePtr.p->nextNode;
+ }
+ }
+
+ waitDropTabWritingToFile(signal, tabPtr);
+}
+
+void
+Dbdih::waitDropTabWritingToFile(Signal* signal, TabRecordPtr tabPtr){
+
+ if(tabPtr.p->tabLcpStatus == TabRecord::TLS_WRITING_TO_FILE){
+ jam();
+ signal->theData[0] = DihContinueB::WAIT_DROP_TAB_WRITING_TO_FILE;
+ signal->theData[1] = tabPtr.i;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 2);
+ return;
+ }
+
+ ndbrequire(tabPtr.p->tabLcpStatus == TabRecord::TLS_COMPLETED);
+ checkPrepDropTabComplete(signal, tabPtr);
+}
+
+void
+Dbdih::checkPrepDropTabComplete(Signal* signal, TabRecordPtr tabPtr){
+
+ if(tabPtr.p->tabLcpStatus != TabRecord::TLS_COMPLETED){
+ jam();
+ return;
+ }
+
+ if(!tabPtr.p->m_prepDropTab.waitDropTabCount.done()){
+ jam();
+ return;
+ }
+
+ const Uint32 ref = tabPtr.p->m_prepDropTab.senderRef;
+ if(ref != 0){
+ PrepDropTabConf* conf = (PrepDropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ conf->senderData = tabPtr.p->m_prepDropTab.senderData;
+ sendSignal(tabPtr.p->m_prepDropTab.senderRef, GSN_PREP_DROP_TAB_CONF,
+ signal, PrepDropTabConf::SignalLength, JBB);
+ tabPtr.p->m_prepDropTab.senderRef = 0;
+ }
+}
+
+void
+Dbdih::execWAIT_DROP_TAB_REF(Signal* signal){
+ jamEntry();
+ WaitDropTabRef * ref = (WaitDropTabRef*)signal->getDataPtr();
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = ref->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_DROPPING);
+ Uint32 nodeId = refToNode(ref->senderRef);
+
+ ndbrequire(ref->errorCode == WaitDropTabRef::NoSuchTable ||
+ ref->errorCode == WaitDropTabRef::NF_FakeErrorREF);
+
+ tabPtr.p->m_prepDropTab.waitDropTabCount.clearWaitingFor(nodeId);
+ checkPrepDropTabComplete(signal, tabPtr);
+}
+
+void
+Dbdih::execWAIT_DROP_TAB_CONF(Signal* signal){
+ jamEntry();
+ WaitDropTabConf * conf = (WaitDropTabConf*)signal->getDataPtr();
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = conf->tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_DROPPING);
+ Uint32 nodeId = refToNode(conf->senderRef);
+ tabPtr.p->m_prepDropTab.waitDropTabCount.clearWaitingFor(nodeId);
+ checkPrepDropTabComplete(signal, tabPtr);
+}
+
+void
+Dbdih::checkWaitDropTabFailedLqh(Signal* signal, Uint32 nodeId, Uint32 tableId){
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+
+ WaitDropTabConf * conf = (WaitDropTabConf*)signal->getDataPtr();
+ conf->tableId = tableId;
+
+ const Uint32 RT_BREAK = 16;
+ for(Uint32 i = 0; i<RT_BREAK && tabPtr.i < ctabFileSize; i++, tabPtr.i++){
+ ptrAss(tabPtr, tabRecord);
+ if(tabPtr.p->tabStatus == TabRecord::TS_DROPPING){
+ if(tabPtr.p->m_prepDropTab.waitDropTabCount.isWaitingFor(nodeId)){
+ conf->senderRef = calcLqhBlockRef(nodeId);
+ execWAIT_DROP_TAB_CONF(signal);
+ tabPtr.i++;
+ break;
+ }
+ }
+ }
+
+ if(tabPtr.i == ctabFileSize){
+ /**
+ * Finished
+ */
+ jam();
+ return;
+ }
+
+ signal->theData[0] = DihContinueB::CHECK_WAIT_DROP_TAB_FAILED_LQH;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+}
+
+
+void
+Dbdih::execNDB_TAMPER(Signal* signal)
+{
+ if ((ERROR_INSERTED(7011)) &&
+ (signal->theData[0] == 7012)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ calculateKeepGciLab(signal, 0, 0);
+ return;
+ }//if
+ SET_ERROR_INSERT_VALUE(signal->theData[0]);
+ return;
+}//Dbdih::execNDB_TAMPER()
+
+void Dbdih::execSET_VAR_REQ(Signal* signal) {
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+ int val = setVarReq->value();
+
+
+ switch (var) {
+ case TimeBetweenLocalCheckpoints:
+ c_lcpState.clcpDelay = val;
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case TimeBetweenGlobalCheckpoints:
+ cgcpDelay = val;
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ } // switch
+#endif
+}
+
+void Dbdih::execBLOCK_COMMIT_ORD(Signal* signal){
+ BlockCommitOrd* const block = (BlockCommitOrd *)&signal->theData[0];
+
+ jamEntry();
+#if 0
+ ndbrequire(c_blockCommit == false ||
+ c_blockCommitNo == block->failNo);
+#else
+ if(!(c_blockCommit == false || c_blockCommitNo == block->failNo)){
+ infoEvent("Possible bug in Dbdih::execBLOCK_COMMIT_ORD c_blockCommit = %d c_blockCommitNo = %d"
+ " sig->failNo = %d", c_blockCommit, c_blockCommitNo, block->failNo);
+ }
+#endif
+ c_blockCommit = true;
+ c_blockCommitNo = block->failNo;
+}
+
+void Dbdih::execUNBLOCK_COMMIT_ORD(Signal* signal){
+ UnblockCommitOrd* const unblock = (UnblockCommitOrd *)&signal->theData[0];
+ (void)unblock;
+
+ jamEntry();
+
+ if(c_blockCommit == true){
+ jam();
+ // ndbrequire(c_blockCommitNo == unblock->failNo);
+
+ c_blockCommit = false;
+ emptyverificbuffer(signal, true);
+ }
+}
+
+void Dbdih::execSTOP_PERM_REQ(Signal* signal){
+
+ jamEntry();
+
+ StopPermReq* const req = (StopPermReq*)&signal->theData[0];
+ StopPermRef* const ref = (StopPermRef*)&signal->theData[0];
+
+ const Uint32 senderData = req->senderData;
+ const BlockReference senderRef = req->senderRef;
+ const NodeId nodeId = refToNode(senderRef);
+
+ if (isMaster()) {
+ /**
+ * Master
+ */
+ jam();
+ CRASH_INSERTION(7065);
+ if (c_stopPermMaster.clientRef != 0) {
+ jam();
+
+ ref->senderData = senderData;
+ ref->errorCode = StopPermRef::NodeShutdownInProgress;
+ sendSignal(senderRef, GSN_STOP_PERM_REF, signal,
+ StopPermRef::SignalLength, JBB);
+ return;
+ }//if
+
+ if (c_nodeStartMaster.activeState) {
+ jam();
+ ref->senderData = senderData;
+ ref->errorCode = StopPermRef::NodeStartInProgress;
+ sendSignal(senderRef, GSN_STOP_PERM_REF, signal,
+ StopPermRef::SignalLength, JBB);
+ return;
+ }//if
+
+ /**
+ * Lock
+ */
+ c_nodeStartMaster.activeState = true;
+ c_stopPermMaster.clientRef = senderRef;
+
+ c_stopPermMaster.clientData = senderData;
+ c_stopPermMaster.returnValue = 0;
+ c_switchReplicas.clear();
+
+ Mutex mutex(signal, c_mutexMgr, c_switchPrimaryMutexHandle);
+ Callback c = { safe_cast(&Dbdih::switch_primary_stop_node), nodeId };
+ ndbrequire(mutex.lock(c));
+ } else {
+ /**
+ * Proxy part
+ */
+ jam();
+ CRASH_INSERTION(7066);
+ if(c_stopPermProxy.clientRef != 0){
+ jam();
+ ref->senderData = senderData;
+ ref->errorCode = StopPermRef::NodeShutdownInProgress;
+ sendSignal(senderRef, GSN_STOP_PERM_REF, signal, 2, JBB);
+ return;
+ }//if
+
+ c_stopPermProxy.clientRef = senderRef;
+ c_stopPermProxy.masterRef = cmasterdihref;
+ c_stopPermProxy.clientData = senderData;
+
+ req->senderRef = reference();
+ req->senderData = senderData;
+ sendSignal(cmasterdihref, GSN_STOP_PERM_REQ, signal,
+ StopPermReq::SignalLength, JBB);
+ }//if
+}//Dbdih::execSTOP_PERM_REQ()
+
+void
+Dbdih::switch_primary_stop_node(Signal* signal, Uint32 node_id, Uint32 ret_val)
+{
+ ndbrequire(ret_val == 0);
+ signal->theData[0] = DihContinueB::SwitchReplica;
+ signal->theData[1] = node_id;
+ signal->theData[2] = 0; // table id
+ signal->theData[3] = 0; // fragment id
+ sendSignal(reference(), GSN_CONTINUEB, signal, 4, JBB);
+}
+
+void Dbdih::execSTOP_PERM_REF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(c_stopPermProxy.clientRef != 0);
+ ndbrequire(c_stopPermProxy.masterRef == signal->senderBlockRef());
+ sendSignal(c_stopPermProxy.clientRef, GSN_STOP_PERM_REF, signal, 2, JBB);
+ c_stopPermProxy.clientRef = 0;
+}//Dbdih::execSTOP_PERM_REF()
+
+void Dbdih::execSTOP_PERM_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(c_stopPermProxy.clientRef != 0);
+ ndbrequire(c_stopPermProxy.masterRef == signal->senderBlockRef());
+ sendSignal(c_stopPermProxy.clientRef, GSN_STOP_PERM_CONF, signal, 1, JBB);
+ c_stopPermProxy.clientRef = 0;
+}//Dbdih::execSTOP_PERM_CONF()
+
+void Dbdih::execDIH_SWITCH_REPLICA_REQ(Signal* signal)
+{
+ jamEntry();
+ DihSwitchReplicaReq* const req = (DihSwitchReplicaReq*)&signal->theData[0];
+ const Uint32 tableId = req->tableId;
+ const Uint32 fragNo = req->fragNo;
+ const BlockReference senderRef = req->senderRef;
+
+ CRASH_INSERTION(7067);
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_ACTIVE);
+ if (tabPtr.p->tabCopyStatus != TabRecord::CS_IDLE) {
+ jam();
+ sendSignal(reference(), GSN_DIH_SWITCH_REPLICA_REQ, signal,
+ DihSwitchReplicaReq::SignalLength, JBB);
+ return;
+ }//if
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragNo, fragPtr);
+
+ /**
+ * Do funky stuff
+ */
+ Uint32 oldOrder[MAX_REPLICAS];
+ const Uint32 noOfReplicas = extractNodeInfo(fragPtr.p, oldOrder);
+
+ if (noOfReplicas < req->noOfReplicas) {
+ jam();
+ //---------------------------------------------------------------------
+ // A crash occurred in the middle of our switch handling.
+ //---------------------------------------------------------------------
+ DihSwitchReplicaRef* const ref = (DihSwitchReplicaRef*)&signal->theData[0];
+ ref->senderNode = cownNodeId;
+ ref->errorCode = StopPermRef::NF_CausedAbortOfStopProcedure;
+ sendSignal(senderRef, GSN_DIH_SWITCH_REPLICA_REF, signal,
+ DihSwitchReplicaRef::SignalLength, JBB);
+ }//if
+ for (Uint32 i = 0; i < noOfReplicas; i++) {
+ jam();
+ ndbrequire(i < MAX_REPLICAS);
+ fragPtr.p->activeNodes[i] = req->newNodeOrder[i];
+ }//for
+ /**
+ * Reply
+ */
+ DihSwitchReplicaConf* const conf = (DihSwitchReplicaConf*)&signal->theData[0];
+ conf->senderNode = cownNodeId;
+ sendSignal(senderRef, GSN_DIH_SWITCH_REPLICA_CONF, signal,
+ DihSwitchReplicaConf::SignalLength, JBB);
+}//Dbdih::execDIH_SWITCH_REPLICA_REQ()
+
+void Dbdih::execDIH_SWITCH_REPLICA_CONF(Signal* signal)
+{
+ jamEntry();
+ /**
+ * Response to master
+ */
+ CRASH_INSERTION(7068);
+ DihSwitchReplicaConf* const conf = (DihSwitchReplicaConf*)&signal->theData[0];
+ switchReplicaReply(signal, conf->senderNode);
+}//Dbdih::execDIH_SWITCH_REPLICA_CONF()
+
+void Dbdih::execDIH_SWITCH_REPLICA_REF(Signal* signal)
+{
+ jamEntry();
+ DihSwitchReplicaRef* const ref = (DihSwitchReplicaRef*)&signal->theData[0];
+ if(c_stopPermMaster.returnValue == 0){
+ jam();
+ c_stopPermMaster.returnValue = ref->errorCode;
+ }//if
+ switchReplicaReply(signal, ref->senderNode);
+}//Dbdih::execDIH_SWITCH_REPLICA_REF()
+
+void Dbdih::switchReplicaReply(Signal* signal,
+ NodeId nodeId){
+ jam();
+ receiveLoopMacro(DIH_SWITCH_REPLICA_REQ, nodeId);
+ //------------------------------------------------------
+ // We have received all responses from the nodes. Thus
+ // we have completed switching replica roles. Continue
+ // with the next fragment.
+ //------------------------------------------------------
+ if(c_stopPermMaster.returnValue != 0){
+ jam();
+ c_switchReplicas.tableId = ctabFileSize + 1;
+ }//if
+ c_switchReplicas.fragNo++;
+
+ signal->theData[0] = DihContinueB::SwitchReplica;
+ signal->theData[1] = c_switchReplicas.nodeId;
+ signal->theData[2] = c_switchReplicas.tableId;
+ signal->theData[3] = c_switchReplicas.fragNo;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 4, JBB);
+}//Dbdih::switchReplicaReply()
+
+void
+Dbdih::switchReplica(Signal* signal,
+ Uint32 nodeId,
+ Uint32 tableId,
+ Uint32 fragNo){
+ jam();
+ DihSwitchReplicaReq* const req = (DihSwitchReplicaReq*)&signal->theData[0];
+
+ const Uint32 RT_BREAK = 64;
+
+ for (Uint32 i = 0; i < RT_BREAK; i++) {
+ jam();
+ if (tableId >= ctabFileSize) {
+ jam();
+ StopPermConf* const conf = (StopPermConf*)&signal->theData[0];
+ StopPermRef* const ref = (StopPermRef*)&signal->theData[0];
+ /**
+ * Finished with all tables
+ */
+ if(c_stopPermMaster.returnValue == 0) {
+ jam();
+ conf->senderData = c_stopPermMaster.clientData;
+ sendSignal(c_stopPermMaster.clientRef, GSN_STOP_PERM_CONF,
+ signal, 1, JBB);
+ } else {
+ jam();
+ ref->senderData = c_stopPermMaster.clientData;
+ ref->errorCode = c_stopPermMaster.returnValue;
+ sendSignal(c_stopPermMaster.clientRef, GSN_STOP_PERM_REF, signal, 2,JBB);
+ }//if
+
+ /**
+ * UnLock
+ */
+ c_nodeStartMaster.activeState = false;
+ c_stopPermMaster.clientRef = 0;
+ c_stopPermMaster.clientData = 0;
+ c_stopPermMaster.returnValue = 0;
+ Mutex mutex(signal, c_mutexMgr, c_switchPrimaryMutexHandle);
+ mutex.unlock(); // ignore result
+ return;
+ }//if
+
+ TabRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabFileSize, tabRecord);
+
+ if (tabPtr.p->tabStatus != TabRecord::TS_ACTIVE) {
+ jam();
+ tableId++;
+ fragNo = 0;
+ continue;
+ }//if
+ if (fragNo >= tabPtr.p->totalfragments) {
+ jam();
+ tableId++;
+ fragNo = 0;
+ continue;
+ }//if
+ FragmentstorePtr fragPtr;
+ getFragstore(tabPtr.p, fragNo, fragPtr);
+
+ Uint32 oldOrder[MAX_REPLICAS];
+ const Uint32 noOfReplicas = extractNodeInfo(fragPtr.p, oldOrder);
+
+ if(oldOrder[0] != nodeId) {
+ jam();
+ fragNo++;
+ continue;
+ }//if
+ req->tableId = tableId;
+ req->fragNo = fragNo;
+ req->noOfReplicas = noOfReplicas;
+ for (Uint32 i = 0; i < (noOfReplicas - 1); i++) {
+ req->newNodeOrder[i] = oldOrder[i+1];
+ }//for
+ req->newNodeOrder[noOfReplicas-1] = nodeId;
+ req->senderRef = reference();
+
+ /**
+ * Initialize struct
+ */
+ c_switchReplicas.tableId = tableId;
+ c_switchReplicas.fragNo = fragNo;
+ c_switchReplicas.nodeId = nodeId;
+
+ sendLoopMacro(DIH_SWITCH_REPLICA_REQ, sendDIH_SWITCH_REPLICA_REQ);
+ return;
+ }//for
+
+ signal->theData[0] = DihContinueB::SwitchReplica;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tableId;
+ signal->theData[3] = fragNo;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 4, JBB);
+}//Dbdih::switchReplica()
+
+void Dbdih::execSTOP_ME_REQ(Signal* signal)
+{
+ jamEntry();
+ StopMeReq* const req = (StopMeReq*)&signal->theData[0];
+ const BlockReference senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 nodeId = refToNode(senderRef);
+ {
+ /**
+ * Set node dead (remove from operations)
+ */
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->useInTransactions = false;
+ }
+ if (nodeId != getOwnNodeId()) {
+ jam();
+ StopMeConf * const stopMeConf = (StopMeConf *)&signal->theData[0];
+ stopMeConf->senderData = senderData;
+ stopMeConf->senderRef = reference();
+ sendSignal(senderRef, GSN_STOP_ME_CONF, signal,
+ StopMeConf::SignalLength, JBB);
+ return;
+ }//if
+
+ /**
+ * Local signal
+ */
+ jam();
+ ndbrequire(c_stopMe.clientRef == 0);
+
+ c_stopMe.clientData = senderData;
+ c_stopMe.clientRef = senderRef;
+
+ req->senderData = senderData;
+ req->senderRef = reference();
+
+ sendLoopMacro(STOP_ME_REQ, sendSTOP_ME_REQ);
+
+ /**
+ * Send conf to self
+ */
+ StopMeConf * const stopMeConf = (StopMeConf *)&signal->theData[0];
+ stopMeConf->senderData = senderData;
+ stopMeConf->senderRef = reference();
+ sendSignal(reference(), GSN_STOP_ME_CONF, signal,
+ StopMeConf::SignalLength, JBB);
+}//Dbdih::execSTOP_ME_REQ()
+
+void Dbdih::execSTOP_ME_REF(Signal* signal)
+{
+ ndbrequire(false);
+}
+
+void Dbdih::execSTOP_ME_CONF(Signal* signal)
+{
+ jamEntry();
+ StopMeConf * const stopMeConf = (StopMeConf *)&signal->theData[0];
+
+ const Uint32 senderRef = stopMeConf->senderRef;
+ const Uint32 senderData = stopMeConf->senderData;
+ const Uint32 nodeId = refToNode(senderRef);
+
+ ndbrequire(c_stopMe.clientRef != 0);
+ ndbrequire(c_stopMe.clientData == senderData);
+
+ receiveLoopMacro(STOP_ME_REQ, nodeId);
+ //---------------------------------------------------------
+ // All STOP_ME_REQ have been received. We will send the
+ // confirmation back to the requesting block.
+ //---------------------------------------------------------
+
+ stopMeConf->senderRef = reference();
+ stopMeConf->senderData = c_stopMe.clientData;
+ sendSignal(c_stopMe.clientRef, GSN_STOP_ME_CONF, signal,
+ StopMeConf::SignalLength, JBB);
+ c_stopMe.clientRef = 0;
+}//Dbdih::execSTOP_ME_CONF()
+
+void Dbdih::execWAIT_GCP_REQ(Signal* signal)
+{
+ jamEntry();
+ WaitGCPReq* const req = (WaitGCPReq*)&signal->theData[0];
+ WaitGCPRef* const ref = (WaitGCPRef*)&signal->theData[0];
+ WaitGCPConf* const conf = (WaitGCPConf*)&signal->theData[0];
+ const Uint32 senderData = req->senderData;
+ const BlockReference senderRef = req->senderRef;
+ const Uint32 requestType = req->requestType;
+
+ if(requestType == WaitGCPReq::CurrentGCI) {
+ jam();
+ conf->senderData = senderData;
+ conf->gcp = cnewgcp;
+ sendSignal(senderRef, GSN_WAIT_GCP_CONF, signal,
+ WaitGCPConf::SignalLength, JBB);
+ return;
+ }//if
+
+ if(isMaster()) {
+ /**
+ * Master
+ */
+ jam();
+
+ if((requestType == WaitGCPReq::CompleteIfRunning) &&
+ (cgcpStatus == GCP_READY)) {
+ jam();
+ conf->senderData = senderData;
+ conf->gcp = coldgcp;
+ sendSignal(senderRef, GSN_WAIT_GCP_CONF, signal,
+ WaitGCPConf::SignalLength, JBB);
+ return;
+ }//if
+
+ WaitGCPMasterPtr ptr;
+ if(c_waitGCPMasterList.seize(ptr) == false){
+ jam();
+ ref->senderData = senderData;
+ ref->errorCode = WaitGCPRef::NoWaitGCPRecords;
+ sendSignal(senderRef, GSN_WAIT_GCP_REF, signal,
+ WaitGCPRef::SignalLength, JBB);
+ return;
+ }//if
+ ptr.p->clientRef = senderRef;
+ ptr.p->clientData = senderData;
+
+ if((requestType == WaitGCPReq::CompleteForceStart) &&
+ (cgcpStatus == GCP_READY)) {
+ jam();
+ cstartGcpNow = true;
+ }//if
+ return;
+ } else {
+ /**
+ * Proxy part
+ */
+ jam();
+ WaitGCPProxyPtr ptr;
+ if (c_waitGCPProxyList.seize(ptr) == false) {
+ jam();
+ ref->senderData = senderData;
+ ref->errorCode = WaitGCPRef::NoWaitGCPRecords;
+ sendSignal(senderRef, GSN_WAIT_GCP_REF, signal,
+ WaitGCPRef::SignalLength, JBB);
+ return;
+ }//if
+ ptr.p->clientRef = senderRef;
+ ptr.p->clientData = senderData;
+ ptr.p->masterRef = cmasterdihref;
+
+ req->senderData = ptr.i;
+ req->senderRef = reference();
+ req->requestType = requestType;
+
+ sendSignal(cmasterdihref, GSN_WAIT_GCP_REQ, signal,
+ WaitGCPReq::SignalLength, JBB);
+ return;
+ }//if
+}//Dbdih::execWAIT_GCP_REQ()
+
+void Dbdih::execWAIT_GCP_REF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(!isMaster());
+ WaitGCPRef* const ref = (WaitGCPRef*)&signal->theData[0];
+
+ const Uint32 proxyPtr = ref->senderData;
+ const Uint32 errorCode = ref->errorCode;
+
+ WaitGCPProxyPtr ptr;
+ ptr.i = proxyPtr;
+ c_waitGCPProxyList.getPtr(ptr);
+
+ ref->senderData = ptr.p->clientData;
+ ref->errorCode = errorCode;
+ sendSignal(ptr.p->clientRef, GSN_WAIT_GCP_REF, signal,
+ WaitGCPRef::SignalLength, JBB);
+
+ c_waitGCPProxyList.release(ptr);
+}//Dbdih::execWAIT_GCP_REF()
+
+void Dbdih::execWAIT_GCP_CONF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(!isMaster());
+ WaitGCPConf* const conf = (WaitGCPConf*)&signal->theData[0];
+ const Uint32 proxyPtr = conf->senderData;
+ const Uint32 gcp = conf->gcp;
+ WaitGCPProxyPtr ptr;
+
+ ptr.i = proxyPtr;
+ c_waitGCPProxyList.getPtr(ptr);
+
+ conf->senderData = ptr.p->clientData;
+ conf->gcp = gcp;
+ sendSignal(ptr.p->clientRef, GSN_WAIT_GCP_CONF, signal,
+ WaitGCPConf::SignalLength, JBB);
+
+ c_waitGCPProxyList.release(ptr);
+}//Dbdih::execWAIT_GCP_CONF()
+
+void Dbdih::checkWaitGCPProxy(Signal* signal, NodeId failedNodeId)
+{
+ jam();
+ WaitGCPRef* const ref = (WaitGCPRef*)&signal->theData[0];
+ ref->errorCode = WaitGCPRef::NF_CausedAbortOfProcedure;
+
+ WaitGCPProxyPtr ptr;
+ c_waitGCPProxyList.first(ptr);
+ while(ptr.i != RNIL) {
+ jam();
+ const Uint32 i = ptr.i;
+ const Uint32 clientData = ptr.p->clientData;
+ const BlockReference clientRef = ptr.p->clientRef;
+ const BlockReference masterRef = ptr.p->masterRef;
+
+ c_waitGCPProxyList.next(ptr);
+ if(refToNode(masterRef) == failedNodeId) {
+ jam();
+ c_waitGCPProxyList.release(i);
+ ref->senderData = clientData;
+ sendSignal(clientRef, GSN_WAIT_GCP_REF, signal,
+ WaitGCPRef::SignalLength, JBB);
+ }//if
+ }//while
+}//Dbdih::checkWaitGCPProxy()
+
+void Dbdih::checkWaitGCPMaster(Signal* signal, NodeId failedNodeId)
+{
+ jam();
+ WaitGCPMasterPtr ptr;
+ c_waitGCPMasterList.first(ptr);
+
+ while (ptr.i != RNIL) {
+ jam();
+ const Uint32 i = ptr.i;
+ const NodeId nodeId = refToNode(ptr.p->clientRef);
+
+ c_waitGCPMasterList.next(ptr);
+ if (nodeId == failedNodeId) {
+ jam()
+ c_waitGCPMasterList.release(i);
+ }//if
+ }//while
+}//Dbdih::checkWaitGCPMaster()
+
+void Dbdih::emptyWaitGCPMasterQueue(Signal* signal)
+{
+ jam();
+ WaitGCPConf* const conf = (WaitGCPConf*)&signal->theData[0];
+ conf->gcp = coldgcp;
+
+ WaitGCPMasterPtr ptr;
+ c_waitGCPMasterList.first(ptr);
+ while(ptr.i != RNIL) {
+ jam();
+ const Uint32 i = ptr.i;
+ const Uint32 clientData = ptr.p->clientData;
+ const BlockReference clientRef = ptr.p->clientRef;
+
+ c_waitGCPMasterList.next(ptr);
+ conf->senderData = clientData;
+ sendSignal(clientRef, GSN_WAIT_GCP_CONF, signal,
+ WaitGCPConf::SignalLength, JBB);
+
+ c_waitGCPMasterList.release(i);
+ }//while
+}//Dbdih::emptyWaitGCPMasterQueue()
+
+void Dbdih::setNodeStatus(Uint32 nodeId, NodeRecord::NodeStatus newStatus)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->nodeStatus = newStatus;
+}//Dbdih::setNodeStatus()
+
+Dbdih::NodeRecord::NodeStatus Dbdih::getNodeStatus(Uint32 nodeId)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ return nodePtr.p->nodeStatus;
+}//Dbdih::getNodeStatus()
+
+Sysfile::ActiveStatus
+Dbdih::getNodeActiveStatus(Uint32 nodeId)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ return nodePtr.p->activeStatus;
+}//Dbdih::getNodeActiveStatus()
+
+
+void
+Dbdih::setNodeActiveStatus(Uint32 nodeId, Sysfile::ActiveStatus newStatus)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->activeStatus = newStatus;
+}//Dbdih::setNodeActiveStatus()
+
+void Dbdih::setAllowNodeStart(Uint32 nodeId, bool newState)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->allowNodeStart = newState;
+}//Dbdih::setAllowNodeStart()
+
+void Dbdih::setNodeCopyCompleted(Uint32 nodeId, bool newState)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ nodePtr.p->copyCompleted = newState;
+}//Dbdih::setNodeCopyCompleted()
+
+bool Dbdih::getAllowNodeStart(Uint32 nodeId)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ return nodePtr.p->allowNodeStart;
+}//Dbdih::getAllowNodeStart()
+
+bool Dbdih::getNodeCopyCompleted(Uint32 nodeId)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ return nodePtr.p->copyCompleted;
+}//Dbdih::getNodeCopyCompleted()
+
+bool Dbdih::checkNodeAlive(Uint32 nodeId)
+{
+ NodeRecordPtr nodePtr;
+ nodePtr.i = nodeId;
+ ndbrequire(nodeId > 0);
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRecord);
+ if (nodePtr.p->nodeStatus != NodeRecord::ALIVE) {
+ return false;
+ } else {
+ return true;
+ }//if
+}//Dbdih::checkNodeAlive()
+
+bool Dbdih::isMaster()
+{
+ return (reference() == cmasterdihref);
+}//Dbdih::isMaster()
+
+bool Dbdih::isActiveMaster()
+{
+ return ((reference() == cmasterdihref) && (cmasterState == MASTER_ACTIVE));
+}//Dbdih::isActiveMaster()
+
+Dbdih::NodeRecord::NodeRecord(){
+ m_nodefailSteps.clear();
+ gcpstate = NodeRecord::READY;
+
+ activeStatus = Sysfile::NS_NotDefined;
+ recNODE_FAILREP = ZFALSE;
+ nodeGroup = ZNIL;
+ dbtcFailCompleted = ZTRUE;
+ dbdictFailCompleted = ZTRUE;
+ dbdihFailCompleted = ZTRUE;
+ dblqhFailCompleted = ZTRUE;
+ noOfStartedChkpt = 0;
+ noOfQueuedChkpt = 0;
+ lcpStateAtTakeOver = (MasterLCPConf::State)255;
+
+ activeTabptr = RNIL;
+ nodeStatus = NodeRecord::NOT_IN_CLUSTER;
+ useInTransactions = false;
+ copyCompleted = false;
+ allowNodeStart = true;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbdih/LCP.txt b/storage/ndb/src/kernel/blocks/dbdih/LCP.txt
new file mode 100644
index 00000000000..500c82f6baf
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/LCP.txt
@@ -0,0 +1,35 @@
+
+Master DIH LQH
+========== ==========
+
+1) TCGETOPSIZEREQ -> all TC
+
+2) If sum(operation size) < Threshold
+ Goto 1
+
+3) For each table
+ Calc Keep GCI (local using CONTINUEB)
+
+4) COPY_GCIREQ -> all DIH
+
+5) TC_CLOPSIZEREQ -> all TC
+
+6) For each fragment
+ LCP_FRAG_ORD -> LQH
+
+ Do LCP...
+ 1) LCP_FRAG_REP -> all DIH
+ 2) If last fragment
+ LCP_COMPLETE_REP -> all DIH
+
+7) When receiving LCP_COMPLETE_REP from DIH
+ 1) If all DIHs have completed
+ Goto 1
+
+All DIHs
+==========
+1) When receiving LCP_FRAG_REP
+ If all fragments & replicas done in table
+ 1) Save Table descriptor
+ 2) If all tables done + LCP_COMPLETE_REP(from lqh) has arrived
+ LCP_COMPLETE_REP -> master DIH
diff --git a/storage/ndb/src/kernel/blocks/dbdih/Makefile.am b/storage/ndb/src/kernel/blocks/dbdih/Makefile.am
new file mode 100644
index 00000000000..d6ad380b806
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LIBRARIES = libdbdih.a
+
+libdbdih_a_SOURCES = DbdihInit.cpp DbdihMain.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbdih.dsp
+
+libdbdih.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbdih_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbdih/Sysfile.hpp b/storage/ndb/src/kernel/blocks/dbdih/Sysfile.hpp
new file mode 100644
index 00000000000..3e2f3b0dd48
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/Sysfile.hpp
@@ -0,0 +1,275 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SYSFILE_HPP
+#define SYSFILE_HPP
+
+#include <ndb_types.h>
+#include <ndb_limits.h>
+#include <NodeBitmask.hpp>
+
+/**
+ * No bits in Sysfile to represent nodeid
+ */
+#define NODEID_BITS 8
+
+/**
+ * Constant representing that node do not belong to
+ * any node group
+ */
+#define NO_NODE_GROUP_ID ((1 << NODEID_BITS) - 1)
+
+/**
+ * Dummy macro to make emacs indent better
+ */
+#define _F(x) x
+
+/**
+ * No of 32 bits word in sysfile
+ *
+ * 5 +
+ * MAX_NDB_NODES + // lastCompletedGCI
+ * NODE_ARRAY_SIZE(MAX_NDB_NODES, 4) + // nodeStatus
+ * NODE_ARRAY_SIZE(MAX_NDB_NODES, NODEID_BITS) + // nodeGroups
+ * NODE_ARRAY_SIZE(MAX_NDB_NODES, NODEID_BITS) + // takeOver
+ * NodeBitmask::NDB_NODE_BITMASK_SIZE // Lcp Active
+ */
+#define _SYSFILE_SIZE32 (5 + \
+ MAX_NDB_NODES + \
+ NODE_ARRAY_SIZE(MAX_NDB_NODES, 4) + \
+ NODE_ARRAY_SIZE(MAX_NDB_NODES, NODEID_BITS) + \
+ NODE_ARRAY_SIZE(MAX_NDB_NODES, NODEID_BITS) + \
+ _NDB_NODE_BITMASK_SIZE)
+
+/**
+ * This struct defines the format of P<X>.sysfile
+ */
+struct Sysfile {
+public:
+
+ /**
+ * No of 32 bits words in the sysfile
+ */
+ STATIC_CONST( SYSFILE_SIZE32 = _SYSFILE_SIZE32 );
+
+ Uint32 systemRestartBits;
+
+ static bool getInitialStartOngoing(const Uint32 & systemRestartBits);
+ static void setInitialStartOngoing(Uint32 & systemRestartBits);
+ static void clearInitialStartOngoing(Uint32 & systemRestartBits);
+
+ static bool getRestartOngoing(const Uint32 & systemRestartBits);
+ static void setRestartOngoing(Uint32 & systemRestartBits);
+ static void clearRestartOngoing(Uint32 & systemRestartBits);
+
+ static bool getLCPOngoing(const Uint32 & systemRestartBits);
+ static void setLCPOngoing(Uint32 & systemRestartBits);
+ static void clearLCPOngoing(Uint32 & systemRestartBits);
+
+ Uint32 keepGCI;
+ Uint32 oldestRestorableGCI;
+ Uint32 newestRestorableGCI;
+ Uint32 latestLCP_ID;
+
+ /**
+ * Last completed GCI for each node
+ */
+ Uint32 lastCompletedGCI[MAX_NDB_NODES];
+
+ /**
+ * Active status bits
+ *
+ * It takes 4 bits to represent it
+ */
+ enum ActiveStatus {
+ NS_Active = 0
+ ,NS_ActiveMissed_1 = 1
+ ,NS_ActiveMissed_2 = 2
+ ,NS_ActiveMissed_3 = 3
+ ,NS_HotSpare = 4
+ ,NS_NotActive_NotTakenOver = 5
+ ,NS_TakeOver = 6
+ ,NS_NotActive_TakenOver = 7
+ ,NS_NotDefined = 8
+ ,NS_Standby = 9
+ };
+ STATIC_CONST( NODE_STATUS_SIZE = NODE_ARRAY_SIZE(MAX_NDB_NODES, 4) );
+ Uint32 nodeStatus[NODE_STATUS_SIZE];
+
+ static Uint32 getNodeStatus(NodeId, const Uint32 nodeStatus[]);
+ static void setNodeStatus(NodeId, Uint32 nodeStatus[], Uint32 status);
+
+ /**
+ * The node group of each node
+ * Sizeof(NodeGroup) = 8 Bit
+ */
+ STATIC_CONST( NODE_GROUPS_SIZE = NODE_ARRAY_SIZE(MAX_NDB_NODES,
+ NODEID_BITS) );
+ Uint32 nodeGroups[NODE_GROUPS_SIZE];
+
+ static Uint16 getNodeGroup(NodeId, const Uint32 nodeGroups[]);
+ static void setNodeGroup(NodeId, Uint32 nodeGroups[], Uint16 group);
+
+ /**
+ * Any node can take over for any node
+ */
+ STATIC_CONST( TAKE_OVER_SIZE = NODE_ARRAY_SIZE(MAX_NDB_NODES,
+ NODEID_BITS) );
+ Uint32 takeOver[TAKE_OVER_SIZE];
+
+ static NodeId getTakeOverNode(NodeId, const Uint32 takeOver[]);
+ static void setTakeOverNode(NodeId, Uint32 takeOver[], NodeId toNode);
+
+ /**
+ * Is a node running a LCP
+ */
+ Uint32 lcpActive[NdbNodeBitmask::Size];
+};
+
+#if (MAX_NDB_NODES > (1<<NODEID_BITS))
+#error "Sysfile node id is too small"
+#endif
+
+/**
+ * Restart Info
+ *
+ * i = Initial start completed
+ * r = Crash during system restart
+ * l = Crash during local checkpoint
+
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * irl
+ */
+inline
+bool
+Sysfile::getInitialStartOngoing(const Uint32 & systemRestartBits){
+ return systemRestartBits & 1;
+}
+
+inline
+void
+Sysfile::setInitialStartOngoing(Uint32 & systemRestartBits){
+ systemRestartBits |= 1;
+}
+
+inline
+void
+Sysfile::clearInitialStartOngoing(Uint32 & systemRestartBits){
+ systemRestartBits &= ~1;
+}
+
+inline
+bool
+Sysfile::getRestartOngoing(const Uint32 & systemRestartBits){
+ return (systemRestartBits & 2) != 0;
+}
+
+inline
+void
+Sysfile::setRestartOngoing(Uint32 & systemRestartBits){
+ systemRestartBits |= 2;
+}
+
+inline
+void
+Sysfile::clearRestartOngoing(Uint32 & systemRestartBits){
+ systemRestartBits &= ~2;
+}
+
+inline
+bool
+Sysfile::getLCPOngoing(const Uint32 & systemRestartBits){
+ return systemRestartBits & 4;
+}
+
+inline
+void
+Sysfile::setLCPOngoing(Uint32 & systemRestartBits){
+ systemRestartBits |= 4;
+}
+
+inline
+void
+Sysfile::clearLCPOngoing(Uint32 & systemRestartBits){
+ systemRestartBits &= ~4;
+}
+
+inline
+Uint32
+Sysfile::getNodeStatus(NodeId nodeId, const Uint32 nodeStatus[]){
+ const int word = nodeId >> 3;
+ const int shift = (nodeId & 7) << 2;
+
+ return (nodeStatus[word] >> shift) & 15;
+}
+
+inline
+void
+Sysfile::setNodeStatus(NodeId nodeId, Uint32 nodeStatus[], Uint32 status){
+ const int word = nodeId >> 3;
+ const int shift = (nodeId & 7) << 2;
+
+ const Uint32 mask = ~(((Uint32)15) << shift);
+ const Uint32 tmp = nodeStatus[word];
+
+ nodeStatus[word] = (tmp & mask) | ((status & 15) << shift);
+}
+
+inline
+Uint16
+Sysfile::getNodeGroup(NodeId nodeId, const Uint32 nodeGroups[]){
+ const int word = nodeId >> 2;
+ const int shift = (nodeId & 3) << 3;
+
+ return (nodeGroups[word] >> shift) & 255;
+}
+
+inline
+void
+Sysfile::setNodeGroup(NodeId nodeId, Uint32 nodeGroups[], Uint16 group){
+ const int word = nodeId >> 2;
+ const int shift = (nodeId & 3) << 3;
+
+ const Uint32 mask = ~(((Uint32)255) << shift);
+ const Uint32 tmp = nodeGroups[word];
+
+ nodeGroups[word] = (tmp & mask) | ((group & 255) << shift);
+}
+
+inline
+NodeId
+Sysfile::getTakeOverNode(NodeId nodeId, const Uint32 takeOver[]){
+ const int word = nodeId >> 2;
+ const int shift = (nodeId & 3) << 3;
+
+ return (takeOver[word] >> shift) & 255;
+}
+
+inline
+void
+Sysfile::setTakeOverNode(NodeId nodeId, Uint32 takeOver[], NodeId toNode){
+ const int word = nodeId >> 2;
+ const int shift = (nodeId & 3) << 3;
+
+ const Uint32 mask = ~(((Uint32)255) << shift);
+ const Uint32 tmp = takeOver[word];
+
+ takeOver[word] = (tmp & mask) | ((toNode & 255) << shift);
+}
+
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbdih/printSysfile/Makefile b/storage/ndb/src/kernel/blocks/dbdih/printSysfile/Makefile
new file mode 100644
index 00000000000..4c4b1026aff
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/printSysfile/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := printSysfile
+BIN_TARGET_ARCHIVES := portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES := printSysfile.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/blocks/dbdih/printSysfile/printSysfile.cpp b/storage/ndb/src/kernel/blocks/dbdih/printSysfile/printSysfile.cpp
new file mode 100644
index 00000000000..efa4b9c92c5
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbdih/printSysfile/printSysfile.cpp
@@ -0,0 +1,158 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <Sysfile.hpp>
+
+void
+usage(const char * prg){
+ ndbout << "Usage " << prg
+ << " P[0-1].sysfile" << endl;
+}
+
+struct NSString {
+ Sysfile::ActiveStatus NodeStatus;
+ const char * desc;
+};
+
+static const
+NSString NodeStatusStrings[] = {
+ { Sysfile::NS_Active, "Active " },
+ { Sysfile::NS_ActiveMissed_1, "Active missed 1" },
+ { Sysfile::NS_ActiveMissed_2, "Active missed 2" },
+ { Sysfile::NS_ActiveMissed_3, "Active missed 3" },
+ { Sysfile::NS_HotSpare, "Hot spare " },
+ { Sysfile::NS_NotActive_NotTakenOver, "Not active " },
+ { Sysfile::NS_TakeOver, "Take over " },
+ { Sysfile::NS_NotActive_TakenOver, "Taken over " },
+ { Sysfile::NS_NotDefined, "Not defined " },
+ { Sysfile::NS_Standby, "Stand by " }
+};
+
+const
+char * getNSString(Uint32 ns){
+ for(Uint32 i = 0; i<(sizeof(NodeStatusStrings)/sizeof(NSString)); i++)
+ if((Uint32)NodeStatusStrings[i].NodeStatus == ns)
+ return NodeStatusStrings[i].desc;
+ return "<Unknown state>";
+}
+
+void
+fill(const char * buf, int mod){
+ int len = strlen(buf)+1;
+ ndbout << buf << " ";
+ while((len % mod) != 0){
+ ndbout << " ";
+ len++;
+ }
+}
+
+void
+print(const char * filename, const Sysfile * sysfile){
+ char buf[255];
+ ndbout << "----- Sysfile: " << filename << " -----" << endl;
+ ndbout << "Initial start ongoing: "
+ << Sysfile::getInitialStartOngoing(sysfile->systemRestartBits)
+ << ", ";
+
+ ndbout << "Restart Ongoing: "
+ << Sysfile::getRestartOngoing(sysfile->systemRestartBits)
+ << ", ";
+
+ ndbout << "LCP Ongoing: "
+ << Sysfile::getLCPOngoing(sysfile->systemRestartBits)
+ << endl;
+
+
+ ndbout << "-- Global Checkpoint Identities: --" << endl;
+ sprintf(buf, "keepGCI = %u", sysfile->keepGCI);
+ fill(buf, 40);
+ ndbout << " -- Tail of REDO log" << endl;
+
+ sprintf(buf, "oldestRestorableGCI = %u", sysfile->oldestRestorableGCI);
+ fill(buf, 40);
+ ndbout << " -- " << endl;
+
+ sprintf(buf, "newestRestorableGCI = %u", sysfile->newestRestorableGCI);
+ fill(buf, 40);
+ ndbout << " -- " << endl;
+
+ sprintf(buf, "latestLCP = %u", sysfile->latestLCP_ID);
+ fill(buf, 40);
+ ndbout << " -- " << endl;
+
+ ndbout << "-- Node status: --" << endl;
+ for(int i = 1; i < MAX_NDB_NODES; i++){
+ if(Sysfile::getNodeStatus(i, sysfile->nodeStatus) !=Sysfile::NS_NotDefined){
+ sprintf(buf,
+ "Node %.2d -- %s GCP: %d, NodeGroup: %d, TakeOverNode: %d, "
+ "LCP Ongoing: %s",
+ i,
+ getNSString(Sysfile::getNodeStatus(i,sysfile->nodeStatus)),
+ sysfile->lastCompletedGCI[i],
+ Sysfile::getNodeGroup(i, sysfile->nodeGroups),
+ Sysfile::getTakeOverNode(i, sysfile->takeOver),
+ BitmaskImpl::get(NdbNodeBitmask::Size,
+ sysfile->lcpActive, i) != 0 ? "yes" : "no");
+ ndbout << buf << endl;
+ }
+ }
+}
+
+NDB_COMMAND(printSysfile,
+ "printSysfile", "printSysfile", "Prints a sysfile", 16384){
+ if(argc < 2){
+ usage(argv[0]);
+ return 0;
+ }
+
+ for(int i = 1; i<argc; i++){
+ const char * filename = argv[i];
+
+ struct stat sbuf;
+ const int res = stat(filename, &sbuf);
+ if(res != 0){
+ ndbout << "Could not find file: \"" << filename << "\"" << endl;
+ continue;
+ }
+ const Uint32 bytes = sbuf.st_size;
+
+ Uint32 * buf = new Uint32[bytes/4+1];
+
+ FILE * f = fopen(filename, "rb");
+ if(f == 0){
+ ndbout << "Failed to open file" << endl;
+ delete [] buf;
+ continue;
+ }
+ Uint32 sz = fread(buf, 1, bytes, f);
+ fclose(f);
+ if(sz != bytes){
+ ndbout << "Failure while reading file" << endl;
+ delete [] buf;
+ continue;
+ }
+
+ print(filename, (Sysfile *)&buf[0]);
+ delete [] buf;
+ continue;
+ }
+ return 0;
+}
diff --git a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
new file mode 100644
index 00000000000..e7debe1f978
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
@@ -0,0 +1,2953 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBLQH_H
+#define DBLQH_H
+
+#include <pc.hpp>
+#include <ndb_limits.h>
+#include <SimulatedBlock.hpp>
+#include <DLList.hpp>
+#include <DLFifoList.hpp>
+#include <DLHashTable.hpp>
+
+#include <NodeBitmask.hpp>
+#include <signaldata/LCP.hpp>
+#include <signaldata/LqhTransConf.hpp>
+#include <signaldata/LqhFrag.hpp>
+
+// primary key is stored in TUP
+#include <../dbtup/Dbtup.hpp>
+
+#ifdef DBLQH_C
+// Constants
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS USED WHEN MASTER REQUESTS STATE OF COPY FRAGMENTS. */
+/* ------------------------------------------------------------------------- */
+#define ZCOPY_CLOSING 0
+#define ZCOPY_ONGOING 1
+#define ZCOPY_ACTIVATION 2
+/* ------------------------------------------------------------------------- */
+/* STATES FOR THE VARIABLE GCP_LOG_PART_STATE */
+/* ------------------------------------------------------------------------- */
+#define ZIDLE 0
+#define ZWAIT_DISK 1
+#define ZON_DISK 2
+#define ZACTIVE 1
+/* ------------------------------------------------------------------------- */
+/* STATES FOR THE VARIABLE CSR_PHASES_STARTED */
+/* ------------------------------------------------------------------------- */
+#define ZSR_NO_PHASE_STARTED 0
+#define ZSR_PHASE1_COMPLETED 1
+#define ZSR_PHASE2_COMPLETED 2
+#define ZSR_BOTH_PHASES_STARTED 3
+/* ------------------------------------------------------------------------- */
+/* THE NUMBER OF PAGES IN A MBYTE, THE TWO LOGARITHM OF THIS. */
+/* THE NUMBER OF MBYTES IN A LOG FILE. */
+/* THE MAX NUMBER OF PAGES READ/WRITTEN FROM/TO DISK DURING */
+/* A WRITE OR READ. */
+/* ------------------------------------------------------------------------- */
+#define ZNOT_DIRTY 0
+#define ZDIRTY 1
+#define ZREAD_AHEAD_SIZE 8
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS OF THE LOG PAGES */
+/* ------------------------------------------------------------------------- */
+#define ZPAGE_HEADER_SIZE 32
+#define ZNO_MBYTES_IN_FILE 16
+#define ZPAGE_SIZE 8192
+#define ZPAGES_IN_MBYTE 32
+#define ZTWOLOG_NO_PAGES_IN_MBYTE 5
+#define ZTWOLOG_PAGE_SIZE 13
+#define ZMAX_MM_BUFFER_SIZE 32 // Main memory window during log execution
+
+#define ZMAX_PAGES_WRITTEN 8 // Max pages before writing to disk (=> config)
+#define ZMIN_READ_BUFFER_SIZE 2 // Minimum number of pages to execute log
+#define ZMIN_LOG_PAGES_OPERATION 10 // Minimum no of pages before stopping
+
+#define ZPOS_CHECKSUM 0
+#define ZPOS_LOG_LAP 1
+#define ZPOS_MAX_GCI_COMPLETED 2
+#define ZPOS_MAX_GCI_STARTED 3
+#define ZNEXT_PAGE 4
+#define ZPREV_PAGE 5
+#define ZPOS_VERSION 6
+#define ZPOS_NO_LOG_FILES 7
+#define ZCURR_PAGE_INDEX 8
+#define ZLAST_LOG_PREP_REF 10
+#define ZPOS_DIRTY 11
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS FOR THE VARIOUS REPLICA AND NODE TYPES. */
+/* ------------------------------------------------------------------------- */
+#define ZPRIMARY_NODE 0
+#define ZBACKUP_NODE 1
+#define ZSTANDBY_NODE 2
+#define ZTC_NODE 3
+#define ZLOG_NODE 3
+/* ------------------------------------------------------------------------- */
+/* VARIOUS CONSTANTS USED AS FLAGS TO THE FILE MANAGER. */
+/* ------------------------------------------------------------------------- */
+#define ZOPEN_READ 0
+#define ZOPEN_WRITE 1
+#define ZOPEN_READ_WRITE 2
+#define ZVAR_NO_LOG_PAGE_WORD 1
+#define ZLIST_OF_PAIRS 0
+#define ZLIST_OF_PAIRS_SYNCH 16
+#define ZARRAY_OF_PAGES 1
+#define ZLIST_OF_MEM_PAGES 2
+#define ZLIST_OF_MEM_PAGES_SYNCH 18
+#define ZCLOSE_NO_DELETE 0
+#define ZCLOSE_DELETE 1
+#define ZPAGE_ZERO 0
+/* ------------------------------------------------------------------------- */
+/* THE FOLLOWING CONSTANTS ARE USED TO DESCRIBE THE TYPES OF */
+/* LOG RECORDS, THE SIZE OF THE VARIOUS LOG RECORD TYPES AND */
+/* THE POSITIONS WITHIN THOSE LOG RECORDS. */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* THESE CONSTANTS DESCRIBE THE SIZES OF VARIOUS TYPES OF LOG REORDS. */
+/* NEXT_LOG_SIZE IS ACTUALLY ONE. THE REASON WE SET IT TO 2 IS TO */
+/* SIMPLIFY THE CODE SINCE OTHERWISE HAVE TO USE A SPECIAL VERSION */
+/* OF READ_LOGWORD WHEN READING LOG RECORD TYPE */
+/* SINCE NEXT MBYTE TYPE COULD BE THE VERY LAST WORD IN THE MBYTE. */
+/* BY SETTING IT TO 2 WE ENSURE IT IS NEVER THE VERY LAST WORD */
+/* IN THE MBYTE. */
+/* ------------------------------------------------------------------------- */
+#define ZFD_HEADER_SIZE 3
+#define ZFD_PART_SIZE 48
+#define ZLOG_HEAD_SIZE 6
+#define ZNEXT_LOG_SIZE 2
+#define ZABORT_LOG_SIZE 3
+#define ZCOMMIT_LOG_SIZE 9
+#define ZCOMPLETED_GCI_LOG_SIZE 2
+/* ------------------------------------------------------------------------- */
+/* THESE CONSTANTS DESCRIBE THE TYPE OF A LOG RECORD. */
+/* THIS IS THE FIRST WORD OF A LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZNEW_PREP_OP_TYPE 0
+#define ZPREP_OP_TYPE 1
+#define ZCOMMIT_TYPE 2
+#define ZABORT_TYPE 3
+#define ZFD_TYPE 4
+#define ZFRAG_SPLIT_TYPE 5
+#define ZNEXT_LOG_RECORD_TYPE 6
+#define ZNEXT_MBYTE_TYPE 7
+#define ZCOMPLETED_GCI_TYPE 8
+#define ZINVALID_COMMIT_TYPE 9
+/* ------------------------------------------------------------------------- */
+/* THE POSITIONS OF LOGGED DATA IN A FILE DESCRIPTOR LOG RECORD HEADER.*/
+/* ALSO THE MAXIMUM NUMBER OF FILE DESCRIPTORS IN A LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_LOG_TYPE 0
+#define ZPOS_NO_FD 1
+#define ZPOS_FILE_NO 2
+#define ZMAX_LOG_FILES_IN_PAGE_ZERO 40
+/* ------------------------------------------------------------------------- */
+/* THE POSITIONS WITHIN A PREPARE LOG RECORD AND A NEW PREPARE */
+/* LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_HASH_VALUE 2
+#define ZPOS_SCHEMA_VERSION 3
+#define ZPOS_TRANS_TICKET 4
+#define ZPOS_OP_TYPE 5
+#define ZPOS_NO_ATTRINFO 6
+#define ZPOS_NO_KEYINFO 7
+/* ------------------------------------------------------------------------- */
+/* THE POSITIONS WITHIN A COMMIT LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_COMMIT_TRANSID1 1
+#define ZPOS_COMMIT_TRANSID2 2
+#define ZPOS_COMMIT_GCI 3
+#define ZPOS_COMMIT_TABLE_REF 4
+#define ZPOS_COMMIT_FRAGID 5
+#define ZPOS_COMMIT_FILE_NO 6
+#define ZPOS_COMMIT_START_PAGE_NO 7
+#define ZPOS_COMMIT_START_PAGE_INDEX 8
+#define ZPOS_COMMIT_STOP_PAGE_NO 9
+/* ------------------------------------------------------------------------- */
+/* THE POSITIONS WITHIN A ABORT LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_ABORT_TRANSID1 1
+#define ZPOS_ABORT_TRANSID2 2
+/* ------------------------------------------------------------------------- */
+/* THE POSITION WITHIN A COMPLETED GCI LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_COMPLETED_GCI 1
+/* ------------------------------------------------------------------------- */
+/* THE POSITIONS WITHIN A NEW PREPARE LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+#define ZPOS_NEW_PREP_FILE_NO 8
+#define ZPOS_NEW_PREP_PAGE_REF 9
+
+#define ZLAST_WRITE_IN_FILE 1
+#define ZENFORCE_WRITE 2
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS USED AS INPUT TO SUBROUTINE WRITE_LOG_PAGES AMONG OTHERS. */
+/* ------------------------------------------------------------------------- */
+#define ZNORMAL 0
+#define ZINIT 1
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS USED BY CONTINUEB TO DEDUCE WHICH CONTINUE SIGNAL IS TO */
+/* BE EXECUTED AS A RESULT OF THIS CONTINUEB SIGNAL. */
+/* ------------------------------------------------------------------------- */
+#define ZLOG_LQHKEYREQ 0
+#define ZPACK_LQHKEYREQ 1
+#define ZSEND_ATTRINFO 2
+#define ZSR_GCI_LIMITS 3
+#define ZSR_LOG_LIMITS 4
+#define ZSEND_EXEC_CONF 5
+#define ZEXEC_SR 6
+#define ZSR_FOURTH_COMP 7
+#define ZINIT_FOURTH 8
+#define ZTIME_SUPERVISION 9
+#define ZSR_PHASE3_START 10
+#define ZLQH_TRANS_NEXT 11
+#define ZLQH_RELEASE_AT_NODE_FAILURE 12
+#define ZSCAN_TC_CONNECT 13
+#define ZINITIALISE_RECORDS 14
+#define ZINIT_GCP_REC 15
+#define ZRESTART_OPERATIONS_AFTER_STOP 16
+#define ZCHECK_LCP_STOP_BLOCKED 17
+#define ZSCAN_MARKERS 18
+#define ZOPERATION_EVENT_REP 19
+#define ZPREP_DROP_TABLE 20
+
+/* ------------------------------------------------------------------------- */
+/* NODE STATE DURING SYSTEM RESTART, VARIABLES CNODES_SR_STATE */
+/* AND CNODES_EXEC_SR_STATE. */
+/* ------------------------------------------------------------------------- */
+#define ZSTART_SR 1
+#define ZEXEC_SR_COMPLETED 2
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS USED BY NODE STATUS TO DEDUCE THE STATUS OF A NODE. */
+/* ------------------------------------------------------------------------- */
+#define ZNODE_UP 0
+#define ZNODE_DOWN 1
+/* ------------------------------------------------------------------------- */
+/* START PHASES */
+/* ------------------------------------------------------------------------- */
+#define ZLAST_START_PHASE 255
+#define ZSTART_PHASE1 1
+#define ZSTART_PHASE2 2
+#define ZSTART_PHASE3 3
+#define ZSTART_PHASE4 4
+#define ZSTART_PHASE6 6
+/* ------------------------------------------------------------------------- */
+/* CONSTANTS USED BY SCAN AND COPY FRAGMENT PROCEDURES */
+/* ------------------------------------------------------------------------- */
+#define ZSTORED_PROC_SCAN 0
+#define ZSTORED_PROC_COPY 2
+#define ZDELETE_STORED_PROC_ID 3
+//#define ZSCAN_NEXT 1
+//#define ZSCAN_NEXT_COMMIT 2
+//#define ZSCAN_NEXT_ABORT 12
+#define ZCOPY_COMMIT 3
+#define ZCOPY_REPEAT 4
+#define ZCOPY_ABORT 5
+#define ZCOPY_CLOSE 6
+//#define ZSCAN_CLOSE 6
+//#define ZEMPTY_FRAGMENT 0
+#define ZWRITE_LOCK 1
+#define ZSCAN_FRAG_CLOSED 2
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES ADDED IN VERSION 0.1 AND 0.2 */
+/* ------------------------------------------------------------------------- */
+#define ZNOT_FOUND 1 // Not an error code, a return value
+#define ZNO_FREE_LQH_CONNECTION 414
+#define ZGET_DATAREC_ERROR 418
+#define ZGET_ATTRINBUF_ERROR 419
+#define ZNO_FREE_FRAGMENTREC 460 // Insert new fragment error code
+#define ZTAB_FILE_SIZE 464 // Insert new fragment error code + Start kernel
+#define ZNO_ADD_FRAGREC 465 // Insert new fragment error code
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES ADDED IN VERSION 0.3 */
+/* ------------------------------------------------------------------------- */
+#define ZTAIL_PROBLEM_IN_LOG_ERROR 410
+#define ZGCI_TOO_LOW_ERROR 429 // GCP_SAVEREF error code
+#define ZTAB_STATE_ERROR 474 // Insert new fragment error code
+#define ZTOO_NEW_GCI_ERROR 479 // LCP Start error
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES ADDED IN VERSION 0.4 */
+/* ------------------------------------------------------------------------- */
+
+#define ZNO_FREE_FRAG_SCAN_REC_ERROR 490 // SCAN_FRAGREF error code
+#define ZCOPY_NO_FRAGMENT_ERROR 491 // COPY_FRAGREF error code
+#define ZTAKE_OVER_ERROR 499
+#define ZCOPY_NODE_ERROR 1204
+#define ZTOO_MANY_COPY_ACTIVE_ERROR 1208 // COPY_FRAG and COPY_ACTIVEREF code
+#define ZCOPY_ACTIVE_ERROR 1210 // COPY_ACTIVEREF error code
+#define ZNO_TC_CONNECT_ERROR 1217 // Simple Read + SCAN
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES ADDED IN VERSION 1.X */
+/* ------------------------------------------------------------------------- */
+//#define ZSCAN_BOOK_ACC_OP_ERROR 1219 // SCAN_FRAGREF error code
+#define ZFILE_CHANGE_PROBLEM_IN_LOG_ERROR 1220
+#define ZTEMPORARY_REDO_LOG_FAILURE 1221
+#define ZNO_FREE_MARKER_RECORDS_ERROR 1222
+#define ZNODE_SHUTDOWN_IN_PROGESS 1223
+#define ZTOO_MANY_FRAGMENTS 1224
+#define ZTABLE_NOT_DEFINED 1225
+#define ZDROP_TABLE_IN_PROGRESS 1226
+#define ZINVALID_SCHEMA_VERSION 1227
+
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES ADDED IN VERSION 2.X */
+/* ------------------------------------------------------------------------- */
+#define ZNODE_FAILURE_ERROR 400
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES FROM ACC */
+/* ------------------------------------------------------------------------- */
+#define ZNO_TUPLE_FOUND 626
+#define ZTUPLE_ALREADY_EXIST 630
+/* ------------------------------------------------------------------------- */
+/* ERROR CODES FROM TUP */
+/* ------------------------------------------------------------------------- */
+#define ZSEARCH_CONDITION_FALSE 899
+#define ZUSER_ERROR_CODE_LIMIT 6000
+#endif
+
+/**
+ * @class dblqh
+ *
+ * @section secIntro Introduction
+ *
+ * Dblqh is the coordinator of the LDM. Dblqh is responsible for
+ * performing operations on tuples. It does this job with help of
+ * Dbacc block (that manages the index structures) and Dbtup
+ * (that manages the tuples).
+ *
+ * Dblqh also keeps track of the participants and acts as a coordinator of
+ * 2-phase commits. Logical redo logging is also handled by the Dblqh
+ * block.
+ *
+ * @section secModules Modules
+ *
+ * The code is partitioned into the following modules:
+ * - START / RESTART
+ * - Start phase 1: Load our block reference and our processor id
+ * - Start phase 2: Initiate all records within the block
+ * Connect LQH with ACC and TUP.
+ * - Start phase 4: Connect LQH with LQH. Connect every LQH with
+ * every LQH in the database system.
+ * If initial start, then create the fragment log files.
+ * If system restart or node restart,
+ * then open the fragment log files and
+ * find the end of the log files.
+ * - ADD / DELETE FRAGMENT<br>
+ * Used by dictionary to create new fragments and delete old fragments.
+ * - EXECUTION<br>
+ * handles the reception of lqhkeyreq and all processing
+ * of operations on behalf of this request.
+ * This does also involve reception of various types of attrinfo
+ * and keyinfo.
+ * It also involves communication with ACC and TUP.
+ * - LOG<br>
+ * The log module handles the reading and writing of the log.
+ * It is also responsible for handling system restart.
+ * It controls the system restart in TUP and ACC as well.
+ * - TRANSACTION<br>
+ * This module handles the commit and the complete phases.
+ * - MODULE TO HANDLE TC FAILURE<br>
+ * - SCAN<br>
+ * This module contains the code that handles a scan of a particular
+ * fragment.
+ * It operates under the control of TC and orders ACC to
+ * perform a scan of all tuples in the fragment.
+ * TUP performs the necessary search conditions
+ * to ensure that only valid tuples are returned to the application.
+ * - NODE RECOVERY<br>
+ * Used when a node has failed.
+ * It performs a copy of a fragment to a new replica of the fragment.
+ * It does also shut down all connections to the failed node.
+ * - LOCAL CHECKPOINT<br>
+ * Handles execution and control of LCPs
+ * It controls the LCPs in TUP and ACC.
+ * It also interacts with DIH to control which GCPs are recoverable.
+ * - GLOBAL CHECKPOINT<br>
+ * Helps DIH in discovering when GCPs are recoverable.
+ * It handles the request gcp_savereq that requests LQH to
+ * save a particular GCP to disk and respond when completed.
+ * - FILE HANDLING<br>
+ * With submodules:
+ * - SIGNAL RECEPTION
+ * - NORMAL OPERATION
+ * - FILE CHANGE
+ * - INITIAL START
+ * - SYSTEM RESTART PHASE ONE
+ * - SYSTEM RESTART PHASE TWO,
+ * - SYSTEM RESTART PHASE THREE
+ * - SYSTEM RESTART PHASE FOUR
+ * - ERROR
+ * - TEST
+ * - LOG
+ */
+class Dblqh: public SimulatedBlock {
+public:
+ enum LcpCloseState {
+ LCP_IDLE = 0,
+ LCP_RUNNING = 1, // LCP is running
+ LCP_CLOSE_STARTED = 2, // Completion(closing of files) has started
+ ACC_LCP_CLOSE_COMPLETED = 3,
+ TUP_LCP_CLOSE_COMPLETED = 4
+ };
+
+ enum ExecUndoLogState {
+ EULS_IDLE = 0,
+ EULS_STARTED = 1,
+ EULS_COMPLETED = 2,
+ EULS_ACC_COMPLETED = 3,
+ EULS_TUP_COMPLETED = 4
+ };
+
+ struct AddFragRecord {
+ enum AddFragStatus {
+ FREE = 0,
+ ACC_ADDFRAG = 1,
+ WAIT_TWO_TUP = 2,
+ WAIT_ONE_TUP = 3,
+ WAIT_TWO_TUX = 4,
+ WAIT_ONE_TUX = 5,
+ WAIT_ADD_ATTR = 6,
+ TUP_ATTR_WAIT1 = 7,
+ TUP_ATTR_WAIT2 = 8,
+ TUX_ATTR_WAIT1 = 9,
+ TUX_ATTR_WAIT2 = 10
+ };
+ LqhAddAttrReq::Entry attributes[LqhAddAttrReq::MAX_ATTRIBUTES];
+ UintR accConnectptr;
+ AddFragStatus addfragStatus;
+ UintR dictConnectptr;
+ UintR fragmentPtr;
+ UintR nextAddfragrec;
+ UintR noOfAllocPages;
+ UintR schemaVer;
+ UintR tup1Connectptr;
+ UintR tup2Connectptr;
+ UintR tux1Connectptr;
+ UintR tux2Connectptr;
+ UintR checksumIndicator;
+ UintR GCPIndicator;
+ BlockReference dictBlockref;
+ Uint32 m_senderAttrPtr;
+ Uint16 addfragErrorCode;
+ Uint16 attrSentToTup;
+ Uint16 attrReceived;
+ Uint16 addFragid;
+ Uint16 fragid1;
+ Uint16 fragid2;
+ Uint16 noOfAttr;
+ Uint16 noOfNull;
+ Uint16 tabId;
+ Uint16 totalAttrReceived;
+ Uint16 fragCopyCreation;
+ Uint16 noOfKeyAttr;
+ Uint32 noOfNewAttr; // noOfCharsets in upper half
+ Uint16 noOfAttributeGroups;
+ Uint16 lh3DistrBits;
+ Uint16 tableType;
+ Uint16 primaryTableId;
+ };// Size 108 bytes
+ typedef Ptr<AddFragRecord> AddFragRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ ATTRIBUTE INFORMATION RECORD $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * Can contain one (1) attrinfo signal.
+ * One signal contains 24 attr. info words.
+ * But 32 elements are used to make plex happy.
+ * Some of the elements are used to the following things:
+ * - Data length in this record is stored in the
+ * element indexed by ZINBUF_DATA_LEN.
+ * - Next attrinbuf is pointed out by the element
+ * indexed by ZINBUF_NEXT.
+ */
+ struct Attrbuf {
+ UintR attrbuf[32];
+ }; // Size 128 bytes
+ typedef Ptr<Attrbuf> AttrbufPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ DATA BUFFER $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This buffer is used as a general data storage.
+ */
+ struct Databuf {
+ UintR data[4];
+ UintR nextDatabuf;
+ }; // size 20 bytes
+ typedef Ptr<Databuf> DatabufPtr;
+
+ struct ScanRecord {
+ enum ScanState {
+ SCAN_FREE = 0,
+ WAIT_STORED_PROC_COPY = 1,
+ WAIT_STORED_PROC_SCAN = 2,
+ WAIT_NEXT_SCAN_COPY = 3,
+ WAIT_NEXT_SCAN = 4,
+ WAIT_DELETE_STORED_PROC_ID_SCAN = 5,
+ WAIT_DELETE_STORED_PROC_ID_COPY = 6,
+ WAIT_ACC_COPY = 7,
+ WAIT_ACC_SCAN = 8,
+ WAIT_SCAN_NEXTREQ = 10,
+ WAIT_CLOSE_SCAN = 12,
+ WAIT_CLOSE_COPY = 13,
+ WAIT_RELEASE_LOCK = 14,
+ WAIT_TUPKEY_COPY = 15,
+ WAIT_LQHKEY_COPY = 16,
+ IN_QUEUE = 17
+ };
+ enum ScanType {
+ ST_IDLE = 0,
+ SCAN = 1,
+ COPY = 2
+ };
+
+ UintR scan_acc_op_ptr[32];
+ Uint32 scan_acc_index;
+ Uint32 scan_acc_attr_recs;
+ UintR scanApiOpPtr;
+ UintR scanLocalref[2];
+
+ Uint32 m_max_batch_size_rows;
+ Uint32 m_max_batch_size_bytes;
+
+ Uint32 m_curr_batch_size_rows;
+ Uint32 m_curr_batch_size_bytes;
+
+ bool check_scan_batch_completed() const;
+
+ UintR copyPtr;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ Uint32 nextHash;
+ Uint32 prevHash;
+ bool equal(const ScanRecord & key) const {
+ return scanNumber == key.scanNumber && fragPtrI == key.fragPtrI;
+ }
+ Uint32 hashValue() const {
+ return fragPtrI ^ scanNumber;
+ }
+
+ UintR scanAccPtr;
+ UintR scanAiLength;
+ UintR scanErrorCounter;
+ UintR scanLocalFragid;
+ UintR scanSchemaVersion;
+
+ /**
+ * This is _always_ main table, even in range scan
+ * in which case scanTcrec->fragmentptr is different
+ */
+ Uint32 fragPtrI;
+ UintR scanStoredProcId;
+ ScanState scanState;
+ UintR scanTcrec;
+ ScanType scanType;
+ BlockReference scanApiBlockref;
+ NodeId scanNodeId;
+ Uint16 scanReleaseCounter;
+ Uint16 scanNumber;
+
+ Uint8 scanCompletedStatus;
+ Uint8 scanFlag;
+ Uint8 scanLockHold;
+ Uint8 scanLockMode;
+ Uint8 readCommitted;
+ Uint8 rangeScan;
+ Uint8 descending;
+ Uint8 scanTcWaiting;
+ Uint8 scanKeyinfoFlag;
+ Uint8 m_last_row;
+ }; // Size 272 bytes
+ typedef Ptr<ScanRecord> ScanRecordPtr;
+
+ struct Fragrecord {
+ enum ExecSrStatus {
+ IDLE = 0,
+ ACTIVE_REMOVE_AFTER = 1,
+ ACTIVE = 2
+ };
+ /**
+ * Possible state transitions are:
+ * - FREE -> DEFINED Fragment record is allocated
+ * - DEFINED -> ACTIVE Add fragment is completed and
+ * fragment is ready to
+ * receive operations.
+ * - DEFINED -> ACTIVE_CREATION Add fragment is completed and
+ * fragment is ready to
+ * receive operations in parallel
+ * with a copy fragment
+ * which is performed from the
+ * primary replica
+ * - DEFINED -> CRASH_RECOVERING A fragment is ready to be
+ * recovered from a local
+ * checkpoint on disk
+ * - ACTIVE -> BLOCKED A local checkpoint is to be
+ * started. No more operations
+ * are allowed to be started until
+ * the local checkpoint
+ * has been started.
+ * - ACTIVE -> REMOVING A fragment is removed from the node
+ * - BLOCKED -> ACTIVE Operations are allowed again in
+ * the fragment.
+ * - CRASH_RECOVERING -> ACTIVE A fragment has been recovered and
+ * are now ready for
+ * operations again.
+ * - CRASH_RECOVERING -> REMOVING Fragment recovery failed or
+ * was cancelled.
+ * - ACTIVE_CREATION -> ACTIVE A fragment is now copied and now
+ * is a normal fragment
+ * - ACTIVE_CREATION -> REMOVING Copying of the fragment failed
+ * - REMOVING -> FREE Removing of the fragment is
+ * completed and the fragment
+ * is now free again.
+ */
+ enum FragStatus {
+ FREE = 0, ///< Fragment record is currently not in use
+ FSACTIVE = 1, ///< Fragment is defined and usable for operations
+ DEFINED = 2, ///< Fragment is defined but not yet usable by
+ ///< operations
+ BLOCKED = 3, ///< LQH is waiting for all active operations to
+ ///< complete the current phase so that the
+ ///< local checkpoint can be started.
+ ACTIVE_CREATION = 4, ///< Fragment is defined and active but is under
+ ///< creation by the primary LQH.
+ CRASH_RECOVERING = 5, ///< Fragment is recovering after a crash by
+ ///< executing the fragment log and so forth.
+ ///< Will need further breakdown.
+ REMOVING = 6 ///< The fragment is currently removed.
+ ///< Operations are not allowed.
+ };
+ enum LogFlag {
+ STATE_TRUE = 0,
+ STATE_FALSE = 1
+ };
+ enum SrStatus {
+ SS_IDLE = 0,
+ SS_STARTED = 1,
+ SS_COMPLETED = 2
+ };
+ enum LcpFlag {
+ LCP_STATE_TRUE = 0,
+ LCP_STATE_FALSE = 1
+ };
+ /**
+ * Last GCI for executing the fragment log in this phase.
+ */
+ UintR execSrLastGci[4];
+ /**
+ * Start GCI for executing the fragment log in this phase.
+ */
+ UintR execSrStartGci[4];
+ /**
+ * Requesting user pointer for executing the fragment log in
+ * this phase
+ */
+ UintR execSrUserptr[4];
+ /**
+ * The LCP identifier of the LCP's.
+ * =0 means that the LCP number has not been stored.
+ * The LCP identifier is supplied by DIH when starting the LCP.
+ */
+ UintR lcpId[MAX_LCP_STORED];
+ UintR maxGciInLcp;
+ /**
+ * This variable contains the maximum global checkpoint
+ * identifier that exists in a certain local checkpoint.
+ * Maximum 4 local checkpoints is possible in this release.
+ */
+ UintR maxGciCompletedInLcp;
+ UintR srLastGci[4];
+ UintR srStartGci[4];
+ /**
+ * The fragment pointers in ACC
+ */
+ UintR accFragptr[2];
+ /**
+ * The EXEC_SR variables are used to keep track of which fragments
+ * that are interested in being executed as part of executing the
+ * fragment loop.
+ * It is initialised for every phase of executing the
+ * fragment log (the fragment log can be executed upto four times).
+ *
+ * Each execution is capable of executing the log records on four
+ * fragment replicas.
+ */
+ /**
+ * Requesting block reference for executing the fragment log
+ * in this phase.
+ */
+ BlockReference execSrBlockref[4];
+ /**
+ * This variable contains references to active scan and copy
+ * fragment operations on the fragment.
+ * A maximum of four concurrently active is allowed.
+ */
+ typedef Bitmask<4> ScanNumberMask;
+ ScanNumberMask m_scanNumberMask;
+ DLList<ScanRecord>::Head m_activeScans;
+ DLFifoList<ScanRecord>::Head m_queuedScans;
+
+ Uint16 srLqhLognode[4];
+ /**
+ * The fragment pointers in TUP and TUX
+ */
+ UintR tupFragptr[2];
+ UintR tuxFragptr[2];
+ /**
+ * This queue is where operations are put when blocked in ACC
+ * during start of a local chkp.
+ */
+ UintR accBlockedList;
+ /**
+ * This is the queue where all operations that are active on the
+ * fragment is put.
+ * This is used to deduct when the fragment do
+ * no longer contain any active operations.
+ * This is needed when starting a local checkpoint.
+ */
+ UintR activeList;
+ /**
+ * This variable keeps track of how many operations that are
+ * active that have skipped writing the log but not yet committed
+ * or aborted. This is used during start of fragment.
+ */
+ UintR activeTcCounter;
+ /**
+ * This status specifies whether this fragment is actively
+ * engaged in executing the fragment log.
+ */
+ ExecSrStatus execSrStatus;
+ /**
+ * The fragment id of this fragment.
+ */
+ UintR fragId;
+ /**
+ * Status of fragment
+ */
+ FragStatus fragStatus;
+ /**
+ * Indicates a local checkpoint is active and thus can generate
+ * UNDO log records.
+ */
+ UintR fragActiveStatus;
+ /**
+ * Reference to current LCP record.
+ * If no LCP is ongoing on the fragment then the value is RNIL.
+ * If LCP_REF /= RNIL then a local checkpoint is ongoing in the
+ * fragment.
+ * LCP_STATE in LCP_RECORD specifies the state of the
+ * local checkpoint.
+ */
+ UintR lcpRef;
+ /**
+ * This flag indicates whether logging is currently activated at
+ * the fragment.
+ * During a system restart it is temporarily shut off.
+ * Some fragments have it permanently shut off.
+ */
+ LogFlag logFlag;
+ UintR masterPtr;
+ /**
+ * This variable contains the maximum global checkpoint identifier
+ * which was completed when the local checkpoint was started.
+ */
+ /**
+ * Reference to the next fragment record in a free list of fragment
+ * records.
+ */
+ UintR nextFrag;
+ /**
+ * The newest GCI that has been committed on fragment
+ */
+ UintR newestGci;
+ SrStatus srStatus;
+ UintR srUserptr;
+ /**
+ * The starting global checkpoint of this fragment.
+ */
+ UintR startGci;
+ /**
+ * A reference to the table owning this fragment.
+ */
+ UintR tabRef;
+ /**
+ * This is the queue to put operations that have been blocked
+ * during start of a local chkp.
+ */
+ UintR firstWaitQueue;
+ UintR lastWaitQueue;
+ /**
+ * The block reference to ACC on the fragment makes it
+ * possible to have different ACC blocks for different
+ * fragments in the future.
+ */
+ BlockReference accBlockref;
+ /**
+ * Ordered index block.
+ */
+ BlockReference tuxBlockref;
+ /**
+ * The master block reference as sent in COPY_ACTIVEREQ.
+ */
+ BlockReference masterBlockref;
+ /**
+ * These variables are used during system restart to recall
+ * from which node to execute the fragment log and which GCI's
+ * this node should start and stop from. Also to remember who
+ * to send the response to when system restart is completed.
+ */
+ BlockReference srBlockref;
+ /**
+ * The block reference to TUP on the fragment makes it
+ * possible to have different TUP blocks for different
+ * fragments in the future.
+ */
+ BlockReference tupBlockref;
+ /**
+ * This state indicates if the fragment will participate in a
+ * checkpoint.
+ * Temporary tables with Fragrecord::logFlag permanently off
+ * will also have Fragrecord::lcpFlag off.
+ */
+ LcpFlag lcpFlag;
+ /**
+ * Used to ensure that updates started with old
+ * configuration do not arrive here after the copy fragment
+ * has started.
+ * If they are allowed to arrive after they
+ * could update a record that has already been replicated to
+ * the new node. This type of arrival should be extremely
+ * rare but we must anyway ensure that no harm is done.
+ */
+ Uint16 copyNode;
+ /**
+ * This variable ensures that only one copy fragment is
+ * active at a time on the fragment.
+ */
+ Uint8 copyFragState;
+ /**
+ * The number of fragment replicas that will execute the log
+ * records in this round of executing the fragment
+ * log. Maximum four is possible.
+ */
+ Uint8 execSrNoReplicas;
+ /**
+ * This variable contains what type of replica this fragment
+ * is. Two types are possible:
+ * - Primary/Backup replica = 0
+ * - Stand-by replica = 1
+ *
+ * It is not possible to distinguish between primary and
+ * backup on a fragment.
+ * This can only be done per transaction.
+ * DIH can change from primary to backup without informing
+ * the various replicas about this change.
+ */
+ Uint8 fragCopy;
+ /**
+ * This is the last fragment distribution key that we have
+ * heard of.
+ */
+ Uint8 fragDistributionKey;
+ /**
+ * The identity of the next local checkpoint this fragment
+ * should perform.
+ */
+ Uint8 nextLcp;
+ /**
+ * How many local checkpoints does the fragment contain
+ */
+ Uint8 srChkpnr;
+ Uint8 srNoLognodes;
+ /**
+ * Table type.
+ */
+ Uint8 tableType;
+ /**
+ * For ordered index fragment, i-value of corresponding
+ * fragment in primary table.
+ */
+ UintR tableFragptr;
+ };
+ typedef Ptr<Fragrecord> FragrecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ GLOBAL CHECKPOINT RECORD $$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This record describes a global checkpoint that is
+ * completed. It waits for all log records belonging to this
+ * global checkpoint to be saved on disk.
+ */
+ struct GcpRecord {
+ /**
+ * The file number within each log part where the log was
+ * located when gcp_savereq was received. The last record
+ * belonging to this global checkpoint is certainly before
+ * this place in the log. We could come even closer but it
+ * would cost performance and doesn't seem like a good
+ * idea. This is simple and it works.
+ */
+ Uint16 gcpFilePtr[4];
+ /**
+ * The page number within the file for each log part.
+ */
+ Uint16 gcpPageNo[4];
+ /**
+ * The word number within the last page that was written for
+ * each log part.
+ */
+ Uint16 gcpWordNo[4];
+ /**
+ * The identity of this global checkpoint.
+ */
+ UintR gcpId;
+ /**
+ * The state of this global checkpoint, one for each log part.
+ */
+ Uint8 gcpLogPartState[4];
+ /**
+ * The sync state of this global checkpoint, one for each
+ * log part.
+ */
+ Uint8 gcpSyncReady[4];
+ /**
+ * User pointer of the sender of gcp_savereq (= master DIH).
+ */
+ UintR gcpUserptr;
+ /**
+ * Block reference of the sender of gcp_savereq
+ * (= master DIH).
+ */
+ BlockReference gcpBlockref;
+ }; // Size 44 bytes
+ typedef Ptr<GcpRecord> GcpRecordPtr;
+
+ struct HostRecord {
+ bool inPackedList;
+ UintR noOfPackedWordsLqh;
+ UintR packedWordsLqh[30];
+ UintR noOfPackedWordsTc;
+ UintR packedWordsTc[29];
+ BlockReference hostLqhBlockRef;
+ BlockReference hostTcBlockRef;
+ };// Size 128 bytes
+ typedef Ptr<HostRecord> HostRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ LOCAL CHECKPOINT RECORD $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This record contains the information about a local
+ * checkpoint that is ongoing. This record is also used as a
+ * system restart record.
+ */
+ struct LcpRecord {
+ LcpRecord() { m_EMPTY_LCP_REQ.clear(); }
+
+ enum LcpState {
+ LCP_IDLE = 0,
+ LCP_STARTED = 1,
+ LCP_COMPLETED = 2,
+ LCP_WAIT_FRAGID = 3,
+ LCP_WAIT_TUP_PREPLCP = 4,
+ LCP_WAIT_HOLDOPS = 5,
+ LCP_WAIT_ACTIVE_FINISH = 6,
+ LCP_START_CHKP = 7,
+ LCP_BLOCKED_COMP = 8,
+ LCP_SR_WAIT_FRAGID = 9,
+ LCP_SR_STARTED = 10,
+ LCP_SR_COMPLETED = 11
+ };
+ Uint32 firstLcpLocAcc;
+ Uint32 firstLcpLocTup;
+ Uint32 lcpAccptr;
+
+ LcpState lcpState;
+ bool lastFragmentFlag;
+
+ struct FragOrd {
+ Uint32 fragPtrI;
+ LcpFragOrd lcpFragOrd;
+ };
+ FragOrd currentFragment;
+
+ bool lcpQueued;
+ FragOrd queuedFragment;
+
+ bool reportEmpty;
+ NdbNodeBitmask m_EMPTY_LCP_REQ;
+ }; // Size 76 bytes
+ typedef Ptr<LcpRecord> LcpRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$ LOCAL CHECKPOINT SUPPORT RECORD $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This record contains the information about an outstanding
+ * request to TUP or ACC. Used for both local checkpoints and
+ * system restart.
+ */
+ struct LcpLocRecord {
+ enum LcpLocstate {
+ IDLE = 0,
+ WAIT_TUP_PREPLCP = 1,
+ WAIT_LCPHOLDOP = 2,
+ HOLDOP_READY = 3,
+ ACC_WAIT_STARTED = 4,
+ ACC_STARTED = 5,
+ ACC_COMPLETED = 6,
+ TUP_WAIT_STARTED = 7,
+ TUP_STARTED = 8,
+ TUP_COMPLETED = 9,
+ SR_ACC_STARTED = 10,
+ SR_TUP_STARTED = 11,
+ SR_ACC_COMPLETED = 12,
+ SR_TUP_COMPLETED = 13
+ };
+ enum WaitingBlock {
+ ACC = 0,
+ TUP = 1,
+ NONE = 2
+ };
+
+ LcpLocstate lcpLocstate;
+ UintR locFragid;
+ UintR masterLcpRec;
+ UintR nextLcpLoc;
+ UintR tupRef;
+ WaitingBlock waitingBlock;
+ Uint32 accContCounter;
+ }; // 28 bytes
+ typedef Ptr<LcpLocRecord> LcpLocRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* */
+ /* THE RECORDS THAT START BY LOG_ ARE A PART OF THE LOG MANAGER. */
+ /* THESE RECORDS ARE USED TO HANDLE THE FRAGMENT LOG. */
+ /* */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ LOG RECORD $$$$$$$ */
+ /* */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* THIS RECORD IS ALIGNED TO BE 256 BYTES. */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This record describes the current state of a log.
+ * A log consists of a number of log files.
+ * These log files are described by the log file record.
+ *
+ * There will be 4 sets of log files.
+ * Different tables will use different log files dependent
+ * on the table id.
+ * This ensures that more than one outstanding request can
+ * be sent to the file system.
+ * The log file to use is found by performing a very simple hash
+ * function.
+ */
+ struct LogPartRecord {
+ enum LogPartState {
+ IDLE = 0, ///< Nothing happens at the moment
+ ACTIVE = 1, ///< An operation is active logging
+ SR_FIRST_PHASE = 2, ///< Finding the end of the log and
+ ///< the information about global
+ ///< checkpoints in the log is ongoing.
+ SR_FIRST_PHASE_COMPLETED = 3, ///< First phase completed
+ SR_THIRD_PHASE_STARTED = 4, ///< Executing fragment log is in 3rd ph
+ SR_THIRD_PHASE_COMPLETED = 5,
+ SR_FOURTH_PHASE_STARTED = 6, ///< Finding the log tail and head
+ ///< is the fourth phase.
+ SR_FOURTH_PHASE_COMPLETED = 7,
+ FILE_CHANGE_PROBLEM = 8, ///< For some reason the write to
+ ///< page zero in file zero have not
+ ///< finished after 15 mbyte of
+ ///< log data have been written
+ TAIL_PROBLEM = 9 ///< Only 1 mbyte of log left.
+ ///< No operations allowed to enter the
+ ///< log. Only special log records
+ ///< are allowed
+ };
+ enum WaitWriteGciLog {
+ WWGL_TRUE = 0,
+ WWGL_FALSE = 1
+ };
+ enum LogExecState {
+ LES_IDLE = 0,
+ LES_SEARCH_STOP = 1,
+ LES_SEARCH_START = 2,
+ LES_EXEC_LOG = 3,
+ LES_EXEC_LOG_NEW_MBYTE = 4,
+ LES_EXEC_LOG_NEW_FILE = 5,
+ LES_EXEC_LOGREC_FROM_FILE = 6,
+ LES_EXEC_LOG_COMPLETED = 7,
+ LES_WAIT_READ_EXEC_SR_NEW_MBYTE = 8,
+ LES_WAIT_READ_EXEC_SR = 9,
+ LES_EXEC_LOG_INVALIDATE = 10
+ };
+
+ /**
+ * Is a CONTINUEB(ZLOG_LQHKEYREQ) signal sent and
+ * outstanding. We do not want several instances of this
+ * signal out in the air since that would create multiple
+ * writers of the list.
+ */
+ UintR LogLqhKeyReqSent;
+ /**
+ * Contains the current log file where log records are
+ * written. During system restart it is used to indicate the
+ * last log file.
+ */
+ UintR currentLogfile;
+ /**
+ * The log file used to execute log records from far behind.
+ */
+ UintR execSrExecLogFile;
+ /**
+ * The currently executing prepare record starts in this log
+ * page. This variable is used to enable that a log record is
+ * executed multiple times in execution of the log.
+ */
+ UintR execSrLogPage;
+ /**
+ * This variable keeps track of the lfo record where the
+ * pages that were read from disk when an operations log
+ * record were not found in the main memory buffer for log
+ * pages.
+ */
+ UintR execSrLfoRec;
+ /**
+ * The starting page number when reading log from far behind.
+ */
+ UintR execSrStartPageNo;
+ /**
+ * The last page number when reading log from far behind.
+ */
+ UintR execSrStopPageNo;
+ /**
+ * Contains a reference to the first log file, file number 0.
+ */
+ UintR firstLogfile;
+ /**
+ * The head of the operations queued for logging.
+ */
+ UintR firstLogQueue;
+ /**
+ * This variable contains the oldest operation in this log
+ * part which have not been committed yet.
+ */
+ UintR firstLogTcrec;
+ /**
+ * The first reference to a set of 8 pages. These are used
+ * during execution of the log to keep track of which pages
+ * are in memory and which are not.
+ */
+ UintR firstPageRef;
+ /**
+ * This variable contains the global checkpoint record
+ * waiting for disk writes to complete.
+ */
+ UintR gcprec;
+ /**
+ * The last reference to a set of 8 pages. These are used
+ * during execution of the log to keep track of which pages
+ * are in memory and which are not.
+ */
+ UintR lastPageRef;
+ /**
+ * The tail of the operations queued for logging.
+ */
+ UintR lastLogQueue;
+ /**
+ * This variable contains the newest operation in this log
+ * part which have not been committed yet.
+ */
+ UintR lastLogTcrec;
+ /**
+ * This variable indicates which was the last mbyte that was
+ * written before the system crashed. Discovered during
+ * system restart.
+ */
+ UintR lastLogfile;
+ /**
+ * This variable is used to keep track of the state during
+ * the third phase of the system restart, i.e. when
+ * LogPartRecord::logPartState ==
+ * LogPartRecord::SR_THIRD_PHASE_STARTED.
+ */
+ LogExecState logExecState;
+ /**
+ * This variable contains the lap number of this log part.
+ */
+ UintR logLap;
+ /**
+ * This variable contains the place to stop executing the log
+ * in this phase.
+ */
+ UintR logLastGci;
+ /**
+ * This variable contains the place to start executing the
+ * log in this phase.
+ */
+ UintR logStartGci;
+ /**
+ * The latest GCI completed in this log part.
+ */
+ UintR logPartNewestCompletedGCI;
+ /**
+ * The current state of this log part.
+ */
+ LogPartState logPartState;
+ /**
+ * A timer that is set every time a log page is sent to disk.
+ * Ensures that log pages are not kept in main memory for
+ * more than a certain time.
+ */
+ UintR logPartTimer;
+ /**
+ * The current timer which is set by the periodic signal
+ * received by LQH
+ */
+ UintR logTimer;
+ /**
+ * Contains the number of the log tail file and the mbyte
+ * reference within that file. This information ensures that
+ * the tail is not overwritten when writing new log records.
+ */
+ UintR logTailFileNo;
+ /**
+ * The TcConnectionrec used during execution of this log part.
+ */
+ UintR logTcConrec;
+ /**
+ * The number of pages that currently resides in the main
+ * memory buffer. It does not refer pages that are currently
+ * read from the log files. Only to pages already read
+ * from the log file.
+ */
+ UintR mmBufferSize;
+ /**
+ * Contains the current number of log files in this log part.
+ */
+ UintR noLogFiles;
+ /**
+ * This variable is used only during execution of a log
+ * record. It keeps track of in which page record a log
+ * record was started. It is used then to deduce which
+ * pages that are dirty after that the log records on the
+ * page have been executed.
+ *
+ * It is also used to find out where to write the invalidate
+ * command when that is needed.
+ */
+ UintR prevLogpage;
+ /**
+ * The number of files remaining to gather GCI information
+ * for during system restart. Only used if number of files
+ * is larger than 60.
+ */
+ UintR srRemainingFiles;
+ /**
+ * The log file where to start executing the log during
+ * system restart.
+ */
+ UintR startLogfile;
+ /**
+ * The last log file in which to execute the log during system
+ * restart.
+ */
+ UintR stopLogfile;
+ /**
+ * This variable keeps track of when we want to write a complete
+ * gci log record but have been blocked by an ongoing log operation.
+ */
+ WaitWriteGciLog waitWriteGciLog;
+ /**
+ * The currently executing prepare record starts in this index
+ * in the log page.
+ */
+ Uint16 execSrLogPageIndex;
+ /**
+ * Which of the four exec_sr's in the fragment is currently executing
+ */
+ Uint16 execSrExecuteIndex;
+ /**
+ * The number of pages executed in the current mbyte.
+ */
+ Uint16 execSrPagesExecuted;
+ /**
+ * The number of pages read from disk that have arrived and are
+ * currently awaiting execution of the log.
+ */
+ Uint16 execSrPagesRead;
+ /**
+ * The number of pages read from disk and currently not arrived
+ * to the block.
+ */
+ Uint16 execSrPagesReading;
+ /**
+ * This variable refers to the new header file where we will
+ * start writing the log after a system restart have been completed.
+ */
+ Uint16 headFileNo;
+ /**
+ * This variable refers to the page number within the header file.
+ */
+ Uint16 headPageNo;
+ /**
+ * This variable refers to the index within the new header
+ * page.
+ */
+ Uint16 headPageIndex;
+ /**
+ * This variables indicates which was the last mbyte in the last
+ * logfile before a system crash. Discovered during system restart.
+ */
+ Uint16 lastMbyte;
+ /**
+ * This variable is used only during execution of a log
+ * record. It keeps track of in which file page a log
+ * record was started. It is used if it is needed to write a
+ * dirty page to disk during log execution (this happens when
+ * commit records are invalidated).
+ */
+ Uint16 prevFilepage;
+ /**
+ * This is used to save where we were in the execution of log
+ * records when we find a commit record that needs to be
+ * executed.
+ *
+ * This variable is also used to remember the index where the
+ * log type was in the log record. It is only used in this
+ * role when finding a commit record that needs to be
+ * invalidated.
+ */
+ Uint16 savePageIndex;
+ Uint8 logTailMbyte;
+ /**
+ * The mbyte within the starting log file where to start
+ * executing the log.
+ */
+ Uint8 startMbyte;
+ /**
+ * The last mbyte in which to execute the log during system
+ * restart.
+ */
+ Uint8 stopMbyte;
+ /**
+ * This variable refers to the file where invalidation is
+ * occuring during system/node restart.
+ */
+ Uint16 invalidateFileNo;
+ /**
+ * This variable refers to the page where invalidation is
+ * occuring during system/node restart.
+ */
+ Uint16 invalidatePageNo;
+ }; // Size 164 Bytes
+ typedef Ptr<LogPartRecord> LogPartRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ LOG FILE RECORD $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* THIS RECORD IS ALIGNED TO BE 288 (256 + 32) BYTES. */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This record contains information about a log file.
+ * A log file contains log records from several tables and
+ * fragments of a table. LQH can contain more than
+ * one log file to ensure faster log processing.
+ *
+ * The number of pages to write to disk at a time is
+ * configurable.
+ */
+ struct LogFileRecord {
+ enum FileChangeState {
+ NOT_ONGOING = 0,
+ BOTH_WRITES_ONGOING = 1,
+ LAST_WRITE_ONGOING = 2,
+ FIRST_WRITE_ONGOING = 3,
+ WRITE_PAGE_ZERO_ONGOING = 4
+ };
+ enum LogFileStatus {
+ LFS_IDLE = 0, ///< Log file record not in use
+ CLOSED = 1, ///< Log file closed
+ OPENING_INIT = 2,
+ OPEN_SR_FRONTPAGE = 3, ///< Log file opened as part of system
+ ///< restart. Open file 0 to find
+ ///< the front page of the log part.
+ OPEN_SR_LAST_FILE = 4, ///< Open last log file that was written
+ ///< before the system restart.
+ OPEN_SR_NEXT_FILE = 5, ///< Open a log file which is 16 files
+ ///< backwards to find the next
+ ///< information about GCPs.
+ OPEN_EXEC_SR_START = 6, ///< Log file opened as part of
+ ///< executing
+ ///< log during system restart.
+ OPEN_EXEC_SR_NEW_MBYTE = 7,
+ OPEN_SR_FOURTH_PHASE = 8,
+ OPEN_SR_FOURTH_NEXT = 9,
+ OPEN_SR_FOURTH_ZERO = 10,
+ OPENING_WRITE_LOG = 11, ///< Log file opened as part of writing
+ ///< log during normal operation.
+ OPEN_EXEC_LOG = 12,
+ CLOSING_INIT = 13,
+ CLOSING_SR = 14, ///< Log file closed as part of system
+ ///< restart. Currently trying to
+ ///< find where to start executing the
+ ///< log
+ CLOSING_EXEC_SR = 15, ///< Log file closed as part of
+ ///< executing log during system restart
+ CLOSING_EXEC_SR_COMPLETED = 16,
+ CLOSING_WRITE_LOG = 17, ///< Log file closed as part of writing
+ ///< log during normal operation.
+ CLOSING_EXEC_LOG = 18,
+ OPEN_INIT = 19,
+ OPEN = 20, ///< Log file open
+ OPEN_SR_INVALIDATE_PAGES = 21,
+ CLOSE_SR_INVALIDATE_PAGES = 22
+ };
+
+ /**
+ * When a new mbyte is started in the log we have to find out
+ * how far back in the log we still have prepared operations
+ * which have been neither committed or aborted. This variable
+ * keeps track of this value for each of the mbytes in this
+ * log file. This is used in writing down these values in the
+ * header of each log file. That information is used during
+ * system restart to find the tail of the log.
+ */
+ UintR logLastPrepRef[16];
+ /**
+ * The max global checkpoint completed before the mbyte in the
+ * log file was started. One variable per mbyte.
+ */
+ UintR logMaxGciCompleted[16];
+ /**
+ * The max global checkpoint started before the mbyte in the log
+ * file was started. One variable per mbyte.
+ */
+ UintR logMaxGciStarted[16];
+ /**
+ * This variable contains the file name as needed by the file
+ * system when opening the file.
+ */
+ UintR fileName[4];
+ /**
+ * This variable has a reference to the log page which is
+ * currently in use by the log.
+ */
+ UintR currentLogpage;
+ /**
+ * The number of the current mbyte in the log file.
+ */
+ UintR currentMbyte;
+ /**
+ * This variable is used when changing files. It is to find
+ * out when both the last write in the previous file and the
+ * first write in this file has been completed. After these
+ * writes have completed the variable keeps track of when the
+ * write to page zero in file zero is completed.
+ */
+ FileChangeState fileChangeState;
+ /**
+ * The number of the file within this log part.
+ */
+ UintR fileNo;
+ /**
+ * This variable shows where to read/write the next pages into
+ * the log. Used when writing the log during normal operation
+ * and when reading the log during system restart. It
+ * specifies the page position where each page is 8 kbyte.
+ */
+ UintR filePosition;
+ /**
+ * This contains the file pointer needed by the file system
+ * when reading/writing/closing and synching.
+ */
+ UintR fileRef;
+ /**
+ * The head of the pages waiting for shipment to disk.
+ * They are filled with log info.
+ */
+ UintR firstFilledPage;
+ /**
+ * A list of active read/write operations on the log file.
+ * Operations are always put in last and the first should
+ * always complete first.
+ */
+ UintR firstLfo;
+ UintR lastLfo;
+ /**
+ * The tail of the pages waiting for shipment to disk.
+ * They are filled with log info.
+ */
+ UintR lastFilledPage;
+ /**
+ * This variable keeps track of the last written page in the
+ * file while writing page zero in file zero when changing log
+ * file.
+ */
+ UintR lastPageWritten;
+ /**
+ * This variable keeps track of the last written word in the
+ * last page written in the file while writing page zero in
+ * file zero when changing log file.
+ */
+ UintR lastWordWritten;
+ /**
+ * This variable contains the last word written in the last page.
+ */
+ UintR logFilePagesToDiskWithoutSynch;
+ /**
+ * This variable keeps track of the number of pages written since
+ * last synch on this log file.
+ */
+ LogFileStatus logFileStatus;
+ /**
+ * A reference to page zero in this file.
+ * This page is written before the file is closed.
+ */
+ UintR logPageZero;
+ /**
+ * This variable contains a reference to the record describing
+ * this log part. One of four records (0,1,2 or 3).
+ */
+ UintR logPartRec;
+ /**
+ * Next free log file record or next log file in this log.
+ */
+ UintR nextLogFile;
+ /**
+ * The previous log file.
+ */
+ UintR prevLogFile;
+ /**
+ * The number of remaining words in this mbyte of the log file.
+ */
+ UintR remainingWordsInMbyte;
+ /**
+ * The current file page within the current log file. This is
+ * a reference within the file and not a reference to a log
+ * page record. It is used to deduce where log records are
+ * written. Particularly completed gcp records and prepare log
+ * records.
+ */
+ Uint16 currentFilepage;
+ /**
+ * The number of pages in the list referenced by
+ * LOG_PAGE_BUFFER.
+ */
+ Uint16 noLogpagesInBuffer;
+ }; // Size 288 bytes
+ typedef Ptr<LogFileRecord> LogFileRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ LOG OPERATION RECORD $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * This record contains a currently active file operation
+ * that has started by the log module.
+ */
+ struct LogFileOperationRecord {
+ enum LfoState {
+ IDLE = 0, ///< Operation is not used at the moment
+ INIT_WRITE_AT_END = 1, ///< Write in file so that it grows to
+ ///< 16 Mbyte
+ INIT_FIRST_PAGE = 2, ///< Initialise the first page in a file
+ WRITE_GCI_ZERO = 3,
+ WRITE_INIT_MBYTE = 4,
+ WRITE_DIRTY = 5,
+ READ_SR_FRONTPAGE = 6, ///< Read page zero in file zero during
+ ///< system restart
+ READ_SR_LAST_FILE = 7, ///< Read page zero in last file open
+ ///< before system crash
+ READ_SR_NEXT_FILE = 8, ///< Read 60 files backwards to find
+ ///< further information GCPs in page
+ ///< zero
+ READ_SR_LAST_MBYTE = 9,
+ READ_EXEC_SR = 10,
+ READ_EXEC_LOG = 11,
+ READ_SR_FOURTH_PHASE = 12,
+ READ_SR_FOURTH_ZERO = 13,
+ FIRST_PAGE_WRITE_IN_LOGFILE = 14,
+ LAST_WRITE_IN_FILE = 15,
+ WRITE_PAGE_ZERO = 16,
+ ACTIVE_WRITE_LOG = 17, ///< A write operation during
+ ///< writing of log
+ READ_SR_INVALIDATE_PAGES = 18,
+ WRITE_SR_INVALIDATE_PAGES = 19
+ };
+ /**
+ * We have to remember the log pages read.
+ * Otherwise we cannot build the linked list after the pages have
+ * arrived to main memory.
+ */
+ UintR logPageArray[16];
+ /**
+ * A list of the pages that are part of this active operation.
+ */
+ UintR firstLfoPage;
+ /**
+ * A timer to ensure that records are not lost.
+ */
+ UintR lfoTimer;
+ /**
+ * The word number of the last written word in the last during
+ * a file write.
+ */
+ UintR lfoWordWritten;
+ /**
+ * This variable contains the state of the log file operation.
+ */
+ LfoState lfoState;
+ /**
+ * The log file that the file operation affects.
+ */
+ UintR logFileRec;
+ /**
+ * The log file operations on a file are kept in a linked list.
+ */
+ UintR nextLfo;
+ /**
+ * The page number of the first read/written page during a file
+ * read/write.
+ */
+ Uint16 lfoPageNo;
+ /**
+ * The number of pages written or read during an operation to
+ * the log file.
+ */
+ Uint16 noPagesRw;
+ }; // 92 bytes
+ typedef Ptr<LogFileOperationRecord> LogFileOperationRecordPtr;
+
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /* $$$$$$$ LOG PAGE RECORD $$$$$$$ */
+ /* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
+ /**
+ * These are the 8 k pages used to store log records before storing
+ * them in the file system.
+ * Since 64 kbyte is sent to disk at a time it is necessary to have
+ * at least 4*64 kbytes of log pages.
+ * To handle multiple outstanding requests we need some additional pages.
+ * Thus we allocate 1 mbyte to ensure that we do not get problems with
+ * insufficient number of pages.
+ */
+ struct LogPageRecord {
+ /**
+ * This variable contains the pages that are sent to disk.
+ *
+ * All pages contain a header of 12 words:
+ * - WORD 0: CHECKSUM Calculated before storing on disk and
+ * checked when read from disk.
+ * - WORD 1: LAP How many wraparounds have the log
+ * experienced since initial start of the
+ * system.
+ * - WORD 2: MAX_GCI_COMPLETED Which is the maximum gci which have
+ * completed before this page. This
+ * gci will not be found in this
+ * page and hereafter in the log.
+ * - WORD 3: MAX_GCI_STARTED The maximum gci which have started
+ * before this page.
+ * - WORD 4: NEXT_PAGE Pointer to the next page.
+ * Only used in main memory
+ * - WORD 5: PREVIOUS_PAGE Pointer to the previous page.
+ * Currently not used.
+ * - WORD 6: VERSION NDB version that wrote the page.
+ * - WORD 7: NO_LOG_FILES Number of log files in this log part.
+ * - WORD 8: CURRENT PAGE INDEX This keeps track of where we are in the
+ * page.
+ * This is only used when pages is in
+ * memory.
+ * - WORD 9: OLD PREPARE FILE NO This keeps track of the oldest prepare
+ * operation still alive (not committed
+ * or aborted) when this mbyte started.
+ * - WORD 10: OLD PREPARE PAGE REF File page reference within this file
+ * number.
+ * Page no + Page index.
+ * If no prepare was alive then these
+ * values points this mbyte.
+ * - WORD 11: DIRTY FLAG = 0 means not dirty and
+ * = 1 means the page is dirty.
+ * Is used when executing log when
+ * a need to write invalid commit
+ * records arise.
+ *
+ * The remaining 2036 words are used for log information, i.e.
+ * log records.
+ *
+ * A log record on this page has the following layout:
+ * - WORD 0: LOG RECORD TYPE
+ * The following types are supported:
+ * - PREPARE OPERATION An operation not yet committed.
+ * - NEW PREPARE OPERATION A prepared operation already
+ * logged is inserted
+ * into the log again so that the
+ * log tail can be advanced.
+ * This can happen when a transaction is
+ * committed for a long time.
+ * - ABORT TRANSACTION A previously prepared transaction
+ * was aborted.
+ * - COMMIT TRANSACTION A previously prepared transaction
+ * was committed.
+ * - INVALID COMMIT A previous commit record was
+ * invalidated by a
+ * subsequent system restart.
+ * A log record must be invalidated
+ * in a system restart if it belongs
+ * to a global checkpoint id which
+ * is not included in the system
+ * restart.
+ * Otherwise it will be included in
+ * a subsequent system restart since
+ * it will then most likely belong
+ * to a global checkpoint id which
+ * is part of that system
+ * restart.
+ * This is not a correct behaviour
+ * since this operation is lost in a
+ * system restart and should not
+ * reappear at a later system
+ * restart.
+ * - COMPLETED GCI A GCI has now been completed.
+ * - FRAGMENT SPLIT A fragment has been split
+ * (not implemented yet)
+ * - FILE DESCRIPTOR This is always the first log record
+ * in a file.
+ * It is always placed on page 0 after
+ * the header.
+ * It is written when the file is
+ * opened and when the file is closed.
+ * - NEXT LOG RECORD This log record only records where
+ * the next log record starts.
+ * - NEXT MBYTE RECORD This log record specifies that there
+ * are no more log records in this mbyte.
+ *
+ *
+ * A FILE DESCRIPTOR log record continues as follows:
+ * - WORD 1: NO_LOG_DESCRIPTORS This defines the number of
+ * descriptors of log files that
+ * will follow hereafter (max 32).
+ * the log descriptor will describe
+ * information about
+ * max_gci_completed,
+ * max_gci_started and log_lap at
+ * every 1 mbyte of the log file
+ * since a log file is 16 mbyte
+ * always, i need 16 entries in the
+ * array with max_gci_completed,
+ * max_gci_started and log_lap. thus
+ * 32 entries per log file
+ * descriptor (max 32*48 = 1536,
+ * always fits in page 0).
+ * - WORD 2: LAST LOG FILE The number of the log file currently
+ * open. This is only valid in file 0.
+ * - WORD 3 - WORD 18: MAX_GCI_COMPLETED for every 1 mbyte
+ * in this log file.
+ * - WORD 19 - WORD 34: MAX_GCI_STARTED for every 1 mbyte
+ * in this log file.
+ *
+ * Then it continues for NO_LOG_DESCRIPTORS until all subsequent
+ * log files (max 32) have been properly described.
+ *
+ *
+ * A PREPARE OPERATION log record continues as follows:
+ * - WORD 1: LOG RECORD SIZE
+ * - WORD 2: HASH VALUE
+ * - WORD 3: SCHEMA VERSION
+ * - WORD 4: OPERATION TYPE
+ * = 0 READ,
+ * = 1 UPDATE,
+ * = 2 INSERT,
+ * = 3 DELETE
+ * - WORD 5: NUMBER OF WORDS IN ATTRINFO PART
+ * - WORD 6: KEY LENGTH IN WORDS
+ * - WORD 7 - (WORD 7 + KEY_LENGTH - 1) The tuple key
+ * - (WORD 7 + KEY_LENGTH) -
+ * (WORD 7 + KEY_LENGTH + ATTRINFO_LENGTH - 1) The attrinfo
+ *
+ * A log record can be spread in several pages in some cases.
+ * The next log record always starts immediately after this log record.
+ * A log record does however never traverse a 1 mbyte boundary.
+ * This is used to ensure that we can always come back if something
+ * strange occurs in the log file.
+ * To ensure this we also have log records which only records
+ * the next log record.
+ *
+ *
+ * A COMMIT TRANSACTION log record continues as follows:
+ * - WORD 1: TRANSACTION ID PART 1
+ * - WORD 2: TRANSACTION ID PART 2
+ * - WORD 3: FRAGMENT ID OF THE OPERATION
+ * - WORD 4: TABLE ID OF THE OPERATION
+ * - WORD 5: THE FILE NUMBER OF THE PREPARE RECORD
+ * - WORD 6: THE STARTING PAGE NUMBER OF THE PREPARE RECORD
+ * - WORD 7: THE STARTING PAGE INDEX OF THE PREPARE RECORD
+ * - WORD 8: THE STOP PAGE NUMBER OF THE PREPARE RECORD
+ * - WORD 9: GLOBAL CHECKPOINT OF THE TRANSACTION
+ *
+ *
+ * An ABORT TRANSACTION log record continues as follows:
+ * - WORD 1: TRANSACTION ID PART 1
+ * - WORD 2: TRANSACTION ID PART 2
+ *
+ *
+ * A COMPLETED CGI log record continues as follows:
+ * - WORD 1: THE COMPLETED GCI
+ *
+ *
+ * A NEXT LOG RECORD log record continues as follows:
+ * - There is no more information needed.
+ * The next log record will always refer to the start of the next page.
+ *
+ * A NEXT MBYTE RECORD log record continues as follows:
+ * - There is no more information needed.
+ * The next mbyte will always refer to the start of the next mbyte.
+ */
+ UintR logPageWord[8192]; // Size 32 kbytes
+ };
+ typedef Ptr<LogPageRecord> LogPageRecordPtr;
+
+ struct PageRefRecord {
+ UintR pageRef[8];
+ UintR prNext;
+ UintR prPrev;
+ Uint16 prFileNo;
+ Uint16 prPageNo;
+ }; // size 44 bytes
+ typedef Ptr<PageRefRecord> PageRefRecordPtr;
+
+ struct Tablerec {
+ enum TableStatus {
+ TABLE_DEFINED = 0,
+ NOT_DEFINED = 1,
+ ADD_TABLE_ONGOING = 2,
+ PREP_DROP_TABLE_ONGOING = 3,
+ PREP_DROP_TABLE_DONE = 4
+ };
+
+ UintR fragrec[MAX_FRAG_PER_NODE];
+ Uint16 fragid[MAX_FRAG_PER_NODE];
+ /**
+ * Status of the table
+ */
+ TableStatus tableStatus;
+ /**
+ * Table type and target table of index.
+ */
+ Uint16 tableType;
+ Uint16 primaryTableId;
+ Uint32 schemaVersion;
+
+ Uint32 usageCount;
+ NdbNodeBitmask waitingTC;
+ NdbNodeBitmask waitingDIH;
+ }; // Size 100 bytes
+ typedef Ptr<Tablerec> TablerecPtr;
+
+ struct TcConnectionrec {
+ enum ListState {
+ NOT_IN_LIST = 0,
+ IN_ACTIVE_LIST = 1,
+ ACC_BLOCK_LIST = 2,
+ WAIT_QUEUE_LIST = 3
+ };
+ enum LogWriteState {
+ NOT_STARTED = 0,
+ NOT_WRITTEN = 1,
+ NOT_WRITTEN_WAIT = 2,
+ WRITTEN = 3
+ };
+ enum AbortState {
+ ABORT_IDLE = 0,
+ ABORT_ACTIVE = 1,
+ NEW_FROM_TC = 2,
+ REQ_FROM_TC = 3,
+ ABORT_FROM_TC = 4,
+ ABORT_FROM_LQH = 5
+ };
+ enum TransactionState {
+ IDLE = 0,
+
+ /* -------------------------------------------------------------------- */
+ // Transaction in progress states
+ /* -------------------------------------------------------------------- */
+ WAIT_ACC = 1,
+ WAIT_TUPKEYINFO = 2,
+ WAIT_ATTR = 3,
+ WAIT_TUP = 4,
+ STOPPED = 5,
+ LOG_QUEUED = 6,
+ PREPARED = 7,
+ LOG_COMMIT_WRITTEN_WAIT_SIGNAL = 8,
+ LOG_COMMIT_QUEUED_WAIT_SIGNAL = 9,
+
+ /* -------------------------------------------------------------------- */
+ // Commit in progress states
+ /* -------------------------------------------------------------------- */
+ COMMIT_STOPPED = 10,
+ LOG_COMMIT_QUEUED = 11,
+ COMMIT_QUEUED = 12,
+ COMMITTED = 13,
+
+ /* -------------------------------------------------------------------- */
+ // Abort in progress states
+ /* -------------------------------------------------------------------- */
+ WAIT_ACC_ABORT = 14,
+ ABORT_QUEUED = 15,
+ ABORT_STOPPED = 16,
+ WAIT_AI_AFTER_ABORT = 17,
+ LOG_ABORT_QUEUED = 18,
+ WAIT_TUP_TO_ABORT = 19,
+
+ /* -------------------------------------------------------------------- */
+ // Scan in progress states
+ /* -------------------------------------------------------------------- */
+ WAIT_SCAN_AI = 20,
+ SCAN_STATE_USED = 21,
+ SCAN_FIRST_STOPPED = 22,
+ SCAN_CHECK_STOPPED = 23,
+ SCAN_STOPPED = 24,
+ SCAN_RELEASE_STOPPED = 25,
+ SCAN_CLOSE_STOPPED = 26,
+ COPY_CLOSE_STOPPED = 27,
+ COPY_FIRST_STOPPED = 28,
+ COPY_STOPPED = 29,
+ SCAN_TUPKEY = 30,
+ COPY_TUPKEY = 31,
+
+ TC_NOT_CONNECTED = 32,
+ PREPARED_RECEIVED_COMMIT = 33, // Temporary state in write commit log
+ LOG_COMMIT_WRITTEN = 34 // Temporary state in write commit log
+ };
+ enum ConnectState {
+ DISCONNECTED = 0,
+ CONNECTED = 1,
+ COPY_CONNECTED = 2,
+ LOG_CONNECTED = 3
+ };
+ ConnectState connectState;
+ UintR copyCountWords;
+ UintR firstAttrinfo[5];
+ UintR tupkeyData[4];
+ UintR transid[2];
+ AbortState abortState;
+ UintR accConnectrec;
+ UintR applOprec;
+ UintR clientConnectrec;
+ UintR tcTimer;
+ UintR currReclenAi;
+ UintR currTupAiLen;
+ UintR firstAttrinbuf;
+ UintR firstTupkeybuf;
+ UintR fragmentid;
+ UintR fragmentptr;
+ UintR gci;
+ UintR hashValue;
+ UintR lastTupkeybuf;
+ UintR lastAttrinbuf;
+ /**
+ * Each operation (TcConnectrec) can be stored in max one out of many
+ * lists.
+ * This variable keeps track of which list it is in.
+ */
+ ListState listState;
+
+ UintR logStartFileNo;
+ LogWriteState logWriteState;
+ UintR nextHashRec;
+ UintR nextLogTcrec;
+ UintR nextTcLogQueue;
+ UintR nextTc;
+ UintR nextTcConnectrec;
+ UintR prevHashRec;
+ UintR prevLogTcrec;
+ UintR prevTc;
+ UintR readlenAi;
+ UintR reqRef;
+ UintR reqinfo;
+ UintR schemaVersion;
+ UintR storedProcId;
+ UintR simpleTcConnect;
+ UintR tableref;
+ UintR tcOprec;
+ UintR tcScanInfo;
+ UintR tcScanRec;
+ UintR totReclenAi;
+ UintR totSendlenAi;
+ UintR tupConnectrec;
+ UintR savePointId;
+ TransactionState transactionState;
+ BlockReference applRef;
+ BlockReference clientBlockref;
+
+ BlockReference reqBlockref;
+ BlockReference tcBlockref;
+ BlockReference tcAccBlockref;
+ BlockReference tcTuxBlockref;
+ BlockReference tcTupBlockref;
+ Uint32 commitAckMarker;
+ union {
+ Uint32 m_scan_curr_range_no;
+ UintR noFiredTriggers;
+ };
+ Uint16 errorCode;
+ Uint16 logStartPageIndex;
+ Uint16 logStartPageNo;
+ Uint16 logStopPageNo;
+ Uint16 nextReplica;
+ Uint16 primKeyLen;
+ Uint16 save1;
+ Uint16 nodeAfterNext[3];
+
+ Uint8 activeCreat;
+ Uint8 apiVersionNo;
+ Uint8 dirtyOp;
+ Uint8 indTakeOver;
+ Uint8 lastReplicaNo;
+ Uint8 localFragptr;
+ Uint8 lockType;
+ Uint8 nextSeqNoReplica;
+ Uint8 opSimple;
+ Uint8 opExec;
+ Uint8 operation;
+ Uint8 reclenAiLqhkey;
+ Uint8 m_offset_current_keybuf;
+ Uint8 replicaType;
+ Uint8 simpleRead;
+ Uint8 seqNoReplica;
+ Uint8 tcNodeFailrec;
+ }; /* p2c: size = 280 bytes */
+
+ typedef Ptr<TcConnectionrec> TcConnectionrecPtr;
+
+ struct TcNodeFailRecord {
+ enum TcFailStatus {
+ TC_STATE_TRUE = 0,
+ TC_STATE_FALSE = 1,
+ TC_STATE_BREAK = 2
+ };
+ UintR lastNewTcRef;
+ UintR newTcRef;
+ TcFailStatus tcFailStatus;
+ UintR tcRecNow;
+ BlockReference lastNewTcBlockref;
+ BlockReference newTcBlockref;
+ Uint16 oldNodeId;
+ }; // Size 28 bytes
+ typedef Ptr<TcNodeFailRecord> TcNodeFailRecordPtr;
+
+ struct CommitLogRecord {
+ Uint32 startPageNo;
+ Uint32 startPageIndex;
+ Uint32 stopPageNo;
+ Uint32 fileNo;
+ };
+
+public:
+ Dblqh(const class Configuration &);
+ virtual ~Dblqh();
+
+private:
+ BLOCK_DEFINES(Dblqh);
+
+ void execPACKED_SIGNAL(Signal* signal);
+ void execDEBUG_SIG(Signal* signal);
+ void execATTRINFO(Signal* signal);
+ void execKEYINFO(Signal* signal);
+ void execLQHKEYREQ(Signal* signal);
+ void execLQHKEYREF(Signal* signal);
+ void execCOMMIT(Signal* signal);
+ void execCOMPLETE(Signal* signal);
+ void execLQHKEYCONF(Signal* signal);
+ void execTESTSIG(Signal* signal);
+ void execLQH_RESTART_OP(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+ void execSTART_RECREQ(Signal* signal);
+ void execSTART_RECCONF(Signal* signal);
+ void execEXEC_FRAGREQ(Signal* signal);
+ void execEXEC_FRAGCONF(Signal* signal);
+ void execEXEC_FRAGREF(Signal* signal);
+ void execSTART_EXEC_SR(Signal* signal);
+ void execEXEC_SRREQ(Signal* signal);
+ void execEXEC_SRCONF(Signal* signal);
+ void execREAD_PSUEDO_REQ(Signal* signal);
+
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execACC_COM_BLOCK(Signal* signal);
+ void execACC_COM_UNBLOCK(Signal* signal);
+ void execTUP_COM_BLOCK(Signal* signal);
+ void execTUP_COM_UNBLOCK(Signal* signal);
+ void execACC_ABORTCONF(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execCHECK_LCP_STOP(Signal* signal);
+ void execSEND_PACKED(Signal* signal);
+ void execTUP_ATTRINFO(Signal* signal);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ void execLQHFRAGREQ(Signal* signal);
+ void execLQHADDATTREQ(Signal* signal);
+ void execTUP_ADD_ATTCONF(Signal* signal);
+ void execTUP_ADD_ATTRREF(Signal* signal);
+ void execACCFRAGCONF(Signal* signal);
+ void execACCFRAGREF(Signal* signal);
+ void execTUPFRAGCONF(Signal* signal);
+ void execTUPFRAGREF(Signal* signal);
+ void execTAB_COMMITREQ(Signal* signal);
+ void execACCSEIZECONF(Signal* signal);
+ void execACCSEIZEREF(Signal* signal);
+ void execREAD_NODESCONF(Signal* signal);
+ void execREAD_NODESREF(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+ void execTUPSEIZECONF(Signal* signal);
+ void execTUPSEIZEREF(Signal* signal);
+ void execACCKEYCONF(Signal* signal);
+ void execACCKEYREF(Signal* signal);
+ void execTUPKEYCONF(Signal* signal);
+ void execTUPKEYREF(Signal* signal);
+ void execABORT(Signal* signal);
+ void execABORTREQ(Signal* signal);
+ void execCOMMITREQ(Signal* signal);
+ void execCOMPLETEREQ(Signal* signal);
+ void execMEMCHECKREQ(Signal* signal);
+ void execSCAN_FRAGREQ(Signal* signal);
+ void execSCAN_NEXTREQ(Signal* signal);
+ void execACC_SCANCONF(Signal* signal);
+ void execACC_SCANREF(Signal* signal);
+ void execNEXT_SCANCONF(Signal* signal);
+ void execNEXT_SCANREF(Signal* signal);
+ void execACC_TO_REF(Signal* signal);
+ void execSTORED_PROCCONF(Signal* signal);
+ void execSTORED_PROCREF(Signal* signal);
+ void execCOPY_FRAGREQ(Signal* signal);
+ void execCOPY_ACTIVEREQ(Signal* signal);
+ void execCOPY_STATEREQ(Signal* signal);
+ void execLQH_TRANSREQ(Signal* signal);
+ void execTRANSID_AI(Signal* signal);
+ void execINCL_NODEREQ(Signal* signal);
+ void execACC_LCPCONF(Signal* signal);
+ void execACC_LCPREF(Signal* signal);
+ void execACC_LCPSTARTED(Signal* signal);
+ void execACC_CONTOPCONF(Signal* signal);
+ void execLCP_FRAGIDCONF(Signal* signal);
+ void execLCP_FRAGIDREF(Signal* signal);
+ void execLCP_HOLDOPCONF(Signal* signal);
+ void execLCP_HOLDOPREF(Signal* signal);
+ void execTUP_PREPLCPCONF(Signal* signal);
+ void execTUP_PREPLCPREF(Signal* signal);
+ void execTUP_LCPCONF(Signal* signal);
+ void execTUP_LCPREF(Signal* signal);
+ void execTUP_LCPSTARTED(Signal* signal);
+ void execEND_LCPCONF(Signal* signal);
+
+ void execLCP_FRAG_ORD(Signal* signal);
+ void execEMPTY_LCP_REQ(Signal* signal);
+
+ void execSTART_FRAGREQ(Signal* signal);
+ void execSTART_RECREF(Signal* signal);
+ void execSR_FRAGIDCONF(Signal* signal);
+ void execSR_FRAGIDREF(Signal* signal);
+ void execACC_SRCONF(Signal* signal);
+ void execACC_SRREF(Signal* signal);
+ void execTUP_SRCONF(Signal* signal);
+ void execTUP_SRREF(Signal* signal);
+ void execGCP_SAVEREQ(Signal* signal);
+ void execFSOPENCONF(Signal* signal);
+ void execFSOPENREF(Signal* signal);
+ void execFSCLOSECONF(Signal* signal);
+ void execFSCLOSEREF(Signal* signal);
+ void execFSWRITECONF(Signal* signal);
+ void execFSWRITEREF(Signal* signal);
+ void execFSREADCONF(Signal* signal);
+ void execFSREADREF(Signal* signal);
+ void execSCAN_HBREP(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+ void execTIME_SIGNAL(Signal* signal);
+ void execFSSYNCCONF(Signal* signal);
+ void execFSSYNCREF(Signal* signal);
+
+ void execALTER_TAB_REQ(Signal* signal);
+ void execALTER_TAB_CONF(Signal* signal);
+
+ void execCREATE_TRIG_CONF(Signal* signal);
+ void execCREATE_TRIG_REF(Signal* signal);
+ void execCREATE_TRIG_REQ(Signal* signal);
+
+ void execDROP_TRIG_CONF(Signal* signal);
+ void execDROP_TRIG_REF(Signal* signal);
+ void execDROP_TRIG_REQ(Signal* signal);
+
+ void execPREP_DROP_TAB_REQ(Signal* signal);
+ void execWAIT_DROP_TAB_REQ(Signal* signal);
+ void execDROP_TAB_REQ(Signal* signal);
+
+ void execLQH_ALLOCREQ(Signal* signal);
+ void execLQH_WRITELOG_REQ(Signal* signal);
+
+ void execTUXFRAGCONF(Signal* signal);
+ void execTUXFRAGREF(Signal* signal);
+ void execTUX_ADD_ATTRCONF(Signal* signal);
+ void execTUX_ADD_ATTRREF(Signal* signal);
+
+ // Statement blocks
+
+ void init_acc_ptr_list(ScanRecord*);
+ bool seize_acc_ptr_list(ScanRecord*, Uint32);
+ void release_acc_ptr_list(ScanRecord*);
+ Uint32 get_acc_ptr_from_scan_record(ScanRecord*, Uint32, bool);
+ void set_acc_ptr_in_scan_record(ScanRecord*, Uint32, Uint32);
+ void i_get_acc_ptr(ScanRecord*, Uint32*&, Uint32);
+
+ void removeTable(Uint32 tableId);
+ void sendLCP_COMPLETE_REP(Signal* signal, Uint32 lcpId);
+ void sendEMPTY_LCP_CONF(Signal* signal, bool idle);
+ void sendLCP_FRAGIDREQ(Signal* signal);
+ void sendLCP_FRAG_REP(Signal * signal, const LcpRecord::FragOrd &) const;
+
+ void updatePackedList(Signal* signal, HostRecord * ahostptr, Uint16 hostId);
+ void LQHKEY_abort(Signal* signal, int errortype);
+ void LQHKEY_error(Signal* signal, int errortype);
+ void nextRecordCopy(Signal* signal);
+ void calculateHash(Signal* signal);
+ void continueAfterCheckLcpStopBlocked(Signal* signal);
+ void checkLcpStopBlockedLab(Signal* signal);
+ void sendCommittedTc(Signal* signal, BlockReference atcBlockref);
+ void sendCompletedTc(Signal* signal, BlockReference atcBlockref);
+ void sendLqhkeyconfTc(Signal* signal, BlockReference atcBlockref);
+ void sendCommitLqh(Signal* signal, BlockReference alqhBlockref);
+ void sendCompleteLqh(Signal* signal, BlockReference alqhBlockref);
+ void sendPackedSignalLqh(Signal* signal, HostRecord * ahostptr);
+ void sendPackedSignalTc(Signal* signal, HostRecord * ahostptr);
+ Uint32 handleLongTupKey(Signal* signal,
+ Uint32 lenSofar,
+ Uint32 primKeyLen,
+ Uint32* dataPtr);
+ void cleanUp(Signal* signal);
+ void sendAttrinfoLoop(Signal* signal);
+ void sendAttrinfoSignal(Signal* signal);
+ void sendLqhAttrinfoSignal(Signal* signal);
+ void sendKeyinfoAcc(Signal* signal, Uint32 pos);
+ Uint32 initScanrec(const class ScanFragReq *);
+ void initScanTc(Signal* signal,
+ Uint32 transid1,
+ Uint32 transid2,
+ Uint32 fragId,
+ Uint32 nodeId);
+ void finishScanrec(Signal* signal);
+ void releaseScanrec(Signal* signal);
+ void seizeScanrec(Signal* signal);
+ Uint32 sendKeyinfo20(Signal* signal, ScanRecord *, TcConnectionrec *);
+ void sendScanFragConf(Signal* signal, Uint32 scanCompleted);
+ void initCopyrec(Signal* signal);
+ void initCopyTc(Signal* signal);
+ void sendCopyActiveConf(Signal* signal,Uint32 tableId);
+ void checkLcpCompleted(Signal* signal);
+ void checkLcpHoldop(Signal* signal);
+ void checkLcpStarted(Signal* signal);
+ void checkLcpTupprep(Signal* signal);
+ void getNextFragForLcp(Signal* signal);
+ void initLcpLocAcc(Signal* signal, Uint32 fragId);
+ void initLcpLocTup(Signal* signal, Uint32 fragId);
+ void moveAccActiveFrag(Signal* signal);
+ void moveActiveToAcc(Signal* signal);
+ void releaseLocalLcps(Signal* signal);
+ void seizeLcpLoc(Signal* signal);
+ void sendAccContOp(Signal* signal);
+ void sendStartLcp(Signal* signal);
+ void setLogTail(Signal* signal, Uint32 keepGci);
+ Uint32 remainingLogSize(const LogFileRecordPtr &sltCurrLogFilePtr,
+ const LogPartRecordPtr &sltLogPartPtr);
+ void checkGcpCompleted(Signal* signal, Uint32 pageWritten, Uint32 wordWritten);
+ void initFsopenconf(Signal* signal);
+ void initFsrwconf(Signal* signal);
+ void initLfo(Signal* signal);
+ void initLogfile(Signal* signal, Uint32 fileNo);
+ void initLogpage(Signal* signal);
+ void openFileRw(Signal* signal, LogFileRecordPtr olfLogFilePtr);
+ void openLogfileInit(Signal* signal);
+ void openNextLogfile(Signal* signal);
+ void releaseLfo(Signal* signal);
+ void releaseLfoPages(Signal* signal);
+ void releaseLogpage(Signal* signal);
+ void seizeLfo(Signal* signal);
+ void seizeLogfile(Signal* signal);
+ void seizeLogpage(Signal* signal);
+ void writeFileDescriptor(Signal* signal);
+ void writeFileHeaderOpen(Signal* signal, Uint32 type);
+ void writeInitMbyte(Signal* signal);
+ void writeSinglePage(Signal* signal, Uint32 pageNo, Uint32 wordWritten);
+ void buildLinkedLogPageList(Signal* signal);
+ void changeMbyte(Signal* signal);
+ Uint32 checkIfExecLog(Signal* signal);
+ void checkNewMbyte(Signal* signal);
+ void checkReadExecSr(Signal* signal);
+ void checkScanTcCompleted(Signal* signal);
+ void checkSrCompleted(Signal* signal);
+ void closeFile(Signal* signal, LogFileRecordPtr logFilePtr);
+ void completedLogPage(Signal* signal, Uint32 clpType);
+ void deleteFragrec(Uint32 fragId);
+ void deleteTransidHash(Signal* signal);
+ void findLogfile(Signal* signal,
+ Uint32 fileNo,
+ LogPartRecordPtr flfLogPartPtr,
+ LogFileRecordPtr* parLogFilePtr);
+ void findPageRef(Signal* signal, CommitLogRecord* commitLogRecord);
+ int findTransaction(UintR Transid1, UintR Transid2, UintR TcOprec);
+ void getFirstInLogQueue(Signal* signal);
+ bool getFragmentrec(Signal* signal, Uint32 fragId);
+ void initialiseAddfragrec(Signal* signal);
+ void initialiseAttrbuf(Signal* signal);
+ void initialiseDatabuf(Signal* signal);
+ void initialiseFragrec(Signal* signal);
+ void initialiseGcprec(Signal* signal);
+ void initialiseLcpRec(Signal* signal);
+ void initialiseLcpLocrec(Signal* signal);
+ void initialiseLfo(Signal* signal);
+ void initialiseLogFile(Signal* signal);
+ void initialiseLogPage(Signal* signal);
+ void initialiseLogPart(Signal* signal);
+ void initialisePageRef(Signal* signal);
+ void initialiseScanrec(Signal* signal);
+ void initialiseTabrec(Signal* signal);
+ void initialiseTcrec(Signal* signal);
+ void initialiseTcNodeFailRec(Signal* signal);
+ void initFragrec(Signal* signal,
+ Uint32 tableId,
+ Uint32 fragId,
+ Uint32 copyType);
+ void initFragrecSr(Signal* signal);
+ void initGciInLogFileRec(Signal* signal, Uint32 noFdDesc);
+ void initLcpSr(Signal* signal,
+ Uint32 lcpNo,
+ Uint32 lcpId,
+ Uint32 tableId,
+ Uint32 fragId,
+ Uint32 fragPtr);
+ void initLogpart(Signal* signal);
+ void initLogPointers(Signal* signal);
+ void initReqinfoExecSr(Signal* signal);
+ bool insertFragrec(Signal* signal, Uint32 fragId);
+ void linkActiveFrag(Signal* signal);
+ void linkFragQueue(Signal* signal);
+ void linkWaitLog(Signal* signal, LogPartRecordPtr regLogPartPtr);
+ void logNextStart(Signal* signal);
+ void moveToPageRef(Signal* signal);
+ void readAttrinfo(Signal* signal);
+ void readCommitLog(Signal* signal, CommitLogRecord* commitLogRecord);
+ void readExecLog(Signal* signal);
+ void readExecSrNewMbyte(Signal* signal);
+ void readExecSr(Signal* signal);
+ void readKey(Signal* signal);
+ void readLogData(Signal* signal, Uint32 noOfWords, Uint32* dataPtr);
+ void readLogHeader(Signal* signal);
+ Uint32 readLogword(Signal* signal);
+ Uint32 readLogwordExec(Signal* signal);
+ void readSinglePage(Signal* signal, Uint32 pageNo);
+ void releaseAccList(Signal* signal);
+ void releaseActiveCopy(Signal* signal);
+ void releaseActiveFrag(Signal* signal);
+ void releaseActiveList(Signal* signal);
+ void releaseAddfragrec(Signal* signal);
+ void releaseFragrec();
+ void releaseLcpLoc(Signal* signal);
+ void releaseOprec(Signal* signal);
+ void releasePageRef(Signal* signal);
+ void releaseMmPages(Signal* signal);
+ void releasePrPages(Signal* signal);
+ void releaseTcrec(Signal* signal, TcConnectionrecPtr tcConnectptr);
+ void releaseTcrecLog(Signal* signal, TcConnectionrecPtr tcConnectptr);
+ void releaseWaitQueue(Signal* signal);
+ void removeLogTcrec(Signal* signal);
+ void removePageRef(Signal* signal);
+ Uint32 returnExecLog(Signal* signal);
+ int saveTupattrbuf(Signal* signal, Uint32* dataPtr, Uint32 length);
+ void seizeAddfragrec(Signal* signal);
+ void seizeAttrinbuf(Signal* signal);
+ Uint32 seize_attrinbuf();
+ Uint32 release_attrinbuf(Uint32);
+ Uint32 copy_bounds(Uint32 * dst, TcConnectionrec*);
+
+ void seizeFragmentrec(Signal* signal);
+ void seizePageRef(Signal* signal);
+ void seizeTcrec();
+ void seizeTupkeybuf(Signal* signal);
+ void sendAborted(Signal* signal);
+ void sendLqhTransconf(Signal* signal, LqhTransConf::OperationStatus);
+ void sendTupkey(Signal* signal);
+ void startExecSr(Signal* signal);
+ void startNextExecSr(Signal* signal);
+ void startTimeSupervision(Signal* signal);
+ void stepAhead(Signal* signal, Uint32 stepAheadWords);
+ void systemError(Signal* signal);
+ void writeAbortLog(Signal* signal);
+ void writeCommitLog(Signal* signal, LogPartRecordPtr regLogPartPtr);
+ void writeCompletedGciLog(Signal* signal);
+ void writeDirty(Signal* signal);
+ void writeKey(Signal* signal);
+ void writeLogHeader(Signal* signal);
+ void writeLogWord(Signal* signal, Uint32 data);
+ void writeNextLog(Signal* signal);
+ void errorReport(Signal* signal, int place);
+ void warningReport(Signal* signal, int place);
+ void invalidateLogAfterLastGCI(Signal *signal);
+ void readFileInInvalidate(Signal *signal);
+ void exitFromInvalidate(Signal* signal);
+ Uint32 calcPageCheckSum(LogPageRecordPtr logP);
+
+ // Generated statement blocks
+ void systemErrorLab(Signal* signal);
+ void initFourth(Signal* signal);
+ void packLqhkeyreqLab(Signal* signal);
+ void sendNdbSttorryLab(Signal* signal);
+ void execSrCompletedLab(Signal* signal);
+ void execLogRecord(Signal* signal);
+ void srPhase3Comp(Signal* signal);
+ void srLogLimits(Signal* signal);
+ void srGciLimits(Signal* signal);
+ void srPhase3Start(Signal* signal);
+ void warningHandlerLab(Signal* signal);
+ void checkStartCompletedLab(Signal* signal);
+ void continueAbortLab(Signal* signal);
+ void abortContinueAfterBlockedLab(Signal* signal, bool canBlock);
+ void abortCommonLab(Signal* signal);
+ void localCommitLab(Signal* signal);
+ void abortErrorLab(Signal* signal);
+ void continueAfterReceivingAllAiLab(Signal* signal);
+ void abortStateHandlerLab(Signal* signal);
+ void writeAttrinfoLab(Signal* signal);
+ void scanAttrinfoLab(Signal* signal, Uint32* dataPtr, Uint32 length);
+ void abort_scan(Signal* signal, Uint32 scan_ptr_i, Uint32 errcode);
+ void localAbortStateHandlerLab(Signal* signal);
+ void logLqhkeyreqLab(Signal* signal);
+ void lqhAttrinfoLab(Signal* signal, Uint32* dataPtr, Uint32 length);
+ void rwConcludedAiLab(Signal* signal);
+ void aiStateErrorCheckLab(Signal* signal, Uint32* dataPtr, Uint32 length);
+ void takeOverErrorLab(Signal* signal);
+ void endgettupkeyLab(Signal* signal);
+ void noFreeRecordLab(Signal* signal,
+ const class LqhKeyReq * lqhKeyReq,
+ Uint32 errorCode);
+ void logLqhkeyrefLab(Signal* signal);
+ void closeCopyLab(Signal* signal);
+ void commitReplyLab(Signal* signal);
+ void completeUnusualLab(Signal* signal);
+ void completeTransNotLastLab(Signal* signal);
+ void completedLab(Signal* signal);
+ void copyCompletedLab(Signal* signal);
+ void completeLcpRoundLab(Signal* signal);
+ void continueAfterLogAbortWriteLab(Signal* signal);
+ void sendAttrinfoLab(Signal* signal);
+ void sendExecConf(Signal* signal);
+ void execSr(Signal* signal);
+ void srFourthComp(Signal* signal);
+ void timeSup(Signal* signal);
+ void closeCopyRequestLab(Signal* signal);
+ void closeScanRequestLab(Signal* signal);
+ void scanTcConnectLab(Signal* signal, Uint32 startTcCon, Uint32 fragId);
+ void initGcpRecLab(Signal* signal);
+ void prepareContinueAfterBlockedLab(Signal* signal);
+ void commitContinueAfterBlockedLab(Signal* signal);
+ void continueCopyAfterBlockedLab(Signal* signal);
+ void continueFirstCopyAfterBlockedLab(Signal* signal);
+ void continueFirstScanAfterBlockedLab(Signal* signal);
+ void continueScanAfterBlockedLab(Signal* signal);
+ void continueScanReleaseAfterBlockedLab(Signal* signal);
+ void continueCloseScanAfterBlockedLab(Signal* signal);
+ void continueCloseCopyAfterBlockedLab(Signal* signal);
+ void sendExecFragRefLab(Signal* signal);
+ void fragrefLab(Signal* signal, BlockReference retRef,
+ Uint32 retPtr, Uint32 errorCode);
+ void abortAddFragOps(Signal* signal);
+ void rwConcludedLab(Signal* signal);
+ void sendsttorryLab(Signal* signal);
+ void initialiseRecordsLab(Signal* signal, Uint32 data, Uint32, Uint32);
+ void startphase2Lab(Signal* signal, Uint32 config);
+ void startphase3Lab(Signal* signal);
+ void startphase4Lab(Signal* signal);
+ void startphase6Lab(Signal* signal);
+ void moreconnectionsLab(Signal* signal);
+ void scanReleaseLocksLab(Signal* signal);
+ void closeScanLab(Signal* signal);
+ void nextScanConfLoopLab(Signal* signal);
+ void scanNextLoopLab(Signal* signal);
+ void commitReqLab(Signal* signal, Uint32 gci);
+ void completeTransLastLab(Signal* signal);
+ void tupScanCloseConfLab(Signal* signal);
+ void tupCopyCloseConfLab(Signal* signal);
+ void accScanCloseConfLab(Signal* signal);
+ void accCopyCloseConfLab(Signal* signal);
+ void nextScanConfScanLab(Signal* signal);
+ void nextScanConfCopyLab(Signal* signal);
+ void continueScanNextReqLab(Signal* signal);
+ void keyinfoLab(const Uint32 * src, const Uint32 * end);
+ void copySendTupkeyReqLab(Signal* signal);
+ void storedProcConfScanLab(Signal* signal);
+ void storedProcConfCopyLab(Signal* signal);
+ void copyStateFinishedLab(Signal* signal);
+ void lcpCompletedLab(Signal* signal);
+ void lcpStartedLab(Signal* signal);
+ void contChkpNextFragLab(Signal* signal);
+ void startLcpRoundLab(Signal* signal);
+ void startFragRefLab(Signal* signal);
+ void srCompletedLab(Signal* signal);
+ void openFileInitLab(Signal* signal);
+ void openSrFrontpageLab(Signal* signal);
+ void openSrLastFileLab(Signal* signal);
+ void openSrNextFileLab(Signal* signal);
+ void openExecSrStartLab(Signal* signal);
+ void openExecSrNewMbyteLab(Signal* signal);
+ void openSrFourthPhaseLab(Signal* signal);
+ void openSrFourthZeroSkipInitLab(Signal* signal);
+ void openSrFourthZeroLab(Signal* signal);
+ void openExecLogLab(Signal* signal);
+ void checkInitCompletedLab(Signal* signal);
+ void closingSrLab(Signal* signal);
+ void closeExecSrLab(Signal* signal);
+ void execLogComp(Signal* signal);
+ void closeWriteLogLab(Signal* signal);
+ void closeExecLogLab(Signal* signal);
+ void writePageZeroLab(Signal* signal);
+ void lastWriteInFileLab(Signal* signal);
+ void initWriteEndLab(Signal* signal);
+ void initFirstPageLab(Signal* signal);
+ void writeGciZeroLab(Signal* signal);
+ void writeDirtyLab(Signal* signal);
+ void writeInitMbyteLab(Signal* signal);
+ void writeLogfileLab(Signal* signal);
+ void firstPageWriteLab(Signal* signal);
+ void readSrLastMbyteLab(Signal* signal);
+ void readSrLastFileLab(Signal* signal);
+ void readSrNextFileLab(Signal* signal);
+ void readExecSrLab(Signal* signal);
+ void readExecLogLab(Signal* signal);
+ void readSrFourthPhaseLab(Signal* signal);
+ void readSrFourthZeroLab(Signal* signal);
+ void copyLqhKeyRefLab(Signal* signal);
+ void restartOperationsLab(Signal* signal);
+ void lqhTransNextLab(Signal* signal);
+ void restartOperationsAfterStopLab(Signal* signal);
+ void sttorStartphase1Lab(Signal* signal);
+ void startphase1Lab(Signal* signal, Uint32 config, Uint32 nodeId);
+ void tupkeyConfLab(Signal* signal);
+ void copyTupkeyConfLab(Signal* signal);
+ void scanTupkeyConfLab(Signal* signal);
+ void scanTupkeyRefLab(Signal* signal);
+ void accScanConfScanLab(Signal* signal);
+ void accScanConfCopyLab(Signal* signal);
+ void scanLockReleasedLab(Signal* signal);
+ void openSrFourthNextLab(Signal* signal);
+ void closingInitLab(Signal* signal);
+ void closeExecSrCompletedLab(Signal* signal);
+ void readSrFrontpageLab(Signal* signal);
+
+ void sendAddFragReq(Signal* signal);
+ void sendAddAttrReq(Signal* signal);
+ void checkDropTab(Signal*);
+ Uint32 checkDropTabState(Tablerec::TableStatus, Uint32) const;
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ Dbtup* c_tup;
+ Uint32 readPrimaryKeys(ScanRecord*, TcConnectionrec*, Uint32 * dst);
+// ----------------------------------------------------------------
+// These are variables handling the records. For most records one
+// pointer to the array of structs, one pointer-struct, a file size
+// and a first free record variable. The pointer struct are temporary
+// variables that are kept on the class object since there are often a
+// great deal of those variables that exist simultaneously and
+// thus no perfect solution of handling them is currently available.
+// ----------------------------------------------------------------
+/* ------------------------------------------------------------------------- */
+/* POSITIONS WITHIN THE ATTRINBUF AND THE MAX SIZE OF DATA WITHIN AN */
+/* ATTRINBUF. */
+/* ------------------------------------------------------------------------- */
+
+
+#define ZADDFRAGREC_FILE_SIZE 1
+ AddFragRecord *addFragRecord;
+ AddFragRecordPtr addfragptr;
+ UintR cfirstfreeAddfragrec;
+ UintR caddfragrecFileSize;
+
+#define ZATTRINBUF_FILE_SIZE 12288 // 1.5 MByte
+#define ZINBUF_DATA_LEN 24 /* POSITION OF 'DATA LENGHT'-VARIABLE. */
+#define ZINBUF_NEXT 25 /* POSITION OF 'NEXT'-VARIABLE. */
+ Attrbuf *attrbuf;
+ AttrbufPtr attrinbufptr;
+ UintR cfirstfreeAttrinbuf;
+ UintR cattrinbufFileSize;
+ Uint32 c_no_attrinbuf_recs;
+
+#define ZDATABUF_FILE_SIZE 10000 // 200 kByte
+ Databuf *databuf;
+ DatabufPtr databufptr;
+ UintR cfirstfreeDatabuf;
+ UintR cdatabufFileSize;
+
+// Configurable
+ Fragrecord *fragrecord;
+ FragrecordPtr fragptr;
+ UintR cfirstfreeFragrec;
+ UintR cfragrecFileSize;
+
+#define ZGCPREC_FILE_SIZE 1
+ GcpRecord *gcpRecord;
+ GcpRecordPtr gcpPtr;
+ UintR cgcprecFileSize;
+
+// MAX_NDB_NODES is the size of this array
+ HostRecord *hostRecord;
+ UintR chostFileSize;
+
+#define ZNO_CONCURRENT_LCP 1
+ LcpRecord *lcpRecord;
+ LcpRecordPtr lcpPtr;
+ UintR cfirstfreeLcpLoc;
+ UintR clcpFileSize;
+
+#define ZLCP_LOCREC_FILE_SIZE 4
+ LcpLocRecord *lcpLocRecord;
+ LcpLocRecordPtr lcpLocptr;
+ UintR clcpLocrecFileSize;
+
+#define ZLOG_PART_FILE_SIZE 4
+ LogPartRecord *logPartRecord;
+ LogPartRecordPtr logPartPtr;
+ UintR clogPartFileSize;
+
+// Configurable
+ LogFileRecord *logFileRecord;
+ LogFileRecordPtr logFilePtr;
+ UintR cfirstfreeLogFile;
+ UintR clogFileFileSize;
+
+#define ZLFO_FILE_SIZE 256 /* MAX 256 OUTSTANDING FILE OPERATIONS */
+ LogFileOperationRecord *logFileOperationRecord;
+ LogFileOperationRecordPtr lfoPtr;
+ UintR cfirstfreeLfo;
+ UintR clfoFileSize;
+
+ LogPageRecord *logPageRecord;
+ LogPageRecordPtr logPagePtr;
+ UintR cfirstfreeLogPage;
+ UintR clogPageFileSize;
+
+#define ZPAGE_REF_FILE_SIZE 20
+ PageRefRecord *pageRefRecord;
+ PageRefRecordPtr pageRefPtr;
+ UintR cfirstfreePageRef;
+ UintR cpageRefFileSize;
+
+#define ZSCANREC_FILE_SIZE 100
+ ArrayPool<ScanRecord> c_scanRecordPool;
+ ScanRecordPtr scanptr;
+ UintR cscanNoFreeRec;
+ Uint32 cscanrecFileSize;
+
+// Configurable
+ Tablerec *tablerec;
+ TablerecPtr tabptr;
+ UintR ctabrecFileSize;
+
+// Configurable
+ TcConnectionrec *tcConnectionrec;
+ TcConnectionrecPtr tcConnectptr;
+ UintR cfirstfreeTcConrec;
+ UintR ctcConnectrecFileSize;
+
+// MAX_NDB_NODES is the size of this array
+ TcNodeFailRecord *tcNodeFailRecord;
+ TcNodeFailRecordPtr tcNodeFailptr;
+ UintR ctcNodeFailrecFileSize;
+
+ Uint16 terrorCode;
+
+ Uint32 c_firstInNodeGroup;
+
+// ------------------------------------------------------------------------
+// These variables are used to store block state which do not need arrays
+// of struct's.
+// ------------------------------------------------------------------------
+ Uint32 c_lcpId;
+ Uint32 cnoOfFragsCheckpointed;
+
+/* ------------------------------------------------------------------------- */
+// cmaxWordsAtNodeRec keeps track of how many words that currently are
+// outstanding in a node recovery situation.
+// cbookedAccOps keeps track of how many operation records that have been
+// booked in ACC for the scan processes.
+// cmaxAccOps contains the maximum number of operation records which can be
+// allocated for scan purposes in ACC.
+/* ------------------------------------------------------------------------- */
+ UintR cmaxWordsAtNodeRec;
+ UintR cbookedAccOps;
+ UintR cmaxAccOps;
+/* ------------------------------------------------------------------------- */
+/*THIS STATE VARIABLE IS ZTRUE IF AN ADD NODE IS ONGOING. ADD NODE MEANS */
+/*THAT CONNECTIONS ARE SET-UP TO THE NEW NODE. */
+/* ------------------------------------------------------------------------- */
+ Uint8 caddNodeState;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE SPECIFIES WHICH TYPE OF RESTART THAT IS ONGOING */
+/* ------------------------------------------------------------------------- */
+ Uint16 cstartType;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE INDICATES WHETHER AN INITIAL RESTART IS ONGOING OR NOT. */
+/* ------------------------------------------------------------------------- */
+ Uint8 cinitialStartOngoing;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE KEEPS TRACK OF WHEN TUP AND ACC HAVE COMPLETED EXECUTING */
+/*THEIR UNDO LOG. */
+/* ------------------------------------------------------------------------- */
+ ExecUndoLogState csrExecUndoLogState;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE KEEPS TRACK OF WHEN TUP AND ACC HAVE CONFIRMED COMPLETION */
+/*OF A LOCAL CHECKPOINT ROUND. */
+/* ------------------------------------------------------------------------- */
+ LcpCloseState clcpCompletedState;
+/* ------------------------------------------------------------------------- */
+/*DURING CONNECTION PROCESSES IN SYSTEM RESTART THESE VARIABLES KEEP TRACK */
+/*OF HOW MANY CONNECTIONS AND RELEASES THAT ARE TO BE PERFORMED. */
+/* ------------------------------------------------------------------------- */
+/***************************************************************************>*/
+/*THESE VARIABLES CONTAIN INFORMATION USED DURING SYSTEM RESTART. */
+/***************************************************************************>*/
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE IS ZTRUE IF THE SIGNAL START_REC_REQ HAVE BEEN RECEIVED. */
+/*RECEPTION OF THIS SIGNAL INDICATES THAT ALL FRAGMENTS THAT THIS NODE */
+/*SHOULD START HAVE BEEN RECEIVED. */
+/* ------------------------------------------------------------------------- */
+ Uint8 cstartRecReq;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE KEEPS TRACK OF HOW MANY FRAGMENTS THAT PARTICIPATE IN */
+/*EXECUTING THE LOG. IF ZERO WE DON'T NEED TO EXECUTE THE LOG AT ALL. */
+/* ------------------------------------------------------------------------- */
+ UintR cnoFragmentsExecSr;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE KEEPS TRACK OF WHICH OF THE FIRST TWO RESTART PHASES THAT */
+/*HAVE COMPLETED. */
+/* ------------------------------------------------------------------------- */
+ Uint8 csrPhaseStarted;
+/* ------------------------------------------------------------------------- */
+/*NUMBER OF PHASES COMPLETED OF EXECUTING THE FRAGMENT LOG. */
+/* ------------------------------------------------------------------------- */
+ Uint8 csrPhasesCompleted;
+/* ------------------------------------------------------------------------- */
+/*THE BLOCK REFERENCE OF THE MASTER DIH DURING SYSTEM RESTART. */
+/* ------------------------------------------------------------------------- */
+ BlockReference cmasterDihBlockref;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE IS THE HEAD OF A LINKED LIST OF FRAGMENTS WAITING TO BE */
+/*RESTORED FROM DISK. */
+/* ------------------------------------------------------------------------- */
+ UintR cfirstWaitFragSr;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE IS THE HEAD OF A LINKED LIST OF FRAGMENTS THAT HAVE BEEN */
+/*RESTORED FROM DISK THAT AWAITS EXECUTION OF THE FRAGMENT LOG. */
+/* ------------------------------------------------------------------------- */
+ UintR cfirstCompletedFragSr;
+
+ /**
+ * List of fragment that the log execution is completed for
+ */
+ Uint32 c_redo_log_complete_frags;
+
+/* ------------------------------------------------------------------------- */
+/*USED DURING SYSTEM RESTART, INDICATES THE OLDEST GCI THAT CAN BE RESTARTED */
+/*FROM AFTER THIS SYSTEM RESTART. USED TO FIND THE LOG TAIL. */
+/* ------------------------------------------------------------------------- */
+ UintR crestartOldestGci;
+/* ------------------------------------------------------------------------- */
+/*USED DURING SYSTEM RESTART, INDICATES THE NEWEST GCI THAT CAN BE RESTARTED */
+/*AFTER THIS SYSTEM RESTART. USED TO FIND THE LOG HEAD. */
+/* ------------------------------------------------------------------------- */
+ UintR crestartNewestGci;
+/* ------------------------------------------------------------------------- */
+/*THE NUMBER OF LOG FILES. SET AS A PARAMETER WHEN NDB IS STARTED. */
+/* ------------------------------------------------------------------------- */
+ UintR cnoLogFiles;
+/* ------------------------------------------------------------------------- */
+/*THESE TWO VARIABLES CONTAIN THE NEWEST GCI RECEIVED IN THE BLOCK AND THE */
+/*NEWEST COMPLETED GCI IN THE BLOCK. */
+/* ------------------------------------------------------------------------- */
+ UintR cnewestGci;
+ UintR cnewestCompletedGci;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE ONLY PASSES INFORMATION FROM STTOR TO STTORRY = TEMPORARY */
+/* ------------------------------------------------------------------------- */
+ Uint16 csignalKey;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE CONTAINS THE CURRENT START PHASE IN THE BLOCK. IS ZNIL IF */
+/*NO SYSTEM RESTART IS ONGOING. */
+/* ------------------------------------------------------------------------- */
+ Uint16 cstartPhase;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE CONTAIN THE CURRENT GLOBAL CHECKPOINT RECORD. IT'S RNIL IF */
+/*NOT A GCP SAVE IS ONGOING. */
+/* ------------------------------------------------------------------------- */
+ UintR ccurrentGcprec;
+/* ------------------------------------------------------------------------- */
+/*THESE VARIABLES ARE USED TO KEEP TRACK OF ALL ACTIVE COPY FRAGMENTS IN LQH.*/
+/* ------------------------------------------------------------------------- */
+ Uint8 cnoActiveCopy;
+ UintR cactiveCopy[4];
+
+/* ------------------------------------------------------------------------- */
+/*THESE VARIABLES CONTAIN THE BLOCK REFERENCES OF THE OTHER NDB BLOCKS. */
+/*ALSO THE BLOCK REFERENCE OF MY OWN BLOCK = LQH */
+/* ------------------------------------------------------------------------- */
+ BlockReference caccBlockref;
+ BlockReference ctupBlockref;
+ BlockReference ctuxBlockref;
+ BlockReference cownref;
+ UintR cLqhTimeOutCount;
+ UintR cLqhTimeOutCheckCount;
+ UintR cnoOfLogPages;
+ bool caccCommitBlocked;
+ bool ctupCommitBlocked;
+ bool cCommitBlocked;
+ UintR cCounterAccCommitBlocked;
+ UintR cCounterTupCommitBlocked;
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE CONTAINS MY OWN PROCESSOR ID. */
+/* ------------------------------------------------------------------------- */
+ NodeId cownNodeid;
+
+/* ------------------------------------------------------------------------- */
+/*THESE VARIABLES CONTAIN INFORMATION ABOUT THE OTHER NODES IN THE SYSTEM */
+/*THESE VARIABLES ARE MOSTLY USED AT SYSTEM RESTART AND ADD NODE TO SET-UP */
+/*AND RELEASE CONNECTIONS TO OTHER NODES IN THE CLUSTER. */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/*THIS ARRAY CONTAINS THE PROCESSOR ID'S OF THE NODES THAT ARE ALIVE. */
+/*CNO_OF_NODES SPECIFIES HOW MANY NODES THAT ARE CURRENTLY ALIVE. */
+/*CNODE_VERSION SPECIFIES THE NDB VERSION EXECUTING ON THE NODE. */
+/* ------------------------------------------------------------------------- */
+ UintR cpackedListIndex;
+ Uint16 cpackedList[MAX_NDB_NODES];
+ UintR cnodeData[MAX_NDB_NODES];
+ UintR cnodeStatus[MAX_NDB_NODES];
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE INDICATES WHETHER A CERTAIN NODE HAS SENT ALL FRAGMENTS THAT */
+/*NEED TO HAVE THE LOG EXECUTED. */
+/* ------------------------------------------------------------------------- */
+ Uint8 cnodeSrState[MAX_NDB_NODES];
+/* ------------------------------------------------------------------------- */
+/*THIS VARIABLE INDICATES WHETHER A CERTAIN NODE HAVE EXECUTED THE LOG */
+/* ------------------------------------------------------------------------- */
+ Uint8 cnodeExecSrState[MAX_NDB_NODES];
+ UintR cnoOfNodes;
+
+/* ------------------------------------------------------------------------- */
+/* THIS VARIABLE CONTAINS THE DIRECTORY OF A HASH TABLE OF ALL ACTIVE */
+/* OPERATION IN THE BLOCK. IT IS USED TO BE ABLE TO QUICKLY ABORT AN */
+/* OPERATION WHERE THE CONNECTION WAS LOST DUE TO NODE FAILURES. IT IS */
+/* ACTUALLY USED FOR ALL ABORTS COMMANDED BY TC. */
+/* ------------------------------------------------------------------------- */
+ UintR preComputedRequestInfoMask;
+ UintR ctransidHash[1024];
+
+ Uint32 c_diskless;
+
+public:
+ /**
+ *
+ */
+ struct CommitAckMarker {
+ Uint32 transid1;
+ Uint32 transid2;
+
+ Uint32 apiRef; // Api block ref
+ Uint32 apiOprec; // Connection Object in NDB API
+ Uint32 tcNodeId;
+ union { Uint32 nextPool; Uint32 nextHash; };
+ Uint32 prevHash;
+
+ inline bool equal(const CommitAckMarker & p) const {
+ return ((p.transid1 == transid1) && (p.transid2 == transid2));
+ }
+
+ inline Uint32 hashValue() const {
+ return transid1;
+ }
+ };
+
+ typedef Ptr<CommitAckMarker> CommitAckMarkerPtr;
+ ArrayPool<CommitAckMarker> m_commitAckMarkerPool;
+ DLHashTable<CommitAckMarker> m_commitAckMarkerHash;
+ typedef DLHashTable<CommitAckMarker>::Iterator CommitAckMarkerIterator;
+ void execREMOVE_MARKER_ORD(Signal* signal);
+ void scanMarkers(Signal* signal, Uint32 tcNodeFail, Uint32 bucket, Uint32 i);
+
+ struct Counters {
+ Uint32 operations;
+
+ inline void clear(){
+ operations = 0;
+ }
+ };
+
+ Counters c_Counters;
+
+ inline bool getAllowRead() const {
+ return getNodeState().startLevel < NodeState::SL_STOPPING_3;
+ }
+
+ DLHashTable<ScanRecord> c_scanTakeOverHash;
+};
+
+inline
+bool
+Dblqh::ScanRecord::check_scan_batch_completed() const
+{
+ Uint32 max_rows = m_max_batch_size_rows;
+ Uint32 max_bytes = m_max_batch_size_bytes;
+
+ return (max_rows > 0 && (m_curr_batch_size_rows >= max_rows)) ||
+ (max_bytes > 0 && (m_curr_batch_size_bytes >= max_bytes));
+}
+
+inline
+void
+Dblqh::i_get_acc_ptr(ScanRecord* scanP, Uint32* &acc_ptr, Uint32 index)
+{
+ if (index == 0) {
+ acc_ptr= (Uint32*)&scanP->scan_acc_op_ptr[0];
+ } else {
+ Uint32 attr_buf_index, attr_buf_rec;
+
+ AttrbufPtr regAttrPtr;
+ jam();
+ attr_buf_rec= (index + 31) / 32;
+ attr_buf_index= (index - 1) & 31;
+ regAttrPtr.i= scanP->scan_acc_op_ptr[attr_buf_rec];
+ ptrCheckGuard(regAttrPtr, cattrinbufFileSize, attrbuf);
+ acc_ptr= (Uint32*)&regAttrPtr.p->attrbuf[attr_buf_index];
+ }
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp b/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp
new file mode 100644
index 00000000000..e39d0ca68a6
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp
@@ -0,0 +1,455 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <pc.hpp>
+#define DBLQH_C
+#include "Dblqh.hpp"
+#include <ndb_limits.h>
+
+#define DEBUG(x) { ndbout << "LQH::" << x << endl; }
+
+void Dblqh::initData()
+{
+ caddfragrecFileSize = ZADDFRAGREC_FILE_SIZE;
+ cattrinbufFileSize = ZATTRINBUF_FILE_SIZE;
+ c_no_attrinbuf_recs= ZATTRINBUF_FILE_SIZE;
+ cdatabufFileSize = ZDATABUF_FILE_SIZE;
+ cfragrecFileSize = 0;
+ cgcprecFileSize = ZGCPREC_FILE_SIZE;
+ chostFileSize = MAX_NDB_NODES;
+ clcpFileSize = ZNO_CONCURRENT_LCP;
+ clcpLocrecFileSize = ZLCP_LOCREC_FILE_SIZE;
+ clfoFileSize = ZLFO_FILE_SIZE;
+ clogFileFileSize = 0;
+ clogPartFileSize = ZLOG_PART_FILE_SIZE;
+ cpageRefFileSize = ZPAGE_REF_FILE_SIZE;
+ cscanrecFileSize = ZSCANREC_FILE_SIZE;
+ ctabrecFileSize = 0;
+ ctcConnectrecFileSize = 0;
+ ctcNodeFailrecFileSize = MAX_NDB_NODES;
+
+ addFragRecord = 0;
+ attrbuf = 0;
+ databuf = 0;
+ fragrecord = 0;
+ gcpRecord = 0;
+ hostRecord = 0;
+ lcpRecord = 0;
+ lcpLocRecord = 0;
+ logPartRecord = 0;
+ logFileRecord = 0;
+ logFileOperationRecord = 0;
+ logPageRecord = 0;
+ pageRefRecord = 0;
+ tablerec = 0;
+ tcConnectionrec = 0;
+ tcNodeFailRecord = 0;
+
+ // Records with constant sizes
+
+ cLqhTimeOutCount = 0;
+ cLqhTimeOutCheckCount = 0;
+ cbookedAccOps = 0;
+ c_redo_log_complete_frags = RNIL;
+}//Dblqh::initData()
+
+void Dblqh::initRecords()
+{
+ // Records with dynamic sizes
+ addFragRecord = (AddFragRecord*)allocRecord("AddFragRecord",
+ sizeof(AddFragRecord),
+ caddfragrecFileSize);
+ attrbuf = (Attrbuf*)allocRecord("Attrbuf",
+ sizeof(Attrbuf),
+ cattrinbufFileSize);
+
+ databuf = (Databuf*)allocRecord("Databuf",
+ sizeof(Databuf),
+ cdatabufFileSize);
+
+ fragrecord = (Fragrecord*)allocRecord("Fragrecord",
+ sizeof(Fragrecord),
+ cfragrecFileSize);
+
+ gcpRecord = (GcpRecord*)allocRecord("GcpRecord",
+ sizeof(GcpRecord),
+ cgcprecFileSize);
+
+ hostRecord = (HostRecord*)allocRecord("HostRecord",
+ sizeof(HostRecord),
+ chostFileSize);
+
+ lcpRecord = (LcpRecord*)allocRecord("LcpRecord",
+ sizeof(LcpRecord),
+ clcpFileSize);
+
+ for(Uint32 i = 0; i<clcpFileSize; i++){
+ new (&lcpRecord[i])LcpRecord();
+ }
+
+ lcpLocRecord = (LcpLocRecord*)allocRecord("LcpLocRecord",
+ sizeof(LcpLocRecord),
+ clcpLocrecFileSize);
+
+ logPartRecord = (LogPartRecord*)allocRecord("LogPartRecord",
+ sizeof(LogPartRecord),
+ clogPartFileSize);
+
+ logFileRecord = (LogFileRecord*)allocRecord("LogFileRecord",
+ sizeof(LogFileRecord),
+ clogFileFileSize);
+
+ logFileOperationRecord = (LogFileOperationRecord*)
+ allocRecord("LogFileOperationRecord",
+ sizeof(LogFileOperationRecord),
+ clfoFileSize);
+
+ logPageRecord = (LogPageRecord*)allocRecord("LogPageRecord",
+ sizeof(LogPageRecord),
+ clogPageFileSize,
+ false);
+
+ pageRefRecord = (PageRefRecord*)allocRecord("PageRefRecord",
+ sizeof(PageRefRecord),
+ cpageRefFileSize);
+
+ cscanNoFreeRec = cscanrecFileSize;
+ c_scanRecordPool.setSize(cscanrecFileSize);
+ c_scanTakeOverHash.setSize(64);
+
+ tablerec = (Tablerec*)allocRecord("Tablerec",
+ sizeof(Tablerec),
+ ctabrecFileSize);
+
+ tcConnectionrec = (TcConnectionrec*)allocRecord("TcConnectionrec",
+ sizeof(TcConnectionrec),
+ ctcConnectrecFileSize);
+
+ m_commitAckMarkerPool.setSize(ctcConnectrecFileSize);
+ m_commitAckMarkerHash.setSize(1024);
+
+ tcNodeFailRecord = (TcNodeFailRecord*)allocRecord("TcNodeFailRecord",
+ sizeof(TcNodeFailRecord),
+ ctcNodeFailrecFileSize);
+
+ /*
+ ndbout << "FRAGREC SIZE = " << sizeof(Fragrecord) << endl;
+ ndbout << "TAB SIZE = " << sizeof(Tablerec) << endl;
+ ndbout << "GCP SIZE = " << sizeof(GcpRecord) << endl;
+ ndbout << "LCP SIZE = " << sizeof(LcpRecord) << endl;
+ ndbout << "LCPLOC SIZE = " << sizeof(LcpLocRecord) << endl;
+ ndbout << "LOGPART SIZE = " << sizeof(LogPartRecord) << endl;
+ ndbout << "LOGFILE SIZE = " << sizeof(LogFileRecord) << endl;
+ ndbout << "TC SIZE = " << sizeof(TcConnectionrec) << endl;
+ ndbout << "HOST SIZE = " << sizeof(HostRecord) << endl;
+ ndbout << "LFO SIZE = " << sizeof(LogFileOperationRecord) << endl;
+ ndbout << "PR SIZE = " << sizeof(PageRefRecord) << endl;
+ ndbout << "SCAN SIZE = " << sizeof(ScanRecord) << endl;
+*/
+
+ // Initialize BAT for interface to file system
+ NewVARIABLE* bat = allocateBat(2);
+ bat[1].WA = &logPageRecord->logPageWord[0];
+ bat[1].nrr = clogPageFileSize;
+ bat[1].ClusterSize = sizeof(LogPageRecord);
+ bat[1].bits.q = ZTWOLOG_PAGE_SIZE;
+ bat[1].bits.v = 5;
+}//Dblqh::initRecords()
+
+Dblqh::Dblqh(const class Configuration & conf):
+ SimulatedBlock(DBLQH, conf),
+ m_commitAckMarkerHash(m_commitAckMarkerPool),
+ c_scanTakeOverHash(c_scanRecordPool)
+{
+ Uint32 log_page_size= 0;
+ BLOCK_CONSTRUCTOR(Dblqh);
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_REDO_BUFFER,
+ &log_page_size);
+
+ /**
+ * Always set page size in half MBytes
+ */
+ clogPageFileSize= (log_page_size / sizeof(LogPageRecord));
+ Uint32 mega_byte_part= clogPageFileSize & 15;
+ if (mega_byte_part != 0) {
+ jam();
+ clogPageFileSize+= (16 - mega_byte_part);
+ }
+
+ addRecSignal(GSN_PACKED_SIGNAL, &Dblqh::execPACKED_SIGNAL);
+ addRecSignal(GSN_DEBUG_SIG, &Dblqh::execDEBUG_SIG);
+ addRecSignal(GSN_ATTRINFO, &Dblqh::execATTRINFO);
+ addRecSignal(GSN_KEYINFO, &Dblqh::execKEYINFO);
+ addRecSignal(GSN_LQHKEYREQ, &Dblqh::execLQHKEYREQ);
+ addRecSignal(GSN_LQHKEYREF, &Dblqh::execLQHKEYREF);
+ addRecSignal(GSN_COMMIT, &Dblqh::execCOMMIT);
+ addRecSignal(GSN_COMPLETE, &Dblqh::execCOMPLETE);
+ addRecSignal(GSN_LQHKEYCONF, &Dblqh::execLQHKEYCONF);
+#ifdef VM_TRACE
+ addRecSignal(GSN_TESTSIG, &Dblqh::execTESTSIG);
+#endif
+ addRecSignal(GSN_LQH_RESTART_OP, &Dblqh::execLQH_RESTART_OP);
+ addRecSignal(GSN_CONTINUEB, &Dblqh::execCONTINUEB);
+ addRecSignal(GSN_START_RECREQ, &Dblqh::execSTART_RECREQ);
+ addRecSignal(GSN_START_RECCONF, &Dblqh::execSTART_RECCONF);
+ addRecSignal(GSN_EXEC_FRAGREQ, &Dblqh::execEXEC_FRAGREQ);
+ addRecSignal(GSN_EXEC_FRAGCONF, &Dblqh::execEXEC_FRAGCONF);
+ addRecSignal(GSN_EXEC_FRAGREF, &Dblqh::execEXEC_FRAGREF);
+ addRecSignal(GSN_START_EXEC_SR, &Dblqh::execSTART_EXEC_SR);
+ addRecSignal(GSN_EXEC_SRREQ, &Dblqh::execEXEC_SRREQ);
+ addRecSignal(GSN_EXEC_SRCONF, &Dblqh::execEXEC_SRCONF);
+ addRecSignal(GSN_SCAN_HBREP, &Dblqh::execSCAN_HBREP);
+
+ addRecSignal(GSN_ALTER_TAB_REQ, &Dblqh::execALTER_TAB_REQ);
+
+ // Trigger signals, transit to from TUP
+ addRecSignal(GSN_CREATE_TRIG_REQ, &Dblqh::execCREATE_TRIG_REQ);
+ addRecSignal(GSN_CREATE_TRIG_CONF, &Dblqh::execCREATE_TRIG_CONF);
+ addRecSignal(GSN_CREATE_TRIG_REF, &Dblqh::execCREATE_TRIG_REF);
+
+ addRecSignal(GSN_DROP_TRIG_REQ, &Dblqh::execDROP_TRIG_REQ);
+ addRecSignal(GSN_DROP_TRIG_CONF, &Dblqh::execDROP_TRIG_CONF);
+ addRecSignal(GSN_DROP_TRIG_REF, &Dblqh::execDROP_TRIG_REF);
+
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dblqh::execDUMP_STATE_ORD);
+ addRecSignal(GSN_ACC_COM_BLOCK, &Dblqh::execACC_COM_BLOCK);
+ addRecSignal(GSN_ACC_COM_UNBLOCK, &Dblqh::execACC_COM_UNBLOCK);
+ addRecSignal(GSN_TUP_COM_BLOCK, &Dblqh::execTUP_COM_BLOCK);
+ addRecSignal(GSN_TUP_COM_UNBLOCK, &Dblqh::execTUP_COM_UNBLOCK);
+ addRecSignal(GSN_NODE_FAILREP, &Dblqh::execNODE_FAILREP);
+ addRecSignal(GSN_CHECK_LCP_STOP, &Dblqh::execCHECK_LCP_STOP);
+ addRecSignal(GSN_SEND_PACKED, &Dblqh::execSEND_PACKED);
+ addRecSignal(GSN_TUP_ATTRINFO, &Dblqh::execTUP_ATTRINFO);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dblqh::execREAD_CONFIG_REQ, true);
+ addRecSignal(GSN_LQHFRAGREQ, &Dblqh::execLQHFRAGREQ);
+ addRecSignal(GSN_LQHADDATTREQ, &Dblqh::execLQHADDATTREQ);
+ addRecSignal(GSN_TUP_ADD_ATTCONF, &Dblqh::execTUP_ADD_ATTCONF);
+ addRecSignal(GSN_TUP_ADD_ATTRREF, &Dblqh::execTUP_ADD_ATTRREF);
+ addRecSignal(GSN_ACCFRAGCONF, &Dblqh::execACCFRAGCONF);
+ addRecSignal(GSN_ACCFRAGREF, &Dblqh::execACCFRAGREF);
+ addRecSignal(GSN_TUPFRAGCONF, &Dblqh::execTUPFRAGCONF);
+ addRecSignal(GSN_TUPFRAGREF, &Dblqh::execTUPFRAGREF);
+ addRecSignal(GSN_TAB_COMMITREQ, &Dblqh::execTAB_COMMITREQ);
+ addRecSignal(GSN_ACCSEIZECONF, &Dblqh::execACCSEIZECONF);
+ addRecSignal(GSN_ACCSEIZEREF, &Dblqh::execACCSEIZEREF);
+ addRecSignal(GSN_READ_NODESCONF, &Dblqh::execREAD_NODESCONF);
+ addRecSignal(GSN_READ_NODESREF, &Dblqh::execREAD_NODESREF);
+ addRecSignal(GSN_STTOR, &Dblqh::execSTTOR);
+ addRecSignal(GSN_NDB_STTOR, &Dblqh::execNDB_STTOR);
+ addRecSignal(GSN_TUPSEIZECONF, &Dblqh::execTUPSEIZECONF);
+ addRecSignal(GSN_TUPSEIZEREF, &Dblqh::execTUPSEIZEREF);
+ addRecSignal(GSN_ACCKEYCONF, &Dblqh::execACCKEYCONF);
+ addRecSignal(GSN_ACCKEYREF, &Dblqh::execACCKEYREF);
+ addRecSignal(GSN_TUPKEYCONF, &Dblqh::execTUPKEYCONF);
+ addRecSignal(GSN_TUPKEYREF, &Dblqh::execTUPKEYREF);
+ addRecSignal(GSN_ABORT, &Dblqh::execABORT);
+ addRecSignal(GSN_ABORTREQ, &Dblqh::execABORTREQ);
+ addRecSignal(GSN_COMMITREQ, &Dblqh::execCOMMITREQ);
+ addRecSignal(GSN_COMPLETEREQ, &Dblqh::execCOMPLETEREQ);
+#ifdef VM_TRACE
+ addRecSignal(GSN_MEMCHECKREQ, &Dblqh::execMEMCHECKREQ);
+#endif
+ addRecSignal(GSN_SCAN_FRAGREQ, &Dblqh::execSCAN_FRAGREQ);
+ addRecSignal(GSN_SCAN_NEXTREQ, &Dblqh::execSCAN_NEXTREQ);
+ addRecSignal(GSN_ACC_SCANCONF, &Dblqh::execACC_SCANCONF);
+ addRecSignal(GSN_ACC_SCANREF, &Dblqh::execACC_SCANREF);
+ addRecSignal(GSN_NEXT_SCANCONF, &Dblqh::execNEXT_SCANCONF);
+ addRecSignal(GSN_NEXT_SCANREF, &Dblqh::execNEXT_SCANREF);
+ addRecSignal(GSN_STORED_PROCCONF, &Dblqh::execSTORED_PROCCONF);
+ addRecSignal(GSN_STORED_PROCREF, &Dblqh::execSTORED_PROCREF);
+ addRecSignal(GSN_COPY_FRAGREQ, &Dblqh::execCOPY_FRAGREQ);
+ addRecSignal(GSN_COPY_ACTIVEREQ, &Dblqh::execCOPY_ACTIVEREQ);
+ addRecSignal(GSN_COPY_STATEREQ, &Dblqh::execCOPY_STATEREQ);
+ addRecSignal(GSN_LQH_TRANSREQ, &Dblqh::execLQH_TRANSREQ);
+ addRecSignal(GSN_TRANSID_AI, &Dblqh::execTRANSID_AI);
+ addRecSignal(GSN_INCL_NODEREQ, &Dblqh::execINCL_NODEREQ);
+ addRecSignal(GSN_ACC_LCPCONF, &Dblqh::execACC_LCPCONF);
+ addRecSignal(GSN_ACC_LCPREF, &Dblqh::execACC_LCPREF);
+ addRecSignal(GSN_ACC_LCPSTARTED, &Dblqh::execACC_LCPSTARTED);
+ addRecSignal(GSN_ACC_CONTOPCONF, &Dblqh::execACC_CONTOPCONF);
+ addRecSignal(GSN_LCP_FRAGIDCONF, &Dblqh::execLCP_FRAGIDCONF);
+ addRecSignal(GSN_LCP_FRAGIDREF, &Dblqh::execLCP_FRAGIDREF);
+ addRecSignal(GSN_LCP_HOLDOPCONF, &Dblqh::execLCP_HOLDOPCONF);
+ addRecSignal(GSN_LCP_HOLDOPREF, &Dblqh::execLCP_HOLDOPREF);
+ addRecSignal(GSN_TUP_PREPLCPCONF, &Dblqh::execTUP_PREPLCPCONF);
+ addRecSignal(GSN_TUP_PREPLCPREF, &Dblqh::execTUP_PREPLCPREF);
+ addRecSignal(GSN_TUP_LCPCONF, &Dblqh::execTUP_LCPCONF);
+ addRecSignal(GSN_TUP_LCPREF, &Dblqh::execTUP_LCPREF);
+ addRecSignal(GSN_TUP_LCPSTARTED, &Dblqh::execTUP_LCPSTARTED);
+ addRecSignal(GSN_END_LCPCONF, &Dblqh::execEND_LCPCONF);
+
+ addRecSignal(GSN_EMPTY_LCP_REQ, &Dblqh::execEMPTY_LCP_REQ);
+ addRecSignal(GSN_LCP_FRAG_ORD, &Dblqh::execLCP_FRAG_ORD);
+
+ addRecSignal(GSN_START_FRAGREQ, &Dblqh::execSTART_FRAGREQ);
+ addRecSignal(GSN_START_RECREF, &Dblqh::execSTART_RECREF);
+ addRecSignal(GSN_SR_FRAGIDCONF, &Dblqh::execSR_FRAGIDCONF);
+ addRecSignal(GSN_SR_FRAGIDREF, &Dblqh::execSR_FRAGIDREF);
+ addRecSignal(GSN_ACC_SRCONF, &Dblqh::execACC_SRCONF);
+ addRecSignal(GSN_ACC_SRREF, &Dblqh::execACC_SRREF);
+ addRecSignal(GSN_TUP_SRCONF, &Dblqh::execTUP_SRCONF);
+ addRecSignal(GSN_TUP_SRREF, &Dblqh::execTUP_SRREF);
+ addRecSignal(GSN_GCP_SAVEREQ, &Dblqh::execGCP_SAVEREQ);
+ addRecSignal(GSN_FSOPENCONF, &Dblqh::execFSOPENCONF);
+ addRecSignal(GSN_FSOPENREF, &Dblqh::execFSOPENREF);
+ addRecSignal(GSN_FSCLOSECONF, &Dblqh::execFSCLOSECONF);
+ addRecSignal(GSN_FSCLOSEREF, &Dblqh::execFSCLOSEREF);
+ addRecSignal(GSN_FSWRITECONF, &Dblqh::execFSWRITECONF);
+ addRecSignal(GSN_FSWRITEREF, &Dblqh::execFSWRITEREF);
+ addRecSignal(GSN_FSREADCONF, &Dblqh::execFSREADCONF);
+ addRecSignal(GSN_FSREADREF, &Dblqh::execFSREADREF);
+ addRecSignal(GSN_ACC_ABORTCONF, &Dblqh::execACC_ABORTCONF);
+ addRecSignal(GSN_SET_VAR_REQ, &Dblqh::execSET_VAR_REQ);
+ addRecSignal(GSN_TIME_SIGNAL, &Dblqh::execTIME_SIGNAL);
+ addRecSignal(GSN_FSSYNCCONF, &Dblqh::execFSSYNCCONF);
+ addRecSignal(GSN_FSSYNCREF, &Dblqh::execFSSYNCREF);
+ addRecSignal(GSN_REMOVE_MARKER_ORD, &Dblqh::execREMOVE_MARKER_ORD);
+
+ //addRecSignal(GSN_DROP_TAB_REQ, &Dblqh::execDROP_TAB_REQ);
+ addRecSignal(GSN_PREP_DROP_TAB_REQ, &Dblqh::execPREP_DROP_TAB_REQ);
+ addRecSignal(GSN_WAIT_DROP_TAB_REQ, &Dblqh::execWAIT_DROP_TAB_REQ);
+ addRecSignal(GSN_DROP_TAB_REQ, &Dblqh::execDROP_TAB_REQ);
+
+ addRecSignal(GSN_LQH_ALLOCREQ, &Dblqh::execLQH_ALLOCREQ);
+ addRecSignal(GSN_LQH_WRITELOG_REQ, &Dblqh::execLQH_WRITELOG_REQ);
+
+ // TUX
+ addRecSignal(GSN_TUXFRAGCONF, &Dblqh::execTUXFRAGCONF);
+ addRecSignal(GSN_TUXFRAGREF, &Dblqh::execTUXFRAGREF);
+ addRecSignal(GSN_TUX_ADD_ATTRCONF, &Dblqh::execTUX_ADD_ATTRCONF);
+ addRecSignal(GSN_TUX_ADD_ATTRREF, &Dblqh::execTUX_ADD_ATTRREF);
+
+ addRecSignal(GSN_READ_PSUEDO_REQ, &Dblqh::execREAD_PSUEDO_REQ);
+
+ initData();
+
+#ifdef VM_TRACE
+ {
+ void* tmp[] = {
+ &addfragptr,
+ &attrinbufptr,
+ &databufptr,
+ &fragptr,
+ &gcpPtr,
+ &lcpPtr,
+ &lcpLocptr,
+ &logPartPtr,
+ &logFilePtr,
+ &lfoPtr,
+ &logPagePtr,
+ &pageRefPtr,
+ &scanptr,
+ &tabptr,
+ &tcConnectptr,
+ &tcNodeFailptr,
+ };
+ init_globals_list(tmp, sizeof(tmp)/sizeof(tmp[0]));
+ }
+#endif
+
+}//Dblqh::Dblqh()
+
+Dblqh::~Dblqh()
+{
+ // Records with dynamic sizes
+ deallocRecord((void **)&addFragRecord, "AddFragRecord",
+ sizeof(AddFragRecord),
+ caddfragrecFileSize);
+
+ deallocRecord((void**)&attrbuf,
+ "Attrbuf",
+ sizeof(Attrbuf),
+ cattrinbufFileSize);
+
+ deallocRecord((void**)&databuf,
+ "Databuf",
+ sizeof(Databuf),
+ cdatabufFileSize);
+
+ deallocRecord((void**)&fragrecord,
+ "Fragrecord",
+ sizeof(Fragrecord),
+ cfragrecFileSize);
+
+ deallocRecord((void**)&gcpRecord,
+ "GcpRecord",
+ sizeof(GcpRecord),
+ cgcprecFileSize);
+
+ deallocRecord((void**)&hostRecord,
+ "HostRecord",
+ sizeof(HostRecord),
+ chostFileSize);
+
+ deallocRecord((void**)&lcpRecord,
+ "LcpRecord",
+ sizeof(LcpRecord),
+ clcpFileSize);
+
+ deallocRecord((void**)&lcpLocRecord,
+ "LcpLocRecord",
+ sizeof(LcpLocRecord),
+ clcpLocrecFileSize);
+
+ deallocRecord((void**)&logPartRecord,
+ "LogPartRecord",
+ sizeof(LogPartRecord),
+ clogPartFileSize);
+
+ deallocRecord((void**)&logFileRecord,
+ "LogFileRecord",
+ sizeof(LogFileRecord),
+ clogFileFileSize);
+
+ deallocRecord((void**)&logFileOperationRecord,
+ "LogFileOperationRecord",
+ sizeof(LogFileOperationRecord),
+ clfoFileSize);
+
+ deallocRecord((void**)&logPageRecord,
+ "LogPageRecord",
+ sizeof(LogPageRecord),
+ clogPageFileSize);
+
+ deallocRecord((void**)&pageRefRecord,
+ "PageRefRecord",
+ sizeof(PageRefRecord),
+ cpageRefFileSize);
+
+
+ deallocRecord((void**)&tablerec,
+ "Tablerec",
+ sizeof(Tablerec),
+ ctabrecFileSize);
+
+ deallocRecord((void**)&tcConnectionrec,
+ "TcConnectionrec",
+ sizeof(TcConnectionrec),
+ ctcConnectrecFileSize);
+
+ deallocRecord((void**)&tcNodeFailRecord,
+ "TcNodeFailRecord",
+ sizeof(TcNodeFailRecord),
+ ctcNodeFailrecFileSize);
+}//Dblqh::~Dblqh()
+
+BLOCK_FUNCTIONS(Dblqh)
+
diff --git a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
new file mode 100644
index 00000000000..c34d4ddb566
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
@@ -0,0 +1,18661 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBLQH_C
+#include "Dblqh.hpp"
+#include <ndb_limits.h>
+#include <md5_hash.hpp>
+
+#include <ndb_version.h>
+#include <signaldata/TuxBound.hpp>
+#include <signaldata/AccScan.hpp>
+#include <signaldata/CopyActive.hpp>
+#include <signaldata/CopyFrag.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/EmptyLcp.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/ExecFragReq.hpp>
+#include <signaldata/GCPSave.hpp>
+#include <signaldata/TcKeyRef.hpp>
+#include <signaldata/LqhKey.hpp>
+#include <signaldata/NextScan.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/RelTabMem.hpp>
+#include <signaldata/ScanFrag.hpp>
+#include <signaldata/SrFragidConf.hpp>
+#include <signaldata/StartFragReq.hpp>
+#include <signaldata/StartRec.hpp>
+#include <signaldata/TupKey.hpp>
+#include <signaldata/TupCommit.hpp>
+#include <signaldata/LqhFrag.hpp>
+#include <signaldata/AccFrag.hpp>
+#include <signaldata/TupFrag.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/PackedSignal.hpp>
+
+#include <signaldata/PrepDropTab.hpp>
+#include <signaldata/DropTab.hpp>
+
+#include <signaldata/AlterTab.hpp>
+
+#include <signaldata/LCP.hpp>
+
+// Use DEBUG to print messages that should be
+// seen only when we debug the product
+#ifdef VM_TRACE
+#define DEBUG(x) ndbout << "DBLQH: "<< x << endl;
+NdbOut &
+operator<<(NdbOut& out, Dblqh::TcConnectionrec::TransactionState state){
+ out << (int)state;
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut& out, Dblqh::TcConnectionrec::LogWriteState state){
+ out << (int)state;
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut& out, Dblqh::TcConnectionrec::ListState state){
+ out << (int)state;
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut& out, Dblqh::TcConnectionrec::AbortState state){
+ out << (int)state;
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut& out, Dblqh::ScanRecord::ScanState state){
+ out << (int)state;
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut& out, Dblqh::LogFileOperationRecord::LfoState state){
+ out << (int)state;
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut& out, Dblqh::ScanRecord::ScanType state){
+ out << (int)state;
+ return out;
+}
+
+#else
+#define DEBUG(x)
+#endif
+
+//#define MARKER_TRACE 1
+//#define TRACE_SCAN_TAKEOVER 1
+
+const Uint32 NR_ScanNo = 0;
+
+void Dblqh::execACC_COM_BLOCK(Signal* signal)
+{
+ jamEntry();
+/* ------------------------------------------------------------------------- */
+// Undo log buffer in ACC is in critical sector of being full.
+/* ------------------------------------------------------------------------- */
+ cCounterAccCommitBlocked++;
+ caccCommitBlocked = true;
+ cCommitBlocked = true;
+ return;
+}//Dblqh::execACC_COM_BLOCK()
+
+void Dblqh::execACC_COM_UNBLOCK(Signal* signal)
+{
+ jamEntry();
+/* ------------------------------------------------------------------------- */
+// Undo log buffer in ACC ok again.
+/* ------------------------------------------------------------------------- */
+ caccCommitBlocked = false;
+ if (ctupCommitBlocked == false) {
+ jam();
+ cCommitBlocked = false;
+ }//if
+ return;
+}//Dblqh::execACC_COM_UNBLOCK()
+
+void Dblqh::execTUP_COM_BLOCK(Signal* signal)
+{
+ jamEntry();
+/* ------------------------------------------------------------------------- */
+// Undo log buffer in TUP is in critical sector of being full.
+/* ------------------------------------------------------------------------- */
+ cCounterTupCommitBlocked++;
+ ctupCommitBlocked = true;
+ cCommitBlocked = true;
+ return;
+}//Dblqh::execTUP_COM_BLOCK()
+
+void Dblqh::execTUP_COM_UNBLOCK(Signal* signal)
+{
+ jamEntry();
+/* ------------------------------------------------------------------------- */
+// Undo log buffer in TUP ok again.
+/* ------------------------------------------------------------------------- */
+ ctupCommitBlocked = false;
+ if (caccCommitBlocked == false) {
+ jam();
+ cCommitBlocked = false;
+ }//if
+ return;
+}//Dblqh::execTUP_COM_UNBLOCK()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEND SYSTEM ERROR ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::systemError(Signal* signal)
+{
+ progError(0, 0);
+}//Dblqh::systemError()
+
+/* *************** */
+/* ACCSEIZEREF > */
+/* *************** */
+void Dblqh::execACCSEIZEREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execACCSEIZEREF()
+
+/* ******************************************************>> */
+/* THIS SIGNAL IS USED TO HANDLE REAL-TIME */
+/* BREAKS THAT ARE NECESSARY TO ENSURE REAL-TIME */
+/* OPERATION OF LQH. */
+/* This signal is also used for signal loops, for example */
+/* the timeout handling for writing logs every second. */
+/* ******************************************************>> */
+void Dblqh::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ Uint32 tcase = signal->theData[0];
+ Uint32 data0 = signal->theData[1];
+ Uint32 data1 = signal->theData[2];
+ Uint32 data2 = signal->theData[3];
+#if 0
+ if (tcase == RNIL) {
+ tcConnectptr.i = data0;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ ndbout << "State = " << tcConnectptr.p->transactionState;
+ ndbout << " seqNoReplica = " << tcConnectptr.p->seqNoReplica;
+ ndbout << " tcNodeFailrec = " << tcConnectptr.p->tcNodeFailrec;
+ ndbout << " activeCreat = " << tcConnectptr.p->activeCreat;
+ ndbout << endl;
+ ndbout << "tupkeyData0 = " << tcConnectptr.p->tupkeyData[0];
+ ndbout << "tupkeyData1 = " << tcConnectptr.p->tupkeyData[1];
+ ndbout << "tupkeyData2 = " << tcConnectptr.p->tupkeyData[2];
+ ndbout << "tupkeyData3 = " << tcConnectptr.p->tupkeyData[3];
+ ndbout << endl;
+ ndbout << "abortState = " << tcConnectptr.p->abortState;
+ ndbout << "listState = " << tcConnectptr.p->listState;
+ ndbout << endl;
+ return;
+ }//if
+#endif
+ switch (tcase) {
+ case ZLOG_LQHKEYREQ:
+ if (cnoOfLogPages == 0) {
+ jam();
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 2);
+ return;
+ }//if
+ logPartPtr.i = data0;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+
+ tcConnectptr.i = logPartPtr.p->firstLogQueue;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if ((cCommitBlocked == true) &&
+ (fragptr.p->fragActiveStatus == ZTRUE)) {
+ jam();
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 2);
+ return;
+ }//if
+ logPartPtr.p->LogLqhKeyReqSent = ZFALSE;
+ getFirstInLogQueue(signal);
+
+ switch (tcConnectptr.p->transactionState) {
+ case TcConnectionrec::LOG_QUEUED:
+ if (tcConnectptr.p->abortState != TcConnectionrec::ABORT_IDLE) {
+ jam();
+ logNextStart(signal);
+ abortCommonLab(signal);
+ return;
+ } else {
+ jam();
+/*------------------------------------------------------------*/
+/* WE MUST SET THE STATE OF THE LOG PART TO IDLE TO */
+/* ENSURE THAT WE ARE NOT QUEUED AGAIN ON THE LOG PART */
+/* WE WILL SET THE LOG PART STATE TO ACTIVE IMMEDIATELY */
+/* SO NO OTHER PROCESS WILL SEE THIS STATE. IT IS MERELY*/
+/* USED TO ENABLE REUSE OF CODE. */
+/*------------------------------------------------------------*/
+ if (logPartPtr.p->logPartState == LogPartRecord::ACTIVE) {
+ jam();
+ logPartPtr.p->logPartState = LogPartRecord::IDLE;
+ }//if
+ logLqhkeyreqLab(signal);
+ return;
+ }//if
+ break;
+ case TcConnectionrec::LOG_ABORT_QUEUED:
+ jam();
+ writeAbortLog(signal);
+ removeLogTcrec(signal);
+ logNextStart(signal);
+ continueAfterLogAbortWriteLab(signal);
+ return;
+ break;
+ case TcConnectionrec::LOG_COMMIT_QUEUED:
+ case TcConnectionrec::LOG_COMMIT_QUEUED_WAIT_SIGNAL:
+ jam();
+ writeCommitLog(signal, logPartPtr);
+ logNextStart(signal);
+ if (tcConnectptr.p->transactionState == TcConnectionrec::LOG_COMMIT_QUEUED) {
+ if (tcConnectptr.p->seqNoReplica != 0) {
+ jam();
+ commitReplyLab(signal);
+ } else {
+ jam();
+ localCommitLab(signal);
+ }//if
+ return;
+ } else {
+ jam();
+ tcConnectptr.p->transactionState = TcConnectionrec::LOG_COMMIT_WRITTEN_WAIT_SIGNAL;
+ return;
+ }//if
+ break;
+ case TcConnectionrec::COMMIT_QUEUED:
+ jam();
+ logNextStart(signal);
+ localCommitLab(signal);
+ break;
+ case TcConnectionrec::ABORT_QUEUED:
+ jam();
+ logNextStart(signal);
+ abortCommonLab(signal);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+ break;
+ case ZSR_GCI_LIMITS:
+ jam();
+ signal->theData[0] = data0;
+ srGciLimits(signal);
+ return;
+ break;
+ case ZSR_LOG_LIMITS:
+ jam();
+ signal->theData[0] = data0;
+ signal->theData[1] = data1;
+ signal->theData[2] = data2;
+ srLogLimits(signal);
+ return;
+ break;
+ case ZSEND_EXEC_CONF:
+ jam();
+ signal->theData[0] = data0;
+ sendExecConf(signal);
+ return;
+ break;
+ case ZEXEC_SR:
+ jam();
+ signal->theData[0] = data0;
+ execSr(signal);
+ return;
+ break;
+ case ZSR_FOURTH_COMP:
+ jam();
+ signal->theData[0] = data0;
+ srFourthComp(signal);
+ return;
+ break;
+ case ZINIT_FOURTH:
+ jam();
+ signal->theData[0] = data0;
+ initFourth(signal);
+ return;
+ break;
+ case ZTIME_SUPERVISION:
+ jam();
+ signal->theData[0] = data0;
+ timeSup(signal);
+ return;
+ break;
+ case ZSR_PHASE3_START:
+ jam();
+ signal->theData[0] = data0;
+ srPhase3Start(signal);
+ return;
+ break;
+ case ZLQH_TRANS_NEXT:
+ jam();
+ tcNodeFailptr.i = data0;
+ ptrCheckGuard(tcNodeFailptr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+ lqhTransNextLab(signal);
+ return;
+ break;
+ case ZSCAN_TC_CONNECT:
+ jam();
+ tabptr.i = data1;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ scanTcConnectLab(signal, data0, data2);
+ return;
+ break;
+ case ZINITIALISE_RECORDS:
+ jam();
+ initialiseRecordsLab(signal, data0, data2, signal->theData[4]);
+ return;
+ break;
+ case ZINIT_GCP_REC:
+ jam();
+ gcpPtr.i = 0;
+ ptrAss(gcpPtr, gcpRecord);
+ initGcpRecLab(signal);
+ return;
+ break;
+ case ZRESTART_OPERATIONS_AFTER_STOP:
+ jam();
+ tcConnectptr.i = data0;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ if (tcConnectptr.p->listState != TcConnectionrec::WAIT_QUEUE_LIST) {
+ jam();
+ return;
+ }//if
+ releaseWaitQueue(signal);
+ linkActiveFrag(signal);
+ restartOperationsAfterStopLab(signal);
+ return;
+ break;
+ case ZCHECK_LCP_STOP_BLOCKED:
+ jam();
+ c_scanRecordPool.getPtr(scanptr, data0);
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ checkLcpStopBlockedLab(signal);
+ return;
+ case ZSCAN_MARKERS:
+ jam();
+ scanMarkers(signal, data0, data1, data2);
+ return;
+ break;
+
+ case ZOPERATION_EVENT_REP:
+ jam();
+ /* --------------------------------------------------------------------- */
+ // Report information about transaction activity once per second.
+ /* --------------------------------------------------------------------- */
+ if (signal->theData[1] == 0) {
+ signal->theData[0] = NDB_LE_OperationReportCounters;
+ signal->theData[1] = c_Counters.operations;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+ }//if
+ c_Counters.clear();
+ signal->theData[0] = ZOPERATION_EVENT_REP;
+ signal->theData[1] = 0;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 5000, 2);
+ break;
+ case ZPREP_DROP_TABLE:
+ jam();
+ checkDropTab(signal);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dblqh::execCONTINUEB()
+
+/* *********************************************************> */
+/* Request from DBDIH to include a new node in the node list */
+/* and so forth. */
+/* *********************************************************> */
+void Dblqh::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+ BlockReference retRef = signal->theData[0];
+ Uint32 nodeId = signal->theData[1];
+ cnewestGci = signal->theData[2];
+ cnewestCompletedGci = signal->theData[2] - 1;
+ ndbrequire(cnoOfNodes < MAX_NDB_NODES);
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ if (cnodeData[i] == nodeId) {
+ jam();
+ cnodeStatus[i] = ZNODE_UP;
+ }//if
+ }//for
+ signal->theData[0] = cownref;
+ sendSignal(retRef, GSN_INCL_NODECONF, signal, 1, JBB);
+ return;
+}//Dblqh::execINCL_NODEREQ()
+
+void Dblqh::execTUPSEIZEREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execTUPSEIZEREF()
+
+/* ########################################################################## */
+/* ####### START / RESTART MODULE ####### */
+/* ########################################################################## */
+/* ************************************************************************>> */
+/* This is first signal that arrives in a start / restart. Sender is NDBCNTR_REF. */
+/* ************************************************************************>> */
+void Dblqh::execSTTOR(Signal* signal)
+{
+ UintR tstartPhase;
+
+ jamEntry();
+ /* START CASE */
+ tstartPhase = signal->theData[1];
+ /* SYSTEM RESTART RANK */
+ csignalKey = signal->theData[6];
+ switch (tstartPhase) {
+ case ZSTART_PHASE1:
+ jam();
+ cstartPhase = tstartPhase;
+ sttorStartphase1Lab(signal);
+ c_tup = (Dbtup*)globalData.getBlock(DBTUP);
+ ndbrequire(c_tup != 0);
+ return;
+ break;
+ default:
+ jam();
+ /*empty*/;
+ sendsttorryLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::execSTTOR()
+
+/* ***************************************> */
+/* Restart phases 1 - 6, sender is Ndbcntr */
+/* ***************************************> */
+void Dblqh::execNDB_STTOR(Signal* signal)
+{
+ jamEntry();
+ Uint32 ownNodeId = signal->theData[1]; /* START PHASE*/
+ cstartPhase = signal->theData[2]; /* MY NODE ID */
+ cstartType = signal->theData[3]; /* START TYPE */
+
+ switch (cstartPhase) {
+ case ZSTART_PHASE1:
+ jam();
+ preComputedRequestInfoMask = 0;
+ LqhKeyReq::setKeyLen(preComputedRequestInfoMask, RI_KEYLEN_MASK);
+ LqhKeyReq::setLastReplicaNo(preComputedRequestInfoMask, RI_LAST_REPL_MASK);
+ LqhKeyReq::setLockType(preComputedRequestInfoMask, RI_LOCK_TYPE_MASK);
+ // Dont LqhKeyReq::setApplicationAddressFlag
+ LqhKeyReq::setDirtyFlag(preComputedRequestInfoMask, 1);
+ // Dont LqhKeyReq::setInterpretedFlag
+ LqhKeyReq::setSimpleFlag(preComputedRequestInfoMask, 1);
+ LqhKeyReq::setOperation(preComputedRequestInfoMask, RI_OPERATION_MASK);
+ // Dont setAIInLqhKeyReq
+ // Dont setSeqNoReplica
+ // Dont setSameClientAndTcFlag
+ // Dont setReturnedReadLenAIFlag
+ // Dont setAPIVersion
+ LqhKeyReq::setMarkerFlag(preComputedRequestInfoMask, 1);
+ //preComputedRequestInfoMask = 0x003d7fff;
+ startphase1Lab(signal, /* dummy */ ~0, ownNodeId);
+
+ signal->theData[0] = ZOPERATION_EVENT_REP;
+ signal->theData[1] = 1;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 2);
+ return;
+ break;
+ case ZSTART_PHASE2:
+ jam();
+ startphase2Lab(signal, /* dummy */ ~0);
+ return;
+ break;
+ case ZSTART_PHASE3:
+ jam();
+ startphase3Lab(signal);
+ return;
+ break;
+ case ZSTART_PHASE4:
+ jam();
+ startphase4Lab(signal);
+ return;
+ break;
+ case ZSTART_PHASE6:
+ jam();
+ startphase6Lab(signal);
+ return;
+ break;
+ default:
+ jam();
+ /*empty*/;
+ sendNdbSttorryLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::execNDB_STTOR()
+
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* +++++++ START PHASE 1 +++++++ */
+/* LOAD OUR BLOCK REFERENCE AND OUR PROCESSOR ID */
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+void Dblqh::sttorStartphase1Lab(Signal* signal)
+{
+ sendsttorryLab(signal);
+ return;
+}//Dblqh::sttorStartphase1Lab()
+
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* +++++++ START PHASE 2 +++++++ */
+/* */
+/* INITIATE ALL RECORDS WITHIN THE BLOCK */
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+void Dblqh::startphase1Lab(Signal* signal, Uint32 _dummy, Uint32 ownNodeId)
+{
+ UintR Ti;
+ HostRecordPtr ThostPtr;
+
+/* ------- INITIATE ALL RECORDS ------- */
+ cownNodeid = ownNodeId;
+ caccBlockref = calcAccBlockRef (cownNodeid);
+ ctupBlockref = calcTupBlockRef (cownNodeid);
+ ctuxBlockref = calcTuxBlockRef (cownNodeid);
+ cownref = calcLqhBlockRef (cownNodeid);
+ for (Ti = 0; Ti < chostFileSize; Ti++) {
+ ThostPtr.i = Ti;
+ ptrCheckGuard(ThostPtr, chostFileSize, hostRecord);
+ ThostPtr.p->hostLqhBlockRef = calcLqhBlockRef(ThostPtr.i);
+ ThostPtr.p->hostTcBlockRef = calcTcBlockRef(ThostPtr.i);
+ ThostPtr.p->inPackedList = false;
+ ThostPtr.p->noOfPackedWordsLqh = 0;
+ ThostPtr.p->noOfPackedWordsTc = 0;
+ }//for
+ cpackedListIndex = 0;
+ sendNdbSttorryLab(signal);
+ return;
+}//Dblqh::startphase1Lab()
+
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* +++++++ START PHASE 2 +++++++ */
+/* */
+/* CONNECT LQH WITH ACC AND TUP. */
+/* EVERY CONNECTION RECORD IN LQH IS ASSIGNED TO ONE ACC CONNECTION RECORD */
+/* AND ONE TUP CONNECTION RECORD. */
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+void Dblqh::startphase2Lab(Signal* signal, Uint32 _dummy)
+{
+ cmaxWordsAtNodeRec = MAX_NO_WORDS_OUTSTANDING_COPY_FRAGMENT;
+/* -- ACC AND TUP CONNECTION PROCESS -- */
+ tcConnectptr.i = 0;
+ ptrAss(tcConnectptr, tcConnectionrec);
+ moreconnectionsLab(signal);
+ return;
+}//Dblqh::startphase2Lab()
+
+void Dblqh::moreconnectionsLab(Signal* signal)
+{
+ tcConnectptr.p->tcAccBlockref = caccBlockref;
+ // set TUX block here (no operation is seized in TUX)
+ tcConnectptr.p->tcTuxBlockref = ctuxBlockref;
+/* NO STATE CHECKING IS PERFORMED, ASSUMED TO WORK */
+/* *************** */
+/* ACCSEIZEREQ < */
+/* *************** */
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ sendSignal(caccBlockref, GSN_ACCSEIZEREQ, signal, 2, JBB);
+ return;
+}//Dblqh::moreconnectionsLab()
+
+/* ***************> */
+/* ACCSEIZECONF > */
+/* ***************> */
+void Dblqh::execACCSEIZECONF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ tcConnectptr.p->accConnectrec = signal->theData[1];
+/* *************** */
+/* TUPSEIZEREQ < */
+/* *************** */
+ tcConnectptr.p->tcTupBlockref = ctupBlockref;
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ sendSignal(ctupBlockref, GSN_TUPSEIZEREQ, signal, 2, JBB);
+ return;
+}//Dblqh::execACCSEIZECONF()
+
+/* ***************> */
+/* TUPSEIZECONF > */
+/* ***************> */
+void Dblqh::execTUPSEIZECONF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ tcConnectptr.p->tupConnectrec = signal->theData[1];
+/* ------- CHECK IF THERE ARE MORE CONNECTIONS TO BE CONNECTED ------- */
+ tcConnectptr.i = tcConnectptr.p->nextTcConnectrec;
+ if (tcConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ moreconnectionsLab(signal);
+ return;
+ }//if
+/* ALL LQH_CONNECT RECORDS ARE CONNECTED TO ACC AND TUP ---- */
+ sendNdbSttorryLab(signal);
+ return;
+}//Dblqh::execTUPSEIZECONF()
+
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* +++++++ START PHASE 4 +++++++ */
+/* */
+/* CONNECT LQH WITH LQH. */
+/* CONNECT EACH LQH WITH EVERY LQH IN THE DATABASE SYSTEM. */
+/* IF INITIAL START THEN CREATE THE FRAGMENT LOG FILES */
+/*IF SYSTEM RESTART OR NODE RESTART THEN OPEN THE FRAGMENT LOG FILES AND */
+/*FIND THE END OF THE LOG FILES. */
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* WAIT UNTIL ADD NODE PROCESSES ARE COMPLETED */
+/* IF INITIAL START ALSO WAIT FOR LOG FILES TO INITIALISED */
+/*START TIME SUPERVISION OF LOG FILES. WE HAVE TO WRITE LOG PAGES TO DISK */
+/*EVEN IF THE PAGES ARE NOT FULL TO ENSURE THAT THEY COME TO DISK ASAP. */
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+void Dblqh::startphase3Lab(Signal* signal)
+{
+ LogFileRecordPtr prevLogFilePtr;
+ LogFileRecordPtr zeroLogFilePtr;
+
+ caddNodeState = ZTRUE;
+/* ***************<< */
+/* READ_NODESREQ < */
+/* ***************<< */
+ cinitialStartOngoing = ZTRUE;
+ ndbrequire(cnoLogFiles != 0);
+
+ for (logPartPtr.i = 0; logPartPtr.i < 4; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ initLogpart(signal);
+ for (Uint32 fileNo = 0; fileNo < cnoLogFiles; fileNo++) {
+ seizeLogfile(signal);
+ if (fileNo != 0) {
+ jam();
+ prevLogFilePtr.p->nextLogFile = logFilePtr.i;
+ logFilePtr.p->prevLogFile = prevLogFilePtr.i;
+ } else {
+ jam();
+ logPartPtr.p->firstLogfile = logFilePtr.i;
+ logPartPtr.p->currentLogfile = logFilePtr.i;
+ zeroLogFilePtr.i = logFilePtr.i;
+ zeroLogFilePtr.p = logFilePtr.p;
+ }//if
+ prevLogFilePtr.i = logFilePtr.i;
+ prevLogFilePtr.p = logFilePtr.p;
+ initLogfile(signal, fileNo);
+ if ((cstartType == NodeState::ST_INITIAL_START) ||
+ (cstartType == NodeState::ST_INITIAL_NODE_RESTART)) {
+ if (logFilePtr.i == zeroLogFilePtr.i) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*IN AN INITIAL START WE START BY CREATING ALL LOG FILES AND SETTING THEIR */
+/*PROPER SIZE AND INITIALISING PAGE ZERO IN ALL FILES. */
+/*WE START BY CREATING FILE ZERO IN EACH LOG PART AND THEN PROCEED */
+/*SEQUENTIALLY THROUGH ALL LOG FILES IN THE LOG PART. */
+/* ------------------------------------------------------------------------- */
+ openLogfileInit(signal);
+ }//if
+ }//if
+ }//for
+ zeroLogFilePtr.p->prevLogFile = logFilePtr.i;
+ logFilePtr.p->nextLogFile = zeroLogFilePtr.i;
+ }//for
+ if (cstartType != NodeState::ST_INITIAL_START &&
+ cstartType != NodeState::ST_INITIAL_NODE_RESTART) {
+ jam();
+ ndbrequire(cstartType == NodeState::ST_NODE_RESTART ||
+ cstartType == NodeState::ST_SYSTEM_RESTART);
+ /** --------------------------------------------------------------------
+ * THIS CODE KICKS OFF THE SYSTEM RESTART AND NODE RESTART. IT STARTS UP
+ * THE RESTART BY FINDING THE END OF THE LOG AND FROM THERE FINDING THE
+ * INFO ABOUT THE GLOBAL CHECKPOINTS IN THE FRAGMENT LOG.
+ --------------------------------------------------------------------- */
+ for (logPartPtr.i = 0; logPartPtr.i < 4; logPartPtr.i++) {
+ jam();
+ LogFileRecordPtr locLogFilePtr;
+ ptrAss(logPartPtr, logPartRecord);
+ locLogFilePtr.i = logPartPtr.p->firstLogfile;
+ ptrCheckGuard(locLogFilePtr, clogFileFileSize, logFileRecord);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_FRONTPAGE;
+ openFileRw(signal, locLogFilePtr);
+ }//for
+ }//if
+
+ signal->theData[0] = cownref;
+ sendSignal(NDBCNTR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+ return;
+}//Dblqh::startphase3Lab()
+
+/* ****************** */
+/* READ_NODESCONF > */
+/* ****************** */
+void Dblqh::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+
+ ReadNodesConf * const readNodes = (ReadNodesConf *)&signal->theData[0];
+ cnoOfNodes = readNodes->noOfNodes;
+
+ unsigned ind = 0;
+ unsigned i = 0;
+ for (i = 1; i < MAX_NDB_NODES; i++) {
+ jam();
+ if (NodeBitmask::get(readNodes->allNodes, i)) {
+ jam();
+ cnodeData[ind] = i;
+ cnodeStatus[ind] = NodeBitmask::get(readNodes->inactiveNodes, i);
+ //readNodes->getVersionId(i, readNodes->theVersionIds) not used
+ ind++;
+ }//if
+ }//for
+ ndbrequire(ind == cnoOfNodes);
+ ndbrequire(cnoOfNodes >= 1 && cnoOfNodes < MAX_NDB_NODES);
+ ndbrequire(!(cnoOfNodes == 1 && cstartType == NodeState::ST_NODE_RESTART));
+
+ caddNodeState = ZFALSE;
+ if (cstartType == NodeState::ST_SYSTEM_RESTART) {
+ jam();
+ sendNdbSttorryLab(signal);
+ return;
+ }//if
+ checkStartCompletedLab(signal);
+ return;
+}//Dblqh::execREAD_NODESCONF()
+
+void Dblqh::checkStartCompletedLab(Signal* signal)
+{
+ if (caddNodeState == ZFALSE) {
+ if (cinitialStartOngoing == ZFALSE) {
+ jam();
+ sendNdbSttorryLab(signal);
+ return;
+ }//if
+ }//if
+ return;
+}//Dblqh::checkStartCompletedLab()
+
+void Dblqh::startphase4Lab(Signal* signal)
+{
+ sendNdbSttorryLab(signal);
+ return;
+}//Dblqh::startphase4Lab()
+
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* SET CONCURRENCY OF LOCAL CHECKPOINTS TO BE USED AFTER SYSTEM RESTART. */
+/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+void Dblqh::startphase6Lab(Signal* signal)
+{
+ cstartPhase = ZNIL;
+ cstartType = ZNIL;
+ sendNdbSttorryLab(signal);
+ return;
+}//Dblqh::startphase6Lab()
+
+void Dblqh::sendNdbSttorryLab(Signal* signal)
+{
+ signal->theData[0] = cownref;
+ sendSignal(NDBCNTR_REF, GSN_NDB_STTORRY, signal, 1, JBB);
+ return;
+}//Dblqh::sendNdbSttorryLab()
+
+void Dblqh::sendsttorryLab(Signal* signal)
+{
+/* *********<< */
+/* STTORRY < */
+/* *********<< */
+ signal->theData[0] = csignalKey; /* SIGNAL KEY */
+ signal->theData[1] = 3; /* BLOCK CATEGORY */
+ signal->theData[2] = 2; /* SIGNAL VERSION NUMBER */
+ signal->theData[3] = ZSTART_PHASE1;
+ signal->theData[4] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+ return;
+}//Dblqh::sendsttorryLab()
+
+/* ***************>> */
+/* READ_NODESREF > */
+/* ***************>> */
+void Dblqh::execREAD_NODESREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execREAD_NODESREF()
+
+/* *************** */
+/* SIZEALT_REP > */
+/* *************** */
+void Dblqh::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ jamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ cnoLogFiles = 8;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_NO_REDOLOG_FILES,
+ &cnoLogFiles));
+ ndbrequire(cnoLogFiles > 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_FRAG, &cfragrecFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_TABLE, &ctabrecFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_TC_CONNECT,
+ &ctcConnectrecFileSize));
+ clogFileFileSize = 4 * cnoLogFiles;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_SCAN, &cscanrecFileSize));
+ cmaxAccOps = cscanrecFileSize * MAX_PARALLEL_OP_PER_SCAN;
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_DISCLESS, &c_diskless));
+
+ initRecords();
+ initialiseRecordsLab(signal, 0, ref, senderData);
+
+ return;
+}//Dblqh::execSIZEALT_REP()
+
+/* ########################################################################## */
+/* ####### ADD/DELETE FRAGMENT MODULE ####### */
+/* THIS MODULE IS USED BY DICTIONARY TO CREATE NEW FRAGMENTS AND DELETE */
+/* OLD FRAGMENTS. */
+/* */
+/* ########################################################################## */
+/* -------------------------------------------------------------- */
+/* FRAG REQ */
+/* -------------------------------------------------------------- */
+/* *********************************************************> */
+/* LQHFRAGREQ: Create new fragments for a table. Sender DICT */
+/* *********************************************************> */
+
+// this unbelievable mess could be replaced by one signal to LQH
+// and execute direct to local DICT to get everything at once
+
+void Dblqh::execLQHFRAGREQ(Signal* signal)
+{
+ jamEntry();
+ LqhFragReq * req = (LqhFragReq*)signal->getDataPtr();
+
+ Uint32 retPtr = req->senderData;
+ BlockReference retRef = req->senderRef;
+ Uint32 fragId = req->fragmentId;
+ Uint32 reqinfo = req->requestInfo;
+ tabptr.i = req->tableId;
+ Uint16 tlocalKeylen = req->localKeyLength;
+ Uint32 tmaxLoadFactor = req->maxLoadFactor;
+ Uint32 tminLoadFactor = req->minLoadFactor;
+ Uint8 tk = req->kValue;
+ Uint8 tlhstar = req->lh3DistrBits;
+ Uint8 tlh = req->lh3PageBits;
+ Uint32 tnoOfAttr = req->noOfAttributes;
+ Uint32 tnoOfNull = req->noOfNullAttributes;
+ Uint32 noOfAlloc = req->noOfPagesToPreAllocate;
+ Uint32 tschemaVersion = req->schemaVersion;
+ Uint32 ttupKeyLength = req->keyLength;
+ Uint32 nextLcp = req->nextLCP;
+ Uint32 noOfKeyAttr = req->noOfKeyAttr;
+ Uint32 noOfNewAttr = req->noOfNewAttr;
+ Uint32 checksumIndicator = req->checksumIndicator;
+ Uint32 noOfAttributeGroups = req->noOfAttributeGroups;
+ Uint32 gcpIndicator = req->GCPIndicator;
+ Uint32 startGci = req->startGci;
+ Uint32 tableType = req->tableType;
+ Uint32 primaryTableId = req->primaryTableId;
+
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ bool tempTable = ((reqinfo & LqhFragReq::TemporaryTable) != 0);
+
+ /* Temporary tables set to defined in system restart */
+ if (tabptr.p->tableStatus == Tablerec::NOT_DEFINED){
+ tabptr.p->tableStatus = Tablerec::ADD_TABLE_ONGOING;
+ tabptr.p->tableType = tableType;
+ tabptr.p->primaryTableId = primaryTableId;
+ tabptr.p->schemaVersion = tschemaVersion;
+ }//if
+
+ if (tabptr.p->tableStatus != Tablerec::ADD_TABLE_ONGOING){
+ jam();
+ fragrefLab(signal, retRef, retPtr, ZTAB_STATE_ERROR);
+ return;
+ }//if
+ //--------------------------------------------------------------------
+ // We could arrive here if we create the fragment as part of a take
+ // over by a hot spare node. The table is then is already created
+ // and bit 31 is set, thus indicating that we are creating a fragment
+ // by copy creation. Also since the node has already been started we
+ // know that it is not a node restart ongoing.
+ //--------------------------------------------------------------------
+
+ if (getFragmentrec(signal, fragId)) {
+ jam();
+ fragrefLab(signal, retRef, retPtr, terrorCode);
+ return;
+ }//if
+ if (!insertFragrec(signal, fragId)) {
+ jam();
+ fragrefLab(signal, retRef, retPtr, terrorCode);
+ return;
+ }//if
+ Uint32 copyType = reqinfo & 3;
+ initFragrec(signal, tabptr.i, fragId, copyType);
+ fragptr.p->startGci = startGci;
+ fragptr.p->newestGci = startGci;
+ fragptr.p->tableType = tableType;
+
+ if (DictTabInfo::isOrderedIndex(tableType)) {
+ jam();
+ // find corresponding primary table fragment
+ TablerecPtr tTablePtr;
+ tTablePtr.i = primaryTableId;
+ ptrCheckGuard(tTablePtr, ctabrecFileSize, tablerec);
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = RNIL;
+ for (Uint32 i = 0; i < MAX_FRAG_PER_NODE; i++) {
+ if (tTablePtr.p->fragid[i] == fragptr.p->fragId) {
+ jam();
+ tFragPtr.i = tTablePtr.p->fragrec[i];
+ break;
+ }
+ }
+ ndbrequire(tFragPtr.i != RNIL);
+ // store it
+ fragptr.p->tableFragptr = tFragPtr.i;
+ } else {
+ fragptr.p->tableFragptr = fragptr.i;
+ }
+
+ if (tempTable) {
+//--------------------------------------------
+// reqinfo bit 3-4 = 2 means temporary table
+// without logging or checkpointing.
+//--------------------------------------------
+ jam();
+ fragptr.p->logFlag = Fragrecord::STATE_FALSE;
+ fragptr.p->lcpFlag = Fragrecord::LCP_STATE_FALSE;
+ }//if
+
+ fragptr.p->nextLcp = nextLcp;
+//----------------------------------------------
+// For node restarts it is not necessarily zero
+//----------------------------------------------
+ if (cfirstfreeAddfragrec == RNIL) {
+ jam();
+ deleteFragrec(fragId);
+ fragrefLab(signal, retRef, retPtr, ZNO_ADD_FRAGREC);
+ return;
+ }//if
+ seizeAddfragrec(signal);
+ addfragptr.p->addFragid = fragId;
+ addfragptr.p->fragmentPtr = fragptr.i;
+ addfragptr.p->dictBlockref = retRef;
+ addfragptr.p->dictConnectptr = retPtr;
+ addfragptr.p->m_senderAttrPtr = RNIL;
+ addfragptr.p->noOfAttr = tnoOfAttr;
+ addfragptr.p->noOfNull = tnoOfNull;
+ addfragptr.p->noOfAllocPages = noOfAlloc;
+ addfragptr.p->tabId = tabptr.i;
+ addfragptr.p->totalAttrReceived = 0;
+ addfragptr.p->attrSentToTup = ZNIL;/* TO FIND PROGRAMMING ERRORS QUICKLY */
+ addfragptr.p->schemaVer = tschemaVersion;
+ Uint32 tmp = (reqinfo & LqhFragReq::CreateInRunning);
+ addfragptr.p->fragCopyCreation = (tmp == 0 ? 0 : 1);
+ addfragptr.p->addfragErrorCode = 0;
+ addfragptr.p->noOfKeyAttr = noOfKeyAttr;
+ addfragptr.p->noOfNewAttr = noOfNewAttr;
+ addfragptr.p->checksumIndicator = checksumIndicator;
+ addfragptr.p->noOfAttributeGroups = noOfAttributeGroups;
+ addfragptr.p->GCPIndicator = gcpIndicator;
+ addfragptr.p->lh3DistrBits = tlhstar;
+ addfragptr.p->tableType = tableType;
+ addfragptr.p->primaryTableId = primaryTableId;
+ //
+ addfragptr.p->tup1Connectptr = RNIL;
+ addfragptr.p->tup2Connectptr = RNIL;
+ addfragptr.p->tux1Connectptr = RNIL;
+ addfragptr.p->tux2Connectptr = RNIL;
+
+ if (DictTabInfo::isTable(tableType) ||
+ DictTabInfo::isHashIndex(tableType)) {
+ jam();
+ AccFragReq* const accreq = (AccFragReq*)signal->getDataPtrSend();
+ accreq->userPtr = addfragptr.i;
+ accreq->userRef = cownref;
+ accreq->tableId = tabptr.i;
+ accreq->reqInfo = copyType << 4;
+ accreq->fragId = fragId;
+ accreq->localKeyLen = tlocalKeylen;
+ accreq->maxLoadFactor = tmaxLoadFactor;
+ accreq->minLoadFactor = tminLoadFactor;
+ accreq->kValue = tk;
+ accreq->lhFragBits = tlhstar;
+ accreq->lhDirBits = tlh;
+ accreq->keyLength = ttupKeyLength;
+ /* ----------------------------------------------------------------------- */
+ /* Send ACCFRAGREQ, when confirmation is received send 2 * TUPFRAGREQ to */
+ /* create 2 tuple fragments on this node. */
+ /* ----------------------------------------------------------------------- */
+ addfragptr.p->addfragStatus = AddFragRecord::ACC_ADDFRAG;
+ sendSignal(fragptr.p->accBlockref, GSN_ACCFRAGREQ,
+ signal, AccFragReq::SignalLength, JBB);
+ return;
+ }
+ if (DictTabInfo::isOrderedIndex(tableType)) {
+ jam();
+ // NOTE: next 2 lines stolen from ACC
+ addfragptr.p->fragid1 = (fragId << 1) | 0;
+ addfragptr.p->fragid2 = (fragId << 1) | 1;
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_TWO_TUP;
+ sendAddFragReq(signal);
+ return;
+ }
+ ndbrequire(false);
+}//Dblqh::execLQHFRAGREQ()
+
+/* *************** */
+/* ACCFRAGCONF > */
+/* *************** */
+void Dblqh::execACCFRAGCONF(Signal* signal)
+{
+ jamEntry();
+ addfragptr.i = signal->theData[0];
+ Uint32 taccConnectptr = signal->theData[1];
+ Uint32 fragId1 = signal->theData[2];
+ Uint32 fragId2 = signal->theData[3];
+ Uint32 accFragPtr1 = signal->theData[4];
+ Uint32 accFragPtr2 = signal->theData[5];
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ ndbrequire(addfragptr.p->addfragStatus == AddFragRecord::ACC_ADDFRAG);
+
+ addfragptr.p->accConnectptr = taccConnectptr;
+ addfragptr.p->fragid1 = fragId1;
+ addfragptr.p->fragid2 = fragId2;
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->accFragptr[0] = accFragPtr1;
+ fragptr.p->accFragptr[1] = accFragPtr2;
+
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_TWO_TUP;
+ sendAddFragReq(signal);
+}//Dblqh::execACCFRAGCONF()
+
+/* *************** */
+/* TUPFRAGCONF > */
+/* *************** */
+void Dblqh::execTUPFRAGCONF(Signal* signal)
+{
+ jamEntry();
+ addfragptr.i = signal->theData[0];
+ Uint32 tupConnectptr = signal->theData[1];
+ Uint32 tupFragPtr = signal->theData[2]; /* TUP FRAGMENT POINTER */
+ Uint32 localFragId = signal->theData[3]; /* LOCAL FRAGMENT ID */
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (localFragId == addfragptr.p->fragid1) {
+ jam();
+ fragptr.p->tupFragptr[0] = tupFragPtr;
+ } else if (localFragId == addfragptr.p->fragid2) {
+ jam();
+ fragptr.p->tupFragptr[1] = tupFragPtr;
+ } else {
+ ndbrequire(false);
+ return;
+ }//if
+ switch (addfragptr.p->addfragStatus) {
+ case AddFragRecord::WAIT_TWO_TUP:
+ jam();
+ fragptr.p->tupFragptr[0] = tupFragPtr;
+ addfragptr.p->tup1Connectptr = tupConnectptr;
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_ONE_TUP;
+ sendAddFragReq(signal);
+ break;
+ case AddFragRecord::WAIT_ONE_TUP:
+ jam();
+ fragptr.p->tupFragptr[1] = tupFragPtr;
+ addfragptr.p->tup2Connectptr = tupConnectptr;
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType)) {
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_TWO_TUX;
+ sendAddFragReq(signal);
+ break;
+ }
+ goto done_with_frag;
+ break;
+ case AddFragRecord::WAIT_TWO_TUX:
+ jam();
+ fragptr.p->tuxFragptr[0] = tupFragPtr;
+ addfragptr.p->tux1Connectptr = tupConnectptr;
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_ONE_TUX;
+ sendAddFragReq(signal);
+ break;
+ case AddFragRecord::WAIT_ONE_TUX:
+ jam();
+ fragptr.p->tuxFragptr[1] = tupFragPtr;
+ addfragptr.p->tux2Connectptr = tupConnectptr;
+ goto done_with_frag;
+ break;
+ done_with_frag:
+ /* ---------------------------------------------------------------- */
+ /* Finished create of fragments. Now ready for creating attributes. */
+ /* ---------------------------------------------------------------- */
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_ADD_ATTR;
+ {
+ LqhFragConf* conf = (LqhFragConf*)signal->getDataPtrSend();
+ conf->senderData = addfragptr.p->dictConnectptr;
+ conf->lqhFragPtr = addfragptr.i;
+ sendSignal(addfragptr.p->dictBlockref, GSN_LQHFRAGCONF,
+ signal, LqhFragConf::SignalLength, JBB);
+ }
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+}//Dblqh::execTUPFRAGCONF()
+
+/* *************** */
+/* TUXFRAGCONF > */
+/* *************** */
+void Dblqh::execTUXFRAGCONF(Signal* signal)
+{
+ jamEntry();
+ execTUPFRAGCONF(signal);
+}//Dblqh::execTUXFRAGCONF
+
+/*
+ * Add fragment in TUP or TUX. Called up to 4 times.
+ */
+void
+Dblqh::sendAddFragReq(Signal* signal)
+{
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUP ||
+ addfragptr.p->addfragStatus == AddFragRecord::WAIT_ONE_TUP) {
+ if (DictTabInfo::isTable(addfragptr.p->tableType) ||
+ DictTabInfo::isHashIndex(addfragptr.p->tableType)) {
+ jam();
+ signal->theData[0] = addfragptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = 0; /* ADD TABLE */
+ signal->theData[3] = addfragptr.p->tabId;
+ signal->theData[4] = addfragptr.p->noOfAttr;
+ signal->theData[5] =
+ addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUP
+ ? addfragptr.p->fragid1 : addfragptr.p->fragid2;
+ signal->theData[6] = (addfragptr.p->noOfAllocPages >> 1) + 1;
+ signal->theData[7] = addfragptr.p->noOfNull;
+ signal->theData[8] = addfragptr.p->schemaVer;
+ signal->theData[9] = addfragptr.p->noOfKeyAttr;
+ signal->theData[10] = addfragptr.p->noOfNewAttr;
+ signal->theData[11] = addfragptr.p->checksumIndicator;
+ signal->theData[12] = addfragptr.p->noOfAttributeGroups;
+ signal->theData[13] = addfragptr.p->GCPIndicator;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUPFRAGREQ,
+ signal, TupFragReq::SignalLength, JBB);
+ return;
+ }
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType)) {
+ jam();
+ signal->theData[0] = addfragptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = 0; /* ADD TABLE */
+ signal->theData[3] = addfragptr.p->tabId;
+ signal->theData[4] = 1; /* ordered index: one array attr */
+ signal->theData[5] =
+ addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUP
+ ? addfragptr.p->fragid1 : addfragptr.p->fragid2;
+ signal->theData[6] = (addfragptr.p->noOfAllocPages >> 1) + 1;
+ signal->theData[7] = 0; /* ordered index: no nullable */
+ signal->theData[8] = addfragptr.p->schemaVer;
+ signal->theData[9] = 1; /* ordered index: one key */
+ signal->theData[10] = addfragptr.p->noOfNewAttr;
+ signal->theData[11] = addfragptr.p->checksumIndicator;
+ signal->theData[12] = addfragptr.p->noOfAttributeGroups;
+ signal->theData[13] = addfragptr.p->GCPIndicator;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUPFRAGREQ,
+ signal, TupFragReq::SignalLength, JBB);
+ return;
+ }
+ }
+ if (addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUX ||
+ addfragptr.p->addfragStatus == AddFragRecord::WAIT_ONE_TUX) {
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType)) {
+ jam();
+ TuxFragReq* const tuxreq = (TuxFragReq*)signal->getDataPtrSend();
+ tuxreq->userPtr = addfragptr.i;
+ tuxreq->userRef = cownref;
+ tuxreq->reqInfo = 0; /* ADD TABLE */
+ tuxreq->tableId = addfragptr.p->tabId;
+ ndbrequire(addfragptr.p->noOfAttr >= 2);
+ tuxreq->noOfAttr = addfragptr.p->noOfAttr - 1; /* skip NDB$TNODE */
+ tuxreq->fragId =
+ addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUX
+ ? addfragptr.p->fragid1: addfragptr.p->fragid2;
+ tuxreq->fragOff = addfragptr.p->lh3DistrBits;
+ tuxreq->tableType = addfragptr.p->tableType;
+ tuxreq->primaryTableId = addfragptr.p->primaryTableId;
+ // pointer to index fragment in TUP
+ tuxreq->tupIndexFragPtrI =
+ addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUX ?
+ fragptr.p->tupFragptr[0] : fragptr.p->tupFragptr[1];
+ // pointers to table fragments in TUP and ACC
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = fragptr.p->tableFragptr;
+ ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
+ tuxreq->tupTableFragPtrI[0] = tFragPtr.p->tupFragptr[0];
+ tuxreq->tupTableFragPtrI[1] = tFragPtr.p->tupFragptr[1];
+ tuxreq->accTableFragPtrI[0] = tFragPtr.p->accFragptr[0];
+ tuxreq->accTableFragPtrI[1] = tFragPtr.p->accFragptr[1];
+ sendSignal(fragptr.p->tuxBlockref, GSN_TUXFRAGREQ,
+ signal, TuxFragReq::SignalLength, JBB);
+ return;
+ }
+ }
+ ndbrequire(false);
+}//Dblqh::sendAddFragReq
+
+/* ************************************************************************> */
+/* LQHADDATTRREQ: Request from DICT to create attributes for the new table. */
+/* ************************************************************************> */
+void Dblqh::execLQHADDATTREQ(Signal* signal)
+{
+ jamEntry();
+ LqhAddAttrReq * const req = (LqhAddAttrReq*)signal->getDataPtr();
+
+ addfragptr.i = req->lqhFragPtr;
+ const Uint32 tnoOfAttr = req->noOfAttributes;
+ const Uint32 senderData = req->senderData;
+ const Uint32 senderAttrPtr = req->senderAttrPtr;
+
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ ndbrequire(addfragptr.p->addfragStatus == AddFragRecord::WAIT_ADD_ATTR);
+ ndbrequire((tnoOfAttr != 0) && (tnoOfAttr <= LqhAddAttrReq::MAX_ATTRIBUTES));
+ addfragptr.p->totalAttrReceived += tnoOfAttr;
+ ndbrequire(addfragptr.p->totalAttrReceived <= addfragptr.p->noOfAttr);
+
+ addfragptr.p->attrReceived = tnoOfAttr;
+ for (Uint32 i = 0; i < tnoOfAttr; i++) {
+ addfragptr.p->attributes[i] = req->attributes[i];
+ }//for
+ addfragptr.p->attrSentToTup = 0;
+ ndbrequire(addfragptr.p->dictConnectptr == senderData);
+ addfragptr.p->m_senderAttrPtr = senderAttrPtr;
+ addfragptr.p->addfragStatus = AddFragRecord::TUP_ATTR_WAIT1;
+ sendAddAttrReq(signal);
+}//Dblqh::execLQHADDATTREQ()
+
+/* *********************>> */
+/* TUP_ADD_ATTCONF > */
+/* *********************>> */
+void Dblqh::execTUP_ADD_ATTCONF(Signal* signal)
+{
+ jamEntry();
+ addfragptr.i = signal->theData[0];
+ // implies that operation was released on the other side
+ const bool lastAttr = signal->theData[1];
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ switch (addfragptr.p->addfragStatus) {
+ case AddFragRecord::TUP_ATTR_WAIT1:
+ jam();
+ if (lastAttr)
+ addfragptr.p->tup1Connectptr = RNIL;
+ addfragptr.p->addfragStatus = AddFragRecord::TUP_ATTR_WAIT2;
+ sendAddAttrReq(signal);
+ break;
+ case AddFragRecord::TUP_ATTR_WAIT2:
+ jam();
+ if (lastAttr)
+ addfragptr.p->tup2Connectptr = RNIL;
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType)) {
+ addfragptr.p->addfragStatus = AddFragRecord::TUX_ATTR_WAIT1;
+ sendAddAttrReq(signal);
+ break;
+ }
+ goto done_with_attr;
+ break;
+ case AddFragRecord::TUX_ATTR_WAIT1:
+ jam();
+ if (lastAttr)
+ addfragptr.p->tux1Connectptr = RNIL;
+ addfragptr.p->addfragStatus = AddFragRecord::TUX_ATTR_WAIT2;
+ sendAddAttrReq(signal);
+ break;
+ case AddFragRecord::TUX_ATTR_WAIT2:
+ jam();
+ if (lastAttr)
+ addfragptr.p->tux2Connectptr = RNIL;
+ goto done_with_attr;
+ break;
+ done_with_attr:
+ addfragptr.p->attrSentToTup = addfragptr.p->attrSentToTup + 1;
+ ndbrequire(addfragptr.p->attrSentToTup <= addfragptr.p->attrReceived);
+ ndbrequire(addfragptr.p->totalAttrReceived <= addfragptr.p->noOfAttr);
+ if (addfragptr.p->attrSentToTup < addfragptr.p->attrReceived) {
+ // more in this batch
+ jam();
+ addfragptr.p->addfragStatus = AddFragRecord::TUP_ATTR_WAIT1;
+ sendAddAttrReq(signal);
+ } else if (addfragptr.p->totalAttrReceived < addfragptr.p->noOfAttr) {
+ // more batches to receive
+ jam();
+ addfragptr.p->addfragStatus = AddFragRecord::WAIT_ADD_ATTR;
+ LqhAddAttrConf *const conf = (LqhAddAttrConf*)signal->getDataPtrSend();
+ conf->senderData = addfragptr.p->dictConnectptr;
+ conf->senderAttrPtr = addfragptr.p->m_senderAttrPtr;
+ conf->fragId = addfragptr.p->addFragid;
+ sendSignal(addfragptr.p->dictBlockref, GSN_LQHADDATTCONF,
+ signal, LqhAddAttrConf::SignalLength, JBB);
+ } else {
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ /* ------------------------------------------------------------------
+ * WE HAVE NOW COMPLETED ADDING THIS FRAGMENT. WE NOW NEED TO SET THE
+ * PROPER STATE IN FRAG_STATUS DEPENDENT ON IF WE ARE CREATING A NEW
+ * REPLICA OR IF WE ARE CREATING A TABLE. FOR FRAGMENTS IN COPY
+ * PROCESS WE DO NOT WANT LOGGING ACTIVATED.
+ * ----------------------------------------------------------------- */
+ if (addfragptr.p->fragCopyCreation == 1) {
+ jam();
+ if (! DictTabInfo::isOrderedIndex(addfragptr.p->tableType))
+ fragptr.p->fragStatus = Fragrecord::ACTIVE_CREATION;
+ else
+ fragptr.p->fragStatus = Fragrecord::FSACTIVE;
+ fragptr.p->logFlag = Fragrecord::STATE_FALSE;
+ } else {
+ jam();
+ fragptr.p->fragStatus = Fragrecord::FSACTIVE;
+ }//if
+ LqhAddAttrConf *const conf = (LqhAddAttrConf*)signal->getDataPtrSend();
+ conf->senderData = addfragptr.p->dictConnectptr;
+ conf->senderAttrPtr = addfragptr.p->m_senderAttrPtr;
+ conf->fragId = addfragptr.p->addFragid;
+ sendSignal(addfragptr.p->dictBlockref, GSN_LQHADDATTCONF, signal,
+ LqhAddAttrConf::SignalLength, JBB);
+ releaseAddfragrec(signal);
+ }//if
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+}
+
+/* **********************>> */
+/* TUX_ADD_ATTRCONF > */
+/* **********************>> */
+void Dblqh::execTUX_ADD_ATTRCONF(Signal* signal)
+{
+ jamEntry();
+ execTUP_ADD_ATTCONF(signal);
+}//Dblqh::execTUX_ADD_ATTRCONF
+
+/*
+ * Add attribute in TUP or TUX. Called up to 4 times.
+ */
+void
+Dblqh::sendAddAttrReq(Signal* signal)
+{
+ arrGuard(addfragptr.p->attrSentToTup, LqhAddAttrReq::MAX_ATTRIBUTES);
+ LqhAddAttrReq::Entry& entry =
+ addfragptr.p->attributes[addfragptr.p->attrSentToTup];
+ const Uint32 attrId = entry.attrId & 0xffff;
+ const Uint32 primaryAttrId = entry.attrId >> 16;
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (addfragptr.p->addfragStatus == AddFragRecord::TUP_ATTR_WAIT1 ||
+ addfragptr.p->addfragStatus == AddFragRecord::TUP_ATTR_WAIT2) {
+ if (DictTabInfo::isTable(addfragptr.p->tableType) ||
+ DictTabInfo::isHashIndex(addfragptr.p->tableType) ||
+ (DictTabInfo::isOrderedIndex(addfragptr.p->tableType) &&
+ primaryAttrId == ZNIL)) {
+ jam();
+ TupAddAttrReq* const tupreq = (TupAddAttrReq*)signal->getDataPtrSend();
+ tupreq->tupConnectPtr =
+ addfragptr.p->addfragStatus == AddFragRecord::TUP_ATTR_WAIT1
+ ? addfragptr.p->tup1Connectptr : addfragptr.p->tup2Connectptr;
+ tupreq->notused1 = 0;
+ tupreq->attrId = attrId;
+ tupreq->attrDescriptor = entry.attrDescriptor;
+ tupreq->extTypeInfo = entry.extTypeInfo;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUP_ADD_ATTRREQ,
+ signal, TupAddAttrReq::SignalLength, JBB);
+ return;
+ }
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType) &&
+ primaryAttrId != ZNIL) {
+ // this attribute is not for TUP
+ jam();
+ TupAddAttrConf* tupconf = (TupAddAttrConf*)signal->getDataPtrSend();
+ tupconf->userPtr = addfragptr.i;
+ tupconf->lastAttr = false;
+ sendSignal(reference(), GSN_TUP_ADD_ATTCONF,
+ signal, TupAddAttrConf::SignalLength, JBB);
+ return;
+ }
+ }
+ if (addfragptr.p->addfragStatus == AddFragRecord::TUX_ATTR_WAIT1 ||
+ addfragptr.p->addfragStatus == AddFragRecord::TUX_ATTR_WAIT2) {
+ jam();
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType) &&
+ primaryAttrId != ZNIL) {
+ jam();
+ TuxAddAttrReq* const tuxreq = (TuxAddAttrReq*)signal->getDataPtrSend();
+ tuxreq->tuxConnectPtr =
+ addfragptr.p->addfragStatus == AddFragRecord::TUX_ATTR_WAIT1
+ ? addfragptr.p->tux1Connectptr : addfragptr.p->tux2Connectptr;
+ tuxreq->notused1 = 0;
+ tuxreq->attrId = attrId;
+ tuxreq->attrDescriptor = entry.attrDescriptor;
+ tuxreq->extTypeInfo = entry.extTypeInfo;
+ tuxreq->primaryAttrId = primaryAttrId;
+ sendSignal(fragptr.p->tuxBlockref, GSN_TUX_ADD_ATTRREQ,
+ signal, TuxAddAttrReq::SignalLength, JBB);
+ return;
+ }
+ if (DictTabInfo::isOrderedIndex(addfragptr.p->tableType) &&
+ primaryAttrId == ZNIL) {
+ // this attribute is not for TUX
+ jam();
+ TuxAddAttrConf* tuxconf = (TuxAddAttrConf*)signal->getDataPtrSend();
+ tuxconf->userPtr = addfragptr.i;
+ tuxconf->lastAttr = false;
+ sendSignal(reference(), GSN_TUX_ADD_ATTRCONF,
+ signal, TuxAddAttrConf::SignalLength, JBB);
+ return;
+ }
+ }
+ ndbrequire(false);
+}//Dblqh::sendAddAttrReq
+
+/* ************************************************************************>> */
+/* TAB_COMMITREQ: Commit the new table for use in transactions. Sender DICT. */
+/* ************************************************************************>> */
+void Dblqh::execTAB_COMMITREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 dihPtr = signal->theData[0];
+ BlockReference dihBlockref = signal->theData[1];
+ tabptr.i = signal->theData[2];
+
+ if (tabptr.i >= ctabrecFileSize) {
+ jam();
+ terrorCode = ZTAB_FILE_SIZE;
+ signal->theData[0] = dihPtr;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = tabptr.i;
+ signal->theData[3] = terrorCode;
+ sendSignal(dihBlockref, GSN_TAB_COMMITREF, signal, 4, JBB);
+ return;
+ }//if
+ ptrAss(tabptr, tablerec);
+ if (tabptr.p->tableStatus != Tablerec::ADD_TABLE_ONGOING) {
+ jam();
+ terrorCode = ZTAB_STATE_ERROR;
+ signal->theData[0] = dihPtr;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = tabptr.i;
+ signal->theData[3] = terrorCode;
+ signal->theData[4] = tabptr.p->tableStatus;
+ sendSignal(dihBlockref, GSN_TAB_COMMITREF, signal, 5, JBB);
+ ndbrequire(false);
+ return;
+ }//if
+ tabptr.p->usageCount = 0;
+ tabptr.p->tableStatus = Tablerec::TABLE_DEFINED;
+ signal->theData[0] = dihPtr;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = tabptr.i;
+ sendSignal(dihBlockref, GSN_TAB_COMMITCONF, signal, 3, JBB);
+ return;
+}//Dblqh::execTAB_COMMITREQ()
+
+
+void Dblqh::fragrefLab(Signal* signal,
+ BlockReference fragBlockRef,
+ Uint32 fragConPtr,
+ Uint32 errorCode)
+{
+ LqhFragRef * ref = (LqhFragRef*)signal->getDataPtrSend();
+ ref->senderData = fragConPtr;
+ ref->errorCode = errorCode;
+ sendSignal(fragBlockRef, GSN_LQHFRAGREF, signal,
+ LqhFragRef::SignalLength, JBB);
+ return;
+}//Dblqh::fragrefLab()
+
+/*
+ * Abort on-going ops.
+ */
+void Dblqh::abortAddFragOps(Signal* signal)
+{
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ signal->theData[0] = (Uint32)-1;
+ if (addfragptr.p->tup1Connectptr != RNIL) {
+ jam();
+ signal->theData[1] = addfragptr.p->tup1Connectptr;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUPFRAGREQ, signal, 2, JBB);
+ addfragptr.p->tup1Connectptr = RNIL;
+ }
+ if (addfragptr.p->tup2Connectptr != RNIL) {
+ jam();
+ signal->theData[1] = addfragptr.p->tup2Connectptr;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUPFRAGREQ, signal, 2, JBB);
+ addfragptr.p->tup2Connectptr = RNIL;
+ }
+ if (addfragptr.p->tux1Connectptr != RNIL) {
+ jam();
+ signal->theData[1] = addfragptr.p->tux1Connectptr;
+ sendSignal(fragptr.p->tuxBlockref, GSN_TUXFRAGREQ, signal, 2, JBB);
+ addfragptr.p->tux1Connectptr = RNIL;
+ }
+ if (addfragptr.p->tux2Connectptr != RNIL) {
+ jam();
+ signal->theData[1] = addfragptr.p->tux2Connectptr;
+ sendSignal(fragptr.p->tuxBlockref, GSN_TUXFRAGREQ, signal, 2, JBB);
+ addfragptr.p->tux2Connectptr = RNIL;
+ }
+}
+
+/* ************>> */
+/* ACCFRAGREF > */
+/* ************>> */
+void Dblqh::execACCFRAGREF(Signal* signal)
+{
+ jamEntry();
+ addfragptr.i = signal->theData[0];
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ terrorCode = signal->theData[1];
+ ndbrequire(addfragptr.p->addfragStatus == AddFragRecord::ACC_ADDFRAG);
+ addfragptr.p->addfragErrorCode = terrorCode;
+
+ const Uint32 ref = addfragptr.p->dictBlockref;
+ const Uint32 senderData = addfragptr.p->dictConnectptr;
+ const Uint32 errorCode = addfragptr.p->addfragErrorCode;
+ releaseAddfragrec(signal);
+ fragrefLab(signal, ref, senderData, errorCode);
+
+ return;
+}//Dblqh::execACCFRAGREF()
+
+/* ************>> */
+/* TUPFRAGREF > */
+/* ************>> */
+void Dblqh::execTUPFRAGREF(Signal* signal)
+{
+ jamEntry();
+ addfragptr.i = signal->theData[0];
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ terrorCode = signal->theData[1];
+ fragptr.i = addfragptr.p->fragmentPtr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ addfragptr.p->addfragErrorCode = terrorCode;
+
+ // no operation to release, just add some jams
+ switch (addfragptr.p->addfragStatus) {
+ case AddFragRecord::WAIT_TWO_TUP:
+ jam();
+ break;
+ case AddFragRecord::WAIT_ONE_TUP:
+ jam();
+ break;
+ case AddFragRecord::WAIT_TWO_TUX:
+ jam();
+ break;
+ case AddFragRecord::WAIT_ONE_TUX:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ abortAddFragOps(signal);
+
+ const Uint32 ref = addfragptr.p->dictBlockref;
+ const Uint32 senderData = addfragptr.p->dictConnectptr;
+ const Uint32 errorCode = addfragptr.p->addfragErrorCode;
+ releaseAddfragrec(signal);
+ fragrefLab(signal, ref, senderData, errorCode);
+
+}//Dblqh::execTUPFRAGREF()
+
+/* ************>> */
+/* TUXFRAGREF > */
+/* ************>> */
+void Dblqh::execTUXFRAGREF(Signal* signal)
+{
+ jamEntry();
+ execTUPFRAGREF(signal);
+}//Dblqh::execTUXFRAGREF
+
+/* *********************> */
+/* TUP_ADD_ATTREF > */
+/* *********************> */
+void Dblqh::execTUP_ADD_ATTRREF(Signal* signal)
+{
+ jamEntry();
+ addfragptr.i = signal->theData[0];
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ terrorCode = signal->theData[1];
+ addfragptr.p->addfragErrorCode = terrorCode;
+
+ // operation was released on the other side
+ switch (addfragptr.p->addfragStatus) {
+ case AddFragRecord::TUP_ATTR_WAIT1:
+ jam();
+ ndbrequire(addfragptr.p->tup1Connectptr != RNIL);
+ addfragptr.p->tup1Connectptr = RNIL;
+ break;
+ case AddFragRecord::TUP_ATTR_WAIT2:
+ jam();
+ ndbrequire(addfragptr.p->tup2Connectptr != RNIL);
+ addfragptr.p->tup2Connectptr = RNIL;
+ break;
+ case AddFragRecord::TUX_ATTR_WAIT1:
+ jam();
+ ndbrequire(addfragptr.p->tux1Connectptr != RNIL);
+ addfragptr.p->tux1Connectptr = RNIL;
+ break;
+ case AddFragRecord::TUX_ATTR_WAIT2:
+ jam();
+ ndbrequire(addfragptr.p->tux2Connectptr != RNIL);
+ addfragptr.p->tux2Connectptr = RNIL;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ abortAddFragOps(signal);
+
+ const Uint32 Ref = addfragptr.p->dictBlockref;
+ const Uint32 senderData = addfragptr.p->dictConnectptr;
+ const Uint32 errorCode = addfragptr.p->addfragErrorCode;
+ releaseAddfragrec(signal);
+
+ LqhAddAttrRef *const ref = (LqhAddAttrRef*)signal->getDataPtrSend();
+ ref->senderData = senderData;
+ ref->errorCode = errorCode;
+ sendSignal(Ref, GSN_LQHADDATTREF, signal,
+ LqhAddAttrRef::SignalLength, JBB);
+
+}//Dblqh::execTUP_ADD_ATTRREF()
+
+/* **********************> */
+/* TUX_ADD_ATTRREF > */
+/* **********************> */
+void Dblqh::execTUX_ADD_ATTRREF(Signal* signal)
+{
+ jamEntry();
+ execTUP_ADD_ATTRREF(signal);
+}//Dblqh::execTUX_ADD_ATTRREF
+
+void
+Dblqh::execPREP_DROP_TAB_REQ(Signal* signal){
+ jamEntry();
+
+ PrepDropTabReq* req = (PrepDropTabReq*)signal->getDataPtr();
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+
+ TablerecPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFileSize, tablerec);
+
+ Uint32 errCode = 0;
+ errCode = checkDropTabState(tabPtr.p->tableStatus, GSN_PREP_DROP_TAB_REQ);
+ if(errCode != 0){
+ jam();
+
+ PrepDropTabRef* ref = (PrepDropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = errCode;
+ sendSignal(senderRef, GSN_PREP_DROP_TAB_REF, signal,
+ PrepDropTabRef::SignalLength, JBB);
+ return;
+ }
+
+ tabPtr.p->tableStatus = Tablerec::PREP_DROP_TABLE_ONGOING;
+ tabPtr.p->waitingTC.clear();
+ tabPtr.p->waitingDIH.clear();
+
+ PrepDropTabConf * conf = (PrepDropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ sendSignal(senderRef, GSN_PREP_DROP_TAB_CONF, signal,
+ PrepDropTabConf::SignalLength, JBB);
+
+ signal->theData[0] = ZPREP_DROP_TABLE;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = senderRef;
+ signal->theData[3] = senderData;
+ checkDropTab(signal);
+}
+
+void
+Dblqh::checkDropTab(Signal* signal){
+
+ TablerecPtr tabPtr;
+ tabPtr.i = signal->theData[1];
+ ptrCheckGuard(tabPtr, ctabrecFileSize, tablerec);
+
+ ndbrequire(tabPtr.p->tableStatus == Tablerec::PREP_DROP_TABLE_ONGOING);
+
+ if(tabPtr.p->usageCount > 0){
+ jam();
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 4);
+ return;
+ }
+
+ bool lcpDone = true;
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+ if(lcpPtr.p->lcpState != LcpRecord::LCP_IDLE){
+ jam();
+
+ if(lcpPtr.p->currentFragment.lcpFragOrd.tableId == tabPtr.i){
+ jam();
+ lcpDone = false;
+ }
+
+ if(lcpPtr.p->lcpQueued &&
+ lcpPtr.p->queuedFragment.lcpFragOrd.tableId == tabPtr.i){
+ jam();
+ lcpDone = false;
+ }
+ }
+
+ if(!lcpDone){
+ jam();
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 4);
+ return;
+ }
+
+ tabPtr.p->tableStatus = Tablerec::PREP_DROP_TABLE_DONE;
+
+ WaitDropTabConf * conf = (WaitDropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ for(Uint32 i = 1; i<MAX_NDB_NODES; i++){
+ if(tabPtr.p->waitingTC.get(i)){
+ tabPtr.p->waitingTC.clear(i);
+ sendSignal(calcTcBlockRef(i), GSN_WAIT_DROP_TAB_CONF, signal,
+ WaitDropTabConf::SignalLength, JBB);
+ }
+ if(tabPtr.p->waitingDIH.get(i)){
+ tabPtr.p->waitingDIH.clear(i);
+ sendSignal(calcDihBlockRef(i), GSN_WAIT_DROP_TAB_CONF, signal,
+ WaitDropTabConf::SignalLength, JBB);
+ }
+ }
+}
+
+void
+Dblqh::execWAIT_DROP_TAB_REQ(Signal* signal){
+ jamEntry();
+ WaitDropTabReq * req = (WaitDropTabReq*)signal->getDataPtr();
+
+ TablerecPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFileSize, tablerec);
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 nodeId = refToNode(senderRef);
+ Uint32 blockNo = refToBlock(senderRef);
+
+ if(tabPtr.p->tableStatus == Tablerec::PREP_DROP_TABLE_ONGOING){
+ jam();
+ switch(blockNo){
+ case DBTC:
+ tabPtr.p->waitingTC.set(nodeId);
+ break;
+ case DBDIH:
+ tabPtr.p->waitingDIH.set(nodeId);
+ break;
+ default:
+ ndbrequire(false);
+ }
+ return;
+ }
+
+ if(tabPtr.p->tableStatus == Tablerec::PREP_DROP_TABLE_DONE){
+ jam();
+ WaitDropTabConf * conf = (WaitDropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ sendSignal(senderRef, GSN_WAIT_DROP_TAB_CONF, signal,
+ WaitDropTabConf::SignalLength, JBB);
+ return;
+ }
+
+ WaitDropTabRef * ref = (WaitDropTabRef*)signal->getDataPtrSend();
+ ref->tableId = tabPtr.i;
+ ref->senderRef = reference();
+
+ bool ok = false;
+ switch(tabPtr.p->tableStatus){
+ case Tablerec::TABLE_DEFINED:
+ ok = true;
+ ref->errorCode = WaitDropTabRef::IllegalTableState;
+ break;
+ case Tablerec::NOT_DEFINED:
+ ok = true;
+ ref->errorCode = WaitDropTabRef::NoSuchTable;
+ break;
+ case Tablerec::ADD_TABLE_ONGOING:
+ ok = true;
+ ref->errorCode = WaitDropTabRef::IllegalTableState;
+ break;
+ case Tablerec::PREP_DROP_TABLE_ONGOING:
+ case Tablerec::PREP_DROP_TABLE_DONE:
+ // Should have been take care of above
+ ndbrequire(false);
+ }
+ ndbrequire(ok);
+ ref->tableStatus = tabPtr.p->tableStatus;
+ sendSignal(senderRef, GSN_WAIT_DROP_TAB_REF, signal,
+ WaitDropTabRef::SignalLength, JBB);
+ return;
+}
+
+void
+Dblqh::execDROP_TAB_REQ(Signal* signal){
+ jamEntry();
+
+ DropTabReq* req = (DropTabReq*)signal->getDataPtr();
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+
+ TablerecPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFileSize, tablerec);
+
+ do {
+ if(req->requestType == DropTabReq::RestartDropTab){
+ jam();
+ break;
+ }
+
+ if(req->requestType == DropTabReq::OnlineDropTab){
+ jam();
+ Uint32 errCode = 0;
+ errCode = checkDropTabState(tabPtr.p->tableStatus, GSN_DROP_TAB_REQ);
+ if(errCode != 0){
+ jam();
+
+ DropTabRef* ref = (DropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = errCode;
+ sendSignal(senderRef, GSN_DROP_TAB_REF, signal,
+ DropTabRef::SignalLength, JBB);
+ return;
+ }
+ }
+
+ removeTable(tabPtr.i);
+
+ } while(false);
+
+ ndbrequire(tabPtr.p->usageCount == 0);
+ tabPtr.p->tableStatus = Tablerec::NOT_DEFINED;
+
+ DropTabConf * const dropConf = (DropTabConf *)signal->getDataPtrSend();
+ dropConf->senderRef = reference();
+ dropConf->senderData = senderData;
+ dropConf->tableId = tabPtr.i;
+ sendSignal(senderRef, GSN_DROP_TAB_CONF,
+ signal, DropTabConf::SignalLength, JBB);
+}
+
+Uint32
+Dblqh::checkDropTabState(Tablerec::TableStatus status, Uint32 gsn) const{
+
+ if(gsn == GSN_PREP_DROP_TAB_REQ){
+ switch(status){
+ case Tablerec::NOT_DEFINED:
+ jam();
+ // Fall through
+ case Tablerec::ADD_TABLE_ONGOING:
+ jam();
+ return PrepDropTabRef::NoSuchTable;
+ break;
+ case Tablerec::PREP_DROP_TABLE_ONGOING:
+ jam();
+ return PrepDropTabRef::PrepDropInProgress;
+ break;
+ case Tablerec::PREP_DROP_TABLE_DONE:
+ jam();
+ return PrepDropTabRef::DropInProgress;
+ break;
+ case Tablerec::TABLE_DEFINED:
+ jam();
+ return 0;
+ break;
+ }
+ ndbrequire(0);
+ }
+
+ if(gsn == GSN_DROP_TAB_REQ){
+ switch(status){
+ case Tablerec::NOT_DEFINED:
+ jam();
+ // Fall through
+ case Tablerec::ADD_TABLE_ONGOING:
+ jam();
+ return DropTabRef::NoSuchTable;
+ break;
+ case Tablerec::PREP_DROP_TABLE_ONGOING:
+ jam();
+ return DropTabRef::PrepDropInProgress;
+ break;
+ case Tablerec::PREP_DROP_TABLE_DONE:
+ jam();
+ return 0;
+ break;
+ case Tablerec::TABLE_DEFINED:
+ jam();
+ return DropTabRef::DropWoPrep;
+ }
+ ndbrequire(0);
+ }
+ ndbrequire(0);
+ return RNIL;
+}
+
+void Dblqh::removeTable(Uint32 tableId)
+{
+ tabptr.i = tableId;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+
+ for (Uint32 i = (MAX_FRAG_PER_NODE - 1); (Uint32)~i; i--) {
+ jam();
+ if (tabptr.p->fragid[i] != ZNIL) {
+ jam();
+ deleteFragrec(tabptr.p->fragid[i]);
+ }//if
+ }//for
+}//Dblqh::removeTable()
+
+void
+Dblqh::execALTER_TAB_REQ(Signal* signal)
+{
+ jamEntry();
+ AlterTabReq* const req = (AlterTabReq*)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 changeMask = req->changeMask;
+ const Uint32 tableId = req->tableId;
+ const Uint32 tableVersion = req->tableVersion;
+ const Uint32 gci = req->gci;
+ AlterTabReq::RequestType requestType =
+ (AlterTabReq::RequestType) req->requestType;
+
+ TablerecPtr tablePtr;
+ tablePtr.i = tableId;
+ ptrCheckGuard(tablePtr, ctabrecFileSize, tablerec);
+ tablePtr.p->schemaVersion = tableVersion;
+
+ // Request handled successfully
+ AlterTabConf * conf = (AlterTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->changeMask = changeMask;
+ conf->tableId = tableId;
+ conf->tableVersion = tableVersion;
+ conf->gci = gci;
+ conf->requestType = requestType;
+ sendSignal(senderRef, GSN_ALTER_TAB_CONF, signal,
+ AlterTabConf::SignalLength, JBB);
+}
+
+/* ************************************************************************>>
+ * TIME_SIGNAL: Handles time-out of local operations. This is a clean-up
+ * handler. If no other measure has succeeded in cleaning up after time-outs
+ * or else then this routine will remove the transaction after 120 seconds of
+ * inactivity. The check is performed once per 10 second. Sender is QMGR.
+ * ************************************************************************>> */
+void Dblqh::execTIME_SIGNAL(Signal* signal)
+{
+ jamEntry();
+ cLqhTimeOutCount++;
+ cLqhTimeOutCheckCount++;
+ if ((cCounterAccCommitBlocked > 0) ||
+ (cCounterTupCommitBlocked > 0)) {
+ jam();
+ signal->theData[0] = NDB_LE_UndoLogBlocked;
+ signal->theData[1] = cCounterTupCommitBlocked;
+ signal->theData[2] = cCounterAccCommitBlocked;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+
+ cCounterTupCommitBlocked = 0;
+ cCounterAccCommitBlocked = 0;
+ }//if
+ if (cLqhTimeOutCheckCount < 10) {
+ jam();
+ return;
+ }//if
+ cLqhTimeOutCheckCount = 0;
+#ifdef VM_TRACE
+ TcConnectionrecPtr tTcConptr;
+
+ for (tTcConptr.i = 0; tTcConptr.i < ctcConnectrecFileSize;
+ tTcConptr.i++) {
+ jam();
+ ptrAss(tTcConptr, tcConnectionrec);
+ if ((tTcConptr.p->tcTimer != 0) &&
+ ((tTcConptr.p->tcTimer + 120) < cLqhTimeOutCount)) {
+ ndbout << "Dblqh::execTIME_SIGNAL"<<endl
+ << "Timeout found in tcConnectRecord " <<tTcConptr.i<<endl
+ << " cLqhTimeOutCount = " << cLqhTimeOutCount << endl
+ << " tcTimer="<<tTcConptr.p->tcTimer<<endl
+ << " tcTimer+120="<<tTcConptr.p->tcTimer + 120<<endl;
+
+ ndbout << " transactionState = " << tTcConptr.p->transactionState<<endl;
+ ndbout << " operation = " << tTcConptr.p->operation<<endl;
+ ndbout << " tcNodeFailrec = " << tTcConptr.p->tcNodeFailrec
+ << " seqNoReplica = " << tTcConptr.p->seqNoReplica
+ << " simpleRead = " << tTcConptr.p->simpleRead
+ << endl;
+ ndbout << " replicaType = " << tTcConptr.p->replicaType
+ << " reclenAiLqhkey = " << tTcConptr.p->reclenAiLqhkey
+ << " opExec = " << tTcConptr.p->opExec
+ << endl;
+ ndbout << " opSimple = " << tTcConptr.p->opSimple
+ << " nextSeqNoReplica = " << tTcConptr.p->nextSeqNoReplica
+ << " lockType = " << tTcConptr.p->lockType
+ << " localFragptr = " << tTcConptr.p->localFragptr
+ << endl;
+ ndbout << " lastReplicaNo = " << tTcConptr.p->lastReplicaNo
+ << " indTakeOver = " << tTcConptr.p->indTakeOver
+ << " dirtyOp = " << tTcConptr.p->dirtyOp
+ << endl;
+ ndbout << " activeCreat = " << tTcConptr.p->activeCreat
+ << " tcBlockref = " << hex << tTcConptr.p->tcBlockref
+ << " reqBlockref = " << hex << tTcConptr.p->reqBlockref
+ << " primKeyLen = " << tTcConptr.p->primKeyLen
+ << endl;
+ ndbout << " nextReplica = " << tTcConptr.p->nextReplica
+ << " tcBlockref = " << hex << tTcConptr.p->tcBlockref
+ << " reqBlockref = " << hex << tTcConptr.p->reqBlockref
+ << " primKeyLen = " << tTcConptr.p->primKeyLen
+ << endl;
+ ndbout << " logStopPageNo = " << tTcConptr.p->logStopPageNo
+ << " logStartPageNo = " << tTcConptr.p->logStartPageNo
+ << " logStartPageIndex = " << tTcConptr.p->logStartPageIndex
+ << endl;
+ ndbout << " errorCode = " << tTcConptr.p->errorCode
+ << " clientBlockref = " << hex << tTcConptr.p->clientBlockref
+ << " applRef = " << hex << tTcConptr.p->applRef
+ << " totSendlenAi = " << tTcConptr.p->totSendlenAi
+ << endl;
+ ndbout << " totReclenAi = " << tTcConptr.p->totReclenAi
+ << " tcScanRec = " << tTcConptr.p->tcScanRec
+ << " tcScanInfo = " << tTcConptr.p->tcScanInfo
+ << " tcOprec = " << hex << tTcConptr.p->tcOprec
+ << endl;
+ ndbout << " tableref = " << tTcConptr.p->tableref
+ << " simpleTcConnect = " << tTcConptr.p->simpleTcConnect
+ << " storedProcId = " << tTcConptr.p->storedProcId
+ << " schemaVersion = " << tTcConptr.p->schemaVersion
+ << endl;
+ ndbout << " reqinfo = " << tTcConptr.p->reqinfo
+ << " reqRef = " << tTcConptr.p->reqRef
+ << " readlenAi = " << tTcConptr.p->readlenAi
+ << " prevTc = " << tTcConptr.p->prevTc
+ << endl;
+ ndbout << " prevLogTcrec = " << tTcConptr.p->prevLogTcrec
+ << " prevHashRec = " << tTcConptr.p->prevHashRec
+ << " nodeAfterNext0 = " << tTcConptr.p->nodeAfterNext[0]
+ << " nodeAfterNext1 = " << tTcConptr.p->nodeAfterNext[1]
+ << endl;
+ ndbout << " nextTcConnectrec = " << tTcConptr.p->nextTcConnectrec
+ << " nextTc = " << tTcConptr.p->nextTc
+ << " nextTcLogQueue = " << tTcConptr.p->nextTcLogQueue
+ << " nextLogTcrec = " << tTcConptr.p->nextLogTcrec
+ << endl;
+ ndbout << " nextHashRec = " << tTcConptr.p->nextHashRec
+ << " logWriteState = " << tTcConptr.p->logWriteState
+ << " logStartFileNo = " << tTcConptr.p->logStartFileNo
+ << " listState = " << tTcConptr.p->listState
+ << endl;
+ ndbout << " lastAttrinbuf = " << tTcConptr.p->lastAttrinbuf
+ << " lastTupkeybuf = " << tTcConptr.p->lastTupkeybuf
+ << " hashValue = " << tTcConptr.p->hashValue
+ << endl;
+ ndbout << " gci = " << tTcConptr.p->gci
+ << " fragmentptr = " << tTcConptr.p->fragmentptr
+ << " fragmentid = " << tTcConptr.p->fragmentid
+ << " firstTupkeybuf = " << tTcConptr.p->firstTupkeybuf
+ << endl;
+ ndbout << " firstAttrinbuf = " << tTcConptr.p->firstAttrinbuf
+ << " currTupAiLen = " << tTcConptr.p->currTupAiLen
+ << " currReclenAi = " << tTcConptr.p->currReclenAi
+ << endl;
+ ndbout << " tcTimer = " << tTcConptr.p->tcTimer
+ << " clientConnectrec = " << tTcConptr.p->clientConnectrec
+ << " applOprec = " << hex << tTcConptr.p->applOprec
+ << " abortState = " << tTcConptr.p->abortState
+ << endl;
+ ndbout << " transid0 = " << hex << tTcConptr.p->transid[0]
+ << " transid1 = " << hex << tTcConptr.p->transid[1]
+ << " tupkeyData0 = " << tTcConptr.p->tupkeyData[0]
+ << " tupkeyData1 = " << tTcConptr.p->tupkeyData[1]
+ << endl;
+ ndbout << " tupkeyData2 = " << tTcConptr.p->tupkeyData[2]
+ << " tupkeyData3 = " << tTcConptr.p->tupkeyData[3]
+ << endl;
+ switch (tTcConptr.p->transactionState) {
+
+ case TcConnectionrec::SCAN_STATE_USED:
+ if (tTcConptr.p->tcScanRec < cscanrecFileSize){
+ ScanRecordPtr TscanPtr;
+ c_scanRecordPool.getPtr(TscanPtr, tTcConptr.p->tcScanRec);
+ ndbout << " scanState = " << TscanPtr.p->scanState << endl;
+ //TscanPtr.p->scanLocalref[2];
+ ndbout << " copyPtr="<<TscanPtr.p->copyPtr
+ << " scanAccPtr="<<TscanPtr.p->scanAccPtr
+ << " scanAiLength="<<TscanPtr.p->scanAiLength
+ << endl;
+ ndbout << " m_curr_batch_size_rows="<<
+ TscanPtr.p->m_curr_batch_size_rows
+ << " m_max_batch_size_rows="<<
+ TscanPtr.p->m_max_batch_size_rows
+ << " scanErrorCounter="<<TscanPtr.p->scanErrorCounter
+ << " scanLocalFragid="<<TscanPtr.p->scanLocalFragid
+ << endl;
+ ndbout << " scanSchemaVersion="<<TscanPtr.p->scanSchemaVersion
+ << " scanStoredProcId="<<TscanPtr.p->scanStoredProcId
+ << " scanTcrec="<<TscanPtr.p->scanTcrec
+ << endl;
+ ndbout << " scanType="<<TscanPtr.p->scanType
+ << " scanApiBlockref="<<TscanPtr.p->scanApiBlockref
+ << " scanNodeId="<<TscanPtr.p->scanNodeId
+ << " scanCompletedStatus="<<TscanPtr.p->scanCompletedStatus
+ << endl;
+ ndbout << " scanFlag="<<TscanPtr.p->scanFlag
+ << " scanLockHold="<<TscanPtr.p->scanLockHold
+ << " scanLockMode="<<TscanPtr.p->scanLockMode
+ << " scanNumber="<<TscanPtr.p->scanNumber
+ << endl;
+ ndbout << " scanReleaseCounter="<<TscanPtr.p->scanReleaseCounter
+ << " scanTcWaiting="<<TscanPtr.p->scanTcWaiting
+ << " scanKeyinfoFlag="<<TscanPtr.p->scanKeyinfoFlag
+ << endl;
+ }else{
+ ndbout << "No connected scan record found" << endl;
+ }
+ break;
+ default:
+ break;
+ }//switch
+
+ // Reset the timer
+ tTcConptr.p->tcTimer = 0;
+ }//if
+ }//for
+#endif
+#ifdef VM_TRACE
+ for (lfoPtr.i = 0; lfoPtr.i < clfoFileSize; lfoPtr.i++) {
+ ptrAss(lfoPtr, logFileOperationRecord);
+ if ((lfoPtr.p->lfoTimer != 0) &&
+ ((lfoPtr.p->lfoTimer + 120) < cLqhTimeOutCount)) {
+ ndbout << "We have lost LFO record" << endl;
+ ndbout << "index = " << lfoPtr.i;
+ ndbout << "State = " << lfoPtr.p->lfoState;
+ ndbout << " Page No = " << lfoPtr.p->lfoPageNo;
+ ndbout << " noPagesRw = " << lfoPtr.p->noPagesRw;
+ ndbout << "lfoWordWritten = " << lfoPtr.p->lfoWordWritten << endl;
+ lfoPtr.p->lfoTimer = cLqhTimeOutCount;
+ }//if
+ }//for
+
+#endif
+
+#if 0
+ LcpRecordPtr TlcpPtr;
+ // Print information about the current local checkpoint
+ TlcpPtr.i = 0;
+ ptrAss(TlcpPtr, lcpRecord);
+ ndbout << "Information about LCP in this LQH" << endl
+ << " lcpState="<<TlcpPtr.p->lcpState<<endl
+ << " firstLcpLocAcc="<<TlcpPtr.p->firstLcpLocAcc<<endl
+ << " firstLcpLocTup="<<TlcpPtr.p->firstLcpLocTup<<endl
+ << " lcpAccptr="<<TlcpPtr.p->lcpAccptr<<endl
+ << " lastFragmentFlag="<<TlcpPtr.p->lastFragmentFlag<<endl
+ << " lcpQueued="<<TlcpPtr.p->lcpQueued<<endl
+ << " reportEmptyref="<< TlcpPtr.p->reportEmptyRef<<endl
+ << " reportEmpty="<<TlcpPtr.p->reportEmpty<<endl;
+#endif
+}//Dblqh::execTIME_SIGNAL()
+
+/* ######################################################################### */
+/* ####### EXECUTION MODULE ####### */
+/* THIS MODULE HANDLES THE RECEPTION OF LQHKEYREQ AND ALL PROCESSING */
+/* OF OPERATIONS ON BEHALF OF THIS REQUEST. THIS DOES ALSO INVOLVE */
+/* RECEPTION OF VARIOUS TYPES OF ATTRINFO AND KEYINFO. IT DOES ALSO */
+/* INVOLVE COMMUNICATION WITH ACC AND TUP. */
+/* ######################################################################### */
+
+void Dblqh::noFreeRecordLab(Signal* signal,
+ const LqhKeyReq * lqhKeyReq,
+ Uint32 errCode)
+{
+ jamEntry();
+ const Uint32 transid1 = lqhKeyReq->transId1;
+ const Uint32 transid2 = lqhKeyReq->transId2;
+ const Uint32 reqInfo = lqhKeyReq->requestInfo;
+
+ if(errCode == ZNO_FREE_MARKER_RECORDS_ERROR ||
+ errCode == ZNODE_SHUTDOWN_IN_PROGESS){
+ releaseTcrec(signal, tcConnectptr);
+ }
+
+ if (LqhKeyReq::getSimpleFlag(reqInfo) &&
+ LqhKeyReq::getOperation(reqInfo) == ZREAD){
+ jam();
+ ndbrequire(LqhKeyReq::getApplicationAddressFlag(reqInfo));
+ const Uint32 apiRef = lqhKeyReq->variableData[0];
+ const Uint32 apiOpRec = lqhKeyReq->variableData[1];
+
+ TcKeyRef * const tcKeyRef = (TcKeyRef *) signal->getDataPtrSend();
+
+ tcKeyRef->connectPtr = apiOpRec;
+ tcKeyRef->transId[0] = transid1;
+ tcKeyRef->transId[1] = transid2;
+ tcKeyRef->errorCode = errCode;
+ sendSignal(apiRef, GSN_TCKEYREF, signal, TcKeyRef::SignalLength, JBB);
+ } else {
+ jam();
+
+ 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 = (LqhKeyRef*)signal->getDataPtrSend();
+ ref->userRef = clientPtr;
+ ref->connectPtr = TcOprec;
+ ref->errorCode = errCode;
+ ref->transId1 = transid1;
+ ref->transId2 = transid2;
+ sendSignal(signal->senderBlockRef(), GSN_LQHKEYREF, signal,
+ LqhKeyRef::SignalLength, JBB);
+ }//if
+ return;
+}//Dblqh::noFreeRecordLab()
+
+void Dblqh::LQHKEY_abort(Signal* signal, int errortype)
+{
+ switch (errortype) {
+ case 0:
+ jam();
+ terrorCode = ZCOPY_NODE_ERROR;
+ break;
+ case 1:
+ jam();
+ terrorCode = ZNO_FREE_LQH_CONNECTION;
+ break;
+ case 2:
+ jam();
+ terrorCode = signal->theData[1];
+ break;
+ case 3:
+ jam();
+ ndbrequire((tcConnectptr.p->transactionState == TcConnectionrec::WAIT_ACC_ABORT) ||
+ (tcConnectptr.p->transactionState == TcConnectionrec::ABORT_STOPPED) ||
+ (tcConnectptr.p->transactionState == TcConnectionrec::ABORT_QUEUED));
+ return;
+ break;
+ case 4:
+ jam();
+ if(tabptr.p->tableStatus == Tablerec::NOT_DEFINED){
+ jam();
+ terrorCode = ZTABLE_NOT_DEFINED;
+ } else if (tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_ONGOING ||
+ tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_DONE){
+ jam();
+ terrorCode = ZDROP_TABLE_IN_PROGRESS;
+ } else {
+ ndbrequire(0);
+ }
+ break;
+ case 5:
+ jam();
+ terrorCode = ZINVALID_SCHEMA_VERSION;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ abortErrorLab(signal);
+}//Dblqh::LQHKEY_abort()
+
+void Dblqh::LQHKEY_error(Signal* signal, int errortype)
+{
+ switch (errortype) {
+ case 0:
+ jam();
+ break;
+ case 1:
+ jam();
+ break;
+ case 2:
+ jam();
+ break;
+ case 3:
+ jam();
+ break;
+ case 4:
+ jam();
+ break;
+ case 5:
+ jam();
+ break;
+ case 6:
+ jam();
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ ndbrequire(false);
+}//Dblqh::LQHKEY_error()
+
+void Dblqh::execLQHKEYREF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ terrorCode = signal->theData[2];
+ Uint32 transid1 = signal->theData[3];
+ Uint32 transid2 = signal->theData[4];
+ if (tcConnectptr.i >= ctcConnectrecFileSize) {
+ errorReport(signal, 3);
+ return;
+ }//if
+/*------------------------------------------------------------------*/
+/* WE HAVE TO CHECK THAT THE SIGNAL DO NOT BELONG TO SOMETHING*/
+/* REMOVED DUE TO A TIME-OUT. */
+/*------------------------------------------------------------------*/
+ ptrAss(tcConnectptr, tcConnectionrec);
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ switch (regTcPtr->connectState) {
+ case TcConnectionrec::CONNECTED:
+ jam();
+ if ((regTcPtr->transid[0] != transid1) ||
+ (regTcPtr->transid[1] != transid2)) {
+ warningReport(signal, 14);
+ return;
+ }//if
+ if (regTcPtr->abortState != TcConnectionrec::ABORT_IDLE) {
+ warningReport(signal, 15);
+ return;
+ }//if
+ abortErrorLab(signal);
+ return;
+ break;
+ case TcConnectionrec::LOG_CONNECTED:
+ jam();
+ logLqhkeyrefLab(signal);
+ return;
+ break;
+ case TcConnectionrec::COPY_CONNECTED:
+ jam();
+ copyLqhKeyRefLab(signal);
+ return;
+ break;
+ default:
+ warningReport(signal, 16);
+ return;
+ break;
+ }//switch
+}//Dblqh::execLQHKEYREF()
+
+/* -------------------------------------------------------------------------- */
+/* ------- ENTER PACKED_SIGNAL ------- */
+/* Execution of packed signal. The packed signal can contain COMMIT, COMPLETE */
+/* or LQHKEYCONF signals. These signals will be executed by their resp. exec */
+/* functions. */
+/* -------------------------------------------------------------------------- */
+void Dblqh::execPACKED_SIGNAL(Signal* signal)
+{
+ Uint32 Tstep = 0;
+ Uint32 Tlength;
+ Uint32 TpackedData[28];
+ Uint32 sig0, sig1, sig2, sig3 ,sig4, sig5, sig6;
+
+ jamEntry();
+ Tlength = signal->length();
+ ndbrequire(Tlength <= 25);
+ MEMCOPY_NO_WORDS(&TpackedData[0], &signal->theData[0], Tlength);
+ while (Tlength > Tstep) {
+ switch (TpackedData[Tstep] >> 28) {
+ case ZCOMMIT:
+ jam();
+ sig0 = TpackedData[Tstep + 0] & 0x0FFFFFFF;
+ sig1 = TpackedData[Tstep + 1];
+ sig2 = TpackedData[Tstep + 2];
+ sig3 = TpackedData[Tstep + 3];
+ signal->theData[0] = sig0;
+ signal->theData[1] = sig1;
+ signal->theData[2] = sig2;
+ signal->theData[3] = sig3;
+ signal->header.theLength = 4;
+ execCOMMIT(signal);
+ Tstep += 4;
+ break;
+ case ZCOMPLETE:
+ jam();
+ sig0 = TpackedData[Tstep + 0] & 0x0FFFFFFF;
+ sig1 = TpackedData[Tstep + 1];
+ sig2 = TpackedData[Tstep + 2];
+ signal->theData[0] = sig0;
+ signal->theData[1] = sig1;
+ signal->theData[2] = sig2;
+ signal->header.theLength = 3;
+ execCOMPLETE(signal);
+ Tstep += 3;
+ break;
+ case ZLQHKEYCONF: {
+ jam();
+ LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+
+ sig0 = TpackedData[Tstep + 0] & 0x0FFFFFFF;
+ sig1 = TpackedData[Tstep + 1];
+ sig2 = TpackedData[Tstep + 2];
+ sig3 = TpackedData[Tstep + 3];
+ sig4 = TpackedData[Tstep + 4];
+ sig5 = TpackedData[Tstep + 5];
+ sig6 = TpackedData[Tstep + 6];
+ lqhKeyConf->connectPtr = sig0;
+ lqhKeyConf->opPtr = sig1;
+ lqhKeyConf->userRef = sig2;
+ lqhKeyConf->readLen = sig3;
+ lqhKeyConf->transId1 = sig4;
+ lqhKeyConf->transId2 = sig5;
+ lqhKeyConf->noFiredTriggers = sig6;
+ execLQHKEYCONF(signal);
+ Tstep += LqhKeyConf::SignalLength;
+ break;
+ }
+ case ZREMOVE_MARKER:
+ jam();
+ sig0 = TpackedData[Tstep + 1];
+ sig1 = TpackedData[Tstep + 2];
+ signal->theData[0] = sig0;
+ signal->theData[1] = sig1;
+ signal->header.theLength = 2;
+ execREMOVE_MARKER_ORD(signal);
+ Tstep += 3;
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ }//switch
+ }//while
+ ndbrequire(Tlength == Tstep);
+ return;
+}//Dblqh::execPACKED_SIGNAL()
+
+void
+Dblqh::execREMOVE_MARKER_ORD(Signal* signal)
+{
+ CommitAckMarker key;
+ key.transid1 = signal->theData[0];
+ key.transid2 = signal->theData[1];
+ jamEntry();
+
+ CommitAckMarkerPtr removedPtr;
+ m_commitAckMarkerHash.release(removedPtr, key);
+ ndbrequire(removedPtr.i != RNIL);
+#ifdef MARKER_TRACE
+ ndbout_c("Rem marker[%.8x %.8x]", key.transid1, key.transid2);
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* ------- ENTER SEND_PACKED ------- */
+/* Used to force a packed signal to be sent if local signal buffer is not */
+/* empty. */
+/* -------------------------------------------------------------------------- */
+void Dblqh::execSEND_PACKED(Signal* signal)
+{
+ HostRecordPtr Thostptr;
+ UintR i;
+ UintR TpackedListIndex = cpackedListIndex;
+ jamEntry();
+ for (i = 0; i < TpackedListIndex; i++) {
+ Thostptr.i = cpackedList[i];
+ ptrAss(Thostptr, hostRecord);
+ jam();
+ ndbrequire(Thostptr.i - 1 < MAX_NDB_NODES - 1);
+ if (Thostptr.p->noOfPackedWordsLqh > 0) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ }//if
+ if (Thostptr.p->noOfPackedWordsTc > 0) {
+ jam();
+ sendPackedSignalTc(signal, Thostptr.p);
+ }//if
+ Thostptr.p->inPackedList = false;
+ }//for
+ cpackedListIndex = 0;
+ return;
+}//Dblqh::execSEND_PACKED()
+
+void
+Dblqh::updatePackedList(Signal* signal, HostRecord * ahostptr, Uint16 hostId)
+{
+ Uint32 TpackedListIndex = cpackedListIndex;
+ if (ahostptr->inPackedList == false) {
+ jam();
+ ahostptr->inPackedList = true;
+ cpackedList[TpackedListIndex] = hostId;
+ cpackedListIndex = TpackedListIndex + 1;
+ }//if
+}//Dblqh::updatePackedList()
+
+void
+Dblqh::execREAD_PSUEDO_REQ(Signal* signal){
+ jamEntry();
+ TcConnectionrecPtr regTcPtr;
+ regTcPtr.i = signal->theData[0];
+ ptrCheckGuard(regTcPtr, ctcConnectrecFileSize, tcConnectionrec);
+
+ if(signal->theData[1] != AttributeHeader::RANGE_NO)
+ {
+ jam();
+ FragrecordPtr regFragptr;
+ regFragptr.i = regTcPtr.p->fragmentptr;
+ ptrCheckGuard(regFragptr, cfragrecFileSize, fragrecord);
+
+ signal->theData[0] = regFragptr.p->accFragptr[regTcPtr.p->localFragptr];
+ EXECUTE_DIRECT(DBACC, GSN_READ_PSUEDO_REQ, signal, 2);
+ }
+ else
+ {
+ signal->theData[0] = regTcPtr.p->m_scan_curr_range_no;
+ }
+}
+
+/* ************>> */
+/* TUPKEYCONF > */
+/* ************>> */
+void Dblqh::execTUPKEYCONF(Signal* signal)
+{
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ const TupKeyConf * const tupKeyConf = (TupKeyConf *)signal->getDataPtr();
+ Uint32 tcIndex = tupKeyConf->userPtr;
+ jamEntry();
+ tcConnectptr.i = tcIndex;
+ ptrCheckGuard(tcConnectptr, ttcConnectrecFileSize, regTcConnectionrec);
+ switch (tcConnectptr.p->transactionState) {
+ case TcConnectionrec::WAIT_TUP:
+ jam();
+ if (tcConnectptr.p->seqNoReplica == 0) // Primary replica
+ tcConnectptr.p->noFiredTriggers = tupKeyConf->noFiredTriggers;
+ tupkeyConfLab(signal);
+ break;
+ case TcConnectionrec::COPY_TUPKEY:
+ jam();
+ copyTupkeyConfLab(signal);
+ break;
+ case TcConnectionrec::SCAN_TUPKEY:
+ jam();
+ scanTupkeyConfLab(signal);
+ break;
+ case TcConnectionrec::WAIT_TUP_TO_ABORT:
+ jam();
+/* ------------------------------------------------------------------------- */
+// Abort was not ready to start until this signal came back. Now we are ready
+// to start the abort.
+/* ------------------------------------------------------------------------- */
+ releaseActiveFrag(signal);
+ abortCommonLab(signal);
+ break;
+ case TcConnectionrec::WAIT_ACC_ABORT:
+ case TcConnectionrec::ABORT_QUEUED:
+ jam();
+/* -------------------------------------------------------------------------- */
+/* IGNORE SINCE ABORT OF THIS OPERATION IS ONGOING ALREADY. */
+/* -------------------------------------------------------------------------- */
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dblqh::execTUPKEYCONF()
+
+/* ************> */
+/* TUPKEYREF > */
+/* ************> */
+void Dblqh::execTUPKEYREF(Signal* signal)
+{
+ const TupKeyRef * const tupKeyRef = (TupKeyRef *)signal->getDataPtr();
+
+ jamEntry();
+ tcConnectptr.i = tupKeyRef->userRef;
+ terrorCode = tupKeyRef->errorCode;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ switch (tcConnectptr.p->transactionState) {
+ case TcConnectionrec::WAIT_TUP:
+ jam();
+ releaseActiveFrag(signal);
+ abortErrorLab(signal);
+ break;
+ case TcConnectionrec::COPY_TUPKEY:
+ ndbrequire(false);
+ break;
+ case TcConnectionrec::SCAN_TUPKEY:
+ jam();
+ scanTupkeyRefLab(signal);
+ break;
+ case TcConnectionrec::WAIT_TUP_TO_ABORT:
+ jam();
+/* ------------------------------------------------------------------------- */
+// Abort was not ready to start until this signal came back. Now we are ready
+// to start the abort.
+/* ------------------------------------------------------------------------- */
+ releaseActiveFrag(signal);
+ abortCommonLab(signal);
+ break;
+ case TcConnectionrec::WAIT_ACC_ABORT:
+ case TcConnectionrec::ABORT_QUEUED:
+ jam();
+/* ------------------------------------------------------------------------- */
+/* IGNORE SINCE ABORT OF THIS OPERATION IS ONGOING ALREADY. */
+/* ------------------------------------------------------------------------- */
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dblqh::execTUPKEYREF()
+
+void Dblqh::sendPackedSignalLqh(Signal* signal, HostRecord * ahostptr)
+{
+ Uint32 noOfWords = ahostptr->noOfPackedWordsLqh;
+ BlockReference hostRef = ahostptr->hostLqhBlockRef;
+ MEMCOPY_NO_WORDS(&signal->theData[0],
+ &ahostptr->packedWordsLqh[0],
+ noOfWords);
+ sendSignal(hostRef, GSN_PACKED_SIGNAL, signal, noOfWords, JBB);
+ ahostptr->noOfPackedWordsLqh = 0;
+}//Dblqh::sendPackedSignalLqh()
+
+void Dblqh::sendPackedSignalTc(Signal* signal, HostRecord * ahostptr)
+{
+ Uint32 noOfWords = ahostptr->noOfPackedWordsTc;
+ BlockReference hostRef = ahostptr->hostTcBlockRef;
+ MEMCOPY_NO_WORDS(&signal->theData[0],
+ &ahostptr->packedWordsTc[0],
+ noOfWords);
+ sendSignal(hostRef, GSN_PACKED_SIGNAL, signal, noOfWords, JBB);
+ ahostptr->noOfPackedWordsTc = 0;
+}//Dblqh::sendPackedSignalTc()
+
+void Dblqh::sendCommitLqh(Signal* signal, BlockReference alqhBlockref)
+{
+ HostRecordPtr Thostptr;
+ Thostptr.i = refToNode(alqhBlockref);
+ ptrCheckGuard(Thostptr, chostFileSize, hostRecord);
+ if (Thostptr.p->noOfPackedWordsLqh > 21) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ Uint32 pos = Thostptr.p->noOfPackedWordsLqh;
+ Uint32 ptrAndType = tcConnectptr.p->clientConnectrec | (ZCOMMIT << 28);
+ Uint32 gci = tcConnectptr.p->gci;
+ Uint32 transid1 = tcConnectptr.p->transid[0];
+ Uint32 transid2 = tcConnectptr.p->transid[1];
+ Thostptr.p->packedWordsLqh[pos] = ptrAndType;
+ Thostptr.p->packedWordsLqh[pos + 1] = gci;
+ Thostptr.p->packedWordsLqh[pos + 2] = transid1;
+ Thostptr.p->packedWordsLqh[pos + 3] = transid2;
+ Thostptr.p->noOfPackedWordsLqh = pos + 4;
+}//Dblqh::sendCommitLqh()
+
+void Dblqh::sendCompleteLqh(Signal* signal, BlockReference alqhBlockref)
+{
+ HostRecordPtr Thostptr;
+ Thostptr.i = refToNode(alqhBlockref);
+ ptrCheckGuard(Thostptr, chostFileSize, hostRecord);
+ if (Thostptr.p->noOfPackedWordsLqh > 22) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ Uint32 pos = Thostptr.p->noOfPackedWordsLqh;
+ Uint32 ptrAndType = tcConnectptr.p->clientConnectrec | (ZCOMPLETE << 28);
+ Uint32 transid1 = tcConnectptr.p->transid[0];
+ Uint32 transid2 = tcConnectptr.p->transid[1];
+ Thostptr.p->packedWordsLqh[pos] = ptrAndType;
+ Thostptr.p->packedWordsLqh[pos + 1] = transid1;
+ Thostptr.p->packedWordsLqh[pos + 2] = transid2;
+ Thostptr.p->noOfPackedWordsLqh = pos + 3;
+}//Dblqh::sendCompleteLqh()
+
+void Dblqh::sendCommittedTc(Signal* signal, BlockReference atcBlockref)
+{
+ HostRecordPtr Thostptr;
+ Thostptr.i = refToNode(atcBlockref);
+ ptrCheckGuard(Thostptr, chostFileSize, hostRecord);
+ if (Thostptr.p->noOfPackedWordsTc > 22) {
+ jam();
+ sendPackedSignalTc(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ Uint32 pos = Thostptr.p->noOfPackedWordsTc;
+ Uint32 ptrAndType = tcConnectptr.p->clientConnectrec | (ZCOMMITTED << 28);
+ Uint32 transid1 = tcConnectptr.p->transid[0];
+ Uint32 transid2 = tcConnectptr.p->transid[1];
+ Thostptr.p->packedWordsTc[pos] = ptrAndType;
+ Thostptr.p->packedWordsTc[pos + 1] = transid1;
+ Thostptr.p->packedWordsTc[pos + 2] = transid2;
+ Thostptr.p->noOfPackedWordsTc = pos + 3;
+}//Dblqh::sendCommittedTc()
+
+void Dblqh::sendCompletedTc(Signal* signal, BlockReference atcBlockref)
+{
+ HostRecordPtr Thostptr;
+ Thostptr.i = refToNode(atcBlockref);
+ ptrCheckGuard(Thostptr, chostFileSize, hostRecord);
+ if (Thostptr.p->noOfPackedWordsTc > 22) {
+ jam();
+ sendPackedSignalTc(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ Uint32 pos = Thostptr.p->noOfPackedWordsTc;
+ Uint32 ptrAndType = tcConnectptr.p->clientConnectrec | (ZCOMPLETED << 28);
+ Uint32 transid1 = tcConnectptr.p->transid[0];
+ Uint32 transid2 = tcConnectptr.p->transid[1];
+ Thostptr.p->packedWordsTc[pos] = ptrAndType;
+ Thostptr.p->packedWordsTc[pos + 1] = transid1;
+ Thostptr.p->packedWordsTc[pos + 2] = transid2;
+ Thostptr.p->noOfPackedWordsTc = pos + 3;
+}//Dblqh::sendCompletedTc()
+
+void Dblqh::sendLqhkeyconfTc(Signal* signal, BlockReference atcBlockref)
+{
+ LqhKeyConf* lqhKeyConf;
+ HostRecordPtr Thostptr;
+
+ Thostptr.i = refToNode(atcBlockref);
+ ptrCheckGuard(Thostptr, chostFileSize, hostRecord);
+ if (refToBlock(atcBlockref) == DBTC) {
+ jam();
+/*******************************************************************
+// This signal was intended for DBTC as part of the normal transaction
+// execution.
+********************************************************************/
+ if (Thostptr.p->noOfPackedWordsTc > (25 - LqhKeyConf::SignalLength)) {
+ jam();
+ sendPackedSignalTc(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ lqhKeyConf = (LqhKeyConf *)
+ &Thostptr.p->packedWordsTc[Thostptr.p->noOfPackedWordsTc];
+ Thostptr.p->noOfPackedWordsTc += LqhKeyConf::SignalLength;
+ } else {
+ jam();
+/*******************************************************************
+// This signal was intended for DBLQH as part of log execution or
+// node recovery.
+********************************************************************/
+ if (Thostptr.p->noOfPackedWordsLqh > (25 - LqhKeyConf::SignalLength)) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ lqhKeyConf = (LqhKeyConf *)
+ &Thostptr.p->packedWordsLqh[Thostptr.p->noOfPackedWordsLqh];
+ Thostptr.p->noOfPackedWordsLqh += LqhKeyConf::SignalLength;
+ }//if
+ Uint32 ptrAndType = tcConnectptr.i | (ZLQHKEYCONF << 28);
+ Uint32 tcOprec = tcConnectptr.p->tcOprec;
+ Uint32 ownRef = cownref;
+ Uint32 readlenAi = tcConnectptr.p->readlenAi;
+ Uint32 transid1 = tcConnectptr.p->transid[0];
+ Uint32 transid2 = tcConnectptr.p->transid[1];
+ Uint32 noFiredTriggers = tcConnectptr.p->noFiredTriggers;
+ lqhKeyConf->connectPtr = ptrAndType;
+ lqhKeyConf->opPtr = tcOprec;
+ lqhKeyConf->userRef = ownRef;
+ lqhKeyConf->readLen = readlenAi;
+ lqhKeyConf->transId1 = transid1;
+ lqhKeyConf->transId2 = transid2;
+ lqhKeyConf->noFiredTriggers = noFiredTriggers;
+}//Dblqh::sendLqhkeyconfTc()
+
+/* ************************************************************************>>
+ * KEYINFO: Get tuple request from DBTC. Next step is to contact DBACC to get
+ * key to tuple if all key/attrinfo has been received, else for more attrinfo
+ * signals.
+ * ************************************************************************>> */
+void Dblqh::execKEYINFO(Signal* signal)
+{
+ Uint32 tcOprec = signal->theData[0];
+ Uint32 transid1 = signal->theData[1];
+ Uint32 transid2 = signal->theData[2];
+ jamEntry();
+ if (findTransaction(transid1, transid2, tcOprec) != ZOK) {
+ jam();
+ return;
+ }//if
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ TcConnectionrec::TransactionState state = regTcPtr->transactionState;
+ if (state != TcConnectionrec::WAIT_TUPKEYINFO &&
+ state != TcConnectionrec::WAIT_SCAN_AI)
+ {
+ jam();
+/*****************************************************************************/
+/* TRANSACTION WAS ABORTED, THIS IS MOST LIKELY A SIGNAL BELONGING TO THE */
+/* ABORTED TRANSACTION. THUS IGNORE THE SIGNAL. */
+/*****************************************************************************/
+ return;
+ }//if
+ Uint32 errorCode = handleLongTupKey(signal,
+ (Uint32)regTcPtr->save1,
+ (Uint32)regTcPtr->primKeyLen,
+ &signal->theData[3]);
+ if (errorCode != 0) {
+ if (errorCode == 1) {
+ jam();
+ return;
+ }//if
+ jam();
+ terrorCode = errorCode;
+ if(state == TcConnectionrec::WAIT_TUPKEYINFO)
+ abortErrorLab(signal);
+ else
+ abort_scan(signal, regTcPtr->tcScanRec, errorCode);
+ return;
+ }//if
+ if(state == TcConnectionrec::WAIT_TUPKEYINFO)
+ {
+ FragrecordPtr regFragptr;
+ regFragptr.i = regTcPtr->fragmentptr;
+ ptrCheckGuard(regFragptr, cfragrecFileSize, fragrecord);
+ fragptr = regFragptr;
+ endgettupkeyLab(signal);
+ }
+ return;
+}//Dblqh::execKEYINFO()
+
+/* ------------------------------------------------------------------------- */
+/* FILL IN KEY DATA INTO DATA BUFFERS. */
+/* ------------------------------------------------------------------------- */
+Uint32 Dblqh::handleLongTupKey(Signal* signal,
+ Uint32 keyLength,
+ Uint32 primKeyLength,
+ Uint32* dataPtr)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Uint32 dataPos = 0;
+ while (true) {
+ keyLength += 4;
+ if (cfirstfreeDatabuf == RNIL) {
+ jam();
+ return ZGET_DATAREC_ERROR;
+ }//if
+ seizeTupkeybuf(signal);
+ Databuf * const regDataPtr = databufptr.p;
+ Uint32 data0 = dataPtr[dataPos];
+ Uint32 data1 = dataPtr[dataPos + 1];
+ Uint32 data2 = dataPtr[dataPos + 2];
+ Uint32 data3 = dataPtr[dataPos + 3];
+ regDataPtr->data[0] = data0;
+ regDataPtr->data[1] = data1;
+ regDataPtr->data[2] = data2;
+ regDataPtr->data[3] = data3;
+ dataPos += 4;
+ if (keyLength < primKeyLength) {
+ if (dataPos > 16) {
+ jam();
+/* SAVE STATE AND WAIT FOR KEYINFO */
+ regTcPtr->save1 = keyLength;
+ return 1;
+ }//if
+ } else {
+ jam();
+ return 0;
+ }//if
+ }//while
+}//Dblqh::handleLongTupKey()
+
+/* ------------------------------------------------------------------------- */
+/* ------- HANDLE ATTRINFO SIGNALS ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+/* ************************************************************************>> */
+/* ATTRINFO: Continuation of KEYINFO signal (except for scans that do not use*/
+/* any KEYINFO). When all key and attribute info is received we contact DBACC*/
+/* for index handling. */
+/* ************************************************************************>> */
+void Dblqh::execATTRINFO(Signal* signal)
+{
+ Uint32 tcOprec = signal->theData[0];
+ Uint32 transid1 = signal->theData[1];
+ Uint32 transid2 = signal->theData[2];
+ jamEntry();
+ if (findTransaction(transid1,
+ transid2,
+ tcOprec) != ZOK) {
+ jam();
+ return;
+ }//if
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Uint32 length = signal->length() - 3;
+ Uint32 totReclenAi = regTcPtr->totReclenAi;
+ Uint32 currReclenAi = regTcPtr->currReclenAi + length;
+ Uint32* dataPtr = &signal->theData[3];
+ regTcPtr->currReclenAi = currReclenAi;
+ if (totReclenAi == currReclenAi) {
+ switch (regTcPtr->transactionState) {
+ case TcConnectionrec::WAIT_ATTR:
+ {
+ Fragrecord *regFragrecord = fragrecord;
+ Uint32 fragIndex = regTcPtr->fragmentptr;
+ Uint32 tfragrecFileSize = cfragrecFileSize;
+ jam();
+ fragptr.i = fragIndex;
+ ptrCheckGuard(fragptr, tfragrecFileSize, regFragrecord);
+ lqhAttrinfoLab(signal, dataPtr, length);
+ endgettupkeyLab(signal);
+ return;
+ break;
+ }
+ case TcConnectionrec::WAIT_SCAN_AI:
+ jam();
+ scanAttrinfoLab(signal, dataPtr, length);
+ return;
+ break;
+ case TcConnectionrec::WAIT_TUP_TO_ABORT:
+ case TcConnectionrec::LOG_ABORT_QUEUED:
+ case TcConnectionrec::ABORT_QUEUED:
+ case TcConnectionrec::ABORT_STOPPED:
+ case TcConnectionrec::WAIT_ACC_ABORT:
+ case TcConnectionrec::WAIT_AI_AFTER_ABORT:
+ jam();
+ aiStateErrorCheckLab(signal, dataPtr,length);
+ return;
+ break;
+ default:
+ jam();
+ ndbrequire(regTcPtr->abortState != TcConnectionrec::ABORT_IDLE);
+ break;
+ }//switch
+ } else if (currReclenAi < totReclenAi) {
+ jam();
+ switch (regTcPtr->transactionState) {
+ case TcConnectionrec::WAIT_ATTR:
+ jam();
+ lqhAttrinfoLab(signal, dataPtr, length);
+ return;
+ break;
+ case TcConnectionrec::WAIT_SCAN_AI:
+ jam();
+ scanAttrinfoLab(signal, dataPtr, length);
+ return;
+ break;
+ case TcConnectionrec::WAIT_TUP_TO_ABORT:
+ case TcConnectionrec::LOG_ABORT_QUEUED:
+ case TcConnectionrec::ABORT_QUEUED:
+ case TcConnectionrec::ABORT_STOPPED:
+ case TcConnectionrec::WAIT_ACC_ABORT:
+ case TcConnectionrec::WAIT_AI_AFTER_ABORT:
+ jam();
+ aiStateErrorCheckLab(signal, dataPtr, length);
+ return;
+ break;
+ default:
+ jam();
+ ndbrequire(regTcPtr->abortState != TcConnectionrec::ABORT_IDLE);
+ break;
+ }//switch
+ } else {
+ switch (regTcPtr->transactionState) {
+ case TcConnectionrec::WAIT_SCAN_AI:
+ jam();
+ scanAttrinfoLab(signal, dataPtr, length);
+ return;
+ break;
+ default:
+ ndbout_c("%d", regTcPtr->transactionState);
+ ndbrequire(false);
+ break;
+ }//switch
+ }//if
+ return;
+}//Dblqh::execATTRINFO()
+
+/* ************************************************************************>> */
+/* TUP_ATTRINFO: Interpreted execution in DBTUP generates redo-log info */
+/* which is sent back to DBLQH for logging. This is because the decision */
+/* to execute or not is made in DBTUP and thus we cannot start logging until */
+/* DBTUP part has been run. */
+/* ************************************************************************>> */
+void Dblqh::execTUP_ATTRINFO(Signal* signal)
+{
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ Uint32 length = signal->length() - 3;
+ Uint32 tcIndex = signal->theData[0];
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ jamEntry();
+ tcConnectptr.i = tcIndex;
+ ptrCheckGuard(tcConnectptr, ttcConnectrecFileSize, regTcConnectionrec);
+ ndbrequire(tcConnectptr.p->transactionState == TcConnectionrec::WAIT_TUP);
+ if (saveTupattrbuf(signal, &signal->theData[3], length) == ZOK) {
+ return;
+ } else {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* WE ARE WAITING FOR RESPONSE FROM TUP HERE. THUS WE NEED TO */
+/* GO THROUGH THE STATE MACHINE FOR THE OPERATION. */
+/* ------------------------------------------------------------------------- */
+ localAbortStateHandlerLab(signal);
+ }//if
+}//Dblqh::execTUP_ATTRINFO()
+
+/* ------------------------------------------------------------------------- */
+/* ------- HANDLE ATTRINFO FROM LQH ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::lqhAttrinfoLab(Signal* signal, Uint32* dataPtr, Uint32 length)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->operation != ZREAD) {
+ if (regTcPtr->opExec != 1) {
+ if (saveTupattrbuf(signal, dataPtr, length) == ZOK) {
+ ;
+ } else {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* WE MIGHT BE WAITING FOR RESPONSE FROM SOME BLOCK HERE. THUS WE NEED TO */
+/* GO THROUGH THE STATE MACHINE FOR THE OPERATION. */
+/* ------------------------------------------------------------------------- */
+ localAbortStateHandlerLab(signal);
+ return;
+ }//if
+ }//if
+ }//if
+ Uint32 sig0 = regTcPtr->tupConnectrec;
+ Uint32 blockNo = refToBlock(regTcPtr->tcTupBlockref);
+ signal->theData[0] = sig0;
+ EXECUTE_DIRECT(blockNo, GSN_ATTRINFO, signal, length + 3);
+ jamEntry();
+}//Dblqh::lqhAttrinfoLab()
+
+/* ------------------------------------------------------------------------- */
+/* ------ FIND TRANSACTION BY USING HASH TABLE ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+int Dblqh::findTransaction(UintR Transid1, UintR Transid2, UintR TcOprec)
+{
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ TcConnectionrecPtr locTcConnectptr;
+
+ Uint32 ThashIndex = (Transid1 ^ TcOprec) & 1023;
+ locTcConnectptr.i = ctransidHash[ThashIndex];
+ while (locTcConnectptr.i != RNIL) {
+ ptrCheckGuard(locTcConnectptr, ttcConnectrecFileSize, regTcConnectionrec);
+ if ((locTcConnectptr.p->transid[0] == Transid1) &&
+ (locTcConnectptr.p->transid[1] == Transid2) &&
+ (locTcConnectptr.p->tcOprec == TcOprec)) {
+/* FIRST PART OF TRANSACTION CORRECT */
+/* SECOND PART ALSO CORRECT */
+/* THE OPERATION RECORD POINTER IN TC WAS ALSO CORRECT */
+ jam();
+ tcConnectptr.i = locTcConnectptr.i;
+ tcConnectptr.p = locTcConnectptr.p;
+ return (int)ZOK;
+ }//if
+ jam();
+/* THIS WAS NOT THE TRANSACTION WHICH WAS SOUGHT */
+ locTcConnectptr.i = locTcConnectptr.p->nextHashRec;
+ }//while
+/* WE DID NOT FIND THE TRANSACTION, REPORT NOT FOUND */
+ return (int)ZNOT_FOUND;
+}//Dblqh::findTransaction()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SAVE ATTRINFO FROM TUP IN ATTRINBUF ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+int Dblqh::saveTupattrbuf(Signal* signal, Uint32* dataPtr, Uint32 length)
+{
+ Uint32 tfirstfreeAttrinbuf = cfirstfreeAttrinbuf;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Uint32 currTupAiLen = regTcPtr->currTupAiLen;
+ if (tfirstfreeAttrinbuf == RNIL) {
+ jam();
+ terrorCode = ZGET_ATTRINBUF_ERROR;
+ return ZGET_ATTRINBUF_ERROR;
+ }//if
+ seizeAttrinbuf(signal);
+ Attrbuf * const regAttrPtr = attrinbufptr.p;
+ MEMCOPY_NO_WORDS(&regAttrPtr->attrbuf[0], dataPtr, length);
+ regTcPtr->currTupAiLen = currTupAiLen + length;
+ regAttrPtr->attrbuf[ZINBUF_DATA_LEN] = length;
+ return ZOK;
+}//Dblqh::saveTupattrbuf()
+
+/* ==========================================================================
+ * ======= SEIZE ATTRIBUTE IN BUFFER =======
+ *
+ * GET A NEW ATTRINBUF AND SETS ATTRINBUFPTR.
+ * ========================================================================= */
+void Dblqh::seizeAttrinbuf(Signal* signal)
+{
+ AttrbufPtr tmpAttrinbufptr;
+ AttrbufPtr regAttrinbufptr;
+ Attrbuf *regAttrbuf = attrbuf;
+ Uint32 tattrinbufFileSize = cattrinbufFileSize;
+
+ regAttrinbufptr.i = seize_attrinbuf();
+ tmpAttrinbufptr.i = tcConnectptr.p->lastAttrinbuf;
+ ptrCheckGuard(regAttrinbufptr, tattrinbufFileSize, regAttrbuf);
+ tcConnectptr.p->lastAttrinbuf = regAttrinbufptr.i;
+ regAttrinbufptr.p->attrbuf[ZINBUF_DATA_LEN] = 0;
+ if (tmpAttrinbufptr.i == RNIL) {
+ jam();
+ tcConnectptr.p->firstAttrinbuf = regAttrinbufptr.i;
+ } else {
+ jam();
+ ptrCheckGuard(tmpAttrinbufptr, tattrinbufFileSize, regAttrbuf);
+ tmpAttrinbufptr.p->attrbuf[ZINBUF_NEXT] = regAttrinbufptr.i;
+ }//if
+ regAttrinbufptr.p->attrbuf[ZINBUF_NEXT] = RNIL;
+ attrinbufptr = regAttrinbufptr;
+}//Dblqh::seizeAttrinbuf()
+
+/* ==========================================================================
+ * ======= SEIZE TC CONNECT RECORD =======
+ *
+ * GETS A NEW TC CONNECT RECORD FROM FREELIST.
+ * ========================================================================= */
+void Dblqh::seizeTcrec()
+{
+ TcConnectionrecPtr locTcConnectptr;
+
+ locTcConnectptr.i = cfirstfreeTcConrec;
+ ptrCheckGuard(locTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ Uint32 nextTc = locTcConnectptr.p->nextTcConnectrec;
+ locTcConnectptr.p->nextTcConnectrec = RNIL;
+ locTcConnectptr.p->clientConnectrec = RNIL;
+ locTcConnectptr.p->clientBlockref = RNIL;
+ locTcConnectptr.p->abortState = TcConnectionrec::ABORT_IDLE;
+ locTcConnectptr.p->tcTimer = cLqhTimeOutCount;
+ locTcConnectptr.p->tableref = RNIL;
+ locTcConnectptr.p->savePointId = 0;
+ cfirstfreeTcConrec = nextTc;
+ tcConnectptr = locTcConnectptr;
+ locTcConnectptr.p->connectState = TcConnectionrec::CONNECTED;
+}//Dblqh::seizeTcrec()
+
+/* ==========================================================================
+ * ======= SEIZE DATA BUFFER =======
+ * ========================================================================= */
+void Dblqh::seizeTupkeybuf(Signal* signal)
+{
+ Databuf *regDatabuf = databuf;
+ DatabufPtr tmpDatabufptr;
+ DatabufPtr regDatabufptr;
+ Uint32 tdatabufFileSize = cdatabufFileSize;
+
+/* ------- GET A DATABUF. ------- */
+ regDatabufptr.i = cfirstfreeDatabuf;
+ tmpDatabufptr.i = tcConnectptr.p->lastTupkeybuf;
+ ptrCheckGuard(regDatabufptr, tdatabufFileSize, regDatabuf);
+ Uint32 nextFirst = regDatabufptr.p->nextDatabuf;
+ tcConnectptr.p->lastTupkeybuf = regDatabufptr.i;
+ if (tmpDatabufptr.i == RNIL) {
+ jam();
+ tcConnectptr.p->firstTupkeybuf = regDatabufptr.i;
+ } else {
+ jam();
+ ptrCheckGuard(tmpDatabufptr, tdatabufFileSize, regDatabuf);
+ tmpDatabufptr.p->nextDatabuf = regDatabufptr.i;
+ }//if
+ cfirstfreeDatabuf = nextFirst;
+ regDatabufptr.p->nextDatabuf = RNIL;
+ databufptr = regDatabufptr;
+}//Dblqh::seizeTupkeybuf()
+
+/* ------------------------------------------------------------------------- */
+/* ------- TAKE CARE OF LQHKEYREQ ------- */
+/* LQHKEYREQ IS THE SIGNAL THAT STARTS ALL OPERATIONS IN THE LQH BLOCK */
+/* THIS SIGNAL CONTAINS A LOT OF INFORMATION ABOUT WHAT TYPE OF OPERATION, */
+/* KEY INFORMATION, ATTRIBUTE INFORMATION, NODE INFORMATION AND A LOT MORE */
+/* ------------------------------------------------------------------------- */
+void Dblqh::execLQHKEYREQ(Signal* signal)
+{
+ UintR sig0, sig1, sig2, sig3, sig4, sig5;
+ Uint8 tfragDistKey;
+
+ const LqhKeyReq * const lqhKeyReq = (LqhKeyReq *)signal->getDataPtr();
+
+ sig0 = lqhKeyReq->clientConnectPtr;
+ if (cfirstfreeTcConrec != RNIL && !ERROR_INSERTED(5031)) {
+ jamEntry();
+ seizeTcrec();
+ } else {
+/* ------------------------------------------------------------------------- */
+/* NO FREE TC RECORD AVAILABLE, THUS WE CANNOT HANDLE THE REQUEST. */
+/* ------------------------------------------------------------------------- */
+ if (ERROR_INSERTED(5031)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ }
+ noFreeRecordLab(signal, lqhKeyReq, ZNO_TC_CONNECT_ERROR);
+ return;
+ }//if
+
+ if(ERROR_INSERTED(5038) &&
+ refToNode(signal->getSendersBlockRef()) != getOwnNodeId()){
+ jam();
+ SET_ERROR_INSERT_VALUE(5039);
+ return;
+ }
+
+ c_Counters.operations++;
+
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ regTcPtr->clientBlockref = signal->senderBlockRef();
+ regTcPtr->clientConnectrec = sig0;
+ regTcPtr->tcOprec = sig0;
+ regTcPtr->storedProcId = ZNIL;
+
+ UintR TtotReclenAi = lqhKeyReq->attrLen;
+ sig1 = lqhKeyReq->savePointId;
+ sig2 = lqhKeyReq->hashValue;
+ UintR Treqinfo = lqhKeyReq->requestInfo;
+ sig4 = lqhKeyReq->tableSchemaVersion;
+ sig5 = lqhKeyReq->tcBlockref;
+
+ regTcPtr->savePointId = sig1;
+ regTcPtr->hashValue = sig2;
+ const Uint32 schemaVersion = regTcPtr->schemaVersion = LqhKeyReq::getSchemaVersion(sig4);
+ tabptr.i = LqhKeyReq::getTableId(sig4);
+ regTcPtr->tcBlockref = sig5;
+
+ const Uint8 op = LqhKeyReq::getOperation(Treqinfo);
+ if (op == ZREAD && !getAllowRead()){
+ noFreeRecordLab(signal, lqhKeyReq, ZNODE_SHUTDOWN_IN_PROGESS);
+ return;
+ }
+
+ regTcPtr->totReclenAi = LqhKeyReq::getAttrLen(TtotReclenAi);
+ regTcPtr->tcScanInfo = lqhKeyReq->scanInfo;
+ regTcPtr->indTakeOver = LqhKeyReq::getScanTakeOverFlag(TtotReclenAi);
+
+ regTcPtr->readlenAi = 0;
+ regTcPtr->currTupAiLen = 0;
+ regTcPtr->listState = TcConnectionrec::NOT_IN_LIST;
+ regTcPtr->logWriteState = TcConnectionrec::NOT_STARTED;
+ regTcPtr->fragmentptr = RNIL;
+
+ sig0 = lqhKeyReq->fragmentData;
+ sig1 = lqhKeyReq->transId1;
+ sig2 = lqhKeyReq->transId2;
+ sig3 = lqhKeyReq->variableData[0];
+ sig4 = lqhKeyReq->variableData[1];
+
+ regTcPtr->fragmentid = LqhKeyReq::getFragmentId(sig0);
+ regTcPtr->nextReplica = LqhKeyReq::getNextReplicaNodeId(sig0);
+ regTcPtr->transid[0] = sig1;
+ regTcPtr->transid[1] = sig2;
+ regTcPtr->applRef = sig3;
+ regTcPtr->applOprec = sig4;
+
+ regTcPtr->commitAckMarker = RNIL;
+ if(LqhKeyReq::getMarkerFlag(Treqinfo)){
+ jam();
+
+ CommitAckMarkerPtr markerPtr;
+ m_commitAckMarkerHash.seize(markerPtr);
+ if(markerPtr.i == RNIL){
+ noFreeRecordLab(signal, lqhKeyReq, ZNO_FREE_MARKER_RECORDS_ERROR);
+ return;
+ }
+ markerPtr.p->transid1 = sig1;
+ markerPtr.p->transid2 = sig2;
+ markerPtr.p->apiRef = sig3;
+ markerPtr.p->apiOprec = sig4;
+ const NodeId tcNodeId = refToNode(sig5);
+ markerPtr.p->tcNodeId = tcNodeId;
+
+ CommitAckMarkerPtr tmp;
+#ifdef VM_TRACE
+#ifdef MARKER_TRACE
+ ndbout_c("Add marker[%.8x %.8x]", markerPtr.p->transid1, markerPtr.p->transid2);
+#endif
+ ndbrequire(!m_commitAckMarkerHash.find(tmp, * markerPtr.p));
+#endif
+ m_commitAckMarkerHash.add(markerPtr);
+ regTcPtr->commitAckMarker = markerPtr.i;
+ }
+
+ regTcPtr->reqinfo = Treqinfo;
+ regTcPtr->lastReplicaNo = LqhKeyReq::getLastReplicaNo(Treqinfo);
+ regTcPtr->lockType = LqhKeyReq::getLockType(Treqinfo);
+ regTcPtr->dirtyOp = LqhKeyReq::getDirtyFlag(Treqinfo);
+ regTcPtr->opExec = LqhKeyReq::getInterpretedFlag(Treqinfo);
+ regTcPtr->opSimple = LqhKeyReq::getSimpleFlag(Treqinfo);
+ regTcPtr->operation = LqhKeyReq::getOperation(Treqinfo);
+ regTcPtr->simpleRead = regTcPtr->operation == ZREAD && regTcPtr->opSimple;
+ regTcPtr->seqNoReplica = LqhKeyReq::getSeqNoReplica(Treqinfo);
+ UintR TreclenAiLqhkey = LqhKeyReq::getAIInLqhKeyReq(Treqinfo);
+ regTcPtr->apiVersionNo = 0;
+
+ CRASH_INSERTION2(5041, regTcPtr->simpleRead &&
+ refToNode(signal->senderBlockRef()) != cownNodeid);
+
+ regTcPtr->reclenAiLqhkey = TreclenAiLqhkey;
+ regTcPtr->currReclenAi = TreclenAiLqhkey;
+ UintR TitcKeyLen = LqhKeyReq::getKeyLen(Treqinfo);
+ regTcPtr->primKeyLen = TitcKeyLen;
+ regTcPtr->noFiredTriggers = lqhKeyReq->noFiredTriggers;
+
+ UintR TapplAddressInd = LqhKeyReq::getApplicationAddressFlag(Treqinfo);
+ UintR nextPos = (TapplAddressInd << 1);
+ UintR TsameClientAndTcOprec = LqhKeyReq::getSameClientAndTcFlag(Treqinfo);
+ if (TsameClientAndTcOprec == 1) {
+ regTcPtr->tcOprec = lqhKeyReq->variableData[nextPos];
+ nextPos++;
+ }//if
+ UintR TnextReplicasIndicator = regTcPtr->lastReplicaNo -
+ regTcPtr->seqNoReplica;
+ if (TnextReplicasIndicator > 1) {
+ regTcPtr->nodeAfterNext[0] = lqhKeyReq->variableData[nextPos] & 0xFFFF;
+ regTcPtr->nodeAfterNext[1] = lqhKeyReq->variableData[nextPos] >> 16;
+ nextPos++;
+ }//if
+ UintR TstoredProcIndicator = LqhKeyReq::getStoredProcFlag(TtotReclenAi);
+ if (TstoredProcIndicator == 1) {
+ regTcPtr->storedProcId = lqhKeyReq->variableData[nextPos] & ZNIL;
+ nextPos++;
+ }//if
+ UintR TreadLenAiIndicator = LqhKeyReq::getReturnedReadLenAIFlag(Treqinfo);
+ if (TreadLenAiIndicator == 1) {
+ regTcPtr->readlenAi = lqhKeyReq->variableData[nextPos] & ZNIL;
+ nextPos++;
+ }//if
+ sig0 = lqhKeyReq->variableData[nextPos + 0];
+ sig1 = lqhKeyReq->variableData[nextPos + 1];
+ sig2 = lqhKeyReq->variableData[nextPos + 2];
+ sig3 = lqhKeyReq->variableData[nextPos + 3];
+
+ regTcPtr->tupkeyData[0] = sig0;
+ regTcPtr->tupkeyData[1] = sig1;
+ regTcPtr->tupkeyData[2] = sig2;
+ regTcPtr->tupkeyData[3] = sig3;
+
+ if (TitcKeyLen > 0) {
+ if (TitcKeyLen < 4) {
+ nextPos += TitcKeyLen;
+ } else {
+ nextPos += 4;
+ }//if
+ } else {
+ LQHKEY_error(signal, 3);
+ return;
+ }//if
+
+ if ((LqhKeyReq::FixedSignalLength + nextPos + TreclenAiLqhkey) !=
+ signal->length()) {
+ LQHKEY_error(signal, 2);
+ return;
+ }//if
+ UintR TseqNoReplica = regTcPtr->seqNoReplica;
+ UintR TlastReplicaNo = regTcPtr->lastReplicaNo;
+ if (TseqNoReplica == TlastReplicaNo) {
+ jam();
+ regTcPtr->nextReplica = ZNIL;
+ } else {
+ if (TseqNoReplica < TlastReplicaNo) {
+ jam();
+ regTcPtr->nextSeqNoReplica = TseqNoReplica + 1;
+ if ((regTcPtr->nextReplica == 0) ||
+ (regTcPtr->nextReplica == cownNodeid)) {
+ LQHKEY_error(signal, 0);
+ }//if
+ } else {
+ LQHKEY_error(signal, 4);
+ return;
+ }//if
+ }//if
+ TcConnectionrecPtr localNextTcConnectptr;
+ Uint32 hashIndex = (regTcPtr->transid[0] ^ regTcPtr->tcOprec) & 1023;
+ localNextTcConnectptr.i = ctransidHash[hashIndex];
+ ctransidHash[hashIndex] = tcConnectptr.i;
+ regTcPtr->prevHashRec = RNIL;
+ regTcPtr->nextHashRec = localNextTcConnectptr.i;
+ if (localNextTcConnectptr.i != RNIL) {
+/* -------------------------------------------------------------------------- */
+/* ENSURE THAT THE NEXT RECORD HAS SET PREVIOUS TO OUR RECORD IF IT EXISTS */
+/* -------------------------------------------------------------------------- */
+ ptrCheckGuard(localNextTcConnectptr,
+ ctcConnectrecFileSize, tcConnectionrec);
+ jam();
+ localNextTcConnectptr.p->prevHashRec = tcConnectptr.i;
+ }//if
+ if (tabptr.i >= ctabrecFileSize) {
+ LQHKEY_error(signal, 5);
+ return;
+ }//if
+ ptrAss(tabptr, tablerec);
+ if(tabptr.p->tableStatus != Tablerec::TABLE_DEFINED){
+ LQHKEY_abort(signal, 4);
+ return;
+ }
+ if(tabptr.p->schemaVersion != schemaVersion){
+ LQHKEY_abort(signal, 5);
+ return;
+ }
+
+ regTcPtr->tableref = tabptr.i;
+ tabptr.p->usageCount++;
+
+ if (!getFragmentrec(signal, regTcPtr->fragmentid)) {
+ LQHKEY_error(signal, 6);
+ return;
+ }//if
+ regTcPtr->localFragptr = regTcPtr->hashValue & 1;
+ Uint8 TcopyType = fragptr.p->fragCopy;
+ tfragDistKey = fragptr.p->fragDistributionKey;
+ if (fragptr.p->fragStatus == Fragrecord::ACTIVE_CREATION) {
+ jam();
+ regTcPtr->activeCreat = ZTRUE;
+ CRASH_INSERTION(5002);
+ } else {
+ regTcPtr->activeCreat = ZFALSE;
+ }//if
+ regTcPtr->replicaType = TcopyType;
+ regTcPtr->fragmentptr = fragptr.i;
+ Uint8 TdistKey = LqhKeyReq::getDistributionKey(TtotReclenAi);
+ if ((tfragDistKey != TdistKey) &&
+ (regTcPtr->seqNoReplica == 0) &&
+ (regTcPtr->dirtyOp == ZFALSE) &&
+ (regTcPtr->simpleRead == ZFALSE)) {
+ /* ----------------------------------------------------------------------
+ * WE HAVE DIFFERENT OPINION THAN THE DIH THAT STARTED THE TRANSACTION.
+ * THE REASON COULD BE THAT THIS IS AN OLD DISTRIBUTION WHICH IS NO LONGER
+ * VALID TO USE. THIS MUST BE CHECKED.
+ * ONE IS ADDED TO THE DISTRIBUTION KEY EVERY TIME WE ADD A NEW REPLICA.
+ * FAILED REPLICAS DO NOT AFFECT THE DISTRIBUTION KEY. THIS MEANS THAT THE
+ * MAXIMUM DEVIATION CAN BE ONE BETWEEN THOSE TWO VALUES.
+ * --------------------------------------------------------------------- */
+ Int32 tmp = TdistKey - tfragDistKey;
+ tmp = (tmp < 0 ? - tmp : tmp);
+ if ((tmp <= 1) || (tfragDistKey == 0)) {
+ LQHKEY_abort(signal, 0);
+ return;
+ }//if
+ LQHKEY_error(signal, 1);
+ }//if
+ if (TreclenAiLqhkey != 0) {
+ if (regTcPtr->operation != ZREAD) {
+ if (regTcPtr->operation != ZDELETE) {
+ if (regTcPtr->opExec != 1) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* */
+/* UPDATES, WRITES AND INSERTS THAT ARE NOT INTERPRETED WILL USE THE */
+/* SAME ATTRINFO IN ALL REPLICAS. THUS WE SAVE THE ATTRINFO ALREADY */
+/* TO SAVE A SIGNAL FROM TUP TO LQH. INTERPRETED EXECUTION IN TUP */
+/* WILL CREATE NEW ATTRINFO FOR THE OTHER REPLICAS AND IT IS THUS NOT */
+/* A GOOD IDEA TO SAVE THE INFORMATION HERE. READS WILL ALSO BE */
+/* UNNECESSARY TO SAVE SINCE THAT ATTRINFO WILL NEVER BE SENT TO ANY */
+/* MORE REPLICAS. */
+/*---------------------------------------------------------------------------*/
+/* READS AND DELETES CAN ONLY HAVE INFORMATION ABOUT WHAT IS TO BE READ. */
+/* NO INFORMATION THAT NEEDS LOGGING. */
+/*---------------------------------------------------------------------------*/
+ sig0 = lqhKeyReq->variableData[nextPos + 0];
+ sig1 = lqhKeyReq->variableData[nextPos + 1];
+ sig2 = lqhKeyReq->variableData[nextPos + 2];
+ sig3 = lqhKeyReq->variableData[nextPos + 3];
+ sig4 = lqhKeyReq->variableData[nextPos + 4];
+
+ regTcPtr->firstAttrinfo[0] = sig0;
+ regTcPtr->firstAttrinfo[1] = sig1;
+ regTcPtr->firstAttrinfo[2] = sig2;
+ regTcPtr->firstAttrinfo[3] = sig3;
+ regTcPtr->firstAttrinfo[4] = sig4;
+ regTcPtr->currTupAiLen = TreclenAiLqhkey;
+ } else {
+ jam();
+ regTcPtr->reclenAiLqhkey = 0;
+ }//if
+ } else {
+ jam();
+ regTcPtr->reclenAiLqhkey = 0;
+ }//if
+ }//if
+ sig0 = lqhKeyReq->variableData[nextPos + 0];
+ sig1 = lqhKeyReq->variableData[nextPos + 1];
+ sig2 = lqhKeyReq->variableData[nextPos + 2];
+ sig3 = lqhKeyReq->variableData[nextPos + 3];
+ sig4 = lqhKeyReq->variableData[nextPos + 4];
+
+ signal->theData[0] = regTcPtr->tupConnectrec;
+ signal->theData[3] = sig0;
+ signal->theData[4] = sig1;
+ signal->theData[5] = sig2;
+ signal->theData[6] = sig3;
+ signal->theData[7] = sig4;
+ EXECUTE_DIRECT(refToBlock(regTcPtr->tcTupBlockref), GSN_ATTRINFO,
+ signal, TreclenAiLqhkey + 3);
+ jamEntry();
+ if (signal->theData[0] == (UintR)-1) {
+ LQHKEY_abort(signal, 2);
+ return;
+ }//if
+ }//if
+/* ------- TAKE CARE OF PRIM KEY DATA ------- */
+ if (regTcPtr->primKeyLen <= 4) {
+ endgettupkeyLab(signal);
+ return;
+ } else {
+ jam();
+/*--------------------------------------------------------------------*/
+/* KEY LENGTH WAS MORE THAN 4 WORDS (WORD = 4 BYTE). THUS WE */
+/* HAVE TO ALLOCATE A DATA BUFFER TO STORE THE KEY DATA AND */
+/* WAIT FOR THE KEYINFO SIGNAL. */
+/*--------------------------------------------------------------------*/
+ regTcPtr->save1 = 4;
+ regTcPtr->transactionState = TcConnectionrec::WAIT_TUPKEYINFO;
+ return;
+ }//if
+ return;
+}//Dblqh::execLQHKEYREQ()
+
+void Dblqh::endgettupkeyLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->totReclenAi == regTcPtr->currReclenAi) {
+ ;
+ } else {
+ jam();
+ ndbrequire(regTcPtr->currReclenAi < regTcPtr->totReclenAi);
+ regTcPtr->transactionState = TcConnectionrec::WAIT_ATTR;
+ return;
+ }//if
+/* ---------------------------------------------------------------------- */
+/* NOW RECEPTION OF LQHKEYREQ IS COMPLETED THE NEXT STEP IS TO START*/
+/* PROCESSING THE MESSAGE. IF THE MESSAGE IS TO A STAND-BY NODE */
+/* WITHOUT NETWORK REDUNDANCY OR PREPARE-TO-COMMIT ACTIVATED THE */
+/* PREPARATION TO SEND TO THE NEXT NODE WILL START IMMEDIATELY. */
+/* */
+/* OTHERWISE THE PROCESSING WILL START AFTER SETTING THE PROPER */
+/* STATE. HOWEVER BEFORE PROCESSING THE MESSAGE */
+/* IT IS NECESSARY TO CHECK THAT THE FRAGMENT IS NOT PERFORMING */
+/* A CHECKPOINT. THE OPERATION SHALL ALSO BE LINKED INTO THE */
+/* FRAGMENT QUEUE OR LIST OF ACTIVE OPERATIONS. */
+/* */
+/* THE FIRST STEP IN PROCESSING THE MESSAGE IS TO CONTACT DBACC. */
+/*------------------------------------------------------------------------*/
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ case Fragrecord::CRASH_RECOVERING:
+ case Fragrecord::ACTIVE_CREATION:
+ linkActiveFrag(signal);
+ prepareContinueAfterBlockedLab(signal);
+ return;
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ regTcPtr->transactionState = TcConnectionrec::STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dblqh::endgettupkeyLab()
+
+void Dblqh::prepareContinueAfterBlockedLab(Signal* signal)
+{
+ UintR ttcScanOp;
+ UintR taccreq;
+
+/* -------------------------------------------------------------------------- */
+/* INPUT: TC_CONNECTPTR ACTIVE CONNECTION RECORD */
+/* FRAGPTR FRAGMENT RECORD */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* CONTINUE HERE AFTER BEING BLOCKED FOR A WHILE DURING LOCAL CHECKPOINT. */
+/* -------------------------------------------------------------------------- */
+/* ALSO AFTER NORMAL PROCEDURE WE CONTINUE HERE */
+/* -------------------------------------------------------------------------- */
+ Uint32 tc_ptr_i = tcConnectptr.i;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->indTakeOver == ZTRUE) {
+ jam();
+ ttcScanOp = KeyInfo20::getScanOp(regTcPtr->tcScanInfo);
+ scanptr.i = RNIL;
+ {
+ ScanRecord key;
+ key.scanNumber = KeyInfo20::getScanNo(regTcPtr->tcScanInfo);
+ key.fragPtrI = fragptr.i;
+ c_scanTakeOverHash.find(scanptr, key);
+#ifdef TRACE_SCAN_TAKEOVER
+ if(scanptr.i == RNIL)
+ ndbout_c("not finding (%d %d)", key.scanNumber, key.fragPtrI);
+#endif
+ }
+ if (scanptr.i == RNIL) {
+ jam();
+ releaseActiveFrag(signal);
+ takeOverErrorLab(signal);
+ return;
+ }//if
+ Uint32 accOpPtr= get_acc_ptr_from_scan_record(scanptr.p,
+ ttcScanOp,
+ true);
+ if (accOpPtr == RNIL) {
+ jam();
+ releaseActiveFrag(signal);
+ takeOverErrorLab(signal);
+ return;
+ }//if
+ signal->theData[1] = accOpPtr;
+ signal->theData[2] = regTcPtr->transid[0];
+ signal->theData[3] = regTcPtr->transid[1];
+ EXECUTE_DIRECT(refToBlock(regTcPtr->tcAccBlockref), GSN_ACC_TO_REQ,
+ signal, 4);
+ if (signal->theData[0] == (UintR)-1) {
+ execACC_TO_REF(signal);
+ return;
+ }//if
+ jamEntry();
+ }//if
+/*-------------------------------------------------------------------*/
+/* IT IS NOW TIME TO CONTACT ACC. THE TUPLE KEY WILL BE SENT */
+/* AND THIS WILL BE TRANSLATED INTO A LOCAL KEY BY USING THE */
+/* LOCAL PART OF THE LH3-ALGORITHM. ALSO PROPER LOCKS ON THE */
+/* TUPLE WILL BE SET. FOR INSERTS AND DELETES THE MESSAGE WILL */
+/* START AN INSERT/DELETE INTO THE HASH TABLE. */
+/* */
+/* BEFORE SENDING THE MESSAGE THE REQUEST INFORMATION IS SET */
+/* PROPERLY. */
+/* ----------------------------------------------------------------- */
+#if 0
+ if (regTcPtr->tableref != 0) {
+ switch (regTcPtr->operation) {
+ case ZREAD: ndbout << "Läsning "; break;
+ case ZUPDATE: ndbout << " Uppdatering "; break;
+ case ZWRITE: ndbout << "Write "; break;
+ case ZINSERT: ndbout << "Inläggning "; break;
+ case ZDELETE: ndbout << "Borttagning "; break;
+ default: ndbout << "????"; break;
+ }
+ ndbout << "med nyckel = " << regTcPtr->tupkeyData[0] << endl;
+ }
+#endif
+
+ regTcPtr->transactionState = TcConnectionrec::WAIT_ACC;
+ taccreq = regTcPtr->operation;
+ taccreq = taccreq + (regTcPtr->opSimple << 3);
+ taccreq = taccreq + (regTcPtr->lockType << 4);
+ taccreq = taccreq + (regTcPtr->dirtyOp << 6);
+ taccreq = taccreq + (regTcPtr->replicaType << 7);
+ taccreq = taccreq + (regTcPtr->apiVersionNo << 9);
+/* ************ */
+/* ACCKEYREQ < */
+/* ************ */
+ ndbrequire(regTcPtr->localFragptr < 2);
+ Uint32 sig0, sig1, sig2, sig3, sig4;
+ sig0 = regTcPtr->accConnectrec;
+ sig1 = fragptr.p->accFragptr[regTcPtr->localFragptr];
+ sig2 = regTcPtr->hashValue;
+ sig3 = regTcPtr->primKeyLen;
+ sig4 = regTcPtr->transid[0];
+ signal->theData[0] = sig0;
+ signal->theData[1] = sig1;
+ signal->theData[2] = taccreq;
+ signal->theData[3] = sig2;
+ signal->theData[4] = sig3;
+ signal->theData[5] = sig4;
+
+ sig0 = regTcPtr->transid[1];
+ sig1 = regTcPtr->tupkeyData[0];
+ sig2 = regTcPtr->tupkeyData[1];
+ sig3 = regTcPtr->tupkeyData[2];
+ sig4 = regTcPtr->tupkeyData[3];
+ signal->theData[6] = sig0;
+ signal->theData[7] = sig1;
+ signal->theData[8] = sig2;
+ signal->theData[9] = sig3;
+ signal->theData[10] = sig4;
+ if (regTcPtr->primKeyLen > 4) {
+ sendKeyinfoAcc(signal, 11);
+ }//if
+ EXECUTE_DIRECT(refToBlock(regTcPtr->tcAccBlockref), GSN_ACCKEYREQ,
+ signal, 7 + regTcPtr->primKeyLen);
+ if (signal->theData[0] < RNIL) {
+ signal->theData[0] = tc_ptr_i;
+ execACCKEYCONF(signal);
+ return;
+ } else if (signal->theData[0] == RNIL) {
+ ;
+ } else {
+ ndbrequire(signal->theData[0] == (UintR)-1);
+ signal->theData[0] = tc_ptr_i;
+ execACCKEYREF(signal);
+ }//if
+ return;
+}//Dblqh::prepareContinueAfterBlockedLab()
+
+/* ========================================================================== */
+/* ======= SEND KEYINFO TO ACC ======= */
+/* */
+/* ========================================================================== */
+void Dblqh::sendKeyinfoAcc(Signal* signal, Uint32 Ti)
+{
+ DatabufPtr regDatabufptr;
+ regDatabufptr.i = tcConnectptr.p->firstTupkeybuf;
+
+ do {
+ jam();
+ ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf);
+ Uint32 sig0 = regDatabufptr.p->data[0];
+ Uint32 sig1 = regDatabufptr.p->data[1];
+ Uint32 sig2 = regDatabufptr.p->data[2];
+ Uint32 sig3 = regDatabufptr.p->data[3];
+ signal->theData[Ti] = sig0;
+ signal->theData[Ti + 1] = sig1;
+ signal->theData[Ti + 2] = sig2;
+ signal->theData[Ti + 3] = sig3;
+ regDatabufptr.i = regDatabufptr.p->nextDatabuf;
+ Ti += 4;
+ } while (regDatabufptr.i != RNIL);
+}//Dblqh::sendKeyinfoAcc()
+
+void Dblqh::execLQH_ALLOCREQ(Signal* signal)
+{
+ TcConnectionrecPtr regTcPtr;
+ FragrecordPtr regFragptr;
+
+ jamEntry();
+ regTcPtr.i = signal->theData[0];
+ ptrCheckGuard(regTcPtr, ctcConnectrecFileSize, tcConnectionrec);
+
+ regFragptr.i = regTcPtr.p->fragmentptr;
+ ptrCheckGuard(regFragptr, cfragrecFileSize, fragrecord);
+
+ ndbrequire(regTcPtr.p->localFragptr < 2);
+ signal->theData[0] = regTcPtr.p->tupConnectrec;
+ signal->theData[1] = regFragptr.p->tupFragptr[regTcPtr.p->localFragptr];
+ signal->theData[2] = regTcPtr.p->tableref;
+ Uint32 tup = refToBlock(regTcPtr.p->tcTupBlockref);
+ EXECUTE_DIRECT(tup, GSN_TUP_ALLOCREQ, signal, 3);
+}//Dblqh::execTUP_ALLOCREQ()
+
+/* ************>> */
+/* ACCKEYCONF > */
+/* ************>> */
+void Dblqh::execACCKEYCONF(Signal* signal)
+{
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ Uint32 tcIndex = signal->theData[0];
+ Uint32 Tfragid = signal->theData[2];
+ Uint32 localKey1 = signal->theData[3];
+ Uint32 localKey2 = signal->theData[4];
+ Uint32 localKeyFlag = signal->theData[5];
+ jamEntry();
+ tcConnectptr.i = tcIndex;
+ ptrCheckGuard(tcConnectptr, ttcConnectrecFileSize, regTcConnectionrec);
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->transactionState != TcConnectionrec::WAIT_ACC) {
+ LQHKEY_abort(signal, 3);
+ return;
+ }//if
+ /* ------------------------------------------------------------------------
+ * Set transaction state and also reset the activeCreat since that is only
+ * valid in cases where the record was not present.
+ * ------------------------------------------------------------------------ */
+ regTcPtr->transactionState = TcConnectionrec::WAIT_TUP;
+ regTcPtr->activeCreat = ZFALSE;
+ /* ------------------------------------------------------------------------
+ * IT IS NOW TIME TO CONTACT THE TUPLE MANAGER. THE TUPLE MANAGER NEEDS THE
+ * INFORMATION ON WHICH TABLE AND FRAGMENT, THE LOCAL KEY AND IT NEEDS TO
+ * KNOW THE TYPE OF OPERATION TO PERFORM. TUP CAN SEND THE ATTRINFO DATA
+ * EITHER TO THE TC BLOCK OR DIRECTLY TO THE APPLICATION. THE SCHEMA VERSION
+ * IS NEEDED SINCE TWO SCHEMA VERSIONS CAN BE ACTIVE SIMULTANEOUSLY ON A
+ * TABLE.
+ * ------------------------------------------------------------------------ */
+ if (regTcPtr->operation == ZWRITE) {
+ if (signal->theData[1] > 0) {
+ /* --------------------------------------------------------------------
+ * ACC did perform an insert and thus we should indicate that the WRITE
+ * is an INSERT otherwise it is an UPDATE.
+ * -------------------------------------------------------------------- */
+ jam();
+ regTcPtr->operation = ZINSERT;
+ } else {
+ jam();
+ tcConnectptr.p->operation = ZUPDATE;
+ }//if
+ }//if
+ ndbrequire(localKeyFlag == 1);
+ localKey2 = localKey1 & MAX_TUPLES_PER_PAGE;
+ localKey1 = localKey1 >> MAX_TUPLES_BITS;
+ Uint32 Ttupreq = regTcPtr->dirtyOp;
+ Ttupreq = Ttupreq + (regTcPtr->opSimple << 1);
+ Ttupreq = Ttupreq + (regTcPtr->operation << 6);
+ Ttupreq = Ttupreq + (regTcPtr->opExec << 10);
+ Ttupreq = Ttupreq + (regTcPtr->apiVersionNo << 11);
+
+ /* ---------------------------------------------------------------------
+ * Clear interpreted mode bit since we do not want the next replica to
+ * use interpreted mode. The next replica will receive a normal write.
+ * --------------------------------------------------------------------- */
+ regTcPtr->opExec = 0;
+ /* ************< */
+ /* TUPKEYREQ < */
+ /* ************< */
+ TupKeyReq * const tupKeyReq = (TupKeyReq *)signal->getDataPtrSend();
+ Uint32 sig0, sig1, sig2, sig3;
+
+ sig0 = regTcPtr->tupConnectrec;
+ sig1 = regTcPtr->tableref;
+ tupKeyReq->connectPtr = sig0;
+ tupKeyReq->request = Ttupreq;
+ tupKeyReq->tableRef = sig1;
+ tupKeyReq->fragId = Tfragid;
+ tupKeyReq->keyRef1 = localKey1;
+ tupKeyReq->keyRef2 = localKey2;
+
+ sig0 = regTcPtr->totReclenAi;
+ sig1 = regTcPtr->applOprec;
+ sig2 = regTcPtr->applRef;
+ sig3 = regTcPtr->schemaVersion;
+ FragrecordPtr regFragptr;
+ regFragptr.i = regTcPtr->fragmentptr;
+ ptrCheckGuard(regFragptr, cfragrecFileSize, fragrecord);
+ tupKeyReq->attrBufLen = sig0;
+ tupKeyReq->opRef = sig1;
+ tupKeyReq->applRef = sig2;
+ tupKeyReq->schemaVersion = sig3;
+
+ ndbrequire(regTcPtr->localFragptr < 2);
+ sig0 = regTcPtr->storedProcId;
+ sig1 = regTcPtr->transid[0];
+ sig2 = regTcPtr->transid[1];
+ sig3 = regFragptr.p->tupFragptr[regTcPtr->localFragptr];
+ Uint32 tup = refToBlock(regTcPtr->tcTupBlockref);
+
+ tupKeyReq->storedProcedure = sig0;
+ tupKeyReq->transId1 = sig1;
+ tupKeyReq->transId2 = sig2;
+ tupKeyReq->fragPtr = sig3;
+ tupKeyReq->primaryReplica = (tcConnectptr.p->seqNoReplica == 0)?true:false;
+ tupKeyReq->coordinatorTC = tcConnectptr.p->tcBlockref;
+ tupKeyReq->tcOpIndex = tcConnectptr.p->tcOprec;
+ tupKeyReq->savePointId = tcConnectptr.p->savePointId;
+
+ EXECUTE_DIRECT(tup, GSN_TUPKEYREQ, signal, TupKeyReq::SignalLength);
+}//Dblqh::execACCKEYCONF()
+
+/* --------------------------------------------------------------------------
+ * ------- ENTER TUP... -------
+ * ENTER TUPKEYCONF WITH
+ * TC_CONNECTPTR,
+ * TDATA2, LOCAL KEY REFERENCE 1, ONLY INTERESTING AFTER INSERT
+ * TDATA3, LOCAL KEY REFERENCE 1, ONLY INTERESTING AFTER INSERT
+ * TDATA4, TOTAL LENGTH OF READ DATA SENT TO TC/APPLICATION
+ * TDATA5 TOTAL LENGTH OF UPDATE DATA SENT TO/FROM TUP
+ * GOTO TUPKEY_CONF
+ *
+ * TAKE CARE OF RESPONSES FROM TUPLE MANAGER.
+ * -------------------------------------------------------------------------- */
+void Dblqh::tupkeyConfLab(Signal* signal)
+{
+/* ---- GET OPERATION TYPE AND CHECK WHAT KIND OF OPERATION IS REQUESTED ---- */
+ const TupKeyConf * const tupKeyConf = (TupKeyConf *)&signal->theData[0];
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->simpleRead) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THE OPERATION IS A SIMPLE READ. WE WILL IMMEDIATELY COMMIT THE OPERATION.
+ * SINCE WE HAVE NOT RELEASED THE FRAGMENT LOCK (FOR LOCAL CHECKPOINTS) YET
+ * WE CAN GO IMMEDIATELY TO COMMIT_CONTINUE_AFTER_BLOCKED.
+ * WE HAVE ALREADY SENT THE RESPONSE SO WE ARE NOT INTERESTED IN READ LENGTH
+ * ---------------------------------------------------------------------- */
+ regTcPtr->gci = cnewestGci;
+ releaseActiveFrag(signal);
+ commitContinueAfterBlockedLab(signal);
+ return;
+ }//if
+ if (tupKeyConf->readLength != 0) {
+ jam();
+
+ /* SET BIT 15 IN REQINFO */
+ LqhKeyReq::setApplicationAddressFlag(regTcPtr->reqinfo, 1);
+
+ regTcPtr->readlenAi = tupKeyConf->readLength;
+ }//if
+ regTcPtr->totSendlenAi = tupKeyConf->writeLength;
+ ndbrequire(regTcPtr->totSendlenAi == regTcPtr->currTupAiLen);
+ rwConcludedLab(signal);
+ return;
+}//Dblqh::tupkeyConfLab()
+
+/* --------------------------------------------------------------------------
+ * THE CODE IS FOUND IN THE SIGNAL RECEPTION PART OF LQH
+ * -------------------------------------------------------------------------- */
+void Dblqh::rwConcludedLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ /* ------------------------------------------------------------------------
+ * WE HAVE NOW CONCLUDED READING/WRITING IN ACC AND TUP FOR THIS OPERATION.
+ * IT IS NOW TIME TO LOG THE OPERATION, SEND REQUEST TO NEXT NODE OR TC AND
+ * FOR SOME TYPES OF OPERATIONS IT IS EVEN TIME TO COMMIT THE OPERATION.
+ * ------------------------------------------------------------------------ */
+ if (regTcPtr->operation == ZREAD) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * A NORMAL READ OPERATION IS NOT LOGGED BUT IS NOT COMMITTED UNTIL THE
+ * COMMIT SIGNAL ARRIVES. THUS WE CONTINUE PACKING THE RESPONSE.
+ * ---------------------------------------------------------------------- */
+ releaseActiveFrag(signal);
+ packLqhkeyreqLab(signal);
+ return;
+ } else {
+ FragrecordPtr regFragptr;
+ regFragptr.i = regTcPtr->fragmentptr;
+ ptrCheckGuard(regFragptr, cfragrecFileSize, fragrecord);
+ if (regFragptr.p->logFlag == Fragrecord::STATE_FALSE){
+ if (regTcPtr->dirtyOp == ZTRUE) {
+ jam();
+ /* ------------------------------------------------------------------
+ * THIS OPERATION WAS A WRITE OPERATION THAT DO NOT NEED LOGGING AND
+ * THAT CAN CAN BE COMMITTED IMMEDIATELY.
+ * ------------------------------------------------------------------ */
+ regTcPtr->gci = cnewestGci;
+ releaseActiveFrag(signal);
+ commitContinueAfterBlockedLab(signal);
+ return;
+ } else {
+ jam();
+ /* ------------------------------------------------------------------
+ * A NORMAL WRITE OPERATION ON A FRAGMENT WHICH DO NOT NEED LOGGING.
+ * WE WILL PACK THE REQUEST/RESPONSE TO THE NEXT NODE/TO TC.
+ * ------------------------------------------------------------------ */
+ regTcPtr->logWriteState = TcConnectionrec::NOT_WRITTEN;
+ releaseActiveFrag(signal);
+ packLqhkeyreqLab(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * A DIRTY OPERATION WHICH NEEDS LOGGING. WE START BY LOGGING THE
+ * REQUEST. IN THIS CASE WE WILL RELEASE THE FRAGMENT LOCK FIRST.
+ * --------------------------------------------------------------------
+ * A NORMAL WRITE OPERATION THAT NEEDS LOGGING AND WILL NOT BE
+ * PREMATURELY COMMITTED.
+ * -------------------------------------------------------------------- */
+ releaseActiveFrag(signal);
+ logLqhkeyreqLab(signal);
+ return;
+ }//if
+ }//if
+}//Dblqh::rwConcludedLab()
+
+void Dblqh::rwConcludedAiLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ fragptr.i = regTcPtr->fragmentptr;
+ /* ------------------------------------------------------------------------
+ * WE HAVE NOW CONCLUDED READING/WRITING IN ACC AND TUP FOR THIS OPERATION.
+ * IT IS NOW TIME TO LOG THE OPERATION, SEND REQUEST TO NEXT NODE OR TC AND
+ * FOR SOME TYPES OF OPERATIONS IT IS EVEN TIME TO COMMIT THE OPERATION.
+ * IN THIS CASE WE HAVE ALREADY RELEASED THE FRAGMENT LOCK.
+ * ERROR CASES AT FRAGMENT CREATION AND STAND-BY NODES ARE THE REASONS FOR
+ * COMING HERE.
+ * ------------------------------------------------------------------------ */
+ if (regTcPtr->operation == ZREAD) {
+ if (regTcPtr->opSimple == 1) {
+ jam();
+ /* --------------------------------------------------------------------
+ * THE OPERATION IS A SIMPLE READ. WE WILL IMMEDIATELY COMMIT THE
+ * OPERATION.
+ * -------------------------------------------------------------------- */
+ regTcPtr->gci = cnewestGci;
+ localCommitLab(signal);
+ return;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * A NORMAL READ OPERATION IS NOT LOGGED BUT IS NOT COMMITTED UNTIL
+ * THE COMMIT SIGNAL ARRIVES. THUS WE CONTINUE PACKING THE RESPONSE.
+ * -------------------------------------------------------------------- */
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ packLqhkeyreqLab(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (fragptr.p->logFlag == Fragrecord::STATE_FALSE) {
+ if (regTcPtr->dirtyOp == ZTRUE) {
+ /* ------------------------------------------------------------------
+ * THIS OPERATION WAS A WRITE OPERATION THAT DO NOT NEED LOGGING AND
+ * THAT CAN CAN BE COMMITTED IMMEDIATELY.
+ * ------------------------------------------------------------------ */
+ jam();
+ /* ----------------------------------------------------------------
+ * IT MUST BE ACTIVE CREATION OF A FRAGMENT.
+ * ---------------------------------------------------------------- */
+ regTcPtr->gci = cnewestGci;
+ localCommitLab(signal);
+ return;
+ } else {
+ /* ------------------------------------------------------------------
+ * A NORMAL WRITE OPERATION ON A FRAGMENT WHICH DO NOT NEED LOGGING.
+ * WE WILL PACK THE REQUEST/RESPONSE TO THE NEXT NODE/TO TC.
+ * ------------------------------------------------------------------ */
+ jam();
+ /* ---------------------------------------------------------------
+ * IT MUST BE ACTIVE CREATION OF A FRAGMENT.
+ * NOT A DIRTY OPERATION THUS PACK REQUEST/RESPONSE.
+ * ---------------------------------------------------------------- */
+ regTcPtr->logWriteState = TcConnectionrec::NOT_WRITTEN;
+ packLqhkeyreqLab(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * A DIRTY OPERATION WHICH NEEDS LOGGING. WE START BY LOGGING THE
+ * REQUEST. IN THIS CASE WE WILL RELEASE THE FRAGMENT LOCK FIRST.
+ * -------------------------------------------------------------------- */
+ /* A NORMAL WRITE OPERATION THAT NEEDS LOGGING AND WILL NOT BE
+ * PREMATURELY COMMITTED.
+ * -------------------------------------------------------------------- */
+ logLqhkeyreqLab(signal);
+ return;
+ }//if
+ }//if
+}//Dblqh::rwConcludedAiLab()
+
+/* ##########################################################################
+ * ####### LOG MODULE #######
+ *
+ * ##########################################################################
+ * --------------------------------------------------------------------------
+ * THE LOG MODULE HANDLES THE READING AND WRITING OF THE LOG
+ * IT IS ALSO RESPONSIBLE FOR HANDLING THE SYSTEM RESTART.
+ * IT CONTROLS THE SYSTEM RESTART IN TUP AND ACC AS WELL.
+ * -------------------------------------------------------------------------- */
+void Dblqh::logLqhkeyreqLab(Signal* signal)
+{
+ UintR tcurrentFilepage;
+ TcConnectionrecPtr tmpTcConnectptr;
+
+ if (cnoOfLogPages < ZMIN_LOG_PAGES_OPERATION || ERROR_INSERTED(5032)) {
+ jam();
+ if(ERROR_INSERTED(5032)){
+ CLEAR_ERROR_INSERT_VALUE;
+ }
+/*---------------------------------------------------------------------------*/
+// The log disk is having problems in catching up with the speed of execution.
+// We must wait with writing the log of this operation to ensure we do not
+// overload the log.
+/*---------------------------------------------------------------------------*/
+ terrorCode = ZTEMPORARY_REDO_LOG_FAILURE;
+ abortErrorLab(signal);
+ return;
+ }//if
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ logPartPtr.i = regTcPtr->hashValue & 3;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+/* -------------------------------------------------- */
+/* THIS PART IS USED TO WRITE THE LOG */
+/* -------------------------------------------------- */
+/* -------------------------------------------------- */
+/* CHECK IF A LOG OPERATION IS ONGOING ALREADY. */
+/* IF SO THEN QUEUE THE OPERATION FOR LATER */
+/* RESTART WHEN THE LOG PART IS FREE AGAIN. */
+/* -------------------------------------------------- */
+ LogPartRecord * const regLogPartPtr = logPartPtr.p;
+
+ if(ERROR_INSERTED(5033)){
+ jam();
+ CLEAR_ERROR_INSERT_VALUE;
+
+ if ((regLogPartPtr->firstLogQueue != RNIL) &&
+ (regLogPartPtr->LogLqhKeyReqSent == ZFALSE)) {
+ /* -------------------------------------------------- */
+ /* WE HAVE A PROBLEM IN THAT THE LOG HAS NO */
+ /* ROOM FOR ADDITIONAL OPERATIONS AT THE MOMENT.*/
+ /* -------------------------------------------------- */
+ /* -------------------------------------------------- */
+ /* WE MUST STILL RESTART QUEUED OPERATIONS SO */
+ /* THEY ALSO CAN BE ABORTED. */
+ /* -------------------------------------------------- */
+ regLogPartPtr->LogLqhKeyReqSent = ZTRUE;
+ signal->theData[0] = ZLOG_LQHKEYREQ;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+
+ terrorCode = ZTAIL_PROBLEM_IN_LOG_ERROR;
+ abortErrorLab(signal);
+ return;
+ }
+
+ if (regLogPartPtr->logPartState == LogPartRecord::IDLE) {
+ ;
+ } else if (regLogPartPtr->logPartState == LogPartRecord::ACTIVE) {
+ jam();
+ linkWaitLog(signal, logPartPtr);
+ regTcPtr->transactionState = TcConnectionrec::LOG_QUEUED;
+ return;
+ } else {
+ if ((regLogPartPtr->firstLogQueue != RNIL) &&
+ (regLogPartPtr->LogLqhKeyReqSent == ZFALSE)) {
+/* -------------------------------------------------- */
+/* WE HAVE A PROBLEM IN THAT THE LOG HAS NO */
+/* ROOM FOR ADDITIONAL OPERATIONS AT THE MOMENT.*/
+/* -------------------------------------------------- */
+/* -------------------------------------------------- */
+/* WE MUST STILL RESTART QUEUED OPERATIONS SO */
+/* THEY ALSO CAN BE ABORTED. */
+/* -------------------------------------------------- */
+ regLogPartPtr->LogLqhKeyReqSent = ZTRUE;
+ signal->theData[0] = ZLOG_LQHKEYREQ;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+ if (regLogPartPtr->logPartState == LogPartRecord::TAIL_PROBLEM) {
+ jam();
+ terrorCode = ZTAIL_PROBLEM_IN_LOG_ERROR;
+ } else {
+ ndbrequire(regLogPartPtr->logPartState == LogPartRecord::FILE_CHANGE_PROBLEM);
+ jam();
+ terrorCode = ZFILE_CHANGE_PROBLEM_IN_LOG_ERROR;
+ }//if
+ abortErrorLab(signal);
+ return;
+ }//if
+ regLogPartPtr->logPartState = LogPartRecord::ACTIVE;
+ logFilePtr.i = regLogPartPtr->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+/* -------------------------------------------------- */
+/* CHECK IF A NEW MBYTE IS TO BE STARTED. IF */
+/* SO INSERT A NEXT LOG RECORD, WRITE THE LOG */
+/* AND PLACE THE LOG POINTER ON THE NEW POSITION*/
+/* IF A NEW FILE IS TO BE USED, CHANGE FILE AND */
+/* ALSO START OPENING THE NEXT LOG FILE. IF A */
+/* LAP HAS BEEN COMPLETED THEN ADD ONE TO LAP */
+/* COUNTER. */
+/* -------------------------------------------------- */
+ checkNewMbyte(signal);
+/* -------------------------------------------------- */
+/* INSERT THE OPERATION RECORD LAST IN THE LIST */
+/* OF NOT COMPLETED OPERATIONS. ALSO RECORD THE */
+/* FILE NO, PAGE NO AND PAGE INDEX OF THE START */
+/* OF THIS LOG RECORD. */
+/* IT IS NOT ALLOWED TO INSERT IT INTO THE LIST */
+/* BEFORE CHECKING THE NEW MBYTE SINCE THAT WILL*/
+/* CAUSE THE OLD VALUES OF TC_CONNECTPTR TO BE */
+/* USED IN WRITE_FILE_DESCRIPTOR. */
+/* -------------------------------------------------- */
+ Uint32 tcIndex = tcConnectptr.i;
+ tmpTcConnectptr.i = regLogPartPtr->lastLogTcrec;
+ regLogPartPtr->lastLogTcrec = tcIndex;
+ if (tmpTcConnectptr.i == RNIL) {
+ jam();
+ regLogPartPtr->firstLogTcrec = tcIndex;
+ } else {
+ ptrCheckGuard(tmpTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ tmpTcConnectptr.p->nextLogTcrec = tcIndex;
+ }//if
+ Uint32 fileNo = logFilePtr.p->fileNo;
+ tcurrentFilepage = logFilePtr.p->currentFilepage;
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ regTcPtr->nextLogTcrec = RNIL;
+ regTcPtr->prevLogTcrec = tmpTcConnectptr.i;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ Uint32 pageIndex = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ regTcPtr->logStartFileNo = fileNo;
+ regTcPtr->logStartPageNo = tcurrentFilepage;
+ regTcPtr->logStartPageIndex = pageIndex;
+/* -------------------------------------------------- */
+/* WRITE THE LOG HEADER OF THIS OPERATION. */
+/* -------------------------------------------------- */
+ writeLogHeader(signal);
+/* -------------------------------------------------- */
+/* WRITE THE TUPLE KEY OF THIS OPERATION. */
+/* -------------------------------------------------- */
+ writeKey(signal);
+/* -------------------------------------------------- */
+/* WRITE THE ATTRIBUTE INFO OF THIS OPERATION. */
+/* -------------------------------------------------- */
+ writeAttrinfoLab(signal);
+
+ logNextStart(signal);
+/* -------------------------------------------------- */
+/* RESET THE STATE OF THE LOG PART. IF ANY */
+/* OPERATIONS HAVE QUEUED THEN START THE FIRST */
+/* OF THESE. */
+/* -------------------------------------------------- */
+/* -------------------------------------------------- */
+/* CONTINUE WITH PACKING OF LQHKEYREQ */
+/* -------------------------------------------------- */
+ tcurrentFilepage = logFilePtr.p->currentFilepage;
+ if (logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] == ZPAGE_HEADER_SIZE) {
+ jam();
+ tcurrentFilepage--;
+ }//if
+ regTcPtr->logStopPageNo = tcurrentFilepage;
+ regTcPtr->logWriteState = TcConnectionrec::WRITTEN;
+ if (regTcPtr->abortState != TcConnectionrec::ABORT_IDLE) {
+/* -------------------------------------------------- */
+/* AN ABORT HAVE BEEN ORDERED. THE ABORT WAITED */
+/* FOR THE LOG WRITE TO BE COMPLETED. NOW WE */
+/* CAN PROCEED WITH THE NORMAL ABORT HANDLING. */
+/* -------------------------------------------------- */
+ abortCommonLab(signal);
+ return;
+ }//if
+ if (regTcPtr->dirtyOp != ZTRUE) {
+ packLqhkeyreqLab(signal);
+ } else {
+ /* ----------------------------------------------------------------------
+ * I NEED TO INSERT A COMMIT LOG RECORD SINCE WE ARE WRITING LOG IN THIS
+ * TRANSACTION. SINCE WE RELEASED THE LOG LOCK JUST NOW NO ONE ELSE CAN BE
+ * ACTIVE IN WRITING THE LOG. WE THUS WRITE THE LOG WITHOUT GETTING A LOCK
+ * SINCE WE ARE ONLY WRITING A COMMIT LOG RECORD.
+ * ---------------------------------------------------------------------- */
+ writeCommitLog(signal, logPartPtr);
+ /* ----------------------------------------------------------------------
+ * DIRTY OPERATIONS SHOULD COMMIT BEFORE THEY PACK THE REQUEST/RESPONSE.
+ * ---------------------------------------------------------------------- */
+ regTcPtr->gci = cnewestGci;
+ localCommitLab(signal);
+ }//if
+}//Dblqh::logLqhkeyreqLab()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEND LQHKEYREQ */
+/* */
+/* NO STATE CHECKING SINCE THE SIGNAL IS A LOCAL SIGNAL. THE EXECUTION OF */
+/* THE OPERATION IS COMPLETED. IT IS NOW TIME TO SEND THE OPERATION TO THE */
+/* NEXT REPLICA OR TO TC. */
+/* ------------------------------------------------------------------------- */
+void Dblqh::packLqhkeyreqLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->nextReplica == ZNIL) {
+/* ------------------------------------------------------------------------- */
+/* ------- SEND LQHKEYCONF ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+ sendLqhkeyconfTc(signal, regTcPtr->tcBlockref);
+ if (regTcPtr->dirtyOp != ZTRUE) {
+ jam();
+ regTcPtr->transactionState = TcConnectionrec::PREPARED;
+ releaseOprec(signal);
+ } else {
+ jam();
+/*************************************************************>*/
+/* DIRTY WRITES ARE USED IN TWO SITUATIONS. THE FIRST */
+/* SITUATION IS WHEN THEY ARE USED TO UPDATE COUNTERS AND*/
+/* OTHER ATTRIBUTES WHICH ARE NOT SENSITIVE TO CONSISTE- */
+/* NCY. THE SECOND SITUATION IS BY OPERATIONS THAT ARE */
+/* SENT AS PART OF A COPY FRAGMENT PROCESS. */
+/* */
+/* DURING A COPY FRAGMENT PROCESS THERE IS NO LOGGING */
+/* ONGOING SINCE THE FRAGMENT IS NOT COMPLETE YET. THE */
+/* LOGGING STARTS AFTER COMPLETING THE LAST COPY TUPLE */
+/* OPERATION. THE EXECUTION OF THE LAST COPY TUPLE DOES */
+/* ALSO START A LOCAL CHECKPOINT SO THAT THE FRAGMENT */
+/* REPLICA IS RECOVERABLE. THUS GLOBAL CHECKPOINT ID FOR */
+/* THOSE OPERATIONS ARE NOT INTERESTING. */
+/* */
+/* A DIRTY WRITE IS BY DEFINITION NOT CONSISTENT. THUS */
+/* IT CAN USE ANY GLOBAL CHECKPOINT. THE IDEA HERE IS TO */
+/* ALWAYS USE THE LATEST DEFINED GLOBAL CHECKPOINT ID IN */
+/* THIS NODE. */
+/*************************************************************>*/
+ cleanUp(signal);
+ }//if
+ return;
+ }//if
+/* ------------------------------------------------------------------------- */
+/* ------- SEND LQHKEYREQ ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* THERE ARE MORE REPLICAS TO SEND THE OPERATION TO. A NEW LQHKEYREQ WILL BE */
+/* PREPARED FOR THE NEXT REPLICA. */
+/* ------------------------------------------------------------------------- */
+/* CLEAR REPLICA TYPE, ATTRINFO INDICATOR (IN LQHKEYREQ), */
+/* INTERPRETED EXECUTION, SEQUENTIAL NUMBER OF REPLICA. */
+// Set bit indicating Client and TC record not the same.
+// Set readlenAi indicator if readlenAi != 0
+// Stored Procedure Indicator not set.
+/* ------------------------------------------------------------------------- */
+ LqhKeyReq * const lqhKeyReq = (LqhKeyReq *)&signal->theData[0];
+
+ UintR Treqinfo;
+ UintR sig0, sig1, sig2, sig3, sig4, sig5, sig6;
+ Treqinfo = preComputedRequestInfoMask & regTcPtr->reqinfo;
+
+ UintR TapplAddressIndicator = (regTcPtr->nextSeqNoReplica == 0 ? 0 : 1);
+ LqhKeyReq::setApplicationAddressFlag(Treqinfo, TapplAddressIndicator);
+ LqhKeyReq::setInterpretedFlag(Treqinfo, regTcPtr->opExec);
+ LqhKeyReq::setSeqNoReplica(Treqinfo, regTcPtr->nextSeqNoReplica);
+ LqhKeyReq::setAIInLqhKeyReq(Treqinfo, regTcPtr->reclenAiLqhkey);
+ UintR TreadLenAiInd = (regTcPtr->readlenAi == 0 ? 0 : 1);
+ UintR TsameLqhAndClient = (tcConnectptr.i ==
+ regTcPtr->tcOprec ? 0 : 1);
+ LqhKeyReq::setSameClientAndTcFlag(Treqinfo, TsameLqhAndClient);
+ LqhKeyReq::setReturnedReadLenAIFlag(Treqinfo, TreadLenAiInd);
+
+ UintR TotReclenAi = regTcPtr->totSendlenAi;
+/* ------------------------------------------------------------------------- */
+/* WE ARE NOW PREPARED TO SEND THE LQHKEYREQ. WE HAVE TO DECIDE IF ATTRINFO */
+/* IS INCLUDED IN THE LQHKEYREQ SIGNAL AND THEN SEND IT. */
+/* TAKE OVER SCAN OPERATION IS NEVER USED ON BACKUPS, LOG RECORDS AND START-UP*/
+/* OF NEW REPLICA AND THUS ONLY TOT_SENDLEN_AI IS USED THE UPPER 16 BITS ARE */
+/* ZERO. */
+/* ------------------------------------------------------------------------- */
+ sig0 = tcConnectptr.i;
+ sig1 = regTcPtr->savePointId;
+ sig2 = regTcPtr->hashValue;
+ sig4 = regTcPtr->tcBlockref;
+
+ lqhKeyReq->clientConnectPtr = sig0;
+ lqhKeyReq->attrLen = TotReclenAi;
+ lqhKeyReq->savePointId = sig1;
+ lqhKeyReq->hashValue = sig2;
+ lqhKeyReq->requestInfo = Treqinfo;
+ lqhKeyReq->tcBlockref = sig4;
+
+ sig0 = regTcPtr->tableref + (regTcPtr->schemaVersion << 16);
+ sig1 = regTcPtr->fragmentid + (regTcPtr->nodeAfterNext[0] << 16);
+ sig2 = regTcPtr->transid[0];
+ sig3 = regTcPtr->transid[1];
+ sig4 = regTcPtr->applRef;
+ sig5 = regTcPtr->applOprec;
+ sig6 = regTcPtr->tcOprec;
+ UintR nextPos = (TapplAddressIndicator << 1);
+
+ lqhKeyReq->tableSchemaVersion = sig0;
+ lqhKeyReq->fragmentData = sig1;
+ lqhKeyReq->transId1 = sig2;
+ lqhKeyReq->transId2 = sig3;
+ lqhKeyReq->noFiredTriggers = regTcPtr->noFiredTriggers;
+ lqhKeyReq->variableData[0] = sig4;
+ lqhKeyReq->variableData[1] = sig5;
+ lqhKeyReq->variableData[2] = sig6;
+
+ nextPos += TsameLqhAndClient;
+
+ if ((regTcPtr->lastReplicaNo - regTcPtr->nextSeqNoReplica) > 1) {
+ sig0 = (UintR)regTcPtr->nodeAfterNext[1] +
+ (UintR)(regTcPtr->nodeAfterNext[2] << 16);
+ lqhKeyReq->variableData[nextPos] = sig0;
+ nextPos++;
+ }//if
+ sig0 = regTcPtr->readlenAi;
+ sig1 = regTcPtr->tupkeyData[0];
+ sig2 = regTcPtr->tupkeyData[1];
+ sig3 = regTcPtr->tupkeyData[2];
+ sig4 = regTcPtr->tupkeyData[3];
+
+ lqhKeyReq->variableData[nextPos] = sig0;
+ nextPos += TreadLenAiInd;
+ lqhKeyReq->variableData[nextPos] = sig1;
+ lqhKeyReq->variableData[nextPos + 1] = sig2;
+ lqhKeyReq->variableData[nextPos + 2] = sig3;
+ lqhKeyReq->variableData[nextPos + 3] = sig4;
+ UintR TkeyLen = LqhKeyReq::getKeyLen(Treqinfo);
+ if (TkeyLen < 4) {
+ nextPos += TkeyLen;
+ } else {
+ nextPos += 4;
+ }//if
+
+ sig0 = regTcPtr->firstAttrinfo[0];
+ sig1 = regTcPtr->firstAttrinfo[1];
+ sig2 = regTcPtr->firstAttrinfo[2];
+ sig3 = regTcPtr->firstAttrinfo[3];
+ sig4 = regTcPtr->firstAttrinfo[4];
+ UintR TAiLen = regTcPtr->reclenAiLqhkey;
+ BlockReference lqhRef = calcLqhBlockRef(regTcPtr->nextReplica);
+
+ lqhKeyReq->variableData[nextPos] = sig0;
+ lqhKeyReq->variableData[nextPos + 1] = sig1;
+ lqhKeyReq->variableData[nextPos + 2] = sig2;
+ lqhKeyReq->variableData[nextPos + 3] = sig3;
+ lqhKeyReq->variableData[nextPos + 4] = sig4;
+
+ nextPos += TAiLen;
+
+ sendSignal(lqhRef, GSN_LQHKEYREQ, signal,
+ nextPos + LqhKeyReq::FixedSignalLength, JBB);
+ if (regTcPtr->primKeyLen > 4) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* MORE THAN 4 WORDS OF KEY DATA IS IN THE OPERATION. THEREFORE WE NEED TO */
+/* PREPARE A KEYINFO SIGNAL. MORE THAN ONE KEYINFO SIGNAL CAN BE SENT. */
+/* ------------------------------------------------------------------------- */
+ sendTupkey(signal);
+ }//if
+/* ------------------------------------------------------------------------- */
+/* NOW I AM PREPARED TO SEND ALL THE ATTRINFO SIGNALS. AT THE MOMENT A LOOP */
+/* SENDS ALL AT ONCE. LATER WE HAVE TO ADDRESS THE PROBLEM THAT THESE COULD */
+/* LEAD TO BUFFER EXPLOSION => NODE CRASH. */
+/* ------------------------------------------------------------------------- */
+/* NEW CODE TO SEND ATTRINFO IN PACK_LQHKEYREQ */
+/* THIS CODE USES A REAL-TIME BREAK AFTER */
+/* SENDING 16 SIGNALS. */
+/* -------------------------------------------------- */
+ sig0 = regTcPtr->tcOprec;
+ sig1 = regTcPtr->transid[0];
+ sig2 = regTcPtr->transid[1];
+ signal->theData[0] = sig0;
+ signal->theData[1] = sig1;
+ signal->theData[2] = sig2;
+ AttrbufPtr regAttrinbufptr;
+ regAttrinbufptr.i = regTcPtr->firstAttrinbuf;
+ while (regAttrinbufptr.i != RNIL) {
+ ptrCheckGuard(regAttrinbufptr, cattrinbufFileSize, attrbuf);
+ jam();
+ Uint32 dataLen = regAttrinbufptr.p->attrbuf[ZINBUF_DATA_LEN];
+ ndbrequire(dataLen != 0);
+ MEMCOPY_NO_WORDS(&signal->theData[3], &regAttrinbufptr.p->attrbuf[0], dataLen);
+ regAttrinbufptr.i = regAttrinbufptr.p->attrbuf[ZINBUF_NEXT];
+ sendSignal(lqhRef, GSN_ATTRINFO, signal, dataLen + 3, JBB);
+ }//while
+ regTcPtr->transactionState = TcConnectionrec::PREPARED;
+ if (regTcPtr->dirtyOp == ZTRUE) {
+ jam();
+/*************************************************************>*/
+/* DIRTY WRITES ARE USED IN TWO SITUATIONS. THE FIRST */
+/* SITUATION IS WHEN THEY ARE USED TO UPDATE COUNTERS AND*/
+/* OTHER ATTRIBUTES WHICH ARE NOT SENSITIVE TO CONSISTE- */
+/* NCY. THE SECOND SITUATION IS BY OPERATIONS THAT ARE */
+/* SENT AS PART OF A COPY FRAGMENT PROCESS. */
+/* */
+/* DURING A COPY FRAGMENT PROCESS THERE IS NO LOGGING */
+/* ONGOING SINCE THE FRAGMENT IS NOT COMPLETE YET. THE */
+/* LOGGING STARTS AFTER COMPLETING THE LAST COPY TUPLE */
+/* OPERATION. THE EXECUTION OF THE LAST COPY TUPLE DOES */
+/* ALSO START A LOCAL CHECKPOINT SO THAT THE FRAGMENT */
+/* REPLICA IS RECOVERABLE. THUS GLOBAL CHECKPOINT ID FOR */
+/* THOSE OPERATIONS ARE NOT INTERESTING. */
+/* */
+/* A DIRTY WRITE IS BY DEFINITION NOT CONSISTENT. THUS */
+/* IT CAN USE ANY GLOBAL CHECKPOINT. THE IDEA HERE IS TO */
+/* ALWAYS USE THE LATEST DEFINED GLOBAL CHECKPOINT ID IN */
+/* THIS NODE. */
+/*************************************************************>*/
+ cleanUp(signal);
+ return;
+ }//if
+ /* ------------------------------------------------------------------------
+ * ALL INFORMATION NEEDED BY THE COMMIT PHASE AND COMPLETE PHASE IS
+ * KEPT IN THE TC_CONNECT RECORD. TO ENSURE PROPER USE OF MEMORY
+ * RESOURCES WE DEALLOCATE THE ATTRINFO RECORD AND KEY RECORDS
+ * AS SOON AS POSSIBLE.
+ * ------------------------------------------------------------------------ */
+ releaseOprec(signal);
+}//Dblqh::packLqhkeyreqLab()
+
+/* ========================================================================= */
+/* ==== CHECK IF THE LOG RECORD FITS INTO THE CURRENT MBYTE, ======= */
+/* OTHERWISE SWITCH TO NEXT MBYTE. */
+/* */
+/* ========================================================================= */
+void Dblqh::checkNewMbyte(Signal* signal)
+{
+ UintR tcnmTmp;
+ UintR ttotalLogSize;
+
+/* -------------------------------------------------- */
+/* CHECK IF A NEW MBYTE OF LOG RECORD IS TO BE */
+/* OPENED BEFORE WRITING THE LOG RECORD. NO LOG */
+/* RECORDS ARE ALLOWED TO SPAN A MBYTE BOUNDARY */
+/* */
+/* INPUT: TC_CONNECTPTR THE OPERATION */
+/* LOG_FILE_PTR THE LOG FILE */
+/* OUTPUT: LOG_FILE_PTR THE NEW LOG FILE */
+/* -------------------------------------------------- */
+ ttotalLogSize = ZLOG_HEAD_SIZE + tcConnectptr.p->currTupAiLen;
+ ttotalLogSize = ttotalLogSize + tcConnectptr.p->primKeyLen;
+ tcnmTmp = logFilePtr.p->remainingWordsInMbyte;
+ if ((ttotalLogSize + ZNEXT_LOG_SIZE) <= tcnmTmp) {
+ ndbrequire(tcnmTmp >= ttotalLogSize);
+ logFilePtr.p->remainingWordsInMbyte = tcnmTmp - ttotalLogSize;
+ return;
+ } else {
+ jam();
+/* -------------------------------------------------- */
+/* IT WAS NOT ENOUGH SPACE IN THIS MBYTE FOR */
+/* THIS LOG RECORD. MOVE TO NEXT MBYTE */
+/* THIS MIGHT INCLUDE CHANGING LOG FILE */
+/* -------------------------------------------------- */
+/* WE HAVE TO INSERT A NEXT LOG RECORD FIRST */
+/* -------------------------------------------------- */
+/* THEN CONTINUE BY WRITING THE FILE DESCRIPTORS*/
+/* -------------------------------------------------- */
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ changeMbyte(signal);
+ tcnmTmp = logFilePtr.p->remainingWordsInMbyte;
+ }//if
+ ndbrequire(tcnmTmp >= ttotalLogSize);
+ logFilePtr.p->remainingWordsInMbyte = tcnmTmp - ttotalLogSize;
+}//Dblqh::checkNewMbyte()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE OPERATION HEADER TO LOG -------
+ *
+ * SUBROUTINE SHORT NAME: WLH
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeLogHeader(Signal* signal)
+{
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ Uint32 hashValue = tcConnectptr.p->hashValue;
+ Uint32 operation = tcConnectptr.p->operation;
+ Uint32 keyLen = tcConnectptr.p->primKeyLen;
+ Uint32 aiLen = tcConnectptr.p->currTupAiLen;
+ Uint32 totLogLen = aiLen + keyLen + ZLOG_HEAD_SIZE;
+ if ((logPos + ZLOG_HEAD_SIZE) < ZPAGE_SIZE) {
+ Uint32* dataPtr = &logPagePtr.p->logPageWord[logPos];
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos + ZLOG_HEAD_SIZE;
+ dataPtr[0] = ZPREP_OP_TYPE;
+ dataPtr[1] = totLogLen;
+ dataPtr[2] = hashValue;
+ dataPtr[3] = operation;
+ dataPtr[4] = aiLen;
+ dataPtr[5] = keyLen;
+ } else {
+ writeLogWord(signal, ZPREP_OP_TYPE);
+ writeLogWord(signal, totLogLen);
+ writeLogWord(signal, hashValue);
+ writeLogWord(signal, operation);
+ writeLogWord(signal, aiLen);
+ writeLogWord(signal, keyLen);
+ }//if
+}//Dblqh::writeLogHeader()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE TUPLE KEY TO LOG -------
+ *
+ * SUBROUTINE SHORT NAME: WK
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeKey(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Uint32 logPos, endPos, dataLen;
+ Int32 remainingLen;
+ logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ remainingLen = regTcPtr->primKeyLen;
+ dataLen = remainingLen;
+ if (remainingLen > 4)
+ dataLen = 4;
+ remainingLen -= dataLen;
+ endPos = logPos + dataLen;
+ if (endPos < ZPAGE_SIZE) {
+ MEMCOPY_NO_WORDS(&logPagePtr.p->logPageWord[logPos],
+ &regTcPtr->tupkeyData[0],
+ dataLen);
+ } else {
+ jam();
+ for (Uint32 i = 0; i < dataLen; i++)
+ writeLogWord(signal, regTcPtr->tupkeyData[i]);
+ endPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ }//if
+ DatabufPtr regDatabufptr;
+ regDatabufptr.i = regTcPtr->firstTupkeybuf;
+ while (remainingLen > 0) {
+ logPos = endPos;
+ ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf);
+ dataLen = remainingLen;
+ if (remainingLen > 4)
+ dataLen = 4;
+ remainingLen -= dataLen;
+ endPos += dataLen;
+ if (endPos < ZPAGE_SIZE) {
+ MEMCOPY_NO_WORDS(&logPagePtr.p->logPageWord[logPos],
+ &regDatabufptr.p->data[0],
+ dataLen);
+ } else {
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos;
+ for (Uint32 i = 0; i < dataLen; i++)
+ writeLogWord(signal, regDatabufptr.p->data[i]);
+ endPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ }//if
+ regDatabufptr.i = regDatabufptr.p->nextDatabuf;
+ }//while
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = endPos;
+ ndbrequire(regDatabufptr.i == RNIL);
+}//Dblqh::writeKey()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE ATTRINFO TO LOG -------
+ *
+ * SUBROUTINE SHORT NAME: WA
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeAttrinfoLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Uint32 totLen = regTcPtr->currTupAiLen;
+ if (totLen == 0)
+ return;
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ Uint32 lqhLen = regTcPtr->reclenAiLqhkey;
+ ndbrequire(totLen >= lqhLen);
+ Uint32 endPos = logPos + lqhLen;
+ totLen -= lqhLen;
+ if (endPos < ZPAGE_SIZE) {
+ MEMCOPY_NO_WORDS(&logPagePtr.p->logPageWord[logPos],
+ &regTcPtr->firstAttrinfo[0],
+ lqhLen);
+ } else {
+ for (Uint32 i = 0; i < lqhLen; i++)
+ writeLogWord(signal, regTcPtr->firstAttrinfo[i]);
+ endPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ }//if
+ AttrbufPtr regAttrinbufptr;
+ regAttrinbufptr.i = regTcPtr->firstAttrinbuf;
+ while (totLen > 0) {
+ logPos = endPos;
+ ptrCheckGuard(regAttrinbufptr, cattrinbufFileSize, attrbuf);
+ Uint32 dataLen = regAttrinbufptr.p->attrbuf[ZINBUF_DATA_LEN];
+ ndbrequire(totLen >= dataLen);
+ ndbrequire(dataLen > 0);
+ totLen -= dataLen;
+ endPos += dataLen;
+ if (endPos < ZPAGE_SIZE) {
+ MEMCOPY_NO_WORDS(&logPagePtr.p->logPageWord[logPos],
+ &regAttrinbufptr.p->attrbuf[0],
+ dataLen);
+ } else {
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos;
+ for (Uint32 i = 0; i < dataLen; i++)
+ writeLogWord(signal, regAttrinbufptr.p->attrbuf[i]);
+ endPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ }//if
+ regAttrinbufptr.i = regAttrinbufptr.p->attrbuf[ZINBUF_NEXT];
+ }//while
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = endPos;
+ ndbrequire(regAttrinbufptr.i == RNIL);
+}//Dblqh::writeAttrinfoLab()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEND TUPLE KEY IN KEYINFO SIGNAL(S) ------- */
+/* */
+/* SUBROUTINE SHORT NAME: STU */
+/* ------------------------------------------------------------------------- */
+void Dblqh::sendTupkey(Signal* signal)
+{
+ UintR TdataPos = 3;
+ BlockReference lqhRef = calcLqhBlockRef(tcConnectptr.p->nextReplica);
+ signal->theData[0] = tcConnectptr.p->tcOprec;
+ signal->theData[1] = tcConnectptr.p->transid[0];
+ signal->theData[2] = tcConnectptr.p->transid[1];
+ databufptr.i = tcConnectptr.p->firstTupkeybuf;
+ do {
+ ptrCheckGuard(databufptr, cdatabufFileSize, databuf);
+ signal->theData[TdataPos] = databufptr.p->data[0];
+ signal->theData[TdataPos + 1] = databufptr.p->data[1];
+ signal->theData[TdataPos + 2] = databufptr.p->data[2];
+ signal->theData[TdataPos + 3] = databufptr.p->data[3];
+
+ databufptr.i = databufptr.p->nextDatabuf;
+ TdataPos += 4;
+ if (databufptr.i == RNIL) {
+ jam();
+ sendSignal(lqhRef, GSN_KEYINFO, signal, TdataPos, JBB);
+ return;
+ } else if (TdataPos == 23) {
+ jam();
+ sendSignal(lqhRef, GSN_KEYINFO, signal, 23, JBB);
+ TdataPos = 3;
+ }
+ } while (1);
+}//Dblqh::sendTupkey()
+
+void Dblqh::cleanUp(Signal* signal)
+{
+ releaseOprec(signal);
+ deleteTransidHash(signal);
+ releaseTcrec(signal, tcConnectptr);
+}//Dblqh::cleanUp()
+
+/* --------------------------------------------------------------------------
+ * ---- RELEASE ALL RECORDS CONNECTED TO THE OPERATION RECORD AND THE ----
+ * OPERATION RECORD ITSELF
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseOprec(Signal* signal)
+{
+ UintR Tmpbuf;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+/* ---- RELEASE DATA BUFFERS ------------------- */
+ DatabufPtr regDatabufptr;
+ regDatabufptr.i = regTcPtr->firstTupkeybuf;
+/* --------------------------------------------------------------------------
+ * ------- RELEASE DATA BUFFERS -------
+ *
+ * ------------------------------------------------------------------------- */
+
+ while (regDatabufptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf);
+ Tmpbuf = regDatabufptr.p->nextDatabuf;
+ regDatabufptr.p->nextDatabuf = cfirstfreeDatabuf;
+ cfirstfreeDatabuf = regDatabufptr.i;
+ regDatabufptr.i = Tmpbuf;
+ }//while
+/* ---- RELEASE ATTRINFO BUFFERS ------------------- */
+ AttrbufPtr regAttrinbufptr;
+ regAttrinbufptr.i = regTcPtr->firstAttrinbuf;
+ /* ########################################################################
+ * ####### RELEASE_ATTRINBUF #######
+ *
+ * ####################################################################### */
+ while (regAttrinbufptr.i != RNIL) {
+ jam();
+ regAttrinbufptr.i= release_attrinbuf(regAttrinbufptr.i);
+ }//while
+ regTcPtr->firstAttrinbuf = RNIL;
+ regTcPtr->lastAttrinbuf = RNIL;
+ regTcPtr->firstTupkeybuf = RNIL;
+ regTcPtr->lastTupkeybuf = RNIL;
+}//Dblqh::releaseOprec()
+
+/* ------------------------------------------------------------------------- */
+/* ------ DELETE TRANSACTION ID FROM HASH TABLE ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::deleteTransidHash(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ TcConnectionrecPtr prevHashptr;
+ TcConnectionrecPtr nextHashptr;
+
+ prevHashptr.i = regTcPtr->prevHashRec;
+ nextHashptr.i = regTcPtr->nextHashRec;
+ if (prevHashptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(prevHashptr, ctcConnectrecFileSize, tcConnectionrec);
+ prevHashptr.p->nextHashRec = nextHashptr.i;
+ } else {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* THE OPERATION WAS PLACED FIRST IN THE LIST OF THE HASH TABLE. NEED TO SET */
+/* A NEW LEADER OF THE LIST. */
+/* ------------------------------------------------------------------------- */
+ Uint32 hashIndex = (regTcPtr->transid[0] ^ regTcPtr->tcOprec) & 1023;
+ ctransidHash[hashIndex] = nextHashptr.i;
+ }//if
+ if (nextHashptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(nextHashptr, ctcConnectrecFileSize, tcConnectionrec);
+ nextHashptr.p->prevHashRec = prevHashptr.i;
+ }//if
+}//Dblqh::deleteTransidHash()
+
+/* --------------------------------------------------------------------------
+ * ------- LINK OPERATION IN ACTIVE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME: LAF
+// Input Pointers:
+// tcConnectptr
+// fragptr
+ * ------------------------------------------------------------------------- */
+void Dblqh::linkActiveFrag(Signal* signal)
+{
+ TcConnectionrecPtr lafTcConnectptr;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Fragrecord * const regFragPtr = fragptr.p;
+ Uint32 tcIndex = tcConnectptr.i;
+ lafTcConnectptr.i = regFragPtr->activeList;
+ regTcPtr->prevTc = RNIL;
+ regFragPtr->activeList = tcIndex;
+ ndbrequire(regTcPtr->listState == TcConnectionrec::NOT_IN_LIST);
+ regTcPtr->nextTc = lafTcConnectptr.i;
+ regTcPtr->listState = TcConnectionrec::IN_ACTIVE_LIST;
+ if (lafTcConnectptr.i == RNIL) {
+ return;
+ } else {
+ jam();
+ ptrCheckGuard(lafTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ lafTcConnectptr.p->prevTc = tcIndex;
+ }//if
+ return;
+}//Dblqh::linkActiveFrag()
+
+/* -------------------------------------------------------------------------
+ * ------- RELEASE OPERATION FROM ACTIVE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME = RAF
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseActiveFrag(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ TcConnectionrecPtr ralTcNextConnectptr;
+ TcConnectionrecPtr ralTcPrevConnectptr;
+ fragptr.i = regTcPtr->fragmentptr;
+ ralTcPrevConnectptr.i = regTcPtr->prevTc;
+ ralTcNextConnectptr.i = regTcPtr->nextTc;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ Fragrecord * const regFragPtr = fragptr.p;
+ ndbrequire(regTcPtr->listState == TcConnectionrec::IN_ACTIVE_LIST);
+ regTcPtr->listState = TcConnectionrec::NOT_IN_LIST;
+
+ if (ralTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(ralTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ ralTcNextConnectptr.p->prevTc = ralTcPrevConnectptr.i;
+ }//if
+ if (ralTcPrevConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(ralTcPrevConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ ralTcPrevConnectptr.p->nextTc = regTcPtr->nextTc;
+ } else {
+ jam();
+ /* ----------------------------------------------------------------------
+ * OPERATION RECORD IS FIRST IN ACTIVE LIST
+ * THIS MEANS THAT THERE EXISTS NO PREVIOUS TC THAT NEEDS TO BE UPDATED.
+ * --------------------------------------------------------------------- */
+ regFragPtr->activeList = ralTcNextConnectptr.i;
+ }//if
+ if (regFragPtr->lcpRef != RNIL) {
+ jam();
+ lcpPtr.i = regFragPtr->lcpRef;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ ndbrequire(lcpPtr.p->lcpState == LcpRecord::LCP_WAIT_ACTIVE_FINISH);
+
+ /* --------------------------------------------------------------------
+ * IF A FRAGMENT IS CURRENTLY STARTING A LOCAL CHECKPOINT AND IT
+ * IS WAITING FOR ACTIVE OPERATIONS TO BE COMPLETED WITH THE
+ * CURRENT PHASE, THEN IT IS CHECKED WHETHER THE
+ * LAST ACTIVE OPERATION WAS NOW COMPLETED.
+ * ------------------------------------------------------------------- */
+ if (regFragPtr->activeList == RNIL) {
+ jam();
+ /* ------------------------------------------------------------------
+ * ACTIVE LIST ON FRAGMENT IS EMPTY AND WE ARE WAITING FOR
+ * THIS TO HAPPEN.
+ * WE WILL NOW START THE CHECKPOINT IN TUP AND ACC.
+ * ----------------------------------------------------------------- */
+ /* SEND START LOCAL CHECKPOINT TO ACC AND TUP */
+ /* ----------------------------------------------------------------- */
+ fragptr.p->lcpRef = RNIL;
+ lcpPtr.p->lcpState = LcpRecord::LCP_START_CHKP;
+ sendStartLcp(signal);
+ }//if
+ }//if
+}//Dblqh::releaseActiveFrag()
+
+/* ######################################################################### */
+/* ####### TRANSACTION MODULE ####### */
+/* THIS MODULE HANDLES THE COMMIT AND THE COMPLETE PHASE. */
+/* ######################################################################### */
+void Dblqh::warningReport(Signal* signal, int place)
+{
+ switch (place) {
+ case 0:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMMIT in wrong state in Dblqh" << endl;
+#endif
+ break;
+ case 1:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMMIT with wrong transid in Dblqh" << endl;
+#endif
+ break;
+ case 2:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMPLETE in wrong state in Dblqh" << endl;
+#endif
+ break;
+ case 3:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMPLETE with wrong transid in Dblqh" << endl;
+#endif
+ break;
+ case 4:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMMITREQ in wrong state in Dblqh" << endl;
+#endif
+ break;
+ case 5:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMMITREQ with wrong transid in Dblqh" << endl;
+#endif
+ break;
+ case 6:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMPLETEREQ in wrong state in Dblqh" << endl;
+#endif
+ break;
+ case 7:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMPLETEREQ with wrong transid in Dblqh" << endl;
+#endif
+ break;
+ case 8:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received ABORT with non-existing transid in Dblqh" << endl;
+#endif
+ break;
+ case 9:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received ABORTREQ with non-existing transid in Dblqh" << endl;
+#endif
+ break;
+ case 10:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received ABORTREQ in wrong state in Dblqh" << endl;
+#endif
+ break;
+ case 11:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMMIT when tc-rec released in Dblqh" << endl;
+#endif
+ break;
+ case 12:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received COMPLETE when tc-rec released in Dblqh" << endl;
+#endif
+ break;
+ case 13:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received LQHKEYREF when tc-rec released in Dblqh" << endl;
+#endif
+ break;
+ case 14:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received LQHKEYREF with wrong transid in Dblqh" << endl;
+#endif
+ break;
+ case 15:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "W: Received LQHKEYREF when already aborting in Dblqh" << endl;
+#endif
+ break;
+ case 16:
+ jam();
+ ndbrequire(cstartPhase == ZNIL);
+#ifdef ABORT_TRACE
+ ndbout << "W: Received LQHKEYREF in wrong state in Dblqh" << endl;
+#endif
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ return;
+}//Dblqh::warningReport()
+
+void Dblqh::errorReport(Signal* signal, int place)
+{
+ switch (place) {
+ case 0:
+ jam();
+ break;
+ case 1:
+ jam();
+ break;
+ case 2:
+ jam();
+ break;
+ case 3:
+ jam();
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ systemErrorLab(signal);
+ return;
+}//Dblqh::errorReport()
+
+/* ************************************************************************>>
+ * COMMIT: Start commit request from TC. This signal is originally sent as a
+ * packed signal and this function is called from execPACKED_SIGNAL.
+ * This is the normal commit protocol where TC first send this signal to the
+ * backup node which then will send COMMIT to the primary node. If
+ * everything is ok the primary node send COMMITTED back to TC.
+ * ************************************************************************>> */
+void Dblqh::execCOMMIT(Signal* signal)
+{
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ Uint32 tcIndex = signal->theData[0];
+ Uint32 gci = signal->theData[1];
+ Uint32 transid1 = signal->theData[2];
+ Uint32 transid2 = signal->theData[3];
+ jamEntry();
+ if (tcIndex >= ttcConnectrecFileSize) {
+ errorReport(signal, 0);
+ return;
+ }//if
+ if (ERROR_INSERTED(5011)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMMIT, signal, 2000, 4);
+ return;
+ }//if
+ if (ERROR_INSERTED(5012)) {
+ SET_ERROR_INSERT_VALUE(5017);
+ sendSignalWithDelay(cownref, GSN_COMMIT, signal, 2000, 4);
+ return;
+ }//if
+ tcConnectptr.i = tcIndex;
+ ptrAss(tcConnectptr, regTcConnectionrec);
+ if ((tcConnectptr.p->transid[0] == transid1) &&
+ (tcConnectptr.p->transid[1] == transid2)) {
+ commitReqLab(signal, gci);
+ return;
+ }//if
+ warningReport(signal, 1);
+ return;
+}//Dblqh::execCOMMIT()
+
+/* ************************************************************************>>
+ * COMMITREQ: Commit request from TC. This is the commit protocol used if
+ * one of the nodes is not behaving correctly. TC explicitly sends COMMITREQ
+ * to both the backup and primary node and gets a COMMITCONF back if the
+ * COMMIT was ok.
+ * ************************************************************************>> */
+void Dblqh::execCOMMITREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 reqPtr = signal->theData[0];
+ BlockReference reqBlockref = signal->theData[1];
+ Uint32 gci = signal->theData[2];
+ Uint32 transid1 = signal->theData[3];
+ Uint32 transid2 = signal->theData[4];
+ Uint32 tcOprec = signal->theData[6];
+ if (ERROR_INSERTED(5004)) {
+ systemErrorLab(signal);
+ }
+ if (ERROR_INSERTED(5017)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMMITREQ, signal, 2000, 7);
+ return;
+ }//if
+ if (findTransaction(transid1,
+ transid2,
+ tcOprec) != ZOK) {
+ warningReport(signal, 5);
+ return;
+ }//if
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ switch (regTcPtr->transactionState) {
+ case TcConnectionrec::PREPARED:
+ case TcConnectionrec::LOG_COMMIT_QUEUED_WAIT_SIGNAL:
+ case TcConnectionrec::LOG_COMMIT_WRITTEN_WAIT_SIGNAL:
+ jam();
+/*-------------------------------------------------------*/
+/* THE NORMAL CASE. */
+/*-------------------------------------------------------*/
+ regTcPtr->reqBlockref = reqBlockref;
+ regTcPtr->reqRef = reqPtr;
+ regTcPtr->abortState = TcConnectionrec::REQ_FROM_TC;
+ commitReqLab(signal, gci);
+ return;
+ break;
+ case TcConnectionrec::COMMITTED:
+ jam();
+/*---------------------------------------------------------*/
+/* FOR SOME REASON THE COMMIT PHASE HAVE BEEN */
+/* FINISHED AFTER A TIME OUT. WE NEED ONLY SEND A */
+/* COMMITCONF SIGNAL. */
+/*---------------------------------------------------------*/
+ regTcPtr->reqBlockref = reqBlockref;
+ regTcPtr->reqRef = reqPtr;
+ regTcPtr->abortState = TcConnectionrec::REQ_FROM_TC;
+ signal->theData[0] = regTcPtr->reqRef;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = regTcPtr->transid[0];
+ signal->theData[3] = regTcPtr->transid[1];
+ sendSignal(regTcPtr->reqBlockref, GSN_COMMITCONF, signal, 4, JBB);
+ break;
+ case TcConnectionrec::COMMIT_STOPPED:
+ jam();
+ regTcPtr->reqBlockref = reqBlockref;
+ regTcPtr->reqRef = reqPtr;
+ regTcPtr->abortState = TcConnectionrec::REQ_FROM_TC;
+ /*empty*/;
+ break;
+ default:
+ jam();
+ warningReport(signal, 4);
+ return;
+ break;
+ }//switch
+ return;
+}//Dblqh::execCOMMITREQ()
+
+/* ************************************************************************>>
+ * COMPLETE : Complete the transaction. Sent as a packed signal from TC.
+ * Works the same way as COMMIT protocol. This is the normal case with both
+ * primary and backup working (See COMMIT).
+ * ************************************************************************>> */
+void Dblqh::execCOMPLETE(Signal* signal)
+{
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ Uint32 tcIndex = signal->theData[0];
+ Uint32 transid1 = signal->theData[1];
+ Uint32 transid2 = signal->theData[2];
+ jamEntry();
+ if (tcIndex >= ttcConnectrecFileSize) {
+ errorReport(signal, 1);
+ return;
+ }//if
+ if (ERROR_INSERTED(5013)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMPLETE, signal, 2000, 3);
+ return;
+ }//if
+ if (ERROR_INSERTED(5014)) {
+ SET_ERROR_INSERT_VALUE(5018);
+ sendSignalWithDelay(cownref, GSN_COMPLETE, signal, 2000, 3);
+ return;
+ }//if
+ tcConnectptr.i = tcIndex;
+ ptrAss(tcConnectptr, regTcConnectionrec);
+ if ((tcConnectptr.p->transactionState == TcConnectionrec::COMMITTED) &&
+ (tcConnectptr.p->transid[0] == transid1) &&
+ (tcConnectptr.p->transid[1] == transid2)) {
+ if (tcConnectptr.p->seqNoReplica != 0) {
+ jam();
+ localCommitLab(signal);
+ return;
+ } else {
+ jam();
+ completeTransLastLab(signal);
+ return;
+ }//if
+ }//if
+ if (tcConnectptr.p->transactionState != TcConnectionrec::COMMITTED) {
+ warningReport(signal, 2);
+ } else {
+ warningReport(signal, 3);
+ }//if
+}//Dblqh::execCOMPLETE()
+
+/* ************************************************************************>>
+ * COMPLETEREQ: Complete request from TC. Same as COMPLETE but used if one
+ * node is not working ok (See COMMIT).
+ * ************************************************************************>> */
+void Dblqh::execCOMPLETEREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 reqPtr = signal->theData[0];
+ BlockReference reqBlockref = signal->theData[1];
+ Uint32 transid1 = signal->theData[2];
+ Uint32 transid2 = signal->theData[3];
+ Uint32 tcOprec = signal->theData[5];
+ if (ERROR_INSERTED(5005)) {
+ systemErrorLab(signal);
+ }
+ if (ERROR_INSERTED(5018)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMPLETEREQ, signal, 2000, 6);
+ return;
+ }//if
+ if (findTransaction(transid1,
+ transid2,
+ tcOprec) != ZOK) {
+ jam();
+/*---------------------------------------------------------*/
+/* FOR SOME REASON THE COMPLETE PHASE STARTED AFTER */
+/* A TIME OUT. THE TRANSACTION IS GONE. WE NEED TO */
+/* REPORT COMPLETION ANYWAY. */
+/*---------------------------------------------------------*/
+ signal->theData[0] = reqPtr;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = transid1;
+ signal->theData[3] = transid2;
+ sendSignal(reqBlockref, GSN_COMPLETECONF, signal, 4, JBB);
+ warningReport(signal, 7);
+ return;
+ }//if
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ switch (regTcPtr->transactionState) {
+ case TcConnectionrec::COMMITTED:
+ jam();
+ regTcPtr->reqBlockref = reqBlockref;
+ regTcPtr->reqRef = reqPtr;
+ regTcPtr->abortState = TcConnectionrec::REQ_FROM_TC;
+ /*empty*/;
+ break;
+/*---------------------------------------------------------*/
+/* THE NORMAL CASE. */
+/*---------------------------------------------------------*/
+ case TcConnectionrec::COMMIT_STOPPED:
+ jam();
+/*---------------------------------------------------------*/
+/* FOR SOME REASON THE COMPLETE PHASE STARTED AFTER */
+/* A TIME OUT. WE HAVE SET THE PROPER VARIABLES SUCH */
+/* THAT A COMPLETECONF WILL BE SENT WHEN COMPLETE IS */
+/* FINISHED. */
+/*---------------------------------------------------------*/
+ regTcPtr->reqBlockref = reqBlockref;
+ regTcPtr->reqRef = reqPtr;
+ regTcPtr->abortState = TcConnectionrec::REQ_FROM_TC;
+ return;
+ break;
+ default:
+ jam();
+ warningReport(signal, 6);
+ return;
+ break;
+ }//switch
+ if (regTcPtr->seqNoReplica != 0) {
+ jam();
+ localCommitLab(signal);
+ return;
+ } else {
+ jam();
+ completeTransLastLab(signal);
+ return;
+ }//if
+}//Dblqh::execCOMPLETEREQ()
+
+/* ************> */
+/* COMPLETED > */
+/* ************> */
+void Dblqh::execLQHKEYCONF(Signal* signal)
+{
+ LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+ Uint32 tcIndex = lqhKeyConf->opPtr;
+ Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
+ TcConnectionrec *regTcConnectionrec = tcConnectionrec;
+ jamEntry();
+ if (tcIndex >= ttcConnectrecFileSize) {
+ errorReport(signal, 2);
+ return;
+ }//if
+ tcConnectptr.i = tcIndex;
+ ptrAss(tcConnectptr, regTcConnectionrec);
+ switch (tcConnectptr.p->connectState) {
+ case TcConnectionrec::LOG_CONNECTED:
+ jam();
+ completedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::COPY_CONNECTED:
+ jam();
+ copyCompletedLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dblqh::execLQHKEYCONF()
+
+/* ------------------------------------------------------------------------- */
+/* ------- COMMIT PHASE ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::commitReqLab(Signal* signal, Uint32 gci)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ TcConnectionrec::LogWriteState logWriteState = regTcPtr->logWriteState;
+ TcConnectionrec::TransactionState transState = regTcPtr->transactionState;
+ regTcPtr->gci = gci;
+ if (transState == TcConnectionrec::PREPARED) {
+ if (logWriteState == TcConnectionrec::WRITTEN) {
+ jam();
+ regTcPtr->transactionState = TcConnectionrec::PREPARED_RECEIVED_COMMIT;
+ TcConnectionrecPtr saveTcPtr = tcConnectptr;
+ Uint32 blockNo = refToBlock(regTcPtr->tcTupBlockref);
+ signal->theData[0] = regTcPtr->tupConnectrec;
+ signal->theData[1] = gci;
+ EXECUTE_DIRECT(blockNo, GSN_TUP_WRITELOG_REQ, signal, 2);
+ jamEntry();
+ if (regTcPtr->transactionState == TcConnectionrec::LOG_COMMIT_QUEUED) {
+ jam();
+ return;
+ }//if
+ ndbrequire(regTcPtr->transactionState == TcConnectionrec::LOG_COMMIT_WRITTEN);
+ tcConnectptr = saveTcPtr;
+ } else if (logWriteState == TcConnectionrec::NOT_STARTED) {
+ jam();
+ } else if (logWriteState == TcConnectionrec::NOT_WRITTEN) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* IT IS A READ OPERATION OR OTHER OPERATION THAT DO NOT USE THE LOG. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+/* THE LOG HAS NOT BEEN WRITTEN SINCE THE LOG FLAG WAS FALSE. THIS CAN OCCUR */
+/* WHEN WE ARE STARTING A NEW FRAGMENT. */
+/*---------------------------------------------------------------------------*/
+ regTcPtr->logWriteState = TcConnectionrec::NOT_STARTED;
+ } else {
+ ndbrequire(logWriteState == TcConnectionrec::NOT_WRITTEN_WAIT);
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE STATE WAS SET TO NOT_WRITTEN BY THE OPERATION BUT LATER A SCAN OF ALL */
+/* OPERATION RECORD CHANGED IT INTO NOT_WRITTEN_WAIT. THIS INDICATES THAT WE */
+/* ARE WAITING FOR THIS OPERATION TO COMMIT OR ABORT SO THAT WE CAN FIND THE */
+/* STARTING GLOBAL CHECKPOINT OF THIS NEW FRAGMENT. */
+/*---------------------------------------------------------------------------*/
+ checkScanTcCompleted(signal);
+ }//if
+ } else if (transState == TcConnectionrec::LOG_COMMIT_QUEUED_WAIT_SIGNAL) {
+ jam();
+ regTcPtr->transactionState = TcConnectionrec::LOG_COMMIT_QUEUED;
+ return;
+ } else if (transState == TcConnectionrec::LOG_COMMIT_WRITTEN_WAIT_SIGNAL) {
+ jam();
+ } else {
+ warningReport(signal, 0);
+ return;
+ }//if
+ if (regTcPtr->seqNoReplica != 0) {
+ jam();
+ commitReplyLab(signal);
+ return;
+ }//if
+ localCommitLab(signal);
+ return;
+}//Dblqh::commitReqLab()
+
+void Dblqh::execLQH_WRITELOG_REQ(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Uint32 gci = signal->theData[1];
+ Uint32 newestGci = cnewestGci;
+ TcConnectionrec::LogWriteState logWriteState = regTcPtr->logWriteState;
+ TcConnectionrec::TransactionState transState = regTcPtr->transactionState;
+ regTcPtr->gci = gci;
+ if (gci > newestGci) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* KEEP TRACK OF NEWEST GLOBAL CHECKPOINT THAT LQH HAS HEARD OF. */
+/* ------------------------------------------------------------------------- */
+ cnewestGci = gci;
+ }//if
+ if (logWriteState == TcConnectionrec::WRITTEN) {
+/*---------------------------------------------------------------------------*/
+/* I NEED TO INSERT A COMMIT LOG RECORD SINCE WE ARE WRITING LOG IN THIS */
+/* TRANSACTION. */
+/*---------------------------------------------------------------------------*/
+ jam();
+ LogPartRecordPtr regLogPartPtr;
+ Uint32 noOfLogPages = cnoOfLogPages;
+ jam();
+ regLogPartPtr.i = regTcPtr->hashValue & 3;
+ ptrCheckGuard(regLogPartPtr, clogPartFileSize, logPartRecord);
+ if ((regLogPartPtr.p->logPartState == LogPartRecord::ACTIVE) ||
+ (noOfLogPages == 0)) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS LOG PART WAS CURRENTLY ACTIVE WRITING ANOTHER LOG RECORD. WE MUST */
+/* WAIT UNTIL THIS PART HAS COMPLETED ITS OPERATION. */
+/*---------------------------------------------------------------------------*/
+// We must delay the write of commit info to the log to safe-guard against
+// a crash due to lack of log pages. We temporary stop all log writes to this
+// log part to ensure that we don't get a buffer explosion in the delayed
+// signal buffer instead.
+/*---------------------------------------------------------------------------*/
+ linkWaitLog(signal, regLogPartPtr);
+ if (transState == TcConnectionrec::PREPARED) {
+ jam();
+ regTcPtr->transactionState = TcConnectionrec::LOG_COMMIT_QUEUED_WAIT_SIGNAL;
+ } else {
+ jam();
+ ndbrequire(transState == TcConnectionrec::PREPARED_RECEIVED_COMMIT);
+ regTcPtr->transactionState = TcConnectionrec::LOG_COMMIT_QUEUED;
+ }//if
+ if (regLogPartPtr.p->logPartState == LogPartRecord::IDLE) {
+ jam();
+ regLogPartPtr.p->logPartState = LogPartRecord::ACTIVE;
+ }//if
+ return;
+ }//if
+ writeCommitLog(signal, regLogPartPtr);
+ if (transState == TcConnectionrec::PREPARED) {
+ jam();
+ regTcPtr->transactionState = TcConnectionrec::LOG_COMMIT_WRITTEN_WAIT_SIGNAL;
+ } else {
+ jam();
+ ndbrequire(transState == TcConnectionrec::PREPARED_RECEIVED_COMMIT);
+ regTcPtr->transactionState = TcConnectionrec::LOG_COMMIT_WRITTEN;
+ }//if
+ }//if
+}//Dblqh::execLQH_WRITELOG_REQ()
+
+void Dblqh::localCommitLab(Signal* signal)
+{
+ FragrecordPtr regFragptr;
+ regFragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(regFragptr, cfragrecFileSize, fragrecord);
+ Fragrecord::FragStatus status = regFragptr.p->fragStatus;
+ fragptr = regFragptr;
+ switch (status) {
+ case Fragrecord::FSACTIVE:
+ case Fragrecord::CRASH_RECOVERING:
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ commitContinueAfterBlockedLab(signal);
+ return;
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::COMMIT_STOPPED;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dblqh::localCommitLab()
+
+void Dblqh::commitContinueAfterBlockedLab(Signal* signal)
+{
+/* ------------------------------------------------------------------------- */
+/*INPUT: TC_CONNECTPTR ACTIVE OPERATION RECORD */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/*CONTINUE HERE AFTER BEING BLOCKED FOR A WHILE DURING LOCAL CHECKPOINT. */
+/*The operation is already removed from the active list since there is no */
+/*chance for any real-time breaks before we need to release it. */
+/* ------------------------------------------------------------------------- */
+/*ALSO AFTER NORMAL PROCEDURE WE CONTINUE */
+/*WE MUST COMMIT TUP BEFORE ACC TO ENSURE THAT NO ONE RACES IN AND SEES A */
+/*DIRTY STATE IN TUP. */
+/* ------------------------------------------------------------------------- */
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Fragrecord * const regFragptr = fragptr.p;
+ Uint32 operation = regTcPtr->operation;
+ Uint32 simpleRead = regTcPtr->simpleRead;
+ Uint32 dirtyOp = regTcPtr->dirtyOp;
+ if (regTcPtr->activeCreat == ZFALSE) {
+ if ((cCommitBlocked == true) &&
+ (regFragptr->fragActiveStatus == ZTRUE)) {
+ jam();
+/* ------------------------------------------------------------------------- */
+// TUP and/or ACC have problems in writing the undo log to disk fast enough.
+// We must avoid the commit at this time and try later instead. The fragment
+// is also active with a local checkpoint and this commit can generate UNDO
+// log records that overflow the UNDO log buffer.
+/* ------------------------------------------------------------------------- */
+/*---------------------------------------------------------------------------*/
+// We must delay the write of commit info to the log to safe-guard against
+// a crash due to lack of log pages. We temporary stop all log writes to this
+// log part to ensure that we don't get a buffer explosion in the delayed
+// signal buffer instead.
+/*---------------------------------------------------------------------------*/
+ logPartPtr.i = regTcPtr->hashValue & 3;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ linkWaitLog(signal, logPartPtr);
+ regTcPtr->transactionState = TcConnectionrec::COMMIT_QUEUED;
+ if (logPartPtr.p->logPartState == LogPartRecord::IDLE) {
+ jam();
+ logPartPtr.p->logPartState = LogPartRecord::ACTIVE;
+ }//if
+ return;
+ }//if
+ if (operation != ZREAD) {
+ TupCommitReq * const tupCommitReq =
+ (TupCommitReq *)signal->getDataPtrSend();
+ Uint32 sig0 = regTcPtr->tupConnectrec;
+ Uint32 tup = refToBlock(regTcPtr->tcTupBlockref);
+ jam();
+ tupCommitReq->opPtr = sig0;
+ tupCommitReq->gci = regTcPtr->gci;
+ tupCommitReq->hashValue = regTcPtr->hashValue;
+ EXECUTE_DIRECT(tup, GSN_TUP_COMMITREQ, signal,
+ TupCommitReq::SignalLength);
+ Uint32 acc = refToBlock(regTcPtr->tcAccBlockref);
+ signal->theData[0] = regTcPtr->accConnectrec;
+ EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1);
+ } else {
+ if(!dirtyOp){
+ Uint32 acc = refToBlock(regTcPtr->tcAccBlockref);
+ signal->theData[0] = regTcPtr->accConnectrec;
+ EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1);
+ }
+ }
+ jamEntry();
+ if (simpleRead) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*THE OPERATION WAS A SIMPLE READ THUS THE COMMIT PHASE IS ONLY NEEDED TO */
+/*RELEASE THE LOCKS. AT THIS POINT IN THE CODE THE LOCKS ARE RELEASED AND WE */
+/*ARE IN A POSITION TO SEND LQHKEYCONF TO TC. WE WILL ALSO RELEASE ALL */
+/*RESOURCES BELONGING TO THIS OPERATION SINCE NO MORE WORK WILL BE */
+/*PERFORMED. */
+/* ------------------------------------------------------------------------- */
+ cleanUp(signal);
+ return;
+ }//if
+ }//if
+ Uint32 seqNoReplica = regTcPtr->seqNoReplica;
+ if (regTcPtr->gci > regFragptr->newestGci) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*IT IS THE FIRST TIME THIS GLOBAL CHECKPOINT IS INVOLVED IN UPDATING THIS */
+/*FRAGMENT. UPDATE THE VARIABLE THAT KEEPS TRACK OF NEWEST GCI IN FRAGMENT */
+/* ------------------------------------------------------------------------- */
+ regFragptr->newestGci = regTcPtr->gci;
+ }//if
+ if (dirtyOp != ZTRUE) {
+ if (seqNoReplica != 0) {
+ jam();
+ completeTransNotLastLab(signal);
+ return;
+ }//if
+ commitReplyLab(signal);
+ return;
+ } else {
+/* ------------------------------------------------------------------------- */
+/*WE MUST HANDLE DIRTY WRITES IN A SPECIAL WAY. THESE OPERATIONS WILL NOT */
+/*SEND ANY COMMIT OR COMPLETE MESSAGES TO OTHER NODES. THEY WILL MERELY SEND */
+/*THOSE SIGNALS INTERNALLY. */
+/* ------------------------------------------------------------------------- */
+ if (regTcPtr->abortState == TcConnectionrec::ABORT_IDLE) {
+ jam();
+ packLqhkeyreqLab(signal);
+ } else {
+ ndbrequire(regTcPtr->abortState != TcConnectionrec::NEW_FROM_TC);
+ jam();
+ sendLqhTransconf(signal, LqhTransConf::Committed);
+ cleanUp(signal);
+ }//if
+ }//if
+}//Dblqh::commitContinueAfterBlockedLab()
+
+void Dblqh::commitReplyLab(Signal* signal)
+{
+/* -------------------------------------------------------------- */
+/* BACKUP AND STAND-BY REPLICAS ONLY UPDATE THE TRANSACTION STATE */
+/* -------------------------------------------------------------- */
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ TcConnectionrec::AbortState abortState = regTcPtr->abortState;
+ regTcPtr->transactionState = TcConnectionrec::COMMITTED;
+ if (abortState == TcConnectionrec::ABORT_IDLE) {
+ Uint32 clientBlockref = regTcPtr->clientBlockref;
+ if (regTcPtr->seqNoReplica == 0) {
+ jam();
+ sendCommittedTc(signal, clientBlockref);
+ return;
+ } else {
+ jam();
+ sendCommitLqh(signal, clientBlockref);
+ return;
+ }//if
+ } else if (regTcPtr->abortState == TcConnectionrec::REQ_FROM_TC) {
+ jam();
+ signal->theData[0] = regTcPtr->reqRef;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = regTcPtr->transid[0];
+ signal->theData[3] = regTcPtr->transid[1];
+ sendSignal(tcConnectptr.p->reqBlockref, GSN_COMMITCONF, signal, 4, JBB);
+ } else {
+ ndbrequire(regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC);
+ jam();
+ sendLqhTransconf(signal, LqhTransConf::Committed);
+ }//if
+ return;
+}//Dblqh::commitReplyLab()
+
+/* ------------------------------------------------------------------------- */
+/* ------- COMPLETE PHASE ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::completeTransNotLastLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->abortState == TcConnectionrec::ABORT_IDLE) {
+ Uint32 clientBlockref = regTcPtr->clientBlockref;
+ jam();
+ sendCompleteLqh(signal, clientBlockref);
+ cleanUp(signal);
+ return;
+ } else {
+ jam();
+ completeUnusualLab(signal);
+ return;
+ }//if
+}//Dblqh::completeTransNotLastLab()
+
+void Dblqh::completeTransLastLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->abortState == TcConnectionrec::ABORT_IDLE) {
+ Uint32 clientBlockref = regTcPtr->clientBlockref;
+ jam();
+/* ------------------------------------------------------------------------- */
+/*DIRTY WRITES WHICH ARE LAST IN THE CHAIN OF REPLICAS WILL SEND COMPLETED */
+/*INSTEAD OF SENDING PREPARED TO THE TC (OR OTHER INITIATOR OF OPERATION). */
+/* ------------------------------------------------------------------------- */
+ sendCompletedTc(signal, clientBlockref);
+ cleanUp(signal);
+ return;
+ } else {
+ jam();
+ completeUnusualLab(signal);
+ return;
+ }//if
+}//Dblqh::completeTransLastLab()
+
+void Dblqh::completeUnusualLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->abortState == TcConnectionrec::ABORT_FROM_TC) {
+ jam();
+ sendAborted(signal);
+ } else if (regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC) {
+ jam();
+ sendLqhTransconf(signal, LqhTransConf::Committed);
+ } else {
+ ndbrequire(regTcPtr->abortState == TcConnectionrec::REQ_FROM_TC);
+ jam();
+ signal->theData[0] = regTcPtr->reqRef;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = regTcPtr->transid[0];
+ signal->theData[3] = regTcPtr->transid[1];
+ sendSignal(regTcPtr->reqBlockref,
+ GSN_COMPLETECONF, signal, 4, JBB);
+ }//if
+ cleanUp(signal);
+ return;
+}//Dblqh::completeUnusualLab()
+
+/* ========================================================================= */
+/* ======= RELEASE TC CONNECT RECORD ======= */
+/* */
+/* RELEASE A TC CONNECT RECORD TO THE FREELIST. */
+/* ========================================================================= */
+void Dblqh::releaseTcrec(Signal* signal, TcConnectionrecPtr locTcConnectptr)
+{
+ jam();
+ locTcConnectptr.p->tcTimer = 0;
+ locTcConnectptr.p->transactionState = TcConnectionrec::TC_NOT_CONNECTED;
+ locTcConnectptr.p->nextTcConnectrec = cfirstfreeTcConrec;
+ cfirstfreeTcConrec = locTcConnectptr.i;
+
+ TablerecPtr tabPtr;
+ tabPtr.i = locTcConnectptr.p->tableref;
+ if(tabPtr.i == RNIL)
+ return;
+
+ ptrCheckGuard(tabPtr, ctabrecFileSize, tablerec);
+
+ /**
+ * Normal case
+ */
+ ndbrequire(tabPtr.p->usageCount > 0);
+ tabPtr.p->usageCount--;
+}//Dblqh::releaseTcrec()
+
+void Dblqh::releaseTcrecLog(Signal* signal, TcConnectionrecPtr locTcConnectptr)
+{
+ jam();
+ locTcConnectptr.p->tcTimer = 0;
+ locTcConnectptr.p->transactionState = TcConnectionrec::TC_NOT_CONNECTED;
+ locTcConnectptr.p->nextTcConnectrec = cfirstfreeTcConrec;
+ cfirstfreeTcConrec = locTcConnectptr.i;
+
+ TablerecPtr tabPtr;
+ tabPtr.i = locTcConnectptr.p->tableref;
+ if(tabPtr.i == RNIL)
+ return;
+
+}//Dblqh::releaseTcrecLog()
+
+/* ------------------------------------------------------------------------- */
+/* ------- ABORT PHASE ------- */
+/* */
+/*THIS PART IS USED AT ERRORS THAT CAUSE ABORT OF TRANSACTION. */
+/* ------------------------------------------------------------------------- */
+/* ***************************************************>> */
+/* ABORT: Abort transaction in connection. Sender TC. */
+/* This is the normal protocol (See COMMIT) */
+/* ***************************************************>> */
+void Dblqh::execABORT(Signal* signal)
+{
+ jamEntry();
+ Uint32 tcOprec = signal->theData[0];
+ BlockReference tcBlockref = signal->theData[1];
+ Uint32 transid1 = signal->theData[2];
+ Uint32 transid2 = signal->theData[3];
+ CRASH_INSERTION(5003);
+ if (ERROR_INSERTED(5015)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_ABORT, signal, 2000, 4);
+ return;
+ }//if
+ if (findTransaction(transid1,
+ transid2,
+ tcOprec) != ZOK) {
+ jam();
+
+ if(ERROR_INSERTED(5039) &&
+ refToNode(signal->getSendersBlockRef()) != getOwnNodeId()){
+ jam();
+ SET_ERROR_INSERT_VALUE(5040);
+ return;
+ }
+
+ if(ERROR_INSERTED(5040) &&
+ refToNode(signal->getSendersBlockRef()) != getOwnNodeId()){
+ jam();
+ SET_ERROR_INSERT_VALUE(5003);
+ return;
+ }
+
+/* ------------------------------------------------------------------------- */
+// SEND ABORTED EVEN IF NOT FOUND.
+//THE TRANSACTION MIGHT NEVER HAVE ARRIVED HERE.
+/* ------------------------------------------------------------------------- */
+ signal->theData[0] = tcOprec;
+ signal->theData[1] = transid1;
+ signal->theData[2] = transid2;
+ signal->theData[3] = cownNodeid;
+ signal->theData[4] = ZTRUE;
+ sendSignal(tcBlockref, GSN_ABORTED, signal, 5, JBB);
+ warningReport(signal, 8);
+ return;
+ }//if
+/* ------------------------------------------------------------------------- */
+/*A GUIDING DESIGN PRINCIPLE IN HANDLING THESE ERROR SITUATIONS HAVE BEEN */
+/*KEEP IT SIMPLE. THUS WE RATHER INSERT A WAIT AND SET THE ABORT_STATE TO */
+/*ACTIVE RATHER THAN WRITE NEW CODE TO HANDLE EVERY SPECIAL SITUATION. */
+/* ------------------------------------------------------------------------- */
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->nextReplica != ZNIL) {
+/* ------------------------------------------------------------------------- */
+// We will immediately send the ABORT message also to the next LQH node in line.
+/* ------------------------------------------------------------------------- */
+ BlockReference TLqhRef = calcLqhBlockRef(regTcPtr->nextReplica);
+ signal->theData[0] = regTcPtr->tcOprec;
+ signal->theData[1] = regTcPtr->tcBlockref;
+ signal->theData[2] = regTcPtr->transid[0];
+ signal->theData[3] = regTcPtr->transid[1];
+ sendSignal(TLqhRef, GSN_ABORT, signal, 4, JBB);
+ }//if
+ regTcPtr->abortState = TcConnectionrec::ABORT_FROM_TC;
+ regTcPtr->activeCreat = ZFALSE;
+
+ const Uint32 commitAckMarker = regTcPtr->commitAckMarker;
+ if(commitAckMarker != RNIL){
+ jam();
+#ifdef MARKER_TRACE
+ {
+ CommitAckMarkerPtr tmp;
+ m_commitAckMarkerHash.getPtr(tmp, commitAckMarker);
+ ndbout_c("Ab2 marker[%.8x %.8x]", tmp.p->transid1, tmp.p->transid2);
+ }
+#endif
+ m_commitAckMarkerHash.release(commitAckMarker);
+ regTcPtr->commitAckMarker = RNIL;
+ }
+
+ abortStateHandlerLab(signal);
+
+ return;
+}//Dblqh::execABORT()
+
+/* ************************************************************************>>
+ * ABORTREQ: Same as ABORT but used in case one node isn't working ok.
+ * (See COMMITREQ)
+ * ************************************************************************>> */
+void Dblqh::execABORTREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 reqPtr = signal->theData[0];
+ BlockReference reqBlockref = signal->theData[1];
+ Uint32 transid1 = signal->theData[2];
+ Uint32 transid2 = signal->theData[3];
+ Uint32 tcOprec = signal->theData[5];
+ if (ERROR_INSERTED(5006)) {
+ systemErrorLab(signal);
+ }
+ if (ERROR_INSERTED(5016)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_ABORTREQ, signal, 2000, 6);
+ return;
+ }//if
+ if (findTransaction(transid1,
+ transid2,
+ tcOprec) != ZOK) {
+ signal->theData[0] = reqPtr;
+ signal->theData[2] = cownNodeid;
+ signal->theData[3] = transid1;
+ signal->theData[4] = transid2;
+ sendSignal(reqBlockref, GSN_ABORTCONF, signal, 5, JBB);
+ warningReport(signal, 9);
+ return;
+ }//if
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->transactionState != TcConnectionrec::PREPARED) {
+ warningReport(signal, 10);
+ return;
+ }//if
+ regTcPtr->reqBlockref = reqBlockref;
+ regTcPtr->reqRef = reqPtr;
+ regTcPtr->abortState = TcConnectionrec::REQ_FROM_TC;
+ regTcPtr->activeCreat = ZFALSE;
+ abortCommonLab(signal);
+ return;
+}//Dblqh::execABORTREQ()
+
+/* ************>> */
+/* ACC_TO_REF > */
+/* ************>> */
+void Dblqh::execACC_TO_REF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ releaseActiveFrag(signal);
+ abortErrorLab(signal);
+ return;
+}//Dblqh::execACC_TO_REF()
+
+/* ************> */
+/* ACCKEYREF > */
+/* ************> */
+void Dblqh::execACCKEYREF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ terrorCode = signal->theData[1];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ TcConnectionrec * const tcPtr = tcConnectptr.p;
+ switch (tcPtr->transactionState) {
+ case TcConnectionrec::WAIT_ACC:
+ jam();
+ releaseActiveFrag(signal);
+ break;
+ case TcConnectionrec::WAIT_ACC_ABORT:
+ case TcConnectionrec::ABORT_STOPPED:
+ case TcConnectionrec::ABORT_QUEUED:
+ jam();
+/* ------------------------------------------------------------------------- */
+/*IGNORE SINCE ABORT OF THIS OPERATION IS ONGOING ALREADY. */
+/* ------------------------------------------------------------------------- */
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ const Uint32 errCode = terrorCode;
+ tcPtr->errorCode = errCode;
+/* ------------------------------------------------------------------------- */
+/*WHEN AN ABORT FROM TC ARRIVES IT COULD ACTUALLY BE A CORRECT BEHAVIOUR */
+/*SINCE THE TUPLE MIGHT NOT HAVE ARRIVED YET OR ALREADY HAVE BEEN INSERTED. */
+/* ------------------------------------------------------------------------- */
+ if (tcPtr->activeCreat == ZTRUE) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*THIS IS A NORMAL EVENT DURING CREATION OF A FRAGMENT. PERFORM ABORT IN */
+/*TUP AND ACC AND THEN CONTINUE WITH NORMAL COMMIT PROCESSING. IF THE ERROR */
+/*HAPPENS TO BE A SERIOUS ERROR THEN PERFORM ABORT PROCESSING AS NORMAL. */
+/* ------------------------------------------------------------------------- */
+ switch (tcPtr->operation) {
+ case ZUPDATE:
+ case ZDELETE:
+ jam();
+ if (errCode != ZNO_TUPLE_FOUND) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*A NORMAL ERROR WILL BE TREATED AS A NORMAL ABORT AND WILL ABORT THE */
+/*TRANSACTION. NO SPECIAL HANDLING IS NEEDED. */
+/* ------------------------------------------------------------------------- */
+ tcPtr->activeCreat = ZFALSE;
+ }//if
+ break;
+ case ZINSERT:
+ jam();
+ if (errCode != ZTUPLE_ALREADY_EXIST) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*A NORMAL ERROR WILL BE TREATED AS A NORMAL ABORT AND WILL ABORT THE */
+/*TRANSACTION. NO SPECIAL HANDLING IS NEEDED. */
+/* ------------------------------------------------------------------------- */
+ tcPtr->activeCreat = ZFALSE;
+ }//if
+ break;
+ default:
+ jam();
+/* ------------------------------------------------------------------------- */
+/*A NORMAL ERROR WILL BE TREATED AS A NORMAL ABORT AND WILL ABORT THE */
+/*TRANSACTION. NO SPECIAL HANDLING IS NEEDED. */
+/* ------------------------------------------------------------------------- */
+ tcPtr->activeCreat = ZFALSE;
+ break;
+ }//switch
+ } else {
+ /**
+ * Only primary replica can get ZTUPLE_ALREADY_EXIST || ZNO_TUPLE_FOUND
+ *
+ * Unless it's a simple or dirty read
+ *
+ * NOT TRUE!
+ * 1) op1 - primary insert ok
+ * 2) op1 - backup insert fail (log full or what ever)
+ * 3) op1 - delete ok @ primary
+ * 4) op1 - delete fail @ backup
+ *
+ * -> ZNO_TUPLE_FOUND is possible
+ */
+ ndbrequire
+ (tcPtr->seqNoReplica == 0 ||
+ errCode != ZTUPLE_ALREADY_EXIST ||
+ (tcPtr->operation == ZREAD && (tcPtr->dirtyOp || tcPtr->opSimple)));
+ }
+ tcPtr->abortState = TcConnectionrec::ABORT_FROM_LQH;
+ abortCommonLab(signal);
+ return;
+}//Dblqh::execACCKEYREF()
+
+void Dblqh::localAbortStateHandlerLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->abortState != TcConnectionrec::ABORT_IDLE) {
+ jam();
+ return;
+ }//if
+ regTcPtr->activeCreat = ZFALSE;
+ regTcPtr->abortState = TcConnectionrec::ABORT_FROM_LQH;
+ regTcPtr->errorCode = terrorCode;
+ abortStateHandlerLab(signal);
+ return;
+}//Dblqh::localAbortStateHandlerLab()
+
+void Dblqh::abortStateHandlerLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ switch (regTcPtr->transactionState) {
+ case TcConnectionrec::PREPARED:
+ jam();
+/* ------------------------------------------------------------------------- */
+/*THE OPERATION IS ALREADY PREPARED AND SENT TO THE NEXT LQH OR BACK TO TC. */
+/*WE CAN SIMPLY CONTINUE WITH THE ABORT PROCESS. */
+/*IF IT WAS A CHECK FOR TRANSACTION STATUS THEN WE REPORT THE STATUS TO THE */
+/*NEW TC AND CONTINUE WITH THE NEXT OPERATION IN LQH. */
+/* ------------------------------------------------------------------------- */
+ if (regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC) {
+ jam();
+ sendLqhTransconf(signal, LqhTransConf::Prepared);
+ return;
+ }//if
+ break;
+ case TcConnectionrec::LOG_COMMIT_WRITTEN_WAIT_SIGNAL:
+ case TcConnectionrec::LOG_COMMIT_QUEUED_WAIT_SIGNAL:
+ jam();
+/* ------------------------------------------------------------------------- */
+// We can only reach these states for multi-updates on a record in a transaction.
+// We know that at least one of those has received the COMMIT signal, thus we
+// declare us only prepared since we then receive the expected COMMIT signal.
+/* ------------------------------------------------------------------------- */
+ ndbrequire(regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC);
+ sendLqhTransconf(signal, LqhTransConf::Prepared);
+ break;
+ case TcConnectionrec::WAIT_TUPKEYINFO:
+ case TcConnectionrec::WAIT_ATTR:
+ jam();
+/* ------------------------------------------------------------------------- */
+/* WE ARE CURRENTLY WAITING FOR MORE INFORMATION. WE CAN START THE ABORT */
+/* PROCESS IMMEDIATELY. THE KEYINFO AND ATTRINFO SIGNALS WILL BE DROPPED */
+/* SINCE THE ABORT STATE WILL BE SET. */
+/* ------------------------------------------------------------------------- */
+ break;
+ case TcConnectionrec::WAIT_TUP:
+ jam();
+/* ------------------------------------------------------------------------- */
+// TUP is currently active. We have to wait for the TUPKEYREF or TUPKEYCONF
+// to arrive since we might otherwise jeopardise the local checkpoint
+// consistency in overload situations.
+/* ------------------------------------------------------------------------- */
+ regTcPtr->transactionState = TcConnectionrec::WAIT_TUP_TO_ABORT;
+ return;
+ case TcConnectionrec::WAIT_ACC:
+ jam();
+ if (regTcPtr->listState == TcConnectionrec::ACC_BLOCK_LIST) {
+ jam();
+/* ------------------------------------------------------------------------- */
+// If the operation is in the ACC Blocked list the operation is not allowed
+// to start yet. We release it from the ACC Blocked list and will go through
+// the gate in abortCommonLab(..) where it will be blocked.
+/* ------------------------------------------------------------------------- */
+ fragptr.i = regTcPtr->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ releaseAccList(signal);
+ } else {
+ jam();
+/* ------------------------------------------------------------------------- */
+// We start the abort immediately since the operation is still in the active
+// list and the fragment cannot have been frozen yet. By sending LCP_HOLDOPCONF
+// as direct signals we avoid the problem that we might find the operation
+// in an unexpected list in ACC.
+// We cannot accept being blocked before aborting ACC here since that would
+// lead to seriously complex issues.
+/* ------------------------------------------------------------------------- */
+ abortContinueAfterBlockedLab(signal, false);
+ return;
+ }//if
+ break;
+ case TcConnectionrec::LOG_QUEUED:
+ jam();
+/* ------------------------------------------------------------------------- */
+/*CURRENTLY QUEUED FOR LOGGING. WAIT UNTIL THE LOG RECORD HAVE BEEN INSERTED */
+/*AND THEN CONTINUE THE ABORT PROCESS. */
+//Could also be waiting for an overloaded log disk. In this case it is easy
+//to abort when CONTINUEB arrives.
+/* ------------------------------------------------------------------------- */
+ return;
+ break;
+ case TcConnectionrec::STOPPED:
+ jam();
+ /* ---------------------------------------------------------------------
+ * WE ARE CURRENTLY QUEUED FOR ACCESS TO THE FRAGMENT BY A LCP
+ * Since nothing has been done, just release operation
+ * i.e. no prepare log record has been written
+ * so no abort log records needs to be written
+ */
+ releaseWaitQueue(signal);
+ continueAfterLogAbortWriteLab(signal);
+ return;
+ break;
+ case TcConnectionrec::WAIT_AI_AFTER_ABORT:
+ jam();
+/* ------------------------------------------------------------------------- */
+/* ABORT OF ACC AND TUP ALREADY COMPLETED. THIS STATE IS ONLY USED WHEN */
+/* CREATING A NEW FRAGMENT. */
+/* ------------------------------------------------------------------------- */
+ continueAbortLab(signal);
+ return;
+ break;
+ case TcConnectionrec::WAIT_TUP_TO_ABORT:
+ case TcConnectionrec::ABORT_STOPPED:
+ case TcConnectionrec::LOG_ABORT_QUEUED:
+ case TcConnectionrec::WAIT_ACC_ABORT:
+ case TcConnectionrec::ABORT_QUEUED:
+ jam();
+/* ------------------------------------------------------------------------- */
+/*ABORT IS ALREADY ONGOING DUE TO SOME ERROR. WE HAVE ALREADY SET THE STATE */
+/*OF THE ABORT SO THAT WE KNOW THAT TC EXPECTS A REPORT. WE CAN THUS SIMPLY */
+/*EXIT. */
+/* ------------------------------------------------------------------------- */
+ return;
+ break;
+ case TcConnectionrec::COMMIT_STOPPED:
+ case TcConnectionrec::LOG_COMMIT_QUEUED:
+ case TcConnectionrec::COMMIT_QUEUED:
+ jam();
+/* ------------------------------------------------------------------------- */
+/*THIS IS ONLY AN ALLOWED STATE IF A DIRTY WRITE OR SIMPLE READ IS PERFORMED.*/
+/*IF WE ARE MERELY CHECKING THE TRANSACTION STATE IT IS ALSO AN ALLOWED STATE*/
+/* ------------------------------------------------------------------------- */
+ if (regTcPtr->dirtyOp == ZTRUE) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*COMPLETE THE DIRTY WRITE AND THEN REPORT COMPLETED BACK TO TC. SINCE IT IS */
+/*A DIRTY WRITE IT IS ALLOWED TO COMMIT EVEN IF THE TRANSACTION ABORTS. */
+/* ------------------------------------------------------------------------- */
+ return;
+ }//if
+ if (regTcPtr->simpleRead) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*A SIMPLE READ IS CURRENTLY RELEASING THE LOCKS OR WAITING FOR ACCESS TO */
+/*ACC TO CLEAR THE LOCKS. COMPLETE THIS PROCESS AND THEN RETURN AS NORMAL. */
+/*NO DATA HAS CHANGED DUE TO THIS SIMPLE READ ANYWAY. */
+/* ------------------------------------------------------------------------- */
+ return;
+ }//if
+ ndbrequire(regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC);
+ jam();
+/* ------------------------------------------------------------------------- */
+/*WE ARE ONLY CHECKING THE STATUS OF THE TRANSACTION. IT IS COMMITTING. */
+/*COMPLETE THE COMMIT LOCALLY AND THEN SEND REPORT OF COMMITTED TO THE NEW TC*/
+/* ------------------------------------------------------------------------- */
+ return;
+ break;
+ case TcConnectionrec::COMMITTED:
+ jam();
+ ndbrequire(regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC);
+/* ------------------------------------------------------------------------- */
+/*WE ARE CHECKING TRANSACTION STATUS. REPORT COMMITTED AND CONTINUE WITH THE */
+/*NEXT OPERATION. */
+/* ------------------------------------------------------------------------- */
+ sendLqhTransconf(signal, LqhTransConf::Committed);
+ return;
+ break;
+ default:
+ ndbrequire(false);
+/* ------------------------------------------------------------------------- */
+/*THE STATE WAS NOT AN ALLOWED STATE ON A NORMAL OPERATION. SCANS AND COPY */
+/*FRAGMENT OPERATIONS SHOULD HAVE EXECUTED IN ANOTHER PATH. */
+/* ------------------------------------------------------------------------- */
+ break;
+ }//switch
+ abortCommonLab(signal);
+ return;
+}//Dblqh::abortStateHandlerLab()
+
+void Dblqh::abortErrorLab(Signal* signal)
+{
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->abortState == TcConnectionrec::ABORT_IDLE) {
+ jam();
+ regTcPtr->abortState = TcConnectionrec::ABORT_FROM_LQH;
+ regTcPtr->errorCode = terrorCode;
+ }//if
+ /* -----------------------------------------------------------------------
+ * ACTIVE CREATION IS RESET FOR ALL ERRORS WHICH SHOULD BE HANDLED
+ * WITH NORMAL ABORT HANDLING.
+ * ----------------------------------------------------------------------- */
+ regTcPtr->activeCreat = ZFALSE;
+ abortCommonLab(signal);
+ return;
+}//Dblqh::abortErrorLab()
+
+void Dblqh::abortCommonLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ const Uint32 commitAckMarker = regTcPtr->commitAckMarker;
+ if(regTcPtr->activeCreat != ZTRUE && commitAckMarker != RNIL){
+ /**
+ * There is no NR ongoing and we have a marker
+ */
+ jam();
+#ifdef MARKER_TRACE
+ {
+ CommitAckMarkerPtr tmp;
+ m_commitAckMarkerHash.getPtr(tmp, commitAckMarker);
+ ndbout_c("Abo marker[%.8x %.8x]", tmp.p->transid1, tmp.p->transid2);
+ }
+#endif
+ m_commitAckMarkerHash.release(commitAckMarker);
+ regTcPtr->commitAckMarker = RNIL;
+ }
+
+ fragptr.i = regTcPtr->fragmentptr;
+ if (fragptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ case Fragrecord::CRASH_RECOVERING:
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ linkActiveFrag(signal);
+ abortContinueAfterBlockedLab(signal, true);
+ return;
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ regTcPtr->transactionState = TcConnectionrec::ABORT_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ } else {
+ jam();
+ continueAbortLab(signal);
+ }//if
+}//Dblqh::abortCommonLab()
+
+void Dblqh::abortContinueAfterBlockedLab(Signal* signal, bool canBlock)
+{
+ /* ------------------------------------------------------------------------
+ * INPUT: TC_CONNECTPTR ACTIVE OPERATION RECORD
+ * ------------------------------------------------------------------------
+ * ------------------------------------------------------------------------
+ * CAN COME HERE AS RESTART AFTER BEING BLOCKED BY A LOCAL CHECKPOINT.
+ * ------------------------------------------------------------------------
+ * ALSO AS PART OF A NORMAL ABORT WITHOUT BLOCKING.
+ * WE MUST ABORT TUP BEFORE ACC TO ENSURE THAT NO ONE RACES IN
+ * AND SEES A STATE IN TUP.
+ * ------------------------------------------------------------------------ */
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ fragptr.i = regTcPtr->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if ((cCommitBlocked == true) &&
+ (fragptr.p->fragActiveStatus == ZTRUE) &&
+ (canBlock == true) &&
+ (regTcPtr->operation != ZREAD)) {
+ jam();
+/* ------------------------------------------------------------------------- */
+// TUP and/or ACC have problems in writing the undo log to disk fast enough.
+// We must avoid the abort at this time and try later instead. The fragment
+// is also active with a local checkpoint and this commit can generate UNDO
+// log records that overflow the UNDO log buffer.
+//
+// In certain situations it is simply too complex to insert a wait state here
+// since ACC is active and we cannot release the operation from the active
+// list without causing great complexity.
+/* ------------------------------------------------------------------------- */
+/*---------------------------------------------------------------------------*/
+// We must delay the write of abort info to the log to safe-guard against
+// a crash due to lack of log pages. We temporary stop all log writes to this
+// log part to ensure that we don't get a buffer explosion in the delayed
+// signal buffer instead.
+/*---------------------------------------------------------------------------*/
+ releaseActiveFrag(signal);
+ logPartPtr.i = regTcPtr->hashValue & 3;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ linkWaitLog(signal, logPartPtr);
+ regTcPtr->transactionState = TcConnectionrec::ABORT_QUEUED;
+ if (logPartPtr.p->logPartState == LogPartRecord::IDLE) {
+ jam();
+ logPartPtr.p->logPartState = LogPartRecord::ACTIVE;
+ }//if
+ return;
+ }//if
+ signal->theData[0] = regTcPtr->tupConnectrec;
+ EXECUTE_DIRECT(DBTUP, GSN_TUP_ABORTREQ, signal, 1);
+ regTcPtr->transactionState = TcConnectionrec::WAIT_ACC_ABORT;
+ signal->theData[0] = regTcPtr->accConnectrec;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_ABORTREQ, signal, 1);
+ /* ------------------------------------------------------------------------
+ * We need to insert a real-time break by sending ACC_ABORTCONF through the
+ * job buffer to ensure that we catch any ACCKEYCONF or TUPKEYCONF or
+ * TUPKEYREF that are in the job buffer but not yet processed. Doing
+ * everything without that would race and create a state error when they
+ * are executed.
+ * ----------------------------------------------------------------------- */
+ return;
+}//Dblqh::abortContinueAfterBlockedLab()
+
+/* ******************>> */
+/* ACC_ABORTCONF > */
+/* ******************>> */
+void Dblqh::execACC_ABORTCONF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ ndbrequire(regTcPtr->transactionState == TcConnectionrec::WAIT_ACC_ABORT);
+ if (regTcPtr->activeCreat == ZTRUE) {
+ /* ----------------------------------------------------------------------
+ * A NORMAL EVENT DURING CREATION OF A FRAGMENT. WE NOW NEED TO CONTINUE
+ * WITH NORMAL COMMIT PROCESSING.
+ * ---------------------------------------------------------------------- */
+ if (regTcPtr->currTupAiLen == regTcPtr->totReclenAi) {
+ jam();
+ regTcPtr->abortState = TcConnectionrec::ABORT_IDLE;
+ rwConcludedLab(signal);
+ return;
+ } else {
+ ndbrequire(regTcPtr->currTupAiLen < regTcPtr->totReclenAi);
+ jam();
+ releaseActiveFrag(signal);
+ regTcPtr->transactionState = TcConnectionrec::WAIT_AI_AFTER_ABORT;
+ return;
+ }//if
+ }//if
+ releaseActiveFrag(signal);
+ continueAbortLab(signal);
+ return;
+}//Dblqh::execACC_ABORTCONF()
+
+void Dblqh::continueAbortLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ /* ------------------------------------------------------------------------
+ * AN ERROR OCCURED IN THE ACTIVE CREATION AFTER THE ABORT PHASE.
+ * WE NEED TO CONTINUE WITH A NORMAL ABORT.
+ * ------------------------------------------------------------------------
+ * ALSO USED FOR NORMAL CLEAN UP AFTER A NORMAL ABORT.
+ * ------------------------------------------------------------------------
+ * ALSO USED WHEN NO FRAGMENT WAS SET UP ON OPERATION.
+ * ------------------------------------------------------------------------ */
+ if (regTcPtr->logWriteState == TcConnectionrec::WRITTEN) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * I NEED TO INSERT A ABORT LOG RECORD SINCE WE ARE WRITING LOG IN THIS
+ * TRANSACTION.
+ * ---------------------------------------------------------------------- */
+ initLogPointers(signal);
+ if (logPartPtr.p->logPartState == LogPartRecord::ACTIVE) {
+ jam();
+ /* --------------------------------------------------------------------
+ * A PREPARE OPERATION IS CURRENTLY WRITING IN THE LOG.
+ * WE MUST WAIT ON OUR TURN TO WRITE THE LOG.
+ * IT IS NECESSARY TO WRITE ONE LOG RECORD COMPLETELY
+ * AT A TIME OTHERWISE WE WILL SCRAMBLE THE LOG.
+ * -------------------------------------------------------------------- */
+ linkWaitLog(signal, logPartPtr);
+ regTcPtr->transactionState = TcConnectionrec::LOG_ABORT_QUEUED;
+ return;
+ }//if
+ if (cnoOfLogPages == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+// We must delay the write of commit info to the log to safe-guard against
+// a crash due to lack of log pages. We temporary stop all log writes to this
+// log part to ensure that we don't get a buffer explosion in the delayed
+// signal buffer instead.
+/*---------------------------------------------------------------------------*/
+ linkWaitLog(signal, logPartPtr);
+ regTcPtr->transactionState = TcConnectionrec::LOG_ABORT_QUEUED;
+ if (logPartPtr.p->logPartState == LogPartRecord::IDLE) {
+ jam();
+ logPartPtr.p->logPartState = LogPartRecord::ACTIVE;
+ }//if
+ return;
+ }//if
+ writeAbortLog(signal);
+ removeLogTcrec(signal);
+ } else if (regTcPtr->logWriteState == TcConnectionrec::NOT_STARTED) {
+ jam();
+ } else if (regTcPtr->logWriteState == TcConnectionrec::NOT_WRITTEN) {
+ jam();
+ /* ------------------------------------------------------------------
+ * IT IS A READ OPERATION OR OTHER OPERATION THAT DO NOT USE THE LOG.
+ * ------------------------------------------------------------------ */
+ /* ------------------------------------------------------------------
+ * THE LOG HAS NOT BEEN WRITTEN SINCE THE LOG FLAG WAS FALSE.
+ * THIS CAN OCCUR WHEN WE ARE STARTING A NEW FRAGMENT.
+ * ------------------------------------------------------------------ */
+ regTcPtr->logWriteState = TcConnectionrec::NOT_STARTED;
+ } else {
+ ndbrequire(regTcPtr->logWriteState == TcConnectionrec::NOT_WRITTEN_WAIT);
+ jam();
+ /* ----------------------------------------------------------------
+ * THE STATE WAS SET TO NOT_WRITTEN BY THE OPERATION BUT LATER
+ * A SCAN OF ALL OPERATION RECORD CHANGED IT INTO NOT_WRITTEN_WAIT.
+ * THIS INDICATES THAT WE ARE WAITING FOR THIS OPERATION TO COMMIT
+ * OR ABORT SO THAT WE CAN FIND THE
+ * STARTING GLOBAL CHECKPOINT OF THIS NEW FRAGMENT.
+ * ---------------------------------------------------------------- */
+ checkScanTcCompleted(signal);
+ }//if
+ continueAfterLogAbortWriteLab(signal);
+ return;
+}//Dblqh::continueAbortLab()
+
+void Dblqh::continueAfterLogAbortWriteLab(Signal* signal)
+{
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->simpleRead) {
+ jam();
+ TcKeyRef * const tcKeyRef = (TcKeyRef *) signal->getDataPtrSend();
+
+ tcKeyRef->connectPtr = regTcPtr->applOprec;
+ tcKeyRef->transId[0] = regTcPtr->transid[0];
+ tcKeyRef->transId[1] = regTcPtr->transid[1];
+ tcKeyRef->errorCode = regTcPtr->errorCode;
+ sendSignal(regTcPtr->applRef,
+ GSN_TCKEYREF, signal, TcKeyRef::SignalLength, JBB);
+ cleanUp(signal);
+ return;
+ }//if
+ if (regTcPtr->abortState == TcConnectionrec::ABORT_FROM_LQH) {
+ LqhKeyRef * const lqhKeyRef = (LqhKeyRef *)signal->getDataPtrSend();
+
+ jam();
+ lqhKeyRef->userRef = regTcPtr->clientConnectrec;
+ lqhKeyRef->connectPtr = regTcPtr->tcOprec;
+ lqhKeyRef->errorCode = regTcPtr->errorCode;
+ lqhKeyRef->transId1 = regTcPtr->transid[0];
+ lqhKeyRef->transId2 = regTcPtr->transid[1];
+ sendSignal(regTcPtr->clientBlockref, GSN_LQHKEYREF, signal,
+ LqhKeyRef::SignalLength, JBB);
+ } else if (regTcPtr->abortState == TcConnectionrec::ABORT_FROM_TC) {
+ jam();
+ sendAborted(signal);
+ } else if (regTcPtr->abortState == TcConnectionrec::NEW_FROM_TC) {
+ jam();
+ sendLqhTransconf(signal, LqhTransConf::Aborted);
+ } else {
+ ndbrequire(regTcPtr->abortState == TcConnectionrec::REQ_FROM_TC);
+ jam();
+ signal->theData[0] = regTcPtr->reqRef;
+ signal->theData[1] = tcConnectptr.i;
+ signal->theData[2] = cownNodeid;
+ signal->theData[3] = regTcPtr->transid[0];
+ signal->theData[4] = regTcPtr->transid[1];
+ sendSignal(regTcPtr->reqBlockref, GSN_ABORTCONF,
+ signal, 5, JBB);
+ }//if
+ cleanUp(signal);
+}//Dblqh::continueAfterLogAbortWriteLab()
+
+/* ##########################################################################
+ * ####### MODULE TO HANDLE TC FAILURE #######
+ *
+ * ########################################################################## */
+
+/* ************************************************************************>>
+ * NODE_FAILREP: Node failure report. Sender Ndbcntr. Set status of failed
+ * node to down and reply with NF_COMPLETEREP to DIH which will report that
+ * LQH has completed failure handling.
+ * ************************************************************************>> */
+void Dblqh::execNODE_FAILREP(Signal* signal)
+{
+ UintR TfoundNodes = 0;
+ UintR TnoOfNodes;
+ UintR Tdata[MAX_NDB_NODES];
+ Uint32 i;
+
+ NodeFailRep * const nodeFail = (NodeFailRep *)&signal->theData[0];
+
+ TnoOfNodes = nodeFail->noOfNodes;
+ UintR index = 0;
+ for (i = 1; i < MAX_NDB_NODES; i++) {
+ jam();
+ if(NodeBitmask::get(nodeFail->theNodes, i)){
+ jam();
+ Tdata[index] = i;
+ index++;
+ }//if
+ }//for
+
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+
+ ndbrequire(index == TnoOfNodes);
+ ndbrequire(cnoOfNodes - 1 < MAX_NDB_NODES);
+ for (i = 0; i < TnoOfNodes; i++) {
+ const Uint32 nodeId = Tdata[i];
+ lcpPtr.p->m_EMPTY_LCP_REQ.clear(nodeId);
+
+ for (Uint32 j = 0; j < cnoOfNodes; j++) {
+ jam();
+ if (cnodeData[j] == nodeId){
+ jam();
+ cnodeStatus[j] = ZNODE_DOWN;
+
+ TfoundNodes++;
+ }//if
+ }//for
+ NFCompleteRep * const nfCompRep = (NFCompleteRep *)&signal->theData[0];
+ nfCompRep->blockNo = DBLQH;
+ nfCompRep->nodeId = cownNodeid;
+ nfCompRep->failedNodeId = Tdata[i];
+ sendSignal(DBDIH_REF, GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+ }//for
+ ndbrequire(TnoOfNodes == TfoundNodes);
+}//Dblqh::execNODE_FAILREP()
+
+/* ************************************************************************>>
+ * LQH_TRANSREQ: Report status of all transactions where TC was coordinated
+ * by a crashed TC
+ * ************************************************************************>> */
+/* ************************************************************************>>
+ * THIS SIGNAL IS RECEIVED AFTER A NODE CRASH.
+ * THE NODE HAD A TC AND COORDINATED A NUMBER OF TRANSACTIONS.
+ * NOW THE MASTER NODE IS PICKING UP THOSE TRANSACTIONS
+ * TO COMPLETE THEM. EITHER ABORT THEM OR COMMIT THEM.
+ * ************************************************************************>> */
+void Dblqh::execLQH_TRANSREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 newTcPtr = signal->theData[0];
+ BlockReference newTcBlockref = signal->theData[1];
+ Uint32 oldNodeId = signal->theData[2];
+ tcNodeFailptr.i = oldNodeId;
+ ptrCheckGuard(tcNodeFailptr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+ if ((tcNodeFailptr.p->tcFailStatus == TcNodeFailRecord::TC_STATE_TRUE) ||
+ (tcNodeFailptr.p->tcFailStatus == TcNodeFailRecord::TC_STATE_BREAK)) {
+ jam();
+ tcNodeFailptr.p->lastNewTcBlockref = newTcBlockref;
+ /* ------------------------------------------------------------------------
+ * WE HAVE RECEIVED A SIGNAL SPECIFYING THAT WE NEED TO HANDLE THE FAILURE
+ * OF A TC. NOW WE RECEIVE ANOTHER SIGNAL WITH THE SAME ORDER. THIS CAN
+ * OCCUR IF THE NEW TC FAILS. WE MUST BE CAREFUL IN THIS CASE SO THAT WE DO
+ * NOT START PARALLEL ACTIVITIES TRYING TO DO THE SAME THING. WE SAVE THE
+ * NEW BLOCK REFERENCE TO THE LAST NEW TC IN A VARIABLE AND ASSIGN TO IT TO
+ * NEW_TC_BLOCKREF WHEN THE OLD PROCESS RETURNS TO LQH_TRANS_NEXT. IT IS
+ * CERTAIN TO COME THERE SINCE THIS IS THE ONLY PATH TO TAKE CARE OF THE
+ * NEXT TC CONNECT RECORD. WE SET THE STATUS TO BREAK TO INDICATE TO THE OLD
+ * PROCESS WHAT IS HAPPENING.
+ * ------------------------------------------------------------------------ */
+ tcNodeFailptr.p->lastNewTcRef = newTcPtr;
+ tcNodeFailptr.p->tcFailStatus = TcNodeFailRecord::TC_STATE_BREAK;
+ return;
+ }//if
+ tcNodeFailptr.p->oldNodeId = oldNodeId;
+ tcNodeFailptr.p->newTcBlockref = newTcBlockref;
+ tcNodeFailptr.p->newTcRef = newTcPtr;
+ tcNodeFailptr.p->tcRecNow = 0;
+ tcNodeFailptr.p->tcFailStatus = TcNodeFailRecord::TC_STATE_TRUE;
+ signal->theData[0] = ZLQH_TRANS_NEXT;
+ signal->theData[1] = tcNodeFailptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//Dblqh::execLQH_TRANSREQ()
+
+void Dblqh::lqhTransNextLab(Signal* signal)
+{
+ UintR tend;
+ UintR tstart;
+ UintR guard0;
+
+ if (tcNodeFailptr.p->tcFailStatus == TcNodeFailRecord::TC_STATE_BREAK) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * AN INTERRUPTION TO THIS NODE FAIL HANDLING WAS RECEIVED AND A NEW
+ * TC HAVE BEEN ASSIGNED TO TAKE OVER THE FAILED TC. PROBABLY THE OLD
+ * NEW TC HAVE FAILED.
+ * ---------------------------------------------------------------------- */
+ tcNodeFailptr.p->newTcBlockref = tcNodeFailptr.p->lastNewTcBlockref;
+ tcNodeFailptr.p->newTcRef = tcNodeFailptr.p->lastNewTcRef;
+ tcNodeFailptr.p->tcRecNow = 0;
+ tcNodeFailptr.p->tcFailStatus = TcNodeFailRecord::TC_STATE_TRUE;
+ }//if
+ tstart = tcNodeFailptr.p->tcRecNow;
+ tend = tstart + 200;
+ guard0 = tend;
+ for (tcConnectptr.i = tstart; tcConnectptr.i <= guard0; tcConnectptr.i++) {
+ jam();
+ if (tcConnectptr.i >= ctcConnectrecFileSize) {
+ jam();
+ /**
+ * Finished with scanning operation record
+ *
+ * now scan markers
+ */
+ scanMarkers(signal, tcNodeFailptr.i, 0, RNIL);
+ return;
+ }//if
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ if (tcConnectptr.p->transactionState != TcConnectionrec::IDLE) {
+ if (tcConnectptr.p->transactionState != TcConnectionrec::TC_NOT_CONNECTED) {
+ if (tcConnectptr.p->tcScanRec == RNIL) {
+ if (refToNode(tcConnectptr.p->tcBlockref) == tcNodeFailptr.p->oldNodeId) {
+ if (tcConnectptr.p->operation != ZREAD) {
+ jam();
+ tcConnectptr.p->tcNodeFailrec = tcNodeFailptr.i;
+ tcConnectptr.p->abortState = TcConnectionrec::NEW_FROM_TC;
+ abortStateHandlerLab(signal);
+ return;
+ } else {
+ jam();
+ if (tcConnectptr.p->opSimple != ZTRUE) {
+ jam();
+ tcConnectptr.p->tcNodeFailrec = tcNodeFailptr.i;
+ tcConnectptr.p->abortState = TcConnectionrec::NEW_FROM_TC;
+ abortStateHandlerLab(signal);
+ return;
+ }//if
+ }//if
+ }//if
+ } else {
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ if (scanptr.p->scanType == ScanRecord::COPY) {
+ jam();
+ if (scanptr.p->scanNodeId == tcNodeFailptr.p->oldNodeId) {
+ jam();
+ /* ------------------------------------------------------------
+ * THE RECEIVER OF THE COPY HAVE FAILED.
+ * WE HAVE TO CLOSE THE COPY PROCESS.
+ * ------------------------------------------------------------ */
+ tcConnectptr.p->tcNodeFailrec = tcNodeFailptr.i;
+ tcConnectptr.p->abortState = TcConnectionrec::NEW_FROM_TC;
+ closeCopyRequestLab(signal);
+ return;
+ }//if
+ } else {
+ if (scanptr.p->scanType == ScanRecord::SCAN) {
+ jam();
+ if (refToNode(tcConnectptr.p->tcBlockref) ==
+ tcNodeFailptr.p->oldNodeId) {
+ jam();
+ tcConnectptr.p->tcNodeFailrec = tcNodeFailptr.i;
+ tcConnectptr.p->abortState = TcConnectionrec::NEW_FROM_TC;
+ closeScanRequestLab(signal);
+ return;
+ }//if
+ } else {
+ jam();
+ /* ------------------------------------------------------------
+ * THIS IS AN ERROR THAT SHOULD NOT OCCUR. WE CRASH THE SYSTEM.
+ * ------------------------------------------------------------ */
+ systemErrorLab(signal);
+ return;
+ }//if
+ }//if
+ }//if
+ }//if
+ }//if
+ }//for
+ tcNodeFailptr.p->tcRecNow = tend + 1;
+ signal->theData[0] = ZLQH_TRANS_NEXT;
+ signal->theData[1] = tcNodeFailptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//Dblqh::lqhTransNextLab()
+
+void
+Dblqh::scanMarkers(Signal* signal,
+ Uint32 tcNodeFail,
+ Uint32 startBucket,
+ Uint32 i){
+
+ jam();
+
+ TcNodeFailRecordPtr tcNodeFailPtr;
+ tcNodeFailPtr.i = tcNodeFail;
+ ptrCheckGuard(tcNodeFailPtr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+ const Uint32 crashedTcNodeId = tcNodeFailPtr.p->oldNodeId;
+
+ CommitAckMarkerIterator iter;
+ if(i == RNIL){
+ m_commitAckMarkerHash.next(startBucket, iter);
+ } else {
+ jam();
+ iter.curr.i = i;
+ iter.bucket = startBucket;
+ m_commitAckMarkerHash.getPtr(iter.curr);
+ m_commitAckMarkerHash.next(iter);
+ }
+
+ const Uint32 RT_BREAK = 256;
+ for(i = 0; i<RT_BREAK || iter.bucket == startBucket; i++){
+ jam();
+
+ if(iter.curr.i == RNIL){
+ /**
+ * Done with iteration
+ */
+ jam();
+
+ tcNodeFailPtr.p->tcFailStatus = TcNodeFailRecord::TC_STATE_FALSE;
+ signal->theData[0] = tcNodeFailPtr.p->newTcRef;
+ signal->theData[1] = cownNodeid;
+ signal->theData[2] = LqhTransConf::LastTransConf;
+ sendSignal(tcNodeFailPtr.p->newTcBlockref, GSN_LQH_TRANSCONF,
+ signal, 3, JBB);
+ return;
+ }
+
+ if(iter.curr.p->tcNodeId == crashedTcNodeId){
+ jam();
+
+ /**
+ * Found marker belonging to crashed node
+ */
+ LqhTransConf * const lqhTransConf = (LqhTransConf *)&signal->theData[0];
+ lqhTransConf->tcRef = tcNodeFailPtr.p->newTcRef;
+ lqhTransConf->lqhNodeId = cownNodeid;
+ lqhTransConf->operationStatus = LqhTransConf::Marker;
+ lqhTransConf->transId1 = iter.curr.p->transid1;
+ lqhTransConf->transId2 = iter.curr.p->transid2;
+ lqhTransConf->apiRef = iter.curr.p->apiRef;
+ lqhTransConf->apiOpRec = iter.curr.p->apiOprec;
+ sendSignal(tcNodeFailPtr.p->newTcBlockref, GSN_LQH_TRANSCONF,
+ signal, 7, JBB);
+
+ signal->theData[0] = ZSCAN_MARKERS;
+ signal->theData[1] = tcNodeFailPtr.i;
+ signal->theData[2] = iter.bucket;
+ signal->theData[3] = iter.curr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ return;
+ }
+
+ m_commitAckMarkerHash.next(iter);
+ }
+
+ signal->theData[0] = ZSCAN_MARKERS;
+ signal->theData[1] = tcNodeFailPtr.i;
+ signal->theData[2] = iter.bucket;
+ signal->theData[3] = RNIL;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+}
+
+/* #########################################################################
+ * ####### SCAN MODULE #######
+ *
+ * #########################################################################
+ * -------------------------------------------------------------------------
+ * THIS MODULE CONTAINS THE CODE THAT HANDLES A SCAN OF A PARTICULAR FRAGMENT
+ * IT OPERATES UNDER THE CONTROL OF TC AND ORDERS ACC TO PERFORM A SCAN OF
+ * ALL TUPLES IN THE FRAGMENT. TUP PERFORMS THE NECESSARY SEARCH CONDITIONS
+ * TO ENSURE THAT ONLY VALID TUPLES ARE RETURNED TO THE APPLICATION.
+ * ------------------------------------------------------------------------- */
+/* *************** */
+/* ACC_SCANCONF > */
+/* *************** */
+void Dblqh::execACC_SCANCONF(Signal* signal)
+{
+ AccScanConf * const accScanConf = (AccScanConf *)&signal->theData[0];
+ jamEntry();
+ scanptr.i = accScanConf->scanPtr;
+ c_scanRecordPool.getPtr(scanptr);
+ if (scanptr.p->scanState == ScanRecord::WAIT_ACC_SCAN) {
+ accScanConfScanLab(signal);
+ } else {
+ ndbrequire(scanptr.p->scanState == ScanRecord::WAIT_ACC_COPY);
+ accScanConfCopyLab(signal);
+ }//if
+}//Dblqh::execACC_SCANCONF()
+
+/* ************>> */
+/* ACC_SCANREF > */
+/* ************>> */
+void Dblqh::execACC_SCANREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execACC_SCANREF()
+
+/* ***************>> */
+/* NEXT_SCANCONF > */
+/* ***************>> */
+void Dblqh::execNEXT_SCANCONF(Signal* signal)
+{
+ NextScanConf * const nextScanConf = (NextScanConf *)&signal->theData[0];
+ jamEntry();
+ scanptr.i = nextScanConf->scanPtr;
+ c_scanRecordPool.getPtr(scanptr);
+ if (nextScanConf->localKeyLength == 1) {
+ jam();
+ nextScanConf->localKey[1] =
+ nextScanConf->localKey[0] & MAX_TUPLES_PER_PAGE;
+ nextScanConf->localKey[0] = nextScanConf->localKey[0] >> MAX_TUPLES_BITS;
+ }//if
+ switch (scanptr.p->scanState) {
+ case ScanRecord::WAIT_CLOSE_SCAN:
+ jam();
+ accScanCloseConfLab(signal);
+ break;
+ case ScanRecord::WAIT_CLOSE_COPY:
+ jam();
+ accCopyCloseConfLab(signal);
+ break;
+ case ScanRecord::WAIT_NEXT_SCAN:
+ jam();
+ nextScanConfScanLab(signal);
+ break;
+ case ScanRecord::WAIT_NEXT_SCAN_COPY:
+ jam();
+ nextScanConfCopyLab(signal);
+ break;
+ case ScanRecord::WAIT_RELEASE_LOCK:
+ jam();
+ ndbrequire(signal->length() == 1);
+ scanLockReleasedLab(signal);
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dblqh::execNEXT_SCANCONF()
+
+/* ***************> */
+/* NEXT_SCANREF > */
+/* ***************> */
+void Dblqh::execNEXT_SCANREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execNEXT_SCANREF()
+
+/* ******************> */
+/* STORED_PROCCONF > */
+/* ******************> */
+void Dblqh::execSTORED_PROCCONF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ Uint32 storedProcId = signal->theData[1];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ switch (scanptr.p->scanState) {
+ case ScanRecord::WAIT_STORED_PROC_SCAN:
+ jam();
+ scanptr.p->scanStoredProcId = storedProcId;
+ storedProcConfScanLab(signal);
+ break;
+ case ScanRecord::WAIT_DELETE_STORED_PROC_ID_SCAN:
+ jam();
+ releaseActiveFrag(signal);
+ tupScanCloseConfLab(signal);
+ break;
+ case ScanRecord::WAIT_STORED_PROC_COPY:
+ jam();
+ scanptr.p->scanStoredProcId = storedProcId;
+ storedProcConfCopyLab(signal);
+ break;
+ case ScanRecord::WAIT_DELETE_STORED_PROC_ID_COPY:
+ jam();
+ releaseActiveFrag(signal);
+ tupCopyCloseConfLab(signal);
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dblqh::execSTORED_PROCCONF()
+
+/* ****************** */
+/* STORED_PROCREF > */
+/* ****************** */
+void Dblqh::execSTORED_PROCREF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ Uint32 errorCode = signal->theData[1];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ switch (scanptr.p->scanState) {
+ case ScanRecord::WAIT_STORED_PROC_SCAN:
+ jam();
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ scanptr.p->scanStoredProcId = signal->theData[2];
+ tcConnectptr.p->errorCode = errorCode;
+ closeScanLab(signal);
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dblqh::execSTORED_PROCREF()
+
+/* --------------------------------------------------------------------------
+ * ENTER SCAN_NEXTREQ
+ * --------------------------------------------------------------------------
+ * PRECONDITION:
+ * TRANSACTION_STATE = SCAN_STATE
+ * SCAN_STATE = WAIT_SCAN_NEXTREQ
+ *
+ * Case scanLockHold: ZTRUE = Unlock previous round of
+ * scanned row(s) and fetch next set of rows.
+ * ZFALSE = Fetch new set of rows.
+ * Number of rows to read depends on parallelism and how many rows
+ * left to scan in the fragment. SCAN_NEXTREQ can also be sent with
+ * closeFlag == ZTRUE to close the scan.
+ * ------------------------------------------------------------------------- */
+void Dblqh::execSCAN_NEXTREQ(Signal* signal)
+{
+ jamEntry();
+ const ScanFragNextReq * const nextReq =
+ (ScanFragNextReq*)&signal->theData[0];
+ const Uint32 transid1 = nextReq->transId1;
+ const Uint32 transid2 = nextReq->transId2;
+ const Uint32 senderData = nextReq->senderData;
+
+ if (findTransaction(transid1, transid2, senderData) != ZOK){
+ jam();
+ DEBUG(senderData <<
+ " Received SCAN_NEXTREQ in LQH with close flag when closed");
+ ndbrequire(nextReq->closeFlag == ZTRUE);
+ return;
+ }
+
+ // Crash node if signal sender is same node
+ CRASH_INSERTION2(5021, refToNode(signal->senderBlockRef()) == cownNodeid);
+ // Crash node if signal sender is NOT same node
+ CRASH_INSERTION2(5022, refToNode(signal->senderBlockRef()) != cownNodeid);
+
+ if (ERROR_INSERTED(5023)){
+ // Drop signal if sender is same node
+ if (refToNode(signal->senderBlockRef()) == cownNodeid) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }
+ }//if
+ if (ERROR_INSERTED(5024)){
+ // Drop signal if sender is NOT same node
+ if (refToNode(signal->senderBlockRef()) != cownNodeid) {
+ CLEAR_ERROR_INSERT_VALUE;
+ 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(5030)){
+ ndbout << "ERROR 5030" << endl;
+ CLEAR_ERROR_INSERT_VALUE;
+ // Drop signal
+ return;
+ }//if
+
+ if(ERROR_INSERTED(5036)){
+ return;
+ }
+
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ ndbrequire(scanptr.i != RNIL);
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanTcWaiting = ZTRUE;
+
+ /* ------------------------------------------------------------------
+ * If close flag is set this scan should be closed
+ * If we are waiting for SCAN_NEXTREQ set flag to stop scanning and
+ * continue execution else set flags and wait until the scan
+ * completes itself
+ * ------------------------------------------------------------------ */
+ if (nextReq->closeFlag == ZTRUE){
+ jam();
+ if(ERROR_INSERTED(5034)){
+ CLEAR_ERROR_INSERT_VALUE;
+ }
+ if(ERROR_INSERTED(5036)){
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }
+ closeScanRequestLab(signal);
+ return;
+ }//if
+
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+
+ /**
+ * Change parameters while running
+ * (is currently not supported)
+ */
+ const Uint32 max_rows = nextReq->batch_size_rows;
+ const Uint32 max_bytes = nextReq->batch_size_bytes;
+ ndbrequire(scanptr.p->m_max_batch_size_rows == max_rows);
+ ndbrequire(scanptr.p->m_max_batch_size_bytes == max_bytes);
+
+ /* --------------------------------------------------------------------
+ * If scanLockHold = TRUE we need to unlock previous round of
+ * scanned records.
+ * scanReleaseLocks will set states for this and send a NEXT_SCANREQ.
+ * When confirm signal NEXT_SCANCONF arrives we call
+ * continueScanNextReqLab to continue scanning new rows and
+ * acquiring new locks.
+ * -------------------------------------------------------------------- */
+ if ((scanptr.p->scanLockHold == ZTRUE) &&
+ (scanptr.p->m_curr_batch_size_rows > 0)) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+
+ /* -----------------------------------------------------------------------
+ * We end up here when scanLockHold = FALSE or no rows was locked from
+ * previous round.
+ * Simply continue scanning.
+ * ----------------------------------------------------------------------- */
+ continueScanNextReqLab(signal);
+}//Dblqh::execSCAN_NEXTREQ()
+
+void Dblqh::continueScanNextReqLab(Signal* signal)
+{
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+ closeScanLab(signal);
+ return;
+ }//if
+
+ if(scanptr.p->m_last_row){
+ jam();
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
+ sendScanFragConf(signal, ZFALSE);
+ return;
+ }
+
+ // Update timer on tcConnectRecord
+ tcConnectptr.p->tcTimer = cLqhTimeOutCount;
+ init_acc_ptr_list(scanptr.p);
+ scanptr.p->scanFlag = NextScanReq::ZSCAN_NEXT;
+ scanNextLoopLab(signal);
+}//Dblqh::continueScanNextReqLab()
+
+/* -------------------------------------------------------------------------
+ * WE NEED TO RELEASE LOCKS BEFORE CONTINUING
+ * ------------------------------------------------------------------------- */
+void Dblqh::scanReleaseLocksLab(Signal* signal)
+{
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_RELEASE_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ }//switch
+ continueScanReleaseAfterBlockedLab(signal);
+}//Dblqh::scanReleaseLocksLab()
+
+void Dblqh::continueScanReleaseAfterBlockedLab(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanState = ScanRecord::WAIT_RELEASE_LOCK;
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1]=
+ get_acc_ptr_from_scan_record(scanptr.p,
+ scanptr.p->scanReleaseCounter -1,
+ false);
+ signal->theData[2] = NextScanReq::ZSCAN_COMMIT;
+ if (! scanptr.p->rangeScan)
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ else
+ sendSignal(tcConnectptr.p->tcTuxBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+}//Dblqh::continueScanReleaseAfterBlockedLab()
+
+/* -------------------------------------------------------------------------
+ * ENTER SCAN_NEXTREQ
+ * -------------------------------------------------------------------------
+ * SCAN_NEXT_REQ SIGNAL ARRIVED IN THE MIDDLE OF EXECUTION OF THE SCAN.
+ * IT WAS A REQUEST TO CLOSE THE SCAN. WE WILL CLOSE THE SCAN IN A
+ * CAREFUL MANNER TO ENSURE THAT NO ERROR OCCURS.
+ * -------------------------------------------------------------------------
+ * PRECONDITION:
+ * TRANSACTION_STATE = SCAN_STATE_USED
+ * TSCAN_COMPLETED = ZTRUE
+ * -------------------------------------------------------------------------
+ * WE CAN ALSO ARRIVE AT THIS LABEL AFTER A NODE CRASH OF THE SCAN
+ * COORDINATOR.
+ * ------------------------------------------------------------------------- */
+void Dblqh::closeScanRequestLab(Signal* signal)
+{
+ DEBUG("transactionState = " << tcConnectptr.p->transactionState);
+ switch (tcConnectptr.p->transactionState) {
+ case TcConnectionrec::SCAN_STATE_USED:
+ DEBUG("scanState = " << scanptr.p->scanState);
+ switch (scanptr.p->scanState) {
+ case ScanRecord::IN_QUEUE:
+ jam();
+ tupScanCloseConfLab(signal);
+ break;
+ case ScanRecord::WAIT_NEXT_SCAN:
+ jam();
+ /* -------------------------------------------------------------------
+ * SET COMPLETION STATUS AND WAIT FOR OPPORTUNITY TO STOP THE SCAN.
+ * ------------------------------------------------------------------- */
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ break;
+ case ScanRecord::WAIT_ACC_SCAN:
+ case ScanRecord::WAIT_STORED_PROC_SCAN:
+ jam();
+ /* -------------------------------------------------------------------
+ * WE ARE CURRENTLY STARTING UP THE SCAN. SET COMPLETED STATUS
+ * AND WAIT FOR COMPLETION OF STARTUP.
+ * ------------------------------------------------------------------- */
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ break;
+ case ScanRecord::WAIT_CLOSE_SCAN:
+ case ScanRecord::WAIT_DELETE_STORED_PROC_ID_SCAN:
+ jam();
+ /*empty*/;
+ break;
+ /* -------------------------------------------------------------------
+ * CLOSE IS ALREADY ONGOING. WE NEED NOT DO ANYTHING.
+ * ------------------------------------------------------------------- */
+ case ScanRecord::WAIT_RELEASE_LOCK:
+ jam();
+ /* -------------------------------------------------------------------
+ * WE ARE CURRENTLY RELEASING RECORD LOCKS. AFTER COMPLETING THIS
+ * WE WILL START TO CLOSE THE SCAN.
+ * ------------------------------------------------------------------- */
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ break;
+ case ScanRecord::WAIT_SCAN_NEXTREQ:
+ jam();
+ /* -------------------------------------------------------------------
+ * WE ARE WAITING FOR A SCAN_NEXTREQ FROM SCAN COORDINATOR(TC)
+ * WICH HAVE CRASHED. CLOSE THE SCAN
+ * ------------------------------------------------------------------- */
+ scanptr.p->scanCompletedStatus = ZTRUE;
+
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+
+ if (scanptr.p->scanLockHold == ZTRUE) {
+ if (scanptr.p->m_curr_batch_size_rows > 0) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ }//if
+ closeScanLab(signal);
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+ break;
+ case TcConnectionrec::WAIT_SCAN_AI:
+ jam();
+ /* ---------------------------------------------------------------------
+ * WE ARE STILL WAITING FOR THE ATTRIBUTE INFORMATION THAT
+ * OBVIOUSLY WILL NOT ARRIVE. WE CAN QUIT IMMEDIATELY HERE.
+ * --------------------------------------------------------------------- */
+ //XXX jonas this have to be wrong...
+ releaseOprec(signal);
+ if (tcConnectptr.p->abortState == TcConnectionrec::NEW_FROM_TC) {
+ jam();
+ tcNodeFailptr.i = tcConnectptr.p->tcNodeFailrec;
+ ptrCheckGuard(tcNodeFailptr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+ tcNodeFailptr.p->tcRecNow = tcConnectptr.i + 1;
+ signal->theData[0] = ZLQH_TRANS_NEXT;
+ signal->theData[1] = tcNodeFailptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+ tcConnectptr.p->abortState = TcConnectionrec::ABORT_ACTIVE;
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes= 0;
+ sendScanFragConf(signal, ZTRUE);
+ abort_scan(signal, scanptr.i, 0);
+ return;
+ break;
+ case TcConnectionrec::SCAN_TUPKEY:
+ case TcConnectionrec::SCAN_FIRST_STOPPED:
+ case TcConnectionrec::SCAN_CHECK_STOPPED:
+ case TcConnectionrec::SCAN_STOPPED:
+ jam();
+ /* ---------------------------------------------------------------------
+ * SET COMPLETION STATUS AND WAIT FOR OPPORTUNITY TO STOP THE SCAN.
+ * --------------------------------------------------------------------- */
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ break;
+ case TcConnectionrec::SCAN_RELEASE_STOPPED:
+ jam();
+ /* ---------------------------------------------------------------------
+ * WE ARE CURRENTLY RELEASING RECORD LOCKS. AFTER COMPLETING
+ * THIS WE WILL START TO CLOSE THE SCAN.
+ * --------------------------------------------------------------------- */
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ break;
+ case TcConnectionrec::SCAN_CLOSE_STOPPED:
+ jam();
+ /* ---------------------------------------------------------------------
+ * CLOSE IS ALREADY ONGOING. WE NEED NOT DO ANYTHING.
+ * --------------------------------------------------------------------- */
+ /*empty*/;
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dblqh::closeScanRequestLab()
+
+/* -------------------------------------------------------------------------
+ * ENTER NEXT_SCANCONF
+ * -------------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_RELEASE_LOCK
+ * ------------------------------------------------------------------------- */
+void Dblqh::scanLockReleasedLab(Signal* signal)
+{
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ releaseActiveFrag(signal);
+
+ if (scanptr.p->scanReleaseCounter == scanptr.p->m_curr_batch_size_rows) {
+ if ((scanptr.p->scanErrorCounter > 0) ||
+ (scanptr.p->scanCompletedStatus == ZTRUE)) {
+ jam();
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes = 0;
+ closeScanLab(signal);
+ } else if (scanptr.p->check_scan_batch_completed() &&
+ scanptr.p->scanLockHold != ZTRUE) {
+ jam();
+ scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
+ sendScanFragConf(signal, ZFALSE);
+ } else if (scanptr.p->m_last_row && !scanptr.p->scanLockHold) {
+ jam();
+ closeScanLab(signal);
+ return;
+ } else {
+ jam();
+ /*
+ * We came here after releasing locks after
+ * receiving SCAN_NEXTREQ from TC. We only come here
+ * when scanHoldLock == ZTRUE
+ */
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes = 0;
+ continueScanNextReqLab(signal);
+ }//if
+ } else if (scanptr.p->scanReleaseCounter < scanptr.p->m_curr_batch_size_rows) {
+ jam();
+ scanptr.p->scanReleaseCounter++;
+ scanReleaseLocksLab(signal);
+ } else {
+ jam();
+ /*
+ We come here when we have been scanning for a long time and not been able
+ to find m_max_batch_size_rows records to return. We needed to release
+ the record we didn't want, but now we are returning all found records to
+ the API.
+ */
+ scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
+ sendScanFragConf(signal, ZFALSE);
+ }//if
+}//Dblqh::scanLockReleasedLab()
+
+bool
+Dblqh::seize_acc_ptr_list(ScanRecord* scanP, Uint32 batch_size)
+{
+ Uint32 i;
+ Uint32 attr_buf_recs= (batch_size + 30) / 32;
+
+ if (batch_size > 1) {
+ if (c_no_attrinbuf_recs < attr_buf_recs) {
+ jam();
+ return false;
+ }
+ for (i= 1; i <= attr_buf_recs; i++) {
+ scanP->scan_acc_op_ptr[i]= seize_attrinbuf();
+ }
+ }
+ scanP->scan_acc_attr_recs= attr_buf_recs;
+ scanP->scan_acc_index = 0;
+ return true;
+}
+
+void
+Dblqh::release_acc_ptr_list(ScanRecord* scanP)
+{
+ Uint32 i, attr_buf_recs;
+ attr_buf_recs= scanP->scan_acc_attr_recs;
+
+ for (i= 1; i <= attr_buf_recs; i++) {
+ release_attrinbuf(scanP->scan_acc_op_ptr[i]);
+ }
+ scanP->scan_acc_attr_recs= 0;
+ scanP->scan_acc_index = 0;
+}
+
+Uint32
+Dblqh::seize_attrinbuf()
+{
+ AttrbufPtr regAttrPtr;
+ Uint32 ret_attr_buf;
+ ndbrequire(c_no_attrinbuf_recs > 0);
+ c_no_attrinbuf_recs--;
+ ret_attr_buf= cfirstfreeAttrinbuf;
+ regAttrPtr.i= ret_attr_buf;
+ ptrCheckGuard(regAttrPtr, cattrinbufFileSize, attrbuf);
+ cfirstfreeAttrinbuf= regAttrPtr.p->attrbuf[ZINBUF_NEXT];
+ return ret_attr_buf;
+}
+
+Uint32
+Dblqh::release_attrinbuf(Uint32 attr_buf_i)
+{
+ Uint32 next_buf;
+ AttrbufPtr regAttrPtr;
+ c_no_attrinbuf_recs++;
+ regAttrPtr.i= attr_buf_i;
+ ptrCheckGuard(regAttrPtr, cattrinbufFileSize, attrbuf);
+ next_buf= regAttrPtr.p->attrbuf[ZINBUF_NEXT];
+ regAttrPtr.p->attrbuf[ZINBUF_NEXT]= cfirstfreeAttrinbuf;
+ cfirstfreeAttrinbuf= regAttrPtr.i;
+ return next_buf;
+}
+
+void
+Dblqh::init_acc_ptr_list(ScanRecord* scanP)
+{
+ scanP->scan_acc_index = 0;
+}
+
+Uint32
+Dblqh::get_acc_ptr_from_scan_record(ScanRecord* scanP,
+ Uint32 index,
+ bool crash_flag)
+{
+ Uint32* acc_ptr;
+ Uint32 attr_buf_rec, attr_buf_index;
+ if (!((index < MAX_PARALLEL_OP_PER_SCAN) &&
+ index < scanP->scan_acc_index)) {
+ ndbrequire(crash_flag);
+ return RNIL;
+ }
+ i_get_acc_ptr(scanP, acc_ptr, index);
+ return *acc_ptr;
+}
+
+void
+Dblqh::set_acc_ptr_in_scan_record(ScanRecord* scanP,
+ Uint32 index, Uint32 acc)
+{
+ Uint32 *acc_ptr;
+ ndbrequire((index == 0 || scanP->scan_acc_index == index) &&
+ (index < MAX_PARALLEL_OP_PER_SCAN));
+ scanP->scan_acc_index= index + 1;
+ i_get_acc_ptr(scanP, acc_ptr, index);
+ *acc_ptr= acc;
+}
+
+/* -------------------------------------------------------------------------
+ * SCAN_FRAGREQ: Request to start scanning the specified fragment of a table.
+ * ------------------------------------------------------------------------- */
+void Dblqh::execSCAN_FRAGREQ(Signal* signal)
+{
+ ScanFragReq * const scanFragReq = (ScanFragReq *)&signal->theData[0];
+ ScanFragRef * ref;
+ const Uint32 transid1 = scanFragReq->transId1;
+ const Uint32 transid2 = scanFragReq->transId2;
+ Uint32 errorCode= 0;
+ Uint32 senderData;
+ Uint32 hashIndex;
+ TcConnectionrecPtr nextHashptr;
+
+ jamEntry();
+ const Uint32 reqinfo = scanFragReq->requestInfo;
+ const Uint32 fragId = (scanFragReq->fragmentNoKeyLen & 0xFFFF);
+ const Uint32 keyLen = (scanFragReq->fragmentNoKeyLen >> 16);
+ tabptr.i = scanFragReq->tableId;
+ const Uint32 max_rows = scanFragReq->batch_size_rows;
+ const Uint32 scanLockMode = ScanFragReq::getLockMode(reqinfo);
+ const Uint8 keyinfo = ScanFragReq::getKeyinfoFlag(reqinfo);
+ const Uint8 rangeScan = ScanFragReq::getRangeScanFlag(reqinfo);
+
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ if(tabptr.p->tableStatus != Tablerec::TABLE_DEFINED){
+ senderData = scanFragReq->senderData;
+ goto error_handler_early_1;
+ }
+
+ if (cfirstfreeTcConrec != RNIL) {
+ seizeTcrec();
+ tcConnectptr.p->clientConnectrec = scanFragReq->senderData;
+ tcConnectptr.p->clientBlockref = signal->senderBlockRef();
+ tcConnectptr.p->savePointId = scanFragReq->savePointId;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * NO FREE TC RECORD AVAILABLE, THUS WE CANNOT HANDLE THE REQUEST.
+ * -------------------------------------------------------------------- */
+ errorCode = ZNO_TC_CONNECT_ERROR;
+ senderData = scanFragReq->senderData;
+ goto error_handler_early;
+ }//if
+ /**
+ * A write allways have to get keyinfo
+ */
+ ndbrequire(scanLockMode == 0 || keyinfo);
+
+ ndbrequire(max_rows > 0 && max_rows <= MAX_PARALLEL_OP_PER_SCAN);
+ if (!getFragmentrec(signal, fragId)) {
+ errorCode = __LINE__;
+ goto error_handler;
+ }//if
+
+ // Verify scan type vs table type (both sides are boolean)
+ if (rangeScan != DictTabInfo::isOrderedIndex(fragptr.p->tableType)) {
+ errorCode = __LINE__; // XXX fix
+ goto error_handler;
+ }//if
+
+ // 1 scan record is reserved for node recovery
+ if (cscanNoFreeRec < 2) {
+ jam();
+ errorCode = ScanFragRef::ZNO_FREE_SCANREC_ERROR;
+ goto error_handler;
+ }
+
+ // XXX adjust cmaxAccOps for range scans and remove this comment
+ if ((cbookedAccOps + max_rows) > cmaxAccOps) {
+ jam();
+ errorCode = ScanFragRef::ZSCAN_BOOK_ACC_OP_ERROR;
+ goto error_handler;
+ }//if
+
+ ndbrequire(c_scanRecordPool.seize(scanptr));
+ initScanTc(signal,
+ transid1,
+ transid2,
+ fragId,
+ ZNIL);
+ tcConnectptr.p->save1 = 4;
+ tcConnectptr.p->primKeyLen = keyLen + 4; // hard coded in execKEYINFO
+ errorCode = initScanrec(scanFragReq);
+ if (errorCode != ZOK) {
+ jam();
+ goto error_handler2;
+ }//if
+ cscanNoFreeRec--;
+ cbookedAccOps += max_rows;
+
+ hashIndex = (tcConnectptr.p->transid[0] ^ tcConnectptr.p->tcOprec) & 1023;
+ nextHashptr.i = ctransidHash[hashIndex];
+ ctransidHash[hashIndex] = tcConnectptr.i;
+ tcConnectptr.p->prevHashRec = RNIL;
+ tcConnectptr.p->nextHashRec = nextHashptr.i;
+ if (nextHashptr.i != RNIL) {
+ jam();
+ /* ---------------------------------------------------------------------
+ * ENSURE THAT THE NEXT RECORD HAS SET PREVIOUS TO OUR RECORD
+ * IF IT EXISTS
+ * --------------------------------------------------------------------- */
+ ptrCheckGuard(nextHashptr, ctcConnectrecFileSize, tcConnectionrec);
+ nextHashptr.p->prevHashRec = tcConnectptr.i;
+ }//if
+ if (scanptr.p->scanAiLength > 0) {
+ jam();
+ tcConnectptr.p->transactionState = TcConnectionrec::WAIT_SCAN_AI;
+ return;
+ }//if
+ continueAfterReceivingAllAiLab(signal);
+ return;
+
+error_handler2:
+ // no scan number allocated
+ c_scanRecordPool.release(scanptr);
+error_handler:
+ ref = (ScanFragRef*)&signal->theData[0];
+ tcConnectptr.p->abortState = TcConnectionrec::ABORT_ACTIVE;
+ ref->senderData = tcConnectptr.p->clientConnectrec;
+ ref->transId1 = transid1;
+ ref->transId2 = transid2;
+ ref->errorCode = errorCode;
+ sendSignal(tcConnectptr.p->clientBlockref, GSN_SCAN_FRAGREF, signal,
+ ScanFragRef::SignalLength, JBB);
+ releaseOprec(signal);
+ releaseTcrec(signal, tcConnectptr);
+ return;
+
+ error_handler_early_1:
+ if(tabptr.p->tableStatus == Tablerec::NOT_DEFINED){
+ jam();
+ errorCode = ZTABLE_NOT_DEFINED;
+ } else if (tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_ONGOING ||
+ tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_DONE){
+ jam();
+ errorCode = ZDROP_TABLE_IN_PROGRESS;
+ } else {
+ ndbrequire(0);
+ }
+ error_handler_early:
+ ref = (ScanFragRef*)&signal->theData[0];
+ ref->senderData = senderData;
+ ref->transId1 = transid1;
+ ref->transId2 = transid2;
+ ref->errorCode = errorCode;
+ sendSignal(signal->senderBlockRef(), GSN_SCAN_FRAGREF, signal,
+ ScanFragRef::SignalLength, JBB);
+}//Dblqh::execSCAN_FRAGREQ()
+
+void Dblqh::continueAfterReceivingAllAiLab(Signal* signal)
+{
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+
+ if(scanptr.p->scanState == ScanRecord::IN_QUEUE){
+ jam();
+ return;
+ }
+
+ scanptr.p->scanState = ScanRecord::WAIT_ACC_SCAN;
+ AccScanReq * req = (AccScanReq*)&signal->theData[0];
+ req->senderData = scanptr.i;
+ req->senderRef = cownref;
+ req->tableId = tcConnectptr.p->tableref;
+ req->fragmentNo = tcConnectptr.p->fragmentid;
+ req->requestInfo = 0;
+ AccScanReq::setLockMode(req->requestInfo, scanptr.p->scanLockMode);
+ AccScanReq::setReadCommittedFlag(req->requestInfo, scanptr.p->readCommitted);
+ AccScanReq::setDescendingFlag(req->requestInfo, scanptr.p->descending);
+ req->transId1 = tcConnectptr.p->transid[0];
+ req->transId2 = tcConnectptr.p->transid[1];
+ req->savePointId = tcConnectptr.p->savePointId;
+ // always use if-stmt to switch (instead of setting a "scan block ref")
+ if (! scanptr.p->rangeScan)
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_ACC_SCANREQ, signal,
+ AccScanReq::SignalLength, JBB);
+ else
+ sendSignal(tcConnectptr.p->tcTuxBlockref, GSN_ACC_SCANREQ, signal,
+ AccScanReq::SignalLength, JBB);
+}//Dblqh::continueAfterReceivingAllAiLab()
+
+void Dblqh::scanAttrinfoLab(Signal* signal, Uint32* dataPtr, Uint32 length)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ if (saveTupattrbuf(signal, dataPtr, length) == ZOK) {
+ if (tcConnectptr.p->currTupAiLen < scanptr.p->scanAiLength) {
+ jam();
+ } else {
+ jam();
+ ndbrequire(tcConnectptr.p->currTupAiLen == scanptr.p->scanAiLength);
+ continueAfterReceivingAllAiLab(signal);
+ }//if
+ return;
+ }//if
+ abort_scan(signal, scanptr.i, ZGET_ATTRINBUF_ERROR);
+}
+
+void Dblqh::abort_scan(Signal* signal, Uint32 scan_ptr_i, Uint32 errcode){
+ jam();
+ scanptr.i = scan_ptr_i;
+ c_scanRecordPool.getPtr(scanptr);
+
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ finishScanrec(signal);
+ releaseScanrec(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::IDLE;
+ tcConnectptr.p->abortState = TcConnectionrec::ABORT_ACTIVE;
+
+ if(errcode)
+ {
+ jam();
+ ScanFragRef * ref = (ScanFragRef*)&signal->theData[0];
+ ref->senderData = tcConnectptr.p->clientConnectrec;
+ ref->transId1 = tcConnectptr.p->transid[0];
+ ref->transId2 = tcConnectptr.p->transid[1];
+ ref->errorCode = errcode;
+ sendSignal(tcConnectptr.p->clientBlockref, GSN_SCAN_FRAGREF, signal,
+ ScanFragRef::SignalLength, JBB);
+ }
+ deleteTransidHash(signal);
+ releaseOprec(signal);
+ releaseTcrec(signal, tcConnectptr);
+}
+
+/*---------------------------------------------------------------------*/
+/* Send this 'I am alive' signal to TC when it is received from ACC */
+/* We include the scanPtr.i that comes from ACC in signalData[1], this */
+/* tells TC which fragment record to check for a timeout. */
+/*---------------------------------------------------------------------*/
+void Dblqh::execSCAN_HBREP(Signal* signal)
+{
+ jamEntry();
+ scanptr.i = signal->theData[0];
+ c_scanRecordPool.getPtr(scanptr);
+ switch(scanptr.p->scanType){
+ case ScanRecord::SCAN:
+ if (scanptr.p->scanTcWaiting == ZTRUE) {
+ jam();
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ const Uint32 transid1 = signal->theData[1];
+ const Uint32 transid2 = signal->theData[2];
+ ndbrequire(transid1 == tcConnectptr.p->transid[0] &&
+ transid2 == tcConnectptr.p->transid[1]);
+
+ // Update counter on tcConnectPtr
+ if (tcConnectptr.p->tcTimer != 0){
+ tcConnectptr.p->tcTimer = cLqhTimeOutCount;
+ } else {
+ jam();
+ //ndbout << "SCAN_HBREP when tcTimer was off" << endl;
+ }
+
+ signal->theData[0] = tcConnectptr.p->clientConnectrec;
+ signal->theData[1] = tcConnectptr.p->transid[0];
+ signal->theData[2] = tcConnectptr.p->transid[1];
+ sendSignal(tcConnectptr.p->clientBlockref,
+ GSN_SCAN_HBREP, signal, 3, JBB);
+ }//if
+ break;
+ case ScanRecord::COPY:
+ // ndbout << "Dblqh::execSCAN_HBREP Dropping SCAN_HBREP" << endl;
+ break;
+ default:
+ ndbrequire(false);
+ }
+}
+
+void Dblqh::accScanConfScanLab(Signal* signal)
+{
+ AccScanConf * const accScanConf = (AccScanConf *)&signal->theData[0];
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ /* -----------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_ACC_SCAN
+ * ----------------------------------------------------------------------- */
+ if (accScanConf->flag == AccScanConf::ZEMPTY_FRAGMENT) {
+ jam();
+ /* ---------------------------------------------------------------------
+ * THE FRAGMENT WAS EMPTY.
+ * REPORT SUCCESSFUL COPYING.
+ * --------------------------------------------------------------------- */
+ tupScanCloseConfLab(signal);
+ return;
+ }//if
+ scanptr.p->scanAccPtr = accScanConf->accPtr;
+ if (scanptr.p->rangeScan) {
+ jam();
+ TuxBoundInfo* req = (TuxBoundInfo*)signal->getDataPtrSend();
+ req->errorCode = RNIL;
+ req->tuxScanPtrI = scanptr.p->scanAccPtr;
+ Uint32 len = req->boundAiLength = copy_bounds(req->data, tcConnectptr.p);
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_BOUND_INFO, signal,
+ TuxBoundInfo::SignalLength + len);
+
+ jamEntry();
+ if (req->errorCode != 0) {
+ jam();
+ /*
+ * Cannot use STORED_PROCREF to abort since even the REF
+ * returns a stored proc id. So record error and continue.
+ * The scan is already Invalid in TUX and returns empty set.
+ */
+ tcConnectptr.p->errorCode = req->errorCode;
+ }
+ }
+
+ scanptr.p->scanState = ScanRecord::WAIT_STORED_PROC_SCAN;
+ if(scanptr.p->scanStoredProcId == RNIL)
+ {
+ jam();
+ signal->theData[0] = tcConnectptr.p->tupConnectrec;
+ signal->theData[1] = tcConnectptr.p->tableref;
+ signal->theData[2] = scanptr.p->scanSchemaVersion;
+ signal->theData[3] = ZSTORED_PROC_SCAN;
+
+ signal->theData[4] = scanptr.p->scanAiLength;
+ sendSignal(tcConnectptr.p->tcTupBlockref,
+ GSN_STORED_PROCREQ, signal, 5, JBB);
+
+ signal->theData[0] = tcConnectptr.p->tupConnectrec;
+ AttrbufPtr regAttrinbufptr;
+ Uint32 firstAttr = regAttrinbufptr.i = tcConnectptr.p->firstAttrinbuf;
+ while (regAttrinbufptr.i != RNIL) {
+ ptrCheckGuard(regAttrinbufptr, cattrinbufFileSize, attrbuf);
+ jam();
+ Uint32 dataLen = regAttrinbufptr.p->attrbuf[ZINBUF_DATA_LEN];
+ ndbrequire(dataLen != 0);
+ // first 3 words already set in STORED_PROCREQ
+ MEMCOPY_NO_WORDS(&signal->theData[3],
+ &regAttrinbufptr.p->attrbuf[0],
+ dataLen);
+ sendSignal(tcConnectptr.p->tcTupBlockref,
+ GSN_ATTRINFO, signal, dataLen + 3, JBB);
+ regAttrinbufptr.i = regAttrinbufptr.p->attrbuf[ZINBUF_NEXT];
+ c_no_attrinbuf_recs++;
+ }//while
+
+ /**
+ * Release attr info
+ */
+ if(firstAttr != RNIL)
+ {
+ regAttrinbufptr.p->attrbuf[ZINBUF_NEXT] = cfirstfreeAttrinbuf;
+ cfirstfreeAttrinbuf = firstAttr;
+ tcConnectptr.p->firstAttrinbuf = tcConnectptr.p->lastAttrinbuf = RNIL;
+ }
+ }
+ else
+ {
+ jam();
+ storedProcConfScanLab(signal);
+ }
+}//Dblqh::accScanConfScanLab()
+
+#define print_buf(s,idx,len) {\
+ printf(s); Uint32 t2=len; DatabufPtr t3; t3.i = idx; \
+ while(t3.i != RNIL && t2-- > 0){\
+ ptrCheckGuard(t3, cdatabufFileSize, databuf);\
+ printf("%d ", t3.i); t3.i= t3.p->nextDatabuf;\
+ } printf("\n"); }
+
+Uint32
+Dblqh::copy_bounds(Uint32 * dst, TcConnectionrec* tcPtrP)
+{
+ /**
+ * copy_bounds handles multiple bounds by
+ * in the 16 upper bits of the first words (used to specify bound type)
+ * setting the length of this specific bound
+ *
+ */
+
+ DatabufPtr regDatabufptr;
+ Uint32 left = 4 - tcPtrP->m_offset_current_keybuf; // left in buf
+ Uint32 totalLen = tcPtrP->primKeyLen - 4;
+ regDatabufptr.i = tcPtrP->firstTupkeybuf;
+
+ ndbassert(tcPtrP->primKeyLen >= 4);
+ ndbassert(tcPtrP->m_offset_current_keybuf < 4);
+ ndbassert(!(totalLen == 0 && regDatabufptr.i != RNIL));
+ ndbassert(!(totalLen != 0 && regDatabufptr.i == RNIL));
+
+ if(totalLen)
+ {
+ ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf);
+ Uint32 sig0 = regDatabufptr.p->data[0];
+ Uint32 sig1 = regDatabufptr.p->data[1];
+ Uint32 sig2 = regDatabufptr.p->data[2];
+ Uint32 sig3 = regDatabufptr.p->data[3];
+
+ switch(left){
+ case 4:
+ * dst++ = sig0;
+ case 3:
+ * dst++ = sig1;
+ case 2:
+ * dst++ = sig2;
+ case 1:
+ * dst++ = sig3;
+ }
+
+ Uint32 first = (* (dst - left)); // First word in range
+
+ // Length of this range
+ Uint8 offset;
+ const Uint32 len = (first >> 16) ? (first >> 16) : totalLen;
+ tcPtrP->m_scan_curr_range_no = (first & 0xFFF0) >> 4;
+ (* (dst - left)) = (first & 0xF); // Remove length & range no
+
+ if(len < left)
+ {
+ offset = len;
+ }
+ else
+ {
+ Databuf * lastP;
+ left = (len - left);
+ regDatabufptr.i = regDatabufptr.p->nextDatabuf;
+
+ while(left >= 4)
+ {
+ left -= 4;
+ lastP = regDatabufptr.p;
+ ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf);
+ sig0 = regDatabufptr.p->data[0];
+ sig1 = regDatabufptr.p->data[1];
+ sig2 = regDatabufptr.p->data[2];
+ sig3 = regDatabufptr.p->data[3];
+ regDatabufptr.i = regDatabufptr.p->nextDatabuf;
+
+ * dst++ = sig0;
+ * dst++ = sig1;
+ * dst++ = sig2;
+ * dst++ = sig3;
+ }
+
+ if(left > 0)
+ {
+ lastP = regDatabufptr.p;
+ ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf);
+ sig0 = regDatabufptr.p->data[0];
+ sig1 = regDatabufptr.p->data[1];
+ sig2 = regDatabufptr.p->data[2];
+ sig3 = regDatabufptr.p->data[3];
+ * dst++ = sig0;
+ * dst++ = sig1;
+ * dst++ = sig2;
+ * dst++ = sig3;
+ }
+ else
+ {
+ lastP = regDatabufptr.p;
+ }
+ offset = left & 3;
+ lastP->nextDatabuf = cfirstfreeDatabuf;
+ cfirstfreeDatabuf = tcPtrP->firstTupkeybuf;
+ ndbassert(cfirstfreeDatabuf != RNIL);
+ }
+
+ if(len == totalLen && regDatabufptr.i != RNIL)
+ {
+ regDatabufptr.p->nextDatabuf = cfirstfreeDatabuf;
+ cfirstfreeDatabuf = regDatabufptr.i;
+ tcPtrP->lastTupkeybuf = regDatabufptr.i = RNIL;
+ ndbassert(cfirstfreeDatabuf != RNIL);
+ }
+
+ tcPtrP->m_offset_current_keybuf = offset;
+ tcPtrP->firstTupkeybuf = regDatabufptr.i;
+ tcPtrP->primKeyLen = 4 + totalLen - len;
+
+ return len;
+ }
+ return totalLen;
+}
+
+/* -------------------------------------------------------------------------
+ * ENTER STORED_PROCCONF WITH
+ * TC_CONNECTPTR,
+ * TSTORED_PROC_ID
+ * -------------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_STORED_PROC_SCAN
+ * ------------------------------------------------------------------------- */
+void Dblqh::storedProcConfScanLab(Signal* signal)
+{
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+ // STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
+ closeScanLab(signal);
+ return;
+ }//if
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_FIRST_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ continueFirstScanAfterBlockedLab(signal);
+}//Dblqh::storedProcConfScanLab()
+
+void Dblqh::continueFirstScanAfterBlockedLab(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanState = ScanRecord::WAIT_NEXT_SCAN;
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = RNIL;
+ signal->theData[2] = NextScanReq::ZSCAN_NEXT;
+ if (! scanptr.p->rangeScan)
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ else
+ sendSignal(tcConnectptr.p->tcTuxBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ return;
+}//Dblqh::continueFirstScanAfterBlockedLab()
+
+/* -------------------------------------------------------------------------
+ * When executing a scan we must come up to the surface at times to make
+ * sure we can quickly start local checkpoints.
+ * ------------------------------------------------------------------------- */
+void Dblqh::execCHECK_LCP_STOP(Signal* signal)
+{
+ jamEntry();
+ scanptr.i = signal->theData[0];
+ c_scanRecordPool.getPtr(scanptr);
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (signal->theData[1] == ZTRUE) {
+ jam();
+ releaseActiveFrag(signal);
+ signal->theData[0] = ZCHECK_LCP_STOP_BLOCKED;
+ signal->theData[1] = scanptr.i;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 2);
+ signal->theData[0] = RNIL;
+ return;
+ }//if
+ if (fragptr.p->fragStatus != Fragrecord::FSACTIVE) {
+ ndbrequire(fragptr.p->fragStatus == Fragrecord::BLOCKED);
+ releaseActiveFrag(signal);
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_CHECK_STOPPED;
+ signal->theData[0] = RNIL;
+ }//if
+}//Dblqh::execCHECK_LCP_STOP()
+
+void Dblqh::checkLcpStopBlockedLab(Signal* signal)
+{
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ continueAfterCheckLcpStopBlocked(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_CHECK_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dblqh::checkLcpStopBlockedLab()
+
+void Dblqh::continueAfterCheckLcpStopBlocked(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = AccCheckScan::ZNOT_CHECK_LCP_STOP;
+ if (! scanptr.p->rangeScan)
+ EXECUTE_DIRECT(DBACC, GSN_ACC_CHECK_SCAN, signal, 2);
+ else
+ EXECUTE_DIRECT(DBTUX, GSN_ACC_CHECK_SCAN, signal, 2);
+}//Dblqh::continueAfterCheckLcpStopBlocked()
+
+/* -------------------------------------------------------------------------
+ * ENTER NEXT_SCANCONF
+ * -------------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_NEXT_SCAN
+ * ------------------------------------------------------------------------- */
+void Dblqh::nextScanConfScanLab(Signal* signal)
+{
+ NextScanConf * const nextScanConf = (NextScanConf *)&signal->theData[0];
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ if (nextScanConf->fragId == RNIL) {
+ jam();
+ /* ---------------------------------------------------------------------
+ * THERE ARE NO MORE TUPLES TO FETCH. IF WE HAVE ANY
+ * OPERATIONS STILL NEEDING A LOCK WE REPORT TO THE
+ * APPLICATION AND CLOSE THE SCAN WHEN THE NEXT SCAN
+ * REQUEST IS RECEIVED. IF WE DO NOT HAVE ANY NEED FOR
+ * LOCKS WE CAN CLOSE THE SCAN IMMEDIATELY.
+ * --------------------------------------------------------------------- */
+ releaseActiveFrag(signal);
+ /*************************************************************
+ * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
+ ************************************************************ */
+ if (!scanptr.p->scanLockHold)
+ {
+ jam();
+ closeScanLab(signal);
+ return;
+ }
+
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ if ((scanptr.p->scanLockHold == ZTRUE) &&
+ (scanptr.p->m_curr_batch_size_rows > 0)) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ jam();
+ closeScanLab(signal);
+ return;
+ }//if
+
+ if (scanptr.p->m_curr_batch_size_rows > 0) {
+ jam();
+
+ if((tcConnectptr.p->primKeyLen - 4) == 0)
+ scanptr.p->scanCompletedStatus = ZTRUE;
+
+ scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
+ sendScanFragConf(signal, ZFALSE);
+ return;
+ }//if
+ closeScanLab(signal);
+ return;
+ }//if
+
+ // If accOperationPtr == RNIL no record was returned by ACC
+ if (nextScanConf->accOperationPtr == RNIL) {
+ jam();
+ /*************************************************************
+ * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
+ ************************************************************ */
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ releaseActiveFrag(signal);
+ if ((scanptr.p->scanLockHold == ZTRUE) &&
+ (scanptr.p->m_curr_batch_size_rows > 0)) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ jam();
+ closeScanLab(signal);
+ return;
+ }//if
+
+ if (scanptr.p->m_curr_batch_size_rows > 0) {
+ jam();
+ releaseActiveFrag(signal);
+ scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
+ sendScanFragConf(signal, ZFALSE);
+ return;
+ }//if
+
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ if (! scanptr.p->rangeScan)
+ sendSignal(tcConnectptr.p->tcAccBlockref,
+ GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ else
+ sendSignal(tcConnectptr.p->tcTuxBlockref,
+ GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ }//if
+ jam();
+ set_acc_ptr_in_scan_record(scanptr.p,
+ scanptr.p->m_curr_batch_size_rows,
+ nextScanConf->accOperationPtr);
+ jam();
+ scanptr.p->scanLocalref[0] = nextScanConf->localKey[0];
+ scanptr.p->scanLocalref[1] = nextScanConf->localKey[1];
+ scanptr.p->scanLocalFragid = nextScanConf->fragId;
+ nextScanConfLoopLab(signal);
+}//Dblqh::nextScanConfScanLab()
+
+void Dblqh::nextScanConfLoopLab(Signal* signal)
+{
+ /* ----------------------------------------------------------------------
+ * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
+ * ---------------------------------------------------------------------- */
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+ releaseActiveFrag(signal);
+ if ((scanptr.p->scanLockHold == ZTRUE) &&
+ (scanptr.p->m_curr_batch_size_rows > 0)) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ closeScanLab(signal);
+ return;
+ }//if
+ jam();
+ Uint32 tableRef;
+ Uint32 tupFragPtr;
+ Uint32 reqinfo = (scanptr.p->scanLockHold == ZFALSE);
+ reqinfo = reqinfo + (tcConnectptr.p->operation << 6);
+ reqinfo = reqinfo + (tcConnectptr.p->opExec << 10);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_TUPKEY;
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (! scanptr.p->rangeScan) {
+ tableRef = tcConnectptr.p->tableref;
+ tupFragPtr = fragptr.p->tupFragptr[scanptr.p->scanLocalFragid & 1];
+ } else {
+ jam();
+ // for ordered index use primary table
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = fragptr.p->tableFragptr;
+ ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
+ tableRef = tFragPtr.p->tabRef;
+ tupFragPtr = tFragPtr.p->tupFragptr[scanptr.p->scanLocalFragid & 1];
+ }
+ {
+ jam();
+ TupKeyReq * const tupKeyReq = (TupKeyReq *)signal->getDataPtrSend();
+
+ tupKeyReq->connectPtr = tcConnectptr.p->tupConnectrec;
+ tupKeyReq->request = reqinfo;
+ tupKeyReq->tableRef = tableRef;
+ tupKeyReq->fragId = scanptr.p->scanLocalFragid;
+ tupKeyReq->keyRef1 = scanptr.p->scanLocalref[0];
+ tupKeyReq->keyRef2 = scanptr.p->scanLocalref[1];
+ tupKeyReq->attrBufLen = 0;
+ tupKeyReq->opRef = scanptr.p->scanApiOpPtr;
+ tupKeyReq->applRef = scanptr.p->scanApiBlockref;
+ tupKeyReq->schemaVersion = scanptr.p->scanSchemaVersion;
+ tupKeyReq->storedProcedure = scanptr.p->scanStoredProcId;
+ tupKeyReq->transId1 = tcConnectptr.p->transid[0];
+ tupKeyReq->transId2 = tcConnectptr.p->transid[1];
+ tupKeyReq->fragPtr = tupFragPtr;
+ tupKeyReq->primaryReplica = (tcConnectptr.p->seqNoReplica == 0)?true:false;
+ tupKeyReq->coordinatorTC = tcConnectptr.p->tcBlockref;
+ tupKeyReq->tcOpIndex = tcConnectptr.p->tcOprec;
+ tupKeyReq->savePointId = tcConnectptr.p->savePointId;
+ Uint32 blockNo = refToBlock(tcConnectptr.p->tcTupBlockref);
+ EXECUTE_DIRECT(blockNo, GSN_TUPKEYREQ, signal,
+ TupKeyReq::SignalLength);
+ }
+}
+
+/* -------------------------------------------------------------------------
+ * RECEPTION OF FURTHER KEY INFORMATION WHEN KEY SIZE > 16 BYTES.
+ * -------------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_SCAN_KEYINFO
+ * ------------------------------------------------------------------------- */
+void
+Dblqh::keyinfoLab(const Uint32 * src, const Uint32 * end)
+{
+ do {
+ jam();
+ seizeTupkeybuf(0);
+ databufptr.p->data[0] = * src ++;
+ databufptr.p->data[1] = * src ++;
+ databufptr.p->data[2] = * src ++;
+ databufptr.p->data[3] = * src ++;
+ } while (src < end);
+}//Dblqh::keyinfoLab()
+
+Uint32
+Dblqh::readPrimaryKeys(ScanRecord *scanP, TcConnectionrec *tcConP, Uint32 *dst)
+{
+ Uint32 tableId = tcConP->tableref;
+ Uint32 fragId = scanP->scanLocalFragid;
+ Uint32 fragPageId = scanP->scanLocalref[0];
+ Uint32 pageIndex = scanP->scanLocalref[1];
+
+ if(scanP->rangeScan)
+ {
+ jam();
+ // for ordered index use primary table
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = fragptr.p->tableFragptr;
+ ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
+ tableId = tFragPtr.p->tabRef;
+ }
+
+ int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, dst, false);
+ if(0)
+ ndbout_c("readPrimaryKeys(table: %d fragment: %d [ %d %d ] -> %d",
+ tableId, fragId, fragPageId, pageIndex, ret);
+ ndbassert(ret > 0);
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------------
+ * ENTER TUPKEYCONF
+ * -------------------------------------------------------------------------
+ * PRECONDITION: TRANSACTION_STATE = SCAN_TUPKEY
+ * ------------------------------------------------------------------------- */
+void Dblqh::scanTupkeyConfLab(Signal* signal)
+{
+ const TupKeyConf * conf = (TupKeyConf *)signal->getDataPtr();
+ UintR tdata4 = conf->readLength;
+ UintR tdata5 = conf->lastRow;
+
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ releaseActiveFrag(signal);
+ c_scanRecordPool.getPtr(scanptr);
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ /* ---------------------------------------------------------------------
+ * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
+ * --------------------------------------------------------------------- */
+ if ((scanptr.p->scanLockHold == ZTRUE) &&
+ (scanptr.p->m_curr_batch_size_rows > 0)) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ jam();
+ closeScanLab(signal);
+ return;
+ }//if
+ if (scanptr.p->scanKeyinfoFlag) {
+ jam();
+ // Inform API about keyinfo len aswell
+ tdata4 += sendKeyinfo20(signal, scanptr.p, tcConnectptr.p);
+ }//if
+ ndbrequire(scanptr.p->m_curr_batch_size_rows < MAX_PARALLEL_OP_PER_SCAN);
+ scanptr.p->m_curr_batch_size_bytes+= tdata4;
+ scanptr.p->m_curr_batch_size_rows++;
+ scanptr.p->m_last_row = tdata5;
+ if (scanptr.p->check_scan_batch_completed() | tdata5){
+ if (scanptr.p->scanLockHold == ZTRUE) {
+ jam();
+ scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
+ sendScanFragConf(signal, ZFALSE);
+ return;
+ } else {
+ jam();
+ scanptr.p->scanReleaseCounter = scanptr.p->m_curr_batch_size_rows;
+ scanReleaseLocksLab(signal);
+ return;
+ }
+ } else {
+ if (scanptr.p->scanLockHold == ZTRUE) {
+ jam();
+ scanptr.p->scanFlag = NextScanReq::ZSCAN_NEXT;
+ } else {
+ jam();
+ scanptr.p->scanFlag = NextScanReq::ZSCAN_NEXT_COMMIT;
+ }
+ }
+ scanNextLoopLab(signal);
+}//Dblqh::scanTupkeyConfLab()
+
+void Dblqh::scanNextLoopLab(Signal* signal)
+{
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ }//switch
+ continueScanAfterBlockedLab(signal);
+}//Dblqh::scanNextLoopLab()
+
+void Dblqh::continueScanAfterBlockedLab(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ Uint32 accOpPtr;
+ if (scanptr.p->scanFlag == NextScanReq::ZSCAN_NEXT_ABORT) {
+ jam();
+ scanptr.p->scanFlag = NextScanReq::ZSCAN_NEXT_COMMIT;
+ accOpPtr= get_acc_ptr_from_scan_record(scanptr.p,
+ scanptr.p->m_curr_batch_size_rows,
+ false);
+ scanptr.p->scan_acc_index--;
+ } else if (scanptr.p->scanFlag == NextScanReq::ZSCAN_NEXT_COMMIT) {
+ jam();
+ accOpPtr= get_acc_ptr_from_scan_record(scanptr.p,
+ scanptr.p->m_curr_batch_size_rows-1,
+ false);
+ } else {
+ jam();
+ accOpPtr = RNIL; // The value is not used in ACC
+ }//if
+ scanptr.p->scanState = ScanRecord::WAIT_NEXT_SCAN;
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = accOpPtr;
+ signal->theData[2] = scanptr.p->scanFlag;
+ if (! scanptr.p->rangeScan)
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3,JBB);
+ else
+ sendSignal(tcConnectptr.p->tcTuxBlockref, GSN_NEXT_SCANREQ, signal, 3,JBB);
+}//Dblqh::continueScanAfterBlockedLab()
+
+/* -------------------------------------------------------------------------
+ * ENTER TUPKEYREF WITH
+ * TC_CONNECTPTR,
+ * TERROR_CODE
+ * -------------------------------------------------------------------------
+ * PRECONDITION: TRANSACTION_STATE = SCAN_TUPKEY
+ * ------------------------------------------------------------------------- */
+void Dblqh::scanTupkeyRefLab(Signal* signal)
+{
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ releaseActiveFrag(signal);
+ c_scanRecordPool.getPtr(scanptr);
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ /* ---------------------------------------------------------------------
+ * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
+ * --------------------------------------------------------------------- */
+ if ((scanptr.p->scanLockHold == ZTRUE) &&
+ (scanptr.p->m_curr_batch_size_rows > 0)) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ jam();
+ closeScanLab(signal);
+ return;
+ }//if
+ if ((terrorCode != ZSEARCH_CONDITION_FALSE) &&
+ (terrorCode != ZNO_TUPLE_FOUND) &&
+ (terrorCode >= ZUSER_ERROR_CODE_LIMIT)) {
+ scanptr.p->scanErrorCounter++;
+ tcConnectptr.p->errorCode = terrorCode;
+
+ if (scanptr.p->scanLockHold == ZTRUE) {
+ jam();
+ scanptr.p->scanReleaseCounter = 1;
+ } else {
+ jam();
+ scanptr.p->m_curr_batch_size_rows++;
+ scanptr.p->scanReleaseCounter = scanptr.p->m_curr_batch_size_rows;
+ }//if
+ /* --------------------------------------------------------------------
+ * WE NEED TO RELEASE ALL LOCKS CURRENTLY
+ * HELD BY THIS SCAN.
+ * -------------------------------------------------------------------- */
+ scanReleaseLocksLab(signal);
+ return;
+ }//if
+ Uint32 time_passed= tcConnectptr.p->tcTimer - cLqhTimeOutCount;
+ if (scanptr.p->m_curr_batch_size_rows > 0) {
+ if (time_passed > 1) {
+ /* -----------------------------------------------------------------------
+ * WE NEED TO ENSURE THAT WE DO NOT SEARCH FOR THE NEXT TUPLE FOR A
+ * LONG TIME WHILE WE KEEP A LOCK ON A FOUND TUPLE. WE RATHER REPORT
+ * THE FOUND TUPLE IF FOUND TUPLES ARE RARE. If more than 10 ms passed we
+ * send the found tuples to the API.
+ * ----------------------------------------------------------------------- */
+ scanptr.p->scanReleaseCounter = scanptr.p->m_curr_batch_size_rows + 1;
+ scanReleaseLocksLab(signal);
+ return;
+ }
+ } else {
+ if (time_passed > 10) {
+ jam();
+ signal->theData[0]= scanptr.i;
+ signal->theData[1]= tcConnectptr.p->transid[0];
+ signal->theData[2]= tcConnectptr.p->transid[1];
+ execSCAN_HBREP(signal);
+ }
+ }
+ scanptr.p->scanFlag = NextScanReq::ZSCAN_NEXT_ABORT;
+ scanNextLoopLab(signal);
+}//Dblqh::scanTupkeyRefLab()
+
+/* -------------------------------------------------------------------------
+ * THE SCAN HAS BEEN COMPLETED. EITHER BY REACHING THE END OR BY COMMAND
+ * FROM THE APPLICATION OR BY SOME SORT OF ERROR CONDITION.
+ * ------------------------------------------------------------------------- */
+void Dblqh::closeScanLab(Signal* signal)
+{
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_CLOSE_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ ndbrequire(false);
+ }//switch
+ continueCloseScanAfterBlockedLab(signal);
+}//Dblqh::closeScanLab()
+
+void Dblqh::continueCloseScanAfterBlockedLab(Signal* signal)
+{
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanState = ScanRecord::WAIT_CLOSE_SCAN;
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = RNIL;
+ signal->theData[2] = NextScanReq::ZSCAN_CLOSE;
+ if (! scanptr.p->rangeScan)
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ else
+ sendSignal(tcConnectptr.p->tcTuxBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+}//Dblqh::continueCloseScanAfterBlockedLab()
+
+/* -------------------------------------------------------------------------
+ * ENTER NEXT_SCANCONF
+ * -------------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_CLOSE_SCAN
+ * ------------------------------------------------------------------------- */
+void Dblqh::accScanCloseConfLab(Signal* signal)
+{
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+
+ if((tcConnectptr.p->primKeyLen - 4) > 0 &&
+ scanptr.p->scanCompletedStatus != ZTRUE)
+ {
+ jam();
+ releaseActiveFrag(signal);
+ continueAfterReceivingAllAiLab(signal);
+ return;
+ }
+
+ scanptr.p->scanState = ScanRecord::WAIT_DELETE_STORED_PROC_ID_SCAN;
+ signal->theData[0] = tcConnectptr.p->tupConnectrec;
+ signal->theData[1] = tcConnectptr.p->tableref;
+ signal->theData[2] = scanptr.p->scanSchemaVersion;
+ signal->theData[3] = ZDELETE_STORED_PROC_ID;
+ signal->theData[4] = scanptr.p->scanStoredProcId;
+ sendSignal(tcConnectptr.p->tcTupBlockref,
+ GSN_STORED_PROCREQ, signal, 5, JBB);
+}//Dblqh::accScanCloseConfLab()
+
+/* -------------------------------------------------------------------------
+ * ENTER STORED_PROCCONF WITH
+ * -------------------------------------------------------------------------
+ * PRECONDITION: SCAN_STATE = WAIT_DELETE_STORED_PROC_ID_SCAN
+ * ------------------------------------------------------------------------- */
+void Dblqh::tupScanCloseConfLab(Signal* signal)
+{
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (tcConnectptr.p->abortState == TcConnectionrec::NEW_FROM_TC) {
+ jam();
+ tcNodeFailptr.i = tcConnectptr.p->tcNodeFailrec;
+ ptrCheckGuard(tcNodeFailptr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+ tcNodeFailptr.p->tcRecNow = tcConnectptr.i + 1;
+ signal->theData[0] = ZLQH_TRANS_NEXT;
+ signal->theData[1] = tcNodeFailptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ } else if (tcConnectptr.p->errorCode != 0) {
+ jam();
+ ScanFragRef * ref = (ScanFragRef*)&signal->theData[0];
+ ref->senderData = tcConnectptr.p->clientConnectrec;
+ ref->transId1 = tcConnectptr.p->transid[0];
+ ref->transId2 = tcConnectptr.p->transid[1];
+ ref->errorCode = tcConnectptr.p->errorCode;
+ sendSignal(tcConnectptr.p->clientBlockref, GSN_SCAN_FRAGREF, signal,
+ ScanFragRef::SignalLength, JBB);
+ } else {
+ jam();
+ sendScanFragConf(signal, ZSCAN_FRAG_CLOSED);
+ }//if
+ finishScanrec(signal);
+ releaseScanrec(signal);
+ tcConnectptr.p->tcScanRec = RNIL;
+ deleteTransidHash(signal);
+ releaseOprec(signal);
+ releaseTcrec(signal, tcConnectptr);
+}//Dblqh::tupScanCloseConfLab()
+
+/* =========================================================================
+ * ======= INITIATE SCAN RECORD =======
+ *
+ * SUBROUTINE SHORT NAME = ISC
+ * ========================================================================= */
+Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq)
+{
+ const Uint32 reqinfo = scanFragReq->requestInfo;
+ const Uint32 max_rows = scanFragReq->batch_size_rows;
+ const Uint32 max_bytes = scanFragReq->batch_size_bytes;
+ const Uint32 scanLockMode = ScanFragReq::getLockMode(reqinfo);
+ const Uint32 scanLockHold = ScanFragReq::getHoldLockFlag(reqinfo);
+ const Uint32 keyinfo = ScanFragReq::getKeyinfoFlag(reqinfo);
+ const Uint32 readCommitted = ScanFragReq::getReadCommittedFlag(reqinfo);
+ const Uint32 idx = ScanFragReq::getRangeScanFlag(reqinfo);
+ const Uint32 descending = ScanFragReq::getDescendingFlag(reqinfo);
+ const Uint32 attrLen = ScanFragReq::getAttrLen(reqinfo);
+ const Uint32 scanPrio = ScanFragReq::getScanPrio(reqinfo);
+
+ scanptr.p->scanKeyinfoFlag = keyinfo;
+ scanptr.p->scanLockHold = scanLockHold;
+ scanptr.p->scanCompletedStatus = ZFALSE;
+ scanptr.p->scanType = ScanRecord::SCAN;
+ scanptr.p->scanApiBlockref = scanFragReq->resultRef;
+ scanptr.p->scanAiLength = attrLen;
+ scanptr.p->scanTcrec = tcConnectptr.i;
+ scanptr.p->scanSchemaVersion = scanFragReq->schemaVersion;
+
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes= 0;
+ scanptr.p->m_max_batch_size_rows = max_rows;
+ scanptr.p->m_max_batch_size_bytes = max_bytes;
+
+ scanptr.p->scanErrorCounter = 0;
+ scanptr.p->scanLockMode = scanLockMode;
+ scanptr.p->readCommitted = readCommitted;
+ scanptr.p->rangeScan = idx;
+ scanptr.p->descending = descending;
+ scanptr.p->scanState = ScanRecord::SCAN_FREE;
+ scanptr.p->scanFlag = ZFALSE;
+ scanptr.p->scanLocalref[0] = 0;
+ scanptr.p->scanLocalref[1] = 0;
+ scanptr.p->scanLocalFragid = 0;
+ scanptr.p->scanTcWaiting = ZTRUE;
+ scanptr.p->scanNumber = ~0;
+ scanptr.p->scanApiOpPtr = scanFragReq->clientOpPtr;
+ scanptr.p->m_last_row = 0;
+ scanptr.p->scanStoredProcId = RNIL;
+
+ if (max_rows == 0 || (max_bytes > 0 && max_rows > max_bytes)){
+ jam();
+ return ScanFragRef::ZWRONG_BATCH_SIZE;
+ }
+ if (!seize_acc_ptr_list(scanptr.p, max_rows)){
+ jam();
+ return ScanFragRef::ZTOO_MANY_ACTIVE_SCAN_ERROR;
+ }
+ /**
+ * Used for scan take over
+ */
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = fragptr.p->tableFragptr;
+ ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
+ scanptr.p->fragPtrI = fragptr.p->tableFragptr;
+
+ /**
+ * !idx uses 1 - (MAX_PARALLEL_SCANS_PER_FRAG - 1) = 1-11
+ * idx uses from MAX_PARALLEL_SCANS_PER_FRAG - MAX = 12-42)
+ */
+ Uint32 start = (idx ? MAX_PARALLEL_SCANS_PER_FRAG : 1 );
+ Uint32 stop = (idx ? MAX_PARALLEL_INDEX_SCANS_PER_FRAG : MAX_PARALLEL_SCANS_PER_FRAG - 1);
+ stop += start;
+ Uint32 free = tFragPtr.p->m_scanNumberMask.find(start);
+
+ if(free == Fragrecord::ScanNumberMask::NotFound || free >= stop){
+ jam();
+
+ if(scanPrio == 0){
+ jam();
+ return ScanFragRef::ZTOO_MANY_ACTIVE_SCAN_ERROR;
+ }
+
+ /**
+ * Put on queue
+ */
+ scanptr.p->scanState = ScanRecord::IN_QUEUE;
+ LocalDLFifoList<ScanRecord> queue(c_scanRecordPool,
+ fragptr.p->m_queuedScans);
+ queue.add(scanptr);
+ return ZOK;
+ }
+
+ scanptr.p->scanNumber = free;
+ tFragPtr.p->m_scanNumberMask.clear(free);// Update mask
+
+ LocalDLList<ScanRecord> active(c_scanRecordPool, fragptr.p->m_activeScans);
+ active.add(scanptr);
+ if(scanptr.p->scanKeyinfoFlag){
+ jam();
+#ifdef VM_TRACE
+ ScanRecordPtr tmp;
+ ndbrequire(!c_scanTakeOverHash.find(tmp, * scanptr.p));
+#endif
+#ifdef TRACE_SCAN_TAKEOVER
+ ndbout_c("adding (%d %d) table: %d fragId: %d frag.i: %d tableFragptr: %d",
+ scanptr.p->scanNumber, scanptr.p->fragPtrI,
+ tabptr.i, scanFragReq->fragmentNoKeyLen & 0xFFFF,
+ fragptr.i, fragptr.p->tableFragptr);
+#endif
+ c_scanTakeOverHash.add(scanptr);
+ }
+ init_acc_ptr_list(scanptr.p);
+ return ZOK;
+}
+
+/* =========================================================================
+ * ======= INITIATE TC RECORD AT SCAN =======
+ *
+ * SUBROUTINE SHORT NAME = IST
+ * ========================================================================= */
+void Dblqh::initScanTc(Signal* signal,
+ Uint32 transid1,
+ Uint32 transid2,
+ Uint32 fragId,
+ Uint32 nodeId)
+{
+ tcConnectptr.p->transid[0] = transid1;
+ tcConnectptr.p->transid[1] = transid2;
+ tcConnectptr.p->tcScanRec = scanptr.i;
+ tcConnectptr.p->tableref = tabptr.i;
+ tcConnectptr.p->fragmentid = fragId;
+ tcConnectptr.p->fragmentptr = fragptr.i;
+ tcConnectptr.p->tcOprec = tcConnectptr.p->clientConnectrec;
+ tcConnectptr.p->tcBlockref = tcConnectptr.p->clientBlockref;
+ tcConnectptr.p->errorCode = 0;
+ tcConnectptr.p->reclenAiLqhkey = 0;
+ tcConnectptr.p->abortState = TcConnectionrec::ABORT_IDLE;
+ tcConnectptr.p->nextReplica = nodeId;
+ tcConnectptr.p->currTupAiLen = 0;
+ tcConnectptr.p->opExec = 1;
+ tcConnectptr.p->operation = ZREAD;
+ tcConnectptr.p->listState = TcConnectionrec::NOT_IN_LIST;
+ tcConnectptr.p->commitAckMarker = RNIL;
+ tcConnectptr.p->m_offset_current_keybuf = 0;
+ tcConnectptr.p->m_scan_curr_range_no = 0;
+
+ tabptr.p->usageCount++;
+}//Dblqh::initScanTc()
+
+/* =========================================================================
+ * ======= FINISH SCAN RECORD =======
+ *
+ * REMOVE SCAN RECORD FROM PER FRAGMENT LIST.
+ * ========================================================================= */
+void Dblqh::finishScanrec(Signal* signal)
+{
+ release_acc_ptr_list(scanptr.p);
+
+ LocalDLFifoList<ScanRecord> queue(c_scanRecordPool,
+ fragptr.p->m_queuedScans);
+
+ if(scanptr.p->scanState == ScanRecord::IN_QUEUE){
+ jam();
+ queue.release(scanptr);
+ return;
+ }
+
+ if(scanptr.p->scanKeyinfoFlag){
+ jam();
+ ScanRecordPtr tmp;
+#ifdef TRACE_SCAN_TAKEOVER
+ ndbout_c("removing (%d %d)", scanptr.p->scanNumber, scanptr.p->fragPtrI);
+#endif
+ c_scanTakeOverHash.remove(tmp, * scanptr.p);
+ ndbrequire(tmp.p == scanptr.p);
+ }
+
+ LocalDLList<ScanRecord> scans(c_scanRecordPool, fragptr.p->m_activeScans);
+ scans.release(scanptr);
+
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = scanptr.p->fragPtrI;
+ ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
+
+ const Uint32 scanNumber = scanptr.p->scanNumber;
+ ndbrequire(!tFragPtr.p->m_scanNumberMask.get(scanNumber));
+ ScanRecordPtr restart;
+
+ /**
+ * Start on of queued scans
+ */
+ if(scanNumber == NR_ScanNo || !queue.first(restart)){
+ jam();
+ tFragPtr.p->m_scanNumberMask.set(scanNumber);
+ return;
+ }
+
+ if(ERROR_INSERTED(5034)){
+ jam();
+ tFragPtr.p->m_scanNumberMask.set(scanNumber);
+ return;
+ }
+
+ ndbrequire(restart.p->scanState == ScanRecord::IN_QUEUE);
+
+ ScanRecordPtr tmpScan = scanptr;
+ TcConnectionrecPtr tmpTc = tcConnectptr;
+
+ tcConnectptr.i = restart.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ restart.p->scanNumber = scanNumber;
+
+ queue.remove(restart);
+ scans.add(restart);
+ if(restart.p->scanKeyinfoFlag){
+ jam();
+#ifdef VM_TRACE
+ ScanRecordPtr tmp;
+ ndbrequire(!c_scanTakeOverHash.find(tmp, * restart.p));
+#endif
+ c_scanTakeOverHash.add(restart);
+#ifdef TRACE_SCAN_TAKEOVER
+ ndbout_c("adding-r (%d %d)", restart.p->scanNumber, restart.p->fragPtrI);
+#endif
+ }
+
+ restart.p->scanState = ScanRecord::SCAN_FREE; // set in initScanRec
+ if(tcConnectptr.p->transactionState == TcConnectionrec::SCAN_STATE_USED)
+ {
+ jam();
+ scanptr = restart;
+ continueAfterReceivingAllAiLab(signal);
+ }
+ else
+ {
+ ndbrequire(tcConnectptr.p->transactionState == TcConnectionrec::WAIT_SCAN_AI);
+ }
+ scanptr = tmpScan;
+ tcConnectptr = tmpTc;
+}//Dblqh::finishScanrec()
+
+/* =========================================================================
+ * ======= RELEASE SCAN RECORD =======
+ *
+ * RELEASE A SCAN RECORD TO THE FREELIST.
+ * ========================================================================= */
+void Dblqh::releaseScanrec(Signal* signal)
+{
+ scanptr.p->scanState = ScanRecord::SCAN_FREE;
+ scanptr.p->scanType = ScanRecord::ST_IDLE;
+ scanptr.p->scanTcWaiting = ZFALSE;
+ cbookedAccOps -= scanptr.p->m_max_batch_size_rows;
+ cscanNoFreeRec++;
+}//Dblqh::releaseScanrec()
+
+/* ------------------------------------------------------------------------
+ * ------- SEND KEYINFO20 TO API -------
+ *
+ * ------------------------------------------------------------------------ */
+Uint32 Dblqh::sendKeyinfo20(Signal* signal,
+ ScanRecord * scanP,
+ TcConnectionrec * tcConP)
+{
+ ndbrequire(scanP->m_curr_batch_size_rows < MAX_PARALLEL_OP_PER_SCAN);
+ KeyInfo20 * keyInfo = (KeyInfo20 *)&signal->theData[0];
+
+ /**
+ * Note that this code requires signal->theData to be big enough for
+ * a entire key
+ */
+ const BlockReference ref = scanP->scanApiBlockref;
+ const Uint32 scanOp = scanP->m_curr_batch_size_rows;
+ const Uint32 nodeId = refToNode(ref);
+ const bool connectedToNode = getNodeInfo(nodeId).m_connected;
+ const Uint32 type = getNodeInfo(nodeId).m_type;
+ const bool is_api = (type >= NodeInfo::API && type <= NodeInfo::REP);
+ const bool old_dest = (getNodeInfo(nodeId).m_version < MAKE_VERSION(3,5,0));
+ const bool longable = true; // TODO is_api && !old_dest;
+
+ Uint32 * dst = keyInfo->keyData;
+ dst += nodeId == getOwnNodeId() ? 0 : KeyInfo20::DataLength;
+
+ Uint32 keyLen = readPrimaryKeys(scanP, tcConP, dst);
+ Uint32 fragId = tcConP->fragmentid;
+ keyInfo->clientOpPtr = scanP->scanApiOpPtr;
+ keyInfo->keyLen = keyLen;
+ keyInfo->scanInfo_Node =
+ KeyInfo20::setScanInfo(scanOp, scanP->scanNumber) + (fragId << 20);
+ keyInfo->transId1 = tcConP->transid[0];
+ keyInfo->transId2 = tcConP->transid[1];
+
+ Uint32 * src = signal->theData+25;
+ if(connectedToNode){
+ jam();
+
+ if(nodeId != getOwnNodeId()){
+ jam();
+
+ if(keyLen <= KeyInfo20::DataLength || !longable) {
+ while(keyLen > KeyInfo20::DataLength){
+ jam();
+ MEMCOPY_NO_WORDS(keyInfo->keyData, src, KeyInfo20::DataLength);
+ sendSignal(ref, GSN_KEYINFO20, signal, 25, JBB);
+ src += KeyInfo20::DataLength;;
+ keyLen -= KeyInfo20::DataLength;
+ }
+
+ MEMCOPY_NO_WORDS(keyInfo->keyData, src, keyLen);
+ sendSignal(ref, GSN_KEYINFO20, signal,
+ KeyInfo20::HeaderLength+keyLen, JBB);
+ return keyLen;
+ }
+
+ LinearSectionPtr ptr[3];
+ ptr[0].p = src;
+ ptr[0].sz = keyLen;
+ sendSignal(ref, GSN_KEYINFO20, signal, KeyInfo20::HeaderLength,
+ JBB, ptr, 1);
+ return keyLen;
+ }
+
+ EXECUTE_DIRECT(refToBlock(ref), GSN_KEYINFO20, signal,
+ KeyInfo20::HeaderLength + keyLen);
+ jamEntry();
+ return keyLen;
+ }
+
+ /**
+ * If this node does not have a direct connection
+ * to the receiving node we want to send the signals
+ * routed via the node that controls this read
+ */
+ Uint32 routeBlockref = tcConP->clientBlockref;
+
+ if(keyLen < KeyInfo20::DataLength || !longable){
+ jam();
+
+ while (keyLen > (KeyInfo20::DataLength - 1)) {
+ jam();
+ MEMCOPY_NO_WORDS(keyInfo->keyData, src, KeyInfo20::DataLength - 1);
+ keyInfo->keyData[KeyInfo20::DataLength-1] = ref;
+ sendSignal(routeBlockref, GSN_KEYINFO20_R, signal, 25, JBB);
+ src += KeyInfo20::DataLength - 1;
+ keyLen -= KeyInfo20::DataLength - 1;
+ }
+
+ MEMCOPY_NO_WORDS(keyInfo->keyData, src, keyLen);
+ keyInfo->keyData[keyLen] = ref;
+ sendSignal(routeBlockref, GSN_KEYINFO20_R, signal,
+ KeyInfo20::HeaderLength+keyLen+1, JBB);
+ return keyLen;
+ }
+
+ keyInfo->keyData[0] = ref;
+ LinearSectionPtr ptr[3];
+ ptr[0].p = src;
+ ptr[0].sz = keyLen;
+ sendSignal(routeBlockref, GSN_KEYINFO20_R, signal,
+ KeyInfo20::HeaderLength+1, JBB, ptr, 1);
+ return keyLen;
+}
+
+/* ------------------------------------------------------------------------
+ * ------- SEND SCAN_FRAGCONF TO TC THAT CONTROLS THE SCAN -------
+ *
+ * ------------------------------------------------------------------------ */
+void Dblqh::sendScanFragConf(Signal* signal, Uint32 scanCompleted)
+{
+ Uint32 completed_ops= scanptr.p->m_curr_batch_size_rows;
+ Uint32 total_len= scanptr.p->m_curr_batch_size_bytes;
+ scanptr.p->scanTcWaiting = ZFALSE;
+
+ if(ERROR_INSERTED(5037)){
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }
+ ScanFragConf * conf = (ScanFragConf*)&signal->theData[0];
+ NodeId tc_node_id= refToNode(tcConnectptr.p->clientBlockref);
+ Uint32 trans_id1= tcConnectptr.p->transid[0];
+ Uint32 trans_id2= tcConnectptr.p->transid[1];
+
+ conf->senderData = tcConnectptr.p->clientConnectrec;
+ conf->completedOps = completed_ops;
+ conf->fragmentCompleted = scanCompleted;
+ conf->transId1 = trans_id1;
+ conf->transId2 = trans_id2;
+ conf->total_len= total_len;
+ sendSignal(tcConnectptr.p->clientBlockref, GSN_SCAN_FRAGCONF,
+ signal, ScanFragConf::SignalLength, JBB);
+
+ if(!scanptr.p->scanLockHold)
+ {
+ jam();
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes= 0;
+ }
+}//Dblqh::sendScanFragConf()
+
+/* ######################################################################### */
+/* ####### NODE RECOVERY MODULE ####### */
+/* */
+/* ######################################################################### */
+/*---------------------------------------------------------------------------*/
+/* */
+/* THIS MODULE IS USED WHEN A NODE HAS FAILED. IT PERFORMS A COPY OF A */
+/* FRAGMENT TO A NEW REPLICA OF THE FRAGMENT. IT DOES ALSO SHUT DOWN ALL */
+/* CONNECTIONS TO THE FAILED NODE. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::calculateHash(Signal* signal)
+{
+ DatabufPtr locDatabufptr;
+ UintR Ti;
+ UintR Tdata0;
+ UintR Tdata1;
+ UintR Tdata2;
+ UintR Tdata3;
+ UintR* Tdata32;
+ Uint64 Tdata[512];
+
+ Tdata32 = (UintR*)&Tdata[0];
+
+ Tdata0 = tcConnectptr.p->tupkeyData[0];
+ Tdata1 = tcConnectptr.p->tupkeyData[1];
+ Tdata2 = tcConnectptr.p->tupkeyData[2];
+ Tdata3 = tcConnectptr.p->tupkeyData[3];
+ Tdata32[0] = Tdata0;
+ Tdata32[1] = Tdata1;
+ Tdata32[2] = Tdata2;
+ Tdata32[3] = Tdata3;
+ locDatabufptr.i = tcConnectptr.p->firstTupkeybuf;
+ Ti = 4;
+ while (locDatabufptr.i != RNIL) {
+ ptrCheckGuard(locDatabufptr, cdatabufFileSize, databuf);
+ Tdata0 = locDatabufptr.p->data[0];
+ Tdata1 = locDatabufptr.p->data[1];
+ Tdata2 = locDatabufptr.p->data[2];
+ Tdata3 = locDatabufptr.p->data[3];
+ Tdata32[Ti ] = Tdata0;
+ Tdata32[Ti + 1] = Tdata1;
+ Tdata32[Ti + 2] = Tdata2;
+ Tdata32[Ti + 3] = Tdata3;
+ locDatabufptr.i = locDatabufptr.p->nextDatabuf;
+ Ti += 4;
+ }//while
+ tcConnectptr.p->hashValue =
+ md5_hash((Uint64*)&Tdata32[0], (UintR)tcConnectptr.p->primKeyLen);
+}//Dblqh::calculateHash()
+
+/* *************************************** */
+/* COPY_FRAGREQ: Start copying a fragment */
+/* *************************************** */
+void Dblqh::execCOPY_FRAGREQ(Signal* signal)
+{
+ jamEntry();
+ const CopyFragReq * const copyFragReq = (CopyFragReq *)&signal->theData[0];
+ tabptr.i = copyFragReq->tableId;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ const Uint32 fragId = copyFragReq->fragId;
+ const Uint32 copyPtr = copyFragReq->userPtr;
+ const Uint32 userRef = copyFragReq->userRef;
+ const Uint32 nodeId = copyFragReq->nodeId;
+
+ ndbrequire(cnoActiveCopy < 3);
+ ndbrequire(getFragmentrec(signal, fragId));
+ ndbrequire(fragptr.p->copyFragState == ZIDLE);
+ ndbrequire(cfirstfreeTcConrec != RNIL);
+ ndbrequire(fragptr.p->m_scanNumberMask.get(NR_ScanNo));
+
+ fragptr.p->fragDistributionKey = copyFragReq->distributionKey;
+
+ if (DictTabInfo::isOrderedIndex(tabptr.p->tableType)) {
+ jam();
+ /**
+ * Ordered index doesn't need to be copied
+ */
+ CopyFragConf * const conf = (CopyFragConf *)&signal->theData[0];
+ conf->userPtr = copyPtr;
+ conf->sendingNodeId = cownNodeid;
+ conf->startingNodeId = nodeId;
+ conf->tableId = tabptr.i;
+ conf->fragId = fragId;
+ sendSignal(userRef, GSN_COPY_FRAGCONF, signal,
+ CopyFragConf::SignalLength, JBB);
+ return;
+ }//if
+
+ LocalDLList<ScanRecord> scans(c_scanRecordPool, fragptr.p->m_activeScans);
+ ndbrequire(scans.seize(scanptr));
+/* ------------------------------------------------------------------------- */
+// We keep track of how many operation records in ACC that has been booked.
+// Copy fragment has records always booked and thus need not book any. The
+// most operations in parallel use is the m_max_batch_size_rows.
+// This variable has to be set-up here since it is used by releaseScanrec
+// to unbook operation records in ACC.
+/* ------------------------------------------------------------------------- */
+ scanptr.p->m_max_batch_size_rows = 0;
+ scanptr.p->rangeScan = 0;
+ seizeTcrec();
+
+ /**
+ * Remove implicit cast/usage of CopyFragReq
+ */
+ //initCopyrec(signal);
+ scanptr.p->copyPtr = copyPtr;
+ scanptr.p->scanType = ScanRecord::COPY;
+ scanptr.p->scanApiBlockref = userRef;
+ scanptr.p->scanNodeId = nodeId;
+ scanptr.p->scanTcrec = tcConnectptr.i;
+ scanptr.p->scanSchemaVersion = copyFragReq->schemaVersion;
+ scanptr.p->scanCompletedStatus = ZFALSE;
+ scanptr.p->scanErrorCounter = 0;
+ scanptr.p->scanNumber = NR_ScanNo;
+ scanptr.p->scanKeyinfoFlag = 0; // Don't put into hash
+ scanptr.p->fragPtrI = fragptr.i;
+ fragptr.p->m_scanNumberMask.clear(NR_ScanNo);
+
+ initScanTc(signal,
+ 0,
+ (DBLQH << 20) + (cownNodeid << 8),
+ fragId,
+ copyFragReq->nodeId);
+ cactiveCopy[cnoActiveCopy] = fragptr.i;
+ cnoActiveCopy++;
+
+ tcConnectptr.p->copyCountWords = 0;
+ tcConnectptr.p->tcOprec = tcConnectptr.i;
+ tcConnectptr.p->schemaVersion = scanptr.p->scanSchemaVersion;
+ scanptr.p->scanState = ScanRecord::WAIT_ACC_COPY;
+ AccScanReq * req = (AccScanReq*)&signal->theData[0];
+ req->senderData = scanptr.i;
+ req->senderRef = cownref;
+ req->tableId = tabptr.i;
+ req->fragmentNo = fragId;
+ req->requestInfo = 0;
+ AccScanReq::setLockMode(req->requestInfo, 0);
+ AccScanReq::setReadCommittedFlag(req->requestInfo, 0);
+ req->transId1 = tcConnectptr.p->transid[0];
+ req->transId2 = tcConnectptr.p->transid[1];
+ req->savePointId = tcConnectptr.p->savePointId;
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_ACC_SCANREQ, signal,
+ AccScanReq::SignalLength, JBB);
+ return;
+}//Dblqh::execCOPY_FRAGREQ()
+
+void Dblqh::accScanConfCopyLab(Signal* signal)
+{
+ AccScanConf * const accScanConf = (AccScanConf *)&signal->theData[0];
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+/*--------------------------------------------------------------------------*/
+/* PRECONDITION: SCAN_STATE = WAIT_ACC_COPY */
+/*--------------------------------------------------------------------------*/
+ if (accScanConf->flag == AccScanConf::ZEMPTY_FRAGMENT) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE FRAGMENT WAS EMPTY. */
+/* REPORT SUCCESSFUL COPYING. */
+/*---------------------------------------------------------------------------*/
+ tupCopyCloseConfLab(signal);
+ return;
+ }//if
+ scanptr.p->scanAccPtr = accScanConf->accPtr;
+ scanptr.p->scanState = ScanRecord::WAIT_STORED_PROC_COPY;
+ signal->theData[0] = tcConnectptr.p->tupConnectrec;
+ signal->theData[1] = tcConnectptr.p->tableref;
+ signal->theData[2] = scanptr.p->scanSchemaVersion;
+ signal->theData[3] = ZSTORED_PROC_COPY;
+// theData[4] is not used in TUP with ZSTORED_PROC_COPY
+ sendSignal(tcConnectptr.p->tcTupBlockref, GSN_STORED_PROCREQ, signal, 5, JBB);
+ return;
+}//Dblqh::accScanConfCopyLab()
+
+/*---------------------------------------------------------------------------*/
+/* ENTER STORED_PROCCONF WITH */
+/* TC_CONNECTPTR, */
+/* TSTORED_PROC_ID */
+/*---------------------------------------------------------------------------*/
+void Dblqh::storedProcConfCopyLab(Signal* signal)
+{
+/*---------------------------------------------------------------------------*/
+/* PRECONDITION: SCAN_STATE = WAIT_STORED_PROC_COPY */
+/*---------------------------------------------------------------------------*/
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE COPY PROCESS HAVE BEEN COMPLETED, MOST LIKELY DUE TO A NODE FAILURE.*/
+/*---------------------------------------------------------------------------*/
+ closeCopyLab(signal);
+ return;
+ }//if
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanState = ScanRecord::WAIT_NEXT_SCAN_COPY;
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::COPY_FIRST_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ continueFirstCopyAfterBlockedLab(signal);
+ return;
+}//Dblqh::storedProcConfCopyLab()
+
+void Dblqh::continueFirstCopyAfterBlockedLab(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = RNIL;
+ signal->theData[2] = NextScanReq::ZSCAN_NEXT;
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ return;
+}//Dblqh::continueFirstCopyAfterBlockedLab()
+
+/*---------------------------------------------------------------------------*/
+/* ENTER NEXT_SCANCONF WITH */
+/* SCANPTR, */
+/* TFRAGID, */
+/* TACC_OPPTR, */
+/* TLOCAL_KEY1, */
+/* TLOCAL_KEY2, */
+/* TKEY_LENGTH, */
+/* TKEY1, */
+/* TKEY2, */
+/* TKEY3, */
+/* TKEY4 */
+/*---------------------------------------------------------------------------*/
+/* PRECONDITION: SCAN_STATE = WAIT_NEXT_SCAN_COPY */
+/*---------------------------------------------------------------------------*/
+void Dblqh::nextScanConfCopyLab(Signal* signal)
+{
+ NextScanConf * const nextScanConf = (NextScanConf *)&signal->theData[0];
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ if (nextScanConf->fragId == RNIL) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THERE ARE NO MORE TUPLES TO FETCH. WE NEED TO CLOSE */
+/* THE COPY IN ACC AND DELETE THE STORED PROCEDURE IN TUP */
+/*---------------------------------------------------------------------------*/
+ releaseActiveFrag(signal);
+ if (tcConnectptr.p->copyCountWords == 0) {
+ closeCopyLab(signal);
+ return;
+ }//if
+/*---------------------------------------------------------------------------*/
+// Wait until copying is completed also at the starting node before reporting
+// completion. Signal completion through scanCompletedStatus-flag.
+/*---------------------------------------------------------------------------*/
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ return;
+ }//if
+
+ // If accOperationPtr == RNIL no record was returned by ACC
+ if (nextScanConf->accOperationPtr == RNIL) {
+ jam();
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP;
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB);
+ return;
+ }
+
+ set_acc_ptr_in_scan_record(scanptr.p, 0, nextScanConf->accOperationPtr);
+ initCopyTc(signal);
+ copySendTupkeyReqLab(signal);
+ return;
+}//Dblqh::nextScanConfCopyLab()
+
+void Dblqh::copySendTupkeyReqLab(Signal* signal)
+{
+ Uint32 reqinfo = 0;
+ Uint32 tupFragPtr;
+
+ reqinfo = reqinfo + (tcConnectptr.p->operation << 6);
+ reqinfo = reqinfo + (tcConnectptr.p->opExec << 10);
+ tcConnectptr.p->transactionState = TcConnectionrec::COPY_TUPKEY;
+ scanptr.p->scanState = ScanRecord::WAIT_TUPKEY_COPY;
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ tupFragPtr = fragptr.p->tupFragptr[scanptr.p->scanLocalFragid & 1];
+ {
+ TupKeyReq * const tupKeyReq = (TupKeyReq *)signal->getDataPtrSend();
+
+ tupKeyReq->connectPtr = tcConnectptr.p->tupConnectrec;
+ tupKeyReq->request = reqinfo;
+ tupKeyReq->tableRef = tcConnectptr.p->tableref;
+ tupKeyReq->fragId = scanptr.p->scanLocalFragid;
+ tupKeyReq->keyRef1 = scanptr.p->scanLocalref[0];
+ tupKeyReq->keyRef2 = scanptr.p->scanLocalref[1];
+ tupKeyReq->attrBufLen = 0;
+ tupKeyReq->opRef = tcConnectptr.i;
+ tupKeyReq->applRef = cownref;
+ tupKeyReq->schemaVersion = scanptr.p->scanSchemaVersion;
+ tupKeyReq->storedProcedure = scanptr.p->scanStoredProcId;
+ tupKeyReq->transId1 = tcConnectptr.p->transid[0];
+ tupKeyReq->transId2 = tcConnectptr.p->transid[1];
+ tupKeyReq->fragPtr = tupFragPtr;
+ tupKeyReq->primaryReplica = (tcConnectptr.p->seqNoReplica == 0)?true:false;
+ tupKeyReq->coordinatorTC = tcConnectptr.p->tcBlockref;
+ tupKeyReq->tcOpIndex = tcConnectptr.p->tcOprec;
+ tupKeyReq->savePointId = tcConnectptr.p->savePointId;
+ Uint32 blockNo = refToBlock(tcConnectptr.p->tcTupBlockref);
+ EXECUTE_DIRECT(blockNo, GSN_TUPKEYREQ, signal,
+ TupKeyReq::SignalLength);
+ }
+}//Dblqh::copySendTupkeyReqLab()
+
+/*---------------------------------------------------------------------------*/
+/* USED IN COPYING OPERATION TO RECEIVE ATTRINFO FROM TUP. */
+/*---------------------------------------------------------------------------*/
+/* ************>> */
+/* TRANSID_AI > */
+/* ************>> */
+void Dblqh::execTRANSID_AI(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ Uint32 length = signal->length() - 3;
+ ndbrequire(tcConnectptr.p->transactionState == TcConnectionrec::COPY_TUPKEY);
+ Uint32 * src = &signal->theData[3];
+ while(length > 22){
+ if (saveTupattrbuf(signal, src, 22) == ZOK) {
+ ;
+ } else {
+ jam();
+ tcConnectptr.p->errorCode = ZGET_ATTRINBUF_ERROR;
+ return;
+ }//if
+ src += 22;
+ length -= 22;
+ }
+ if (saveTupattrbuf(signal, src, length) == ZOK) {
+ return;
+ }
+ jam();
+ tcConnectptr.p->errorCode = ZGET_ATTRINBUF_ERROR;
+}//Dblqh::execTRANSID_AI()
+
+/*--------------------------------------------------------------------------*/
+/* ENTER TUPKEYCONF WITH */
+/* TC_CONNECTPTR, */
+/* TDATA2, */
+/* TDATA3, */
+/* TDATA4, */
+/* TDATA5 */
+/*--------------------------------------------------------------------------*/
+/* PRECONDITION: TRANSACTION_STATE = COPY_TUPKEY */
+/*--------------------------------------------------------------------------*/
+void Dblqh::copyTupkeyConfLab(Signal* signal)
+{
+ const TupKeyConf * const tupKeyConf = (TupKeyConf *)signal->getDataPtr();
+
+ UintR readLength = tupKeyConf->readLength;
+
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ ScanRecord* scanP = scanptr.p;
+ releaseActiveFrag(signal);
+ if (tcConnectptr.p->errorCode != 0) {
+ jam();
+ closeCopyLab(signal);
+ return;
+ }//if
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE COPY PROCESS HAVE BEEN CLOSED. MOST LIKELY A NODE FAILURE. */
+/*---------------------------------------------------------------------------*/
+ closeCopyLab(signal);
+ return;
+ }//if
+ TcConnectionrec * tcConP = tcConnectptr.p;
+ tcConnectptr.p->totSendlenAi = readLength;
+ tcConnectptr.p->connectState = TcConnectionrec::COPY_CONNECTED;
+
+ // Read primary keys (used to get here via scan keyinfo)
+ Uint32* tmp = signal->getDataPtrSend()+24;
+ Uint32 len= tcConnectptr.p->primKeyLen = readPrimaryKeys(scanP, tcConP, tmp);
+
+ // Calculate hash (no need to linearies key)
+ tcConnectptr.p->hashValue = md5_hash((Uint64*)tmp, len);
+
+ // Move into databuffer to make packLqhkeyreqLab happy
+ memcpy(tcConP->tupkeyData, tmp, 4*4);
+ if(len > 4)
+ keyinfoLab(tmp+4, tmp + len);
+ LqhKeyReq::setKeyLen(tcConP->reqinfo, len);
+
+/*---------------------------------------------------------------------------*/
+// To avoid using up to many operation records in ACC we will increase the
+// constant to ensure that we never send more than 40 records at a time.
+// This is where the constant 56 comes from. For long records this constant
+// will not matter that much. The current maximum is 6000 words outstanding
+// (including a number of those 56 words not really sent). We also have to
+// ensure that there are never more simultaneous usage of these operation
+// records to ensure that node recovery does not fail because of simultaneous
+// scanning.
+/*---------------------------------------------------------------------------*/
+ UintR TnoOfWords = readLength + len;
+ TnoOfWords = TnoOfWords + MAGIC_CONSTANT;
+ TnoOfWords = TnoOfWords + (TnoOfWords >> 2);
+
+ /*-----------------------------------------------------------------
+ * NOTE for transid1!
+ * Transid1 in the tcConnection record is used load regulate the
+ * copy(node recovery) process.
+ * The number of outstanding words are written in the transid1
+ * variable. This will be sent to the starting node in the
+ * LQHKEYREQ signal and when the answer is returned in the LQHKEYCONF
+ * we can reduce the number of outstanding words and check to see
+ * if more LQHKEYREQ signals should be sent.
+ *
+ * However efficient this method is rather unsafe in such way that
+ * it overwrites the transid1 original data.
+ *
+ * Also see TR 587.
+ *----------------------------------------------------------------*/
+ tcConnectptr.p->transid[0] = TnoOfWords; // Data overload, see note!
+ packLqhkeyreqLab(signal);
+ tcConnectptr.p->copyCountWords += TnoOfWords;
+ scanptr.p->scanState = ScanRecord::WAIT_LQHKEY_COPY;
+ if (tcConnectptr.p->copyCountWords < cmaxWordsAtNodeRec) {
+ nextRecordCopy(signal);
+ return;
+ }//if
+ return;
+}//Dblqh::copyTupkeyConfLab()
+
+/*---------------------------------------------------------------------------*/
+/* ENTER LQHKEYCONF */
+/*---------------------------------------------------------------------------*/
+/* PRECONDITION: CONNECT_STATE = COPY_CONNECTED */
+/*---------------------------------------------------------------------------*/
+void Dblqh::copyCompletedLab(Signal* signal)
+{
+ const LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+
+ ndbrequire(tcConnectptr.p->transid[1] == lqhKeyConf->transId2);
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ if (tcConnectptr.p->copyCountWords >= cmaxWordsAtNodeRec) {
+ tcConnectptr.p->copyCountWords -= lqhKeyConf->transId1; // Data overload, see note!
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+/*---------------------------------------------------------------------------*/
+// Copy to complete, we will not start any new copying.
+/*---------------------------------------------------------------------------*/
+ closeCopyLab(signal);
+ return;
+ }//if
+ if (tcConnectptr.p->copyCountWords < cmaxWordsAtNodeRec) {
+ jam();
+ nextRecordCopy(signal);
+ }//if
+ return;
+ }//if
+ tcConnectptr.p->copyCountWords -= lqhKeyConf->transId1; // Data overload, see note!
+ ndbrequire(tcConnectptr.p->copyCountWords <= cmaxWordsAtNodeRec);
+ if (tcConnectptr.p->copyCountWords > 0) {
+ jam();
+ return;
+ }//if
+/*---------------------------------------------------------------------------*/
+// No more outstanding copies. We will only start new ones from here if it was
+// stopped before and this only happens when copyCountWords is bigger than the
+// threshold value. Since this did not occur we must be waiting for completion.
+// Check that this is so. If not we crash to find out what is going on.
+/*---------------------------------------------------------------------------*/
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+ closeCopyLab(signal);
+ return;
+ }//if
+ if (scanptr.p->scanState == ScanRecord::WAIT_LQHKEY_COPY) {
+ jam();
+/*---------------------------------------------------------------------------*/
+// Make sure that something is in progress. Otherwise we will simply stop
+// and nothing more will happen.
+/*---------------------------------------------------------------------------*/
+ systemErrorLab(signal);
+ return;
+ }//if
+ return;
+}//Dblqh::copyCompletedLab()
+
+void Dblqh::nextRecordCopy(Signal* signal)
+{
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ if (scanptr.p->scanState != ScanRecord::WAIT_LQHKEY_COPY) {
+ jam();
+/*---------------------------------------------------------------------------*/
+// Make sure that nothing is in progress. Otherwise we will have to simultaneous
+// scans on the same record and this will certainly lead to unexpected
+// behaviour.
+/*---------------------------------------------------------------------------*/
+ systemErrorLab(signal);
+ return;
+ }//if
+ scanptr.p->scanState = ScanRecord::WAIT_NEXT_SCAN_COPY;
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::COPY_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ continueCopyAfterBlockedLab(signal);
+ return;
+}//Dblqh::nextRecordCopy()
+
+void Dblqh::continueCopyAfterBlockedLab(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ tcConnectptr.p->errorCode = 0;
+ Uint32 acc_op_ptr= get_acc_ptr_from_scan_record(scanptr.p, 0, false);
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = acc_op_ptr;
+ signal->theData[2] = NextScanReq::ZSCAN_NEXT_COMMIT;
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ return;
+}//Dblqh::continueCopyAfterBlockedLab()
+
+void Dblqh::copyLqhKeyRefLab(Signal* signal)
+{
+ ndbrequire(tcConnectptr.p->transid[1] == signal->theData[4]);
+ tcConnectptr.p->copyCountWords -= signal->theData[3];
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanErrorCounter++;
+ tcConnectptr.p->errorCode = terrorCode;
+ closeCopyLab(signal);
+ return;
+}//Dblqh::copyLqhKeyRefLab()
+
+void Dblqh::closeCopyLab(Signal* signal)
+{
+ if (tcConnectptr.p->copyCountWords > 0) {
+/*---------------------------------------------------------------------------*/
+// We are still waiting for responses from the starting node.
+// Wait until all of those have arrived until we start the
+// close process.
+/*---------------------------------------------------------------------------*/
+ jam();
+ return;
+ }//if
+ tcConnectptr.p->transid[0] = 0;
+ tcConnectptr.p->transid[1] = 0;
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ scanptr.p->scanState = ScanRecord::WAIT_CLOSE_COPY;
+ switch (fragptr.p->fragStatus) {
+ case Fragrecord::FSACTIVE:
+ jam();
+ linkActiveFrag(signal);
+ break;
+ case Fragrecord::BLOCKED:
+ jam();
+ linkFragQueue(signal);
+ tcConnectptr.p->transactionState = TcConnectionrec::COPY_CLOSE_STOPPED;
+ return;
+ break;
+ case Fragrecord::FREE:
+ jam();
+ case Fragrecord::ACTIVE_CREATION:
+ jam();
+ case Fragrecord::CRASH_RECOVERING:
+ jam();
+ case Fragrecord::DEFINED:
+ jam();
+ case Fragrecord::REMOVING:
+ jam();
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ continueCloseCopyAfterBlockedLab(signal);
+ return;
+}//Dblqh::closeCopyLab()
+
+void Dblqh::continueCloseCopyAfterBlockedLab(Signal* signal)
+{
+ scanptr.i = tcConnectptr.p->tcScanRec;
+ c_scanRecordPool.getPtr(scanptr);
+ signal->theData[0] = scanptr.p->scanAccPtr;
+ signal->theData[1] = RNIL;
+ signal->theData[2] = ZCOPY_CLOSE;
+ sendSignal(tcConnectptr.p->tcAccBlockref, GSN_NEXT_SCANREQ, signal, 3, JBB);
+ return;
+}//Dblqh::continueCloseCopyAfterBlockedLab()
+
+/*---------------------------------------------------------------------------*/
+/* ENTER NEXT_SCANCONF WITH */
+/* SCANPTR, */
+/* TFRAGID, */
+/* TACC_OPPTR, */
+/* TLOCAL_KEY1, */
+/* TLOCAL_KEY2, */
+/* TKEY_LENGTH, */
+/* TKEY1, */
+/* TKEY2, */
+/* TKEY3, */
+/* TKEY4 */
+/*---------------------------------------------------------------------------*/
+/* PRECONDITION: SCAN_STATE = WAIT_CLOSE_COPY */
+/*---------------------------------------------------------------------------*/
+void Dblqh::accCopyCloseConfLab(Signal* signal)
+{
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ scanptr.p->scanState = ScanRecord::WAIT_DELETE_STORED_PROC_ID_COPY;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ signal->theData[0] = tcConnectptr.p->tupConnectrec;
+ signal->theData[1] = tcConnectptr.p->tableref;
+ signal->theData[2] = scanptr.p->scanSchemaVersion;
+ signal->theData[3] = ZDELETE_STORED_PROC_ID;
+ signal->theData[4] = scanptr.p->scanStoredProcId;
+ sendSignal(tcConnectptr.p->tcTupBlockref, GSN_STORED_PROCREQ, signal, 5, JBB);
+ return;
+}//Dblqh::accCopyCloseConfLab()
+
+/*---------------------------------------------------------------------------*/
+/* ENTER STORED_PROCCONF WITH */
+/* TC_CONNECTPTR, */
+/* TSTORED_PROC_ID */
+/*---------------------------------------------------------------------------*/
+/* PRECONDITION: SCAN_STATE = WAIT_DELETE_STORED_PROC_ID_COPY */
+/*---------------------------------------------------------------------------*/
+void Dblqh::tupCopyCloseConfLab(Signal* signal)
+{
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->copyFragState = ZIDLE;
+
+ if (tcConnectptr.p->abortState == TcConnectionrec::NEW_FROM_TC) {
+ jam();
+ tcNodeFailptr.i = tcConnectptr.p->tcNodeFailrec;
+ ptrCheckGuard(tcNodeFailptr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+ tcNodeFailptr.p->tcRecNow = tcConnectptr.i + 1;
+ signal->theData[0] = ZLQH_TRANS_NEXT;
+ signal->theData[1] = tcNodeFailptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+
+ CopyFragRef * const ref = (CopyFragRef *)&signal->theData[0];
+ ref->userPtr = scanptr.p->copyPtr;
+ ref->sendingNodeId = cownNodeid;
+ ref->startingNodeId = scanptr.p->scanNodeId;
+ ref->tableId = fragptr.p->tabRef;
+ ref->fragId = fragptr.p->fragId;
+ ref->errorCode = ZNODE_FAILURE_ERROR;
+ sendSignal(scanptr.p->scanApiBlockref, GSN_COPY_FRAGREF, signal,
+ CopyFragRef::SignalLength, JBB);
+ } else {
+ if (scanptr.p->scanErrorCounter > 0) {
+ jam();
+ CopyFragRef * const ref = (CopyFragRef *)&signal->theData[0];
+ ref->userPtr = scanptr.p->copyPtr;
+ ref->sendingNodeId = cownNodeid;
+ ref->startingNodeId = scanptr.p->scanNodeId;
+ ref->tableId = fragptr.p->tabRef;
+ ref->fragId = fragptr.p->fragId;
+ ref->errorCode = tcConnectptr.p->errorCode;
+ sendSignal(scanptr.p->scanApiBlockref, GSN_COPY_FRAGREF, signal,
+ CopyFragRef::SignalLength, JBB);
+ } else {
+ jam();
+ CopyFragConf * const conf = (CopyFragConf *)&signal->theData[0];
+ conf->userPtr = scanptr.p->copyPtr;
+ conf->sendingNodeId = cownNodeid;
+ conf->startingNodeId = scanptr.p->scanNodeId;
+ conf->tableId = tcConnectptr.p->tableref;
+ conf->fragId = tcConnectptr.p->fragmentid;
+ sendSignal(scanptr.p->scanApiBlockref, GSN_COPY_FRAGCONF, signal,
+ CopyFragConf::SignalLength, JBB);
+ }//if
+ }//if
+ releaseActiveCopy(signal);
+ tcConnectptr.p->tcScanRec = RNIL;
+ finishScanrec(signal);
+ releaseOprec(signal);
+ releaseTcrec(signal, tcConnectptr);
+ releaseScanrec(signal);
+}//Dblqh::tupCopyCloseConfLab()
+
+/*---------------------------------------------------------------------------*/
+/* A NODE FAILURE OCCURRED DURING THE COPY PROCESS. WE NEED TO CLOSE THE */
+/* COPY PROCESS SINCE A NODE FAILURE DURING THE COPY PROCESS WILL ALSO */
+/* FAIL THE NODE THAT IS TRYING TO START-UP. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::closeCopyRequestLab(Signal* signal)
+{
+ scanptr.p->scanErrorCounter++;
+ switch (scanptr.p->scanState) {
+ case ScanRecord::WAIT_TUPKEY_COPY:
+ case ScanRecord::WAIT_NEXT_SCAN_COPY:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* SET COMPLETION STATUS AND WAIT FOR OPPORTUNITY TO STOP THE SCAN. */
+// ALSO SET NO OF WORDS OUTSTANDING TO ZERO TO AVOID ETERNAL WAIT.
+/*---------------------------------------------------------------------------*/
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ tcConnectptr.p->copyCountWords = 0;
+ break;
+ case ScanRecord::WAIT_ACC_COPY:
+ case ScanRecord::WAIT_STORED_PROC_COPY:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* WE ARE CURRENTLY STARTING UP THE SCAN. SET COMPLETED STATUS AND WAIT FOR*/
+/* COMPLETION OF STARTUP. */
+/*---------------------------------------------------------------------------*/
+ scanptr.p->scanCompletedStatus = ZTRUE;
+ break;
+ case ScanRecord::WAIT_CLOSE_COPY:
+ case ScanRecord::WAIT_DELETE_STORED_PROC_ID_COPY:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* CLOSE IS ALREADY ONGOING. WE NEED NOT DO ANYTHING. */
+/*---------------------------------------------------------------------------*/
+ break;
+ case ScanRecord::WAIT_LQHKEY_COPY:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* WE ARE WAITING FOR THE FAILED NODE. THE NODE WILL NEVER COME BACK. */
+// WE NEED TO START THE FAILURE HANDLING IMMEDIATELY.
+// ALSO SET NO OF WORDS OUTSTANDING TO ZERO TO AVOID ETERNAL WAIT.
+/*---------------------------------------------------------------------------*/
+ tcConnectptr.p->copyCountWords = 0;
+ closeCopyLab(signal);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dblqh::closeCopyRequestLab()
+
+/* ****************************************************** */
+/* COPY_ACTIVEREQ: Change state of a fragment to ACTIVE. */
+/* ****************************************************** */
+void Dblqh::execCOPY_ACTIVEREQ(Signal* signal)
+{
+ CRASH_INSERTION(5026);
+
+ const CopyActiveReq * const req = (CopyActiveReq *)&signal->theData[0];
+ jamEntry();
+ Uint32 masterPtr = req->userPtr;
+ BlockReference masterRef = req->userRef;
+ tabptr.i = req->tableId;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ Uint32 fragId = req->fragId;
+ ndbrequire(getFragmentrec(signal, fragId));
+
+ fragptr.p->fragDistributionKey = req->distributionKey;
+
+ ndbrequire(cnoActiveCopy < 3);
+ cactiveCopy[cnoActiveCopy] = fragptr.i;
+ cnoActiveCopy++;
+ fragptr.p->masterBlockref = masterRef;
+ fragptr.p->masterPtr = masterPtr;
+ if (fragptr.p->fragStatus == Fragrecord::FSACTIVE) {
+ jam();
+/*------------------------------------------------------*/
+/* PROCESS HAVE ALREADY BEEN STARTED BY PREVIOUS */
+/* MASTER. WE HAVE ALREADY SET THE PROPER MASTER */
+/* BLOCK REFERENCE. */
+/*------------------------------------------------------*/
+ if (fragptr.p->activeTcCounter == 0) {
+ jam();
+/*------------------------------------------------------*/
+/* PROCESS WAS EVEN COMPLETED. */
+/*------------------------------------------------------*/
+ sendCopyActiveConf(signal, tabptr.i);
+ }//if
+ return;
+ }//if
+ fragptr.p->fragStatus = Fragrecord::FSACTIVE;
+ if (fragptr.p->lcpFlag == Fragrecord::LCP_STATE_TRUE) {
+ jam();
+ fragptr.p->logFlag = Fragrecord::STATE_TRUE;
+ }//if
+ fragptr.p->activeTcCounter = 1;
+/*------------------------------------------------------*/
+/* SET IT TO ONE TO ENSURE THAT IT IS NOT POSSIBLE*/
+/* TO DECREASE IT TO ZERO UNTIL WE HAVE COMPLETED */
+/* THE SCAN. */
+/*------------------------------------------------------*/
+ signal->theData[0] = ZSCAN_TC_CONNECT;
+ signal->theData[1] = 0;
+ signal->theData[2] = tabptr.i;
+ signal->theData[3] = fragId;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ return;
+}//Dblqh::execCOPY_ACTIVEREQ()
+
+void Dblqh::scanTcConnectLab(Signal* signal, Uint32 tstartTcConnect, Uint32 fragId)
+{
+ Uint32 tendTcConnect;
+
+ ndbrequire(getFragmentrec(signal, fragId));
+ if ((tstartTcConnect + 200) >= ctcConnectrecFileSize) {
+ jam();
+ tendTcConnect = ctcConnectrecFileSize - 1;
+ } else {
+ jam();
+ tendTcConnect = tstartTcConnect + 200;
+ }//if
+ for (tcConnectptr.i = tstartTcConnect;
+ tcConnectptr.i <= tendTcConnect;
+ tcConnectptr.i++) {
+ jam();
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ if (tcConnectptr.p->transactionState != TcConnectionrec::IDLE) {
+ switch (tcConnectptr.p->logWriteState) {
+ case TcConnectionrec::NOT_WRITTEN:
+ jam();
+ if (fragptr.i == tcConnectptr.p->fragmentptr) {
+ jam();
+ fragptr.p->activeTcCounter = fragptr.p->activeTcCounter + 1;
+ tcConnectptr.p->logWriteState = TcConnectionrec::NOT_WRITTEN_WAIT;
+ }//if
+ break;
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }//switch
+ }//if
+ }//for
+ if (tendTcConnect < (ctcConnectrecFileSize - 1)) {
+ jam();
+ signal->theData[0] = ZSCAN_TC_CONNECT;
+ signal->theData[1] = tendTcConnect + 1;
+ signal->theData[2] = tabptr.i;
+ signal->theData[3] = fragId;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ } else {
+ jam();
+/*------------------------------------------------------*/
+/* THE SCAN HAVE BEEN COMPLETED. WE CHECK IF ALL */
+/* OPERATIONS HAVE ALREADY BEEN COMPLETED. */
+/*------------------------------------------------------*/
+ ndbrequire(fragptr.p->activeTcCounter > 0);
+ fragptr.p->activeTcCounter--;
+ if (fragptr.p->activeTcCounter == 0) {
+ jam();
+/*------------------------------------------------------*/
+/* SET START GLOBAL CHECKPOINT TO THE NEXT */
+/* CHECKPOINT WE HAVE NOT YET HEARD ANYTHING ABOUT*/
+/* THIS GCP WILL BE COMPLETELY COVERED BY THE LOG.*/
+/*------------------------------------------------------*/
+ fragptr.p->startGci = cnewestGci + 1;
+ sendCopyActiveConf(signal, tabptr.i);
+ }//if
+ }//if
+ return;
+}//Dblqh::scanTcConnectLab()
+
+/*---------------------------------------------------------------------------*/
+/* A NEW MASTER IS REQUESTING THE STATE IN LQH OF THE COPY FRAGMENT PARTS. */
+/*---------------------------------------------------------------------------*/
+/* ***************>> */
+/* COPY_STATEREQ > */
+/* ***************>> */
+void Dblqh::execCOPY_STATEREQ(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(0)
+#if 0
+ Uint32* dataPtr = &signal->theData[2];
+ BlockReference tmasterBlockref = signal->theData[0];
+ Uint32 tnoCopy = 0;
+ do {
+ jam();
+ arrGuard(tnoCopy, 4);
+ fragptr.i = cactiveCopy[tnoCopy];
+ if (fragptr.i == RNIL) {
+ jam();
+ break;
+ }//if
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (fragptr.p->copyFragState != ZIDLE) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS FRAGMENT IS CURRENTLY ACTIVE IN COPYING THE FRAGMENT. */
+/*---------------------------------------------------------------------------*/
+ scanptr.i = fragptr.p->fragScanRec[NR_ScanNo];
+ c_scanRecordPool.getPtr(scanptr);
+ if (scanptr.p->scanCompletedStatus == ZTRUE) {
+ jam();
+ dataPtr[3 + (tnoCopy << 2)] = ZCOPY_CLOSING;
+ } else {
+ jam();
+ dataPtr[3 + (tnoCopy << 2)] = ZCOPY_ONGOING;
+ }//if
+ dataPtr[2 + (tnoCopy << 2)] = scanptr.p->scanSchemaVersion;
+ scanptr.p->scanApiBlockref = tmasterBlockref;
+ } else {
+ ndbrequire(fragptr.p->activeTcCounter != 0);
+/*---------------------------------------------------------------------------*/
+/* COPY FRAGMENT IS COMPLETED AND WE ARE CURRENTLY GETTING THE STARTING */
+/* GCI OF THE NEW REPLICA OF THIS FRAGMENT. */
+/*---------------------------------------------------------------------------*/
+ fragptr.p->masterBlockref = tmasterBlockref;
+ dataPtr[3 + (tnoCopy << 2)] = ZCOPY_ACTIVATION;
+ }//if
+ dataPtr[tnoCopy << 2] = fragptr.p->tabRef;
+ dataPtr[1 + (tnoCopy << 2)] = fragptr.p->fragId;
+ tnoCopy++;
+ } while (tnoCopy < cnoActiveCopy);
+ signal->theData[0] = cownNodeid;
+ signal->theData[1] = tnoCopy;
+ sendSignal(tmasterBlockref, GSN_COPY_STATECONF, signal, 18, JBB);
+#endif
+ return;
+}//Dblqh::execCOPY_STATEREQ()
+
+/* ========================================================================= */
+/* ======= INITIATE TC RECORD AT COPY FRAGMENT ======= */
+/* */
+/* SUBROUTINE SHORT NAME = ICT */
+/* ========================================================================= */
+void Dblqh::initCopyTc(Signal* signal)
+{
+ const NextScanConf * const nextScanConf = (NextScanConf *)&signal->theData[0];
+ scanptr.p->scanLocalref[0] = nextScanConf->localKey[0];
+ scanptr.p->scanLocalref[1] = nextScanConf->localKey[1];
+ scanptr.p->scanLocalFragid = nextScanConf->fragId;
+ tcConnectptr.p->operation = ZREAD;
+ tcConnectptr.p->apiVersionNo = 0;
+ tcConnectptr.p->opExec = 0; /* NOT INTERPRETED MODE */
+ tcConnectptr.p->schemaVersion = scanptr.p->scanSchemaVersion;
+ Uint32 reqinfo = 0;
+ LqhKeyReq::setLockType(reqinfo, ZINSERT);
+ LqhKeyReq::setDirtyFlag(reqinfo, 1);
+ LqhKeyReq::setSimpleFlag(reqinfo, 1);
+ LqhKeyReq::setOperation(reqinfo, ZWRITE);
+ /* AILen in LQHKEYREQ IS ZERO */
+ tcConnectptr.p->reqinfo = reqinfo;
+/* ------------------------------------------------------------------------ */
+/* THE RECEIVING NODE WILL EXPECT THAT IT IS THE LAST NODE AND WILL */
+/* SEND COMPLETED AS THE RESPONSE SIGNAL SINCE DIRTY_OP BIT IS SET. */
+/* ------------------------------------------------------------------------ */
+ tcConnectptr.p->nodeAfterNext[0] = ZNIL;
+ tcConnectptr.p->nodeAfterNext[1] = ZNIL;
+ tcConnectptr.p->tcBlockref = cownref;
+ tcConnectptr.p->readlenAi = 0;
+ tcConnectptr.p->storedProcId = ZNIL;
+ tcConnectptr.p->opExec = 0;
+ tcConnectptr.p->nextSeqNoReplica = 0;
+ tcConnectptr.p->dirtyOp = ZFALSE;
+ tcConnectptr.p->lastReplicaNo = 0;
+ tcConnectptr.p->currTupAiLen = 0;
+ tcConnectptr.p->tcTimer = cLqhTimeOutCount;
+}//Dblqh::initCopyTc()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEND COPY_ACTIVECONF TO MASTER DIH ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::sendCopyActiveConf(Signal* signal, Uint32 tableId)
+{
+ releaseActiveCopy(signal);
+ CopyActiveConf * const conf = (CopyActiveConf *)&signal->theData[0];
+ conf->userPtr = fragptr.p->masterPtr;
+ conf->tableId = tableId;
+ conf->fragId = fragptr.p->fragId;
+ conf->startingNodeId = cownNodeid;
+ conf->startGci = fragptr.p->startGci;
+ sendSignal(fragptr.p->masterBlockref, GSN_COPY_ACTIVECONF, signal,
+ CopyActiveConf::SignalLength, JBB);
+}//Dblqh::sendCopyActiveConf()
+
+/* ##########################################################################
+ * ####### LOCAL CHECKPOINT MODULE #######
+ *
+ * ##########################################################################
+ * --------------------------------------------------------------------------
+ * THIS MODULE HANDLES THE EXECUTION AND CONTROL OF LOCAL CHECKPOINTS
+ * IT CONTROLS THE LOCAL CHECKPOINTS IN TUP AND ACC. IT DOES ALSO INTERACT
+ * WITH DIH TO CONTROL WHICH GLOBAL CHECKPOINTS THAT ARE RECOVERABLE
+ * ------------------------------------------------------------------------- */
+void Dblqh::execEMPTY_LCP_REQ(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(5008);
+ EmptyLcpReq * const emptyLcpOrd = (EmptyLcpReq*)&signal->theData[0];
+
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+
+ Uint32 nodeId = refToNode(emptyLcpOrd->senderRef);
+
+ lcpPtr.p->m_EMPTY_LCP_REQ.set(nodeId);
+ lcpPtr.p->reportEmpty = true;
+
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_IDLE){
+ jam();
+ bool ok = false;
+ switch(clcpCompletedState){
+ case LCP_IDLE:
+ ok = true;
+ sendEMPTY_LCP_CONF(signal, true);
+ break;
+ case LCP_RUNNING:
+ ok = true;
+ sendEMPTY_LCP_CONF(signal, false);
+ break;
+ case LCP_CLOSE_STARTED:
+ jam();
+ case ACC_LCP_CLOSE_COMPLETED:
+ jam();
+ case TUP_LCP_CLOSE_COMPLETED:
+ jam();
+ ok = true;
+ break;
+ }
+ ndbrequire(ok);
+
+ }//if
+
+ return;
+}//Dblqh::execEMPTY_LCPREQ()
+
+void Dblqh::execLCP_FRAG_ORD(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(5010);
+ LcpFragOrd * const lcpFragOrd = (LcpFragOrd *)&signal->theData[0];
+ Uint32 lcpId = lcpFragOrd->lcpId;
+
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+
+ lcpPtr.p->lastFragmentFlag = lcpFragOrd->lastFragmentFlag;
+ if (lcpFragOrd->lastFragmentFlag) {
+ jam();
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_IDLE) {
+ jam();
+ /* ----------------------------------------------------------
+ * NOW THE COMPLETE LOCAL CHECKPOINT ROUND IS COMPLETED.
+ * -------------------------------------------------------- */
+ if (cnoOfFragsCheckpointed > 0) {
+ jam();
+ completeLcpRoundLab(signal);
+ } else {
+ jam();
+ sendLCP_COMPLETE_REP(signal, lcpId);
+ }//if
+ }
+ return;
+ }//if
+ tabptr.i = lcpFragOrd->tableId;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+
+ ndbrequire(tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_ONGOING ||
+ tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_DONE ||
+ tabptr.p->tableStatus == Tablerec::TABLE_DEFINED);
+
+ ndbrequire(getFragmentrec(signal, lcpFragOrd->fragmentId));
+
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+ ndbrequire(!lcpPtr.p->lcpQueued);
+ if (c_lcpId < lcpFragOrd->lcpId) {
+ jam();
+ /**
+ * A new LCP
+ */
+ c_lcpId = lcpFragOrd->lcpId;
+ ndbrequire(lcpPtr.p->lcpState == LcpRecord::LCP_IDLE);
+ setLogTail(signal, lcpFragOrd->keepGci);
+ ndbrequire(clcpCompletedState == LCP_IDLE);
+ clcpCompletedState = LCP_RUNNING;
+ }//if
+ cnoOfFragsCheckpointed++;
+
+ if(tabptr.p->tableStatus == Tablerec::PREP_DROP_TABLE_DONE){
+ jam();
+ LcpRecord::FragOrd fragOrd;
+ fragOrd.fragPtrI = fragptr.i;
+ fragOrd.lcpFragOrd = * lcpFragOrd;
+ sendLCP_FRAG_REP(signal, fragOrd);
+ return;
+ }
+
+ if (lcpPtr.p->lcpState != LcpRecord::LCP_IDLE) {
+ ndbrequire(lcpPtr.p->lcpQueued == false);
+ lcpPtr.p->lcpQueued = true;
+ lcpPtr.p->queuedFragment.fragPtrI = fragptr.i;
+ lcpPtr.p->queuedFragment.lcpFragOrd = * lcpFragOrd;
+ return;
+ }//if
+
+ lcpPtr.p->currentFragment.fragPtrI = fragptr.i;
+ lcpPtr.p->currentFragment.lcpFragOrd = * lcpFragOrd;
+
+ sendLCP_FRAGIDREQ(signal);
+}//Dblqh::execLCP_FRAGORD()
+
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_PTR:LCP_STATE = WAIT_FRAGID
+ * --------------------------------------------------------------------------
+ * WE NOW HAVE THE LOCAL FRAGMENTS THAT THE LOCAL CHECKPOINT WILL USE.
+ * -------------------------------------------------------------------------- */
+void Dblqh::execLCP_FRAGIDCONF(Signal* signal)
+{
+ UintR Tfragid[4];
+
+ jamEntry();
+
+ lcpPtr.i = signal->theData[0];
+
+ Uint32 TaccPtr = signal->theData[1];
+ Uint32 noLocfrag = signal->theData[2];
+ Tfragid[0] = signal->theData[3];
+ Tfragid[1] = signal->theData[4];
+
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ ndbrequire(lcpPtr.p->lcpState == LcpRecord::LCP_WAIT_FRAGID);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECKING OF TNO_LOCFRAG VALUE. OUT OF BOUND WILL IMPLY THAT AN
+ * INDEX OUT OF RANGE WILL CAUSE A SYSTEM RESTART WHICH IS DESIRED.
+ * ------------------------------------------------------------------------ */
+ lcpPtr.p->lcpAccptr = TaccPtr;
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ ndbrequire(noLocfrag - 1 < 2);
+ for (Uint32 Tindex = 0; Tindex < noLocfrag; Tindex++) {
+ jam();
+ Uint32 fragId = Tfragid[Tindex];
+ /* ----------------------------------------------------------------------
+ * THERE IS NO ERROR CHECKING ON PURPOSE. IT IS POSSIBLE TO CALCULATE HOW
+ * MANY LOCAL LCP RECORDS THERE SHOULD BE. IT SHOULD NEVER HAPPEN THAT
+ * THERE IS NO ONE FREE. IF THERE IS NO ONE IT WILL ALSO BE A POINTER
+ * OUT OF RANGE WHICH IS AN ERROR CODE IN ITSELF. REUSES ERROR HANDLING
+ * IN AXE VM.
+ * ---------------------------------------------------------------------- */
+ seizeLcpLoc(signal);
+ initLcpLocAcc(signal, fragId);
+ seizeLcpLoc(signal);
+ initLcpLocTup(signal, fragId);
+ signal->theData[0] = lcpLocptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ signal->theData[3] = lcpLocptr.p->locFragid;
+ signal->theData[4] = lcpPtr.p->currentFragment.lcpFragOrd.lcpNo;
+ signal->theData[5] = lcpPtr.p->currentFragment.lcpFragOrd.lcpId % MAX_LCP_STORED;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUP_PREPLCPREQ, signal, 6, JBB);
+ }//for
+ lcpPtr.p->lcpState = LcpRecord::LCP_WAIT_TUP_PREPLCP;
+ return;
+}//Dblqh::execLCP_FRAGIDCONF()
+
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_STATE = WAIT_TUPPREPLCP
+ * --------------------------------------------------------------------------
+ * WE HAVE NOW PREPARED A LOCAL FRAGMENT IN TUP FOR LCP EXECUTION.
+ * -------------------------------------------------------------------------- */
+void Dblqh::execTUP_PREPLCPCONF(Signal* signal)
+{
+ UintR ttupPtr;
+
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ttupPtr = signal->theData[1];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::WAIT_TUP_PREPLCP);
+
+ lcpLocptr.p->tupRef = ttupPtr;
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::IDLE;
+ checkLcpTupprep(signal);
+ if (lcpPtr.p->lcpState != LcpRecord::LCP_WAIT_HOLDOPS) {
+ jam();
+ return;
+ }//if
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ lcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ do {
+ jam();
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::WAIT_LCPHOLDOP;
+ signal->theData[0] = lcpPtr.p->lcpAccptr;
+ signal->theData[1] = lcpLocptr.p->locFragid;
+ signal->theData[2] = 0;
+ signal->theData[3] = lcpLocptr.i;
+ sendSignal(fragptr.p->accBlockref, GSN_LCP_HOLDOPREQ, signal, 4, JBA);
+ lcpLocptr.i = lcpLocptr.p->nextLcpLoc;
+ } while (lcpLocptr.i != RNIL);
+ /* ------------------------------------------------------------------------
+ * SET STATE ON FRAGMENT TO BLOCKED TO ENSURE THAT NO MORE OPERATIONS ARE
+ * STARTED FROM LQH IN TUP AND ACC UNTIL THE START CHECKPOINT HAS BEEN
+ * COMPLETED. ALSO SET THE LOCAL CHECKPOINT STATE TO WAIT FOR
+ * LCP_HOLDOPCONF
+ * ----------------------------------------------------------------------- */
+ fragptr.p->fragStatus = Fragrecord::BLOCKED;
+ fragptr.p->fragActiveStatus = ZTRUE;
+ lcpPtr.p->lcpState = LcpRecord::LCP_WAIT_HOLDOPS;
+ return;
+}//Dblqh::execTUP_PREPLCPCONF()
+
+void Dblqh::execTUP_PREPLCPREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execTUP_PREPLCPREF()
+
+void Dblqh::execLCP_FRAGIDREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execLCP_FRAGIDREF()
+
+/* --------------------------------------------------------------------------
+ * A NUMBER OF OPERATIONS THAT HAVE BEEN SET ON HOLD IN ACC. MOVE THOSE TO
+ * LIST OF BLOCKED ACC OPERATIONS. IF MORE OPERATIONS ARE BLOCKED GET THOSE
+ * OTHERWISE CONTINUE THE LOCAL CHECKPOINT BY REQUESTING TUP AND ACC TO
+ * WRITE THEIR START CHECKPOINT.
+ * --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = WAIT_LCPHOLDOP
+ * ------------------------------------------------------------------------- */
+/* ***************>> */
+/* LCP_HOLDOPCONF > */
+/* ***************>> */
+void Dblqh::execLCP_HOLDOPCONF(Signal* signal)
+{
+ UintR tnoHoldops;
+ Uint32 Tdata[23];
+ Uint32 Tlength;
+
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ Tlength = signal->theData[1];
+ for (Uint32 i = 0; i < 23; i++)
+ Tdata[i] = signal->theData[i + 2];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::WAIT_LCPHOLDOP);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN THIS
+ * REFERENCE WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A SYSTEM RESTART.
+ * ----------------------------------------------------------------------- */
+ tnoHoldops = Tlength & 65535;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ ndbrequire(tnoHoldops <= 23);
+ for (Uint32 Tindex = 0; Tindex < tnoHoldops; Tindex++) {
+ jam();
+ tcConnectptr.i = Tdata[Tindex];
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ moveActiveToAcc(signal);
+ }//for
+ if ((Tlength >> 16) == 1) {
+ jam();
+ /* MORE HOLDOPS NEEDED */
+ signal->theData[0] = lcpPtr.p->lcpAccptr;
+ signal->theData[1] = lcpLocptr.p->locFragid;
+ signal->theData[2] = 1;
+ signal->theData[3] = lcpLocptr.i;
+ sendSignal(fragptr.p->accBlockref, GSN_LCP_HOLDOPREQ, signal, 4, JBA);
+ return;
+ } else {
+ jam();
+
+ /* NO MORE HOLDOPS NEEDED */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::HOLDOP_READY;
+ checkLcpHoldop(signal);
+
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_WAIT_ACTIVE_FINISH) {
+ if (fragptr.p->activeList == RNIL) {
+ jam();
+ /* ------------------------------------------------------------------
+ * THERE ARE NO MORE ACTIVE OPERATIONS. IT IS NOW OK TO START THE
+ * LOCAL CHECKPOINT IN BOTH TUP AND ACC.
+ * ----------------------------------------------------------------- */
+ sendStartLcp(signal);
+ lcpPtr.p->lcpState = LcpRecord::LCP_START_CHKP;
+ } else {
+ jam();
+ // Set this to signal releaseActiveFrag
+ // that it should check to see if itäs time to call sendStartLcp
+ fragptr.p->lcpRef = lcpPtr.i;
+ }//if
+ }//if
+ }//if
+
+ /* ----------------------- */
+ /* ELSE */
+ /* ------------------------------------------------------------------------
+ * THERE ARE STILL MORE ACTIVE OPERATIONS. WAIT UNTIL THEY ARE FINSIHED.
+ * THIS IS DISCOVERED WHEN RELEASE_ACTIVE_FRAG IS EXECUTED.
+ * ------------------------------------------------------------------------
+ * DO NOTHING, EXIT IS EXECUTED BELOW
+ * ----------------------------------------------------------------------- */
+ return;
+}//Dblqh::execLCP_HOLDOPCONF()
+
+/* ***************> */
+/* LCP_HOLDOPREF > */
+/* ***************> */
+void Dblqh::execLCP_HOLDOPREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execLCP_HOLDOPREF()
+
+/* ************************************************************************>>
+ * ACC_LCPSTARTED: Confirm that ACC started local checkpoint and undo
+ * logging is on.
+ * ************************************************************************>>
+ * --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = ACC_WAIT_STARTED
+ * ------------------------------------------------------------------------- */
+void Dblqh::execACC_LCPSTARTED(Signal* signal)
+{
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_WAIT_STARTED);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN THIS
+ * REFERENCE WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A SYSTEM RESTART.
+ * ----------------------------------------------------------------------- */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::ACC_STARTED;
+ lcpStartedLab(signal);
+ return;
+}//Dblqh::execACC_LCPSTARTED()
+
+/* ******************************************> */
+/* TUP_LCPSTARTED: Same as above but for TUP. */
+/* ******************************************> */
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = TUP_WAIT_STARTED
+ * ------------------------------------------------------------------------- */
+void Dblqh::execTUP_LCPSTARTED(Signal* signal)
+{
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::TUP_WAIT_STARTED);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN THIS REFERENCE
+ * WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A SYSTEM RESTART.
+ * ----------------------------------------------------------------------- */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::TUP_STARTED;
+ lcpStartedLab(signal);
+ return;
+}//Dblqh::execTUP_LCPSTARTED()
+
+void Dblqh::lcpStartedLab(Signal* signal)
+{
+ checkLcpStarted(signal);
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_STARTED) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THE LOCAL CHECKPOINT HAS BEEN STARTED. IT IS NOW TIME TO
+ * RESTART THE TRANSACTIONS WHICH HAVE BEEN BLOCKED.
+ * --------------------------------------------------------------------- */
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ /* ----------------------------------------------------------------------
+ * UPDATE THE MAX_GCI_IN_LCP AND MAX_GCI_COMPLETED_IN_LCP NOW BEFORE
+ * ACTIVATING THE FRAGMENT AGAIN.
+ * --------------------------------------------------------------------- */
+ ndbrequire(lcpPtr.p->currentFragment.lcpFragOrd.lcpNo < MAX_LCP_STORED);
+ fragptr.p->maxGciInLcp = fragptr.p->newestGci;
+ fragptr.p->maxGciCompletedInLcp = cnewestCompletedGci;
+ sendAccContOp(signal); /* START OPERATIONS IN ACC */
+ moveAccActiveFrag(signal); /* MOVE FROM ACC BLOCKED LIST TO ACTIVE LIST
+ ON FRAGMENT */
+ }
+ /*---------------*/
+ /* ELSE */
+ /*-------------------------------------------------------------------------*/
+ /* THE LOCAL CHECKPOINT HAS NOT BEEN STARTED. EXIT AND WAIT FOR
+ * MORE SIGNALS */
+ /*-------------------------------------------------------------------------*/
+ /* DO NOTHING, EXIT IS EXECUTED BELOW */
+ /*-------------------------------------------------------------------------*/
+ return;
+}//Dblqh::lcpStartedLab()
+
+/*---------------------------------------------------------------------------
+ * ACC HAVE RESTARTED THE BLOCKED OPERATIONS AGAIN IN ONE FRAGMENT PART.
+ * IT IS NOW OUR TURN TO RESTART ALL OPERATIONS QUEUED IN LQH IF ALL
+ * FRAGMENT PARTS ARE COMPLETED.
+ *-------------------------------------------------------------------------- */
+void Dblqh::execACC_CONTOPCONF(Signal* signal)
+{
+ if(ERROR_INSERTED(5035) && signal->getSendersBlockRef() != reference()){
+ sendSignalWithDelay(reference(), GSN_ACC_CONTOPCONF, signal, 1000,
+ signal->length());
+ return;
+ }
+
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ lcpLocptr.p->accContCounter = 1;
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ lcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ do {
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (lcpLocptr.p->accContCounter == 0) {
+ jam();
+ return;
+ }//if
+ lcpLocptr.i = lcpLocptr.p->nextLcpLoc;
+ } while (lcpLocptr.i != RNIL);
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ restartOperationsLab(signal);
+ return;
+}//Dblqh::execACC_CONTOPCONF()
+
+/* ********************************************************* */
+/* LQH_RESTART_OP: Restart operations after beeing blocked. */
+/* ********************************************************* */
+/*---------------------------------------------------------------------------*/
+/* PRECONDITION: FRAG_STATUS = BLOCKED AND LCP_STATE = STARTED */
+/*---------------------------------------------------------------------------*/
+void Dblqh::execLQH_RESTART_OP(Signal* signal)
+{
+ jamEntry();
+ fragptr.i = signal->theData[0];
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+
+ lcpPtr.i = signal->theData[1];
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ ndbrequire(fragptr.p->fragStatus == Fragrecord::BLOCKED);
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_STARTED) {
+ jam();
+ /***********************************************************************/
+ /* THIS SIGNAL CAN ONLY BE RECEIVED WHEN FRAGMENT IS BLOCKED AND
+ * THE LOCAL CHECKPOINT HAS BEEN STARTED. THE BLOCKING WILL BE
+ * REMOVED AS SOON AS ALL OPERATIONS HAVE BEEN STARTED.
+ ***********************************************************************/
+ restartOperationsLab(signal);
+ } else if (lcpPtr.p->lcpState == LcpRecord::LCP_BLOCKED_COMP) {
+ jam();
+ /*******************************************************************>
+ * THE CHECKPOINT IS COMPLETED BUT HAS NOT YET STARTED UP
+ * ALL OPERATIONS AGAIN.
+ * WE PERFORM THIS START-UP BEFORE CONTINUING WITH THE NEXT
+ * FRAGMENT OF THE LOCAL CHECKPOINT TO AVOID ANY STRANGE ERRORS.
+ *******************************************************************> */
+ restartOperationsLab(signal);
+ } else {
+ ndbrequire(false);
+ }
+}//Dblqh::execLQH_RESTART_OP()
+
+void Dblqh::restartOperationsLab(Signal* signal)
+{
+ Uint32 loopCount = 0;
+ tcConnectptr.i = fragptr.p->firstWaitQueue;
+ do {
+ if (tcConnectptr.i != RNIL) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* START UP THE TRANSACTION AGAIN. WE START IT AS A SEPARATE SIGNAL. */
+/*---------------------------------------------------------------------------*/
+ signal->theData[0] = ZRESTART_OPERATIONS_AFTER_STOP;
+ signal->theData[1] = tcConnectptr.i;
+ signal->theData[2] = fragptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ tcConnectptr.i = tcConnectptr.p->nextTc;
+ } else {
+ jam();
+/*--------------------------------------------------------------------------*/
+/* NO MORE OPERATIONS TO RESTART. WE CAN NOW RESET THE STATE TO ACTIVE AND */
+/* RESTART NORMAL ACTIVITIES ON THE FRAGMENT WHILE THE FUZZY PART OF THE */
+/* LOCAL CHECKPOINT IS COMPLETING. */
+/* IF THE CHECKPOINT WAS COMPLETED ALREADY ON THIS FRAGMENT WE PROCEED WITH */
+/* THE NEXT FRAGMENT NOW THAT WE HAVE COMPLETED THIS CHECKPOINT. */
+/*--------------------------------------------------------------------------*/
+ fragptr.p->fragStatus = Fragrecord::FSACTIVE;
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_BLOCKED_COMP) {
+ jam();
+ contChkpNextFragLab(signal);
+ return;
+ }//if
+ return;
+ }//if
+ loopCount++;
+ if (loopCount > 16) {
+ jam();
+ signal->theData[0] = fragptr.i;
+ signal->theData[1] = lcpPtr.i;
+ sendSignal(cownref, GSN_LQH_RESTART_OP, signal, 2, JBB);
+ return;
+ }//if
+ } while (1);
+}//Dblqh::restartOperationsLab()
+
+void Dblqh::restartOperationsAfterStopLab(Signal* signal)
+{
+ /*-------------------------------------------------------------------------
+ * WHEN ARRIVING HERE THE OPERATION IS ALREADY SET IN THE ACTIVE LIST.
+ * THUS WE CAN IMMEDIATELY CALL THE METHODS THAT EXECUTE FROM WHERE
+ * THE OPERATION WAS STOPPED.
+ *------------------------------------------------------------------------ */
+ switch (tcConnectptr.p->transactionState) {
+ case TcConnectionrec::STOPPED:
+ jam();
+ /*-----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND ACCKEYREQ
+ *---------------------------------------------------------------------- */
+ prepareContinueAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::COMMIT_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND ACC_COMMITREQ
+ * --------------------------------------------------------------------- */
+ releaseActiveFrag(signal);
+ commitContinueAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::ABORT_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND ACC_ABORTREQ
+ * --------------------------------------------------------------------- */
+ abortContinueAfterBlockedLab(signal, true);
+ return;
+ break;
+ case TcConnectionrec::COPY_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING COPY FRAGMENT
+ * --------------------------------------------------------------------- */
+ continueCopyAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::COPY_FIRST_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING COPY FRAGMENT
+ * --------------------------------------------------------------------- */
+ continueFirstCopyAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::SCAN_FIRST_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING SCAN
+ * --------------------------------------------------------------------- */
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ continueFirstScanAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::SCAN_CHECK_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING SCAN
+ * --------------------------------------------------------------------- */
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ continueAfterCheckLcpStopBlocked(signal);
+ return;
+ break;
+ case TcConnectionrec::SCAN_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING SCAN
+ * --------------------------------------------------------------------- */
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ continueScanAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::SCAN_RELEASE_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING RELEASE
+ * LOCKS IN SCAN
+ * --------------------------------------------------------------------- */
+ tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED;
+ continueScanReleaseAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::SCAN_CLOSE_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING CLOSE OF SCAN
+ * --------------------------------------------------------------------- */
+ continueCloseScanAfterBlockedLab(signal);
+ return;
+ break;
+ case TcConnectionrec::COPY_CLOSE_STOPPED:
+ jam();
+ /* ----------------------------------------------------------------------
+ * STOPPED BEFORE TRYING TO SEND NEXT_SCANREQ DURING CLOSE OF COPY
+ * --------------------------------------------------------------------- */
+ continueCloseCopyAfterBlockedLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::restartOperationsAfterStopLab()
+
+/* *************** */
+/* ACC_LCPCONF > */
+/* *************** */
+/*---------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = ACC_STARTED
+ *-------------------------------------------------------------------------- */
+void Dblqh::execACC_LCPCONF(Signal* signal)
+{
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_STARTED);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN
+ * THIS REFERENCE WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A
+ * SYSTEM RESTART.
+ * ----------------------------------------------------------------------- */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::ACC_COMPLETED;
+ lcpCompletedLab(signal);
+ return;
+}//Dblqh::execACC_LCPCONF()
+
+/* *************** */
+/* TUP_LCPCONF > */
+/* *************** */
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = TUP_STARTED
+ * ------------------------------------------------------------------------- */
+void Dblqh::execTUP_LCPCONF(Signal* signal)
+{
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::TUP_STARTED);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN THIS
+ * REFERENCE WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A SYSTEM RESTART.
+ * ----------------------------------------------------------------------- */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::TUP_COMPLETED;
+ lcpCompletedLab(signal);
+ return;
+}//Dblqh::execTUP_LCPCONF()
+
+void Dblqh::lcpCompletedLab(Signal* signal)
+{
+ checkLcpCompleted(signal);
+ if (lcpPtr.p->lcpState != LcpRecord::LCP_COMPLETED) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THE LOCAL CHECKPOINT HAS NOT BEEN COMPLETED, EXIT & WAIT
+ * FOR MORE SIGNALS
+ * --------------------------------------------------------------------- */
+ return;
+ }//if
+ /* ------------------------------------------------------------------------
+ * THE LOCAL CHECKPOINT HAS BEEN COMPLETED. IT IS NOW TIME TO START
+ * A LOCAL CHECKPOINT ON THE NEXT FRAGMENT OR COMPLETE THIS LCP ROUND.
+ * ------------------------------------------------------------------------
+ * WE START BY SENDING LCP_REPORT TO DIH TO REPORT THE COMPLETED LCP.
+ * TO CATER FOR NODE CRASHES WE SEND IT IN PARALLEL TO ALL NODES.
+ * ----------------------------------------------------------------------- */
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->fragActiveStatus = ZFALSE;
+
+ contChkpNextFragLab(signal);
+ return;
+}//Dblqh::lcpCompletedLab()
+
+void
+Dblqh::sendLCP_FRAG_REP(Signal * signal,
+ const LcpRecord::FragOrd & fragOrd) const {
+
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragOrd.fragPtrI;
+ ptrCheckGuard(fragPtr, cfragrecFileSize, fragrecord);
+
+ ndbrequire(fragOrd.lcpFragOrd.lcpNo < MAX_LCP_STORED);
+ LcpFragRep * const lcpReport = (LcpFragRep *)&signal->theData[0];
+ lcpReport->nodeId = cownNodeid;
+ lcpReport->lcpId = fragOrd.lcpFragOrd.lcpId;
+ lcpReport->lcpNo = fragOrd.lcpFragOrd.lcpNo;
+ lcpReport->tableId = fragOrd.lcpFragOrd.tableId;
+ lcpReport->fragId = fragOrd.lcpFragOrd.fragmentId;
+ lcpReport->maxGciCompleted = fragPtr.p->maxGciCompletedInLcp;
+ lcpReport->maxGciStarted = fragPtr.p->maxGciInLcp;
+
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ Uint32 nodeId = cnodeData[i];
+ if(cnodeStatus[i] == ZNODE_UP){
+ jam();
+ BlockReference Tblockref = calcDihBlockRef(nodeId);
+ sendSignal(Tblockref, GSN_LCP_FRAG_REP, signal,
+ LcpFragRep::SignalLength, JBB);
+ }//if
+ }//for
+}
+
+void Dblqh::contChkpNextFragLab(Signal* signal)
+{
+ /* ------------------------------------------------------------------------
+ * UPDATE THE LATEST LOCAL CHECKPOINT COMPLETED ON FRAGMENT.
+ * UPDATE THE LCP_ID OF THIS CHECKPOINT.
+ * REMOVE THE LINK BETWEEN THE FRAGMENT RECORD AND THE LCP RECORD.
+ * ----------------------------------------------------------------------- */
+ if (fragptr.p->fragStatus == Fragrecord::BLOCKED) {
+ jam();
+ /**
+ * LCP of fragment complete
+ * but restarting of operations isn't
+ */
+ lcpPtr.p->lcpState = LcpRecord::LCP_BLOCKED_COMP;
+ //restartOperationsLab(signal);
+ return;
+ }//if
+
+ /**
+ * Send rep when fragment is done + unblocked
+ */
+ sendLCP_FRAG_REP(signal, lcpPtr.p->currentFragment);
+
+ /* ------------------------------------------------------------------------
+ * WE ALSO RELEASE THE LOCAL LCP RECORDS.
+ * ----------------------------------------------------------------------- */
+ releaseLocalLcps(signal);
+ if (lcpPtr.p->lcpQueued) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * Transfer the state from the queued to the active LCP.
+ * --------------------------------------------------------------------- */
+ lcpPtr.p->lcpQueued = false;
+ lcpPtr.p->currentFragment = lcpPtr.p->queuedFragment;
+
+ /* ----------------------------------------------------------------------
+ * START THE QUEUED LOCAL CHECKPOINT.
+ * --------------------------------------------------------------------- */
+ sendLCP_FRAGIDREQ(signal);
+ return;
+ }//if
+
+ lcpPtr.p->lcpState = LcpRecord::LCP_IDLE;
+ if (lcpPtr.p->lastFragmentFlag){
+ jam();
+ /* ----------------------------------------------------------------------
+ * NOW THE COMPLETE LOCAL CHECKPOINT ROUND IS COMPLETED.
+ * --------------------------------------------------------------------- */
+ completeLcpRoundLab(signal);
+ return;
+ }//if
+
+ if (lcpPtr.p->reportEmpty) {
+ jam();
+ sendEMPTY_LCP_CONF(signal, false);
+ }//if
+ return;
+}//Dblqh::contChkpNextFragLab()
+
+void Dblqh::sendLCP_FRAGIDREQ(Signal* signal)
+{
+ ndbrequire(lcpPtr.p->firstLcpLocTup == RNIL);
+ ndbrequire(lcpPtr.p->firstLcpLocAcc == RNIL);
+
+ TablerecPtr tabPtr;
+ tabPtr.i = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ ptrAss(tabPtr, tablerec);
+ if(tabPtr.p->tableStatus == Tablerec::PREP_DROP_TABLE_ONGOING ||
+ tabPtr.p->tableStatus == Tablerec::PREP_DROP_TABLE_DONE){
+ jam();
+ /**
+ * Fake that the fragment is done
+ */
+ lcpCompletedLab(signal);
+ return;
+ }
+
+ ndbrequire(tabPtr.p->tableStatus == Tablerec::TABLE_DEFINED);
+
+ lcpPtr.p->lcpState = LcpRecord::LCP_WAIT_FRAGID;
+ signal->theData[0] = lcpPtr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lcpPtr.p->currentFragment.lcpFragOrd.lcpNo;
+ signal->theData[3] = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ signal->theData[4] = lcpPtr.p->currentFragment.lcpFragOrd.fragmentId;
+ signal->theData[5] = lcpPtr.p->currentFragment.lcpFragOrd.lcpId % MAX_LCP_STORED;
+ sendSignal(fragptr.p->accBlockref, GSN_LCP_FRAGIDREQ, signal, 6, JBB);
+}//Dblqh::sendLCP_FRAGIDREQ()
+
+void Dblqh::sendEMPTY_LCP_CONF(Signal* signal, bool idle)
+{
+
+ EmptyLcpConf * const rep = (EmptyLcpConf*)&signal->theData[0];
+ /* ----------------------------------------------------------------------
+ * We have been requested to report when there are no more local
+ * waiting to be started or ongoing. In this signal we also report
+ * the last completed fragments state.
+ * ---------------------------------------------------------------------- */
+ rep->senderNodeId = getOwnNodeId();
+ if(!idle){
+ jam();
+ rep->idle = 0 ;
+ rep->tableId = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ rep->fragmentId = lcpPtr.p->currentFragment.lcpFragOrd.fragmentId;
+ rep->lcpNo = lcpPtr.p->currentFragment.lcpFragOrd.lcpNo;
+ rep->lcpId = lcpPtr.p->currentFragment.lcpFragOrd.lcpId;
+ } else {
+ jam();
+ rep->idle = 1;
+ rep->tableId = ~0;
+ rep->fragmentId = ~0;
+ rep->lcpNo = ~0;
+ rep->lcpId = c_lcpId;
+ }
+
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ Uint32 nodeId = cnodeData[i];
+ if (lcpPtr.p->m_EMPTY_LCP_REQ.get(nodeId)) {
+ jam();
+
+ BlockReference blockref = calcDihBlockRef(nodeId);
+ sendSignal(blockref, GSN_EMPTY_LCP_CONF, signal,
+ EmptyLcpConf::SignalLength, JBB);
+ }//if
+ }//for
+
+ lcpPtr.p->reportEmpty = false;
+ lcpPtr.p->m_EMPTY_LCP_REQ.clear();
+}//Dblqh::sendEMPTY_LCPCONF()
+
+void Dblqh::execACC_LCPREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execACC_LCPREF()
+
+void Dblqh::execTUP_LCPREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execTUP_LCPREF()
+
+/* --------------------------------------------------------------------------
+ * THE LOCAL CHECKPOINT ROUND IS NOW COMPLETED. SEND COMPLETED MESSAGE
+ * TO THE MASTER DIH.
+ * ------------------------------------------------------------------------- */
+void Dblqh::completeLcpRoundLab(Signal* signal)
+{
+ clcpCompletedState = LCP_CLOSE_STARTED;
+ signal->theData[0] = caccBlockref;
+ signal->theData[1] = cownref;
+ sendSignal(caccBlockref, GSN_END_LCPREQ, signal, 2, JBB);
+ signal->theData[0] = ctupBlockref;
+ signal->theData[1] = cownref;
+ sendSignal(ctupBlockref, GSN_END_LCPREQ, signal, 2, JBB);
+ return;
+}//Dblqh::completeLcpRoundLab()
+
+void Dblqh::execEND_LCPCONF(Signal* signal)
+{
+ jamEntry();
+ BlockReference userpointer = signal->theData[0];
+ if (userpointer == caccBlockref) {
+ if (clcpCompletedState == LCP_CLOSE_STARTED) {
+ jam();
+ clcpCompletedState = ACC_LCP_CLOSE_COMPLETED;
+ return;
+ } else {
+ jam();
+ ndbrequire(clcpCompletedState == TUP_LCP_CLOSE_COMPLETED);
+ clcpCompletedState = LCP_IDLE;
+ }//if
+ } else {
+ ndbrequire(userpointer == ctupBlockref);
+ if (clcpCompletedState == LCP_CLOSE_STARTED) {
+ jam();
+ clcpCompletedState = TUP_LCP_CLOSE_COMPLETED;
+ return;
+ } else {
+ jam();
+ ndbrequire(clcpCompletedState == ACC_LCP_CLOSE_COMPLETED);
+ clcpCompletedState = LCP_IDLE;
+ }//if
+ }//if
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+ sendLCP_COMPLETE_REP(signal, lcpPtr.p->currentFragment.lcpFragOrd.lcpId);
+}//Dblqh::execEND_LCPCONF()
+
+void Dblqh::sendLCP_COMPLETE_REP(Signal* signal, Uint32 lcpId)
+{
+ cnoOfFragsCheckpointed = 0;
+ ndbrequire((cnoOfNodes - 1) < (MAX_NDB_NODES - 1));
+ /* ------------------------------------------------------------------------
+ * WE SEND COMP_LCP_ROUND TO ALL NODES TO PREPARE FOR NODE CRASHES.
+ * ----------------------------------------------------------------------- */
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+ lcpPtr.p->lastFragmentFlag = false;
+
+ LcpCompleteRep* rep = (LcpCompleteRep*)signal->getDataPtrSend();
+ rep->nodeId = getOwnNodeId();
+ rep->lcpId = lcpId;
+ rep->blockNo = DBLQH;
+
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ Uint32 nodeId = cnodeData[i];
+ if(cnodeStatus[i] == ZNODE_UP){
+ jam();
+
+ BlockReference blockref = calcDihBlockRef(nodeId);
+ sendSignal(blockref, GSN_LCP_COMPLETE_REP, signal,
+ LcpCompleteRep::SignalLength, JBB);
+ }//if
+ }//for
+
+ if(lcpPtr.p->reportEmpty){
+ jam();
+ sendEMPTY_LCP_CONF(signal, true);
+ }
+ return;
+}//Dblqh::sendCOMP_LCP_ROUND()
+
+/* ==========================================================================
+ * ======= CHECK IF ALL PARTS OF A LOCAL CHECKPOINT ARE COMPLETED =======
+ *
+ * SUBROUTINE SHORT NAME = CLC
+ * ========================================================================= */
+void Dblqh::checkLcpCompleted(Signal* signal)
+{
+ LcpLocRecordPtr clcLcpLocptr;
+
+ clcLcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ while (clcLcpLocptr.i != RNIL) {
+ ptrCheckGuard(clcLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (clcLcpLocptr.p->lcpLocstate != LcpLocRecord::ACC_COMPLETED) {
+ jam();
+ ndbrequire((clcLcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_WAIT_STARTED) ||
+ (clcLcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_STARTED));
+ return;
+ }//if
+ clcLcpLocptr.i = clcLcpLocptr.p->nextLcpLoc;
+ }
+
+ clcLcpLocptr.i = lcpPtr.p->firstLcpLocTup;
+ while (clcLcpLocptr.i != RNIL){
+ ptrCheckGuard(clcLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (clcLcpLocptr.p->lcpLocstate != LcpLocRecord::TUP_COMPLETED) {
+ jam();
+ ndbrequire((clcLcpLocptr.p->lcpLocstate==LcpLocRecord::TUP_WAIT_STARTED)
+ ||(clcLcpLocptr.p->lcpLocstate == LcpLocRecord::TUP_STARTED));
+ return;
+ }//if
+ clcLcpLocptr.i = clcLcpLocptr.p->nextLcpLoc;
+ }
+
+ lcpPtr.p->lcpState = LcpRecord::LCP_COMPLETED;
+}//Dblqh::checkLcpCompleted()
+
+/* ==========================================================================
+ * ======= CHECK IF ALL HOLD OPERATIONS ARE COMPLETED =======
+ *
+ * SUBROUTINE SHORT NAME = CHO
+ * ========================================================================= */
+void Dblqh::checkLcpHoldop(Signal* signal)
+{
+ LcpLocRecordPtr choLcpLocptr;
+
+ choLcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ do {
+ ptrCheckGuard(choLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (choLcpLocptr.p->lcpLocstate != LcpLocRecord::HOLDOP_READY) {
+ ndbrequire(choLcpLocptr.p->lcpLocstate == LcpLocRecord::WAIT_LCPHOLDOP);
+ return;
+ }//if
+ choLcpLocptr.i = choLcpLocptr.p->nextLcpLoc;
+ } while (choLcpLocptr.i != RNIL);
+ lcpPtr.p->lcpState = LcpRecord::LCP_WAIT_ACTIVE_FINISH;
+}//Dblqh::checkLcpHoldop()
+
+/* ==========================================================================
+ * ======= CHECK IF ALL PARTS OF A LOCAL CHECKPOINT ARE STARTED =======
+ *
+ * SUBROUTINE SHORT NAME = CLS
+ * ========================================================================== */
+void Dblqh::checkLcpStarted(Signal* signal)
+{
+ LcpLocRecordPtr clsLcpLocptr;
+
+ terrorCode = ZOK;
+ clsLcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ int i = 0;
+ do {
+ ptrCheckGuard(clsLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (clsLcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_WAIT_STARTED){
+ return;
+ }//if
+ clsLcpLocptr.i = clsLcpLocptr.p->nextLcpLoc;
+ i++;
+ } while (clsLcpLocptr.i != RNIL);
+
+ i = 0;
+ clsLcpLocptr.i = lcpPtr.p->firstLcpLocTup;
+ do {
+ ptrCheckGuard(clsLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (clsLcpLocptr.p->lcpLocstate == LcpLocRecord::TUP_WAIT_STARTED){
+ return;
+ }//if
+ clsLcpLocptr.i = clsLcpLocptr.p->nextLcpLoc;
+ i++;
+ } while (clsLcpLocptr.i != RNIL);
+ lcpPtr.p->lcpState = LcpRecord::LCP_STARTED;
+}//Dblqh::checkLcpStarted()
+
+/* ==========================================================================
+ * ======= CHECK IF ALL PREPARE TUP OPERATIONS ARE COMPLETED =======
+ *
+ * SUBROUTINE SHORT NAME = CLT
+ * ========================================================================== */
+void Dblqh::checkLcpTupprep(Signal* signal)
+{
+ LcpLocRecordPtr cltLcpLocptr;
+ cltLcpLocptr.i = lcpPtr.p->firstLcpLocTup;
+ do {
+ ptrCheckGuard(cltLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (cltLcpLocptr.p->lcpLocstate != LcpLocRecord::IDLE) {
+ ndbrequire(cltLcpLocptr.p->lcpLocstate == LcpLocRecord::WAIT_TUP_PREPLCP);
+ return;
+ }//if
+ cltLcpLocptr.i = cltLcpLocptr.p->nextLcpLoc;
+ } while (cltLcpLocptr.i != RNIL);
+ lcpPtr.p->lcpState = LcpRecord::LCP_WAIT_HOLDOPS;
+}//Dblqh::checkLcpTupprep()
+
+/* ==========================================================================
+ * ======= INITIATE LCP LOCAL RECORD USED TOWARDS ACC =======
+ *
+ * ========================================================================== */
+void Dblqh::initLcpLocAcc(Signal* signal, Uint32 fragId)
+{
+ lcpLocptr.p->nextLcpLoc = lcpPtr.p->firstLcpLocAcc;
+ lcpPtr.p->firstLcpLocAcc = lcpLocptr.i;
+ lcpLocptr.p->locFragid = fragId;
+ lcpLocptr.p->waitingBlock = LcpLocRecord::ACC;
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::IDLE;
+ lcpLocptr.p->masterLcpRec = lcpPtr.i;
+ lcpLocptr.p->tupRef = RNIL;
+}//Dblqh::initLcpLocAcc()
+
+/* ==========================================================================
+ * ======= INITIATE LCP LOCAL RECORD USED TOWARDS TUP =======
+ *
+ * ========================================================================== */
+void Dblqh::initLcpLocTup(Signal* signal, Uint32 fragId)
+{
+ lcpLocptr.p->nextLcpLoc = lcpPtr.p->firstLcpLocTup;
+ lcpPtr.p->firstLcpLocTup = lcpLocptr.i;
+ lcpLocptr.p->locFragid = fragId;
+ lcpLocptr.p->waitingBlock = LcpLocRecord::TUP;
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::WAIT_TUP_PREPLCP;
+ lcpLocptr.p->masterLcpRec = lcpPtr.i;
+ lcpLocptr.p->tupRef = RNIL;
+}//Dblqh::initLcpLocTup()
+
+/* --------------------------------------------------------------------------
+ * ------- MOVE OPERATION FROM ACC WAITING LIST ON FRAGMENT -------
+ * ------- TO ACTIVE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME = MAA
+ * -------------------------------------------------------------------------- */
+void Dblqh::moveAccActiveFrag(Signal* signal)
+{
+ UintR maaTcNextConnectptr;
+
+ tcConnectptr.i = fragptr.p->accBlockedList;
+ fragptr.p->accBlockedList = RNIL;
+ /* ------------------------------------------------------------------------
+ * WE WILL MOVE ALL RECORDS FROM THE ACC BLOCKED LIST AT ONCE.
+ * ------------------------------------------------------------------------ */
+ while (tcConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ maaTcNextConnectptr = tcConnectptr.p->nextTc;
+ ndbrequire(tcConnectptr.p->listState == TcConnectionrec::ACC_BLOCK_LIST);
+ tcConnectptr.p->listState = TcConnectionrec::NOT_IN_LIST;
+ linkActiveFrag(signal);
+ tcConnectptr.i = maaTcNextConnectptr;
+ }//while
+}//Dblqh::moveAccActiveFrag()
+
+/* --------------------------------------------------------------------------
+ * ------- MOVE OPERATION FROM ACTIVE LIST ON FRAGMENT -------
+ * ------- TO ACC BLOCKED LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME = MAT
+ * -------------------------------------------------------------------------- */
+void Dblqh::moveActiveToAcc(Signal* signal)
+{
+ TcConnectionrecPtr matTcNextConnectptr;
+
+ releaseActiveList(signal);
+ /* ------------------------------------------------------------------------
+ * PUT OPERATION RECORD FIRST IN ACC BLOCKED LIST.
+ * ------------------------------------------------------------------------ */
+ matTcNextConnectptr.i = fragptr.p->accBlockedList;
+ tcConnectptr.p->nextTc = matTcNextConnectptr.i;
+ tcConnectptr.p->prevTc = RNIL;
+ tcConnectptr.p->listState = TcConnectionrec::ACC_BLOCK_LIST;
+ fragptr.p->accBlockedList = tcConnectptr.i;
+ if (matTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(matTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ matTcNextConnectptr.p->prevTc = tcConnectptr.i;
+ }//if
+}//Dblqh::moveActiveToAcc()
+
+/* ------------------------------------------------------------------------- */
+/* ---- RELEASE LOCAL LCP RECORDS AFTER COMPLETION OF A LOCAL CHECKPOINT---- */
+/* */
+/* SUBROUTINE SHORT NAME = RLL */
+/* ------------------------------------------------------------------------- */
+void Dblqh::releaseLocalLcps(Signal* signal)
+{
+ lcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ while (lcpLocptr.i != RNIL){
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ Uint32 tmp = lcpLocptr.p->nextLcpLoc;
+ releaseLcpLoc(signal);
+ lcpLocptr.i = tmp;
+ }
+ lcpPtr.p->firstLcpLocAcc = RNIL;
+
+ lcpLocptr.i = lcpPtr.p->firstLcpLocTup;
+ while (lcpLocptr.i != RNIL){
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ Uint32 tmp = lcpLocptr.p->nextLcpLoc;
+ releaseLcpLoc(signal);
+ lcpLocptr.i = tmp;
+ }
+ lcpPtr.p->firstLcpLocTup = RNIL;
+
+}//Dblqh::releaseLocalLcps()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEIZE LCP LOCAL RECORD ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::seizeLcpLoc(Signal* signal)
+{
+ lcpLocptr.i = cfirstfreeLcpLoc;
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ cfirstfreeLcpLoc = lcpLocptr.p->nextLcpLoc;
+ lcpLocptr.p->nextLcpLoc = RNIL;
+}//Dblqh::seizeLcpLoc()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEND ACC_CONT_OP ------- */
+/* */
+/* INPUT: LCP_PTR LOCAL CHECKPOINT RECORD */
+/* FRAGPTR FRAGMENT RECORD */
+/* */
+/* SUBROUTINE SHORT NAME = SAC */
+/* ------------------------------------------------------------------------- */
+void Dblqh::sendAccContOp(Signal* signal)
+{
+ LcpLocRecordPtr sacLcpLocptr;
+
+ int count = 0;
+ sacLcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ do {
+ ptrCheckGuard(sacLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ sacLcpLocptr.p->accContCounter = 0;
+ if(sacLcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_STARTED){
+ /* ------------------------------------------------------------------- */
+ /*SEND START OPERATIONS TO ACC AGAIN */
+ /* ------------------------------------------------------------------- */
+ signal->theData[0] = lcpPtr.p->lcpAccptr;
+ signal->theData[1] = sacLcpLocptr.p->locFragid;
+ sendSignal(fragptr.p->accBlockref, GSN_ACC_CONTOPREQ, signal, 2, JBA);
+ count++;
+ } else if(sacLcpLocptr.p->lcpLocstate == LcpLocRecord::ACC_COMPLETED){
+ signal->theData[0] = sacLcpLocptr.i;
+ sendSignal(reference(), GSN_ACC_CONTOPCONF, signal, 1, JBB);
+ } else {
+ ndbrequire(false);
+ }
+ sacLcpLocptr.i = sacLcpLocptr.p->nextLcpLoc;
+ } while (sacLcpLocptr.i != RNIL);
+
+}//Dblqh::sendAccContOp()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEND ACC_LCPREQ AND TUP_LCPREQ ------- */
+/* */
+/* INPUT: LCP_PTR LOCAL CHECKPOINT RECORD */
+/* FRAGPTR FRAGMENT RECORD */
+/* SUBROUTINE SHORT NAME = STL */
+/* ------------------------------------------------------------------------- */
+void Dblqh::sendStartLcp(Signal* signal)
+{
+ LcpLocRecordPtr stlLcpLocptr;
+ stlLcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+ do {
+ jam();
+ ptrCheckGuard(stlLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ stlLcpLocptr.p->lcpLocstate = LcpLocRecord::ACC_WAIT_STARTED;
+ signal->theData[0] = lcpPtr.p->lcpAccptr;
+ signal->theData[1] = stlLcpLocptr.i;
+ signal->theData[2] = stlLcpLocptr.p->locFragid;
+ sendSignal(fragptr.p->accBlockref, GSN_ACC_LCPREQ, signal, 3, JBA);
+ stlLcpLocptr.i = stlLcpLocptr.p->nextLcpLoc;
+ } while (stlLcpLocptr.i != RNIL);
+
+ stlLcpLocptr.i = lcpPtr.p->firstLcpLocTup;
+ do {
+ jam();
+ ptrCheckGuard(stlLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ stlLcpLocptr.p->lcpLocstate = LcpLocRecord::TUP_WAIT_STARTED;
+ signal->theData[0] = stlLcpLocptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = stlLcpLocptr.p->tupRef;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUP_LCPREQ, signal, 3, JBA);
+ stlLcpLocptr.i = stlLcpLocptr.p->nextLcpLoc;
+ } while (stlLcpLocptr.i != RNIL);
+}//Dblqh::sendStartLcp()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SET THE LOG TAIL IN THE LOG FILES ------- */
+/* */
+/*THIS SUBROUTINE HAVE BEEN BUGGY AND IS RATHER COMPLEX. IT IS IMPORTANT TO */
+/*REMEMBER THAT WE SEARCH FROM THE TAIL UNTIL WE REACH THE HEAD (CURRENT). */
+/*THE TAIL AND HEAD CAN BE ON THE SAME MBYTE. WE SEARCH UNTIL WE FIND A MBYTE*/
+/*THAT WE NEED TO KEEP. WE THEN SET THE TAIL TO BE THE PREVIOUS. IF WE DO */
+/*NOT FIND A MBYTE THAT WE NEED TO KEEP UNTIL WE REACH THE HEAD THEN WE USE */
+/*THE HEAD AS TAIL. FINALLY WE HAVE TO MOVE BACK THE TAIL TO ALSO INCLUDE */
+/*ALL PREPARE RECORDS. THIS MEANS THAT LONG-LIVED TRANSACTIONS ARE DANGEROUS */
+/*FOR SHORT LOGS. */
+/* ------------------------------------------------------------------------- */
+
+// this function has not been verified yet
+Uint32 Dblqh::remainingLogSize(const LogFileRecordPtr &sltCurrLogFilePtr,
+ const LogPartRecordPtr &sltLogPartPtr)
+{
+ Uint32 hf = sltCurrLogFilePtr.p->fileNo*ZNO_MBYTES_IN_FILE+sltCurrLogFilePtr.p->currentMbyte;
+ Uint32 tf = sltLogPartPtr.p->logTailFileNo*ZNO_MBYTES_IN_FILE+sltLogPartPtr.p->logTailMbyte;
+ Uint32 sz = sltLogPartPtr.p->noLogFiles*ZNO_MBYTES_IN_FILE;
+ if (tf > hf) hf += sz;
+ return sz-(hf-tf);
+}
+
+void Dblqh::setLogTail(Signal* signal, Uint32 keepGci)
+{
+ LogPartRecordPtr sltLogPartPtr;
+ LogFileRecordPtr sltLogFilePtr;
+#if 0
+ LogFileRecordPtr sltCurrLogFilePtr;
+#endif
+ UintR tsltMbyte;
+ UintR tsltStartMbyte;
+ UintR tsltIndex;
+ UintR tsltFlag;
+
+ for (sltLogPartPtr.i = 0; sltLogPartPtr.i < 4; sltLogPartPtr.i++) {
+ jam();
+ ptrAss(sltLogPartPtr, logPartRecord);
+ findLogfile(signal, sltLogPartPtr.p->logTailFileNo,
+ sltLogPartPtr, &sltLogFilePtr);
+
+#if 0
+ sltCurrLogFilePtr.i = sltLogPartPtr.p->currentLogfile;
+ ptrCheckGuard(sltCurrLogFilePtr, clogFileFileSize, logFileRecord);
+ infoEvent("setLogTail: Available log file %d size = %d[mbytes]+%d[words]", sltLogPartPtr.i,
+ remainingLogSize(sltCurrLogFilePtr, sltLogPartPtr), sltCurrLogFilePtr.p->remainingWordsInMbyte);
+#endif
+
+ tsltMbyte = sltLogPartPtr.p->logTailMbyte;
+ tsltStartMbyte = tsltMbyte;
+ tsltFlag = ZFALSE;
+ if (sltLogFilePtr.i == sltLogPartPtr.p->currentLogfile) {
+/* ------------------------------------------------------------------------- */
+/*THE LOG AND THE TAIL IS ALREADY IN THE SAME FILE. */
+/* ------------------------------------------------------------------------- */
+ if (sltLogFilePtr.p->currentMbyte >= sltLogPartPtr.p->logTailMbyte) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*THE CURRENT MBYTE IS AHEAD OF OR AT THE TAIL. THUS WE WILL ONLY LOOK FOR */
+/*THE TAIL UNTIL WE REACH THE CURRENT MBYTE WHICH IS IN THIS LOG FILE. */
+/*IF THE LOG TAIL IS AHEAD OF THE CURRENT MBYTE BUT IN THE SAME LOG FILE */
+/*THEN WE HAVE TO SEARCH THROUGH ALL FILES BEFORE WE COME TO THE CURRENT */
+/*MBYTE. WE ALWAYS STOP WHEN WE COME TO THE CURRENT MBYTE SINCE THE TAIL */
+/*CAN NEVER BE BEFORE THE HEAD. */
+/* ------------------------------------------------------------------------- */
+ tsltFlag = ZTRUE;
+ }//if
+ }//if
+
+/* ------------------------------------------------------------------------- */
+/*NOW START SEARCHING FOR THE NEW TAIL, STARTING AT THE CURRENT TAIL AND */
+/*PROCEEDING UNTIL WE FIND A MBYTE WHICH IS NEEDED TO KEEP OR UNTIL WE REACH */
+/*CURRENT MBYTE (THE HEAD). */
+/* ------------------------------------------------------------------------- */
+ SLT_LOOP:
+ for (tsltIndex = tsltStartMbyte;
+ tsltIndex <= ZNO_MBYTES_IN_FILE - 1;
+ tsltIndex++) {
+ if (sltLogFilePtr.p->logMaxGciStarted[tsltIndex] >= keepGci) {
+/* ------------------------------------------------------------------------- */
+/*WE ARE NOT ALLOWED TO STEP THE LOG ANY FURTHER AHEAD */
+/*SET THE NEW LOG TAIL AND CONTINUE WITH NEXT LOG PART. */
+/*THIS MBYTE IS NOT TO BE INCLUDED SO WE NEED TO STEP BACK ONE MBYTE. */
+/* ------------------------------------------------------------------------- */
+ if (tsltIndex != 0) {
+ jam();
+ tsltMbyte = tsltIndex - 1;
+ } else {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*STEPPING BACK INCLUDES ALSO STEPPING BACK TO THE PREVIOUS LOG FILE. */
+/* ------------------------------------------------------------------------- */
+ tsltMbyte = ZNO_MBYTES_IN_FILE - 1;
+ sltLogFilePtr.i = sltLogFilePtr.p->prevLogFile;
+ ptrCheckGuard(sltLogFilePtr, clogFileFileSize, logFileRecord);
+ }//if
+ goto SLT_BREAK;
+ } else {
+ jam();
+ if (tsltFlag == ZTRUE) {
+/* ------------------------------------------------------------------------- */
+/*WE ARE IN THE SAME FILE AS THE CURRENT MBYTE AND WE CAN REACH THE CURRENT */
+/*MBYTE BEFORE WE REACH A NEW TAIL. */
+/* ------------------------------------------------------------------------- */
+ if (tsltIndex == sltLogFilePtr.p->currentMbyte) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*THE TAIL OF THE LOG IS ACTUALLY WITHIN THE CURRENT MBYTE. THUS WE SET THE */
+/*LOG TAIL TO BE THE CURRENT MBYTE. */
+/* ------------------------------------------------------------------------- */
+ tsltMbyte = sltLogFilePtr.p->currentMbyte;
+ goto SLT_BREAK;
+ }//if
+ }//if
+ }//if
+ }//for
+ sltLogFilePtr.i = sltLogFilePtr.p->nextLogFile;
+ ptrCheckGuard(sltLogFilePtr, clogFileFileSize, logFileRecord);
+ if (sltLogFilePtr.i == sltLogPartPtr.p->currentLogfile) {
+ jam();
+ tsltFlag = ZTRUE;
+ }//if
+ tsltStartMbyte = 0;
+ goto SLT_LOOP;
+ SLT_BREAK:
+ jam();
+ {
+ UintR ToldTailFileNo = sltLogPartPtr.p->logTailFileNo;
+ UintR ToldTailMByte = sltLogPartPtr.p->logTailMbyte;
+
+ arrGuard(tsltMbyte, 16);
+ sltLogPartPtr.p->logTailFileNo =
+ sltLogFilePtr.p->logLastPrepRef[tsltMbyte] >> 16;
+/* ------------------------------------------------------------------------- */
+/*SINCE LOG_MAX_GCI_STARTED ONLY KEEP TRACK OF COMMIT LOG RECORDS WE ALSO */
+/*HAVE TO STEP BACK THE TAIL SO THAT WE INCLUDE ALL PREPARE RECORDS */
+/*NEEDED FOR THOSE COMMIT RECORDS IN THIS MBYTE. THIS IS A RATHER */
+/*CONSERVATIVE APPROACH BUT IT WORKS. */
+/* ------------------------------------------------------------------------- */
+ sltLogPartPtr.p->logTailMbyte =
+ sltLogFilePtr.p->logLastPrepRef[tsltMbyte] & 65535;
+ if ((ToldTailFileNo != sltLogPartPtr.p->logTailFileNo) ||
+ (ToldTailMByte != sltLogPartPtr.p->logTailMbyte)) {
+ jam();
+ if (sltLogPartPtr.p->logPartState == LogPartRecord::TAIL_PROBLEM) {
+ if (sltLogPartPtr.p->firstLogQueue == RNIL) {
+ jam();
+ sltLogPartPtr.p->logPartState = LogPartRecord::IDLE;
+ } else {
+ jam();
+ sltLogPartPtr.p->logPartState = LogPartRecord::ACTIVE;
+ }//if
+ }//if
+ }//if
+ }
+#if 0
+ infoEvent("setLogTail: Available log file %d size = %d[mbytes]+%d[words]", sltLogPartPtr.i,
+ remainingLogSize(sltCurrLogFilePtr, sltLogPartPtr), sltCurrLogFilePtr.p->remainingWordsInMbyte);
+#endif
+ }//for
+
+}//Dblqh::setLogTail()
+
+/* ######################################################################### */
+/* ####### GLOBAL CHECKPOINT MODULE ####### */
+/* */
+/* ######################################################################### */
+/*---------------------------------------------------------------------------*/
+/* THIS MODULE HELPS DIH IN DISCOVERING WHEN GLOBAL CHECKPOINTS ARE */
+/* RECOVERABLE. IT HANDLES THE REQUEST GCP_SAVEREQ THAT REQUESTS LQH TO */
+/* SAVE A PARTICULAR GLOBAL CHECKPOINT TO DISK AND RESPOND WHEN COMPLETED. */
+/*---------------------------------------------------------------------------*/
+/* *************** */
+/* GCP_SAVEREQ > */
+/* *************** */
+void Dblqh::execGCP_SAVEREQ(Signal* signal)
+{
+ jamEntry();
+ const GCPSaveReq * const saveReq = (GCPSaveReq *)&signal->theData[0];
+
+ if (ERROR_INSERTED(5000)) {
+ systemErrorLab(signal);
+ }
+
+ if (ERROR_INSERTED(5007)){
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_GCP_SAVEREQ, signal, 10000,
+ signal->length());
+ return;
+ }
+
+ const Uint32 dihBlockRef = saveReq->dihBlockRef;
+ const Uint32 dihPtr = saveReq->dihPtr;
+ const Uint32 gci = saveReq->gci;
+
+ ndbrequire(gci >= cnewestCompletedGci);
+
+ if (gci == cnewestCompletedGci) {
+/*---------------------------------------------------------------------------*/
+/* GLOBAL CHECKPOINT HAVE ALREADY BEEN HANDLED. REQUEST MUST HAVE BEEN SENT */
+/* FROM NEW MASTER DIH. */
+/*---------------------------------------------------------------------------*/
+ if (ccurrentGcprec == RNIL) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS INDICATES THAT WE HAVE ALREADY SENT GCP_SAVECONF TO PREVIOUS MASTER. */
+/* WE SIMPLY SEND IT ALSO TO THE NEW MASTER. */
+/*---------------------------------------------------------------------------*/
+ GCPSaveConf * const saveConf = (GCPSaveConf*)&signal->theData[0];
+ saveConf->dihPtr = dihPtr;
+ saveConf->nodeId = getOwnNodeId();
+ saveConf->gci = cnewestCompletedGci;
+ sendSignal(dihBlockRef, GSN_GCP_SAVECONF, signal,
+ GCPSaveConf::SignalLength, JBA);
+ return;
+ }
+ jam();
+/*---------------------------------------------------------------------------*/
+/* WE HAVE NOT YET SENT THE RESPONSE TO THE OLD MASTER. WE WILL SET THE NEW */
+/* RECEIVER OF THE RESPONSE AND THEN EXIT SINCE THE PROCESS IS ALREADY */
+/* STARTED. */
+/*---------------------------------------------------------------------------*/
+ gcpPtr.i = ccurrentGcprec;
+ ptrCheckGuard(gcpPtr, cgcprecFileSize, gcpRecord);
+ gcpPtr.p->gcpUserptr = dihPtr;
+ gcpPtr.p->gcpBlockref = dihBlockRef;
+ return;
+ }//if
+
+ ndbrequire(ccurrentGcprec == RNIL);
+
+
+ if(getNodeState().startLevel >= NodeState::SL_STOPPING_4){
+ GCPSaveRef * const saveRef = (GCPSaveRef*)&signal->theData[0];
+ saveRef->dihPtr = dihPtr;
+ saveRef->nodeId = getOwnNodeId();
+ saveRef->gci = gci;
+ saveRef->errorCode = GCPSaveRef::NodeShutdownInProgress;
+ sendSignal(dihBlockRef, GSN_GCP_SAVEREF, signal,
+ GCPSaveRef::SignalLength, JBB);
+ return;
+ }
+
+ if(getNodeState().getNodeRestartInProgress()){
+ GCPSaveRef * const saveRef = (GCPSaveRef*)&signal->theData[0];
+ saveRef->dihPtr = dihPtr;
+ saveRef->nodeId = getOwnNodeId();
+ saveRef->gci = gci;
+ saveRef->errorCode = GCPSaveRef::NodeRestartInProgress;
+ sendSignal(dihBlockRef, GSN_GCP_SAVEREF, signal,
+ GCPSaveRef::SignalLength, JBB);
+ return;
+ }
+
+ ccurrentGcprec = 0;
+ gcpPtr.i = ccurrentGcprec;
+ ptrCheckGuard(gcpPtr, cgcprecFileSize, gcpRecord);
+
+ cnewestCompletedGci = gci;
+ if (gci > cnewestGci) {
+ jam();
+ cnewestGci = gci;
+ }//if
+
+ gcpPtr.p->gcpBlockref = dihBlockRef;
+ gcpPtr.p->gcpUserptr = dihPtr;
+ gcpPtr.p->gcpId = gci;
+ bool tlogActive = false;
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ ptrAss(logPartPtr, logPartRecord);
+ if (logPartPtr.p->logPartState == LogPartRecord::ACTIVE) {
+ jam();
+ logPartPtr.p->waitWriteGciLog = LogPartRecord::WWGL_TRUE;
+ tlogActive = true;
+ } else {
+ jam();
+ logPartPtr.p->waitWriteGciLog = LogPartRecord::WWGL_FALSE;
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ writeCompletedGciLog(signal);
+ }//if
+ }//for
+ if (tlogActive == true) {
+ jam();
+ return;
+ }//if
+ initGcpRecLab(signal);
+ startTimeSupervision(signal);
+ return;
+}//Dblqh::execGCP_SAVEREQ()
+
+/* ------------------------------------------------------------------------- */
+/* START TIME SUPERVISION OF THE LOG PARTS. */
+/* ------------------------------------------------------------------------- */
+void Dblqh::startTimeSupervision(Signal* signal)
+{
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/* WE HAVE TO START CHECKING IF THE LOG IS TO BE WRITTEN EVEN IF PAGES ARE */
+/* FULL. INITIALISE THE VALUES OF WHERE WE ARE IN THE LOG CURRENTLY. */
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+ logPartPtr.p->logPartTimer = 0;
+ logPartPtr.p->logTimer = 1;
+ signal->theData[0] = ZTIME_SUPERVISION;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//for
+}//Dblqh::startTimeSupervision()
+
+/*---------------------------------------------------------------------------*/
+/* WE SET THE GLOBAL CHECKPOINT VARIABLES AFTER WRITING THE COMPLETED GCI LOG*/
+/* RECORD. THIS ENSURES THAT WE WILL ENCOUNTER THE COMPLETED GCI RECORD WHEN */
+/* WE EXECUTE THE FRAGMENT LOG. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::initGcpRecLab(Signal* signal)
+{
+/* ======================================================================== */
+/* ======= INITIATE GCP RECORD ======= */
+/* */
+/* SUBROUTINE SHORT NAME = IGR */
+/* ======================================================================== */
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+/*--------------------------------------------------*/
+/* BY SETTING THE GCPREC = 0 WE START THE */
+/* CHECKING BY CHECK_GCP_COMPLETED. THIS */
+/* CHECKING MUST NOT BE STARTED UNTIL WE HAVE */
+/* INSERTED ALL COMPLETE GCI LOG RECORDS IN */
+/* ALL LOG PARTS. */
+/*--------------------------------------------------*/
+ logPartPtr.p->gcprec = 0;
+ gcpPtr.p->gcpLogPartState[logPartPtr.i] = ZWAIT_DISK;
+ gcpPtr.p->gcpSyncReady[logPartPtr.i] = ZFALSE;
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ gcpPtr.p->gcpFilePtr[logPartPtr.i] = logFilePtr.i;
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ if (logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] == ZPAGE_HEADER_SIZE) {
+ jam();
+/*--------------------------------------------------*/
+/* SINCE THE CURRENT FILEPAGE POINTS AT THE */
+/* NEXT WORD TO BE WRITTEN WE HAVE TO ADJUST */
+/* FOR THIS BY DECREASING THE FILE PAGE BY ONE*/
+/* IF NO WORD HAS BEEN WRITTEN ON THE CURRENT */
+/* FILEPAGE. */
+/*--------------------------------------------------*/
+ gcpPtr.p->gcpPageNo[logPartPtr.i] = logFilePtr.p->currentFilepage - 1;
+ gcpPtr.p->gcpWordNo[logPartPtr.i] = ZPAGE_SIZE - 1;
+ } else {
+ jam();
+ gcpPtr.p->gcpPageNo[logPartPtr.i] = logFilePtr.p->currentFilepage;
+ gcpPtr.p->gcpWordNo[logPartPtr.i] =
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] - 1;
+ }//if
+ }//for
+ return;
+}//Dblqh::initGcpRecLab()
+
+/* ========================================================================= */
+/* ==== CHECK IF ANY GLOBAL CHECKPOINTS ARE COMPLETED AFTER A COMPLETED===== */
+/* DISK WRITE. */
+/* */
+/* SUBROUTINE SHORT NAME = CGC */
+/* ========================================================================= */
+void Dblqh::checkGcpCompleted(Signal* signal,
+ Uint32 tcgcPageWritten,
+ Uint32 tcgcWordWritten)
+{
+ UintR tcgcFlag;
+ UintR tcgcJ;
+
+ gcpPtr.i = logPartPtr.p->gcprec;
+ if (gcpPtr.i != RNIL) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* IF THE GLOBAL CHECKPOINT IS NOT WAITING FOR COMPLETION THEN WE CAN QUIT */
+/* THE SEARCH IMMEDIATELY. */
+/* ------------------------------------------------------------------------- */
+ ptrCheckGuard(gcpPtr, cgcprecFileSize, gcpRecord);
+ if (gcpPtr.p->gcpFilePtr[logPartPtr.i] == logFilePtr.i) {
+/* ------------------------------------------------------------------------- */
+/* IF THE COMPLETED DISK OPERATION WAS ON ANOTHER FILE THAN THE ONE WE ARE */
+/* WAITING FOR, THEN WE CAN ALSO QUIT THE SEARCH IMMEDIATELY. */
+/* ------------------------------------------------------------------------- */
+ if (tcgcPageWritten < gcpPtr.p->gcpPageNo[logPartPtr.i]) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* THIS LOG PART HAVE NOT YET WRITTEN THE GLOBAL CHECKPOINT TO DISK. */
+/* ------------------------------------------------------------------------- */
+ return;
+ } else {
+ if (tcgcPageWritten == gcpPtr.p->gcpPageNo[logPartPtr.i]) {
+ if (tcgcWordWritten < gcpPtr.p->gcpWordNo[logPartPtr.i]) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* THIS LOG PART HAVE NOT YET WRITTEN THE GLOBAL CHECKPOINT TO DISK. */
+/* ------------------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//if
+/* ------------------------------------------------------------------------- */
+/* THIS LOG PART HAVE WRITTEN THE GLOBAL CHECKPOINT TO DISK. */
+/* ------------------------------------------------------------------------- */
+ logPartPtr.p->gcprec = RNIL;
+ gcpPtr.p->gcpLogPartState[logPartPtr.i] = ZON_DISK;
+ tcgcFlag = ZTRUE;
+ for (tcgcJ = 0; tcgcJ <= 3; tcgcJ++) {
+ jam();
+ if (gcpPtr.p->gcpLogPartState[tcgcJ] != ZON_DISK) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*ALL LOG PARTS HAVE NOT SAVED THIS GLOBAL CHECKPOINT TO DISK YET. WAIT FOR */
+/*THEM TO COMPLETE. */
+/* ------------------------------------------------------------------------- */
+ tcgcFlag = ZFALSE;
+ }//if
+ }//for
+ if (tcgcFlag == ZTRUE) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/*WE HAVE FOUND A COMPLETED GLOBAL CHECKPOINT OPERATION. WE NOW NEED TO SEND */
+/*GCP_SAVECONF, REMOVE THE GCP RECORD FROM THE LIST OF WAITING GCP RECORDS */
+/*ON THIS LOG PART AND RELEASE THE GCP RECORD. */
+// After changing the log implementation we need to perform a FSSYNCREQ on all
+// log files where the last log word resided first before proceeding.
+/* ------------------------------------------------------------------------- */
+ UintR Ti;
+ for (Ti = 0; Ti < 4; Ti++) {
+ LogFileRecordPtr loopLogFilePtr;
+ loopLogFilePtr.i = gcpPtr.p->gcpFilePtr[Ti];
+ ptrCheckGuard(loopLogFilePtr, clogFileFileSize, logFileRecord);
+ if (loopLogFilePtr.p->logFileStatus == LogFileRecord::OPEN) {
+ jam();
+ signal->theData[0] = loopLogFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = gcpPtr.p->gcpFilePtr[Ti];
+ sendSignal(NDBFS_REF, GSN_FSSYNCREQ, signal, 3, JBA);
+ } else {
+ ndbrequire((loopLogFilePtr.p->logFileStatus ==
+ LogFileRecord::CLOSED) ||
+ (loopLogFilePtr.p->logFileStatus ==
+ LogFileRecord::CLOSING_WRITE_LOG) ||
+ (loopLogFilePtr.p->logFileStatus ==
+ LogFileRecord::OPENING_WRITE_LOG));
+ signal->theData[0] = loopLogFilePtr.i;
+ execFSSYNCCONF(signal);
+ }//if
+ }//for
+ return;
+ }//if
+ }//if
+ }//if
+}//Dblqh::checkGcpCompleted()
+
+void
+Dblqh::execFSSYNCCONF(Signal* signal)
+{
+ GcpRecordPtr localGcpPtr;
+ LogFileRecordPtr localLogFilePtr;
+ LogPartRecordPtr localLogPartPtr;
+ localLogFilePtr.i = signal->theData[0];
+ ptrCheckGuard(localLogFilePtr, clogFileFileSize, logFileRecord);
+ localLogPartPtr.i = localLogFilePtr.p->logPartRec;
+ localGcpPtr.i = ccurrentGcprec;
+ ptrCheckGuard(localGcpPtr, cgcprecFileSize, gcpRecord);
+ localGcpPtr.p->gcpSyncReady[localLogPartPtr.i] = ZTRUE;
+ UintR Ti;
+ for (Ti = 0; Ti < 4; Ti++) {
+ jam();
+ if (localGcpPtr.p->gcpSyncReady[Ti] == ZFALSE) {
+ jam();
+ return;
+ }//if
+ }//for
+ GCPSaveConf * const saveConf = (GCPSaveConf *)&signal->theData[0];
+ saveConf->dihPtr = localGcpPtr.p->gcpUserptr;
+ saveConf->nodeId = getOwnNodeId();
+ saveConf->gci = localGcpPtr.p->gcpId;
+ sendSignal(localGcpPtr.p->gcpBlockref, GSN_GCP_SAVECONF, signal,
+ GCPSaveConf::SignalLength, JBA);
+ ccurrentGcprec = RNIL;
+}//Dblqh::execFSSYNCCONF()
+
+void
+Dblqh::execFSSYNCREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execFSSYNCREF()
+
+
+/* ######################################################################### */
+/* ####### FILE HANDLING MODULE ####### */
+/* */
+/* ######################################################################### */
+/* THIS MODULE HANDLES RESPONSE MESSAGES FROM THE FILE SYSTEM */
+/* ######################################################################### */
+/* ######################################################################### */
+/* SIGNAL RECEPTION MODULE */
+/* THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING. */
+/* */
+/* THIS MODULE CHECKS THE STATE AND JUMPS TO THE PROPER PART OF THE FILE */
+/* HANDLING MODULE. */
+/* ######################################################################### */
+/* *************** */
+/* FSCLOSECONF > */
+/* *************** */
+void Dblqh::execFSCLOSECONF(Signal* signal)
+{
+ jamEntry();
+ logFilePtr.i = signal->theData[0];
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ switch (logFilePtr.p->logFileStatus) {
+ case LogFileRecord::CLOSE_SR_INVALIDATE_PAGES:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ // Set the prev file to check if we shall close it.
+ logFilePtr.i = logFilePtr.p->prevLogFile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ exitFromInvalidate(signal);
+ return;
+ break;
+ case LogFileRecord::CLOSING_INIT:
+ jam();
+ closingInitLab(signal);
+ return;
+ break;
+ case LogFileRecord::CLOSING_SR:
+ jam();
+ closingSrLab(signal);
+ return;
+ break;
+ case LogFileRecord::CLOSING_EXEC_SR:
+ jam();
+ closeExecSrLab(signal);
+ return;
+ break;
+ case LogFileRecord::CLOSING_EXEC_SR_COMPLETED:
+ jam();
+ closeExecSrCompletedLab(signal);
+ return;
+ break;
+ case LogFileRecord::CLOSING_WRITE_LOG:
+ jam();
+ closeWriteLogLab(signal);
+ return;
+ break;
+ case LogFileRecord::CLOSING_EXEC_LOG:
+ jam();
+ closeExecLogLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::execFSCLOSECONF()
+
+/* ************>> */
+/* FSCLOSEREF > */
+/* ************>> */
+void Dblqh::execFSCLOSEREF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execFSCLOSEREF()
+
+/* ************>> */
+/* FSOPENCONF > */
+/* ************>> */
+void Dblqh::execFSOPENCONF(Signal* signal)
+{
+ jamEntry();
+ initFsopenconf(signal);
+ switch (logFilePtr.p->logFileStatus) {
+ case LogFileRecord::OPEN_SR_INVALIDATE_PAGES:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ readFileInInvalidate(signal);
+ return;
+ break;
+ case LogFileRecord::OPENING_INIT:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openFileInitLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_SR_FRONTPAGE:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openSrFrontpageLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_SR_LAST_FILE:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openSrLastFileLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_SR_NEXT_FILE:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openSrNextFileLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_EXEC_SR_START:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openExecSrStartLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_EXEC_SR_NEW_MBYTE:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openExecSrNewMbyteLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_SR_FOURTH_PHASE:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openSrFourthPhaseLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_SR_FOURTH_NEXT:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openSrFourthNextLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPEN_SR_FOURTH_ZERO:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openSrFourthZeroLab(signal);
+ return;
+ break;
+ case LogFileRecord::OPENING_WRITE_LOG:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ return;
+ break;
+ case LogFileRecord::OPEN_EXEC_LOG:
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN;
+ openExecLogLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::execFSOPENCONF()
+
+/* ************> */
+/* FSOPENREF > */
+/* ************> */
+void Dblqh::execFSOPENREF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execFSOPENREF()
+
+/* ************>> */
+/* FSREADCONF > */
+/* ************>> */
+void Dblqh::execFSREADCONF(Signal* signal)
+{
+ jamEntry();
+ initFsrwconf(signal);
+
+ switch (lfoPtr.p->lfoState) {
+ case LogFileOperationRecord::READ_SR_LAST_MBYTE:
+ jam();
+ releaseLfo(signal);
+ readSrLastMbyteLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_FRONTPAGE:
+ jam();
+ releaseLfo(signal);
+ readSrFrontpageLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_LAST_FILE:
+ jam();
+ releaseLfo(signal);
+ readSrLastFileLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_NEXT_FILE:
+ jam();
+ releaseLfo(signal);
+ readSrNextFileLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_EXEC_SR:
+ jam();
+ readExecSrLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_EXEC_LOG:
+ jam();
+ readExecLogLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_INVALIDATE_PAGES:
+ jam();
+ invalidateLogAfterLastGCI(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_FOURTH_PHASE:
+ jam();
+ releaseLfo(signal);
+ readSrFourthPhaseLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_FOURTH_ZERO:
+ jam();
+ releaseLfo(signal);
+ readSrFourthZeroLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::execFSREADCONF()
+
+/* ************>> */
+/* FSREADCONF > */
+/* ************>> */
+void Dblqh::execFSREADREF(Signal* signal)
+{
+ jamEntry();
+ lfoPtr.i = signal->theData[0];
+ ptrCheckGuard(lfoPtr, clfoFileSize, logFileOperationRecord);
+ terrorCode = signal->theData[1];
+ switch (lfoPtr.p->lfoState) {
+ case LogFileOperationRecord::READ_SR_LAST_MBYTE:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_FRONTPAGE:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_LAST_FILE:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_NEXT_FILE:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_EXEC_SR:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_EXEC_LOG:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_FOURTH_PHASE:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_FOURTH_ZERO:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::READ_SR_INVALIDATE_PAGES:
+ jam()
+ systemErrorLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ return;
+}//Dblqh::execFSREADREF()
+
+/* *************** */
+/* FSWRITECONF > */
+/* *************** */
+void Dblqh::execFSWRITECONF(Signal* signal)
+{
+ jamEntry();
+ initFsrwconf(signal);
+ switch (lfoPtr.p->lfoState) {
+ case LogFileOperationRecord::WRITE_SR_INVALIDATE_PAGES:
+ jam();
+ invalidateLogAfterLastGCI(signal);
+ return;
+ break;
+ case LogFileOperationRecord::WRITE_PAGE_ZERO:
+ jam();
+ writePageZeroLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::LAST_WRITE_IN_FILE:
+ jam();
+ lastWriteInFileLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::INIT_WRITE_AT_END:
+ jam();
+ initWriteEndLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::INIT_FIRST_PAGE:
+ jam();
+ initFirstPageLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::WRITE_GCI_ZERO:
+ jam();
+ writeGciZeroLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::WRITE_DIRTY:
+ jam();
+ writeDirtyLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::WRITE_INIT_MBYTE:
+ jam();
+ writeInitMbyteLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::ACTIVE_WRITE_LOG:
+ jam();
+ writeLogfileLab(signal);
+ return;
+ break;
+ case LogFileOperationRecord::FIRST_PAGE_WRITE_IN_LOGFILE:
+ jam();
+ firstPageWriteLab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Dblqh::execFSWRITECONF()
+
+/* ************>> */
+/* FSWRITEREF > */
+/* ************>> */
+void Dblqh::execFSWRITEREF(Signal* signal)
+{
+ jamEntry();
+ lfoPtr.i = signal->theData[0];
+ ptrCheckGuard(lfoPtr, clfoFileSize, logFileOperationRecord);
+ terrorCode = signal->theData[1];
+ switch (lfoPtr.p->lfoState) {
+ case LogFileOperationRecord::WRITE_PAGE_ZERO:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::LAST_WRITE_IN_FILE:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::INIT_WRITE_AT_END:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::INIT_FIRST_PAGE:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::WRITE_GCI_ZERO:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::WRITE_DIRTY:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::WRITE_INIT_MBYTE:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::ACTIVE_WRITE_LOG:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::FIRST_PAGE_WRITE_IN_LOGFILE:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case LogFileOperationRecord::WRITE_SR_INVALIDATE_PAGES:
+ jam();
+ systemErrorLab(signal);
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+}//Dblqh::execFSWRITEREF()
+
+
+/* ========================================================================= */
+/* ======= INITIATE WHEN RECEIVING FSOPENCONF ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initFsopenconf(Signal* signal)
+{
+ logFilePtr.i = signal->theData[0];
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logFilePtr.p->fileRef = signal->theData[1];
+ logPartPtr.i = logFilePtr.p->logPartRec;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logFilePtr.p->currentMbyte = 0;
+ logFilePtr.p->filePosition = 0;
+ logFilePtr.p->logFilePagesToDiskWithoutSynch = 0;
+}//Dblqh::initFsopenconf()
+
+/* ========================================================================= */
+/* ======= INITIATE WHEN RECEIVING FSREADCONF AND FSWRITECONF ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initFsrwconf(Signal* signal)
+{
+ lfoPtr.i = signal->theData[0];
+ ptrCheckGuard(lfoPtr, clfoFileSize, logFileOperationRecord);
+ logFilePtr.i = lfoPtr.p->logFileRec;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPartPtr.i = logFilePtr.p->logPartRec;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logPagePtr.i = lfoPtr.p->firstLfoPage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+}//Dblqh::initFsrwconf()
+
+/* ######################################################################### */
+/* NORMAL OPERATION MODULE */
+/* THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING. */
+/* */
+/* THIS PART HANDLES THE NORMAL OPENING, CLOSING AND WRITING OF LOG FILES */
+/* DURING NORMAL OPERATION. */
+/* ######################################################################### */
+/*---------------------------------------------------------------------------*/
+/* THIS SIGNAL IS USED TO SUPERVISE THAT THE LOG RECORDS ARE NOT KEPT IN MAIN*/
+/* MEMORY FOR MORE THAN 1 SECOND TO ACHIEVE THE PROPER RELIABILITY. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::timeSup(Signal* signal)
+{
+ LogPageRecordPtr origLogPagePtr;
+ Uint32 wordWritten;
+
+ jamEntry();
+ logPartPtr.i = signal->theData[0];
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ if (logPartPtr.p->logPartTimer != logPartPtr.p->logTimer) {
+ jam();
+/*--------------------------------------------------------------------------*/
+/* THIS LOG PART HAS NOT WRITTEN TO DISK DURING THE LAST SECOND. */
+/*--------------------------------------------------------------------------*/
+ switch (logPartPtr.p->logPartState) {
+ case LogPartRecord::FILE_CHANGE_PROBLEM:
+ jam();
+/*--------------------------------------------------------------------------*/
+/* THIS LOG PART HAS PROBLEMS IN CHANGING FILES MAKING IT IMPOSSIBLE */
+// TO WRITE TO THE FILE CURRENTLY. WE WILL COMEBACK LATER AND SEE IF
+// THE PROBLEM HAS BEEN FIXED.
+/*--------------------------------------------------------------------------*/
+ case LogPartRecord::ACTIVE:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* AN OPERATION IS CURRENTLY ACTIVE IN WRITING THIS LOG PART. WE THUS CANNOT */
+/* WRITE ANYTHING TO DISK AT THIS MOMENT. WE WILL SEND A SIGNAL DELAYED FOR */
+/* 10 MS AND THEN TRY AGAIN. POSSIBLY THE LOG PART WILL HAVE BEEN WRITTEN */
+/* UNTIL THEN OR ELSE IT SHOULD BE FREE TO WRITE AGAIN. */
+/*---------------------------------------------------------------------------*/
+ signal->theData[0] = ZTIME_SUPERVISION;
+ signal->theData[1] = logPartPtr.i;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 2);
+ return;
+ break;
+ case LogPartRecord::IDLE:
+ case LogPartRecord::TAIL_PROBLEM:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* IDLE AND NOT WRITTEN TO DISK IN A SECOND. ALSO WHEN WE HAVE A TAIL PROBLEM*/
+/* WE HAVE TO WRITE TO DISK AT TIMES. WE WILL FIRST CHECK WHETHER ANYTHING */
+/* AT ALL HAVE BEEN WRITTEN TO THE PAGES BEFORE WRITING TO DISK. */
+/*---------------------------------------------------------------------------*/
+/* WE HAVE TO WRITE TO DISK IN ALL CASES SINCE THERE COULD BE INFORMATION */
+/* STILL IN THE LOG THAT WAS GENERATED BEFORE THE PREVIOUS TIME SUPERVISION */
+/* BUT AFTER THE LAST DISK WRITE. THIS PREVIOUSLY STOPPED ALL DISK WRITES */
+/* WHEN NO MORE LOG WRITES WERE PERFORMED (THIS HAPPENED WHEN LOG GOT FULL */
+/* AND AFTER LOADING THE INITIAL RECORDS IN INITIAL START). */
+/*---------------------------------------------------------------------------*/
+ if (((logFilePtr.p->currentFilepage + 1) & (ZPAGES_IN_MBYTE -1)) == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS IS THE LAST PAGE IN THIS MBYTE. WRITE NEXT LOG AND SWITCH TO NEXT */
+/* MBYTE. */
+/*---------------------------------------------------------------------------*/
+ changeMbyte(signal);
+ } else {
+/*---------------------------------------------------------------------------*/
+/* WRITE THE LOG PAGE TO DISK EVEN IF IT IS NOT FULL. KEEP PAGE AND WRITE A */
+/* COPY. THE ORIGINAL PAGE WILL BE WRITTEN AGAIN LATER ON. */
+/*---------------------------------------------------------------------------*/
+ wordWritten = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] - 1;
+ origLogPagePtr.i = logPagePtr.i;
+ origLogPagePtr.p = logPagePtr.p;
+ seizeLogpage(signal);
+ MEMCOPY_NO_WORDS(&logPagePtr.p->logPageWord[0],
+ &origLogPagePtr.p->logPageWord[0],
+ wordWritten + 1);
+ ndbrequire(wordWritten < ZPAGE_SIZE);
+ if (logFilePtr.p->noLogpagesInBuffer > 0) {
+ jam();
+ completedLogPage(signal, ZENFORCE_WRITE);
+/*---------------------------------------------------------------------------*/
+/*SINCE WE ARE ONLY WRITING PART OF THE LAST PAGE WE HAVE TO UPDATE THE WORD */
+/*WRITTEN TO REFLECT THE REAL LAST WORD WRITTEN. WE ALSO HAVE TO MOVE THE */
+/*FILE POSITION ONE STEP BACKWARDS SINCE WE ARE NOT WRITING THE LAST PAGE */
+/*COMPLETELY. IT WILL BE WRITTEN AGAIN. */
+/*---------------------------------------------------------------------------*/
+ lfoPtr.p->lfoWordWritten = wordWritten;
+ logFilePtr.p->filePosition = logFilePtr.p->filePosition - 1;
+ } else {
+ if (wordWritten == (ZPAGE_HEADER_SIZE - 1)) {
+/*---------------------------------------------------------------------------*/
+/*THIS IS POSSIBLE BUT VERY UNLIKELY. IF THE PAGE WAS COMPLETED AFTER THE LAST*/
+/*WRITE TO DISK THEN NO_LOG_PAGES_IN_BUFFER > 0 AND IF NOT WRITTEN SINCE LAST*/
+/*WRITE TO DISK THEN THE PREVIOUS PAGE MUST HAVE BEEN WRITTEN BY SOME */
+/*OPERATION AND THAT BECAME COMPLETELY FULL. IN ANY CASE WE NEED NOT WRITE AN*/
+/*EMPTY PAGE TO DISK. */
+/*---------------------------------------------------------------------------*/
+ jam();
+ releaseLogpage(signal);
+ } else {
+ jam();
+ writeSinglePage(signal, logFilePtr.p->currentFilepage, wordWritten);
+ lfoPtr.p->lfoState = LogFileOperationRecord::ACTIVE_WRITE_LOG;
+ }//if
+ }//if
+ }//if
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ }//if
+ logPartPtr.p->logTimer++;
+ return;
+}//Dblqh::timeSup()
+
+void Dblqh::writeLogfileLab(Signal* signal)
+{
+/*---------------------------------------------------------------------------*/
+/* CHECK IF ANY GLOBAL CHECKPOINTS ARE COMPLETED DUE TO THIS COMPLETED DISK */
+/* WRITE. */
+/*---------------------------------------------------------------------------*/
+ switch (logFilePtr.p->fileChangeState) {
+ case LogFileRecord::NOT_ONGOING:
+ jam();
+ checkGcpCompleted(signal,
+ ((lfoPtr.p->lfoPageNo + lfoPtr.p->noPagesRw) - 1),
+ lfoPtr.p->lfoWordWritten);
+ break;
+#if 0
+ case LogFileRecord::BOTH_WRITES_ONGOING:
+ jam();
+ ndbout_c("not crashing!!");
+ // Fall-through
+#endif
+ case LogFileRecord::WRITE_PAGE_ZERO_ONGOING:
+ case LogFileRecord::LAST_WRITE_ONGOING:
+ jam();
+ logFilePtr.p->lastPageWritten = (lfoPtr.p->lfoPageNo + lfoPtr.p->noPagesRw) - 1;
+ logFilePtr.p->lastWordWritten = lfoPtr.p->lfoWordWritten;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ releaseLfoPages(signal);
+ releaseLfo(signal);
+ return;
+}//Dblqh::writeLogfileLab()
+
+void Dblqh::closeWriteLogLab(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ return;
+}//Dblqh::closeWriteLogLab()
+
+/* ######################################################################### */
+/* FILE CHANGE MODULE */
+/* THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING. */
+/* */
+/*THIS PART OF THE FILE MODULE HANDLES WHEN WE ARE CHANGING LOG FILE DURING */
+/*NORMAL OPERATION. WE HAVE TO BE CAREFUL WHEN WE ARE CHANGING LOG FILE SO */
+/*THAT WE DO NOT COMPLICATE THE SYSTEM RESTART PROCESS TOO MUCH. */
+/*THE IDEA IS THAT WE START BY WRITING THE LAST WRITE IN THE OLD FILE AND WE */
+/*ALSO WRITE THE FIRST PAGE OF THE NEW FILE CONCURRENT WITH THAT. THIS FIRST */
+/*PAGE IN THE NEW FILE DO NOT CONTAIN ANY LOG RECORDS OTHER THAN A DESCRIPTOR*/
+/*CONTAINING INFORMATION ABOUT GCI'S NEEDED AT SYSTEM RESTART AND A NEXT LOG */
+/*RECORD. */
+/* */
+/*WHEN BOTH OF THOSE WRITES HAVE COMPLETED WE ALSO WRITE PAGE ZERO IN FILE */
+/*ZERO. THE ONLY INFORMATION WHICH IS INTERESTING HERE IS THE NEW FILE NUMBER*/
+/* */
+/*IF OPTIMISATIONS ARE NEEDED OF THE LOG HANDLING THEN IT IS POSSIBLE TO */
+/*AVOID WRITING THE FIRST PAGE OF THE NEW PAGE IMMEDIATELY. THIS COMPLICATES */
+/*THE SYSTEM RESTART AND ONE HAS TO TAKE SPECIAL CARE WITH FILE ZERO. IT IS */
+/*HOWEVER NO LARGE PROBLEM TO CHANGE INTO THIS SCENARIO. TO AVOID ALSO THE */
+/*WRITING OF PAGE ZERO IS ALSO POSSIBLE BUT COMPLICATES THE DESIGN EVEN */
+/*FURTHER. IT GETS FAIRLY COMPLEX TO FIND THE END OF THE LOG. SOME SORT OF */
+/*BINARY SEARCH IS HOWEVER MOST LIKELY A GOOD METHODOLOGY FOR THIS. */
+/* ######################################################################### */
+void Dblqh::firstPageWriteLab(Signal* signal)
+{
+ releaseLfo(signal);
+/*---------------------------------------------------------------------------*/
+/* RELEASE PAGE ZERO IF THE FILE IS NOT FILE 0. */
+/*---------------------------------------------------------------------------*/
+ Uint32 fileNo = logFilePtr.p->fileNo;
+ if (fileNo != 0) {
+ jam();
+ releaseLogpage(signal);
+ }//if
+/*---------------------------------------------------------------------------*/
+/* IF A NEW FILE HAS BEEN OPENED WE SHALL ALWAYS ALSO WRITE TO PAGE O IN */
+/* FILE 0. THE AIM IS TO MAKE RESTARTS EASIER BY SPECIFYING WHICH IS THE */
+/* LAST FILE WHERE LOGGING HAS STARTED. */
+/*---------------------------------------------------------------------------*/
+/* FIRST CHECK WHETHER THE LAST WRITE IN THE PREVIOUS FILE HAVE COMPLETED */
+/*---------------------------------------------------------------------------*/
+ if (logFilePtr.p->fileChangeState == LogFileRecord::BOTH_WRITES_ONGOING) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE LAST WRITE WAS STILL ONGOING. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->fileChangeState = LogFileRecord::LAST_WRITE_ONGOING;
+ return;
+ } else {
+ jam();
+ ndbrequire(logFilePtr.p->fileChangeState == LogFileRecord::FIRST_WRITE_ONGOING);
+/*---------------------------------------------------------------------------*/
+/* WRITE TO PAGE 0 IN IN FILE 0 NOW. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->fileChangeState = LogFileRecord::WRITE_PAGE_ZERO_ONGOING;
+ if (fileNo == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* IF THE NEW FILE WAS 0 THEN WE HAVE ALREADY WRITTEN PAGE ZERO IN FILE 0. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->fileChangeState = LogFileRecord::NOT_ONGOING;
+ return;
+ } else {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* WRITE PAGE ZERO IN FILE ZERO. LOG_FILE_REC WILL REFER TO THE LOG FILE WE */
+/* HAVE JUST WRITTEN PAGE ZERO IN TO GET HOLD OF LOG_FILE_PTR FOR THIS */
+/* RECORD QUICKLY. THIS IS NEEDED TO GET HOLD OF THE FILE_CHANGE_STATE. */
+/* THE ONLY INFORMATION WE WANT TO CHANGE IS THE LAST FILE NUMBER IN THE */
+/* FILE DESCRIPTOR. THIS IS USED AT SYSTEM RESTART TO FIND THE END OF THE */
+/* LOG PART. */
+/*---------------------------------------------------------------------------*/
+ Uint32 currLogFile = logFilePtr.i;
+ logFilePtr.i = logPartPtr.p->firstLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logFilePtr.p->logPageZero;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_FILE_NO] = fileNo;
+ writeSinglePage(signal, 0, ZPAGE_SIZE - 1);
+ lfoPtr.p->logFileRec = currLogFile;
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_PAGE_ZERO;
+ return;
+ }//if
+ }//if
+}//Dblqh::firstPageWriteLab()
+
+void Dblqh::lastWriteInFileLab(Signal* signal)
+{
+ LogFileRecordPtr locLogFilePtr;
+/*---------------------------------------------------------------------------*/
+/* CHECK IF ANY GLOBAL CHECKPOINTS ARE COMPLETED DUE TO THIS COMPLETED DISK */
+/* WRITE. */
+/*---------------------------------------------------------------------------*/
+ checkGcpCompleted(signal,
+ ((lfoPtr.p->lfoPageNo + lfoPtr.p->noPagesRw) - 1),
+ (ZPAGE_SIZE - 1));
+ releaseLfoPages(signal);
+ releaseLfo(signal);
+/*---------------------------------------------------------------------------*/
+/* IF THE FILE IS NOT IN USE OR THE NEXT FILE TO BE USED WE WILL CLOSE IT. */
+/*---------------------------------------------------------------------------*/
+ locLogFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(locLogFilePtr, clogFileFileSize, logFileRecord);
+ if (logFilePtr.i != locLogFilePtr.i) {
+ if (logFilePtr.i != locLogFilePtr.p->nextLogFile) {
+ if (logFilePtr.p->fileNo != 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE FILE IS NOT FILE ZERO EITHER. WE WILL NOT CLOSE FILE ZERO SINCE WE */
+/* USE IT TO KEEP TRACK OF THE CURRENT LOG FILE BY WRITING PAGE ZERO IN */
+/* FILE ZERO. */
+/*---------------------------------------------------------------------------*/
+/* WE WILL CLOSE THE FILE. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_WRITE_LOG;
+ closeFile(signal, logFilePtr);
+ }//if
+ }//if
+ }//if
+/*---------------------------------------------------------------------------*/
+/* IF A NEW FILE HAS BEEN OPENED WE SHALL ALWAYS ALSO WRITE TO PAGE O IN */
+/* FILE 0. THE AIM IS TO MAKE RESTARTS EASIER BY SPECIFYING WHICH IS THE */
+/* LAST FILE WHERE LOGGING HAS STARTED. */
+/*---------------------------------------------------------------------------*/
+/* FIRST CHECK WHETHER THE FIRST WRITE IN THE NEW FILE HAVE COMPLETED */
+/* THIS STATE INFORMATION IS IN THE NEW LOG FILE AND THUS WE HAVE TO MOVE */
+/* THE LOG FILE POINTER TO THIS LOG FILE. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.i = logFilePtr.p->nextLogFile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ if (logFilePtr.p->fileChangeState == LogFileRecord::BOTH_WRITES_ONGOING) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE FIRST WRITE WAS STILL ONGOING. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->fileChangeState = LogFileRecord::FIRST_WRITE_ONGOING;
+ return;
+ } else {
+ ndbrequire(logFilePtr.p->fileChangeState == LogFileRecord::LAST_WRITE_ONGOING);
+/*---------------------------------------------------------------------------*/
+/* WRITE TO PAGE 0 IN IN FILE 0 NOW. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->fileChangeState = LogFileRecord::WRITE_PAGE_ZERO_ONGOING;
+ Uint32 fileNo = logFilePtr.p->fileNo;
+ if (fileNo == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* IF THE NEW FILE WAS 0 THEN WE HAVE ALREADY WRITTEN PAGE ZERO IN FILE 0. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->fileChangeState = LogFileRecord::NOT_ONGOING;
+ return;
+ } else {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* WRITE PAGE ZERO IN FILE ZERO. LOG_FILE_REC WILL REFER TO THE LOG FILE WE */
+/* HAVE JUST WRITTEN PAGE ZERO IN TO GET HOLD OF LOG_FILE_PTR FOR THIS */
+/* RECORD QUICKLY. THIS IS NEEDED TO GET HOLD OF THE FILE_CHANGE_STATE. */
+/* THE ONLY INFORMATION WE WANT TO CHANGE IS THE LAST FILE NUMBER IN THE */
+/* FILE DESCRIPTOR. THIS IS USED AT SYSTEM RESTART TO FIND THE END OF THE */
+/* LOG PART. */
+/*---------------------------------------------------------------------------*/
+ Uint32 currLogFile = logFilePtr.i;
+ logFilePtr.i = logPartPtr.p->firstLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logFilePtr.p->logPageZero;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_FILE_NO] = fileNo;
+ writeSinglePage(signal, 0, ZPAGE_SIZE - 1);
+ lfoPtr.p->logFileRec = currLogFile;
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_PAGE_ZERO;
+ return;
+ }//if
+ }//if
+}//Dblqh::lastWriteInFileLab()
+
+void Dblqh::writePageZeroLab(Signal* signal)
+{
+ logFilePtr.p->fileChangeState = LogFileRecord::NOT_ONGOING;
+/*---------------------------------------------------------------------------*/
+/* IT COULD HAVE ARRIVED PAGE WRITES TO THE CURRENT FILE WHILE WE WERE */
+/* WAITING FOR THIS DISK WRITE TO COMPLETE. THEY COULD NOT CHECK FOR */
+/* COMPLETED GLOBAL CHECKPOINTS. THUS WE SHOULD DO THAT NOW INSTEAD. */
+/*---------------------------------------------------------------------------*/
+ checkGcpCompleted(signal,
+ logFilePtr.p->lastPageWritten,
+ logFilePtr.p->lastWordWritten);
+ releaseLfo(signal);
+ return;
+}//Dblqh::writePageZeroLab()
+
+/* ######################################################################### */
+/* INITIAL START MODULE */
+/* THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING. */
+/* */
+/*THIS MODULE INITIALISES ALL THE LOG FILES THAT ARE NEEDED AT A SYSTEM */
+/*RESTART AND WHICH ARE USED DURING NORMAL OPERATIONS. IT CREATES THE FILES */
+/*AND SETS A PROPER SIZE OF THEM AND INITIALISES THE FIRST PAGE IN EACH FILE */
+/* ######################################################################### */
+void Dblqh::openFileInitLab(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN_INIT;
+ seizeLogpage(signal);
+ writeSinglePage(signal, (ZNO_MBYTES_IN_FILE * ZPAGES_IN_MBYTE) - 1, ZPAGE_SIZE - 1);
+ lfoPtr.p->lfoState = LogFileOperationRecord::INIT_WRITE_AT_END;
+ return;
+}//Dblqh::openFileInitLab()
+
+void Dblqh::initWriteEndLab(Signal* signal)
+{
+ releaseLfo(signal);
+ initLogpage(signal);
+ if (logFilePtr.p->fileNo == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* PAGE ZERO IN FILE ZERO MUST SET LOG LAP TO ONE SINCE IT HAS STARTED */
+/* WRITING TO THE LOG, ALSO GLOBAL CHECKPOINTS ARE SET TO ZERO. */
+/*---------------------------------------------------------------------------*/
+ logPagePtr.p->logPageWord[ZPOS_LOG_LAP] = 1;
+ logPagePtr.p->logPageWord[ZPOS_MAX_GCI_STARTED] = 0;
+ logPagePtr.p->logPageWord[ZPOS_MAX_GCI_COMPLETED] = 0;
+ logFilePtr.p->logMaxGciStarted[0] = 0;
+ logFilePtr.p->logMaxGciCompleted[0] = 0;
+ }//if
+/*---------------------------------------------------------------------------*/
+/* REUSE CODE FOR INITIALISATION OF FIRST PAGE IN ALL LOG FILES. */
+/*---------------------------------------------------------------------------*/
+ writeFileHeaderOpen(signal, ZINIT);
+ return;
+}//Dblqh::initWriteEndLab()
+
+void Dblqh::initFirstPageLab(Signal* signal)
+{
+ releaseLfo(signal);
+ if (logFilePtr.p->fileNo == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* IN FILE ZERO WE WILL INSERT A PAGE ONE WHERE WE WILL INSERT A COMPLETED */
+/* GCI RECORD FOR GCI = 0. */
+/*---------------------------------------------------------------------------*/
+ initLogpage(signal);
+ logPagePtr.p->logPageWord[ZPOS_LOG_LAP] = 1;
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE] = ZCOMPLETED_GCI_TYPE;
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + 1] = 1;
+ writeSinglePage(signal, 1, ZPAGE_SIZE - 1);
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_GCI_ZERO;
+ return;
+ }//if
+ logFilePtr.p->currentMbyte = 1;
+ writeInitMbyte(signal);
+ return;
+}//Dblqh::initFirstPageLab()
+
+void Dblqh::writeGciZeroLab(Signal* signal)
+{
+ releaseLfo(signal);
+ logFilePtr.p->currentMbyte = 1;
+ writeInitMbyte(signal);
+ return;
+}//Dblqh::writeGciZeroLab()
+
+void Dblqh::writeInitMbyteLab(Signal* signal)
+{
+ releaseLfo(signal);
+ logFilePtr.p->currentMbyte = logFilePtr.p->currentMbyte + 1;
+ if (logFilePtr.p->currentMbyte == ZNO_MBYTES_IN_FILE) {
+ jam();
+ releaseLogpage(signal);
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_INIT;
+ closeFile(signal, logFilePtr);
+ return;
+ }//if
+ writeInitMbyte(signal);
+ return;
+}//Dblqh::writeInitMbyteLab()
+
+void Dblqh::closingInitLab(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ logPartPtr.i = logFilePtr.p->logPartRec;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ if (logFilePtr.p->nextLogFile == logPartPtr.p->firstLogfile) {
+ jam();
+ checkInitCompletedLab(signal);
+ return;
+ } else {
+ jam();
+ logFilePtr.i = logFilePtr.p->nextLogFile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ openLogfileInit(signal);
+ }//if
+ return;
+}//Dblqh::closingInitLab()
+
+void Dblqh::checkInitCompletedLab(Signal* signal)
+{
+ logPartPtr.p->logPartState = LogPartRecord::SR_FIRST_PHASE_COMPLETED;
+/*---------------------------------------------------------------------------*/
+/* WE HAVE NOW INITIALISED ALL FILES IN THIS LOG PART. WE CAN NOW SET THE */
+/* THE LOG LAP TO ONE SINCE WE WILL START WITH LOG LAP ONE. LOG LAP = ZERO */
+/* MEANS THIS PART OF THE LOG IS NOT WRITTEN YET. */
+/*---------------------------------------------------------------------------*/
+ logPartPtr.p->logLap = 1;
+ logPartPtr.i = 0;
+CHECK_LOG_PARTS_LOOP:
+ ptrAss(logPartPtr, logPartRecord);
+ if (logPartPtr.p->logPartState != LogPartRecord::SR_FIRST_PHASE_COMPLETED) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS PART HAS STILL NOT COMPLETED. WAIT FOR THIS TO OCCUR. */
+/*---------------------------------------------------------------------------*/
+ return;
+ }//if
+ if (logPartPtr.i == 3) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* ALL LOG PARTS ARE COMPLETED. NOW WE CAN CONTINUE WITH THE RESTART */
+/* PROCESSING. THE NEXT STEP IS TO PREPARE FOR EXECUTING OPERATIONS. THUS WE */
+/* NEED TO INITIALISE ALL NEEDED DATA AND TO OPEN FILE ZERO AND THE NEXT AND */
+/* TO SET THE CURRENT LOG PAGE TO BE PAGE 1 IN FILE ZERO. */
+/*---------------------------------------------------------------------------*/
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ ptrAss(logPartPtr, logPartRecord);
+ signal->theData[0] = ZINIT_FOURTH;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//for
+ return;
+ } else {
+ jam();
+ logPartPtr.i = logPartPtr.i + 1;
+ goto CHECK_LOG_PARTS_LOOP;
+ }//if
+}//Dblqh::checkInitCompletedLab()
+
+/* ========================================================================= */
+/* ======= INITIATE LOG FILE OPERATION RECORD WHEN ALLOCATED ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initLfo(Signal* signal)
+{
+ lfoPtr.p->firstLfoPage = RNIL;
+ lfoPtr.p->lfoState = LogFileOperationRecord::IDLE;
+ lfoPtr.p->logFileRec = logFilePtr.i;
+ lfoPtr.p->noPagesRw = 0;
+ lfoPtr.p->lfoPageNo = ZNIL;
+}//Dblqh::initLfo()
+
+/* ========================================================================= */
+/* ======= INITIATE LOG FILE WHEN ALLOCATED ======= */
+/* */
+/* INPUT: TFILE_NO NUMBER OF THE FILE INITIATED */
+/* LOG_PART_PTR NUMBER OF LOG PART */
+/* SUBROUTINE SHORT NAME = IL */
+/* ========================================================================= */
+void Dblqh::initLogfile(Signal* signal, Uint32 fileNo)
+{
+ UintR tilTmp;
+ UintR tilIndex;
+
+ logFilePtr.p->currentFilepage = 0;
+ logFilePtr.p->currentLogpage = RNIL;
+ logFilePtr.p->fileName[0] = (UintR)-1;
+ logFilePtr.p->fileName[1] = (UintR)-1; /* = H'FFFFFFFF = -1 */
+ logFilePtr.p->fileName[2] = fileNo; /* Sfile_no */
+ tilTmp = 1; /* VERSION 1 OF FILE NAME */
+ tilTmp = (tilTmp << 8) + 1; /* FRAGMENT LOG => .FRAGLOG AS EXTENSION */
+ tilTmp = (tilTmp << 8) + (8 + logPartPtr.i); /* DIRECTORY = D(8+Part)/DBLQH */
+ tilTmp = (tilTmp << 8) + 255; /* IGNORE Pxx PART OF FILE NAME */
+ logFilePtr.p->fileName[3] = tilTmp;
+/* ========================================================================= */
+/* FILE NAME BECOMES /D2/DBLQH/Tpart_no/Sfile_no.FRAGLOG */
+/* ========================================================================= */
+ logFilePtr.p->fileNo = fileNo;
+ logFilePtr.p->filePosition = 0;
+ logFilePtr.p->firstLfo = RNIL;
+ logFilePtr.p->lastLfo = RNIL;
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ logFilePtr.p->logPartRec = logPartPtr.i;
+ logFilePtr.p->noLogpagesInBuffer = 0;
+ logFilePtr.p->firstFilledPage = RNIL;
+ logFilePtr.p->lastFilledPage = RNIL;
+ logFilePtr.p->lastPageWritten = 0;
+ logFilePtr.p->logPageZero = RNIL;
+ logFilePtr.p->currentMbyte = 0;
+ for (tilIndex = 0; tilIndex <= 15; tilIndex++) {
+ logFilePtr.p->logMaxGciCompleted[tilIndex] = (UintR)-1;
+ logFilePtr.p->logMaxGciStarted[tilIndex] = (UintR)-1;
+ logFilePtr.p->logLastPrepRef[tilIndex] = 0;
+ }//for
+}//Dblqh::initLogfile()
+
+/* ========================================================================= */
+/* ======= INITIATE LOG PAGE WHEN ALLOCATED ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initLogpage(Signal* signal)
+{
+ TcConnectionrecPtr ilpTcConnectptr;
+
+ logPagePtr.p->logPageWord[ZPOS_LOG_LAP] = logPartPtr.p->logLap;
+ logPagePtr.p->logPageWord[ZPOS_MAX_GCI_COMPLETED] =
+ logPartPtr.p->logPartNewestCompletedGCI;
+ logPagePtr.p->logPageWord[ZPOS_MAX_GCI_STARTED] = cnewestGci;
+ logPagePtr.p->logPageWord[ZPOS_VERSION] = NDB_VERSION;
+ logPagePtr.p->logPageWord[ZPOS_NO_LOG_FILES] = logPartPtr.p->noLogFiles;
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = ZPAGE_HEADER_SIZE;
+ ilpTcConnectptr.i = logPartPtr.p->firstLogTcrec;
+ if (ilpTcConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(ilpTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ logPagePtr.p->logPageWord[ZLAST_LOG_PREP_REF] =
+ (ilpTcConnectptr.p->logStartFileNo << 16) +
+ (ilpTcConnectptr.p->logStartPageNo >> ZTWOLOG_NO_PAGES_IN_MBYTE);
+ } else {
+ jam();
+ logPagePtr.p->logPageWord[ZLAST_LOG_PREP_REF] =
+ (logFilePtr.p->fileNo << 16) +
+ (logFilePtr.p->currentFilepage >> ZTWOLOG_NO_PAGES_IN_MBYTE);
+ }//if
+}//Dblqh::initLogpage()
+
+/* ------------------------------------------------------------------------- */
+/* ------- OPEN LOG FILE FOR READ AND WRITE ------- */
+/* */
+/* SUBROUTINE SHORT NAME = OFR */
+/* ------------------------------------------------------------------------- */
+void Dblqh::openFileRw(Signal* signal, LogFileRecordPtr olfLogFilePtr)
+{
+ signal->theData[0] = cownref;
+ signal->theData[1] = olfLogFilePtr.i;
+ signal->theData[2] = olfLogFilePtr.p->fileName[0];
+ signal->theData[3] = olfLogFilePtr.p->fileName[1];
+ signal->theData[4] = olfLogFilePtr.p->fileName[2];
+ signal->theData[5] = olfLogFilePtr.p->fileName[3];
+ signal->theData[6] = ZOPEN_READ_WRITE;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+}//Dblqh::openFileRw()
+
+/* ------------------------------------------------------------------------- */
+/* ------- OPEN LOG FILE DURING INITIAL START ------- */
+/* */
+/* SUBROUTINE SHORT NAME = OLI */
+/* ------------------------------------------------------------------------- */
+void Dblqh::openLogfileInit(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::OPENING_INIT;
+ signal->theData[0] = cownref;
+ signal->theData[1] = logFilePtr.i;
+ signal->theData[2] = logFilePtr.p->fileName[0];
+ signal->theData[3] = logFilePtr.p->fileName[1];
+ signal->theData[4] = logFilePtr.p->fileName[2];
+ signal->theData[5] = logFilePtr.p->fileName[3];
+ signal->theData[6] = 0x302;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+}//Dblqh::openLogfileInit()
+
+/* OPEN FOR READ/WRITE, DO CREATE AND DO TRUNCATE FILE */
+/* ------------------------------------------------------------------------- */
+/* ------- OPEN NEXT LOG FILE ------- */
+/* */
+/* SUBROUTINE SHORT NAME = ONL */
+/* ------------------------------------------------------------------------- */
+void Dblqh::openNextLogfile(Signal* signal)
+{
+ LogFileRecordPtr onlLogFilePtr;
+
+ if (logPartPtr.p->noLogFiles > 2) {
+ jam();
+/* -------------------------------------------------- */
+/* IF ONLY 1 OR 2 LOG FILES EXIST THEN THEY ARE */
+/* ALWAYS OPEN AND THUS IT IS NOT NECESSARY TO */
+/* OPEN THEM NOW. */
+/* -------------------------------------------------- */
+ onlLogFilePtr.i = logFilePtr.p->nextLogFile;
+ ptrCheckGuard(onlLogFilePtr, clogFileFileSize, logFileRecord);
+ if (onlLogFilePtr.p->logFileStatus != LogFileRecord::CLOSED) {
+ ndbrequire(onlLogFilePtr.p->fileNo == 0);
+ return;
+ }//if
+ onlLogFilePtr.p->logFileStatus = LogFileRecord::OPENING_WRITE_LOG;
+ signal->theData[0] = cownref;
+ signal->theData[1] = onlLogFilePtr.i;
+ signal->theData[2] = onlLogFilePtr.p->fileName[0];
+ signal->theData[3] = onlLogFilePtr.p->fileName[1];
+ signal->theData[4] = onlLogFilePtr.p->fileName[2];
+ signal->theData[5] = onlLogFilePtr.p->fileName[3];
+ signal->theData[6] = 2;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ }//if
+}//Dblqh::openNextLogfile()
+
+ /* OPEN FOR READ/WRITE, DON'T CREATE AND DON'T TRUNCATE FILE */
+/* ------------------------------------------------------------------------- */
+/* ------- RELEASE LFO RECORD ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::releaseLfo(Signal* signal)
+{
+#ifdef VM_TRACE
+ // Check that lfo record isn't already in free list
+ LogFileOperationRecordPtr TlfoPtr;
+ TlfoPtr.i = cfirstfreeLfo;
+ while (TlfoPtr.i != RNIL){
+ ptrCheckGuard(TlfoPtr, clfoFileSize, logFileOperationRecord);
+ ndbrequire(TlfoPtr.i != lfoPtr.i);
+ TlfoPtr.i = TlfoPtr.p->nextLfo;
+ }
+#endif
+ lfoPtr.p->nextLfo = cfirstfreeLfo;
+ lfoPtr.p->lfoTimer = 0;
+ cfirstfreeLfo = lfoPtr.i;
+ lfoPtr.p->lfoState = LogFileOperationRecord::IDLE;
+}//Dblqh::releaseLfo()
+
+/* ------------------------------------------------------------------------- */
+/* ------- RELEASE ALL LOG PAGES CONNECTED TO A LFO RECORD ------- */
+/* */
+/* SUBROUTINE SHORT NAME = RLP */
+/* ------------------------------------------------------------------------- */
+void Dblqh::releaseLfoPages(Signal* signal)
+{
+ LogPageRecordPtr rlpLogPagePtr;
+
+ logPagePtr.i = lfoPtr.p->firstLfoPage;
+RLP_LOOP:
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ rlpLogPagePtr.i = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ releaseLogpage(signal);
+ if (rlpLogPagePtr.i != RNIL) {
+ jam();
+ logPagePtr.i = rlpLogPagePtr.i;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ goto RLP_LOOP;
+ }//if
+ lfoPtr.p->firstLfoPage = RNIL;
+}//Dblqh::releaseLfoPages()
+
+/* ------------------------------------------------------------------------- */
+/* ------- RELEASE LOG PAGE ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::releaseLogpage(Signal* signal)
+{
+#ifdef VM_TRACE
+ // Check that log page isn't already in free list
+ LogPageRecordPtr TlogPagePtr;
+ TlogPagePtr.i = cfirstfreeLogPage;
+ while (TlogPagePtr.i != RNIL){
+ ptrCheckGuard(TlogPagePtr, clogPageFileSize, logPageRecord);
+ ndbrequire(TlogPagePtr.i != logPagePtr.i);
+ TlogPagePtr.i = TlogPagePtr.p->logPageWord[ZNEXT_PAGE];
+ }
+#endif
+
+ cnoOfLogPages++;
+ logPagePtr.p->logPageWord[ZNEXT_PAGE] = cfirstfreeLogPage;
+ cfirstfreeLogPage = logPagePtr.i;
+}//Dblqh::releaseLogpage()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEIZE LFO RECORD ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::seizeLfo(Signal* signal)
+{
+ lfoPtr.i = cfirstfreeLfo;
+ ptrCheckGuard(lfoPtr, clfoFileSize, logFileOperationRecord);
+ cfirstfreeLfo = lfoPtr.p->nextLfo;
+ lfoPtr.p->nextLfo = RNIL;
+ lfoPtr.p->lfoTimer = cLqhTimeOutCount;
+}//Dblqh::seizeLfo()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEIZE LOG FILE RECORD ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::seizeLogfile(Signal* signal)
+{
+ logFilePtr.i = cfirstfreeLogFile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+/* ------------------------------------------------------------------------- */
+/*IF LIST IS EMPTY THEN A SYSTEM CRASH IS INVOKED SINCE LOG_FILE_PTR = RNIL */
+/* ------------------------------------------------------------------------- */
+ cfirstfreeLogFile = logFilePtr.p->nextLogFile;
+ logFilePtr.p->nextLogFile = RNIL;
+}//Dblqh::seizeLogfile()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEIZE LOG PAGE RECORD ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::seizeLogpage(Signal* signal)
+{
+ cnoOfLogPages--;
+ logPagePtr.i = cfirstfreeLogPage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+/* ------------------------------------------------------------------------- */
+/*IF LIST IS EMPTY THEN A SYSTEM CRASH IS INVOKED SINCE LOG_PAGE_PTR = RNIL */
+/* ------------------------------------------------------------------------- */
+ cfirstfreeLogPage = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ logPagePtr.p->logPageWord[ZNEXT_PAGE] = RNIL;
+}//Dblqh::seizeLogpage()
+
+/* ------------------------------------------------------------------------- */
+/* ------- WRITE FILE DESCRIPTOR INFORMATION ------- */
+/* */
+/* SUBROUTINE SHORT NAME: WFD */
+// Pointer handling:
+// logFilePtr in
+// logPartPtr in
+/* ------------------------------------------------------------------------- */
+void Dblqh::writeFileDescriptor(Signal* signal)
+{
+ TcConnectionrecPtr wfdTcConnectptr;
+ UintR twfdFileNo;
+ UintR twfdMbyte;
+
+/* -------------------------------------------------- */
+/* START BY WRITING TO LOG FILE RECORD */
+/* -------------------------------------------------- */
+ arrGuard(logFilePtr.p->currentMbyte, 16);
+ logFilePtr.p->logMaxGciCompleted[logFilePtr.p->currentMbyte] =
+ logPartPtr.p->logPartNewestCompletedGCI;
+ logFilePtr.p->logMaxGciStarted[logFilePtr.p->currentMbyte] = cnewestGci;
+ wfdTcConnectptr.i = logPartPtr.p->firstLogTcrec;
+ if (wfdTcConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(wfdTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ twfdFileNo = wfdTcConnectptr.p->logStartFileNo;
+ twfdMbyte = wfdTcConnectptr.p->logStartPageNo >> ZTWOLOG_NO_PAGES_IN_MBYTE;
+ logFilePtr.p->logLastPrepRef[logFilePtr.p->currentMbyte] =
+ (twfdFileNo << 16) + twfdMbyte;
+ } else {
+ jam();
+ logFilePtr.p->logLastPrepRef[logFilePtr.p->currentMbyte] =
+ (logFilePtr.p->fileNo << 16) + logFilePtr.p->currentMbyte;
+ }//if
+}//Dblqh::writeFileDescriptor()
+
+/* ------------------------------------------------------------------------- */
+/* ------- WRITE THE HEADER PAGE OF A NEW FILE ------- */
+/* */
+/* SUBROUTINE SHORT NAME: WMO */
+/* ------------------------------------------------------------------------- */
+void Dblqh::writeFileHeaderOpen(Signal* signal, Uint32 wmoType)
+{
+ LogFileRecordPtr wmoLogFilePtr;
+ UintR twmoNoLogDescriptors;
+ UintR twmoLoop;
+ UintR twmoIndex;
+
+/* -------------------------------------------------- */
+/* WRITE HEADER INFORMATION IN THE NEW FILE. */
+/* -------------------------------------------------- */
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_LOG_TYPE] = ZFD_TYPE;
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_FILE_NO] =
+ logFilePtr.p->fileNo;
+ if (logPartPtr.p->noLogFiles > ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ jam();
+ twmoNoLogDescriptors = ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ } else {
+ jam();
+ twmoNoLogDescriptors = logPartPtr.p->noLogFiles;
+ }//if
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_NO_FD] =
+ twmoNoLogDescriptors;
+ wmoLogFilePtr.i = logFilePtr.i;
+ twmoLoop = 0;
+WMO_LOOP:
+ jam();
+ if (twmoLoop < twmoNoLogDescriptors) {
+ jam();
+ ptrCheckGuard(wmoLogFilePtr, clogFileFileSize, logFileRecord);
+ for (twmoIndex = 0; twmoIndex <= ZNO_MBYTES_IN_FILE - 1; twmoIndex++) {
+ jam();
+ arrGuard(((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (twmoLoop * ZFD_PART_SIZE)) + twmoIndex, ZPAGE_SIZE);
+ logPagePtr.p->logPageWord[((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (twmoLoop * ZFD_PART_SIZE)) + twmoIndex] =
+ wmoLogFilePtr.p->logMaxGciCompleted[twmoIndex];
+ arrGuard((((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (twmoLoop * ZFD_PART_SIZE)) + ZNO_MBYTES_IN_FILE) +
+ twmoIndex, ZPAGE_SIZE);
+ logPagePtr.p->logPageWord[(((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (twmoLoop * ZFD_PART_SIZE)) + ZNO_MBYTES_IN_FILE) + twmoIndex] =
+ wmoLogFilePtr.p->logMaxGciStarted[twmoIndex];
+ arrGuard((((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (twmoLoop * ZFD_PART_SIZE)) + (2 * ZNO_MBYTES_IN_FILE)) +
+ twmoIndex, ZPAGE_SIZE);
+ logPagePtr.p->logPageWord[(((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (twmoLoop * ZFD_PART_SIZE)) + (2 * ZNO_MBYTES_IN_FILE)) + twmoIndex] =
+ wmoLogFilePtr.p->logLastPrepRef[twmoIndex];
+ }//for
+ wmoLogFilePtr.i = wmoLogFilePtr.p->prevLogFile;
+ twmoLoop = twmoLoop + 1;
+ goto WMO_LOOP;
+ }//if
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] =
+ (ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (ZFD_PART_SIZE * twmoNoLogDescriptors);
+ arrGuard(logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX], ZPAGE_SIZE);
+ logPagePtr.p->logPageWord[logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX]] =
+ ZNEXT_LOG_RECORD_TYPE;
+/* ------------------------------------------------------- */
+/* THIS IS A SPECIAL WRITE OF THE FIRST PAGE IN THE */
+/* LOG FILE. THIS HAS SPECIAL SIGNIFANCE TO FIND */
+/* THE END OF THE LOG AT SYSTEM RESTART. */
+/* ------------------------------------------------------- */
+ writeSinglePage(signal, 0, ZPAGE_SIZE - 1);
+ if (wmoType == ZINIT) {
+ jam();
+ lfoPtr.p->lfoState = LogFileOperationRecord::INIT_FIRST_PAGE;
+ } else {
+ jam();
+ lfoPtr.p->lfoState = LogFileOperationRecord::FIRST_PAGE_WRITE_IN_LOGFILE;
+ }//if
+ logFilePtr.p->filePosition = 1;
+ if (wmoType == ZNORMAL) {
+ jam();
+/* -------------------------------------------------- */
+/* ALLOCATE A NEW PAGE SINCE THE CURRENT IS */
+/* WRITTEN. */
+/* -------------------------------------------------- */
+ seizeLogpage(signal);
+ initLogpage(signal);
+ logFilePtr.p->currentLogpage = logPagePtr.i;
+ logFilePtr.p->currentFilepage = logFilePtr.p->currentFilepage + 1;
+ }//if
+}//Dblqh::writeFileHeaderOpen()
+
+/* -------------------------------------------------- */
+/* THE NEW FILE POSITION WILL ALWAYS BE 1 SINCE */
+/* WE JUST WROTE THE FIRST PAGE IN THE LOG FILE */
+/* -------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* ------- WRITE A MBYTE HEADER DURING INITIAL START ------- */
+/* */
+/* SUBROUTINE SHORT NAME: WIM */
+/* ------------------------------------------------------------------------- */
+void Dblqh::writeInitMbyte(Signal* signal)
+{
+ initLogpage(signal);
+ writeSinglePage(signal, logFilePtr.p->currentMbyte * ZPAGES_IN_MBYTE, ZPAGE_SIZE - 1);
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_INIT_MBYTE;
+}//Dblqh::writeInitMbyte()
+
+/* ------------------------------------------------------------------------- */
+/* ------- WRITE A SINGLE PAGE INTO A FILE ------- */
+/* */
+/* INPUT: TWSP_PAGE_NO THE PAGE NUMBER WRITTEN */
+/* SUBROUTINE SHORT NAME: WSP */
+/* ------------------------------------------------------------------------- */
+void Dblqh::writeSinglePage(Signal* signal, Uint32 pageNo, Uint32 wordWritten)
+{
+ seizeLfo(signal);
+ initLfo(signal);
+ lfoPtr.p->firstLfoPage = logPagePtr.i;
+ logPagePtr.p->logPageWord[ZNEXT_PAGE] = RNIL;
+
+ // Calculate checksum for page
+ logPagePtr.p->logPageWord[ZPOS_CHECKSUM] = calcPageCheckSum(logPagePtr);
+
+ lfoPtr.p->lfoPageNo = pageNo;
+ lfoPtr.p->lfoWordWritten = wordWritten;
+ lfoPtr.p->noPagesRw = 1;
+/* -------------------------------------------------- */
+/* SET TIMER ON THIS LOG PART TO SIGNIFY THAT A */
+/* LOG RECORD HAS BEEN SENT AT THIS TIME. */
+/* -------------------------------------------------- */
+ logPartPtr.p->logPartTimer = logPartPtr.p->logTimer;
+ signal->theData[0] = logFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lfoPtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS_SYNCH;
+ signal->theData[4] = ZVAR_NO_LOG_PAGE_WORD;
+ signal->theData[5] = 1; /* ONE PAGE WRITTEN */
+ signal->theData[6] = logPagePtr.i;
+ signal->theData[7] = pageNo;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+}//Dblqh::writeSinglePage()
+
+/* ##########################################################################
+ * SYSTEM RESTART PHASE ONE MODULE
+ * THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING.
+ *
+ * THIS MODULE CONTAINS THE CODE FOR THE FIRST PHASE OF THE SYSTEM RESTART.
+ * THE AIM OF THIS PHASE IS TO FIND THE END OF THE LOG AND TO FIND
+ * INFORMATION ABOUT WHERE GLOBAL CHECKPOINTS ARE COMPLETED AND STARTED
+ * IN THE LOG. THIS INFORMATION IS NEEDED TO START PHASE THREE OF
+ * THE SYSTEM RESTART.
+ * ########################################################################## */
+/* --------------------------------------------------------------------------
+ * A SYSTEM RESTART OR NODE RESTART IS ONGOING. WE HAVE NOW OPENED FILE 0
+ * NOW WE NEED TO READ PAGE 0 TO FIND WHICH LOG FILE THAT WAS OPEN AT
+ * CRASH TIME.
+ * -------------------------------------------------------------------------- */
+void Dblqh::openSrFrontpageLab(Signal* signal)
+{
+ readSinglePage(signal, 0);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_FRONTPAGE;
+ return;
+}//Dblqh::openSrFrontpageLab()
+
+/* -------------------------------------------------------------------------
+ * WE HAVE NOW READ PAGE 0 IN FILE 0. CHECK THE LAST OPEN FILE. ACTUALLY THE
+ * LAST OPEN FILE COULD BE THE NEXT AFTER THAT. CHECK THAT FIRST. WHEN THE
+ * LAST WAS FOUND WE CAN FIND ALL THE NEEDED INFORMATION WHERE TO START AND
+ * STOP READING THE LOG.
+ * -------------------------------------------------------------------------- */
+void Dblqh::readSrFrontpageLab(Signal* signal)
+{
+ Uint32 fileNo = logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_FILE_NO];
+ if (fileNo == 0) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * FILE 0 WAS ALSO LAST FILE SO WE DO NOT NEED TO READ IT AGAIN.
+ * ---------------------------------------------------------------------- */
+ readSrLastFileLab(signal);
+ return;
+ }//if
+ /* ------------------------------------------------------------------------
+ * CLOSE FILE 0 SO THAT WE HAVE CLOSED ALL FILES WHEN STARTING TO READ
+ * THE FRAGMENT LOG. ALSO RELEASE PAGE ZERO.
+ * ------------------------------------------------------------------------ */
+ releaseLogpage(signal);
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_SR;
+ closeFile(signal, logFilePtr);
+ LogFileRecordPtr locLogFilePtr;
+ findLogfile(signal, fileNo, logPartPtr, &locLogFilePtr);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_LAST_FILE;
+ openFileRw(signal, locLogFilePtr);
+ return;
+}//Dblqh::readSrFrontpageLab()
+
+void Dblqh::openSrLastFileLab(Signal* signal)
+{
+ readSinglePage(signal, 0);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_LAST_FILE;
+ return;
+}//Dblqh::openSrLastFileLab()
+
+void Dblqh::readSrLastFileLab(Signal* signal)
+{
+ logPartPtr.p->logLap = logPagePtr.p->logPageWord[ZPOS_LOG_LAP];
+ if (logPartPtr.p->noLogFiles > ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ jam();
+ initGciInLogFileRec(signal, ZMAX_LOG_FILES_IN_PAGE_ZERO);
+ } else {
+ jam();
+ initGciInLogFileRec(signal, logPartPtr.p->noLogFiles);
+ }//if
+ releaseLogpage(signal);
+ /* ------------------------------------------------------------------------
+ * NOW WE HAVE FOUND THE LAST LOG FILE. WE ALSO NEED TO FIND THE LAST
+ * MBYTE THAT WAS LAST WRITTEN BEFORE THE SYSTEM CRASH.
+ * ------------------------------------------------------------------------ */
+ logPartPtr.p->lastLogfile = logFilePtr.i;
+ readSinglePage(signal, 0);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_LAST_MBYTE;
+ logFilePtr.p->currentMbyte = 0;
+ return;
+}//Dblqh::readSrLastFileLab()
+
+void Dblqh::readSrLastMbyteLab(Signal* signal)
+{
+ if (logPartPtr.p->lastMbyte == ZNIL) {
+ if (logPagePtr.p->logPageWord[ZPOS_LOG_LAP] < logPartPtr.p->logLap) {
+ jam();
+ logPartPtr.p->lastMbyte = logFilePtr.p->currentMbyte - 1;
+ }//if
+ }//if
+ arrGuard(logFilePtr.p->currentMbyte, 16);
+ logFilePtr.p->logMaxGciCompleted[logFilePtr.p->currentMbyte] =
+ logPagePtr.p->logPageWord[ZPOS_MAX_GCI_COMPLETED];
+ logFilePtr.p->logMaxGciStarted[logFilePtr.p->currentMbyte] =
+ logPagePtr.p->logPageWord[ZPOS_MAX_GCI_STARTED];
+ logFilePtr.p->logLastPrepRef[logFilePtr.p->currentMbyte] =
+ logPagePtr.p->logPageWord[ZLAST_LOG_PREP_REF];
+ releaseLogpage(signal);
+ if (logFilePtr.p->currentMbyte < (ZNO_MBYTES_IN_FILE - 1)) {
+ jam();
+ logFilePtr.p->currentMbyte++;
+ readSinglePage(signal, ZPAGES_IN_MBYTE * logFilePtr.p->currentMbyte);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_LAST_MBYTE;
+ return;
+ } else {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THE LOG WAS IN THE LAST MBYTE WHEN THE CRASH OCCURRED SINCE ALL
+ * LOG LAPS ARE EQUAL TO THE CURRENT LOG LAP.
+ * ---------------------------------------------------------------------- */
+ if (logPartPtr.p->lastMbyte == ZNIL) {
+ jam();
+ logPartPtr.p->lastMbyte = ZNO_MBYTES_IN_FILE - 1;
+ }//if
+ }//if
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_SR;
+ closeFile(signal, logFilePtr);
+ if (logPartPtr.p->noLogFiles > ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ Uint32 fileNo;
+ if (logFilePtr.p->fileNo >= ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ jam();
+ fileNo = logFilePtr.p->fileNo - ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ } else {
+ jam();
+ fileNo =
+ (logPartPtr.p->noLogFiles + logFilePtr.p->fileNo) -
+ ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ }//if
+ if (fileNo == 0) {
+ jam();
+ /* --------------------------------------------------------------------
+ * AVOID USING FILE 0 AGAIN SINCE THAT IS PROBABLY CLOSING AT THE
+ * MOMENT.
+ * -------------------------------------------------------------------- */
+ fileNo = 1;
+ logPartPtr.p->srRemainingFiles =
+ logPartPtr.p->noLogFiles - (ZMAX_LOG_FILES_IN_PAGE_ZERO - 1);
+ } else {
+ jam();
+ logPartPtr.p->srRemainingFiles =
+ logPartPtr.p->noLogFiles - ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ }//if
+ LogFileRecordPtr locLogFilePtr;
+ findLogfile(signal, fileNo, logPartPtr, &locLogFilePtr);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_NEXT_FILE;
+ openFileRw(signal, locLogFilePtr);
+ return;
+ }//if
+ /* ------------------------------------------------------------------------
+ * THERE WERE NO NEED TO READ ANY MORE PAGE ZERO IN OTHER FILES.
+ * WE NOW HAVE ALL THE NEEDED INFORMATION ABOUT THE GCI'S THAT WE NEED.
+ * NOW JUST WAIT FOR CLOSE OPERATIONS TO COMPLETE.
+ * ------------------------------------------------------------------------ */
+ return;
+}//Dblqh::readSrLastMbyteLab()
+
+void Dblqh::openSrNextFileLab(Signal* signal)
+{
+ readSinglePage(signal, 0);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_NEXT_FILE;
+ return;
+}//Dblqh::openSrNextFileLab()
+
+void Dblqh::readSrNextFileLab(Signal* signal)
+{
+ if (logPartPtr.p->srRemainingFiles > ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ jam();
+ initGciInLogFileRec(signal, ZMAX_LOG_FILES_IN_PAGE_ZERO);
+ } else {
+ jam();
+ initGciInLogFileRec(signal, logPartPtr.p->srRemainingFiles);
+ }//if
+ releaseLogpage(signal);
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_SR;
+ closeFile(signal, logFilePtr);
+ if (logPartPtr.p->srRemainingFiles > ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ Uint32 fileNo;
+ if (logFilePtr.p->fileNo >= ZMAX_LOG_FILES_IN_PAGE_ZERO) {
+ jam();
+ fileNo = logFilePtr.p->fileNo - ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ } else {
+ jam();
+ fileNo =
+ (logPartPtr.p->noLogFiles + logFilePtr.p->fileNo) -
+ ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ }//if
+ if (fileNo == 0) {
+ jam();
+ /* --------------------------------------------------------------------
+ * AVOID USING FILE 0 AGAIN SINCE THAT IS PROBABLY CLOSING AT THE MOMENT.
+ * -------------------------------------------------------------------- */
+ fileNo = 1;
+ logPartPtr.p->srRemainingFiles =
+ logPartPtr.p->srRemainingFiles - (ZMAX_LOG_FILES_IN_PAGE_ZERO - 1);
+ } else {
+ jam();
+ logPartPtr.p->srRemainingFiles =
+ logPartPtr.p->srRemainingFiles - ZMAX_LOG_FILES_IN_PAGE_ZERO;
+ }//if
+ LogFileRecordPtr locLogFilePtr;
+ findLogfile(signal, fileNo, logPartPtr, &locLogFilePtr);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_NEXT_FILE;
+ openFileRw(signal, locLogFilePtr);
+ }//if
+ /* ------------------------------------------------------------------------
+ * THERE WERE NO NEED TO READ ANY MORE PAGE ZERO IN OTHER FILES.
+ * WE NOW HAVE ALL THE NEEDED INFORMATION ABOUT THE GCI'S THAT WE NEED.
+ * NOW JUST WAIT FOR CLOSE OPERATIONS TO COMPLETE.
+ * ------------------------------------------------------------------------ */
+ return;
+}//Dblqh::readSrNextFileLab()
+
+void Dblqh::closingSrLab(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ logPartPtr.i = logFilePtr.p->logPartRec;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logFilePtr.i = logPartPtr.p->firstLogfile;
+ do {
+ jam();
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ if (logFilePtr.p->logFileStatus != LogFileRecord::CLOSED) {
+ jam();
+ /* --------------------------------------------------------------------
+ * EXIT AND WAIT FOR REMAINING LOG FILES TO COMPLETE THEIR WORK.
+ * -------------------------------------------------------------------- */
+ return;
+ }//if
+ logFilePtr.i = logFilePtr.p->nextLogFile;
+ } while (logFilePtr.i != logPartPtr.p->firstLogfile);
+ /* ------------------------------------------------------------------------
+ * ALL FILES IN THIS PART HAVE BEEN CLOSED. THIS INDICATES THAT THE FIRST
+ * PHASE OF THE SYSTEM RESTART HAVE BEEN CONCLUDED FOR THIS LOG PART.
+ * CHECK IF ALL OTHER LOG PARTS ARE ALSO COMPLETED.
+ * ------------------------------------------------------------------------ */
+ logPartPtr.p->logPartState = LogPartRecord::SR_FIRST_PHASE_COMPLETED;
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ if (logPartPtr.p->logPartState != LogPartRecord::SR_FIRST_PHASE_COMPLETED) {
+ jam();
+ /* --------------------------------------------------------------------
+ * EXIT AND WAIT FOR THE REST OF THE LOG PARTS TO COMPLETE.
+ * -------------------------------------------------------------------- */
+ return;
+ }//if
+ }//for
+ /* ------------------------------------------------------------------------
+ * THE FIRST PHASE HAVE BEEN COMPLETED.
+ * ------------------------------------------------------------------------ */
+ signal->theData[0] = ZSR_PHASE3_START;
+ signal->theData[1] = ZSR_PHASE1_COMPLETED;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//Dblqh::closingSrLab()
+
+/* ##########################################################################
+ * ####### SYSTEM RESTART PHASE TWO MODULE #######
+ *
+ * THIS MODULE HANDLES THE SYSTEM RESTART WHERE LQH CONTROLS TUP AND ACC TO
+ * ENSURE THAT THEY HAVE KNOWLEDGE OF ALL FRAGMENTS AND HAVE DONE THE NEEDED
+ * READING OF DATA FROM FILE AND EXECUTION OF LOCAL LOGS. THIS PROCESS
+ * EXECUTES CONCURRENTLY WITH PHASE ONE OF THE SYSTEM RESTART. THIS PHASE
+ * FINDS THE INFORMATION ABOUT THE FRAGMENT LOG NEEDED TO EXECUTE THE FRAGMENT
+ * LOG.
+ * WHEN TUP AND ACC HAVE PREPARED ALL FRAGMENTS THEN LQH ORDERS THOSE LQH'S
+ * THAT ARE RESPONSIBLE TO EXECUTE THE FRAGMENT LOGS TO DO SO. IT IS POSSIBLE
+ * THAT ANOTHER NODE EXECUTES THE LOG FOR A FRAGMENT RESIDING AT THIS NODE.
+ * ########################################################################## */
+/* ***************>> */
+/* START_FRAGREQ > */
+/* ***************>> */
+void Dblqh::execSTART_FRAGREQ(Signal* signal)
+{
+ const StartFragReq * const startFragReq = (StartFragReq *)&signal->theData[0];
+ jamEntry();
+
+ tabptr.i = startFragReq->tableId;
+ Uint32 fragId = startFragReq->fragId;
+
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ if (!getFragmentrec(signal, fragId)) {
+ startFragRefLab(signal);
+ return;
+ }//if
+ tabptr.p->tableStatus = Tablerec::TABLE_DEFINED;
+
+ initFragrecSr(signal);
+ if (startFragReq->lcpNo == ZNIL) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THERE WAS NO LOCAL CHECKPOINT AVAILABLE FOR THIS FRAGMENT. WE DO
+ * NOT NEED TO READ IN THE LOCAL FRAGMENT. WE HAVE ALREADY ADDED THE
+ * FRAGMENT AS AN EMPTY FRAGMENT AT THIS POINT. THUS WE CAN SIMPLY
+ * EXIT AND THE FRAGMENT WILL PARTICIPATE IN THE EXECUTION OF THE LOG.
+ * PUT FRAGMENT ON LIST OF COMPLETED FRAGMENTS FOR EXECUTION OF LOG.
+ * ---------------------------------------------------------------------- */
+ fragptr.p->nextFrag = cfirstCompletedFragSr;
+ cfirstCompletedFragSr = fragptr.i;
+ return;
+ }//if
+ if (cfirstWaitFragSr == RNIL) {
+ jam();
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_IDLE) {
+ jam();
+ initLcpSr(signal, startFragReq->lcpNo,
+ startFragReq->lcpId, tabptr.i,
+ fragId, fragptr.i);
+ signal->theData[0] = lcpPtr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lcpPtr.p->currentFragment.lcpFragOrd.lcpNo;
+ signal->theData[3] = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ signal->theData[4] = lcpPtr.p->currentFragment.lcpFragOrd.fragmentId;
+ sendSignal(fragptr.p->accBlockref, GSN_SR_FRAGIDREQ, signal, 5, JBB);
+ return;
+ }//if
+ }//if
+ fragptr.p->nextFrag = cfirstWaitFragSr;
+ cfirstWaitFragSr = fragptr.i;
+}//Dblqh::execSTART_FRAGREQ()
+
+void Dblqh::startFragRefLab(Signal* signal)
+{
+ const StartFragReq * const startFragReq = (StartFragReq *)&signal->theData[0];
+ BlockReference userRef = startFragReq->userRef;
+ Uint32 userPtr = startFragReq->userPtr;
+ signal->theData[0] = userPtr;
+ signal->theData[1] = terrorCode;
+ signal->theData[2] = cownNodeid;
+ sendSignal(userRef, GSN_START_FRAGREF, signal, 3, JBB);
+ return;
+}//Dblqh::startFragRefLab()
+
+/* ***************>> */
+/* SR_FRAGIDCONF > */
+/* ***************>> */
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_PTR:LCP_STATE = SR_WAIT_FRAGID
+ * -------------------------------------------------------------------------- */
+void Dblqh::execSR_FRAGIDCONF(Signal* signal)
+{
+ SrFragidConf * const srFragidConf = (SrFragidConf *)&signal->theData[0];
+ jamEntry();
+
+ lcpPtr.i = srFragidConf->lcpPtr;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ ndbrequire(lcpPtr.p->lcpState == LcpRecord::LCP_SR_WAIT_FRAGID);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECKING OF TNO_LOCFRAG VALUE. OUT OF BOUND WILL IMPLY THAT AN
+ * INDEX OUT OF RANGE WILL CAUSE A SYSTEM RESTART WHICH IS DESIRED.
+ * ------------------------------------------------------------------------ */
+ lcpPtr.p->lcpAccptr = srFragidConf->accPtr;
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->accFragptr[0] = srFragidConf->fragPtr[0];
+ fragptr.p->accFragptr[1] = srFragidConf->fragPtr[1];
+ Uint32 noLocFrag = srFragidConf->noLocFrag;
+ ndbrequire(noLocFrag == 2);
+ Uint32 fragid[2];
+ Uint32 i;
+ for (i = 0; i < noLocFrag; i++) {
+ fragid[i] = srFragidConf->fragId[i];
+ }//for
+
+ for (i = 0; i < noLocFrag; i++) {
+ jam();
+ Uint32 fragId = fragid[i];
+ /* ----------------------------------------------------------------------
+ * THERE IS NO ERROR CHECKING ON PURPOSE. IT IS POSSIBLE TO CALCULATE HOW
+ * MANY LOCAL LCP RECORDS THERE SHOULD BE. IT SHOULD NEVER HAPPEN THAT
+ * THERE IS NO ONE FREE. IF THERE IS NO ONE IT WILL ALSO BE A POINTER
+ * OUT OF RANGE WHICH IS AN ERROR CODE IN ITSELF. REUSES ERROR
+ * HANDLING IN AXE VM.
+ * ---------------------------------------------------------------------- */
+ seizeLcpLoc(signal);
+ initLcpLocAcc(signal, fragId);
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::SR_ACC_STARTED;
+ signal->theData[0] = lcpPtr.p->lcpAccptr;
+ signal->theData[1] = lcpLocptr.i;
+ signal->theData[2] = lcpLocptr.p->locFragid;
+ signal->theData[3] = lcpPtr.p->currentFragment.lcpFragOrd.lcpId % MAX_LCP_STORED;
+ sendSignal(fragptr.p->accBlockref, GSN_ACC_SRREQ, signal, 4, JBB);
+ seizeLcpLoc(signal);
+ initLcpLocTup(signal, fragId);
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::SR_TUP_STARTED;
+ signal->theData[0] = lcpLocptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ signal->theData[3] = lcpLocptr.p->locFragid;
+ signal->theData[4] = lcpPtr.p->currentFragment.lcpFragOrd.lcpNo;
+ sendSignal(fragptr.p->tupBlockref, GSN_TUP_SRREQ, signal, 5, JBB);
+ }//for
+ lcpPtr.p->lcpState = LcpRecord::LCP_SR_STARTED;
+ return;
+}//Dblqh::execSR_FRAGIDCONF()
+
+/* ***************> */
+/* SR_FRAGIDREF > */
+/* ***************> */
+void Dblqh::execSR_FRAGIDREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execSR_FRAGIDREF()
+
+/* ************>> */
+/* ACC_SRCONF > */
+/* ************>> */
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = SR_ACC_STARTED
+ * -------------------------------------------------------------------------- */
+void Dblqh::execACC_SRCONF(Signal* signal)
+{
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (lcpLocptr.p->lcpLocstate != LcpLocRecord::SR_ACC_STARTED) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN THIS REFERENCE
+ * WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A SYSTEM RESTART.
+ * ------------------------------------------------------------------------ */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::SR_ACC_COMPLETED;
+ srCompletedLab(signal);
+ return;
+}//Dblqh::execACC_SRCONF()
+
+/* ************> */
+/* ACC_SRREF > */
+/* ************> */
+void Dblqh::execACC_SRREF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execACC_SRREF()
+
+/* ************>> */
+/* TUP_SRCONF > */
+/* ************>> */
+/* --------------------------------------------------------------------------
+ * PRECONDITION: LCP_LOCPTR:LCP_LOCSTATE = SR_TUP_STARTED
+ * -------------------------------------------------------------------------- */
+void Dblqh::execTUP_SRCONF(Signal* signal)
+{
+ jamEntry();
+ lcpLocptr.i = signal->theData[0];
+ ptrCheckGuard(lcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ Uint32 tupFragPtr = signal->theData[1];
+ ndbrequire(lcpLocptr.p->lcpLocstate == LcpLocRecord::SR_TUP_STARTED);
+
+ lcpPtr.i = lcpLocptr.p->masterLcpRec;
+ ptrCheckGuard(lcpPtr, clcpFileSize, lcpRecord);
+ /* ------------------------------------------------------------------------
+ * NO ERROR CHECK ON USING VALUE IN MASTER_LCP_REC. ERROR IN THIS REFERENCE
+ * WILL CAUSE POINTER OUT OF RANGE WHICH CAUSES A SYSTEM RESTART.
+ * ------------------------------------------------------------------------ */
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::SR_TUP_COMPLETED;
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (lcpLocptr.i == lcpPtr.p->firstLcpLocTup) {
+ jam();
+ fragptr.p->tupFragptr[1] = tupFragPtr;
+ } else {
+ jam();
+ fragptr.p->tupFragptr[0] = tupFragPtr;
+ }//if
+ srCompletedLab(signal);
+ return;
+}//Dblqh::execTUP_SRCONF()
+
+void Dblqh::srCompletedLab(Signal* signal)
+{
+ checkSrCompleted(signal);
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_SR_COMPLETED) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THE SYSTEM RESTART OF THIS FRAGMENT HAS BEEN COMPLETED. IT IS NOW
+ * TIME TO START A SYSTEM RESTART ON THE NEXT FRAGMENT OR CONTINUE
+ * WITH THE NEXT STEP OF THE SYSTEM RESTART. THIS STEP IS TO EXECUTE
+ * THE FRAGMENT LOGS.
+ * ----------------------------------------------------------------------
+ * WE RELEASE THE LOCAL LCP RECORDS.
+ * --------------------------------------------------------------------- */
+ releaseLocalLcps(signal);
+ /* ----------------------------------------------------------------------
+ * PUT FRAGMENT ON LIST OF FRAGMENTS WHICH HAVE BEEN STARTED AS PART OF
+ * THE SYSTEM RESTART. THEY ARE NOW WAITING TO EXECUTE THE FRAGMENT LOG.
+ * --------------------------------------------------------------------- */
+ fragptr.i = lcpPtr.p->currentFragment.fragPtrI;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->nextFrag = cfirstCompletedFragSr;
+ cfirstCompletedFragSr = fragptr.i;
+ if (cfirstWaitFragSr != RNIL) {
+ jam();
+ /* --------------------------------------------------------------------
+ * ANOTHER FRAGMENT IS WAITING FOR SYSTEM RESTART. RESTART THIS
+ * FRAGMENT AS WELL.
+ * -------------------------------------------------------------------- */
+ fragptr.i = cfirstWaitFragSr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ cfirstWaitFragSr = fragptr.p->nextFrag;
+ /* --------------------------------------------------------------------
+ * RETRIEVE DATA FROM THE FRAGMENT RECORD.
+ * -------------------------------------------------------------------- */
+ ndbrequire(fragptr.p->srChkpnr < MAX_LCP_STORED);
+ initLcpSr(signal,
+ fragptr.p->srChkpnr,
+ fragptr.p->lcpId[fragptr.p->srChkpnr],
+ fragptr.p->tabRef,
+ fragptr.p->fragId,
+ fragptr.i);
+ signal->theData[0] = lcpPtr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lcpPtr.p->currentFragment.lcpFragOrd.lcpNo;
+ signal->theData[3] = lcpPtr.p->currentFragment.lcpFragOrd.tableId;
+ signal->theData[4] = lcpPtr.p->currentFragment.lcpFragOrd.fragmentId;
+ sendSignal(fragptr.p->accBlockref, GSN_SR_FRAGIDREQ, signal, 5, JBB);
+ return;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * NO MORE FRAGMENTS ARE WAITING FOR SYSTEM RESTART.
+ * -------------------------------------------------------------------- */
+ lcpPtr.p->lcpState = LcpRecord::LCP_IDLE;
+ if (cstartRecReq == ZTRUE) {
+ jam();
+ /* ----------------------------------------------------------------
+ * WE HAVE ALSO RECEIVED AN INDICATION THAT NO MORE FRAGMENTS
+ * NEEDS RESTART.
+ * NOW IT IS TIME TO START EXECUTING THE UNDO LOG.
+ * ----------------------------------------------------------------
+ * WE ARE NOW IN A POSITION TO ORDER TUP AND ACC TO START
+ * EXECUTING THEIR UNDO LOGS. THIS MUST BE DONE BEFORE THE
+ * FRAGMENT LOGS CAN BE EXECUTED.
+ * ---------------------------------------------------------------- */
+ csrExecUndoLogState = EULS_STARTED;
+ signal->theData[0] = caccBlockref;
+ signal->theData[1] = cownref;
+ sendSignal(caccBlockref, GSN_START_RECREQ, signal, 2, JBB);
+ signal->theData[0] = ctupBlockref;
+ signal->theData[1] = cownref;
+ sendSignal(ctupBlockref, GSN_START_RECREQ, signal, 2, JBB);
+ return;
+ } else {
+ jam();
+ /* ----------------------------------------------------------------
+ * WE HAVE NOT RECEIVED ALL FRAGMENTS YET OR AT LEAST NOT WE
+ * HAVE NOT RECEIVED THE START_RECREQ SIGNAL. EXIT AND WAIT
+ * FOR MORE.
+ * ---------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//if
+ /*---------------*/
+ /* ELSE */
+ /*-------------------------------------------------------------------------
+ * THE SYSTEM RESTART ON THIS FRAGMENT HAS NOT BEEN COMPLETED,
+ * EXIT AND WAIT FOR MORE SIGNALS
+ *-------------------------------------------------------------------------
+ * DO NOTHING, EXIT IS EXECUTED BELOW
+ *------------------------------------------------------------------------- */
+ return;
+}//Dblqh::srCompletedLab()
+
+/* ************> */
+/* TUP_SRREF > */
+/* ************> */
+void Dblqh::execTUP_SRREF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execTUP_SRREF()
+
+/* ***************> */
+/* START_RECREQ > */
+/* ***************> */
+void Dblqh::execSTART_RECREQ(Signal* signal)
+{
+ CRASH_INSERTION(5027);
+
+ jamEntry();
+ StartRecReq * const req = (StartRecReq*)&signal->theData[0];
+ cmasterDihBlockref = req->senderRef;
+
+ crestartOldestGci = req->keepGci;
+ crestartNewestGci = req->lastCompletedGci;
+ cnewestGci = req->newestGci;
+
+ ndbrequire(req->receivingNodeId == cownNodeid);
+
+ cnewestCompletedGci = cnewestGci;
+ cstartRecReq = ZTRUE;
+ for (logPartPtr.i = 0; logPartPtr.i < 4; logPartPtr.i++) {
+ ptrAss(logPartPtr, logPartRecord);
+ logPartPtr.p->logPartNewestCompletedGCI = cnewestCompletedGci;
+ }//for
+ /* ------------------------------------------------------------------------
+ * WE HAVE TO SET THE OLDEST AND THE NEWEST GLOBAL CHECKPOINT IDENTITY
+ * THAT WILL SURVIVE THIS SYSTEM RESTART. THIS IS NEEDED SO THAT WE CAN
+ * SET THE LOG HEAD AND LOG TAIL PROPERLY BEFORE STARTING THE SYSTEM AGAIN.
+ * WE ALSO NEED TO SET CNEWEST_GCI TO ENSURE THAT LOG RECORDS ARE EXECUTED
+ * WITH A PROPER GCI.
+ *------------------------------------------------------------------------ */
+ if (cstartType == NodeState::ST_NODE_RESTART) {
+ jam();
+ signal->theData[0] = ZSR_PHASE3_START;
+ signal->theData[1] = ZSR_PHASE2_COMPLETED;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+ if(cstartType == NodeState::ST_INITIAL_NODE_RESTART){
+ jam();
+ StartRecConf * conf = (StartRecConf*)signal->getDataPtrSend();
+ conf->startingNodeId = getOwnNodeId();
+ sendSignal(cmasterDihBlockref, GSN_START_RECCONF, signal,
+ StartRecConf::SignalLength, JBB);
+ return;
+ }//if
+ if (cfirstWaitFragSr == RNIL) {
+ /* ----------------------------------------------------------------------
+ * THERE ARE NO FRAGMENTS WAITING TO BE RESTARTED.
+ * --------------------------------------------------------------------- */
+ lcpPtr.i = 0;
+ ptrAss(lcpPtr, lcpRecord);
+ if (lcpPtr.p->lcpState == LcpRecord::LCP_IDLE) {
+ jam();
+ /* --------------------------------------------------------------------
+ * THERE ARE NO FRAGMENTS THAT ARE CURRENTLY PERFORMING THEIR
+ * SYSTEM RESTART.
+ * --------------------------------------------------------------------
+ * WE ARE NOW IN A POSITION TO ORDER TUP AND ACC TO START EXECUTING
+ * THEIR UNDO LOGS. THIS MUST BE DONE BEFORE THE FRAGMENT LOGS
+ * CAN BE EXECUTED.
+ * ------------------------------------------------------------------- */
+ csrExecUndoLogState = EULS_STARTED;
+ signal->theData[0] = caccBlockref;
+ signal->theData[1] = cownref;
+ sendSignal(caccBlockref, GSN_START_RECREQ, signal, 2, JBB);
+ signal->theData[0] = ctupBlockref;
+ signal->theData[1] = cownref;
+ sendSignal(ctupBlockref, GSN_START_RECREQ, signal, 2, JBB);
+ }//if
+ }//if
+ /* -----------------------------------------------------------------------
+ * EXIT AND WAIT FOR COMPLETION OF ALL FRAGMENTS.
+ * ----------------------------------------------------------------------- */
+ return;
+}//Dblqh::execSTART_RECREQ()
+
+/* ***************>> */
+/* START_RECCONF > */
+/* ***************>> */
+void Dblqh::execSTART_RECCONF(Signal* signal)
+{
+ jamEntry();
+ BlockReference userRef = signal->theData[0];
+ if (userRef == caccBlockref) {
+ if (csrExecUndoLogState == EULS_STARTED) {
+ jam();
+ csrExecUndoLogState = EULS_ACC_COMPLETED;
+ } else {
+ ndbrequire(csrExecUndoLogState == EULS_TUP_COMPLETED);
+ jam();
+ csrExecUndoLogState = EULS_COMPLETED;
+ /* --------------------------------------------------------------------
+ * START THE FIRST PHASE OF EXECUTION OF THE LOG.
+ * ------------------------------------------------------------------- */
+ startExecSr(signal);
+ }//if
+ } else {
+ ndbrequire(userRef == ctupBlockref);
+ if (csrExecUndoLogState == EULS_STARTED) {
+ jam();
+ csrExecUndoLogState = EULS_TUP_COMPLETED;
+ } else {
+ ndbrequire(csrExecUndoLogState == EULS_ACC_COMPLETED);
+ jam();
+ csrExecUndoLogState = EULS_COMPLETED;
+ /* --------------------------------------------------------------------
+ * START THE FIRST PHASE OF EXECUTION OF THE LOG.
+ * ------------------------------------------------------------------- */
+ startExecSr(signal);
+ }//if
+ }//if
+ return;
+}//Dblqh::execSTART_RECCONF()
+
+/* ***************> */
+/* START_RECREF > */
+/* ***************> */
+void Dblqh::execSTART_RECREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}//Dblqh::execSTART_RECREF()
+
+/* ***************>> */
+/* START_EXEC_SR > */
+/* ***************>> */
+void Dblqh::execSTART_EXEC_SR(Signal* signal)
+{
+ FragrecordPtr prevFragptr;
+ jamEntry();
+ fragptr.i = signal->theData[0];
+ prevFragptr.i = signal->theData[1];
+ if (fragptr.i == RNIL) {
+ jam();
+ ndbrequire(cnoOfNodes < MAX_NDB_NODES);
+ /* ----------------------------------------------------------------------
+ * NO MORE FRAGMENTS TO START EXECUTING THE LOG ON.
+ * SEND EXEC_SRREQ TO ALL LQH TO INDICATE THAT THIS NODE WILL
+ * NOT REQUEST ANY MORE FRAGMENTS TO EXECUTE THE FRAGMENT LOG ON.
+ * ----------------------------------------------------------------------
+ * WE NEED TO SEND THOSE SIGNALS EVEN IF WE HAVE NOT REQUESTED
+ * ANY FRAGMENTS PARTICIPATE IN THIS PHASE.
+ * --------------------------------------------------------------------- */
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ if (cnodeStatus[i] == ZNODE_UP) {
+ jam();
+ ndbrequire(cnodeData[i] < MAX_NDB_NODES);
+ BlockReference ref = calcLqhBlockRef(cnodeData[i]);
+ signal->theData[0] = cownNodeid;
+ sendSignal(ref, GSN_EXEC_SRREQ, signal, 1, JBB);
+ }//if
+ }//for
+ } else {
+ jam();
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (fragptr.p->srNoLognodes > csrPhasesCompleted) {
+ jam();
+ Uint32 index = csrPhasesCompleted;
+ arrGuard(index, 4);
+ BlockReference ref = calcLqhBlockRef(fragptr.p->srLqhLognode[index]);
+ fragptr.p->srStatus = Fragrecord::SS_STARTED;
+ /* --------------------------------------------------------------------
+ * SINCE WE CAN HAVE SEVERAL LQH NODES PER FRAGMENT WE CALCULATE
+ * THE LQH POINTER IN SUCH A WAY THAT WE CAN DEDUCE WHICH OF THE
+ * LQH NODES THAT HAS RESPONDED WHEN EXEC_FRAGCONF IS RECEIVED.
+ * ------------------------------------------------------------------- */
+ ExecFragReq * const execFragReq = (ExecFragReq *)&signal->theData[0];
+ execFragReq->userPtr = fragptr.i;
+ execFragReq->userRef = cownref;
+ execFragReq->tableId = fragptr.p->tabRef;
+ execFragReq->fragId = fragptr.p->fragId;
+ execFragReq->startGci = fragptr.p->srStartGci[index];
+ execFragReq->lastGci = fragptr.p->srLastGci[index];
+ sendSignal(ref, GSN_EXEC_FRAGREQ, signal, ExecFragReq::SignalLength, JBB);
+ prevFragptr.i = fragptr.i;
+ fragptr.i = fragptr.p->nextFrag;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * THIS FRAGMENT IS NOW FINISHED WITH THE SYSTEM RESTART. IT DOES
+ * NOT NEED TO PARTICIPATE IN ANY MORE PHASES. REMOVE IT FROM THE
+ * LIST OF COMPLETED FRAGMENTS TO EXECUTE THE LOG ON.
+ * ALSO SEND START_FRAGCONF TO DIH AND SET THE STATE TO ACTIVE ON THE
+ * FRAGMENT.
+ * ------------------------------------------------------------------- */
+ Uint32 next = fragptr.p->nextFrag;
+ if (prevFragptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(prevFragptr, cfragrecFileSize, fragrecord);
+ prevFragptr.p->nextFrag = next;
+ } else {
+ jam();
+ cfirstCompletedFragSr = next;
+ }//if
+
+ /**
+ * Put fragment on list which has completed REDO log
+ */
+ fragptr.p->nextFrag = c_redo_log_complete_frags;
+ c_redo_log_complete_frags = fragptr.i;
+
+ fragptr.p->fragStatus = Fragrecord::FSACTIVE;
+ fragptr.p->logFlag = Fragrecord::STATE_TRUE;
+ signal->theData[0] = fragptr.p->srUserptr;
+ signal->theData[1] = cownNodeid;
+ sendSignal(fragptr.p->srBlockref, GSN_START_FRAGCONF, signal, 2, JBB);
+ /* --------------------------------------------------------------------
+ * WE HAVE TO ENSURE THAT THIS FRAGMENT IS NOT PUT BACK ON THE LIST BY
+ * MISTAKE. WE DO THIS BY ALSO REMOVING IT AS PREVIOUS IN START_EXEC_SR
+ * THIS IS PERFORMED BY KEEPING PREV_FRAGPTR AS PREV_FRAGPTR BUT MOVING
+ * FRAGPTR TO THE NEXT FRAGMENT IN THE LIST.
+ * ------------------------------------------------------------------- */
+ fragptr.i = next;
+ }//if
+ signal->theData[0] = fragptr.i;
+ signal->theData[1] = prevFragptr.i;
+ sendSignal(cownref, GSN_START_EXEC_SR, signal, 2, JBB);
+ }//if
+ return;
+}//Dblqh::execSTART_EXEC_SR()
+
+/* ***************> */
+/* EXEC_FRAGREQ > */
+/* ***************> */
+/* --------------------------------------------------------------------------
+ * THIS SIGNAL IS USED TO REQUEST THAT A FRAGMENT PARTICIPATES IN EXECUTING
+ * THE LOG IN THIS NODE.
+ * ------------------------------------------------------------------------- */
+void Dblqh::execEXEC_FRAGREQ(Signal* signal)
+{
+ ExecFragReq * const execFragReq = (ExecFragReq *)&signal->theData[0];
+ jamEntry();
+ tabptr.i = execFragReq->tableId;
+ Uint32 fragId = execFragReq->fragId;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ if (!getFragmentrec(signal, fragId)) {
+ jam();
+ if (!insertFragrec(signal, fragId)) {
+ jam();
+ sendExecFragRefLab(signal);
+ return;
+ }//if
+ initFragrec(signal, tabptr.i, fragId, ZLOG_NODE);
+ fragptr.p->execSrStatus = Fragrecord::ACTIVE_REMOVE_AFTER;
+ } else {
+ jam();
+ if (fragptr.p->execSrStatus == Fragrecord::ACTIVE_REMOVE_AFTER) {
+ jam();
+ fragptr.p->execSrStatus = Fragrecord::ACTIVE_REMOVE_AFTER;
+ } else {
+ jam();
+ }//if
+ }//if
+ ndbrequire(fragptr.p->execSrNoReplicas < 4);
+ fragptr.p->execSrBlockref[fragptr.p->execSrNoReplicas] = execFragReq->userRef;
+ fragptr.p->execSrUserptr[fragptr.p->execSrNoReplicas] = execFragReq->userPtr;
+ fragptr.p->execSrStartGci[fragptr.p->execSrNoReplicas] = execFragReq->startGci;
+ fragptr.p->execSrLastGci[fragptr.p->execSrNoReplicas] = execFragReq->lastGci;
+ fragptr.p->execSrStatus = Fragrecord::ACTIVE;
+ fragptr.p->execSrNoReplicas++;
+ cnoFragmentsExecSr++;
+ return;
+}//Dblqh::execEXEC_FRAGREQ()
+
+void Dblqh::sendExecFragRefLab(Signal* signal)
+{
+ ExecFragReq * const execFragReq = (ExecFragReq *)&signal->theData[0];
+ BlockReference retRef = execFragReq->userRef;
+ Uint32 retPtr = execFragReq->userPtr;
+
+ signal->theData[0] = retPtr;
+ signal->theData[1] = terrorCode;
+ sendSignal(retRef, GSN_EXEC_FRAGREF, signal, 2, JBB);
+ return;
+}//Dblqh::sendExecFragRefLab()
+
+/* ***************>> */
+/* EXEC_FRAGCONF > */
+/* ***************>> */
+void Dblqh::execEXEC_FRAGCONF(Signal* signal)
+{
+ jamEntry();
+ fragptr.i = signal->theData[0];
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->srStatus = Fragrecord::SS_COMPLETED;
+ return;
+}//Dblqh::execEXEC_FRAGCONF()
+
+/* ***************> */
+/* EXEC_FRAGREF > */
+/* ***************> */
+void Dblqh::execEXEC_FRAGREF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ systemErrorLab(signal);
+ return;
+}//Dblqh::execEXEC_FRAGREF()
+
+/* *************** */
+/* EXEC_SRCONF > */
+/* *************** */
+void Dblqh::execEXEC_SRCONF(Signal* signal)
+{
+ jamEntry();
+ Uint32 nodeId = signal->theData[0];
+ arrGuard(nodeId, MAX_NDB_NODES);
+ cnodeExecSrState[nodeId] = ZEXEC_SR_COMPLETED;
+ ndbrequire(cnoOfNodes < MAX_NDB_NODES);
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ if (cnodeStatus[i] == ZNODE_UP) {
+ jam();
+ nodeId = cnodeData[i];
+ arrGuard(nodeId, MAX_NDB_NODES);
+ if (cnodeExecSrState[nodeId] != ZEXEC_SR_COMPLETED) {
+ jam();
+ /* ------------------------------------------------------------------
+ * ALL NODES HAVE NOT REPORTED COMPLETION OF EXECUTING FRAGMENT
+ * LOGS YET.
+ * ----------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//for
+ /* ------------------------------------------------------------------------
+ * CLEAR NODE SYSTEM RESTART EXECUTION STATE TO PREPARE FOR NEXT PHASE OF
+ * LOG EXECUTION.
+ * ----------------------------------------------------------------------- */
+ for (nodeId = 0; nodeId < MAX_NDB_NODES; nodeId++) {
+ cnodeExecSrState[nodeId] = ZSTART_SR;
+ }//for
+ /* ------------------------------------------------------------------------
+ * NOW CHECK IF ALL FRAGMENTS IN THIS PHASE HAVE COMPLETED. IF SO START THE
+ * NEXT PHASE.
+ * ----------------------------------------------------------------------- */
+ fragptr.i = cfirstCompletedFragSr;
+ if (fragptr.i == RNIL) {
+ jam();
+ execSrCompletedLab(signal);
+ return;
+ }//if
+ do {
+ jam();
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ ndbrequire(fragptr.p->srStatus == Fragrecord::SS_COMPLETED);
+ fragptr.i = fragptr.p->nextFrag;
+ } while (fragptr.i != RNIL);
+ execSrCompletedLab(signal);
+ return;
+}//Dblqh::execEXEC_SRCONF()
+
+void Dblqh::execSrCompletedLab(Signal* signal)
+{
+ csrPhasesCompleted++;
+ /* ------------------------------------------------------------------------
+ * ALL FRAGMENTS WERE COMPLETED. THIS PHASE IS COMPLETED. IT IS NOW TIME TO
+ * START THE NEXT PHASE.
+ * ----------------------------------------------------------------------- */
+ if (csrPhasesCompleted >= 4) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THIS WAS THE LAST PHASE. WE HAVE NOW COMPLETED THE EXECUTION THE
+ * FRAGMENT LOGS IN ALL NODES. BEFORE WE SEND START_RECCONF TO THE
+ * MASTER DIH TO INDICATE A COMPLETED SYSTEM RESTART IT IS NECESSARY
+ * TO FIND THE HEAD AND THE TAIL OF THE LOG WHEN NEW OPERATIONS START
+ * TO COME AGAIN.
+ *
+ * THE FIRST STEP IS TO FIND THE HEAD AND TAIL MBYTE OF EACH LOG PART.
+ * TO DO THIS WE REUSE THE CONTINUEB SIGNAL SR_LOG_LIMITS. THEN WE
+ * HAVE TO FIND THE ACTUAL PAGE NUMBER AND PAGE INDEX WHERE TO
+ * CONTINUE WRITING THE LOG AFTER THE SYSTEM RESTART.
+ * --------------------------------------------------------------------- */
+ for (logPartPtr.i = 0; logPartPtr.i < 4; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ logPartPtr.p->logPartState = LogPartRecord::SR_FOURTH_PHASE_STARTED;
+ logPartPtr.p->logLastGci = crestartNewestGci;
+ logPartPtr.p->logStartGci = crestartOldestGci;
+ logPartPtr.p->logExecState = LogPartRecord::LES_SEARCH_STOP;
+ if (logPartPtr.p->headFileNo == ZNIL) {
+ jam();
+ /* -----------------------------------------------------------------
+ * IF WE HAVEN'T FOUND ANY HEAD OF THE LOG THEN WE ARE IN SERIOUS
+ * PROBLEM. THIS SHOULD NOT OCCUR. IF IT OCCURS ANYWAY THEN WE
+ * HAVE TO FIND A CURE FOR THIS PROBLEM.
+ * ----------------------------------------------------------------- */
+ systemErrorLab(signal);
+ return;
+ }//if
+ signal->theData[0] = ZSR_LOG_LIMITS;
+ signal->theData[1] = logPartPtr.i;
+ signal->theData[2] = logPartPtr.p->lastLogfile;
+ signal->theData[3] = logPartPtr.p->lastMbyte;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ }//for
+ return;
+ } else {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THERE ARE YET MORE PHASES TO RESTART.
+ * WE MUST INITIALISE DATA FOR NEXT PHASE AND SEND START SIGNAL.
+ * --------------------------------------------------------------------- */
+ startExecSr(signal);
+ }//if
+ return;
+}//Dblqh::execSrCompletedLab()
+
+/* ************>> */
+/* EXEC_SRREQ > */
+/* ************>> */
+void Dblqh::execEXEC_SRREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 nodeId = signal->theData[0];
+ ndbrequire(nodeId < MAX_NDB_NODES);
+ cnodeSrState[nodeId] = ZEXEC_SR_COMPLETED;
+ ndbrequire(cnoOfNodes < MAX_NDB_NODES);
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ if (cnodeStatus[i] == ZNODE_UP) {
+ jam();
+ nodeId = cnodeData[i];
+ if (cnodeSrState[nodeId] != ZEXEC_SR_COMPLETED) {
+ jam();
+ /* ------------------------------------------------------------------
+ * ALL NODES HAVE NOT REPORTED COMPLETION OF SENDING EXEC_FRAGREQ YET.
+ * ----------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//for
+ /* ------------------------------------------------------------------------
+ * CLEAR NODE SYSTEM RESTART STATE TO PREPARE FOR NEXT PHASE OF LOG
+ * EXECUTION
+ * ----------------------------------------------------------------------- */
+ for (nodeId = 0; nodeId < MAX_NDB_NODES; nodeId++) {
+ cnodeSrState[nodeId] = ZSTART_SR;
+ }//for
+ if (csrPhasesCompleted != 0) {
+ /* ----------------------------------------------------------------------
+ * THE FIRST PHASE MUST ALWAYS EXECUTE THE LOG.
+ * --------------------------------------------------------------------- */
+ if (cnoFragmentsExecSr == 0) {
+ jam();
+ /* --------------------------------------------------------------------
+ * THERE WERE NO FRAGMENTS THAT NEEDED TO EXECUTE THE LOG IN THIS PHASE.
+ * ------------------------------------------------------------------- */
+ srPhase3Comp(signal);
+ return;
+ }//if
+ }//if
+ /* ------------------------------------------------------------------------
+ * NOW ALL NODES HAVE SENT ALL EXEC_FRAGREQ. NOW WE CAN START EXECUTING THE
+ * LOG FROM THE MINIMUM GCI NEEDED UNTIL THE MAXIMUM GCI NEEDED.
+ *
+ * WE MUST FIRST CHECK IF THE FIRST PHASE OF THE SYSTEM RESTART HAS BEEN
+ * COMPLETED. THIS HANDLING IS PERFORMED IN THE FILE SYSTEM MODULE
+ * ----------------------------------------------------------------------- */
+ signal->theData[0] = ZSR_PHASE3_START;
+ signal->theData[1] = ZSR_PHASE2_COMPLETED;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//Dblqh::execEXEC_SRREQ()
+
+/* ######################################################################### */
+/* SYSTEM RESTART PHASE THREE MODULE */
+/* THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING. */
+/* */
+/* THIS MODULE IS CONCERNED WITH EXECUTING THE FRAGMENT LOG. IT DOES ALSO */
+/* CONTAIN SIGNAL RECEPTIONS LQHKEYCONF AND LQHKEYREF SINCE LQHKEYREQ IS USED*/
+/* TO EXECUTE THE LOG RECORDS. */
+/* */
+/* BEFORE IT STARTS IT HAS BEEN DECIDED WHERE TO START AND WHERE TO STOP */
+/* READING THE FRAGMENT LOG BY USING THE INFORMATION ABOUT GCI DISCOVERED IN */
+/* PHASE ONE OF THE SYSTEM RESTART. */
+/* ######################################################################### */
+/*---------------------------------------------------------------------------*/
+/* PHASE THREE OF THE SYSTEM RESTART CAN NOW START. ONE OF THE PHASES HAVE */
+/* COMPLETED. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::srPhase3Start(Signal* signal)
+{
+ UintR tsrPhaseStarted;
+
+ jamEntry();
+ tsrPhaseStarted = signal->theData[0];
+ if (csrPhaseStarted == ZSR_NO_PHASE_STARTED) {
+ jam();
+ csrPhaseStarted = tsrPhaseStarted;
+ if (cstartType == NodeState::ST_NODE_RESTART) {
+ ndbrequire(cinitialStartOngoing == ZTRUE);
+ cinitialStartOngoing = ZFALSE;
+ checkStartCompletedLab(signal);
+ }//if
+ return;
+ }//if
+ ndbrequire(csrPhaseStarted != tsrPhaseStarted);
+ ndbrequire(csrPhaseStarted != ZSR_BOTH_PHASES_STARTED);
+
+ csrPhaseStarted = ZSR_BOTH_PHASES_STARTED;
+ for (logPartPtr.i = 0; logPartPtr.i < 4; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ logPartPtr.p->logPartState = LogPartRecord::SR_THIRD_PHASE_STARTED;
+ logPartPtr.p->logStartGci = (UintR)-1;
+ if (csrPhasesCompleted == 0) {
+ jam();
+ /* --------------------------------------------------------------------
+ * THE FIRST PHASE WE MUST ENSURE THAT IT REACHES THE END OF THE LOG.
+ * ------------------------------------------------------------------- */
+ logPartPtr.p->logLastGci = crestartNewestGci;
+ } else {
+ jam();
+ logPartPtr.p->logLastGci = 2;
+ }//if
+ }//for
+ if (cstartType == NodeState::ST_NODE_RESTART) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * FOR A NODE RESTART WE HAVE NO FRAGMENTS DEFINED YET.
+ * THUS WE CAN SKIP THAT PART
+ * --------------------------------------------------------------------- */
+ signal->theData[0] = ZSR_GCI_LIMITS;
+ signal->theData[1] = RNIL;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ } else {
+ jam();
+ signal->theData[0] = ZSR_GCI_LIMITS;
+ signal->theData[1] = 0;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+ return;
+}//Dblqh::srPhase3Start()
+
+/* --------------------------------------------------------------------------
+ * WE NOW WE NEED TO FIND THE LIMITS WITHIN WHICH TO EXECUTE
+ * THE FRAGMENT LOG
+ * ------------------------------------------------------------------------- */
+void Dblqh::srGciLimits(Signal* signal)
+{
+ LogPartRecordPtr tmpLogPartPtr;
+
+ jamEntry();
+ fragptr.i = signal->theData[0];
+ Uint32 loopCount = 0;
+ logPartPtr.i = 0;
+ ptrAss(logPartPtr, logPartRecord);
+ while (fragptr.i < cfragrecFileSize) {
+ jam();
+ ptrAss(fragptr, fragrecord);
+ if (fragptr.p->execSrStatus != Fragrecord::IDLE) {
+ jam();
+ ndbrequire(fragptr.p->execSrNoReplicas - 1 < 4);
+ for (Uint32 i = 0; i < fragptr.p->execSrNoReplicas; i++) {
+ jam();
+ if (fragptr.p->execSrStartGci[i] < logPartPtr.p->logStartGci) {
+ jam();
+ logPartPtr.p->logStartGci = fragptr.p->execSrStartGci[i];
+ }//if
+ if (fragptr.p->execSrLastGci[i] > logPartPtr.p->logLastGci) {
+ jam();
+ logPartPtr.p->logLastGci = fragptr.p->execSrLastGci[i];
+ }//if
+ }//for
+ }//if
+ loopCount++;
+ if (loopCount > 20) {
+ jam();
+ signal->theData[0] = ZSR_GCI_LIMITS;
+ signal->theData[1] = fragptr.i + 1;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ } else {
+ jam();
+ fragptr.i++;
+ }//if
+ }//while
+ if (logPartPtr.p->logStartGci == (UintR)-1) {
+ jam();
+ /* --------------------------------------------------------------------
+ * THERE WERE NO FRAGMENTS TO INSTALL WE WILL EXECUTE THE LOG AS
+ * SHORT AS POSSIBLE TO REACH THE END OF THE LOG. THIS WE DO BY
+ * STARTING AT THE STOP GCI.
+ * ------------------------------------------------------------------- */
+ logPartPtr.p->logStartGci = logPartPtr.p->logLastGci;
+ }//if
+ for (tmpLogPartPtr.i = 1; tmpLogPartPtr.i < 4; tmpLogPartPtr.i++) {
+ ptrAss(tmpLogPartPtr, logPartRecord);
+ tmpLogPartPtr.p->logStartGci = logPartPtr.p->logStartGci;
+ tmpLogPartPtr.p->logLastGci = logPartPtr.p->logLastGci;
+ }//for
+ for (logPartPtr.i = 0; logPartPtr.i < 4; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ logPartPtr.p->logExecState = LogPartRecord::LES_SEARCH_STOP;
+ signal->theData[0] = ZSR_LOG_LIMITS;
+ signal->theData[1] = logPartPtr.i;
+ signal->theData[2] = logPartPtr.p->lastLogfile;
+ signal->theData[3] = logPartPtr.p->lastMbyte;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ }//for
+}//Dblqh::srGciLimits()
+
+/* --------------------------------------------------------------------------
+ * IT IS NOW TIME TO FIND WHERE TO START EXECUTING THE LOG.
+ * THIS SIGNAL IS SENT FOR EACH LOG PART AND STARTS THE EXECUTION
+ * OF THE LOG FOR THIS PART.
+ *-------------------------------------------------------------------------- */
+void Dblqh::srLogLimits(Signal* signal)
+{
+ Uint32 tlastPrepRef;
+ Uint32 tmbyte;
+
+ jamEntry();
+ logPartPtr.i = signal->theData[0];
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logFilePtr.i = signal->theData[1];
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ tmbyte = signal->theData[2];
+ Uint32 loopCount = 0;
+ /* ------------------------------------------------------------------------
+ * WE ARE SEARCHING FOR THE START AND STOP MBYTE OF THE LOG THAT IS TO BE
+ * EXECUTED.
+ * ----------------------------------------------------------------------- */
+ while(true) {
+ ndbrequire(tmbyte < 16);
+ if (logPartPtr.p->logExecState == LogPartRecord::LES_SEARCH_STOP) {
+ if (logFilePtr.p->logMaxGciCompleted[tmbyte] < logPartPtr.p->logLastGci) {
+ jam();
+ /* --------------------------------------------------------------------
+ * WE ARE STEPPING BACKWARDS FROM MBYTE TO MBYTE. THIS IS THE FIRST
+ * MBYTE WHICH IS TO BE INCLUDED IN THE LOG EXECUTION. THE STOP GCI
+ * HAS NOT BEEN COMPLETED BEFORE THIS MBYTE. THUS THIS MBYTE HAVE
+ * TO BE EXECUTED.
+ * ------------------------------------------------------------------- */
+ logPartPtr.p->stopLogfile = logFilePtr.i;
+ logPartPtr.p->stopMbyte = tmbyte;
+ logPartPtr.p->logExecState = LogPartRecord::LES_SEARCH_START;
+ }//if
+ }//if
+ /* ------------------------------------------------------------------------
+ * WHEN WE HAVEN'T FOUND THE STOP MBYTE IT IS NOT NECESSARY TO LOOK FOR THE
+ * START MBYTE. THE REASON IS THE FOLLOWING LOGIC CHAIN:
+ * MAX_GCI_STARTED >= MAX_GCI_COMPLETED >= LAST_GCI >= START_GCI
+ * THUS MAX_GCI_STARTED >= START_GCI. THUS MAX_GCI_STARTED < START_GCI CAN
+ * NOT BE TRUE AS WE WILL CHECK OTHERWISE.
+ * ----------------------------------------------------------------------- */
+ if (logPartPtr.p->logExecState == LogPartRecord::LES_SEARCH_START) {
+ if (logFilePtr.p->logMaxGciStarted[tmbyte] < logPartPtr.p->logStartGci) {
+ jam();
+ /* --------------------------------------------------------------------
+ * WE HAVE NOW FOUND THE START OF THE EXECUTION OF THE LOG.
+ * WE STILL HAVE TO MOVE IT BACKWARDS TO ALSO INCLUDE THE
+ * PREPARE RECORDS WHICH WERE STARTED IN A PREVIOUS MBYTE.
+ * ------------------------------------------------------------------- */
+ tlastPrepRef = logFilePtr.p->logLastPrepRef[tmbyte];
+ logPartPtr.p->startMbyte = tlastPrepRef & 65535;
+ LogFileRecordPtr locLogFilePtr;
+ findLogfile(signal, tlastPrepRef >> 16, logPartPtr, &locLogFilePtr);
+ logPartPtr.p->startLogfile = locLogFilePtr.i;
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG;
+ }//if
+ }//if
+ if (logPartPtr.p->logExecState != LogPartRecord::LES_EXEC_LOG) {
+ if (tmbyte == 0) {
+ jam();
+ tmbyte = ZNO_MBYTES_IN_FILE - 1;
+ logFilePtr.i = logFilePtr.p->prevLogFile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ } else {
+ jam();
+ tmbyte--;
+ }//if
+ if (logPartPtr.p->lastLogfile == logFilePtr.i) {
+ ndbrequire(logPartPtr.p->lastMbyte != tmbyte);
+ }//if
+ if (loopCount > 20) {
+ jam();
+ signal->theData[0] = ZSR_LOG_LIMITS;
+ signal->theData[1] = logPartPtr.i;
+ signal->theData[2] = logFilePtr.i;
+ signal->theData[3] = tmbyte;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 4, JBB);
+ return;
+ }//if
+ loopCount++;
+ } else {
+ jam();
+ break;
+ }//if
+ }//while
+ /* ------------------------------------------------------------------------
+ * WE HAVE NOW FOUND BOTH THE START AND THE STOP OF THE LOG. NOW START
+ * EXECUTING THE LOG. THE FIRST ACTION IS TO OPEN THE LOG FILE WHERE TO
+ * START EXECUTING THE LOG.
+ * ----------------------------------------------------------------------- */
+ if (logPartPtr.p->logPartState == LogPartRecord::SR_THIRD_PHASE_STARTED) {
+ jam();
+ logFilePtr.i = logPartPtr.p->startLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN_EXEC_SR_START;
+ openFileRw(signal, logFilePtr);
+ } else {
+ jam();
+ ndbrequire(logPartPtr.p->logPartState == LogPartRecord::SR_FOURTH_PHASE_STARTED);
+ /* --------------------------------------------------------------------
+ * WE HAVE NOW FOUND THE TAIL MBYTE IN THE TAIL FILE.
+ * SET THOSE PARAMETERS IN THE LOG PART.
+ * WE HAVE ALSO FOUND THE HEAD MBYTE. WE STILL HAVE TO SEARCH
+ * FOR THE PAGE NUMBER AND PAGE INDEX WHERE TO SET THE HEAD.
+ * ------------------------------------------------------------------- */
+ logFilePtr.i = logPartPtr.p->startLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPartPtr.p->logTailFileNo = logFilePtr.p->fileNo;
+ logPartPtr.p->logTailMbyte = logPartPtr.p->startMbyte;
+ /* --------------------------------------------------------------------
+ * THE HEAD WE ACTUALLY FOUND DURING EXECUTION OF LOG SO WE USE
+ * THIS INFO HERE RATHER THAN THE MBYTE WE FOUND TO BE THE HEADER.
+ * ------------------------------------------------------------------- */
+ LogFileRecordPtr locLogFilePtr;
+ findLogfile(signal, logPartPtr.p->headFileNo, logPartPtr, &locLogFilePtr);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_FOURTH_PHASE;
+ openFileRw(signal, locLogFilePtr);
+ }//if
+ return;
+}//Dblqh::srLogLimits()
+
+void Dblqh::openExecSrStartLab(Signal* signal)
+{
+ logPartPtr.p->currentLogfile = logFilePtr.i;
+ logFilePtr.p->currentMbyte = logPartPtr.p->startMbyte;
+ /* ------------------------------------------------------------------------
+ * WE NEED A TC CONNECT RECORD TO HANDLE EXECUTION OF LOG RECORDS.
+ * ------------------------------------------------------------------------ */
+ seizeTcrec();
+ logPartPtr.p->logTcConrec = tcConnectptr.i;
+ /* ------------------------------------------------------------------------
+ * THE FIRST LOG RECORD TO EXECUTE IS ALWAYS AT A NEW MBYTE.
+ * SET THE NUMBER OF PAGES IN THE MAIN MEMORY BUFFER TO ZERO AS AN INITIAL
+ * VALUE. THIS VALUE WILL BE UPDATED AND ENSURED THAT IT RELEASES PAGES IN
+ * THE SUBROUTINE READ_EXEC_SR.
+ * ----------------------------------------------------------------------- */
+ logPartPtr.p->mmBufferSize = 0;
+ readExecSrNewMbyte(signal);
+ return;
+}//Dblqh::openExecSrStartLab()
+
+/* ---------------------------------------------------------------------------
+ * WE WILL ALWAYS ENSURE THAT WE HAVE AT LEAST 16 KBYTE OF LOG PAGES WHEN WE
+ * START READING A LOG RECORD. THE ONLY EXCEPTION IS WHEN WE COME CLOSE TO A
+ * MBYTE BOUNDARY. SINCE WE KNOW THAT LOG RECORDS ARE NEVER WRITTEN ACROSS A
+ * MBYTE BOUNDARY THIS IS NOT A PROBLEM.
+ *
+ * WE START BY READING 64 KBYTE BEFORE STARTING TO EXECUTE THE LOG RECORDS.
+ * WHEN WE COME BELOW 64 KBYTE WE READ ANOTHER SET OF LOG PAGES. WHEN WE
+ * GO BELOW 16 KBYTE WE WAIT UNTIL THE READ PAGES HAVE ENTERED THE BLOCK.
+ * ------------------------------------------------------------------------- */
+/* --------------------------------------------------------------------------
+ * NEW PAGES FROM LOG FILE DURING EXECUTION OF LOG HAS ARRIVED.
+ * ------------------------------------------------------------------------- */
+void Dblqh::readExecSrLab(Signal* signal)
+{
+ buildLinkedLogPageList(signal);
+ /* ------------------------------------------------------------------------
+ * WE NEED TO SET THE CURRENT PAGE INDEX OF THE FIRST PAGE SINCE IT CAN BE
+ * USED IMMEDIATELY WITHOUT ANY OTHER INITIALISATION. THE REST OF THE PAGES
+ * WILL BE INITIALISED BY READ_LOGWORD.
+ * ----------------------------------------------------------------------- */
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = ZPAGE_HEADER_SIZE;
+ if (logPartPtr.p->logExecState ==
+ LogPartRecord::LES_WAIT_READ_EXEC_SR_NEW_MBYTE) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THIS IS THE FIRST READ DURING THE EXECUTION OF THIS MBYTE. SET THE
+ * NEW CURRENT LOG PAGE TO THE FIRST OF THESE PAGES. CHANGE
+ * LOG_EXEC_STATE TO ENSURE THAT WE START EXECUTION OF THE LOG.
+ * --------------------------------------------------------------------- */
+ logFilePtr.p->currentFilepage = logFilePtr.p->currentMbyte *
+ ZPAGES_IN_MBYTE;
+ logPartPtr.p->prevFilepage = logFilePtr.p->currentFilepage;
+ logFilePtr.p->currentLogpage = lfoPtr.p->firstLfoPage;
+ logPartPtr.p->prevLogpage = logFilePtr.p->currentLogpage;
+ }//if
+ moveToPageRef(signal);
+ releaseLfo(signal);
+ /* ------------------------------------------------------------------------
+ * NOW WE HAVE COMPLETED THE RECEPTION OF THESE PAGES.
+ * NOW CHECK IF WE NEED TO READ MORE PAGES.
+ * ----------------------------------------------------------------------- */
+ checkReadExecSr(signal);
+ if (logPartPtr.p->logExecState == LogPartRecord::LES_EXEC_LOG) {
+ jam();
+ signal->theData[0] = ZEXEC_SR;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+ return;
+}//Dblqh::readExecSrLab()
+
+void Dblqh::openExecSrNewMbyteLab(Signal* signal)
+{
+ readExecSrNewMbyte(signal);
+ return;
+}//Dblqh::openExecSrNewMbyteLab()
+
+void Dblqh::closeExecSrLab(Signal* signal)
+{
+ LogFileRecordPtr locLogFilePtr;
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ logPartPtr.i = logFilePtr.p->logPartRec;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ locLogFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(locLogFilePtr, clogFileFileSize, logFileRecord);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_EXEC_SR_NEW_MBYTE;
+ openFileRw(signal, locLogFilePtr);
+ return;
+}//Dblqh::closeExecSrLab()
+
+void Dblqh::writeDirtyLab(Signal* signal)
+{
+ releaseLfo(signal);
+ signal->theData[0] = logPartPtr.i;
+ execSr(signal);
+ return;
+}//Dblqh::writeDirtyLab()
+
+/* --------------------------------------------------------------------------
+ * EXECUTE A LOG RECORD WITHIN THE CURRENT MBYTE.
+ * ------------------------------------------------------------------------- */
+void Dblqh::execSr(Signal* signal)
+{
+ LogFileRecordPtr nextLogFilePtr;
+ LogPageRecordPtr tmpLogPagePtr;
+ Uint32 logWord;
+
+ jamEntry();
+ logPartPtr.i = signal->theData[0];
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+
+ do {
+ jam();
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logPartPtr.p->prevLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ if (logPagePtr.p->logPageWord[ZPOS_DIRTY] == ZDIRTY) {
+ jam();
+ switch (logPartPtr.p->logExecState) {
+ case LogPartRecord::LES_EXEC_LOG_COMPLETED:
+ case LogPartRecord::LES_EXEC_LOG_NEW_FILE:
+ case LogPartRecord::LES_EXEC_LOG_NEW_MBYTE:
+ jam();
+ /* ------------------------------------------------------------------
+ * IN THIS WE HAVE COMPLETED EXECUTION OF THE CURRENT LOG PAGE
+ * AND CAN WRITE IT TO DISK SINCE IT IS DIRTY.
+ * ----------------------------------------------------------------- */
+ writeDirty(signal);
+ return;
+ break;
+ case LogPartRecord::LES_EXEC_LOG:
+ jam();
+ /* --------------------------------------------------------------------
+ * IN THIS CASE WE ONLY WRITE THE PAGE TO DISK IF WE HAVE COMPLETED
+ * EXECUTION OF LOG RECORDS BELONGING TO THIS LOG PAGE.
+ * ------------------------------------------------------------------- */
+ if (logFilePtr.p->currentLogpage != logPartPtr.p->prevLogpage) {
+ jam();
+ writeDirty(signal);
+ return;
+ }//if
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ }//if
+ if (logFilePtr.p->currentLogpage != logPartPtr.p->prevLogpage) {
+ jam();
+ logPartPtr.p->prevLogpage = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ logPartPtr.p->prevFilepage++;
+ continue;
+ }//if
+ switch (logPartPtr.p->logExecState) {
+ case LogPartRecord::LES_EXEC_LOG_COMPLETED:
+ jam();
+ releaseMmPages(signal);
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_EXEC_SR_COMPLETED;
+ closeFile(signal, logFilePtr);
+ return;
+ break;
+ case LogPartRecord::LES_EXEC_LOG_NEW_MBYTE:
+ jam();
+ logFilePtr.p->currentMbyte++;
+ readExecSrNewMbyte(signal);
+ return;
+ break;
+ case LogPartRecord::LES_EXEC_LOG_NEW_FILE:
+ jam();
+ nextLogFilePtr.i = logFilePtr.p->nextLogFile;
+ logPartPtr.p->currentLogfile = nextLogFilePtr.i;
+ ptrCheckGuard(nextLogFilePtr, clogFileFileSize, logFileRecord);
+ nextLogFilePtr.p->currentMbyte = 0;
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSING_EXEC_SR;
+ closeFile(signal, logFilePtr);
+ return;
+ break;
+ case LogPartRecord::LES_EXEC_LOG:
+ jam();
+ /*empty*/;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPartPtr.p->savePageIndex = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ if (logPartPtr.p->execSrPagesRead < ZMIN_READ_BUFFER_SIZE) {
+ /* --------------------------------------------------------------------
+ * THERE WERE LESS THAN 16 KBYTE OF LOG PAGES REMAINING. WE WAIT UNTIL
+ * THE NEXT 64 KBYTE ARRIVES UNTIL WE CONTINUE AGAIN.
+ * ------------------------------------------------------------------- */
+ if ((logPartPtr.p->execSrPagesRead +
+ logPartPtr.p->execSrPagesExecuted) < ZPAGES_IN_MBYTE) {
+ jam();
+ /* ------------------------------------------------------------------
+ * WE ONLY STOP AND WAIT IF THERE MORE PAGES TO READ. IF IT IS NOT
+ * THEN IT IS THE END OF THE MBYTE AND WE WILL CONTINUE. IT IS NO
+ * RISK THAT A LOG RECORD WE FIND WILL NOT BE READ AT THIS TIME
+ * SINCE THE LOG RECORDS NEVER SPAN OVER A MBYTE BOUNDARY.
+ * ----------------------------------------------------------------- */
+ readExecSr(signal);
+ logPartPtr.p->logExecState = LogPartRecord::LES_WAIT_READ_EXEC_SR;
+ return;
+ }//if
+ }//if
+ logWord = readLogword(signal);
+ switch (logWord) {
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZPREP_OP_TYPE:
+ {
+ logWord = readLogword(signal);
+ stepAhead(signal, logWord - 2);
+ break;
+ }
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZINVALID_COMMIT_TYPE:
+ jam();
+ stepAhead(signal, ZCOMMIT_LOG_SIZE - 1);
+ break;
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZCOMMIT_TYPE:
+ {
+ CommitLogRecord commitLogRecord;
+ jam();
+ tcConnectptr.i = logPartPtr.p->logTcConrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ readCommitLog(signal, &commitLogRecord);
+ if (tcConnectptr.p->gci > crestartNewestGci) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS LOG RECORD MUST BE IGNORED. IT IS PART OF A GLOBAL CHECKPOINT WHICH */
+/* WILL BE INVALIDATED BY THE SYSTEM RESTART. IF NOT INVALIDATED IT MIGHT BE */
+/* EXECUTED IN A FUTURE SYSTEM RESTART. */
+/*---------------------------------------------------------------------------*/
+ tmpLogPagePtr.i = logPartPtr.p->prevLogpage;
+ ptrCheckGuard(tmpLogPagePtr, clogPageFileSize, logPageRecord);
+ arrGuard(logPartPtr.p->savePageIndex, ZPAGE_SIZE);
+ tmpLogPagePtr.p->logPageWord[logPartPtr.p->savePageIndex] =
+ ZINVALID_COMMIT_TYPE;
+ tmpLogPagePtr.p->logPageWord[ZPOS_DIRTY] = ZDIRTY;
+ } else {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* CHECK IF I AM SUPPOSED TO EXECUTE THIS LOG RECORD. IF I AM THEN SAVE PAGE */
+/* INDEX IN CURRENT LOG PAGE SINCE IT WILL BE OVERWRITTEN WHEN EXECUTING THE */
+/* LOG RECORD. */
+/*---------------------------------------------------------------------------*/
+ logPartPtr.p->execSrExecuteIndex = 0;
+ Uint32 result = checkIfExecLog(signal);
+ if (result == ZOK) {
+ jam();
+//*---------------------------------------------------------------------------*/
+/* IN A NODE RESTART WE WILL NEVER END UP HERE SINCE NO FRAGMENTS HAVE BEEN */
+/* DEFINED YET. THUS NO EXTRA CHECKING FOR NODE RESTART IS NECESSARY. */
+/*---------------------------------------------------------------------------*/
+ logPartPtr.p->savePageIndex =
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ tcConnectptr.p->fragmentptr = fragptr.i;
+ findPageRef(signal, &commitLogRecord);
+ logPartPtr.p->execSrLogPageIndex = commitLogRecord.startPageIndex;
+ if (logPagePtr.i != RNIL) {
+ jam();
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = commitLogRecord.startPageIndex;
+ logPartPtr.p->execSrLogPage = logPagePtr.i;
+ execLogRecord(signal);
+ return;
+ }//if
+ logPartPtr.p->execSrStartPageNo = commitLogRecord.startPageNo;
+ logPartPtr.p->execSrStopPageNo = commitLogRecord.stopPageNo;
+ findLogfile(signal, commitLogRecord.fileNo, logPartPtr, &logFilePtr);
+ logPartPtr.p->execSrExecLogFile = logFilePtr.i;
+ if (logFilePtr.i == logPartPtr.p->currentLogfile) {
+ jam();
+ readExecLog(signal);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_EXEC_LOG;
+ return;
+ } else {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THE FILE IS CURRENTLY NOT OPEN. WE MUST OPEN IT BEFORE WE CAN READ FROM */
+/* THE FILE. */
+/*---------------------------------------------------------------------------*/
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN_EXEC_LOG;
+ openFileRw(signal, logFilePtr);
+ return;
+ }//if
+ }//if
+ }//if
+ break;
+ }
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZABORT_TYPE:
+ jam();
+ stepAhead(signal, ZABORT_LOG_SIZE - 1);
+ break;
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZFD_TYPE:
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS IS THE FIRST ITEM WE ENCOUNTER IN A NEW FILE. AT THIS MOMENT WE SHALL*/
+/* SIMPLY BYPASS IT. IT HAS NO SIGNIFANCE WHEN EXECUTING THE LOG. IT HAS ITS */
+/* SIGNIFANCE WHEN FINDING THE START END THE END OF THE LOG. */
+/* WE HARDCODE THE PAGE INDEX SINCE THIS SHOULD NEVER BE FOUND AT ANY OTHER */
+/* PLACE THAN IN THE FIRST PAGE OF A NEW FILE IN THE FIRST POSITION AFTER THE*/
+/* HEADER. */
+/*---------------------------------------------------------------------------*/
+ ndbrequire(logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] ==
+ (ZPAGE_HEADER_SIZE + ZPOS_NO_FD));
+ {
+ Uint32 noFdDescriptors =
+ logPagePtr.p->logPageWord[ZPAGE_HEADER_SIZE + ZPOS_NO_FD];
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] =
+ (ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (noFdDescriptors * ZFD_PART_SIZE);
+ }
+ break;
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZNEXT_LOG_RECORD_TYPE:
+ jam();
+ stepAhead(signal, ZPAGE_SIZE - logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX]);
+ break;
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZNEXT_MBYTE_TYPE:
+/*---------------------------------------------------------------------------*/
+/* WE WILL SKIP A PART OF THE LOG FILE. ACTUALLY THE NEXT POINTER IS TO */
+/* A NEW MBYTE. THEREFORE WE WILL START UP A NEW MBYTE. THIS NEW MBYTE IS */
+/* HOWEVER ONLY STARTED IF IT IS NOT AFTER THE STOP MBYTE. */
+/* IF WE HAVE REACHED THE END OF THE STOP MBYTE THEN THE EXECUTION OF THE LOG*/
+/* IS COMPLETED. */
+/*---------------------------------------------------------------------------*/
+ if (logPartPtr.p->currentLogfile == logPartPtr.p->stopLogfile) {
+ if (logFilePtr.p->currentMbyte == logPartPtr.p->stopMbyte) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* THIS WAS THE LAST MBYTE TO EXECUTE IN THIS LOG PART. WE SHOULD HAVE FOUND */
+/* A COMPLETED GCI RECORD OF THE LAST GCI BEFORE THIS. FOR SOME REASON THIS */
+/* RECORD WAS NOT AVAILABLE ON THE LOG. CRASH THE SYSTEM, A VERY SERIOUS */
+/* ERROR WHICH WE MUST REALLY WORK HARD TO AVOID. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+/* SEND A SIGNAL TO THE SIGNAL LOG AND THEN CRASH THE SYSTEM. */
+/*---------------------------------------------------------------------------*/
+ signal->theData[0] = RNIL;
+ signal->theData[1] = logPartPtr.i;
+ Uint32 tmp = logFilePtr.p->fileName[3];
+ tmp = (tmp >> 8) & 0xff;// To get the Directory, DXX.
+ signal->theData[2] = tmp;
+ signal->theData[3] = logFilePtr.p->fileNo;
+ signal->theData[4] = logFilePtr.p->currentFilepage;
+ signal->theData[5] = logFilePtr.p->currentMbyte;
+ signal->theData[6] = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ sendSignal(cownref, GSN_DEBUG_SIG, signal, 7, JBA);
+ return;
+ }//if
+ }//if
+/*---------------------------------------------------------------------------*/
+/* START EXECUTION OF A NEW MBYTE IN THE LOG. */
+/*---------------------------------------------------------------------------*/
+ if (logFilePtr.p->currentMbyte < (ZNO_MBYTES_IN_FILE - 1)) {
+ jam();
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG_NEW_MBYTE;
+ } else {
+ ndbrequire(logFilePtr.p->currentMbyte == (ZNO_MBYTES_IN_FILE - 1));
+ jam();
+/*---------------------------------------------------------------------------*/
+/* WE HAVE TO CHANGE FILE. CLOSE THIS ONE AND THEN OPEN THE NEXT. */
+/*---------------------------------------------------------------------------*/
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG_NEW_FILE;
+ }//if
+ break;
+/* ========================================================================= */
+/* ========================================================================= */
+ case ZCOMPLETED_GCI_TYPE:
+ jam();
+ logWord = readLogword(signal);
+ if (logWord == logPartPtr.p->logLastGci) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/* IF IT IS THE LAST GCI TO LIVE AFTER SYSTEM RESTART THEN WE RECORD THE NEXT*/
+/* WORD AS THE NEW HEADER OF THE LOG FILE. OTHERWISE WE SIMPLY IGNORE THIS */
+/* LOG RECORD. */
+/*---------------------------------------------------------------------------*/
+ if (csrPhasesCompleted == 0) {
+ jam();
+/*---------------------------------------------------------------------------*/
+/*WE ONLY RECORD THE HEAD OF THE LOG IN THE FIRST LOG ROUND OF LOG EXECUTION.*/
+/*---------------------------------------------------------------------------*/
+ logPartPtr.p->headFileNo = logFilePtr.p->fileNo;
+ logPartPtr.p->headPageNo = logFilePtr.p->currentFilepage;
+ logPartPtr.p->headPageIndex =
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ }//if
+/*---------------------------------------------------------------------------*/
+/* THERE IS NO NEED OF EXECUTING PAST THIS LINE SINCE THERE WILL ONLY BE LOG */
+/* RECORDS THAT WILL BE OF NO INTEREST. THUS CLOSE THE FILE AND START THE */
+/* NEXT PHASE OF THE SYSTEM RESTART. */
+/*---------------------------------------------------------------------------*/
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG_COMPLETED;
+ }//if
+ break;
+ default:
+ jam();
+/* ========================================================================= */
+/* ========================================================================= */
+/*---------------------------------------------------------------------------*/
+/* SEND A SIGNAL TO THE SIGNAL LOG AND THEN CRASH THE SYSTEM. */
+/*---------------------------------------------------------------------------*/
+ signal->theData[0] = RNIL;
+ signal->theData[1] = logPartPtr.i;
+ Uint32 tmp = logFilePtr.p->fileName[3];
+ tmp = (tmp >> 8) & 0xff;// To get the Directory, DXX.
+ signal->theData[2] = tmp;
+ signal->theData[3] = logFilePtr.p->fileNo;
+ signal->theData[4] = logFilePtr.p->currentMbyte;
+ signal->theData[5] = logFilePtr.p->currentFilepage;
+ signal->theData[6] = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ signal->theData[7] = logWord;
+ sendSignal(cownref, GSN_DEBUG_SIG, signal, 8, JBA);
+ return;
+ break;
+ }//switch
+/*---------------------------------------------------------------------------*/
+// We continue to execute log records until we find a proper one to execute or
+// that we reach a new page.
+/*---------------------------------------------------------------------------*/
+ } while (1);
+}//Dblqh::execSr()
+
+/*---------------------------------------------------------------------------*/
+/* THIS SIGNAL IS ONLY RECEIVED TO BE CAPTURED IN THE SIGNAL LOG. IT IS */
+/* ALSO USED TO CRASH THE SYSTEM AFTER SENDING A SIGNAL TO THE LOG. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::execDEBUG_SIG(Signal* signal)
+{
+/*
+2.5 TEMPORARY VARIABLES
+-----------------------
+*/
+ UintR tdebug;
+
+ jamEntry();
+ logPagePtr.i = signal->theData[0];
+ tdebug = logPagePtr.p->logPageWord[0];
+
+ char buf[100];
+ BaseString::snprintf(buf, 100,
+ "Error while reading REDO log.\n"
+ "D=%d, F=%d Mb=%d FP=%d W1=%d W2=%d",
+ signal->theData[2], signal->theData[3], signal->theData[4],
+ signal->theData[5], signal->theData[6], signal->theData[7]);
+
+ progError(__LINE__, ERR_SR_REDOLOG, buf);
+
+ return;
+}//Dblqh::execDEBUG_SIG()
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+void Dblqh::closeExecLogLab(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ signal->theData[0] = ZEXEC_SR;
+ signal->theData[1] = logFilePtr.p->logPartRec;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//Dblqh::closeExecLogLab()
+
+void Dblqh::openExecLogLab(Signal* signal)
+{
+ readExecLog(signal);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_EXEC_LOG;
+ return;
+}//Dblqh::openExecLogLab()
+
+void Dblqh::readExecLogLab(Signal* signal)
+{
+ buildLinkedLogPageList(signal);
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOGREC_FROM_FILE;
+ logPartPtr.p->execSrLfoRec = lfoPtr.i;
+ logPartPtr.p->execSrLogPage = logPagePtr.i;
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] =
+ logPartPtr.p->execSrLogPageIndex;
+ execLogRecord(signal);
+ return;
+}//Dblqh::readExecLogLab()
+
+/*---------------------------------------------------------------------------*/
+/* THIS CODE IS USED TO EXECUTE A LOG RECORD WHEN IT'S DATA HAVE BEEN LOCATED*/
+/* AND TRANSFERRED INTO MEMORY. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::execLogRecord(Signal* signal)
+{
+ jamEntry();
+
+ tcConnectptr.i = logPartPtr.p->logTcConrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ // Read a log record and prepare it for execution
+ readLogHeader(signal);
+ readKey(signal);
+ readAttrinfo(signal);
+ initReqinfoExecSr(signal);
+ arrGuard(logPartPtr.p->execSrExecuteIndex, 4);
+ BlockReference ref = fragptr.p->execSrBlockref[logPartPtr.p->execSrExecuteIndex];
+ tcConnectptr.p->nextReplica = refToNode(ref);
+ tcConnectptr.p->connectState = TcConnectionrec::LOG_CONNECTED;
+ tcConnectptr.p->tcOprec = tcConnectptr.i;
+ packLqhkeyreqLab(signal);
+ return;
+}//Dblqh::execLogRecord()
+
+//----------------------------------------------------------------------------
+// This function invalidates log pages after the last GCI record in a
+// system/node restart. This is to ensure that the end of the log is
+// consistent. This function is executed last in start phase 3.
+// RT 450. EDTJAMO.
+//----------------------------------------------------------------------------
+void Dblqh::invalidateLogAfterLastGCI(Signal* signal) {
+
+ jam();
+ if (logPartPtr.p->logExecState != LogPartRecord::LES_EXEC_LOG_INVALIDATE) {
+ jam();
+ systemError(signal);
+ }
+
+ if (logFilePtr.p->fileNo != logPartPtr.p->invalidateFileNo) {
+ jam();
+ systemError(signal);
+ }
+
+ switch (lfoPtr.p->lfoState) {
+ case LogFileOperationRecord::WRITE_SR_INVALIDATE_PAGES:
+ jam();
+ releaseLfo(signal);
+ releaseLogpage(signal);
+ if (logPartPtr.p->invalidatePageNo < (ZNO_MBYTES_IN_FILE * ZPAGES_IN_MBYTE - 1)) {
+ // We continue in this file.
+ logPartPtr.p->invalidatePageNo++;
+ } else {
+ // We continue in the next file.
+ logFilePtr.i = logFilePtr.p->nextLogFile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPartPtr.p->invalidateFileNo = logFilePtr.p->fileNo;
+ // Page 0 is used for file descriptors.
+ logPartPtr.p->invalidatePageNo = 1;
+ if (logFilePtr.p->logFileStatus != LogFileRecord::OPEN) {
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_INVALIDATE_PAGES;
+ openFileRw(signal, logFilePtr);
+ return;
+ break;
+ }
+ }
+ // Read a page from the log file.
+ readFileInInvalidate(signal);
+ return;
+ break;
+
+ case LogFileOperationRecord::READ_SR_INVALIDATE_PAGES:
+ jam();
+ releaseLfo(signal);
+ // Check if this page must be invalidated.
+ // If the log lap number on a page after the head of the tail is the same
+ // as the actual log lap number we must invalidate this page. Otherwise it
+ // could be impossible to find the end of the log in a later system/node
+ // restart.
+ if (logPagePtr.p->logPageWord[ZPOS_LOG_LAP] == logPartPtr.p->logLap) {
+ // This page must be invalidated.
+ logPagePtr.p->logPageWord[ZPOS_LOG_LAP] = 0;
+ // Contact NDBFS. Real time break.
+ writeSinglePage(signal, logPartPtr.p->invalidatePageNo, ZPAGE_SIZE - 1);
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_SR_INVALIDATE_PAGES;
+ } else {
+ // We are done with invalidating. Finish start phase 3.4.
+ exitFromInvalidate(signal);
+ }
+ return;
+ break;
+
+ default:
+ jam();
+ systemError(signal);
+ return;
+ break;
+ }
+
+ return;
+}//Dblqh::invalidateLogAfterLastGCI
+
+void Dblqh::readFileInInvalidate(Signal* signal) {
+ jam();
+ // Contact NDBFS. Real time break.
+ readSinglePage(signal, logPartPtr.p->invalidatePageNo);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_INVALIDATE_PAGES;
+}
+
+void Dblqh::exitFromInvalidate(Signal* signal) {
+ jam();
+ // Close files if necessary. Current file and the next file should be
+ // left open.
+ if (logFilePtr.i != logPartPtr.p->currentLogfile) {
+ LogFileRecordPtr currentLogFilePtr;
+ LogFileRecordPtr nextAfterCurrentLogFilePtr;
+
+ currentLogFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(currentLogFilePtr, clogFileFileSize, logFileRecord);
+
+ nextAfterCurrentLogFilePtr.i = currentLogFilePtr.p->nextLogFile;
+
+ if (logFilePtr.i != nextAfterCurrentLogFilePtr.i) {
+ // This file should be closed.
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSE_SR_INVALIDATE_PAGES;
+ closeFile(signal, logFilePtr);
+ // Return from this function and wait for close confirm. Then come back
+ // and test the previous file for closing.
+ return;
+ }
+ }
+
+ // We are done with closing files, send completed signal and exit this phase.
+ signal->theData[0] = ZSR_FOURTH_COMP;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}
+
+
+/*---------------------------------------------------------------------------*/
+/* THE EXECUTION OF A LOG RECORD IS COMPLETED. RELEASE PAGES IF THEY WERE */
+/* READ FROM DISK FOR THIS PARTICULAR OPERATION. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::completedLab(Signal* signal)
+{
+ Uint32 result = returnExecLog(signal);
+/*---------------------------------------------------------------------------*/
+/* ENTER COMPLETED WITH */
+/* LQH_CONNECTPTR */
+/*---------------------------------------------------------------------------*/
+ if (result == ZOK) {
+ jam();
+ execLogRecord(signal);
+ return;
+ } else if (result == ZNOT_OK) {
+ jam();
+ signal->theData[0] = ZEXEC_SR;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ } else {
+ jam();
+ /*empty*/;
+ }//if
+/*---------------------------------------------------------------------------*/
+/* WE HAVE TO WAIT FOR CLOSING OF THE EXECUTED LOG FILE BEFORE PROCEEDING IN */
+/* RARE CASES. */
+/*---------------------------------------------------------------------------*/
+ return;
+}//Dblqh::completedLab()
+
+/*---------------------------------------------------------------------------*/
+/* EXECUTION OF LOG RECORD WAS NOT SUCCESSFUL. CHECK IF IT IS OK ANYWAY, */
+/* THEN EXECUTE THE NEXT LOG RECORD. */
+/*---------------------------------------------------------------------------*/
+void Dblqh::logLqhkeyrefLab(Signal* signal)
+{
+ Uint32 result = returnExecLog(signal);
+ switch (tcConnectptr.p->operation) {
+ case ZUPDATE:
+ case ZDELETE:
+ jam();
+ ndbrequire(terrorCode == ZNO_TUPLE_FOUND);
+ break;
+ case ZINSERT:
+ jam();
+ ndbrequire(terrorCode == ZTUPLE_ALREADY_EXIST);
+ break;
+ default:
+ ndbrequire(false);
+ return;
+ break;
+ }//switch
+ if (result == ZOK) {
+ jam();
+ execLogRecord(signal);
+ return;
+ } else if (result == ZNOT_OK) {
+ jam();
+ signal->theData[0] = ZEXEC_SR;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ } else {
+ jam();
+ /*empty*/;
+ }//if
+ /* ------------------------------------------------------------------------
+ * WE HAVE TO WAIT FOR CLOSING OF THE EXECUTED LOG FILE BEFORE
+ * PROCEEDING IN RARE CASES.
+ * ----------------------------------------------------------------------- */
+ return;
+}//Dblqh::logLqhkeyrefLab()
+
+void Dblqh::closeExecSrCompletedLab(Signal* signal)
+{
+ logFilePtr.p->logFileStatus = LogFileRecord::CLOSED;
+ signal->theData[0] = logFilePtr.p->logPartRec;
+ execLogComp(signal);
+ return;
+}//Dblqh::closeExecSrCompletedLab()
+
+/* --------------------------------------------------------------------------
+ * ONE OF THE LOG PARTS HAVE COMPLETED EXECUTING THE LOG. CHECK IF ALL LOG
+ * PARTS ARE COMPLETED. IF SO START SENDING EXEC_FRAGCONF AND EXEC_SRCONF.
+ * ------------------------------------------------------------------------- */
+void Dblqh::execLogComp(Signal* signal)
+{
+ logPartPtr.i = signal->theData[0];
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logPartPtr.p->logPartState = LogPartRecord::SR_THIRD_PHASE_COMPLETED;
+ /* ------------------------------------------------------------------------
+ * WE MUST RELEASE THE TC CONNECT RECORD HERE SO THAT IT CAN BE REUSED.
+ * ----------------------------------------------------------------------- */
+ tcConnectptr.i = logPartPtr.p->logTcConrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ releaseTcrecLog(signal, tcConnectptr);
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ if (logPartPtr.p->logPartState != LogPartRecord::SR_THIRD_PHASE_COMPLETED) {
+ if (logPartPtr.p->logPartState != LogPartRecord::SR_THIRD_PHASE_STARTED) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ } else {
+ jam();
+ /* ------------------------------------------------------------------
+ * THIS LOG PART WAS NOT COMPLETED YET. EXIT AND WAIT FOR IT
+ * TO COMPLETE
+ * ----------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//for
+ /* ------------------------------------------------------------------------
+ * ALL LOG PARTS HAVE COMPLETED THE EXECUTION OF THE LOG. WE CAN NOW START
+ * SENDING THE EXEC_FRAGCONF SIGNALS TO ALL INVOLVED FRAGMENTS.
+ * ----------------------------------------------------------------------- */
+ if (cstartType != NodeState::ST_NODE_RESTART) {
+ jam();
+ signal->theData[0] = ZSEND_EXEC_CONF;
+ signal->theData[1] = 0;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ } else {
+ jam();
+ /* ----------------------------------------------------------------------
+ * FOR NODE RESTART WE CAN SKIP A NUMBER OF STEPS SINCE WE HAVE NO
+ * FRAGMENTS DEFINED AT THIS POINT. OBVIOUSLY WE WILL NOT NEED TO
+ * EXECUTE ANY MORE LOG STEPS EITHER AND THUS WE CAN IMMEDIATELY
+ * START FINDING THE END AND THE START OF THE LOG.
+ * --------------------------------------------------------------------- */
+ csrPhasesCompleted = 3;
+ execSrCompletedLab(signal);
+ return;
+ }//if
+ return;
+}//Dblqh::execLogComp()
+
+/* --------------------------------------------------------------------------
+ * GO THROUGH THE FRAGMENT RECORDS TO DEDUCE TO WHICH SHALL BE SENT
+ * EXEC_FRAGCONF AFTER COMPLETING THE EXECUTION OF THE LOG.
+ * ------------------------------------------------------------------------- */
+void Dblqh::sendExecConf(Signal* signal)
+{
+ jamEntry();
+ fragptr.i = signal->theData[0];
+ Uint32 loopCount = 0;
+ while (fragptr.i < cfragrecFileSize) {
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ if (fragptr.p->execSrStatus != Fragrecord::IDLE) {
+ jam();
+ ndbrequire(fragptr.p->execSrNoReplicas - 1 < 4);
+ for (Uint32 i = 0; i < fragptr.p->execSrNoReplicas; i++) {
+ jam();
+ signal->theData[0] = fragptr.p->execSrUserptr[i];
+ sendSignal(fragptr.p->execSrBlockref[i], GSN_EXEC_FRAGCONF,
+ signal, 1, JBB);
+ }//for
+ if (fragptr.p->execSrStatus == Fragrecord::ACTIVE) {
+ jam();
+ fragptr.p->execSrStatus = Fragrecord::IDLE;
+ } else {
+ ndbrequire(fragptr.p->execSrStatus == Fragrecord::ACTIVE_REMOVE_AFTER);
+ jam();
+ Uint32 fragId = fragptr.p->fragId;
+ tabptr.i = fragptr.p->tabRef;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ deleteFragrec(fragId);
+ }//if
+ fragptr.p->execSrNoReplicas = 0;
+ }//if
+ loopCount++;
+ if (loopCount > 20) {
+ jam();
+ signal->theData[0] = ZSEND_EXEC_CONF;
+ signal->theData[1] = fragptr.i + 1;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ } else {
+ jam();
+ fragptr.i++;
+ }//if
+ }//while
+ /* ----------------------------------------------------------------------
+ * WE HAVE NOW SENT ALL EXEC_FRAGCONF. NOW IT IS TIME TO SEND
+ * EXEC_SRCONF TO ALL NODES.
+ * --------------------------------------------------------------------- */
+ srPhase3Comp(signal);
+}//Dblqh::sendExecConf()
+
+/* --------------------------------------------------------------------------
+ * PHASE 3 HAS NOW COMPLETED. INFORM ALL OTHER NODES OF THIS EVENT.
+ * ------------------------------------------------------------------------- */
+void Dblqh::srPhase3Comp(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(cnoOfNodes < MAX_NDB_NODES);
+ for (Uint32 i = 0; i < cnoOfNodes; i++) {
+ jam();
+ if (cnodeStatus[i] == ZNODE_UP) {
+ jam();
+ ndbrequire(cnodeData[i] < MAX_NDB_NODES);
+ BlockReference ref = calcLqhBlockRef(cnodeData[i]);
+ signal->theData[0] = cownNodeid;
+ sendSignal(ref, GSN_EXEC_SRCONF, signal, 1, JBB);
+ }//if
+ }//for
+ return;
+}//Dblqh::srPhase3Comp()
+
+/* ##########################################################################
+ * SYSTEM RESTART PHASE FOUR MODULE
+ * THIS MODULE IS A SUB-MODULE OF THE FILE SYSTEM HANDLING.
+ *
+ * THIS MODULE SETS UP THE HEAD AND TAIL POINTERS OF THE LOG PARTS IN THE
+ * FRAGMENT LOG. WHEN IT IS COMPLETED IT REPORTS TO THE MASTER DIH THAT
+ * IT HAS COMPLETED THE PART OF THE SYSTEM RESTART WHERE THE DATABASE IS
+ * LOADED.
+ * IT ALSO OPENS THE CURRENT LOG FILE AND THE NEXT AND SETS UP THE FIRST
+ * LOG PAGE WHERE NEW LOG DATA IS TO BE INSERTED WHEN THE SYSTEM STARTS
+ * AGAIN.
+ *
+ * THIS PART IS ACTUALLY EXECUTED FOR ALL RESTART TYPES.
+ * ######################################################################### */
+void Dblqh::initFourth(Signal* signal)
+{
+ LogFileRecordPtr locLogFilePtr;
+ jamEntry();
+ logPartPtr.i = signal->theData[0];
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ crestartNewestGci = 1;
+ crestartOldestGci = 1;
+ /* ------------------------------------------------------------------------
+ * INITIALISE LOG PART AND LOG FILES AS NEEDED.
+ * ----------------------------------------------------------------------- */
+ logPartPtr.p->headFileNo = 0;
+ logPartPtr.p->headPageNo = 1;
+ logPartPtr.p->headPageIndex = ZPAGE_HEADER_SIZE + 2;
+ logPartPtr.p->logPartState = LogPartRecord::SR_FOURTH_PHASE_STARTED;
+ logPartPtr.p->logTailFileNo = 0;
+ logPartPtr.p->logTailMbyte = 0;
+ locLogFilePtr.i = logPartPtr.p->firstLogfile;
+ ptrCheckGuard(locLogFilePtr, clogFileFileSize, logFileRecord);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_FOURTH_PHASE;
+ openFileRw(signal, locLogFilePtr);
+ return;
+}//Dblqh::initFourth()
+
+void Dblqh::openSrFourthPhaseLab(Signal* signal)
+{
+ /* ------------------------------------------------------------------------
+ * WE HAVE NOW OPENED THE HEAD LOG FILE WE WILL NOW START READING IT
+ * FROM THE HEAD MBYTE TO FIND THE NEW HEAD OF THE LOG.
+ * ----------------------------------------------------------------------- */
+ readSinglePage(signal, logPartPtr.p->headPageNo);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_FOURTH_PHASE;
+ return;
+}//Dblqh::openSrFourthPhaseLab()
+
+void Dblqh::readSrFourthPhaseLab(Signal* signal)
+{
+ if(c_diskless){
+ jam();
+ logPagePtr.p->logPageWord[ZPOS_LOG_LAP] = 1;
+ }
+
+ /* ------------------------------------------------------------------------
+ * INITIALISE ALL LOG PART INFO AND LOG FILE INFO THAT IS NEEDED TO
+ * START UP THE SYSTEM.
+ * ------------------------------------------------------------------------
+ * INITIALISE THE NEWEST GLOBAL CHECKPOINT IDENTITY AND THE NEWEST
+ * COMPLETED GLOBAL CHECKPOINT IDENITY AS THE NEWEST THAT WAS RESTARTED.
+ * ------------------------------------------------------------------------
+ * INITIALISE THE HEAD PAGE INDEX IN THIS PAGE.
+ * ASSIGN IT AS THE CURRENT LOGPAGE.
+ * ASSIGN THE FILE AS THE CURRENT LOG FILE.
+ * ASSIGN THE CURRENT FILE NUMBER FROM THE CURRENT LOG FILE AND THE NEXT
+ * FILE NUMBER FROM THE NEXT LOG FILE.
+ * ASSIGN THE CURRENT FILEPAGE FROM HEAD PAGE NUMBER.
+ * ASSIGN THE CURRENT MBYTE BY DIVIDING PAGE NUMBER BY 128.
+ * INITIALISE LOG LAP TO BE THE LOG LAP AS FOUND IN THE HEAD PAGE.
+ * WE HAVE TO CALCULATE THE NUMBER OF REMAINING WORDS IN THIS MBYTE.
+ * ----------------------------------------------------------------------- */
+ cnewestGci = crestartNewestGci;
+ cnewestCompletedGci = crestartNewestGci;
+ logPartPtr.p->logPartNewestCompletedGCI = cnewestCompletedGci;
+ logPartPtr.p->currentLogfile = logFilePtr.i;
+ logFilePtr.p->filePosition = logPartPtr.p->headPageNo;
+ logFilePtr.p->currentMbyte =
+ logPartPtr.p->headPageNo >> ZTWOLOG_NO_PAGES_IN_MBYTE;
+ logFilePtr.p->fileChangeState = LogFileRecord::NOT_ONGOING;
+ logPartPtr.p->logLap = logPagePtr.p->logPageWord[ZPOS_LOG_LAP];
+ logFilePtr.p->currentFilepage = logPartPtr.p->headPageNo;
+ logFilePtr.p->currentLogpage = logPagePtr.i;
+
+ initLogpage(signal);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPartPtr.p->headPageIndex;
+ logFilePtr.p->remainingWordsInMbyte =
+ ((
+ ((logFilePtr.p->currentMbyte + 1) * ZPAGES_IN_MBYTE) -
+ logFilePtr.p->currentFilepage) *
+ (ZPAGE_SIZE - ZPAGE_HEADER_SIZE)) -
+ (logPartPtr.p->headPageIndex - ZPAGE_HEADER_SIZE);
+ /* ------------------------------------------------------------------------
+ * THE NEXT STEP IS TO OPEN THE NEXT LOG FILE (IF THERE IS ONE).
+ * ----------------------------------------------------------------------- */
+ if (logFilePtr.p->nextLogFile != logFilePtr.i) {
+ LogFileRecordPtr locLogFilePtr;
+ jam();
+ locLogFilePtr.i = logFilePtr.p->nextLogFile;
+ ptrCheckGuard(locLogFilePtr, clogFileFileSize, logFileRecord);
+ locLogFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_FOURTH_NEXT;
+ openFileRw(signal, locLogFilePtr);
+ } else {
+ jam();
+ /* ----------------------------------------------------------------------
+ * THIS CAN ONLY OCCUR IF WE HAVE ONLY ONE LOG FILE. THIS LOG FILE MUST
+ * BE LOG FILE ZERO AND THAT IS THE FILE WE CURRENTLY HAVE READ.
+ * THUS WE CAN CONTINUE IMMEDIATELY TO READ PAGE ZERO IN FILE ZERO.
+ * --------------------------------------------------------------------- */
+ openSrFourthZeroSkipInitLab(signal);
+ return;
+ }//if
+ return;
+}//Dblqh::readSrFourthPhaseLab()
+
+void Dblqh::openSrFourthNextLab(Signal* signal)
+{
+ /* ------------------------------------------------------------------------
+ * WE MUST ALSO HAVE FILE 0 OPEN ALL THE TIME.
+ * ----------------------------------------------------------------------- */
+ logFilePtr.i = logPartPtr.p->firstLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ if (logFilePtr.p->logFileStatus == LogFileRecord::OPEN) {
+ jam();
+ openSrFourthZeroSkipInitLab(signal);
+ return;
+ } else {
+ jam();
+ logFilePtr.p->logFileStatus = LogFileRecord::OPEN_SR_FOURTH_ZERO;
+ openFileRw(signal, logFilePtr);
+ }//if
+ return;
+}//Dblqh::openSrFourthNextLab()
+
+void Dblqh::openSrFourthZeroLab(Signal* signal)
+{
+ openSrFourthZeroSkipInitLab(signal);
+ return;
+}//Dblqh::openSrFourthZeroLab()
+
+void Dblqh::openSrFourthZeroSkipInitLab(Signal* signal)
+{
+ if (logFilePtr.i == logPartPtr.p->currentLogfile) {
+ if (logFilePtr.p->currentFilepage == 0) {
+ jam();
+ /* -------------------------------------------------------------------
+ * THE HEADER PAGE IN THE LOG IS PAGE ZERO IN FILE ZERO.
+ * THIS SHOULD NEVER OCCUR.
+ * ------------------------------------------------------------------- */
+ systemErrorLab(signal);
+ return;
+ }//if
+ }//if
+ readSinglePage(signal, 0);
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_SR_FOURTH_ZERO;
+ return;
+}//Dblqh::openSrFourthZeroSkipInitLab()
+
+void Dblqh::readSrFourthZeroLab(Signal* signal)
+{
+ logFilePtr.p->logPageZero = logPagePtr.i;
+ // --------------------------------------------------------------------
+ // This is moved to invalidateLogAfterLastGCI(), RT453.
+ // signal->theData[0] = ZSR_FOURTH_COMP;
+ // signal->theData[1] = logPartPtr.i;
+ // sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ // --------------------------------------------------------------------
+
+ // Need to invalidate log pages after the head of the log. RT 453. EDTJAMO.
+ // Set the start of the invalidation.
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPartPtr.p->invalidateFileNo = logPartPtr.p->headFileNo;
+ logPartPtr.p->invalidatePageNo = logPartPtr.p->headPageNo;
+
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG_INVALIDATE;
+ seizeLfo(signal);
+ initLfo(signal);
+ // The state here is a little confusing, but simulates that we return
+ // to invalidateLogAfterLastGCI() from an invalidate write and are ready
+ // to read a page from file.
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_SR_INVALIDATE_PAGES;
+
+ invalidateLogAfterLastGCI(signal);
+ return;
+}//Dblqh::readSrFourthZeroLab()
+
+/* --------------------------------------------------------------------------
+ * ONE OF THE LOG PARTS HAVE COMPLETED PHASE FOUR OF THE SYSTEM RESTART.
+ * CHECK IF ALL LOG PARTS ARE COMPLETED. IF SO SEND START_RECCONF
+ * ------------------------------------------------------------------------- */
+void Dblqh::srFourthComp(Signal* signal)
+{
+ jamEntry();
+ logPartPtr.i = signal->theData[0];
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logPartPtr.p->logPartState = LogPartRecord::SR_FOURTH_PHASE_COMPLETED;
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ jam();
+ ptrAss(logPartPtr, logPartRecord);
+ if (logPartPtr.p->logPartState != LogPartRecord::SR_FOURTH_PHASE_COMPLETED) {
+ if (logPartPtr.p->logPartState != LogPartRecord::SR_FOURTH_PHASE_STARTED) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ } else {
+ jam();
+ /* ------------------------------------------------------------------
+ * THIS LOG PART WAS NOT COMPLETED YET.
+ * EXIT AND WAIT FOR IT TO COMPLETE
+ * ----------------------------------------------------------------- */
+ return;
+ }//if
+ }//if
+ }//for
+ /* ------------------------------------------------------------------------
+ * ALL LOG PARTS HAVE COMPLETED PHASE FOUR OF THE SYSTEM RESTART.
+ * WE CAN NOW SEND START_RECCONF TO THE MASTER DIH IF IT WAS A
+ * SYSTEM RESTART. OTHERWISE WE WILL CONTINUE WITH AN INITIAL START.
+ * SET LOG PART STATE TO IDLE TO
+ * INDICATE THAT NOTHING IS GOING ON IN THE LOG PART.
+ * ----------------------------------------------------------------------- */
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ ptrAss(logPartPtr, logPartRecord);
+ logPartPtr.p->logPartState = LogPartRecord::IDLE;
+ }//for
+
+ if ((cstartType == NodeState::ST_INITIAL_START) ||
+ (cstartType == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+
+ ndbrequire(cinitialStartOngoing == ZTRUE);
+ cinitialStartOngoing = ZFALSE;
+
+ checkStartCompletedLab(signal);
+ return;
+ } else if ((cstartType == NodeState::ST_NODE_RESTART) ||
+ (cstartType == NodeState::ST_SYSTEM_RESTART)) {
+ jam();
+ StartRecConf * conf = (StartRecConf*)signal->getDataPtrSend();
+ conf->startingNodeId = getOwnNodeId();
+ sendSignal(cmasterDihBlockref, GSN_START_RECCONF, signal,
+ StartRecConf::SignalLength, JBB);
+
+ if(cstartType == NodeState::ST_SYSTEM_RESTART){
+ fragptr.i = c_redo_log_complete_frags;
+ while(fragptr.i != RNIL){
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ signal->theData[0] = fragptr.p->tabRef;
+ signal->theData[1] = fragptr.p->fragId;
+ sendSignal(DBACC_REF, GSN_EXPANDCHECK2, signal, 2, JBB);
+ fragptr.i = fragptr.p->nextFrag;
+ }
+ }
+ } else {
+ ndbrequire(false);
+ }//if
+ return;
+}//Dblqh::srFourthComp()
+
+/* ######################################################################### */
+/* ####### ERROR MODULE ####### */
+/* */
+/* ######################################################################### */
+void Dblqh::warningHandlerLab(Signal* signal)
+{
+ systemErrorLab(signal);
+ return;
+}//Dblqh::warningHandlerLab()
+
+/*---------------------------------------------------------------------------*/
+/* AN ERROR OCCURRED THAT WE WILL NOT TREAT AS SYSTEM ERROR. MOST OFTEN THIS */
+/* WAS CAUSED BY AN ERRONEUS SIGNAL SENT BY ANOTHER NODE. WE DO NOT WISH TO */
+/* CRASH BECAUSE OF FAULTS IN OTHER NODES. THUS WE ONLY REPORT A WARNING. */
+/* THIS IS CURRENTLY NOT IMPLEMENTED AND FOR THE MOMENT WE GENERATE A SYSTEM */
+/* ERROR SINCE WE WANT TO FIND FAULTS AS QUICKLY AS POSSIBLE IN A TEST PHASE.*/
+/* IN A LATER PHASE WE WILL CHANGE THIS TO BE A WARNING MESSAGE INSTEAD. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+/* THIS TYPE OF ERROR SHOULD NOT GENERATE A SYSTEM ERROR IN A PRODUCT */
+/* RELEASE. THIS IS A TEMPORARY SOLUTION DURING TEST PHASE TO QUICKLY */
+/* FIND ERRORS. NORMALLY THIS SHOULD GENERATE A WARNING MESSAGE ONTO */
+/* SOME ERROR LOGGER. THIS WILL LATER BE IMPLEMENTED BY SOME SIGNAL. */
+/*---------------------------------------------------------------------------*/
+/* ------ SYSTEM ERROR SITUATIONS ------- */
+/* IN SITUATIONS WHERE THE STATE IS ERRONEOUS OR IF THE ERROR OCCURS IN */
+/* THE COMMIT, COMPLETE OR ABORT PHASE, WE PERFORM A CRASH OF THE AXE VM*/
+/*---------------------------------------------------------------------------*/
+
+void Dblqh::systemErrorLab(Signal* signal)
+{
+ progError(0, 0);
+/*************************************************************************>*/
+/* WE WANT TO INVOKE AN IMMEDIATE ERROR HERE SO WE GET THAT BY */
+/* INSERTING A CERTAIN POINTER OUT OF RANGE. */
+/*************************************************************************>*/
+}//Dblqh::systemErrorLab()
+
+/* ------- ERROR SITUATIONS ------- */
+
+void Dblqh::aiStateErrorCheckLab(Signal* signal, Uint32* dataPtr, Uint32 length)
+{
+ ndbrequire(tcConnectptr.p->abortState != TcConnectionrec::ABORT_IDLE);
+ if (tcConnectptr.p->transactionState != TcConnectionrec::IDLE) {
+ jam();
+/*************************************************************************>*/
+/* TRANSACTION ABORT IS ONGOING. IT CAN STILL BE A PART OF AN */
+/* OPERATION THAT SHOULD CONTINUE SINCE THE TUPLE HAS NOT ARRIVED */
+/* YET. THIS IS POSSIBLE IF ACTIVE CREATION OF THE FRAGMENT IS */
+/* ONGOING. */
+/*************************************************************************>*/
+ if (tcConnectptr.p->activeCreat == ZTRUE) {
+ jam();
+/*************************************************************************>*/
+/* ONGOING ABORTS DURING ACTIVE CREATION MUST SAVE THE ATTRIBUTE INFO*/
+/* SO THAT IT CAN BE SENT TO THE NEXT NODE IN THE COMMIT CHAIN. THIS */
+/* IS NEEDED SINCE ALL ABORTS DURING CREATION OF A FRAGMENT ARE NOT */
+/* REALLY ERRORS. A MISSING TUPLE TO BE UPDATED SIMPLY MEANS THAT */
+/* IT HASN'T BEEN TRANSFERRED TO THE NEW REPLICA YET. */
+/*************************************************************************>*/
+/*************************************************************************>*/
+/* AFTER THIS ERROR THE ABORT MUST BE COMPLETED. TO ENSURE THIS SET */
+/* ACTIVE CREATION TO FALSE. THIS WILL ENSURE THAT THE ABORT IS */
+/* COMPLETED. */
+/*************************************************************************>*/
+ if (saveTupattrbuf(signal, dataPtr, length) == ZOK) {
+ jam();
+ if (tcConnectptr.p->transactionState ==
+ TcConnectionrec::WAIT_AI_AFTER_ABORT) {
+ if (tcConnectptr.p->currTupAiLen == tcConnectptr.p->totReclenAi) {
+ jam();
+/*************************************************************************>*/
+/* WE WERE WAITING FOR MORE ATTRIBUTE INFO AFTER A SUCCESSFUL ABORT */
+/* IN ACTIVE CREATION STATE. THE TRANSACTION SHOULD CONTINUE AS IF */
+/* IT WAS COMMITTED. NOW ALL INFO HAS ARRIVED AND WE CAN CONTINUE */
+/* WITH NORMAL PROCESSING AS IF THE TRANSACTION WAS PREPARED. */
+/* SINCE THE FRAGMENT IS UNDER CREATION WE KNOW THAT LOGGING IS */
+/* DISABLED. WE STILL HAVE TO CATER FOR DIRTY OPERATION OR NOT. */
+/*************************************************************************>*/
+ tcConnectptr.p->abortState = TcConnectionrec::ABORT_IDLE;
+ rwConcludedAiLab(signal);
+ return;
+ } else {
+ ndbrequire(tcConnectptr.p->currTupAiLen < tcConnectptr.p->totReclenAi);
+ jam();
+ return; /* STILL WAITING FOR MORE ATTRIBUTE INFO */
+ }//if
+ }//if
+ } else {
+ jam();
+/*************************************************************************>*/
+/* AFTER THIS ERROR THE ABORT MUST BE COMPLETED. TO ENSURE THIS SET */
+/* ACTIVE CREATION TO ABORT. THIS WILL ENSURE THAT THE ABORT IS */
+/* COMPLETED AND THAT THE ERROR CODE IS PROPERLY SET */
+/*************************************************************************>*/
+ tcConnectptr.p->errorCode = terrorCode;
+ tcConnectptr.p->activeCreat = ZFALSE;
+ if (tcConnectptr.p->transactionState ==
+ TcConnectionrec::WAIT_AI_AFTER_ABORT) {
+ jam();
+/*************************************************************************>*/
+/* ABORT IS ALREADY COMPLETED. WE NEED TO RESTART IT FROM WHERE IT */
+/* WAS INTERRUPTED. */
+/*************************************************************************>*/
+ continueAbortLab(signal);
+ return;
+ } else {
+ jam();
+ return;
+/*************************************************************************>*/
+// Abort is ongoing. It will complete since we set the activeCreat = ZFALSE
+/*************************************************************************>*/
+ }//if
+ }//if
+ }//if
+ }//if
+/*************************************************************************>*/
+/* TRANSACTION HAVE BEEN ABORTED. THUS IGNORE ALL SIGNALS BELONGING TO IT. */
+/*************************************************************************>*/
+ return;
+}//Dblqh::aiStateErrorCheckLab()
+
+void Dblqh::takeOverErrorLab(Signal* signal)
+{
+ terrorCode = ZTAKE_OVER_ERROR;
+ abortErrorLab(signal);
+ return;
+}//Dblqh::takeOverErrorLab()
+
+/* ##########################################################################
+ * TEST MODULE
+ * ######################################################################### */
+#ifdef VM_TRACE
+void Dblqh::execTESTSIG(Signal* signal)
+{
+ jamEntry();
+ Uint32 userpointer = signal->theData[0];
+ BlockReference userblockref = signal->theData[1];
+ Uint32 testcase = signal->theData[2];
+
+ signal->theData[0] = userpointer;
+ signal->theData[1] = cownref;
+ signal->theData[2] = testcase;
+ sendSignal(userblockref, GSN_TESTSIG, signal, 25, JBB);
+ return;
+}//Dblqh::execTESTSIG()
+
+/* *************** */
+/* MEMCHECKREQ > */
+/* *************** */
+/* ************************************************************************>>
+ * THIS SIGNAL IS PURELY FOR TESTING PURPOSES. IT CHECKS THE FREE LIST
+ * AND REPORTS THE NUMBER OF FREE RECORDS.
+ * THIS CAN BE DONE TO ENSURE THAT NO RECORDS HAS BEEN LOST
+ * ************************************************************************> */
+void Dblqh::execMEMCHECKREQ(Signal* signal)
+{
+ Uint32* dataPtr = &signal->theData[0];
+ jamEntry();
+ BlockReference userblockref = signal->theData[0];
+ Uint32 index = 0;
+ for (Uint32 i = 0; i < 7; i++)
+ dataPtr[i] = 0;
+ addfragptr.i = cfirstfreeAddfragrec;
+ while (addfragptr.i != RNIL) {
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ addfragptr.i = addfragptr.p->nextAddfragrec;
+ dataPtr[index]++;
+ }//while
+ index++;
+ attrinbufptr.i = cfirstfreeAttrinbuf;
+ while (attrinbufptr.i != RNIL) {
+ ptrCheckGuard(attrinbufptr, cattrinbufFileSize, attrbuf);
+ attrinbufptr.i = attrinbufptr.p->attrbuf[ZINBUF_NEXT];
+ dataPtr[index]++;
+ }//while
+ index++;
+ databufptr.i = cfirstfreeDatabuf;
+ while (databufptr.i != RNIL) {
+ ptrCheckGuard(databufptr, cdatabufFileSize, databuf);
+ databufptr.i = databufptr.p->nextDatabuf;
+ dataPtr[index]++;
+ }//while
+ index++;
+ fragptr.i = cfirstfreeFragrec;
+ while (fragptr.i != RNIL) {
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.i = fragptr.p->nextFrag;
+ dataPtr[index]++;
+ }//while
+ index++;
+ for (tabptr.i = 0;
+ tabptr.i < ctabrecFileSize;
+ tabptr.i++) {
+ ptrAss(tabptr, tablerec);
+ if (tabptr.p->tableStatus == Tablerec::NOT_DEFINED) {
+ dataPtr[index]++;
+ }//if
+ }//for
+ index++;
+ tcConnectptr.i = cfirstfreeTcConrec;
+ while (tcConnectptr.i != RNIL) {
+ ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ tcConnectptr.i = tcConnectptr.p->nextTcConnectrec;
+ dataPtr[index]++;
+ }//while
+ sendSignal(userblockref, GSN_MEMCHECKCONF, signal, 10, JBB);
+ return;
+}//Dblqh::execMEMCHECKREQ()
+
+#endif
+
+/* ************************************************************************* */
+/* ************************* STATEMENT BLOCKS ****************************** */
+/* ************************************************************************* */
+/* ========================================================================= */
+/* ====== BUILD LINKED LIST OF LOG PAGES AFTER RECEIVING FSREADCONF ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::buildLinkedLogPageList(Signal* signal)
+{
+ LogPageRecordPtr bllLogPagePtr;
+
+ arrGuard(lfoPtr.p->noPagesRw - 1, 16);
+ arrGuard(lfoPtr.p->noPagesRw, 16);
+ for (UintR tbllIndex = 0; tbllIndex < lfoPtr.p->noPagesRw; tbllIndex++) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * BUILD LINKED LIST BUT ALSO ENSURE THAT PAGE IS NOT SEEN AS DIRTY
+ * INITIALLY.
+ * --------------------------------------------------------------------- */
+ bllLogPagePtr.i = lfoPtr.p->logPageArray[tbllIndex];
+ ptrCheckGuard(bllLogPagePtr, clogPageFileSize, logPageRecord);
+
+// #if VM_TRACE
+// // Check logPage checksum before modifying it
+// Uint32 calcCheckSum = calcPageCheckSum(bllLogPagePtr);
+// Uint32 checkSum = bllLogPagePtr.p->logPageWord[ZPOS_CHECKSUM];
+// if (checkSum != calcCheckSum) {
+// ndbout << "Redolog: Checksum failure." << endl;
+// progError(__LINE__, ERR_NDBREQUIRE, "Redolog: Checksum failure.");
+// }
+// #endif
+
+ bllLogPagePtr.p->logPageWord[ZNEXT_PAGE] =
+ lfoPtr.p->logPageArray[tbllIndex + 1];
+ bllLogPagePtr.p->logPageWord[ZPOS_DIRTY] = ZNOT_DIRTY;
+ }//for
+ bllLogPagePtr.i = lfoPtr.p->logPageArray[lfoPtr.p->noPagesRw - 1];
+ ptrCheckGuard(bllLogPagePtr, clogPageFileSize, logPageRecord);
+ bllLogPagePtr.p->logPageWord[ZNEXT_PAGE] = RNIL;
+}//Dblqh::buildLinkedLogPageList()
+
+/* =========================================================================
+ * ======= CHANGE TO NEXT MBYTE IN LOG =======
+ *
+ * ========================================================================= */
+void Dblqh::changeMbyte(Signal* signal)
+{
+ writeNextLog(signal);
+ writeFileDescriptor(signal);
+}//Dblqh::changeMbyte()
+
+/* ========================================================================= */
+/* ====== CHECK IF THIS COMMIT LOG RECORD IS TO BE EXECUTED ======= */
+/* */
+/* SUBROUTINE SHORT NAME = CEL */
+/* ========================================================================= */
+Uint32 Dblqh::checkIfExecLog(Signal* signal)
+{
+ tabptr.i = tcConnectptr.p->tableref;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ if (getFragmentrec(signal, tcConnectptr.p->fragmentid) &&
+ (tabptr.p->schemaVersion == tcConnectptr.p->schemaVersion)) {
+ if (fragptr.p->execSrStatus != Fragrecord::IDLE) {
+ if (fragptr.p->execSrNoReplicas > logPartPtr.p->execSrExecuteIndex) {
+ ndbrequire((fragptr.p->execSrNoReplicas - 1) < 4);
+ for (Uint32 i = logPartPtr.p->execSrExecuteIndex;
+ i < fragptr.p->execSrNoReplicas;
+ i++) {
+ jam();
+ if (tcConnectptr.p->gci >= fragptr.p->execSrStartGci[i]) {
+ if (tcConnectptr.p->gci <= fragptr.p->execSrLastGci[i]) {
+ jam();
+ logPartPtr.p->execSrExecuteIndex = i;
+ return ZOK;
+ }//if
+ }//if
+ }//for
+ }//if
+ }//if
+ }//if
+ return ZNOT_OK;
+}//Dblqh::checkIfExecLog()
+
+/* ========================================================================= */
+/* == CHECK IF THERE IS LESS THAN 192 KBYTE IN THE BUFFER PLUS INCOMING === */
+/* READS ALREADY STARTED. IF SO IS THE CASE THEN START ANOTHER READ IF */
+/* THERE ARE MORE PAGES IN THIS MBYTE. */
+/* */
+/* ========================================================================= */
+void Dblqh::checkReadExecSr(Signal* signal)
+{
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG;
+ logPartPtr.p->execSrPagesRead = logPartPtr.p->execSrPagesRead + 8;
+ logPartPtr.p->execSrPagesReading = logPartPtr.p->execSrPagesReading - 8;
+ if ((logPartPtr.p->execSrPagesRead + logPartPtr.p->execSrPagesReading) <
+ ZREAD_AHEAD_SIZE) {
+ jam();
+ /* ----------------------------------------------------------------------
+ * WE HAVE LESS THAN 64 KBYTE OF LOG PAGES REMAINING IN MEMORY OR ON
+ * ITS WAY TO MAIN MEMORY. READ IN 8 MORE PAGES.
+ * --------------------------------------------------------------------- */
+ if ((logPartPtr.p->execSrPagesRead + logPartPtr.p->execSrPagesExecuted) <
+ ZPAGES_IN_MBYTE) {
+ jam();
+ /* --------------------------------------------------------------------
+ * THERE ARE MORE PAGES TO READ IN THIS MBYTE. READ THOSE FIRST
+ * IF >= ZPAGES_IN_MBYTE THEN THERE ARE NO MORE PAGES TO READ. THUS
+ * WE PROCEED WITH EXECUTION OF THE LOG.
+ * ------------------------------------------------------------------- */
+ readExecSr(signal);
+ logPartPtr.p->logExecState = LogPartRecord::LES_WAIT_READ_EXEC_SR;
+ }//if
+ }//if
+}//Dblqh::checkReadExecSr()
+
+/* ========================================================================= */
+/* ==== CHECK IF START OF NEW FRAGMENT IS COMPLETED AND WE CAN ======= */
+/* ==== GET THE START GCI ======= */
+/* */
+/* SUBROUTINE SHORT NAME = CTC */
+/* ========================================================================= */
+void Dblqh::checkScanTcCompleted(Signal* signal)
+{
+ tcConnectptr.p->logWriteState = TcConnectionrec::NOT_STARTED;
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ fragptr.p->activeTcCounter = fragptr.p->activeTcCounter - 1;
+ if (fragptr.p->activeTcCounter == 0) {
+ jam();
+ fragptr.p->startGci = cnewestGci + 1;
+ tabptr.i = tcConnectptr.p->tableref;
+ ptrCheckGuard(tabptr, ctabrecFileSize, tablerec);
+ sendCopyActiveConf(signal, tcConnectptr.p->tableref);
+ }//if
+}//Dblqh::checkScanTcCompleted()
+
+/* ==========================================================================
+ * === CHECK IF ALL PARTS OF A SYSTEM RESTART ON A FRAGMENT ARE COMPLETED ===
+ *
+ * SUBROUTINE SHORT NAME = CSC
+ * ========================================================================= */
+void Dblqh::checkSrCompleted(Signal* signal)
+{
+ LcpLocRecordPtr cscLcpLocptr;
+
+ terrorCode = ZOK;
+ ptrGuard(lcpPtr);
+ cscLcpLocptr.i = lcpPtr.p->firstLcpLocAcc;
+CSC_ACC_DOWHILE:
+ ptrCheckGuard(cscLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (cscLcpLocptr.p->lcpLocstate != LcpLocRecord::SR_ACC_COMPLETED) {
+ jam();
+ if (cscLcpLocptr.p->lcpLocstate != LcpLocRecord::SR_ACC_STARTED) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ return;
+ }//if
+ cscLcpLocptr.i = cscLcpLocptr.p->nextLcpLoc;
+ if (cscLcpLocptr.i != RNIL) {
+ jam();
+ goto CSC_ACC_DOWHILE;
+ }//if
+ cscLcpLocptr.i = lcpPtr.p->firstLcpLocTup;
+CSC_TUP_DOWHILE:
+ ptrCheckGuard(cscLcpLocptr, clcpLocrecFileSize, lcpLocRecord);
+ if (cscLcpLocptr.p->lcpLocstate != LcpLocRecord::SR_TUP_COMPLETED) {
+ jam();
+ if (cscLcpLocptr.p->lcpLocstate != LcpLocRecord::SR_TUP_STARTED) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ return;
+ }//if
+ cscLcpLocptr.i = cscLcpLocptr.p->nextLcpLoc;
+ if (cscLcpLocptr.i != RNIL) {
+ jam();
+ goto CSC_TUP_DOWHILE;
+ }//if
+ lcpPtr.p->lcpState = LcpRecord::LCP_SR_COMPLETED;
+}//Dblqh::checkSrCompleted()
+
+/* ------------------------------------------------------------------------- */
+/* ------ CLOSE A FILE DURING EXECUTION OF FRAGMENT LOG ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::closeFile(Signal* signal, LogFileRecordPtr clfLogFilePtr)
+{
+ signal->theData[0] = clfLogFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = clfLogFilePtr.i;
+ signal->theData[3] = ZCLOSE_NO_DELETE;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+}//Dblqh::closeFile()
+
+
+/* ---------------------------------------------------------------- */
+/* ---------------- A LOG PAGE HAVE BEEN COMPLETED ---------------- */
+/* */
+/* SUBROUTINE SHORT NAME = CLP */
+// Input Pointers:
+// logFilePtr
+// logPagePtr
+// logPartPtr
+// Defines lfoPtr
+/* ---------------------------------------------------------------- */
+void Dblqh::completedLogPage(Signal* signal, Uint32 clpType)
+{
+ LogPageRecordPtr clpLogPagePtr;
+ LogPageRecordPtr wlpLogPagePtr;
+ UintR twlpNoPages;
+ UintR twlpType;
+
+ if (logFilePtr.p->firstFilledPage == RNIL) {
+ jam();
+ logFilePtr.p->firstFilledPage = logPagePtr.i;
+ } else {
+ jam();
+ clpLogPagePtr.i = logFilePtr.p->lastFilledPage;
+ ptrCheckGuard(clpLogPagePtr, clogPageFileSize, logPageRecord);
+ clpLogPagePtr.p->logPageWord[ZNEXT_PAGE] = logPagePtr.i;
+ }//if
+ logFilePtr.p->lastFilledPage = logPagePtr.i;
+ logPagePtr.p->logPageWord[ZNEXT_PAGE] = RNIL;
+ logFilePtr.p->noLogpagesInBuffer = logFilePtr.p->noLogpagesInBuffer + 1;
+ if (logFilePtr.p->noLogpagesInBuffer != ZMAX_PAGES_WRITTEN) {
+ if (clpType != ZLAST_WRITE_IN_FILE) {
+ if (clpType != ZENFORCE_WRITE) {
+ jam();
+ return;
+ }//if
+ }//if
+ }//if
+ twlpType = clpType;
+/* ------------------------------------------------------------------------- */
+/* ------ WRITE A SET OF LOG PAGES TO DISK ------- */
+/* */
+/* SUBROUTINE SHORT NAME: WLP */
+/* ------------------------------------------------------------------------- */
+ seizeLfo(signal);
+ initLfo(signal);
+ Uint32* dataPtr = &signal->theData[6];
+ twlpNoPages = 0;
+ wlpLogPagePtr.i = logFilePtr.p->firstFilledPage;
+ do {
+ dataPtr[twlpNoPages] = wlpLogPagePtr.i;
+ twlpNoPages++;
+ ptrCheckGuard(wlpLogPagePtr, clogPageFileSize, logPageRecord);
+
+ // Calculate checksum for page
+ wlpLogPagePtr.p->logPageWord[ZPOS_CHECKSUM] = calcPageCheckSum(wlpLogPagePtr);
+ wlpLogPagePtr.i = wlpLogPagePtr.p->logPageWord[ZNEXT_PAGE];
+ } while (wlpLogPagePtr.i != RNIL);
+ ndbrequire(twlpNoPages < 9);
+ dataPtr[twlpNoPages] = logFilePtr.p->filePosition;
+/* -------------------------------------------------- */
+/* SET TIMER ON THIS LOG PART TO SIGNIFY THAT A */
+/* LOG RECORD HAS BEEN SENT AT THIS TIME. */
+/* -------------------------------------------------- */
+ logPartPtr.p->logPartTimer = logPartPtr.p->logTimer;
+ signal->theData[0] = logFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lfoPtr.i;
+ logFilePtr.p->logFilePagesToDiskWithoutSynch += twlpNoPages;
+ if (twlpType == ZLAST_WRITE_IN_FILE) {
+ jam();
+ logFilePtr.p->logFilePagesToDiskWithoutSynch = 0;
+ signal->theData[3] = ZLIST_OF_MEM_PAGES_SYNCH;
+ } else if (logFilePtr.p->logFilePagesToDiskWithoutSynch >
+ MAX_REDO_PAGES_WITHOUT_SYNCH) {
+ jam();
+ logFilePtr.p->logFilePagesToDiskWithoutSynch = 0;
+ signal->theData[3] = ZLIST_OF_MEM_PAGES_SYNCH;
+ } else {
+ jam();
+ signal->theData[3] = ZLIST_OF_MEM_PAGES;
+ }//if
+ signal->theData[4] = ZVAR_NO_LOG_PAGE_WORD;
+ signal->theData[5] = twlpNoPages;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 15, JBA);
+ if (twlpType == ZNORMAL) {
+ jam();
+ lfoPtr.p->lfoState = LogFileOperationRecord::ACTIVE_WRITE_LOG;
+ } else if (twlpType == ZLAST_WRITE_IN_FILE) {
+ jam();
+ lfoPtr.p->lfoState = LogFileOperationRecord::LAST_WRITE_IN_FILE;
+ } else {
+ ndbrequire(twlpType == ZENFORCE_WRITE);
+ jam();
+ lfoPtr.p->lfoState = LogFileOperationRecord::ACTIVE_WRITE_LOG;
+ }//if
+ /* ----------------------------------------------------------------------- */
+ /* ------ MOVE PAGES FROM LOG FILE TO LFO RECORD ------- */
+ /* */
+ /* ----------------------------------------------------------------------- */
+ /* -------------------------------------------------- */
+ /* MOVE PAGES TO LFO RECORD AND REMOVE THEM */
+ /* FROM LOG FILE RECORD. */
+ /* -------------------------------------------------- */
+ lfoPtr.p->firstLfoPage = logFilePtr.p->firstFilledPage;
+ logFilePtr.p->firstFilledPage = RNIL;
+ logFilePtr.p->lastFilledPage = RNIL;
+ logFilePtr.p->noLogpagesInBuffer = 0;
+
+ lfoPtr.p->noPagesRw = twlpNoPages;
+ lfoPtr.p->lfoPageNo = logFilePtr.p->filePosition;
+ lfoPtr.p->lfoWordWritten = ZPAGE_SIZE - 1;
+ logFilePtr.p->filePosition += twlpNoPages;
+}//Dblqh::completedLogPage()
+
+/* ---------------------------------------------------------------- */
+/* ---------------- DELETE FRAGMENT RECORD ------------------------ */
+/* */
+/* SUBROUTINE SHORT NAME = DFR */
+/* ---------------------------------------------------------------- */
+void Dblqh::deleteFragrec(Uint32 fragId)
+{
+ Uint32 indexFound= RNIL;
+ fragptr.i = RNIL;
+ for (Uint32 i = (MAX_FRAG_PER_NODE - 1); (Uint32)~i; i--) {
+ jam();
+ if (tabptr.p->fragid[i] == fragId) {
+ fragptr.i = tabptr.p->fragrec[i];
+ indexFound = i;
+ break;
+ }//if
+ }//for
+ if (fragptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ tabptr.p->fragid[indexFound] = ZNIL;
+ tabptr.p->fragrec[indexFound] = RNIL;
+ releaseFragrec();
+ }//if
+}//Dblqh::deleteFragrec()
+
+/* ------------------------------------------------------------------------- */
+/* ------- FIND LOG FILE RECORD GIVEN FILE NUMBER ------- */
+/* */
+/* INPUT: TFLF_FILE_NO THE FILE NUMBER */
+/* FLF_LOG_PART_PTR THE LOG PART RECORD */
+/* OUTPUT: FLF_LOG_FILE_PTR THE FOUND LOG FILE RECORD */
+/* SUBROUTINE SHORT NAME = FLF */
+/* ------------------------------------------------------------------------- */
+void Dblqh::findLogfile(Signal* signal,
+ Uint32 fileNo,
+ LogPartRecordPtr flfLogPartPtr,
+ LogFileRecordPtr* parLogFilePtr)
+{
+ LogFileRecordPtr locLogFilePtr;
+ locLogFilePtr.i = flfLogPartPtr.p->firstLogfile;
+ Uint32 loopCount = 0;
+ while (true) {
+ ptrCheckGuard(locLogFilePtr, clogFileFileSize, logFileRecord);
+ if (locLogFilePtr.p->fileNo == fileNo) {
+ jam();
+ ndbrequire(loopCount == fileNo);
+ parLogFilePtr->i = locLogFilePtr.i;
+ parLogFilePtr->p = locLogFilePtr.p;
+ return;
+ }//if
+ locLogFilePtr.i = locLogFilePtr.p->nextLogFile;
+ loopCount++;
+ ndbrequire(loopCount < flfLogPartPtr.p->noLogFiles);
+ }//while
+}//Dblqh::findLogfile()
+
+/* ------------------------------------------------------------------------- */
+/* ------ FIND PAGE REFERENCE IN MEMORY BUFFER AT LOG EXECUTION ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::findPageRef(Signal* signal, CommitLogRecord* commitLogRecord)
+{
+ UintR tfprIndex;
+
+ logPagePtr.i = RNIL;
+ if (ERROR_INSERTED(5020)) {
+ // Force system to read page from disk
+ return;
+ }
+ pageRefPtr.i = logPartPtr.p->lastPageRef;
+ do {
+ ptrCheckGuard(pageRefPtr, cpageRefFileSize, pageRefRecord);
+ if (commitLogRecord->fileNo == pageRefPtr.p->prFileNo) {
+ if (commitLogRecord->startPageNo >= pageRefPtr.p->prPageNo) {
+ if (commitLogRecord->startPageNo < (Uint16) (pageRefPtr.p->prPageNo + 8)) {
+ jam();
+ tfprIndex = commitLogRecord->startPageNo - pageRefPtr.p->prPageNo;
+ logPagePtr.i = pageRefPtr.p->pageRef[tfprIndex];
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ return;
+ }//if
+ }//if
+ }//if
+ pageRefPtr.i = pageRefPtr.p->prPrev;
+ } while (pageRefPtr.i != RNIL);
+}//Dblqh::findPageRef()
+
+/* ------------------------------------------------------------------------- */
+/* ------ GET FIRST OPERATION QUEUED FOR LOGGING ------- */
+/* */
+/* SUBROUTINE SHORT NAME = GFL */
+/* ------------------------------------------------------------------------- */
+void Dblqh::getFirstInLogQueue(Signal* signal)
+{
+ TcConnectionrecPtr gflTcConnectptr;
+/* -------------------------------------------------- */
+/* GET THE FIRST FROM THE LOG QUEUE AND REMOVE */
+/* IT FROM THE QUEUE. */
+/* -------------------------------------------------- */
+ gflTcConnectptr.i = logPartPtr.p->firstLogQueue;
+ ptrCheckGuard(gflTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ logPartPtr.p->firstLogQueue = gflTcConnectptr.p->nextTcLogQueue;
+ if (logPartPtr.p->firstLogQueue == RNIL) {
+ jam();
+ logPartPtr.p->lastLogQueue = RNIL;
+ }//if
+}//Dblqh::getFirstInLogQueue()
+
+/* ---------------------------------------------------------------- */
+/* ---------------- GET FRAGMENT RECORD --------------------------- */
+/* INPUT: TFRAGID FRAGMENT ID LOOKING FOR */
+/* TABPTR TABLE ID */
+/* SUBROUTINE SHORT NAME = GFR */
+/* ---------------------------------------------------------------- */
+bool Dblqh::getFragmentrec(Signal* signal, Uint32 fragId)
+{
+ for (Uint32 i = (MAX_FRAG_PER_NODE - 1); (UintR)~i; i--) {
+ jam();
+ if (tabptr.p->fragid[i] == fragId) {
+ fragptr.i = tabptr.p->fragrec[i];
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ return true;
+ }//if
+ }//for
+ return false;
+}//Dblqh::getFragmentrec()
+
+/* ========================================================================= */
+/* ====== INITIATE FRAGMENT RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseAddfragrec(Signal* signal)
+{
+ if (caddfragrecFileSize != 0) {
+ for (addfragptr.i = 0; addfragptr.i < caddfragrecFileSize; addfragptr.i++) {
+ ptrAss(addfragptr, addFragRecord);
+ addfragptr.p->addfragStatus = AddFragRecord::FREE;
+ addfragptr.p->nextAddfragrec = addfragptr.i + 1;
+ }//for
+ addfragptr.i = caddfragrecFileSize - 1;
+ ptrAss(addfragptr, addFragRecord);
+ addfragptr.p->nextAddfragrec = RNIL;
+ cfirstfreeAddfragrec = 0;
+ } else {
+ jam();
+ cfirstfreeAddfragrec = RNIL;
+ }//if
+}//Dblqh::initialiseAddfragrec()
+
+/* ========================================================================= */
+/* ====== INITIATE ATTRIBUTE IN AND OUT DATA BUFFER ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseAttrbuf(Signal* signal)
+{
+ if (cattrinbufFileSize != 0) {
+ for (attrinbufptr.i = 0;
+ attrinbufptr.i < cattrinbufFileSize;
+ attrinbufptr.i++) {
+ refresh_watch_dog();
+ ptrAss(attrinbufptr, attrbuf);
+ attrinbufptr.p->attrbuf[ZINBUF_NEXT] = attrinbufptr.i + 1;
+ }//for
+ /* NEXT ATTRINBUF */
+ attrinbufptr.i = cattrinbufFileSize - 1;
+ ptrAss(attrinbufptr, attrbuf);
+ attrinbufptr.p->attrbuf[ZINBUF_NEXT] = RNIL; /* NEXT ATTRINBUF */
+ cfirstfreeAttrinbuf = 0;
+ } else {
+ jam();
+ cfirstfreeAttrinbuf = RNIL;
+ }//if
+}//Dblqh::initialiseAttrbuf()
+
+/* ========================================================================= */
+/* ====== INITIATE DATA BUFFER ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseDatabuf(Signal* signal)
+{
+ if (cdatabufFileSize != 0) {
+ for (databufptr.i = 0; databufptr.i < cdatabufFileSize; databufptr.i++) {
+ refresh_watch_dog();
+ ptrAss(databufptr, databuf);
+ databufptr.p->nextDatabuf = databufptr.i + 1;
+ }//for
+ databufptr.i = cdatabufFileSize - 1;
+ ptrAss(databufptr, databuf);
+ databufptr.p->nextDatabuf = RNIL;
+ cfirstfreeDatabuf = 0;
+ } else {
+ jam();
+ cfirstfreeDatabuf = RNIL;
+ }//if
+}//Dblqh::initialiseDatabuf()
+
+/* ========================================================================= */
+/* ====== INITIATE FRAGMENT RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseFragrec(Signal* signal)
+{
+ if (cfragrecFileSize != 0) {
+ for (fragptr.i = 0; fragptr.i < cfragrecFileSize; fragptr.i++) {
+ refresh_watch_dog();
+ ptrAss(fragptr, fragrecord);
+ fragptr.p->fragStatus = Fragrecord::FREE;
+ fragptr.p->fragActiveStatus = ZFALSE;
+ fragptr.p->execSrStatus = Fragrecord::IDLE;
+ fragptr.p->srStatus = Fragrecord::SS_IDLE;
+ fragptr.p->nextFrag = fragptr.i + 1;
+ }//for
+ fragptr.i = cfragrecFileSize - 1;
+ ptrAss(fragptr, fragrecord);
+ fragptr.p->nextFrag = RNIL;
+ cfirstfreeFragrec = 0;
+ } else {
+ jam();
+ cfirstfreeFragrec = RNIL;
+ }//if
+}//Dblqh::initialiseFragrec()
+
+/* ========================================================================= */
+/* ====== INITIATE FRAGMENT RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseGcprec(Signal* signal)
+{
+ UintR tigpIndex;
+
+ if (cgcprecFileSize != 0) {
+ for (gcpPtr.i = 0; gcpPtr.i < cgcprecFileSize; gcpPtr.i++) {
+ ptrAss(gcpPtr, gcpRecord);
+ for (tigpIndex = 0; tigpIndex <= 3; tigpIndex++) {
+ gcpPtr.p->gcpLogPartState[tigpIndex] = ZIDLE;
+ gcpPtr.p->gcpSyncReady[tigpIndex] = ZFALSE;
+ }//for
+ }//for
+ }//if
+}//Dblqh::initialiseGcprec()
+
+/* ========================================================================= */
+/* ====== INITIATE LCP RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseLcpRec(Signal* signal)
+{
+ if (clcpFileSize != 0) {
+ for (lcpPtr.i = 0; lcpPtr.i < clcpFileSize; lcpPtr.i++) {
+ ptrAss(lcpPtr, lcpRecord);
+ lcpPtr.p->lcpState = LcpRecord::LCP_IDLE;
+ lcpPtr.p->lcpQueued = false;
+ lcpPtr.p->firstLcpLocAcc = RNIL;
+ lcpPtr.p->firstLcpLocTup = RNIL;
+ lcpPtr.p->reportEmpty = false;
+ lcpPtr.p->lastFragmentFlag = false;
+ }//for
+ }//if
+}//Dblqh::initialiseLcpRec()
+
+/* ========================================================================= */
+/* ====== INITIATE LCP LOCAL RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseLcpLocrec(Signal* signal)
+{
+ if (clcpLocrecFileSize != 0) {
+ for (lcpLocptr.i = 0; lcpLocptr.i < clcpLocrecFileSize; lcpLocptr.i++) {
+ ptrAss(lcpLocptr, lcpLocRecord);
+ lcpLocptr.p->nextLcpLoc = lcpLocptr.i + 1;
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::IDLE;
+ lcpLocptr.p->masterLcpRec = RNIL;
+ lcpLocptr.p->waitingBlock = LcpLocRecord::NONE;
+ }//for
+ lcpLocptr.i = clcpLocrecFileSize - 1;
+ ptrAss(lcpLocptr, lcpLocRecord);
+ lcpLocptr.p->nextLcpLoc = RNIL;
+ cfirstfreeLcpLoc = 0;
+ } else {
+ jam();
+ cfirstfreeLcpLoc = RNIL;
+ }//if
+}//Dblqh::initialiseLcpLocrec()
+
+/* ========================================================================= */
+/* ====== INITIATE LOG FILE OPERATION RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseLfo(Signal* signal)
+{
+ if (clfoFileSize != 0) {
+ for (lfoPtr.i = 0; lfoPtr.i < clfoFileSize; lfoPtr.i++) {
+ ptrAss(lfoPtr, logFileOperationRecord);
+ lfoPtr.p->lfoState = LogFileOperationRecord::IDLE;
+ lfoPtr.p->lfoTimer = 0;
+ lfoPtr.p->nextLfo = lfoPtr.i + 1;
+ }//for
+ lfoPtr.i = clfoFileSize - 1;
+ ptrAss(lfoPtr, logFileOperationRecord);
+ lfoPtr.p->nextLfo = RNIL;
+ cfirstfreeLfo = 0;
+ } else {
+ jam();
+ cfirstfreeLfo = RNIL;
+ }//if
+}//Dblqh::initialiseLfo()
+
+/* ========================================================================= */
+/* ====== INITIATE LOG FILE RECORD ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseLogFile(Signal* signal)
+{
+ if (clogFileFileSize != 0) {
+ for (logFilePtr.i = 0; logFilePtr.i < clogFileFileSize; logFilePtr.i++) {
+ ptrAss(logFilePtr, logFileRecord);
+ logFilePtr.p->nextLogFile = logFilePtr.i + 1;
+ logFilePtr.p->logFileStatus = LogFileRecord::LFS_IDLE;
+ }//for
+ logFilePtr.i = clogFileFileSize - 1;
+ ptrAss(logFilePtr, logFileRecord);
+ logFilePtr.p->nextLogFile = RNIL;
+ cfirstfreeLogFile = 0;
+ } else {
+ jam();
+ cfirstfreeLogFile = RNIL;
+ }//if
+}//Dblqh::initialiseLogFile()
+
+/* ========================================================================= */
+/* ====== INITIATE LOG PAGES ======= */
+/* */
+/* ========================================================================= */
+void Dblqh::initialiseLogPage(Signal* signal)
+{
+ if (clogPageFileSize != 0) {
+ for (logPagePtr.i = 0; logPagePtr.i < clogPageFileSize; logPagePtr.i++) {
+ refresh_watch_dog();
+ ptrAss(logPagePtr, logPageRecord);
+ logPagePtr.p->logPageWord[ZNEXT_PAGE] = logPagePtr.i + 1;
+ }//for
+ logPagePtr.i = clogPageFileSize - 1;
+ ptrAss(logPagePtr, logPageRecord);
+ logPagePtr.p->logPageWord[ZNEXT_PAGE] = RNIL;
+ cfirstfreeLogPage = 0;
+ } else {
+ jam();
+ cfirstfreeLogPage = RNIL;
+ }//if
+ cnoOfLogPages = clogPageFileSize;
+}//Dblqh::initialiseLogPage()
+
+/* =========================================================================
+ * ====== INITIATE LOG PART RECORD =======
+ *
+ * ========================================================================= */
+void Dblqh::initialiseLogPart(Signal* signal)
+{
+ for (logPartPtr.i = 0; logPartPtr.i <= 3; logPartPtr.i++) {
+ ptrAss(logPartPtr, logPartRecord);
+ logPartPtr.p->waitWriteGciLog = LogPartRecord::WWGL_FALSE;
+ logPartPtr.p->LogLqhKeyReqSent = ZFALSE;
+ logPartPtr.p->logPartNewestCompletedGCI = (UintR)-1;
+ }//for
+}//Dblqh::initialiseLogPart()
+
+void Dblqh::initialisePageRef(Signal* signal)
+{
+ if (cpageRefFileSize != 0) {
+ for (pageRefPtr.i = 0;
+ pageRefPtr.i < cpageRefFileSize;
+ pageRefPtr.i++) {
+ ptrAss(pageRefPtr, pageRefRecord);
+ pageRefPtr.p->prNext = pageRefPtr.i + 1;
+ }//for
+ pageRefPtr.i = cpageRefFileSize - 1;
+ ptrAss(pageRefPtr, pageRefRecord);
+ pageRefPtr.p->prNext = RNIL;
+ cfirstfreePageRef = 0;
+ } else {
+ jam();
+ cfirstfreePageRef = RNIL;
+ }//if
+}//Dblqh::initialisePageRef()
+
+/* ==========================================================================
+ * ======= INITIATE RECORDS =======
+ *
+ * TAKES CARE OF INITIATION OF ALL RECORDS IN THIS BLOCK.
+ * ========================================================================= */
+void Dblqh::initialiseRecordsLab(Signal* signal, Uint32 data,
+ Uint32 retRef, Uint32 retData)
+{
+ Uint32 i;
+ switch (data) {
+ case 0:
+ jam();
+ for (i = 0; i < MAX_NDB_NODES; i++) {
+ cnodeSrState[i] = ZSTART_SR;
+ cnodeExecSrState[i] = ZSTART_SR;
+ }//for
+ for (i = 0; i < 1024; i++) {
+ ctransidHash[i] = RNIL;
+ }//for
+ for (i = 0; i < 4; i++) {
+ cactiveCopy[i] = RNIL;
+ }//for
+ cnoActiveCopy = 0;
+ cCounterAccCommitBlocked = 0;
+ cCounterTupCommitBlocked = 0;
+ caccCommitBlocked = false;
+ ctupCommitBlocked = false;
+ cCommitBlocked = false;
+ ccurrentGcprec = RNIL;
+ caddNodeState = ZFALSE;
+ cstartRecReq = ZFALSE;
+ cnewestGci = (UintR)-1;
+ cnewestCompletedGci = (UintR)-1;
+ crestartOldestGci = 0;
+ crestartNewestGci = 0;
+ cfirstWaitFragSr = RNIL;
+ cfirstCompletedFragSr = RNIL;
+ csrPhaseStarted = ZSR_NO_PHASE_STARTED;
+ csrPhasesCompleted = 0;
+ cmasterDihBlockref = 0;
+ cnoFragmentsExecSr = 0;
+ clcpCompletedState = LCP_IDLE;
+ csrExecUndoLogState = EULS_IDLE;
+ c_lcpId = 0;
+ cnoOfFragsCheckpointed = 0;
+ break;
+ case 1:
+ jam();
+ initialiseAddfragrec(signal);
+ break;
+ case 2:
+ jam();
+ initialiseAttrbuf(signal);
+ break;
+ case 3:
+ jam();
+ initialiseDatabuf(signal);
+ break;
+ case 4:
+ jam();
+ initialiseFragrec(signal);
+ break;
+ case 5:
+ jam();
+ initialiseGcprec(signal);
+ initialiseLcpRec(signal);
+ initialiseLcpLocrec(signal);
+ break;
+ case 6:
+ jam();
+ initialiseLogPage(signal);
+ break;
+ case 7:
+ jam();
+ initialiseLfo(signal);
+ break;
+ case 8:
+ jam();
+ initialiseLogFile(signal);
+ initialiseLogPart(signal);
+ break;
+ case 9:
+ jam();
+ initialisePageRef(signal);
+ break;
+ case 10:
+ jam();
+ initialiseScanrec(signal);
+ break;
+ case 11:
+ jam();
+ initialiseTabrec(signal);
+ break;
+ case 12:
+ jam();
+ initialiseTcNodeFailRec(signal);
+ initialiseTcrec(signal);
+ {
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = retData;
+ sendSignal(retRef, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+ }
+ return;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+
+ signal->theData[0] = ZINITIALISE_RECORDS;
+ signal->theData[1] = data + 1;
+ signal->theData[2] = 0;
+ signal->theData[3] = retRef;
+ signal->theData[4] = retData;
+ sendSignal(DBLQH_REF, GSN_CONTINUEB, signal, 5, JBB);
+
+ return;
+}//Dblqh::initialiseRecordsLab()
+
+/* ==========================================================================
+ * ======= INITIATE TC CONNECTION RECORD =======
+ *
+ * ========================================================================= */
+void Dblqh::initialiseScanrec(Signal* signal)
+{
+ ndbrequire(cscanrecFileSize > 1);
+ DLList<ScanRecord> tmp(c_scanRecordPool);
+ while (tmp.seize(scanptr)){
+ //new (scanptr.p) ScanRecord();
+ refresh_watch_dog();
+ scanptr.p->scanType = ScanRecord::ST_IDLE;
+ scanptr.p->scanState = ScanRecord::SCAN_FREE;
+ scanptr.p->scanTcWaiting = ZFALSE;
+ scanptr.p->nextHash = RNIL;
+ scanptr.p->prevHash = RNIL;
+ scanptr.p->scan_acc_index= 0;
+ scanptr.p->scan_acc_attr_recs= 0;
+ }
+ tmp.release();
+}//Dblqh::initialiseScanrec()
+
+/* ==========================================================================
+ * ======= INITIATE TABLE RECORD =======
+ *
+ * ========================================================================= */
+void Dblqh::initialiseTabrec(Signal* signal)
+{
+ if (ctabrecFileSize != 0) {
+ for (tabptr.i = 0; tabptr.i < ctabrecFileSize; tabptr.i++) {
+ refresh_watch_dog();
+ ptrAss(tabptr, tablerec);
+ tabptr.p->tableStatus = Tablerec::NOT_DEFINED;
+ tabptr.p->usageCount = 0;
+ for (Uint32 i = 0; i <= (MAX_FRAG_PER_NODE - 1); i++) {
+ tabptr.p->fragid[i] = ZNIL;
+ tabptr.p->fragrec[i] = RNIL;
+ }//for
+ }//for
+ }//if
+}//Dblqh::initialiseTabrec()
+
+/* ==========================================================================
+ * ======= INITIATE TC CONNECTION RECORD =======
+ *
+ * ========================================================================= */
+void Dblqh::initialiseTcrec(Signal* signal)
+{
+ if (ctcConnectrecFileSize != 0) {
+ for (tcConnectptr.i = 0;
+ tcConnectptr.i < ctcConnectrecFileSize;
+ tcConnectptr.i++) {
+ refresh_watch_dog();
+ ptrAss(tcConnectptr, tcConnectionrec);
+ tcConnectptr.p->transactionState = TcConnectionrec::TC_NOT_CONNECTED;
+ tcConnectptr.p->tcScanRec = RNIL;
+ tcConnectptr.p->logWriteState = TcConnectionrec::NOT_STARTED;
+ tcConnectptr.p->firstAttrinbuf = RNIL;
+ tcConnectptr.p->lastAttrinbuf = RNIL;
+ tcConnectptr.p->firstTupkeybuf = RNIL;
+ tcConnectptr.p->lastTupkeybuf = RNIL;
+ tcConnectptr.p->tcTimer = 0;
+ tcConnectptr.p->nextTcConnectrec = tcConnectptr.i + 1;
+ }//for
+ tcConnectptr.i = ctcConnectrecFileSize - 1;
+ ptrAss(tcConnectptr, tcConnectionrec);
+ tcConnectptr.p->nextTcConnectrec = RNIL;
+ cfirstfreeTcConrec = 0;
+ } else {
+ jam();
+ cfirstfreeTcConrec = RNIL;
+ }//if
+}//Dblqh::initialiseTcrec()
+
+/* ==========================================================================
+ * ======= INITIATE TC CONNECTION RECORD =======
+ *
+ * ========================================================================= */
+void Dblqh::initialiseTcNodeFailRec(Signal* signal)
+{
+ if (ctcNodeFailrecFileSize != 0) {
+ for (tcNodeFailptr.i = 0;
+ tcNodeFailptr.i < ctcNodeFailrecFileSize;
+ tcNodeFailptr.i++) {
+ ptrAss(tcNodeFailptr, tcNodeFailRecord);
+ tcNodeFailptr.p->tcFailStatus = TcNodeFailRecord::TC_STATE_FALSE;
+ }//for
+ }//if
+}//Dblqh::initialiseTcNodeFailRec()
+
+/* ==========================================================================
+ * ======= INITIATE FRAGMENT RECORD =======
+ *
+ * SUBROUTINE SHORT NAME = IF
+ * ========================================================================= */
+void Dblqh::initFragrec(Signal* signal,
+ Uint32 tableId,
+ Uint32 fragId,
+ Uint32 copyType)
+{
+ new (fragptr.p) Fragrecord();
+ fragptr.p->m_scanNumberMask.set(); // All is free
+ fragptr.p->accBlockref = caccBlockref;
+ fragptr.p->accBlockedList = RNIL;
+ fragptr.p->activeList = RNIL;
+ fragptr.p->firstWaitQueue = RNIL;
+ fragptr.p->lastWaitQueue = RNIL;
+ fragptr.p->fragStatus = Fragrecord::DEFINED;
+ fragptr.p->fragCopy = copyType;
+ fragptr.p->tupBlockref = ctupBlockref;
+ fragptr.p->tuxBlockref = ctuxBlockref;
+ fragptr.p->lcpRef = RNIL;
+ fragptr.p->logFlag = Fragrecord::STATE_TRUE;
+ fragptr.p->lcpFlag = Fragrecord::LCP_STATE_TRUE;
+ for (Uint32 i = 0; i < MAX_LCP_STORED; i++) {
+ fragptr.p->lcpId[i] = 0;
+ }//for
+ fragptr.p->maxGciCompletedInLcp = 0;
+ fragptr.p->maxGciInLcp = 0;
+ fragptr.p->copyFragState = ZIDLE;
+ fragptr.p->nextFrag = RNIL;
+ fragptr.p->newestGci = cnewestGci;
+ fragptr.p->nextLcp = 0;
+ fragptr.p->tabRef = tableId;
+ fragptr.p->fragId = fragId;
+ fragptr.p->srStatus = Fragrecord::SS_IDLE;
+ fragptr.p->execSrStatus = Fragrecord::IDLE;
+ fragptr.p->execSrNoReplicas = 0;
+ fragptr.p->fragDistributionKey = 0;
+ fragptr.p->activeTcCounter = 0;
+ fragptr.p->tableFragptr = RNIL;
+}//Dblqh::initFragrec()
+
+/* ==========================================================================
+ * ======= INITIATE FRAGMENT RECORD FOR SYSTEM RESTART =======
+ *
+ * SUBROUTINE SHORT NAME = IFS
+ * ========================================================================= */
+void Dblqh::initFragrecSr(Signal* signal)
+{
+ const StartFragReq * const startFragReq = (StartFragReq *)&signal->theData[0];
+ Uint32 lcpNo = startFragReq->lcpNo;
+ Uint32 noOfLogNodes = startFragReq->noOfLogNodes;
+ ndbrequire(noOfLogNodes <= 4);
+ fragptr.p->fragStatus = Fragrecord::CRASH_RECOVERING;
+ fragptr.p->srBlockref = startFragReq->userRef;
+ fragptr.p->srUserptr = startFragReq->userPtr;
+ fragptr.p->srChkpnr = lcpNo;
+ if (lcpNo == (MAX_LCP_STORED - 1)) {
+ jam();
+ fragptr.p->lcpId[lcpNo] = startFragReq->lcpId;
+ fragptr.p->nextLcp = 0;
+ } else if (lcpNo < (MAX_LCP_STORED - 1)) {
+ jam();
+ fragptr.p->lcpId[lcpNo] = startFragReq->lcpId;
+ fragptr.p->nextLcp = lcpNo + 1;
+ } else {
+ ndbrequire(lcpNo == ZNIL);
+ jam();
+ fragptr.p->nextLcp = 0;
+ }//if
+ fragptr.p->srNoLognodes = noOfLogNodes;
+ fragptr.p->logFlag = Fragrecord::STATE_FALSE;
+ fragptr.p->srStatus = Fragrecord::SS_IDLE;
+ if (noOfLogNodes > 0) {
+ jam();
+ for (Uint32 i = 0; i < noOfLogNodes; i++) {
+ jam();
+ fragptr.p->srStartGci[i] = startFragReq->startGci[i];
+ fragptr.p->srLastGci[i] = startFragReq->lastGci[i];
+ fragptr.p->srLqhLognode[i] = startFragReq->lqhLogNode[i];
+ }//for
+ fragptr.p->newestGci = startFragReq->lastGci[noOfLogNodes - 1];
+ } else {
+ fragptr.p->newestGci = cnewestGci;
+ }//if
+}//Dblqh::initFragrecSr()
+
+/* ==========================================================================
+ * ======= INITIATE INFORMATION ABOUT GLOBAL CHECKPOINTS =======
+ * IN LOG FILE RECORDS
+ *
+ * INPUT: LOG_FILE_PTR CURRENT LOG FILE
+ * TNO_FD_DESCRIPTORS THE NUMBER OF FILE DESCRIPTORS
+ * TO READ FROM THE LOG PAGE
+ * LOG_PAGE_PTR PAGE ZERO IN LOG FILE
+ * SUBROUTINE SHORT NAME = IGL
+ * ========================================================================= */
+void Dblqh::initGciInLogFileRec(Signal* signal, Uint32 noFdDescriptors)
+{
+ LogFileRecordPtr iglLogFilePtr;
+ UintR tiglLoop;
+ UintR tiglIndex;
+
+ tiglLoop = 0;
+ iglLogFilePtr.i = logFilePtr.i;
+ iglLogFilePtr.p = logFilePtr.p;
+IGL_LOOP:
+ for (tiglIndex = 0; tiglIndex <= ZNO_MBYTES_IN_FILE - 1; tiglIndex++) {
+ arrGuard(((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (tiglLoop * ZFD_PART_SIZE)) + tiglIndex, ZPAGE_SIZE);
+ iglLogFilePtr.p->logMaxGciCompleted[tiglIndex] =
+ logPagePtr.p->logPageWord[((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (tiglLoop * ZFD_PART_SIZE)) + tiglIndex];
+ arrGuard((((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) + ZNO_MBYTES_IN_FILE) +
+ (tiglLoop * ZFD_PART_SIZE)) + tiglIndex, ZPAGE_SIZE);
+ iglLogFilePtr.p->logMaxGciStarted[tiglIndex] =
+ logPagePtr.p->logPageWord[(((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ ZNO_MBYTES_IN_FILE) +
+ (tiglLoop * ZFD_PART_SIZE)) + tiglIndex];
+ arrGuard((((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (2 * ZNO_MBYTES_IN_FILE)) + (tiglLoop * ZFD_PART_SIZE)) +
+ tiglIndex, ZPAGE_SIZE);
+ iglLogFilePtr.p->logLastPrepRef[tiglIndex] =
+ logPagePtr.p->logPageWord[(((ZPAGE_HEADER_SIZE + ZFD_HEADER_SIZE) +
+ (2 * ZNO_MBYTES_IN_FILE)) +
+ (tiglLoop * ZFD_PART_SIZE)) + tiglIndex];
+ }//for
+ tiglLoop = tiglLoop + 1;
+ if (tiglLoop < noFdDescriptors) {
+ jam();
+ iglLogFilePtr.i = iglLogFilePtr.p->prevLogFile;
+ ptrCheckGuard(iglLogFilePtr, clogFileFileSize, logFileRecord);
+ goto IGL_LOOP;
+ }//if
+}//Dblqh::initGciInLogFileRec()
+
+/* ==========================================================================
+ * ======= INITIATE LCP RECORD WHEN USED FOR SYSTEM RESTART =======
+ *
+ * SUBROUTINE SHORT NAME = ILS
+ * ========================================================================= */
+void Dblqh::initLcpSr(Signal* signal,
+ Uint32 lcpNo,
+ Uint32 lcpId,
+ Uint32 tableId,
+ Uint32 fragId,
+ Uint32 fragPtr)
+{
+ lcpPtr.p->lcpQueued = false;
+ lcpPtr.p->currentFragment.fragPtrI = fragPtr;
+ lcpPtr.p->currentFragment.lcpFragOrd.lcpNo = lcpNo;
+ lcpPtr.p->currentFragment.lcpFragOrd.lcpId = lcpId;
+ lcpPtr.p->currentFragment.lcpFragOrd.tableId = tableId;
+ lcpPtr.p->currentFragment.lcpFragOrd.fragmentId = fragId;
+ lcpPtr.p->lcpState = LcpRecord::LCP_SR_WAIT_FRAGID;
+ lcpPtr.p->firstLcpLocAcc = RNIL;
+ lcpPtr.p->firstLcpLocTup = RNIL;
+ lcpPtr.p->lcpAccptr = RNIL;
+}//Dblqh::initLcpSr()
+
+/* ==========================================================================
+ * ======= INITIATE LOG PART =======
+ *
+ * ========================================================================= */
+void Dblqh::initLogpart(Signal* signal)
+{
+ logPartPtr.p->execSrLogPage = RNIL;
+ logPartPtr.p->execSrLogPageIndex = ZNIL;
+ logPartPtr.p->execSrExecuteIndex = 0;
+ logPartPtr.p->noLogFiles = cnoLogFiles;
+ logPartPtr.p->logLap = 0;
+ logPartPtr.p->logTailFileNo = 0;
+ logPartPtr.p->logTailMbyte = 0;
+ logPartPtr.p->lastMbyte = ZNIL;
+ logPartPtr.p->logPartState = LogPartRecord::SR_FIRST_PHASE;
+ logPartPtr.p->logExecState = LogPartRecord::LES_IDLE;
+ logPartPtr.p->firstLogTcrec = RNIL;
+ logPartPtr.p->lastLogTcrec = RNIL;
+ logPartPtr.p->firstLogQueue = RNIL;
+ logPartPtr.p->lastLogQueue = RNIL;
+ logPartPtr.p->gcprec = RNIL;
+ logPartPtr.p->firstPageRef = RNIL;
+ logPartPtr.p->lastPageRef = RNIL;
+ logPartPtr.p->headFileNo = ZNIL;
+ logPartPtr.p->headPageNo = ZNIL;
+ logPartPtr.p->headPageIndex = ZNIL;
+}//Dblqh::initLogpart()
+
+/* ==========================================================================
+ * ======= INITIATE LOG POINTERS =======
+ *
+ * ========================================================================= */
+void Dblqh::initLogPointers(Signal* signal)
+{
+ logPartPtr.i = tcConnectptr.p->hashValue & 3;
+ ptrCheckGuard(logPartPtr, clogPartFileSize, logPartRecord);
+ logFilePtr.i = logPartPtr.p->currentLogfile;
+ ptrCheckGuard(logFilePtr, clogFileFileSize, logFileRecord);
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+}//Dblqh::initLogPointers()
+
+/* ------------------------------------------------------------------------- */
+/* ------- INIT REQUEST INFO BEFORE EXECUTING A LOG RECORD ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::initReqinfoExecSr(Signal* signal)
+{
+ UintR Treqinfo = 0;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ LqhKeyReq::setKeyLen(Treqinfo, regTcPtr->primKeyLen);
+/* ------------------------------------------------------------------------- */
+/* NUMBER OF BACKUPS AND STANDBYS ARE ZERO AND NEED NOT BE SET. */
+/* REPLICA TYPE IS CLEARED BY SEND_LQHKEYREQ. */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* SET LAST REPLICA NUMBER TO ZERO (BIT 10-11) */
+/* ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* SET DIRTY FLAG */
+/* ------------------------------------------------------------------------- */
+ LqhKeyReq::setDirtyFlag(Treqinfo, 1);
+/* ------------------------------------------------------------------------- */
+/* SET SIMPLE TRANSACTION */
+/* ------------------------------------------------------------------------- */
+ LqhKeyReq::setSimpleFlag(Treqinfo, 1);
+/* ------------------------------------------------------------------------- */
+/* SET OPERATION TYPE AND LOCK MODE (NEVER READ OPERATION OR SCAN IN LOG) */
+/* ------------------------------------------------------------------------- */
+ LqhKeyReq::setLockType(Treqinfo, regTcPtr->operation);
+ LqhKeyReq::setOperation(Treqinfo, regTcPtr->operation);
+ regTcPtr->reqinfo = Treqinfo;
+/* ------------------------------------------------------------------------ */
+/* NO OF BACKUP IS SET TO ONE AND NUMBER OF STANDBY NODES IS SET TO ZERO. */
+/* THUS THE RECEIVING NODE WILL EXPECT THAT IT IS THE LAST NODE AND WILL */
+/* SEND COMPLETED AS THE RESPONSE SIGNAL SINCE DIRTY_OP BIT IS SET. */
+/* ------------------------------------------------------------------------ */
+/* ------------------------------------------------------------------------- */
+/* SET REPLICA TYPE TO PRIMARY AND NUMBER OF REPLICA TO ONE */
+/* ------------------------------------------------------------------------- */
+ regTcPtr->lastReplicaNo = 0;
+ regTcPtr->apiVersionNo = 0;
+ regTcPtr->nextSeqNoReplica = 0;
+ regTcPtr->opExec = 0;
+ regTcPtr->storedProcId = ZNIL;
+ regTcPtr->readlenAi = 0;
+ regTcPtr->nodeAfterNext[0] = ZNIL;
+ regTcPtr->nodeAfterNext[1] = ZNIL;
+ regTcPtr->dirtyOp = ZFALSE;
+ regTcPtr->tcBlockref = cownref;
+}//Dblqh::initReqinfoExecSr()
+
+/* --------------------------------------------------------------------------
+ * ------- INSERT FRAGMENT -------
+ *
+ * ------------------------------------------------------------------------- */
+bool Dblqh::insertFragrec(Signal* signal, Uint32 fragId)
+{
+ terrorCode = ZOK;
+ if (cfirstfreeFragrec == RNIL) {
+ jam();
+ terrorCode = ZNO_FREE_FRAGMENTREC;
+ return false;
+ }//if
+ seizeFragmentrec(signal);
+ for (Uint32 i = (MAX_FRAG_PER_NODE - 1); (Uint32)~i; i--) {
+ jam();
+ if (tabptr.p->fragid[i] == ZNIL) {
+ jam();
+ tabptr.p->fragid[i] = fragId;
+ tabptr.p->fragrec[i] = fragptr.i;
+ return true;
+ }//if
+ }//for
+ terrorCode = ZTOO_MANY_FRAGMENTS;
+ return false;
+}//Dblqh::insertFragrec()
+
+/* --------------------------------------------------------------------------
+ * ------- LINK OPERATION IN ACTIVE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME: LFQ
+// Input Pointers:
+// tcConnectptr
+// fragptr
+* ------------------------------------------------------------------------- */
+void Dblqh::linkFragQueue(Signal* signal)
+{
+ TcConnectionrecPtr lfqTcConnectptr;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ Fragrecord * const regFragPtr = fragptr.p;
+ Uint32 tcIndex = tcConnectptr.i;
+
+ lfqTcConnectptr.i = regFragPtr->lastWaitQueue;
+ regTcPtr->nextTc = RNIL;
+ regFragPtr->lastWaitQueue = tcIndex;
+ regTcPtr->prevTc = lfqTcConnectptr.i;
+ ndbrequire(regTcPtr->listState == TcConnectionrec::NOT_IN_LIST);
+ regTcPtr->listState = TcConnectionrec::WAIT_QUEUE_LIST;
+ if (lfqTcConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(lfqTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ lfqTcConnectptr.p->nextTc = tcIndex;
+ } else {
+ regFragPtr->firstWaitQueue = tcIndex;
+ }//if
+ return;
+}//Dblqh::linkFragQueue()
+
+/* -------------------------------------------------------------------------
+ * ------- LINK OPERATION INTO WAITING FOR LOGGING -------
+ *
+ * SUBROUTINE SHORT NAME = LWL
+// Input Pointers:
+// tcConnectptr
+// logPartPtr
+ * ------------------------------------------------------------------------- */
+void Dblqh::linkWaitLog(Signal* signal, LogPartRecordPtr regLogPartPtr)
+{
+ TcConnectionrecPtr lwlTcConnectptr;
+
+/* -------------------------------------------------- */
+/* LINK ACTIVE OPERATION INTO QUEUE WAITING FOR */
+/* ACCESS TO THE LOG PART. */
+/* -------------------------------------------------- */
+ lwlTcConnectptr.i = regLogPartPtr.p->lastLogQueue;
+ if (lwlTcConnectptr.i == RNIL) {
+ jam();
+ regLogPartPtr.p->firstLogQueue = tcConnectptr.i;
+ } else {
+ jam();
+ ptrCheckGuard(lwlTcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ lwlTcConnectptr.p->nextTcLogQueue = tcConnectptr.i;
+ }//if
+ regLogPartPtr.p->lastLogQueue = tcConnectptr.i;
+ tcConnectptr.p->nextTcLogQueue = RNIL;
+ if (regLogPartPtr.p->LogLqhKeyReqSent == ZFALSE) {
+ jam();
+ regLogPartPtr.p->LogLqhKeyReqSent = ZTRUE;
+ signal->theData[0] = ZLOG_LQHKEYREQ;
+ signal->theData[1] = regLogPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+}//Dblqh::linkWaitLog()
+
+/* --------------------------------------------------------------------------
+ * ------- START THE NEXT OPERATION ON THIS LOG PART IF ANY -------
+ * ------- OPERATIONS ARE QUEUED. -------
+ *
+ * SUBROUTINE SHORT NAME = LNS
+// Input Pointers:
+// tcConnectptr
+// logPartPtr
+ * ------------------------------------------------------------------------- */
+void Dblqh::logNextStart(Signal* signal)
+{
+ LogPartRecordPtr lnsLogPartPtr;
+ UintR tlnsStillWaiting;
+ LogPartRecord * const regLogPartPtr = logPartPtr.p;
+
+ if ((regLogPartPtr->firstLogQueue == RNIL) &&
+ (regLogPartPtr->logPartState == LogPartRecord::ACTIVE) &&
+ (regLogPartPtr->waitWriteGciLog != LogPartRecord::WWGL_TRUE)) {
+// --------------------------------------------------------------------------
+// Optimised route for the common case
+// --------------------------------------------------------------------------
+ regLogPartPtr->logPartState = LogPartRecord::IDLE;
+ return;
+ }//if
+ if (regLogPartPtr->firstLogQueue != RNIL) {
+ jam();
+ if (regLogPartPtr->LogLqhKeyReqSent == ZFALSE) {
+ jam();
+ regLogPartPtr->LogLqhKeyReqSent = ZTRUE;
+ signal->theData[0] = ZLOG_LQHKEYREQ;
+ signal->theData[1] = logPartPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+ } else {
+ if (regLogPartPtr->logPartState == LogPartRecord::ACTIVE) {
+ jam();
+ regLogPartPtr->logPartState = LogPartRecord::IDLE;
+ } else {
+ jam();
+ }//if
+ }//if
+ if (regLogPartPtr->waitWriteGciLog != LogPartRecord::WWGL_TRUE) {
+ jam();
+ return;
+ } else {
+ jam();
+/* --------------------------------------------------------------------------
+ * A COMPLETE GCI LOG RECORD IS WAITING TO BE WRITTEN. WE GIVE THIS HIGHEST
+ * PRIORITY AND WRITE IT IMMEDIATELY. AFTER WRITING IT WE CHECK IF ANY MORE
+ * LOG PARTS ARE WAITING. IF NOT WE SEND A SIGNAL THAT INITIALISES THE GCP
+ * RECORD TO WAIT UNTIL ALL COMPLETE GCI LOG RECORDS HAVE REACHED TO DISK.
+ * -------------------------------------------------------------------------- */
+ writeCompletedGciLog(signal);
+ logPartPtr.p->waitWriteGciLog = LogPartRecord::WWGL_FALSE;
+ tlnsStillWaiting = ZFALSE;
+ for (lnsLogPartPtr.i = 0; lnsLogPartPtr.i < 4; lnsLogPartPtr.i++) {
+ jam();
+ ptrAss(lnsLogPartPtr, logPartRecord);
+ if (lnsLogPartPtr.p->waitWriteGciLog == LogPartRecord::WWGL_TRUE) {
+ jam();
+ tlnsStillWaiting = ZTRUE;
+ }//if
+ }//for
+ if (tlnsStillWaiting == ZFALSE) {
+ jam();
+ signal->theData[0] = ZINIT_GCP_REC;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 1, JBB);
+ }//if
+ }//if
+}//Dblqh::logNextStart()
+
+/* --------------------------------------------------------------------------
+ * ------- MOVE PAGES FROM LFO RECORD TO PAGE REFERENCE RECORD -------
+ * WILL ALWAYS MOVE 8 PAGES TO A PAGE REFERENCE RECORD.
+ *
+ * SUBROUTINE SHORT NAME = MPR
+ * ------------------------------------------------------------------------- */
+void Dblqh::moveToPageRef(Signal* signal)
+{
+ LogPageRecordPtr mprLogPagePtr;
+ PageRefRecordPtr mprPageRefPtr;
+ UintR tmprIndex;
+
+/* --------------------------------------------------------------------------
+ * ------- INSERT PAGE REFERENCE RECORD -------
+ *
+ * INPUT: LFO_PTR LOG FILE OPERATION RECORD
+ * LOG_PART_PTR LOG PART RECORD
+ * PAGE_REF_PTR THE PAGE REFERENCE RECORD TO BE INSERTED.
+ * ------------------------------------------------------------------------- */
+ PageRefRecordPtr iprPageRefPtr;
+
+ if ((logPartPtr.p->mmBufferSize + 8) >= ZMAX_MM_BUFFER_SIZE) {
+ jam();
+ pageRefPtr.i = logPartPtr.p->firstPageRef;
+ ptrCheckGuard(pageRefPtr, cpageRefFileSize, pageRefRecord);
+ releasePrPages(signal);
+ removePageRef(signal);
+ } else {
+ jam();
+ logPartPtr.p->mmBufferSize = logPartPtr.p->mmBufferSize + 8;
+ }//if
+ seizePageRef(signal);
+ if (logPartPtr.p->firstPageRef == RNIL) {
+ jam();
+ logPartPtr.p->firstPageRef = pageRefPtr.i;
+ } else {
+ jam();
+ iprPageRefPtr.i = logPartPtr.p->lastPageRef;
+ ptrCheckGuard(iprPageRefPtr, cpageRefFileSize, pageRefRecord);
+ iprPageRefPtr.p->prNext = pageRefPtr.i;
+ }//if
+ pageRefPtr.p->prPrev = logPartPtr.p->lastPageRef;
+ logPartPtr.p->lastPageRef = pageRefPtr.i;
+
+ pageRefPtr.p->prFileNo = logFilePtr.p->fileNo;
+ pageRefPtr.p->prPageNo = lfoPtr.p->lfoPageNo;
+ tmprIndex = 0;
+ mprLogPagePtr.i = lfoPtr.p->firstLfoPage;
+MPR_LOOP:
+ arrGuard(tmprIndex, 8);
+ pageRefPtr.p->pageRef[tmprIndex] = mprLogPagePtr.i;
+ tmprIndex = tmprIndex + 1;
+ ptrCheckGuard(mprLogPagePtr, clogPageFileSize, logPageRecord);
+ mprLogPagePtr.i = mprLogPagePtr.p->logPageWord[ZNEXT_PAGE];
+ if (mprLogPagePtr.i != RNIL) {
+ jam();
+ goto MPR_LOOP;
+ }//if
+ mprPageRefPtr.i = pageRefPtr.p->prPrev;
+ if (mprPageRefPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(mprPageRefPtr, cpageRefFileSize, pageRefRecord);
+ mprLogPagePtr.i = mprPageRefPtr.p->pageRef[7];
+ ptrCheckGuard(mprLogPagePtr, clogPageFileSize, logPageRecord);
+ mprLogPagePtr.p->logPageWord[ZNEXT_PAGE] = pageRefPtr.p->pageRef[0];
+ }//if
+}//Dblqh::moveToPageRef()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ THE ATTRINFO FROM THE LOG ------- */
+/* */
+/* SUBROUTINE SHORT NAME = RA */
+/* ------------------------------------------------------------------------- */
+void Dblqh::readAttrinfo(Signal* signal)
+{
+ Uint32 remainingLen = tcConnectptr.p->totSendlenAi;
+ if (remainingLen == 0) {
+ jam();
+ tcConnectptr.p->reclenAiLqhkey = 0;
+ return;
+ }//if
+ Uint32 dataLen = remainingLen;
+ if (remainingLen > 5)
+ dataLen = 5;
+ readLogData(signal, dataLen, &tcConnectptr.p->firstAttrinfo[0]);
+ tcConnectptr.p->reclenAiLqhkey = dataLen;
+ remainingLen -= dataLen;
+ while (remainingLen > 0) {
+ jam();
+ dataLen = remainingLen;
+ if (remainingLen > 22)
+ dataLen = 22;
+ seizeAttrinbuf(signal);
+ readLogData(signal, dataLen, &attrinbufptr.p->attrbuf[0]);
+ attrinbufptr.p->attrbuf[ZINBUF_DATA_LEN] = dataLen;
+ remainingLen -= dataLen;
+ }//while
+}//Dblqh::readAttrinfo()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ COMMIT LOG ------- */
+/* */
+/* SUBROUTINE SHORT NAME = RCL */
+/* ------------------------------------------------------------------------- */
+void Dblqh::readCommitLog(Signal* signal, CommitLogRecord* commitLogRecord)
+{
+ Uint32 trclPageIndex = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ if ((trclPageIndex + (ZCOMMIT_LOG_SIZE - 1)) < ZPAGE_SIZE) {
+ jam();
+ tcConnectptr.p->tableref = logPagePtr.p->logPageWord[trclPageIndex + 0];
+ tcConnectptr.p->schemaVersion = logPagePtr.p->logPageWord[trclPageIndex + 1];
+ tcConnectptr.p->fragmentid = logPagePtr.p->logPageWord[trclPageIndex + 2];
+ commitLogRecord->fileNo = logPagePtr.p->logPageWord[trclPageIndex + 3];
+ commitLogRecord->startPageNo = logPagePtr.p->logPageWord[trclPageIndex + 4];
+ commitLogRecord->startPageIndex = logPagePtr.p->logPageWord[trclPageIndex + 5];
+ commitLogRecord->stopPageNo = logPagePtr.p->logPageWord[trclPageIndex + 6];
+ tcConnectptr.p->gci = logPagePtr.p->logPageWord[trclPageIndex + 7];
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] =
+ (trclPageIndex + ZCOMMIT_LOG_SIZE) - 1;
+ } else {
+ jam();
+ tcConnectptr.p->tableref = readLogword(signal);
+ tcConnectptr.p->schemaVersion = readLogword(signal);
+ tcConnectptr.p->fragmentid = readLogword(signal);
+ commitLogRecord->fileNo = readLogword(signal);
+ commitLogRecord->startPageNo = readLogword(signal);
+ commitLogRecord->startPageIndex = readLogword(signal);
+ commitLogRecord->stopPageNo = readLogword(signal);
+ tcConnectptr.p->gci = readLogword(signal);
+ }//if
+ tcConnectptr.p->transid[0] = logPartPtr.i + 65536;
+ tcConnectptr.p->transid[1] = (DBLQH << 20) + (cownNodeid << 8);
+}//Dblqh::readCommitLog()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ LOG PAGES FROM DISK IN ORDER TO EXECUTE A LOG ------- */
+/* RECORD WHICH WAS NOT FOUND IN MAIN MEMORY. */
+/* */
+/* SUBROUTINE SHORT NAME = REL */
+/* ------------------------------------------------------------------------- */
+void Dblqh::readExecLog(Signal* signal)
+{
+ UintR trelIndex;
+ UintR trelI;
+
+ seizeLfo(signal);
+ initLfo(signal);
+ trelI = logPartPtr.p->execSrStopPageNo - logPartPtr.p->execSrStartPageNo;
+ arrGuard(trelI + 1, 16);
+ lfoPtr.p->logPageArray[trelI + 1] = logPartPtr.p->execSrStartPageNo;
+ for (trelIndex = logPartPtr.p->execSrStopPageNo; (trelIndex >= logPartPtr.p->execSrStartPageNo) &&
+ (UintR)~trelIndex; trelIndex--) {
+ jam();
+ seizeLogpage(signal);
+ arrGuard(trelI, 16);
+ lfoPtr.p->logPageArray[trelI] = logPagePtr.i;
+ trelI--;
+ }//for
+ lfoPtr.p->lfoPageNo = logPartPtr.p->execSrStartPageNo;
+ lfoPtr.p->noPagesRw = (logPartPtr.p->execSrStopPageNo -
+ logPartPtr.p->execSrStartPageNo) + 1;
+ lfoPtr.p->firstLfoPage = lfoPtr.p->logPageArray[0];
+ signal->theData[0] = logFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lfoPtr.i;
+ signal->theData[3] = ZLIST_OF_MEM_PAGES; // edtjamo TR509 //ZLIST_OF_PAIRS;
+ signal->theData[4] = ZVAR_NO_LOG_PAGE_WORD;
+ signal->theData[5] = lfoPtr.p->noPagesRw;
+ signal->theData[6] = lfoPtr.p->logPageArray[0];
+ signal->theData[7] = lfoPtr.p->logPageArray[1];
+ signal->theData[8] = lfoPtr.p->logPageArray[2];
+ signal->theData[9] = lfoPtr.p->logPageArray[3];
+ signal->theData[10] = lfoPtr.p->logPageArray[4];
+ signal->theData[11] = lfoPtr.p->logPageArray[5];
+ signal->theData[12] = lfoPtr.p->logPageArray[6];
+ signal->theData[13] = lfoPtr.p->logPageArray[7];
+ signal->theData[14] = lfoPtr.p->logPageArray[8];
+ signal->theData[15] = lfoPtr.p->logPageArray[9];
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 16, JBA);
+}//Dblqh::readExecLog()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ 64 KBYTES WHEN EXECUTING THE FRAGMENT LOG ------- */
+/* */
+/* SUBROUTINE SHORT NAME = RES */
+/* ------------------------------------------------------------------------- */
+void Dblqh::readExecSrNewMbyte(Signal* signal)
+{
+ logFilePtr.p->currentFilepage = logFilePtr.p->currentMbyte * ZPAGES_IN_MBYTE;
+ logFilePtr.p->filePosition = logFilePtr.p->currentMbyte * ZPAGES_IN_MBYTE;
+ logPartPtr.p->execSrPagesRead = 0;
+ logPartPtr.p->execSrPagesReading = 0;
+ logPartPtr.p->execSrPagesExecuted = 0;
+ readExecSr(signal);
+ logPartPtr.p->logExecState = LogPartRecord::LES_WAIT_READ_EXEC_SR_NEW_MBYTE;
+}//Dblqh::readExecSrNewMbyte()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ 64 KBYTES WHEN EXECUTING THE FRAGMENT LOG ------- */
+/* */
+/* SUBROUTINE SHORT NAME = RES */
+/* ------------------------------------------------------------------------- */
+void Dblqh::readExecSr(Signal* signal)
+{
+ UintR tresPageid;
+ UintR tresIndex;
+
+ tresPageid = logFilePtr.p->filePosition;
+ seizeLfo(signal);
+ initLfo(signal);
+ for (tresIndex = 7; (UintR)~tresIndex; tresIndex--) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* GO BACKWARDS SINCE WE INSERT AT THE BEGINNING AND WE WANT THAT FIRST PAGE */
+/* SHALL BE FIRST AND LAST PAGE LAST. */
+/* ------------------------------------------------------------------------- */
+ seizeLogpage(signal);
+ lfoPtr.p->logPageArray[tresIndex] = logPagePtr.i;
+ }//for
+ lfoPtr.p->lfoState = LogFileOperationRecord::READ_EXEC_SR;
+ lfoPtr.p->lfoPageNo = tresPageid;
+ logFilePtr.p->filePosition = logFilePtr.p->filePosition + 8;
+ logPartPtr.p->execSrPagesReading = logPartPtr.p->execSrPagesReading + 8;
+ lfoPtr.p->noPagesRw = 8;
+ lfoPtr.p->firstLfoPage = lfoPtr.p->logPageArray[0];
+ signal->theData[0] = logFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lfoPtr.i;
+ signal->theData[3] = ZLIST_OF_MEM_PAGES;
+ signal->theData[4] = ZVAR_NO_LOG_PAGE_WORD;
+ signal->theData[5] = 8;
+ signal->theData[6] = lfoPtr.p->logPageArray[0];
+ signal->theData[7] = lfoPtr.p->logPageArray[1];
+ signal->theData[8] = lfoPtr.p->logPageArray[2];
+ signal->theData[9] = lfoPtr.p->logPageArray[3];
+ signal->theData[10] = lfoPtr.p->logPageArray[4];
+ signal->theData[11] = lfoPtr.p->logPageArray[5];
+ signal->theData[12] = lfoPtr.p->logPageArray[6];
+ signal->theData[13] = lfoPtr.p->logPageArray[7];
+ signal->theData[14] = tresPageid;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 15, JBA);
+}//Dblqh::readExecSr()
+
+/* ------------------------------------------------------------------------- */
+/* ------------ READ THE PRIMARY KEY FROM THE LOG ---------------- */
+/* */
+/* SUBROUTINE SHORT NAME = RK */
+/* --------------------------------------------------------------------------*/
+void Dblqh::readKey(Signal* signal)
+{
+ Uint32 remainingLen = tcConnectptr.p->primKeyLen;
+ ndbrequire(remainingLen != 0);
+ Uint32 dataLen = remainingLen;
+ if (remainingLen > 4)
+ dataLen = 4;
+ readLogData(signal, dataLen, &tcConnectptr.p->tupkeyData[0]);
+ remainingLen -= dataLen;
+ while (remainingLen > 0) {
+ jam();
+ seizeTupkeybuf(signal);
+ dataLen = remainingLen;
+ if (dataLen > 4)
+ dataLen = 4;
+ readLogData(signal, dataLen, &databufptr.p->data[0]);
+ remainingLen -= dataLen;
+ }//while
+}//Dblqh::readKey()
+
+/* ------------------------------------------------------------------------- */
+/* ------------ READ A NUMBER OF WORDS FROM LOG INTO CDATA ---------------- */
+/* */
+/* SUBROUTINE SHORT NAME = RLD */
+/* --------------------------------------------------------------------------*/
+void Dblqh::readLogData(Signal* signal, Uint32 noOfWords, Uint32* dataPtr)
+{
+ ndbrequire(noOfWords < 32);
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ if ((logPos + noOfWords) >= ZPAGE_SIZE) {
+ for (Uint32 i = 0; i < noOfWords; i++)
+ dataPtr[i] = readLogwordExec(signal);
+ } else {
+ MEMCOPY_NO_WORDS(dataPtr, &logPagePtr.p->logPageWord[logPos], noOfWords);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos + noOfWords;
+ }//if
+}//Dblqh::readLogData()
+
+/* ------------------------------------------------------------------------- */
+/* ------------ READ THE LOG HEADER OF A PREPARE LOG HEADER ---------------- */
+/* */
+/* SUBROUTINE SHORT NAME = RLH */
+/* --------------------------------------------------------------------------*/
+void Dblqh::readLogHeader(Signal* signal)
+{
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ if ((logPos + ZLOG_HEAD_SIZE) < ZPAGE_SIZE) {
+ jam();
+ tcConnectptr.p->hashValue = logPagePtr.p->logPageWord[logPos + 2];
+ tcConnectptr.p->operation = logPagePtr.p->logPageWord[logPos + 3];
+ tcConnectptr.p->totSendlenAi = logPagePtr.p->logPageWord[logPos + 4];
+ tcConnectptr.p->primKeyLen = logPagePtr.p->logPageWord[logPos + 5];
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos + ZLOG_HEAD_SIZE;
+ } else {
+ jam();
+ readLogwordExec(signal); /* IGNORE PREPARE LOG RECORD TYPE */
+ readLogwordExec(signal); /* IGNORE LOG RECORD SIZE */
+ tcConnectptr.p->hashValue = readLogwordExec(signal);
+ tcConnectptr.p->operation = readLogwordExec(signal);
+ tcConnectptr.p->totSendlenAi = readLogwordExec(signal);
+ tcConnectptr.p->primKeyLen = readLogwordExec(signal);
+ }//if
+}//Dblqh::readLogHeader()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ A WORD FROM THE LOG ------- */
+/* */
+/* OUTPUT: TLOG_WORD */
+/* SUBROUTINE SHORT NAME = RLW */
+/* ------------------------------------------------------------------------- */
+Uint32 Dblqh::readLogword(Signal* signal)
+{
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ ndbrequire(logPos < ZPAGE_SIZE);
+ Uint32 logWord = logPagePtr.p->logPageWord[logPos];
+ logPos++;
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos;
+ if (logPos >= ZPAGE_SIZE) {
+ jam();
+ logPagePtr.i = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = ZPAGE_HEADER_SIZE;
+ logFilePtr.p->currentLogpage = logPagePtr.i;
+ logFilePtr.p->currentFilepage++;
+ logPartPtr.p->execSrPagesRead--;
+ logPartPtr.p->execSrPagesExecuted++;
+ }//if
+ return logWord;
+}//Dblqh::readLogword()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ A WORD FROM THE LOG WHEN EXECUTING A LOG RECORD ------- */
+/* */
+/* OUTPUT: TLOG_WORD */
+/* SUBROUTINE SHORT NAME = RWE */
+/* ------------------------------------------------------------------------- */
+Uint32 Dblqh::readLogwordExec(Signal* signal)
+{
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ ndbrequire(logPos < ZPAGE_SIZE);
+ Uint32 logWord = logPagePtr.p->logPageWord[logPos];
+ logPos++;
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos;
+ if (logPos >= ZPAGE_SIZE) {
+ jam();
+ logPagePtr.i = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ if (logPagePtr.i != RNIL){
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = ZPAGE_HEADER_SIZE;
+ } else {
+ // Reading word at the last pos in the last page
+ // Don't step forward to next page!
+ jam();
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX]++;
+ }
+ }//if
+ return logWord;
+}//Dblqh::readLogwordExec()
+
+/* ------------------------------------------------------------------------- */
+/* ------- READ A SINGLE PAGE FROM THE LOG ------- */
+/* */
+/* INPUT: TRSP_PAGE_NO */
+/* SUBROUTINE SHORT NAME = RSP */
+/* ------------------------------------------------------------------------- */
+void Dblqh::readSinglePage(Signal* signal, Uint32 pageNo)
+{
+ seizeLfo(signal);
+ initLfo(signal);
+ seizeLogpage(signal);
+ lfoPtr.p->firstLfoPage = logPagePtr.i;
+ lfoPtr.p->lfoPageNo = pageNo;
+ lfoPtr.p->noPagesRw = 1;
+ signal->theData[0] = logFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lfoPtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS;
+ signal->theData[4] = ZVAR_NO_LOG_PAGE_WORD;
+ signal->theData[5] = 1;
+ signal->theData[6] = logPagePtr.i;
+ signal->theData[7] = pageNo;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+}//Dblqh::readSinglePage()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE OPERATION FROM ACTIVE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME = RAC
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseAccList(Signal* signal)
+{
+ TcConnectionrecPtr racTcNextConnectptr;
+ TcConnectionrecPtr racTcPrevConnectptr;
+
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ racTcPrevConnectptr.i = tcConnectptr.p->prevTc;
+ racTcNextConnectptr.i = tcConnectptr.p->nextTc;
+ if (tcConnectptr.p->listState != TcConnectionrec::ACC_BLOCK_LIST) {
+ jam();
+ systemError(signal);
+ }//if
+ tcConnectptr.p->listState = TcConnectionrec::NOT_IN_LIST;
+ if (racTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(racTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ racTcNextConnectptr.p->prevTc = racTcPrevConnectptr.i;
+ }//if
+ if (racTcPrevConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(racTcPrevConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ racTcPrevConnectptr.p->nextTc = tcConnectptr.p->nextTc;
+ } else {
+ jam();
+ /* ---------------------------------------------------------------------
+ * OPERATION RECORD IS FIRST IN ACTIVE LIST
+ * THIS MEANS THAT THERE EXISTS NO PREVIOUS TC THAT NEEDS TO BE UPDATED.
+ * --------------------------------------------------------------------- */
+ fragptr.p->accBlockedList = racTcNextConnectptr.i;
+ }//if
+}//Dblqh::releaseAccList()
+
+/* --------------------------------------------------------------------------
+ * ------- REMOVE COPY FRAGMENT FROM ACTIVE COPY LIST -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseActiveCopy(Signal* signal)
+{
+ /* MUST BE 8 BIT */
+ UintR tracFlag;
+ UintR tracIndex;
+
+ tracFlag = ZFALSE;
+ for (tracIndex = 0; tracIndex < 4; tracIndex++) {
+ if (tracFlag == ZFALSE) {
+ jam();
+ if (cactiveCopy[tracIndex] == fragptr.i) {
+ jam();
+ tracFlag = ZTRUE;
+ }//if
+ } else {
+ if (tracIndex < 3) {
+ jam();
+ cactiveCopy[tracIndex - 1] = cactiveCopy[tracIndex];
+ } else {
+ jam();
+ cactiveCopy[3] = RNIL;
+ }//if
+ }//if
+ }//for
+ ndbrequire(tracFlag == ZTRUE);
+ cnoActiveCopy--;
+}//Dblqh::releaseActiveCopy()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE OPERATION FROM ACTIVE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME = RAL
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseActiveList(Signal* signal)
+{
+ TcConnectionrecPtr ralTcNextConnectptr;
+ TcConnectionrecPtr ralTcPrevConnectptr;
+ ralTcPrevConnectptr.i = tcConnectptr.p->prevTc;
+ ralTcNextConnectptr.i = tcConnectptr.p->nextTc;
+ ndbrequire(tcConnectptr.p->listState == TcConnectionrec::IN_ACTIVE_LIST);
+ tcConnectptr.p->listState = TcConnectionrec::NOT_IN_LIST;
+ if (ralTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(ralTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ ralTcNextConnectptr.p->prevTc = ralTcPrevConnectptr.i;
+ }//if
+ if (ralTcPrevConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(ralTcPrevConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ ralTcPrevConnectptr.p->nextTc = tcConnectptr.p->nextTc;
+ } else {
+ jam();
+ /* ----------------------------------------------------------------------
+ * OPERATION RECORD IS FIRST IN ACTIVE LIST
+ * THIS MEANS THAT THERE EXISTS NO PREVIOUS TC THAT NEEDS TO BE UPDATED.
+ * --------------------------------------------------------------------- */
+ fragptr.p->activeList = ralTcNextConnectptr.i;
+ }//if
+}//Dblqh::releaseActiveList()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE ADD FRAGMENT RECORD -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseAddfragrec(Signal* signal)
+{
+ addfragptr.p->addfragStatus = AddFragRecord::FREE;
+ addfragptr.p->nextAddfragrec = cfirstfreeAddfragrec;
+ cfirstfreeAddfragrec = addfragptr.i;
+}//Dblqh::releaseAddfragrec()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE FRAGMENT RECORD -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseFragrec()
+{
+ fragptr.p->fragStatus = Fragrecord::FREE;
+ fragptr.p->nextFrag = cfirstfreeFragrec;
+ cfirstfreeFragrec = fragptr.i;
+}//Dblqh::releaseFragrec()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE LCP LOCAL RECORD -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseLcpLoc(Signal* signal)
+{
+ lcpLocptr.p->lcpLocstate = LcpLocRecord::IDLE;
+ lcpLocptr.p->nextLcpLoc = cfirstfreeLcpLoc;
+ cfirstfreeLcpLoc = lcpLocptr.i;
+}//Dblqh::releaseLcpLoc()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE A PAGE REFERENCE RECORD. -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releasePageRef(Signal* signal)
+{
+ pageRefPtr.p->prNext = cfirstfreePageRef;
+ cfirstfreePageRef = pageRefPtr.i;
+}//Dblqh::releasePageRef()
+
+/* --------------------------------------------------------------------------
+ * --- RELEASE ALL PAGES IN THE MM BUFFER AFTER EXECUTING THE LOG ON IT. ----
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseMmPages(Signal* signal)
+{
+RMP_LOOP:
+ jam();
+ pageRefPtr.i = logPartPtr.p->firstPageRef;
+ if (pageRefPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(pageRefPtr, cpageRefFileSize, pageRefRecord);
+ releasePrPages(signal);
+ removePageRef(signal);
+ goto RMP_LOOP;
+ }//if
+}//Dblqh::releaseMmPages()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE A SET OF PAGES AFTER EXECUTING THE LOG ON IT. -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::releasePrPages(Signal* signal)
+{
+ UintR trppIndex;
+
+ for (trppIndex = 0; trppIndex <= 7; trppIndex++) {
+ jam();
+ logPagePtr.i = pageRefPtr.p->pageRef[trppIndex];
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ releaseLogpage(signal);
+ }//for
+}//Dblqh::releasePrPages()
+
+/* --------------------------------------------------------------------------
+ * ------- RELEASE OPERATION FROM WAIT QUEUE LIST ON FRAGMENT -------
+ *
+ * SUBROUTINE SHORT NAME : RWA
+ * ------------------------------------------------------------------------- */
+void Dblqh::releaseWaitQueue(Signal* signal)
+{
+ TcConnectionrecPtr rwaTcNextConnectptr;
+ TcConnectionrecPtr rwaTcPrevConnectptr;
+
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ rwaTcPrevConnectptr.i = tcConnectptr.p->prevTc;
+ rwaTcNextConnectptr.i = tcConnectptr.p->nextTc;
+ if (tcConnectptr.p->listState != TcConnectionrec::WAIT_QUEUE_LIST) {
+ jam();
+ systemError(signal);
+ }//if
+ tcConnectptr.p->listState = TcConnectionrec::NOT_IN_LIST;
+ if (rwaTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(rwaTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ rwaTcNextConnectptr.p->prevTc = rwaTcPrevConnectptr.i;
+ } else {
+ jam();
+ fragptr.p->lastWaitQueue = rwaTcPrevConnectptr.i;
+ }//if
+ if (rwaTcPrevConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(rwaTcPrevConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ rwaTcPrevConnectptr.p->nextTc = rwaTcNextConnectptr.i;
+ } else {
+ jam();
+ fragptr.p->firstWaitQueue = rwaTcNextConnectptr.i;
+ }//if
+}//Dblqh::releaseWaitQueue()
+
+/* --------------------------------------------------------------------------
+ * ------- REMOVE OPERATION RECORD FROM LIST ON LOG PART OF NOT -------
+ * COMPLETED OPERATIONS IN THE LOG.
+ *
+ * SUBROUTINE SHORT NAME = RLO
+ * ------------------------------------------------------------------------- */
+void Dblqh::removeLogTcrec(Signal* signal)
+{
+ TcConnectionrecPtr rloTcNextConnectptr;
+ TcConnectionrecPtr rloTcPrevConnectptr;
+ rloTcPrevConnectptr.i = tcConnectptr.p->prevLogTcrec;
+ rloTcNextConnectptr.i = tcConnectptr.p->nextLogTcrec;
+ if (rloTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(rloTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ rloTcNextConnectptr.p->prevLogTcrec = rloTcPrevConnectptr.i;
+ } else {
+ jam();
+ logPartPtr.p->lastLogTcrec = rloTcPrevConnectptr.i;
+ }//if
+ if (rloTcPrevConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(rloTcPrevConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ rloTcPrevConnectptr.p->nextLogTcrec = rloTcNextConnectptr.i;
+ } else {
+ jam();
+ logPartPtr.p->firstLogTcrec = rloTcNextConnectptr.i;
+ }//if
+}//Dblqh::removeLogTcrec()
+
+/* --------------------------------------------------------------------------
+ * ------- REMOVE PAGE REFERENCE RECORD FROM LIST IN THIS LOG PART -------
+ *
+ * SUBROUTINE SHORT NAME = RPR
+ * ------------------------------------------------------------------------- */
+void Dblqh::removePageRef(Signal* signal)
+{
+ PageRefRecordPtr rprPageRefPtr;
+
+ pageRefPtr.i = logPartPtr.p->firstPageRef;
+ if (pageRefPtr.i != RNIL) {
+ jam();
+ ptrCheckGuard(pageRefPtr, cpageRefFileSize, pageRefRecord);
+ if (pageRefPtr.p->prNext == RNIL) {
+ jam();
+ logPartPtr.p->lastPageRef = RNIL;
+ logPartPtr.p->firstPageRef = RNIL;
+ } else {
+ jam();
+ logPartPtr.p->firstPageRef = pageRefPtr.p->prNext;
+ rprPageRefPtr.i = pageRefPtr.p->prNext;
+ ptrCheckGuard(rprPageRefPtr, cpageRefFileSize, pageRefRecord);
+ rprPageRefPtr.p->prPrev = RNIL;
+ }//if
+ releasePageRef(signal);
+ }//if
+}//Dblqh::removePageRef()
+
+/* ------------------------------------------------------------------------- */
+/* ------- RETURN FROM EXECUTION OF LOG ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+Uint32 Dblqh::returnExecLog(Signal* signal)
+{
+ tcConnectptr.p->connectState = TcConnectionrec::CONNECTED;
+ initLogPointers(signal);
+ logPartPtr.p->execSrExecuteIndex++;
+ Uint32 result = checkIfExecLog(signal);
+ if (result == ZOK) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* THIS LOG RECORD WILL BE EXECUTED AGAIN TOWARDS ANOTHER NODE. */
+/* ------------------------------------------------------------------------- */
+ logPagePtr.i = logPartPtr.p->execSrLogPage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] =
+ logPartPtr.p->execSrLogPageIndex;
+ } else {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* NO MORE EXECUTION OF THIS LOG RECORD. */
+/* ------------------------------------------------------------------------- */
+ if (logPartPtr.p->logExecState ==
+ LogPartRecord::LES_EXEC_LOGREC_FROM_FILE) {
+ jam();
+/* ------------------------------------------------------------------------- */
+/* THE LOG RECORD WAS READ FROM DISK. RELEASE ITS PAGES IMMEDIATELY. */
+/* ------------------------------------------------------------------------- */
+ lfoPtr.i = logPartPtr.p->execSrLfoRec;
+ ptrCheckGuard(lfoPtr, clfoFileSize, logFileOperationRecord);
+ releaseLfoPages(signal);
+ releaseLfo(signal);
+ logPartPtr.p->logExecState = LogPartRecord::LES_EXEC_LOG;
+ if (logPartPtr.p->execSrExecLogFile != logPartPtr.p->currentLogfile) {
+ jam();
+ LogFileRecordPtr clfLogFilePtr;
+ clfLogFilePtr.i = logPartPtr.p->execSrExecLogFile;
+ ptrCheckGuard(clfLogFilePtr, clogFileFileSize, logFileRecord);
+ clfLogFilePtr.p->logFileStatus = LogFileRecord::CLOSING_EXEC_LOG;
+ closeFile(signal, clfLogFilePtr);
+ result = ZCLOSE_FILE;
+ }//if
+ }//if
+ logPartPtr.p->execSrExecuteIndex = 0;
+ logPartPtr.p->execSrLogPage = RNIL;
+ logPartPtr.p->execSrLogPageIndex = ZNIL;
+ logPagePtr.i = logFilePtr.p->currentLogpage;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPartPtr.p->savePageIndex;
+ }//if
+ return result;
+}//Dblqh::returnExecLog()
+
+/* --------------------------------------------------------------------------
+ * ------- SEIZE ADD FRAGMENT RECORD ------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::seizeAddfragrec(Signal* signal)
+{
+ addfragptr.i = cfirstfreeAddfragrec;
+ ptrCheckGuard(addfragptr, caddfragrecFileSize, addFragRecord);
+ cfirstfreeAddfragrec = addfragptr.p->nextAddfragrec;
+}//Dblqh::seizeAddfragrec()
+
+/* --------------------------------------------------------------------------
+ * ------- SEIZE FRAGMENT RECORD -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::seizeFragmentrec(Signal* signal)
+{
+ fragptr.i = cfirstfreeFragrec;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
+ cfirstfreeFragrec = fragptr.p->nextFrag;
+ fragptr.p->nextFrag = RNIL;
+}//Dblqh::seizeFragmentrec()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEIZE A PAGE REFERENCE RECORD. ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dblqh::seizePageRef(Signal* signal)
+{
+ pageRefPtr.i = cfirstfreePageRef;
+ ptrCheckGuard(pageRefPtr, cpageRefFileSize, pageRefRecord);
+ cfirstfreePageRef = pageRefPtr.p->prNext;
+ pageRefPtr.p->prNext = RNIL;
+}//Dblqh::seizePageRef()
+
+/* --------------------------------------------------------------------------
+ * ------- SEND ABORTED -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::sendAborted(Signal* signal)
+{
+ UintR TlastInd;
+ if (tcConnectptr.p->nextReplica == ZNIL) {
+ TlastInd = ZTRUE;
+ } else {
+ TlastInd = ZFALSE;
+ }//if
+ signal->theData[0] = tcConnectptr.p->tcOprec;
+ signal->theData[1] = tcConnectptr.p->transid[0];
+ signal->theData[2] = tcConnectptr.p->transid[1];
+ signal->theData[3] = cownNodeid;
+ signal->theData[4] = TlastInd;
+ sendSignal(tcConnectptr.p->tcBlockref, GSN_ABORTED, signal, 5, JBB);
+ return;
+}//Dblqh::sendAborted()
+
+/* --------------------------------------------------------------------------
+ * ------- SEND LQH_TRANSCONF -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::sendLqhTransconf(Signal* signal, LqhTransConf::OperationStatus stat)
+{
+ tcNodeFailptr.i = tcConnectptr.p->tcNodeFailrec;
+ ptrCheckGuard(tcNodeFailptr, ctcNodeFailrecFileSize, tcNodeFailRecord);
+
+ Uint32 reqInfo = 0;
+ LqhTransConf::setReplicaType(reqInfo, tcConnectptr.p->replicaType);
+ LqhTransConf::setReplicaNo(reqInfo, tcConnectptr.p->seqNoReplica);
+ LqhTransConf::setLastReplicaNo(reqInfo, tcConnectptr.p->lastReplicaNo);
+ LqhTransConf::setSimpleFlag(reqInfo, tcConnectptr.p->opSimple);
+ LqhTransConf::setDirtyFlag(reqInfo, tcConnectptr.p->dirtyOp);
+ LqhTransConf::setOperation(reqInfo, tcConnectptr.p->operation);
+
+ LqhTransConf * const lqhTransConf = (LqhTransConf *)&signal->theData[0];
+ lqhTransConf->tcRef = tcNodeFailptr.p->newTcRef;
+ lqhTransConf->lqhNodeId = cownNodeid;
+ lqhTransConf->operationStatus = stat;
+ lqhTransConf->lqhConnectPtr = tcConnectptr.i;
+ lqhTransConf->transId1 = tcConnectptr.p->transid[0];
+ lqhTransConf->transId2 = tcConnectptr.p->transid[1];
+ lqhTransConf->oldTcOpRec = tcConnectptr.p->tcOprec;
+ lqhTransConf->requestInfo = reqInfo;
+ lqhTransConf->gci = tcConnectptr.p->gci;
+ lqhTransConf->nextNodeId1 = tcConnectptr.p->nextReplica;
+ lqhTransConf->nextNodeId2 = tcConnectptr.p->nodeAfterNext[0];
+ lqhTransConf->nextNodeId3 = tcConnectptr.p->nodeAfterNext[1];
+ lqhTransConf->apiRef = tcConnectptr.p->applRef;
+ lqhTransConf->apiOpRec = tcConnectptr.p->applOprec;
+ lqhTransConf->tableId = tcConnectptr.p->tableref;
+ sendSignal(tcNodeFailptr.p->newTcBlockref, GSN_LQH_TRANSCONF,
+ signal, LqhTransConf::SignalLength, JBB);
+ tcNodeFailptr.p->tcRecNow = tcConnectptr.i + 1;
+ signal->theData[0] = ZLQH_TRANS_NEXT;
+ signal->theData[1] = tcNodeFailptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+}//Dblqh::sendLqhTransconf()
+
+/* --------------------------------------------------------------------------
+ * ------- START ANOTHER PHASE OF LOG EXECUTION -------
+ * RESET THE VARIABLES NEEDED BY THIS PROCESS AND SEND THE START SIGNAL
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::startExecSr(Signal* signal)
+{
+ cnoFragmentsExecSr = 0;
+ signal->theData[0] = cfirstCompletedFragSr;
+ signal->theData[1] = RNIL;
+ sendSignal(cownref, GSN_START_EXEC_SR, signal, 2, JBB);
+}//Dblqh::startExecSr()
+
+/* ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
+ * ¤¤¤¤¤¤¤ LOG MODULE ¤¤¤¤¤¤¤
+ * ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ */
+/* --------------------------------------------------------------------------
+ * ------- STEP FORWARD IN FRAGMENT LOG DURING LOG EXECUTION -------
+ *
+ * ------------------------------------------------------------------------- */
+void Dblqh::stepAhead(Signal* signal, Uint32 stepAheadWords)
+{
+ UintR tsaPos;
+
+ tsaPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ while ((stepAheadWords + tsaPos) >= ZPAGE_SIZE) {
+ jam();
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = ZPAGE_SIZE;
+ stepAheadWords = stepAheadWords - (ZPAGE_SIZE - tsaPos);
+ logFilePtr.p->currentLogpage = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ logPagePtr.i = logPagePtr.p->logPageWord[ZNEXT_PAGE];
+ logFilePtr.p->currentFilepage++;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = ZPAGE_HEADER_SIZE;
+ logPartPtr.p->execSrPagesRead--;
+ logPartPtr.p->execSrPagesExecuted++;
+ tsaPos = ZPAGE_HEADER_SIZE;
+ }//while
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = stepAheadWords + tsaPos;
+}//Dblqh::stepAhead()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE A ABORT LOG RECORD -------
+ *
+ * SUBROUTINE SHORT NAME: WAL
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeAbortLog(Signal* signal)
+{
+ if ((ZABORT_LOG_SIZE + ZNEXT_LOG_SIZE) >
+ logFilePtr.p->remainingWordsInMbyte) {
+ jam();
+ changeMbyte(signal);
+ }//if
+ logFilePtr.p->remainingWordsInMbyte =
+ logFilePtr.p->remainingWordsInMbyte - ZABORT_LOG_SIZE;
+ writeLogWord(signal, ZABORT_TYPE);
+ writeLogWord(signal, tcConnectptr.p->transid[0]);
+ writeLogWord(signal, tcConnectptr.p->transid[1]);
+}//Dblqh::writeAbortLog()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE A COMMIT LOG RECORD -------
+ *
+ * SUBROUTINE SHORT NAME: WCL
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeCommitLog(Signal* signal, LogPartRecordPtr regLogPartPtr)
+{
+ LogFileRecordPtr regLogFilePtr;
+ LogPageRecordPtr regLogPagePtr;
+ TcConnectionrec * const regTcPtr = tcConnectptr.p;
+ regLogFilePtr.i = regLogPartPtr.p->currentLogfile;
+ ptrCheckGuard(regLogFilePtr, clogFileFileSize, logFileRecord);
+ regLogPagePtr.i = regLogFilePtr.p->currentLogpage;
+ Uint32 twclTmp = regLogFilePtr.p->remainingWordsInMbyte;
+ ptrCheckGuard(regLogPagePtr, clogPageFileSize, logPageRecord);
+ logPartPtr = regLogPartPtr;
+ logFilePtr = regLogFilePtr;
+ logPagePtr = regLogPagePtr;
+ if ((ZCOMMIT_LOG_SIZE + ZNEXT_LOG_SIZE) > twclTmp) {
+ jam();
+ changeMbyte(signal);
+ twclTmp = logFilePtr.p->remainingWordsInMbyte;
+ }//if
+
+ Uint32 twclLogPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ Uint32 tableId = regTcPtr->tableref;
+ Uint32 schemaVersion = regTcPtr->schemaVersion;
+ Uint32 fragId = regTcPtr->fragmentid;
+ Uint32 fileNo = regTcPtr->logStartFileNo;
+ Uint32 startPageNo = regTcPtr->logStartPageNo;
+ Uint32 pageIndex = regTcPtr->logStartPageIndex;
+ Uint32 stopPageNo = regTcPtr->logStopPageNo;
+ Uint32 gci = regTcPtr->gci;
+ logFilePtr.p->remainingWordsInMbyte = twclTmp - ZCOMMIT_LOG_SIZE;
+
+ if ((twclLogPos + ZCOMMIT_LOG_SIZE) >= ZPAGE_SIZE) {
+ writeLogWord(signal, ZCOMMIT_TYPE);
+ writeLogWord(signal, tableId);
+ writeLogWord(signal, schemaVersion);
+ writeLogWord(signal, fragId);
+ writeLogWord(signal, fileNo);
+ writeLogWord(signal, startPageNo);
+ writeLogWord(signal, pageIndex);
+ writeLogWord(signal, stopPageNo);
+ writeLogWord(signal, gci);
+ } else {
+ Uint32* dataPtr = &logPagePtr.p->logPageWord[twclLogPos];
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = twclLogPos + ZCOMMIT_LOG_SIZE;
+ dataPtr[0] = ZCOMMIT_TYPE;
+ dataPtr[1] = tableId;
+ dataPtr[2] = schemaVersion;
+ dataPtr[3] = fragId;
+ dataPtr[4] = fileNo;
+ dataPtr[5] = startPageNo;
+ dataPtr[6] = pageIndex;
+ dataPtr[7] = stopPageNo;
+ dataPtr[8] = gci;
+ }//if
+ TcConnectionrecPtr rloTcNextConnectptr;
+ TcConnectionrecPtr rloTcPrevConnectptr;
+ rloTcPrevConnectptr.i = regTcPtr->prevLogTcrec;
+ rloTcNextConnectptr.i = regTcPtr->nextLogTcrec;
+ if (rloTcNextConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(rloTcNextConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ rloTcNextConnectptr.p->prevLogTcrec = rloTcPrevConnectptr.i;
+ } else {
+ regLogPartPtr.p->lastLogTcrec = rloTcPrevConnectptr.i;
+ }//if
+ if (rloTcPrevConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(rloTcPrevConnectptr, ctcConnectrecFileSize, tcConnectionrec);
+ rloTcPrevConnectptr.p->nextLogTcrec = rloTcNextConnectptr.i;
+ } else {
+ regLogPartPtr.p->firstLogTcrec = rloTcNextConnectptr.i;
+ }//if
+}//Dblqh::writeCommitLog()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE A COMPLETED GCI LOG RECORD -------
+ *
+ * SUBROUTINE SHORT NAME: WCG
+// Input Pointers:
+// logFilePtr
+// logPartPtr
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeCompletedGciLog(Signal* signal)
+{
+ if ((ZCOMPLETED_GCI_LOG_SIZE + ZNEXT_LOG_SIZE) >
+ logFilePtr.p->remainingWordsInMbyte) {
+ jam();
+ changeMbyte(signal);
+ }//if
+ logFilePtr.p->remainingWordsInMbyte =
+ logFilePtr.p->remainingWordsInMbyte - ZCOMPLETED_GCI_LOG_SIZE;
+ writeLogWord(signal, ZCOMPLETED_GCI_TYPE);
+ writeLogWord(signal, cnewestCompletedGci);
+ logPartPtr.p->logPartNewestCompletedGCI = cnewestCompletedGci;
+}//Dblqh::writeCompletedGciLog()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE A DIRTY PAGE DURING LOG EXECUTION -------
+ *
+ * SUBROUTINE SHORT NAME: WD
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeDirty(Signal* signal)
+{
+ logPagePtr.p->logPageWord[ZPOS_DIRTY] = ZNOT_DIRTY;
+
+ // Calculate checksum for page
+ logPagePtr.p->logPageWord[ZPOS_CHECKSUM] = calcPageCheckSum(logPagePtr);
+
+ seizeLfo(signal);
+ initLfo(signal);
+ lfoPtr.p->lfoPageNo = logPartPtr.p->prevFilepage;
+ lfoPtr.p->noPagesRw = 1;
+ lfoPtr.p->lfoState = LogFileOperationRecord::WRITE_DIRTY;
+ lfoPtr.p->firstLfoPage = logPagePtr.i;
+ signal->theData[0] = logFilePtr.p->fileRef;
+ signal->theData[1] = cownref;
+ signal->theData[2] = lfoPtr.i;
+ signal->theData[3] = ZLIST_OF_PAIRS_SYNCH;
+ signal->theData[4] = ZVAR_NO_LOG_PAGE_WORD;
+ signal->theData[5] = 1;
+ signal->theData[6] = logPagePtr.i;
+ signal->theData[7] = logPartPtr.p->prevFilepage;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+}//Dblqh::writeDirty()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE A WORD INTO THE LOG, CHECK FOR NEW PAGE -------
+ *
+ * SUBROUTINE SHORT NAME: WLW
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeLogWord(Signal* signal, Uint32 data)
+{
+ Uint32 logPos = logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX];
+ ndbrequire(logPos < ZPAGE_SIZE);
+ logPagePtr.p->logPageWord[logPos] = data;
+ logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] = logPos + 1;
+ if ((logPos + 1) == ZPAGE_SIZE) {
+ jam();
+ completedLogPage(signal, ZNORMAL);
+ seizeLogpage(signal);
+ initLogpage(signal);
+ logFilePtr.p->currentLogpage = logPagePtr.i;
+ logFilePtr.p->currentFilepage++;
+ }//if
+}//Dblqh::writeLogWord()
+
+/* --------------------------------------------------------------------------
+ * ------- WRITE A NEXT LOG RECORD AND CHANGE TO NEXT MBYTE -------
+ *
+ * SUBROUTINE SHORT NAME: WNL
+// Input Pointers:
+// logFilePtr(Redefines)
+// logPagePtr (Redefines)
+// logPartPtr
+ * ------------------------------------------------------------------------- */
+void Dblqh::writeNextLog(Signal* signal)
+{
+ LogFileRecordPtr wnlNextLogFilePtr;
+ UintR twnlNextFileNo;
+ UintR twnlNewMbyte;
+ UintR twnlRemWords;
+ UintR twnlNextMbyte;
+
+/* -------------------------------------------------- */
+/* CALCULATE THE NEW NUMBER OF REMAINING WORDS */
+/* AS 128*2036 WHERE 128 * 8 KBYTE = 1 MBYTE */
+/* AND 2036 IS THE NUMBER OF WORDS IN A PAGE */
+/* THAT IS USED FOR LOG INFORMATION. */
+/* -------------------------------------------------- */
+ twnlRemWords = ZPAGE_SIZE - ZPAGE_HEADER_SIZE;
+ twnlRemWords = twnlRemWords * ZPAGES_IN_MBYTE;
+ wnlNextLogFilePtr.i = logFilePtr.p->nextLogFile;
+ ptrCheckGuard(wnlNextLogFilePtr, clogFileFileSize, logFileRecord);
+/* -------------------------------------------------- */
+/* WRITE THE NEXT LOG RECORD. */
+/* -------------------------------------------------- */
+ ndbrequire(logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX] < ZPAGE_SIZE);
+ logPagePtr.p->logPageWord[logPagePtr.p->logPageWord[ZCURR_PAGE_INDEX]] =
+ ZNEXT_MBYTE_TYPE;
+ if (logFilePtr.p->currentMbyte == (ZNO_MBYTES_IN_FILE - 1)) {
+ jam();
+/* -------------------------------------------------- */
+/* CALCULATE THE NEW REMAINING WORDS WHEN */
+/* CHANGING LOG FILE IS PERFORMED */
+/* -------------------------------------------------- */
+ twnlRemWords = twnlRemWords - (ZPAGE_SIZE - ZPAGE_HEADER_SIZE);
+/* -------------------------------------------------- */
+/* ENSURE THAT THE LOG PAGES ARE WRITTEN AFTER */
+/* WE HAVE CHANGED MBYTE. */
+/* -------------------------------------------------- */
+/* ENSURE LAST PAGE IN PREVIOUS MBYTE IS */
+/* WRITTEN AND THAT THE STATE OF THE WRITE IS */
+/* PROPERLY SET. */
+/* -------------------------------------------------- */
+/* WE HAVE TO CHANGE LOG FILE */
+/* -------------------------------------------------- */
+ completedLogPage(signal, ZLAST_WRITE_IN_FILE);
+ if (wnlNextLogFilePtr.p->fileNo == 0) {
+ jam();
+/* -------------------------------------------------- */
+/* WE HAVE FINALISED A LOG LAP, START FROM LOG */
+/* FILE 0 AGAIN */
+/* -------------------------------------------------- */
+ logPartPtr.p->logLap++;
+ }//if
+ logPartPtr.p->currentLogfile = wnlNextLogFilePtr.i;
+ logFilePtr.i = wnlNextLogFilePtr.i;
+ logFilePtr.p = wnlNextLogFilePtr.p;
+ twnlNewMbyte = 0;
+ } else {
+ jam();
+/* -------------------------------------------------- */
+/* INCREMENT THE CURRENT MBYTE */
+/* SET PAGE INDEX TO PAGE HEADER SIZE */
+/* -------------------------------------------------- */
+ completedLogPage(signal, ZENFORCE_WRITE);
+ twnlNewMbyte = logFilePtr.p->currentMbyte + 1;
+ }//if
+/* -------------------------------------------------- */
+/* CHANGE TO NEW LOG FILE IF NECESSARY */
+/* UPDATE THE FILE POSITION TO THE NEW MBYTE */
+/* FOUND IN PAGE PART OF TNEXT_LOG_PTR */
+/* ALLOCATE AND INITIATE A NEW PAGE SINCE WE */
+/* HAVE SENT THE PREVIOUS PAGE TO DISK. */
+/* SET THE NEW NUMBER OF REMAINING WORDS IN THE */
+/* NEW MBYTE ALLOCATED. */
+/* -------------------------------------------------- */
+ logFilePtr.p->currentMbyte = twnlNewMbyte;
+ logFilePtr.p->filePosition = twnlNewMbyte * ZPAGES_IN_MBYTE;
+ logFilePtr.p->currentFilepage = twnlNewMbyte * ZPAGES_IN_MBYTE;
+ logFilePtr.p->remainingWordsInMbyte = twnlRemWords;
+ seizeLogpage(signal);
+ if (logFilePtr.p->currentMbyte == 0) {
+ jam();
+ logFilePtr.p->lastPageWritten = 0;
+ if (logFilePtr.p->fileNo == 0) {
+ jam();
+ releaseLogpage(signal);
+ logPagePtr.i = logFilePtr.p->logPageZero;
+ ptrCheckGuard(logPagePtr, clogPageFileSize, logPageRecord);
+ }//if
+ }//if
+ initLogpage(signal);
+ logFilePtr.p->currentLogpage = logPagePtr.i;
+ if (logFilePtr.p->currentMbyte == 0) {
+ jam();
+/* -------------------------------------------------- */
+/* THIS IS A NEW FILE, WRITE THE FILE DESCRIPTOR*/
+/* ALSO OPEN THE NEXT LOG FILE TO ENSURE THAT */
+/* THIS FILE IS OPEN WHEN ITS TURN COMES. */
+/* -------------------------------------------------- */
+ writeFileHeaderOpen(signal, ZNORMAL);
+ openNextLogfile(signal);
+ logFilePtr.p->fileChangeState = LogFileRecord::BOTH_WRITES_ONGOING;
+ }//if
+ if (logFilePtr.p->fileNo == logPartPtr.p->logTailFileNo) {
+ if (logFilePtr.p->currentMbyte == logPartPtr.p->logTailMbyte) {
+ jam();
+/* -------------------------------------------------- */
+/* THE HEAD AND TAIL HAS MET. THIS SHOULD NEVER */
+/* OCCUR. CAN HAPPEN IF THE LOCAL CHECKPOINTS */
+/* TAKE FAR TOO LONG TIME. SO TIMING PROBLEMS */
+/* CAN INVOKE THIS SYSTEM CRASH. HOWEVER ONLY */
+/* VERY SERIOUS TIMING PROBLEMS. */
+/* -------------------------------------------------- */
+ systemError(signal);
+ }//if
+ }//if
+ if (logFilePtr.p->currentMbyte == (ZNO_MBYTES_IN_FILE - 1)) {
+ jam();
+ twnlNextMbyte = 0;
+ if (logFilePtr.p->fileChangeState != LogFileRecord::NOT_ONGOING) {
+ jam();
+ logPartPtr.p->logPartState = LogPartRecord::FILE_CHANGE_PROBLEM;
+ }//if
+ twnlNextFileNo = wnlNextLogFilePtr.p->fileNo;
+ } else {
+ jam();
+ twnlNextMbyte = logFilePtr.p->currentMbyte + 1;
+ twnlNextFileNo = logFilePtr.p->fileNo;
+ }//if
+ if (twnlNextFileNo == logPartPtr.p->logTailFileNo) {
+ if (logPartPtr.p->logTailMbyte == twnlNextMbyte) {
+ jam();
+/* -------------------------------------------------- */
+/* THE NEXT MBYTE WILL BE THE TAIL. WE MUST */
+/* STOP LOGGING NEW OPERATIONS. THIS OPERATION */
+/* ALLOWED TO PASS. ALSO COMMIT, NEXT, COMPLETED*/
+/* GCI, ABORT AND FRAGMENT SPLIT IS ALLOWED. */
+/* OPERATIONS ARE ALLOWED AGAIN WHEN THE TAIL */
+/* IS MOVED FORWARD AS A RESULT OF A START_LCP */
+/* _ROUND SIGNAL ARRIVING FROM DBDIH. */
+/* -------------------------------------------------- */
+ logPartPtr.p->logPartState = LogPartRecord::TAIL_PROBLEM;
+ }//if
+ }//if
+}//Dblqh::writeNextLog()
+
+void
+Dblqh::execDUMP_STATE_ORD(Signal* signal)
+{
+ DumpStateOrd * const dumpState = (DumpStateOrd *)&signal->theData[0];
+ if(dumpState->args[0] == DumpStateOrd::CommitAckMarkersSize){
+ infoEvent("LQH: m_commitAckMarkerPool: %d free size: %d",
+ m_commitAckMarkerPool.getNoOfFree(),
+ m_commitAckMarkerPool.getSize());
+ }
+ if(dumpState->args[0] == DumpStateOrd::CommitAckMarkersDump){
+ infoEvent("LQH: m_commitAckMarkerPool: %d free size: %d",
+ m_commitAckMarkerPool.getNoOfFree(),
+ m_commitAckMarkerPool.getSize());
+
+ CommitAckMarkerIterator iter;
+ for(m_commitAckMarkerHash.first(iter); iter.curr.i != RNIL;
+ m_commitAckMarkerHash.next(iter)){
+ infoEvent("CommitAckMarker: i = %d (0x%x, 0x%x)"
+ " ApiRef: 0x%x apiOprec: 0x%x TcNodeId: %d",
+ iter.curr.i,
+ iter.curr.p->transid1,
+ iter.curr.p->transid2,
+ iter.curr.p->apiRef,
+ iter.curr.p->apiOprec,
+ iter.curr.p->tcNodeId);
+ }
+ }
+
+ // Dump info about number of log pages
+ if(dumpState->args[0] == DumpStateOrd::LqhDumpNoLogPages){
+ infoEvent("LQH: Log pages : %d Free: %d",
+ clogPageFileSize,
+ cnoOfLogPages);
+ }
+
+ // Dump all defined tables that LQH knowns about
+ if(dumpState->args[0] == DumpStateOrd::LqhDumpAllDefinedTabs){
+ for(Uint32 i = 0; i<ctabrecFileSize; i++){
+ TablerecPtr tabPtr;
+ tabPtr.i = i;
+ ptrAss(tabPtr, tablerec);
+ if(tabPtr.p->tableStatus != Tablerec::NOT_DEFINED){
+ infoEvent("Table %d Status: %d Usage: %d",
+ i, tabPtr.p->tableStatus, tabPtr.p->usageCount);
+ }
+ }
+ return;
+ }
+
+ // Dump all ScanRecords
+ if (dumpState->args[0] == DumpStateOrd::LqhDumpAllScanRec){
+ Uint32 recordNo = 0;
+ if (signal->length() == 1)
+ infoEvent("LQH: Dump all ScanRecords - size: %d",
+ cscanrecFileSize);
+ else if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ dumpState->args[0] = DumpStateOrd::LqhDumpOneScanRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+
+ if (recordNo < cscanrecFileSize-1){
+ dumpState->args[0] = DumpStateOrd::LqhDumpAllScanRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ return;
+ }
+
+ // Dump all active ScanRecords
+ if (dumpState->args[0] == DumpStateOrd::LqhDumpAllActiveScanRec){
+ Uint32 recordNo = 0;
+ if (signal->length() == 1)
+ infoEvent("LQH: Dump active ScanRecord - size: %d",
+ cscanrecFileSize);
+ else if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ ScanRecordPtr sp;
+ sp.i = recordNo;
+ c_scanRecordPool.getPtr(scanptr);
+ if (sp.p->scanState != ScanRecord::SCAN_FREE){
+ dumpState->args[0] = DumpStateOrd::LqhDumpOneScanRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+ }
+
+ if (recordNo < cscanrecFileSize-1){
+ dumpState->args[0] = DumpStateOrd::LqhDumpAllActiveScanRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ return;
+ }
+
+ if(dumpState->args[0] == DumpStateOrd::LqhDumpOneScanRec){
+ Uint32 recordNo = RNIL;
+ if (signal->length() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ if (recordNo >= cscanrecFileSize)
+ return;
+
+ ScanRecordPtr sp;
+ sp.i = recordNo;
+ c_scanRecordPool.getPtr(sp);
+ infoEvent("Dblqh::ScanRecord[%d]: state=%d, type=%d, "
+ "complStatus=%d, scanNodeId=%d",
+ sp.i,
+ sp.p->scanState,
+ sp.p->scanType,
+ sp.p->scanCompletedStatus,
+ sp.p->scanNodeId);
+ infoEvent(" apiBref=0x%x, scanAccPtr=%d",
+ sp.p->scanApiBlockref,
+ sp.p->scanAccPtr);
+ infoEvent(" copyptr=%d, ailen=%d, complOps=%d, concurrOps=%d",
+ sp.p->copyPtr,
+ sp.p->scanAiLength,
+ sp.p->m_curr_batch_size_rows,
+ sp.p->m_max_batch_size_rows);
+ infoEvent(" errCnt=%d, localFid=%d, schV=%d",
+ sp.p->scanErrorCounter,
+ sp.p->scanLocalFragid,
+ sp.p->scanSchemaVersion);
+ infoEvent(" stpid=%d, flag=%d, lhold=%d, lmode=%d, num=%d",
+ sp.p->scanStoredProcId,
+ sp.p->scanFlag,
+ sp.p->scanLockHold,
+ sp.p->scanLockMode,
+ sp.p->scanNumber);
+ infoEvent(" relCount=%d, TCwait=%d, TCRec=%d, KIflag=%d",
+ sp.p->scanReleaseCounter,
+ sp.p->scanTcWaiting,
+ sp.p->scanTcrec,
+ sp.p->scanKeyinfoFlag);
+ return;
+ }
+ if(dumpState->args[0] == DumpStateOrd::LqhDumpLcpState){
+
+ infoEvent("== LQH LCP STATE ==");
+ infoEvent(" clcpCompletedState=%d, c_lcpId=%d, cnoOfFragsCheckpointed=%d",
+ clcpCompletedState,
+ c_lcpId,
+ cnoOfFragsCheckpointed);
+
+ LcpRecordPtr TlcpPtr;
+ // Print information about the current local checkpoint
+ TlcpPtr.i = 0;
+ ptrAss(TlcpPtr, lcpRecord);
+ infoEvent(" lcpState=%d firstLcpLocTup=%d firstLcpLocAcc=%d",
+ TlcpPtr.p->lcpState,
+ TlcpPtr.p->firstLcpLocTup,
+ TlcpPtr.p->firstLcpLocAcc);
+ infoEvent(" lcpAccptr=%d lastFragmentFlag=%d",
+ TlcpPtr.p->lcpAccptr,
+ TlcpPtr.p->lastFragmentFlag);
+ infoEvent("currentFragment.fragPtrI=%d",
+ TlcpPtr.p->currentFragment.fragPtrI);
+ infoEvent("currentFragment.lcpFragOrd.tableId=%d",
+ TlcpPtr.p->currentFragment.lcpFragOrd.tableId);
+ infoEvent(" lcpQueued=%d reportEmpty=%d",
+ TlcpPtr.p->lcpQueued,
+ TlcpPtr.p->reportEmpty);
+ char buf[8*_NDB_NODE_BITMASK_SIZE+1];
+ infoEvent(" m_EMPTY_LCP_REQ=%d",
+ TlcpPtr.p->m_EMPTY_LCP_REQ.getText(buf));
+
+ return;
+ }
+
+
+
+}//Dblqh::execDUMP_STATE_ORD()
+
+void Dblqh::execSET_VAR_REQ(Signal* signal)
+{
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+
+ switch (var) {
+
+ case NoOfConcurrentCheckpointsAfterRestart:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case NoOfConcurrentCheckpointsDuringRestart:
+ // Valid only during start so value not set.
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ } // switch
+#endif
+}//execSET_VAR_REQ()
+
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ---------------------- TRIGGER HANDLING ------------------------ */
+/* ---------------------------------------------------------------- */
+/* */
+/* All trigger signals from TRIX are forwarded top TUP */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+// Trigger signals
+void
+Dblqh::execCREATE_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ NodeId myNodeId = getOwnNodeId();
+ BlockReference tupref = calcTupBlockRef(myNodeId);
+
+ sendSignal(tupref, GSN_CREATE_TRIG_REQ, signal, CreateTrigReq::SignalLength, JBB);
+}
+
+void
+Dblqh::execCREATE_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+ NodeId myNodeId = getOwnNodeId();
+ BlockReference dictref = calcDictBlockRef(myNodeId);
+
+ sendSignal(dictref, GSN_CREATE_TRIG_CONF, signal, CreateTrigConf::SignalLength, JBB);
+}
+
+void
+Dblqh::execCREATE_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+ NodeId myNodeId = getOwnNodeId();
+ BlockReference dictref = calcDictBlockRef(myNodeId);
+
+ sendSignal(dictref, GSN_CREATE_TRIG_REF, signal, CreateTrigRef::SignalLength, JBB);
+}
+
+void
+Dblqh::execDROP_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ NodeId myNodeId = getOwnNodeId();
+ BlockReference tupref = calcTupBlockRef(myNodeId);
+
+ sendSignal(tupref, GSN_DROP_TRIG_REQ, signal, DropTrigReq::SignalLength, JBB);
+}
+
+void
+Dblqh::execDROP_TRIG_CONF(Signal* signal)
+{
+ jamEntry();
+ NodeId myNodeId = getOwnNodeId();
+ BlockReference dictref = calcDictBlockRef(myNodeId);
+
+ sendSignal(dictref, GSN_DROP_TRIG_CONF, signal, DropTrigConf::SignalLength, JBB);
+}
+
+void
+Dblqh::execDROP_TRIG_REF(Signal* signal)
+{
+ jamEntry();
+ NodeId myNodeId = getOwnNodeId();
+ BlockReference dictref = calcDictBlockRef(myNodeId);
+
+ sendSignal(dictref, GSN_DROP_TRIG_REF, signal, DropTrigRef::SignalLength, JBB);
+}
+
+Uint32 Dblqh::calcPageCheckSum(LogPageRecordPtr logP){
+ Uint32 checkSum = 37;
+#ifdef VM_TRACE
+ for (Uint32 i = (ZPOS_CHECKSUM+1); i<ZPAGE_SIZE; i++)
+ checkSum = logP.p->logPageWord[i] ^ checkSum;
+#endif
+ return checkSum;
+ }
+
diff --git a/storage/ndb/src/kernel/blocks/dblqh/Makefile.am b/storage/ndb/src/kernel/blocks/dblqh/Makefile.am
new file mode 100644
index 00000000000..854860b269c
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/Makefile.am
@@ -0,0 +1,25 @@
+#SUBDIRS = redoLogReader
+
+noinst_LIBRARIES = libdblqh.a
+
+libdblqh_a_SOURCES = DblqhInit.cpp DblqhMain.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdblqh.dsp
+
+libdblqh.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdblqh_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/Makefile b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/Makefile
new file mode 100644
index 00000000000..a89b648de77
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+BIN_TARGET := redoLogFileReader
+
+SOURCES := records.cpp redoLogFileReader.cpp
+
+TYPE := util
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp
new file mode 100644
index 00000000000..092b7840c20
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp
@@ -0,0 +1,312 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "records.hpp"
+
+void printOut(const char *string, Uint32 value) {
+ ndbout_c("%-30s%-12u%-12x", string, value, value);
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool AbortTransactionRecord::check() {
+ // Not implemented yet.
+ return true;
+}
+
+Uint32 AbortTransactionRecord::getLogRecordSize() {
+ return ABORTTRANSACTIONRECORDSIZE;
+}
+
+NdbOut& operator<<(NdbOut& no, const AbortTransactionRecord& atr) {
+ no << "----------ABORT TRANSACTION RECORD-------------" << endl << endl;
+ printOut("Record type:", atr.m_recordType);
+ printOut("TransactionId1:", atr.m_transactionId1);
+ printOut("TransactionId2:", atr.m_transactionId2);
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool NextMbyteRecord::check() {
+ // Not implemented yet.
+ return true;
+}
+
+Uint32 NextMbyteRecord::getLogRecordSize() {
+ return NEXTMBYTERECORDSIZE;
+}
+
+NdbOut& operator<<(NdbOut& no, const NextMbyteRecord& nmr) {
+ no << "----------NEXT MBYTE RECORD--------------------" << endl << endl;
+ printOut("Record type:", nmr.m_recordType);
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool CommitTransactionRecord::check() {
+ // Not implemented yet.
+ return true;
+}
+
+Uint32 CommitTransactionRecord::getLogRecordSize() {
+ return COMMITTRANSACTIONRECORDSIZE;
+}
+
+NdbOut& operator<<(NdbOut& no, const CommitTransactionRecord& ctr) {
+ no << "----------COMMIT TRANSACTION RECORD------------" << endl << endl;
+ printOut("Record type:", ctr.m_recordType);
+ printOut("TableId", ctr.m_tableId);
+ printOut("FfragmentId", ctr.m_fragmentId);
+ printOut("File no. of Prep. Op.", ctr.m_fileNumberOfPrepareOperation);
+ printOut("Start page no. of Prep. Op.", ctr.m_startPageNumberOfPrepareOperation);
+ printOut("Start page index of Prep. Op.", ctr.m_startPageIndexOfPrepareOperation);
+ printOut("Stop page no. of Prep. Op.", ctr.m_stopPageNumberOfPrepareOperation);
+ printOut("GlobalCheckpoint", ctr.m_globalCheckpoint);
+
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool InvalidCommitTransactionRecord::check() {
+ // Not implemented yet.
+ return true;
+}
+
+Uint32 InvalidCommitTransactionRecord::getLogRecordSize() {
+ return COMMITTRANSACTIONRECORDSIZE;
+}
+
+NdbOut& operator<<(NdbOut& no, const InvalidCommitTransactionRecord& ictr) {
+ no << "------INVALID COMMIT TRANSACTION RECORD--------" << endl << endl;
+ printOut("Record type:", ictr.m_recordType);
+ printOut("TableId", ictr.m_tableId);
+ printOut("FfragmentId", ictr.m_fragmentId);
+ printOut("File no. of Prep. Op.", ictr.m_fileNumberOfPrepareOperation);
+ printOut("Start page no. of Prep. Op.", ictr.m_startPageNumberOfPrepareOperation);
+ printOut("Start page index of Prep. Op.", ictr.m_startPageIndexOfPrepareOperation);
+ printOut("Stop page no. of Prep. Op.", ictr.m_stopPageNumberOfPrepareOperation);
+ printOut("GlobalCheckpoint", ictr.m_globalCheckpoint);
+
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool PrepareOperationRecord::check() {
+ // Not fully implemented.
+ if (m_operationType == 3 && m_attributeLength != 0)
+ return false;
+
+ if (m_logRecordSize != (m_attributeLength + m_keyLength + 7))
+ return false;
+
+ return true;
+}
+
+Uint32 PrepareOperationRecord::getLogRecordSize() {
+ return m_logRecordSize;
+}
+
+NdbOut& operator<<(NdbOut& no, const PrepareOperationRecord& por) {
+ no << "-----------PREPARE OPERATION RECORD------------" << endl << endl;
+ printOut("Record type:", por.m_recordType);
+ printOut("logRecordSize:", por.m_logRecordSize);
+ printOut("hashValue:", por.m_hashValue);
+ printOut("schemaVersion:", por.m_schemaVersion);
+ switch (por.m_operationType) {
+ case 0:
+ ndbout_c("%-30s%-12u%-6s", "operationType:",
+ por.m_operationType, "read");
+ break;
+ case 1:
+ ndbout_c("%-30s%-12u%-6s", "operationType:",
+ por.m_operationType, "update");
+ break;
+ case 2:
+ ndbout_c("%-30s%-12u%-6s", "operationType:",
+ por.m_operationType, "insert");
+ break;
+ case 3:
+ ndbout_c("%-30s%-12u%-6s", "operationType:",
+ por.m_operationType, "delete");
+ break;
+ default:
+ printOut("operationType:", por.m_operationType);
+ }
+ printOut("attributeLength:", por.m_attributeLength);
+ printOut("keyLength:", por.m_keyLength);
+
+#if 1
+ // Print keydata
+ Uint32* p = (Uint32*)&por.m_keyInfo;
+ for(Uint32 i=0; i < por.m_keyLength; i++){
+ printOut("keydata:", *p);
+ p++;
+ }
+
+ // Print attrdata
+ for(Uint32 i=0; i < por.m_attributeLength; i++){
+ printOut("attrdata:", *p);
+ p++;
+ }
+#endif
+
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool CompletedGCIRecord::check() {
+ // Not implemented yet.
+ return true;
+}
+
+Uint32 CompletedGCIRecord::getLogRecordSize() {
+ return COMPLETEDGCIRECORDSIZE;
+}
+
+NdbOut& operator<<(NdbOut& no, const CompletedGCIRecord& cGCIr) {
+ no << "-----------COMPLETED GCI RECORD----------------" << endl << endl;
+ printOut("Record type:", cGCIr.m_recordType);
+ printOut("Completed GCI:", cGCIr.m_theCompletedGCI);
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+bool NextLogRecord::check() {
+ // Not implemented yet.
+ return true;
+}
+
+Uint32 NextLogRecord::getLogRecordSize(Uint32 pageIndex) {
+ return PAGESIZE - pageIndex;
+}
+
+NdbOut& operator<<(NdbOut& no, const NextLogRecord& nl) {
+ no << "-----------NEXT LOG RECORD --------------------" << endl << endl;
+ printOut("Record type:", nl.m_recordType);
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+Uint32 PageHeader::getLogRecordSize() {
+ return PAGEHEADERSIZE;
+}
+
+bool PageHeader::check() {
+ // Not implemented yet.
+ return true;
+}
+
+NdbOut& operator<<(NdbOut& no, const PageHeader& ph) {
+ no << "------------PAGE HEADER------------------------" << endl << endl;
+ ndbout_c("%-30s%-12s%-12s\n", "", "Decimal", "Hex");
+ printOut("Checksum:", ph.m_checksum);
+ printOut("Laps since initial start:", ph.m_lap);
+ printOut("Max gci completed:", ph.m_max_gci_completed);
+ printOut("Max gci started:", ph.m_max_gci_started);
+ printOut("Ptr to next page:", ph.m_next_page);
+ printOut("Ptr to previous page:", ph.m_previous_page);
+ printOut("Ndb version:", ph.m_ndb_version);
+ printOut("Number of log files:", ph.m_number_of_logfiles);
+ printOut("Current page index:", ph.m_current_page_index);
+ printOut("Oldest prepare op. file No.:", ph.m_old_prepare_file_number);
+ printOut("Oldest prepare op. page ref.:", ph.m_old_prepare_page_reference);
+ printOut("Dirty flag:", ph.m_dirty_flag);
+ no << endl;
+ return no;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+Uint32 FileDescriptor::getLogRecordSize() {
+ return FILEDESCRIPTORHEADERSIZE
+ + m_fdHeader.m_noOfDescriptors * FILEDESCRIPTORRECORDSIZE;
+}
+
+NdbOut& operator<<(NdbOut& no, const FileDescriptor& fd) {
+ no << "-------FILE DESCRIPTOR HEADER------------------" << endl << endl;
+ printOut("Record type:", fd.m_fdHeader.m_recordType);
+ printOut("Number of file descriptors:", fd.m_fdHeader.m_noOfDescriptors);
+ printOut("File number:", fd.m_fdHeader.m_fileNo);
+ ndbout << endl;
+ for(Uint32 i = 0; i < fd.m_fdHeader.m_noOfDescriptors; i++) {
+ fd.printARecord(i);
+ }
+ return no;
+}
+
+void FileDescriptor::printARecord( Uint32 recordIndex ) const {
+ ndbout << "------------------FILE DESCRIPTOR " << recordIndex
+ <<" ---------------------" << endl << endl;
+ ndbout_c("%-30s%-12s%-12s\n", "", "Decimal", "Hex");
+
+ for(int i = 1; i <= NO_MBYTE_IN_FILE; i++) {
+ ndbout_c("%s%2d%s%-12u%-12x", "Max GCI completed, mbyte ", i, ": ",
+ m_fdRecord[recordIndex].m_maxGciCompleted[i-1],
+ m_fdRecord[recordIndex].m_maxGciCompleted[i-1]);
+ }
+ for(int i = 1; i <= NO_MBYTE_IN_FILE; i++) {
+ ndbout_c("%s%2d%s%-12u%-12x", "Max GCI started, mbyte ", i, ": ",
+ m_fdRecord[recordIndex].m_maxGciStarted[i-1],
+ m_fdRecord[recordIndex].m_maxGciStarted[i-1]);
+ }
+ for(int i = 1; i <= NO_MBYTE_IN_FILE; i++) {
+ ndbout_c("%s%2d%s%-12u%-12x", "Last prepared ref, mbyte ", i, ": ",
+ m_fdRecord[recordIndex].m_lastPreparedReference[i-1],
+ m_fdRecord[recordIndex].m_lastPreparedReference[i-1]);
+ }
+ ndbout << endl;
+}
+
+bool FileDescriptor::check() {
+ // Not implemented yet.
+ return true;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
diff --git a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp
new file mode 100644
index 00000000000..e73986e4d73
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp
@@ -0,0 +1,235 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <ndb_types.h>
+
+#define ZNEW_PREP_OP_TYPE 0
+#define ZPREP_OP_TYPE 1
+#define ZCOMMIT_TYPE 2
+#define ZABORT_TYPE 3
+#define ZFD_TYPE 4
+#define ZFRAG_SPLIT_TYPE 5
+#define ZNEXT_LOG_RECORD_TYPE 6
+#define ZNEXT_MBYTE_TYPE 7
+#define ZCOMPLETED_GCI_TYPE 8
+#define ZINVALID_COMMIT_TYPE 9
+
+#define MAX_FILE_DESCRIPTORS 40
+#define NO_MBYTE_IN_FILE 16
+
+#define PAGESIZE 8192
+#define NO_PAGES_IN_MBYTE 32
+#define NO_MBYTE_IN_FILE 16
+
+#define COMMITTRANSACTIONRECORDSIZE 8
+#define COMPLETEDGCIRECORDSIZE 2
+#define PAGEHEADERSIZE 32
+#define FILEDESCRIPTORHEADERSIZE 3
+#define FILEDESCRIPTORRECORDSIZE 48
+#define NEXTMBYTERECORDSIZE 1
+#define ABORTTRANSACTIONRECORDSIZE 3
+
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class AbortTransactionRecord {
+ friend NdbOut& operator<<(NdbOut&, const AbortTransactionRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ Uint32 m_recordType;
+ Uint32 m_transactionId1;
+ Uint32 m_transactionId2;
+};
+
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class NextMbyteRecord {
+ friend NdbOut& operator<<(NdbOut&, const NextMbyteRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ Uint32 m_recordType;
+};
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+
+class PrepareOperationRecord {
+ friend NdbOut& operator<<(NdbOut&, const PrepareOperationRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+
+protected:
+ Uint32 m_recordType;
+ Uint32 m_logRecordSize;
+ Uint32 m_hashValue;
+ Uint32 m_schemaVersion;
+ Uint32 m_operationType; // 0 READ, 1 UPDATE, 2 INSERT, 3 DELETE
+ Uint32 m_attributeLength;
+ Uint32 m_keyLength;
+ Uint32 *m_keyInfo; // In this order
+ Uint32 *m_attrInfo;// In this order
+};
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class CompletedGCIRecord {
+ friend NdbOut& operator<<(NdbOut&, const CompletedGCIRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ Uint32 m_recordType;
+ Uint32 m_theCompletedGCI;
+};
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class NextLogRecord {
+ friend NdbOut& operator<<(NdbOut&, const NextLogRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize(Uint32);
+protected:
+ Uint32 m_recordType;
+};
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class PageHeader {
+ friend NdbOut& operator<<(NdbOut&, const PageHeader&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ Uint32 m_checksum;
+ Uint32 m_lap;
+ Uint32 m_max_gci_completed;
+ Uint32 m_max_gci_started;
+ Uint32 m_next_page;
+ Uint32 m_previous_page;
+ Uint32 m_ndb_version;
+ Uint32 m_number_of_logfiles;
+ Uint32 m_current_page_index;
+ Uint32 m_old_prepare_file_number;
+ Uint32 m_old_prepare_page_reference;
+ Uint32 m_dirty_flag;
+};
+
+//----------------------------------------------------------------
+// File descriptor.
+//----------------------------------------------------------------
+
+class FileDescriptorHeader {
+public:
+ Uint32 m_recordType;
+ Uint32 m_noOfDescriptors;
+ Uint32 m_fileNo;
+};
+
+class FileDescriptorRecord {
+public:
+ Uint32 m_maxGciCompleted[16];
+ Uint32 m_maxGciStarted[16];
+ Uint32 m_lastPreparedReference[16];
+};
+
+class FileDescriptor {
+ friend NdbOut& operator<<(NdbOut&, const FileDescriptor&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ void printARecord( Uint32 ) const;
+ FileDescriptorHeader m_fdHeader;
+ FileDescriptorRecord m_fdRecord[1];
+};
+
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class CommitTransactionRecord {
+ friend NdbOut& operator<<(NdbOut&, const CommitTransactionRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ Uint32 m_recordType;
+ Uint32 m_tableId;
+ Uint32 m_fragmentId;
+ Uint32 m_fileNumberOfPrepareOperation;
+ Uint32 m_startPageNumberOfPrepareOperation;
+ Uint32 m_startPageIndexOfPrepareOperation;
+ Uint32 m_stopPageNumberOfPrepareOperation;
+ Uint32 m_globalCheckpoint;
+};
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+class InvalidCommitTransactionRecord {
+ friend NdbOut& operator<<(NdbOut&, const InvalidCommitTransactionRecord&);
+public:
+ bool check();
+ Uint32 getLogRecordSize();
+protected:
+ Uint32 m_recordType;
+ Uint32 m_tableId;
+ Uint32 m_fragmentId;
+ Uint32 m_fileNumberOfPrepareOperation;
+ Uint32 m_startPageNumberOfPrepareOperation;
+ Uint32 m_startPageIndexOfPrepareOperation;
+ Uint32 m_stopPageNumberOfPrepareOperation;
+ Uint32 m_globalCheckpoint;
+};
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+struct NextLogRec {
+
+};
+
+struct NewPrepareOperation {
+
+};
+
+struct FragmentSplit {
+
+};
diff --git a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp
new file mode 100644
index 00000000000..540df7b507e
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp
@@ -0,0 +1,464 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//----------------------------------------------------------------
+// REDOLOGFILEREADER
+// Reads a redo log file and checks it for errors and/or prints
+// the file in a human readable format.
+//
+// Usage: redoLogFileReader <file> [-noprint] [-nocheck]
+// [-mbyte <0-15>] [-mbyteHeaders] [-pageHeaders]
+//
+//----------------------------------------------------------------
+
+
+#include <ndb_global.h>
+
+#include "records.hpp"
+
+#define RETURN_ERROR 1
+#define RETURN_OK 0
+
+#define FROM_BEGINNING 0
+
+void usage(const char * prg);
+Uint32 readRecordOverPageBoundary (Uint32 *, Uint32 , Uint32 , Uint32);
+Uint32 readFromFile(FILE * f, Uint32 *toPtr, Uint32 sizeInWords);
+void readArguments(int argc, const char** argv);
+void doExit();
+
+FILE * f;
+char fileName[256];
+bool thePrintFlag = true;
+bool theCheckFlag = true;
+bool onlyPageHeaders = false;
+bool onlyMbyteHeaders = false;
+bool onlyFileDesc = false;
+bool firstLap = true;
+Uint32 startAtMbyte = 0;
+Uint32 startAtPage = 0;
+Uint32 startAtPageIndex = 0;
+Uint32 *redoLogPage;
+
+NDB_COMMAND(redoLogFileReader, "redoLogFileReader", "redoLogFileReader", "Read a redo log file", 16384) {
+ Uint32 pageIndex = 0;
+ Uint32 oldPageIndex = 0;
+ Uint32 recordType = 1234567890;
+
+ PageHeader *thePageHeader;
+ CompletedGCIRecord *cGCIrecord;
+ PrepareOperationRecord *poRecord;
+ NextLogRecord *nlRecord;
+ FileDescriptor *fdRecord;
+ CommitTransactionRecord *ctRecord;
+ InvalidCommitTransactionRecord *ictRecord;
+ NextMbyteRecord *nmRecord;
+ AbortTransactionRecord *atRecord;
+
+ readArguments(argc, argv);
+
+ f = fopen(fileName, "rb");
+ if(!f){
+ perror("Error: open file");
+ exit(RETURN_ERROR);
+ }
+
+ Uint32 tmpFileOffset = startAtMbyte * PAGESIZE * NO_PAGES_IN_MBYTE * sizeof(Uint32);
+ if (fseek(f, tmpFileOffset, FROM_BEGINNING)) {
+ perror("Error: Move in file");
+ exit(RETURN_ERROR);
+ }
+
+ redoLogPage = new Uint32[PAGESIZE*NO_PAGES_IN_MBYTE];
+
+ // Loop for every mbyte.
+ for (Uint32 j = startAtMbyte; j < NO_MBYTE_IN_FILE; j++) {
+ readFromFile(f, redoLogPage, PAGESIZE*NO_PAGES_IN_MBYTE);
+
+ if (firstLap) {
+ pageIndex = startAtPageIndex;
+ firstLap = false;
+ } else
+ pageIndex = 0;
+
+ // Loop for every page.
+ for (int i = startAtPage; i < NO_PAGES_IN_MBYTE; i++) {
+
+ if (pageIndex == 0) {
+ thePageHeader = (PageHeader *) &redoLogPage[i*PAGESIZE];
+ // Print out mbyte number, page number and page index.
+ ndbout << j << ":" << i << ":" << pageIndex << endl
+ << " " << j*32 + i << ":" << pageIndex << " ";
+ if (thePrintFlag) ndbout << (*thePageHeader);
+ if (theCheckFlag) {
+ if(!thePageHeader->check()) {
+ doExit();
+ }
+
+ Uint32 checkSum = 37;
+ for (int ps = 1; ps < PAGESIZE; ps++)
+ checkSum = redoLogPage[i*PAGESIZE+ps] ^ checkSum;
+
+ if (checkSum != redoLogPage[i*PAGESIZE]){
+ ndbout << "WRONG CHECKSUM: checksum = " << redoLogPage[i*PAGESIZE]
+ << " expected = " << checkSum << endl;
+ doExit();
+ }
+ else
+ ndbout << "expected checksum: " << checkSum << endl;
+
+ }
+ pageIndex += thePageHeader->getLogRecordSize();
+ }
+
+ if (onlyMbyteHeaders) {
+ // Show only the first page header in every mbyte of the file.
+ break;
+ }
+
+ if (onlyPageHeaders) {
+ // Show only page headers. Continue with the next page in this for loop.
+ pageIndex = 0;
+ continue;
+ }
+
+ do {
+ // Print out mbyte number, page number and page index.
+ ndbout << j << ":" << i << ":" << pageIndex << endl
+ << " " << j*32 + i << ":" << pageIndex << " ";
+ recordType = redoLogPage[i*PAGESIZE + pageIndex];
+ switch(recordType) {
+ case ZFD_TYPE:
+ fdRecord = (FileDescriptor *) &redoLogPage[i*PAGESIZE + pageIndex];
+ if (thePrintFlag) ndbout << (*fdRecord);
+ if (theCheckFlag) {
+ if(!fdRecord->check()) {
+ doExit();
+ }
+ }
+ if (onlyFileDesc) {
+ delete [] redoLogPage;
+ exit(RETURN_OK);
+ }
+ pageIndex += fdRecord->getLogRecordSize();
+ break;
+
+ case ZNEXT_LOG_RECORD_TYPE:
+ nlRecord = (NextLogRecord *) (&redoLogPage[i*PAGESIZE] + pageIndex);
+ pageIndex += nlRecord->getLogRecordSize(pageIndex);
+ if (pageIndex <= PAGESIZE) {
+ if (thePrintFlag) ndbout << (*nlRecord);
+ if (theCheckFlag) {
+ if(!nlRecord->check()) {
+ doExit();
+ }
+ }
+ }
+ break;
+
+ case ZCOMPLETED_GCI_TYPE:
+ cGCIrecord = (CompletedGCIRecord *) &redoLogPage[i*PAGESIZE + pageIndex];
+ pageIndex += cGCIrecord->getLogRecordSize();
+ if (pageIndex <= PAGESIZE) {
+ if (thePrintFlag) ndbout << (*cGCIrecord);
+ if (theCheckFlag) {
+ if(!cGCIrecord->check()) {
+ doExit();
+ }
+ }
+ }
+ break;
+
+ case ZPREP_OP_TYPE:
+ poRecord = (PrepareOperationRecord *) &redoLogPage[i*PAGESIZE + pageIndex];
+ pageIndex += poRecord->getLogRecordSize();
+ if (pageIndex <= PAGESIZE) {
+ if (thePrintFlag) ndbout << (*poRecord);
+ if (theCheckFlag) {
+ if(!poRecord->check()) {
+ doExit();
+ }
+ }
+ }
+ else {
+ oldPageIndex = pageIndex - poRecord->getLogRecordSize();
+ }
+ break;
+
+ case ZCOMMIT_TYPE:
+ ctRecord = (CommitTransactionRecord *) &redoLogPage[i*PAGESIZE + pageIndex];
+ pageIndex += ctRecord->getLogRecordSize();
+ if (pageIndex <= PAGESIZE) {
+ if (thePrintFlag) ndbout << (*ctRecord);
+ if (theCheckFlag) {
+ if(!ctRecord->check()) {
+ doExit();
+ }
+ }
+ }
+ else {
+ oldPageIndex = pageIndex - ctRecord->getLogRecordSize();
+ }
+ break;
+
+ case ZINVALID_COMMIT_TYPE:
+ ictRecord = (InvalidCommitTransactionRecord *) &redoLogPage[i*PAGESIZE + pageIndex];
+ pageIndex += ictRecord->getLogRecordSize();
+ if (pageIndex <= PAGESIZE) {
+ if (thePrintFlag) ndbout << (*ictRecord);
+ if (theCheckFlag) {
+ if(!ictRecord->check()) {
+ doExit();
+ }
+ }
+ }
+ else {
+ oldPageIndex = pageIndex - ictRecord->getLogRecordSize();
+ }
+ break;
+
+ case ZNEXT_MBYTE_TYPE:
+ nmRecord = (NextMbyteRecord *) &redoLogPage[i*PAGESIZE + pageIndex];
+ if (thePrintFlag) ndbout << (*nmRecord);
+ i = NO_PAGES_IN_MBYTE;
+ break;
+
+ case ZABORT_TYPE:
+ atRecord = (AbortTransactionRecord *) &redoLogPage[i*PAGESIZE + pageIndex];
+ pageIndex += atRecord->getLogRecordSize();
+ if (pageIndex <= PAGESIZE) {
+ if (thePrintFlag) ndbout << (*atRecord);
+ if (theCheckFlag) {
+ if(!atRecord->check()) {
+ doExit();
+ }
+ }
+ }
+ break;
+
+ case ZNEW_PREP_OP_TYPE:
+ case ZFRAG_SPLIT_TYPE:
+ ndbout << endl << "Record type = " << recordType << " not implemented." << endl;
+ doExit();
+
+ default:
+ ndbout << " ------ERROR: UNKNOWN RECORD TYPE------" << endl;
+
+ // Print out remaining data in this page
+ for (int j = pageIndex; j < PAGESIZE; j++){
+ Uint32 unknown = redoLogPage[i*PAGESIZE + j];
+
+ ndbout_c("%-30d%-12u%-12x", j, unknown, unknown);
+ }
+
+ doExit();
+ }
+ } while(pageIndex < PAGESIZE && i < NO_PAGES_IN_MBYTE);
+
+ if (pageIndex > PAGESIZE) {
+ // The last record overlapped page boundary. Must redo that record.
+ pageIndex = readRecordOverPageBoundary(&redoLogPage[i*PAGESIZE],
+ pageIndex, oldPageIndex, recordType);
+ } else {
+ pageIndex = 0;
+ }
+ ndbout << endl;
+ }//for
+ ndbout << endl;
+ if (startAtMbyte != 0) {
+ break;
+ }
+ }//for
+ fclose(f);
+ delete [] redoLogPage;
+ exit(RETURN_OK);
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+Uint32 readFromFile(FILE * f, Uint32 *toPtr, Uint32 sizeInWords) {
+ Uint32 noOfReadWords;
+ if ( !(noOfReadWords = fread(toPtr, sizeof(Uint32), sizeInWords, f)) ) {
+ ndbout << "Error reading file" << endl;
+ doExit();
+ }
+
+ return noOfReadWords;
+}
+
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+Uint32 readRecordOverPageBoundary(Uint32 *pagePtr, Uint32 pageIndex, Uint32 oldPageIndex, Uint32 recordType) {
+ Uint32 pageHeader[PAGEHEADERSIZE];
+ Uint32 tmpPages[PAGESIZE*10];
+ PageHeader *thePageHeader;
+ Uint32 recordSize = 0;
+
+ PrepareOperationRecord *poRecord;
+ CommitTransactionRecord *ctRecord;
+ InvalidCommitTransactionRecord *ictRecord;
+
+ memcpy(pageHeader, pagePtr + PAGESIZE, PAGEHEADERSIZE*sizeof(Uint32));
+ memcpy(tmpPages, pagePtr + oldPageIndex, (PAGESIZE - oldPageIndex)*sizeof(Uint32));
+ memcpy(tmpPages + PAGESIZE - oldPageIndex ,
+ (pagePtr + PAGESIZE + PAGEHEADERSIZE),
+ (PAGESIZE - PAGEHEADERSIZE)*sizeof(Uint32));
+
+ switch(recordType) {
+ case ZPREP_OP_TYPE:
+ poRecord = (PrepareOperationRecord *) tmpPages;
+ recordSize = poRecord->getLogRecordSize();
+ if (recordSize < (PAGESIZE - PAGEHEADERSIZE)) {
+ if (theCheckFlag) {
+ if(!poRecord->check()) {
+ doExit();
+ }
+ }
+ if (thePrintFlag) ndbout << (*poRecord);
+ } else {
+ ndbout << "Error: Record greater than a Page" << endl;
+ }
+ break;
+
+ case ZCOMMIT_TYPE:
+ ctRecord = (CommitTransactionRecord *) tmpPages;
+ recordSize = ctRecord->getLogRecordSize();
+ if (recordSize < (PAGESIZE - PAGEHEADERSIZE)) {
+ if (theCheckFlag) {
+ if(!ctRecord->check()) {
+ doExit();
+ }
+ }
+ if (thePrintFlag) ndbout << (*ctRecord);
+ } else {
+ ndbout << endl << "Error: Record greater than a Page" << endl;
+ }
+ break;
+
+ case ZINVALID_COMMIT_TYPE:
+ ictRecord = (InvalidCommitTransactionRecord *) tmpPages;
+ recordSize = ictRecord->getLogRecordSize();
+ if (recordSize < (PAGESIZE - PAGEHEADERSIZE)) {
+ if (theCheckFlag) {
+ if(!ictRecord->check()) {
+ doExit();
+ }
+ }
+ if (thePrintFlag) ndbout << (*ictRecord);
+ } else {
+ ndbout << endl << "Error: Record greater than a Page" << endl;
+ }
+ break;
+
+ case ZNEW_PREP_OP_TYPE:
+ case ZABORT_TYPE:
+ case ZFRAG_SPLIT_TYPE:
+ case ZNEXT_MBYTE_TYPE:
+ ndbout << endl << "Record type = " << recordType << " not implemented." << endl;
+ return 0;
+
+ default:
+ ndbout << endl << "Error: Unknown record type. Record type = " << recordType << endl;
+ return 0;
+ }
+
+ thePageHeader = (PageHeader *) (pagePtr + PAGESIZE);
+ if (thePrintFlag) ndbout << (*thePageHeader);
+
+ return PAGEHEADERSIZE - PAGESIZE + oldPageIndex + recordSize;
+}
+
+//----------------------------------------------------------------
+//
+//----------------------------------------------------------------
+
+
+void usage(const char * prg){
+ ndbout << endl << "Usage: " << endl << prg
+ << " <Binary log file> [-noprint] [-nocheck] [-mbyte <0-15>] "
+ << "[-mbyteheaders] [-pageheaders] [-filedescriptors] [-page <0-31>] "
+ << "[-pageindex <12-8191>]"
+ << endl << endl;
+
+}
+void readArguments(int argc, const char** argv)
+{
+ if(argc < 2 || argc > 9){
+ usage(argv[0]);
+ doExit();
+ }
+
+ strcpy(fileName, argv[1]);
+ argc--;
+
+ int i = 2;
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-noprint") == 0) {
+ thePrintFlag = false;
+ } else if (strcmp(argv[i], "-nocheck") == 0) {
+ theCheckFlag = false;
+ } else if (strcmp(argv[i], "-mbyteheaders") == 0) {
+ onlyMbyteHeaders = true;
+ } else if (strcmp(argv[i], "-pageheaders") == 0) {
+ onlyPageHeaders = true;
+ } else if (strcmp(argv[i], "-filedescriptors") == 0) {
+ onlyFileDesc = true;
+ } else if (strcmp(argv[i], "-mbyte") == 0) {
+ startAtMbyte = atoi(argv[i+1]);
+ if (startAtMbyte > 15) {
+ usage(argv[0]);
+ doExit();
+ }
+ argc--;
+ i++;
+ } else if (strcmp(argv[i], "-page") == 0) {
+ startAtPage = atoi(argv[i+1]);
+ if (startAtPage > 31) {
+ usage(argv[0]);
+ doExit();
+ }
+ argc--;
+ i++;
+ } else if (strcmp(argv[i], "-pageindex") == 0) {
+ startAtPageIndex = atoi(argv[i+1]);
+ if (startAtPageIndex > 8191 || startAtPageIndex < 12) {
+ usage(argv[0]);
+ doExit();
+ }
+ argc--;
+ i++;
+ } else {
+ usage(argv[0]);
+ doExit();
+ }
+ argc--;
+ i++;
+ }
+
+}
+
+void doExit() {
+ ndbout << "Error in redoLogReader(). Exiting!" << endl;
+ fclose(f);
+ delete [] redoLogPage;
+ exit(RETURN_ERROR);
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
new file mode 100644
index 00000000000..2baa4400409
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
@@ -0,0 +1,1974 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBTC_H
+#define DBTC_H
+
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+#include <DLHashTable.hpp>
+#include <SLList.hpp>
+#include <DLList.hpp>
+#include <DLFifoList.hpp>
+#include <DataBuffer.hpp>
+#include <Bitmask.hpp>
+#include <AttributeList.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/LqhTransConf.hpp>
+#include <signaldata/LqhKey.hpp>
+#include <signaldata/TrigAttrInfo.hpp>
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/TransIdAI.hpp>
+#include <signaldata/EventReport.hpp>
+#include <trigger_definitions.h>
+#include <SignalCounter.hpp>
+
+#ifdef DBTC_C
+/*
+ * 2.2 LOCAL SYMBOLS
+ * -----------------
+ */
+#define Z8NIL 255
+#define ZAPI_CONNECT_FILESIZE 20
+#define ZATTRBUF_FILESIZE 4000
+#define ZCLOSED 2
+#define ZCOMMITING 0 /* VALUE FOR TRANSTATUS */
+#define ZCOMMIT_SETUP 2
+#define ZCONTINUE_ABORT_080 4
+#define ZDATABUF_FILESIZE 4000
+#define ZGCP_FILESIZE 10
+#define ZINBUF_DATA_LEN 24 /* POSITION OF 'DATA LENGHT'-VARIABLE. */
+#define ZINBUF_NEXT 27 /* POSITION OF 'NEXT'-VARIABLE. */
+#define ZINBUF_PREV 26 /* POSITION OF 'PREVIOUS'-VARIABLE. */
+#define ZINTSPH1 1
+#define ZINTSPH2 2
+#define ZINTSPH3 3
+#define ZINTSPH6 6
+#define ZLASTPHASE 255
+#define ZMAX_DATA_IN_LQHKEYREQ 12
+#define ZNODEBUF_FILESIZE 2000
+#define ZNR_OF_SEIZE 10
+#define ZSCANREC_FILE_SIZE 100
+#define ZSCAN_FRAGREC_FILE_SIZE 400
+#define ZSCAN_OPREC_FILE_SIZE 400
+#define ZSEND_ATTRINFO 0
+#define ZSPH1 1
+#define ZTABREC_FILESIZE 16
+#define ZTAKE_OVER_ACTIVE 1
+#define ZTAKE_OVER_IDLE 0
+#define ZTC_CONNECT_FILESIZE 200
+#define ZTCOPCONF_SIZE 6
+
+// ----------------------------------------
+// Error Codes for Scan
+// ----------------------------------------
+#define ZNO_CONCURRENCY_ERROR 242
+#define ZTOO_HIGH_CONCURRENCY_ERROR 244
+#define ZNO_SCANREC_ERROR 245
+#define ZNO_FRAGMENT_ERROR 246
+#define ZSCAN_AI_LEN_ERROR 269
+#define ZSCAN_LQH_ERROR 270
+#define ZSCAN_FRAG_LQH_ERROR 274
+
+#define ZSCANTIME_OUT_ERROR 296
+#define ZSCANTIME_OUT_ERROR2 297
+
+// ----------------------------------------
+// Error Codes for transactions
+// ----------------------------------------
+#define ZSTATE_ERROR 202
+#define ZLENGTH_ERROR 207 // Also Scan
+#define ZERO_KEYLEN_ERROR 208
+#define ZSIGNAL_ERROR 209
+#define ZGET_ATTRBUF_ERROR 217 // Also Scan
+#define ZGET_DATAREC_ERROR 218
+#define ZMORE_AI_IN_TCKEYREQ_ERROR 220
+#define ZCOMMITINPROGRESS 230
+#define ZROLLBACKNOTALLOWED 232
+#define ZNO_FREE_TC_CONNECTION 233 // Also Scan
+#define ZABORTINPROGRESS 237
+#define ZPREPAREINPROGRESS 238
+#define ZWRONG_SCHEMA_VERSION_ERROR 241 // Also Scan
+#define ZSCAN_NODE_ERROR 250
+#define ZTRANS_STATUS_ERROR 253
+#define ZTIME_OUT_ERROR 266
+#define ZSIMPLE_READ_WITHOUT_AI 271
+#define ZNO_AI_WITH_UPDATE 272
+#define ZSEIZE_API_COPY_ERROR 275
+#define ZSCANINPROGRESS 276
+#define ZABORT_ERROR 277
+#define ZCOMMIT_TYPE_ERROR 278
+
+#define ZNO_FREE_TC_MARKER 279
+#define ZNODE_SHUTDOWN_IN_PROGRESS 280
+#define ZCLUSTER_SHUTDOWN_IN_PROGRESS 281
+#define ZWRONG_STATE 282
+#define ZCLUSTER_IN_SINGLEUSER_MODE 299
+
+#define ZDROP_TABLE_IN_PROGRESS 283
+#define ZNO_SUCH_TABLE 284
+#define ZUNKNOWN_TABLE_ERROR 285
+#define ZNODEFAIL_BEFORE_COMMIT 286
+#define ZINDEX_CORRUPT_ERROR 287
+
+// ----------------------------------------
+// Seize error
+// ----------------------------------------
+#define ZNO_FREE_API_CONNECTION 219
+#define ZSYSTEM_NOT_STARTED_ERROR 203
+
+// ----------------------------------------
+// Release errors
+// ----------------------------------------
+#define ZINVALID_CONNECTION 229
+
+
+#define ZNOT_FOUND 626
+#define ZALREADYEXIST 630
+#define ZINCONSISTENTHASHINDEX 892
+#define ZNOTUNIQUE 893
+#endif
+
+class Dbtc: public SimulatedBlock {
+public:
+ enum ConnectionState {
+ CS_CONNECTED = 0,
+ CS_DISCONNECTED = 1,
+ CS_STARTED = 2,
+ CS_RECEIVING = 3,
+ CS_PREPARED = 4,
+ CS_START_PREPARING = 5,
+ CS_REC_PREPARING = 6,
+ CS_RESTART = 7,
+ CS_ABORTING = 8,
+ CS_COMPLETING = 9,
+ CS_COMPLETE_SENT = 10,
+ CS_PREPARE_TO_COMMIT = 11,
+ CS_COMMIT_SENT = 12,
+ CS_START_COMMITTING = 13,
+ CS_COMMITTING = 14,
+ CS_REC_COMMITTING = 15,
+ CS_WAIT_ABORT_CONF = 16,
+ CS_WAIT_COMPLETE_CONF = 17,
+ CS_WAIT_COMMIT_CONF = 18,
+ CS_FAIL_ABORTING = 19,
+ CS_FAIL_ABORTED = 20,
+ CS_FAIL_PREPARED = 21,
+ CS_FAIL_COMMITTING = 22,
+ CS_FAIL_COMMITTED = 23,
+ CS_FAIL_COMPLETED = 24,
+ CS_START_SCAN = 25
+ };
+
+ enum OperationState {
+ OS_CONNECTING_DICT = 0,
+ OS_CONNECTED = 1,
+ OS_OPERATING = 2,
+ OS_PREPARED = 3,
+ OS_COMMITTING = 4,
+ OS_COMMITTED = 5,
+ OS_COMPLETING = 6,
+ OS_COMPLETED = 7,
+ OS_RESTART = 8,
+ OS_ABORTING = 9,
+ OS_ABORT_SENT = 10,
+ OS_TAKE_OVER = 11,
+ OS_WAIT_DIH = 12,
+ OS_WAIT_KEYINFO = 13,
+ OS_WAIT_ATTR = 14,
+ OS_WAIT_COMMIT_CONF = 15,
+ OS_WAIT_ABORT_CONF = 16,
+ OS_WAIT_COMPLETE_CONF = 17,
+ OS_WAIT_SCAN = 18
+ };
+
+ enum AbortState {
+ AS_IDLE = 0,
+ AS_ACTIVE = 1
+ };
+
+ enum HostState {
+ HS_ALIVE = 0,
+ HS_DEAD = 1
+ };
+
+ enum LqhTransState {
+ LTS_IDLE = 0,
+ LTS_ACTIVE = 1
+ };
+
+ enum TakeOverState {
+ TOS_NOT_DEFINED = 0,
+ TOS_IDLE = 1,
+ TOS_ACTIVE = 2,
+ TOS_COMPLETED = 3,
+ TOS_NODE_FAILED = 4
+ };
+
+ enum FailState {
+ FS_IDLE = 0,
+ FS_LISTENING = 1,
+ FS_COMPLETING = 2
+ };
+
+ enum SystemStartState {
+ SSS_TRUE = 0,
+ SSS_FALSE = 1
+ };
+
+ enum TimeOutCheckState {
+ TOCS_TRUE = 0,
+ TOCS_FALSE = 1
+ };
+
+ enum ReturnSignal {
+ RS_NO_RETURN = 0,
+ RS_TCKEYCONF = 1,
+ RS_TC_COMMITCONF = 3,
+ RS_TCROLLBACKCONF = 4,
+ RS_TCROLLBACKREP = 5
+ };
+
+ enum IndexOperationState {
+ IOS_NOOP = 0,
+ IOS_INDEX_ACCESS = 1,
+ IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF = 2,
+ IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI = 3,
+ IOS_INDEX_OPERATION = 4
+ };
+
+ enum IndexState {
+ IS_BUILDING = 0, // build in progress, start state at create
+ IS_ONLINE = 1 // ready to use
+ };
+
+
+ /**--------------------------------------------------------------------------
+ * LOCAL SYMBOLS PER 'SYMBOL-VALUED' VARIABLE
+ *
+ *
+ * NSYMB ZAPI_CONNECT_FILESIZE = 20
+ * NSYMB ZTC_CONNECT_FILESIZE = 200
+ * NSYMB ZHOST_FILESIZE = 16
+ * NSYMB ZDATABUF_FILESIZE = 4000
+ * NSYMB ZATTRBUF_FILESIZE = 4000
+ * NSYMB ZGCP_FILESIZE = 10
+ *
+ *
+ * ABORTED CODES
+ * TPHASE NSYMB ZSPH1 = 1
+ * NSYMB ZLASTPHASE = 255
+ *
+ *
+ * LQH_TRANS
+ * NSYMB ZTRANS_ABORTED = 1
+ * NSYMB ZTRANS_PREPARED = 2
+ * NSYMB ZTRANS_COMMITTED = 3
+ * NSYMB ZCOMPLETED_LQH_TRANS = 4
+ * NSYMB ZTRANS_COMPLETED = 5
+ *
+ *
+ * TAKE OVER
+ * NSYMB ZTAKE_OVER_IDLE = 0
+ * NSYMB ZTAKE_OVER_ACTIVE = 1
+ *
+ * ATTRBUF (ATTRBUF_RECORD)
+ * NSYMB ZINBUF_DATA_LEN = 24
+ * NSYMB ZINBUF_NEXTFREE = 25 (NOT USED )
+ * NSYMB ZINBUF_PREV = 26
+ * NSYMB ZINBUF_NEXT = 27
+ -------------------------------------------------------------------------*/
+ /*
+ 2.3 RECORDS AND FILESIZES
+ -------------------------
+ */
+ /* **************************************************************** */
+ /* ---------------------------------------------------------------- */
+ /* ------------------- TRIGGER AND INDEX DATA --------------------- */
+ /* ---------------------------------------------------------------- */
+ /* **************************************************************** */
+ /* ********* DEFINED TRIGGER DATA ********* */
+ /* THIS RECORD FORMS LISTS OF ACTIVE */
+ /* TRIGGERS FOR EACH TABLE. */
+ /* THE RECORDS ARE MANAGED BY A TRIGGER */
+ /* POOL WHERE A TRIGGER RECORD IS SEIZED */
+ /* WHEN A TRIGGER IS ACTIVATED AND RELEASED */
+ /* WHEN THE TRIGGER IS DEACTIVATED. */
+ /* **************************************** */
+ struct TcDefinedTriggerData {
+ /**
+ * Trigger id, used to identify the trigger
+ */
+ UintR triggerId;
+
+ /**
+ * Trigger type, defines what the trigger is used for
+ */
+ TriggerType::Value triggerType;
+
+ /**
+ * Trigger type, defines what the trigger is used for
+ */
+ TriggerEvent::Value triggerEvent;
+
+ /**
+ * Attribute mask, defines what attributes are to be monitored
+ * Can be seen as a compact representation of SQL column name list
+ */
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attributeMask;
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+
+ /**
+ * Index id, only used by secondary_index triggers. This is same as
+ * index table id in DICT.
+ **/
+ Uint32 indexId;
+
+ /**
+ * Prev pointer (used in list)
+ */
+ Uint32 prevList;
+
+ inline void print(NdbOut & s) const {
+ s << "[DefinedTriggerData = " << triggerId << "]";
+ }
+ };
+ typedef Ptr<TcDefinedTriggerData> DefinedTriggerPtr;
+
+ /**
+ * Pool of trigger data record
+ */
+ ArrayPool<TcDefinedTriggerData> c_theDefinedTriggerPool;
+
+ /**
+ * The list of active triggers
+ */
+ DLList<TcDefinedTriggerData> c_theDefinedTriggers;
+
+ typedef DataBuffer<11> AttributeBuffer;
+
+ AttributeBuffer::DataBufferPool c_theAttributeBufferPool;
+
+ UintR c_transactionBufferSpace;
+
+
+ /* ********** FIRED TRIGGER DATA ********** */
+ /* THIS RECORD FORMS LISTS OF FIRED */
+ /* TRIGGERS FOR A TRANSACTION. */
+ /* THE RECORDS ARE MANAGED BY A TRIGGER */
+ /* POOL WHERE A TRIGGER RECORD IS SEIZED */
+ /* WHEN A TRIGGER IS ACTIVATED AND RELEASED */
+ /* WHEN THE TRIGGER IS DEACTIVATED. */
+ /* **************************************** */
+ struct TcFiredTriggerData {
+ TcFiredTriggerData() {}
+
+ /**
+ * Trigger id, used to identify the trigger
+ **/
+ Uint32 triggerId;
+
+ /**
+ * The operation that fired the trigger
+ */
+ Uint32 fireingOperation;
+
+ /**
+ * Used for scrapping in case of node failure
+ */
+ Uint32 nodeId;
+
+ /**
+ * Trigger attribute info, primary key value(s)
+ */
+ AttributeBuffer::Head keyValues;
+
+ /**
+ * Trigger attribute info, attribute value(s) before operation
+ */
+ AttributeBuffer::Head beforeValues;
+
+ /**
+ * Trigger attribute info, attribute value(s) after operation
+ */
+ AttributeBuffer::Head afterValues;
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ Uint32 nextHash;
+ };
+
+ /**
+ * Prev pointer (used in list)
+ */
+ union {
+ Uint32 prevList;
+ Uint32 prevHash;
+ };
+
+ inline void print(NdbOut & s) const {
+ s << "[FiredTriggerData = " << triggerId << "]";
+ }
+
+ inline Uint32 hashValue() const {
+ return fireingOperation ^ nodeId;
+ }
+
+ inline bool equal(const TcFiredTriggerData & rec) const {
+ return fireingOperation == rec.fireingOperation && nodeId == rec.nodeId;
+ }
+ };
+ typedef Ptr<TcFiredTriggerData> FiredTriggerPtr;
+
+ /**
+ * Pool of trigger data record
+ */
+ ArrayPool<TcFiredTriggerData> c_theFiredTriggerPool;
+ DLHashTable<TcFiredTriggerData> c_firedTriggerHash;
+ AttributeBuffer::DataBufferPool c_theTriggerAttrInfoPool;
+
+ Uint32 c_maxNumberOfDefinedTriggers;
+ Uint32 c_maxNumberOfFiredTriggers;
+
+ struct AttrInfoRecord {
+ /**
+ * Pre-allocated AttrInfo signal
+ */
+ AttrInfo attrInfo;
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ /**
+ * Prev pointer (used in list)
+ */
+ Uint32 prevList;
+ };
+
+
+ /* ************* INDEX DATA *************** */
+ /* THIS RECORD FORMS LISTS OF ACTIVE */
+ /* INDEX FOR EACH TABLE. */
+ /* THE RECORDS ARE MANAGED BY A INDEX */
+ /* POOL WHERE AN INDEX RECORD IS SEIZED */
+ /* WHEN AN INDEX IS CREATED AND RELEASED */
+ /* WHEN THE INDEX IS DROPPED. */
+ /* **************************************** */
+ struct TcIndexData {
+ /**
+ * IndexState
+ */
+ IndexState indexState;
+
+ /**
+ * Index id, same as index table id in DICT
+ */
+ Uint32 indexId;
+
+ /**
+ * Index attribute list. Only the length is used in v21x.
+ */
+ AttributeList attributeList;
+
+ /**
+ * Primary table id, the primary table to be indexed
+ */
+ Uint32 primaryTableId;
+
+ /**
+ * Primary key position in secondary table
+ */
+ Uint32 primaryKeyPos;
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ /**
+ * Prev pointer (used in list)
+ */
+ Uint32 prevList;
+ };
+
+ typedef Ptr<TcIndexData> TcIndexDataPtr;
+
+ /**
+ * Pool of index data record
+ */
+ ArrayPool<TcIndexData> c_theIndexPool;
+
+ /**
+ * The list of defined indexes
+ */
+ ArrayList<TcIndexData> c_theIndexes;
+ UintR c_maxNumberOfIndexes;
+
+ struct TcIndexOperation {
+ TcIndexOperation(AttributeBuffer::DataBufferPool & abp) :
+ indexOpState(IOS_NOOP),
+ expectedKeyInfo(0),
+ keyInfo(abp),
+ expectedAttrInfo(0),
+ attrInfo(abp),
+ expectedTransIdAI(0),
+ transIdAI(abp),
+ indexReadTcConnect(RNIL)
+ {}
+
+ ~TcIndexOperation()
+ {
+ }
+
+ // Index data
+ Uint32 indexOpId;
+ IndexOperationState indexOpState; // Used to mark on-going TcKeyReq
+ Uint32 expectedKeyInfo;
+ AttributeBuffer keyInfo; // For accumulating IndxKeyInfo
+ Uint32 expectedAttrInfo;
+ AttributeBuffer attrInfo; // For accumulating IndxAttrInfo
+ Uint32 expectedTransIdAI;
+ AttributeBuffer transIdAI; // For accumulating TransId_AI
+
+ TcKeyReq tcIndxReq;
+ UintR connectionIndex;
+ UintR indexReadTcConnect; //
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ /**
+ * Prev pointer (used in list)
+ */
+ Uint32 prevList;
+ };
+
+ typedef Ptr<TcIndexOperation> TcIndexOperationPtr;
+
+ /**
+ * Pool of index data record
+ */
+ ArrayPool<TcIndexOperation> c_theIndexOperationPool;
+
+ /**
+ * The list of index operations
+ */
+ ArrayList<TcIndexOperation> c_theIndexOperations;
+
+ UintR c_maxNumberOfIndexOperations;
+
+ struct TcSeizedIndexOperation {
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ /**
+ * Prev pointer (used in list)
+ */
+ Uint32 prevList;
+ };
+
+ /**
+ * Pool of seized index operations
+ */
+ ArrayPool<TcSeizedIndexOperation> c_theSeizedIndexOperationPool;
+
+ typedef Ptr<TcSeizedIndexOperation> TcSeizedIndexOperationPtr;
+
+ /************************** API CONNECT RECORD ***********************
+ * The API connect record contains the connection record to which the
+ * application connects.
+ *
+ * The application can send one operation at a time. It can send a
+ * new operation immediately after sending the previous operation.
+ * Thereby several operations can be active in one transaction within TC.
+ * This is achieved by using the API connect record.
+ * Each active operation is handled by the TC connect record.
+ * As soon as the TC connect record has sent the
+ * request to the LQH it is ready to receive new operations.
+ * The LQH connect record takes care of waiting for an operation to
+ * complete.
+ * When an operation has completed on the LQH connect record,
+ * a new operation can be started on this LQH connect record.
+ *******************************************************************
+ *
+ * API CONNECT RECORD ALIGNED TO BE 256 BYTES
+ ********************************************************************/
+
+ /*******************************************************************>*/
+ // We break out the API Timer for optimisation on scanning rather than
+ // on fast access.
+ /*******************************************************************>*/
+ inline void setApiConTimer(Uint32 apiConPtrI, Uint32 value, Uint32 line){
+ c_apiConTimer[apiConPtrI] = value;
+ c_apiConTimer_line[apiConPtrI] = line;
+ }
+
+ inline Uint32 getApiConTimer(Uint32 apiConPtrI) const {
+ return c_apiConTimer[apiConPtrI];
+ }
+ UintR* c_apiConTimer;
+ UintR* c_apiConTimer_line;
+
+ struct ApiConnectRecord {
+ ApiConnectRecord(ArrayPool<TcFiredTriggerData> & firedTriggerPool,
+ ArrayPool<TcSeizedIndexOperation> & seizedIndexOpPool):
+ theFiredTriggers(firedTriggerPool),
+ isIndexOp(false),
+ theSeizedIndexOperations(seizedIndexOpPool)
+ {}
+
+ //---------------------------------------------------
+ // First 16 byte cache line. Hot variables.
+ //---------------------------------------------------
+ ConnectionState apiConnectstate;
+ UintR transid[2];
+ UintR firstTcConnect;
+
+ //---------------------------------------------------
+ // Second 16 byte cache line. Hot variables.
+ //---------------------------------------------------
+ UintR lqhkeyconfrec;
+ UintR cachePtr;
+ UintR currSavePointId;
+ UintR counter;
+
+ //---------------------------------------------------
+ // Third 16 byte cache line. First and second cache
+ // line plus this will be enough for copy API records.
+ // Variables used in late phases.
+ //---------------------------------------------------
+ UintR nextGcpConnect;
+ UintR prevGcpConnect;
+ UintR gcpPointer;
+ UintR ndbapiConnect;
+
+ //---------------------------------------------------
+ // Fourth 16 byte cache line. Only used in late phases.
+ // Plus 4 bytes of error handling.
+ //---------------------------------------------------
+ UintR nextApiConnect;
+ BlockReference ndbapiBlockref;
+ UintR apiCopyRecord;
+ UintR globalcheckpointid;
+
+ //---------------------------------------------------
+ // Second 64 byte cache line starts. First 16 byte
+ // cache line in this one. Variables primarily used
+ // in early phase.
+ //---------------------------------------------------
+ UintR lastTcConnect;
+ UintR lqhkeyreqrec;
+ AbortState abortState;
+ Uint32 buddyPtr;
+ Uint8 m_exec_flag;
+ Uint8 unused2;
+ Uint8 takeOverRec;
+ Uint8 currentReplicaNo;
+
+ //---------------------------------------------------
+ // Error Handling variables. If cache line 32 bytes
+ // ensures that cache line is still only read in
+ // early phases.
+ //---------------------------------------------------
+ union {
+ UintR apiScanRec;
+ UintR commitAckMarker;
+ };
+ UintR currentTcConnect;
+ BlockReference tcBlockref;
+ Uint16 returncode;
+ Uint16 takeOverInd;
+
+ //---------------------------------------------------
+ // Second 64 byte cache line. Third 16 byte cache line
+ // in this one. Variables primarily used in early phase
+ // and checked in late phase.
+ // Fourth cache line is the tcSendArray that is used
+ // when two and three operations are responded to in
+ // parallel. The first two entries in tcSendArray is
+ // part of the third cache line.
+ //---------------------------------------------------
+ //---------------------------------------------------
+ // timeOutCounter is used waiting for ABORTCONF, COMMITCONF
+ // and COMPLETECONF
+ //---------------------------------------------------
+ UintR failureNr;
+ Uint8 tckeyrec; // Ändrad från R
+ Uint8 tcindxrec;
+ Uint8 apiFailState; // Ändrad från R
+ ReturnSignal returnsignal;
+ Uint8 timeOutCounter;
+
+ UintR tcSendArray[6];
+
+ // Trigger data
+
+ /**
+ * The list of fired triggers
+ */
+ DLFifoList<TcFiredTriggerData> theFiredTriggers;
+
+ bool triggerPending; // Used to mark waiting for a CONTINUEB
+
+ // Index data
+
+ bool isIndexOp; // Used to mark on-going TcKeyReq as indx table access
+ bool indexOpReturn;
+ UintR noIndexOp; // No outstanding index ops
+
+ // Index op return context
+ UintR indexOp;
+ UintR clientData;
+ UintR attrInfoLen;
+
+ UintR accumulatingIndexOp;
+ UintR executingIndexOp;
+ UintR tcIndxSendArray[6];
+ ArrayList<TcSeizedIndexOperation> theSeizedIndexOperations;
+ };
+
+ typedef Ptr<ApiConnectRecord> ApiConnectRecordPtr;
+
+
+ /************************** TC CONNECT RECORD ************************/
+ /* *******************************************************************/
+ /* TC CONNECT RECORD KEEPS ALL INFORMATION TO CARRY OUT A TRANSACTION*/
+ /* THE TRANSACTION CONTROLLER ESTABLISHES CONNECTIONS TO DIFFERENT */
+ /* BLOCKS TO CARRY OUT THE TRANSACTION. THERE CAN BE SEVERAL RECORDS */
+ /* PER ACTIVE TRANSACTION. THE TC CONNECT RECORD COOPERATES WITH THE */
+ /* API CONNECT RECORD FOR COMMUNICATION WITH THE API AND WITH THE */
+ /* LQH CONNECT RECORD FOR COMMUNICATION WITH THE LQH'S INVOLVED IN */
+ /* THE TRANSACTION. TC CONNECT RECORD IS PERMANENTLY CONNECTED TO A */
+ /* RECORD IN DICT AND ONE IN DIH. IT CONTAINS A LIST OF ACTIVE LQH */
+ /* CONNECT RECORDS AND A LIST OF STARTED BUT NOT ACTIVE LQH CONNECT */
+ /* RECORDS. IT DOES ALSO CONTAIN A LIST OF ALL OPERATIONS THAT ARE */
+ /* EXECUTED WITH THE TC CONNECT RECORD. */
+ /*******************************************************************>*/
+ /* TC_CONNECT RECORD ALIGNED TO BE 128 BYTES */
+ /*******************************************************************>*/
+ struct TcConnectRecord {
+ //---------------------------------------------------
+ // First 16 byte cache line. Those variables are only
+ // used in error cases.
+ //---------------------------------------------------
+ UintR tcOprec; /* TC OPREC of operation being taken over */
+ Uint16 failData[4]; /* Failed nodes when taking over an operation */
+ UintR nextTcFailHash;
+
+ //---------------------------------------------------
+ // Second 16 byte cache line. Those variables are used
+ // from LQHKEYCONF to sending COMMIT and COMPLETED.
+ //---------------------------------------------------
+ UintR lastLqhCon; /* Connect record in last replicas Lqh record */
+ Uint16 lastLqhNodeId; /* Node id of last replicas Lqh */
+ Uint16 m_execAbortOption;/* TcKeyReq::ExecuteAbortOption */
+ UintR commitAckMarker; /* CommitMarker I value */
+
+ //---------------------------------------------------
+ // Third 16 byte cache line. The hottest variables.
+ //---------------------------------------------------
+ OperationState tcConnectstate; /* THE STATE OF THE CONNECT*/
+ UintR apiConnect; /* POINTER TO API CONNECT RECORD */
+ UintR nextTcConnect; /* NEXT TC RECORD*/
+ Uint8 dirtyOp;
+ Uint8 lastReplicaNo; /* NUMBER OF THE LAST REPLICA IN THE OPERATION */
+ Uint8 noOfNodes; /* TOTAL NUMBER OF NODES IN OPERATION */
+ Uint8 operation; /* OPERATION TYPE */
+ /* 0 = READ REQUEST */
+ /* 1 = UPDATE REQUEST */
+ /* 2 = INSERT REQUEST */
+ /* 3 = DELETE REQUEST */
+
+ //---------------------------------------------------
+ // Fourth 16 byte cache line. The mildly hot variables.
+ // tcNodedata expands 4 Bytes into the next cache line
+ // with indexes almost never used.
+ //---------------------------------------------------
+ UintR clientData; /* SENDERS OPERATION POINTER */
+ UintR dihConnectptr; /* CONNECTION TO DIH BLOCK ON THIS NODE */
+ UintR prevTcConnect; /* DOUBLY LINKED LIST OF TC CONNECT RECORDS*/
+ UintR savePointId;
+
+ Uint16 tcNodedata[4];
+
+ // Trigger data
+ FiredTriggerPtr accumulatingTriggerData;
+ UintR noFiredTriggers;
+ UintR noReceivedTriggers;
+ UintR triggerExecutionCount;
+ UintR triggeringOperation;
+ UintR savedState[LqhKeyConf::SignalLength];
+
+ // Index data
+ bool isIndexOp; // Used to mark on-going TcKeyReq as index table access
+ UintR indexOp;
+ UintR currentIndexId;
+ UintR attrInfoLen;
+ };
+
+ friend struct TcConnectRecord;
+
+ typedef Ptr<TcConnectRecord> TcConnectRecordPtr;
+
+ // ********************** CACHE RECORD **************************************
+ //---------------------------------------------------------------------------
+ // This record is used between reception of TCKEYREQ and sending of LQHKEYREQ
+ // It is separatedso as to improve the cache hit rate and also to minimise
+ // the necessary memory storage in NDB Cluster.
+ //---------------------------------------------------------------------------
+
+ struct CacheRecord {
+ //---------------------------------------------------
+ // First 16 byte cache line. Variables used by
+ // ATTRINFO processing.
+ //---------------------------------------------------
+ UintR firstAttrbuf; /* POINTER TO LINKED LIST OF ATTRIBUTE BUFFERS */
+ UintR lastAttrbuf; /* POINTER TO LINKED LIST OF ATTRIBUTE BUFFERS */
+ UintR currReclenAi;
+ Uint16 attrlength; /* ATTRIBUTE INFORMATION LENGTH */
+ Uint16 save1;
+
+ //---------------------------------------------------
+ // Second 16 byte cache line. Variables initiated by
+ // TCKEYREQ and used in LQHKEYREQ.
+ //---------------------------------------------------
+ UintR attrinfo15[4];
+
+ //---------------------------------------------------
+ // Third 16 byte cache line. Variables initiated by
+ // TCKEYREQ and used in LQHKEYREQ.
+ //---------------------------------------------------
+ UintR attrinfo0;
+ UintR schemaVersion;/* SCHEMA VERSION USED IN TRANSACTION */
+ UintR tableref; /* POINTER TO THE TABLE IN WHICH THE FRAGMENT EXISTS*/
+ Uint16 apiVersionNo;
+ Uint16 keylen; /* KEY LENGTH SENT BY REQUEST SIGNAL */
+
+ //---------------------------------------------------
+ // Fourth 16 byte cache line. Variables initiated by
+ // TCKEYREQ and used in LQHKEYREQ.
+ //---------------------------------------------------
+ UintR keydata[4]; /* RECEIVES FIRST 16 BYTES OF TUPLE KEY */
+
+ //---------------------------------------------------
+ // First 16 byte cache line in second 64 byte cache
+ // line. Diverse use.
+ //---------------------------------------------------
+ UintR fragmentid; /* THE COMPUTED FRAGMENT ID */
+ UintR hashValue; /* THE HASH VALUE USED TO LOCATE FRAGMENT */
+
+ Uint8 distributionKeyIndicator;
+ Uint8 m_special_hash; // collation or distribution key
+ Uint8 unused2;
+ Uint8 lenAiInTckeyreq; /* LENGTH OF ATTRIBUTE INFORMATION IN TCKEYREQ */
+
+ Uint8 fragmentDistributionKey; /* DIH generation no */
+
+ /**
+ * EXECUTION MODE OF OPERATION
+ * 0 = NORMAL EXECUTION, 1 = INTERPRETED EXECUTION
+ */
+ Uint8 opExec;
+
+ /**
+ * LOCK TYPE OF OPERATION IF READ OPERATION
+ * 0 = READ LOCK, 1 = WRITE LOCK
+ */
+ Uint8 opLock;
+
+ /**
+ * IS THE OPERATION A SIMPLE TRANSACTION
+ * 0 = NO, 1 = YES
+ */
+ Uint8 opSimple;
+
+ //---------------------------------------------------
+ // Second 16 byte cache line in second 64 byte cache
+ // line. Diverse use.
+ //---------------------------------------------------
+ UintR distributionKey;
+ UintR nextCacheRec;
+ UintR unused3;
+ Uint32 scanInfo;
+
+ //---------------------------------------------------
+ // Third 16 byte cache line in second 64
+ // byte cache line. Diverse use.
+ //---------------------------------------------------
+ Uint32 unused4;
+ Uint32 scanTakeOverInd;
+ UintR firstKeybuf; /* POINTER THE LINKED LIST OF KEY BUFFERS */
+ UintR lastKeybuf; /* VARIABLE POINTING TO THE LAST KEY BUFFER */
+
+ //---------------------------------------------------
+ // Fourth 16 byte cache line in second 64
+ // byte cache line. Not used currently.
+ //---------------------------------------------------
+ UintR packedCacheVar[4];
+ };
+
+ typedef Ptr<CacheRecord> CacheRecordPtr;
+
+ /* ************************ HOST RECORD ********************************** */
+ /********************************************************/
+ /* THIS RECORD CONTAINS ALIVE-STATUS ON ALL NODES IN THE*/
+ /* SYSTEM */
+ /********************************************************/
+ /* THIS RECORD IS ALIGNED TO BE 128 BYTES. */
+ /********************************************************/
+ struct HostRecord {
+ HostState hostStatus;
+ LqhTransState lqhTransStatus;
+ TakeOverState takeOverStatus;
+ bool inPackedList;
+ UintR noOfPackedWordsLqh;
+ UintR packedWordsLqh[26];
+ UintR noOfWordsTCKEYCONF;
+ UintR packedWordsTCKEYCONF[30];
+ UintR noOfWordsTCINDXCONF;
+ UintR packedWordsTCINDXCONF[30];
+ BlockReference hostLqhBlockRef;
+ }; /* p2c: size = 128 bytes */
+
+ typedef Ptr<HostRecord> HostRecordPtr;
+
+ /* *********** TABLE RECORD ********************************************* */
+
+ /********************************************************/
+ /* THIS RECORD CONTAINS THE CURRENT SCHEMA VERSION OF */
+ /* ALL TABLES IN THE SYSTEM. */
+ /********************************************************/
+ struct TableRecord {
+ Uint32 currentSchemaVersion;
+ Uint8 enabled;
+ Uint8 dropping;
+ Uint8 tableType;
+ Uint8 storedTable;
+
+ Uint8 noOfKeyAttr;
+ Uint8 hasCharAttr;
+ Uint8 noOfDistrKeys;
+
+ struct KeyAttr {
+ Uint32 attributeDescriptor;
+ CHARSET_INFO* charsetInfo;
+ } keyAttr[MAX_ATTRIBUTES_IN_INDEX];
+
+ bool checkTable(Uint32 schemaVersion) const {
+ return enabled && !dropping && (schemaVersion == currentSchemaVersion);
+ }
+
+ Uint32 getErrorCode(Uint32 schemaVersion) const;
+
+ struct DropTable {
+ Uint32 senderRef;
+ Uint32 senderData;
+ SignalCounter waitDropTabCount;
+ } dropTable;
+ };
+ typedef Ptr<TableRecord> TableRecordPtr;
+
+ /**
+ * There is max 16 ScanFragRec's for
+ * each scan started in TC. Each ScanFragRec is used by
+ * a scan fragment "process" that scans one fragment at a time.
+ * It will receive max 16 tuples in each request
+ */
+ struct ScanFragRec {
+ ScanFragRec(){
+ stopFragTimer();
+ lqhBlockref = 0;
+ scanFragState = IDLE;
+ scanRec = RNIL;
+ }
+ /**
+ * ScanFragState
+ * WAIT_GET_PRIMCONF : Waiting for DIGETPRIMCONF when starting a new
+ * fragment scan
+ * LQH_ACTIVE : The scan process has sent a command to LQH and is
+ * waiting for the response
+ * LQH_ACTIVE_CLOSE : The scan process has sent close to LQH and is
+ * waiting for the response
+ * DELIVERED : The result have been delivered, this scan frag process
+ * are waiting for a SCAN_NEXTREQ to tell us to continue scanning
+ * RETURNING_FROM_DELIVERY : SCAN_NEXTREQ received and continuing scan
+ * soon
+ * QUEUED_FOR_DELIVERY : Result queued in TC and waiting for delivery
+ * to API
+ * COMPLETED : The fragment scan processes has completed and finally
+ * sent a SCAN_PROCCONF
+ */
+ enum ScanFragState {
+ IDLE = 0,
+ WAIT_GET_PRIMCONF = 1,
+ LQH_ACTIVE = 2,
+ DELIVERED = 4,
+ QUEUED_FOR_DELIVERY = 6,
+ COMPLETED = 7
+ };
+ // Timer for checking timeout of this fragment scan
+ Uint32 scanFragTimer;
+
+ // Id of the current scanned fragment
+ Uint32 scanFragId;
+
+ // Blockreference of LQH
+ BlockReference lqhBlockref;
+
+ // getNodeInfo.m_connectCount, set at seize used so that
+ // I don't accidently kill a starting node
+ Uint32 m_connectCount;
+
+ // State of this fragment scan
+ ScanFragState scanFragState;
+
+ // Id of the ScanRecord this fragment scan belongs to
+ Uint32 scanRec;
+
+ // The value of fragmentCompleted in the last received SCAN_FRAGCONF
+ Uint8 m_scan_frag_conf_status;
+
+ inline void startFragTimer(Uint32 timeVal){
+ scanFragTimer = timeVal;
+ }
+ inline void stopFragTimer(void){
+ scanFragTimer = 0;
+ }
+
+ Uint32 m_ops;
+ Uint32 m_chksum;
+ Uint32 m_apiPtr;
+ Uint32 m_totalLen;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+
+ typedef Ptr<ScanFragRec> ScanFragRecPtr;
+ typedef LocalDLList<ScanFragRec> ScanFragList;
+
+ /**
+ * Each scan allocates one ScanRecord to store information
+ * about the current scan
+ *
+ */
+ struct ScanRecord {
+ ScanRecord() {}
+ /** NOTE! This is the old comment for ScanState. - MASV
+ * STATE TRANSITIONS OF SCAN_STATE. SCAN_STATE IS THE STATE
+ * VARIABLE OF THE RECEIVE AND DELIVERY PROCESS.
+ * THE PROCESS HAS THREE STEPS IT GOES THROUGH.
+ * 1) THE INITIAL STATES WHEN RECEIVING DATA FOR THE SCAN.
+ * - WAIT_SCAN_TAB_INFO
+ * - WAIT_AI
+ * - WAIT_FRAGMENT_COUNT
+ * 2) THE EXECUTION STATES WHEN THE SCAN IS PERFORMED.
+ * - SCAN_NEXT_ORDERED
+ * - DELIVERED
+ * - QUEUED_DELIVERED
+ * 3) THE CLOSING STATE WHEN THE SCAN PROCESS IS CLOSING UP
+ * EVERYTHING.
+ * - CLOSING_SCAN
+ * INITIAL START WHEN SCAN_TABREQ RECEIVED
+ * -> WAIT_SCAN_TAB_INFO (IF ANY SCAN_TABINFO TO BE RECEIVED)
+ * -> WAIT_AI (IF NO SCAN_TAB_INFO BUT ATTRINFO IS RECEIVED)
+ * -> WAIT_FRAGMENT_COUNT (IF NEITHER SCAN_TABINFO OR ATTRINFO
+ * RECEIVED)
+ *
+ * WAIT_SCAN_TAB_INFO TRANSITIONS:
+ * -> WAIT_SCAN_TABINFO (WHEN MORE SCAN_TABINFO RECEIVED)
+ * -> WAIT_AI (WHEN ATTRINFO RECEIVED AFTER RECEIVING ALL
+ * SCAN_TABINFO)
+ * -> WAIT_FRAGMENT_COUNT (WHEN NO ATTRINFO RECEIVED AFTER
+ * RECEIVING ALL SCAN_TABINFO )
+ * WAIT_AI TRANSITIONS:
+ * -> WAIT_AI (WHEN MORE ATTRINFO RECEIVED)
+ * -> WAIT_FRAGMENT_COUNT (WHEN ALL ATTRINFO RECEIVED)
+ *
+ * WAIT_FRAGMENT_COUNT TRANSITIONS:
+ * -> SCAN_NEXT_ORDERED
+ *
+ * SCAN_NEXT_ORDERED TRANSITIONS:
+ * -> DELIVERED (WHEN FIRST SCAN_FRAGCONF ARRIVES WITH OPERATIONS
+ * TO REPORT IN IT)
+ * -> CLOSING_SCAN (WHEN SCAN IS CLOSED BY SCAN_NEXTREQ OR BY SOME
+ * ERROR)
+ *
+ * DELIVERED TRANSITIONS:
+ * -> SCAN_NEXT_ORDERED (IF SCAN_NEXTREQ ARRIVES BEFORE ANY NEW
+ * OPERATIONS TO REPORT ARRIVES)
+ * -> QUEUED_DELIVERED (IF NEW OPERATION TO REPORT ARRIVES BEFORE
+ * SCAN_NEXTREQ)
+ * -> CLOSING_SCAN (WHEN SCAN IS CLOSED BY SCAN_NEXTREQ OR BY SOME
+ * ERROR)
+ *
+ * QUEUED_DELIVERED TRANSITIONS:
+ * -> DELIVERED (WHEN SCAN_NEXTREQ ARRIVES AND QUEUED OPERATIONS
+ * TO REPORT ARE SENT TO THE APPLICATION)
+ * -> CLOSING_SCAN (WHEN SCAN IS CLOSED BY SCAN_NEXTREQ OR BY
+ * SOME ERROR)
+ */
+ enum ScanState {
+ IDLE = 0,
+ WAIT_SCAN_TAB_INFO = 1,
+ WAIT_AI = 2,
+ WAIT_FRAGMENT_COUNT = 3,
+ RUNNING = 4,
+ CLOSING_SCAN = 5
+ };
+
+ // State of this scan
+ ScanState scanState;
+
+ DLList<ScanFragRec>::Head m_running_scan_frags; // Currently in LQH
+ union { Uint32 m_queued_count; Uint32 scanReceivedOperations; };
+ DLList<ScanFragRec>::Head m_queued_scan_frags; // In TC !sent to API
+ DLList<ScanFragRec>::Head m_delivered_scan_frags;// Delivered to API
+
+ // Id of the next fragment to be scanned. Used by scan fragment
+ // processes when they are ready for the next fragment
+ Uint32 scanNextFragId;
+
+ // Total number of fragments in the table we are scanning
+ Uint32 scanNoFrag;
+
+ // Index of next ScanRecords when in free list
+ Uint32 nextScan;
+
+ // Length of expected attribute information
+ union { Uint32 scanAiLength; Uint32 m_booked_fragments_count; };
+
+ Uint32 scanKeyLen;
+
+ // Reference to ApiConnectRecord
+ Uint32 scanApiRec;
+
+ // Reference to TcConnectRecord
+ Uint32 scanTcrec;
+
+ // Number of scan frag processes that belong to this scan
+ Uint32 scanParallel;
+
+ // Schema version used by this scan
+ Uint32 scanSchemaVersion;
+
+ // Index of stored procedure belonging to this scan
+ Uint32 scanStoredProcId;
+
+ // The index of table that is scanned
+ Uint32 scanTableref;
+
+ // Number of operation records per scanned fragment
+ // Number of operations in first batch
+ // Max number of bytes per batch
+ union {
+ Uint16 first_batch_size_rows;
+ Uint16 batch_size_rows;
+ };
+ Uint32 batch_byte_size;
+
+ Uint32 scanRequestInfo; // ScanFrag format
+
+ // Close is ordered
+ bool m_close_scan_req;
+ };
+ typedef Ptr<ScanRecord> ScanRecordPtr;
+
+ /* **********************************************************************$ */
+ /* ******$ DATA BUFFER ******$ */
+ /* */
+ /* THIS BUFFER IS USED AS A GENERAL DATA STORAGE. */
+ /* **********************************************************************$ */
+ struct DatabufRecord {
+ UintR data[4];
+ /* 4 * 1 WORD = 4 WORD */
+ UintR nextDatabuf;
+ }; /* p2c: size = 20 bytes */
+
+ typedef Ptr<DatabufRecord> DatabufRecordPtr;
+
+ /* **********************************************************************$ */
+ /* ******$ ATTRIBUTE INFORMATION RECORD ******$ */
+ /*
+ * CAN CONTAIN ONE (1) ATTRINFO SIGNAL. ONE SIGNAL CONTAINS 24 ATTR.
+ * INFO WORDS. BUT 32 ELEMENTS ARE USED TO MAKE PLEX HAPPY.
+ * SOME OF THE ELEMENTS ARE USED TO THE FOLLOWING THINGS:
+ * DATA LENGHT IN THIS RECORD IS STORED IN THE ELEMENT INDEXED BY
+ * ZINBUF_DATA_LEN.
+ * NEXT FREE ATTRBUF IS POINTED OUT BY THE ELEMENT INDEXED BY
+ * PREVIOUS ATTRBUF IS POINTED OUT BY THE ELEMENT INDEXED BY ZINBUF_PREV
+ * (NOT USED YET).
+ * NEXT ATTRBUF IS POINTED OUT BY THE ELEMENT INDEXED BY ZINBUF_NEXT. */
+ /* ******************************************************************** */
+ struct AttrbufRecord {
+ UintR attrbuf[32];
+ }; /* p2c: size = 128 bytes */
+
+ typedef Ptr<AttrbufRecord> AttrbufRecordPtr;
+
+ /*************************************************************************>*/
+ /* GLOBAL CHECKPOINT INFORMATION RECORD */
+ /* */
+ /* THIS RECORD IS USED TO STORE THE GLOBALCHECKPOINT NUMBER AND A
+ * COUNTER DURING THE COMPLETION PHASE OF THE TRANSACTION */
+ /*************************************************************************>*/
+ /* */
+ /* GCP RECORD ALIGNED TO BE 32 BYTES */
+ /*************************************************************************>*/
+ struct GcpRecord {
+ UintR gcpUnused1[2]; /* p2c: Not used */
+ UintR firstApiConnect;
+ UintR lastApiConnect;
+ UintR gcpId;
+ UintR nextGcp;
+ UintR gcpUnused2; /* p2c: Not used */
+ Uint16 gcpNomoretransRec;
+ }; /* p2c: size = 32 bytes */
+
+ typedef Ptr<GcpRecord> GcpRecordPtr;
+
+ /*************************************************************************>*/
+ /* TC_FAIL_RECORD */
+ /* THIS RECORD IS USED WHEN HANDLING TAKE OVER OF ANOTHER FAILED
+ * TC NODE. */
+ /*************************************************************************>*/
+ struct TcFailRecord {
+ Uint16 queueList[MAX_NDB_NODES];
+ Uint8 takeOverProcState[MAX_NDB_NODES];
+ UintR completedTakeOver;
+ UintR currentHashIndexTakeOver;
+ FailState failStatus;
+ Uint16 queueIndex;
+ Uint16 takeOverNode;
+ }; /* p2c: size = 64 bytes */
+
+ typedef Ptr<TcFailRecord> TcFailRecordPtr;
+
+public:
+ Dbtc(const class Configuration &);
+ virtual ~Dbtc();
+
+private:
+ BLOCK_DEFINES(Dbtc);
+
+ // Transit signals
+ void execPACKED_SIGNAL(Signal* signal);
+ void execABORTED(Signal* signal);
+ void execATTRINFO(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+ void execKEYINFO(Signal* signal);
+ void execSCAN_NEXTREQ(Signal* signal);
+ void execSCAN_PROCREQ(Signal* signal);
+ void execSCAN_PROCCONF(Signal* signal);
+ void execTAKE_OVERTCREQ(Signal* signal);
+ void execTAKE_OVERTCCONF(Signal* signal);
+ void execLQHKEYREF(Signal* signal);
+ void execTRANSID_AI_R(Signal* signal);
+ void execKEYINFO20_R(Signal* signal);
+
+ // Received signals
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execSEND_PACKED(Signal* signal);
+ void execCOMPLETED(Signal* signal);
+ void execCOMMITTED(Signal* signal);
+ void execDIGETNODESREF(Signal* signal);
+ void execDIGETPRIMCONF(Signal* signal);
+ void execDIGETPRIMREF(Signal* signal);
+ void execDISEIZECONF(Signal* signal);
+ void execDIVERIFYCONF(Signal* signal);
+ void execDI_FCOUNTCONF(Signal* signal);
+ void execDI_FCOUNTREF(Signal* signal);
+ void execGCP_NOMORETRANS(Signal* signal);
+ void execLQHKEYCONF(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+ void execREAD_NODESCONF(Signal* signal);
+ void execREAD_NODESREF(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execTC_COMMITREQ(Signal* signal);
+ void execTC_CLOPSIZEREQ(Signal* signal);
+ void execTCGETOPSIZEREQ(Signal* signal);
+ void execTCKEYREQ(Signal* signal);
+ void execTCRELEASEREQ(Signal* signal);
+ void execTCSEIZEREQ(Signal* signal);
+ void execTCROLLBACKREQ(Signal* signal);
+ void execTC_HBREP(Signal* signal);
+ void execTC_SCHVERREQ(Signal* signal);
+ void execSCAN_TABREQ(Signal* signal);
+ void execSCAN_TABINFO(Signal* signal);
+ void execSCAN_FRAGCONF(Signal* signal);
+ void execSCAN_FRAGREF(Signal* signal);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ void execLQH_TRANSCONF(Signal* signal);
+ void execCOMPLETECONF(Signal* signal);
+ void execCOMMITCONF(Signal* signal);
+ void execABORTCONF(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execINCL_NODEREQ(Signal* signal);
+ void execTIME_SIGNAL(Signal* signal);
+ void execAPI_FAILREQ(Signal* signal);
+ void execSCAN_HBREP(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+
+ void execABORT_ALL_REQ(Signal* signal);
+
+ void execCREATE_TRIG_REQ(Signal* signal);
+ void execDROP_TRIG_REQ(Signal* signal);
+ void execFIRE_TRIG_ORD(Signal* signal);
+ void execTRIG_ATTRINFO(Signal* signal);
+ void execCREATE_INDX_REQ(Signal* signal);
+ void execDROP_INDX_REQ(Signal* signal);
+ void execTCINDXREQ(Signal* signal);
+ void execINDXKEYINFO(Signal* signal);
+ void execINDXATTRINFO(Signal* signal);
+ void execALTER_INDX_REQ(Signal* signal);
+
+ // Index table lookup
+ void execTCKEYCONF(Signal* signal);
+ void execTCKEYREF(Signal* signal);
+ void execTRANSID_AI(Signal* signal);
+ void execTCROLLBACKREP(Signal* signal);
+
+ void execCREATE_TAB_REQ(Signal* signal);
+ void execPREP_DROP_TAB_REQ(Signal* signal);
+ void execDROP_TAB_REQ(Signal* signal);
+ void execWAIT_DROP_TAB_REF(Signal* signal);
+ void execWAIT_DROP_TAB_CONF(Signal* signal);
+ void checkWaitDropTabFailedLqh(Signal*, Uint32 nodeId, Uint32 tableId);
+ void execALTER_TAB_REQ(Signal* signal);
+ void set_timeout_value(Uint32 timeOut);
+ void set_appl_timeout_value(Uint32 timeOut);
+ void set_no_parallel_takeover(Uint32);
+ void updateBuddyTimer(ApiConnectRecordPtr);
+
+ // Statement blocks
+ void updatePackedList(Signal* signal, HostRecord* ahostptr,
+ Uint16 ahostIndex);
+ void clearTcNodeData(Signal* signal,
+ UintR TLastLqhIndicator,
+ UintR Tstart);
+ void errorReport(Signal* signal, int place);
+ void warningReport(Signal* signal, int place);
+ void printState(Signal* signal, int place);
+ int seizeTcRecord(Signal* signal);
+ int seizeCacheRecord(Signal* signal);
+ void TCKEY_abort(Signal* signal, int place);
+ void copyFromToLen(UintR* sourceBuffer, UintR* destBuffer, UintR copyLen);
+ void reportNodeFailed(Signal* signal, Uint32 nodeId);
+ void sendPackedTCKEYCONF(Signal* signal,
+ HostRecord * ahostptr,
+ UintR hostId);
+ void sendPackedTCINDXCONF(Signal* signal,
+ HostRecord * ahostptr,
+ UintR hostId);
+ void sendPackedSignalLqh(Signal* signal, HostRecord * ahostptr);
+ void sendCommitLqh(Signal* signal,
+ TcConnectRecord * const regTcPtr);
+ void sendCompleteLqh(Signal* signal,
+ TcConnectRecord * const regTcPtr);
+ void sendTCKEY_FAILREF(Signal* signal, const ApiConnectRecord *);
+ void sendTCKEY_FAILCONF(Signal* signal, ApiConnectRecord *);
+ void checkStartTimeout(Signal* signal);
+ void checkStartFragTimeout(Signal* signal);
+ void timeOutFoundFragLab(Signal* signal, Uint32 TscanConPtr);
+ void timeOutLoopStartFragLab(Signal* signal, Uint32 TscanConPtr);
+ int releaseAndAbort(Signal* signal);
+ void findApiConnectFail(Signal* signal);
+ void findTcConnectFail(Signal* signal);
+ void initApiConnectFail(Signal* signal);
+ void initTcConnectFail(Signal* signal);
+ void initTcFail(Signal* signal);
+ void releaseTakeOver(Signal* signal);
+ void setupFailData(Signal* signal);
+ void updateApiStateFail(Signal* signal);
+ void updateTcStateFail(Signal* signal);
+ void handleApiFailState(Signal* signal, UintR anApiConnectptr);
+ void handleFailedApiNode(Signal* signal,
+ UintR aFailedNode,
+ UintR anApiConnectPtr);
+ void handleScanStop(Signal* signal, UintR aFailedNode);
+ void initScanTcrec(Signal* signal);
+ void initScanrec(ScanRecordPtr, const class ScanTabReq*,
+ const UintR scanParallel,
+ const UintR noOprecPerFrag);
+ void initScanfragrec(Signal* signal);
+ void releaseScanResources(ScanRecordPtr);
+ ScanRecordPtr seizeScanrec(Signal* signal);
+ void sendScanFragReq(Signal*, ScanRecord*, ScanFragRec*);
+ void sendScanTabConf(Signal* signal, ScanRecordPtr);
+ void close_scan_req(Signal*, ScanRecordPtr, bool received_req);
+ void close_scan_req_send_conf(Signal*, ScanRecordPtr);
+
+ void checkGcp(Signal* signal);
+ void commitGciHandling(Signal* signal, UintR Tgci);
+ void copyApi(Signal* signal);
+ void DIVER_node_fail_handling(Signal* signal, UintR Tgci);
+ void gcpTcfinished(Signal* signal);
+ void handleGcp(Signal* signal);
+ void hash(Signal* signal);
+ bool handle_special_hash(Uint32 dstHash[4],
+ Uint32* src, Uint32 srcLen,
+ Uint32 tabPtrI, bool distr);
+
+ void initApiConnect(Signal* signal);
+ void initApiConnectRec(Signal* signal,
+ ApiConnectRecord * const regApiPtr,
+ bool releaseIndexOperations = false);
+ void initattrbuf(Signal* signal);
+ void initdatabuf(Signal* signal);
+ void initgcp(Signal* signal);
+ void inithost(Signal* signal);
+ void initialiseScanrec(Signal* signal);
+ void initialiseScanFragrec(Signal* signal);
+ void initialiseScanOprec(Signal* signal);
+ void initTable(Signal* signal);
+ void initialiseTcConnect(Signal* signal);
+ void linkApiToGcp(Signal* signal);
+ void linkGciInGcilist(Signal* signal);
+ void linkKeybuf(Signal* signal);
+ void linkTcInConnectionlist(Signal* signal);
+ void releaseAbortResources(Signal* signal);
+ void releaseApiCon(Signal* signal, UintR aApiConnectPtr);
+ void releaseApiConCopy(Signal* signal);
+ void releaseApiConnectFail(Signal* signal);
+ void releaseAttrinfo();
+ void releaseGcp(Signal* signal);
+ void releaseKeys();
+ void releaseSimpleRead(Signal*, ApiConnectRecordPtr, TcConnectRecord*);
+ void releaseDirtyWrite(Signal* signal);
+ void releaseTcCon();
+ void releaseTcConnectFail(Signal* signal);
+ void releaseTransResources(Signal* signal);
+ void saveAttrbuf(Signal* signal);
+ void seizeApiConnect(Signal* signal);
+ void seizeApiConnectCopy(Signal* signal);
+ void seizeApiConnectFail(Signal* signal);
+ void seizeDatabuf(Signal* signal);
+ void seizeGcp(Signal* signal);
+ void seizeTcConnect(Signal* signal);
+ void seizeTcConnectFail(Signal* signal);
+ void sendApiCommit(Signal* signal);
+ void sendAttrinfo(Signal* signal,
+ UintR TattrinfoPtr,
+ AttrbufRecord * const regAttrPtr,
+ UintR TBref);
+ void sendContinueTimeOutControl(Signal* signal, Uint32 TapiConPtr);
+ void sendKeyinfo(Signal* signal, BlockReference TBRef, Uint32 len);
+ void sendlqhkeyreq(Signal* signal, BlockReference TBRef);
+ void sendSystemError(Signal* signal);
+ void sendtckeyconf(Signal* signal, UintR TcommitFlag);
+ void sendTcIndxConf(Signal* signal, UintR TcommitFlag);
+ void unlinkApiConnect(Signal* signal);
+ void unlinkGcp(Signal* signal);
+ void unlinkReadyTcCon(Signal* signal);
+ void handleFailedOperation(Signal* signal,
+ const LqhKeyRef * const lqhKeyRef,
+ bool gotLqhKeyRef);
+ void markOperationAborted(ApiConnectRecord * const regApiPtr,
+ TcConnectRecord * const regTcPtr);
+ void clearCommitAckMarker(ApiConnectRecord * const regApiPtr,
+ TcConnectRecord * const regTcPtr);
+ // Trigger and index handling
+ bool saveINDXKEYINFO(Signal* signal,
+ TcIndexOperation* indexOp,
+ const Uint32 *src,
+ Uint32 len);
+ bool receivedAllINDXKEYINFO(TcIndexOperation* indexOp);
+ bool saveINDXATTRINFO(Signal* signal,
+ TcIndexOperation* indexOp,
+ const Uint32 *src,
+ Uint32 len);
+ bool receivedAllINDXATTRINFO(TcIndexOperation* indexOp);
+ bool saveTRANSID_AI(Signal* signal,
+ TcIndexOperation* indexOp,
+ const Uint32 *src,
+ Uint32 len);
+ bool receivedAllTRANSID_AI(TcIndexOperation* indexOp);
+ void readIndexTable(Signal* signal,
+ ApiConnectRecord* regApiPtr,
+ TcIndexOperation* indexOp);
+ void executeIndexOperation(Signal* signal,
+ ApiConnectRecord* regApiPtr,
+ TcIndexOperation* indexOp);
+ bool seizeIndexOperation(ApiConnectRecord* regApiPtr,
+ TcIndexOperationPtr& indexOpPtr);
+ void releaseIndexOperation(ApiConnectRecord* regApiPtr,
+ TcIndexOperation* indexOp);
+ void releaseAllSeizedIndexOperations(ApiConnectRecord* regApiPtr);
+ void setupIndexOpReturn(ApiConnectRecord* regApiPtr,
+ TcConnectRecord* regTcPtr);
+
+ void saveTriggeringOpState(Signal* signal,
+ TcConnectRecord* trigOp);
+ void restoreTriggeringOpState(Signal* signal,
+ TcConnectRecord* trigOp);
+ void continueTriggeringOp(Signal* signal,
+ TcConnectRecord* trigOp);
+
+ void scheduleFiredTrigger(ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr);
+ void executeTriggers(Signal* signal, ApiConnectRecordPtr* transPtr);
+ void executeTrigger(Signal* signal,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr);
+ void executeIndexTrigger(Signal* signal,
+ TcDefinedTriggerData* definedTriggerData,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr);
+ void insertIntoIndexTable(Signal* signal,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr,
+ TcIndexData* indexData,
+ bool holdOperation = false);
+ void deleteFromIndexTable(Signal* signal,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr,
+ TcIndexData* indexData,
+ bool holdOperation = false);
+ void releaseFiredTriggerData(DLFifoList<TcFiredTriggerData>* triggers);
+ // Generated statement blocks
+ void warningHandlerLab(Signal* signal);
+ void systemErrorLab(Signal* signal);
+ void sendSignalErrorRefuseLab(Signal* signal);
+ void scanTabRefLab(Signal* signal, Uint32 errCode);
+ void diFcountReqLab(Signal* signal, ScanRecordPtr);
+ void signalErrorRefuseLab(Signal* signal);
+ void abort080Lab(Signal* signal);
+ void packKeyData000Lab(Signal* signal, BlockReference TBRef, Uint32 len);
+ void abortScanLab(Signal* signal, ScanRecordPtr, Uint32 errCode);
+ void sendAbortedAfterTimeout(Signal* signal, int Tcheck);
+ void abort010Lab(Signal* signal);
+ void abort015Lab(Signal* signal);
+ void packLqhkeyreq(Signal* signal, BlockReference TBRef);
+ void packLqhkeyreq040Lab(Signal* signal,
+ UintR anAttrBufIndex,
+ BlockReference TBRef);
+ void packLqhkeyreq040Lab(Signal* signal);
+ void returnFromQueuedDeliveryLab(Signal* signal);
+ void startTakeOverLab(Signal* signal);
+ void toCompleteHandlingLab(Signal* signal);
+ void toCommitHandlingLab(Signal* signal);
+ void toAbortHandlingLab(Signal* signal);
+ void abortErrorLab(Signal* signal);
+ void nodeTakeOverCompletedLab(Signal* signal);
+ void ndbsttorry010Lab(Signal* signal);
+ void commit020Lab(Signal* signal);
+ void complete010Lab(Signal* signal);
+ void releaseAtErrorLab(Signal* signal);
+ void seizeDatabuferrorLab(Signal* signal);
+ void scanAttrinfoLab(Signal* signal, UintR Tlen);
+ void seizeAttrbuferrorLab(Signal* signal);
+ void attrinfoDihReceivedLab(Signal* signal);
+ void aiErrorLab(Signal* signal);
+ void attrinfo020Lab(Signal* signal);
+ void scanReleaseResourcesLab(Signal* signal);
+ void scanCompletedLab(Signal* signal);
+ void scanError(Signal* signal, ScanRecordPtr, Uint32 errorCode);
+ void diverify010Lab(Signal* signal);
+ void intstartphase2x010Lab(Signal* signal);
+ void intstartphase3x010Lab(Signal* signal);
+ void sttorryLab(Signal* signal);
+ void abortBeginErrorLab(Signal* signal);
+ void tabStateErrorLab(Signal* signal);
+ void wrongSchemaVersionErrorLab(Signal* signal);
+ void noFreeConnectionErrorLab(Signal* signal);
+ void tckeyreq050Lab(Signal* signal);
+ void timeOutFoundLab(Signal* signal, UintR anAdd);
+ void completeTransAtTakeOverLab(Signal* signal, UintR TtakeOverInd);
+ void completeTransAtTakeOverDoLast(Signal* signal, UintR TtakeOverInd);
+ void completeTransAtTakeOverDoOne(Signal* signal, UintR TtakeOverInd);
+ void timeOutLoopStartLab(Signal* signal, Uint32 apiConnectPtr);
+ void initialiseRecordsLab(Signal* signal, UintR Tdata0, Uint32, Uint32);
+ void tckeyreq020Lab(Signal* signal);
+ void intstartphase2x020Lab(Signal* signal);
+ void intstartphase1x010Lab(Signal* signal);
+ void startphase1x010Lab(Signal* signal);
+
+ void lqhKeyConf_checkTransactionState(Signal * signal,
+ ApiConnectRecord * const regApiPtr);
+
+ void checkDropTab(Signal* signal);
+
+ void checkScanActiveInFailedLqh(Signal* signal,
+ Uint32 scanPtrI,
+ Uint32 failedNodeId);
+ void checkScanFragList(Signal*, Uint32 failedNodeId, ScanRecord * scanP,
+ LocalDLList<ScanFragRec>::Head&);
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ // Transit signals
+
+
+ ApiConnectRecord *apiConnectRecord;
+ ApiConnectRecordPtr apiConnectptr;
+ UintR capiConnectFilesize;
+
+ TcConnectRecord *tcConnectRecord;
+ TcConnectRecordPtr tcConnectptr;
+ UintR ctcConnectFilesize;
+
+ CacheRecord *cacheRecord;
+ CacheRecordPtr cachePtr;
+ UintR ccacheFilesize;
+
+ AttrbufRecord *attrbufRecord;
+ AttrbufRecordPtr attrbufptr;
+ UintR cattrbufFilesize;
+
+ HostRecord *hostRecord;
+ HostRecordPtr hostptr;
+ UintR chostFilesize;
+
+ GcpRecord *gcpRecord;
+ GcpRecordPtr gcpPtr;
+ UintR cgcpFilesize;
+
+ TableRecord *tableRecord;
+ UintR ctabrecFilesize;
+
+ UintR thashValue;
+ UintR tdistrHashValue;
+
+ UintR ttransid_ptr;
+ UintR cfailure_nr;
+ UintR coperationsize;
+ UintR ctcTimer;
+
+ ApiConnectRecordPtr tmpApiConnectptr;
+ UintR tcheckGcpId;
+
+ struct TransCounters {
+ enum { Off, Timer, Started } c_trans_status;
+ UintR cattrinfoCount;
+ UintR ctransCount;
+ UintR ccommitCount;
+ UintR creadCount;
+ UintR csimpleReadCount;
+ UintR cwriteCount;
+ UintR cabortCount;
+ UintR cconcurrentOp;
+ Uint32 c_scan_count;
+ Uint32 c_range_scan_count;
+ void reset () {
+ cattrinfoCount = ctransCount = ccommitCount = creadCount =
+ csimpleReadCount = cwriteCount = cabortCount =
+ c_scan_count = c_range_scan_count = 0;
+ }
+ Uint32 report(Signal* signal){
+ signal->theData[0] = NDB_LE_TransReportCounters;
+ signal->theData[1] = ctransCount;
+ signal->theData[2] = ccommitCount;
+ signal->theData[3] = creadCount;
+ signal->theData[4] = csimpleReadCount;
+ signal->theData[5] = cwriteCount;
+ signal->theData[6] = cattrinfoCount;
+ signal->theData[7] = cconcurrentOp;
+ signal->theData[8] = cabortCount;
+ signal->theData[9] = c_scan_count;
+ signal->theData[10] = c_range_scan_count;
+ return 11;
+ }
+ } c_counters;
+
+ Uint16 cownNodeid;
+ Uint16 terrorCode;
+
+ UintR cfirstfreeAttrbuf;
+ UintR cfirstfreeTcConnect;
+ UintR cfirstfreeApiConnectCopy;
+ UintR cfirstfreeCacheRec;
+
+ UintR cfirstgcp;
+ UintR clastgcp;
+ UintR cfirstfreeGcp;
+ UintR cfirstfreeScanrec;
+
+ TableRecordPtr tabptr;
+ UintR cfirstfreeApiConnectFail;
+ UintR cfirstfreeApiConnect;
+
+ UintR cfirstfreeDatabuf;
+ BlockReference cdihblockref;
+ BlockReference cownref; /* OWN BLOCK REFERENCE */
+
+ ApiConnectRecordPtr timeOutptr;
+
+ ScanRecord *scanRecord;
+ UintR cscanrecFileSize;
+
+ UnsafeArrayPool<ScanFragRec> c_scan_frag_pool;
+ ScanFragRecPtr scanFragptr;
+
+ UintR cscanFragrecFileSize;
+ UintR cdatabufFilesize;
+
+ BlockReference cdictblockref;
+ BlockReference cerrorBlockref;
+ BlockReference clqhblockref;
+ BlockReference cndbcntrblockref;
+
+ Uint16 csignalKey;
+ Uint16 csystemnodes;
+ Uint16 cnodes[4];
+ NodeId cmasterNodeId;
+ UintR cnoParallelTakeOver;
+ TimeOutCheckState ctimeOutCheckFragActive;
+
+ UintR ctimeOutCheckFragCounter;
+ UintR ctimeOutCheckCounter;
+ UintR ctimeOutValue;
+ UintR ctimeOutCheckDelay;
+ Uint32 ctimeOutCheckHeartbeat;
+ Uint32 ctimeOutCheckLastHeartbeat;
+ Uint32 ctimeOutMissedHeartbeats;
+ Uint32 c_appl_timeout_value;
+
+ SystemStartState csystemStart;
+ TimeOutCheckState ctimeOutCheckActive;
+
+ BlockReference capiFailRef;
+ UintR cpackedListIndex;
+ Uint16 cpackedList[MAX_NODES];
+ UintR capiConnectClosing[MAX_NODES];
+ UintR con_lineNodes;
+
+ DatabufRecord *databufRecord;
+ DatabufRecordPtr databufptr;
+ DatabufRecordPtr tmpDatabufptr;
+
+ UintR treqinfo;
+ UintR ttransid1;
+ UintR ttransid2;
+
+ UintR tabortInd;
+
+ NodeId tnodeid;
+ BlockReference tblockref;
+
+ LqhTransConf::OperationStatus ttransStatus;
+ UintR ttcOprec;
+ NodeId tfailedNodeId;
+ Uint8 tcurrentReplicaNo;
+ Uint8 tpad1;
+
+ UintR tgci;
+ UintR tapplRef;
+ UintR tapplOprec;
+
+ UintR tindex;
+ UintR tmaxData;
+ UintR tmp;
+
+ UintR tnodes;
+ BlockReference tusersblkref;
+ UintR tuserpointer;
+ UintR tloadCode;
+
+ UintR tconfig1;
+ UintR tconfig2;
+
+ UintR cdata[32];
+ UintR ctransidFailHash[512];
+ UintR ctcConnectFailHash[1024];
+
+ /**
+ * Commit Ack handling
+ */
+public:
+ struct CommitAckMarker {
+ Uint32 transid1;
+ Uint32 transid2;
+ union { Uint32 nextPool; Uint32 nextHash; };
+ Uint32 prevHash;
+ Uint32 apiConnectPtr;
+ Uint16 apiNodeId;
+ Uint16 noOfLqhs;
+ Uint16 lqhNodeId[MAX_REPLICAS];
+
+ inline bool equal(const CommitAckMarker & p) const {
+ return ((p.transid1 == transid1) && (p.transid2 == transid2));
+ }
+
+ inline Uint32 hashValue() const {
+ return transid1;
+ }
+ };
+private:
+ typedef Ptr<CommitAckMarker> CommitAckMarkerPtr;
+ typedef DLHashTable<CommitAckMarker>::Iterator CommitAckMarkerIterator;
+
+ ArrayPool<CommitAckMarker> m_commitAckMarkerPool;
+ DLHashTable<CommitAckMarker> m_commitAckMarkerHash;
+
+ void execTC_COMMIT_ACK(Signal* signal);
+ void sendRemoveMarkers(Signal*, const CommitAckMarker *);
+ void sendRemoveMarker(Signal* signal,
+ NodeId nodeId,
+ Uint32 transid1,
+ Uint32 transid2);
+ void removeMarkerForFailedAPI(Signal* signal, Uint32 nodeId, Uint32 bucket);
+
+ bool getAllowStartTransaction() const {
+ if(getNodeState().getSingleUserMode())
+ return true;
+ return getNodeState().startLevel < NodeState::SL_STOPPING_2;
+ }
+
+ void checkAbortAllTimeout(Signal* signal, Uint32 sleepTime);
+ struct AbortAllRecord {
+ AbortAllRecord(){ clientRef = 0; }
+ Uint32 clientData;
+ BlockReference clientRef;
+
+ Uint32 oldTimeOutValue;
+ };
+ AbortAllRecord c_abortRec;
+
+ /************************** API CONNECT RECORD ***********************/
+ /* *******************************************************************/
+ /* THE API CONNECT RECORD CONTAINS THE CONNECTION RECORD TO WHICH THE*/
+ /* APPLICATION CONNECTS. THE APPLICATION CAN SEND ONE OPERATION AT A */
+ /* TIME. IT CAN SEND A NEW OPERATION IMMEDIATELY AFTER SENDING THE */
+ /* PREVIOUS OPERATION. THEREBY SEVERAL OPERATIONS CAN BE ACTIVE IN */
+ /* ONE TRANSACTION WITHIN TC. THIS IS ACHIEVED BY USING THE API */
+ /* CONNECT RECORD. EACH ACTIVE OPERATION IS HANDLED BY THE TC */
+ /* CONNECT RECORD. AS SOON AS THE TC CONNECT RECORD HAS SENT THE */
+ /* REQUEST TO THE LQH IT IS READY TO RECEIVE NEW OPERATIONS. THE */
+ /* LQH CONNECT RECORD TAKES CARE OF WAITING FOR AN OPERATION TO */
+ /* COMPLETE. WHEN AN OPERATION HAS COMPLETED ON THE LQH CONNECT */
+ /* RECORD A NEW OPERATION CAN BE STARTED ON THIS LQH CONNECT RECORD. */
+ /*******************************************************************>*/
+ /* */
+ /* API CONNECT RECORD ALIGNED TO BE 256 BYTES */
+ /*******************************************************************>*/
+ /************************** TC CONNECT RECORD ************************/
+ /* *******************************************************************/
+ /* TC CONNECT RECORD KEEPS ALL INFORMATION TO CARRY OUT A TRANSACTION*/
+ /* THE TRANSACTION CONTROLLER ESTABLISHES CONNECTIONS TO DIFFERENT */
+ /* BLOCKS TO CARRY OUT THE TRANSACTION. THERE CAN BE SEVERAL RECORDS */
+ /* PER ACTIVE TRANSACTION. THE TC CONNECT RECORD COOPERATES WITH THE */
+ /* API CONNECT RECORD FOR COMMUNICATION WITH THE API AND WITH THE */
+ /* LQH CONNECT RECORD FOR COMMUNICATION WITH THE LQH'S INVOLVED IN */
+ /* THE TRANSACTION. TC CONNECT RECORD IS PERMANENTLY CONNECTED TO A */
+ /* RECORD IN DICT AND ONE IN DIH. IT CONTAINS A LIST OF ACTIVE LQH */
+ /* CONNECT RECORDS AND A LIST OF STARTED BUT NOT ACTIVE LQH CONNECT */
+ /* RECORDS. IT DOES ALSO CONTAIN A LIST OF ALL OPERATIONS THAT ARE */
+ /* EXECUTED WITH THE TC CONNECT RECORD. */
+ /*******************************************************************>*/
+ /* TC_CONNECT RECORD ALIGNED TO BE 128 BYTES */
+ /*******************************************************************>*/
+ UintR cfirstfreeTcConnectFail;
+
+ /* POINTER FOR THE LQH RECORD*/
+ /* ************************ HOST RECORD ********************************* */
+ /********************************************************/
+ /* THIS RECORD CONTAINS ALIVE-STATUS ON ALL NODES IN THE*/
+ /* SYSTEM */
+ /********************************************************/
+ /* THIS RECORD IS ALIGNED TO BE 8 BYTES. */
+ /********************************************************/
+ /* ************************ TABLE RECORD ******************************** */
+ /********************************************************/
+ /* THIS RECORD CONTAINS THE CURRENT SCHEMA VERSION OF */
+ /* ALL TABLES IN THE SYSTEM. */
+ /********************************************************/
+ /*-------------------------------------------------------------------------*/
+ /* THE TC CONNECTION USED BY THIS SCAN. */
+ /*-------------------------------------------------------------------------*/
+ /*-------------------------------------------------------------------------*/
+ /* LENGTH READ FOR A PARTICULAR SCANNED OPERATION. */
+ /*-------------------------------------------------------------------------*/
+ /*-------------------------------------------------------------------------*/
+ /* REFERENCE TO THE SCAN RECORD FOR THIS SCAN PROCESS. */
+ /*-------------------------------------------------------------------------*/
+ /* *********************************************************************** */
+ /* ******$ DATA BUFFER ******$ */
+ /* */
+ /* THIS BUFFER IS USED AS A GENERAL DATA STORAGE. */
+ /* *********************************************************************** */
+ /* *********************************************************************** */
+ /* ******$ ATTRIBUTE INFORMATION RECORD ******$ */
+ /*
+ CAN CONTAIN ONE (1) ATTRINFO SIGNAL. ONE SIGNAL CONTAINS 24 ATTR.
+ INFO WORDS. BUT 32 ELEMENTS ARE USED TO MAKE PLEX HAPPY.
+ SOME OF THE ELEMENTS ARE USED TO THE FOLLOWING THINGS:
+ DATA LENGHT IN THIS RECORD IS STORED IN THE ELEMENT INDEXED BY
+ ZINBUF_DATA_LEN.
+ NEXT FREE ATTRBUF IS POINTED OUT BY THE ELEMENT INDEXED BY
+ PREVIOUS ATTRBUF IS POINTED OUT BY THE ELEMENT INDEXED BY ZINBUF_PREV
+ (NOT USED YET).
+ NEXT ATTRBUF IS POINTED OUT BY THE ELEMENT INDEXED BY ZINBUF_NEXT.
+ */
+ /* ********************************************************************** */
+ /**************************************************************************/
+ /* GLOBAL CHECKPOINT INFORMATION RECORD */
+ /* */
+ /* THIS RECORD IS USED TO STORE THE GCP NUMBER AND A COUNTER */
+ /* DURING THE COMPLETION PHASE OF THE TRANSACTION */
+ /**************************************************************************/
+ /* */
+ /* GCP RECORD ALIGNED TO BE 32 BYTES */
+ /**************************************************************************/
+ /**************************************************************************/
+ /* TC_FAIL_RECORD */
+ /* THIS RECORD IS USED WHEN HANDLING TAKE OVER OF ANOTHER FAILED TC NODE.*/
+ /**************************************************************************/
+ TcFailRecord *tcFailRecord;
+ TcFailRecordPtr tcNodeFailptr;
+ /**************************************************************************/
+ // Temporary variables that are not allowed to use for storage between
+ // signals. They
+ // can only be used in a signal to transfer values between subroutines.
+ // In the long run
+ // those variables should be removed and exchanged for stack
+ // variable communication.
+ /**************************************************************************/
+};
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp b/storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp
new file mode 100644
index 00000000000..59c8237f20a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp
@@ -0,0 +1,368 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTC_C
+#include "Dbtc.hpp"
+#include <pc.hpp>
+#include <ndb_limits.h>
+#include <Properties.hpp>
+#include <Configuration.hpp>
+
+#define DEBUG(x) { ndbout << "TC::" << x << endl; }
+
+
+void Dbtc::initData()
+{
+ cattrbufFilesize = ZATTRBUF_FILESIZE;
+ capiConnectFilesize = ZAPI_CONNECT_FILESIZE;
+ ccacheFilesize = ZAPI_CONNECT_FILESIZE;
+ chostFilesize = MAX_NODES;
+ cdatabufFilesize = ZDATABUF_FILESIZE;
+ cgcpFilesize = ZGCP_FILESIZE;
+ cscanrecFileSize = ZSCANREC_FILE_SIZE;
+ cscanFragrecFileSize = ZSCAN_FRAGREC_FILE_SIZE;
+ ctabrecFilesize = ZTABREC_FILESIZE;
+ ctcConnectFilesize = ZTC_CONNECT_FILESIZE;
+ cdihblockref = DBDIH_REF;
+ cdictblockref = DBDICT_REF;
+ clqhblockref = DBLQH_REF;
+ cerrorBlockref = NDBCNTR_REF;
+
+ cacheRecord = 0;
+ apiConnectRecord = 0;
+ tcConnectRecord = 0;
+ hostRecord = 0;
+ tableRecord = 0;
+ scanRecord = 0;
+ databufRecord = 0;
+ attrbufRecord = 0;
+ gcpRecord = 0;
+ tcFailRecord = 0;
+ c_apiConTimer = 0;
+ c_apiConTimer_line = 0;
+ // Records with constant sizes
+ tcFailRecord = (TcFailRecord*)allocRecord("TcFailRecord",
+ sizeof(TcFailRecord), 1);
+
+ // Variables
+ ctcTimer = 0;
+
+ // Trigger and index pools
+ c_theDefinedTriggerPool.setSize(c_maxNumberOfDefinedTriggers);
+ c_theFiredTriggerPool.setSize(c_maxNumberOfFiredTriggers);
+ c_theIndexPool.setSize(c_maxNumberOfIndexes);
+ c_theIndexOperationPool.setSize(c_maxNumberOfIndexOperations);
+ c_theSeizedIndexOperationPool.setSize(c_maxNumberOfIndexOperations);
+ c_theAttributeBufferPool.setSize(c_transactionBufferSpace);
+ c_firedTriggerHash.setSize((c_maxNumberOfFiredTriggers+10)/10);
+}//Dbtc::initData()
+
+void Dbtc::initRecords()
+{
+ void *p;
+ // Records with dynamic sizes
+ cacheRecord = (CacheRecord*)allocRecord("CacheRecord",
+ sizeof(CacheRecord),
+ ccacheFilesize);
+
+ apiConnectRecord = (ApiConnectRecord*)allocRecord("ApiConnectRecord",
+ sizeof(ApiConnectRecord),
+ capiConnectFilesize);
+
+ for(unsigned i = 0; i<capiConnectFilesize; i++) {
+ p = &apiConnectRecord[i];
+ new (p) ApiConnectRecord(c_theFiredTriggerPool,
+ c_theSeizedIndexOperationPool);
+ }
+ // Init all fired triggers
+ DLFifoList<TcFiredTriggerData> triggers(c_theFiredTriggerPool);
+ FiredTriggerPtr tptr;
+ while(triggers.seize(tptr) == true) {
+ p= tptr.p;
+ new (p) TcFiredTriggerData();
+ }
+ triggers.release();
+
+ /*
+ // Init all index records
+ ArrayList<TcIndexData> indexes(c_theIndexPool);
+ TcIndexDataPtr iptr;
+ while(indexes.seize(iptr) == true) {
+ new (iptr.p) TcIndexData(c_theAttrInfoListPool);
+ }
+ indexes.release();
+ */
+
+ // Init all index operation records
+ ArrayList<TcIndexOperation> indexOps(c_theIndexOperationPool);
+ TcIndexOperationPtr ioptr;
+ while(indexOps.seize(ioptr) == true) {
+ p= ioptr.p;
+ new (p) TcIndexOperation(c_theAttributeBufferPool);
+ }
+ indexOps.release();
+
+ c_apiConTimer = (UintR*)allocRecord("ApiConTimer",
+ sizeof(UintR),
+ capiConnectFilesize);
+
+ c_apiConTimer_line = (UintR*)allocRecord("ApiConTimer_line",
+ sizeof(UintR),
+ capiConnectFilesize);
+
+ tcConnectRecord = (TcConnectRecord*)allocRecord("TcConnectRecord",
+ sizeof(TcConnectRecord),
+ ctcConnectFilesize);
+
+ m_commitAckMarkerPool.setSize(capiConnectFilesize);
+ m_commitAckMarkerHash.setSize(512);
+
+ hostRecord = (HostRecord*)allocRecord("HostRecord",
+ sizeof(HostRecord),
+ chostFilesize);
+
+ tableRecord = (TableRecord*)allocRecord("TableRecord",
+ sizeof(TableRecord),
+ ctabrecFilesize);
+
+ scanRecord = (ScanRecord*)allocRecord("ScanRecord",
+ sizeof(ScanRecord),
+ cscanrecFileSize);
+
+
+ c_scan_frag_pool.setSize(cscanFragrecFileSize);
+ {
+ ScanFragRecPtr ptr;
+ SLList<ScanFragRec> tmp(c_scan_frag_pool);
+ while(tmp.seize(ptr)) {
+ new (ptr.p) ScanFragRec();
+ }
+ tmp.release();
+ }
+
+ indexOps.release();
+
+ databufRecord = (DatabufRecord*)allocRecord("DatabufRecord",
+ sizeof(DatabufRecord),
+ cdatabufFilesize);
+
+ attrbufRecord = (AttrbufRecord*)allocRecord("AttrbufRecord",
+ sizeof(AttrbufRecord),
+ cattrbufFilesize);
+
+ gcpRecord = (GcpRecord*)allocRecord("GcpRecord",
+ sizeof(GcpRecord),
+ cgcpFilesize);
+
+}//Dbtc::initRecords()
+
+Dbtc::Dbtc(const class Configuration & conf):
+ SimulatedBlock(DBTC, conf),
+ c_theDefinedTriggers(c_theDefinedTriggerPool),
+ c_firedTriggerHash(c_theFiredTriggerPool),
+ c_maxNumberOfDefinedTriggers(0),
+ c_maxNumberOfFiredTriggers(0),
+ c_theIndexes(c_theIndexPool),
+ c_maxNumberOfIndexes(0),
+ c_theIndexOperations(c_theIndexOperationPool),
+ c_maxNumberOfIndexOperations(0),
+ m_commitAckMarkerHash(m_commitAckMarkerPool)
+{
+ BLOCK_CONSTRUCTOR(Dbtc);
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ Uint32 transactionBufferMemory = 0;
+ Uint32 maxNoOfIndexes = 0, maxNoOfConcurrentIndexOperations = 0;
+ Uint32 maxNoOfTriggers = 0, maxNoOfFiredTriggers = 0;
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_TRANS_BUFFER_MEM,
+ &transactionBufferMemory);
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_UNIQUE_HASH_INDEXES,
+ &maxNoOfIndexes);
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_INDEX_OPS,
+ &maxNoOfConcurrentIndexOperations);
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_TRIGGERS,
+ &maxNoOfTriggers);
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_TRIGGER_OPS,
+ &maxNoOfFiredTriggers);
+
+ c_transactionBufferSpace =
+ transactionBufferMemory / AttributeBuffer::getSegmentSize();
+ c_maxNumberOfIndexes = maxNoOfIndexes;
+ c_maxNumberOfIndexOperations = maxNoOfConcurrentIndexOperations;
+ c_maxNumberOfDefinedTriggers = maxNoOfTriggers;
+ c_maxNumberOfFiredTriggers = maxNoOfFiredTriggers;
+
+ // Transit signals
+ addRecSignal(GSN_PACKED_SIGNAL, &Dbtc::execPACKED_SIGNAL);
+ addRecSignal(GSN_ABORTED, &Dbtc::execABORTED);
+ addRecSignal(GSN_ATTRINFO, &Dbtc::execATTRINFO);
+ addRecSignal(GSN_CONTINUEB, &Dbtc::execCONTINUEB);
+ addRecSignal(GSN_KEYINFO, &Dbtc::execKEYINFO);
+ addRecSignal(GSN_SCAN_NEXTREQ, &Dbtc::execSCAN_NEXTREQ);
+ addRecSignal(GSN_TAKE_OVERTCREQ, &Dbtc::execTAKE_OVERTCREQ);
+ addRecSignal(GSN_TAKE_OVERTCCONF, &Dbtc::execTAKE_OVERTCCONF);
+ addRecSignal(GSN_LQHKEYREF, &Dbtc::execLQHKEYREF);
+
+ // Received signals
+
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbtc::execDUMP_STATE_ORD);
+ addRecSignal(GSN_SEND_PACKED, &Dbtc::execSEND_PACKED);
+ addRecSignal(GSN_SCAN_HBREP, &Dbtc::execSCAN_HBREP);
+ addRecSignal(GSN_COMPLETED, &Dbtc::execCOMPLETED);
+ addRecSignal(GSN_COMMITTED, &Dbtc::execCOMMITTED);
+ addRecSignal(GSN_DIGETPRIMCONF, &Dbtc::execDIGETPRIMCONF);
+ addRecSignal(GSN_DIGETPRIMREF, &Dbtc::execDIGETPRIMREF);
+ addRecSignal(GSN_DISEIZECONF, &Dbtc::execDISEIZECONF);
+ addRecSignal(GSN_DIVERIFYCONF, &Dbtc::execDIVERIFYCONF);
+ addRecSignal(GSN_DI_FCOUNTCONF, &Dbtc::execDI_FCOUNTCONF);
+ addRecSignal(GSN_DI_FCOUNTREF, &Dbtc::execDI_FCOUNTREF);
+ addRecSignal(GSN_GCP_NOMORETRANS, &Dbtc::execGCP_NOMORETRANS);
+ addRecSignal(GSN_LQHKEYCONF, &Dbtc::execLQHKEYCONF);
+ addRecSignal(GSN_NDB_STTOR, &Dbtc::execNDB_STTOR);
+ addRecSignal(GSN_READ_NODESCONF, &Dbtc::execREAD_NODESCONF);
+ addRecSignal(GSN_READ_NODESREF, &Dbtc::execREAD_NODESREF);
+ addRecSignal(GSN_STTOR, &Dbtc::execSTTOR);
+ addRecSignal(GSN_TC_COMMITREQ, &Dbtc::execTC_COMMITREQ);
+ addRecSignal(GSN_TC_CLOPSIZEREQ, &Dbtc::execTC_CLOPSIZEREQ);
+ addRecSignal(GSN_TCGETOPSIZEREQ, &Dbtc::execTCGETOPSIZEREQ);
+ addRecSignal(GSN_TCKEYREQ, &Dbtc::execTCKEYREQ);
+ addRecSignal(GSN_TCRELEASEREQ, &Dbtc::execTCRELEASEREQ);
+ addRecSignal(GSN_TCSEIZEREQ, &Dbtc::execTCSEIZEREQ);
+ addRecSignal(GSN_TCROLLBACKREQ, &Dbtc::execTCROLLBACKREQ);
+ addRecSignal(GSN_TC_HBREP, &Dbtc::execTC_HBREP);
+ addRecSignal(GSN_TC_SCHVERREQ, &Dbtc::execTC_SCHVERREQ);
+ addRecSignal(GSN_SCAN_TABREQ, &Dbtc::execSCAN_TABREQ);
+ addRecSignal(GSN_SCAN_FRAGCONF, &Dbtc::execSCAN_FRAGCONF);
+ addRecSignal(GSN_SCAN_FRAGREF, &Dbtc::execSCAN_FRAGREF);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbtc::execREAD_CONFIG_REQ, true);
+ addRecSignal(GSN_LQH_TRANSCONF, &Dbtc::execLQH_TRANSCONF);
+ addRecSignal(GSN_COMPLETECONF, &Dbtc::execCOMPLETECONF);
+ addRecSignal(GSN_COMMITCONF, &Dbtc::execCOMMITCONF);
+ addRecSignal(GSN_ABORTCONF, &Dbtc::execABORTCONF);
+ addRecSignal(GSN_NODE_FAILREP, &Dbtc::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Dbtc::execINCL_NODEREQ);
+ addRecSignal(GSN_TIME_SIGNAL, &Dbtc::execTIME_SIGNAL);
+ addRecSignal(GSN_API_FAILREQ, &Dbtc::execAPI_FAILREQ);
+ addRecSignal(GSN_SET_VAR_REQ, &Dbtc::execSET_VAR_REQ);
+
+ addRecSignal(GSN_TC_COMMIT_ACK, &Dbtc::execTC_COMMIT_ACK);
+ addRecSignal(GSN_ABORT_ALL_REQ, &Dbtc::execABORT_ALL_REQ);
+
+ addRecSignal(GSN_CREATE_TRIG_REQ, &Dbtc::execCREATE_TRIG_REQ);
+ addRecSignal(GSN_DROP_TRIG_REQ, &Dbtc::execDROP_TRIG_REQ);
+ addRecSignal(GSN_FIRE_TRIG_ORD, &Dbtc::execFIRE_TRIG_ORD);
+ addRecSignal(GSN_TRIG_ATTRINFO, &Dbtc::execTRIG_ATTRINFO);
+
+ addRecSignal(GSN_CREATE_INDX_REQ, &Dbtc::execCREATE_INDX_REQ);
+ addRecSignal(GSN_DROP_INDX_REQ, &Dbtc::execDROP_INDX_REQ);
+ addRecSignal(GSN_TCINDXREQ, &Dbtc::execTCINDXREQ);
+ addRecSignal(GSN_INDXKEYINFO, &Dbtc::execINDXKEYINFO);
+ addRecSignal(GSN_INDXATTRINFO, &Dbtc::execINDXATTRINFO);
+ addRecSignal(GSN_ALTER_INDX_REQ, &Dbtc::execALTER_INDX_REQ);
+
+ addRecSignal(GSN_TRANSID_AI_R, &Dbtc::execTRANSID_AI_R);
+ addRecSignal(GSN_KEYINFO20_R, &Dbtc::execKEYINFO20_R);
+
+ // Index table lookup
+ addRecSignal(GSN_TCKEYCONF, &Dbtc::execTCKEYCONF);
+ addRecSignal(GSN_TCKEYREF, &Dbtc::execTCKEYREF);
+ addRecSignal(GSN_TRANSID_AI, &Dbtc::execTRANSID_AI);
+ addRecSignal(GSN_TCROLLBACKREP, &Dbtc::execTCROLLBACKREP);
+
+ //addRecSignal(GSN_CREATE_TAB_REQ, &Dbtc::execCREATE_TAB_REQ);
+ addRecSignal(GSN_DROP_TAB_REQ, &Dbtc::execDROP_TAB_REQ);
+ addRecSignal(GSN_PREP_DROP_TAB_REQ, &Dbtc::execPREP_DROP_TAB_REQ);
+ addRecSignal(GSN_WAIT_DROP_TAB_REF, &Dbtc::execWAIT_DROP_TAB_REF);
+ addRecSignal(GSN_WAIT_DROP_TAB_CONF, &Dbtc::execWAIT_DROP_TAB_CONF);
+
+ addRecSignal(GSN_ALTER_TAB_REQ, &Dbtc::execALTER_TAB_REQ);
+
+ initData();
+
+#ifdef VM_TRACE
+ {
+ void* tmp[] = { &apiConnectptr,
+ &tcConnectptr,
+ &cachePtr,
+ &attrbufptr,
+ &hostptr,
+ &gcpPtr,
+ &tmpApiConnectptr,
+ &timeOutptr,
+ &scanFragptr,
+ &databufptr,
+ &tmpDatabufptr };
+ init_globals_list(tmp, sizeof(tmp)/sizeof(tmp[0]));
+ }
+#endif
+}//Dbtc::Dbtc()
+
+Dbtc::~Dbtc()
+{
+ // Records with dynamic sizes
+ deallocRecord((void **)&cacheRecord, "CacheRecord",
+ sizeof(CacheRecord),
+ ccacheFilesize);
+
+ deallocRecord((void **)&apiConnectRecord, "ApiConnectRecord",
+ sizeof(ApiConnectRecord),
+ capiConnectFilesize);
+
+ deallocRecord((void **)&tcConnectRecord, "TcConnectRecord",
+ sizeof(TcConnectRecord),
+ ctcConnectFilesize);
+
+ deallocRecord((void **)&hostRecord, "HostRecord",
+ sizeof(HostRecord),
+ chostFilesize);
+
+ deallocRecord((void **)&tableRecord, "TableRecord",
+ sizeof(TableRecord),
+ ctabrecFilesize);
+
+ deallocRecord((void **)&scanRecord, "ScanRecord",
+ sizeof(ScanRecord),
+ cscanrecFileSize);
+
+ deallocRecord((void **)&databufRecord, "DatabufRecord",
+ sizeof(DatabufRecord),
+ cdatabufFilesize);
+
+ deallocRecord((void **)&attrbufRecord, "AttrbufRecord",
+ sizeof(AttrbufRecord),
+ cattrbufFilesize);
+
+ deallocRecord((void **)&gcpRecord, "GcpRecord",
+ sizeof(GcpRecord),
+ cgcpFilesize);
+
+ deallocRecord((void **)&tcFailRecord, "TcFailRecord",
+ sizeof(TcFailRecord), 1);
+
+ deallocRecord((void **)&c_apiConTimer, "ApiConTimer",
+ sizeof(UintR),
+ capiConnectFilesize);
+
+ deallocRecord((void **)&c_apiConTimer_line, "ApiConTimer",
+ sizeof(UintR),
+ capiConnectFilesize);
+}//Dbtc::~Dbtc()
+
+BLOCK_FUNCTIONS(Dbtc)
+
diff --git a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
new file mode 100644
index 00000000000..17d55176830
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
@@ -0,0 +1,13098 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTC_C
+
+#include "Dbtc.hpp"
+#include "md5_hash.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <my_sys.h>
+
+#include <signaldata/EventReport.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/TcKeyConf.hpp>
+#include <signaldata/TcKeyRef.hpp>
+#include <signaldata/KeyInfo.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/TransIdAI.hpp>
+#include <signaldata/TcRollbackRep.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/LqhKey.hpp>
+#include <signaldata/TcCommit.hpp>
+#include <signaldata/TcContinueB.hpp>
+#include <signaldata/TcKeyFailConf.hpp>
+#include <signaldata/AbortAll.hpp>
+#include <signaldata/ScanFrag.hpp>
+#include <signaldata/ScanTab.hpp>
+#include <signaldata/PrepDropTab.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/AlterTab.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/FireTrigOrd.hpp>
+#include <signaldata/TrigAttrInfo.hpp>
+#include <signaldata/CreateIndx.hpp>
+#include <signaldata/DropIndx.hpp>
+#include <signaldata/AlterIndx.hpp>
+#include <signaldata/ScanTab.hpp>
+#include <signaldata/SystemError.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/DisconnectRep.hpp>
+#include <signaldata/TcHbRep.hpp>
+
+#include <signaldata/PrepDropTab.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/IndxKeyInfo.hpp>
+#include <signaldata/IndxAttrInfo.hpp>
+#include <signaldata/PackedSignal.hpp>
+#include <AttributeHeader.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <AttributeDescriptor.hpp>
+#include <SectionReader.hpp>
+
+#include <NdbOut.hpp>
+#include <DebuggerNames.hpp>
+
+// Use DEBUG to print messages that should be
+// seen only when we debug the product
+#ifdef VM_TRACE
+#define DEBUG(x) ndbout << "DBTC: "<< x << endl;
+#else
+#define DEBUG(x)
+#endif
+
+#define INTERNAL_TRIGGER_TCKEYREQ_JBA 0
+
+#ifdef VM_TRACE
+NdbOut &
+operator<<(NdbOut& out, Dbtc::ConnectionState state){
+ switch(state){
+ case Dbtc::CS_CONNECTED: out << "CS_CONNECTED"; break;
+ case Dbtc::CS_DISCONNECTED: out << "CS_DISCONNECTED"; break;
+ case Dbtc::CS_STARTED: out << "CS_STARTED"; break;
+ case Dbtc::CS_RECEIVING: out << "CS_RECEIVING"; break;
+ case Dbtc::CS_PREPARED: out << "CS_PREPARED"; break;
+ case Dbtc::CS_START_PREPARING: out << "CS_START_PREPARING"; break;
+ case Dbtc::CS_REC_PREPARING: out << "CS_REC_PREPARING"; break;
+ case Dbtc::CS_RESTART: out << "CS_RESTART"; break;
+ case Dbtc::CS_ABORTING: out << "CS_ABORTING"; break;
+ case Dbtc::CS_COMPLETING: out << "CS_COMPLETING"; break;
+ case Dbtc::CS_COMPLETE_SENT: out << "CS_COMPLETE_SENT"; break;
+ case Dbtc::CS_PREPARE_TO_COMMIT: out << "CS_PREPARE_TO_COMMIT"; break;
+ case Dbtc::CS_COMMIT_SENT: out << "CS_COMMIT_SENT"; break;
+ case Dbtc::CS_START_COMMITTING: out << "CS_START_COMMITTING"; break;
+ case Dbtc::CS_COMMITTING: out << "CS_COMMITTING"; break;
+ case Dbtc::CS_REC_COMMITTING: out << "CS_REC_COMMITTING"; break;
+ case Dbtc::CS_WAIT_ABORT_CONF: out << "CS_WAIT_ABORT_CONF"; break;
+ case Dbtc::CS_WAIT_COMPLETE_CONF: out << "CS_WAIT_COMPLETE_CONF"; break;
+ case Dbtc::CS_WAIT_COMMIT_CONF: out << "CS_WAIT_COMMIT_CONF"; break;
+ case Dbtc::CS_FAIL_ABORTING: out << "CS_FAIL_ABORTING"; break;
+ case Dbtc::CS_FAIL_ABORTED: out << "CS_FAIL_ABORTED"; break;
+ case Dbtc::CS_FAIL_PREPARED: out << "CS_FAIL_PREPARED"; break;
+ case Dbtc::CS_FAIL_COMMITTING: out << "CS_FAIL_COMMITTING"; break;
+ case Dbtc::CS_FAIL_COMMITTED: out << "CS_FAIL_COMMITTED"; break;
+ case Dbtc::CS_FAIL_COMPLETED: out << "CS_FAIL_COMPLETED"; break;
+ case Dbtc::CS_START_SCAN: out << "CS_START_SCAN"; break;
+ default:
+ out << "Unknown: " << (int)state; break;
+ }
+ return out;
+}
+NdbOut &
+operator<<(NdbOut& out, Dbtc::OperationState state){
+ out << (int)state;
+ return out;
+}
+NdbOut &
+operator<<(NdbOut& out, Dbtc::AbortState state){
+ out << (int)state;
+ return out;
+}
+NdbOut &
+operator<<(NdbOut& out, Dbtc::ReturnSignal state){
+ out << (int)state;
+ return out;
+}
+NdbOut &
+operator<<(NdbOut& out, Dbtc::ScanRecord::ScanState state){
+ out << (int)state;
+ return out;
+}
+NdbOut &
+operator<<(NdbOut& out, Dbtc::ScanFragRec::ScanFragState state){
+ out << (int)state;
+ return out;
+}
+#endif
+
+void
+Dbtc::updateBuddyTimer(ApiConnectRecordPtr apiPtr)
+{
+ if (apiPtr.p->buddyPtr != RNIL) {
+ jam();
+ ApiConnectRecordPtr buddyApiPtr;
+ buddyApiPtr.i = apiPtr.p->buddyPtr;
+ ptrCheckGuard(buddyApiPtr, capiConnectFilesize, apiConnectRecord);
+ if (getApiConTimer(buddyApiPtr.i) != 0) {
+ if ((apiPtr.p->transid[0] == buddyApiPtr.p->transid[0]) &&
+ (apiPtr.p->transid[1] == buddyApiPtr.p->transid[1])) {
+ jam();
+ setApiConTimer(buddyApiPtr.i, ctcTimer, __LINE__);
+ } else {
+ jam();
+ // Not a buddy anymore since not the same transid
+ apiPtr.p->buddyPtr = RNIL;
+ }//if
+ }//if
+ }//if
+}
+
+void Dbtc::execCONTINUEB(Signal* signal)
+{
+ UintR tcase;
+
+ jamEntry();
+ tcase = signal->theData[0];
+ UintR Tdata0 = signal->theData[1];
+ UintR Tdata1 = signal->theData[2];
+ UintR Tdata2 = signal->theData[3];
+ switch (tcase) {
+ case TcContinueB::ZRETURN_FROM_QUEUED_DELIVERY:
+ jam();
+ ndbrequire(false);
+ return;
+ case TcContinueB::ZCOMPLETE_TRANS_AT_TAKE_OVER:
+ jam();
+ tcNodeFailptr.i = Tdata0;
+ ptrCheckGuard(tcNodeFailptr, 1, tcFailRecord);
+ completeTransAtTakeOverLab(signal, Tdata1);
+ return;
+ case TcContinueB::ZCONTINUE_TIME_OUT_CONTROL:
+ jam();
+ timeOutLoopStartLab(signal, Tdata0);
+ return;
+ case TcContinueB::ZNODE_TAKE_OVER_COMPLETED:
+ jam();
+ tnodeid = Tdata0;
+ tcNodeFailptr.i = 0;
+ ptrAss(tcNodeFailptr, tcFailRecord);
+ nodeTakeOverCompletedLab(signal);
+ return;
+ case TcContinueB::ZINITIALISE_RECORDS:
+ jam();
+ initialiseRecordsLab(signal, Tdata0, Tdata2, signal->theData[4]);
+ return;
+ case TcContinueB::ZSEND_COMMIT_LOOP:
+ jam();
+ apiConnectptr.i = Tdata0;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ tcConnectptr.i = Tdata1;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ commit020Lab(signal);
+ return;
+ case TcContinueB::ZSEND_COMPLETE_LOOP:
+ jam();
+ apiConnectptr.i = Tdata0;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ tcConnectptr.i = Tdata1;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ complete010Lab(signal);
+ return;
+ case TcContinueB::ZHANDLE_FAILED_API_NODE:
+ jam();
+ handleFailedApiNode(signal, Tdata0, Tdata1);
+ return;
+ case TcContinueB::ZTRANS_EVENT_REP:
+ jam();
+ /* -------------------------------------------------------------------- */
+ // Report information about transaction activity once per second.
+ /* -------------------------------------------------------------------- */
+ if (c_counters.c_trans_status == TransCounters::Timer){
+ Uint32 len = c_counters.report(signal);
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, len, JBB);
+
+ c_counters.reset();
+ signal->theData[0] = TcContinueB::ZTRANS_EVENT_REP;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 5000, 1);
+ }
+ return;
+ case TcContinueB::ZCONTINUE_TIME_OUT_FRAG_CONTROL:
+ jam();
+ timeOutLoopStartFragLab(signal, Tdata0);
+ return;
+ case TcContinueB::ZABORT_BREAK:
+ jam();
+ tcConnectptr.i = Tdata0;
+ apiConnectptr.i = Tdata1;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ apiConnectptr.p->counter--;
+ abort015Lab(signal);
+ return;
+ case TcContinueB::ZABORT_TIMEOUT_BREAK:
+ jam();
+ tcConnectptr.i = Tdata0;
+ apiConnectptr.i = Tdata1;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ apiConnectptr.p->counter--;
+ sendAbortedAfterTimeout(signal, 1);
+ return;
+ case TcContinueB::ZHANDLE_FAILED_API_NODE_REMOVE_MARKERS:
+ jam();
+ removeMarkerForFailedAPI(signal, Tdata0, Tdata1);
+ return;
+ case TcContinueB::ZWAIT_ABORT_ALL:
+ jam();
+ checkAbortAllTimeout(signal, Tdata0);
+ return;
+ case TcContinueB::ZCHECK_SCAN_ACTIVE_FAILED_LQH:
+ jam();
+ checkScanActiveInFailedLqh(signal, Tdata0, Tdata1);
+ return;
+ case TcContinueB::CHECK_WAIT_DROP_TAB_FAILED_LQH:
+ jam();
+ checkWaitDropTabFailedLqh(signal, Tdata0, Tdata1);
+ return;
+ case TcContinueB::TRIGGER_PENDING:
+ jam();
+ ApiConnectRecordPtr transPtr;
+ transPtr.i = Tdata0;
+ ptrCheckGuard(transPtr, capiConnectFilesize, apiConnectRecord);
+ transPtr.p->triggerPending = false;
+ executeTriggers(signal, &transPtr);
+ return;
+ case TcContinueB::DelayTCKEYCONF:
+ jam();
+ apiConnectptr.i = Tdata0;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ sendtckeyconf(signal, Tdata1);
+ return;
+ default:
+ ndbrequire(false);
+ }//switch
+}
+
+void Dbtc::execDIGETNODESREF(Signal* signal)
+{
+ jamEntry();
+ terrorCode = signal->theData[1];
+ releaseAtErrorLab(signal);
+}
+
+void Dbtc::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+ tblockref = signal->theData[0];
+ hostptr.i = signal->theData[1];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ hostptr.p->hostStatus = HS_ALIVE;
+ hostptr.p->takeOverStatus = TOS_IDLE;
+ signal->theData[0] = cownref;
+ sendSignal(tblockref, GSN_INCL_NODECONF, signal, 1, JBB);
+}
+
+void Dbtc::execREAD_NODESREF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}
+
+void Dbtc::execTC_SCHVERREQ(Signal* signal)
+{
+ jamEntry();
+ if (! assembleFragments(signal)) {
+ jam();
+ return;
+ }
+ tabptr.i = signal->theData[0];
+ ptrCheckGuard(tabptr, ctabrecFilesize, tableRecord);
+ tabptr.p->currentSchemaVersion = signal->theData[1];
+ tabptr.p->storedTable = (bool)signal->theData[2];
+ BlockReference retRef = signal->theData[3];
+ tabptr.p->tableType = (Uint8)signal->theData[4];
+ BlockReference retPtr = signal->theData[5];
+ Uint32 noOfKeyAttr = signal->theData[6];
+ ndbrequire(noOfKeyAttr <= MAX_ATTRIBUTES_IN_INDEX);
+ Uint32 hasCharAttr = 0;
+ Uint32 noOfDistrKeys = 0;
+ SegmentedSectionPtr s0Ptr;
+ signal->getSection(s0Ptr, 0);
+ SectionReader r0(s0Ptr, getSectionSegmentPool());
+ Uint32 i = 0;
+ while (i < noOfKeyAttr) {
+ jam();
+ Uint32 attributeDescriptor = ~0;
+ Uint32 csNumber = ~0;
+ if (! r0.getWord(&attributeDescriptor) ||
+ ! r0.getWord(&csNumber)) {
+ jam();
+ break;
+ }
+ CHARSET_INFO* cs = 0;
+ if (csNumber != 0) {
+ cs = all_charsets[csNumber];
+ ndbrequire(cs != 0);
+ hasCharAttr = 1;
+ }
+
+ noOfDistrKeys += AttributeDescriptor::getDKey(attributeDescriptor);
+ tabptr.p->keyAttr[i].attributeDescriptor = attributeDescriptor;
+ tabptr.p->keyAttr[i].charsetInfo = cs;
+ i++;
+ }
+ ndbrequire(i == noOfKeyAttr);
+ releaseSections(signal);
+
+ ndbrequire(tabptr.p->enabled == false);
+ tabptr.p->enabled = true;
+ tabptr.p->dropping = false;
+ tabptr.p->noOfKeyAttr = noOfKeyAttr;
+ tabptr.p->hasCharAttr = hasCharAttr;
+ tabptr.p->noOfDistrKeys = noOfDistrKeys;
+
+ signal->theData[0] = tabptr.i;
+ signal->theData[1] = retPtr;
+ sendSignal(retRef, GSN_TC_SCHVERCONF, signal, 2, JBB);
+}//Dbtc::execTC_SCHVERREQ()
+
+void
+Dbtc::execPREP_DROP_TAB_REQ(Signal* signal)
+{
+ jamEntry();
+
+ PrepDropTabReq* req = (PrepDropTabReq*)signal->getDataPtr();
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+
+ if(!tabPtr.p->enabled){
+ jam();
+ PrepDropTabRef* ref = (PrepDropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = PrepDropTabRef::NoSuchTable;
+ sendSignal(senderRef, GSN_PREP_DROP_TAB_REF, signal,
+ PrepDropTabRef::SignalLength, JBB);
+ return;
+ }
+
+ if(tabPtr.p->dropping){
+ jam();
+ PrepDropTabRef* ref = (PrepDropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = PrepDropTabRef::DropInProgress;
+ sendSignal(senderRef, GSN_PREP_DROP_TAB_REF, signal,
+ PrepDropTabRef::SignalLength, JBB);
+ return;
+ }
+
+ tabPtr.p->dropping = true;
+ tabPtr.p->dropTable.senderRef = senderRef;
+ tabPtr.p->dropTable.senderData = senderData;
+
+ {
+ WaitDropTabReq * req = (WaitDropTabReq*)signal->getDataPtrSend();
+ req->tableId = tabPtr.i;
+ req->senderRef = reference();
+
+ HostRecordPtr hostPtr;
+ tabPtr.p->dropTable.waitDropTabCount.clearWaitingFor();
+ for (hostPtr.i = 1; hostPtr.i < MAX_NDB_NODES; hostPtr.i++) {
+ jam();
+ ptrAss(hostPtr, hostRecord);
+ if (hostPtr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tabPtr.p->dropTable.waitDropTabCount.setWaitingFor(hostPtr.i);
+ sendSignal(calcLqhBlockRef(hostPtr.i), GSN_WAIT_DROP_TAB_REQ,
+ signal, WaitDropTabReq::SignalLength, JBB);
+ }//for
+ }//if
+
+ ndbrequire(tabPtr.p->dropTable.waitDropTabCount.done() != true);
+ }
+}
+
+void
+Dbtc::execWAIT_DROP_TAB_CONF(Signal* signal)
+{
+ jamEntry();
+ WaitDropTabConf * conf = (WaitDropTabConf*)signal->getDataPtr();
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = conf->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+
+ ndbrequire(tabPtr.p->dropping == true);
+ Uint32 nodeId = refToNode(conf->senderRef);
+ tabPtr.p->dropTable.waitDropTabCount.clearWaitingFor(nodeId);
+
+ if(!tabPtr.p->dropTable.waitDropTabCount.done()){
+ jam();
+ return;
+ }
+
+ {
+ PrepDropTabConf* conf = (PrepDropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ conf->senderData = tabPtr.p->dropTable.senderData;
+ sendSignal(tabPtr.p->dropTable.senderRef, GSN_PREP_DROP_TAB_CONF, signal,
+ PrepDropTabConf::SignalLength, JBB);
+ tabPtr.p->dropTable.senderRef = 0;
+ }
+}
+
+void
+Dbtc::execWAIT_DROP_TAB_REF(Signal* signal)
+{
+ jamEntry();
+ WaitDropTabRef * ref = (WaitDropTabRef*)signal->getDataPtr();
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = ref->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+
+ ndbrequire(tabPtr.p->dropping == true);
+ Uint32 nodeId = refToNode(ref->senderRef);
+ tabPtr.p->dropTable.waitDropTabCount.clearWaitingFor(nodeId);
+
+ ndbrequire(ref->errorCode == WaitDropTabRef::NoSuchTable ||
+ ref->errorCode == WaitDropTabRef::NF_FakeErrorREF);
+
+ if(!tabPtr.p->dropTable.waitDropTabCount.done()){
+ jam();
+ return;
+ }
+
+ {
+ PrepDropTabConf* conf = (PrepDropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ conf->senderData = tabPtr.p->dropTable.senderData;
+ sendSignal(tabPtr.p->dropTable.senderRef, GSN_PREP_DROP_TAB_CONF, signal,
+ PrepDropTabConf::SignalLength, JBB);
+ tabPtr.p->dropTable.senderRef = 0;
+ }
+}
+
+void
+Dbtc::checkWaitDropTabFailedLqh(Signal* signal, Uint32 nodeId, Uint32 tableId)
+{
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = tableId;
+
+ WaitDropTabConf * conf = (WaitDropTabConf*)signal->getDataPtr();
+ conf->tableId = tableId;
+
+ const Uint32 RT_BREAK = 16;
+ for(Uint32 i = 0; i<RT_BREAK && tabPtr.i < ctabrecFilesize; i++, tabPtr.i++){
+ jam();
+ ptrAss(tabPtr, tableRecord);
+ if(tabPtr.p->enabled && tabPtr.p->dropping){
+ if(tabPtr.p->dropTable.waitDropTabCount.isWaitingFor(nodeId)){
+ jam();
+ conf->senderRef = calcLqhBlockRef(nodeId);
+ execWAIT_DROP_TAB_CONF(signal);
+ tabPtr.i++;
+ break;
+ }
+ }
+ }
+
+ if(tabPtr.i == ctabrecFilesize){
+ /**
+ * Finished
+ */
+ jam();
+ return;
+ }
+
+ signal->theData[0] = TcContinueB::CHECK_WAIT_DROP_TAB_FAILED_LQH;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = tabPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+}
+
+void
+Dbtc::execDROP_TAB_REQ(Signal* signal)
+{
+ jamEntry();
+
+ DropTabReq* req = (DropTabReq*)signal->getDataPtr();
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+ DropTabReq::RequestType rt = (DropTabReq::RequestType)req->requestType;
+
+ if(!tabPtr.p->enabled && rt == DropTabReq::OnlineDropTab){
+ jam();
+ DropTabRef* ref = (DropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = DropTabRef::NoSuchTable;
+ sendSignal(senderRef, GSN_DROP_TAB_REF, signal,
+ DropTabRef::SignalLength, JBB);
+ return;
+ }
+
+ if(!tabPtr.p->dropping && rt == DropTabReq::OnlineDropTab){
+ jam();
+ DropTabRef* ref = (DropTabRef*)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->senderData = senderData;
+ ref->tableId = tabPtr.i;
+ ref->errorCode = DropTabRef::DropWoPrep;
+ sendSignal(senderRef, GSN_DROP_TAB_REF, signal,
+ DropTabRef::SignalLength, JBB);
+ return;
+ }
+
+ tabPtr.p->enabled = false;
+ tabPtr.p->dropping = false;
+
+ DropTabConf * conf = (DropTabConf*)signal->getDataPtrSend();
+ conf->tableId = tabPtr.i;
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ sendSignal(senderRef, GSN_DROP_TAB_CONF, signal,
+ PrepDropTabConf::SignalLength, JBB);
+}
+
+void Dbtc::execALTER_TAB_REQ(Signal * signal)
+{
+ AlterTabReq* const req = (AlterTabReq*)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+ const Uint32 changeMask = req->changeMask;
+ const Uint32 tableId = req->tableId;
+ const Uint32 tableVersion = req->tableVersion;
+ const Uint32 gci = req->gci;
+ AlterTabReq::RequestType requestType =
+ (AlterTabReq::RequestType) req->requestType;
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+ tabPtr.p->currentSchemaVersion = tableVersion;
+
+ // Request handled successfully
+ AlterTabConf * conf = (AlterTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->changeMask = changeMask;
+ conf->tableId = tableId;
+ conf->tableVersion = tableVersion;
+ conf->gci = gci;
+ conf->requestType = requestType;
+ sendSignal(senderRef, GSN_ALTER_TAB_CONF, signal,
+ AlterTabConf::SignalLength, JBB);
+}
+
+/* ***************************************************************************/
+/* START / RESTART */
+/* ***************************************************************************/
+void Dbtc::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ jamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ UintR apiConnect;
+ UintR tcConnect;
+ UintR tables;
+ UintR localScan;
+ UintR tcScan;
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TC_API_CONNECT, &apiConnect));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TC_TC_CONNECT, &tcConnect));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TC_TABLE, &tables));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TC_LOCAL_SCAN, &localScan));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TC_SCAN, &tcScan));
+
+ ccacheFilesize = (apiConnect/3) + 1;
+ capiConnectFilesize = apiConnect;
+ ctcConnectFilesize = tcConnect;
+ ctabrecFilesize = tables;
+ cscanrecFileSize = tcScan;
+ cscanFragrecFileSize = localScan;
+
+ initRecords();
+ initialiseRecordsLab(signal, 0, ref, senderData);
+
+ Uint32 val = 3000;
+ ndb_mgm_get_int_parameter(p, CFG_DB_TRANSACTION_DEADLOCK_TIMEOUT, &val);
+ set_timeout_value(val);
+
+ val = 3000;
+ ndb_mgm_get_int_parameter(p, CFG_DB_TRANSACTION_INACTIVE_TIMEOUT, &val);
+ set_appl_timeout_value(val);
+
+ val = 1;
+ //ndb_mgm_get_int_parameter(p, CFG_DB_PARALLEL_TRANSACTION_TAKEOVER, &val);
+ set_no_parallel_takeover(val);
+
+ ctimeOutCheckDelay = 50; // 500ms
+}//Dbtc::execSIZEALT_REP()
+
+void Dbtc::execSTTOR(Signal* signal)
+{
+ Uint16 tphase;
+
+ jamEntry();
+ /* START CASE */
+ tphase = signal->theData[1];
+ csignalKey = signal->theData[6];
+ switch (tphase) {
+ case ZSPH1:
+ jam();
+ startphase1x010Lab(signal);
+ return;
+ default:
+ jam();
+ sttorryLab(signal); /* START PHASE 255 */
+ return;
+ }//switch
+}//Dbtc::execSTTOR()
+
+void Dbtc::sttorryLab(Signal* signal)
+{
+ signal->theData[0] = csignalKey;
+ signal->theData[1] = 3; /* BLOCK CATEGORY */
+ signal->theData[2] = 2; /* SIGNAL VERSION NUMBER */
+ signal->theData[3] = ZSPH1;
+ signal->theData[4] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+}//Dbtc::sttorryLab()
+
+/* ***************************************************************************/
+/* INTERNAL START / RESTART */
+/*****************************************************************************/
+void Dbtc::execNDB_STTOR(Signal* signal)
+{
+ Uint16 tndbstartphase;
+ Uint16 tstarttype;
+
+ jamEntry();
+ tusersblkref = signal->theData[0];
+ tnodeid = signal->theData[1];
+ tndbstartphase = signal->theData[2]; /* START PHASE */
+ tstarttype = signal->theData[3]; /* START TYPE */
+ switch (tndbstartphase) {
+ case ZINTSPH1:
+ jam();
+ intstartphase1x010Lab(signal);
+ return;
+ case ZINTSPH2:
+ jam();
+ intstartphase2x010Lab(signal);
+ return;
+ case ZINTSPH3:
+ jam();
+ intstartphase3x010Lab(signal); /* SEIZE CONNECT RECORD IN EACH LQH*/
+// Start transaction event reporting.
+ c_counters.c_trans_status = TransCounters::Timer;
+ c_counters.reset();
+ signal->theData[0] = TcContinueB::ZTRANS_EVENT_REP;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 1);
+ return;
+ case ZINTSPH6:
+ jam();
+ csystemStart = SSS_TRUE;
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ ndbsttorry010Lab(signal);
+ return;
+}//Dbtc::execNDB_STTOR()
+
+void Dbtc::ndbsttorry010Lab(Signal* signal)
+{
+ signal->theData[0] = cownref;
+ sendSignal(cndbcntrblockref, GSN_NDB_STTORRY, signal, 1, JBB);
+}//Dbtc::ndbsttorry010Lab()
+
+void
+Dbtc::set_timeout_value(Uint32 timeOut)
+{
+ timeOut = timeOut / 10;
+ if (timeOut < 2) {
+ jam();
+ timeOut = 100;
+ }//if
+ ctimeOutValue = timeOut;
+}
+
+void
+Dbtc::set_appl_timeout_value(Uint32 timeOut)
+{
+ timeOut /= 10;
+ if (timeOut < ctimeOutValue) {
+ jam();
+ c_appl_timeout_value = ctimeOutValue;
+ }//if
+ c_appl_timeout_value = timeOut;
+}
+
+void
+Dbtc::set_no_parallel_takeover(Uint32 noParallelTakeOver)
+{
+ if (noParallelTakeOver == 0) {
+ jam();
+ noParallelTakeOver = 1;
+ } else if (noParallelTakeOver > MAX_NDB_NODES) {
+ jam();
+ noParallelTakeOver = MAX_NDB_NODES;
+ }//if
+ cnoParallelTakeOver = noParallelTakeOver;
+}
+
+/* ***************************************************************************/
+/* S T A R T P H A S E 1 X */
+/* INITIALISE BLOCKREF AND BLOCKNUMBERS */
+/* ***************************************************************************/
+void Dbtc::startphase1x010Lab(Signal* signal)
+{
+ csystemStart = SSS_FALSE;
+ ctimeOutCheckCounter = 0;
+ ctimeOutCheckFragCounter = 0;
+ ctimeOutMissedHeartbeats = 0;
+ ctimeOutCheckHeartbeat = 0;
+ ctimeOutCheckLastHeartbeat = 0;
+ ctimeOutCheckActive = TOCS_FALSE;
+ ctimeOutCheckFragActive = TOCS_FALSE;
+ sttorryLab(signal);
+}//Dbtc::startphase1x010Lab()
+
+/*****************************************************************************/
+/* I N T S T A R T P H A S E 1 X */
+/* INITIALISE ALL RECORDS. */
+/*****************************************************************************/
+void Dbtc::intstartphase1x010Lab(Signal* signal)
+{
+ cownNodeid = tnodeid;
+ cownref = calcTcBlockRef(cownNodeid);
+ clqhblockref = calcLqhBlockRef(cownNodeid);
+ cdihblockref = calcDihBlockRef(cownNodeid);
+ cdictblockref = calcDictBlockRef(cownNodeid);
+ cndbcntrblockref = calcNdbCntrBlockRef(cownNodeid);
+ cerrorBlockref = calcNdbCntrBlockRef(cownNodeid);
+ coperationsize = 0;
+ cfailure_nr = 0;
+ ndbsttorry010Lab(signal);
+}//Dbtc::intstartphase1x010Lab()
+
+/*****************************************************************************/
+/* I N T S T A R T P H A S E 2 X */
+/* SET-UP LOCAL CONNECTIONS. */
+/*****************************************************************************/
+void Dbtc::intstartphase2x010Lab(Signal* signal)
+{
+ tcConnectptr.i = cfirstfreeTcConnect;
+ intstartphase2x020Lab(signal);
+}//Dbtc::intstartphase2x010Lab()
+
+void Dbtc::intstartphase2x020Lab(Signal* signal)
+{
+ if (tcConnectptr.i == RNIL) {
+ jam();
+ ndbsttorry010Lab(signal);
+ return;
+ }//if
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ tcConnectptr.p->tcConnectstate = OS_CONNECTING_DICT;
+/* ****************** */
+/* DISEIZEREQ < */
+/* ****************** */
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ sendSignal(cdihblockref, GSN_DISEIZEREQ, signal, 2, JBB);
+}//Dbtc::intstartphase2x020Lab()
+
+void Dbtc::execDISEIZECONF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ tcConnectptr.p->dihConnectptr = signal->theData[1];
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ intstartphase2x020Lab(signal);
+}//Dbtc::execDISEIZECONF()
+
+/*****************************************************************************/
+/* I N T S T A R T P H A S E 3 X */
+/* PREPARE DISTRIBUTED CONNECTIONS */
+/*****************************************************************************/
+void Dbtc::intstartphase3x010Lab(Signal* signal)
+{
+ signal->theData[0] = cownref;
+ sendSignal(cndbcntrblockref, GSN_READ_NODESREQ, signal, 1, JBB);
+}//Dbtc::intstartphase3x010Lab()
+
+void Dbtc::execREAD_NODESCONF(Signal* signal)
+{
+ UintR guard0;
+
+ jamEntry();
+
+ ReadNodesConf * const readNodes = (ReadNodesConf *)&signal->theData[0];
+
+ csystemnodes = readNodes->noOfNodes;
+ cmasterNodeId = readNodes->masterNodeId;
+
+ con_lineNodes = 0;
+ arrGuard(csystemnodes, MAX_NDB_NODES);
+ guard0 = csystemnodes - 1;
+ arrGuard(guard0, MAX_NDB_NODES); // Check not zero nodes
+
+ for (unsigned i = 1; i < MAX_NDB_NODES; i++) {
+ jam();
+ if (NodeBitmask::get(readNodes->allNodes, i)) {
+ hostptr.i = i;
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+
+ hostptr.p->takeOverStatus = TOS_IDLE;
+
+ if (NodeBitmask::get(readNodes->inactiveNodes, i)) {
+ jam();
+ hostptr.p->hostStatus = HS_DEAD;
+ } else {
+ jam();
+ con_lineNodes++;
+ hostptr.p->hostStatus = HS_ALIVE;
+ }//if
+ }//if
+ }//for
+ ndbsttorry010Lab(signal);
+}//Dbtc::execREAD_NODESCONF()
+
+/*****************************************************************************/
+/* A P I _ F A I L R E Q */
+// An API node has failed for some reason. We need to disconnect all API
+// connections to the API node. This also includes
+/*****************************************************************************/
+void Dbtc::execAPI_FAILREQ(Signal* signal)
+{
+ /***************************************************************************
+ * Set the block reference to return API_FAILCONF to. Set the number of api
+ * connects currently closing to one to indicate that we are still in the
+ * process of going through the api connect records. Thus checking for zero
+ * can only be true after all api connect records have been checked.
+ **************************************************************************/
+ jamEntry();
+ capiFailRef = signal->theData[1];
+ arrGuard(signal->theData[0], MAX_NODES);
+ capiConnectClosing[signal->theData[0]] = 1;
+ handleFailedApiNode(signal, signal->theData[0], (UintR)0);
+}
+
+void
+Dbtc::handleFailedApiNode(Signal* signal,
+ UintR TapiFailedNode,
+ UintR TapiConnectPtr)
+{
+ UintR TloopCount = 0;
+ arrGuard(TapiFailedNode, MAX_NODES);
+ apiConnectptr.i = TapiConnectPtr;
+ do {
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ const UintR TapiNode = refToNode(apiConnectptr.p->ndbapiBlockref);
+ if (TapiNode == TapiFailedNode) {
+#ifdef VM_TRACE
+ if (apiConnectptr.p->apiFailState != ZFALSE) {
+ ndbout << "Error in previous API fail handling discovered" << endl
+ << " apiConnectptr.i = " << apiConnectptr.i << endl
+ << " apiConnectstate = " << apiConnectptr.p->apiConnectstate
+ << endl
+ << " ndbapiBlockref = " << hex
+ << apiConnectptr.p->ndbapiBlockref << endl
+ << " apiNode = " << refToNode(apiConnectptr.p->ndbapiBlockref)
+ << endl;
+ if (apiConnectptr.p->lastTcConnect != RNIL){
+ jam();
+ tcConnectptr.i = apiConnectptr.p->lastTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ ndbout << " tcConnectptr.i = " << tcConnectptr.i << endl
+ << " tcConnectstate = " << tcConnectptr.p->tcConnectstate
+ << endl;
+ }
+ }//if
+#endif
+
+ apiConnectptr.p->returnsignal = RS_NO_RETURN;
+ /***********************************************************************/
+ // The connected node is the failed node.
+ /**********************************************************************/
+ switch(apiConnectptr.p->apiConnectstate) {
+ case CS_DISCONNECTED:
+ /*********************************************************************/
+ // These states do not need any special handling.
+ // Simply continue with the next.
+ /*********************************************************************/
+ jam();
+ break;
+ case CS_ABORTING:
+ /*********************************************************************/
+ // This could actually mean that the API connection is already
+ // ready to release if the abortState is IDLE.
+ /*********************************************************************/
+ if (apiConnectptr.p->abortState == AS_IDLE) {
+ jam();
+ releaseApiCon(signal, apiConnectptr.i);
+ } else {
+ jam();
+ capiConnectClosing[TapiFailedNode]++;
+ apiConnectptr.p->apiFailState = ZTRUE;
+ }//if
+ break;
+ case CS_WAIT_ABORT_CONF:
+ case CS_WAIT_COMMIT_CONF:
+ case CS_START_COMMITTING:
+ case CS_PREPARE_TO_COMMIT:
+ case CS_COMMITTING:
+ case CS_COMMIT_SENT:
+ /*********************************************************************/
+ // These states indicate that an abort process or commit process is
+ // already ongoing. We will set a state in the api record indicating
+ // that the API node has failed.
+ // Also we will increase the number of outstanding api records to
+ // wait for before we can respond with API_FAILCONF.
+ /*********************************************************************/
+ jam();
+ capiConnectClosing[TapiFailedNode]++;
+ apiConnectptr.p->apiFailState = ZTRUE;
+ break;
+ case CS_START_SCAN:
+ /*********************************************************************/
+ // The api record was performing a scan operation. We need to check
+ // on the scan state. Since completing a scan process might involve
+ // sending several signals we will increase the loop count by 64.
+ /*********************************************************************/
+ jam();
+
+ apiConnectptr.p->apiFailState = ZTRUE;
+ capiConnectClosing[TapiFailedNode]++;
+
+ ScanRecordPtr scanPtr;
+ scanPtr.i = apiConnectptr.p->apiScanRec;
+ ptrCheckGuard(scanPtr, cscanrecFileSize, scanRecord);
+ close_scan_req(signal, scanPtr, true);
+
+ TloopCount += 64;
+ break;
+ case CS_CONNECTED:
+ /*********************************************************************/
+ // The api record is connected to failed node. We need to release the
+ // connection and set it in a disconnected state.
+ /*********************************************************************/
+ jam();
+ releaseApiCon(signal, apiConnectptr.i);
+ break;
+ case CS_REC_COMMITTING:
+ case CS_RECEIVING:
+ case CS_STARTED:
+ /*********************************************************************/
+ // The api record was in the process of performing a transaction but
+ // had not yet sent all information.
+ // We need to initiate an ABORT since the API will not provide any
+ // more information.
+ // Since the abort can send many signals we will insert a real-time
+ // break after checking this record.
+ /*********************************************************************/
+ jam();
+ apiConnectptr.p->apiFailState = ZTRUE;
+ capiConnectClosing[TapiFailedNode]++;
+ abort010Lab(signal);
+ TloopCount = 256;
+ break;
+ case CS_PREPARED:
+ jam();
+ case CS_REC_PREPARING:
+ jam();
+ case CS_START_PREPARING:
+ jam();
+ /*********************************************************************/
+ // Not implemented yet.
+ /*********************************************************************/
+ systemErrorLab(signal);
+ break;
+ case CS_RESTART:
+ jam();
+ case CS_COMPLETING:
+ jam();
+ case CS_COMPLETE_SENT:
+ jam();
+ case CS_WAIT_COMPLETE_CONF:
+ jam();
+ case CS_FAIL_ABORTING:
+ jam();
+ case CS_FAIL_ABORTED:
+ jam();
+ case CS_FAIL_PREPARED:
+ jam();
+ case CS_FAIL_COMMITTING:
+ jam();
+ case CS_FAIL_COMMITTED:
+ /*********************************************************************/
+ // These states are only valid on copy and fail API connections.
+ /*********************************************************************/
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+ } else {
+ jam();
+ }//if
+ apiConnectptr.i++;
+ if (apiConnectptr.i > ((capiConnectFilesize / 3) - 1)) {
+ jam();
+ /**
+ * Finished with scanning connection record
+ *
+ * Now scan markers
+ */
+ removeMarkerForFailedAPI(signal, TapiFailedNode, 0);
+ return;
+ }//if
+ } while (TloopCount++ < 256);
+ signal->theData[0] = TcContinueB::ZHANDLE_FAILED_API_NODE;
+ signal->theData[1] = TapiFailedNode;
+ signal->theData[2] = apiConnectptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+}//Dbtc::handleFailedApiNode()
+
+void
+Dbtc::removeMarkerForFailedAPI(Signal* signal,
+ Uint32 nodeId,
+ Uint32 startBucket)
+{
+ TcFailRecordPtr node_fail_ptr;
+ node_fail_ptr.i = 0;
+ ptrAss(node_fail_ptr, tcFailRecord);
+ if(node_fail_ptr.p->failStatus != FS_IDLE) {
+ jam();
+ DEBUG("Restarting removeMarkerForFailedAPI");
+ /**
+ * TC take-over in progress
+ * needs to restart as this
+ * creates new markers
+ */
+ signal->theData[0] = TcContinueB::ZHANDLE_FAILED_API_NODE_REMOVE_MARKERS;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = 0;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 500, 3);
+ return;
+ }
+
+ CommitAckMarkerIterator iter;
+ m_commitAckMarkerHash.next(startBucket, iter);
+
+ const Uint32 RT_BREAK = 256;
+ for(Uint32 i = 0; i<RT_BREAK || iter.bucket == startBucket; i++){
+ jam();
+
+ if(iter.curr.i == RNIL){
+ jam();
+ /**
+ * Done with iteration
+ */
+ capiConnectClosing[nodeId]--;
+ if (capiConnectClosing[nodeId] == 0) {
+ jam();
+ /********************************************************************/
+ // No outstanding ABORT or COMMIT's of this failed API node.
+ // We can respond with API_FAILCONF
+ /********************************************************************/
+ signal->theData[0] = nodeId;
+ signal->theData[1] = cownref;
+ sendSignal(capiFailRef, GSN_API_FAILCONF, signal, 2, JBB);
+ }
+ return;
+ }
+
+ if(iter.curr.p->apiNodeId == nodeId){
+ jam();
+
+ /**
+ * Check so that the record is not still in use
+ *
+ */
+ ApiConnectRecordPtr apiConnectPtr;
+ apiConnectPtr.i = iter.curr.p->apiConnectPtr;
+ ptrCheckGuard(apiConnectPtr, capiConnectFilesize, apiConnectRecord);
+ if(apiConnectPtr.p->commitAckMarker == iter.curr.i){
+ jam();
+ /**
+ * The record is still active
+ *
+ * Don't remove it, but continueb instead
+ */
+ break;
+ }
+ sendRemoveMarkers(signal, iter.curr.p);
+ m_commitAckMarkerHash.release(iter.curr);
+
+ break;
+ }
+ m_commitAckMarkerHash.next(iter);
+ }
+
+ signal->theData[0] = TcContinueB::ZHANDLE_FAILED_API_NODE_REMOVE_MARKERS;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = iter.bucket;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+}
+
+void Dbtc::handleApiFailState(Signal* signal, UintR TapiConnectptr)
+{
+ ApiConnectRecordPtr TlocalApiConnectptr;
+ UintR TfailedApiNode;
+
+ TlocalApiConnectptr.i = TapiConnectptr;
+ ptrCheckGuard(TlocalApiConnectptr, capiConnectFilesize, apiConnectRecord);
+ TfailedApiNode = refToNode(TlocalApiConnectptr.p->ndbapiBlockref);
+ arrGuard(TfailedApiNode, MAX_NODES);
+ capiConnectClosing[TfailedApiNode]--;
+ releaseApiCon(signal, TapiConnectptr);
+ TlocalApiConnectptr.p->apiFailState = ZFALSE;
+ if (capiConnectClosing[TfailedApiNode] == 0) {
+ jam();
+ signal->theData[0] = TfailedApiNode;
+ signal->theData[1] = cownref;
+ sendSignal(capiFailRef, GSN_API_FAILCONF, signal, 2, JBB);
+ }//if
+}//Dbtc::handleApiFailState()
+
+/****************************************************************************
+ * T C S E I Z E R E Q
+ * THE APPLICATION SENDS A REQUEST TO SEIZE A CONNECT RECORD TO CARRY OUT A
+ * TRANSACTION
+ * TC BLOCK TAKE OUT A CONNECT RECORD FROM THE FREE LIST AND ESTABLISHES ALL
+ * NECESSARY CONNECTION BEFORE REPLYING TO THE APPLICATION BLOCK
+ ****************************************************************************/
+void Dbtc::execTCSEIZEREQ(Signal* signal)
+{
+ UintR tapiPointer;
+ BlockReference tapiBlockref; /* SENDER BLOCK REFERENCE*/
+
+ jamEntry();
+ tapiPointer = signal->theData[0]; /* REQUEST SENDERS CONNECT RECORD POINTER*/
+ tapiBlockref = signal->theData[1]; /* SENDERS BLOCK REFERENCE*/
+
+ const NodeState::StartLevel sl =
+ (NodeState::StartLevel)getNodeState().startLevel;
+
+ const NodeId senderNodeId = refToNode(tapiBlockref);
+ const bool local = senderNodeId == getOwnNodeId() || senderNodeId == 0;
+
+ if(!(senderNodeId == getNodeState().getSingleUserApi()) &&
+ !getNodeState().getSingleUserMode()) {
+ if(!(sl==NodeState::SL_SINGLEUSER &&
+ senderNodeId == getNodeState().getSingleUserApi())) {
+ if (!(sl == NodeState::SL_STARTED ||
+ (sl == NodeState::SL_STARTING && local == true))) {
+ jam();
+
+ Uint32 errCode;
+ if(!(sl == NodeState::SL_SINGLEUSER && local))
+ {
+ switch(sl){
+ case NodeState::SL_STARTING:
+ errCode = ZSYSTEM_NOT_STARTED_ERROR;
+ break;
+ case NodeState::SL_STOPPING_1:
+ case NodeState::SL_STOPPING_2:
+ case NodeState::SL_STOPPING_3:
+ case NodeState::SL_STOPPING_4:
+ if(getNodeState().stopping.systemShutdown)
+ errCode = ZCLUSTER_SHUTDOWN_IN_PROGRESS;
+ else
+ errCode = ZNODE_SHUTDOWN_IN_PROGRESS;
+ break;
+ case NodeState::SL_SINGLEUSER:
+ errCode = ZCLUSTER_IN_SINGLEUSER_MODE;
+ break;
+ default:
+ errCode = ZWRONG_STATE;
+ break;
+ }
+ signal->theData[0] = tapiPointer;
+ signal->theData[1] = errCode;
+ sendSignal(tapiBlockref, GSN_TCSEIZEREF, signal, 2, JBB);
+ return;
+ }//if (!(sl == SL_SINGLEUSER))
+ } //if
+ }
+ }
+
+ seizeApiConnect(signal);
+ if (terrorCode == ZOK) {
+ jam();
+ apiConnectptr.p->ndbapiConnect = tapiPointer;
+ apiConnectptr.p->ndbapiBlockref = tapiBlockref;
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = apiConnectptr.i;
+ sendSignal(tapiBlockref, GSN_TCSEIZECONF, signal, 2, JBB);
+ return;
+ }
+
+ signal->theData[0] = tapiPointer;
+ signal->theData[1] = terrorCode;
+ sendSignal(tapiBlockref, GSN_TCSEIZEREF, signal, 2, JBB);
+}//Dbtc::execTCSEIZEREQ()
+
+/****************************************************************************/
+/* T C R E L E A S E Q */
+/* REQUEST TO RELEASE A CONNECT RECORD */
+/****************************************************************************/
+void Dbtc::execTCRELEASEREQ(Signal* signal)
+{
+ UintR tapiPointer;
+ BlockReference tapiBlockref; /* SENDER BLOCK REFERENCE*/
+
+ jamEntry();
+ tapiPointer = signal->theData[0]; /* REQUEST SENDERS CONNECT RECORD POINTER*/
+ tapiBlockref = signal->theData[1];/* SENDERS BLOCK REFERENCE*/
+ tuserpointer = signal->theData[2];
+ if (tapiPointer >= capiConnectFilesize) {
+ jam();
+ signal->theData[0] = tuserpointer;
+ signal->theData[1] = ZINVALID_CONNECTION;
+ signal->theData[2] = __LINE__;
+ sendSignal(tapiBlockref, GSN_TCRELEASEREF, signal, 3, JBB);
+ return;
+ } else {
+ jam();
+ apiConnectptr.i = tapiPointer;
+ }//if
+ ptrAss(apiConnectptr, apiConnectRecord);
+ if (apiConnectptr.p->apiConnectstate == CS_DISCONNECTED) {
+ jam();
+ signal->theData[0] = tuserpointer;
+ sendSignal(tapiBlockref, GSN_TCRELEASECONF, signal, 1, JBB);
+ } else {
+ if (tapiBlockref == apiConnectptr.p->ndbapiBlockref) {
+ if (apiConnectptr.p->apiConnectstate == CS_CONNECTED ||
+ (apiConnectptr.p->apiConnectstate == CS_ABORTING &&
+ apiConnectptr.p->abortState == AS_IDLE) ||
+ (apiConnectptr.p->apiConnectstate == CS_STARTED &&
+ apiConnectptr.p->firstTcConnect == RNIL))
+ {
+ jam(); /* JUST REPLY OK */
+ releaseApiCon(signal, apiConnectptr.i);
+ signal->theData[0] = tuserpointer;
+ sendSignal(tapiBlockref,
+ GSN_TCRELEASECONF, signal, 1, JBB);
+ } else {
+ jam();
+ signal->theData[0] = tuserpointer;
+ signal->theData[1] = ZINVALID_CONNECTION;
+ signal->theData[2] = __LINE__;
+ signal->theData[3] = apiConnectptr.p->apiConnectstate;
+ sendSignal(tapiBlockref,
+ GSN_TCRELEASEREF, signal, 4, JBB);
+ }
+ } else {
+ jam();
+ signal->theData[0] = tuserpointer;
+ signal->theData[1] = ZINVALID_CONNECTION;
+ signal->theData[2] = __LINE__;
+ signal->theData[3] = tapiBlockref;
+ signal->theData[4] = apiConnectptr.p->ndbapiBlockref;
+ sendSignal(tapiBlockref, GSN_TCRELEASEREF, signal, 5, JBB);
+ }//if
+ }//if
+}//Dbtc::execTCRELEASEREQ()
+
+/****************************************************************************/
+// Error Handling for TCKEYREQ messages
+/****************************************************************************/
+void Dbtc::signalErrorRefuseLab(Signal* signal)
+{
+ ptrGuard(apiConnectptr);
+ if (apiConnectptr.p->apiConnectstate != CS_DISCONNECTED) {
+ jam();
+ apiConnectptr.p->abortState = AS_IDLE;
+ apiConnectptr.p->apiConnectstate = CS_ABORTING;
+ }//if
+ sendSignalErrorRefuseLab(signal);
+}//Dbtc::signalErrorRefuseLab()
+
+void Dbtc::sendSignalErrorRefuseLab(Signal* signal)
+{
+ ndbassert(false);
+ ptrGuard(apiConnectptr);
+ if (apiConnectptr.p->apiConnectstate != CS_DISCONNECTED) {
+ jam();
+ ndbrequire(false);
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = signal->theData[ttransid_ptr];
+ signal->theData[2] = signal->theData[ttransid_ptr + 1];
+ signal->theData[3] = ZSIGNAL_ERROR;
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_TCROLLBACKREP,
+ signal, 4, JBB);
+ }
+}//Dbtc::sendSignalErrorRefuseLab()
+
+void Dbtc::abortBeginErrorLab(Signal* signal)
+{
+ apiConnectptr.p->transid[0] = signal->theData[ttransid_ptr];
+ apiConnectptr.p->transid[1] = signal->theData[ttransid_ptr + 1];
+ abortErrorLab(signal);
+}//Dbtc::abortBeginErrorLab()
+
+void Dbtc::printState(Signal* signal, int place)
+{
+#ifdef VM_TRACE // Change to if 0 to disable these printouts
+ ndbout << "-- Dbtc::printState -- " << endl;
+ ndbout << "Received from place = " << place
+ << " apiConnectptr.i = " << apiConnectptr.i
+ << " apiConnectstate = " << apiConnectptr.p->apiConnectstate << endl;
+ ndbout << "ctcTimer = " << ctcTimer
+ << " ndbapiBlockref = " << hex <<apiConnectptr.p->ndbapiBlockref
+ << " Transid = " << apiConnectptr.p->transid[0]
+ << " " << apiConnectptr.p->transid[1] << endl;
+ ndbout << " apiTimer = " << getApiConTimer(apiConnectptr.i)
+ << " counter = " << apiConnectptr.p->counter
+ << " lqhkeyconfrec = " << apiConnectptr.p->lqhkeyconfrec
+ << " lqhkeyreqrec = " << apiConnectptr.p->lqhkeyreqrec << endl;
+ ndbout << "abortState = " << apiConnectptr.p->abortState
+ << " apiScanRec = " << apiConnectptr.p->apiScanRec
+ << " returncode = " << apiConnectptr.p->returncode << endl;
+ ndbout << "tckeyrec = " << apiConnectptr.p->tckeyrec
+ << " returnsignal = " << apiConnectptr.p->returnsignal
+ << " apiFailState = " << apiConnectptr.p->apiFailState << endl;
+ if (apiConnectptr.p->cachePtr != RNIL) {
+ jam();
+ CacheRecord *localCacheRecord = cacheRecord;
+ UintR TcacheFilesize = ccacheFilesize;
+ UintR TcachePtr = apiConnectptr.p->cachePtr;
+ if (TcachePtr < TcacheFilesize) {
+ jam();
+ CacheRecord * const regCachePtr = &localCacheRecord[TcachePtr];
+ ndbout << "currReclenAi = " << regCachePtr->currReclenAi
+ << " attrlength = " << regCachePtr->attrlength
+ << " tableref = " << regCachePtr->tableref
+ << " keylen = " << regCachePtr->keylen << endl;
+ } else {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ }//if
+#endif
+ return;
+}//Dbtc::printState()
+
+void
+Dbtc::TCKEY_abort(Signal* signal, int place)
+{
+ switch (place) {
+ case 0:
+ jam();
+ terrorCode = ZSTATE_ERROR;
+ apiConnectptr.p->firstTcConnect = RNIL;
+ printState(signal, 4);
+ abortBeginErrorLab(signal);
+ return;
+ case 1:
+ jam();
+ printState(signal, 3);
+ sendSignalErrorRefuseLab(signal);
+ return;
+ case 2:{
+ printState(signal, 6);
+ const TcKeyReq * const tcKeyReq = (TcKeyReq *)&signal->theData[0];
+ const Uint32 t1 = tcKeyReq->transId1;
+ const Uint32 t2 = tcKeyReq->transId2;
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = t1;
+ signal->theData[2] = t2;
+ signal->theData[3] = ZABORT_ERROR;
+ ndbrequire(false);
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_TCROLLBACKREP,
+ signal, 4, JBB);
+ return;
+ }
+ case 3:
+ jam();
+ printState(signal, 7);
+ noFreeConnectionErrorLab(signal);
+ return;
+ case 4:
+ jam();
+ terrorCode = ZERO_KEYLEN_ERROR;
+ releaseAtErrorLab(signal);
+ return;
+ case 5:
+ jam();
+ terrorCode = ZNO_AI_WITH_UPDATE;
+ releaseAtErrorLab(signal);
+ return;
+ case 6:
+ jam();
+ warningHandlerLab(signal);
+ return;
+
+ case 7:
+ jam();
+ tabStateErrorLab(signal);
+ return;
+
+ case 8:
+ jam();
+ wrongSchemaVersionErrorLab(signal);
+ return;
+
+ case 9:
+ jam();
+ terrorCode = ZSTATE_ERROR;
+ releaseAtErrorLab(signal);
+ return;
+
+ case 10:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 11:
+ jam();
+ terrorCode = ZMORE_AI_IN_TCKEYREQ_ERROR;
+ releaseAtErrorLab(signal);
+ return;
+
+ case 12:
+ jam();
+ terrorCode = ZSIMPLE_READ_WITHOUT_AI;
+ releaseAtErrorLab(signal);
+ return;
+
+ case 13:
+ jam();
+ switch (tcConnectptr.p->tcConnectstate) {
+ case OS_WAIT_KEYINFO:
+ jam();
+ printState(signal, 8);
+ terrorCode = ZSTATE_ERROR;
+ abortErrorLab(signal);
+ return;
+ default:
+ jam();
+ /********************************************************************/
+ /* MISMATCH BETWEEN STATE ON API CONNECTION AND THIS */
+ /* PARTICULAR TC CONNECT RECORD. THIS MUST BE CAUSED BY NDB */
+ /* INTERNAL ERROR. */
+ /********************************************************************/
+ systemErrorLab(signal);
+ return;
+ }//switch
+ return;
+
+ case 15:
+ jam();
+ terrorCode = ZSCAN_NODE_ERROR;
+ releaseAtErrorLab(signal);
+ return;
+
+ case 16:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 17:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 18:
+ jam();
+ warningHandlerLab(signal);
+ return;
+
+ case 19:
+ jam();
+ return;
+
+ case 20:
+ jam();
+ warningHandlerLab(signal);
+ return;
+
+ case 21:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 22:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 23:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 24:
+ jam();
+ seizeAttrbuferrorLab(signal);
+ return;
+
+ case 25:
+ jam();
+ warningHandlerLab(signal);
+ return;
+
+ case 26:
+ jam();
+ return;
+
+ case 27:
+ systemErrorLab(signal);
+ jam();
+ return;
+
+ case 28:
+ jam();
+ // NOT USED
+ return;
+
+ case 29:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 30:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 31:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 32:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 33:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 34:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 35:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 36:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 37:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 38:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 39:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 40:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 41:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 42:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 43:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 44:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 45:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 46:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 47:
+ jam();
+ terrorCode = apiConnectptr.p->returncode;
+ releaseAtErrorLab(signal);
+ return;
+
+ case 48:
+ jam();
+ terrorCode = ZCOMMIT_TYPE_ERROR;
+ releaseAtErrorLab(signal);
+ return;
+
+ case 49:
+ jam();
+ abortErrorLab(signal);
+ return;
+
+ case 50:
+ jam();
+ systemErrorLab(signal);
+ return;
+
+ case 51:
+ jam();
+ abortErrorLab(signal);
+ return;
+
+ case 52:
+ jam();
+ abortErrorLab(signal);
+ return;
+
+ case 53:
+ jam();
+ abortErrorLab(signal);
+ return;
+
+ case 54:
+ jam();
+ abortErrorLab(signal);
+ return;
+
+ case 55:
+ jam();
+ printState(signal, 5);
+ sendSignalErrorRefuseLab(signal);
+ return;
+
+ case 56:{
+ jam();
+ terrorCode = ZNO_FREE_TC_MARKER;
+ abortErrorLab(signal);
+ return;
+ }
+ case 57:{
+ jam();
+ /**
+ * Initialize object before starting error handling
+ */
+ initApiConnectRec(signal, apiConnectptr.p, true);
+ switch(getNodeState().startLevel){
+ case NodeState::SL_STOPPING_2:
+ case NodeState::SL_STOPPING_3:
+ case NodeState::SL_STOPPING_4:
+ if(getNodeState().stopping.systemShutdown)
+ terrorCode = ZCLUSTER_SHUTDOWN_IN_PROGRESS;
+ else
+ terrorCode = ZNODE_SHUTDOWN_IN_PROGRESS;
+ break;
+ case NodeState::SL_SINGLEUSER:
+ terrorCode = ZCLUSTER_IN_SINGLEUSER_MODE;
+ break;
+ default:
+ terrorCode = ZWRONG_STATE;
+ break;
+ }
+ abortErrorLab(signal);
+ return;
+ }
+
+ case 58:{
+ jam();
+ releaseAtErrorLab(signal);
+ return;
+ }
+
+ case 59:{
+ jam();
+ terrorCode = ZABORTINPROGRESS;
+ abortErrorLab(signal);
+ return;
+ }
+
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//switch
+}
+
+void Dbtc::execKEYINFO(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+ jamEntry();
+ apiConnectptr.i = signal->theData[0];
+ tmaxData = 20;
+ if (apiConnectptr.i >= capiConnectFilesize) {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+ ptrAss(apiConnectptr, apiConnectRecord);
+ ttransid_ptr = 1;
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[1];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[2];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ jam();
+ printState(signal, 10);
+ sendSignalErrorRefuseLab(signal);
+ return;
+ }//if
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_RECEIVING:
+ case CS_REC_COMMITTING:
+ case CS_START_SCAN:
+ jam();
+ /*empty*/;
+ break;
+ /* OK */
+ case CS_ABORTING:
+ jam();
+ return; /* IGNORE */
+ case CS_CONNECTED:
+ jam();
+ /****************************************************************>*/
+ /* MOST LIKELY CAUSED BY A MISSED SIGNAL. SEND REFUSE AND */
+ /* SET STATE TO ABORTING. */
+ /****************************************************************>*/
+ printState(signal, 11);
+ signalErrorRefuseLab(signal);
+ return;
+ case CS_STARTED:
+ jam();
+ /****************************************************************>*/
+ /* MOST LIKELY CAUSED BY A MISSED SIGNAL. SEND REFUSE AND */
+ /* SET STATE TO ABORTING. SINCE A TRANSACTION WAS STARTED */
+ /* WE ALSO NEED TO ABORT THIS TRANSACTION. */
+ /****************************************************************>*/
+ terrorCode = ZSIGNAL_ERROR;
+ printState(signal, 2);
+ abortErrorLab(signal);
+ return;
+ default:
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//switch
+
+ CacheRecord *localCacheRecord = cacheRecord;
+ UintR TcacheFilesize = ccacheFilesize;
+ UintR TcachePtr = apiConnectptr.p->cachePtr;
+ UintR TtcTimer = ctcTimer;
+ CacheRecord * const regCachePtr = &localCacheRecord[TcachePtr];
+ if (TcachePtr >= TcacheFilesize) {
+ TCKEY_abort(signal, 42);
+ return;
+ }//if
+ setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
+ cachePtr.i = TcachePtr;
+ cachePtr.p = regCachePtr;
+
+ tcConnectptr.i = apiConnectptr.p->lastTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ switch (tcConnectptr.p->tcConnectstate) {
+ case OS_WAIT_KEYINFO:
+ jam();
+ tckeyreq020Lab(signal);
+ return;
+ case OS_WAIT_SCAN:
+ break;
+ default:
+ jam();
+ terrorCode = ZSTATE_ERROR;
+ abortErrorLab(signal);
+ return;
+ }//switch
+
+ UintR TdataPos = 0;
+ UintR TkeyLen = regCachePtr->keylen;
+ UintR Tlen = regCachePtr->save1;
+
+ do {
+ if (cfirstfreeDatabuf == RNIL) {
+ jam();
+ abort();
+ seizeDatabuferrorLab(signal);
+ return;
+ }//if
+ linkKeybuf(signal);
+ arrGuard(TdataPos, 19);
+ databufptr.p->data[0] = signal->theData[TdataPos + 3];
+ databufptr.p->data[1] = signal->theData[TdataPos + 4];
+ databufptr.p->data[2] = signal->theData[TdataPos + 5];
+ databufptr.p->data[3] = signal->theData[TdataPos + 6];
+ Tlen = Tlen + 4;
+ TdataPos = TdataPos + 4;
+ if (Tlen < TkeyLen) {
+ jam();
+ if (TdataPos >= tmaxData) {
+ jam();
+ /*----------------------------------------------------*/
+ /** EXIT AND WAIT FOR SIGNAL KEYINFO OR KEYINFO9 **/
+ /** WHEN EITHER OF THE SIGNALS IS RECEIVED A JUMP **/
+ /** TO LABEL "KEYINFO_LABEL" IS DONE. THEN THE **/
+ /** PROGRAM RETURNS TO LABEL TCKEYREQ020 **/
+ /*----------------------------------------------------*/
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ regCachePtr->save1 = Tlen;
+ return;
+ }//if
+ } else {
+ jam();
+ return;
+ }//if
+ } while (1);
+ return;
+}//Dbtc::execKEYINFO()
+
+/*---------------------------------------------------------------------------*/
+/* */
+/* MORE THAN FOUR WORDS OF KEY DATA. WE NEED TO PACK THIS IN KEYINFO SIGNALS.*/
+/* WE WILL ALWAYS PACK 4 WORDS AT A TIME. */
+/*---------------------------------------------------------------------------*/
+void Dbtc::packKeyData000Lab(Signal* signal,
+ BlockReference TBRef,
+ Uint32 totalLen)
+{
+ CacheRecord * const regCachePtr = cachePtr.p;
+
+ jam();
+ Uint32 len = 0;
+ databufptr.i = regCachePtr->firstKeybuf;
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ Uint32 * dst = signal->theData+3;
+ ptrCheckGuard(databufptr, cdatabufFilesize, databufRecord);
+
+ do {
+ jam();
+ databufptr.i = databufptr.p->nextDatabuf;
+ dst[len + 0] = databufptr.p->data[0];
+ dst[len + 1] = databufptr.p->data[1];
+ dst[len + 2] = databufptr.p->data[2];
+ dst[len + 3] = databufptr.p->data[3];
+ len += 4;
+ if (totalLen <= 4) {
+ jam();
+ /*---------------------------------------------------------------------*/
+ /* LAST PACK OF KEY DATA HAVE BEEN SENT */
+ /*---------------------------------------------------------------------*/
+ /* THERE WERE UNSENT INFORMATION, SEND IT. */
+ /*---------------------------------------------------------------------*/
+ sendSignal(TBRef, GSN_KEYINFO, signal, 3 + len, JBB);
+ return;
+ } else if(len == KeyInfo::DataLength){
+ jam();
+ len = 0;
+ sendSignal(TBRef, GSN_KEYINFO, signal, 3 + KeyInfo::DataLength, JBB);
+ }
+ totalLen -= 4;
+ ptrCheckGuard(databufptr, cdatabufFilesize, databufRecord);
+ } while (1);
+}//Dbtc::packKeyData000Lab()
+
+void Dbtc::tckeyreq020Lab(Signal* signal)
+{
+ CacheRecord * const regCachePtr = cachePtr.p;
+ UintR TdataPos = 0;
+ UintR TkeyLen = regCachePtr->keylen;
+ UintR Tlen = regCachePtr->save1;
+
+ do {
+ if (cfirstfreeDatabuf == RNIL) {
+ jam();
+ seizeDatabuferrorLab(signal);
+ return;
+ }//if
+ linkKeybuf(signal);
+ arrGuard(TdataPos, 19);
+ databufptr.p->data[0] = signal->theData[TdataPos + 3];
+ databufptr.p->data[1] = signal->theData[TdataPos + 4];
+ databufptr.p->data[2] = signal->theData[TdataPos + 5];
+ databufptr.p->data[3] = signal->theData[TdataPos + 6];
+ Tlen = Tlen + 4;
+ TdataPos = TdataPos + 4;
+ if (Tlen < TkeyLen) {
+ jam();
+ if (TdataPos >= tmaxData) {
+ jam();
+ /*----------------------------------------------------*/
+ /** EXIT AND WAIT FOR SIGNAL KEYINFO OR KEYINFO9 **/
+ /** WHEN EITHER OF THE SIGNALS IS RECEIVED A JUMP **/
+ /** TO LABEL "KEYINFO_LABEL" IS DONE. THEN THE **/
+ /** PROGRAM RETURNS TO LABEL TCKEYREQ020 **/
+ /*----------------------------------------------------*/
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ regCachePtr->save1 = Tlen;
+ tcConnectptr.p->tcConnectstate = OS_WAIT_KEYINFO;
+ return;
+ }//if
+ } else {
+ jam();
+ tckeyreq050Lab(signal);
+ return;
+ }//if
+ } while (1);
+ return;
+}//Dbtc::tckeyreq020Lab()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SAVE ATTRIBUTE INFORMATION IN OPERATION RECORD ------- */
+/* ------------------------------------------------------------------------- */
+void Dbtc::saveAttrbuf(Signal* signal)
+{
+ CacheRecord * const regCachePtr = cachePtr.p;
+ UintR TfirstfreeAttrbuf = cfirstfreeAttrbuf;
+ UintR TattrbufFilesize = cattrbufFilesize;
+ UintR TTcfirstAttrbuf = regCachePtr->firstAttrbuf;
+ UintR Tlen = signal->length() - 3;
+ AttrbufRecord *localAttrbufRecord = attrbufRecord;
+
+ AttrbufRecord * const regAttrPtr = &localAttrbufRecord[TfirstfreeAttrbuf];
+ if (TfirstfreeAttrbuf >= TattrbufFilesize) {
+ TCKEY_abort(signal, 21);
+ return;
+ }//if
+ UintR Tnext = regAttrPtr->attrbuf[ZINBUF_NEXT];
+ if (TTcfirstAttrbuf == RNIL) {
+ jam();
+ regCachePtr->firstAttrbuf = TfirstfreeAttrbuf;
+ } else {
+ AttrbufRecordPtr saAttrbufptr;
+
+ saAttrbufptr.i = regCachePtr->lastAttrbuf;
+ jam();
+ if (saAttrbufptr.i >= TattrbufFilesize) {
+ TCKEY_abort(signal, 22);
+ return;
+ }//if
+ saAttrbufptr.p = &localAttrbufRecord[saAttrbufptr.i];
+ saAttrbufptr.p->attrbuf[ZINBUF_NEXT] = TfirstfreeAttrbuf;
+ }//if
+
+ cfirstfreeAttrbuf = Tnext;
+ regAttrPtr->attrbuf[ZINBUF_NEXT] = RNIL;
+ regCachePtr->lastAttrbuf = TfirstfreeAttrbuf;
+ regAttrPtr->attrbuf[ZINBUF_DATA_LEN] = Tlen;
+
+ UintR Tdata1 = signal->theData[3];
+ UintR Tdata2 = signal->theData[4];
+ UintR Tdata3 = signal->theData[5];
+ UintR Tdata4 = signal->theData[6];
+ UintR Tdata5 = signal->theData[7];
+ UintR Tdata6 = signal->theData[8];
+ UintR Tdata7 = signal->theData[9];
+ UintR Tdata8 = signal->theData[10];
+
+ regAttrPtr->attrbuf[0] = Tdata1;
+ regAttrPtr->attrbuf[1] = Tdata2;
+ regAttrPtr->attrbuf[2] = Tdata3;
+ regAttrPtr->attrbuf[3] = Tdata4;
+ regAttrPtr->attrbuf[4] = Tdata5;
+ regAttrPtr->attrbuf[5] = Tdata6;
+ regAttrPtr->attrbuf[6] = Tdata7;
+ regAttrPtr->attrbuf[7] = Tdata8;
+
+ if (Tlen > 8) {
+
+ Tdata1 = signal->theData[11];
+ Tdata2 = signal->theData[12];
+ Tdata3 = signal->theData[13];
+ Tdata4 = signal->theData[14];
+ Tdata5 = signal->theData[15];
+ Tdata6 = signal->theData[16];
+ Tdata7 = signal->theData[17];
+
+ regAttrPtr->attrbuf[8] = Tdata1;
+ regAttrPtr->attrbuf[9] = Tdata2;
+ regAttrPtr->attrbuf[10] = Tdata3;
+ regAttrPtr->attrbuf[11] = Tdata4;
+ regAttrPtr->attrbuf[12] = Tdata5;
+ regAttrPtr->attrbuf[13] = Tdata6;
+ regAttrPtr->attrbuf[14] = Tdata7;
+ jam();
+ if (Tlen > 15) {
+
+ Tdata1 = signal->theData[18];
+ Tdata2 = signal->theData[19];
+ Tdata3 = signal->theData[20];
+ Tdata4 = signal->theData[21];
+ Tdata5 = signal->theData[22];
+ Tdata6 = signal->theData[23];
+ Tdata7 = signal->theData[24];
+
+ jam();
+ regAttrPtr->attrbuf[15] = Tdata1;
+ regAttrPtr->attrbuf[16] = Tdata2;
+ regAttrPtr->attrbuf[17] = Tdata3;
+ regAttrPtr->attrbuf[18] = Tdata4;
+ regAttrPtr->attrbuf[19] = Tdata5;
+ regAttrPtr->attrbuf[20] = Tdata6;
+ regAttrPtr->attrbuf[21] = Tdata7;
+ }//if
+ }//if
+}//Dbtc::saveAttrbuf()
+
+void Dbtc::execATTRINFO(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+ UintR Tdata1 = signal->theData[0];
+ UintR Tlength = signal->length();
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+ jamEntry();
+ apiConnectptr.i = Tdata1;
+ ttransid_ptr = 1;
+ if (Tdata1 >= TapiConnectFilesize) {
+ DEBUG("Drop ATTRINFO, wrong apiConnectptr");
+ TCKEY_abort(signal, 18);
+ return;
+ }//if
+
+ UintR Tdata2 = signal->theData[1];
+ UintR Tdata3 = signal->theData[2];
+ ApiConnectRecord * const regApiPtr = &localApiConnectRecord[Tdata1];
+ compare_transid1 = regApiPtr->transid[0] ^ Tdata2;
+ compare_transid2 = regApiPtr->transid[1] ^ Tdata3;
+ apiConnectptr.p = regApiPtr;
+ compare_transid1 = compare_transid1 | compare_transid2;
+
+ if (compare_transid1 != 0) {
+ DEBUG("Drop ATTRINFO, wrong transid, lenght="<<Tlength
+ << " transid("<<hex<<Tdata2<<", "<<Tdata3);
+ TCKEY_abort(signal, 19);
+ return;
+ }//if
+ if (Tlength < 4) {
+ DEBUG("Drop ATTRINFO, wrong length = " << Tlength);
+ TCKEY_abort(signal, 20);
+ return;
+ }
+ Tlength -= 3;
+ UintR TcompREC_COMMIT = (regApiPtr->apiConnectstate == CS_REC_COMMITTING);
+ UintR TcompRECEIVING = (regApiPtr->apiConnectstate == CS_RECEIVING);
+ UintR TcompBOTH = TcompREC_COMMIT | TcompRECEIVING;
+
+ if (TcompBOTH) {
+ jam();
+ if (ERROR_INSERTED(8015)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ if (ERROR_INSERTED(8016)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ CacheRecord *localCacheRecord = cacheRecord;
+ UintR TcacheFilesize = ccacheFilesize;
+ UintR TcachePtr = regApiPtr->cachePtr;
+ UintR TtcTimer = ctcTimer;
+ CacheRecord * const regCachePtr = &localCacheRecord[TcachePtr];
+ if (TcachePtr >= TcacheFilesize) {
+ TCKEY_abort(signal, 43);
+ return;
+ }//if
+ UintR TfirstfreeAttrbuf = cfirstfreeAttrbuf;
+ UintR TcurrReclenAi = regCachePtr->currReclenAi;
+ UintR TattrLen = regCachePtr->attrlength;
+
+ setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
+ cachePtr.i = TcachePtr;
+ cachePtr.p = regCachePtr;
+ TcurrReclenAi = TcurrReclenAi + Tlength;
+ regCachePtr->currReclenAi = TcurrReclenAi;
+ int TattrlengthRemain = TattrLen - TcurrReclenAi;
+
+ if (TfirstfreeAttrbuf == RNIL) {
+ DEBUG("No more attrinfo buffers");
+ TCKEY_abort(signal, 24);
+ return;
+ }//if
+ saveAttrbuf(signal);
+ if (TattrlengthRemain == 0) {
+ /****************************************************************>*/
+ /* HERE WE HAVE FOUND THAT THE LAST SIGNAL BELONGING TO THIS */
+ /* OPERATION HAVE BEEN RECEIVED. THIS MEANS THAT WE CAN NOW REUSE */
+ /* THE API CONNECT RECORD. HOWEVER IF PREPARE OR COMMIT HAVE BEEN */
+ /* RECEIVED THEN IT IS NOT ALLOWED TO RECEIVE ANY FURTHER */
+ /* OPERATIONS. */
+ /****************************************************************>*/
+ UintR TlastConnect = regApiPtr->lastTcConnect;
+ if (TcompRECEIVING) {
+ jam();
+ regApiPtr->apiConnectstate = CS_STARTED;
+ } else {
+ jam();
+ regApiPtr->apiConnectstate = CS_START_COMMITTING;
+ }//if
+ tcConnectptr.i = TlastConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ attrinfoDihReceivedLab(signal);
+ } else if (TattrlengthRemain < 0) {
+ jam();
+ DEBUG("ATTRINFO wrong total length="<<Tlength
+ <<", TattrlengthRemain="<<TattrlengthRemain
+ <<", TattrLen="<<TattrLen
+ <<", TcurrReclenAi="<<TcurrReclenAi);
+ tcConnectptr.i = regApiPtr->lastTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ aiErrorLab(signal);
+ }//if
+ return;
+ } else if (regApiPtr->apiConnectstate == CS_START_SCAN) {
+ jam();
+ scanAttrinfoLab(signal, Tlength);
+ return;
+ } else {
+ switch (regApiPtr->apiConnectstate) {
+ case CS_ABORTING:
+ jam();
+ /* JUST IGNORE THE SIGNAL*/
+ // DEBUG("Drop ATTRINFO, CS_ABORTING");
+ return;
+ case CS_CONNECTED:
+ jam();
+ /* MOST LIKELY CAUSED BY A MISSED SIGNAL.*/
+ // DEBUG("Drop ATTRINFO, CS_CONNECTED");
+ return;
+ case CS_STARTED:
+ jam();
+ /****************************************************************>*/
+ /* MOST LIKELY CAUSED BY A MISSED SIGNAL. SEND REFUSE AND */
+ /* SET STATE TO ABORTING. SINCE A TRANSACTION WAS STARTED */
+ /* WE ALSO NEED TO ABORT THIS TRANSACTION. */
+ /****************************************************************>*/
+ terrorCode = ZSIGNAL_ERROR;
+ printState(signal, 1);
+ abortErrorLab(signal);
+ return;
+ default:
+ jam();
+ /****************************************************************>*/
+ /* SIGNAL RECEIVED IN AN UNEXPECTED STATE. WE IGNORE SIGNAL */
+ /* SINCE WE DO NOT REALLY KNOW WHERE THE ERROR OCCURRED. */
+ /****************************************************************>*/
+ DEBUG("Drop ATTRINFO, illegal state="<<regApiPtr->apiConnectstate);
+ printState(signal, 9);
+ return;
+ }//switch
+ }//if
+}//Dbtc::execATTRINFO()
+
+/* *********************************************************************>> */
+/* */
+/* MODULE: HASH MODULE */
+/* DESCRIPTION: CONTAINS THE HASH VALUE CALCULATION */
+/* *********************************************************************> */
+void Dbtc::hash(Signal* signal)
+{
+ DatabufRecordPtr locDatabufptr;
+ UintR ti;
+ UintR Tdata0;
+ UintR Tdata1;
+ UintR Tdata2;
+ UintR Tdata3;
+ UintR* Tdata32;
+
+ CacheRecord * const regCachePtr = cachePtr.p;
+ Tdata32 = signal->theData;
+
+ Tdata0 = regCachePtr->keydata[0];
+ Tdata1 = regCachePtr->keydata[1];
+ Tdata2 = regCachePtr->keydata[2];
+ Tdata3 = regCachePtr->keydata[3];
+ Tdata32[0] = Tdata0;
+ Tdata32[1] = Tdata1;
+ Tdata32[2] = Tdata2;
+ Tdata32[3] = Tdata3;
+ if (regCachePtr->keylen > 4) {
+ locDatabufptr.i = regCachePtr->firstKeybuf;
+ ti = 4;
+ while (locDatabufptr.i != RNIL) {
+ ptrCheckGuard(locDatabufptr, cdatabufFilesize, databufRecord);
+ Tdata0 = locDatabufptr.p->data[0];
+ Tdata1 = locDatabufptr.p->data[1];
+ Tdata2 = locDatabufptr.p->data[2];
+ Tdata3 = locDatabufptr.p->data[3];
+ Tdata32[ti ] = Tdata0;
+ Tdata32[ti + 1] = Tdata1;
+ Tdata32[ti + 2] = Tdata2;
+ Tdata32[ti + 3] = Tdata3;
+ locDatabufptr.i = locDatabufptr.p->nextDatabuf;
+ ti += 4;
+ }//while
+ }//if
+
+ UintR keylen = (UintR)regCachePtr->keylen;
+ Uint32 distKey = regCachePtr->distributionKeyIndicator;
+
+ Uint32 tmp[4];
+ if(!regCachePtr->m_special_hash)
+ {
+ md5_hash(tmp, (Uint64*)&Tdata32[0], keylen);
+ }
+ else
+ {
+ handle_special_hash(tmp, Tdata32, keylen, regCachePtr->tableref, !distKey);
+ }
+
+ thashValue = tmp[0];
+ if (distKey){
+ jam();
+ tdistrHashValue = regCachePtr->distributionKey;
+ } else {
+ jam();
+ tdistrHashValue = tmp[1];
+ }//if
+}//Dbtc::hash()
+
+bool
+Dbtc::handle_special_hash(Uint32 dstHash[4], Uint32* src, Uint32 srcLen,
+ Uint32 tabPtrI,
+ bool distr)
+{
+ Uint64 Tmp[MAX_KEY_SIZE_IN_WORDS * 4 * MAX_XFRM_MULTIPLY];
+ const Uint32 dstSize = sizeof(Tmp) / 4;
+ const TableRecord* tabPtrP = &tableRecord[tabPtrI];
+ const Uint32 noOfKeyAttr = tabPtrP->noOfKeyAttr;
+ Uint32 noOfDistrKeys = tabPtrP->noOfDistrKeys;
+ const bool hasCharAttr = tabPtrP->hasCharAttr;
+
+ Uint32 *dst = (Uint32*)Tmp;
+ Uint32 dstPos = 0;
+ Uint32 srcPos = 0;
+ Uint32 keyPartLen[MAX_ATTRIBUTES_IN_INDEX];
+ if(hasCharAttr){
+ Uint32 i = 0;
+ while (i < noOfKeyAttr) {
+ const TableRecord::KeyAttr& keyAttr = tabPtrP->keyAttr[i];
+
+ Uint32 srcBytes =
+ AttributeDescriptor::getSizeInBytes(keyAttr.attributeDescriptor);
+ Uint32 srcWords = (srcBytes + 3) / 4;
+ Uint32 dstWords = ~0;
+ uchar* dstPtr = (uchar*)&dst[dstPos];
+ const uchar* srcPtr = (const uchar*)&src[srcPos];
+ CHARSET_INFO* cs = keyAttr.charsetInfo;
+
+ if (cs == NULL) {
+ jam();
+ memcpy(dstPtr, srcPtr, srcWords << 2);
+ dstWords = srcWords;
+ } else {
+ jam();
+ Uint32 typeId =
+ AttributeDescriptor::getType(keyAttr.attributeDescriptor);
+ Uint32 lb, len;
+ bool ok = NdbSqlUtil::get_var_length(typeId, srcPtr, srcBytes, lb, len);
+ ndbrequire(ok);
+ Uint32 xmul = cs->strxfrm_multiply;
+ if (xmul == 0)
+ xmul = 1;
+ /*
+ * Varchar is really Char. End spaces do not matter. To get
+ * same hash we blank-pad to maximum length via strnxfrm.
+ * TODO use MySQL charset-aware hash function instead
+ */
+ Uint32 dstLen = xmul * (srcBytes - lb);
+ ndbrequire(dstLen <= ((dstSize - dstPos) << 2));
+ int n = NdbSqlUtil::strnxfrm_bug7284(cs, dstPtr, dstLen, srcPtr + lb, len);
+ ndbrequire(n != -1);
+ while ((n & 3) != 0) {
+ dstPtr[n++] = 0;
+ }
+ dstWords = (n >> 2);
+ }
+ dstPos += dstWords;
+ srcPos += srcWords;
+ keyPartLen[i++] = dstWords;
+ }
+ }
+ else
+ {
+ dst = src;
+ dstPos = srcLen;
+ }
+
+ md5_hash(dstHash, (Uint64*)dst, dstPos);
+
+ if(distr && noOfDistrKeys)
+ {
+ jam();
+ src = dst;
+ dstPos = 0;
+ Uint32 i = 0;
+ if(hasCharAttr)
+ {
+ while (i < noOfKeyAttr && noOfDistrKeys)
+ {
+ const TableRecord::KeyAttr& keyAttr = tabPtrP->keyAttr[i];
+ Uint32 len = keyPartLen[i];
+ if(AttributeDescriptor::getDKey(keyAttr.attributeDescriptor))
+ {
+ noOfDistrKeys--;
+ memmove(dst+dstPos, src, len << 2);
+ dstPos += len;
+ }
+ src += len;
+ i++;
+ }
+ }
+ else
+ {
+ while (i < noOfKeyAttr && noOfDistrKeys)
+ {
+ const TableRecord::KeyAttr& keyAttr = tabPtrP->keyAttr[i];
+ Uint32 len =
+ AttributeDescriptor::getSizeInBytes(keyAttr.attributeDescriptor);
+ len = (len + 3) / 4;
+ if(AttributeDescriptor::getDKey(keyAttr.attributeDescriptor))
+ {
+ noOfDistrKeys--;
+ memmove(dst+dstPos, src, len << 2);
+ dstPos += len;
+ }
+ src += len;
+ i++;
+ }
+ }
+ Uint32 tmp[4];
+ md5_hash(tmp, (Uint64*)dst, dstPos);
+ dstHash[1] = tmp[1];
+ }
+ return true; // success
+}
+
+/*
+INIT_API_CONNECT_REC
+---------------------------
+*/
+/* ========================================================================= */
+/* ======= INIT_API_CONNECT_REC ======= */
+/* */
+/* ========================================================================= */
+void Dbtc::initApiConnectRec(Signal* signal,
+ ApiConnectRecord * const regApiPtr,
+ bool releaseIndexOperations)
+{
+ const TcKeyReq * const tcKeyReq = (TcKeyReq *)&signal->theData[0];
+ UintR TfailureNr = cfailure_nr;
+ UintR TtransCount = c_counters.ctransCount;
+ UintR Ttransid0 = tcKeyReq->transId1;
+ UintR Ttransid1 = tcKeyReq->transId2;
+
+ regApiPtr->m_exec_flag = 0;
+ regApiPtr->returncode = 0;
+ regApiPtr->returnsignal = RS_TCKEYCONF;
+ ndbassert(regApiPtr->firstTcConnect == RNIL);
+ regApiPtr->firstTcConnect = RNIL;
+ regApiPtr->lastTcConnect = RNIL;
+ regApiPtr->globalcheckpointid = 0;
+ regApiPtr->lqhkeyconfrec = 0;
+ regApiPtr->lqhkeyreqrec = 0;
+ regApiPtr->tckeyrec = 0;
+ regApiPtr->tcindxrec = 0;
+ regApiPtr->failureNr = TfailureNr;
+ regApiPtr->transid[0] = Ttransid0;
+ regApiPtr->transid[1] = Ttransid1;
+ regApiPtr->commitAckMarker = RNIL;
+ regApiPtr->buddyPtr = RNIL;
+ regApiPtr->currSavePointId = 0;
+ // Trigger data
+ releaseFiredTriggerData(&regApiPtr->theFiredTriggers),
+ // Index data
+ regApiPtr->indexOpReturn = false;
+ regApiPtr->noIndexOp = 0;
+ if(releaseIndexOperations)
+ releaseAllSeizedIndexOperations(regApiPtr);
+
+ c_counters.ctransCount = TtransCount + 1;
+}//Dbtc::initApiConnectRec()
+
+int
+Dbtc::seizeTcRecord(Signal* signal)
+{
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+ UintR TfirstfreeTcConnect = cfirstfreeTcConnect;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ tcConnectptr.i = TfirstfreeTcConnect;
+ if (TfirstfreeTcConnect >= TtcConnectFilesize) {
+ int place = 3;
+ if (TfirstfreeTcConnect != RNIL) {
+ place = 10;
+ }//if
+ TCKEY_abort(signal, place);
+ return 1;
+ }//if
+ //--------------------------------------------------------------------------
+ // Optimised version of ptrAss(tcConnectptr, tcConnectRecord)
+ //--------------------------------------------------------------------------
+ TcConnectRecord * const regTcPtr =
+ &localTcConnectRecord[TfirstfreeTcConnect];
+
+ UintR TconcurrentOp = c_counters.cconcurrentOp;
+ UintR TlastTcConnect = regApiPtr->lastTcConnect;
+ UintR TtcConnectptrIndex = tcConnectptr.i;
+ TcConnectRecordPtr tmpTcConnectptr;
+
+ cfirstfreeTcConnect = regTcPtr->nextTcConnect;
+ tcConnectptr.p = regTcPtr;
+
+ c_counters.cconcurrentOp = TconcurrentOp + 1;
+ regTcPtr->prevTcConnect = TlastTcConnect;
+ regTcPtr->nextTcConnect = RNIL;
+ regTcPtr->accumulatingTriggerData.i = RNIL;
+ regTcPtr->accumulatingTriggerData.p = NULL;
+ regTcPtr->noFiredTriggers = 0;
+ regTcPtr->noReceivedTriggers = 0;
+ regTcPtr->triggerExecutionCount = 0;
+ regTcPtr->triggeringOperation = RNIL;
+ regTcPtr->isIndexOp = false;
+ regTcPtr->indexOp = RNIL;
+ regTcPtr->currentIndexId = RNIL;
+
+ regApiPtr->lastTcConnect = TtcConnectptrIndex;
+
+ if (TlastTcConnect == RNIL) {
+ jam();
+ regApiPtr->firstTcConnect = TtcConnectptrIndex;
+ } else {
+ tmpTcConnectptr.i = TlastTcConnect;
+ jam();
+ ptrCheckGuard(tmpTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ tmpTcConnectptr.p->nextTcConnect = TtcConnectptrIndex;
+ }//if
+ return 0;
+}//Dbtc::seizeTcRecord()
+
+int
+Dbtc::seizeCacheRecord(Signal* signal)
+{
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ UintR TfirstfreeCacheRec = cfirstfreeCacheRec;
+ UintR TcacheFilesize = ccacheFilesize;
+ CacheRecord *localCacheRecord = cacheRecord;
+ if (TfirstfreeCacheRec >= TcacheFilesize) {
+ TCKEY_abort(signal, 41);
+ return 1;
+ }//if
+ CacheRecord * const regCachePtr = &localCacheRecord[TfirstfreeCacheRec];
+
+ regApiPtr->cachePtr = TfirstfreeCacheRec;
+ cfirstfreeCacheRec = regCachePtr->nextCacheRec;
+ cachePtr.i = TfirstfreeCacheRec;
+ cachePtr.p = regCachePtr;
+
+#ifdef VM_TRACE
+ // This is a good place to check that resources have
+ // been properly released from CacheRecord
+ ndbrequire(regCachePtr->firstKeybuf == RNIL);
+ ndbrequire(regCachePtr->lastKeybuf == RNIL);
+#endif
+ regCachePtr->firstKeybuf = RNIL;
+ regCachePtr->lastKeybuf = RNIL;
+ regCachePtr->firstAttrbuf = RNIL;
+ regCachePtr->lastAttrbuf = RNIL;
+ regCachePtr->currReclenAi = 0;
+ return 0;
+}//Dbtc::seizeCacheRecord()
+
+/*****************************************************************************/
+/* T C K E Y R E Q */
+/* AFTER HAVING ESTABLISHED THE CONNECT, THE APPLICATION BLOCK SENDS AN */
+/* OPERATION REQUEST TO TC. ALL NECESSARY INFORMATION TO CARRY OUT REQUEST */
+/* IS FURNISHED IN PARAMETERS. TC STORES THIS INFORMATION AND ENQUIRES */
+/* FROM DIH ABOUT THE NODES WHICH MAY HAVE THE REQUESTED DATA */
+/*****************************************************************************/
+void Dbtc::execTCKEYREQ(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+ UintR titcLenAiInTckeyreq;
+ UintR TkeyLength;
+ const TcKeyReq * const tcKeyReq = (TcKeyReq *)signal->getDataPtr();
+ UintR Treqinfo;
+
+ jamEntry();
+ /*-------------------------------------------------------------------------
+ * Common error routines are used for several signals, they need to know
+ * where to find the transaction identifier in the signal.
+ *-------------------------------------------------------------------------*/
+ const UintR TapiIndex = tcKeyReq->apiConnectPtr;
+ const UintR TapiMaxIndex = capiConnectFilesize;
+ const UintR TtabIndex = tcKeyReq->tableId;
+ const UintR TtabMaxIndex = ctabrecFilesize;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+ ttransid_ptr = 6;
+ apiConnectptr.i = TapiIndex;
+ if (TapiIndex >= TapiMaxIndex) {
+ TCKEY_abort(signal, 6);
+ return;
+ }//if
+ if (TtabIndex >= TtabMaxIndex) {
+ TCKEY_abort(signal, 7);
+ return;
+ }//if
+
+ Treqinfo = tcKeyReq->requestInfo;
+ //--------------------------------------------------------------------------
+ // Optimised version of ptrAss(tabptr, tableRecord)
+ // Optimised version of ptrAss(apiConnectptr, apiConnectRecord)
+ //--------------------------------------------------------------------------
+ ApiConnectRecord * const regApiPtr = &localApiConnectRecord[TapiIndex];
+ apiConnectptr.p = regApiPtr;
+
+ Uint32 TstartFlag = tcKeyReq->getStartFlag(Treqinfo);
+ Uint32 TexecFlag = TcKeyReq::getExecuteFlag(Treqinfo);
+
+ bool isIndexOp = regApiPtr->isIndexOp;
+ bool isIndexOpReturn = regApiPtr->indexOpReturn;
+ regApiPtr->isIndexOp = false; // Reset marker
+ regApiPtr->m_exec_flag |= TexecFlag;
+ switch (regApiPtr->apiConnectstate) {
+ case CS_CONNECTED:{
+ if (TstartFlag == 1 && getAllowStartTransaction() == true){
+ //---------------------------------------------------------------------
+ // Initialise API connect record if transaction is started.
+ //---------------------------------------------------------------------
+ jam();
+ initApiConnectRec(signal, regApiPtr);
+ regApiPtr->m_exec_flag = TexecFlag;
+ } else {
+ if(getAllowStartTransaction() == true){
+ /*------------------------------------------------------------------
+ * WE EXPECTED A START TRANSACTION. SINCE NO OPERATIONS HAVE BEEN
+ * RECEIVED WE INDICATE THIS BY SETTING FIRST_TC_CONNECT TO RNIL TO
+ * ENSURE PROPER OPERATION OF THE COMMON ABORT HANDLING.
+ *-----------------------------------------------------------------*/
+ TCKEY_abort(signal, 0);
+ return;
+ } else {
+ /**
+ * getAllowStartTransaction() == false
+ */
+ TCKEY_abort(signal, 57);
+ return;
+ }//if
+ }
+ }
+ break;
+ case CS_STARTED:
+ if(TstartFlag == 1 && regApiPtr->firstTcConnect == RNIL)
+ {
+ /**
+ * If last operation in last transaction was a simple/dirty read
+ * it does not have to be committed or rollbacked hence,
+ * the state will be CS_STARTED
+ */
+ jam();
+ initApiConnectRec(signal, regApiPtr);
+ regApiPtr->m_exec_flag = TexecFlag;
+ } else {
+ //----------------------------------------------------------------------
+ // Transaction is started already.
+ // Check that the operation is on the same transaction.
+ //-----------------------------------------------------------------------
+ compare_transid1 = regApiPtr->transid[0] ^ tcKeyReq->transId1;
+ compare_transid2 = regApiPtr->transid[1] ^ tcKeyReq->transId2;
+ jam();
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ TCKEY_abort(signal, 1);
+ return;
+ }//if
+ }
+ break;
+ case CS_ABORTING:
+ if (regApiPtr->abortState == AS_IDLE) {
+ if (TstartFlag == 1) {
+ //--------------------------------------------------------------------
+ // Previous transaction had been aborted and the abort was completed.
+ // It is then OK to start a new transaction again.
+ //--------------------------------------------------------------------
+ jam();
+ initApiConnectRec(signal, regApiPtr);
+ regApiPtr->m_exec_flag = TexecFlag;
+ } else if(TexecFlag) {
+ TCKEY_abort(signal, 59);
+ return;
+ } else {
+ //--------------------------------------------------------------------
+ // The current transaction was aborted successfully.
+ // We will not do anything before we receive an operation
+ // with a start indicator. We will ignore this signal.
+ //--------------------------------------------------------------------
+ jam();
+ DEBUG("Drop TCKEYREQ - apiConnectState=CS_ABORTING, ==AS_IDLE");
+ return;
+ }//if
+ } else {
+ //----------------------------------------------------------------------
+ // Previous transaction is still aborting
+ //----------------------------------------------------------------------
+ jam();
+ if (TstartFlag == 1) {
+ //--------------------------------------------------------------------
+ // If a new transaction tries to start while the old is
+ // still aborting, we will report this to the starting API.
+ //--------------------------------------------------------------------
+ TCKEY_abort(signal, 2);
+ return;
+ } else if(TexecFlag) {
+ TCKEY_abort(signal, 59);
+ return;
+ }
+ //----------------------------------------------------------------------
+ // Ignore signals without start indicator set when aborting transaction.
+ //----------------------------------------------------------------------
+ DEBUG("Drop TCKEYREQ - apiConnectState=CS_ABORTING, !=AS_IDLE");
+ return;
+ }//if
+ break;
+ case CS_START_COMMITTING:
+ jam();
+ if(isIndexOpReturn || TcKeyReq::getExecutingTrigger(Treqinfo)){
+ break;
+ }
+ default:
+ jam();
+ /*----------------------------------------------------------------------
+ * IN THIS CASE THE NDBAPI IS AN UNTRUSTED ENTITY THAT HAS SENT A SIGNAL
+ * WHEN IT WAS NOT EXPECTED TO.
+ * WE MIGHT BE IN A PROCESS TO RECEIVE, PREPARE,
+ * COMMIT OR COMPLETE AND OBVIOUSLY THIS IS NOT A DESIRED EVENT.
+ * WE WILL ALWAYS COMPLETE THE ABORT HANDLING BEFORE WE ALLOW
+ * ANYTHING TO HAPPEN ON THIS CONNECTION AGAIN.
+ * THUS THERE IS NO ACTION FROM THE API THAT CAN SPEED UP THIS PROCESS.
+ *---------------------------------------------------------------------*/
+ TCKEY_abort(signal, 55);
+ return;
+ }//switch
+
+ TableRecordPtr localTabptr;
+ localTabptr.i = TtabIndex;
+ localTabptr.p = &tableRecord[TtabIndex];
+ if (localTabptr.p->checkTable(tcKeyReq->tableSchemaVersion)) {
+ ;
+ } else {
+ /*-----------------------------------------------------------------------*/
+ /* THE API IS WORKING WITH AN OLD SCHEMA VERSION. IT NEEDS REPLACEMENT. */
+ /* COULD ALSO BE THAT THE TABLE IS NOT DEFINED. */
+ /*-----------------------------------------------------------------------*/
+ TCKEY_abort(signal, 8);
+ return;
+ }//if
+
+ //-------------------------------------------------------------------------
+ // Error Insertion for testing purposes. Test to see what happens when no
+ // more TC records available.
+ //-------------------------------------------------------------------------
+ if (ERROR_INSERTED(8032)) {
+ TCKEY_abort(signal, 3);
+ return;
+ }//if
+
+ if (seizeTcRecord(signal) != 0) {
+ return;
+ }//if
+
+ if (seizeCacheRecord(signal) != 0) {
+ return;
+ }//if
+
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ CacheRecord * const regCachePtr = cachePtr.p;
+
+ /*
+ INIT_TC_CONNECT_REC
+ -------------------------
+ */
+ /* ---------------------------------------------------------------------- */
+ /* ------- INIT OPERATION RECORD WITH SIGNAL DATA AND RNILS ------- */
+ /* */
+ /* ---------------------------------------------------------------------- */
+
+ UintR TapiVersionNo = tcKeyReq->getAPIVersion(tcKeyReq->attrLen);
+ UintR Tlqhkeyreqrec = regApiPtr->lqhkeyreqrec;
+ regApiPtr->lqhkeyreqrec = Tlqhkeyreqrec + 1;
+ regCachePtr->apiVersionNo = TapiVersionNo;
+
+ UintR TapiConnectptrIndex = apiConnectptr.i;
+ UintR TsenderData = tcKeyReq->senderData;
+ UintR TattrLen = tcKeyReq->getAttrinfoLen(tcKeyReq->attrLen);
+ UintR TattrinfoCount = c_counters.cattrinfoCount;
+
+ regTcPtr->apiConnect = TapiConnectptrIndex;
+ regTcPtr->clientData = TsenderData;
+ regTcPtr->commitAckMarker = RNIL;
+ regTcPtr->isIndexOp = isIndexOp;
+ regTcPtr->indexOp = regApiPtr->executingIndexOp;
+ regTcPtr->savePointId = regApiPtr->currSavePointId;
+ regApiPtr->executingIndexOp = RNIL;
+
+ if (TcKeyReq::getExecutingTrigger(Treqinfo)) {
+ // Save the TcOperationPtr for fireing operation
+ regTcPtr->triggeringOperation = TsenderData;
+ }
+
+ if (TexecFlag){
+ Uint32 currSPId = regApiPtr->currSavePointId;
+ regApiPtr->currSavePointId = ++currSPId;
+ }
+
+ regCachePtr->attrlength = TattrLen;
+ c_counters.cattrinfoCount = TattrinfoCount + TattrLen;
+
+ UintR TtabptrIndex = localTabptr.i;
+ UintR TtableSchemaVersion = tcKeyReq->tableSchemaVersion;
+ Uint8 TOperationType = tcKeyReq->getOperationType(Treqinfo);
+ regCachePtr->tableref = TtabptrIndex;
+ regCachePtr->schemaVersion = TtableSchemaVersion;
+ regTcPtr->operation = TOperationType;
+
+ Uint8 TSimpleFlag = tcKeyReq->getSimpleFlag(Treqinfo);
+ Uint8 TDirtyFlag = tcKeyReq->getDirtyFlag(Treqinfo);
+ Uint8 TInterpretedFlag = tcKeyReq->getInterpretedFlag(Treqinfo);
+ Uint8 TDistrKeyFlag = tcKeyReq->getDistributionKeyFlag(Treqinfo);
+ Uint8 TexecuteFlag = TexecFlag;
+
+ regCachePtr->opSimple = TSimpleFlag;
+ regCachePtr->opExec = TInterpretedFlag;
+ regTcPtr->dirtyOp = TDirtyFlag;
+ regCachePtr->distributionKeyIndicator = TDistrKeyFlag;
+
+ //-------------------------------------------------------------
+ // The next step is to read the upto three conditional words.
+ //-------------------------------------------------------------
+ Uint32 TkeyIndex;
+ Uint32* TOptionalDataPtr = (Uint32*)&tcKeyReq->scanInfo;
+ {
+ Uint32 TDistrGHIndex = tcKeyReq->getScanIndFlag(Treqinfo);
+ Uint32 TDistrKeyIndex = TDistrGHIndex;
+
+ Uint32 TscanInfo = tcKeyReq->getTakeOverScanInfo(TOptionalDataPtr[0]);
+
+ regCachePtr->scanTakeOverInd = TDistrGHIndex;
+ regCachePtr->scanInfo = TscanInfo;
+
+ regCachePtr->distributionKey = TOptionalDataPtr[TDistrKeyIndex];
+
+ TkeyIndex = TDistrKeyIndex + TDistrKeyFlag;
+ }
+ Uint32* TkeyDataPtr = &TOptionalDataPtr[TkeyIndex];
+
+ UintR Tdata1 = TkeyDataPtr[0];
+ UintR Tdata2 = TkeyDataPtr[1];
+ UintR Tdata3 = TkeyDataPtr[2];
+ UintR Tdata4 = TkeyDataPtr[3];
+ UintR Tdata5;
+
+ regCachePtr->keydata[0] = Tdata1;
+ regCachePtr->keydata[1] = Tdata2;
+ regCachePtr->keydata[2] = Tdata3;
+ regCachePtr->keydata[3] = Tdata4;
+
+ TkeyLength = tcKeyReq->getKeyLength(Treqinfo);
+ Uint32 TAIDataIndex;
+ if (TkeyLength > 8) {
+ TAIDataIndex = TkeyIndex + 8;
+ } else {
+ if (TkeyLength == 0) {
+ TCKEY_abort(signal, 4);
+ return;
+ }//if
+ TAIDataIndex = TkeyIndex + TkeyLength;
+ }//if
+ Uint32* TAIDataPtr = &TOptionalDataPtr[TAIDataIndex];
+
+ titcLenAiInTckeyreq = tcKeyReq->getAIInTcKeyReq(Treqinfo);
+ regCachePtr->keylen = TkeyLength;
+ regCachePtr->lenAiInTckeyreq = titcLenAiInTckeyreq;
+ regCachePtr->currReclenAi = titcLenAiInTckeyreq;
+ regCachePtr->m_special_hash =
+ localTabptr.p->hasCharAttr | (localTabptr.p->noOfDistrKeys > 0);
+ Tdata1 = TAIDataPtr[0];
+ Tdata2 = TAIDataPtr[1];
+ Tdata3 = TAIDataPtr[2];
+ Tdata4 = TAIDataPtr[3];
+ Tdata5 = TAIDataPtr[4];
+
+ regCachePtr->attrinfo0 = Tdata1;
+ regCachePtr->attrinfo15[0] = Tdata2;
+ regCachePtr->attrinfo15[1] = Tdata3;
+ regCachePtr->attrinfo15[2] = Tdata4;
+ regCachePtr->attrinfo15[3] = Tdata5;
+
+ if (TOperationType == ZREAD) {
+ Uint32 TreadCount = c_counters.creadCount;
+ jam();
+ regCachePtr->opLock = 0;
+ c_counters.creadCount = TreadCount + 1;
+ } else if(TOperationType == ZREAD_EX){
+ Uint32 TreadCount = c_counters.creadCount;
+ jam();
+ TOperationType = ZREAD;
+ regTcPtr->operation = ZREAD;
+ regCachePtr->opLock = ZUPDATE;
+ c_counters.creadCount = TreadCount + 1;
+ } else {
+ if(regApiPtr->commitAckMarker == RNIL){
+ jam();
+ CommitAckMarkerPtr tmp;
+ if(!m_commitAckMarkerHash.seize(tmp)){
+ TCKEY_abort(signal, 56);
+ return;
+ } else {
+ regTcPtr->commitAckMarker = tmp.i;
+ regApiPtr->commitAckMarker = tmp.i;
+ tmp.p->transid1 = tcKeyReq->transId1;
+ tmp.p->transid2 = tcKeyReq->transId2;
+ tmp.p->apiNodeId = refToNode(regApiPtr->ndbapiBlockref);
+ tmp.p->apiConnectPtr = TapiIndex;
+ tmp.p->noOfLqhs = 0;
+ m_commitAckMarkerHash.add(tmp);
+ }
+ }
+
+ UintR TwriteCount = c_counters.cwriteCount;
+ UintR Toperationsize = coperationsize;
+ /* --------------------------------------------------------------------
+ * THIS IS A TEMPORARY TABLE, DON'T UPDATE coperationsize.
+ * THIS VARIABLE CONTROLS THE INTERVAL BETWEEN LCP'S AND
+ * TEMP TABLES DON'T PARTICIPATE.
+ * -------------------------------------------------------------------- */
+ if (localTabptr.p->storedTable) {
+ coperationsize = ((Toperationsize + TattrLen) + TkeyLength) + 17;
+ }
+ c_counters.cwriteCount = TwriteCount + 1;
+ switch (TOperationType) {
+ case ZUPDATE:
+ jam();
+ if (TattrLen == 0) {
+ //TCKEY_abort(signal, 5);
+ //return;
+ }//if
+ /*---------------------------------------------------------------------*/
+ // The missing break is intentional since we also want to set the opLock
+ // variable also for updates
+ /*---------------------------------------------------------------------*/
+ case ZINSERT:
+ case ZDELETE:
+ jam();
+ regCachePtr->opLock = TOperationType;
+ break;
+ case ZWRITE:
+ jam();
+ // A write operation is originally an insert operation.
+ regCachePtr->opLock = ZINSERT;
+ break;
+ default:
+ TCKEY_abort(signal, 9);
+ return;
+ }//switch
+ }//if
+
+ Uint32 TabortOption = tcKeyReq->getAbortOption(Treqinfo);
+ regTcPtr->m_execAbortOption = TabortOption;
+
+ /*-------------------------------------------------------------------------
+ * Check error handling per operation
+ * If CommitFlag is set state accordingly and check for early abort
+ *------------------------------------------------------------------------*/
+ if (tcKeyReq->getCommitFlag(Treqinfo) == 1) {
+ ndbrequire(TexecuteFlag);
+ regApiPtr->apiConnectstate = CS_REC_COMMITTING;
+ } else {
+ /* ---------------------------------------------------------------------
+ * PREPARE TRANSACTION IS NOT IMPLEMENTED YET.
+ * ---------------------------------------------------------------------
+ * ELSIF (TREQINFO => 3) (*) 1 = 1 THEN
+ * IF PREPARE TRANSACTION THEN
+ * API_CONNECTPTR:API_CONNECTSTATE = REC_PREPARING
+ * SET STATE TO PREPARING
+ * --------------------------------------------------------------------- */
+ if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
+ jam();
+ // Trigger execution at commit
+ regApiPtr->apiConnectstate = CS_REC_COMMITTING;
+ } else {
+ jam();
+ regApiPtr->apiConnectstate = CS_RECEIVING;
+ }//if
+ }//if
+ if (TkeyLength <= 4) {
+ tckeyreq050Lab(signal);
+ return;
+ } else {
+ if (cfirstfreeDatabuf != RNIL) {
+ jam();
+ linkKeybuf(signal);
+ Tdata1 = TkeyDataPtr[4];
+ Tdata2 = TkeyDataPtr[5];
+ Tdata3 = TkeyDataPtr[6];
+ Tdata4 = TkeyDataPtr[7];
+
+ DatabufRecord * const regDataPtr = databufptr.p;
+ regDataPtr->data[0] = Tdata1;
+ regDataPtr->data[1] = Tdata2;
+ regDataPtr->data[2] = Tdata3;
+ regDataPtr->data[3] = Tdata4;
+ } else {
+ jam();
+ seizeDatabuferrorLab(signal);
+ return;
+ }//if
+ if (TkeyLength <= 8) {
+ jam();
+ tckeyreq050Lab(signal);
+ return;
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * THE TCKEYREQ DIDN'T CONTAIN ALL KEY DATA,
+ * SAVE STATE AND WAIT FOR KEYINFO
+ * --------------------------------------------------------------------*/
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ regCachePtr->save1 = 8;
+ regTcPtr->tcConnectstate = OS_WAIT_KEYINFO;
+ return;
+ }//if
+ }//if
+ return;
+}//Dbtc::execTCKEYREQ()
+
+void Dbtc::tckeyreq050Lab(Signal* signal)
+{
+ UintR tnoOfBackup;
+ UintR tnoOfStandby;
+ UintR tnodeinfo;
+
+ hash(signal); /* NOW IT IS TIME TO CALCULATE THE HASH VALUE*/
+
+ CacheRecord * const regCachePtr = cachePtr.p;
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+
+ UintR TtcTimer = ctcTimer;
+ UintR ThashValue = thashValue;
+ UintR TdistrHashValue = tdistrHashValue;
+ UintR TdihConnectptr = regTcPtr->dihConnectptr;
+ UintR Ttableref = regCachePtr->tableref;
+
+ TableRecordPtr localTabptr;
+ localTabptr.i = Ttableref;
+ localTabptr.p = &tableRecord[localTabptr.i];
+ Uint32 schemaVersion = regCachePtr->schemaVersion;
+ if(localTabptr.p->checkTable(schemaVersion)){
+ ;
+ } else {
+ terrorCode = localTabptr.p->getErrorCode(schemaVersion);
+ TCKEY_abort(signal, 58);
+ return;
+ }
+
+ setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
+ regCachePtr->hashValue = ThashValue;
+
+ signal->theData[0] = TdihConnectptr;
+ signal->theData[1] = Ttableref;
+ signal->theData[2] = TdistrHashValue;
+
+ /*-------------------------------------------------------------*/
+ /* FOR EFFICIENCY REASONS WE AVOID THE SIGNAL SENDING HERE AND */
+ /* PROCEED IMMEDIATELY TO DIH. IN MULTI-THREADED VERSIONS WE */
+ /* HAVE TO INSERT A MUTEX ON DIH TO ENSURE PROPER OPERATION. */
+ /* SINCE THIS SIGNAL AND DIVERIFYREQ ARE THE ONLY SIGNALS SENT */
+ /* TO DIH IN TRAFFIC IT SHOULD BE OK (3% OF THE EXECUTION TIME */
+ /* IS SPENT IN DIH AND EVEN LESS IN REPLICATED NDB. */
+ /*-------------------------------------------------------------*/
+ EXECUTE_DIRECT(DBDIH, GSN_DIGETNODESREQ, signal, 3);
+ UintR TerrorIndicator = signal->theData[0];
+ jamEntry();
+ if (TerrorIndicator != 0) {
+ execDIGETNODESREF(signal);
+ return;
+ }
+
+ if(ERROR_INSERTED(8050) && signal->theData[3] != getOwnNodeId())
+ {
+ ndbassert(false);
+ signal->theData[1] = 626;
+ execDIGETNODESREF(signal);
+ return;
+ }
+
+ /****************>>*/
+ /* DIGETNODESCONF >*/
+ /* ***************>*/
+
+ UintR Tdata1 = signal->theData[1];
+ UintR Tdata2 = signal->theData[2];
+ UintR Tdata3 = signal->theData[3];
+ UintR Tdata4 = signal->theData[4];
+ UintR Tdata5 = signal->theData[5];
+ UintR Tdata6 = signal->theData[6];
+
+ regCachePtr->fragmentid = Tdata1;
+ tnodeinfo = Tdata2;
+
+ regTcPtr->tcNodedata[0] = Tdata3;
+ regTcPtr->tcNodedata[1] = Tdata4;
+ regTcPtr->tcNodedata[2] = Tdata5;
+ regTcPtr->tcNodedata[3] = Tdata6;
+
+ Uint8 Toperation = regTcPtr->operation;
+ Uint8 Tdirty = regTcPtr->dirtyOp;
+ tnoOfBackup = tnodeinfo & 3;
+ tnoOfStandby = (tnodeinfo >> 8) & 3;
+
+ regCachePtr->fragmentDistributionKey = (tnodeinfo >> 16) & 255;
+ if (Toperation == ZREAD) {
+ if (Tdirty == 1) {
+ jam();
+ /*-------------------------------------------------------------*/
+ /* A SIMPLE READ CAN SELECT ANY OF THE PRIMARY AND */
+ /* BACKUP NODES TO READ. WE WILL TRY TO SELECT THIS */
+ /* NODE IF POSSIBLE TO AVOID UNNECESSARY COMMUNICATION */
+ /* WITH SIMPLE READS. */
+ /*-------------------------------------------------------------*/
+ arrGuard(tnoOfBackup, 4);
+ UintR Tindex;
+ UintR TownNode = cownNodeid;
+ for (Tindex = 1; Tindex <= tnoOfBackup; Tindex++) {
+ UintR Tnode = regTcPtr->tcNodedata[Tindex];
+ jam();
+ if (Tnode == TownNode) {
+ jam();
+ regTcPtr->tcNodedata[0] = Tnode;
+ }//if
+ }//for
+ if(ERROR_INSERTED(8048) || ERROR_INSERTED(8049))
+ {
+ for (Tindex = 0; Tindex <= tnoOfBackup; Tindex++)
+ {
+ UintR Tnode = regTcPtr->tcNodedata[Tindex];
+ jam();
+ if (Tnode != TownNode) {
+ jam();
+ regTcPtr->tcNodedata[0] = Tnode;
+ ndbout_c("Choosing %d", Tnode);
+ }//if
+ }//for
+ }
+ }//if
+ jam();
+ regTcPtr->lastReplicaNo = 0;
+ regTcPtr->noOfNodes = 1;
+ } else {
+ UintR TlastReplicaNo;
+ jam();
+ TlastReplicaNo = tnoOfBackup + tnoOfStandby;
+ regTcPtr->lastReplicaNo = (Uint8)TlastReplicaNo;
+ regTcPtr->noOfNodes = (Uint8)(TlastReplicaNo + 1);
+ }//if
+ if (regCachePtr->lenAiInTckeyreq == regCachePtr->attrlength) {
+ /****************************************************************>*/
+ /* HERE WE HAVE FOUND THAT THE LAST SIGNAL BELONGING TO THIS */
+ /* OPERATION HAVE BEEN RECEIVED. THIS MEANS THAT WE CAN NOW REUSE */
+ /* THE API CONNECT RECORD. HOWEVER IF PREPARE OR COMMIT HAVE BEEN */
+ /* RECEIVED THEN IT IS NOT ALLOWED TO RECEIVE ANY FURTHER */
+ /* OPERATIONS. WE KNOW THAT WE WILL WAIT FOR DICT NEXT. IT IS NOT */
+ /* POSSIBLE FOR THE TC CONNECTION TO BE READY YET. */
+ /****************************************************************>*/
+ switch (regApiPtr->apiConnectstate) {
+ case CS_RECEIVING:
+ jam();
+ regApiPtr->apiConnectstate = CS_STARTED;
+ break;
+ case CS_REC_COMMITTING:
+ jam();
+ regApiPtr->apiConnectstate = CS_START_COMMITTING;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//switch
+ attrinfoDihReceivedLab(signal);
+ return;
+ } else {
+ if (regCachePtr->lenAiInTckeyreq < regCachePtr->attrlength) {
+ TtcTimer = ctcTimer;
+ jam();
+ setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
+ regTcPtr->tcConnectstate = OS_WAIT_ATTR;
+ return;
+ } else {
+ TCKEY_abort(signal, 11);
+ return;
+ }//if
+ }//if
+ return;
+}//Dbtc::tckeyreq050Lab()
+
+void Dbtc::attrinfoDihReceivedLab(Signal* signal)
+{
+ CacheRecord * const regCachePtr = cachePtr.p;
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ Uint16 Tnode = regTcPtr->tcNodedata[0];
+
+ TableRecordPtr localTabptr;
+ localTabptr.i = regCachePtr->tableref;
+ localTabptr.p = &tableRecord[localTabptr.i];
+
+ if(localTabptr.p->checkTable(regCachePtr->schemaVersion)){
+ ;
+ } else {
+ terrorCode = localTabptr.p->getErrorCode(regCachePtr->schemaVersion);
+ TCKEY_abort(signal, 58);
+ return;
+ }
+ arrGuard(Tnode, MAX_NDB_NODES);
+ packLqhkeyreq(signal, calcLqhBlockRef(Tnode));
+}//Dbtc::attrinfoDihReceivedLab()
+
+void Dbtc::packLqhkeyreq(Signal* signal,
+ BlockReference TBRef)
+{
+ CacheRecord * const regCachePtr = cachePtr.p;
+ UintR Tkeylen = regCachePtr->keylen;
+ UintR TfirstAttrbuf = regCachePtr->firstAttrbuf;
+ sendlqhkeyreq(signal, TBRef);
+ if (Tkeylen > 4) {
+ packKeyData000Lab(signal, TBRef, Tkeylen - 4);
+ releaseKeys();
+ }//if
+ packLqhkeyreq040Lab(signal,
+ TfirstAttrbuf,
+ TBRef);
+}//Dbtc::packLqhkeyreq()
+
+void Dbtc::sendlqhkeyreq(Signal* signal,
+ BlockReference TBRef)
+{
+ UintR tslrAttrLen;
+ UintR Tdata10;
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ CacheRecord * const regCachePtr = cachePtr.p;
+#ifdef ERROR_INSERT
+ if (ERROR_INSERTED(8002)) {
+ systemErrorLab(signal);
+ }//if
+ if (ERROR_INSERTED(8007)) {
+ if (apiConnectptr.p->apiConnectstate == CS_STARTED) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8008)) {
+ if (apiConnectptr.p->apiConnectstate == CS_START_COMMITTING) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8009)) {
+ if (apiConnectptr.p->apiConnectstate == CS_STARTED) {
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8010)) {
+ if (apiConnectptr.p->apiConnectstate == CS_START_COMMITTING) {
+ return;
+ }//if
+ }//if
+#endif
+
+ tslrAttrLen = 0;
+ LqhKeyReq::setAttrLen(tslrAttrLen, regCachePtr->attrlength);
+ /* ---------------------------------------------------------------------- */
+ // Bit16 == 0 since StoredProcedures are not yet supported.
+ /* ---------------------------------------------------------------------- */
+ LqhKeyReq::setDistributionKey(tslrAttrLen, regCachePtr->fragmentDistributionKey);
+ LqhKeyReq::setScanTakeOverFlag(tslrAttrLen, regCachePtr->scanTakeOverInd);
+
+ Tdata10 = 0;
+ LqhKeyReq::setKeyLen(Tdata10, regCachePtr->keylen);
+ LqhKeyReq::setLastReplicaNo(Tdata10, regTcPtr->lastReplicaNo);
+ LqhKeyReq::setLockType(Tdata10, regCachePtr->opLock);
+ /* ---------------------------------------------------------------------- */
+ // Indicate Application Reference is present in bit 15
+ /* ---------------------------------------------------------------------- */
+ LqhKeyReq::setApplicationAddressFlag(Tdata10, 1);
+ LqhKeyReq::setDirtyFlag(Tdata10, regTcPtr->dirtyOp);
+ LqhKeyReq::setInterpretedFlag(Tdata10, regCachePtr->opExec);
+ LqhKeyReq::setSimpleFlag(Tdata10, regCachePtr->opSimple);
+ LqhKeyReq::setOperation(Tdata10, regTcPtr->operation);
+ /* -----------------------------------------------------------------------
+ * Sequential Number of first LQH = 0, bit 22-23
+ * IF ATTRIBUTE INFORMATION IS SENT IN TCKEYREQ,
+ * IT IS ALSO SENT IN LQHKEYREQ
+ * ----------------------------------------------------------------------- */
+ LqhKeyReq::setAIInLqhKeyReq(Tdata10, regCachePtr->lenAiInTckeyreq);
+ /* -----------------------------------------------------------------------
+ * Bit 27 == 0 since TC record is the same as the client record.
+ * Bit 28 == 0 since readLenAi can only be set after reading in LQH.
+ * ----------------------------------------------------------------------- */
+ //LqhKeyReq::setAPIVersion(Tdata10, regCachePtr->apiVersionNo);
+ Uint32 commitAckMarker = regTcPtr->commitAckMarker;
+ if(commitAckMarker != RNIL){
+ jam();
+
+ LqhKeyReq::setMarkerFlag(Tdata10, 1);
+
+ CommitAckMarker * tmp;
+ tmp = m_commitAckMarkerHash.getPtr(commitAckMarker);
+
+ /**
+ * Populate LQH array
+ */
+ const Uint32 noOfLqhs = regTcPtr->noOfNodes;
+ tmp->noOfLqhs = noOfLqhs;
+ for(Uint32 i = 0; i<noOfLqhs; i++){
+ tmp->lqhNodeId[i] = regTcPtr->tcNodedata[i];
+ }
+ }
+
+ /* ************************************************************> */
+ /* NO READ LENGTH SENT FROM TC. SEQUENTIAL NUMBER IS 1 AND IT */
+ /* IS SENT TO A PRIMARY NODE. */
+ /* ************************************************************> */
+ UintR sig0, sig1, sig2, sig3, sig4, sig5, sig6;
+
+ LqhKeyReq * const lqhKeyReq = (LqhKeyReq *)signal->getDataPtrSend();
+
+ sig0 = tcConnectptr.i;
+ sig2 = regCachePtr->hashValue;
+ sig4 = cownref;
+ sig5 = regTcPtr->savePointId;
+
+ lqhKeyReq->clientConnectPtr = sig0;
+ lqhKeyReq->attrLen = tslrAttrLen;
+ lqhKeyReq->hashValue = sig2;
+ lqhKeyReq->requestInfo = Tdata10;
+ lqhKeyReq->tcBlockref = sig4;
+ lqhKeyReq->savePointId = sig5;
+
+ sig0 = regCachePtr->tableref + (regCachePtr->schemaVersion << 16);
+ sig1 = regCachePtr->fragmentid + (regTcPtr->tcNodedata[1] << 16);
+ sig2 = regApiPtr->transid[0];
+ sig3 = regApiPtr->transid[1];
+ sig4 = regApiPtr->ndbapiBlockref;
+ sig5 = regTcPtr->clientData;
+ sig6 = regCachePtr->scanInfo;
+
+ lqhKeyReq->tableSchemaVersion = sig0;
+ lqhKeyReq->fragmentData = sig1;
+ lqhKeyReq->transId1 = sig2;
+ lqhKeyReq->transId2 = sig3;
+ lqhKeyReq->scanInfo = sig6;
+
+ lqhKeyReq->variableData[0] = sig4;
+ lqhKeyReq->variableData[1] = sig5;
+
+ UintR nextPos = 2;
+
+ if (regTcPtr->lastReplicaNo > 1) {
+ sig0 = (UintR)regTcPtr->tcNodedata[2] +
+ (UintR)(regTcPtr->tcNodedata[3] << 16);
+ lqhKeyReq->variableData[nextPos] = sig0;
+ nextPos++;
+ }//if
+
+ sig0 = regCachePtr->keydata[0];
+ sig1 = regCachePtr->keydata[1];
+ sig2 = regCachePtr->keydata[2];
+ sig3 = regCachePtr->keydata[3];
+ UintR Tkeylen = regCachePtr->keylen;
+
+ lqhKeyReq->variableData[nextPos + 0] = sig0;
+ lqhKeyReq->variableData[nextPos + 1] = sig1;
+ lqhKeyReq->variableData[nextPos + 2] = sig2;
+ lqhKeyReq->variableData[nextPos + 3] = sig3;
+
+ if (Tkeylen < 4) {
+ nextPos += Tkeylen;
+ } else {
+ nextPos += 4;
+ }//if
+
+ sig0 = regCachePtr->attrinfo0;
+ sig1 = regCachePtr->attrinfo15[0];
+ sig2 = regCachePtr->attrinfo15[1];
+ sig3 = regCachePtr->attrinfo15[2];
+ sig4 = regCachePtr->attrinfo15[3];
+ UintR TlenAi = regCachePtr->lenAiInTckeyreq;
+
+ lqhKeyReq->variableData[nextPos + 0] = sig0;
+ lqhKeyReq->variableData[nextPos + 1] = sig1;
+ lqhKeyReq->variableData[nextPos + 2] = sig2;
+ lqhKeyReq->variableData[nextPos + 3] = sig3;
+ lqhKeyReq->variableData[nextPos + 4] = sig4;
+
+ nextPos += TlenAi;
+
+ // Reset trigger count
+ regTcPtr->accumulatingTriggerData.i = RNIL;
+ regTcPtr->accumulatingTriggerData.p = NULL;
+ regTcPtr->noFiredTriggers = 0;
+ regTcPtr->triggerExecutionCount = 0;
+
+ sendSignal(TBRef, GSN_LQHKEYREQ, signal,
+ nextPos + LqhKeyReq::FixedSignalLength, JBB);
+}//Dbtc::sendlqhkeyreq()
+
+void Dbtc::packLqhkeyreq040Lab(Signal* signal,
+ UintR anAttrBufIndex,
+ BlockReference TBRef)
+{
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ CacheRecord * const regCachePtr = cachePtr.p;
+#ifdef ERROR_INSERT
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ if (ERROR_INSERTED(8009)) {
+ if (regApiPtr->apiConnectstate == CS_STARTED) {
+ attrbufptr.i = RNIL;
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8010)) {
+ if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
+ attrbufptr.i = RNIL;
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+#endif
+
+ UintR TattrbufFilesize = cattrbufFilesize;
+ AttrbufRecord *localAttrbufRecord = attrbufRecord;
+ while (1) {
+ if (anAttrBufIndex == RNIL) {
+ UintR TtcTimer = ctcTimer;
+ UintR Tread = (regTcPtr->operation == ZREAD);
+ UintR Tsimple = (regCachePtr->opSimple == ZTRUE);
+ UintR Tboth = Tread & Tsimple;
+ setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
+ jam();
+ /*--------------------------------------------------------------------
+ * WE HAVE SENT ALL THE SIGNALS OF THIS OPERATION. SET STATE AND EXIT.
+ *---------------------------------------------------------------------*/
+ releaseAttrinfo();
+ if (Tboth) {
+ jam();
+ releaseSimpleRead(signal, apiConnectptr, tcConnectptr.p);
+ return;
+ }//if
+ regTcPtr->tcConnectstate = OS_OPERATING;
+ return;
+ }//if
+ if (anAttrBufIndex < TattrbufFilesize) {
+ AttrbufRecord * const regAttrPtr = &localAttrbufRecord[anAttrBufIndex];
+ anAttrBufIndex = regAttrPtr->attrbuf[ZINBUF_NEXT];
+ sendAttrinfo(signal,
+ tcConnectptr.i,
+ regAttrPtr,
+ TBRef);
+ } else {
+ TCKEY_abort(signal, 17);
+ return;
+ }//if
+ }//while
+}//Dbtc::packLqhkeyreq040Lab()
+
+/* ========================================================================= */
+/* ------- RELEASE ALL ATTRINFO RECORDS IN AN OPERATION RECORD ------- */
+/* ========================================================================= */
+void Dbtc::releaseAttrinfo()
+{
+ UintR Tmp;
+ AttrbufRecordPtr Tattrbufptr;
+ CacheRecord * const regCachePtr = cachePtr.p;
+ UintR TattrbufFilesize = cattrbufFilesize;
+ UintR TfirstfreeAttrbuf = cfirstfreeAttrbuf;
+ Tattrbufptr.i = regCachePtr->firstAttrbuf;
+ AttrbufRecord *localAttrbufRecord = attrbufRecord;
+
+ while (Tattrbufptr.i < TattrbufFilesize) {
+ Tattrbufptr.p = &localAttrbufRecord[Tattrbufptr.i];
+ Tmp = Tattrbufptr.p->attrbuf[ZINBUF_NEXT];
+ Tattrbufptr.p->attrbuf[ZINBUF_NEXT] = TfirstfreeAttrbuf;
+ TfirstfreeAttrbuf = Tattrbufptr.i;
+ Tattrbufptr.i = Tmp;
+ jam();
+ }//while
+ if (Tattrbufptr.i == RNIL) {
+//---------------------------------------------------
+// Now we will release the cache record at the same
+// time as releasing the attrinfo records.
+//---------------------------------------------------
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ UintR TfirstfreeCacheRec = cfirstfreeCacheRec;
+ UintR TCacheIndex = cachePtr.i;
+ cfirstfreeAttrbuf = TfirstfreeAttrbuf;
+ regCachePtr->nextCacheRec = TfirstfreeCacheRec;
+ cfirstfreeCacheRec = TCacheIndex;
+ regApiPtr->cachePtr = RNIL;
+ return;
+ }//if
+ systemErrorLab(0);
+ return;
+}//Dbtc::releaseAttrinfo()
+
+/* ========================================================================= */
+/* ------- RELEASE ALL RECORDS CONNECTED TO A SIMPLE OPERATION ------- */
+/* ========================================================================= */
+void Dbtc::releaseSimpleRead(Signal* signal,
+ ApiConnectRecordPtr regApiPtr,
+ TcConnectRecord* regTcPtr)
+{
+ Uint32 Ttckeyrec = regApiPtr.p->tckeyrec;
+ Uint32 TclientData = regTcPtr->clientData;
+ Uint32 Tnode = regTcPtr->tcNodedata[0];
+ Uint32 Tlqhkeyreqrec = regApiPtr.p->lqhkeyreqrec;
+ Uint32 TsimpleReadCount = c_counters.csimpleReadCount;
+ ConnectionState state = regApiPtr.p->apiConnectstate;
+
+ regApiPtr.p->tcSendArray[Ttckeyrec] = TclientData;
+ regApiPtr.p->tcSendArray[Ttckeyrec + 1] = TcKeyConf::SimpleReadBit | Tnode;
+ regApiPtr.p->tckeyrec = Ttckeyrec + 2;
+
+ unlinkReadyTcCon(signal);
+ releaseTcCon();
+
+ /**
+ * No LQHKEYCONF in Simple/Dirty read
+ * Therefore decrese no LQHKEYCONF(REF) we are waiting for
+ */
+ c_counters.csimpleReadCount = TsimpleReadCount + 1;
+ regApiPtr.p->lqhkeyreqrec = --Tlqhkeyreqrec;
+
+ if(Tlqhkeyreqrec == 0)
+ {
+ /**
+ * Special case of lqhKeyConf_checkTransactionState:
+ * - commit with zero operations: handle only for simple read
+ */
+ sendtckeyconf(signal, state == CS_START_COMMITTING);
+ regApiPtr.p->apiConnectstate =
+ (state == CS_START_COMMITTING ? CS_CONNECTED : state);
+ setApiConTimer(regApiPtr.i, 0, __LINE__);
+
+ return;
+ }
+
+ /**
+ * Emulate LQHKEYCONF
+ */
+ lqhKeyConf_checkTransactionState(signal, regApiPtr.p);
+}//Dbtc::releaseSimpleRead()
+
+/* ------------------------------------------------------------------------- */
+/* ------- CHECK IF ALL TC CONNECTIONS ARE COMPLETED ------- */
+/* ------------------------------------------------------------------------- */
+void Dbtc::unlinkReadyTcCon(Signal* signal)
+{
+ TcConnectRecordPtr urtTcConnectptr;
+
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ if (regTcPtr->prevTcConnect != RNIL) {
+ jam();
+ urtTcConnectptr.i = regTcPtr->prevTcConnect;
+ ptrCheckGuard(urtTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ urtTcConnectptr.p->nextTcConnect = regTcPtr->nextTcConnect;
+ } else {
+ jam();
+ regApiPtr->firstTcConnect = regTcPtr->nextTcConnect;
+ }//if
+ if (regTcPtr->nextTcConnect != RNIL) {
+ jam();
+ urtTcConnectptr.i = regTcPtr->nextTcConnect;
+ ptrCheckGuard(urtTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ urtTcConnectptr.p->prevTcConnect = regTcPtr->prevTcConnect;
+ } else {
+ jam();
+ regApiPtr->lastTcConnect = tcConnectptr.p->prevTcConnect;
+ }//if
+}//Dbtc::unlinkReadyTcCon()
+
+void Dbtc::releaseTcCon()
+{
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ UintR TfirstfreeTcConnect = cfirstfreeTcConnect;
+ UintR TconcurrentOp = c_counters.cconcurrentOp;
+ UintR TtcConnectptrIndex = tcConnectptr.i;
+
+ regTcPtr->tcConnectstate = OS_CONNECTED;
+ regTcPtr->nextTcConnect = TfirstfreeTcConnect;
+ regTcPtr->apiConnect = RNIL;
+ regTcPtr->isIndexOp = false;
+ regTcPtr->indexOp = RNIL;
+ cfirstfreeTcConnect = TtcConnectptrIndex;
+ c_counters.cconcurrentOp = TconcurrentOp - 1;
+}//Dbtc::releaseTcCon()
+
+void Dbtc::execPACKED_SIGNAL(Signal* signal)
+{
+ LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+
+ UintR Ti;
+ UintR Tstep = 0;
+ UintR Tlength;
+ UintR TpackedData[28];
+ UintR Tdata1, Tdata2, Tdata3, Tdata4;
+
+ jamEntry();
+ Tlength = signal->length();
+ if (Tlength > 25) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ Uint32* TpackDataPtr;
+ for (Ti = 0; Ti < Tlength; Ti += 4) {
+ Uint32* TsigDataPtr = &signal->theData[Ti];
+ Tdata1 = TsigDataPtr[0];
+ Tdata2 = TsigDataPtr[1];
+ Tdata3 = TsigDataPtr[2];
+ Tdata4 = TsigDataPtr[3];
+
+ TpackDataPtr = &TpackedData[Ti];
+ TpackDataPtr[0] = Tdata1;
+ TpackDataPtr[1] = Tdata2;
+ TpackDataPtr[2] = Tdata3;
+ TpackDataPtr[3] = Tdata4;
+ }//for
+ while (Tlength > Tstep) {
+
+ TpackDataPtr = &TpackedData[Tstep];
+ Tdata1 = TpackDataPtr[0];
+ Tdata2 = TpackDataPtr[1];
+ Tdata3 = TpackDataPtr[2];
+
+ lqhKeyConf->connectPtr = Tdata1 & 0x0FFFFFFF;
+ lqhKeyConf->opPtr = Tdata2;
+ lqhKeyConf->userRef = Tdata3;
+
+ switch (Tdata1 >> 28) {
+ case ZCOMMITTED:
+ signal->header.theLength = 3;
+ execCOMMITTED(signal);
+ Tstep += 3;
+ break;
+ case ZCOMPLETED:
+ signal->header.theLength = 3;
+ execCOMPLETED(signal);
+ Tstep += 3;
+ break;
+ case ZLQHKEYCONF:
+ jam();
+ Tdata1 = TpackDataPtr[3];
+ Tdata2 = TpackDataPtr[4];
+ Tdata3 = TpackDataPtr[5];
+ Tdata4 = TpackDataPtr[6];
+
+ lqhKeyConf->readLen = Tdata1;
+ lqhKeyConf->transId1 = Tdata2;
+ lqhKeyConf->transId2 = Tdata3;
+ lqhKeyConf->noFiredTriggers = Tdata4;
+ signal->header.theLength = LqhKeyConf::SignalLength;
+ execLQHKEYCONF(signal);
+ Tstep += LqhKeyConf::SignalLength;
+ break;
+ default:
+ systemErrorLab(signal);
+ return;
+ }//switch
+ }//while
+ return;
+}//Dbtc::execPACKED_SIGNAL()
+
+void Dbtc::execLQHKEYCONF(Signal* signal)
+{
+ const LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+ UintR compare_transid1, compare_transid2;
+ BlockReference tlastLqhBlockref;
+ UintR tlastLqhConnect;
+ UintR treadlenAi;
+ UintR TtcConnectptrIndex;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+
+ tlastLqhConnect = lqhKeyConf->connectPtr;
+ TtcConnectptrIndex = lqhKeyConf->opPtr;
+ tlastLqhBlockref = lqhKeyConf->userRef;
+ treadlenAi = lqhKeyConf->readLen;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+
+ /*------------------------------------------------------------------------
+ * NUMBER OF EXTERNAL TRIGGERS FIRED IN DATA[6]
+ * OPERATION IS NOW COMPLETED. CHECK FOR CORRECT OPERATION POINTER
+ * TO ENSURE NO CRASHES BECAUSE OF ERRONEUS NODES. CHECK STATE OF
+ * OPERATION. THEN SET OPERATION STATE AND RETRIEVE ALL POINTERS
+ * OF THIS OPERATION. PUT COMPLETED OPERATION IN LIST OF COMPLETED
+ * OPERATIONS ON THE LQH CONNECT RECORD.
+ *------------------------------------------------------------------------
+ * THIS SIGNAL ALWAYS ARRIVE BEFORE THE ABORTED SIGNAL ARRIVES SINCE IT USES
+ * THE SAME PATH BACK TO TC AS THE ABORTED SIGNAL DO. WE DO HOWEVER HAVE A
+ * PROBLEM WHEN WE ENCOUNTER A TIME-OUT WAITING FOR THE ABORTED SIGNAL.
+ * THEN THIS SIGNAL MIGHT ARRIVE WHEN THE TC CONNECT RECORD HAVE BEEN REUSED
+ * BY OTHER TRANSACTION THUS WE CHECK THE TRANSACTION ID OF THE SIGNAL
+ * BEFORE ACCEPTING THIS SIGNAL.
+ * Due to packing of LQHKEYCONF the ABORTED signal can now arrive before
+ * this.
+ * This is more reason to ignore the signal if not all states are correct.
+ *------------------------------------------------------------------------*/
+ if (TtcConnectptrIndex >= TtcConnectFilesize) {
+ TCKEY_abort(signal, 25);
+ return;
+ }//if
+ TcConnectRecord* const regTcPtr = &localTcConnectRecord[TtcConnectptrIndex];
+ OperationState TtcConnectstate = regTcPtr->tcConnectstate;
+ tcConnectptr.i = TtcConnectptrIndex;
+ tcConnectptr.p = regTcPtr;
+ if (TtcConnectstate != OS_OPERATING) {
+ warningReport(signal, 23);
+ return;
+ }//if
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+ UintR TapiConnectptrIndex = regTcPtr->apiConnect;
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ UintR Ttrans1 = lqhKeyConf->transId1;
+ UintR Ttrans2 = lqhKeyConf->transId2;
+ Uint32 noFired = lqhKeyConf->noFiredTriggers;
+
+ if (TapiConnectptrIndex >= TapiConnectFilesize) {
+ TCKEY_abort(signal, 29);
+ return;
+ }//if
+ ApiConnectRecord * const regApiPtr =
+ &localApiConnectRecord[TapiConnectptrIndex];
+ apiConnectptr.i = TapiConnectptrIndex;
+ apiConnectptr.p = regApiPtr;
+ compare_transid1 = regApiPtr->transid[0] ^ Ttrans1;
+ compare_transid2 = regApiPtr->transid[1] ^ Ttrans2;
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ warningReport(signal, 24);
+ return;
+ }//if
+
+#ifdef ERROR_INSERT
+ if (ERROR_INSERTED(8029)) {
+ systemErrorLab(signal);
+ }//if
+ if (ERROR_INSERTED(8003)) {
+ if (regApiPtr->apiConnectstate == CS_STARTED) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8004)) {
+ if (regApiPtr->apiConnectstate == CS_RECEIVING) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8005)) {
+ if (regApiPtr->apiConnectstate == CS_REC_COMMITTING) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8006)) {
+ if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(8023)) {
+ SET_ERROR_INSERT_VALUE(8024);
+ return;
+ }//if
+#endif
+ UintR TtcTimer = ctcTimer;
+ regTcPtr->lastLqhCon = tlastLqhConnect;
+ regTcPtr->lastLqhNodeId = refToNode(tlastLqhBlockref);
+ regTcPtr->noFiredTriggers = noFired;
+
+ UintR Ttckeyrec = (UintR)regApiPtr->tckeyrec;
+ UintR TclientData = regTcPtr->clientData;
+ UintR TdirtyOp = regTcPtr->dirtyOp;
+ ConnectionState TapiConnectstate = regApiPtr->apiConnectstate;
+ if (Ttckeyrec > (ZTCOPCONF_SIZE - 2)) {
+ TCKEY_abort(signal, 30);
+ return;
+ }
+ if (TapiConnectstate == CS_ABORTING) {
+ warningReport(signal, 27);
+ return;
+ }//if
+
+ setApiConTimer(apiConnectptr.i, TtcTimer, __LINE__);
+
+ if (regTcPtr->isIndexOp) {
+ jam();
+ // This was an internal TCKEYREQ
+ // will be returned unpacked
+ regTcPtr->attrInfoLen = treadlenAi;
+ } else {
+ if (noFired == 0 && regTcPtr->triggeringOperation == RNIL) {
+ jam();
+ /*
+ * Skip counting triggering operations the first round
+ * since they will enter execLQHKEYCONF a second time
+ * Skip counting internally generated TcKeyReq
+ */
+ regApiPtr->tcSendArray[Ttckeyrec] = TclientData;
+ regApiPtr->tcSendArray[Ttckeyrec + 1] = treadlenAi;
+ regApiPtr->tckeyrec = Ttckeyrec + 2;
+ }//if
+ }//if
+ if (TdirtyOp == ZTRUE) {
+ UintR Tlqhkeyreqrec = regApiPtr->lqhkeyreqrec;
+ jam();
+ releaseDirtyWrite(signal);
+ regApiPtr->lqhkeyreqrec = Tlqhkeyreqrec - 1;
+ } else {
+ jam();
+ if (noFired == 0) {
+ jam();
+ // No triggers to execute
+ UintR Tlqhkeyconfrec = regApiPtr->lqhkeyconfrec;
+ regApiPtr->lqhkeyconfrec = Tlqhkeyconfrec + 1;
+ regTcPtr->tcConnectstate = OS_PREPARED;
+ }
+ }//if
+
+ /**
+ * And now decide what to do next
+ */
+ if (regTcPtr->triggeringOperation != RNIL) {
+ jam();
+ // This operation was created by a trigger execting operation
+ // Restart it if we have executed all it's triggers
+ TcConnectRecordPtr opPtr;
+
+ opPtr.i = regTcPtr->triggeringOperation;
+ ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
+ opPtr.p->triggerExecutionCount--;
+ if (opPtr.p->triggerExecutionCount == 0) {
+ /*
+ We have completed current trigger execution
+ Continue triggering operation
+ */
+ jam();
+ continueTriggeringOp(signal, opPtr.p);
+ }
+ } else if (noFired == 0) {
+ // This operation did not fire any triggers, finish operation
+ jam();
+ if (regTcPtr->isIndexOp) {
+ jam();
+ setupIndexOpReturn(regApiPtr, regTcPtr);
+ }
+ lqhKeyConf_checkTransactionState(signal, regApiPtr);
+ } else {
+ // We have fired triggers
+ jam();
+ saveTriggeringOpState(signal, regTcPtr);
+ if (regTcPtr->noReceivedTriggers == noFired) {
+ ApiConnectRecordPtr transPtr;
+
+ // We have received all data
+ jam();
+ transPtr.i = TapiConnectptrIndex;
+ transPtr.p = regApiPtr;
+ executeTriggers(signal, &transPtr);
+ }
+ // else wait for more trigger data
+ }
+}//Dbtc::execLQHKEYCONF()
+
+void Dbtc::setupIndexOpReturn(ApiConnectRecord* regApiPtr,
+ TcConnectRecord* regTcPtr)
+{
+ regApiPtr->indexOpReturn = true;
+ regApiPtr->indexOp = regTcPtr->indexOp;
+ regApiPtr->clientData = regTcPtr->clientData;
+ regApiPtr->attrInfoLen = regTcPtr->attrInfoLen;
+}
+
+/**
+ * lqhKeyConf_checkTransactionState
+ *
+ * This functions checks state variables, and
+ * decides if it should wait for more LQHKEYCONF signals
+ * or if it should start commiting
+ */
+void
+Dbtc::lqhKeyConf_checkTransactionState(Signal * signal,
+ ApiConnectRecord * const apiConnectPtrP)
+{
+/*---------------------------------------------------------------*/
+/* IF THE COMMIT FLAG IS SET IN SIGNAL TCKEYREQ THEN DBTC HAS TO */
+/* SEND TCKEYCONF FOR ALL OPERATIONS EXCEPT THE LAST ONE. WHEN */
+/* THE TRANSACTION THEN IS COMMITTED TCKEYCONF IS SENT FOR THE */
+/* WHOLE TRANSACTION */
+/* IF THE COMMIT FLAG IS NOT RECECIVED DBTC WILL SEND TCKEYCONF */
+/* FOR ALL OPERATIONS, AND THEN WAIT FOR THE API TO CONCLUDE THE */
+/* TRANSACTION */
+/*---------------------------------------------------------------*/
+ ConnectionState TapiConnectstate = apiConnectPtrP->apiConnectstate;
+ UintR Tlqhkeyconfrec = apiConnectPtrP->lqhkeyconfrec;
+ UintR Tlqhkeyreqrec = apiConnectPtrP->lqhkeyreqrec;
+ int TnoOfOutStanding = Tlqhkeyreqrec - Tlqhkeyconfrec;
+
+ switch (TapiConnectstate) {
+ case CS_START_COMMITTING:
+ if (TnoOfOutStanding == 0) {
+ jam();
+ diverify010Lab(signal);
+ return;
+ } else if (TnoOfOutStanding > 0) {
+ if (apiConnectPtrP->tckeyrec == ZTCOPCONF_SIZE) {
+ jam();
+ sendtckeyconf(signal, 0);
+ return;
+ } else if (apiConnectPtrP->indexOpReturn) {
+ jam();
+ sendtckeyconf(signal, 0);
+ return;
+ }//if
+ jam();
+ return;
+ } else {
+ TCKEY_abort(signal, 44);
+ return;
+ }//if
+ return;
+ case CS_STARTED:
+ case CS_RECEIVING:
+ if (TnoOfOutStanding == 0) {
+ jam();
+ sendtckeyconf(signal, 2);
+ return;
+ } else {
+ if (apiConnectPtrP->tckeyrec == ZTCOPCONF_SIZE) {
+ jam();
+ sendtckeyconf(signal, 0);
+ return;
+ } else if (apiConnectPtrP->indexOpReturn) {
+ jam();
+ sendtckeyconf(signal, 0);
+ return;
+ }//if
+ jam();
+ }//if
+ return;
+ case CS_REC_COMMITTING:
+ if (TnoOfOutStanding > 0) {
+ if (apiConnectPtrP->tckeyrec == ZTCOPCONF_SIZE) {
+ jam();
+ sendtckeyconf(signal, 0);
+ return;
+ } else if (apiConnectPtrP->indexOpReturn) {
+ jam();
+ sendtckeyconf(signal, 0);
+ return;
+ }//if
+ jam();
+ return;
+ }//if
+ TCKEY_abort(signal, 45);
+ return;
+ case CS_CONNECTED:
+ jam();
+/*---------------------------------------------------------------*/
+/* WE HAVE CONCLUDED THE TRANSACTION SINCE IT WAS ONLY */
+/* CONSISTING OF DIRTY WRITES AND ALL OF THOSE WERE */
+/* COMPLETED. ENSURE TCKEYREC IS ZERO TO PREVENT ERRORS. */
+/*---------------------------------------------------------------*/
+ apiConnectPtrP->tckeyrec = 0;
+ return;
+ default:
+ TCKEY_abort(signal, 46);
+ return;
+ }//switch
+}//Dbtc::lqhKeyConf_checkTransactionState()
+
+void Dbtc::sendtckeyconf(Signal* signal, UintR TcommitFlag)
+{
+ if(ERROR_INSERTED(8049)){
+ CLEAR_ERROR_INSERT_VALUE;
+ signal->theData[0] = TcContinueB::DelayTCKEYCONF;
+ signal->theData[1] = apiConnectptr.i;
+ signal->theData[2] = TcommitFlag;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 3000, 3);
+ return;
+ }
+
+ HostRecordPtr localHostptr;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ const UintR TopWords = (UintR)regApiPtr->tckeyrec;
+ localHostptr.i = refToNode(regApiPtr->ndbapiBlockref);
+ const Uint32 type = getNodeInfo(localHostptr.i).m_type;
+ const bool is_api = (type >= NodeInfo::API && type <= NodeInfo::REP);
+ const BlockNumber TblockNum = refToBlock(regApiPtr->ndbapiBlockref);
+ const Uint32 Tmarker = (regApiPtr->commitAckMarker == RNIL) ? 0 : 1;
+ ptrAss(localHostptr, hostRecord);
+ UintR TcurrLen = localHostptr.p->noOfWordsTCKEYCONF;
+ UintR confInfo = 0;
+ TcKeyConf::setCommitFlag(confInfo, TcommitFlag == 1);
+ TcKeyConf::setMarkerFlag(confInfo, Tmarker);
+ const UintR TpacketLen = 6 + TopWords;
+ regApiPtr->tckeyrec = 0;
+
+ if (regApiPtr->indexOpReturn) {
+ jam();
+ // Return internally generated TCKEY
+ TcKeyConf * const tcKeyConf = (TcKeyConf *)signal->getDataPtrSend();
+ TcKeyConf::setNoOfOperations(confInfo, 1);
+ tcKeyConf->apiConnectPtr = regApiPtr->indexOp;
+ tcKeyConf->gci = regApiPtr->globalcheckpointid;
+ tcKeyConf->confInfo = confInfo;
+ tcKeyConf->transId1 = regApiPtr->transid[0];
+ tcKeyConf->transId2 = regApiPtr->transid[1];
+ tcKeyConf->operations[0].apiOperationPtr = regApiPtr->clientData;
+ tcKeyConf->operations[0].attrInfoLen = regApiPtr->attrInfoLen;
+ Uint32 sigLen = TcKeyConf::StaticLength + TcKeyConf::OperationLength;
+ EXECUTE_DIRECT(DBTC, GSN_TCKEYCONF, signal, sigLen);
+ regApiPtr->indexOpReturn = false;
+ if (TopWords == 0) {
+ jam();
+ return; // No queued TcKeyConf
+ }//if
+ }//if
+ if(TcommitFlag){
+ jam();
+ regApiPtr->m_exec_flag = 0;
+ }
+ TcKeyConf::setNoOfOperations(confInfo, (TopWords >> 1));
+ if ((TpacketLen > 25) || !is_api){
+ TcKeyConf * const tcKeyConf = (TcKeyConf *)signal->getDataPtrSend();
+
+ jam();
+ tcKeyConf->apiConnectPtr = regApiPtr->ndbapiConnect;
+ tcKeyConf->gci = regApiPtr->globalcheckpointid;;
+ tcKeyConf->confInfo = confInfo;
+ tcKeyConf->transId1 = regApiPtr->transid[0];
+ tcKeyConf->transId2 = regApiPtr->transid[1];
+ copyFromToLen(&regApiPtr->tcSendArray[0],
+ (UintR*)&tcKeyConf->operations,
+ (UintR)ZTCOPCONF_SIZE);
+ sendSignal(regApiPtr->ndbapiBlockref,
+ GSN_TCKEYCONF, signal, (TpacketLen - 1), JBB);
+ return;
+ } else if (((TcurrLen + TpacketLen) > 25) && (TcurrLen > 0)) {
+ jam();
+ sendPackedTCKEYCONF(signal, localHostptr.p, localHostptr.i);
+ TcurrLen = 0;
+ } else {
+ jam();
+ updatePackedList(signal, localHostptr.p, localHostptr.i);
+ }//if
+ // -------------------------------------------------------------------------
+ // The header contains the block reference of receiver plus the real signal
+ // length - 3, since we have the real signal length plus one additional word
+ // for the header we have to do - 4.
+ // -------------------------------------------------------------------------
+ UintR Tpack0 = (TblockNum << 16) + (TpacketLen - 4);
+ UintR Tpack1 = regApiPtr->ndbapiConnect;
+ UintR Tpack2 = regApiPtr->globalcheckpointid;
+ UintR Tpack3 = confInfo;
+ UintR Tpack4 = regApiPtr->transid[0];
+ UintR Tpack5 = regApiPtr->transid[1];
+
+ localHostptr.p->noOfWordsTCKEYCONF = TcurrLen + TpacketLen;
+
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + 0] = Tpack0;
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + 1] = Tpack1;
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + 2] = Tpack2;
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + 3] = Tpack3;
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + 4] = Tpack4;
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + 5] = Tpack5;
+
+ UintR Ti;
+ for (Ti = 6; Ti < TpacketLen; Ti++) {
+ localHostptr.p->packedWordsTCKEYCONF[TcurrLen + Ti] =
+ regApiPtr->tcSendArray[Ti - 6];
+ }//for
+}//Dbtc::sendtckeyconf()
+
+void Dbtc::copyFromToLen(UintR* sourceBuffer, UintR* destBuffer, UintR Tlen)
+{
+ UintR Tindex = 0;
+ UintR Ti;
+ while (Tlen >= 4) {
+ UintR Tdata0 = sourceBuffer[Tindex + 0];
+ UintR Tdata1 = sourceBuffer[Tindex + 1];
+ UintR Tdata2 = sourceBuffer[Tindex + 2];
+ UintR Tdata3 = sourceBuffer[Tindex + 3];
+ Tlen -= 4;
+ destBuffer[Tindex + 0] = Tdata0;
+ destBuffer[Tindex + 1] = Tdata1;
+ destBuffer[Tindex + 2] = Tdata2;
+ destBuffer[Tindex + 3] = Tdata3;
+ Tindex += 4;
+ }//while
+ for (Ti = 0; Ti < Tlen; Ti++, Tindex++) {
+ destBuffer[Tindex] = sourceBuffer[Tindex];
+ }//for
+}//Dbtc::copyFromToLen()
+
+void Dbtc::execSEND_PACKED(Signal* signal)
+{
+ HostRecordPtr Thostptr;
+ HostRecord *localHostRecord = hostRecord;
+ UintR i;
+ UintR TpackedListIndex = cpackedListIndex;
+ jamEntry();
+ for (i = 0; i < TpackedListIndex; i++) {
+ Thostptr.i = cpackedList[i];
+ ptrAss(Thostptr, localHostRecord);
+ arrGuard(Thostptr.i - 1, MAX_NODES - 1);
+ UintR TnoOfPackedWordsLqh = Thostptr.p->noOfPackedWordsLqh;
+ UintR TnoOfWordsTCKEYCONF = Thostptr.p->noOfWordsTCKEYCONF;
+ UintR TnoOfWordsTCINDXCONF = Thostptr.p->noOfWordsTCINDXCONF;
+ jam();
+ if (TnoOfPackedWordsLqh > 0) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ }//if
+ if (TnoOfWordsTCKEYCONF > 0) {
+ jam();
+ sendPackedTCKEYCONF(signal, Thostptr.p, (Uint32)Thostptr.i);
+ }//if
+ if (TnoOfWordsTCINDXCONF > 0) {
+ jam();
+ sendPackedTCINDXCONF(signal, Thostptr.p, (Uint32)Thostptr.i);
+ }//if
+ Thostptr.p->inPackedList = false;
+ }//for
+ cpackedListIndex = 0;
+ return;
+}//Dbtc::execSEND_PACKED()
+
+void
+Dbtc::updatePackedList(Signal* signal, HostRecord* ahostptr, Uint16 ahostIndex)
+{
+ if (ahostptr->inPackedList == false) {
+ UintR TpackedListIndex = cpackedListIndex;
+ jam();
+ ahostptr->inPackedList = true;
+ cpackedList[TpackedListIndex] = ahostIndex;
+ cpackedListIndex = TpackedListIndex + 1;
+ }//if
+}//Dbtc::updatePackedList()
+
+void Dbtc::sendPackedSignalLqh(Signal* signal, HostRecord * ahostptr)
+{
+ UintR Tj;
+ UintR TnoOfWords = ahostptr->noOfPackedWordsLqh;
+ for (Tj = 0; Tj < TnoOfWords; Tj += 4) {
+ UintR sig0 = ahostptr->packedWordsLqh[Tj + 0];
+ UintR sig1 = ahostptr->packedWordsLqh[Tj + 1];
+ UintR sig2 = ahostptr->packedWordsLqh[Tj + 2];
+ UintR sig3 = ahostptr->packedWordsLqh[Tj + 3];
+ signal->theData[Tj + 0] = sig0;
+ signal->theData[Tj + 1] = sig1;
+ signal->theData[Tj + 2] = sig2;
+ signal->theData[Tj + 3] = sig3;
+ }//for
+ ahostptr->noOfPackedWordsLqh = 0;
+ sendSignal(ahostptr->hostLqhBlockRef,
+ GSN_PACKED_SIGNAL,
+ signal,
+ TnoOfWords,
+ JBB);
+}//Dbtc::sendPackedSignalLqh()
+
+void Dbtc::sendPackedTCKEYCONF(Signal* signal,
+ HostRecord * ahostptr,
+ UintR hostId)
+{
+ UintR Tj;
+ UintR TnoOfWords = ahostptr->noOfWordsTCKEYCONF;
+ BlockReference TBref = numberToRef(API_PACKED, hostId);
+ for (Tj = 0; Tj < ahostptr->noOfWordsTCKEYCONF; Tj += 4) {
+ UintR sig0 = ahostptr->packedWordsTCKEYCONF[Tj + 0];
+ UintR sig1 = ahostptr->packedWordsTCKEYCONF[Tj + 1];
+ UintR sig2 = ahostptr->packedWordsTCKEYCONF[Tj + 2];
+ UintR sig3 = ahostptr->packedWordsTCKEYCONF[Tj + 3];
+ signal->theData[Tj + 0] = sig0;
+ signal->theData[Tj + 1] = sig1;
+ signal->theData[Tj + 2] = sig2;
+ signal->theData[Tj + 3] = sig3;
+ }//for
+ ahostptr->noOfWordsTCKEYCONF = 0;
+ sendSignal(TBref, GSN_TCKEYCONF, signal, TnoOfWords, JBB);
+}//Dbtc::sendPackedTCKEYCONF()
+
+void Dbtc::sendPackedTCINDXCONF(Signal* signal,
+ HostRecord * ahostptr,
+ UintR hostId)
+{
+ UintR Tj;
+ UintR TnoOfWords = ahostptr->noOfWordsTCINDXCONF;
+ BlockReference TBref = numberToRef(API_PACKED, hostId);
+ for (Tj = 0; Tj < ahostptr->noOfWordsTCINDXCONF; Tj += 4) {
+ UintR sig0 = ahostptr->packedWordsTCINDXCONF[Tj + 0];
+ UintR sig1 = ahostptr->packedWordsTCINDXCONF[Tj + 1];
+ UintR sig2 = ahostptr->packedWordsTCINDXCONF[Tj + 2];
+ UintR sig3 = ahostptr->packedWordsTCINDXCONF[Tj + 3];
+ signal->theData[Tj + 0] = sig0;
+ signal->theData[Tj + 1] = sig1;
+ signal->theData[Tj + 2] = sig2;
+ signal->theData[Tj + 3] = sig3;
+ }//for
+ ahostptr->noOfWordsTCINDXCONF = 0;
+ sendSignal(TBref, GSN_TCINDXCONF, signal, TnoOfWords, JBB);
+}//Dbtc::sendPackedTCINDXCONF()
+
+/*
+4.3.11 DIVERIFY
+---------------
+*/
+/*****************************************************************************/
+/* D I V E R I F Y */
+/* */
+/*****************************************************************************/
+void Dbtc::diverify010Lab(Signal* signal)
+{
+ UintR TfirstfreeApiConnectCopy = cfirstfreeApiConnectCopy;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ signal->theData[0] = apiConnectptr.i;
+ if (ERROR_INSERTED(8022)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ if (TfirstfreeApiConnectCopy != RNIL) {
+ seizeApiConnectCopy(signal);
+ regApiPtr->apiConnectstate = CS_PREPARE_TO_COMMIT;
+ /*-----------------------------------------------------------------------
+ * WE COME HERE ONLY IF THE TRANSACTION IS PREPARED ON ALL TC CONNECTIONS.
+ * THUS WE CAN START THE COMMIT PHASE BY SENDING DIVERIFY ON ALL TC
+ * CONNECTIONS AND THEN WHEN ALL DIVERIFYCONF HAVE BEEN RECEIVED THE
+ * COMMIT MESSAGE CAN BE SENT TO ALL INVOLVED PARTS.
+ *-----------------------------------------------------------------------*/
+ EXECUTE_DIRECT(DBDIH, GSN_DIVERIFYREQ, signal, 1);
+ if (signal->theData[2] == 0) {
+ execDIVERIFYCONF(signal);
+ }
+ return;
+ } else {
+ /*-----------------------------------------------------------------------
+ * There were no free copy connections available. We must abort the
+ * transaction since otherwise we will have a problem with the report
+ * to the application.
+ * This should more or less not happen but if it happens we do not want to
+ * crash and we do not want to create code to handle it properly since
+ * it is difficult to test it and will be complex to handle a problem
+ * more or less not occurring.
+ *-----------------------------------------------------------------------*/
+ terrorCode = ZSEIZE_API_COPY_ERROR;
+ abortErrorLab(signal);
+ return;
+ }//if
+}//Dbtc::diverify010Lab()
+
+/* ------------------------------------------------------------------------- */
+/* ------- SEIZE_API_CONNECT ------- */
+/* SEIZE CONNECT RECORD FOR A REQUEST */
+/* ------------------------------------------------------------------------- */
+void Dbtc::seizeApiConnectCopy(Signal* signal)
+{
+ ApiConnectRecordPtr locApiConnectptr;
+
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+
+ locApiConnectptr.i = cfirstfreeApiConnectCopy;
+ ptrCheckGuard(locApiConnectptr, TapiConnectFilesize, localApiConnectRecord);
+ cfirstfreeApiConnectCopy = locApiConnectptr.p->nextApiConnect;
+ locApiConnectptr.p->nextApiConnect = RNIL;
+ regApiPtr->apiCopyRecord = locApiConnectptr.i;
+ regApiPtr->triggerPending = false;
+ regApiPtr->isIndexOp = false;
+}//Dbtc::seizeApiConnectCopy()
+
+void Dbtc::execDIVERIFYCONF(Signal* signal)
+{
+ UintR TapiConnectptrIndex = signal->theData[0];
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ UintR Tgci = signal->theData[1];
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+ jamEntry();
+ if (ERROR_INSERTED(8017)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ if (TapiConnectptrIndex >= TapiConnectFilesize) {
+ TCKEY_abort(signal, 31);
+ return;
+ }//if
+ ApiConnectRecord * const regApiPtr =
+ &localApiConnectRecord[TapiConnectptrIndex];
+ ConnectionState TapiConnectstate = regApiPtr->apiConnectstate;
+ UintR TApifailureNr = regApiPtr->failureNr;
+ UintR Tfailure_nr = cfailure_nr;
+ apiConnectptr.i = TapiConnectptrIndex;
+ apiConnectptr.p = regApiPtr;
+ if (TapiConnectstate != CS_PREPARE_TO_COMMIT) {
+ TCKEY_abort(signal, 32);
+ return;
+ }//if
+ /*--------------------------------------------------------------------------
+ * THIS IS THE COMMIT POINT. IF WE ARRIVE HERE THE TRANSACTION IS COMMITTED
+ * UNLESS EVERYTHING CRASHES BEFORE WE HAVE BEEN ABLE TO REPORT THE COMMIT
+ * DECISION. THERE IS NO TURNING BACK FROM THIS DECISION FROM HERE ON.
+ * WE WILL INSERT THE TRANSACTION INTO ITS PROPER QUEUE OF
+ * TRANSACTIONS FOR ITS GLOBAL CHECKPOINT.
+ *-------------------------------------------------------------------------*/
+ if (TApifailureNr != Tfailure_nr) {
+ DIVER_node_fail_handling(signal, Tgci);
+ return;
+ }//if
+ commitGciHandling(signal, Tgci);
+
+ /**************************************************************************
+ * C O M M I T
+ * THE TRANSACTION HAVE NOW BEEN VERIFIED AND NOW THE COMMIT PHASE CAN START
+ **************************************************************************/
+
+ UintR TtcConnectptrIndex = regApiPtr->firstTcConnect;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+
+ regApiPtr->counter = regApiPtr->lqhkeyconfrec;
+ regApiPtr->apiConnectstate = CS_COMMITTING;
+ if (TtcConnectptrIndex >= TtcConnectFilesize) {
+ TCKEY_abort(signal, 33);
+ return;
+ }//if
+ TcConnectRecord* const regTcPtr = &localTcConnectRecord[TtcConnectptrIndex];
+ tcConnectptr.i = TtcConnectptrIndex;
+ tcConnectptr.p = regTcPtr;
+ commit020Lab(signal);
+}//Dbtc::execDIVERIFYCONF()
+
+/*--------------------------------------------------------------------------*/
+/* COMMIT_GCI_HANDLING */
+/* SET UP GLOBAL CHECKPOINT DATA STRUCTURE AT THE COMMIT POINT. */
+/*--------------------------------------------------------------------------*/
+void Dbtc::commitGciHandling(Signal* signal, UintR Tgci)
+{
+ GcpRecordPtr localGcpPointer;
+
+ UintR TgcpFilesize = cgcpFilesize;
+ UintR Tfirstgcp = cfirstgcp;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ GcpRecord *localGcpRecord = gcpRecord;
+
+ regApiPtr->globalcheckpointid = Tgci;
+ if (Tfirstgcp != RNIL) {
+ /* IF THIS GLOBAL CHECKPOINT ALREADY EXISTS */
+ localGcpPointer.i = Tfirstgcp;
+ ptrCheckGuard(localGcpPointer, TgcpFilesize, localGcpRecord);
+ do {
+ if (regApiPtr->globalcheckpointid == localGcpPointer.p->gcpId) {
+ jam();
+ gcpPtr.i = localGcpPointer.i;
+ gcpPtr.p = localGcpPointer.p;
+ linkApiToGcp(signal);
+ return;
+ } else {
+ localGcpPointer.i = localGcpPointer.p->nextGcp;
+ jam();
+ if (localGcpPointer.i != RNIL) {
+ jam();
+ ptrCheckGuard(localGcpPointer, TgcpFilesize, localGcpRecord);
+ continue;
+ }//if
+ }//if
+ seizeGcp(signal);
+ linkApiToGcp(signal);
+ return;
+ } while (1);
+ } else {
+ jam();
+ seizeGcp(signal);
+ linkApiToGcp(signal);
+ }//if
+}//Dbtc::commitGciHandling()
+
+/* --------------------------------------------------------------------------*/
+/* -LINK AN API CONNECT RECORD IN STATE PREPARED INTO THE LIST WITH GLOBAL - */
+/* CHECKPOINTS. WHEN THE TRANSACTION I COMPLETED THE API CONNECT RECORD IS */
+/* LINKED OUT OF THE LIST. */
+/*---------------------------------------------------------------------------*/
+void Dbtc::linkApiToGcp(Signal* signal)
+{
+ ApiConnectRecordPtr localApiConnectptr;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ GcpRecord * const regGcpPtr = gcpPtr.p;
+ UintR TapiConnectptrIndex = apiConnectptr.i;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+ regApiPtr->nextGcpConnect = RNIL;
+ if (regGcpPtr->firstApiConnect == RNIL) {
+ regGcpPtr->firstApiConnect = TapiConnectptrIndex;
+ jam();
+ } else {
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ localApiConnectptr.i = regGcpPtr->lastApiConnect;
+ jam();
+ ptrCheckGuard(localApiConnectptr,
+ TapiConnectFilesize, localApiConnectRecord);
+ localApiConnectptr.p->nextGcpConnect = TapiConnectptrIndex;
+ }//if
+ UintR TlastApiConnect = regGcpPtr->lastApiConnect;
+ regApiPtr->gcpPointer = gcpPtr.i;
+ regApiPtr->prevGcpConnect = TlastApiConnect;
+ regGcpPtr->lastApiConnect = TapiConnectptrIndex;
+}//Dbtc::linkApiToGcp()
+
+void Dbtc::seizeGcp(Signal* signal)
+{
+ GcpRecordPtr tmpGcpPointer;
+ GcpRecordPtr localGcpPointer;
+
+ UintR Tfirstgcp = cfirstgcp;
+ UintR Tglobalcheckpointid = apiConnectptr.p->globalcheckpointid;
+ UintR TgcpFilesize = cgcpFilesize;
+ GcpRecord *localGcpRecord = gcpRecord;
+
+ localGcpPointer.i = cfirstfreeGcp;
+ ptrCheckGuard(localGcpPointer, TgcpFilesize, localGcpRecord);
+ UintR TfirstfreeGcp = localGcpPointer.p->nextGcp;
+ localGcpPointer.p->gcpId = Tglobalcheckpointid;
+ localGcpPointer.p->nextGcp = RNIL;
+ localGcpPointer.p->firstApiConnect = RNIL;
+ localGcpPointer.p->lastApiConnect = RNIL;
+ localGcpPointer.p->gcpNomoretransRec = ZFALSE;
+ cfirstfreeGcp = TfirstfreeGcp;
+
+ if (Tfirstgcp == RNIL) {
+ jam();
+ cfirstgcp = localGcpPointer.i;
+ } else {
+ tmpGcpPointer.i = clastgcp;
+ jam();
+ ptrCheckGuard(tmpGcpPointer, TgcpFilesize, localGcpRecord);
+ tmpGcpPointer.p->nextGcp = localGcpPointer.i;
+ }//if
+ clastgcp = localGcpPointer.i;
+ gcpPtr = localGcpPointer;
+}//Dbtc::seizeGcp()
+
+/*---------------------------------------------------------------------------*/
+// Send COMMIT messages to all LQH operations involved in the transaction.
+/*---------------------------------------------------------------------------*/
+void Dbtc::commit020Lab(Signal* signal)
+{
+ TcConnectRecordPtr localTcConnectptr;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+
+ localTcConnectptr.p = tcConnectptr.p;
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ UintR Tcount = 0;
+ do {
+ /*-----------------------------------------------------------------------
+ * WE ARE NOW READY TO RELEASE ALL OPERATIONS ON THE LQH
+ *-----------------------------------------------------------------------*/
+ /* *********< */
+ /* COMMIT < */
+ /* *********< */
+ localTcConnectptr.i = localTcConnectptr.p->nextTcConnect;
+ localTcConnectptr.p->tcConnectstate = OS_COMMITTING;
+ sendCommitLqh(signal, localTcConnectptr.p);
+
+ if (localTcConnectptr.i != RNIL) {
+ Tcount = Tcount + 1;
+ if (Tcount < 16) {
+ ptrCheckGuard(localTcConnectptr,
+ TtcConnectFilesize, localTcConnectRecord);
+ jam();
+ continue;
+ } else {
+ jam();
+ if (ERROR_INSERTED(8014)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ signal->theData[0] = TcContinueB::ZSEND_COMMIT_LOOP;
+ signal->theData[1] = apiConnectptr.i;
+ signal->theData[2] = localTcConnectptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }//if
+ } else {
+ jam();
+ regApiPtr->apiConnectstate = CS_COMMIT_SENT;
+ return;
+ }//if
+ } while (1);
+}//Dbtc::commit020Lab()
+
+void Dbtc::sendCommitLqh(Signal* signal,
+ TcConnectRecord * const regTcPtr)
+{
+ HostRecordPtr Thostptr;
+ UintR ThostFilesize = chostFilesize;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ Thostptr.i = regTcPtr->lastLqhNodeId;
+ ptrCheckGuard(Thostptr, ThostFilesize, hostRecord);
+ if (Thostptr.p->noOfPackedWordsLqh > 21) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+ UintR Tindex = Thostptr.p->noOfPackedWordsLqh;
+ UintR* TDataPtr = &Thostptr.p->packedWordsLqh[Tindex];
+ UintR Tdata1 = regTcPtr->lastLqhCon;
+ UintR Tdata2 = regApiPtr->globalcheckpointid;
+ UintR Tdata3 = regApiPtr->transid[0];
+ UintR Tdata4 = regApiPtr->transid[1];
+
+ TDataPtr[0] = Tdata1 | (ZCOMMIT << 28);
+ TDataPtr[1] = Tdata2;
+ TDataPtr[2] = Tdata3;
+ TDataPtr[3] = Tdata4;
+ Thostptr.p->noOfPackedWordsLqh = Tindex + 4;
+}//Dbtc::sendCommitLqh()
+
+void
+Dbtc::DIVER_node_fail_handling(Signal* signal, UintR Tgci)
+{
+ /*------------------------------------------------------------------------
+ * AT LEAST ONE NODE HAS FAILED DURING THE TRANSACTION. WE NEED TO CHECK IF
+ * THIS IS SO SERIOUS THAT WE NEED TO ABORT THE TRANSACTION. IN BOTH THE
+ * ABORT AND THE COMMIT CASES WE NEED TO SET-UP THE DATA FOR THE
+ * ABORT/COMMIT/COMPLETE HANDLING AS ALSO USED BY TAKE OVER FUNCTIONALITY.
+ *------------------------------------------------------------------------*/
+ tabortInd = ZFALSE;
+ setupFailData(signal);
+ if (tabortInd == ZFALSE) {
+ jam();
+ commitGciHandling(signal, Tgci);
+ toCommitHandlingLab(signal);
+ } else {
+ jam();
+ apiConnectptr.p->returnsignal = RS_TCROLLBACKREP;
+ apiConnectptr.p->returncode = ZNODEFAIL_BEFORE_COMMIT;
+ toAbortHandlingLab(signal);
+ }//if
+ return;
+}//Dbtc::DIVER_node_fail_handling()
+
+
+/* ------------------------------------------------------------------------- */
+/* ------- ENTER COMMITTED ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dbtc::execCOMMITTED(Signal* signal)
+{
+ TcConnectRecordPtr localTcConnectptr;
+ ApiConnectRecordPtr localApiConnectptr;
+
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+#ifdef ERROR_INSERT
+ if (ERROR_INSERTED(8018)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ if (ERROR_INSERTED(8030)) {
+ systemErrorLab(signal);
+ }//if
+ if (ERROR_INSERTED(8025)) {
+ SET_ERROR_INSERT_VALUE(8026);
+ return;
+ }//if
+ if (ERROR_INSERTED(8041)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMMITTED, signal, 2000, 3);
+ return;
+ }//if
+ if (ERROR_INSERTED(8042)) {
+ SET_ERROR_INSERT_VALUE(8046);
+ sendSignalWithDelay(cownref, GSN_COMMITTED, signal, 2000, 4);
+ return;
+ }//if
+#endif
+ localTcConnectptr.i = signal->theData[0];
+ jamEntry();
+ ptrCheckGuard(localTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ localApiConnectptr.i = localTcConnectptr.p->apiConnect;
+ if (localTcConnectptr.p->tcConnectstate != OS_COMMITTING) {
+ warningReport(signal, 4);
+ return;
+ }//if
+ ptrCheckGuard(localApiConnectptr, TapiConnectFilesize,
+ localApiConnectRecord);
+ UintR Tcounter = localApiConnectptr.p->counter - 1;
+ ConnectionState TapiConnectstate = localApiConnectptr.p->apiConnectstate;
+ UintR Tdata1 = localApiConnectptr.p->transid[0] - signal->theData[1];
+ UintR Tdata2 = localApiConnectptr.p->transid[1] - signal->theData[2];
+ Tdata1 = Tdata1 | Tdata2;
+ bool TcheckCondition =
+ (TapiConnectstate != CS_COMMIT_SENT) || (Tcounter != 0);
+
+ setApiConTimer(localApiConnectptr.i, ctcTimer, __LINE__);
+ localApiConnectptr.p->counter = Tcounter;
+ localTcConnectptr.p->tcConnectstate = OS_COMMITTED;
+ if (Tdata1 != 0) {
+ warningReport(signal, 5);
+ return;
+ }//if
+ if (TcheckCondition) {
+ jam();
+ /*-------------------------------------------------------*/
+ // We have not sent all COMMIT requests yet. We could be
+ // in the state that all sent are COMMITTED but we are
+ // still waiting for a CONTINUEB to send the rest of the
+ // COMMIT requests.
+ /*-------------------------------------------------------*/
+ return;
+ }//if
+ if (ERROR_INSERTED(8020)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ /*-------------------------------------------------------*/
+ /* THE ENTIRE TRANSACTION IS NOW COMMITED */
+ /* NOW WE NEED TO SEND THE RESPONSE TO THE APPLICATION. */
+ /* THE APPLICATION CAN THEN REUSE THE API CONNECTION AND */
+ /* THEREFORE WE NEED TO MOVE THE API CONNECTION TO A */
+ /* NEW API CONNECT RECORD. */
+ /*-------------------------------------------------------*/
+
+ apiConnectptr = localApiConnectptr;
+ sendApiCommit(signal);
+
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ localTcConnectptr.i = regApiPtr->firstTcConnect;
+ UintR Tlqhkeyconfrec = regApiPtr->lqhkeyconfrec;
+ ptrCheckGuard(localTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ regApiPtr->counter = Tlqhkeyconfrec;
+
+ tcConnectptr = localTcConnectptr;
+ complete010Lab(signal);
+ return;
+
+}//Dbtc::execCOMMITTED()
+
+/*-------------------------------------------------------*/
+/* SEND_API_COMMIT */
+/* SEND COMMIT DECISION TO THE API. */
+/*-------------------------------------------------------*/
+void Dbtc::sendApiCommit(Signal* signal)
+{
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+
+ if (regApiPtr->returnsignal == RS_TCKEYCONF) {
+ sendtckeyconf(signal, 1);
+ } else if (regApiPtr->returnsignal == RS_TC_COMMITCONF) {
+ jam();
+ TcCommitConf * const commitConf = (TcCommitConf *)&signal->theData[0];
+ if(regApiPtr->commitAckMarker == RNIL){
+ jam();
+ commitConf->apiConnectPtr = regApiPtr->ndbapiConnect;
+ } else {
+ jam();
+ commitConf->apiConnectPtr = regApiPtr->ndbapiConnect | 1;
+ }
+ commitConf->transId1 = regApiPtr->transid[0];
+ commitConf->transId2 = regApiPtr->transid[1];
+
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TC_COMMITCONF, signal, 3, JBB);
+ } else if (regApiPtr->returnsignal == RS_NO_RETURN) {
+ jam();
+ } else {
+ TCKEY_abort(signal, 37);
+ return;
+ }//if
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ UintR TcommitCount = c_counters.ccommitCount;
+ UintR TapiIndex = apiConnectptr.i;
+ UintR TnewApiIndex = regApiPtr->apiCopyRecord;
+ UintR TapiFailState = regApiPtr->apiFailState;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+ tmpApiConnectptr.p = apiConnectptr.p;
+ tmpApiConnectptr.i = TapiIndex;
+ c_counters.ccommitCount = TcommitCount + 1;
+ apiConnectptr.i = TnewApiIndex;
+ ptrCheckGuard(apiConnectptr, TapiConnectFilesize, localApiConnectRecord);
+ copyApi(signal);
+ if (TapiFailState != ZTRUE) {
+ return;
+ } else {
+ jam();
+ handleApiFailState(signal, tmpApiConnectptr.i);
+ return;
+ }//if
+}//Dbtc::sendApiCommit()
+
+/* ========================================================================= */
+/* ======= COPY_API ======= */
+/* COPY API RECORD ALSO RESET THE OLD API RECORD SO THAT IT */
+/* IS PREPARED TO RECEIVE A NEW TRANSACTION. */
+/*===========================================================================*/
+void Dbtc::copyApi(Signal* signal)
+{
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ ApiConnectRecord * const regTmpApiPtr = tmpApiConnectptr.p;
+
+ UintR TndbapiConnect = regTmpApiPtr->ndbapiConnect;
+ UintR TfirstTcConnect = regTmpApiPtr->firstTcConnect;
+ UintR Ttransid1 = regTmpApiPtr->transid[0];
+ UintR Ttransid2 = regTmpApiPtr->transid[1];
+ UintR Tlqhkeyconfrec = regTmpApiPtr->lqhkeyconfrec;
+ UintR TgcpPointer = regTmpApiPtr->gcpPointer;
+ UintR TgcpFilesize = cgcpFilesize;
+ UintR TcommitAckMarker = regTmpApiPtr->commitAckMarker;
+ GcpRecord *localGcpRecord = gcpRecord;
+
+ regApiPtr->ndbapiBlockref = regTmpApiPtr->ndbapiBlockref;
+ regApiPtr->ndbapiConnect = TndbapiConnect;
+ regApiPtr->firstTcConnect = TfirstTcConnect;
+ regApiPtr->apiConnectstate = CS_COMPLETING;
+ regApiPtr->transid[0] = Ttransid1;
+ regApiPtr->transid[1] = Ttransid2;
+ regApiPtr->lqhkeyconfrec = Tlqhkeyconfrec;
+ regApiPtr->commitAckMarker = TcommitAckMarker;
+
+ gcpPtr.i = TgcpPointer;
+ ptrCheckGuard(gcpPtr, TgcpFilesize, localGcpRecord);
+ unlinkApiConnect(signal);
+ linkApiToGcp(signal);
+ setApiConTimer(tmpApiConnectptr.i, 0, __LINE__);
+ regTmpApiPtr->apiConnectstate = CS_CONNECTED;
+ regTmpApiPtr->commitAckMarker = RNIL;
+ regTmpApiPtr->firstTcConnect = RNIL;
+ regTmpApiPtr->lastTcConnect = RNIL;
+}//Dbtc::copyApi()
+
+void Dbtc::unlinkApiConnect(Signal* signal)
+{
+ ApiConnectRecordPtr localApiConnectptr;
+ ApiConnectRecord * const regTmpApiPtr = tmpApiConnectptr.p;
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ UintR TprevGcpConnect = regTmpApiPtr->prevGcpConnect;
+ UintR TnextGcpConnect = regTmpApiPtr->nextGcpConnect;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+ if (TprevGcpConnect == RNIL) {
+ gcpPtr.p->firstApiConnect = TnextGcpConnect;
+ jam();
+ } else {
+ localApiConnectptr.i = TprevGcpConnect;
+ jam();
+ ptrCheckGuard(localApiConnectptr,
+ TapiConnectFilesize, localApiConnectRecord);
+ localApiConnectptr.p->nextGcpConnect = TnextGcpConnect;
+ }//if
+ if (TnextGcpConnect == RNIL) {
+ gcpPtr.p->lastApiConnect = TprevGcpConnect;
+ jam();
+ } else {
+ localApiConnectptr.i = TnextGcpConnect;
+ jam();
+ ptrCheckGuard(localApiConnectptr,
+ TapiConnectFilesize, localApiConnectRecord);
+ localApiConnectptr.p->prevGcpConnect = TprevGcpConnect;
+ }//if
+}//Dbtc::unlinkApiConnect()
+
+void Dbtc::complete010Lab(Signal* signal)
+{
+ TcConnectRecordPtr localTcConnectptr;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+
+ localTcConnectptr.p = tcConnectptr.p;
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ UintR TapiConnectptrIndex = apiConnectptr.i;
+ UintR Tcount = 0;
+ do {
+ localTcConnectptr.p->apiConnect = TapiConnectptrIndex;
+ localTcConnectptr.p->tcConnectstate = OS_COMPLETING;
+
+ /* ************ */
+ /* COMPLETE < */
+ /* ************ */
+ const Uint32 nextTcConnect = localTcConnectptr.p->nextTcConnect;
+ sendCompleteLqh(signal, localTcConnectptr.p);
+ localTcConnectptr.i = nextTcConnect;
+ if (localTcConnectptr.i != RNIL) {
+ Tcount++;
+ if (Tcount < 16) {
+ ptrCheckGuard(localTcConnectptr,
+ TtcConnectFilesize, localTcConnectRecord);
+ jam();
+ continue;
+ } else {
+ jam();
+ if (ERROR_INSERTED(8013)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ signal->theData[0] = TcContinueB::ZSEND_COMPLETE_LOOP;
+ signal->theData[1] = apiConnectptr.i;
+ signal->theData[2] = localTcConnectptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }//if
+ } else {
+ jam();
+ regApiPtr->apiConnectstate = CS_COMPLETE_SENT;
+ return;
+ }//if
+ } while (1);
+}//Dbtc::complete010Lab()
+
+void Dbtc::sendCompleteLqh(Signal* signal,
+ TcConnectRecord * const regTcPtr)
+{
+ HostRecordPtr Thostptr;
+ UintR ThostFilesize = chostFilesize;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ Thostptr.i = regTcPtr->lastLqhNodeId;
+ ptrCheckGuard(Thostptr, ThostFilesize, hostRecord);
+ if (Thostptr.p->noOfPackedWordsLqh > 22) {
+ jam();
+ sendPackedSignalLqh(signal, Thostptr.p);
+ } else {
+ jam();
+ updatePackedList(signal, Thostptr.p, Thostptr.i);
+ }//if
+
+ UintR Tindex = Thostptr.p->noOfPackedWordsLqh;
+ UintR* TDataPtr = &Thostptr.p->packedWordsLqh[Tindex];
+ UintR Tdata1 = regTcPtr->lastLqhCon | (ZCOMPLETE << 28);
+ UintR Tdata2 = regApiPtr->transid[0];
+ UintR Tdata3 = regApiPtr->transid[1];
+
+ TDataPtr[0] = Tdata1;
+ TDataPtr[1] = Tdata2;
+ TDataPtr[2] = Tdata3;
+ Thostptr.p->noOfPackedWordsLqh = Tindex + 3;
+}//Dbtc::sendCompleteLqh()
+
+void
+Dbtc::execTC_COMMIT_ACK(Signal* signal){
+ jamEntry();
+
+ CommitAckMarker key;
+ key.transid1 = signal->theData[0];
+ key.transid2 = signal->theData[1];
+
+ CommitAckMarkerPtr removedMarker;
+ m_commitAckMarkerHash.release(removedMarker, key);
+ if (removedMarker.i == RNIL) {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+ sendRemoveMarkers(signal, removedMarker.p);
+}
+
+void
+Dbtc::sendRemoveMarkers(Signal* signal, const CommitAckMarker * marker){
+ jam();
+ const Uint32 noOfLqhs = marker->noOfLqhs;
+ const Uint32 transId1 = marker->transid1;
+ const Uint32 transId2 = marker->transid2;
+
+ for(Uint32 i = 0; i<noOfLqhs; i++){
+ jam();
+ const NodeId nodeId = marker->lqhNodeId[i];
+ sendRemoveMarker(signal, nodeId, transId1, transId2);
+ }
+}
+
+void
+Dbtc::sendRemoveMarker(Signal* signal,
+ NodeId nodeId,
+ Uint32 transid1,
+ Uint32 transid2){
+ /**
+ * Seize host ptr
+ */
+ HostRecordPtr hostPtr;
+ const UintR ThostFilesize = chostFilesize;
+ hostPtr.i = nodeId;
+ ptrCheckGuard(hostPtr, ThostFilesize, hostRecord);
+
+ if (hostPtr.p->noOfPackedWordsLqh > (25 - 3)){
+ jam();
+ sendPackedSignalLqh(signal, hostPtr.p);
+ } else {
+ jam();
+ updatePackedList(signal, hostPtr.p, hostPtr.i);
+ }//if
+
+ UintR numWord = hostPtr.p->noOfPackedWordsLqh;
+ UintR* dataPtr = &hostPtr.p->packedWordsLqh[numWord];
+
+ dataPtr[0] = (ZREMOVE_MARKER << 28);
+ dataPtr[1] = transid1;
+ dataPtr[2] = transid2;
+ hostPtr.p->noOfPackedWordsLqh = numWord + 3;
+}
+
+void Dbtc::execCOMPLETED(Signal* signal)
+{
+ TcConnectRecordPtr localTcConnectptr;
+ ApiConnectRecordPtr localApiConnectptr;
+
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ UintR TapiConnectFilesize = capiConnectFilesize;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+
+#ifdef ERROR_INSERT
+ if (ERROR_INSERTED(8031)) {
+ systemErrorLab(signal);
+ }//if
+ if (ERROR_INSERTED(8019)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }//if
+ if (ERROR_INSERTED(8027)) {
+ SET_ERROR_INSERT_VALUE(8028);
+ return;
+ }//if
+ if (ERROR_INSERTED(8043)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMPLETED, signal, 2000, 3);
+ return;
+ }//if
+ if (ERROR_INSERTED(8044)) {
+ SET_ERROR_INSERT_VALUE(8047);
+ sendSignalWithDelay(cownref, GSN_COMPLETED, signal, 2000, 3);
+ return;
+ }//if
+#endif
+ localTcConnectptr.i = signal->theData[0];
+ jamEntry();
+ ptrCheckGuard(localTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ bool Tcond1 = (localTcConnectptr.p->tcConnectstate != OS_COMPLETING);
+ localApiConnectptr.i = localTcConnectptr.p->apiConnect;
+ if (Tcond1) {
+ warningReport(signal, 6);
+ return;
+ }//if
+ ptrCheckGuard(localApiConnectptr, TapiConnectFilesize,
+ localApiConnectRecord);
+ UintR Tdata1 = localApiConnectptr.p->transid[0] - signal->theData[1];
+ UintR Tdata2 = localApiConnectptr.p->transid[1] - signal->theData[2];
+ UintR Tcounter = localApiConnectptr.p->counter - 1;
+ ConnectionState TapiConnectstate = localApiConnectptr.p->apiConnectstate;
+ Tdata1 = Tdata1 | Tdata2;
+ bool TcheckCondition =
+ (TapiConnectstate != CS_COMPLETE_SENT) || (Tcounter != 0);
+ if (Tdata1 != 0) {
+ warningReport(signal, 7);
+ return;
+ }//if
+ setApiConTimer(localApiConnectptr.i, ctcTimer, __LINE__);
+ localApiConnectptr.p->counter = Tcounter;
+ localTcConnectptr.p->tcConnectstate = OS_COMPLETED;
+ localTcConnectptr.p->noOfNodes = 0; // == releaseNodes(signal)
+ if (TcheckCondition) {
+ jam();
+ /*-------------------------------------------------------*/
+ // We have not sent all COMPLETE requests yet. We could be
+ // in the state that all sent are COMPLETED but we are
+ // still waiting for a CONTINUEB to send the rest of the
+ // COMPLETE requests.
+ /*-------------------------------------------------------*/
+ return;
+ }//if
+ if (ERROR_INSERTED(8021)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ apiConnectptr = localApiConnectptr;
+ releaseTransResources(signal);
+}//Dbtc::execCOMPLETED()
+
+/*---------------------------------------------------------------------------*/
+/* RELEASE_TRANS_RESOURCES */
+/* RELEASE ALL RESOURCES THAT ARE CONNECTED TO THIS TRANSACTION. */
+/*---------------------------------------------------------------------------*/
+void Dbtc::releaseTransResources(Signal* signal)
+{
+ TcConnectRecordPtr localTcConnectptr;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+
+ localTcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ do {
+ jam();
+ ptrCheckGuard(localTcConnectptr, TtcConnectFilesize, localTcConnectRecord);
+ UintR rtrTcConnectptrIndex = localTcConnectptr.p->nextTcConnect;
+ tcConnectptr.i = localTcConnectptr.i;
+ tcConnectptr.p = localTcConnectptr.p;
+ localTcConnectptr.i = rtrTcConnectptrIndex;
+ releaseTcCon();
+ } while (localTcConnectptr.i != RNIL);
+ handleGcp(signal);
+ releaseFiredTriggerData(&apiConnectptr.p->theFiredTriggers);
+ releaseAllSeizedIndexOperations(apiConnectptr.p);
+ releaseApiConCopy(signal);
+}//Dbtc::releaseTransResources()
+
+/* *********************************************************************>> */
+/* MODULE: HANDLE_GCP */
+/* DESCRIPTION: HANDLES GLOBAL CHECKPOINT HANDLING AT THE COMPLETION */
+/* OF THE COMMIT PHASE AND THE ABORT PHASE. WE MUST ENSURE THAT TC */
+/* SENDS GCP_TCFINISHED WHEN ALL TRANSACTIONS BELONGING TO A CERTAIN */
+/* GLOBAL CHECKPOINT HAVE COMPLETED. */
+/* *********************************************************************>> */
+void Dbtc::handleGcp(Signal* signal)
+{
+ GcpRecord *localGcpRecord = gcpRecord;
+ GcpRecordPtr localGcpPtr;
+ UintR TapiConnectptrIndex = apiConnectptr.i;
+ UintR TgcpFilesize = cgcpFilesize;
+ localGcpPtr.i = apiConnectptr.p->gcpPointer;
+ tmpApiConnectptr.i = TapiConnectptrIndex;
+ tmpApiConnectptr.p = apiConnectptr.p;
+ ptrCheckGuard(localGcpPtr, TgcpFilesize, localGcpRecord);
+ gcpPtr.i = localGcpPtr.i;
+ gcpPtr.p = localGcpPtr.p;
+ unlinkApiConnect(signal);
+ if (localGcpPtr.p->firstApiConnect == RNIL) {
+ if (localGcpPtr.p->gcpNomoretransRec == ZTRUE) {
+ jam();
+ tcheckGcpId = localGcpPtr.p->gcpId;
+ gcpTcfinished(signal);
+ unlinkGcp(signal);
+ }//if
+ }//if
+}//Dbtc::handleGcp()
+
+void Dbtc::releaseApiConCopy(Signal* signal)
+{
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ UintR TfirstfreeApiConnectCopyOld = cfirstfreeApiConnectCopy;
+ cfirstfreeApiConnectCopy = apiConnectptr.i;
+ regApiPtr->nextApiConnect = TfirstfreeApiConnectCopyOld;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ regApiPtr->apiConnectstate = CS_RESTART;
+}//Dbtc::releaseApiConCopy()
+
+/* ========================================================================= */
+/* ------- RELEASE ALL RECORDS CONNECTED TO A DIRTY WRITE OPERATION ------- */
+/* ========================================================================= */
+void Dbtc::releaseDirtyWrite(Signal* signal)
+{
+ unlinkReadyTcCon(signal);
+ releaseTcCon();
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
+ if (regApiPtr->firstTcConnect == RNIL) {
+ jam();
+ regApiPtr->apiConnectstate = CS_CONNECTED;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ sendtckeyconf(signal, 1);
+ }//if
+ }//if
+}//Dbtc::releaseDirtyWrite()
+
+/*****************************************************************************
+ * L Q H K E Y R E F
+ * WHEN LQHKEYREF IS RECEIVED DBTC WILL CHECK IF COMMIT FLAG WAS SENT FROM THE
+ * APPLICATION. IF SO, THE WHOLE TRANSACTION WILL BE ROLLED BACK AND SIGNAL
+ * TCROLLBACKREP WILL BE SENT TO THE API.
+ *
+ * OTHERWISE TC WILL CHECK THE ERRORCODE. IF THE ERRORCODE IS INDICATING THAT
+ * THE "ROW IS NOT FOUND" FOR UPDATE/READ/DELETE OPERATIONS AND "ROW ALREADY
+ * EXISTS" FOR INSERT OPERATIONS, DBTC WILL RELEASE THE OPERATION AND THEN
+ * SEND RETURN SIGNAL TCKEYREF TO THE USER. THE USER THEN HAVE TO SEND
+ * SIGNAL TC_COMMITREQ OR TC_ROLLBACKREQ TO CONCLUDE THE TRANSACTION.
+ * IF ANY TCKEYREQ WITH COMMIT IS RECEIVED AND API_CONNECTSTATE EQUALS
+ * "REC_LQHREFUSE",
+ * THE OPERATION WILL BE TREATED AS AN OPERATION WITHOUT COMMIT. WHEN ANY
+ * OTHER FAULTCODE IS RECEIVED THE WHOLE TRANSACTION MUST BE ROLLED BACK
+ *****************************************************************************/
+void Dbtc::execLQHKEYREF(Signal* signal)
+{
+ const LqhKeyRef * const lqhKeyRef = (LqhKeyRef *)signal->getDataPtr();
+ jamEntry();
+
+ UintR compare_transid1, compare_transid2;
+ UintR TtcConnectFilesize = ctcConnectFilesize;
+ /*-------------------------------------------------------------------------
+ *
+ * RELEASE NODE BUFFER(S) TO INDICATE THAT THIS OPERATION HAVE NO
+ * TRANSACTION PARTS ACTIVE ANYMORE.
+ * LQHKEYREF HAVE CLEARED ALL PARTS ON ITS PATH BACK TO TC.
+ *-------------------------------------------------------------------------*/
+ if (lqhKeyRef->connectPtr < TtcConnectFilesize) {
+ /*-----------------------------------------------------------------------
+ * WE HAVE TO CHECK THAT THE TRANSACTION IS STILL VALID. FIRST WE CHECK
+ * THAT THE LQH IS STILL CONNECTED TO A TC, IF THIS HOLDS TRUE THEN THE
+ * TC MUST BE CONNECTED TO AN API CONNECT RECORD.
+ * WE MUST ENSURE THAT THE TRANSACTION ID OF THIS API CONNECT
+ * RECORD IS STILL THE SAME AS THE ONE LQHKEYREF REFERS TO.
+ * IF NOT SIMPLY EXIT AND FORGET THE SIGNAL SINCE THE TRANSACTION IS
+ * ALREADY COMPLETED (ABORTED).
+ *-----------------------------------------------------------------------*/
+ tcConnectptr.i = lqhKeyRef->connectPtr;
+ Uint32 errCode = terrorCode = lqhKeyRef->errorCode;
+ ptrAss(tcConnectptr, tcConnectRecord);
+ TcConnectRecord * const regTcPtr = tcConnectptr.p;
+ if (regTcPtr->tcConnectstate == OS_OPERATING) {
+ apiConnectptr.i = regTcPtr->apiConnect;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ compare_transid1 = regApiPtr->transid[0] ^ lqhKeyRef->transId1;
+ compare_transid2 = regApiPtr->transid[1] ^ lqhKeyRef->transId2;
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ warningReport(signal, 25);
+ return;
+ }//if
+
+ const ConnectionState state = regApiPtr->apiConnectstate;
+ const Uint32 triggeringOp = regTcPtr->triggeringOperation;
+ if (triggeringOp != RNIL) {
+ jam();
+ // This operation was created by a trigger execting operation
+ TcConnectRecordPtr opPtr;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+
+ const Uint32 currentIndexId = regTcPtr->currentIndexId;
+ ndbassert(currentIndexId != 0); // Only index triggers so far
+
+ opPtr.i = triggeringOp;
+ ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
+
+ // The operation executed an index trigger
+ const Uint32 opType = regTcPtr->operation;
+ if (errCode == ZALREADYEXIST)
+ errCode = terrorCode = ZNOTUNIQUE;
+ else if (!(opType == ZDELETE && errCode == ZNOT_FOUND)) {
+ jam();
+ /**
+ * "Normal path"
+ */
+ // fall-through
+ } else {
+ jam();
+ /** ZDELETE && NOT_FOUND */
+ TcIndexData* indexData = c_theIndexes.getPtr(currentIndexId);
+ if(indexData->indexState == IS_BUILDING && state != CS_ABORTING){
+ jam();
+ /**
+ * Ignore error
+ */
+ regApiPtr->lqhkeyconfrec++;
+
+ unlinkReadyTcCon(signal);
+ releaseTcCon();
+
+ opPtr.p->triggerExecutionCount--;
+ if (opPtr.p->triggerExecutionCount == 0) {
+ /**
+ * We have completed current trigger execution
+ * Continue triggering operation
+ */
+ jam();
+ continueTriggeringOp(signal, opPtr.p);
+ }
+ return;
+ }
+ }
+ }
+
+ Uint32 marker = regTcPtr->commitAckMarker;
+ markOperationAborted(regApiPtr, regTcPtr);
+
+ if(regApiPtr->apiConnectstate == CS_ABORTING){
+ /**
+ * We're already aborting' so don't send an "extra" TCKEYREF
+ */
+ jam();
+ return;
+ }
+
+ const Uint32 abort = regTcPtr->m_execAbortOption;
+ if (abort == TcKeyReq::AbortOnError || triggeringOp != RNIL) {
+ /**
+ * No error is allowed on this operation
+ */
+ TCKEY_abort(signal, 49);
+ return;
+ }//if
+
+ if (marker != RNIL){
+ /**
+ * This was an insert/update/delete/write which failed
+ * that contained the marker
+ * Currently unsupported to place new marker
+ */
+ TCKEY_abort(signal, 49);
+ return;
+ }
+
+ /* *************** */
+ /* TCKEYREF < */
+ /* *************** */
+ TcKeyRef * const tcKeyRef = (TcKeyRef *) signal->getDataPtrSend();
+ tcKeyRef->transId[0] = regApiPtr->transid[0];
+ tcKeyRef->transId[1] = regApiPtr->transid[1];
+ tcKeyRef->errorCode = terrorCode;
+ bool isIndexOp = regTcPtr->isIndexOp;
+ Uint32 indexOp = tcConnectptr.p->indexOp;
+ Uint32 clientData = regTcPtr->clientData;
+ unlinkReadyTcCon(signal); /* LINK TC CONNECT RECORD OUT OF */
+ releaseTcCon(); /* RELEASE THE TC CONNECT RECORD */
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ if (isIndexOp) {
+ jam();
+ regApiPtr->lqhkeyreqrec--; // Compensate for extra during read
+ tcKeyRef->connectPtr = indexOp;
+ EXECUTE_DIRECT(DBTC, GSN_TCKEYREF, signal, TcKeyRef::SignalLength);
+ apiConnectptr.i = regTcPtr->apiConnect;
+ apiConnectptr.p = regApiPtr;
+ } else {
+ jam();
+ tcKeyRef->connectPtr = clientData;
+ sendSignal(regApiPtr->ndbapiBlockref,
+ GSN_TCKEYREF, signal, TcKeyRef::SignalLength, JBB);
+ }//if
+
+ /*---------------------------------------------------------------------
+ * SINCE WE ARE NOT ABORTING WE NEED TO UPDATE THE COUNT OF HOW MANY
+ * LQHKEYREQ THAT HAVE RETURNED.
+ * IF NO MORE OUTSTANDING LQHKEYREQ'S THEN WE NEED TO
+ * TCKEYCONF (IF THERE IS ANYTHING TO SEND).
+ *---------------------------------------------------------------------*/
+ regApiPtr->lqhkeyreqrec--;
+ if (regApiPtr->lqhkeyconfrec == regApiPtr->lqhkeyreqrec) {
+ if (regApiPtr->apiConnectstate == CS_START_COMMITTING) {
+ if(regApiPtr->lqhkeyconfrec) {
+ jam();
+ diverify010Lab(signal);
+ } else {
+ jam();
+ sendtckeyconf(signal, 1);
+ regApiPtr->apiConnectstate = CS_CONNECTED;
+ }
+ return;
+ } else if (regApiPtr->tckeyrec > 0 || regApiPtr->m_exec_flag) {
+ jam();
+ sendtckeyconf(signal, 2);
+ return;
+ }
+ }//if
+ return;
+
+ } else {
+ warningReport(signal, 26);
+ }//if
+ } else {
+ errorReport(signal, 6);
+ }//if
+ return;
+}//Dbtc::execLQHKEYREF()
+
+void Dbtc::clearCommitAckMarker(ApiConnectRecord * const regApiPtr,
+ TcConnectRecord * const regTcPtr)
+{
+ const Uint32 commitAckMarker = regTcPtr->commitAckMarker;
+ if (regApiPtr->commitAckMarker == RNIL)
+ ndbassert(commitAckMarker == RNIL);
+ if (commitAckMarker != RNIL)
+ ndbassert(regApiPtr->commitAckMarker != RNIL);
+ if(commitAckMarker != RNIL){
+ jam();
+ m_commitAckMarkerHash.release(commitAckMarker);
+ regTcPtr->commitAckMarker = RNIL;
+ regApiPtr->commitAckMarker = RNIL;
+ }
+}
+
+void Dbtc::markOperationAborted(ApiConnectRecord * const regApiPtr,
+ TcConnectRecord * const regTcPtr)
+{
+ /*------------------------------------------------------------------------
+ * RELEASE NODES TO INDICATE THAT THE OPERATION IS ALREADY ABORTED IN THE
+ * LQH'S ALSO SET STATE TO ABORTING TO INDICATE THE ABORT IS
+ * ALREADY COMPLETED.
+ *------------------------------------------------------------------------*/
+ regTcPtr->noOfNodes = 0; // == releaseNodes(signal)
+ regTcPtr->tcConnectstate = OS_ABORTING;
+ clearCommitAckMarker(regApiPtr, regTcPtr);
+}
+
+/*--------------------------------------*/
+/* EXIT AND WAIT FOR SIGNAL TCOMMITREQ */
+/* OR TCROLLBACKREQ FROM THE USER TO */
+/* CONTINUE THE TRANSACTION */
+/*--------------------------------------*/
+void Dbtc::execTC_COMMITREQ(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+
+ jamEntry();
+ apiConnectptr.i = signal->theData[0];
+ if (apiConnectptr.i < capiConnectFilesize) {
+ ptrAss(apiConnectptr, apiConnectRecord);
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[1];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[2];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ jam();
+ return;
+ }//if
+
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+
+ const Uint32 apiConnectPtr = regApiPtr->ndbapiConnect;
+ const Uint32 apiBlockRef = regApiPtr->ndbapiBlockref;
+ const Uint32 transId1 = regApiPtr->transid[0];
+ const Uint32 transId2 = regApiPtr->transid[1];
+ Uint32 errorCode = 0;
+
+ regApiPtr->m_exec_flag = 1;
+ switch (regApiPtr->apiConnectstate) {
+ case CS_STARTED:
+ tcConnectptr.i = regApiPtr->firstTcConnect;
+ if (tcConnectptr.i != RNIL) {
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ if (regApiPtr->lqhkeyconfrec == regApiPtr->lqhkeyreqrec) {
+ jam();
+ /*******************************************************************/
+ // The proper case where the application is waiting for commit or
+ // abort order.
+ // Start the commit order.
+ /*******************************************************************/
+ regApiPtr->returnsignal = RS_TC_COMMITCONF;
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ diverify010Lab(signal);
+ return;
+ } else {
+ jam();
+ /*******************************************************************/
+ // The transaction is started but not all operations are completed.
+ // It is not possible to commit the transaction in this state.
+ // We will abort it instead.
+ /*******************************************************************/
+ regApiPtr->returnsignal = RS_NO_RETURN;
+ errorCode = ZTRANS_STATUS_ERROR;
+ abort010Lab(signal);
+ }//if
+ } else {
+ jam();
+ /**
+ * No operations, accept commit
+ */
+ TcCommitConf * const commitConf = (TcCommitConf *)&signal->theData[0];
+ commitConf->apiConnectPtr = apiConnectPtr;
+ commitConf->transId1 = transId1;
+ commitConf->transId2 = transId2;
+
+ sendSignal(apiBlockRef, GSN_TC_COMMITCONF, signal, 3, JBB);
+
+ regApiPtr->returnsignal = RS_NO_RETURN;
+ releaseAbortResources(signal);
+ return;
+ }//if
+ break;
+ case CS_RECEIVING:
+ jam();
+ /***********************************************************************/
+ // A transaction is still receiving data. We cannot commit an unfinished
+ // transaction. We will abort it instead.
+ /***********************************************************************/
+ regApiPtr->returnsignal = RS_NO_RETURN;
+ errorCode = ZPREPAREINPROGRESS;
+ abort010Lab(signal);
+ break;
+
+ case CS_START_COMMITTING:
+ case CS_COMMITTING:
+ case CS_COMMIT_SENT:
+ case CS_COMPLETING:
+ case CS_COMPLETE_SENT:
+ case CS_REC_COMMITTING:
+ case CS_PREPARE_TO_COMMIT:
+ jam();
+ /***********************************************************************/
+ // The transaction is already performing a commit but it is not concluded
+ // yet.
+ /***********************************************************************/
+ errorCode = ZCOMMITINPROGRESS;
+ break;
+ case CS_ABORTING:
+ jam();
+ errorCode = ZABORTINPROGRESS;
+ break;
+ case CS_START_SCAN:
+ jam();
+ /***********************************************************************/
+ // The transaction is a scan. Scans cannot commit
+ /***********************************************************************/
+ errorCode = ZSCANINPROGRESS;
+ break;
+ case CS_PREPARED:
+ jam();
+ return;
+ case CS_START_PREPARING:
+ jam();
+ return;
+ case CS_REC_PREPARING:
+ jam();
+ return;
+ break;
+ default:
+ warningHandlerLab(signal);
+ return;
+ }//switch
+ TcCommitRef * const commitRef = (TcCommitRef*)&signal->theData[0];
+ commitRef->apiConnectPtr = apiConnectPtr;
+ commitRef->transId1 = transId1;
+ commitRef->transId2 = transId2;
+ commitRef->errorCode = errorCode;
+ sendSignal(apiBlockRef, GSN_TC_COMMITREF, signal,
+ TcCommitRef::SignalLength, JBB);
+ return;
+ } else /** apiConnectptr.i < capiConnectFilesize */ {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }
+}//Dbtc::execTC_COMMITREQ()
+
+void Dbtc::execTCROLLBACKREQ(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+
+ jamEntry();
+ apiConnectptr.i = signal->theData[0];
+ if (apiConnectptr.i >= capiConnectFilesize) {
+ goto TC_ROLL_warning;
+ }//if
+ ptrAss(apiConnectptr, apiConnectRecord);
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[1];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[2];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ jam();
+ return;
+ }//if
+
+ apiConnectptr.p->m_exec_flag = 1;
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_STARTED:
+ case CS_RECEIVING:
+ jam();
+ apiConnectptr.p->returnsignal = RS_TCROLLBACKCONF;
+ abort010Lab(signal);
+ return;
+ case CS_CONNECTED:
+ jam();
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_TCROLLBACKCONF,
+ signal, 3, JBB);
+ break;
+ case CS_START_SCAN:
+ case CS_PREPARE_TO_COMMIT:
+ case CS_COMMITTING:
+ case CS_COMMIT_SENT:
+ case CS_COMPLETING:
+ case CS_COMPLETE_SENT:
+ case CS_WAIT_COMMIT_CONF:
+ case CS_WAIT_COMPLETE_CONF:
+ case CS_RESTART:
+ case CS_DISCONNECTED:
+ case CS_START_COMMITTING:
+ case CS_REC_COMMITTING:
+ jam();
+ /* ***************< */
+ /* TC_ROLLBACKREF < */
+ /* ***************< */
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ signal->theData[3] = ZROLLBACKNOTALLOWED;
+ signal->theData[4] = apiConnectptr.p->apiConnectstate;
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_TCROLLBACKREF,
+ signal, 5, JBB);
+ break;
+ /* SEND A REFUSAL SIGNAL*/
+ case CS_ABORTING:
+ jam();
+ if (apiConnectptr.p->abortState == AS_IDLE) {
+ jam();
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_TCROLLBACKCONF,
+ signal, 3, JBB);
+ } else {
+ jam();
+ apiConnectptr.p->returnsignal = RS_TCROLLBACKCONF;
+ }//if
+ break;
+ case CS_WAIT_ABORT_CONF:
+ jam();
+ apiConnectptr.p->returnsignal = RS_TCROLLBACKCONF;
+ break;
+ case CS_START_PREPARING:
+ jam();
+ case CS_PREPARED:
+ jam();
+ case CS_REC_PREPARING:
+ jam();
+ default:
+ goto TC_ROLL_system_error;
+ break;
+ }//switch
+ return;
+
+TC_ROLL_warning:
+ jam();
+ warningHandlerLab(signal);
+ return;
+
+TC_ROLL_system_error:
+ jam();
+ systemErrorLab(signal);
+ return;
+}//Dbtc::execTCROLLBACKREQ()
+
+void Dbtc::execTC_HBREP(Signal* signal)
+{
+ const TcHbRep * const tcHbRep =
+ (TcHbRep *)signal->getDataPtr();
+
+ jamEntry();
+ apiConnectptr.i = tcHbRep->apiConnectPtr;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+
+ if (apiConnectptr.p->transid[0] == tcHbRep->transId1 &&
+ apiConnectptr.p->transid[1] == tcHbRep->transId2){
+
+ if (getApiConTimer(apiConnectptr.i) != 0){
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ } else {
+ DEBUG("TCHBREP received when timer was off apiConnectptr.i="
+ << apiConnectptr.i);
+ }
+ }
+}//Dbtc::execTCHBREP()
+
+/*
+4.3.15 ABORT
+-----------
+*/
+/*****************************************************************************/
+/* A B O R T */
+/* */
+/*****************************************************************************/
+void Dbtc::warningReport(Signal* signal, int place)
+{
+ switch (place) {
+ case 0:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "ABORTED to not active TC record" << endl;
+#endif
+ break;
+ case 1:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "ABORTED to TC record active with new transaction" << endl;
+#endif
+ break;
+ case 2:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "ABORTED to active TC record not expecting ABORTED" << endl;
+#endif
+ break;
+ case 3:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "ABORTED to TC rec active with trans but wrong node" << endl;
+ ndbout << "This is ok when aborting in node failure situations" << endl;
+#endif
+ break;
+ case 4:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMMITTED in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 5:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMMITTED with wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 6:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMPLETED in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 7:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMPLETED with wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 8:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMMITCONF with tc-rec in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 9:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMMITCONF with api-rec in wrong state in Dbtc" <<endl;
+#endif
+ break;
+ case 10:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMMITCONF with wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 11:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMMITCONF from wrong nodeid in Dbtc" << endl;
+#endif
+ break;
+ case 12:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMPLETECONF, tc-rec in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 13:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMPLETECONF, api-rec in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 14:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMPLETECONF with wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 15:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received COMPLETECONF from wrong nodeid in Dbtc" << endl;
+#endif
+ break;
+ case 16:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received ABORTCONF, tc-rec in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 17:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received ABORTCONF, api-rec in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 18:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received ABORTCONF with wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 19:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received ABORTCONF from wrong nodeid in Dbtc" << endl;
+#endif
+ break;
+ case 20:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Time-out waiting for ABORTCONF in Dbtc" << endl;
+#endif
+ break;
+ case 21:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Time-out waiting for COMMITCONF in Dbtc" << endl;
+#endif
+ break;
+ case 22:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Time-out waiting for COMPLETECONF in Dbtc" << endl;
+#endif
+ break;
+ case 23:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received LQHKEYCONF in wrong tc-state in Dbtc" << endl;
+#endif
+ break;
+ case 24:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received LQHKEYREF to wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 25:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received LQHKEYREF in wrong state in Dbtc" << endl;
+#endif
+ break;
+ case 26:
+ jam();
+#ifdef ABORT_TRACE
+ ndbout << "Received LQHKEYCONF to wrong transid in Dbtc" << endl;
+#endif
+ break;
+ case 27:
+ jam();
+ // printState(signal, 27);
+#ifdef ABORT_TRACE
+ ndbout << "Received LQHKEYCONF in wrong api-state in Dbtc" << endl;
+#endif
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ return;
+}//Dbtc::warningReport()
+
+void Dbtc::errorReport(Signal* signal, int place)
+{
+ switch (place) {
+ case 0:
+ jam();
+ break;
+ case 1:
+ jam();
+ break;
+ case 2:
+ jam();
+ break;
+ case 3:
+ jam();
+ break;
+ case 4:
+ jam();
+ break;
+ case 5:
+ jam();
+ break;
+ case 6:
+ jam();
+ break;
+ default:
+ jam();
+ break;
+ }//switch
+ systemErrorLab(signal);
+ return;
+}//Dbtc::errorReport()
+
+/* ------------------------------------------------------------------------- */
+/* ------- ENTER ABORTED ------- */
+/* */
+/*-------------------------------------------------------------------------- */
+void Dbtc::execABORTED(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ UintR Tnodeid = signal->theData[3];
+ UintR TlastLqhInd = signal->theData[4];
+
+ if (ERROR_INSERTED(8040)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_ABORTED, signal, 2000, 5);
+ return;
+ }//if
+ /*------------------------------------------------------------------------
+ * ONE PARTICIPANT IN THE TRANSACTION HAS REPORTED THAT IT IS ABORTED.
+ *------------------------------------------------------------------------*/
+ if (tcConnectptr.i >= ctcConnectFilesize) {
+ errorReport(signal, 0);
+ return;
+ }//if
+ /*-------------------------------------------------------------------------
+ * WE HAVE TO CHECK THAT THIS IS NOT AN OLD SIGNAL BELONGING TO A
+ * TRANSACTION ALREADY ABORTED. THIS CAN HAPPEN WHEN TIME-OUT OCCURS
+ * IN TC WAITING FOR ABORTED.
+ *-------------------------------------------------------------------------*/
+ ptrAss(tcConnectptr, tcConnectRecord);
+ if (tcConnectptr.p->tcConnectstate != OS_ABORT_SENT) {
+ warningReport(signal, 2);
+ return;
+ /*-----------------------------------------------------------------------*/
+ // ABORTED reported on an operation not expecting ABORT.
+ /*-----------------------------------------------------------------------*/
+ }//if
+ apiConnectptr.i = tcConnectptr.p->apiConnect;
+ if (apiConnectptr.i >= capiConnectFilesize) {
+ warningReport(signal, 0);
+ return;
+ }//if
+ ptrAss(apiConnectptr, apiConnectRecord);
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[1];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[2];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ warningReport(signal, 1);
+ return;
+ }//if
+ if (ERROR_INSERTED(8024)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+
+ /**
+ * Release marker
+ */
+ clearCommitAckMarker(apiConnectptr.p, tcConnectptr.p);
+
+ Uint32 i;
+ Uint32 Tfound = 0;
+ for (i = 0; i < tcConnectptr.p->noOfNodes; i++) {
+ jam();
+ if (tcConnectptr.p->tcNodedata[i] == Tnodeid) {
+ /*---------------------------------------------------------------------
+ * We have received ABORTED from one of the participants in this
+ * operation in this aborted transaction.
+ * Record all nodes that have completed abort.
+ * If last indicator is set it means that no more replica has
+ * heard of the operation and are thus also aborted.
+ *---------------------------------------------------------------------*/
+ jam();
+ Tfound = 1;
+ clearTcNodeData(signal, TlastLqhInd, i);
+ }//if
+ }//for
+ if (Tfound == 0) {
+ warningReport(signal, 3);
+ return;
+ }
+ for (i = 0; i < tcConnectptr.p->noOfNodes; i++) {
+ if (tcConnectptr.p->tcNodedata[i] != 0) {
+ /*--------------------------------------------------------------------
+ * There are still outstanding ABORTED's to wait for.
+ *--------------------------------------------------------------------*/
+ jam();
+ return;
+ }//if
+ }//for
+ tcConnectptr.p->noOfNodes = 0;
+ tcConnectptr.p->tcConnectstate = OS_ABORTING;
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ apiConnectptr.p->counter--;
+ if (apiConnectptr.p->counter > 0) {
+ jam();
+ /*----------------------------------------------------------------------
+ * WE ARE STILL WAITING FOR MORE PARTICIPANTS TO SEND ABORTED.
+ *----------------------------------------------------------------------*/
+ return;
+ }//if
+ /*------------------------------------------------------------------------*/
+ /* */
+ /* WE HAVE NOW COMPLETED THE ABORT PROCESS. WE HAVE RECEIVED ABORTED */
+ /* FROM ALL PARTICIPANTS IN THE TRANSACTION. WE CAN NOW RELEASE ALL */
+ /* RESOURCES CONNECTED TO THE TRANSACTION AND SEND THE ABORT RESPONSE */
+ /*------------------------------------------------------------------------*/
+ releaseAbortResources(signal);
+}//Dbtc::execABORTED()
+
+void Dbtc::clearTcNodeData(Signal* signal,
+ UintR TLastLqhIndicator,
+ UintR Tstart)
+{
+ UintR Ti;
+ if (TLastLqhIndicator == ZTRUE) {
+ for (Ti = Tstart ; Ti < tcConnectptr.p->noOfNodes; Ti++) {
+ jam();
+ tcConnectptr.p->tcNodedata[Ti] = 0;
+ }//for
+ } else {
+ jam();
+ tcConnectptr.p->tcNodedata[Tstart] = 0;
+ }//for
+}//clearTcNodeData()
+
+void Dbtc::abortErrorLab(Signal* signal)
+{
+ ptrGuard(apiConnectptr);
+ ApiConnectRecord * transP = apiConnectptr.p;
+ if (transP->apiConnectstate == CS_ABORTING && transP->abortState != AS_IDLE){
+ jam();
+ return;
+ }
+ transP->returnsignal = RS_TCROLLBACKREP;
+ if(transP->returncode == 0){
+ jam();
+ transP->returncode = terrorCode;
+ }
+ abort010Lab(signal);
+}//Dbtc::abortErrorLab()
+
+void Dbtc::abort010Lab(Signal* signal)
+{
+ ApiConnectRecord * transP = apiConnectptr.p;
+ if (transP->apiConnectstate == CS_ABORTING && transP->abortState != AS_IDLE){
+ jam();
+ return;
+ }
+ transP->apiConnectstate = CS_ABORTING;
+ /*------------------------------------------------------------------------*/
+ /* AN ABORT DECISION HAS BEEN TAKEN FOR SOME REASON. WE NEED TO ABORT */
+ /* ALL PARTICIPANTS IN THE TRANSACTION. */
+ /*------------------------------------------------------------------------*/
+ transP->abortState = AS_ACTIVE;
+ transP->counter = 0;
+
+ if (transP->firstTcConnect == RNIL) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ /* WE HAVE NO PARTICIPANTS IN THE TRANSACTION. */
+ /*-----------------------------------------------------------------------*/
+ releaseAbortResources(signal);
+ return;
+ }//if
+ tcConnectptr.i = transP->firstTcConnect;
+ abort015Lab(signal);
+}//Dbtc::abort010Lab()
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* WE WILL ABORT ONE NODE PER OPERATION AT A TIME. THIS IS TO KEEP */
+/* ERROR HANDLING OF THIS PROCESS FAIRLY SIMPLE AND TRACTABLE. */
+/* EVEN IF NO NODE OF THIS PARTICULAR NODE NUMBER NEEDS ABORTION WE */
+/* MUST ENSURE THAT ALL NODES ARE CHECKED. THUS A FAULTY NODE DOES */
+/* NOT MEAN THAT ALL NODES IN AN OPERATION IS ABORTED. FOR THIS REASON*/
+/* WE SET THE TCONTINUE_ABORT TO TRUE WHEN A FAULTY NODE IS DETECTED. */
+/*--------------------------------------------------------------------------*/
+void Dbtc::abort015Lab(Signal* signal)
+{
+ Uint32 TloopCount = 0;
+ABORT020:
+ jam();
+ TloopCount++;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ switch (tcConnectptr.p->tcConnectstate) {
+ case OS_WAIT_DIH:
+ case OS_WAIT_KEYINFO:
+ case OS_WAIT_ATTR:
+ jam();
+ /*----------------------------------------------------------------------*/
+ /* WE ARE STILL WAITING FOR MORE KEYINFO/ATTRINFO. WE HAVE NOT CONTACTED*/
+ /* ANY LQH YET AND SO WE CAN SIMPLY SET STATE TO ABORTING. */
+ /*----------------------------------------------------------------------*/
+ tcConnectptr.p->noOfNodes = 0; // == releaseAbort(signal)
+ tcConnectptr.p->tcConnectstate = OS_ABORTING;
+ break;
+ case OS_CONNECTED:
+ jam();
+ /*-----------------------------------------------------------------------
+ * WE ARE STILL IN THE INITIAL PHASE OF THIS OPERATION.
+ * NEED NOT BOTHER ABOUT ANY LQH ABORTS.
+ *-----------------------------------------------------------------------*/
+ tcConnectptr.p->noOfNodes = 0; // == releaseAbort(signal)
+ tcConnectptr.p->tcConnectstate = OS_ABORTING;
+ break;
+ case OS_PREPARED:
+ jam();
+ case OS_OPERATING:
+ jam();
+ /*----------------------------------------------------------------------
+ * WE HAVE SENT LQHKEYREQ AND ARE IN SOME STATE OF EITHER STILL
+ * SENDING THE OPERATION, WAITING FOR REPLIES, WAITING FOR MORE
+ * ATTRINFO OR OPERATION IS PREPARED. WE NEED TO ABORT ALL LQH'S.
+ *----------------------------------------------------------------------*/
+ releaseAndAbort(signal);
+ tcConnectptr.p->tcConnectstate = OS_ABORT_SENT;
+ TloopCount += 127;
+ break;
+ case OS_ABORTING:
+ jam();
+ break;
+ case OS_ABORT_SENT:
+ jam();
+ DEBUG("ABORT_SENT state in abort015Lab(), not expected");
+ systemErrorLab(signal);
+ return;
+ default:
+ jam();
+ DEBUG("tcConnectstate = " << tcConnectptr.p->tcConnectstate);
+ systemErrorLab(signal);
+ return;
+ }//switch
+
+ if (tcConnectptr.p->nextTcConnect != RNIL) {
+ jam();
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ if (TloopCount < 1024) {
+ goto ABORT020;
+ } else {
+ jam();
+ /*---------------------------------------------------------------------
+ * Reset timer to avoid time-out in real-time break.
+ * Increase counter to ensure that we don't think that all ABORTED have
+ * been received before all have been sent.
+ *---------------------------------------------------------------------*/
+ apiConnectptr.p->counter++;
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ signal->theData[0] = TcContinueB::ZABORT_BREAK;
+ signal->theData[1] = tcConnectptr.i;
+ signal->theData[2] = apiConnectptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }//if
+ }//if
+ if (apiConnectptr.p->counter > 0) {
+ jam();
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ return;
+ }//if
+ /*-----------------------------------------------------------------------
+ * WE HAVE NOW COMPLETED THE ABORT PROCESS. WE HAVE RECEIVED ABORTED
+ * FROM ALL PARTICIPANTS IN THE TRANSACTION. WE CAN NOW RELEASE ALL
+ * RESOURCES CONNECTED TO THE TRANSACTION AND SEND THE ABORT RESPONSE
+ *------------------------------------------------------------------------*/
+ releaseAbortResources(signal);
+}//Dbtc::abort015Lab()
+
+/*--------------------------------------------------------------------------*/
+/* RELEASE KEY AND ATTRINFO OBJECTS AND SEND ABORT TO THE LQH BLOCK. */
+/*--------------------------------------------------------------------------*/
+int Dbtc::releaseAndAbort(Signal* signal)
+{
+ HostRecordPtr localHostptr;
+ UintR TnoLoops = tcConnectptr.p->noOfNodes;
+
+ apiConnectptr.p->counter++;
+ bool prevAlive = false;
+ for (Uint32 Ti = 0; Ti < TnoLoops ; Ti++) {
+ localHostptr.i = tcConnectptr.p->tcNodedata[Ti];
+ ptrCheckGuard(localHostptr, chostFilesize, hostRecord);
+ if (localHostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ if (prevAlive) {
+ // if previous is alive, its LQH forwards abort to this node
+ jam();
+ continue;
+ }
+ /* ************< */
+ /* ABORT < */
+ /* ************< */
+ tblockref = calcLqhBlockRef(localHostptr.i);
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = apiConnectptr.p->transid[0];
+ signal->theData[3] = apiConnectptr.p->transid[1];
+ sendSignal(tblockref, GSN_ABORT, signal, 4, JBB);
+ prevAlive = true;
+ } else {
+ jam();
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ signal->theData[3] = localHostptr.i;
+ signal->theData[4] = ZFALSE;
+ sendSignal(cownref, GSN_ABORTED, signal, 5, JBB);
+ prevAlive = false;
+ }//if
+ }//for
+ return 1;
+}//Dbtc::releaseAndAbort()
+
+/* ------------------------------------------------------------------------- */
+/* ------- ENTER TIME_SIGNAL ------- */
+/* */
+/* ------------------------------------------------------------------------- */
+void Dbtc::execTIME_SIGNAL(Signal* signal)
+{
+
+ jamEntry();
+ ctcTimer++;
+ if (csystemStart != SSS_TRUE) {
+ jam();
+ return;
+ }//if
+ checkStartTimeout(signal);
+ checkStartFragTimeout(signal);
+}//Dbtc::execTIME_SIGNAL()
+
+/*------------------------------------------------*/
+/* Start timeout handling if not already going on */
+/*------------------------------------------------*/
+void Dbtc::checkStartTimeout(Signal* signal)
+{
+ ctimeOutCheckCounter++;
+ if (ctimeOutCheckActive == TOCS_TRUE) {
+ jam();
+ // Check heartbeat of timeout loop
+ if(ctimeOutCheckHeartbeat > ctimeOutCheckLastHeartbeat){
+ jam();
+ ctimeOutMissedHeartbeats = 0;
+ }else{
+ jam();
+ ctimeOutMissedHeartbeats++;
+ if (ctimeOutMissedHeartbeats > 100){
+ jam();
+ systemErrorLab(signal);
+ }
+ }
+ ctimeOutCheckLastHeartbeat = ctimeOutCheckHeartbeat;
+ return;
+ }//if
+ if (ctimeOutCheckCounter < ctimeOutCheckDelay) {
+ jam();
+ /*------------------------------------------------------------------*/
+ /* */
+ /* NO TIME-OUT CHECKED THIS TIME. WAIT MORE. */
+ /*------------------------------------------------------------------*/
+ return;
+ }//if
+ ctimeOutCheckActive = TOCS_TRUE;
+ ctimeOutCheckCounter = 0;
+ timeOutLoopStartLab(signal, 0); // 0 is first api connect record
+ return;
+}//Dbtc::execTIME_SIGNAL()
+
+/*----------------------------------------------------------------*/
+/* Start fragment (scan) timeout handling if not already going on */
+/*----------------------------------------------------------------*/
+void Dbtc::checkStartFragTimeout(Signal* signal)
+{
+ ctimeOutCheckFragCounter++;
+ if (ctimeOutCheckFragActive == TOCS_TRUE) {
+ jam();
+ return;
+ }//if
+ if (ctimeOutCheckFragCounter < ctimeOutCheckDelay) {
+ jam();
+ /*------------------------------------------------------------------*/
+ /* NO TIME-OUT CHECKED THIS TIME. WAIT MORE. */
+ /*------------------------------------------------------------------*/
+ return;
+ }//if
+
+ // Go through the fragment records and look for timeout in a scan.
+ ctimeOutCheckFragActive = TOCS_TRUE;
+ ctimeOutCheckFragCounter = 0;
+ timeOutLoopStartFragLab(signal, 0); // 0 means first scan record
+}//checkStartFragTimeout()
+
+/*------------------------------------------------------------------*/
+/* IT IS NOW TIME TO CHECK WHETHER ANY TRANSACTIONS HAVE */
+/* BEEN DELAYED FOR SO LONG THAT WE ARE FORCED TO PERFORM */
+/* SOME ACTION, EITHER ABORT OR RESEND OR REMOVE A NODE FROM */
+/* THE WAITING PART OF A PROTOCOL. */
+/*
+The algorithm used here is to check 1024 transactions at a time before
+doing a real-time break.
+To avoid aborting both transactions in a deadlock detected by time-out
+we insert a random extra time-out of upto 630 ms by using the lowest
+six bits of the api connect reference.
+We spread it out from 0 to 630 ms if base time-out is larger than 3 sec,
+we spread it out from 0 to 70 ms if base time-out is smaller than 300 msec,
+and otherwise we spread it out 310 ms.
+*/
+/*------------------------------------------------------------------*/
+void Dbtc::timeOutLoopStartLab(Signal* signal, Uint32 api_con_ptr)
+{
+ Uint32 end_ptr, time_passed, time_out_value, mask_value;
+ const Uint32 api_con_sz= capiConnectFilesize;
+ const Uint32 tc_timer= ctcTimer;
+ const Uint32 time_out_param= ctimeOutValue;
+
+ ctimeOutCheckHeartbeat = tc_timer;
+
+ if (api_con_ptr + 1024 < api_con_sz) {
+ jam();
+ end_ptr= api_con_ptr + 1024;
+ } else {
+ jam();
+ end_ptr= api_con_sz;
+ }
+ if (time_out_param > 300) {
+ jam();
+ mask_value= 63;
+ } else if (time_out_param < 30) {
+ jam();
+ mask_value= 7;
+ } else {
+ jam();
+ mask_value= 31;
+ }
+ for ( ; api_con_ptr < end_ptr; api_con_ptr++) {
+ Uint32 api_timer= getApiConTimer(api_con_ptr);
+ jam();
+ if (api_timer != 0) {
+ time_out_value= time_out_param + (api_con_ptr & mask_value);
+ time_passed= tc_timer - api_timer;
+ if (time_passed > time_out_value) {
+ jam();
+ timeOutFoundLab(signal, api_con_ptr);
+ return;
+ }
+ }
+ }
+ if (api_con_ptr == api_con_sz) {
+ jam();
+ /*------------------------------------------------------------------*/
+ /* */
+ /* WE HAVE NOW CHECKED ALL TRANSACTIONS FOR TIME-OUT AND ALSO */
+ /* STARTED TIME-OUT HANDLING OF THOSE WE FOUND. WE ARE NOW */
+ /* READY AND CAN WAIT FOR THE NEXT TIME-OUT CHECK. */
+ /*------------------------------------------------------------------*/
+ ctimeOutCheckActive = TOCS_FALSE;
+ } else {
+ jam();
+ sendContinueTimeOutControl(signal, api_con_ptr);
+ }
+ return;
+}//Dbtc::timeOutLoopStartLab()
+
+void Dbtc::timeOutFoundLab(Signal* signal, Uint32 TapiConPtr)
+{
+ sendContinueTimeOutControl(signal, TapiConPtr + 1);
+
+ apiConnectptr.i = TapiConPtr;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ /*------------------------------------------------------------------*/
+ /* */
+ /* THIS TRANSACTION HAVE EXPERIENCED A TIME-OUT AND WE NEED TO*/
+ /* FIND OUT WHAT WE NEED TO DO BASED ON THE STATE INFORMATION.*/
+ /*------------------------------------------------------------------*/
+ DEBUG("[ H'" << hex << apiConnectptr.p->transid[0]
+ << " H'" << apiConnectptr.p->transid[1] << "] " << dec
+ << "Time-out in state = " << apiConnectptr.p->apiConnectstate
+ << " apiConnectptr.i = " << apiConnectptr.i
+ << " - exec: " << apiConnectptr.p->m_exec_flag
+ << " - place: " << c_apiConTimer_line[apiConnectptr.i]);
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_STARTED:
+ ndbrequire(c_apiConTimer_line[apiConnectptr.i] != 3615);
+ if(apiConnectptr.p->lqhkeyreqrec == apiConnectptr.p->lqhkeyconfrec){
+ jam();
+ /*
+ We are waiting for application to continue the transaction. In this
+ particular state we will use the application timeout parameter rather
+ than the shorter Deadlock detection timeout.
+ */
+ if ((ctcTimer - getApiConTimer(apiConnectptr.i)) <= c_appl_timeout_value) {
+ jam();
+ return;
+ }//if
+ }
+ apiConnectptr.p->returnsignal = RS_TCROLLBACKREP;
+ apiConnectptr.p->returncode = ZTIME_OUT_ERROR;
+ abort010Lab(signal);
+ return;
+ case CS_RECEIVING:
+ case CS_REC_COMMITTING:
+ case CS_START_COMMITTING:
+ jam();
+ /*------------------------------------------------------------------*/
+ /* WE ARE STILL IN THE PREPARE PHASE AND THE TRANSACTION HAS */
+ /* NOT YET REACHED ITS COMMIT POINT. THUS IT IS NOW OK TO */
+ /* START ABORTING THE TRANSACTION. ALSO START CHECKING THE */
+ /* REMAINING TRANSACTIONS. */
+ /*------------------------------------------------------------------*/
+ terrorCode = ZTIME_OUT_ERROR;
+ abortErrorLab(signal);
+ return;
+ case CS_COMMITTING:
+ jam();
+ /*------------------------------------------------------------------*/
+ // We are simply waiting for a signal in the job buffer. Only extreme
+ // conditions should get us here. We ignore it.
+ /*------------------------------------------------------------------*/
+ case CS_COMPLETING:
+ jam();
+ /*------------------------------------------------------------------*/
+ // We are simply waiting for a signal in the job buffer. Only extreme
+ // conditions should get us here. We ignore it.
+ /*------------------------------------------------------------------*/
+ case CS_PREPARE_TO_COMMIT:
+ jam();
+ /*------------------------------------------------------------------*/
+ /* WE ARE WAITING FOR DIH TO COMMIT THE TRANSACTION. WE SIMPLY*/
+ /* KEEP WAITING SINCE THERE IS NO BETTER IDEA ON WHAT TO DO. */
+ /* IF IT IS BLOCKED THEN NO TRANSACTION WILL PASS THIS GATE. */
+ // To ensure against strange bugs we crash the system if we have passed
+ // time-out period by a factor of 10 and it is also at least 5 seconds.
+ /*------------------------------------------------------------------*/
+ if (((ctcTimer - getApiConTimer(apiConnectptr.i)) > (10 * ctimeOutValue)) &&
+ ((ctcTimer - getApiConTimer(apiConnectptr.i)) > 500)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ break;
+ case CS_COMMIT_SENT:
+ jam();
+ /*------------------------------------------------------------------*/
+ /* WE HAVE SENT COMMIT TO A NUMBER OF NODES. WE ARE CURRENTLY */
+ /* WAITING FOR THEIR REPLY. WITH NODE RECOVERY SUPPORTED WE */
+ /* WILL CHECK FOR CRASHED NODES AND RESEND THE COMMIT SIGNAL */
+ /* TO THOSE NODES THAT HAVE MISSED THE COMMIT SIGNAL DUE TO */
+ /* A NODE FAILURE. */
+ /*------------------------------------------------------------------*/
+ tabortInd = ZCOMMIT_SETUP;
+ setupFailData(signal);
+ toCommitHandlingLab(signal);
+ return;
+ case CS_COMPLETE_SENT:
+ jam();
+ /*--------------------------------------------------------------------*/
+ /* WE HAVE SENT COMPLETE TO A NUMBER OF NODES. WE ARE CURRENTLY */
+ /* WAITING FOR THEIR REPLY. WITH NODE RECOVERY SUPPORTED WE */
+ /* WILL CHECK FOR CRASHED NODES AND RESEND THE COMPLETE SIGNAL */
+ /* TO THOSE NODES THAT HAVE MISSED THE COMPLETE SIGNAL DUE TO */
+ /* A NODE FAILURE. */
+ /*--------------------------------------------------------------------*/
+ tabortInd = ZCOMMIT_SETUP;
+ setupFailData(signal);
+ toCompleteHandlingLab(signal);
+ return;
+ case CS_ABORTING:
+ jam();
+ /*------------------------------------------------------------------*/
+ /* TIME-OUT DURING ABORT. WE NEED TO SEND ABORTED FOR ALL */
+ /* NODES THAT HAVE FAILED BEFORE SENDING ABORTED. */
+ /*------------------------------------------------------------------*/
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ sendAbortedAfterTimeout(signal, 0);
+ break;
+ case CS_START_SCAN:{
+ jam();
+ ScanRecordPtr scanPtr;
+ scanPtr.i = apiConnectptr.p->apiScanRec;
+ ptrCheckGuard(scanPtr, cscanrecFileSize, scanRecord);
+ scanError(signal, scanPtr, ZSCANTIME_OUT_ERROR);
+ break;
+ }
+ case CS_WAIT_ABORT_CONF:
+ jam();
+ tcConnectptr.i = apiConnectptr.p->currentTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ arrGuard(apiConnectptr.p->currentReplicaNo, 4);
+ hostptr.i = tcConnectptr.p->tcNodedata[apiConnectptr.p->currentReplicaNo];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ /*------------------------------------------------------------------*/
+ // Time-out waiting for ABORTCONF. We will resend the ABORTREQ just in
+ // case.
+ /*------------------------------------------------------------------*/
+ warningReport(signal, 20);
+ apiConnectptr.p->timeOutCounter++;
+ if (apiConnectptr.p->timeOutCounter > 3) {
+ /*------------------------------------------------------------------*/
+ // 100 time-outs are not acceptable. We will shoot down the node
+ // not responding.
+ /*------------------------------------------------------------------*/
+ reportNodeFailed(signal, hostptr.i);
+ }//if
+ apiConnectptr.p->currentReplicaNo++;
+ }//if
+ tcurrentReplicaNo = (Uint8)Z8NIL;
+ toAbortHandlingLab(signal);
+ return;
+ case CS_WAIT_COMMIT_CONF:
+ jam();
+ tcConnectptr.i = apiConnectptr.p->currentTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ arrGuard(apiConnectptr.p->currentReplicaNo, 4);
+ hostptr.i = tcConnectptr.p->tcNodedata[apiConnectptr.p->currentReplicaNo];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ /*------------------------------------------------------------------*/
+ // Time-out waiting for COMMITCONF. We will resend the COMMITREQ just in
+ // case.
+ /*------------------------------------------------------------------*/
+ warningReport(signal, 21);
+ apiConnectptr.p->timeOutCounter++;
+ if (apiConnectptr.p->timeOutCounter > 3) {
+ /*------------------------------------------------------------------*/
+ // 100 time-outs are not acceptable. We will shoot down the node
+ // not responding.
+ /*------------------------------------------------------------------*/
+ reportNodeFailed(signal, hostptr.i);
+ }//if
+ apiConnectptr.p->currentReplicaNo++;
+ }//if
+ tcurrentReplicaNo = (Uint8)Z8NIL;
+ toCommitHandlingLab(signal);
+ return;
+ case CS_WAIT_COMPLETE_CONF:
+ jam();
+ tcConnectptr.i = apiConnectptr.p->currentTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ arrGuard(apiConnectptr.p->currentReplicaNo, 4);
+ hostptr.i = tcConnectptr.p->tcNodedata[apiConnectptr.p->currentReplicaNo];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ /*------------------------------------------------------------------*/
+ // Time-out waiting for COMPLETECONF. We will resend the COMPLETEREQ
+ // just in case.
+ /*------------------------------------------------------------------*/
+ warningReport(signal, 22);
+ apiConnectptr.p->timeOutCounter++;
+ if (apiConnectptr.p->timeOutCounter > 100) {
+ /*------------------------------------------------------------------*/
+ // 100 time-outs are not acceptable. We will shoot down the node
+ // not responding.
+ /*------------------------------------------------------------------*/
+ reportNodeFailed(signal, hostptr.i);
+ }//if
+ apiConnectptr.p->currentReplicaNo++;
+ }//if
+ tcurrentReplicaNo = (Uint8)Z8NIL;
+ toCompleteHandlingLab(signal);
+ return;
+ case CS_FAIL_PREPARED:
+ jam();
+ case CS_FAIL_COMMITTING:
+ jam();
+ case CS_FAIL_COMMITTED:
+ jam();
+ case CS_REC_PREPARING:
+ jam();
+ case CS_START_PREPARING:
+ jam();
+ case CS_PREPARED:
+ jam();
+ case CS_RESTART:
+ jam();
+ case CS_FAIL_ABORTED:
+ jam();
+ case CS_DISCONNECTED:
+ jam();
+ default:
+ jam();
+ /*------------------------------------------------------------------*/
+ /* AN IMPOSSIBLE STATE IS SET. CRASH THE SYSTEM. */
+ /*------------------------------------------------------------------*/
+ DEBUG("State = " << apiConnectptr.p->apiConnectstate);
+ systemErrorLab(signal);
+ return;
+ }//switch
+ return;
+}//Dbtc::timeOutFoundLab()
+
+void Dbtc::sendAbortedAfterTimeout(Signal* signal, int Tcheck)
+{
+ ApiConnectRecord * transP = apiConnectptr.p;
+ if(transP->abortState == AS_IDLE){
+ jam();
+ warningEvent("TC: %d: %d state=%d abort==IDLE place: %d fop=%d t: %d",
+ __LINE__,
+ apiConnectptr.i,
+ transP->apiConnectstate,
+ c_apiConTimer_line[apiConnectptr.i],
+ transP->firstTcConnect,
+ c_apiConTimer[apiConnectptr.i]
+ );
+ ndbout_c("TC: %d: %d state=%d abort==IDLE place: %d fop=%d t: %d",
+ __LINE__,
+ apiConnectptr.i,
+ transP->apiConnectstate,
+ c_apiConTimer_line[apiConnectptr.i],
+ transP->firstTcConnect,
+ c_apiConTimer[apiConnectptr.i]
+ );
+ ndbrequire(false);
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ return;
+ }
+
+ OperationState tmp[16];
+
+ Uint32 TloopCount = 0;
+ do {
+ jam();
+ if (tcConnectptr.i == RNIL) {
+ jam();
+ if (Tcheck == 0) {
+ jam();
+ /*------------------------------------------------------------------
+ * All nodes had already reported ABORTED for all tcConnect records.
+ * Crash since it is an error situation that we then received a
+ * time-out.
+ *------------------------------------------------------------------*/
+ char buf[96]; buf[0] = 0;
+ char buf2[96];
+ BaseString::snprintf(buf, sizeof(buf), "TC %d: %d ops:",
+ __LINE__, apiConnectptr.i);
+ for(Uint32 i = 0; i<TloopCount; i++){
+ BaseString::snprintf(buf2, sizeof(buf2), "%s %d", buf, tmp[i]);
+ BaseString::snprintf(buf, sizeof(buf), buf2);
+ }
+ warningEvent(buf);
+ ndbout_c(buf);
+ ndbrequire(false);
+ }
+ releaseAbortResources(signal);
+ return;
+ }//if
+ TloopCount++;
+ if (TloopCount >= 1024) {
+ jam();
+ /*------------------------------------------------------------------*/
+ // Insert a real-time break for large transactions to avoid blowing
+ // away the job buffer.
+ /*------------------------------------------------------------------*/
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ apiConnectptr.p->counter++;
+ signal->theData[0] = TcContinueB::ZABORT_TIMEOUT_BREAK;
+ signal->theData[1] = tcConnectptr.i;
+ signal->theData[2] = apiConnectptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }//if
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ if(TloopCount < 16){
+ jam();
+ tmp[TloopCount-1] = tcConnectptr.p->tcConnectstate;
+ }
+
+ if (tcConnectptr.p->tcConnectstate == OS_ABORT_SENT) {
+ jam();
+ /*------------------------------------------------------------------*/
+ // We have sent an ABORT signal to this node but not yet received any
+ // reply. We have to send an ABORTED signal on our own in some cases.
+ // If the node is declared as up and running and still do not respond
+ // in time to the ABORT signal we will declare it as dead.
+ /*------------------------------------------------------------------*/
+ UintR Ti = 0;
+ arrGuard(tcConnectptr.p->noOfNodes, 4);
+ for (Ti = 0; Ti < tcConnectptr.p->noOfNodes; Ti++) {
+ jam();
+ if (tcConnectptr.p->tcNodedata[Ti] != 0) {
+ TloopCount += 31;
+ Tcheck = 1;
+ hostptr.i = tcConnectptr.p->tcNodedata[Ti];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ /*---------------------------------------------------------------
+ * A backup replica has not sent ABORTED.
+ * Could be that a node before him has crashed.
+ * Send an ABORT signal specifically to this node.
+ * We will not send to any more nodes after this
+ * to avoid race problems.
+ * To also ensure that we use this message also as a heartbeat
+ * we will move this node to the primary replica seat.
+ * The primary replica and any failed node after it will
+ * be removed from the node list. Update also number of nodes.
+ * Finally break the loop to ensure we don't mess
+ * things up by executing another loop.
+ * We also update the timer to ensure we don't get time-out
+ * too early.
+ *--------------------------------------------------------------*/
+ BlockReference TBRef = calcLqhBlockRef(hostptr.i);
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = apiConnectptr.p->transid[0];
+ signal->theData[3] = apiConnectptr.p->transid[1];
+ sendSignal(TBRef, GSN_ABORT, signal, 4, JBB);
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ break;
+ } else {
+ jam();
+ /*--------------------------------------------------------------
+ * The node we are waiting for is dead. We will send ABORTED to
+ * ourselves vicarious for the failed node.
+ *--------------------------------------------------------------*/
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ signal->theData[3] = hostptr.i;
+ signal->theData[4] = ZFALSE;
+ sendSignal(cownref, GSN_ABORTED, signal, 5, JBB);
+ }//if
+ }//if
+ }//for
+ }//if
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ } while (1);
+}//Dbtc::sendAbortedAfterTimeout()
+
+void Dbtc::reportNodeFailed(Signal* signal, Uint32 nodeId)
+{
+ DisconnectRep * const rep = (DisconnectRep *)&signal->theData[0];
+ rep->nodeId = nodeId;
+ rep->err = DisconnectRep::TcReportNodeFailed;
+ sendSignal(QMGR_REF, GSN_DISCONNECT_REP, signal,
+ DisconnectRep::SignalLength, JBB);
+}//Dbtc::reportNodeFailed()
+
+/*-------------------------------------------------*/
+/* Timeout-loop for scanned fragments. */
+/*-------------------------------------------------*/
+void Dbtc::timeOutLoopStartFragLab(Signal* signal, Uint32 TscanConPtr)
+{
+ ScanFragRecPtr timeOutPtr[8];
+ UintR tfragTimer[8];
+ UintR texpiredTime[8];
+ UintR TloopCount = 0;
+ Uint32 TtcTimer = ctcTimer;
+
+ while ((TscanConPtr + 8) < cscanFragrecFileSize) {
+ jam();
+ timeOutPtr[0].i = TscanConPtr + 0;
+ timeOutPtr[1].i = TscanConPtr + 1;
+ timeOutPtr[2].i = TscanConPtr + 2;
+ timeOutPtr[3].i = TscanConPtr + 3;
+ timeOutPtr[4].i = TscanConPtr + 4;
+ timeOutPtr[5].i = TscanConPtr + 5;
+ timeOutPtr[6].i = TscanConPtr + 6;
+ timeOutPtr[7].i = TscanConPtr + 7;
+
+ c_scan_frag_pool.getPtrForce(timeOutPtr[0]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[1]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[2]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[3]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[4]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[5]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[6]);
+ c_scan_frag_pool.getPtrForce(timeOutPtr[7]);
+
+ tfragTimer[0] = timeOutPtr[0].p->scanFragTimer;
+ tfragTimer[1] = timeOutPtr[1].p->scanFragTimer;
+ tfragTimer[2] = timeOutPtr[2].p->scanFragTimer;
+ tfragTimer[3] = timeOutPtr[3].p->scanFragTimer;
+ tfragTimer[4] = timeOutPtr[4].p->scanFragTimer;
+ tfragTimer[5] = timeOutPtr[5].p->scanFragTimer;
+ tfragTimer[6] = timeOutPtr[6].p->scanFragTimer;
+ tfragTimer[7] = timeOutPtr[7].p->scanFragTimer;
+
+ texpiredTime[0] = TtcTimer - tfragTimer[0];
+ texpiredTime[1] = TtcTimer - tfragTimer[1];
+ texpiredTime[2] = TtcTimer - tfragTimer[2];
+ texpiredTime[3] = TtcTimer - tfragTimer[3];
+ texpiredTime[4] = TtcTimer - tfragTimer[4];
+ texpiredTime[5] = TtcTimer - tfragTimer[5];
+ texpiredTime[6] = TtcTimer - tfragTimer[6];
+ texpiredTime[7] = TtcTimer - tfragTimer[7];
+
+ for (Uint32 Ti = 0; Ti < 8; Ti++) {
+ jam();
+ if (tfragTimer[Ti] != 0) {
+
+ if (texpiredTime[Ti] > ctimeOutValue) {
+ jam();
+ DEBUG("Fragment timeout found:"<<
+ " ctimeOutValue=" <<ctimeOutValue
+ <<", texpiredTime="<<texpiredTime[Ti]<<endl
+ <<" tfragTimer="<<tfragTimer[Ti]
+ <<", ctcTimer="<<ctcTimer);
+ timeOutFoundFragLab(signal, TscanConPtr + Ti);
+ return;
+ }//if
+ }//if
+ }//for
+ TscanConPtr += 8;
+ /*----------------------------------------------------------------*/
+ /* We split the process up checking 1024 fragmentrecords at a time*/
+ /* to maintain real time behaviour. */
+ /*----------------------------------------------------------------*/
+ if (TloopCount++ > 128 ) {
+ jam();
+ signal->theData[0] = TcContinueB::ZCONTINUE_TIME_OUT_FRAG_CONTROL;
+ signal->theData[1] = TscanConPtr;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+ }//while
+ for ( ; TscanConPtr < cscanFragrecFileSize; TscanConPtr++){
+ jam();
+ timeOutPtr[0].i = TscanConPtr;
+ c_scan_frag_pool.getPtrForce(timeOutPtr[0]);
+ if (timeOutPtr[0].p->scanFragTimer != 0) {
+ texpiredTime[0] = ctcTimer - timeOutPtr[0].p->scanFragTimer;
+ if (texpiredTime[0] > ctimeOutValue) {
+ jam();
+ DEBUG("Fragment timeout found:"<<
+ " ctimeOutValue=" <<ctimeOutValue
+ <<", texpiredTime="<<texpiredTime[0]<<endl
+ <<" tfragTimer="<<tfragTimer[0]
+ <<", ctcTimer="<<ctcTimer);
+ timeOutFoundFragLab(signal, TscanConPtr);
+ return;
+ }//if
+ }//if
+ }//for
+ ctimeOutCheckFragActive = TOCS_FALSE;
+
+ return;
+}//timeOutLoopStartFragLab()
+
+/*--------------------------------------------------------------------------*/
+/*Handle the heartbeat signal from LQH in a scan process */
+// (Set timer on fragrec.)
+/*--------------------------------------------------------------------------*/
+void Dbtc::execSCAN_HBREP(Signal* signal)
+{
+ jamEntry();
+
+ scanFragptr.i = signal->theData[0];
+ c_scan_frag_pool.getPtr(scanFragptr);
+ switch (scanFragptr.p->scanFragState){
+ case ScanFragRec::LQH_ACTIVE:
+ break;
+ default:
+ DEBUG("execSCAN_HBREP: scanFragState="<<scanFragptr.p->scanFragState);
+ systemErrorLab(signal);
+ break;
+ }
+
+ ScanRecordPtr scanptr;
+ scanptr.i = scanFragptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+
+ apiConnectptr.i = scanptr.p->scanApiRec;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+
+ if (!(apiConnectptr.p->transid[0] == signal->theData[1] &&
+ apiConnectptr.p->transid[1] == signal->theData[2])){
+ jam();
+ /**
+ * Send signal back to sender so that the crash occurs there
+ */
+ // Save original transid
+ signal->theData[3] = signal->theData[0];
+ signal->theData[4] = signal->theData[1];
+ // Set transid to illegal values
+ signal->theData[1] = RNIL;
+ signal->theData[2] = RNIL;
+
+ sendSignal(signal->senderBlockRef(), GSN_SCAN_HBREP, signal, 5, JBA);
+ DEBUG("SCAN_HBREP with wrong transid("
+ <<signal->theData[3]<<", "<<signal->theData[4]<<")");
+ return;
+ }//if
+
+ // Update timer on ScanFragRec
+ if (scanFragptr.p->scanFragTimer != 0){
+ updateBuddyTimer(apiConnectptr);
+ scanFragptr.p->startFragTimer(ctcTimer);
+ } else {
+ ndbassert(false);
+ DEBUG("SCAN_HBREP when scanFragTimer was turned off");
+ }
+}//execSCAN_HBREP()
+
+/*--------------------------------------------------------------------------*/
+/* Timeout has occured on a fragment which means a scan has timed out. */
+/* If this is true we have an error in LQH/ACC. */
+/*--------------------------------------------------------------------------*/
+void Dbtc::timeOutFoundFragLab(Signal* signal, UintR TscanConPtr)
+{
+ ScanFragRecPtr ptr;
+ c_scan_frag_pool.getPtr(ptr, TscanConPtr);
+ DEBUG(TscanConPtr << " timeOutFoundFragLab: scanFragState = "<< ptr.p->scanFragState);
+
+ /*-------------------------------------------------------------------------*/
+ // The scan fragment has expired its timeout. Check its state to decide
+ // what to do.
+ /*-------------------------------------------------------------------------*/
+ switch (ptr.p->scanFragState) {
+ case ScanFragRec::WAIT_GET_PRIMCONF:
+ jam();
+ ndbrequire(false);
+ break;
+ case ScanFragRec::LQH_ACTIVE:{
+ jam();
+
+ /**
+ * The LQH expired it's timeout, try to close it
+ */
+ Uint32 nodeId = refToNode(ptr.p->lqhBlockref);
+ Uint32 connectCount = getNodeInfo(nodeId).m_connectCount;
+ ScanRecordPtr scanptr;
+ scanptr.i = ptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+
+ if(connectCount != ptr.p->m_connectCount){
+ jam();
+ /**
+ * The node has died
+ */
+ ptr.p->scanFragState = ScanFragRec::COMPLETED;
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ run.release(ptr);
+ ptr.p->stopFragTimer();
+ }
+
+ scanError(signal, scanptr, ZSCAN_FRAG_LQH_ERROR);
+ break;
+ }
+ case ScanFragRec::DELIVERED:
+ jam();
+ case ScanFragRec::IDLE:
+ jam();
+ case ScanFragRec::QUEUED_FOR_DELIVERY:
+ jam();
+ /*-----------------------------------------------------------------------
+ * Should never occur. We will simply report set the timer to zero and
+ * continue. In a debug version we should crash here but not in a release
+ * version. In a release version we will simply set the time-out to zero.
+ *-----------------------------------------------------------------------*/
+#ifdef VM_TRACE
+ systemErrorLab(signal);
+#endif
+ scanFragptr.p->stopFragTimer();
+ break;
+ default:
+ jam();
+ /*-----------------------------------------------------------------------
+ * Non-existent state. Crash.
+ *-----------------------------------------------------------------------*/
+ systemErrorLab(signal);
+ break;
+ }//switch
+
+ signal->theData[0] = TcContinueB::ZCONTINUE_TIME_OUT_FRAG_CONTROL;
+ signal->theData[1] = TscanConPtr + 1;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+}//timeOutFoundFragLab()
+
+
+/*
+ 4.3.16 GCP_NOMORETRANS
+ ----------------------
+*/
+/*****************************************************************************
+ * G C P _ N O M O R E T R A N S
+ *
+ * WHEN DBTC RECEIVES SIGNAL GCP_NOMORETRANS A CHECK IS DONE TO FIND OUT IF
+ * THERE ARE ANY GLOBAL CHECKPOINTS GOING ON - CFIRSTGCP /= RNIL. DBTC THEN
+ * SEARCHES THE GCP_RECORD FILE TO FIND OUT IF THERE ARE ANY TRANSACTIONS NOT
+ * CONCLUDED WITH THIS SPECIFIC CHECKPOINT - GCP_PTR:GCP_ID = TCHECK_GCP_ID.
+ * FOR EACH TRANSACTION WHERE API_CONNECTSTATE EQUALS PREPARED, COMMITTING,
+ * COMMITTED OR COMPLETING SIGNAL CONTINUEB IS SENT WITH A DELAY OF 100 MS,
+ * THE COUNTER GCP_PTR:OUTSTANDINGAPI IS INCREASED. WHEN CONTINUEB IS RECEIVED
+ * THE COUNTER IS DECREASED AND A CHECK IS DONE TO FIND OUT IF ALL
+ * TRANSACTIONS ARE CONCLUDED. IF SO, SIGNAL GCP_TCFINISHED IS SENT.
+ *****************************************************************************/
+void Dbtc::execGCP_NOMORETRANS(Signal* signal)
+{
+ jamEntry();
+ tcheckGcpId = signal->theData[1];
+ if (cfirstgcp != RNIL) {
+ jam();
+ /* A GLOBAL CHECKPOINT IS GOING ON */
+ gcpPtr.i = cfirstgcp; /* SET POINTER TO FIRST GCP IN QUEUE*/
+ ptrCheckGuard(gcpPtr, cgcpFilesize, gcpRecord);
+ if (gcpPtr.p->gcpId == tcheckGcpId) {
+ jam();
+ if (gcpPtr.p->firstApiConnect != RNIL) {
+ jam();
+ gcpPtr.p->gcpNomoretransRec = ZTRUE;
+ } else {
+ jam();
+ gcpTcfinished(signal);
+ unlinkGcp(signal);
+ }//if
+ } else {
+ jam();
+ /*------------------------------------------------------------*/
+ /* IF IT IS NOT THE FIRST THEN THERE SHOULD BE NO */
+ /* RECORD FOR THIS GLOBAL CHECKPOINT. WE ALWAYS REMOVE */
+ /* THE GLOBAL CHECKPOINTS IN ORDER. */
+ /*------------------------------------------------------------*/
+ gcpTcfinished(signal);
+ }//if
+ } else {
+ jam();
+ gcpTcfinished(signal);
+ }//if
+ return;
+}//Dbtc::execGCP_NOMORETRANS()
+
+/*****************************************************************************/
+/* */
+/* TAKE OVER MODULE */
+/* */
+/*****************************************************************************/
+/* */
+/* THIS PART OF TC TAKES OVER THE COMMIT/ABORT OF TRANSACTIONS WHERE THE */
+/* NODE ACTING AS TC HAVE FAILED. IT STARTS BY QUERYING ALL NODES ABOUT */
+/* ANY OPERATIONS PARTICIPATING IN A TRANSACTION WHERE THE TC NODE HAVE */
+/* FAILED. */
+/* */
+/* AFTER RECEIVING INFORMATION FROM ALL NODES ABOUT OPERATION STATUS THIS */
+/* CODE WILL ENSURE THAT ALL AFFECTED TRANSACTIONS ARE PROPERLY ABORTED OR*/
+/* COMMITTED. THE ORIGINATING APPLICATION NODE WILL ALSO BE CONTACTED. */
+/* IF THE ORIGINATING APPLICATION ALSO FAILED THEN THERE IS CURRENTLY NO */
+/* WAY TO FIND OUT WHETHER A TRANSACTION WAS PERFORMED OR NOT. */
+/*****************************************************************************/
+void Dbtc::execNODE_FAILREP(Signal* signal)
+{
+ HostRecordPtr tmpHostptr;
+ jamEntry();
+
+ NodeFailRep * const nodeFail = (NodeFailRep *)&signal->theData[0];
+
+ cfailure_nr = nodeFail->failNo;
+ const Uint32 tnoOfNodes = nodeFail->noOfNodes;
+ const Uint32 tnewMasterId = nodeFail->masterNodeId;
+
+ arrGuard(tnoOfNodes, MAX_NDB_NODES);
+ int index = 0;
+ for (unsigned i = 1; i< MAX_NDB_NODES; i++) {
+ if(NodeBitmask::get(nodeFail->theNodes, i)){
+ cdata[index] = i;
+ index++;
+ }//if
+ }//for
+
+ tcNodeFailptr.i = 0;
+ ptrAss(tcNodeFailptr, tcFailRecord);
+ Uint32 tindex;
+ for (tindex = 0; tindex < tnoOfNodes; tindex++) {
+ jam();
+ hostptr.i = cdata[tindex];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ /*------------------------------------------------------------*/
+ /* SET STATUS OF THE FAILED NODE TO DEAD SINCE IT HAS */
+ /* FAILED. */
+ /*------------------------------------------------------------*/
+ hostptr.p->hostStatus = HS_DEAD;
+
+ if (hostptr.p->takeOverStatus == TOS_COMPLETED) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* A VERY UNUSUAL SITUATION. THE TAKE OVER WAS COMPLETED*/
+ /* EVEN BEFORE WE HEARD ABOUT THE NODE FAILURE REPORT. */
+ /* HOWEVER UNUSUAL THIS SITUATION IS POSSIBLE. */
+ /*------------------------------------------------------------*/
+ /* RELEASE THE CURRENTLY UNUSED LQH CONNECTIONS. THE */
+ /* REMAINING WILL BE RELEASED WHEN THE TRANSACTION THAT */
+ /* USED THEM IS COMPLETED. */
+ /*------------------------------------------------------------*/
+ {
+ NFCompleteRep * const nfRep = (NFCompleteRep *)&signal->theData[0];
+ nfRep->blockNo = DBTC;
+ nfRep->nodeId = cownNodeid;
+ nfRep->failedNodeId = hostptr.i;
+ }
+ sendSignal(cdihblockref, GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+ } else {
+ ndbrequire(hostptr.p->takeOverStatus == TOS_IDLE);
+ hostptr.p->takeOverStatus = TOS_NODE_FAILED;
+ }//if
+
+ if (tcNodeFailptr.p->failStatus == FS_LISTENING) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* THE CURRENT TAKE OVER CAN BE AFFECTED BY THIS NODE */
+ /* FAILURE. */
+ /*------------------------------------------------------------*/
+ if (hostptr.p->lqhTransStatus == LTS_ACTIVE) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE WERE WAITING FOR THE FAILED NODE IN THE TAKE OVER */
+ /* PROTOCOL FOR TC. */
+ /*------------------------------------------------------------*/
+ signal->theData[0] = TcContinueB::ZNODE_TAKE_OVER_COMPLETED;
+ signal->theData[1] = hostptr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+ }//if
+
+ }//for
+
+ const bool masterFailed = (cmasterNodeId != tnewMasterId);
+ cmasterNodeId = tnewMasterId;
+
+ if(getOwnNodeId() == cmasterNodeId && masterFailed){
+ /**
+ * Master has failed and I'm the new master
+ */
+ jam();
+
+ for (hostptr.i = 1; hostptr.i < MAX_NDB_NODES; hostptr.i++) {
+ jam();
+ ptrAss(hostptr, hostRecord);
+ if (hostptr.p->hostStatus != HS_ALIVE) {
+ jam();
+ if (hostptr.p->takeOverStatus == TOS_COMPLETED) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* SEND TAKE OVER CONFIRMATION TO ALL ALIVE NODES IF */
+ /* TAKE OVER IS COMPLETED. THIS IS PERFORMED TO ENSURE */
+ /* THAT ALL NODES AGREE ON THE IDLE STATE OF THE TAKE */
+ /* OVER. THIS MIGHT BE MISSED IN AN ERROR SITUATION IF */
+ /* MASTER FAILS AFTER SENDING CONFIRMATION TO NEW */
+ /* MASTER BUT FAILING BEFORE SENDING TO ANOTHER NODE */
+ /* WHICH WAS NOT MASTER. IF THIS NODE LATER BECOMES */
+ /* MASTER IT MIGHT START A NEW TAKE OVER EVEN AFTER THE */
+ /* CRASHED NODE HAVE ALREADY RECOVERED. */
+ /*------------------------------------------------------------*/
+ for(tmpHostptr.i = 1; tmpHostptr.i < MAX_NDB_NODES;tmpHostptr.i++) {
+ jam();
+ ptrAss(tmpHostptr, hostRecord);
+ if (tmpHostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tblockref = calcTcBlockRef(tmpHostptr.i);
+ signal->theData[0] = hostptr.i;
+ sendSignal(tblockref, GSN_TAKE_OVERTCCONF, signal, 1, JBB);
+ }//if
+ }//for
+ }//if
+ }//if
+ }//for
+ }
+
+ if(getOwnNodeId() == cmasterNodeId){
+ jam();
+ for (hostptr.i = 1; hostptr.i < MAX_NDB_NODES; hostptr.i++) {
+ jam();
+ ptrAss(hostptr, hostRecord);
+ if (hostptr.p->hostStatus != HS_ALIVE) {
+ jam();
+ if (hostptr.p->takeOverStatus == TOS_NODE_FAILED) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* CONCLUDE ALL ACTIVITIES THE FAILED TC DID CONTROL */
+ /* SINCE WE ARE THE MASTER. THIS COULD HAVE BEEN STARTED*/
+ /* BY A PREVIOUS MASTER BUT HAVE NOT BEEN CONCLUDED YET.*/
+ /*------------------------------------------------------------*/
+ hostptr.p->takeOverStatus = TOS_ACTIVE;
+ signal->theData[0] = hostptr.i;
+ sendSignal(cownref, GSN_TAKE_OVERTCREQ, signal, 1, JBB);
+ }//if
+ }//if
+ }//for
+ }//if
+ for (tindex = 0; tindex < tnoOfNodes; tindex++) {
+ jam();
+ hostptr.i = cdata[tindex];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ /*------------------------------------------------------------*/
+ /* LOOP THROUGH AND ABORT ALL SCANS THAT WHERE */
+ /* CONTROLLED BY THIS TC AND ACTIVE IN THE FAILED */
+ /* NODE'S LQH */
+ /*------------------------------------------------------------*/
+ checkScanActiveInFailedLqh(signal, 0, hostptr.i);
+ checkWaitDropTabFailedLqh(signal, hostptr.i, 0); // nodeid, tableid
+ }//for
+
+}//Dbtc::execNODE_FAILREP()
+
+void Dbtc::checkScanActiveInFailedLqh(Signal* signal,
+ Uint32 scanPtrI,
+ Uint32 failedNodeId){
+
+ ScanRecordPtr scanptr;
+ for (scanptr.i = scanPtrI; scanptr.i < cscanrecFileSize; scanptr.i++) {
+ jam();
+ ptrAss(scanptr, scanRecord);
+ bool found = false;
+ if (scanptr.p->scanState != ScanRecord::IDLE){
+ jam();
+ ScanFragRecPtr ptr;
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ for(run.first(ptr); !ptr.isNull(); ){
+ jam();
+ ScanFragRecPtr curr = ptr;
+ run.next(ptr);
+ if (curr.p->scanFragState == ScanFragRec::LQH_ACTIVE &&
+ refToNode(curr.p->lqhBlockref) == failedNodeId){
+ jam();
+
+ run.release(curr);
+ curr.p->scanFragState = ScanFragRec::COMPLETED;
+ curr.p->stopFragTimer();
+ found = true;
+ }
+ }
+ }
+ if(found){
+ jam();
+ scanError(signal, scanptr, ZSCAN_LQH_ERROR);
+ }
+
+ // Send CONTINUEB to continue later
+ signal->theData[0] = TcContinueB::ZCHECK_SCAN_ACTIVE_FAILED_LQH;
+ signal->theData[1] = scanptr.i + 1; // Check next scanptr
+ signal->theData[2] = failedNodeId;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ return;
+ }//for
+}
+
+void
+Dbtc::checkScanFragList(Signal* signal,
+ Uint32 failedNodeId,
+ ScanRecord * scanP,
+ ScanFragList::Head & head){
+
+ DEBUG("checkScanActiveInFailedLqh: scanFragError");
+}
+
+void Dbtc::execTAKE_OVERTCCONF(Signal* signal)
+{
+ jamEntry();
+ tfailedNodeId = signal->theData[0];
+ hostptr.i = tfailedNodeId;
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ switch (hostptr.p->takeOverStatus) {
+ case TOS_IDLE:
+ jam();
+ /*------------------------------------------------------------*/
+ /* THIS MESSAGE ARRIVED EVEN BEFORE THE NODE_FAILREP */
+ /* MESSAGE. THIS IS POSSIBLE IN EXTREME SITUATIONS. */
+ /* WE SET THE STATE TO TAKE_OVER_COMPLETED AND WAIT */
+ /* FOR THE NODE_FAILREP MESSAGE. */
+ /*------------------------------------------------------------*/
+ hostptr.p->takeOverStatus = TOS_COMPLETED;
+ break;
+ case TOS_NODE_FAILED:
+ case TOS_ACTIVE:
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE ARE NOT MASTER AND THE TAKE OVER IS ACTIVE OR WE */
+ /* ARE MASTER AND THE TAKE OVER IS ACTIVE. IN BOTH */
+ /* WE SET THE STATE TO TAKE_OVER_COMPLETED. */
+ /*------------------------------------------------------------*/
+ /* RELEASE THE CURRENTLY UNUSED LQH CONNECTIONS. THE */
+ /* REMAINING WILL BE RELEASED WHEN THE TRANSACTION THAT */
+ /* USED THEM IS COMPLETED. */
+ /*------------------------------------------------------------*/
+ hostptr.p->takeOverStatus = TOS_COMPLETED;
+ {
+ NFCompleteRep * const nfRep = (NFCompleteRep *)&signal->theData[0];
+ nfRep->blockNo = DBTC;
+ nfRep->nodeId = cownNodeid;
+ nfRep->failedNodeId = hostptr.i;
+ }
+ sendSignal(cdihblockref, GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBB);
+ break;
+ case TOS_COMPLETED:
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE HAVE ALREADY RECEIVED THE CONF SIGNAL. IT IS MOST */
+ /* LIKELY SENT FROM A NEW MASTER WHICH WASN'T SURE IF */
+ /* THIS NODE HEARD THE CONF SIGNAL FROM THE OLD MASTER. */
+ /* WE SIMPLY IGNORE THE MESSAGE. */
+ /*------------------------------------------------------------*/
+ /*empty*/;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//switch
+}//Dbtc::execTAKE_OVERTCCONF()
+
+void Dbtc::execTAKE_OVERTCREQ(Signal* signal)
+{
+ jamEntry();
+ tfailedNodeId = signal->theData[0];
+ tcNodeFailptr.i = 0;
+ ptrAss(tcNodeFailptr, tcFailRecord);
+ if (tcNodeFailptr.p->failStatus != FS_IDLE) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE CAN CURRENTLY ONLY HANDLE ONE TAKE OVER AT A TIME */
+ /*------------------------------------------------------------*/
+ /* IF MORE THAN ONE TAKE OVER IS REQUESTED WE WILL */
+ /* QUEUE THE TAKE OVER AND START IT AS SOON AS THE */
+ /* PREVIOUS ARE COMPLETED. */
+ /*------------------------------------------------------------*/
+ arrGuard(tcNodeFailptr.p->queueIndex, MAX_NDB_NODES);
+ tcNodeFailptr.p->queueList[tcNodeFailptr.p->queueIndex] = tfailedNodeId;
+ tcNodeFailptr.p->queueIndex = tcNodeFailptr.p->queueIndex + 1;
+ return;
+ }//if
+ startTakeOverLab(signal);
+}//Dbtc::execTAKE_OVERTCREQ()
+
+/*------------------------------------------------------------*/
+/* INITIALISE THE HASH TABLES FOR STORING TRANSACTIONS */
+/* AND OPERATIONS DURING TC TAKE OVER. */
+/*------------------------------------------------------------*/
+void Dbtc::startTakeOverLab(Signal* signal)
+{
+ for (tindex = 0; tindex <= 511; tindex++) {
+ ctransidFailHash[tindex] = RNIL;
+ }//for
+ for (tindex = 0; tindex <= 1023; tindex++) {
+ ctcConnectFailHash[tindex] = RNIL;
+ }//for
+ tcNodeFailptr.p->failStatus = FS_LISTENING;
+ tcNodeFailptr.p->takeOverNode = tfailedNodeId;
+ for (hostptr.i = 1; hostptr.i < MAX_NDB_NODES; hostptr.i++) {
+ jam();
+ ptrAss(hostptr, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tblockref = calcLqhBlockRef(hostptr.i);
+ hostptr.p->lqhTransStatus = LTS_ACTIVE;
+ signal->theData[0] = tcNodeFailptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = tfailedNodeId;
+ sendSignal(tblockref, GSN_LQH_TRANSREQ, signal, 3, JBB);
+ }//if
+ }//for
+}//Dbtc::startTakeOverLab()
+
+/*------------------------------------------------------------*/
+/* A REPORT OF AN OPERATION WHERE TC FAILED HAS ARRIVED.*/
+/*------------------------------------------------------------*/
+void Dbtc::execLQH_TRANSCONF(Signal* signal)
+{
+ jamEntry();
+ LqhTransConf * const lqhTransConf = (LqhTransConf *)&signal->theData[0];
+
+ tcNodeFailptr.i = lqhTransConf->tcRef;
+ ptrCheckGuard(tcNodeFailptr, 1, tcFailRecord);
+ tnodeid = lqhTransConf->lqhNodeId;
+ ttransStatus = (LqhTransConf::OperationStatus)lqhTransConf->operationStatus;
+ ttransid1 = lqhTransConf->transId1;
+ ttransid2 = lqhTransConf->transId2;
+ ttcOprec = lqhTransConf->oldTcOpRec;
+ treqinfo = lqhTransConf->requestInfo;
+ tgci = lqhTransConf->gci;
+ cnodes[0] = lqhTransConf->nextNodeId1;
+ cnodes[1] = lqhTransConf->nextNodeId2;
+ cnodes[2] = lqhTransConf->nextNodeId3;
+ const Uint32 ref = tapplRef = lqhTransConf->apiRef;
+ tapplOprec = lqhTransConf->apiOpRec;
+ const Uint32 tableId = lqhTransConf->tableId;
+
+ if (ttransStatus == LqhTransConf::LastTransConf){
+ jam();
+ /*------------------------------------------------------------*/
+ /* A NODE HAS REPORTED COMPLETION OF TAKE OVER REPORTING*/
+ /*------------------------------------------------------------*/
+ nodeTakeOverCompletedLab(signal);
+ return;
+ }//if
+ if (ttransStatus == LqhTransConf::Marker){
+ jam();
+ treqinfo = 0;
+ LqhTransConf::setMarkerFlag(treqinfo, 1);
+ } else {
+ TableRecordPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+ switch((DictTabInfo::TableType)tabPtr.p->tableType){
+ case DictTabInfo::SystemTable:
+ case DictTabInfo::UserTable:
+ break;
+ default:
+ tapplRef = 0;
+ tapplOprec = 0;
+ }
+ }
+
+ findApiConnectFail(signal);
+
+ if(apiConnectptr.p->ndbapiBlockref == 0 && tapplRef != 0){
+ apiConnectptr.p->ndbapiBlockref = ref;
+ apiConnectptr.p->ndbapiConnect = tapplOprec;
+ }
+
+ if (ttransStatus != LqhTransConf::Marker){
+ jam();
+ findTcConnectFail(signal);
+ }
+}//Dbtc::execLQH_TRANSCONF()
+
+/*------------------------------------------------------------*/
+/* A NODE HAS REPORTED COMPLETION OF TAKE OVER REPORTING*/
+/*------------------------------------------------------------*/
+void Dbtc::nodeTakeOverCompletedLab(Signal* signal)
+{
+ Uint32 guard0;
+
+ hostptr.i = tnodeid;
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ hostptr.p->lqhTransStatus = LTS_IDLE;
+ for (hostptr.i = 1; hostptr.i < MAX_NDB_NODES; hostptr.i++) {
+ jam();
+ ptrAss(hostptr, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ if (hostptr.p->lqhTransStatus == LTS_ACTIVE) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* NOT ALL NODES ARE COMPLETED WITH REPORTING IN THE */
+ /* TAKE OVER. */
+ /*------------------------------------------------------------*/
+ return;
+ }//if
+ }//if
+ }//for
+ /*------------------------------------------------------------*/
+ /* ALL NODES HAVE REPORTED ON THE STATUS OF THE VARIOUS */
+ /* OPERATIONS THAT WAS CONTROLLED BY THE FAILED TC. WE */
+ /* ARE NOW IN A POSITION TO COMPLETE ALL OF THOSE */
+ /* TRANSACTIONS EITHER IN A SUCCESSFUL WAY OR IN AN */
+ /* UNSUCCESSFUL WAY. WE WILL ALSO REPORT THIS CONCLUSION*/
+ /* TO THE APPLICATION IF THAT IS STILL ALIVE. */
+ /*------------------------------------------------------------*/
+ tcNodeFailptr.p->currentHashIndexTakeOver = 0;
+ tcNodeFailptr.p->completedTakeOver = 0;
+ tcNodeFailptr.p->failStatus = FS_COMPLETING;
+ guard0 = cnoParallelTakeOver - 1;
+ /*------------------------------------------------------------*/
+ /* WE WILL COMPLETE THE TRANSACTIONS BY STARTING A */
+ /* NUMBER OF PARALLEL ACTIVITIES. EACH ACTIVITY WILL */
+ /* COMPLETE ONE TRANSACTION AT A TIME AND IN THAT */
+ /* TRANSACTION IT WILL COMPLETE ONE OPERATION AT A TIME.*/
+ /* WHEN ALL ACTIVITIES ARE COMPLETED THEN THE TAKE OVER */
+ /* IS COMPLETED. */
+ /*------------------------------------------------------------*/
+ arrGuard(guard0, MAX_NDB_NODES);
+ for (tindex = 0; tindex <= guard0; tindex++) {
+ jam();
+ tcNodeFailptr.p->takeOverProcState[tindex] = ZTAKE_OVER_ACTIVE;
+ signal->theData[0] = TcContinueB::ZCOMPLETE_TRANS_AT_TAKE_OVER;
+ signal->theData[1] = tcNodeFailptr.i;
+ signal->theData[2] = tindex;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ }//for
+}//Dbtc::nodeTakeOverCompletedLab()
+
+/*------------------------------------------------------------*/
+/* COMPLETE A NEW TRANSACTION FROM THE HASH TABLE OF */
+/* TRANSACTIONS TO COMPLETE. */
+/*------------------------------------------------------------*/
+void Dbtc::completeTransAtTakeOverLab(Signal* signal, UintR TtakeOverInd)
+{
+ jam();
+ while (tcNodeFailptr.p->currentHashIndexTakeOver < 512){
+ jam();
+ apiConnectptr.i =
+ ctransidFailHash[tcNodeFailptr.p->currentHashIndexTakeOver];
+ if (apiConnectptr.i != RNIL) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE HAVE FOUND A TRANSACTION THAT NEEDS TO BE */
+ /* COMPLETED. REMOVE IT FROM THE HASH TABLE SUCH THAT */
+ /* NOT ANOTHER ACTIVITY ALSO TRIES TO COMPLETE THIS */
+ /* TRANSACTION. */
+ /*------------------------------------------------------------*/
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ ctransidFailHash[tcNodeFailptr.p->currentHashIndexTakeOver] =
+ apiConnectptr.p->nextApiConnect;
+
+ completeTransAtTakeOverDoOne(signal, TtakeOverInd);
+ // One transaction taken care of, return from this function
+ // and wait for the next CONTINUEB to continue processing
+ break;
+
+ } else {
+ if (tcNodeFailptr.p->currentHashIndexTakeOver < 511){
+ jam();
+ tcNodeFailptr.p->currentHashIndexTakeOver++;
+ } else {
+ jam();
+ completeTransAtTakeOverDoLast(signal, TtakeOverInd);
+ tcNodeFailptr.p->currentHashIndexTakeOver++;
+ }//if
+ }//if
+ }//while
+}//Dbtc::completeTransAtTakeOverLab()
+
+
+
+
+void Dbtc::completeTransAtTakeOverDoLast(Signal* signal, UintR TtakeOverInd)
+{
+ Uint32 guard0;
+ /*------------------------------------------------------------*/
+ /* THERE ARE NO MORE TRANSACTIONS TO COMPLETE. THIS */
+ /* ACTIVITY IS COMPLETED. */
+ /*------------------------------------------------------------*/
+ arrGuard(TtakeOverInd, MAX_NDB_NODES);
+ if (tcNodeFailptr.p->takeOverProcState[TtakeOverInd] != ZTAKE_OVER_ACTIVE) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ tcNodeFailptr.p->takeOverProcState[TtakeOverInd] = ZTAKE_OVER_IDLE;
+ tcNodeFailptr.p->completedTakeOver++;
+
+ if (tcNodeFailptr.p->completedTakeOver == cnoParallelTakeOver) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE WERE THE LAST ACTIVITY THAT WAS COMPLETED. WE NEED*/
+ /* TO REPORT THE COMPLETION OF THE TAKE OVER TO ALL */
+ /* NODES THAT ARE ALIVE. */
+ /*------------------------------------------------------------*/
+ for (hostptr.i = 1; hostptr.i < MAX_NDB_NODES; hostptr.i++) {
+ jam();
+ ptrAss(hostptr, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tblockref = calcTcBlockRef(hostptr.i);
+ signal->theData[0] = tcNodeFailptr.p->takeOverNode;
+ sendSignal(tblockref, GSN_TAKE_OVERTCCONF, signal, 1, JBB);
+ }//if
+ }//for
+ if (tcNodeFailptr.p->queueIndex > 0) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* THERE ARE MORE NODES TO TAKE OVER. WE NEED TO START */
+ /* THE TAKE OVER. */
+ /*------------------------------------------------------------*/
+ tfailedNodeId = tcNodeFailptr.p->queueList[0];
+ guard0 = tcNodeFailptr.p->queueIndex - 1;
+ arrGuard(guard0 + 1, MAX_NDB_NODES);
+ for (tindex = 0; tindex <= guard0; tindex++) {
+ jam();
+ tcNodeFailptr.p->queueList[tindex] =
+ tcNodeFailptr.p->queueList[tindex + 1];
+ }//for
+ tcNodeFailptr.p->queueIndex--;
+ startTakeOverLab(signal);
+ return;
+ } else {
+ jam();
+ tcNodeFailptr.p->failStatus = FS_IDLE;
+ }//if
+ }//if
+ return;
+}//Dbtc::completeTransAtTakeOverDoLast()
+
+void Dbtc::completeTransAtTakeOverDoOne(Signal* signal, UintR TtakeOverInd)
+{
+ apiConnectptr.p->takeOverRec = (Uint8)tcNodeFailptr.i;
+ apiConnectptr.p->takeOverInd = TtakeOverInd;
+
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_FAIL_COMMITTED:
+ jam();
+ /*------------------------------------------------------------*/
+ /* ALL PARTS OF THE TRANSACTIONS REPORTED COMMITTED. WE */
+ /* HAVE THUS COMPLETED THE COMMIT PHASE. WE CAN REPORT */
+ /* COMMITTED TO THE APPLICATION AND CONTINUE WITH THE */
+ /* COMPLETE PHASE. */
+ /*------------------------------------------------------------*/
+ sendTCKEY_FAILCONF(signal, apiConnectptr.p);
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->currentTcConnect = tcConnectptr.i;
+ apiConnectptr.p->currentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ toCompleteHandlingLab(signal);
+ return;
+ case CS_FAIL_COMMITTING:
+ jam();
+ /*------------------------------------------------------------*/
+ /* AT LEAST ONE PART WAS ONLY PREPARED AND AT LEAST ONE */
+ /* PART WAS COMMITTED. COMPLETE THE COMMIT PHASE FIRST. */
+ /* THEN CONTINUE AS AFTER COMMITTED. */
+ /*------------------------------------------------------------*/
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->currentTcConnect = tcConnectptr.i;
+ apiConnectptr.p->currentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ toCommitHandlingLab(signal);
+ return;
+ case CS_FAIL_ABORTING:
+ case CS_FAIL_PREPARED:
+ jam();
+ /*------------------------------------------------------------*/
+ /* WE WILL ABORT THE TRANSACTION IF IT IS IN A PREPARED */
+ /* STATE IN THIS VERSION. IN LATER VERSIONS WE WILL */
+ /* HAVE TO ADD CODE FOR HANDLING OF PREPARED-TO-COMMIT */
+ /* TRANSACTIONS. THESE ARE NOT ALLOWED TO ABORT UNTIL WE*/
+ /* HAVE HEARD FROM THE TRANSACTION COORDINATOR. */
+ /* */
+ /* IT IS POSSIBLE TO COMMIT TRANSACTIONS THAT ARE */
+ /* PREPARED ACTUALLY. WE WILL LEAVE THIS PROBLEM UNTIL */
+ /* LATER VERSIONS. */
+ /*------------------------------------------------------------*/
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->currentTcConnect = tcConnectptr.i;
+ apiConnectptr.p->currentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ toAbortHandlingLab(signal);
+ return;
+ case CS_FAIL_ABORTED:
+ jam();
+ sendTCKEY_FAILREF(signal, apiConnectptr.p);
+
+ signal->theData[0] = TcContinueB::ZCOMPLETE_TRANS_AT_TAKE_OVER;
+ signal->theData[1] = (UintR)apiConnectptr.p->takeOverRec;
+ signal->theData[2] = apiConnectptr.p->takeOverInd;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ releaseTakeOver(signal);
+ break;
+ case CS_FAIL_COMPLETED:
+ jam();
+ sendTCKEY_FAILCONF(signal, apiConnectptr.p);
+
+ signal->theData[0] = TcContinueB::ZCOMPLETE_TRANS_AT_TAKE_OVER;
+ signal->theData[1] = (UintR)apiConnectptr.p->takeOverRec;
+ signal->theData[2] = apiConnectptr.p->takeOverInd;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ releaseApiConnectFail(signal);
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//switch
+}//Dbtc::completeTransAtTakeOverDoOne()
+
+void
+Dbtc::sendTCKEY_FAILREF(Signal* signal, const ApiConnectRecord * regApiPtr){
+ jam();
+
+ const Uint32 ref = regApiPtr->ndbapiBlockref;
+ if(ref != 0){
+ signal->theData[0] = regApiPtr->ndbapiConnect;
+ signal->theData[1] = regApiPtr->transid[0];
+ signal->theData[2] = regApiPtr->transid[1];
+
+ sendSignal(ref, GSN_TCKEY_FAILREF, signal, 3, JBB);
+ }
+}
+
+void
+Dbtc::sendTCKEY_FAILCONF(Signal* signal, ApiConnectRecord * regApiPtr){
+ jam();
+ TcKeyFailConf * const failConf = (TcKeyFailConf *)&signal->theData[0];
+
+ const Uint32 ref = regApiPtr->ndbapiBlockref;
+ const Uint32 marker = regApiPtr->commitAckMarker;
+ if(ref != 0){
+ failConf->apiConnectPtr = regApiPtr->ndbapiConnect | (marker != RNIL);
+ failConf->transId1 = regApiPtr->transid[0];
+ failConf->transId2 = regApiPtr->transid[1];
+
+ sendSignal(regApiPtr->ndbapiBlockref,
+ GSN_TCKEY_FAILCONF, signal, TcKeyFailConf::SignalLength, JBB);
+ }
+ regApiPtr->commitAckMarker = RNIL;
+}
+
+/*------------------------------------------------------------*/
+/* THIS PART HANDLES THE ABORT PHASE IN THE CASE OF A */
+/* NODE FAILURE BEFORE THE COMMIT DECISION. */
+/*------------------------------------------------------------*/
+/* ABORT REQUEST SUCCESSFULLY COMPLETED ON TNODEID */
+/*------------------------------------------------------------*/
+void Dbtc::execABORTCONF(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ tnodeid = signal->theData[2];
+ if (ERROR_INSERTED(8045)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_ABORTCONF, signal, 2000, 5);
+ return;
+ }//if
+ if (tcConnectptr.i >= ctcConnectFilesize) {
+ errorReport(signal, 5);
+ return;
+ }//if
+ ptrAss(tcConnectptr, tcConnectRecord);
+ if (tcConnectptr.p->tcConnectstate != OS_WAIT_ABORT_CONF) {
+ warningReport(signal, 16);
+ return;
+ }//if
+ apiConnectptr.i = tcConnectptr.p->apiConnect;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ if (apiConnectptr.p->apiConnectstate != CS_WAIT_ABORT_CONF) {
+ warningReport(signal, 17);
+ return;
+ }//if
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[3];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[4];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ warningReport(signal, 18);
+ return;
+ }//if
+ arrGuard(apiConnectptr.p->currentReplicaNo, 4);
+ if (tcConnectptr.p->tcNodedata[apiConnectptr.p->currentReplicaNo] !=
+ tnodeid) {
+ warningReport(signal, 19);
+ return;
+ }//if
+ tcurrentReplicaNo = (Uint8)Z8NIL;
+ tcConnectptr.p->tcConnectstate = OS_ABORTING;
+ toAbortHandlingLab(signal);
+}//Dbtc::execABORTCONF()
+
+void Dbtc::toAbortHandlingLab(Signal* signal)
+{
+ do {
+ if (tcurrentReplicaNo != (Uint8)Z8NIL) {
+ jam();
+ arrGuard(tcurrentReplicaNo, 4);
+ const LqhTransConf::OperationStatus stat =
+ (LqhTransConf::OperationStatus)
+ tcConnectptr.p->failData[tcurrentReplicaNo];
+ switch(stat){
+ case LqhTransConf::InvalidStatus:
+ case LqhTransConf::Aborted:
+ jam();
+ /*empty*/;
+ break;
+ case LqhTransConf::Prepared:
+ jam();
+ hostptr.i = tcConnectptr.p->tcNodedata[tcurrentReplicaNo];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tblockref = calcLqhBlockRef(hostptr.i);
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ tcConnectptr.p->tcConnectstate = OS_WAIT_ABORT_CONF;
+ apiConnectptr.p->apiConnectstate = CS_WAIT_ABORT_CONF;
+ apiConnectptr.p->timeOutCounter = 0;
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = apiConnectptr.p->transid[0];
+ signal->theData[3] = apiConnectptr.p->transid[1];
+ signal->theData[4] = apiConnectptr.p->tcBlockref;
+ signal->theData[5] = tcConnectptr.p->tcOprec;
+ sendSignal(tblockref, GSN_ABORTREQ, signal, 6, JBB);
+ return;
+ }//if
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//switch
+ }//if
+ if (apiConnectptr.p->currentReplicaNo > 0) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* THERE IS STILL ANOTHER REPLICA THAT NEEDS TO BE */
+ /* ABORTED. */
+ /*------------------------------------------------------------*/
+ apiConnectptr.p->currentReplicaNo--;
+ tcurrentReplicaNo = apiConnectptr.p->currentReplicaNo;
+ } else {
+ /*------------------------------------------------------------*/
+ /* THE LAST REPLICA IN THIS OPERATION HAVE COMMITTED. */
+ /*------------------------------------------------------------*/
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ if (tcConnectptr.i == RNIL) {
+ /*------------------------------------------------------------*/
+ /* WE HAVE COMPLETED THE ABORT PHASE. WE CAN NOW REPORT */
+ /* THE ABORT STATUS TO THE APPLICATION AND CONTINUE */
+ /* WITH THE NEXT TRANSACTION. */
+ /*------------------------------------------------------------*/
+ if (apiConnectptr.p->takeOverRec != (Uint8)Z8NIL) {
+ jam();
+ sendTCKEY_FAILREF(signal, apiConnectptr.p);
+ const Uint32 marker = apiConnectptr.p->commitAckMarker;
+ if(marker != RNIL){
+ jam();
+
+ CommitAckMarkerPtr tmp;
+ tmp.i = marker;
+ tmp.p = m_commitAckMarkerHash.getPtr(tmp.i);
+
+ m_commitAckMarkerHash.release(tmp);
+ apiConnectptr.p->commitAckMarker = RNIL;
+ }
+
+ /*------------------------------------------------------------*/
+ /* WE HAVE COMPLETED THIS TRANSACTION NOW AND CAN */
+ /* CONTINUE THE PROCESS WITH THE NEXT TRANSACTION. */
+ /*------------------------------------------------------------*/
+ signal->theData[0] = TcContinueB::ZCOMPLETE_TRANS_AT_TAKE_OVER;
+ signal->theData[1] = (UintR)apiConnectptr.p->takeOverRec;
+ signal->theData[2] = apiConnectptr.p->takeOverInd;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ releaseTakeOver(signal);
+ } else {
+ jam();
+ releaseAbortResources(signal);
+ }//if
+ return;
+ }//if
+ apiConnectptr.p->currentTcConnect = tcConnectptr.i;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->currentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ }//if
+ } while (1);
+}//Dbtc::toAbortHandlingLab()
+
+/*------------------------------------------------------------*/
+/* THIS PART HANDLES THE COMMIT PHASE IN THE CASE OF A */
+/* NODE FAILURE IN THE MIDDLE OF THE COMMIT PHASE. */
+/*------------------------------------------------------------*/
+/* COMMIT REQUEST SUCCESSFULLY COMPLETED ON TNODEID */
+/*------------------------------------------------------------*/
+void Dbtc::execCOMMITCONF(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ tnodeid = signal->theData[1];
+ if (ERROR_INSERTED(8046)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMMITCONF, signal, 2000, 4);
+ return;
+ }//if
+ if (tcConnectptr.i >= ctcConnectFilesize) {
+ errorReport(signal, 4);
+ return;
+ }//if
+ ptrAss(tcConnectptr, tcConnectRecord);
+ if (tcConnectptr.p->tcConnectstate != OS_WAIT_COMMIT_CONF) {
+ warningReport(signal, 8);
+ return;
+ }//if
+ apiConnectptr.i = tcConnectptr.p->apiConnect;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ if (apiConnectptr.p->apiConnectstate != CS_WAIT_COMMIT_CONF) {
+ warningReport(signal, 9);
+ return;
+ }//if
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[2];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[3];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ warningReport(signal, 10);
+ return;
+ }//if
+ arrGuard(apiConnectptr.p->currentReplicaNo, 4);
+ if (tcConnectptr.p->tcNodedata[apiConnectptr.p->currentReplicaNo] !=
+ tnodeid) {
+ warningReport(signal, 11);
+ return;
+ }//if
+ if (ERROR_INSERTED(8026)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ tcurrentReplicaNo = (Uint8)Z8NIL;
+ tcConnectptr.p->tcConnectstate = OS_COMMITTED;
+ toCommitHandlingLab(signal);
+}//Dbtc::execCOMMITCONF()
+
+void Dbtc::toCommitHandlingLab(Signal* signal)
+{
+ do {
+ if (tcurrentReplicaNo != (Uint8)Z8NIL) {
+ jam();
+ arrGuard(tcurrentReplicaNo, 4);
+ switch (tcConnectptr.p->failData[tcurrentReplicaNo]) {
+ case LqhTransConf::InvalidStatus:
+ jam();
+ /*empty*/;
+ break;
+ case LqhTransConf::Committed:
+ jam();
+ /*empty*/;
+ break;
+ case LqhTransConf::Prepared:
+ jam();
+ /*------------------------------------------------------------*/
+ /* THE NODE WAS PREPARED AND IS WAITING FOR ABORT OR */
+ /* COMMIT REQUEST FROM TC. */
+ /*------------------------------------------------------------*/
+ hostptr.i = tcConnectptr.p->tcNodedata[tcurrentReplicaNo];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tblockref = calcLqhBlockRef(hostptr.i);
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ apiConnectptr.p->apiConnectstate = CS_WAIT_COMMIT_CONF;
+ apiConnectptr.p->timeOutCounter = 0;
+ tcConnectptr.p->tcConnectstate = OS_WAIT_COMMIT_CONF;
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = apiConnectptr.p->globalcheckpointid;
+ signal->theData[3] = apiConnectptr.p->transid[0];
+ signal->theData[4] = apiConnectptr.p->transid[1];
+ signal->theData[5] = apiConnectptr.p->tcBlockref;
+ signal->theData[6] = tcConnectptr.p->tcOprec;
+ sendSignal(tblockref, GSN_COMMITREQ, signal, 7, JBB);
+ return;
+ }//if
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ }//if
+ if (apiConnectptr.p->currentReplicaNo > 0) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* THERE IS STILL ANOTHER REPLICA THAT NEEDS TO BE */
+ /* COMMITTED. */
+ /*------------------------------------------------------------*/
+ apiConnectptr.p->currentReplicaNo--;
+ tcurrentReplicaNo = apiConnectptr.p->currentReplicaNo;
+ } else {
+ /*------------------------------------------------------------*/
+ /* THE LAST REPLICA IN THIS OPERATION HAVE COMMITTED. */
+ /*------------------------------------------------------------*/
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ if (tcConnectptr.i == RNIL) {
+ /*------------------------------------------------------------*/
+ /* WE HAVE COMPLETED THE COMMIT PHASE. WE CAN NOW REPORT*/
+ /* THE COMMIT STATUS TO THE APPLICATION AND CONTINUE */
+ /* WITH THE COMPLETE PHASE. */
+ /*------------------------------------------------------------*/
+ if (apiConnectptr.p->takeOverRec != (Uint8)Z8NIL) {
+ jam();
+ sendTCKEY_FAILCONF(signal, apiConnectptr.p);
+ } else {
+ jam();
+ sendApiCommit(signal);
+ }//if
+ apiConnectptr.p->currentTcConnect = apiConnectptr.p->firstTcConnect;
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ apiConnectptr.p->currentReplicaNo = tcurrentReplicaNo;
+ toCompleteHandlingLab(signal);
+ return;
+ }//if
+ apiConnectptr.p->currentTcConnect = tcConnectptr.i;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->currentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ }//if
+ } while (1);
+}//Dbtc::toCommitHandlingLab()
+
+/*------------------------------------------------------------*/
+/* COMMON PART TO HANDLE COMPLETE PHASE WHEN ANY NODE */
+/* HAVE FAILED. */
+/*------------------------------------------------------------*/
+/* THE NODE WITH TNODEID HAVE COMPLETED THE OPERATION */
+/*------------------------------------------------------------*/
+void Dbtc::execCOMPLETECONF(Signal* signal)
+{
+ UintR compare_transid1, compare_transid2;
+
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ tnodeid = signal->theData[1];
+ if (ERROR_INSERTED(8047)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ sendSignalWithDelay(cownref, GSN_COMPLETECONF, signal, 2000, 4);
+ return;
+ }//if
+ if (tcConnectptr.i >= ctcConnectFilesize) {
+ errorReport(signal, 3);
+ return;
+ }//if
+ ptrAss(tcConnectptr, tcConnectRecord);
+ if (tcConnectptr.p->tcConnectstate != OS_WAIT_COMPLETE_CONF) {
+ warningReport(signal, 12);
+ return;
+ }//if
+ apiConnectptr.i = tcConnectptr.p->apiConnect;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ if (apiConnectptr.p->apiConnectstate != CS_WAIT_COMPLETE_CONF) {
+ warningReport(signal, 13);
+ return;
+ }//if
+ compare_transid1 = apiConnectptr.p->transid[0] ^ signal->theData[2];
+ compare_transid2 = apiConnectptr.p->transid[1] ^ signal->theData[3];
+ compare_transid1 = compare_transid1 | compare_transid2;
+ if (compare_transid1 != 0) {
+ warningReport(signal, 14);
+ return;
+ }//if
+ arrGuard(apiConnectptr.p->currentReplicaNo, 4);
+ if (tcConnectptr.p->tcNodedata[apiConnectptr.p->currentReplicaNo] !=
+ tnodeid) {
+ warningReport(signal, 15);
+ return;
+ }//if
+ if (ERROR_INSERTED(8028)) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ tcConnectptr.p->tcConnectstate = OS_COMPLETED;
+ tcurrentReplicaNo = (Uint8)Z8NIL;
+ toCompleteHandlingLab(signal);
+}//Dbtc::execCOMPLETECONF()
+
+void Dbtc::toCompleteHandlingLab(Signal* signal)
+{
+ do {
+ if (tcurrentReplicaNo != (Uint8)Z8NIL) {
+ jam();
+ arrGuard(tcurrentReplicaNo, 4);
+ switch (tcConnectptr.p->failData[tcurrentReplicaNo]) {
+ case LqhTransConf::InvalidStatus:
+ jam();
+ /*empty*/;
+ break;
+ default:
+ jam();
+ /*------------------------------------------------------------*/
+ /* THIS NODE DID NOT REPORT ANYTHING FOR THIS OPERATION */
+ /* IT MUST HAVE FAILED. */
+ /*------------------------------------------------------------*/
+ /*------------------------------------------------------------*/
+ /* SEND COMPLETEREQ TO THE NEXT REPLICA. */
+ /*------------------------------------------------------------*/
+ hostptr.i = tcConnectptr.p->tcNodedata[tcurrentReplicaNo];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus == HS_ALIVE) {
+ jam();
+ tblockref = calcLqhBlockRef(hostptr.i);
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ tcConnectptr.p->tcConnectstate = OS_WAIT_COMPLETE_CONF;
+ apiConnectptr.p->apiConnectstate = CS_WAIT_COMPLETE_CONF;
+ apiConnectptr.p->timeOutCounter = 0;
+ tcConnectptr.p->apiConnect = apiConnectptr.i;
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = cownref;
+ signal->theData[2] = apiConnectptr.p->transid[0];
+ signal->theData[3] = apiConnectptr.p->transid[1];
+ signal->theData[4] = apiConnectptr.p->tcBlockref;
+ signal->theData[5] = tcConnectptr.p->tcOprec;
+ sendSignal(tblockref, GSN_COMPLETEREQ, signal, 6, JBB);
+ return;
+ }//if
+ break;
+ }//switch
+ }//if
+ if (apiConnectptr.p->currentReplicaNo != 0) {
+ jam();
+ /*------------------------------------------------------------*/
+ /* THERE ARE STILL MORE REPLICAS IN THIS OPERATION. WE */
+ /* NEED TO CONTINUE WITH THOSE REPLICAS. */
+ /*------------------------------------------------------------*/
+ apiConnectptr.p->currentReplicaNo--;
+ tcurrentReplicaNo = apiConnectptr.p->currentReplicaNo;
+ } else {
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ if (tcConnectptr.i == RNIL) {
+ /*------------------------------------------------------------*/
+ /* WE HAVE COMPLETED THIS TRANSACTION NOW AND CAN */
+ /* CONTINUE THE PROCESS WITH THE NEXT TRANSACTION. */
+ /*------------------------------------------------------------*/
+ if (apiConnectptr.p->takeOverRec != (Uint8)Z8NIL) {
+ jam();
+ signal->theData[0] = TcContinueB::ZCOMPLETE_TRANS_AT_TAKE_OVER;
+ signal->theData[1] = (UintR)apiConnectptr.p->takeOverRec;
+ signal->theData[2] = apiConnectptr.p->takeOverInd;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 3, JBB);
+ releaseTakeOver(signal);
+ } else {
+ jam();
+ releaseTransResources(signal);
+ }//if
+ return;
+ }//if
+ /*------------------------------------------------------------*/
+ /* WE HAVE COMPLETED AN OPERATION AND THERE ARE MORE TO */
+ /* COMPLETE. TAKE THE NEXT OPERATION AND START WITH THE */
+ /* FIRST REPLICA SINCE IT IS THE COMPLETE PHASE. */
+ /*------------------------------------------------------------*/
+ apiConnectptr.p->currentTcConnect = tcConnectptr.i;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ apiConnectptr.p->currentReplicaNo = tcurrentReplicaNo;
+ }//if
+ } while (1);
+}//Dbtc::toCompleteHandlingLab()
+
+/*------------------------------------------------------------*/
+/* */
+/* FIND THE API CONNECT RECORD FOR THIS TRANSACTION */
+/* DURING TAKE OVER FROM A FAILED TC. IF NONE EXISTS */
+/* YET THEN SEIZE A NEW API CONNECT RECORD AND LINK IT */
+/* INTO THE HASH TABLE. */
+/*------------------------------------------------------------*/
+void Dbtc::findApiConnectFail(Signal* signal)
+{
+ ApiConnectRecordPtr fafPrevApiConnectptr;
+ ApiConnectRecordPtr fafNextApiConnectptr;
+ UintR tfafHashNumber;
+
+ tfafHashNumber = ttransid1 & 511;
+ fafPrevApiConnectptr.i = RNIL;
+ ptrNull(fafPrevApiConnectptr);
+ arrGuard(tfafHashNumber, 512);
+ fafNextApiConnectptr.i = ctransidFailHash[tfafHashNumber];
+ ptrCheck(fafNextApiConnectptr, capiConnectFilesize, apiConnectRecord);
+FAF_LOOP:
+ jam();
+ if (fafNextApiConnectptr.i == RNIL) {
+ jam();
+ if (cfirstfreeApiConnectFail == RNIL) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ seizeApiConnectFail(signal);
+ if (fafPrevApiConnectptr.i == RNIL) {
+ jam();
+ ctransidFailHash[tfafHashNumber] = apiConnectptr.i;
+ } else {
+ jam();
+ ptrGuard(fafPrevApiConnectptr);
+ fafPrevApiConnectptr.p->nextApiConnect = apiConnectptr.i;
+ }//if
+ apiConnectptr.p->nextApiConnect = RNIL;
+ initApiConnectFail(signal);
+ } else {
+ jam();
+ fafPrevApiConnectptr.i = fafNextApiConnectptr.i;
+ fafPrevApiConnectptr.p = fafNextApiConnectptr.p;
+ apiConnectptr.i = fafNextApiConnectptr.i;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ fafNextApiConnectptr.i = apiConnectptr.p->nextApiConnect;
+ ptrCheck(fafNextApiConnectptr, capiConnectFilesize, apiConnectRecord);
+ if ((apiConnectptr.p->transid[1] != ttransid2) ||
+ (apiConnectptr.p->transid[0] != ttransid1)) {
+ goto FAF_LOOP;
+ }//if
+ updateApiStateFail(signal);
+ }//if
+}//Dbtc::findApiConnectFail()
+
+/*----------------------------------------------------------*/
+/* FIND THE TC CONNECT AND IF NOT FOUND ALLOCATE A NEW */
+/*----------------------------------------------------------*/
+void Dbtc::findTcConnectFail(Signal* signal)
+{
+ UintR tftfHashNumber;
+
+ tftfHashNumber = (ttransid1 ^ ttcOprec) & 1023;
+ tcConnectptr.i = ctcConnectFailHash[tftfHashNumber];
+ do {
+ if (tcConnectptr.i == RNIL) {
+ jam();
+ if (cfirstfreeTcConnectFail == RNIL) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ seizeTcConnectFail(signal);
+ linkTcInConnectionlist(signal);
+ tcConnectptr.p->nextTcFailHash = ctcConnectFailHash[tftfHashNumber];
+ ctcConnectFailHash[tftfHashNumber] = tcConnectptr.i;
+ initTcConnectFail(signal);
+ return;
+ } else {
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ if (tcConnectptr.p->tcOprec != ttcOprec) {
+ jam(); /* FRAGMENTID = TC_OPREC HERE, LOOP ANOTHER TURN */
+ tcConnectptr.i = tcConnectptr.p->nextTcFailHash;
+ } else {
+ updateTcStateFail(signal);
+ return;
+ }//if
+ }//if
+ } while (1);
+}//Dbtc::findTcConnectFail()
+
+/*----------------------------------------------------------*/
+/* INITIALISE AN API CONNECT FAIL RECORD */
+/*----------------------------------------------------------*/
+void Dbtc::initApiConnectFail(Signal* signal)
+{
+ apiConnectptr.p->transid[0] = ttransid1;
+ apiConnectptr.p->transid[1] = ttransid2;
+ apiConnectptr.p->firstTcConnect = RNIL;
+ apiConnectptr.p->currSavePointId = 0;
+ apiConnectptr.p->lastTcConnect = RNIL;
+ tblockref = calcTcBlockRef(tcNodeFailptr.p->takeOverNode);
+
+ apiConnectptr.p->tcBlockref = tblockref;
+ apiConnectptr.p->ndbapiBlockref = 0;
+ apiConnectptr.p->ndbapiConnect = 0;
+ apiConnectptr.p->buddyPtr = RNIL;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ switch(ttransStatus){
+ case LqhTransConf::Committed:
+ jam();
+ apiConnectptr.p->globalcheckpointid = tgci;
+ apiConnectptr.p->apiConnectstate = CS_FAIL_COMMITTED;
+ break;
+ case LqhTransConf::Prepared:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_PREPARED;
+ break;
+ case LqhTransConf::Aborted:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_ABORTED;
+ break;
+ case LqhTransConf::Marker:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_COMPLETED;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ }//if
+ apiConnectptr.p->commitAckMarker = RNIL;
+ if(LqhTransConf::getMarkerFlag(treqinfo)){
+ jam();
+ CommitAckMarkerPtr tmp;
+ m_commitAckMarkerHash.seize(tmp);
+
+ ndbrequire(tmp.i != RNIL);
+
+ apiConnectptr.p->commitAckMarker = tmp.i;
+ tmp.p->transid1 = ttransid1;
+ tmp.p->transid2 = ttransid2;
+ tmp.p->apiNodeId = refToNode(tapplRef);
+ tmp.p->noOfLqhs = 1;
+ tmp.p->lqhNodeId[0] = tnodeid;
+ tmp.p->apiConnectPtr = apiConnectptr.i;
+ m_commitAckMarkerHash.add(tmp);
+ }
+}//Dbtc::initApiConnectFail()
+
+/*------------------------------------------------------------*/
+/* INITIALISE AT TC CONNECT AT TAKE OVER WHEN ALLOCATING*/
+/* THE TC CONNECT RECORD. */
+/*------------------------------------------------------------*/
+void Dbtc::initTcConnectFail(Signal* signal)
+{
+ tcConnectptr.p->apiConnect = apiConnectptr.i;
+ tcConnectptr.p->tcOprec = ttcOprec;
+ Uint32 treplicaNo = LqhTransConf::getReplicaNo(treqinfo);
+ for (Uint32 i = 0; i < MAX_REPLICAS; i++) {
+ tcConnectptr.p->failData[i] = LqhTransConf::InvalidStatus;
+ }//for
+ tcConnectptr.p->tcNodedata[treplicaNo] = tnodeid;
+ tcConnectptr.p->failData[treplicaNo] = ttransStatus;
+ tcConnectptr.p->lastReplicaNo = LqhTransConf::getLastReplicaNo(treqinfo);
+ tcConnectptr.p->dirtyOp = LqhTransConf::getDirtyFlag(treqinfo);
+
+}//Dbtc::initTcConnectFail()
+
+/*----------------------------------------------------------*/
+/* INITIALISE TC NODE FAIL RECORD. */
+/*----------------------------------------------------------*/
+void Dbtc::initTcFail(Signal* signal)
+{
+ tcNodeFailptr.i = 0;
+ ptrAss(tcNodeFailptr, tcFailRecord);
+ tcNodeFailptr.p->queueIndex = 0;
+ tcNodeFailptr.p->failStatus = FS_IDLE;
+}//Dbtc::initTcFail()
+
+/*----------------------------------------------------------*/
+/* RELEASE_TAKE_OVER */
+/*----------------------------------------------------------*/
+void Dbtc::releaseTakeOver(Signal* signal)
+{
+ TcConnectRecordPtr rtoNextTcConnectptr;
+
+ rtoNextTcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ do {
+ jam();
+ tcConnectptr.i = rtoNextTcConnectptr.i;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ rtoNextTcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ releaseTcConnectFail(signal);
+ } while (rtoNextTcConnectptr.i != RNIL);
+ releaseApiConnectFail(signal);
+}//Dbtc::releaseTakeOver()
+
+/*---------------------------------------------------------------------------*/
+/* SETUP_FAIL_DATA */
+/* SETUP DATA TO REUSE TAKE OVER CODE FOR HANDLING ABORT/COMMIT IN NODE */
+/* FAILURE SITUATIONS. */
+/*---------------------------------------------------------------------------*/
+void Dbtc::setupFailData(Signal* signal)
+{
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ do {
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ switch (tcConnectptr.p->tcConnectstate) {
+ case OS_PREPARED:
+ case OS_COMMITTING:
+ jam();
+ for (tindex = 0; tindex <= tcConnectptr.p->lastReplicaNo; tindex++) {
+ jam();
+ /*-------------------------------------------------------------------
+ * KEYDATA IS USED TO KEEP AN INDICATION OF STATE IN LQH.
+ * IN THIS CASE ALL LQH'S ARE PREPARED AND WAITING FOR
+ * COMMIT/ABORT DECISION.
+ *------------------------------------------------------------------*/
+ arrGuard(tindex, 4);
+ tcConnectptr.p->failData[tindex] = LqhTransConf::Prepared;
+ }//for
+ break;
+ case OS_COMMITTED:
+ case OS_COMPLETING:
+ jam();
+ for (tindex = 0; tindex <= tcConnectptr.p->lastReplicaNo; tindex++) {
+ jam();
+ /*-------------------------------------------------------------------
+ * KEYDATA IS USED TO KEEP AN INDICATION OF STATE IN LQH.
+ * IN THIS CASE ALL LQH'S ARE COMMITTED AND WAITING FOR
+ * COMPLETE MESSAGE.
+ *------------------------------------------------------------------*/
+ arrGuard(tindex, 4);
+ tcConnectptr.p->failData[tindex] = LqhTransConf::Committed;
+ }//for
+ break;
+ case OS_COMPLETED:
+ jam();
+ for (tindex = 0; tindex <= tcConnectptr.p->lastReplicaNo; tindex++) {
+ jam();
+ /*-------------------------------------------------------------------
+ * KEYDATA IS USED TO KEEP AN INDICATION OF STATE IN LQH.
+ * IN THIS CASE ALL LQH'S ARE COMPLETED.
+ *-------------------------------------------------------------------*/
+ arrGuard(tindex, 4);
+ tcConnectptr.p->failData[tindex] = LqhTransConf::InvalidStatus;
+ }//for
+ break;
+ default:
+ jam();
+ sendSystemError(signal);
+ break;
+ }//switch
+ if (tabortInd != ZCOMMIT_SETUP) {
+ jam();
+ for (UintR Ti = 0; Ti <= tcConnectptr.p->lastReplicaNo; Ti++) {
+ hostptr.i = tcConnectptr.p->tcNodedata[Ti];
+ ptrCheckGuard(hostptr, chostFilesize, hostRecord);
+ if (hostptr.p->hostStatus != HS_ALIVE) {
+ jam();
+ /*-----------------------------------------------------------------
+ * FAILURE OF ANY INVOLVED NODE ALWAYS INVOKES AN ABORT DECISION.
+ *-----------------------------------------------------------------*/
+ tabortInd = ZTRUE;
+ }//if
+ }//for
+ }//if
+ tcConnectptr.p->tcConnectstate = OS_TAKE_OVER;
+ tcConnectptr.p->tcOprec = tcConnectptr.i;
+ tcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ } while (tcConnectptr.i != RNIL);
+ apiConnectptr.p->tcBlockref = cownref;
+ apiConnectptr.p->currentTcConnect = apiConnectptr.p->firstTcConnect;
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->currentReplicaNo = tcConnectptr.p->lastReplicaNo;
+ tcurrentReplicaNo = tcConnectptr.p->lastReplicaNo;
+}//Dbtc::setupFailData()
+
+/*----------------------------------------------------------*/
+/* UPDATE THE STATE OF THE API CONNECT FOR THIS PART. */
+/*----------------------------------------------------------*/
+void Dbtc::updateApiStateFail(Signal* signal)
+{
+ if(LqhTransConf::getMarkerFlag(treqinfo)){
+ jam();
+ const Uint32 marker = apiConnectptr.p->commitAckMarker;
+ if(marker == RNIL){
+ jam();
+
+ CommitAckMarkerPtr tmp;
+ m_commitAckMarkerHash.seize(tmp);
+ ndbrequire(tmp.i != RNIL);
+
+ apiConnectptr.p->commitAckMarker = tmp.i;
+ tmp.p->transid1 = ttransid1;
+ tmp.p->transid2 = ttransid2;
+ tmp.p->apiNodeId = refToNode(tapplRef);
+ tmp.p->noOfLqhs = 1;
+ tmp.p->lqhNodeId[0] = tnodeid;
+ tmp.p->apiConnectPtr = apiConnectptr.i;
+ m_commitAckMarkerHash.add(tmp);
+ } else {
+ jam();
+
+ CommitAckMarkerPtr tmp;
+ tmp.i = marker;
+ tmp.p = m_commitAckMarkerHash.getPtr(marker);
+
+ const Uint32 noOfLqhs = tmp.p->noOfLqhs;
+ ndbrequire(noOfLqhs < MAX_REPLICAS);
+ tmp.p->lqhNodeId[noOfLqhs] = tnodeid;
+ tmp.p->noOfLqhs = (noOfLqhs + 1);
+ }
+ }
+
+ switch (ttransStatus) {
+ case LqhTransConf::Committed:
+ jam();
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_FAIL_COMMITTING:
+ case CS_FAIL_COMMITTED:
+ jam();
+ ndbrequire(tgci == apiConnectptr.p->globalcheckpointid);
+ break;
+ case CS_FAIL_PREPARED:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_COMMITTING;
+ apiConnectptr.p->globalcheckpointid = tgci;
+ break;
+ case CS_FAIL_COMPLETED:
+ jam();
+ apiConnectptr.p->globalcheckpointid = tgci;
+ apiConnectptr.p->apiConnectstate = CS_FAIL_COMMITTED;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+ break;
+ case LqhTransConf::Prepared:
+ jam();
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_FAIL_COMMITTED:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_COMMITTING;
+ break;
+ case CS_FAIL_ABORTED:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_ABORTING;
+ break;
+ case CS_FAIL_COMMITTING:
+ case CS_FAIL_PREPARED:
+ case CS_FAIL_ABORTING:
+ jam();
+ /*empty*/;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+ break;
+ case LqhTransConf::Aborted:
+ jam();
+ switch (apiConnectptr.p->apiConnectstate) {
+ case CS_FAIL_COMMITTING:
+ case CS_FAIL_COMMITTED:
+ jam();
+ systemErrorLab(signal);
+ break;
+ case CS_FAIL_PREPARED:
+ jam();
+ apiConnectptr.p->apiConnectstate = CS_FAIL_ABORTING;
+ break;
+ case CS_FAIL_ABORTING:
+ case CS_FAIL_ABORTED:
+ jam();
+ /*empty*/;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+ break;
+ case LqhTransConf::Marker:
+ jam();
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+}//Dbtc::updateApiStateFail()
+
+/*------------------------------------------------------------*/
+/* UPDATE_TC_STATE_FAIL */
+/* */
+/* WE NEED TO UPDATE THE STATUS OF TC_CONNECT RECORD AND*/
+/* WE ALSO NEED TO CHECK THAT THERE IS CONSISTENCY */
+/* BETWEEN THE DIFFERENT REPLICAS. */
+/*------------------------------------------------------------*/
+void Dbtc::updateTcStateFail(Signal* signal)
+{
+ const Uint8 treplicaNo = LqhTransConf::getReplicaNo(treqinfo);
+ const Uint8 tlastReplicaNo = LqhTransConf::getLastReplicaNo(treqinfo);
+ const Uint8 tdirtyOp = LqhTransConf::getDirtyFlag(treqinfo);
+
+ TcConnectRecord * regTcPtr = tcConnectptr.p;
+
+ ndbrequire(regTcPtr->apiConnect == apiConnectptr.i);
+ ndbrequire(regTcPtr->failData[treplicaNo] == LqhTransConf::InvalidStatus);
+ ndbrequire(regTcPtr->lastReplicaNo == tlastReplicaNo);
+ ndbrequire(regTcPtr->dirtyOp == tdirtyOp);
+
+ regTcPtr->tcNodedata[treplicaNo] = tnodeid;
+ regTcPtr->failData[treplicaNo] = ttransStatus;
+}//Dbtc::updateTcStateFail()
+
+void Dbtc::execTCGETOPSIZEREQ(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(8000);
+
+ UintR Tuserpointer = signal->theData[0]; /* DBDIH POINTER */
+ BlockReference Tusersblkref = signal->theData[1];/* DBDIH BLOCK REFERENCE */
+ signal->theData[0] = Tuserpointer;
+ signal->theData[1] = coperationsize;
+ sendSignal(Tusersblkref, GSN_TCGETOPSIZECONF, signal, 2, JBB);
+}//Dbtc::execTCGETOPSIZEREQ()
+
+void Dbtc::execTC_CLOPSIZEREQ(Signal* signal)
+{
+ jamEntry();
+ CRASH_INSERTION(8001);
+
+ tuserpointer = signal->theData[0];
+ tusersblkref = signal->theData[1];
+ /* DBDIH BLOCK REFERENCE */
+ coperationsize = 0;
+ signal->theData[0] = tuserpointer;
+ sendSignal(tusersblkref, GSN_TC_CLOPSIZECONF, signal, 1, JBB);
+}//Dbtc::execTC_CLOPSIZEREQ()
+
+/* ######################################################################### */
+/* ####### ERROR MODULE ####### */
+/* ######################################################################### */
+void Dbtc::tabStateErrorLab(Signal* signal)
+{
+ terrorCode = ZSTATE_ERROR;
+ releaseAtErrorLab(signal);
+}//Dbtc::tabStateErrorLab()
+
+void Dbtc::wrongSchemaVersionErrorLab(Signal* signal)
+{
+ const TcKeyReq * const tcKeyReq = (TcKeyReq *)&signal->theData[0];
+
+ TableRecordPtr tabPtr;
+ tabPtr.i = tcKeyReq->tableId;
+ const Uint32 schemVer = tcKeyReq->tableSchemaVersion;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+
+ terrorCode = tabPtr.p->getErrorCode(schemVer);
+
+ abortErrorLab(signal);
+}//Dbtc::wrongSchemaVersionErrorLab()
+
+void Dbtc::noFreeConnectionErrorLab(Signal* signal)
+{
+ terrorCode = ZNO_FREE_TC_CONNECTION;
+ abortErrorLab(signal); /* RECORD. OTHERWISE GOTO ERRORHANDLING */
+}//Dbtc::noFreeConnectionErrorLab()
+
+void Dbtc::aiErrorLab(Signal* signal)
+{
+ terrorCode = ZLENGTH_ERROR;
+ abortErrorLab(signal);
+}//Dbtc::aiErrorLab()
+
+void Dbtc::seizeAttrbuferrorLab(Signal* signal)
+{
+ terrorCode = ZGET_ATTRBUF_ERROR;
+ abortErrorLab(signal);
+}//Dbtc::seizeAttrbuferrorLab()
+
+void Dbtc::seizeDatabuferrorLab(Signal* signal)
+{
+ terrorCode = ZGET_DATAREC_ERROR;
+ releaseAtErrorLab(signal);
+}//Dbtc::seizeDatabuferrorLab()
+
+void Dbtc::releaseAtErrorLab(Signal* signal)
+{
+ ptrGuard(tcConnectptr);
+ tcConnectptr.p->tcConnectstate = OS_ABORTING;
+ /*-------------------------------------------------------------------------*
+ * A FAILURE OF THIS OPERATION HAS OCCURRED. THIS FAILURE WAS EITHER A
+ * FAULTY PARAMETER OR A RESOURCE THAT WAS NOT AVAILABLE.
+ * WE WILL ABORT THE ENTIRE TRANSACTION SINCE THIS IS THE SAFEST PATH
+ * TO HANDLE THIS PROBLEM.
+ * SINCE WE HAVE NOT YET CONTACTED ANY LQH WE SET NUMBER OF NODES TO ZERO
+ * WE ALSO SET THE STATE TO ABORTING TO INDICATE THAT WE ARE NOT EXPECTING
+ * ANY SIGNALS.
+ *-------------------------------------------------------------------------*/
+ tcConnectptr.p->noOfNodes = 0;
+ abortErrorLab(signal);
+}//Dbtc::releaseAtErrorLab()
+
+void Dbtc::warningHandlerLab(Signal* signal)
+{
+ ndbassert(false);
+}//Dbtc::warningHandlerLab()
+
+void Dbtc::systemErrorLab(Signal* signal)
+{
+ progError(0, 0);
+}//Dbtc::systemErrorLab()
+
+
+/* ######################################################################### *
+ * ####### SCAN MODULE ####### *
+ * ######################################################################### *
+
+ The application orders a scan of a table. We divide the scan into a scan on
+ each fragment. The scan uses the primary replicas since the scan might be
+ used for an update in a separate transaction.
+
+ Scans are always done as a separate transaction. Locks from the scan
+ can be overtaken by another transaction. Scans can never lock the entire
+ table. Locks are released immediately after the read has been verified
+ by the application. There is not even an option to leave the locks.
+ The reason is that this would hurt real-time behaviour too much.
+
+ -# The first step in handling a scan of a table is to receive all signals
+ defining the scan. If failures occur during this step we release all
+ resource and reply with SCAN_TABREF providing the error code.
+ If system load is too high, the request will not be allowed.
+
+ -# The second step retrieves the number of fragments that exist in the
+ table. It also ensures that the table actually exist. After this,
+ the scan is ready to be parallelised. The idea is that the receiving
+ process (hereafter called delivery process) will start up a number
+ of scan processes. Each of these scan processes will
+ independently scan one fragment at a time. The delivery
+ process object is the scan record and the scan process object is
+ the scan fragment record plus the scan operation record.
+
+ -# The third step is thus performed in parallel. In the third step each
+ scan process retrieves the primary replica of the fragment it will
+ scan. Then it starts the scan as soon as the load on that node permits.
+
+ The LQH returns either when it retrieved the maximum number of tuples or
+ when it has retrived at least one tuple and is hindered by a lock to
+ retrieve the next tuple. This is to ensure that a scan process never
+ can be involved in a deadlock situation.
+
+ When the scan process receives a number of tuples to report to the
+ application it checks the state of the delivery process. Only one delivery
+ at a time is handled by the application. Thus if the delivery process
+ has already sent a number of tuples to the application this set of tuples
+ are queued.
+
+ When the application requests the next set of tuples it is immediately
+ delivered if any are queued, otherwise it waits for the next scan
+ process that is ready to deliver.
+
+
+ ERROR HANDLING
+
+ As already mentioned it is rather easy to handle errors before the scan
+ processes have started. In this case it is enough to release the resources
+ and send SCAN_TAB_REF.
+
+ If an error occurs in any of the scan processes then we have to stop all
+ scan processes. We do however only stop the delivery process and ask
+ the api to order us to close the scan. The reason is that we can easily
+ enter into difficult timing problems since the application and this
+ block is out of synch we will thus always start by report the error to
+ the application and wait for a close request. This error report uses the
+ SCAN_TABREF signal with a special error code that the api must check for.
+
+
+ CLOSING AN ACTIVE SCAN
+
+ The application can close a scan for several reasons before it is completed.
+ One reason was mentioned above where an error in a scan process led to a
+ request to close the scan. Another reason could simply be that the
+ application found what it looked for and is thus not interested in the
+ rest of the scan.
+
+ IT COULD ALSO BE DEPENDENT ON INTERNAL ERRORS IN THE API.
+
+ When a close scan request is received, all scan processes are stopped and all
+ resources belonging to those scan processes are released. Stopping the scan
+ processes most often includes communication with an LQH where the local scan
+ is controlled. Finally all resources belonging to the scan is released and
+ the SCAN_TABCONF is sent with an indication of that the scan is closed.
+
+
+ CLOSING A COMPLETED SCAN
+
+ When all scan processes are completed then a report is sent to the
+ application which indicates that no more tuples can be fetched.
+ The application will send a close scan and the same action as when
+ closing an active scan is performed.
+ In this case it will of course not find any active scan processes.
+ It will even find all scan processes already released.
+
+ The reason for requiring the api to close the scan is the same as above.
+ It is to avoid any timing problems due to that the api and this block
+ is out of synch.
+
+ * ######################################################################## */
+void Dbtc::execSCAN_TABREQ(Signal* signal)
+{
+ const ScanTabReq * const scanTabReq = (ScanTabReq *)&signal->theData[0];
+ const Uint32 ri = scanTabReq->requestInfo;
+ const Uint32 aiLength = (scanTabReq->attrLenKeyLen & 0xFFFF);
+ const Uint32 keyLen = scanTabReq->attrLenKeyLen >> 16;
+ const Uint32 schemaVersion = scanTabReq->tableSchemaVersion;
+ const Uint32 transid1 = scanTabReq->transId1;
+ const Uint32 transid2 = scanTabReq->transId2;
+ const Uint32 tmpXX = scanTabReq->buddyConPtr;
+ const Uint32 buddyPtr = (tmpXX == 0xFFFFFFFF ? RNIL : tmpXX);
+ Uint32 currSavePointId = 0;
+
+ Uint32 scanConcurrency = scanTabReq->getParallelism(ri);
+ Uint32 noOprecPerFrag = ScanTabReq::getScanBatch(ri);
+ Uint32 scanParallel = scanConcurrency;
+ Uint32 errCode;
+ ScanRecordPtr scanptr;
+
+ jamEntry();
+
+ SegmentedSectionPtr api_op_ptr;
+ signal->getSection(api_op_ptr, 0);
+ copy(&cdata[0], api_op_ptr);
+ releaseSections(signal);
+
+ apiConnectptr.i = scanTabReq->apiConnectPtr;
+ tabptr.i = scanTabReq->tableId;
+
+ if (apiConnectptr.i >= capiConnectFilesize)
+ {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+
+ ptrAss(apiConnectptr, apiConnectRecord);
+ ApiConnectRecord * transP = apiConnectptr.p;
+
+ if (transP->apiConnectstate != CS_CONNECTED) {
+ jam();
+ // could be left over from TCKEYREQ rollback
+ if (transP->apiConnectstate == CS_ABORTING &&
+ transP->abortState == AS_IDLE) {
+ jam();
+ } else if(transP->apiConnectstate == CS_STARTED &&
+ transP->firstTcConnect == RNIL){
+ jam();
+ // left over from simple/dirty read
+ } else {
+ jam();
+ errCode = ZSTATE_ERROR;
+ goto SCAN_TAB_error_no_state_change;
+ }
+ }
+
+ if(tabptr.i >= ctabrecFilesize)
+ {
+ errCode = ZUNKNOWN_TABLE_ERROR;
+ goto SCAN_TAB_error;
+ }
+
+ ptrAss(tabptr, tableRecord);
+ if ((aiLength == 0) ||
+ (!tabptr.p->checkTable(schemaVersion)) ||
+ (scanConcurrency == 0) ||
+ (cfirstfreeTcConnect == RNIL) ||
+ (cfirstfreeScanrec == RNIL)) {
+ goto SCAN_error_check;
+ }
+ if (buddyPtr != RNIL) {
+ jam();
+ ApiConnectRecordPtr buddyApiPtr;
+ buddyApiPtr.i = buddyPtr;
+ ptrCheckGuard(buddyApiPtr, capiConnectFilesize, apiConnectRecord);
+ if ((transid1 == buddyApiPtr.p->transid[0]) &&
+ (transid2 == buddyApiPtr.p->transid[1])) {
+ jam();
+
+ if (buddyApiPtr.p->apiConnectstate == CS_ABORTING) {
+ // transaction has been aborted
+ jam();
+ errCode = buddyApiPtr.p->returncode;
+ goto SCAN_TAB_error;
+ }//if
+ currSavePointId = buddyApiPtr.p->currSavePointId;
+ buddyApiPtr.p->currSavePointId++;
+ }
+ }
+
+ seizeTcConnect(signal);
+ tcConnectptr.p->apiConnect = apiConnectptr.i;
+ tcConnectptr.p->tcConnectstate = OS_WAIT_SCAN;
+ apiConnectptr.p->lastTcConnect = tcConnectptr.i;
+
+ seizeCacheRecord(signal);
+ cachePtr.p->keylen = keyLen;
+ cachePtr.p->save1 = 0;
+ cachePtr.p->distributionKey = scanTabReq->distributionKey;
+ cachePtr.p->distributionKeyIndicator= ScanTabReq::getDistributionKeyFlag(ri);
+ scanptr = seizeScanrec(signal);
+
+ ndbrequire(transP->apiScanRec == RNIL);
+ ndbrequire(scanptr.p->scanApiRec == RNIL);
+
+ initScanrec(scanptr, scanTabReq, scanParallel, noOprecPerFrag);
+
+ transP->apiScanRec = scanptr.i;
+ transP->returncode = 0;
+ transP->transid[0] = transid1;
+ transP->transid[1] = transid2;
+ transP->buddyPtr = buddyPtr;
+
+ // The scan is started
+ transP->apiConnectstate = CS_START_SCAN;
+ transP->currSavePointId = currSavePointId;
+
+ /**********************************************************
+ * We start the timer on scanRec to be able to discover a
+ * timeout in the API the API now is in charge!
+ ***********************************************************/
+ setApiConTimer(apiConnectptr.i, ctcTimer, __LINE__);
+ updateBuddyTimer(apiConnectptr);
+
+ /***********************************************************
+ * WE HAVE NOW RECEIVED ALL REFERENCES TO SCAN OBJECTS IN
+ * THE API. WE ARE NOW READY TO RECEIVE THE ATTRIBUTE INFO
+ * IF ANY TO RECEIVE.
+ **********************************************************/
+ scanptr.p->scanState = ScanRecord::WAIT_AI;
+ return;
+
+ SCAN_error_check:
+ if (aiLength == 0) {
+ jam()
+ errCode = ZSCAN_AI_LEN_ERROR;
+ goto SCAN_TAB_error;
+ }//if
+ if (!tabptr.p->checkTable(schemaVersion)){
+ jam();
+ errCode = tabptr.p->getErrorCode(schemaVersion);
+ goto SCAN_TAB_error;
+ }//if
+ if (scanConcurrency == 0) {
+ jam();
+ errCode = ZNO_CONCURRENCY_ERROR;
+ goto SCAN_TAB_error;
+ }//if
+ if (cfirstfreeTcConnect == RNIL) {
+ jam();
+ errCode = ZNO_FREE_TC_CONNECTION;
+ goto SCAN_TAB_error;
+ }//if
+ ndbrequire(cfirstfreeScanrec == RNIL);
+ jam();
+ errCode = ZNO_SCANREC_ERROR;
+ goto SCAN_TAB_error;
+
+SCAN_TAB_error:
+ jam();
+ /**
+ * Prepare for up coming ATTRINFO/KEYINFO
+ */
+ transP->apiConnectstate = CS_ABORTING;
+ transP->abortState = AS_IDLE;
+ transP->transid[0] = transid1;
+ transP->transid[1] = transid2;
+
+SCAN_TAB_error_no_state_change:
+
+ ScanTabRef * ref = (ScanTabRef*)&signal->theData[0];
+ ref->apiConnectPtr = transP->ndbapiConnect;
+ ref->transId1 = transid1;
+ ref->transId2 = transid2;
+ ref->errorCode = errCode;
+ ref->closeNeeded = 0;
+ sendSignal(transP->ndbapiBlockref, GSN_SCAN_TABREF,
+ signal, ScanTabRef::SignalLength, JBB);
+ return;
+}//Dbtc::execSCAN_TABREQ()
+
+void Dbtc::initScanrec(ScanRecordPtr scanptr,
+ const ScanTabReq * scanTabReq,
+ UintR scanParallel,
+ UintR noOprecPerFrag)
+{
+ const UintR ri = scanTabReq->requestInfo;
+ scanptr.p->scanTcrec = tcConnectptr.i;
+ scanptr.p->scanApiRec = apiConnectptr.i;
+ scanptr.p->scanAiLength = scanTabReq->attrLenKeyLen & 0xFFFF;
+ scanptr.p->scanKeyLen = scanTabReq->attrLenKeyLen >> 16;
+ scanptr.p->scanTableref = tabptr.i;
+ scanptr.p->scanSchemaVersion = scanTabReq->tableSchemaVersion;
+ scanptr.p->scanParallel = scanParallel;
+ 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;
+
+ Uint32 tmp = 0;
+ ScanFragReq::setLockMode(tmp, ScanTabReq::getLockMode(ri));
+ ScanFragReq::setHoldLockFlag(tmp, ScanTabReq::getHoldLockFlag(ri));
+ ScanFragReq::setKeyinfoFlag(tmp, ScanTabReq::getKeyinfoFlag(ri));
+ ScanFragReq::setReadCommittedFlag(tmp,ScanTabReq::getReadCommittedFlag(ri));
+ ScanFragReq::setRangeScanFlag(tmp, ScanTabReq::getRangeScanFlag(ri));
+ ScanFragReq::setDescendingFlag(tmp, ScanTabReq::getDescendingFlag(ri));
+ ScanFragReq::setAttrLen(tmp, scanTabReq->attrLenKeyLen & 0xFFFF);
+
+ scanptr.p->scanRequestInfo = tmp;
+ scanptr.p->scanStoredProcId = scanTabReq->storedProcId;
+ scanptr.p->scanState = ScanRecord::RUNNING;
+ scanptr.p->m_queued_count = 0;
+
+ ScanFragList list(c_scan_frag_pool,
+ scanptr.p->m_running_scan_frags);
+ for (Uint32 i = 0; i < scanParallel; i++) {
+ jam();
+ ScanFragRecPtr ptr;
+ ndbrequire(list.seize(ptr));
+ ptr.p->scanRec = scanptr.i;
+ ptr.p->scanFragId = 0;
+ ptr.p->m_apiPtr = cdata[i];
+ }//for
+
+ (* (ScanTabReq::getRangeScanFlag(ri) ?
+ &c_counters.c_range_scan_count :
+ &c_counters.c_scan_count))++;
+}//Dbtc::initScanrec()
+
+void Dbtc::scanTabRefLab(Signal* signal, Uint32 errCode)
+{
+ ScanTabRef * ref = (ScanTabRef*)&signal->theData[0];
+ ref->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
+ ref->transId1 = apiConnectptr.p->transid[0];
+ ref->transId2 = apiConnectptr.p->transid[1];
+ ref->errorCode = errCode;
+ ref->closeNeeded = 0;
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_SCAN_TABREF,
+ signal, ScanTabRef::SignalLength, JBB);
+}//Dbtc::scanTabRefLab()
+
+/*---------------------------------------------------------------------------*/
+/* */
+/* RECEPTION OF ATTRINFO FOR SCAN TABLE REQUEST. */
+/*---------------------------------------------------------------------------*/
+void Dbtc::scanAttrinfoLab(Signal* signal, UintR Tlen)
+{
+ ScanRecordPtr scanptr;
+ scanptr.i = apiConnectptr.p->apiScanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ cachePtr.i = apiConnectptr.p->cachePtr;
+ ptrCheckGuard(cachePtr, ccacheFilesize, cacheRecord);
+ CacheRecord * const regCachePtr = cachePtr.p;
+ ndbrequire(scanptr.p->scanState == ScanRecord::WAIT_AI);
+
+ regCachePtr->currReclenAi = regCachePtr->currReclenAi + Tlen;
+ if (regCachePtr->currReclenAi < scanptr.p->scanAiLength) {
+ if (cfirstfreeAttrbuf == RNIL) {
+ goto scanAttrinfo_attrbuf_error;
+ }//if
+ saveAttrbuf(signal);
+ } else {
+ if (regCachePtr->currReclenAi > scanptr.p->scanAiLength) {
+ goto scanAttrinfo_len_error;
+ } else {
+ /* CURR_RECLEN_AI = SCAN_AI_LENGTH */
+ if (cfirstfreeAttrbuf == RNIL) {
+ goto scanAttrinfo_attrbuf2_error;
+ }//if
+ saveAttrbuf(signal);
+ /**************************************************
+ * WE HAVE NOW RECEIVED ALL INFORMATION CONCERNING
+ * THIS SCAN. WE ARE READY TO START THE ACTUAL
+ * EXECUTION OF THE SCAN QUERY
+ **************************************************/
+ diFcountReqLab(signal, scanptr);
+ return;
+ }//if
+ }//if
+ return;
+
+scanAttrinfo_attrbuf_error:
+ jam();
+ abortScanLab(signal, scanptr, ZGET_ATTRBUF_ERROR);
+ return;
+
+scanAttrinfo_attrbuf2_error:
+ jam();
+ abortScanLab(signal, scanptr, ZGET_ATTRBUF_ERROR);
+ return;
+
+scanAttrinfo_len_error:
+ jam();
+ abortScanLab(signal, scanptr, ZLENGTH_ERROR);
+ return;
+}//Dbtc::scanAttrinfoLab()
+
+void Dbtc::diFcountReqLab(Signal* signal, ScanRecordPtr scanptr)
+{
+ /**
+ * Check so that the table is not being dropped
+ */
+ TableRecordPtr tabPtr;
+ tabPtr.i = scanptr.p->scanTableref;
+ tabPtr.p = &tableRecord[tabPtr.i];
+ if (tabPtr.p->checkTable(scanptr.p->scanSchemaVersion)){
+ ;
+ } else {
+ abortScanLab(signal, scanptr,
+ tabPtr.p->getErrorCode(scanptr.p->scanSchemaVersion));
+ return;
+ }
+
+ scanptr.p->scanNextFragId = 0;
+ scanptr.p->m_booked_fragments_count= 0;
+ scanptr.p->scanState = ScanRecord::WAIT_FRAGMENT_COUNT;
+
+ if(!cachePtr.p->distributionKeyIndicator)
+ {
+ jam();
+ /*************************************************
+ * THE FIRST STEP TO RECEIVE IS SUCCESSFULLY COMPLETED.
+ * WE MUST FIRST GET THE NUMBER OF FRAGMENTS IN THE TABLE.
+ ***************************************************/
+ signal->theData[0] = tcConnectptr.p->dihConnectptr;
+ signal->theData[1] = scanptr.p->scanTableref;
+ sendSignal(cdihblockref, GSN_DI_FCOUNTREQ, signal, 2, JBB);
+ }
+ else
+ {
+ signal->theData[0] = tcConnectptr.p->dihConnectptr;
+ signal->theData[1] = tabPtr.i;
+ signal->theData[2] = cachePtr.p->distributionKey;
+ EXECUTE_DIRECT(DBDIH, GSN_DIGETNODESREQ, signal, 3);
+ UintR TerrorIndicator = signal->theData[0];
+ jamEntry();
+ if (TerrorIndicator != 0) {
+ signal->theData[0] = tcConnectptr.i;
+ //signal->theData[1] Contains error
+ execDI_FCOUNTREF(signal);
+ return;
+ }
+
+ UintR Tdata1 = signal->theData[1];
+ scanptr.p->scanNextFragId = Tdata1;
+
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = 1; // Frag count
+ execDI_FCOUNTCONF(signal);
+ }
+ return;
+}//Dbtc::diFcountReqLab()
+
+/********************************************************************
+ * execDI_FCOUNTCONF
+ *
+ * WE HAVE ASKED DIH ABOUT THE NUMBER OF FRAGMENTS IN THIS TABLE.
+ * WE WILL NOW START A NUMBER OF PARALLEL SCAN PROCESSES. EACH OF
+ * THESE WILL SCAN ONE FRAGMENT AT A TIME. THEY WILL CONTINUE THIS
+ * UNTIL THERE ARE NO MORE FRAGMENTS TO SCAN OR UNTIL THE APPLICATION
+ * CLOSES THE SCAN.
+ ********************************************************************/
+void Dbtc::execDI_FCOUNTCONF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ Uint32 tfragCount = signal->theData[1];
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.i = tcConnectptr.p->apiConnect;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ ScanRecordPtr scanptr;
+ scanptr.i = apiConnectptr.p->apiScanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ ndbrequire(scanptr.p->scanState == ScanRecord::WAIT_FRAGMENT_COUNT);
+ if (apiConnectptr.p->apiFailState == ZTRUE) {
+ jam();
+ releaseScanResources(scanptr);
+ handleApiFailState(signal, apiConnectptr.i);
+ return;
+ }//if
+ if (tfragCount == 0) {
+ jam();
+ abortScanLab(signal, scanptr, ZNO_FRAGMENT_ERROR);
+ return;
+ }//if
+
+ /**
+ * Check so that the table is not being dropped
+ */
+ TableRecordPtr tabPtr;
+ tabPtr.i = scanptr.p->scanTableref;
+ tabPtr.p = &tableRecord[tabPtr.i];
+ if (tabPtr.p->checkTable(scanptr.p->scanSchemaVersion)){
+ ;
+ } else {
+ abortScanLab(signal, scanptr,
+ tabPtr.p->getErrorCode(scanptr.p->scanSchemaVersion));
+ return;
+ }
+
+ scanptr.p->scanParallel = tfragCount;
+ scanptr.p->scanNoFrag = tfragCount;
+ scanptr.p->scanState = ScanRecord::RUNNING;
+
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ updateBuddyTimer(apiConnectptr);
+
+ ScanFragRecPtr ptr;
+ ScanFragList list(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+ for (list.first(ptr); !ptr.isNull() && tfragCount;
+ list.next(ptr), tfragCount--){
+ jam();
+
+ ptr.p->lqhBlockref = 0;
+ ptr.p->startFragTimer(ctcTimer);
+ ptr.p->scanFragId = scanptr.p->scanNextFragId++;
+ ptr.p->scanFragState = ScanFragRec::WAIT_GET_PRIMCONF;
+ ptr.p->startFragTimer(ctcTimer);
+
+ signal->theData[0] = tcConnectptr.p->dihConnectptr;
+ signal->theData[1] = ptr.i;
+ signal->theData[2] = scanptr.p->scanTableref;
+ signal->theData[3] = ptr.p->scanFragId;
+ sendSignal(cdihblockref, GSN_DIGETPRIMREQ, signal, 4, JBB);
+ }//for
+
+ ScanFragList queued(c_scan_frag_pool, scanptr.p->m_queued_scan_frags);
+ for (; !ptr.isNull();)
+ {
+ ptr.p->m_ops = 0;
+ ptr.p->m_totalLen = 0;
+ ptr.p->m_scan_frag_conf_status = 1;
+ ptr.p->scanFragState = ScanFragRec::QUEUED_FOR_DELIVERY;
+ ptr.p->stopFragTimer();
+
+ ScanFragRecPtr tmp = ptr;
+ list.next(ptr);
+ list.remove(tmp);
+ queued.add(tmp);
+ scanptr.p->m_queued_count++;
+ }
+}//Dbtc::execDI_FCOUNTCONF()
+
+/******************************************************
+ * execDI_FCOUNTREF
+ ******************************************************/
+void Dbtc::execDI_FCOUNTREF(Signal* signal)
+{
+ jamEntry();
+ tcConnectptr.i = signal->theData[0];
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ const Uint32 errCode = signal->theData[1];
+ apiConnectptr.i = tcConnectptr.p->apiConnect;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ ScanRecordPtr scanptr;
+ scanptr.i = apiConnectptr.p->apiScanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ ndbrequire(scanptr.p->scanState == ScanRecord::WAIT_FRAGMENT_COUNT);
+ if (apiConnectptr.p->apiFailState == ZTRUE) {
+ jam();
+ releaseScanResources(scanptr);
+ handleApiFailState(signal, apiConnectptr.i);
+ return;
+ }//if
+ abortScanLab(signal, scanptr, errCode);
+}//Dbtc::execDI_FCOUNTREF()
+
+void Dbtc::abortScanLab(Signal* signal, ScanRecordPtr scanptr, Uint32 errCode)
+{
+ scanTabRefLab(signal, errCode);
+ releaseScanResources(scanptr);
+}//Dbtc::abortScanLab()
+
+void Dbtc::releaseScanResources(ScanRecordPtr scanPtr)
+{
+ if (apiConnectptr.p->cachePtr != RNIL) {
+ cachePtr.i = apiConnectptr.p->cachePtr;
+ ptrCheckGuard(cachePtr, ccacheFilesize, cacheRecord);
+ releaseKeys();
+ releaseAttrinfo();
+ }//if
+ tcConnectptr.i = scanPtr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ releaseTcCon();
+
+ ndbrequire(scanPtr.p->m_running_scan_frags.isEmpty());
+ ndbrequire(scanPtr.p->m_queued_scan_frags.isEmpty());
+ ndbrequire(scanPtr.p->m_delivered_scan_frags.isEmpty());
+
+ ndbassert(scanPtr.p->scanApiRec == apiConnectptr.i);
+ ndbassert(apiConnectptr.p->apiScanRec == scanPtr.i);
+
+ // link into free list
+ scanPtr.p->nextScan = cfirstfreeScanrec;
+ scanPtr.p->scanState = ScanRecord::IDLE;
+ scanPtr.p->scanTcrec = RNIL;
+ scanPtr.p->scanApiRec = RNIL;
+ cfirstfreeScanrec = scanPtr.i;
+
+ apiConnectptr.p->apiScanRec = RNIL;
+ apiConnectptr.p->apiConnectstate = CS_CONNECTED;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+}//Dbtc::releaseScanResources()
+
+
+/****************************************************************
+ * execDIGETPRIMCONF
+ *
+ * WE HAVE RECEIVED THE PRIMARY NODE OF THIS FRAGMENT.
+ * WE ARE NOW READY TO ASK FOR PERMISSION TO LOAD THIS
+ * SPECIFIC NODE WITH A SCAN OPERATION.
+ ****************************************************************/
+void Dbtc::execDIGETPRIMCONF(Signal* signal)
+{
+ jamEntry();
+ // tcConnectptr.i in theData[0] is not used
+ scanFragptr.i = signal->theData[1];
+ c_scan_frag_pool.getPtr(scanFragptr);
+
+ tnodeid = signal->theData[2];
+ arrGuard(tnodeid, MAX_NDB_NODES);
+
+ ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::WAIT_GET_PRIMCONF);
+ scanFragptr.p->stopFragTimer();
+
+ ScanRecordPtr scanptr;
+ scanptr.i = scanFragptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+
+ /**
+ * This must be false as select count(*) otherwise
+ * can "pass" committing on backup fragments and
+ * get incorrect row count
+ */
+ if(false && ScanFragReq::getReadCommittedFlag(scanptr.p->scanRequestInfo))
+ {
+ jam();
+ Uint32 max = 3+signal->theData[6];
+ Uint32 nodeid = getOwnNodeId();
+ for(Uint32 i = 3; i<max; i++)
+ if(signal->theData[i] == nodeid)
+ {
+ jam();
+ tnodeid = nodeid;
+ break;
+ }
+ }
+
+ {
+ /**
+ * Check table
+ */
+ TableRecordPtr tabPtr;
+ tabPtr.i = scanptr.p->scanTableref;
+ ptrAss(tabPtr, tableRecord);
+ Uint32 schemaVersion = scanptr.p->scanSchemaVersion;
+ if(tabPtr.p->checkTable(schemaVersion) == false){
+ jam();
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ run.release(scanFragptr);
+ scanError(signal, scanptr, tabPtr.p->getErrorCode(schemaVersion));
+ return;
+ }
+ }
+
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.i = scanptr.p->scanApiRec;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ cachePtr.i = apiConnectptr.p->cachePtr;
+ ptrCheckGuard(cachePtr, ccacheFilesize, cacheRecord);
+ switch (scanptr.p->scanState) {
+ case ScanRecord::CLOSING_SCAN:
+ jam();
+ updateBuddyTimer(apiConnectptr);
+ {
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ run.release(scanFragptr);
+ }
+ close_scan_req_send_conf(signal, scanptr);
+ return;
+ default:
+ jam();
+ /*empty*/;
+ break;
+ }//switch
+ Uint32 ref = calcLqhBlockRef(tnodeid);
+ scanFragptr.p->lqhBlockref = ref;
+ scanFragptr.p->m_connectCount = getNodeInfo(tnodeid).m_connectCount;
+ sendScanFragReq(signal, scanptr.p, scanFragptr.p);
+ if(ERROR_INSERTED(8035))
+ globalTransporterRegistry.performSend();
+ attrbufptr.i = cachePtr.p->firstAttrbuf;
+ while (attrbufptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(attrbufptr, cattrbufFilesize, attrbufRecord);
+ sendAttrinfo(signal,
+ scanFragptr.i,
+ attrbufptr.p,
+ ref);
+ attrbufptr.i = attrbufptr.p->attrbuf[ZINBUF_NEXT];
+ if(ERROR_INSERTED(8035))
+ globalTransporterRegistry.performSend();
+ }//while
+ scanFragptr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
+ scanFragptr.p->startFragTimer(ctcTimer);
+ updateBuddyTimer(apiConnectptr);
+ /*********************************************
+ * WE HAVE NOW STARTED A FRAGMENT SCAN. NOW
+ * WAIT FOR THE FIRST SCANNED RECORDS
+ *********************************************/
+}//Dbtc::execDIGETPRIMCONF
+
+/***************************************************
+ * execDIGETPRIMREF
+ *
+ * WE ARE NOW FORCED TO STOP THE SCAN. THIS ERROR
+ * IS NOT RECOVERABLE SINCE THERE IS A PROBLEM WITH
+ * FINDING A PRIMARY REPLICA OF A CERTAIN FRAGMENT.
+ ***************************************************/
+void Dbtc::execDIGETPRIMREF(Signal* signal)
+{
+ jamEntry();
+ // tcConnectptr.i in theData[0] is not used.
+ scanFragptr.i = signal->theData[1];
+ const Uint32 errCode = signal->theData[2];
+ c_scan_frag_pool.getPtr(scanFragptr);
+ ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::WAIT_GET_PRIMCONF);
+
+ ScanRecordPtr scanptr;
+ scanptr.i = scanFragptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ run.release(scanFragptr);
+
+ scanError(signal, scanptr, errCode);
+}//Dbtc::execDIGETPRIMREF()
+
+/**
+ * Dbtc::execSCAN_FRAGREF
+ * Our attempt to scan a fragment was refused
+ * set error code and close all other fragment
+ * scan's belonging to this scan
+ */
+void Dbtc::execSCAN_FRAGREF(Signal* signal)
+{
+ const ScanFragRef * const ref = (ScanFragRef *)&signal->theData[0];
+
+ jamEntry();
+ const Uint32 errCode = ref->errorCode;
+
+ scanFragptr.i = ref->senderData;
+ c_scan_frag_pool.getPtr(scanFragptr);
+
+ ScanRecordPtr scanptr;
+ scanptr.i = scanFragptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+
+ apiConnectptr.i = scanptr.p->scanApiRec;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+
+ Uint32 transid1 = apiConnectptr.p->transid[0] ^ ref->transId1;
+ Uint32 transid2 = apiConnectptr.p->transid[1] ^ ref->transId2;
+ transid1 = transid1 | transid2;
+ if (transid1 != 0) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+
+ /**
+ * Set errorcode, close connection to this lqh fragment,
+ * stop fragment timer and call scanFragError to start
+ * close of the other fragment scans
+ */
+ ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::LQH_ACTIVE);
+ {
+ scanFragptr.p->scanFragState = ScanFragRec::COMPLETED;
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ run.release(scanFragptr);
+ scanFragptr.p->stopFragTimer();
+ }
+ scanError(signal, scanptr, errCode);
+}//Dbtc::execSCAN_FRAGREF()
+
+/**
+ * Dbtc::scanError
+ *
+ * Called when an error occurs during
+ */
+void Dbtc::scanError(Signal* signal, ScanRecordPtr scanptr, Uint32 errorCode)
+{
+ jam();
+ ScanRecord* scanP = scanptr.p;
+
+ DEBUG("scanError, errorCode = "<< errorCode <<
+ ", scanState = " << scanptr.p->scanState);
+
+ apiConnectptr.i = scanP->scanApiRec;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ ndbrequire(apiConnectptr.p->apiScanRec == scanptr.i);
+
+ if(scanP->scanState == ScanRecord::CLOSING_SCAN){
+ jam();
+ close_scan_req_send_conf(signal, scanptr);
+ return;
+ }
+
+ ndbrequire(scanP->scanState == ScanRecord::RUNNING);
+
+ /**
+ * Close scan wo/ having received an order to do so
+ */
+ close_scan_req(signal, scanptr, false);
+
+ const bool apiFail = (apiConnectptr.p->apiFailState == ZTRUE);
+ if(apiFail){
+ jam();
+ return;
+ }
+
+ ScanTabRef * ref = (ScanTabRef*)&signal->theData[0];
+ ref->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
+ ref->transId1 = apiConnectptr.p->transid[0];
+ ref->transId2 = apiConnectptr.p->transid[1];
+ ref->errorCode = errorCode;
+ ref->closeNeeded = 1;
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_SCAN_TABREF,
+ signal, ScanTabRef::SignalLength, JBB);
+}//Dbtc::scanError()
+
+/************************************************************
+ * execSCAN_FRAGCONF
+ *
+ * A NUMBER OF OPERATIONS HAVE BEEN COMPLETED IN THIS
+ * FRAGMENT. TAKE CARE OF AND ISSUE FURTHER ACTIONS.
+ ************************************************************/
+void Dbtc::execSCAN_FRAGCONF(Signal* signal)
+{
+ Uint32 transid1, transid2, total_len;
+ jamEntry();
+
+ const ScanFragConf * const conf = (ScanFragConf*)&signal->theData[0];
+ const Uint32 noCompletedOps = conf->completedOps;
+ const Uint32 status = conf->fragmentCompleted;
+
+ scanFragptr.i = conf->senderData;
+ c_scan_frag_pool.getPtr(scanFragptr);
+
+ ScanRecordPtr scanptr;
+ scanptr.i = scanFragptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+
+ apiConnectptr.i = scanptr.p->scanApiRec;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+
+ transid1 = apiConnectptr.p->transid[0] ^ conf->transId1;
+ transid2 = apiConnectptr.p->transid[1] ^ conf->transId2;
+ total_len= conf->total_len;
+ transid1 = transid1 | transid2;
+ if (transid1 != 0) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+
+ ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::LQH_ACTIVE);
+
+ if(scanptr.p->scanState == ScanRecord::CLOSING_SCAN){
+ jam();
+ if(status == 0){
+ /**
+ * We have started closing = we sent a close -> ignore this
+ */
+ return;
+ } else {
+ jam();
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+
+ run.release(scanFragptr);
+ scanFragptr.p->stopFragTimer();
+ scanFragptr.p->scanFragState = ScanFragRec::COMPLETED;
+ }
+ close_scan_req_send_conf(signal, scanptr);
+ return;
+ }
+
+ if(noCompletedOps == 0 && status != 0 &&
+ scanptr.p->scanNextFragId+scanptr.p->m_booked_fragments_count < scanptr.p->scanNoFrag){
+ /**
+ * Start on next fragment
+ */
+ scanFragptr.p->scanFragState = ScanFragRec::WAIT_GET_PRIMCONF;
+ scanFragptr.p->startFragTimer(ctcTimer);
+
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ scanFragptr.p->scanFragId = scanptr.p->scanNextFragId++;
+ signal->theData[0] = tcConnectptr.p->dihConnectptr;
+ signal->theData[1] = scanFragptr.i;
+ signal->theData[2] = scanptr.p->scanTableref;
+ signal->theData[3] = scanFragptr.p->scanFragId;
+ sendSignal(cdihblockref, GSN_DIGETPRIMREQ, signal, 4, JBB);
+ return;
+ }
+ /*
+ Uint32 totalLen = 0;
+ for(Uint32 i = 0; i<noCompletedOps; i++){
+ Uint32 tmp = conf->opReturnDataLen[i];
+ totalLen += tmp;
+ }
+ */
+ {
+ ScanFragList run(c_scan_frag_pool, scanptr.p->m_running_scan_frags);
+ ScanFragList queued(c_scan_frag_pool, scanptr.p->m_queued_scan_frags);
+
+ run.remove(scanFragptr);
+ queued.add(scanFragptr);
+ scanptr.p->m_queued_count++;
+ }
+
+ scanFragptr.p->m_scan_frag_conf_status = status;
+ scanFragptr.p->m_ops = noCompletedOps;
+ scanFragptr.p->m_totalLen = total_len;
+ scanFragptr.p->scanFragState = ScanFragRec::QUEUED_FOR_DELIVERY;
+ scanFragptr.p->stopFragTimer();
+
+ if(scanptr.p->m_queued_count > /** Min */ 0){
+ jam();
+ sendScanTabConf(signal, scanptr);
+ }
+}//Dbtc::execSCAN_FRAGCONF()
+
+/****************************************************************************
+ * execSCAN_NEXTREQ
+ *
+ * THE APPLICATION HAVE PROCESSED THE TUPLES TRANSFERRED AND IS NOW READY FOR
+ * MORE. THIS SIGNAL IS ALSO USED TO CLOSE THE SCAN.
+ ****************************************************************************/
+void Dbtc::execSCAN_NEXTREQ(Signal* signal)
+{
+ const ScanNextReq * const req = (ScanNextReq *)&signal->theData[0];
+ const UintR transid1 = req->transId1;
+ const UintR transid2 = req->transId2;
+ const UintR stopScan = req->stopScan;
+
+ jamEntry();
+
+ apiConnectptr.i = req->apiConnectPtr;
+ if (apiConnectptr.i >= capiConnectFilesize) {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+ ptrAss(apiConnectptr, apiConnectRecord);
+
+ /**
+ * Check transid
+ */
+ const UintR ctransid1 = apiConnectptr.p->transid[0] ^ transid1;
+ const UintR ctransid2 = apiConnectptr.p->transid[1] ^ transid2;
+ if ((ctransid1 | ctransid2) != 0){
+ ScanTabRef * ref = (ScanTabRef*)&signal->theData[0];
+ ref->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
+ ref->transId1 = transid1;
+ ref->transId2 = transid2;
+ ref->errorCode = ZSTATE_ERROR;
+ ref->closeNeeded = 0;
+ sendSignal(signal->senderBlockRef(), GSN_SCAN_TABREF,
+ signal, ScanTabRef::SignalLength, JBB);
+ DEBUG("Wrong transid");
+ return;
+ }
+
+ /**
+ * Check state of API connection
+ */
+ if (apiConnectptr.p->apiConnectstate != CS_START_SCAN) {
+ jam();
+ if (apiConnectptr.p->apiConnectstate == CS_CONNECTED) {
+ jam();
+ /*********************************************************************
+ * The application sends a SCAN_NEXTREQ after experiencing a time-out.
+ * We will send a SCAN_TABREF to indicate a time-out occurred.
+ *********************************************************************/
+ DEBUG("scanTabRefLab: ZSCANTIME_OUT_ERROR2");
+ ndbout_c("apiConnectptr(%d) -> abort", apiConnectptr.i);
+ ndbrequire(false); //B2 indication of strange things going on
+ scanTabRefLab(signal, ZSCANTIME_OUT_ERROR2);
+ return;
+ }
+ DEBUG("scanTabRefLab: ZSTATE_ERROR");
+ DEBUG(" apiConnectstate="<<apiConnectptr.p->apiConnectstate);
+ ndbrequire(false); //B2 indication of strange things going on
+ scanTabRefLab(signal, ZSTATE_ERROR);
+ return;
+ }//if
+
+ /*******************************************************
+ * START THE ACTUAL LOGIC OF SCAN_NEXTREQ.
+ ********************************************************/
+ // Stop the timer that is used to check for timeout in the API
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ ScanRecordPtr scanptr;
+ scanptr.i = apiConnectptr.p->apiScanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ ScanRecord* scanP = scanptr.p;
+
+ const Uint32 len = signal->getLength() - 4;
+
+ if (stopScan == ZTRUE) {
+ jam();
+ /*********************************************************************
+ * APPLICATION IS CLOSING THE SCAN.
+ **********************************************************************/
+ close_scan_req(signal, scanptr, true);
+ return;
+ }//if
+
+ if (scanptr.p->scanState == ScanRecord::CLOSING_SCAN){
+ jam();
+ /**
+ * The scan is closing (typically due to error)
+ * but the API hasn't understood it yet
+ *
+ * Wait for API close request
+ */
+ return;
+ }
+
+ // Copy op ptrs so I dont overwrite them when sending...
+ memcpy(signal->getDataPtrSend()+25, signal->getDataPtr()+4, 4 * len);
+
+ ScanFragNextReq tmp;
+ tmp.closeFlag = ZFALSE;
+ tmp.transId1 = apiConnectptr.p->transid[0];
+ tmp.transId2 = apiConnectptr.p->transid[1];
+ tmp.batch_size_rows = scanP->batch_size_rows;
+ tmp.batch_size_bytes = scanP->batch_byte_size;
+
+ ScanFragList running(c_scan_frag_pool, scanP->m_running_scan_frags);
+ ScanFragList delivered(c_scan_frag_pool, scanP->m_delivered_scan_frags);
+ for(Uint32 i = 0 ; i<len; i++){
+ jam();
+ scanFragptr.i = signal->theData[i+25];
+ c_scan_frag_pool.getPtr(scanFragptr);
+ ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::DELIVERED);
+
+ scanFragptr.p->startFragTimer(ctcTimer);
+ scanFragptr.p->m_ops = 0;
+
+ if(scanFragptr.p->m_scan_frag_conf_status)
+ {
+ /**
+ * last scan was complete
+ */
+ jam();
+ ndbrequire(scanptr.p->scanNextFragId < scanptr.p->scanNoFrag);
+ jam();
+ ndbassert(scanptr.p->m_booked_fragments_count);
+ scanptr.p->m_booked_fragments_count--;
+ scanFragptr.p->scanFragState = ScanFragRec::WAIT_GET_PRIMCONF;
+
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ scanFragptr.p->scanFragId = scanptr.p->scanNextFragId++;
+ signal->theData[0] = tcConnectptr.p->dihConnectptr;
+ signal->theData[1] = scanFragptr.i;
+ signal->theData[2] = scanptr.p->scanTableref;
+ signal->theData[3] = scanFragptr.p->scanFragId;
+ sendSignal(cdihblockref, GSN_DIGETPRIMREQ, signal, 4, JBB);
+ }
+ else
+ {
+ jam();
+ scanFragptr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
+ ScanFragNextReq * req = (ScanFragNextReq*)signal->getDataPtrSend();
+ * req = tmp;
+ req->senderData = scanFragptr.i;
+ sendSignal(scanFragptr.p->lqhBlockref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+ }
+ delivered.remove(scanFragptr);
+ running.add(scanFragptr);
+ }//for
+
+}//Dbtc::execSCAN_NEXTREQ()
+
+void
+Dbtc::close_scan_req(Signal* signal, ScanRecordPtr scanPtr, bool req_received){
+
+ ScanRecord* scanP = scanPtr.p;
+ ndbrequire(scanPtr.p->scanState != ScanRecord::IDLE);
+ scanPtr.p->scanState = ScanRecord::CLOSING_SCAN;
+ scanPtr.p->m_close_scan_req = req_received;
+
+ /**
+ * Queue : Action
+ * ============= : =================
+ * completed : -
+ * running : close -> LQH
+ * delivered w/ : close -> LQH
+ * delivered wo/ : move to completed
+ * queued w/ : close -> LQH
+ * queued wo/ : move to completed
+ */
+
+ ScanFragNextReq * nextReq = (ScanFragNextReq*)&signal->theData[0];
+ nextReq->closeFlag = ZTRUE;
+ nextReq->transId1 = apiConnectptr.p->transid[0];
+ nextReq->transId2 = apiConnectptr.p->transid[1];
+
+ {
+ ScanFragRecPtr ptr;
+ ScanFragList running(c_scan_frag_pool, scanP->m_running_scan_frags);
+ ScanFragList delivered(c_scan_frag_pool, scanP->m_delivered_scan_frags);
+ ScanFragList queued(c_scan_frag_pool, scanP->m_queued_scan_frags);
+
+ // Close running
+ for(running.first(ptr); !ptr.isNull(); ){
+ ScanFragRecPtr curr = ptr; // Remove while iterating...
+ running.next(ptr);
+
+ if(curr.p->scanFragState == ScanFragRec::WAIT_GET_PRIMCONF){
+ jam();
+ continue;
+ }
+ ndbrequire(curr.p->scanFragState == ScanFragRec::LQH_ACTIVE);
+
+ curr.p->startFragTimer(ctcTimer);
+ curr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
+ nextReq->senderData = curr.i;
+ sendSignal(curr.p->lqhBlockref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+ }
+
+ // Close delivered
+ for(delivered.first(ptr); !ptr.isNull(); ){
+ jam();
+ ScanFragRecPtr curr = ptr; // Remove while iterating...
+ delivered.next(ptr);
+
+ ndbrequire(curr.p->scanFragState == ScanFragRec::DELIVERED);
+ delivered.remove(curr);
+
+ if(curr.p->m_ops > 0 && curr.p->m_scan_frag_conf_status == 0){
+ jam();
+ running.add(curr);
+ curr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
+ curr.p->startFragTimer(ctcTimer);
+ nextReq->senderData = curr.i;
+ sendSignal(curr.p->lqhBlockref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+
+ } else {
+ jam();
+ c_scan_frag_pool.release(curr);
+ curr.p->scanFragState = ScanFragRec::COMPLETED;
+ curr.p->stopFragTimer();
+ }
+ }//for
+
+ /**
+ * All queued with data should be closed
+ */
+ for(queued.first(ptr); !ptr.isNull(); ){
+ jam();
+ ndbrequire(ptr.p->scanFragState == ScanFragRec::QUEUED_FOR_DELIVERY);
+ ScanFragRecPtr curr = ptr; // Remove while iterating...
+ queued.next(ptr);
+
+ queued.remove(curr);
+ scanP->m_queued_count--;
+
+ if(curr.p->m_ops > 0){
+ jam();
+ running.add(curr);
+ curr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
+ curr.p->startFragTimer(ctcTimer);
+ nextReq->senderData = curr.i;
+ sendSignal(curr.p->lqhBlockref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+ } else {
+ jam();
+ c_scan_frag_pool.release(curr);
+ curr.p->scanFragState = ScanFragRec::COMPLETED;
+ curr.p->stopFragTimer();
+ }
+ }
+ }
+ close_scan_req_send_conf(signal, scanPtr);
+}
+
+void
+Dbtc::close_scan_req_send_conf(Signal* signal, ScanRecordPtr scanPtr){
+
+ jam();
+
+ ndbrequire(scanPtr.p->m_queued_scan_frags.isEmpty());
+ ndbrequire(scanPtr.p->m_delivered_scan_frags.isEmpty());
+ //ndbrequire(scanPtr.p->m_running_scan_frags.isEmpty());
+
+#if 0
+ {
+ ScanFragList comp(c_scan_frag_pool, scanPtr.p->m_completed_scan_frags);
+ ScanFragRecPtr ptr;
+ for(comp.first(ptr); !ptr.isNull(); comp.next(ptr)){
+ ndbrequire(ptr.p->scanFragTimer == 0);
+ ndbrequire(ptr.p->scanFragState == ScanFragRec::COMPLETED);
+ }
+ }
+#endif
+
+ if(!scanPtr.p->m_running_scan_frags.isEmpty()){
+ jam();
+ return;
+ }
+
+ const bool apiFail = (apiConnectptr.p->apiFailState == ZTRUE);
+
+ if(!scanPtr.p->m_close_scan_req){
+ jam();
+ /**
+ * The API hasn't order closing yet
+ */
+ return;
+ }
+
+ Uint32 ref = apiConnectptr.p->ndbapiBlockref;
+ if(!apiFail && ref){
+ jam();
+ ScanTabConf * conf = (ScanTabConf*)&signal->theData[0];
+ conf->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
+ conf->requestInfo = ScanTabConf::EndOfData;
+ conf->transId1 = apiConnectptr.p->transid[0];
+ conf->transId2 = apiConnectptr.p->transid[1];
+ sendSignal(ref, GSN_SCAN_TABCONF, signal, ScanTabConf::SignalLength, JBB);
+ }
+
+ releaseScanResources(scanPtr);
+
+ if(apiFail){
+ jam();
+ /**
+ * API has failed
+ */
+ handleApiFailState(signal, apiConnectptr.i);
+ }
+}
+
+Dbtc::ScanRecordPtr
+Dbtc::seizeScanrec(Signal* signal) {
+ ScanRecordPtr scanptr;
+ scanptr.i = cfirstfreeScanrec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ cfirstfreeScanrec = scanptr.p->nextScan;
+ scanptr.p->nextScan = RNIL;
+ ndbrequire(scanptr.p->scanState == ScanRecord::IDLE);
+ return scanptr;
+}//Dbtc::seizeScanrec()
+
+void Dbtc::sendScanFragReq(Signal* signal,
+ ScanRecord* scanP,
+ ScanFragRec* scanFragP)
+{
+ ScanFragReq * const req = (ScanFragReq *)&signal->theData[0];
+ Uint32 requestInfo = scanP->scanRequestInfo;
+ ScanFragReq::setScanPrio(requestInfo, 1);
+ apiConnectptr.i = scanP->scanApiRec;
+ req->tableId = scanP->scanTableref;
+ req->schemaVersion = scanP->scanSchemaVersion;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ req->senderData = scanFragptr.i;
+ req->requestInfo = requestInfo;
+ req->fragmentNoKeyLen = scanFragP->scanFragId | (scanP->scanKeyLen << 16);
+ req->resultRef = apiConnectptr.p->ndbapiBlockref;
+ req->savePointId = apiConnectptr.p->currSavePointId;
+ req->transId1 = apiConnectptr.p->transid[0];
+ req->transId2 = apiConnectptr.p->transid[1];
+ req->clientOpPtr = scanFragP->m_apiPtr;
+ req->batch_size_rows= scanP->batch_size_rows;
+ req->batch_size_bytes= scanP->batch_byte_size;
+ sendSignal(scanFragP->lqhBlockref, GSN_SCAN_FRAGREQ, signal,
+ ScanFragReq::SignalLength, JBB);
+ if(scanP->scanKeyLen > 0)
+ {
+ tcConnectptr.i = scanFragptr.i;
+ packKeyData000Lab(signal, scanFragP->lqhBlockref, scanP->scanKeyLen);
+ }
+ updateBuddyTimer(apiConnectptr);
+ scanFragP->startFragTimer(ctcTimer);
+}//Dbtc::sendScanFragReq()
+
+
+void Dbtc::sendScanTabConf(Signal* signal, ScanRecordPtr scanPtr) {
+ jam();
+ Uint32* ops = signal->getDataPtrSend()+4;
+ Uint32 op_count = scanPtr.p->m_queued_count;
+ if(4 + 3 * op_count > 25){
+ jam();
+ ops += 21;
+ }
+
+ int left = scanPtr.p->scanNoFrag - scanPtr.p->scanNextFragId;
+ Uint32 booked = scanPtr.p->m_booked_fragments_count;
+
+ ScanTabConf * conf = (ScanTabConf*)&signal->theData[0];
+ conf->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
+ conf->requestInfo = op_count;
+ conf->transId1 = apiConnectptr.p->transid[0];
+ conf->transId2 = apiConnectptr.p->transid[1];
+ ScanFragRecPtr ptr;
+ {
+ ScanFragList queued(c_scan_frag_pool, scanPtr.p->m_queued_scan_frags);
+ ScanFragList delivered(c_scan_frag_pool,scanPtr.p->m_delivered_scan_frags);
+ for(queued.first(ptr); !ptr.isNull(); ){
+ ndbrequire(ptr.p->scanFragState == ScanFragRec::QUEUED_FOR_DELIVERY);
+ ScanFragRecPtr curr = ptr; // Remove while iterating...
+ queued.next(ptr);
+
+ bool done = curr.p->m_scan_frag_conf_status && (left <= (int)booked);
+ if(curr.p->m_scan_frag_conf_status)
+ booked++;
+
+ * ops++ = curr.p->m_apiPtr;
+ * ops++ = done ? RNIL : curr.i;
+ * ops++ = (curr.p->m_totalLen << 10) + curr.p->m_ops;
+
+ queued.remove(curr);
+ if(!done){
+ delivered.add(curr);
+ curr.p->scanFragState = ScanFragRec::DELIVERED;
+ curr.p->stopFragTimer();
+ } else {
+ c_scan_frag_pool.release(curr);
+ curr.p->scanFragState = ScanFragRec::COMPLETED;
+ curr.p->stopFragTimer();
+ }
+ }
+ }
+
+ scanPtr.p->m_booked_fragments_count = booked;
+ if(scanPtr.p->m_delivered_scan_frags.isEmpty() &&
+ scanPtr.p->m_running_scan_frags.isEmpty())
+ {
+ conf->requestInfo = op_count | ScanTabConf::EndOfData;
+ releaseScanResources(scanPtr);
+ }
+
+ if(4 + 3 * 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 {
+ jam();
+ sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_SCAN_TABCONF, signal,
+ ScanTabConf::SignalLength + 3 * op_count, JBB);
+ }
+ scanPtr.p->m_queued_count = 0;
+}//Dbtc::sendScanTabConf()
+
+
+void Dbtc::gcpTcfinished(Signal* signal)
+{
+ signal->theData[1] = tcheckGcpId;
+ sendSignal(cdihblockref, GSN_GCP_TCFINISHED, signal, 2, JBB);
+}//Dbtc::gcpTcfinished()
+
+void Dbtc::initApiConnect(Signal* signal)
+{
+ Uint32 tiacTmp;
+ Uint32 guard4;
+
+ tiacTmp = capiConnectFilesize / 3;
+ ndbrequire(tiacTmp > 0);
+ guard4 = tiacTmp + 1;
+ for (cachePtr.i = 0; cachePtr.i < guard4; cachePtr.i++) {
+ refresh_watch_dog();
+ ptrAss(cachePtr, cacheRecord);
+ cachePtr.p->firstAttrbuf = RNIL;
+ cachePtr.p->lastAttrbuf = RNIL;
+ cachePtr.p->firstKeybuf = RNIL;
+ cachePtr.p->lastKeybuf = RNIL;
+ cachePtr.p->nextCacheRec = cachePtr.i + 1;
+ }//for
+ cachePtr.i = tiacTmp;
+ ptrCheckGuard(cachePtr, ccacheFilesize, cacheRecord);
+ cachePtr.p->nextCacheRec = RNIL;
+ cfirstfreeCacheRec = 0;
+
+ guard4 = tiacTmp - 1;
+ for (apiConnectptr.i = 0; apiConnectptr.i <= guard4; apiConnectptr.i++) {
+ refresh_watch_dog();
+ jam();
+ ptrAss(apiConnectptr, apiConnectRecord);
+ apiConnectptr.p->apiConnectstate = CS_DISCONNECTED;
+ apiConnectptr.p->apiFailState = ZFALSE;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ apiConnectptr.p->takeOverRec = (Uint8)Z8NIL;
+ apiConnectptr.p->cachePtr = RNIL;
+ apiConnectptr.p->nextApiConnect = apiConnectptr.i + 1;
+ apiConnectptr.p->ndbapiBlockref = 0xFFFFFFFF; // Invalid ref
+ apiConnectptr.p->commitAckMarker = RNIL;
+ apiConnectptr.p->firstTcConnect = RNIL;
+ apiConnectptr.p->lastTcConnect = RNIL;
+ apiConnectptr.p->triggerPending = false;
+ apiConnectptr.p->isIndexOp = false;
+ apiConnectptr.p->accumulatingIndexOp = RNIL;
+ apiConnectptr.p->executingIndexOp = RNIL;
+ apiConnectptr.p->buddyPtr = RNIL;
+ apiConnectptr.p->currSavePointId = 0;
+ }//for
+ apiConnectptr.i = tiacTmp - 1;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ apiConnectptr.p->nextApiConnect = RNIL;
+ cfirstfreeApiConnect = 0;
+ guard4 = (2 * tiacTmp) - 1;
+ for (apiConnectptr.i = tiacTmp; apiConnectptr.i <= guard4; apiConnectptr.i++)
+ {
+ refresh_watch_dog();
+ jam();
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ apiConnectptr.p->apiConnectstate = CS_RESTART;
+ apiConnectptr.p->apiFailState = ZFALSE;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ apiConnectptr.p->takeOverRec = (Uint8)Z8NIL;
+ apiConnectptr.p->cachePtr = RNIL;
+ apiConnectptr.p->nextApiConnect = apiConnectptr.i + 1;
+ apiConnectptr.p->ndbapiBlockref = 0xFFFFFFFF; // Invalid ref
+ apiConnectptr.p->commitAckMarker = RNIL;
+ apiConnectptr.p->firstTcConnect = RNIL;
+ apiConnectptr.p->lastTcConnect = RNIL;
+ apiConnectptr.p->triggerPending = false;
+ apiConnectptr.p->isIndexOp = false;
+ apiConnectptr.p->accumulatingIndexOp = RNIL;
+ apiConnectptr.p->executingIndexOp = RNIL;
+ apiConnectptr.p->buddyPtr = RNIL;
+ apiConnectptr.p->currSavePointId = 0;
+ }//for
+ apiConnectptr.i = (2 * tiacTmp) - 1;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ apiConnectptr.p->nextApiConnect = RNIL;
+ cfirstfreeApiConnectCopy = tiacTmp;
+ guard4 = (3 * tiacTmp) - 1;
+ for (apiConnectptr.i = 2 * tiacTmp; apiConnectptr.i <= guard4;
+ apiConnectptr.i++) {
+ refresh_watch_dog();
+ jam();
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ apiConnectptr.p->apiFailState = ZFALSE;
+ apiConnectptr.p->apiConnectstate = CS_RESTART;
+ apiConnectptr.p->takeOverRec = (Uint8)Z8NIL;
+ apiConnectptr.p->cachePtr = RNIL;
+ apiConnectptr.p->nextApiConnect = apiConnectptr.i + 1;
+ apiConnectptr.p->ndbapiBlockref = 0xFFFFFFFF; // Invalid ref
+ apiConnectptr.p->commitAckMarker = RNIL;
+ apiConnectptr.p->firstTcConnect = RNIL;
+ apiConnectptr.p->lastTcConnect = RNIL;
+ apiConnectptr.p->triggerPending = false;
+ apiConnectptr.p->isIndexOp = false;
+ apiConnectptr.p->accumulatingIndexOp = RNIL;
+ apiConnectptr.p->executingIndexOp = RNIL;
+ apiConnectptr.p->buddyPtr = RNIL;
+ apiConnectptr.p->currSavePointId = 0;
+ }//for
+ apiConnectptr.i = (3 * tiacTmp) - 1;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ apiConnectptr.p->nextApiConnect = RNIL;
+ cfirstfreeApiConnectFail = 2 * tiacTmp;
+}//Dbtc::initApiConnect()
+
+void Dbtc::initattrbuf(Signal* signal)
+{
+ ndbrequire(cattrbufFilesize > 0);
+ for (attrbufptr.i = 0; attrbufptr.i < cattrbufFilesize; attrbufptr.i++) {
+ refresh_watch_dog();
+ jam();
+ ptrAss(attrbufptr, attrbufRecord);
+ attrbufptr.p->attrbuf[ZINBUF_NEXT] = attrbufptr.i + 1; /* NEXT ATTRBUF */
+ }//for
+ attrbufptr.i = cattrbufFilesize - 1;
+ ptrAss(attrbufptr, attrbufRecord);
+ attrbufptr.p->attrbuf[ZINBUF_NEXT] = RNIL; /* NEXT ATTRBUF */
+ cfirstfreeAttrbuf = 0;
+}//Dbtc::initattrbuf()
+
+void Dbtc::initdatabuf(Signal* signal)
+{
+ ndbrequire(cdatabufFilesize > 0);
+ for (databufptr.i = 0; databufptr.i < cdatabufFilesize; databufptr.i++) {
+ refresh_watch_dog();
+ ptrAss(databufptr, databufRecord);
+ databufptr.p->nextDatabuf = databufptr.i + 1;
+ }//for
+ databufptr.i = cdatabufFilesize - 1;
+ ptrCheckGuard(databufptr, cdatabufFilesize, databufRecord);
+ databufptr.p->nextDatabuf = RNIL;
+ cfirstfreeDatabuf = 0;
+}//Dbtc::initdatabuf()
+
+void Dbtc::initgcp(Signal* signal)
+{
+ ndbrequire(cgcpFilesize > 0);
+ for (gcpPtr.i = 0; gcpPtr.i < cgcpFilesize; gcpPtr.i++) {
+ ptrAss(gcpPtr, gcpRecord);
+ gcpPtr.p->nextGcp = gcpPtr.i + 1;
+ }//for
+ gcpPtr.i = cgcpFilesize - 1;
+ ptrCheckGuard(gcpPtr, cgcpFilesize, gcpRecord);
+ gcpPtr.p->nextGcp = RNIL;
+ cfirstfreeGcp = 0;
+ cfirstgcp = RNIL;
+ clastgcp = RNIL;
+}//Dbtc::initgcp()
+
+void Dbtc::inithost(Signal* signal)
+{
+ cpackedListIndex = 0;
+ ndbrequire(chostFilesize > 0);
+ for (hostptr.i = 0; hostptr.i < chostFilesize; hostptr.i++) {
+ jam();
+ ptrAss(hostptr, hostRecord);
+ hostptr.p->hostStatus = HS_DEAD;
+ hostptr.p->inPackedList = false;
+ hostptr.p->takeOverStatus = TOS_NOT_DEFINED;
+ hostptr.p->lqhTransStatus = LTS_IDLE;
+ hostptr.p->noOfWordsTCKEYCONF = 0;
+ hostptr.p->noOfWordsTCINDXCONF = 0;
+ hostptr.p->noOfPackedWordsLqh = 0;
+ hostptr.p->hostLqhBlockRef = calcLqhBlockRef(hostptr.i);
+ }//for
+}//Dbtc::inithost()
+
+void Dbtc::initialiseRecordsLab(Signal* signal, UintR Tdata0,
+ Uint32 retRef, Uint32 retData)
+{
+ switch (Tdata0) {
+ case 0:
+ jam();
+ initApiConnect(signal);
+ break;
+ case 1:
+ jam();
+ initattrbuf(signal);
+ break;
+ case 2:
+ jam();
+ initdatabuf(signal);
+ break;
+ case 3:
+ jam();
+ initgcp(signal);
+ break;
+ case 4:
+ jam();
+ inithost(signal);
+ break;
+ case 5:
+ jam();
+ // UNUSED Free to initialise something
+ break;
+ case 6:
+ jam();
+ initTable(signal);
+ break;
+ case 7:
+ jam();
+ initialiseScanrec(signal);
+ break;
+ case 8:
+ jam();
+ initialiseScanOprec(signal);
+ break;
+ case 9:
+ jam();
+ initialiseScanFragrec(signal);
+ break;
+ case 10:
+ jam();
+ initialiseTcConnect(signal);
+ break;
+ case 11:
+ jam();
+ initTcFail(signal);
+
+ {
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = retData;
+ sendSignal(retRef, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+ }
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+
+ signal->theData[0] = TcContinueB::ZINITIALISE_RECORDS;
+ signal->theData[1] = Tdata0 + 1;
+ signal->theData[2] = 0;
+ signal->theData[3] = retRef;
+ signal->theData[4] = retData;
+ sendSignal(DBTC_REF, GSN_CONTINUEB, signal, 5, JBB);
+}
+
+/* ========================================================================= */
+/* ======= INITIALISE_SCANREC ======= */
+/* */
+/* ========================================================================= */
+void Dbtc::initialiseScanrec(Signal* signal)
+{
+ ScanRecordPtr scanptr;
+ ndbrequire(cscanrecFileSize > 0);
+ for (scanptr.i = 0; scanptr.i < cscanrecFileSize; scanptr.i++) {
+ refresh_watch_dog();
+ jam();
+ ptrAss(scanptr, scanRecord);
+ new (scanptr.p) ScanRecord();
+ scanptr.p->scanState = ScanRecord::IDLE;
+ scanptr.p->scanApiRec = RNIL;
+ scanptr.p->nextScan = scanptr.i + 1;
+ }//for
+ scanptr.i = cscanrecFileSize - 1;
+ ptrAss(scanptr, scanRecord);
+ scanptr.p->nextScan = RNIL;
+ cfirstfreeScanrec = 0;
+}//Dbtc::initialiseScanrec()
+
+void Dbtc::initialiseScanFragrec(Signal* signal)
+{
+}//Dbtc::initialiseScanFragrec()
+
+void Dbtc::initialiseScanOprec(Signal* signal)
+{
+}//Dbtc::initialiseScanOprec()
+
+void Dbtc::initTable(Signal* signal)
+{
+
+ ndbrequire(ctabrecFilesize > 0);
+ for (tabptr.i = 0; tabptr.i < ctabrecFilesize; tabptr.i++) {
+ refresh_watch_dog();
+ ptrAss(tabptr, tableRecord);
+ tabptr.p->currentSchemaVersion = 0;
+ tabptr.p->storedTable = true;
+ tabptr.p->tableType = 0;
+ tabptr.p->enabled = false;
+ tabptr.p->dropping = false;
+ tabptr.p->noOfKeyAttr = 0;
+ tabptr.p->hasCharAttr = 0;
+ tabptr.p->noOfDistrKeys = 0;
+ for (unsigned k = 0; k < MAX_ATTRIBUTES_IN_INDEX; k++) {
+ tabptr.p->keyAttr[k].attributeDescriptor = 0;
+ tabptr.p->keyAttr[k].charsetInfo = 0;
+ }
+ }//for
+}//Dbtc::initTable()
+
+void Dbtc::initialiseTcConnect(Signal* signal)
+{
+ ndbrequire(ctcConnectFilesize >= 2);
+
+ // Place half of tcConnectptr's in cfirstfreeTcConnectFail list
+ Uint32 titcTmp = ctcConnectFilesize / 2;
+ for (tcConnectptr.i = 0; tcConnectptr.i < titcTmp; tcConnectptr.i++) {
+ refresh_watch_dog();
+ jam();
+ ptrAss(tcConnectptr, tcConnectRecord);
+ tcConnectptr.p->tcConnectstate = OS_RESTART;
+ tcConnectptr.p->apiConnect = RNIL;
+ tcConnectptr.p->noOfNodes = 0;
+ tcConnectptr.p->nextTcConnect = tcConnectptr.i + 1;
+ }//for
+ tcConnectptr.i = titcTmp - 1;
+ ptrAss(tcConnectptr, tcConnectRecord);
+ tcConnectptr.p->nextTcConnect = RNIL;
+ cfirstfreeTcConnectFail = 0;
+
+ // Place other half in cfirstfreeTcConnect list
+ for (tcConnectptr.i = titcTmp; tcConnectptr.i < ctcConnectFilesize;
+ tcConnectptr.i++) {
+ refresh_watch_dog();
+ jam();
+ ptrAss(tcConnectptr, tcConnectRecord);
+ tcConnectptr.p->tcConnectstate = OS_RESTART;
+ tcConnectptr.p->apiConnect = RNIL;
+ tcConnectptr.p->noOfNodes = 0;
+ tcConnectptr.p->nextTcConnect = tcConnectptr.i + 1;
+ }//for
+ tcConnectptr.i = ctcConnectFilesize - 1;
+ ptrAss(tcConnectptr, tcConnectRecord);
+ tcConnectptr.p->nextTcConnect = RNIL;
+ cfirstfreeTcConnect = titcTmp;
+ c_counters.cconcurrentOp = 0;
+}//Dbtc::initialiseTcConnect()
+
+/* ------------------------------------------------------------------------- */
+/* ---- LINK A GLOBAL CHECKPOINT RECORD INTO THE LIST WITH TRANSACTIONS */
+/* WAITING FOR COMPLETION. */
+/* ------------------------------------------------------------------------- */
+void Dbtc::linkGciInGcilist(Signal* signal)
+{
+ GcpRecordPtr tmpGcpPointer;
+ if (cfirstgcp == RNIL) {
+ jam();
+ cfirstgcp = gcpPtr.i;
+ } else {
+ jam();
+ tmpGcpPointer.i = clastgcp;
+ ptrCheckGuard(tmpGcpPointer, cgcpFilesize, gcpRecord);
+ tmpGcpPointer.p->nextGcp = gcpPtr.i;
+ }//if
+ clastgcp = gcpPtr.i;
+}//Dbtc::linkGciInGcilist()
+
+/* ------------------------------------------------------------------------- */
+/* ------- LINK SECONDARY KEY BUFFER IN OPERATION RECORD ------- */
+/* ------------------------------------------------------------------------- */
+void Dbtc::linkKeybuf(Signal* signal)
+{
+ seizeDatabuf(signal);
+ tmpDatabufptr.i = cachePtr.p->lastKeybuf;
+ cachePtr.p->lastKeybuf = databufptr.i;
+ if (tmpDatabufptr.i == RNIL) {
+ jam();
+ cachePtr.p->firstKeybuf = databufptr.i;
+ } else {
+ jam();
+ ptrCheckGuard(tmpDatabufptr, cdatabufFilesize, databufRecord);
+ tmpDatabufptr.p->nextDatabuf = databufptr.i;
+ }//if
+}//Dbtc::linkKeybuf()
+
+/* ------------------------------------------------------------------------- */
+/* ------- LINK A TC CONNECT RECORD INTO THE API LIST OF TC CONNECTIONS --- */
+/* ------------------------------------------------------------------------- */
+void Dbtc::linkTcInConnectionlist(Signal* signal)
+{
+ /* POINTER FOR THE CONNECT_RECORD */
+ TcConnectRecordPtr ltcTcConnectptr;
+
+ tcConnectptr.p->nextTcConnect = RNIL;
+ ltcTcConnectptr.i = apiConnectptr.p->lastTcConnect;
+ ptrCheck(ltcTcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ apiConnectptr.p->lastTcConnect = tcConnectptr.i;
+ if (ltcTcConnectptr.i == RNIL) {
+ jam();
+ apiConnectptr.p->firstTcConnect = tcConnectptr.i;
+ } else {
+ jam();
+ ptrGuard(ltcTcConnectptr);
+ ltcTcConnectptr.p->nextTcConnect = tcConnectptr.i;
+ }//if
+}//Dbtc::linkTcInConnectionlist()
+
+/*---------------------------------------------------------------------------*/
+/* RELEASE_ABORT_RESOURCES */
+/* THIS CODE RELEASES ALL RESOURCES AFTER AN ABORT OF A TRANSACTION AND ALSO */
+/* SENDS THE ABORT DECISION TO THE APPLICATION. */
+/*---------------------------------------------------------------------------*/
+void Dbtc::releaseAbortResources(Signal* signal)
+{
+ TcConnectRecordPtr rarTcConnectptr;
+
+ c_counters.cabortCount++;
+ if (apiConnectptr.p->cachePtr != RNIL) {
+ cachePtr.i = apiConnectptr.p->cachePtr;
+ ptrCheckGuard(cachePtr, ccacheFilesize, cacheRecord);
+ releaseAttrinfo();
+ releaseKeys();
+ }//if
+ tcConnectptr.i = apiConnectptr.p->firstTcConnect;
+ while (tcConnectptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ // Clear any markers that were set in CS_RECEIVING state
+ clearCommitAckMarker(apiConnectptr.p, tcConnectptr.p);
+ rarTcConnectptr.i = tcConnectptr.p->nextTcConnect;
+ releaseTcCon();
+ tcConnectptr.i = rarTcConnectptr.i;
+ }//while
+ apiConnectptr.p->firstTcConnect = RNIL;
+ apiConnectptr.p->lastTcConnect = RNIL;
+
+ // MASV let state be CS_ABORTING until all
+ // signals in the "air" have been received. Reset to CS_CONNECTED
+ // will be done when a TCKEYREQ with start flag is recieved
+ // or releaseApiCon is called
+ // apiConnectptr.p->apiConnectstate = CS_CONNECTED;
+ apiConnectptr.p->apiConnectstate = CS_ABORTING;
+ apiConnectptr.p->abortState = AS_IDLE;
+
+ if(apiConnectptr.p->m_exec_flag || apiConnectptr.p->apiFailState == ZTRUE){
+ jam();
+ bool ok = false;
+ Uint32 blockRef = apiConnectptr.p->ndbapiBlockref;
+ ReturnSignal ret = apiConnectptr.p->returnsignal;
+ apiConnectptr.p->returnsignal = RS_NO_RETURN;
+ apiConnectptr.p->m_exec_flag = 0;
+ switch(ret){
+ case RS_TCROLLBACKCONF:
+ jam();
+ ok = true;
+ signal->theData[0] = apiConnectptr.p->ndbapiConnect;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ sendSignal(blockRef, GSN_TCROLLBACKCONF, signal, 3, JBB);
+ break;
+ case RS_TCROLLBACKREP:{
+ jam();
+ ok = true;
+ TcRollbackRep * const tcRollbackRep =
+ (TcRollbackRep *) signal->getDataPtr();
+
+ tcRollbackRep->connectPtr = apiConnectptr.p->ndbapiConnect;
+ tcRollbackRep->transId[0] = apiConnectptr.p->transid[0];
+ tcRollbackRep->transId[1] = apiConnectptr.p->transid[1];
+ tcRollbackRep->returnCode = apiConnectptr.p->returncode;
+ sendSignal(blockRef, GSN_TCROLLBACKREP, signal,
+ TcRollbackRep::SignalLength, JBB);
+ }
+ break;
+ case RS_NO_RETURN:
+ jam();
+ ok = true;
+ break;
+ case RS_TCKEYCONF:
+ case RS_TC_COMMITCONF:
+ break;
+ }
+ if(!ok){
+ jam();
+ ndbout_c("returnsignal = %d", apiConnectptr.p->returnsignal);
+ sendSystemError(signal);
+ }//if
+
+ }
+ setApiConTimer(apiConnectptr.i, 0,
+ 100000+c_apiConTimer_line[apiConnectptr.i]);
+ if (apiConnectptr.p->apiFailState == ZTRUE) {
+ jam();
+ handleApiFailState(signal, apiConnectptr.i);
+ return;
+ }//if
+}//Dbtc::releaseAbortResources()
+
+void Dbtc::releaseApiCon(Signal* signal, UintR TapiConnectPtr)
+{
+ ApiConnectRecordPtr TlocalApiConnectptr;
+
+ TlocalApiConnectptr.i = TapiConnectPtr;
+ ptrCheckGuard(TlocalApiConnectptr, capiConnectFilesize, apiConnectRecord);
+ TlocalApiConnectptr.p->nextApiConnect = cfirstfreeApiConnect;
+ cfirstfreeApiConnect = TlocalApiConnectptr.i;
+ setApiConTimer(TlocalApiConnectptr.i, 0, __LINE__);
+ TlocalApiConnectptr.p->apiConnectstate = CS_DISCONNECTED;
+ ndbassert(TlocalApiConnectptr.p->apiScanRec == RNIL);
+ TlocalApiConnectptr.p->ndbapiBlockref = 0;
+}//Dbtc::releaseApiCon()
+
+void Dbtc::releaseApiConnectFail(Signal* signal)
+{
+ apiConnectptr.p->apiConnectstate = CS_RESTART;
+ apiConnectptr.p->takeOverRec = (Uint8)Z8NIL;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ apiConnectptr.p->nextApiConnect = cfirstfreeApiConnectFail;
+ cfirstfreeApiConnectFail = apiConnectptr.i;
+}//Dbtc::releaseApiConnectFail()
+
+void Dbtc::releaseGcp(Signal* signal)
+{
+ ptrGuard(gcpPtr);
+ gcpPtr.p->nextGcp = cfirstfreeGcp;
+ cfirstfreeGcp = gcpPtr.i;
+}//Dbtc::releaseGcp()
+
+void Dbtc::releaseKeys()
+{
+ UintR Tmp;
+ databufptr.i = cachePtr.p->firstKeybuf;
+ while (databufptr.i != RNIL) {
+ jam();
+ ptrCheckGuard(databufptr, cdatabufFilesize, databufRecord);
+ Tmp = databufptr.p->nextDatabuf;
+ databufptr.p->nextDatabuf = cfirstfreeDatabuf;
+ cfirstfreeDatabuf = databufptr.i;
+ databufptr.i = Tmp;
+ }//while
+ cachePtr.p->firstKeybuf = RNIL;
+ cachePtr.p->lastKeybuf = RNIL;
+}//Dbtc::releaseKeys()
+
+void Dbtc::releaseTcConnectFail(Signal* signal)
+{
+ ptrGuard(tcConnectptr);
+ tcConnectptr.p->nextTcConnect = cfirstfreeTcConnectFail;
+ cfirstfreeTcConnectFail = tcConnectptr.i;
+}//Dbtc::releaseTcConnectFail()
+
+void Dbtc::seizeApiConnect(Signal* signal)
+{
+ if (cfirstfreeApiConnect != RNIL) {
+ jam();
+ terrorCode = ZOK;
+ apiConnectptr.i = cfirstfreeApiConnect; /* ASSIGN A FREE RECORD FROM */
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ cfirstfreeApiConnect = apiConnectptr.p->nextApiConnect;
+ apiConnectptr.p->nextApiConnect = RNIL;
+ setApiConTimer(apiConnectptr.i, 0, __LINE__);
+ apiConnectptr.p->apiConnectstate = CS_CONNECTED; /* STATE OF CONNECTION */
+ apiConnectptr.p->triggerPending = false;
+ apiConnectptr.p->isIndexOp = false;
+ } else {
+ jam();
+ terrorCode = ZNO_FREE_API_CONNECTION;
+ }//if
+}//Dbtc::seizeApiConnect()
+
+void Dbtc::seizeApiConnectFail(Signal* signal)
+{
+ apiConnectptr.i = cfirstfreeApiConnectFail;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ cfirstfreeApiConnectFail = apiConnectptr.p->nextApiConnect;
+}//Dbtc::seizeApiConnectFail()
+
+void Dbtc::seizeDatabuf(Signal* signal)
+{
+ databufptr.i = cfirstfreeDatabuf;
+ ptrCheckGuard(databufptr, cdatabufFilesize, databufRecord);
+ cfirstfreeDatabuf = databufptr.p->nextDatabuf;
+ databufptr.p->nextDatabuf = RNIL;
+}//Dbtc::seizeDatabuf()
+
+void Dbtc::seizeTcConnect(Signal* signal)
+{
+ tcConnectptr.i = cfirstfreeTcConnect;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ cfirstfreeTcConnect = tcConnectptr.p->nextTcConnect;
+ c_counters.cconcurrentOp++;
+ tcConnectptr.p->isIndexOp = false;
+}//Dbtc::seizeTcConnect()
+
+void Dbtc::seizeTcConnectFail(Signal* signal)
+{
+ tcConnectptr.i = cfirstfreeTcConnectFail;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ cfirstfreeTcConnectFail = tcConnectptr.p->nextTcConnect;
+}//Dbtc::seizeTcConnectFail()
+
+void Dbtc::sendAttrinfo(Signal* signal,
+ UintR TattrinfoPtr,
+ AttrbufRecord * const regAttrPtr,
+ UintR TBref)
+{
+ UintR TdataPos;
+ UintR sig0, sig1, sig2, sig3, sig4, sig5, sig6, sig7;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ TdataPos = regAttrPtr->attrbuf[ZINBUF_DATA_LEN];
+ sig0 = TattrinfoPtr;
+ sig1 = regApiPtr->transid[0];
+ sig2 = regApiPtr->transid[1];
+
+ signal->theData[0] = sig0;
+ signal->theData[1] = sig1;
+ signal->theData[2] = sig2;
+
+ sig0 = regAttrPtr->attrbuf[0];
+ sig1 = regAttrPtr->attrbuf[1];
+ sig2 = regAttrPtr->attrbuf[2];
+ sig3 = regAttrPtr->attrbuf[3];
+ sig4 = regAttrPtr->attrbuf[4];
+ sig5 = regAttrPtr->attrbuf[5];
+ sig6 = regAttrPtr->attrbuf[6];
+ sig7 = regAttrPtr->attrbuf[7];
+
+ signal->theData[3] = sig0;
+ signal->theData[4] = sig1;
+ signal->theData[5] = sig2;
+ signal->theData[6] = sig3;
+ signal->theData[7] = sig4;
+ signal->theData[8] = sig5;
+ signal->theData[9] = sig6;
+ signal->theData[10] = sig7;
+
+ if (TdataPos > 8) {
+ sig0 = regAttrPtr->attrbuf[8];
+ sig1 = regAttrPtr->attrbuf[9];
+ sig2 = regAttrPtr->attrbuf[10];
+ sig3 = regAttrPtr->attrbuf[11];
+ sig4 = regAttrPtr->attrbuf[12];
+ sig5 = regAttrPtr->attrbuf[13];
+ sig6 = regAttrPtr->attrbuf[14];
+
+ jam();
+ signal->theData[11] = sig0;
+ signal->theData[12] = sig1;
+ signal->theData[13] = sig2;
+ signal->theData[14] = sig3;
+ signal->theData[15] = sig4;
+ signal->theData[16] = sig5;
+ signal->theData[17] = sig6;
+
+ if (TdataPos > 15) {
+
+ sig0 = regAttrPtr->attrbuf[15];
+ sig1 = regAttrPtr->attrbuf[16];
+ sig2 = regAttrPtr->attrbuf[17];
+ sig3 = regAttrPtr->attrbuf[18];
+ sig4 = regAttrPtr->attrbuf[19];
+ sig5 = regAttrPtr->attrbuf[20];
+ sig6 = regAttrPtr->attrbuf[21];
+
+ jam();
+ signal->theData[18] = sig0;
+ signal->theData[19] = sig1;
+ signal->theData[20] = sig2;
+ signal->theData[21] = sig3;
+ signal->theData[22] = sig4;
+ signal->theData[23] = sig5;
+ signal->theData[24] = sig6;
+ }//if
+ }//if
+ sendSignal(TBref, GSN_ATTRINFO, signal, TdataPos + 3, JBB);
+}//Dbtc::sendAttrinfo()
+
+void Dbtc::sendContinueTimeOutControl(Signal* signal, Uint32 TapiConPtr)
+{
+ signal->theData[0] = TcContinueB::ZCONTINUE_TIME_OUT_CONTROL;
+ signal->theData[1] = TapiConPtr;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+}//Dbtc::sendContinueTimeOutControl()
+
+void Dbtc::sendKeyinfo(Signal* signal, BlockReference TBRef, Uint32 len)
+{
+ signal->theData[0] = tcConnectptr.i;
+ signal->theData[1] = apiConnectptr.p->transid[0];
+ signal->theData[2] = apiConnectptr.p->transid[1];
+ signal->theData[3] = cdata[0];
+ signal->theData[4] = cdata[1];
+ signal->theData[5] = cdata[2];
+ signal->theData[6] = cdata[3];
+ signal->theData[7] = cdata[4];
+ signal->theData[8] = cdata[5];
+ signal->theData[9] = cdata[6];
+ signal->theData[10] = cdata[7];
+ signal->theData[11] = cdata[8];
+ signal->theData[12] = cdata[9];
+ signal->theData[13] = cdata[10];
+ signal->theData[14] = cdata[11];
+ signal->theData[15] = cdata[12];
+ signal->theData[16] = cdata[13];
+ signal->theData[17] = cdata[14];
+ signal->theData[18] = cdata[15];
+ signal->theData[19] = cdata[16];
+ signal->theData[20] = cdata[17];
+ signal->theData[21] = cdata[18];
+ signal->theData[22] = cdata[19];
+ sendSignal(TBRef, GSN_KEYINFO, signal, 3 + len, JBB);
+}//Dbtc::sendKeyinfo()
+
+void Dbtc::sendSystemError(Signal* signal)
+{
+ progError(0, 0);
+}//Dbtc::sendSystemError()
+
+/* ========================================================================= */
+/* ------- LINK ACTUAL GCP OUT OF LIST ------- */
+/* ------------------------------------------------------------------------- */
+void Dbtc::unlinkGcp(Signal* signal)
+{
+ if (cfirstgcp == gcpPtr.i) {
+ jam();
+ cfirstgcp = gcpPtr.p->nextGcp;
+ if (gcpPtr.i == clastgcp) {
+ jam();
+ clastgcp = RNIL;
+ }//if
+ } else {
+ jam();
+ /* --------------------------------------------------------------------
+ * WE ARE TRYING TO REMOVE A GLOBAL CHECKPOINT WHICH WAS NOT THE OLDEST.
+ * THIS IS A SYSTEM ERROR.
+ * ------------------------------------------------------------------- */
+ sendSystemError(signal);
+ }//if
+ gcpPtr.p->nextGcp = cfirstfreeGcp;
+ cfirstfreeGcp = gcpPtr.i;
+}//Dbtc::unlinkGcp()
+
+void
+Dbtc::execDUMP_STATE_ORD(Signal* signal)
+{
+ DumpStateOrd * const dumpState = (DumpStateOrd *)&signal->theData[0];
+ if(signal->theData[0] == DumpStateOrd::CommitAckMarkersSize){
+ infoEvent("TC: m_commitAckMarkerPool: %d free size: %d",
+ m_commitAckMarkerPool.getNoOfFree(),
+ m_commitAckMarkerPool.getSize());
+ }
+ if(signal->theData[0] == DumpStateOrd::CommitAckMarkersDump){
+ infoEvent("TC: m_commitAckMarkerPool: %d free size: %d",
+ m_commitAckMarkerPool.getNoOfFree(),
+ m_commitAckMarkerPool.getSize());
+
+ CommitAckMarkerIterator iter;
+ for(m_commitAckMarkerHash.first(iter); iter.curr.i != RNIL;
+ m_commitAckMarkerHash.next(iter)){
+ infoEvent("CommitAckMarker: i = %d (0x%x, 0x%x)"
+ " Api: %d Lghs(%d): %d %d %d %d bucket = %d",
+ iter.curr.i,
+ iter.curr.p->transid1,
+ iter.curr.p->transid2,
+ iter.curr.p->apiNodeId,
+ iter.curr.p->noOfLqhs,
+ iter.curr.p->lqhNodeId[0],
+ iter.curr.p->lqhNodeId[1],
+ iter.curr.p->lqhNodeId[2],
+ iter.curr.p->lqhNodeId[3],
+ iter.bucket);
+ }
+ }
+ // Dump all ScanFragRecs
+ if (dumpState->args[0] == DumpStateOrd::TcDumpAllScanFragRec){
+ Uint32 recordNo = 0;
+ if (signal->getLength() == 1)
+ infoEvent("TC: Dump all ScanFragRec - size: %d",
+ cscanFragrecFileSize);
+ else if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ dumpState->args[0] = DumpStateOrd::TcDumpOneScanFragRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+
+ if (recordNo < cscanFragrecFileSize-1){
+ dumpState->args[0] = DumpStateOrd::TcDumpAllScanFragRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ }
+
+ // Dump one ScanFragRec
+ if (dumpState->args[0] == DumpStateOrd::TcDumpOneScanFragRec){
+ Uint32 recordNo = RNIL;
+ if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ if (recordNo >= cscanFragrecFileSize)
+ return;
+
+ ScanFragRecPtr sfp;
+ sfp.i = recordNo;
+ c_scan_frag_pool.getPtr(sfp);
+ infoEvent("Dbtc::ScanFragRec[%d]: state=%d fragid=%d",
+ sfp.i,
+ sfp.p->scanFragState,
+ sfp.p->scanFragId);
+ infoEvent(" nodeid=%d, timer=%d",
+ refToNode(sfp.p->lqhBlockref),
+ sfp.p->scanFragTimer);
+ }
+
+ // Dump all ScanRecords
+ if (dumpState->args[0] == DumpStateOrd::TcDumpAllScanRec){
+ Uint32 recordNo = 0;
+ if (signal->getLength() == 1)
+ infoEvent("TC: Dump all ScanRecord - size: %d",
+ cscanrecFileSize);
+ else if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ dumpState->args[0] = DumpStateOrd::TcDumpOneScanRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+
+ if (recordNo < cscanrecFileSize-1){
+ dumpState->args[0] = DumpStateOrd::TcDumpAllScanRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ }
+
+ // Dump all active ScanRecords
+ if (dumpState->args[0] == DumpStateOrd::TcDumpAllActiveScanRec){
+ Uint32 recordNo = 0;
+ if (signal->getLength() == 1)
+ infoEvent("TC: Dump active ScanRecord - size: %d",
+ cscanrecFileSize);
+ else if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ ScanRecordPtr sp;
+ sp.i = recordNo;
+ ptrAss(sp, scanRecord);
+ if (sp.p->scanState != ScanRecord::IDLE){
+ dumpState->args[0] = DumpStateOrd::TcDumpOneScanRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+ }
+
+ if (recordNo < cscanrecFileSize-1){
+ dumpState->args[0] = DumpStateOrd::TcDumpAllActiveScanRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ }
+
+ // Dump one ScanRecord
+ // and associated ScanFragRec and ApiConnectRecord
+ if (dumpState->args[0] == DumpStateOrd::TcDumpOneScanRec){
+ Uint32 recordNo = RNIL;
+ if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ if (recordNo >= cscanrecFileSize)
+ return;
+
+ ScanRecordPtr sp;
+ sp.i = recordNo;
+ ptrAss(sp, scanRecord);
+ infoEvent("Dbtc::ScanRecord[%d]: state=%d"
+ "nextfrag=%d, nofrag=%d",
+ sp.i,
+ sp.p->scanState,
+ sp.p->scanNextFragId,
+ sp.p->scanNoFrag);
+ infoEvent(" ailen=%d, para=%d, receivedop=%d, noOprePperFrag=%d",
+ sp.p->scanAiLength,
+ sp.p->scanParallel,
+ sp.p->scanReceivedOperations,
+ sp.p->batch_size_rows);
+ infoEvent(" schv=%d, tab=%d, sproc=%d",
+ sp.p->scanSchemaVersion,
+ sp.p->scanTableref,
+ sp.p->scanStoredProcId);
+ infoEvent(" apiRec=%d, next=%d",
+ sp.p->scanApiRec, sp.p->nextScan);
+
+ if (sp.p->scanState != ScanRecord::IDLE){
+ // Request dump of ScanFragRec
+ ScanFragRecPtr sfptr;
+#define DUMP_SFR(x){\
+ ScanFragList list(c_scan_frag_pool, x);\
+ for(list.first(sfptr); !sfptr.isNull(); list.next(sfptr)){\
+ dumpState->args[0] = DumpStateOrd::TcDumpOneScanFragRec; \
+ dumpState->args[1] = sfptr.i;\
+ execDUMP_STATE_ORD(signal);\
+ }}
+
+ DUMP_SFR(sp.p->m_running_scan_frags);
+ DUMP_SFR(sp.p->m_queued_scan_frags);
+ DUMP_SFR(sp.p->m_delivered_scan_frags);
+
+ // Request dump of ApiConnectRecord
+ dumpState->args[0] = DumpStateOrd::TcDumpOneApiConnectRec;
+ dumpState->args[1] = sp.p->scanApiRec;
+ execDUMP_STATE_ORD(signal);
+ }
+
+ }
+
+ // Dump all ApiConnectRecord(s)
+ if (dumpState->args[0] == DumpStateOrd::TcDumpAllApiConnectRec){
+ Uint32 recordNo = 0;
+ if (signal->getLength() == 1)
+ infoEvent("TC: Dump all ApiConnectRecord - size: %d",
+ capiConnectFilesize);
+ else if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ dumpState->args[0] = DumpStateOrd::TcDumpOneApiConnectRec;
+ dumpState->args[1] = recordNo;
+ execDUMP_STATE_ORD(signal);
+
+ if (recordNo < capiConnectFilesize-1){
+ dumpState->args[0] = DumpStateOrd::TcDumpAllApiConnectRec;
+ dumpState->args[1] = recordNo+1;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ }
+ }
+
+ // Dump one ApiConnectRecord
+ if (dumpState->args[0] == DumpStateOrd::TcDumpOneApiConnectRec){
+ Uint32 recordNo = RNIL;
+ if (signal->getLength() == 2)
+ recordNo = dumpState->args[1];
+ else
+ return;
+
+ if (recordNo >= capiConnectFilesize)
+ return;
+
+ ApiConnectRecordPtr ap;
+ ap.i = recordNo;
+ ptrAss(ap, apiConnectRecord);
+ infoEvent("Dbtc::ApiConnectRecord[%d]: state=%d, abortState=%d, "
+ "apiFailState=%d",
+ ap.i,
+ ap.p->apiConnectstate,
+ ap.p->abortState,
+ ap.p->apiFailState);
+ infoEvent(" transid(0x%x, 0x%x), apiBref=0x%x, scanRec=%d",
+ ap.p->transid[0],
+ ap.p->transid[1],
+ ap.p->ndbapiBlockref,
+ ap.p->apiScanRec);
+ infoEvent(" ctcTimer=%d, apiTimer=%d, counter=%d, retcode=%d, "
+ "retsig=%d",
+ ctcTimer, getApiConTimer(ap.i),
+ ap.p->counter,
+ ap.p->returncode,
+ ap.p->returnsignal);
+ infoEvent(" lqhkeyconfrec=%d, lqhkeyreqrec=%d, "
+ "tckeyrec=%d",
+ ap.p->lqhkeyconfrec,
+ ap.p->lqhkeyreqrec,
+ ap.p->tckeyrec);
+ infoEvent(" next=%d ",
+ ap.p->nextApiConnect);
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::TcSetTransactionTimeout){
+ jam();
+ if(signal->getLength() > 1){
+ set_timeout_value(signal->theData[1]);
+ }
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::TcSetApplTransactionTimeout){
+ jam();
+ if(signal->getLength() > 1){
+ set_appl_timeout_value(signal->theData[1]);
+ }
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::StartTcTimer){
+ c_counters.c_trans_status = TransCounters::Started;
+ c_counters.reset();
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::StopTcTimer){
+ c_counters.c_trans_status = TransCounters::Off;
+ Uint32 len = c_counters.report(signal);
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, len, JBB);
+ c_counters.reset();
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::StartPeriodicTcTimer){
+ c_counters.c_trans_status = TransCounters::Timer;
+ c_counters.reset();
+ signal->theData[0] = TcContinueB::ZTRANS_EVENT_REP;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 5000, 1);
+ }
+}//Dbtc::execDUMP_STATE_ORD()
+
+void Dbtc::execSET_VAR_REQ(Signal* signal)
+{
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+ int val = setVarReq->value();
+
+
+ switch (var) {
+
+ case TransactionInactiveTime:
+ jam();
+ set_appl_timeout_value(val);
+ break;
+ case TransactionDeadlockDetectionTimeout:
+ set_timeout_value(val);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case NoOfConcurrentProcessesHandleTakeover:
+ set_no_parallel_takeover(val);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ } // switch
+#endif
+}
+
+void Dbtc::execABORT_ALL_REQ(Signal* signal)
+{
+ jamEntry();
+ AbortAllReq * req = (AbortAllReq*)&signal->theData[0];
+ AbortAllRef * ref = (AbortAllRef*)&signal->theData[0];
+
+ const Uint32 senderData = req->senderData;
+ const BlockReference senderRef = req->senderRef;
+
+ if(getAllowStartTransaction() == true && !getNodeState().getSingleUserMode()){
+ jam();
+
+ ref->senderData = senderData;
+ ref->errorCode = AbortAllRef::InvalidState;
+ sendSignal(senderRef, GSN_ABORT_ALL_REF, signal,
+ AbortAllRef::SignalLength, JBB);
+ return;
+ }
+
+ if(c_abortRec.clientRef != 0){
+ jam();
+
+ ref->senderData = senderData;
+ ref->errorCode = AbortAllRef::AbortAlreadyInProgress;
+ sendSignal(senderRef, GSN_ABORT_ALL_REF, signal,
+ AbortAllRef::SignalLength, JBB);
+ return;
+ }
+
+ if(refToNode(senderRef) != getOwnNodeId()){
+ jam();
+
+ ref->senderData = senderData;
+ ref->errorCode = AbortAllRef::FunctionNotImplemented;
+ sendSignal(senderRef, GSN_ABORT_ALL_REF, signal,
+ AbortAllRef::SignalLength, JBB);
+ return;
+ }
+
+ c_abortRec.clientRef = senderRef;
+ c_abortRec.clientData = senderData;
+ c_abortRec.oldTimeOutValue = ctimeOutValue;
+
+ ctimeOutValue = 0;
+ const Uint32 sleepTime = (2 * 10 * ctimeOutCheckDelay + 199) / 200;
+
+ checkAbortAllTimeout(signal, (sleepTime == 0 ? 1 : sleepTime));
+}
+
+void Dbtc::checkAbortAllTimeout(Signal* signal, Uint32 sleepTime)
+{
+
+ ndbrequire(c_abortRec.clientRef != 0);
+
+ if(sleepTime > 0){
+ jam();
+
+ sleepTime -= 1;
+ signal->theData[0] = TcContinueB::ZWAIT_ABORT_ALL;
+ signal->theData[1] = sleepTime;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 200, 2);
+ return;
+ }
+
+ AbortAllConf * conf = (AbortAllConf*)&signal->theData[0];
+ conf->senderData = c_abortRec.clientData;
+ sendSignal(c_abortRec.clientRef, GSN_ABORT_ALL_CONF, signal,
+ AbortAllConf::SignalLength, JBB);
+
+ ctimeOutValue = c_abortRec.oldTimeOutValue;
+ c_abortRec.clientRef = 0;
+}
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ------------------ TRIGGER AND INDEX HANDLING ------------------ */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+void Dbtc::execCREATE_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ CreateTrigReq * const createTrigReq =
+ (CreateTrigReq *)&signal->theData[0];
+ TcDefinedTriggerData* triggerData;
+ DefinedTriggerPtr triggerPtr;
+ BlockReference sender = signal->senderBlockRef();
+
+ releaseSections(signal);
+
+ triggerPtr.i = createTrigReq->getTriggerId();
+ if (ERROR_INSERTED(8033) ||
+ !c_theDefinedTriggers.seizeId(triggerPtr,
+ createTrigReq->getTriggerId())) {
+ CLEAR_ERROR_INSERT_VALUE;
+ // Failed to allocate trigger record
+ CreateTrigRef * const createTrigRef =
+ (CreateTrigRef *)&signal->theData[0];
+
+ createTrigRef->setConnectionPtr(createTrigReq->getConnectionPtr());
+ createTrigRef->setErrorCode(CreateTrigRef::TooManyTriggers);
+ sendSignal(sender, GSN_CREATE_TRIG_REF,
+ signal, CreateTrigRef::SignalLength, JBB);
+ return;
+ }
+
+ triggerData = triggerPtr.p;
+ triggerData->triggerId = createTrigReq->getTriggerId();
+ triggerData->triggerType = createTrigReq->getTriggerType();
+ triggerData->triggerEvent = createTrigReq->getTriggerEvent();
+ triggerData->attributeMask = createTrigReq->getAttributeMask();
+ if (triggerData->triggerType == TriggerType::SECONDARY_INDEX)
+ triggerData->indexId = createTrigReq->getIndexId();
+ CreateTrigConf * const createTrigConf =
+ (CreateTrigConf *)&signal->theData[0];
+
+ createTrigConf->setConnectionPtr(createTrigReq->getConnectionPtr());
+ sendSignal(sender, GSN_CREATE_TRIG_CONF,
+ signal, CreateTrigConf::SignalLength, JBB);
+}
+
+
+void Dbtc::execDROP_TRIG_REQ(Signal* signal)
+{
+ jamEntry();
+ DropTrigReq * const dropTrigReq = (DropTrigReq *)&signal->theData[0];
+ BlockReference sender = signal->senderBlockRef();
+
+ if ((c_theDefinedTriggers.getPtr(dropTrigReq->getTriggerId())) == NULL) {
+ jam();
+ // Failed to find find trigger record
+ DropTrigRef * const dropTrigRef = (DropTrigRef *)&signal->theData[0];
+
+ dropTrigRef->setConnectionPtr(dropTrigReq->getConnectionPtr());
+ dropTrigRef->setErrorCode(DropTrigRef::TriggerNotFound);
+ sendSignal(sender, GSN_DROP_TRIG_REF,
+ signal, DropTrigRef::SignalLength, JBB);
+ return;
+ }
+
+ // Release trigger record
+ c_theDefinedTriggers.release(dropTrigReq->getTriggerId());
+
+ DropTrigConf * const dropTrigConf = (DropTrigConf *)&signal->theData[0];
+
+ dropTrigConf->setConnectionPtr(dropTrigReq->getConnectionPtr());
+ sendSignal(sender, GSN_DROP_TRIG_CONF,
+ signal, DropTrigConf::SignalLength, JBB);
+}
+
+void Dbtc::execCREATE_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ CreateIndxReq * const createIndxReq =
+ (CreateIndxReq *)signal->getDataPtr();
+ TcIndexData* indexData;
+ TcIndexDataPtr indexPtr;
+ BlockReference sender = signal->senderBlockRef();
+
+ if (ERROR_INSERTED(8034) ||
+ !c_theIndexes.seizeId(indexPtr, createIndxReq->getIndexId())) {
+ CLEAR_ERROR_INSERT_VALUE;
+ // Failed to allocate index record
+ CreateIndxRef * const createIndxRef =
+ (CreateIndxRef *)&signal->theData[0];
+
+ createIndxRef->setConnectionPtr(createIndxReq->getConnectionPtr());
+ createIndxRef->setErrorCode(CreateIndxRef::TooManyIndexes);
+ releaseSections(signal);
+ sendSignal(sender, GSN_CREATE_INDX_REF,
+ signal, CreateIndxRef::SignalLength, JBB);
+ return;
+ }
+ indexData = indexPtr.p;
+ // Indexes always start in state IS_BUILDING
+ // Will become IS_ONLINE in execALTER_INDX_REQ
+ indexData->indexState = IS_BUILDING;
+ indexData->indexId = indexPtr.i;
+ indexData->primaryTableId = createIndxReq->getTableId();
+
+ // So far need only attribute count
+ SegmentedSectionPtr ssPtr;
+ signal->getSection(ssPtr, CreateIndxReq::ATTRIBUTE_LIST_SECTION);
+ SimplePropertiesSectionReader r0(ssPtr, getSectionSegmentPool());
+ r0.reset(); // undo implicit first()
+ if (!r0.getWord(&indexData->attributeList.sz) ||
+ !r0.getWords(indexData->attributeList.id, indexData->attributeList.sz)) {
+ ndbrequire(false);
+ }
+ indexData->primaryKeyPos = indexData->attributeList.sz;
+
+ releaseSections(signal);
+
+ CreateIndxConf * const createIndxConf =
+ (CreateIndxConf *)&signal->theData[0];
+
+ createIndxConf->setConnectionPtr(createIndxReq->getConnectionPtr());
+ createIndxConf->setTableId(createIndxReq->getTableId());
+ createIndxConf->setIndexId(createIndxReq->getIndexId());
+ sendSignal(sender, GSN_CREATE_INDX_CONF,
+ signal, CreateIndxConf::SignalLength, JBB);
+}
+
+void Dbtc::execALTER_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ AlterIndxReq * const alterIndxReq = (AlterIndxReq *)signal->getDataPtr();
+ TcIndexData* indexData;
+ //BlockReference sender = signal->senderBlockRef();
+ BlockReference sender = (BlockReference) alterIndxReq->getUserRef();
+ Uint32 connectionPtr = alterIndxReq->getConnectionPtr();
+ AlterIndxReq::RequestType requestType = alterIndxReq->getRequestType();
+ Uint32 tableId = alterIndxReq->getTableId();
+ Uint32 indexId = alterIndxReq->getIndexId();
+ bool online = (alterIndxReq->getOnline() == 1) ? true : false;
+
+ if ((indexData = c_theIndexes.getPtr(indexId)) == NULL) {
+ jam();
+ // Failed to find index record
+ AlterIndxRef * const alterIndxRef =
+ (AlterIndxRef *)signal->getDataPtrSend();
+
+ alterIndxRef->setUserRef(reference());
+ alterIndxRef->setConnectionPtr(connectionPtr);
+ alterIndxRef->setRequestType(requestType);
+ alterIndxRef->setTableId(tableId);
+ alterIndxRef->setIndexId(indexId);
+ alterIndxRef->setErrorCode(AlterIndxRef::IndexNotFound);
+ alterIndxRef->setErrorLine(__LINE__);
+ alterIndxRef->setErrorNode(getOwnNodeId());
+ sendSignal(sender, GSN_ALTER_INDX_REF,
+ signal, AlterIndxRef::SignalLength, JBB);
+ return;
+ }
+ // Found index record, alter it's state
+ if (online) {
+ jam();
+ indexData->indexState = IS_ONLINE;
+ } else {
+ jam();
+ indexData->indexState = IS_BUILDING;
+ }//if
+ AlterIndxConf * const alterIndxConf =
+ (AlterIndxConf *)signal->getDataPtrSend();
+
+ alterIndxConf->setUserRef(reference());
+ alterIndxConf->setConnectionPtr(connectionPtr);
+ alterIndxConf->setRequestType(requestType);
+ alterIndxConf->setTableId(tableId);
+ alterIndxConf->setIndexId(indexId);
+ sendSignal(sender, GSN_ALTER_INDX_CONF,
+ signal, AlterIndxConf::SignalLength, JBB);
+}
+
+void Dbtc::execFIRE_TRIG_ORD(Signal* signal)
+{
+ jamEntry();
+ FireTrigOrd * const fireOrd = (FireTrigOrd *)signal->getDataPtr();
+ ApiConnectRecord *localApiConnectRecord = apiConnectRecord;
+ ApiConnectRecordPtr transPtr;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+ TcConnectRecordPtr opPtr;
+
+ /**
+ * TODO
+ * Check transid,
+ * Fix overload i.e invalid word count
+ */
+ TcFiredTriggerData key;
+ key.fireingOperation = fireOrd->getConnectionPtr();
+ key.nodeId = refToNode(signal->getSendersBlockRef());
+ FiredTriggerPtr trigPtr;
+ if(c_firedTriggerHash.find(trigPtr, key)){
+
+ c_firedTriggerHash.remove(trigPtr);
+
+ bool ok = trigPtr.p->keyValues.getSize() == fireOrd->m_noPrimKeyWords;
+ ok &= trigPtr.p->afterValues.getSize() == fireOrd->m_noAfterValueWords;
+ ok &= trigPtr.p->beforeValues.getSize() == fireOrd->m_noBeforeValueWords;
+ if(ok){
+ opPtr.i = key.fireingOperation;
+ ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
+ transPtr.i = opPtr.p->apiConnect;
+ transPtr.p = &localApiConnectRecord[transPtr.i];
+
+ opPtr.p->noReceivedTriggers++;
+ opPtr.p->triggerExecutionCount++;
+
+ // Insert fired trigger in execution queue
+ transPtr.p->theFiredTriggers.add(trigPtr);
+ if (opPtr.p->noReceivedTriggers == opPtr.p->noFiredTriggers) {
+ executeTriggers(signal, &transPtr);
+ }
+ return;
+ }
+ jam();
+ c_theFiredTriggerPool.release(trigPtr);
+ }
+ jam();
+ /**
+ * Failed to find record or invalid word counts
+ */
+ ndbrequire(false);
+}
+
+void Dbtc::execTRIG_ATTRINFO(Signal* signal)
+{
+ jamEntry();
+ TrigAttrInfo * const trigAttrInfo = (TrigAttrInfo *)signal->getDataPtr();
+ Uint32 attrInfoLength = signal->getLength() - TrigAttrInfo::StaticLength;
+ const Uint32 *src = trigAttrInfo->getData();
+ FiredTriggerPtr firedTrigPtr;
+
+ TcFiredTriggerData key;
+ key.fireingOperation = trigAttrInfo->getConnectionPtr();
+ key.nodeId = refToNode(signal->getSendersBlockRef());
+ if(!c_firedTriggerHash.find(firedTrigPtr, key)){
+ jam();
+ if(!c_firedTriggerHash.seize(firedTrigPtr)){
+ jam();
+ /**
+ * Will be handled when FIRE_TRIG_ORD arrives
+ */
+ ndbout_c("op: %d node: %d failed to seize",
+ key.fireingOperation, key.nodeId);
+ return;
+ }
+ ndbrequire(firedTrigPtr.p->keyValues.getSize() == 0 &&
+ firedTrigPtr.p->beforeValues.getSize() == 0 &&
+ firedTrigPtr.p->afterValues.getSize() == 0);
+
+ firedTrigPtr.p->nodeId = refToNode(signal->getSendersBlockRef());
+ firedTrigPtr.p->fireingOperation = key.fireingOperation;
+ firedTrigPtr.p->triggerId = trigAttrInfo->getTriggerId();
+ c_firedTriggerHash.add(firedTrigPtr);
+ }
+
+ AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
+ switch (trigAttrInfo->getAttrInfoType()) {
+ case(TrigAttrInfo::PRIMARY_KEY):
+ jam();
+ {
+ LocalDataBuffer<11> buf(pool, firedTrigPtr.p->keyValues);
+ buf.append(src, attrInfoLength);
+ }
+ break;
+ case(TrigAttrInfo::BEFORE_VALUES):
+ jam();
+ {
+ LocalDataBuffer<11> buf(pool, firedTrigPtr.p->beforeValues);
+ buf.append(src, attrInfoLength);
+ }
+ break;
+ case(TrigAttrInfo::AFTER_VALUES):
+ jam();
+ {
+ LocalDataBuffer<11> buf(pool, firedTrigPtr.p->afterValues);
+ buf.append(src, attrInfoLength);
+ }
+ break;
+ default:
+ ndbrequire(false);
+ }
+}
+
+void Dbtc::execDROP_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ DropIndxReq * const dropIndxReq = (DropIndxReq *)signal->getDataPtr();
+ TcIndexData* indexData;
+ BlockReference sender = signal->senderBlockRef();
+
+ if ((indexData = c_theIndexes.getPtr(dropIndxReq->getIndexId())) == NULL) {
+ jam();
+ // Failed to find index record
+ DropIndxRef * const dropIndxRef =
+ (DropIndxRef *)signal->getDataPtrSend();
+
+ dropIndxRef->setConnectionPtr(dropIndxReq->getConnectionPtr());
+ dropIndxRef->setErrorCode(DropIndxRef::IndexNotFound);
+ sendSignal(sender, GSN_DROP_INDX_REF,
+ signal, DropIndxRef::SignalLength, JBB);
+ return;
+ }
+ // Release index record
+ c_theIndexes.release(dropIndxReq->getIndexId());
+
+ DropIndxConf * const dropIndxConf =
+ (DropIndxConf *)signal->getDataPtrSend();
+
+ dropIndxConf->setConnectionPtr(dropIndxReq->getConnectionPtr());
+ sendSignal(sender, GSN_DROP_INDX_CONF,
+ signal, DropIndxConf::SignalLength, JBB);
+}
+
+void Dbtc::execTCINDXREQ(Signal* signal)
+{
+ jamEntry();
+
+ TcKeyReq * const tcIndxReq = (TcKeyReq *)signal->getDataPtr();
+ const UintR TapiIndex = tcIndxReq->apiConnectPtr;
+ Uint32 tcIndxRequestInfo = tcIndxReq->requestInfo;
+ Uint32 startFlag = tcIndxReq->getStartFlag(tcIndxRequestInfo);
+ Uint32 * dataPtr = &tcIndxReq->scanInfo;
+ Uint32 indexBufSize = 8; // Maximum for index in TCINDXREQ
+ Uint32 attrBufSize = 5; // Maximum for attrInfo in TCINDXREQ
+ ApiConnectRecordPtr transPtr;
+ transPtr.i = TapiIndex;
+ if (transPtr.i >= capiConnectFilesize) {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+ ptrAss(transPtr, apiConnectRecord);
+ ApiConnectRecord * const regApiPtr = transPtr.p;
+ // Seize index operation
+ TcIndexOperationPtr indexOpPtr;
+ if ((startFlag == 1) &&
+ (regApiPtr->apiConnectstate == CS_CONNECTED ||
+ (regApiPtr->apiConnectstate == CS_STARTED &&
+ regApiPtr->firstTcConnect == RNIL)) ||
+ (regApiPtr->apiConnectstate == CS_ABORTING &&
+ regApiPtr->abortState == AS_IDLE)) {
+ jam();
+ // This is a newly started transaction, clean-up
+ releaseAllSeizedIndexOperations(regApiPtr);
+ }//if
+ if (!seizeIndexOperation(regApiPtr, indexOpPtr)) {
+ jam();
+ // Failed to allocate index operation
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = tcIndxReq->senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4000;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ TcIndexOperation* indexOp = indexOpPtr.p;
+ indexOp->indexOpId = indexOpPtr.i;
+
+ // Save original signal
+ indexOp->tcIndxReq = *tcIndxReq;
+ indexOp->connectionIndex = TapiIndex;
+ regApiPtr->accumulatingIndexOp = indexOp->indexOpId;
+
+ // If operation is readTupleExclusive or updateTuple then read index
+ // table with exclusive lock
+ Uint32 indexLength = TcKeyReq::getKeyLength(tcIndxRequestInfo);
+ Uint32 attrLength = tcIndxReq->attrLen;
+ indexOp->expectedKeyInfo = indexLength;
+ Uint32 includedIndexLength = MIN(indexLength, indexBufSize);
+ indexOp->expectedAttrInfo = attrLength;
+ Uint32 includedAttrLength = MIN(attrLength, attrBufSize);
+ if (saveINDXKEYINFO(signal,
+ indexOp,
+ dataPtr,
+ includedIndexLength)) {
+ jam();
+ // We have received all we need
+ readIndexTable(signal, regApiPtr, indexOp);
+ return;
+ }
+ dataPtr += includedIndexLength;
+ if (saveINDXATTRINFO(signal,
+ indexOp,
+ dataPtr,
+ includedAttrLength)) {
+ jam();
+ // We have received all we need
+ readIndexTable(signal, regApiPtr, indexOp);
+ return;
+ }
+}
+
+
+void Dbtc::sendTcIndxConf(Signal* signal, UintR TcommitFlag)
+{
+ HostRecordPtr localHostptr;
+ ApiConnectRecord * const regApiPtr = apiConnectptr.p;
+ const UintR TopWords = (UintR)regApiPtr->tcindxrec;
+ localHostptr.i = refToNode(regApiPtr->ndbapiBlockref);
+ const Uint32 type = getNodeInfo(localHostptr.i).m_type;
+ const bool is_api = (type >= NodeInfo::API && type <= NodeInfo::REP);
+ const BlockNumber TblockNum = refToBlock(regApiPtr->ndbapiBlockref);
+ const Uint32 Tmarker = (regApiPtr->commitAckMarker == RNIL ? 0 : 1);
+ ptrAss(localHostptr, hostRecord);
+ UintR TcurrLen = localHostptr.p->noOfWordsTCINDXCONF;
+ UintR confInfo = 0;
+ TcIndxConf::setNoOfOperations(confInfo, (TopWords >> 1));
+ TcIndxConf::setCommitFlag(confInfo, TcommitFlag == 1);
+ TcIndxConf::setMarkerFlag(confInfo, Tmarker);
+ const UintR TpacketLen = 6 + TopWords;
+ regApiPtr->tcindxrec = 0;
+
+ if(TcommitFlag || (regApiPtr->lqhkeyreqrec == regApiPtr->lqhkeyconfrec)){
+ jam();
+ regApiPtr->m_exec_flag = 0;
+ }
+
+ if ((TpacketLen > 25) || !is_api){
+ TcIndxConf * const tcIndxConf = (TcIndxConf *)signal->getDataPtrSend();
+
+ jam();
+ tcIndxConf->apiConnectPtr = regApiPtr->ndbapiConnect;
+ tcIndxConf->gci = regApiPtr->globalcheckpointid;;
+ tcIndxConf->confInfo = confInfo;
+ tcIndxConf->transId1 = regApiPtr->transid[0];
+ tcIndxConf->transId2 = regApiPtr->transid[1];
+ copyFromToLen(&regApiPtr->tcIndxSendArray[0],
+ (UintR*)&tcIndxConf->operations,
+ (UintR)ZTCOPCONF_SIZE);
+ sendSignal(regApiPtr->ndbapiBlockref,
+ GSN_TCINDXCONF, signal, (TpacketLen - 1), JBB);
+ return;
+ } else if (((TcurrLen + TpacketLen) > 25) && (TcurrLen > 0)) {
+ jam();
+ sendPackedTCINDXCONF(signal, localHostptr.p, localHostptr.i);
+ TcurrLen = 0;
+ } else {
+ jam();
+ updatePackedList(signal, localHostptr.p, localHostptr.i);
+ }//if
+// -------------------------------------------------------------------------
+// The header contains the block reference of receiver plus the real signal
+// length - 3, since we have the real signal length plus one additional word
+// for the header we have to do - 4.
+// -------------------------------------------------------------------------
+ UintR Tpack0 = (TblockNum << 16) + (TpacketLen - 4);
+ UintR Tpack1 = regApiPtr->ndbapiConnect;
+ UintR Tpack2 = regApiPtr->globalcheckpointid;
+ UintR Tpack3 = confInfo;
+ UintR Tpack4 = regApiPtr->transid[0];
+ UintR Tpack5 = regApiPtr->transid[1];
+
+ localHostptr.p->noOfWordsTCINDXCONF = TcurrLen + TpacketLen;
+
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 0] = Tpack0;
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 1] = Tpack1;
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 2] = Tpack2;
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 3] = Tpack3;
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 4] = Tpack4;
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + 5] = Tpack5;
+
+ UintR Ti;
+ for (Ti = 6; Ti < TpacketLen; Ti++) {
+ localHostptr.p->packedWordsTCINDXCONF[TcurrLen + Ti] =
+ regApiPtr->tcIndxSendArray[Ti - 6];
+ }//for
+}//Dbtc::sendTcIndxConf()
+
+void Dbtc::execINDXKEYINFO(Signal* signal)
+{
+ jamEntry();
+ Uint32 keyInfoLength = signal->getLength() - IndxKeyInfo::HeaderLength;
+ IndxKeyInfo * const indxKeyInfo = (IndxKeyInfo *)signal->getDataPtr();
+ const Uint32 *src = indxKeyInfo->getData();
+ const UintR TconnectIndex = indxKeyInfo->connectPtr;
+ ApiConnectRecordPtr transPtr;
+ transPtr.i = TconnectIndex;
+ if (transPtr.i >= capiConnectFilesize) {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+ ptrAss(transPtr, apiConnectRecord);
+ ApiConnectRecord * const regApiPtr = transPtr.p;
+ TcIndexOperationPtr indexOpPtr;
+ TcIndexOperation* indexOp;
+
+ indexOpPtr.i = regApiPtr->accumulatingIndexOp;
+ indexOp = c_theIndexOperations.getPtr(indexOpPtr.i);
+ if (saveINDXKEYINFO(signal,
+ indexOp,
+ src,
+ keyInfoLength)) {
+ jam();
+ // We have received all we need
+ readIndexTable(signal, regApiPtr, indexOp);
+ }
+}
+
+void Dbtc::execINDXATTRINFO(Signal* signal)
+{
+ jamEntry();
+ Uint32 attrInfoLength = signal->getLength() - IndxAttrInfo::HeaderLength;
+ IndxAttrInfo * const indxAttrInfo = (IndxAttrInfo *)signal->getDataPtr();
+ const Uint32 *src = indxAttrInfo->getData();
+ const UintR TconnectIndex = indxAttrInfo->connectPtr;
+ ApiConnectRecordPtr transPtr;
+ transPtr.i = TconnectIndex;
+ if (transPtr.i >= capiConnectFilesize) {
+ jam();
+ warningHandlerLab(signal);
+ return;
+ }//if
+ ptrAss(transPtr, apiConnectRecord);
+ ApiConnectRecord * const regApiPtr = transPtr.p;
+ TcIndexOperationPtr indexOpPtr;
+ TcIndexOperation* indexOp;
+
+ indexOpPtr.i = regApiPtr->accumulatingIndexOp;
+ indexOp = c_theIndexOperations.getPtr(indexOpPtr.i);
+ if (saveINDXATTRINFO(signal,
+ indexOp,
+ src,
+ attrInfoLength)) {
+ jam();
+ // We have received all we need
+ readIndexTable(signal, regApiPtr, indexOp);
+ }
+}
+
+/**
+ * Save signal INDXKEYINFO
+ * Return true if we have received all needed data
+ */
+bool Dbtc::saveINDXKEYINFO(Signal* signal,
+ TcIndexOperation* indexOp,
+ const Uint32 *src,
+ Uint32 len)
+{
+ if (!indexOp->keyInfo.append(src, len)) {
+ jam();
+ // Failed to seize keyInfo, abort transaction
+#ifdef VM_TRACE
+ ndbout_c("Dbtc::saveINDXKEYINFO: Failed to seize keyinfo\n");
+#endif
+ // Abort transaction
+ apiConnectptr.i = indexOp->connectionIndex;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ releaseIndexOperation(apiConnectptr.p, indexOp);
+ terrorCode = 4000;
+ abortErrorLab(signal);
+ return true;
+ }
+ if (receivedAllINDXKEYINFO(indexOp) && receivedAllINDXATTRINFO(indexOp)) {
+ jam();
+ return true;
+ }
+ return false;
+}
+
+bool Dbtc::receivedAllINDXKEYINFO(TcIndexOperation* indexOp)
+{
+ return (indexOp->keyInfo.getSize() == indexOp->expectedKeyInfo);
+}
+
+/**
+ * Save signal INDXATTRINFO
+ * Return true if we have received all needed data
+ */
+bool Dbtc::saveINDXATTRINFO(Signal* signal,
+ TcIndexOperation* indexOp,
+ const Uint32 *src,
+ Uint32 len)
+{
+ if (!indexOp->attrInfo.append(src, len)) {
+ jam();
+#ifdef VM_TRACE
+ ndbout_c("Dbtc::saveINDXATTRINFO: Failed to seize attrInfo\n");
+#endif
+ apiConnectptr.i = indexOp->connectionIndex;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ releaseIndexOperation(apiConnectptr.p, indexOp);
+ terrorCode = 4000;
+ abortErrorLab(signal);
+ return true;
+ }
+ if (receivedAllINDXKEYINFO(indexOp) && receivedAllINDXATTRINFO(indexOp)) {
+ jam();
+ return true;
+ }
+ return false;
+}
+
+bool Dbtc::receivedAllINDXATTRINFO(TcIndexOperation* indexOp)
+{
+ return (indexOp->attrInfo.getSize() == indexOp->expectedAttrInfo);
+}
+
+bool Dbtc::saveTRANSID_AI(Signal* signal,
+ TcIndexOperation* indexOp,
+ const Uint32 *src,
+ Uint32 len)
+{
+ Uint32 currentTransIdAILength = indexOp->transIdAI.getSize();
+
+ if (currentTransIdAILength == 0) {
+ jam();
+ // Read first AttributeHeader to get expected size
+ // of the single key attribute expected
+ AttributeHeader* head = (AttributeHeader *) src;
+ indexOp->expectedTransIdAI = head->getHeaderSize() + head->getDataSize();
+ }
+ if (!indexOp->transIdAI.append(src, len)) {
+ jam();
+#ifdef VM_TRACE
+ ndbout_c("Dbtc::saveTRANSID_AI: Failed to seize transIdAI\n");
+#endif
+ apiConnectptr.i = indexOp->connectionIndex;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ releaseIndexOperation(apiConnectptr.p, indexOp);
+ terrorCode = 4000;
+ abortErrorLab(signal);
+ return false;
+ }
+ return true;
+}
+
+bool Dbtc::receivedAllTRANSID_AI(TcIndexOperation* indexOp)
+{
+ return (indexOp->transIdAI.getSize() == indexOp->expectedTransIdAI);
+}
+
+/**
+ * Receive signal TCINDXCONF
+ * This can be either the return of reading an index table
+ * or performing an index operation
+ */
+void Dbtc::execTCKEYCONF(Signal* signal)
+{
+ TcKeyConf * const tcKeyConf = (TcKeyConf *)signal->getDataPtr();
+ TcIndexOperationPtr indexOpPtr;
+
+ jamEntry();
+ indexOpPtr.i = tcKeyConf->apiConnectPtr;
+ TcIndexOperation* indexOp = c_theIndexOperations.getPtr(indexOpPtr.i);
+ Uint32 confInfo = tcKeyConf->confInfo;
+
+ /**
+ * Check on TCKEYCONF wheater the the transaction was committed
+ */
+ Uint32 Tcommit = TcKeyConf::getCommitFlag(confInfo);
+
+ indexOpPtr.p = indexOp;
+ if (!indexOp) {
+ jam();
+ // Missing index operation
+ return;
+ }
+ const UintR TconnectIndex = indexOp->connectionIndex;
+ ApiConnectRecord * const regApiPtr = &apiConnectRecord[TconnectIndex];
+ apiConnectptr.p = regApiPtr;
+ apiConnectptr.i = TconnectIndex;
+ switch(indexOp->indexOpState) {
+ case(IOS_NOOP): {
+ jam();
+ // Should never happen, abort
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4349;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ case(IOS_INDEX_ACCESS): {
+ jam();
+ // Wait for TRANSID_AI
+ indexOp->indexOpState = IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI;
+ break;
+ }
+ case(IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI): {
+ jam();
+ // Double TCKEYCONF, should never happen, abort
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4349;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ case(IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF): {
+ jam();
+ // Continue with index operation
+ executeIndexOperation(signal, regApiPtr, indexOp);
+ break;
+ }
+ case(IOS_INDEX_OPERATION): {
+ // We are done, send TCINDXCONF
+ jam();
+ Uint32 Ttcindxrec = regApiPtr->tcindxrec;
+ // Copy reply from TcKeyConf
+
+ ndbassert(regApiPtr->noIndexOp);
+ regApiPtr->noIndexOp--; // Decrease count
+ regApiPtr->tcIndxSendArray[Ttcindxrec] = indexOp->tcIndxReq.senderData;
+ regApiPtr->tcIndxSendArray[Ttcindxrec + 1] =
+ tcKeyConf->operations[0].attrInfoLen;
+ regApiPtr->tcindxrec = Ttcindxrec + 2;
+ if (regApiPtr->noIndexOp == 0) {
+ jam();
+ sendTcIndxConf(signal, Tcommit);
+ } else if (regApiPtr->tcindxrec == ZTCOPCONF_SIZE) {
+ jam();
+ sendTcIndxConf(signal, 0);
+ }
+ releaseIndexOperation(regApiPtr, indexOp);
+ break;
+ }
+ }
+}
+
+void Dbtc::execTCKEYREF(Signal* signal)
+{
+ TcKeyRef * const tcKeyRef = (TcKeyRef *)signal->getDataPtr();
+ TcIndexOperationPtr indexOpPtr;
+
+ jamEntry();
+ indexOpPtr.i = tcKeyRef->connectPtr;
+ TcIndexOperation* indexOp = c_theIndexOperations.getPtr(indexOpPtr.i);
+ indexOpPtr.p = indexOp;
+ if (!indexOp) {
+ jam();
+ // Missing index operation
+ return;
+ }
+ const UintR TconnectIndex = indexOp->connectionIndex;
+ ApiConnectRecord * const regApiPtr = &apiConnectRecord[TconnectIndex];
+ Uint32 tcKeyRequestInfo = indexOp->tcIndxReq.requestInfo;
+ Uint32 commitFlg = TcKeyReq::getCommitFlag(tcKeyRequestInfo);
+
+ switch(indexOp->indexOpState) {
+ case(IOS_NOOP): {
+ jam();
+ // Should never happen, abort
+ break;
+ }
+ case(IOS_INDEX_ACCESS):
+ case(IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI):
+ case(IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF): {
+ jam();
+ // If we fail index access for a non-read operation during commit
+ // we abort transaction
+ if (commitFlg == 1) {
+ jam();
+ releaseIndexOperation(regApiPtr, indexOp);
+ apiConnectptr.i = indexOp->connectionIndex;
+ ptrCheckGuard(apiConnectptr, capiConnectFilesize, apiConnectRecord);
+ terrorCode = tcKeyRef->errorCode;
+ abortErrorLab(signal);
+ break;
+ }
+ /**
+ * Increase count as it will be decreased below...
+ * (and the code is written to handle failing lookup on "real" table
+ * not lookup on index table)
+ */
+ regApiPtr->noIndexOp++;
+ // else continue
+ }
+ case(IOS_INDEX_OPERATION): {
+ // Send TCINDXREF
+
+ jam();
+ TcKeyReq * const tcIndxReq = &indexOp->tcIndxReq;
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ ndbassert(regApiPtr->noIndexOp);
+ regApiPtr->noIndexOp--; // Decrease count
+ tcIndxRef->connectPtr = tcIndxReq->senderData;
+ tcIndxRef->transId[0] = tcKeyRef->transId[0];
+ tcIndxRef->transId[1] = tcKeyRef->transId[1];
+ tcIndxRef->errorCode = tcKeyRef->errorCode;
+ sendSignal(regApiPtr->ndbapiBlockref,
+ GSN_TCINDXREF, signal, TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ }
+}
+
+void Dbtc::execTRANSID_AI_R(Signal* signal){
+ TransIdAI * const transIdAI = (TransIdAI *)signal->getDataPtr();
+ Uint32 sigLen = signal->length();
+ Uint32 dataLen = sigLen - TransIdAI::HeaderLength - 1;
+ Uint32 recBlockref = transIdAI->attrData[dataLen];
+
+ jamEntry();
+
+ /**
+ * Forward signal to final destination
+ * Truncate last word since that was used to hold the final dest.
+ */
+ sendSignal(recBlockref, GSN_TRANSID_AI,
+ signal, sigLen - 1, JBB);
+}
+
+void Dbtc::execKEYINFO20_R(Signal* signal){
+ KeyInfo20 * const keyInfo = (KeyInfo20 *)signal->getDataPtr();
+ Uint32 sigLen = signal->length();
+ Uint32 dataLen = sigLen - KeyInfo20::HeaderLength - 1;
+ Uint32 recBlockref = keyInfo->keyData[dataLen];
+
+ jamEntry();
+
+ /**
+ * Forward signal to final destination
+ * Truncate last word since that was used to hold the final dest.
+ */
+ sendSignal(recBlockref, GSN_KEYINFO20,
+ signal, sigLen - 1, JBB);
+}
+
+
+void Dbtc::execTRANSID_AI(Signal* signal)
+{
+ TransIdAI * const transIdAI = (TransIdAI *)signal->getDataPtr();
+
+ jamEntry();
+ TcIndexOperationPtr indexOpPtr;
+ indexOpPtr.i = transIdAI->connectPtr;
+ TcIndexOperation* indexOp = c_theIndexOperations.getPtr(indexOpPtr.i);
+ indexOpPtr.p = indexOp;
+ if (!indexOp) {
+ jam();
+ // Missing index operation
+ }
+ const UintR TconnectIndex = indexOp->connectionIndex;
+ // ApiConnectRecord * const regApiPtr = &apiConnectRecord[TconnectIndex];
+ ApiConnectRecordPtr transPtr;
+
+ transPtr.i = TconnectIndex;
+ ptrCheckGuard(transPtr, capiConnectFilesize, apiConnectRecord);
+ ApiConnectRecord * const regApiPtr = transPtr.p;
+
+ // Acccumulate attribute data
+ if (!saveTRANSID_AI(signal,
+ indexOp,
+ transIdAI->getData(),
+ signal->getLength() - TransIdAI::HeaderLength)) {
+ jam();
+ // Failed to allocate space for TransIdAI
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4000;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+
+ switch(indexOp->indexOpState) {
+ case(IOS_NOOP): {
+ jam();
+ // Should never happen, abort
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4349;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ break;
+ }
+ case(IOS_INDEX_ACCESS): {
+ jam();
+ // Check if all TRANSID_AI have been received
+ if (receivedAllTRANSID_AI(indexOp)) {
+ jam();
+ // Wait for TRANSID_AI
+ indexOp->indexOpState = IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF;
+ }
+ break;
+ }
+ case(IOS_INDEX_ACCESS_WAIT_FOR_TCKEYCONF): {
+ jam();
+#ifdef VM_TRACE
+ ndbout_c("Dbtc::execTRANSID_AI: Too many TRANSID_AI, ignore for now\n");
+#endif
+ /*
+ // Too many TRANSID_AI
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndexRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4349;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ */
+ break;
+ }
+ case(IOS_INDEX_ACCESS_WAIT_FOR_TRANSID_AI): {
+ jam();
+ // Check if all TRANSID_AI have been received
+ if (receivedAllTRANSID_AI(indexOp)) {
+ jam();
+ // Continue with index operation
+ executeIndexOperation(signal, regApiPtr, indexOp);
+ }
+ // else continue waiting for more TRANSID_AI
+ break;
+ }
+ case(IOS_INDEX_OPERATION): {
+ // Should never receive TRANSID_AI in this state!!
+ jam();
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4349;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ }
+}
+
+void Dbtc::execTCROLLBACKREP(Signal* signal)
+{
+ TcRollbackRep* tcRollbackRep = (TcRollbackRep *)signal->getDataPtr();
+ jamEntry();
+ TcIndexOperationPtr indexOpPtr;
+ indexOpPtr.i = tcRollbackRep->connectPtr;
+ TcIndexOperation* indexOp = c_theIndexOperations.getPtr(indexOpPtr.i);
+ indexOpPtr.p = indexOp;
+ tcRollbackRep = (TcRollbackRep *)signal->getDataPtrSend();
+ tcRollbackRep->connectPtr = indexOp->tcIndxReq.senderData;
+ sendSignal(apiConnectptr.p->ndbapiBlockref,
+ GSN_TCROLLBACKREP, signal, TcRollbackRep::SignalLength, JBB);
+}
+
+/**
+ * Read index table with the index attributes as PK
+ */
+void Dbtc::readIndexTable(Signal* signal,
+ ApiConnectRecord* regApiPtr,
+ TcIndexOperation* indexOp)
+{
+ Uint32 keyBufSize = 8; // Maximum for key in TCKEYREQ
+ Uint32 dataPos = 0;
+ TcKeyReq * const tcKeyReq = (TcKeyReq *)signal->getDataPtrSend();
+ Uint32 * dataPtr = &tcKeyReq->scanInfo;
+ Uint32 tcKeyLength = TcKeyReq::StaticLength;
+ Uint32 tcKeyRequestInfo = indexOp->tcIndxReq.requestInfo;
+ AttributeBuffer::DataBufferIterator keyIter;
+ Uint32 keyLength = TcKeyReq::getKeyLength(tcKeyRequestInfo);
+ TcIndexData* indexData;
+ Uint32 transId1 = indexOp->tcIndxReq.transId1;
+ Uint32 transId2 = indexOp->tcIndxReq.transId2;
+
+ const Operation_t opType =
+ (Operation_t)TcKeyReq::getOperationType(tcKeyRequestInfo);
+
+ // Find index table
+ if ((indexData = c_theIndexes.getPtr(indexOp->tcIndxReq.tableId)) == NULL) {
+ jam();
+ // Failed to find index record
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4000;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ tcKeyReq->transId1 = transId1;
+ tcKeyReq->transId2 = transId2;
+ tcKeyReq->tableId = indexData->indexId;
+ tcKeyLength += MIN(keyLength, keyBufSize);
+ tcKeyReq->tableSchemaVersion = indexOp->tcIndxReq.tableSchemaVersion;
+ TcKeyReq::setOperationType(tcKeyRequestInfo,
+ opType == ZREAD ? ZREAD : ZREAD_EX);
+ TcKeyReq::setAIInTcKeyReq(tcKeyRequestInfo, 1); // Allways send one AttrInfo
+ TcKeyReq::setExecutingTrigger(tcKeyRequestInfo, 0);
+ BlockReference originalReceiver = regApiPtr->ndbapiBlockref;
+ regApiPtr->ndbapiBlockref = reference(); // Send result to me
+ tcKeyReq->senderData = indexOp->indexOpId;
+ indexOp->indexOpState = IOS_INDEX_ACCESS;
+ regApiPtr->executingIndexOp = regApiPtr->accumulatingIndexOp;
+ regApiPtr->accumulatingIndexOp = RNIL;
+ regApiPtr->isIndexOp = true;
+
+ Uint32 remainingKey = indexOp->keyInfo.getSize();
+ bool moreKeyData = indexOp->keyInfo.first(keyIter);
+ // *********** KEYINFO in TCKEYREQ ***********
+ while((dataPos < keyBufSize) &&
+ (remainingKey-- != 0)) {
+ *dataPtr++ = *keyIter.data;
+ dataPos++;
+ moreKeyData = indexOp->keyInfo.next(keyIter);
+ }
+ // *********** ATTRINFO in TCKEYREQ ***********
+ tcKeyReq->attrLen = 1; // Primary key is stored as one attribute
+ AttributeHeader::init(dataPtr, indexData->primaryKeyPos, 0);
+ tcKeyLength++;
+ tcKeyReq->requestInfo = tcKeyRequestInfo;
+
+ ndbassert(TcKeyReq::getDirtyFlag(tcKeyRequestInfo) == 0);
+ ndbassert(TcKeyReq::getSimpleFlag(tcKeyRequestInfo) == 0);
+ EXECUTE_DIRECT(DBTC, GSN_TCKEYREQ, signal, tcKeyLength);
+
+ /**
+ * "Fool" TC not to start commiting transaction since it always will
+ * have one outstanding lqhkeyreq
+ * This is later decreased when the index read is complete
+ */
+ regApiPtr->lqhkeyreqrec++;
+
+ /**
+ * Remember ptr to index read operation
+ * (used to set correct save point id on index operation later)
+ */
+ indexOp->indexReadTcConnect = regApiPtr->lastTcConnect;
+
+ jamEntry();
+ // *********** KEYINFO ***********
+ if (moreKeyData) {
+ jam();
+ // Send KEYINFO sequence
+ KeyInfo * const keyInfo = (KeyInfo *)signal->getDataPtrSend();
+
+ keyInfo->connectPtr = indexOp->tcIndxReq.apiConnectPtr;
+ keyInfo->transId[0] = transId1;
+ keyInfo->transId[1] = transId2;
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ while(remainingKey-- != 0) {// If we have not read complete key
+ *dataPtr++ = *keyIter.data;
+ dataPos++;
+ if (dataPos == KeyInfo::DataLength) {
+ // Flush KEYINFO
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength);
+ jamEntry();
+ dataPos = 0;
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ }
+ moreKeyData = indexOp->keyInfo.next(keyIter);
+ }
+ if (dataPos != 0) {
+ // Flush last KEYINFO
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + dataPos);
+ jamEntry();
+ }
+ }
+
+ regApiPtr->ndbapiBlockref = originalReceiver; // reset original receiver
+}
+
+/**
+ * Execute the index operation with the result from
+ * the index table read as PK
+ */
+void Dbtc::executeIndexOperation(Signal* signal,
+ ApiConnectRecord* regApiPtr,
+ TcIndexOperation* indexOp) {
+
+ Uint32 keyBufSize = 8; // Maximum for key in TCKEYREQ
+ Uint32 attrBufSize = 5;
+ Uint32 dataPos = 0;
+ TcKeyReq * const tcIndxReq = &indexOp->tcIndxReq;
+ TcKeyReq * const tcKeyReq = (TcKeyReq *)signal->getDataPtrSend();
+ Uint32 * dataPtr = &tcKeyReq->scanInfo;
+ Uint32 tcKeyLength = TcKeyReq::StaticLength;
+ Uint32 tcKeyRequestInfo = tcIndxReq->requestInfo;
+ TcIndexData* indexData;
+ AttributeBuffer::DataBufferIterator attrIter;
+ AttributeBuffer::DataBufferIterator aiIter;
+ bool moreKeyData = indexOp->transIdAI.first(aiIter);
+
+ // Find index table
+ if ((indexData = c_theIndexes.getPtr(tcIndxReq->tableId)) == NULL) {
+ jam();
+ // Failed to find index record
+ TcKeyRef * const tcIndxRef = (TcKeyRef *)signal->getDataPtrSend();
+
+ tcIndxRef->connectPtr = indexOp->tcIndxReq.senderData;
+ tcIndxRef->transId[0] = regApiPtr->transid[0];
+ tcIndxRef->transId[1] = regApiPtr->transid[1];
+ tcIndxRef->errorCode = 4349;
+ sendSignal(regApiPtr->ndbapiBlockref, GSN_TCINDXREF, signal,
+ TcKeyRef::SignalLength, JBB);
+ return;
+ }
+ // Find schema version of primary table
+ TableRecordPtr tabPtr;
+ tabPtr.i = indexData->primaryTableId;
+ ptrCheckGuard(tabPtr, ctabrecFilesize, tableRecord);
+
+ tcKeyReq->apiConnectPtr = tcIndxReq->apiConnectPtr;
+ tcKeyReq->attrLen = tcIndxReq->attrLen;
+ tcKeyReq->tableId = indexData->primaryTableId;
+ tcKeyReq->tableSchemaVersion = tabPtr.p->currentSchemaVersion;
+ tcKeyReq->transId1 = regApiPtr->transid[0];
+ tcKeyReq->transId2 = regApiPtr->transid[1];
+ tcKeyReq->senderData = tcIndxReq->senderData; // Needed for TRANSID_AI to API
+ indexOp->indexOpState = IOS_INDEX_OPERATION;
+ regApiPtr->isIndexOp = true;
+ regApiPtr->executingIndexOp = indexOp->indexOpId;;
+ regApiPtr->noIndexOp++; // Increase count
+
+ // Filter out AttributeHeader:s since this should not be in key
+ AttributeHeader* attrHeader = (AttributeHeader *) aiIter.data;
+
+ Uint32 headerSize = attrHeader->getHeaderSize();
+ Uint32 keySize = attrHeader->getDataSize();
+ TcKeyReq::setKeyLength(tcKeyRequestInfo, keySize);
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreKeyData = indexOp->transIdAI.next(aiIter);
+ } else {
+ jam();
+ moreKeyData = indexOp->transIdAI.next(aiIter, headerSize - 1);
+ }//if
+ while(// If we have not read complete key
+ (keySize != 0) &&
+ (dataPos < keyBufSize)) {
+ *dataPtr++ = *aiIter.data;
+ dataPos++;
+ keySize--;
+ moreKeyData = indexOp->transIdAI.next(aiIter);
+ }
+ tcKeyLength += dataPos;
+
+ Uint32 attributesLength = indexOp->attrInfo.getSize();
+ if (attributesLength <= attrBufSize) {
+ jam();
+ // ATTRINFO fits in TCKEYREQ
+ // Pack ATTRINFO IN TCKEYREQ
+ TcKeyReq::setAIInTcKeyReq(tcKeyRequestInfo, indexOp->attrInfo.getSize());
+ // Insert IndxAttrInfo
+ for(bool moreAttrData = indexOp->attrInfo.first(attrIter);
+ moreAttrData;
+ moreAttrData = indexOp->attrInfo.next(attrIter)) {
+ *dataPtr++ = *attrIter.data;
+ }
+ tcKeyLength += attributesLength;
+ } else {
+ jam();
+ // No ATTRINFO in TCKEYREQ
+ TcKeyReq::setAIInTcKeyReq(tcKeyRequestInfo, 0);
+ }
+
+ TcKeyReq::setCommitFlag(tcKeyRequestInfo, 0);
+ TcKeyReq::setExecuteFlag(tcKeyRequestInfo, 0);
+ TcKeyReq::setExecutingTrigger(tcKeyRequestInfo, 0);
+ tcKeyReq->requestInfo = tcKeyRequestInfo;
+
+ ndbassert(TcKeyReq::getDirtyFlag(tcKeyRequestInfo) == 0);
+ ndbassert(TcKeyReq::getSimpleFlag(tcKeyRequestInfo) == 0);
+
+ /**
+ * Decrease lqhkeyreqrec to compensate for addition
+ * during read of index table
+ * I.e. let TC start committing when other operations has completed
+ */
+ regApiPtr->lqhkeyreqrec--;
+
+ /**
+ * Fix savepoint id -
+ * fix so that index operation has the same savepoint id
+ * as the read of the index table (TCINDXREQ)
+ */
+ TcConnectRecordPtr tmp;
+ tmp.i = indexOp->indexReadTcConnect;
+ ptrCheckGuard(tmp, ctcConnectFilesize, tcConnectRecord);
+ const Uint32 currSavePointId = regApiPtr->currSavePointId;
+ regApiPtr->currSavePointId = tmp.p->savePointId;
+ EXECUTE_DIRECT(DBTC, GSN_TCKEYREQ, signal, tcKeyLength);
+ regApiPtr->currSavePointId = currSavePointId;
+
+ jamEntry();
+ // *********** KEYINFO ***********
+ if (moreKeyData) {
+ jam();
+ // Send KEYINFO sequence
+ KeyInfo * const keyInfo = (KeyInfo *)signal->getDataPtrSend();
+
+ keyInfo->connectPtr = indexOp->tcIndxReq.apiConnectPtr;
+ keyInfo->transId[0] = regApiPtr->transid[0];
+ keyInfo->transId[1] = regApiPtr->transid[1];
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ // Pack any part of a key attribute that did no fit TCKEYREQ
+ while(keySize-- != 0) {// If we have not read complete key
+ *dataPtr++ = *aiIter.data;
+ dataPos++;
+ if (dataPos == KeyInfo::DataLength) {
+ // Flush KEYINFO
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength);
+ jamEntry();
+ dataPos = 0;
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ }
+ moreKeyData = indexOp->transIdAI.next(aiIter);
+ }
+ if (dataPos != 0) {
+ // Flush last KEYINFO
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + dataPos);
+ jamEntry();
+ }
+ }
+
+ // *********** ATTRINFO ***********
+ if (attributesLength > attrBufSize) {
+ jam();
+ // No ATTRINFO in TcKeyReq
+ TcKeyReq::setAIInTcKeyReq(tcKeyReq->requestInfo, 0);
+ // Send ATTRINFO sequence
+ AttrInfo * const attrInfo = (AttrInfo *)signal->getDataPtrSend();
+ Uint32 attrInfoPos = 0;
+
+ attrInfo->connectPtr = indexOp->tcIndxReq.apiConnectPtr;
+ attrInfo->transId[0] = regApiPtr->transid[0];
+ attrInfo->transId[1] = regApiPtr->transid[1];
+ dataPtr = (Uint32 *) &attrInfo->attrData;
+
+
+ // Insert attribute values (insert key values of primary table)
+ for(bool moreAttrData = indexOp->attrInfo.first(attrIter);
+ moreAttrData;
+ moreAttrData = indexOp->attrInfo.next(attrIter)) {
+ *dataPtr++ = *attrIter.data;
+ attrInfoPos++;
+ if (attrInfoPos == AttrInfo::DataLength) {
+ // Flush ATTRINFO
+ EXECUTE_DIRECT(DBTC, GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + AttrInfo::DataLength);
+ jamEntry();
+ attrInfoPos = 0;
+ dataPtr = (Uint32 *) &attrInfo->attrData;
+ }
+ }
+ if (attrInfoPos != 0) {
+ // Send last ATTRINFO
+ EXECUTE_DIRECT(DBTC, GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + attrInfoPos);
+ jamEntry();
+ }
+ }
+}
+
+bool Dbtc::seizeIndexOperation(ApiConnectRecord* regApiPtr,
+ TcIndexOperationPtr& indexOpPtr)
+{
+ bool seizeOk;
+
+ seizeOk = c_theIndexOperations.seize(indexOpPtr);
+ if (seizeOk) {
+ jam();
+ TcSeizedIndexOperationPtr seizedIndexOpPtr;
+ seizeOk &= regApiPtr->theSeizedIndexOperations.seizeId(seizedIndexOpPtr,
+ indexOpPtr.i);
+ }
+ return seizeOk;
+}
+
+void Dbtc::releaseIndexOperation(ApiConnectRecord* regApiPtr,
+ TcIndexOperation* indexOp)
+{
+ indexOp->indexOpState = IOS_NOOP;
+ indexOp->expectedKeyInfo = 0;
+ indexOp->keyInfo.release();
+ indexOp->expectedAttrInfo = 0;
+ indexOp->attrInfo.release();
+ indexOp->expectedTransIdAI = 0;
+ indexOp->transIdAI.release();
+ regApiPtr->theSeizedIndexOperations.release(indexOp->indexOpId);
+ c_theIndexOperations.release(indexOp->indexOpId);
+}
+
+void Dbtc::releaseAllSeizedIndexOperations(ApiConnectRecord* regApiPtr)
+{
+ TcSeizedIndexOperationPtr seizedIndexOpPtr;
+
+ regApiPtr->theSeizedIndexOperations.first(seizedIndexOpPtr);
+ while(seizedIndexOpPtr.i != RNIL) {
+ jam();
+ TcIndexOperation* indexOp =
+ c_theIndexOperations.getPtr(seizedIndexOpPtr.i);
+
+ indexOp->indexOpState = IOS_NOOP;
+ indexOp->expectedKeyInfo = 0;
+ indexOp->keyInfo.release();
+ indexOp->expectedAttrInfo = 0;
+ indexOp->attrInfo.release();
+ indexOp->expectedTransIdAI = 0;
+ indexOp->transIdAI.release();
+ c_theIndexOperations.release(seizedIndexOpPtr.i);
+ regApiPtr->theSeizedIndexOperations.next(seizedIndexOpPtr);
+ }
+ regApiPtr->theSeizedIndexOperations.release();
+}
+
+void Dbtc::saveTriggeringOpState(Signal* signal, TcConnectRecord* trigOp)
+{
+ LqhKeyConf * lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+ copyFromToLen((UintR*)lqhKeyConf,
+ &trigOp->savedState[0],
+ LqhKeyConf::SignalLength);
+}
+
+void Dbtc::continueTriggeringOp(Signal* signal, TcConnectRecord* trigOp)
+{
+ LqhKeyConf * lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+ copyFromToLen(&trigOp->savedState[0],
+ (UintR*)lqhKeyConf,
+ LqhKeyConf::SignalLength);
+
+ lqhKeyConf->noFiredTriggers = 0;
+ trigOp->noReceivedTriggers = 0;
+
+ // All triggers executed successfully, continue operation
+ execLQHKEYCONF(signal);
+}
+
+void Dbtc::scheduleFiredTrigger(ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr)
+{
+ // Set initial values for trigger fireing operation
+ opPtr->p->triggerExecutionCount++;
+
+ // Insert fired trigger in execution queue
+ transPtr->p->theFiredTriggers.add(opPtr->p->accumulatingTriggerData);
+ opPtr->p->accumulatingTriggerData.i = RNIL;
+ opPtr->p->accumulatingTriggerData.p = NULL;
+}
+
+void Dbtc::executeTriggers(Signal* signal, ApiConnectRecordPtr* transPtr)
+{
+ ApiConnectRecord* regApiPtr = transPtr->p;
+ TcConnectRecord *localTcConnectRecord = tcConnectRecord;
+ TcConnectRecordPtr opPtr;
+ FiredTriggerPtr trigPtr;
+
+ if (!regApiPtr->theFiredTriggers.isEmpty()) {
+ jam();
+ if ((regApiPtr->apiConnectstate == CS_STARTED) ||
+ (regApiPtr->apiConnectstate == CS_START_COMMITTING)) {
+ jam();
+ regApiPtr->theFiredTriggers.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ jam();
+ // Execute all ready triggers in parallel
+ opPtr.i = trigPtr.p->fireingOperation;
+ ptrCheckGuard(opPtr, ctcConnectFilesize, localTcConnectRecord);
+ FiredTriggerPtr nextTrigPtr = trigPtr;
+ regApiPtr->theFiredTriggers.next(nextTrigPtr);
+ if (opPtr.p->noReceivedTriggers == opPtr.p->noFiredTriggers) {
+ jam();
+ // Fireing operation is ready to have a trigger executing
+ executeTrigger(signal, trigPtr.p, transPtr, &opPtr);
+ // Should allow for interleaving here by sending a CONTINUEB and
+ // return
+ // Release trigger records
+ AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
+ LocalDataBuffer<11> tmp1(pool, trigPtr.p->keyValues);
+ tmp1.release();
+ LocalDataBuffer<11> tmp2(pool, trigPtr.p->beforeValues);
+ tmp2.release();
+ LocalDataBuffer<11> tmp3(pool, trigPtr.p->afterValues);
+ tmp3.release();
+ regApiPtr->theFiredTriggers.release(trigPtr.i);
+ }
+ trigPtr = nextTrigPtr;
+ }
+ return;
+ // No more triggers, continue transaction after last executed trigger has
+ // reurned (in execLQHKEYCONF or execLQHKEYREF)
+ } else {
+ // Wait until transaction is ready to execute a trigger
+ jam();
+ if (!regApiPtr->triggerPending) {
+ jam();
+ regApiPtr->triggerPending = true;
+ signal->theData[0] = TcContinueB::TRIGGER_PENDING;
+ signal->theData[1] = transPtr->i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 3, JBB);
+ }
+ // else
+ // We are already waiting for a pending trigger (CONTINUEB)
+ }
+ }
+}
+
+void Dbtc::executeTrigger(Signal* signal,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr)
+{
+ TcDefinedTriggerData* definedTriggerData;
+
+ if ((definedTriggerData =
+ c_theDefinedTriggers.getPtr(firedTriggerData->triggerId))
+ != NULL) {
+ switch(definedTriggerData->triggerType) {
+ case(TriggerType::SECONDARY_INDEX):
+ jam();
+ executeIndexTrigger(signal, definedTriggerData, firedTriggerData,
+ transPtr, opPtr);
+ break;
+ default:
+ ndbrequire(false);
+ }
+ }
+}
+
+void Dbtc::executeIndexTrigger(Signal* signal,
+ TcDefinedTriggerData* definedTriggerData,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr)
+{
+ TcIndexData* indexData;
+
+ indexData = c_theIndexes.getPtr(definedTriggerData->indexId);
+ ndbassert(indexData != NULL);
+
+ switch (definedTriggerData->triggerEvent) {
+ case(TriggerEvent::TE_INSERT): {
+ jam();
+ insertIntoIndexTable(signal, firedTriggerData, transPtr, opPtr, indexData);
+ break;
+ }
+ case(TriggerEvent::TE_DELETE): {
+ jam();
+ deleteFromIndexTable(signal, firedTriggerData, transPtr, opPtr, indexData);
+ break;
+ }
+ case(TriggerEvent::TE_UPDATE): {
+ jam();
+ deleteFromIndexTable(signal, firedTriggerData, transPtr, opPtr,
+ indexData, true); // Hold the triggering operation
+ insertIntoIndexTable(signal, firedTriggerData, transPtr, opPtr, indexData);
+ break;
+ }
+ default:
+ ndbrequire(false);
+ }
+}
+
+void Dbtc::releaseFiredTriggerData(DLFifoList<TcFiredTriggerData>* triggers)
+{
+ FiredTriggerPtr trigPtr;
+
+ triggers->first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ jam();
+ // Release trigger records
+
+ AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
+ LocalDataBuffer<11> tmp1(pool, trigPtr.p->keyValues);
+ tmp1.release();
+ LocalDataBuffer<11> tmp2(pool, trigPtr.p->beforeValues);
+ tmp2.release();
+ LocalDataBuffer<11> tmp3(pool, trigPtr.p->afterValues);
+ tmp3.release();
+
+ triggers->next(trigPtr);
+ }
+ triggers->release();
+}
+
+void Dbtc::insertIntoIndexTable(Signal* signal,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr,
+ TcIndexData* indexData,
+ bool holdOperation)
+{
+ ApiConnectRecord* regApiPtr = transPtr->p;
+ TcConnectRecord* opRecord = opPtr->p;
+ TcKeyReq * const tcKeyReq = (TcKeyReq *)signal->getDataPtrSend();
+ Uint32 tcKeyRequestInfo = 0;
+ Uint32 tcKeyLength = TcKeyReq::StaticLength;
+ TableRecordPtr indexTabPtr;
+ AttributeBuffer::DataBufferIterator iter;
+ Uint32 attrId = 0;
+ Uint32 keyLength = 0;
+ Uint32 totalPrimaryKeyLength = 0;
+ Uint32 hops;
+
+ indexTabPtr.i = indexData->indexId;
+ ptrCheckGuard(indexTabPtr, ctabrecFilesize, tableRecord);
+ tcKeyReq->apiConnectPtr = transPtr->i;
+ tcKeyReq->senderData = opPtr->i;
+ if (holdOperation) {
+ jam();
+ opRecord->triggerExecutionCount++;
+ }//if
+ // Calculate key length and renumber attribute id:s
+ AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
+ LocalDataBuffer<11> afterValues(pool, firedTriggerData->afterValues);
+ bool skipNull = false;
+ for(bool moreKeyAttrs = afterValues.first(iter); moreKeyAttrs; attrId++) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ // Filter out NULL valued attributes
+ if (attrHeader->isNULL()) {
+ skipNull = true;
+ break;
+ }
+ attrHeader->setAttributeId(attrId);
+ keyLength += attrHeader->getDataSize();
+ hops = attrHeader->getHeaderSize() + attrHeader->getDataSize();
+ moreKeyAttrs = afterValues.next(iter, hops);
+ }
+ if (skipNull) {
+ jam();
+ opRecord->triggerExecutionCount--;
+ if (opRecord->triggerExecutionCount == 0) {
+ /*
+ We have completed current trigger execution
+ Continue triggering operation
+ */
+ jam();
+ continueTriggeringOp(signal, opRecord);
+ }//if
+ return;
+ }//if
+
+ // Calculate total length of primary key to be stored in index table
+ LocalDataBuffer<11> keyValues(pool, firedTriggerData->keyValues);
+ for(bool moreAttrData = keyValues.first(iter); moreAttrData; ) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ totalPrimaryKeyLength += attrHeader->getDataSize();
+ hops = attrHeader->getHeaderSize() + attrHeader->getDataSize();
+ moreAttrData = keyValues.next(iter, hops);
+ }
+ AttributeHeader pkAttrHeader(attrId, totalPrimaryKeyLength);
+
+ TcKeyReq::setKeyLength(tcKeyRequestInfo, keyLength);
+ tcKeyReq->attrLen = afterValues.getSize() +
+ pkAttrHeader.getHeaderSize() + pkAttrHeader.getDataSize();
+ tcKeyReq->tableId = indexData->indexId;
+ TcKeyReq::setOperationType(tcKeyRequestInfo, ZINSERT);
+ TcKeyReq::setExecutingTrigger(tcKeyRequestInfo, true);
+ tcKeyReq->tableSchemaVersion = indexTabPtr.p->currentSchemaVersion;
+ tcKeyReq->transId1 = regApiPtr->transid[0];
+ tcKeyReq->transId2 = regApiPtr->transid[1];
+ Uint32 * dataPtr = &tcKeyReq->scanInfo;
+ // Write first part of key in TCKEYREQ
+ Uint32 keyBufSize = 8; // Maximum for key in TCKEYREQ
+ Uint32 attrBufSize = 5; // Maximum for key in TCKEYREQ
+ Uint32 dataPos = 0;
+ // Filter out AttributeHeader:s since this should no be in key
+ bool moreKeyData = afterValues.first(iter);
+ Uint32 headerSize = 0, keyAttrSize = 0, dataSize = 0, headAndData = 0;
+
+ while (moreKeyData && (dataPos < keyBufSize)) {
+ /*
+ * If we have not read complete key
+ * and it fits in the signal
+ */
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ headerSize = attrHeader->getHeaderSize();
+ keyAttrSize = attrHeader->getDataSize();
+ headAndData = headerSize + attrHeader->getDataSize();
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreKeyData = afterValues.next(iter);
+ } else {
+ jam();
+ moreKeyData = afterValues.next(iter, headerSize - 1);
+ }//if
+ while((keyAttrSize != 0) && (dataPos < keyBufSize)) {
+ // If we have not read complete key
+ jam();
+ *dataPtr++ = *iter.data;
+ dataPos++;
+ keyAttrSize--;
+ moreKeyData = afterValues.next(iter);
+ }
+ if (keyAttrSize != 0) {
+ jam();
+ break;
+ }//if
+ }
+
+ tcKeyLength += dataPos;
+ Uint32 attributesLength = afterValues.getSize() +
+ pkAttrHeader.getHeaderSize() + pkAttrHeader.getDataSize();
+ if (attributesLength <= attrBufSize) {
+ jam();
+ // ATTRINFO fits in TCKEYREQ
+ // Pack ATTRINFO IN TCKEYREQ as one attribute
+ TcKeyReq::setAIInTcKeyReq(tcKeyRequestInfo, attributesLength);
+ bool moreAttrData;
+ // Insert primary key attributes (insert after values of primary table)
+ for(moreAttrData = afterValues.first(iter);
+ moreAttrData;
+ moreAttrData = afterValues.next(iter)) {
+ *dataPtr++ = *iter.data;
+ }
+ // Insert attribute values (insert key values of primary table)
+ // as one attribute
+ pkAttrHeader.insertHeader(dataPtr);
+ dataPtr += pkAttrHeader.getHeaderSize();
+ moreAttrData = keyValues.first(iter);
+ while(moreAttrData) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ headerSize = attrHeader->getHeaderSize();
+ dataSize = attrHeader->getDataSize();
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreAttrData = keyValues.next(iter);
+ } else {
+ jam();
+ moreAttrData = keyValues.next(iter, headerSize - 1);
+ }//if
+ // Copy attribute data
+ while(dataSize-- != 0) {
+ *dataPtr++ = *iter.data;
+ moreAttrData = keyValues.next(iter);
+ }
+ }
+ tcKeyLength += attributesLength;
+ } else {
+ jam();
+ // No ATTRINFO in TCKEYREQ
+ TcKeyReq::setAIInTcKeyReq(tcKeyRequestInfo, 0);
+ }
+ tcKeyReq->requestInfo = tcKeyRequestInfo;
+
+ /**
+ * Fix savepoint id -
+ * fix so that insert has same savepoint id as triggering operation
+ */
+ const Uint32 currSavePointId = regApiPtr->currSavePointId;
+ regApiPtr->currSavePointId = opRecord->savePointId;
+ EXECUTE_DIRECT(DBTC, GSN_TCKEYREQ, signal, tcKeyLength);
+ regApiPtr->currSavePointId = currSavePointId;
+ tcConnectptr.p->currentIndexId = indexData->indexId;
+ jamEntry();
+
+ // *********** KEYINFO ***********
+ if (moreKeyData) {
+ jam();
+ // Send KEYINFO sequence
+ KeyInfo * const keyInfo = (KeyInfo *)signal->getDataPtrSend();
+
+ keyInfo->connectPtr = transPtr->i;
+ keyInfo->transId[0] = regApiPtr->transid[0];
+ keyInfo->transId[1] = regApiPtr->transid[1];
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ // Pack any part of a key attribute that did no fit TCKEYREQ
+ while((keyAttrSize != 0) && (dataPos < KeyInfo::DataLength)) {
+ // If we have not read complete key
+ *dataPtr++ = *iter.data;
+ dataPos++;
+ keyAttrSize--;
+ if (dataPos == KeyInfo::DataLength) {
+ jam();
+ // Flush KEYINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength);
+ jamEntry();
+#endif
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ }
+ moreKeyData = afterValues.next(iter);
+ }
+
+ while(moreKeyData) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ headerSize = attrHeader->getHeaderSize();
+ keyAttrSize = attrHeader->getDataSize();
+ headAndData = headerSize + attrHeader->getDataSize();
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreKeyData = afterValues.next(iter);
+ } else {
+ jam();
+ moreKeyData = afterValues.next(iter, headerSize - 1);
+ }//if
+ while (keyAttrSize-- != 0) {
+ *dataPtr++ = *iter.data;
+ dataPos++;
+ if (dataPos == KeyInfo::DataLength) {
+ jam();
+ // Flush KEYINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength);
+ jamEntry();
+#endif
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ }
+ moreKeyData = afterValues.next(iter);
+ }
+ }
+ if (dataPos != 0) {
+ jam();
+ // Flush last KEYINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + dataPos, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + dataPos);
+ jamEntry();
+#endif
+ }
+ }
+
+ // *********** ATTRINFO ***********
+ if (attributesLength > attrBufSize) {
+ jam();
+ // No ATTRINFO in TcKeyReq
+ TcKeyReq::setAIInTcKeyReq(tcKeyReq->requestInfo, 0);
+ // Send ATTRINFO sequence
+ AttrInfo * const attrInfo = (AttrInfo *)signal->getDataPtrSend();
+ Uint32 attrInfoPos = 0;
+
+ attrInfo->connectPtr = transPtr->i;
+ attrInfo->transId[0] = regApiPtr->transid[0];
+ attrInfo->transId[1] = regApiPtr->transid[1];
+ dataPtr = (Uint32 *) &attrInfo->attrData;
+
+ bool moreAttrData;
+ // Insert primary key attributes (insert after values of primary table)
+ for(moreAttrData = afterValues.first(iter);
+ moreAttrData;
+ moreAttrData = afterValues.next(iter)) {
+ *dataPtr++ = *iter.data;
+ attrInfoPos++;
+ if (attrInfoPos == AttrInfo::DataLength) {
+ jam();
+ // Flush ATTRINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + AttrInfo::DataLength, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + AttrInfo::DataLength);
+ jamEntry();
+#endif
+ dataPtr = (Uint32 *) &attrInfo->attrData;
+ attrInfoPos = 0;
+ }
+ }
+ // Insert attribute values (insert key values of primary table)
+ // as one attribute
+ pkAttrHeader.insertHeader(dataPtr);
+ dataPtr += pkAttrHeader.getHeaderSize();
+ attrInfoPos += pkAttrHeader.getHeaderSize();
+ moreAttrData = keyValues.first(iter);
+ while(moreAttrData) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ headerSize = attrHeader->getHeaderSize();
+ dataSize = attrHeader->getDataSize();
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreAttrData = keyValues.next(iter);
+ } else {
+ jam();
+ moreAttrData = keyValues.next(iter, headerSize - 1);
+ }//if
+ while(dataSize-- != 0) { // If we have not read complete key
+ if (attrInfoPos == AttrInfo::DataLength) {
+ jam();
+ // Flush ATTRINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + AttrInfo::DataLength, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + AttrInfo::DataLength);
+ jamEntry();
+#endif
+ dataPtr = (Uint32 *) &attrInfo->attrData;
+ attrInfoPos = 0;
+ }
+ *dataPtr++ = *iter.data;
+ attrInfoPos++;
+ moreAttrData = keyValues.next(iter);
+ }
+ }
+ if (attrInfoPos != 0) {
+ jam();
+ // Flush last ATTRINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + attrInfoPos, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + attrInfoPos);
+ jamEntry();
+#endif
+ }
+ }
+}
+
+void Dbtc::deleteFromIndexTable(Signal* signal,
+ TcFiredTriggerData* firedTriggerData,
+ ApiConnectRecordPtr* transPtr,
+ TcConnectRecordPtr* opPtr,
+ TcIndexData* indexData,
+ bool holdOperation)
+{
+ ApiConnectRecord* regApiPtr = transPtr->p;
+ TcConnectRecord* opRecord = opPtr->p;
+ TcKeyReq * const tcKeyReq = (TcKeyReq *)signal->getDataPtrSend();
+ Uint32 tcKeyRequestInfo = 0;
+ Uint32 tcKeyLength = 12; // Static length
+ TableRecordPtr indexTabPtr;
+ AttributeBuffer::DataBufferIterator iter;
+ Uint32 attrId = 0;
+ Uint32 keyLength = 0;
+ Uint32 hops;
+
+ indexTabPtr.i = indexData->indexId;
+ ptrCheckGuard(indexTabPtr, ctabrecFilesize, tableRecord);
+ tcKeyReq->apiConnectPtr = transPtr->i;
+ tcKeyReq->senderData = opPtr->i;
+ if (holdOperation) {
+ jam();
+ opRecord->triggerExecutionCount++;
+ }//if
+ // Calculate key length and renumber attribute id:s
+ AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
+ LocalDataBuffer<11> beforeValues(pool, firedTriggerData->beforeValues);
+ bool skipNull = false;
+ for(bool moreKeyAttrs = beforeValues.first(iter);
+ (moreKeyAttrs);
+ attrId++) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ // Filter out NULL valued attributes
+ if (attrHeader->isNULL()) {
+ skipNull = true;
+ break;
+ }
+ attrHeader->setAttributeId(attrId);
+ keyLength += attrHeader->getDataSize();
+ hops = attrHeader->getHeaderSize() + attrHeader->getDataSize();
+ moreKeyAttrs = beforeValues.next(iter, hops);
+ }
+
+ if (skipNull) {
+ jam();
+ opRecord->triggerExecutionCount--;
+ if (opRecord->triggerExecutionCount == 0) {
+ /*
+ We have completed current trigger execution
+ Continue triggering operation
+ */
+ jam();
+ continueTriggeringOp(signal, opRecord);
+ }//if
+ return;
+ }//if
+
+ TcKeyReq::setKeyLength(tcKeyRequestInfo, keyLength);
+ tcKeyReq->attrLen = 0;
+ tcKeyReq->tableId = indexData->indexId;
+ TcKeyReq::setOperationType(tcKeyRequestInfo, ZDELETE);
+ TcKeyReq::setExecutingTrigger(tcKeyRequestInfo, true);
+ tcKeyReq->tableSchemaVersion = indexTabPtr.p->currentSchemaVersion;
+ tcKeyReq->transId1 = regApiPtr->transid[0];
+ tcKeyReq->transId2 = regApiPtr->transid[1];
+ Uint32 * dataPtr = &tcKeyReq->scanInfo;
+ // Write first part of key in TCKEYREQ
+ Uint32 keyBufSize = 8; // Maximum for key in TCKEYREQ
+ Uint32 dataPos = 0;
+ // Filter out AttributeHeader:s since this should no be in key
+ bool moreKeyData = beforeValues.first(iter);
+ Uint32 headerSize = 0, keyAttrSize = 0, headAndData = 0;
+
+ while (moreKeyData &&
+ (dataPos < keyBufSize)) {
+ /*
+ If we have not read complete key
+ and it fits in the signal
+ */
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ headerSize = attrHeader->getHeaderSize();
+ keyAttrSize = attrHeader->getDataSize();
+ headAndData = headerSize + attrHeader->getDataSize();
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreKeyData = beforeValues.next(iter);
+ } else {
+ jam();
+ moreKeyData = beforeValues.next(iter, headerSize - 1);
+ }//if
+ while((keyAttrSize != 0) &&
+ (dataPos < keyBufSize)) {
+ // If we have not read complete key
+ jam();
+ *dataPtr++ = *iter.data;
+ dataPos++;
+ keyAttrSize--;
+ moreKeyData = beforeValues.next(iter);
+ }
+ if (keyAttrSize != 0) {
+ jam();
+ break;
+ }//if
+ }
+
+ tcKeyLength += dataPos;
+ tcKeyReq->requestInfo = tcKeyRequestInfo;
+
+ /**
+ * Fix savepoint id -
+ * fix so that delete has same savepoint id as triggering operation
+ */
+ const Uint32 currSavePointId = regApiPtr->currSavePointId;
+ regApiPtr->currSavePointId = opRecord->savePointId;
+ EXECUTE_DIRECT(DBTC, GSN_TCKEYREQ, signal, tcKeyLength);
+ regApiPtr->currSavePointId = currSavePointId;
+ tcConnectptr.p->currentIndexId = indexData->indexId;
+ jamEntry();
+
+ // *********** KEYINFO ***********
+ if (moreKeyData) {
+ jam();
+ // Send KEYINFO sequence
+ KeyInfo * const keyInfo = (KeyInfo *)signal->getDataPtrSend();
+
+ keyInfo->connectPtr = transPtr->i;
+ keyInfo->transId[0] = regApiPtr->transid[0];
+ keyInfo->transId[1] = regApiPtr->transid[1];
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ // Pack any part of a key attribute that did no fit TCKEYREQ
+ while((keyAttrSize != 0) &&
+ (dataPos < KeyInfo::DataLength)) {
+ // If we have not read complete key
+ *dataPtr++ = *iter.data;
+ dataPos++;
+ keyAttrSize--;
+ if (dataPos == KeyInfo::DataLength) {
+ jam();
+ // Flush KEYINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength);
+ jamEntry();
+#endif
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ }
+ moreKeyData = beforeValues.next(iter);
+ }
+
+ while(moreKeyData) {
+ jam();
+ AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
+
+ headerSize = attrHeader->getHeaderSize();
+ keyAttrSize = attrHeader->getDataSize();
+ headAndData = headerSize + attrHeader->getDataSize();
+ // Skip header
+ if (headerSize == 1) {
+ jam();
+ moreKeyData = beforeValues.next(iter);
+ } else {
+ jam();
+ moreKeyData = beforeValues.next(iter,
+ headerSize - 1);
+ }//if
+ while (keyAttrSize-- != 0) {
+ *dataPtr++ = *iter.data;
+ dataPos++;
+ if (dataPos == KeyInfo::DataLength) {
+ jam();
+ // Flush KEYINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + KeyInfo::DataLength);
+ jamEntry();
+#endif
+ dataPtr = (Uint32 *) &keyInfo->keyData;
+ dataPos = 0;
+ }
+ moreKeyData = beforeValues.next(iter);
+ }
+ }
+ if (dataPos != 0) {
+ jam();
+ // Flush last KEYINFO
+#if INTERNAL_TRIGGER_TCKEYREQ_JBA
+ sendSignal(reference(), GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + dataPos, JBA);
+#else
+ EXECUTE_DIRECT(DBTC, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + dataPos);
+ jamEntry();
+#endif
+ }
+ }
+}
+
+Uint32
+Dbtc::TableRecord::getErrorCode(Uint32 schemaVersion) const {
+ if(!enabled)
+ return ZNO_SUCH_TABLE;
+ if(dropping)
+ return ZDROP_TABLE_IN_PROGRESS;
+ if(schemaVersion != currentSchemaVersion)
+ return ZWRONG_SCHEMA_VERSION_ERROR;
+ ErrorReporter::handleAssert("Dbtc::TableRecord::getErrorCode",
+ __FILE__, __LINE__);
+ return 0;
+}
+
diff --git a/storage/ndb/src/kernel/blocks/dbtc/Makefile.am b/storage/ndb/src/kernel/blocks/dbtc/Makefile.am
new file mode 100644
index 00000000000..98ee2639bac
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtc/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LIBRARIES = libdbtc.a
+
+libdbtc_a_SOURCES = DbtcInit.cpp DbtcMain.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbtc.dsp
+
+libdbtc.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbtc_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbtup/AttributeOffset.hpp b/storage/ndb/src/kernel/blocks/dbtup/AttributeOffset.hpp
new file mode 100644
index 00000000000..2c62adab3e5
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/AttributeOffset.hpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ATTRIBUTE_OFFSET_HPP
+#define ATTRIBUTE_OFFSET_HPP
+
+class AttributeOffset {
+ friend class Dbtup;
+
+private:
+ static void setOffset(Uint32 & desc, Uint32 offset);
+ static void setCharsetPos(Uint32 & desc, Uint32 offset);
+ static void setNullFlagPos(Uint32 & desc, Uint32 offset);
+
+ static Uint32 getOffset(const Uint32 &);
+ static bool getCharsetFlag(const Uint32 &);
+ static Uint32 getCharsetPos(const Uint32 &);
+ static Uint32 getNullFlagPos(const Uint32 &);
+ static Uint32 getNullFlagOffset(const Uint32 &);
+ static Uint32 getNullFlagBitOffset(const Uint32 &);
+ static bool isNULL(const Uint32 &, const Uint32 &);
+};
+
+/**
+ * Allow for 4096 attributes, all nullable, and for 128 different
+ * character sets.
+ *
+ * a = Attribute offset - 11 bits 0-10 ( addr word in 8 kb )
+ * c = Has charset flag 1 bits 11-11
+ * s = Charset pointer position - 7 bits 12-18 ( in table descriptor )
+ * f = Null flag offset in word - 5 bits 20-24 ( address 32 bits )
+ * w = Null word offset - 7 bits 25-32 ( f+w addr 4096 attrs )
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * aaaaaaaaaaacsssssss fffffwwwwwww
+ */
+
+#define AO_ATTRIBUTE_OFFSET_SHIFT 0
+#define AO_ATTRIBUTE_OFFSET_MASK 0x7ff
+
+#define AO_CHARSET_FLAG_SHIFT 11
+#define AO_CHARSET_POS_SHIFT 12
+#define AO_CHARSET_POS_MASK 127
+
+#define AO_NULL_FLAG_POS_MASK 0xfff // f+w
+#define AO_NULL_FLAG_POS_SHIFT 20
+
+#define AO_NULL_FLAG_WORD_MASK 31 // f
+#define AO_NULL_FLAG_OFFSET_SHIFT 5
+
+inline
+void
+AttributeOffset::setOffset(Uint32 & desc, Uint32 offset){
+ ASSERT_MAX(offset, AO_ATTRIBUTE_OFFSET_MASK, "AttributeOffset::setOffset");
+ desc |= (offset << AO_ATTRIBUTE_OFFSET_SHIFT);
+}
+
+inline
+void
+AttributeOffset::setCharsetPos(Uint32 & desc, Uint32 offset) {
+ ASSERT_MAX(offset, AO_CHARSET_POS_MASK, "AttributeOffset::setCharsetPos");
+ desc |= (1 << AO_CHARSET_FLAG_SHIFT);
+ desc |= (offset << AO_CHARSET_POS_SHIFT);
+}
+
+inline
+void
+AttributeOffset::setNullFlagPos(Uint32 & desc, Uint32 pos){
+ ASSERT_MAX(pos, AO_NULL_FLAG_POS_MASK, "AttributeOffset::setNullFlagPos");
+ desc |= (pos << AO_NULL_FLAG_POS_SHIFT);
+}
+
+inline
+Uint32
+AttributeOffset::getOffset(const Uint32 & desc)
+{
+ return (desc >> AO_ATTRIBUTE_OFFSET_SHIFT) & AO_ATTRIBUTE_OFFSET_MASK;
+}
+
+inline
+bool
+AttributeOffset::getCharsetFlag(const Uint32 & desc)
+{
+ return (desc >> AO_CHARSET_FLAG_SHIFT) & 1;
+}
+
+inline
+Uint32
+AttributeOffset::getCharsetPos(const Uint32 & desc)
+{
+ return (desc >> AO_CHARSET_POS_SHIFT) & AO_CHARSET_POS_MASK;
+}
+
+inline
+Uint32
+AttributeOffset::getNullFlagPos(const Uint32 & desc)
+{
+ return ((desc >> AO_NULL_FLAG_POS_SHIFT) & AO_NULL_FLAG_POS_MASK);
+}
+
+inline
+Uint32
+AttributeOffset::getNullFlagOffset(const Uint32 & desc)
+{
+ return (getNullFlagPos(desc) >> AO_NULL_FLAG_OFFSET_SHIFT);
+}
+
+inline
+Uint32
+AttributeOffset::getNullFlagBitOffset(const Uint32 & desc)
+{
+ return (getNullFlagPos(desc) & AO_NULL_FLAG_WORD_MASK);
+}
+
+inline
+bool
+AttributeOffset::isNULL(const Uint32 & pageWord, const Uint32 & desc)
+{
+ return (((pageWord >> getNullFlagBitOffset(desc)) & 1) == 1);
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
new file mode 100644
index 00000000000..6d169d20d16
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
@@ -0,0 +1,2409 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBTUP_H
+#define DBTUP_H
+
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+#include <ndb_limits.h>
+#include <trigger_definitions.h>
+#include <ArrayList.hpp>
+#include <AttributeHeader.hpp>
+#include <Bitmask.hpp>
+#include <signaldata/TupKey.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/TrigAttrInfo.hpp>
+#include <signaldata/BuildIndx.hpp>
+
+#define ZWORDS_ON_PAGE 8192 /* NUMBER OF WORDS ON A PAGE. */
+#define ZATTRBUF_SIZE 32 /* SIZE OF ATTRIBUTE RECORD BUFFER */
+#define ZMIN_PAGE_LIMIT_TUPKEYREQ 5
+#define ZTUP_VERSION_BITS 15
+
+#ifdef DBTUP_C
+//------------------------------------------------------------------
+// Jam Handling:
+//
+// When DBTUP reports lines through jam in the trace files it has to
+// be interpreted. 4024 means as an example line 24 in DbtupCommit.cpp
+// Thus 4000 is added to the line number beacuse it is located in the
+// file DbtupCommit.cpp. The following is the exhaustive list of the
+// added value in the various files. ndbrequire, ptrCheckGuard still
+// only reports the line number in the file it currently is located in.
+//
+// DbtupExecQuery.cpp 0
+// DbtupBuffer.cpp 2000
+// DbtupRoutines.cpp 3000
+// DbtupCommit.cpp 5000
+// DbtupFixAlloc.cpp 6000
+// DbtupTrigger.cpp 7000
+// DbtupAbort.cpp 9000
+// DbtupLCP.cpp 10000
+// DbtupUndoLog.cpp 12000
+// DbtupPageMap.cpp 14000
+// DbtupPagMan.cpp 16000
+// DbtupStoredProcDef.cpp 18000
+// DbtupMeta.cpp 20000
+// DbtupTabDesMan.cpp 22000
+// DbtupGen.cpp 24000
+// DbtupSystemRestart.cpp 26000
+// DbtupIndex.cpp 28000
+// DbtupDebug.cpp 30000
+//------------------------------------------------------------------
+
+/*
+2.2 LOCAL SYMBOLS
+-----------------
+*/
+/* ---------------------------------------------------------------- */
+/* S I Z E O F R E C O R D S */
+/* ---------------------------------------------------------------- */
+#define ZNO_OF_ATTRBUFREC 10000 /* SIZE OF ATTRIBUTE INFO FILE */
+#define ZNO_OF_CONCURRENT_OPEN_OP 40 /* NUMBER OF CONCURRENT OPENS */
+#define ZNO_OF_CONCURRENT_WRITE_OP 80 /* NUMBER OF CONCURRENT DISK WRITES*/
+#define ZNO_OF_FRAGOPREC 20 /* NUMBER OF CONCURRENT ADD FRAG. */
+#define ZNO_OF_LCP_REC 10 /* NUMBER OF CONCURRENT CHECKPOINTS*/
+#define TOT_PAGE_RECORD_SPACE 262144 /* SIZE OF PAGE RECORD FILE. */
+#define ZNO_OF_PAGE TOT_PAGE_RECORD_SPACE/ZWORDS_ON_PAGE
+#define ZNO_OF_PAGE_RANGE_REC 128 /* SIZE OF PAGE RANGE FILE */
+#define ZNO_OF_PARALLELL_UNDO_FILES 16 /* NUMBER OF PARALLEL UNDO FILES */
+#define ZNO_OF_RESTART_INFO_REC 10 /* MAXIMUM PARALLELL RESTART INFOS */
+ /* 24 SEGMENTS WITH 8 PAGES IN EACH*/
+ /* PLUS ONE UNDO BUFFER CACHE */
+// Undo record identifiers are 32-bits with page index 13-bits
+#define ZUNDO_RECORD_ID_PAGE_INDEX 13 /* 13 BITS = 8192 WORDS/PAGE */
+#define ZUNDO_RECORD_ID_PAGE_INDEX_MASK (ZWORDS_ON_PAGE - 1) /* 1111111111111 */
+
+// Trigger constants
+#define ZDEFAULT_MAX_NO_TRIGGERS_PER_TABLE 16
+
+/* ---------------------------------------------------------------- */
+// VARIABLE NUMBERS OF PAGE_WORD, UNDO_WORD AND LOGIC_WORD FOR
+// COMMUNICATION WITH FILE SYSTEM
+/* ---------------------------------------------------------------- */
+#define ZBASE_ADDR_PAGE_WORD 1 /* BASE ADDRESS OF PAGE_WORD VAR */
+#define ZBASE_ADDR_UNDO_WORD 2 /* BASE ADDRESS OF UNDO_WORD VAR */
+#define ZBASE_ADDR_LOGIC_WORD 3 /* BASE ADDRESS OF LOGIC_WORD VAR */
+
+/* ---------------------------------------------------------------- */
+// NUMBER OF PAGES SENT TO DISK IN DATA BUFFER AND UNDO BUFFER WHEN
+// OPTIMUM PERFORMANCE IS ACHIEVED.
+/* ---------------------------------------------------------------- */
+#define ZUB_SEGMENT_SIZE 8 /* SEGMENT SIZE OF UNDO BUFFER */
+#define ZDB_SEGMENT_SIZE 8 /* SEGMENT SIZE OF DATA BUFFER */
+
+/* ---------------------------------------------------------------- */
+/* A ATTRIBUTE MAY BE NULL, DYNAMIC OR NORMAL. A NORMAL ATTRIBUTE */
+/* IS A ATTRIBUTE THAT IS NOT NULL OR DYNAMIC. A NULL ATTRIBUTE */
+/* MAY HAVE NO VALUE. A DYNAMIC ATTRIBUTE IS A NULL ATTRIBUTE THAT */
+/* DOES NOT HAVE TO BE A MEMBER OF EVERY TUPLE I A CERTAIN TABLE. */
+/* ---------------------------------------------------------------- */
+/**
+ * #defines moved into include/kernel/Interpreter.hpp
+ */
+#define ZMAX_REGISTER 21
+#define ZINSERT_DELETE 0
+/* ---------------------------------------------------------------- */
+/* THE MINIMUM SIZE OF AN 'EMPTY' TUPLE HEADER IN R-WORDS */
+/* ---------------------------------------------------------------- */
+#define ZTUP_HEAD_MINIMUM_SIZE 2
+ /* THE TUPLE HEADER FIELD 'SIZE OF NULL ATTR. FIELD' SPECIFYES */
+ /* THE SIZE OF THE TUPLE HEADER FIELD 'NULL ATTR. FIELD'. */
+ /* THE TUPLE HEADER FIELD 'TYPE' SPECIFYES THE TYPE OF THE TUPLE */
+ /* HEADER. */
+ /* TUPLE ATTRIBUTE INDEX CLUSTERS, ATTRIBUTE */
+ /* CLUSTERS AND A DYNAMIC ATTRIBUTE HEADER. */
+ /* IT MAY ALSO CONTAIN SHORT ATTRIBUTES AND */
+ /* POINTERS TO LONG ATTRIBUTE HEADERS. */
+ /* TUPLE ATTRIBUTE INDEX CLUSTERS, ATTRIBUTE */
+ /* CLUSTERS AND A DYNAMIC ATTRIBUTE HEADER. */
+
+#define ZTH_TYPE3 2 /* TUPLE HEADER THAT MAY HAVE A POINTER TO */
+ /* A DYNAMIC ATTRIBUTE HEADER. IT MAY ALSO */
+ /* CONTAIN SHORT ATTRIBUTES AND POINTERS */
+ /* TO LONG ATTRIBUTE HEADERS. */
+
+ /* DATA STRUCTURE TYPES */
+ /* WHEN ATTRIBUTE INFO IS SENT WITH A ATTRINFO-SIGNAL THE */
+ /* VARIABLE TYPE IS SPECIFYED. THIS MUST BE DONE TO BE ABLE TO */
+ /* NOW HOW MUCH DATA OF A ATTRIBUTE TO READ FROM ATTRINFO. */
+#define ZFIXED_ARRAY 2 /* ZFIXED ARRAY FIELD. */
+#define ZNON_ARRAY 1 /* NORMAL FIELD. */
+#define ZVAR_ARRAY 0 /* VARIABLE ARRAY FIELD */
+#define ZNOT_STORE 3 /* THE ATTR IS STORED IN THE INDEX BLOCK */
+#define ZMAX_SMALL_VAR_ARRAY 256
+
+ /* PLEASE OBSERVE THAT THEESE CONSTANTS CORRESPONDS TO THE NUMBER */
+ /* OF BITS NEEDED TO REPRESENT THEM D O N O T C H A N G E */
+#define Z1BIT_VAR 0 /* 1 BIT VARIABLE. */
+#define Z2BIT_VAR 1 /* 2 BIT VARIABLE. */
+#define Z4BIT_VAR 2 /* 4 BIT VARIABLE. */
+#define Z8BIT_VAR 3 /* 8 BIT VARIABLE. */
+#define Z16BIT_VAR 4 /* 16 BIT VARIABLE. */
+#define Z32BIT_VAR 5 /* 32 BIT VARIABLE. */
+#define Z64BIT_VAR 6 /* 64 BIT VARIABLE. */
+#define Z128BIT_VAR 7 /* 128 BIT VARIABLE. */
+
+ /* WHEN A REQUEST CAN NOT BE EXECUTED BECAUSE OF A ERROR THE */
+ /* ERROR MUST BE IDENTIFYED BY MEANS OF A ERROR CODE AND SENT TO */
+ /* THE REQUESTER. */
+#define ZGET_OPREC_ERROR 804 // TUP_SEIZEREF
+
+#define ZEXIST_FRAG_ERROR 816 // Add fragment
+#define ZFULL_FRAGRECORD_ERROR 817 // Add fragment
+#define ZNO_FREE_PAGE_RANGE_ERROR 818 // Add fragment
+#define ZNOFREE_FRAGOP_ERROR 830 // Add fragment
+#define ZTOO_LARGE_TUPLE_ERROR 851 // Add fragment
+#define ZNO_FREE_TAB_ENTRY_ERROR 852 // Add fragment
+#define ZNO_PAGES_ALLOCATED_ERROR 881 // Add fragment
+
+#define ZGET_REALPID_ERROR 809
+#define ZNOT_IMPLEMENTED_ERROR 812
+#define ZSEIZE_ATTRINBUFREC_ERROR 805
+#define ZTOO_MUCH_ATTRINFO_ERROR 823
+#define ZMEM_NOTABDESCR_ERROR 826
+#define ZMEM_NOMEM_ERROR 827
+#define ZAI_INCONSISTENCY_ERROR 829
+#define ZNO_ILLEGAL_NULL_ATTR 839
+#define ZNOT_NULL_ATTR 840
+#define ZNO_INSTRUCTION_ERROR 871
+#define ZOUTSIDE_OF_PROGRAM_ERROR 876
+#define ZSTORED_PROC_ID_ERROR 877
+#define ZREGISTER_INIT_ERROR 878
+#define ZATTRIBUTE_ID_ERROR 879
+#define ZTRY_TO_READ_TOO_MUCH_ERROR 880
+#define ZTOTAL_LEN_ERROR 882
+#define ZATTR_INTERPRETER_ERROR 883
+#define ZSTACK_OVERFLOW_ERROR 884
+#define ZSTACK_UNDERFLOW_ERROR 885
+#define ZTOO_MANY_INSTRUCTIONS_ERROR 886
+#define ZTRY_TO_UPDATE_ERROR 888
+#define ZCALL_ERROR 890
+#define ZTEMPORARY_RESOURCE_FAILURE 891
+
+#define ZSTORED_SEIZE_ATTRINBUFREC_ERROR 873 // Part of Scan
+
+#define ZREAD_ONLY_CONSTRAINT_VIOLATION 893
+#define ZVAR_SIZED_NOT_SUPPORTED 894
+#define ZINCONSISTENT_NULL_ATTRIBUTE_COUNT 895
+#define ZTUPLE_CORRUPTED_ERROR 896
+#define ZTRY_UPDATE_PRIMARY_KEY 897
+#define ZMUST_BE_ABORTED_ERROR 898
+#define ZTUPLE_DELETED_ERROR 626
+#define ZINSERT_ERROR 630
+
+#define ZINVALID_CHAR_FORMAT 744
+
+
+ /* SOME WORD POSITIONS OF FIELDS IN SOME HEADERS */
+#define ZPAGE_STATE_POS 0 /* POSITION OF PAGE STATE */
+#define ZPAGE_NEXT_POS 1 /* POSITION OF THE NEXT POINTER WHEN IN FREELIST */
+#define ZPAGE_PREV_POS 2 /* POSITION OF THE PREVIOUS POINTER WHEN IN FREELIST */
+#define ZFREELIST_HEADER_POS 3 /* POSITION OF THE FIRST FREELIST */
+#define ZPAGE_FRAG_PAGE_ID_POS 4 /* POSITION OF FRAG PAGE ID WHEN USED*/
+#define ZPAGE_NEXT_CLUST_POS 5 /* POSITION OF NEXT FREE SET OF PAGES */
+#define ZPAGE_FIRST_CLUST_POS 2 /* POSITION OF THE POINTER TO THE FIRST PAGE IN A CLUSTER */
+#define ZPAGE_LAST_CLUST_POS 6 /* POSITION OF THE POINTER TO THE LAST PAGE IN A CLUSTER */
+#define ZPAGE_PREV_CLUST_POS 7 /* POSITION OF THE PREVIOUS POINTER */
+#define ZPAGE_HEADER_SIZE 32 /* NUMBER OF WORDS IN MEM PAGEHEADER */
+#define ZDISK_PAGE_HEADER_SIZE 32 /* NUMBER OF WORDS IN DISK PAGEHEADER */
+#define ZNO_OF_FREE_BLOCKS 3 /* NO OF FREE BLOCK IN THE DISK PAGE */
+#define ZDISK_PAGE_ID 8 /* ID OF THE PAGE ON THE DISK */
+#define ZBLOCK_LIST 9
+#define ZCOPY_OF_PAGE 10
+#define ZPAGE_PHYSICAL_INDEX 11
+#define ZNEXT_IN_PAGE_USED_LIST 12
+#define ZPREV_IN_PAGE_USED_LIST 13
+#define ZDISK_USED_TYPE 14
+#define ZFREE_COMMON 1 /* PAGE STATE, PAGE IN COMMON AREA */
+#define ZEMPTY_MM 2 /* PAGE STATE, PAGE IN EMPTY LIST */
+#define ZTH_MM_FREE 3 /* PAGE STATE, TUPLE HEADER PAGE WITH FREE AREA */
+#define ZTH_MM_FULL 4 /* PAGE STATE, TUPLE HEADER PAGE WHICH IS FULL */
+#define ZAC_MM_FREE 5 /* PAGE STATE, ATTRIBUTE CLUSTER PAGE WITH FREE AREA */
+#define ZTH_MM_FREE_COPY 7 /* PAGE STATE, TH COPY PAGE WITH FREE AREA */
+#define ZTH_MM_FULL_COPY 8 /* PAGE STATE, TH COPY PAGE WHICH IS FULL */
+#define ZAC_MM_FREE_COPY 9 /* PAGE STATE, AC COPY PAGE WITH FREE AREA */
+#define ZMAX_NO_COPY_PAGES 4 /* THE MAXIMUM NUMBER OF COPY PAGES ALLOWED PER FRAGMENT */
+
+ /* CONSTANTS USED TO HANDLE TABLE DESCRIPTOR RECORDS */
+ /* ALL POSITIONS AND SIZES IS BASED ON R-WORDS (32-BIT ON APZ 212) */
+#define ZTD_HEADER 0 /* HEADER POSITION */
+#define ZTD_DATASIZE 1 /* SIZE OF THE DATA IN THIS CHUNK */
+#define ZTD_SIZE 2 /* TOTAL SIZE OF TABLE DESCRIPTOR */
+
+ /* TRAILER POSITIONS FROM END OF TABLE DESCRIPTOR RECORD */
+#define ZTD_TR_SIZE 1 /* SIZE DESCRIPTOR POS FROM END+1 */
+#define ZTD_TR_TYPE 2
+#define ZTD_TRAILER_SIZE 2 /* TOTAL SIZE OF TABLE TRAILER */
+#define ZAD_SIZE 2 /* TOTAL SIZE OF ATTR DESCRIPTOR */
+#define ZAD_LOG_SIZE 1 /* TWO LOG OF TOTAL SIZE OF ATTR DESCRIPTOR */
+
+ /* CONSTANTS USED TO HANDLE TABLE DESCRIPTOR AS A FREELIST */
+#define ZTD_FL_HEADER 0 /* HEADER POSITION */
+#define ZTD_FL_SIZE 1 /* TOTAL SIZE OF THIS FREELIST ENTRY */
+#define ZTD_FL_PREV 2 /* PREVIOUS RECORD IN FREELIST */
+#define ZTD_FL_NEXT 3 /* NEXT RECORD IN FREELIST */
+#define ZTD_FREE_SIZE 16 /* SIZE NEEDED TO HOLD ONE FL ENTRY */
+
+ /* CONSTANTS USED IN LSB OF TABLE DESCRIPTOR HEADER DESCRIBING USAGE */
+#define ZTD_TYPE_FREE 0 /* RECORD LINKED INTO FREELIST */
+#define ZTD_TYPE_NORMAL 1 /* RECORD USED AS TABLE DESCRIPTOR */
+ /* ATTRIBUTE OPERATION CONSTANTS */
+#define ZLEAF 1
+#define ZNON_LEAF 2
+
+ /* ATTRINBUFREC VARIABLE POSITIONS. */
+#define ZBUF_PREV 29 /* POSITION OF 'PREV'-VARIABLE (USED BY INTERPRETED EXEC) */
+#define ZBUF_DATA_LEN 30 /* POSITION OF 'DATA LENGTH'-VARIABLE. */
+#define ZBUF_NEXT 31 /* POSITION OF 'NEXT'-VARIABLE. */
+#define ZSAVE_BUF_NEXT 28
+#define ZSAVE_BUF_DATA_LEN 27
+
+ /* RETURN POINTS. */
+ /* RESTART PHASES */
+#define ZSTARTPHASE1 1
+#define ZSTARTPHASE2 2
+#define ZSTARTPHASE3 3
+#define ZSTARTPHASE4 4
+#define ZSTARTPHASE6 6
+
+#define ZADDFRAG 0
+
+ /* CHECKPOINT RECORD TYPES */
+#define ZLCPR_TYPE_INSERT_TH 0 /* INSERT TUPLE HEADER */
+#define ZLCPR_TYPE_DELETE_TH 1 /* DELETE TUPLE HEADER */
+#define ZLCPR_TYPE_UPDATE_TH 2 /* DON'T CREATE IT, JUST UPDETE */
+#define ZLCPR_TYPE_INSERT_TH_NO_DATA 3 /* INSERT TUPLE HEADER */
+#define ZLCPR_ABORT_UPDATE 4 /* UNDO AN UPDATE OPERATION THAT WAS ACTIVE IN LCP */
+#define ZLCPR_ABORT_INSERT 5 /* UNDO AN INSERT OPERATION THAT WAS ACTIVE IN LCP */
+#define ZTABLE_DESCRIPTOR 6 /* TABLE DESCRIPTOR */
+#define ZINDICATE_NO_OP_ACTIVE 7 /* ENSURE THAT NO OPERATION ACTIVE AFTER RESTART */
+#define ZLCPR_UNDO_LOG_PAGE_HEADER 8 /* CHANGE IN PAGE HEADER IS UNDO LOGGED */
+#define ZLCPR_TYPE_UPDATE_GCI 9 /* Update GCI at commit time */
+#define ZNO_CHECKPOINT_RECORDS 10 /* NUMBER OF CHECKPOINTRECORD TYPES */
+
+ /* RESULT CODES */
+ /* ELEMENT POSITIONS IN SYSTEM RESTART INFO PAGE OF THE DATA FILE */
+#define ZSRI_NO_OF_FRAG_PAGES_POS 10 /* NUMBER OF FRAGMENT PAGES WHEN CHECKPOINT STARTED */
+#define ZSRI_TUP_RESERVED_SIZE_POS 11 /* RESERVED SIZE OF THE TUPLE WHEN CP STARTED */
+#define ZSRI_TUP_FIXED_AREA_POS 12 /* SIZE OF THE TUPLE FIXED AREA WHEN CP STARTED */
+#define ZSRI_TAB_DESCR_SIZE 13 /* SIZE OF THE TABLE DESCRIPTOR WHEN CP STARTED */
+#define ZSRI_NO_OF_ATTRIBUTES_POS 14 /* NUMBER OF ATTRIBUTES */
+#define ZSRI_UNDO_LOG_END_REC_ID 15 /* LAST UNDO LOG RECORD ID FOR THIS CHECKPOINT */
+#define ZSRI_UNDO_LOG_END_PAGE_ID 16 /* LAST USED LOG PAGE ID FOR THIS CHECKPOINT */
+#define ZSRI_TH_FREE_FIRST 17 /* FIRST FREE PAGE OF TUPLE HEADERS */
+#define ZSRI_TH_FREE_COPY_FIRST 18 /* FIRST FREE PAGE OF TUPLE HEADER COPIES */
+#define ZSRI_EMPTY_PRIM_PAGE 27 /* FIRST EMPTY PAGE */
+#define ZSRI_NO_COPY_PAGES_ALLOC 28 /* NO COPY PAGES IN FRAGMENT AT LOCAL CHECKPOINT */
+#define ZSRI_UNDO_FILE_VER 29 /* CHECK POINT ID OF THE UNDO FILE */
+#define ZSRI_NO_OF_INDEX_ATTR 30 /* No of index attributes */
+#define ZNO_OF_PAGES_CLUSTER_REC 0
+
+//------------------------------------------------------------
+// TUP_CONTINUEB codes
+//------------------------------------------------------------
+#define ZSTART_EXEC_UNDO_LOG 0
+#define ZCONT_START_SAVE_CL 1
+#define ZCONT_SAVE_DP 2
+#define ZCONT_EXECUTE_LC 3
+#define ZCONT_LOAD_DP 4
+#define ZLOAD_BAL_LCP_TIMER 5
+#define ZINITIALISE_RECORDS 6
+#define ZREL_FRAG 7
+#define ZREPORT_MEMORY_USAGE 8
+#define ZBUILD_INDEX 9
+
+#define ZINDEX_STORAGE 0
+#define ZDATA_WORD_AT_DISK_PAGE 2030
+#define ZALLOC_DISK_PAGE_LAST_INDEX 2047
+#define ZWORD_IN_BLOCK 127 /* NO OF WORD IN A BLOCK */
+#define ZNO_DISK_PAGES_FILE_REC 100
+#define ZMASK_PAGE_INDEX 0x7ff
+#define ZBIT_PAGE_INDEX 11 /* 8 KBYT PAGE = 2048 WORDS */
+#define ZSCAN_PROCEDURE 0
+#define ZCOPY_PROCEDURE 2
+#define ZSTORED_PROCEDURE_DELETE 3
+#define ZSTORED_PROCEDURE_FREE 0xffff
+#define ZMIN_PAGE_LIMIT_TUP_COMMITREQ 2
+#define ZUNDO_PAGE_HEADER_SIZE 2 /* SIZE OF UNDO PAGE HEADER */
+#endif
+
+class Dbtup: public SimulatedBlock {
+public:
+
+ typedef bool (Dbtup::* ReadFunction)(Uint32*,
+ AttributeHeader*,
+ Uint32,
+ Uint32);
+ typedef bool (Dbtup::* UpdateFunction)(Uint32*,
+ Uint32,
+ Uint32);
+// State values
+enum State {
+ NOT_INITIALIZED = 0,
+ COMMON_AREA_PAGES = 1,
+ UNDO_RESTART_PAGES = 2,
+ UNDO_PAGES = 3,
+ READ_ONE_PAGE = 4,
+ CHECKPOINT_DATA_READ = 7,
+ CHECKPOINT_DATA_READ_PAGE_ZERO = 8,
+ CHECKPOINT_DATA_WRITE = 9,
+ CHECKPOINT_DATA_WRITE_LAST = 10,
+ CHECKPOINT_DATA_WRITE_FLUSH = 11,
+ CHECKPOINT_UNDO_READ = 12,
+ CHECKPOINT_UNDO_READ_FIRST = 13,
+ CHECKPOINT_UNDO_WRITE = 14,
+ CHECKPOINT_UNDO_WRITE_FLUSH = 15,
+ CHECKPOINT_TD_READ = 16,
+ IDLE = 17,
+ ACTIVE = 18,
+ SYSTEM_RESTART = 19,
+ NO_OTHER_OP = 20,
+ COMMIT_DELETE = 21,
+ TO_BE_COMMITTED = 22,
+ ABORTED = 23,
+ ALREADY_ABORTED_INSERT = 24,
+ ALREADY_ABORTED = 25,
+ ABORT_INSERT = 26,
+ ABORT_UPDATE = 27,
+ INIT = 28,
+ INITIAL_READ = 29,
+ INTERPRETED_EXECUTION = 30,
+ FINAL_READ = 31,
+ FINAL_UPDATE = 32,
+ DISCONNECTED = 33,
+ DEFINED = 34,
+ ERROR_WAIT_TUPKEYREQ = 35,
+ STARTED = 36,
+ NOT_DEFINED = 37,
+ COMPLETED = 38,
+ WAIT_ABORT = 39,
+ NORMAL_PAGE = 40,
+ COPY_PAGE = 41,
+ DELETE_BLOCK = 42,
+ WAIT_STORED_PROCEDURE_ATTR_INFO = 43,
+ DATA_FILE_READ = 45,
+ DATA_FILE_WRITE = 46,
+ LCP_DATA_FILE_READ = 47,
+ LCP_DATA_FILE_WRITE = 48,
+ LCP_DATA_FILE_WRITE_WITH_UNDO = 49,
+ LCP_DATA_FILE_CLOSE = 50,
+ LCP_UNDO_FILE_READ = 51,
+ LCP_UNDO_FILE_CLOSE = 52,
+ LCP_UNDO_FILE_WRITE = 53,
+ OPENING_DATA_FILE = 54,
+ INITIATING_RESTART_INFO = 55,
+ INITIATING_FRAGMENT = 56,
+ OPENING_UNDO_FILE = 57,
+ READING_RESTART_INFO = 58,
+ INIT_UNDO_SEGMENTS = 59,
+ READING_TAB_DESCR = 60,
+ READING_DATA_PAGES = 61,
+ WAIT_COPY_PROCEDURE = 62,
+ TOO_MUCH_AI = 63,
+ SAME_PAGE = 64,
+ DEFINING = 65,
+ TUPLE_BLOCKED = 66,
+ ERROR_WAIT_STORED_PROCREQ = 67
+};
+
+// Records
+/* ************** ATTRIBUTE INFO BUFFER RECORD ****************** */
+/* THIS RECORD IS USED AS A BUFFER FOR INCOMING AND OUTGOING DATA */
+/* ************************************************************** */
+struct Attrbufrec {
+ Uint32 attrbuf[ZATTRBUF_SIZE];
+}; /* p2c: size = 128 bytes */
+
+typedef Ptr<Attrbufrec> AttrbufrecPtr;
+
+/* ********** CHECKPOINT INFORMATION ************ */
+/* THIS RECORD HOLDS INFORMATION NEEDED TO */
+/* PERFORM A CHECKPOINT. IT'S POSSIBLE TO RUN */
+/* MULTIPLE CHECKPOINTS AT A TIME. THIS RECORD */
+/* MAKES IT POSSIBLE TO DISTINGER BETWEEN THE */
+/* DIFFERENT CHECKPOINTS. */
+/* ********************************************** */
+struct CheckpointInfo {
+ Uint32 lcpNextRec; /* NEXT RECORD IN FREELIST */
+ Uint32 lcpCheckpointVersion; /* VERSION OF THE CHECKPOINT */
+ Uint32 lcpLocalLogInfoP; /* POINTER TO A LOCAL LOG INFO RECORD */
+ Uint32 lcpUserptr; /* USERPOINTER TO THE BLOCK REQUESTING THE CP */
+ Uint32 lcpFragmentP; /* FRAGMENT POINTER TO WHICH THE CHECKPOINT APPLIES */
+ Uint32 lcpFragmentId; /* FRAGMENT ID */
+ Uint32 lcpTabPtr; /* TABLE POINTER */
+ Uint32 lcpDataBufferSegmentP; /* POINTER TO A DISK BUFFER SEGMENT POINTER (DATA) */
+ Uint32 lcpDataFileHandle; /* FILE HANDLES FOR DATA FILE. LOG FILE HANDLE IN LOCAL_LOG_INFO_RECORD */
+ /* FILE HANDLE TO THE OPEN DATA FILE */
+ Uint32 lcpNoOfPages;
+ Uint32 lcpThFreeFirst;
+ Uint32 lcpThFreeCopyFirst;
+ Uint32 lcpEmptyPrimPage;
+ Uint32 lcpNoCopyPagesAlloc;
+ Uint32 lcpTmpOperPtr; /* TEMPORARY STORAGE OF OPER_PTR DURING SAVE */
+ BlockReference lcpBlockref; /* BLOCKREFERENCE TO THE BLOCK REQUESTING THE CP */
+};
+typedef Ptr<CheckpointInfo> CheckpointInfoPtr;
+
+/* *********** DISK BUFFER SEGMENT INFO ********* */
+/* THIS RECORD HOLDS INFORMATION NEEDED DURING */
+/* A WRITE OF THE DATA BUFFER TO DISK. WHEN THE */
+/* WRITE SIGNAL IS SENT A POINTER TO THIS RECORD */
+/* IS INCLUDED. WHEN THE WRITE IS COMPLETED AND */
+/* CONFIRMED THE PTR TO THIS RECORD IS RETURNED */
+/* AND THE BUFFER PAGES COULD EASILY BE LOCATED */
+/* AND DEALLOCATED. THE CHECKPOINT_INFO_VERSION */
+/* KEEPS TRACK OF THE CHECPOINT_INFO_RECORD THAT */
+/* INITIATED THE WRITE AND THE CP_PAGE_TO_DISK */
+/* ELEMENT COULD BE INCREASED BY THE NUMBER OF */
+/* PAGES WRITTEN. */
+/* ********************************************** */
+struct DiskBufferSegmentInfo {
+ Uint32 pdxDataPage[16]; /* ARRAY OF DATA BUFFER PAGES */
+ Uint32 pdxUndoBufferSet[2];
+ Uint32 pdxNextRec;
+ State pdxBuffertype;
+ State pdxOperation;
+ /*---------------------------------------------------------------------------*/
+ /* PDX_FLAGS BITS AND THEIR USAGE: */
+ /* BIT 0 1 COMMENT */
+ /*---------------------------------------------------------------------------*/
+ /* 0 SEGMENT INVALID SEGMENT VALID USED DURING READS */
+ /* 1-15 NOT USED */
+ /*---------------------------------------------------------------------------*/
+ Uint32 pdxCheckpointInfoP; /* USED DURING LOCAL CHKP */
+ Uint32 pdxRestartInfoP; /* USED DURING RESTART */
+ Uint32 pdxLocalLogInfoP; /* POINTS TO A LOCAL LOG INFO */
+ Uint32 pdxFilePage; /* START PAGE IN FILE */
+ Uint32 pdxNumDataPages; /* NUMBER OF DATA PAGES */
+};
+typedef Ptr<DiskBufferSegmentInfo> DiskBufferSegmentInfoPtr;
+
+struct Fragoperrec {
+ bool definingFragment;
+ Uint32 nextFragoprec;
+ Uint32 lqhPtrFrag;
+ Uint32 fragidFrag;
+ Uint32 tableidFrag;
+ Uint32 fragPointer;
+ Uint32 attributeCount;
+ Uint32 currNullBit;
+ Uint32 noOfNullBits;
+ Uint32 noOfNewAttrCount;
+ Uint32 charsetIndex;
+ BlockReference lqhBlockrefFrag;
+ bool inUse;
+};
+typedef Ptr<Fragoperrec> FragoperrecPtr;
+
+struct Fragrecord {
+ Uint32 nextStartRange;
+ Uint32 currentPageRange;
+ Uint32 rootPageRange;
+ Uint32 noOfPages;
+ Uint32 emptyPrimPage;
+
+ Uint32 firstusedOprec;
+ Uint32 lastusedOprec;
+
+ Uint32 thFreeFirst;
+ Uint32 thFreeCopyFirst;
+ Uint32 noCopyPagesAlloc;
+
+ Uint32 checkpointVersion;
+ Uint32 minPageNotWrittenInCheckpoint;
+ Uint32 maxPageWrittenInCheckpoint;
+ State fragStatus;
+ Uint32 fragTableId;
+ Uint32 fragmentId;
+ Uint32 nextfreefrag;
+};
+typedef Ptr<Fragrecord> FragrecordPtr;
+
+ /* ************ LOCAL LOG FILE INFO ************* */
+ /* THIS RECORD HOLDS INFORMATION NEEDED DURING */
+ /* CHECKPOINT AND RESTART. THERE ARE FOUR */
+ /* PARALLELL UNDO LOG FILES, EACH ONE REPRESENTED */
+ /* BY AN ENTITY OF THIS RECORD. */
+ /* BECAUSE EACH FILE IS SHARED BETWEEN FOUR */
+ /* TABLES AND HAS ITS OWN PAGEPOINTERS AND */
+ /* WORDPOINTERS. */
+ /* ********************************************** */
+struct LocalLogInfo {
+ Uint32 lliActiveLcp; /* NUMBER OF ACTIVE LOCAL CHECKPOINTS ON THIS FILE */
+ Uint32 lliEndPageId; /* PAGE IDENTIFIER OF LAST PAGE WITH LOG DATA */
+ Uint32 lliPrevRecordId; /* PREVIOUS RECORD IN THIS LOGFILE */
+ Uint32 lliLogFilePage; /* PAGE IN LOGFILE */
+ Uint32 lliNumFragments; /* NO OF FRAGMENTS RESTARTING FROM THIS LOCAL LOG */
+ Uint32 lliUndoBufferSegmentP; /* POINTER TO A DISK BUFFER SEGMENT POINTER (UNDO) */
+ Uint32 lliUndoFileHandle; /* FILE HANDLE OF UNDO LOG FILE */
+ Uint32 lliUndoPage; /* UNDO PAGE IN BUFFER */
+ Uint32 lliUndoWord;
+ Uint32 lliUndoPagesToDiskWithoutSynch;
+};
+typedef Ptr<LocalLogInfo> LocalLogInfoPtr;
+
+struct Operationrec {
+// Easy to remove (2 words)
+ Uint32 attroutbufLen;
+ Uint32 logSize;
+
+// Needed (20 words)
+ State tupleState;
+ Uint32 prevActiveOp;
+ Uint32 nextActiveOp;
+ Uint32 nextOprecInList;
+ Uint32 prevOprecInList;
+ Uint32 tableRef;
+ Uint32 fragId;
+ Uint32 fragmentPtr;
+ Uint32 fragPageId;
+ Uint32 realPageId;
+ bool undoLogged;
+ Uint32 realPageIdC;
+ Uint32 fragPageIdC;
+ Uint32 firstAttrinbufrec;
+ Uint32 lastAttrinbufrec;
+ Uint32 attrinbufLen;
+ Uint32 currentAttrinbufLen;
+ Uint32 userpointer;
+ State transstate;
+ Uint32 savePointId;
+
+// Easy to remove (3 words)
+ Uint32 tcOperationPtr;
+ Uint32 transid1;
+ Uint32 transid2;
+
+// Needed (2 words)
+ Uint16 pageIndex;
+ Uint16 pageOffset;
+ Uint16 pageOffsetC;
+ Uint16 pageIndexC;
+// Hard to remove
+ Uint16 tupVersion;
+
+// Easy to remove (1.5 word)
+ BlockReference recBlockref;
+ BlockReference userblockref;
+ Uint16 storedProcedureId;
+
+ Uint8 inFragList;
+ Uint8 inActiveOpList;
+ Uint8 deleteInsertFlag;
+
+// Needed (1 word)
+ Uint8 dirtyOp;
+ Uint8 interpretedExec;
+ Uint8 optype;
+ Uint8 opSimple;
+
+// Used by triggers
+ Uint32 primaryReplica;
+ BlockReference coordinatorTC;
+ Uint32 tcOpIndex;
+ Uint32 gci;
+ Uint32 noFiredTriggers;
+ union {
+ Uint32 hashValue; // only used in TUP_COMMITREQ
+ Uint32 lastRow;
+ };
+ Bitmask<MAXNROFATTRIBUTESINWORDS> changeMask;
+};
+typedef Ptr<Operationrec> OperationrecPtr;
+
+struct Page {
+ Uint32 pageWord[ZWORDS_ON_PAGE];
+};
+typedef Ptr<Page> PagePtr;
+
+ /* ****************************** PAGE RANGE RECORD ************************** */
+ /* PAGE RANGES AND BASE PAGE ID. EACH RANGE HAS A CORRESPONDING BASE PAGE ID */
+ /* THAT IS USED TO CALCULATE REAL PAGE ID FROM A FRAGMENT PAGE ID AND A TABLE */
+ /* REFERENCE. */
+ /* THE PAGE RANGES ARE ORGANISED IN A B-TREE FASHION WHERE THE VARIABLE TYPE */
+ /* SPECIFIES IF A LEAF NODE HAS BEEN REACHED. IF A LEAF NODE HAS BEEN REACHED */
+ /* THEN BASE_PAGE_ID IS THE BASE_PAGE_ID OF THE SET OF PAGES THAT WAS */
+ /* ALLOCATED IN THAT RANGE. OTHERWISE BASE_PAGE_ID IS THE POINTER TO THE NEXT */
+ /* PAGE_RANGE RECORD. */
+ /* *************************************************************************** */
+struct PageRange {
+ Uint32 startRange[4]; /* START OF RANGE */
+ Uint32 endRange[4]; /* END OF THIS RANGE */
+ Uint32 basePageId[4]; /* BASE PAGE ID. */
+/*---- VARIABLE BASE_PAGE_ID2 (4) 8 DS NEEDED WHEN SUPPORTING 40 BIT PAGE ID -------*/
+ Uint8 type[4]; /* TYPE OF BASE PAGE ID */
+ Uint32 nextFree; /* NEXT FREE PAGE RANGE RECORD */
+ Uint32 parentPtr; /* THE PARENT TO THE PAGE RANGE REC IN THE B-TREE */
+ Uint8 currentIndexPos;
+};
+typedef Ptr<PageRange> PageRangePtr;
+
+ /* *********** PENDING UNDO WRITE INFO ********** */
+ /* THIS RECORD HOLDS INFORMATION NEEDED DURING */
+ /* A FILE OPEN OPERATION */
+ /* IF THE FILE OPEN IS A PART OF A CHECKPOINT THE */
+ /* CHECKPOINT_INFO_P WILL HOLD A POINTER TO THE */
+ /* CHECKPOINT_INFOR_PTR RECORD */
+ /* IF IT IS A PART OF RESTART THE PFO_RESTART_INFO*/
+ /* ELEMENT WILL POINT TO A RESTART INFO RECORD */
+ /* ********************************************** */
+struct PendingFileOpenInfo {
+ Uint32 pfoNextRec;
+ State pfoOpenType;
+ Uint32 pfoCheckpointInfoP;
+ Uint32 pfoRestartInfoP;
+};
+typedef Ptr<PendingFileOpenInfo> PendingFileOpenInfoPtr;
+
+struct RestartInfoRecord {
+ Uint32 sriNextRec;
+ State sriState; /* BLOCKREFERENCE TO THE REQUESTING BLOCK */
+ Uint32 sriUserptr; /* USERPOINTER TO THE REQUESTING BLOCK */
+ Uint32 sriDataBufferSegmentP; /* POINTER TO A DISK BUFFER SEGMENT POINTER (DATA) */
+ Uint32 sriDataFileHandle; /* FILE HANDLE TO THE OPEN DATA FILE */
+ Uint32 sriCheckpointVersion; /* CHECKPOINT VERSION TO RESTART FROM */
+ Uint32 sriFragid; /* FRAGMENT ID */
+ Uint32 sriFragP; /* FRAGMENT POINTER */
+ Uint32 sriTableId; /* TABLE ID */
+ Uint32 sriLocalLogInfoP; /* POINTER TO A LOCAL LOG INFO RECORD */
+ Uint32 sriNumDataPages; /* NUMBER OF DATA PAGES TO READ */
+ Uint32 sriCurDataPageFromBuffer; /* THE CHECKPOINT IS COMPLETED */
+ BlockReference sriBlockref;
+};
+typedef Ptr<RestartInfoRecord> RestartInfoRecordPtr;
+
+ /* ************* TRIGGER DATA ************* */
+ /* THIS RECORD FORMS LISTS OF ACTIVE */
+ /* TRIGGERS FOR EACH TABLE. */
+ /* THE RECORDS ARE MANAGED BY A TRIGGER */
+ /* POOL wHERE A TRIGGER RECORD IS SEIZED */
+ /* WHEN A TRIGGER IS ACTIVATED AND RELEASED */
+ /* WHEN THE TRIGGER IS DEACTIVATED. */
+ /* **************************************** */
+struct TupTriggerData {
+
+ /**
+ * Trigger id, used by DICT/TRIX to identify the trigger
+ */
+ Uint32 triggerId;
+
+ /**
+ * Index id is needed for ordered index.
+ */
+ Uint32 indexId;
+
+ /**
+ * Trigger type etc, defines what the trigger is used for
+ */
+ TriggerType::Value triggerType;
+ TriggerActionTime::Value triggerActionTime;
+ TriggerEvent::Value triggerEvent;
+ /**
+ * Receiver block
+ */
+ Uint32 m_receiverBlock;
+
+ /**
+ * Monitor all replicas, i.e. trigger will fire on all nodes where tuples
+ * are stored
+ */
+ bool monitorReplicas;
+
+ /**
+ * Monitor all attributes, the trigger monitors all changes to attributes
+ * in the table
+ */
+ bool monitorAllAttributes;
+
+ /**
+ * Send only changed attributes at trigger firing time.
+ */
+ bool sendOnlyChangedAttributes;
+
+ /**
+ * Send also before values at trigger firing time.
+ */
+ bool sendBeforeValues;
+
+ /**
+ * Attribute mask, defines what attributes are to be monitored
+ * Can be seen as a compact representation of SQL column name list
+ */
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attributeMask;
+
+ /**
+ * Next ptr (used in pool/list)
+ */
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+
+ /**
+ * Prev pointer (used in list)
+ */
+ Uint32 prevList;
+
+ inline void print(NdbOut & s) const { s << "[TriggerData = " << triggerId << "]"; };
+};
+
+typedef Ptr<TupTriggerData> TriggerPtr;
+
+/**
+ * Pool of trigger data record
+ */
+ArrayPool<TupTriggerData> c_triggerPool;
+
+ /* ************ TABLE RECORD ************ */
+ /* THIS RECORD FORMS A LIST OF TABLE */
+ /* REFERENCE INFORMATION. ONE RECORD */
+ /* PER TABLE REFERENCE. */
+ /* ************************************** */
+struct Tablerec {
+ Tablerec(ArrayPool<TupTriggerData> & triggerPool) :
+ afterInsertTriggers(triggerPool),
+ afterDeleteTriggers(triggerPool),
+ afterUpdateTriggers(triggerPool),
+ subscriptionInsertTriggers(triggerPool),
+ subscriptionDeleteTriggers(triggerPool),
+ subscriptionUpdateTriggers(triggerPool),
+ constraintUpdateTriggers(triggerPool),
+ tuxCustomTriggers(triggerPool)
+ {}
+
+ Bitmask<MAXNROFATTRIBUTESINWORDS> notNullAttributeMask;
+
+ ReadFunction* readFunctionArray;
+ UpdateFunction* updateFunctionArray;
+ CHARSET_INFO** charsetArray;
+
+ Uint32 readKeyArray;
+ Uint32 tabDescriptor;
+ Uint32 attributeGroupDescriptor;
+
+ bool GCPIndicator;
+ bool checksumIndicator;
+
+ Uint16 tupheadsize;
+ Uint16 noOfAttr;
+ Uint16 noOfKeyAttr;
+ Uint16 noOfCharsets;
+ Uint16 noOfNewAttr;
+ Uint16 noOfNullAttr;
+ Uint16 noOfAttributeGroups;
+
+ Uint8 tupChecksumIndex;
+ Uint8 tupNullIndex;
+ Uint8 tupNullWords;
+ Uint8 tupGCPIndex;
+
+ // Lists of trigger data for active triggers
+ ArrayList<TupTriggerData> afterInsertTriggers;
+ ArrayList<TupTriggerData> afterDeleteTriggers;
+ ArrayList<TupTriggerData> afterUpdateTriggers;
+ ArrayList<TupTriggerData> subscriptionInsertTriggers;
+ ArrayList<TupTriggerData> subscriptionDeleteTriggers;
+ ArrayList<TupTriggerData> subscriptionUpdateTriggers;
+ ArrayList<TupTriggerData> constraintUpdateTriggers;
+
+ // List of ordered indexes
+ ArrayList<TupTriggerData> tuxCustomTriggers;
+
+ Uint32 fragid[2 * MAX_FRAG_PER_NODE];
+ Uint32 fragrec[2 * MAX_FRAG_PER_NODE];
+
+ struct {
+ Uint32 tabUserPtr;
+ Uint32 tabUserRef;
+ } m_dropTable;
+ State tableStatus;
+};
+
+typedef Ptr<Tablerec> TablerecPtr;
+
+struct storedProc {
+ Uint32 storedLinkFirst;
+ Uint32 storedLinkLast;
+ Uint32 storedCounter;
+ Uint32 nextPool;
+ Uint16 storedCode;
+ Uint16 storedProcLength;
+};
+
+typedef Ptr<storedProc> StoredProcPtr;
+
+ArrayPool<storedProc> c_storedProcPool;
+
+/* **************************** TABLE_DESCRIPTOR RECORD ******************************** */
+/* THIS VARIABLE IS USED TO STORE TABLE DESCRIPTIONS. A TABLE DESCRIPTION IS STORED AS A */
+/* CONTIGUOS ARRAY IN THIS VARIABLE. WHEN A NEW TABLE IS ADDED A CHUNK IS ALLOCATED IN */
+/* THIS RECORD. WHEN ATTRIBUTES ARE ADDED TO THE TABLE, A NEW CHUNK OF PROPER SIZE IS */
+/* ALLOCATED AND ALL DATA IS COPIED TO THIS NEW CHUNK AND THEN THE OLD CHUNK IS PUT IN */
+/* THE FREE LIST. EACH TABLE IS DESCRIBED BY A NUMBER OF TABLE DESCRIPTIVE ATTRIBUTES */
+/* AND A NUMBER OF ATTRIBUTE DESCRIPTORS AS SHOWN IN FIGURE BELOW */
+/* */
+/* WHEN ALLOCATING A TABLE DESCRIPTOR THE SIZE IS ALWAYS A MULTIPLE OF 16 WORDS. */
+/* */
+/* ---------------------------------------------- */
+/* | TRAILER USED FOR ALLOC/DEALLOC | */
+/* ---------------------------------------------- */
+/* | TABLE DESCRIPTIVE ATTRIBUTES | */
+/* ---------------------------------------------- */
+/* | ATTRIBUTE DESCRIPTION 1 | */
+/* ---------------------------------------------- */
+/* | ATTRIBUTE DESCRIPTION 2 | */
+/* ---------------------------------------------- */
+/* | | */
+/* | | */
+/* | | */
+/* ---------------------------------------------- */
+/* | ATTRIBUTE DESCRIPTION N | */
+/* ---------------------------------------------- */
+/* */
+/* THE TABLE DESCRIPTIVE ATTRIBUTES CONTAINS THE FOLLOWING ATTRIBUTES: */
+/* */
+/* ---------------------------------------------- */
+/* | HEADER (TYPE OF INFO) | */
+/* ---------------------------------------------- */
+/* | SIZE OF WHOLE CHUNK (INCL. TRAILER) | */
+/* ---------------------------------------------- */
+/* | TABLE IDENTITY | */
+/* ---------------------------------------------- */
+/* | FRAGMENT IDENTITY | */
+/* ---------------------------------------------- */
+/* | NUMBER OF ATTRIBUTES | */
+/* ---------------------------------------------- */
+/* | SIZE OF FIXED ATTRIBUTES | */
+/* ---------------------------------------------- */
+/* | NUMBER OF NULL FIELDS | */
+/* ---------------------------------------------- */
+/* | NOT USED | */
+/* ---------------------------------------------- */
+/* */
+/* THESE ATTRIBUTES ARE ALL ONE R-VARIABLE IN THE RECORD. */
+/* NORMALLY ONLY ONE TABLE DESCRIPTOR IS USED. DURING SCHEMA CHANGES THERE COULD */
+/* HOWEVER EXIST MORE THAN ONE TABLE DESCRIPTION SINCE THE SCHEMA CHANGE OF VARIOUS */
+/* FRAGMENTS ARE NOT SYNCHRONISED. THIS MEANS THAT ALTHOUGH THE SCHEMA HAS CHANGED */
+/* IN ALL FRAGMENTS, BUT THE FRAGMENTS HAVE NOT REMOVED THE ATTRIBUTES IN THE SAME */
+/* TIME-FRAME. THEREBY SOME ATTRIBUTE INFORMATION MIGHT DIFFER BETWEEN FRAGMENTS. */
+/* EXAMPLES OF ATTRIBUTES THAT MIGHT DIFFER ARE SIZE OF FIXED ATTRIBUTES, NUMBER OF */
+/* ATTRIBUTES, FIELD START WORD, START BIT. */
+/* */
+/* AN ATTRIBUTE DESCRIPTION CONTAINS THE FOLLOWING ATTRIBUTES: */
+/* */
+/* ---------------------------------------------- */
+/* | Field Type, 4 bits (LSB Bits) | */
+/* ---------------------------------------------- */
+/* | Attribute Size, 4 bits | */
+/* ---------------------------------------------- */
+/* | NULL indicator 1 bit | */
+/* ---------------------------------------------- */
+/* | Indicator if TUP stores attr. 1 bit | */
+/* ---------------------------------------------- */
+/* | Not used 6 bits | */
+/* ---------------------------------------------- */
+/* | No. of elements in fixed array 16 bits | */
+/* ---------------------------------------------- */
+/* ---------------------------------------------- */
+/* | Field Start Word, 21 bits (LSB Bits) | */
+/* ---------------------------------------------- */
+/* | NULL Bit, 11 bits | */
+/* ---------------------------------------------- */
+/* */
+/* THE ATTRIBUTE SIZE CAN BE 1,2,4,8,16,32,64 AND 128 BITS. */
+/* */
+/* THE UNUSED PARTS OF THE RECORDS ARE PUT IN A LINKED LIST OF FREE PARTS. EACH OF */
+/* THOSE FREE PARTS HAVE THREE RECORDS ASSIGNED AS SHOWN IN THIS STRUCTURE */
+/* ALL FREE PARTS ARE SET INTO A CHUNK LIST WHERE EACH CHUNK IS AT LEAST 16 WORDS */
+/* */
+/* ---------------------------------------------- */
+/* | HEADER = RNIL | */
+/* ---------------------------------------------- */
+/* | SIZE OF FREE AREA | */
+/* ---------------------------------------------- */
+/* | POINTER TO PREVIOUS FREE AREA | */
+/* ---------------------------------------------- */
+/* | POINTER TO NEXT FREE AREA | */
+/* ---------------------------------------------- */
+/* */
+/* IF THE POINTER TO THE NEXT AREA IS RNIL THEN THIS IS THE LAST FREE AREA. */
+/* */
+/*****************************************************************************************/
+struct TableDescriptor {
+ Uint32 tabDescr;
+};
+typedef Ptr<TableDescriptor> TableDescriptorPtr;
+
+struct HostBuffer {
+ bool inPackedList;
+ Uint32 packetLenTA;
+ Uint32 noOfPacketsTA;
+ Uint32 packetBufferTA[30];
+};
+typedef Ptr<HostBuffer> HostBufferPtr;
+
+ /* **************** UNDO PAGE RECORD ******************* */
+ /* THIS RECORD FORMS AN UNDO PAGE CONTAINING A NUMBER OF */
+ /* DATA WORDS. CURRENTLY THERE ARE 2048 WORDS ON A PAGE */
+ /* EACH OF 32 BITS (4 BYTES) WHICH FORMS AN UNDO PAGE */
+ /* WITH A TOTAL OF 8192 BYTES */
+ /* ***************************************************** */
+struct UndoPage {
+ Uint32 undoPageWord[ZWORDS_ON_PAGE]; /* 32 KB */
+};
+typedef Ptr<UndoPage> UndoPagePtr;
+
+ /*
+ * Build index operation record.
+ */
+ struct BuildIndexRec {
+ // request cannot use signal class due to extra members
+ Uint32 m_request[BuildIndxReq::SignalLength];
+ Uint32 m_triggerPtrI; // the index trigger
+ Uint32 m_fragNo; // fragment number under Tablerec
+ Uint32 m_pageId; // logical fragment page id
+ Uint32 m_tupleNo; // tuple number on page (pageIndex >> 1)
+ BuildIndxRef::ErrorCode m_errorCode;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+ typedef Ptr<BuildIndexRec> BuildIndexPtr;
+ ArrayPool<BuildIndexRec> c_buildIndexPool;
+ ArrayList<BuildIndexRec> c_buildIndexList;
+ Uint32 c_noOfBuildIndexRec;
+
+public:
+ Dbtup(const class Configuration &);
+ virtual ~Dbtup();
+
+ /*
+ * TUX uses logical tuple address when talking to ACC and LQH.
+ */
+ void tuxGetTupAddr(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32& tupAddr);
+
+ /*
+ * TUX index in TUP has single Uint32 array attribute which stores an
+ * index node. TUX reads and writes the node directly via pointer.
+ */
+ int tuxAllocNode(Signal* signal, Uint32 fragPtrI, Uint32& pageId, Uint32& pageOffset, Uint32*& node);
+ void tuxFreeNode(Signal* signal, Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* node);
+ void tuxGetNode(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32*& node);
+
+ /*
+ * TUX reads primary table attributes for index keys. Tuple is
+ * specified by location of original tuple and version number. Input
+ * is attribute ids in AttributeHeader format. Output is attribute
+ * data with headers. Uses readAttributes with xfrm option set.
+ * Returns number of words or negative (-terrorCode) on error.
+ */
+ int tuxReadAttrs(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32 tupVersion, const Uint32* attrIds, Uint32 numAttrs, Uint32* dataOut);
+
+ /*
+ * TUX reads primary key without headers into an array of words. Used
+ * for md5 summing and when returning keyinfo. Returns number of
+ * words or negative (-terrorCode) on error.
+ */
+ int tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* dataOut, bool xfrmFlag);
+
+ /*
+ * ACC reads primary key without headers into an array of words. At
+ * this point in ACC deconstruction, ACC still uses logical references
+ * to fragment and tuple.
+ */
+ int accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIndex, Uint32* dataOut, bool xfrmFlag);
+
+ /*
+ * TUX checks if tuple is visible to scan.
+ */
+ bool tuxQueryTh(Uint32 fragPtrI, Uint32 tupAddr, Uint32 tupVersion, Uint32 transId1, Uint32 transId2, Uint32 savePointId);
+
+private:
+ BLOCK_DEFINES(Dbtup);
+
+ // Transit signals
+ void execDEBUG_SIG(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+
+ // Received signals
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execSEND_PACKED(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execTUP_LCPREQ(Signal* signal);
+ void execEND_LCPREQ(Signal* signal);
+ void execSTART_RECREQ(Signal* signal);
+ void execMEMCHECKREQ(Signal* signal);
+ void execTUPSEIZEREQ(Signal* signal);
+ void execTUPRELEASEREQ(Signal* signal);
+ void execSTORED_PROCREQ(Signal* signal);
+ void execTUPFRAGREQ(Signal* signal);
+ void execTUP_ADD_ATTRREQ(Signal* signal);
+ void execTUP_COMMITREQ(Signal* signal);
+ void execTUP_ABORTREQ(Signal* signal);
+ void execTUP_SRREQ(Signal* signal);
+ void execTUP_PREPLCPREQ(Signal* signal);
+ void execFSOPENCONF(Signal* signal);
+ void execFSOPENREF(Signal* signal);
+ void execFSCLOSECONF(Signal* signal);
+ void execFSCLOSEREF(Signal* signal);
+ void execFSWRITECONF(Signal* signal);
+ void execFSWRITEREF(Signal* signal);
+ void execFSREADCONF(Signal* signal);
+ void execFSREADREF(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+ void execDROP_TAB_REQ(Signal* signal);
+ void execALTER_TAB_REQ(Signal* signal);
+ void execFSREMOVECONF(Signal* signal);
+ void execFSREMOVEREF(Signal* signal);
+ void execTUP_ALLOCREQ(Signal* signal);
+ void execTUP_DEALLOCREQ(Signal* signal);
+ void execTUP_WRITELOG_REQ(Signal* signal);
+
+ // Ordered index related
+ void execBUILDINDXREQ(Signal* signal);
+ void buildIndex(Signal* signal, Uint32 buildPtrI);
+ void buildIndexReply(Signal* signal, const BuildIndexRec* buildRec);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+// Methods to handle execution of TUPKEYREQ + ATTRINFO.
+//
+// Module Execution Manager
+//
+// The TUPKEYREQ signal is central to this block. This signal is used
+// by everybody that needs to read data residing in DBTUP. The data is
+// read using an interpreter approach.
+//
+// Operations only needing to read execute a simplified version of the
+// interpreter where the only instruction is read Attribute to send.
+// Operations only needing to update the record (insert or update)
+// execute a simplified version of the interpreter where the only
+// instruction is write Attribute.
+//
+// Currently TUPKEYREQ is used in the following situations.
+// 1) Normal transaction execution. Can be any of the types described
+// below.
+// 2) Execution of fragment redo log during system restart.
+// In this situation there will only be normal updates, inserts
+// and deletes performed.
+// 3) A special type of normal transaction execution is to write the
+// records arriving from the primary replica in the node restart
+// processing. This will always be normal write operations which
+// are translated to inserts or updates before arriving to TUP.
+// 4) Scan processing. The scan processing will use normal reads or
+// interpreted reads in their execution. There will be one TUPKEYREQ
+// signal for each record processed.
+// 5) Copy fragment processing. This is a special type of scan used in the
+// primary replica at system restart. It reads the entire reads and
+// converts those to writes to the starting node. In this special case
+// LQH acts as an API node and receives also the ATTRINFO sent in the
+// TRANSID_AI signals.
+//
+// Signal Diagram:
+//
+// In Signals:
+// -----------
+//
+// Logically there is one request TUPKEYREQ which requests to read/write data
+// of one tuple in the database. Since the definition of what to read and write
+// can be bigger than the maximum signal size we segment the signal. The definition
+// of what to read/write/interpreted program is sent before the TUPKEYREQ signal.
+//
+// ---> ATTRINFO
+// ...
+// ---> ATTRINFO
+// ---> TUPKEYREQ
+// The number of ATTRINFO signals can be anything between 0 and upwards.
+// The total size of the ATTRINFO is not allowed to be more than 16384 words.
+// There is always one and only one TUPKEYREQ.
+//
+// Response Signals (successful case):
+//
+// Simple/Dirty Read Operation
+// ---------------------------
+//
+// <---- TRANSID_AI (to API)
+// ...
+// <---- TRANSID_AI (to API)
+// <---- READCONF (to API)
+// <---- TUPKEYCONF (to LQH)
+// There is always exactly one READCONF25 sent last. The number of
+// TRANSID_AI is dependent on how much that was read. The maximum size
+// of the ATTRINFO sent back is 16384 words. The signals are sent
+// directly to the application with an address provided by the
+// TUPKEYREQ signal.
+// A positive response signal is also sent to LQH.
+//
+// Normal Read Operation
+// ---------------------
+//
+// <---- TRANSID_AI (to API)
+// ...
+// <---- TRANSID_AI (to API)
+// <---- TUPKEYCONF (to LQH)
+// The number of TRANSID_AI is dependent on how much that was read.
+// The maximum size of the ATTRINFO sent back is 16384 words. The
+// signals are sent directly to the application with an address
+// provided by the TUPKEYREQ signal.
+// A positive response signal is also sent to LQH.
+//
+// Normal update/insert/delete operation
+// -------------------------------------
+//
+// <---- TUPKEYCONF
+// After successful updating of the tuple LQH is informed of this.
+//
+// Delete with read
+// ----------------
+//
+// Will behave as a normal read although it also prepares the
+// deletion of the tuple.
+//
+// Interpreted Update
+// ------------------
+//
+// <---- TRANSID_AI (to API)
+// ...
+// <---- TRANSID_AI (to API)
+// <---- TUP_ATTRINFO (to LQH)
+// ...
+// <---- TUP_ATTRINFO (to LQH)
+// <---- TUPKEYCONF (to LQH)
+//
+// The interpreted Update contains five sections:
+// The first section performs read Attribute operations
+// that send results back to the API.
+//
+// The second section executes the interpreted program
+// where data from attributes can be updated and it
+// can also read attribute values into the registers.
+//
+// The third section performs unconditional updates of
+// attributes.
+//
+// The fourth section can read the attributes to be sent to the
+// API after updating the record.
+//
+// The fifth section contains subroutines used by the interpreter
+// in the second section.
+//
+// All types of interpreted programs contains the same five sections.
+// The only difference is that only interpreted updates can update
+// attributes. Interpreted inserts are not allowed.
+//
+// Interpreted Updates have to send back the information about the
+// attributes they have updated. This information will be shipped to
+// the log and also to any other replicas. Thus interpreted updates
+// are only performed in the primary replica. The fragment redo log
+// in LQH will contain information so that normal update/inserts/deletes
+// can be performed using TUPKEYREQ.
+//
+// Interpreted Read
+// ----------------
+//
+// From a signalling point of view the Interpreted Read behaves as
+// as a Normal Read. The interpreted Read is often used by Scan's.
+//
+// Interpreted Delete
+// ------------------
+//
+// <---- TUPKEYCONF
+// After successful prepartion to delete the tuple LQH is informed
+// of this.
+//
+// Interpreted Delete with Read
+// ----------------------------
+//
+// From a signalling point of view an interpreted delete with read
+// behaves as a normal read.
+//
+// Continuation after successful case:
+//
+// After a read of any kind the operation record is ready to be used
+// again by a new operation.
+//
+// Any updates, inserts or deletes waits for either of two messages.
+// A commit specifying that the operation is to be performed for real
+// or an abort specifying that the operation is to be rolled back and
+// the record to be restored in its original format.
+//
+// This is handled by the module Transaction Manager.
+//
+// Response Signals (unsuccessful case):
+//
+// <---- TUPKEYREF (to LQH)
+// A signal is sent back to LQH informing about the unsuccessful
+// operation. In this case TUP waits for an abort signal to arrive
+// before the operation record is ready for the next operation.
+// This is handled by the Transaction Manager.
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+
+// *****************************************************************
+// Signal Reception methods.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void execTUPKEYREQ(Signal* signal);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void execATTRINFO(Signal* signal);
+
+// Trigger signals
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void execCREATE_TRIG_REQ(Signal* signal);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void execDROP_TRIG_REQ(Signal* signal);
+
+// *****************************************************************
+// Support methods for ATTRINFO.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void handleATTRINFOforTUPKEYREQ(Signal* signal,
+ Uint32 length,
+ Operationrec * const regOperPtr);
+
+// *****************************************************************
+// Setting up the environment for reads, inserts, updates and deletes.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int handleReadReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr,
+ Page* pagePtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int handleUpdateReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int handleInsertReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int handleDeleteReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int updateStartLab(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr);
+
+// *****************************************************************
+// Interpreter Handling methods.
+// *****************************************************************
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int interpreterStartLab(Signal* signal,
+ Page* const pagePtr,
+ Uint32 TupHeadOffset);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int interpreterNextLab(Signal* signal,
+ Page* const pagePtr,
+ Uint32 TupHeadOffset,
+ Uint32* logMemory,
+ Uint32* mainProgram,
+ Uint32 TmainProgLen,
+ Uint32* subroutineProg,
+ Uint32 TsubroutineLen,
+ Uint32 * tmpArea,
+ Uint32 tmpAreaSz);
+
+// *****************************************************************
+// Signal Sending methods.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void sendReadAttrinfo(Signal* signal,
+ Uint32 TnoOfData,
+ const Operationrec * const regOperPtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void sendLogAttrinfo(Signal* signal,
+ Uint32 TlogSize,
+ Operationrec * const regOperPtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void sendTUPKEYCONF(Signal* signal, Operationrec *
+ const regOperPtr,
+ Uint32 TlogSize);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+// *****************************************************************
+// The methods that perform the actual read and update of attributes
+// in the tuple.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int readAttributes(Page* const pagePtr,
+ Uint32 TupHeadOffset,
+ const Uint32* inBuffer,
+ Uint32 inBufLen,
+ Uint32* outBuffer,
+ Uint32 TmaxRead,
+ bool xfrmFlag);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int readAttributesWithoutHeader(Page* const pagePtr,
+ Uint32 TupHeadOffset,
+ Uint32* inBuffer,
+ Uint32 inBufLen,
+ Uint32* outBuffer,
+ Uint32* attrBuffer,
+ Uint32 TmaxRead);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int updateAttributes(Page* const pagePtr,
+ Uint32 TupHeadOffset,
+ Uint32* inBuffer,
+ Uint32 inBufLen);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHOneWordNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateFixedSizeTHOneWordNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHTwoWordNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateFixedSizeTHTwoWordNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHManyWordNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateFixedSizeTHManyWordNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHOneWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateFixedSizeTHOneWordNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHTwoWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateFixedSizeTHTwoWordNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHManyWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readFixedSizeTHZeroWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateFixedSizeTHManyWordNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readVariableSizedAttr(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateVariableSizedAttr(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readVarSizeUnlimitedNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateVarSizeUnlimitedNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readVarSizeUnlimitedNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateVarSizeUnlimitedNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readBigVarSizeNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateBigVarSizeNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readBigVarSizeNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateBigVarSizeNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readSmallVarSizeNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateSmallVarSizeNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readSmallVarSizeNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateSmallVarSizeNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readDynFixedSize(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateDynFixedSize(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readDynVarSizeUnlimited(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateDynVarSizeUnlimited(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readDynBigVarSize(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateDynBigVarSize(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool readDynSmallVarSize(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool updateDynSmallVarSize(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2);
+
+
+ bool readBitsNULLable(Uint32* outBuffer, AttributeHeader*, Uint32, Uint32);
+ bool updateBitsNULLable(Uint32* inBuffer, Uint32, Uint32);
+ bool readBitsNotNULL(Uint32* outBuffer, AttributeHeader*, Uint32, Uint32);
+ bool updateBitsNotNULL(Uint32* inBuffer, Uint32, Uint32);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ bool nullFlagCheck(Uint32 attrDes2);
+ Uint32 read_psuedo(Uint32 attrId, Uint32* outBuffer);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void setUpQueryRoutines(Tablerec* const regTabPtr);
+
+// *****************************************************************
+// Service methods.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void copyAttrinfo(Signal* signal, Operationrec * const regOperPtr, Uint32* inBuffer);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void initOpConnection(Operationrec* regOperPtr, Fragrecord*);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void initOperationrec(Signal* signal);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int initStoredOperationrec(Operationrec* const regOperPtr,
+ Uint32 storedId);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void insertActiveOpList(Signal* signal,
+ OperationrecPtr regOperPtr,
+ Page * const pagePtr,
+ Uint32 pageOffset);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void linkOpIntoFragList(OperationrecPtr regOperPtr,
+ Fragrecord* const regFragPtr);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void bufferTRANSID_AI(Signal* signal, BlockReference aRef, Uint32 Tlen);
+
+//------------------------------------------------------------------
+// Trigger handling routines
+//------------------------------------------------------------------
+ ArrayList<TupTriggerData>* findTriggerList(Tablerec* table,
+ TriggerType::Value ttype,
+ TriggerActionTime::Value ttime,
+ TriggerEvent::Value tevent);
+
+ bool createTrigger(Tablerec* table, const CreateTrigReq* req);
+
+ Uint32 dropTrigger(Tablerec* table, const DropTrigReq* req);
+
+ void checkImmediateTriggersAfterInsert(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const tablePtr);
+
+ void checkImmediateTriggersAfterUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const tablePtr);
+
+ void checkImmediateTriggersAfterDelete(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const tablePtr);
+
+#if 0
+ void checkDeferredTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr);
+#endif
+ void checkDetachedTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr);
+
+ void fireImmediateTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr);
+
+ void fireDeferredTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr);
+
+ void fireDetachedTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr);
+
+ void executeTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr);
+
+ void executeTrigger(Signal* signal,
+ TupTriggerData* const trigPtr,
+ Operationrec* const regOperPtr);
+
+ bool readTriggerInfo(TupTriggerData* const trigPtr,
+ Operationrec* const regOperPtr,
+ Uint32* const keyBuffer,
+ Uint32& noPrimKey,
+ Uint32* const mainBuffer,
+ Uint32& noMainWords,
+ Uint32* const copyBuffer,
+ Uint32& noCopyWords);
+
+ void sendTrigAttrInfo(Signal* signal,
+ Uint32* data,
+ Uint32 dataLen,
+ bool executeDirect,
+ BlockReference receiverReference);
+
+ Uint32 setAttrIds(Bitmask<MAXNROFATTRIBUTESINWORDS>& attributeMask,
+ Uint32 noOfAttributes,
+ Uint32* inBuffer);
+
+ void sendFireTrigOrd(Signal* signal,
+ Operationrec * const regOperPtr,
+ TupTriggerData* const trigPtr,
+ Uint32 noPrimKeySignals,
+ Uint32 noBeforeSignals,
+ Uint32 noAfterSignals);
+
+ bool primaryKey(Tablerec* const, Uint32);
+
+ // these set terrorCode and return non-zero on error
+
+ int executeTuxInsertTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr);
+
+ int executeTuxUpdateTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr);
+
+ int executeTuxDeleteTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr);
+
+ // these crash the node on error
+
+ void executeTuxCommitTriggers(Signal* signal,
+ Operationrec* regOperPtr,
+ Tablerec* const regTabPtr);
+
+ void executeTuxAbortTriggers(Signal* signal,
+ Operationrec* regOperPtr,
+ Tablerec* const regTabPtr);
+
+// *****************************************************************
+// Error Handling routines.
+// *****************************************************************
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ int TUPKEY_abort(Signal* signal, int error_type);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+ void tupkeyErrorLab(Signal* signal);
+
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+// Methods to handle execution of TUP_COMMITREQ + TUP_ABORTREQ.
+//
+// Module Transaction Manager
+//
+// The Transaction Manager module is responsible for the commit
+// and abort of operations started by the Execution Manager.
+//
+// Commit Operation:
+// ----------------
+//
+// Failures in commit processing is not allowed since that would
+// leave the database in an unreliable state. Thus the only way
+// to handle failures in commit processing is to crash the node.
+//
+// TUP_COMMITREQ can only be received in the wait state after a
+// successful TUPKEYREQ which was not a read operation.
+//
+// Commit of Delete:
+// -----------------
+//
+// This will actually perform the deletion of the record unless
+// other operations also are connected to the record. In this case
+// we will set the delete state on the record that becomes the owner
+// of the record.
+//
+// Commit of Update:
+// ----------------
+//
+// We will release the copy record where the original record was kept.
+// Also here we will take special care if more operations are updating
+// the record simultaneously.
+//
+// Commit of Insert:
+// -----------------
+//
+// Will simply reset the state of the operation record.
+//
+// Signal Diagram:
+// ---> TUP_COMMITREQ (from LQH)
+// <---- TUP_COMMITCONF (to LQH)
+//
+//
+// Abort Operation:
+// ----------------
+//
+// Signal Diagram:
+// ---> TUP_ABORTREQ (from LQH)
+// <---- TUP_ABORTCONF (to LQH)
+//
+// Failures in abort processing is not allowed since that would
+// leave the database in an unreliable state. Thus the only way
+// to handle failures in abort processing is to crash the node.
+//
+// Abort messages can arrive at any time. It can arrive even before
+// anything at all have arrived of the operation. It can arrive after
+// receiving a number of ATTRINFO but before TUPKEYREQ has been received.
+// It must arrive after that we sent TUPKEYREF in response to TUPKEYREQ
+// and finally it can arrive after successfully performing the TUPKEYREQ
+// in all cases including the read case.
+//------------------------------------------------------------------
+//------------------------------------------------------------------
+
+#if 0
+ void checkPages(Fragrecord* const regFragPtr);
+#endif
+ void printoutTuplePage(Uint32 fragid, Uint32 pageid, Uint32 printLimit);
+
+ bool checkUpdateOfPrimaryKey(Uint32* updateBuffer, Tablerec* const regTabPtr);
+
+ void setNullBits(Page* const regPage, Tablerec* const regTabPtr, Uint32 pageOffset);
+ bool checkNullAttributes(Operationrec* const, Tablerec* const);
+ bool getPage(PagePtr& pagePtr,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr);
+
+ bool getPageLastCommitted(Operationrec* const regOperPtr,
+ Operationrec* const leaderOpPtr);
+
+ bool getPageThroughSavePoint(Operationrec* const regOperPtr,
+ Operationrec* const leaderOpPtr);
+
+ Uint32 calculateChecksum(Page* const pagePtr, Uint32 tupHeadOffset, Uint32 tupHeadSize);
+ void setChecksum(Page* const pagePtr, Uint32 tupHeadOffset, Uint32 tupHeadSize);
+
+ void commitSimple(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr);
+
+ void commitRecord(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr);
+
+ void setTupleStatesSetOpType(Operationrec* const regOperPtr,
+ Page* const pagePtr,
+ Uint32& opType,
+ OperationrecPtr& firstOpPtr);
+
+ void findBeforeValueOperation(OperationrecPtr& befOpPtr,
+ OperationrecPtr firstOpPtr);
+
+ void calculateChangeMask(Page* const PagePtr,
+ Tablerec* const regTabPtr,
+ Uint32 pageOffset,
+ Bitmask<MAXNROFATTRIBUTESINWORDS>& attributeMask);
+
+ void updateGcpId(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr);
+
+ void abortUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr);
+ void commitUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr);
+
+ void setTupleStateOnPreviousOps(Uint32 prevOpIndex);
+ void copyMem(Signal* signal, Uint32 sourceIndex, Uint32 destIndex);
+
+ void freeAllAttrBuffers(Operationrec* const regOperPtr);
+ void freeAttrinbufrec(Uint32 anAttrBufRec);
+ void removeActiveOpList(Operationrec* const regOperPtr);
+
+ void updatePackedList(Signal* signal, Uint16 ahostIndex);
+
+ void setUpDescriptorReferences(Uint32 descriptorReference,
+ Tablerec* const regTabPtr,
+ const Uint32* offset);
+ void setUpKeyArray(Tablerec* const regTabPtr);
+ bool addfragtotab(Tablerec* const regTabPtr, Uint32 fragId, Uint32 fragIndex);
+ void deleteFragTab(Tablerec* const regTabPtr, Uint32 fragId);
+ void abortAddFragOp(Signal* signal);
+ void releaseTabDescr(Tablerec* const regTabPtr);
+ void getFragmentrec(FragrecordPtr& regFragPtr, Uint32 fragId, Tablerec* const regTabPtr);
+
+ void initialiseRecordsLab(Signal* signal, Uint32 switchData, Uint32, Uint32);
+ void initializeAttrbufrec();
+ void initializeCheckpointInfoRec();
+ void initializeDiskBufferSegmentRecord();
+ void initializeFragoperrec();
+ void initializeFragrecord();
+ void initializeHostBuffer();
+ void initializeLocalLogInfo();
+ void initializeOperationrec();
+ void initializePendingFileOpenInfoRecord();
+ void initializeRestartInfoRec();
+ void initializeTablerec();
+ void initializeTabDescr();
+ void initializeUndoPage();
+
+ void initTab(Tablerec* const regTabPtr);
+
+ void startphase3Lab(Signal* signal, Uint32 config1, Uint32 config2);
+
+ void fragrefuseLab(Signal* signal, FragoperrecPtr fragOperPtr);
+ void fragrefuse1Lab(Signal* signal, FragoperrecPtr fragOperPtr);
+ void fragrefuse2Lab(Signal* signal, FragoperrecPtr fragOperPtr, FragrecordPtr regFragPtr);
+ void fragrefuse3Lab(Signal* signal,
+ FragoperrecPtr fragOperPtr,
+ FragrecordPtr regFragPtr,
+ Tablerec* const regTabPtr,
+ Uint32 fragId);
+ void fragrefuse4Lab(Signal* signal,
+ FragoperrecPtr fragOperPtr,
+ FragrecordPtr regFragPtr,
+ Tablerec* const regTabPtr,
+ Uint32 fragId);
+ void addattrrefuseLab(Signal* signal,
+ FragrecordPtr regFragPtr,
+ FragoperrecPtr fragOperPtr,
+ Tablerec* const regTabPtr,
+ Uint32 fragId);
+
+
+ void checkLcpActiveBufferPage(Uint32 minPageNotWrittenInCheckpoint, DiskBufferSegmentInfoPtr dbsiPtr);
+ void lcpWriteListDataPageSegment(Signal* signal,
+ DiskBufferSegmentInfoPtr dbsiPtr,
+ CheckpointInfoPtr ciPtr,
+ bool flushFlag);
+ void lcpFlushLogLab(Signal* signal, CheckpointInfoPtr ciPtr);
+ void lcpClosedDataFileLab(Signal* signal, CheckpointInfoPtr ciPtr);
+ void lcpEndconfLab(Signal* signal);
+ void lcpSaveDataPageLab(Signal* signal, Uint32 ciIndex);
+ void lcpCompletedLab(Signal* signal, Uint32 ciIndex);
+ void lcpFlushRestartInfoLab(Signal* signal, Uint32 ciIndex);
+ void lcpSaveCopyListLab(Signal* signal, CheckpointInfoPtr ciPtr);
+
+ void sendFSREMOVEREQ(Signal* signal, TablerecPtr tabPtr);
+ void releaseFragment(Signal* signal, Uint32 tableId);
+
+ void allocDataBufferSegment(Signal* signal, DiskBufferSegmentInfoPtr& dbsiPtr);
+ void allocRestartUndoBufferSegment(Signal* signal, DiskBufferSegmentInfoPtr& dbsiPtr, LocalLogInfoPtr lliPtr);
+ void freeDiskBufferSegmentRecord(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr);
+ void freeUndoBufferPages(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr);
+
+ void releaseCheckpointInfoRecord(CheckpointInfoPtr ciPtr);
+ void releaseDiskBufferSegmentRecord(DiskBufferSegmentInfoPtr dbsiPtr);
+ void releaseFragoperrec(FragoperrecPtr fragOperPtr);
+ void releaseFragrec(FragrecordPtr regFragPtr);
+ void releasePendingFileOpenInfoRecord(PendingFileOpenInfoPtr pfoPtr);
+ void releaseRestartInfoRecord(RestartInfoRecordPtr riPtr);
+
+ void seizeDiskBufferSegmentRecord(DiskBufferSegmentInfoPtr& dbsiPtr);
+ void seizeCheckpointInfoRecord(CheckpointInfoPtr& ciPtr);
+ void seizeFragoperrec(FragoperrecPtr& fragOperPtr);
+ void seizeFragrecord(FragrecordPtr& regFragPtr);
+ void seizeOpRec(OperationrecPtr& regOperPtr);
+ void seizePendingFileOpenInfoRecord(PendingFileOpenInfoPtr& pfoiPtr);
+ void seizeRestartInfoRecord(RestartInfoRecordPtr& riPtr);
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ void rfrClosedDataFileLab(Signal* signal, Uint32 restartIndex);
+ void rfrCompletedLab(Signal* signal, RestartInfoRecordPtr riPtr);
+ void rfrInitRestartInfoLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr);
+ void rfrLoadDataPagesLab(Signal* signal, RestartInfoRecordPtr riPtr, DiskBufferSegmentInfoPtr dbsiPtr);
+ void rfrReadFirstUndoSegment(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr);
+ void rfrReadNextDataSegment(Signal* signal, RestartInfoRecordPtr riPtr, DiskBufferSegmentInfoPtr dbsiPtr);
+ void rfrReadNextUndoSegment(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr);
+ void rfrReadRestartInfoLab(Signal* signal, RestartInfoRecordPtr riPtr);
+ void rfrReadSecondUndoLogLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr);
+
+ void startExecUndoLogLab(Signal* signal, Uint32 lliIndex);
+ void readExecUndoLogLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr);
+ void closeExecUndoLogLab(Signal* signal, LocalLogInfoPtr lliPtr);
+ void endExecUndoLogLab(Signal* signal, Uint32 lliIndex);
+
+ struct XlcStruct {
+ Uint32 PageId;
+ Uint32 PageIndex;
+ Uint32 LogRecordType;
+ Uint32 FragId;
+ FragrecordPtr FragPtr;
+ LocalLogInfoPtr LliPtr;
+ DiskBufferSegmentInfoPtr DbsiPtr;
+ UndoPagePtr UPPtr;
+ TablerecPtr TabPtr;
+ };
+
+ void xlcGetNextRecordLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr);
+ void xlcRestartCompletedLab(Signal* signal);
+
+ void xlcCopyData(XlcStruct& xlcStruct, Uint32 pageOffset, Uint32 noOfWords, PagePtr pagePtr);
+ void xlcGetLogHeader(XlcStruct& xlcStruct);
+ Uint32 xlcGetLogWord(XlcStruct& xlcStruct);
+
+ void xlcAbortInsert(Signal* signal, XlcStruct& xlcStruct);
+ void xlcAbortUpdate(Signal* signal, XlcStruct& xlcStruct);
+ void xlcDeleteTh(XlcStruct& xlcStruct);
+ void xlcIndicateNoOpActive(XlcStruct& xlcStruct);
+ void xlcInsertTh(XlcStruct& xlcStruct);
+ void xlcTableDescriptor(XlcStruct& xlcStruct);
+ void xlcUndoLogPageHeader(XlcStruct& xlcStruct);
+ void xlcUpdateTh(XlcStruct& xlcStruct);
+ void xlcUpdateGCI(XlcStruct& xlcStruct);
+
+
+ void cprAddData(Signal* signal,
+ Fragrecord* const regFragPtr,
+ Uint32 pageIndex,
+ Uint32 noOfWords,
+ Uint32 startOffset);
+ void cprAddGCIUpdate(Signal* signal,
+ Uint32 prevGCI,
+ Fragrecord* const regFragPtr);
+ void cprAddLogHeader(Signal* signal,
+ LocalLogInfo* const lliPtr,
+ Uint32 recordType,
+ Uint32 tableId,
+ Uint32 fragId);
+ void cprAddUndoLogPageHeader(Signal* signal,
+ Page* const regPagePtr,
+ Fragrecord* const regFragPtr);
+ void cprAddUndoLogRecord(Signal* signal,
+ Uint32 recordType,
+ Uint32 pageId,
+ Uint32 pageIndex,
+ Uint32 tableId,
+ Uint32 fragId,
+ Uint32 localLogIndex);
+ void cprAddAbortUpdate(Signal* signal,
+ LocalLogInfo* const lliPtr,
+ Operationrec* const regOperPtr);
+ void cprAddUndoLogWord(Signal* signal,
+ LocalLogInfo* const lliPtr,
+ Uint32 undoWord);
+ bool isUndoLoggingNeeded(Fragrecord* const regFragPtr, Uint32 pageId);
+ bool isUndoLoggingActive(Fragrecord* const regFragPtr);
+ bool isUndoLoggingBlocked(Fragrecord* const regFragPtr);
+ bool isPageUndoLogged(Fragrecord* const regFragPtr, Uint32 pageId);
+
+ void seizeUndoBufferSegment(Signal* signal, UndoPagePtr& regUndoPagePtr);
+ void lcpWriteUndoSegment(Signal* signal, LocalLogInfo* const lliPtr, bool flushFlag);
+
+
+ void deleteScanProcedure(Signal* signal, Operationrec* regOperPtr);
+ void copyProcedure(Signal* signal,
+ TablerecPtr regTabPtr,
+ Operationrec* regOperPtr);
+ void scanProcedure(Signal* signal,
+ Operationrec* regOperPtr,
+ Uint32 lenAttrInfo);
+ void storedSeizeAttrinbufrecErrorLab(Signal* signal,
+ Operationrec* regOperPtr);
+ bool storedProcedureAttrInfo(Signal* signal,
+ Operationrec* regOperPtr,
+ Uint32 length,
+ Uint32 firstWord,
+ bool copyProc);
+
+//-----------------------------------------------------------------------------
+// Table Descriptor Memory Manager
+//-----------------------------------------------------------------------------
+
+// Public methods
+ Uint32 getTabDescrOffsets(const Tablerec* regTabPtr, Uint32* offset);
+ Uint32 allocTabDescr(const Tablerec* regTabPtr, Uint32* offset);
+ void freeTabDescr(Uint32 retRef, Uint32 retNo);
+ Uint32 getTabDescrWord(Uint32 index);
+ void setTabDescrWord(Uint32 index, Uint32 word);
+
+// Private methods
+ Uint32 sizeOfReadFunction();
+ void removeTdArea(Uint32 tabDesRef, Uint32 list);
+ void insertTdArea(Uint32 sizeOfChunk, Uint32 tabDesRef, Uint32 list);
+ Uint32 itdaMergeTabDescr(Uint32 retRef, Uint32 retNo);
+
+//------------------------------------------------------------------------------------------------------
+// Page Memory Manager
+//------------------------------------------------------------------------------------------------------
+
+// Public methods
+ void allocConsPages(Uint32 noOfPagesToAllocate,
+ Uint32& noOfPagesAllocated,
+ Uint32& allocPageRef);
+ void returnCommonArea(Uint32 retPageRef, Uint32 retNo);
+ void initializePage();
+
+// Private methods
+ void removeCommonArea(Uint32 remPageRef, Uint32 list);
+ void insertCommonArea(Uint32 insPageRef, Uint32 list);
+ void findFreeLeftNeighbours(Uint32& allocPageRef, Uint32& noPagesAllocated, Uint32 noPagesToAllocate);
+ void findFreeRightNeighbours(Uint32& allocPageRef, Uint32& noPagesAllocated, Uint32 noPagesToAllocate);
+ Uint32 nextHigherTwoLog(Uint32 input);
+
+// Private data
+ Uint32 cfreepageList[16];
+
+//------------------------------------------------------------------------------------------------------
+// Page Mapper, convert logical page id's to physical page id's
+// The page mapper also handles the pages allocated to the fragment.
+//------------------------------------------------------------------------------------------------------
+//
+// Public methods
+ Uint32 getRealpid(Fragrecord* const regFragPtr, Uint32 logicalPageId);
+ Uint32 getNoOfPages(Fragrecord* const regFragPtr);
+ void initPageRangeSize(Uint32 size);
+ bool insertPageRangeTab(Fragrecord* const regFragPtr,
+ Uint32 startPageId,
+ Uint32 noPages);
+ void releaseFragPages(Fragrecord* const regFragPtr);
+ void initFragRange(Fragrecord* const regFragPtr);
+ void initializePageRange();
+ Uint32 getEmptyPage(Fragrecord* const regFragPtr);
+ Uint32 allocFragPages(Fragrecord* const regFragPtr, Uint32 noOfPagesAllocated);
+
+// Private methods
+ Uint32 leafPageRangeFull(Fragrecord* const regFragPtr, PageRangePtr currPageRangePtr);
+ void releasePagerange(PageRangePtr regPRPtr);
+ void seizePagerange(PageRangePtr& regPageRangePtr);
+ void errorHandler(Uint32 errorCode);
+ void allocMoreFragPages(Fragrecord* const regFragPtr);
+
+// Private data
+ Uint32 cfirstfreerange;
+ PageRange *pageRange;
+ Uint32 c_noOfFreePageRanges;
+ Uint32 cnoOfPageRangeRec;
+
+//------------------------------------------------------------------------------------------------------
+// Fixed Allocator
+// Allocates and deallocates tuples of fixed size on a fragment.
+//------------------------------------------------------------------------------------------------------
+//
+// Public methods
+ bool allocTh(Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Uint32 pageType,
+ Signal* signal,
+ Uint32& pageOffset,
+ PagePtr& pagePtr);
+
+ void freeThSr(Tablerec* const regTabPtr,
+ Page* const regPagePtr,
+ Uint32 freePageOffset);
+
+ void freeTh(Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Signal* signal,
+ Page* const regPagePtr,
+ Uint32 freePageOffset);
+
+ void getThAtPageSr(Page* const regPagePtr,
+ Uint32& pageOffset);
+
+// Private methods
+ void convertThPage(Uint32 Tupheadsize,
+ Page* const regPagePtr);
+
+ void getThAtPage(Fragrecord* const regFragPtr,
+ Page* const regPagePtr,
+ Signal* signal,
+ Uint32& pageOffset);
+
+ void getEmptyPageThCopy(Fragrecord* const regFragPtr,
+ Signal* signal,
+ Page* const regPagePtr);
+
+ void getEmptyPageTh(Fragrecord* const regFragPtr,
+ Signal* signal,
+ Page* const regPagePtr);
+
+//------------------------------------------------------------------------------------------------------
+// Temporary variables used for storing commonly used variables in certain modules
+//------------------------------------------------------------------------------------------------------
+
+ FragrecordPtr fragptr;
+ OperationrecPtr operPtr;
+ TablerecPtr tabptr;
+
+// readAttributes and updateAttributes module
+ Uint32 tCheckOffset;
+ Uint32 tMaxRead;
+ Uint32 tOutBufIndex;
+ Uint32* tTupleHeader;
+ bool tXfrmFlag;
+
+// updateAttributes module
+ Uint32 tInBufIndex;
+ Uint32 tInBufLen;
+
+ Uint32 terrorCode;
+
+//------------------------------------------------------------------------------------------------------
+// Common stored variables. Variables that have a valid value always.
+//------------------------------------------------------------------------------------------------------
+ Uint32 cnoOfLcpRec;
+ Uint32 cnoOfParallellUndoFiles;
+ Uint32 cnoOfUndoPage;
+
+ Attrbufrec *attrbufrec;
+ Uint32 cfirstfreeAttrbufrec;
+ Uint32 cnoOfAttrbufrec;
+ Uint32 cnoFreeAttrbufrec;
+
+ CheckpointInfo *checkpointInfo;
+ Uint32 cfirstfreeLcp;
+
+ DiskBufferSegmentInfo *diskBufferSegmentInfo;
+ Uint32 cfirstfreePdx;
+ Uint32 cnoOfConcurrentWriteOp;
+
+ Fragoperrec *fragoperrec;
+ Uint32 cfirstfreeFragopr;
+ Uint32 cnoOfFragoprec;
+
+ Fragrecord *fragrecord;
+ Uint32 cfirstfreefrag;
+ Uint32 cnoOfFragrec;
+
+ HostBuffer *hostBuffer;
+
+ LocalLogInfo *localLogInfo;
+ Uint32 cnoOfLocalLogInfo;
+
+ Uint32 cfirstfreeOprec;
+ Operationrec *operationrec;
+ Uint32 cnoOfOprec;
+
+ Page *page;
+ Uint32 cnoOfPage;
+ Uint32 cnoOfAllocatedPages;
+
+ PendingFileOpenInfo *pendingFileOpenInfo;
+ Uint32 cfirstfreePfo;
+ Uint32 cnoOfConcurrentOpenOp;
+
+ RestartInfoRecord *restartInfoRecord;
+ Uint32 cfirstfreeSri;
+ Uint32 cnoOfRestartInfoRec;
+
+ Tablerec *tablerec;
+ Uint32 cnoOfTablerec;
+
+ TableDescriptor *tableDescriptor;
+ Uint32 cnoOfTabDescrRec;
+
+ UndoPage *undoPage;
+ Uint32 cfirstfreeUndoSeg;
+ Int32 cnoFreeUndoSeg;
+
+
+
+ Uint32 cnoOfDataPagesToDiskWithoutSynch;
+
+ Uint32 cdata[32];
+ Uint32 cdataPages[16];
+ Uint32 cpackedListIndex;
+ Uint32 cpackedList[MAX_NODES];
+ Uint32 cfreeTdList[16];
+ Uint32 clastBitMask;
+ Uint32 clblPageCounter;
+ Uint32 clblPagesPerTick;
+ Uint32 clblPagesPerTickAfterSr;
+ BlockReference clqhBlockref;
+ Uint32 clqhUserpointer;
+ Uint32 cminusOne;
+ BlockReference cndbcntrRef;
+ Uint32 cundoFileVersion;
+ BlockReference cownref;
+ Uint32 cownNodeId;
+ Uint32 czero;
+
+ // A little bit bigger to cover overwrites in copy algorithms (16384 real size).
+#define ZATTR_BUFFER_SIZE 16384
+ Uint32 clogMemBuffer[ZATTR_BUFFER_SIZE + 16];
+ Uint32 coutBuffer[ZATTR_BUFFER_SIZE + 16];
+ Uint32 cinBuffer[ZATTR_BUFFER_SIZE + 16];
+ Uint32 totNoOfPagesAllocated;
+
+ // Trigger variables
+ Uint32 c_maxTriggersPerTable;
+
+ // Counters for num UNDO log records executed
+ Uint32 cSrUndoRecords[9];
+
+ STATIC_CONST(MAX_PARALLELL_TUP_SRREQ = 2);
+ Uint32 c_sr_free_page_0;
+
+ Uint32 c_errorInsert4000TableId;
+
+ void initGlobalTemporaryVars();
+ void reportMemoryUsage(Signal* signal, int incDec);
+
+
+#ifdef VM_TRACE
+ struct Th {
+ Uint32 data[1];
+ };
+ friend class NdbOut& operator<<(NdbOut&, const Operationrec&);
+ friend class NdbOut& operator<<(NdbOut&, const Th&);
+#endif
+};
+
+inline
+bool Dbtup::isUndoLoggingNeeded(Fragrecord* const regFragPtr,
+ Uint32 pageId)
+{
+ if ((regFragPtr->checkpointVersion != RNIL) &&
+ (pageId >= regFragPtr->minPageNotWrittenInCheckpoint) &&
+ (pageId < regFragPtr->maxPageWrittenInCheckpoint)) {
+ return true;
+ }//if
+ return false;
+}//Dbtup::isUndoLoggingNeeded()
+
+inline
+bool Dbtup::isUndoLoggingActive(Fragrecord* const regFragPtr)
+{
+ if (regFragPtr->checkpointVersion != RNIL) {
+ return true;
+ }//if
+ return false;
+}//Dbtup::isUndoLoggingNeeded()
+
+inline
+bool Dbtup::isUndoLoggingBlocked(Fragrecord* const regFragPtr)
+{
+ if ((regFragPtr->checkpointVersion != RNIL) &&
+ (cnoFreeUndoSeg < ZMIN_PAGE_LIMIT_TUPKEYREQ)) {
+ return true;
+ }//if
+ return false;
+}//Dbtup::isUndoLoggingNeeded()
+
+inline
+bool Dbtup::isPageUndoLogged(Fragrecord* const regFragPtr,
+ Uint32 pageId)
+{
+ if ((pageId >= regFragPtr->minPageNotWrittenInCheckpoint) &&
+ (pageId < regFragPtr->maxPageWrittenInCheckpoint)) {
+ return true;
+ }//if
+ return false;
+}//Dbtup::isUndoLoggingNeeded()
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp
new file mode 100644
index 00000000000..e9043a8b52d
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp
@@ -0,0 +1,473 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(9000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(9000 + __LINE__); }
+
+void Dbtup::freeAllAttrBuffers(Operationrec* const regOperPtr)
+{
+ if (regOperPtr->storedProcedureId == ZNIL) {
+ ljam();
+ freeAttrinbufrec(regOperPtr->firstAttrinbufrec);
+ } else {
+ StoredProcPtr storedPtr;
+ c_storedProcPool.getPtr(storedPtr, (Uint32)regOperPtr->storedProcedureId);
+ ndbrequire(storedPtr.p->storedCode == ZSCAN_PROCEDURE);
+ ljam();
+ storedPtr.p->storedCounter--;
+ regOperPtr->storedProcedureId = ZNIL;
+ }//if
+ regOperPtr->firstAttrinbufrec = RNIL;
+ regOperPtr->lastAttrinbufrec = RNIL;
+}//Dbtup::freeAllAttrBuffers()
+
+void Dbtup::freeAttrinbufrec(Uint32 anAttrBuf)
+{
+ Uint32 Ttemp;
+ AttrbufrecPtr localAttrBufPtr;
+ Uint32 RnoFree = cnoFreeAttrbufrec;
+ localAttrBufPtr.i = anAttrBuf;
+ while (localAttrBufPtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(localAttrBufPtr, cnoOfAttrbufrec, attrbufrec);
+ Ttemp = localAttrBufPtr.p->attrbuf[ZBUF_NEXT];
+ localAttrBufPtr.p->attrbuf[ZBUF_NEXT] = cfirstfreeAttrbufrec;
+ cfirstfreeAttrbufrec = localAttrBufPtr.i;
+ localAttrBufPtr.i = Ttemp;
+ RnoFree++;
+ }//if
+ cnoFreeAttrbufrec = RnoFree;
+}//Dbtup::freeAttrinbufrec()
+
+/* ----------------------------------------------------------------- */
+/* ----------- ABORT THIS PART OF THE TRANSACTION ------------------ */
+/* ----------------------------------------------------------------- */
+void Dbtup::execTUP_ABORTREQ(Signal* signal)
+{
+ OperationrecPtr regOperPtr;
+ FragrecordPtr regFragPtr;
+ TablerecPtr regTabPtr;
+
+ ljamEntry();
+ regOperPtr.i = signal->theData[0];
+ ptrCheckGuard(regOperPtr, cnoOfOprec, operationrec);
+ ndbrequire((regOperPtr.p->transstate == STARTED) ||
+ (regOperPtr.p->transstate == TOO_MUCH_AI) ||
+ (regOperPtr.p->transstate == ERROR_WAIT_TUPKEYREQ) ||
+ (regOperPtr.p->transstate == IDLE));
+ if (regOperPtr.p->optype == ZREAD) {
+ ljam();
+ freeAllAttrBuffers(regOperPtr.p);
+ initOpConnection(regOperPtr.p, 0);
+ return;
+ }//if
+
+ regTabPtr.i = regOperPtr.p->tableRef;
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+
+ regFragPtr.i = regOperPtr.p->fragmentPtr;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+
+ // XXX should be integrated into the code that comes after
+ if (!regTabPtr.p->tuxCustomTriggers.isEmpty() &&
+ regOperPtr.p->tupleState == NO_OTHER_OP) {
+ ljam();
+ executeTuxAbortTriggers(signal,
+ regOperPtr.p,
+ regTabPtr.p);
+ OperationrecPtr loopOpPtr;
+ loopOpPtr.i = regOperPtr.p->prevActiveOp;
+ while (loopOpPtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ if (loopOpPtr.p->tupleState != ALREADY_ABORTED) {
+ ljam();
+ executeTuxAbortTriggers(signal,
+ loopOpPtr.p,
+ regTabPtr.p);
+ }
+ loopOpPtr.i = loopOpPtr.p->prevActiveOp;
+ }
+ }
+
+ Uint32 prevActiveOp = regOperPtr.p->prevActiveOp;
+ removeActiveOpList(regOperPtr.p);
+ if (regOperPtr.p->tupleState == NO_OTHER_OP) {
+ if (prevActiveOp == RNIL) {
+ ljam();
+ abortUpdate(signal, regOperPtr.p, regFragPtr.p, regTabPtr.p);
+ } else { //prevActiveOp != RNIL
+ setTupleStateOnPreviousOps(prevActiveOp);
+ if (regOperPtr.p->optype == ZDELETE) {
+ ljam();
+ OperationrecPtr prevOpPtr;
+ prevOpPtr.i = prevActiveOp;
+ ptrCheckGuard(prevOpPtr, cnoOfOprec, operationrec);
+ ndbrequire(prevOpPtr.p->realPageIdC != RNIL);
+ ndbrequire(prevOpPtr.p->optype == ZINSERT);
+ abortUpdate(signal, prevOpPtr.p, regFragPtr.p, regTabPtr.p);
+ } else {
+ jam();
+ abortUpdate(signal, regOperPtr.p, regFragPtr.p, regTabPtr.p);
+ }//if
+ }//if
+ } else {
+ ndbrequire(regOperPtr.p->tupleState == ALREADY_ABORTED);
+ commitUpdate(signal, regOperPtr.p, regFragPtr.p, regTabPtr.p);
+ }//if
+ initOpConnection(regOperPtr.p, regFragPtr.p);
+}//execTUP_ABORTREQ()
+
+void Dbtup::setTupleStateOnPreviousOps(Uint32 prevOpIndex)
+{
+ OperationrecPtr loopOpPtr;
+ loopOpPtr.i = prevOpIndex;
+ do {
+ ljam();
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ loopOpPtr.p->tupleState = ALREADY_ABORTED;
+ loopOpPtr.i = loopOpPtr.p->prevActiveOp;
+ } while (loopOpPtr.i != RNIL);
+}//Dbtup::setTupleStateOnPreviousOps()
+
+/* ---------------------------------------------------------------- */
+/* ------------ PERFORM AN ABORT OF AN UPDATE OPERATION ----------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::abortUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr)
+{
+ /* RESTORE THE ORIGINAL DATA */
+ /* THE OPER_PTR ALREADY CONTAINS BOTH THE PAGE AND THE COPY PAGE */
+ if (regOperPtr->realPageIdC != RNIL) {
+ ljam();
+ /***********************/
+ /* CHECKPOINT SPECIFIC */
+ /***********************/
+ if (isUndoLoggingNeeded(regFragPtr, regOperPtr->fragPageIdC)) {
+ if (regOperPtr->undoLogged) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THE UPDATE WAS MADE AFTER THE LOCAL CHECKPOINT STARTED. */
+/* THUS THE ORIGINAL TUPLE WILL BE RESTORED BY A LOG RECORD */
+/* CREATED WHEN UPDATING. THUS IT IS ENOUGH TO LOG THE UNDO */
+/* OF THE COPY RELEASE == INSERT THE COPY TUPLE HEADER WITH */
+/* NO DATA. */
+/* ---------------------------------------------------------------- */
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_INSERT_TH_NO_DATA,
+ regOperPtr->fragPageIdC,
+ regOperPtr->pageIndexC,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ } else {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THE UPDATE WAS MADE BEFORE THE LOCAL CHECKPOINT STARTED. */
+/* THE TUPLE WILL THUS BE RESTORED BY COPYING FROM THE COPY. */
+/* THUS WE DO NOT NEED TO RESTORE THE DATA IN THE ORIGINAL. */
+/* WE DO HOWEVER NEED TO ENSURE THAT THE COPY CONTAINS THE */
+/* CORRECT DATA. */
+/* ---------------------------------------------------------------- */
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_INSERT_TH,
+ regOperPtr->fragPageIdC,
+ regOperPtr->pageIndexC,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ cprAddData(signal,
+ regFragPtr,
+ regOperPtr->realPageIdC,
+ regTabPtr->tupheadsize,
+ regOperPtr->pageOffsetC);
+ }//if
+ }//if
+ Uint32 rpid = regOperPtr->realPageId;
+ Uint32 rpid_copy = regOperPtr->realPageIdC;
+ Uint32 offset = regOperPtr->pageOffset;
+ Uint32 offset_copy = regOperPtr->pageOffsetC;
+ Uint32 tuple_size = regTabPtr->tupheadsize;
+ Uint32 end = offset + tuple_size;
+ Uint32 end_copy = offset_copy + tuple_size;
+ ndbrequire(rpid < cnoOfPage &&
+ rpid_copy < cnoOfPage &&
+ end <= ZWORDS_ON_PAGE &&
+ end_copy <= ZWORDS_ON_PAGE);
+ void* Tdestination = (void*)&page[rpid].pageWord[offset + 1];
+ const void* Tsource = (void*)&page[rpid_copy].pageWord[offset_copy + 1];
+ MEMCOPY_NO_WORDS(Tdestination, Tsource, (tuple_size - 1));
+ {
+ PagePtr pagePtr;
+
+ pagePtr.i = rpid_copy;
+ ptrAss(pagePtr, page);
+ freeTh(regFragPtr,
+ regTabPtr,
+ signal,
+ pagePtr.p,
+ offset_copy);
+ }
+ regOperPtr->realPageIdC = RNIL;
+ regOperPtr->fragPageIdC = RNIL;
+ regOperPtr->pageOffsetC = ZNIL;
+ regOperPtr->pageIndexC = ZNIL;
+ }//if
+}//Dbtup::abortUpdate()
+
+/* **************************************************************** */
+/* ********************** TRANSACTION ERROR MODULE **************** */
+/* **************************************************************** */
+int Dbtup::TUPKEY_abort(Signal* signal, int error_type)
+{
+ switch(error_type) {
+ case 0:
+ ndbrequire(false);
+ break;
+// Not used currently
+
+ case 1:
+//tmupdate_alloc_error:
+ ljam();
+ break;
+
+ case 2:
+ ndbrequire(false);
+ break;
+// Not used currently
+
+ break;
+
+ case 3:
+//tmupdate_alloc_error:
+ ljam();
+ break;
+
+ case 4:
+//Trying to read non-existing attribute identity
+ ljam();
+ terrorCode = ZATTRIBUTE_ID_ERROR;
+ break;
+
+ case 6:
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ break;
+
+ case 7:
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ break;
+
+ case 8:
+ ljam();
+ terrorCode = ZATTR_INTERPRETER_ERROR;
+ break;
+
+ case 9:
+ ljam();
+//Trying to read non-existing attribute identity
+ ljam();
+ terrorCode = ZATTRIBUTE_ID_ERROR;
+ break;
+
+ case 11:
+ ljam();
+ terrorCode = ZATTR_INTERPRETER_ERROR;
+ break;
+
+ case 12:
+ ljam();
+ ndbrequire(false);
+ break;
+
+ case 13:
+ ljam();
+ ndbrequire(false);
+ break;
+
+ case 14:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 15:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 16:
+ ljam();
+ terrorCode = ZTRY_TO_UPDATE_ERROR;
+ break;
+
+ case 17:
+ ljam();
+ terrorCode = ZNO_ILLEGAL_NULL_ATTR;
+ break;
+
+ case 18:
+ ljam();
+ terrorCode = ZNOT_NULL_ATTR;
+ break;
+
+ case 19:
+ ljam();
+ terrorCode = ZTRY_TO_UPDATE_ERROR;
+ break;
+
+ case 20:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 21:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 22:
+ ljam();
+ terrorCode = ZTOTAL_LEN_ERROR;
+ break;
+
+ case 23:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 24:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 25:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 26:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 27:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 28:
+ ljam();
+ terrorCode = ZREGISTER_INIT_ERROR;
+ break;
+
+ case 29:
+ ljam();
+ break;
+
+ case 30:
+ ljam();
+ terrorCode = ZCALL_ERROR;
+ break;
+
+ case 31:
+ ljam();
+ terrorCode = ZSTACK_OVERFLOW_ERROR;
+ break;
+
+ case 32:
+ ljam();
+ terrorCode = ZSTACK_UNDERFLOW_ERROR;
+ break;
+
+ case 33:
+ ljam();
+ terrorCode = ZNO_INSTRUCTION_ERROR;
+ break;
+
+ case 34:
+ ljam();
+ terrorCode = ZOUTSIDE_OF_PROGRAM_ERROR;
+ break;
+
+ case 35:
+ ljam();
+ terrorCode = ZTOO_MANY_INSTRUCTIONS_ERROR;
+ break;
+
+ case 36:
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ break;
+
+ case 37:
+ ljam();
+ terrorCode = ZTEMPORARY_RESOURCE_FAILURE;
+ break;
+
+ case 38:
+ ljam();
+ terrorCode = ZTEMPORARY_RESOURCE_FAILURE;
+ break;
+
+ case 39:
+ ljam();
+ if (operPtr.p->transstate == TOO_MUCH_AI) {
+ ljam();
+ terrorCode = ZTOO_MUCH_ATTRINFO_ERROR;
+ } else if (operPtr.p->transstate == ERROR_WAIT_TUPKEYREQ) {
+ ljam();
+ terrorCode = ZSEIZE_ATTRINBUFREC_ERROR;
+ } else {
+ ndbrequire(false);
+ }//if
+ break;
+
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ tupkeyErrorLab(signal);
+ return -1;
+}//Dbtup::TUPKEY_abort()
+
+void Dbtup::tupkeyErrorLab(Signal* signal)
+{
+ Operationrec * const regOperPtr = operPtr.p;
+
+ freeAllAttrBuffers(regOperPtr);
+ abortUpdate(signal, regOperPtr, fragptr.p, tabptr.p);
+ removeActiveOpList(regOperPtr);
+ initOpConnection(regOperPtr, fragptr.p);
+ regOperPtr->transstate = IDLE;
+ regOperPtr->tupleState = NO_OTHER_OP;
+ TupKeyRef * const tupKeyRef = (TupKeyRef *)signal->getDataPtrSend();
+
+ tupKeyRef->userRef = regOperPtr->userpointer;
+ tupKeyRef->errorCode = terrorCode;
+ sendSignal(regOperPtr->userblockref, GSN_TUPKEYREF, signal,
+ TupKeyRef::SignalLength, JBB);
+ return;
+}//Dbtup::tupkeyErrorLab()
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupBuffer.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupBuffer.cpp
new file mode 100644
index 00000000000..6527864135b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupBuffer.cpp
@@ -0,0 +1,273 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <signaldata/TransIdAI.hpp>
+
+#define ljam() { jamLine(2000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(2000 + __LINE__); }
+
+void Dbtup::execSEND_PACKED(Signal* signal)
+{
+ Uint16 hostId;
+ Uint32 i;
+ Uint32 TpackedListIndex = cpackedListIndex;
+ ljamEntry();
+ for (i = 0; i < TpackedListIndex; i++) {
+ ljam();
+ hostId = cpackedList[i];
+ ndbrequire((hostId - 1) < (MAX_NODES - 1)); // Also check not zero
+ Uint32 TpacketTA = hostBuffer[hostId].noOfPacketsTA;
+ if (TpacketTA != 0) {
+ ljam();
+ BlockReference TBref = numberToRef(API_PACKED, hostId);
+ Uint32 TpacketLen = hostBuffer[hostId].packetLenTA;
+ MEMCOPY_NO_WORDS(&signal->theData[0],
+ &hostBuffer[hostId].packetBufferTA[0],
+ TpacketLen);
+ sendSignal(TBref, GSN_TRANSID_AI, signal, TpacketLen, JBB);
+ hostBuffer[hostId].noOfPacketsTA = 0;
+ hostBuffer[hostId].packetLenTA = 0;
+ }//if
+ hostBuffer[hostId].inPackedList = false;
+ }//for
+ cpackedListIndex = 0;
+}//Dbtup::execSEND_PACKED()
+
+void Dbtup::bufferTRANSID_AI(Signal* signal, BlockReference aRef,
+ Uint32 Tlen)
+{
+ if(Tlen == 3)
+ return;
+
+ Uint32 hostId = refToNode(aRef);
+ Uint32 Theader = ((refToBlock(aRef) << 16)+(Tlen-3));
+
+ ndbrequire(hostId < MAX_NODES);
+ Uint32 TpacketLen = hostBuffer[hostId].packetLenTA;
+ Uint32 TnoOfPackets = hostBuffer[hostId].noOfPacketsTA;
+ Uint32 sig0 = signal->theData[0];
+ Uint32 sig1 = signal->theData[1];
+ Uint32 sig2 = signal->theData[2];
+
+ BlockReference TBref = numberToRef(API_PACKED, hostId);
+
+ if ((Tlen + TpacketLen + 1) <= 25) {
+// ----------------------------------------------------------------
+// There is still space in the buffer. We will copy it into the
+// buffer.
+// ----------------------------------------------------------------
+ ljam();
+ updatePackedList(signal, hostId);
+ } else if (false && TnoOfPackets == 1) {
+// ----------------------------------------------------------------
+// The buffer is full and there was only one packet buffered. We
+// will send this as a normal signal.
+// ----------------------------------------------------------------
+ Uint32 TnewRef = numberToRef((hostBuffer[hostId].packetBufferTA[0] >> 16),
+ hostId);
+ MEMCOPY_NO_WORDS(&signal->theData[0],
+ &hostBuffer[hostId].packetBufferTA[1],
+ TpacketLen - 1);
+ sendSignal(TnewRef, GSN_TRANSID_AI, signal, (TpacketLen - 1), JBB);
+ TpacketLen = 0;
+ TnoOfPackets = 0;
+ } else {
+// ----------------------------------------------------------------
+// The buffer is full but at least two packets. Send those in
+// packed form.
+// ----------------------------------------------------------------
+ MEMCOPY_NO_WORDS(&signal->theData[0],
+ &hostBuffer[hostId].packetBufferTA[0],
+ TpacketLen);
+ sendSignal(TBref, GSN_TRANSID_AI, signal, TpacketLen, JBB);
+ TpacketLen = 0;
+ TnoOfPackets = 0;
+ }//if
+// ----------------------------------------------------------------
+// Copy the signal into the buffer
+// ----------------------------------------------------------------
+ hostBuffer[hostId].packetBufferTA[TpacketLen + 0] = Theader;
+ hostBuffer[hostId].packetBufferTA[TpacketLen + 1] = sig0;
+ hostBuffer[hostId].packetBufferTA[TpacketLen + 2] = sig1;
+ hostBuffer[hostId].packetBufferTA[TpacketLen + 3] = sig2;
+ hostBuffer[hostId].noOfPacketsTA = TnoOfPackets + 1;
+ hostBuffer[hostId].packetLenTA = Tlen + TpacketLen + 1;
+ MEMCOPY_NO_WORDS(&hostBuffer[hostId].packetBufferTA[TpacketLen + 4],
+ &signal->theData[25],
+ Tlen - 3);
+}//Dbtup::bufferTRANSID_AI()
+
+void Dbtup::updatePackedList(Signal* signal, Uint16 hostId)
+{
+ if (hostBuffer[hostId].inPackedList == false) {
+ Uint32 TpackedListIndex = cpackedListIndex;
+ ljam();
+ hostBuffer[hostId].inPackedList = true;
+ cpackedList[TpackedListIndex] = hostId;
+ cpackedListIndex = TpackedListIndex + 1;
+ }//if
+}//Dbtup::updatePackedList()
+
+/* ---------------------------------------------------------------- */
+/* ----------------------- SEND READ ATTRINFO --------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::sendReadAttrinfo(Signal* signal,
+ Uint32 ToutBufIndex,
+ const Operationrec * const regOperPtr)
+{
+ const BlockReference recBlockref = regOperPtr->recBlockref;
+ const Uint32 sig0 = regOperPtr->tcOperationPtr;
+ const Uint32 sig1 = regOperPtr->transid1;
+ const Uint32 sig2 = regOperPtr->transid2;
+
+ const Uint32 block = refToBlock(recBlockref);
+ const Uint32 nodeId = refToNode(recBlockref);
+
+ bool connectedToNode = getNodeInfo(nodeId).m_connected;
+ const Uint32 type = getNodeInfo(nodeId).m_type;
+ bool is_api = (type >= NodeInfo::API && type <= NodeInfo::REP);
+ bool old_dest = (getNodeInfo(nodeId).m_version < MAKE_VERSION(3,5,0));
+ const Uint32 TpacketTA = hostBuffer[nodeId].noOfPacketsTA;
+ const Uint32 TpacketLen = hostBuffer[nodeId].packetLenTA;
+
+ if (ERROR_INSERTED(4006) && (nodeId != getOwnNodeId())){
+ // Use error insert to turn routing on
+ ljam();
+ connectedToNode = false;
+ }
+
+ TransIdAI * transIdAI = (TransIdAI *)signal->getDataPtrSend();
+ transIdAI->connectPtr = sig0;
+ transIdAI->transId[0] = sig1;
+ transIdAI->transId[1] = sig2;
+
+ if (connectedToNode){
+ /**
+ * Own node -> execute direct
+ */
+ if(nodeId != getOwnNodeId()){
+ ljam();
+
+ /**
+ * Send long sig
+ */
+ if(ToutBufIndex >= 22 && is_api && !old_dest) {
+ ljam();
+ /**
+ * Flush buffer so that order is maintained
+ */
+ if (TpacketTA != 0) {
+ ljam();
+ BlockReference TBref = numberToRef(API_PACKED, nodeId);
+ MEMCOPY_NO_WORDS(&signal->theData[0],
+ &hostBuffer[nodeId].packetBufferTA[0],
+ TpacketLen);
+ sendSignal(TBref, GSN_TRANSID_AI, signal, TpacketLen, JBB);
+ hostBuffer[nodeId].noOfPacketsTA = 0;
+ hostBuffer[nodeId].packetLenTA = 0;
+ transIdAI->connectPtr = sig0;
+ transIdAI->transId[0] = sig1;
+ transIdAI->transId[1] = sig2;
+ }//if
+ LinearSectionPtr ptr[3];
+ ptr[0].p = &signal->theData[25];
+ ptr[0].sz = ToutBufIndex;
+ sendSignal(recBlockref, GSN_TRANSID_AI, signal, 3, JBB, ptr, 1);
+ return;
+ }
+
+ /**
+ * short sig + api -> buffer
+ */
+#ifndef NDB_NO_DROPPED_SIGNAL
+ if (ToutBufIndex < 22 && is_api){
+ ljam();
+ bufferTRANSID_AI(signal, recBlockref, 3+ToutBufIndex);
+ return;
+ }//if
+#endif
+
+ /**
+ * rest -> old send sig
+ */
+ Uint32 * src = signal->theData+25;
+ if(ToutBufIndex >= 22){
+ do {
+ ljam();
+ MEMCOPY_NO_WORDS(&signal->theData[3], src, 22);
+ sendSignal(recBlockref, GSN_TRANSID_AI, signal, 25, JBB);
+ ToutBufIndex -= 22;
+ src += 22;
+ } while(ToutBufIndex >= 22);
+ }
+
+ if(ToutBufIndex > 0){
+ ljam();
+ MEMCOPY_NO_WORDS(&signal->theData[3], src, ToutBufIndex);
+ sendSignal(recBlockref, GSN_TRANSID_AI, signal, 3+ToutBufIndex, JBB);
+ }
+ return;
+ }
+ EXECUTE_DIRECT(block, GSN_TRANSID_AI, signal, 3 + ToutBufIndex);
+ ljamEntry();
+ return;
+ }
+
+ /**
+ * If this node does not have a direct connection
+ * to the receiving node we want to send the signals
+ * routed via the node that controls this read
+ */
+ Uint32 routeBlockref = regOperPtr->coordinatorTC;
+
+ if(true){ // TODO is_api && !old_dest){
+ ljam();
+ transIdAI->attrData[0] = recBlockref;
+ LinearSectionPtr ptr[3];
+ ptr[0].p = &signal->theData[25];
+ ptr[0].sz = ToutBufIndex;
+ sendSignal(routeBlockref, GSN_TRANSID_AI_R, signal, 4, JBB, ptr, 1);
+ return;
+ }
+
+ /**
+ * Fill in a TRANSID_AI signal, use last word to store
+ * final destination and send it to route node
+ * as signal TRANSID_AI_R (R as in Routed)
+ */
+ Uint32 tot = ToutBufIndex;
+ Uint32 sent = 0;
+ Uint32 maxLen = TransIdAI::DataLength - 1;
+ while (sent < tot) {
+ ljam();
+ Uint32 dataLen = (tot - sent > maxLen) ? maxLen : tot - sent;
+ Uint32 sigLen = dataLen + TransIdAI::HeaderLength + 1;
+ MEMCOPY_NO_WORDS(&transIdAI->attrData,
+ &signal->theData[25+sent],
+ dataLen);
+ // Set final destination in last word
+ transIdAI->attrData[dataLen] = recBlockref;
+
+ sendSignal(routeBlockref, GSN_TRANSID_AI_R,
+ signal, sigLen, JBB);
+ sent += dataLen;
+ }
+}//Dbtup::sendReadAttrinfo()
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp
new file mode 100644
index 00000000000..cbd56c3281f
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp
@@ -0,0 +1,586 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <signaldata/TupCommit.hpp>
+
+#define ljam() { jamLine(5000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(5000 + __LINE__); }
+
+void Dbtup::execTUP_WRITELOG_REQ(Signal* signal)
+{
+ jamEntry();
+ OperationrecPtr loopOpPtr;
+ loopOpPtr.i = signal->theData[0];
+ Uint32 gci = signal->theData[1];
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ while (loopOpPtr.p->nextActiveOp != RNIL) {
+ ljam();
+ loopOpPtr.i = loopOpPtr.p->nextActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ }//while
+ do {
+ Uint32 blockNo = refToBlock(loopOpPtr.p->userblockref);
+ ndbrequire(loopOpPtr.p->transstate == STARTED);
+ signal->theData[0] = loopOpPtr.p->userpointer;
+ signal->theData[1] = gci;
+ if (loopOpPtr.p->prevActiveOp == RNIL) {
+ ljam();
+ EXECUTE_DIRECT(blockNo, GSN_LQH_WRITELOG_REQ, signal, 2);
+ return;
+ }//if
+ ljam();
+ EXECUTE_DIRECT(blockNo, GSN_LQH_WRITELOG_REQ, signal, 2);
+ jamEntry();
+ loopOpPtr.i = loopOpPtr.p->prevActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ } while (true);
+}//Dbtup::execTUP_WRITELOG_REQ()
+
+void Dbtup::execTUP_DEALLOCREQ(Signal* signal)
+{
+ TablerecPtr regTabPtr;
+ FragrecordPtr regFragPtr;
+
+ jamEntry();
+
+ Uint32 fragId = signal->theData[0];
+ regTabPtr.i = signal->theData[1];
+ Uint32 fragPageId = signal->theData[2];
+ Uint32 pageIndex = signal->theData[3];
+
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+ getFragmentrec(regFragPtr, fragId, regTabPtr.p);
+ ndbrequire(regFragPtr.p != NULL);
+
+ PagePtr pagePtr;
+ pagePtr.i = getRealpid(regFragPtr.p, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageIndexScaled = pageIndex >> 1;
+ ndbrequire((pageIndex & 1) == 0);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE +
+ (regTabPtr.p->tupheadsize * pageIndexScaled);
+//---------------------------------------------------
+/* --- Deallocate a tuple as requested by ACC --- */
+//---------------------------------------------------
+ if (isUndoLoggingNeeded(regFragPtr.p, fragPageId)) {
+ ljam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_INSERT_TH,
+ fragPageId,
+ pageIndex,
+ regTabPtr.i,
+ fragId,
+ regFragPtr.p->checkpointVersion);
+ cprAddData(signal,
+ regFragPtr.p,
+ pagePtr.i,
+ regTabPtr.p->tupheadsize,
+ pageOffset);
+ }//if
+ {
+ freeTh(regFragPtr.p,
+ regTabPtr.p,
+ signal,
+ pagePtr.p,
+ pageOffset);
+ }
+}
+
+/* ---------------------------------------------------------------- */
+/* ------------ PERFORM A COMMIT ON AN UPDATE OPERATION ---------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::commitUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr)
+{
+ if (regOperPtr->realPageIdC != RNIL) {
+ if (isUndoLoggingNeeded(regFragPtr, regOperPtr->fragPageIdC)) {
+/* ------------------------------------------------------------------------ */
+/* IF THE COPY WAS CREATED WITHIN THIS CHECKPOINT WE ONLY HAVE */
+/* TO LOG THE CREATION OF THE COPY. IF HOWEVER IT WAS CREATED BEFORE SAVE */
+/* THIS CHECKPOINT, WE HAVE TO THE DATA AS WELL. */
+/* ------------------------------------------------------------------------ */
+ if (regOperPtr->undoLogged) {
+ ljam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_INSERT_TH_NO_DATA,
+ regOperPtr->fragPageIdC,
+ regOperPtr->pageIndexC,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ } else {
+ ljam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_INSERT_TH,
+ regOperPtr->fragPageIdC,
+ regOperPtr->pageIndexC,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ cprAddData(signal,
+ regFragPtr,
+ regOperPtr->realPageIdC,
+ regTabPtr->tupheadsize,
+ regOperPtr->pageOffsetC);
+ }//if
+ }//if
+
+ PagePtr copyPagePtr;
+ copyPagePtr.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(copyPagePtr, cnoOfPage, page);
+ freeTh(regFragPtr,
+ regTabPtr,
+ signal,
+ copyPagePtr.p,
+ (Uint32)regOperPtr->pageOffsetC);
+ regOperPtr->realPageIdC = RNIL;
+ regOperPtr->fragPageIdC = RNIL;
+ regOperPtr->pageOffsetC = ZNIL;
+ regOperPtr->pageIndexC = ZNIL;
+ }//if
+}//Dbtup::commitUpdate()
+
+void
+Dbtup::commitSimple(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr)
+{
+ operPtr.p = regOperPtr;
+ fragptr.p = regFragPtr;
+ tabptr.p = regTabPtr;
+
+ // Checking detached triggers
+ checkDetachedTriggers(signal,
+ regOperPtr,
+ regTabPtr);
+
+ removeActiveOpList(regOperPtr);
+ if (regOperPtr->optype == ZUPDATE) {
+ ljam();
+ commitUpdate(signal, regOperPtr, regFragPtr, regTabPtr);
+ if (regTabPtr->GCPIndicator) {
+ updateGcpId(signal, regOperPtr, regFragPtr, regTabPtr);
+ }//if
+ } else if (regOperPtr->optype == ZINSERT) {
+ ljam();
+ if (regTabPtr->GCPIndicator) {
+ updateGcpId(signal, regOperPtr, regFragPtr, regTabPtr);
+ }//if
+ } else {
+ ndbrequire(regOperPtr->optype == ZDELETE);
+ }//if
+}//Dbtup::commitSimple()
+
+void Dbtup::removeActiveOpList(Operationrec* const regOperPtr)
+{
+ if (regOperPtr->inActiveOpList == ZTRUE) {
+ OperationrecPtr raoOperPtr;
+ regOperPtr->inActiveOpList = ZFALSE;
+ if (regOperPtr->prevActiveOp != RNIL) {
+ ljam();
+ raoOperPtr.i = regOperPtr->prevActiveOp;
+ ptrCheckGuard(raoOperPtr, cnoOfOprec, operationrec);
+ raoOperPtr.p->nextActiveOp = regOperPtr->nextActiveOp;
+ } else {
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ ndbrequire(regOperPtr->pageOffset < ZWORDS_ON_PAGE);
+ pagePtr.p->pageWord[regOperPtr->pageOffset] = regOperPtr->nextActiveOp;
+ }//if
+ if (regOperPtr->nextActiveOp != RNIL) {
+ ljam();
+ raoOperPtr.i = regOperPtr->nextActiveOp;
+ ptrCheckGuard(raoOperPtr, cnoOfOprec, operationrec);
+ raoOperPtr.p->prevActiveOp = regOperPtr->prevActiveOp;
+ }//if
+ regOperPtr->prevActiveOp = RNIL;
+ regOperPtr->nextActiveOp = RNIL;
+ }//if
+}//Dbtup::removeActiveOpList()
+
+/* ---------------------------------------------------------------- */
+/* INITIALIZATION OF ONE CONNECTION RECORD TO PREPARE FOR NEXT OP. */
+/* ---------------------------------------------------------------- */
+void Dbtup::initOpConnection(Operationrec* regOperPtr,
+ Fragrecord * fragPtrP)
+{
+ Uint32 RinFragList = regOperPtr->inFragList;
+ regOperPtr->transstate = IDLE;
+ regOperPtr->currentAttrinbufLen = 0;
+ regOperPtr->optype = ZREAD;
+ if (RinFragList == ZTRUE) {
+ OperationrecPtr tropNextLinkPtr;
+ OperationrecPtr tropPrevLinkPtr;
+/*----------------------------------------------------------------- */
+/* TO ENSURE THAT WE HAVE SUCCESSFUL ABORTS OF FOLLOWING */
+/* OPERATIONS WHICH NEVER STARTED WE SET THE OPTYPE TO READ. */
+/*----------------------------------------------------------------- */
+/* REMOVE IT FROM THE DOUBLY LINKED LIST ON THE FRAGMENT */
+/*----------------------------------------------------------------- */
+ tropPrevLinkPtr.i = regOperPtr->prevOprecInList;
+ tropNextLinkPtr.i = regOperPtr->nextOprecInList;
+ regOperPtr->inFragList = ZFALSE;
+ if (tropPrevLinkPtr.i == RNIL) {
+ ljam();
+ fragPtrP->firstusedOprec = tropNextLinkPtr.i;
+ } else {
+ ljam();
+ ptrCheckGuard(tropPrevLinkPtr, cnoOfOprec, operationrec);
+ tropPrevLinkPtr.p->nextOprecInList = tropNextLinkPtr.i;
+ }//if
+ if (tropNextLinkPtr.i == RNIL) {
+ fragPtrP->lastusedOprec = tropPrevLinkPtr.i;
+ } else {
+ ptrCheckGuard(tropNextLinkPtr, cnoOfOprec, operationrec);
+ tropNextLinkPtr.p->prevOprecInList = tropPrevLinkPtr.i;
+ }
+ regOperPtr->prevOprecInList = RNIL;
+ regOperPtr->nextOprecInList = RNIL;
+ }//if
+}//Dbtup::initOpConnection()
+
+/* ----------------------------------------------------------------- */
+/* --------------- COMMIT THIS PART OF A TRANSACTION --------------- */
+/* ----------------------------------------------------------------- */
+void Dbtup::execTUP_COMMITREQ(Signal* signal)
+{
+ FragrecordPtr regFragPtr;
+ OperationrecPtr regOperPtr;
+ TablerecPtr regTabPtr;
+
+ TupCommitReq * const tupCommitReq = (TupCommitReq *)signal->getDataPtr();
+
+ ljamEntry();
+ regOperPtr.i = tupCommitReq->opPtr;
+ ptrCheckGuard(regOperPtr, cnoOfOprec, operationrec);
+
+ ndbrequire(regOperPtr.p->transstate == STARTED);
+ regOperPtr.p->gci = tupCommitReq->gci;
+ regOperPtr.p->hashValue = tupCommitReq->hashValue;
+
+ regFragPtr.i = regOperPtr.p->fragmentPtr;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+
+ regTabPtr.i = regOperPtr.p->tableRef;
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+
+ if (!regTabPtr.p->tuxCustomTriggers.isEmpty()) {
+ ljam();
+ executeTuxCommitTriggers(signal,
+ regOperPtr.p,
+ regTabPtr.p);
+ }
+
+ if (regOperPtr.p->tupleState == NO_OTHER_OP) {
+ if ((regOperPtr.p->prevActiveOp == RNIL) &&
+ (regOperPtr.p->nextActiveOp == RNIL)) {
+ ljam();
+/* ---------------------------------------------------------- */
+// We handle the simple case separately as an optimisation
+/* ---------------------------------------------------------- */
+ commitSimple(signal,
+ regOperPtr.p,
+ regFragPtr.p,
+ regTabPtr.p);
+ } else {
+/* ---------------------------------------------------------- */
+// This is the first commit message of this record in this
+// transaction. We will commit this record completely for this
+// transaction. If there are other operations they will be
+// responsible to release their own resources. Also commit of
+// a delete is postponed until the last operation is committed
+// on the tuple.
+//
+// As part of this commitRecord we will also handle detached
+// triggers and release of resources for this operation.
+/* ---------------------------------------------------------- */
+ ljam();
+ commitRecord(signal,
+ regOperPtr.p,
+ regFragPtr.p,
+ regTabPtr.p);
+ removeActiveOpList(regOperPtr.p);
+ }//if
+ } else {
+ ljam();
+/* ---------------------------------------------------------- */
+// Release any copy tuples
+/* ---------------------------------------------------------- */
+ ndbrequire(regOperPtr.p->tupleState == TO_BE_COMMITTED);
+ commitUpdate(signal, regOperPtr.p, regFragPtr.p, regTabPtr.p);
+ removeActiveOpList(regOperPtr.p);
+ }//if
+ initOpConnection(regOperPtr.p, regFragPtr.p);
+}//execTUP_COMMITREQ()
+
+void
+Dbtup::updateGcpId(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr)
+{
+ PagePtr pagePtr;
+ ljam();
+//--------------------------------------------------------------------
+// Is this code safe for UNDO logging. Not sure currently. RONM
+//--------------------------------------------------------------------
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 temp = regOperPtr->pageOffset + regTabPtr->tupGCPIndex;
+ ndbrequire((temp < ZWORDS_ON_PAGE) &&
+ (regTabPtr->tupGCPIndex < regTabPtr->tupheadsize));
+ if (isUndoLoggingNeeded(regFragPtr, regOperPtr->fragPageId)) {
+ Uint32 prevGCI = pagePtr.p->pageWord[temp];
+ ljam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_UPDATE_GCI,
+ regOperPtr->fragPageId,
+ regOperPtr->pageIndex,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ cprAddGCIUpdate(signal,
+ prevGCI,
+ regFragPtr);
+ }//if
+ pagePtr.p->pageWord[temp] = regOperPtr->gci;
+ if (regTabPtr->checksumIndicator) {
+ ljam();
+ setChecksum(pagePtr.p, regOperPtr->pageOffset, regTabPtr->tupheadsize);
+ }//if
+}//Dbtup::updateGcpId()
+
+void
+Dbtup::commitRecord(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr)
+{
+ Uint32 opType;
+ OperationrecPtr firstOpPtr;
+ PagePtr pagePtr;
+
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+
+ setTupleStatesSetOpType(regOperPtr, pagePtr.p, opType, firstOpPtr);
+
+ fragptr.p = regFragPtr;
+ tabptr.p = regTabPtr;
+
+ if (opType == ZINSERT_DELETE) {
+ ljam();
+//--------------------------------------------------------------------
+// We started by inserting the tuple and ended by deleting. Seen from
+// transactions point of view no changes were made.
+//--------------------------------------------------------------------
+ commitUpdate(signal, regOperPtr, regFragPtr, regTabPtr);
+ return;
+ } else if (opType == ZINSERT) {
+ ljam();
+//--------------------------------------------------------------------
+// We started by inserting whereafter we made several changes to the
+// tuple that could include updates, deletes and new inserts. The final
+// state of the tuple is the original tuple. This is reached from this
+// operation. We change the optype on this operation to ZINSERT to
+// ensure proper operation of the detached trigger.
+// We restore the optype after executing triggers although not really
+// needed.
+//--------------------------------------------------------------------
+ Uint32 saveOpType = regOperPtr->optype;
+ regOperPtr->optype = ZINSERT;
+ operPtr.p = regOperPtr;
+
+ checkDetachedTriggers(signal,
+ regOperPtr,
+ regTabPtr);
+
+ regOperPtr->optype = saveOpType;
+ } else if (opType == ZUPDATE) {
+ ljam();
+//--------------------------------------------------------------------
+// We want to use the first operation which contains a copy tuple
+// reference. This operation contains the before value of this record
+// for this transaction. Then this operation is used for executing
+// triggers with optype set to update.
+//--------------------------------------------------------------------
+ OperationrecPtr befOpPtr;
+ findBeforeValueOperation(befOpPtr, firstOpPtr);
+
+ Uint32 saveOpType = befOpPtr.p->optype;
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attributeMask;
+ Bitmask<MAXNROFATTRIBUTESINWORDS> saveAttributeMask;
+
+ calculateChangeMask(pagePtr.p,
+ regTabPtr,
+ befOpPtr.p->pageOffset,
+ attributeMask);
+
+ saveAttributeMask.clear();
+ saveAttributeMask.bitOR(befOpPtr.p->changeMask);
+ befOpPtr.p->changeMask.clear();
+ befOpPtr.p->changeMask.bitOR(attributeMask);
+
+ operPtr.p = befOpPtr.p;
+ checkDetachedTriggers(signal,
+ befOpPtr.p,
+ regTabPtr);
+
+ befOpPtr.p->changeMask.clear();
+ befOpPtr.p->changeMask.bitOR(saveAttributeMask);
+
+ befOpPtr.p->optype = saveOpType;
+ } else if (opType == ZDELETE) {
+ ljam();
+//--------------------------------------------------------------------
+// We want to use the first operation which contains a copy tuple.
+// We benefit from the fact that we know that it cannot be a simple
+// delete and it cannot be an insert followed by a delete. Thus there
+// must either be an update or a insert following a delete. In both
+// cases we will find a before value in a copy tuple.
+//
+// An added complexity is that the trigger handling assumes that the
+// before value is located in the original tuple so we have to move the
+// copy tuple reference to the original tuple reference and afterwards
+// restore it again.
+//--------------------------------------------------------------------
+ OperationrecPtr befOpPtr;
+ findBeforeValueOperation(befOpPtr, firstOpPtr);
+ Uint32 saveOpType = befOpPtr.p->optype;
+
+ Uint32 realPageId = befOpPtr.p->realPageId;
+ Uint32 pageOffset = befOpPtr.p->pageOffset;
+ Uint32 fragPageId = befOpPtr.p->fragPageId;
+ Uint32 pageIndex = befOpPtr.p->pageIndex;
+
+ befOpPtr.p->realPageId = befOpPtr.p->realPageIdC;
+ befOpPtr.p->pageOffset = befOpPtr.p->pageOffsetC;
+ befOpPtr.p->fragPageId = befOpPtr.p->fragPageIdC;
+ befOpPtr.p->pageIndex = befOpPtr.p->pageIndexC;
+
+ operPtr.p = befOpPtr.p;
+ checkDetachedTriggers(signal,
+ befOpPtr.p,
+ regTabPtr);
+
+ befOpPtr.p->realPageId = realPageId;
+ befOpPtr.p->pageOffset = pageOffset;
+ befOpPtr.p->fragPageId = fragPageId;
+ befOpPtr.p->pageIndex = pageIndex;
+ befOpPtr.p->optype = saveOpType;
+ } else {
+ ndbrequire(false);
+ }//if
+
+ commitUpdate(signal, regOperPtr, regFragPtr, regTabPtr);
+ if (regTabPtr->GCPIndicator) {
+ updateGcpId(signal, regOperPtr, regFragPtr, regTabPtr);
+ }//if
+}//Dbtup::commitRecord()
+
+void
+Dbtup::setTupleStatesSetOpType(Operationrec* const regOperPtr,
+ Page* const pagePtr,
+ Uint32& opType,
+ OperationrecPtr& firstOpPtr)
+{
+ OperationrecPtr loopOpPtr;
+ OperationrecPtr lastOpPtr;
+
+ ndbrequire(regOperPtr->pageOffset < ZWORDS_ON_PAGE);
+ loopOpPtr.i = pagePtr->pageWord[regOperPtr->pageOffset];
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ lastOpPtr = loopOpPtr;
+ if (loopOpPtr.p->optype == ZDELETE) {
+ ljam();
+ opType = ZDELETE;
+ } else {
+ ljam();
+ opType = ZUPDATE;
+ }//if
+ do {
+ ljam();
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ firstOpPtr = loopOpPtr;
+ loopOpPtr.p->tupleState = TO_BE_COMMITTED;
+ loopOpPtr.i = loopOpPtr.p->nextActiveOp;
+ } while (loopOpPtr.i != RNIL);
+ if (opType == ZDELETE) {
+ ljam();
+ if (firstOpPtr.p->optype == ZINSERT) {
+ ljam();
+ opType = ZINSERT_DELETE;
+ }//if
+ } else {
+ ljam();
+ if (firstOpPtr.p->optype == ZINSERT) {
+ ljam();
+ opType = ZINSERT;
+ }//if
+ }///if
+}//Dbtup::setTupleStatesSetOpType()
+
+void Dbtup::findBeforeValueOperation(OperationrecPtr& befOpPtr,
+ OperationrecPtr firstOpPtr)
+{
+ befOpPtr = firstOpPtr;
+ if (befOpPtr.p->realPageIdC != RNIL) {
+ ljam();
+ return;
+ } else {
+ ljam();
+ befOpPtr.i = befOpPtr.p->prevActiveOp;
+ ptrCheckGuard(befOpPtr, cnoOfOprec, operationrec);
+ ndbrequire(befOpPtr.p->realPageIdC != RNIL);
+ }//if
+}//Dbtup::findBeforeValueOperation()
+
+void
+Dbtup::calculateChangeMask(Page* const pagePtr,
+ Tablerec* const regTabPtr,
+ Uint32 pageOffset,
+ Bitmask<MAXNROFATTRIBUTESINWORDS>& attributeMask)
+{
+ OperationrecPtr loopOpPtr;
+
+ attributeMask.clear();
+ ndbrequire(pageOffset < ZWORDS_ON_PAGE);
+ loopOpPtr.i = pagePtr->pageWord[pageOffset];
+ do {
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ if (loopOpPtr.p->optype == ZUPDATE) {
+ ljam();
+ attributeMask.bitOR(loopOpPtr.p->changeMask);
+ } else if (loopOpPtr.p->optype == ZINSERT) {
+ ljam();
+ attributeMask.set();
+ return;
+ } else {
+ ndbrequire(loopOpPtr.p->optype == ZDELETE);
+ }//if
+ loopOpPtr.i = loopOpPtr.p->nextActiveOp;
+ } while (loopOpPtr.i != RNIL);
+}//Dbtup::calculateChangeMask()
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp
new file mode 100644
index 00000000000..8c43de52a75
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp
@@ -0,0 +1,411 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/EventReport.hpp>
+#include <Vector.hpp>
+
+#define ljam() { jamLine(30000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(30000 + __LINE__); }
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ------------------------ DEBUG MODULE -------------------------- */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+void Dbtup::execDEBUG_SIG(Signal* signal)
+{
+ PagePtr regPagePtr;
+ ljamEntry();
+ regPagePtr.i = signal->theData[0];
+ ptrCheckGuard(regPagePtr, cnoOfPage, page);
+}//Dbtup::execDEBUG_SIG()
+
+#ifdef TEST_MR
+#include <time.h>
+
+void startTimer(struct timespec *tp)
+{
+ clock_gettime(CLOCK_REALTIME, tp);
+}//startTimer()
+
+int stopTimer(struct timespec *tp)
+{
+ double timer_count;
+ struct timespec theStopTime;
+ clock_gettime(CLOCK_REALTIME, &theStopTime);
+ timer_count = (double)(1000000*((double)theStopTime.tv_sec - (double)tp->tv_sec)) +
+ (double)((double)((double)theStopTime.tv_nsec - (double)tp->tv_nsec)/(double)1000);
+ return (int)timer_count;
+}//stopTimer()
+
+#endif // end TEST_MR
+
+struct Chunk {
+ Uint32 pageId;
+ Uint32 pageCount;
+};
+
+void
+Dbtup::reportMemoryUsage(Signal* signal, int incDec){
+ signal->theData[0] = NDB_LE_MemoryUsage;
+ signal->theData[1] = incDec;
+ signal->theData[2] = sizeof(Page);
+ signal->theData[3] = cnoOfAllocatedPages;
+ signal->theData[4] = cnoOfPage;
+ signal->theData[5] = DBTUP;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 6, JBB);
+}
+
+void
+Dbtup::execDUMP_STATE_ORD(Signal* signal)
+{
+ Uint32 type = signal->theData[0];
+ if(type == DumpStateOrd::DumpPageMemory){
+ reportMemoryUsage(signal, 0);
+ return;
+ }
+ DumpStateOrd * const dumpState = (DumpStateOrd *)&signal->theData[0];
+
+#if 0
+ if (type == 100) {
+ RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
+ req->primaryTableId = 2;
+ req->secondaryTableId = RNIL;
+ req->userPtr = 2;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownref, GSN_REL_TABMEMREQ, signal,
+ RelTabMemReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 101) {
+ RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
+ req->primaryTableId = 4;
+ req->secondaryTableId = 5;
+ req->userPtr = 4;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownref, GSN_REL_TABMEMREQ, signal,
+ RelTabMemReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 102) {
+ RelTabMemReq * const req = (RelTabMemReq *)signal->getDataPtrSend();
+ req->primaryTableId = 6;
+ req->secondaryTableId = 8;
+ req->userPtr = 6;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownref, GSN_REL_TABMEMREQ, signal,
+ RelTabMemReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 103) {
+ DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
+ req->primaryTableId = 2;
+ req->secondaryTableId = RNIL;
+ req->userPtr = 2;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownref, GSN_DROP_TABFILEREQ, signal,
+ DropTabFileReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 104) {
+ DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
+ req->primaryTableId = 4;
+ req->secondaryTableId = 5;
+ req->userPtr = 4;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownref, GSN_DROP_TABFILEREQ, signal,
+ DropTabFileReq::SignalLength, JBB);
+ return;
+ }//if
+ if (type == 105) {
+ DropTabFileReq * const req = (DropTabFileReq *)signal->getDataPtrSend();
+ req->primaryTableId = 6;
+ req->secondaryTableId = 8;
+ req->userPtr = 6;
+ req->userRef = DBDICT_REF;
+ sendSignal(cownref, GSN_DROP_TABFILEREQ, signal,
+ DropTabFileReq::SignalLength, JBB);
+ return;
+ }//if
+#endif
+#ifdef ERROR_INSERT
+ if (type == DumpStateOrd::EnableUndoDelayDataWrite) {
+ ndbout << "Dbtup:: delay write of datapages for table = "
+ << dumpState->args[1]<< endl;
+ c_errorInsert4000TableId = dumpState->args[1];
+ SET_ERROR_INSERT_VALUE(4000);
+ return;
+ }//if
+#endif
+#ifdef VM_TRACE
+ if (type == 1211){
+ ndbout_c("Startar modul test av Page Manager");
+
+ Vector<Chunk> chunks;
+ const Uint32 LOOPS = 1000;
+ for(Uint32 i = 0; i<LOOPS; i++){
+
+ // Case
+ Uint32 c = (rand() % 3);
+ const Uint32 free = cnoOfPage - cnoOfAllocatedPages;
+
+ Uint32 alloc = 0;
+ if(free <= 1){
+ c = 0;
+ alloc = 1;
+ } else
+ alloc = 1 + (rand() % (free - 1));
+
+ if(chunks.size() == 0 && c == 0){
+ c = 1 + rand() % 2;
+ }
+
+ ndbout_c("loop=%d case=%d free=%d alloc=%d", i, c, free, alloc);
+ switch(c){
+ case 0:{ // Release
+ const int ch = rand() % chunks.size();
+ Chunk chunk = chunks[ch];
+ chunks.erase(ch);
+ returnCommonArea(chunk.pageId, chunk.pageCount);
+ }
+ break;
+ case 2: { // Seize(n) - fail
+ alloc += free;
+ // Fall through
+ }
+ case 1: { // Seize(n) (success)
+
+ Chunk chunk;
+ allocConsPages(alloc, chunk.pageCount, chunk.pageId);
+ ndbrequire(chunk.pageCount <= alloc);
+ if(chunk.pageCount != 0){
+ chunks.push_back(chunk);
+ if(chunk.pageCount != alloc) {
+ ndbout_c(" Tried to allocate %d - only allocated %d - free: %d",
+ alloc, chunk.pageCount, free);
+ }
+ } else {
+ ndbout_c(" Failed to alloc %d pages with %d pages free",
+ alloc, free);
+ }
+
+ for(Uint32 i = 0; i<chunk.pageCount; i++){
+ PagePtr pagePtr;
+ pagePtr.i = chunk.pageId + i;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ pagePtr.p->pageWord[ZPAGE_STATE_POS] = ~ZFREE_COMMON;
+ }
+
+ if(alloc == 1 && free > 0)
+ ndbrequire(chunk.pageCount == alloc);
+ }
+ break;
+ }
+ }
+ while(chunks.size() > 0){
+ Chunk chunk = chunks.back();
+ returnCommonArea(chunk.pageId, chunk.pageCount);
+ chunks.erase(chunks.size() - 1);
+ }
+ }
+#endif
+}//Dbtup::execDUMP_STATE_ORD()
+
+/* ---------------------------------------------------------------- */
+/* --------- MEMORY CHECK ----------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execMEMCHECKREQ(Signal* signal)
+{
+ PagePtr regPagePtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ CheckpointInfoPtr ciPtr;
+ UndoPagePtr regUndoPagePtr;
+ Uint32* data = &signal->theData[0];
+
+ ljamEntry();
+ BlockReference blockref = signal->theData[0];
+ Uint32 i;
+ for (i = 0; i < 25; i++) {
+ ljam();
+ data[i] = 0;
+ }//for
+ for (i = 0; i < 16; i++) {
+ regPagePtr.i = cfreepageList[i];
+ ljam();
+ while (regPagePtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(regPagePtr, cnoOfPage, page);
+ regPagePtr.i = regPagePtr.p->pageWord[ZPAGE_NEXT_POS];
+ data[0]++;
+ }//while
+ }//for
+ regUndoPagePtr.i = cfirstfreeUndoSeg;
+ while (regUndoPagePtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+ regUndoPagePtr.i = regUndoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS];
+ data[1] += ZUB_SEGMENT_SIZE;
+ }//while
+ ciPtr.i = cfirstfreeLcp;
+ while (ciPtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ ciPtr.i = ciPtr.p->lcpNextRec;
+ data[2]++;
+ }//while
+ dbsiPtr.i = cfirstfreePdx;
+ while (dbsiPtr.i != ZNIL) {
+ ljam();
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ dbsiPtr.i = dbsiPtr.p->pdxNextRec;
+ data[3]++;
+ }//while
+ sendSignal(blockref, GSN_MEMCHECKCONF, signal, 25, JBB);
+}//Dbtup::memCheck()
+
+// ------------------------------------------------------------------------
+// Help function to be used when debugging. Prints out a tuple page.
+// printLimit is the number of bytes that is printed out from the page. A
+// page is of size 32768 bytes as of March 2003.
+// ------------------------------------------------------------------------
+void Dbtup::printoutTuplePage(Uint32 fragid, Uint32 pageid, Uint32 printLimit)
+{
+ PagePtr tmpPageP;
+ FragrecordPtr tmpFragP;
+ TablerecPtr tmpTableP;
+ Uint32 tmpTupleSize;
+
+ tmpPageP.i = pageid;
+ ptrCheckGuard(tmpPageP, cnoOfPage, page);
+
+ tmpFragP.i = fragid;
+ ptrCheckGuard(tmpFragP, cnoOfFragrec, fragrecord);
+
+ tmpTableP.i = tmpFragP.p->fragTableId;
+ ptrCheckGuard(tmpTableP, cnoOfTablerec, tablerec);
+
+ tmpTupleSize = tmpTableP.p->tupheadsize;
+
+ ndbout << "Fragid: " << fragid << " Pageid: " << pageid << endl
+ << "----------------------------------------" << endl;
+
+ ndbout << "PageHead : ";
+ for (Uint32 i1 = 0; i1 < ZPAGE_HEADER_SIZE; i1++) {
+ if (i1 == 3)
+ ndbout << (tmpPageP.p->pageWord[i1] >> 16) << "," << (tmpPageP.p->pageWord[i1] & 0xffff) << " ";
+ else if (tmpPageP.p->pageWord[i1] == 4059165169u)
+ ndbout << "F1F1F1F1 ";
+ else if (tmpPageP.p->pageWord[i1] == 268435455u)
+ ndbout << "RNIL ";
+ else
+ ndbout << tmpPageP.p->pageWord[i1] << " ";
+ }//for
+ ndbout << endl;
+ for (Uint32 i = ZPAGE_HEADER_SIZE; i < printLimit; i += tmpTupleSize) {
+ ndbout << "pagepos " << i << " : ";
+
+ for (Uint32 j = i; j < i + tmpTupleSize; j++) {
+ if (tmpPageP.p->pageWord[j] == 4059165169u)
+ ndbout << "F1F1F1F1 ";
+ else if (tmpPageP.p->pageWord[j] == 268435455u)
+ ndbout << "RNIL ";
+ else
+ ndbout << tmpPageP.p->pageWord[j] << " ";
+ }//for
+ ndbout << endl;
+ }//for
+}//Dbtup::printoutTuplePage
+
+#ifdef VM_TRACE
+NdbOut&
+operator<<(NdbOut& out, const Dbtup::Operationrec& op)
+{
+ out << "[Operationrec " << hex << &op;
+ // table
+ out << " [tableRef " << dec << op.tableRef << "]";
+ out << " [fragId " << dec << op.fragId << "]";
+ out << " [fragmentPtr " << hex << op.fragmentPtr << "]";
+ // type
+ out << " [optype " << dec << op.optype << "]";
+ out << " [deleteInsertFlag " << dec << op.deleteInsertFlag << "]";
+ out << " [dirtyOp " << dec << op.dirtyOp << "]";
+ out << " [interpretedExec " << dec << op.interpretedExec << "]";
+ out << " [opSimple " << dec << op.opSimple << "]";
+ // state
+ out << " [tupleState " << dec << (Uint32) op.tupleState << "]";
+ out << " [transstate " << dec << (Uint32) op.transstate << "]";
+ out << " [inFragList " << dec << op.inFragList << "]";
+ out << " [inActiveOpList " << dec << op.inActiveOpList << "]";
+ out << " [undoLogged " << dec << (Uint32) op.undoLogged << "]";
+ // links
+ out << " [prevActiveOp " << hex << op.prevActiveOp << "]";
+ out << " [nextActiveOp " << hex << op.nextActiveOp << "]";
+ // tuples
+ out << " [tupVersion " << hex << op.tupVersion << "]";
+ out << " [fragPageId " << dec << op.fragPageId << "]";
+ out << " [pageIndex " << dec << op.pageIndex << "]";
+ out << " [realPageId " << hex << op.realPageId << "]";
+ out << " [pageOffset " << dec << op.pageOffset << "]";
+ out << " [fragPageIdC " << dec << op.fragPageIdC << "]";
+ out << " [pageIndexC " << dec << op.pageIndexC << "]";
+ out << " [realPageIdC " << hex << op.realPageIdC << "]";
+ out << " [pageOffsetC " << dec << op.pageOffsetC << "]";
+ // trans
+ out << " [transid1 " << hex << op.transid1 << "]";
+ out << " [transid2 " << hex << op.transid2 << "]";
+ out << "]";
+ return out;
+}
+
+// uses global tabptr
+NdbOut&
+operator<<(NdbOut& out, const Dbtup::Th& th)
+{
+ // ugly
+ Dbtup* tup = (Dbtup*)globalData.getBlock(DBTUP);
+ const Dbtup::Tablerec& tab = *tup->tabptr.p;
+ unsigned i = 0;
+ out << "[Th " << hex << &th;
+ out << " [op " << hex << th.data[i++] << "]";
+ out << " [version " << hex << (Uint16)th.data[i++] << "]";
+ if (tab.checksumIndicator)
+ out << " [checksum " << hex << th.data[i++] << "]";
+ out << " [nullbits";
+ for (unsigned j = 0; j < tab.tupNullWords; j++)
+ out << " " << hex << th.data[i++];
+ out << "]";
+ if (tab.GCPIndicator)
+ out << " [gcp " << dec << th.data[i++] << "]";
+ out << " [data";
+ while (i < tab.tupheadsize)
+ out << " " << hex << th.data[i++];
+ out << "]";
+ out << "]";
+ return out;
+}
+#endif
+
+#ifdef VM_TRACE
+template class Vector<Chunk>;
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
new file mode 100644
index 00000000000..761f959acdc
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
@@ -0,0 +1,2052 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <AttributeHeader.hpp>
+#include <Interpreter.hpp>
+#include <signaldata/TupCommit.hpp>
+#include <signaldata/TupKey.hpp>
+#include <NdbSqlUtil.hpp>
+
+/* ----------------------------------------------------------------- */
+/* ----------- INIT_STORED_OPERATIONREC -------------- */
+/* ----------------------------------------------------------------- */
+int Dbtup::initStoredOperationrec(Operationrec* const regOperPtr,
+ Uint32 storedId)
+{
+ jam();
+ StoredProcPtr storedPtr;
+ c_storedProcPool.getPtr(storedPtr, storedId);
+ if (storedPtr.i != RNIL) {
+ if (storedPtr.p->storedCode == ZSCAN_PROCEDURE) {
+ storedPtr.p->storedCounter++;
+ regOperPtr->firstAttrinbufrec = storedPtr.p->storedLinkFirst;
+ regOperPtr->lastAttrinbufrec = storedPtr.p->storedLinkLast;
+ regOperPtr->attrinbufLen = storedPtr.p->storedProcLength;
+ regOperPtr->currentAttrinbufLen = storedPtr.p->storedProcLength;
+ return ZOK;
+ }//if
+ }//if
+ terrorCode = ZSTORED_PROC_ID_ERROR;
+ return terrorCode;
+}//Dbtup::initStoredOperationrec()
+
+void Dbtup::copyAttrinfo(Signal* signal,
+ Operationrec * const regOperPtr,
+ Uint32* inBuffer)
+{
+ AttrbufrecPtr copyAttrBufPtr;
+ Uint32 RnoOfAttrBufrec = cnoOfAttrbufrec;
+ int RbufLen;
+ Uint32 RinBufIndex = 0;
+ Uint32 Rnext;
+ Uint32 Rfirst;
+ Uint32 TstoredProcedure = (regOperPtr->storedProcedureId != ZNIL);
+ Uint32 RnoFree = cnoFreeAttrbufrec;
+
+//-------------------------------------------------------------------------
+// As a prelude to the execution of the TUPKEYREQ we will copy the program
+// into the inBuffer to enable easy execution without any complex jumping
+// between the buffers. In particular this will make the interpreter less
+// complex. Hopefully it does also improve performance.
+//-------------------------------------------------------------------------
+ copyAttrBufPtr.i = regOperPtr->firstAttrinbufrec;
+ while (copyAttrBufPtr.i != RNIL) {
+ jam();
+ ndbrequire(copyAttrBufPtr.i < RnoOfAttrBufrec);
+ ptrAss(copyAttrBufPtr, attrbufrec);
+ RbufLen = copyAttrBufPtr.p->attrbuf[ZBUF_DATA_LEN];
+ Rnext = copyAttrBufPtr.p->attrbuf[ZBUF_NEXT];
+ Rfirst = cfirstfreeAttrbufrec;
+ MEMCOPY_NO_WORDS(&inBuffer[RinBufIndex],
+ &copyAttrBufPtr.p->attrbuf[0],
+ RbufLen);
+ RinBufIndex += RbufLen;
+ if (!TstoredProcedure) {
+ copyAttrBufPtr.p->attrbuf[ZBUF_NEXT] = Rfirst;
+ cfirstfreeAttrbufrec = copyAttrBufPtr.i;
+ RnoFree++;
+ }//if
+ copyAttrBufPtr.i = Rnext;
+ }//while
+ cnoFreeAttrbufrec = RnoFree;
+ if (TstoredProcedure) {
+ jam();
+ StoredProcPtr storedPtr;
+ c_storedProcPool.getPtr(storedPtr, (Uint32)regOperPtr->storedProcedureId);
+ ndbrequire(storedPtr.p->storedCode == ZSCAN_PROCEDURE);
+ storedPtr.p->storedCounter--;
+ regOperPtr->storedProcedureId = ZNIL;
+ }//if
+ // Release the ATTRINFO buffers
+ regOperPtr->firstAttrinbufrec = RNIL;
+ regOperPtr->lastAttrinbufrec = RNIL;
+}//Dbtup::copyAttrinfo()
+
+void Dbtup::handleATTRINFOforTUPKEYREQ(Signal* signal,
+ Uint32 length,
+ Operationrec * const regOperPtr)
+{
+ AttrbufrecPtr TAttrinbufptr;
+ TAttrinbufptr.i = cfirstfreeAttrbufrec;
+ if ((cfirstfreeAttrbufrec < cnoOfAttrbufrec) &&
+ (cnoFreeAttrbufrec > MIN_ATTRBUF)) {
+ ptrAss(TAttrinbufptr, attrbufrec);
+ MEMCOPY_NO_WORDS(&TAttrinbufptr.p->attrbuf[0],
+ &signal->theData[3],
+ length);
+ Uint32 RnoFree = cnoFreeAttrbufrec;
+ Uint32 Rnext = TAttrinbufptr.p->attrbuf[ZBUF_NEXT];
+ TAttrinbufptr.p->attrbuf[ZBUF_DATA_LEN] = length;
+ TAttrinbufptr.p->attrbuf[ZBUF_NEXT] = RNIL;
+
+ AttrbufrecPtr locAttrinbufptr;
+ Uint32 RnewLen = regOperPtr->currentAttrinbufLen;
+
+ locAttrinbufptr.i = regOperPtr->lastAttrinbufrec;
+ cfirstfreeAttrbufrec = Rnext;
+ cnoFreeAttrbufrec = RnoFree - 1;
+ RnewLen += length;
+ regOperPtr->lastAttrinbufrec = TAttrinbufptr.i;
+ regOperPtr->currentAttrinbufLen = RnewLen;
+ if (locAttrinbufptr.i == RNIL) {
+ regOperPtr->firstAttrinbufrec = TAttrinbufptr.i;
+ return;
+ } else {
+ jam();
+ ptrCheckGuard(locAttrinbufptr, cnoOfAttrbufrec, attrbufrec);
+ locAttrinbufptr.p->attrbuf[ZBUF_NEXT] = TAttrinbufptr.i;
+ }//if
+ if (RnewLen < ZATTR_BUFFER_SIZE) {
+ return;
+ } else {
+ jam();
+ regOperPtr->transstate = TOO_MUCH_AI;
+ return;
+ }//if
+ } else if (cnoFreeAttrbufrec <= MIN_ATTRBUF) {
+ jam();
+ regOperPtr->transstate = ERROR_WAIT_TUPKEYREQ;
+ } else {
+ ndbrequire(false);
+ }//if
+}//Dbtup::handleATTRINFOforTUPKEYREQ()
+
+void Dbtup::execATTRINFO(Signal* signal)
+{
+ OperationrecPtr regOpPtr;
+ Uint32 Rsig0 = signal->theData[0];
+ Uint32 Rlen = signal->length();
+ regOpPtr.i = Rsig0;
+
+ jamEntry();
+
+ ptrCheckGuard(regOpPtr, cnoOfOprec, operationrec);
+ if (regOpPtr.p->transstate == IDLE) {
+ handleATTRINFOforTUPKEYREQ(signal, Rlen - 3, regOpPtr.p);
+ return;
+ } else if (regOpPtr.p->transstate == WAIT_STORED_PROCEDURE_ATTR_INFO) {
+ storedProcedureAttrInfo(signal, regOpPtr.p, Rlen - 3, 3, false);
+ return;
+ }//if
+ switch (regOpPtr.p->transstate) {
+ case ERROR_WAIT_STORED_PROCREQ:
+ jam();
+ case TOO_MUCH_AI:
+ jam();
+ case ERROR_WAIT_TUPKEYREQ:
+ jam();
+ return; /* IGNORE ATTRINFO IN THOSE STATES, WAITING FOR ABORT SIGNAL */
+ break;
+ case DISCONNECTED:
+ jam();
+ case STARTED:
+ jam();
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dbtup::execATTRINFO()
+
+void Dbtup::execTUP_ALLOCREQ(Signal* signal)
+{
+ OperationrecPtr regOperPtr;
+ TablerecPtr regTabPtr;
+ FragrecordPtr regFragPtr;
+
+ jamEntry();
+
+ regOperPtr.i = signal->theData[0];
+ regFragPtr.i = signal->theData[1];
+ regTabPtr.i = signal->theData[2];
+
+ if (!((regOperPtr.i < cnoOfOprec) &&
+ (regFragPtr.i < cnoOfFragrec) &&
+ (regTabPtr.i < cnoOfTablerec))) {
+ ndbrequire(false);
+ }//if
+ ptrAss(regOperPtr, operationrec);
+ ptrAss(regFragPtr, fragrecord);
+ ptrAss(regTabPtr, tablerec);
+
+//---------------------------------------------------
+/* --- Allocate a tuple as requested by ACC --- */
+//---------------------------------------------------
+ PagePtr pagePtr;
+ Uint32 pageOffset;
+ if (!allocTh(regFragPtr.p,
+ regTabPtr.p,
+ NORMAL_PAGE,
+ signal,
+ pageOffset,
+ pagePtr)) {
+ signal->theData[0] = terrorCode; // Indicate failure
+ return;
+ }//if
+ Uint32 fragPageId = pagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ Uint32 pageIndex = ((pageOffset - ZPAGE_HEADER_SIZE) /
+ regTabPtr.p->tupheadsize) << 1;
+ regOperPtr.p->tableRef = regTabPtr.i;
+ regOperPtr.p->fragId = regFragPtr.p->fragmentId;
+ regOperPtr.p->realPageId = pagePtr.i;
+ regOperPtr.p->fragPageId = fragPageId;
+ regOperPtr.p->pageOffset = pageOffset;
+ regOperPtr.p->pageIndex = pageIndex;
+ /* -------------------------------------------------------------- */
+ /* AN INSERT IS UNDONE BY FREEING THE DATA OCCUPIED BY THE INSERT */
+ /* THE ONLY DATA WE HAVE TO LOG EXCEPT THE TYPE, PAGE AND INDEX */
+ /* IS THE AMOUNT OF DATA TO FREE */
+ /* -------------------------------------------------------------- */
+ if (isUndoLoggingNeeded(regFragPtr.p, fragPageId)) {
+ jam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_DELETE_TH,
+ fragPageId,
+ pageIndex,
+ regTabPtr.i,
+ regFragPtr.p->fragmentId,
+ regFragPtr.p->checkpointVersion);
+ }//if
+
+ //---------------------------------------------------------------
+ // Initialise Active operation list by setting the list to empty
+ //---------------------------------------------------------------
+ ndbrequire(pageOffset < ZWORDS_ON_PAGE);
+ pagePtr.p->pageWord[pageOffset] = RNIL;
+
+ signal->theData[0] = 0;
+ signal->theData[1] = fragPageId;
+ signal->theData[2] = pageIndex;
+}//Dbtup::execTUP_ALLOCREQ()
+
+void
+Dbtup::setChecksum(Page* const pagePtr, Uint32 tupHeadOffset, Uint32 tupHeadSize)
+{
+ // 2 == regTabPtr.p->tupChecksumIndex
+ pagePtr->pageWord[tupHeadOffset + 2] = 0;
+ Uint32 checksum = calculateChecksum(pagePtr, tupHeadOffset, tupHeadSize);
+ pagePtr->pageWord[tupHeadOffset + 2] = checksum;
+}//Dbtup::setChecksum()
+
+Uint32
+Dbtup::calculateChecksum(Page* pagePtr,
+ Uint32 tupHeadOffset,
+ Uint32 tupHeadSize)
+{
+ Uint32 checksum = 0;
+ Uint32 loopStop = tupHeadOffset + tupHeadSize;
+ ndbrequire(loopStop <= ZWORDS_ON_PAGE);
+ // includes tupVersion
+ for (Uint32 i = tupHeadOffset + 1; i < loopStop; i++) {
+ checksum ^= pagePtr->pageWord[i];
+ }//if
+ return checksum;
+}//Dbtup::calculateChecksum()
+
+/* ----------------------------------------------------------------- */
+/* ----------- INSERT_ACTIVE_OP_LIST -------------- */
+/* ----------------------------------------------------------------- */
+void Dbtup::insertActiveOpList(Signal* signal,
+ OperationrecPtr regOperPtr,
+ Page* const pagePtr,
+ Uint32 pageOffset)
+{
+ OperationrecPtr iaoPrevOpPtr;
+ ndbrequire(regOperPtr.p->inActiveOpList == ZFALSE);
+ regOperPtr.p->inActiveOpList = ZTRUE;
+ ndbrequire(pageOffset < ZWORDS_ON_PAGE);
+ iaoPrevOpPtr.i = pagePtr->pageWord[pageOffset];
+ pagePtr->pageWord[pageOffset] = regOperPtr.i;
+ regOperPtr.p->prevActiveOp = RNIL;
+ regOperPtr.p->nextActiveOp = iaoPrevOpPtr.i;
+ if (iaoPrevOpPtr.i == RNIL) {
+ return;
+ } else {
+ jam();
+ ptrCheckGuard(iaoPrevOpPtr, cnoOfOprec, operationrec);
+ iaoPrevOpPtr.p->prevActiveOp = regOperPtr.i;
+ if (iaoPrevOpPtr.p->optype == ZDELETE &&
+ regOperPtr.p->optype == ZINSERT) {
+ jam();
+ // mark both
+ iaoPrevOpPtr.p->deleteInsertFlag = 1;
+ regOperPtr.p->deleteInsertFlag = 1;
+ }
+ return;
+ }//if
+}//Dbtup::insertActiveOpList()
+
+void Dbtup::linkOpIntoFragList(OperationrecPtr regOperPtr,
+ Fragrecord* const regFragPtr)
+{
+ OperationrecPtr sopTmpOperPtr;
+ Uint32 tail = regFragPtr->lastusedOprec;
+ ndbrequire(regOperPtr.p->inFragList == ZFALSE);
+ regOperPtr.p->inFragList = ZTRUE;
+ regOperPtr.p->prevOprecInList = tail;
+ regOperPtr.p->nextOprecInList = RNIL;
+ sopTmpOperPtr.i = tail;
+ if (tail == RNIL) {
+ regFragPtr->firstusedOprec = regOperPtr.i;
+ } else {
+ jam();
+ ptrCheckGuard(sopTmpOperPtr, cnoOfOprec, operationrec);
+ sopTmpOperPtr.p->nextOprecInList = regOperPtr.i;
+ }//if
+ regFragPtr->lastusedOprec = regOperPtr.i;
+}//Dbtup::linkOpIntoFragList()
+
+/*
+This routine is optimised for use from TUPKEYREQ.
+This means that a lot of input data is stored in the operation record.
+The routine expects the following data in the operation record to be
+set-up properly.
+Transaction data
+1) transid1
+2) transid2
+3) savePointId
+
+Operation data
+4) optype
+5) dirtyOp
+
+Tuple address
+6) fragPageId
+7) pageIndex
+
+regFragPtr and regTabPtr are references to the table and fragment data and
+is read-only.
+
+The routine will set up the following data in the operation record if
+returned with success.
+
+Tuple address data
+1) realPageId
+2) fragPageId
+3) pageOffset
+4) pageIndex
+
+Also the pagePtr is an output variable if the routine returns with success.
+It's input value can be undefined.
+*/
+bool
+Dbtup::getPage(PagePtr& pagePtr,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr)
+{
+/* ------------------------------------------------------------------------- */
+// GET THE REFERENCE TO THE TUPLE HEADER BY TRANSLATING THE FRAGMENT PAGE ID
+// INTO A REAL PAGE ID AND BY USING THE PAGE INDEX TO DERIVE THE PROPER INDEX
+// IN THE REAL PAGE.
+/* ------------------------------------------------------------------------- */
+ pagePtr.i = getRealpid(regFragPtr, regOperPtr->fragPageId);
+ regOperPtr->realPageId = pagePtr.i;
+ Uint32 RpageIndex = regOperPtr->pageIndex;
+ Uint32 Rtupheadsize = regTabPtr->tupheadsize;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 RpageIndexScaled = RpageIndex >> 1;
+ ndbrequire((RpageIndex & 1) == 0);
+ regOperPtr->pageOffset = ZPAGE_HEADER_SIZE +
+ (Rtupheadsize * RpageIndexScaled);
+
+ OperationrecPtr leaderOpPtr;
+ ndbrequire(regOperPtr->pageOffset < ZWORDS_ON_PAGE);
+ leaderOpPtr.i = pagePtr.p->pageWord[regOperPtr->pageOffset];
+ if (leaderOpPtr.i == RNIL) {
+ return true;
+ }//if
+ ptrCheckGuard(leaderOpPtr, cnoOfOprec, operationrec);
+ bool dirtyRead = ((regOperPtr->optype == ZREAD) &&
+ (regOperPtr->dirtyOp == 1));
+ if (dirtyRead) {
+ bool sameTrans = ((regOperPtr->transid1 == leaderOpPtr.p->transid1) &&
+ (regOperPtr->transid2 == leaderOpPtr.p->transid2));
+ if (!sameTrans) {
+ if (!getPageLastCommitted(regOperPtr, leaderOpPtr.p)) {
+ return false;
+ }//if
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ return true;
+ }//if
+ }//if
+ if (regOperPtr->optype == ZREAD) {
+ /*
+ Read uses savepoint id's to find the correct tuple version.
+ */
+ if (getPageThroughSavePoint(regOperPtr, leaderOpPtr.p)) {
+ jam();
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ return true;
+ }
+ return false;
+ }
+//----------------------------------------------------------------------
+// Check that no other operation is already active on the tuple. Also
+// that abort or commit is not ongoing.
+//----------------------------------------------------------------------
+ if (leaderOpPtr.p->tupleState == NO_OTHER_OP) {
+ jam();
+ if ((leaderOpPtr.p->optype == ZDELETE) &&
+ (regOperPtr->optype != ZINSERT)) {
+ jam();
+ terrorCode = ZTUPLE_DELETED_ERROR;
+ return false;
+ }//if
+ return true;
+ } else if (leaderOpPtr.p->tupleState == ALREADY_ABORTED) {
+ jam();
+ terrorCode = ZMUST_BE_ABORTED_ERROR;
+ return false;
+ } else {
+ ndbrequire(false);
+ }//if
+ return true;
+}//Dbtup::getPage()
+
+bool
+Dbtup::getPageThroughSavePoint(Operationrec* regOperPtr,
+ Operationrec* leaderOpPtr)
+{
+ bool found = false;
+ OperationrecPtr loopOpPtr;
+ loopOpPtr.p = leaderOpPtr;
+ while(true) {
+ if (regOperPtr->savePointId > loopOpPtr.p->savePointId) {
+ jam();
+ found = true;
+ break;
+ }
+ if (loopOpPtr.p->nextActiveOp == RNIL) {
+ break;
+ }
+ loopOpPtr.i = loopOpPtr.p->nextActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ jam();
+ }
+ if (!found) {
+ return getPageLastCommitted(regOperPtr, loopOpPtr.p);
+ } else {
+ if (loopOpPtr.p->optype == ZDELETE) {
+ jam();
+ terrorCode = ZTUPLE_DELETED_ERROR;
+ return false;
+ }
+ if (loopOpPtr.p->tupleState == ALREADY_ABORTED) {
+ /*
+ Requested tuple version has already been aborted
+ */
+ jam();
+ terrorCode = ZMUST_BE_ABORTED_ERROR;
+ return false;
+ }
+ bool use_copy;
+ if (loopOpPtr.p->prevActiveOp == RNIL) {
+ jam();
+ /*
+ Use original tuple since we are reading from the last written tuple.
+ We are the
+ */
+ use_copy = false;
+ } else {
+ /*
+ Go forward in time to find a copy of the tuple which this operation
+ produced
+ */
+ loopOpPtr.i = loopOpPtr.p->prevActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ if (loopOpPtr.p->optype == ZDELETE) {
+ /*
+ This operation was a Delete and thus have no copy tuple attached to
+ it. We will move forward to the next that either doesn't exist in
+ which case we will return the original tuple of any operation and
+ otherwise it must be an insert which contains a copy record.
+ */
+ if (loopOpPtr.p->prevActiveOp == RNIL) {
+ jam();
+ use_copy = false;
+ } else {
+ jam();
+ loopOpPtr.i = loopOpPtr.p->prevActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ ndbrequire(loopOpPtr.p->optype == ZINSERT);
+ use_copy = true;
+ }
+ } else if (loopOpPtr.p->optype == ZUPDATE) {
+ jam();
+ /*
+ This operation which was the next in time have a copy which was the
+ result of the previous operation which we want to use. Thus use
+ the copy tuple of this operation.
+ */
+ use_copy = true;
+ } else {
+ /*
+ This operation was an insert that happened after an insert or update.
+ This is not a possible case.
+ */
+ ndbrequire(false);
+ return false;
+ }
+ }
+ if (use_copy) {
+ regOperPtr->realPageId = loopOpPtr.p->realPageIdC;
+ regOperPtr->fragPageId = loopOpPtr.p->fragPageIdC;
+ regOperPtr->pageIndex = loopOpPtr.p->pageIndexC;
+ regOperPtr->pageOffset = loopOpPtr.p->pageOffsetC;
+ } else {
+ regOperPtr->realPageId = loopOpPtr.p->realPageId;
+ regOperPtr->fragPageId = loopOpPtr.p->fragPageId;
+ regOperPtr->pageIndex = loopOpPtr.p->pageIndex;
+ regOperPtr->pageOffset = loopOpPtr.p->pageOffset;
+ }
+ return true;
+ }
+}
+
+bool
+Dbtup::getPageLastCommitted(Operationrec* const regOperPtr,
+ Operationrec* const leaderOpPtr)
+{
+//----------------------------------------------------------------------
+// Dirty reads wants to read the latest committed tuple. The latest
+// tuple value could be not existing or else we have to find the copy
+// tuple. Start by finding the end of the list to find the first operation
+// on the record in the ongoing transaction.
+//----------------------------------------------------------------------
+ jam();
+ OperationrecPtr loopOpPtr;
+ loopOpPtr.p = leaderOpPtr;
+ while (loopOpPtr.p->nextActiveOp != RNIL) {
+ jam();
+ loopOpPtr.i = loopOpPtr.p->nextActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ }//while
+ if (loopOpPtr.p->optype == ZINSERT) {
+ jam();
+//----------------------------------------------------------------------
+// With an insert in the start of the list we know that the tuple did not
+// exist before this transaction was started. We don't care if the current
+// transaction is in the commit phase since the commit is not really
+// completed until the operation is gone from TUP.
+//----------------------------------------------------------------------
+ terrorCode = ZTUPLE_DELETED_ERROR;
+ return false;
+ } else {
+//----------------------------------------------------------------------
+// A successful update and delete as first in the queue means that a tuple
+// exist in the committed world. We need to find it.
+//----------------------------------------------------------------------
+ if (loopOpPtr.p->optype == ZUPDATE) {
+ jam();
+//----------------------------------------------------------------------
+// The first operation was a delete we set our tuple reference to the
+// copy tuple of this operation.
+//----------------------------------------------------------------------
+ regOperPtr->realPageId = loopOpPtr.p->realPageIdC;
+ regOperPtr->fragPageId = loopOpPtr.p->fragPageIdC;
+ regOperPtr->pageIndex = loopOpPtr.p->pageIndexC;
+ regOperPtr->pageOffset = loopOpPtr.p->pageOffsetC;
+ } else if ((loopOpPtr.p->optype == ZDELETE) &&
+ (loopOpPtr.p->prevActiveOp == RNIL)) {
+ jam();
+//----------------------------------------------------------------------
+// There was only a delete. The original tuple still is ok.
+//----------------------------------------------------------------------
+ } else {
+ jam();
+//----------------------------------------------------------------------
+// There was another operation after the delete, this must be an insert
+// and we have found our copy tuple there.
+//----------------------------------------------------------------------
+ loopOpPtr.i = loopOpPtr.p->prevActiveOp;
+ ptrCheckGuard(loopOpPtr, cnoOfOprec, operationrec);
+ ndbrequire(loopOpPtr.p->optype == ZINSERT);
+ regOperPtr->realPageId = loopOpPtr.p->realPageIdC;
+ regOperPtr->fragPageId = loopOpPtr.p->fragPageIdC;
+ regOperPtr->pageIndex = loopOpPtr.p->pageIndexC;
+ regOperPtr->pageOffset = loopOpPtr.p->pageOffsetC;
+ }//if
+ }//if
+ return true;
+}//Dbtup::getPageLastCommitted()
+
+void Dbtup::execTUPKEYREQ(Signal* signal)
+{
+ TupKeyReq * const tupKeyReq = (TupKeyReq *)signal->getDataPtr();
+ Uint32 RoperPtr = tupKeyReq->connectPtr;
+ Uint32 Rtabptr = tupKeyReq->tableRef;
+ Uint32 RfragId = tupKeyReq->fragId;
+ Uint32 Rstoredid = tupKeyReq->storedProcedure;
+ Uint32 Rfragptr = tupKeyReq->fragPtr;
+
+ Uint32 RnoOfOprec = cnoOfOprec;
+ Uint32 RnoOfTablerec = cnoOfTablerec;
+ Uint32 RnoOfFragrec = cnoOfFragrec;
+
+ operPtr.i = RoperPtr;
+ fragptr.i = Rfragptr;
+ tabptr.i = Rtabptr;
+ jamEntry();
+
+ ndbrequire(((RoperPtr < RnoOfOprec) &&
+ (Rtabptr < RnoOfTablerec) &&
+ (Rfragptr < RnoOfFragrec)));
+ ptrAss(operPtr, operationrec);
+ Operationrec * const regOperPtr = operPtr.p;
+ ptrAss(fragptr, fragrecord);
+ Fragrecord * const regFragPtr = fragptr.p;
+ ptrAss(tabptr, tablerec);
+ Tablerec* const regTabPtr = tabptr.p;
+
+ Uint32 TrequestInfo = tupKeyReq->request;
+
+ if (regOperPtr->transstate != IDLE) {
+ TUPKEY_abort(signal, 39);
+ return;
+ }//if
+/* ----------------------------------------------------------------- */
+// Operation is ZREAD when we arrive here so no need to worry about the
+// abort process.
+/* ----------------------------------------------------------------- */
+/* ----------- INITIATE THE OPERATION RECORD -------------- */
+/* ----------------------------------------------------------------- */
+ regOperPtr->fragmentPtr = Rfragptr;
+ regOperPtr->dirtyOp = TrequestInfo & 1;
+ regOperPtr->opSimple = (TrequestInfo >> 1) & 1;
+ regOperPtr->interpretedExec = (TrequestInfo >> 10) & 1;
+ regOperPtr->optype = (TrequestInfo >> 6) & 0xf;
+
+ // Attributes needed by trigger execution
+ regOperPtr->noFiredTriggers = 0;
+ regOperPtr->tableRef = Rtabptr;
+ regOperPtr->tcOperationPtr = tupKeyReq->opRef;
+ regOperPtr->primaryReplica = tupKeyReq->primaryReplica;
+ regOperPtr->coordinatorTC = tupKeyReq->coordinatorTC;
+ regOperPtr->tcOpIndex = tupKeyReq->tcOpIndex;
+ regOperPtr->savePointId = tupKeyReq->savePointId;
+
+ regOperPtr->fragId = RfragId;
+
+ regOperPtr->fragPageId = tupKeyReq->keyRef1;
+ regOperPtr->pageIndex = tupKeyReq->keyRef2;
+ regOperPtr->attrinbufLen = regOperPtr->logSize = tupKeyReq->attrBufLen;
+ regOperPtr->recBlockref = tupKeyReq->applRef;
+
+// Schema Version in tupKeyReq->schemaVersion not used in this version
+ regOperPtr->storedProcedureId = Rstoredid;
+ regOperPtr->transid1 = tupKeyReq->transId1;
+ regOperPtr->transid2 = tupKeyReq->transId2;
+
+ regOperPtr->attroutbufLen = 0;
+/* ----------------------------------------------------------------------- */
+// INITIALISE TO DEFAULT VALUE
+// INIT THE COPY REFERENCE RECORDS TO RNIL TO ENSURE THAT THEIR VALUES
+// ARE VALID IF THEY EXISTS
+// NO PENDING CHECKPOINT WHEN COPY CREATED (DEFAULT)
+// NO TUPLE HAS BEEN ALLOCATED YET
+// NO COPY HAS BEEN CREATED YET
+/* ----------------------------------------------------------------------- */
+ regOperPtr->undoLogged = false;
+ regOperPtr->realPageId = RNIL;
+ regOperPtr->realPageIdC = RNIL;
+ regOperPtr->fragPageIdC = RNIL;
+
+ regOperPtr->pageOffset = ZNIL;
+ regOperPtr->pageOffsetC = ZNIL;
+
+ regOperPtr->pageIndexC = ZNIL;
+
+ // version not yet known
+ regOperPtr->tupVersion = ZNIL;
+ regOperPtr->deleteInsertFlag = 0;
+
+ regOperPtr->tupleState = TUPLE_BLOCKED;
+ regOperPtr->changeMask.clear();
+
+ if (Rstoredid != ZNIL) {
+ ndbrequire(initStoredOperationrec(regOperPtr, Rstoredid) == ZOK);
+ }//if
+ copyAttrinfo(signal, regOperPtr, &cinBuffer[0]);
+
+ PagePtr pagePtr;
+ if (!getPage(pagePtr, regOperPtr, regFragPtr, regTabPtr)) {
+ tupkeyErrorLab(signal);
+ return;
+ }//if
+
+ Uint32 Roptype = regOperPtr->optype;
+ if (Roptype == ZREAD) {
+ jam();
+ if (handleReadReq(signal, regOperPtr, regTabPtr, pagePtr.p) != -1) {
+ sendTUPKEYCONF(signal, regOperPtr, 0);
+/* ------------------------------------------------------------------------- */
+// Read Operations need not to be taken out of any lists. We also do not
+// need to wait for commit since there is no changes to commit. Thus we
+// prepare the operation record already now for the next operation.
+// Write operations have set the state to STARTED above indicating that
+// they are waiting for the Commit or Abort decision.
+/* ------------------------------------------------------------------------- */
+ regOperPtr->transstate = IDLE;
+ regOperPtr->currentAttrinbufLen = 0;
+ }//if
+ return;
+ }//if
+ linkOpIntoFragList(operPtr, regFragPtr);
+ insertActiveOpList(signal,
+ operPtr,
+ pagePtr.p,
+ regOperPtr->pageOffset);
+ if (isUndoLoggingBlocked(regFragPtr)) {
+ TUPKEY_abort(signal, 38);
+ return;
+ }//if
+/* ---------------------------------------------------------------------- */
+// WE SET THE CURRENT ACTIVE OPERATION IN THE TUPLE TO POINT TO OUR
+//OPERATION RECORD. IF SEVERAL OPERATIONS WORK ON THIS TUPLE THEY ARE
+// LINKED TO OUR OPERATION RECORD. DIRTY READS CAN ACCESS THE COPY
+// TUPLE THROUGH OUR OPERATION RECORD.
+/* ---------------------------------------------------------------------- */
+ if (Roptype == ZINSERT) {
+ jam();
+ if (handleInsertReq(signal, regOperPtr,
+ regFragPtr, regTabPtr, pagePtr.p) == -1) {
+ return;
+ }//if
+ if (!regTabPtr->tuxCustomTriggers.isEmpty()) {
+ jam();
+ if (executeTuxInsertTriggers(signal, regOperPtr, regTabPtr) != 0) {
+ jam();
+ tupkeyErrorLab(signal);
+ return;
+ }
+ }
+ checkImmediateTriggersAfterInsert(signal,
+ regOperPtr,
+ regTabPtr);
+ sendTUPKEYCONF(signal, regOperPtr, regOperPtr->logSize);
+ return;
+ }//if
+ if (regTabPtr->checksumIndicator &&
+ (calculateChecksum(pagePtr.p,
+ regOperPtr->pageOffset,
+ regTabPtr->tupheadsize) != 0)) {
+ jam();
+ terrorCode = ZTUPLE_CORRUPTED_ERROR;
+ tupkeyErrorLab(signal);
+ return;
+ }//if
+ if (Roptype == ZUPDATE) {
+ jam();
+ if (handleUpdateReq(signal, regOperPtr,
+ regFragPtr, regTabPtr, pagePtr.p) == -1) {
+ return;
+ }//if
+ // If update operation is done on primary,
+ // check any after op triggers
+ terrorCode = 0;
+ if (!regTabPtr->tuxCustomTriggers.isEmpty()) {
+ jam();
+ if (executeTuxUpdateTriggers(signal, regOperPtr, regTabPtr) != 0) {
+ jam();
+ tupkeyErrorLab(signal);
+ return;
+ }
+ }
+ checkImmediateTriggersAfterUpdate(signal,
+ regOperPtr,
+ regTabPtr);
+ // XXX use terrorCode for now since all methods are void
+ if (terrorCode != 0) {
+ tupkeyErrorLab(signal);
+ return;
+ }
+ sendTUPKEYCONF(signal, regOperPtr, regOperPtr->logSize);
+ return;
+ } else if (Roptype == ZDELETE) {
+ jam();
+ if (handleDeleteReq(signal, regOperPtr,
+ regFragPtr, regTabPtr, pagePtr.p) == -1) {
+ return;
+ }//if
+ // If delete operation is done on primary,
+ // check any after op triggers
+ if (!regTabPtr->tuxCustomTriggers.isEmpty()) {
+ jam();
+ if (executeTuxDeleteTriggers(signal, regOperPtr, regTabPtr) != 0) {
+ jam();
+ tupkeyErrorLab(signal);
+ return;
+ }
+ }
+ checkImmediateTriggersAfterDelete(signal,
+ regOperPtr,
+ regTabPtr);
+ sendTUPKEYCONF(signal, regOperPtr, 0);
+ return;
+ } else {
+ ndbrequire(false);
+ }//if
+}//Dbtup::execTUPKEYREQ()
+
+/* ---------------------------------------------------------------- */
+/* ------------------------ CONFIRM REQUEST ----------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::sendTUPKEYCONF(Signal* signal,
+ Operationrec * const regOperPtr,
+ Uint32 TlogSize)
+{
+ TupKeyConf * const tupKeyConf = (TupKeyConf *)signal->getDataPtrSend();
+
+ Uint32 RuserPointer = regOperPtr->userpointer;
+ Uint32 RattroutbufLen = regOperPtr->attroutbufLen;
+ Uint32 RnoFiredTriggers = regOperPtr->noFiredTriggers;
+ BlockReference Ruserblockref = regOperPtr->userblockref;
+ Uint32 lastRow = regOperPtr->lastRow;
+
+ regOperPtr->transstate = STARTED;
+ regOperPtr->tupleState = NO_OTHER_OP;
+ tupKeyConf->userPtr = RuserPointer;
+ tupKeyConf->readLength = RattroutbufLen;
+ tupKeyConf->writeLength = TlogSize;
+ tupKeyConf->noFiredTriggers = RnoFiredTriggers;
+ tupKeyConf->lastRow = lastRow;
+
+ EXECUTE_DIRECT(refToBlock(Ruserblockref), GSN_TUPKEYCONF, signal,
+ TupKeyConf::SignalLength);
+ return;
+}//Dbtup::sendTUPKEYCONF()
+
+#define MAX_READ (sizeof(signal->theData) > MAX_MESSAGE_SIZE ? MAX_MESSAGE_SIZE : sizeof(signal->theData))
+
+/* ---------------------------------------------------------------- */
+/* ----------------------------- READ ---------------------------- */
+/* ---------------------------------------------------------------- */
+int Dbtup::handleReadReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr,
+ Page* pagePtr)
+{
+ Uint32 Ttupheadoffset = regOperPtr->pageOffset;
+ const BlockReference sendBref = regOperPtr->recBlockref;
+ if (regTabPtr->checksumIndicator &&
+ (calculateChecksum(pagePtr, Ttupheadoffset,
+ regTabPtr->tupheadsize) != 0)) {
+ jam();
+ terrorCode = ZTUPLE_CORRUPTED_ERROR;
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+
+ Uint32 * dst = &signal->theData[25];
+ Uint32 dstLen = (MAX_READ / 4) - 25;
+ const Uint32 node = refToNode(sendBref);
+ if(node != 0 && node != getOwnNodeId()) {
+ ;
+ } else {
+ jam();
+ /**
+ * execute direct
+ */
+ dst = &signal->theData[3];
+ dstLen = (MAX_READ / 4) - 3;
+ }
+
+ if (regOperPtr->interpretedExec != 1) {
+ jam();
+ int ret = readAttributes(pagePtr,
+ Ttupheadoffset,
+ &cinBuffer[0],
+ regOperPtr->attrinbufLen,
+ dst,
+ dstLen,
+ false);
+ if (ret != -1) {
+/* ------------------------------------------------------------------------- */
+// We have read all data into coutBuffer. Now send it to the API.
+/* ------------------------------------------------------------------------- */
+ jam();
+ Uint32 TnoOfDataRead= (Uint32) ret;
+ regOperPtr->attroutbufLen = TnoOfDataRead;
+ sendReadAttrinfo(signal, TnoOfDataRead, regOperPtr);
+ return 0;
+ }//if
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ } else {
+ jam();
+ regOperPtr->lastRow = 0;
+ if (interpreterStartLab(signal, pagePtr, Ttupheadoffset) != -1) {
+ return 0;
+ }//if
+ return -1;
+ }//if
+}//Dbtup::handleReadReq()
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------- UPDATE ---------------------------- */
+/* ---------------------------------------------------------------- */
+int Dbtup::handleUpdateReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr)
+{
+ PagePtr copyPagePtr;
+ Uint32 tuple_size = regTabPtr->tupheadsize;
+
+//---------------------------------------------------
+/* --- MAKE A COPY OF THIS TUPLE ON A COPY PAGE --- */
+//---------------------------------------------------
+ Uint32 RpageOffsetC;
+ if (!allocTh(regFragPtr,
+ regTabPtr,
+ COPY_PAGE,
+ signal,
+ RpageOffsetC,
+ copyPagePtr)) {
+ TUPKEY_abort(signal, 1);
+ return -1;
+ }//if
+ Uint32 RpageIdC = copyPagePtr.i;
+ Uint32 RfragPageIdC = copyPagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ Uint32 indexC = ((RpageOffsetC - ZPAGE_HEADER_SIZE) / tuple_size) << 1;
+ regOperPtr->pageIndexC = indexC;
+ regOperPtr->fragPageIdC = RfragPageIdC;
+ regOperPtr->realPageIdC = RpageIdC;
+ regOperPtr->pageOffsetC = RpageOffsetC;
+ /* -------------------------------------------------------------- */
+ /* IF WE HAVE AN ONGING CHECKPOINT WE HAVE TO LOG THE ALLOCATION */
+ /* OF THE TUPLE HEADER TO BE ABLE TO DELETE IT UPON RESTART */
+ /* THE ONLY DATA EXCEPT THE TYPE, PAGE, INDEX IS THE SIZE TO FREE */
+ /* -------------------------------------------------------------- */
+ if (isUndoLoggingActive(regFragPtr)) {
+ if (isPageUndoLogged(regFragPtr, RfragPageIdC)) {
+ jam();
+ regOperPtr->undoLogged = true;
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_DELETE_TH,
+ RfragPageIdC,
+ indexC,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ }//if
+ if (isPageUndoLogged(regFragPtr, regOperPtr->fragPageId)) {
+ jam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_TYPE_UPDATE_TH,
+ regOperPtr->fragPageId,
+ regOperPtr->pageIndex,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ cprAddData(signal,
+ regFragPtr,
+ regOperPtr->realPageId,
+ tuple_size,
+ regOperPtr->pageOffset);
+ }//if
+ }//if
+ Uint32 RwordCount = tuple_size - 1;
+ Uint32 end_dest = RpageOffsetC + tuple_size;
+ Uint32 offset = regOperPtr->pageOffset;
+ Uint32 end_source = offset + tuple_size;
+ ndbrequire(end_dest <= ZWORDS_ON_PAGE && end_source <= ZWORDS_ON_PAGE);
+ void* Tdestination = (void*)&copyPagePtr.p->pageWord[RpageOffsetC + 1];
+ const void* Tsource = (void*)&pagePtr->pageWord[offset + 1];
+ MEMCOPY_NO_WORDS(Tdestination, Tsource, RwordCount);
+
+ Uint32 prev_tup_version;
+ // nextActiveOp is before this op in event order
+ if (regOperPtr->nextActiveOp == RNIL) {
+ jam();
+ prev_tup_version = ((const Uint32*)Tsource)[0];
+ } else {
+ OperationrecPtr prevOperPtr;
+ jam();
+ prevOperPtr.i = regOperPtr->nextActiveOp;
+ ptrCheckGuard(prevOperPtr, cnoOfOprec, operationrec);
+ prev_tup_version = prevOperPtr.p->tupVersion;
+ }//if
+ regOperPtr->tupVersion = (prev_tup_version + 1) &
+ ((1 << ZTUP_VERSION_BITS) - 1);
+ // global variable alert
+ ndbassert(operationrec + operPtr.i == regOperPtr);
+ copyPagePtr.p->pageWord[RpageOffsetC] = operPtr.i;
+
+ return updateStartLab(signal, regOperPtr, regTabPtr, pagePtr);
+}//Dbtup::handleUpdateReq()
+
+/* ---------------------------------------------------------------- */
+/* ----------------------------- INSERT --------------------------- */
+/* ---------------------------------------------------------------- */
+int Dbtup::handleInsertReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr)
+{
+ Uint32 ret_value;
+
+ if (regOperPtr->nextActiveOp != RNIL) {
+ jam();
+ OperationrecPtr prevExecOpPtr;
+ prevExecOpPtr.i = regOperPtr->nextActiveOp;
+ ptrCheckGuard(prevExecOpPtr, cnoOfOprec, operationrec);
+ if (prevExecOpPtr.p->optype != ZDELETE) {
+ terrorCode = ZINSERT_ERROR;
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+ ret_value = handleUpdateReq(signal, regOperPtr,
+ regFragPtr, regTabPtr, pagePtr);
+ } else {
+ jam();
+ regOperPtr->tupVersion = 0;
+ ret_value = updateStartLab(signal, regOperPtr, regTabPtr, pagePtr);
+ }//if
+ if (ret_value != (Uint32)-1) {
+ if (checkNullAttributes(regOperPtr, regTabPtr)) {
+ jam();
+ return 0;
+ }//if
+ TUPKEY_abort(signal, 17);
+ }//if
+ return -1;
+}//Dbtup::handleInsertReq()
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------- DELETE ---------------------------- */
+/* ---------------------------------------------------------------- */
+int Dbtup::handleDeleteReq(Signal* signal,
+ Operationrec* const regOperPtr,
+ Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr)
+{
+ // delete must set but not increment tupVersion
+ if (regOperPtr->nextActiveOp != RNIL) {
+ OperationrecPtr prevExecOpPtr;
+ prevExecOpPtr.i = regOperPtr->nextActiveOp;
+ ptrCheckGuard(prevExecOpPtr, cnoOfOprec, operationrec);
+ regOperPtr->tupVersion = prevExecOpPtr.p->tupVersion;
+ } else {
+ jam();
+ regOperPtr->tupVersion = pagePtr->pageWord[regOperPtr->pageOffset + 1];
+ }
+ if (isUndoLoggingNeeded(regFragPtr, regOperPtr->fragPageId)) {
+ jam();
+ cprAddUndoLogRecord(signal,
+ ZINDICATE_NO_OP_ACTIVE,
+ regOperPtr->fragPageId,
+ regOperPtr->pageIndex,
+ regOperPtr->tableRef,
+ regOperPtr->fragId,
+ regFragPtr->checkpointVersion);
+ }//if
+ if (regOperPtr->attrinbufLen == 0) {
+ return 0;
+ }//if
+/* ------------------------------------------------------------------------ */
+/* THE APPLICATION WANTS TO READ THE TUPLE BEFORE IT IS DELETED. */
+/* ------------------------------------------------------------------------ */
+ return handleReadReq(signal, regOperPtr, regTabPtr, pagePtr);
+}//Dbtup::handleDeleteReq()
+
+int
+Dbtup::updateStartLab(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr,
+ Page* const pagePtr)
+{
+ int retValue;
+ if (regOperPtr->optype == ZINSERT) {
+ jam();
+ setNullBits(pagePtr, regTabPtr, regOperPtr->pageOffset);
+ }
+ if (regOperPtr->interpretedExec != 1) {
+ jam();
+ retValue = updateAttributes(pagePtr,
+ regOperPtr->pageOffset,
+ &cinBuffer[0],
+ regOperPtr->attrinbufLen);
+ if (retValue == -1) {
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+ } else {
+ jam();
+ retValue = interpreterStartLab(signal, pagePtr, regOperPtr->pageOffset);
+ }//if
+ ndbrequire(regOperPtr->tupVersion != ZNIL);
+ pagePtr->pageWord[regOperPtr->pageOffset + 1] = regOperPtr->tupVersion;
+ if (regTabPtr->checksumIndicator) {
+ jam();
+ setChecksum(pagePtr, regOperPtr->pageOffset, regTabPtr->tupheadsize);
+ }//if
+ return retValue;
+}//Dbtup::updateStartLab()
+
+void
+Dbtup::setNullBits(Page* const regPage, Tablerec* const regTabPtr, Uint32 pageOffset)
+{
+ Uint32 noOfExtraNullWords = regTabPtr->tupNullWords;
+ Uint32 nullOffsetStart = regTabPtr->tupNullIndex + pageOffset;
+ ndbrequire((noOfExtraNullWords + nullOffsetStart) < ZWORDS_ON_PAGE);
+ for (Uint32 i = 0; i < noOfExtraNullWords; i++) {
+ regPage->pageWord[nullOffsetStart + i] = 0xFFFFFFFF;
+ }//for
+}//Dbtup::setNullBits()
+
+bool
+Dbtup::checkNullAttributes(Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+// Implement checking of updating all not null attributes in an insert here.
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attributeMask;
+ /*
+ * The idea here is maybe that changeMask is not-null attributes
+ * and must contain notNullAttributeMask. But:
+ *
+ * 1. changeMask has all bits set on insert
+ * 2. not-null is checked in each UpdateFunction
+ * 3. the code below does not work except trivially due to 1.
+ *
+ * XXX remove or fix
+ */
+ attributeMask.clear();
+ attributeMask.bitOR(regOperPtr->changeMask);
+ attributeMask.bitAND(regTabPtr->notNullAttributeMask);
+ attributeMask.bitXOR(regTabPtr->notNullAttributeMask);
+ if (!attributeMask.isclear()) {
+ return false;
+ }//if
+ return true;
+}//Dbtup::checkNullAttributes()
+
+/* ---------------------------------------------------------------- */
+/* THIS IS THE START OF THE INTERPRETED EXECUTION OF UPDATES. WE */
+/* START BY LINKING ALL ATTRINFO'S IN A DOUBLY LINKED LIST (THEY ARE*/
+/* ALREADY IN A LINKED LIST). WE ALLOCATE A REGISTER MEMORY (EQUAL */
+/* TO AN ATTRINFO RECORD). THE INTERPRETER GOES THROUGH FOUR PHASES*/
+/* DURING THE FIRST PHASE IT IS ONLY ALLOWED TO READ ATTRIBUTES THAT*/
+/* ARE SENT TO THE CLIENT APPLICATION. DURING THE SECOND PHASE IT IS*/
+/* ALLOWED TO READ FROM ATTRIBUTES INTO REGISTERS, TO UPDATE */
+/* ATTRIBUTES BASED ON EITHER A CONSTANT VALUE OR A REGISTER VALUE, */
+/* A DIVERSE SET OF OPERATIONS ON REGISTERS ARE AVAILABLE AS WELL. */
+/* IT IS ALSO POSSIBLE TO PERFORM JUMPS WITHIN THE INSTRUCTIONS THAT*/
+/* BELONGS TO THE SECOND PHASE. ALSO SUBROUTINES CAN BE CALLED IN */
+/* THIS PHASE. THE THIRD PHASE IS TO AGAIN READ ATTRIBUTES AND */
+/* FINALLY THE FOURTH PHASE READS SELECTED REGISTERS AND SEND THEM */
+/* TO THE CLIENT APPLICATION. */
+/* THERE IS A FIFTH REGION WHICH CONTAINS SUBROUTINES CALLABLE FROM */
+/* THE INTERPRETER EXECUTION REGION. */
+/* THE FIRST FIVE WORDS WILL GIVE THE LENGTH OF THE FIVEE REGIONS */
+/* */
+/* THIS MEANS THAT FROM THE APPLICATIONS POINT OF VIEW THE DATABASE */
+/* CAN HANDLE SUBROUTINE CALLS WHERE THE CODE IS SENT IN THE REQUEST*/
+/* THE RETURN PARAMETERS ARE FIXED AND CAN EITHER BE GENERATED */
+/* BEFORE THE EXECUTION OF THE ROUTINE OR AFTER. */
+/* */
+/* IN LATER VERSIONS WE WILL ADD MORE THINGS LIKE THE POSSIBILITY */
+/* TO ALLOCATE MEMORY AND USE THIS AS LOCAL STORAGE. IT IS ALSO */
+/* IMAGINABLE TO HAVE SPECIAL ROUTINES THAT CAN PERFORM CERTAIN */
+/* OPERATIONS ON BLOB'S DEPENDENT ON WHAT THE BLOB REPRESENTS. */
+/* */
+/* */
+/* ----------------------------------------- */
+/* + INITIAL READ REGION + */
+/* ----------------------------------------- */
+/* + INTERPRETED EXECUTE REGION + */
+/* ----------------------------------------- */
+/* + FINAL UPDATE REGION + */
+/* ----------------------------------------- */
+/* + FINAL READ REGION + */
+/* ----------------------------------------- */
+/* + SUBROUTINE REGION + */
+/* ----------------------------------------- */
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/* ----------------- INTERPRETED EXECUTION ----------------------- */
+/* ---------------------------------------------------------------- */
+int Dbtup::interpreterStartLab(Signal* signal,
+ Page* const pagePtr,
+ Uint32 TupHeadOffset)
+{
+ Operationrec * const regOperPtr = operPtr.p;
+ Uint32 RtotalLen;
+ int TnoDataRW;
+
+ Uint32 RinitReadLen = cinBuffer[0];
+ Uint32 RexecRegionLen = cinBuffer[1];
+ Uint32 RfinalUpdateLen = cinBuffer[2];
+ Uint32 RfinalRLen = cinBuffer[3];
+ Uint32 RsubLen = cinBuffer[4];
+
+ Uint32 RattrinbufLen = regOperPtr->attrinbufLen;
+ const BlockReference sendBref = regOperPtr->recBlockref;
+
+ Uint32 * dst = &signal->theData[25];
+ Uint32 dstLen = (MAX_READ / 4) - 25;
+ const Uint32 node = refToNode(sendBref);
+ if(node != 0 && node != getOwnNodeId()) {
+ ;
+ } else {
+ jam();
+ /**
+ * execute direct
+ */
+ dst = &signal->theData[3];
+ dstLen = (MAX_READ / 4) - 3;
+ }
+
+ RtotalLen = RinitReadLen;
+ RtotalLen += RexecRegionLen;
+ RtotalLen += RfinalUpdateLen;
+ RtotalLen += RfinalRLen;
+ RtotalLen += RsubLen;
+
+ Uint32 RattroutCounter = 0;
+ Uint32 RinstructionCounter = 5;
+ Uint32 RlogSize = 0;
+
+ if (((RtotalLen + 5) == RattrinbufLen) &&
+ (RattrinbufLen >= 5) &&
+ (RattrinbufLen < ZATTR_BUFFER_SIZE)) {
+ /* ---------------------------------------------------------------- */
+ // We start by checking consistency. We must have the first five
+ // words of the ATTRINFO to give us the length of the regions. The
+ // size of these regions must be the same as the total ATTRINFO
+ // length and finally the total length must be within the limits.
+ /* ---------------------------------------------------------------- */
+
+ if (RinitReadLen > 0) {
+ jam();
+ /* ---------------------------------------------------------------- */
+ // The first step that can be taken in the interpreter is to read
+ // data of the tuple before any updates have been applied.
+ /* ---------------------------------------------------------------- */
+ TnoDataRW = readAttributes(pagePtr,
+ TupHeadOffset,
+ &cinBuffer[5],
+ RinitReadLen,
+ &dst[0],
+ dstLen,
+ false);
+ if (TnoDataRW != -1) {
+ RattroutCounter = TnoDataRW;
+ RinstructionCounter += RinitReadLen;
+ } else {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+ }//if
+ if (RexecRegionLen > 0) {
+ jam();
+ /* ---------------------------------------------------------------- */
+ // The next step is the actual interpreted execution. This executes
+ // a register-based virtual machine which can read and write attributes
+ // to and from registers.
+ /* ---------------------------------------------------------------- */
+ Uint32 RsubPC = RinstructionCounter + RfinalUpdateLen + RfinalRLen;
+ TnoDataRW = interpreterNextLab(signal,
+ pagePtr,
+ TupHeadOffset,
+ &clogMemBuffer[0],
+ &cinBuffer[RinstructionCounter],
+ RexecRegionLen,
+ &cinBuffer[RsubPC],
+ RsubLen,
+ &coutBuffer[0],
+ sizeof(coutBuffer) / 4);
+ if (TnoDataRW != -1) {
+ RinstructionCounter += RexecRegionLen;
+ RlogSize = TnoDataRW;
+ } else {
+ jam();
+ return -1;
+ }//if
+ }//if
+ if (RfinalUpdateLen > 0) {
+ jam();
+ /* ---------------------------------------------------------------- */
+ // We can also apply a set of updates without any conditions as part
+ // of the interpreted execution.
+ /* ---------------------------------------------------------------- */
+ if (regOperPtr->optype == ZUPDATE) {
+ TnoDataRW = updateAttributes(pagePtr,
+ TupHeadOffset,
+ &cinBuffer[RinstructionCounter],
+ RfinalUpdateLen);
+ if (TnoDataRW != -1) {
+ MEMCOPY_NO_WORDS(&clogMemBuffer[RlogSize],
+ &cinBuffer[RinstructionCounter],
+ RfinalUpdateLen);
+ RinstructionCounter += RfinalUpdateLen;
+ RlogSize += RfinalUpdateLen;
+ } else {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 19);
+ }//if
+ }//if
+ if (RfinalRLen > 0) {
+ jam();
+ /* ---------------------------------------------------------------- */
+ // The final action is that we can also read the tuple after it has
+ // been updated.
+ /* ---------------------------------------------------------------- */
+ TnoDataRW = readAttributes(pagePtr,
+ TupHeadOffset,
+ &cinBuffer[RinstructionCounter],
+ RfinalRLen,
+ &dst[RattroutCounter],
+ (dstLen - RattroutCounter),
+ false);
+ if (TnoDataRW != -1) {
+ RattroutCounter += TnoDataRW;
+ } else {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+ }//if
+ regOperPtr->logSize = RlogSize;
+ regOperPtr->attroutbufLen = RattroutCounter;
+ sendReadAttrinfo(signal, RattroutCounter, regOperPtr);
+ if (RlogSize > 0) {
+ sendLogAttrinfo(signal, RlogSize, regOperPtr);
+ }//if
+ return 0;
+ } else {
+ return TUPKEY_abort(signal, 22);
+ }//if
+}//Dbtup::interpreterStartLab()
+
+/* ---------------------------------------------------------------- */
+/* WHEN EXECUTION IS INTERPRETED WE NEED TO SEND SOME ATTRINFO*/
+/* BACK TO LQH FOR LOGGING AND SENDING TO BACKUP AND STANDBY */
+/* NODES. */
+/* INPUT: LOG_ATTRINFOPTR WHERE TO FETCH DATA FROM */
+/* TLOG_START FIRST INDEX TO LOG */
+/* TLOG_END LAST INDEX + 1 TO LOG */
+/* ---------------------------------------------------------------- */
+void Dbtup::sendLogAttrinfo(Signal* signal,
+ Uint32 TlogSize,
+ Operationrec * const regOperPtr)
+
+{
+ Uint32 TbufferIndex = 0;
+ signal->theData[0] = regOperPtr->userpointer;
+ while (TlogSize > 22) {
+ MEMCOPY_NO_WORDS(&signal->theData[3],
+ &clogMemBuffer[TbufferIndex],
+ 22);
+ EXECUTE_DIRECT(refToBlock(regOperPtr->userblockref),
+ GSN_TUP_ATTRINFO, signal, 25);
+ TbufferIndex += 22;
+ TlogSize -= 22;
+ }//while
+ MEMCOPY_NO_WORDS(&signal->theData[3],
+ &clogMemBuffer[TbufferIndex],
+ TlogSize);
+ EXECUTE_DIRECT(refToBlock(regOperPtr->userblockref),
+ GSN_TUP_ATTRINFO, signal, 3 + TlogSize);
+}//Dbtup::sendLogAttrinfo()
+
+inline
+Uint32
+brancher(Uint32 TheInstruction, Uint32 TprogramCounter)
+{
+ Uint32 TbranchDirection = TheInstruction >> 31;
+ Uint32 TbranchLength = (TheInstruction >> 16) & 0x7fff;
+ TprogramCounter--;
+ if (TbranchDirection == 1) {
+ jam();
+ /* ---------------------------------------------------------------- */
+ /* WE JUMP BACKWARDS. */
+ /* ---------------------------------------------------------------- */
+ return (TprogramCounter - TbranchLength);
+ } else {
+ jam();
+ /* ---------------------------------------------------------------- */
+ /* WE JUMP FORWARD. */
+ /* ---------------------------------------------------------------- */
+ return (TprogramCounter + TbranchLength);
+ }//if
+}//brancher()
+
+int Dbtup::interpreterNextLab(Signal* signal,
+ Page* const pagePtr,
+ Uint32 TupHeadOffset,
+ Uint32* logMemory,
+ Uint32* mainProgram,
+ Uint32 TmainProgLen,
+ Uint32* subroutineProg,
+ Uint32 TsubroutineLen,
+ Uint32 * tmpArea,
+ Uint32 tmpAreaSz)
+{
+ register Uint32* TcurrentProgram = mainProgram;
+ register Uint32 TcurrentSize = TmainProgLen;
+ register Uint32 RnoOfInstructions = 0;
+ register Uint32 TprogramCounter = 0;
+ register Uint32 theInstruction;
+ register Uint32 theRegister;
+ Uint32 TdataWritten = 0;
+ Uint32 RstackPtr = 0;
+ union {
+ Uint32 TregMemBuffer[32];
+ Uint64 Tdummy[16];
+ };
+ Uint32 TstackMemBuffer[32];
+
+ /* ---------------------------------------------------------------- */
+ // Initialise all 8 registers to contain the NULL value.
+ // In this version we can handle 32 and 64 bit unsigned integers.
+ // They are handled as 64 bit values. Thus the 32 most significant
+ // bits are zeroed for 32 bit values.
+ /* ---------------------------------------------------------------- */
+ TregMemBuffer[0] = 0;
+ TregMemBuffer[4] = 0;
+ TregMemBuffer[8] = 0;
+ TregMemBuffer[12] = 0;
+ TregMemBuffer[16] = 0;
+ TregMemBuffer[20] = 0;
+ TregMemBuffer[24] = 0;
+ TregMemBuffer[28] = 0;
+ Uint32 tmpHabitant = ~0;
+
+ while (RnoOfInstructions < 8000) {
+ /* ---------------------------------------------------------------- */
+ /* EXECUTE THE NEXT INTERPRETER INSTRUCTION. */
+ /* ---------------------------------------------------------------- */
+ RnoOfInstructions++;
+ theInstruction = TcurrentProgram[TprogramCounter];
+ theRegister = Interpreter::getReg1(theInstruction) << 2;
+ if (TprogramCounter < TcurrentSize) {
+ TprogramCounter++;
+ switch (Interpreter::getOpCode(theInstruction)) {
+ case Interpreter::READ_ATTR_INTO_REG:
+ jam();
+ /* ---------------------------------------------------------------- */
+ // Read an attribute from the tuple into a register.
+ // While reading an attribute we allow the attribute to be an array
+ // as long as it fits in the 64 bits of the register.
+ /* ---------------------------------------------------------------- */
+ {
+ Uint32 theAttrinfo = theInstruction;
+ int TnoDataRW= readAttributes(pagePtr,
+ TupHeadOffset,
+ &theAttrinfo,
+ (Uint32)1,
+ &TregMemBuffer[theRegister],
+ (Uint32)3,
+ false);
+ if (TnoDataRW == 2) {
+ /* ------------------------------------------------------------- */
+ // Two words read means that we get the instruction plus one 32
+ // word read. Thus we set the register to be a 32 bit register.
+ /* ------------------------------------------------------------- */
+ TregMemBuffer[theRegister] = 0x50;
+ * (Int64*)(TregMemBuffer+theRegister+2) = TregMemBuffer[theRegister+1];
+ } else if (TnoDataRW == 3) {
+ /* ------------------------------------------------------------- */
+ // Three words read means that we get the instruction plus two
+ // 32 words read. Thus we set the register to be a 64 bit register.
+ /* ------------------------------------------------------------- */
+ TregMemBuffer[theRegister] = 0x60;
+ TregMemBuffer[theRegister+3] = TregMemBuffer[theRegister+2];
+ TregMemBuffer[theRegister+2] = TregMemBuffer[theRegister+1];
+ } else if (TnoDataRW == 1) {
+ /* ------------------------------------------------------------- */
+ // One word read means that we must have read a NULL value. We set
+ // the register to indicate a NULL value.
+ /* ------------------------------------------------------------- */
+ TregMemBuffer[theRegister] = 0;
+ TregMemBuffer[theRegister + 2] = 0;
+ TregMemBuffer[theRegister + 3] = 0;
+ } else if (TnoDataRW == -1) {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ } else {
+ /* ------------------------------------------------------------- */
+ // Any other return value from the read attribute here is not
+ // allowed and will lead to a system crash.
+ /* ------------------------------------------------------------- */
+ ndbrequire(false);
+ }//if
+ break;
+ }
+
+ case Interpreter::WRITE_ATTR_FROM_REG:
+ jam();
+ {
+ Uint32 TattrId = theInstruction >> 16;
+ Uint32 TattrDescrIndex = tabptr.p->tabDescriptor +
+ (TattrId << ZAD_LOG_SIZE);
+ Uint32 TattrDesc1 = tableDescriptor[TattrDescrIndex].tabDescr;
+ Uint32 TregType = TregMemBuffer[theRegister];
+
+ /* --------------------------------------------------------------- */
+ // Calculate the number of words of this attribute.
+ // We allow writes into arrays as long as they fit into the 64 bit
+ // register size.
+ /* --------------------------------------------------------------- */
+ Uint32 TattrNoOfWords = AttributeDescriptor::getSizeInWords(TattrDesc1);
+ Uint32 Toptype = operPtr.p->optype;
+
+ Uint32 TdataForUpdate[3];
+ Uint32 Tlen;
+
+ AttributeHeader& ah = AttributeHeader::init(&TdataForUpdate[0],
+ TattrId, TattrNoOfWords);
+ TdataForUpdate[1] = TregMemBuffer[theRegister + 2];
+ TdataForUpdate[2] = TregMemBuffer[theRegister + 3];
+ Tlen = TattrNoOfWords + 1;
+ if (Toptype == ZUPDATE) {
+ if (TattrNoOfWords <= 2) {
+ if (TregType == 0) {
+ /* --------------------------------------------------------- */
+ // Write a NULL value into the attribute
+ /* --------------------------------------------------------- */
+ ah.setNULL();
+ Tlen = 1;
+ }//if
+ int TnoDataRW= updateAttributes(pagePtr,
+ TupHeadOffset,
+ &TdataForUpdate[0],
+ Tlen);
+ if (TnoDataRW != -1) {
+ /* --------------------------------------------------------- */
+ // Write the written data also into the log buffer so that it
+ // will be logged.
+ /* --------------------------------------------------------- */
+ logMemory[TdataWritten + 0] = TdataForUpdate[0];
+ logMemory[TdataWritten + 1] = TdataForUpdate[1];
+ logMemory[TdataWritten + 2] = TdataForUpdate[2];
+ TdataWritten += Tlen;
+ } else {
+ tupkeyErrorLab(signal);
+ return -1;
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 15);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 16);
+ }//if
+ break;
+ }
+
+ case Interpreter::LOAD_CONST_NULL:
+ jam();
+ TregMemBuffer[theRegister] = 0; /* NULL INDICATOR */
+ break;
+
+ case Interpreter::LOAD_CONST16:
+ jam();
+ TregMemBuffer[theRegister] = 0x50; /* 32 BIT UNSIGNED CONSTANT */
+ * (Int64*)(TregMemBuffer+theRegister+2) = theInstruction >> 16;
+ break;
+
+ case Interpreter::LOAD_CONST32:
+ jam();
+ TregMemBuffer[theRegister] = 0x50; /* 32 BIT UNSIGNED CONSTANT */
+ * (Int64*)(TregMemBuffer+theRegister+2) = *
+ (TcurrentProgram+TprogramCounter);
+ TprogramCounter++;
+ break;
+
+ case Interpreter::LOAD_CONST64:
+ jam();
+ TregMemBuffer[theRegister] = 0x60; /* 64 BIT UNSIGNED CONSTANT */
+ TregMemBuffer[theRegister + 2 ] = * (TcurrentProgram + TprogramCounter++);
+ TregMemBuffer[theRegister + 3 ] = * (TcurrentProgram + TprogramCounter++);
+ break;
+
+ case Interpreter::ADD_REG_REG:
+ jam();
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+ Uint32 TdestRegister = Interpreter::getReg3(theInstruction) << 2;
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Int64 Tright0 = * (Int64*)(TregMemBuffer + TrightRegister + 2);
+
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Int64 Tleft0 = * (Int64*)(TregMemBuffer + theRegister + 2);
+
+ if ((TleftType | TrightType) != 0) {
+ Uint64 Tdest0 = Tleft0 + Tright0;
+ * (Int64*)(TregMemBuffer+TdestRegister+2) = Tdest0;
+ TregMemBuffer[TdestRegister] = 0x60;
+ } else {
+ return TUPKEY_abort(signal, 20);
+ }
+ break;
+ }
+
+ case Interpreter::SUB_REG_REG:
+ jam();
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+ Uint32 TdestRegister = Interpreter::getReg3(theInstruction) << 2;
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Int64 Tright0 = * (Int64*)(TregMemBuffer + TrightRegister + 2);
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Int64 Tleft0 = * (Int64*)(TregMemBuffer + theRegister + 2);
+
+ if ((TleftType | TrightType) != 0) {
+ Int64 Tdest0 = Tleft0 - Tright0;
+ * (Int64*)(TregMemBuffer+TdestRegister+2) = Tdest0;
+ TregMemBuffer[TdestRegister] = 0x60;
+ } else {
+ return TUPKEY_abort(signal, 20);
+ }
+ break;
+ }
+
+ case Interpreter::BRANCH:
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ break;
+
+ case Interpreter::BRANCH_REG_EQ_NULL:
+ if (TregMemBuffer[theRegister] != 0) {
+ jam();
+ continue;
+ } else {
+ jam();
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ break;
+
+ case Interpreter::BRANCH_REG_NE_NULL:
+ if (TregMemBuffer[theRegister] == 0) {
+ jam();
+ continue;
+ } else {
+ jam();
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ break;
+
+
+ case Interpreter::BRANCH_EQ_REG_REG:
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Uint32 Tleft0 = TregMemBuffer[theRegister + 2];
+ Uint32 Tleft1 = TregMemBuffer[theRegister + 3];
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Uint32 Tright0 = TregMemBuffer[TrightRegister + 2];
+ Uint32 Tright1 = TregMemBuffer[TrightRegister + 3];
+ if ((TrightType | TleftType) != 0) {
+ jam();
+ if ((Tleft0 == Tright0) && (Tleft1 == Tright1)) {
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 23);
+ }//if
+ break;
+ }
+
+ case Interpreter::BRANCH_NE_REG_REG:
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Uint32 Tleft0 = TregMemBuffer[theRegister + 2];
+ Uint32 Tleft1 = TregMemBuffer[theRegister + 3];
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Uint32 Tright0 = TregMemBuffer[TrightRegister + 2];
+ Uint32 Tright1 = TregMemBuffer[TrightRegister + 3];
+ if ((TrightType | TleftType) != 0) {
+ jam();
+ if ((Tleft0 != Tright0) || (Tleft1 != Tright1)) {
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 24);
+ }//if
+ break;
+ }
+
+ case Interpreter::BRANCH_LT_REG_REG:
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Int64 Tright0 = * (Int64*)(TregMemBuffer + TrightRegister + 2);
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Int64 Tleft0 = * (Int64*)(TregMemBuffer + theRegister + 2);
+
+
+ if ((TrightType | TleftType) != 0) {
+ jam();
+ if (Tleft0 < Tright0) {
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 24);
+ }//if
+ break;
+ }
+
+ case Interpreter::BRANCH_LE_REG_REG:
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Int64 Tright0 = * (Int64*)(TregMemBuffer + TrightRegister + 2);
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Int64 Tleft0 = * (Int64*)(TregMemBuffer + theRegister + 2);
+
+
+ if ((TrightType | TleftType) != 0) {
+ jam();
+ if (Tleft0 <= Tright0) {
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 26);
+ }//if
+ break;
+ }
+
+ case Interpreter::BRANCH_GT_REG_REG:
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Int64 Tright0 = * (Int64*)(TregMemBuffer + TrightRegister + 2);
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Int64 Tleft0 = * (Int64*)(TregMemBuffer + theRegister + 2);
+
+
+ if ((TrightType | TleftType) != 0) {
+ jam();
+ if (Tleft0 > Tright0){
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 27);
+ }//if
+ break;
+ }
+
+ case Interpreter::BRANCH_GE_REG_REG:
+ {
+ Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
+
+ Uint32 TrightType = TregMemBuffer[TrightRegister];
+ Int64 Tright0 = * (Int64*)(TregMemBuffer + TrightRegister + 2);
+
+ Uint32 TleftType = TregMemBuffer[theRegister];
+ Int64 Tleft0 = * (Int64*)(TregMemBuffer + theRegister + 2);
+
+
+ if ((TrightType | TleftType) != 0) {
+ jam();
+ if (Tleft0 >= Tright0){
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 28);
+ }//if
+ break;
+ }
+
+ case Interpreter::BRANCH_ATTR_OP_ARG:{
+ jam();
+ Uint32 cond = Interpreter::getBinaryCondition(theInstruction);
+ Uint32 ins2 = TcurrentProgram[TprogramCounter];
+ Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16;
+ Uint32 argLen = Interpreter::getBranchCol_Len(ins2);
+
+ if(tmpHabitant != attrId){
+ Int32 TnoDataR = readAttributes(pagePtr,
+ TupHeadOffset,
+ &attrId, 1,
+ tmpArea, tmpAreaSz,
+ false);
+
+ if (TnoDataR == -1) {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ }
+ tmpHabitant = attrId;
+ }
+
+ // get type
+ attrId >>= 16;
+ Uint32 TattrDescrIndex = tabptr.p->tabDescriptor +
+ (attrId << ZAD_LOG_SIZE);
+ Uint32 TattrDesc1 = tableDescriptor[TattrDescrIndex].tabDescr;
+ Uint32 TattrDesc2 = tableDescriptor[TattrDescrIndex+1].tabDescr;
+ Uint32 typeId = AttributeDescriptor::getType(TattrDesc1);
+ void * cs = 0;
+ if(AttributeOffset::getCharsetFlag(TattrDesc2))
+ {
+ Uint32 pos = AttributeOffset::getCharsetPos(TattrDesc2);
+ cs = tabptr.p->charsetArray[pos];
+ }
+ const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(typeId);
+
+ // get data
+ AttributeHeader ah(tmpArea[0]);
+ const char* s1 = (char*)&tmpArea[1];
+ const char* s2 = (char*)&TcurrentProgram[TprogramCounter+1];
+ // fixed length in 5.0
+ Uint32 attrLen = AttributeDescriptor::getSizeInBytes(TattrDesc1);
+
+ bool r1_null = ah.isNULL();
+ bool r2_null = argLen == 0;
+ int res1;
+ if (cond != Interpreter::LIKE &&
+ cond != Interpreter::NOT_LIKE) {
+ if (r1_null || r2_null) {
+ // NULL==NULL and NULL<not-NULL
+ res1 = r1_null && r2_null ? 0 : r1_null ? -1 : 1;
+ } else {
+ res1 = (*sqlType.m_cmp)(cs, s1, attrLen, s2, argLen, true);
+ }
+ } else {
+ if (r1_null || r2_null) {
+ // NULL like NULL is true (has no practical use)
+ res1 = r1_null && r2_null ? 0 : -1;
+ } else {
+ res1 = (*sqlType.m_like)(cs, s1, attrLen, s2, argLen);
+ }
+ }
+
+ int res = 0;
+ switch ((Interpreter::BinaryCondition)cond) {
+ case Interpreter::EQ:
+ res = (res1 == 0);
+ break;
+ case Interpreter::NE:
+ res = (res1 != 0);
+ break;
+ // note the condition is backwards
+ case Interpreter::LT:
+ res = (res1 > 0);
+ break;
+ case Interpreter::LE:
+ res = (res1 >= 0);
+ break;
+ case Interpreter::GT:
+ res = (res1 < 0);
+ break;
+ case Interpreter::GE:
+ res = (res1 <= 0);
+ break;
+ case Interpreter::LIKE:
+ res = (res1 == 0);
+ break;
+ case Interpreter::NOT_LIKE:
+ res = (res1 == 1);
+ break;
+ // XXX handle invalid value
+ }
+#ifdef TRACE_INTERPRETER
+ ndbout_c("cond=%u attr(%d)='%.*s'(%d) str='%.*s'(%d) res1=%d res=%d",
+ cond, attrId >> 16,
+ attrLen, s1, attrLen, argLen, s2, argLen, res1, res);
+#endif
+ if (res)
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ else
+ {
+ Uint32 tmp = ((argLen + 3) >> 2) + 1;
+ TprogramCounter += tmp;
+ }
+ break;
+ }
+
+ case Interpreter::BRANCH_ATTR_EQ_NULL:{
+ jam();
+ Uint32 ins2 = TcurrentProgram[TprogramCounter];
+ Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16;
+
+ if(tmpHabitant != attrId){
+ Int32 TnoDataR = readAttributes(pagePtr,
+ TupHeadOffset,
+ &attrId, 1,
+ tmpArea, tmpAreaSz,
+ false);
+
+ if (TnoDataR == -1) {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ }
+ tmpHabitant = attrId;
+ }
+
+ AttributeHeader ah(tmpArea[0]);
+ if(ah.isNULL()){
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ } else {
+ TprogramCounter ++;
+ }
+ break;
+ }
+
+ case Interpreter::BRANCH_ATTR_NE_NULL:{
+ jam();
+ Uint32 ins2 = TcurrentProgram[TprogramCounter];
+ Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16;
+
+ if(tmpHabitant != attrId){
+ Int32 TnoDataR = readAttributes(pagePtr,
+ TupHeadOffset,
+ &attrId, 1,
+ tmpArea, tmpAreaSz,
+ false);
+
+ if (TnoDataR == -1) {
+ jam();
+ tupkeyErrorLab(signal);
+ return -1;
+ }
+ tmpHabitant = attrId;
+ }
+
+ AttributeHeader ah(tmpArea[0]);
+ if(ah.isNULL()){
+ TprogramCounter ++;
+ } else {
+ TprogramCounter = brancher(theInstruction, TprogramCounter);
+ }
+ break;
+ }
+
+ case Interpreter::EXIT_OK:
+ jam();
+#ifdef TRACE_INTERPRETER
+ ndbout_c(" - exit_ok");
+#endif
+ return TdataWritten;
+
+ case Interpreter::EXIT_OK_LAST:
+ jam();
+#ifdef TRACE_INTERPRETER
+ ndbout_c(" - exit_ok_last");
+#endif
+ operPtr.p->lastRow = 1;
+ return TdataWritten;
+
+ case Interpreter::EXIT_REFUSE:
+ jam();
+#ifdef TRACE_INTERPRETER
+ ndbout_c(" - exit_nok");
+#endif
+ terrorCode = theInstruction >> 16;
+ return TUPKEY_abort(signal, 29);
+
+ case Interpreter::CALL:
+ jam();
+ RstackPtr++;
+ if (RstackPtr < 32) {
+ TstackMemBuffer[RstackPtr] = TprogramCounter + 1;
+ TprogramCounter = theInstruction >> 16;
+ if (TprogramCounter < TsubroutineLen) {
+ TcurrentProgram = subroutineProg;
+ TcurrentSize = TsubroutineLen;
+ } else {
+ return TUPKEY_abort(signal, 30);
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 31);
+ }//if
+ break;
+
+ case Interpreter::RETURN:
+ jam();
+ if (RstackPtr > 0) {
+ TprogramCounter = TstackMemBuffer[RstackPtr];
+ RstackPtr--;
+ if (RstackPtr == 0) {
+ jam();
+ /* ------------------------------------------------------------- */
+ // We are back to the main program.
+ /* ------------------------------------------------------------- */
+ TcurrentProgram = mainProgram;
+ TcurrentSize = TmainProgLen;
+ }//if
+ } else {
+ return TUPKEY_abort(signal, 32);
+ }//if
+ break;
+
+ default:
+ return TUPKEY_abort(signal, 33);
+ }//switch
+ } else {
+ return TUPKEY_abort(signal, 34);
+ }//if
+ }//while
+ return TUPKEY_abort(signal, 35);
+}//Dbtup::interpreterNextLab()
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp
new file mode 100644
index 00000000000..cdd54ba2337
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp
@@ -0,0 +1,384 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(6000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(6000 + __LINE__); }
+
+//
+// Fixed Allocator
+// This module is used to allocate and free fixed size tuples from the
+// set of pages attached to a fragment. The fixed size is preset per
+// fragment and their can only be one such value per fragment in the
+// current implementation.
+//
+// Public methods
+// bool allocTh(Fragrecord* const regFragPtr, # In
+// Tablerec* const regTabPtr, # In
+// Uint32 pageType, # In
+// Signal* signal, # In
+// Uint32& pageOffset, # Out
+// PagePtr& pagePtr) # In/Out
+// This method allocates a fixed size and the pagePtr is a reference
+// to the page and pageOffset is the offset in the page of the tuple.
+//
+// freeTh()
+// This method is used to free a tuple header in normal transaction
+// handling.
+//
+// freeThSr()
+// This method is used to free a tuple as part of executing the undo
+// log records.
+//
+// getThAtPageSr()
+// This method is used to allocate a tuple on a set page as part of
+// undo log execution.
+//
+//
+// Private methods
+// getThAtPage()
+// This method gets a tuple from a page with free tuples.
+//
+// convertThPage()
+// Convert an empty page into a page of free tuples in a linked list.
+//
+// getEmptyPageThCopy()
+// A page recently taken from set of empty pages on fragment is made
+// part of the copy pages.
+//
+// getEmptyPageTh()
+// A page recently taken from the set of empty pages on the fragment is
+// is made part of the set of free pages with fixed size tuples in the
+// fragment.
+//
+bool Dbtup::allocTh(Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Uint32 pageType,
+ Signal* signal,
+ Uint32& pageOffset,
+ PagePtr& pagePtr)
+{
+ if (pageType == SAME_PAGE) {
+ ljam();
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ if (pagePtr.p->pageWord[ZPAGE_STATE_POS] == ZTH_MM_FREE) {
+ ljam();
+ getThAtPage(regFragPtr, pagePtr.p, signal, pageOffset);
+ return true;
+ }//if
+ pageType = NORMAL_PAGE;
+ }//if
+ if (pageType == COPY_PAGE) {
+/* ---------------------------------------------------------------- */
+// Allocate a tuple header for the copy of the tuple header
+/* ---------------------------------------------------------------- */
+ if (regFragPtr->thFreeCopyFirst == RNIL) {
+/* ---------------------------------------------------------------- */
+// No page in list with free tuple header exists
+/* ---------------------------------------------------------------- */
+ if (regFragPtr->noCopyPagesAlloc < ZMAX_NO_COPY_PAGES) {
+ ljam();
+/* ---------------------------------------------------------------- */
+// We have not yet allocated the maximum number of copy pages for
+// this fragment.
+/* ---------------------------------------------------------------- */
+ pagePtr.i = getEmptyPage(regFragPtr);
+ if (pagePtr.i != RNIL) {
+ ljam();
+/* ---------------------------------------------------------------- */
+// We have empty pages already allocated to this fragment. Allocate
+// one of those as copy page.
+/* ---------------------------------------------------------------- */
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ getEmptyPageThCopy(regFragPtr, signal, pagePtr.p);
+/* ---------------------------------------------------------------- */
+// Convert page into a tuple header page.
+/* ---------------------------------------------------------------- */
+ convertThPage(regTabPtr->tupheadsize, pagePtr.p);
+ getThAtPage(regFragPtr, pagePtr.p, signal, pageOffset);
+ return true;
+ }//if
+ }//if
+ } else {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* NORMAL PATH WHEN COPY PAGE REQUESTED, GET PAGE POINTER */
+/* AND THEN GOTO COMMON HANDLING OF GET TUPLE HEADER AT PAGE. */
+/* ---------------------------------------------------------------- */
+ pagePtr.i = getRealpid(regFragPtr, regFragPtr->thFreeCopyFirst);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ getThAtPage(regFragPtr, pagePtr.p, signal, pageOffset);
+ return true;
+ }//if
+ }//if
+/* ---------------------------------------------------------------- */
+/* EITHER NORMAL PAGE REQUESTED OR ALLOCATION FROM COPY PAGE */
+/* FAILED. TRY ALLOCATING FROM NORMAL PAGE. */
+/* ---------------------------------------------------------------- */
+ Uint32 fragPageId = regFragPtr->thFreeFirst;
+ if (fragPageId == RNIL) {
+/* ---------------------------------------------------------------- */
+// No prepared tuple header page with free entries exists.
+/* ---------------------------------------------------------------- */
+ pagePtr.i = getEmptyPage(regFragPtr);
+ if (pagePtr.i != RNIL) {
+ ljam();
+/* ---------------------------------------------------------------- */
+// We found empty pages on the fragment. Allocate an empty page and
+// convert it into a tuple header page and put it in thFreeFirst-list.
+/* ---------------------------------------------------------------- */
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ getEmptyPageTh(regFragPtr, signal, pagePtr.p);
+ convertThPage(regTabPtr->tupheadsize, pagePtr.p);
+ getThAtPage(regFragPtr, pagePtr.p, signal, pageOffset);
+ return true;
+ } else {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THERE ARE NO EMPTY PAGES. MEMORY CAN NOT BE ALLOCATED. */
+/* ---------------------------------------------------------------- */
+ terrorCode = ZMEM_NOMEM_ERROR;
+ return false;
+ }//if
+ } else {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THIS SHOULD BE THE COMMON PATH THROUGH THE CODE, FREE */
+/* COPY PAGE EXISTED. */
+/* ---------------------------------------------------------------- */
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ getThAtPage(regFragPtr, pagePtr.p, signal, pageOffset);
+ return true;
+ }//if
+ ndbrequire(false); // Dead code
+ return false;
+}//Dbtup::allocTh()
+
+void Dbtup::convertThPage(Uint32 Tupheadsize,
+ Page* const regPagePtr)
+{
+ Uint32 ctpConstant = Tupheadsize << 16;
+ Uint32 nextTuple = ZPAGE_HEADER_SIZE + Tupheadsize;
+ Uint32 endOfList;
+ /*
+ ASSUMES AT LEAST ONE TUPLE HEADER FITS AND THEREFORE NO HANDLING
+ OF ZERO AS EXTREME CASE
+ */
+ do {
+ ljam();
+ endOfList = nextTuple - Tupheadsize;
+ regPagePtr->pageWord[endOfList] = ctpConstant + nextTuple;
+ nextTuple += Tupheadsize;
+ } while (nextTuple <= ZWORDS_ON_PAGE);
+ regPagePtr->pageWord[endOfList] = ctpConstant;
+ Uint32 startOfList = ZPAGE_HEADER_SIZE;
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = (startOfList << 16) + endOfList;
+}//Dbtup::convertThPage()
+
+void Dbtup::getEmptyPageTh(Fragrecord* const regFragPtr,
+ Signal* signal,
+ Page* const regPagePtr)
+{
+ if (isUndoLoggingNeeded(regFragPtr, regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS])) {
+ cprAddUndoLogPageHeader(signal,
+ regPagePtr,
+ regFragPtr);
+ }//if
+ regPagePtr->pageWord[ZPAGE_NEXT_POS] = regFragPtr->thFreeFirst;
+ regPagePtr->pageWord[ZPAGE_STATE_POS] = ZTH_MM_FREE;
+ regFragPtr->thFreeFirst = regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+
+ndbrequire(regFragPtr->thFreeFirst != (RNIL -1));
+}//Dbtup::getEmptyPageTh()
+
+void Dbtup::getEmptyPageThCopy(Fragrecord* const regFragPtr,
+ Signal* signal,
+ Page* const regPagePtr)
+{
+ if (isUndoLoggingNeeded(regFragPtr, regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS])) {
+ cprAddUndoLogPageHeader(signal,
+ regPagePtr,
+ regFragPtr);
+ }//if
+ regPagePtr->pageWord[ZPAGE_NEXT_POS] = regFragPtr->thFreeCopyFirst;
+ regPagePtr->pageWord[ZPAGE_STATE_POS] = ZTH_MM_FREE_COPY;
+ regFragPtr->thFreeCopyFirst = regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ regFragPtr->noCopyPagesAlloc++;
+}//Dbtup::getEmptyPageThCopy()
+
+void Dbtup::getThAtPage(Fragrecord* const regFragPtr,
+ Page* const regPagePtr,
+ Signal* signal,
+ Uint32& pageOffset)
+{
+ Uint32 freeListHeader = regPagePtr->pageWord[ZFREELIST_HEADER_POS];
+ Uint32 startTuple = freeListHeader >> 16;
+ Uint32 endTuple = freeListHeader & 0xffff;
+ pageOffset = startTuple; /* START IS THE ONE ALLOCATED */
+ if (startTuple > 0) {
+ if (startTuple != endTuple) {
+/* ---------------------------------------------------------------- */
+/* NOT THE LAST, SIMPLY RESHUFFLE POINTERS. */
+/* ---------------------------------------------------------------- */
+ ndbrequire(startTuple < ZWORDS_ON_PAGE);
+ startTuple = regPagePtr->pageWord[startTuple] & 0xffff;
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = endTuple +
+ (startTuple << 16);
+ return;
+ } else {
+/* ---------------------------------------------------------------- */
+/* THIS WAS THE LAST TUPLE HEADER IN THIS PAGE. REMOVE IT FROM*/
+/* THE TUPLE HEADER FREE LIST OR TH COPY FREE LIST. ALSO SET */
+/* A PROPER PAGE STATE. */
+/* */
+/* WE ALSO HAVE TO INSERT AN UNDO LOG ENTRY TO ENSURE PAGE */
+/* ARE MAINTAINED EVEN AFTER A SYSTEM CRASH. */
+/* ---------------------------------------------------------------- */
+ if (isUndoLoggingNeeded(regFragPtr,
+ regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS])) {
+ cprAddUndoLogPageHeader(signal,
+ regPagePtr,
+ regFragPtr);
+ }//if
+ if (regPagePtr->pageWord[ZPAGE_STATE_POS] == ZTH_MM_FREE) {
+ ljam();
+ regFragPtr->thFreeFirst = regPagePtr->pageWord[ZPAGE_NEXT_POS];
+ regPagePtr->pageWord[ZPAGE_STATE_POS] = ZTH_MM_FULL;
+ } else if (regPagePtr->pageWord[ZPAGE_STATE_POS] == ZTH_MM_FREE_COPY) {
+ ljam();
+ regFragPtr->thFreeCopyFirst = regPagePtr->pageWord[ZPAGE_NEXT_POS];
+ regPagePtr->pageWord[ZPAGE_STATE_POS] = ZTH_MM_FULL_COPY;
+ } else {
+ ndbrequire(false);
+ }//if
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = 0;
+ regPagePtr->pageWord[ZPAGE_NEXT_POS] = RNIL;
+ }//if
+ } else {
+ ndbrequire(false);
+ }//if
+ return;
+}//Dbtup::getThAtPage()
+
+void Dbtup::getThAtPageSr(Page* const regPagePtr,
+ Uint32& pageOffset)
+{
+ Uint32 freeListHeader = regPagePtr->pageWord[ZFREELIST_HEADER_POS];
+ Uint32 startTuple = freeListHeader >> 16;
+ Uint32 endTuple = freeListHeader & 0xffff;
+ ndbrequire(startTuple > 0);
+ pageOffset = startTuple; /* START IS THE ONE ALLOCATED */
+ if (startTuple == endTuple) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THIS WAS THE LAST TUPLE HEADER IN THIS PAGE. SINCE WE ARE */
+/* UNDOING PAGE UPDATES WE SHALL NOT DO ANYTHING ABOUT THE */
+/* PAGE HEADER. THIS IS DONE BY SEPARATE LOG RECORDS. */
+/* ---------------------------------------------------------------- */
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = 0;
+ } else {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* NOT THE LAST, SIMPLY RESHUFFLE POINTERS. */
+/* ---------------------------------------------------------------- */
+ ndbrequire(startTuple < ZWORDS_ON_PAGE);
+ startTuple = regPagePtr->pageWord[startTuple] & 0xffff; /* GET NEXT POINTER */
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = endTuple + (startTuple << 16);
+ }//if
+}//Dbtup::getThAtPageSr()
+
+void Dbtup::freeTh(Fragrecord* const regFragPtr,
+ Tablerec* const regTabPtr,
+ Signal* signal,
+ Page* const regPagePtr,
+ Uint32 freePageOffset)
+{
+ Uint32 startOfList = regPagePtr->pageWord[ZFREELIST_HEADER_POS] >> 16;
+ Uint32 endOfList = regPagePtr->pageWord[ZFREELIST_HEADER_POS] & 0xffff;
+/* LINK THE NOW FREE TUPLE SPACE INTO BEGINNING OF FREE LIST OF OF THE PAGE */
+/* SET THE SIZE OF THE NEW FREE SPACE AND LINK TO THE OLD START OF FREELIST */
+ ndbrequire(freePageOffset < ZWORDS_ON_PAGE);
+ regPagePtr->pageWord[freePageOffset] = (regTabPtr->tupheadsize << 16) +
+ startOfList;
+ if (endOfList == 0) {
+ ljam();
+ ndbrequire(startOfList == 0);
+/* ---------------------------------------------------------------- */
+/* THE PAGE WAS PREVIOUSLY FULL, NO EMPTY SPACE AT ALL. */
+/* THIS ENTRY WILL THEN BE BOTH THE START AND THE END OF THE */
+/* LIST. IT WILL ALSO BE PUT ON THE PROPER FREE LIST. */
+/* */
+/* UPDATE OF NEXT POINTER AND PAGE STATE MUST BE LOGGED TO */
+/* THE UNDO LOG TO ENSURE THAT FREE LISTS ARE OK AFTER A */
+/* SYSTEM RESTART. */
+/* ---------------------------------------------------------------- */
+ if (isUndoLoggingNeeded(regFragPtr, regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS])) {
+ cprAddUndoLogPageHeader(signal,
+ regPagePtr,
+ regFragPtr);
+ }//if
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = (freePageOffset << 16) + freePageOffset;
+ if (regPagePtr->pageWord[ZPAGE_STATE_POS] == ZTH_MM_FULL) {
+ ljam();
+ regPagePtr->pageWord[ZPAGE_NEXT_POS] = regFragPtr->thFreeFirst;
+ regFragPtr->thFreeFirst = regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ regPagePtr->pageWord[ZPAGE_STATE_POS] = ZTH_MM_FREE;
+ } else {
+ ndbrequire(regPagePtr->pageWord[ZPAGE_STATE_POS] == ZTH_MM_FULL_COPY);
+ ljam();
+ regPagePtr->pageWord[ZPAGE_NEXT_POS] = regFragPtr->thFreeCopyFirst;
+ regFragPtr->thFreeCopyFirst = regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ regPagePtr->pageWord[ZPAGE_STATE_POS] = ZTH_MM_FREE_COPY;
+ }//if
+ } else {
+ ljam();
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = (freePageOffset << 16) + endOfList;
+ }//if
+}//Dbtup::freeTh()
+
+void Dbtup::freeThSr(Tablerec* const regTabPtr,
+ Page* const regPagePtr,
+ Uint32 freePageOffset)
+{
+/* ------------------------------------------------------------------------ */
+/* LINK THE NOW FREE TUPLE SPACE INTO BEGINNING OF FREE LIST OF OF THE PAGE */
+/* SET THE SIZE OF THE NEW FREE SPACE AND LINK TO THE OLD START OF FREELIST */
+/* ------------------------------------------------------------------------ */
+ Uint32 startOfList = regPagePtr->pageWord[ZFREELIST_HEADER_POS] >> 16;
+ Uint32 endOfList = regPagePtr->pageWord[ZFREELIST_HEADER_POS] & 0xffff;
+ ndbrequire(freePageOffset < ZWORDS_ON_PAGE);
+ regPagePtr->pageWord[freePageOffset] = (regTabPtr->tupheadsize << 16) + startOfList;
+ if (endOfList == 0) {
+ ljam();
+ ndbrequire(startOfList == 0);
+/* ---------------------------------------------------------------- */
+/* THE PAGE WAS PREVIOUSLY FULL, NO EMPTY SPACE AT ALL. */
+/* THIS ENTRY WILL THEN BE BOTH THE START AND THE END OF THE */
+/* LIST. IT WILL ALSO BE PUT ON THE PROPER FREE LIST. */
+/* ---------------------------------------------------------------- */
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = (freePageOffset << 16) + freePageOffset;
+ } else {
+ ljam();
+ regPagePtr->pageWord[ZFREELIST_HEADER_POS] = (freePageOffset << 16) + endOfList;
+ }//if
+}//Dbtup::freeThSr()
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp
new file mode 100644
index 00000000000..0d7430e662d
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp
@@ -0,0 +1,1343 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <AttributeHeader.hpp>
+#include <Interpreter.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+#include <signaldata/FsRemoveReq.hpp>
+#include <signaldata/TupCommit.hpp>
+#include <signaldata/TupKey.hpp>
+
+#include <signaldata/DropTab.hpp>
+
+#define DEBUG(x) { ndbout << "TUP::" << x << endl; }
+
+#define ljam() { jamLine(24000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(24000 + __LINE__); }
+
+void Dbtup::initData()
+{
+ cnoOfAttrbufrec = ZNO_OF_ATTRBUFREC;
+ cnoOfLcpRec = ZNO_OF_LCP_REC;
+ cnoOfConcurrentOpenOp = ZNO_OF_CONCURRENT_OPEN_OP;
+ cnoOfConcurrentWriteOp = ZNO_OF_CONCURRENT_WRITE_OP;
+ cnoOfFragoprec = 2 * MAX_FRAG_PER_NODE;
+ cnoOfPageRangeRec = ZNO_OF_PAGE_RANGE_REC;
+ cnoOfParallellUndoFiles = ZNO_OF_PARALLELL_UNDO_FILES;
+ cnoOfRestartInfoRec = ZNO_OF_RESTART_INFO_REC;
+ c_maxTriggersPerTable = ZDEFAULT_MAX_NO_TRIGGERS_PER_TABLE;
+ c_noOfBuildIndexRec = 32;
+
+ attrbufrec = 0;
+ checkpointInfo = 0;
+ diskBufferSegmentInfo = 0;
+ fragoperrec = 0;
+ fragrecord = 0;
+ hostBuffer = 0;
+ localLogInfo = 0;
+ operationrec = 0;
+ page = 0;
+ pageRange = 0;
+ pendingFileOpenInfo = 0;
+ restartInfoRecord = 0;
+ tablerec = 0;
+ tableDescriptor = 0;
+ undoPage = 0;
+ totNoOfPagesAllocated = 0;
+ cnoOfAllocatedPages = 0;
+
+ // Records with constant sizes
+}//Dbtup::initData()
+
+Dbtup::Dbtup(const class Configuration & conf)
+ : SimulatedBlock(DBTUP, conf),
+ c_storedProcPool(),
+ c_buildIndexList(c_buildIndexPool)
+{
+ Uint32 log_page_size= 0;
+ BLOCK_CONSTRUCTOR(Dbtup);
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_UNDO_DATA_BUFFER,
+ &log_page_size);
+
+ /**
+ * Always set page size in half MBytes
+ */
+ cnoOfUndoPage= (log_page_size / sizeof(UndoPage));
+ Uint32 mega_byte_part= cnoOfUndoPage & 15;
+ if (mega_byte_part != 0) {
+ jam();
+ cnoOfUndoPage+= (16 - mega_byte_part);
+ }
+
+ addRecSignal(GSN_DEBUG_SIG, &Dbtup::execDEBUG_SIG);
+ addRecSignal(GSN_CONTINUEB, &Dbtup::execCONTINUEB);
+
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbtup::execDUMP_STATE_ORD);
+ addRecSignal(GSN_SEND_PACKED, &Dbtup::execSEND_PACKED);
+ addRecSignal(GSN_ATTRINFO, &Dbtup::execATTRINFO);
+ addRecSignal(GSN_STTOR, &Dbtup::execSTTOR);
+ addRecSignal(GSN_TUP_LCPREQ, &Dbtup::execTUP_LCPREQ);
+ addRecSignal(GSN_END_LCPREQ, &Dbtup::execEND_LCPREQ);
+ addRecSignal(GSN_START_RECREQ, &Dbtup::execSTART_RECREQ);
+ addRecSignal(GSN_MEMCHECKREQ, &Dbtup::execMEMCHECKREQ);
+ addRecSignal(GSN_TUPKEYREQ, &Dbtup::execTUPKEYREQ);
+ addRecSignal(GSN_TUPSEIZEREQ, &Dbtup::execTUPSEIZEREQ);
+ addRecSignal(GSN_TUPRELEASEREQ, &Dbtup::execTUPRELEASEREQ);
+ addRecSignal(GSN_STORED_PROCREQ, &Dbtup::execSTORED_PROCREQ);
+ addRecSignal(GSN_TUPFRAGREQ, &Dbtup::execTUPFRAGREQ);
+ addRecSignal(GSN_TUP_ADD_ATTRREQ, &Dbtup::execTUP_ADD_ATTRREQ);
+ addRecSignal(GSN_TUP_COMMITREQ, &Dbtup::execTUP_COMMITREQ);
+ addRecSignal(GSN_TUP_ABORTREQ, &Dbtup::execTUP_ABORTREQ);
+ addRecSignal(GSN_TUP_SRREQ, &Dbtup::execTUP_SRREQ);
+ addRecSignal(GSN_TUP_PREPLCPREQ, &Dbtup::execTUP_PREPLCPREQ);
+ addRecSignal(GSN_FSOPENCONF, &Dbtup::execFSOPENCONF);
+ addRecSignal(GSN_FSOPENREF, &Dbtup::execFSOPENREF);
+ addRecSignal(GSN_FSCLOSECONF, &Dbtup::execFSCLOSECONF);
+ addRecSignal(GSN_FSCLOSEREF, &Dbtup::execFSCLOSEREF);
+ addRecSignal(GSN_FSWRITECONF, &Dbtup::execFSWRITECONF);
+ addRecSignal(GSN_FSWRITEREF, &Dbtup::execFSWRITEREF);
+ addRecSignal(GSN_FSREADCONF, &Dbtup::execFSREADCONF);
+ addRecSignal(GSN_FSREADREF, &Dbtup::execFSREADREF);
+ addRecSignal(GSN_NDB_STTOR, &Dbtup::execNDB_STTOR);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbtup::execREAD_CONFIG_REQ, true);
+ addRecSignal(GSN_SET_VAR_REQ, &Dbtup::execSET_VAR_REQ);
+
+ // Trigger Signals
+ addRecSignal(GSN_CREATE_TRIG_REQ, &Dbtup::execCREATE_TRIG_REQ);
+ addRecSignal(GSN_DROP_TRIG_REQ, &Dbtup::execDROP_TRIG_REQ);
+
+ addRecSignal(GSN_DROP_TAB_REQ, &Dbtup::execDROP_TAB_REQ);
+ addRecSignal(GSN_FSREMOVEREF, &Dbtup::execFSREMOVEREF);
+ addRecSignal(GSN_FSREMOVECONF, &Dbtup::execFSREMOVECONF);
+
+ addRecSignal(GSN_TUP_ALLOCREQ, &Dbtup::execTUP_ALLOCREQ);
+ addRecSignal(GSN_TUP_DEALLOCREQ, &Dbtup::execTUP_DEALLOCREQ);
+ addRecSignal(GSN_TUP_WRITELOG_REQ, &Dbtup::execTUP_WRITELOG_REQ);
+
+ // Ordered index related
+ addRecSignal(GSN_BUILDINDXREQ, &Dbtup::execBUILDINDXREQ);
+
+ initData();
+}//Dbtup::Dbtup()
+
+Dbtup::~Dbtup()
+{
+ // Records with dynamic sizes
+ deallocRecord((void **)&attrbufrec,"Attrbufrec",
+ sizeof(Attrbufrec),
+ cnoOfAttrbufrec);
+
+ deallocRecord((void **)&checkpointInfo,"CheckpointInfo",
+ sizeof(CheckpointInfo),
+ cnoOfLcpRec);
+
+ deallocRecord((void **)&diskBufferSegmentInfo,
+ "DiskBufferSegmentInfo",
+ sizeof(DiskBufferSegmentInfo),
+ cnoOfConcurrentWriteOp);
+
+ deallocRecord((void **)&fragoperrec,"Fragoperrec",
+ sizeof(Fragoperrec),
+ cnoOfFragoprec);
+
+ deallocRecord((void **)&fragrecord,"Fragrecord",
+ sizeof(Fragrecord),
+ cnoOfFragrec);
+
+ deallocRecord((void **)&hostBuffer,"HostBuffer",
+ sizeof(HostBuffer),
+ MAX_NODES);
+
+ deallocRecord((void **)&localLogInfo,"LocalLogInfo",
+ sizeof(LocalLogInfo),
+ cnoOfParallellUndoFiles);
+
+ deallocRecord((void **)&operationrec,"Operationrec",
+ sizeof(Operationrec),
+ cnoOfOprec);
+
+ deallocRecord((void **)&page,"Page",
+ sizeof(Page),
+ cnoOfPage);
+
+ deallocRecord((void **)&pageRange,"PageRange",
+ sizeof(PageRange),
+ cnoOfPageRangeRec);
+
+ deallocRecord((void **)&pendingFileOpenInfo,
+ "PendingFileOpenInfo",
+ sizeof(PendingFileOpenInfo),
+ cnoOfConcurrentOpenOp);
+
+ deallocRecord((void **)&restartInfoRecord,
+ "RestartInfoRecord",
+ sizeof(RestartInfoRecord),
+ cnoOfRestartInfoRec);
+
+ deallocRecord((void **)&tablerec,"Tablerec",
+ sizeof(Tablerec),
+ cnoOfTablerec);
+
+ deallocRecord((void **)&tableDescriptor, "TableDescriptor",
+ sizeof(TableDescriptor),
+ cnoOfTabDescrRec);
+
+ deallocRecord((void **)&undoPage,"UndoPage",
+ sizeof(UndoPage),
+ cnoOfUndoPage);
+
+}//Dbtup::~Dbtup()
+
+BLOCK_FUNCTIONS(Dbtup)
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ----- GENERAL SIGNAL MULTIPLEXER (FS + CONTINUEB) -------------- */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+void Dbtup::execFSCLOSECONF(Signal* signal)
+{
+ PendingFileOpenInfoPtr pfoPtr;
+ ljamEntry();
+ pfoPtr.i = signal->theData[0];
+ ptrCheckGuard(pfoPtr, cnoOfConcurrentOpenOp, pendingFileOpenInfo);
+ switch (pfoPtr.p->pfoOpenType) {
+ case LCP_DATA_FILE_CLOSE:
+ {
+ CheckpointInfoPtr ciPtr;
+ ljam();
+ ciPtr.i = pfoPtr.p->pfoCheckpointInfoP;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ ciPtr.p->lcpDataFileHandle = RNIL;
+ lcpClosedDataFileLab(signal, ciPtr);
+ break;
+ }
+ case LCP_UNDO_FILE_CLOSE:
+ {
+ LocalLogInfoPtr lliPtr;
+ ljam();
+ lliPtr.i = pfoPtr.p->pfoCheckpointInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ lliPtr.p->lliUndoFileHandle = RNIL;
+ lcpEndconfLab(signal);
+ break;
+ }
+ case LCP_UNDO_FILE_READ:
+ ljam();
+ endExecUndoLogLab(signal, pfoPtr.p->pfoCheckpointInfoP);
+ break;
+ case LCP_DATA_FILE_READ:
+ ljam();
+ rfrClosedDataFileLab(signal, pfoPtr.p->pfoCheckpointInfoP);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ releasePendingFileOpenInfoRecord(pfoPtr);
+}//Dbtup::execFSCLOSECONF()
+
+void Dbtup::execFSCLOSEREF(Signal* signal)
+{
+ ljamEntry();
+ ndbrequire(false);
+}//Dbtup::execFSCLOSEREF()
+
+void Dbtup::execFSOPENCONF(Signal* signal)
+{
+ PendingFileOpenInfoPtr pfoPtr;
+
+ ljamEntry();
+ pfoPtr.i = signal->theData[0];
+ Uint32 fileHandle = signal->theData[1];
+ ptrCheckGuard(pfoPtr, cnoOfConcurrentOpenOp, pendingFileOpenInfo);
+ switch (pfoPtr.p->pfoOpenType) {
+ case LCP_DATA_FILE_READ:
+ {
+ RestartInfoRecordPtr riPtr;
+ ljam();
+ riPtr.i = pfoPtr.p->pfoRestartInfoP;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+ riPtr.p->sriDataFileHandle = fileHandle;
+ rfrReadRestartInfoLab(signal, riPtr);
+ break;
+ }
+ case LCP_UNDO_FILE_READ:
+ {
+ RestartInfoRecordPtr riPtr;
+ LocalLogInfoPtr lliPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+
+ ljam();
+ riPtr.i = pfoPtr.p->pfoRestartInfoP;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+ lliPtr.i = riPtr.p->sriLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ lliPtr.p->lliUndoFileHandle = fileHandle;
+ dbsiPtr.i = riPtr.p->sriDataBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ rfrLoadDataPagesLab(signal, riPtr, dbsiPtr);
+ break;
+ }
+ case LCP_DATA_FILE_WRITE_WITH_UNDO:
+ {
+ CheckpointInfoPtr ciPtr;
+ LocalLogInfoPtr lliPtr;
+
+ ljam();
+ ciPtr.i = pfoPtr.p->pfoCheckpointInfoP;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ ciPtr.p->lcpDataFileHandle = fileHandle;
+ if (lliPtr.p->lliUndoFileHandle != RNIL) {
+ ljam();
+ signal->theData[0] = ciPtr.p->lcpUserptr;
+ signal->theData[1] = ciPtr.i;
+ sendSignal(ciPtr.p->lcpBlockref, GSN_TUP_PREPLCPCONF, signal, 2, JBB);
+ }//if
+ break;
+ }
+ case LCP_DATA_FILE_WRITE:
+ {
+ CheckpointInfoPtr ciPtr;
+
+ ljam();
+ ciPtr.i = pfoPtr.p->pfoCheckpointInfoP;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ ciPtr.p->lcpDataFileHandle = fileHandle;
+ signal->theData[0] = ciPtr.p->lcpUserptr;
+ signal->theData[1] = ciPtr.i;
+ sendSignal(ciPtr.p->lcpBlockref, GSN_TUP_PREPLCPCONF, signal, 2, JBB);
+ break;
+ }
+ case LCP_UNDO_FILE_WRITE:
+ {
+ CheckpointInfoPtr ciPtr;
+ LocalLogInfoPtr lliPtr;
+
+ ljam();
+ ciPtr.i = pfoPtr.p->pfoCheckpointInfoP;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ lliPtr.p->lliUndoFileHandle = fileHandle;
+ if (ciPtr.p->lcpDataFileHandle != RNIL) {
+ ljam();
+ signal->theData[0] = ciPtr.p->lcpUserptr;
+ signal->theData[1] = ciPtr.i;
+ sendSignal(ciPtr.p->lcpBlockref, GSN_TUP_PREPLCPCONF, signal, 2, JBB);
+ }//if
+ break;
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ releasePendingFileOpenInfoRecord(pfoPtr);
+}//Dbtup::execFSOPENCONF()
+
+void Dbtup::execFSOPENREF(Signal* signal)
+{
+ ljamEntry();
+ ndbrequire(false);
+}//Dbtup::execFSOPENREF()
+
+void Dbtup::execFSREADCONF(Signal* signal)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ ljamEntry();
+ dbsiPtr.i = signal->theData[0];
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ switch (dbsiPtr.p->pdxOperation) {
+ case CHECKPOINT_DATA_READ:
+ {
+ RestartInfoRecordPtr riPtr;
+ ljam();
+ riPtr.i = dbsiPtr.p->pdxRestartInfoP;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+/************************************************************/
+/* VERIFY THAT THE PAGES ARE CORRECT, HAVE A CORRECT */
+/* STATE AND A CORRECT PAGE ID. */
+/************************************************************/
+ ndbrequire(dbsiPtr.p->pdxNumDataPages <= 16);
+ for (Uint32 i = 0; i < dbsiPtr.p->pdxNumDataPages; i++) {
+ PagePtr pagePtr;
+ ljam();
+ pagePtr.i = dbsiPtr.p->pdxDataPage[i];
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ ndbrequire(pagePtr.p->pageWord[ZPAGE_STATE_POS] != 0);
+ ndbrequire(pagePtr.p->pageWord[ZPAGE_STATE_POS] <= ZAC_MM_FREE_COPY);
+ ndbrequire(pagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS] == ((dbsiPtr.p->pdxFilePage - 1) + i));
+ }//for
+ rfrLoadDataPagesLab(signal, riPtr, dbsiPtr);
+ break;
+ }
+ case CHECKPOINT_DATA_READ_PAGE_ZERO:
+ {
+ ljam();
+ rfrInitRestartInfoLab(signal, dbsiPtr);
+ break;
+ }
+ case CHECKPOINT_UNDO_READ:
+ {
+ LocalLogInfoPtr lliPtr;
+ ljam();
+ lliPtr.i = dbsiPtr.p->pdxCheckpointInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ xlcGetNextRecordLab(signal, dbsiPtr, lliPtr);
+ break;
+ }
+ case CHECKPOINT_UNDO_READ_FIRST:
+ ljam();
+ rfrReadSecondUndoLogLab(signal, dbsiPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbtup::execFSREADCONF()
+
+void Dbtup::execFSREADREF(Signal* signal)
+{
+ ljamEntry();
+ ndbrequire(false);
+}//Dbtup::execFSREADREF()
+
+void Dbtup::execFSWRITECONF(Signal* signal)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+
+ ljamEntry();
+ dbsiPtr.i = signal->theData[0];
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ switch (dbsiPtr.p->pdxOperation) {
+ case CHECKPOINT_DATA_WRITE:
+ ljam();
+ lcpSaveDataPageLab(signal, dbsiPtr.p->pdxCheckpointInfoP);
+ break;
+ case CHECKPOINT_DATA_WRITE_LAST:
+ {
+ CheckpointInfoPtr ciPtr;
+ ljam();
+ ciPtr.i = dbsiPtr.p->pdxCheckpointInfoP;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ lcpFlushLogLab(signal, ciPtr);
+ break;
+ }
+ case CHECKPOINT_DATA_WRITE_FLUSH:
+ {
+ ljam();
+ Uint32 ciIndex = dbsiPtr.p->pdxCheckpointInfoP;
+ freeDiskBufferSegmentRecord(signal, dbsiPtr);
+ lcpCompletedLab(signal, ciIndex);
+ break;
+ }
+ case CHECKPOINT_UNDO_WRITE_FLUSH:
+ {
+ ljam();
+ Uint32 ciIndex = dbsiPtr.p->pdxCheckpointInfoP;
+ freeDiskBufferSegmentRecord(signal, dbsiPtr);
+ lcpFlushRestartInfoLab(signal, ciIndex);
+ break;
+ }
+ case CHECKPOINT_UNDO_WRITE:
+ ljam();
+ freeDiskBufferSegmentRecord(signal, dbsiPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ return;
+}//Dbtup::execFSWRITECONF()
+
+void Dbtup::execFSWRITEREF(Signal* signal)
+{
+ ljamEntry();
+ ndbrequire(false);
+}//Dbtup::execFSWRITEREF()
+
+void Dbtup::execCONTINUEB(Signal* signal)
+{
+ ljamEntry();
+ Uint32 actionType = signal->theData[0];
+ Uint32 dataPtr = signal->theData[1];
+ switch (actionType) {
+ case ZSTART_EXEC_UNDO_LOG:
+ ljam();
+ startExecUndoLogLab(signal, dataPtr);
+ break;
+ case ZCONT_SAVE_DP:
+ ljam();
+ lcpSaveDataPageLab(signal, dataPtr);
+ break;
+ case ZCONT_START_SAVE_CL:
+ {
+ CheckpointInfoPtr ciPtr;
+
+ ljam();
+ ciPtr.i = dataPtr;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ lcpSaveCopyListLab(signal, ciPtr);
+ break;
+ }
+ case ZCONT_EXECUTE_LC:
+ {
+ LocalLogInfoPtr lliPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+
+ ljam();
+ lliPtr.i = dataPtr;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ dbsiPtr.i = lliPtr.p->lliUndoBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ xlcGetNextRecordLab(signal, dbsiPtr, lliPtr);
+ break;
+ }
+ case ZCONT_LOAD_DP:
+ {
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ RestartInfoRecordPtr riPtr;
+
+ ljam();
+ riPtr.i = dataPtr;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+ dbsiPtr.i = riPtr.p->sriDataBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ rfrLoadDataPagesLab(signal, riPtr, dbsiPtr);
+ break;
+ }
+ case ZLOAD_BAL_LCP_TIMER:
+ ljam();
+ clblPageCounter = clblPagesPerTick;
+ signal->theData[0] = ZLOAD_BAL_LCP_TIMER;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 400, 1);
+ break;
+ case ZINITIALISE_RECORDS:
+ ljam();
+ initialiseRecordsLab(signal, dataPtr,
+ signal->theData[2], signal->theData[3]);
+ break;
+ case ZREL_FRAG:
+ ljam();
+ releaseFragment(signal, dataPtr);
+ break;
+ case ZREPORT_MEMORY_USAGE:{
+ ljam();
+ static int c_currentMemUsed = 0;
+ int now = (cnoOfAllocatedPages * 100)/cnoOfPage;
+ const int thresholds[] = { 100, 90, 80, 0 };
+
+ Uint32 i = 0;
+ const Uint32 sz = sizeof(thresholds)/sizeof(thresholds[0]);
+ for(i = 0; i<sz; i++){
+ if(now >= thresholds[i]){
+ now = thresholds[i];
+ break;
+ }
+ }
+
+ if(now != c_currentMemUsed){
+ reportMemoryUsage(signal, now > c_currentMemUsed ? 1 : -1);
+ c_currentMemUsed = now;
+ }
+ signal->theData[0] = ZREPORT_MEMORY_USAGE;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 2000, 1);
+ return;
+ }
+ case ZBUILD_INDEX:
+ ljam();
+ buildIndex(signal, dataPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbtup::execTUP_CONTINUEB()
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ------------------- SYSTEM RESTART MODULE ---------------------- */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+void Dbtup::execSTTOR(Signal* signal)
+{
+ ljamEntry();
+ Uint32 startPhase = signal->theData[1];
+ Uint32 sigKey = signal->theData[6];
+ switch (startPhase) {
+ case ZSTARTPHASE1:
+ ljam();
+ CLEAR_ERROR_INSERT_VALUE;
+ cownref = calcTupBlockRef(0);
+ break;
+ default:
+ ljam();
+ break;
+ }//switch
+ signal->theData[0] = sigKey;
+ signal->theData[1] = 3;
+ signal->theData[2] = 2;
+ signal->theData[3] = ZSTARTPHASE1;
+ signal->theData[4] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+ return;
+}//Dbtup::execSTTOR()
+
+/************************************************************************************************/
+// SIZE_ALTREP INITIALIZE DATA STRUCTURES, FILES AND DS VARIABLES, GET READY FOR EXTERNAL
+// CONNECTIONS.
+/************************************************************************************************/
+void Dbtup::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ ljamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_FRAG, &cnoOfFragrec));
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_OP_RECS, &cnoOfOprec));
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_PAGE, &cnoOfPage));
+ Uint32 noOfTriggers= 0;
+
+ Uint32 tmp= 0;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_PAGE_RANGE, &tmp));
+ initPageRangeSize(tmp);
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_TABLE, &cnoOfTablerec));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_TABLE_DESC,
+ &cnoOfTabDescrRec));
+ Uint32 noOfStoredProc;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUP_STORED_PROC,
+ &noOfStoredProc));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_NO_TRIGGERS,
+ &noOfTriggers));
+
+ cnoOfTabDescrRec = (cnoOfTabDescrRec & 0xFFFFFFF0) + 16;
+ c_storedProcPool.setSize(noOfStoredProc);
+ c_buildIndexPool.setSize(c_noOfBuildIndexRec);
+ c_triggerPool.setSize(noOfTriggers);
+
+ initRecords();
+ czero = 0;
+ cminusOne = czero - 1;
+ clastBitMask = 1;
+ clastBitMask = clastBitMask << 31;
+ cnoOfLocalLogInfo = 0;
+ cnoFreeUndoSeg = 0;
+
+ initialiseRecordsLab(signal, 0, ref, senderData);
+
+ clblPagesPerTick = 50;
+ //ndb_mgm_get_int_parameter(p, CFG_DB_, &clblPagesPerTick);
+
+ clblPagesPerTickAfterSr = 50;
+ //ndb_mgm_get_int_parameter(p, CFG_DB_, &clblPagesPerTickAfterSr);
+
+}//Dbtup::execSIZEALT_REP()
+
+void Dbtup::initRecords()
+{
+ // Records with dynamic sizes
+ attrbufrec = (Attrbufrec*)allocRecord("Attrbufrec",
+ sizeof(Attrbufrec),
+ cnoOfAttrbufrec);
+
+ checkpointInfo = (CheckpointInfo*)allocRecord("CheckpointInfo",
+ sizeof(CheckpointInfo),
+ cnoOfLcpRec);
+
+ diskBufferSegmentInfo = (DiskBufferSegmentInfo*)
+ allocRecord("DiskBufferSegmentInfo",
+ sizeof(DiskBufferSegmentInfo),
+ cnoOfConcurrentWriteOp);
+
+ fragoperrec = (Fragoperrec*)allocRecord("Fragoperrec",
+ sizeof(Fragoperrec),
+ cnoOfFragoprec);
+
+ fragrecord = (Fragrecord*)allocRecord("Fragrecord",
+ sizeof(Fragrecord),
+ cnoOfFragrec);
+
+ hostBuffer = (HostBuffer*)allocRecord("HostBuffer",
+ sizeof(HostBuffer),
+ MAX_NODES);
+
+ localLogInfo = (LocalLogInfo*)allocRecord("LocalLogInfo",
+ sizeof(LocalLogInfo),
+ cnoOfParallellUndoFiles);
+
+ operationrec = (Operationrec*)allocRecord("Operationrec",
+ sizeof(Operationrec),
+ cnoOfOprec);
+
+ page = (Page*)allocRecord("Page",
+ sizeof(Page),
+ cnoOfPage,
+ false);
+
+ pageRange = (PageRange*)allocRecord("PageRange",
+ sizeof(PageRange),
+ cnoOfPageRangeRec);
+
+ pendingFileOpenInfo = (PendingFileOpenInfo*)
+ allocRecord("PendingFileOpenInfo",
+ sizeof(PendingFileOpenInfo),
+ cnoOfConcurrentOpenOp);
+
+ restartInfoRecord = (RestartInfoRecord*)
+ allocRecord("RestartInfoRecord",
+ sizeof(RestartInfoRecord),
+ cnoOfRestartInfoRec);
+
+
+ tablerec = (Tablerec*)allocRecord("Tablerec",
+ sizeof(Tablerec),
+ cnoOfTablerec);
+
+ for(unsigned i = 0; i<cnoOfTablerec; i++) {
+ void * p = &tablerec[i];
+ new (p) Tablerec(c_triggerPool);
+ }
+
+ tableDescriptor = (TableDescriptor*)
+ allocRecord("TableDescriptor",
+ sizeof(TableDescriptor),
+ cnoOfTabDescrRec);
+
+ undoPage = (UndoPage*)allocRecord("UndoPage",
+ sizeof(UndoPage),
+ cnoOfUndoPage);
+
+
+ // Initialize BAT for interface to file system
+ NewVARIABLE* bat = allocateBat(3);
+ bat[1].WA = &page->pageWord[0];
+ bat[1].nrr = cnoOfPage;
+ bat[1].ClusterSize = sizeof(Page);
+ bat[1].bits.q = 13; /* 8192 words/page */
+ bat[1].bits.v = 5;
+ bat[2].WA = &undoPage->undoPageWord[0];
+ bat[2].nrr = cnoOfUndoPage;
+ bat[2].ClusterSize = sizeof(UndoPage);
+ bat[2].bits.q = 13; /* 8192 words/page */
+ bat[2].bits.v = 5;
+}//Dbtup::initRecords()
+
+void Dbtup::initialiseRecordsLab(Signal* signal, Uint32 switchData,
+ Uint32 retRef, Uint32 retData)
+{
+ switch (switchData) {
+ case 0:
+ ljam();
+ initializeHostBuffer();
+ break;
+ case 1:
+ ljam();
+ initializeOperationrec();
+ break;
+ case 2:
+ ljam();
+ initializePage();
+ break;
+ case 3:
+ ljam();
+ initializeUndoPage();
+ break;
+ case 4:
+ ljam();
+ initializeTablerec();
+ break;
+ case 5:
+ ljam();
+ initializeCheckpointInfoRec();
+ break;
+ case 6:
+ ljam();
+ initializeFragrecord();
+ break;
+ case 7:
+ ljam();
+ initializeFragoperrec();
+ break;
+ case 8:
+ ljam();
+ initializePageRange();
+ break;
+ case 9:
+ ljam();
+ initializeTabDescr();
+ break;
+ case 10:
+ ljam();
+ initializeDiskBufferSegmentRecord();
+ break;
+ case 11:
+ ljam();
+ initializeLocalLogInfo();
+ break;
+ case 12:
+ ljam();
+ initializeAttrbufrec();
+ break;
+ case 13:
+ ljam();
+ initializePendingFileOpenInfoRecord();
+ break;
+ case 14:
+ ljam();
+ initializeRestartInfoRec();
+
+ {
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = retData;
+ sendSignal(retRef, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+ }
+ return;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ signal->theData[0] = ZINITIALISE_RECORDS;
+ signal->theData[1] = switchData + 1;
+ signal->theData[2] = retRef;
+ signal->theData[3] = retData;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 4, JBB);
+ return;
+}//Dbtup::initialiseRecordsLab()
+
+void Dbtup::execNDB_STTOR(Signal* signal)
+{
+ ljamEntry();
+ cndbcntrRef = signal->theData[0];
+ Uint32 ownNodeId = signal->theData[1];
+ Uint32 startPhase = signal->theData[2];
+ switch (startPhase) {
+ case ZSTARTPHASE1:
+ ljam();
+ cownNodeId = ownNodeId;
+ cownref = calcTupBlockRef(ownNodeId);
+ break;
+ case ZSTARTPHASE2:
+ ljam();
+ break;
+ case ZSTARTPHASE3:
+ ljam();
+ startphase3Lab(signal, ~0, ~0);
+ break;
+ case ZSTARTPHASE4:
+ ljam();
+ break;
+ case ZSTARTPHASE6:
+ ljam();
+/*****************************************/
+/* NOW SET THE DISK WRITE SPEED TO */
+/* PAGES PER TICK AFTER SYSTEM */
+/* RESTART. */
+/*****************************************/
+ clblPagesPerTick = clblPagesPerTickAfterSr;
+
+ signal->theData[0] = ZREPORT_MEMORY_USAGE;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 2000, 1);
+ break;
+ default:
+ ljam();
+ break;
+ }//switch
+ signal->theData[0] = cownref;
+ sendSignal(cndbcntrRef, GSN_NDB_STTORRY, signal, 1, JBB);
+}//Dbtup::execNDB_STTOR()
+
+void Dbtup::startphase3Lab(Signal* signal, Uint32 config1, Uint32 config2)
+{
+ clblPageCounter = clblPagesPerTick;
+ signal->theData[0] = ZLOAD_BAL_LCP_TIMER;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 100, 1);
+}//Dbtup::startphase3Lab()
+
+void Dbtup::initializeAttrbufrec()
+{
+ AttrbufrecPtr attrBufPtr;
+ for (attrBufPtr.i = 0;
+ attrBufPtr.i < cnoOfAttrbufrec; attrBufPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(attrBufPtr, attrbufrec);
+ attrBufPtr.p->attrbuf[ZBUF_NEXT] = attrBufPtr.i + 1;
+ }//for
+ attrBufPtr.i = cnoOfAttrbufrec - 1;
+ ptrAss(attrBufPtr, attrbufrec);
+ attrBufPtr.p->attrbuf[ZBUF_NEXT] = RNIL;
+ cfirstfreeAttrbufrec = 0;
+ cnoFreeAttrbufrec = cnoOfAttrbufrec;
+}//Dbtup::initializeAttrbufrec()
+
+void Dbtup::initializeCheckpointInfoRec()
+{
+ CheckpointInfoPtr checkpointInfoPtr;
+ for (checkpointInfoPtr.i = 0;
+ checkpointInfoPtr.i < cnoOfLcpRec; checkpointInfoPtr.i++) {
+ ptrAss(checkpointInfoPtr, checkpointInfo);
+ checkpointInfoPtr.p->lcpNextRec = checkpointInfoPtr.i + 1;
+ }//for
+ checkpointInfoPtr.i = cnoOfLcpRec - 1;
+ ptrAss(checkpointInfoPtr, checkpointInfo);
+ checkpointInfoPtr.p->lcpNextRec = RNIL;
+ cfirstfreeLcp = 0;
+}//Dbtup::initializeCheckpointInfoRec()
+
+void Dbtup::initializeDiskBufferSegmentRecord()
+{
+ DiskBufferSegmentInfoPtr diskBufferSegmentPtr;
+ for (diskBufferSegmentPtr.i = 0;
+ diskBufferSegmentPtr.i < cnoOfConcurrentWriteOp; diskBufferSegmentPtr.i++) {
+ ptrAss(diskBufferSegmentPtr, diskBufferSegmentInfo);
+ diskBufferSegmentPtr.p->pdxNextRec = diskBufferSegmentPtr.i + 1;
+ diskBufferSegmentPtr.p->pdxBuffertype = NOT_INITIALIZED;
+ }//for
+ diskBufferSegmentPtr.i = cnoOfConcurrentWriteOp - 1;
+ ptrAss(diskBufferSegmentPtr, diskBufferSegmentInfo);
+ diskBufferSegmentPtr.p->pdxNextRec = RNIL;
+ cfirstfreePdx = 0;
+}//Dbtup::initializeDiskBufferSegmentRecord()
+
+void Dbtup::initializeFragoperrec()
+{
+ FragoperrecPtr fragoperPtr;
+ for (fragoperPtr.i = 0; fragoperPtr.i < cnoOfFragoprec; fragoperPtr.i++) {
+ ptrAss(fragoperPtr, fragoperrec);
+ fragoperPtr.p->nextFragoprec = fragoperPtr.i + 1;
+ }//for
+ fragoperPtr.i = cnoOfFragoprec - 1;
+ ptrAss(fragoperPtr, fragoperrec);
+ fragoperPtr.p->nextFragoprec = RNIL;
+ cfirstfreeFragopr = 0;
+}//Dbtup::initializeFragoperrec()
+
+void Dbtup::initializeFragrecord()
+{
+ FragrecordPtr regFragPtr;
+ for (regFragPtr.i = 0; regFragPtr.i < cnoOfFragrec; regFragPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(regFragPtr, fragrecord);
+ regFragPtr.p->nextfreefrag = regFragPtr.i + 1;
+ regFragPtr.p->checkpointVersion = RNIL;
+ regFragPtr.p->firstusedOprec = RNIL;
+ regFragPtr.p->lastusedOprec = RNIL;
+ regFragPtr.p->fragStatus = IDLE;
+ }//for
+ regFragPtr.i = cnoOfFragrec - 1;
+ ptrAss(regFragPtr, fragrecord);
+ regFragPtr.p->nextfreefrag = RNIL;
+ cfirstfreefrag = 0;
+}//Dbtup::initializeFragrecord()
+
+void Dbtup::initializeHostBuffer()
+{
+ Uint32 hostId;
+ cpackedListIndex = 0;
+ for (hostId = 0; hostId < MAX_NODES; hostId++) {
+ hostBuffer[hostId].inPackedList = false;
+ hostBuffer[hostId].noOfPacketsTA = 0;
+ hostBuffer[hostId].packetLenTA = 0;
+ }//for
+}//Dbtup::initializeHostBuffer()
+
+void Dbtup::initializeLocalLogInfo()
+{
+ LocalLogInfoPtr localLogInfoPtr;
+ for (localLogInfoPtr.i = 0;
+ localLogInfoPtr.i < cnoOfParallellUndoFiles; localLogInfoPtr.i++) {
+ ptrAss(localLogInfoPtr, localLogInfo);
+ localLogInfoPtr.p->lliActiveLcp = 0;
+ localLogInfoPtr.p->lliUndoFileHandle = RNIL;
+ }//for
+}//Dbtup::initializeLocalLogInfo()
+
+void Dbtup::initializeOperationrec()
+{
+ OperationrecPtr regOpPtr;
+ for (regOpPtr.i = 0; regOpPtr.i < cnoOfOprec; regOpPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(regOpPtr, operationrec);
+ regOpPtr.p->firstAttrinbufrec = RNIL;
+ regOpPtr.p->lastAttrinbufrec = RNIL;
+ regOpPtr.p->prevOprecInList = RNIL;
+ regOpPtr.p->nextOprecInList = regOpPtr.i + 1;
+ regOpPtr.p->optype = ZREAD;
+ regOpPtr.p->inFragList = ZFALSE;
+ regOpPtr.p->inActiveOpList = ZFALSE;
+/* FOR ABORT HANDLING BEFORE ANY SUCCESSFUL OPERATION */
+ regOpPtr.p->transstate = DISCONNECTED;
+ regOpPtr.p->storedProcedureId = ZNIL;
+ regOpPtr.p->prevActiveOp = RNIL;
+ regOpPtr.p->nextActiveOp = RNIL;
+ regOpPtr.p->tupVersion = ZNIL;
+ regOpPtr.p->deleteInsertFlag = 0;
+ }//for
+ regOpPtr.i = cnoOfOprec - 1;
+ ptrAss(regOpPtr, operationrec);
+ regOpPtr.p->nextOprecInList = RNIL;
+ cfirstfreeOprec = 0;
+}//Dbtup::initializeOperationrec()
+
+void Dbtup::initializePendingFileOpenInfoRecord()
+{
+ PendingFileOpenInfoPtr pendingFileOpenInfoPtr;
+ for (pendingFileOpenInfoPtr.i = 0;
+ pendingFileOpenInfoPtr.i < cnoOfConcurrentOpenOp; pendingFileOpenInfoPtr.i++) {
+ ptrAss(pendingFileOpenInfoPtr, pendingFileOpenInfo);
+ pendingFileOpenInfoPtr.p->pfoNextRec = pendingFileOpenInfoPtr.i + 1;
+ }//for
+ pendingFileOpenInfoPtr.i = cnoOfConcurrentOpenOp - 1;
+ ptrAss(pendingFileOpenInfoPtr, pendingFileOpenInfo);
+ pendingFileOpenInfoPtr.p->pfoNextRec = RNIL;
+ cfirstfreePfo = 0;
+}//Dbtup::initializePendingFileOpenInfoRecord()
+
+void Dbtup::initializeRestartInfoRec()
+{
+ RestartInfoRecordPtr restartInfoPtr;
+ for (restartInfoPtr.i = 0; restartInfoPtr.i < cnoOfRestartInfoRec; restartInfoPtr.i++) {
+ ptrAss(restartInfoPtr, restartInfoRecord);
+ restartInfoPtr.p->sriNextRec = restartInfoPtr.i + 1;
+ }//for
+ restartInfoPtr.i = cnoOfRestartInfoRec - 1;
+ ptrAss(restartInfoPtr, restartInfoRecord);
+ restartInfoPtr.p->sriNextRec = RNIL;
+ cfirstfreeSri = 0;
+}//Dbtup::initializeRestartInfoRec()
+
+void Dbtup::initializeTablerec()
+{
+ TablerecPtr regTabPtr;
+ for (regTabPtr.i = 0; regTabPtr.i < cnoOfTablerec; regTabPtr.i++) {
+ ljam();
+ refresh_watch_dog();
+ ptrAss(regTabPtr, tablerec);
+ initTab(regTabPtr.p);
+ }//for
+}//Dbtup::initializeTablerec()
+
+void
+Dbtup::initTab(Tablerec* const regTabPtr)
+{
+ for (Uint32 i = 0; i < (2 * MAX_FRAG_PER_NODE); i++) {
+ regTabPtr->fragid[i] = RNIL;
+ regTabPtr->fragrec[i] = RNIL;
+ }//for
+ regTabPtr->readFunctionArray = NULL;
+ regTabPtr->updateFunctionArray = NULL;
+ regTabPtr->charsetArray = NULL;
+
+ regTabPtr->tabDescriptor = RNIL;
+ regTabPtr->attributeGroupDescriptor = RNIL;
+ regTabPtr->readKeyArray = RNIL;
+
+ regTabPtr->checksumIndicator = false;
+ regTabPtr->GCPIndicator = false;
+
+ regTabPtr->noOfAttr = 0;
+ regTabPtr->noOfKeyAttr = 0;
+ regTabPtr->noOfNewAttr = 0;
+ regTabPtr->noOfAttributeGroups = 0;
+
+ regTabPtr->tupheadsize = 0;
+ regTabPtr->tupNullIndex = 0;
+ regTabPtr->tupNullWords = 0;
+ regTabPtr->tupChecksumIndex = 0;
+ regTabPtr->tupGCPIndex = 0;
+
+ regTabPtr->m_dropTable.tabUserPtr = RNIL;
+ regTabPtr->m_dropTable.tabUserRef = 0;
+ regTabPtr->tableStatus = NOT_DEFINED;
+
+ // Clear trigger data
+ if (!regTabPtr->afterInsertTriggers.isEmpty())
+ regTabPtr->afterInsertTriggers.release();
+ if (!regTabPtr->afterDeleteTriggers.isEmpty())
+ regTabPtr->afterDeleteTriggers.release();
+ if (!regTabPtr->afterUpdateTriggers.isEmpty())
+ regTabPtr->afterUpdateTriggers.release();
+ if (!regTabPtr->subscriptionInsertTriggers.isEmpty())
+ regTabPtr->subscriptionInsertTriggers.release();
+ if (!regTabPtr->subscriptionDeleteTriggers.isEmpty())
+ regTabPtr->subscriptionDeleteTriggers.release();
+ if (!regTabPtr->subscriptionUpdateTriggers.isEmpty())
+ regTabPtr->subscriptionUpdateTriggers.release();
+ if (!regTabPtr->constraintUpdateTriggers.isEmpty())
+ regTabPtr->constraintUpdateTriggers.release();
+ if (!regTabPtr->tuxCustomTriggers.isEmpty())
+ regTabPtr->tuxCustomTriggers.release();
+}//Dbtup::initTab()
+
+void Dbtup::initializeTabDescr()
+{
+ TableDescriptorPtr regTabDesPtr;
+ for (Uint32 i = 0; i < 16; i++) {
+ cfreeTdList[i] = RNIL;
+ }//for
+ for (regTabDesPtr.i = 0; regTabDesPtr.i < cnoOfTabDescrRec; regTabDesPtr.i++) {
+ refresh_watch_dog();
+ ptrAss(regTabDesPtr, tableDescriptor);
+ regTabDesPtr.p->tabDescr = RNIL;
+ }//for
+ freeTabDescr(0, cnoOfTabDescrRec);
+}//Dbtup::initializeTabDescr()
+
+void Dbtup::initializeUndoPage()
+{
+ UndoPagePtr undoPagep;
+ for (undoPagep.i = 0;
+ undoPagep.i < cnoOfUndoPage;
+ undoPagep.i = undoPagep.i + ZUB_SEGMENT_SIZE) {
+ refresh_watch_dog();
+ ptrAss(undoPagep, undoPage);
+ undoPagep.p->undoPageWord[ZPAGE_NEXT_POS] = undoPagep.i +
+ ZUB_SEGMENT_SIZE;
+ cnoFreeUndoSeg++;
+ }//for
+ undoPagep.i = cnoOfUndoPage - ZUB_SEGMENT_SIZE;
+ ptrAss(undoPagep, undoPage);
+ undoPagep.p->undoPageWord[ZPAGE_NEXT_POS] = RNIL;
+ cfirstfreeUndoSeg = 0;
+}//Dbtup::initializeUndoPage()
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/* --------------- CONNECT/DISCONNECT MODULE ---------------------- */
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execTUPSEIZEREQ(Signal* signal)
+{
+ OperationrecPtr regOperPtr;
+ ljamEntry();
+ Uint32 userPtr = signal->theData[0];
+ BlockReference userRef = signal->theData[1];
+ if (cfirstfreeOprec != RNIL) {
+ ljam();
+ seizeOpRec(regOperPtr);
+ } else {
+ ljam();
+ signal->theData[0] = userPtr;
+ signal->theData[1] = ZGET_OPREC_ERROR;
+ sendSignal(userRef, GSN_TUPSEIZEREF, signal, 2, JBB);
+ return;
+ }//if
+ regOperPtr.p->optype = ZREAD;
+ initOpConnection(regOperPtr.p, 0);
+ regOperPtr.p->userpointer = userPtr;
+ regOperPtr.p->userblockref = userRef;
+ signal->theData[0] = regOperPtr.p->userpointer;
+ signal->theData[1] = regOperPtr.i;
+ sendSignal(userRef, GSN_TUPSEIZECONF, signal, 2, JBB);
+ return;
+}//Dbtup::execTUPSEIZEREQ()
+
+#define printFragment(t){ for(Uint32 i = 0; i < (2 * MAX_FRAG_PER_NODE);i++){\
+ ndbout_c("table = %d fragid[%d] = %d fragrec[%d] = %d", \
+ t.i, t.p->fragid[i], i, t.p->fragrec[i]); }}
+
+void Dbtup::execTUPRELEASEREQ(Signal* signal)
+{
+ OperationrecPtr regOperPtr;
+ ljamEntry();
+ regOperPtr.i = signal->theData[0];
+ ptrCheckGuard(regOperPtr, cnoOfOprec, operationrec);
+ regOperPtr.p->transstate = DISCONNECTED;
+ regOperPtr.p->nextOprecInList = cfirstfreeOprec;
+ cfirstfreeOprec = regOperPtr.i;
+ signal->theData[0] = regOperPtr.p->userpointer;
+ sendSignal(regOperPtr.p->userblockref, GSN_TUPRELEASECONF, signal, 1, JBB);
+ return;
+}//Dbtup::execTUPRELEASEREQ()
+
+/* ---------------------------------------------------------------- */
+/* ---------------- FREE_DISK_BUFFER_SEGMENT_RECORD --------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* THIS ROUTINE DEALLOCATES A DISK SEGMENT AND ITS DATA PAGES */
+/* */
+/* INPUT: DISK_BUFFER_SEGMENT_PTR THE DISK SEGMENT */
+/* */
+/* -----------------------------------------------------------------*/
+void Dbtup::freeDiskBufferSegmentRecord(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ switch (dbsiPtr.p->pdxBuffertype) {
+ case UNDO_PAGES:
+ case COMMON_AREA_PAGES:
+ ljam();
+ freeUndoBufferPages(signal, dbsiPtr);
+ break;
+ case UNDO_RESTART_PAGES:
+ ljam();
+ dbsiPtr.p->pdxDataPage[0] = dbsiPtr.p->pdxUndoBufferSet[0];
+ freeUndoBufferPages(signal, dbsiPtr);
+ dbsiPtr.p->pdxDataPage[0] = dbsiPtr.p->pdxUndoBufferSet[1];
+ freeUndoBufferPages(signal, dbsiPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ releaseDiskBufferSegmentRecord(dbsiPtr);
+}//Dbtup::freeDiskBufferSegmentRecord()
+
+/* ---------------------------------------------------------------- */
+/* -------------------- FREE_UNDO_BUFFER_PAGES -------------------- */
+/* ---------------------------------------------------------------- */
+/* */
+/* THIS ROUTINE DEALLOCATES A SEGMENT OF UNDO PAGES */
+/* */
+/* INPUT: UNDO_PAGEP POINTER TO FIRST PAGE IN SEGMENT */
+/* */
+/* -----------------------------------------------------------------*/
+void Dbtup::freeUndoBufferPages(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ UndoPagePtr undoPagePtr;
+
+ undoPagePtr.i = dbsiPtr.p->pdxDataPage[0];
+ ptrCheckGuard(undoPagePtr, cnoOfUndoPage, undoPage);
+ undoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS] = cfirstfreeUndoSeg;
+ cfirstfreeUndoSeg = undoPagePtr.i;
+ cnoFreeUndoSeg++;
+ if (cnoFreeUndoSeg == ZMIN_PAGE_LIMIT_TUP_COMMITREQ) {
+ EXECUTE_DIRECT(DBLQH, GSN_TUP_COM_UNBLOCK, signal, 1);
+ ljamEntry();
+ }//if
+}//Dbtup::freeUndoBufferPages()
+
+void Dbtup::releaseCheckpointInfoRecord(CheckpointInfoPtr ciPtr)
+{
+ ciPtr.p->lcpNextRec = cfirstfreeLcp;
+ cfirstfreeLcp = ciPtr.i;
+}//Dbtup::releaseCheckpointInfoRecord()
+
+void Dbtup::releaseDiskBufferSegmentRecord(DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ dbsiPtr.p->pdxNextRec = cfirstfreePdx;
+ cfirstfreePdx = dbsiPtr.i;
+}//Dbtup::releaseDiskBufferSegmentRecord()
+
+void Dbtup::releaseFragrec(FragrecordPtr regFragPtr)
+{
+ regFragPtr.p->nextfreefrag = cfirstfreefrag;
+ cfirstfreefrag = regFragPtr.i;
+}//Dbtup::releaseFragrec()
+
+void Dbtup::releasePendingFileOpenInfoRecord(PendingFileOpenInfoPtr pfoPtr)
+{
+ pfoPtr.p->pfoNextRec = cfirstfreePfo;
+ cfirstfreePfo = pfoPtr.i;
+}//Dbtup::releasePendingFileOpenInfoRecord()
+
+void Dbtup::releaseRestartInfoRecord(RestartInfoRecordPtr riPtr)
+{
+ riPtr.p->sriNextRec = cfirstfreeSri;
+ cfirstfreeSri = riPtr.i;
+}//Dbtup::releaseRestartInfoRecord()
+
+void Dbtup::seizeCheckpointInfoRecord(CheckpointInfoPtr& ciPtr)
+{
+ ciPtr.i = cfirstfreeLcp;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ cfirstfreeLcp = ciPtr.p->lcpNextRec;
+ ciPtr.p->lcpNextRec = RNIL;
+}//Dbtup::seizeCheckpointInfoRecord()
+
+void Dbtup::seizeDiskBufferSegmentRecord(DiskBufferSegmentInfoPtr& dbsiPtr)
+{
+ dbsiPtr.i = cfirstfreePdx;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ cfirstfreePdx = dbsiPtr.p->pdxNextRec;
+ dbsiPtr.p->pdxNextRec = RNIL;
+ for (Uint32 i = 0; i < 16; i++) {
+ dbsiPtr.p->pdxDataPage[i] = RNIL;
+ }//for
+ dbsiPtr.p->pdxCheckpointInfoP = RNIL;
+ dbsiPtr.p->pdxRestartInfoP = RNIL;
+ dbsiPtr.p->pdxLocalLogInfoP = RNIL;
+ dbsiPtr.p->pdxFilePage = 0;
+ dbsiPtr.p->pdxNumDataPages = 0;
+}//Dbtup::seizeDiskBufferSegmentRecord()
+
+void Dbtup::seizeOpRec(OperationrecPtr& regOperPtr)
+{
+ regOperPtr.i = cfirstfreeOprec;
+ ptrCheckGuard(regOperPtr, cnoOfOprec, operationrec);
+ cfirstfreeOprec = regOperPtr.p->nextOprecInList;
+}//Dbtup::seizeOpRec()
+
+void Dbtup::seizePendingFileOpenInfoRecord(PendingFileOpenInfoPtr& pfoiPtr)
+{
+ pfoiPtr.i = cfirstfreePfo;
+ ptrCheckGuard(pfoiPtr, cnoOfConcurrentOpenOp, pendingFileOpenInfo);
+ cfirstfreePfo = pfoiPtr.p->pfoNextRec;
+ pfoiPtr.p->pfoNextRec = RNIL;
+}//Dbtup::seizePendingFileOpenInfoRecord()
+
+void Dbtup::execSET_VAR_REQ(Signal* signal)
+{
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)signal->getDataPtrSend();
+ ConfigParamId var = setVarReq->variable();
+ int val = setVarReq->value();
+
+ switch (var) {
+
+ case NoOfDiskPagesToDiskAfterRestartTUP:
+ clblPagesPerTick = val;
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case NoOfDiskPagesToDiskDuringRestartTUP:
+ // Valid only during start so value not set.
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ } // switch
+#endif
+
+}//execSET_VAR_REQ()
+
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp
new file mode 100644
index 00000000000..ab6e0642e11
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp
@@ -0,0 +1,569 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <AttributeHeader.hpp>
+#include <signaldata/TuxMaint.hpp>
+
+#define ljam() { jamLine(28000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(28000 + __LINE__); }
+
+// methods used by ordered index
+
+void
+Dbtup::tuxGetTupAddr(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32& tupAddr)
+{
+ ljamEntry();
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ PagePtr pagePtr;
+ pagePtr.i = pageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 fragPageId = pagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ Uint32 tupheadsize = tablePtr.p->tupheadsize;
+ ndbrequire(pageOffset >= ZPAGE_HEADER_SIZE);
+ Uint32 offset = pageOffset - ZPAGE_HEADER_SIZE;
+ ndbrequire(offset % tupheadsize == 0);
+ Uint32 pageIndex = (offset / tupheadsize) << 1;
+ tupAddr = (fragPageId << MAX_TUPLES_BITS) | pageIndex;
+}
+
+int
+Dbtup::tuxAllocNode(Signal* signal, Uint32 fragPtrI, Uint32& pageId, Uint32& pageOffset, Uint32*& node)
+{
+ ljamEntry();
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ PagePtr pagePtr;
+ terrorCode = 0;
+ if (! allocTh(fragPtr.p, tablePtr.p, NORMAL_PAGE, signal, pageOffset, pagePtr)) {
+ ljam();
+ ndbrequire(terrorCode != 0);
+ return terrorCode;
+ }
+ pageId = pagePtr.i;
+ Uint32 attrDescIndex = tablePtr.p->tabDescriptor + (0 << ZAD_LOG_SIZE);
+ Uint32 attrDataOffset = AttributeOffset::getOffset(tableDescriptor[attrDescIndex + 1].tabDescr);
+ node = &pagePtr.p->pageWord[pageOffset] + attrDataOffset;
+ return 0;
+}
+
+void
+Dbtup::tuxFreeNode(Signal* signal, Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* node)
+{
+ ljamEntry();
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ PagePtr pagePtr;
+ pagePtr.i = pageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 attrDescIndex = tablePtr.p->tabDescriptor + (0 << ZAD_LOG_SIZE);
+ Uint32 attrDataOffset = AttributeOffset::getOffset(tableDescriptor[attrDescIndex + 1].tabDescr);
+ ndbrequire(node == &pagePtr.p->pageWord[pageOffset] + attrDataOffset);
+ freeTh(fragPtr.p, tablePtr.p, signal, pagePtr.p, pageOffset);
+}
+
+void
+Dbtup::tuxGetNode(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32*& node)
+{
+ ljamEntry();
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ PagePtr pagePtr;
+ pagePtr.i = pageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 attrDescIndex = tablePtr.p->tabDescriptor + (0 << ZAD_LOG_SIZE);
+ Uint32 attrDataOffset = AttributeOffset::getOffset(tableDescriptor[attrDescIndex + 1].tabDescr);
+ node = &pagePtr.p->pageWord[pageOffset] + attrDataOffset;
+}
+
+int
+Dbtup::tuxReadAttrs(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32 tupVersion, const Uint32* attrIds, Uint32 numAttrs, Uint32* dataOut)
+{
+ ljamEntry();
+ // use own variables instead of globals
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ PagePtr pagePtr;
+ pagePtr.i = pageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ // search for tuple version if not original
+ if (pagePtr.p->pageWord[pageOffset + 1] != tupVersion) {
+ ljam();
+ OperationrecPtr opPtr;
+ opPtr.i = pagePtr.p->pageWord[pageOffset];
+ Uint32 loopGuard = 0;
+ while (true) {
+ ptrCheckGuard(opPtr, cnoOfOprec, operationrec);
+ if (opPtr.p->realPageIdC != RNIL) {
+ // update page and offset
+ pagePtr.i = opPtr.p->realPageIdC;
+ pageOffset = opPtr.p->pageOffsetC;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ if (pagePtr.p->pageWord[pageOffset + 1] == tupVersion) {
+ ljam();
+ break;
+ }
+ }
+ ljam();
+ opPtr.i = opPtr.p->nextActiveOp;
+ ndbrequire(++loopGuard < (1 << ZTUP_VERSION_BITS));
+ }
+ }
+ // read key attributes from found tuple version
+ // save globals
+ TablerecPtr tabptr_old = tabptr;
+ FragrecordPtr fragptr_old = fragptr;
+ OperationrecPtr operPtr_old = operPtr;
+ // new globals
+ tabptr = tablePtr;
+ fragptr = fragPtr;
+ operPtr.i = RNIL;
+ operPtr.p = NULL;
+ // do it
+ int ret = readAttributes(pagePtr.p, pageOffset, attrIds, numAttrs, dataOut, ZNIL, true);
+ // restore globals
+ tabptr = tabptr_old;
+ fragptr = fragptr_old;
+ operPtr = operPtr_old;
+ // done
+ if (ret == -1) {
+ ret = terrorCode ? (-(int)terrorCode) : -1;
+ }
+ return ret;
+}
+
+int
+Dbtup::tuxReadPk(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* dataOut, bool xfrmFlag)
+{
+ ljamEntry();
+ // use own variables instead of globals
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ PagePtr pagePtr;
+ pagePtr.i = pageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ const Uint32 tabDescriptor = tablePtr.p->tabDescriptor;
+ const Uint32* attrIds = &tableDescriptor[tablePtr.p->readKeyArray].tabDescr;
+ const Uint32 numAttrs = tablePtr.p->noOfKeyAttr;
+ // read pk attributes from original tuple
+ // save globals
+ TablerecPtr tabptr_old = tabptr;
+ FragrecordPtr fragptr_old = fragptr;
+ OperationrecPtr operPtr_old = operPtr;
+ // new globals
+ tabptr = tablePtr;
+ fragptr = fragPtr;
+ operPtr.i = RNIL;
+ operPtr.p = NULL;
+ // do it
+ int ret = readAttributes(pagePtr.p, pageOffset, attrIds, numAttrs, dataOut, ZNIL, xfrmFlag);
+ // restore globals
+ tabptr = tabptr_old;
+ fragptr = fragptr_old;
+ operPtr = operPtr_old;
+ // done
+ if (ret != -1) {
+ // remove headers
+ Uint32 n = 0;
+ Uint32 i = 0;
+ while (n < numAttrs) {
+ const AttributeHeader ah(dataOut[i]);
+ Uint32 size = ah.getDataSize();
+ ndbrequire(size != 0);
+ for (Uint32 j = 0; j < size; j++) {
+ dataOut[i + j - n] = dataOut[i + j + 1];
+ }
+ n += 1;
+ i += 1 + size;
+ }
+ ndbrequire((int)i == ret);
+ ret -= numAttrs;
+ } else {
+ ret = terrorCode ? (-(int)terrorCode) : -1;
+ }
+ return ret;
+}
+
+int
+Dbtup::accReadPk(Uint32 tableId, Uint32 fragId, Uint32 fragPageId, Uint32 pageIndex, Uint32* dataOut, bool xfrmFlag)
+{
+ ljamEntry();
+ // get table
+ TablerecPtr tablePtr;
+ tablePtr.i = tableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ // get fragment
+ FragrecordPtr fragPtr;
+ getFragmentrec(fragPtr, fragId, tablePtr.p);
+ // get real page id and tuple offset
+ PagePtr pagePtr;
+ Uint32 pageId = getRealpid(fragPtr.p, fragPageId);
+ ndbrequire((pageIndex & 0x1) == 0);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + (pageIndex >> 1) * tablePtr.p->tupheadsize;
+ // use TUX routine - optimize later
+ int ret = tuxReadPk(fragPtr.i, pageId, pageOffset, dataOut, xfrmFlag);
+ return ret;
+}
+
+bool
+Dbtup::tuxQueryTh(Uint32 fragPtrI, Uint32 tupAddr, Uint32 tupVersion, Uint32 transId1, Uint32 transId2, Uint32 savePointId)
+{
+ ljamEntry();
+ FragrecordPtr fragPtr;
+ fragPtr.i = fragPtrI;
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ TablerecPtr tablePtr;
+ tablePtr.i = fragPtr.p->fragTableId;
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ // get page
+ PagePtr pagePtr;
+ Uint32 fragPageId = tupAddr >> MAX_TUPLES_BITS;
+ Uint32 pageIndex = tupAddr & ((1 << MAX_TUPLES_BITS ) - 1);
+ // use temp op rec
+ Operationrec tempOp;
+ tempOp.fragPageId = fragPageId;
+ tempOp.pageIndex = pageIndex;
+ tempOp.transid1 = transId1;
+ tempOp.transid2 = transId2;
+ tempOp.savePointId = savePointId;
+ tempOp.optype = ZREAD;
+ tempOp.dirtyOp = 1;
+ if (getPage(pagePtr, &tempOp, fragPtr.p, tablePtr.p)) {
+ /*
+ * We use the normal getPage which will return the tuple to be used
+ * for this transaction and savepoint id. If its tuple version
+ * equals the requested then we have a visible tuple otherwise not.
+ */
+ ljam();
+ Uint32 read_tupVersion = pagePtr.p->pageWord[tempOp.pageOffset + 1];
+ if (read_tupVersion == tupVersion) {
+ ljam();
+ return true;
+ }
+ }
+ return false;
+}
+
+// ordered index build
+
+//#define TIME_MEASUREMENT
+#ifdef TIME_MEASUREMENT
+ static Uint32 time_events;
+ NDB_TICKS tot_time_passed;
+ Uint32 number_events;
+#endif
+void
+Dbtup::execBUILDINDXREQ(Signal* signal)
+{
+ ljamEntry();
+#ifdef TIME_MEASUREMENT
+ time_events = 0;
+ tot_time_passed = 0;
+ number_events = 1;
+#endif
+ // get new operation
+ BuildIndexPtr buildPtr;
+ if (! c_buildIndexList.seize(buildPtr)) {
+ ljam();
+ BuildIndexRec buildRec;
+ memcpy(buildRec.m_request, signal->theData, sizeof(buildRec.m_request));
+ buildRec.m_errorCode = BuildIndxRef::Busy;
+ buildIndexReply(signal, &buildRec);
+ return;
+ }
+ memcpy(buildPtr.p->m_request, signal->theData, sizeof(buildPtr.p->m_request));
+ // check
+ buildPtr.p->m_errorCode = BuildIndxRef::NoError;
+ do {
+ const BuildIndxReq* buildReq = (const BuildIndxReq*)buildPtr.p->m_request;
+ if (buildReq->getTableId() >= cnoOfTablerec) {
+ ljam();
+ buildPtr.p->m_errorCode = BuildIndxRef::InvalidPrimaryTable;
+ break;
+ }
+ TablerecPtr tablePtr;
+ tablePtr.i = buildReq->getTableId();
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ if (tablePtr.p->tableStatus != DEFINED) {
+ ljam();
+ buildPtr.p->m_errorCode = BuildIndxRef::InvalidPrimaryTable;
+ break;
+ }
+ if (! DictTabInfo::isOrderedIndex(buildReq->getIndexType())) {
+ ljam();
+ buildPtr.p->m_errorCode = BuildIndxRef::InvalidIndexType;
+ break;
+ }
+ const ArrayList<TupTriggerData>& triggerList = tablePtr.p->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ if (triggerPtr.p->indexId == buildReq->getIndexId()) {
+ ljam();
+ break;
+ }
+ triggerList.next(triggerPtr);
+ }
+ if (triggerPtr.i == RNIL) {
+ ljam();
+ // trigger was not created
+ buildPtr.p->m_errorCode = BuildIndxRef::InternalError;
+ break;
+ }
+ buildPtr.p->m_triggerPtrI = triggerPtr.i;
+ // set to first tuple position
+ buildPtr.p->m_fragNo = 0;
+ buildPtr.p->m_pageId = 0;
+ buildPtr.p->m_tupleNo = 0;
+ // start build
+ buildIndex(signal, buildPtr.i);
+ return;
+ } while (0);
+ // check failed
+ buildIndexReply(signal, buildPtr.p);
+ c_buildIndexList.release(buildPtr);
+}
+
+void
+Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI)
+{
+ // get build record
+ BuildIndexPtr buildPtr;
+ buildPtr.i = buildPtrI;
+ c_buildIndexList.getPtr(buildPtr);
+ const BuildIndxReq* buildReq = (const BuildIndxReq*)buildPtr.p->m_request;
+ // get table
+ TablerecPtr tablePtr;
+ tablePtr.i = buildReq->getTableId();
+ ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
+ // get trigger
+ TriggerPtr triggerPtr;
+ triggerPtr.i = buildPtr.p->m_triggerPtrI;
+ c_triggerPool.getPtr(triggerPtr);
+ ndbrequire(triggerPtr.p->indexId == buildReq->getIndexId());
+#ifdef TIME_MEASUREMENT
+ MicroSecondTimer start;
+ MicroSecondTimer stop;
+ NDB_TICKS time_passed;
+#endif
+ do {
+ // get fragment
+ FragrecordPtr fragPtr;
+ if (buildPtr.p->m_fragNo == 2 * MAX_FRAG_PER_NODE) {
+ ljam();
+ // build ready
+ buildIndexReply(signal, buildPtr.p);
+ c_buildIndexList.release(buildPtr);
+ return;
+ }
+ ndbrequire(buildPtr.p->m_fragNo < 2 * MAX_FRAG_PER_NODE);
+ fragPtr.i = tablePtr.p->fragrec[buildPtr.p->m_fragNo];
+ if (fragPtr.i == RNIL) {
+ ljam();
+ buildPtr.p->m_fragNo++;
+ buildPtr.p->m_pageId = 0;
+ buildPtr.p->m_tupleNo = 0;
+ break;
+ }
+ ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
+ // get page
+ PagePtr pagePtr;
+ if (buildPtr.p->m_pageId >= fragPtr.p->noOfPages) {
+ ljam();
+ buildPtr.p->m_fragNo++;
+ buildPtr.p->m_pageId = 0;
+ buildPtr.p->m_tupleNo = 0;
+ break;
+ }
+ Uint32 realPageId = getRealpid(fragPtr.p, buildPtr.p->m_pageId);
+ pagePtr.i = realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ const Uint32 pageState = pagePtr.p->pageWord[ZPAGE_STATE_POS];
+ if (pageState != ZTH_MM_FREE &&
+ pageState != ZTH_MM_FREE_COPY &&
+ pageState != ZTH_MM_FULL &&
+ pageState != ZTH_MM_FULL_COPY) {
+ ljam();
+ buildPtr.p->m_pageId++;
+ buildPtr.p->m_tupleNo = 0;
+ break;
+ }
+ // get tuple
+ const Uint32 tupheadsize = tablePtr.p->tupheadsize;
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + buildPtr.p->m_tupleNo * tupheadsize;
+ if (pageOffset + tupheadsize > ZWORDS_ON_PAGE) {
+ ljam();
+ buildPtr.p->m_pageId++;
+ buildPtr.p->m_tupleNo = 0;
+ break;
+ }
+ // skip over free tuple
+ bool isFree = false;
+ if (pageState == ZTH_MM_FREE ||
+ pageState == ZTH_MM_FREE_COPY) {
+ ljam();
+ if ((pagePtr.p->pageWord[pageOffset] >> 16) == tupheadsize) {
+ // verify it really is free XXX far too expensive
+ Uint32 nextTuple = pagePtr.p->pageWord[ZFREELIST_HEADER_POS] >> 16;
+ ndbrequire(nextTuple != 0);
+ while (nextTuple != 0) {
+ ljam();
+ if (nextTuple == pageOffset) {
+ ljam();
+ isFree = true;
+ break;
+ }
+ nextTuple = pagePtr.p->pageWord[nextTuple] & 0xffff;
+ }
+ }
+ }
+ if (isFree) {
+ ljam();
+ buildPtr.p->m_tupleNo++;
+ break;
+ }
+ Uint32 tupVersion = pagePtr.p->pageWord[pageOffset + 1];
+ OperationrecPtr pageOperPtr;
+ pageOperPtr.i = pagePtr.p->pageWord[pageOffset];
+ if (pageOperPtr.i != RNIL) {
+ /*
+ If there is an ongoing operation on the tuple then it is either a
+ copy tuple or an original tuple with an ongoing transaction. In
+ both cases realPageId and pageOffset refer to the original tuple.
+ The tuple address stored in TUX will always be the original tuple
+ but with the tuple version of the tuple we found.
+
+ This is necessary to avoid having to update TUX at abort of
+ update. If an update aborts then the copy tuple is copied to
+ the original tuple. The build will however have found that
+ tuple as a copy tuple. The original tuple is stable and is thus
+ preferrable to store in TUX.
+ */
+ ljam();
+ ptrCheckGuard(pageOperPtr, cnoOfOprec, operationrec);
+ realPageId = pageOperPtr.p->realPageId;
+ pageOffset = pageOperPtr.p->pageOffset;
+ }//if
+#ifdef TIME_MEASUREMENT
+ NdbTick_getMicroTimer(&start);
+#endif
+ // add to index
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ req->errorCode = RNIL;
+ req->tableId = tablePtr.i;
+ req->indexId = triggerPtr.p->indexId;
+ req->fragId = tablePtr.p->fragid[buildPtr.p->m_fragNo];
+ req->pageId = realPageId;
+ req->pageOffset = pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpAdd;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ if (req->errorCode != 0) {
+ switch (req->errorCode) {
+ case TuxMaintReq::NoMemError:
+ ljam();
+ buildPtr.p->m_errorCode = BuildIndxRef::AllocationFailure;
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ buildIndexReply(signal, buildPtr.p);
+ c_buildIndexList.release(buildPtr);
+ return;
+ }
+#ifdef TIME_MEASUREMENT
+ NdbTick_getMicroTimer(&stop);
+ time_passed = NdbTick_getMicrosPassed(start, stop);
+ if (time_passed < 1000) {
+ time_events++;
+ tot_time_passed += time_passed;
+ if (time_events == number_events) {
+ NDB_TICKS mean_time_passed = tot_time_passed / (NDB_TICKS)number_events;
+ ndbout << "Number of events = " << number_events;
+ ndbout << " Mean time passed = " << mean_time_passed << endl;
+ number_events <<= 1;
+ tot_time_passed = (NDB_TICKS)0;
+ time_events = 0;
+ }//if
+ }
+#endif
+ // next tuple
+ buildPtr.p->m_tupleNo++;
+ break;
+ } while (0);
+ signal->theData[0] = ZBUILD_INDEX;
+ signal->theData[1] = buildPtr.i;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2, JBB);
+}
+
+void
+Dbtup::buildIndexReply(Signal* signal, const BuildIndexRec* buildPtrP)
+{
+ const BuildIndxReq* const buildReq = (const BuildIndxReq*)buildPtrP->m_request;
+ // conf is subset of ref
+ BuildIndxRef* rep = (BuildIndxRef*)signal->getDataPtr();
+ rep->setUserRef(buildReq->getUserRef());
+ rep->setConnectionPtr(buildReq->getConnectionPtr());
+ rep->setRequestType(buildReq->getRequestType());
+ rep->setTableId(buildReq->getTableId());
+ rep->setIndexType(buildReq->getIndexType());
+ rep->setIndexId(buildReq->getIndexId());
+ // conf
+ if (buildPtrP->m_errorCode == BuildIndxRef::NoError) {
+ ljam();
+ sendSignal(rep->getUserRef(), GSN_BUILDINDXCONF,
+ signal, BuildIndxConf::SignalLength, JBB);
+ return;
+ }
+ // ref
+ rep->setErrorCode(buildPtrP->m_errorCode);
+ sendSignal(rep->getUserRef(), GSN_BUILDINDXREF,
+ signal, BuildIndxRef::SignalLength, JBB);
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupLCP.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupLCP.cpp
new file mode 100644
index 00000000000..370ef4c4ba5
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupLCP.cpp
@@ -0,0 +1,596 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+
+#define ljam() { jamLine(10000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(10000 + __LINE__); }
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/* -------------------- LOCAL CHECKPOINT MODULE ------------------- */
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execTUP_PREPLCPREQ(Signal* signal)
+{
+ CheckpointInfoPtr ciPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ FragrecordPtr regFragPtr;
+ LocalLogInfoPtr lliPtr;
+ TablerecPtr regTabPtr;
+
+ ljamEntry();
+ Uint32 userptr = signal->theData[0];
+ BlockReference userblockref = signal->theData[1];
+ regTabPtr.i = signal->theData[2];
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+ Uint32 fragId = signal->theData[3];
+ Uint32 checkpointNumber = signal->theData[4];
+ cundoFileVersion = signal->theData[5];
+
+ getFragmentrec(regFragPtr, fragId, regTabPtr.p);
+ ndbrequire(regTabPtr.i != RNIL);
+ seizeCheckpointInfoRecord(ciPtr);
+
+ lliPtr.i = (cundoFileVersion << 2) + (regTabPtr.i & 0x3);
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ cnoOfDataPagesToDiskWithoutSynch = 0;
+
+ ciPtr.p->lcpDataFileHandle = RNIL;
+ ciPtr.p->lcpCheckpointVersion = checkpointNumber;
+ ciPtr.p->lcpLocalLogInfoP = lliPtr.i;
+ ciPtr.p->lcpFragmentP = regFragPtr.i; /* SET THE FRAGMENT */
+ ciPtr.p->lcpFragmentId = fragId; /* SAVE THE FRAGMENT IDENTITY */
+ ciPtr.p->lcpTabPtr = regTabPtr.i; /* SET THE TABLE POINTER */
+ ciPtr.p->lcpBlockref = userblockref; /* SET THE BLOCK REFERENCE */
+ ciPtr.p->lcpUserptr = userptr; /* SET THE USERPOINTER */
+
+ /***************************************************************/
+ /* OPEN THE UNDO FILE FOR WRITE */
+ /* UPON FSOPENCONF */
+ /***************************************************************/
+ if (lliPtr.p->lliActiveLcp == 0) { /* IS THE UNDO LOG FILE OPEN? */
+ PendingFileOpenInfoPtr undoPfoiPtr;
+ UndoPagePtr regUndoPagePtr;
+
+ ljam();
+ lliPtr.p->lliPrevRecordId = 0;
+ lliPtr.p->lliLogFilePage = 0;
+ lliPtr.p->lliUndoPagesToDiskWithoutSynch = 0;
+ lliPtr.p->lliUndoWord = ZUNDO_PAGE_HEADER_SIZE;
+
+ seizeUndoBufferSegment(signal, regUndoPagePtr);
+ seizeDiskBufferSegmentRecord(dbsiPtr);
+ dbsiPtr.p->pdxBuffertype = UNDO_PAGES;
+ for (Uint32 i = 0; i < ZUB_SEGMENT_SIZE; i++) {
+ dbsiPtr.p->pdxDataPage[i] = regUndoPagePtr.i + i;
+ }//for
+ dbsiPtr.p->pdxFilePage = lliPtr.p->lliLogFilePage;
+ lliPtr.p->lliUndoPage = regUndoPagePtr.i;
+ lliPtr.p->lliUndoBufferSegmentP = dbsiPtr.i;
+ /* F LEVEL NOT USED */
+ Uint32 fileType = 1; /* VERSION */
+ fileType = (fileType << 8) | 2; /* .LOCLOG */
+ fileType = (fileType << 8) | 6; /* D6 */
+ fileType = (fileType << 8) | 0xff; /* DON'T USE P DIRECTORY LEVEL */
+ Uint32 fileFlag = 0x301; /* CREATE, WRITE ONLY, TRUNCATE */
+
+ seizePendingFileOpenInfoRecord(undoPfoiPtr);
+ undoPfoiPtr.p->pfoOpenType = LCP_UNDO_FILE_WRITE;
+ undoPfoiPtr.p->pfoCheckpointInfoP = ciPtr.i;
+
+ signal->theData[0] = cownref;
+ signal->theData[1] = undoPfoiPtr.i;
+ signal->theData[2] = lliPtr.i;
+ signal->theData[3] = 0xFFFFFFFF;
+ signal->theData[4] = cundoFileVersion;
+ signal->theData[5] = fileType;
+ signal->theData[6] = fileFlag;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ }//if
+ /***************************************************************/
+ /* OPEN THE DATA FILE FOR WRITE */
+ /* THE FILE HANDLE WILL BE SET IN THE CHECKPOINT_INFO_RECORD */
+ /* UPON FSOPENCONF */
+ /***************************************************************/
+ /* OPEN THE DATA FILE IN THE FOLLOWING FORM */
+ /* D5/DBTUP/T<TABID>/F<FRAGID>/S<CHECKPOINT_NUMBER>.DATA */
+
+ PendingFileOpenInfoPtr dataPfoiPtr;
+
+ Uint32 fileType = 1; /* VERSION */
+ fileType = (fileType << 8) | 0; /* .DATA */
+ fileType = (fileType << 8) | 5; /* D5 */
+ fileType = (fileType << 8) | 0xff; /* DON'T USE P DIRECTORY LEVEL */
+ Uint32 fileFlag = 0x301; /* CREATE, WRITE ONLY, TRUNCATE */
+
+ seizePendingFileOpenInfoRecord(dataPfoiPtr); /* SEIZE A NEW FILE OPEN INFO */
+ if (lliPtr.p->lliActiveLcp == 0) {
+ ljam();
+ dataPfoiPtr.p->pfoOpenType = LCP_DATA_FILE_WRITE_WITH_UNDO;
+ } else {
+ ljam();
+ dataPfoiPtr.p->pfoOpenType = LCP_DATA_FILE_WRITE;
+ }//if
+ dataPfoiPtr.p->pfoCheckpointInfoP = ciPtr.i;
+
+ /* LET'S OPEN THE DATA FILE FOR WRITE */
+ /* INCREASE NUMBER OF ACTIVE CHECKPOINTS */
+ lliPtr.p->lliActiveLcp = 1;
+ signal->theData[0] = cownref;
+ signal->theData[1] = dataPfoiPtr.i;
+ signal->theData[2] = ciPtr.p->lcpTabPtr;
+ signal->theData[3] = ciPtr.p->lcpFragmentId;
+ signal->theData[4] = ciPtr.p->lcpCheckpointVersion;
+ signal->theData[5] = fileType;
+ signal->theData[6] = fileFlag;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ return;
+}//Dbtup::execTUP_PREPLCPREQ()
+
+/* ---------------------------------------------------------------- */
+/* ------------------------ START CHECKPOINT --------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execTUP_LCPREQ(Signal* signal)
+{
+ CheckpointInfoPtr ciPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ FragrecordPtr regFragPtr;
+ LocalLogInfoPtr lliPtr;
+
+ ljamEntry();
+// Uint32 userptr = signal->theData[0];
+// BlockReference userblockref = signal->theData[1];
+ ciPtr.i = signal->theData[2];
+
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ regFragPtr.i = ciPtr.p->lcpFragmentP;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+
+/* ---------------------------------------------------------------- */
+/* ASSIGNING A VALUE DIFFERENT FROM RNIL TO CHECKPOINT VERSION*/
+/* TRIGGERS THAT UNDO LOGGING WILL START FOR THIS FRAGMENT. */
+/* WE ASSIGN IT THE POINTER TO THE CHECKPOINT RECORD FOR */
+/* OPTIMISATION OF THE WRITING OF THE UNDO LOG. */
+/* ---------------------------------------------------------------- */
+ regFragPtr.p->checkpointVersion = ciPtr.p->lcpLocalLogInfoP; /* MARK START OF UNDO LOGGING */
+
+ regFragPtr.p->maxPageWrittenInCheckpoint = getNoOfPages(regFragPtr.p);
+ regFragPtr.p->minPageNotWrittenInCheckpoint = 0;
+ ndbrequire(getNoOfPages(regFragPtr.p) > 0);
+ allocDataBufferSegment(signal, dbsiPtr);
+
+ dbsiPtr.p->pdxNumDataPages = 0;
+ dbsiPtr.p->pdxFilePage = 1;
+ ciPtr.p->lcpDataBufferSegmentP = dbsiPtr.i;
+ dbsiPtr.p->pdxCheckpointInfoP = ciPtr.i;
+ ciPtr.p->lcpNoOfPages = getNoOfPages(regFragPtr.p);
+ ciPtr.p->lcpNoCopyPagesAlloc = regFragPtr.p->noCopyPagesAlloc;
+ ciPtr.p->lcpEmptyPrimPage = regFragPtr.p->emptyPrimPage;
+ ciPtr.p->lcpThFreeFirst = regFragPtr.p->thFreeFirst;
+ ciPtr.p->lcpThFreeCopyFirst = regFragPtr.p->thFreeCopyFirst;
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+/* ---------------------------------------------------------------- */
+/* --- PERFORM A COPY OF THE TABLE DESCRIPTOR FOR THIS FRAGMENT --- */
+/* ---------------------------------------------------------------- */
+ cprAddLogHeader(signal,
+ lliPtr.p,
+ ZTABLE_DESCRIPTOR,
+ ciPtr.p->lcpTabPtr,
+ ciPtr.p->lcpFragmentId);
+
+/* ---------------------------------------------------------------- */
+/* CONTINUE WITH SAVING ACTIVE OPERATIONS AFTER A REAL-TIME */
+/* BREAK. */
+/* ---------------------------------------------------------------- */
+ ciPtr.p->lcpTmpOperPtr = regFragPtr.p->firstusedOprec;
+ lcpSaveCopyListLab(signal, ciPtr);
+ return;
+}//Dbtup::execTUP_LCPREQ()
+
+void Dbtup::allocDataBufferSegment(Signal* signal, DiskBufferSegmentInfoPtr& dbsiPtr)
+{
+ UndoPagePtr regUndoPagePtr;
+
+ seizeDiskBufferSegmentRecord(dbsiPtr);
+ dbsiPtr.p->pdxBuffertype = COMMON_AREA_PAGES;
+ ndbrequire(cfirstfreeUndoSeg != RNIL);
+ if (cnoFreeUndoSeg == ZMIN_PAGE_LIMIT_TUP_COMMITREQ) {
+ EXECUTE_DIRECT(DBLQH, GSN_TUP_COM_BLOCK, signal, 1);
+ ljamEntry();
+ }//if
+ cnoFreeUndoSeg--;
+ ndbrequire(cnoFreeUndoSeg >= 0);
+
+ regUndoPagePtr.i = cfirstfreeUndoSeg;
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+ cfirstfreeUndoSeg = regUndoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS];
+ regUndoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS] = RNIL;
+ for (Uint32 i = 0; i < ZUB_SEGMENT_SIZE; i++) {
+ dbsiPtr.p->pdxDataPage[i] = regUndoPagePtr.i + i;
+ }//for
+}//Dbtup::allocDataBufferSegment()
+
+/* ---------------------------------------------------------------- */
+/* --- PERFORM A COPY OF THE ACTIVE OPERATIONS FOR THIS FRAGMENT -- */
+/* ---------------------------------------------------------------- */
+void Dbtup::lcpSaveCopyListLab(Signal* signal, CheckpointInfoPtr ciPtr)
+{
+ FragrecordPtr regFragPtr;
+ LocalLogInfoPtr lliPtr;
+ OperationrecPtr regOpPtr;
+
+ regFragPtr.i = ciPtr.p->lcpFragmentP;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ regOpPtr.i = ciPtr.p->lcpTmpOperPtr;
+
+/* -------------------------------------------------------------------------------- */
+/* TRAVERSE THE ENTIRE BLOCK OF OPERATIONS. CHECK IF THERE ARE EXISTING COPYS OF */
+/* TUPLES IN THE CHECKPOINTED FRAGMENT. SAVE THOSE IN A LIST IN THE FOLLOWING FORM: */
+/* */
+/* SOURCE PAGE */
+/* SOURCE INDEX */
+/* COPY PAGE */
+/* COPY INDEX */
+/* -------------------------------------------------------------------------------- */
+ Uint32 loopCount = 0;
+ while ((regOpPtr.i != RNIL) && (loopCount < 50)) {
+ ljam();
+ ptrCheckGuard(regOpPtr, cnoOfOprec, operationrec);
+ if (regOpPtr.p->realPageId != RNIL) {
+/* ---------------------------------------------------------------- */
+// We ensure that we have actually allocated the tuple header and
+// also found it. Otherwise we will fill the undo log with garbage.
+/* ---------------------------------------------------------------- */
+ if (regOpPtr.p->optype == ZUPDATE ||
+ (regOpPtr.p->optype == ZINSERT && regOpPtr.p->deleteInsertFlag)) {
+ ljam();
+ if (regOpPtr.p->realPageIdC != RNIL) {
+/* ---------------------------------------------------------------- */
+// We ensure that we have actually allocated the tuple header copy.
+// Otherwise we will fill the undo log with garbage.
+/* ---------------------------------------------------------------- */
+ cprAddLogHeader(signal,
+ lliPtr.p,
+ ZLCPR_ABORT_UPDATE,
+ ciPtr.p->lcpTabPtr,
+ ciPtr.p->lcpFragmentId);
+ cprAddAbortUpdate(signal, lliPtr.p, regOpPtr.p);
+ }//if
+ } else if (regOpPtr.p->optype == ZINSERT) {
+ ljam();
+ cprAddUndoLogRecord(signal,
+ ZLCPR_ABORT_INSERT,
+ regOpPtr.p->fragPageId,
+ regOpPtr.p->pageIndex,
+ regOpPtr.p->tableRef,
+ regOpPtr.p->fragId,
+ regFragPtr.p->checkpointVersion);
+ } else {
+ ndbrequire(regOpPtr.p->optype == ZDELETE);
+ ljam();
+ cprAddUndoLogRecord(signal,
+ ZINDICATE_NO_OP_ACTIVE,
+ regOpPtr.p->fragPageId,
+ regOpPtr.p->pageIndex,
+ regOpPtr.p->tableRef,
+ regOpPtr.p->fragId,
+ regFragPtr.p->checkpointVersion);
+ }//if
+ }//if
+ loopCount++;;
+ regOpPtr.i = regOpPtr.p->nextOprecInList;
+ }//while
+ if (regOpPtr.i == RNIL) {
+ ljam();
+
+ signal->theData[0] = ciPtr.p->lcpUserptr;
+ sendSignal(ciPtr.p->lcpBlockref, GSN_TUP_LCPSTARTED, signal, 1, JBA);
+
+ signal->theData[0] = ZCONT_SAVE_DP;
+ signal->theData[1] = ciPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ } else {
+ ljam();
+ ciPtr.p->lcpTmpOperPtr = regOpPtr.i;
+ signal->theData[0] = ZCONT_START_SAVE_CL;
+ signal->theData[1] = ciPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+}//Dbtup::lcpSaveCopyListLab()
+
+/* ---------------------------------------------------------------- */
+/* ------- PERFORM A COPY OF ONE DATAPAGE DURING CHECKPOINT ------- */
+/* ---------------------------------------------------------------- */
+/* THE RANGE OF DATA PAGES IS INCLUDED IN THE CHECKPOINT_INFO_PTR */
+/* LAST_PAGE_TO_BUFFER ELEMENT IS INCREASED UNTIL ALL PAGES ARE */
+/* COPIED TO THE DISK BUFFER. WHEN A DISK BUFFER SEGMENT IS FULL */
+/* IT WILL BE WRITTEN TO DISK (TYPICALLY EACH 8:TH PAGE) */
+/* ---------------------------------------------------------------- */
+void Dbtup::lcpSaveDataPageLab(Signal* signal, Uint32 ciIndex)
+{
+ CheckpointInfoPtr ciPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ FragrecordPtr regFragPtr;
+ LocalLogInfoPtr lliPtr;
+ UndoPagePtr undoCopyPagePtr;
+ PagePtr pagePtr;
+
+ ciPtr.i = ciIndex;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+ if (ERROR_INSERTED(4000)){
+ if (ciPtr.p->lcpTabPtr == c_errorInsert4000TableId) {
+ // Delay writing of data pages during LCP
+ ndbout << "Delay writing of data pages during LCP" << endl;
+ signal->theData[0] = ZCONT_SAVE_DP;
+ signal->theData[1] = ciIndex;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 1000, 2);
+ return;
+ }//if
+ }//if
+ if (clblPageCounter == 0) {
+ ljam();
+ signal->theData[0] = ZCONT_SAVE_DP;
+ signal->theData[1] = ciPtr.i;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 100, 2);
+ return;
+ } else {
+ ljam();
+ clblPageCounter--;
+ }//if
+
+ regFragPtr.i = ciPtr.p->lcpFragmentP;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+ dbsiPtr.i = ciPtr.p->lcpDataBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+
+ pagePtr.i = getRealpid(regFragPtr.p, regFragPtr.p->minPageNotWrittenInCheckpoint);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ ndbrequire(dbsiPtr.p->pdxNumDataPages < 16);
+ undoCopyPagePtr.i = dbsiPtr.p->pdxDataPage[dbsiPtr.p->pdxNumDataPages];
+ ptrCheckGuard(undoCopyPagePtr, cnoOfUndoPage, undoPage);
+ MEMCOPY_NO_WORDS(&undoCopyPagePtr.p->undoPageWord[0],
+ &pagePtr.p->pageWord[0],
+ ZWORDS_ON_PAGE);
+ regFragPtr.p->minPageNotWrittenInCheckpoint++;
+ dbsiPtr.p->pdxNumDataPages++;
+ if (regFragPtr.p->minPageNotWrittenInCheckpoint == regFragPtr.p->maxPageWrittenInCheckpoint) {
+ /* ---------------------------------------------------------- */
+ /* ALL PAGES ARE COPIED, TIME TO FINISH THE CHECKPOINT */
+ /* SAVE THE END POSITIONS OF THE LOG RECORDS SINCE ALL DATA */
+ /* PAGES ARE NOW SAFE ON DISK AND NO MORE LOGGING WILL APPEAR */
+ /* ---------------------------------------------------------- */
+ ljam();
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ regFragPtr.p->checkpointVersion = RNIL; /* UNDO LOGGING IS SHUT OFF */
+ lcpWriteListDataPageSegment(signal, dbsiPtr, ciPtr, false);
+ dbsiPtr.p->pdxOperation = CHECKPOINT_DATA_WRITE_LAST;
+ } else if (dbsiPtr.p->pdxNumDataPages == ZDB_SEGMENT_SIZE) {
+ ljam();
+ lcpWriteListDataPageSegment(signal, dbsiPtr, ciPtr, false);
+ dbsiPtr.p->pdxOperation = CHECKPOINT_DATA_WRITE;
+ } else {
+ ljam();
+ signal->theData[0] = ZCONT_SAVE_DP;
+ signal->theData[1] = ciPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+}//Dbtup::lcpSaveDataPageLab()
+
+void Dbtup::lcpWriteListDataPageSegment(Signal* signal,
+ DiskBufferSegmentInfoPtr dbsiPtr,
+ CheckpointInfoPtr ciPtr,
+ bool flushFlag)
+{
+ Uint32 flags = 1;
+ cnoOfDataPagesToDiskWithoutSynch += dbsiPtr.p->pdxNumDataPages;
+ if ((cnoOfDataPagesToDiskWithoutSynch > MAX_PAGES_WITHOUT_SYNCH) ||
+ (flushFlag)) {
+ ljam();
+/* ---------------------------------------------------------------- */
+// To avoid synching too big chunks at a time we synch after writing
+// a certain number of data pages. (e.g. 2 MBytes).
+/* ---------------------------------------------------------------- */
+ cnoOfDataPagesToDiskWithoutSynch = 0;
+ flags |= 0x10; //Set synch flag unconditionally
+ }//if
+ signal->theData[0] = ciPtr.p->lcpDataFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = dbsiPtr.i;
+ signal->theData[3] = flags;
+ signal->theData[4] = ZBASE_ADDR_UNDO_WORD;
+ signal->theData[5] = dbsiPtr.p->pdxNumDataPages;
+ signal->theData[6] = dbsiPtr.p->pdxDataPage[0];
+ signal->theData[7] = dbsiPtr.p->pdxFilePage;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+
+ dbsiPtr.p->pdxFilePage += dbsiPtr.p->pdxNumDataPages;
+ dbsiPtr.p->pdxNumDataPages = 0;
+}//Dbtup::lcpWriteListDataPageSegment()
+
+void Dbtup::lcpFlushLogLab(Signal* signal, CheckpointInfoPtr ciPtr)
+{
+ DiskBufferSegmentInfoPtr oldDbsiPtr;
+ LocalLogInfoPtr lliPtr;
+ UndoPagePtr oldUndoPagePtr;
+ UndoPagePtr newUndoPagePtr;
+
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ oldDbsiPtr.i = lliPtr.p->lliUndoBufferSegmentP;
+ ptrCheckGuard(oldDbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ oldDbsiPtr.p->pdxNumDataPages++;
+ if (clblPageCounter > 0) {
+ ljam();
+ clblPageCounter--;
+ }//if
+ oldUndoPagePtr.i = lliPtr.p->lliUndoPage;
+ ptrCheckGuard(oldUndoPagePtr, cnoOfUndoPage, undoPage);
+ lcpWriteUndoSegment(signal, lliPtr.p, true);
+ oldDbsiPtr.p->pdxOperation = CHECKPOINT_UNDO_WRITE_FLUSH;
+ oldDbsiPtr.p->pdxCheckpointInfoP = ciPtr.i;
+
+/* ---------------------------------------------------------------- */
+/* SINCE LAST PAGE SENT TO DISK WAS NOT FULL YET WE COPY IT */
+/* TO THE NEW LAST PAGE. */
+/* ---------------------------------------------------------------- */
+ newUndoPagePtr.i = lliPtr.p->lliUndoPage;
+ ptrCheckGuard(newUndoPagePtr, cnoOfUndoPage, undoPage);
+ ndbrequire(lliPtr.p->lliUndoWord < ZWORDS_ON_PAGE);
+ MEMCOPY_NO_WORDS(&newUndoPagePtr.p->undoPageWord[0],
+ &oldUndoPagePtr.p->undoPageWord[0],
+ lliPtr.p->lliUndoWord);
+}//Dbtup::lcpFlushLogLab()
+
+void Dbtup::lcpFlushRestartInfoLab(Signal* signal, Uint32 ciIndex)
+{
+ CheckpointInfoPtr ciPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ LocalLogInfoPtr lliPtr;
+ UndoPagePtr undoCopyPagePtr;
+
+ ciPtr.i = ciIndex;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+
+ lliPtr.i = ciPtr.p->lcpLocalLogInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ dbsiPtr.i = ciPtr.p->lcpDataBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ undoCopyPagePtr.i = dbsiPtr.p->pdxDataPage[0]; /* UNDO INFO STORED AT PAGE 0 */
+ ptrCheckGuard(undoCopyPagePtr, cnoOfUndoPage, undoPage);
+ ndbrequire(ciPtr.p->lcpNoOfPages > 0);
+ undoCopyPagePtr.p->undoPageWord[ZSRI_NO_OF_FRAG_PAGES_POS] = ciPtr.p->lcpNoOfPages;
+ undoCopyPagePtr.p->undoPageWord[ZSRI_NO_COPY_PAGES_ALLOC] = ciPtr.p->lcpNoCopyPagesAlloc;
+ undoCopyPagePtr.p->undoPageWord[ZSRI_EMPTY_PRIM_PAGE] = ciPtr.p->lcpEmptyPrimPage;
+ undoCopyPagePtr.p->undoPageWord[ZSRI_TH_FREE_FIRST] = ciPtr.p->lcpThFreeFirst;
+ undoCopyPagePtr.p->undoPageWord[ZSRI_TH_FREE_COPY_FIRST] = ciPtr.p->lcpThFreeCopyFirst;
+ undoCopyPagePtr.p->undoPageWord[ZSRI_UNDO_LOG_END_REC_ID] = lliPtr.p->lliPrevRecordId;
+ undoCopyPagePtr.p->undoPageWord[ZSRI_UNDO_FILE_VER] = cundoFileVersion;
+ if (lliPtr.p->lliUndoWord == ZUNDO_PAGE_HEADER_SIZE) {
+ ljam();
+ undoCopyPagePtr.p->undoPageWord[ZSRI_UNDO_LOG_END_PAGE_ID] = lliPtr.p->lliLogFilePage - 1;
+ } else {
+ ljam();
+ undoCopyPagePtr.p->undoPageWord[ZSRI_UNDO_LOG_END_PAGE_ID] = lliPtr.p->lliLogFilePage;
+ }//if
+ dbsiPtr.p->pdxNumDataPages = 1;
+ dbsiPtr.p->pdxFilePage = 0;
+ if (clblPageCounter > 0) {
+ ljam();
+ clblPageCounter--;
+ }//if
+ lcpWriteListDataPageSegment(signal, dbsiPtr, ciPtr, true);
+ dbsiPtr.p->pdxOperation = CHECKPOINT_DATA_WRITE_FLUSH;
+ return;
+}//Dbtup::lcpFlushRestartInfoLab()
+
+void Dbtup::lcpCompletedLab(Signal* signal, Uint32 ciIndex)
+{
+ CheckpointInfoPtr ciPtr;
+ PendingFileOpenInfoPtr pfoiPtr;
+/* ---------------------------------------------------------------------- */
+/* INSERT CODE TO CLOSE DATA FILE HERE. DO THIS BEFORE SEND CONF */
+/* ---------------------------------------------------------------------- */
+ ciPtr.i = ciIndex;
+ ptrCheckGuard(ciPtr, cnoOfLcpRec, checkpointInfo);
+
+ seizePendingFileOpenInfoRecord(pfoiPtr);
+ pfoiPtr.p->pfoOpenType = LCP_DATA_FILE_CLOSE;
+ pfoiPtr.p->pfoCheckpointInfoP = ciPtr.i;
+
+ signal->theData[0] = ciPtr.p->lcpDataFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = pfoiPtr.i;
+ signal->theData[3] = 0;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ return;
+}//Dbtup::lcpCompletedLab()
+
+void Dbtup::lcpClosedDataFileLab(Signal* signal, CheckpointInfoPtr ciPtr)
+{
+ signal->theData[0] = ciPtr.p->lcpUserptr;
+ sendSignal(ciPtr.p->lcpBlockref, GSN_TUP_LCPCONF, signal, 1, JBB);
+ releaseCheckpointInfoRecord(ciPtr);
+ return;
+}//Dbtup::lcpClosedDataFileLab()
+
+/* ---------------------------------------------------------------------- */
+/* LCP END IS THE LAST STEP IN THE LCP PROCESS IT WILL CLOSE THE LOGFILES */
+/* AND RELEASE THE ALLOCATED CHECKPOINT_INFO_RECORDS */
+/* ---------------------------------------------------------------------- */
+void Dbtup::execEND_LCPREQ(Signal* signal)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ LocalLogInfoPtr lliPtr;
+ PendingFileOpenInfoPtr pfoiPtr;
+
+ ljamEntry();
+ clqhUserpointer = signal->theData[0];
+ clqhBlockref = signal->theData[1];
+ for (lliPtr.i = 0; lliPtr.i < 16; lliPtr.i++) {
+ ljam();
+ ptrAss(lliPtr, localLogInfo);
+ if (lliPtr.p->lliActiveLcp > 0) {
+ ljam();
+ dbsiPtr.i = lliPtr.p->lliUndoBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ freeDiskBufferSegmentRecord(signal, dbsiPtr);
+
+ seizePendingFileOpenInfoRecord(pfoiPtr); /* SEIZE A NEW FILE OPEN INFO */
+ pfoiPtr.p->pfoOpenType = LCP_UNDO_FILE_CLOSE;
+ pfoiPtr.p->pfoCheckpointInfoP = lliPtr.i;
+
+ signal->theData[0] = lliPtr.p->lliUndoFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = pfoiPtr.i;
+ signal->theData[3] = 0;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ lliPtr.p->lliActiveLcp = 0;
+ }//if
+ }//for
+ return;
+}//Dbtup::execEND_LCPREQ()
+
+void Dbtup::lcpEndconfLab(Signal* signal)
+{
+ LocalLogInfoPtr lliPtr;
+ for (lliPtr.i = 0; lliPtr.i < 16; lliPtr.i++) {
+ ljam();
+ ptrAss(lliPtr, localLogInfo);
+ if (lliPtr.p->lliUndoFileHandle != RNIL) {
+ ljam();
+/* ---------------------------------------------------------------------- */
+/* WAIT UNTIL ALL LOG FILES HAVE BEEN CLOSED. */
+/* ---------------------------------------------------------------------- */
+ return;
+ }//if
+ }//for
+ signal->theData[0] = clqhUserpointer;
+ sendSignal(clqhBlockref, GSN_END_LCPCONF, signal, 1, JBB);
+ return;
+}//Dbtup::lcpEndconfLab()
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
new file mode 100644
index 00000000000..4ce807528c4
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
@@ -0,0 +1,711 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <signaldata/TupFrag.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRemoveReq.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/AlterTab.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <my_sys.h>
+
+#define ljam() { jamLine(20000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(20000 + __LINE__); }
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/* --------------- ADD/DROP FRAGMENT TABLE MODULE ----------------- */
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execTUPFRAGREQ(Signal* signal)
+{
+ ljamEntry();
+
+ if (signal->theData[0] == (Uint32)-1) {
+ ljam();
+ abortAddFragOp(signal);
+ return;
+ }
+
+ FragoperrecPtr fragOperPtr;
+ FragrecordPtr regFragPtr;
+ TablerecPtr regTabPtr;
+
+ Uint32 userptr = signal->theData[0];
+ Uint32 userblockref = signal->theData[1];
+ Uint32 reqinfo = signal->theData[2];
+ regTabPtr.i = signal->theData[3];
+ Uint32 noOfAttributes = signal->theData[4];
+ Uint32 fragId = signal->theData[5];
+ Uint32 noOfNullAttr = signal->theData[7];
+ /* Uint32 schemaVersion = signal->theData[8];*/
+ Uint32 noOfKeyAttr = signal->theData[9];
+
+ Uint32 noOfNewAttr = (signal->theData[10] & 0xFFFF);
+ /* DICT sends number of character sets in upper half */
+ Uint32 noOfCharsets = (signal->theData[10] >> 16);
+
+ Uint32 checksumIndicator = signal->theData[11];
+ Uint32 noOfAttributeGroups = signal->theData[12];
+ Uint32 globalCheckpointIdIndicator = signal->theData[13];
+
+#ifndef VM_TRACE
+ // config mismatch - do not crash if release compiled
+ if (regTabPtr.i >= cnoOfTablerec) {
+ ljam();
+ signal->theData[0] = userptr;
+ signal->theData[1] = 800;
+ sendSignal(userblockref, GSN_TUPFRAGREF, signal, 2, JBB);
+ return;
+ }
+#endif
+
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+ if (cfirstfreeFragopr == RNIL) {
+ ljam();
+ signal->theData[0] = userptr;
+ signal->theData[1] = ZNOFREE_FRAGOP_ERROR;
+ sendSignal(userblockref, GSN_TUPFRAGREF, signal, 2, JBB);
+ return;
+ }//if
+ seizeFragoperrec(fragOperPtr);
+
+ fragOperPtr.p->nextFragoprec = RNIL;
+ fragOperPtr.p->lqhBlockrefFrag = userblockref;
+ fragOperPtr.p->lqhPtrFrag = userptr;
+ fragOperPtr.p->fragidFrag = fragId;
+ fragOperPtr.p->tableidFrag = regTabPtr.i;
+ fragOperPtr.p->attributeCount = noOfAttributes;
+ fragOperPtr.p->noOfNullBits = noOfNullAttr;
+ fragOperPtr.p->noOfNewAttrCount = noOfNewAttr;
+ fragOperPtr.p->charsetIndex = 0;
+ fragOperPtr.p->currNullBit = 0;
+
+ ndbrequire(reqinfo == ZADDFRAG);
+
+ getFragmentrec(regFragPtr, fragId, regTabPtr.p);
+ if (regFragPtr.i != RNIL) {
+ ljam();
+ terrorCode = ZEXIST_FRAG_ERROR; /* THE FRAGMENT ALREADY EXIST */
+ fragrefuse1Lab(signal, fragOperPtr);
+ return;
+ }//if
+ if (cfirstfreefrag != RNIL) {
+ ljam();
+ seizeFragrecord(regFragPtr);
+ } else {
+ ljam();
+ terrorCode = ZFULL_FRAGRECORD_ERROR;
+ fragrefuse1Lab(signal, fragOperPtr);
+ return;
+ }//if
+ initFragRange(regFragPtr.p);
+ if (!addfragtotab(regTabPtr.p, fragId, regFragPtr.i)) {
+ ljam();
+ terrorCode = ZNO_FREE_TAB_ENTRY_ERROR;
+ fragrefuse2Lab(signal, fragOperPtr, regFragPtr);
+ return;
+ }//if
+ if (cfirstfreerange == RNIL) {
+ ljam();
+ terrorCode = ZNO_FREE_PAGE_RANGE_ERROR;
+ fragrefuse3Lab(signal, fragOperPtr, regFragPtr, regTabPtr.p, fragId);
+ return;
+ }//if
+
+ regFragPtr.p->emptyPrimPage = RNIL;
+ regFragPtr.p->thFreeFirst = RNIL;
+ regFragPtr.p->thFreeCopyFirst = RNIL;
+ regFragPtr.p->noCopyPagesAlloc = 0;
+ regFragPtr.p->fragTableId = regTabPtr.i;
+ regFragPtr.p->fragmentId = fragId;
+ regFragPtr.p->checkpointVersion = RNIL;
+
+ Uint32 noAllocatedPages = 2;
+ noAllocatedPages = allocFragPages(regFragPtr.p, noAllocatedPages);
+
+ if (noAllocatedPages == 0) {
+ ljam();
+ terrorCode = ZNO_PAGES_ALLOCATED_ERROR;
+ fragrefuse3Lab(signal, fragOperPtr, regFragPtr, regTabPtr.p, fragId);
+ return;
+ }//if
+
+ if (ERROR_INSERTED(4007) && regTabPtr.p->fragid[0] == fragId ||
+ ERROR_INSERTED(4008) && regTabPtr.p->fragid[1] == fragId) {
+ ljam();
+ terrorCode = 1;
+ fragrefuse4Lab(signal, fragOperPtr, regFragPtr, regTabPtr.p, fragId);
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }
+
+ if (regTabPtr.p->tableStatus == NOT_DEFINED) {
+ ljam();
+//-------------------------------------------------------------------------------------
+// We are setting up references to the header of the tuple.
+// Active operation This word contains a reference to the operation active on the tuple
+// at the moment. RNIL means no one active at all. Not optional.
+// Tuple version Uses only low 16 bits. Not optional.
+// Checksum The third header word is optional and contains a checksum of the
+// tuple header.
+// Null-bits A number of words to contain null bits for all non-dynamic attributes.
+// Each word contains upto 32 null bits. Each time a new word is needed
+// we allocate the complete word. Zero nullable attributes means that
+// there is no word at all
+// Global Checkpoint id
+// This word is optional. When used it is stored as a 32-bit unsigned
+// attribute with attribute identity 0. Thus the kernel assumes that
+// this is the first word after the header.
+//-------------------------------------------------------------------------------------
+ fragOperPtr.p->definingFragment = true;
+ regTabPtr.p->tableStatus = DEFINING;
+ regTabPtr.p->checksumIndicator = (checksumIndicator != 0 ? true : false);
+ regTabPtr.p->GCPIndicator = (globalCheckpointIdIndicator != 0 ? true : false);
+
+ regTabPtr.p->tupChecksumIndex = 2;
+ regTabPtr.p->tupNullIndex = 2 + (checksumIndicator != 0 ? 1 : 0);
+ regTabPtr.p->tupNullWords = (noOfNullAttr + 31) >> 5;
+ regTabPtr.p->tupGCPIndex = regTabPtr.p->tupNullIndex + regTabPtr.p->tupNullWords;
+ regTabPtr.p->tupheadsize = regTabPtr.p->tupGCPIndex;
+
+ regTabPtr.p->noOfKeyAttr = noOfKeyAttr;
+ regTabPtr.p->noOfCharsets = noOfCharsets;
+ regTabPtr.p->noOfAttr = noOfAttributes;
+ regTabPtr.p->noOfNewAttr = noOfNewAttr;
+ regTabPtr.p->noOfNullAttr = noOfNullAttr;
+ regTabPtr.p->noOfAttributeGroups = noOfAttributeGroups;
+
+ regTabPtr.p->notNullAttributeMask.clear();
+
+ Uint32 offset[10];
+ Uint32 tableDescriptorRef = allocTabDescr(regTabPtr.p, offset);
+ if (tableDescriptorRef == RNIL) {
+ ljam();
+ fragrefuse4Lab(signal, fragOperPtr, regFragPtr, regTabPtr.p, fragId);
+ return;
+ }//if
+ setUpDescriptorReferences(tableDescriptorRef, regTabPtr.p, offset);
+ } else {
+ ljam();
+ fragOperPtr.p->definingFragment = false;
+ }//if
+ signal->theData[0] = fragOperPtr.p->lqhPtrFrag;
+ signal->theData[1] = fragOperPtr.i;
+ signal->theData[2] = regFragPtr.i;
+ signal->theData[3] = fragId;
+ sendSignal(fragOperPtr.p->lqhBlockrefFrag, GSN_TUPFRAGCONF, signal, 4, JBB);
+ return;
+}//Dbtup::execTUPFRAGREQ()
+
+/* -------------------------------------------------------------------- */
+/* ------------------------- ADDFRAGTOTAB ----------------------------- */
+/* PUTS A FRAGMENT POINTER AND FID IN THE TABLE ARRAY OF THE TID RECORD */
+/* -------------------------------------------------------------------- */
+bool Dbtup::addfragtotab(Tablerec* const regTabPtr, Uint32 fragId, Uint32 fragIndex)
+{
+ for (Uint32 i = 0; i < (2 * MAX_FRAG_PER_NODE); i++) {
+ ljam();
+ if (regTabPtr->fragid[i] == RNIL) {
+ ljam();
+ regTabPtr->fragid[i] = fragId;
+ regTabPtr->fragrec[i] = fragIndex;
+ return true;
+ }//if
+ }//for
+ return false;
+}//Dbtup::addfragtotab()
+
+void Dbtup::getFragmentrec(FragrecordPtr& regFragPtr, Uint32 fragId, Tablerec* const regTabPtr)
+{
+ for (Uint32 i = 0; i < (2 * MAX_FRAG_PER_NODE); i++) {
+ ljam();
+ if (regTabPtr->fragid[i] == fragId) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* A FRAGMENT RECORD HAVE BEEN FOUND FOR THIS OPERATION. */
+/* ---------------------------------------------------------------- */
+ regFragPtr.i = regTabPtr->fragrec[i];
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+ return;
+ }//if
+ }//for
+ regFragPtr.i = RNIL;
+ ptrNull(regFragPtr);
+}//Dbtup::getFragmentrec()
+
+void Dbtup::seizeFragrecord(FragrecordPtr& regFragPtr)
+{
+ regFragPtr.i = cfirstfreefrag;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+ cfirstfreefrag = regFragPtr.p->nextfreefrag;
+ regFragPtr.p->nextfreefrag = RNIL;
+}//Dbtup::seizeFragrecord()
+
+/* ---------------------------------------------------------------- */
+/* SEIZE A FRAGMENT OPERATION RECORD */
+/* ---------------------------------------------------------------- */
+void Dbtup::seizeFragoperrec(FragoperrecPtr& fragOperPtr)
+{
+ fragOperPtr.i = cfirstfreeFragopr;
+ ptrCheckGuard(fragOperPtr, cnoOfFragoprec, fragoperrec);
+ cfirstfreeFragopr = fragOperPtr.p->nextFragoprec;
+ fragOperPtr.p->nextFragoprec = RNIL;
+ fragOperPtr.p->inUse = true;
+}//Dbtup::seizeFragoperrec()
+
+/* **************************************************************** */
+/* ************** TUP_ADD_ATTRREQ ****************** */
+/* **************************************************************** */
+void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal)
+{
+ FragrecordPtr regFragPtr;
+ FragoperrecPtr fragOperPtr;
+ TablerecPtr regTabPtr;
+
+ ljamEntry();
+ fragOperPtr.i = signal->theData[0];
+ ptrCheckGuard(fragOperPtr, cnoOfFragoprec, fragoperrec);
+ Uint32 attrId = signal->theData[2];
+ Uint32 attrDescriptor = signal->theData[3];
+ // DICT sends charset number in upper half
+ Uint32 csNumber = (signal->theData[4] >> 16);
+
+ regTabPtr.i = fragOperPtr.p->tableidFrag;
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+
+ Uint32 fragId = fragOperPtr.p->fragidFrag;
+
+ getFragmentrec(regFragPtr, fragId, regTabPtr.p);
+ ndbrequire(regFragPtr.i != RNIL);
+
+ ndbrequire(fragOperPtr.p->attributeCount > 0);
+ fragOperPtr.p->attributeCount--;
+ const bool lastAttr = (fragOperPtr.p->attributeCount == 0);
+
+ if ((regTabPtr.p->tableStatus == DEFINING) &&
+ (fragOperPtr.p->definingFragment)) {
+ ljam();
+ Uint32 firstTabDesIndex = regTabPtr.p->tabDescriptor + (attrId * ZAD_SIZE);
+ setTabDescrWord(firstTabDesIndex, attrDescriptor);
+ Uint32 attrLen = AttributeDescriptor::getSize(attrDescriptor);
+ Uint32 nullBitPos = fragOperPtr.p->currNullBit;
+ Uint32 bitCount = 0;
+
+ if (AttributeDescriptor::getNullable(attrDescriptor)) {
+ if (!AttributeDescriptor::getDynamic(attrDescriptor)) {
+ ljam(); /* NULL ATTR */
+ fragOperPtr.p->currNullBit++;
+ }//if
+ } else {
+ ljam();
+ regTabPtr.p->notNullAttributeMask.set(attrId);
+ }//if
+
+ Uint32 attrDes2 = 0;
+ if (!AttributeDescriptor::getDynamic(attrDescriptor)) {
+ ljam();
+ Uint32 attributePos = regTabPtr.p->tupheadsize;
+ switch (AttributeDescriptor::getArrayType(attrDescriptor)) {
+ case 1:
+ case 2:
+ {
+ ljam();
+ if(attrLen != 0)
+ {
+ ljam();
+ Uint32 bitsUsed =
+ AttributeDescriptor::getArraySize(attrDescriptor) * (1 << attrLen);
+ regTabPtr.p->tupheadsize += ((bitsUsed + 31) >> 5);
+ break;
+ }
+ else
+ {
+ ljam();
+ bitCount = AttributeDescriptor::getArraySize(attrDescriptor);
+ fragOperPtr.p->currNullBit += bitCount;
+ break;
+ }
+ }
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ if(nullBitPos + bitCount + 1 >= MAX_NULL_BITS)
+ {
+ terrorCode = TupAddAttrRef::TooManyBitsUsed;
+ addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId);
+ return;
+ }
+ AttributeOffset::setOffset(attrDes2, attributePos);
+ AttributeOffset::setNullFlagPos(attrDes2, nullBitPos);
+ } else {
+ ndbrequire(false);
+ }//if
+ if (csNumber != 0) {
+ CHARSET_INFO* cs = all_charsets[csNumber];
+ ndbrequire(cs != NULL);
+ Uint32 i = 0;
+ while (i < fragOperPtr.p->charsetIndex) {
+ ljam();
+ if (regTabPtr.p->charsetArray[i] == cs)
+ break;
+ i++;
+ }
+ if (i == fragOperPtr.p->charsetIndex) {
+ ljam();
+ fragOperPtr.p->charsetIndex++;
+ }
+ ndbrequire(i < regTabPtr.p->noOfCharsets);
+ regTabPtr.p->charsetArray[i] = cs;
+ AttributeOffset::setCharsetPos(attrDes2, i);
+ }
+ setTabDescrWord(firstTabDesIndex + 1, attrDes2);
+
+ if (regTabPtr.p->tupheadsize > MAX_TUPLE_SIZE_IN_WORDS) {
+ ljam();
+ terrorCode = ZTOO_LARGE_TUPLE_ERROR;
+ addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId);
+ return;
+ }//if
+ if (lastAttr &&
+ (fragOperPtr.p->currNullBit != fragOperPtr.p->noOfNullBits))
+ {
+ ljam();
+ terrorCode = ZINCONSISTENT_NULL_ATTRIBUTE_COUNT;
+ addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId);
+ return;
+ }//if
+ }//if
+ if (ERROR_INSERTED(4009) && regTabPtr.p->fragid[0] == fragId && attrId == 0 ||
+ ERROR_INSERTED(4010) && regTabPtr.p->fragid[0] == fragId && lastAttr ||
+ ERROR_INSERTED(4011) && regTabPtr.p->fragid[1] == fragId && attrId == 0 ||
+ ERROR_INSERTED(4012) && regTabPtr.p->fragid[1] == fragId && lastAttr) {
+ ljam();
+ terrorCode = 1;
+ addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId);
+ CLEAR_ERROR_INSERT_VALUE;
+ return;
+ }
+/* **************************************************************** */
+/* ************** TUP_ADD_ATTCONF ****************** */
+/* **************************************************************** */
+ signal->theData[0] = fragOperPtr.p->lqhPtrFrag;
+ signal->theData[1] = lastAttr;
+ sendSignal(fragOperPtr.p->lqhBlockrefFrag, GSN_TUP_ADD_ATTCONF, signal, 2, JBB);
+ if (! lastAttr) {
+ ljam();
+ return; /* EXIT AND WAIT FOR MORE */
+ }//if
+ regFragPtr.p->fragStatus = ACTIVE;
+ if (regTabPtr.p->tableStatus == DEFINING) {
+ ljam();
+ setUpQueryRoutines(regTabPtr.p);
+ setUpKeyArray(regTabPtr.p);
+ regTabPtr.p->tableStatus = DEFINED;
+ }//if
+ releaseFragoperrec(fragOperPtr);
+ return;
+}//Dbtup::execTUP_ADD_ATTRREQ()
+
+/*
+ * Descriptor has these parts:
+ *
+ * 0 readFunctionArray ( one for each attribute )
+ * 1 updateFunctionArray ( ditto )
+ * 2 charsetArray ( pointers to distinct CHARSET_INFO )
+ * 3 readKeyArray ( attribute ids of keys )
+ * 4 attributeGroupDescriptor ( currently size 1 but unused )
+ * 5 tabDescriptor ( attribute descriptors, each ZAD_SIZE )
+ */
+
+void Dbtup::setUpDescriptorReferences(Uint32 descriptorReference,
+ Tablerec* const regTabPtr,
+ const Uint32* offset)
+{
+ Uint32* desc = &tableDescriptor[descriptorReference].tabDescr;
+ regTabPtr->readFunctionArray = (ReadFunction*)(desc + offset[0]);
+ regTabPtr->updateFunctionArray = (UpdateFunction*)(desc + offset[1]);
+ regTabPtr->charsetArray = (CHARSET_INFO**)(desc + offset[2]);
+ regTabPtr->readKeyArray = descriptorReference + offset[3];
+ regTabPtr->attributeGroupDescriptor = descriptorReference + offset[4];
+ regTabPtr->tabDescriptor = descriptorReference + offset[5];
+}//Dbtup::setUpDescriptorReferences()
+
+Uint32
+Dbtup::sizeOfReadFunction()
+{
+ ReadFunction* tmp = (ReadFunction*)&tableDescriptor[0];
+ TableDescriptor* start = &tableDescriptor[0];
+ TableDescriptor * end = (TableDescriptor*)(tmp + 1);
+ return (Uint32)(end - start);
+}//Dbtup::sizeOfReadFunction()
+
+void Dbtup::setUpKeyArray(Tablerec* const regTabPtr)
+{
+ ndbrequire((regTabPtr->readKeyArray + regTabPtr->noOfKeyAttr) < cnoOfTabDescrRec);
+ Uint32* keyArray = &tableDescriptor[regTabPtr->readKeyArray].tabDescr;
+ Uint32 countKeyAttr = 0;
+ for (Uint32 i = 0; i < regTabPtr->noOfAttr; i++) {
+ ljam();
+ Uint32 refAttr = regTabPtr->tabDescriptor + (i * ZAD_SIZE);
+ Uint32 attrDescriptor = getTabDescrWord(refAttr);
+ if (AttributeDescriptor::getPrimaryKey(attrDescriptor)) {
+ ljam();
+ AttributeHeader::init(&keyArray[countKeyAttr], i, 0);
+ countKeyAttr++;
+ }//if
+ }//for
+ ndbrequire(countKeyAttr == regTabPtr->noOfKeyAttr);
+}//Dbtup::setUpKeyArray()
+
+void Dbtup::addattrrefuseLab(Signal* signal,
+ FragrecordPtr regFragPtr,
+ FragoperrecPtr fragOperPtr,
+ Tablerec* const regTabPtr,
+ Uint32 fragId)
+{
+ releaseFragPages(regFragPtr.p);
+ deleteFragTab(regTabPtr, fragId);
+ releaseFragrec(regFragPtr);
+ releaseTabDescr(regTabPtr);
+ initTab(regTabPtr);
+
+ signal->theData[0] = fragOperPtr.p->lqhPtrFrag;
+ signal->theData[1] = terrorCode;
+ sendSignal(fragOperPtr.p->lqhBlockrefFrag, GSN_TUP_ADD_ATTRREF, signal, 2, JBB);
+ releaseFragoperrec(fragOperPtr);
+ return;
+}//Dbtup::addattrrefuseLab()
+
+void Dbtup::fragrefuse4Lab(Signal* signal,
+ FragoperrecPtr fragOperPtr,
+ FragrecordPtr regFragPtr,
+ Tablerec* const regTabPtr,
+ Uint32 fragId)
+{
+ releaseFragPages(regFragPtr.p);
+ fragrefuse3Lab(signal, fragOperPtr, regFragPtr, regTabPtr, fragId);
+ initTab(regTabPtr);
+ return;
+}//Dbtup::fragrefuse4Lab()
+
+void Dbtup::fragrefuse3Lab(Signal* signal,
+ FragoperrecPtr fragOperPtr,
+ FragrecordPtr regFragPtr,
+ Tablerec* const regTabPtr,
+ Uint32 fragId)
+{
+ fragrefuse2Lab(signal, fragOperPtr, regFragPtr);
+ deleteFragTab(regTabPtr, fragId);
+ return;
+}//Dbtup::fragrefuse3Lab()
+
+void Dbtup::fragrefuse2Lab(Signal* signal, FragoperrecPtr fragOperPtr, FragrecordPtr regFragPtr)
+{
+ fragrefuse1Lab(signal, fragOperPtr);
+ releaseFragrec(regFragPtr);
+ return;
+}//Dbtup::fragrefuse2Lab()
+
+void Dbtup::fragrefuse1Lab(Signal* signal, FragoperrecPtr fragOperPtr)
+{
+ fragrefuseLab(signal, fragOperPtr);
+ releaseFragoperrec(fragOperPtr);
+ return;
+}//Dbtup::fragrefuse1Lab()
+
+void Dbtup::fragrefuseLab(Signal* signal, FragoperrecPtr fragOperPtr)
+{
+ signal->theData[0] = fragOperPtr.p->lqhPtrFrag;
+ signal->theData[1] = terrorCode;
+ sendSignal(fragOperPtr.p->lqhBlockrefFrag, GSN_TUPFRAGREF, signal, 2, JBB);
+ return;
+}//Dbtup::fragrefuseLab()
+
+void Dbtup::releaseFragoperrec(FragoperrecPtr fragOperPtr)
+{
+ fragOperPtr.p->inUse = false;
+ fragOperPtr.p->nextFragoprec = cfirstfreeFragopr;
+ cfirstfreeFragopr = fragOperPtr.i;
+}//Dbtup::releaseFragoperrec()
+
+void Dbtup::deleteFragTab(Tablerec* const regTabPtr, Uint32 fragId)
+{
+ for (Uint32 i = 0; i < (2 * MAX_FRAG_PER_NODE); i++) {
+ ljam();
+ if (regTabPtr->fragid[i] == fragId) {
+ ljam();
+ regTabPtr->fragid[i] = RNIL;
+ regTabPtr->fragrec[i] = RNIL;
+ return;
+ }//if
+ }//for
+ ndbrequire(false);
+}//Dbtup::deleteFragTab()
+
+/*
+ * LQH aborts on-going create table operation. The table is later
+ * dropped by DICT.
+ */
+void Dbtup::abortAddFragOp(Signal* signal)
+{
+ FragoperrecPtr fragOperPtr;
+
+ fragOperPtr.i = signal->theData[1];
+ ptrCheckGuard(fragOperPtr, cnoOfFragoprec, fragoperrec);
+ ndbrequire(fragOperPtr.p->inUse);
+ releaseFragoperrec(fragOperPtr);
+}
+
+void
+Dbtup::execDROP_TAB_REQ(Signal* signal)
+{
+ ljamEntry();
+ DropTabReq* req = (DropTabReq*)signal->getDataPtr();
+
+ TablerecPtr tabPtr;
+ tabPtr.i = req->tableId;
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+ tabPtr.p->m_dropTable.tabUserRef = req->senderRef;
+ tabPtr.p->m_dropTable.tabUserPtr = req->senderData;
+
+ signal->theData[0] = ZREL_FRAG;
+ signal->theData[1] = tabPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+}//Dbtup::execDROP_TAB_REQ()
+
+void Dbtup::releaseTabDescr(Tablerec* const regTabPtr)
+{
+ Uint32 descriptor = regTabPtr->readKeyArray;
+ if (descriptor != RNIL) {
+ ljam();
+ Uint32 offset[10];
+ getTabDescrOffsets(regTabPtr, offset);
+
+ regTabPtr->tabDescriptor = RNIL;
+ regTabPtr->readKeyArray = RNIL;
+ regTabPtr->readFunctionArray = NULL;
+ regTabPtr->updateFunctionArray = NULL;
+ regTabPtr->charsetArray = NULL;
+ regTabPtr->attributeGroupDescriptor= RNIL;
+
+ // move to start of descriptor
+ descriptor -= offset[3];
+ Uint32 retNo = getTabDescrWord(descriptor + ZTD_DATASIZE);
+ ndbrequire(getTabDescrWord(descriptor + ZTD_HEADER) == ZTD_TYPE_NORMAL);
+ ndbrequire(retNo == getTabDescrWord((descriptor + retNo) - ZTD_TR_SIZE));
+ ndbrequire(ZTD_TYPE_NORMAL == getTabDescrWord((descriptor + retNo) - ZTD_TR_TYPE));
+ freeTabDescr(descriptor, retNo);
+ }//if
+}//Dbtup::releaseTabDescr()
+
+void Dbtup::releaseFragment(Signal* signal, Uint32 tableId)
+{
+ TablerecPtr tabPtr;
+ tabPtr.i = tableId;
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+ Uint32 fragIndex = RNIL;
+ Uint32 fragId = RNIL;
+ Uint32 i = 0;
+ for (i = 0; i < (2 * MAX_FRAG_PER_NODE); i++) {
+ ljam();
+ if (tabPtr.p->fragid[i] != RNIL) {
+ ljam();
+ fragIndex = tabPtr.p->fragrec[i];
+ fragId = tabPtr.p->fragid[i];
+ break;
+ }//if
+ }//for
+ if (fragIndex != RNIL) {
+ ljam();
+
+ FragrecordPtr regFragPtr;
+ regFragPtr.i = fragIndex;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+ releaseFragPages(regFragPtr.p);
+
+ tabPtr.p->fragid[i] = RNIL;
+ tabPtr.p->fragrec[i] = RNIL;
+ releaseFragrec(regFragPtr);
+
+ signal->theData[0] = ZREL_FRAG;
+ signal->theData[1] = tableId;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+
+ /**
+ * Finished...
+ */
+ sendFSREMOVEREQ(signal, tabPtr);
+}//Dbtup::releaseFragment()
+
+void Dbtup::sendFSREMOVEREQ(Signal* signal, TablerecPtr tabPtr)
+{
+ FsRemoveReq * const fsReq = (FsRemoveReq *)signal->getDataPtrSend();
+ fsReq->userReference = cownref;
+ fsReq->userPointer = tabPtr.i;
+ fsReq->fileNumber[0] = tabPtr.i;
+ fsReq->fileNumber[1] = (Uint32)-1; // Remove all fragments
+ fsReq->fileNumber[2] = (Uint32)-1; // Remove all data files within fragment
+ fsReq->fileNumber[3] = 255 | // No P-value used here
+ (5 << 8) | // Data-files in D5
+ (0 << 16) | // Data-files
+ (1 << 24); // Version 1 of fileNumber
+
+ fsReq->directory = 1;
+ fsReq->ownDirectory = 1;
+ sendSignal(NDBFS_REF, GSN_FSREMOVEREQ, signal,
+ FsRemoveReq::SignalLength, JBA);
+}//Dbtup::sendFSREMOVEREQ()
+
+void Dbtup::execFSREMOVECONF(Signal* signal)
+{
+ ljamEntry();
+
+ FsConf * const fsConf = (FsConf *)signal->getDataPtrSend();
+ TablerecPtr tabPtr;
+ tabPtr.i = fsConf->userPointer;
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+
+ DropTabConf * const dropConf = (DropTabConf *)signal->getDataPtrSend();
+ dropConf->senderRef = reference();
+ dropConf->senderData = tabPtr.p->m_dropTable.tabUserPtr;
+ dropConf->tableId = tabPtr.i;
+ sendSignal(tabPtr.p->m_dropTable.tabUserRef, GSN_DROP_TAB_CONF,
+ signal, DropTabConf::SignalLength, JBB);
+
+ releaseTabDescr(tabPtr.p);
+ initTab(tabPtr.p);
+}//Dbtup::execFSREMOVECONF()
+
+void Dbtup::execFSREMOVEREF(Signal* signal)
+{
+ ljamEntry();
+ ndbrequire(false);
+}//Dbtup::execFSREMOVEREF()
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupPagMan.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupPagMan.cpp
new file mode 100644
index 00000000000..9722aa437c0
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupPagMan.cpp
@@ -0,0 +1,370 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(16000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(16000 + __LINE__); }
+
+/* ---------------------------------------------------------------- */
+// 4) Page Memory Manager (buddy algorithm)
+//
+// The following data structures in Dbtup is used by the Page Memory
+// Manager.
+//
+// cfreepageList[16]
+// Pages with a header
+//
+// The cfreepageList is 16 free lists. Free list 0 contains chunks of
+// pages with 2^0 (=1) pages in each chunk. Free list 1 chunks of 2^1
+// (=2) pages in each chunk and so forth upto free list 15 which
+// contains chunks of 2^15 (=32768) pages in each chunk.
+// The cfreepageList array contains the pointer to the first chunk
+// in each of those lists. The lists are doubly linked where the
+// first page in each chunk contains the next and previous references
+// in position ZPAGE_NEXT_CLUST_POS and ZPAGE_PREV_CLUST_POS in the
+// page header.
+// In addition the leading page and the last page in each chunk is marked
+// with a state (=ZFREE_COMMON) in position ZPAGE_STATE_POS in page
+// header. This state indicates that the page is the leading or last page
+// in a chunk of free pages. Furthermore the leading and last page is
+// also marked with a reference to the leading (=ZPAGE_FIRST_CLUST_POS)
+// and the last page (=ZPAGE_LAST_CLUST_POS) in the chunk.
+//
+// The aim of these data structures is to enable a free area handling of
+// free pages based on a buddy algorithm. When allocating pages it is
+// performed in chunks of pages and the algorithm tries to make the
+// chunks as large as possible.
+// This manager is invoked when fragments lack internal page space to
+// accomodate all the data they are requested to store. It is also
+// invoked when fragments deallocate page space back to the free area.
+//
+// The following routines are part of the external interface:
+// void
+// allocConsPages(Uint32 noOfPagesToAllocate, #In
+// Uint32& noOfPagesAllocated, #Out
+// Uint32& retPageRef) #Out
+// void
+// returnCommonArea(Uint32 retPageRef, #In
+// Uint32 retNoPages) #In
+//
+// allocConsPages tries to allocate noOfPagesToAllocate pages in one chunk.
+// If this fails it delivers a chunk as large as possible. It returns the
+// i-value of the first page in the chunk delivered, if zero pages returned
+// this i-value is undefined. It also returns the size of the chunk actually
+// delivered.
+//
+// returnCommonArea is used when somebody is returning pages to the free area.
+// It is used both from internal routines and external routines.
+//
+// The following routines are private routines used to support the
+// above external interface:
+// removeCommonArea()
+// insertCommonArea()
+// findFreeLeftNeighbours()
+// findFreeRightNeighbours()
+// Uint32
+// nextHigherTwoLog(Uint32 input)
+//
+// nextHigherTwoLog is a support routine which is a mathematical function with
+// an integer as input and an integer as output. It calculates the 2-log of
+// (input + 1). If the 2-log of (input + 1) is larger than 15 then the routine
+// will return 15. It is part of the external interface since it is also used
+// by other similar memory management algorithms.
+//
+// External dependencies:
+// None.
+//
+// Side Effects:
+// Apart from the above mentioned data structures there are no more
+// side effects other than through the subroutine parameters in the
+// external interface.
+//
+/* ---------------------------------------------------------------- */
+
+/* ---------------------------------------------------------------- */
+/* CALCULATE THE 2-LOG + 1 OF TMP AND PUT RESULT INTO TBITS */
+/* ---------------------------------------------------------------- */
+Uint32 Dbtup::nextHigherTwoLog(Uint32 input)
+{
+ input = input | (input >> 8);
+ input = input | (input >> 4);
+ input = input | (input >> 2);
+ input = input | (input >> 1);
+ Uint32 output = (input & 0x5555) + ((input >> 1) & 0x5555);
+ output = (output & 0x3333) + ((output >> 2) & 0x3333);
+ output = output + (output >> 4);
+ output = (output & 0xf) + ((output >> 8) & 0xf);
+ return output;
+}//nextHigherTwoLog()
+
+void Dbtup::initializePage()
+{
+ for (Uint32 i = 0; i < 16; i++) {
+ cfreepageList[i] = RNIL;
+ }//for
+ PagePtr pagePtr;
+ for (pagePtr.i = 0; pagePtr.i < cnoOfPage; pagePtr.i++) {
+ ljam();
+ refresh_watch_dog();
+ ptrAss(pagePtr, page);
+ pagePtr.p->pageWord[ZPAGE_PHYSICAL_INDEX] = pagePtr.i;
+ pagePtr.p->pageWord[ZPAGE_NEXT_POS] = pagePtr.i + 1;
+ pagePtr.p->pageWord[ZPAGE_NEXT_CLUST_POS] = RNIL;
+ pagePtr.p->pageWord[ZPAGE_LAST_CLUST_POS] = RNIL;
+ pagePtr.p->pageWord[ZPAGE_PREV_POS] = RNIL;
+ pagePtr.p->pageWord[ZPAGE_STATE_POS] = ZFREE_COMMON;
+ }//for
+ pagePtr.i = cnoOfPage - 1;
+ ptrAss(pagePtr, page);
+ pagePtr.p->pageWord[ZPAGE_NEXT_POS] = RNIL;
+
+ pagePtr.i = 0;
+ ptrAss(pagePtr, page);
+ pagePtr.p->pageWord[ZPAGE_STATE_POS] = ~ZFREE_COMMON;
+
+ for(size_t j = 0; j<MAX_PARALLELL_TUP_SRREQ; j++){
+ pagePtr.i = 1+j;
+ ptrAss(pagePtr, page);
+ pagePtr.p->pageWord[ZPAGE_STATE_POS] = ~ZFREE_COMMON;
+ }
+
+ Uint32 tmp = 1 + MAX_PARALLELL_TUP_SRREQ;
+ returnCommonArea(tmp, cnoOfPage - tmp);
+ cnoOfAllocatedPages = tmp; // Is updated by returnCommonArea
+ c_sr_free_page_0 = ~0;
+}//Dbtup::initializePage()
+
+void Dbtup::allocConsPages(Uint32 noOfPagesToAllocate,
+ Uint32& noOfPagesAllocated,
+ Uint32& allocPageRef)
+{
+ if (noOfPagesToAllocate == 0){
+ ljam();
+ noOfPagesAllocated = 0;
+ return;
+ }//if
+
+ Uint32 firstListToCheck = nextHigherTwoLog(noOfPagesToAllocate - 1);
+ for (Uint32 i = firstListToCheck; i < 16; i++) {
+ ljam();
+ if (cfreepageList[i] != RNIL) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* PROPER AMOUNT OF PAGES WERE FOUND. NOW SPLIT THE FOUND */
+/* AREA AND RETURN THE PART NOT NEEDED. */
+/* ---------------------------------------------------------------- */
+ noOfPagesAllocated = noOfPagesToAllocate;
+ allocPageRef = cfreepageList[i];
+ removeCommonArea(allocPageRef, i);
+ Uint32 retNo = (1 << i) - noOfPagesToAllocate;
+ Uint32 retPageRef = allocPageRef + noOfPagesToAllocate;
+ returnCommonArea(retPageRef, retNo);
+ return;
+ }//if
+ }//for
+/* ---------------------------------------------------------------- */
+/* PROPER AMOUNT OF PAGES WERE NOT FOUND. FIND AS MUCH AS */
+/* POSSIBLE. */
+/* ---------------------------------------------------------------- */
+ for (Uint32 j = firstListToCheck; (Uint32)~j; j--) {
+ ljam();
+ if (cfreepageList[j] != RNIL) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* SOME AREA WAS FOUND, ALLOCATE ALL OF IT. */
+/* ---------------------------------------------------------------- */
+ allocPageRef = cfreepageList[j];
+ removeCommonArea(allocPageRef, j);
+ noOfPagesAllocated = 1 << j;
+ findFreeLeftNeighbours(allocPageRef, noOfPagesAllocated,
+ noOfPagesToAllocate);
+ findFreeRightNeighbours(allocPageRef, noOfPagesAllocated,
+ noOfPagesToAllocate);
+
+ return;
+ }//if
+ }//for
+/* ---------------------------------------------------------------- */
+/* NO FREE AREA AT ALL EXISTED. RETURN ZERO PAGES */
+/* ---------------------------------------------------------------- */
+ noOfPagesAllocated = 0;
+ return;
+}//allocConsPages()
+
+void Dbtup::returnCommonArea(Uint32 retPageRef, Uint32 retNo)
+{
+ do {
+ ljam();
+ if (retNo == 0) {
+ ljam();
+ return;
+ }//if
+ Uint32 list = nextHigherTwoLog(retNo) - 1;
+ retNo -= (1 << list);
+ insertCommonArea(retPageRef, list);
+ retPageRef += (1 << list);
+ } while (1);
+}//Dbtup::returnCommonArea()
+
+void Dbtup::findFreeLeftNeighbours(Uint32& allocPageRef,
+ Uint32& noPagesAllocated,
+ Uint32 noOfPagesToAllocate)
+{
+ PagePtr pageFirstPtr, pageLastPtr;
+ Uint32 remainAllocate = noOfPagesToAllocate - noPagesAllocated;
+ while (allocPageRef > 0) {
+ ljam();
+ pageLastPtr.i = allocPageRef - 1;
+ ptrCheckGuard(pageLastPtr, cnoOfPage, page);
+ if (pageLastPtr.p->pageWord[ZPAGE_STATE_POS] != ZFREE_COMMON) {
+ ljam();
+ return;
+ } else {
+ ljam();
+ pageFirstPtr.i = pageLastPtr.p->pageWord[ZPAGE_FIRST_CLUST_POS];
+ ndbrequire(pageFirstPtr.i != RNIL);
+ Uint32 list = nextHigherTwoLog(pageLastPtr.i - pageFirstPtr.i);
+ removeCommonArea(pageFirstPtr.i, list);
+ Uint32 listSize = 1 << list;
+ if (listSize > remainAllocate) {
+ ljam();
+ Uint32 retNo = listSize - remainAllocate;
+ returnCommonArea(pageFirstPtr.i, retNo);
+ allocPageRef = pageFirstPtr.i + retNo;
+ noPagesAllocated = noOfPagesToAllocate;
+ return;
+ } else {
+ ljam();
+ allocPageRef = pageFirstPtr.i;
+ noPagesAllocated += listSize;
+ remainAllocate -= listSize;
+ }//if
+ }//if
+ }//while
+}//Dbtup::findFreeLeftNeighbours()
+
+void Dbtup::findFreeRightNeighbours(Uint32& allocPageRef,
+ Uint32& noPagesAllocated,
+ Uint32 noOfPagesToAllocate)
+{
+ PagePtr pageFirstPtr, pageLastPtr;
+ Uint32 remainAllocate = noOfPagesToAllocate - noPagesAllocated;
+ if (remainAllocate == 0) {
+ ljam();
+ return;
+ }//if
+ while ((allocPageRef + noPagesAllocated) < cnoOfPage) {
+ ljam();
+ pageFirstPtr.i = allocPageRef + noPagesAllocated;
+ ptrCheckGuard(pageFirstPtr, cnoOfPage, page);
+ if (pageFirstPtr.p->pageWord[ZPAGE_STATE_POS] != ZFREE_COMMON) {
+ ljam();
+ return;
+ } else {
+ ljam();
+ pageLastPtr.i = pageFirstPtr.p->pageWord[ZPAGE_LAST_CLUST_POS];
+ ndbrequire(pageLastPtr.i != RNIL);
+ Uint32 list = nextHigherTwoLog(pageLastPtr.i - pageFirstPtr.i);
+ removeCommonArea(pageFirstPtr.i, list);
+ Uint32 listSize = 1 << list;
+ if (listSize > remainAllocate) {
+ ljam();
+ Uint32 retPageRef = pageFirstPtr.i + remainAllocate;
+ Uint32 retNo = listSize - remainAllocate;
+ returnCommonArea(retPageRef, retNo);
+ noPagesAllocated += remainAllocate;
+ return;
+ } else {
+ ljam();
+ noPagesAllocated += listSize;
+ remainAllocate -= listSize;
+ }//if
+ }//if
+ }//while
+}//Dbtup::findFreeRightNeighbours()
+
+void Dbtup::insertCommonArea(Uint32 insPageRef, Uint32 insList)
+{
+ cnoOfAllocatedPages -= (1 << insList);
+ PagePtr pageLastPtr, pageInsPtr;
+
+ pageInsPtr.i = insPageRef;
+ ptrCheckGuard(pageInsPtr, cnoOfPage, page);
+ ndbrequire(insList < 16);
+ pageLastPtr.i = (pageInsPtr.i + (1 << insList)) - 1;
+
+ pageInsPtr.p->pageWord[ZPAGE_NEXT_CLUST_POS] = cfreepageList[insList];
+ pageInsPtr.p->pageWord[ZPAGE_PREV_CLUST_POS] = RNIL;
+ pageInsPtr.p->pageWord[ZPAGE_LAST_CLUST_POS] = pageLastPtr.i;
+ cfreepageList[insList] = pageInsPtr.i;
+
+ ptrCheckGuard(pageLastPtr, cnoOfPage, page);
+ pageLastPtr.p->pageWord[ZPAGE_FIRST_CLUST_POS] = pageInsPtr.i;
+ pageLastPtr.p->pageWord[ZPAGE_NEXT_POS] = RNIL;
+}//Dbtup::insertCommonArea()
+
+void Dbtup::removeCommonArea(Uint32 remPageRef, Uint32 list)
+{
+ cnoOfAllocatedPages += (1 << list);
+ PagePtr pagePrevPtr, pageNextPtr, pageLastPtr, pageSearchPtr, remPagePtr;
+
+ remPagePtr.i = remPageRef;
+ ptrCheckGuard(remPagePtr, cnoOfPage, page);
+ ndbrequire(list < 16);
+ if (cfreepageList[list] == remPagePtr.i) {
+ ljam();
+ cfreepageList[list] = remPagePtr.p->pageWord[ZPAGE_NEXT_CLUST_POS];
+ pageNextPtr.i = cfreepageList[list];
+ if (pageNextPtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(pageNextPtr, cnoOfPage, page);
+ pageNextPtr.p->pageWord[ZPAGE_PREV_CLUST_POS] = RNIL;
+ }//if
+ } else {
+ pageSearchPtr.i = cfreepageList[list];
+ while (true) {
+ ljam();
+ ptrCheckGuard(pageSearchPtr, cnoOfPage, page);
+ pagePrevPtr = pageSearchPtr;
+ pageSearchPtr.i = pageSearchPtr.p->pageWord[ZPAGE_NEXT_CLUST_POS];
+ if (pageSearchPtr.i == remPagePtr.i) {
+ ljam();
+ break;
+ }//if
+ }//while
+ pageNextPtr.i = remPagePtr.p->pageWord[ZPAGE_NEXT_CLUST_POS];
+ pagePrevPtr.p->pageWord[ZPAGE_NEXT_CLUST_POS] = pageNextPtr.i;
+ if (pageNextPtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(pageNextPtr, cnoOfPage, page);
+ pageNextPtr.p->pageWord[ZPAGE_PREV_CLUST_POS] = pagePrevPtr.i;
+ }//if
+ }//if
+ remPagePtr.p->pageWord[ZPAGE_NEXT_CLUST_POS] = RNIL;
+ remPagePtr.p->pageWord[ZPAGE_LAST_CLUST_POS] = RNIL;
+ remPagePtr.p->pageWord[ZPAGE_PREV_CLUST_POS] = RNIL;
+
+ pageLastPtr.i = (remPagePtr.i + (1 << list)) - 1;
+ ptrCheckGuard(pageLastPtr, cnoOfPage, page);
+ pageLastPtr.p->pageWord[ZPAGE_FIRST_CLUST_POS] = RNIL;
+}//Dbtup::removeCommonArea()
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp
new file mode 100644
index 00000000000..1f674876642
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp
@@ -0,0 +1,556 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(14000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(14000 + __LINE__); }
+
+//
+// PageMap is a service used by Dbtup to map logical page id's to physical
+// page id's. The mapping is needs the fragment and the logical page id to
+// provide the physical id.
+//
+// This is a part of Dbtup which is the exclusive user of a certain set of
+// variables on the fragment record and it is the exclusive user of the
+// struct for page ranges.
+//
+//
+// The following methods operate on the data handled by the page map class.
+//
+// Public methods
+// insertPageRange(Uint32 startPageId, # In
+// Uint32 noPages) # In
+// Inserts a range of pages into the mapping structure.
+//
+// void releaseFragPages()
+// Releases all pages and their mappings belonging to a fragment.
+//
+// Uint32 allocFragPages(Uint32 tafpNoAllocRequested)
+// Allocate a set of pages to the fragment from the page manager
+//
+// Uint32 getEmptyPage()
+// Get an empty page from the pool of empty pages on the fragment.
+// It returns the physical page id of the empty page.
+// Returns RNIL if no empty page is available.
+//
+// Uint32 getRealpid(Uint32 logicalPageId)
+// Return the physical page id provided the logical page id
+//
+// void initializePageRange()
+// Initialise free list of page ranges and initialise the page raneg records.
+//
+// void initFragRange()
+// Initialise the fragment variables when allocating a fragment to a table.
+//
+// void initPageRangeSize(Uint32 size)
+// Initialise the number of page ranges.
+//
+// Uint32 getNoOfPages()
+// Get the number of pages on the fragment currently.
+//
+//
+// Private methods
+// Uint32 leafPageRangeFull(PageRangePtr currPageRangePtr)
+//
+// void errorHandler()
+// Method to crash NDB kernel in case of weird data set-up
+//
+// void allocMoreFragPages()
+// When no more empty pages are attached to the fragment and we need more
+// we allocate more pages from the page manager using this method.
+//
+// Private data
+// On the fragment record
+// currentPageRange # The current page range where to insert the next range
+// rootPageRange # The root of the page ranges owned
+// nextStartRange # The next page id to assign when expanding the
+// # page map
+// noOfPages # The number of pages in the fragment
+// emptyPrimPage # The first page of the empty pages in the fragment
+//
+// The full page range struct
+
+Uint32 Dbtup::getEmptyPage(Fragrecord* const regFragPtr)
+{
+ Uint32 logicalPageId = regFragPtr->emptyPrimPage;
+ if (logicalPageId == RNIL) {
+ ljam();
+ allocMoreFragPages(regFragPtr);
+ logicalPageId = regFragPtr->emptyPrimPage;
+ if (logicalPageId == RNIL) {
+ ljam();
+ return RNIL;
+ }//if
+ }//if
+ Uint32 physicalPageId = getRealpid(regFragPtr, logicalPageId);
+ PagePtr pagePtr;
+ pagePtr.i = physicalPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ regFragPtr->emptyPrimPage = pagePtr.p->pageWord[ZPAGE_NEXT_POS];
+ return physicalPageId;
+}//Dbtup::getEmptyPage()
+
+Uint32 Dbtup::getRealpid(Fragrecord* const regFragPtr, Uint32 logicalPageId)
+{
+ PageRangePtr grpPageRangePtr;
+ Uint32 loopLimit;
+ Uint32 loopCount = 0;
+ Uint32 pageRangeLimit = cnoOfPageRangeRec;
+
+ grpPageRangePtr.i = regFragPtr->rootPageRange;
+ while (true) {
+ ndbrequire(loopCount++ < 100);
+ ndbrequire(grpPageRangePtr.i < pageRangeLimit);
+ ptrAss(grpPageRangePtr, pageRange);
+ loopLimit = grpPageRangePtr.p->currentIndexPos;
+ ndbrequire(loopLimit <= 3);
+ for (Uint32 i = 0; i <= loopLimit; i++) {
+ ljam();
+ if (grpPageRangePtr.p->startRange[i] <= logicalPageId) {
+ if (grpPageRangePtr.p->endRange[i] >= logicalPageId) {
+ if (grpPageRangePtr.p->type[i] == ZLEAF) {
+ ljam();
+ Uint32 realPageId = (logicalPageId - grpPageRangePtr.p->startRange[i]) +
+ grpPageRangePtr.p->basePageId[i];
+ return realPageId;
+ } else {
+ ndbrequire(grpPageRangePtr.p->type[i] == ZNON_LEAF);
+ grpPageRangePtr.i = grpPageRangePtr.p->basePageId[i];
+ }//if
+ }//if
+ }//if
+ }//for
+ }//while
+ return 0;
+}//Dbtup::getRealpid()
+
+Uint32 Dbtup::getNoOfPages(Fragrecord* const regFragPtr)
+{
+ return regFragPtr->noOfPages;
+}//Dbtup::getNoOfPages()
+
+void Dbtup::initPageRangeSize(Uint32 size)
+{
+ cnoOfPageRangeRec = size;
+}//Dbtup::initPageRangeSize()
+
+/* ---------------------------------------------------------------- */
+/* ----------------------- INSERT_PAGE_RANGE_TAB ------------------ */
+/* ---------------------------------------------------------------- */
+/* INSERT A PAGE RANGE INTO THE FRAGMENT */
+/* */
+/* NOTE: THE METHOD IS ATOMIC. EITHER THE ACTION IS */
+/* PERFORMED FULLY OR NO ACTION IS PERFORMED AT ALL. */
+/* TO SUPPORT THIS THE CODE HAS A CLEANUP PART AFTER */
+/* ERRORS. */
+/* ---------------------------------------------------------------- */
+bool Dbtup::insertPageRangeTab(Fragrecord* const regFragPtr,
+ Uint32 startPageId,
+ Uint32 noPages)
+{
+ PageRangePtr currPageRangePtr;
+ if (cfirstfreerange == RNIL) {
+ ljam();
+ return false;
+ }//if
+ currPageRangePtr.i = regFragPtr->currentPageRange;
+ if (currPageRangePtr.i == RNIL) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THE FIRST PAGE RANGE IS HANDLED WITH SPECIAL CODE */
+/* ---------------------------------------------------------------- */
+ seizePagerange(currPageRangePtr);
+ regFragPtr->rootPageRange = currPageRangePtr.i;
+ currPageRangePtr.p->currentIndexPos = 0;
+ currPageRangePtr.p->parentPtr = RNIL;
+ } else {
+ ljam();
+ ptrCheckGuard(currPageRangePtr, cnoOfPageRangeRec, pageRange);
+ if (currPageRangePtr.p->currentIndexPos < 3) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THE SIMPLE CASE WHEN IT IS ONLY NECESSARY TO FILL IN THE */
+/* NEXT EMPTY POSITION IN THE PAGE RANGE RECORD IS TREATED */
+/* BY COMMON CODE AT THE END OF THE SUBROUTINE. */
+/* ---------------------------------------------------------------- */
+ currPageRangePtr.p->currentIndexPos++;
+ } else {
+ ljam();
+ ndbrequire(currPageRangePtr.p->currentIndexPos == 3);
+ currPageRangePtr.i = leafPageRangeFull(regFragPtr, currPageRangePtr);
+ if (currPageRangePtr.i == RNIL) {
+ return false;
+ }//if
+ ptrCheckGuard(currPageRangePtr, cnoOfPageRangeRec, pageRange);
+ }//if
+ }//if
+ currPageRangePtr.p->startRange[currPageRangePtr.p->currentIndexPos] = regFragPtr->nextStartRange;
+/* ---------------------------------------------------------------- */
+/* NOW SET THE LEAF LEVEL PAGE RANGE RECORD PROPERLY */
+/* PAGE_RANGE_PTR REFERS TO LEAF RECORD WHEN ARRIVING HERE */
+/* ---------------------------------------------------------------- */
+ currPageRangePtr.p->endRange[currPageRangePtr.p->currentIndexPos] =
+ (regFragPtr->nextStartRange + noPages) - 1;
+ currPageRangePtr.p->basePageId[currPageRangePtr.p->currentIndexPos] = startPageId;
+ currPageRangePtr.p->type[currPageRangePtr.p->currentIndexPos] = ZLEAF;
+/* ---------------------------------------------------------------- */
+/* WE NEED TO UPDATE THE CURRENT PAGE RANGE IN CASE IT HAS */
+/* CHANGED. WE ALSO NEED TO UPDATE THE NEXT START RANGE */
+/* ---------------------------------------------------------------- */
+ regFragPtr->currentPageRange = currPageRangePtr.i;
+ regFragPtr->nextStartRange += noPages;
+/* ---------------------------------------------------------------- */
+/* WE NEED TO UPDATE THE END RANGE IN ALL PAGE RANGE RECORDS */
+/* UP TO THE ROOT. */
+/* ---------------------------------------------------------------- */
+ PageRangePtr loopPageRangePtr;
+ loopPageRangePtr = currPageRangePtr;
+ while (true) {
+ ljam();
+ loopPageRangePtr.i = loopPageRangePtr.p->parentPtr;
+ if (loopPageRangePtr.i != RNIL) {
+ ljam();
+ ptrCheckGuard(loopPageRangePtr, cnoOfPageRangeRec, pageRange);
+ ndbrequire(loopPageRangePtr.p->currentIndexPos < 4);
+ loopPageRangePtr.p->endRange[loopPageRangePtr.p->currentIndexPos] += noPages;
+ } else {
+ ljam();
+ break;
+ }//if
+ }//while
+ regFragPtr->noOfPages += noPages;
+ return true;
+}//Dbtup::insertPageRangeTab()
+
+
+void Dbtup::releaseFragPages(Fragrecord* const regFragPtr)
+{
+ if (regFragPtr->rootPageRange == RNIL) {
+ ljam();
+ return;
+ }//if
+ PageRangePtr regPRPtr;
+ regPRPtr.i = regFragPtr->rootPageRange;
+ ptrCheckGuard(regPRPtr, cnoOfPageRangeRec, pageRange);
+ while (true) {
+ ljam();
+ const Uint32 indexPos = regPRPtr.p->currentIndexPos;
+ ndbrequire(indexPos < 4);
+
+ const Uint32 basePageId = regPRPtr.p->basePageId[indexPos];
+ regPRPtr.p->basePageId[indexPos] = RNIL;
+ if (basePageId == RNIL) {
+ ljam();
+ /**
+ * Finished with indexPos continue with next
+ */
+ if (indexPos > 0) {
+ ljam();
+ regPRPtr.p->currentIndexPos--;
+ continue;
+ }//if
+
+ /* ---------------------------------------------------------------- */
+ /* THE PAGE RANGE REC IS EMPTY. RELEASE IT. */
+ /*----------------------------------------------------------------- */
+ Uint32 parentPtr = regPRPtr.p->parentPtr;
+ releasePagerange(regPRPtr);
+
+ if (parentPtr != RNIL) {
+ ljam();
+ regPRPtr.i = parentPtr;
+ ptrCheckGuard(regPRPtr, cnoOfPageRangeRec, pageRange);
+ continue;
+ }//if
+
+ ljam();
+ ndbrequire(regPRPtr.i == regFragPtr->rootPageRange);
+ initFragRange(regFragPtr);
+ return;
+ } else {
+ if (regPRPtr.p->type[indexPos] == ZNON_LEAF) {
+ jam();
+ /* ---------------------------------------------------------------- */
+ // A non-leaf node, we must release everything below it before we
+ // release this node.
+ /* ---------------------------------------------------------------- */
+ regPRPtr.i = basePageId;
+ ptrCheckGuard(regPRPtr, cnoOfPageRangeRec, pageRange);
+ } else {
+ jam();
+ ndbrequire(regPRPtr.p->type[indexPos] == ZLEAF);
+ /* ---------------------------------------------------------------- */
+ /* PAGE_RANGE_PTR /= RNIL AND THE CURRENT POS IS NOT A CHLED. */
+ /*----------------------------------------------------------------- */
+ const Uint32 start = regPRPtr.p->startRange[indexPos];
+ const Uint32 stop = regPRPtr.p->endRange[indexPos];
+ ndbrequire(stop >= start);
+ const Uint32 retNo = (stop - start + 1);
+ returnCommonArea(basePageId, retNo);
+ }//if
+ }//if
+ }//while
+}//Dbtup::releaseFragPages()
+
+void Dbtup::initializePageRange()
+{
+ PageRangePtr regPTRPtr;
+ for (regPTRPtr.i = 0;
+ regPTRPtr.i < cnoOfPageRangeRec; regPTRPtr.i++) {
+ ptrAss(regPTRPtr, pageRange);
+ regPTRPtr.p->nextFree = regPTRPtr.i + 1;
+ }//for
+ regPTRPtr.i = cnoOfPageRangeRec - 1;
+ ptrAss(regPTRPtr, pageRange);
+ regPTRPtr.p->nextFree = RNIL;
+ cfirstfreerange = 0;
+ c_noOfFreePageRanges = cnoOfPageRangeRec;
+}//Dbtup::initializePageRange()
+
+void Dbtup::initFragRange(Fragrecord* const regFragPtr)
+{
+ regFragPtr->emptyPrimPage = RNIL;
+ regFragPtr->rootPageRange = RNIL;
+ regFragPtr->currentPageRange = RNIL;
+ regFragPtr->noOfPages = 0;
+ regFragPtr->nextStartRange = 0;
+}//initFragRange()
+
+Uint32 Dbtup::allocFragPages(Fragrecord* const regFragPtr, Uint32 tafpNoAllocRequested)
+{
+ Uint32 tafpPagesAllocated = 0;
+ while (true) {
+ Uint32 noOfPagesAllocated = 0;
+ Uint32 noPagesToAllocate = tafpNoAllocRequested - tafpPagesAllocated;
+ Uint32 retPageRef = RNIL;
+ allocConsPages(noPagesToAllocate, noOfPagesAllocated, retPageRef);
+ if (noOfPagesAllocated == 0) {
+ ljam();
+ return tafpPagesAllocated;
+ }//if
+/* ---------------------------------------------------------------- */
+/* IT IS NOW TIME TO PUT THE ALLOCATED AREA INTO THE PAGE */
+/* RANGE TABLE. */
+/* ---------------------------------------------------------------- */
+ Uint32 startRange = regFragPtr->nextStartRange;
+ if (!insertPageRangeTab(regFragPtr, retPageRef, noOfPagesAllocated)) {
+ ljam();
+ returnCommonArea(retPageRef, noOfPagesAllocated);
+ return tafpPagesAllocated;
+ }//if
+ tafpPagesAllocated += noOfPagesAllocated;
+ Uint32 loopLimit = retPageRef + noOfPagesAllocated;
+ PagePtr loopPagePtr;
+/* ---------------------------------------------------------------- */
+/* SINCE A NUMBER OF PAGES WERE ALLOCATED FROM COMMON AREA */
+/* WITH SUCCESS IT IS NOW TIME TO CHANGE THE STATE OF */
+/* THOSE PAGES TO EMPTY_MM AND LINK THEM INTO THE EMPTY */
+/* PAGE LIST OF THE FRAGMENT. */
+/* ---------------------------------------------------------------- */
+ for (loopPagePtr.i = retPageRef; loopPagePtr.i < loopLimit; loopPagePtr.i++) {
+ ljam();
+ ptrCheckGuard(loopPagePtr, cnoOfPage, page);
+ loopPagePtr.p->pageWord[ZPAGE_STATE_POS] = ZEMPTY_MM;
+ loopPagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS] = startRange +
+ (loopPagePtr.i - retPageRef);
+ loopPagePtr.p->pageWord[ZPAGE_NEXT_POS] = loopPagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS] + 1;
+ }//for
+ loopPagePtr.i = (retPageRef + noOfPagesAllocated) - 1;
+ ptrCheckGuard(loopPagePtr, cnoOfPage, page);
+ loopPagePtr.p->pageWord[ZPAGE_NEXT_POS] = regFragPtr->emptyPrimPage;
+ regFragPtr->emptyPrimPage = startRange;
+/* ---------------------------------------------------------------- */
+/* WAS ENOUGH PAGES ALLOCATED OR ARE MORE NEEDED. */
+/* ---------------------------------------------------------------- */
+ if (tafpPagesAllocated < tafpNoAllocRequested) {
+ ljam();
+ } else {
+ ndbrequire(tafpPagesAllocated == tafpNoAllocRequested);
+ ljam();
+ return tafpNoAllocRequested;
+ }//if
+ }//while
+}//Dbtup::allocFragPages()
+
+void Dbtup::allocMoreFragPages(Fragrecord* const regFragPtr)
+{
+ Uint32 noAllocPages = regFragPtr->noOfPages >> 3; // 12.5%
+ noAllocPages += regFragPtr->noOfPages >> 4; // 6.25%
+ noAllocPages += 2;
+/* -----------------------------------------------------------------*/
+// We will grow by 18.75% plus two more additional pages to grow
+// a little bit quicker in the beginning.
+/* -----------------------------------------------------------------*/
+ allocFragPages(regFragPtr, noAllocPages);
+}//Dbtup::allocMoreFragPages()
+
+Uint32 Dbtup::leafPageRangeFull(Fragrecord* const regFragPtr, PageRangePtr currPageRangePtr)
+{
+/* ---------------------------------------------------------------- */
+/* THE COMPLEX CASE WHEN THE LEAF NODE IS FULL. GO UP THE TREE*/
+/* TO FIND THE FIRST RECORD WITH A FREE ENTRY. ALLOCATE NEW */
+/* PAGE RANGE RECORDS THEN ALL THE WAY DOWN TO THE LEAF LEVEL */
+/* AGAIN. THE TREE SHOULD ALWAYS REMAIN BALANCED. */
+/* ---------------------------------------------------------------- */
+ PageRangePtr parentPageRangePtr;
+ PageRangePtr foundPageRangePtr;
+ parentPageRangePtr = currPageRangePtr;
+ Uint32 tiprNoLevels = 1;
+ while (true) {
+ ljam();
+ parentPageRangePtr.i = parentPageRangePtr.p->parentPtr;
+ if (parentPageRangePtr.i == RNIL) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* WE HAVE REACHED THE ROOT. A NEW ROOT MUST BE ALLOCATED. */
+/* ---------------------------------------------------------------- */
+ if (c_noOfFreePageRanges < tiprNoLevels) {
+ ljam();
+ return RNIL;
+ }//if
+ PageRangePtr oldRootPRPtr;
+ PageRangePtr newRootPRPtr;
+ oldRootPRPtr.i = regFragPtr->rootPageRange;
+ ptrCheckGuard(oldRootPRPtr, cnoOfPageRangeRec, pageRange);
+ seizePagerange(newRootPRPtr);
+ regFragPtr->rootPageRange = newRootPRPtr.i;
+ oldRootPRPtr.p->parentPtr = newRootPRPtr.i;
+
+ newRootPRPtr.p->basePageId[0] = oldRootPRPtr.i;
+ newRootPRPtr.p->parentPtr = RNIL;
+ newRootPRPtr.p->startRange[0] = 0;
+ newRootPRPtr.p->endRange[0] = regFragPtr->nextStartRange - 1;
+ newRootPRPtr.p->type[0] = ZNON_LEAF;
+ newRootPRPtr.p->startRange[1] = regFragPtr->nextStartRange;
+ newRootPRPtr.p->endRange[1] = regFragPtr->nextStartRange - 1;
+ newRootPRPtr.p->type[1] = ZNON_LEAF;
+ newRootPRPtr.p->currentIndexPos = 1;
+ foundPageRangePtr = newRootPRPtr;
+ break;
+ } else {
+ ljam();
+ ptrCheckGuard(parentPageRangePtr, cnoOfPageRangeRec, pageRange);
+ if (parentPageRangePtr.p->currentIndexPos < 3) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* WE HAVE FOUND AN EMPTY ENTRY IN A PAGE RANGE RECORD. */
+/* ALLOCATE A NEW PAGE RANGE RECORD, FILL IN THE START RANGE, */
+/* ALLOCATE A NEW PAGE RANGE RECORD AND UPDATE THE POINTERS */
+/* ---------------------------------------------------------------- */
+ parentPageRangePtr.p->currentIndexPos++;
+ parentPageRangePtr.p->startRange[parentPageRangePtr.p->currentIndexPos] = regFragPtr->nextStartRange;
+ parentPageRangePtr.p->endRange[parentPageRangePtr.p->currentIndexPos] = regFragPtr->nextStartRange - 1;
+ parentPageRangePtr.p->type[parentPageRangePtr.p->currentIndexPos] = ZNON_LEAF;
+ foundPageRangePtr = parentPageRangePtr;
+ break;
+ } else {
+ ljam();
+ ndbrequire(parentPageRangePtr.p->currentIndexPos == 3);
+/* ---------------------------------------------------------------- */
+/* THE PAGE RANGE RECORD WAS FULL. FIND THE PARENT RECORD */
+/* AND INCREASE THE NUMBER OF LEVELS WE HAVE TRAVERSED */
+/* GOING UP THE TREE. */
+/* ---------------------------------------------------------------- */
+ tiprNoLevels++;
+ }//if
+ }//if
+ }//while
+/* ---------------------------------------------------------------- */
+/* REMEMBER THE ERROR LEVEL IN CASE OF ALLOCATION ERRORS */
+/* ---------------------------------------------------------------- */
+ PageRangePtr newPageRangePtr;
+ PageRangePtr prevPageRangePtr;
+ prevPageRangePtr = foundPageRangePtr;
+ if (c_noOfFreePageRanges < tiprNoLevels) {
+ ljam();
+ return RNIL;
+ }//if
+/* ---------------------------------------------------------------- */
+/* NOW WE HAVE PERFORMED THE SEARCH UPWARDS AND FILLED IN THE */
+/* PROPER FIELDS IN THE PAGE RANGE RECORD WHERE SOME SPACE */
+/* WAS FOUND. THE NEXT STEP IS TO ALLOCATE PAGE RANGES SO */
+/* THAT WE KEEP THE B-TREE BALANCED. THE NEW PAGE RANGE */
+/* ARE ALSO PROPERLY UPDATED ON THE PATH TO THE LEAF LEVEL. */
+/* ---------------------------------------------------------------- */
+ while (true) {
+ ljam();
+ seizePagerange(newPageRangePtr);
+ tiprNoLevels--;
+ ndbrequire(prevPageRangePtr.p->currentIndexPos < 4);
+ prevPageRangePtr.p->basePageId[prevPageRangePtr.p->currentIndexPos] = newPageRangePtr.i;
+ newPageRangePtr.p->parentPtr = prevPageRangePtr.i;
+ newPageRangePtr.p->currentIndexPos = 0;
+ if (tiprNoLevels > 0) {
+ ljam();
+ newPageRangePtr.p->startRange[0] = regFragPtr->nextStartRange;
+ newPageRangePtr.p->endRange[0] = regFragPtr->nextStartRange - 1;
+ newPageRangePtr.p->type[0] = ZNON_LEAF;
+ prevPageRangePtr = newPageRangePtr;
+ } else {
+ ljam();
+ break;
+ }//if
+ }//while
+ return newPageRangePtr.i;
+}//Dbtup::leafPageRangeFull()
+
+void Dbtup::releasePagerange(PageRangePtr regPRPtr)
+{
+ regPRPtr.p->nextFree = cfirstfreerange;
+ cfirstfreerange = regPRPtr.i;
+ c_noOfFreePageRanges++;
+}//Dbtup::releasePagerange()
+
+void Dbtup::seizePagerange(PageRangePtr& regPageRangePtr)
+{
+ regPageRangePtr.i = cfirstfreerange;
+ ptrCheckGuard(regPageRangePtr, cnoOfPageRangeRec, pageRange);
+ cfirstfreerange = regPageRangePtr.p->nextFree;
+ regPageRangePtr.p->nextFree = RNIL;
+ regPageRangePtr.p->currentIndexPos = 0;
+ regPageRangePtr.p->parentPtr = RNIL;
+ for (Uint32 i = 0; i < 4; i++) {
+ regPageRangePtr.p->startRange[i] = 1;
+ regPageRangePtr.p->endRange[i] = 0;
+ regPageRangePtr.p->type[i] = ZNON_LEAF;
+ regPageRangePtr.p->basePageId[i] = (Uint32)-1;
+ }//for
+ c_noOfFreePageRanges--;
+}//Dbtup::seizePagerange()
+
+void Dbtup::errorHandler(Uint32 errorCode)
+{
+ switch (errorCode) {
+ case 0:
+ ljam();
+ break;
+ case 1:
+ ljam();
+ break;
+ case 2:
+ ljam();
+ break;
+ default:
+ ljam();
+ }
+ ndbrequire(false);
+}//Dbtup::errorHandler()
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp
new file mode 100644
index 00000000000..c3f85cdebd5
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp
@@ -0,0 +1,1185 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <AttributeHeader.hpp>
+
+#define ljam() { jamLine(3000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(3000 + __LINE__); }
+
+void
+Dbtup::setUpQueryRoutines(Tablerec* const regTabPtr)
+{
+ Uint32 startDescriptor = regTabPtr->tabDescriptor;
+ ndbrequire((startDescriptor + (regTabPtr->noOfAttr << ZAD_LOG_SIZE)) <= cnoOfTabDescrRec);
+ for (Uint32 i = 0; i < regTabPtr->noOfAttr; i++) {
+ Uint32 attrDescriptorStart = startDescriptor + (i << ZAD_LOG_SIZE);
+ Uint32 attrDescriptor = tableDescriptor[attrDescriptorStart].tabDescr;
+ Uint32 attrOffset = tableDescriptor[attrDescriptorStart + 1].tabDescr;
+ if (!AttributeDescriptor::getDynamic(attrDescriptor)) {
+ if ((AttributeDescriptor::getArrayType(attrDescriptor) == ZNON_ARRAY) ||
+ (AttributeDescriptor::getArrayType(attrDescriptor) == ZFIXED_ARRAY)) {
+ if (!AttributeDescriptor::getNullable(attrDescriptor)) {
+ if (AttributeDescriptor::getSize(attrDescriptor) == 0){
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readBitsNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateBitsNotNULL;
+ } else if (AttributeDescriptor::getSizeInWords(attrDescriptor) == 1){
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHOneWordNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHOneWordNotNULL;
+ } else if (AttributeDescriptor::getSizeInWords(attrDescriptor) == 2) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHTwoWordNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHTwoWordNotNULL;
+ } else if (AttributeDescriptor::getSizeInWords(attrDescriptor) > 2) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHManyWordNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNotNULL;
+ } else {
+ ndbrequire(false);
+ }//if
+ // replace functions for char attribute
+ if (AttributeOffset::getCharsetFlag(attrOffset)) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHManyWordNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNotNULL;
+ }
+ } else {
+ if (AttributeDescriptor::getSize(attrDescriptor) == 0){
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readBitsNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateBitsNULLable;
+ } else if (AttributeDescriptor::getSizeInWords(attrDescriptor) == 1){
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHOneWordNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable;
+ } else if (AttributeDescriptor::getSizeInWords(attrDescriptor) == 2) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHTwoWordNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable;
+ } else if (AttributeDescriptor::getSizeInWords(attrDescriptor) > 2) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHManyWordNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable;
+ } else {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHZeroWordNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable;
+ }//if
+ // replace functions for char attribute
+ if (AttributeOffset::getCharsetFlag(attrOffset)) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readFixedSizeTHManyWordNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateFixedSizeTHManyWordNULLable;
+ }
+ }//if
+ } else if (AttributeDescriptor::getArrayType(attrDescriptor) == ZVAR_ARRAY) {
+ if (!AttributeDescriptor::getNullable(attrDescriptor)) {
+ if (AttributeDescriptor::getArraySize(attrDescriptor) == 0) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readVarSizeUnlimitedNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateVarSizeUnlimitedNotNULL;
+ } else if (AttributeDescriptor::getArraySize(attrDescriptor) > ZMAX_SMALL_VAR_ARRAY) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readBigVarSizeNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateBigVarSizeNotNULL;
+ } else {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readSmallVarSizeNotNULL;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateSmallVarSizeNotNULL;
+ }//if
+ } else {
+ if (AttributeDescriptor::getArraySize(attrDescriptor) == 0) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readVarSizeUnlimitedNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateVarSizeUnlimitedNULLable;
+ } else if (AttributeDescriptor::getArraySize(attrDescriptor) > ZMAX_SMALL_VAR_ARRAY) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readBigVarSizeNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateBigVarSizeNULLable;
+ } else {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readSmallVarSizeNULLable;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateSmallVarSizeNULLable;
+ }//if
+ }//if
+ } else {
+ ndbrequire(false);
+ }//if
+ } else {
+ if ((AttributeDescriptor::getArrayType(attrDescriptor) == ZNON_ARRAY) ||
+ (AttributeDescriptor::getArrayType(attrDescriptor) == ZFIXED_ARRAY)) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readDynFixedSize;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateDynFixedSize;
+ } else if (AttributeDescriptor::getType(attrDescriptor) == ZVAR_ARRAY) {
+ if (AttributeDescriptor::getArraySize(attrDescriptor) == 0) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readDynVarSizeUnlimited;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateDynVarSizeUnlimited;
+ } else if (AttributeDescriptor::getArraySize(attrDescriptor) > ZMAX_SMALL_VAR_ARRAY) {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readDynBigVarSize;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateDynBigVarSize;
+ } else {
+ ljam();
+ regTabPtr->readFunctionArray[i] = &Dbtup::readDynSmallVarSize;
+ regTabPtr->updateFunctionArray[i] = &Dbtup::updateDynSmallVarSize;
+ }//if
+ } else {
+ ndbrequire(false);
+ }//if
+ }//if
+ }//for
+}//Dbtup::setUpQueryRoutines()
+
+/* ---------------------------------------------------------------- */
+/* THIS ROUTINE IS USED TO READ A NUMBER OF ATTRIBUTES IN THE */
+/* DATABASE AND PLACE THE RESULT IN ATTRINFO RECORDS. */
+//
+// In addition to the parameters used in the call it also relies on the
+// following variables set-up properly.
+//
+// operPtr.p Operation record pointer
+// fragptr.p Fragment record pointer
+// tabptr.p Table record pointer
+/* ---------------------------------------------------------------- */
+int Dbtup::readAttributes(Page* const pagePtr,
+ Uint32 tupHeadOffset,
+ const Uint32* inBuffer,
+ Uint32 inBufLen,
+ Uint32* outBuffer,
+ Uint32 maxRead,
+ bool xfrmFlag)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Uint32 numAttributes = regTabPtr->noOfAttr;
+ Uint32 attrDescriptorStart = regTabPtr->tabDescriptor;
+ Uint32 inBufIndex = 0;
+
+ ndbrequire(attrDescriptorStart + (numAttributes << ZAD_LOG_SIZE) <= cnoOfTabDescrRec);
+
+ tOutBufIndex = 0;
+ tCheckOffset = regTabPtr->tupheadsize;
+ tMaxRead = maxRead;
+ tTupleHeader = &pagePtr->pageWord[tupHeadOffset];
+ tXfrmFlag = xfrmFlag;
+
+ ndbrequire(tupHeadOffset + tCheckOffset <= ZWORDS_ON_PAGE);
+ while (inBufIndex < inBufLen) {
+ Uint32 tmpAttrBufIndex = tOutBufIndex;
+ AttributeHeader ahIn(inBuffer[inBufIndex]);
+ inBufIndex++;
+ Uint32 attributeId = ahIn.getAttributeId();
+ Uint32 attrDescriptorIndex = attrDescriptorStart + (attributeId << ZAD_LOG_SIZE);
+ ljam();
+
+ AttributeHeader::init(&outBuffer[tmpAttrBufIndex], attributeId, 0);
+ AttributeHeader* ahOut = (AttributeHeader*)&outBuffer[tmpAttrBufIndex];
+ tOutBufIndex = tmpAttrBufIndex + 1;
+ if (attributeId < numAttributes) {
+ Uint32 attributeDescriptor = tableDescriptor[attrDescriptorIndex].tabDescr;
+ Uint32 attributeOffset = tableDescriptor[attrDescriptorIndex + 1].tabDescr;
+ ReadFunction f = regTabPtr->readFunctionArray[attributeId];
+ if ((this->*f)(outBuffer,
+ ahOut,
+ attributeDescriptor,
+ attributeOffset)) {
+ continue;
+ } else {
+ return -1;
+ }//if
+ } else if(attributeId & AttributeHeader::PSUEDO){
+ Uint32 sz = read_psuedo(attributeId,
+ outBuffer+tmpAttrBufIndex+1);
+ AttributeHeader::init(&outBuffer[tmpAttrBufIndex], attributeId, sz);
+ tOutBufIndex = tmpAttrBufIndex + 1 + sz;
+ } else {
+ terrorCode = ZATTRIBUTE_ID_ERROR;
+ return -1;
+ }//if
+ }//while
+ return tOutBufIndex;
+}//Dbtup::readAttributes()
+
+#if 0
+int Dbtup::readAttributesWithoutHeader(Page* const pagePtr,
+ Uint32 tupHeadOffset,
+ Uint32* inBuffer,
+ Uint32 inBufLen,
+ Uint32* outBuffer,
+ Uint32* attrBuffer,
+ Uint32 maxRead)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Uint32 numAttributes = regTabPtr->noOfAttr;
+ Uint32 attrDescriptorStart = regTabPtr->tabDescriptor;
+ Uint32 inBufIndex = 0;
+ Uint32 attrBufIndex = 0;
+
+ ndbrequire(attrDescriptorStart + (numAttributes << ZAD_LOG_SIZE) <= cnoOfTabDescrRec);
+
+ tOutBufIndex = 0;
+ tCheckOffset = regTabPtr->tupheadsize;
+ tMaxRead = maxRead;
+ tTupleHeader = &pagePtr->pageWord[tupHeadOffset];
+
+ ndbrequire(tupHeadOffset + tCheckOffset <= ZWORDS_ON_PAGE);
+ while (inBufIndex < inBufLen) {
+ AttributeHeader ahIn(inBuffer[inBufIndex]);
+ inBufIndex++;
+ Uint32 attributeId = ahIn.getAttributeId();
+ Uint32 attrDescriptorIndex = attrDescriptorStart + (attributeId << ZAD_LOG_SIZE);
+ ljam();
+
+ AttributeHeader::init(&attrBuffer[attrBufIndex], attributeId, 0);
+ AttributeHeader* ahOut = (AttributeHeader*)&attrBuffer[attrBufIndex];
+ attrBufIndex++;
+ if (attributeId < numAttributes) {
+ Uint32 attributeDescriptor = tableDescriptor[attrDescriptorIndex].tabDescr;
+ Uint32 attributeOffset = tableDescriptor[attrDescriptorIndex + 1].tabDescr;
+ ReadFunction f = regTabPtr->readFunctionArray[attributeId];
+ if ((this->*f)(outBuffer,
+ ahOut,
+ attributeDescriptor,
+ attributeOffset)) {
+ continue;
+ } else {
+ return -1;
+ }//if
+ } else {
+ terrorCode = ZATTRIBUTE_ID_ERROR;
+ return -1;
+ }//if
+ }//while
+ ndbrequire(attrBufIndex == inBufLen);
+ return tOutBufIndex;
+}//Dbtup::readAttributes()
+#endif
+
+bool
+Dbtup::readFixedSizeTHOneWordNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Uint32 indexBuf = tOutBufIndex;
+ Uint32 readOffset = AttributeOffset::getOffset(attrDes2);
+ Uint32 const wordRead = tTupleHeader[readOffset];
+ Uint32 newIndexBuf = indexBuf + 1;
+ Uint32 maxRead = tMaxRead;
+
+ ndbrequire(readOffset < tCheckOffset);
+ if (newIndexBuf <= maxRead) {
+ ljam();
+ outBuffer[indexBuf] = wordRead;
+ ahOut->setDataSize(1);
+ tOutBufIndex = newIndexBuf;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ return false;
+ }//if
+}//Dbtup::readFixedSizeTHOneWordNotNULL()
+
+bool
+Dbtup::readFixedSizeTHTwoWordNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Uint32 indexBuf = tOutBufIndex;
+ Uint32 readOffset = AttributeOffset::getOffset(attrDes2);
+ Uint32 const wordReadFirst = tTupleHeader[readOffset];
+ Uint32 const wordReadSecond = tTupleHeader[readOffset + 1];
+ Uint32 newIndexBuf = indexBuf + 2;
+ Uint32 maxRead = tMaxRead;
+
+ ndbrequire(readOffset + 1 < tCheckOffset);
+ if (newIndexBuf <= maxRead) {
+ ljam();
+ ahOut->setDataSize(2);
+ outBuffer[indexBuf] = wordReadFirst;
+ outBuffer[indexBuf + 1] = wordReadSecond;
+ tOutBufIndex = newIndexBuf;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ return false;
+ }//if
+}//Dbtup::readFixedSizeTHTwoWordNotNULL()
+
+bool
+Dbtup::readFixedSizeTHManyWordNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Uint32 indexBuf = tOutBufIndex;
+ Uint32 charsetFlag = AttributeOffset::getCharsetFlag(attrDes2);
+ Uint32 readOffset = AttributeOffset::getOffset(attrDes2);
+ Uint32 attrNoOfWords = AttributeDescriptor::getSizeInWords(attrDescriptor);
+ Uint32 maxRead = tMaxRead;
+
+ ndbrequire((readOffset + attrNoOfWords - 1) < tCheckOffset);
+ if (! charsetFlag || ! tXfrmFlag) {
+ Uint32 newIndexBuf = indexBuf + attrNoOfWords;
+ if (newIndexBuf <= maxRead) {
+ ljam();
+ ahOut->setDataSize(attrNoOfWords);
+ MEMCOPY_NO_WORDS(&outBuffer[indexBuf],
+ &tTupleHeader[readOffset],
+ attrNoOfWords);
+ tOutBufIndex = newIndexBuf;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ }//if
+ } else {
+ ljam();
+ Tablerec* regTabPtr = tabptr.p;
+ Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(attrDescriptor);
+ uchar* dstPtr = (uchar*)&outBuffer[indexBuf];
+ const uchar* srcPtr = (uchar*)&tTupleHeader[readOffset];
+ Uint32 i = AttributeOffset::getCharsetPos(attrDes2);
+ ndbrequire(i < regTabPtr->noOfCharsets);
+ CHARSET_INFO* cs = regTabPtr->charsetArray[i];
+ Uint32 typeId = AttributeDescriptor::getType(attrDescriptor);
+ Uint32 lb, len;
+ bool ok = NdbSqlUtil::get_var_length(typeId, srcPtr, srcBytes, lb, len);
+ if (ok) {
+ Uint32 xmul = cs->strxfrm_multiply;
+ if (xmul == 0)
+ xmul = 1;
+ // see comment in DbtcMain.cpp
+ Uint32 dstLen = xmul * (srcBytes - lb);
+ Uint32 maxIndexBuf = indexBuf + (dstLen >> 2);
+ if (maxIndexBuf <= maxRead) {
+ ljam();
+ int n = NdbSqlUtil::strnxfrm_bug7284(cs, dstPtr, dstLen, srcPtr + lb, len);
+ ndbrequire(n != -1);
+ while ((n & 3) != 0) {
+ dstPtr[n++] = 0;
+ }
+ Uint32 dstWords = (n >> 2);
+ ahOut->setDataSize(dstWords);
+ Uint32 newIndexBuf = indexBuf + dstWords;
+ ndbrequire(newIndexBuf <= maxRead);
+ tOutBufIndex = newIndexBuf;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ }
+ } else {
+ ljam();
+ terrorCode = ZTUPLE_CORRUPTED_ERROR;
+ }
+ }
+ return false;
+}//Dbtup::readFixedSizeTHManyWordNotNULL()
+
+bool
+Dbtup::readFixedSizeTHOneWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ if (!nullFlagCheck(attrDes2)) {
+ ljam();
+ return readFixedSizeTHOneWordNotNULL(outBuffer,
+ ahOut,
+ attrDescriptor,
+ attrDes2);
+ } else {
+ ljam();
+ ahOut->setNULL();
+ return true;
+ }//if
+}//Dbtup::readFixedSizeTHOneWordNULLable()
+
+bool
+Dbtup::readFixedSizeTHTwoWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ if (!nullFlagCheck(attrDes2)) {
+ ljam();
+ return readFixedSizeTHTwoWordNotNULL(outBuffer,
+ ahOut,
+ attrDescriptor,
+ attrDes2);
+ } else {
+ ljam();
+ ahOut->setNULL();
+ return true;
+ }//if
+}//Dbtup::readFixedSizeTHTwoWordNULLable()
+
+bool
+Dbtup::readFixedSizeTHManyWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ if (!nullFlagCheck(attrDes2)) {
+ ljam();
+ return readFixedSizeTHManyWordNotNULL(outBuffer,
+ ahOut,
+ attrDescriptor,
+ attrDes2);
+ } else {
+ ljam();
+ ahOut->setNULL();
+ return true;
+ }//if
+}//Dbtup::readFixedSizeTHManyWordNULLable()
+
+bool
+Dbtup::readFixedSizeTHZeroWordNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ if (nullFlagCheck(attrDes2)) {
+ ljam();
+ ahOut->setNULL();
+ }//if
+ return true;
+}//Dbtup::readFixedSizeTHZeroWordNULLable()
+
+bool
+Dbtup::nullFlagCheck(Uint32 attrDes2)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Uint32 nullFlagOffsetInTuple = AttributeOffset::getNullFlagOffset(attrDes2);
+ ndbrequire(nullFlagOffsetInTuple < regTabPtr->tupNullWords);
+ nullFlagOffsetInTuple += regTabPtr->tupNullIndex;
+ ndbrequire(nullFlagOffsetInTuple < tCheckOffset);
+
+ return (AttributeOffset::isNULL(tTupleHeader[nullFlagOffsetInTuple], attrDes2));
+}//Dbtup::nullFlagCheck()
+
+bool
+Dbtup::readVariableSizedAttr(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readVariableSizedAttr()
+
+bool
+Dbtup::readVarSizeUnlimitedNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readVarSizeUnlimitedNotNULL()
+
+bool
+Dbtup::readVarSizeUnlimitedNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readVarSizeUnlimitedNULLable()
+
+bool
+Dbtup::readBigVarSizeNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readBigVarSizeNotNULL()
+
+bool
+Dbtup::readBigVarSizeNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readBigVarSizeNULLable()
+
+bool
+Dbtup::readSmallVarSizeNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readSmallVarSizeNotNULL()
+
+bool
+Dbtup::readSmallVarSizeNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readSmallVarSizeNULLable()
+
+bool
+Dbtup::readDynFixedSize(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readDynFixedSize()
+
+bool
+Dbtup::readDynVarSizeUnlimited(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readDynVarSizeUnlimited()
+
+bool
+Dbtup::readDynBigVarSize(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readDynBigVarSize()
+
+bool
+Dbtup::readDynSmallVarSize(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::readDynSmallVarSize()
+
+/* ---------------------------------------------------------------------- */
+/* THIS ROUTINE IS USED TO UPDATE A NUMBER OF ATTRIBUTES. IT IS */
+/* USED BY THE INSERT ROUTINE, THE UPDATE ROUTINE AND IT CAN BE */
+/* CALLED SEVERAL TIMES FROM THE INTERPRETER. */
+// In addition to the parameters used in the call it also relies on the
+// following variables set-up properly.
+//
+// pagep.p Page record pointer
+// fragptr.p Fragment record pointer
+// operPtr.p Operation record pointer
+// tabptr.p Table record pointer
+/* ---------------------------------------------------------------------- */
+int Dbtup::updateAttributes(Page* const pagePtr,
+ Uint32 tupHeadOffset,
+ Uint32* inBuffer,
+ Uint32 inBufLen)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Operationrec* const regOperPtr = operPtr.p;
+ Uint32 numAttributes = regTabPtr->noOfAttr;
+ Uint32 attrDescriptorStart = regTabPtr->tabDescriptor;
+ ndbrequire(attrDescriptorStart + (numAttributes << ZAD_LOG_SIZE) <= cnoOfTabDescrRec);
+
+ tCheckOffset = regTabPtr->tupheadsize;
+ tTupleHeader = &pagePtr->pageWord[tupHeadOffset];
+ Uint32 inBufIndex = 0;
+ tInBufIndex = 0;
+ tInBufLen = inBufLen;
+
+ ndbrequire(tupHeadOffset + tCheckOffset <= ZWORDS_ON_PAGE);
+ while (inBufIndex < inBufLen) {
+ AttributeHeader ahIn(inBuffer[inBufIndex]);
+ Uint32 attributeId = ahIn.getAttributeId();
+ Uint32 attrDescriptorIndex = attrDescriptorStart + (attributeId << ZAD_LOG_SIZE);
+ if (attributeId < numAttributes) {
+ Uint32 attrDescriptor = tableDescriptor[attrDescriptorIndex].tabDescr;
+ Uint32 attributeOffset = tableDescriptor[attrDescriptorIndex + 1].tabDescr;
+ if ((AttributeDescriptor::getPrimaryKey(attrDescriptor)) &&
+ (regOperPtr->optype != ZINSERT)) {
+ if (checkUpdateOfPrimaryKey(&inBuffer[inBufIndex], regTabPtr)) {
+ ljam();
+ terrorCode = ZTRY_UPDATE_PRIMARY_KEY;
+ return -1;
+ }//if
+ }//if
+ UpdateFunction f = regTabPtr->updateFunctionArray[attributeId];
+ ljam();
+ regOperPtr->changeMask.set(attributeId);
+ if ((this->*f)(inBuffer,
+ attrDescriptor,
+ attributeOffset)) {
+ inBufIndex = tInBufIndex;
+ continue;
+ } else {
+ ljam();
+ return -1;
+ }//if
+ } else {
+ ljam();
+ terrorCode = ZATTRIBUTE_ID_ERROR;
+ return -1;
+ }//if
+ }//while
+ return 0;
+}//Dbtup::updateAttributes()
+
+bool
+Dbtup::checkUpdateOfPrimaryKey(Uint32* updateBuffer, Tablerec* const regTabPtr)
+{
+ Uint32 keyReadBuffer[MAX_KEY_SIZE_IN_WORDS];
+ Uint32 attributeHeader;
+ AttributeHeader* ahOut = (AttributeHeader*)&attributeHeader;
+ AttributeHeader ahIn(*updateBuffer);
+ Uint32 attributeId = ahIn.getAttributeId();
+ Uint32 attrDescriptorIndex = regTabPtr->tabDescriptor + (attributeId << ZAD_LOG_SIZE);
+ Uint32 attrDescriptor = tableDescriptor[attrDescriptorIndex].tabDescr;
+ Uint32 attributeOffset = tableDescriptor[attrDescriptorIndex + 1].tabDescr;
+ ReadFunction f = regTabPtr->readFunctionArray[attributeId];
+
+ AttributeHeader::init(&attributeHeader, attributeId, 0);
+ tOutBufIndex = 0;
+ tMaxRead = MAX_KEY_SIZE_IN_WORDS;
+
+ bool tmp = tXfrmFlag;
+ tXfrmFlag = false;
+ ndbrequire((this->*f)(&keyReadBuffer[0], ahOut, attrDescriptor, attributeOffset));
+ tXfrmFlag = tmp;
+ ndbrequire(tOutBufIndex == ahOut->getDataSize());
+ if (ahIn.getDataSize() != ahOut->getDataSize()) {
+ ljam();
+ return true;
+ }//if
+ if (memcmp(&keyReadBuffer[0], &updateBuffer[1], tOutBufIndex << 2) != 0) {
+ ljam();
+ return true;
+ }//if
+ return false;
+}//Dbtup::checkUpdateOfPrimaryKey()
+
+#if 0
+void Dbtup::checkPages(Fragrecord* const regFragPtr)
+{
+ Uint32 noPages = getNoOfPages(regFragPtr);
+ for (Uint32 i = 0; i < noPages ; i++) {
+ PagePtr pagePtr;
+ pagePtr.i = getRealpid(regFragPtr, i);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ ndbrequire(pagePtr.p->pageWord[1] != (RNIL - 1));
+ }
+}
+#endif
+
+bool
+Dbtup::updateFixedSizeTHOneWordNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Uint32 indexBuf = tInBufIndex;
+ Uint32 inBufLen = tInBufLen;
+ Uint32 updateOffset = AttributeOffset::getOffset(attrDes2);
+ AttributeHeader ahIn(inBuffer[indexBuf]);
+ Uint32 nullIndicator = ahIn.isNULL();
+ Uint32 newIndex = indexBuf + 2;
+ ndbrequire(updateOffset < tCheckOffset);
+
+ if (newIndex <= inBufLen) {
+ Uint32 updateWord = inBuffer[indexBuf + 1];
+ if (!nullIndicator) {
+ ljam();
+ tInBufIndex = newIndex;
+ tTupleHeader[updateOffset] = updateWord;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZNOT_NULL_ATTR;
+ return false;
+ }//if
+ } else {
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ return false;
+ }//if
+ return true;
+}//Dbtup::updateFixedSizeTHOneWordNotNULL()
+
+bool
+Dbtup::updateFixedSizeTHTwoWordNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Uint32 indexBuf = tInBufIndex;
+ Uint32 inBufLen = tInBufLen;
+ Uint32 updateOffset = AttributeOffset::getOffset(attrDes2);
+ AttributeHeader ahIn(inBuffer[indexBuf]);
+ Uint32 nullIndicator = ahIn.isNULL();
+ Uint32 newIndex = indexBuf + 3;
+ ndbrequire((updateOffset + 1) < tCheckOffset);
+
+ if (newIndex <= inBufLen) {
+ Uint32 updateWord1 = inBuffer[indexBuf + 1];
+ Uint32 updateWord2 = inBuffer[indexBuf + 2];
+ if (!nullIndicator) {
+ ljam();
+ tInBufIndex = newIndex;
+ tTupleHeader[updateOffset] = updateWord1;
+ tTupleHeader[updateOffset + 1] = updateWord2;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZNOT_NULL_ATTR;
+ return false;
+ }//if
+ } else {
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ return false;
+ }//if
+}//Dbtup::updateFixedSizeTHTwoWordNotNULL()
+
+bool
+Dbtup::updateFixedSizeTHManyWordNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Uint32 indexBuf = tInBufIndex;
+ Uint32 inBufLen = tInBufLen;
+ Uint32 updateOffset = AttributeOffset::getOffset(attrDes2);
+ Uint32 charsetFlag = AttributeOffset::getCharsetFlag(attrDes2);
+ AttributeHeader ahIn(inBuffer[indexBuf]);
+ Uint32 nullIndicator = ahIn.isNULL();
+ Uint32 noOfWords = AttributeDescriptor::getSizeInWords(attrDescriptor);
+ Uint32 newIndex = indexBuf + noOfWords + 1;
+ ndbrequire((updateOffset + noOfWords - 1) < tCheckOffset);
+
+ if (newIndex <= inBufLen) {
+ if (!nullIndicator) {
+ ljam();
+ if (charsetFlag) {
+ ljam();
+ Tablerec* regTabPtr = tabptr.p;
+ Uint32 typeId = AttributeDescriptor::getType(attrDescriptor);
+ Uint32 bytes = AttributeDescriptor::getSizeInBytes(attrDescriptor);
+ Uint32 i = AttributeOffset::getCharsetPos(attrDes2);
+ ndbrequire(i < regTabPtr->noOfCharsets);
+ // not const in MySQL
+ CHARSET_INFO* cs = regTabPtr->charsetArray[i];
+ const char* ssrc = (const char*)&inBuffer[tInBufIndex + 1];
+ Uint32 lb, len;
+ if (! NdbSqlUtil::get_var_length(typeId, ssrc, bytes, lb, len)) {
+ ljam();
+ terrorCode = ZINVALID_CHAR_FORMAT;
+ return false;
+ }
+ // fast fix bug#7340
+ if (typeId != NDB_TYPE_TEXT &&
+ (*cs->cset->well_formed_len)(cs, ssrc + lb, ssrc + lb + len, ZNIL) != len) {
+ ljam();
+ terrorCode = ZINVALID_CHAR_FORMAT;
+ return false;
+ }
+ }
+ tInBufIndex = newIndex;
+ MEMCOPY_NO_WORDS(&tTupleHeader[updateOffset],
+ &inBuffer[indexBuf + 1],
+ noOfWords);
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZNOT_NULL_ATTR;
+ return false;
+ }//if
+ } else {
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ return false;
+ }//if
+}//Dbtup::updateFixedSizeTHManyWordNotNULL()
+
+bool
+Dbtup::updateFixedSizeTHManyWordNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ AttributeHeader ahIn(inBuffer[tInBufIndex]);
+ Uint32 nullIndicator = ahIn.isNULL();
+ Uint32 nullFlagOffset = AttributeOffset::getNullFlagOffset(attrDes2);
+ Uint32 nullFlagBitOffset = AttributeOffset::getNullFlagBitOffset(attrDes2);
+ Uint32 nullWordOffset = nullFlagOffset + regTabPtr->tupNullIndex;
+ ndbrequire((nullFlagOffset < regTabPtr->tupNullWords) &&
+ (nullWordOffset < tCheckOffset));
+ Uint32 nullBits = tTupleHeader[nullWordOffset];
+
+ if (!nullIndicator) {
+ nullBits &= (~(1 << nullFlagBitOffset));
+ ljam();
+ tTupleHeader[nullWordOffset] = nullBits;
+ return updateFixedSizeTHManyWordNotNULL(inBuffer,
+ attrDescriptor,
+ attrDes2);
+ } else {
+ Uint32 newIndex = tInBufIndex + 1;
+ if (newIndex <= tInBufLen) {
+ nullBits |= (1 << nullFlagBitOffset);
+ ljam();
+ tTupleHeader[nullWordOffset] = nullBits;
+ tInBufIndex = newIndex;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ return false;
+ }//if
+ }//if
+}//Dbtup::updateFixedSizeTHManyWordNULLable()
+
+bool
+Dbtup::updateVariableSizedAttr(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateVariableSizedAttr()
+
+bool
+Dbtup::updateVarSizeUnlimitedNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateVarSizeUnlimitedNotNULL()
+
+bool
+Dbtup::updateVarSizeUnlimitedNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateVarSizeUnlimitedNULLable()
+
+bool
+Dbtup::updateBigVarSizeNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateBigVarSizeNotNULL()
+
+bool
+Dbtup::updateBigVarSizeNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateBigVarSizeNULLable()
+
+bool
+Dbtup::updateSmallVarSizeNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateSmallVarSizeNotNULL()
+
+bool
+Dbtup::updateSmallVarSizeNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateSmallVarSizeNULLable()
+
+bool
+Dbtup::updateDynFixedSize(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateDynFixedSize()
+
+bool
+Dbtup::updateDynVarSizeUnlimited(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateDynVarSizeUnlimited()
+
+bool
+Dbtup::updateDynBigVarSize(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateDynBigVarSize()
+
+bool
+Dbtup::updateDynSmallVarSize(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ ljam();
+ terrorCode = ZVAR_SIZED_NOT_SUPPORTED;
+ return false;
+}//Dbtup::updateDynSmallVarSize()
+
+Uint32
+Dbtup::read_psuedo(Uint32 attrId, Uint32* outBuffer){
+ Uint32 tmp[sizeof(SignalHeader)+25];
+ Signal * signal = (Signal*)&tmp;
+ switch(attrId){
+ case AttributeHeader::FRAGMENT:
+ * outBuffer = operPtr.p->fragId >> 1; // remove "hash" bit
+ return 1;
+ case AttributeHeader::FRAGMENT_MEMORY:
+ {
+ Uint64 tmp= fragptr.p->noOfPages;
+ tmp*= 32768;
+ memcpy(outBuffer,&tmp,8);
+ }
+ return 2;
+ case AttributeHeader::ROW_SIZE:
+ * outBuffer = tabptr.p->tupheadsize << 2;
+ return 1;
+ case AttributeHeader::ROW_COUNT:
+ case AttributeHeader::COMMIT_COUNT:
+ signal->theData[0] = operPtr.p->userpointer;
+ signal->theData[1] = attrId;
+
+ EXECUTE_DIRECT(DBLQH, GSN_READ_PSUEDO_REQ, signal, 2);
+ outBuffer[0] = signal->theData[0];
+ outBuffer[1] = signal->theData[1];
+ return 2;
+ case AttributeHeader::RANGE_NO:
+ signal->theData[0] = operPtr.p->userpointer;
+ signal->theData[1] = attrId;
+
+ EXECUTE_DIRECT(DBLQH, GSN_READ_PSUEDO_REQ, signal, 2);
+ outBuffer[0] = signal->theData[0];
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+bool
+Dbtup::readBitsNotNULL(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2);
+ Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor);
+ Uint32 indexBuf = tOutBufIndex;
+ Uint32 newIndexBuf = indexBuf + ((bitCount + 31) >> 5);
+ Uint32 maxRead = tMaxRead;
+
+ if (newIndexBuf <= maxRead) {
+ ljam();
+ ahOut->setDataSize((bitCount + 31) >> 5);
+ tOutBufIndex = newIndexBuf;
+
+ BitmaskImpl::getField(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos,
+ bitCount,
+ outBuffer+indexBuf);
+
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ return false;
+ }//if
+}
+
+bool
+Dbtup::readBitsNULLable(Uint32* outBuffer,
+ AttributeHeader* ahOut,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2);
+ Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor);
+
+ Uint32 indexBuf = tOutBufIndex;
+ Uint32 newIndexBuf = indexBuf + ((bitCount + 31) >> 5);
+ Uint32 maxRead = tMaxRead;
+
+ if(BitmaskImpl::get(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos))
+ {
+ ljam();
+ ahOut->setNULL();
+ return true;
+ }
+
+
+ if (newIndexBuf <= maxRead) {
+ ljam();
+ ahOut->setDataSize((bitCount + 31) >> 5);
+ tOutBufIndex = newIndexBuf;
+ BitmaskImpl::getField(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos+1,
+ bitCount,
+ outBuffer+indexBuf);
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZTRY_TO_READ_TOO_MUCH_ERROR;
+ return false;
+ }//if
+}
+
+bool
+Dbtup::updateBitsNotNULL(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ Uint32 indexBuf = tInBufIndex;
+ Uint32 inBufLen = tInBufLen;
+ AttributeHeader ahIn(inBuffer[indexBuf]);
+ Uint32 nullIndicator = ahIn.isNULL();
+ Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2);
+ Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor);
+ Uint32 newIndex = indexBuf + 1 + ((bitCount + 31) >> 5);
+
+ if (newIndex <= inBufLen) {
+ if (!nullIndicator) {
+ BitmaskImpl::setField(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos,
+ bitCount,
+ inBuffer+indexBuf+1);
+ tInBufIndex = newIndex;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZNOT_NULL_ATTR;
+ return false;
+ }//if
+ } else {
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ return false;
+ }//if
+ return true;
+}
+
+bool
+Dbtup::updateBitsNULLable(Uint32* inBuffer,
+ Uint32 attrDescriptor,
+ Uint32 attrDes2)
+{
+ Tablerec* const regTabPtr = tabptr.p;
+ AttributeHeader ahIn(inBuffer[tInBufIndex]);
+ Uint32 indexBuf = tInBufIndex;
+ Uint32 nullIndicator = ahIn.isNULL();
+ Uint32 pos = AttributeOffset::getNullFlagPos(attrDes2);
+ Uint32 bitCount = AttributeDescriptor::getArraySize(attrDescriptor);
+
+ if (!nullIndicator) {
+ BitmaskImpl::clear(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos);
+ BitmaskImpl::setField(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos+1,
+ bitCount,
+ inBuffer+indexBuf+1);
+
+ Uint32 newIndex = indexBuf + 1 + ((bitCount + 31) >> 5);
+ tInBufIndex = newIndex;
+ return true;
+ } else {
+ Uint32 newIndex = tInBufIndex + 1;
+ if (newIndex <= tInBufLen) {
+ ljam();
+ BitmaskImpl::set(regTabPtr->tupNullWords,
+ tTupleHeader+regTabPtr->tupNullIndex,
+ pos);
+
+ tInBufIndex = newIndex;
+ return true;
+ } else {
+ ljam();
+ terrorCode = ZAI_INCONSISTENCY_ERROR;
+ return false;
+ }//if
+ }//if
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupStoredProcDef.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupStoredProcDef.cpp
new file mode 100644
index 00000000000..3b957688a1c
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupStoredProcDef.cpp
@@ -0,0 +1,230 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(18000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(18000 + __LINE__); }
+
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/* ------------ADD/DROP STORED PROCEDURE MODULE ------------------- */
+/* ---------------------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execSTORED_PROCREQ(Signal* signal)
+{
+ OperationrecPtr regOperPtr;
+ TablerecPtr regTabPtr;
+ ljamEntry();
+ regOperPtr.i = signal->theData[0];
+ ptrCheckGuard(regOperPtr, cnoOfOprec, operationrec);
+ regTabPtr.i = signal->theData[1];
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+
+ Uint32 requestInfo = signal->theData[3];
+
+ ndbrequire(regOperPtr.p->transstate == IDLE ||
+ ((regOperPtr.p->transstate == ERROR_WAIT_STORED_PROCREQ) &&
+ (requestInfo == ZSTORED_PROCEDURE_DELETE)));
+ ndbrequire(regTabPtr.p->tableStatus == DEFINED);
+ switch (requestInfo) {
+ case ZSCAN_PROCEDURE:
+ ljam();
+ scanProcedure(signal,
+ regOperPtr.p,
+ signal->theData[4]);
+ break;
+ case ZCOPY_PROCEDURE:
+ ljam();
+ copyProcedure(signal, regTabPtr, regOperPtr.p);
+ break;
+ case ZSTORED_PROCEDURE_DELETE:
+ ljam();
+ deleteScanProcedure(signal, regOperPtr.p);
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+}//Dbtup::execSTORED_PROCREQ()
+
+void Dbtup::deleteScanProcedure(Signal* signal,
+ Operationrec* regOperPtr)
+{
+ StoredProcPtr storedPtr;
+ Uint32 storedProcId = signal->theData[4];
+ c_storedProcPool.getPtr(storedPtr, storedProcId);
+ ndbrequire(storedPtr.p->storedCode == ZSCAN_PROCEDURE);
+ ndbrequire(storedPtr.p->storedCounter == 0);
+ Uint32 firstAttrinbuf = storedPtr.p->storedLinkFirst;
+ storedPtr.p->storedCode = ZSTORED_PROCEDURE_FREE;
+ storedPtr.p->storedLinkFirst = RNIL;
+ storedPtr.p->storedLinkLast = RNIL;
+ storedPtr.p->storedProcLength = 0;
+ c_storedProcPool.release(storedPtr);
+ freeAttrinbufrec(firstAttrinbuf);
+ regOperPtr->currentAttrinbufLen = 0;
+ regOperPtr->transstate = IDLE;
+ signal->theData[0] = regOperPtr->userpointer;
+ signal->theData[1] = storedProcId;
+ sendSignal(regOperPtr->userblockref, GSN_STORED_PROCCONF, signal, 2, JBB);
+}//Dbtup::deleteScanProcedure()
+
+void Dbtup::scanProcedure(Signal* signal,
+ Operationrec* regOperPtr,
+ Uint32 lenAttrInfo)
+{
+//--------------------------------------------------------
+// We introduce the maxCheck so that there is always one
+// stored procedure entry free for copy procedures. Thus
+// no amount of scanning can cause problems for the node
+// recovery functionality.
+//--------------------------------------------------------
+ StoredProcPtr storedPtr;
+ c_storedProcPool.seize(storedPtr);
+ ndbrequire(storedPtr.i != RNIL);
+ storedPtr.p->storedCode = ZSCAN_PROCEDURE;
+ storedPtr.p->storedCounter = 0;
+ storedPtr.p->storedProcLength = lenAttrInfo;
+ storedPtr.p->storedLinkFirst = RNIL;
+ storedPtr.p->storedLinkLast = RNIL;
+ regOperPtr->transstate = WAIT_STORED_PROCEDURE_ATTR_INFO;
+ regOperPtr->attrinbufLen = lenAttrInfo;
+ regOperPtr->currentAttrinbufLen = 0;
+ regOperPtr->pageOffset = storedPtr.i;
+}//Dbtup::scanProcedure()
+
+void Dbtup::copyProcedure(Signal* signal,
+ TablerecPtr regTabPtr,
+ Operationrec* regOperPtr)
+{
+ Uint32 TnoOfAttributes = regTabPtr.p->noOfAttr;
+ scanProcedure(signal,
+ regOperPtr,
+ TnoOfAttributes);
+
+ Uint32 length = 0;
+ for (Uint32 Ti = 0; Ti < TnoOfAttributes; Ti++) {
+ AttributeHeader::init(&signal->theData[length + 1], Ti, 0);
+ length++;
+ if (length == 24) {
+ ljam();
+ ndbrequire(storedProcedureAttrInfo(signal, regOperPtr, length, 1, true));
+ length = 0;
+ }//if
+ }//for
+ if (length != 0) {
+ ljam();
+ ndbrequire(storedProcedureAttrInfo(signal, regOperPtr, length, 1, true));
+ }//if
+ ndbrequire(regOperPtr->currentAttrinbufLen == 0);
+}//Dbtup::copyProcedure()
+
+bool Dbtup::storedProcedureAttrInfo(Signal* signal,
+ Operationrec* regOperPtr,
+ Uint32 length,
+ Uint32 firstWord,
+ bool copyProcedure)
+{
+ AttrbufrecPtr regAttrPtr;
+ Uint32 RnoFree = cnoFreeAttrbufrec;
+ if (ERROR_INSERTED(4004) && !copyProcedure) {
+ CLEAR_ERROR_INSERT_VALUE;
+ storedSeizeAttrinbufrecErrorLab(signal, regOperPtr);
+ return false;
+ }//if
+ regOperPtr->currentAttrinbufLen += length;
+ ndbrequire(regOperPtr->currentAttrinbufLen <= regOperPtr->attrinbufLen);
+ if ((RnoFree > MIN_ATTRBUF) ||
+ (copyProcedure)) {
+ ljam();
+ regAttrPtr.i = cfirstfreeAttrbufrec;
+ ptrCheckGuard(regAttrPtr, cnoOfAttrbufrec, attrbufrec);
+ regAttrPtr.p->attrbuf[ZBUF_DATA_LEN] = 0;
+ cfirstfreeAttrbufrec = regAttrPtr.p->attrbuf[ZBUF_NEXT];
+ cnoFreeAttrbufrec = RnoFree - 1;
+ regAttrPtr.p->attrbuf[ZBUF_NEXT] = RNIL;
+ } else {
+ ljam();
+ storedSeizeAttrinbufrecErrorLab(signal, regOperPtr);
+ return false;
+ }//if
+ if (regOperPtr->firstAttrinbufrec == RNIL) {
+ ljam();
+ regOperPtr->firstAttrinbufrec = regAttrPtr.i;
+ }//if
+ regAttrPtr.p->attrbuf[ZBUF_NEXT] = RNIL;
+ if (regOperPtr->lastAttrinbufrec != RNIL) {
+ AttrbufrecPtr tempAttrinbufptr;
+ ljam();
+ tempAttrinbufptr.i = regOperPtr->lastAttrinbufrec;
+ ptrCheckGuard(tempAttrinbufptr, cnoOfAttrbufrec, attrbufrec);
+ tempAttrinbufptr.p->attrbuf[ZBUF_NEXT] = regAttrPtr.i;
+ }//if
+ regOperPtr->lastAttrinbufrec = regAttrPtr.i;
+
+ regAttrPtr.p->attrbuf[ZBUF_DATA_LEN] = length;
+ MEMCOPY_NO_WORDS(&regAttrPtr.p->attrbuf[0],
+ &signal->theData[firstWord],
+ length);
+
+ if (regOperPtr->currentAttrinbufLen < regOperPtr->attrinbufLen) {
+ ljam();
+ return true;
+ }//if
+ if (ERROR_INSERTED(4005) && !copyProcedure) {
+ CLEAR_ERROR_INSERT_VALUE;
+ storedSeizeAttrinbufrecErrorLab(signal, regOperPtr);
+ return false;
+ }//if
+
+ StoredProcPtr storedPtr;
+ c_storedProcPool.getPtr(storedPtr, (Uint32)regOperPtr->pageOffset);
+ ndbrequire(storedPtr.p->storedCode == ZSCAN_PROCEDURE);
+
+ regOperPtr->currentAttrinbufLen = 0;
+ storedPtr.p->storedLinkFirst = regOperPtr->firstAttrinbufrec;
+ storedPtr.p->storedLinkLast = regOperPtr->lastAttrinbufrec;
+ regOperPtr->firstAttrinbufrec = RNIL;
+ regOperPtr->lastAttrinbufrec = RNIL;
+ regOperPtr->transstate = IDLE;
+ signal->theData[0] = regOperPtr->userpointer;
+ signal->theData[1] = storedPtr.i;
+ sendSignal(regOperPtr->userblockref, GSN_STORED_PROCCONF, signal, 2, JBB);
+ return true;
+}//Dbtup::storedProcedureAttrInfo()
+
+void Dbtup::storedSeizeAttrinbufrecErrorLab(Signal* signal,
+ Operationrec* regOperPtr)
+{
+ StoredProcPtr storedPtr;
+ c_storedProcPool.getPtr(storedPtr, (Uint32)regOperPtr->pageOffset);
+ ndbrequire(storedPtr.p->storedCode == ZSCAN_PROCEDURE);
+
+ storedPtr.p->storedLinkFirst = regOperPtr->firstAttrinbufrec;
+ regOperPtr->firstAttrinbufrec = RNIL;
+ regOperPtr->lastAttrinbufrec = RNIL;
+ regOperPtr->transstate = ERROR_WAIT_STORED_PROCREQ;
+ signal->theData[0] = regOperPtr->userpointer;
+ signal->theData[1] = ZSTORED_SEIZE_ATTRINBUFREC_ERROR;
+ signal->theData[2] = regOperPtr->pageOffset;
+ sendSignal(regOperPtr->userblockref, GSN_STORED_PROCREF, signal, 3, JBB);
+}//Dbtup::storedSeizeAttrinbufrecErrorLab()
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupSystemRestart.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupSystemRestart.cpp
new file mode 100644
index 00000000000..33d63e8ce49
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupSystemRestart.cpp
@@ -0,0 +1,1021 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+
+#define ljam() { jamLine(26000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(26000 + __LINE__); }
+
+/* **************************************************************** */
+/* ********************* SYSTEM RESTART MANAGER ******************* */
+/* **************************************************************** */
+/***************************************************************/
+/* CHECK RESTART STATE AND SET NEW STATE CALLED IN OPEN, */
+/* READ AND COPY STATES */
+/***************************************************************/
+void Dbtup::execTUP_SRREQ(Signal* signal)
+{
+ RestartInfoRecordPtr riPtr;
+ PendingFileOpenInfoPtr pfoiPtr;
+
+ ljamEntry();
+ Uint32 userPtr = signal->theData[0];
+ Uint32 userBlockref = signal->theData[1];
+ Uint32 tableId = signal->theData[2];
+ Uint32 fragId = signal->theData[3];
+ Uint32 checkpointNumber = signal->theData[4];
+
+ seizeRestartInfoRecord(riPtr);
+
+ riPtr.p->sriUserptr = userPtr;
+ riPtr.p->sriBlockref = userBlockref;
+ riPtr.p->sriState = OPENING_DATA_FILE;
+ riPtr.p->sriCheckpointVersion = checkpointNumber;
+ riPtr.p->sriFragid = fragId;
+ riPtr.p->sriTableId = tableId;
+
+ /* OPEN THE DATA FILE IN THE FOLLOWING FORM */
+ /* D5/DBTUP/T<TABID>/F<FRAGID>/S<CHECKPOINT_NUMBER>.DATA */
+ Uint32 fileType = 1; /* VERSION */
+ fileType = (fileType << 8) | 0; /* .DATA */
+ fileType = (fileType << 8) | 5; /* D5 */
+ fileType = (fileType << 8) | 0xff; /* DON'T USE P DIRECTORY LEVEL */
+ Uint32 fileFlag = 0; /* READ ONLY */
+
+ seizePendingFileOpenInfoRecord(pfoiPtr);
+ pfoiPtr.p->pfoOpenType = LCP_DATA_FILE_READ;
+ pfoiPtr.p->pfoRestartInfoP = riPtr.i;
+
+ signal->theData[0] = cownref;
+ signal->theData[1] = pfoiPtr.i;
+ signal->theData[2] = tableId;
+ signal->theData[3] = fragId;
+ signal->theData[4] = checkpointNumber;
+ signal->theData[5] = fileType;
+ signal->theData[6] = fileFlag;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+ return;
+}//Dbtup::execTUP_SRREQ()
+
+void Dbtup::seizeRestartInfoRecord(RestartInfoRecordPtr& riPtr)
+{
+ riPtr.i = cfirstfreeSri;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+ cfirstfreeSri = riPtr.p->sriNextRec;
+ riPtr.p->sriNextRec = RNIL;
+}//Dbtup::seizeRestartInfoRecord()
+
+void Dbtup::rfrReadRestartInfoLab(Signal* signal, RestartInfoRecordPtr riPtr)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+
+ seizeDiskBufferSegmentRecord(dbsiPtr);
+ riPtr.p->sriDataBufferSegmentP = dbsiPtr.i;
+ Uint32 retPageRef = RNIL;
+ Uint32 noAllocPages = 1;
+ Uint32 noOfPagesAllocated;
+ {
+ /**
+ * Use low pages for 0-pages during SR
+ * bitmask of free pages is kept in c_sr_free_page_0
+ */
+ Uint32 tmp = c_sr_free_page_0;
+ for(Uint32 i = 1; i<(1+MAX_PARALLELL_TUP_SRREQ); i++){
+ if(tmp & (1 << i)){
+ retPageRef = i;
+ c_sr_free_page_0 = tmp & (~(1 << i));
+ break;
+ }
+ }
+ ndbrequire(retPageRef != RNIL);
+ }
+
+ dbsiPtr.p->pdxDataPage[0] = retPageRef;
+ dbsiPtr.p->pdxNumDataPages = 1;
+ dbsiPtr.p->pdxFilePage = 0;
+ rfrReadNextDataSegment(signal, riPtr, dbsiPtr);
+ dbsiPtr.p->pdxOperation = CHECKPOINT_DATA_READ_PAGE_ZERO;
+}//Dbtup::rfrReadRestartInfoLab()
+
+/***************************************************************/
+/* THE RESTART INFORMATION IS NOW READ INTO THE DATA BUFFER */
+/* USE THE RESTART INFORMATION TO INITIATE THE RESTART RECORD */
+/***************************************************************/
+void
+Dbtup::rfrInitRestartInfoLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ Uint32 TzeroDataPage[64];
+ Uint32 Ti;
+ FragrecordPtr regFragPtr;
+ LocalLogInfoPtr lliPtr;
+ PagePtr pagePtr;
+ RestartInfoRecordPtr riPtr;
+ TablerecPtr regTabPtr;
+
+ riPtr.i = dbsiPtr.p->pdxRestartInfoP;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+
+ regTabPtr.i = riPtr.p->sriTableId;
+ ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec);
+
+ Uint32 fragId = riPtr.p->sriFragid;
+ getFragmentrec(regFragPtr, fragId, regTabPtr.p);
+ riPtr.p->sriFragP = regFragPtr.i;
+
+ /* ----- PAGE ALLOCATION --- */
+ /* ALLOCATE PAGES TO FRAGMENT, INSERT THEM INTO PAGE RANGE TABLE AND */
+ /* ALSO CONVERT THEM INTO EMPTY PAGES AND INSERT THEM INTO THE EMPTY LIST */
+ /* OF THE FRAGMENT. SET ALL LISTS OF FREE PAGES TO RNIL */
+
+ ndbrequire(cfirstfreerange != RNIL);
+ pagePtr.i = dbsiPtr.p->pdxDataPage[0];
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ for (Ti = 0; Ti < 63; Ti++) {
+ /***************************************************************/
+ // Save Important content from Page zero in stack variable so
+ // that we can immediately release page zero.
+ /***************************************************************/
+ TzeroDataPage[Ti] = pagePtr.p->pageWord[Ti];
+ }//for
+ /************************************************************************/
+ /* NOW WE DON'T NEED THE RESTART INFO BUFFER PAGE ANYMORE */
+ /* LETS REMOVE IT AND REUSE THE SEGMENT FOR REAL DATA PAGES */
+ /* REMOVE ONE PAGE ONLY, PAGEP IS ALREADY SET TO THE RESTART INFO PAGE */
+ /************************************************************************/
+ {
+ ndbrequire(pagePtr.i > 0 && pagePtr.i <= MAX_PARALLELL_TUP_SRREQ);
+ c_sr_free_page_0 |= (1 << pagePtr.i);
+ }
+
+ Uint32 undoFileVersion = TzeroDataPage[ZSRI_UNDO_FILE_VER];
+ lliPtr.i = (undoFileVersion << 2) + (regTabPtr.i & 0x3);
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ riPtr.p->sriLocalLogInfoP = lliPtr.i;
+
+ ndbrequire(regFragPtr.p->fragTableId == regTabPtr.i);
+ ndbrequire(regFragPtr.p->fragmentId == fragId);
+
+ regFragPtr.p->fragStatus = SYSTEM_RESTART;
+
+ regFragPtr.p->noCopyPagesAlloc = TzeroDataPage[ZSRI_NO_COPY_PAGES_ALLOC];
+
+ riPtr.p->sriCurDataPageFromBuffer = 0;
+ riPtr.p->sriNumDataPages = TzeroDataPage[ZSRI_NO_OF_FRAG_PAGES_POS];
+
+ ndbrequire(riPtr.p->sriNumDataPages >= regFragPtr.p->noOfPages);
+ const Uint32 pageCount = riPtr.p->sriNumDataPages - regFragPtr.p->noOfPages;
+ if(pageCount > 0){
+ Uint32 noAllocPages = allocFragPages(regFragPtr.p, pageCount);
+ ndbrequire(noAllocPages == pageCount);
+ }//if
+ ndbrequire(getNoOfPages(regFragPtr.p) == riPtr.p->sriNumDataPages);
+
+/***************************************************************/
+// Set the variables on fragment record which might have been
+// affected by allocFragPages.
+/***************************************************************/
+
+ regFragPtr.p->emptyPrimPage = TzeroDataPage[ZSRI_EMPTY_PRIM_PAGE];
+ regFragPtr.p->thFreeFirst = TzeroDataPage[ZSRI_TH_FREE_FIRST];
+ regFragPtr.p->thFreeCopyFirst = TzeroDataPage[ZSRI_TH_FREE_COPY_FIRST];
+
+/***************************************************************/
+/* THE RESTART INFORMATION IS NOW READ INTO THE DATA BUFFER */
+/* USE THE RESTART INFORMATION TO INITIATE THE FRAGMENT */
+/***************************************************************/
+ /**
+ * IF THIS UNDO FILE IS NOT OPEN, IT WILL BE OPENED HERE AND THE EXECUTION
+ * WILL CONTINUE WHEN THE FSOPENCONF IS ENTERED.
+ * IF IT'S ALREADY IN USE THE EXECUTION WILL CONTINUE BY A
+ * CONTINUE B SIGNAL
+ */
+ if (lliPtr.p->lliActiveLcp == 0) {
+ PendingFileOpenInfoPtr pfoiPtr;
+ ljam();
+/***************************************************************/
+/* OPEN THE UNDO FILE FOR READ */
+/* THE FILE HANDLE WILL BE SET IN THE LOCAL_LOG_INFO_REC */
+/* UPON FSOPENCONF */
+/***************************************************************/
+ cnoOfLocalLogInfo++;
+ /* F_LEVEL NOT USED */
+ Uint32 fileType = 1; /* VERSION */
+ fileType = (fileType << 8) | 2; /* .LOCLOG */
+ fileType = (fileType << 8) | 6; /* D6 */
+ fileType = (fileType << 8) | 0xff; /* DON'T USE P DIRECTORY LEVEL */
+ Uint32 fileFlag = 0; /* READ ONLY */
+
+ seizePendingFileOpenInfoRecord(pfoiPtr);
+ pfoiPtr.p->pfoOpenType = LCP_UNDO_FILE_READ;
+ pfoiPtr.p->pfoRestartInfoP = riPtr.i;
+
+ signal->theData[0] = cownref;
+ signal->theData[1] = pfoiPtr.i;
+ signal->theData[2] = lliPtr.i;
+ signal->theData[3] = 0xFFFFFFFF;
+ signal->theData[4] = undoFileVersion;
+ signal->theData[5] = fileType;
+ signal->theData[6] = fileFlag;
+ sendSignal(NDBFS_REF, GSN_FSOPENREQ, signal, 7, JBA);
+
+ lliPtr.p->lliPrevRecordId = 0;
+ lliPtr.p->lliActiveLcp = 1;
+ lliPtr.p->lliNumFragments = 1;
+ } else {
+ ljam();
+ signal->theData[0] = ZCONT_LOAD_DP;
+ signal->theData[1] = riPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ lliPtr.p->lliNumFragments++;
+ }//if
+ /* RETAIN THE HIGH- AND LOWSCORE ID:S OF THE LOGRECORD POSITIONS. WE HAVE TO EXECUTE THE */
+ /* UNDO LOG BETWEEN THE END AND START RECORDS FOR ALL RECORDS THAT INCLUDE FRAGMENTS OF */
+ /* THE RIGHT CHECKPOINT VERSION TO COMPLETE THE OPERATION WE HAVE TO RUN ALL LOGS THAT */
+ /* HAS THE NUMBER OF LCP ELEMENT GREATER THAN 0, I.E. IS INCLUDED. */
+ if (TzeroDataPage[ZSRI_UNDO_LOG_END_REC_ID] > lliPtr.p->lliPrevRecordId) {
+ ljam();
+ lliPtr.p->lliPrevRecordId = TzeroDataPage[ZSRI_UNDO_LOG_END_REC_ID];
+ lliPtr.p->lliEndPageId = TzeroDataPage[ZSRI_UNDO_LOG_END_PAGE_ID];
+ }//if
+ return;
+}//Dbtup::rfrInitRestartInfoLab()
+
+/***************************************************************/
+/* LOAD THE NEXT DATA PAGE SEGMENT INTO MEMORY */
+/***************************************************************/
+void Dbtup::rfrLoadDataPagesLab(Signal* signal, RestartInfoRecordPtr riPtr, DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ FragrecordPtr regFragPtr;
+
+ if (riPtr.p->sriCurDataPageFromBuffer >= riPtr.p->sriNumDataPages) {
+ ljam();
+ rfrCompletedLab(signal, riPtr);
+ return;
+ }//if
+ Uint32 startPage = riPtr.p->sriCurDataPageFromBuffer;
+ Uint32 endPage;
+ if ((startPage + ZDB_SEGMENT_SIZE) < riPtr.p->sriNumDataPages) {
+ ljam();
+ endPage = startPage + ZDB_SEGMENT_SIZE;
+ } else {
+ ljam();
+ endPage = riPtr.p->sriNumDataPages;
+ }//if
+ regFragPtr.i = riPtr.p->sriFragP;
+ ptrCheckGuard(regFragPtr, cnoOfFragrec, fragrecord);
+ ndbrequire((endPage - startPage) <= 16);
+ Uint32 i = 0;
+ for (Uint32 pageId = startPage; pageId < endPage; pageId++) {
+ ljam();
+ dbsiPtr.p->pdxDataPage[i] = getRealpid(regFragPtr.p, pageId);
+ i++;
+ }//for
+ dbsiPtr.p->pdxNumDataPages = endPage - startPage; /* SET THE NUMBER OF DATA PAGES */
+ riPtr.p->sriCurDataPageFromBuffer = endPage;
+ dbsiPtr.p->pdxFilePage = startPage + 1;
+ rfrReadNextDataSegment(signal, riPtr, dbsiPtr);
+ return;
+}//Dbtup::rfrLoadDataPagesLab()
+
+void Dbtup::rfrCompletedLab(Signal* signal, RestartInfoRecordPtr riPtr)
+{
+ PendingFileOpenInfoPtr pfoPtr;
+/* ---------------------------------------------------------------------- */
+/* CLOSE THE DATA FILE BEFORE SENDING TUP_SRCONF */
+/* ---------------------------------------------------------------------- */
+ seizePendingFileOpenInfoRecord(pfoPtr);
+ pfoPtr.p->pfoOpenType = LCP_DATA_FILE_READ;
+ pfoPtr.p->pfoCheckpointInfoP = riPtr.i;
+
+ signal->theData[0] = riPtr.p->sriDataFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = pfoPtr.i;
+ signal->theData[3] = 0;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+}//Dbtup::rfrCompletedLab()
+
+void Dbtup::rfrClosedDataFileLab(Signal* signal, Uint32 restartIndex)
+{
+ RestartInfoRecordPtr riPtr;
+ DiskBufferSegmentInfoPtr dbsiPtr;
+
+ riPtr.i = restartIndex;
+ ptrCheckGuard(riPtr, cnoOfRestartInfoRec, restartInfoRecord);
+ riPtr.p->sriDataFileHandle = RNIL;
+ dbsiPtr.i = riPtr.p->sriDataBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ releaseDiskBufferSegmentRecord(dbsiPtr);
+ signal->theData[0] = riPtr.p->sriUserptr;
+ signal->theData[1] = riPtr.p->sriFragP;
+ sendSignal(riPtr.p->sriBlockref, GSN_TUP_SRCONF, signal, 2, JBB);
+ releaseRestartInfoRecord(riPtr);
+}//Dbtup::rfrClosedDataFileLab()
+
+/* ---------------------------------------------------------------- */
+/* ---------------------- EXECUTE LOCAL LOG ---------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::execSTART_RECREQ(Signal* signal)
+{
+ ljamEntry();
+ clqhUserpointer = signal->theData[0];
+ clqhBlockref = signal->theData[1];
+
+ for (int i = 0; i < ZNO_CHECKPOINT_RECORDS; i++){
+ cSrUndoRecords[i] = 0;
+ }//for
+
+ if (cnoOfLocalLogInfo == 0) {
+ ljam();
+/* ---------------------------------------------------------------- */
+/* THERE WERE NO LOCAL LOGS TO EXECUTE IN THIS SYSTEM RESTART */
+/* ---------------------------------------------------------------- */
+ xlcRestartCompletedLab(signal);
+ return;
+ }//if
+ LocalLogInfoPtr lliPtr;
+ for (lliPtr.i = 0; lliPtr.i < 16; lliPtr.i++) {
+ ljam();
+ ptrAss(lliPtr, localLogInfo);
+ if (lliPtr.p->lliActiveLcp == 1) {
+ ljam();
+ signal->theData[0] = ZSTART_EXEC_UNDO_LOG;
+ signal->theData[1] = lliPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ }//if
+ }//for
+ return;
+}//Dbtup::execSTART_RECREQ()
+
+void Dbtup::closeExecUndoLogLab(Signal* signal, LocalLogInfoPtr lliPtr)
+{
+ PendingFileOpenInfoPtr pfoPtr;
+/* ---------------------------------------------------------------------- */
+/* CLOSE THE UNDO LOG BEFORE COMPLETION OF THE SYSTEM RESTART */
+/* ---------------------------------------------------------------------- */
+ seizePendingFileOpenInfoRecord(pfoPtr);
+ pfoPtr.p->pfoOpenType = LCP_UNDO_FILE_READ;
+ pfoPtr.p->pfoCheckpointInfoP = lliPtr.i;
+
+ signal->theData[0] = lliPtr.p->lliUndoFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = pfoPtr.i;
+ signal->theData[3] = 0;
+ sendSignal(NDBFS_REF, GSN_FSCLOSEREQ, signal, 4, JBA);
+ return;
+}//Dbtup::closeExecUndoLogLab()
+
+void Dbtup::endExecUndoLogLab(Signal* signal, Uint32 lliIndex)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ LocalLogInfoPtr lliPtr;
+
+ lliPtr.i = lliIndex;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+ lliPtr.p->lliUndoFileHandle = RNIL;
+ lliPtr.p->lliActiveLcp = 0;
+/* ---------------------------------------------------------------------- */
+/* WE HAVE NOW CLOSED THE LOG. WE WAIT FOR ALL LOCAL LOGS TO */
+/* COMPLETE LOG EXECUTION BEFORE SENDING THE RESPONSE TO LQH. */
+/* ---------------------------------------------------------------------- */
+ dbsiPtr.i = lliPtr.p->lliUndoBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ freeDiskBufferSegmentRecord(signal, dbsiPtr);
+ lliPtr.p->lliUndoBufferSegmentP = RNIL;
+ for (lliPtr.i = 0; lliPtr.i < 16; lliPtr.i++) {
+ ljam();
+ ptrAss(lliPtr, localLogInfo);
+ if (lliPtr.p->lliActiveLcp == 1) {
+ ljam();
+ return;
+ }//if
+ }//for
+ xlcRestartCompletedLab(signal);
+ return;
+}//Dbtup::endExecUndoLogLab()
+
+void Dbtup::xlcRestartCompletedLab(Signal* signal)
+{
+ cnoOfLocalLogInfo = 0;
+
+ signal->theData[0] = NDB_LE_UNDORecordsExecuted;
+ signal->theData[1] = DBTUP; // From block
+ signal->theData[2] = 0; // Total records executed
+ for (int i = 0; i < 10; i++) {
+ if (i < ZNO_CHECKPOINT_RECORDS){
+ signal->theData[i+3] = cSrUndoRecords[i];
+ signal->theData[2] += cSrUndoRecords[i];
+ } else {
+ signal->theData[i+3] = 0; // Unsused data
+ }//if
+ }//for
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 12, JBB);
+
+/* ---------------------------------------------------------------------- */
+/* ALL LOCAL LOGS HAVE COMPLETED. WE HAVE COMPLETED OUR PART OF THE */
+/* SYSTEM RESTART. */
+/* ---------------------------------------------------------------------- */
+ signal->theData[0] = clqhUserpointer;
+ sendSignal(clqhBlockref, GSN_START_RECCONF, signal, 1, JBB);
+ return;
+}//Dbtup::xlcRestartCompletedLab()
+
+void Dbtup::startExecUndoLogLab(Signal* signal, Uint32 lliIndex)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ LocalLogInfoPtr lliPtr;
+
+/* ---------------------------------------------------------------------- */
+/* START EXECUTING THE LOG FOR THIS PART. WE BEGIN BY READING THE */
+/* LAST 16 PAGES. */
+/* ---------------------------------------------------------------------- */
+ /* SET THE PREVIOS RECORD TO THE LAST ONE BECAUSE THAT'S WHERE TO START */
+ lliPtr.i = lliIndex;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+
+ allocRestartUndoBufferSegment(signal, dbsiPtr, lliPtr);
+ lliPtr.p->lliUndoBufferSegmentP = dbsiPtr.i;
+ dbsiPtr.p->pdxCheckpointInfoP = lliPtr.i;
+ if (lliPtr.p->lliEndPageId > ((2 * ZUB_SEGMENT_SIZE) - 1)) {
+ ljam();
+ dbsiPtr.p->pdxNumDataPages = ZUB_SEGMENT_SIZE;
+ dbsiPtr.p->pdxFilePage = lliPtr.p->lliEndPageId - (ZUB_SEGMENT_SIZE - 1);
+ } else if (lliPtr.p->lliEndPageId > (ZUB_SEGMENT_SIZE - 1)) {
+ ljam();
+ dbsiPtr.p->pdxNumDataPages = lliPtr.p->lliEndPageId - (ZUB_SEGMENT_SIZE - 1);
+ dbsiPtr.p->pdxFilePage = ZUB_SEGMENT_SIZE;
+ } else {
+ ljam();
+ dbsiPtr.p->pdxNumDataPages = lliPtr.p->lliEndPageId + 1;
+ dbsiPtr.p->pdxFilePage = 0;
+ rfrReadNextUndoSegment(signal, dbsiPtr, lliPtr);
+ return;
+ }//if
+ rfrReadFirstUndoSegment(signal, dbsiPtr, lliPtr);
+ return;
+}//Dbtup::startExecUndoLogLab()
+
+void Dbtup::rfrReadSecondUndoLogLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ LocalLogInfoPtr lliPtr;
+ lliPtr.i = dbsiPtr.p->pdxCheckpointInfoP;
+ ptrCheckGuard(lliPtr, cnoOfParallellUndoFiles, localLogInfo);
+
+ dbsiPtr.p->pdxNumDataPages = ZUB_SEGMENT_SIZE;
+ dbsiPtr.p->pdxFilePage -= ZUB_SEGMENT_SIZE;
+ rfrReadNextUndoSegment(signal, dbsiPtr, lliPtr);
+ return;
+}//Dbtup::rfrReadSecondUndoLogLab()
+
+void Dbtup::readExecUndoLogLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr)
+{
+/* ---------------------------------------------------------------------- */
+/* THE NEXT UNDO LOG RECORD HAS NOT BEEN READ FROM DISK YET. WE WILL*/
+/* READ UPTO 8 PAGES BACKWARDS OF THE UNDO LOG FILE. WE WILL KEEP */
+/* THE LAST 8 PAGES TO ENSURE THAT WE WILL BE ABLE TO READ THE NEXT */
+/* LOG RECORD EVEN IF IT SPANS UPTO 8 PAGES. */
+/* ---------------------------------------------------------------------- */
+ if (dbsiPtr.p->pdxFilePage >= ZUB_SEGMENT_SIZE) {
+ ljam();
+ for (Uint32 i = 0; i < ZUB_SEGMENT_SIZE; i++) {
+ ljam();
+ Uint32 savePageId = dbsiPtr.p->pdxDataPage[i + ZUB_SEGMENT_SIZE];
+ dbsiPtr.p->pdxDataPage[i + ZUB_SEGMENT_SIZE] = dbsiPtr.p->pdxDataPage[i];
+ dbsiPtr.p->pdxDataPage[i] = savePageId;
+ }//for
+ dbsiPtr.p->pdxNumDataPages = ZUB_SEGMENT_SIZE;
+ dbsiPtr.p->pdxFilePage = dbsiPtr.p->pdxFilePage - ZUB_SEGMENT_SIZE;
+ } else {
+ ljam();
+ Uint32 dataPages[16];
+ ndbrequire(dbsiPtr.p->pdxFilePage > 0);
+ ndbrequire(dbsiPtr.p->pdxFilePage <= ZUB_SEGMENT_SIZE);
+ Uint32 i;
+ for (i = 0; i < dbsiPtr.p->pdxFilePage; i++) {
+ ljam();
+ dataPages[i] = dbsiPtr.p->pdxDataPage[i + ZUB_SEGMENT_SIZE];
+ }//for
+ for (i = 0; i < ZUB_SEGMENT_SIZE; i++) {
+ ljam();
+ dataPages[i + dbsiPtr.p->pdxFilePage] = dbsiPtr.p->pdxDataPage[i];
+ }//for
+ Uint32 limitLoop = ZUB_SEGMENT_SIZE + dbsiPtr.p->pdxFilePage;
+ for (i = 0; i < limitLoop; i++) {
+ ljam();
+ dbsiPtr.p->pdxDataPage[i] = dataPages[i];
+ }//for
+ dbsiPtr.p->pdxNumDataPages = dbsiPtr.p->pdxFilePage;
+ dbsiPtr.p->pdxFilePage = 0;
+ }//if
+ rfrReadNextUndoSegment(signal, dbsiPtr, lliPtr);
+ return;
+}//Dbtup::readExecUndoLogLab()
+
+void Dbtup::rfrReadNextDataSegment(Signal* signal, RestartInfoRecordPtr riPtr, DiskBufferSegmentInfoPtr dbsiPtr)
+{
+ dbsiPtr.p->pdxRestartInfoP = riPtr.i;
+ dbsiPtr.p->pdxOperation = CHECKPOINT_DATA_READ;
+ ndbrequire(dbsiPtr.p->pdxNumDataPages <= 8);
+
+ signal->theData[0] = riPtr.p->sriDataFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = dbsiPtr.i;
+ signal->theData[3] = 2;
+ signal->theData[4] = ZBASE_ADDR_PAGE_WORD;
+ signal->theData[5] = dbsiPtr.p->pdxNumDataPages;
+ for (Uint32 i = 0; i < dbsiPtr.p->pdxNumDataPages; i++) {
+ ljam();
+ signal->theData[6 + i] = dbsiPtr.p->pdxDataPage[i];
+ }//for
+ signal->theData[6 + dbsiPtr.p->pdxNumDataPages] = dbsiPtr.p->pdxFilePage;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 15, JBA);
+}//Dbtup::rfrReadNextDataSegment()
+
+/* ---------------------------------------------------------------- */
+/* ------------------- RFR_READ_FIRST_UNDO_SEGMENT ---------------- */
+/* ---------------------------------------------------------------- */
+/* THIS ROUTINE READS IN THE FIRST UNDO SEGMENT INTO THE CURRENTLY */
+/* ACTIVE UNDO BUFFER SEGMENT */
+/* -----------------------------------------------------------------*/
+void Dbtup::rfrReadFirstUndoSegment(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr)
+{
+ dbsiPtr.p->pdxOperation = CHECKPOINT_UNDO_READ_FIRST;
+
+ signal->theData[0] = lliPtr.p->lliUndoFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = dbsiPtr.i;
+ signal->theData[3] = 1;
+ signal->theData[4] = ZBASE_ADDR_UNDO_WORD;
+ signal->theData[5] = dbsiPtr.p->pdxNumDataPages;
+ signal->theData[6] = dbsiPtr.p->pdxDataPage[ZUB_SEGMENT_SIZE];
+ signal->theData[7] = dbsiPtr.p->pdxFilePage;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+}//Dbtup::rfrReadFirstUndoSegment()
+
+/* ---------------------------------------------------------------- */
+/* ------------------- RFR_READ_NEXT_UNDO_SEGMENT ----------------- */
+/* ---------------------------------------------------------------- */
+/* THIS ROUTINE READS IN THE NEXT UNDO SEGMENT INTO THE CURRENTLY */
+/* ACTIVE UNDO BUFFER SEGMENT AND SWITCH TO THE UNACTIVE, READY ONE */
+/* -----------------------------------------------------------------*/
+void Dbtup::rfrReadNextUndoSegment(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr)
+{
+ dbsiPtr.p->pdxOperation = CHECKPOINT_UNDO_READ;
+
+ signal->theData[0] = lliPtr.p->lliUndoFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = dbsiPtr.i;
+ signal->theData[3] = 1;
+ signal->theData[4] = ZBASE_ADDR_UNDO_WORD;
+ signal->theData[5] = dbsiPtr.p->pdxNumDataPages;
+ signal->theData[6] = dbsiPtr.p->pdxDataPage[0];
+ signal->theData[7] = dbsiPtr.p->pdxFilePage;
+ sendSignal(NDBFS_REF, GSN_FSREADREQ, signal, 8, JBA);
+}//Dbtup::rfrReadNextUndoSegment()
+
+void Dbtup::xlcGetNextRecordLab(Signal* signal, DiskBufferSegmentInfoPtr dbsiPtr, LocalLogInfoPtr lliPtr)
+{
+ Uint32 loopCount = 0;
+/* ---------------------------------------------------------------------- */
+/* EXECUTE A NEW SET OF UNDO LOG RECORDS. */
+/* ---------------------------------------------------------------------- */
+ XlcStruct xlcStruct;
+
+ xlcStruct.LliPtr = lliPtr;
+ xlcStruct.DbsiPtr = dbsiPtr;
+
+ do {
+ ljam();
+ loopCount++;
+ if (loopCount == 20) {
+ ljam();
+ signal->theData[0] = ZCONT_EXECUTE_LC;
+ signal->theData[1] = xlcStruct.LliPtr.i;
+ sendSignal(cownref, GSN_CONTINUEB, signal, 2, JBB);
+ return;
+ }//if
+ if (xlcStruct.LliPtr.p->lliPrevRecordId == 0) {
+ ljam();
+ closeExecUndoLogLab(signal, xlcStruct.LliPtr);
+ return;
+ }//if
+ xlcStruct.PageId = xlcStruct.LliPtr.p->lliPrevRecordId >> ZUNDO_RECORD_ID_PAGE_INDEX;
+ xlcStruct.PageIndex = xlcStruct.LliPtr.p->lliPrevRecordId & ZUNDO_RECORD_ID_PAGE_INDEX_MASK;
+ if (xlcStruct.PageId < xlcStruct.DbsiPtr.p->pdxFilePage) {
+ ljam();
+ readExecUndoLogLab(signal, xlcStruct.DbsiPtr, xlcStruct.LliPtr);
+ return;
+ }//if
+ ndbrequire((xlcStruct.PageId - xlcStruct.DbsiPtr.p->pdxFilePage) < 16);
+ xlcStruct.UPPtr.i = xlcStruct.DbsiPtr.p->pdxDataPage[xlcStruct.PageId - xlcStruct.DbsiPtr.p->pdxFilePage];
+ ptrCheckGuard(xlcStruct.UPPtr, cnoOfUndoPage, undoPage);
+ xlcGetLogHeader(xlcStruct);
+ getFragmentrec(xlcStruct.FragPtr, xlcStruct.FragId, xlcStruct.TabPtr.p);
+ if (xlcStruct.FragPtr.i == RNIL) {
+ ljam();
+ continue;
+ }//if
+ if (xlcStruct.FragPtr.p->fragStatus != SYSTEM_RESTART) {
+ ljam();
+ continue;
+ }//if
+ ndbrequire(xlcStruct.LogRecordType < ZNO_CHECKPOINT_RECORDS);
+ cSrUndoRecords[xlcStruct.LogRecordType]++;
+ switch (xlcStruct.LogRecordType) {
+ case ZLCPR_TYPE_INSERT_TH:
+ ljam();
+ xlcInsertTh(xlcStruct);
+ break;
+ case ZLCPR_TYPE_DELETE_TH:
+ ljam();
+ xlcDeleteTh(xlcStruct);
+ break;
+ case ZLCPR_TYPE_UPDATE_TH:
+ ljam();
+ xlcUpdateTh(xlcStruct);
+ break;
+ case ZLCPR_TYPE_INSERT_TH_NO_DATA:
+ ljam();
+ xlcInsertTh(xlcStruct);
+ break;
+ case ZLCPR_ABORT_UPDATE:
+ ljam();
+ xlcAbortUpdate(signal, xlcStruct);
+ break;
+ case ZLCPR_ABORT_INSERT:
+ ljam();
+ xlcAbortInsert(signal, xlcStruct);
+ break;
+ case ZTABLE_DESCRIPTOR:
+ ljam();
+ xlcTableDescriptor(xlcStruct);
+ if (xlcStruct.LliPtr.p->lliNumFragments == 0) {
+ ljam();
+ closeExecUndoLogLab(signal, xlcStruct.LliPtr);
+ return;
+ }//if
+ break;
+ case ZLCPR_UNDO_LOG_PAGE_HEADER:
+ ljam();
+ xlcUndoLogPageHeader(xlcStruct);
+ break;
+ case ZINDICATE_NO_OP_ACTIVE:
+ ljam();
+ xlcIndicateNoOpActive(xlcStruct);
+ break;
+ case ZLCPR_TYPE_UPDATE_GCI:
+ ljam();
+ xlcUpdateGCI(xlcStruct);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+ } while (1);
+}//Dbtup::xlcGetNextRecordLab()
+
+/* ---------------------------------------------------------------- */
+/* ----------------- XLC_GET_LOG_HEADER ---------------------- */
+/* ---------------------------------------------------------------- */
+void Dbtup::xlcGetLogHeader(XlcStruct& xlcStruct)
+{
+ Uint32 pIndex = xlcStruct.PageIndex;
+ Uint32 fragId;
+ Uint32 tableId;
+ Uint32 prevId;
+ if ((pIndex + 4) < ZWORDS_ON_PAGE) {
+ UndoPage* const regUndoPagePtr = xlcStruct.UPPtr.p;
+ ljam();
+ xlcStruct.LogRecordType = regUndoPagePtr->undoPageWord[pIndex];
+ prevId = regUndoPagePtr->undoPageWord[pIndex + 1];
+ tableId = regUndoPagePtr->undoPageWord[pIndex + 2];
+ fragId = regUndoPagePtr->undoPageWord[pIndex + 3];
+ xlcStruct.PageIndex = pIndex + 4;
+ } else {
+ ljam();
+ xlcStruct.LogRecordType = xlcGetLogWord(xlcStruct);
+ prevId = xlcGetLogWord(xlcStruct);
+ tableId = xlcGetLogWord(xlcStruct);
+ fragId = xlcGetLogWord(xlcStruct);
+ }//if
+ xlcStruct.LliPtr.p->lliPrevRecordId = prevId;
+ xlcStruct.FragId = fragId;
+ xlcStruct.TabPtr.i = tableId;
+ ptrCheckGuard(xlcStruct.TabPtr, cnoOfTablerec, tablerec);
+}//Dbtup::xlcGetLogHeader()
+
+/* ------------------------------------------------------------------- */
+/* ---------------------- XLC_GET_LOG_WORD --------------------------- */
+/* ------------------------------------------------------------------- */
+Uint32 Dbtup::xlcGetLogWord(XlcStruct& xlcStruct)
+{
+ Uint32 pIndex = xlcStruct.PageIndex;
+ ndbrequire(xlcStruct.UPPtr.p != NULL);
+ ndbrequire(pIndex < ZWORDS_ON_PAGE);
+ Uint32 logWord = xlcStruct.UPPtr.p->undoPageWord[pIndex];
+ pIndex++;
+ xlcStruct.PageIndex = pIndex;
+ if (pIndex == ZWORDS_ON_PAGE) {
+ ljam();
+ xlcStruct.PageIndex = ZUNDO_PAGE_HEADER_SIZE;
+ xlcStruct.PageId++;
+ if ((xlcStruct.PageId - xlcStruct.DbsiPtr.p->pdxFilePage) >= (2 * ZUB_SEGMENT_SIZE)) {
+ ljam();
+ xlcStruct.UPPtr.i = RNIL;
+ ptrNull(xlcStruct.UPPtr);
+ } else {
+ ljam();
+ Uint32 index = xlcStruct.PageId - xlcStruct.DbsiPtr.p->pdxFilePage;
+ ndbrequire(index < 16);
+ xlcStruct.UPPtr.i = xlcStruct.DbsiPtr.p->pdxDataPage[index];
+ ptrCheckGuard(xlcStruct.UPPtr, cnoOfUndoPage, undoPage);
+ }//if
+ }//if
+ return logWord;
+}//Dbtup::xlcGetLogWord()
+
+ /****************************************************/
+ /* INSERT A TUPLE HEADER THE DATA IS THE TUPLE DATA */
+ /****************************************************/
+void Dbtup::xlcInsertTh(XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndex & 1) == 0);
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageOffset;
+ getThAtPageSr(pagePtr.p, pageOffset);
+ ndbrequire(pageOffset == (ZPAGE_HEADER_SIZE + (regTabPtr->tupheadsize * (pageIndex >> 1))));
+ if (xlcStruct.LogRecordType == ZLCPR_TYPE_INSERT_TH) {
+ ljam();
+ xlcCopyData(xlcStruct, pageOffset, regTabPtr->tupheadsize, pagePtr);
+ } else {
+ ndbrequire(xlcStruct.LogRecordType == ZLCPR_TYPE_INSERT_TH_NO_DATA);
+ ljam();
+ }//if
+/* ----------------------------------------*/
+/* INDICATE THAT NO OPERATIONS ACTIVE */
+/* ----------------------------------------*/
+ ndbrequire(pageOffset < ZWORDS_ON_PAGE);
+ pagePtr.p->pageWord[pageOffset] = RNIL;
+}//Dbtup::xlcInsertTh()
+
+ /**********************************************/
+ /* DELETE A TUPLE HEADER - NO ADDITIONAL DATA */
+ /**********************************************/
+void Dbtup::xlcDeleteTh(XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndex & 1) == 0);
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + (regTabPtr->tupheadsize * (pageIndex >> 1));
+ freeThSr(regTabPtr, pagePtr.p, pageOffset);
+}//Dbtup::xlcDeleteTh()
+
+ /*****************************************************/
+ /* UPDATE A TUPLE HEADER, THE DATA IS THE TUPLE DATA */
+ /*****************************************************/
+void Dbtup::xlcUpdateTh(XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndex & 1) == 0);
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + (regTabPtr->tupheadsize * (pageIndex >> 1));
+ xlcCopyData(xlcStruct, pageOffset, regTabPtr->tupheadsize, pagePtr);
+/* ----------------------------------------*/
+/* INDICATE THAT NO OPERATIONS ACTIVE */
+/* ----------------------------------------*/
+ ndbrequire(pageOffset < ZWORDS_ON_PAGE);
+ pagePtr.p->pageWord[pageOffset] = RNIL;
+}//Dbtup::xlcUpdateTh()
+
+ /**************************************************/
+ /* ABORT AN INSERT OPERATION - NO ADDITIONAL DATA */
+ /**************************************************/
+void Dbtup::xlcAbortInsert(Signal* signal, XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndex & 1) == 0);
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + (regTabPtr->tupheadsize * (pageIndex >> 1));
+ freeTh(regFragPtr, regTabPtr, signal, pagePtr.p, pageOffset);
+}//Dbtup::xlcAbortInsert()
+
+ /*****************************************************/
+ /* COPY DATA FROM COPY TUPLE TO ORIGINAL TUPLE */
+ /*****************************************************/
+void Dbtup::xlcAbortUpdate(Signal* signal, XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+ Uint32 tuple_size = regTabPtr->tupheadsize;
+
+ Uint32 fragPageIdC = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndexC = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndexC & 1) == 0);
+ Uint32 TdestPageId = getRealpid(regFragPtr, fragPageIdC);
+ Uint32 TcmDestIndex = ZPAGE_HEADER_SIZE +
+ (tuple_size * (pageIndexC >> 1));
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndex & 1) == 0);
+ Uint32 TsourcePageId = getRealpid(regFragPtr, fragPageId);
+ Uint32 TcmSourceIndex = ZPAGE_HEADER_SIZE +
+ (tuple_size * (pageIndex >> 1));
+ Uint32 end_source = tuple_size + TcmSourceIndex;
+ Uint32 end_dest = tuple_size + TcmDestIndex;
+
+ void* Tdestination = (void*)&page[TdestPageId].pageWord[TcmDestIndex + 1];
+ const void* Tsource =
+ (void*)&page[TsourcePageId].pageWord[TcmSourceIndex + 1];
+
+ ndbrequire(TsourcePageId < cnoOfPage &&
+ TdestPageId < cnoOfPage &&
+ end_source <= ZWORDS_ON_PAGE &&
+ end_dest <= ZWORDS_ON_PAGE);
+ MEMCOPY_NO_WORDS(Tdestination, Tsource, (tuple_size - 1));
+
+ pagePtr.i = TsourcePageId;
+ ptrAss(pagePtr, page);
+ freeTh(regFragPtr, regTabPtr, signal, pagePtr.p, TcmSourceIndex);
+
+ pagePtr.i = TdestPageId;
+ ptrAss(pagePtr, page);
+ pagePtr.p->pageWord[TcmDestIndex] = RNIL;
+}//Dbtup::xlcAbortUpdate()
+
+ /*****************************/
+ /* RESTORE UPDATED GCI VALUE */
+ /*****************************/
+void Dbtup::xlcUpdateGCI(XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ Uint32 restoredGCI = xlcGetLogWord(xlcStruct);
+
+ ndbrequire((pageIndex & 1) == 0);
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + (regTabPtr->tupheadsize * (pageIndex >> 1));
+ Uint32 gciOffset = pageOffset + regTabPtr->tupGCPIndex;
+ ndbrequire((gciOffset < ZWORDS_ON_PAGE) &&
+ (regTabPtr->tupGCPIndex < regTabPtr->tupheadsize));
+ pagePtr.p->pageWord[gciOffset] = restoredGCI;
+}//Dbtup::xlcUpdateGCI()
+
+ /*****************************************************/
+ /* READ TABLE DESCRIPTOR FROM UNDO LOG */
+ /*****************************************************/
+void Dbtup::xlcTableDescriptor(XlcStruct& xlcStruct)
+{
+ xlcStruct.LliPtr.p->lliNumFragments--;
+ xlcStruct.FragPtr.p->fragStatus = ACTIVE;
+}//Dbtup::xlcTableDescriptor()
+
+ /********************************************************/
+ /* UPDATE PAGE STATE AND NEXT POINTER IN PAGE */
+ /********************************************************/
+void Dbtup::xlcUndoLogPageHeader(XlcStruct& xlcStruct)
+{
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ PagePtr xlcPagep;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ xlcPagep.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(xlcPagep, cnoOfPage, page);
+ Uint32 logWord = xlcGetLogWord(xlcStruct);
+ ndbrequire(logWord != 0);
+ ndbrequire(logWord <= ZAC_MM_FREE_COPY);
+
+ xlcPagep.p->pageWord[ZPAGE_STATE_POS] = logWord;
+ xlcPagep.p->pageWord[ZPAGE_NEXT_POS] = xlcGetLogWord(xlcStruct);
+}//Dbtup::xlcUndoLogPageHeader()
+
+ /********************************************************/
+ /* INDICATE THAT NO OPERATIONS ACTIVE */
+ /********************************************************/
+void Dbtup::xlcIndicateNoOpActive(XlcStruct& xlcStruct)
+{
+ PagePtr pagePtr;
+ Fragrecord* const regFragPtr = xlcStruct.FragPtr.p;
+ Tablerec* const regTabPtr = xlcStruct.TabPtr.p;
+
+ Uint32 fragPageId = xlcGetLogWord(xlcStruct);
+ Uint32 pageIndex = xlcGetLogWord(xlcStruct);
+ ndbrequire((pageIndex & 1) == 0);
+ pagePtr.i = getRealpid(regFragPtr, fragPageId);
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 pageOffset = ZPAGE_HEADER_SIZE + (regTabPtr->tupheadsize * (pageIndex >> 1));
+/* ----------------------------------------*/
+/* INDICATE THAT NO OPERATIONS ACTIVE */
+/* ----------------------------------------*/
+ ndbrequire(pageOffset < ZWORDS_ON_PAGE);
+ pagePtr.p->pageWord[pageOffset] = RNIL;
+}//Dbtup::xlcIndicateNoOpActive()
+
+ /********************************************************/
+ /* THIS IS THE COMMON ROUTINE TO COPY DATA FROM THE */
+ /* UNDO BUFFER TO THE DATA PAGES. IT USES THE */
+ /* XLC_REQUEST_SEGMENT SUB TO GET MORE DATA WHEN NEEDED */
+ /********************************************************/
+void Dbtup::xlcCopyData(XlcStruct& xlcStruct, Uint32 pageOffset, Uint32 noOfWords, PagePtr pagePtr)
+{
+ ndbrequire((pageOffset + noOfWords - 1) < ZWORDS_ON_PAGE);
+ for (Uint32 i = 1; i < noOfWords; i++) {
+ ljam();
+ pagePtr.p->pageWord[pageOffset + i] = xlcGetLogWord(xlcStruct);
+ }//for
+}//Dbtup::xlcCopyData()
+
+void Dbtup::allocRestartUndoBufferSegment(Signal* signal, DiskBufferSegmentInfoPtr& dbsiPtr, LocalLogInfoPtr lliPtr)
+{
+ UndoPagePtr undoPagePtr;
+
+ ndbrequire(cfirstfreeUndoSeg != RNIL);
+ if (cnoFreeUndoSeg == ZMIN_PAGE_LIMIT_TUP_COMMITREQ) {
+ EXECUTE_DIRECT(DBLQH, GSN_TUP_COM_BLOCK, signal, 1);
+ ljamEntry();
+ }//if
+ cnoFreeUndoSeg--;
+ ndbrequire(cnoFreeUndoSeg >= 0);
+ undoPagePtr.i = cfirstfreeUndoSeg;
+ ptrCheckGuard(undoPagePtr, cnoOfUndoPage, undoPage);
+ cfirstfreeUndoSeg = undoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS];
+ undoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS] = RNIL;
+ seizeDiskBufferSegmentRecord(dbsiPtr);
+ dbsiPtr.p->pdxBuffertype = UNDO_RESTART_PAGES;
+ dbsiPtr.p->pdxUndoBufferSet[0] = undoPagePtr.i;
+ Uint32 i;
+ for (i = 0; i < ZUB_SEGMENT_SIZE; i++) {
+ dbsiPtr.p->pdxDataPage[i] = undoPagePtr.i + i;
+ }//for
+
+ ndbrequire(cfirstfreeUndoSeg != RNIL);
+ if (cnoFreeUndoSeg == ZMIN_PAGE_LIMIT_TUP_COMMITREQ) {
+ EXECUTE_DIRECT(DBLQH, GSN_TUP_COM_BLOCK, signal, 1);
+ ljamEntry();
+ }//if
+ cnoFreeUndoSeg--;
+ ndbrequire(cnoFreeUndoSeg >= 0);
+ undoPagePtr.i = cfirstfreeUndoSeg;
+ ptrCheckGuard(undoPagePtr, cnoOfUndoPage, undoPage);
+ cfirstfreeUndoSeg = undoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS];
+ undoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS] = RNIL;
+ dbsiPtr.p->pdxUndoBufferSet[1] = undoPagePtr.i;
+// lliPtr.p->lliUndoPage = undoPagePtr.i;
+ for (i = ZUB_SEGMENT_SIZE; i < (2 * ZUB_SEGMENT_SIZE); i++) {
+ dbsiPtr.p->pdxDataPage[i] = undoPagePtr.i + (i - ZUB_SEGMENT_SIZE);
+ }//for
+ return;
+}//Dbtup::allocRestartUndoBufferSegment()
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupTabDesMan.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupTabDesMan.cpp
new file mode 100644
index 00000000000..642ba270760
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupTabDesMan.cpp
@@ -0,0 +1,212 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(22000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(22000 + __LINE__); }
+
+/* **************************************************************** */
+/* *********** TABLE DESCRIPTOR MEMORY MANAGER ******************** */
+/* **************************************************************** */
+/* This module is used to allocate and deallocate table descriptor */
+/* memory attached to fragments (could be allocated per table */
+/* instead. Performs its task by a buddy algorithm. */
+/* **************************************************************** */
+
+Uint32
+Dbtup::getTabDescrOffsets(const Tablerec* regTabPtr, Uint32* offset)
+{
+ // belongs to configure.in
+ unsigned sizeOfPointer = sizeof(CHARSET_INFO*);
+ ndbrequire((sizeOfPointer & 0x3) == 0);
+ sizeOfPointer = (sizeOfPointer >> 2);
+ // do in layout order and return offsets (see DbtupMeta.cpp)
+ Uint32 allocSize = 0;
+ // magically aligned to 8 bytes
+ offset[0] = allocSize += ZTD_SIZE;
+ offset[1] = allocSize += regTabPtr->noOfAttr * sizeOfReadFunction();
+ offset[2] = allocSize += regTabPtr->noOfAttr * sizeOfReadFunction();
+ offset[3] = allocSize += regTabPtr->noOfCharsets * sizeOfPointer;
+ offset[4] = allocSize += regTabPtr->noOfKeyAttr;
+ offset[5] = allocSize += regTabPtr->noOfAttributeGroups;
+ allocSize += regTabPtr->noOfAttr * ZAD_SIZE;
+ allocSize += ZTD_TRAILER_SIZE;
+ // return number of words
+ return allocSize;
+}
+
+Uint32 Dbtup::allocTabDescr(const Tablerec* regTabPtr, Uint32* offset)
+{
+ Uint32 reference = RNIL;
+ Uint32 allocSize = getTabDescrOffsets(regTabPtr, offset);
+/* ---------------------------------------------------------------- */
+/* ALWAYS ALLOCATE A MULTIPLE OF 16 BYTES */
+/* ---------------------------------------------------------------- */
+ allocSize = (((allocSize - 1) >> 4) + 1) << 4;
+ Uint32 list = nextHigherTwoLog(allocSize - 1); /* CALCULATE WHICH LIST IT BELONGS TO */
+ for (Uint32 i = list; i < 16; i++) {
+ ljam();
+ if (cfreeTdList[i] != RNIL) {
+ ljam();
+ reference = cfreeTdList[i];
+ removeTdArea(reference, i); /* REMOVE THE AREA FROM THE FREELIST */
+ Uint32 retNo = (1 << i) - allocSize; /* CALCULATE THE DIFFERENCE */
+ if (retNo >= ZTD_FREE_SIZE) {
+ ljam();
+ Uint32 retRef = reference + allocSize; /* SET THE RETURN POINTER */
+ retNo = itdaMergeTabDescr(retRef, retNo); /* MERGE WITH POSSIBLE RIGHT NEIGHBOURS */
+ freeTabDescr(retRef, retNo); /* RETURN UNUSED TD SPACE TO THE TD AREA */
+ } else {
+ ljam();
+ allocSize = 1 << i;
+ }//if
+ break;
+ }//if
+ }//for
+ if (reference == RNIL) {
+ ljam();
+ terrorCode = ZMEM_NOTABDESCR_ERROR;
+ return RNIL;
+ } else {
+ ljam();
+ setTabDescrWord((reference + allocSize) - ZTD_TR_TYPE, ZTD_TYPE_NORMAL);
+ setTabDescrWord(reference + ZTD_DATASIZE, allocSize);
+
+ /* INITIALIZE THE TRAILER RECORD WITH TYPE AND SIZE */
+ /* THE TRAILER IS USED TO SIMPLIFY MERGE OF FREE AREAS */
+
+ setTabDescrWord(reference + ZTD_HEADER, ZTD_TYPE_NORMAL);
+ setTabDescrWord((reference + allocSize) - ZTD_TR_SIZE, allocSize);
+ return reference;
+ }//if
+}//Dbtup::allocTabDescr()
+
+void Dbtup::freeTabDescr(Uint32 retRef, Uint32 retNo)
+{
+ while (retNo >= ZTD_FREE_SIZE) {
+ ljam();
+ Uint32 list = nextHigherTwoLog(retNo);
+ list--; /* RETURN TO NEXT LOWER LIST */
+ Uint32 sizeOfChunk = 1 << list;
+ insertTdArea(sizeOfChunk, retRef, list);
+ retRef += sizeOfChunk;
+ retNo -= sizeOfChunk;
+ }//while
+}//Dbtup::freeTabDescr()
+
+Uint32
+Dbtup::getTabDescrWord(Uint32 index)
+{
+ ndbrequire(index < cnoOfTabDescrRec);
+ return tableDescriptor[index].tabDescr;
+}//Dbtup::getTabDescrWord()
+
+void
+Dbtup::setTabDescrWord(Uint32 index, Uint32 word)
+{
+ ndbrequire(index < cnoOfTabDescrRec);
+ tableDescriptor[index].tabDescr = word;
+}//Dbtup::setTabDescrWord()
+
+void Dbtup::insertTdArea(Uint32 sizeOfChunk, Uint32 tabDesRef, Uint32 list)
+{
+ ndbrequire(list < 16);
+ setTabDescrWord(tabDesRef + ZTD_FL_HEADER, ZTD_TYPE_FREE);
+ setTabDescrWord(tabDesRef + ZTD_FL_NEXT, cfreeTdList[list]);
+ if (cfreeTdList[list] != RNIL) {
+ ljam(); /* PREVIOUSLY EMPTY SLOT */
+ setTabDescrWord(cfreeTdList[list] + ZTD_FL_PREV, tabDesRef);
+ }//if
+ cfreeTdList[list] = tabDesRef; /* RELINK THE LIST */
+
+ setTabDescrWord(tabDesRef + ZTD_FL_PREV, RNIL);
+ setTabDescrWord(tabDesRef + ZTD_FL_SIZE, 1 << list);
+ setTabDescrWord((tabDesRef + (1 << list)) - ZTD_TR_TYPE, ZTD_TYPE_FREE);
+ setTabDescrWord((tabDesRef + (1 << list)) - ZTD_TR_SIZE, 1 << list);
+}//Dbtup::insertTdArea()
+
+/* ---------------------------------------------------------------- */
+/* ----------------------- MERGE_TAB_DESCR ------------------------ */
+/* ---------------------------------------------------------------- */
+/* INPUT: TAB_DESCR_PTR POINTING AT THE CURRENT CHUNK */
+/* */
+/* SHORTNAME: MTD */
+/* -----------------------------------------------------------------*/
+Uint32 Dbtup::itdaMergeTabDescr(Uint32 retRef, Uint32 retNo)
+{
+ /* THE SIZE OF THE PART TO MERGE MUST BE OF THE SAME SIZE AS THE INSERTED PART */
+ /* THIS IS TRUE EITHER IF ONE PART HAS THE SAME SIZE OR THE SUM OF BOTH PARTS */
+ /* TOGETHER HAS THE SAME SIZE AS THE PART TO BE INSERTED */
+ /* FIND THE SIZES OF THE PARTS TO THE RIGHT OF THE PART TO BE REINSERTED */
+ while ((retRef + retNo) < cnoOfTabDescrRec) {
+ ljam();
+ Uint32 tabDesRef = retRef + retNo;
+ Uint32 headerWord = getTabDescrWord(tabDesRef + ZTD_FL_HEADER);
+ if (headerWord == ZTD_TYPE_FREE) {
+ ljam();
+ Uint32 sizeOfMergedPart = getTabDescrWord(tabDesRef + ZTD_FL_SIZE);
+
+ retNo += sizeOfMergedPart;
+ Uint32 list = nextHigherTwoLog(sizeOfMergedPart - 1);
+ removeTdArea(tabDesRef, list);
+ } else {
+ ljam();
+ return retNo;
+ }//if
+ }//while
+ ndbrequire((retRef + retNo) == cnoOfTabDescrRec);
+ return retNo;
+}//Dbtup::itdaMergeTabDescr()
+
+/* ---------------------------------------------------------------- */
+/* ------------------------ REMOVE_TD_AREA ------------------------ */
+/* ---------------------------------------------------------------- */
+/* */
+/* THIS ROUTINE REMOVES A TD CHUNK FROM THE POOL OF TD RECORDS */
+/* */
+/* INPUT: TLIST LIST TO USE */
+/* TAB_DESCR_PTR POINTS TO THE CHUNK TO BE REMOVED */
+/* */
+/* SHORTNAME: RMTA */
+/* -----------------------------------------------------------------*/
+void Dbtup::removeTdArea(Uint32 tabDesRef, Uint32 list)
+{
+ ndbrequire(list < 16);
+ Uint32 tabDescrNextPtr = getTabDescrWord(tabDesRef + ZTD_FL_NEXT);
+ Uint32 tabDescrPrevPtr = getTabDescrWord(tabDesRef + ZTD_FL_PREV);
+
+ setTabDescrWord(tabDesRef + ZTD_HEADER, ZTD_TYPE_NORMAL);
+ setTabDescrWord((tabDesRef + (1 << list)) - ZTD_TR_TYPE, ZTD_TYPE_NORMAL);
+
+ if (tabDesRef == cfreeTdList[list]) {
+ ljam();
+ cfreeTdList[list] = tabDescrNextPtr; /* RELINK THE LIST */
+ }//if
+ if (tabDescrNextPtr != RNIL) {
+ ljam();
+ setTabDescrWord(tabDescrNextPtr + ZTD_FL_PREV, tabDescrPrevPtr);
+ }//if
+ if (tabDescrPrevPtr != RNIL) {
+ ljam();
+ setTabDescrWord(tabDescrPrevPtr + ZTD_FL_NEXT, tabDescrNextPtr);
+ }//if
+}//Dbtup::removeTdArea()
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp
new file mode 100644
index 00000000000..476a4b5724b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp
@@ -0,0 +1,1150 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+#include <AttributeDescriptor.hpp>
+#include "AttributeOffset.hpp"
+#include <AttributeHeader.hpp>
+#include <signaldata/FireTrigOrd.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/TuxMaint.hpp>
+
+#define ljam() { jamLine(7000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(7000 + __LINE__); }
+
+/* **************************************************************** */
+/* ---------------------------------------------------------------- */
+/* ----------------------- TRIGGER HANDLING ----------------------- */
+/* ---------------------------------------------------------------- */
+/* **************************************************************** */
+
+ArrayList<Dbtup::TupTriggerData>*
+Dbtup::findTriggerList(Tablerec* table,
+ TriggerType::Value ttype,
+ TriggerActionTime::Value ttime,
+ TriggerEvent::Value tevent)
+{
+ ArrayList<TupTriggerData>* tlist = NULL;
+ switch (ttype) {
+ case TriggerType::SUBSCRIPTION:
+ case TriggerType::SUBSCRIPTION_BEFORE:
+ switch (tevent) {
+ case TriggerEvent::TE_INSERT:
+ ljam();
+ if (ttime == TriggerActionTime::TA_DETACHED)
+ tlist = &table->subscriptionInsertTriggers;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_DETACHED)
+ tlist = &table->subscriptionUpdateTriggers;
+ break;
+ case TriggerEvent::TE_DELETE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_DETACHED)
+ tlist = &table->subscriptionDeleteTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TriggerType::SECONDARY_INDEX:
+ switch (tevent) {
+ case TriggerEvent::TE_INSERT:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->afterInsertTriggers;
+ break;
+ case TriggerEvent::TE_UPDATE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->afterUpdateTriggers;
+ break;
+ case TriggerEvent::TE_DELETE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->afterDeleteTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TriggerType::ORDERED_INDEX:
+ switch (tevent) {
+ case TriggerEvent::TE_CUSTOM:
+ ljam();
+ if (ttime == TriggerActionTime::TA_CUSTOM)
+ tlist = &table->tuxCustomTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TriggerType::READ_ONLY_CONSTRAINT:
+ switch (tevent) {
+ case TriggerEvent::TE_UPDATE:
+ ljam();
+ if (ttime == TriggerActionTime::TA_AFTER)
+ tlist = &table->constraintUpdateTriggers;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return tlist;
+}
+
+// Trigger signals
+void
+Dbtup::execCREATE_TRIG_REQ(Signal* signal)
+{
+ ljamEntry();
+ BlockReference senderRef = signal->getSendersBlockRef();
+ const CreateTrigReq reqCopy = *(const CreateTrigReq*)signal->getDataPtr();
+ const CreateTrigReq* const req = &reqCopy;
+
+ // Find table
+ TablerecPtr tabPtr;
+ tabPtr.i = req->getTableId();
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+ // Create trigger and associate it with the table
+ if (createTrigger(tabPtr.p, req)) {
+ ljam();
+ // Send conf
+ CreateTrigConf* const conf = (CreateTrigConf*)signal->getDataPtrSend();
+ conf->setUserRef(reference());
+ conf->setConnectionPtr(req->getConnectionPtr());
+ conf->setRequestType(req->getRequestType());
+ conf->setTableId(req->getTableId());
+ conf->setIndexId(req->getIndexId());
+ conf->setTriggerId(req->getTriggerId());
+ conf->setTriggerInfo(req->getTriggerInfo());
+ sendSignal(senderRef, GSN_CREATE_TRIG_CONF,
+ signal, CreateTrigConf::SignalLength, JBB);
+ } else {
+ ljam();
+ // Send ref
+ CreateTrigRef* const ref = (CreateTrigRef*)signal->getDataPtrSend();
+ ref->setUserRef(reference());
+ ref->setConnectionPtr(req->getConnectionPtr());
+ ref->setRequestType(req->getRequestType());
+ ref->setTableId(req->getTableId());
+ ref->setIndexId(req->getIndexId());
+ ref->setTriggerId(req->getTriggerId());
+ ref->setTriggerInfo(req->getTriggerInfo());
+ ref->setErrorCode(CreateTrigRef::TooManyTriggers);
+ sendSignal(senderRef, GSN_CREATE_TRIG_REF,
+ signal, CreateTrigRef::SignalLength, JBB);
+ }
+}//Dbtup::execCREATE_TRIG_REQ()
+
+void
+Dbtup::execDROP_TRIG_REQ(Signal* signal)
+{
+ ljamEntry();
+ BlockReference senderRef = signal->getSendersBlockRef();
+ const DropTrigReq reqCopy = *(const DropTrigReq*)signal->getDataPtr();
+ const DropTrigReq* const req = &reqCopy;
+
+ // Find table
+ TablerecPtr tabPtr;
+ tabPtr.i = req->getTableId();
+ ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec);
+
+ // Drop trigger
+ Uint32 r = dropTrigger(tabPtr.p, req);
+ if (r == 0){
+ // Send conf
+ DropTrigConf* const conf = (DropTrigConf*)signal->getDataPtrSend();
+ conf->setUserRef(senderRef);
+ conf->setConnectionPtr(req->getConnectionPtr());
+ conf->setRequestType(req->getRequestType());
+ conf->setTableId(req->getTableId());
+ conf->setIndexId(req->getIndexId());
+ conf->setTriggerId(req->getTriggerId());
+ sendSignal(senderRef, GSN_DROP_TRIG_CONF,
+ signal, DropTrigConf::SignalLength, JBB);
+ } else {
+ // Send ref
+ DropTrigRef* const ref = (DropTrigRef*)signal->getDataPtrSend();
+ ref->setUserRef(senderRef);
+ ref->setConnectionPtr(req->getConnectionPtr());
+ ref->setRequestType(req->getRequestType());
+ ref->setTableId(req->getTableId());
+ ref->setIndexId(req->getIndexId());
+ ref->setTriggerId(req->getTriggerId());
+ ref->setErrorCode((DropTrigRef::ErrorCode)r);
+ ref->setErrorLine(__LINE__);
+ ref->setErrorNode(refToNode(reference()));
+ sendSignal(senderRef, GSN_DROP_TRIG_REF,
+ signal, DropTrigRef::SignalLength, JBB);
+ }
+}//Dbtup::DROP_TRIG_REQ()
+
+/* ---------------------------------------------------------------- */
+/* ------------------------- createTrigger ------------------------ */
+/* */
+/* Creates a new trigger record by fetching one from the trigger */
+/* pool and associates it with the given table. */
+/* Trigger type can be one of secondary_index, subscription, */
+/* constraint(NYI), foreign_key(NYI), schema_upgrade(NYI), */
+/* api_trigger(NYI) or sql_trigger(NYI). */
+/* Note that this method only checks for total number of allowed */
+/* triggers. Checking the number of allowed triggers per table is */
+/* done by TRIX. */
+/* */
+/* ---------------------------------------------------------------- */
+bool
+Dbtup::createTrigger(Tablerec* table, const CreateTrigReq* req)
+{
+ if (ERROR_INSERTED(4003)) {
+ CLEAR_ERROR_INSERT_VALUE;
+ return false;
+ }
+ TriggerType::Value ttype = req->getTriggerType();
+ TriggerActionTime::Value ttime = req->getTriggerActionTime();
+ TriggerEvent::Value tevent = req->getTriggerEvent();
+
+ ArrayList<TupTriggerData>* tlist = findTriggerList(table, ttype, ttime, tevent);
+ ndbrequire(tlist != NULL);
+
+ TriggerPtr tptr;
+ if (!tlist->seize(tptr))
+ return false;
+
+ // Set trigger id
+ tptr.p->triggerId = req->getTriggerId();
+
+ // ndbout_c("Create TupTrigger %u = %u %u %u %u", tptr.p->triggerId, table, ttype, ttime, tevent);
+
+ // Set index id
+ tptr.p->indexId = req->getIndexId();
+
+ // Set trigger type etc
+ tptr.p->triggerType = ttype;
+ tptr.p->triggerActionTime = ttime;
+ tptr.p->triggerEvent = tevent;
+
+ tptr.p->sendBeforeValues = true;
+ if ((tptr.p->triggerType == TriggerType::SUBSCRIPTION) &&
+ ((tptr.p->triggerEvent == TriggerEvent::TE_UPDATE) ||
+ (tptr.p->triggerEvent == TriggerEvent::TE_DELETE))) {
+ ljam();
+ tptr.p->sendBeforeValues = false;
+ }
+ tptr.p->sendOnlyChangedAttributes = false;
+ if (((tptr.p->triggerType == TriggerType::SUBSCRIPTION) ||
+ (tptr.p->triggerType == TriggerType::SUBSCRIPTION_BEFORE)) &&
+ (tptr.p->triggerEvent == TriggerEvent::TE_UPDATE)) {
+ ljam();
+ tptr.p->sendOnlyChangedAttributes = true;
+ }
+
+ // Set monitor all
+ tptr.p->monitorAllAttributes = req->getMonitorAllAttributes();
+ tptr.p->monitorReplicas = req->getMonitorReplicas();
+ tptr.p->m_receiverBlock = refToBlock(req->getReceiverRef());
+
+ tptr.p->attributeMask.clear();
+ if (tptr.p->monitorAllAttributes) {
+ ljam();
+ for(Uint32 i = 0; i < table->noOfAttr; i++) {
+ if (!primaryKey(table, i)) {
+ ljam();
+ tptr.p->attributeMask.set(i);
+ }
+ }
+ } else {
+ // Set attribute mask
+ ljam();
+ tptr.p->attributeMask = req->getAttributeMask();
+ }
+ return true;
+}//Dbtup::createTrigger()
+
+bool
+Dbtup::primaryKey(Tablerec* const regTabPtr, Uint32 attrId)
+{
+ Uint32 attrDescriptorStart = regTabPtr->tabDescriptor;
+ Uint32 attrDescriptor = getTabDescrWord(attrDescriptorStart + (attrId * ZAD_SIZE));
+ return (bool)AttributeDescriptor::getPrimaryKey(attrDescriptor);
+}//Dbtup::primaryKey()
+
+/* ---------------------------------------------------------------- */
+/* -------------------------- dropTrigger ------------------------- */
+/* */
+/* Deletes a trigger record by disassociating it with the given */
+/* table and returning it to the trigger pool. */
+/* Trigger type can be one of secondary_index, subscription, */
+/* constraint(NYI), foreign_key(NYI), schema_upgrade(NYI), */
+/* api_trigger(NYI) or sql_trigger(NYI). */
+/* */
+/* ---------------------------------------------------------------- */
+Uint32
+Dbtup::dropTrigger(Tablerec* table, const DropTrigReq* req)
+{
+ Uint32 triggerId = req->getTriggerId();
+
+ TriggerType::Value ttype = req->getTriggerType();
+ TriggerActionTime::Value ttime = req->getTriggerActionTime();
+ TriggerEvent::Value tevent = req->getTriggerEvent();
+
+ // ndbout_c("Drop TupTrigger %u = %u %u %u %u", triggerId, table, ttype, ttime, tevent);
+
+ ArrayList<TupTriggerData>* tlist = findTriggerList(table, ttype, ttime, tevent);
+ ndbrequire(tlist != NULL);
+
+ Ptr<TupTriggerData> ptr;
+ for (tlist->first(ptr); !ptr.isNull(); tlist->next(ptr)) {
+ ljam();
+ if (ptr.p->triggerId == triggerId) {
+ ljam();
+ tlist->release(ptr.i);
+ return 0;
+ }
+ }
+ return DropTrigRef::TriggerNotFound;
+}//Dbtup::dropTrigger()
+
+/* ---------------------------------------------------------------- */
+/* -------------- checkImmediateTriggersAfterOp ------------------ */
+/* */
+/* Called after an insert, delete, or update operation takes */
+/* place. Fetches before tuple for deletes and updates and */
+/* after tuple for inserts and updates. */
+/* Executes immediate triggers by sending FIRETRIGORD */
+/* */
+/* ---------------------------------------------------------------- */
+void Dbtup::checkImmediateTriggersAfterInsert(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ if(refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ return;
+ }
+
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->afterInsertTriggers.isEmpty()))) {
+ ljam();
+ fireImmediateTriggers(signal,
+ regTablePtr->afterInsertTriggers,
+ regOperPtr);
+ }//if
+}//Dbtup::checkImmediateTriggersAfterInsert()
+
+void Dbtup::checkImmediateTriggersAfterUpdate(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ if(refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ return;
+ }
+
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->afterUpdateTriggers.isEmpty()))) {
+ ljam();
+ fireImmediateTriggers(signal,
+ regTablePtr->afterUpdateTriggers,
+ regOperPtr);
+ }//if
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->constraintUpdateTriggers.isEmpty()))) {
+ ljam();
+ fireImmediateTriggers(signal,
+ regTablePtr->constraintUpdateTriggers,
+ regOperPtr);
+ }//if
+}//Dbtup::checkImmediateTriggersAfterUpdate()
+
+void Dbtup::checkImmediateTriggersAfterDelete(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ if(refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ return;
+ }
+
+ if ((regOperPtr->primaryReplica) &&
+ (!(regTablePtr->afterDeleteTriggers.isEmpty()))) {
+ ljam();
+ executeTriggers(signal,
+ regTablePtr->afterDeleteTriggers,
+ regOperPtr);
+ }//if
+}//Dbtup::checkImmediateTriggersAfterDelete()
+
+#if 0
+/* ---------------------------------------------------------------- */
+/* --------------------- checkDeferredTriggers -------------------- */
+/* */
+/* Called before commit after an insert, delete, or update */
+/* operation. Fetches before tuple for deletes and updates and */
+/* after tuple for inserts and updates. */
+/* Executes deferred triggers by sending FIRETRIGORD */
+/* */
+/* ---------------------------------------------------------------- */
+void Dbtup::checkDeferredTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ ljam();
+ // NYI
+}//Dbtup::checkDeferredTriggers()
+#endif
+
+/* ---------------------------------------------------------------- */
+/* --------------------- checkDetachedTriggers -------------------- */
+/* */
+/* Called at commit after an insert, delete, or update operation. */
+/* Fetches before tuple for deletes and updates and */
+/* after tuple for inserts and updates. */
+/* Executes detached triggers by sending FIRETRIGORD */
+/* */
+/* ---------------------------------------------------------------- */
+void Dbtup::checkDetachedTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTablePtr)
+{
+ switch(regOperPtr->optype) {
+ case(ZINSERT):
+ ljam();
+ if (regTablePtr->subscriptionInsertTriggers.isEmpty()) {
+ // Table has no active triggers monitoring inserts at commit
+ ljam();
+ return;
+ }//if
+
+ // If any fired immediate insert trigger then fetch after tuple
+ fireDetachedTriggers(signal,
+ regTablePtr->subscriptionInsertTriggers,
+ regOperPtr);
+ break;
+ case(ZDELETE):
+ ljam();
+ if (regTablePtr->subscriptionDeleteTriggers.isEmpty()) {
+ // Table has no active triggers monitoring deletes at commit
+ ljam();
+ return;
+ }//if
+
+ // Execute any after delete triggers by sending
+ // FIRETRIGORD with the before tuple
+ executeTriggers(signal,
+ regTablePtr->subscriptionDeleteTriggers,
+ regOperPtr);
+ break;
+ case(ZUPDATE):
+ ljam();
+ if (regTablePtr->subscriptionUpdateTriggers.isEmpty()) {
+ // Table has no active triggers monitoring updates at commit
+ ljam();
+ return;
+ }//if
+
+ // If any fired immediate update trigger then fetch after tuple
+ // and send two FIRETRIGORD one with before tuple and one with after tuple
+ fireDetachedTriggers(signal,
+ regTablePtr->subscriptionUpdateTriggers,
+ regOperPtr);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbtup::CheckDetachedTriggers()
+
+void
+Dbtup::fireImmediateTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ if (trigPtr.p->monitorAllAttributes ||
+ trigPtr.p->attributeMask.overlaps(regOperPtr->changeMask)) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr.p,
+ regOperPtr);
+ }//if
+ triggerList.next(trigPtr);
+ }//while
+}//Dbtup::fireImmediateTriggers()
+
+#if 0
+void
+Dbtup::fireDeferredTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ if (trigPtr.p->monitorAllAttributes ||
+ trigPtr.p->attributeMask.overlaps(regOperPtr->changeMask)) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr,
+ regOperPtr);
+ }//if
+ triggerList.next(trigPtr);
+ }//while
+}//Dbtup::fireDeferredTriggers()
+#endif
+
+void
+Dbtup::fireDetachedTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* const regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ if ((trigPtr.p->monitorReplicas || regOperPtr->primaryReplica) &&
+ (trigPtr.p->monitorAllAttributes ||
+ trigPtr.p->attributeMask.overlaps(regOperPtr->changeMask))) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr.p,
+ regOperPtr);
+ }//if
+ triggerList.next(trigPtr);
+ }//while
+}//Dbtup::fireDetachedTriggers()
+
+void Dbtup::executeTriggers(Signal* signal,
+ ArrayList<TupTriggerData>& triggerList,
+ Operationrec* regOperPtr)
+{
+ TriggerPtr trigPtr;
+ triggerList.first(trigPtr);
+ while (trigPtr.i != RNIL) {
+ ljam();
+ executeTrigger(signal,
+ trigPtr.p,
+ regOperPtr);
+ triggerList.next(trigPtr);
+
+ }//while
+}//Dbtup::executeTriggers()
+
+void Dbtup::executeTrigger(Signal* signal,
+ TupTriggerData* const trigPtr,
+ Operationrec* const regOperPtr)
+{
+
+ /**
+ * The block below does not work together with GREP.
+ * I have 2 db nodes (2 replicas) -> one node group.
+ * I want to have FIRETRIG_ORD sent to all SumaParticipants,
+ * from all nodes in the node group described above. However,
+ * only one of the nodes in the node group actually sends the
+ * FIRE_TRIG_ORD, and the other node enters this "hack" below.
+ * I don't really know what the code snippet below does, but it
+ * does not work with GREP the way Lars and I want it.
+ * We need to have triggers fired from both the primary and the
+ * backup replica, not only the primary as it is now.
+ *
+ * Note: In Suma, I have changed triggers to be created with
+ * setMonitorReplicas(true).
+ * /Johan
+ *
+ * See RT 709
+ */
+ // XXX quick fix to NR, should fix in LQHKEYREQ instead
+ /*
+ if (refToBlock(regOperPtr->coordinatorTC) == DBLQH) {
+ jam();
+ return;
+ }
+ */
+ BlockReference ref = trigPtr->m_receiverBlock;
+ Uint32* const keyBuffer = &cinBuffer[0];
+ Uint32* const mainBuffer = &coutBuffer[0];
+ Uint32* const copyBuffer = &clogMemBuffer[0];
+
+ Uint32 noPrimKey, noMainWords, noCopyWords;
+
+ if (ref == BACKUP) {
+ ljam();
+ /*
+ In order for the implementation of BACKUP to work even when changing
+ primaries in the middle of the backup we need to set the trigger on
+ all replicas. This check checks whether this is the node where this
+ trigger should be fired. The check should preferably have been put
+ completely in the BACKUP block but it was about five times simpler
+ to put it here and also much faster for the backup (small overhead
+ for everybody else.
+ */
+ signal->theData[0] = trigPtr->triggerId;
+ signal->theData[1] = regOperPtr->fragId;
+ EXECUTE_DIRECT(BACKUP, GSN_BACKUP_TRIG_REQ, signal, 2);
+ ljamEntry();
+ if (signal->theData[0] == 0) {
+ ljam();
+ return;
+ }//if
+ }//if
+ if (!readTriggerInfo(trigPtr,
+ regOperPtr,
+ keyBuffer,
+ noPrimKey,
+ mainBuffer,
+ noMainWords,
+ copyBuffer,
+ noCopyWords)) {
+ ljam();
+ return;
+ }//if
+//--------------------------------------------------------------------
+// Now all data for this trigger has been read. It is now time to send
+// the trigger information consisting of two or three sets of TRIG_
+// ATTRINFO signals and one FIRE_TRIG_ORD signal.
+// We start by setting common header info for all TRIG_ATTRINFO signals.
+//--------------------------------------------------------------------
+ bool executeDirect;
+ TrigAttrInfo* const trigAttrInfo = (TrigAttrInfo *)signal->getDataPtrSend();
+ trigAttrInfo->setConnectionPtr(regOperPtr->tcOpIndex);
+ trigAttrInfo->setTriggerId(trigPtr->triggerId);
+
+ switch(trigPtr->triggerType) {
+ case (TriggerType::SECONDARY_INDEX):
+ ljam();
+ ref = regOperPtr->coordinatorTC;
+ executeDirect = false;
+ break;
+ case (TriggerType::SUBSCRIPTION):
+ case (TriggerType::SUBSCRIPTION_BEFORE):
+ ljam();
+ // Since only backup uses subscription triggers we send to backup directly for now
+ ref = trigPtr->m_receiverBlock;
+ executeDirect = true;
+ break;
+ case (TriggerType::READ_ONLY_CONSTRAINT):
+ terrorCode = ZREAD_ONLY_CONSTRAINT_VIOLATION;
+ // XXX should return status and abort the rest
+ return;
+ default:
+ ndbrequire(false);
+ executeDirect= false; // remove warning
+ }//switch
+
+ regOperPtr->noFiredTriggers++;
+
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::PRIMARY_KEY);
+ sendTrigAttrInfo(signal, keyBuffer, noPrimKey, executeDirect, ref);
+
+ Uint32 noAfter = 0;
+ Uint32 noBefore = 0;
+ switch(regOperPtr->optype) {
+ case(ZINSERT):
+ ljam();
+ // Send AttrInfo signals with new attribute values
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::AFTER_VALUES);
+ sendTrigAttrInfo(signal, mainBuffer, noMainWords, executeDirect, ref);
+ noAfter = noMainWords;
+ break;
+ case(ZDELETE):
+ if (trigPtr->sendBeforeValues) {
+ ljam();
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::BEFORE_VALUES);
+ sendTrigAttrInfo(signal, mainBuffer, noMainWords, executeDirect, ref);
+ noBefore = noMainWords;
+ }//if
+ break;
+ case(ZUPDATE):
+ ljam();
+ if (trigPtr->sendBeforeValues) {
+ ljam();
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::BEFORE_VALUES);
+ sendTrigAttrInfo(signal, copyBuffer, noCopyWords, executeDirect, ref);
+ noBefore = noCopyWords;
+ }//if
+ trigAttrInfo->setAttrInfoType(TrigAttrInfo::AFTER_VALUES);
+ sendTrigAttrInfo(signal, mainBuffer, noMainWords, executeDirect, ref);
+ noAfter = noMainWords;
+ break;
+ default:
+ ndbrequire(false);
+ }//switch
+ sendFireTrigOrd(signal,
+ regOperPtr,
+ trigPtr,
+ noPrimKey,
+ noBefore,
+ noAfter);
+}//Dbtup::executeTrigger()
+
+Uint32 Dbtup::setAttrIds(Bitmask<MAXNROFATTRIBUTESINWORDS>& attributeMask,
+ Uint32 noOfAttributes,
+ Uint32* inBuffer)
+{
+ Uint32 bufIndx = 0;
+ for (Uint32 i = 0; i < noOfAttributes; i++) {
+ ljam();
+ if (attributeMask.get(i)) {
+ ljam();
+ AttributeHeader::init(&inBuffer[bufIndx++], i, 0);
+ }//if
+ }//for
+ return bufIndx;
+}//Dbtup::setAttrIds()
+
+bool Dbtup::readTriggerInfo(TupTriggerData* const trigPtr,
+ Operationrec* const regOperPtr,
+ Uint32* const keyBuffer,
+ Uint32& noPrimKey,
+ Uint32* const mainBuffer,
+ Uint32& noMainWords,
+ Uint32* const copyBuffer,
+ Uint32& noCopyWords)
+{
+ noCopyWords = 0;
+ noMainWords = 0;
+ Uint32 readBuffer[MAX_ATTRIBUTES_IN_TABLE];
+ PagePtr pagep;
+
+//---------------------------------------------------------------------------
+// Set-up variables needed by readAttributes operPtr.p, tabptr.p
+//---------------------------------------------------------------------------
+ operPtr.p = regOperPtr;
+ tabptr.i = regOperPtr->tableRef;
+ ptrCheckGuard(tabptr, cnoOfTablerec, tablerec);
+ Tablerec* const regTabPtr = tabptr.p;
+//--------------------------------------------------------------------
+// Initialise pagep and tuple offset for read of main tuple
+//--------------------------------------------------------------------
+ Uint32 tupheadoffset = regOperPtr->pageOffset;
+ pagep.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagep, cnoOfPage, page);
+
+//--------------------------------------------------------------------
+// Read Primary Key Values
+//--------------------------------------------------------------------
+ int ret= readAttributes(pagep.p,
+ tupheadoffset,
+ &tableDescriptor[regTabPtr->readKeyArray].tabDescr,
+ regTabPtr->noOfKeyAttr,
+ keyBuffer,
+ ZATTR_BUFFER_SIZE,
+ false);
+ ndbrequire(ret != -1);
+ noPrimKey= ret;
+
+ Uint32 numAttrsToRead;
+ if ((regOperPtr->optype == ZUPDATE) &&
+ (trigPtr->sendOnlyChangedAttributes)) {
+ ljam();
+//--------------------------------------------------------------------
+// Update that sends only changed information
+//--------------------------------------------------------------------
+ Bitmask<MAXNROFATTRIBUTESINWORDS> attributeMask;
+ attributeMask = trigPtr->attributeMask;
+ attributeMask.bitAND(regOperPtr->changeMask);
+ numAttrsToRead = setAttrIds(attributeMask, regTabPtr->noOfAttr, &readBuffer[0]);
+
+ } else if ((regOperPtr->optype == ZDELETE) &&
+ (!trigPtr->sendBeforeValues)) {
+ ljam();
+//--------------------------------------------------------------------
+// Delete without sending before values only read Primary Key
+//--------------------------------------------------------------------
+ return true;
+ } else {
+ ljam();
+//--------------------------------------------------------------------
+// All others send all attributes that are monitored
+//--------------------------------------------------------------------
+ numAttrsToRead = setAttrIds(trigPtr->attributeMask, regTabPtr->noOfAttr, &readBuffer[0]);
+ }//if
+ ndbrequire(numAttrsToRead < MAX_ATTRIBUTES_IN_TABLE);
+//--------------------------------------------------------------------
+// Read Main tuple values
+//--------------------------------------------------------------------
+ if ((regOperPtr->optype != ZDELETE) ||
+ (trigPtr->sendBeforeValues)) {
+ ljam();
+ int ret= readAttributes(pagep.p,
+ tupheadoffset,
+ &readBuffer[0],
+ numAttrsToRead,
+ mainBuffer,
+ ZATTR_BUFFER_SIZE,
+ false);
+ ndbrequire(ret != -1);
+ noMainWords= ret;
+ } else {
+ ljam();
+ noMainWords = 0;
+ }//if
+//--------------------------------------------------------------------
+// Read Copy tuple values for UPDATE's
+//--------------------------------------------------------------------
+// Initialise pagep and tuple offset for read of copy tuple
+//--------------------------------------------------------------------
+ if ((regOperPtr->optype == ZUPDATE) &&
+ (trigPtr->sendBeforeValues)) {
+ ljam();
+
+ tupheadoffset = regOperPtr->pageOffsetC;
+ pagep.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(pagep, cnoOfPage, page);
+
+ int ret= readAttributes(pagep.p,
+ tupheadoffset,
+ &readBuffer[0],
+ numAttrsToRead,
+ copyBuffer,
+ ZATTR_BUFFER_SIZE,
+ false);
+
+ ndbrequire(ret != -1);
+ noCopyWords = ret;
+ if ((noMainWords == noCopyWords) &&
+ (memcmp(mainBuffer, copyBuffer, noMainWords << 2) == 0)) {
+//--------------------------------------------------------------------
+// Although a trigger was fired it was not necessary since the old
+// value and the new value was exactly the same
+//--------------------------------------------------------------------
+ ljam();
+ return false;
+ }//if
+ }//if
+ return true;
+}//Dbtup::readTriggerInfo()
+
+void Dbtup::sendTrigAttrInfo(Signal* signal,
+ Uint32* data,
+ Uint32 dataLen,
+ bool executeDirect,
+ BlockReference receiverReference)
+{
+ TrigAttrInfo* const trigAttrInfo = (TrigAttrInfo *)signal->getDataPtrSend();
+ Uint32 sigLen;
+ Uint32 dataIndex = 0;
+ do {
+ sigLen = dataLen - dataIndex;
+ if (sigLen > TrigAttrInfo::DataLength) {
+ ljam();
+ sigLen = TrigAttrInfo::DataLength;
+ }//if
+ MEMCOPY_NO_WORDS(trigAttrInfo->getData(),
+ data + dataIndex,
+ sigLen);
+ if (executeDirect) {
+ ljam();
+ EXECUTE_DIRECT(receiverReference,
+ GSN_TRIG_ATTRINFO,
+ signal,
+ TrigAttrInfo::StaticLength + sigLen);
+ ljamEntry();
+ } else {
+ ljam();
+ sendSignal(receiverReference,
+ GSN_TRIG_ATTRINFO,
+ signal,
+ TrigAttrInfo::StaticLength + sigLen,
+ JBB);
+ }//if
+ dataIndex += sigLen;
+ } while (dataLen != dataIndex);
+}//Dbtup::sendTrigAttrInfo()
+
+void Dbtup::sendFireTrigOrd(Signal* signal,
+ Operationrec * const regOperPtr,
+ TupTriggerData* const trigPtr,
+ Uint32 noPrimKeyWords,
+ Uint32 noBeforeValueWords,
+ Uint32 noAfterValueWords)
+{
+ FireTrigOrd* const fireTrigOrd = (FireTrigOrd *)signal->getDataPtrSend();
+
+ fireTrigOrd->setConnectionPtr(regOperPtr->tcOpIndex);
+ fireTrigOrd->setTriggerId(trigPtr->triggerId);
+
+ switch(regOperPtr->optype) {
+ case(ZINSERT):
+ ljam();
+ fireTrigOrd->setTriggerEvent(TriggerEvent::TE_INSERT);
+ break;
+ case(ZDELETE):
+ ljam();
+ fireTrigOrd->setTriggerEvent(TriggerEvent::TE_DELETE);
+ break;
+ case(ZUPDATE):
+ ljam();
+ fireTrigOrd->setTriggerEvent(TriggerEvent::TE_UPDATE);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+
+ fireTrigOrd->setNoOfPrimaryKeyWords(noPrimKeyWords);
+ fireTrigOrd->setNoOfBeforeValueWords(noBeforeValueWords);
+ fireTrigOrd->setNoOfAfterValueWords(noAfterValueWords);
+
+ switch(trigPtr->triggerType) {
+ case (TriggerType::SECONDARY_INDEX):
+ ljam();
+ sendSignal(regOperPtr->coordinatorTC, GSN_FIRE_TRIG_ORD,
+ signal, FireTrigOrd::SignalLength, JBB);
+ break;
+ case (TriggerType::SUBSCRIPTION_BEFORE): // Only Suma
+ ljam();
+ // Since only backup uses subscription triggers we
+ // send to backup directly for now
+ fireTrigOrd->setGCI(regOperPtr->gci);
+ fireTrigOrd->setHashValue(regOperPtr->hashValue);
+ EXECUTE_DIRECT(trigPtr->m_receiverBlock,
+ GSN_FIRE_TRIG_ORD,
+ signal,
+ FireTrigOrd::SignalWithHashValueLength);
+ break;
+ case (TriggerType::SUBSCRIPTION):
+ ljam();
+ // Since only backup uses subscription triggers we
+ // send to backup directly for now
+ fireTrigOrd->setGCI(regOperPtr->gci);
+ EXECUTE_DIRECT(trigPtr->m_receiverBlock,
+ GSN_FIRE_TRIG_ORD,
+ signal,
+ FireTrigOrd::SignalWithGCILength);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }//switch
+}//Dbtup::sendFireTrigOrd()
+
+/*
+ * Ordered index triggers.
+ *
+ * Insert: add entry to index
+ * Update: add entry to index, de|ay remove until commit
+ * Delete: do nothing, delay remove until commit
+ * Commit: remove entry delayed from update and delete
+ * Abort : remove entry added by insert and update
+ *
+ * See Notes.txt for the details.
+ */
+
+int
+Dbtup::executeTuxInsertTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffset + 1];
+ ndbrequire(tupVersion == regOperPtr->tupVersion);
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpAdd;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ if (req->errorCode != 0) {
+ ljam();
+ terrorCode = req->errorCode;
+ return -1;
+ }
+ triggerList.next(triggerPtr);
+ }
+ return 0;
+}
+
+int
+Dbtup::executeTuxUpdateTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ Uint32 tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffset + 1];
+ ndbrequire(tupVersion == regOperPtr->tupVersion);
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpAdd;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ if (req->errorCode != 0) {
+ ljam();
+ terrorCode = req->errorCode;
+ return -1;
+ }
+ triggerList.next(triggerPtr);
+ }
+ return 0;
+}
+
+int
+Dbtup::executeTuxDeleteTriggers(Signal* signal,
+ Operationrec* const regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ // do nothing
+ return 0;
+}
+
+void
+Dbtup::executeTuxCommitTriggers(Signal* signal,
+ Operationrec* regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ // get version
+ // XXX could add prevTupVersion to Operationrec
+ Uint32 tupVersion;
+ if (regOperPtr->optype == ZINSERT) {
+ if (! regOperPtr->deleteInsertFlag)
+ return;
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffsetC + 1];
+ ndbrequire(tupVersion != regOperPtr->tupVersion);
+ } else if (regOperPtr->optype == ZUPDATE) {
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageIdC;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffsetC + 1];
+ ndbrequire(tupVersion != regOperPtr->tupVersion);
+ } else if (regOperPtr->optype == ZDELETE) {
+ if (regOperPtr->deleteInsertFlag)
+ return;
+ ljam();
+ PagePtr pagePtr;
+ pagePtr.i = regOperPtr->realPageId;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ tupVersion = pagePtr.p->pageWord[regOperPtr->pageOffset + 1];
+ ndbrequire(tupVersion == regOperPtr->tupVersion);
+ } else {
+ ndbrequire(false);
+ tupVersion= 0; // remove warning
+ }
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpRemove;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL;
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ // commit must succeed
+ ndbrequire(req->errorCode == 0);
+ triggerList.next(triggerPtr);
+ }
+}
+
+void
+Dbtup::executeTuxAbortTriggers(Signal* signal,
+ Operationrec* regOperPtr,
+ Tablerec* const regTabPtr)
+{
+ TuxMaintReq* const req = (TuxMaintReq*)signal->getDataPtrSend();
+ // get version
+ Uint32 tupVersion;
+ if (regOperPtr->optype == ZINSERT) {
+ ljam();
+ tupVersion = regOperPtr->tupVersion;
+ } else if (regOperPtr->optype == ZUPDATE) {
+ ljam();
+ tupVersion = regOperPtr->tupVersion;
+ } else if (regOperPtr->optype == ZDELETE) {
+ ljam();
+ return;
+ } else {
+ ndbrequire(false);
+ tupVersion= 0; // remove warning
+ }
+ // fill in constant part
+ req->tableId = regOperPtr->tableRef;
+ req->fragId = regOperPtr->fragId;
+ req->pageId = regOperPtr->realPageId;
+ req->pageOffset = regOperPtr->pageOffset;
+ req->tupVersion = tupVersion;
+ req->opInfo = TuxMaintReq::OpRemove;
+ // loop over index list
+ const ArrayList<TupTriggerData>& triggerList = regTabPtr->tuxCustomTriggers;
+ TriggerPtr triggerPtr;
+ triggerList.first(triggerPtr);
+ while (triggerPtr.i != RNIL) {
+ ljam();
+ req->indexId = triggerPtr.p->indexId;
+ req->errorCode = RNIL,
+ EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ,
+ signal, TuxMaintReq::SignalLength);
+ ljamEntry();
+ // abort must succeed
+ ndbrequire(req->errorCode == 0);
+ triggerList.next(triggerPtr);
+ }
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupUndoLog.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupUndoLog.cpp
new file mode 100644
index 00000000000..869f399583f
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupUndoLog.cpp
@@ -0,0 +1,284 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define DBTUP_C
+#include "Dbtup.hpp"
+#include <RefConvert.hpp>
+#include <ndb_limits.h>
+#include <pc.hpp>
+
+#define ljam() { jamLine(12000 + __LINE__); }
+#define ljamEntry() { jamEntryLine(12000 + __LINE__); }
+
+void Dbtup::cprAddData(Signal* signal,
+ Fragrecord* const regFragPtr,
+ Uint32 pageIndex,
+ Uint32 noOfWords,
+ Uint32 startOffset)
+{
+ UndoPagePtr undoPagePtr;
+ PagePtr pagePtr;
+ LocalLogInfoPtr regLliPtr;
+
+ regLliPtr.i = regFragPtr->checkpointVersion;
+ ptrCheckGuard(regLliPtr, cnoOfParallellUndoFiles, localLogInfo);
+
+ pagePtr.i = pageIndex;
+ ptrCheckGuard(pagePtr, cnoOfPage, page);
+ undoPagePtr.i = regLliPtr.p->lliUndoPage;
+ ptrCheckGuard(undoPagePtr, cnoOfUndoPage, undoPage);
+
+ startOffset++;
+ noOfWords--;
+ if ((regLliPtr.p->lliUndoWord + noOfWords) < ZWORDS_ON_PAGE) {
+ ljam();
+ MEMCOPY_NO_WORDS(&undoPagePtr.p->undoPageWord[regLliPtr.p->lliUndoWord],
+ &pagePtr.p->pageWord[startOffset],
+ noOfWords);
+ regLliPtr.p->lliUndoWord += noOfWords;
+ } else {
+ for (Uint32 i = 0; i < noOfWords; i++) {
+ ljam();
+ Uint32 undoWord = pagePtr.p->pageWord[startOffset + i];
+ cprAddUndoLogWord(signal, regLliPtr.p, undoWord);
+ }//for
+ }//if
+}//Dbtup::cprAddData()
+
+void Dbtup::cprAddLogHeader(Signal* signal,
+ LocalLogInfo* const lliPtr,
+ Uint32 recordType,
+ Uint32 tableId,
+ Uint32 fragId)
+{
+ Uint32 prevRecId = lliPtr->lliPrevRecordId;
+ lliPtr->lliPrevRecordId = lliPtr->lliUndoWord + (lliPtr->lliLogFilePage << ZUNDO_RECORD_ID_PAGE_INDEX);
+ cprAddUndoLogWord(signal, lliPtr, recordType);
+ cprAddUndoLogWord(signal, lliPtr, prevRecId);
+ cprAddUndoLogWord(signal, lliPtr, tableId);
+ cprAddUndoLogWord(signal, lliPtr, fragId);
+}//Dbtup::cprAddLogHeader()
+
+void Dbtup::cprAddGCIUpdate(Signal* signal,
+ Uint32 prevGCI,
+ Fragrecord* const regFragPtr)
+{
+ LocalLogInfoPtr regLliPtr;
+ regLliPtr.i = regFragPtr->checkpointVersion;
+ ptrCheckGuard(regLliPtr, cnoOfParallellUndoFiles, localLogInfo);
+
+ cprAddUndoLogWord(signal, regLliPtr.p, prevGCI);
+}//Dbtup::cprAddLogHeader()
+
+void Dbtup::cprAddUndoLogPageHeader(Signal* signal,
+ Page* const regPagePtr,
+ Fragrecord* const regFragPtr)
+{
+ UndoPagePtr regUndoPagePtr;
+ LocalLogInfoPtr regLliPtr;
+
+ regLliPtr.i = regFragPtr->checkpointVersion;
+ ptrCheckGuard(regLliPtr, cnoOfParallellUndoFiles, localLogInfo);
+
+ Uint32 prevRecId = regLliPtr.p->lliPrevRecordId;
+ Uint32 lliWord = regLliPtr.p->lliUndoWord;
+ regLliPtr.p->lliPrevRecordId = lliWord +
+ (regLliPtr.p->lliLogFilePage << ZUNDO_RECORD_ID_PAGE_INDEX);
+ if ((lliWord + 7) < ZWORDS_ON_PAGE) {
+ ljam();
+ regUndoPagePtr.i = regLliPtr.p->lliUndoPage;
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+
+ regUndoPagePtr.p->undoPageWord[lliWord] = ZLCPR_UNDO_LOG_PAGE_HEADER;
+ regUndoPagePtr.p->undoPageWord[lliWord + 1] = prevRecId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 2] = regFragPtr->fragTableId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 3] = regFragPtr->fragmentId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 4] = regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS];
+ regUndoPagePtr.p->undoPageWord[lliWord + 5] = regPagePtr->pageWord[ZPAGE_STATE_POS];
+ regUndoPagePtr.p->undoPageWord[lliWord + 6] = regPagePtr->pageWord[ZPAGE_NEXT_POS];
+ regLliPtr.p->lliUndoWord = lliWord + 7;
+ } else {
+ ljam();
+ cprAddUndoLogWord(signal, regLliPtr.p, ZLCPR_UNDO_LOG_PAGE_HEADER);
+ cprAddUndoLogWord(signal, regLliPtr.p, prevRecId);
+ cprAddUndoLogWord(signal, regLliPtr.p, regFragPtr->fragTableId);
+ cprAddUndoLogWord(signal, regLliPtr.p, regFragPtr->fragmentId);
+ cprAddUndoLogWord(signal, regLliPtr.p, regPagePtr->pageWord[ZPAGE_FRAG_PAGE_ID_POS]);
+ cprAddUndoLogWord(signal, regLliPtr.p, regPagePtr->pageWord[ZPAGE_STATE_POS]);
+ cprAddUndoLogWord(signal, regLliPtr.p, regPagePtr->pageWord[ZPAGE_NEXT_POS]);
+ }//if
+}//Dbtup::cprAddUndoLogPageHeader()
+
+void Dbtup::cprAddUndoLogRecord(Signal* signal,
+ Uint32 recordType,
+ Uint32 pageId,
+ Uint32 pageIndex,
+ Uint32 tableId,
+ Uint32 fragId,
+ Uint32 localLogIndex)
+{
+ LocalLogInfoPtr regLliPtr;
+ UndoPagePtr regUndoPagePtr;
+
+ regLliPtr.i = localLogIndex;
+ ptrCheckGuard(regLliPtr, cnoOfParallellUndoFiles, localLogInfo);
+
+ Uint32 prevRecId = regLliPtr.p->lliPrevRecordId;
+ Uint32 lliWord = regLliPtr.p->lliUndoWord;
+
+ regLliPtr.p->lliPrevRecordId = lliWord +
+ (regLliPtr.p->lliLogFilePage << ZUNDO_RECORD_ID_PAGE_INDEX);
+ if ((lliWord + 6) < ZWORDS_ON_PAGE) {
+ ljam();
+ regUndoPagePtr.i = regLliPtr.p->lliUndoPage;
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+ regUndoPagePtr.p->undoPageWord[lliWord] = recordType;
+ regUndoPagePtr.p->undoPageWord[lliWord + 1] = prevRecId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 2] = tableId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 3] = fragId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 4] = pageId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 5] = pageIndex;
+
+ regLliPtr.p->lliUndoWord = lliWord + 6;
+ } else {
+ ljam();
+ cprAddUndoLogWord(signal, regLliPtr.p, recordType);
+ cprAddUndoLogWord(signal, regLliPtr.p, prevRecId);
+ cprAddUndoLogWord(signal, regLliPtr.p, tableId);
+ cprAddUndoLogWord(signal, regLliPtr.p, fragId);
+ cprAddUndoLogWord(signal, regLliPtr.p, pageId);
+ cprAddUndoLogWord(signal, regLliPtr.p, pageIndex);
+ }//if
+}//Dbtup::cprAddUndoLogRecord()
+
+void Dbtup::cprAddAbortUpdate(Signal* signal,
+ LocalLogInfo* const lliPtr,
+ Operationrec* const regOperPtr)
+{
+ Uint32 lliWord = lliPtr->lliUndoWord;
+ if ((lliWord + 4) < ZWORDS_ON_PAGE) {
+ ljam();
+ UndoPagePtr regUndoPagePtr;
+ regUndoPagePtr.i = lliPtr->lliUndoPage;
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+
+ regUndoPagePtr.p->undoPageWord[lliWord] = regOperPtr->fragPageId;
+ regUndoPagePtr.p->undoPageWord[lliWord + 1] = regOperPtr->pageIndex;
+ regUndoPagePtr.p->undoPageWord[lliWord + 2] = regOperPtr->fragPageIdC;
+ regUndoPagePtr.p->undoPageWord[lliWord + 3] = regOperPtr->pageIndexC;
+ lliPtr->lliUndoWord = lliWord + 4;
+ } else {
+ ljam();
+ cprAddUndoLogWord(signal, lliPtr, regOperPtr->fragPageId);
+ cprAddUndoLogWord(signal, lliPtr, regOperPtr->pageIndex);
+ cprAddUndoLogWord(signal, lliPtr, regOperPtr->fragPageIdC);
+ cprAddUndoLogWord(signal, lliPtr, regOperPtr->pageIndexC);
+ }//if
+}//Dbtup::cprAddAbortUpdate()
+
+void Dbtup::cprAddUndoLogWord(Signal* signal, LocalLogInfo* const lliPtr, Uint32 undoWord)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+ UndoPagePtr regUndoPagePtr;
+
+ ljam();
+ regUndoPagePtr.i = lliPtr->lliUndoPage;
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+ ndbrequire(lliPtr->lliUndoWord < ZWORDS_ON_PAGE);
+ regUndoPagePtr.p->undoPageWord[lliPtr->lliUndoWord] = undoWord;
+
+ lliPtr->lliUndoWord++;
+ if (lliPtr->lliUndoWord == ZWORDS_ON_PAGE) {
+ ljam();
+ lliPtr->lliUndoWord = ZUNDO_PAGE_HEADER_SIZE;
+ lliPtr->lliUndoPage++;
+ if (clblPageCounter > 0) {
+ ljam();
+ clblPageCounter--;
+ }//if
+ dbsiPtr.i = lliPtr->lliUndoBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ dbsiPtr.p->pdxNumDataPages++;
+ ndbrequire(dbsiPtr.p->pdxNumDataPages < 16);
+ lliPtr->lliLogFilePage++;
+ if (dbsiPtr.p->pdxNumDataPages == ZUB_SEGMENT_SIZE) {
+ ljam();
+ lcpWriteUndoSegment(signal, lliPtr, false);
+ }//if
+ }//if
+}//Dbtup::cprAddUndoLogWord()
+
+void Dbtup::lcpWriteUndoSegment(Signal* signal, LocalLogInfo* const lliPtr, bool flushFlag)
+{
+ DiskBufferSegmentInfoPtr dbsiPtr;
+
+ dbsiPtr.i = lliPtr->lliUndoBufferSegmentP;
+ ptrCheckGuard(dbsiPtr, cnoOfConcurrentWriteOp, diskBufferSegmentInfo);
+ Uint32 flags = 1;
+ lliPtr->lliUndoPagesToDiskWithoutSynch += dbsiPtr.p->pdxNumDataPages;
+ if ((lliPtr->lliUndoPagesToDiskWithoutSynch > MAX_PAGES_WITHOUT_SYNCH) ||
+ (flushFlag)) {
+ ljam();
+/* ---------------------------------------------------------------- */
+// To avoid synching too big chunks at a time we synch after writing
+// a certain number of data pages. (e.g. 2 MBytes).
+/* ---------------------------------------------------------------- */
+ lliPtr->lliUndoPagesToDiskWithoutSynch = 0;
+ flags |= 0x10; //Set synch flag unconditionally
+ }//if
+ dbsiPtr.p->pdxOperation = CHECKPOINT_UNDO_WRITE;
+ signal->theData[0] = lliPtr->lliUndoFileHandle;
+ signal->theData[1] = cownref;
+ signal->theData[2] = dbsiPtr.i;
+ signal->theData[3] = flags;
+ signal->theData[4] = ZBASE_ADDR_UNDO_WORD;
+ signal->theData[5] = dbsiPtr.p->pdxNumDataPages;
+ signal->theData[6] = dbsiPtr.p->pdxDataPage[0];
+ signal->theData[7] = dbsiPtr.p->pdxFilePage;
+ sendSignal(NDBFS_REF, GSN_FSWRITEREQ, signal, 8, JBA);
+
+ DiskBufferSegmentInfoPtr newDbsiPtr;
+ UndoPagePtr newUndoPagePtr;
+
+ seizeUndoBufferSegment(signal, newUndoPagePtr);
+ seizeDiskBufferSegmentRecord(newDbsiPtr);
+ newDbsiPtr.p->pdxBuffertype = UNDO_PAGES;
+ for (Uint32 i = 0; i < ZUB_SEGMENT_SIZE; i++) {
+ newDbsiPtr.p->pdxDataPage[i] = newUndoPagePtr.i + i;
+ }//for
+ newDbsiPtr.p->pdxFilePage = lliPtr->lliLogFilePage;
+ lliPtr->lliUndoPage = newUndoPagePtr.i;
+ lliPtr->lliUndoBufferSegmentP = newDbsiPtr.i;
+}//Dbtup::lcpWriteUndoSegment()
+
+void Dbtup::seizeUndoBufferSegment(Signal* signal, UndoPagePtr& regUndoPagePtr)
+{
+ if (cnoFreeUndoSeg == ZMIN_PAGE_LIMIT_TUP_COMMITREQ) {
+ EXECUTE_DIRECT(DBLQH, GSN_TUP_COM_BLOCK, signal, 1);
+ ljamEntry();
+ }//if
+ cnoFreeUndoSeg--;
+ ndbrequire(cnoFreeUndoSeg >= 0);
+ ndbrequire(cfirstfreeUndoSeg != RNIL);
+ regUndoPagePtr.i = cfirstfreeUndoSeg;
+ ptrCheckGuard(regUndoPagePtr, cnoOfUndoPage, undoPage);
+ cfirstfreeUndoSeg = regUndoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS];
+ regUndoPagePtr.p->undoPageWord[ZPAGE_NEXT_POS] = RNIL;
+}//Dbtup::seizeUndoBufferSegment()
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/dbtup/Makefile.am b/storage/ndb/src/kernel/blocks/dbtup/Makefile.am
new file mode 100644
index 00000000000..e51410e6be3
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/Makefile.am
@@ -0,0 +1,41 @@
+noinst_LIBRARIES = libdbtup.a
+
+libdbtup_a_SOURCES = \
+ DbtupExecQuery.cpp \
+ DbtupBuffer.cpp \
+ DbtupRoutines.cpp \
+ DbtupCommit.cpp \
+ DbtupFixAlloc.cpp \
+ DbtupTrigger.cpp \
+ DbtupAbort.cpp \
+ DbtupLCP.cpp \
+ DbtupUndoLog.cpp \
+ DbtupPageMap.cpp \
+ DbtupPagMan.cpp \
+ DbtupStoredProcDef.cpp \
+ DbtupMeta.cpp \
+ DbtupTabDesMan.cpp \
+ DbtupGen.cpp \
+ DbtupSystemRestart.cpp \
+ DbtupIndex.cpp \
+ DbtupDebug.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbtup.dsp
+
+libdbtup.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbtup_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbtup/Notes.txt b/storage/ndb/src/kernel/blocks/dbtup/Notes.txt
new file mode 100644
index 00000000000..9d47c591fe8
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtup/Notes.txt
@@ -0,0 +1,183 @@
+Operations, tuples, versions
+============================
+
+Operation types.
+
+INSERT insert new original tuple, or insert after delete
+UPDATE update
+DELETE delete
+
+Following need not be considered here.
+
+READ does not change tuples or versions
+WRITE turns into INSERT or UPDATE in LQH
+
+We use more specific names in some cases:
+
+first/INSERT initial insert of new tuple
+delete/INSERT INSERT preceded by DELETE
+DELETE/last DELETE as last operation
+DELETE/insert DELETE followed by INSERT
+
+Tuple + op Can be followed by
+-------------- ------------------
+does not exist first/INSERT
+tuple exists UPDATE DELETE
+INSERT UPDATE DELETE
+UPDATE UPDATE DELETE
+DELETE delete/INSERT
+
+Operations on same tuple are kept in doubly linked list until
+commit or abort. The links at both ends are RNIL i.e. the list
+is not circular. The links are:
+
+nextActiveOp the operation BEFORE this one, in event order
+prevActiveOp the operation AFTER this one, in event order
+
+Operations are done on the "original tuple" i.e. the tuple is
+modified in place. If an operation is about to write over data
+in original tuple, it first copies the tuple to a "copy tuple".
+
+Operation Copy tuple
+--------- ----------
+first/INSERT no
+delete/INSERT yes (this is in effect an update)
+UPDATE yes
+DELETE no
+
+The operation points to the tuples via:
+
+realPageId page i-value of original tuple
+pageOffset word offset of original tuple on the page
+realPageIdC page i-value of copy tuple or RNIL is no copy exists
+pageOffsetC word offset of copy tuple on the page
+
+The original tuple and the copy tuple (if any) point back to
+the operation via word 0. In copy tuple this pointer is never
+changed. In original tuple however it always points to the LATEST
+existing operation i.e. the one with prevActiveOp == RNIL.
+Thus word 0 of original tuple is changed on 2 occasions:
+
+- when a new operation is added to the list
+- when commit or abort removes the latest operation
+
+Note that commit/abort of operations occurs in random order.
+The list is adjusted accordingly.
+
+Versions
+--------
+
+Tuple version is stored in tuple word 1. A new original tuple
+gets version 0. The version is incremented by each new operation
+which makes a copy tuple. Version number wraps around at 15 bits.
+
+When a copy tuple is made, the version in original tuple is copied
+to copy tuple as part of tuple data. This takes place before
+the version in original tuple is updated.
+
+Each operation record contains tuple version called tupVersion.
+
+- at insert of new original tuple, tupVersion is set to 0
+
+- if tuple already exists, the FIRST operation (in event order)
+ reads tupVersion from tuple word 1. If the operation is
+ not DELETE, the version is incremented
+
+- subsequent operation reads tupVersion from the operation
+ BEFORE it (nextActiveOp). If this subsequent operation is
+ not DELETE, the version is incremented
+
+When the operation writes the tuple it sets word 1 to tupVersion.
+In detail, per operation type, where INSERT is divided into
+insert of new original tuple and insert after delete:
+
+Operation Copy Increment Set version in original
+--------- ---- --------- -----------------------
+first/INSERT no no yes, to 0
+delete/INSERT yes yes yes
+UPDATE yes yes yes
+DELETE no no no
+
+Thus an existing version is incremented if and only if
+a copy tuple is made.
+
+Ordered index maintenance
+-------------------------
+
+Each index entry has logical tuple address and tuple version.
+Index entries are added during prepare phase (when each operation
+is executed) and removed during commit or abort phase.
+
+Access to correct tuple version (original or copy) is required
+in TUX which reads index key values 1) to check that at least one
+is not null 2) to do tree search 3) to set min/max prefixes.
+See "Read attributes" below.
+
+An additional complication is that commit/abort of operations
+arrives in random order. So we cannot check for, for example,
+DELETE/insert by looking at prevActiveOp.
+
+Phase Op Action Version in
+----- -- ------ ----------
+prepare INSERT add op and original
+prepare UPDATE add op and original
+prepare DELETE none -
+
+commit first/INSERT none -
+commit delete/INSERT remove copy tuple 1)
+commit UPDATE remove copy tuple 1)
+commit DELETE/last remove op and original
+commit DELETE/insert none -
+
+abort INSERT remove op
+abort UPDATE remove op
+abort DELETE none -
+
+1) alternatively, store prevTupVersion in operation record.
+
+Read attributes, query status
+-----------------------------
+
+TUP_READ_ATTRS signal (or equivalent direct call) reads attribute
+values. Input is logical address of original tuple and tuple
+version. The steps are:
+
+- Translate logical address to physical address of original tuple.
+
+- If version of original tuple in word 1 is right, stop.
+
+- Otherwise word 0 points to LATEST not yet deleted operation.
+ Walk through operation list via nextActiveOp.
+
+- If an operation on the list has realPageIdC == RNIL, skip it.
+
+- Otherwise find copy tuple via realPageIdC, pageOffsetC.
+ If the version of the copy tuple in word 1 is right, stop.
+
+- Call readAttributes() on the tuple found (original or copy).
+
+In short, the version must exist in some not yet deleted tuple,
+either in original or in some copy.
+
+Note that this must work during all phases since index code
+needs to read index key attributes from correct tuple version in
+each add/remove operation.
+
+TUP_QUERY_TH signal (or equivalent direct call) does same search
+for tuple version. It is called from index scan and returns info
+used to decide if the scan can see the tuple.
+
+This signal may also be called during any phase since commit/abort
+of all operations is not done in one time-slice.
+
+Commit and abort
+----------------
+
+[ hairy stuff ]
+
+Problems
+--------
+
+Current abort code can destroy a tuple version too early. This
+happens in test case "ticuur" (insert-commit-update-update-rollback),
+if abort of first update arrives before abort of second update.
diff --git a/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp b/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp
new file mode 100644
index 00000000000..5c12472a0f7
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp
@@ -0,0 +1,1291 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBTUX_H
+#define DBTUX_H
+
+#include <ndb_limits.h>
+#include <SimulatedBlock.hpp>
+#include <AttributeDescriptor.hpp>
+#include <AttributeHeader.hpp>
+#include <ArrayPool.hpp>
+#include <DataBuffer.hpp>
+#include <md5_hash.hpp>
+
+// big brother
+#include <Dbtup.hpp>
+
+// signal classes
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/TuxContinueB.hpp>
+#include <signaldata/TupFrag.hpp>
+#include <signaldata/AlterIndx.hpp>
+#include <signaldata/DropTab.hpp>
+#include <signaldata/TuxMaint.hpp>
+#include <signaldata/AccScan.hpp>
+#include <signaldata/TuxBound.hpp>
+#include <signaldata/NextScan.hpp>
+#include <signaldata/AccLock.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+// debug
+#ifdef VM_TRACE
+#include <NdbOut.hpp>
+#include <OutputStream.hpp>
+#endif
+
+// jams
+#undef jam
+#undef jamEntry
+#ifdef DBTUX_GEN_CPP
+#define jam() jamLine(10000 + __LINE__)
+#define jamEntry() jamEntryLine(10000 + __LINE__)
+#endif
+#ifdef DBTUX_META_CPP
+#define jam() jamLine(20000 + __LINE__)
+#define jamEntry() jamEntryLine(20000 + __LINE__)
+#endif
+#ifdef DBTUX_MAINT_CPP
+#define jam() jamLine(30000 + __LINE__)
+#define jamEntry() jamEntryLine(30000 + __LINE__)
+#endif
+#ifdef DBTUX_NODE_CPP
+#define jam() jamLine(40000 + __LINE__)
+#define jamEntry() jamEntryLine(40000 + __LINE__)
+#endif
+#ifdef DBTUX_TREE_CPP
+#define jam() jamLine(50000 + __LINE__)
+#define jamEntry() jamEntryLine(50000 + __LINE__)
+#endif
+#ifdef DBTUX_SCAN_CPP
+#define jam() jamLine(60000 + __LINE__)
+#define jamEntry() jamEntryLine(60000 + __LINE__)
+#endif
+#ifdef DBTUX_SEARCH_CPP
+#define jam() jamLine(70000 + __LINE__)
+#define jamEntry() jamEntryLine(70000 + __LINE__)
+#endif
+#ifdef DBTUX_CMP_CPP
+#define jam() jamLine(80000 + __LINE__)
+#define jamEntry() jamEntryLine(80000 + __LINE__)
+#endif
+#ifdef DBTUX_DEBUG_CPP
+#define jam() jamLine(90000 + __LINE__)
+#define jamEntry() jamEntryLine(90000 + __LINE__)
+#endif
+#ifndef jam
+#define jam() jamLine(__LINE__)
+#define jamEntry() jamEntryLine(__LINE__)
+#endif
+
+#undef max
+#undef min
+
+class Configuration;
+
+class Dbtux : public SimulatedBlock {
+public:
+ Dbtux(const Configuration& conf);
+ virtual ~Dbtux();
+
+ // pointer to TUP instance in this thread
+ Dbtup* c_tup;
+
+private:
+ // sizes are in words (Uint32)
+ STATIC_CONST( MaxIndexFragments = 2 * MAX_FRAG_PER_NODE );
+ STATIC_CONST( MaxIndexAttributes = MAX_ATTRIBUTES_IN_INDEX );
+ STATIC_CONST( MaxAttrDataSize = 2048 );
+public:
+ STATIC_CONST( DescPageSize = 256 );
+private:
+ STATIC_CONST( MaxTreeNodeSize = MAX_TTREE_NODE_SIZE );
+ STATIC_CONST( MaxPrefSize = MAX_TTREE_PREF_SIZE );
+ STATIC_CONST( ScanBoundSegmentSize = 7 );
+ STATIC_CONST( MaxAccLockOps = MAX_PARALLEL_OP_PER_SCAN );
+ BLOCK_DEFINES(Dbtux);
+
+ // forward declarations
+ struct DescEnt;
+
+ /*
+ * Pointer to array of Uint32.
+ */
+ struct Data {
+ private:
+ Uint32* m_data;
+ public:
+ Data();
+ Data(Uint32* data);
+ Data& operator=(Uint32* data);
+ operator Uint32*() const;
+ Data& operator+=(size_t n);
+ AttributeHeader& ah() const;
+ };
+ friend class Data;
+
+ /*
+ * Pointer to array of constant Uint32.
+ */
+ struct ConstData;
+ friend struct ConstData;
+ struct ConstData {
+ private:
+ const Uint32* m_data;
+ public:
+ ConstData();
+ ConstData(const Uint32* data);
+ ConstData& operator=(const Uint32* data);
+ operator const Uint32*() const;
+ ConstData& operator+=(size_t n);
+ const AttributeHeader& ah() const;
+ // non-const pointer can be cast to const pointer
+ ConstData(Data data);
+ ConstData& operator=(Data data);
+ };
+
+ // AttributeHeader size is assumed to be 1 word
+ STATIC_CONST( AttributeHeaderSize = 1 );
+
+ /*
+ * Logical tuple address, "local key". Identifies table tuples.
+ */
+ typedef Uint32 TupAddr;
+ STATIC_CONST( NullTupAddr = (Uint32)-1 );
+
+ /*
+ * Physical tuple address in TUP. Provides fast access to table tuple
+ * or index node. Valid within the db node and across timeslices.
+ * Not valid between db nodes or across restarts.
+ *
+ * To avoid wasting an Uint16 the pageid is split in two.
+ */
+ struct TupLoc {
+ private:
+ Uint16 m_pageId1; // page i-value (big-endian)
+ Uint16 m_pageId2;
+ Uint16 m_pageOffset; // page offset in words
+ public:
+ TupLoc();
+ TupLoc(Uint32 pageId, Uint16 pageOffset);
+ Uint32 getPageId() const;
+ void setPageId(Uint32 pageId);
+ Uint32 getPageOffset() const;
+ void setPageOffset(Uint32 pageOffset);
+ bool operator==(const TupLoc& loc) const;
+ bool operator!=(const TupLoc& loc) const;
+ };
+
+ /*
+ * There is no const member NullTupLoc since the compiler may not be
+ * able to optimize it to TupLoc() constants. Instead null values are
+ * constructed on the stack with TupLoc().
+ */
+#define NullTupLoc TupLoc()
+
+ // tree definitions
+
+ /*
+ * Tree entry. Points to a tuple in primary table via physical
+ * address of "original" tuple and tuple version.
+ *
+ * ZTUP_VERSION_BITS must be 15 (or less).
+ */
+ struct TreeEnt;
+ friend struct TreeEnt;
+ struct TreeEnt {
+ TupLoc m_tupLoc; // address of original tuple
+ unsigned m_tupVersion : 15; // version
+ unsigned m_fragBit : 1; // which duplicated table fragment
+ TreeEnt();
+ // methods
+ bool eq(const TreeEnt ent) const;
+ int cmp(const TreeEnt ent) const;
+ };
+ STATIC_CONST( TreeEntSize = sizeof(TreeEnt) >> 2 );
+ static const TreeEnt NullTreeEnt;
+
+ /*
+ * Tree node has 1) fixed part 2) a prefix of index key data for min
+ * entry 3) max and min entries 4) rest of entries 5) one extra entry
+ * used as work space.
+ *
+ * struct TreeNode part 1, size 6 words
+ * min prefix part 2, size TreeHead::m_prefSize
+ * max entry part 3
+ * min entry part 3
+ * rest of entries part 4
+ * work entry part 5
+ *
+ * There are 3 links to other nodes: left child, right child, parent.
+ * Occupancy (number of entries) is at least 1 except temporarily when
+ * a node is about to be removed.
+ */
+ struct TreeNode;
+ friend struct TreeNode;
+ struct TreeNode {
+ TupLoc m_link[3]; // link to 0-left child 1-right child 2-parent
+ unsigned m_side : 2; // we are 0-left child 1-right child 2-root
+ unsigned m_balance : 2; // balance -1, 0, +1 plus 1 for Solaris CC
+ unsigned pad1 : 4;
+ Uint8 m_occup; // current number of entries
+ Uint32 m_nodeScan; // list of scans at this node
+ TreeNode();
+ };
+ STATIC_CONST( NodeHeadSize = sizeof(TreeNode) >> 2 );
+
+ /*
+ * Tree node "access size" was for an early version with signal
+ * interface to TUP. It is now used only to compute sizes.
+ */
+ enum AccSize {
+ AccNone = 0,
+ AccHead = 1, // part 1
+ AccPref = 2, // parts 1-3
+ AccFull = 3 // parts 1-5
+ };
+
+ /*
+ * Tree header. There is one in each fragment. Contains tree
+ * parameters and address of root node.
+ */
+ struct TreeHead;
+ friend struct TreeHead;
+ struct TreeHead {
+ Uint8 m_nodeSize; // words in tree node
+ Uint8 m_prefSize; // words in min prefix
+ Uint8 m_minOccup; // min entries in internal node
+ Uint8 m_maxOccup; // max entries in node
+ TupLoc m_root; // root node
+ TreeHead();
+ // methods
+ unsigned getSize(AccSize acc) const;
+ Data getPref(TreeNode* node) const;
+ TreeEnt* getEntList(TreeNode* node) const;
+ };
+
+ /*
+ * Tree position. Specifies node, position within node (from 0 to
+ * m_occup), and whether the position is at an existing entry or
+ * before one (if any). Position m_occup points past the node and is
+ * also represented by position 0 of next node. Includes direction
+ * used by scan.
+ */
+ struct TreePos;
+ friend struct TreePos;
+ struct TreePos {
+ TupLoc m_loc; // physical node address
+ Uint16 m_pos; // position 0 to m_occup
+ Uint8 m_match; // at an existing entry
+ Uint8 m_dir; // see scanNext()
+ TreePos();
+ };
+
+ // packed metadata
+
+ /*
+ * Descriptor page. The "hot" metadata for an index is stored as
+ * a contiguous array of words on some page.
+ */
+ struct DescPage;
+ friend struct DescPage;
+ struct DescPage {
+ Uint32 m_nextPage;
+ Uint32 m_numFree; // number of free words
+ union {
+ Uint32 m_data[DescPageSize];
+ Uint32 nextPool;
+ };
+ DescPage();
+ };
+ typedef Ptr<DescPage> DescPagePtr;
+ ArrayPool<DescPage> c_descPagePool;
+ Uint32 c_descPageList;
+
+ /*
+ * Header for index metadata. Size must be multiple of word size.
+ */
+ struct DescHead {
+ unsigned m_indexId : 24;
+ unsigned pad1 : 8;
+ };
+ STATIC_CONST( DescHeadSize = sizeof(DescHead) >> 2 );
+
+ /*
+ * Attribute metadata. Size must be multiple of word size.
+ *
+ * Prefix comparison of char data must use strxfrm and binary
+ * comparison. The charset is currently unused.
+ */
+ struct DescAttr {
+ Uint32 m_attrDesc; // standard AttributeDescriptor
+ Uint16 m_primaryAttrId;
+ unsigned m_typeId : 6;
+ unsigned m_charset : 10;
+ };
+ STATIC_CONST( DescAttrSize = sizeof(DescAttr) >> 2 );
+
+ /*
+ * Complete metadata for one index. The array of attributes has
+ * variable size.
+ */
+ struct DescEnt;
+ friend struct DescEnt;
+ struct DescEnt {
+ DescHead m_descHead;
+ DescAttr m_descAttr[1]; // variable size data
+ };
+
+ // range scan
+
+ /*
+ * Scan bounds are stored in linked list of segments.
+ */
+ typedef DataBuffer<ScanBoundSegmentSize> ScanBound;
+ typedef DataBuffer<ScanBoundSegmentSize>::ConstDataBufferIterator ScanBoundIterator;
+ typedef DataBuffer<ScanBoundSegmentSize>::DataBufferPool ScanBoundPool;
+ ScanBoundPool c_scanBoundPool;
+
+ /*
+ * Scan operation.
+ *
+ * Tuples are locked one at a time. The current lock op is set to
+ * RNIL as soon as the lock is obtained and passed to LQH. We must
+ * however remember all locks which LQH has not returned for unlocking
+ * since they must be aborted by us when the scan is closed.
+ *
+ * Scan state describes the entry we are interested in. There is
+ * a separate lock wait flag. It may be for current entry or it may
+ * be for an entry we were moved away from. In any case nothing
+ * happens with current entry before lock wait flag is cleared.
+ *
+ * An unfinished scan is always linked to some tree node, and has
+ * current position and direction (see comments at scanNext). There
+ * is also a copy of latest entry found.
+ */
+ struct ScanOp;
+ friend struct ScanOp;
+ struct ScanOp {
+ enum {
+ Undef = 0,
+ First = 1, // before first entry
+ Current = 2, // at current before locking
+ Blocked = 3, // at current waiting for ACC lock
+ Locked = 4, // at current and locked or no lock needed
+ Next = 5, // looking for next extry
+ Last = 6, // after last entry
+ Aborting = 7, // lock wait at scan close
+ Invalid = 9 // cannot return REF to LQH currently
+ };
+ Uint16 m_state;
+ Uint16 m_lockwait;
+ Uint32 m_userPtr; // scanptr.i in LQH
+ Uint32 m_userRef;
+ Uint32 m_tableId;
+ Uint32 m_indexId;
+ Uint32 m_fragId;
+ Uint32 m_fragPtrI;
+ Uint32 m_transId1;
+ Uint32 m_transId2;
+ Uint32 m_savePointId;
+ // lock waited for or obtained and not yet passed to LQH
+ Uint32 m_accLockOp;
+ Uint8 m_readCommitted; // no locking
+ Uint8 m_lockMode;
+ Uint8 m_descending;
+ ScanBound m_boundMin;
+ ScanBound m_boundMax;
+ ScanBound* m_bound[2]; // pointers to above 2
+ Uint16 m_boundCnt[2]; // number of bounds in each
+ TreePos m_scanPos; // position
+ TreeEnt m_scanEnt; // latest entry found
+ Uint32 m_nodeScan; // next scan at node (single-linked)
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ /*
+ * Locks obtained and passed to LQH but not yet returned by LQH.
+ * The max was increased from 16 to 992 (default 64). Record max
+ * ever used in this scan. TODO fix quadratic behaviour
+ */
+ Uint32 m_maxAccLockOps;
+ Uint32 m_accLockOps[MaxAccLockOps];
+ ScanOp(ScanBoundPool& scanBoundPool);
+ };
+ typedef Ptr<ScanOp> ScanOpPtr;
+ ArrayPool<ScanOp> c_scanOpPool;
+
+ // indexes and fragments
+
+ /*
+ * Ordered index. Top level data structure. The primary table (table
+ * being indexed) lives in TUP.
+ */
+ struct Index;
+ friend struct Index;
+ struct Index {
+ enum State {
+ NotDefined = 0,
+ Defining = 1,
+ Online = 2, // triggers activated and build done
+ Dropping = 9
+ };
+ State m_state;
+ DictTabInfo::TableType m_tableType;
+ Uint32 m_tableId;
+ Uint16 unused;
+ Uint16 m_numFrags;
+ Uint32 m_fragId[MaxIndexFragments];
+ Uint32 m_fragPtrI[MaxIndexFragments];
+ Uint32 m_descPage; // descriptor page
+ Uint16 m_descOff; // offset within the page
+ Uint16 m_numAttrs;
+ bool m_storeNullKey;
+ union {
+ Uint32 nextPool;
+ };
+ Index();
+ };
+ typedef Ptr<Index> IndexPtr;
+ ArrayPool<Index> c_indexPool;
+
+ /*
+ * Fragment of an index, as known to DIH/TC. Represents the two
+ * duplicate fragments known to LQH/ACC/TUP. Includes tree header.
+ * There are no maintenance operation records yet.
+ */
+ struct Frag;
+ friend struct Frag;
+ struct Frag {
+ Uint32 m_tableId; // copy from index level
+ Uint32 m_indexId;
+ Uint16 unused;
+ Uint16 m_fragId;
+ Uint32 m_descPage; // copy from index level
+ Uint16 m_descOff;
+ Uint16 m_numAttrs;
+ bool m_storeNullKey;
+ TreeHead m_tree;
+ TupLoc m_freeLoc; // list of free index nodes
+ DLList<ScanOp> m_scanList; // current scans on this fragment
+ Uint32 m_tupIndexFragPtrI;
+ Uint32 m_tupTableFragPtrI[2];
+ Uint32 m_accTableFragPtrI[2];
+ union {
+ Uint32 nextPool;
+ };
+ Frag(ArrayPool<ScanOp>& scanOpPool);
+ };
+ typedef Ptr<Frag> FragPtr;
+ ArrayPool<Frag> c_fragPool;
+
+ /*
+ * Fragment metadata operation.
+ */
+ struct FragOp {
+ Uint32 m_userPtr;
+ Uint32 m_userRef;
+ Uint32 m_indexId;
+ Uint32 m_fragId;
+ Uint32 m_fragPtrI;
+ Uint32 m_fragNo; // fragment number starting at zero
+ Uint32 m_numAttrsRecvd;
+ union {
+ Uint32 nextPool;
+ };
+ FragOp();
+ };
+ typedef Ptr<FragOp> FragOpPtr;
+ ArrayPool<FragOp> c_fragOpPool;
+
+ // node handles
+
+ /*
+ * A node handle is a reference to a tree node in TUP. It is used to
+ * operate on the node. Node handles are allocated on the stack.
+ */
+ struct NodeHandle;
+ friend struct NodeHandle;
+ struct NodeHandle {
+ Frag& m_frag; // fragment using the node
+ TupLoc m_loc; // physical node address
+ TreeNode* m_node; // pointer to node storage
+ NodeHandle(Frag& frag);
+ NodeHandle(const NodeHandle& node);
+ NodeHandle& operator=(const NodeHandle& node);
+ // check if unassigned
+ bool isNull();
+ // getters
+ TupLoc getLink(unsigned i);
+ unsigned getChilds(); // cannot spell
+ unsigned getSide();
+ unsigned getOccup();
+ int getBalance();
+ Uint32 getNodeScan();
+ // setters
+ void setLink(unsigned i, TupLoc loc);
+ void setSide(unsigned i);
+ void setOccup(unsigned n);
+ void setBalance(int b);
+ void setNodeScan(Uint32 scanPtrI);
+ // access other parts of the node
+ Data getPref();
+ TreeEnt getEnt(unsigned pos);
+ TreeEnt getMinMax(unsigned i);
+ // for ndbrequire and ndbassert
+ void progError(int line, int cause, const char* file);
+ };
+
+ // methods
+
+ /*
+ * DbtuxGen.cpp
+ */
+ void execCONTINUEB(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ // utils
+ void setKeyAttrs(const Frag& frag);
+ void readKeyAttrs(const Frag& frag, TreeEnt ent, unsigned start, Data keyData);
+ void readTablePk(const Frag& frag, TreeEnt ent, Data pkData, unsigned& pkSize);
+ void copyAttrs(const Frag& frag, ConstData data1, Data data2, unsigned maxlen2 = MaxAttrDataSize);
+
+ /*
+ * DbtuxMeta.cpp
+ */
+ void execTUXFRAGREQ(Signal* signal);
+ void execTUX_ADD_ATTRREQ(Signal* signal);
+ void execALTER_INDX_REQ(Signal* signal);
+ void execDROP_TAB_REQ(Signal* signal);
+ bool allocDescEnt(IndexPtr indexPtr);
+ void freeDescEnt(IndexPtr indexPtr);
+ void abortAddFragOp(Signal* signal);
+ void dropIndex(Signal* signal, IndexPtr indexPtr, Uint32 senderRef, Uint32 senderData);
+
+ /*
+ * DbtuxMaint.cpp
+ */
+ void execTUX_MAINT_REQ(Signal* signal);
+
+ /*
+ * DbtuxNode.cpp
+ */
+ int allocNode(Signal* signal, NodeHandle& node);
+ void selectNode(NodeHandle& node, TupLoc loc);
+ void insertNode(NodeHandle& node);
+ void deleteNode(NodeHandle& node);
+ void setNodePref(NodeHandle& node);
+ // node operations
+ void nodePushUp(NodeHandle& node, unsigned pos, const TreeEnt& ent, Uint32 scanList);
+ void nodePushUpScans(NodeHandle& node, unsigned pos);
+ void nodePopDown(NodeHandle& node, unsigned pos, TreeEnt& en, Uint32* scanList);
+ void nodePopDownScans(NodeHandle& node, unsigned pos);
+ void nodePushDown(NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32& scanList);
+ void nodePushDownScans(NodeHandle& node, unsigned pos);
+ void nodePopUp(NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32 scanList);
+ void nodePopUpScans(NodeHandle& node, unsigned pos);
+ void nodeSlide(NodeHandle& dstNode, NodeHandle& srcNode, unsigned cnt, unsigned i);
+ // scans linked to node
+ void addScanList(NodeHandle& node, unsigned pos, Uint32 scanList);
+ void removeScanList(NodeHandle& node, unsigned pos, Uint32& scanList);
+ void moveScanList(NodeHandle& node, unsigned pos);
+ void linkScan(NodeHandle& node, ScanOpPtr scanPtr);
+ void unlinkScan(NodeHandle& node, ScanOpPtr scanPtr);
+ bool islinkScan(NodeHandle& node, ScanOpPtr scanPtr);
+
+ /*
+ * DbtuxTree.cpp
+ */
+ // add entry
+ void treeAdd(Frag& frag, TreePos treePos, TreeEnt ent);
+ void treeAddFull(Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent);
+ void treeAddNode(Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent, NodeHandle parentNode, unsigned i);
+ void treeAddRebalance(Frag& frag, NodeHandle node, unsigned i);
+ // remove entry
+ void treeRemove(Frag& frag, TreePos treePos);
+ void treeRemoveInner(Frag& frag, NodeHandle lubNode, unsigned pos);
+ void treeRemoveSemi(Frag& frag, NodeHandle node, unsigned i);
+ void treeRemoveLeaf(Frag& frag, NodeHandle node);
+ void treeRemoveNode(Frag& frag, NodeHandle node);
+ void treeRemoveRebalance(Frag& frag, NodeHandle node, unsigned i);
+ // rotate
+ void treeRotateSingle(Frag& frag, NodeHandle& node, unsigned i);
+ void treeRotateDouble(Frag& frag, NodeHandle& node, unsigned i);
+
+ /*
+ * DbtuxScan.cpp
+ */
+ void execACC_SCANREQ(Signal* signal);
+ void execTUX_BOUND_INFO(Signal* signal);
+ void execNEXT_SCANREQ(Signal* signal);
+ void execACC_CHECK_SCAN(Signal* signal);
+ void execACCKEYCONF(Signal* signal);
+ void execACCKEYREF(Signal* signal);
+ void execACC_ABORTCONF(Signal* signal);
+ void scanFirst(ScanOpPtr scanPtr);
+ void scanNext(ScanOpPtr scanPtr, bool fromMaintReq);
+ bool scanVisible(ScanOpPtr scanPtr, TreeEnt ent);
+ void scanClose(Signal* signal, ScanOpPtr scanPtr);
+ void addAccLockOp(ScanOp& scan, Uint32 accLockOp);
+ void removeAccLockOp(ScanOp& scan, Uint32 accLockOp);
+ void releaseScanOp(ScanOpPtr& scanPtr);
+
+ /*
+ * DbtuxSearch.cpp
+ */
+ void searchToAdd(Frag& frag, ConstData searchKey, TreeEnt searchEnt, TreePos& treePos);
+ void searchToRemove(Frag& frag, ConstData searchKey, TreeEnt searchEnt, TreePos& treePos);
+ void searchToScan(Frag& frag, ConstData boundInfo, unsigned boundCount, bool descending, TreePos& treePos);
+ void searchToScanAscending(Frag& frag, ConstData boundInfo, unsigned boundCount, TreePos& treePos);
+ void searchToScanDescending(Frag& frag, ConstData boundInfo, unsigned boundCount, TreePos& treePos);
+
+ /*
+ * DbtuxCmp.cpp
+ */
+ int cmpSearchKey(const Frag& frag, unsigned& start, ConstData searchKey, ConstData entryData, unsigned maxlen = MaxAttrDataSize);
+ int cmpScanBound(const Frag& frag, unsigned dir, ConstData boundInfo, unsigned boundCount, ConstData entryData, unsigned maxlen = MaxAttrDataSize);
+
+ /*
+ * DbtuxDebug.cpp
+ */
+ void execDUMP_STATE_ORD(Signal* signal);
+#ifdef VM_TRACE
+ struct PrintPar {
+ char m_path[100]; // LR prefix
+ unsigned m_side; // expected side
+ TupLoc m_parent; // expected parent address
+ int m_depth; // returned depth
+ unsigned m_occup; // returned occupancy
+ TreeEnt m_minmax[2]; // returned subtree min and max
+ bool m_ok; // returned status
+ PrintPar();
+ };
+ void printTree(Signal* signal, Frag& frag, NdbOut& out);
+ void printNode(Frag& frag, NdbOut& out, TupLoc loc, PrintPar& par);
+ friend class NdbOut& operator<<(NdbOut&, const TupLoc&);
+ friend class NdbOut& operator<<(NdbOut&, const TreeEnt&);
+ friend class NdbOut& operator<<(NdbOut&, const TreeNode&);
+ friend class NdbOut& operator<<(NdbOut&, const TreeHead&);
+ friend class NdbOut& operator<<(NdbOut&, const TreePos&);
+ friend class NdbOut& operator<<(NdbOut&, const DescAttr&);
+ friend class NdbOut& operator<<(NdbOut&, const ScanOp&);
+ friend class NdbOut& operator<<(NdbOut&, const Index&);
+ friend class NdbOut& operator<<(NdbOut&, const Frag&);
+ friend class NdbOut& operator<<(NdbOut&, const FragOp&);
+ friend class NdbOut& operator<<(NdbOut&, const NodeHandle&);
+ FILE* debugFile;
+ NdbOut debugOut;
+ unsigned debugFlags;
+ enum {
+ DebugMeta = 1, // log create and drop index
+ DebugMaint = 2, // log maintenance ops
+ DebugTree = 4, // log and check tree after each op
+ DebugScan = 8 // log scans
+ };
+ STATIC_CONST( DataFillByte = 0xa2 );
+ STATIC_CONST( NodeFillByte = 0xa4 );
+#endif
+
+ // start up info
+ Uint32 c_internalStartPhase;
+ Uint32 c_typeOfStart;
+
+ /*
+ * Global data set at operation start. Unpacked from index metadata.
+ * Not passed as parameter to methods. Invalid across timeslices.
+ *
+ * TODO inline all into index metadata
+ */
+
+ // index key attr ids with sizes in AttributeHeader format
+ Data c_keyAttrs;
+
+ // pointers to index key comparison functions
+ NdbSqlUtil::Cmp** c_sqlCmp;
+
+ /*
+ * Other buffers used during the operation.
+ */
+
+ // buffer for search key data with headers
+ Data c_searchKey;
+
+ // buffer for current entry key data with headers
+ Data c_entryKey;
+
+ // buffer for scan bounds and keyinfo (primary key)
+ Data c_dataBuffer;
+
+ // inlined utils
+ DescEnt& getDescEnt(Uint32 descPage, Uint32 descOff);
+ Uint32 getTupAddr(const Frag& frag, TreeEnt ent);
+ static unsigned min(unsigned x, unsigned y);
+ static unsigned max(unsigned x, unsigned y);
+};
+
+// Dbtux::Data
+
+inline
+Dbtux::Data::Data() :
+ m_data(0)
+{
+}
+
+inline
+Dbtux::Data::Data(Uint32* data) :
+ m_data(data)
+{
+}
+
+inline Dbtux::Data&
+Dbtux::Data::operator=(Uint32* data)
+{
+ m_data = data;
+ return *this;
+}
+
+inline
+Dbtux::Data::operator Uint32*() const
+{
+ return m_data;
+}
+
+inline Dbtux::Data&
+Dbtux::Data::operator+=(size_t n)
+{
+ m_data += n;
+ return *this;
+}
+
+inline AttributeHeader&
+Dbtux::Data::ah() const
+{
+ return *reinterpret_cast<AttributeHeader*>(m_data);
+}
+
+// Dbtux::ConstData
+
+inline
+Dbtux::ConstData::ConstData() :
+ m_data(0)
+{
+}
+
+inline
+Dbtux::ConstData::ConstData(const Uint32* data) :
+ m_data(data)
+{
+}
+
+inline Dbtux::ConstData&
+Dbtux::ConstData::operator=(const Uint32* data)
+{
+ m_data = data;
+ return *this;
+}
+
+inline
+Dbtux::ConstData::operator const Uint32*() const
+{
+ return m_data;
+}
+
+inline Dbtux::ConstData&
+Dbtux::ConstData::operator+=(size_t n)
+{
+ m_data += n;
+ return *this;
+}
+
+inline const AttributeHeader&
+Dbtux::ConstData::ah() const
+{
+ return *reinterpret_cast<const AttributeHeader*>(m_data);
+}
+
+inline
+Dbtux::ConstData::ConstData(Data data) :
+ m_data(static_cast<Uint32*>(data))
+{
+}
+
+inline Dbtux::ConstData&
+Dbtux::ConstData::operator=(Data data)
+{
+ m_data = static_cast<Uint32*>(data);
+ return *this;
+}
+
+// Dbtux::TupLoc
+
+inline
+Dbtux::TupLoc::TupLoc() :
+ m_pageId1(RNIL >> 16),
+ m_pageId2(RNIL & 0xFFFF),
+ m_pageOffset(0)
+{
+}
+
+inline
+Dbtux::TupLoc::TupLoc(Uint32 pageId, Uint16 pageOffset) :
+ m_pageId1(pageId >> 16),
+ m_pageId2(pageId & 0xFFFF),
+ m_pageOffset(pageOffset)
+{
+}
+
+inline Uint32
+Dbtux::TupLoc::getPageId() const
+{
+ return (m_pageId1 << 16) | m_pageId2;
+}
+
+inline void
+Dbtux::TupLoc::setPageId(Uint32 pageId)
+{
+ m_pageId1 = (pageId >> 16);
+ m_pageId2 = (pageId & 0xFFFF);
+}
+
+inline Uint32
+Dbtux::TupLoc::getPageOffset() const
+{
+ return (Uint32)m_pageOffset;
+}
+
+inline void
+Dbtux::TupLoc::setPageOffset(Uint32 pageOffset)
+{
+ m_pageOffset = (Uint16)pageOffset;
+}
+
+inline bool
+Dbtux::TupLoc::operator==(const TupLoc& loc) const
+{
+ return
+ m_pageId1 == loc.m_pageId1 &&
+ m_pageId2 == loc.m_pageId2 &&
+ m_pageOffset == loc.m_pageOffset;
+}
+
+inline bool
+Dbtux::TupLoc::operator!=(const TupLoc& loc) const
+{
+ return ! (*this == loc);
+}
+
+// Dbtux::TreeEnt
+
+inline
+Dbtux::TreeEnt::TreeEnt() :
+ m_tupLoc(),
+ m_tupVersion(0),
+ m_fragBit(0)
+{
+}
+
+inline bool
+Dbtux::TreeEnt::eq(const TreeEnt ent) const
+{
+ return
+ m_tupLoc == ent.m_tupLoc &&
+ m_tupVersion == ent.m_tupVersion &&
+ m_fragBit == ent.m_fragBit;
+}
+
+inline int
+Dbtux::TreeEnt::cmp(const TreeEnt ent) const
+{
+ if (m_tupLoc.getPageId() < ent.m_tupLoc.getPageId())
+ return -1;
+ if (m_tupLoc.getPageId() > ent.m_tupLoc.getPageId())
+ return +1;
+ if (m_tupLoc.getPageOffset() < ent.m_tupLoc.getPageOffset())
+ return -1;
+ if (m_tupLoc.getPageOffset() > ent.m_tupLoc.getPageOffset())
+ return +1;
+ if (m_tupVersion < ent.m_tupVersion)
+ return -1;
+ if (m_tupVersion > ent.m_tupVersion)
+ return +1;
+ if (m_fragBit < ent.m_fragBit)
+ return -1;
+ if (m_fragBit > ent.m_fragBit)
+ return +1;
+ return 0;
+}
+
+// Dbtux::TreeNode
+
+inline
+Dbtux::TreeNode::TreeNode() :
+ m_side(2),
+ m_balance(0 + 1),
+ pad1(0),
+ m_occup(0),
+ m_nodeScan(RNIL)
+{
+ m_link[0] = NullTupLoc;
+ m_link[1] = NullTupLoc;
+ m_link[2] = NullTupLoc;
+}
+
+// Dbtux::TreeHead
+
+inline
+Dbtux::TreeHead::TreeHead() :
+ m_nodeSize(0),
+ m_prefSize(0),
+ m_minOccup(0),
+ m_maxOccup(0),
+ m_root()
+{
+}
+
+inline unsigned
+Dbtux::TreeHead::getSize(AccSize acc) const
+{
+ switch (acc) {
+ case AccNone:
+ return 0;
+ case AccHead:
+ return NodeHeadSize;
+ case AccPref:
+ return NodeHeadSize + m_prefSize + 2 * TreeEntSize;
+ case AccFull:
+ return m_nodeSize;
+ }
+ return 0;
+}
+
+inline Dbtux::Data
+Dbtux::TreeHead::getPref(TreeNode* node) const
+{
+ Uint32* ptr = (Uint32*)node + NodeHeadSize;
+ return ptr;
+}
+
+inline Dbtux::TreeEnt*
+Dbtux::TreeHead::getEntList(TreeNode* node) const
+{
+ Uint32* ptr = (Uint32*)node + NodeHeadSize + m_prefSize;
+ return (TreeEnt*)ptr;
+}
+
+// Dbtux::TreePos
+
+inline
+Dbtux::TreePos::TreePos() :
+ m_loc(),
+ m_pos(ZNIL),
+ m_match(false),
+ m_dir(255)
+{
+}
+
+// Dbtux::DescPage
+
+inline
+Dbtux::DescPage::DescPage() :
+ m_nextPage(RNIL),
+ m_numFree(ZNIL)
+{
+ for (unsigned i = 0; i < DescPageSize; i++) {
+#ifdef VM_TRACE
+ m_data[i] = 0x13571357;
+#else
+ m_data[i] = 0;
+#endif
+ }
+}
+
+// Dbtux::ScanOp
+
+inline
+Dbtux::ScanOp::ScanOp(ScanBoundPool& scanBoundPool) :
+ m_state(Undef),
+ m_lockwait(false),
+ m_userPtr(RNIL),
+ m_userRef(RNIL),
+ m_tableId(RNIL),
+ m_indexId(RNIL),
+ m_fragPtrI(RNIL),
+ m_transId1(0),
+ m_transId2(0),
+ m_savePointId(0),
+ m_accLockOp(RNIL),
+ m_readCommitted(0),
+ m_lockMode(0),
+ m_descending(0),
+ m_boundMin(scanBoundPool),
+ m_boundMax(scanBoundPool),
+ m_scanPos(),
+ m_scanEnt(),
+ m_nodeScan(RNIL),
+ m_maxAccLockOps(0)
+{
+ m_bound[0] = &m_boundMin;
+ m_bound[1] = &m_boundMax;
+ m_boundCnt[0] = 0;
+ m_boundCnt[1] = 0;
+#ifdef VM_TRACE
+ for (unsigned i = 0; i < MaxAccLockOps; i++) {
+ m_accLockOps[i] = 0x1f1f1f1f;
+ }
+#endif
+}
+
+// Dbtux::Index
+
+inline
+Dbtux::Index::Index() :
+ m_state(NotDefined),
+ m_tableType(DictTabInfo::UndefTableType),
+ m_tableId(RNIL),
+ m_numFrags(0),
+ m_descPage(RNIL),
+ m_descOff(0),
+ m_numAttrs(0),
+ m_storeNullKey(false)
+{
+ for (unsigned i = 0; i < MaxIndexFragments; i++) {
+ m_fragId[i] = ZNIL;
+ m_fragPtrI[i] = RNIL;
+ };
+}
+
+// Dbtux::Frag
+
+inline
+Dbtux::Frag::Frag(ArrayPool<ScanOp>& scanOpPool) :
+ m_tableId(RNIL),
+ m_indexId(RNIL),
+ m_fragId(ZNIL),
+ m_descPage(RNIL),
+ m_descOff(0),
+ m_numAttrs(ZNIL),
+ m_storeNullKey(false),
+ m_tree(),
+ m_freeLoc(),
+ m_scanList(scanOpPool),
+ m_tupIndexFragPtrI(RNIL)
+{
+ m_tupTableFragPtrI[0] = RNIL;
+ m_tupTableFragPtrI[1] = RNIL;
+ m_accTableFragPtrI[0] = RNIL;
+ m_accTableFragPtrI[1] = RNIL;
+}
+
+// Dbtux::FragOp
+
+inline
+Dbtux::FragOp::FragOp() :
+ m_userPtr(RNIL),
+ m_userRef(RNIL),
+ m_indexId(RNIL),
+ m_fragId(ZNIL),
+ m_fragPtrI(RNIL),
+ m_fragNo(ZNIL),
+ m_numAttrsRecvd(ZNIL)
+{
+}
+
+// Dbtux::NodeHandle
+
+inline
+Dbtux::NodeHandle::NodeHandle(Frag& frag) :
+ m_frag(frag),
+ m_loc(),
+ m_node(0)
+{
+}
+
+inline
+Dbtux::NodeHandle::NodeHandle(const NodeHandle& node) :
+ m_frag(node.m_frag),
+ m_loc(node.m_loc),
+ m_node(node.m_node)
+{
+}
+
+inline Dbtux::NodeHandle&
+Dbtux::NodeHandle::operator=(const NodeHandle& node)
+{
+ ndbassert(&m_frag == &node.m_frag);
+ m_loc = node.m_loc;
+ m_node = node.m_node;
+ return *this;
+}
+
+inline bool
+Dbtux::NodeHandle::isNull()
+{
+ return m_node == 0;
+}
+
+inline Dbtux::TupLoc
+Dbtux::NodeHandle::getLink(unsigned i)
+{
+ ndbrequire(i <= 2);
+ return m_node->m_link[i];
+}
+
+inline unsigned
+Dbtux::NodeHandle::getChilds()
+{
+ return (m_node->m_link[0] != NullTupLoc) + (m_node->m_link[1] != NullTupLoc);
+}
+
+inline unsigned
+Dbtux::NodeHandle::getSide()
+{
+ return m_node->m_side;
+}
+
+inline unsigned
+Dbtux::NodeHandle::getOccup()
+{
+ return m_node->m_occup;
+}
+
+inline int
+Dbtux::NodeHandle::getBalance()
+{
+ return (int)m_node->m_balance - 1;
+}
+
+inline Uint32
+Dbtux::NodeHandle::getNodeScan()
+{
+ return m_node->m_nodeScan;
+}
+
+inline void
+Dbtux::NodeHandle::setLink(unsigned i, TupLoc loc)
+{
+ ndbrequire(i <= 2);
+ m_node->m_link[i] = loc;
+}
+
+inline void
+Dbtux::NodeHandle::setSide(unsigned i)
+{
+ ndbrequire(i <= 2);
+ m_node->m_side = i;
+}
+
+inline void
+Dbtux::NodeHandle::setOccup(unsigned n)
+{
+ TreeHead& tree = m_frag.m_tree;
+ ndbrequire(n <= tree.m_maxOccup);
+ m_node->m_occup = n;
+}
+
+inline void
+Dbtux::NodeHandle::setBalance(int b)
+{
+ ndbrequire(abs(b) <= 1);
+ m_node->m_balance = (unsigned)(b + 1);
+}
+
+inline void
+Dbtux::NodeHandle::setNodeScan(Uint32 scanPtrI)
+{
+ m_node->m_nodeScan = scanPtrI;
+}
+
+inline Dbtux::Data
+Dbtux::NodeHandle::getPref()
+{
+ TreeHead& tree = m_frag.m_tree;
+ return tree.getPref(m_node);
+}
+
+inline Dbtux::TreeEnt
+Dbtux::NodeHandle::getEnt(unsigned pos)
+{
+ TreeHead& tree = m_frag.m_tree;
+ TreeEnt* entList = tree.getEntList(m_node);
+ const unsigned occup = m_node->m_occup;
+ ndbrequire(pos < occup);
+ return entList[(1 + pos) % occup];
+}
+
+inline Dbtux::TreeEnt
+Dbtux::NodeHandle::getMinMax(unsigned i)
+{
+ const unsigned occup = m_node->m_occup;
+ ndbrequire(i <= 1 && occup != 0);
+ return getEnt(i == 0 ? 0 : occup - 1);
+}
+
+// parameters for methods
+
+#ifdef VM_TRACE
+inline
+Dbtux::PrintPar::PrintPar() :
+ // caller fills in
+ m_path(),
+ m_side(255),
+ m_parent(),
+ // default return values
+ m_depth(0),
+ m_occup(0),
+ m_ok(true)
+{
+}
+#endif
+
+// utils
+
+inline Dbtux::DescEnt&
+Dbtux::getDescEnt(Uint32 descPage, Uint32 descOff)
+{
+ DescPagePtr pagePtr;
+ pagePtr.i = descPage;
+ c_descPagePool.getPtr(pagePtr);
+ ndbrequire(descOff < DescPageSize);
+ DescEnt* descEnt = (DescEnt*)&pagePtr.p->m_data[descOff];
+ return *descEnt;
+}
+
+inline Uint32
+Dbtux::getTupAddr(const Frag& frag, TreeEnt ent)
+{
+ const Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit];
+ const TupLoc tupLoc = ent.m_tupLoc;
+ Uint32 tupAddr = NullTupAddr;
+ c_tup->tuxGetTupAddr(tableFragPtrI, tupLoc.getPageId(), tupLoc.getPageOffset(), tupAddr);
+ jamEntry();
+ return tupAddr;
+}
+
+inline unsigned
+Dbtux::min(unsigned x, unsigned y)
+{
+ return x < y ? x : y;
+}
+
+inline unsigned
+Dbtux::max(unsigned x, unsigned y)
+{
+ return x > y ? x : y;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp
new file mode 100644
index 00000000000..cf815b14c1a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp
@@ -0,0 +1,175 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_CMP_CPP
+#include "Dbtux.hpp"
+
+/*
+ * Search key vs node prefix or entry.
+ *
+ * The comparison starts at given attribute position. The position is
+ * updated by number of equal initial attributes found. The entry data
+ * may be partial in which case CmpUnknown may be returned.
+ *
+ * The attributes are normalized and have variable size given in words.
+ */
+int
+Dbtux::cmpSearchKey(const Frag& frag, unsigned& start, ConstData searchKey, ConstData entryData, unsigned maxlen)
+{
+ const unsigned numAttrs = frag.m_numAttrs;
+ const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff);
+ // skip to right position in search key only
+ for (unsigned i = 0; i < start; i++) {
+ jam();
+ searchKey += AttributeHeaderSize + searchKey.ah().getDataSize();
+ }
+ // number of words of entry data left
+ unsigned len2 = maxlen;
+ int ret = 0;
+ while (start < numAttrs) {
+ if (len2 <= AttributeHeaderSize) {
+ jam();
+ ret = NdbSqlUtil::CmpUnknown;
+ break;
+ }
+ len2 -= AttributeHeaderSize;
+ if (! searchKey.ah().isNULL()) {
+ if (! entryData.ah().isNULL()) {
+ jam();
+ // verify attribute id
+ const DescAttr& descAttr = descEnt.m_descAttr[start];
+ ndbrequire(searchKey.ah().getAttributeId() == descAttr.m_primaryAttrId);
+ ndbrequire(entryData.ah().getAttributeId() == descAttr.m_primaryAttrId);
+ // sizes
+ const unsigned size1 = searchKey.ah().getDataSize();
+ const unsigned size2 = min(entryData.ah().getDataSize(), len2);
+ len2 -= size2;
+ // compare
+ NdbSqlUtil::Cmp* const cmp = c_sqlCmp[start];
+ const Uint32* const p1 = &searchKey[AttributeHeaderSize];
+ const Uint32* const p2 = &entryData[AttributeHeaderSize];
+ const bool full = (maxlen == MaxAttrDataSize);
+ ret = (*cmp)(0, p1, size1 << 2, p2, size2 << 2, full);
+ if (ret != 0) {
+ jam();
+ break;
+ }
+ } else {
+ jam();
+ // not NULL > NULL
+ ret = +1;
+ break;
+ }
+ } else {
+ if (! entryData.ah().isNULL()) {
+ jam();
+ // NULL < not NULL
+ ret = -1;
+ break;
+ }
+ }
+ searchKey += AttributeHeaderSize + searchKey.ah().getDataSize();
+ entryData += AttributeHeaderSize + entryData.ah().getDataSize();
+ start++;
+ }
+ return ret;
+}
+
+/*
+ * Scan bound vs node prefix or entry.
+ *
+ * Compare lower or upper bound and index entry data. The entry data
+ * may be partial in which case CmpUnknown may be returned. Otherwise
+ * returns -1 if the bound is to the left of the entry and +1 if the
+ * bound is to the right of the entry.
+ *
+ * The routine is similar to cmpSearchKey, but 0 is never returned.
+ * Suppose all attributes compare equal. Recall that all bounds except
+ * possibly the last one are non-strict. Use the given bound direction
+ * (0-lower 1-upper) and strictness of last bound to return -1 or +1.
+ *
+ * Following example illustrates this. We are at (a=2, b=3).
+ *
+ * idir bounds strict return
+ * 0 a >= 2 and b >= 3 no -1
+ * 0 a >= 2 and b > 3 yes +1
+ * 1 a <= 2 and b <= 3 no +1
+ * 1 a <= 2 and b < 3 yes -1
+ *
+ * The attributes are normalized and have variable size given in words.
+ */
+int
+Dbtux::cmpScanBound(const Frag& frag, unsigned idir, ConstData boundInfo, unsigned boundCount, ConstData entryData, unsigned maxlen)
+{
+ const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff);
+ // direction 0-lower 1-upper
+ ndbrequire(idir <= 1);
+ // number of words of data left
+ unsigned len2 = maxlen;
+ // in case of no bounds, init last type to something non-strict
+ unsigned type = 4;
+ while (boundCount != 0) {
+ if (len2 <= AttributeHeaderSize) {
+ jam();
+ return NdbSqlUtil::CmpUnknown;
+ }
+ len2 -= AttributeHeaderSize;
+ // get and skip bound type (it is used after the loop)
+ type = boundInfo[0];
+ boundInfo += 1;
+ if (! boundInfo.ah().isNULL()) {
+ if (! entryData.ah().isNULL()) {
+ jam();
+ // verify attribute id
+ const Uint32 index = boundInfo.ah().getAttributeId();
+ ndbrequire(index < frag.m_numAttrs);
+ const DescAttr& descAttr = descEnt.m_descAttr[index];
+ ndbrequire(entryData.ah().getAttributeId() == descAttr.m_primaryAttrId);
+ // sizes
+ const unsigned size1 = boundInfo.ah().getDataSize();
+ const unsigned size2 = min(entryData.ah().getDataSize(), len2);
+ len2 -= size2;
+ // compare
+ NdbSqlUtil::Cmp* const cmp = c_sqlCmp[index];
+ const Uint32* const p1 = &boundInfo[AttributeHeaderSize];
+ const Uint32* const p2 = &entryData[AttributeHeaderSize];
+ const bool full = (maxlen == MaxAttrDataSize);
+ int ret = (*cmp)(0, p1, size1 << 2, p2, size2 << 2, full);
+ if (ret != 0) {
+ jam();
+ return ret;
+ }
+ } else {
+ jam();
+ // not NULL > NULL
+ return +1;
+ }
+ } else {
+ jam();
+ if (! entryData.ah().isNULL()) {
+ jam();
+ // NULL < not NULL
+ return -1;
+ }
+ }
+ boundInfo += AttributeHeaderSize + boundInfo.ah().getDataSize();
+ entryData += AttributeHeaderSize + entryData.ah().getDataSize();
+ boundCount -= 1;
+ }
+ // all attributes were equal
+ const int strict = (type & 0x1);
+ return (idir == 0 ? (strict == 0 ? -1 : +1) : (strict == 0 ? +1 : -1));
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp
new file mode 100644
index 00000000000..ed29dc57915
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp
@@ -0,0 +1,443 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_DEBUG_CPP
+#include "Dbtux.hpp"
+
+/*
+ * 12001 log file 0-close 1-open 2-append 3-append to signal log
+ * 12002 log flags 1-meta 2-maint 4-tree 8-scan
+ */
+void
+Dbtux::execDUMP_STATE_ORD(Signal* signal)
+{
+ jamEntry();
+#ifdef VM_TRACE
+ if (signal->theData[0] == DumpStateOrd::TuxLogToFile) {
+ unsigned flag = signal->theData[1];
+ const char* const tuxlog = "tux.log";
+ FILE* slFile = globalSignalLoggers.getOutputStream();
+ if (flag <= 3) {
+ if (debugFile != 0) {
+ if (debugFile != slFile)
+ fclose(debugFile);
+ debugFile = 0;
+ debugOut = *new NdbOut(*new NullOutputStream());
+ }
+ if (flag == 1)
+ debugFile = fopen(tuxlog, "w");
+ if (flag == 2)
+ debugFile = fopen(tuxlog, "a");
+ if (flag == 3)
+ debugFile = slFile;
+ if (debugFile != 0)
+ debugOut = *new NdbOut(*new FileOutputStream(debugFile));
+ }
+ return;
+ }
+ if (signal->theData[0] == DumpStateOrd::TuxSetLogFlags) {
+ debugFlags = signal->theData[1];
+ return;
+ }
+ if (signal->theData[0] == DumpStateOrd::TuxMetaDataJunk) {
+ // read table definition
+ Uint32 tableId = signal->theData[1];
+ Uint32 tableVersion = signal->theData[2];
+ int ret;
+ MetaData md(this);
+ MetaData::Table table;
+ MetaData::Attribute attribute;
+ infoEvent("md: read table %u %u", tableId, tableVersion);
+ if ((ret = md.lock(false)) < 0) {
+ infoEvent("md.lock error %d", ret);
+ return;
+ }
+ if ((ret = md.getTable(table, tableId, tableVersion)) < 0) {
+ infoEvent("md.getTable error %d", ret);
+ // lock is released by destructor
+ return;
+ }
+ infoEvent("md: %s type=%d attrs=%u", table.tableName, table.tableType, table.noOfAttributes);
+ for (Uint32 i = 0; i < table.noOfAttributes; i++) {
+ if ((ret = md.getAttribute(attribute, table, i)) < 0) {
+ infoEvent("mg.getAttribute %u error %d", i, ret);
+ // lock is released by destructor
+ return;
+ }
+ infoEvent("md: %d %s", attribute.attributeId, attribute.attributeName);
+ }
+ if ((ret = md.unlock(false)) < 0) {
+ infoEvent("md.unlock error %d", ret);
+ return;
+ }
+ return;
+ }
+#endif
+}
+
+#ifdef VM_TRACE
+
+void
+Dbtux::printTree(Signal* signal, Frag& frag, NdbOut& out)
+{
+ TreeHead& tree = frag.m_tree;
+ PrintPar par;
+ strcpy(par.m_path, ".");
+ par.m_side = 2;
+ par.m_parent = NullTupLoc;
+ printNode(frag, out, tree.m_root, par);
+ out.m_out->flush();
+ if (! par.m_ok) {
+ if (debugFile == 0) {
+ signal->theData[0] = 12001;
+ signal->theData[1] = 1;
+ execDUMP_STATE_ORD(signal);
+ if (debugFile != 0) {
+ printTree(signal, frag, debugOut);
+ }
+ }
+ ndbrequire(false);
+ }
+}
+
+void
+Dbtux::printNode(Frag& frag, NdbOut& out, TupLoc loc, PrintPar& par)
+{
+ if (loc == NullTupLoc) {
+ par.m_depth = 0;
+ return;
+ }
+ TreeHead& tree = frag.m_tree;
+ NodeHandle node(frag);
+ selectNode(node, loc);
+ out << par.m_path << " " << node << endl;
+ // check children
+ PrintPar cpar[2];
+ ndbrequire(strlen(par.m_path) + 1 < sizeof(par.m_path));
+ for (unsigned i = 0; i <= 1; i++) {
+ sprintf(cpar[i].m_path, "%s%c", par.m_path, "LR"[i]);
+ cpar[i].m_side = i;
+ cpar[i].m_depth = 0;
+ cpar[i].m_parent = loc;
+ printNode(frag, out, node.getLink(i), cpar[i]);
+ if (! cpar[i].m_ok) {
+ par.m_ok = false;
+ }
+ }
+ static const char* const sep = " *** ";
+ // check child-parent links
+ if (node.getLink(2) != par.m_parent) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "parent loc " << hex << node.getLink(2);
+ out << " should be " << hex << par.m_parent << endl;
+ }
+ if (node.getSide() != par.m_side) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "side " << dec << node.getSide();
+ out << " should be " << dec << par.m_side << endl;
+ }
+ // check balance
+ const int balance = -cpar[0].m_depth + cpar[1].m_depth;
+ if (node.getBalance() != balance) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "balance " << node.getBalance();
+ out << " should be " << balance << endl;
+ }
+ if (abs(node.getBalance()) > 1) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "balance " << node.getBalance() << " is invalid" << endl;
+ }
+ // check occupancy
+ if (node.getOccup() == 0 || node.getOccup() > tree.m_maxOccup) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "occupancy " << node.getOccup();
+ out << " zero or greater than max " << tree.m_maxOccup << endl;
+ }
+ // check for occupancy of interior node
+ if (node.getChilds() == 2 && node.getOccup() < tree.m_minOccup) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "occupancy " << node.getOccup() << " of interior node";
+ out << " less than min " << tree.m_minOccup << endl;
+ }
+#ifdef dbtux_totally_groks_t_trees
+ // check missed semi-leaf/leaf merge
+ for (unsigned i = 0; i <= 1; i++) {
+ if (node.getLink(i) != NullTupLoc &&
+ node.getLink(1 - i) == NullTupLoc &&
+ // our semi-leaf seems to satify interior minOccup condition
+ node.getOccup() < tree.m_minOccup) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "missed merge with child " << i << endl;
+ }
+ }
+#endif
+ // check inline prefix
+ { ConstData data1 = node.getPref();
+ Uint32 data2[MaxPrefSize];
+ memset(data2, DataFillByte, MaxPrefSize << 2);
+ readKeyAttrs(frag, node.getMinMax(0), 0, c_searchKey);
+ copyAttrs(frag, c_searchKey, data2, tree.m_prefSize);
+ for (unsigned n = 0; n < tree.m_prefSize; n++) {
+ if (data1[n] != data2[n]) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << "inline prefix mismatch word " << n;
+ out << " value " << hex << data1[n];
+ out << " should be " << hex << data2[n] << endl;
+ break;
+ }
+ }
+ }
+ // check ordering within node
+ for (unsigned j = 1; j < node.getOccup(); j++) {
+ const TreeEnt ent1 = node.getEnt(j - 1);
+ const TreeEnt ent2 = node.getEnt(j);
+ unsigned start = 0;
+ readKeyAttrs(frag, ent1, start, c_searchKey);
+ readKeyAttrs(frag, ent2, start, c_entryKey);
+ int ret = cmpSearchKey(frag, start, c_searchKey, c_entryKey);
+ if (ret == 0)
+ ret = ent1.cmp(ent2);
+ if (ret != -1) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << " disorder within node at pos " << j << endl;
+ }
+ }
+ // check ordering wrt subtrees
+ for (unsigned i = 0; i <= 1; i++) {
+ if (node.getLink(i) == NullTupLoc)
+ continue;
+ const TreeEnt ent1 = cpar[i].m_minmax[1 - i];
+ const TreeEnt ent2 = node.getMinMax(i);
+ unsigned start = 0;
+ readKeyAttrs(frag, ent1, start, c_searchKey);
+ readKeyAttrs(frag, ent2, start, c_entryKey);
+ int ret = cmpSearchKey(frag, start, c_searchKey, c_entryKey);
+ if (ret == 0)
+ ret = ent1.cmp(ent2);
+ if (ret != (i == 0 ? -1 : +1)) {
+ par.m_ok = false;
+ out << par.m_path << sep;
+ out << " disorder wrt subtree " << i << endl;
+ }
+ }
+ // return values
+ par.m_depth = 1 + max(cpar[0].m_depth, cpar[1].m_depth);
+ par.m_occup = node.getOccup();
+ for (unsigned i = 0; i <= 1; i++) {
+ if (node.getLink(i) == NullTupLoc)
+ par.m_minmax[i] = node.getMinMax(i);
+ else
+ par.m_minmax[i] = cpar[i].m_minmax[i];
+ }
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::TupLoc& loc)
+{
+ if (loc == Dbtux::NullTupLoc) {
+ out << "null";
+ } else {
+ out << dec << loc.getPageId();
+ out << "." << dec << loc.getPageOffset();
+ }
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::TreeEnt& ent)
+{
+ out << dec << ent.m_fragBit;
+ out << "-" << ent.m_tupLoc;
+ out << "-" << dec << ent.m_tupVersion;
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::TreeNode& node)
+{
+ out << "[TreeNode " << hex << &node;
+ out << " [left " << node.m_link[0] << "]";
+ out << " [right " << node.m_link[1] << "]";
+ out << " [up " << node.m_link[2] << "]";
+ out << " [side " << dec << node.m_side << "]";
+ out << " [occup " << dec << node.m_occup << "]";
+ out << " [balance " << dec << (int)node.m_balance - 1 << "]";
+ out << " [nodeScan " << hex << node.m_nodeScan << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::TreeHead& tree)
+{
+ out << "[TreeHead " << hex << &tree;
+ out << " [nodeSize " << dec << tree.m_nodeSize << "]";
+ out << " [prefSize " << dec << tree.m_prefSize << "]";
+ out << " [minOccup " << dec << tree.m_minOccup << "]";
+ out << " [maxOccup " << dec << tree.m_maxOccup << "]";
+ out << " [AccHead " << dec << tree.getSize(Dbtux::AccHead) << "]";
+ out << " [AccPref " << dec << tree.getSize(Dbtux::AccPref) << "]";
+ out << " [AccFull " << dec << tree.getSize(Dbtux::AccFull) << "]";
+ out << " [root " << hex << tree.m_root << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::TreePos& pos)
+{
+ out << "[TreePos " << hex << &pos;
+ out << " [loc " << pos.m_loc << "]";
+ out << " [pos " << dec << pos.m_pos << "]";
+ out << " [match " << dec << pos.m_match << "]";
+ out << " [dir " << dec << pos.m_dir << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::DescAttr& descAttr)
+{
+ out << "[DescAttr " << hex << &descAttr;
+ out << " [attrDesc " << hex << descAttr.m_attrDesc;
+ out << " [primaryAttrId " << dec << descAttr.m_primaryAttrId << "]";
+ out << " [typeId " << dec << descAttr.m_typeId << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::ScanOp& scan)
+{
+ out << "[ScanOp " << hex << &scan;
+ out << " [state " << dec << scan.m_state << "]";
+ out << " [lockwait " << dec << scan.m_lockwait << "]";
+ out << " [indexId " << dec << scan.m_indexId << "]";
+ out << " [fragId " << dec << scan.m_fragId << "]";
+ out << " [transId " << hex << scan.m_transId1 << " " << scan.m_transId2 << "]";
+ out << " [savePointId " << dec << scan.m_savePointId << "]";
+ out << " [accLockOp " << hex << scan.m_accLockOp << "]";
+ out << " [accLockOps";
+ for (unsigned i = 0; i < scan.m_maxAccLockOps; i++) {
+ if (scan.m_accLockOps[i] != RNIL)
+ out << " " << hex << scan.m_accLockOps[i];
+ }
+ out << "]";
+ out << " [readCommitted " << dec << scan.m_readCommitted << "]";
+ out << " [lockMode " << dec << scan.m_lockMode << "]";
+ out << " [descending " << dec << scan.m_descending << "]";
+ out << " [pos " << scan.m_scanPos << "]";
+ out << " [ent " << scan.m_scanEnt << "]";
+ for (unsigned i = 0; i <= 1; i++) {
+ out << " [bound " << dec << i;
+ Dbtux::ScanBound& bound = *scan.m_bound[i];
+ Dbtux::ScanBoundIterator iter;
+ bound.first(iter);
+ for (unsigned j = 0; j < bound.getSize(); j++) {
+ out << " " << hex << *iter.data;
+ bound.next(iter);
+ }
+ out << "]";
+ }
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::Index& index)
+{
+ out << "[Index " << hex << &index;
+ out << " [tableId " << dec << index.m_tableId << "]";
+ out << " [numFrags " << dec << index.m_numFrags << "]";
+ for (unsigned i = 0; i < index.m_numFrags; i++) {
+ out << " [frag " << dec << i << " ";
+ // dangerous and wrong
+ Dbtux* tux = (Dbtux*)globalData.getBlock(DBTUX);
+ const Dbtux::Frag& frag = *tux->c_fragPool.getPtr(index.m_fragPtrI[i]);
+ out << frag;
+ out << "]";
+ }
+ out << " [descPage " << hex << index.m_descPage << "]";
+ out << " [descOff " << dec << index.m_descOff << "]";
+ out << " [numAttrs " << dec << index.m_numAttrs << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::Frag& frag)
+{
+ out << "[Frag " << hex << &frag;
+ out << " [tableId " << dec << frag.m_tableId << "]";
+ out << " [indexId " << dec << frag.m_indexId << "]";
+ out << " [fragId " << dec << frag.m_fragId << "]";
+ out << " [descPage " << hex << frag.m_descPage << "]";
+ out << " [descOff " << dec << frag.m_descOff << "]";
+ out << " [numAttrs " << dec << frag.m_numAttrs << "]";
+ out << " [tree " << frag.m_tree << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::FragOp& fragOp)
+{
+ out << "[FragOp " << hex << &fragOp;
+ out << " [userPtr " << dec << fragOp.m_userPtr << "]";
+ out << " [indexId " << dec << fragOp.m_indexId << "]";
+ out << " [fragId " << dec << fragOp.m_fragId << "]";
+ out << " [fragNo " << dec << fragOp.m_fragNo << "]";
+ out << " numAttrsRecvd " << dec << fragOp.m_numAttrsRecvd << "]";
+ out << "]";
+ return out;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const Dbtux::NodeHandle& node)
+{
+ const Dbtux::Frag& frag = node.m_frag;
+ const Dbtux::TreeHead& tree = frag.m_tree;
+ out << "[NodeHandle " << hex << &node;
+ out << " [loc " << node.m_loc << "]";
+ out << " [node " << *node.m_node << "]";
+ const Uint32* data;
+ out << " [pref";
+ data = (const Uint32*)node.m_node + Dbtux::NodeHeadSize;
+ for (unsigned j = 0; j < tree.m_prefSize; j++)
+ out << " " << hex << data[j];
+ out << "]";
+ out << " [entList";
+ unsigned numpos = node.m_node->m_occup;
+ data = (const Uint32*)node.m_node + Dbtux::NodeHeadSize + tree.m_prefSize;
+ const Dbtux::TreeEnt* entList = (const Dbtux::TreeEnt*)data;
+ // print entries in logical order
+ for (unsigned pos = 1; pos <= numpos; pos++)
+ out << " " << entList[pos % numpos];
+ out << "]";
+ out << "]";
+ return out;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp
new file mode 100644
index 00000000000..5640fdf2899
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp
@@ -0,0 +1,317 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_GEN_CPP
+#include "Dbtux.hpp"
+
+Dbtux::Dbtux(const Configuration& conf) :
+ SimulatedBlock(DBTUX, conf),
+ c_tup(0),
+ c_descPageList(RNIL),
+#ifdef VM_TRACE
+ debugFile(0),
+ debugOut(*new NullOutputStream()),
+ debugFlags(0),
+#endif
+ c_internalStartPhase(0),
+ c_typeOfStart(NodeState::ST_ILLEGAL_TYPE),
+ c_dataBuffer(0)
+{
+ BLOCK_CONSTRUCTOR(Dbtux);
+ // verify size assumptions (also when release-compiled)
+ ndbrequire(
+ (sizeof(TreeEnt) & 0x3) == 0 &&
+ (sizeof(TreeNode) & 0x3) == 0 &&
+ (sizeof(DescHead) & 0x3) == 0 &&
+ (sizeof(DescAttr) & 0x3) == 0
+ );
+ /*
+ * DbtuxGen.cpp
+ */
+ addRecSignal(GSN_CONTINUEB, &Dbtux::execCONTINUEB);
+ addRecSignal(GSN_STTOR, &Dbtux::execSTTOR);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbtux::execREAD_CONFIG_REQ, true);
+ /*
+ * DbtuxMeta.cpp
+ */
+ addRecSignal(GSN_TUXFRAGREQ, &Dbtux::execTUXFRAGREQ);
+ addRecSignal(GSN_TUX_ADD_ATTRREQ, &Dbtux::execTUX_ADD_ATTRREQ);
+ addRecSignal(GSN_ALTER_INDX_REQ, &Dbtux::execALTER_INDX_REQ);
+ addRecSignal(GSN_DROP_TAB_REQ, &Dbtux::execDROP_TAB_REQ);
+ /*
+ * DbtuxMaint.cpp
+ */
+ addRecSignal(GSN_TUX_MAINT_REQ, &Dbtux::execTUX_MAINT_REQ);
+ /*
+ * DbtuxScan.cpp
+ */
+ addRecSignal(GSN_ACC_SCANREQ, &Dbtux::execACC_SCANREQ);
+ addRecSignal(GSN_TUX_BOUND_INFO, &Dbtux::execTUX_BOUND_INFO);
+ addRecSignal(GSN_NEXT_SCANREQ, &Dbtux::execNEXT_SCANREQ);
+ addRecSignal(GSN_ACC_CHECK_SCAN, &Dbtux::execACC_CHECK_SCAN);
+ addRecSignal(GSN_ACCKEYCONF, &Dbtux::execACCKEYCONF);
+ addRecSignal(GSN_ACCKEYREF, &Dbtux::execACCKEYREF);
+ addRecSignal(GSN_ACC_ABORTCONF, &Dbtux::execACC_ABORTCONF);
+ /*
+ * DbtuxDebug.cpp
+ */
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbtux::execDUMP_STATE_ORD);
+}
+
+Dbtux::~Dbtux()
+{
+}
+
+void
+Dbtux::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ const Uint32* data = signal->getDataPtr();
+ switch (data[0]) {
+ case TuxContinueB::DropIndex: // currently unused
+ {
+ IndexPtr indexPtr;
+ c_indexPool.getPtr(indexPtr, data[1]);
+ dropIndex(signal, indexPtr, data[2], data[3]);
+ }
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+}
+
+/*
+ * STTOR is sent to one block at a time. In NDBCNTR it triggers
+ * NDB_STTOR to the "old" blocks. STTOR carries start phase (SP) and
+ * NDB_STTOR carries internal start phase (ISP).
+ *
+ * SP ISP activities
+ * 1 none
+ * 2 1
+ * 3 2 recover metadata, activate indexes
+ * 4 3 recover data
+ * 5 4-6
+ * 6 skip
+ * 7 skip
+ * 8 7 build non-logged indexes on SR
+ *
+ * DBTUX catches type of start (IS, SR, NR, INR) at SP 3 and updates
+ * internal start phase at SP 7. These are used to prevent index
+ * maintenance operations caused by redo log at SR.
+ */
+void
+Dbtux::execSTTOR(Signal* signal)
+{
+ jamEntry();
+ Uint32 startPhase = signal->theData[1];
+ switch (startPhase) {
+ case 1:
+ jam();
+ CLEAR_ERROR_INSERT_VALUE;
+ c_tup = (Dbtup*)globalData.getBlock(DBTUP);
+ ndbrequire(c_tup != 0);
+ break;
+ case 3:
+ jam();
+ c_typeOfStart = signal->theData[7];
+ break;
+ case 7:
+ c_internalStartPhase = 6;
+ default:
+ jam();
+ break;
+ }
+ signal->theData[0] = 0; // garbage
+ signal->theData[1] = 0; // garbage
+ signal->theData[2] = 0; // garbage
+ signal->theData[3] = 1;
+ signal->theData[4] = 3; // for c_typeOfStart
+ signal->theData[5] = 7; // for c_internalStartPhase
+ signal->theData[6] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 7, JBB);
+}
+
+void
+Dbtux::execREAD_CONFIG_REQ(Signal* signal)
+{
+ jamEntry();
+
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ Uint32 nIndex;
+ Uint32 nFragment;
+ Uint32 nAttribute;
+ Uint32 nScanOp;
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUX_INDEX, &nIndex));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUX_FRAGMENT, &nFragment));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUX_ATTRIBUTE, &nAttribute));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_TUX_SCAN_OP, &nScanOp));
+
+ const Uint32 nDescPage = (nIndex * DescHeadSize + nAttribute * DescAttrSize + DescPageSize - 1) / DescPageSize;
+ const Uint32 nScanBoundWords = nScanOp * ScanBoundSegmentSize * 4;
+
+ c_indexPool.setSize(nIndex);
+ c_fragPool.setSize(nFragment);
+ c_descPagePool.setSize(nDescPage);
+ c_fragOpPool.setSize(MaxIndexFragments);
+ c_scanOpPool.setSize(nScanOp);
+ c_scanBoundPool.setSize(nScanBoundWords);
+ /*
+ * Index id is physical array index. We seize and initialize all
+ * index records now.
+ */
+ IndexPtr indexPtr;
+ while (1) {
+ jam();
+ refresh_watch_dog();
+ c_indexPool.seize(indexPtr);
+ if (indexPtr.i == RNIL) {
+ jam();
+ break;
+ }
+ new (indexPtr.p) Index();
+ }
+ // allocate buffers
+ c_keyAttrs = (Uint32*)allocRecord("c_keyAttrs", sizeof(Uint32), MaxIndexAttributes);
+ c_sqlCmp = (NdbSqlUtil::Cmp**)allocRecord("c_sqlCmp", sizeof(NdbSqlUtil::Cmp*), MaxIndexAttributes);
+ c_searchKey = (Uint32*)allocRecord("c_searchKey", sizeof(Uint32), MaxAttrDataSize);
+ c_entryKey = (Uint32*)allocRecord("c_entryKey", sizeof(Uint32), MaxAttrDataSize);
+ c_dataBuffer = (Uint32*)allocRecord("c_dataBuffer", sizeof(Uint64), (MaxAttrDataSize + 1) >> 1);
+ // ack
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ sendSignal(ref, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+}
+
+// utils
+
+void
+Dbtux::setKeyAttrs(const Frag& frag)
+{
+ Data keyAttrs = c_keyAttrs; // global
+ NdbSqlUtil::Cmp** sqlCmp = c_sqlCmp; // global
+ const unsigned numAttrs = frag.m_numAttrs;
+ const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff);
+ for (unsigned i = 0; i < numAttrs; i++) {
+ jam();
+ const DescAttr& descAttr = descEnt.m_descAttr[i];
+ Uint32 size = AttributeDescriptor::getSizeInWords(descAttr.m_attrDesc);
+ // set attr id and fixed size
+ keyAttrs.ah() = AttributeHeader(descAttr.m_primaryAttrId, size);
+ keyAttrs += 1;
+ // set comparison method pointer
+ const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getTypeBinary(descAttr.m_typeId);
+ ndbrequire(sqlType.m_cmp != 0);
+ *(sqlCmp++) = sqlType.m_cmp;
+ }
+}
+
+void
+Dbtux::readKeyAttrs(const Frag& frag, TreeEnt ent, unsigned start, Data keyData)
+{
+ ConstData keyAttrs = c_keyAttrs; // global
+ const Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit];
+ const TupLoc tupLoc = ent.m_tupLoc;
+ const Uint32 tupVersion = ent.m_tupVersion;
+ ndbrequire(start < frag.m_numAttrs);
+ const Uint32 numAttrs = frag.m_numAttrs - start;
+ // skip to start position in keyAttrs only
+ keyAttrs += start;
+ int ret = c_tup->tuxReadAttrs(tableFragPtrI, tupLoc.getPageId(), tupLoc.getPageOffset(), tupVersion, keyAttrs, numAttrs, keyData);
+ jamEntry();
+ // TODO handle error
+ ndbrequire(ret > 0);
+#ifdef VM_TRACE
+ if (debugFlags & (DebugMaint | DebugScan)) {
+ debugOut << "readKeyAttrs:" << endl;
+ ConstData data = keyData;
+ Uint32 totalSize = 0;
+ for (Uint32 i = start; i < frag.m_numAttrs; i++) {
+ Uint32 attrId = data.ah().getAttributeId();
+ Uint32 dataSize = data.ah().getDataSize();
+ debugOut << i << " attrId=" << attrId << " size=" << dataSize;
+ data += 1;
+ for (Uint32 j = 0; j < dataSize; j++) {
+ debugOut << " " << hex << data[0];
+ data += 1;
+ }
+ debugOut << endl;
+ totalSize += 1 + dataSize;
+ }
+ ndbassert((int)totalSize == ret);
+ }
+#endif
+}
+
+void
+Dbtux::readTablePk(const Frag& frag, TreeEnt ent, Data pkData, unsigned& pkSize)
+{
+ const Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit];
+ const TupLoc tupLoc = ent.m_tupLoc;
+ int ret = c_tup->tuxReadPk(tableFragPtrI, tupLoc.getPageId(), tupLoc.getPageOffset(), pkData, true);
+ jamEntry();
+ // TODO handle error
+ ndbrequire(ret > 0);
+ pkSize = ret;
+}
+
+/*
+ * Copy attribute data with headers. Input is all index key data.
+ * Copies whatever fits.
+ */
+void
+Dbtux::copyAttrs(const Frag& frag, ConstData data1, Data data2, unsigned maxlen2)
+{
+ unsigned n = frag.m_numAttrs;
+ unsigned len2 = maxlen2;
+ while (n != 0) {
+ jam();
+ const unsigned dataSize = data1.ah().getDataSize();
+ // copy header
+ if (len2 == 0)
+ return;
+ data2[0] = data1[0];
+ data1 += 1;
+ data2 += 1;
+ len2 -= 1;
+ // copy data
+ for (unsigned i = 0; i < dataSize; i++) {
+ if (len2 == 0)
+ return;
+ data2[i] = data1[i];
+ len2 -= 1;
+ }
+ data1 += dataSize;
+ data2 += dataSize;
+ n -= 1;
+ }
+#ifdef VM_TRACE
+ memset(data2, DataFillByte, len2 << 2);
+#endif
+}
+
+BLOCK_FUNCTIONS(Dbtux)
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp
new file mode 100644
index 00000000000..4b568badc67
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp
@@ -0,0 +1,183 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_MAINT_CPP
+#include "Dbtux.hpp"
+
+/*
+ * Maintain index.
+ */
+
+void
+Dbtux::execTUX_MAINT_REQ(Signal* signal)
+{
+ jamEntry();
+ TuxMaintReq* const sig = (TuxMaintReq*)signal->getDataPtrSend();
+ // ignore requests from redo log
+ if (c_internalStartPhase < 6 &&
+ c_typeOfStart != NodeState::ST_NODE_RESTART &&
+ c_typeOfStart != NodeState::ST_INITIAL_NODE_RESTART) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugMaint) {
+ TupLoc tupLoc(sig->pageId, sig->pageOffset);
+ debugOut << "opInfo=" << hex << sig->opInfo;
+ debugOut << " tableId=" << dec << sig->tableId;
+ debugOut << " indexId=" << dec << sig->indexId;
+ debugOut << " fragId=" << dec << sig->fragId;
+ debugOut << " tupLoc=" << tupLoc;
+ debugOut << " tupVersion=" << dec << sig->tupVersion;
+ debugOut << " -- ignored at ISP=" << dec << c_internalStartPhase;
+ debugOut << " TOS=" << dec << c_typeOfStart;
+ debugOut << endl;
+ }
+#endif
+ sig->errorCode = 0;
+ return;
+ }
+ TuxMaintReq reqCopy = *sig;
+ TuxMaintReq* const req = &reqCopy;
+ const Uint32 opCode = req->opInfo & 0xFF;
+ const Uint32 opFlag = req->opInfo >> 8;
+ // get the index
+ IndexPtr indexPtr;
+ c_indexPool.getPtr(indexPtr, req->indexId);
+ ndbrequire(indexPtr.p->m_tableId == req->tableId);
+ // get base fragment id and extra bits
+ const Uint32 fragId = req->fragId & ~1;
+ const Uint32 fragBit = req->fragId & 1;
+ // get the fragment
+ FragPtr fragPtr;
+ fragPtr.i = RNIL;
+ for (unsigned i = 0; i < indexPtr.p->m_numFrags; i++) {
+ jam();
+ if (indexPtr.p->m_fragId[i] == fragId) {
+ jam();
+ c_fragPool.getPtr(fragPtr, indexPtr.p->m_fragPtrI[i]);
+ break;
+ }
+ }
+ ndbrequire(fragPtr.i != RNIL);
+ Frag& frag = *fragPtr.p;
+ // set up index keys for this operation
+ setKeyAttrs(frag);
+ // set up search entry
+ TreeEnt ent;
+ ent.m_tupLoc = TupLoc(req->pageId, req->pageOffset);
+ ent.m_tupVersion = req->tupVersion;
+ ent.m_fragBit = fragBit;
+ // read search key
+ readKeyAttrs(frag, ent, 0, c_searchKey);
+ if (! frag.m_storeNullKey) {
+ // check if all keys are null
+ const unsigned numAttrs = frag.m_numAttrs;
+ bool allNull = true;
+ for (unsigned i = 0; i < numAttrs; i++) {
+ if (c_searchKey[i] != 0) {
+ jam();
+ allNull = false;
+ break;
+ }
+ }
+ if (allNull) {
+ jam();
+ req->errorCode = 0;
+ *sig = *req;
+ return;
+ }
+ }
+#ifdef VM_TRACE
+ if (debugFlags & DebugMaint) {
+ debugOut << "opCode=" << dec << opCode;
+ debugOut << " opFlag=" << dec << opFlag;
+ debugOut << " tableId=" << dec << req->tableId;
+ debugOut << " indexId=" << dec << req->indexId;
+ debugOut << " fragId=" << dec << req->fragId;
+ debugOut << " entry=" << ent;
+ debugOut << endl;
+ }
+#endif
+ // do the operation
+ req->errorCode = 0;
+ TreePos treePos;
+ switch (opCode) {
+ case TuxMaintReq::OpAdd:
+ jam();
+ searchToAdd(frag, c_searchKey, ent, treePos);
+#ifdef VM_TRACE
+ if (debugFlags & DebugMaint) {
+ debugOut << treePos << (treePos.m_match ? " - error" : "") << endl;
+ }
+#endif
+ if (treePos.m_match) {
+ jam();
+ // there is no "Building" state so this will have to do
+ if (indexPtr.p->m_state == Index::Online) {
+ jam();
+ req->errorCode = TuxMaintReq::SearchError;
+ }
+ break;
+ }
+ /*
+ * At most one new node is inserted in the operation. Pre-allocate
+ * it so that the operation cannot fail.
+ */
+ if (frag.m_freeLoc == NullTupLoc) {
+ jam();
+ NodeHandle node(frag);
+ req->errorCode = allocNode(signal, node);
+ if (req->errorCode != 0) {
+ jam();
+ break;
+ }
+ // link to freelist
+ node.setLink(0, frag.m_freeLoc);
+ frag.m_freeLoc = node.m_loc;
+ ndbrequire(frag.m_freeLoc != NullTupLoc);
+ }
+ treeAdd(frag, treePos, ent);
+ break;
+ case TuxMaintReq::OpRemove:
+ jam();
+ searchToRemove(frag, c_searchKey, ent, treePos);
+#ifdef VM_TRACE
+ if (debugFlags & DebugMaint) {
+ debugOut << treePos << (! treePos.m_match ? " - error" : "") << endl;
+ }
+#endif
+ if (! treePos.m_match) {
+ jam();
+ // there is no "Building" state so this will have to do
+ if (indexPtr.p->m_state == Index::Online) {
+ jam();
+ req->errorCode = TuxMaintReq::SearchError;
+ }
+ break;
+ }
+ treeRemove(frag, treePos);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+#ifdef VM_TRACE
+ if (debugFlags & DebugTree) {
+ printTree(signal, frag, debugOut);
+ }
+#endif
+ // copy back
+ *sig = *req;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp
new file mode 100644
index 00000000000..93c4a583624
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp
@@ -0,0 +1,513 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_META_CPP
+#include "Dbtux.hpp"
+#include <my_sys.h>
+
+/*
+ * Create index.
+ *
+ * For historical reasons it looks like we are adding random fragments
+ * and attributes to existing index. In fact all fragments must be
+ * created at one time and they have identical attributes.
+ */
+
+void
+Dbtux::execTUXFRAGREQ(Signal* signal)
+{
+ jamEntry();
+ if (signal->theData[0] == (Uint32)-1) {
+ jam();
+ abortAddFragOp(signal);
+ return;
+ }
+ const TuxFragReq reqCopy = *(const TuxFragReq*)signal->getDataPtr();
+ const TuxFragReq* const req = &reqCopy;
+ IndexPtr indexPtr;
+ indexPtr.i = RNIL;
+ FragOpPtr fragOpPtr;
+ fragOpPtr.i = RNIL;
+ TuxFragRef::ErrorCode errorCode = TuxFragRef::NoError;
+ do {
+ // get the index record
+ if (req->tableId >= c_indexPool.getSize()) {
+ jam();
+ errorCode = TuxFragRef::InvalidRequest;
+ break;
+ }
+ c_indexPool.getPtr(indexPtr, req->tableId);
+ if (indexPtr.p->m_state != Index::NotDefined &&
+ indexPtr.p->m_state != Index::Defining) {
+ jam();
+ errorCode = TuxFragRef::InvalidRequest;
+ indexPtr.i = RNIL; // leave alone
+ break;
+ }
+ // get new operation record
+ c_fragOpPool.seize(fragOpPtr);
+ ndbrequire(fragOpPtr.i != RNIL);
+ new (fragOpPtr.p) FragOp();
+ fragOpPtr.p->m_userPtr = req->userPtr;
+ fragOpPtr.p->m_userRef = req->userRef;
+ fragOpPtr.p->m_indexId = req->tableId;
+ fragOpPtr.p->m_fragId = req->fragId;
+ fragOpPtr.p->m_fragNo = indexPtr.p->m_numFrags;
+ fragOpPtr.p->m_numAttrsRecvd = 0;
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Seize frag op " << fragOpPtr.i << " " << *fragOpPtr.p << endl;
+ }
+#endif
+ // check if index has place for more fragments
+ ndbrequire(indexPtr.p->m_numFrags < MaxIndexFragments);
+ // seize new fragment record
+ FragPtr fragPtr;
+ c_fragPool.seize(fragPtr);
+ if (fragPtr.i == RNIL) {
+ jam();
+ errorCode = TuxFragRef::NoFreeFragment;
+ break;
+ }
+ new (fragPtr.p) Frag(c_scanOpPool);
+ fragPtr.p->m_tableId = req->primaryTableId;
+ fragPtr.p->m_indexId = req->tableId;
+ fragPtr.p->m_fragId = req->fragId;
+ fragPtr.p->m_numAttrs = req->noOfAttr;
+ fragPtr.p->m_storeNullKey = true; // not yet configurable
+ fragPtr.p->m_tupIndexFragPtrI = req->tupIndexFragPtrI;
+ fragPtr.p->m_tupTableFragPtrI[0] = req->tupTableFragPtrI[0];
+ fragPtr.p->m_tupTableFragPtrI[1] = req->tupTableFragPtrI[1];
+ fragPtr.p->m_accTableFragPtrI[0] = req->accTableFragPtrI[0];
+ fragPtr.p->m_accTableFragPtrI[1] = req->accTableFragPtrI[1];
+ // add the fragment to the index
+ indexPtr.p->m_fragId[indexPtr.p->m_numFrags] = req->fragId;
+ indexPtr.p->m_fragPtrI[indexPtr.p->m_numFrags] = fragPtr.i;
+ indexPtr.p->m_numFrags++;
+ // save under operation
+ fragOpPtr.p->m_fragPtrI = fragPtr.i;
+ // prepare to receive attributes
+ if (fragOpPtr.p->m_fragNo == 0) {
+ jam();
+ // receiving first fragment
+ ndbrequire(
+ indexPtr.p->m_state == Index::NotDefined &&
+ DictTabInfo::isOrderedIndex(req->tableType) &&
+ req->noOfAttr > 0 &&
+ req->noOfAttr <= MaxIndexAttributes &&
+ indexPtr.p->m_descPage == RNIL);
+ indexPtr.p->m_state = Index::Defining;
+ indexPtr.p->m_tableType = (DictTabInfo::TableType)req->tableType;
+ indexPtr.p->m_tableId = req->primaryTableId;
+ indexPtr.p->m_numAttrs = req->noOfAttr;
+ indexPtr.p->m_storeNullKey = true; // not yet configurable
+ // allocate attribute descriptors
+ if (! allocDescEnt(indexPtr)) {
+ jam();
+ errorCode = TuxFragRef::NoFreeAttributes;
+ break;
+ }
+ } else {
+ // receiving subsequent fragment
+ jam();
+ ndbrequire(
+ indexPtr.p->m_state == Index::Defining &&
+ indexPtr.p->m_tableType == (DictTabInfo::TableType)req->tableType &&
+ indexPtr.p->m_tableId == req->primaryTableId &&
+ indexPtr.p->m_numAttrs == req->noOfAttr);
+ }
+ // copy metadata address to each fragment
+ fragPtr.p->m_descPage = indexPtr.p->m_descPage;
+ fragPtr.p->m_descOff = indexPtr.p->m_descOff;
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Add frag " << fragPtr.i << " " << *fragPtr.p << endl;
+ }
+#endif
+ // error inserts
+ if (ERROR_INSERTED(12001) && fragOpPtr.p->m_fragNo == 0 ||
+ ERROR_INSERTED(12002) && fragOpPtr.p->m_fragNo == 1) {
+ jam();
+ errorCode = (TuxFragRef::ErrorCode)1;
+ CLEAR_ERROR_INSERT_VALUE;
+ break;
+ }
+ // success
+ TuxFragConf* const conf = (TuxFragConf*)signal->getDataPtrSend();
+ conf->userPtr = req->userPtr;
+ conf->tuxConnectPtr = fragOpPtr.i;
+ conf->fragPtr = fragPtr.i;
+ conf->fragId = fragPtr.p->m_fragId;
+ sendSignal(req->userRef, GSN_TUXFRAGCONF,
+ signal, TuxFragConf::SignalLength, JBB);
+ return;
+ } while (0);
+ // error
+ TuxFragRef* const ref = (TuxFragRef*)signal->getDataPtrSend();
+ ref->userPtr = req->userPtr;
+ ref->errorCode = errorCode;
+ sendSignal(req->userRef, GSN_TUXFRAGREF,
+ signal, TuxFragRef::SignalLength, JBB);
+ if (fragOpPtr.i != RNIL) {
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Release on frag error frag op " << fragOpPtr.i << " " << *fragOpPtr.p << endl;
+ }
+#endif
+ c_fragOpPool.release(fragOpPtr);
+ }
+ if (indexPtr.i != RNIL) {
+ jam();
+ // let DICT drop the unfinished index
+ }
+}
+
+void
+Dbtux::execTUX_ADD_ATTRREQ(Signal* signal)
+{
+ jamEntry();
+ const TuxAddAttrReq reqCopy = *(const TuxAddAttrReq*)signal->getDataPtr();
+ const TuxAddAttrReq* const req = &reqCopy;
+ // get the records
+ FragOpPtr fragOpPtr;
+ IndexPtr indexPtr;
+ FragPtr fragPtr;
+ c_fragOpPool.getPtr(fragOpPtr, req->tuxConnectPtr);
+ c_indexPool.getPtr(indexPtr, fragOpPtr.p->m_indexId);
+ c_fragPool.getPtr(fragPtr, fragOpPtr.p->m_fragPtrI);
+ TuxAddAttrRef::ErrorCode errorCode = TuxAddAttrRef::NoError;
+ do {
+ // expected attribute id
+ const unsigned attrId = fragOpPtr.p->m_numAttrsRecvd++;
+ ndbrequire(
+ indexPtr.p->m_state == Index::Defining &&
+ attrId < indexPtr.p->m_numAttrs &&
+ attrId == req->attrId);
+ // define the attribute
+ DescEnt& descEnt = getDescEnt(indexPtr.p->m_descPage, indexPtr.p->m_descOff);
+ DescAttr& descAttr = descEnt.m_descAttr[attrId];
+ descAttr.m_attrDesc = req->attrDescriptor;
+ descAttr.m_primaryAttrId = req->primaryAttrId;
+ descAttr.m_typeId = AttributeDescriptor::getType(req->attrDescriptor);
+ descAttr.m_charset = (req->extTypeInfo >> 16);
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Add frag " << fragPtr.i << " attr " << attrId << " " << descAttr << endl;
+ }
+#endif
+ // check that type is valid and has a binary comparison method
+ const NdbSqlUtil::Type& type = NdbSqlUtil::getTypeBinary(descAttr.m_typeId);
+ if (type.m_typeId == NdbSqlUtil::Type::Undefined ||
+ type.m_cmp == 0) {
+ jam();
+ errorCode = TuxAddAttrRef::InvalidAttributeType;
+ break;
+ }
+ if (descAttr.m_charset != 0) {
+ CHARSET_INFO *cs = all_charsets[descAttr.m_charset];
+ ndbrequire(cs != 0);
+ if (! NdbSqlUtil::usable_in_ordered_index(descAttr.m_typeId, cs)) {
+ jam();
+ errorCode = TuxAddAttrRef::InvalidCharset;
+ break;
+ }
+ }
+ const bool lastAttr = (indexPtr.p->m_numAttrs == fragOpPtr.p->m_numAttrsRecvd);
+ if (ERROR_INSERTED(12003) && fragOpPtr.p->m_fragNo == 0 && attrId == 0 ||
+ ERROR_INSERTED(12004) && fragOpPtr.p->m_fragNo == 0 && lastAttr ||
+ ERROR_INSERTED(12005) && fragOpPtr.p->m_fragNo == 1 && attrId == 0 ||
+ ERROR_INSERTED(12006) && fragOpPtr.p->m_fragNo == 1 && lastAttr) {
+ errorCode = (TuxAddAttrRef::ErrorCode)1;
+ CLEAR_ERROR_INSERT_VALUE;
+ break;
+ }
+ if (lastAttr) {
+ jam();
+ // initialize tree header
+ TreeHead& tree = fragPtr.p->m_tree;
+ new (&tree) TreeHead();
+ // make these configurable later
+ tree.m_nodeSize = MAX_TTREE_NODE_SIZE;
+ tree.m_prefSize = MAX_TTREE_PREF_SIZE;
+ const unsigned maxSlack = MAX_TTREE_NODE_SLACK;
+ // size up to and including first 2 entries
+ const unsigned pref = tree.getSize(AccPref);
+ if (! (pref <= tree.m_nodeSize)) {
+ jam();
+ errorCode = TuxAddAttrRef::InvalidNodeSize;
+ break;
+ }
+ const unsigned slots = (tree.m_nodeSize - pref) / TreeEntSize;
+ // leave out work space entry
+ tree.m_maxOccup = 2 + slots - 1;
+ // min occupancy of interior node must be at least 2
+ if (! (2 + maxSlack <= tree.m_maxOccup)) {
+ jam();
+ errorCode = TuxAddAttrRef::InvalidNodeSize;
+ break;
+ }
+ tree.m_minOccup = tree.m_maxOccup - maxSlack;
+ // root node does not exist (also set by ctor)
+ tree.m_root = NullTupLoc;
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ if (fragOpPtr.p->m_fragNo == 0) {
+ debugOut << "Index id=" << indexPtr.i;
+ debugOut << " nodeSize=" << tree.m_nodeSize;
+ debugOut << " headSize=" << NodeHeadSize;
+ debugOut << " prefSize=" << tree.m_prefSize;
+ debugOut << " entrySize=" << TreeEntSize;
+ debugOut << " minOccup=" << tree.m_minOccup;
+ debugOut << " maxOccup=" << tree.m_maxOccup;
+ debugOut << endl;
+ }
+ }
+#endif
+ // fragment is defined
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Release frag op " << fragOpPtr.i << " " << *fragOpPtr.p << endl;
+ }
+#endif
+ c_fragOpPool.release(fragOpPtr);
+ }
+ // success
+ TuxAddAttrConf* conf = (TuxAddAttrConf*)signal->getDataPtrSend();
+ conf->userPtr = fragOpPtr.p->m_userPtr;
+ conf->lastAttr = lastAttr;
+ sendSignal(fragOpPtr.p->m_userRef, GSN_TUX_ADD_ATTRCONF,
+ signal, TuxAddAttrConf::SignalLength, JBB);
+ return;
+ } while (0);
+ // error
+ TuxAddAttrRef* ref = (TuxAddAttrRef*)signal->getDataPtrSend();
+ ref->userPtr = fragOpPtr.p->m_userPtr;
+ ref->errorCode = errorCode;
+ sendSignal(fragOpPtr.p->m_userRef, GSN_TUX_ADD_ATTRREF,
+ signal, TuxAddAttrRef::SignalLength, JBB);
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Release on attr error frag op " << fragOpPtr.i << " " << *fragOpPtr.p << endl;
+ }
+#endif
+ c_fragOpPool.release(fragOpPtr);
+ // let DICT drop the unfinished index
+}
+
+/*
+ * LQH aborts on-going create index operation.
+ */
+void
+Dbtux::abortAddFragOp(Signal* signal)
+{
+ FragOpPtr fragOpPtr;
+ IndexPtr indexPtr;
+ c_fragOpPool.getPtr(fragOpPtr, signal->theData[1]);
+ c_indexPool.getPtr(indexPtr, fragOpPtr.p->m_indexId);
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Release on abort frag op " << fragOpPtr.i << " " << *fragOpPtr.p << endl;
+ }
+#endif
+ c_fragOpPool.release(fragOpPtr);
+ // let DICT drop the unfinished index
+}
+
+/*
+ * Set index online. Currently at system restart this arrives before
+ * build and is therefore not correct.
+ */
+void
+Dbtux::execALTER_INDX_REQ(Signal* signal)
+{
+ jamEntry();
+ const AlterIndxReq reqCopy = *(const AlterIndxReq*)signal->getDataPtr();
+ const AlterIndxReq* const req = &reqCopy;
+ // set index online after build
+ IndexPtr indexPtr;
+ c_indexPool.getPtr(indexPtr, req->getIndexId());
+ indexPtr.p->m_state = Index::Online;
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Online index " << indexPtr.i << " " << *indexPtr.p << endl;
+ }
+#endif
+ // success
+ AlterIndxConf* const conf = (AlterIndxConf*)signal->getDataPtrSend();
+ conf->setUserRef(reference());
+ conf->setConnectionPtr(req->getConnectionPtr());
+ conf->setRequestType(req->getRequestType());
+ conf->setTableId(req->getTableId());
+ conf->setIndexId(req->getIndexId());
+ conf->setIndexVersion(req->getIndexVersion());
+ sendSignal(req->getUserRef(), GSN_ALTER_INDX_CONF,
+ signal, AlterIndxConf::SignalLength, JBB);
+}
+
+/*
+ * Drop index.
+ *
+ * Uses same DROP_TAB_REQ signal as normal tables.
+ */
+
+void
+Dbtux::execDROP_TAB_REQ(Signal* signal)
+{
+ jamEntry();
+ const DropTabReq reqCopy = *(const DropTabReq*)signal->getDataPtr();
+ const DropTabReq* const req = &reqCopy;
+ IndexPtr indexPtr;
+
+ Uint32 tableId = req->tableId;
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+ if (tableId >= c_indexPool.getSize()) {
+ jam();
+ // reply to sender
+ DropTabConf* const conf = (DropTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->tableId = tableId;
+ sendSignal(senderRef, GSN_DROP_TAB_CONF,
+ signal, DropTabConf::SignalLength, JBB);
+ return;
+ }
+
+ c_indexPool.getPtr(indexPtr, req->tableId);
+ // drop works regardless of index state
+#ifdef VM_TRACE
+ if (debugFlags & DebugMeta) {
+ debugOut << "Drop index " << indexPtr.i << " " << *indexPtr.p << endl;
+ }
+#endif
+ ndbrequire(req->senderRef != 0);
+ dropIndex(signal, indexPtr, req->senderRef, req->senderData);
+}
+
+void
+Dbtux::dropIndex(Signal* signal, IndexPtr indexPtr, Uint32 senderRef, Uint32 senderData)
+{
+ jam();
+ indexPtr.p->m_state = Index::Dropping;
+ // drop fragments
+ while (indexPtr.p->m_numFrags > 0) {
+ jam();
+ Uint32 i = --indexPtr.p->m_numFrags;
+ FragPtr fragPtr;
+ c_fragPool.getPtr(fragPtr, indexPtr.p->m_fragPtrI[i]);
+ c_fragPool.release(fragPtr);
+ }
+ // drop attributes
+ if (indexPtr.p->m_descPage != RNIL) {
+ jam();
+ freeDescEnt(indexPtr);
+ indexPtr.p->m_descPage = RNIL;
+ }
+ if (senderRef != 0) {
+ jam();
+ // reply to sender
+ DropTabConf* const conf = (DropTabConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->tableId = indexPtr.i;
+ sendSignal(senderRef, GSN_DROP_TAB_CONF,
+ signal, DropTabConf::SignalLength, JBB);
+ }
+ new (indexPtr.p) Index();
+}
+
+/*
+ * Subroutines.
+ */
+
+bool
+Dbtux::allocDescEnt(IndexPtr indexPtr)
+{
+ jam();
+ const unsigned size = DescHeadSize + indexPtr.p->m_numAttrs * DescAttrSize;
+ DescPagePtr pagePtr;
+ pagePtr.i = c_descPageList;
+ while (pagePtr.i != RNIL) {
+ jam();
+ c_descPagePool.getPtr(pagePtr);
+ if (pagePtr.p->m_numFree >= size) {
+ jam();
+ break;
+ }
+ pagePtr.i = pagePtr.p->m_nextPage;
+ }
+ if (pagePtr.i == RNIL) {
+ jam();
+ if (! c_descPagePool.seize(pagePtr)) {
+ jam();
+ return false;
+ }
+ new (pagePtr.p) DescPage();
+ // add in front of list
+ pagePtr.p->m_nextPage = c_descPageList;
+ c_descPageList = pagePtr.i;
+ pagePtr.p->m_numFree = DescPageSize;
+ }
+ ndbrequire(pagePtr.p->m_numFree >= size);
+ indexPtr.p->m_descPage = pagePtr.i;
+ indexPtr.p->m_descOff = DescPageSize - pagePtr.p->m_numFree;
+ pagePtr.p->m_numFree -= size;
+ DescEnt& descEnt = getDescEnt(indexPtr.p->m_descPage, indexPtr.p->m_descOff);
+ descEnt.m_descHead.m_indexId = indexPtr.i;
+ descEnt.m_descHead.pad1 = 0;
+ return true;
+}
+
+void
+Dbtux::freeDescEnt(IndexPtr indexPtr)
+{
+ DescPagePtr pagePtr;
+ c_descPagePool.getPtr(pagePtr, indexPtr.p->m_descPage);
+ Uint32* const data = pagePtr.p->m_data;
+ const unsigned size = DescHeadSize + indexPtr.p->m_numAttrs * DescAttrSize;
+ unsigned off = indexPtr.p->m_descOff;
+ // move the gap to the free area at the top
+ while (off + size < DescPageSize - pagePtr.p->m_numFree) {
+ jam();
+ // next entry to move over the gap
+ DescEnt& descEnt2 = *(DescEnt*)&data[off + size];
+ Uint32 indexId2 = descEnt2.m_descHead.m_indexId;
+ Index& index2 = *c_indexPool.getPtr(indexId2);
+ unsigned size2 = DescHeadSize + index2.m_numAttrs * DescAttrSize;
+ ndbrequire(
+ index2.m_descPage == pagePtr.i &&
+ index2.m_descOff == off + size);
+ // move the entry (overlapping copy if size < size2)
+ unsigned i;
+ for (i = 0; i < size2; i++) {
+ jam();
+ data[off + i] = data[off + size + i];
+ }
+ off += size2;
+ // adjust page offset in index and all fragments
+ index2.m_descOff -= size;
+ for (i = 0; i < index2.m_numFrags; i++) {
+ jam();
+ Frag& frag2 = *c_fragPool.getPtr(index2.m_fragPtrI[i]);
+ frag2.m_descOff -= size;
+ ndbrequire(
+ frag2.m_descPage == index2.m_descPage &&
+ frag2.m_descOff == index2.m_descOff);
+ }
+ }
+ ndbrequire(off + size == DescPageSize - pagePtr.p->m_numFree);
+ pagePtr.p->m_numFree += size;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp
new file mode 100644
index 00000000000..855a8ed1c29
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp
@@ -0,0 +1,581 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_NODE_CPP
+#include "Dbtux.hpp"
+
+/*
+ * Allocate index node in TUP.
+ */
+int
+Dbtux::allocNode(Signal* signal, NodeHandle& node)
+{
+ Frag& frag = node.m_frag;
+ Uint32 pageId = NullTupLoc.getPageId();
+ Uint32 pageOffset = NullTupLoc.getPageOffset();
+ Uint32* node32 = 0;
+ int errorCode = c_tup->tuxAllocNode(signal, frag.m_tupIndexFragPtrI, pageId, pageOffset, node32);
+ jamEntry();
+ if (errorCode == 0) {
+ jam();
+ node.m_loc = TupLoc(pageId, pageOffset);
+ node.m_node = reinterpret_cast<TreeNode*>(node32);
+ ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0);
+ }
+ return errorCode;
+}
+
+/*
+ * Set handle to point to existing node.
+ */
+void
+Dbtux::selectNode(NodeHandle& node, TupLoc loc)
+{
+ Frag& frag = node.m_frag;
+ ndbrequire(loc != NullTupLoc);
+ Uint32 pageId = loc.getPageId();
+ Uint32 pageOffset = loc.getPageOffset();
+ Uint32* node32 = 0;
+ c_tup->tuxGetNode(frag.m_tupIndexFragPtrI, pageId, pageOffset, node32);
+ jamEntry();
+ node.m_loc = loc;
+ node.m_node = reinterpret_cast<TreeNode*>(node32);
+ ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0);
+}
+
+/*
+ * Set handle to point to new node. Uses a pre-allocated node.
+ */
+void
+Dbtux::insertNode(NodeHandle& node)
+{
+ Frag& frag = node.m_frag;
+ // unlink from freelist
+ selectNode(node, frag.m_freeLoc);
+ frag.m_freeLoc = node.getLink(0);
+ new (node.m_node) TreeNode();
+#ifdef VM_TRACE
+ TreeHead& tree = frag.m_tree;
+ memset(node.getPref(), DataFillByte, tree.m_prefSize << 2);
+ TreeEnt* entList = tree.getEntList(node.m_node);
+ memset(entList, NodeFillByte, (tree.m_maxOccup + 1) * (TreeEntSize << 2));
+#endif
+}
+
+/*
+ * Delete existing node. Simply put it on the freelist.
+ */
+void
+Dbtux::deleteNode(NodeHandle& node)
+{
+ Frag& frag = node.m_frag;
+ ndbrequire(node.getOccup() == 0);
+ // link to freelist
+ node.setLink(0, frag.m_freeLoc);
+ frag.m_freeLoc = node.m_loc;
+ // invalidate the handle
+ node.m_loc = NullTupLoc;
+ node.m_node = 0;
+}
+
+/*
+ * Set prefix. Copies the number of words that fits. Includes
+ * attribute headers for now. XXX use null mask instead
+ */
+void
+Dbtux::setNodePref(NodeHandle& node)
+{
+ const Frag& frag = node.m_frag;
+ const TreeHead& tree = frag.m_tree;
+ readKeyAttrs(frag, node.getMinMax(0), 0, c_entryKey);
+ copyAttrs(frag, c_entryKey, node.getPref(), tree.m_prefSize);
+}
+
+// node operations
+
+/*
+ * Add entry at position. Move entries greater than or equal to the old
+ * one (if any) to the right.
+ *
+ * X
+ * v
+ * A B C D E _ _ => A B C X D E _
+ * 0 1 2 3 4 5 6 0 1 2 3 4 5 6
+ *
+ * Add list of scans at the new entry.
+ */
+void
+Dbtux::nodePushUp(NodeHandle& node, unsigned pos, const TreeEnt& ent, Uint32 scanList)
+{
+ Frag& frag = node.m_frag;
+ TreeHead& tree = frag.m_tree;
+ const unsigned occup = node.getOccup();
+ ndbrequire(occup < tree.m_maxOccup && pos <= occup);
+ // fix old scans
+ if (node.getNodeScan() != RNIL)
+ nodePushUpScans(node, pos);
+ // fix node
+ TreeEnt* const entList = tree.getEntList(node.m_node);
+ entList[occup] = entList[0];
+ TreeEnt* const tmpList = entList + 1;
+ for (unsigned i = occup; i > pos; i--) {
+ jam();
+ tmpList[i] = tmpList[i - 1];
+ }
+ tmpList[pos] = ent;
+ entList[0] = entList[occup + 1];
+ node.setOccup(occup + 1);
+ // add new scans
+ if (scanList != RNIL)
+ addScanList(node, pos, scanList);
+ // fix prefix
+ if (occup == 0 || pos == 0)
+ setNodePref(node);
+}
+
+void
+Dbtux::nodePushUpScans(NodeHandle& node, unsigned pos)
+{
+ const unsigned occup = node.getOccup();
+ ScanOpPtr scanPtr;
+ scanPtr.i = node.getNodeScan();
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
+ if (scanPos.m_pos >= pos) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "At pushUp pos=" << pos << " " << node << endl;
+ }
+#endif
+ scanPos.m_pos++;
+ }
+ scanPtr.i = scanPtr.p->m_nodeScan;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Remove and return entry at position. Move entries greater than the
+ * removed one to the left. This is the opposite of nodePushUp.
+ *
+ * D
+ * ^ ^
+ * A B C D E F _ => A B C E F _ _
+ * 0 1 2 3 4 5 6 0 1 2 3 4 5 6
+ *
+ * Scans at removed entry are returned if non-zero location is passed or
+ * else moved forward.
+ */
+void
+Dbtux::nodePopDown(NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32* scanList)
+{
+ Frag& frag = node.m_frag;
+ TreeHead& tree = frag.m_tree;
+ const unsigned occup = node.getOccup();
+ ndbrequire(occup <= tree.m_maxOccup && pos < occup);
+ if (node.getNodeScan() != RNIL) {
+ // remove or move scans at this position
+ if (scanList == 0)
+ moveScanList(node, pos);
+ else
+ removeScanList(node, pos, *scanList);
+ // fix other scans
+ if (node.getNodeScan() != RNIL)
+ nodePopDownScans(node, pos);
+ }
+ // fix node
+ TreeEnt* const entList = tree.getEntList(node.m_node);
+ entList[occup] = entList[0];
+ TreeEnt* const tmpList = entList + 1;
+ ent = tmpList[pos];
+ for (unsigned i = pos; i < occup - 1; i++) {
+ jam();
+ tmpList[i] = tmpList[i + 1];
+ }
+ entList[0] = entList[occup - 1];
+ node.setOccup(occup - 1);
+ // fix prefix
+ if (occup != 1 && pos == 0)
+ setNodePref(node);
+}
+
+void
+Dbtux::nodePopDownScans(NodeHandle& node, unsigned pos)
+{
+ const unsigned occup = node.getOccup();
+ ScanOpPtr scanPtr;
+ scanPtr.i = node.getNodeScan();
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
+ // handled before
+ ndbrequire(scanPos.m_pos != pos);
+ if (scanPos.m_pos > pos) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "At popDown pos=" << pos << " " << node << endl;
+ }
+#endif
+ scanPos.m_pos--;
+ }
+ scanPtr.i = scanPtr.p->m_nodeScan;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Add entry at existing position. Move entries less than or equal to
+ * the old one to the left. Remove and return old min entry.
+ *
+ * X A
+ * ^ v ^
+ * A B C D E _ _ => B C D X E _ _
+ * 0 1 2 3 4 5 6 0 1 2 3 4 5 6
+ *
+ * Return list of scans at the removed position 0.
+ */
+void
+Dbtux::nodePushDown(NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32& scanList)
+{
+ Frag& frag = node.m_frag;
+ TreeHead& tree = frag.m_tree;
+ const unsigned occup = node.getOccup();
+ ndbrequire(occup <= tree.m_maxOccup && pos < occup);
+ if (node.getNodeScan() != RNIL) {
+ // remove scans at 0
+ removeScanList(node, 0, scanList);
+ // fix other scans
+ if (node.getNodeScan() != RNIL)
+ nodePushDownScans(node, pos);
+ }
+ // fix node
+ TreeEnt* const entList = tree.getEntList(node.m_node);
+ entList[occup] = entList[0];
+ TreeEnt* const tmpList = entList + 1;
+ TreeEnt oldMin = tmpList[0];
+ for (unsigned i = 0; i < pos; i++) {
+ jam();
+ tmpList[i] = tmpList[i + 1];
+ }
+ tmpList[pos] = ent;
+ ent = oldMin;
+ entList[0] = entList[occup];
+ // fix prefix
+ if (true)
+ setNodePref(node);
+}
+
+void
+Dbtux::nodePushDownScans(NodeHandle& node, unsigned pos)
+{
+ const unsigned occup = node.getOccup();
+ ScanOpPtr scanPtr;
+ scanPtr.i = node.getNodeScan();
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
+ // handled before
+ ndbrequire(scanPos.m_pos != 0);
+ if (scanPos.m_pos <= pos) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "At pushDown pos=" << pos << " " << node << endl;
+ }
+#endif
+ scanPos.m_pos--;
+ }
+ scanPtr.i = scanPtr.p->m_nodeScan;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Remove and return entry at position. Move entries less than the
+ * removed one to the right. Replace min entry by the input entry.
+ * This is the opposite of nodePushDown.
+ *
+ * X D
+ * v ^ ^
+ * A B C D E _ _ => X A B C E _ _
+ * 0 1 2 3 4 5 6 0 1 2 3 4 5 6
+ *
+ * Move scans at removed entry and add scans at the new entry.
+ */
+void
+Dbtux::nodePopUp(NodeHandle& node, unsigned pos, TreeEnt& ent, Uint32 scanList)
+{
+ Frag& frag = node.m_frag;
+ TreeHead& tree = frag.m_tree;
+ const unsigned occup = node.getOccup();
+ ndbrequire(occup <= tree.m_maxOccup && pos < occup);
+ if (node.getNodeScan() != RNIL) {
+ // move scans whose entry disappears
+ moveScanList(node, pos);
+ // fix other scans
+ if (node.getNodeScan() != RNIL)
+ nodePopUpScans(node, pos);
+ }
+ // fix node
+ TreeEnt* const entList = tree.getEntList(node.m_node);
+ entList[occup] = entList[0];
+ TreeEnt* const tmpList = entList + 1;
+ TreeEnt newMin = ent;
+ ent = tmpList[pos];
+ for (unsigned i = pos; i > 0; i--) {
+ jam();
+ tmpList[i] = tmpList[i - 1];
+ }
+ tmpList[0] = newMin;
+ entList[0] = entList[occup];
+ // add scans
+ if (scanList != RNIL)
+ addScanList(node, 0, scanList);
+ // fix prefix
+ if (true)
+ setNodePref(node);
+}
+
+void
+Dbtux::nodePopUpScans(NodeHandle& node, unsigned pos)
+{
+ const unsigned occup = node.getOccup();
+ ScanOpPtr scanPtr;
+ scanPtr.i = node.getNodeScan();
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup);
+ ndbrequire(scanPos.m_pos != pos);
+ if (scanPos.m_pos < pos) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "At popUp pos=" << pos << " " << node << endl;
+ }
+#endif
+ scanPos.m_pos++;
+ }
+ scanPtr.i = scanPtr.p->m_nodeScan;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Move number of entries from another node to this node before the min
+ * (i=0) or after the max (i=1). Expensive but not often used.
+ */
+void
+Dbtux::nodeSlide(NodeHandle& dstNode, NodeHandle& srcNode, unsigned cnt, unsigned i)
+{
+ Frag& frag = dstNode.m_frag;
+ TreeHead& tree = frag.m_tree;
+ ndbrequire(i <= 1);
+ while (cnt != 0) {
+ TreeEnt ent;
+ Uint32 scanList = RNIL;
+ nodePopDown(srcNode, i == 0 ? srcNode.getOccup() - 1 : 0, ent, &scanList);
+ nodePushUp(dstNode, i == 0 ? 0 : dstNode.getOccup(), ent, scanList);
+ cnt--;
+ }
+}
+
+// scans linked to node
+
+
+/*
+ * Add list of scans to node at given position.
+ */
+void
+Dbtux::addScanList(NodeHandle& node, unsigned pos, Uint32 scanList)
+{
+ ScanOpPtr scanPtr;
+ scanPtr.i = scanList;
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Add scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "To pos=" << pos << " " << node << endl;
+ }
+#endif
+ const Uint32 nextPtrI = scanPtr.p->m_nodeScan;
+ scanPtr.p->m_nodeScan = RNIL;
+ linkScan(node, scanPtr);
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ // set position but leave direction alone
+ scanPos.m_loc = node.m_loc;
+ scanPos.m_pos = pos;
+ scanPtr.i = nextPtrI;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Remove list of scans from node at given position. The return
+ * location must point to existing list (in fact RNIL always).
+ */
+void
+Dbtux::removeScanList(NodeHandle& node, unsigned pos, Uint32& scanList)
+{
+ ScanOpPtr scanPtr;
+ scanPtr.i = node.getNodeScan();
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+ const Uint32 nextPtrI = scanPtr.p->m_nodeScan;
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ ndbrequire(scanPos.m_loc == node.m_loc);
+ if (scanPos.m_pos == pos) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Remove scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "Fron pos=" << pos << " " << node << endl;
+ }
+#endif
+ unlinkScan(node, scanPtr);
+ scanPtr.p->m_nodeScan = scanList;
+ scanList = scanPtr.i;
+ // unset position but leave direction alone
+ scanPos.m_loc = NullTupLoc;
+ scanPos.m_pos = ZNIL;
+ }
+ scanPtr.i = nextPtrI;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Move list of scans away from entry about to be removed. Uses scan
+ * method scanNext().
+ */
+void
+Dbtux::moveScanList(NodeHandle& node, unsigned pos)
+{
+ ScanOpPtr scanPtr;
+ scanPtr.i = node.getNodeScan();
+ do {
+ jam();
+ c_scanOpPool.getPtr(scanPtr);
+ TreePos& scanPos = scanPtr.p->m_scanPos;
+ const Uint32 nextPtrI = scanPtr.p->m_nodeScan;
+ ndbrequire(scanPos.m_loc == node.m_loc);
+ if (scanPos.m_pos == pos) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "At pos=" << pos << " " << node << endl;
+ }
+#endif
+ scanNext(scanPtr, true);
+ ndbrequire(! (scanPos.m_loc == node.m_loc && scanPos.m_pos == pos));
+ }
+ scanPtr.i = nextPtrI;
+ } while (scanPtr.i != RNIL);
+}
+
+/*
+ * Link scan to the list under the node. The list is single-linked and
+ * ordering does not matter.
+ */
+void
+Dbtux::linkScan(NodeHandle& node, ScanOpPtr scanPtr)
+{
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Link scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "To node " << node << endl;
+ }
+#endif
+ ndbrequire(! islinkScan(node, scanPtr) && scanPtr.p->m_nodeScan == RNIL);
+ scanPtr.p->m_nodeScan = node.getNodeScan();
+ node.setNodeScan(scanPtr.i);
+}
+
+/*
+ * Unlink a scan from the list under the node.
+ */
+void
+Dbtux::unlinkScan(NodeHandle& node, ScanOpPtr scanPtr)
+{
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Unlink scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ debugOut << "From node " << node << endl;
+ }
+#endif
+ ScanOpPtr currPtr;
+ currPtr.i = node.getNodeScan();
+ ScanOpPtr prevPtr;
+ prevPtr.i = RNIL;
+ while (true) {
+ jam();
+ c_scanOpPool.getPtr(currPtr);
+ Uint32 nextPtrI = currPtr.p->m_nodeScan;
+ if (currPtr.i == scanPtr.i) {
+ jam();
+ if (prevPtr.i == RNIL) {
+ node.setNodeScan(nextPtrI);
+ } else {
+ jam();
+ prevPtr.p->m_nodeScan = nextPtrI;
+ }
+ scanPtr.p->m_nodeScan = RNIL;
+ // check for duplicates
+ ndbrequire(! islinkScan(node, scanPtr));
+ return;
+ }
+ prevPtr = currPtr;
+ currPtr.i = nextPtrI;
+ }
+}
+
+/*
+ * Check if a scan is linked to this node. Only for ndbrequire.
+ */
+bool
+Dbtux::islinkScan(NodeHandle& node, ScanOpPtr scanPtr)
+{
+ ScanOpPtr currPtr;
+ currPtr.i = node.getNodeScan();
+ while (currPtr.i != RNIL) {
+ jam();
+ c_scanOpPool.getPtr(currPtr);
+ if (currPtr.i == scanPtr.i) {
+ jam();
+ return true;
+ }
+ currPtr.i = currPtr.p->m_nodeScan;
+ }
+ return false;
+}
+
+void
+Dbtux::NodeHandle::progError(int line, int cause, const char* file)
+{
+ ErrorReporter::handleAssert("Dbtux::NodeHandle: assert failed", file, line);
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp
new file mode 100644
index 00000000000..a61b7c1f5ca
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp
@@ -0,0 +1,1041 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_SCAN_CPP
+#include "Dbtux.hpp"
+#include <my_sys.h>
+
+void
+Dbtux::execACC_SCANREQ(Signal* signal)
+{
+ jamEntry();
+ const AccScanReq reqCopy = *(const AccScanReq*)signal->getDataPtr();
+ const AccScanReq* const req = &reqCopy;
+ ScanOpPtr scanPtr;
+ scanPtr.i = RNIL;
+ do {
+ // get the index
+ IndexPtr indexPtr;
+ c_indexPool.getPtr(indexPtr, req->tableId);
+ // get the fragment
+ FragPtr fragPtr;
+ fragPtr.i = RNIL;
+ for (unsigned i = 0; i < indexPtr.p->m_numFrags; i++) {
+ jam();
+ if (indexPtr.p->m_fragId[i] == req->fragmentNo << 1) {
+ jam();
+ c_fragPool.getPtr(fragPtr, indexPtr.p->m_fragPtrI[i]);
+ break;
+ }
+ }
+ ndbrequire(fragPtr.i != RNIL);
+ Frag& frag = *fragPtr.p;
+ // must be normal DIH/TC fragment
+ TreeHead& tree = frag.m_tree;
+ // check for empty fragment
+ if (tree.m_root == NullTupLoc) {
+ jam();
+ AccScanConf* const conf = (AccScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = req->senderData;
+ conf->accPtr = RNIL;
+ conf->flag = AccScanConf::ZEMPTY_FRAGMENT;
+ sendSignal(req->senderRef, GSN_ACC_SCANCONF,
+ signal, AccScanConf::SignalLength, JBB);
+ return;
+ }
+ // seize from pool and link to per-fragment list
+ if (! frag.m_scanList.seize(scanPtr)) {
+ jam();
+ break;
+ }
+ new (scanPtr.p) ScanOp(c_scanBoundPool);
+ scanPtr.p->m_state = ScanOp::First;
+ scanPtr.p->m_userPtr = req->senderData;
+ scanPtr.p->m_userRef = req->senderRef;
+ scanPtr.p->m_tableId = indexPtr.p->m_tableId;
+ scanPtr.p->m_indexId = indexPtr.i;
+ scanPtr.p->m_fragId = fragPtr.p->m_fragId;
+ scanPtr.p->m_fragPtrI = fragPtr.i;
+ scanPtr.p->m_transId1 = req->transId1;
+ scanPtr.p->m_transId2 = req->transId2;
+ scanPtr.p->m_savePointId = req->savePointId;
+ scanPtr.p->m_readCommitted = AccScanReq::getReadCommittedFlag(req->requestInfo);
+ scanPtr.p->m_lockMode = AccScanReq::getLockMode(req->requestInfo);
+ scanPtr.p->m_descending = AccScanReq::getDescendingFlag(req->requestInfo);
+ /*
+ * readCommitted lockMode keyInfo
+ * 1 0 0 - read committed (no lock)
+ * 0 0 0 - read latest (read lock)
+ * 0 1 1 - read exclusive (write lock)
+ */
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Seize scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ }
+#endif
+ // conf
+ AccScanConf* const conf = (AccScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = req->senderData;
+ conf->accPtr = scanPtr.i;
+ conf->flag = AccScanConf::ZNOT_EMPTY_FRAGMENT;
+ sendSignal(req->senderRef, GSN_ACC_SCANCONF,
+ signal, AccScanConf::SignalLength, JBB);
+ return;
+ } while (0);
+ if (scanPtr.i != RNIL) {
+ jam();
+ releaseScanOp(scanPtr);
+ }
+ // LQH does not handle REF
+ signal->theData[0] = 0x313;
+ sendSignal(req->senderRef, GSN_ACC_SCANREF,
+ signal, 1, JBB);
+}
+
+/*
+ * Receive bounds for scan in single direct call. The bounds can arrive
+ * in any order. Attribute ids are those of index table.
+ *
+ * Replace EQ by equivalent LE + GE. Check for conflicting bounds.
+ * Check that sets of lower and upper bounds are on initial sequences of
+ * keys and that all but possibly last bound is non-strict.
+ *
+ * Finally save the sets of lower and upper bounds (i.e. start key and
+ * end key). Full bound type is included but only the strict bit is
+ * used since lower and upper have now been separated.
+ */
+void
+Dbtux::execTUX_BOUND_INFO(Signal* signal)
+{
+ jamEntry();
+ // get records
+ TuxBoundInfo* const sig = (TuxBoundInfo*)signal->getDataPtrSend();
+ const TuxBoundInfo* const req = (const TuxBoundInfo*)sig;
+ ScanOp& scan = *c_scanOpPool.getPtr(req->tuxScanPtrI);
+ const Index& index = *c_indexPool.getPtr(scan.m_indexId);
+ const DescEnt& descEnt = getDescEnt(index.m_descPage, index.m_descOff);
+ // collect normalized lower and upper bounds
+ struct BoundInfo {
+ int type2; // with EQ -> LE/GE
+ Uint32 offset; // offset in xfrmData
+ Uint32 size;
+ };
+ BoundInfo boundInfo[2][MaxIndexAttributes];
+ const unsigned dstSize = 1024 * MAX_XFRM_MULTIPLY;
+ Uint32 xfrmData[dstSize];
+ Uint32 dstPos = 0;
+ // largest attrId seen plus one
+ Uint32 maxAttrId[2] = { 0, 0 };
+ // walk through entries
+ const Uint32* const data = (Uint32*)sig + TuxBoundInfo::SignalLength;
+ Uint32 offset = 0;
+ while (offset + 2 <= req->boundAiLength) {
+ jam();
+ const unsigned type = data[offset];
+ const AttributeHeader* ah = (const AttributeHeader*)&data[offset + 1];
+ const Uint32 attrId = ah->getAttributeId();
+ const Uint32 dataSize = ah->getDataSize();
+ if (type > 4 || attrId >= index.m_numAttrs || dstPos + 2 + dataSize > dstSize) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::InvalidAttrInfo;
+ return;
+ }
+ // copy header
+ xfrmData[dstPos + 0] = data[offset + 0];
+ xfrmData[dstPos + 1] = data[offset + 1];
+ // copy bound value
+ Uint32 dstWords = 0;
+ if (! ah->isNULL()) {
+ jam();
+ const DescAttr& descAttr = descEnt.m_descAttr[attrId];
+ Uint32 srcBytes = AttributeDescriptor::getSizeInBytes(descAttr.m_attrDesc);
+ Uint32 srcWords = (srcBytes + 3) / 4;
+ if (srcWords != dataSize) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::InvalidAttrInfo;
+ return;
+ }
+ uchar* dstPtr = (uchar*)&xfrmData[dstPos + 2];
+ const uchar* srcPtr = (const uchar*)&data[offset + 2];
+ if (descAttr.m_charset == 0) {
+ memcpy(dstPtr, srcPtr, srcWords << 2);
+ dstWords = srcWords;
+ } else {
+ jam();
+ Uint32 typeId = descAttr.m_typeId;
+ Uint32 lb, len;
+ bool ok = NdbSqlUtil::get_var_length(typeId, srcPtr, srcBytes, lb, len);
+ if (! ok) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::InvalidCharFormat;
+ return;
+ }
+ CHARSET_INFO* cs = all_charsets[descAttr.m_charset];
+ Uint32 xmul = cs->strxfrm_multiply;
+ if (xmul == 0)
+ xmul = 1;
+ // see comment in DbtcMain.cpp
+ Uint32 dstLen = xmul * (srcBytes - lb);
+ if (dstLen > ((dstSize - dstPos) << 2)) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::TooMuchAttrInfo;
+ return;
+ }
+ int n = NdbSqlUtil::strnxfrm_bug7284(cs, dstPtr, dstLen, srcPtr + lb, len);
+ ndbrequire(n != -1);
+ while ((n & 3) != 0) {
+ dstPtr[n++] = 0;
+ }
+ dstWords = n / 4;
+ }
+ }
+ for (unsigned j = 0; j <= 1; j++) {
+ jam();
+ // check if lower/upper bit matches
+ const unsigned luBit = (j << 1);
+ if ((type & 0x2) != luBit && type != 4)
+ continue;
+ // EQ -> LE, GE
+ const unsigned type2 = (type & 0x1) | luBit;
+ // fill in any gap
+ while (maxAttrId[j] <= attrId) {
+ jam();
+ BoundInfo& b = boundInfo[j][maxAttrId[j]++];
+ b.type2 = -1;
+ }
+ BoundInfo& b = boundInfo[j][attrId];
+ if (b.type2 != -1) {
+ // compare with previously defined bound
+ if (b.type2 != (int)type2 ||
+ b.size != 2 + dstWords ||
+ memcmp(&xfrmData[b.offset + 2], &xfrmData[dstPos + 2], dstWords << 2) != 0) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::InvalidBounds;
+ return;
+ }
+ } else {
+ // fix length
+ AttributeHeader* ah = (AttributeHeader*)&xfrmData[dstPos + 1];
+ ah->setDataSize(dstWords);
+ // enter new bound
+ jam();
+ b.type2 = type2;
+ b.offset = dstPos;
+ b.size = 2 + dstWords;
+ }
+ }
+ // jump to next
+ offset += 2 + dataSize;
+ dstPos += 2 + dstWords;
+ }
+ if (offset != req->boundAiLength) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::InvalidAttrInfo;
+ return;
+ }
+ for (unsigned j = 0; j <= 1; j++) {
+ // save lower/upper bound in index attribute id order
+ for (unsigned i = 0; i < maxAttrId[j]; i++) {
+ jam();
+ const BoundInfo& b = boundInfo[j][i];
+ // check for gap or strict bound before last
+ if (b.type2 == -1 || (i + 1 < maxAttrId[j] && (b.type2 & 0x1))) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::InvalidBounds;
+ return;
+ }
+ bool ok = scan.m_bound[j]->append(&xfrmData[b.offset], b.size);
+ if (! ok) {
+ jam();
+ scan.m_state = ScanOp::Invalid;
+ sig->errorCode = TuxBoundInfo::OutOfBuffers;
+ return;
+ }
+ }
+ scan.m_boundCnt[j] = maxAttrId[j];
+ }
+ // no error
+ sig->errorCode = 0;
+}
+
+void
+Dbtux::execNEXT_SCANREQ(Signal* signal)
+{
+ jamEntry();
+ const NextScanReq reqCopy = *(const NextScanReq*)signal->getDataPtr();
+ const NextScanReq* const req = &reqCopy;
+ ScanOpPtr scanPtr;
+ scanPtr.i = req->accPtr;
+ c_scanOpPool.getPtr(scanPtr);
+ ScanOp& scan = *scanPtr.p;
+ Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "NEXT_SCANREQ scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ // handle unlock previous and close scan
+ switch (req->scanFlag) {
+ case NextScanReq::ZSCAN_NEXT:
+ jam();
+ break;
+ case NextScanReq::ZSCAN_NEXT_COMMIT:
+ jam();
+ case NextScanReq::ZSCAN_COMMIT:
+ jam();
+ if (! scan.m_readCommitted) {
+ jam();
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo = AccLockReq::Unlock;
+ lockReq->accOpPtr = req->accOperationPtr;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength);
+ jamEntry();
+ ndbrequire(lockReq->returnCode == AccLockReq::Success);
+ removeAccLockOp(scan, req->accOperationPtr);
+ }
+ if (req->scanFlag == NextScanReq::ZSCAN_COMMIT) {
+ jam();
+ NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = scan.m_userPtr;
+ unsigned signalLength = 1;
+ sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF,
+ signal, signalLength, JBB);
+ return;
+ }
+ break;
+ case NextScanReq::ZSCAN_CLOSE:
+ jam();
+ // unlink from tree node first to avoid state changes
+ if (scan.m_scanPos.m_loc != NullTupLoc) {
+ jam();
+ const TupLoc loc = scan.m_scanPos.m_loc;
+ NodeHandle node(frag);
+ selectNode(node, loc);
+ unlinkScan(node, scanPtr);
+ scan.m_scanPos.m_loc = NullTupLoc;
+ }
+ if (scan.m_lockwait) {
+ jam();
+ ndbrequire(scan.m_accLockOp != RNIL);
+ // use ACC_ABORTCONF to flush out any reply in job buffer
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo = AccLockReq::AbortWithConf;
+ lockReq->accOpPtr = scan.m_accLockOp;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength);
+ jamEntry();
+ ndbrequire(lockReq->returnCode == AccLockReq::Success);
+ scan.m_state = ScanOp::Aborting;
+ return;
+ }
+ if (scan.m_state == ScanOp::Locked) {
+ jam();
+ ndbrequire(scan.m_accLockOp != RNIL);
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo = AccLockReq::Unlock;
+ lockReq->accOpPtr = scan.m_accLockOp;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength);
+ jamEntry();
+ ndbrequire(lockReq->returnCode == AccLockReq::Success);
+ scan.m_accLockOp = RNIL;
+ }
+ scan.m_state = ScanOp::Aborting;
+ scanClose(signal, scanPtr);
+ return;
+ case NextScanReq::ZSCAN_NEXT_ABORT:
+ jam();
+ default:
+ jam();
+ ndbrequire(false);
+ break;
+ }
+ // start looking for next scan result
+ AccCheckScan* checkReq = (AccCheckScan*)signal->getDataPtrSend();
+ checkReq->accPtr = scanPtr.i;
+ checkReq->checkLcpStop = AccCheckScan::ZNOT_CHECK_LCP_STOP;
+ EXECUTE_DIRECT(DBTUX, GSN_ACC_CHECK_SCAN, signal, AccCheckScan::SignalLength);
+ jamEntry();
+}
+
+void
+Dbtux::execACC_CHECK_SCAN(Signal* signal)
+{
+ jamEntry();
+ const AccCheckScan reqCopy = *(const AccCheckScan*)signal->getDataPtr();
+ const AccCheckScan* const req = &reqCopy;
+ ScanOpPtr scanPtr;
+ scanPtr.i = req->accPtr;
+ c_scanOpPool.getPtr(scanPtr);
+ ScanOp& scan = *scanPtr.p;
+ Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "ACC_CHECK_SCAN scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ if (req->checkLcpStop == AccCheckScan::ZCHECK_LCP_STOP) {
+ jam();
+ signal->theData[0] = scan.m_userPtr;
+ signal->theData[1] = true;
+ EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2);
+ jamEntry();
+ return; // stop
+ }
+ if (scan.m_lockwait) {
+ jam();
+ // LQH asks if we are waiting for lock and we tell it to ask again
+ const TreeEnt ent = scan.m_scanEnt;
+ NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = scan.m_userPtr;
+ conf->accOperationPtr = RNIL; // no tuple returned
+ conf->fragId = frag.m_fragId | ent.m_fragBit;
+ unsigned signalLength = 3;
+ // if TC has ordered scan close, it will be detected here
+ sendSignal(scan.m_userRef, GSN_NEXT_SCANCONF,
+ signal, signalLength, JBB);
+ return; // stop
+ }
+ if (scan.m_state == ScanOp::First) {
+ jam();
+ // search is done only once in single range scan
+ scanFirst(scanPtr);
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "First scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ }
+ if (scan.m_state == ScanOp::Next) {
+ jam();
+ // look for next
+ scanNext(scanPtr, false);
+ }
+ // for reading tuple key in Current or Locked state
+ Data pkData = c_dataBuffer;
+ unsigned pkSize = 0; // indicates not yet done
+ if (scan.m_state == ScanOp::Current) {
+ // found an entry to return
+ jam();
+ ndbrequire(scan.m_accLockOp == RNIL);
+ if (! scan.m_readCommitted) {
+ jam();
+ const TreeEnt ent = scan.m_scanEnt;
+ // read tuple key
+ readTablePk(frag, ent, pkData, pkSize);
+ // get read lock or exclusive lock
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo =
+ scan.m_lockMode == 0 ? AccLockReq::LockShared : AccLockReq::LockExclusive;
+ lockReq->accOpPtr = RNIL;
+ lockReq->userPtr = scanPtr.i;
+ lockReq->userRef = reference();
+ lockReq->tableId = scan.m_tableId;
+ lockReq->fragId = frag.m_fragId | ent.m_fragBit;
+ lockReq->fragPtrI = frag.m_accTableFragPtrI[ent.m_fragBit];
+ const Uint32* const buf32 = static_cast<Uint32*>(pkData);
+ const Uint64* const buf64 = reinterpret_cast<const Uint64*>(buf32);
+ lockReq->hashValue = md5_hash(buf64, pkSize);
+ lockReq->tupAddr = getTupAddr(frag, ent);
+ lockReq->transId1 = scan.m_transId1;
+ lockReq->transId2 = scan.m_transId2;
+ // execute
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::LockSignalLength);
+ jamEntry();
+ switch (lockReq->returnCode) {
+ case AccLockReq::Success:
+ jam();
+ scan.m_state = ScanOp::Locked;
+ scan.m_accLockOp = lockReq->accOpPtr;
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Lock immediate scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ break;
+ case AccLockReq::IsBlocked:
+ jam();
+ // normal lock wait
+ scan.m_state = ScanOp::Blocked;
+ scan.m_lockwait = true;
+ scan.m_accLockOp = lockReq->accOpPtr;
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Lock wait scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ // LQH will wake us up
+ signal->theData[0] = scan.m_userPtr;
+ signal->theData[1] = true;
+ EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2);
+ jamEntry();
+ return; // stop
+ break;
+ case AccLockReq::Refused:
+ jam();
+ // we cannot see deleted tuple (assert only)
+ ndbassert(false);
+ // skip it
+ scan.m_state = ScanOp::Next;
+ signal->theData[0] = scan.m_userPtr;
+ signal->theData[1] = true;
+ EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2);
+ jamEntry();
+ return; // stop
+ break;
+ case AccLockReq::NoFreeOp:
+ jam();
+ // max ops should depend on max scans (assert only)
+ ndbassert(false);
+ // stay in Current state
+ scan.m_state = ScanOp::Current;
+ signal->theData[0] = scan.m_userPtr;
+ signal->theData[1] = true;
+ EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2);
+ jamEntry();
+ return; // stop
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ } else {
+ scan.m_state = ScanOp::Locked;
+ }
+ }
+ if (scan.m_state == ScanOp::Locked) {
+ // we have lock or do not need one
+ jam();
+ // read keys if not already done (uses signal)
+ const TreeEnt ent = scan.m_scanEnt;
+ // conf signal
+ NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = scan.m_userPtr;
+ // the lock is passed to LQH
+ Uint32 accLockOp = scan.m_accLockOp;
+ if (accLockOp != RNIL) {
+ scan.m_accLockOp = RNIL;
+ // remember it until LQH unlocks it
+ addAccLockOp(scan, accLockOp);
+ } else {
+ ndbrequire(scan.m_readCommitted);
+ // operation RNIL in LQH would signal no tuple returned
+ accLockOp = (Uint32)-1;
+ }
+ conf->accOperationPtr = accLockOp;
+ conf->fragId = frag.m_fragId | ent.m_fragBit;
+ conf->localKey[0] = getTupAddr(frag, ent);
+ conf->localKey[1] = 0;
+ conf->localKeyLength = 1;
+ unsigned signalLength = 6;
+ // add key info
+ if (! scan.m_readCommitted) {
+ sendSignal(scan.m_userRef, GSN_NEXT_SCANCONF,
+ signal, signalLength, JBB);
+ } else {
+ Uint32 blockNo = refToBlock(scan.m_userRef);
+ EXECUTE_DIRECT(blockNo, GSN_NEXT_SCANCONF, signal, signalLength);
+ }
+ // next time look for next entry
+ scan.m_state = ScanOp::Next;
+ return;
+ }
+ // XXX in ACC this is checked before req->checkLcpStop
+ if (scan.m_state == ScanOp::Last ||
+ scan.m_state == ScanOp::Invalid) {
+ jam();
+ NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = scan.m_userPtr;
+ conf->accOperationPtr = RNIL;
+ conf->fragId = RNIL;
+ unsigned signalLength = 3;
+ sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF,
+ signal, signalLength, JBB);
+ return;
+ }
+ ndbrequire(false);
+}
+
+/*
+ * Lock succeeded (after delay) in ACC. If the lock is for current
+ * entry, set state to Locked. If the lock is for an entry we were
+ * moved away from, simply unlock it. Finally, if we are closing the
+ * scan, do nothing since we have already sent an abort request.
+ */
+void
+Dbtux::execACCKEYCONF(Signal* signal)
+{
+ jamEntry();
+ ScanOpPtr scanPtr;
+ scanPtr.i = signal->theData[0];
+ c_scanOpPool.getPtr(scanPtr);
+ ScanOp& scan = *scanPtr.p;
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Lock obtained scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ ndbrequire(scan.m_lockwait && scan.m_accLockOp != RNIL);
+ scan.m_lockwait = false;
+ if (scan.m_state == ScanOp::Blocked) {
+ // the lock wait was for current entry
+ jam();
+ scan.m_state = ScanOp::Locked;
+ // LQH has the ball
+ return;
+ }
+ if (scan.m_state != ScanOp::Aborting) {
+ // we were moved, release lock
+ jam();
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo = AccLockReq::Unlock;
+ lockReq->accOpPtr = scan.m_accLockOp;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength);
+ jamEntry();
+ ndbrequire(lockReq->returnCode == AccLockReq::Success);
+ scan.m_accLockOp = RNIL;
+ // LQH has the ball
+ return;
+ }
+ // lose the lock
+ scan.m_accLockOp = RNIL;
+ // continue at ACC_ABORTCONF
+}
+
+/*
+ * Lock failed (after delay) in ACC. Probably means somebody ahead of
+ * us in lock queue deleted the tuple.
+ */
+void
+Dbtux::execACCKEYREF(Signal* signal)
+{
+ jamEntry();
+ ScanOpPtr scanPtr;
+ scanPtr.i = signal->theData[0];
+ c_scanOpPool.getPtr(scanPtr);
+ ScanOp& scan = *scanPtr.p;
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Lock refused scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ ndbrequire(scan.m_lockwait && scan.m_accLockOp != RNIL);
+ scan.m_lockwait = false;
+ if (scan.m_state != ScanOp::Aborting) {
+ jam();
+ // release the operation
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo = AccLockReq::Abort;
+ lockReq->accOpPtr = scan.m_accLockOp;
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength);
+ jamEntry();
+ ndbrequire(lockReq->returnCode == AccLockReq::Success);
+ scan.m_accLockOp = RNIL;
+ // scan position should already have been moved (assert only)
+ if (scan.m_state == ScanOp::Blocked) {
+ jam();
+ ndbassert(false);
+ scan.m_state = ScanOp::Next;
+ }
+ // LQH has the ball
+ return;
+ }
+ // lose the lock
+ scan.m_accLockOp = RNIL;
+ // continue at ACC_ABORTCONF
+}
+
+/*
+ * Received when scan is closing. This signal arrives after any
+ * ACCKEYCON or ACCKEYREF which may have been in job buffer.
+ */
+void
+Dbtux::execACC_ABORTCONF(Signal* signal)
+{
+ jamEntry();
+ ScanOpPtr scanPtr;
+ scanPtr.i = signal->theData[0];
+ c_scanOpPool.getPtr(scanPtr);
+ ScanOp& scan = *scanPtr.p;
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "ACC_ABORTCONF scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ ndbrequire(scan.m_state == ScanOp::Aborting);
+ // most likely we are still in lock wait
+ if (scan.m_lockwait) {
+ jam();
+ scan.m_lockwait = false;
+ scan.m_accLockOp = RNIL;
+ }
+ scanClose(signal, scanPtr);
+}
+
+/*
+ * Find start position for single range scan. If it exists, sets state
+ * to Next and links the scan to the node. The first entry is returned
+ * by scanNext.
+ */
+void
+Dbtux::scanFirst(ScanOpPtr scanPtr)
+{
+ ScanOp& scan = *scanPtr.p;
+ Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
+ TreeHead& tree = frag.m_tree;
+ // set up index keys for this operation
+ setKeyAttrs(frag);
+ // scan direction 0, 1
+ const unsigned idir = scan.m_descending;
+ // unpack start key into c_dataBuffer
+ const ScanBound& bound = *scan.m_bound[idir];
+ ScanBoundIterator iter;
+ bound.first(iter);
+ for (unsigned j = 0; j < bound.getSize(); j++) {
+ jam();
+ c_dataBuffer[j] = *iter.data;
+ bound.next(iter);
+ }
+ TreePos treePos;
+ searchToScan(frag, c_dataBuffer, scan.m_boundCnt[idir], scan.m_descending, treePos);
+ if (treePos.m_loc == NullTupLoc) {
+ // empty result set
+ jam();
+ scan.m_state = ScanOp::Last;
+ return;
+ }
+ // set position and state
+ scan.m_scanPos = treePos;
+ scan.m_state = ScanOp::Next;
+ // link the scan to node found
+ NodeHandle node(frag);
+ selectNode(node, treePos.m_loc);
+ linkScan(node, scanPtr);
+}
+
+/*
+ * Move to next entry. The scan is already linked to some node. When
+ * we leave, if an entry was found, it will be linked to a possibly
+ * different node. The scan has a position, and a direction which tells
+ * from where we came to this position. This is one of (all comments
+ * are in terms of ascending scan):
+ *
+ * 0 - up from left child (scan this node next)
+ * 1 - up from right child (proceed to parent)
+ * 2 - up from root (the scan ends)
+ * 3 - left to right within node (at end proceed to right child)
+ * 4 - down from parent (proceed to left child)
+ *
+ * If an entry was found, scan direction is 3. Therefore tree
+ * re-organizations need not worry about scan direction.
+ */
+void
+Dbtux::scanNext(ScanOpPtr scanPtr, bool fromMaintReq)
+{
+ ScanOp& scan = *scanPtr.p;
+ Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Next in scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+ // cannot be moved away from tuple we have locked
+ ndbrequire(scan.m_state != ScanOp::Locked);
+ // set up index keys for this operation
+ setKeyAttrs(frag);
+ // scan direction
+ const unsigned idir = scan.m_descending; // 0, 1
+ const int jdir = 1 - 2 * (int)idir; // 1, -1
+ // unpack end key into c_dataBuffer
+ const ScanBound& bound = *scan.m_bound[1 - idir];
+ ScanBoundIterator iter;
+ bound.first(iter);
+ for (unsigned j = 0; j < bound.getSize(); j++) {
+ jam();
+ c_dataBuffer[j] = *iter.data;
+ bound.next(iter);
+ }
+ // use copy of position
+ TreePos pos = scan.m_scanPos;
+ // get and remember original node
+ NodeHandle origNode(frag);
+ selectNode(origNode, pos.m_loc);
+ ndbrequire(islinkScan(origNode, scanPtr));
+ // current node in loop
+ NodeHandle node = origNode;
+ // copy of entry found
+ TreeEnt ent;
+ while (true) {
+ jam();
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Scan next pos " << pos << " " << node << endl;
+ }
+#endif
+ if (pos.m_dir == 2) {
+ // coming up from root ends the scan
+ jam();
+ pos.m_loc = NullTupLoc;
+ scan.m_state = ScanOp::Last;
+ break;
+ }
+ if (node.m_loc != pos.m_loc) {
+ jam();
+ selectNode(node, pos.m_loc);
+ }
+ if (pos.m_dir == 4) {
+ // coming down from parent proceed to left child
+ jam();
+ TupLoc loc = node.getLink(idir);
+ if (loc != NullTupLoc) {
+ jam();
+ pos.m_loc = loc;
+ pos.m_dir = 4; // unchanged
+ continue;
+ }
+ // pretend we came from left child
+ pos.m_dir = idir;
+ }
+ const unsigned occup = node.getOccup();
+ if (occup == 0) {
+ jam();
+ ndbrequire(fromMaintReq);
+ // move back to parent - see comment in treeRemoveInner
+ pos.m_loc = node.getLink(2);
+ pos.m_dir = node.getSide();
+ continue;
+ }
+ if (pos.m_dir == idir) {
+ // coming up from left child scan current node
+ jam();
+ pos.m_pos = idir == 0 ? 0 : occup - 1;
+ pos.m_match = false;
+ pos.m_dir = 3;
+ }
+ if (pos.m_dir == 3) {
+ // within node
+ jam();
+ // advance position
+ if (! pos.m_match)
+ pos.m_match = true;
+ else
+ // becomes ZNIL (which is > occup) if 0 and scan descending
+ pos.m_pos += jdir;
+ if (pos.m_pos < occup) {
+ jam();
+ ent = node.getEnt(pos.m_pos);
+ pos.m_dir = 3; // unchanged
+ // read and compare all attributes
+ readKeyAttrs(frag, ent, 0, c_entryKey);
+ int ret = cmpScanBound(frag, 1 - idir, c_dataBuffer, scan.m_boundCnt[1 - idir], c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ if (jdir * ret < 0) {
+ jam();
+ // hit upper bound of single range scan
+ pos.m_loc = NullTupLoc;
+ scan.m_state = ScanOp::Last;
+ break;
+ }
+ // can we see it
+ if (! scanVisible(scanPtr, ent)) {
+ jam();
+ continue;
+ }
+ // found entry
+ scan.m_state = ScanOp::Current;
+ break;
+ }
+ // after node proceed to right child
+ TupLoc loc = node.getLink(1 - idir);
+ if (loc != NullTupLoc) {
+ jam();
+ pos.m_loc = loc;
+ pos.m_dir = 4;
+ continue;
+ }
+ // pretend we came from right child
+ pos.m_dir = 1 - idir;
+ }
+ if (pos.m_dir == 1 - idir) {
+ // coming up from right child proceed to parent
+ jam();
+ pos.m_loc = node.getLink(2);
+ pos.m_dir = node.getSide();
+ continue;
+ }
+ ndbrequire(false);
+ }
+ // copy back position
+ scan.m_scanPos = pos;
+ // relink
+ if (scan.m_state == ScanOp::Current) {
+ ndbrequire(pos.m_match == true && pos.m_dir == 3);
+ ndbrequire(pos.m_loc == node.m_loc);
+ if (origNode.m_loc != node.m_loc) {
+ jam();
+ unlinkScan(origNode, scanPtr);
+ linkScan(node, scanPtr);
+ }
+ // copy found entry
+ scan.m_scanEnt = ent;
+ } else if (scan.m_state == ScanOp::Last) {
+ jam();
+ ndbrequire(pos.m_loc == NullTupLoc);
+ unlinkScan(origNode, scanPtr);
+ } else {
+ ndbrequire(false);
+ }
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Next out scan " << scanPtr.i << " " << scan << endl;
+ }
+#endif
+}
+
+/*
+ * Check if an entry is visible to the scan.
+ *
+ * There is a special check to never accept same tuple twice in a row.
+ * This is faster than asking TUP. It also fixes some special cases
+ * which are not analyzed or handled yet.
+ */
+bool
+Dbtux::scanVisible(ScanOpPtr scanPtr, TreeEnt ent)
+{
+ const ScanOp& scan = *scanPtr.p;
+ const Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
+ Uint32 fragBit = ent.m_fragBit;
+ Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[fragBit];
+ Uint32 fragId = frag.m_fragId | fragBit;
+ Uint32 tupAddr = getTupAddr(frag, ent);
+ Uint32 tupVersion = ent.m_tupVersion;
+ // check for same tuple twice in row
+ if (scan.m_scanEnt.m_tupLoc == ent.m_tupLoc &&
+ scan.m_scanEnt.m_fragBit == fragBit) {
+ jam();
+ return false;
+ }
+ Uint32 transId1 = scan.m_transId1;
+ Uint32 transId2 = scan.m_transId2;
+ Uint32 savePointId = scan.m_savePointId;
+ bool ret = c_tup->tuxQueryTh(tableFragPtrI, tupAddr, tupVersion, transId1, transId2, savePointId);
+ jamEntry();
+ return ret;
+}
+
+/*
+ * Finish closing of scan and send conf. Any lock wait has been done
+ * already.
+ */
+void
+Dbtux::scanClose(Signal* signal, ScanOpPtr scanPtr)
+{
+ ScanOp& scan = *scanPtr.p;
+ ndbrequire(! scan.m_lockwait && scan.m_accLockOp == RNIL);
+ // unlock all not unlocked by LQH
+ for (unsigned i = 0; i < scan.m_maxAccLockOps; i++) {
+ if (scan.m_accLockOps[i] != RNIL) {
+ jam();
+ AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend();
+ lockReq->returnCode = RNIL;
+ lockReq->requestInfo = AccLockReq::Abort;
+ lockReq->accOpPtr = scan.m_accLockOps[i];
+ EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength);
+ jamEntry();
+ ndbrequire(lockReq->returnCode == AccLockReq::Success);
+ scan.m_accLockOps[i] = RNIL;
+ }
+ }
+ // send conf
+ NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend();
+ conf->scanPtr = scanPtr.p->m_userPtr;
+ conf->accOperationPtr = RNIL;
+ conf->fragId = RNIL;
+ unsigned signalLength = 3;
+ sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF,
+ signal, signalLength, JBB);
+ releaseScanOp(scanPtr);
+}
+
+void
+Dbtux::addAccLockOp(ScanOp& scan, Uint32 accLockOp)
+{
+ ndbrequire(accLockOp != RNIL);
+ Uint32* list = scan.m_accLockOps;
+ bool ok = false;
+ for (unsigned i = 0; i < scan.m_maxAccLockOps; i++) {
+ ndbrequire(list[i] != accLockOp);
+ if (! ok && list[i] == RNIL) {
+ list[i] = accLockOp;
+ ok = true;
+ // continue check for duplicates
+ }
+ }
+ if (! ok) {
+ unsigned i = scan.m_maxAccLockOps;
+ if (i < MaxAccLockOps) {
+ list[i] = accLockOp;
+ ok = true;
+ scan.m_maxAccLockOps = i + 1;
+ }
+ }
+ ndbrequire(ok);
+}
+
+void
+Dbtux::removeAccLockOp(ScanOp& scan, Uint32 accLockOp)
+{
+ ndbrequire(accLockOp != RNIL);
+ Uint32* list = scan.m_accLockOps;
+ bool ok = false;
+ for (unsigned i = 0; i < scan.m_maxAccLockOps; i++) {
+ if (list[i] == accLockOp) {
+ list[i] = RNIL;
+ ok = true;
+ break;
+ }
+ }
+ ndbrequire(ok);
+}
+
+/*
+ * Release allocated records.
+ */
+void
+Dbtux::releaseScanOp(ScanOpPtr& scanPtr)
+{
+#ifdef VM_TRACE
+ if (debugFlags & DebugScan) {
+ debugOut << "Release scan " << scanPtr.i << " " << *scanPtr.p << endl;
+ }
+#endif
+ Frag& frag = *c_fragPool.getPtr(scanPtr.p->m_fragPtrI);
+ scanPtr.p->m_boundMin.release();
+ scanPtr.p->m_boundMax.release();
+ // unlink from per-fragment list and release from pool
+ frag.m_scanList.release(scanPtr);
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxSearch.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxSearch.cpp
new file mode 100644
index 00000000000..b0e2a664bfd
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxSearch.cpp
@@ -0,0 +1,449 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_SEARCH_CPP
+#include "Dbtux.hpp"
+
+/*
+ * Search for entry to add.
+ *
+ * Similar to searchToRemove (see below).
+ *
+ * TODO optimize for initial equal attrs in node min/max
+ */
+void
+Dbtux::searchToAdd(Frag& frag, ConstData searchKey, TreeEnt searchEnt, TreePos& treePos)
+{
+ const TreeHead& tree = frag.m_tree;
+ const unsigned numAttrs = frag.m_numAttrs;
+ NodeHandle currNode(frag);
+ currNode.m_loc = tree.m_root;
+ // assume success
+ treePos.m_match = false;
+ if (currNode.m_loc == NullTupLoc) {
+ // empty tree
+ jam();
+ return;
+ }
+ NodeHandle glbNode(frag); // potential g.l.b of final node
+ /*
+ * In order to not (yet) change old behaviour, a position between
+ * 2 nodes returns the one at the bottom of the tree.
+ */
+ NodeHandle bottomNode(frag);
+ while (true) {
+ jam();
+ selectNode(currNode, currNode.m_loc);
+ int ret;
+ // compare prefix
+ unsigned start = 0;
+ ret = cmpSearchKey(frag, start, searchKey, currNode.getPref(), tree.m_prefSize);
+ if (ret == NdbSqlUtil::CmpUnknown) {
+ jam();
+ // read and compare remaining attributes
+ ndbrequire(start < numAttrs);
+ readKeyAttrs(frag, currNode.getMinMax(0), start, c_entryKey);
+ ret = cmpSearchKey(frag, start, searchKey, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ }
+ if (ret == 0) {
+ jam();
+ // keys are equal, compare entry values
+ ret = searchEnt.cmp(currNode.getMinMax(0));
+ }
+ if (ret < 0) {
+ jam();
+ const TupLoc loc = currNode.getLink(0);
+ if (loc != NullTupLoc) {
+ jam();
+ // continue to left subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ if (! glbNode.isNull()) {
+ jam();
+ // move up to the g.l.b but remember the bottom node
+ bottomNode = currNode;
+ currNode = glbNode;
+ }
+ } else if (ret > 0) {
+ jam();
+ const TupLoc loc = currNode.getLink(1);
+ if (loc != NullTupLoc) {
+ jam();
+ // save potential g.l.b
+ glbNode = currNode;
+ // continue to right subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ } else {
+ jam();
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = 0;
+ // failed
+ treePos.m_match = true;
+ return;
+ }
+ break;
+ }
+ // anticipate
+ treePos.m_loc = currNode.m_loc;
+ // binary search
+ int lo = -1;
+ unsigned hi = currNode.getOccup();
+ int ret;
+ while (1) {
+ jam();
+ // hi - lo > 1 implies lo < j < hi
+ int j = (hi + lo) / 2;
+ // read and compare attributes
+ unsigned start = 0;
+ readKeyAttrs(frag, currNode.getEnt(j), start, c_entryKey);
+ ret = cmpSearchKey(frag, start, searchKey, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ if (ret == 0) {
+ jam();
+ // keys are equal, compare entry values
+ ret = searchEnt.cmp(currNode.getEnt(j));
+ }
+ if (ret < 0)
+ hi = j;
+ else if (ret > 0)
+ lo = j;
+ else {
+ treePos.m_pos = j;
+ // failed
+ treePos.m_match = true;
+ return;
+ }
+ if (hi - lo == 1)
+ break;
+ }
+ if (ret < 0) {
+ jam();
+ treePos.m_pos = hi;
+ return;
+ }
+ if (hi < currNode.getOccup()) {
+ jam();
+ treePos.m_pos = hi;
+ return;
+ }
+ if (bottomNode.isNull()) {
+ jam();
+ treePos.m_pos = hi;
+ return;
+ }
+ jam();
+ // backwards compatible for now
+ treePos.m_loc = bottomNode.m_loc;
+ treePos.m_pos = 0;
+}
+
+/*
+ * Search for entry to remove.
+ *
+ * Compares search key to each node min. A move to right subtree can
+ * overshoot target node. The last such node is saved. The final node
+ * is a semi-leaf or leaf. If search key is less than final node min
+ * then the saved node is the g.l.b of the final node and we move back
+ * to it.
+ */
+void
+Dbtux::searchToRemove(Frag& frag, ConstData searchKey, TreeEnt searchEnt, TreePos& treePos)
+{
+ const TreeHead& tree = frag.m_tree;
+ const unsigned numAttrs = frag.m_numAttrs;
+ NodeHandle currNode(frag);
+ currNode.m_loc = tree.m_root;
+ // assume success
+ treePos.m_match = true;
+ if (currNode.m_loc == NullTupLoc) {
+ // empty tree
+ jam();
+ // failed
+ treePos.m_match = false;
+ return;
+ }
+ NodeHandle glbNode(frag); // potential g.l.b of final node
+ while (true) {
+ jam();
+ selectNode(currNode, currNode.m_loc);
+ int ret;
+ // compare prefix
+ unsigned start = 0;
+ ret = cmpSearchKey(frag, start, searchKey, currNode.getPref(), tree.m_prefSize);
+ if (ret == NdbSqlUtil::CmpUnknown) {
+ jam();
+ // read and compare remaining attributes
+ ndbrequire(start < numAttrs);
+ readKeyAttrs(frag, currNode.getMinMax(0), start, c_entryKey);
+ ret = cmpSearchKey(frag, start, searchKey, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ }
+ if (ret == 0) {
+ jam();
+ // keys are equal, compare entry values
+ ret = searchEnt.cmp(currNode.getMinMax(0));
+ }
+ if (ret < 0) {
+ jam();
+ const TupLoc loc = currNode.getLink(0);
+ if (loc != NullTupLoc) {
+ jam();
+ // continue to left subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ if (! glbNode.isNull()) {
+ jam();
+ // move up to the g.l.b
+ currNode = glbNode;
+ }
+ } else if (ret > 0) {
+ jam();
+ const TupLoc loc = currNode.getLink(1);
+ if (loc != NullTupLoc) {
+ jam();
+ // save potential g.l.b
+ glbNode = currNode;
+ // continue to right subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ } else {
+ jam();
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = 0;
+ return;
+ }
+ break;
+ }
+ // anticipate
+ treePos.m_loc = currNode.m_loc;
+ // pos 0 was handled above
+ for (unsigned j = 1, occup = currNode.getOccup(); j < occup; j++) {
+ jam();
+ // compare only the entry
+ if (searchEnt.eq(currNode.getEnt(j))) {
+ jam();
+ treePos.m_pos = j;
+ return;
+ }
+ }
+ treePos.m_pos = currNode.getOccup();
+ // failed
+ treePos.m_match = false;
+}
+
+/*
+ * Search for scan start position.
+ *
+ * Similar to searchToAdd. The routines differ somewhat depending on
+ * scan direction and are done by separate methods.
+ */
+void
+Dbtux::searchToScan(Frag& frag, ConstData boundInfo, unsigned boundCount, bool descending, TreePos& treePos)
+{
+ const TreeHead& tree = frag.m_tree;
+ if (tree.m_root != NullTupLoc) {
+ if (! descending)
+ searchToScanAscending(frag, boundInfo, boundCount, treePos);
+ else
+ searchToScanDescending(frag, boundInfo, boundCount, treePos);
+ return;
+ }
+ // empty tree
+}
+
+void
+Dbtux::searchToScanAscending(Frag& frag, ConstData boundInfo, unsigned boundCount, TreePos& treePos)
+{
+ const TreeHead& tree = frag.m_tree;
+ NodeHandle currNode(frag);
+ currNode.m_loc = tree.m_root;
+ NodeHandle glbNode(frag); // potential g.l.b of final node
+ NodeHandle bottomNode(frag);
+ // always before entry
+ treePos.m_match = false;
+ while (true) {
+ jam();
+ selectNode(currNode, currNode.m_loc);
+ int ret;
+ // compare prefix
+ ret = cmpScanBound(frag, 0, boundInfo, boundCount, currNode.getPref(), tree.m_prefSize);
+ if (ret == NdbSqlUtil::CmpUnknown) {
+ jam();
+ // read and compare all attributes
+ readKeyAttrs(frag, currNode.getMinMax(0), 0, c_entryKey);
+ ret = cmpScanBound(frag, 0, boundInfo, boundCount, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ }
+ if (ret < 0) {
+ // bound is left of this node
+ jam();
+ const TupLoc loc = currNode.getLink(0);
+ if (loc != NullTupLoc) {
+ jam();
+ // continue to left subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ if (! glbNode.isNull()) {
+ jam();
+ // move up to the g.l.b but remember the bottom node
+ bottomNode = currNode;
+ currNode = glbNode;
+ } else {
+ // start scanning this node
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = 0;
+ treePos.m_dir = 3;
+ return;
+ }
+ } else if (ret > 0) {
+ // bound is at or right of this node
+ jam();
+ const TupLoc loc = currNode.getLink(1);
+ if (loc != NullTupLoc) {
+ jam();
+ // save potential g.l.b
+ glbNode = currNode;
+ // continue to right subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ } else {
+ ndbrequire(false);
+ }
+ break;
+ }
+ for (unsigned j = 0, occup = currNode.getOccup(); j < occup; j++) {
+ jam();
+ int ret;
+ // read and compare attributes
+ readKeyAttrs(frag, currNode.getEnt(j), 0, c_entryKey);
+ ret = cmpScanBound(frag, 0, boundInfo, boundCount, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ if (ret < 0) {
+ // found first entry satisfying the bound
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = j;
+ treePos.m_dir = 3;
+ return;
+ }
+ }
+ // bound is to right of this node
+ if (! bottomNode.isNull()) {
+ jam();
+ // start scanning the l.u.b
+ treePos.m_loc = bottomNode.m_loc;
+ treePos.m_pos = 0;
+ treePos.m_dir = 3;
+ return;
+ }
+ // start scanning upwards (pretend we came from right child)
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_dir = 1;
+}
+
+void
+Dbtux::searchToScanDescending(Frag& frag, ConstData boundInfo, unsigned boundCount, TreePos& treePos)
+{
+ const TreeHead& tree = frag.m_tree;
+ NodeHandle currNode(frag);
+ currNode.m_loc = tree.m_root;
+ NodeHandle glbNode(frag); // potential g.l.b of final node
+ NodeHandle bottomNode(frag);
+ // always before entry
+ treePos.m_match = false;
+ while (true) {
+ jam();
+ selectNode(currNode, currNode.m_loc);
+ int ret;
+ // compare prefix
+ ret = cmpScanBound(frag, 1, boundInfo, boundCount, currNode.getPref(), tree.m_prefSize);
+ if (ret == NdbSqlUtil::CmpUnknown) {
+ jam();
+ // read and compare all attributes
+ readKeyAttrs(frag, currNode.getMinMax(0), 0, c_entryKey);
+ ret = cmpScanBound(frag, 1, boundInfo, boundCount, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ }
+ if (ret < 0) {
+ // bound is left of this node
+ jam();
+ const TupLoc loc = currNode.getLink(0);
+ if (loc != NullTupLoc) {
+ jam();
+ // continue to left subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ if (! glbNode.isNull()) {
+ jam();
+ // move up to the g.l.b but remember the bottom node
+ bottomNode = currNode;
+ currNode = glbNode;
+ } else {
+ // empty result set
+ return;
+ }
+ } else if (ret > 0) {
+ // bound is at or right of this node
+ jam();
+ const TupLoc loc = currNode.getLink(1);
+ if (loc != NullTupLoc) {
+ jam();
+ // save potential g.l.b
+ glbNode = currNode;
+ // continue to right subtree
+ currNode.m_loc = loc;
+ continue;
+ }
+ } else {
+ ndbrequire(false);
+ }
+ break;
+ }
+ for (unsigned j = 0, occup = currNode.getOccup(); j < occup; j++) {
+ jam();
+ int ret;
+ // read and compare attributes
+ readKeyAttrs(frag, currNode.getEnt(j), 0, c_entryKey);
+ ret = cmpScanBound(frag, 1, boundInfo, boundCount, c_entryKey);
+ ndbrequire(ret != NdbSqlUtil::CmpUnknown);
+ if (ret < 0) {
+ if (j > 0) {
+ // start scanning from previous entry
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = j - 1;
+ treePos.m_dir = 3;
+ return;
+ }
+ // start scanning upwards (pretend we came from left child)
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = 0;
+ treePos.m_dir = 0;
+ return;
+ }
+ }
+ // start scanning this node
+ treePos.m_loc = currNode.m_loc;
+ treePos.m_pos = currNode.getOccup() - 1;
+ treePos.m_dir = 3;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp
new file mode 100644
index 00000000000..5107a8d8e31
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp
@@ -0,0 +1,709 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define DBTUX_TREE_CPP
+#include "Dbtux.hpp"
+
+/*
+ * Add entry. Handle the case when there is room for one more. This
+ * is the common case given slack in nodes.
+ */
+void
+Dbtux::treeAdd(Frag& frag, TreePos treePos, TreeEnt ent)
+{
+ TreeHead& tree = frag.m_tree;
+ NodeHandle node(frag);
+ if (treePos.m_loc != NullTupLoc) {
+ // non-empty tree
+ jam();
+ selectNode(node, treePos.m_loc);
+ unsigned pos = treePos.m_pos;
+ if (node.getOccup() < tree.m_maxOccup) {
+ // node has room
+ jam();
+ nodePushUp(node, pos, ent, RNIL);
+ return;
+ }
+ treeAddFull(frag, node, pos, ent);
+ return;
+ }
+ jam();
+ insertNode(node);
+ nodePushUp(node, 0, ent, RNIL);
+ node.setSide(2);
+ tree.m_root = node.m_loc;
+}
+
+/*
+ * Add entry when node is full. Handle the case when there is g.l.b
+ * node in left subtree with room for one more. It will receive the min
+ * entry of this node. The min entry could be the entry to add.
+ */
+void
+Dbtux::treeAddFull(Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent)
+{
+ TreeHead& tree = frag.m_tree;
+ TupLoc loc = lubNode.getLink(0);
+ if (loc != NullTupLoc) {
+ // find g.l.b node
+ NodeHandle glbNode(frag);
+ do {
+ jam();
+ selectNode(glbNode, loc);
+ loc = glbNode.getLink(1);
+ } while (loc != NullTupLoc);
+ if (glbNode.getOccup() < tree.m_maxOccup) {
+ // g.l.b node has room
+ jam();
+ Uint32 scanList = RNIL;
+ if (pos != 0) {
+ jam();
+ // add the new entry and return min entry
+ nodePushDown(lubNode, pos - 1, ent, scanList);
+ }
+ // g.l.b node receives min entry from l.u.b node
+ nodePushUp(glbNode, glbNode.getOccup(), ent, scanList);
+ return;
+ }
+ treeAddNode(frag, lubNode, pos, ent, glbNode, 1);
+ return;
+ }
+ treeAddNode(frag, lubNode, pos, ent, lubNode, 0);
+}
+
+/*
+ * Add entry when there is no g.l.b node in left subtree or the g.l.b
+ * node is full. We must add a new left or right child node which
+ * becomes the new g.l.b node.
+ */
+void
+Dbtux::treeAddNode(Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent, NodeHandle parentNode, unsigned i)
+{
+ NodeHandle glbNode(frag);
+ insertNode(glbNode);
+ // connect parent and child
+ parentNode.setLink(i, glbNode.m_loc);
+ glbNode.setLink(2, parentNode.m_loc);
+ glbNode.setSide(i);
+ Uint32 scanList = RNIL;
+ if (pos != 0) {
+ jam();
+ // add the new entry and return min entry
+ nodePushDown(lubNode, pos - 1, ent, scanList);
+ }
+ // g.l.b node receives min entry from l.u.b node
+ nodePushUp(glbNode, 0, ent, scanList);
+ // re-balance the tree
+ treeAddRebalance(frag, parentNode, i);
+}
+
+/*
+ * Re-balance tree after adding a node. The process starts with the
+ * parent of the added node.
+ */
+void
+Dbtux::treeAddRebalance(Frag& frag, NodeHandle node, unsigned i)
+{
+ while (true) {
+ // height of subtree i has increased by 1
+ int j = (i == 0 ? -1 : +1);
+ int b = node.getBalance();
+ if (b == 0) {
+ // perfectly balanced
+ jam();
+ node.setBalance(j);
+ // height change propagates up
+ } else if (b == -j) {
+ // height of shorter subtree increased
+ jam();
+ node.setBalance(0);
+ // height of tree did not change - done
+ break;
+ } else if (b == j) {
+ // height of longer subtree increased
+ jam();
+ NodeHandle childNode(frag);
+ selectNode(childNode, node.getLink(i));
+ int b2 = childNode.getBalance();
+ if (b2 == b) {
+ jam();
+ treeRotateSingle(frag, node, i);
+ } else if (b2 == -b) {
+ jam();
+ treeRotateDouble(frag, node, i);
+ } else {
+ // height of subtree increased so it cannot be perfectly balanced
+ ndbrequire(false);
+ }
+ // height of tree did not increase - done
+ break;
+ } else {
+ ndbrequire(false);
+ }
+ TupLoc parentLoc = node.getLink(2);
+ if (parentLoc == NullTupLoc) {
+ jam();
+ // root node - done
+ break;
+ }
+ i = node.getSide();
+ selectNode(node, parentLoc);
+ }
+}
+
+/*
+ * Remove entry. Optimize for nodes with slack. Handle the case when
+ * there is no underflow i.e. occupancy remains at least minOccup. For
+ * interior nodes this is a requirement. For others it means that we do
+ * not need to consider merge of semi-leaf and leaf.
+ */
+void
+Dbtux::treeRemove(Frag& frag, TreePos treePos)
+{
+ TreeHead& tree = frag.m_tree;
+ unsigned pos = treePos.m_pos;
+ NodeHandle node(frag);
+ selectNode(node, treePos.m_loc);
+ TreeEnt ent;
+ if (node.getOccup() > tree.m_minOccup) {
+ // no underflow in any node type
+ jam();
+ nodePopDown(node, pos, ent, 0);
+ return;
+ }
+ if (node.getChilds() == 2) {
+ // underflow in interior node
+ jam();
+ treeRemoveInner(frag, node, pos);
+ return;
+ }
+ // remove entry in semi/leaf
+ nodePopDown(node, pos, ent, 0);
+ if (node.getLink(0) != NullTupLoc) {
+ jam();
+ treeRemoveSemi(frag, node, 0);
+ return;
+ }
+ if (node.getLink(1) != NullTupLoc) {
+ jam();
+ treeRemoveSemi(frag, node, 1);
+ return;
+ }
+ treeRemoveLeaf(frag, node);
+}
+
+/*
+ * Remove entry when interior node underflows. There is g.l.b node in
+ * left subtree to borrow an entry from. The max entry of the g.l.b
+ * node becomes the min entry of this node.
+ */
+void
+Dbtux::treeRemoveInner(Frag& frag, NodeHandle lubNode, unsigned pos)
+{
+ TreeHead& tree = frag.m_tree;
+ TreeEnt ent;
+ // find g.l.b node
+ NodeHandle glbNode(frag);
+ TupLoc loc = lubNode.getLink(0);
+ do {
+ jam();
+ selectNode(glbNode, loc);
+ loc = glbNode.getLink(1);
+ } while (loc != NullTupLoc);
+ // borrow max entry from semi/leaf
+ Uint32 scanList = RNIL;
+ nodePopDown(glbNode, glbNode.getOccup() - 1, ent, &scanList);
+ // g.l.b may be empty now
+ // a descending scan may try to enter the empty g.l.b
+ // we prevent this in scanNext
+ nodePopUp(lubNode, pos, ent, scanList);
+ if (glbNode.getLink(0) != NullTupLoc) {
+ jam();
+ treeRemoveSemi(frag, glbNode, 0);
+ return;
+ }
+ treeRemoveLeaf(frag, glbNode);
+}
+
+/*
+ * Handle semi-leaf after removing an entry. Move entries from leaf to
+ * semi-leaf to bring semi-leaf occupancy above minOccup, if possible.
+ * The leaf may become empty.
+ */
+void
+Dbtux::treeRemoveSemi(Frag& frag, NodeHandle semiNode, unsigned i)
+{
+ TreeHead& tree = frag.m_tree;
+ ndbrequire(semiNode.getChilds() < 2);
+ TupLoc leafLoc = semiNode.getLink(i);
+ NodeHandle leafNode(frag);
+ selectNode(leafNode, leafLoc);
+ if (semiNode.getOccup() < tree.m_minOccup) {
+ jam();
+ unsigned cnt = min(leafNode.getOccup(), tree.m_minOccup - semiNode.getOccup());
+ nodeSlide(semiNode, leafNode, cnt, i);
+ if (leafNode.getOccup() == 0) {
+ // remove empty leaf
+ jam();
+ treeRemoveNode(frag, leafNode);
+ }
+ }
+}
+
+/*
+ * Handle leaf after removing an entry. If parent is semi-leaf, move
+ * entries to it as in the semi-leaf case. If parent is interior node,
+ * do nothing.
+ */
+void
+Dbtux::treeRemoveLeaf(Frag& frag, NodeHandle leafNode)
+{
+ TreeHead& tree = frag.m_tree;
+ TupLoc parentLoc = leafNode.getLink(2);
+ if (parentLoc != NullTupLoc) {
+ jam();
+ NodeHandle parentNode(frag);
+ selectNode(parentNode, parentLoc);
+ unsigned i = leafNode.getSide();
+ if (parentNode.getLink(1 - i) == NullTupLoc) {
+ // parent is semi-leaf
+ jam();
+ if (parentNode.getOccup() < tree.m_minOccup) {
+ jam();
+ unsigned cnt = min(leafNode.getOccup(), tree.m_minOccup - parentNode.getOccup());
+ nodeSlide(parentNode, leafNode, cnt, i);
+ }
+ }
+ }
+ if (leafNode.getOccup() == 0) {
+ jam();
+ // remove empty leaf
+ treeRemoveNode(frag, leafNode);
+ }
+}
+
+/*
+ * Remove empty leaf.
+ */
+void
+Dbtux::treeRemoveNode(Frag& frag, NodeHandle leafNode)
+{
+ TreeHead& tree = frag.m_tree;
+ ndbrequire(leafNode.getChilds() == 0);
+ TupLoc parentLoc = leafNode.getLink(2);
+ unsigned i = leafNode.getSide();
+ deleteNode(leafNode);
+ if (parentLoc != NullTupLoc) {
+ jam();
+ NodeHandle parentNode(frag);
+ selectNode(parentNode, parentLoc);
+ parentNode.setLink(i, NullTupLoc);
+ // re-balance the tree
+ treeRemoveRebalance(frag, parentNode, i);
+ return;
+ }
+ // tree is now empty
+ tree.m_root = NullTupLoc;
+}
+
+/*
+ * Re-balance tree after removing a node. The process starts with the
+ * parent of the removed node.
+ */
+void
+Dbtux::treeRemoveRebalance(Frag& frag, NodeHandle node, unsigned i)
+{
+ while (true) {
+ // height of subtree i has decreased by 1
+ int j = (i == 0 ? -1 : +1);
+ int b = node.getBalance();
+ if (b == 0) {
+ // perfectly balanced
+ jam();
+ node.setBalance(-j);
+ // height of tree did not change - done
+ return;
+ } else if (b == j) {
+ // height of longer subtree has decreased
+ jam();
+ node.setBalance(0);
+ // height change propagates up
+ } else if (b == -j) {
+ // height of shorter subtree has decreased
+ jam();
+ // child on the other side
+ NodeHandle childNode(frag);
+ selectNode(childNode, node.getLink(1 - i));
+ int b2 = childNode.getBalance();
+ if (b2 == b) {
+ jam();
+ treeRotateSingle(frag, node, 1 - i);
+ // height of tree decreased and propagates up
+ } else if (b2 == -b) {
+ jam();
+ treeRotateDouble(frag, node, 1 - i);
+ // height of tree decreased and propagates up
+ } else {
+ jam();
+ treeRotateSingle(frag, node, 1 - i);
+ // height of tree did not change - done
+ return;
+ }
+ } else {
+ ndbrequire(false);
+ }
+ TupLoc parentLoc = node.getLink(2);
+ if (parentLoc == NullTupLoc) {
+ jam();
+ // root node - done
+ return;
+ }
+ i = node.getSide();
+ selectNode(node, parentLoc);
+ }
+}
+
+/*
+ * Single rotation about node 5. One of LL (i=0) or RR (i=1).
+ *
+ * 0 0
+ * | |
+ * 5 ==> 3
+ * / \ / \
+ * 3 6 2 5
+ * / \ / / \
+ * 2 4 1 4 6
+ * /
+ * 1
+ *
+ * In this change 5,3 and 2 must always be there. 0, 1, 2, 4 and 6 are
+ * all optional. If 4 are there it changes side.
+*/
+void
+Dbtux::treeRotateSingle(Frag& frag, NodeHandle& node, unsigned i)
+{
+ ndbrequire(i <= 1);
+ /*
+ 5 is the old top node that have been unbalanced due to an insert or
+ delete. The balance is still the old balance before the update.
+ Verify that bal5 is 1 if RR rotate and -1 if LL rotate.
+ */
+ NodeHandle node5 = node;
+ const TupLoc loc5 = node5.m_loc;
+ const int bal5 = node5.getBalance();
+ const int side5 = node5.getSide();
+ ndbrequire(bal5 + (1 - i) == i);
+ /*
+ 3 is the new root of this part of the tree which is to swap place with
+ node 5. For an insert to cause this it must have the same balance as 5.
+ For deletes it can have the balance 0.
+ */
+ TupLoc loc3 = node5.getLink(i);
+ NodeHandle node3(frag);
+ selectNode(node3, loc3);
+ const int bal3 = node3.getBalance();
+ /*
+ 2 must always be there but is not changed. Thus we mereley check that it
+ exists.
+ */
+ ndbrequire(node3.getLink(i) != NullTupLoc);
+ /*
+ 4 is not necessarily there but if it is there it will move from one
+ side of 3 to the other side of 5. For LL it moves from the right side
+ to the left side and for RR it moves from the left side to the right
+ side. This means that it also changes parent from 3 to 5.
+ */
+ TupLoc loc4 = node3.getLink(1 - i);
+ NodeHandle node4(frag);
+ if (loc4 != NullTupLoc) {
+ jam();
+ selectNode(node4, loc4);
+ ndbrequire(node4.getSide() == (1 - i) &&
+ node4.getLink(2) == loc3);
+ node4.setSide(i);
+ node4.setLink(2, loc5);
+ }//if
+
+ /*
+ Retrieve the address of 5's parent before it is destroyed
+ */
+ TupLoc loc0 = node5.getLink(2);
+
+ /*
+ The next step is to perform the rotation. 3 will inherit 5's parent
+ and side. 5 will become a child of 3 on the right side for LL and on
+ the left side for RR.
+ 5 will get 3 as the parent. It will get 4 as a child and it will be
+ on the right side of 3 for LL and left side of 3 for RR.
+ The final step of the rotate is to check whether 5 originally had any
+ parent. If it had not then 3 is the new root node.
+ We will also verify some preconditions for the change to occur.
+ 1. 3 must have had 5 as parent before the change.
+ 2. 3's side is left for LL and right for RR before change.
+ */
+ ndbrequire(node3.getLink(2) == loc5);
+ ndbrequire(node3.getSide() == i);
+ node3.setLink(1 - i, loc5);
+ node3.setLink(2, loc0);
+ node3.setSide(side5);
+ node5.setLink(i, loc4);
+ node5.setLink(2, loc3);
+ node5.setSide(1 - i);
+ if (loc0 != NullTupLoc) {
+ jam();
+ NodeHandle node0(frag);
+ selectNode(node0, loc0);
+ node0.setLink(side5, loc3);
+ } else {
+ jam();
+ frag.m_tree.m_root = loc3;
+ }//if
+ /* The final step of the change is to update the balance of 3 and
+ 5 that changed places. There are two cases here. The first case is
+ when 3 unbalanced in the same direction by an insert or a delete.
+ In this case the changes will make the tree balanced again for both
+ 3 and 5.
+ The second case only occurs at deletes. In this case 3 starts out
+ balanced. In the figure above this could occur if 4 starts out with
+ a right node and the rotate is triggered by a delete of 6's only child.
+ In this case 5 will change balance but still be unbalanced and 3 will
+ be unbalanced in the opposite direction of 5.
+ */
+ if (bal3 == bal5) {
+ jam();
+ node3.setBalance(0);
+ node5.setBalance(0);
+ } else if (bal3 == 0) {
+ jam();
+ node3.setBalance(-bal5);
+ node5.setBalance(bal5);
+ } else {
+ ndbrequire(false);
+ }//if
+ /*
+ Set node to 3 as return parameter for enabling caller to continue
+ traversing the tree.
+ */
+ node = node3;
+}
+
+/*
+ * Double rotation about node 6. One of LR (i=0) or RL (i=1).
+ *
+ * 0 0
+ * | |
+ * 6 ==> 4
+ * / \ / \
+ * 2 7 2 6
+ * / \ / \ / \
+ * 1 4 1 3 5 7
+ * / \
+ * 3 5
+ *
+ * In this change 6, 2 and 4 must be there, all others are optional.
+ * We will start by proving a Lemma.
+ * Lemma:
+ * The height of the sub-trees 1 and 7 and the maximum height of the
+ * threes from 3 and 5 are all the same.
+ * Proof:
+ * maxheight(3,5) is defined as the maximum height of 3 and 5.
+ * If height(7) > maxheight(3,5) then the AVL condition is ok and we
+ * don't need to perform a rotation.
+ * If height(7) < maxheight(3,5) then the balance of 6 would be at least
+ * -3 which cannot happen in an AVL tree even before a rotation.
+ * Thus we conclude that height(7) == maxheight(3,5)
+ *
+ * The next step is to prove that the height of 1 is equal to maxheight(3,5).
+ * If height(1) - 1 > maxheight(3,5) then we would have
+ * balance in 6 equal to -3 at least which cannot happen in an AVL-tree.
+ * If height(1) - 1 = maxheight(3,5) then we should have solved the
+ * unbalance with a single rotate and not with a double rotate.
+ * If height(1) + 1 = maxheight(3,5) then we would be doing a rotate
+ * with node 2 as the root of the rotation.
+ * If height(1) + k = maxheight(3,5) where k >= 2 then the tree could not have
+ * been an AVL-tree before the insert or delete.
+ * Thus we conclude that height(1) = maxheight(3,5)
+ *
+ * Thus we conclude that height(1) = maxheight(3,5) = height(7).
+ *
+ * Observation:
+ * The balance of node 4 before the rotation can be any (-1, 0, +1).
+ *
+ * The following changes are needed:
+ * Node 6:
+ * 1) Changes parent from 0 -> 4
+ * 2) 1 - i link stays the same
+ * 3) i side link is derived from 1 - i side link from 4
+ * 4) Side is set to 1 - i
+ * 5) Balance change:
+ * If balance(4) == 0 then balance(6) = 0
+ * since height(3) = height(5) = maxheight(3,5) = height(7)
+ * If balance(4) == +1 then balance(6) = 0
+ * since height(5) = maxheight(3,5) = height(7)
+ * If balance(4) == -1 then balance(6) = 1
+ * since height(5) + 1 = maxheight(3,5) = height(7)
+ *
+ * Node 2:
+ * 1) Changes parent from 6 -> 4
+ * 2) i side link stays the same
+ * 3) 1 - i side link is derived from i side link of 4
+ * 4) Side is set to i (thus not changed)
+ * 5) Balance change:
+ * If balance(4) == 0 then balance(2) = 0
+ * since height(3) = height(5) = maxheight(3,5) = height(1)
+ * If balance(4) == -1 then balance(2) = 0
+ * since height(3) = maxheight(3,5) = height(1)
+ * If balance(4) == +1 then balance(6) = 1
+ * since height(3) + 1 = maxheight(3,5) = height(1)
+ *
+ * Node 4:
+ * 1) Inherits parent from 6
+ * 2) i side link is 2
+ * 3) 1 - i side link is 6
+ * 4) Side is inherited from 6
+ * 5) Balance(4) = 0 independent of previous balance
+ * Proof:
+ * If height(1) = 0 then only 2, 4 and 6 are involved and then it is
+ * trivially true.
+ * If height(1) >= 1 then we are sure that 1 and 7 exist with the same
+ * height and that if 3 and 5 exist they are of the same height as 1 and
+ * 7 and thus we know that 4 is balanced since newheight(2) = newheight(6).
+ *
+ * If Node 3 exists:
+ * 1) Change parent from 4 to 2
+ * 2) Change side from i to 1 - i
+ *
+ * If Node 5 exists:
+ * 1) Change parent from 4 to 6
+ * 2) Change side from 1 - i to i
+ *
+ * If Node 0 exists:
+ * 1) previous link to 6 is replaced by link to 4 on proper side
+ *
+ * Node 1 and 7 needs no changes at all.
+ *
+ * Some additional requires are that balance(2) = - balance(6) = -1/+1 since
+ * otherwise we would do a single rotate.
+ *
+ * The balance(6) is -1 if i == 0 and 1 if i == 1
+ *
+ */
+void
+Dbtux::treeRotateDouble(Frag& frag, NodeHandle& node, unsigned i)
+{
+ TreeHead& tree = frag.m_tree;
+
+ // old top node
+ NodeHandle node6 = node;
+ const TupLoc loc6 = node6.m_loc;
+ // the un-updated balance
+ const int bal6 = node6.getBalance();
+ const unsigned side6 = node6.getSide();
+
+ // level 1
+ TupLoc loc2 = node6.getLink(i);
+ NodeHandle node2(frag);
+ selectNode(node2, loc2);
+ const int bal2 = node2.getBalance();
+
+ // level 2
+ TupLoc loc4 = node2.getLink(1 - i);
+ NodeHandle node4(frag);
+ selectNode(node4, loc4);
+ const int bal4 = node4.getBalance();
+
+ ndbrequire(i <= 1);
+ ndbrequire(bal6 + (1 - i) == i);
+ ndbrequire(bal2 == -bal6);
+ ndbrequire(node2.getLink(2) == loc6);
+ ndbrequire(node2.getSide() == i);
+ ndbrequire(node4.getLink(2) == loc2);
+
+ // level 3
+ TupLoc loc3 = node4.getLink(i);
+ TupLoc loc5 = node4.getLink(1 - i);
+
+ // fill up leaf before it becomes internal
+ if (loc3 == NullTupLoc && loc5 == NullTupLoc) {
+ jam();
+ if (node4.getOccup() < tree.m_minOccup) {
+ jam();
+ unsigned cnt = tree.m_minOccup - node4.getOccup();
+ ndbrequire(cnt < node2.getOccup());
+ nodeSlide(node4, node2, cnt, i);
+ ndbrequire(node4.getOccup() >= tree.m_minOccup);
+ ndbrequire(node2.getOccup() != 0);
+ }
+ } else {
+ if (loc3 != NullTupLoc) {
+ jam();
+ NodeHandle node3(frag);
+ selectNode(node3, loc3);
+ node3.setLink(2, loc2);
+ node3.setSide(1 - i);
+ }
+ if (loc5 != NullTupLoc) {
+ jam();
+ NodeHandle node5(frag);
+ selectNode(node5, loc5);
+ node5.setLink(2, node6.m_loc);
+ node5.setSide(i);
+ }
+ }
+ // parent
+ TupLoc loc0 = node6.getLink(2);
+ NodeHandle node0(frag);
+ // perform the rotation
+ node6.setLink(i, loc5);
+ node6.setLink(2, loc4);
+ node6.setSide(1 - i);
+
+ node2.setLink(1 - i, loc3);
+ node2.setLink(2, loc4);
+
+ node4.setLink(i, loc2);
+ node4.setLink(1 - i, loc6);
+ node4.setLink(2, loc0);
+ node4.setSide(side6);
+
+ if (loc0 != NullTupLoc) {
+ jam();
+ selectNode(node0, loc0);
+ node0.setLink(side6, loc4);
+ } else {
+ jam();
+ frag.m_tree.m_root = loc4;
+ }
+ // set balance of changed nodes
+ node4.setBalance(0);
+ if (bal4 == 0) {
+ jam();
+ node2.setBalance(0);
+ node6.setBalance(0);
+ } else if (bal4 == -bal2) {
+ jam();
+ node2.setBalance(0);
+ node6.setBalance(bal2);
+ } else if (bal4 == bal2) {
+ jam();
+ node2.setBalance(-bal2);
+ node6.setBalance(0);
+ } else {
+ ndbrequire(false);
+ }
+ // new top node
+ node = node4;
+}
diff --git a/storage/ndb/src/kernel/blocks/dbtux/Makefile.am b/storage/ndb/src/kernel/blocks/dbtux/Makefile.am
new file mode 100644
index 00000000000..b5951e8ed37
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/Makefile.am
@@ -0,0 +1,34 @@
+noinst_LIBRARIES = libdbtux.a
+
+libdbtux_a_SOURCES = \
+ DbtuxGen.cpp \
+ DbtuxMeta.cpp \
+ DbtuxMaint.cpp \
+ DbtuxNode.cpp \
+ DbtuxTree.cpp \
+ DbtuxScan.cpp \
+ DbtuxSearch.cpp \
+ DbtuxCmp.cpp \
+ DbtuxDebug.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/kernel/blocks/dbtup
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbtux.dsp
+
+libdbtux.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbtux_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/dbtux/Times.txt b/storage/ndb/src/kernel/blocks/dbtux/Times.txt
new file mode 100644
index 00000000000..68120084846
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/Times.txt
@@ -0,0 +1,151 @@
+ordered index performance
+=========================
+
+"mc02" 2x1700 MHz linux-2.4.9 gcc-2.96 -O3 one db-node
+
+case a: maintenance: index on Unsigned
+testOIBasic -case u -table 1 -index 2 -fragtype small -threads 10 -rows 100000 -subloop 1 -nologging
+
+case b: maintenance: index on Varchar(5) + Varchar(5) + Varchar(20) + Unsigned
+testOIBasic -case u -table 2 -index 5 -fragtype small -threads 10 -rows 100000 -subloop 1 -nologging
+
+case c: full scan: index on PK Unsigned
+testOIBasic -case v -table 1 -index 1 -fragtype small -threads 10 -rows 100000 -subloop 1 -nologging
+
+case d: scan 1 tuple via EQ: index on PK Unsigned
+testOIBasic -case w -table 1 -index 1 -fragtype small -threads 10 -rows 100000 -samples 50000 -subloop 1 -nologging -v2
+
+a, b
+1 million rows, pk update without index, pk update with index
+shows ms / 1000 rows for each and pct overhead
+
+c
+1 million rows, index on PK, full table scan, full index scan
+shows ms / 1000 rows for each and index time overhead
+
+d
+1 million rows, index on PK, read table via each pk, scan index for each pk
+shows ms / 1000 rows for each and index time overhead
+samples 10% of all PKs (100,000 pk reads, 100,000 scans)
+
+the "pct" values are from more accurate total times (not shown)
+comments [ ... ] are after the case
+
+040616 mc02/a 40 ms 87 ms 114 pct
+ mc02/b 51 ms 128 ms 148 pct
+
+optim 1 mc02/a 38 ms 85 ms 124 pct
+ mc02/b 51 ms 123 ms 140 pct
+
+optim 2 mc02/a 41 ms 80 ms 96 pct
+ mc02/b 51 ms 117 ms 128 pct
+
+optim 3 mc02/a 43 ms 80 ms 85 pct
+ mc02/b 54 ms 118 ms 117 pct
+
+optim 4 mc02/a 42 ms 80 ms 87 pct
+ mc02/b 51 ms 119 ms 129 pct
+
+optim 5 mc02/a 43 ms 77 ms 77 pct
+ mc02/b 54 ms 118 ms 117 pct
+
+optim 6 mc02/a 42 ms 70 ms 66 pct
+ mc02/b 53 ms 109 ms 105 pct
+
+optim 7 mc02/a 42 ms 69 ms 61 pct
+ mc02/b 52 ms 106 ms 101 pct
+
+optim 8 mc02/a 42 ms 69 ms 62 pct
+ mc02/b 54 ms 104 ms 92 pct
+
+optim 9 mc02/a 43 ms 67 ms 54 pct
+ mc02/b 53 ms 102 ms 91 pct
+
+optim 10 mc02/a 44 ms 65 ms 46 pct
+ mc02/b 53 ms 88 ms 66 pct
+
+optim 11 mc02/a 43 ms 63 ms 46 pct
+ mc02/b 52 ms 86 ms 63 pct
+
+optim 12 mc02/a 38 ms 55 ms 43 pct
+ mc02/b 47 ms 77 ms 63 pct
+ mc02/c 10 ms 14 ms 47 pct
+ mc02/d 176 ms 281 ms 59 pct
+
+optim 13 mc02/a 40 ms 57 ms 42 pct
+ mc02/b 47 ms 77 ms 61 pct
+ mc02/c 9 ms 13 ms 50 pct
+ mc02/d 170 ms 256 ms 50 pct
+
+optim 13 mc02/a 39 ms 59 ms 50 pct
+ mc02/b 47 ms 77 ms 61 pct
+ mc02/c 9 ms 12 ms 44 pct
+ mc02/d 246 ms 289 ms 17 pct
+
+[ after wl-1884 store all-NULL keys (the tests have pctnull=10 per column) ]
+[ case d: bug in testOIBasic killed PK read performance ]
+
+optim 14 mc02/a 41 ms 60 ms 44 pct
+ mc02/b 46 ms 81 ms 73 pct
+ mc02/c 9 ms 13 ms 37 pct
+ mc02/d 242 ms 285 ms 17 pct
+
+[ case b: do long keys suffer from many subroutine calls? ]
+[ case d: bug in testOIBasic killed PK read performance ]
+
+none mc02/a 35 ms 60 ms 71 pct
+ mc02/b 42 ms 75 ms 76 pct
+ mc02/c 5 ms 12 ms 106 pct
+ mc02/d 165 ms 238 ms 44 pct
+
+[ johan re-installed mc02 as fedora gcc-3.3.2, tux uses more C++ stuff than tup]
+
+charsets mc02/a 35 ms 60 ms 71 pct
+ mc02/b 42 ms 84 ms 97 pct
+ mc02/c 5 ms 12 ms 109 pct
+ mc02/d 190 ms 236 ms 23 pct
+
+[ case b: TUX can no longer use pointers to TUP data ]
+
+optim 15 mc02/a 34 ms 60 ms 72 pct
+ mc02/b 42 ms 85 ms 100 pct
+ mc02/c 5 ms 12 ms 110 pct
+ mc02/d 178 ms 242 ms 35 pct
+
+[ corrected wasted space in index node ]
+
+optim 16 mc02/a 34 ms 53 ms 53 pct
+ mc02/b 42 ms 75 ms 75 pct
+
+[ binary search of bounding node when adding entry ]
+
+none mc02/a 35 ms 53 ms 51 pct
+ mc02/b 42 ms 75 ms 76 pct
+
+[ rewrote treeAdd / treeRemove ]
+
+optim 17 mc02/a 35 ms 52 ms 49 pct
+ mc02/b 43 ms 75 ms 75 pct
+
+[ allow slack (2) in interior nodes - almost no effect?? ]
+
+wl-1942 mc02/a 35 ms 52 ms 49 pct
+ mc02/b 42 ms 75 ms 76 pct
+
+before mc02/c 5 ms 13 ms 126 pct
+ mc02/d 134 ms 238 ms 78 pct
+
+after mc02/c 5 ms 10 ms 70 pct
+ mc02/d 178 ms 242 ms 69 pct
+
+[ prelim performance fix for max batch size 16 -> 992 ]
+
+wl-2066 mc02/c 5 ms 10 ms 87 pct
+before mc02/d 140 ms 237 ms 69 pct
+
+wl-2066 mc02/c 5 ms 10 ms 69 pct
+after mc02/d 150 ms 229 ms 52 pct
+
+[ wl-2066 = remove ACC storage, use TUX test to see effect ]
+
+vim: set et:
diff --git a/storage/ndb/src/kernel/blocks/dbtux/tuxstatus.html b/storage/ndb/src/kernel/blocks/dbtux/tuxstatus.html
new file mode 100644
index 00000000000..264809cefd3
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/tuxstatus.html
@@ -0,0 +1,120 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<TITLE>NDB Ordered Index Status</TITLE>
+</HEAD>
+<BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
+<p>
+<h2>NDB Ordered Index Status</h2>
+<p>
+<h3>Alpha release Jan 30, 2004</h3>
+<p>
+<ul>
+ <li>
+ Up to 32 index attributes of any type, possibly nullable.
+ <li>
+ Index build i.e. table need not be empty.
+ <li>
+ Logging NOT done, index rebuilt at system restart.
+ <li>
+ Single range scan with lower and upper bounds.
+ <li>
+ Scan with locking: read latest, read for update.
+ <li>
+ LIMITED number of parallel scans.
+ <li>
+ Total result set NOT in index key order.
+ <li>
+ NDB ODBC optimizer to use ordered index for equality but NOT for ranges.
+ <li>
+ MySQL optimizer to use ordered index for equality and ranges.
+</ul>
+<p>
+As an example, consider following index on integer attributes.
+<p>
+<tt>SQL&gt;create index X on T (A, B, C) nologging;</tt>
+<p>
+Single range scan means that bounds are set on
+an initial sequence of index keys, and all but last is an equality.
+<br>
+For example following scans are supported (the last 2 not via NDB ODBC).
+<p>
+<tt>SQL&gt;select * from T where A = 1;</tt>
+<br>
+<tt>SQL&gt;select * from T where A = 1 and B = 10 and C = 20;</tt>
+<br>
+<tt>SQL&gt;select * from T where A &lt; 10;</tt>
+<br>
+<tt>SQL&gt;select * from T where A = 1 and 10 &lt; B and B &lt; 20;</tt>
+<p>
+Following scans are NOT supported:
+<p>
+<tt>SQL&gt;select * from T where B = 1;</tt>
+<br>
+<tt>SQL&gt;select * from T where A &lt; 10 and B &lt; 20;</tt>
+<br>
+<h3>Features and dates</h3>
+[ Now = Jan 19 ]
+<p>
+<table border=1 cellpadding=1>
+<tr align="left">
+ <th width="40%">Feature</th>
+ <th width="15%">Now</th> <th width="15%">Jan 30</th> <th width="15%">Mar 01</th> <th width="15%">Never</th>
+</tr>
+<tr align=left>
+ <td>Index maintenance</td>
+ <td>X</td> <td>-</td> <td>-</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Basic scan</td>
+ <td>X 1)</td> <td>-</td> <td>-</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Scan bounds on nullable attributes</td>
+ <td>-</td> <td>X</td> <td>-</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Scan with locking</td>
+ <td>-</td> <td>X</td> <td>-</td> <td>-</td>
+</tr>
+<tr align="left">
+ <td>NDB ODBC equality bounds</td>
+ <td>-</td> <td>X</td> <td>-</td> <td>-</td>
+</tr>
+<tr align="left">
+ <td>MySQL integration</td>
+ <td>-</td> <td>X</td> <td>-</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Index build</td>
+ <td>2)</td> <td>X</td> <td>-</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Unlimited number of scans</td>
+ <td>3)</td> <td>-</td> <td>X</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Total ordering</td>
+ <td>-</td> <td>-</td> <td>X</td> <td>-</td>
+</tr>
+<tr align=left>
+ <td>Multiple range scan</td>
+ <td>-</td> <td>-</td> <td>X</td> <td>-</td>
+</tr>
+<tr align="left">
+ <td>NDB ODBC range bounds</td>
+ <td>-</td> <td>-</td> <td>-</td> <td>X</td>
+</tr>
+<tr align=left>
+ <td>Logging</td>
+ <td>-</td> <td>-</td> <td>-</td> <td>X</td>
+</tr>
+</table>
+<p>
+1) No locking and bounds must be on non-nullable key attributes.
+<br>
+2) Currently table must be empty when index is created.
+<br>
+3) Currently limited to 11 simultaneous per fragment.
+</BODY>
+</HTML>
diff --git a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
new file mode 100644
index 00000000000..b94bb8e6d7e
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
@@ -0,0 +1,2589 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "DbUtil.hpp"
+
+#include <ndb_version.h>
+
+#include <signaldata/WaitGCP.hpp>
+#include <signaldata/KeyInfo.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/TcKeyConf.hpp>
+#include <signaldata/TcKeyFailConf.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/DictTabInfo.hpp>
+
+#include <signaldata/UtilSequence.hpp>
+#include <signaldata/UtilPrepare.hpp>
+#include <signaldata/UtilRelease.hpp>
+#include <signaldata/UtilExecute.hpp>
+#include <signaldata/UtilLock.hpp>
+
+#include <SectionReader.hpp>
+#include <Interpreter.hpp>
+#include <AttributeHeader.hpp>
+
+#include <NdbTick.h>
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Startup
+ * ------------------------------------------------------------------------
+ *
+ * Constructors, startup, initializations
+ **************************************************************************/
+
+DbUtil::DbUtil(const Configuration & conf) :
+ SimulatedBlock(DBUTIL, conf),
+ c_runningPrepares(c_preparePool),
+ c_runningPreparedOperations(c_preparedOperationPool),
+ c_seizingTransactions(c_transactionPool),
+ c_runningTransactions(c_transactionPool),
+ c_lockQueues(c_lockQueuePool)
+{
+ BLOCK_CONSTRUCTOR(DbUtil);
+
+ // Add received signals
+ addRecSignal(GSN_STTOR, &DbUtil::execSTTOR);
+ addRecSignal(GSN_NDB_STTOR, &DbUtil::execNDB_STTOR);
+ addRecSignal(GSN_DUMP_STATE_ORD, &DbUtil::execDUMP_STATE_ORD);
+ addRecSignal(GSN_CONTINUEB, &DbUtil::execCONTINUEB);
+
+ //addRecSignal(GSN_TCSEIZEREF, &DbUtil::execTCSEIZEREF);
+ addRecSignal(GSN_TCSEIZECONF, &DbUtil::execTCSEIZECONF);
+ addRecSignal(GSN_TCKEYCONF, &DbUtil::execTCKEYCONF);
+ addRecSignal(GSN_TCKEYREF, &DbUtil::execTCKEYREF);
+ addRecSignal(GSN_TCROLLBACKREP, &DbUtil::execTCROLLBACKREP);
+
+ //addRecSignal(GSN_TCKEY_FAILCONF, &DbUtil::execTCKEY_FAILCONF);
+ //addRecSignal(GSN_TCKEY_FAILREF, &DbUtil::execTCKEY_FAILREF);
+ addRecSignal(GSN_TRANSID_AI, &DbUtil::execTRANSID_AI);
+
+ /**
+ * Sequence Service
+ */
+ addRecSignal(GSN_UTIL_SEQUENCE_REQ, &DbUtil::execUTIL_SEQUENCE_REQ);
+ // Debug
+ addRecSignal(GSN_UTIL_SEQUENCE_REF, &DbUtil::execUTIL_SEQUENCE_REF);
+ addRecSignal(GSN_UTIL_SEQUENCE_CONF, &DbUtil::execUTIL_SEQUENCE_CONF);
+
+ /**
+ * Locking
+ */
+ addRecSignal(GSN_UTIL_CREATE_LOCK_REQ, &DbUtil::execUTIL_CREATE_LOCK_REQ);
+ addRecSignal(GSN_UTIL_DESTROY_LOCK_REQ, &DbUtil::execUTIL_DESTORY_LOCK_REQ);
+ addRecSignal(GSN_UTIL_LOCK_REQ, &DbUtil::execUTIL_LOCK_REQ);
+ addRecSignal(GSN_UTIL_UNLOCK_REQ, &DbUtil::execUTIL_UNLOCK_REQ);
+
+ /**
+ * Backend towards Dict
+ */
+ addRecSignal(GSN_GET_TABINFOREF, &DbUtil::execGET_TABINFOREF);
+ addRecSignal(GSN_GET_TABINFO_CONF, &DbUtil::execGET_TABINFO_CONF);
+
+ /**
+ * Prepare / Execute / Release Services
+ */
+ addRecSignal(GSN_UTIL_PREPARE_REQ, &DbUtil::execUTIL_PREPARE_REQ);
+ addRecSignal(GSN_UTIL_PREPARE_CONF, &DbUtil::execUTIL_PREPARE_CONF);
+ addRecSignal(GSN_UTIL_PREPARE_REF, &DbUtil::execUTIL_PREPARE_REF);
+
+ addRecSignal(GSN_UTIL_EXECUTE_REQ, &DbUtil::execUTIL_EXECUTE_REQ);
+ addRecSignal(GSN_UTIL_EXECUTE_CONF, &DbUtil::execUTIL_EXECUTE_CONF);
+ addRecSignal(GSN_UTIL_EXECUTE_REF, &DbUtil::execUTIL_EXECUTE_REF);
+
+ addRecSignal(GSN_UTIL_RELEASE_REQ, &DbUtil::execUTIL_RELEASE_REQ);
+ addRecSignal(GSN_UTIL_RELEASE_CONF, &DbUtil::execUTIL_RELEASE_CONF);
+ addRecSignal(GSN_UTIL_RELEASE_REF, &DbUtil::execUTIL_RELEASE_REF);
+
+ c_pagePool.setSize(10);
+ c_preparePool.setSize(1); // one parallel prepare at a time
+ c_preparedOperationPool.setSize(5); // three hardcoded, two for test
+ c_operationPool.setSize(64); // 64 parallel operations
+ c_transactionPool.setSize(32); // 16 parallel transactions
+ c_attrMappingPool.setSize(100);
+ c_dataBufPool.setSize(6000); // 6000*11*4 = 264K > 8k+8k*16 = 256k
+ {
+ SLList<Prepare> tmp(c_preparePool);
+ PreparePtr ptr;
+ while(tmp.seize(ptr))
+ new (ptr.p) Prepare(c_pagePool);
+ tmp.release();
+ }
+ {
+ SLList<Operation> tmp(c_operationPool);
+ OperationPtr ptr;
+ while(tmp.seize(ptr))
+ new (ptr.p) Operation(c_dataBufPool, c_dataBufPool, c_dataBufPool);
+ tmp.release();
+ }
+ {
+ SLList<PreparedOperation> tmp(c_preparedOperationPool);
+ PreparedOperationPtr ptr;
+ while(tmp.seize(ptr))
+ new (ptr.p) PreparedOperation(c_attrMappingPool,
+ c_dataBufPool, c_dataBufPool);
+ tmp.release();
+ }
+ {
+ SLList<Transaction> tmp(c_transactionPool);
+ TransactionPtr ptr;
+ while(tmp.seize(ptr))
+ new (ptr.p) Transaction(c_pagePool, c_operationPool);
+ tmp.release();
+ }
+
+ c_lockQueuePool.setSize(5);
+ c_lockElementPool.setSize(5);
+ c_lockQueues.setSize(8);
+}
+
+DbUtil::~DbUtil()
+{
+}
+
+BLOCK_FUNCTIONS(DbUtil)
+
+void
+DbUtil::releasePrepare(PreparePtr prepPtr) {
+ prepPtr.p->preparePages.release();
+ c_runningPrepares.release(prepPtr); // Automatic release in pool
+}
+
+void
+DbUtil::releasePreparedOperation(PreparedOperationPtr prepOpPtr) {
+ prepOpPtr.p->attrMapping.release();
+ prepOpPtr.p->attrInfo.release();
+ prepOpPtr.p->rsInfo.release();
+ prepOpPtr.p->pkBitmask.clear();
+ c_preparedOperationPool.release(prepOpPtr); // No list holding these structs
+}
+
+void
+DbUtil::releaseTransaction(TransactionPtr transPtr){
+ transPtr.p->executePages.release();
+ OperationPtr opPtr;
+ for(transPtr.p->operations.first(opPtr); opPtr.i != RNIL;
+ transPtr.p->operations.next(opPtr)){
+ opPtr.p->attrInfo.release();
+ opPtr.p->keyInfo.release();
+ opPtr.p->rs.release();
+ if (opPtr.p->prepOp != 0 && opPtr.p->prepOp_i != RNIL) {
+ if (opPtr.p->prepOp->releaseFlag) {
+ PreparedOperationPtr prepOpPtr;
+ prepOpPtr.i = opPtr.p->prepOp_i;
+ prepOpPtr.p = opPtr.p->prepOp;
+ releasePreparedOperation(prepOpPtr);
+ }
+ }
+ }
+ transPtr.p->operations.release();
+ c_runningTransactions.release(transPtr);
+}
+
+void
+DbUtil::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 startphase = signal->theData[1];
+
+ if(startphase == 1){
+ c_transId[0] = (number() << 20) + (getOwnNodeId() << 8);
+ c_transId[1] = 0;
+ }
+
+ if(startphase == 6){
+ hardcodedPrepare();
+ connectTc(signal);
+ }
+
+ signal->theData[0] = 0;
+ signal->theData[3] = 1;
+ signal->theData[4] = 6;
+ signal->theData[5] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 6, JBB);
+
+ return;
+}
+
+void
+DbUtil::execNDB_STTOR(Signal* signal)
+{
+ (void)signal; // Don't want compiler warning
+
+ jamEntry();
+}
+
+
+/***************************
+ * Seize a number of TC records
+ * to use for Util transactions
+ */
+
+void
+DbUtil::connectTc(Signal* signal){
+
+ TransactionPtr ptr;
+ while(c_seizingTransactions.seize(ptr)){
+ signal->theData[0] = ptr.i << 1; // See TcCommitConf
+ signal->theData[1] = reference();
+ sendSignal(DBTC_REF, GSN_TCSEIZEREQ, signal, 2, JBB);
+ }
+}
+
+void
+DbUtil::execTCSEIZECONF(Signal* signal){
+ jamEntry();
+
+ TransactionPtr ptr;
+ ptr.i = signal->theData[0] >> 1;
+ c_seizingTransactions.getPtr(ptr, signal->theData[0] >> 1);
+ ptr.p->connectPtr = signal->theData[1];
+
+ c_seizingTransactions.release(ptr);
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Misc
+ * ------------------------------------------------------------------------
+ *
+ * ContinueB, Dump
+ **************************************************************************/
+
+void
+DbUtil::execCONTINUEB(Signal* signal){
+ jamEntry();
+ const Uint32 Tdata0 = signal->theData[0];
+
+ switch(Tdata0){
+ default:
+ ndbrequire(0);
+ }
+}
+
+void
+DbUtil::execDUMP_STATE_ORD(Signal* signal){
+ jamEntry();
+
+ /****************************************************************************
+ * SEQUENCE SERVICE
+ *
+ * 200 : Simple test of Public Sequence Interface
+ * ----------------------------------------------
+ * - Sends a SEQUENCE_REQ signal to Util (itself)
+ */
+ const Uint32 tCase = signal->theData[0];
+ if(tCase == 200){
+ jam()
+ ndbout << "--------------------------------------------------" << endl;
+ UtilSequenceReq * req = (UtilSequenceReq*)signal->getDataPtrSend();
+ Uint32 seqId = 1;
+ Uint32 reqTy = UtilSequenceReq::CurrVal;
+
+ if(signal->length() > 1) seqId = signal->theData[1];
+ if(signal->length() > 2) reqTy = signal->theData[2];
+
+ req->senderData = 12;
+ req->sequenceId = seqId;
+ req->requestType = reqTy;
+
+ sendSignal(DBUTIL_REF, GSN_UTIL_SEQUENCE_REQ,
+ signal, UtilSequenceReq::SignalLength, JBB);
+ }
+
+ /****************************************************************************/
+ /* // Obsolete tests, should be rewritten for long signals!!
+ if(tCase == 210){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0], 128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Delete);
+ w.add(UtilPrepareReq::TableName, "sys/def/SYSTAB_0");
+ w.add(UtilPrepareReq::AttributeName, "SYSKEY_0"); // AttrNo = 0
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 211){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Insert);
+ w.add(UtilPrepareReq::TableName, "sys/def/SYSTAB_0");
+ w.add(UtilPrepareReq::AttributeName, "SYSKEY_0"); // AttrNo = 0
+ w.add(UtilPrepareReq::AttributeName, "NEXTID"); // AttrNo = 1
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 212){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Update);
+ w.add(UtilPrepareReq::TableName, "sys/def/SYSTAB_0");
+ w.add(UtilPrepareReq::AttributeName, "SYSKEY_0"); // AttrNo = 0
+ w.add(UtilPrepareReq::AttributeName, "NEXTID"); // AttrNo = 1
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 213){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Read);
+ w.add(UtilPrepareReq::TableName, "sys/def/SYSTAB_0");
+ w.add(UtilPrepareReq::AttributeName, "SYSKEY_0"); // AttrNo = 0
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 214){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0], 128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Delete);
+ w.add(UtilPrepareReq::TableId, (unsigned int)0); // SYSTAB_0
+ w.add(UtilPrepareReq::AttributeId, (unsigned int)0);// SYSKEY_0
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 215){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Insert);
+ w.add(UtilPrepareReq::TableId, (unsigned int)0); // SYSTAB_0
+ w.add(UtilPrepareReq::AttributeId, (unsigned int)0); // SYSKEY_0
+ w.add(UtilPrepareReq::AttributeId, 1); // NEXTID
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 216){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Update);
+ w.add(UtilPrepareReq::TableId, (unsigned int)0); // SYSTAB_0
+ w.add(UtilPrepareReq::AttributeId, (unsigned int)0);// SYSKEY_0
+ w.add(UtilPrepareReq::AttributeId, 1); // NEXTID
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ if(tCase == 217){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Read);
+ w.add(UtilPrepareReq::TableId, (unsigned int)0); // SYSTAB_0
+ w.add(UtilPrepareReq::AttributeId, (unsigned int)0);// SYSKEY_0
+ Uint32 length = w.getWordsUsed();
+ ndbassert(length <= pageSizeInWords);
+
+ sendUtilPrepareReqSignals(signal, propPage, length);
+ }
+ */
+ /****************************************************************************/
+ /* // Obsolete tests, should be rewritten for long signals!!
+ if(tCase == 220){
+ jam();
+ ndbout << "--------------------------------------------------" << endl;
+ Uint32 prepI = signal->theData[1];
+ Uint32 length = signal->theData[2];
+ Uint32 attributeValue0 = signal->theData[3];
+ Uint32 attributeValue1a = signal->theData[4];
+ Uint32 attributeValue1b = signal->theData[5];
+ ndbrequire(prepI != 0);
+
+ UtilExecuteReq * req = (UtilExecuteReq *)signal->getDataPtrSend();
+
+ req->senderData = 221;
+ req->prepareId = prepI;
+ req->totalDataLen = length; // Including headers
+ req->offset = 0;
+
+ AttributeHeader::init(&req->attrData[0], 0, 1); // AttrNo 0, DataSize
+ req->attrData[1] = attributeValue0; // AttrValue
+ AttributeHeader::init(&req->attrData[2], 1, 2); // AttrNo 1, DataSize
+ req->attrData[3] = attributeValue1a; // AttrValue
+ req->attrData[4] = attributeValue1b; // AttrValue
+
+ printUTIL_EXECUTE_REQ(stdout, signal->getDataPtrSend(), 3 + 5,0);
+ sendSignal(DBUTIL_REF, GSN_UTIL_EXECUTE_REQ, signal, 3 + 5, JBB);
+ }
+*/
+ /****************************************************************************
+ * 230 : PRINT STATE
+ */
+#ifdef ARRAY_GUARD
+ if(tCase == 230){
+ jam();
+
+ ndbout << "--------------------------------------------------" << endl;
+ if (signal->length() <= 1) {
+ ndbout << "Usage: DUMP 230 <recordType> <recordNo>" << endl
+ << "[1] Print Prepare (running) records" << endl
+ << "[2] Print PreparedOperation records" << endl
+ << "[3] Print Transaction records" << endl
+ << "[4] Print Operation records" << endl
+ << "Ex. \"dump 230 1 2\" prints Prepare record no 2." << endl
+ << endl
+ << "210 : PREPARE_REQ DELETE SYSTAB_0 SYSKEY_0" << endl
+ << "211 : PREPARE_REQ INSERT SYSTAB_0 SYSKEY_0 NEXTID" << endl
+ << "212 : PREPARE_REQ UPDATE SYSTAB_0 SYSKEY_0 NEXTID" << endl
+ << "213 : PREPARE_REQ READ SYSTAB_0 SYSKEY_0" << endl
+ << "214 : PREPARE_REQ DELETE SYSTAB_0 SYSKEY_0 using id" << endl
+ << "215 : PREPARE_REQ INSERT SYSTAB_0 SYSKEY_0 NEXTID using id" << endl
+ << "216 : PREPARE_REQ UPDATE SYSTAB_0 SYSKEY_0 NEXTID using id" << endl
+ << "217 : PREPARE_REQ READ SYSTAB_0 SYSKEY_0 using id" << endl
+ << "220 : EXECUTE_REQ <PrepId> <Len> <Val1> <Val2a> <Val2b>" <<endl
+ << "299 : Crash system (using ndbrequire(0))"
+ << endl
+ << "Ex. \"dump 220 3 5 1 0 17 \" prints Prepare record no 2."
+ << endl;
+ return;
+ }
+
+ switch (signal->theData[1]) {
+ case 1:
+ // ** Print a specific record **
+ if (signal->length() >= 3) {
+ PreparePtr prepPtr;
+ if (!c_preparePool.isSeized(signal->theData[2])) {
+ ndbout << "Prepare Id: " << signal->theData[2]
+ << " (Not seized!)" << endl;
+ } else {
+ c_preparePool.getPtr(prepPtr, signal->theData[2]);
+ prepPtr.p->print();
+ }
+ return;
+ }
+
+ // ** Print all records **
+ PreparePtr prepPtr;
+ if (!c_runningPrepares.first(prepPtr)) {
+ ndbout << "No Prepare records exist" << endl;
+ return;
+ }
+
+ while (!prepPtr.isNull()) {
+ prepPtr.p->print();
+ c_runningPrepares.next(prepPtr);
+ }
+ return;
+
+ case 2:
+ // ** Print a specific record **
+ if (signal->length() >= 3) {
+ if (!c_preparedOperationPool.isSeized(signal->theData[2])) {
+ ndbout << "PreparedOperation Id: " << signal->theData[2]
+ << " (Not seized!)" << endl;
+ return;
+ }
+ ndbout << "PreparedOperation Id: " << signal->theData[2] << endl;
+ PreparedOperationPtr prepOpPtr;
+ c_runningPreparedOperations.getPtr(prepOpPtr, signal->theData[2]);
+ prepOpPtr.p->print();
+ return;
+ }
+
+ // ** Print all records **
+ PreparedOperationPtr prepOpPtr;
+ if (!c_runningPreparedOperations.first(prepOpPtr)) {
+ ndbout << "No PreparedOperations exist" << endl;
+ return;
+ }
+ while (!prepOpPtr.isNull()) {
+ ndbout << "[-PreparedOperation no " << prepOpPtr.i << ":";
+ prepOpPtr.p->print();
+ ndbout << "]";
+ c_runningPreparedOperations.next(prepOpPtr);
+ }
+ return;
+
+ case 3:
+ // ** Print a specific record **
+ if (signal->length() >= 3) {
+ ndbout << "Print specific record not implemented." << endl;
+ return;
+ }
+
+ // ** Print all records **
+ ndbout << "Print all records not implemented, specify an Id." << endl;
+ return;
+
+ case 4:
+ ndbout << "Not implemented" << endl;
+ return;
+
+ default:
+ ndbout << "Unknown input (try without any data)" << endl;
+ return;
+ }
+ }
+#endif
+ if(tCase == 240 && signal->getLength() == 2){
+ MutexManager::ActiveMutexPtr ptr;
+ ndbrequire(c_mutexMgr.seize(ptr));
+ ptr.p->m_mutexId = signal->theData[1];
+ Callback c = { safe_cast(&DbUtil::mutex_created), ptr.i };
+ ptr.p->m_callback = c;
+ c_mutexMgr.create(signal, ptr);
+ ndbout_c("c_mutexMgr.create ptrI=%d mutexId=%d", ptr.i, ptr.p->m_mutexId);
+ }
+
+ if(tCase == 241 && signal->getLength() == 2){
+ MutexManager::ActiveMutexPtr ptr;
+ ndbrequire(c_mutexMgr.seize(ptr));
+ ptr.p->m_mutexId = signal->theData[1];
+ Callback c = { safe_cast(&DbUtil::mutex_locked), ptr.i };
+ ptr.p->m_callback = c;
+ c_mutexMgr.lock(signal, ptr);
+ ndbout_c("c_mutexMgr.lock ptrI=%d mutexId=%d", ptr.i, ptr.p->m_mutexId);
+ }
+
+ if(tCase == 242 && signal->getLength() == 2){
+ MutexManager::ActiveMutexPtr ptr;
+ ptr.i = signal->theData[1];
+ c_mutexMgr.getPtr(ptr);
+ Callback c = { safe_cast(&DbUtil::mutex_unlocked), ptr.i };
+ ptr.p->m_callback = c;
+ c_mutexMgr.unlock(signal, ptr);
+ ndbout_c("c_mutexMgr.unlock ptrI=%d mutexId=%d", ptr.i, ptr.p->m_mutexId);
+ }
+
+ if(tCase == 243 && signal->getLength() == 3){
+ MutexManager::ActiveMutexPtr ptr;
+ ndbrequire(c_mutexMgr.seize(ptr));
+ ptr.p->m_mutexId = signal->theData[1];
+ ptr.p->m_mutexKey = signal->theData[2];
+ Callback c = { safe_cast(&DbUtil::mutex_destroyed), ptr.i };
+ ptr.p->m_callback = c;
+ c_mutexMgr.destroy(signal, ptr);
+ ndbout_c("c_mutexMgr.destroy ptrI=%d mutexId=%d key=%d",
+ ptr.i, ptr.p->m_mutexId, ptr.p->m_mutexKey);
+ }
+}
+
+void
+DbUtil::mutex_created(Signal* signal, Uint32 ptrI, Uint32 retVal){
+ MutexManager::ActiveMutexPtr ptr; ptr.i = ptrI;
+ c_mutexMgr.getPtr(ptr);
+ ndbout_c("mutex_created - mutexId=%d, retVal=%d",
+ ptr.p->m_mutexId, retVal);
+ c_mutexMgr.release(ptrI);
+}
+
+void
+DbUtil::mutex_destroyed(Signal* signal, Uint32 ptrI, Uint32 retVal){
+ MutexManager::ActiveMutexPtr ptr; ptr.i = ptrI;
+ c_mutexMgr.getPtr(ptr);
+ ndbout_c("mutex_destroyed - mutexId=%d, retVal=%d",
+ ptr.p->m_mutexId, retVal);
+ c_mutexMgr.release(ptrI);
+}
+
+void
+DbUtil::mutex_locked(Signal* signal, Uint32 ptrI, Uint32 retVal){
+ MutexManager::ActiveMutexPtr ptr; ptr.i = ptrI;
+ c_mutexMgr.getPtr(ptr);
+ ndbout_c("mutex_locked - mutexId=%d, retVal=%d key=%d ptrI=%d",
+ ptr.p->m_mutexId, retVal, ptr.p->m_mutexKey, ptrI);
+ if(retVal)
+ c_mutexMgr.release(ptrI);
+}
+
+void
+DbUtil::mutex_unlocked(Signal* signal, Uint32 ptrI, Uint32 retVal){
+ MutexManager::ActiveMutexPtr ptr; ptr.i = ptrI;
+ c_mutexMgr.getPtr(ptr);
+ ndbout_c("mutex_unlocked - mutexId=%d, retVal=%d",
+ ptr.p->m_mutexId, retVal);
+ if(!retVal)
+ c_mutexMgr.release(ptrI);
+}
+
+void
+DbUtil::execUTIL_SEQUENCE_REF(Signal* signal){
+ jamEntry();
+ ndbout << "UTIL_SEQUENCE_REF" << endl;
+ printUTIL_SEQUENCE_REF(stdout, signal->getDataPtrSend(), signal->length(), 0);
+}
+
+void
+DbUtil::execUTIL_SEQUENCE_CONF(Signal* signal){
+ jamEntry();
+ ndbout << "UTIL_SEQUENCE_CONF" << endl;
+ printUTIL_SEQUENCE_CONF(stdout, signal->getDataPtrSend(), signal->length(),0);
+}
+
+void
+DbUtil::execUTIL_PREPARE_CONF(Signal* signal){
+ jamEntry();
+ ndbout << "UTIL_PREPARE_CONF" << endl;
+ printUTIL_PREPARE_CONF(stdout, signal->getDataPtrSend(), signal->length(), 0);
+}
+
+void
+DbUtil::execUTIL_PREPARE_REF(Signal* signal){
+ jamEntry();
+ ndbout << "UTIL_PREPARE_REF" << endl;
+ printUTIL_PREPARE_REF(stdout, signal->getDataPtrSend(), signal->length(), 0);
+}
+
+void
+DbUtil::execUTIL_EXECUTE_CONF(Signal* signal) {
+ jamEntry();
+ ndbout << "UTIL_EXECUTE_CONF" << endl;
+ printUTIL_EXECUTE_CONF(stdout, signal->getDataPtrSend(), signal->length(), 0);
+}
+
+void
+DbUtil::execUTIL_EXECUTE_REF(Signal* signal) {
+ jamEntry();
+
+ ndbout << "UTIL_EXECUTE_REF" << endl;
+ printUTIL_EXECUTE_REF(stdout, signal->getDataPtrSend(), signal->length(), 0);
+}
+
+void
+DbUtil::execUTIL_RELEASE_CONF(Signal* signal) {
+ jamEntry();
+ ndbout << "UTIL_RELEASE_CONF" << endl;
+}
+
+void
+DbUtil::execUTIL_RELEASE_REF(Signal* signal) {
+ jamEntry();
+
+ ndbout << "UTIL_RELEASE_REF" << endl;
+}
+
+void
+DbUtil::sendUtilPrepareRef(Signal* signal, UtilPrepareRef::ErrorCode error,
+ Uint32 recipient, Uint32 senderData){
+ UtilPrepareRef * ref = (UtilPrepareRef *)signal->getDataPtrSend();
+ ref->errorCode = error;
+ ref->senderData = senderData;
+
+ sendSignal(recipient, GSN_UTIL_PREPARE_REF, signal,
+ UtilPrepareRef::SignalLength, JBB);
+}
+
+void
+DbUtil::sendUtilExecuteRef(Signal* signal, UtilExecuteRef::ErrorCode error,
+ Uint32 TCerror, Uint32 recipient, Uint32 senderData){
+
+ UtilExecuteRef * ref = (UtilExecuteRef *)signal->getDataPtrSend();
+ ref->senderData = senderData;
+ ref->errorCode = error;
+ ref->TCErrorCode = TCerror;
+
+ sendSignal(recipient, GSN_UTIL_EXECUTE_REF, signal,
+ UtilPrepareRef::SignalLength, JBB);
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Prepare service
+ * ------------------------------------------------------------------------
+ *
+ * Prepares a transaction by storing info in some structs
+ **************************************************************************/
+
+void
+DbUtil::execUTIL_PREPARE_REQ(Signal* signal)
+{
+ jamEntry();
+
+ /****************
+ * Decode Signal
+ ****************/
+ UtilPrepareReq * req = (UtilPrepareReq *)signal->getDataPtr();
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+
+ if(signal->getNoOfSections() == 0) {
+ // Missing prepare data
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal, UtilPrepareRef::MISSING_PROPERTIES_SECTION,
+ senderRef, senderData);
+ return;
+ }
+
+ PreparePtr prepPtr;
+ SegmentedSectionPtr ptr;
+
+ jam();
+ if(!c_runningPrepares.seize(prepPtr)) {
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal, UtilPrepareRef::PREPARE_SEIZE_ERROR,
+ senderRef, senderData);
+ return;
+ };
+ signal->getSection(ptr, UtilPrepareReq::PROPERTIES_SECTION);
+ const Uint32 noPages = (ptr.sz + sizeof(Page32)) / sizeof(Page32);
+ ndbassert(noPages > 0);
+ if (!prepPtr.p->preparePages.seize(noPages)) {
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal, UtilPrepareRef::PREPARE_PAGES_SEIZE_ERROR,
+ senderRef, senderData);
+ c_preparePool.release(prepPtr);
+ return;
+ }
+ // Save SimpleProperties
+ Uint32* target = &prepPtr.p->preparePages.getPtr(0)->data[0];
+ copy(target, ptr);
+ prepPtr.p->prepDataLen = ptr.sz;
+ // Release long signal sections
+ releaseSections(signal);
+ // Check table properties with DICT
+ SimplePropertiesSectionReader reader(ptr, getSectionSegmentPool());
+ prepPtr.p->clientRef = senderRef;
+ prepPtr.p->clientData = senderData;
+ // Release long signal sections
+ releaseSections(signal);
+ readPrepareProps(signal, &reader, prepPtr.i);
+}
+
+void DbUtil::readPrepareProps(Signal* signal,
+ SimpleProperties::Reader* reader,
+ Uint32 senderData)
+{
+ jam();
+#if 0
+ printf("DbUtil::readPrepareProps: Received SimpleProperties:\n");
+ reader->printAll(ndbout);
+#endif
+ ndbrequire(reader->first());
+ ndbrequire(reader->getKey() == UtilPrepareReq::NoOfOperations);
+ ndbrequire(reader->getUint32() == 1); // Only one op/trans implemented
+
+ ndbrequire(reader->next());
+ ndbrequire(reader->getKey() == UtilPrepareReq::OperationType);
+
+ ndbrequire(reader->next());
+ UtilPrepareReq::KeyValue tableKey =
+ (UtilPrepareReq::KeyValue) reader->getKey();
+ ndbrequire((tableKey == UtilPrepareReq::TableName) ||
+ (tableKey == UtilPrepareReq::TableId));
+
+ /************************
+ * Ask Dict for metadata
+ ************************/
+ {
+ GetTabInfoReq * req = (GetTabInfoReq *)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = senderData;
+ if (tableKey == UtilPrepareReq::TableName) {
+ jam();
+ char tableName[MAX_TAB_NAME_SIZE];
+ req->requestType = GetTabInfoReq::RequestByName |
+ GetTabInfoReq::LongSignalConf;
+
+ req->tableNameLen = reader->getValueLen(); // Including trailing \0
+
+ /********************************************
+ * Code signal data and send signals to DICT
+ ********************************************/
+
+ ndbrequire(req->tableNameLen < MAX_TAB_NAME_SIZE);
+ reader->getString((char*)tableName);
+ LinearSectionPtr ptr[1];
+ ptr[0].p = (Uint32*)tableName;
+ ptr[0].sz = req->tableNameLen;
+ sendSignal(DBDICT_REF, GSN_GET_TABINFOREQ, signal,
+ GetTabInfoReq::SignalLength, JBB, ptr,1);
+
+ }
+ else { // (tableKey == UtilPrepareReq::TableId)
+ jam();
+ req->requestType = GetTabInfoReq::RequestById |
+ GetTabInfoReq::LongSignalConf;
+ req->tableId = reader->getUint32();
+ sendSignal(DBDICT_REF, GSN_GET_TABINFOREQ, signal,
+ GetTabInfoReq::SignalLength, JBB);
+ }
+
+ }
+}
+
+/**
+ * @note We assume that this signal comes due to a request related
+ * to a Prepare struct. DictTabInfo:s 'senderData' denotes
+ * the Prepare struct related to the request.
+ */
+void
+DbUtil::execGET_TABINFO_CONF(Signal* signal){
+ jamEntry();
+
+ if(!assembleFragments(signal)){
+ jam();
+ return;
+ }
+
+ /****************
+ * Decode signal
+ ****************/
+ GetTabInfoConf * const conf = (GetTabInfoConf*)signal->getDataPtr();
+ const Uint32 prepI = conf->senderData;
+ const Uint32 totalLen = conf->totalLen;
+
+ SegmentedSectionPtr dictTabInfoPtr;
+ signal->getSection(dictTabInfoPtr, GetTabInfoConf::DICT_TAB_INFO);
+ ndbrequire(dictTabInfoPtr.sz == totalLen);
+
+ PreparePtr prepPtr;
+ c_runningPrepares.getPtr(prepPtr, prepI);
+ prepareOperation(signal, prepPtr);
+}
+
+void
+DbUtil::execGET_TABINFOREF(Signal* signal){
+ jamEntry();
+
+ GetTabInfoRef * ref = (GetTabInfoRef *)signal->getDataPtr();
+ Uint32 prepI = ref->senderData;
+#define EVENT_DEBUG
+#if 0 //def EVENT_DEBUG
+ ndbout << "Signal GET_TABINFOREF received." << endl;
+ ndbout << "Error Code: " << ref->errorCode << endl;
+
+ switch (ref->errorCode) {
+ case GetTabInfoRef::InvalidTableId:
+ ndbout << " Msg: Invalid table id" << endl;
+ break;
+ case GetTabInfoRef::TableNotDefined:
+ ndbout << " Msg: Table not defined" << endl;
+ break;
+ case GetTabInfoRef::TableNameToLong:
+ ndbout << " Msg: Table node too long" << endl;
+ break;
+ default:
+ ndbout << " Msg: Unknown error returned from Dict" << endl;
+ break;
+ }
+#endif
+
+ PreparePtr prepPtr;
+ c_runningPrepares.getPtr(prepPtr, prepI);
+
+ sendUtilPrepareRef(signal, UtilPrepareRef::DICT_TAB_INFO_ERROR,
+ prepPtr.p->clientRef, prepPtr.p->clientData);
+
+ releasePrepare(prepPtr);
+}
+
+
+/******************************************************************************
+ * Prepare Operation
+ *
+ * Using a prepare record, prepare an operation (i.e. create PreparedOperation).
+ * Info from both Pepare request (PreparePages) and DictTabInfo is used.
+ *
+ * Algorithm:
+ * -# Seize AttrbuteMapping
+ * - Lookup in preparePages how many attributes should be prepared
+ * - Seize AttributeMapping
+ * -# For each attributes in preparePages
+ * - Lookup id and isPK in dictInfoPages
+ * - Store "no -> (AttributeId, Position)" in AttributeMapping
+ * -# For each map in AttributeMapping
+ * - if (isPK) then assign offset
+ ******************************************************************************/
+void
+DbUtil::prepareOperation(Signal* signal, PreparePtr prepPtr)
+{
+ jam();
+
+ /*******************************************
+ * Seize and store PreparedOperation struct
+ *******************************************/
+ PreparedOperationPtr prepOpPtr;
+ if(!c_runningPreparedOperations.seize(prepOpPtr)) {
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal, UtilPrepareRef::PREPARED_OPERATION_SEIZE_ERROR,
+ prepPtr.p->clientRef, prepPtr.p->clientData);
+ releasePrepare(prepPtr);
+ return;
+ }
+ prepPtr.p->prepOpPtr = prepOpPtr;
+
+ /********************
+ * Read request info
+ ********************/
+ SimplePropertiesLinearReader prepPagesReader(&prepPtr.p->preparePages.getPtr(0)->data[0],
+ prepPtr.p->prepDataLen);
+
+ ndbrequire(prepPagesReader.first());
+ ndbrequire(prepPagesReader.getKey() == UtilPrepareReq::NoOfOperations);
+ const Uint32 noOfOperations = prepPagesReader.getUint32();
+ ndbrequire(noOfOperations == 1);
+
+ ndbrequire(prepPagesReader.next());
+ ndbrequire(prepPagesReader.getKey() == UtilPrepareReq::OperationType);
+ const Uint32 operationType = prepPagesReader.getUint32();
+
+ ndbrequire(prepPagesReader.next());
+
+ char tableName[MAX_TAB_NAME_SIZE];
+ Uint32 tableId;
+ UtilPrepareReq::KeyValue tableKey =
+ (UtilPrepareReq::KeyValue) prepPagesReader.getKey();
+ if (tableKey == UtilPrepareReq::TableId) {
+ jam();
+ tableId = prepPagesReader.getUint32();
+ }
+ else {
+ jam();
+ ndbrequire(prepPagesReader.getKey() == UtilPrepareReq::TableName);
+ ndbrequire(prepPagesReader.getValueLen() <= MAX_TAB_NAME_SIZE);
+ prepPagesReader.getString(tableName);
+ }
+ /******************************************************************
+ * Seize AttributeMapping (by counting no of attribs in prepPages)
+ ******************************************************************/
+ Uint32 noOfAttributes = 0; // No of attributes in PreparePages (used later)
+ while(prepPagesReader.next()) {
+ if (tableKey == UtilPrepareReq::TableName) {
+ jam();
+ ndbrequire(prepPagesReader.getKey() == UtilPrepareReq::AttributeName);
+ } else {
+ jam();
+ ndbrequire(prepPagesReader.getKey() == UtilPrepareReq::AttributeId);
+ }
+ noOfAttributes++;
+ }
+ ndbrequire(prepPtr.p->prepOpPtr.p->attrMapping.seize(noOfAttributes));
+ if (operationType == UtilPrepareReq::Read) {
+ ndbrequire(prepPtr.p->prepOpPtr.p->rsInfo.seize(noOfAttributes));
+ }
+ /***************************************
+ * For each attribute name, lookup info
+ ***************************************/
+ // Goto start of attribute names
+ ndbrequire(prepPagesReader.first() && prepPagesReader.next() &&
+ prepPagesReader.next());
+
+ DictTabInfo::Table tableDesc; tableDesc.init();
+ AttrMappingBuffer::DataBufferIterator attrMappingIt;
+ ndbrequire(prepPtr.p->prepOpPtr.p->attrMapping.first(attrMappingIt));
+
+ ResultSetBuffer::DataBufferIterator rsInfoIt;
+ if (operationType == UtilPrepareReq::Read) {
+ ndbrequire(prepPtr.p->prepOpPtr.p->rsInfo.first(rsInfoIt));
+ }
+
+ Uint32 noOfPKAttribsStored = 0;
+ Uint32 noOfNonPKAttribsStored = 0;
+ Uint32 attrLength = 0;
+ Uint32 pkAttrLength = 0;
+ char attrNameRequested[MAX_ATTR_NAME_SIZE];
+ Uint32 attrIdRequested;
+
+ while(prepPagesReader.next()) {
+ UtilPrepareReq::KeyValue attributeKey =
+ (UtilPrepareReq::KeyValue) prepPagesReader.getKey();
+
+ ndbrequire((attributeKey == UtilPrepareReq::AttributeName) ||
+ (attributeKey == UtilPrepareReq::AttributeId));
+ if (attributeKey == UtilPrepareReq::AttributeName) {
+ jam();
+ ndbrequire(prepPagesReader.getValueLen() <= MAX_ATTR_NAME_SIZE);
+
+ prepPagesReader.getString(attrNameRequested);
+ attrIdRequested= ~0u;
+ } else {
+ jam();
+ attrIdRequested = prepPagesReader.getUint32();
+ }
+ /*****************************************
+ * Copy DictTabInfo into tableDesc struct
+ *****************************************/
+
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, GetTabInfoConf::DICT_TAB_INFO);
+ SimplePropertiesSectionReader dictInfoReader(ptr, getSectionSegmentPool());
+
+ SimpleProperties::UnpackStatus unpackStatus;
+ unpackStatus = SimpleProperties::unpack(dictInfoReader, &tableDesc,
+ DictTabInfo::TableMapping,
+ DictTabInfo::TableMappingSize,
+ true, true);
+ ndbrequire(unpackStatus == SimpleProperties::Break);
+
+ /************************
+ * Lookup in DictTabInfo
+ ************************/
+ DictTabInfo::Attribute attrDesc; attrDesc.init();
+ char attrName[MAX_ATTR_NAME_SIZE];
+ Uint32 attrId= ~(Uint32)0;
+ bool attributeFound = false;
+ Uint32 noOfKeysFound = 0; // # PK attrs found before attr in DICTdata
+ Uint32 noOfNonKeysFound = 0; // # nonPK attrs found before attr in DICTdata
+ for (Uint32 i=0; i<tableDesc.NoOfAttributes; i++) {
+ if (tableKey == UtilPrepareReq::TableName) {
+ jam();
+ ndbrequire(dictInfoReader.getKey() == DictTabInfo::AttributeName);
+ ndbrequire(dictInfoReader.getValueLen() <= MAX_ATTR_NAME_SIZE);
+ dictInfoReader.getString(attrName);
+ attrId= ~(Uint32)0; // attrId not used
+ } else { // (tableKey == UtilPrepareReq::TableId)
+ jam();
+ dictInfoReader.next(); // Skip name
+ ndbrequire(dictInfoReader.getKey() == DictTabInfo::AttributeId);
+ attrId = dictInfoReader.getUint32();
+ attrName[0]= '\0'; // attrName not used
+ }
+ unpackStatus = SimpleProperties::unpack(dictInfoReader, &attrDesc,
+ DictTabInfo::AttributeMapping,
+ DictTabInfo::AttributeMappingSize,
+ true, true);
+ ndbrequire(unpackStatus == SimpleProperties::Break);
+ //attrDesc.print(stdout);
+
+ if (attrDesc.AttributeKeyFlag) { jam(); noOfKeysFound++; }
+ else { jam(); noOfNonKeysFound++; }
+ if (attributeKey == UtilPrepareReq::AttributeName) {
+ if (strcmp(attrName, attrNameRequested) == 0) {
+ attributeFound = true;
+ break;
+ }
+ }
+ else // (attributeKey == UtilPrepareReq::AttributeId)
+ if (attrId == attrIdRequested) {
+ attributeFound = true;
+ break;
+ }
+
+ // Move to next attribute
+ ndbassert(dictInfoReader.getKey() == DictTabInfo::AttributeEnd);
+ dictInfoReader.next();
+ }
+
+ /**********************
+ * Attribute not found
+ **********************/
+ if (!attributeFound) {
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal,
+ UtilPrepareRef::DICT_TAB_INFO_ERROR,
+ prepPtr.p->clientRef, prepPtr.p->clientData);
+ infoEvent("UTIL: Unknown attribute requested: %s in table: %s",
+ attrNameRequested, tableName);
+ releasePreparedOperation(prepOpPtr);
+ releasePrepare(prepPtr);
+ return;
+ }
+
+ /**************************************************************
+ * Attribute found - store in mapping (AttributeId, Position)
+ **************************************************************/
+ AttributeHeader & attrMap =
+ AttributeHeader::init(attrMappingIt.data,
+ attrDesc.AttributeId, // 1. Store AttrId
+ 0);
+
+ if (attrDesc.AttributeKeyFlag) {
+ // ** Attribute belongs to PK **
+ prepOpPtr.p->pkBitmask.set(attrDesc.AttributeId);
+ attrMap.setDataSize(noOfKeysFound - 1); // 2. Store Position
+ noOfPKAttribsStored++;
+ } else {
+ attrMap.setDataSize(0x3fff); // 2. Store Position (fake)
+ noOfNonPKAttribsStored++;
+
+ /***********************************************************
+ * Error: Read nonPK Attr before all PK attr have been read
+ ***********************************************************/
+ if (noOfPKAttribsStored != tableDesc.NoOfKeyAttr) {
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal,
+ UtilPrepareRef::DICT_TAB_INFO_ERROR,
+ prepPtr.p->clientRef, prepPtr.p->clientData);
+ infoEvent("UTIL: Non-PK attr not allowed before "
+ "all PK attrs have been defined, table: %s",
+ tableName);
+ releasePreparedOperation(prepOpPtr);
+ releasePrepare(prepPtr);
+ return;
+ }
+ }
+#if 0
+ ndbout << "BEFORE: attrLength: " << attrLength << endl;
+#endif
+ {
+ int len = 0;
+ switch (attrDesc.AttributeSize) {
+ case DictTabInfo::an8Bit:
+ len = (attrDesc.AttributeArraySize + 3)/ 4;
+ break;
+ case DictTabInfo::a16Bit:
+ len = (attrDesc.AttributeArraySize + 1) / 2;
+ break;
+ case DictTabInfo::a32Bit:
+ len = attrDesc.AttributeArraySize;
+ break;
+ case DictTabInfo::a64Bit:
+ len = attrDesc.AttributeArraySize * 2;
+ break;
+ case DictTabInfo::a128Bit:
+ len = attrDesc.AttributeArraySize * 4;
+ break;
+ }
+ attrLength += len;
+ if (attrDesc.AttributeKeyFlag)
+ pkAttrLength += len;
+
+ if (operationType == UtilPrepareReq::Read) {
+ AttributeHeader::init(rsInfoIt.data,
+ attrDesc.AttributeId, // 1. Store AttrId
+ len);
+ prepOpPtr.p->rsInfo.next(rsInfoIt, 1);
+ }
+ }
+#if 0
+ ndbout << ": AttributeSize: " << attrDesc.AttributeSize << endl;
+ ndbout << ": AttributeArraySize: " << attrDesc.AttributeArraySize << endl;
+ ndbout << "AFTER: attrLength: " << attrLength << endl;
+#endif
+ //attrMappingIt.print(stdout);
+ //prepPtr.p->prepOpPtr.p->attrMapping.print(stdout);
+ prepPtr.p->prepOpPtr.p->attrMapping.next(attrMappingIt, 1);
+ }
+
+ /***************************
+ * Error: Not all PKs found
+ ***************************/
+ if (noOfPKAttribsStored != tableDesc.NoOfKeyAttr) {
+ jam();
+ releaseSections(signal);
+ sendUtilPrepareRef(signal,
+ UtilPrepareRef::DICT_TAB_INFO_ERROR,
+ prepPtr.p->clientRef, prepPtr.p->clientData);
+ infoEvent("UTIL: Not all primary key attributes requested for table: %s",
+ tableName);
+ releasePreparedOperation(prepOpPtr);
+ releasePrepare(prepPtr);
+ return;
+ }
+
+#if 0
+ AttrMappingBuffer::ConstDataBufferIterator tmpIt;
+ for (prepPtr.p->prepOpPtr.p->attrMapping.first(tmpIt); tmpIt.curr.i != RNIL;
+ prepPtr.p->prepOpPtr.p->attrMapping.next(tmpIt)) {
+ AttributeHeader* ah = (AttributeHeader *) tmpIt.data;
+ ah->print(stdout);
+ }
+#endif
+
+ /**********************************************
+ * Preparing of PreparedOperation signal train
+ **********************************************/
+ Uint32 static_len = TcKeyReq::StaticLength;
+ prepOpPtr.p->tckey.tableId = tableDesc.TableId;
+ prepOpPtr.p->tckey.tableSchemaVersion = tableDesc.TableVersion;
+ prepOpPtr.p->noOfKeyAttr = tableDesc.NoOfKeyAttr;
+ prepOpPtr.p->keyLen = tableDesc.KeyLength; // Total no of words in PK
+ if (prepOpPtr.p->keyLen > TcKeyReq::MaxKeyInfo) {
+ jam();
+ prepOpPtr.p->tckeyLenInBytes = (static_len + TcKeyReq::MaxKeyInfo) * 4;
+ } else {
+ jam();
+ prepOpPtr.p->tckeyLenInBytes = (static_len + prepOpPtr.p->keyLen) * 4;
+ }
+ prepOpPtr.p->keyDataPos = static_len; // Start of keyInfo[] in tckeyreq
+
+ Uint32 requestInfo = 0;
+ TcKeyReq::setAbortOption(requestInfo, TcKeyReq::AbortOnError);
+ TcKeyReq::setKeyLength(requestInfo, tableDesc.KeyLength);
+ switch(operationType) {
+ case(UtilPrepareReq::Read):
+ prepOpPtr.p->rsLen =
+ attrLength +
+ tableDesc.NoOfKeyAttr +
+ noOfNonPKAttribsStored; // Read needs a resultset
+ prepOpPtr.p->noOfAttr = tableDesc.NoOfKeyAttr + noOfNonPKAttribsStored;
+ prepOpPtr.p->tckey.attrLen = prepOpPtr.p->noOfAttr;
+ TcKeyReq::setOperationType(requestInfo, ZREAD);
+ break;
+ case(UtilPrepareReq::Update):
+ prepOpPtr.p->rsLen = 0;
+ prepOpPtr.p->noOfAttr = tableDesc.NoOfKeyAttr + noOfNonPKAttribsStored;
+ prepOpPtr.p->tckey.attrLen = attrLength + prepOpPtr.p->noOfAttr;
+ TcKeyReq::setOperationType(requestInfo, ZUPDATE);
+ break;
+ case(UtilPrepareReq::Insert):
+ prepOpPtr.p->rsLen = 0;
+ prepOpPtr.p->noOfAttr = tableDesc.NoOfKeyAttr + noOfNonPKAttribsStored;
+ prepOpPtr.p->tckey.attrLen = attrLength + prepOpPtr.p->noOfAttr;
+ TcKeyReq::setOperationType(requestInfo, ZINSERT);
+ break;
+ case(UtilPrepareReq::Delete):
+ // The number of attributes should equal the size of the primary key
+ ndbrequire(tableDesc.KeyLength == attrLength);
+ prepOpPtr.p->rsLen = 0;
+ prepOpPtr.p->noOfAttr = tableDesc.NoOfKeyAttr;
+ prepOpPtr.p->tckey.attrLen = 0;
+ TcKeyReq::setOperationType(requestInfo, ZDELETE);
+ break;
+ case(UtilPrepareReq::Write):
+ prepOpPtr.p->rsLen = 0;
+ prepOpPtr.p->noOfAttr = tableDesc.NoOfKeyAttr + noOfNonPKAttribsStored;
+ prepOpPtr.p->tckey.attrLen = attrLength + prepOpPtr.p->noOfAttr;
+ TcKeyReq::setOperationType(requestInfo, ZWRITE);
+ break;
+ }
+ TcKeyReq::setAIInTcKeyReq(requestInfo, 0); // Attrinfo sent separately
+ prepOpPtr.p->tckey.requestInfo = requestInfo;
+
+ /****************************
+ * Confirm completed prepare
+ ****************************/
+ UtilPrepareConf * conf = (UtilPrepareConf *)signal->getDataPtr();
+ conf->senderData = prepPtr.p->clientData;
+ conf->prepareId = prepPtr.p->prepOpPtr.i;
+
+ releaseSections(signal);
+ sendSignal(prepPtr.p->clientRef, GSN_UTIL_PREPARE_CONF, signal,
+ UtilPrepareConf::SignalLength, JBB);
+
+#if 0
+ prepPtr.p->prepOpPtr.p->print();
+#endif
+ releasePrepare(prepPtr);
+}
+
+
+void
+DbUtil::execUTIL_RELEASE_REQ(Signal* signal){
+ jamEntry();
+
+ UtilReleaseReq * req = (UtilReleaseReq *)signal->getDataPtr();
+ const Uint32 clientRef = signal->senderBlockRef();
+ const Uint32 prepareId = req->prepareId;
+ const Uint32 senderData = req->senderData;
+
+#if 0
+ /**
+ * This only works in when ARRAY_GUARD is defined (debug-mode)
+ */
+ if (!c_preparedOperationPool.isSeized(prepareId)) {
+ UtilReleaseRef * ref = (UtilReleaseRef *)signal->getDataPtr();
+ ref->prepareId = prepareId;
+ ref->errorCode = UtilReleaseRef::NO_SUCH_PREPARE_SEIZED;
+ sendSignal(clientRef, GSN_UTIL_RELEASE_REF, signal,
+ UtilReleaseRef::SignalLength, JBB);
+ }
+#endif
+ PreparedOperationPtr prepOpPtr;
+ c_preparedOperationPool.getPtr(prepOpPtr, prepareId);
+
+ releasePreparedOperation(prepOpPtr);
+
+ UtilReleaseConf * const conf = (UtilReleaseConf*)signal->getDataPtrSend();
+ conf->senderData = senderData;
+ sendSignal(clientRef, GSN_UTIL_RELEASE_CONF, signal,
+ UtilReleaseConf::SignalLength, JBB);
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Sequence Service
+ * ------------------------------------------------------------------------
+ *
+ * A service with a stored incrementable number
+ **************************************************************************/
+
+void
+DbUtil::hardcodedPrepare() {
+ /**
+ * Prepare SequenceCurrVal (READ)
+ */
+ {
+ PreparedOperationPtr ptr;
+ ndbrequire(c_preparedOperationPool.seizeId(ptr, 0));
+ ptr.p->keyLen = 1;
+ ptr.p->tckey.attrLen = 1;
+ ptr.p->rsLen = 3;
+ ptr.p->tckeyLenInBytes = (TcKeyReq::StaticLength +
+ ptr.p->keyLen + ptr.p->tckey.attrLen) * 4;
+ ptr.p->keyDataPos = TcKeyReq::StaticLength;
+ ptr.p->tckey.tableId = 0;
+ Uint32 requestInfo = 0;
+ TcKeyReq::setAbortOption(requestInfo, TcKeyReq::CommitIfFailFree);
+ TcKeyReq::setOperationType(requestInfo, ZREAD);
+ TcKeyReq::setKeyLength(requestInfo, 1);
+ TcKeyReq::setAIInTcKeyReq(requestInfo, 1);
+ ptr.p->tckey.requestInfo = requestInfo;
+ ptr.p->tckey.tableSchemaVersion = 1;
+
+ // This is actually attr data
+ AttributeHeader::init(&ptr.p->tckey.distrGroupHashValue, 1, 0);
+
+ ndbrequire(ptr.p->rsInfo.seize(1));
+ ResultSetInfoBuffer::DataBufferIterator it;
+ ptr.p->rsInfo.first(it);
+ AttributeHeader::init(it.data, 1, 2); // Attribute 1 - 2 data words
+ }
+
+ /**
+ * Prepare SequenceNextVal (UPDATE)
+ */
+ {
+ PreparedOperationPtr ptr;
+ ndbrequire(c_preparedOperationPool.seizeId(ptr, 1));
+ ptr.p->keyLen = 1;
+ ptr.p->rsLen = 3;
+ ptr.p->tckeyLenInBytes = (TcKeyReq::StaticLength + ptr.p->keyLen + 5) * 4;
+ ptr.p->keyDataPos = TcKeyReq::StaticLength;
+ ptr.p->tckey.attrLen = 11;
+ ptr.p->tckey.tableId = 0;
+ Uint32 requestInfo = 0;
+ TcKeyReq::setAbortOption(requestInfo, TcKeyReq::CommitIfFailFree);
+ TcKeyReq::setOperationType(requestInfo, ZUPDATE);
+ TcKeyReq::setKeyLength(requestInfo, 1);
+ TcKeyReq::setAIInTcKeyReq(requestInfo, 5);
+ TcKeyReq::setInterpretedFlag(requestInfo, 1);
+ ptr.p->tckey.requestInfo = requestInfo;
+ ptr.p->tckey.tableSchemaVersion = 1;
+
+ // Signal is packed, which is why attrInfo is at distrGroupHashValue
+ // position
+ Uint32 * attrInfo = &ptr.p->tckey.distrGroupHashValue;
+ attrInfo[0] = 0; // IntialReadSize
+ attrInfo[1] = 5; // InterpretedSize
+ attrInfo[2] = 0; // FinalUpdateSize
+ attrInfo[3] = 1; // FinalReadSize
+ attrInfo[4] = 0; // SubroutineSize
+
+ { // AttrInfo
+ ndbrequire(ptr.p->attrInfo.seize(6));
+ AttrInfoBuffer::DataBufferIterator it;
+ ptr.p->attrInfo.first(it);
+ * it.data = Interpreter::Read(1, 6);
+ ndbrequire(ptr.p->attrInfo.next(it));
+ * it.data = Interpreter::LoadConst16(7, 1);
+ ndbrequire(ptr.p->attrInfo.next(it));
+ * it.data = Interpreter::Add(7, 6, 7);
+ ndbrequire(ptr.p->attrInfo.next(it));
+ * it.data = Interpreter::Write(1, 7);
+ ndbrequire(ptr.p->attrInfo.next(it));
+ * it.data = Interpreter::ExitOK();
+
+ ndbrequire(ptr.p->attrInfo.next(it));
+ AttributeHeader::init(it.data, 1, 0);
+ }
+
+ { // ResultSet
+ ndbrequire(ptr.p->rsInfo.seize(1));
+ ResultSetInfoBuffer::DataBufferIterator it;
+ ptr.p->rsInfo.first(it);
+ AttributeHeader::init(it.data, 1, 2); // Attribute 1 - 2 data words
+ }
+ }
+
+ /**
+ * Prepare CreateSequence (INSERT)
+ */
+ {
+ PreparedOperationPtr ptr;
+ ndbrequire(c_preparedOperationPool.seizeId(ptr, 2));
+ ptr.p->keyLen = 1;
+ ptr.p->tckey.attrLen = 5;
+ ptr.p->rsLen = 0;
+ ptr.p->tckeyLenInBytes = (TcKeyReq::StaticLength +
+ ptr.p->keyLen + ptr.p->tckey.attrLen) * 4;
+ ptr.p->keyDataPos = TcKeyReq::StaticLength;
+ ptr.p->tckey.tableId = 0;
+ Uint32 requestInfo = 0;
+ TcKeyReq::setAbortOption(requestInfo, TcKeyReq::CommitIfFailFree);
+ TcKeyReq::setOperationType(requestInfo, ZINSERT);
+ TcKeyReq::setKeyLength(requestInfo, 1);
+ TcKeyReq::setAIInTcKeyReq(requestInfo, 0);
+ ptr.p->tckey.requestInfo = requestInfo;
+ ptr.p->tckey.tableSchemaVersion = 1;
+ }
+}
+
+void
+DbUtil::execUTIL_SEQUENCE_REQ(Signal* signal){
+ jamEntry();
+
+ UtilSequenceReq * req = (UtilSequenceReq*)signal->getDataPtr();
+
+ PreparedOperation * prepOp;
+
+ switch(req->requestType){
+ case UtilSequenceReq::CurrVal:
+ prepOp = c_preparedOperationPool.getPtr(0); //c_SequenceCurrVal
+ break;
+ case UtilSequenceReq::NextVal:
+ prepOp = c_preparedOperationPool.getPtr(1); //c_SequenceNextVal
+ break;
+ case UtilSequenceReq::Create:
+ prepOp = c_preparedOperationPool.getPtr(2); //c_CreateSequence
+ break;
+ default:
+ ndbrequire(false);
+ prepOp = 0; // remove warning
+ }
+
+ /**
+ * 1 Transaction with 1 operation
+ */
+ TransactionPtr transPtr;
+ ndbrequire(c_runningTransactions.seize(transPtr));
+
+ OperationPtr opPtr;
+ ndbrequire(transPtr.p->operations.seize(opPtr));
+
+ ndbrequire(opPtr.p->rs.seize(prepOp->rsLen));
+ ndbrequire(opPtr.p->keyInfo.seize(prepOp->keyLen));
+
+ transPtr.p->gsn = GSN_UTIL_SEQUENCE_REQ;
+ transPtr.p->clientRef = signal->senderBlockRef();
+ transPtr.p->clientData = req->senderData;
+ transPtr.p->sequence.sequenceId = req->sequenceId;
+ transPtr.p->sequence.requestType = req->requestType;
+
+ opPtr.p->prepOp = prepOp;
+ opPtr.p->prepOp_i = RNIL;
+
+ KeyInfoBuffer::DataBufferIterator it;
+ opPtr.p->keyInfo.first(it);
+ it.data[0] = transPtr.p->sequence.sequenceId;
+
+ if(req->requestType == UtilSequenceReq::Create){
+ ndbrequire(opPtr.p->attrInfo.seize(5));
+ AttrInfoBuffer::DataBufferIterator it;
+
+ opPtr.p->attrInfo.first(it);
+ AttributeHeader::init(it.data, 0, 1);
+
+ ndbrequire(opPtr.p->attrInfo.next(it));
+ * it.data = transPtr.p->sequence.sequenceId;
+
+ ndbrequire(opPtr.p->attrInfo.next(it));
+ AttributeHeader::init(it.data, 1, 2);
+
+ ndbrequire(opPtr.p->attrInfo.next(it));
+ * it.data = 0;
+
+ ndbrequire(opPtr.p->attrInfo.next(it));
+ * it.data = 0;
+ }
+
+ runTransaction(signal, transPtr);
+}
+
+int
+DbUtil::getResultSet(Signal* signal, const Transaction * transP,
+ struct LinearSectionPtr sectionsPtr[]) {
+ OperationPtr opPtr;
+ ndbrequire(transP->operations.first(opPtr));
+ ndbrequire(transP->operations.hasNext(opPtr) == false);
+
+ int noAttr = 0;
+ int dataSz = 0;
+ Uint32* tmpBuf = signal->theData + 25;
+ const Uint32* headerBuffer = tmpBuf;
+
+ const ResultSetBuffer & rs = opPtr.p->rs;
+ ResultSetInfoBuffer::ConstDataBufferIterator it;
+
+ // extract headers
+ for(rs.first(it); it.curr.i != RNIL; ) {
+ *tmpBuf++ = it.data[0];
+ rs.next(it, ((AttributeHeader*)&it.data[0])->getDataSize() + 1);
+ noAttr++;
+ }
+
+ if (noAttr == 0)
+ return 0;
+
+ const Uint32* dataBuffer = tmpBuf;
+
+ // extract data
+ for(rs.first(it); it.curr.i != RNIL; ) {
+ int sz = ((AttributeHeader*)&it.data[0])->getDataSize();
+ rs.next(it,1);
+ for (int i = 0; i < sz; i++) {
+ *tmpBuf++ = *it.data;
+ rs.next(it,1);
+ dataSz++;
+ }
+ }
+
+ sectionsPtr[UtilExecuteReq::HEADER_SECTION].p = (Uint32 *)headerBuffer;
+ sectionsPtr[UtilExecuteReq::HEADER_SECTION].sz = noAttr;
+ sectionsPtr[UtilExecuteReq::DATA_SECTION].p = (Uint32 *)dataBuffer;
+ sectionsPtr[UtilExecuteReq::DATA_SECTION].sz = dataSz;
+
+ return 1;
+}
+
+void
+DbUtil::reportSequence(Signal* signal, const Transaction * transP){
+ OperationPtr opPtr;
+ ndbrequire(transP->operations.first(opPtr));
+ ndbrequire(transP->operations.hasNext(opPtr) == false);
+
+ if(transP->errorCode == 0){
+ jam(); // OK
+
+ UtilSequenceConf * ret = (UtilSequenceConf *)signal->getDataPtrSend();
+ ret->senderData = transP->clientData;
+ ret->sequenceId = transP->sequence.sequenceId;
+ ret->requestType = transP->sequence.requestType;
+
+ bool ok = false;
+ switch(transP->sequence.requestType){
+ case UtilSequenceReq::CurrVal:
+ case UtilSequenceReq::NextVal:{
+ ok = true;
+ ndbrequire(opPtr.p->rsRecv == 3);
+
+ ResultSetBuffer::DataBufferIterator rsit;
+ ndbrequire(opPtr.p->rs.first(rsit));
+
+ ret->sequenceValue[0] = rsit.data[1];
+ ret->sequenceValue[1] = rsit.data[2];
+ break;
+ }
+ case UtilSequenceReq::Create:
+ ok = true;
+ ret->sequenceValue[0] = 0;
+ ret->sequenceValue[1] = 0;
+ break;
+ }
+ ndbrequire(ok);
+ sendSignal(transP->clientRef, GSN_UTIL_SEQUENCE_CONF, signal,
+ UtilSequenceConf::SignalLength, JBB);
+ return;
+ }
+
+ UtilSequenceRef::ErrorCode errCode = UtilSequenceRef::TCError;
+
+ switch(transP->sequence.requestType)
+ {
+ case UtilSequenceReq::CurrVal:
+ case UtilSequenceReq::NextVal:{
+ if (transP->errorCode == 626)
+ errCode = UtilSequenceRef::NoSuchSequence;
+ break;
+ }
+ case UtilSequenceReq::Create:
+ break;
+ }
+
+ UtilSequenceRef * ret = (UtilSequenceRef *)signal->getDataPtrSend();
+ ret->senderData = transP->clientData;
+ ret->sequenceId = transP->sequence.sequenceId;
+ ret->requestType = transP->sequence.requestType;
+ ret->errorCode = (Uint32)errCode;
+ sendSignal(transP->clientRef, GSN_UTIL_SEQUENCE_REF, signal,
+ UtilSequenceRef::SignalLength, JBB);
+}
+#if 0
+ Ndb ndb("ndb","def");
+ NdbConnection* tConnection = ndb.startTransaction();
+ NdbOperation* tOperation = tConnection->getNdbOperation("SYSTAB_0");
+
+ //#if 0 && API_CODE
+ if( tOperation != NULL ) {
+ tOperation->interpretedUpdateTuple();
+ tOperation->equal((U_Int32)0, keyValue );
+ tNextId_Result = tOperation->getValue((U_Int32)1);
+ tOperation->incValue((U_Int32)1, (U_Int32)8192);
+
+ if (tConnection->execute( Commit ) != -1 ) {
+ U_Int64 tValue = tNextId_Result->u_64_value(); // Read result value
+ theFirstTransId = tValue;
+ theLastTransId = tValue + 8191;
+ closeTransaction(tConnection);
+ return startTransactionLocal(aPriority, nodeId);
+ }
+ }
+ /**
+ * IntialReadSize = 0;
+ * InterpretedSize = incValue(1);
+ * FinalUpdateSize = 0;
+ * FinalReadSize = 1; // Read value
+ * SubroutineSize = 0;
+ */
+#endif
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Transaction execution request
+ * ------------------------------------------------------------------------
+ *
+ * Handle requests to execute a prepared transaction
+ **************************************************************************/
+
+void
+DbUtil::execUTIL_EXECUTE_REQ(Signal* signal)
+{
+ jamEntry();
+
+ UtilExecuteReq * req = (UtilExecuteReq *)signal->getDataPtr();
+ const Uint32 clientRef = req->senderRef;
+ const Uint32 clientData = req->senderData;
+ const Uint32 prepareId = req->getPrepareId();
+ const bool releaseFlag = req->getReleaseFlag();
+
+ if(signal->getNoOfSections() == 0) {
+ // Missing prepare data
+ jam();
+ releaseSections(signal);
+ sendUtilExecuteRef(signal, UtilExecuteRef::MissingDataSection,
+ 0, clientRef, clientData);
+ return;
+ }
+ /*******************************
+ * Get PreparedOperation struct
+ *******************************/
+ PreparedOperationPtr prepOpPtr;
+ c_runningPreparedOperations.first(prepOpPtr);
+ while (!prepOpPtr.isNull() && prepOpPtr.i != prepareId)
+ c_runningPreparedOperations.next(prepOpPtr);
+
+ if (prepOpPtr.i != prepareId) {
+ jam();
+ releaseSections(signal);
+ sendUtilExecuteRef(signal, UtilExecuteRef::IllegalPrepareId,
+ 0, clientRef, clientData);
+ return;
+ }
+
+ prepOpPtr.p->releaseFlag = releaseFlag;
+
+ TransactionPtr transPtr;
+ OperationPtr opPtr;
+ SegmentedSectionPtr headerPtr, dataPtr;
+
+ signal->getSection(headerPtr, UtilExecuteReq::HEADER_SECTION);
+ SectionReader headerReader(headerPtr, getSectionSegmentPool());
+ signal->getSection(dataPtr, UtilExecuteReq::DATA_SECTION);
+ SectionReader dataReader(dataPtr, getSectionSegmentPool());
+
+#if 0 //def EVENT_DEBUG
+ // Debugging
+ printf("DbUtil::execUTIL_EXECUTEL_REQ: Headers (%u): ", headerPtr.sz);
+ Uint32 word;
+ while(headerReader.getWord(&word))
+ printf("H'%.8x ", word);
+ printf("\n");
+ printf("DbUtil::execUTIL_EXECUTEL_REQ: Data (%u): ", dataPtr.sz);
+ headerReader.reset();
+ while(dataReader.getWord(&word))
+ printf("H'%.8x ", word);
+ printf("\n");
+ dataReader.reset();
+#endif
+
+// Uint32 totalDataLen = headerPtr.sz + dataPtr.sz;
+
+ /************************************************************
+ * Seize Transaction record
+ ************************************************************/
+ ndbrequire(c_runningTransactions.seize(transPtr));
+ transPtr.p->gsn = GSN_UTIL_EXECUTE_REQ;
+ transPtr.p->clientRef = clientRef;
+ transPtr.p->clientData = clientData;
+ ndbrequire(transPtr.p->operations.seize(opPtr));
+ opPtr.p->prepOp = prepOpPtr.p;
+ opPtr.p->prepOp_i = prepOpPtr.i;
+
+#if 0 //def EVENT_DEBUG
+ printf("opPtr.p->rs.seize( %u )\n", prepOpPtr.p->rsLen);
+#endif
+ ndbrequire(opPtr.p->rs.seize(prepOpPtr.p->rsLen));
+
+ /***********************************************************
+ * Store signal data on linear memory in Transaction record
+ ***********************************************************/
+ KeyInfoBuffer* keyInfo = &opPtr.p->keyInfo;
+ AttrInfoBuffer* attrInfo = &opPtr.p->attrInfo;
+ AttributeHeader header;
+ Uint32* tempBuf = signal->theData + 25;
+ bool dataComplete = true;
+
+ while(headerReader.getWord((Uint32 *)&header)) {
+ Uint32* bufStart = tempBuf;
+ header.insertHeader(tempBuf++);
+ for(unsigned int i = 0; i < header.getDataSize(); i++) {
+ if (!dataReader.getWord(tempBuf++)) {
+ dataComplete = false;
+ break;
+ }
+ }
+ bool res = true;
+
+#if 0 //def EVENT_DEBUG
+ if (TcKeyReq::getOperationType(prepOpPtr.p->tckey.requestInfo) ==
+ TcKeyReq::Read) {
+ if(prepOpPtr.p->pkBitmask.get(header.getAttributeId()))
+ printf("PrimaryKey\n");
+ }
+ printf("AttrId %u Hdrsz %d Datasz %u \n",
+ header.getAttributeId(),
+ header.getHeaderSize(),
+ header.getDataSize());
+#endif
+
+ if(prepOpPtr.p->pkBitmask.get(header.getAttributeId()))
+ // A primary key attribute
+ res = keyInfo->append(bufStart + header.getHeaderSize(),
+ header.getDataSize());
+
+ switch (TcKeyReq::getOperationType(prepOpPtr.p->tckey.requestInfo)) {
+ case ZREAD:
+ res &= attrInfo->append(bufStart, header.getHeaderSize());
+ break;
+ case ZDELETE:
+ // no attrinfo for Delete
+ break;
+ default:
+ res &= attrInfo->append(bufStart,
+ header.getHeaderSize() + header.getDataSize());
+ }
+
+ if (!res) {
+ // Failed to allocate buffer data
+ jam();
+ releaseSections(signal);
+ sendUtilExecuteRef(signal, UtilExecuteRef::AllocationError,
+ 0, clientRef, clientData);
+ releaseTransaction(transPtr);
+ return;
+ }
+ }
+ if (!dataComplete) {
+ // Missing data in data section
+ jam();
+ releaseSections(signal);
+ sendUtilExecuteRef(signal, UtilExecuteRef::MissingData,
+ 0, clientRef, clientData);
+ releaseTransaction(transPtr);
+ return;
+ }
+
+ const Uint32 l1 = prepOpPtr.p->tckey.attrLen;
+ const Uint32 l2 =
+ prepOpPtr.p->attrInfo.getSize() + opPtr.p->attrInfo.getSize();
+
+ if (TcKeyReq::getOperationType(prepOpPtr.p->tckey.requestInfo) != ZREAD){
+ ndbrequire(l1 == l2);
+ } else {
+#if 0
+ ndbout_c("TcKeyReq::Read");
+#endif
+ }
+
+ releaseSections(signal);
+ transPtr.p->noOfRetries = 3;
+ runTransaction(signal, transPtr);
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: General transaction machinery
+ * ------------------------------------------------------------------------
+ * Executes a prepared transaction
+ **************************************************************************/
+void
+DbUtil::runTransaction(Signal* signal, TransactionPtr transPtr){
+
+ /* Init transaction */
+ transPtr.p->sent = 0;
+ transPtr.p->recv = 0;
+ transPtr.p->errorCode = 0;
+ getTransId(transPtr.p);
+
+ OperationPtr opPtr;
+ ndbrequire(transPtr.p->operations.first(opPtr));
+
+ /* First operation */
+ Uint32 start = 0;
+ TcKeyReq::setStartFlag(start, 1);
+ runOperation(signal, transPtr, opPtr, start);
+ transPtr.p->sent ++;
+
+ /* Rest of operations */
+ start = 0;
+ while(opPtr.i != RNIL){
+ runOperation(signal, transPtr, opPtr, start);
+ transPtr.p->sent ++;
+ }
+ //transPtr.p->print();
+}
+
+void
+DbUtil::runOperation(Signal* signal, TransactionPtr & transPtr,
+ OperationPtr & opPtr, Uint32 start) {
+ Uint32 opI = opPtr.i;
+ Operation * op = opPtr.p;
+ const PreparedOperation * pop = op->prepOp;
+
+ if(!transPtr.p->operations.next(opPtr)){
+ TcKeyReq::setCommitFlag(start, 1); // Last operation
+ TcKeyReq::setExecuteFlag(start, 1);
+ }
+
+#if 0 //def EVENT_DEBUG
+ if (TcKeyReq::getOperationType(pop->tckey.requestInfo) ==
+ TcKeyReq::Read) {
+ printf("TcKeyReq::Read runOperation\n");
+ }
+#endif
+
+ /**
+ * Init operation w.r.t result set
+ */
+ initResultSet(op->rs, pop->rsInfo);
+ op->rs.first(op->rsIterator);
+ op->rsRecv = 0;
+#if 0 //def EVENT_DEBUG
+ printf("pop->rsLen %u\n", pop->rsLen);
+#endif
+ op->rsExpect = 0;
+ op->transPtrI = transPtr.i;
+
+ TcKeyReq * tcKey = (TcKeyReq*)signal->getDataPtrSend();
+ //ndbout << "*** 6 ***"<< endl; pop->print();
+ memcpy(tcKey, &pop->tckey, pop->tckeyLenInBytes);
+ //ndbout << "*** 6b ***"<< endl;
+ //printTCKEYREQ(stdout, signal->getDataPtrSend(),
+ // pop->tckeyLenInBytes >> 2, 0);
+ tcKey->apiConnectPtr = transPtr.p->connectPtr;
+ tcKey->senderData = opI;
+ tcKey->transId1 = transPtr.p->transId[0];
+ tcKey->transId2 = transPtr.p->transId[1];
+ tcKey->requestInfo |= start;
+
+#if 0 //def EVENT_DEBUG
+ // Debugging
+ printf("DbUtil::runOperation: KEYINFO\n");
+ op->keyInfo.print(stdout);
+ printf("DbUtil::runOperation: ATTRINFO\n");
+ op->attrInfo.print(stdout);
+#endif
+
+ /**
+ * Key Info
+ */
+ //KeyInfoBuffer::DataBufferIterator kit;
+ KeyInfoIterator kit;
+ op->keyInfo.first(kit);
+ Uint32 *keyDst = ((Uint32*)tcKey) + pop->keyDataPos;
+ for(Uint32 i = 0; i<8 && kit.curr.i != RNIL; i++, op->keyInfo.next(kit)){
+ keyDst[i] = * kit.data;
+ }
+ //ndbout << "*** 7 ***" << endl;
+ //printTCKEYREQ(stdout, signal->getDataPtrSend(),
+ // pop->tckeyLenInBytes >> 2, 0);
+
+#if 0 //def EVENT_DEBUG
+ printf("DbUtil::runOperation: sendSignal(DBTC_REF, GSN_TCKEYREQ, signal, %d , JBB)\n", pop->tckeyLenInBytes >> 2);
+ printTCKEYREQ(stdout, signal->getDataPtr(), pop->tckeyLenInBytes >> 2,0);
+#endif
+ sendSignal(DBTC_REF, GSN_TCKEYREQ, signal, pop->tckeyLenInBytes >> 2, JBB);
+
+ /**
+ * More the 8 words of key info not implemented
+ */
+ // ndbrequire(kit.curr.i == RNIL); // Yes it is
+
+ /**
+ * KeyInfo
+ */
+ KeyInfo* keyInfo = (KeyInfo *)signal->getDataPtrSend();
+ keyInfo->connectPtr = transPtr.p->connectPtr;
+ keyInfo->transId[0] = transPtr.p->transId[0];
+ keyInfo->transId[1] = transPtr.p->transId[1];
+ sendKeyInfo(signal, keyInfo, op->keyInfo, kit);
+
+ /**
+ * AttrInfo
+ */
+ AttrInfo* attrInfo = (AttrInfo *)signal->getDataPtrSend();
+ attrInfo->connectPtr = transPtr.p->connectPtr;
+ attrInfo->transId[0] = transPtr.p->transId[0];
+ attrInfo->transId[1] = transPtr.p->transId[1];
+
+ AttrInfoIterator ait;
+ pop->attrInfo.first(ait);
+ sendAttrInfo(signal, attrInfo, pop->attrInfo, ait);
+
+ op->attrInfo.first(ait);
+ sendAttrInfo(signal, attrInfo, op->attrInfo, ait);
+}
+
+void
+DbUtil::sendKeyInfo(Signal* signal,
+ KeyInfo* keyInfo,
+ const KeyInfoBuffer & keyBuf,
+ KeyInfoIterator & kit)
+{
+ while(kit.curr.i != RNIL) {
+ Uint32 *keyDst = keyInfo->keyData;
+ Uint32 keyDataLen = 0;
+ for(Uint32 i = 0; i<KeyInfo::DataLength && kit.curr.i != RNIL;
+ i++, keyBuf.next(kit)){
+ keyDst[i] = * kit.data;
+ keyDataLen++;
+ }
+#if 0 //def EVENT_DEBUG
+ printf("DbUtil::sendKeyInfo: sendSignal(DBTC_REF, GSN_KEYINFO, signal, %d , JBB)\n", KeyInfo::HeaderLength + keyDataLen);
+#endif
+ sendSignal(DBTC_REF, GSN_KEYINFO, signal,
+ KeyInfo::HeaderLength + keyDataLen, JBB);
+ }
+}
+
+void
+DbUtil::sendAttrInfo(Signal* signal,
+ AttrInfo* attrInfo,
+ const AttrInfoBuffer & attrBuf,
+ AttrInfoIterator & ait)
+{
+ while(ait.curr.i != RNIL) {
+ Uint32 *attrDst = attrInfo->attrData;
+ Uint32 i = 0;
+ for(i = 0; i<AttrInfo::DataLength && ait.curr.i != RNIL;
+ i++, attrBuf.next(ait)){
+ attrDst[i] = * ait.data;
+ }
+#if 0 //def EVENT_DEBUG
+ printf("DbUtil::sendAttrInfo: sendSignal(DBTC_REF, GSN_ATTRINFO, signal, %d , JBB)\n", AttrInfo::HeaderLength + i);
+#endif
+ sendSignal(DBTC_REF, GSN_ATTRINFO, signal,
+ AttrInfo::HeaderLength + i, JBB);
+ }
+}
+
+void
+DbUtil::initResultSet(ResultSetBuffer & rs,
+ const ResultSetInfoBuffer & rsi){
+
+ ResultSetBuffer::DataBufferIterator rsit;
+ rs.first(rsit);
+
+ ResultSetInfoBuffer::ConstDataBufferIterator rsiit;
+ for(rsi.first(rsiit); rsiit.curr.i != RNIL; rsi.next(rsiit)){
+ ndbrequire(rsit.curr.i != RNIL);
+
+ rsit.data[0] = rsiit.data[0];
+#if 0 //def EVENT_DEBUG
+ printf("Init resultset %u, sz %d\n",
+ rsit.curr.i,
+ ((AttributeHeader*)&rsit.data[0])->getDataSize() + 1);
+#endif
+ rs.next(rsit, ((AttributeHeader*)&rsit.data[0])->getDataSize() + 1);
+ }
+}
+
+void
+DbUtil::getTransId(Transaction * transP){
+
+ Uint32 tmp[2];
+ tmp[0] = c_transId[0];
+ tmp[1] = c_transId[1];
+
+ transP->transId[0] = tmp[0];
+ transP->transId[1] = tmp[1];
+
+ c_transId[1] = tmp[1] + 1;
+}
+
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: Post Execute
+ * ------------------------------------------------------------------------
+ *
+ * Handles result from a sent transaction
+ **************************************************************************/
+
+/**
+ * execTRANSID_AI
+ *
+ * Receive result from transaction
+ *
+ * NOTE: This codes assumes that
+ * TransidAI::DataLength = ResultSetBuffer::getSegmentSize() * n
+ */
+void
+DbUtil::execTRANSID_AI(Signal* signal){
+ jamEntry();
+#if 0 //def EVENT_DEBUG
+ ndbout_c("File: %s line: %u",__FILE__,__LINE__);
+#endif
+
+ const Uint32 opI = signal->theData[0];
+ const Uint32 transId1 = signal->theData[1];
+ const Uint32 transId2 = signal->theData[2];
+ const Uint32 dataLen = signal->length() - 3;
+
+ Operation * opP = c_operationPool.getPtr(opI);
+ TransactionPtr transPtr;
+ c_runningTransactions.getPtr(transPtr, opP->transPtrI);
+
+ ndbrequire(transId1 == transPtr.p->transId[0] &&
+ transId2 == transPtr.p->transId[1]);
+ opP->rsRecv += dataLen;
+
+ /**
+ * Save result
+ */
+ const Uint32 *src = &signal->theData[3];
+ ResultSetBuffer::DataBufferIterator rs = opP->rsIterator;
+
+ ndbrequire(opP->rs.import(rs,src,dataLen));
+ opP->rs.next(rs, dataLen);
+ opP->rsIterator = rs;
+
+ if(!opP->complete()){
+ jam();
+ return;
+ }
+
+ transPtr.p->recv++;
+ if(!transPtr.p->complete()){
+ jam();
+ return;
+ }
+
+ finishTransaction(signal, transPtr);
+}
+
+void
+DbUtil::execTCKEYCONF(Signal* signal){
+ jamEntry();
+#if 0 //def EVENT_DEBUG
+ ndbout_c("File: %s line: %u",__FILE__,__LINE__);
+#endif
+
+ TcKeyConf * keyConf = (TcKeyConf*)signal->getDataPtr();
+
+ //const Uint32 gci = keyConf->gci;
+ const Uint32 transI = keyConf->apiConnectPtr >> 1;
+ const Uint32 confInfo = keyConf->confInfo;
+ const Uint32 transId1 = keyConf->transId1;
+ const Uint32 transId2 = keyConf->transId2;
+
+ Uint32 recv = 0;
+ const Uint32 ops = TcKeyConf::getNoOfOperations(confInfo);
+ for(Uint32 i = 0; i<ops; i++){
+ OperationPtr opPtr;
+ c_operationPool.getPtr(opPtr, keyConf->operations[i].apiOperationPtr);
+
+ ndbrequire(opPtr.p->transPtrI == transI);
+ opPtr.p->rsExpect += keyConf->operations[i].attrInfoLen;
+ if(opPtr.p->complete()){
+ recv++;
+ }
+ }
+
+ /**
+ * Check commit ack marker flag
+ */
+ if (TcKeyConf::getMarkerFlag(confInfo)){
+ signal->theData[0] = transId1;
+ signal->theData[1] = transId2;
+ sendSignal(DBTC_REF, GSN_TC_COMMIT_ACK, signal, 2, JBB);
+ }//if
+
+ TransactionPtr transPtr;
+ c_runningTransactions.getPtr(transPtr, transI);
+ ndbrequire(transId1 == transPtr.p->transId[0] &&
+ transId2 == transPtr.p->transId[1]);
+
+ transPtr.p->recv += recv;
+ if(!transPtr.p->complete()){
+ jam();
+ return;
+ }
+ finishTransaction(signal, transPtr);
+}
+
+void
+DbUtil::execTCKEYREF(Signal* signal){
+ jamEntry();
+#if 0 //def EVENT_DEBUG
+ ndbout_c("File: %s line: %u",__FILE__,__LINE__);
+#endif
+
+ const Uint32 transI = signal->theData[0] >> 1;
+ const Uint32 transId1 = signal->theData[1];
+ const Uint32 transId2 = signal->theData[2];
+ const Uint32 errCode = signal->theData[3];
+
+ TransactionPtr transPtr;
+ c_runningTransactions.getPtr(transPtr, transI);
+ ndbrequire(transId1 == transPtr.p->transId[0] &&
+ transId2 == transPtr.p->transId[1]);
+
+ //if(getClassification(errCode) == PermanentError){
+ //}
+
+ //ndbout << "Transaction error (code: " << errCode << ")" << endl;
+
+ transPtr.p->errorCode = errCode;
+ finishTransaction(signal, transPtr);
+}
+
+void
+DbUtil::execTCROLLBACKREP(Signal* signal){
+ jamEntry();
+#if 0 //def EVENT_DEBUG
+ ndbout_c("File: %s line: %u",__FILE__,__LINE__);
+#endif
+
+ const Uint32 transI = signal->theData[0] >> 1;
+ const Uint32 transId1 = signal->theData[1];
+ const Uint32 transId2 = signal->theData[2];
+ const Uint32 errCode = signal->theData[3];
+
+ TransactionPtr transPtr;
+ c_runningTransactions.getPtr(transPtr, transI);
+ ndbrequire(transId1 == transPtr.p->transId[0] &&
+ transId2 == transPtr.p->transId[1]);
+
+ //if(getClassification(errCode) == PermanentError){
+ //}
+
+#if 0 //def EVENT_DEBUG
+ ndbout << "Transaction error (code: " << errCode << ")" << endl;
+#endif
+
+ if(transPtr.p->noOfRetries > 0){
+ transPtr.p->noOfRetries--;
+ switch(errCode){
+ case 266:
+ case 410:
+ case 1204:
+#if 0
+ ndbout_c("errCode: %d noOfRetries: %d -> retry",
+ errCode, transPtr.p->noOfRetries);
+#endif
+ runTransaction(signal, transPtr);
+ return;
+ }
+ }
+
+ transPtr.p->errorCode = errCode;
+ finishTransaction(signal, transPtr);
+}
+
+void
+DbUtil::finishTransaction(Signal* signal, TransactionPtr transPtr){
+#if 0 //def EVENT_DEBUG
+ ndbout_c("Transaction %x %x completed %s",
+ transPtr.p->transId[0],
+ transPtr.p->transId[1],
+ transPtr.p->errorCode == 0 ? "OK" : "FAILED");
+#endif
+
+ /*
+ How to find the correct RS? Could we have multi-RS/transaction?
+
+ Operation * opP = c_operationPool.getPtr(opI);
+
+ ResultSetBuffer::DataBufferIterator rsit;
+ ndbrequire(opP->rs.first(rsit));
+ ndbout << "F Result: " << rsit.data << endl;
+
+ while (opP->rs.next(rsit)) {
+ ndbout << "R Result: " << rsit.data << endl;
+ }
+ */
+
+ switch(transPtr.p->gsn){
+ case GSN_UTIL_SEQUENCE_REQ:
+ jam();
+ reportSequence(signal, transPtr.p);
+ break;
+ case GSN_UTIL_EXECUTE_REQ:
+ if (transPtr.p->errorCode) {
+ UtilExecuteRef * ret = (UtilExecuteRef *)signal->getDataPtrSend();
+ ret->senderData = transPtr.p->clientData;
+ ret->errorCode = UtilExecuteRef::TCError;
+ ret->TCErrorCode = transPtr.p->errorCode;
+ sendSignal(transPtr.p->clientRef, GSN_UTIL_EXECUTE_REF, signal,
+ UtilExecuteRef::SignalLength, JBB);
+ } else {
+ struct LinearSectionPtr sectionsPtr[UtilExecuteReq::NoOfSections];
+ UtilExecuteConf * ret = (UtilExecuteConf *)signal->getDataPtrSend();
+ ret->senderData = transPtr.p->clientData;
+ if (getResultSet(signal, transPtr.p, sectionsPtr)) {
+#if 0 //def EVENT_DEBUG
+ for (int j = 0; j < 2; j++) {
+ printf("Result set %u %u\n", j,sectionsPtr[j].sz);
+ for (int i=0; i < sectionsPtr[j].sz; i++)
+ printf("H'%.8x ", sectionsPtr[j].p[i]);
+ printf("\n");
+ }
+#endif
+ sendSignal(transPtr.p->clientRef, GSN_UTIL_EXECUTE_CONF, signal,
+ UtilExecuteConf::SignalLength, JBB,
+ sectionsPtr, UtilExecuteReq::NoOfSections);
+ } else
+ sendSignal(transPtr.p->clientRef, GSN_UTIL_EXECUTE_CONF, signal,
+ UtilExecuteConf::SignalLength, JBB);
+ }
+ break;
+ default:
+ ndbrequire(0);
+ break;
+ }
+ releaseTransaction(transPtr);
+}
+
+void
+DbUtil::execUTIL_LOCK_REQ(Signal * signal){
+ jamEntry();
+ UtilLockReq * req = (UtilLockReq*)signal->getDataPtr();
+ const Uint32 lockId = req->lockId;
+
+ LockQueuePtr lockQPtr;
+ if(!c_lockQueues.find(lockQPtr, lockId)){
+ jam();
+ sendLOCK_REF(signal, req, UtilLockRef::NoSuchLock);
+ return;
+ }
+
+// const Uint32 requestInfo = req->requestInfo;
+ const Uint32 senderNode = refToNode(req->senderRef);
+ if(senderNode != getOwnNodeId() && senderNode != 0){
+ jam();
+ sendLOCK_REF(signal, req, UtilLockRef::DistributedLockNotSupported);
+ return;
+ }
+
+ LocalDLFifoList<LockQueueElement> queue(c_lockElementPool,
+ lockQPtr.p->m_queue);
+ if(req->requestInfo & UtilLockReq::TryLock && !queue.isEmpty()){
+ jam();
+ sendLOCK_REF(signal, req, UtilLockRef::LockAlreadyHeld);
+ return;
+ }
+
+ LockQueueElementPtr lockEPtr;
+ if(!c_lockElementPool.seize(lockEPtr)){
+ jam();
+ sendLOCK_REF(signal, req, UtilLockRef::OutOfLockRecords);
+ return;
+ }
+
+ lockEPtr.p->m_senderRef = req->senderRef;
+ lockEPtr.p->m_senderData = req->senderData;
+
+ if(queue.isEmpty()){
+ jam();
+ sendLOCK_CONF(signal, lockQPtr.p, lockEPtr.p);
+ }
+
+ queue.add(lockEPtr);
+}
+
+void
+DbUtil::execUTIL_UNLOCK_REQ(Signal* signal){
+ jamEntry();
+
+ UtilUnlockReq * req = (UtilUnlockReq*)signal->getDataPtr();
+ const Uint32 lockId = req->lockId;
+
+ LockQueuePtr lockQPtr;
+ if(!c_lockQueues.find(lockQPtr, lockId)){
+ jam();
+ sendUNLOCK_REF(signal, req, UtilUnlockRef::NoSuchLock);
+ return;
+ }
+
+ LocalDLFifoList<LockQueueElement> queue(c_lockElementPool,
+ lockQPtr.p->m_queue);
+ LockQueueElementPtr lockEPtr;
+ if(!queue.first(lockEPtr)){
+ jam();
+ sendUNLOCK_REF(signal, req, UtilUnlockRef::NotLockOwner);
+ return;
+ }
+
+ if(lockQPtr.p->m_lockKey != req->lockKey){
+ jam();
+ sendUNLOCK_REF(signal, req, UtilUnlockRef::NotLockOwner);
+ return;
+ }
+
+ sendUNLOCK_CONF(signal, lockQPtr.p, lockEPtr.p);
+ queue.release(lockEPtr);
+
+ if(queue.first(lockEPtr)){
+ jam();
+ sendLOCK_CONF(signal, lockQPtr.p, lockEPtr.p);
+ return;
+ }
+}
+
+void
+DbUtil::sendLOCK_REF(Signal* signal,
+ const UtilLockReq * req, UtilLockRef::ErrorCode err){
+ const Uint32 senderData = req->senderData;
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 lockId = req->lockId;
+
+ UtilLockRef * ref = (UtilLockRef*)signal->getDataPtrSend();
+ ref->senderData = senderData;
+ ref->senderRef = reference();
+ ref->lockId = lockId;
+ ref->errorCode = err;
+ sendSignal(senderRef, GSN_UTIL_LOCK_REF, signal,
+ UtilLockRef::SignalLength, JBB);
+}
+
+void
+DbUtil::sendLOCK_CONF(Signal* signal,
+ LockQueue * lockQP,
+ LockQueueElement * lockEP){
+ const Uint32 senderData = lockEP->m_senderData;
+ const Uint32 senderRef = lockEP->m_senderRef;
+ const Uint32 lockId = lockQP->m_lockId;
+ const Uint32 lockKey = ++lockQP->m_lockKey;
+
+ UtilLockConf * conf = (UtilLockConf*)signal->getDataPtrSend();
+ conf->senderData = senderData;
+ conf->senderRef = reference();
+ conf->lockId = lockId;
+ conf->lockKey = lockKey;
+ sendSignal(senderRef, GSN_UTIL_LOCK_CONF, signal,
+ UtilLockConf::SignalLength, JBB);
+}
+
+void
+DbUtil::sendUNLOCK_REF(Signal* signal,
+ const UtilUnlockReq* req, UtilUnlockRef::ErrorCode err){
+
+ const Uint32 senderData = req->senderData;
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 lockId = req->lockId;
+
+ UtilUnlockRef * ref = (UtilUnlockRef*)signal->getDataPtrSend();
+ ref->senderData = senderData;
+ ref->senderRef = reference();
+ ref->lockId = lockId;
+ ref->errorCode = err;
+ sendSignal(senderRef, GSN_UTIL_UNLOCK_REF, signal,
+ UtilUnlockRef::SignalLength, JBB);
+}
+
+void
+DbUtil::sendUNLOCK_CONF(Signal* signal,
+ LockQueue * lockQP,
+ LockQueueElement * lockEP){
+ const Uint32 senderData = lockEP->m_senderData;
+ const Uint32 senderRef = lockEP->m_senderRef;
+ const Uint32 lockId = lockQP->m_lockId;
+ ++lockQP->m_lockKey;
+
+ UtilUnlockConf * conf = (UtilUnlockConf*)signal->getDataPtrSend();
+ conf->senderData = senderData;
+ conf->senderRef = reference();
+ conf->lockId = lockId;
+ sendSignal(senderRef, GSN_UTIL_UNLOCK_CONF, signal,
+ UtilUnlockConf::SignalLength, JBB);
+}
+
+void
+DbUtil::execUTIL_CREATE_LOCK_REQ(Signal* signal){
+ jamEntry();
+ UtilCreateLockReq req = * (UtilCreateLockReq*)signal->getDataPtr();
+
+ UtilCreateLockRef::ErrorCode err = UtilCreateLockRef::OK;
+
+ do {
+ LockQueuePtr lockQPtr;
+ if(c_lockQueues.find(lockQPtr, req.lockId)){
+ jam();
+ err = UtilCreateLockRef::LockIdAlreadyUsed;
+ break;
+ }
+
+ if(req.lockType != UtilCreateLockReq::Mutex){
+ jam();
+ err = UtilCreateLockRef::UnsupportedLockType;
+ break;
+ }
+
+ if(!c_lockQueues.seize(lockQPtr)){
+ jam();
+ err = UtilCreateLockRef::OutOfLockQueueRecords;
+ break;
+ }
+
+ new (lockQPtr.p) LockQueue(req.lockId);
+ c_lockQueues.add(lockQPtr);
+
+ UtilCreateLockConf * conf = (UtilCreateLockConf*)signal->getDataPtrSend();
+ conf->senderData = req.senderData;
+ conf->senderRef = reference();
+ conf->lockId = req.lockId;
+
+ sendSignal(req.senderRef, GSN_UTIL_CREATE_LOCK_CONF, signal,
+ UtilCreateLockConf::SignalLength, JBB);
+ return;
+ } while(false);
+
+ UtilCreateLockRef * ref = (UtilCreateLockRef*)signal->getDataPtrSend();
+ ref->senderData = req.senderData;
+ ref->senderRef = reference();
+ ref->lockId = req.lockId;
+ ref->errorCode = err;
+
+ sendSignal(req.senderRef, GSN_UTIL_CREATE_LOCK_REF, signal,
+ UtilCreateLockRef::SignalLength, JBB);
+}
+
+void
+DbUtil::execUTIL_DESTORY_LOCK_REQ(Signal* signal){
+ jamEntry();
+
+ UtilDestroyLockReq req = * (UtilDestroyLockReq*)signal->getDataPtr();
+ UtilDestroyLockRef::ErrorCode err = UtilDestroyLockRef::OK;
+ do {
+ LockQueuePtr lockQPtr;
+ if(!c_lockQueues.find(lockQPtr, req.lockId)){
+ jam();
+ err = UtilDestroyLockRef::NoSuchLock;
+ break;
+ }
+
+ LocalDLFifoList<LockQueueElement> queue(c_lockElementPool,
+ lockQPtr.p->m_queue);
+ LockQueueElementPtr lockEPtr;
+ if(!queue.first(lockEPtr)){
+ jam();
+ err = UtilDestroyLockRef::NotLockOwner;
+ break;
+ }
+
+ if(lockQPtr.p->m_lockKey != req.lockKey){
+ jam();
+ err = UtilDestroyLockRef::NotLockOwner;
+ break;
+ }
+
+ /**
+ * OK
+ */
+
+ // Inform all in lock queue that queue has been destroyed
+ UtilLockRef * ref = (UtilLockRef*)signal->getDataPtrSend();
+ ref->lockId = req.lockId;
+ ref->errorCode = UtilLockRef::NoSuchLock;
+ ref->senderRef = reference();
+ LockQueueElementPtr loopPtr = lockEPtr;
+ for(queue.next(loopPtr); !loopPtr.isNull(); queue.next(loopPtr)){
+ jam();
+ ref->senderData = loopPtr.p->m_senderData;
+ const Uint32 senderRef = loopPtr.p->m_senderRef;
+ sendSignal(senderRef, GSN_UTIL_LOCK_REF, signal,
+ UtilLockRef::SignalLength, JBB);
+ }
+ queue.release();
+ c_lockQueues.release(lockQPtr);
+
+ // Send Destroy conf
+ UtilDestroyLockConf* conf=(UtilDestroyLockConf*)signal->getDataPtrSend();
+ conf->senderData = req.senderData;
+ conf->senderRef = reference();
+ conf->lockId = req.lockId;
+ sendSignal(req.senderRef, GSN_UTIL_DESTROY_LOCK_CONF, signal,
+ UtilDestroyLockConf::SignalLength, JBB);
+ return;
+ } while(false);
+
+ UtilDestroyLockRef * ref = (UtilDestroyLockRef*)signal->getDataPtrSend();
+ ref->senderData = req.senderData;
+ ref->senderRef = reference();
+ ref->lockId = req.lockId;
+ ref->errorCode = err;
+ sendSignal(req.senderRef, GSN_UTIL_DESTROY_LOCK_REF, signal,
+ UtilDestroyLockRef::SignalLength, JBB);
+}
+
+template class ArrayPool<DbUtil::Page32>;
diff --git a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp
new file mode 100644
index 00000000000..5499970fde3
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp
@@ -0,0 +1,485 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBUTIL_H
+#define DBUTIL_H
+
+#include <ndb_limits.h>
+#include <SimulatedBlock.hpp>
+#include <NodeBitmask.hpp>
+
+#include <ArrayList.hpp>
+#include <ArrayPool.hpp>
+#include <SLList.hpp>
+#include <DLList.hpp>
+#include <DLFifoList.hpp>
+#include <DataBuffer.hpp>
+#include <KeyTable.hpp>
+
+#include <signaldata/KeyInfo.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/UtilPrepare.hpp>
+#include <signaldata/UtilExecute.hpp>
+#include <signaldata/UtilLock.hpp>
+#include <SimpleProperties.hpp>
+
+#define UTIL_WORDS_PER_PAGE 1023
+
+/**
+ * @class DbUtil
+ * @brief Database utilities
+ *
+ * This block implements transactional services which can be used by other
+ * blocks.
+ *
+ * @section secSequence Module: The Sequence Service
+ *
+ * A sequence is a varaible stored in the database. Each time it is
+ * requested with "NextVal" it returns a unique number. If requested
+ * with "CurrVal" it returns the current number.
+ *
+ * - Request: SEQUENCE_REQ
+ * Requests the 'NextVal' or 'CurrVal' for sequence variable 'sequenceId'.
+ *
+ * - Response: SEQUENCE_CONF / REF (if failure)
+ * Returns value requested.
+ */
+class DbUtil : public SimulatedBlock
+{
+public:
+ DbUtil(const class Configuration & conf);
+ virtual ~DbUtil();
+ BLOCK_DEFINES(DbUtil);
+
+protected:
+ /**
+ * Startup & Misc
+ */
+ void execSTTOR(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+
+ /**
+ * Sequence Service : Public interface
+ */
+ void execUTIL_SEQUENCE_REQ(Signal* signal);
+ void execUTIL_SEQUENCE_REF(Signal* signal);
+ void execUTIL_SEQUENCE_CONF(Signal* signal);
+
+ /**
+ * Prepare Service : Public interface
+ */
+ void execUTIL_PREPARE_REQ(Signal* signal);
+ void execUTIL_PREPARE_CONF(Signal* signal);
+ void execUTIL_PREPARE_REF(Signal* signal);
+
+ /**
+ * Delete Service : Public interface
+ */
+ void execUTIL_DELETE_REQ(Signal* signal);
+ void execUTIL_DELETE_REF(Signal* signal);
+ void execUTIL_DELETE_CONF(Signal* signal);
+
+ /**
+ * Execute Service : Public interface
+ */
+ void execUTIL_EXECUTE_REQ(Signal* signal);
+ void execUTIL_EXECUTE_REF(Signal* signal);
+ void execUTIL_EXECUTE_CONF(Signal* signal);
+
+ /**
+ * Prepare Release Service : Public interface
+ */
+ void execUTIL_RELEASE_REQ(Signal* signal);
+ void execUTIL_RELEASE_CONF(Signal* signal);
+ void execUTIL_RELEASE_REF(Signal* signal);
+
+ /**
+ * Backend interface to a used TC service
+ */
+ void execTCSEIZECONF(Signal* signal);
+ void execTCKEYCONF(Signal* signal);
+ void execTCKEYREF(Signal* signal);
+ void execTCROLLBACKREP(Signal* signal);
+ void execTCKEY_FAILCONF(Signal* signal);
+ void execTCKEY_FAILREF(Signal* signal);
+ void execTRANSID_AI(Signal* signal);
+
+ /**
+ * Backend interface to a used DICT service
+ */
+ void execGET_TABINFOREF(Signal*);
+ void execGET_TABINFO_CONF(Signal* signal);
+
+private:
+
+public:
+ struct PreparedOperation;
+
+ typedef DataBuffer<11> KeyInfoBuffer;
+ typedef KeyInfoBuffer::ConstDataBufferIterator KeyInfoIterator;
+ typedef DataBuffer<11> AttrInfoBuffer;
+ typedef AttrInfoBuffer::ConstDataBufferIterator AttrInfoIterator;
+ typedef DataBuffer<11> ResultSetBuffer;
+ typedef DataBuffer<11> ResultSetInfoBuffer;
+ typedef DataBuffer<1> AttrMappingBuffer;
+
+ /**
+ * @struct Page32
+ * @brief For storing SimpleProperties objects and similar temporary data
+ */
+ struct Page32 {
+ Uint32 data[UTIL_WORDS_PER_PAGE];
+ Uint32 nextPool; // Note: This used as data when seized
+ };
+
+ /**
+ * @struct Prepare
+ * @brief Info regarding prepare request (contains a prepared operation)
+ *
+ * The prepare phase interprets the table and attribute names sent
+ * in the prepare request from the client and asks DICT for meta
+ * information.
+ */
+ struct Prepare {
+ Prepare(ArrayPool<Page32> & ap) : preparePages(ap) {}
+
+ /*** Client info ***/
+ Uint32 clientRef;
+ Uint32 clientData;
+
+ /**
+ * SimpleProp sent in UTIL_PREPARE_REQ
+ *
+ * Example format:
+ * - UtilPrepareReq::NoOfOperations=1
+ * - UtilPrepareReq::OperationType=UtilPrepareReq::Delete
+ * - UtilPrepareReq::TableName="SYSTAB_0"
+ * - UtilPrepareReq::AttributeName="SYSKEY_0"
+ */
+ Uint32 prepDataLen;
+ Array<Page32> preparePages;
+
+ /*** PreparedOperation constructed in Prepare phase ***/
+ Ptr<PreparedOperation> prepOpPtr;
+
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+
+ void print() const {
+ ndbout << "[-Prepare-" << endl
+ << " clientRef: " << clientRef
+ << ", clientData: " << clientData
+ << "]" << endl;
+ }
+ };
+
+ /**
+ * @struct PreparedOperation
+ * @brief Contains instantiated TcKeyReq signaldata for operation
+ *
+ * The prepare phase is finished by storing the request in a
+ * PreparedOperation record.
+ */
+ struct PreparedOperation {
+ PreparedOperation(AttrMappingBuffer::DataBufferPool & am,
+ AttrInfoBuffer::DataBufferPool & ai,
+ ResultSetInfoBuffer::DataBufferPool & rs) :
+ releaseFlag(false), attrMapping(am), attrInfo(ai), rsInfo(rs)
+ {
+ pkBitmask.clear();
+ }
+
+ /*** Various Operation Info ***/
+ Uint32 keyLen; // Length of primary key (fixed size is assumed)
+ Uint32 rsLen; // Size of result set
+ Uint32 noOfKeyAttr; // Number of key attributes
+ Uint32 noOfAttr; // Number of attributes
+ bool releaseFlag; // flag if operation release after completion
+
+ /**
+ * Attribute Mapping
+ *
+ * This datastructure (buffer of AttributeHeader:s) are used to map
+ * each execute request to a TCKEYREQ train of signals.
+ *
+ * The datastructure contains (AttributeId, Position) pairs, where
+ * - AttributeId is id used in database, and
+ * - Position is position of attribute value in TCKEYREQ keyinfo
+ * part of the train of signals which will be send to TC.
+ * Position == 0x3fff means it should *not* be sent
+ * in keyinfo part.
+ */
+ AttrMappingBuffer attrMapping;
+
+ /*** First signal in tckeyreq train ***/
+ Uint32 tckeyLenInBytes; // TcKeyReq total signal length (in bytes)
+ Uint32 keyDataPos; // Where to store keydata[] in tckey signal
+ // (in #words from base in tckey signal)
+ TcKeyReq tckey; // Signaldata for first signal in train
+
+ /*** Attrinfo signals sent to TC (part of tckeyreq train) ***/
+ AttrInfoBuffer attrInfo;
+
+ /*** Result of executed operation ***/
+ ResultSetInfoBuffer rsInfo;
+
+ Bitmask<MAX_ATTRIBUTES_IN_TABLE> pkBitmask;
+
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+
+ void print() const {
+ ndbout << "[-PreparedOperation-" << endl
+ << " keyLen: " << keyLen
+ << ", rsLen: " << rsLen
+ << ", noOfKeyAttr: " << noOfKeyAttr
+ << ", noOfAttr: " << noOfAttr
+ << ", tckeyLenInBytes: " << tckeyLenInBytes
+ << ", keyDataPos: " << keyDataPos << endl
+ << "-AttrMapping- (AttrId, KeyPos)-pairs "
+ << "(Pos=3fff if non-key attr):" << endl;
+ attrMapping.print(stdout);
+ ndbout << "[-tckey- ";
+ printTCKEYREQ(stdout, (Uint32*)&tckey, 8, 0);
+ ndbout << "[-attrInfo- ";
+ attrInfo.print(stdout);
+ ndbout << "[-rsInfo- ";
+ rsInfo.print(stdout);
+ ndbout << "]]]]" << endl;
+ }
+ };
+
+ /**
+ * @struct Operation
+ * @brief Used in execution (contains resultset and buffers for result)
+ */
+ struct Operation {
+ Operation(KeyInfoBuffer::DataBufferPool & ki,
+ AttrInfoBuffer::DataBufferPool & ai,
+ ResultSetBuffer::DataBufferPool & _rs) :
+ prepOp_i(RNIL), keyInfo(ki), attrInfo(ai), rs(_rs) {}
+
+ PreparedOperation * prepOp;
+ Uint32 prepOp_i;
+ KeyInfoBuffer keyInfo;
+ AttrInfoBuffer attrInfo;
+ ResultSetBuffer rs;
+ ResultSetBuffer::DataBufferIterator rsIterator;
+
+ Uint32 transPtrI;
+
+ Uint32 rsRecv;
+ Uint32 rsExpect;
+ inline bool complete() const { return rsRecv == rsExpect; }
+
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+
+ void print() const {
+ ndbout << "[-Operation-" << endl
+ << " transPtrI: " << transPtrI
+ << ", rsRecv: " << rsRecv;
+ ndbout << "[-PreparedOperation-" << endl;
+ prepOp->print();
+ ndbout << "[-keyInfo-" << endl;
+ keyInfo.print(stdout);
+ ndbout << "[-attrInfo-" << endl;
+ attrInfo.print(stdout);
+ ndbout << "]]" << endl;
+ }
+ };
+
+ /**
+ * @struct Transaction
+ * @brief Used in execution (contains list of operations)
+ */
+ struct Transaction {
+ Transaction(ArrayPool<Page32> & ap, ArrayPool<Operation> & op) :
+ executePages(ap), operations(op) {}
+
+ Uint32 clientRef;
+ Uint32 clientData;
+ Array<Page32> executePages;
+
+ Uint32 gsn; // Request type (SEQUENCE, DELETE, etc)
+ union {
+ /**
+ * Sequence transaction
+ */
+ struct {
+ Uint32 sequenceId;
+ Uint32 requestType;
+ } sequence;
+ };
+
+ Uint32 connectPtr;
+ Uint32 transId[2];
+ SLList<Operation> operations;
+
+ Uint32 errorCode;
+ Uint32 noOfRetries;
+ Uint32 sent; // No of operations sent
+ Uint32 recv; // No of completed operations received
+ inline bool complete() const { return sent == recv; };
+
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+
+ void print() const {
+ ndbout << "[-Transaction-" << endl
+ << " clientRef: " << clientRef
+ << ", clientData: " << clientData
+ << ", gsn: " << gsn
+ << ", errorCode: " << errorCode
+ << endl
+ << " sent: " << sent << " operations"
+ << ", recv: " << recv << " completed operations";
+ OperationPtr opPtr;
+ this->operations.first(opPtr);
+ while(opPtr.i != RNIL){
+ ndbout << "[-Operation-" << endl;
+ opPtr.p->print();
+ this->operations.next(opPtr);
+ }
+ ndbout << "]" << endl;
+ }
+ };
+
+ typedef Ptr<Page32> Page32Ptr;
+ typedef Ptr<Prepare> PreparePtr;
+ typedef Ptr<Transaction> TransactionPtr;
+ typedef Ptr<Operation> OperationPtr;
+ typedef Ptr<PreparedOperation> PreparedOperationPtr;
+
+ Uint32 c_transId[2];
+ ArrayPool<Page32> c_pagePool;
+ ArrayPool<Prepare> c_preparePool;
+ ArrayPool<Operation> c_operationPool;
+ ArrayPool<PreparedOperation> c_preparedOperationPool;
+ ArrayPool<Transaction> c_transactionPool;
+
+ DataBuffer<1>::DataBufferPool c_attrMappingPool;
+ DataBuffer<11>::DataBufferPool c_dataBufPool;
+ DLList<Prepare> c_runningPrepares;
+ DLList<PreparedOperation> c_runningPreparedOperations;
+ DLList<Transaction> c_seizingTransactions; // Being seized at TC
+ DLList<Transaction> c_runningTransactions; // Seized and now exec.
+
+ void getTransId(Transaction *);
+ void initResultSet(ResultSetBuffer &, const ResultSetInfoBuffer &);
+ void runTransaction(Signal* signal, TransactionPtr);
+ void runOperation(Signal* signal, TransactionPtr &, OperationPtr &, Uint32);
+ void sendKeyInfo(Signal* signal,
+ KeyInfo* keyInfo,
+ const KeyInfoBuffer & keyBuf,
+ KeyInfoIterator & kit);
+ void sendAttrInfo(Signal*,
+ AttrInfo* attrInfo,
+ const AttrInfoBuffer &,
+ AttrInfoIterator & ait);
+ int getResultSet(Signal* signal, const Transaction * transP,
+ struct LinearSectionPtr sectionsPtr[]);
+ void finishTransaction(Signal*, TransactionPtr);
+ void releaseTransaction(TransactionPtr transPtr);
+ void hardcodedPrepare();
+ void connectTc(Signal* signal);
+ void reportSequence(Signal*, const Transaction *);
+ void readPrepareProps(Signal* signal,
+ SimpleProperties::Reader* reader,
+ Uint32 senderData);
+ void prepareOperation(Signal*, PreparePtr);
+ void sendUtilPrepareRef(Signal*, UtilPrepareRef::ErrorCode, Uint32, Uint32);
+ void sendUtilExecuteRef(Signal*, UtilExecuteRef::ErrorCode,
+ Uint32, Uint32, Uint32);
+ void releasePrepare(PreparePtr);
+ void releasePreparedOperation(PreparedOperationPtr);
+
+ /***************************************************************************
+ * Lock manager
+ */
+ struct LockQueueElement {
+ Uint32 m_senderData;
+ Uint32 m_senderRef;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+ typedef Ptr<LockQueueElement> LockQueueElementPtr;
+
+ struct LockQueue {
+ LockQueue(){}
+ LockQueue(Uint32 id) : m_queue() { m_lockId = id; m_lockKey = 0;}
+ union {
+ Uint32 m_lockId;
+ Uint32 key;
+ };
+ Uint32 m_lockKey;
+ DLFifoList<LockQueueElement>::Head m_queue;
+ union {
+ Uint32 nextHash;
+ Uint32 nextPool;
+ };
+ Uint32 prevHash;
+
+ Uint32 hashValue() const {
+ return m_lockId;
+ }
+ bool equal(const LockQueue & rec) const {
+ return m_lockId == rec.m_lockId;
+ }
+ };
+ typedef Ptr<LockQueue> LockQueuePtr;
+
+
+ ArrayPool<LockQueue> c_lockQueuePool;
+ ArrayPool<LockQueueElement> c_lockElementPool;
+ KeyTable<LockQueue> c_lockQueues;
+
+ void execUTIL_CREATE_LOCK_REQ(Signal* signal);
+ void execUTIL_DESTORY_LOCK_REQ(Signal* signal);
+ void execUTIL_LOCK_REQ(Signal* signal);
+ void execUTIL_UNLOCK_REQ(Signal* signal);
+
+ void sendLOCK_REF(Signal*, const UtilLockReq * req, UtilLockRef::ErrorCode);
+ void sendLOCK_CONF(Signal*, LockQueue *, LockQueueElement *);
+
+ void sendUNLOCK_REF(Signal*, const UtilUnlockReq*, UtilUnlockRef::ErrorCode);
+ void sendUNLOCK_CONF(Signal*, LockQueue *, LockQueueElement *);
+
+ // For testing of mutex:es
+ void mutex_created(Signal* signal, Uint32 mutexId, Uint32 retVal);
+ void mutex_destroyed(Signal* signal, Uint32 mutexId, Uint32 retVal);
+ void mutex_locked(Signal* signal, Uint32 mutexId, Uint32 retVal);
+ void mutex_unlocked(Signal* signal, Uint32 mutexId, Uint32 retVal);
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.txt b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.txt
new file mode 100644
index 00000000000..cc8c1985009
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.txt
@@ -0,0 +1,68 @@
+UTIL Protocols
+--------------
+Transactions are executed in two phases:
+1) PREPARE
+2) EXECUTE
+
+
+PREPARE PHASE
+-------------
+1) ** REQUEST **
+ Client (any block) requests prepare service from Util:
+
+ Client --UTIL_PREPARE_REQ--> Util
+ ...
+ Client --UTIL_PREPARE_REQ--> Util
+
+2) ** DICTINFO **
+ Util requests Dict for information about table:
+
+ Util --GET_TABINFOREQ--> Dict
+
+ Util <--DICTTABINFO-- Dict
+ ...
+ Util <--DICTTABINFO-- Dict
+
+3) ** PREPARE **
+ Operation (= transaction) is prepared (DbUtil::prepareOperation)
+
+ a) AttrMapping is created (a map used to read of the
+ actual execute request attribute values and put them in KEYINFO)
+
+ b) TC Signal train is prepared
+
+4) ** CONFIRM **
+ Request is confirmed
+
+ Client <--UTIL_PREPARE_CONF-- Util
+
+
+EXECUTE PHASE
+-------------
+1) Client (any block) requests execute service from Util:
+ (Execute can be INSERT, DELETE,...)
+
+ Client --UTIL_EXECUTE_REQ--> Util (Multi-signals not yet implemented)
+ ...
+ Client --UTIL_EXECUTE_REQ--> Util
+
+2) Util --TCKEYREQ--> tc
+
+ Util --KEYINFO--> tc (sometimes) (Not yet implemented)
+ ...
+ Util --KEYINFO--> tc
+
+ Util --ATTRINFO--> tc (sometimes)
+ ...
+ Util --ATTRINFO--> tc
+
+3) Util <--TCKEYCONF-- tc
+
+ Util --TC_COMMIT_ACK-->tc (sometimes)
+
+ (in parallel with)
+
+ Util <--TRANSID_AI-- tc (sometimes)
+ ...
+ Util <--TRANSID_AI-- tc
+
diff --git a/storage/ndb/src/kernel/blocks/dbutil/Makefile.am b/storage/ndb/src/kernel/blocks/dbutil/Makefile.am
new file mode 100644
index 00000000000..925356c2f76
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbutil/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LIBRARIES = libdbutil.a
+
+libdbutil_a_SOURCES = DbUtil.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libdbutil.dsp
+
+libdbutil.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libdbutil_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/grep/Grep.cpp b/storage/ndb/src/kernel/blocks/grep/Grep.cpp
new file mode 100644
index 00000000000..0527c5415ab
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/grep/Grep.cpp
@@ -0,0 +1,2010 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Grep.hpp"
+#include <ndb_version.h>
+
+#include <NdbTCP.h>
+#include <Bitmask.hpp>
+
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/CheckNodeGroups.hpp>
+#include <signaldata/GrepImpl.hpp>
+#include <signaldata/RepImpl.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/WaitGCP.hpp>
+#include <GrepEvent.hpp>
+#include <AttributeHeader.hpp>
+
+#define CONTINUEB_DELAY 500
+#define SSREPBLOCKNO 2
+#define PSREPBLOCKNO 2
+
+//#define DEBUG_GREP
+//#define DEBUG_GREP_SUBSCRIPTION
+//#define DEBUG_GREP_TRANSFER
+//#define DEBUG_GREP_APPLY
+//#define DEBUG_GREP_DELETE
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: STARTUP of GREP Block, etc
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+static Uint32 g_TypeOfStart = NodeState::ST_ILLEGAL_TYPE;
+void
+Grep::getNodeGroupMembers(Signal* signal) {
+ jam();
+ /**
+ * Ask DIH for nodeGroupMembers
+ */
+ CheckNodeGroups * sd = (CheckNodeGroups*)signal->getDataPtrSend();
+ sd->blockRef = reference();
+ sd->requestType =
+ CheckNodeGroups::Direct |
+ CheckNodeGroups::GetNodeGroupMembers;
+ sd->nodeId = getOwnNodeId();
+ EXECUTE_DIRECT(DBDIH, GSN_CHECKNODEGROUPSREQ, signal,
+ CheckNodeGroups::SignalLength);
+ jamEntry();
+
+ c_nodeGroup = sd->output;
+ c_noNodesInGroup = 0;
+ for (int i = 0; i < MAX_NDB_NODES; i++) {
+ if (sd->mask.get(i)) {
+ if (i == getOwnNodeId()) c_idInNodeGroup = c_noNodesInGroup;
+ c_nodesInGroup[c_noNodesInGroup] = i;
+ c_noNodesInGroup++;
+ }
+ }
+ ndbrequire(c_noNodesInGroup > 0); // at least 1 node in the nodegroup
+
+#ifdef NODEFAIL_DEBUG
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ ndbout_c ("Grep: NodeGroup %u, me %u, me in group %u, member[%u] %u",
+ c_nodeGroup, getOwnNodeId(), c_idInNodeGroup,
+ i, c_nodesInGroup[i]);
+ }
+#endif
+}
+
+
+void
+Grep::execSTTOR(Signal* signal)
+{
+ jamEntry();
+ const Uint32 startphase = signal->theData[1];
+ const Uint32 typeOfStart = signal->theData[7];
+ if (startphase == 3)
+ {
+ jam();
+ signal->theData[0] = reference();
+ g_TypeOfStart = typeOfStart;
+ sendSignal(NDBCNTR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+ return;
+ }
+ if(startphase == 5) {
+ jam();
+ /**
+ * we don't want any log/meta records comming to use
+ * until we are done with the recovery.
+ */
+ if (g_TypeOfStart == NodeState::ST_NODE_RESTART) {
+ jam();
+ pspart.m_recoveryMode = true;
+ getNodeGroupMembers(signal);
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ Uint32 ref =numberToRef(GREP, c_nodesInGroup[i]);
+ if (ref != reference())
+ sendSignal(ref, GSN_GREP_START_ME, signal,
+ 1 /*SumaStartMe::SignalLength*/, JBB);
+ }
+ } else pspart.m_recoveryMode = false;
+
+ }
+
+ if(startphase == 7) {
+ jam();
+ if (g_TypeOfStart == NodeState::ST_NODE_RESTART) {
+ pspart.m_recoveryMode = false;
+ }
+ }
+
+ sendSTTORRY(signal);
+}
+
+
+void
+Grep::PSPart::execSTART_ME(Signal* signal)
+{
+ jamEntry();
+ GrepStartMe * me =(GrepStartMe*)signal->getDataPtr();
+ BlockReference ref = me->senderRef;
+ GrepAddSubReq* const addReq = (GrepAddSubReq *)signal->getDataPtr();
+
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.first(c_subPtr);
+ for(; !c_subPtr.isNull(); c_subscriptions.next(c_subPtr)) {
+ jam();
+ subPtr.i = c_subPtr.curr.i;
+ subPtr.p = c_subscriptions.getPtr(subPtr.i);
+ addReq->subscriptionId = subPtr.p->m_subscriptionId;
+ addReq->subscriptionKey = subPtr.p->m_subscriptionKey;
+ addReq->subscriberData = subPtr.p->m_subscriberData;
+ addReq->subscriptionType = subPtr.p->m_subscriptionType;
+ addReq->senderRef = subPtr.p->m_coordinatorRef;
+ addReq->subscriberRef =subPtr.p->m_subscriberRef;
+
+ sendSignal(ref,
+ GSN_GREP_ADD_SUB_REQ,
+ signal,
+ GrepAddSubReq::SignalLength,
+ JBB);
+ }
+
+ addReq->subscriptionId = 0;
+ addReq->subscriptionKey = 0;
+ addReq->subscriberData = 0;
+ addReq->subscriptionType = 0;
+ addReq->senderRef = 0;
+ addReq->subscriberRef = 0;
+
+ sendSignal(ref,
+ GSN_GREP_ADD_SUB_REQ,
+ signal,
+ GrepAddSubReq::SignalLength,
+ JBB);
+}
+
+void
+Grep::PSPart::execGREP_ADD_SUB_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepAddSubReq * const grepReq = (GrepAddSubReq *)signal->getDataPtr();
+ const Uint32 subId = grepReq->subscriptionId;
+ const Uint32 subKey = grepReq->subscriptionKey;
+ const Uint32 subData = grepReq->subscriberData;
+ const Uint32 subType = grepReq->subscriptionType;
+ const Uint32 coordinatorRef = grepReq->senderRef;
+
+ /**
+ * this is ref to the REP node for this subscription.
+ */
+ const Uint32 subRef = grepReq->subscriberRef;
+
+ if(subId!=0 && subKey!=0) {
+ jam();
+ SubscriptionPtr subPtr;
+ ndbrequire( c_subscriptionPool.seize(subPtr));
+ subPtr.p->m_coordinatorRef = coordinatorRef;
+ subPtr.p->m_subscriptionId = subId;
+ subPtr.p->m_subscriptionKey = subKey;
+ subPtr.p->m_subscriberRef = subRef;
+ subPtr.p->m_subscriberData = subData;
+ subPtr.p->m_subscriptionType = subType;
+
+ c_subscriptions.add(subPtr);
+ }
+ else {
+ jam();
+ GrepAddSubConf * conf = (GrepAddSubConf *)grepReq;
+ conf->noOfSub =
+ c_subscriptionPool.getSize()-c_subscriptionPool.getNoOfFree();
+ sendSignal(signal->getSendersBlockRef(),
+ GSN_GREP_ADD_SUB_CONF,
+ signal,
+ GrepAddSubConf::SignalLength,
+ JBB);
+ }
+}
+
+void
+Grep::PSPart::execGREP_ADD_SUB_REF(Signal* signal)
+{
+ /**
+ * @todo fix error stuff
+ */
+}
+
+void
+Grep::PSPart::execGREP_ADD_SUB_CONF(Signal* signal)
+{
+ jamEntry();
+ GrepAddSubConf* const conf = (GrepAddSubConf *)signal->getDataPtr();
+ Uint32 noOfSubscriptions = conf->noOfSub;
+ Uint32 noOfRestoredSubscriptions =
+ c_subscriptionPool.getSize()-c_subscriptionPool.getNoOfFree();
+ if(noOfSubscriptions!=noOfRestoredSubscriptions) {
+ jam();
+ /**
+ *@todo send ref signal
+ */
+ ndbrequire(false);
+ }
+}
+
+void
+Grep::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+ ReadNodesConf * conf = (ReadNodesConf *)signal->getDataPtr();
+
+#if 0
+ ndbout_c("Grep: Recd READ_NODESCONF");
+#endif
+
+ /******************************
+ * Check which REP nodes exist
+ ******************************/
+ Uint32 i;
+ for (i = 1; i < MAX_NODES; i++)
+ {
+ jam();
+#if 0
+ ndbout_c("Grep: Found node %d of type %d", i, getNodeInfo(i).getType());
+#endif
+ if (getNodeInfo(i).getType() == NodeInfo::REP)
+ {
+ jam();
+ /**
+ * @todo This should work for more than ONE rep node!
+ */
+ pscoord.m_repRef = numberToRef(PSREPBLOCKNO, i);
+ pspart.m_repRef = numberToRef(PSREPBLOCKNO, i);
+#if 0
+ ndbout_c("Grep: REP node %d detected", i);
+#endif
+ }
+ }
+
+ /*****************************
+ * Check which DB nodes exist
+ *****************************/
+ m_aliveNodes.clear();
+
+ Uint32 count = 0;
+ for(i = 0; i<MAX_NDB_NODES; i++)
+ {
+ if (NodeBitmask::get(conf->allNodes, i))
+ {
+ jam();
+ count++;
+
+ NodePtr node;
+ ndbrequire(m_nodes.seize(node));
+
+ node.p->nodeId = i;
+ if (NodeBitmask::get(conf->inactiveNodes, i))
+ {
+ node.p->alive = 0;
+ }
+ else
+ {
+ node.p->alive = 1;
+ m_aliveNodes.set(i);
+ }
+ }
+ }
+ m_masterNodeId = conf->masterNodeId;
+ ndbrequire(count == conf->noOfNodes);
+ sendSTTORRY(signal);
+}
+
+void
+Grep::sendSTTORRY(Signal* signal)
+{
+ signal->theData[0] = 0;
+ signal->theData[3] = 1;
+ signal->theData[4] = 3;
+ signal->theData[5] = 5;
+ signal->theData[6] = 7;
+ signal->theData[7] = 255; // No more start phases from missra
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 8, JBB);
+}
+
+void
+Grep::execNDB_STTOR(Signal* signal)
+{
+ jamEntry();
+}
+
+void
+Grep::execDUMP_STATE_ORD(Signal* signal)
+{
+ jamEntry();
+ //Uint32 tCase = signal->theData[0];
+
+#if 0
+ if(sscoord.m_repRef == 0)
+ {
+ ndbout << "Grep: Recd DUMP signal but has no connection with REP node"
+ << endl;
+ return;
+ }
+#endif
+
+ /*
+ switch (tCase)
+ {
+ case 8100: sscoord.grepReq(signal, GrepReq::START_SUBSCR); break;
+ case 8102: sscoord.grepReq(signal, GrepReq::START_METALOG); break;
+ case 8104: sscoord.grepReq(signal, GrepReq::START_METASCAN); break;
+ case 8106: sscoord.grepReq(signal, GrepReq::START_DATALOG); break;
+ case 8108: sscoord.grepReq(signal, GrepReq::START_DATASCAN); break;
+ case 8110: sscoord.grepReq(signal, GrepReq::STOP_SUBSCR); break;
+ case 8500: sscoord.grepReq(signal, GrepReq::REMOVE_BUFFERS); break;
+ case 8300: sscoord.grepReq(signal, GrepReq::SLOWSTOP); break;
+ case 8400: sscoord.grepReq(signal, GrepReq::FASTSTOP); break;
+ case 8600: sscoord.grepReq(signal, GrepReq::CREATE_SUBSCR); break;
+ case 8700: sscoord.dropTable(signal,(Uint32)signal->theData[1]);break;
+ default: break;
+ }
+ */
+}
+
+/**
+ * Signal received when REP node has failed
+ */
+void
+Grep::execAPI_FAILREQ(Signal* signal)
+{
+ jamEntry();
+ //Uint32 failedApiNode = signal->theData[0];
+ //BlockReference retRef = signal->theData[1];
+
+ /**
+ * @todo We should probably do something smart if the
+ * PS REP node fails???? /Lars
+ */
+
+#if 0
+ ndbout_c("Grep: API_FAILREQ received for API node %d.", failedApiNode);
+#endif
+
+ /**
+ * @note This signal received is NOT allowed to send any CONF
+ * signal, since this would screw up TC/DICT to API
+ * "connections".
+ */
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: GREP Control
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+void
+Grep::execGREP_REQ(Signal* signal)
+{
+ jamEntry();
+
+ //GrepReq * req = (GrepReq *)signal->getDataPtr();
+
+ /**
+ * @todo Fix so that request is redirected to REP Server
+ * Obsolete?
+ * Was: sscoord.grepReq(signal, req->request);
+ */
+ ndbout_c("Warning! REP commands can only be executed at REP SERVER prompt!");
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: NODE STATE HANDLING
+ * ------------------------------------------------------------------------
+ **************************************************************************/
+void
+Grep::execNODE_FAILREP(Signal* signal)
+{
+ jamEntry();
+ NodeFailRep * rep = (NodeFailRep*)signal->getDataPtr();
+ bool changed = false;
+
+ NodePtr nodePtr;
+ for(m_nodes.first(nodePtr); nodePtr.i != RNIL; m_nodes.next(nodePtr))
+ {
+ jam();
+ if (NodeBitmask::get(rep->theNodes, nodePtr.p->nodeId))
+ {
+ jam();
+
+ if (nodePtr.p->alive)
+ {
+ jam();
+ ndbassert(m_aliveNodes.get(nodePtr.p->nodeId));
+ changed = true;
+ }
+ else
+ {
+ ndbassert(!m_aliveNodes.get(nodePtr.p->nodeId));
+ }
+
+ nodePtr.p->alive = 0;
+ m_aliveNodes.clear(nodePtr.p->nodeId);
+ }
+ }
+
+
+ /**
+ * Problem: Fix a node failure running a protocol
+ *
+ * 1. Coordinator node of a protocol dies
+ * - Elect a new coordinator
+ * - send ref to user
+ *
+ * 2. Non-coordinator dies.
+ * - make coordinator aware of this
+ * so that coordinator does not wait for
+ * conf from faulty node
+ * - node recovery will restore the non-coordinator.
+ *
+ */
+}
+
+void
+Grep::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+
+ //const Uint32 senderRef = signal->theData[0];
+ const Uint32 inclNode = signal->theData[1];
+
+ NodePtr node;
+ for(m_nodes.first(node); node.i != RNIL; m_nodes.next(node))
+ {
+ jam();
+ const Uint32 nodeId = node.p->nodeId;
+ if (inclNode == nodeId) {
+ jam();
+
+ ndbrequire(node.p->alive == 0);
+ ndbassert(!m_aliveNodes.get(nodeId));
+
+ node.p->alive = 1;
+ m_aliveNodes.set(nodeId);
+
+ break;
+ }
+ }
+
+ /**
+ * @todo: if we include this DIH's got to be prepared, later if needed...
+ */
+#if 0
+ signal->theData[0] = reference();
+
+ sendSignal(senderRef, GSN_INCL_NODECONF, signal, 1, JBB);
+#endif
+}
+
+
+/**
+ * Helper methods
+ */
+void
+Grep::PSCoord::prepareOperationRec(SubCoordinatorPtr subPtr,
+ BlockReference subscriber,
+ Uint32 subId,
+ Uint32 subKey,
+ Uint32 request)
+{
+ subPtr.p->m_coordinatorRef = reference();
+ subPtr.p->m_subscriberRef = subscriber;
+ subPtr.p->m_subscriberData = subPtr.i;
+ subPtr.p->m_subscriptionId = subId;
+ subPtr.p->m_subscriptionKey = subKey;
+ subPtr.p->m_outstandingRequest = request;
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: CREATE SUBSCRIPTION ID
+ * ------------------------------------------------------------------------
+ *
+ * Requests SUMA to create a unique subscription id
+ **************************************************************************/
+
+void
+Grep::PSCoord::execGREP_CREATE_SUBID_REQ(Signal* signal)
+{
+ jamEntry();
+
+ CreateSubscriptionIdReq * req =
+ (CreateSubscriptionIdReq*)signal->getDataPtr();
+ BlockReference ref = signal->getSendersBlockRef();
+
+ SubCoordinatorPtr subPtr;
+ if( !c_subCoordinatorPool.seize(subPtr)) {
+ jam();
+ SubCoordinator sub;
+ sub.m_subscriberRef = ref;
+ sub.m_subscriptionId = 0;
+ sub.m_subscriptionKey = 0;
+ sendRefToSS(signal, sub, GrepError::SUBSCRIPTION_ID_NOMEM );
+ return;
+ }
+ prepareOperationRec(subPtr,
+ ref,
+ 0,0,
+ GSN_CREATE_SUBID_REQ);
+
+
+ ndbout_c("SUBID_REQ Ref %d",ref);
+ req->senderData=subPtr.p->m_subscriberData;
+
+ sendSignal(SUMA_REF, GSN_CREATE_SUBID_REQ, signal,
+ SubCreateReq::SignalLength, JBB);
+
+#if 1 //def DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSCoord: Sent CREATE_SUBID_REQ to SUMA");
+#endif
+}
+
+void
+Grep::PSCoord::execCREATE_SUBID_CONF(Signal* signal)
+{
+ jamEntry();
+ CreateSubscriptionIdConf const * conf =
+ (CreateSubscriptionIdConf *)signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 subData = conf->subscriberData;
+
+#if 1 //def DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSCoord: Recd GREP_SUBID_CONF (subId:%d, subKey:%d)",
+ subId, subKey);
+#endif
+
+ SubCoordinatorPtr subPtr;
+ c_subCoordinatorPool.getPtr(subPtr, subData);
+ BlockReference repRef = subPtr.p->m_subscriberRef;
+
+ { // Check that id/key is unique
+ SubCoordinator key;
+ SubCoordinatorPtr tmp;
+ key.m_subscriptionId = subId;
+ key.m_subscriptionKey = subKey;
+ if(c_runningSubscriptions.find(tmp, key)){
+ jam();
+ SubCoordinator sub;
+ sub.m_subscriberRef=repRef;
+ sub.m_subscriptionId = subId;
+ sub.m_subscriptionKey = subKey;
+ sendRefToSS(signal,sub, GrepError::SUBSCRIPTION_ID_NOT_UNIQUE );
+ return;
+ }
+ }
+
+ sendSignal(subPtr.p->m_subscriberRef, GSN_GREP_CREATE_SUBID_CONF, signal,
+ CreateSubscriptionIdConf::SignalLength, JBB);
+ c_subCoordinatorPool.release(subData);
+
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionInfo,
+ GrepEvent::GrepPS_CreateSubIdConf,
+ subId,
+ subKey,
+ (Uint32)GrepError::GE_NO_ERROR);
+}
+
+void
+Grep::PSCoord::execCREATE_SUBID_REF(Signal* signal) {
+ jamEntry();
+ CreateSubscriptionIdRef const * ref =
+ (CreateSubscriptionIdRef *)signal->getDataPtr();
+ Uint32 subData = ref->subscriberData;
+ GrepError::GE_Code err;
+
+ Uint32 sendersBlockRef = signal->getSendersBlockRef();
+ if(sendersBlockRef == SUMA_REF)
+ {
+ jam();
+ err = GrepError::SUBSCRIPTION_ID_SUMA_FAILED_CREATE;
+ } else {
+ jam();
+ ndbrequire(false); /* Added since errorcode err unhandled
+ * TODO: fix correct errorcode
+ */
+ err= GrepError::GE_NO_ERROR; // remove compiler warning
+ }
+
+ SubCoordinatorPtr subPtr;
+ c_runningSubscriptions.getPtr(subPtr, subData);
+ BlockReference repref = subPtr.p->m_subscriberRef;
+
+ SubCoordinator sub;
+ sub.m_subscriberRef = repref;
+ sub.m_subscriptionId = 0;
+ sub.m_subscriptionKey = 0;
+ sendRefToSS(signal,sub, err);
+
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: CREATE SUBSCRIPTION
+ * ------------------------------------------------------------------------
+ *
+ * Creates a subscription for every GREP to its local SUMA.
+ * GREP node that executes createSubscription becomes the GREP Coord.
+ **************************************************************************/
+
+/**
+ * Request to create a subscription (sent from SS)
+ */
+void
+Grep::PSCoord::execGREP_SUB_CREATE_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepSubCreateReq const * grepReq = (GrepSubCreateReq *)signal->getDataPtr();
+ Uint32 subId = grepReq->subscriptionId;
+ Uint32 subKey = grepReq->subscriptionKey;
+ Uint32 subType = grepReq->subscriptionType;
+ BlockReference rep = signal->getSendersBlockRef();
+
+ GrepCreateReq * req =(GrepCreateReq*)grepReq;
+
+ SubCoordinatorPtr subPtr;
+
+ if( !c_subCoordinatorPool.seize(subPtr)) {
+ jam();
+ SubCoordinator sub;
+ sub.m_subscriberRef = rep;
+ sub.m_subscriptionId = 0;
+ sub.m_subscriptionKey = 0;
+ sub.m_outstandingRequest = GSN_GREP_CREATE_REQ;
+ sendRefToSS(signal, sub, GrepError::NOSPACE_IN_POOL);
+ return;
+ }
+ prepareOperationRec(subPtr,
+ numberToRef(PSREPBLOCKNO, refToNode(rep)), subId, subKey,
+ GSN_GREP_CREATE_REQ);
+
+ /* Get the payload of the signal.
+ */
+ SegmentedSectionPtr selectedTablesPtr;
+ if(subType == SubCreateReq::SelectiveTableSnapshot) {
+ jam();
+ ndbrequire(signal->getNoOfSections()==1);
+ signal->getSection(selectedTablesPtr,0);
+ signal->header.m_noOfSections = 0;
+ }
+ /**
+ * Prepare the signal to be sent to Grep participatns
+ */
+ subPtr.p->m_subscriptionType = subType;
+ req->senderRef = reference();
+ req->subscriberRef = numberToRef(PSREPBLOCKNO, refToNode(rep));
+ req->subscriberData = subPtr.p->m_subscriberData;
+ req->subscriptionId = subId;
+ req->subscriptionKey = subKey;
+ req->subscriptionType = subType;
+
+ /*add payload if it is a selectivetablesnap*/
+ if(subType == SubCreateReq::SelectiveTableSnapshot) {
+ jam();
+ signal->setSection(selectedTablesPtr, 0);
+ }
+
+ /******************************
+ * Send to all PS participants
+ ******************************/
+ NodeReceiverGroup rg(GREP, m_grep->m_aliveNodes);
+ subPtr.p->m_outstandingParticipants = rg;
+ sendSignal(rg,
+ GSN_GREP_CREATE_REQ, signal,
+ GrepCreateReq::SignalLength, JBB);
+
+
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSCoord: Sent GREP_CREATE_REQ "
+ "(subId:%d, subKey:%d, subData:%d, subType:%d) to parts",
+ subId, subKey, subPtr.p->m_subscriberData, subType);
+#endif
+}
+
+void
+Grep::PSPart::execGREP_CREATE_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepCreateReq * const grepReq = (GrepCreateReq *)signal->getDataPtr();
+ const Uint32 subId = grepReq->subscriptionId;
+ const Uint32 subKey = grepReq->subscriptionKey;
+ const Uint32 subData = grepReq->subscriberData;
+ const Uint32 subType = grepReq->subscriptionType;
+ const Uint32 coordinatorRef = grepReq->senderRef;
+ const Uint32 subRef = grepReq->subscriberRef; //this is ref to the
+ //REP node for this
+ //subscription.
+
+ SubscriptionPtr subPtr;
+ ndbrequire( c_subscriptionPool.seize(subPtr));
+ subPtr.p->m_coordinatorRef = coordinatorRef;
+ subPtr.p->m_subscriptionId = subId;
+ subPtr.p->m_subscriptionKey = subKey;
+ subPtr.p->m_subscriberRef = subRef;
+ subPtr.p->m_subscriberData = subPtr.i;
+ subPtr.p->m_subscriptionType = subType;
+ subPtr.p->m_outstandingRequest = GSN_GREP_CREATE_REQ;
+ subPtr.p->m_operationPtrI = subData;
+
+ c_subscriptions.add(subPtr);
+
+ SegmentedSectionPtr selectedTablesPtr;
+ if(subType == SubCreateReq::SelectiveTableSnapshot) {
+ jam();
+ ndbrequire(signal->getNoOfSections()==1);
+ signal->getSection(selectedTablesPtr,0);// SubCreateReq::TABLE_LIST);
+ signal->header.m_noOfSections = 0;
+ }
+
+ /**
+ * Prepare signal to be sent to SUMA
+ */
+ SubCreateReq * sumaReq = (SubCreateReq *)grepReq;
+ sumaReq->subscriberRef = GREP_REF;
+ sumaReq->subscriberData = subPtr.p->m_subscriberData;
+ sumaReq->subscriptionId = subPtr.p->m_subscriptionId;
+ sumaReq->subscriptionKey = subPtr.p->m_subscriptionKey;
+ sumaReq->subscriptionType = subPtr.p->m_subscriptionType;
+ /*add payload if it is a selectivetablesnap*/
+ if(subType == SubCreateReq::SelectiveTableSnapshot) {
+ jam();
+ signal->setSection(selectedTablesPtr, 0);
+ }
+ sendSignal(SUMA_REF,
+ GSN_SUB_CREATE_REQ,
+ signal,
+ SubCreateReq::SignalLength,
+ JBB);
+}
+
+void
+Grep::PSPart::execSUB_CREATE_CONF(Signal* signal)
+{
+ jamEntry();
+
+ SubCreateConf * const conf = (SubCreateConf *)signal->getDataPtr();
+ Uint32 subData = conf->subscriberData;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ /**
+ @todo check why this can fuck up -johan
+
+ ndbrequire(subPtr.p->m_subscriptionId == conf->subscriptionId);
+ ndbrequire(subPtr.p->m_subscriptionKey == conf->subscriptionKey);
+ */
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSPart: Recd SUB_CREATE_CONF "
+ "(subId:%d, subKey:%d) from SUMA",
+ conf->subscriptionId, conf->subscriptionKey);
+#endif
+
+ /*********************
+ * Send conf to coord
+ *********************/
+ GrepCreateConf * grepConf = (GrepCreateConf*)conf;
+ grepConf->senderNodeId = getOwnNodeId();
+ grepConf->senderData = subPtr.p->m_operationPtrI;
+ sendSignal(subPtr.p->m_coordinatorRef, GSN_GREP_CREATE_CONF, signal,
+ GrepCreateConf::SignalLength, JBB);
+ subPtr.p->m_outstandingRequest = 0;
+}
+
+/**
+ * Handle errors that either occured in:
+ * 1) PSPart
+ * or
+ * 2) propagated from local SUMA
+ */
+void
+Grep::PSPart::execSUB_CREATE_REF(Signal* signal)
+{
+ jamEntry();
+ SubCreateRef * const ref = (SubCreateRef *)signal->getDataPtr();
+ Uint32 subData = ref->subscriberData;
+ GrepError::GE_Code err = (GrepError::GE_Code)ref->err;
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ sendRefToPSCoord(signal, *subPtr.p, err /*error*/);
+ subPtr.p->m_outstandingRequest = 0;
+}
+
+void
+Grep::PSCoord::execGREP_CREATE_CONF(Signal* signal)
+{
+ jamEntry();
+ GrepCreateConf const * conf = (GrepCreateConf *)signal->getDataPtr();
+ Uint32 subData = conf->senderData;
+ Uint32 nodeId = conf->senderNodeId;
+
+ SubCoordinatorPtr subPtr;
+ c_subCoordinatorPool.getPtr(subPtr, subData);
+
+ ndbrequire(subPtr.p->m_outstandingRequest == GSN_GREP_CREATE_REQ);
+
+ subPtr.p->m_outstandingParticipants.clearWaitingFor(nodeId);
+
+ if(!subPtr.p->m_outstandingParticipants.done()) return;
+ /********************************
+ * All participants have CONF:ed
+ ********************************/
+ Uint32 subId = subPtr.p->m_subscriptionId;
+ Uint32 subKey = subPtr.p->m_subscriptionKey;
+
+ GrepSubCreateConf * grepConf = (GrepSubCreateConf *)signal->getDataPtr();
+ grepConf->subscriptionId = subId;
+ grepConf->subscriptionKey = subKey;
+ sendSignal(subPtr.p->m_subscriberRef, GSN_GREP_SUB_CREATE_CONF, signal,
+ GrepSubCreateConf::SignalLength, JBB);
+
+ /**
+ * Send event report
+ */
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionInfo,
+ GrepEvent::GrepPS_SubCreateConf,
+ subId,
+ subKey,
+ (Uint32)GrepError::GE_NO_ERROR);
+
+ c_subCoordinatorPool.release(subPtr);
+
+}
+
+/**
+ * Handle errors that either occured in:
+ * 1) PSCoord
+ * or
+ * 2) propagated from PSPart
+ */
+void
+Grep::PSCoord::execGREP_CREATE_REF(Signal* signal)
+{
+ jamEntry();
+ GrepCreateRef * const ref = (GrepCreateRef *)signal->getDataPtr();
+ Uint32 subData = ref->senderData;
+ Uint32 err = ref->err;
+ SubCoordinatorPtr subPtr;
+ c_runningSubscriptions.getPtr(subPtr, subData);
+
+ sendRefToSS(signal, *subPtr.p, (GrepError::GE_Code)err /*error*/);
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: START SUBSCRIPTION
+ * ------------------------------------------------------------------------
+ *
+ * Starts a subscription at SUMA.
+ * Each participant starts its own subscription.
+ **************************************************************************/
+
+/**
+ * Request to start subscription (Sent from SS)
+ */
+void
+Grep::PSCoord::execGREP_SUB_START_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepSubStartReq * const subReq = (GrepSubStartReq *)signal->getDataPtr();
+ SubscriptionData::Part part = (SubscriptionData::Part) subReq->part;
+ Uint32 subId = subReq->subscriptionId;
+ Uint32 subKey = subReq->subscriptionKey;
+ BlockReference rep = signal->getSendersBlockRef();
+
+ SubCoordinatorPtr subPtr;
+
+ if(!c_subCoordinatorPool.seize(subPtr)) {
+ jam();
+ SubCoordinator sub;
+ sub.m_subscriberRef = rep;
+ sub.m_subscriptionId = 0;
+ sub.m_subscriptionKey = 0;
+ sub.m_outstandingRequest = GSN_GREP_START_REQ;
+ sendRefToSS(signal, sub, GrepError::NOSPACE_IN_POOL);
+ return;
+ }
+
+ prepareOperationRec(subPtr,
+ numberToRef(PSREPBLOCKNO, refToNode(rep)),
+ subId, subKey,
+ GSN_GREP_START_REQ);
+
+ GrepStartReq * const req = (GrepStartReq *) subReq;
+ req->part = (Uint32) part;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->senderData = subPtr.p->m_subscriberData;
+
+ /***************************
+ * Send to all participants
+ ***************************/
+ NodeReceiverGroup rg(GREP, m_grep->m_aliveNodes);
+ subPtr.p->m_outstandingParticipants = rg;
+ sendSignal(rg,
+ GSN_GREP_START_REQ,
+ signal,
+ GrepStartReq::SignalLength, JBB);
+
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSCoord: Sent GREP_START_REQ "
+ "(subId:%d, subKey:%d, senderData:%d, part:%d) to all participants",
+ req->subscriptionId, req->subscriptionKey, req->senderData, part);
+#endif
+}
+
+
+void
+Grep::PSPart::execGREP_START_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepStartReq * const grepReq = (GrepStartReq *) signal->getDataPtr();
+ SubscriptionData::Part part = (SubscriptionData::Part)grepReq->part;
+ Uint32 subId = grepReq->subscriptionId;
+ Uint32 subKey = grepReq->subscriptionKey;
+ Uint32 operationPtrI = grepReq->senderData;
+
+ Subscription key;
+ key.m_subscriptionId = subId;
+ key.m_subscriptionKey = subKey;
+ SubscriptionPtr subPtr;
+ ndbrequire(c_subscriptions.find(subPtr, key));;
+ subPtr.p->m_outstandingRequest = GSN_GREP_START_REQ;
+ subPtr.p->m_operationPtrI = operationPtrI;
+ /**
+ * send SUB_START_REQ to local SUMA
+ */
+ SubStartReq * sumaReq = (SubStartReq *) grepReq;
+ sumaReq->subscriptionId = subId;
+ sumaReq->subscriptionKey = subKey;
+ sumaReq->subscriberData = subPtr.i;
+ sumaReq->part = (Uint32) part;
+
+ sendSignal(SUMA_REF, GSN_SUB_START_REQ, signal,
+ SubStartReq::SignalLength, JBB);
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSPart: Sent SUB_START_REQ (subId:%d, subKey:%d, part:%d)",
+ subId, subKey, (Uint32)part);
+#endif
+}
+
+
+void
+Grep::PSPart::execSUB_START_CONF(Signal* signal)
+{
+ jamEntry();
+
+ SubStartConf * const conf = (SubStartConf *) signal->getDataPtr();
+ SubscriptionData::Part part = (SubscriptionData::Part)conf->part;
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 subData = conf->subscriberData;
+ Uint32 firstGCI = conf->firstGCI;
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSPart: Recd SUB_START_CONF "
+ "(subId:%d, subKey:%d, subData:%d)",
+ subId, subKey, subData);
+#endif
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ ndbrequire(subPtr.p->m_subscriptionId == subId);
+ ndbrequire(subPtr.p->m_subscriptionKey == subKey);
+
+ GrepStartConf * grepConf = (GrepStartConf *)conf;
+ grepConf->senderData = subPtr.p->m_operationPtrI;
+ grepConf->part = (Uint32) part;
+ grepConf->subscriptionKey = subKey;
+ grepConf->subscriptionId = subId;
+ grepConf->firstGCI = firstGCI;
+ grepConf->senderNodeId = getOwnNodeId();
+ sendSignal(subPtr.p->m_coordinatorRef, GSN_GREP_START_CONF, signal,
+ GrepStartConf::SignalLength, JBB);
+ subPtr.p->m_outstandingRequest = 0;
+
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSPart: Sent GREP_START_CONF "
+ "(subId:%d, subKey:%d, subData:%d, part:%d)",
+ subId, subKey, subData, part);
+#endif
+}
+
+
+/**
+ * Handle errors that either occured in:
+ * 1) PSPart
+ * or
+ * 2) propagated from local SUMA
+ *
+ * Propagates REF signal to PSCoord
+ */
+void
+Grep::PSPart::execSUB_START_REF(Signal* signal)
+{
+ SubStartRef * const ref = (SubStartRef *)signal->getDataPtr();
+ Uint32 subData = ref->subscriberData;
+ GrepError::GE_Code err = (GrepError::GE_Code)ref->err;
+ SubscriptionData::Part part = (SubscriptionData::Part)ref->part;
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ sendRefToPSCoord(signal, *subPtr.p, err /*error*/, part);
+ subPtr.p->m_outstandingRequest = 0;
+}
+
+
+/**
+ * Logging has started... (says PS Participant)
+ */
+void
+Grep::PSCoord::execGREP_START_CONF(Signal* signal)
+{
+ jamEntry();
+
+ GrepStartConf * const conf = (GrepStartConf *) signal->getDataPtr();
+ Uint32 subData = conf->senderData;
+ SubscriptionData::Part part = (SubscriptionData::Part)conf->part;
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 firstGCI = conf->firstGCI;
+
+ SubCoordinatorPtr subPtr;
+ c_subCoordinatorPool.getPtr(subPtr, subData);
+ ndbrequire(subPtr.p->m_outstandingRequest == GSN_GREP_START_REQ);
+
+ subPtr.p->m_outstandingParticipants.clearWaitingFor(conf->senderNodeId);
+
+ if(!subPtr.p->m_outstandingParticipants.done()) return;
+ jam();
+
+ /*************************
+ * All participants ready
+ *************************/
+ GrepSubStartConf * grepConf = (GrepSubStartConf *) conf;
+ grepConf->part = part;
+ grepConf->subscriptionId = subId;
+ grepConf->subscriptionKey = subKey;
+ grepConf->firstGCI = firstGCI;
+
+ bool ok = false;
+ switch(part) {
+ case SubscriptionData::MetaData:
+ ok = true;
+ sendSignal(subPtr.p->m_subscriberRef, GSN_GREP_SUB_START_CONF, signal,
+ GrepSubStartConf::SignalLength, JBB);
+
+ /**
+ * Send event report
+ */
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionInfo,
+ GrepEvent::GrepPS_SubStartMetaConf,
+ subId, subKey,
+ (Uint32)GrepError::GE_NO_ERROR);
+
+ c_subCoordinatorPool.release(subPtr);
+ break;
+ case SubscriptionData::TableData:
+ ok = true;
+ sendSignal(subPtr.p->m_subscriberRef, GSN_GREP_SUB_START_CONF, signal,
+ GrepSubStartConf::SignalLength, JBB);
+
+ /**
+ * Send event report
+ */
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionInfo,
+ GrepEvent::GrepPS_SubStartDataConf,
+ subId, subKey,
+ (Uint32)GrepError::GE_NO_ERROR);
+
+
+ c_subCoordinatorPool.release(subPtr);
+ break;
+ }
+ ndbrequire(ok);
+
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSCoord: Recd SUB_START_CONF (subId:%d, subKey:%d, part:%d) "
+ "from all slaves",
+ subId, subKey, (Uint32)part);
+#endif
+}
+
+/**
+ * Handle errors that either occured in:
+ * 1) PSCoord
+ * or
+ * 2) propagated from PSPart
+ */
+void
+Grep::PSCoord::execGREP_START_REF(Signal* signal)
+{
+ jamEntry();
+ GrepStartRef * const ref = (GrepStartRef *)signal->getDataPtr();
+ Uint32 subData = ref->senderData;
+ GrepError::GE_Code err = (GrepError::GE_Code)ref->err;
+ SubscriptionData::Part part = (SubscriptionData::Part)ref->part;
+
+ SubCoordinatorPtr subPtr;
+ c_runningSubscriptions.getPtr(subPtr, subData);
+ sendRefToSS(signal, *subPtr.p, err /*error*/, part);
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: REMOVE SUBSCRIPTION
+ * ------------------------------------------------------------------------
+ *
+ * Remove a subscription at SUMA.
+ * Each participant removes its own subscription.
+ * We start by deleting the subscription inside the requestor
+ * since, we don't know if nodes (REP nodes or DB nodes)
+ * have disconnected after we sent out this and
+ * if we dont delete the sub in the requestor now,
+ * we won't be able to create a new subscription
+ **************************************************************************/
+
+/**
+ * Request to abort subscription (Sent from SS)
+ */
+void
+Grep::PSCoord::execGREP_SUB_REMOVE_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepSubRemoveReq * const subReq = (GrepSubRemoveReq *)signal->getDataPtr();
+ Uint32 subId = subReq->subscriptionId;
+ Uint32 subKey = subReq->subscriptionKey;
+ BlockReference rep = signal->getSendersBlockRef();
+
+ SubCoordinatorPtr subPtr;
+ if( !c_subCoordinatorPool.seize(subPtr)) {
+ jam();
+ SubCoordinator sub;
+ sub.m_subscriberRef = rep;
+ sub.m_subscriptionId = 0;
+ sub.m_subscriptionKey = 0;
+ sub.m_outstandingRequest = GSN_GREP_REMOVE_REQ;
+ sendRefToSS(signal, sub, GrepError::NOSPACE_IN_POOL);
+ return;
+ }
+
+
+ prepareOperationRec(subPtr,
+ numberToRef(PSREPBLOCKNO, refToNode(rep)),
+ subId, subKey,
+ GSN_GREP_REMOVE_REQ);
+
+ c_runningSubscriptions.add(subPtr);
+
+ GrepRemoveReq * req = (GrepRemoveReq *) subReq;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->senderData = subPtr.p->m_subscriberData;
+ req->senderRef = subPtr.p->m_coordinatorRef;
+
+ /***************************
+ * Send to all participants
+ ***************************/
+ NodeReceiverGroup rg(GREP, m_grep->m_aliveNodes);
+ subPtr.p->m_outstandingParticipants = rg;
+ sendSignal(rg,
+ GSN_GREP_REMOVE_REQ, signal,
+ GrepRemoveReq::SignalLength, JBB);
+}
+
+
+void
+Grep::PSPart::execGREP_REMOVE_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepRemoveReq * const grepReq = (GrepRemoveReq *) signal->getDataPtr();
+ Uint32 subId = grepReq->subscriptionId;
+ Uint32 subKey = grepReq->subscriptionKey;
+ Uint32 subData = grepReq->senderData;
+ Uint32 coordinator = grepReq->senderRef;
+
+ Subscription key;
+ key.m_subscriptionId = subId;
+ key.m_subscriptionKey = subKey;
+ SubscriptionPtr subPtr;
+
+ if(!c_subscriptions.find(subPtr, key))
+ {
+ /**
+ * The subscription was not found, so it must be deleted.
+ * Send CONF back, since it does not exist (thus, it is removed)
+ */
+ GrepRemoveConf * grepConf = (GrepRemoveConf *)grepReq;
+ grepConf->subscriptionKey = subKey;
+ grepConf->subscriptionId = subId;
+ grepConf->senderData = subData;
+ grepConf->senderNodeId = getOwnNodeId();
+ sendSignal(coordinator, GSN_GREP_REMOVE_CONF, signal,
+ GrepRemoveConf::SignalLength, JBB);
+ return;
+ }
+
+ subPtr.p->m_operationPtrI = subData;
+ subPtr.p->m_coordinatorRef = coordinator;
+ subPtr.p->m_outstandingRequest = GSN_GREP_REMOVE_REQ;
+
+ /**
+ * send SUB_REMOVE_REQ to local SUMA
+ */
+ SubRemoveReq * sumaReq = (SubRemoveReq *) grepReq;
+ sumaReq->subscriptionId = subId;
+ sumaReq->subscriptionKey = subKey;
+ sumaReq->senderData = subPtr.i;
+ sendSignal(SUMA_REF, GSN_SUB_REMOVE_REQ, signal,
+ SubStartReq::SignalLength, JBB);
+}
+
+
+/**
+ * SUB_REMOVE_CONF (from local SUMA)
+ */
+void
+Grep::PSPart::execSUB_REMOVE_CONF(Signal* signal)
+{
+ jamEntry();
+ SubRemoveConf * const conf = (SubRemoveConf *) signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 subData = conf->subscriberData;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ ndbrequire(subPtr.p->m_subscriptionId == subId);
+ ndbrequire(subPtr.p->m_subscriptionKey == subKey);
+ subPtr.p->m_outstandingRequest = 0;
+ GrepRemoveConf * grepConf = (GrepRemoveConf *)conf;
+ grepConf->subscriptionKey = subKey;
+ grepConf->subscriptionId = subId;
+ grepConf->senderData = subPtr.p->m_operationPtrI;
+ grepConf->senderNodeId = getOwnNodeId();
+ sendSignal(subPtr.p->m_coordinatorRef, GSN_GREP_REMOVE_CONF, signal,
+ GrepRemoveConf::SignalLength, JBB);
+ c_subscriptions.release(subPtr);
+
+}
+
+
+/**
+ * SUB_REMOVE_CONF (from local SUMA)
+ */
+void
+Grep::PSPart::execSUB_REMOVE_REF(Signal* signal)
+{
+ jamEntry();
+ SubRemoveRef * const ref = (SubRemoveRef *)signal->getDataPtr();
+ Uint32 subData = ref->subscriberData;
+ /* GrepError::GE_Code err = (GrepError::GE_Code)ref->err;*/
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+
+ //sendSubRemoveRef_PSCoord(signal, *subPtr.p, err /*error*/);
+}
+
+
+/**
+ * Aborting has been carried out (says Participants)
+ */
+void
+Grep::PSCoord::execGREP_REMOVE_CONF(Signal* signal)
+{
+ jamEntry();
+ GrepRemoveConf * const conf = (GrepRemoveConf *) signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 senderNodeId = conf->senderNodeId;
+ Uint32 subData = conf->senderData;
+ SubCoordinatorPtr subPtr;
+ c_subCoordinatorPool.getPtr(subPtr, subData);
+
+ ndbrequire(subPtr.p->m_outstandingRequest == GSN_GREP_REMOVE_REQ);
+
+ subPtr.p->m_outstandingParticipants.clearWaitingFor(senderNodeId);
+
+ if(!subPtr.p->m_outstandingParticipants.done()) {
+ jam();
+ return;
+ }
+ jam();
+
+ /*************************
+ * All participants ready
+ *************************/
+
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionInfo,
+ GrepEvent::GrepPS_SubRemoveConf,
+ subId, subKey,
+ GrepError::GE_NO_ERROR);
+
+ GrepSubRemoveConf * grepConf = (GrepSubRemoveConf *) conf;
+ grepConf->subscriptionId = subId;
+ grepConf->subscriptionKey = subKey;
+ sendSignal(subPtr.p->m_subscriberRef, GSN_GREP_SUB_REMOVE_CONF, signal,
+ GrepSubRemoveConf::SignalLength, JBB);
+
+ c_subCoordinatorPool.release(subPtr);
+}
+
+
+
+void
+Grep::PSCoord::execGREP_REMOVE_REF(Signal* signal)
+{
+ jamEntry();
+ GrepRemoveRef * const ref = (GrepRemoveRef *)signal->getDataPtr();
+ Uint32 subData = ref->senderData;
+ Uint32 err = ref->err;
+ SubCoordinatorPtr subPtr;
+
+ /**
+ * Get the operationrecord matching subdata and remove it. Subsequent
+ * execGREP_REMOVE_REF will simply be ignored at this stage.
+ */
+ for( c_runningSubscriptions.first(c_subPtr);
+ !c_subPtr.isNull(); c_runningSubscriptions.next(c_subPtr)) {
+ jam();
+ subPtr.i = c_subPtr.curr.i;
+ subPtr.p = c_runningSubscriptions.getPtr(subPtr.i);
+ if(subData == subPtr.i)
+ {
+ sendRefToSS(signal, *subPtr.p, (GrepError::GE_Code)err /*error*/);
+ c_runningSubscriptions.release(subPtr);
+ return;
+ }
+ }
+ return;
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: LOG RECORDS (COMING IN FROM LOCAL SUMA)
+ * ------------------------------------------------------------------------
+ *
+ * After the subscription is started, we get log records from SUMA.
+ * Both table data and meta data log records are received.
+ *
+ * TODO:
+ * @todo Changes in meta data is currently not
+ * allowed during global replication
+ **************************************************************************/
+
+void
+Grep::PSPart::execSUB_META_DATA(Signal* signal)
+{
+ jamEntry();
+ if(m_recoveryMode) {
+ jam();
+ return;
+ }
+ /**
+ * METASCAN and METALOG
+ */
+ SubMetaData * data = (SubMetaData *) signal->getDataPtrSend();
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, data->subscriberData);
+
+ /***************************
+ * Forward data to REP node
+ ***************************/
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_META_DATA, signal,
+ SubMetaData::SignalLength, JBB);
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Grep::PSPart: Sent SUB_META_DATA to REP "
+ "(TableId: %d, SenderData: %d, GCI: %d)",
+ data->tableId, data->senderData, data->gci);
+#endif
+}
+
+/**
+ * Receive table data from SUMA and dispatches it to REP node.
+ */
+void
+Grep::PSPart::execSUB_TABLE_DATA(Signal* signal)
+{
+ jamEntry();
+ if(m_recoveryMode) {
+ jam();
+ return;
+ }
+ ndbrequire(m_repRef!=0);
+
+ if(!assembleFragments(signal)) { jam(); return; }
+
+ /**
+ * Check if it is SCAN or LOG data that has arrived
+ */
+ if(signal->getNoOfSections() == 2)
+ {
+ jam();
+ /**
+ * DATASCAN - Not marked with GCI, so mark with latest seen GCI
+ */
+ if(m_firstScanGCI == 1 && m_lastScanGCI == 0) {
+ m_firstScanGCI = m_latestSeenGCI;
+ m_lastScanGCI = m_latestSeenGCI;
+ }
+ SubTableData * data = (SubTableData*)signal->getDataPtrSend();
+ Uint32 subData = data->senderData;
+ data->gci = m_latestSeenGCI;
+ data->logType = SubTableData::SCAN;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_TABLE_DATA, signal,
+ SubTableData::SignalLength, JBB);
+#ifdef DEBUG_GREP
+ ndbout_c("Grep::PSPart: Sent SUB_TABLE_DATA (Scan, GCI: %d)",
+ data->gci);
+#endif
+ }
+ else
+ {
+ jam();
+ /**
+ * DATALOG (TRIGGER) - Already marked with GCI
+ */
+ SubTableData * data = (SubTableData*)signal->getDataPtrSend();
+ data->logType = SubTableData::LOG;
+ Uint32 subData = data->senderData;
+ if (data->gci > m_latestSeenGCI) m_latestSeenGCI = data->gci;
+
+ // Reformat to sections and send to replication node.
+ LinearSectionPtr ptr[3];
+ ptr[0].p = signal->theData + 25;
+ ptr[0].sz = data->noOfAttributes;
+ ptr[1].p = signal->theData + 25 + MAX_ATTRIBUTES_IN_TABLE;
+ ptr[1].sz = data->dataSize;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_TABLE_DATA,
+ signal, SubTableData::SignalLength, JBB, ptr, 2);
+#ifdef DEBUG_GREP
+ ndbout_c("Grep::PSPart: Sent SUB_TABLE_DATA (Log, GCI: %d)",
+ data->gci);
+#endif
+ }
+}
+
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: START SYNCHRONIZATION
+ * ------------------------------------------------------------------------
+ *
+ *
+ **************************************************************************/
+
+/**
+ * Request to start sync (from Rep SS)
+ */
+void
+Grep::PSCoord::execGREP_SUB_SYNC_REQ(Signal* signal)
+{
+ jamEntry();
+ GrepSubSyncReq * const subReq = (GrepSubSyncReq*)signal->getDataPtr();
+ SubscriptionData::Part part = (SubscriptionData::Part) subReq->part;
+ Uint32 subId = subReq->subscriptionId;
+ Uint32 subKey = subReq->subscriptionKey;
+ BlockReference rep = signal->getSendersBlockRef();
+
+ SubCoordinatorPtr subPtr;
+ if( !c_subCoordinatorPool.seize(subPtr)) {
+ jam();
+ SubCoordinator sub;
+ sub.m_subscriberRef = rep;
+ sub.m_subscriptionId = 0;
+ sub.m_subscriptionKey = 0;
+ sub.m_outstandingRequest = GSN_GREP_SYNC_REQ;
+ sendRefToSS(signal, sub, GrepError::NOSPACE_IN_POOL);
+ return;
+ }
+
+ prepareOperationRec(subPtr,
+ numberToRef(PSREPBLOCKNO, refToNode(rep)),
+ subId, subKey,
+ GSN_GREP_SYNC_REQ);
+
+ GrepSyncReq * req = (GrepSyncReq *)subReq;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->senderData = subPtr.p->m_subscriberData;
+ req->part = (Uint32)part;
+
+ /***************************
+ * Send to all participants
+ ***************************/
+ NodeReceiverGroup rg(GREP, m_grep->m_aliveNodes);
+ subPtr.p->m_outstandingParticipants = rg;
+ sendSignal(rg,
+ GSN_GREP_SYNC_REQ, signal, GrepSyncReq::SignalLength, JBB);
+}
+
+
+/**
+ * Sync req from Grep::PSCoord to PS particpant
+ */
+void
+Grep::PSPart::execGREP_SYNC_REQ(Signal* signal)
+{
+ jamEntry();
+
+ GrepSyncReq * const grepReq = (GrepSyncReq *) signal->getDataPtr();
+ Uint32 part = grepReq->part;
+ Uint32 subId = grepReq->subscriptionId;
+ Uint32 subKey = grepReq->subscriptionKey;
+ Uint32 subData = grepReq->senderData;
+
+ Subscription key;
+ key.m_subscriptionId = subId;
+ key.m_subscriptionKey = subKey;
+ SubscriptionPtr subPtr;
+ ndbrequire(c_subscriptions.find(subPtr, key));
+ subPtr.p->m_operationPtrI = subData;
+ subPtr.p->m_outstandingRequest = GSN_GREP_SYNC_REQ;
+ /**********************************
+ * Send SUB_SYNC_REQ to local SUMA
+ **********************************/
+ SubSyncReq * sumaReq = (SubSyncReq *)grepReq;
+ sumaReq->subscriptionId = subId;
+ sumaReq->subscriptionKey = subKey;
+ sumaReq->subscriberData = subPtr.i;
+ sumaReq->part = part;
+ sendSignal(SUMA_REF, GSN_SUB_SYNC_REQ, signal,
+ SubSyncReq::SignalLength, JBB);
+}
+
+
+/**
+ * SYNC conf from SUMA
+ */
+void
+Grep::PSPart::execSUB_SYNC_CONF(Signal* signal)
+{
+ jamEntry();
+
+ SubSyncConf * const conf = (SubSyncConf *) signal->getDataPtr();
+ Uint32 part = conf->part;
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 subData = conf->subscriberData;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+
+ ndbrequire(subPtr.p->m_subscriptionId == subId);
+ ndbrequire(subPtr.p->m_subscriptionKey == subKey);
+
+ GrepSyncConf * grepConf = (GrepSyncConf *)conf;
+ grepConf->senderNodeId = getOwnNodeId();
+ grepConf->part = part;
+ grepConf->firstGCI = m_firstScanGCI;
+ grepConf->lastGCI = m_lastScanGCI;
+ grepConf->subscriptionId = subId;
+ grepConf->subscriptionKey = subKey;
+ grepConf->senderData = subPtr.p->m_operationPtrI;
+ sendSignal(subPtr.p->m_coordinatorRef, GSN_GREP_SYNC_CONF, signal,
+ GrepSyncConf::SignalLength, JBB);
+
+ m_firstScanGCI = 1;
+ m_lastScanGCI = 0;
+ subPtr.p->m_outstandingRequest = 0;
+}
+
+/**
+ * Handle errors that either occured in:
+ * 1) PSPart
+ * or
+ * 2) propagated from local SUMA
+ *
+ * Propagates REF signal to PSCoord
+ */
+void
+Grep::PSPart::execSUB_SYNC_REF(Signal* signal) {
+ jamEntry();
+ SubSyncRef * const ref = (SubSyncRef *)signal->getDataPtr();
+ Uint32 subData = ref->subscriberData;
+ GrepError::GE_Code err = (GrepError::GE_Code)ref->err;
+ SubscriptionData::Part part = (SubscriptionData::Part)ref->part;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subData);
+ sendRefToPSCoord(signal, *subPtr.p, err /*error*/ ,part);
+ subPtr.p->m_outstandingRequest = 0;
+}
+
+/**
+ * Syncing has started... (says PS Participant)
+ */
+void
+Grep::PSCoord::execGREP_SYNC_CONF(Signal* signal)
+{
+ jamEntry();
+
+ GrepSyncConf const * conf = (GrepSyncConf *)signal->getDataPtr();
+ Uint32 part = conf->part;
+ Uint32 firstGCI = conf->firstGCI;
+ Uint32 lastGCI = conf->lastGCI;
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Uint32 subData = conf->senderData;
+
+ SubCoordinatorPtr subPtr;
+ c_subCoordinatorPool.getPtr(subPtr, subData);
+ ndbrequire(subPtr.p->m_outstandingRequest == GSN_GREP_SYNC_REQ);
+
+ subPtr.p->m_outstandingParticipants.clearWaitingFor(conf->senderNodeId);
+ if(!subPtr.p->m_outstandingParticipants.done()) return;
+
+ /**
+ * Send event
+ */
+ GrepEvent::Subscription event;
+ if(part == SubscriptionData::MetaData)
+ event = GrepEvent::GrepPS_SubSyncMetaConf;
+ else
+ event = GrepEvent::GrepPS_SubSyncDataConf;
+
+ /* @todo Johan: Add firstGCI here. /Lars */
+ m_grep->sendEventRep(signal, NDB_LE_GrepSubscriptionInfo,
+ event, subId, subKey,
+ (Uint32)GrepError::GE_NO_ERROR,
+ lastGCI);
+
+ /*************************
+ * All participants ready
+ *************************/
+ GrepSubSyncConf * grepConf = (GrepSubSyncConf *)conf;
+ grepConf->part = part;
+ grepConf->firstGCI = firstGCI;
+ grepConf->lastGCI = lastGCI;
+ grepConf->subscriptionId = subId;
+ grepConf->subscriptionKey = subKey;
+
+ sendSignal(subPtr.p->m_subscriberRef, GSN_GREP_SUB_SYNC_CONF, signal,
+ GrepSubSyncConf::SignalLength, JBB);
+ c_subCoordinatorPool.release(subPtr);
+}
+
+/**
+ * Handle errors that either occured in:
+ * 1) PSCoord
+ * or
+ * 2) propagated from PSPart
+ */
+void
+Grep::PSCoord::execGREP_SYNC_REF(Signal* signal) {
+ jamEntry();
+ GrepSyncRef * const ref = (GrepSyncRef *)signal->getDataPtr();
+ Uint32 subData = ref->senderData;
+ SubscriptionData::Part part = (SubscriptionData::Part)ref->part;
+ GrepError::GE_Code err = (GrepError::GE_Code)ref->err;
+ SubCoordinatorPtr subPtr;
+ c_runningSubscriptions.getPtr(subPtr, subData);
+ sendRefToSS(signal, *subPtr.p, err /*error*/, part);
+}
+
+
+
+void
+Grep::PSCoord::sendRefToSS(Signal * signal,
+ SubCoordinator sub,
+ GrepError::GE_Code err,
+ SubscriptionData::Part part) {
+ /**
+
+ GrepCreateRef * ref = (GrepCreateRef*)signal->getDataPtrSend();
+ ref->senderData = sub.m_subscriberData;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->err = err;
+ sendSignal(sub.m_coordinatorRef, GSN_GREP_CREATE_REF, signal,
+ GrepCreateRef::SignalLength, JBB);
+*/
+
+ jam();
+ GrepEvent::Subscription event;
+ switch(sub.m_outstandingRequest) {
+ case GSN_GREP_CREATE_SUBID_REQ:
+ {
+ jam();
+ CreateSubscriptionIdRef * ref =
+ (CreateSubscriptionIdRef*)signal->getDataPtrSend();
+ ref->err = (Uint32)err;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ sendSignal(sub.m_subscriberRef,
+ GSN_GREP_CREATE_SUBID_REF,
+ signal,
+ CreateSubscriptionIdRef::SignalLength,
+ JBB);
+ event = GrepEvent::GrepPS_CreateSubIdRef;
+ }
+ break;
+ case GSN_GREP_CREATE_REQ:
+ {
+ jam();
+ GrepSubCreateRef * ref = (GrepSubCreateRef*)signal->getDataPtrSend();
+ ref->err = (Uint32)err;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ sendSignal(sub.m_subscriberRef, GSN_GREP_SUB_CREATE_REF, signal,
+ GrepSubCreateRef::SignalLength, JBB);
+ event = GrepEvent::GrepPS_SubCreateRef;
+ }
+ break;
+ case GSN_GREP_SYNC_REQ:
+ {
+ jam();
+ GrepSubSyncRef * ref = (GrepSubSyncRef*)signal->getDataPtrSend();
+ ref->err = (Uint32)err;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->part = (SubscriptionData::Part) part;
+ sendSignal(sub.m_subscriberRef,
+ GSN_GREP_SUB_SYNC_REF,
+ signal,
+ GrepSubSyncRef::SignalLength,
+ JBB);
+ if(part == SubscriptionData::MetaData)
+ event = GrepEvent::GrepPS_SubSyncMetaRef;
+ else
+ event = GrepEvent::GrepPS_SubSyncDataRef;
+ }
+ break;
+ case GSN_GREP_START_REQ:
+ {
+ jam();
+ GrepSubStartRef * ref = (GrepSubStartRef*)signal->getDataPtrSend();
+ ref->err = (Uint32)err;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+
+ sendSignal(sub.m_subscriberRef, GSN_GREP_SUB_START_REF,
+ signal, GrepSubStartRef::SignalLength, JBB);
+ if(part == SubscriptionData::MetaData)
+ event = GrepEvent::GrepPS_SubStartMetaRef;
+ else
+ event = GrepEvent::GrepPS_SubStartDataRef;
+ /**
+ * Send event report
+ */
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionAlert,
+ event,
+ sub.m_subscriptionId,
+ sub.m_subscriptionKey,
+ (Uint32)err);
+ }
+ break;
+ case GSN_GREP_REMOVE_REQ:
+ {
+ jam();
+ GrepSubRemoveRef * ref = (GrepSubRemoveRef*)signal->getDataPtrSend();
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->err = (Uint32)err;
+
+ sendSignal(sub.m_subscriberRef,
+ GSN_GREP_SUB_REMOVE_REF,
+ signal,
+ GrepSubRemoveRef::SignalLength,
+ JBB);
+
+ event = GrepEvent::GrepPS_SubRemoveRef;
+ }
+ break;
+ default:
+ ndbrequire(false);
+ event= GrepEvent::Rep_Disconnect; // remove compiler warning
+ }
+ /**
+ * Finally, send an event.
+ */
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionAlert,
+ event,
+ sub.m_subscriptionId,
+ sub.m_subscriptionKey,
+ err);
+
+}
+
+
+void
+Grep::PSPart::sendRefToPSCoord(Signal * signal,
+ Subscription sub,
+ GrepError::GE_Code err,
+ SubscriptionData::Part part) {
+
+ jam();
+ GrepEvent::Subscription event;
+ switch(sub.m_outstandingRequest) {
+
+ case GSN_GREP_CREATE_REQ:
+ {
+ GrepCreateRef * ref = (GrepCreateRef*)signal->getDataPtrSend();
+ ref->senderData = sub.m_subscriberData;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->err = err;
+ sendSignal(sub.m_coordinatorRef, GSN_GREP_CREATE_REF, signal,
+ GrepCreateRef::SignalLength, JBB);
+
+ event = GrepEvent::GrepPS_SubCreateRef;
+ }
+ break;
+ case GSN_GREP_SYNC_REQ:
+ {
+ GrepSyncRef * ref = (GrepSyncRef*)signal->getDataPtrSend();
+ ref->senderData = sub.m_subscriberData;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->part = part;
+ ref->err = err;
+ sendSignal(sub.m_coordinatorRef,
+ GSN_GREP_SYNC_REF, signal,
+ GrepSyncRef::SignalLength, JBB);
+ if(part == SubscriptionData::MetaData)
+ event = GrepEvent::GrepPS_SubSyncMetaRef;
+ else
+ event = GrepEvent::GrepPS_SubSyncDataRef;
+ }
+ break;
+ case GSN_GREP_START_REQ:
+ {
+ jam();
+ GrepStartRef * ref = (GrepStartRef*)signal->getDataPtrSend();
+ ref->senderData = sub.m_subscriberData;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->part = (Uint32) part;
+ ref->err = err;
+ sendSignal(sub.m_coordinatorRef, GSN_GREP_START_REF, signal,
+ GrepStartRef::SignalLength, JBB);
+ if(part == SubscriptionData::MetaData)
+ event = GrepEvent::GrepPS_SubStartMetaRef;
+ else
+ event = GrepEvent::GrepPS_SubStartDataRef;
+ }
+ break;
+
+ case GSN_GREP_REMOVE_REQ:
+ {
+ jamEntry();
+ GrepRemoveRef * ref = (GrepRemoveRef*)signal->getDataPtrSend();
+ ref->senderData = sub.m_operationPtrI;
+ ref->subscriptionId = sub.m_subscriptionId;
+ ref->subscriptionKey = sub.m_subscriptionKey;
+ ref->err = err;
+ sendSignal(sub.m_coordinatorRef, GSN_GREP_REMOVE_REF, signal,
+ GrepCreateRef::SignalLength, JBB);
+
+ }
+ break;
+ default:
+ ndbrequire(false);
+ event= GrepEvent::Rep_Disconnect; // remove compiler warning
+ }
+
+ /**
+ * Finally, send an event.
+ */
+ m_grep->sendEventRep(signal,
+ NDB_LE_GrepSubscriptionAlert,
+ event,
+ sub.m_subscriptionId,
+ sub.m_subscriptionKey,
+ err);
+
+}
+
+/**************************************************************************
+ * ------------------------------------------------------------------------
+ * MODULE: GREP PS Coordinator GCP
+ * ------------------------------------------------------------------------
+ *
+ *
+ **************************************************************************/
+
+void
+Grep::PSPart::execSUB_GCP_COMPLETE_REP(Signal* signal)
+{
+ jamEntry();
+ if(m_recoveryMode) {
+ jam();
+ return;
+ }
+ SubGcpCompleteRep * rep = (SubGcpCompleteRep *)signal->getDataPtrSend();
+ rep->senderRef = reference();
+
+ if (rep->gci > m_latestSeenGCI) m_latestSeenGCI = rep->gci;
+ SubscriptionPtr subPtr;
+ c_subscriptions.first(c_subPtr);
+ for(; !c_subPtr.isNull(); c_subscriptions.next(c_subPtr)) {
+
+ subPtr.i = c_subPtr.curr.i;
+ subPtr.p = c_subscriptions.getPtr(subPtr.i);
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_GCP_COMPLETE_REP, signal,
+ SubGcpCompleteRep::SignalLength, JBB);
+ }
+
+#ifdef DEBUG_GREP
+ ndbout_c("Grep::PSPart: Recd SUB_GCP_COMPLETE_REP "
+ "(GCI: %d, nodeId: %d) from SUMA",
+ rep->gci, refToNode(rep->senderRef));
+#endif
+}
+
+
+void
+Grep::PSPart::execSUB_SYNC_CONTINUE_REQ(Signal* signal)
+{
+ jamEntry();
+ SubSyncContinueReq * const req = (SubSyncContinueReq*)signal->getDataPtr();
+ Uint32 subData = req->subscriberData;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr,subData);
+
+ /**
+ * @todo Figure out how to control how much data we can receive?
+ */
+ SubSyncContinueConf * conf = (SubSyncContinueConf*)req;
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ sendSignal(SUMA_REF, GSN_SUB_SYNC_CONTINUE_CONF, signal,
+ SubSyncContinueConf::SignalLength, JBB);
+}
+
+void
+Grep::sendEventRep(Signal * signal,
+ Ndb_logevent_type type,
+ GrepEvent::Subscription event,
+ Uint32 subId,
+ Uint32 subKey,
+ Uint32 err,
+ Uint32 other) {
+ jam();
+ signal->theData[0] = type;
+ signal->theData[1] = event;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ signal->theData[4] = err;
+
+ if(other==0)
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 5 ,JBB);
+ else {
+ signal->theData[5] = other;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 6 ,JBB);
+ }
+}
diff --git a/storage/ndb/src/kernel/blocks/grep/Grep.hpp b/storage/ndb/src/kernel/blocks/grep/Grep.hpp
new file mode 100644
index 00000000000..a14143294e1
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/grep/Grep.hpp
@@ -0,0 +1,535 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 GREP_HPP
+#define GREP_HPP
+
+#include <ndb_limits.h>
+#include <SimulatedBlock.hpp>
+
+#include <NodeBitmask.hpp>
+#include <SignalCounter.hpp>
+#include <SLList.hpp>
+
+#include <DLList.hpp>
+
+#include <GrepError.hpp>
+#include <GrepEvent.hpp>
+
+#include <signaldata/EventReport.hpp>
+#include <signaldata/SumaImpl.hpp>
+
+
+/**
+ * Module in block (Should be placed elsewhere)
+ */
+class BlockComponent {
+public:
+ BlockComponent(SimulatedBlock *);
+ BlockReference reference() { return m_sb->reference(); };
+ BlockNumber number() { return m_sb->number(); };
+
+ void sendSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf ) const {
+ m_sb->sendSignal(rg, gsn, signal, length, jbuf);
+ }
+
+ void sendSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf ) const {
+ m_sb->sendSignal(ref, gsn, signal, length, jbuf);
+ }
+
+ void sendSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections) const {
+ m_sb->sendSignal(ref, gsn, signal, length, jbuf, ptr, noOfSections);
+ }
+
+ void sendSignalWithDelay(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 delayInMilliSeconds,
+ Uint32 length) const {
+
+ m_sb->sendSignalWithDelay(ref, gsn, signal, delayInMilliSeconds, length);
+ }
+
+ NodeId getOwnNodeId() const {
+ return m_sb->getOwnNodeId();
+ }
+
+ bool assembleFragments(Signal * signal) {
+ return m_sb->assembleFragments(signal);
+ }
+
+ void progError(int line, int err_code, const char* extra) {
+ m_sb->progError(line, err_code, extra);
+ }
+
+private:
+ SimulatedBlock * m_sb;
+};
+
+
+
+/**
+ * Participant of GREP Protocols (not necessarily a protocol coordinator)
+ *
+ * This object is only used on primary system
+ */
+#if 0
+class GrepParticipant : public SimulatedBlock
+{
+protected:
+ GrepParticipant(const Configuration & conf);
+ virtual ~GrepParticipant();
+ BLOCK_DEFINES(GrepParticipant);
+
+protected:
+ /***************************************************************************
+ * SUMA Signal Interface
+ ***************************************************************************/
+ void execSUB_CREATE_CONF(Signal*);
+ void execSUB_STARTCONF(Signal*);
+ void execSUB_REMOVE_CONF(Signal*);
+
+ void execSUB_META_DATA(Signal*);
+ void execSUB_TABLE_DATA(Signal*);
+
+ void execSUB_SYNC_CONF(Signal*);
+
+ void execSUB_GCP_COMPLETE_REP(Signal*);
+ void execSUB_SYNC_CONTINUE_REQ(Signal*);
+
+ /***************************************************************************
+ * GREP Coordinator Signal Interface
+ ***************************************************************************/
+ void execGREP_CREATE_REQ(Signal*);
+ void execGREP_START_REQ(Signal*);
+ void execGREP_SYNC_REQ(Signal*);
+ void execGREP_REMOVE_REQ(Signal*);
+
+
+protected:
+ BlockReference m_repRef; ///< Replication node (only one rep node per grep)
+
+private:
+ BlockReference m_coordinator;
+ Uint32 m_latestSeenGCI;
+};
+#endif
+
+
+/**
+ * GREP Coordinator
+ */
+class Grep : public SimulatedBlock //GrepParticipant
+{
+ BLOCK_DEFINES(Grep);
+
+public:
+ Grep(const Configuration & conf);
+ virtual ~Grep();
+
+private:
+ /***************************************************************************
+ * General Signal Recivers
+ ***************************************************************************/
+ void execSTTOR(Signal*);
+ void sendSTTORRY(Signal*);
+ void execNDB_STTOR(Signal*);
+ void execDUMP_STATE_ORD(Signal*);
+ void execREAD_NODESCONF(Signal*);
+ void execNODE_FAILREP(Signal*);
+ void execINCL_NODEREQ(Signal*);
+ void execGREP_REQ(Signal*);
+ void execAPI_FAILREQ(Signal*);
+ /**
+ * Forwarded to PSCoord
+ */
+ //CONF
+ void fwdGREP_CREATE_CONF(Signal* s) {
+ pscoord.execGREP_CREATE_CONF(s); };
+ void fwdGREP_START_CONF(Signal* s) {
+ pscoord.execGREP_START_CONF(s); };
+ void fwdGREP_SYNC_CONF(Signal* s) {
+ pscoord.execGREP_SYNC_CONF(s); };
+ void fwdGREP_REMOVE_CONF(Signal* s) {
+ pscoord.execGREP_REMOVE_CONF(s); };
+ void fwdCREATE_SUBID_CONF(Signal* s) {
+ pscoord.execCREATE_SUBID_CONF(s); };
+
+ //REF
+
+ void fwdGREP_CREATE_REF(Signal* s) {
+ pscoord.execGREP_CREATE_REF(s); };
+ void fwdGREP_START_REF(Signal* s) {
+ pscoord.execGREP_START_REF(s); };
+ void fwdGREP_SYNC_REF(Signal* s) {
+ pscoord.execGREP_SYNC_REF(s); };
+
+ void fwdGREP_REMOVE_REF(Signal* s) {
+ pscoord.execGREP_REMOVE_REF(s); };
+
+ void fwdCREATE_SUBID_REF(Signal* s) {
+ pscoord.execCREATE_SUBID_REF(s); };
+
+ //REQ
+ void fwdGREP_SUB_CREATE_REQ(Signal* s) {
+ pscoord.execGREP_SUB_CREATE_REQ(s); };
+ void fwdGREP_SUB_START_REQ(Signal* s) {
+ pscoord.execGREP_SUB_START_REQ(s); };
+ void fwdGREP_SUB_SYNC_REQ(Signal* s) {
+ pscoord.execGREP_SUB_SYNC_REQ(s); };
+ void fwdGREP_SUB_REMOVE_REQ(Signal* s) {
+ pscoord.execGREP_SUB_REMOVE_REQ(s); };
+ void fwdGREP_CREATE_SUBID_REQ(Signal* s) {
+ pscoord.execGREP_CREATE_SUBID_REQ(s); };
+
+ /**
+ * Forwarded to PSPart
+ */
+
+ void fwdSTART_ME(Signal* s){
+ pspart.execSTART_ME(s);
+ };
+ void fwdGREP_ADD_SUB_REQ(Signal* s){
+ pspart.execGREP_ADD_SUB_REQ(s);
+ };
+ void fwdGREP_ADD_SUB_REF(Signal* s){
+ pspart.execGREP_ADD_SUB_REF(s);
+ };
+ void fwdGREP_ADD_SUB_CONF(Signal* s){
+ pspart.execGREP_ADD_SUB_CONF(s);
+ };
+
+ //CONF
+ void fwdSUB_CREATE_CONF(Signal* s) {
+ pspart.execSUB_CREATE_CONF(s); };
+ void fwdSUB_START_CONF(Signal* s) {
+ pspart.execSUB_START_CONF(s); };
+ void fwdSUB_REMOVE_CONF(Signal* s) {
+ pspart.execSUB_REMOVE_CONF(s); };
+ void fwdSUB_SYNC_CONF(Signal* s) {
+ pspart.execSUB_SYNC_CONF(s); };
+
+ //REF
+
+ void fwdSUB_CREATE_REF(Signal* s) {
+ pspart.execSUB_CREATE_REF(s); };
+ void fwdSUB_START_REF(Signal* s) {
+ pspart.execSUB_START_REF(s); };
+ void fwdSUB_REMOVE_REF(Signal* s) {
+ pspart.execSUB_REMOVE_REF(s); };
+ void fwdSUB_SYNC_REF(Signal* s) {
+ pspart.execSUB_SYNC_REF(s); };
+
+ //REQ
+ void fwdSUB_SYNC_CONTINUE_REQ(Signal* s) {
+ pspart.execSUB_SYNC_CONTINUE_REQ(s); };
+ void fwdGREP_CREATE_REQ(Signal* s) {
+ pspart.execGREP_CREATE_REQ(s); };
+ void fwdGREP_START_REQ(Signal* s) {
+ pspart.execGREP_START_REQ(s); };
+ void fwdGREP_SYNC_REQ(Signal* s) {
+ pspart.execGREP_SYNC_REQ(s); };
+ void fwdGREP_REMOVE_REQ(Signal* s) {
+ pspart.execGREP_REMOVE_REQ(s); };
+
+ void fwdSUB_META_DATA(Signal* s) {
+ pspart.execSUB_META_DATA(s); };
+ void fwdSUB_TABLE_DATA(Signal* s) {
+ pspart.execSUB_TABLE_DATA(s); };
+
+ void fwdSUB_GCP_COMPLETE_REP(Signal* s) {
+ pspart.execSUB_GCP_COMPLETE_REP(s); };
+
+ void sendEventRep(Signal * signal,
+ Ndb_logevent_type type,
+ GrepEvent::Subscription event,
+ Uint32 subId,
+ Uint32 subKey,
+ Uint32 err,
+ Uint32 gci=0);
+
+ void getNodeGroupMembers(Signal* signal);
+
+
+ /***************************************************************************
+ * Block Data
+ ***************************************************************************/
+ struct Node {
+ Uint32 nodeId;
+ Uint32 alive;
+ Uint32 nextList;
+ union { Uint32 prevList; Uint32 nextPool; };
+ };
+ typedef Ptr<Node> NodePtr;
+
+ NodeId m_masterNodeId;
+ SLList<Node> m_nodes;
+ NdbNodeBitmask m_aliveNodes;
+ ArrayPool<Node> m_nodePool;
+
+ /**
+ * for all Suma's to keep track of other Suma's in Node group
+ */
+ Uint32 c_nodeGroup;
+ Uint32 c_noNodesInGroup;
+ Uint32 c_idInNodeGroup;
+ NodeId c_nodesInGroup[4];
+
+
+public:
+ /***************************************************************************
+ * GREP PS Coordinator
+ ***************************************************************************/
+ class PSCoord : public BlockComponent {
+
+ private:
+
+ struct SubCoordinator {
+ Uint32 m_subscriberRef;
+ Uint32 m_subscriberData;
+ Uint32 m_coordinatorRef;
+ Uint32 m_subscriptionId;
+ Uint32 m_subscriptionKey;
+ Uint32 m_subscriptionType;
+ NdbNodeBitmask m_participants;
+ Uint32 m_outstandingRequest;
+ SignalCounter m_outstandingParticipants;
+
+ Uint32 nextHash;
+ union { Uint32 prevHash; Uint32 nextPool; };
+
+ Uint32 hashValue() const {
+ return m_subscriptionId + m_subscriptionKey;
+ }
+
+ bool equal(const SubCoordinator & s) const {
+ return
+ m_subscriptionId == s.m_subscriptionId &&
+ m_subscriptionKey == s.m_subscriptionKey;
+ }
+
+ };
+
+ typedef Ptr<SubCoordinator> SubCoordinatorPtr;
+ ArrayPool<SubCoordinator> c_subCoordinatorPool;
+ DLHashTable<SubCoordinator>::Iterator c_subPtr;
+ DLHashTable<SubCoordinator> c_runningSubscriptions;
+
+ void prepareOperationRec(SubCoordinatorPtr ptr,
+ BlockReference subscriber,
+ Uint32 subId,
+ Uint32 subKey,
+ Uint32 request);
+
+ public:
+ PSCoord(class Grep *);
+
+ void execGREP_CREATE_CONF(Signal*);
+ void execGREP_START_CONF(Signal*);
+ void execGREP_SYNC_CONF(Signal*);
+ void execGREP_REMOVE_CONF(Signal*);
+
+ void execGREP_CREATE_REF(Signal*);
+ void execGREP_START_REF(Signal*);
+ void execGREP_SYNC_REF(Signal*);
+ void execGREP_REMOVE_REF(Signal*);
+
+
+ void execCREATE_SUBID_CONF(Signal*); //comes from SUMA
+ void execGREP_CREATE_SUBID_REQ(Signal*);
+
+ void execGREP_SUB_CREATE_REQ(Signal*);
+ void execGREP_SUB_START_REQ(Signal*);
+ void execGREP_SUB_SYNC_REQ(Signal*);
+ void execGREP_SUB_REMOVE_REQ(Signal*);
+
+
+
+ void execCREATE_SUBID_REF(Signal*);
+
+
+
+ void sendCreateSubIdRef_SS(Signal * signal,
+ Uint32 subId,
+ Uint32 subKey,
+ BlockReference to,
+ GrepError::GE_Code err);
+
+
+ void sendSubRemoveRef_SS(Signal * signal,
+ SubCoordinator sub,
+ GrepError::GE_Code err);
+
+ void sendRefToSS(Signal * signal,
+ SubCoordinator sub,
+ GrepError::GE_Code err,
+ SubscriptionData::Part part = (SubscriptionData::Part)0);
+
+ void setRepRef(BlockReference rr) { m_repRef = rr; };
+ //void setAliveNodes(NdbNodeBitmask an) { m_aliveNodes = an; };
+
+ BlockReference m_repRef; ///< Rep node (only one rep node per grep)
+ // NdbNodeBitmask m_aliveNodes;
+
+ Uint32 m_outstandingRequest;
+ SignalCounter m_outstandingParticipants;
+
+ Grep * m_grep;
+ } pscoord;
+ friend class PSCoord;
+
+ /***************************************************************************
+ * GREP PS Participant
+ ***************************************************************************
+ * Participant of GREP Protocols (not necessarily a protocol coordinator)
+ *
+ * This object is only used on primary system
+ ***************************************************************************/
+ class PSPart: public BlockComponent
+ {
+ //protected:
+ //GrepParticipant(const Configuration & conf);
+ //virtual ~GrepParticipant();
+ //BLOCK_DEFINES(GrepParticipant);
+
+ struct Subscription {
+ Uint32 m_subscriberRef;
+ Uint32 m_subscriberData;
+ Uint32 m_subscriptionId;
+ Uint32 m_subscriptionKey;
+ Uint32 m_subscriptionType;
+ Uint32 m_coordinatorRef;
+ Uint32 m_outstandingRequest;
+ Uint32 m_operationPtrI;
+ Uint32 nextHash;
+ union { Uint32 prevHash; Uint32 nextPool; };
+
+ Uint32 hashValue() const {
+ return m_subscriptionId + m_subscriptionKey;
+ }
+
+ bool equal(const Subscription & s) const {
+ return
+ m_subscriptionId == s.m_subscriptionId &&
+ m_subscriptionKey == s.m_subscriptionKey;
+ }
+
+ };
+ typedef Ptr<Subscription> SubscriptionPtr;
+
+ DLHashTable<Subscription> c_subscriptions;
+ DLHashTable<Subscription>::Iterator c_subPtr;
+ ArrayPool<Subscription> c_subscriptionPool;
+
+ public:
+ PSPart(class Grep *);
+
+
+ //protected:
+ /*************************************************************************
+ * SUMA Signal Interface
+ *************************************************************************/
+ void execSUB_CREATE_CONF(Signal*);
+ void execSUB_START_CONF(Signal*);
+ void execSUB_SYNC_CONF(Signal*);
+ void execSUB_REMOVE_CONF(Signal*);
+
+ void execSUB_CREATE_REF(Signal*);
+ void execSUB_START_REF(Signal*);
+ void execSUB_SYNC_REF(Signal*);
+ void execSUB_REMOVE_REF(Signal*);
+
+
+ void execSUB_META_DATA(Signal*);
+ void execSUB_TABLE_DATA(Signal*);
+
+
+ void execSUB_GCP_COMPLETE_REP(Signal*);
+ void execSUB_SYNC_CONTINUE_REQ(Signal*);
+
+ /*************************************************************************
+ * GREP Coordinator Signal Interface
+ *************************************************************************/
+ void execGREP_CREATE_REQ(Signal*);
+ void execGREP_START_REQ(Signal*);
+ void execGREP_SYNC_REQ(Signal*);
+ void execGREP_REMOVE_REQ(Signal*);
+
+ /**
+ * NR/NF signals
+ */
+ void execSTART_ME(Signal *);
+ void execGREP_ADD_SUB_REQ(Signal *);
+ void execGREP_ADD_SUB_REF(Signal *);
+ void execGREP_ADD_SUB_CONF(Signal *);
+
+ /*************************************************************************
+ * GREP Coordinator error handling interface
+ *************************************************************************/
+
+ void sendRefToPSCoord(Signal * signal,
+ Subscription sub,
+ GrepError::GE_Code err,
+ SubscriptionData::Part part = (SubscriptionData::Part)0);
+
+ //protected:
+ BlockReference m_repRef; ///< Replication node
+ ///< (only one rep node per grep)
+ bool m_recoveryMode;
+
+ private:
+ BlockReference m_coordinator;
+ Uint32 m_firstScanGCI;
+ Uint32 m_lastScanGCI;
+ Uint32 m_latestSeenGCI;
+ Grep * m_grep;
+ } pspart;
+ friend class PSPart;
+
+ /***************************************************************************
+ * AddRecSignal Stuff (should maybe be gerneralized)
+ ***************************************************************************/
+ typedef void (Grep::* ExecSignalLocal1) (Signal* signal);
+ typedef void (Grep::PSCoord::* ExecSignalLocal2) (Signal* signal);
+ typedef void (Grep::PSPart::* ExecSignalLocal4) (Signal* signal);
+};
+
+
+/*************************************************************************
+ * Requestor
+ *
+ * The following methods are callbacks (registered functions)
+ * for the Requestor. The Requestor calls these when it needs
+ * something to be done.
+ *************************************************************************/
+void startSubscription(void * cbObj, Signal*, int type);
+void scanSubscription(void * cbObj, Signal*, int type);
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/grep/GrepInit.cpp b/storage/ndb/src/kernel/blocks/grep/GrepInit.cpp
new file mode 100644
index 00000000000..d764fb1f473
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/grep/GrepInit.cpp
@@ -0,0 +1,164 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Grep.hpp"
+#include <Properties.hpp>
+#include <Configuration.hpp>
+
+/*****************************************************************************
+ * Grep Participant
+ *****************************************************************************/
+#if 0
+GrepParticipant::GrepParticipant(const Configuration & conf) :
+ SimulatedBlock(GREP, conf)
+{
+ BLOCK_CONSTRUCTOR(Grep);
+ //m_repRef = 0;
+ m_latestSeenGCI = 0;
+}
+
+GrepParticipant::~GrepParticipant()
+{
+}
+
+BLOCK_FUNCTIONS(GrepParticipant);
+#endif
+
+/*****************************************************************************
+ * Grep Coordinator
+ *****************************************************************************/
+Grep::Grep(const Configuration & conf) :
+ // GrepParticipant(conf),
+ SimulatedBlock(GREP, conf),
+ m_nodes(m_nodePool),
+ pscoord(this),
+ pspart(this)
+{
+ m_nodePool.setSize(MAX_NDB_NODES);
+ m_masterNodeId = getOwnNodeId();
+
+ /***************************************************************************
+ * General Signals
+ ***************************************************************************/
+ addRecSignal(GSN_STTOR, &Grep::execSTTOR);
+ addRecSignal(GSN_NDB_STTOR, &Grep::execNDB_STTOR);
+ addRecSignal(GSN_DUMP_STATE_ORD, &Grep::execDUMP_STATE_ORD);
+ addRecSignal(GSN_READ_NODESCONF, &Grep::execREAD_NODESCONF);
+ addRecSignal(GSN_NODE_FAILREP, &Grep::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Grep::execINCL_NODEREQ);
+
+ addRecSignal(GSN_GREP_REQ, &Grep::execGREP_REQ);
+ addRecSignal(GSN_API_FAILREQ, &Grep::execAPI_FAILREQ);
+
+
+ /***************************************************************************
+ * Grep::PSCoord Signal Interface
+ ***************************************************************************/
+ /**
+ * From Grep::PSPart
+ */
+ addRecSignal(GSN_GREP_CREATE_CONF, &Grep::fwdGREP_CREATE_CONF);
+ addRecSignal(GSN_GREP_START_CONF, &Grep::fwdGREP_START_CONF);
+ addRecSignal(GSN_GREP_SYNC_CONF, &Grep::fwdGREP_SYNC_CONF);
+ addRecSignal(GSN_GREP_REMOVE_CONF, &Grep::fwdGREP_REMOVE_CONF);
+
+ addRecSignal(GSN_GREP_CREATE_REF, &Grep::fwdGREP_CREATE_REF);
+ addRecSignal(GSN_GREP_START_REF, &Grep::fwdGREP_START_REF);
+ addRecSignal(GSN_GREP_REMOVE_REF, &Grep::fwdGREP_REMOVE_REF);
+
+ /**
+ * From Grep::SSCoord to Grep::PSCoord
+ */
+ addRecSignal(GSN_GREP_SUB_START_REQ, &Grep::fwdGREP_SUB_START_REQ);
+ addRecSignal(GSN_GREP_SUB_CREATE_REQ, &Grep::fwdGREP_SUB_CREATE_REQ);
+ addRecSignal(GSN_GREP_SUB_SYNC_REQ, &Grep::fwdGREP_SUB_SYNC_REQ);
+ addRecSignal(GSN_GREP_SUB_REMOVE_REQ, &Grep::fwdGREP_SUB_REMOVE_REQ);
+ addRecSignal(GSN_GREP_CREATE_SUBID_REQ, &Grep::fwdGREP_CREATE_SUBID_REQ);
+
+ /****************************************************************************
+ * PSPart
+ ***************************************************************************/
+ /**
+ * From SUMA to GREP PS Participant. If suma is not a coodinator
+ */
+ addRecSignal(GSN_SUB_START_CONF, &Grep::fwdSUB_START_CONF);
+ addRecSignal(GSN_SUB_CREATE_CONF, &Grep::fwdSUB_CREATE_CONF);
+ addRecSignal(GSN_SUB_SYNC_CONF, &Grep::fwdSUB_SYNC_CONF);
+ addRecSignal(GSN_SUB_REMOVE_CONF, &Grep::fwdSUB_REMOVE_CONF);
+ addRecSignal(GSN_SUB_CREATE_REF, &Grep::fwdSUB_CREATE_REF);
+ addRecSignal(GSN_SUB_START_REF, &Grep::fwdSUB_START_REF);
+ addRecSignal(GSN_SUB_SYNC_REF, &Grep::fwdSUB_SYNC_REF);
+ addRecSignal(GSN_SUB_REMOVE_REF, &Grep::fwdSUB_REMOVE_REF);
+
+ addRecSignal(GSN_SUB_SYNC_CONTINUE_REQ,
+ &Grep::fwdSUB_SYNC_CONTINUE_REQ);
+
+ /**
+ * From Suma to Grep::PSPart. Data signals.
+ */
+ addRecSignal(GSN_SUB_META_DATA, &Grep::fwdSUB_META_DATA);
+ addRecSignal(GSN_SUB_TABLE_DATA, &Grep::fwdSUB_TABLE_DATA);
+ addRecSignal(GSN_SUB_GCP_COMPLETE_REP, &Grep::fwdSUB_GCP_COMPLETE_REP);
+
+ /**
+ * From Grep::PSCoord to Grep::PSPart
+ */
+ addRecSignal(GSN_GREP_CREATE_REQ, &Grep::fwdGREP_CREATE_REQ);
+ addRecSignal(GSN_GREP_START_REQ, &Grep::fwdGREP_START_REQ);
+ addRecSignal(GSN_GREP_REMOVE_REQ, &Grep::fwdGREP_REMOVE_REQ);
+ addRecSignal(GSN_GREP_SYNC_REQ, &Grep::fwdGREP_SYNC_REQ);
+ addRecSignal(GSN_CREATE_SUBID_CONF, &Grep::fwdCREATE_SUBID_CONF);
+ addRecSignal(GSN_GREP_START_ME, &Grep::fwdSTART_ME);
+ addRecSignal(GSN_GREP_ADD_SUB_REQ, &Grep::fwdGREP_ADD_SUB_REQ);
+ addRecSignal(GSN_GREP_ADD_SUB_REF, &Grep::fwdGREP_ADD_SUB_REF);
+ addRecSignal(GSN_GREP_ADD_SUB_CONF, &Grep::fwdGREP_ADD_SUB_CONF);
+}
+
+Grep::~Grep()
+{
+}
+
+BLOCK_FUNCTIONS(Grep)
+
+Grep::PSPart::PSPart(Grep * sb) :
+ BlockComponent(sb),
+ c_subscriptions(c_subscriptionPool)
+{
+ m_grep = sb;
+
+ m_firstScanGCI = 1; // Empty interval = [1,0]
+ m_lastScanGCI = 0;
+
+ m_latestSeenGCI = 0;
+
+ c_subscriptions.setSize(10);
+ c_subscriptionPool.setSize(10);
+}
+
+Grep::PSCoord::PSCoord(Grep * sb) :
+ BlockComponent(sb),
+ c_runningSubscriptions(c_subCoordinatorPool)
+{
+ m_grep = sb;
+ c_runningSubscriptions.setSize(10);
+ c_subCoordinatorPool.setSize(2);
+}
+
+//BLOCK_FUNCTIONS(Grep::PSCoord);
+
+BlockComponent::BlockComponent(SimulatedBlock * sb) {
+ m_sb = sb;
+}
diff --git a/storage/ndb/src/kernel/blocks/grep/Makefile.am b/storage/ndb/src/kernel/blocks/grep/Makefile.am
new file mode 100644
index 00000000000..6d2b422784b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/grep/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LIBRARIES = libgrep.a
+
+libgrep_a_SOURCES = Grep.cpp GrepInit.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libgrep.dsp
+
+libgrep.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libgrep_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/grep/systab_test/Makefile b/storage/ndb/src/kernel/blocks/grep/systab_test/Makefile
new file mode 100644
index 00000000000..bd69e0f3799
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/grep/systab_test/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := grep_systab_test
+BIN_TARGET_ARCHIVES := portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES = ../GrepSystemTable.cpp grep_systab_test.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/blocks/grep/systab_test/grep_systab_test.cpp b/storage/ndb/src/kernel/blocks/grep/systab_test/grep_systab_test.cpp
new file mode 100644
index 00000000000..e3a77af4e4e
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/grep/systab_test/grep_systab_test.cpp
@@ -0,0 +1,138 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/**
+ * Unit Test for GrepSystemTable
+ */
+
+#include "../GrepSystemTable.hpp"
+#include <SimulatedBlock.hpp>
+
+#define EXEC(X) ( ndbout << endl, ndbout_c(#X), X )
+
+int
+main () {
+ GrepSystemTable st;
+
+ Uint32 f, l;
+
+ ndbout_c("*************************************");
+ ndbout_c("* GrepSystemTable Unit Test Program *");
+ ndbout_c("*************************************");
+
+ ndbout_c("--------------------------------------------------------");
+ ndbout_c("Test 1: Clear");
+ ndbout_c("--------------------------------------------------------");
+
+ EXEC(st.set(GrepSystemTable::PS, 22, 26));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 26);
+
+ EXEC(st.clear(GrepSystemTable::PS, 20, 24));
+ st.print();
+ st.require(GrepSystemTable::PS, 25, 26);
+
+ EXEC(st.clear(GrepSystemTable::PS, 0, 100));
+ st.print();
+ st.require(GrepSystemTable::PS, 1, 0);
+
+ EXEC(st.set(GrepSystemTable::PS, 22, 26));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 26);
+
+ EXEC(st.clear(GrepSystemTable::PS, 24, 28));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 23);
+
+ EXEC(st.clear(GrepSystemTable::PS, 0, 100));
+ st.print();
+ st.require(GrepSystemTable::PS, 1, 0);
+
+ EXEC(st.set(GrepSystemTable::PS, 22, 26));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 26);
+
+ EXEC(st.clear(GrepSystemTable::PS, 24, 26));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 23);
+
+ EXEC(st.clear(GrepSystemTable::PS, 0, 100));
+ st.print();
+ st.require(GrepSystemTable::PS, 1, 0);
+
+ EXEC(st.set(GrepSystemTable::PS, 22, 26));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 26);
+
+ EXEC(st.clear(GrepSystemTable::PS, 22, 24));
+ st.print();
+ st.require(GrepSystemTable::PS, 25, 26);
+
+ ndbout_c("--------------------------------------------------------");
+ ndbout_c("Test 2: PS --> SSreq");
+ ndbout_c("--------------------------------------------------------");
+
+ EXEC(st.set(GrepSystemTable::PS, 22, 26));
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 26);
+ st.require(GrepSystemTable::SSReq, 1, 0);
+
+ if (!EXEC(st.copy(GrepSystemTable::PS, GrepSystemTable::SSReq, 3, &f, &l)))
+ ndbout_c("%s:%d: Illegal copy!", __FILE__, __FILE__);
+ ndbout_c("f=%d, l=%d", f, l);
+ st.print();
+ st.require(GrepSystemTable::PS, 22, 26);
+ st.require(GrepSystemTable::SSReq, 22, 24);
+
+ EXEC(st.clear(GrepSystemTable::PS, 22, 22));
+ st.print();
+ st.require(GrepSystemTable::PS, 23, 26);
+ st.require(GrepSystemTable::SSReq, 22, 24);
+
+ if (!EXEC(st.copy(GrepSystemTable::PS, GrepSystemTable::SSReq, 2, &f, &l)))
+ ndbout_c("%s:%d: Illegal copy!", __FILE__, __LINE__);
+ ndbout_c("f=%d, l=%d", f, l);
+ st.print();
+ st.require(GrepSystemTable::PS, 23, 26);
+ st.require(GrepSystemTable::SSReq, 22, 26);
+
+ st.set(GrepSystemTable::SS, 7, 9);
+ st.set(GrepSystemTable::InsReq, 7, 9);
+ if (EXEC(st.movable(GrepSystemTable::SS, GrepSystemTable::InsReq)))
+ ndbout_c("%s:%d: Illegal move!", __FILE__, __LINE__);
+ st.print();
+ st.require(GrepSystemTable::SS, 7, 9);
+ st.require(GrepSystemTable::InsReq, 7, 9);
+
+ EXEC(st.intervalMinus(7, 9, 7, 7, &f, &l));
+ ndbout_c("f=%d, l=%d", f, l);
+
+ st.clear(GrepSystemTable::InsReq, 8, 9);
+ st.require(GrepSystemTable::SS, 7, 9);
+ st.require(GrepSystemTable::InsReq, 7, 7);
+ if (EXEC(st.movable(GrepSystemTable::SS, GrepSystemTable::InsReq)) != 2)
+ ndbout_c("%s:%d: Illegal move!", __FILE__, __LINE__);
+ st.print();
+
+ EXEC(st.copy(GrepSystemTable::SS, GrepSystemTable::InsReq, &f));
+ st.print();
+ st.require(GrepSystemTable::SS, 7, 9);
+ st.require(GrepSystemTable::InsReq, 7, 8);
+
+ ndbout_c("--------------------------------------------------------");
+ ndbout_c("Test completed");
+ ndbout_c("--------------------------------------------------------");
+}
diff --git a/storage/ndb/src/kernel/blocks/mutexes.hpp b/storage/ndb/src/kernel/blocks/mutexes.hpp
new file mode 100644
index 00000000000..5c0276fc4fa
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/mutexes.hpp
@@ -0,0 +1,39 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 KERNEL_MUTEXES_HPP
+#define KERNEL_MUTEXES_HPP
+
+#include <ndb_types.h>
+
+/**
+ * This mutex is used by:
+ * DIH - before sending START_LCP to all participants
+ * DICT - before commiting a CREATE TABLE
+ * BACKUP - before sending DEFINE_BACKUP
+ */
+#define DIH_START_LCP_MUTEX 0
+#define DICT_COMMIT_TABLE_MUTEX 0
+
+/**
+ * This mutex is used by
+ * DIH - before switching primary replica
+ * BACKUP - before sending DEFINE_BACKUP
+ */
+#define DIH_SWITCH_PRIMARY_MUTEX 1
+#define BACKUP_DEFINE_MUTEX 1
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbcntr/Makefile.am b/storage/ndb/src/kernel/blocks/ndbcntr/Makefile.am
new file mode 100644
index 00000000000..3f24675b2b3
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbcntr/Makefile.am
@@ -0,0 +1,26 @@
+noinst_LIBRARIES = libndbcntr.a
+
+libndbcntr_a_SOURCES = \
+ NdbcntrInit.cpp \
+ NdbcntrSysTable.cpp \
+ NdbcntrMain.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libndbcntr.dsp
+
+libndbcntr.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libndbcntr_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/ndbcntr/Ndbcntr.hpp b/storage/ndb/src/kernel/blocks/ndbcntr/Ndbcntr.hpp
new file mode 100644
index 00000000000..639d300d6df
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbcntr/Ndbcntr.hpp
@@ -0,0 +1,376 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NDBCNTR_H
+#define NDBCNTR_H
+
+
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+#include <ndb_limits.h>
+#include <signaldata/StopReq.hpp>
+#include <signaldata/ResumeReq.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/CntrStart.hpp>
+#include <signaldata/CheckNodeGroups.hpp>
+
+#include <signaldata/UpgradeStartup.hpp>
+
+#include <NodeState.hpp>
+#include <NdbTick.h>
+
+#ifdef NDBCNTR_C
+/*
+2.1 GLOBAL SYMBOLS
+------------------
+*/
+/*
+2.2 LOCAL SYMBOLS
+-----------------
+*/
+#define ZNO_NDB_BLOCKS 6 /* ACC, DICT, DIH, LQH, TC, TUP */
+
+#define ZNOT_AVAILABLE 913
+
+//------- OTHERS ---------------------------------------------
+#define ZSTARTUP 1
+#define ZSHUTDOWN 2
+
+#define ZSIZE_NDB_BLOCKS_REC 16 /* MAX BLOCKS IN NDB */
+#define ZSIZE_SYSTAB 2048
+#define ZSTART_PHASE_1 1
+#define ZSTART_PHASE_2 2
+#define ZSTART_PHASE_3 3
+#define ZSTART_PHASE_4 4
+#define ZSTART_PHASE_5 5
+#define ZSTART_PHASE_6 6
+#define ZSTART_PHASE_7 7
+#define ZSTART_PHASE_8 8
+#define ZSTART_PHASE_9 9
+#define ZSTART_PHASE_END 255
+#define ZWAITPOINT_4_1 1
+#define ZWAITPOINT_4_2 2
+#define ZWAITPOINT_5_1 3
+#define ZWAITPOINT_5_2 4
+#define ZWAITPOINT_6_1 5
+#define ZWAITPOINT_6_2 6
+#define ZWAITPOINT_7_1 7
+#define ZWAITPOINT_7_2 8
+#define ZSYSTAB_VERSION 1
+#endif
+
+class Ndbcntr: public SimulatedBlock {
+public:
+// Records
+
+/* FSREADREQ FSWRITEREQ */
+/**
+ * 2.3 RECORDS AND FILESIZES
+ * ------------------------------------------------------------
+ */
+
+ struct StartRecord {
+ Uint64 m_startTime;
+
+ void reset();
+ NdbNodeBitmask m_starting;
+ NdbNodeBitmask m_waiting; // == (m_withLog | m_withoutLog)
+ NdbNodeBitmask m_withLog;
+ NdbNodeBitmask m_withoutLog;
+ Uint32 m_lastGci;
+ Uint32 m_lastGciNodeId;
+
+ Uint64 m_startPartialTimeout;
+ Uint64 m_startPartitionedTimeout;
+ Uint64 m_startFailureTimeout;
+ struct {
+ Uint32 m_nodeId;
+ Uint32 m_lastGci;
+ } m_logNodes[MAX_NDB_NODES];
+ Uint32 m_logNodesCount;
+ } c_start;
+
+ struct NdbBlocksRec {
+ BlockReference blockref;
+ }; /* p2c: size = 2 bytes */
+
+ typedef Ptr<NdbBlocksRec> NdbBlocksRecPtr;
+
+ /**
+ * Ndbcntr creates and initializes system tables on initial system start.
+ * The tables are defined in static structs in NdbcntrSysTable.cpp.
+ */
+ struct SysColumn {
+ unsigned pos;
+ const char* name;
+ // DictTabInfo
+ DictTabInfo::ExtType type;
+ Uint32 length;
+ bool keyFlag;
+ bool nullable;
+ };
+ struct SysTable {
+ const char* name;
+ unsigned columnCount;
+ const SysColumn* columnList;
+ // DictTabInfo
+ DictTabInfo::TableType tableType;
+ DictTabInfo::FragmentType fragmentType;
+ bool tableLoggedFlag;
+ // saved table id
+ mutable Uint32 tableId;
+ };
+ struct SysIndex {
+ const char* name;
+ const SysTable* primaryTable;
+ Uint32 columnCount;
+ Uint32 columnList[4];
+ // DictTabInfo
+ DictTabInfo::TableType indexType;
+ DictTabInfo::FragmentType fragmentType;
+ bool indexLoggedFlag;
+ // saved index table id
+ mutable Uint32 indexId;
+ };
+ static const SysTable* g_sysTableList[];
+ static const unsigned g_sysTableCount;
+ // the system tables
+ static const SysTable g_sysTable_SYSTAB_0;
+ static const SysTable g_sysTable_NDBEVENTS_0;
+
+public:
+ Ndbcntr(const class Configuration &);
+ virtual ~Ndbcntr();
+
+private:
+ BLOCK_DEFINES(Ndbcntr);
+
+ // Transit signals
+ void execCONTINUEB(Signal* signal);
+ void execREAD_NODESCONF(Signal* signal);
+ void execREAD_NODESREF(Signal* signal);
+ void execCM_ADD_REP(Signal* signal);
+ void execCNTR_START_REQ(Signal* signal);
+ void execCNTR_START_REF(Signal* signal);
+ void execCNTR_START_CONF(Signal* signal);
+ void execCNTR_START_REP(Signal* signal);
+ void execCNTR_WAITREP(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execSYSTEM_ERROR(Signal* signal);
+
+ // Received signals
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execTCSEIZECONF(Signal* signal);
+ void execTCSEIZEREF(Signal* signal);
+ void execTCRELEASECONF(Signal* signal);
+ void execTCRELEASEREF(Signal* signal);
+ void execTCKEYCONF(Signal* signal);
+ void execTCKEYREF(Signal* signal);
+ void execTCROLLBACKREP(Signal* signal);
+ void execGETGCICONF(Signal* signal);
+ void execDIH_RESTARTCONF(Signal* signal);
+ void execDIH_RESTARTREF(Signal* signal);
+ void execCREATE_TABLE_REF(Signal* signal);
+ void execCREATE_TABLE_CONF(Signal* signal);
+ void execNDB_STTORRY(Signal* signal);
+ void execNDB_STARTCONF(Signal* signal);
+ void execREAD_NODESREQ(Signal* signal);
+ void execNDB_STARTREF(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+
+ void execSTOP_PERM_REF(Signal* signal);
+ void execSTOP_PERM_CONF(Signal* signal);
+
+ void execSTOP_ME_REF(Signal* signal);
+ void execSTOP_ME_CONF(Signal* signal);
+
+ void execWAIT_GCP_REF(Signal* signal);
+ void execWAIT_GCP_CONF(Signal* signal);
+
+ void execSTOP_REQ(Signal* signal);
+ void execRESUME_REQ(Signal* signal);
+
+ void execCHANGE_NODE_STATE_CONF(Signal* signal);
+
+ void execABORT_ALL_REF(Signal* signal);
+ void execABORT_ALL_CONF(Signal* signal);
+
+ // Statement blocks
+ void sendCreateTabReq(Signal* signal, const char* buffer, Uint32 bufLen);
+ void startInsertTransactions(Signal* signal);
+ void initData(Signal* signal);
+ void resetStartVariables(Signal* signal);
+ void sendCntrStartReq(Signal* signal);
+ void sendCntrStartRef(Signal*, Uint32 nodeId, CntrStartRef::ErrorCode);
+ void sendNdbSttor(Signal* signal);
+ void sendSttorry(Signal* signal);
+
+ bool trySystemRestart(Signal* signal);
+ void startWaitingNodes(Signal* signal);
+ CheckNodeGroups::Output checkNodeGroups(Signal*, const NdbNodeBitmask &);
+
+ // Generated statement blocks
+ void systemErrorLab(Signal* signal);
+
+ void createSystableLab(Signal* signal, unsigned index);
+ void crSystab7Lab(Signal* signal);
+ void crSystab8Lab(Signal* signal);
+ void crSystab9Lab(Signal* signal);
+
+ void startPhase1Lab(Signal* signal);
+ void startPhase2Lab(Signal* signal);
+ void startPhase3Lab(Signal* signal);
+ void startPhase4Lab(Signal* signal);
+ void startPhase5Lab(Signal* signal);
+ // jump 2 to resync phase counters
+ void startPhase8Lab(Signal* signal);
+ void startPhase9Lab(Signal* signal);
+ void ph2ALab(Signal* signal);
+ void ph2CLab(Signal* signal);
+ void ph2ELab(Signal* signal);
+ void ph2FLab(Signal* signal);
+ void ph2GLab(Signal* signal);
+ void ph3ALab(Signal* signal);
+ void ph4ALab(Signal* signal);
+ void ph4BLab(Signal* signal);
+ void ph4CLab(Signal* signal);
+ void ph5ALab(Signal* signal);
+ void ph6ALab(Signal* signal);
+ void ph6BLab(Signal* signal);
+ void ph7ALab(Signal* signal);
+ void ph8ALab(Signal* signal);
+
+
+ void waitpoint41Lab(Signal* signal);
+ void waitpoint51Lab(Signal* signal);
+ void waitpoint52Lab(Signal* signal);
+ void waitpoint61Lab(Signal* signal);
+ void waitpoint71Lab(Signal* signal);
+
+ void updateNodeState(Signal* signal, const NodeState & newState) const ;
+ void getNodeGroup(Signal* signal);
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ // Variables
+ /**------------------------------------------------------------------------
+ * CONTAIN INFO ABOUT ALL NODES IN CLUSTER. NODE_PTR ARE USED AS NODE NUMBER
+ * IF THE STATE ARE ZDELETE THEN THE NODE DOESN'T EXIST. NODES ARE ALLOWED
+ * TO REGISTER (ZADD) DURING RESTART.
+ *
+ * WHEN THE SYSTEM IS RUNNING THE MASTER WILL CHECK IF ANY NODE HAS MADE
+ * A CNTR_MASTERREQ AND TAKE CARE OF THE REQUEST.
+ * TO CONFIRM THE REQ, THE MASTER DEMANDS THAT ALL RUNNING NODES HAS VOTED
+ * FOR THE NEW NODE.
+ * NODE_PTR:MASTER_REQ IS USED DURING RESTART TO LOG
+ * POSTPONED CNTR_MASTERREQ'S
+ *------------------------------------------------------------------------*/
+ NdbBlocksRec *ndbBlocksRec;
+
+ /*
+ 2.4 COMMON STORED VARIABLES
+ */
+ UintR cgciSystab;
+ UintR ckey;
+ //UintR csystabId;
+ UintR cnoWaitrep6;
+ UintR cnoWaitrep7;
+ UintR ctcConnectionP;
+ UintR ctcReqInfo;
+ Uint8 ctransidPhase;
+ Uint16 cresponses;
+
+ Uint8 cstartPhase;
+ Uint16 cinternalStartphase;
+
+ Uint16 cmasterNodeId;
+ Uint16 cndbBlocksCount;
+ Uint16 cnoStartNodes;
+ UintR cnoWaitrep;
+ NodeState::StartType ctypeOfStart;
+ Uint16 cdynamicNodeId;
+
+ Uint32 c_fsRemoveCount;
+ Uint32 c_nodeGroup;
+ void clearFilesystem(Signal* signal);
+ void execFSREMOVEREF(Signal* signal);
+ void execFSREMOVECONF(Signal* signal);
+
+ NdbNodeBitmask c_allDefinedNodes;
+ NdbNodeBitmask c_clusterNodes; // All members of qmgr cluster
+ NdbNodeBitmask c_startedNodes; // All cntr started nodes
+
+public:
+ struct StopRecord {
+ public:
+ StopRecord(Ndbcntr & _cntr) : cntr(_cntr) {
+ stopReq.senderRef = 0;
+ }
+
+ Ndbcntr & cntr;
+ StopReq stopReq; // Signal data
+ NDB_TICKS stopInitiatedTime; // When was the stop initiated
+
+ bool checkNodeFail(Signal* signal);
+ void checkTimeout(Signal* signal);
+ void checkApiTimeout(Signal* signal);
+ void checkTcTimeout(Signal* signal);
+ void checkLqhTimeout_1(Signal* signal);
+ void checkLqhTimeout_2(Signal* signal);
+
+ BlockNumber number() const { return cntr.number(); }
+ void progError(int line, int cause, const char * extra) {
+ cntr.progError(line, cause, extra);
+ }
+ };
+private:
+ StopRecord c_stopRec;
+ friend struct StopRecord;
+
+ struct Missra {
+ Missra(Ndbcntr & ref) : cntr(ref) { }
+
+ Uint32 currentBlockIndex;
+ Uint32 currentStartPhase;
+ Uint32 nextStartPhase[NO_OF_BLOCKS];
+
+ void execSTART_ORD(Signal* signal);
+ void execSTTORRY(Signal* signal);
+ void sendNextSTTOR(Signal* signal);
+ void execREAD_CONFIG_CONF(Signal* signal);
+ void sendNextREAD_CONFIG_REQ(Signal* signal);
+
+ BlockNumber number() const { return cntr.number(); }
+ void progError(int line, int cause, const char * extra) {
+ cntr.progError(line, cause, extra);
+ }
+ Ndbcntr & cntr;
+ };
+
+ Missra c_missra;
+ friend struct Missra;
+
+ void execSTTORRY(Signal* signal);
+ void execSTART_ORD(Signal* signal);
+ void execREAD_CONFIG_CONF(Signal*);
+
+ friend struct UpgradeStartup;
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp
new file mode 100644
index 00000000000..c7b472fc91a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp
@@ -0,0 +1,117 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#define NDBCNTR_C
+#include "Ndbcntr.hpp"
+#include <ndb_limits.h>
+
+#define DEBUG(x) { ndbout << "Ndbcntr::" << x << endl; }
+
+
+void Ndbcntr::initData()
+{
+
+ // Records with constant sizes
+ ndbBlocksRec = new NdbBlocksRec[ZSIZE_NDB_BLOCKS_REC];
+}//Ndbcntr::initData()
+
+void Ndbcntr::initRecords()
+{
+ // Records with dynamic sizes
+}//Ndbcntr::initRecords()
+
+Ndbcntr::Ndbcntr(const class Configuration & conf):
+ SimulatedBlock(NDBCNTR, conf),
+ cnoWaitrep6(0),
+ cnoWaitrep7(0),
+ c_stopRec(* this),
+ c_missra(* this)
+{
+
+ BLOCK_CONSTRUCTOR(Ndbcntr);
+
+ // Transit signals
+ addRecSignal(GSN_CONTINUEB, &Ndbcntr::execCONTINUEB);
+ addRecSignal(GSN_READ_NODESCONF, &Ndbcntr::execREAD_NODESCONF);
+ addRecSignal(GSN_READ_NODESREF, &Ndbcntr::execREAD_NODESREF);
+ addRecSignal(GSN_CM_ADD_REP, &Ndbcntr::execCM_ADD_REP);
+ addRecSignal(GSN_CNTR_START_REQ, &Ndbcntr::execCNTR_START_REQ);
+ addRecSignal(GSN_CNTR_START_REF, &Ndbcntr::execCNTR_START_REF);
+ addRecSignal(GSN_CNTR_START_CONF, &Ndbcntr::execCNTR_START_CONF);
+ addRecSignal(GSN_CNTR_WAITREP, &Ndbcntr::execCNTR_WAITREP);
+ addRecSignal(GSN_CNTR_START_REP, &Ndbcntr::execCNTR_START_REP);
+ addRecSignal(GSN_NODE_FAILREP, &Ndbcntr::execNODE_FAILREP);
+ addRecSignal(GSN_SYSTEM_ERROR , &Ndbcntr::execSYSTEM_ERROR);
+
+ // Received signals
+ addRecSignal(GSN_DUMP_STATE_ORD, &Ndbcntr::execDUMP_STATE_ORD);
+ addRecSignal(GSN_STTOR, &Ndbcntr::execSTTOR);
+ addRecSignal(GSN_TCSEIZECONF, &Ndbcntr::execTCSEIZECONF);
+ addRecSignal(GSN_TCSEIZEREF, &Ndbcntr::execTCSEIZEREF);
+ addRecSignal(GSN_TCRELEASECONF, &Ndbcntr::execTCRELEASECONF);
+ addRecSignal(GSN_TCRELEASEREF, &Ndbcntr::execTCRELEASEREF);
+ addRecSignal(GSN_TCKEYCONF, &Ndbcntr::execTCKEYCONF);
+ addRecSignal(GSN_TCKEYREF, &Ndbcntr::execTCKEYREF);
+ addRecSignal(GSN_TCROLLBACKREP, &Ndbcntr::execTCROLLBACKREP);
+ addRecSignal(GSN_GETGCICONF, &Ndbcntr::execGETGCICONF);
+ addRecSignal(GSN_DIH_RESTARTCONF, &Ndbcntr::execDIH_RESTARTCONF);
+ addRecSignal(GSN_DIH_RESTARTREF, &Ndbcntr::execDIH_RESTARTREF);
+ addRecSignal(GSN_CREATE_TABLE_REF, &Ndbcntr::execCREATE_TABLE_REF);
+ addRecSignal(GSN_CREATE_TABLE_CONF, &Ndbcntr::execCREATE_TABLE_CONF);
+ addRecSignal(GSN_NDB_STTORRY, &Ndbcntr::execNDB_STTORRY);
+ addRecSignal(GSN_NDB_STARTCONF, &Ndbcntr::execNDB_STARTCONF);
+ addRecSignal(GSN_READ_NODESREQ, &Ndbcntr::execREAD_NODESREQ);
+ addRecSignal(GSN_NDB_STARTREF, &Ndbcntr::execNDB_STARTREF);
+ addRecSignal(GSN_SET_VAR_REQ, &Ndbcntr::execSET_VAR_REQ);
+
+ addRecSignal(GSN_STOP_PERM_REF, &Ndbcntr::execSTOP_PERM_REF);
+ addRecSignal(GSN_STOP_PERM_CONF, &Ndbcntr::execSTOP_PERM_CONF);
+
+ addRecSignal(GSN_STOP_ME_REF, &Ndbcntr::execSTOP_ME_REF);
+ addRecSignal(GSN_STOP_ME_CONF, &Ndbcntr::execSTOP_ME_CONF);
+
+ addRecSignal(GSN_STOP_REQ, &Ndbcntr::execSTOP_REQ);
+ addRecSignal(GSN_RESUME_REQ, &Ndbcntr::execRESUME_REQ);
+
+ addRecSignal(GSN_WAIT_GCP_REF, &Ndbcntr::execWAIT_GCP_REF);
+ addRecSignal(GSN_WAIT_GCP_CONF, &Ndbcntr::execWAIT_GCP_CONF);
+ addRecSignal(GSN_CHANGE_NODE_STATE_CONF,
+ &Ndbcntr::execCHANGE_NODE_STATE_CONF);
+
+ addRecSignal(GSN_ABORT_ALL_REF, &Ndbcntr::execABORT_ALL_REF);
+ addRecSignal(GSN_ABORT_ALL_CONF, &Ndbcntr::execABORT_ALL_CONF);
+
+ addRecSignal(GSN_START_ORD, &Ndbcntr::execSTART_ORD);
+ addRecSignal(GSN_STTORRY, &Ndbcntr::execSTTORRY);
+ addRecSignal(GSN_READ_CONFIG_CONF, &Ndbcntr::execREAD_CONFIG_CONF);
+
+ addRecSignal(GSN_FSREMOVEREF, &Ndbcntr::execFSREMOVEREF);
+ addRecSignal(GSN_FSREMOVECONF, &Ndbcntr::execFSREMOVECONF);
+
+ initData();
+ ctypeOfStart = NodeState::ST_ILLEGAL_TYPE;
+ c_start.m_startTime = NdbTick_CurrentMillisecond();
+}//Ndbcntr::Ndbcntr()
+
+Ndbcntr::~Ndbcntr()
+{
+ delete []ndbBlocksRec;
+
+}//Ndbcntr::~Ndbcntr()
+
+BLOCK_FUNCTIONS(Ndbcntr)
diff --git a/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp
new file mode 100644
index 00000000000..524a40697bf
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp
@@ -0,0 +1,2695 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#define NDBCNTR_C
+#include "Ndbcntr.hpp"
+
+#include <ndb_limits.h>
+#include <ndb_version.h>
+#include <SimpleProperties.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/CreateTable.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/TcKeyConf.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/NodeStateSignalData.hpp>
+#include <signaldata/StopPerm.hpp>
+#include <signaldata/StopMe.hpp>
+#include <signaldata/WaitGCP.hpp>
+#include <signaldata/CheckNodeGroups.hpp>
+#include <signaldata/StartOrd.hpp>
+#include <signaldata/AbortAll.hpp>
+#include <signaldata/SystemError.hpp>
+#include <signaldata/NdbSttor.hpp>
+#include <signaldata/CntrStart.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+#include <signaldata/FsRemoveReq.hpp>
+#include <signaldata/ReadConfig.hpp>
+
+#include <AttributeHeader.hpp>
+#include <Configuration.hpp>
+#include <DebuggerNames.hpp>
+
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+/**
+ * ALL_BLOCKS Used during start phases and while changing node state
+ *
+ * NDBFS_REF Has to be before NDBCNTR_REF (due to "ndb -i" stuff)
+ */
+struct BlockInfo {
+ BlockReference Ref; // BlockReference
+ Uint32 NextSP; // Next start phase
+ Uint32 ErrorInsertStart;
+ Uint32 ErrorInsertStop;
+};
+
+static BlockInfo ALL_BLOCKS[] = {
+ { DBTC_REF, 1 , 8000, 8035 },
+ { DBDIH_REF, 1 , 7000, 7173 },
+ { DBLQH_REF, 1 , 5000, 5030 },
+ { DBACC_REF, 1 , 3000, 3999 },
+ { DBTUP_REF, 1 , 4000, 4007 },
+ { DBDICT_REF, 1 , 6000, 6003 },
+ { NDBFS_REF, 0 , 2000, 2999 },
+ { NDBCNTR_REF, 0 , 1000, 1999 },
+ { QMGR_REF, 1 , 1, 999 },
+ { CMVMI_REF, 1 , 9000, 9999 },
+ { TRIX_REF, 1 , 0, 0 },
+ { BACKUP_REF, 1 , 10000, 10999 },
+ { DBUTIL_REF, 1 , 11000, 11999 },
+ { SUMA_REF, 1 , 13000, 13999 },
+ { GREP_REF, 1 , 0, 0 },
+ { DBTUX_REF, 1 , 12000, 12999 }
+};
+
+static const Uint32 ALL_BLOCKS_SZ = sizeof(ALL_BLOCKS)/sizeof(BlockInfo);
+
+/*******************************/
+/* CONTINUEB */
+/*******************************/
+void Ndbcntr::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ UintR Ttemp1 = signal->theData[0];
+ switch (Ttemp1) {
+ case ZSTARTUP:{
+ if(getNodeState().startLevel == NodeState::SL_STARTED){
+ jam();
+ return;
+ }
+
+ if(cmasterNodeId == getOwnNodeId() && c_start.m_starting.isclear()){
+ jam();
+ trySystemRestart(signal);
+ // Fall-through
+ }
+
+ Uint64 now = NdbTick_CurrentMillisecond();
+ if(now > c_start.m_startFailureTimeout)
+ {
+ jam();
+ Uint32 to_3= 0;
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndb_mgm_get_int_parameter(p, CFG_DB_START_FAILURE_TIMEOUT, &to_3);
+ BaseString tmp;
+ tmp.append("Shutting down node as total restart time exceeds "
+ " StartFailureTimeout as set in config file ");
+ if(to_3 == 0)
+ tmp.append(" 0 (inifinite)");
+ else
+ tmp.appfmt(" %d", to_3);
+
+ progError(__LINE__, ERR_SYSTEM_ERROR, tmp.c_str());
+ }
+
+ signal->theData[0] = ZSTARTUP;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 1000, 1);
+ break;
+ }
+ case ZSHUTDOWN:
+ jam();
+ c_stopRec.checkTimeout(signal);
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Ndbcntr::execCONTINUEB()
+
+/*******************************/
+/* SYSTEM_ERROR */
+/*******************************/
+void Ndbcntr::execSYSTEM_ERROR(Signal* signal)
+{
+ const SystemError * const sysErr = (SystemError *)signal->getDataPtr();
+ char buf[100];
+ int killingNode = refToNode(sysErr->errorRef);
+
+ jamEntry();
+ switch (sysErr->errorCode){
+ case SystemError::StartInProgressError:
+ BaseString::snprintf(buf, sizeof(buf),
+ "Node %d killed this node because "
+ "master start in progress error",
+ killingNode);
+ break;
+
+ case SystemError::GCPStopDetected:
+ BaseString::snprintf(buf, sizeof(buf),
+ "Node %d killed this node because "
+ "GCP stop was detected",
+ killingNode);
+ break;
+
+ case SystemError::ScanfragTimeout:
+ BaseString::snprintf(buf, sizeof(buf),
+ "Node %d killed this node because "
+ "a fragment scan timed out and could not be stopped",
+ killingNode);
+ break;
+
+ case SystemError::ScanfragStateError:
+ BaseString::snprintf(buf, sizeof(buf),
+ "Node %d killed this node because "
+ "the state of a fragment scan was out of sync.",
+ killingNode);
+ break;
+
+ case SystemError::CopyFragRefError:
+ BaseString::snprintf(buf, sizeof(buf),
+ "Node %d killed this node because "
+ "it could not copy a fragment during node restart",
+ killingNode);
+ break;
+
+ default:
+ BaseString::snprintf(buf, sizeof(buf), "System error %d, "
+ " this node was killed by node %d",
+ sysErr->errorCode, killingNode);
+ break;
+ }
+
+ progError(__LINE__,
+ ERR_SYSTEM_ERROR,
+ buf);
+ return;
+}//Ndbcntr::execSYSTEM_ERROR()
+
+void Ndbcntr::execSTTOR(Signal* signal)
+{
+ jamEntry();
+ cstartPhase = signal->theData[1];
+
+ NodeState newState(NodeState::SL_STARTING, cstartPhase,
+ (NodeState::StartType)ctypeOfStart);
+ updateNodeState(signal, newState);
+
+ cndbBlocksCount = 0;
+ cinternalStartphase = cstartPhase - 1;
+
+ switch (cstartPhase) {
+ case 0:
+ if(theConfiguration.getInitialStart()){
+ jam();
+ c_fsRemoveCount = 0;
+ clearFilesystem(signal);
+ return;
+ }
+ sendSttorry(signal);
+ break;
+ case ZSTART_PHASE_1:
+ jam();
+ startPhase1Lab(signal);
+ break;
+ case ZSTART_PHASE_2:
+ jam();
+ startPhase2Lab(signal);
+ break;
+ case ZSTART_PHASE_3:
+ jam();
+ startPhase3Lab(signal);
+ break;
+ case ZSTART_PHASE_4:
+ jam();
+ startPhase4Lab(signal);
+ break;
+ case ZSTART_PHASE_5:
+ jam();
+ startPhase5Lab(signal);
+ break;
+ case 6:
+ jam();
+ getNodeGroup(signal);
+ // Fall through
+ break;
+ case ZSTART_PHASE_8:
+ jam();
+ startPhase8Lab(signal);
+ break;
+ case ZSTART_PHASE_9:
+ jam();
+ startPhase9Lab(signal);
+ break;
+ default:
+ jam();
+ sendSttorry(signal);
+ break;
+ }//switch
+}//Ndbcntr::execSTTOR()
+
+void
+Ndbcntr::getNodeGroup(Signal* signal){
+ jam();
+ CheckNodeGroups * sd = (CheckNodeGroups*)signal->getDataPtrSend();
+ sd->requestType = CheckNodeGroups::Direct | CheckNodeGroups::GetNodeGroup;
+ EXECUTE_DIRECT(DBDIH, GSN_CHECKNODEGROUPSREQ, signal,
+ CheckNodeGroups::SignalLength);
+ jamEntry();
+ c_nodeGroup = sd->output;
+ sendSttorry(signal);
+}
+
+/*******************************/
+/* NDB_STTORRY */
+/*******************************/
+void Ndbcntr::execNDB_STTORRY(Signal* signal)
+{
+ jamEntry();
+ switch (cstartPhase) {
+ case ZSTART_PHASE_2:
+ jam();
+ ph2GLab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_3:
+ jam();
+ ph3ALab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_4:
+ jam();
+ ph4BLab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_5:
+ jam();
+ ph5ALab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_6:
+ jam();
+ ph6ALab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_7:
+ jam();
+ ph6BLab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_8:
+ jam();
+ ph7ALab(signal);
+ return;
+ break;
+ case ZSTART_PHASE_9:
+ jam();
+ ph8ALab(signal);
+ return;
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+}//Ndbcntr::execNDB_STTORRY()
+
+void Ndbcntr::startPhase1Lab(Signal* signal)
+{
+ jamEntry();
+
+ initData(signal);
+
+ cdynamicNodeId = 0;
+
+ NdbBlocksRecPtr ndbBlocksPtr;
+ ndbBlocksPtr.i = 0;
+ ptrAss(ndbBlocksPtr, ndbBlocksRec);
+ ndbBlocksPtr.p->blockref = DBLQH_REF;
+ ndbBlocksPtr.i = 1;
+ ptrAss(ndbBlocksPtr, ndbBlocksRec);
+ ndbBlocksPtr.p->blockref = DBDICT_REF;
+ ndbBlocksPtr.i = 2;
+ ptrAss(ndbBlocksPtr, ndbBlocksRec);
+ ndbBlocksPtr.p->blockref = DBTUP_REF;
+ ndbBlocksPtr.i = 3;
+ ptrAss(ndbBlocksPtr, ndbBlocksRec);
+ ndbBlocksPtr.p->blockref = DBACC_REF;
+ ndbBlocksPtr.i = 4;
+ ptrAss(ndbBlocksPtr, ndbBlocksRec);
+ ndbBlocksPtr.p->blockref = DBTC_REF;
+ ndbBlocksPtr.i = 5;
+ ptrAss(ndbBlocksPtr, ndbBlocksRec);
+ ndbBlocksPtr.p->blockref = DBDIH_REF;
+ sendSttorry(signal);
+ return;
+}
+
+void Ndbcntr::execREAD_NODESREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Ndbcntr::execREAD_NODESREF()
+
+
+/*******************************/
+/* NDB_STARTREF */
+/*******************************/
+void Ndbcntr::execNDB_STARTREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Ndbcntr::execNDB_STARTREF()
+
+/*******************************/
+/* STTOR */
+/*******************************/
+void Ndbcntr::startPhase2Lab(Signal* signal)
+{
+ c_start.m_lastGci = 0;
+ c_start.m_lastGciNodeId = getOwnNodeId();
+
+ signal->theData[0] = reference();
+ sendSignal(DBDIH_REF, GSN_DIH_RESTARTREQ, signal, 1, JBB);
+ return;
+}//Ndbcntr::startPhase2Lab()
+
+/*******************************/
+/* DIH_RESTARTCONF */
+/*******************************/
+void Ndbcntr::execDIH_RESTARTCONF(Signal* signal)
+{
+ jamEntry();
+ //cmasterDihId = signal->theData[0];
+ c_start.m_lastGci = signal->theData[1];
+ ctypeOfStart = NodeState::ST_SYSTEM_RESTART;
+ ph2ALab(signal);
+ return;
+}//Ndbcntr::execDIH_RESTARTCONF()
+
+/*******************************/
+/* DIH_RESTARTREF */
+/*******************************/
+void Ndbcntr::execDIH_RESTARTREF(Signal* signal)
+{
+ jamEntry();
+ ctypeOfStart = NodeState::ST_INITIAL_START;
+ ph2ALab(signal);
+ return;
+}//Ndbcntr::execDIH_RESTARTREF()
+
+void Ndbcntr::ph2ALab(Signal* signal)
+{
+ /******************************/
+ /* request configured nodes */
+ /* from QMGR */
+ /* READ_NODESREQ */
+ /******************************/
+ signal->theData[0] = reference();
+ sendSignal(QMGR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+ return;
+}//Ndbcntr::ph2ALab()
+
+inline
+Uint64
+setTimeout(Uint64 time, Uint32 timeoutValue){
+ if(timeoutValue == 0)
+ return ~(Uint64)0;
+ return time + timeoutValue;
+}
+
+/*******************************/
+/* READ_NODESCONF */
+/*******************************/
+void Ndbcntr::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+ const ReadNodesConf * readNodes = (ReadNodesConf *)&signal->theData[0];
+
+ cmasterNodeId = readNodes->masterNodeId;
+ cdynamicNodeId = readNodes->ndynamicId;
+
+ /**
+ * All defined nodes...
+ */
+ c_allDefinedNodes.assign(NdbNodeBitmask::Size, readNodes->allNodes);
+ c_clusterNodes.assign(NdbNodeBitmask::Size, readNodes->clusterNodes);
+
+ Uint32 to_1 = 30000;
+ Uint32 to_2 = 0;
+ Uint32 to_3 = 0;
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+
+ ndbrequire(p != 0);
+ ndb_mgm_get_int_parameter(p, CFG_DB_START_PARTIAL_TIMEOUT, &to_1);
+ ndb_mgm_get_int_parameter(p, CFG_DB_START_PARTITION_TIMEOUT, &to_2);
+ ndb_mgm_get_int_parameter(p, CFG_DB_START_FAILURE_TIMEOUT, &to_3);
+
+ c_start.m_startTime = NdbTick_CurrentMillisecond();
+ c_start.m_startPartialTimeout = setTimeout(c_start.m_startTime, to_1);
+ c_start.m_startPartitionedTimeout = setTimeout(c_start.m_startTime, to_2);
+ c_start.m_startFailureTimeout = setTimeout(c_start.m_startTime, to_3);
+
+ UpgradeStartup::sendCmAppChg(* this, signal, 0); // ADD
+
+ sendCntrStartReq(signal);
+
+ signal->theData[0] = ZSTARTUP;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 1000, 1);
+
+ return;
+}
+
+void
+Ndbcntr::execCM_ADD_REP(Signal* signal){
+ jamEntry();
+ c_clusterNodes.set(signal->theData[0]);
+}
+
+void
+Ndbcntr::sendCntrStartReq(Signal * signal){
+ jamEntry();
+
+ CntrStartReq * req = (CntrStartReq*)signal->getDataPtrSend();
+ req->startType = ctypeOfStart;
+ req->lastGci = c_start.m_lastGci;
+ req->nodeId = getOwnNodeId();
+ sendSignal(calcNdbCntrBlockRef(cmasterNodeId), GSN_CNTR_START_REQ,
+ signal, CntrStartReq::SignalLength, JBB);
+}
+
+void
+Ndbcntr::execCNTR_START_REF(Signal * signal){
+ jamEntry();
+ const CntrStartRef * ref = (CntrStartRef*)signal->getDataPtr();
+
+ switch(ref->errorCode){
+ case CntrStartRef::NotMaster:
+ jam();
+ cmasterNodeId = ref->masterNodeId;
+ sendCntrStartReq(signal);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Ndbcntr::StartRecord::reset(){
+ m_starting.clear();
+ m_waiting.clear();
+ m_withLog.clear();
+ m_withoutLog.clear();
+ m_lastGci = m_lastGciNodeId = 0;
+ m_startPartialTimeout = ~0;
+ m_startPartitionedTimeout = ~0;
+ m_startFailureTimeout = ~0;
+
+ m_logNodesCount = 0;
+}
+
+void
+Ndbcntr::execCNTR_START_CONF(Signal * signal){
+ jamEntry();
+ const CntrStartConf * conf = (CntrStartConf*)signal->getDataPtr();
+
+ cnoStartNodes = conf->noStartNodes;
+ ctypeOfStart = (NodeState::StartType)conf->startType;
+ c_start.m_lastGci = conf->startGci;
+ cmasterNodeId = conf->masterNodeId;
+ NdbNodeBitmask tmp;
+ tmp.assign(NdbNodeBitmask::Size, conf->startedNodes);
+ c_startedNodes.bitOR(tmp);
+ c_start.m_starting.assign(NdbNodeBitmask::Size, conf->startingNodes);
+ ph2GLab(signal);
+
+ UpgradeStartup::sendCmAppChg(* this, signal, 2); //START
+}
+
+/**
+ * Tried with parallell nr, but it crashed in DIH
+ * so I turned it off, as I don't want to debug DIH now...
+ * Jonas 19/11-03
+ *
+ * After trying for 2 hours, I gave up.
+ * DIH is not designed to support it, and
+ * it requires quite of lot of changes to
+ * make it work
+ * Jonas 5/12-03
+ */
+#define PARALLELL_NR 0
+
+#if PARALLELL_NR
+const bool parallellNR = true;
+#else
+const bool parallellNR = false;
+#endif
+
+void
+Ndbcntr::execCNTR_START_REP(Signal* signal){
+ jamEntry();
+ Uint32 nodeId = signal->theData[0];
+ c_startedNodes.set(nodeId);
+ c_start.m_starting.clear(nodeId);
+
+ if(!c_start.m_starting.isclear()){
+ jam();
+ return;
+ }
+
+ if(cmasterNodeId != getOwnNodeId()){
+ jam();
+ c_start.reset();
+ return;
+ }
+
+ if(c_start.m_waiting.isclear()){
+ jam();
+ c_start.reset();
+ return;
+ }
+
+ startWaitingNodes(signal);
+}
+
+void
+Ndbcntr::execCNTR_START_REQ(Signal * signal){
+ jamEntry();
+ const CntrStartReq * req = (CntrStartReq*)signal->getDataPtr();
+
+ const Uint32 nodeId = req->nodeId;
+ const Uint32 lastGci = req->lastGci;
+ const NodeState::StartType st = (NodeState::StartType)req->startType;
+
+ if(cmasterNodeId == 0){
+ jam();
+ // Has not completed READNODES yet
+ sendSignalWithDelay(reference(), GSN_CNTR_START_REQ, signal, 100,
+ signal->getLength());
+ return;
+ }
+
+ if(cmasterNodeId != getOwnNodeId()){
+ jam();
+ sendCntrStartRef(signal, nodeId, CntrStartRef::NotMaster);
+ return;
+ }
+
+ const NodeState & nodeState = getNodeState();
+ switch(nodeState.startLevel){
+ case NodeState::SL_NOTHING:
+ case NodeState::SL_CMVMI:
+ jam();
+ ndbrequire(false);
+ case NodeState::SL_STARTING:
+ case NodeState::SL_STARTED:
+ jam();
+ break;
+
+ case NodeState::SL_STOPPING_1:
+ case NodeState::SL_STOPPING_2:
+ case NodeState::SL_STOPPING_3:
+ case NodeState::SL_STOPPING_4:
+ jam();
+ sendCntrStartRef(signal, nodeId, CntrStartRef::StopInProgress);
+ return;
+ }
+
+ /**
+ * Am I starting (or started)
+ */
+ const bool starting = (nodeState.startLevel != NodeState::SL_STARTED);
+
+ c_start.m_waiting.set(nodeId);
+ switch(st){
+ case NodeState::ST_INITIAL_START:
+ jam();
+ c_start.m_withoutLog.set(nodeId);
+ break;
+ case NodeState::ST_SYSTEM_RESTART:
+ jam();
+ c_start.m_withLog.set(nodeId);
+ if(starting && lastGci > c_start.m_lastGci){
+ jam();
+ CntrStartRef * ref = (CntrStartRef*)signal->getDataPtrSend();
+ ref->errorCode = CntrStartRef::NotMaster;
+ ref->masterNodeId = nodeId;
+ NodeReceiverGroup rg (NDBCNTR, c_start.m_waiting);
+ sendSignal(rg, GSN_CNTR_START_REF, signal,
+ CntrStartRef::SignalLength, JBB);
+ return;
+ }
+ if(starting){
+ jam();
+ Uint32 i = c_start.m_logNodesCount++;
+ c_start.m_logNodes[i].m_nodeId = nodeId;
+ c_start.m_logNodes[i].m_lastGci = req->lastGci;
+ }
+ break;
+ case NodeState::ST_NODE_RESTART:
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ case NodeState::ST_ILLEGAL_TYPE:
+ ndbrequire(false);
+ }
+
+ const bool startInProgress = !c_start.m_starting.isclear();
+
+ if((starting && startInProgress) || (startInProgress && !parallellNR)){
+ jam();
+ // We're already starting together with a bunch of nodes
+ // Let this node wait...
+ return;
+ }
+
+ if(starting){
+ jam();
+ trySystemRestart(signal);
+ } else {
+ jam();
+ startWaitingNodes(signal);
+ }
+ return;
+}
+
+void
+Ndbcntr::startWaitingNodes(Signal * signal){
+
+#if ! PARALLELL_NR
+ const Uint32 nodeId = c_start.m_waiting.find(0);
+ const Uint32 Tref = calcNdbCntrBlockRef(nodeId);
+ ndbrequire(nodeId != c_start.m_waiting.NotFound);
+
+ NodeState::StartType nrType = NodeState::ST_NODE_RESTART;
+ if(c_start.m_withoutLog.get(nodeId)){
+ jam();
+ nrType = NodeState::ST_INITIAL_NODE_RESTART;
+ }
+
+ /**
+ * Let node perform restart
+ */
+ CntrStartConf * conf = (CntrStartConf*)signal->getDataPtrSend();
+ conf->noStartNodes = 1;
+ conf->startType = nrType;
+ conf->startGci = ~0; // Not used
+ conf->masterNodeId = getOwnNodeId();
+ BitmaskImpl::clear(NdbNodeBitmask::Size, conf->startingNodes);
+ BitmaskImpl::set(NdbNodeBitmask::Size, conf->startingNodes, nodeId);
+ c_startedNodes.copyto(NdbNodeBitmask::Size, conf->startedNodes);
+ sendSignal(Tref, GSN_CNTR_START_CONF, signal,
+ CntrStartConf::SignalLength, JBB);
+
+ c_start.m_waiting.clear(nodeId);
+ c_start.m_withLog.clear(nodeId);
+ c_start.m_withoutLog.clear(nodeId);
+ c_start.m_starting.set(nodeId);
+#else
+ // Parallell nr
+
+ c_start.m_starting = c_start.m_waiting;
+ c_start.m_waiting.clear();
+
+ CntrStartConf * conf = (CntrStartConf*)signal->getDataPtrSend();
+ conf->noStartNodes = 1;
+ conf->startGci = ~0; // Not used
+ conf->masterNodeId = getOwnNodeId();
+ c_start.m_starting.copyto(NdbNodeBitmask::Size, conf->startingNodes);
+ c_startedNodes.copyto(NdbNodeBitmask::Size, conf->startedNodes);
+
+ char buf[100];
+ if(!c_start.m_withLog.isclear()){
+ jam();
+ ndbout_c("Starting nodes w/ log: %s", c_start.m_withLog.getText(buf));
+
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_withLog);
+ conf->startType = NodeState::ST_NODE_RESTART;
+
+ sendSignal(rg, GSN_CNTR_START_CONF, signal,
+ CntrStartConf::SignalLength, JBB);
+ }
+
+ if(!c_start.m_withoutLog.isclear()){
+ jam();
+ ndbout_c("Starting nodes wo/ log: %s", c_start.m_withoutLog.getText(buf));
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_withoutLog);
+ conf->startType = NodeState::ST_INITIAL_NODE_RESTART;
+
+ sendSignal(rg, GSN_CNTR_START_CONF, signal,
+ CntrStartConf::SignalLength, JBB);
+ }
+
+ c_start.m_waiting.clear();
+ c_start.m_withLog.clear();
+ c_start.m_withoutLog.clear();
+#endif
+}
+
+void
+Ndbcntr::sendCntrStartRef(Signal * signal,
+ Uint32 nodeId, CntrStartRef::ErrorCode code){
+ CntrStartRef * ref = (CntrStartRef*)signal->getDataPtrSend();
+ ref->errorCode = code;
+ ref->masterNodeId = cmasterNodeId;
+ sendSignal(calcNdbCntrBlockRef(nodeId), GSN_CNTR_START_REF, signal,
+ CntrStartRef::SignalLength, JBB);
+}
+
+CheckNodeGroups::Output
+Ndbcntr::checkNodeGroups(Signal* signal, const NdbNodeBitmask & mask){
+ CheckNodeGroups* sd = (CheckNodeGroups*)&signal->theData[0];
+ sd->blockRef = reference();
+ sd->requestType = CheckNodeGroups::Direct | CheckNodeGroups::ArbitCheck;
+ sd->mask = mask;
+ EXECUTE_DIRECT(DBDIH, GSN_CHECKNODEGROUPSREQ, signal,
+ CheckNodeGroups::SignalLength);
+ jamEntry();
+ return (CheckNodeGroups::Output)sd->output;
+}
+
+bool
+Ndbcntr::trySystemRestart(Signal* signal){
+ /**
+ * System restart something
+ */
+ const bool allNodes = c_start.m_waiting.equal(c_allDefinedNodes);
+ const bool allClusterNodes = c_start.m_waiting.equal(c_clusterNodes);
+ const Uint64 now = NdbTick_CurrentMillisecond();
+
+ if(!allClusterNodes){
+ jam();
+ return false;
+ }
+
+ if(!allNodes && c_start.m_startPartialTimeout > now){
+ jam();
+ return false;
+ }
+
+ NodeState::StartType srType = NodeState::ST_SYSTEM_RESTART;
+ if(c_start.m_waiting.equal(c_start.m_withoutLog)){
+ if(!allNodes){
+ jam();
+ return false;
+ }
+ jam();
+ srType = NodeState::ST_INITIAL_START;
+ c_start.m_starting = c_start.m_withoutLog; // Used for starting...
+ c_start.m_withoutLog.clear();
+ } else {
+
+ CheckNodeGroups::Output wLog = checkNodeGroups(signal, c_start.m_withLog);
+
+ switch (wLog) {
+ case CheckNodeGroups::Win:
+ jam();
+ break;
+ case CheckNodeGroups::Lose:
+ jam();
+ // If we lose with all nodes, then we're in trouble
+ ndbrequire(!allNodes);
+ return false;
+ case CheckNodeGroups::Partitioning:
+ jam();
+ bool allowPartition = (c_start.m_startPartitionedTimeout != (Uint64)~0);
+
+ if(allNodes){
+ if(allowPartition){
+ jam();
+ break;
+ }
+ ndbrequire(false); // All nodes -> partitioning, which is not allowed
+ }
+
+ if(c_start.m_startPartitionedTimeout > now){
+ jam();
+ return false;
+ }
+ break;
+ }
+
+ // For now only with the "logged"-ones.
+ // Let the others do node restart afterwards...
+ c_start.m_starting = c_start.m_withLog;
+ c_start.m_withLog.clear();
+ }
+
+ /**
+ * Okidoki, we try to start
+ */
+ CntrStartConf * conf = (CntrStartConf*)signal->getDataPtr();
+ conf->noStartNodes = c_start.m_starting.count();
+ conf->startType = srType;
+ conf->startGci = c_start.m_lastGci;
+ conf->masterNodeId = c_start.m_lastGciNodeId;
+ c_start.m_starting.copyto(NdbNodeBitmask::Size, conf->startingNodes);
+ c_startedNodes.copyto(NdbNodeBitmask::Size, conf->startedNodes);
+
+ ndbrequire(c_start.m_lastGciNodeId == getOwnNodeId());
+
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_starting);
+ sendSignal(rg, GSN_CNTR_START_CONF, signal, CntrStartConf::SignalLength,JBB);
+
+ c_start.m_waiting.bitANDC(c_start.m_starting);
+
+ return true;
+}
+
+void Ndbcntr::ph2GLab(Signal* signal)
+{
+ if (cndbBlocksCount < ZNO_NDB_BLOCKS) {
+ jam();
+ sendNdbSttor(signal);
+ return;
+ }//if
+ sendSttorry(signal);
+ return;
+}//Ndbcntr::ph2GLab()
+
+/*
+4.4 START PHASE 3 */
+/*###########################################################################*/
+// SEND SIGNAL NDBSTTOR TO ALL BLOCKS, ACC, DICT, DIH, LQH, TC AND TUP
+// WHEN ALL BLOCKS HAVE RETURNED THEIR NDB_STTORRY ALL BLOCK HAVE FINISHED
+// THEIR LOCAL CONNECTIONs SUCESSFULLY
+// AND THEN WE CAN SEND APPL_STARTREG TO INFORM QMGR THAT WE ARE READY TO
+// SET UP DISTRIBUTED CONNECTIONS.
+/*--------------------------------------------------------------*/
+// THIS IS NDB START PHASE 3.
+/*--------------------------------------------------------------*/
+/*******************************/
+/* STTOR */
+/*******************************/
+void Ndbcntr::startPhase3Lab(Signal* signal)
+{
+ ph3ALab(signal);
+ return;
+}//Ndbcntr::startPhase3Lab()
+
+/*******************************/
+/* NDB_STTORRY */
+/*******************************/
+void Ndbcntr::ph3ALab(Signal* signal)
+{
+ if (cndbBlocksCount < ZNO_NDB_BLOCKS) {
+ jam();
+ sendNdbSttor(signal);
+ return;
+ }//if
+
+ sendSttorry(signal);
+ return;
+}//Ndbcntr::ph3ALab()
+
+/*
+4.5 START PHASE 4 */
+/*###########################################################################*/
+// WAIT FOR ALL NODES IN CLUSTER TO CHANGE STATE INTO ZSTART ,
+// APPL_CHANGEREP IS ALWAYS SENT WHEN SOMEONE HAVE
+// CHANGED THEIR STATE. APPL_STARTCONF INDICATES THAT ALL NODES ARE IN START
+// STATE SEND NDB_STARTREQ TO DIH AND THEN WAIT FOR NDB_STARTCONF
+/*---------------------------------------------------------------------------*/
+/*******************************/
+/* STTOR */
+/*******************************/
+void Ndbcntr::startPhase4Lab(Signal* signal)
+{
+ ph4ALab(signal);
+}//Ndbcntr::startPhase4Lab()
+
+
+void Ndbcntr::ph4ALab(Signal* signal)
+{
+ ph4BLab(signal);
+ return;
+}//Ndbcntr::ph4ALab()
+
+/*******************************/
+/* NDB_STTORRY */
+/*******************************/
+void Ndbcntr::ph4BLab(Signal* signal)
+{
+/*--------------------------------------*/
+/* CASE: CSTART_PHASE = ZSTART_PHASE_4 */
+/*--------------------------------------*/
+ if (cndbBlocksCount < ZNO_NDB_BLOCKS) {
+ jam();
+ sendNdbSttor(signal);
+ return;
+ }//if
+ if ((ctypeOfStart == NodeState::ST_NODE_RESTART) ||
+ (ctypeOfStart == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ sendSttorry(signal);
+ return;
+ }//if
+ waitpoint41Lab(signal);
+ return;
+}//Ndbcntr::ph4BLab()
+
+void Ndbcntr::waitpoint41Lab(Signal* signal)
+{
+ if (getOwnNodeId() == cmasterNodeId) {
+ jam();
+/*--------------------------------------*/
+/* MASTER WAITS UNTIL ALL SLAVES HAS */
+/* SENT THE REPORTS */
+/*--------------------------------------*/
+ cnoWaitrep++;
+ if (cnoWaitrep == cnoStartNodes) {
+ jam();
+ cnoWaitrep = 0;
+/*---------------------------------------------------------------------------*/
+// NDB_STARTREQ STARTS UP ALL SET UP OF DISTRIBUTION INFORMATION IN DIH AND
+// DICT. AFTER SETTING UP THIS
+// DATA IT USES THAT DATA TO SET UP WHICH FRAGMENTS THAT ARE TO START AND
+// WHERE THEY ARE TO START. THEN
+// IT SETS UP THE FRAGMENTS AND RECOVERS THEM BY:
+// 1) READING A LOCAL CHECKPOINT FROM DISK.
+// 2) EXECUTING THE UNDO LOG ON INDEX AND DATA.
+// 3) EXECUTING THE FRAGMENT REDO LOG FROM ONE OR SEVERAL NODES TO
+// RESTORE THE RESTART CONFIGURATION OF DATA IN NDB CLUSTER.
+/*---------------------------------------------------------------------------*/
+ signal->theData[0] = reference();
+ signal->theData[1] = ctypeOfStart;
+ sendSignal(DBDIH_REF, GSN_NDB_STARTREQ, signal, 2, JBB);
+ }//if
+ } else {
+ jam();
+/*--------------------------------------*/
+/* SLAVE NODES WILL PASS HERE ONCE AND */
+/* SEND A WAITPOINT REPORT TO MASTER. */
+/* SLAVES WONT DO ANYTHING UNTIL THEY */
+/* RECEIVE A WAIT REPORT FROM THE MASTER*/
+/*--------------------------------------*/
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_4_1;
+ sendSignal(calcNdbCntrBlockRef(cmasterNodeId),
+ GSN_CNTR_WAITREP, signal, 2, JBB);
+ }//if
+ return;
+}//Ndbcntr::waitpoint41Lab()
+
+/*******************************/
+/* NDB_STARTCONF */
+/*******************************/
+void Ndbcntr::execNDB_STARTCONF(Signal* signal)
+{
+ jamEntry();
+
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_starting);
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_4_2;
+ sendSignal(rg, GSN_CNTR_WAITREP, signal, 2, JBB);
+ return;
+}//Ndbcntr::execNDB_STARTCONF()
+
+/*
+4.6 START PHASE 5 */
+/*###########################################################################*/
+// SEND APPL_RUN TO THE QMGR IN THIS BLOCK
+// SEND NDB_STTOR ALL BLOCKS ACC, DICT, DIH, LQH, TC AND TUP THEN WAIT FOR
+// THEIR NDB_STTORRY
+/*---------------------------------------------------------------------------*/
+/*******************************/
+/* STTOR */
+/*******************************/
+void Ndbcntr::startPhase5Lab(Signal* signal)
+{
+ ph5ALab(signal);
+ return;
+}//Ndbcntr::startPhase5Lab()
+
+/*******************************/
+/* NDB_STTORRY */
+/*******************************/
+/*---------------------------------------------------------------------------*/
+// THIS IS NDB START PHASE 5.
+/*---------------------------------------------------------------------------*/
+// IN THIS START PHASE TUP INITIALISES DISK FILES FOR DISK STORAGE IF INITIAL
+// START. DIH WILL START UP
+// THE GLOBAL CHECKPOINT PROTOCOL AND WILL CONCLUDE ANY UNFINISHED TAKE OVERS
+// THAT STARTED BEFORE THE SYSTEM CRASH.
+/*---------------------------------------------------------------------------*/
+void Ndbcntr::ph5ALab(Signal* signal)
+{
+ if (cndbBlocksCount < ZNO_NDB_BLOCKS) {
+ jam();
+ sendNdbSttor(signal);
+ return;
+ }//if
+
+ cstartPhase = cstartPhase + 1;
+ cinternalStartphase = cstartPhase - 1;
+ if (getOwnNodeId() == cmasterNodeId) {
+ switch(ctypeOfStart){
+ case NodeState::ST_INITIAL_START:
+ jam();
+ /*--------------------------------------*/
+ /* MASTER CNTR IS RESPONSIBLE FOR */
+ /* CREATING SYSTEM TABLES */
+ /*--------------------------------------*/
+ createSystableLab(signal, 0);
+ return;
+ case NodeState::ST_SYSTEM_RESTART:
+ jam();
+ waitpoint52Lab(signal);
+ return;
+ case NodeState::ST_NODE_RESTART:
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ jam();
+ break;
+ case NodeState::ST_ILLEGAL_TYPE:
+ jam();
+ break;
+ }
+ ndbrequire(false);
+ }
+
+ /**
+ * Not master
+ */
+ NdbSttor * const req = (NdbSttor*)signal->getDataPtrSend();
+ switch(ctypeOfStart){
+ case NodeState::ST_NODE_RESTART:
+ case NodeState::ST_INITIAL_NODE_RESTART:
+ jam();
+ /*----------------------------------------------------------------------*/
+ // SEND NDB START PHASE 5 IN NODE RESTARTS TO COPY DATA TO THE NEWLY
+ // STARTED NODE.
+ /*----------------------------------------------------------------------*/
+ req->senderRef = reference();
+ req->nodeId = getOwnNodeId();
+ req->internalStartPhase = cinternalStartphase;
+ req->typeOfStart = ctypeOfStart;
+ req->masterNodeId = cmasterNodeId;
+
+ //#define TRACE_STTOR
+#ifdef TRACE_STTOR
+ ndbout_c("sending NDB_STTOR(%d) to DIH", cinternalStartphase);
+#endif
+ sendSignal(DBDIH_REF, GSN_NDB_STTOR, signal,
+ NdbSttor::SignalLength, JBB);
+ return;
+ case NodeState::ST_INITIAL_START:
+ case NodeState::ST_SYSTEM_RESTART:
+ jam();
+ /*--------------------------------------*/
+ /* DURING SYSTEMRESTART AND INITALSTART:*/
+ /* SLAVE NODES WILL PASS HERE ONCE AND */
+ /* SEND A WAITPOINT REPORT TO MASTER. */
+ /* SLAVES WONT DO ANYTHING UNTIL THEY */
+ /* RECEIVE A WAIT REPORT FROM THE MASTER*/
+ /* WHEN THE MASTER HAS FINISHED HIS WORK*/
+ /*--------------------------------------*/
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_5_2;
+ sendSignal(calcNdbCntrBlockRef(cmasterNodeId),
+ GSN_CNTR_WAITREP, signal, 2, JBB);
+ return;
+ default:
+ ndbrequire(false);
+ }
+}//Ndbcntr::ph5ALab()
+
+void Ndbcntr::waitpoint52Lab(Signal* signal)
+{
+ cnoWaitrep = cnoWaitrep + 1;
+/*---------------------------------------------------------------------------*/
+// THIS WAITING POINT IS ONLY USED BY A MASTER NODE. WE WILL EXECUTE NDB START
+// PHASE 5 FOR DIH IN THE
+// MASTER. THIS WILL START UP LOCAL CHECKPOINTS AND WILL ALSO CONCLUDE ANY
+// UNFINISHED LOCAL CHECKPOINTS
+// BEFORE THE SYSTEM CRASH. THIS WILL ENSURE THAT WE ALWAYS RESTART FROM A
+// WELL KNOWN STATE.
+/*---------------------------------------------------------------------------*/
+/*--------------------------------------*/
+/* MASTER WAITS UNTIL HE RECEIVED WAIT */
+/* REPORTS FROM ALL SLAVE CNTR */
+/*--------------------------------------*/
+ if (cnoWaitrep == cnoStartNodes) {
+ jam();
+ cnoWaitrep = 0;
+
+ NdbSttor * const req = (NdbSttor*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->nodeId = getOwnNodeId();
+ req->internalStartPhase = cinternalStartphase;
+ req->typeOfStart = ctypeOfStart;
+ req->masterNodeId = cmasterNodeId;
+#ifdef TRACE_STTOR
+ ndbout_c("sending NDB_STTOR(%d) to DIH", cinternalStartphase);
+#endif
+ sendSignal(DBDIH_REF, GSN_NDB_STTOR, signal,
+ NdbSttor::SignalLength, JBB);
+ }//if
+ return;
+}//Ndbcntr::waitpoint52Lab()
+
+/*******************************/
+/* NDB_STTORRY */
+/*******************************/
+void Ndbcntr::ph6ALab(Signal* signal)
+{
+ if ((ctypeOfStart == NodeState::ST_NODE_RESTART) ||
+ (ctypeOfStart == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ waitpoint51Lab(signal);
+ return;
+ }//if
+
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_starting);
+ rg.m_nodes.clear(getOwnNodeId());
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_5_1;
+ sendSignal(rg, GSN_CNTR_WAITREP, signal, 2, JBB);
+
+ waitpoint51Lab(signal);
+ return;
+}//Ndbcntr::ph6ALab()
+
+void Ndbcntr::waitpoint51Lab(Signal* signal)
+{
+ cstartPhase = cstartPhase + 1;
+/*---------------------------------------------------------------------------*/
+// A FINAL STEP IS NOW TO SEND NDB_STTOR TO TC. THIS MAKES IT POSSIBLE TO
+// CONNECT TO TC FOR APPLICATIONS.
+// THIS IS NDB START PHASE 6 WHICH IS FOR ALL BLOCKS IN ALL NODES.
+/*---------------------------------------------------------------------------*/
+ cinternalStartphase = cstartPhase - 1;
+ cndbBlocksCount = 0;
+ ph6BLab(signal);
+ return;
+}//Ndbcntr::waitpoint51Lab()
+
+void Ndbcntr::ph6BLab(Signal* signal)
+{
+ // c_missra.currentStartPhase - cstartPhase - cinternalStartphase =
+ // 5 - 7 - 6
+ if (cndbBlocksCount < ZNO_NDB_BLOCKS) {
+ jam();
+ sendNdbSttor(signal);
+ return;
+ }//if
+ if ((ctypeOfStart == NodeState::ST_NODE_RESTART) ||
+ (ctypeOfStart == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ sendSttorry(signal);
+ return;
+ }
+ waitpoint61Lab(signal);
+}
+
+void Ndbcntr::waitpoint61Lab(Signal* signal)
+{
+ if (getOwnNodeId() == cmasterNodeId) {
+ jam();
+ cnoWaitrep6++;
+ if (cnoWaitrep6 == cnoStartNodes) {
+ jam();
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_starting);
+ rg.m_nodes.clear(getOwnNodeId());
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_6_2;
+ sendSignal(rg, GSN_CNTR_WAITREP, signal, 2, JBB);
+ sendSttorry(signal);
+ }
+ } else {
+ jam();
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_6_1;
+ sendSignal(calcNdbCntrBlockRef(cmasterNodeId), GSN_CNTR_WAITREP, signal, 2, JBB);
+ }
+}
+
+// Start phase 8 (internal 7)
+void Ndbcntr::startPhase8Lab(Signal* signal)
+{
+ cinternalStartphase = cstartPhase - 1;
+ cndbBlocksCount = 0;
+ ph7ALab(signal);
+}
+
+void Ndbcntr::ph7ALab(Signal* signal)
+{
+ while (cndbBlocksCount < ZNO_NDB_BLOCKS) {
+ jam();
+ sendNdbSttor(signal);
+ return;
+ }
+ if ((ctypeOfStart == NodeState::ST_NODE_RESTART) ||
+ (ctypeOfStart == NodeState::ST_INITIAL_NODE_RESTART)) {
+ jam();
+ sendSttorry(signal);
+ return;
+ }
+ waitpoint71Lab(signal);
+}
+
+void Ndbcntr::waitpoint71Lab(Signal* signal)
+{
+ if (getOwnNodeId() == cmasterNodeId) {
+ jam();
+ cnoWaitrep7++;
+ if (cnoWaitrep7 == cnoStartNodes) {
+ jam();
+ NodeReceiverGroup rg(NDBCNTR, c_start.m_starting);
+ rg.m_nodes.clear(getOwnNodeId());
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_7_2;
+ sendSignal(rg, GSN_CNTR_WAITREP, signal, 2, JBB);
+ sendSttorry(signal);
+ }
+ } else {
+ jam();
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ZWAITPOINT_7_1;
+ sendSignal(calcNdbCntrBlockRef(cmasterNodeId), GSN_CNTR_WAITREP, signal, 2, JBB);
+ }
+}
+
+// Start phase 9 (internal 8)
+void Ndbcntr::startPhase9Lab(Signal* signal)
+{
+ cinternalStartphase = cstartPhase - 1;
+ cndbBlocksCount = 0;
+ ph8ALab(signal);
+}
+
+void Ndbcntr::ph8ALab(Signal* signal)
+{
+/*---------------------------------------------------------------------------*/
+// NODES WHICH PERFORM A NODE RESTART NEEDS TO GET THE DYNAMIC ID'S
+// OF THE OTHER NODES HERE.
+/*---------------------------------------------------------------------------*/
+ sendSttorry(signal);
+ resetStartVariables(signal);
+ return;
+}//Ndbcntr::ph8BLab()
+
+/*******************************/
+/* CNTR_WAITREP */
+/*******************************/
+void Ndbcntr::execCNTR_WAITREP(Signal* signal)
+{
+ Uint16 twaitPoint;
+
+ jamEntry();
+ twaitPoint = signal->theData[1];
+ switch (twaitPoint) {
+ case ZWAITPOINT_4_1:
+ jam();
+ waitpoint41Lab(signal);
+ break;
+ case ZWAITPOINT_4_2:
+ jam();
+ sendSttorry(signal);
+ break;
+ case ZWAITPOINT_5_1:
+ jam();
+ waitpoint51Lab(signal);
+ break;
+ case ZWAITPOINT_5_2:
+ jam();
+ waitpoint52Lab(signal);
+ break;
+ case ZWAITPOINT_6_1:
+ jam();
+ waitpoint61Lab(signal);
+ break;
+ case ZWAITPOINT_6_2:
+ jam();
+ sendSttorry(signal);
+ break;
+ case ZWAITPOINT_7_1:
+ jam();
+ waitpoint71Lab(signal);
+ break;
+ case ZWAITPOINT_7_2:
+ jam();
+ sendSttorry(signal);
+ break;
+ default:
+ jam();
+ systemErrorLab(signal);
+ break;
+ }//switch
+}//Ndbcntr::execCNTR_WAITREP()
+
+/*******************************/
+/* NODE_FAILREP */
+/*******************************/
+void Ndbcntr::execNODE_FAILREP(Signal* signal)
+{
+ jamEntry();
+
+ const NodeFailRep * nodeFail = (NodeFailRep *)&signal->theData[0];
+ NdbNodeBitmask allFailed;
+ allFailed.assign(NdbNodeBitmask::Size, nodeFail->theNodes);
+
+ NdbNodeBitmask failedStarted = c_startedNodes;
+ NdbNodeBitmask failedStarting = c_start.m_starting;
+ NdbNodeBitmask failedWaiting = c_start.m_waiting;
+
+ failedStarted.bitAND(allFailed);
+ failedStarting.bitAND(allFailed);
+ failedWaiting.bitAND(allFailed);
+
+ const bool tMasterFailed = allFailed.get(cmasterNodeId);
+ const bool tStarted = !failedStarted.isclear();
+ const bool tStarting = !failedStarting.isclear();
+ const bool tWaiting = !failedWaiting.isclear();
+
+ if(tMasterFailed){
+ jam();
+ /**
+ * If master has failed choose qmgr president as master
+ */
+ cmasterNodeId = nodeFail->masterNodeId;
+ }
+
+ /**
+ * Clear node bitmasks from failed nodes
+ */
+ c_start.m_starting.bitANDC(allFailed);
+ c_start.m_waiting.bitANDC(allFailed);
+ c_start.m_withLog.bitANDC(allFailed);
+ c_start.m_withoutLog.bitANDC(allFailed);
+ c_clusterNodes.bitANDC(allFailed);
+ c_startedNodes.bitANDC(allFailed);
+
+ const NodeState & st = getNodeState();
+ if(st.startLevel == st.SL_STARTING){
+ jam();
+
+ const Uint32 phase = st.starting.startPhase;
+
+ const bool tStartConf = (phase > 2) || (phase == 2 && cndbBlocksCount > 0);
+
+ if(tMasterFailed){
+ progError(__LINE__,
+ ERR_SR_OTHERNODEFAILED,
+ "Unhandled node failure during restart");
+ }
+
+ if(tStartConf && tStarting){
+ // One of other starting nodes has crashed...
+ progError(__LINE__,
+ ERR_SR_OTHERNODEFAILED,
+ "Unhandled node failure of starting node during restart");
+ }
+
+ if(tStartConf && tStarted){
+ // One of other started nodes has crashed...
+ progError(__LINE__,
+ ERR_SR_OTHERNODEFAILED,
+ "Unhandled node failure of started node during restart");
+ }
+
+ Uint32 nodeId = 0;
+ while(!allFailed.isclear()){
+ nodeId = allFailed.find(nodeId + 1);
+ allFailed.clear(nodeId);
+ signal->theData[0] = nodeId;
+ sendSignal(QMGR_REF, GSN_NDB_FAILCONF, signal, 1, JBB);
+ }//for
+
+ return;
+ }
+
+ ndbrequire(!allFailed.get(getOwnNodeId()));
+
+ NodeFailRep * rep = (NodeFailRep *)&signal->theData[0];
+ rep->masterNodeId = cmasterNodeId;
+
+ sendSignal(DBTC_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ sendSignal(DBLQH_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ sendSignal(DBDIH_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ sendSignal(DBDICT_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ sendSignal(BACKUP_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ sendSignal(SUMA_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ sendSignal(GREP_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ Uint32 nodeId = 0;
+ while(!allFailed.isclear()){
+ nodeId = allFailed.find(nodeId + 1);
+ allFailed.clear(nodeId);
+ signal->theData[0] = NDB_LE_NODE_FAILREP;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = 0;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+ }//for
+
+ return;
+}//Ndbcntr::execNODE_FAILREP()
+
+/*******************************/
+/* READ_NODESREQ */
+/*******************************/
+void Ndbcntr::execREAD_NODESREQ(Signal* signal)
+{
+ jamEntry();
+
+ /*----------------------------------------------------------------------*/
+ // ANY BLOCK MAY SEND A REQUEST ABOUT NDB NODES AND VERSIONS IN THE
+ // SYSTEM. THIS REQUEST CAN ONLY BE HANDLED IN
+ // ABSOLUTE STARTPHASE 3 OR LATER
+ /*----------------------------------------------------------------------*/
+ BlockReference TuserBlockref = signal->theData[0];
+ ReadNodesConf * const readNodes = (ReadNodesConf *)&signal->theData[0];
+
+ /**
+ * Prepare inactiveNodes bitmask.
+ * The concept as such is by the way pretty useless.
+ * It makes parallell starts more or less impossible...
+ */
+ NdbNodeBitmask tmp1;
+ tmp1.bitOR(c_startedNodes);
+ if(!getNodeState().getNodeRestartInProgress()){
+ tmp1.bitOR(c_start.m_starting);
+ } else {
+ tmp1.set(getOwnNodeId());
+ }
+
+ NdbNodeBitmask tmp2;
+ tmp2.bitOR(c_allDefinedNodes);
+ tmp2.bitANDC(tmp1);
+ /**
+ * Fill in return signal
+ */
+ tmp2.copyto(NdbNodeBitmask::Size, readNodes->inactiveNodes);
+ c_allDefinedNodes.copyto(NdbNodeBitmask::Size, readNodes->allNodes);
+ c_clusterNodes.copyto(NdbNodeBitmask::Size, readNodes->clusterNodes);
+ c_startedNodes.copyto(NdbNodeBitmask::Size, readNodes->startedNodes);
+ c_start.m_starting.copyto(NdbNodeBitmask::Size, readNodes->startingNodes);
+
+ readNodes->noOfNodes = c_allDefinedNodes.count();
+ readNodes->masterNodeId = cmasterNodeId;
+ readNodes->ndynamicId = cdynamicNodeId;
+ if (cstartPhase > ZSTART_PHASE_2) {
+ jam();
+ sendSignal(TuserBlockref, GSN_READ_NODESCONF, signal,
+ ReadNodesConf::SignalLength, JBB);
+
+ } else {
+ jam();
+ signal->theData[0] = ZNOT_AVAILABLE;
+ sendSignal(TuserBlockref, GSN_READ_NODESREF, signal, 1, JBB);
+ }//if
+}//Ndbcntr::execREAD_NODESREQ()
+
+/*----------------------------------------------------------------------*/
+// SENDS APPL_ERROR TO QMGR AND THEN SET A POINTER OUT OF BOUNDS
+/*----------------------------------------------------------------------*/
+void Ndbcntr::systemErrorLab(Signal* signal)
+{
+ progError(0, 0); /* BUG INSERTION */
+ return;
+}//Ndbcntr::systemErrorLab()
+
+/*###########################################################################*/
+/* CNTR MASTER CREATES AND INITIALIZES A SYSTEMTABLE AT INITIALSTART */
+/* |-2048| # 1 00000001 | */
+/* | : | : | */
+/* | -1 | # 1 00000001 | */
+/* | 0 | 0 | */
+/* | 1 | 0 | */
+/* | : | : | */
+/* | 2047| 0 | */
+/*---------------------------------------------------------------------------*/
+void Ndbcntr::createSystableLab(Signal* signal, unsigned index)
+{
+ if (index >= g_sysTableCount) {
+ ndbassert(index == g_sysTableCount);
+ startInsertTransactions(signal);
+ return;
+ }
+ const SysTable& table = *g_sysTableList[index];
+ Uint32 propPage[256];
+ LinearWriter w(propPage, 256);
+
+ // XXX remove commented-out lines later
+
+ w.first();
+ w.add(DictTabInfo::TableName, table.name);
+ w.add(DictTabInfo::TableLoggedFlag, table.tableLoggedFlag);
+ //w.add(DictTabInfo::TableKValue, 6);
+ //w.add(DictTabInfo::MinLoadFactor, 70);
+ //w.add(DictTabInfo::MaxLoadFactor, 80);
+ w.add(DictTabInfo::FragmentTypeVal, (Uint32)table.fragmentType);
+ //w.add(DictTabInfo::TableStorageVal, (Uint32)DictTabInfo::MainMemory);
+ //w.add(DictTabInfo::NoOfKeyAttr, 1);
+ w.add(DictTabInfo::NoOfAttributes, (Uint32)table.columnCount);
+ //w.add(DictTabInfo::NoOfNullable, (Uint32)0);
+ //w.add(DictTabInfo::NoOfVariable, (Uint32)0);
+ //w.add(DictTabInfo::KeyLength, 1);
+ w.add(DictTabInfo::TableTypeVal, (Uint32)table.tableType);
+
+ for (unsigned i = 0; i < table.columnCount; i++) {
+ const SysColumn& column = table.columnList[i];
+ ndbassert(column.pos == i);
+ w.add(DictTabInfo::AttributeName, column.name);
+ w.add(DictTabInfo::AttributeId, (Uint32)column.pos);
+ w.add(DictTabInfo::AttributeKeyFlag, (Uint32)column.keyFlag);
+ //w.add(DictTabInfo::AttributeStorage, (Uint32)DictTabInfo::MainMemory);
+ w.add(DictTabInfo::AttributeNullableFlag, (Uint32)column.nullable);
+ w.add(DictTabInfo::AttributeExtType, (Uint32)column.type);
+ w.add(DictTabInfo::AttributeExtLength, (Uint32)column.length);
+ w.add(DictTabInfo::AttributeEnd, (Uint32)true);
+ }
+ w.add(DictTabInfo::TableEnd, (Uint32)true);
+
+ Uint32 length = w.getWordsUsed();
+ LinearSectionPtr ptr[3];
+ ptr[0].p = &propPage[0];
+ ptr[0].sz = length;
+
+ CreateTableReq* const req = (CreateTableReq*)signal->getDataPtrSend();
+ req->senderData = index;
+ req->senderRef = reference();
+ sendSignal(DBDICT_REF, GSN_CREATE_TABLE_REQ, signal,
+ CreateTableReq::SignalLength, JBB, ptr, 1);
+ return;
+}//Ndbcntr::createSystableLab()
+
+void Ndbcntr::execCREATE_TABLE_REF(Signal* signal)
+{
+ jamEntry();
+ progError(0,0);
+ return;
+}//Ndbcntr::execDICTTABREF()
+
+void Ndbcntr::execCREATE_TABLE_CONF(Signal* signal)
+{
+ jamEntry();
+ CreateTableConf * const conf = (CreateTableConf*)signal->getDataPtrSend();
+ //csystabId = conf->tableId;
+ ndbrequire(conf->senderData < g_sysTableCount);
+ const SysTable& table = *g_sysTableList[conf->senderData];
+ table.tableId = conf->tableId;
+ createSystableLab(signal, conf->senderData + 1);
+ //startInsertTransactions(signal);
+ return;
+}//Ndbcntr::execDICTTABCONF()
+
+/*******************************/
+/* DICTRELEASECONF */
+/*******************************/
+void Ndbcntr::startInsertTransactions(Signal* signal)
+{
+ jamEntry();
+
+ ckey = 1;
+ ctransidPhase = ZTRUE;
+ signal->theData[0] = 0;
+ signal->theData[1] = reference();
+ sendSignal(DBTC_REF, GSN_TCSEIZEREQ, signal, 2, JBB);
+ return;
+}//Ndbcntr::startInsertTransactions()
+
+/*******************************/
+/* TCSEIZECONF */
+/*******************************/
+void Ndbcntr::execTCSEIZECONF(Signal* signal)
+{
+ jamEntry();
+ ctcConnectionP = signal->theData[1];
+ crSystab7Lab(signal);
+ return;
+}//Ndbcntr::execTCSEIZECONF()
+
+const unsigned int RowsPerCommit = 16;
+void Ndbcntr::crSystab7Lab(Signal* signal)
+{
+ UintR tkey;
+ UintR Tmp;
+
+ TcKeyReq * const tcKeyReq = (TcKeyReq *)&signal->theData[0];
+
+ UintR reqInfo_Start = 0;
+ tcKeyReq->setOperationType(reqInfo_Start, ZINSERT); // Insert
+ tcKeyReq->setKeyLength (reqInfo_Start, 1);
+ tcKeyReq->setAIInTcKeyReq (reqInfo_Start, 5);
+ tcKeyReq->setAbortOption (reqInfo_Start, TcKeyReq::AbortOnError);
+
+/* KEY LENGTH = 1, ATTRINFO LENGTH IN TCKEYREQ = 5 */
+ cresponses = 0;
+ const UintR guard0 = ckey + (RowsPerCommit - 1);
+ for (Tmp = ckey; Tmp <= guard0; Tmp++) {
+ UintR reqInfo = reqInfo_Start;
+ if (Tmp == ckey) { // First iteration, Set start flag
+ jam();
+ tcKeyReq->setStartFlag(reqInfo, 1);
+ } //if
+ if (Tmp == guard0) { // Last iteration, Set commit flag
+ jam();
+ tcKeyReq->setCommitFlag(reqInfo, 1);
+ tcKeyReq->setExecuteFlag(reqInfo, 1);
+ } //if
+ if (ctransidPhase == ZTRUE) {
+ jam();
+ tkey = 0;
+ tkey = tkey - Tmp;
+ } else {
+ jam();
+ tkey = Tmp;
+ }//if
+
+ tcKeyReq->apiConnectPtr = ctcConnectionP;
+ tcKeyReq->attrLen = 5;
+ tcKeyReq->tableId = g_sysTable_SYSTAB_0.tableId;
+ tcKeyReq->requestInfo = reqInfo;
+ tcKeyReq->tableSchemaVersion = ZSYSTAB_VERSION;
+ tcKeyReq->transId1 = 0;
+ tcKeyReq->transId2 = ckey;
+
+//-------------------------------------------------------------
+// There is no optional part in this TCKEYREQ. There is one
+// key word and five ATTRINFO words.
+//-------------------------------------------------------------
+ Uint32* tKeyDataPtr = &tcKeyReq->scanInfo;
+ Uint32* tAIDataPtr = &tKeyDataPtr[1];
+
+ tKeyDataPtr[0] = tkey;
+
+ AttributeHeader::init(&tAIDataPtr[0], 0, 1);
+ tAIDataPtr[1] = tkey;
+ AttributeHeader::init(&tAIDataPtr[2], 1, 2);
+ tAIDataPtr[3] = (tkey << 16);
+ tAIDataPtr[4] = 1;
+ sendSignal(DBTC_REF, GSN_TCKEYREQ, signal,
+ TcKeyReq::StaticLength + 6, JBB);
+ }//for
+ ckey = ckey + RowsPerCommit;
+ return;
+}//Ndbcntr::crSystab7Lab()
+
+/*******************************/
+/* TCKEYCONF09 */
+/*******************************/
+void Ndbcntr::execTCKEYCONF(Signal* signal)
+{
+ const TcKeyConf * const keyConf = (TcKeyConf *)&signal->theData[0];
+
+ jamEntry();
+ cgciSystab = keyConf->gci;
+ UintR confInfo = keyConf->confInfo;
+
+ if (TcKeyConf::getMarkerFlag(confInfo)){
+ Uint32 transId1 = keyConf->transId1;
+ Uint32 transId2 = keyConf->transId2;
+ signal->theData[0] = transId1;
+ signal->theData[1] = transId2;
+ sendSignal(DBTC_REF, GSN_TC_COMMIT_ACK, signal, 2, JBB);
+ }//if
+
+ cresponses = cresponses + TcKeyConf::getNoOfOperations(confInfo);
+ if (TcKeyConf::getCommitFlag(confInfo)){
+ jam();
+ ndbrequire(cresponses == RowsPerCommit);
+
+ crSystab8Lab(signal);
+ return;
+ }
+ return;
+}//Ndbcntr::tckeyConfLab()
+
+void Ndbcntr::crSystab8Lab(Signal* signal)
+{
+ if (ckey < ZSIZE_SYSTAB) {
+ jam();
+ crSystab7Lab(signal);
+ return;
+ } else if (ctransidPhase == ZTRUE) {
+ jam();
+ ckey = 1;
+ ctransidPhase = ZFALSE;
+ crSystab7Lab(signal);
+ return;
+ }//if
+ signal->theData[0] = ctcConnectionP;
+ signal->theData[1] = reference();
+ signal->theData[2] = 0;
+ sendSignal(DBTC_REF, GSN_TCRELEASEREQ, signal, 2, JBB);
+ return;
+}//Ndbcntr::crSystab8Lab()
+
+/*******************************/
+/* TCRELEASECONF */
+/*******************************/
+void Ndbcntr::execTCRELEASECONF(Signal* signal)
+{
+ jamEntry();
+ waitpoint52Lab(signal);
+ return;
+}//Ndbcntr::execTCRELEASECONF()
+
+void Ndbcntr::crSystab9Lab(Signal* signal)
+{
+ signal->theData[1] = reference();
+ sendSignalWithDelay(DBDIH_REF, GSN_GETGCIREQ, signal, 100, 2);
+ return;
+}//Ndbcntr::crSystab9Lab()
+
+/*******************************/
+/* GETGCICONF */
+/*******************************/
+void Ndbcntr::execGETGCICONF(Signal* signal)
+{
+ jamEntry();
+
+#ifndef NO_GCP
+ if (signal->theData[1] < cgciSystab) {
+ jam();
+/*--------------------------------------*/
+/* MAKE SURE THAT THE SYSTABLE IS */
+/* NOW SAFE ON DISK */
+/*--------------------------------------*/
+ crSystab9Lab(signal);
+ return;
+ }//if
+#endif
+ waitpoint52Lab(signal);
+ return;
+}//Ndbcntr::execGETGCICONF()
+
+void Ndbcntr::execTCKEYREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Ndbcntr::execTCKEYREF()
+
+void Ndbcntr::execTCROLLBACKREP(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Ndbcntr::execTCROLLBACKREP()
+
+void Ndbcntr::execTCRELEASEREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Ndbcntr::execTCRELEASEREF()
+
+void Ndbcntr::execTCSEIZEREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Ndbcntr::execTCSEIZEREF()
+
+
+/*---------------------------------------------------------------------------*/
+/*INITIALIZE VARIABLES AND RECORDS */
+/*---------------------------------------------------------------------------*/
+void Ndbcntr::initData(Signal* signal)
+{
+ c_start.reset();
+ cmasterNodeId = 0;
+ cnoStartNodes = 0;
+ cnoWaitrep = 0;
+}//Ndbcntr::initData()
+
+
+/*---------------------------------------------------------------------------*/
+/*RESET VARIABLES USED DURING THE START */
+/*---------------------------------------------------------------------------*/
+void Ndbcntr::resetStartVariables(Signal* signal)
+{
+ cnoStartNodes = 0;
+ cnoWaitrep6 = cnoWaitrep7 = 0;
+}//Ndbcntr::resetStartVariables()
+
+
+/*---------------------------------------------------------------------------*/
+// SEND THE SIGNAL
+// INPUT CNDB_BLOCKS_COUNT
+/*---------------------------------------------------------------------------*/
+void Ndbcntr::sendNdbSttor(Signal* signal)
+{
+ NdbBlocksRecPtr ndbBlocksPtr;
+
+ ndbBlocksPtr.i = cndbBlocksCount;
+ ptrCheckGuard(ndbBlocksPtr, ZSIZE_NDB_BLOCKS_REC, ndbBlocksRec);
+
+ NdbSttor * const req = (NdbSttor*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->nodeId = getOwnNodeId();
+ req->internalStartPhase = cinternalStartphase;
+ req->typeOfStart = ctypeOfStart;
+ req->masterNodeId = cmasterNodeId;
+
+ for (int i = 0; i < 16; i++) {
+ // Garbage
+ req->config[i] = 0x88776655;
+ //cfgBlockPtr.p->cfgData[i];
+ }
+
+ //#define MAX_STARTPHASE 2
+#ifdef TRACE_STTOR
+ ndbout_c("sending NDB_STTOR(%d) to %s",
+ cinternalStartphase,
+ getBlockName( refToBlock(ndbBlocksPtr.p->blockref)));
+#endif
+ sendSignal(ndbBlocksPtr.p->blockref, GSN_NDB_STTOR, signal, 22, JBB);
+ cndbBlocksCount++;
+}//Ndbcntr::sendNdbSttor()
+
+/*---------------------------------------------------------------------------*/
+// JUST SEND THE SIGNAL
+/*---------------------------------------------------------------------------*/
+void Ndbcntr::sendSttorry(Signal* signal)
+{
+ signal->theData[3] = ZSTART_PHASE_1;
+ signal->theData[4] = ZSTART_PHASE_2;
+ signal->theData[5] = ZSTART_PHASE_3;
+ signal->theData[6] = ZSTART_PHASE_4;
+ signal->theData[7] = ZSTART_PHASE_5;
+ signal->theData[8] = ZSTART_PHASE_6;
+ // skip simulated phase 7
+ signal->theData[9] = ZSTART_PHASE_8;
+ signal->theData[10] = ZSTART_PHASE_9;
+ signal->theData[11] = ZSTART_PHASE_END;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 12, JBB);
+}//Ndbcntr::sendSttorry()
+
+void
+Ndbcntr::execDUMP_STATE_ORD(Signal* signal)
+{
+ DumpStateOrd * const & dumpState = (DumpStateOrd *)&signal->theData[0];
+ if(signal->theData[0] == 13){
+ infoEvent("Cntr: cstartPhase = %d, cinternalStartphase = %d, block = %d",
+ cstartPhase, cinternalStartphase, cndbBlocksCount);
+ infoEvent("Cntr: cmasterNodeId = %d", cmasterNodeId);
+ }
+
+ if (dumpState->args[0] == DumpStateOrd::NdbcntrTestStopOnError){
+ if (theConfiguration.stopOnError() == true)
+ ((Configuration&)theConfiguration).stopOnError(false);
+
+ const BlockReference tblockref = calcNdbCntrBlockRef(getOwnNodeId());
+
+ SystemError * const sysErr = (SystemError*)&signal->theData[0];
+ sysErr->errorCode = SystemError::TestStopOnError;
+ sysErr->errorRef = reference();
+ sendSignal(tblockref, GSN_SYSTEM_ERROR, signal,
+ SystemError::SignalLength, JBA);
+ }
+
+
+}//Ndbcntr::execDUMP_STATE_ORD()
+
+void Ndbcntr::execSET_VAR_REQ(Signal* signal) {
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+
+ switch (var) {
+ case TimeToWaitAlive:
+ // Valid only during start so value not set.
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ }// switch
+#endif
+}//Ndbcntr::execSET_VAR_REQ()
+
+void Ndbcntr::updateNodeState(Signal* signal, const NodeState& newState) const{
+ NodeStateRep * const stateRep = (NodeStateRep *)&signal->theData[0];
+
+ stateRep->nodeState = newState;
+ stateRep->nodeState.masterNodeId = cmasterNodeId;
+ stateRep->nodeState.setNodeGroup(c_nodeGroup);
+
+ for(Uint32 i = 0; i<ALL_BLOCKS_SZ; i++){
+ sendSignal(ALL_BLOCKS[i].Ref, GSN_NODE_STATE_REP, signal,
+ NodeStateRep::SignalLength, JBB);
+ }
+}
+
+void
+Ndbcntr::execRESUME_REQ(Signal* signal){
+ //ResumeReq * const req = (ResumeReq *)&signal->theData[0];
+ //ResumeRef * const ref = (ResumeRef *)&signal->theData[0];
+
+ jamEntry();
+ //Uint32 senderData = req->senderData;
+ //BlockReference senderRef = req->senderRef;
+ NodeState newState(NodeState::SL_STARTED);
+ updateNodeState(signal, newState);
+ c_stopRec.stopReq.senderRef=0;
+}
+
+void
+Ndbcntr::execSTOP_REQ(Signal* signal){
+ StopReq * const req = (StopReq *)&signal->theData[0];
+ StopRef * const ref = (StopRef *)&signal->theData[0];
+ Uint32 singleuser = req->singleuser;
+ jamEntry();
+ Uint32 senderData = req->senderData;
+ BlockReference senderRef = req->senderRef;
+ bool abort = StopReq::getStopAbort(req->requestInfo);
+
+ if(getNodeState().startLevel < NodeState::SL_STARTED ||
+ abort && !singleuser){
+ /**
+ * Node is not started yet
+ *
+ * So stop it quickly
+ */
+ jam();
+ const Uint32 reqInfo = req->requestInfo;
+ if(StopReq::getPerformRestart(reqInfo)){
+ jam();
+ StartOrd * startOrd = (StartOrd *)&signal->theData[0];
+ startOrd->restartInfo = reqInfo;
+ sendSignal(CMVMI_REF, GSN_START_ORD, signal, 1, JBA);
+ } else {
+ jam();
+ sendSignal(CMVMI_REF, GSN_STOP_ORD, signal, 1, JBA);
+ }
+ return;
+ }
+
+ if(c_stopRec.stopReq.senderRef != 0 && !singleuser){
+ jam();
+ /**
+ * Requested a system shutdown
+ */
+ if(StopReq::getSystemStop(req->requestInfo)){
+ jam();
+ sendSignalWithDelay(reference(), GSN_STOP_REQ, signal, 100,
+ StopReq::SignalLength);
+ return;
+ }
+
+ /**
+ * Requested a node shutdown
+ */
+ if(StopReq::getSystemStop(c_stopRec.stopReq.requestInfo))
+ ref->errorCode = StopRef::SystemShutdownInProgress;
+ else
+ ref->errorCode = StopRef::NodeShutdownInProgress;
+ ref->senderData = senderData;
+ sendSignal(senderRef, GSN_STOP_REF, signal, StopRef::SignalLength, JBB);
+ return;
+ }
+
+ c_stopRec.stopReq = * req;
+ c_stopRec.stopInitiatedTime = NdbTick_CurrentMillisecond();
+
+ if(StopReq::getSystemStop(c_stopRec.stopReq.requestInfo) && !singleuser) {
+ jam();
+ if(StopReq::getPerformRestart(c_stopRec.stopReq.requestInfo)){
+ ((Configuration&)theConfiguration).stopOnError(false);
+ }
+ }
+ if(!singleuser) {
+ if(!c_stopRec.checkNodeFail(signal)){
+ jam();
+ return;
+ }
+ }
+
+ signal->theData[0] = NDB_LE_NDBStopStarted;
+ signal->theData[1] = StopReq::getSystemStop(c_stopRec.stopReq.requestInfo) ? 1 : 0;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ NodeState newState(NodeState::SL_STOPPING_1,
+ StopReq::getSystemStop(c_stopRec.stopReq.requestInfo));
+
+ if(singleuser) {
+ newState.setSingleUser(true);
+ newState.setSingleUserApi(c_stopRec.stopReq.singleUserApi);
+ }
+ updateNodeState(signal, newState);
+ signal->theData[0] = ZSHUTDOWN;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void
+Ndbcntr::StopRecord::checkTimeout(Signal* signal){
+ jamEntry();
+
+ if(!cntr.getNodeState().getSingleUserMode())
+ if(!checkNodeFail(signal)){
+ jam();
+ return;
+ }
+
+ switch(cntr.getNodeState().startLevel){
+ case NodeState::SL_STOPPING_1:
+ checkApiTimeout(signal);
+ break;
+ case NodeState::SL_STOPPING_2:
+ checkTcTimeout(signal);
+ break;
+ case NodeState::SL_STOPPING_3:
+ checkLqhTimeout_1(signal);
+ break;
+ case NodeState::SL_STOPPING_4:
+ checkLqhTimeout_2(signal);
+ break;
+ case NodeState::SL_SINGLEUSER:
+ break;
+ default:
+ ndbrequire(false);
+ }
+}
+
+bool
+Ndbcntr::StopRecord::checkNodeFail(Signal* signal){
+ jam();
+ if(StopReq::getSystemStop(stopReq.requestInfo)){
+ jam();
+ return true;
+ }
+
+ /**
+ * Check if I can survive me stopping
+ */
+ NodeBitmask ndbMask;
+ ndbMask.assign(cntr.c_startedNodes);
+ ndbMask.clear(cntr.getOwnNodeId());
+
+ CheckNodeGroups* sd = (CheckNodeGroups*)&signal->theData[0];
+ sd->blockRef = cntr.reference();
+ sd->requestType = CheckNodeGroups::Direct | CheckNodeGroups::ArbitCheck;
+ sd->mask = ndbMask;
+ cntr.EXECUTE_DIRECT(DBDIH, GSN_CHECKNODEGROUPSREQ, signal,
+ CheckNodeGroups::SignalLength);
+ jamEntry();
+ switch (sd->output) {
+ case CheckNodeGroups::Win:
+ case CheckNodeGroups::Partitioning:
+ return true;
+ break;
+ }
+
+ StopRef * const ref = (StopRef *)&signal->theData[0];
+
+ ref->senderData = stopReq.senderData;
+ ref->errorCode = StopRef::NodeShutdownWouldCauseSystemCrash;
+
+ const BlockReference bref = stopReq.senderRef;
+ cntr.sendSignal(bref, GSN_STOP_REF, signal, StopRef::SignalLength, JBB);
+
+ stopReq.senderRef = 0;
+
+ NodeState newState(NodeState::SL_STARTED);
+
+ cntr.updateNodeState(signal, newState);
+
+ signal->theData[0] = NDB_LE_NDBStopAborted;
+ cntr.sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 1, JBB);
+
+ return false;
+}
+
+void
+Ndbcntr::StopRecord::checkApiTimeout(Signal* signal){
+ const Int32 timeout = stopReq.apiTimeout;
+ const NDB_TICKS alarm = stopInitiatedTime + (NDB_TICKS)timeout;
+ const NDB_TICKS now = NdbTick_CurrentMillisecond();
+ if((timeout >= 0 && now >= alarm)){
+ // || checkWithApiInSomeMagicWay)
+ jam();
+ NodeState newState(NodeState::SL_STOPPING_2,
+ StopReq::getSystemStop(stopReq.requestInfo));
+ if(stopReq.singleuser) {
+ newState.setSingleUser(true);
+ newState.setSingleUserApi(stopReq.singleUserApi);
+ }
+ cntr.updateNodeState(signal, newState);
+
+ stopInitiatedTime = now;
+ }
+
+ signal->theData[0] = ZSHUTDOWN;
+ cntr.sendSignalWithDelay(cntr.reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void
+Ndbcntr::StopRecord::checkTcTimeout(Signal* signal){
+ const Int32 timeout = stopReq.transactionTimeout;
+ const NDB_TICKS alarm = stopInitiatedTime + (NDB_TICKS)timeout;
+ const NDB_TICKS now = NdbTick_CurrentMillisecond();
+ if((timeout >= 0 && now >= alarm)){
+ // || checkWithTcInSomeMagicWay)
+ jam();
+ if(stopReq.getSystemStop(stopReq.requestInfo) || stopReq.singleuser){
+ jam();
+ if(stopReq.singleuser)
+ {
+ jam();
+ AbortAllReq * req = (AbortAllReq*)&signal->theData[0];
+ req->senderRef = cntr.reference();
+ req->senderData = 12;
+ cntr.sendSignal(DBTC_REF, GSN_ABORT_ALL_REQ, signal,
+ AbortAllReq::SignalLength, JBB);
+ }
+ else
+ {
+ WaitGCPReq * req = (WaitGCPReq*)&signal->theData[0];
+ req->senderRef = cntr.reference();
+ req->senderData = 12;
+ req->requestType = WaitGCPReq::CompleteForceStart;
+ cntr.sendSignal(DBDIH_REF, GSN_WAIT_GCP_REQ, signal,
+ WaitGCPReq::SignalLength, JBB);
+ }
+ } else {
+ jam();
+ StopPermReq * req = (StopPermReq*)&signal->theData[0];
+ req->senderRef = cntr.reference();
+ req->senderData = 12;
+ cntr.sendSignal(DBDIH_REF, GSN_STOP_PERM_REQ, signal,
+ StopPermReq::SignalLength, JBB);
+ }
+ return;
+ }
+ signal->theData[0] = ZSHUTDOWN;
+ cntr.sendSignalWithDelay(cntr.reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void Ndbcntr::execSTOP_PERM_REF(Signal* signal){
+ //StopPermRef* const ref = (StopPermRef*)&signal->theData[0];
+
+ jamEntry();
+
+ signal->theData[0] = ZSHUTDOWN;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void Ndbcntr::execSTOP_PERM_CONF(Signal* signal){
+ jamEntry();
+
+ AbortAllReq * req = (AbortAllReq*)&signal->theData[0];
+ req->senderRef = reference();
+ req->senderData = 12;
+ sendSignal(DBTC_REF, GSN_ABORT_ALL_REQ, signal,
+ AbortAllReq::SignalLength, JBB);
+}
+
+void Ndbcntr::execABORT_ALL_CONF(Signal* signal){
+ jamEntry();
+ if(c_stopRec.stopReq.singleuser) {
+ jam();
+ NodeState newState(NodeState::SL_SINGLEUSER);
+ newState.setSingleUser(true);
+ newState.setSingleUserApi(c_stopRec.stopReq.singleUserApi);
+ updateNodeState(signal, newState);
+ c_stopRec.stopInitiatedTime = NdbTick_CurrentMillisecond();
+
+ }
+ else
+ {
+ jam();
+ NodeState newState(NodeState::SL_STOPPING_3,
+ StopReq::getSystemStop(c_stopRec.stopReq.requestInfo));
+ updateNodeState(signal, newState);
+
+ c_stopRec.stopInitiatedTime = NdbTick_CurrentMillisecond();
+
+ signal->theData[0] = ZSHUTDOWN;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 1);
+ }
+}
+
+void Ndbcntr::execABORT_ALL_REF(Signal* signal){
+ jamEntry();
+ ndbrequire(false);
+}
+
+void
+Ndbcntr::StopRecord::checkLqhTimeout_1(Signal* signal){
+ const Int32 timeout = stopReq.readOperationTimeout;
+ const NDB_TICKS alarm = stopInitiatedTime + (NDB_TICKS)timeout;
+ const NDB_TICKS now = NdbTick_CurrentMillisecond();
+
+ if((timeout >= 0 && now >= alarm)){
+ // || checkWithLqhInSomeMagicWay)
+ jam();
+
+ ChangeNodeStateReq * req = (ChangeNodeStateReq*)&signal->theData[0];
+
+ NodeState newState(NodeState::SL_STOPPING_4,
+ StopReq::getSystemStop(stopReq.requestInfo));
+ req->nodeState = newState;
+ req->senderRef = cntr.reference();
+ req->senderData = 12;
+ cntr.sendSignal(DBLQH_REF, GSN_CHANGE_NODE_STATE_REQ, signal, 2, JBB);
+ return;
+ }
+ signal->theData[0] = ZSHUTDOWN;
+ cntr.sendSignalWithDelay(cntr.reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void Ndbcntr::execCHANGE_NODE_STATE_CONF(Signal* signal){
+ jamEntry();
+ signal->theData[0] = reference();
+ signal->theData[1] = 12;
+ sendSignal(DBDIH_REF, GSN_STOP_ME_REQ, signal, 2, JBB);
+}
+
+void Ndbcntr::execSTOP_ME_REF(Signal* signal){
+ jamEntry();
+ ndbrequire(false);
+}
+
+
+void Ndbcntr::execSTOP_ME_CONF(Signal* signal){
+ jamEntry();
+
+ NodeState newState(NodeState::SL_STOPPING_4,
+ StopReq::getSystemStop(c_stopRec.stopReq.requestInfo));
+ updateNodeState(signal, newState);
+
+ c_stopRec.stopInitiatedTime = NdbTick_CurrentMillisecond();
+ signal->theData[0] = ZSHUTDOWN;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void
+Ndbcntr::StopRecord::checkLqhTimeout_2(Signal* signal){
+ const Int32 timeout = stopReq.operationTimeout;
+ const NDB_TICKS alarm = stopInitiatedTime + (NDB_TICKS)timeout;
+ const NDB_TICKS now = NdbTick_CurrentMillisecond();
+
+ if((timeout >= 0 && now >= alarm)){
+ // || checkWithLqhInSomeMagicWay)
+ jam();
+ if(StopReq::getPerformRestart(stopReq.requestInfo)){
+ jam();
+ StartOrd * startOrd = (StartOrd *)&signal->theData[0];
+ startOrd->restartInfo = stopReq.requestInfo;
+ cntr.sendSignal(CMVMI_REF, GSN_START_ORD, signal, 2, JBA);
+ } else {
+ jam();
+ cntr.sendSignal(CMVMI_REF, GSN_STOP_ORD, signal, 1, JBA);
+ }
+ return;
+ }
+ signal->theData[0] = ZSHUTDOWN;
+ cntr.sendSignalWithDelay(cntr.reference(), GSN_CONTINUEB, signal, 100, 1);
+}
+
+void Ndbcntr::execWAIT_GCP_REF(Signal* signal){
+ jamEntry();
+
+ //WaitGCPRef* const ref = (WaitGCPRef*)&signal->theData[0];
+
+ WaitGCPReq * req = (WaitGCPReq*)&signal->theData[0];
+ req->senderRef = reference();
+ req->senderData = 12;
+ req->requestType = WaitGCPReq::CompleteForceStart;
+ sendSignal(DBDIH_REF, GSN_WAIT_GCP_REQ, signal,
+ WaitGCPReq::SignalLength, JBB);
+}
+
+void Ndbcntr::execWAIT_GCP_CONF(Signal* signal){
+ jamEntry();
+
+ ndbrequire(StopReq::getSystemStop(c_stopRec.stopReq.requestInfo));
+ NodeState newState(NodeState::SL_STOPPING_3, true);
+
+ /**
+ * Inform QMGR so that arbitrator won't kill us
+ */
+ NodeStateRep * rep = (NodeStateRep *)&signal->theData[0];
+ rep->nodeState = newState;
+ rep->nodeState.masterNodeId = cmasterNodeId;
+ rep->nodeState.setNodeGroup(c_nodeGroup);
+ EXECUTE_DIRECT(QMGR, GSN_NODE_STATE_REP, signal, NodeStateRep::SignalLength);
+
+ if(StopReq::getPerformRestart(c_stopRec.stopReq.requestInfo)){
+ jam();
+ StartOrd * startOrd = (StartOrd *)&signal->theData[0];
+ startOrd->restartInfo = c_stopRec.stopReq.requestInfo;
+ sendSignalWithDelay(CMVMI_REF, GSN_START_ORD, signal, 500,
+ StartOrd::SignalLength);
+ } else {
+ jam();
+ sendSignalWithDelay(CMVMI_REF, GSN_STOP_ORD, signal, 500, 1);
+ }
+ return;
+}
+
+void Ndbcntr::execSTTORRY(Signal* signal){
+ jamEntry();
+ c_missra.execSTTORRY(signal);
+}
+
+void Ndbcntr::execREAD_CONFIG_CONF(Signal* signal){
+ jamEntry();
+ c_missra.execREAD_CONFIG_CONF(signal);
+}
+
+void Ndbcntr::execSTART_ORD(Signal* signal){
+ jamEntry();
+ ndbrequire(NO_OF_BLOCKS == ALL_BLOCKS_SZ);
+ c_missra.execSTART_ORD(signal);
+}
+
+void
+Ndbcntr::clearFilesystem(Signal* signal){
+ FsRemoveReq * req = (FsRemoveReq *)signal->getDataPtrSend();
+ req->userReference = reference();
+ req->userPointer = 0;
+ req->directory = 1;
+ req->ownDirectory = 1;
+ FsOpenReq::setVersion(req->fileNumber, 3);
+ FsOpenReq::setSuffix(req->fileNumber, FsOpenReq::S_CTL); // Can by any...
+ FsOpenReq::v1_setDisk(req->fileNumber, c_fsRemoveCount);
+ sendSignal(NDBFS_REF, GSN_FSREMOVEREQ, signal,
+ FsRemoveReq::SignalLength, JBA);
+ c_fsRemoveCount++;
+}
+
+void
+Ndbcntr::execFSREMOVEREF(Signal* signal){
+ jamEntry();
+ ndbrequire(0);
+}
+
+void
+Ndbcntr::execFSREMOVECONF(Signal* signal){
+ jamEntry();
+ if(c_fsRemoveCount == 13){
+ jam();
+ sendSttorry(signal);
+ } else {
+ jam();
+ ndbrequire(c_fsRemoveCount < 13);
+ clearFilesystem(signal);
+ }//if
+}
+
+void Ndbcntr::Missra::execSTART_ORD(Signal* signal){
+ signal->theData[0] = NDB_LE_NDBStartStarted;
+ signal->theData[1] = NDB_VERSION;
+ cntr.sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ currentBlockIndex = 0;
+ sendNextREAD_CONFIG_REQ(signal);
+}
+
+void Ndbcntr::Missra::sendNextREAD_CONFIG_REQ(Signal* signal){
+
+ if(currentBlockIndex < ALL_BLOCKS_SZ){
+ jam();
+
+ ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtrSend();
+ req->senderData = 0;
+ req->senderRef = cntr.reference();
+ req->noOfParameters = 0;
+
+ const BlockReference ref = ALL_BLOCKS[currentBlockIndex].Ref;
+
+#if 0
+ ndbout_c("sending READ_CONFIG_REQ to %s(ref=%x index=%d)",
+ getBlockName( refToBlock(ref)),
+ ref,
+ currentBlockIndex);
+#endif
+
+ cntr.sendSignal(ref, GSN_READ_CONFIG_REQ, signal,
+ ReadConfigReq::SignalLength, JBB);
+ return;
+ }
+
+ /**
+ * Finished...
+ */
+ currentStartPhase = 0;
+ for(Uint32 i = 0; i<NO_OF_BLOCKS; i++){
+ if(ALL_BLOCKS[i].NextSP < currentStartPhase)
+ currentStartPhase = ALL_BLOCKS[i].NextSP;
+ }
+
+ currentBlockIndex = 0;
+ sendNextSTTOR(signal);
+}
+
+void Ndbcntr::Missra::execREAD_CONFIG_CONF(Signal* signal){
+ const ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtr();
+
+ const Uint32 ref = conf->senderRef;
+ ndbrequire(refToBlock(ALL_BLOCKS[currentBlockIndex].Ref) == refToBlock(ref));
+
+ currentBlockIndex++;
+ sendNextREAD_CONFIG_REQ(signal);
+}
+
+void Ndbcntr::Missra::execSTTORRY(Signal* signal){
+ const BlockReference ref = signal->senderBlockRef();
+ ndbrequire(refToBlock(ref) == refToBlock(ALL_BLOCKS[currentBlockIndex].Ref));
+
+ /**
+ * Update next start phase
+ */
+ for (Uint32 i = 3; i < 25; i++){
+ jam();
+ if (signal->theData[i] > currentStartPhase){
+ jam();
+ ALL_BLOCKS[currentBlockIndex].NextSP = signal->theData[i];
+ break;
+ }
+ }
+
+ currentBlockIndex++;
+ sendNextSTTOR(signal);
+}
+
+void Ndbcntr::Missra::sendNextSTTOR(Signal* signal){
+
+ for(; currentStartPhase < 255 ; currentStartPhase++){
+ jam();
+
+ const Uint32 start = currentBlockIndex;
+
+ for(; currentBlockIndex < ALL_BLOCKS_SZ; currentBlockIndex++){
+ jam();
+ if(ALL_BLOCKS[currentBlockIndex].NextSP == currentStartPhase){
+ jam();
+ signal->theData[0] = 0;
+ signal->theData[1] = currentStartPhase;
+ signal->theData[2] = 0;
+ signal->theData[3] = 0;
+ signal->theData[4] = 0;
+ signal->theData[5] = 0;
+ signal->theData[6] = 0;
+ signal->theData[7] = cntr.ctypeOfStart;
+
+ const BlockReference ref = ALL_BLOCKS[currentBlockIndex].Ref;
+
+#ifdef MAX_STARTPHASE
+ ndbrequire(currentStartPhase <= MAX_STARTPHASE);
+#endif
+
+#ifdef TRACE_STTOR
+ ndbout_c("sending STTOR(%d) to %s(ref=%x index=%d)",
+ currentStartPhase,
+ getBlockName( refToBlock(ref)),
+ ref,
+ currentBlockIndex);
+#endif
+
+ cntr.sendSignal(ref, GSN_STTOR, signal, 8, JBB);
+
+ return;
+ }
+ }
+
+ currentBlockIndex = 0;
+
+ if(start != 0){
+ /**
+ * At least one wanted this start phase, report it
+ */
+ jam();
+ signal->theData[0] = NDB_LE_StartPhaseCompleted;
+ signal->theData[1] = currentStartPhase;
+ signal->theData[2] = cntr.ctypeOfStart;
+ cntr.sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+ }
+ }
+
+ signal->theData[0] = NDB_LE_NDBStartCompleted;
+ signal->theData[1] = NDB_VERSION;
+ cntr.sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ NodeState newState(NodeState::SL_STARTED);
+ cntr.updateNodeState(signal, newState);
+
+ /**
+ * Backward
+ */
+ UpgradeStartup::sendCmAppChg(cntr, signal, 3); //RUN
+
+ NdbNodeBitmask nodes = cntr.c_clusterNodes;
+ Uint32 node = 0;
+ while((node = nodes.find(node+1)) != NdbNodeBitmask::NotFound){
+ if(cntr.getNodeInfo(node).m_version < MAKE_VERSION(3,5,0)){
+ nodes.clear(node);
+ }
+ }
+
+ NodeReceiverGroup rg(NDBCNTR, nodes);
+ signal->theData[0] = cntr.getOwnNodeId();
+ cntr.sendSignal(rg, GSN_CNTR_START_REP, signal, 1, JBB);
+}
+
+/**
+ * Backward compatible code
+ */
+void
+UpgradeStartup::sendCmAppChg(Ndbcntr& cntr, Signal* signal, Uint32 startLevel){
+
+ if(cntr.getNodeInfo(cntr.cmasterNodeId).m_version >= MAKE_VERSION(3,5,0)){
+ jam();
+ return;
+ }
+
+ /**
+ * Old NDB running
+ */
+
+ signal->theData[0] = startLevel;
+ signal->theData[1] = cntr.getOwnNodeId();
+ signal->theData[2] = 3 | ('N' << 8);
+ signal->theData[3] = 'D' | ('B' << 8);
+ signal->theData[4] = 0;
+ signal->theData[5] = 0;
+ signal->theData[6] = 0;
+ signal->theData[7] = 0;
+ signal->theData[8] = 0;
+ signal->theData[9] = 0;
+ signal->theData[10] = 0;
+ signal->theData[11] = 0;
+
+ NdbNodeBitmask nodes = cntr.c_clusterNodes;
+ nodes.clear(cntr.getOwnNodeId());
+ Uint32 node = 0;
+ while((node = nodes.find(node+1)) != NdbNodeBitmask::NotFound){
+ if(cntr.getNodeInfo(node).m_version < MAKE_VERSION(3,5,0)){
+ cntr.sendSignal(cntr.calcQmgrBlockRef(node),
+ GSN_CM_APPCHG, signal, 12, JBB);
+ } else {
+ cntr.c_startedNodes.set(node); // Fake started
+ }
+ }
+}
+
+void
+UpgradeStartup::execCM_APPCHG(SimulatedBlock & block, Signal* signal){
+ Uint32 state = signal->theData[0];
+ Uint32 nodeId = signal->theData[1];
+ if(block.number() == QMGR){
+ Ndbcntr& cntr = * (Ndbcntr*)globalData.getBlock(CNTR);
+ switch(state){
+ case 0: // ZADD
+ break;
+ case 2: // ZSTART
+ break;
+ case 3: // ZRUN{
+ cntr.c_startedNodes.set(nodeId);
+
+ Uint32 recv = cntr.c_startedNodes.count();
+ Uint32 cnt = cntr.c_clusterNodes.count();
+ if(recv + 1 == cnt){ //+1 == own node
+ /**
+ * Check master
+ */
+ sendCntrMasterReq(cntr, signal, 0);
+ }
+ return;
+ }
+ }
+ block.progError(0,0);
+}
+
+void
+UpgradeStartup::sendCntrMasterReq(Ndbcntr& cntr, Signal* signal, Uint32 n){
+ Uint32 node = cntr.c_startedNodes.find(n);
+ if(node != NdbNodeBitmask::NotFound &&
+ (node == cntr.getOwnNodeId() ||
+ cntr.getNodeInfo(node).m_version >= MAKE_VERSION(3,5,0))){
+ node = cntr.c_startedNodes.find(node+1);
+ }
+
+ if(node == NdbNodeBitmask::NotFound){
+ cntr.progError(0,0);
+ }
+
+ CntrMasterReq * const cntrMasterReq = (CntrMasterReq*)&signal->theData[0];
+ cntr.c_clusterNodes.copyto(NdbNodeBitmask::Size, cntrMasterReq->theNodes);
+ NdbNodeBitmask::clear(cntrMasterReq->theNodes, cntr.getOwnNodeId());
+ cntrMasterReq->userBlockRef = 0;
+ cntrMasterReq->userNodeId = cntr.getOwnNodeId();
+ cntrMasterReq->typeOfStart = NodeState::ST_INITIAL_NODE_RESTART;
+ cntrMasterReq->noRestartNodes = cntr.c_clusterNodes.count() - 1;
+ cntr.sendSignal(cntr.calcNdbCntrBlockRef(node), GSN_CNTR_MASTERREQ,
+ signal, CntrMasterReq::SignalLength, JBB);
+}
+
+void
+UpgradeStartup::execCNTR_MASTER_REPLY(SimulatedBlock & block, Signal* signal){
+ Uint32 gsn = signal->header.theVerId_signalNumber;
+ Uint32 node = refToNode(signal->getSendersBlockRef());
+ if(block.number() == CNTR){
+ Ndbcntr& cntr = (Ndbcntr&)block;
+ switch(gsn){
+ case GSN_CNTR_MASTERREF:
+ sendCntrMasterReq(cntr, signal, node + 1);
+ return;
+ break;
+ case GSN_CNTR_MASTERCONF:{
+ CntrStartConf* conf = (CntrStartConf*)signal->getDataPtrSend();
+ conf->startGci = 0;
+ conf->masterNodeId = node;
+ conf->noStartNodes = 1;
+ conf->startType = NodeState::ST_INITIAL_NODE_RESTART;
+ NodeBitmask mask;
+ mask.clear();
+ mask.copyto(NdbNodeBitmask::Size, conf->startedNodes);
+ mask.clear();
+ mask.set(cntr.getOwnNodeId());
+ mask.copyto(NdbNodeBitmask::Size, conf->startingNodes);
+ cntr.execCNTR_START_CONF(signal);
+ return;
+ }
+ }
+ }
+ block.progError(0,0);
+}
diff --git a/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrSysTable.cpp b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrSysTable.cpp
new file mode 100644
index 00000000000..2a65271a32a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrSysTable.cpp
@@ -0,0 +1,94 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Ndbcntr.hpp"
+
+#define arrayLength(x) sizeof(x)/sizeof(x[0])
+
+// SYSTAB_0
+
+static const Ndbcntr::SysColumn
+column_SYSTAB_0[] = {
+ { 0, "SYSKEY_0",
+ DictTabInfo::ExtUnsigned, 1,
+ true, false
+ },
+ { 1, "NEXTID",
+ DictTabInfo::ExtBigunsigned, 1,
+ false, false
+ }
+};
+
+const Ndbcntr::SysTable
+Ndbcntr::g_sysTable_SYSTAB_0 = {
+ "sys/def/SYSTAB_0",
+ arrayLength(column_SYSTAB_0), column_SYSTAB_0,
+ DictTabInfo::SystemTable,
+ DictTabInfo::AllNodesSmallTable,
+ true, ~0
+};
+
+// NDB$EVENTS_0
+
+static const Ndbcntr::SysColumn
+column_NDBEVENTS_0[] = {
+ { 0, "NAME",
+ DictTabInfo::ExtBinary, MAX_TAB_NAME_SIZE,
+ true, false
+ },
+ { 1, "EVENT_TYPE",
+ DictTabInfo::ExtUnsigned, 1,
+ false, false
+ },
+ { 2, "TABLE_NAME",
+ DictTabInfo::ExtBinary, MAX_TAB_NAME_SIZE,
+ false, false
+ },
+ { 3, "ATTRIBUTE_MASK",
+ DictTabInfo::ExtUnsigned, MAXNROFATTRIBUTESINWORDS,
+ false, false
+ },
+ { 4, "SUBID",
+ DictTabInfo::ExtUnsigned, 1,
+ false, false
+ },
+ { 5, "SUBKEY",
+ DictTabInfo::ExtUnsigned, 1,
+ false, false
+ }
+};
+
+const Ndbcntr::SysTable
+Ndbcntr::g_sysTable_NDBEVENTS_0 = {
+ "sys/def/NDB$EVENTS_0",
+ arrayLength(column_NDBEVENTS_0), column_NDBEVENTS_0,
+ DictTabInfo::SystemTable,
+ DictTabInfo::AllNodesSmallTable,
+ true, ~0
+};
+
+// all
+
+const Ndbcntr::SysTable*
+Ndbcntr::g_sysTableList[] = {
+ &g_sysTable_SYSTAB_0,
+ &g_sysTable_NDBEVENTS_0
+};
+
+//TODO Backup needs this info to allocate appropriate number of records
+//BackupInit.cpp
+const unsigned
+Ndbcntr::g_sysTableCount = arrayLength(Ndbcntr::g_sysTableList);
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.cpp b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.cpp
new file mode 100644
index 00000000000..f76440a462a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.cpp
@@ -0,0 +1,1033 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_sys.h>
+#include <my_pthread.h>
+
+#include <Error.hpp>
+#include "AsyncFile.hpp"
+
+#include <ErrorHandlingMacros.hpp>
+#include <kernel_types.h>
+#include <NdbMem.h>
+#include <NdbThread.h>
+#include <signaldata/FsOpenReq.hpp>
+
+// use this to test broken pread code
+//#define HAVE_BROKEN_PREAD
+
+#ifdef HAVE_BROKEN_PREAD
+#undef HAVE_PWRITE
+#undef HAVE_PREAD
+#endif
+
+#if defined NDB_WIN32 || defined NDB_OSE || defined NDB_SOFTOSE
+#else
+// For readv and writev
+#include <sys/uio.h>
+#endif
+
+#ifndef NDB_WIN32
+#include <dirent.h>
+#endif
+
+// Use this define if you want printouts from AsyncFile class
+//#define DEBUG_ASYNCFILE
+
+#ifdef DEBUG_ASYNCFILE
+#include <NdbOut.hpp>
+#define DEBUG(x) x
+#define PRINT_ERRORANDFLAGS(f) printErrorAndFlags(f)
+void printErrorAndFlags(Uint32 used_flags);
+#else
+#define DEBUG(x)
+#define PRINT_ERRORANDFLAGS(f)
+#endif
+
+// Define the size of the write buffer (for each thread)
+#if defined NDB_SOFTOSE || defined NDB_OSE
+#define WRITEBUFFERSIZE 65536
+#else
+#define WRITEBUFFERSIZE 262144
+#endif
+
+const char *actionName[] = {
+ "open",
+ "close",
+ "closeRemove",
+ "read",
+ "readv",
+ "write",
+ "writev",
+ "writeSync",
+ "writevSync",
+ "sync",
+ "end" };
+
+static int numAsyncFiles = 0;
+
+extern "C" void * runAsyncFile(void* arg)
+{
+ ((AsyncFile*)arg)->run();
+ return (NULL);
+}
+
+AsyncFile::AsyncFile() :
+ theFileName(),
+#ifdef NDB_WIN32
+ hFile(INVALID_HANDLE_VALUE),
+#else
+ theFd(-1),
+#endif
+ theReportTo(0),
+ theMemoryChannelPtr(NULL)
+{
+}
+
+void
+AsyncFile::doStart(Uint32 nodeId,
+ const char * filesystemPath,
+ const char * backup_path) {
+ theFileName.init(nodeId, filesystemPath, backup_path);
+
+ // Stacksize for filesystem threads
+ // An 8k stack should be enough
+ const NDB_THREAD_STACKSIZE stackSize = 8192;
+
+ char buf[16];
+ numAsyncFiles++;
+ BaseString::snprintf(buf, sizeof(buf), "AsyncFile%d", numAsyncFiles);
+
+ theStartMutexPtr = NdbMutex_Create();
+ theStartConditionPtr = NdbCondition_Create();
+ NdbMutex_Lock(theStartMutexPtr);
+ theStartFlag = false;
+ theThreadPtr = NdbThread_Create(runAsyncFile,
+ (void**)this,
+ stackSize,
+ (char*)&buf,
+ NDB_THREAD_PRIO_MEAN);
+
+ NdbCondition_Wait(theStartConditionPtr,
+ theStartMutexPtr);
+ NdbMutex_Unlock(theStartMutexPtr);
+ NdbMutex_Destroy(theStartMutexPtr);
+ NdbCondition_Destroy(theStartConditionPtr);
+}
+
+AsyncFile::~AsyncFile()
+{
+ void *status;
+ Request request;
+ request.action = Request::end;
+ theMemoryChannelPtr->writeChannel( &request );
+ NdbThread_WaitFor(theThreadPtr, &status);
+ NdbThread_Destroy(&theThreadPtr);
+ delete theMemoryChannelPtr;
+}
+
+void
+AsyncFile::reportTo( MemoryChannel<Request> *reportTo )
+{
+ theReportTo = reportTo;
+}
+
+void AsyncFile::execute(Request* request)
+{
+ theMemoryChannelPtr->writeChannel( request );
+}
+
+void
+AsyncFile::run()
+{
+ Request *request;
+ // Create theMemoryChannel in the thread that will wait for it
+ NdbMutex_Lock(theStartMutexPtr);
+ theMemoryChannelPtr = new MemoryChannel<Request>();
+ theStartFlag = true;
+ // Create write buffer for bigger writes
+ theWriteBufferSize = WRITEBUFFERSIZE;
+ theWriteBuffer = (char *) NdbMem_Allocate(theWriteBufferSize);
+ NdbMutex_Unlock(theStartMutexPtr);
+ NdbCondition_Signal(theStartConditionPtr);
+
+ if (!theWriteBuffer) {
+ DEBUG(ndbout_c("AsyncFile::writeReq, Failed allocating write buffer"));
+ return;
+ }//if
+
+ while (1) {
+ request = theMemoryChannelPtr->readChannel();
+ if (!request) {
+ DEBUG(ndbout_c("Nothing read from Memory Channel in AsyncFile"));
+ endReq();
+ return;
+ }//if
+ switch (request->action) {
+ case Request:: open:
+ openReq(request);
+ break;
+ case Request:: close:
+ closeReq(request);
+ break;
+ case Request:: closeRemove:
+ closeReq(request);
+ removeReq(request);
+ break;
+ case Request:: read:
+ readReq(request);
+ break;
+ case Request:: readv:
+ readvReq(request);
+ break;
+ case Request:: write:
+ writeReq(request);
+ break;
+ case Request:: writev:
+ writevReq(request);
+ break;
+ case Request:: writeSync:
+ writeReq(request);
+ syncReq(request);
+ break;
+ case Request:: writevSync:
+ writevReq(request);
+ syncReq(request);
+ break;
+ case Request:: sync:
+ syncReq(request);
+ break;
+ case Request:: append:
+ appendReq(request);
+ break;
+ case Request::rmrf:
+ rmrfReq(request, (char*)theFileName.c_str(), request->par.rmrf.own_directory);
+ break;
+ case Request:: end:
+ if (theFd > 0)
+ closeReq(request);
+ endReq();
+ return;
+ default:
+ abort();
+ break;
+ }//switch
+ theReportTo->writeChannel(request);
+ }//while
+}//AsyncFile::run()
+
+extern bool Global_useO_SYNC;
+extern bool Global_useO_DIRECT;
+extern bool Global_unlinkO_CREAT;
+extern Uint32 Global_syncFreq;
+
+void AsyncFile::openReq(Request* request)
+{
+ m_openedWithSync = false;
+ m_syncFrequency = 0;
+ m_syncCount= 0;
+
+ // for open.flags, see signal FSOPENREQ
+#ifdef NDB_WIN32
+ DWORD dwCreationDisposition;
+ DWORD dwDesiredAccess = 0;
+ DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_NO_BUFFERING;
+ const Uint32 flags = request->par.open.flags;
+
+ // Convert file open flags from Solaris to Windows
+ if ((flags & FsOpenReq::OM_CREATE) && (flags & FsOpenReq::OM_TRUNCATE)){
+ dwCreationDisposition = CREATE_ALWAYS;
+ } else if (flags & FsOpenReq::OM_TRUNCATE){
+ dwCreationDisposition = TRUNCATE_EXISTING;
+ } else if (flags & FsOpenReq::OM_CREATE){
+ dwCreationDisposition = CREATE_NEW;
+ } else {
+ dwCreationDisposition = OPEN_EXISTING;
+ }
+
+ switch(flags & 3){
+ case FsOpenReq::OM_READONLY:
+ dwDesiredAccess = GENERIC_READ;
+ break;
+ case FsOpenReq::OM_WRITEONLY:
+ dwDesiredAccess = GENERIC_WRITE;
+ break;
+ case FsOpenReq::OM_READWRITE:
+ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ request->error = 1000;
+ break;
+ return;
+ }
+
+ hFile = CreateFile(theFileName.c_str(), dwDesiredAccess, dwShareMode,
+ 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
+
+ if(INVALID_HANDLE_VALUE == hFile) {
+ request->error = GetLastError();
+ if(((ERROR_PATH_NOT_FOUND == request->error) || (ERROR_INVALID_NAME == request->error))
+ && (flags & FsOpenReq::OM_CREATE)) {
+ createDirectories();
+ hFile = CreateFile(theFileName.c_str(), dwDesiredAccess, dwShareMode,
+ 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
+
+ if(INVALID_HANDLE_VALUE == hFile)
+ request->error = GetLastError();
+ else
+ request->error = 0;
+
+ return;
+ }
+ }
+ else {
+ request->error = 0;
+ return;
+ }
+#else
+ const Uint32 flags = request->par.open.flags;
+ Uint32 new_flags = 0;
+
+ // Convert file open flags from Solaris to Liux
+ if(flags & FsOpenReq::OM_CREATE){
+ new_flags |= O_CREAT;
+ }
+
+ if(flags & FsOpenReq::OM_TRUNCATE){
+#if 0
+ if(Global_unlinkO_CREAT){
+ unlink(theFileName.c_str());
+ } else
+#endif
+ new_flags |= O_TRUNC;
+ }
+
+ if(flags & FsOpenReq::OM_APPEND){
+ new_flags |= O_APPEND;
+ }
+
+ if(flags & FsOpenReq::OM_SYNC){
+#if 0
+ if(Global_useO_SYNC){
+ new_flags |= O_SYNC;
+ m_openedWithSync = true;
+ m_syncFrequency = 0;
+ } else {
+#endif
+ m_openedWithSync = false;
+ m_syncFrequency = Global_syncFreq;
+#if 0
+ }
+#endif
+ } else {
+ m_openedWithSync = false;
+ m_syncFrequency = 0;
+ }
+
+#if 0
+ //#if NDB_LINUX
+ if(Global_useO_DIRECT){
+ new_flags |= O_DIRECT;
+ }
+#endif
+
+ switch(flags & 0x3){
+ case FsOpenReq::OM_READONLY:
+ new_flags |= O_RDONLY;
+ break;
+ case FsOpenReq::OM_WRITEONLY:
+ new_flags |= O_WRONLY;
+ break;
+ case FsOpenReq::OM_READWRITE:
+ new_flags |= O_RDWR;
+ break;
+ default:
+ request->error = 1000;
+ break;
+ return;
+ }
+ const int mode = S_IRUSR | S_IWUSR | S_IRGRP;
+
+ if (-1 == (theFd = ::open(theFileName.c_str(), new_flags, mode))) {
+ PRINT_ERRORANDFLAGS(new_flags);
+ if( (errno == ENOENT ) && (new_flags & O_CREAT ) ) {
+ createDirectories();
+ if (-1 == (theFd = ::open(theFileName.c_str(), new_flags, mode))) {
+ PRINT_ERRORANDFLAGS(new_flags);
+ request->error = errno;
+ }
+ } else {
+ request->error = errno;
+ }
+ }
+#endif
+}
+
+int
+AsyncFile::readBuffer(char * buf, size_t size, off_t offset){
+ int return_value;
+
+#ifdef NDB_WIN32
+ DWORD dwSFP = SetFilePointer(hFile, offset, 0, FILE_BEGIN);
+ if(dwSFP != offset) {
+ return GetLastError();
+ }
+#elif ! defined(HAVE_PREAD)
+ off_t seek_val;
+ while((seek_val= lseek(theFd, offset, SEEK_SET)) == (off_t)-1
+ && errno == EINTR);
+ if(seek_val == (off_t)-1)
+ {
+ return errno;
+ }
+#endif
+
+ while (size > 0) {
+ size_t bytes_read = 0;
+
+#ifdef NDB_WIN32
+ DWORD dwBytesRead;
+ BOOL bRead = ReadFile(hFile,
+ buf,
+ size,
+ &dwBytesRead,
+ 0);
+ if(!bRead){
+ return GetLastError();
+ }
+ bytes_read = dwBytesRead;
+#elif ! defined(HAVE_PREAD)
+ return_value = ::read(theFd, buf, size);
+#else // UNIX
+ return_value = ::pread(theFd, buf, size, offset);
+#endif
+#ifndef NDB_WIN32
+ if (return_value == -1 && errno == EINTR) {
+ DEBUG(ndbout_c("EINTR in read"));
+ continue;
+ } else if (return_value == -1){
+ return errno;
+ } else {
+ bytes_read = return_value;
+ }
+#endif
+
+ if(bytes_read == 0){
+ DEBUG(ndbout_c("Read underflow %d %d\n %x\n%d %d",
+ size, offset, buf, bytes_read, return_value));
+ return ERR_ReadUnderflow;
+ }
+
+ if(bytes_read != size){
+ DEBUG(ndbout_c("Warning partial read %d != %d",
+ bytes_read, size));
+ }
+
+ buf += bytes_read;
+ size -= bytes_read;
+ offset += bytes_read;
+ }
+ return 0;
+}
+
+void
+AsyncFile::readReq( Request * request)
+{
+ for(int i = 0; i < request->par.readWrite.numberOfPages ; i++) {
+ off_t offset = request->par.readWrite.pages[i].offset;
+ size_t size = request->par.readWrite.pages[i].size;
+ char * buf = request->par.readWrite.pages[i].buf;
+
+ int err = readBuffer(buf, size, offset);
+ if(err != 0){
+ request->error = err;
+ return;
+ }
+ }
+}
+
+void
+AsyncFile::readvReq( Request * request)
+{
+#if ! defined(HAVE_PREAD)
+ readReq(request);
+ return;
+#elif defined NDB_WIN32
+ // ReadFileScatter?
+ readReq(request);
+ return;
+#else
+ int return_value;
+ int length = 0;
+ struct iovec iov[20]; // the parameter in the signal restricts this to 20 deep
+ for(int i=0; i < request->par.readWrite.numberOfPages ; i++) {
+ iov[i].iov_base= request->par.readWrite.pages[i].buf;
+ iov[i].iov_len= request->par.readWrite.pages[i].size;
+ length = length + iov[i].iov_len;
+ }
+ lseek( theFd, request->par.readWrite.pages[0].offset, SEEK_SET );
+ return_value = ::readv(theFd, iov, request->par.readWrite.numberOfPages);
+ if (return_value == -1) {
+ request->error = errno;
+ return;
+ } else if (return_value != length) {
+ request->error = 1011;
+ return;
+ }
+#endif
+}
+
+int
+AsyncFile::extendfile(Request* request) {
+#if ! defined(HAVE_PWRITE)
+ // Find max size of this file in this request
+ int maxOffset = 0;
+ int maxSize = 0;
+ for(int i=0; i < request->par.readWrite.numberOfPages ; i++) {
+ if (request->par.readWrite.pages[i].offset > maxOffset) {
+ maxOffset = request->par.readWrite.pages[i].offset;
+ maxSize = request->par.readWrite.pages[i].size;
+ }
+ }
+ DEBUG(ndbout_c("extendfile: maxOffset=%d, size=%d", maxOffset, maxSize));
+
+ // Allocate a buffer and fill it with zeros
+ void* pbuf = NdbMem_Allocate(maxSize);
+ memset(pbuf, 0, maxSize);
+ for (int p = 0; p <= maxOffset; p = p + maxSize) {
+ int return_value;
+ return_value = lseek(theFd,
+ p,
+ SEEK_SET);
+ if((return_value == -1 ) || (return_value != p)) {
+ return -1;
+ }
+ return_value = ::write(theFd,
+ pbuf,
+ maxSize);
+ if ((return_value == -1) || (return_value != maxSize)) {
+ return -1;
+ }
+ }
+ free(pbuf);
+
+ DEBUG(ndbout_c("extendfile: \"%s\" OK!", theFileName.c_str()));
+ return 0;
+#else
+ request = request;
+ abort();
+ return -1;
+#endif
+}
+
+void
+AsyncFile::writeReq( Request * request)
+{
+ int page_num = 0;
+ bool write_not_complete = true;
+
+ while(write_not_complete) {
+ int totsize = 0;
+ off_t offset = request->par.readWrite.pages[page_num].offset;
+ char* bufptr = theWriteBuffer;
+
+ write_not_complete = false;
+ if (request->par.readWrite.numberOfPages > 1) {
+ off_t page_offset = offset;
+
+ // Multiple page write, copy to buffer for one write
+ for(int i=page_num; i < request->par.readWrite.numberOfPages; i++) {
+ memcpy(bufptr,
+ request->par.readWrite.pages[i].buf,
+ request->par.readWrite.pages[i].size);
+ bufptr += request->par.readWrite.pages[i].size;
+ totsize += request->par.readWrite.pages[i].size;
+ if (((i + 1) < request->par.readWrite.numberOfPages)) {
+ // There are more pages to write
+ // Check that offsets are consequtive
+ off_t tmp = page_offset + request->par.readWrite.pages[i].size;
+ if (tmp != request->par.readWrite.pages[i+1].offset) {
+ // Next page is not aligned with previous, not allowed
+ DEBUG(ndbout_c("Page offsets are not aligned"));
+ request->error = EINVAL;
+ return;
+ }
+ if ((unsigned)(totsize + request->par.readWrite.pages[i+1].size) > (unsigned)theWriteBufferSize) {
+ // We are not finished and the buffer is full
+ write_not_complete = true;
+ // Start again with next page
+ page_num = i + 1;
+ break;
+ }
+ }
+ page_offset += request->par.readWrite.pages[i].size;
+ }
+ bufptr = theWriteBuffer;
+ } else {
+ // One page write, write page directly
+ bufptr = request->par.readWrite.pages[0].buf;
+ totsize = request->par.readWrite.pages[0].size;
+ }
+ int err = writeBuffer(bufptr, totsize, offset);
+ if(err != 0){
+ request->error = err;
+ return;
+ }
+ } // while(write_not_complete)
+}
+
+int
+AsyncFile::writeBuffer(const char * buf, size_t size, off_t offset,
+ size_t chunk_size)
+{
+ size_t bytes_to_write = chunk_size;
+ int return_value;
+
+#ifdef NDB_WIN32
+ DWORD dwSFP = SetFilePointer(hFile, offset, 0, FILE_BEGIN);
+ if(dwSFP != offset) {
+ return GetLastError();
+ }
+#elif ! defined(HAVE_PWRITE)
+ off_t seek_val;
+ while((seek_val= lseek(theFd, offset, SEEK_SET)) == (off_t)-1
+ && errno == EINTR);
+ if(seek_val == (off_t)-1)
+ {
+ return errno;
+ }
+#endif
+
+ while (size > 0) {
+ if (size < bytes_to_write){
+ // We are at the last chunk
+ bytes_to_write = size;
+ }
+ size_t bytes_written = 0;
+
+#ifdef NDB_WIN32
+ DWORD dwWritten;
+ BOOL bWrite = WriteFile(hFile, buf, bytes_to_write, &dwWritten, 0);
+ if(!bWrite) {
+ return GetLastError();
+ }
+ bytes_written = dwWritten;
+ if (bytes_written != bytes_to_write) {
+ DEBUG(ndbout_c("Warning partial write %d != %d", bytes_written, bytes_to_write));
+ }
+
+#elif ! defined(HAVE_PWRITE)
+ return_value = ::write(theFd, buf, bytes_to_write);
+#else // UNIX
+ return_value = ::pwrite(theFd, buf, bytes_to_write, offset);
+#endif
+#ifndef NDB_WIN32
+ if (return_value == -1 && errno == EINTR) {
+ bytes_written = 0;
+ DEBUG(ndbout_c("EINTR in write"));
+ } else if (return_value == -1){
+ return errno;
+ } else {
+ bytes_written = return_value;
+
+ if(bytes_written == 0){
+ abort();
+ }
+
+ if(bytes_written != bytes_to_write){
+ DEBUG(ndbout_c("Warning partial write %d != %d",
+ bytes_written, bytes_to_write));
+ }
+ }
+#endif
+
+ m_syncCount+= bytes_written;
+ buf += bytes_written;
+ size -= bytes_written;
+ offset += bytes_written;
+ }
+ return 0;
+}
+
+void
+AsyncFile::writevReq( Request * request)
+{
+ // WriteFileGather on WIN32?
+ writeReq(request);
+}
+
+
+void
+AsyncFile::closeReq(Request * request)
+{
+ syncReq(request);
+#ifdef NDB_WIN32
+ if(!CloseHandle(hFile)) {
+ request->error = GetLastError();
+ }
+ hFile = INVALID_HANDLE_VALUE;
+#else
+ if (-1 == ::close(theFd)) {
+#ifndef DBUG_OFF
+ if (theFd == -1)
+ abort();
+#endif
+ request->error = errno;
+ }
+ theFd = -1;
+#endif
+}
+
+bool AsyncFile::isOpen(){
+#ifdef NDB_WIN32
+ return (hFile != INVALID_HANDLE_VALUE);
+#else
+ return (theFd != -1);
+#endif
+}
+
+
+void
+AsyncFile::syncReq(Request * request)
+{
+ if(m_openedWithSync ||
+ m_syncCount == 0){
+ return;
+ }
+#ifdef NDB_WIN32
+ if(!FlushFileBuffers(hFile)) {
+ request->error = GetLastError();
+ return;
+ }
+#else
+ if (-1 == ::fsync(theFd)){
+ request->error = errno;
+ return;
+ }
+#endif
+ m_syncCount = 0;
+}
+
+void
+AsyncFile::appendReq(Request * request){
+
+ const char * buf = request->par.append.buf;
+ Uint32 size = request->par.append.size;
+
+ m_syncCount += size;
+
+#ifdef NDB_WIN32
+ DWORD dwWritten = 0;
+ while(size > 0){
+ if(!WriteFile(hFile, buf, size, &dwWritten, 0)){
+ request->error = GetLastError();
+ return ;
+ }
+
+ buf += dwWritten;
+ size -= dwWritten;
+ }
+#else
+ while(size > 0){
+ const int n = write(theFd, buf, size);
+ if(n == -1 && errno == EINTR){
+ continue;
+ }
+ if(n == -1){
+ request->error = errno;
+ return;
+ }
+ if(n == 0){
+ abort();
+ }
+ size -= n;
+ buf += n;
+ }
+#endif
+
+ if(m_syncFrequency != 0 && m_syncCount > m_syncFrequency){
+ syncReq(request);
+ }
+}
+
+void
+AsyncFile::removeReq(Request * request)
+{
+#ifdef NDB_WIN32
+ if(!DeleteFile(theFileName.c_str())) {
+ request->error = GetLastError();
+ }
+#else
+ if (-1 == ::remove(theFileName.c_str())) {
+ request->error = errno;
+
+ }
+#endif
+}
+
+void
+AsyncFile::rmrfReq(Request * request, char * path, bool removePath){
+ Uint32 path_len = strlen(path);
+ Uint32 path_max_copy = PATH_MAX - path_len;
+ char* path_add = &path[path_len];
+#ifndef NDB_WIN32
+ if(!request->par.rmrf.directory){
+ // Remove file
+ if(unlink((const char *)path) != 0 && errno != ENOENT)
+ request->error = errno;
+ return;
+ }
+ // Remove directory
+ DIR* dirp = opendir((const char *)path);
+ if(dirp == 0){
+ if(errno != ENOENT)
+ request->error = errno;
+ return;
+ }
+ struct dirent * dp;
+ while ((dp = readdir(dirp)) != NULL){
+ if ((strcmp(".", dp->d_name) != 0) && (strcmp("..", dp->d_name) != 0)) {
+ BaseString::snprintf(path_add, (size_t)path_max_copy, "%s%s",
+ DIR_SEPARATOR, dp->d_name);
+ if(remove((const char*)path) == 0){
+ path[path_len] = 0;
+ continue;
+ }
+
+ rmrfReq(request, path, true);
+ path[path_len] = 0;
+ if(request->error != 0){
+ closedir(dirp);
+ return;
+ }
+ }
+ }
+ closedir(dirp);
+ if(removePath && rmdir((const char *)path) != 0){
+ request->error = errno;
+ }
+ return;
+#else
+
+ if(!request->par.rmrf.directory){
+ // Remove file
+ if(!DeleteFile(path)){
+ DWORD dwError = GetLastError();
+ if(dwError!=ERROR_FILE_NOT_FOUND)
+ request->error = dwError;
+ }
+ return;
+ }
+
+ strcat(path, "\\*");
+ WIN32_FIND_DATA ffd;
+ HANDLE hFindFile = FindFirstFile(path, &ffd);
+ path[path_len] = 0;
+ if(INVALID_HANDLE_VALUE==hFindFile){
+ DWORD dwError = GetLastError();
+ if(dwError!=ERROR_PATH_NOT_FOUND)
+ request->error = dwError;
+ return;
+ }
+
+ do {
+ if(0!=strcmp(".", ffd.cFileName) && 0!=strcmp("..", ffd.cFileName)){
+ strcat(path, "\\");
+ strcat(path, ffd.cFileName);
+ if(DeleteFile(path)) {
+ path[path_len] = 0;
+ continue;
+ }//if
+
+ rmrfReq(request, path, true);
+ path[path_len] = 0;
+ if(request->error != 0){
+ FindClose(hFindFile);
+ return;
+ }
+ }
+ } while(FindNextFile(hFindFile, &ffd));
+
+ FindClose(hFindFile);
+
+ if(removePath && !RemoveDirectory(path))
+ request->error = GetLastError();
+
+#endif
+}
+
+void AsyncFile::endReq()
+{
+ // Thread is ended with return
+ if (theWriteBuffer) NdbMem_Free(theWriteBuffer);
+}
+
+
+void AsyncFile::createDirectories()
+{
+ for (int i = 0; i < theFileName.levels(); i++) {
+#ifdef NDB_WIN32
+ CreateDirectory(theFileName.directory(i), 0);
+#else
+ //printf("AsyncFile::createDirectories : \"%s\"\n", theFileName.directory(i));
+ mkdir(theFileName.directory(i), S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IRGRP);
+#endif
+ }
+}
+
+#ifdef DEBUG_ASYNCFILE
+void printErrorAndFlags(Uint32 used_flags) {
+ char buf[255];
+ sprintf(buf, "PEAF: errno=%d \"", errno);
+
+ switch(errno) {
+ case EACCES:
+ strcat(buf, "EACCES");
+ break;
+ case EDQUOT:
+ strcat(buf, "EDQUOT");
+ break;
+ case EEXIST :
+ strcat(buf, "EEXIST");
+ break;
+ case EINTR :
+ strcat(buf, "EINTR");
+ break;
+ case EFAULT :
+ strcat(buf, "EFAULT");
+ break;
+ case EIO :
+ strcat(buf, "EIO");
+ break;
+ case EISDIR :
+ strcat(buf, "EISDIR");
+ break;
+ case ELOOP :
+ strcat(buf, "ELOOP");
+ break;
+ case EMFILE :
+ strcat(buf, "EMFILE");
+ break;
+ case ENFILE :
+ strcat(buf, "ENFILE");
+ break;
+ case ENOENT :
+ strcat(buf, "ENOENT ");
+ break;
+ case ENOSPC :
+ strcat(buf, "ENOSPC");
+ break;
+ case ENOTDIR :
+ strcat(buf, "ENOTDIR");
+ break;
+ case ENXIO :
+ strcat(buf, "ENXIO");
+ break;
+ case EOPNOTSUPP:
+ strcat(buf, "EOPNOTSUPP");
+ break;
+#if !defined NDB_OSE && !defined NDB_SOFTOSE
+ case EMULTIHOP :
+ strcat(buf, "EMULTIHOP");
+ break;
+ case ENOLINK :
+ strcat(buf, "ENOLINK");
+ break;
+ case ENOSR :
+ strcat(buf, "ENOSR");
+ break;
+ case EOVERFLOW :
+ strcat(buf, "EOVERFLOW");
+ break;
+#endif
+ case EROFS :
+ strcat(buf, "EROFS");
+ break;
+ case EAGAIN :
+ strcat(buf, "EAGAIN");
+ break;
+ case EINVAL :
+ strcat(buf, "EINVAL");
+ break;
+ case ENOMEM :
+ strcat(buf, "ENOMEM");
+ break;
+ case ETXTBSY :
+ strcat(buf, "ETXTBSY");
+ break;
+ case ENAMETOOLONG:
+ strcat(buf, "ENAMETOOLONG");
+ break;
+ case EBADF:
+ strcat(buf, "EBADF");
+ break;
+ case ESPIPE:
+ strcat(buf, "ESPIPE");
+ break;
+ case ESTALE:
+ strcat(buf, "ESTALE");
+ break;
+ default:
+ strcat(buf, "EOTHER");
+ break;
+ }
+ strcat(buf, "\" ");
+#if defined NDB_OSE
+ strcat(buf, strerror(errno) << " ");
+#endif
+ strcat(buf, " flags: ");
+ switch(used_flags & 3){
+ case O_RDONLY:
+ strcat(buf, "O_RDONLY, ");
+ break;
+ case O_WRONLY:
+ strcat(buf, "O_WRONLY, ");
+ break;
+ case O_RDWR:
+ strcat(buf, "O_RDWR, ");
+ break;
+ default:
+ strcat(buf, "Unknown!!, ");
+ }
+
+ if((used_flags & O_APPEND)==O_APPEND)
+ strcat(buf, "O_APPEND, ");
+ if((used_flags & O_CREAT)==O_CREAT)
+ strcat(buf, "O_CREAT, ");
+ if((used_flags & O_EXCL)==O_EXCL)
+ strcat(buf, "O_EXCL, ");
+ if((used_flags & O_NOCTTY) == O_NOCTTY)
+ strcat(buf, "O_NOCTTY, ");
+ if((used_flags & O_NONBLOCK)==O_NONBLOCK)
+ strcat(buf, "O_NONBLOCK, ");
+ if((used_flags & O_TRUNC)==O_TRUNC)
+ strcat(buf, "O_TRUNC, ");
+#if !defined NDB_OSE && !defined NDB_SOFTOSE
+ if((used_flags & O_DSYNC)==O_DSYNC)
+ strcat(buf, "O_DSYNC, ");
+ if((used_flags & O_NDELAY)==O_NDELAY)
+ strcat(buf, "O_NDELAY, ");
+ if((used_flags & O_RSYNC)==O_RSYNC)
+ strcat(buf, "O_RSYNC, ");
+ if((used_flags & O_SYNC)==O_SYNC)
+ strcat(buf, "O_SYNC, ");
+ DEBUG(ndbout_c(buf));
+#endif
+
+}
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp
new file mode 100644
index 00000000000..2176c93c5d5
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp
@@ -0,0 +1,234 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 AsyncFile_H
+#define AsyncFile_H
+
+//===========================================================================
+//
+// .DESCRIPTION
+// Asynchronous file, All actions are executed concurrently with other
+// activity of the process.
+// Because all action are performed in a seperated thread the result of
+// of a action is send back tru a memory channel.
+// For the asyncronise notivication of a finished request all the calls
+// have a request as paramater, the user can use the userData pointer
+// to add information it needs when the request is send back.
+//
+//
+// .TYPICAL USE:
+// Writing or reading data to/from disk concurrently to other activities.
+//
+//===========================================================================
+//=============================================================================
+//
+// .PUBLIC
+//
+//=============================================================================
+///////////////////////////////////////////////////////////////////////////////
+//
+// AsyncFile( );
+// Description:
+// Initialisation of the class.
+// Parameters:
+// -
+///////////////////////////////////////////////////////////////////////////////
+//
+// ~AsyncFile( );
+// Description:
+// Tell the thread to stop and wait for it to return
+// Parameters:
+// -
+///////////////////////////////////////////////////////////////////////////////
+//
+// doStart( );
+// Description:
+// Spawns the new thread.
+// Parameters:
+// Base path of filesystem
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// void execute(Request *request);
+// Description:
+// performens the requered action.
+// Parameters:
+// request: request to be called when open is finished.
+// action= open|close|read|write|sync
+// if action is open then:
+// par.open.flags= UNIX open flags, see man open
+// par.open.name= name of the file to open
+// if action is read or write then:
+// par.readWrite.buf= user provided buffer to read/write
+// the data from/to
+// par.readWrite.size= how many bytes must be read/written
+// par.readWrite.offset= absolute offset in file in bytes
+// return:
+// return values are stored in the request error field:
+// error= return state of the action, UNIX error see man open/errno
+// userData= is untouched can be used be user.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// void reportTo( MemoryChannel<Request> *reportTo );
+// Description:
+// set the channel where the file must report the result of the
+// actions back to.
+// Parameters:
+// reportTo: the memory channel to use use MemoryChannelMultipleWriter
+// if more
+// than one file uses this channel to report back.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <kernel_types.h>
+#include "MemoryChannel.hpp"
+#include "Filename.hpp"
+
+const int ERR_ReadUnderflow = 1000;
+
+const int WRITECHUNK = 262144;
+
+class AsyncFile;
+
+class Request
+{
+public:
+ enum Action {
+ open,
+ close,
+ closeRemove,
+ read, // Allways leave readv directly after
+ // read because SimblockAsyncFileSystem depends on it
+ readv,
+ write,// Allways leave writev directly after
+ // write because SimblockAsyncFileSystem depends on it
+ writev,
+ writeSync,// Allways leave writevSync directly after
+ // writeSync because SimblockAsyncFileSystem depends on it
+ writevSync,
+ sync,
+ end,
+ append,
+ rmrf
+ };
+ Action action;
+ union {
+ struct {
+ Uint32 flags;
+ } open;
+ struct {
+ int numberOfPages;
+ struct{
+ char *buf;
+ size_t size;
+ off_t offset;
+ } pages[16];
+ } readWrite;
+ struct {
+ const char * buf;
+ size_t size;
+ } append;
+ struct {
+ bool directory;
+ bool own_directory;
+ } rmrf;
+ } par;
+ int error;
+
+ void set(BlockReference userReference,
+ Uint32 userPointer,
+ Uint16 filePointer);
+ BlockReference theUserReference;
+ Uint32 theUserPointer;
+ Uint16 theFilePointer;
+ // Information for open, needed if the first open action fails.
+ AsyncFile* file;
+ Uint32 theTrace;
+};
+
+
+inline
+void
+Request::set(BlockReference userReference,
+ Uint32 userPointer, Uint16 filePointer)
+{
+ theUserReference= userReference;
+ theUserPointer= userPointer;
+ theFilePointer= filePointer;
+}
+
+class AsyncFile
+{
+public:
+ AsyncFile();
+ ~AsyncFile();
+
+ void reportTo( MemoryChannel<Request> *reportTo );
+
+ void execute( Request* request );
+
+ void doStart(Uint32 nodeId, const char * fspath, const char * backup_path);
+ // its a thread so its always running
+ void run();
+
+ bool isOpen();
+
+ Filename theFileName;
+private:
+
+ void openReq(Request *request);
+ void readReq(Request *request);
+ void readvReq(Request *request);
+ void writeReq(Request *request);
+ void writevReq(Request *request);
+
+ void closeReq(Request *request);
+ void syncReq(Request *request);
+ void removeReq(Request *request);
+ void appendReq(Request *request);
+ void rmrfReq(Request *request, char * path, bool removePath);
+ void endReq();
+
+ int readBuffer(char * buf, size_t size, off_t offset);
+ int writeBuffer(const char * buf, size_t size, off_t offset,
+ size_t chunk_size = WRITECHUNK);
+
+ int extendfile(Request* request);
+ void createDirectories();
+
+#ifdef NDB_WIN32
+ HANDLE hFile;
+#else
+ int theFd;
+#endif
+
+ MemoryChannel<Request> *theReportTo;
+ MemoryChannel<Request>* theMemoryChannelPtr;
+
+ struct NdbThread* theThreadPtr;
+ NdbMutex* theStartMutexPtr;
+ NdbCondition* theStartConditionPtr;
+ bool theStartFlag;
+ int theWriteBufferSize;
+ char* theWriteBuffer;
+
+ bool m_openedWithSync;
+ Uint32 m_syncCount;
+ Uint32 m_syncFrequency;
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/AsyncFileTest.cpp b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/AsyncFileTest.cpp
new file mode 100644
index 00000000000..004752c9543
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/AsyncFileTest.cpp
@@ -0,0 +1,695 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//#define TESTDEBUG 1
+
+#include <ndb_global.h>
+
+#include <kernel_types.h>
+#include <Pool.hpp>
+#include "AsyncFile.hpp"
+#include "NdbOut.hpp"
+#include "NdbTick.h"
+#include "NdbThread.h"
+#include "NdbMain.h"
+
+// Test and benchmark functionality of AsyncFile
+// -n Number of files
+// -r Number of simultaneous requests
+// -s Filesize, number of pages
+// -l Number of iterations
+// -remove, remove files after close
+// -reverse, write files in reverse order, start with the last page
+
+#define MAXFILES 255
+#define DEFAULT_NUM_FILES 1
+#define MAXREQUESTS 256
+#define DEFAULT_NUM_REQUESTS 1
+#define MAXFILESIZE 4096
+#define DEFAULT_FILESIZE 2048
+#define FVERSION 0x01000000
+#define PAGESIZE 8192
+
+#define TIMER_START { Uint64 starttick = NdbTick_CurrentMillisecond()
+#define TIMER_PRINT(str, ops) Uint64 stoptick = NdbTick_CurrentMillisecond();\
+ Uint64 totaltime = (stoptick-starttick); \
+ ndbout << ops << " " << str << \
+ " total time " << (int)totaltime << "ms" << endl;\
+ char buf[255];\
+ sprintf(buf, "%d %s/sec\n",(int)((ops*1000)/totaltime), str);\
+ ndbout <<buf << endl;}
+
+static int numberOfFiles = DEFAULT_NUM_FILES;
+static int numberOfRequests = DEFAULT_NUM_REQUESTS;
+static int fileSize = DEFAULT_FILESIZE;
+static int removeFiles = 0;
+static int writeFilesReverse = 0;
+static int numberOfIterations = 1;
+Uint32 FileNameArray[4];
+
+Pool<AsyncFile>* files;
+AsyncFile* openFiles[MAXFILES];
+Pool<Request>* theRequestPool;
+MemoryChannelMultipleWriter<Request>* theReportChannel;
+
+char WritePages[MAXFILES][PAGESIZE];
+char ReadPages[MAXFILES][PAGESIZE];
+
+int readArguments(int argc, const char** argv);
+int openFile(int fileNum);
+int openFileWait();
+int closeFile(int fileNum);
+int closeFileWait();
+int writeFile( int fileNum, int pagenum);
+int writeFileWait();
+int writeSyncFile( int fileNum, int pagenum);
+int writeSyncFileWait();
+int readFile( int fileNum, int pagenum);
+int readFileWait();
+
+
+NDB_COMMAND(aftest, "aftest", "aftest [-n <Number of files>] [-r <Number of simultaneous requests>] [-s <Filesize, number of pages>] [-l <Number of iterations>] [-remove, remove files after close] [-reverse, write files in reverse order, start with the last page]", "Test the AsyncFile class of Ndb", 8192)
+{
+ int s, numReq, numOps;
+
+ readArguments(argc, argv);
+
+ files = new Pool<AsyncFile>(numberOfFiles, 2);
+ theRequestPool = new Pool<Request>;
+ theReportChannel = new MemoryChannelMultipleWriter<Request>;
+
+ ndbout << "AsyncFileTest starting" << endl;
+ ndbout << " " << numberOfFiles << " files" << endl;
+ ndbout << " " << numberOfRequests << " requests" << endl;
+ ndbout << " " << fileSize << " * 8k files" << endl << endl;
+ ndbout << " " << numberOfIterations << " iterations" << endl << endl;
+
+ NdbThread_SetConcurrencyLevel(numberOfFiles+2);
+
+ // initialize data to write to files
+ for (int i = 0; i < MAXFILES; i++) {
+ for (int j = 0; j < PAGESIZE; j++){
+ WritePages[i][j] = (64+i+j)%256;
+ }
+ // memset(&WritePages[i][0], i+64, PAGESIZE);
+ }
+
+ // Set file directory and name
+ // /T27/F27/NDBFS/S27Pnn.data
+ FileNameArray[0] = 27; // T27
+ FileNameArray[1] = 27; // F27
+ FileNameArray[2] = 27; // S27
+ FileNameArray[3] = FVERSION; // Version
+
+ for (int l = 0; l < numberOfIterations; l++)
+ {
+
+ ndbout << "Opening files" << endl;
+ // Open files
+ for (int f = 0; f < numberOfFiles; f++)
+ {
+ openFile(f);
+
+ }
+
+ // Wait for answer
+ openFileWait();
+
+ ndbout << "Files opened!" << endl<< endl;
+
+ // Write to files
+ ndbout << "Started writing" << endl;
+ TIMER_START;
+ s = 0;
+ numReq = 0;
+ numOps = 0;
+ while ( s < fileSize)
+ {
+ for (int r = 0; r < numberOfRequests; r++)
+ {
+ for (int f = 0; f < numberOfFiles; f++)
+ {
+ writeFile(f, s);
+ numReq++;
+ numOps++;
+ }
+
+ s++;
+ }
+
+ while (numReq > 0)
+ {
+ writeFileWait();
+ numReq--;
+ }
+
+ }
+
+ TIMER_PRINT("writes", numOps);
+
+
+ ndbout << "Started reading" << endl;
+ TIMER_START;
+
+ // Read from files
+ s = 0;
+ numReq = 0;
+ numOps = 0;
+ while ( s < fileSize)
+ {
+ for (int r = 0; r < numberOfRequests; r++)
+ {
+ for (int f = 0; f < numberOfFiles; f++)
+ {
+ readFile(f, s);
+ numReq++;
+ numOps++;
+ }
+
+ s++;
+
+ }
+
+ while (numReq > 0)
+ {
+ readFileWait();
+ numReq--;
+ }
+
+ }
+ TIMER_PRINT("reads", numOps);
+
+ ndbout << "Started writing with sync" << endl;
+ TIMER_START;
+
+ // Write to files
+ s = 0;
+ numReq = 0;
+ numOps = 0;
+ while ( s < fileSize)
+ {
+ for (int r = 0; r < numberOfRequests; r++)
+ {
+ for (int f = 0; f < numberOfFiles; f++)
+ {
+ writeSyncFile(f, s);
+ numReq++;
+ numOps++;
+ }
+
+ s++;
+ }
+
+ while (numReq > 0)
+ {
+ writeSyncFileWait();
+ numReq--;
+ }
+
+ }
+
+ TIMER_PRINT("writeSync", numOps);
+
+ // Close files
+ ndbout << "Closing files" << endl;
+ for (int f = 0; f < numberOfFiles; f++)
+ {
+ closeFile(f);
+
+ }
+
+ // Wait for answer
+ closeFileWait();
+
+ ndbout << "Files closed!" << endl<< endl;
+ }
+
+ // Deallocate memory
+ delete files;
+ delete theReportChannel;
+ delete theRequestPool;
+
+ return 0;
+
+}
+
+
+
+int forward( AsyncFile * file, Request* request )
+{
+ file->execute(request);
+ ERROR_CHECK 0;
+ return 1;
+}
+
+int openFile( int fileNum)
+{
+ AsyncFile* file = (AsyncFile *)files->get();
+
+ FileNameArray[3] = fileNum | FVERSION;
+ file->fileName().set( NDBFS_REF, &FileNameArray[0] );
+ ndbout << "openFile: " << file->fileName().c_str() << endl;
+
+ if( ERROR_STATE ) {
+ ERROR_RESET;
+ files->put( file );
+ ndbout << "Failed to set filename" << endl;
+ return 1;
+ }
+ file->reportTo(theReportChannel);
+
+ Request* request = theRequestPool->get();
+ request->action= Request::open;
+ request->error= 0;
+ request->par.open.flags = 0x302; //O_RDWR | O_CREAT | O_TRUNC ; // 770
+ request->set(NDBFS_REF, 0x23456789, fileNum );
+ request->file = file;
+
+ if (!forward(file,request)) {
+ // Something went wrong
+ ndbout << "Could not forward open request" << endl;
+ theRequestPool->put(request);
+ return 1;
+ }
+ return 0;
+}
+
+int closeFile( int fileNum)
+{
+
+ AsyncFile* file = openFiles[fileNum];
+
+ Request* request = theRequestPool->get();
+ if (removeFiles == 1)
+ request->action = Request::closeRemove;
+ else
+ request->action= Request::close;
+
+ request->error= 0;
+ request->set(NDBFS_REF, 0x23456789, fileNum );
+ request->file = file;
+
+ if (!forward(file,request)) {
+ // Something went wrong
+ ndbout << "Could not forward close request" << endl;
+ theRequestPool->put(request);
+ return 1;
+ }
+ return 0;
+}
+
+int writeFile( int fileNum, int pagenum)
+{
+ AsyncFile* file = openFiles[fileNum];
+#ifdef TESTDEBUG
+ ndbout << "writeFile" << fileNum <<": "<<pagenum<<", " << file->fileName().c_str()<< endl;
+#endif
+ Request *request = theRequestPool->get();
+ request->action = Request::write;
+ request->error = 0;
+ request->set(NDBFS_REF, pagenum, fileNum);
+ request->file = openFiles[fileNum];
+
+ // Write only one page, choose the correct page for each file using fileNum
+ request->par.readWrite.pages[0].buf = &WritePages[fileNum][0];
+ request->par.readWrite.pages[0].size = PAGESIZE;
+ if (writeFilesReverse == 1)
+ {
+ // write the last page in the files first
+ // This is a normal way for the Blocks in Ndb to write to a file
+ request->par.readWrite.pages[0].offset = (fileSize - pagenum - 1) * PAGESIZE;
+ }
+ else
+ {
+ request->par.readWrite.pages[0].offset = pagenum * PAGESIZE;
+ }
+ request->par.readWrite.numberOfPages = 1;
+
+ if (!forward(file,request)) {
+ // Something went wrong
+ ndbout << "Could not forward write request" << endl;
+ theRequestPool->put(request);
+ return 1;
+ }
+ return 0;
+
+}
+
+int writeSyncFile( int fileNum, int pagenum)
+{
+ AsyncFile* file = openFiles[fileNum];
+#ifdef TESTDEBUG
+ ndbout << "writeFile" << fileNum <<": "<<pagenum<<", " << file->fileName().c_str() << endl;
+#endif
+ Request *request = theRequestPool->get();
+ request->action = Request::writeSync;
+ request->error = 0;
+ request->set(NDBFS_REF, pagenum, fileNum);
+ request->file = openFiles[fileNum];
+
+ // Write only one page, choose the correct page for each file using fileNum
+ request->par.readWrite.pages[0].buf = &WritePages[fileNum][0];
+ request->par.readWrite.pages[0].size = PAGESIZE;
+ request->par.readWrite.pages[0].offset = pagenum * PAGESIZE;
+ request->par.readWrite.numberOfPages = 1;
+
+ if (!forward(file,request)) {
+ // Something went wrong
+ ndbout << "Could not forward write request" << endl;
+ theRequestPool->put(request);
+ return 1;
+ }
+ return 0;
+
+}
+
+int readFile( int fileNum, int pagenum)
+{
+ AsyncFile* file = openFiles[fileNum];
+#ifdef TESTDEBUG
+ ndbout << "readFile" << fileNum <<": "<<pagenum<<", " << file->fileName().c_str() << endl;
+#endif
+ Request *request = theRequestPool->get();
+ request->action = Request::read;
+ request->error = 0;
+ request->set(NDBFS_REF, pagenum, fileNum);
+ request->file = openFiles[fileNum];
+
+ // Read only one page, choose the correct page for each file using fileNum
+ request->par.readWrite.pages[0].buf = &ReadPages[fileNum][0];
+ request->par.readWrite.pages[0].size = PAGESIZE;
+ request->par.readWrite.pages[0].offset = pagenum * PAGESIZE;
+ request->par.readWrite.numberOfPages = 1;
+
+ if (!forward(file,request)) {
+ // Something went wrong
+ ndbout << "Could not forward read request" << endl;
+ theRequestPool->put(request);
+ return 1;
+ }
+ return 0;
+
+}
+
+int openFileWait()
+{
+ int openedFiles = 0;
+ while (openedFiles < numberOfFiles)
+ {
+ Request* request = theReportChannel->readChannel();
+ if (request)
+ {
+ if (request->action == Request::open)
+ {
+ if (request->error ==0)
+ {
+#ifdef TESTDEBUG
+ ndbout << "Opened file " << request->file->fileName().c_str() << endl;
+#endif
+ openFiles[request->theFilePointer] = request->file;
+ }
+ else
+ {
+ ndbout << "error while opening file" << endl;
+ exit(1);
+ }
+ theRequestPool->put(request);
+ openedFiles++;
+ }
+ else
+ {
+ ndbout << "Unexpected request received" << endl;
+ }
+ }
+ else
+ {
+ ndbout << "Nothing read from theReportChannel" << endl;
+ }
+ }
+ return 0;
+}
+
+int closeFileWait()
+{
+ int closedFiles = 0;
+ while (closedFiles < numberOfFiles)
+ {
+ Request* request = theReportChannel->readChannel();
+ if (request)
+ {
+ if (request->action == Request::close || request->action == Request::closeRemove)
+ {
+ if (request->error ==0)
+ {
+#ifdef TESTDEBUG
+ ndbout << "Closed file " << request->file->fileName().c_str() << endl;
+#endif
+ openFiles[request->theFilePointer] = NULL;
+ files->put(request->file);
+ }
+ else
+ {
+ ndbout << "error while closing file" << endl;
+ exit(1);
+ }
+ theRequestPool->put(request);
+ closedFiles++;
+ }
+ else
+ {
+ ndbout << "Unexpected request received" << endl;
+ }
+ }
+ else
+ {
+ ndbout << "Nothing read from theReportChannel" << endl;
+ }
+ }
+ return 0;
+}
+
+int writeFileWait()
+{
+ Request* request = theReportChannel->readChannel();
+ if (request)
+ {
+ if (request->action == Request::write)
+ {
+ if (request->error == 0)
+ {
+#ifdef TESTDEBUG
+ ndbout << "writeFileWait"<<request->theFilePointer<<", " << request->theUserPointer<<" "<< request->file->fileName().c_str() << endl;
+#endif
+
+ }
+ else
+ {
+ ndbout << "error while writing file, error=" << request->error << endl;
+ exit(1);
+ }
+ theRequestPool->put(request);
+ }
+ else
+ {
+ ndbout << "Unexpected request received" << endl;
+ }
+ }
+ else
+ {
+ ndbout << "Nothing read from theReportChannel" << endl;
+ }
+ return 0;
+}
+
+int writeSyncFileWait()
+{
+ Request* request = theReportChannel->readChannel();
+ if (request)
+ {
+ if (request->action == Request::writeSync)
+ {
+ if (request->error == 0)
+ {
+#ifdef TESTDEBUG
+ ndbout << "writeFileWait"<<request->theFilePointer<<", " << request->theUserPointer<<" "<< request->file->fileName().c_str() << endl;
+#endif
+
+ }
+ else
+ {
+ ndbout << "error while writing file" << endl;
+ exit(1);
+ }
+ theRequestPool->put(request);
+ }
+ else
+ {
+ ndbout << "Unexpected request received" << endl;
+ }
+ }
+ else
+ {
+ ndbout << "Nothing read from theReportChannel" << endl;
+ }
+ return 0;
+}
+
+int readFileWait()
+{
+ Request* request = theReportChannel->readChannel();
+ if (request)
+ {
+ if (request->action == Request::read)
+ {
+ if (request->error == 0)
+ {
+#ifdef TESTDEBUG
+ ndbout << "readFileWait"<<request->theFilePointer<<", " << request->theUserPointer<<" "<< request->file->fileName().c_str() << endl;
+#endif
+ if (memcmp(&(ReadPages[request->theFilePointer][0]), &(WritePages[request->theFilePointer][0]), PAGESIZE)!=0)
+ {
+ ndbout <<"Verification error!" << endl;
+ for (int i = 0; i < PAGESIZE; i++ ){
+ ndbout <<" Compare Page " << i << " : " << ReadPages[request->theFilePointer][i] <<", " <<WritePages[request->theFilePointer][i] << endl;;
+ if( ReadPages[request->theFilePointer][i] !=WritePages[request->theFilePointer][i])
+
+ exit(1);
+ }
+ }
+
+ }
+ else
+ {
+ ndbout << "error while reading file" << endl;
+ exit(1);
+ }
+ theRequestPool->put(request);
+ }
+ else
+ {
+ ndbout << "Unexpected request received" << endl;
+ }
+ }
+ else
+ {
+ ndbout << "Nothing read from theReportChannel" << endl;
+ }
+ return 0;
+}
+
+int readArguments(int argc, const char** argv)
+{
+
+ int i = 1;
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-n") == 0)
+ {
+ numberOfFiles = atoi(argv[i+1]);
+ if ((numberOfFiles < 1) || (numberOfFiles > MAXFILES))
+ {
+ ndbout << "Wrong number of files, default = "<<DEFAULT_NUM_FILES << endl;
+ numberOfFiles = DEFAULT_NUM_FILES;
+ }
+ }
+ else if (strcmp(argv[i], "-r") == 0)
+ {
+ numberOfRequests = atoi(argv[i+1]);
+ if ((numberOfRequests < 1) || (numberOfRequests > MAXREQUESTS))
+ {
+ ndbout << "Wrong number of requests, default = "<<DEFAULT_NUM_REQUESTS << endl;
+ numberOfRequests = DEFAULT_NUM_REQUESTS;
+ }
+ }
+ else if (strcmp(argv[i], "-s") == 0)
+ {
+ fileSize = atoi(argv[i+1]);
+ if ((fileSize < 1) || (fileSize > MAXFILESIZE))
+ {
+ ndbout << "Wrong number of 8k pages, default = "<<DEFAULT_FILESIZE << endl;
+ fileSize = DEFAULT_FILESIZE;
+ }
+ }
+ else if (strcmp(argv[i], "-l") == 0)
+ {
+ numberOfIterations = atoi(argv[i+1]);
+ if ((numberOfIterations < 1))
+ {
+ ndbout << "Wrong number of iterations, default = 1" << endl;
+ numberOfIterations = 1;
+ }
+ }
+ else if (strcmp(argv[i], "-remove") == 0)
+ {
+ removeFiles = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-reverse") == 0)
+ {
+ ndbout << "Writing files reversed" << endl;
+ writeFilesReverse = 1;
+ argc++;
+ i--;
+ }
+
+ argc -= 2;
+ i = i + 2;
+ }
+
+ if ((fileSize % numberOfRequests)!= 0)
+ {
+ numberOfRequests = numberOfRequests - (fileSize % numberOfRequests);
+ ndbout <<"numberOfRequest must be modulo of filesize" << endl;
+ ndbout << "New numberOfRequest="<<numberOfRequests<<endl;
+ }
+ return 0;
+}
+
+
+// Needed for linking...
+
+void ErrorReporter::handleError(ErrorCategory type, int messageID,
+ const char* problemData, const char* objRef, NdbShutdownType stype)
+{
+
+ ndbout << "ErrorReporter::handleError activated" << endl;
+ ndbout << "type= " << type << endl;
+ ndbout << "messageID= " << messageID << endl;
+ ndbout << "problemData= " << problemData << endl;
+ ndbout << "objRef= " << objRef << endl;
+
+ exit(1);
+}
+
+void ErrorReporter::handleAssert(const char* message, const char* file, int line)
+{
+ ndbout << "ErrorReporter::handleAssert activated" << endl;
+ ndbout << "message= " << message << endl;
+ ndbout << "file= " << file << endl;
+ ndbout << "line= " << line << endl;
+ exit(1);
+}
+
+
+GlobalData globalData;
+
+
+Signal::Signal()
+{
+
+}
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/Makefile b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/Makefile
new file mode 100644
index 00000000000..b0356e6da68
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/Makefile
@@ -0,0 +1,27 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := aftest
+BIN_TARGET_ARCHIVES := ndbfs portlib trace signaldataprint
+
+SOURCES = AsyncFileTest.cpp
+
+CFLAGS_AsyncFileTest.cpp = -I../
+
+include $(NDB_TOP)/Epilogue.mk
+
+
+# run basic tests
+run_test :
+ $(NDB_TOP)/bin/$(BIN_TARGET)
+ $(NDB_TOP)/bin/$(BIN_TARGET) -n 8 -r 8 -l 10 -remove
+ $(NDB_TOP)/bin/$(BIN_TARGET) -n 8 -r 8 -l 10 -reverse -remove
+ $(NDB_TOP)/bin/$(BIN_TARGET) -n 8 -r 8 -l 10 -s 512 -remove
+ $(NDB_TOP)/bin/$(BIN_TARGET) -n 8 -r 4 -l 1000
+
+
+
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.cpp b/storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.cpp
new file mode 100644
index 00000000000..30b40097c9b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.cpp
@@ -0,0 +1,20 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "CircularIndex.hpp"
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.hpp b/storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.hpp
new file mode 100644
index 00000000000..349cccdbcb4
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.hpp
@@ -0,0 +1,116 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CircularIndex_H
+#define CircularIndex_H
+
+//===========================================================================
+//
+// .DESCRIPTION
+// Building block for circular buffers. It increment as a normal index.
+// untill it it becomes the maximum size then it becomes zero.
+//
+// .TYPICAL USE:
+// to implement a circular buffer.
+//
+// .EXAMPLE:
+// See MemoryChannel.C
+//===========================================================================
+
+///////////////////////////////////////////////////////////////////////////////
+// CircularIndex( int start= 0,int size=256 );
+// Constuctor
+// Parameters:
+// start: where to start to index
+// size : range of the index, will be from 0 to size-1
+///////////////////////////////////////////////////////////////////////////////
+// operator int ();
+// returns the index
+///////////////////////////////////////////////////////////////////////////////
+// void operator ++ ();
+// increments the index with one, of size is reached it is set to zero
+///////////////////////////////////////////////////////////////////////////////
+// friend int full( const CircularIndex& write, const CircularIndex& read );
+// Taken the write index and the read index from a buffer it is calculated
+// if the buffer is full
+// Parameters:
+// write: index used a write index for the buffer
+// read : index used a read index for the buffer
+// return
+// 0 : not full
+// 1 : full
+///////////////////////////////////////////////////////////////////////////////
+// friend int empty( const CircularIndex& write, const CircularIndex& read );
+// Taken the write index and the read index from a buffer it is calculated
+// if the buffer is empty
+// Parameters:
+// write: index used a write index for the buffer
+// read : index used a read index for the buffer
+// return
+// 0 : not empty
+// 1 : empty
+///////////////////////////////////////////////////////////////////////////////
+
+class CircularIndex
+{
+public:
+ inline CircularIndex( int start= 0,int size=256 );
+ operator int ();
+ CircularIndex& operator ++ ();
+ friend int full( const CircularIndex& write, const CircularIndex& read );
+ friend int empty( const CircularIndex& write, const CircularIndex& read );
+private:
+ int theSize;
+ int theIndex;
+};
+
+inline CircularIndex::operator int ()
+{
+ return theIndex;
+}
+
+inline CircularIndex& CircularIndex::operator ++ ()
+{
+ ++theIndex;
+ if( theIndex >= theSize ){
+ theIndex= 0;
+ }
+ return *this;
+}
+
+
+inline int full( const CircularIndex& write, const CircularIndex& read )
+{
+ int readTmp= read.theIndex;
+
+ if( read.theIndex < write.theIndex )
+ readTmp += read.theSize;
+
+ return ( readTmp - write.theIndex) == 1;
+}
+
+inline int empty( const CircularIndex& write, const CircularIndex& read )
+{
+ return read.theIndex == write.theIndex;
+}
+
+
+inline CircularIndex::CircularIndex( int start,int size ):
+ theSize(size),
+ theIndex(start)
+{
+}
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/Filename.cpp b/storage/ndb/src/kernel/blocks/ndbfs/Filename.cpp
new file mode 100644
index 00000000000..15158ec19ef
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/Filename.cpp
@@ -0,0 +1,219 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include "Filename.hpp"
+#include "ErrorHandlingMacros.hpp"
+#include "Error.hpp"
+#include "RefConvert.hpp"
+#include "DebuggerNames.hpp"
+
+#include <signaldata/FsOpenReq.hpp>
+
+static const char* fileExtension[] = {
+ ".Data",
+ ".FragLog",
+ ".LocLog",
+ ".FragList",
+ ".TableList",
+ ".SchemaLog",
+ ".sysfile",
+ ".log",
+ ".ctl"
+};
+
+static const Uint32 noOfExtensions = sizeof(fileExtension)/sizeof(char*);
+
+Filename::Filename() :
+ theLevelDepth(0)
+{
+}
+
+void
+Filename::init(Uint32 nodeid,
+ const char * pFileSystemPath,
+ const char * pBackupDirPath){
+ DBUG_ENTER("Filename::init");
+
+ if (pFileSystemPath == NULL) {
+ ERROR_SET(fatal, AFS_ERROR_NOPATH, ""," Filename::init()");
+ return;
+ }
+
+ BaseString::snprintf(theFileSystemDirectory, sizeof(theFileSystemDirectory),
+ "%sndb_%u_fs%s", pFileSystemPath, nodeid, DIR_SEPARATOR);
+ strncpy(theBackupDirectory, pBackupDirPath, sizeof(theBackupDirectory));
+
+ DBUG_PRINT("info", ("theFileSystemDirectory=%s", theFileSystemDirectory));
+ DBUG_PRINT("info", ("theBackupDirectory=%s", theBackupDirectory));
+
+#ifdef NDB_WIN32
+ CreateDirectory(theFileSystemDirectory, 0);
+#else
+ mkdir(theFileSystemDirectory, S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IRGRP);
+#endif
+ theBaseDirectory= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+Filename::~Filename(){
+}
+
+void
+Filename::set(BlockReference blockReference,
+ const Uint32 filenumber[4], bool dir)
+{
+ char buf[PATH_MAX];
+ theLevelDepth = 0;
+
+ const Uint32 type = FsOpenReq::getSuffix(filenumber);
+ const Uint32 version = FsOpenReq::getVersion(filenumber);
+
+ if (version == 2)
+ theBaseDirectory= theBackupDirectory;
+ else
+ theBaseDirectory= theFileSystemDirectory;
+ strncpy(theName, theBaseDirectory, PATH_MAX);
+
+ switch(version){
+ case 1 :{
+ const Uint32 diskNo = FsOpenReq::v1_getDisk(filenumber);
+ const Uint32 table = FsOpenReq::v1_getTable(filenumber);
+ const Uint32 frag = FsOpenReq::v1_getFragment(filenumber);
+ const Uint32 S_val = FsOpenReq::v1_getS(filenumber);
+ const Uint32 P_val = FsOpenReq::v1_getP(filenumber);
+
+ if (diskNo < 0xff){
+ BaseString::snprintf(buf, sizeof(buf), "D%d%s", diskNo, DIR_SEPARATOR);
+ strcat(theName, buf);
+ theLevelDepth++;
+ }
+
+ {
+ const char* blockName = getBlockName( refToBlock(blockReference) );
+ if (blockName == NULL){
+ ERROR_SET(ecError, AFS_ERROR_PARAMETER,"","No Block Name");
+ return;
+ }
+ BaseString::snprintf(buf, sizeof(buf), "%s%s", blockName, DIR_SEPARATOR);
+ strcat(theName, buf);
+ theLevelDepth++;
+ }
+
+ if (table < 0xffffffff){
+ BaseString::snprintf(buf, sizeof(buf), "T%d%s", table, DIR_SEPARATOR);
+ strcat(theName, buf);
+ theLevelDepth++;
+ }
+
+ if (frag < 0xffffffff){
+ BaseString::snprintf(buf, sizeof(buf), "F%d%s", frag, DIR_SEPARATOR);
+ strcat(theName, buf);
+ theLevelDepth++;
+ }
+
+
+ if (S_val < 0xffffffff){
+ BaseString::snprintf(buf, sizeof(buf), "S%d", S_val);
+ strcat(theName, buf);
+ }
+
+ if (P_val < 0xff){
+ BaseString::snprintf(buf, sizeof(buf), "P%d", P_val);
+ strcat(theName, buf);
+ }
+
+ }
+ break;
+ case 2:{
+ const Uint32 seq = FsOpenReq::v2_getSequence(filenumber);
+ const Uint32 nodeId = FsOpenReq::v2_getNodeId(filenumber);
+ const Uint32 count = FsOpenReq::v2_getCount(filenumber);
+
+ BaseString::snprintf(buf, sizeof(buf), "BACKUP%sBACKUP-%d%s",
+ DIR_SEPARATOR, seq, DIR_SEPARATOR);
+ strcat(theName, buf);
+ if(count == 0xffffffff) {
+ BaseString::snprintf(buf, sizeof(buf), "BACKUP-%d.%d",
+ seq, nodeId); strcat(theName, buf);
+ } else {
+ BaseString::snprintf(buf, sizeof(buf), "BACKUP-%d-%d.%d",
+ seq, count, nodeId); strcat(theName, buf);
+ }
+ theLevelDepth = 2;
+ break;
+ }
+ break;
+ case 3:{
+ const Uint32 diskNo = FsOpenReq::v1_getDisk(filenumber);
+
+ if(diskNo == 0xFF){
+ ERROR_SET(ecError, AFS_ERROR_PARAMETER,"","Invalid disk specification");
+ }
+
+ BaseString::snprintf(buf, sizeof(buf), "D%d%s", diskNo, DIR_SEPARATOR);
+ strcat(theName, buf);
+ theLevelDepth++;
+ }
+ break;
+ default:
+ ERROR_SET(ecError, AFS_ERROR_PARAMETER,"","Wrong version");
+ }
+ if (type >= noOfExtensions){
+ ERROR_SET(ecError, AFS_ERROR_PARAMETER,"","File Type doesn't exist");
+ return;
+ }
+ strcat(theName, fileExtension[type]);
+
+ if(dir == true){
+ for(int l = strlen(theName) - 1; l >= 0; l--){
+ if(theName[l] == DIR_SEPARATOR[0]){
+ theName[l] = 0;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Find out directory name on level
+ * Ex:
+ * theName = "/tmp/fs/T0/NDBFS/D0/P0/S27.data"
+ * level = 1
+ * would return "/tmp/fs/T0/NDBFS/
+ */
+const char* Filename::directory(int level)
+{
+ const char* p;
+
+ p = theName;
+ p += strlen(theBaseDirectory);
+
+ for (int i = 0; i <= level; i++){
+ p = strstr(p, DIR_SEPARATOR);
+ p++;
+ }
+
+ strncpy(theDirectory, theName, p - theName - 1);
+ theDirectory[p-theName-1] = 0;
+ return theDirectory;
+}
+
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/Filename.hpp b/storage/ndb/src/kernel/blocks/ndbfs/Filename.hpp
new file mode 100644
index 00000000000..249c1b1ca10
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/Filename.hpp
@@ -0,0 +1,100 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 Filename_H
+#define Filename_H
+
+//===========================================================================
+//
+// .DESCRIPTION
+// Takes a 128 bits value (done as a array of four longs) and
+// makes a filename out of it acording the following schema
+// Bits 0-31 T
+// Bits 32-63 F
+// Bits 64-95 S
+// Bits 96-103 P
+// Bits 104-111 D
+// Bits 112-119 File Type
+// Bits 120-127 Version number of Filename
+//
+// T, is used to find/create a directory. If T = 0xFFFF then the
+// file is on top level. In that case the F is of no relevance.
+// F, same as T.
+// S, is used to find/create a filename. If S= 0xFFFF then it is ignored.
+// P, same as S
+// D, is used to find/create the root directory, this is the
+// directory before the blockname. If D= 0xFF then it is ignored.
+// File Type
+// 0 => .Data
+// 1 => .FragLog
+// 2 => .LocLog
+// 3 => .FragList
+// 4 => .TableList
+// 5 => .SchemaLog
+// 6 => .sysfile
+// 15=> ignored
+// Version number of Filename, current version is 0x1, must be
+// used for the this style of options.
+//
+//
+//===========================================================================
+
+#include <ndb_global.h>
+#include <kernel_types.h>
+
+class Filename
+{
+public:
+ // filenumber is 64 bits but is split in to 4 32bits words
+ Filename();
+ ~Filename();
+ void set(BlockReference blockReference,
+ const Uint32 filenumber[4], bool dir = false);
+ const char* baseDirectory() const;
+ const char* directory(int level);
+ int levels() const;
+ const char* c_str() const;
+
+ void init(Uint32 nodeid, const char * fileSystemPath,
+ const char * backupDirPath);
+
+private:
+ int theLevelDepth;
+ char theName[PATH_MAX];
+ char theFileSystemDirectory[PATH_MAX];
+ char theBackupDirectory[PATH_MAX];
+ char *theBaseDirectory;
+ char theDirectory[PATH_MAX];
+};
+
+// inline methods
+inline const char* Filename::c_str() const{
+ return theName;
+}
+
+inline const char* Filename::baseDirectory() const{
+ return theBaseDirectory;
+}
+
+inline int Filename::levels() const{
+ return theLevelDepth;
+}
+
+#endif
+
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/Makefile.am b/storage/ndb/src/kernel/blocks/ndbfs/Makefile.am
new file mode 100644
index 00000000000..a22386f8612
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/Makefile.am
@@ -0,0 +1,27 @@
+noinst_LIBRARIES = libndbfs.a
+
+libndbfs_a_SOURCES = \
+ AsyncFile.cpp \
+ Ndbfs.cpp VoidFs.cpp \
+ Filename.cpp \
+ CircularIndex.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libndbfs.dsp
+
+libndbfs.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libndbfs_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.cpp b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.cpp
new file mode 100644
index 00000000000..a1aebdef7a1
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.cpp
@@ -0,0 +1,18 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//#include "MemoryChannel.hpp"
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp
new file mode 100644
index 00000000000..03911d195ec
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp
@@ -0,0 +1,166 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 MemoryChannel_H
+#define MemoryChannel_H
+
+//===========================================================================
+//
+// .DESCRIPTION
+// Pointer based communication channel for communication between two
+// thread. It does not copy any data in or out the channel so the
+// item that is put in can not be used untill the other thread has
+// given it back. There is no support for detecting the return of a
+// item. The channel is half-duplex.
+// For comminication between 1 writer and 1 reader use the MemoryChannel
+// class, for comminication between multiple writer and 1 reader use the
+// MemoryChannelMultipleWriter. There is no support for multiple readers.
+//
+// .TYPICAL USE:
+// to communicate between threads.
+//
+// .EXAMPLE:
+// See AsyncFile.C
+//===========================================================================
+//
+//
+// MemoryChannel( int size= 256);
+// Constuctor
+// Parameters:
+// size : amount of pointer it can hold
+//
+// void operator ++ ();
+// increments the index with one, if size is reached it is set to zero
+//
+// virtual void write( T *t);
+// Puts the item in the channel if the channel is full an error is reported.
+// Parameters:
+// t: pointer to item to put in the channel, after this the item
+// is shared with the other thread.
+// errors
+// AFS_ERROR_CHANNALFULL, channel is full
+//
+// T* read();
+// Reads a itemn from the channel, if channel is empty it blocks untill
+// an item can be read.
+// return
+// T : item from the channel
+//
+// T* tryRead();
+// Reads a item from the channel, if channel is empty it returns zero.
+// return
+// T : item from the channel or zero if channel is empty.
+//
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+#include "MemoryChannelOSE.hpp"
+#else
+
+#include "ErrorHandlingMacros.hpp"
+#include "Error.hpp"
+#include "CircularIndex.hpp"
+#include "NdbMutex.h"
+#include "NdbCondition.h"
+#include <NdbOut.hpp>
+
+
+template <class T>
+class MemoryChannel
+{
+public:
+ MemoryChannel( int size= 256);
+ virtual ~MemoryChannel( );
+
+ virtual void writeChannel( T *t);
+ T* readChannel();
+ T* tryReadChannel();
+
+private:
+ int theSize;
+ T **theChannel;
+ CircularIndex theWriteIndex;
+ CircularIndex theReadIndex;
+ NdbMutex* theMutexPtr;
+ NdbCondition* theConditionPtr;
+
+};
+
+
+template <class T> MemoryChannel<T>::MemoryChannel( int size):
+ theSize(size),
+ theChannel(new T*[size] ),
+ theWriteIndex(0, size),
+ theReadIndex(0, size)
+{
+ theMutexPtr = NdbMutex_Create();
+ theConditionPtr = NdbCondition_Create();
+}
+
+template <class T> MemoryChannel<T>::~MemoryChannel( )
+{
+ NdbMutex_Destroy(theMutexPtr);
+ NdbCondition_Destroy(theConditionPtr);
+ delete [] theChannel;
+}
+
+template <class T> void MemoryChannel<T>::writeChannel( T *t)
+{
+
+ NdbMutex_Lock(theMutexPtr);
+ if(full(theWriteIndex, theReadIndex) || theChannel == NULL) abort();
+ theChannel[theWriteIndex]= t;
+ ++theWriteIndex;
+ NdbMutex_Unlock(theMutexPtr);
+ NdbCondition_Signal(theConditionPtr);
+}
+
+
+template <class T> T* MemoryChannel<T>::readChannel()
+{
+ T* tmp;
+
+ NdbMutex_Lock(theMutexPtr);
+ while ( empty(theWriteIndex, theReadIndex) )
+ {
+ NdbCondition_Wait(theConditionPtr,
+ theMutexPtr);
+ }
+
+ tmp= theChannel[theReadIndex];
+ ++theReadIndex;
+ NdbMutex_Unlock(theMutexPtr);
+ return tmp;
+}
+
+template <class T> T* MemoryChannel<T>::tryReadChannel()
+{
+ T* tmp= 0;
+ NdbMutex_Lock(theMutexPtr);
+ NdbCondition_WaitTimeout(theConditionPtr,
+ theMutexPtr, 0);
+ if ( !empty(theWriteIndex, theReadIndex) )
+ {
+ tmp= theChannel[theReadIndex];
+ ++theReadIndex;
+ }
+ NdbMutex_Unlock(theMutexPtr);
+ return tmp;
+}
+
+#endif
+
+#endif // MemoryChannel_H
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelOSE.hpp b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelOSE.hpp
new file mode 100644
index 00000000000..ca90bc60153
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelOSE.hpp
@@ -0,0 +1,204 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 MemoryChannelOSE_H
+#define MemoryChannelOSE_H
+
+//===========================================================================
+//
+// .DESCRIPTION
+// Pointer based communication channel for communication between two
+// thread. It sends the pointer to the other signal via an OSE signal
+//
+// .TYPICAL USE:
+// to communicate between threads.
+//
+// .EXAMPLE:
+// See AsyncFile.C
+//===========================================================================
+//
+//
+// MemoryChannel( int size= 256);
+// Constuctor
+// Parameters:
+// size : is ignored in OSE version
+//
+// void operator ++ ();
+// increments the index with one, if size is reached it is set to zero
+//
+// virtual void write( T *t);
+// Puts the item in the channel if the channel is full an error is reported.
+// Parameters:
+// t: pointer to item to put in the channel, after this the item
+// is shared with the other thread.
+// errors
+// AFS_ERROR_CHANNALFULL, channel is full
+//
+// T* read();
+// Reads a itemn from the channel, if channel is empty it blocks untill
+// an item can be read.
+// return
+// T : item from the channel
+//
+// T* tryRead();
+// Reads a item from the channel, if channel is empty it returns zero.
+// return
+// T : item from the channel or zero if channel is empty.
+//
+
+#include <ose.h>
+#include "ErrorHandlingMacros.hpp"
+#include "Error.hpp"
+#include "NdbMutex.h"
+#include "NdbCondition.h"
+
+
+
+
+
+template <class T>
+class MemoryChannel
+{
+public:
+ MemoryChannel( int size= 256);
+ virtual ~MemoryChannel( );
+
+ virtual void writeChannel( T *t);
+ T* readChannel();
+ T* tryReadChannel();
+
+private:
+ PROCESS theReceiverPid;
+};
+
+template <class T> class MemoryChannelMultipleWriter:public MemoryChannel<T>
+{
+public:
+ MemoryChannelMultipleWriter( int size= 256);
+ ~MemoryChannelMultipleWriter( );
+ void writeChannel( T *t);
+
+private:
+};
+
+
+#define MEMCHANNEL_SIGBASE 5643
+
+#define MEMCHANNEL_SIGNAL (MEMCHANNEL_SIGBASE + 1) /* !-SIGNO(struct MemChannelSignal)-! */
+
+
+struct MemChannelSignal
+{
+ SIGSELECT sigNo;
+ void* ptr;
+};
+
+union SIGNAL
+{
+ SIGSELECT sigNo;
+ struct MemChannelSignal memChanSig;
+};
+
+template <class T> MemoryChannel<T>::MemoryChannel( int size )
+{
+ // Default receiver for this channel is the creating process
+ theReceiverPid = current_process();
+}
+
+template <class T> MemoryChannel<T>::~MemoryChannel( )
+{
+}
+
+template <class T> void MemoryChannel<T>::writeChannel( T *t)
+{
+ union SIGNAL* sig;
+
+ sig = alloc(sizeof(struct MemChannelSignal), MEMCHANNEL_SIGNAL);
+ ((struct MemChannelSignal*)sig)->ptr = t;
+ send(&sig, theReceiverPid);
+}
+
+
+template <class T> T* MemoryChannel<T>::readChannel()
+{
+ T* tmp;
+
+ static const SIGSELECT sel_mem[] = {1, MEMCHANNEL_SIGNAL};
+ union SIGNAL* sig;
+
+ tmp = NULL; /* Default value */
+
+ sig = receive((SIGSELECT*)sel_mem);
+ if (sig != NIL){
+ if (sig->sigNo == MEMCHANNEL_SIGNAL){
+ tmp = (T*)(((struct MemChannelSignal*)sig)->ptr);
+ }else{
+ assert(1==0);
+ }
+ free_buf(&sig);
+ }
+
+ return tmp;
+}
+
+template <class T> T* MemoryChannel<T>::tryReadChannel()
+{
+ T* tmp;
+
+ static const SIGSELECT sel_mem[] = {1, MEMCHANNEL_SIGNAL};
+ union SIGNAL* sig;
+
+ tmp = NULL; /* Default value */
+
+ sig = receive_w_tmo(0, (SIGSELECT*)sel_mem);
+ if (sig != NIL){
+ if (sig->sigNo == MEMCHANNEL_SIGNAL){
+ tmp = (T*)(((struct MemChannelSignal*)sig)->ptr);
+ }else{
+ assert(1==0);
+ }
+ free_buf(&sig);
+ }
+
+ return tmp;
+}
+
+
+#endif // MemoryChannel_H
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/Makefile b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/Makefile
new file mode 100644
index 00000000000..68f71bfc4cd
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := mctest
+BIN_TARGET_ARCHIVES := portlib
+
+SOURCES = MemoryChannelTest.cpp
+
+CFLAGS_MemoryChannelTest.cpp = -I../
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/MemoryChannelTest.cpp b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/MemoryChannelTest.cpp
new file mode 100644
index 00000000000..b98c60693f4
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/MemoryChannelTest.cpp
@@ -0,0 +1,193 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "MemoryChannel.hpp"
+#include "NdbThread.h"
+#include "NdbSleep.h"
+#include "NdbOut.hpp"
+#include "NdbMain.h"
+
+
+
+MemoryChannel<int>* theMemoryChannel;
+
+
+extern "C" void* runProducer(void*arg)
+{
+ // The producer will items into the MemoryChannel
+ int count = *(int*)arg;
+ int* p;
+ int i = 0;
+ while (i <= count)
+ {
+ p = new int(i);
+ ndbout << "P: " << *p << endl;
+ theMemoryChannel->writeChannel(p);
+ if (i%5==0)
+ NdbSleep_MilliSleep(i);
+ i++;
+ }
+ return NULL;
+}
+
+extern "C" void* runConsumer(void* arg)
+{
+ // The producer will read items from MemoryChannel and print on screen
+ int count = *(int*)arg;
+ int* p;
+ int i = 0;
+ while (i < count)
+ {
+ p = theMemoryChannel->readChannel();
+ ndbout << "C: " << *p << endl;
+ i = *p;
+ delete p;
+
+ }
+ return NULL;
+}
+
+
+
+class ArgStruct
+{
+public:
+ ArgStruct(int _items, int _no){
+ items=_items;
+ no=_no;
+ };
+ int items;
+ int no;
+};
+
+MemoryChannelMultipleWriter<ArgStruct>* theMemoryChannel2;
+
+extern "C" void* runProducer2(void*arg)
+{
+ // The producer will items into the MemoryChannel
+ ArgStruct* pArg = (ArgStruct*)arg;
+ int count = pArg->items;
+ ArgStruct* p;
+ int i = 0;
+ while (i < count)
+ {
+ p = new ArgStruct(i, pArg->no);
+ ndbout << "P"<<pArg->no<<": " << i << endl;
+ theMemoryChannel2->writeChannel(p);
+ NdbSleep_MilliSleep(i);
+ i++;
+ }
+ return NULL;
+}
+
+extern "C" void* runConsumer2(void* arg)
+{
+ // The producer will read items from MemoryChannel and print on screen
+ ArgStruct* pArg = (ArgStruct*)arg;
+ int count = pArg->items * pArg->no;
+ ArgStruct* p;
+ int i = 0;
+ while (i < count)
+ {
+ p = theMemoryChannel2->readChannel();
+ ndbout << "C: "<< p->no << ", " << p->items << endl;
+ i++;
+ delete p;
+ }
+ ndbout << "Consumer2: " << count << " received" << endl;
+ return NULL;
+}
+
+
+
+
+//#if defined MEMORYCHANNELTEST
+
+//int main(int argc, char **argv)
+NDB_COMMAND(mctest, "mctest", "mctest", "Test the memory channel used in Ndb", 32768)
+{
+
+ ndbout << "==== testing MemoryChannel ====" << endl;
+
+ theMemoryChannel = new MemoryChannel<int>;
+ theMemoryChannel2 = new MemoryChannelMultipleWriter<ArgStruct>;
+
+ NdbThread* consumerThread;
+ NdbThread* producerThread;
+
+ NdbThread_SetConcurrencyLevel(2);
+
+ int numItems = 100;
+ producerThread = NdbThread_Create(runProducer,
+ (void**)&numItems,
+ 4096,
+ (char*)"producer");
+
+ consumerThread = NdbThread_Create(runConsumer,
+ (void**)&numItems,
+ 4096,
+ (char*)"consumer");
+
+
+ void *status;
+ NdbThread_WaitFor(consumerThread, &status);
+ NdbThread_WaitFor(producerThread, &status);
+
+ ndbout << "==== testing MemoryChannelMultipleWriter ====" << endl;
+#define NUM_THREADS2 5
+ NdbThread_SetConcurrencyLevel(NUM_THREADS2+2);
+ NdbThread* producerThreads[NUM_THREADS2];
+
+ ArgStruct *pArg;
+ for (int j = 0; j < NUM_THREADS2; j++)
+ {
+ char buf[25];
+ sprintf((char*)&buf, "producer%d", j);
+ pArg = new ArgStruct(numItems, j);
+ producerThreads[j] = NdbThread_Create(runProducer2,
+ (void**)pArg,
+ 4096,
+ (char*)&buf);
+ }
+
+ pArg = new ArgStruct(numItems, NUM_THREADS2);
+ consumerThread = NdbThread_Create(runConsumer2,
+ (void**)pArg,
+ 4096,
+ (char*)"consumer");
+
+
+ NdbThread_WaitFor(consumerThread, &status);
+ for (int j = 0; j < NUM_THREADS2; j++)
+ {
+ NdbThread_WaitFor(producerThreads[j], &status);
+ }
+
+
+ return 0;
+
+}
+
+void ErrorReporter::handleError(ErrorCategory type, int messageID,
+ const char* problemData, const char* objRef,
+ NdbShutdownType nst)
+{
+
+ ndbout << "ErrorReporter::handleError activated" << endl;
+ exit(1);
+}
+
+//#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp b/storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp
new file mode 100644
index 00000000000..9750e1c5179
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp
@@ -0,0 +1,1018 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "Ndbfs.hpp"
+#include "AsyncFile.hpp"
+#include "Filename.hpp"
+#include "Error.hpp"
+
+#include <signaldata/FsOpenReq.hpp>
+#include <signaldata/FsCloseReq.hpp>
+#include <signaldata/FsReadWriteReq.hpp>
+#include <signaldata/FsAppendReq.hpp>
+#include <signaldata/FsRemoveReq.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+#include <signaldata/NdbfsContinueB.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+#include <RefConvert.hpp>
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+#include <Configuration.hpp>
+
+#define DEBUG(x) { ndbout << "FS::" << x << endl; }
+
+inline
+int pageSize( const NewVARIABLE* baseAddrRef )
+{
+ int log_psize;
+ int log_qsize = baseAddrRef->bits.q;
+ int log_vsize = baseAddrRef->bits.v;
+ if (log_vsize < 3)
+ log_vsize = 3;
+ log_psize = log_qsize + log_vsize - 3;
+ return (1 << log_psize);
+}
+
+
+Ndbfs::Ndbfs(const Configuration & conf) :
+ SimulatedBlock(NDBFS, conf),
+ scanningInProgress(false),
+ theLastId(0),
+ m_maxOpenedFiles(0)
+{
+ theFileSystemPath = conf.fileSystemPath();
+ theBackupFilePath = conf.backupFilePath();
+
+ theRequestPool = new Pool<Request>;
+
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ m_maxFiles = 40;
+ ndb_mgm_get_int_parameter(p, CFG_DB_MAX_OPEN_FILES, &m_maxFiles);
+
+ // Create idle AsyncFiles
+ Uint32 noIdleFiles = m_maxFiles > 27 ? 27 : m_maxFiles ;
+ for (Uint32 i = 0; i < noIdleFiles; i++){
+ theIdleFiles.push_back(createAsyncFile());
+ }
+
+ BLOCK_CONSTRUCTOR(Ndbfs);
+
+ // Set received signals
+ addRecSignal(GSN_DUMP_STATE_ORD, &Ndbfs::execDUMP_STATE_ORD);
+ addRecSignal(GSN_STTOR, &Ndbfs::execSTTOR);
+ addRecSignal(GSN_FSOPENREQ, &Ndbfs::execFSOPENREQ);
+ addRecSignal(GSN_FSCLOSEREQ, &Ndbfs::execFSCLOSEREQ);
+ addRecSignal(GSN_FSWRITEREQ, &Ndbfs::execFSWRITEREQ);
+ addRecSignal(GSN_FSREADREQ, &Ndbfs::execFSREADREQ);
+ addRecSignal(GSN_FSSYNCREQ, &Ndbfs::execFSSYNCREQ);
+ addRecSignal(GSN_CONTINUEB, &Ndbfs::execCONTINUEB);
+ addRecSignal(GSN_FSAPPENDREQ, &Ndbfs::execFSAPPENDREQ);
+ addRecSignal(GSN_FSREMOVEREQ, &Ndbfs::execFSREMOVEREQ);
+ // Set send signals
+}
+
+Ndbfs::~Ndbfs()
+{
+ // Delete all files
+ // AsyncFile destuctor will take care of deleting
+ // the thread it has created
+ for (unsigned i = 0; i < theFiles.size(); i++){
+ AsyncFile* file = theFiles[i];
+ delete file;
+ theFiles[i] = NULL;
+ }//for
+ theFiles.clear();
+
+ delete theRequestPool;
+}
+
+/* Received a restart signal.
+ * Answer it like any other block
+ * PR0 : StartCase
+ * DR0 : StartPhase
+ * DR1 : ?
+ * DR2 : ?
+ * DR3 : ?
+ * DR4 : ?
+ * DR5 : SignalKey
+ */
+void
+Ndbfs::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ if(signal->theData[1] == 0){ // StartPhase 0
+ jam();
+ cownref = NDBFS_REF;
+ // close all open files
+ ndbrequire(theOpenFiles.size() == 0);
+
+ scanningInProgress = false;
+
+ signal->theData[0] = NdbfsContinueB::ZSCAN_MEMORYCHANNEL_10MS_DELAY;
+ sendSignalWithDelay(cownref, GSN_CONTINUEB, signal, 10, 1);
+
+ signal->theData[3] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal,4, JBB);
+ return;
+ }
+ ndbrequire(0);
+}
+
+int
+Ndbfs::forward( AsyncFile * file, Request* request)
+{
+ jam();
+ file->execute(request);
+ return 1;
+}
+
+void
+Ndbfs::execFSOPENREQ(Signal* signal)
+{
+ jamEntry();
+ const FsOpenReq * const fsOpenReq = (FsOpenReq *)&signal->theData[0];
+ const BlockReference userRef = fsOpenReq->userReference;
+ AsyncFile* file = getIdleFile();
+ ndbrequire(file != NULL);
+ ndbrequire(signal->getLength() == FsOpenReq::SignalLength)
+ file->theFileName.set( userRef, fsOpenReq->fileNumber);
+ file->reportTo(&theFromThreads);
+
+ Request* request = theRequestPool->get();
+ request->action = Request::open;
+ request->error = 0;
+ request->par.open.flags = fsOpenReq->fileFlags;
+ request->set(userRef, fsOpenReq->userPointer, newId() );
+ request->file = file;
+ request->theTrace = signal->getTrace();
+
+ ndbrequire(forward(file, request));
+}
+
+void
+Ndbfs::execFSREMOVEREQ(Signal* signal)
+{
+ jamEntry();
+ const FsRemoveReq * const req = (FsRemoveReq *)signal->getDataPtr();
+ const BlockReference userRef = req->userReference;
+ AsyncFile* file = getIdleFile();
+ ndbrequire(file != NULL);
+
+ file->theFileName.set( userRef, req->fileNumber, req->directory);
+ file->reportTo(&theFromThreads);
+
+ Request* request = theRequestPool->get();
+ request->action = Request::rmrf;
+ request->par.rmrf.directory = req->directory;
+ request->par.rmrf.own_directory = req->ownDirectory;
+ request->error = 0;
+ request->set(userRef, req->userPointer, newId() );
+ request->file = file;
+ request->theTrace = signal->getTrace();
+
+ ndbrequire(forward(file, request));
+}
+
+/*
+ * PR0: File Pointer DR0: User reference DR1: User Pointer DR2: Flag bit 0= 1
+ * remove file
+ */
+void
+Ndbfs::execFSCLOSEREQ(Signal * signal)
+{
+ jamEntry();
+ const FsCloseReq * const fsCloseReq = (FsCloseReq *)&signal->theData[0];
+ const BlockReference userRef = fsCloseReq->userReference;
+ const Uint16 filePointer = (Uint16)fsCloseReq->filePointer;
+ const UintR userPointer = fsCloseReq->userPointer;
+
+ AsyncFile* openFile = theOpenFiles.find(filePointer);
+ if (openFile == NULL) {
+ // The file was not open, send error back to sender
+ jam();
+ // Initialise FsRef signal
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = userPointer;
+ fsRef->setErrorCode(fsRef->errorCode, FsRef::fsErrFileDoesNotExist);
+ fsRef->osErrorCode = ~0; // Indicate local error
+ sendSignal(userRef, GSN_FSCLOSEREF, signal, 3, JBB);
+ return;
+ }
+
+ Request *request = theRequestPool->get();
+ if( fsCloseReq->getRemoveFileFlag(fsCloseReq->fileFlag) == true ) {
+ jam();
+ request->action = Request::closeRemove;
+ } else {
+ jam();
+ request->action = Request::close;
+ }
+ request->set(userRef, fsCloseReq->userPointer, filePointer);
+ request->file = openFile;
+ request->error = 0;
+ request->theTrace = signal->getTrace();
+
+ ndbrequire(forward(openFile, request));
+}
+
+void
+Ndbfs::readWriteRequest(int action, Signal * signal)
+{
+ const FsReadWriteReq * const fsRWReq = (FsReadWriteReq *)&signal->theData[0];
+ Uint16 filePointer = (Uint16)fsRWReq->filePointer;
+ const UintR userPointer = fsRWReq->userPointer;
+ const BlockReference userRef = fsRWReq->userReference;
+ const BlockNumber blockNumber = refToBlock(userRef);
+
+ AsyncFile* openFile = theOpenFiles.find(filePointer);
+
+ const NewVARIABLE *myBaseAddrRef = &getBat(blockNumber)[fsRWReq->varIndex];
+ unsigned int tPageSize;
+ unsigned int tClusterSize;
+ unsigned int tNRR;
+ unsigned int tPageOffset;
+ char* tWA;
+ FsRef::NdbfsErrorCodeType errorCode;
+
+ Request *request = theRequestPool->get();
+ request->error = 0;
+ request->set(userRef, userPointer, filePointer);
+ request->file = openFile;
+ request->action = (Request::Action) action;
+ request->theTrace = signal->getTrace();
+
+ if (fsRWReq->numberOfPages == 0) { //Zero pages not allowed
+ jam();
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }
+
+ if (fsRWReq->varIndex >= getBatSize(blockNumber)) {
+ jam();// Ensure that a valid variable is used
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }
+ if (myBaseAddrRef == NULL) {
+ jam(); // Ensure that a valid variable is used
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }
+ if (openFile == NULL) {
+ jam(); //file not open
+ errorCode = FsRef::fsErrFileDoesNotExist;
+ goto error;
+ }
+ tPageSize = pageSize(myBaseAddrRef);
+ tClusterSize = myBaseAddrRef->ClusterSize;
+ tNRR = myBaseAddrRef->nrr;
+ tWA = (char*)myBaseAddrRef->WA;
+
+ switch (fsRWReq->getFormatFlag(fsRWReq->operationFlag)) {
+
+ // List of memory and file pages pairs
+ case FsReadWriteReq::fsFormatListOfPairs: {
+ jam();
+ for (unsigned int i = 0; i < fsRWReq->numberOfPages; i++) {
+ jam();
+ const Uint32 varIndex = fsRWReq->data.listOfPair[i].varIndex;
+ const Uint32 fileOffset = fsRWReq->data.listOfPair[i].fileOffset;
+ if (varIndex >= tNRR) {
+ jam();
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }//if
+ request->par.readWrite.pages[i].buf = &tWA[varIndex * tClusterSize];
+ request->par.readWrite.pages[i].size = tPageSize;
+ request->par.readWrite.pages[i].offset = fileOffset * tPageSize;
+ }//for
+ request->par.readWrite.numberOfPages = fsRWReq->numberOfPages;
+ break;
+ }//case
+
+ // Range of memory page with one file page
+ case FsReadWriteReq::fsFormatArrayOfPages: {
+ if ((fsRWReq->numberOfPages + fsRWReq->data.arrayOfPages.varIndex) > tNRR) {
+ jam();
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }//if
+ const Uint32 varIndex = fsRWReq->data.arrayOfPages.varIndex;
+ const Uint32 fileOffset = fsRWReq->data.arrayOfPages.fileOffset;
+
+ request->par.readWrite.pages[0].offset = fileOffset * tPageSize;
+ request->par.readWrite.pages[0].size = tPageSize * fsRWReq->numberOfPages;
+ request->par.readWrite.numberOfPages = 1;
+ request->par.readWrite.pages[0].buf = &tWA[varIndex * tPageSize];
+ break;
+ }//case
+
+ // List of memory pages followed by one file page
+ case FsReadWriteReq::fsFormatListOfMemPages: {
+
+ tPageOffset = fsRWReq->data.listOfMemPages.varIndex[fsRWReq->numberOfPages];
+ tPageOffset *= tPageSize;
+
+ for (unsigned int i = 0; i < fsRWReq->numberOfPages; i++) {
+ jam();
+ Uint32 varIndex = fsRWReq->data.listOfMemPages.varIndex[i];
+
+ if (varIndex >= tNRR) {
+ jam();
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }//if
+ request->par.readWrite.pages[i].buf = &tWA[varIndex * tClusterSize];
+ request->par.readWrite.pages[i].size = tPageSize;
+ request->par.readWrite.pages[i].offset = tPageOffset + (i*tPageSize);
+ }//for
+ request->par.readWrite.numberOfPages = fsRWReq->numberOfPages;
+ break;
+ // make it a writev or readv
+ }//case
+
+ default: {
+ jam();
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }//default
+
+ }//switch
+
+ ndbrequire(forward(openFile, request));
+ return;
+
+error:
+ theRequestPool->put(request);
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = userPointer;
+ fsRef->setErrorCode(fsRef->errorCode, errorCode);
+ fsRef->osErrorCode = ~0; // Indicate local error
+ switch (action) {
+ case Request:: write:
+ case Request:: writeSync: {
+ jam();
+ sendSignal(userRef, GSN_FSWRITEREF, signal, 3, JBB);
+ break;
+ }//case
+ case Request:: read: {
+ jam();
+ sendSignal(userRef, GSN_FSREADREF, signal, 3, JBB);
+ }//case
+ }//switch
+ return;
+}
+
+/*
+ PR0: File Pointer , theData[0]
+ DR0: User reference, theData[1]
+ DR1: User Pointer, etc.
+ DR2: Flag
+ DR3: Var number
+ DR4: amount of pages
+ DR5->: Memory Page id and File page id according to Flag
+*/
+void
+Ndbfs::execFSWRITEREQ(Signal* signal)
+{
+ jamEntry();
+ const FsReadWriteReq * const fsWriteReq = (FsReadWriteReq *)&signal->theData[0];
+
+ if (fsWriteReq->getSyncFlag(fsWriteReq->operationFlag) == true){
+ jam();
+ readWriteRequest( Request::writeSync, signal );
+ } else {
+ jam();
+ readWriteRequest( Request::write, signal );
+ }
+}
+
+/*
+ PR0: File Pointer
+ DR0: User reference
+ DR1: User Pointer
+ DR2: Flag
+ DR3: Var number
+ DR4: amount of pages
+ DR5->: Memory Page id and File page id according to Flag
+*/
+void
+Ndbfs::execFSREADREQ(Signal* signal)
+{
+ jamEntry();
+ readWriteRequest( Request::read, signal );
+}
+
+/*
+ * PR0: File Pointer DR0: User reference DR1: User Pointer
+ */
+void
+Ndbfs::execFSSYNCREQ(Signal * signal)
+{
+ jamEntry();
+ Uint16 filePointer = (Uint16)signal->theData[0];
+ BlockReference userRef = signal->theData[1];
+ const UintR userPointer = signal->theData[2];
+ AsyncFile* openFile = theOpenFiles.find(filePointer);
+
+ if (openFile == NULL) {
+ jam(); //file not open
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = userPointer;
+ fsRef->setErrorCode(fsRef->errorCode, FsRef::fsErrFileDoesNotExist);
+ fsRef->osErrorCode = ~0; // Indicate local error
+ sendSignal(userRef, GSN_FSSYNCREF, signal, 3, JBB);
+ return;
+ }
+
+ Request *request = theRequestPool->get();
+ request->error = 0;
+ request->action = Request::sync;
+ request->set(userRef, userPointer, filePointer);
+ request->file = openFile;
+ request->theTrace = signal->getTrace();
+
+ ndbrequire(forward(openFile,request));
+}
+
+void
+Ndbfs::execFSAPPENDREQ(Signal * signal)
+{
+ const FsAppendReq * const fsReq = (FsAppendReq *)&signal->theData[0];
+ const Uint16 filePointer = (Uint16)fsReq->filePointer;
+ const UintR userPointer = fsReq->userPointer;
+ const BlockReference userRef = fsReq->userReference;
+ const BlockNumber blockNumber = refToBlock(userRef);
+
+ FsRef::NdbfsErrorCodeType errorCode;
+
+ AsyncFile* openFile = theOpenFiles.find(filePointer);
+ const NewVARIABLE *myBaseAddrRef = &getBat(blockNumber)[fsReq->varIndex];
+
+ const Uint32* tWA = (const Uint32*)myBaseAddrRef->WA;
+ const Uint32 tSz = myBaseAddrRef->nrr;
+ const Uint32 offset = fsReq->offset;
+ const Uint32 size = fsReq->size;
+ Request *request = theRequestPool->get();
+
+ if (openFile == NULL) {
+ jam();
+ errorCode = FsRef::fsErrFileDoesNotExist;
+ goto error;
+ }
+
+ if (myBaseAddrRef == NULL) {
+ jam(); // Ensure that a valid variable is used
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }
+
+ if (fsReq->varIndex >= getBatSize(blockNumber)) {
+ jam();// Ensure that a valid variable is used
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }
+
+ if(offset + size > tSz){
+ jam(); // Ensure that a valid variable is used
+ errorCode = FsRef::fsErrInvalidParameters;
+ goto error;
+ }
+
+ request->error = 0;
+ request->set(userRef, userPointer, filePointer);
+ request->file = openFile;
+ request->action = Request::append;
+ request->theTrace = signal->getTrace();
+
+ request->par.append.buf = (const char *)(tWA + offset);
+ request->par.append.size = size << 2;
+
+ ndbrequire(forward(openFile, request));
+ return;
+
+error:
+ jam();
+ theRequestPool->put(request);
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = userPointer;
+ fsRef->setErrorCode(fsRef->errorCode, errorCode);
+ fsRef->osErrorCode = ~0; // Indicate local error
+
+ jam();
+ sendSignal(userRef, GSN_FSAPPENDREF, signal, 3, JBB);
+ return;
+}
+
+Uint16
+Ndbfs::newId()
+{
+ // finds a new key, eg a new filepointer
+ for (int i = 1; i < SHRT_MAX; i++)
+ {
+ if (theLastId == SHRT_MAX) {
+ jam();
+ theLastId = 1;
+ } else {
+ jam();
+ theLastId++;
+ }
+
+ if(theOpenFiles.find(theLastId) == NULL) {
+ jam();
+ return theLastId;
+ }
+ }
+ ndbrequire(1 == 0);
+ // The program will not reach this point
+ return 0;
+}
+
+AsyncFile*
+Ndbfs::createAsyncFile(){
+
+ // Check limit of open files
+ if (theFiles.size()+1 == m_maxFiles) {
+ // Print info about all open files
+ for (unsigned i = 0; i < theFiles.size(); i++){
+ AsyncFile* file = theFiles[i];
+ ndbout_c("%2d (0x%x): %s", i, file, file->isOpen()?"OPEN":"CLOSED");
+ }
+ ERROR_SET(fatal, AFS_ERROR_MAXOPEN,""," Ndbfs::createAsyncFile");
+ }
+
+ AsyncFile* file = new AsyncFile;
+ file->doStart(getOwnNodeId(), theFileSystemPath, theBackupFilePath);
+
+ // Put the file in list of all files
+ theFiles.push_back(file);
+
+#ifdef VM_TRACE
+ infoEvent("NDBFS: Created new file thread %d", theFiles.size());
+#endif
+
+ return file;
+}
+
+AsyncFile*
+Ndbfs::getIdleFile(){
+ AsyncFile* file;
+ if (theIdleFiles.size() > 0){
+ file = theIdleFiles[0];
+ theIdleFiles.erase(0);
+ } else {
+ file = createAsyncFile();
+ }
+ return file;
+}
+
+
+
+void
+Ndbfs::report(Request * request, Signal* signal)
+{
+ const Uint32 orgTrace = signal->getTrace();
+ signal->setTrace(request->theTrace);
+ const BlockReference ref = request->theUserReference;
+ if (request->error) {
+ jam();
+ // Initialise FsRef signal
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = request->theUserPointer;
+ fsRef->setErrorCode(fsRef->errorCode, translateErrno(request->error));
+ fsRef->osErrorCode = request->error;
+
+ switch (request->action) {
+ case Request:: open: {
+ jam();
+ // Put the file back in idle files list
+ theIdleFiles.push_back(request->file);
+ sendSignal(ref, GSN_FSOPENREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+ case Request:: closeRemove:
+ case Request:: close: {
+ jam();
+ sendSignal(ref, GSN_FSCLOSEREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+ case Request:: writeSync:
+ case Request:: writevSync:
+ case Request:: write:
+ case Request:: writev: {
+ jam();
+ sendSignal(ref, GSN_FSWRITEREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+ case Request:: read:
+ case Request:: readv: {
+ jam();
+ sendSignal(ref, GSN_FSREADREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+ case Request:: sync: {
+ jam();
+ sendSignal(ref, GSN_FSSYNCREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+ case Request::append: {
+ jam();
+ sendSignal(ref, GSN_FSAPPENDREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+ case Request::rmrf: {
+ jam();
+ // Put the file back in idle files list
+ theIdleFiles.push_back(request->file);
+ sendSignal(ref, GSN_FSREMOVEREF, signal, FsRef::SignalLength, JBB);
+ break;
+ }
+
+ case Request:: end: {
+ // Report nothing
+ break;
+ }
+ }//switch
+ } else {
+ jam();
+ FsConf * const fsConf = (FsConf *)&signal->theData[0];
+ fsConf->userPointer = request->theUserPointer;
+ switch (request->action) {
+ case Request:: open: {
+ jam();
+ theOpenFiles.insert(request->file, request->theFilePointer);
+
+ // Keep track on max number of opened files
+ if (theOpenFiles.size() > m_maxOpenedFiles)
+ m_maxOpenedFiles = theOpenFiles.size();
+
+ fsConf->filePointer = request->theFilePointer;
+ sendSignal(ref, GSN_FSOPENCONF, signal, 3, JBB);
+ break;
+ }
+ case Request:: closeRemove:
+ case Request:: close: {
+ jam();
+ // removes the file from OpenFiles list
+ theOpenFiles.erase(request->theFilePointer);
+ // Put the file in idle files list
+ theIdleFiles.push_back(request->file);
+ sendSignal(ref, GSN_FSCLOSECONF, signal, 1, JBB);
+ break;
+ }
+ case Request:: writeSync:
+ case Request:: writevSync:
+ case Request:: write:
+ case Request:: writev: {
+ jam();
+ sendSignal(ref, GSN_FSWRITECONF, signal, 1, JBB);
+ break;
+ }
+ case Request:: read:
+ case Request:: readv: {
+ jam();
+ sendSignal(ref, GSN_FSREADCONF, signal, 1, JBB);
+ break;
+ }
+ case Request:: sync: {
+ jam();
+ sendSignal(ref, GSN_FSSYNCCONF, signal, 1, JBB);
+ break;
+ }//case
+ case Request::append: {
+ jam();
+ signal->theData[1] = request->par.append.size;
+ sendSignal(ref, GSN_FSAPPENDCONF, signal, 2, JBB);
+ break;
+ }
+ case Request::rmrf: {
+ jam();
+ // Put the file in idle files list
+ theIdleFiles.push_back(request->file);
+ sendSignal(ref, GSN_FSREMOVECONF, signal, 1, JBB);
+ break;
+ }
+ case Request:: end: {
+ // Report nothing
+ break;
+ }
+ }
+ }//if
+ signal->setTrace(orgTrace);
+}
+
+
+bool
+Ndbfs::scanIPC(Signal* signal)
+{
+ Request* request = theFromThreads.tryReadChannel();
+ jam();
+ if (request) {
+ jam();
+ report(request, signal);
+ theRequestPool->put(request);
+ return true;
+ }
+ return false;
+}
+
+#if defined NDB_WIN32
+int Ndbfs::translateErrno(int aErrno)
+{
+ switch (aErrno)
+ {
+ //permission denied
+ case ERROR_ACCESS_DENIED:
+
+ return FsRef::fsErrPermissionDenied;
+ //temporary not accessible
+ case ERROR_PATH_BUSY:
+ case ERROR_NO_MORE_SEARCH_HANDLES:
+
+ return FsRef::fsErrTemporaryNotAccessible;
+ //no space left on device
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+
+ return FsRef::fsErrNoSpaceLeftOnDevice;
+ //none valid parameters
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_INVALID_ACCESS:
+ case ERROR_HANDLE_EOF:
+ case ERROR_BUFFER_OVERFLOW:
+
+ return FsRef::fsErrInvalidParameters;
+ //environment error
+ case ERROR_CRC:
+ case ERROR_ARENA_TRASHED:
+ case ERROR_BAD_ENVIRONMENT:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_WRITE_FAULT:
+ case ERROR_READ_FAULT:
+ case ERROR_OPEN_FAILED:
+
+ return FsRef::fsErrEnvironmentError;
+
+ //no more process resources
+ case ERROR_TOO_MANY_OPEN_FILES:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ return FsRef::fsErrNoMoreResources;
+ //no file
+ case ERROR_FILE_NOT_FOUND:
+ return FsRef::fsErrFileDoesNotExist;
+
+ case ERR_ReadUnderflow:
+ return FsRef::fsErrReadUnderflow;
+
+ default:
+ return FsRef::fsErrUnknown;
+ }
+}
+#elif defined NDB_OSE || defined NDB_SOFTOSE
+int Ndbfs::translateErrno(int aErrno)
+{
+ switch (aErrno)
+ {
+ //permission denied
+ case EACCES:
+ case EROFS:
+ case ENXIO:
+ return FsRef::fsErrPermissionDenied;
+ //temporary not accessible
+ case EAGAIN:
+ case ETIMEDOUT:
+ case ENOLCK:
+ return FsRef::fsErrTemporaryNotAccessible;
+ //no space left on device
+ case ENFILE:
+ case EDQUOT:
+ case ENOSPC:
+ return FsRef::fsErrNoSpaceLeftOnDevice;
+ //none valid parameters
+ case EINVAL:
+ case EFBIG:
+ case EBADF:
+ case ENAMETOOLONG:
+ case EFAULT:
+ case EISDIR:
+ return FsRef::fsErrInvalidParameters;
+ //environment error
+ case EMLINK:
+ case ELOOP:
+ return FsRef::fsErrEnvironmentError;
+
+ //no more process resources
+ case EMFILE:
+ case ENOMEM:
+ return FsRef::fsErrNoMoreResources;
+ //no file
+ case ENOENT:
+ return FsRef::fsErrFileDoesNotExist;
+
+ case ERR_ReadUnderflow:
+ return FsRef::fsErrReadUnderflow;
+
+ default:
+ return FsRef::fsErrUnknown;
+ }
+}
+#else
+int Ndbfs::translateErrno(int aErrno)
+{
+ switch (aErrno)
+ {
+ //permission denied
+ case EACCES:
+ case EROFS:
+ case ENXIO:
+ return FsRef::fsErrPermissionDenied;
+ //temporary not accessible
+ case EAGAIN:
+ case ETIMEDOUT:
+ case ENOLCK:
+ case EINTR:
+ case EIO:
+ return FsRef::fsErrTemporaryNotAccessible;
+ //no space left on device
+ case ENFILE:
+ case EDQUOT:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ case ENOSPC:
+ case EFBIG:
+ return FsRef::fsErrNoSpaceLeftOnDevice;
+ //none valid parameters
+ case EINVAL:
+ case EBADF:
+ case ENAMETOOLONG:
+ case EFAULT:
+ case EISDIR:
+ case ENOTDIR:
+ case EEXIST:
+ case ETXTBSY:
+ return FsRef::fsErrInvalidParameters;
+ //environment error
+ case ELOOP:
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+#endif
+#ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+#endif
+#ifdef ESPIPE
+ case ESPIPE:
+#endif
+ case EPIPE:
+ return FsRef::fsErrEnvironmentError;
+
+ //no more process resources
+ case EMFILE:
+ case ENOMEM:
+ return FsRef::fsErrNoMoreResources;
+ //no file
+ case ENOENT:
+ return FsRef::fsErrFileDoesNotExist;
+
+ case ERR_ReadUnderflow:
+ return FsRef::fsErrReadUnderflow;
+
+ default:
+ return FsRef::fsErrUnknown;
+ }
+}
+#endif
+
+
+
+void
+Ndbfs::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ if (signal->theData[0] == NdbfsContinueB::ZSCAN_MEMORYCHANNEL_10MS_DELAY) {
+ jam();
+
+ // Also send CONTINUEB to ourself in order to scan for
+ // incoming answers from AsyncFile on MemoryChannel theFromThreads
+ signal->theData[0] = NdbfsContinueB::ZSCAN_MEMORYCHANNEL_10MS_DELAY;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 10, 1);
+ if (scanningInProgress == true) {
+ jam();
+ return;
+ }
+ }
+ if (scanIPC(signal)) {
+ jam();
+ scanningInProgress = true;
+ signal->theData[0] = NdbfsContinueB::ZSCAN_MEMORYCHANNEL_NO_DELAY;
+ sendSignal(reference(), GSN_CONTINUEB, signal, 1, JBB);
+ } else {
+ jam();
+ scanningInProgress = false;
+ }
+ return;
+}
+
+bool Global_useO_SYNC = false;
+bool Global_useO_DIRECT = false;
+bool Global_unlinkO_CREAT = false;
+Uint32 Global_syncFreq = 1024 * 1024;
+
+void
+Ndbfs::execDUMP_STATE_ORD(Signal* signal)
+{
+ if(signal->theData[0] == 19){
+ if(signal->length() > 1){
+ Global_useO_SYNC = signal->theData[1];
+ }
+ if(signal->length() > 2){
+ Global_syncFreq = signal->theData[2] * 1024 * 1024;
+ }
+ if(signal->length() > 3){
+ Global_unlinkO_CREAT = signal->theData[3];
+ }
+ if(signal->length() > 4){
+ Global_useO_DIRECT = signal->theData[4];
+ }
+ ndbout_c("useO_SYNC = %d syncFreq = %d unlinkO_CREATE = %d O_DIRECT = %d",
+ Global_useO_SYNC,
+ Global_syncFreq,
+ Global_unlinkO_CREAT,
+ Global_useO_DIRECT);
+ return;
+ }
+ if(signal->theData[0] == DumpStateOrd::NdbfsDumpFileStat){
+ infoEvent("NDBFS: Files: %d Open files: %d",
+ theFiles.size(),
+ theOpenFiles.size());
+ infoEvent(" Idle files: %d Max opened files: %d",
+ theIdleFiles.size(),
+ m_maxOpenedFiles);
+ infoEvent(" Max files: %d",
+ m_maxFiles);
+ infoEvent(" Requests: %d",
+ theRequestPool->size());
+
+ return;
+ }
+ if(signal->theData[0] == DumpStateOrd::NdbfsDumpOpenFiles){
+ infoEvent("NDBFS: Dump open files: %d", theOpenFiles.size());
+
+ for (unsigned i = 0; i < theOpenFiles.size(); i++){
+ AsyncFile* file = theOpenFiles.getFile(i);
+ infoEvent("%2d (0x%x): %s", i,file, file->theFileName.c_str());
+ }
+ return;
+ }
+ if(signal->theData[0] == DumpStateOrd::NdbfsDumpAllFiles){
+ infoEvent("NDBFS: Dump all files: %d", theFiles.size());
+
+ for (unsigned i = 0; i < theFiles.size(); i++){
+ AsyncFile* file = theFiles[i];
+ infoEvent("%2d (0x%x): %s", i,file, file->isOpen()?"OPEN":"CLOSED");
+ }
+ return;
+ }
+ if(signal->theData[0] == DumpStateOrd::NdbfsDumpIdleFiles){
+ infoEvent("NDBFS: Dump idle files: %d", theIdleFiles.size());
+
+ for (unsigned i = 0; i < theIdleFiles.size(); i++){
+ AsyncFile* file = theIdleFiles[i];
+ infoEvent("%2d (0x%x): %s", i,file, file->isOpen()?"OPEN":"CLOSED");
+ }
+ return;
+ }
+}//Ndbfs::execDUMP_STATE_ORD()
+
+
+
+BLOCK_FUNCTIONS(Ndbfs)
+
+template class Vector<AsyncFile*>;
+template class Vector<OpenFiles::OpenFileItem>;
+template class MemoryChannel<Request>;
+template class Pool<Request>;
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp b/storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp
new file mode 100644
index 00000000000..c5aaa4e5c49
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp
@@ -0,0 +1,127 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SIMBLOCKASYNCFILESYSTEM_H
+#define SIMBLOCKASYNCFILESYSTEM_H
+
+#include <pc.hpp>
+#include <SimulatedBlock.hpp>
+#include "Pool.hpp"
+#include "AsyncFile.hpp"
+#include "OpenFiles.hpp"
+
+
+
+// Because one NDB Signal request can result in multiple requests to
+// AsyncFile one class must be made responsible to keep track
+// of all out standing request and when all are finished the result
+// must be reported to the sending block.
+
+
+class Ndbfs : public SimulatedBlock
+{
+public:
+ Ndbfs(const class Configuration & conf);
+ virtual ~Ndbfs();
+
+protected:
+ BLOCK_DEFINES(Ndbfs);
+
+ // The signal processing functions
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execFSOPENREQ(Signal* signal);
+ void execFSCLOSEREQ(Signal* signal);
+ void execFSWRITEREQ(Signal* signal);
+ void execFSREADREQ(Signal* signal);
+ void execFSSYNCREQ(Signal* signal);
+ void execFSAPPENDREQ(Signal* signal);
+ void execFSREMOVEREQ(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+
+ bool scanningInProgress;
+ Uint16 newId();
+
+private:
+ int forward(AsyncFile *file, Request* Request);
+ void report(Request* request, Signal* signal);
+ bool scanIPC(Signal* signal);
+
+ // Declared but not defined
+ Ndbfs(Ndbfs & );
+ void operator = (Ndbfs &);
+
+ // Used for uniqe number generation
+ Uint16 theLastId;
+ BlockReference cownref;
+
+ // Communication from files
+ MemoryChannel<Request> theFromThreads;
+
+ Pool<Request>* theRequestPool;
+
+ AsyncFile* createAsyncFile();
+ AsyncFile* getIdleFile();
+
+ Vector<AsyncFile*> theFiles; // List all created AsyncFiles
+ Vector<AsyncFile*> theIdleFiles; // List of idle AsyncFiles
+ OpenFiles theOpenFiles; // List of open AsyncFiles
+ const char * theFileSystemPath;
+ const char * theBackupFilePath;
+
+ // Statistics variables
+ Uint32 m_maxOpenedFiles;
+
+ // Limit for max number of AsyncFiles created
+ Uint32 m_maxFiles;
+
+ void readWriteRequest( int action, Signal * signal );
+
+ static int translateErrno(int aErrno);
+};
+
+class VoidFs : public SimulatedBlock
+{
+public:
+ VoidFs(const class Configuration & conf);
+ virtual ~VoidFs();
+
+protected:
+ BLOCK_DEFINES(VoidFs);
+
+ // The signal processing functions
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execFSOPENREQ(Signal* signal);
+ void execFSCLOSEREQ(Signal* signal);
+ void execFSWRITEREQ(Signal* signal);
+ void execFSREADREQ(Signal* signal);
+ void execFSSYNCREQ(Signal* signal);
+ void execFSAPPENDREQ(Signal* signal);
+ void execFSREMOVEREQ(Signal* signal);
+ void execSTTOR(Signal* signal);
+
+private:
+ // Declared but not defined
+ VoidFs(VoidFs & );
+ void operator = (VoidFs &);
+
+ // Used for uniqe number generation
+ Uint32 c_maxFileNo;
+};
+
+#endif
+
+
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/OpenFiles.hpp b/storage/ndb/src/kernel/blocks/ndbfs/OpenFiles.hpp
new file mode 100644
index 00000000000..b944bb5485b
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/OpenFiles.hpp
@@ -0,0 +1,114 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 OPENFILES_H
+#define OPENFILES_H
+
+#include <Vector.hpp>
+
+class OpenFiles
+{
+public:
+ OpenFiles(){ }
+
+ /* Get a pointer to the file with id */
+ AsyncFile* find(Uint16 id);
+ /* Insert file with id */
+ bool insert(AsyncFile* file, Uint16 id);
+ /* Erase file with id */
+ bool erase(Uint16 id);
+ /* Get number of open files */
+ unsigned size();
+
+ Uint16 getId(unsigned i);
+ AsyncFile* getFile(unsigned i);
+
+
+private:
+
+ class OpenFileItem {
+ public:
+ OpenFileItem(): m_file(NULL), m_id(0){};
+
+ AsyncFile* m_file;
+ Uint16 m_id;
+ };
+
+ Vector<OpenFileItem> m_files;
+};
+
+
+//*****************************************************************************
+inline AsyncFile* OpenFiles::find(Uint16 id){
+ for (unsigned i = 0; i < m_files.size(); i++){
+ if (m_files[i].m_id == id){
+ return m_files[i].m_file;
+ }
+ }
+ return NULL;
+}
+
+//*****************************************************************************
+inline bool OpenFiles::erase(Uint16 id){
+ for (unsigned i = 0; i < m_files.size(); i++){
+ if (m_files[i].m_id == id){
+ m_files.erase(i);
+ return true;
+ }
+ }
+ // Item was not found in list
+ return false;
+}
+
+
+//*****************************************************************************
+inline bool OpenFiles::insert(AsyncFile* file, Uint16 id){
+ // Check if file has already been opened
+ for (unsigned i = 0; i < m_files.size(); i++){
+ if(m_files[i].m_file == NULL)
+ continue;
+
+ if(strcmp(m_files[i].m_file->theFileName.c_str(),
+ file->theFileName.c_str()) == 0){
+ ERROR_SET(fatal, AFS_ERROR_ALLREADY_OPEN,"","OpenFiles::insert()");
+ }
+ }
+
+ // Insert the file into vector
+ OpenFileItem openFile;
+ openFile.m_id = id;
+ openFile.m_file = file;
+ m_files.push_back(openFile);
+
+ return true;
+}
+
+//*****************************************************************************
+inline Uint16 OpenFiles::getId(unsigned i){
+ return m_files[i].m_id;
+}
+
+//*****************************************************************************
+inline AsyncFile* OpenFiles::getFile(unsigned i){
+ return m_files[i].m_file;
+}
+
+//*****************************************************************************
+inline unsigned OpenFiles::size(){
+ return m_files.size();
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/Pool.hpp b/storage/ndb/src/kernel/blocks/ndbfs/Pool.hpp
new file mode 100644
index 00000000000..0410673af6f
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/Pool.hpp
@@ -0,0 +1,261 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 FOR_LIB_POOL_H
+#define FOR_LIB_POOL_H
+
+
+//===========================================================================
+//
+// .PUBLIC
+//
+//===========================================================================
+
+////////////////////////////////////////////////////////////////
+//
+// enum { defInitSize = 256, defIncSize = 64 };
+// Description: type to store initial and incremental size in.
+//
+////////////////////////////////////////////////////////////////
+//
+// Pool(int anInitSize = defInitSize, int anIncSize = defIncSize);
+// Description:
+// Constructor. Allocates anInitSize of objects <template argument>.
+// When the pool runs out of elements, anIncSize elements are added to the
+// pool. (When the pool is not optimized to allocate multiple elements
+// more efficient, the anIncSize MUST be set to 1 to get the best
+// performance...
+//
+// Parameters:
+// defInitSize: Initial size of the pool (# of elements in the pool)
+// defIncSize: # of elements added to the pool when a request to an empty
+// pool is made.
+// Return value:
+// _
+// Errors:
+// -
+// Asserts:
+// _
+//
+////////////////////////////////////////////////////////////////
+//
+// virtual ~Pool();
+// Description:
+// Elements in the pool are all deallocated.
+// Parameters:
+// _
+// Return value:
+// _
+// Errors:
+// -
+// Asserts:
+// theEmptyNodeList==0. No elements are in still in use.
+//
+////////////////////////////////////////////////////////////////
+//
+// T& get();
+// Description:
+// get's an element from the Pool.
+// Parameters:
+// _
+// Return value:
+// T& the element extracted from the Pool. (element must be cleared to
+// mimick newly created element)
+// Errors:
+// -
+// Asserts:
+// _
+//
+////////////////////////////////////////////////////////////////
+//
+// void put(T& aT);
+// Description:
+// Returns an element to the pool.
+// Parameters:
+// aT The element to put back in the pool
+// Return value:
+// void
+// Errors:
+// -
+// Asserts:
+// The pool has "empty" elements, to put element back in...
+//
+//===========================================================================
+//
+// .PRIVATE
+//
+//===========================================================================
+
+////////////////////////////////////////////////////////////////
+//
+// void allocate(int aSize);
+// Description:
+// add aSize elements to the pool
+// Parameters:
+// aSize: # of elements to add to the pool
+// Return value:
+// void
+// Errors:
+// -
+// Asserts:
+// _
+//
+////////////////////////////////////////////////////////////////
+//
+// void deallocate();
+// Description:
+// frees all elements kept in the pool.
+// Parameters:
+// _
+// Return value:
+// void
+// Errors:
+// -
+// Asserts:
+// No elements are "empty" i.e. in use.
+//
+//===========================================================================
+//
+// .PRIVATE
+//
+//===========================================================================
+
+////////////////////////////////////////////////////////////////
+//
+// Pool<T>& operator=(const Pool<T>& cp);
+// Description:
+// Prohibit use of assignement operator.
+// Parameters:
+// cp
+// Return value:
+// Pool<T>&
+// Asserts:
+// _
+//
+////////////////////////////////////////////////////////////////
+//
+// Pool(const Pool<T>& cp);
+// Description:
+// Prohibit use of default copy constructor.
+// Parameters:
+// cp
+// Return value:
+// _
+// Errors:
+// -
+// Asserts:
+// _
+//
+////////////////////////////////////////////////////////////////
+//
+// int initSize;
+// Description: size of the initial size of the pool
+//
+////////////////////////////////////////////////////////////////
+//
+// int incSize;
+// Description: # of elements added to the pool when pool is exhausted.
+//
+////////////////////////////////////////////////////////////////
+//
+// PoolElement<T>* theFullNodeList;
+// Description: List to contain all "unused" elements in the pool
+//
+////////////////////////////////////////////////////////////////
+//
+// PoolElement<T>* theEmptyNodeList;
+// Description: List to contain all "in use" elements in the pool
+//
+//-------------------------------------------------------------------------
+
+template <class T>
+class Pool
+{
+public:
+ enum { defInitSize = 256, defIncSize = 64 };
+
+ Pool(int anInitSize = defInitSize, int anIncSize = defIncSize) :
+ theIncSize(anIncSize),
+ theTop(0),
+ theCurrentSize(0),
+ theList(0)
+ {
+ allocate(anInitSize);
+ }
+
+ virtual ~Pool(void)
+ {
+ for (int i=0; i <theTop ; ++i)
+ delete theList[i];
+
+ delete []theList;
+ }
+
+ T* get();
+ void put(T* aT);
+
+ unsigned size(){ return theTop; };
+
+protected:
+ void allocate(int aSize)
+ {
+ T** tList = theList;
+ int i;
+ theList = new T*[aSize+theCurrentSize];
+ // allocate full list
+ for (i = 0; i < theTop; i++) {
+ theList[i] = tList[i];
+ }
+ delete []tList;
+ for (; (theTop < aSize); theTop++){
+ theList[theTop] = (T*)new T;
+ }
+ theCurrentSize += aSize;
+ }
+
+private:
+ Pool<T>& operator=(const Pool<T>& cp);
+ Pool(const Pool<T>& cp);
+
+ int theIncSize;
+ int theTop;
+ int theCurrentSize;
+
+ T** theList;
+};
+
+//******************************************************************************
+template <class T> inline T* Pool<T>::get()
+{
+ T* tmp;
+ if( theTop == 0 )
+ {
+ allocate(theIncSize);
+ }
+ --theTop;
+ tmp = theList[theTop];
+ return tmp;
+}
+
+//
+//******************************************************************************
+template <class T> inline void Pool<T>::put(T* aT)
+{
+ theList[theTop]= aT;
+ ++theTop;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/ndbfs/VoidFs.cpp b/storage/ndb/src/kernel/blocks/ndbfs/VoidFs.cpp
new file mode 100644
index 00000000000..d093089acfc
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/ndbfs/VoidFs.cpp
@@ -0,0 +1,200 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <limits.h>
+#include <errno.h>
+
+#include "Ndbfs.hpp"
+#include "AsyncFile.hpp"
+#include "Filename.hpp"
+#include "Error.hpp"
+
+#include <signaldata/FsOpenReq.hpp>
+#include <signaldata/FsCloseReq.hpp>
+#include <signaldata/FsReadWriteReq.hpp>
+#include <signaldata/FsAppendReq.hpp>
+#include <signaldata/FsRemoveReq.hpp>
+#include <signaldata/FsConf.hpp>
+#include <signaldata/FsRef.hpp>
+#include <signaldata/NdbfsContinueB.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+#include <RefConvert.hpp>
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+#include <Configuration.hpp>
+
+#define DEBUG(x) { ndbout << "FS::" << x << endl; }
+
+VoidFs::VoidFs(const Configuration & conf) :
+ SimulatedBlock(NDBFS, conf)
+{
+ BLOCK_CONSTRUCTOR(VoidFs);
+
+ // Set received signals
+ addRecSignal(GSN_DUMP_STATE_ORD, &VoidFs::execDUMP_STATE_ORD);
+ addRecSignal(GSN_STTOR, &VoidFs::execSTTOR);
+ addRecSignal(GSN_FSOPENREQ, &VoidFs::execFSOPENREQ);
+ addRecSignal(GSN_FSCLOSEREQ, &VoidFs::execFSCLOSEREQ);
+ addRecSignal(GSN_FSWRITEREQ, &VoidFs::execFSWRITEREQ);
+ addRecSignal(GSN_FSREADREQ, &VoidFs::execFSREADREQ);
+ addRecSignal(GSN_FSSYNCREQ, &VoidFs::execFSSYNCREQ);
+ addRecSignal(GSN_FSAPPENDREQ, &VoidFs::execFSAPPENDREQ);
+ addRecSignal(GSN_FSREMOVEREQ, &VoidFs::execFSREMOVEREQ);
+ // Set send signals
+}
+
+VoidFs::~VoidFs()
+{
+}
+
+void
+VoidFs::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ if(signal->theData[1] == 0){ // StartPhase 0
+ jam();
+ signal->theData[3] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 4, JBB);
+ return;
+ }
+ ndbrequire(0);
+}
+
+void
+VoidFs::execFSOPENREQ(Signal* signal)
+{
+ jamEntry();
+ const FsOpenReq * const fsOpenReq = (FsOpenReq *)&signal->theData[0];
+ const BlockReference userRef = fsOpenReq->userReference;
+ const Uint32 userPointer = fsOpenReq->userPointer;
+
+ Uint32 flags = fsOpenReq->fileFlags;
+ if(flags == FsOpenReq::OM_READONLY){
+ // Initialise FsRef signal
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = userPointer;
+ fsRef->errorCode = FsRef::fsErrFileDoesNotExist;
+ fsRef->osErrorCode = ~0;
+ sendSignal(userRef, GSN_FSOPENREF, signal, 3, JBB);
+ return;
+ }
+
+ if(flags & FsOpenReq::OM_WRITEONLY || flags & FsOpenReq::OM_READWRITE){
+ signal->theData[0] = userPointer;
+ signal->theData[1] = c_maxFileNo++;
+ sendSignal(userRef, GSN_FSOPENCONF, signal, 2, JBB);
+ }
+}
+
+void
+VoidFs::execFSREMOVEREQ(Signal* signal)
+{
+ jamEntry();
+ const FsRemoveReq * const req = (FsRemoveReq *)signal->getDataPtr();
+ const Uint32 userRef = req->userReference;
+ const Uint32 userPointer = req->userPointer;
+
+ signal->theData[0] = userPointer;
+ sendSignal(userRef, GSN_FSREMOVECONF, signal, 1, JBB);
+}
+
+/*
+ * PR0: File Pointer DR0: User reference DR1: User Pointer DR2: Flag bit 0= 1
+ * remove file
+ */
+void
+VoidFs::execFSCLOSEREQ(Signal * signal)
+{
+ jamEntry();
+
+ const FsCloseReq * const req = (FsCloseReq *)signal->getDataPtr();
+ const Uint32 userRef = req->userReference;
+ const Uint32 userPointer = req->userPointer;
+
+ signal->theData[0] = userPointer;
+ sendSignal(userRef, GSN_FSCLOSECONF, signal, 1, JBB);
+}
+
+void
+VoidFs::execFSWRITEREQ(Signal* signal)
+{
+ jamEntry();
+ const FsReadWriteReq * const req = (FsReadWriteReq *)signal->getDataPtr();
+ const Uint32 userRef = req->userReference;
+ const Uint32 userPointer = req->userPointer;
+
+ signal->theData[0] = userPointer;
+ sendSignal(userRef, GSN_FSWRITECONF, signal, 1, JBB);
+}
+
+void
+VoidFs::execFSREADREQ(Signal* signal)
+{
+ jamEntry();
+
+ const FsReadWriteReq * const req = (FsReadWriteReq *)signal->getDataPtr();
+ const Uint32 userRef = req->userReference;
+ const Uint32 userPointer = req->userPointer;
+
+ signal->theData[0] = userPointer;
+ sendSignal(userRef, GSN_FSREADCONF, signal, 1, JBB);
+#if 0
+ FsRef * const fsRef = (FsRef *)&signal->theData[0];
+ fsRef->userPointer = userPointer;
+ fsRef->errorCode = FsRef::fsErrEnvironmentError;
+ fsRef->osErrorCode = ~0; // Indicate local error
+ sendSignal(userRef, GSN_FSREADREF, signal, 3, JBB);
+#endif
+}
+
+void
+VoidFs::execFSSYNCREQ(Signal * signal)
+{
+ jamEntry();
+
+ BlockReference userRef = signal->theData[1];
+ const UintR userPointer = signal->theData[2];
+
+ signal->theData[0] = userPointer;
+ sendSignal(userRef, GSN_FSSYNCCONF, signal, 1, JBB);
+
+ return;
+}
+
+void
+VoidFs::execFSAPPENDREQ(Signal * signal)
+{
+ const FsAppendReq * const fsReq = (FsAppendReq *)&signal->theData[0];
+ const UintR userPointer = fsReq->userPointer;
+ const BlockReference userRef = fsReq->userReference;
+ const Uint32 size = fsReq->size;
+
+ signal->theData[0] = userPointer;
+ signal->theData[1] = size << 2;
+ sendSignal(userRef, GSN_FSAPPENDCONF, signal, 2, JBB);
+}
+
+void
+VoidFs::execDUMP_STATE_ORD(Signal* signal)
+{
+}//VoidFs::execDUMP_STATE_ORD()
+
+
+
+BLOCK_FUNCTIONS(VoidFs)
+
diff --git a/storage/ndb/src/kernel/blocks/new-block.tar.gz b/storage/ndb/src/kernel/blocks/new-block.tar.gz
new file mode 100644
index 00000000000..327503ea0b1
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/new-block.tar.gz
Binary files differ
diff --git a/storage/ndb/src/kernel/blocks/qmgr/Makefile.am b/storage/ndb/src/kernel/blocks/qmgr/Makefile.am
new file mode 100644
index 00000000000..278af2a7865
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/qmgr/Makefile.am
@@ -0,0 +1,25 @@
+noinst_LIBRARIES = libqmgr.a
+
+libqmgr_a_SOURCES = \
+ QmgrInit.cpp \
+ QmgrMain.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libqmgr.dsp
+
+libqmgr.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libqmgr_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp b/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
new file mode 100644
index 00000000000..bff79215264
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
@@ -0,0 +1,392 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 QMGR_H
+#define QMGR_H
+
+
+#include <pc.hpp>
+#include <NdbTick.h>
+#include <SimulatedBlock.hpp>
+#include <NodeBitmask.hpp>
+#include <SignalCounter.hpp>
+
+#include <signaldata/EventReport.hpp>
+#include <signaldata/ArbitSignalData.hpp>
+#include <signaldata/CmRegSignalData.hpp>
+#include <signaldata/ApiRegSignalData.hpp>
+#include <signaldata/FailRep.hpp>
+
+#include "timer.hpp"
+
+#ifdef QMGR_C
+
+#define NO_REG_APP 1
+
+/* Delay values, ms -----------------------------*/
+#define ZDELAY_REGREQ 1000
+
+/* Type of refuse in CM_NODEINFOREF -------------*/
+#define ZNOT_RUNNING 0
+
+/* Type of continue in CONTINUEB ----------------*/
+#define ZREGREQ_TIMELIMIT 0
+#define ZHB_HANDLING 1
+#define ZREGREQ_MASTER_TIMELIMIT 2
+#define ZAPI_HB_HANDLING 3
+#define ZTIMER_HANDLING 4
+#define ZARBIT_HANDLING 5
+
+/* Error Codes ------------------------------*/
+#define ZERRTOOMANY 1101
+#define ZERRALREADYREG 1102
+#define ZERRNHMISSING 1103
+#define ZERRNLMISSING 1104
+#define ZERRAPPMISSING 1105
+#define ZERROR_NOT_IN_CFGFILE 1106
+#define ZERROR_TIMEOUT 1107
+#define ZERROR_NOT_ZINIT 1108
+#define ZERROR_NODEINFOREF 1109
+#define ZERROR_NOTLOCALQMGR 1110
+#define ZERROR_NOTRUNNING 1111
+#define ZCOULD_NOT_OCCUR_ERROR 1112
+#define ZTIME_OUT_ERROR 1113
+#define ZERROR_NOT_DEAD 1114
+#define ZDECLARED_FAIL_ERROR 1115
+#define ZOWN_NODE_ERROR 1116
+#define ZWRONG_STATE_ERROR 1117
+#define ZNODE_ZERO_ERROR 1118
+#define ZWRONG_NODE_ERROR 1119
+
+#endif
+
+
+class Qmgr : public SimulatedBlock {
+public:
+ // State values
+ enum QmgrState {
+ Q_NOT_ACTIVE = 0,
+ Q_ACTIVE = 1
+ };
+
+ enum FailState {
+ NORMAL = 0,
+ WAITING_FOR_FAILCONF1 = 1,
+ WAITING_FOR_FAILCONF2 = 2,
+ WAITING_FOR_NDB_FAILCONF = 3
+ };
+
+ enum Phase {
+ ZINIT = 1, /* All nodes start in phase INIT */
+ ZSTARTING = 2, /* Node is connecting to cluster */
+ ZRUNNING = 3, /* Node is running in the cluster */
+ ZPREPARE_FAIL = 4, /* PREPARATION FOR FAILURE */
+ ZFAIL_CLOSING = 5, /* API/NDB IS DISCONNECTING */
+ ZAPI_ACTIVE = 6, /* API IS RUNNING IN NODE */
+ ZAPI_INACTIVE = 7 /* Inactive API */
+ };
+
+ struct StartRecord {
+ void reset(){ m_startKey++; m_startNode = 0;}
+ Uint32 m_startKey;
+ Uint32 m_startNode;
+ Uint64 m_startTimeout;
+
+ Uint32 m_gsn;
+ SignalCounter m_nodes;
+ } c_start;
+
+ NdbNodeBitmask c_definedNodes; // DB nodes in config
+ NdbNodeBitmask c_clusterNodes; // DB nodes in cluster
+ NodeBitmask c_connectedNodes; // All kinds of connected nodes
+ Uint32 c_maxDynamicId;
+
+ // Records
+ struct NodeRec {
+ UintR ndynamicId;
+ Phase phase;
+ UintR alarmCount;
+
+ QmgrState sendPrepFailReqStatus;
+ QmgrState sendCommitFailReqStatus;
+ QmgrState sendPresToStatus;
+ FailState failState;
+ BlockReference rcv[2]; // remember which failconf we have received
+ BlockReference blockRef;
+
+ NodeRec() { }
+ }; /* p2c: size = 52 bytes */
+
+ typedef Ptr<NodeRec> NodeRecPtr;
+
+ enum ArbitState {
+ ARBIT_NULL = 0,
+ ARBIT_INIT = 1, // create new ticket
+ ARBIT_FIND = 2, // find candidate arbitrator node
+ ARBIT_PREP1 = 3, // PREP db nodes with null ticket
+ ARBIT_PREP2 = 4, // PREP db nodes with current ticket
+ ARBIT_START = 5, // START arbitrator API thread
+ ARBIT_RUN = 6, // running with arbitrator
+ ARBIT_CHOOSE = 7, // ask arbitrator after network partition
+ ARBIT_CRASH = 8 // crash ourselves
+ };
+
+ struct ArbitRec {
+ ArbitState state; // state
+ bool newstate; // flag to initialize new state
+ unsigned thread; // identifies a continueB "thread"
+ NodeId node; // current arbitrator candidate
+ ArbitTicket ticket; // ticket
+ NodeBitmask apiMask[1+2]; // arbitrators 0=all 1,2=per rank
+ NodeBitmask newMask; // new nodes to process in RUN state
+ Uint8 sendCount; // control send/recv of signals
+ Uint8 recvCount;
+ NodeBitmask recvMask; // left to recv
+ Uint32 code; // code field from signal
+ Uint32 failureNr; // cfailureNr at arbitration start
+ Uint32 timeout; // timeout for CHOOSE state
+ NDB_TICKS timestamp; // timestamp for checking timeouts
+
+ inline bool match(ArbitSignalData* sd) {
+ return
+ node == sd->node &&
+ ticket.match(sd->ticket);
+ }
+
+ inline void setTimestamp() {
+ timestamp = NdbTick_CurrentMillisecond();
+ }
+
+ inline NDB_TICKS getTimediff() {
+ NDB_TICKS now = NdbTick_CurrentMillisecond();
+ return now < timestamp ? 0 : now - timestamp;
+ }
+ };
+
+public:
+ Qmgr(const class Configuration &);
+ virtual ~Qmgr();
+
+private:
+ BLOCK_DEFINES(Qmgr);
+
+ // Transit signals
+ void execDEBUG_SIG(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+ void execCM_HEARTBEAT(Signal* signal);
+ void execCM_ADD(Signal* signal);
+ void execCM_ACKADD(Signal* signal);
+ void execCM_REGREQ(Signal* signal);
+ void execCM_REGCONF(Signal* signal);
+ void execCM_REGREF(Signal* signal);
+ void execCM_NODEINFOREQ(Signal* signal);
+ void execCM_NODEINFOCONF(Signal* signal);
+ void execCM_NODEINFOREF(Signal* signal);
+ void execPREP_FAILREQ(Signal* signal);
+ void execPREP_FAILCONF(Signal* signal);
+ void execPREP_FAILREF(Signal* signal);
+ void execCOMMIT_FAILREQ(Signal* signal);
+ void execCOMMIT_FAILCONF(Signal* signal);
+ void execFAIL_REP(Signal* signal);
+ void execPRES_TOREQ(Signal* signal);
+ void execPRES_TOCONF(Signal* signal);
+ void execDISCONNECT_REP(Signal* signal);
+ void execSYSTEM_ERROR(Signal* signal);
+
+ // Received signals
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execCONNECT_REP(Signal* signal);
+ void execNDB_FAILCONF(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execCM_INFOCONF(Signal* signal);
+ void execCLOSE_COMCONF(Signal* signal);
+ void execAPI_REGREQ(Signal* signal);
+ void execAPI_FAILCONF(Signal* signal);
+ void execREAD_NODESREQ(Signal* signal);
+ void execSET_VAR_REQ(Signal* signal);
+
+
+ void execAPI_VERSION_REQ(Signal* signal);
+
+ // Arbitration signals
+ void execARBIT_CFG(Signal* signal);
+ void execARBIT_PREPREQ(Signal* signal);
+ void execARBIT_PREPCONF(Signal* signal);
+ void execARBIT_PREPREF(Signal* signal);
+ void execARBIT_STARTCONF(Signal* signal);
+ void execARBIT_STARTREF(Signal* signal);
+ void execARBIT_CHOOSECONF(Signal* signal);
+ void execARBIT_CHOOSEREF(Signal* signal);
+ void execARBIT_STOPREP(Signal* signal);
+
+ // Statement blocks
+ void node_failed(Signal* signal, Uint16 aFailedNode);
+ void checkStartInterface(Signal* signal);
+ void failReport(Signal* signal,
+ Uint16 aFailedNode,
+ UintR aSendFailRep,
+ FailRep::FailCause failCause);
+ void findNeighbours(Signal* signal);
+ Uint16 translateDynamicIdToNodeId(Signal* signal, UintR TdynamicId);
+
+ void initData(Signal* signal);
+ void sendCloseComReq(Signal* signal, BlockReference TBRef, Uint16 TfailNo);
+ void sendPrepFailReq(Signal* signal, Uint16 aNode);
+ void sendApiFailReq(Signal* signal, Uint16 aFailedNode);
+ void sendApiRegRef(Signal*, Uint32 ref, ApiRegRef::ErrorCode);
+
+ // Generated statement blocks
+ void startphase1(Signal* signal);
+ void electionWon();
+ void cmInfoconf010Lab(Signal* signal);
+ void apiHbHandlingLab(Signal* signal);
+ void timerHandlingLab(Signal* signal);
+ void hbReceivedLab(Signal* signal);
+ void sendCmRegrefLab(Signal* signal, BlockReference ref,
+ CmRegRef::ErrorCode);
+ void systemErrorBecauseOtherNodeFailed(Signal* signal, NodeId);
+ void systemErrorLab(Signal* signal,
+ const char* message = NULL);
+ void prepFailReqLab(Signal* signal);
+ void prepFailConfLab(Signal* signal);
+ void prepFailRefLab(Signal* signal);
+ void commitFailReqLab(Signal* signal);
+ void commitFailConfLab(Signal* signal);
+ void failReportLab(Signal* signal, Uint16 aFailedNode,
+ FailRep::FailCause aFailCause);
+ void sendCommitFailReq(Signal* signal);
+ void presToConfLab(Signal* signal);
+ void sendSttorryLab(Signal* signal);
+ void sttor020Lab(Signal* signal);
+ void closeComConfLab(Signal* signal);
+ void apiRegReqLab(Signal* signal);
+ void regreqTimeLimitLab(Signal* signal);
+ void regreqTimeMasterLimitLab(Signal* signal);
+ void cmRegreq010Lab(Signal* signal);
+ void cmRegconf010Lab(Signal* signal);
+ void sttor010Lab(Signal* signal);
+ void sendHeartbeat(Signal* signal);
+ void checkHeartbeat(Signal* signal);
+ void setHbDelay(UintR aHbDelay);
+ void setHbApiDelay(UintR aHbApiDelay);
+ void setArbitTimeout(UintR aArbitTimeout);
+
+ // Interface to arbitration module
+ void handleArbitStart(Signal* signal);
+ void handleArbitApiFail(Signal* signal, Uint16 nodeId);
+ void handleArbitNdbAdd(Signal* signal, Uint16 nodeId);
+ void handleArbitCheck(Signal* signal);
+
+ // Private arbitration routines
+ Uint32 getArbitDelay();
+ Uint32 getArbitTimeout();
+ void startArbitThread(Signal* signal);
+ void runArbitThread(Signal* signal);
+ void stateArbitInit(Signal* signal);
+ void stateArbitFind(Signal* signal);
+ void stateArbitPrep(Signal* signal);
+ void stateArbitStart(Signal* signal);
+ void stateArbitRun(Signal* signal);
+ void stateArbitChoose(Signal* signal);
+ void stateArbitCrash(Signal* signal);
+ void computeArbitNdbMask(NodeBitmask& aMask);
+ void reportArbitEvent(Signal* signal, Ndb_logevent_type type);
+
+ // Initialisation
+ void initData();
+ void initRecords();
+
+ // Transit signals
+ // Variables
+
+ bool checkAPIVersion(NodeId, Uint32 nodeVersion, Uint32 ownVersion) const;
+ bool checkNDBVersion(NodeId, Uint32 nodeVersion, Uint32 ownVersion) const;
+
+ void cmAddPrepare(Signal* signal, NodeRecPtr nodePtr, const NodeRec* self);
+ void sendCmAckAdd(Signal *, Uint32 nodeId, CmAdd::RequestType);
+ void joinedCluster(Signal* signal, NodeRecPtr nodePtr);
+ void sendCmRegReq(Signal * signal, Uint32 nodeId);
+ void sendCmNodeInfoReq(Signal* signal, Uint32 nodeId, const NodeRec * self);
+
+private:
+ void sendPrepFailReqRef(Signal* signal,
+ Uint32 dstBlockRef,
+ GlobalSignalNumber gsn,
+ Uint32 blockRef,
+ Uint32 failNo,
+ Uint32 noOfNodes,
+ const NodeId theNodes[]);
+
+
+
+ /* Wait this time until we try to join the */
+ /* cluster again */
+
+ /**** Common stored variables ****/
+
+ NodeRec *nodeRec;
+ ArbitRec arbitRec;
+
+ /* Block references ------------------------------*/
+ BlockReference cpdistref; /* Dist. ref of president */
+
+ /* Node numbers. ---------------------------------*/
+ Uint16 cneighbourl; /* Node no. of lower neighbour */
+ Uint16 cneighbourh; /* Node no. of higher neighbour */
+ Uint16 cpresident; /* Node no. of president */
+
+ /* Counters --------------------------------------*/
+ Uint16 cnoOfNodes; /* Static node counter */
+ /* Status flags ----------------------------------*/
+
+ Uint32 c_restartPartialTimeout;
+
+ Uint16 creadyDistCom;
+ Uint16 c_regReqReqSent;
+ Uint16 c_regReqReqRecv;
+ Uint64 c_stopElectionTime;
+ Uint16 cpresidentCandidate;
+ Uint16 cdelayRegreq;
+ Uint16 cpresidentAlive;
+ Uint16 cnoFailedNodes;
+ Uint16 cnoPrepFailedNodes;
+ Uint16 cnoCommitFailedNodes;
+ Uint16 cactivateApiCheck;
+ UintR chbApiDelay;
+
+ UintR ccommitFailureNr;
+ UintR cprepareFailureNr;
+ UintR ctoFailureNr;
+ UintR cfailureNr;
+
+ QmgrState ctoStatus;
+ UintR cLqhTimeSignalCount;
+ bool cHbSent;
+ NDB_TICKS clatestTransactionCheck;
+
+ class Timer interface_check_timer;
+ class Timer hb_check_timer;
+ class Timer hb_send_timer;
+ class Timer hb_api_timer;
+
+
+ Uint16 cfailedNodes[MAX_NDB_NODES];
+ Uint16 cprepFailedNodes[MAX_NDB_NODES];
+ Uint16 ccommitFailedNodes[MAX_NDB_NODES];
+
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp b/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp
new file mode 100644
index 00000000000..ecaeadff47a
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp
@@ -0,0 +1,106 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#define QMGR_C
+#include "Qmgr.hpp"
+
+#define DEBUG(x) { ndbout << "Qmgr::" << x << endl; }
+
+
+void Qmgr::initData()
+{
+ creadyDistCom = ZFALSE;
+
+ // Records with constant sizes
+ nodeRec = new NodeRec[MAX_NODES];
+
+ cnoCommitFailedNodes = 0;
+ c_maxDynamicId = 0;
+ c_clusterNodes.clear();
+
+ Uint32 hbDBAPI = 500;
+ setHbApiDelay(hbDBAPI);
+
+ c_connectedNodes.clear();
+ c_connectedNodes.set(getOwnNodeId());
+}//Qmgr::initData()
+
+void Qmgr::initRecords()
+{
+ // Records with dynamic sizes
+}//Qmgr::initRecords()
+
+Qmgr::Qmgr(const class Configuration & conf)
+ : SimulatedBlock(QMGR, conf)
+{
+ BLOCK_CONSTRUCTOR(Qmgr);
+
+ // Transit signals
+ addRecSignal(GSN_DUMP_STATE_ORD, &Qmgr::execDUMP_STATE_ORD);
+ addRecSignal(GSN_DEBUG_SIG, &Qmgr::execDEBUG_SIG);
+ addRecSignal(GSN_CONTINUEB, &Qmgr::execCONTINUEB);
+ addRecSignal(GSN_CM_HEARTBEAT, &Qmgr::execCM_HEARTBEAT);
+ addRecSignal(GSN_CM_ADD, &Qmgr::execCM_ADD);
+ addRecSignal(GSN_CM_ACKADD, &Qmgr::execCM_ACKADD);
+ addRecSignal(GSN_CM_REGREQ, &Qmgr::execCM_REGREQ);
+ addRecSignal(GSN_CM_REGCONF, &Qmgr::execCM_REGCONF);
+ addRecSignal(GSN_CM_REGREF, &Qmgr::execCM_REGREF);
+ addRecSignal(GSN_CM_NODEINFOREQ, &Qmgr::execCM_NODEINFOREQ);
+ addRecSignal(GSN_CM_NODEINFOCONF, &Qmgr::execCM_NODEINFOCONF);
+ addRecSignal(GSN_CM_NODEINFOREF, &Qmgr::execCM_NODEINFOREF);
+ addRecSignal(GSN_PREP_FAILREQ, &Qmgr::execPREP_FAILREQ);
+ addRecSignal(GSN_PREP_FAILCONF, &Qmgr::execPREP_FAILCONF);
+ addRecSignal(GSN_PREP_FAILREF, &Qmgr::execPREP_FAILREF);
+ addRecSignal(GSN_COMMIT_FAILREQ, &Qmgr::execCOMMIT_FAILREQ);
+ addRecSignal(GSN_COMMIT_FAILCONF, &Qmgr::execCOMMIT_FAILCONF);
+ addRecSignal(GSN_FAIL_REP, &Qmgr::execFAIL_REP);
+ addRecSignal(GSN_PRES_TOREQ, &Qmgr::execPRES_TOREQ);
+ addRecSignal(GSN_PRES_TOCONF, &Qmgr::execPRES_TOCONF);
+
+ // Received signals
+ addRecSignal(GSN_CONNECT_REP, &Qmgr::execCONNECT_REP);
+ addRecSignal(GSN_NDB_FAILCONF, &Qmgr::execNDB_FAILCONF);
+ addRecSignal(GSN_STTOR, &Qmgr::execSTTOR);
+ addRecSignal(GSN_CLOSE_COMCONF, &Qmgr::execCLOSE_COMCONF);
+ addRecSignal(GSN_API_REGREQ, &Qmgr::execAPI_REGREQ);
+ addRecSignal(GSN_API_VERSION_REQ, &Qmgr::execAPI_VERSION_REQ);
+ addRecSignal(GSN_DISCONNECT_REP, &Qmgr::execDISCONNECT_REP);
+ addRecSignal(GSN_API_FAILCONF, &Qmgr::execAPI_FAILCONF);
+ addRecSignal(GSN_READ_NODESREQ, &Qmgr::execREAD_NODESREQ);
+ addRecSignal(GSN_SET_VAR_REQ, &Qmgr::execSET_VAR_REQ);
+
+ // Arbitration signals
+ addRecSignal(GSN_ARBIT_PREPREQ, &Qmgr::execARBIT_PREPREQ);
+ addRecSignal(GSN_ARBIT_PREPCONF, &Qmgr::execARBIT_PREPCONF);
+ addRecSignal(GSN_ARBIT_PREPREF, &Qmgr::execARBIT_PREPREF);
+ addRecSignal(GSN_ARBIT_STARTCONF, &Qmgr::execARBIT_STARTCONF);
+ addRecSignal(GSN_ARBIT_STARTREF, &Qmgr::execARBIT_STARTREF);
+ addRecSignal(GSN_ARBIT_CHOOSECONF, &Qmgr::execARBIT_CHOOSECONF);
+ addRecSignal(GSN_ARBIT_CHOOSEREF, &Qmgr::execARBIT_CHOOSEREF);
+ addRecSignal(GSN_ARBIT_STOPREP, &Qmgr::execARBIT_STOPREP);
+
+ initData();
+}//Qmgr::Qmgr()
+
+Qmgr::~Qmgr()
+{
+ delete []nodeRec;
+}//Qmgr::~Qmgr()
+
+
+BLOCK_FUNCTIONS(Qmgr)
diff --git a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
new file mode 100644
index 00000000000..04373dae93c
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
@@ -0,0 +1,3928 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#define QMGR_C
+#include "Qmgr.hpp"
+#include <pc.hpp>
+#include <NdbTick.h>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/StartOrd.hpp>
+#include <signaldata/CmInit.hpp>
+#include <signaldata/CloseComReqConf.hpp>
+#include <signaldata/PrepFailReqRef.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/CheckNodeGroups.hpp>
+#include <signaldata/ArbitSignalData.hpp>
+#include <signaldata/ApiRegSignalData.hpp>
+#include <signaldata/ApiVersion.hpp>
+#include <signaldata/BlockCommitOrd.hpp>
+#include <signaldata/FailRep.hpp>
+#include <signaldata/DisconnectRep.hpp>
+
+#include <ndb_version.h>
+
+#ifdef DEBUG_ARBIT
+#include <NdbOut.hpp>
+#endif
+
+//#define DEBUG_QMGR_START
+#ifdef DEBUG_QMGR_START
+#include <DebuggerNames.hpp>
+#define DEBUG(x) ndbout << "QMGR " << __LINE__ << ": " << x << endl
+#define DEBUG_START(gsn, node, msg) DEBUG(getSignalName(gsn) << " to: " << node << " - " << msg)
+#define DEBUG_START2(gsn, rg, msg) { char nodes[255]; DEBUG(getSignalName(gsn) << " to: " << rg.m_nodes.getText(nodes) << " - " << msg); }
+#define DEBUG_START3(signal, msg) DEBUG(getSignalName(signal->header.theVerId_signalNumber) << " from " << refToNode(signal->getSendersBlockRef()) << " - " << msg);
+#else
+#define DEBUG(x)
+#define DEBUG_START(gsn, node, msg)
+#define DEBUG_START2(gsn, rg, msg)
+#define DEBUG_START3(signal, msg)
+#endif
+
+// Signal entries and statement blocks
+/* 4 P R O G R A M */
+/*******************************/
+/* CMHEART_BEAT */
+/*******************************/
+void Qmgr::execCM_HEARTBEAT(Signal* signal)
+{
+ NodeRecPtr hbNodePtr;
+ jamEntry();
+ hbNodePtr.i = signal->theData[0];
+ ptrCheckGuard(hbNodePtr, MAX_NDB_NODES, nodeRec);
+ hbNodePtr.p->alarmCount = 0;
+ return;
+}//Qmgr::execCM_HEARTBEAT()
+
+/*******************************/
+/* CM_NODEINFOREF */
+/*******************************/
+void Qmgr::execCM_NODEINFOREF(Signal* signal)
+{
+ jamEntry();
+ systemErrorLab(signal);
+ return;
+}//Qmgr::execCM_NODEINFOREF()
+
+/*******************************/
+/* CONTINUEB */
+/*******************************/
+void Qmgr::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ const Uint32 tcontinuebType = signal->theData[0];
+ const Uint32 tdata0 = signal->theData[1];
+ const Uint32 tdata1 = signal->theData[2];
+ switch (tcontinuebType) {
+ case ZREGREQ_TIMELIMIT:
+ jam();
+ if (c_start.m_startKey != tdata0 || c_start.m_startNode != tdata1) {
+ jam();
+ return;
+ }//if
+ regreqTimeLimitLab(signal);
+ break;
+ case ZREGREQ_MASTER_TIMELIMIT:
+ jam();
+ if (c_start.m_startKey != tdata0 || c_start.m_startNode != tdata1) {
+ jam();
+ return;
+ }//if
+ //regreqMasterTimeLimitLab(signal);
+ failReportLab(signal, c_start.m_startNode, FailRep::ZSTART_IN_REGREQ);
+ return;
+ break;
+ case ZTIMER_HANDLING:
+ jam();
+ timerHandlingLab(signal);
+ return;
+ break;
+ case ZARBIT_HANDLING:
+ jam();
+ runArbitThread(signal);
+ return;
+ break;
+ default:
+ jam();
+ // ZCOULD_NOT_OCCUR_ERROR;
+ systemErrorLab(signal);
+ return;
+ break;
+ }//switch
+ return;
+}//Qmgr::execCONTINUEB()
+
+
+void Qmgr::execDEBUG_SIG(Signal* signal)
+{
+ NodeRecPtr debugNodePtr;
+ jamEntry();
+ debugNodePtr.i = signal->theData[0];
+ ptrCheckGuard(debugNodePtr, MAX_NODES, nodeRec);
+ return;
+}//Qmgr::execDEBUG_SIG()
+
+/*******************************/
+/* FAIL_REP */
+/*******************************/
+void Qmgr::execFAIL_REP(Signal* signal)
+{
+ const FailRep * const failRep = (FailRep *)&signal->theData[0];
+ const NodeId failNodeId = failRep->failNodeId;
+ const FailRep::FailCause failCause = (FailRep::FailCause)failRep->failCause;
+
+ jamEntry();
+ failReportLab(signal, failNodeId, failCause);
+ return;
+}//Qmgr::execFAIL_REP()
+
+/*******************************/
+/* PRES_TOREQ */
+/*******************************/
+void Qmgr::execPRES_TOREQ(Signal* signal)
+{
+ jamEntry();
+ BlockReference Tblockref = signal->theData[0];
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = ccommitFailureNr;
+ sendSignal(Tblockref, GSN_PRES_TOCONF, signal, 2, JBA);
+ return;
+}//Qmgr::execPRES_TOREQ()
+
+/*
+4.2 ADD NODE MODULE*/
+/*##########################################################################*/
+/*
+4.2.1 STTOR */
+/**--------------------------------------------------------------------------
+ * Start phase signal, must be handled by all blocks.
+ * QMGR is only interested in the first phase.
+ * During phase one we clear all registered applications.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* STTOR */
+/*******************************/
+void Qmgr::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ switch(signal->theData[1]){
+ case 1:
+ initData(signal);
+ startphase1(signal);
+ return;
+ case 7:
+ cactivateApiCheck = 1;
+ /**
+ * Start arbitration thread. This could be done as soon as
+ * we have all nodes (or a winning majority).
+ */
+ if (cpresident == getOwnNodeId())
+ handleArbitStart(signal);
+ break;
+ }
+
+ sendSttorryLab(signal);
+ return;
+}//Qmgr::execSTTOR()
+
+void Qmgr::sendSttorryLab(Signal* signal)
+{
+/****************************<*/
+/*< STTORRY <*/
+/****************************<*/
+ signal->theData[3] = 7;
+ signal->theData[4] = 255;
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+ return;
+}//Qmgr::sendSttorryLab()
+
+void Qmgr::startphase1(Signal* signal)
+{
+ jamEntry();
+
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrAss(nodePtr, nodeRec);
+ nodePtr.p->phase = ZSTARTING;
+ nodePtr.p->blockRef = reference();
+ c_connectedNodes.set(nodePtr.i);
+
+ signal->theData[0] = 0; // no answer
+ signal->theData[1] = 0; // no id
+ signal->theData[2] = NodeInfo::DB;
+ sendSignal(CMVMI_REF, GSN_OPEN_COMREQ, signal, 3, JBB);
+
+ execCM_INFOCONF(signal);
+ return;
+}
+
+void Qmgr::setHbDelay(UintR aHbDelay)
+{
+ hb_send_timer.setDelay(aHbDelay < 10 ? 10 : aHbDelay);
+ hb_send_timer.reset();
+ hb_check_timer.setDelay(aHbDelay < 10 ? 10 : aHbDelay);
+ hb_check_timer.reset();
+}
+
+void Qmgr::setHbApiDelay(UintR aHbApiDelay)
+{
+ chbApiDelay = (aHbApiDelay < 100 ? 100 : aHbApiDelay);
+ hb_api_timer.setDelay(chbApiDelay);
+ hb_api_timer.reset();
+}
+
+void Qmgr::setArbitTimeout(UintR aArbitTimeout)
+{
+ arbitRec.timeout = (aArbitTimeout < 10 ? 10 : aArbitTimeout);
+}
+
+void Qmgr::execCONNECT_REP(Signal* signal)
+{
+ const Uint32 nodeId = signal->theData[0];
+ c_connectedNodes.set(nodeId);
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrCheckGuard(nodePtr, MAX_NODES, nodeRec);
+ switch(nodePtr.p->phase){
+ case ZSTARTING:
+ jam();
+ break;
+ case ZRUNNING:
+ case ZPREPARE_FAIL:
+ case ZFAIL_CLOSING:
+ jam();
+ return;
+ case ZINIT:
+ ndbrequire(false);
+ case ZAPI_ACTIVE:
+ case ZAPI_INACTIVE:
+ return;
+ }
+
+ if(!c_start.m_nodes.isWaitingFor(nodeId)){
+ jam();
+ return;
+ }
+
+ switch(c_start.m_gsn){
+ case GSN_CM_REGREQ:
+ jam();
+ sendCmRegReq(signal, nodeId);
+ return;
+ case GSN_CM_NODEINFOREQ:{
+ jam();
+ sendCmNodeInfoReq(signal, nodeId, nodePtr.p);
+ return;
+ }
+ default:
+ return;
+ }
+ return;
+}//Qmgr::execCONNECT_REP()
+
+/*******************************/
+/* CM_INFOCONF */
+/*******************************/
+void Qmgr::execCM_INFOCONF(Signal* signal)
+{
+ cpresident = ZNIL;
+ cpresidentCandidate = getOwnNodeId();
+ cpresidentAlive = ZFALSE;
+ c_stopElectionTime = NdbTick_CurrentMillisecond();
+ c_stopElectionTime += c_restartPartialTimeout;
+ cmInfoconf010Lab(signal);
+
+ return;
+}//Qmgr::execCM_INFOCONF()
+
+void Qmgr::cmInfoconf010Lab(Signal* signal)
+{
+ c_start.m_startKey = 0;
+ c_start.m_startNode = getOwnNodeId();
+ c_start.m_nodes.clearWaitingFor();
+ c_start.m_gsn = GSN_CM_REGREQ;
+
+ NodeRecPtr nodePtr;
+ c_regReqReqSent = c_regReqReqRecv = 0;
+ cnoOfNodes = 0;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+
+ if(getNodeInfo(nodePtr.i).getType() != NodeInfo::DB)
+ continue;
+
+ c_start.m_nodes.setWaitingFor(nodePtr.i);
+ cnoOfNodes++;
+
+ if(!c_connectedNodes.get(nodePtr.i))
+ continue;
+
+ sendCmRegReq(signal, nodePtr.i);
+ }
+
+ //----------------------------------------
+ /* Wait for a while. When it returns */
+ /* we will check if we got any CM_REGREF*/
+ /* or CM_REGREQ (lower nodeid than our */
+ /* own). */
+ //----------------------------------------
+ signal->theData[0] = ZREGREQ_TIMELIMIT;
+ signal->theData[1] = c_start.m_startKey;
+ signal->theData[2] = c_start.m_startNode;
+ sendSignalWithDelay(QMGR_REF, GSN_CONTINUEB, signal, 3000, 3);
+
+ creadyDistCom = ZTRUE;
+ return;
+}//Qmgr::cmInfoconf010Lab()
+
+void
+Qmgr::sendCmRegReq(Signal * signal, Uint32 nodeId){
+ c_regReqReqSent++;
+ CmRegReq * const cmRegReq = (CmRegReq *)&signal->theData[0];
+ cmRegReq->blockRef = reference();
+ cmRegReq->nodeId = getOwnNodeId();
+ cmRegReq->version = NDB_VERSION;
+ const Uint32 ref = calcQmgrBlockRef(nodeId);
+ sendSignal(ref, GSN_CM_REGREQ, signal, CmRegReq::SignalLength, JBB);
+ DEBUG_START(GSN_CM_REGREQ, nodeId, "");
+}
+
+/*
+4.4.11 CM_REGREQ */
+/**--------------------------------------------------------------------------
+ * If this signal is received someone tries to get registrated.
+ * Only the president have the authority make decissions about new nodes,
+ * so only a president or a node that claims to be the president may send a
+ * reply to this signal.
+ * This signal can occur any time after that STTOR was received.
+ * CPRESIDENT: Timelimit has expired and someone has
+ * decided to enter the president role
+ * CPRESIDENT_CANDIDATE:
+ * Assigned when we receive a CM_REGREF, if we got more than one REF
+ * then we always keep the lowest nodenumber.
+ * We accept this nodeno as president when our timelimit expires
+ * We should consider the following cases:
+ * 1- We are the president. If we are busy by adding new nodes to cluster,
+ * then we have to refuse this node to be added.
+ * The refused node will try in ZREFUSE_ADD_TIME seconds again.
+ * If we are not busy then we confirm
+ *
+ * 2- We know the president, we dont bother us about this REQ.
+ * The president has also got this REQ and will take care of it.
+ *
+ * 3- The president are not known. We have received CM_INIT, so we compare the
+ * senders node number to GETOWNNODEID().
+ * If we have a lower number than the sender then we will claim
+ * that we are the president so we send him a refuse signal back.
+ * We have to wait for the CONTINUEB signal before we can enter the
+ * president role. If our GETOWNNODEID() if larger than sender node number,
+ * we are not the president and just have to wait for the
+ * reply signal (REF) to our CM_REGREQ_2.
+ * 4- We havent received the CM_INIT signal so we don't know who we are.
+ * Ignore the request.
+ *--------------------------------------------------------------------------*/
+/*******************************/
+/* CM_REGREQ */
+/*******************************/
+void Qmgr::execCM_REGREQ(Signal* signal)
+{
+ DEBUG_START3(signal, "");
+
+ NodeRecPtr addNodePtr;
+ jamEntry();
+
+ CmRegReq * const cmRegReq = (CmRegReq *)&signal->theData[0];
+ const BlockReference Tblockref = cmRegReq->blockRef;
+ const Uint32 startingVersion = cmRegReq->version;
+ addNodePtr.i = cmRegReq->nodeId;
+
+ if (creadyDistCom == ZFALSE) {
+ jam();
+ /* NOT READY FOR DISTRIBUTED COMMUNICATION.*/
+ return;
+ }//if
+
+ if (!ndbCompatible_ndb_ndb(NDB_VERSION, startingVersion)) {
+ jam();
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZINCOMPATIBLE_VERSION);
+ return;
+ }
+
+ ptrCheckGuard(addNodePtr, MAX_NDB_NODES, nodeRec);
+
+ if (cpresident != getOwnNodeId()){
+ jam();
+ if (cpresident == ZNIL) {
+ /***
+ * We don't know the president.
+ * If the node to be added has lower node id
+ * than our president cancidate. Set it as
+ * candidate
+ */
+ jam();
+ if (addNodePtr.i < cpresidentCandidate) {
+ jam();
+ cpresidentCandidate = addNodePtr.i;
+ }//if
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZELECTION);
+ return;
+ }
+ /**
+ * We are not the president.
+ * We know the president.
+ * President will answer.
+ */
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZNOT_PRESIDENT);
+ return;
+ }//if
+
+ if (c_start.m_startNode != 0){
+ jam();
+ /**
+ * President busy by adding another node
+ */
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZBUSY_PRESIDENT);
+ return;
+ }//if
+
+ if (ctoStatus == Q_ACTIVE) {
+ jam();
+ /**
+ * Active taking over as president
+ */
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZBUSY_TO_PRES);
+ return;
+ }//if
+
+ if (getNodeInfo(addNodePtr.i).m_type != NodeInfo::DB) {
+ jam();
+ /**
+ * The new node is not in config file
+ */
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZNOT_IN_CFG);
+ return;
+ }
+
+ Phase phase = addNodePtr.p->phase;
+ if (phase != ZINIT){
+ jam();
+ DEBUG("phase = " << phase);
+ sendCmRegrefLab(signal, Tblockref, CmRegRef::ZNOT_DEAD);
+ return;
+ }//if
+
+ jam();
+ /**
+ * WE ARE PRESIDENT AND WE ARE NOT BUSY ADDING ANOTHER NODE.
+ * WE WILL TAKE CARE OF THE INCLUSION OF THIS NODE INTO THE CLUSTER.
+ * WE NEED TO START TIME SUPERVISION OF THIS. SINCE WE CANNOT STOP
+ * TIMED SIGNAL IF THE INCLUSION IS INTERRUPTED WE IDENTIFY
+ * EACH INCLUSION WITH A UNIQUE IDENTITY. THIS IS CHECKED WHEN
+ * THE SIGNAL ARRIVES. IF IT HAS CHANGED THEN WE SIMPLY IGNORE
+ * THE TIMED SIGNAL.
+ */
+
+ /**
+ * Update start record
+ */
+ c_start.m_startKey++;
+ c_start.m_startNode = addNodePtr.i;
+
+ /**
+ * Assign dynamic id
+ */
+ UintR TdynId = ++c_maxDynamicId;
+ setNodeInfo(addNodePtr.i).m_version = startingVersion;
+ addNodePtr.p->ndynamicId = TdynId;
+
+ /**
+ * Reply with CM_REGCONF
+ */
+ CmRegConf * const cmRegConf = (CmRegConf *)&signal->theData[0];
+ cmRegConf->presidentBlockRef = reference();
+ cmRegConf->presidentNodeId = getOwnNodeId();
+ cmRegConf->presidentVersion = getNodeInfo(getOwnNodeId()).m_version;
+ cmRegConf->dynamicId = TdynId;
+ c_clusterNodes.copyto(NdbNodeBitmask::Size, cmRegConf->allNdbNodes);
+ sendSignal(Tblockref, GSN_CM_REGCONF, signal,
+ CmRegConf::SignalLength, JBA);
+ DEBUG_START(GSN_CM_REGCONF, refToNode(Tblockref), "");
+
+ /**
+ * Send CmAdd to all nodes (including starting)
+ */
+ c_start.m_nodes = c_clusterNodes;
+ c_start.m_nodes.setWaitingFor(addNodePtr.i);
+ c_start.m_gsn = GSN_CM_ADD;
+
+ NodeReceiverGroup rg(QMGR, c_start.m_nodes);
+ CmAdd * const cmAdd = (CmAdd*)signal->getDataPtrSend();
+ cmAdd->requestType = CmAdd::Prepare;
+ cmAdd->startingNodeId = addNodePtr.i;
+ cmAdd->startingVersion = startingVersion;
+ sendSignal(rg, GSN_CM_ADD, signal, CmAdd::SignalLength, JBA);
+ DEBUG_START2(GSN_CM_ADD, rg, "Prepare");
+
+ /**
+ * Set timer
+ */
+ return;
+ signal->theData[0] = ZREGREQ_MASTER_TIMELIMIT;
+ signal->theData[1] = c_start.m_startKey;
+ sendSignalWithDelay(QMGR_REF, GSN_CONTINUEB, signal, 30000, 2);
+
+ return;
+}//Qmgr::execCM_REGREQ()
+
+void Qmgr::sendCmRegrefLab(Signal* signal, BlockReference TBRef,
+ CmRegRef::ErrorCode Terror)
+{
+ CmRegRef* ref = (CmRegRef*)signal->getDataPtrSend();
+ ref->blockRef = reference();
+ ref->nodeId = getOwnNodeId();
+ ref->errorCode = Terror;
+ ref->presidentCandidate = (cpresident == ZNIL ? cpresidentCandidate : cpresident);
+ sendSignal(TBRef, GSN_CM_REGREF, signal,
+ CmRegRef::SignalLength, JBB);
+ DEBUG_START(GSN_CM_REGREF, refToNode(TBRef), "");
+ return;
+}//Qmgr::sendCmRegrefLab()
+
+/*
+4.4.11 CM_REGCONF */
+/**--------------------------------------------------------------------------
+ * President gives permission to a node which wants to join the cluster.
+ * The president will prepare the cluster that a new node will be added to
+ * cluster. When the new node has set up all connections to the cluster,
+ * the president will send commit to all clusternodes so the phase of the
+ * new node can be changed to ZRUNNING.
+ *--------------------------------------------------------------------------*/
+/*******************************/
+/* CM_REGCONF */
+/*******************************/
+void Qmgr::execCM_REGCONF(Signal* signal)
+{
+ DEBUG_START3(signal, "");
+
+ NodeRecPtr myNodePtr;
+ NodeRecPtr nodePtr;
+ jamEntry();
+
+ const CmRegConf * const cmRegConf = (CmRegConf *)&signal->theData[0];
+
+ if (!ndbCompatible_ndb_ndb(NDB_VERSION, cmRegConf->presidentVersion)) {
+ jam();
+ char buf[128];
+ BaseString::snprintf(buf,sizeof(buf),"incompatible version own=0x%x other=0x%x, shutting down", NDB_VERSION, cmRegConf->presidentVersion);
+ systemErrorLab(signal, buf);
+ return;
+ }
+
+
+ cpdistref = cmRegConf->presidentBlockRef;
+ cpresident = cmRegConf->presidentNodeId;
+ UintR TdynamicId = cmRegConf->dynamicId;
+ c_maxDynamicId = TdynamicId;
+ c_clusterNodes.assign(NdbNodeBitmask::Size, cmRegConf->allNdbNodes);
+
+/*--------------------------------------------------------------*/
+// Send this as an EVENT REPORT to inform about hearing about
+// other NDB node proclaiming to be president.
+/*--------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_CM_REGCONF;
+ signal->theData[1] = getOwnNodeId();
+ signal->theData[2] = cpresident;
+ signal->theData[3] = TdynamicId;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+
+ myNodePtr.i = getOwnNodeId();
+ ptrCheckGuard(myNodePtr, MAX_NDB_NODES, nodeRec);
+ myNodePtr.p->ndynamicId = TdynamicId;
+
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ if (c_clusterNodes.get(nodePtr.i)){
+ jam();
+ ptrAss(nodePtr, nodeRec);
+
+ ndbrequire(nodePtr.p->phase == ZINIT);
+ nodePtr.p->phase = ZRUNNING;
+
+ if(c_connectedNodes.get(nodePtr.i)){
+ jam();
+ sendCmNodeInfoReq(signal, nodePtr.i, myNodePtr.p);
+ }
+ }
+ }
+
+ c_start.m_gsn = GSN_CM_NODEINFOREQ;
+ c_start.m_nodes = c_clusterNodes;
+
+ return;
+}//Qmgr::execCM_REGCONF()
+
+void
+Qmgr::sendCmNodeInfoReq(Signal* signal, Uint32 nodeId, const NodeRec * self){
+ CmNodeInfoReq * const req = (CmNodeInfoReq*)signal->getDataPtrSend();
+ req->nodeId = getOwnNodeId();
+ req->dynamicId = self->ndynamicId;
+ req->version = getNodeInfo(getOwnNodeId()).m_version;
+ const Uint32 ref = calcQmgrBlockRef(nodeId);
+ sendSignal(ref,GSN_CM_NODEINFOREQ, signal, CmNodeInfoReq::SignalLength, JBB);
+ DEBUG_START(GSN_CM_NODEINFOREQ, nodeId, "");
+}
+
+/*
+4.4.11 CM_REGREF */
+/**--------------------------------------------------------------------------
+ * Only a president or a president candidate can refuse a node to get added to
+ * the cluster.
+ * Refuse reasons:
+ * ZBUSY We know that the sender is the president and we have to
+ * make a new CM_REGREQ.
+ * ZNOT_IN_CFG This node number is not specified in the configfile,
+ * SYSTEM ERROR
+ * ZELECTION Sender is a president candidate, his timelimit
+ * hasn't expired so maybe someone else will show up.
+ * Update the CPRESIDENT_CANDIDATE, then wait for our
+ * timelimit to expire.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* CM_REGREF */
+/*******************************/
+void Qmgr::execCM_REGREF(Signal* signal)
+{
+ jamEntry();
+ c_regReqReqRecv++;
+
+ // Ignore block reference in data[0]
+ UintR TaddNodeno = signal->theData[1];
+ UintR TrefuseReason = signal->theData[2];
+ Uint32 candidate = signal->theData[3];
+ DEBUG_START3(signal, TrefuseReason);
+
+ if(candidate != cpresidentCandidate){
+ jam();
+ c_regReqReqRecv = ~0;
+ }
+
+ switch (TrefuseReason) {
+ case CmRegRef::ZINCOMPATIBLE_VERSION:
+ jam();
+ systemErrorLab(signal, "incompatible version, connection refused by running ndb node");
+ break;
+ case CmRegRef::ZBUSY:
+ case CmRegRef::ZBUSY_TO_PRES:
+ case CmRegRef::ZBUSY_PRESIDENT:
+ jam();
+ cpresidentAlive = ZTRUE;
+ signal->theData[3] = 0;
+ break;
+ case CmRegRef::ZNOT_IN_CFG:
+ jam();
+ progError(__LINE__, ERR_NODE_NOT_IN_CONFIG);
+ break;
+ case CmRegRef::ZNOT_DEAD:
+ jam();
+ progError(__LINE__, ERR_NODE_NOT_DEAD);
+ break;
+ case CmRegRef::ZELECTION:
+ jam();
+ if (cpresidentCandidate > TaddNodeno) {
+ jam();
+ //----------------------------------------
+ /* We may already have a candidate */
+ /* choose the lowest nodeno */
+ //----------------------------------------
+ signal->theData[3] = 2;
+ cpresidentCandidate = TaddNodeno;
+ } else {
+ signal->theData[3] = 4;
+ }//if
+ break;
+ case CmRegRef::ZNOT_PRESIDENT:
+ jam();
+ cpresidentAlive = ZTRUE;
+ signal->theData[3] = 3;
+ break;
+ default:
+ jam();
+ signal->theData[3] = 5;
+ /*empty*/;
+ break;
+ }//switch
+/*--------------------------------------------------------------*/
+// Send this as an EVENT REPORT to inform about hearing about
+// other NDB node proclaiming not to be president.
+/*--------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_CM_REGREF;
+ signal->theData[1] = getOwnNodeId();
+ signal->theData[2] = TaddNodeno;
+//-----------------------------------------
+// signal->theData[3] filled in above
+//-----------------------------------------
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+
+ if(cpresidentAlive == ZTRUE){
+ jam();
+ DEBUG("");
+ return;
+ }
+
+ if(c_regReqReqSent != c_regReqReqRecv){
+ jam();
+ DEBUG( c_regReqReqSent << " != " << c_regReqReqRecv);
+ return;
+ }
+
+ if(cpresidentCandidate != getOwnNodeId()){
+ jam();
+ DEBUG("");
+ return;
+ }
+
+ /**
+ * All configured nodes has agreed
+ */
+ Uint64 now = NdbTick_CurrentMillisecond();
+ if((c_regReqReqRecv == cnoOfNodes) || now > c_stopElectionTime){
+ jam();
+ electionWon();
+ sendSttorryLab(signal);
+
+ /**
+ * Start timer handling
+ */
+ signal->theData[0] = ZTIMER_HANDLING;
+ sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 10, JBB);
+ }
+
+ return;
+}//Qmgr::execCM_REGREF()
+
+void
+Qmgr::electionWon(){
+ NodeRecPtr myNodePtr;
+ cpresident = getOwnNodeId(); /* This node becomes president. */
+ myNodePtr.i = getOwnNodeId();
+ ptrCheckGuard(myNodePtr, MAX_NDB_NODES, nodeRec);
+
+ myNodePtr.p->phase = ZRUNNING;
+
+ cpdistref = reference();
+ cneighbourl = ZNIL;
+ cneighbourh = ZNIL;
+ myNodePtr.p->ndynamicId = 1;
+ c_maxDynamicId = 1;
+ c_clusterNodes.clear();
+ c_clusterNodes.set(getOwnNodeId());
+
+ cpresidentAlive = ZTRUE;
+ c_stopElectionTime = ~0;
+ c_start.reset();
+}
+
+/*
+4.4.11 CONTINUEB */
+/*--------------------------------------------------------------------------*/
+/* */
+/*--------------------------------------------------------------------------*/
+/****************************>---------------------------------------------*/
+/* CONTINUEB > SENDER: Own block, Own node */
+/****************************>-------+INPUT : TCONTINUEB_TYPE */
+/*--------------------------------------------------------------*/
+void Qmgr::regreqTimeLimitLab(Signal* signal)
+{
+ if(cpresident == ZNIL){
+ cmInfoconf010Lab(signal);
+ }
+}//Qmgr::regreqTimelimitLab()
+
+/**---------------------------------------------------------------------------
+ * The new node will take care of giving information about own node and ask
+ * all other nodes for nodeinfo. The new node will use CM_NODEINFOREQ for
+ * that purpose. When the setup of connections to all running, the president
+ * will send a commit to all running nodes + the new node
+ * INPUT: NODE_PTR1, must be set as ZNIL if we don't enter CONNECT_NODES)
+ * from signal CM_NODEINFOCONF.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* CM_NODEINFOCONF */
+/*******************************/
+void Qmgr::execCM_NODEINFOCONF(Signal* signal)
+{
+ DEBUG_START3(signal, "");
+
+ jamEntry();
+
+ CmNodeInfoConf * const conf = (CmNodeInfoConf*)signal->getDataPtr();
+
+ const Uint32 nodeId = conf->nodeId;
+ const Uint32 dynamicId = conf->dynamicId;
+ const Uint32 version = conf->version;
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrAss(nodePtr, nodeRec);
+ ndbrequire(nodePtr.p->phase == ZSTARTING);
+ ndbrequire(c_start.m_gsn == GSN_CM_NODEINFOREQ);
+ c_start.m_nodes.clearWaitingFor(nodeId);
+
+ /**
+ * Update node info
+ */
+ NodeRecPtr replyNodePtr;
+ replyNodePtr.i = nodeId;
+ ptrCheckGuard(replyNodePtr, MAX_NDB_NODES, nodeRec);
+ replyNodePtr.p->ndynamicId = dynamicId;
+ replyNodePtr.p->blockRef = signal->getSendersBlockRef();
+ setNodeInfo(replyNodePtr.i).m_version = version;
+
+ if(!c_start.m_nodes.done()){
+ jam();
+ return;
+ }
+
+ /**********************************************<*/
+ /* Send an ack. back to the president. */
+ /* CM_ACKADD */
+ /* The new node has been registered by all */
+ /* running nodes and has stored nodeinfo about */
+ /* all running nodes. The new node has to wait */
+ /* for CM_ADD (commit) from president to become */
+ /* a running node in the cluster. */
+ /**********************************************<*/
+ sendCmAckAdd(signal, getOwnNodeId(), CmAdd::Prepare);
+ return;
+}//Qmgr::execCM_NODEINFOCONF()
+
+/**---------------------------------------------------------------------------
+ * A new node sends nodeinfo about himself. The new node asks for
+ * corresponding nodeinfo back in the CM_NODEINFOCONF.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* CM_NODEINFOREQ */
+/*******************************/
+void Qmgr::execCM_NODEINFOREQ(Signal* signal)
+{
+ jamEntry();
+
+ const Uint32 Tblockref = signal->getSendersBlockRef();
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrAss(nodePtr, nodeRec);
+ if(nodePtr.p->phase != ZRUNNING){
+ jam();
+ signal->theData[0] = reference();
+ signal->theData[1] = getOwnNodeId();
+ signal->theData[2] = ZNOT_RUNNING;
+ sendSignal(Tblockref, GSN_CM_NODEINFOREF, signal, 3, JBB);
+ return;
+ }
+
+ NodeRecPtr addNodePtr;
+ CmNodeInfoReq * const req = (CmNodeInfoReq*)signal->getDataPtr();
+ addNodePtr.i = req->nodeId;
+ ptrCheckGuard(addNodePtr, MAX_NDB_NODES, nodeRec);
+ addNodePtr.p->ndynamicId = req->dynamicId;
+ addNodePtr.p->blockRef = signal->getSendersBlockRef();
+ setNodeInfo(addNodePtr.i).m_version = req->version;
+ c_maxDynamicId = req->dynamicId;
+
+ cmAddPrepare(signal, addNodePtr, nodePtr.p);
+}//Qmgr::execCM_NODEINFOREQ()
+
+void
+Qmgr::cmAddPrepare(Signal* signal, NodeRecPtr nodePtr, const NodeRec * self){
+ jam();
+
+ switch(nodePtr.p->phase){
+ case ZINIT:
+ jam();
+ nodePtr.p->phase = ZSTARTING;
+ return;
+ case ZFAIL_CLOSING:
+ jam();
+#ifdef VM_TRACE
+ ndbout_c("Enabling communication to CM_ADD node state=%d",
+ nodePtr.p->phase);
+#endif
+ nodePtr.p->phase = ZSTARTING;
+ nodePtr.p->failState = NORMAL;
+ signal->theData[0] = 0;
+ signal->theData[1] = nodePtr.i;
+ sendSignal(CMVMI_REF, GSN_OPEN_COMREQ, signal, 2, JBA);
+ return;
+ case ZSTARTING:
+ break;
+ case ZRUNNING:
+ case ZPREPARE_FAIL:
+ case ZAPI_ACTIVE:
+ case ZAPI_INACTIVE:
+ ndbrequire(false);
+ }
+
+ sendCmAckAdd(signal, nodePtr.i, CmAdd::Prepare);
+
+ /* President have prepared us */
+ CmNodeInfoConf * conf = (CmNodeInfoConf*)signal->getDataPtrSend();
+ conf->nodeId = getOwnNodeId();
+ conf->dynamicId = self->ndynamicId;
+ conf->version = getNodeInfo(getOwnNodeId()).m_version;
+ sendSignal(nodePtr.p->blockRef, GSN_CM_NODEINFOCONF, signal,
+ CmNodeInfoConf::SignalLength, JBB);
+ DEBUG_START(GSN_CM_NODEINFOCONF, refToNode(nodePtr.p->blockRef), "");
+}
+
+void
+Qmgr::sendCmAckAdd(Signal * signal, Uint32 nodeId, CmAdd::RequestType type){
+
+ CmAckAdd * cmAckAdd = (CmAckAdd*)signal->getDataPtrSend();
+ cmAckAdd->requestType = type;
+ cmAckAdd->startingNodeId = nodeId;
+ cmAckAdd->senderNodeId = getOwnNodeId();
+ sendSignal(cpdistref, GSN_CM_ACKADD, signal, CmAckAdd::SignalLength, JBA);
+ DEBUG_START(GSN_CM_ACKADD, cpresident, "");
+
+ switch(type){
+ case CmAdd::Prepare:
+ return;
+ case CmAdd::AddCommit:
+ case CmAdd::CommitNew:
+ break;
+ }
+
+ signal->theData[0] = nodeId;
+ EXECUTE_DIRECT(NDBCNTR, GSN_CM_ADD_REP, signal, 1);
+ jamEntry();
+}
+
+/*
+4.4.11 CM_ADD */
+/**--------------------------------------------------------------------------
+ * Prepare a running node to add a new node to the cluster. The running node
+ * will change phase of the new node fron ZINIT to ZWAITING. The running node
+ * will also mark that we have received a prepare. When the new node has sent
+ * us nodeinfo we can send an acknowledgement back to the president. When all
+ * running nodes has acknowledged the new node, the president will send a
+ * commit and we can change phase of the new node to ZRUNNING. The president
+ * will also send CM_ADD to himself.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* CM_ADD */
+/*******************************/
+void Qmgr::execCM_ADD(Signal* signal)
+{
+ NodeRecPtr addNodePtr;
+ jamEntry();
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+
+ CmAdd * const cmAdd = (CmAdd*)signal->getDataPtr();
+ const CmAdd::RequestType type = (CmAdd::RequestType)cmAdd->requestType;
+ addNodePtr.i = cmAdd->startingNodeId;
+ //const Uint32 startingVersion = cmAdd->startingVersion;
+ ptrCheckGuard(addNodePtr, MAX_NDB_NODES, nodeRec);
+
+ DEBUG_START3(signal, type);
+
+ if(nodePtr.p->phase == ZSTARTING){
+ jam();
+ /**
+ * We are joining...
+ */
+ ndbrequire(addNodePtr.i == nodePtr.i);
+ switch(type){
+ case CmAdd::Prepare:
+ ndbrequire(c_start.m_gsn == GSN_CM_NODEINFOREQ);
+ /**
+ * Wait for CM_NODEINFO_CONF
+ */
+ return;
+ case CmAdd::CommitNew:
+ /**
+ * Tata. we're in the cluster
+ */
+ joinedCluster(signal, addNodePtr);
+ return;
+ case CmAdd::AddCommit:
+ ndbrequire(false);
+ }
+ }
+
+ switch (type) {
+ case CmAdd::Prepare:
+ cmAddPrepare(signal, addNodePtr, nodePtr.p);
+ break;
+ case CmAdd::AddCommit:{
+ jam();
+ ndbrequire(addNodePtr.p->phase == ZSTARTING);
+ addNodePtr.p->phase = ZRUNNING;
+ addNodePtr.p->alarmCount = 0;
+ c_clusterNodes.set(addNodePtr.i);
+ findNeighbours(signal);
+
+ /**
+ * SEND A HEARTBEAT IMMEDIATELY TO DECREASE THE RISK THAT WE MISS EARLY
+ * HEARTBEATS.
+ */
+ sendHeartbeat(signal);
+
+ /**
+ * ENABLE COMMUNICATION WITH ALL BLOCKS WITH THE NEWLY ADDED NODE
+ */
+ signal->theData[0] = addNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_ENABLE_COMORD, signal, 1, JBA);
+
+ sendCmAckAdd(signal, addNodePtr.i, CmAdd::AddCommit);
+ if(getOwnNodeId() != cpresident){
+ jam();
+ c_start.reset();
+ }
+ break;
+ }
+ case CmAdd::CommitNew:
+ jam();
+ ndbrequire(false);
+ }
+
+}//Qmgr::execCM_ADD()
+
+void
+Qmgr::joinedCluster(Signal* signal, NodeRecPtr nodePtr){
+ /**
+ * WE HAVE BEEN INCLUDED IN THE CLUSTER WE CAN START BEING PART OF THE
+ * HEARTBEAT PROTOCOL AND WE WILL ALSO ENABLE COMMUNICATION WITH ALL
+ * NODES IN THE CLUSTER.
+ */
+ nodePtr.p->phase = ZRUNNING;
+ nodePtr.p->alarmCount = 0;
+ findNeighbours(signal);
+ c_clusterNodes.set(nodePtr.i);
+ c_start.reset();
+
+ /**
+ * SEND A HEARTBEAT IMMEDIATELY TO DECREASE THE RISK
+ * THAT WE MISS EARLY HEARTBEATS.
+ */
+ sendHeartbeat(signal);
+
+ /**
+ * ENABLE COMMUNICATION WITH ALL BLOCKS IN THE CURRENT CLUSTER AND SET
+ * THE NODES IN THE CLUSTER TO BE RUNNING.
+ */
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if ((nodePtr.p->phase == ZRUNNING) && (nodePtr.i != getOwnNodeId())) {
+ /*-------------------------------------------------------------------*/
+ // Enable full communication to all other nodes. Not really necessary
+ // to open communication to ourself.
+ /*-------------------------------------------------------------------*/
+ jam();
+ signal->theData[0] = nodePtr.i;
+ sendSignal(CMVMI_REF, GSN_ENABLE_COMORD, signal, 1, JBA);
+ }//if
+ }//for
+
+ sendSttorryLab(signal);
+
+ /**
+ * Start timer handling
+ */
+ signal->theData[0] = ZTIMER_HANDLING;
+ sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 10, JBB);
+
+ sendCmAckAdd(signal, getOwnNodeId(), CmAdd::CommitNew);
+}
+
+/* 4.10.7 CM_ACKADD - PRESIDENT IS RECEIVER - */
+/*---------------------------------------------------------------------------*/
+/* Entry point for an ack add signal.
+ * The TTYPE defines if it is a prepare or a commit. */
+/*---------------------------------------------------------------------------*/
+void Qmgr::execCM_ACKADD(Signal* signal)
+{
+ NodeRecPtr addNodePtr;
+ NodeRecPtr senderNodePtr;
+ jamEntry();
+
+ CmAckAdd * const cmAckAdd = (CmAckAdd*)signal->getDataPtr();
+ const CmAdd::RequestType type = (CmAdd::RequestType)cmAckAdd->requestType;
+ addNodePtr.i = cmAckAdd->startingNodeId;
+ senderNodePtr.i = cmAckAdd->senderNodeId;
+
+ DEBUG_START3(signal, type);
+
+ if (cpresident != getOwnNodeId()) {
+ jam();
+ /*-----------------------------------------------------------------------*/
+ /* IF WE ARE NOT PRESIDENT THEN WE SHOULD NOT RECEIVE THIS MESSAGE. */
+ /*------------------------------------------------------------_----------*/
+ warningEvent("Received CM_ACKADD from %d president=%d",
+ senderNodePtr.i, cpresident);
+ return;
+ }//if
+
+ if (addNodePtr.i != c_start.m_startNode) {
+ jam();
+ /*----------------------------------------------------------------------*/
+ /* THIS IS NOT THE STARTING NODE. WE ARE ACTIVE NOW WITH ANOTHER START. */
+ /*----------------------------------------------------------------------*/
+ warningEvent("Received CM_ACKADD from %d with startNode=%d != own %d",
+ senderNodePtr.i, addNodePtr.i, c_start.m_startNode);
+ return;
+ }//if
+
+ ndbrequire(c_start.m_gsn == GSN_CM_ADD);
+ c_start.m_nodes.clearWaitingFor(senderNodePtr.i);
+ if(!c_start.m_nodes.done()){
+ jam();
+ return;
+ }
+
+ switch (type) {
+ case CmAdd::Prepare:{
+ jam();
+
+ /*----------------------------------------------------------------------*/
+ /* ALL RUNNING NODES HAVE PREPARED THE INCLUSION OF THIS NEW NODE. */
+ /*----------------------------------------------------------------------*/
+ c_start.m_gsn = GSN_CM_ADD;
+ c_start.m_nodes = c_clusterNodes;
+
+ CmAdd * const cmAdd = (CmAdd*)signal->getDataPtrSend();
+ cmAdd->requestType = CmAdd::AddCommit;
+ cmAdd->startingNodeId = addNodePtr.i;
+ cmAdd->startingVersion = getNodeInfo(addNodePtr.i).m_version;
+ NodeReceiverGroup rg(QMGR, c_clusterNodes);
+ sendSignal(rg, GSN_CM_ADD, signal, CmAdd::SignalLength, JBA);
+ DEBUG_START2(GSN_CM_ADD, rg, "AddCommit");
+ return;
+ }
+ case CmAdd::AddCommit:{
+ jam();
+
+ /****************************************/
+ /* Send commit to the new node so he */
+ /* will change PHASE into ZRUNNING */
+ /****************************************/
+ c_start.m_gsn = GSN_CM_ADD;
+ c_start.m_nodes.clearWaitingFor();
+ c_start.m_nodes.setWaitingFor(addNodePtr.i);
+
+ CmAdd * const cmAdd = (CmAdd*)signal->getDataPtrSend();
+ cmAdd->requestType = CmAdd::CommitNew;
+ cmAdd->startingNodeId = addNodePtr.i;
+ cmAdd->startingVersion = getNodeInfo(addNodePtr.i).m_version;
+
+ sendSignal(calcQmgrBlockRef(addNodePtr.i), GSN_CM_ADD, signal,
+ CmAdd::SignalLength, JBA);
+ DEBUG_START(GSN_CM_ADD, addNodePtr.i, "CommitNew");
+ return;
+ }
+ case CmAdd::CommitNew:
+ jam();
+ /**
+ * Tell arbitration about new node.
+ */
+ handleArbitNdbAdd(signal, addNodePtr.i);
+ c_start.reset();
+ return;
+ }//switch
+ ndbrequire(false);
+}//Qmgr::execCM_ACKADD()
+
+/**-------------------------------------------------------------------------
+ * WE HAVE BEEN INCLUDED INTO THE CLUSTER. IT IS NOW TIME TO CALCULATE WHICH
+ * ARE OUR LEFT AND RIGHT NEIGHBOURS FOR THE HEARTBEAT PROTOCOL.
+ *--------------------------------------------------------------------------*/
+void Qmgr::findNeighbours(Signal* signal)
+{
+ UintR toldLeftNeighbour;
+ UintR tfnLeftFound;
+ UintR tfnMaxFound;
+ UintR tfnMinFound;
+ UintR tfnRightFound;
+ NodeRecPtr fnNodePtr;
+ NodeRecPtr fnOwnNodePtr;
+
+ toldLeftNeighbour = cneighbourl;
+ tfnLeftFound = 0;
+ tfnMaxFound = 0;
+ tfnMinFound = (UintR)-1;
+ tfnRightFound = (UintR)-1;
+ fnOwnNodePtr.i = getOwnNodeId();
+ ptrCheckGuard(fnOwnNodePtr, MAX_NDB_NODES, nodeRec);
+ for (fnNodePtr.i = 1; fnNodePtr.i < MAX_NDB_NODES; fnNodePtr.i++) {
+ jam();
+ ptrAss(fnNodePtr, nodeRec);
+ if (fnNodePtr.i != fnOwnNodePtr.i) {
+ if (fnNodePtr.p->phase == ZRUNNING) {
+ if (tfnMinFound > fnNodePtr.p->ndynamicId) {
+ jam();
+ tfnMinFound = fnNodePtr.p->ndynamicId;
+ }//if
+ if (tfnMaxFound < fnNodePtr.p->ndynamicId) {
+ jam();
+ tfnMaxFound = fnNodePtr.p->ndynamicId;
+ }//if
+ if (fnOwnNodePtr.p->ndynamicId > fnNodePtr.p->ndynamicId) {
+ jam();
+ if (fnNodePtr.p->ndynamicId > tfnLeftFound) {
+ jam();
+ tfnLeftFound = fnNodePtr.p->ndynamicId;
+ }//if
+ } else {
+ jam();
+ if (fnNodePtr.p->ndynamicId < tfnRightFound) {
+ jam();
+ tfnRightFound = fnNodePtr.p->ndynamicId;
+ }//if
+ }//if
+ }//if
+ }//if
+ }//for
+ if (tfnLeftFound == 0) {
+ if (tfnMinFound == (UintR)-1) {
+ jam();
+ cneighbourl = ZNIL;
+ } else {
+ jam();
+ cneighbourl = translateDynamicIdToNodeId(signal, tfnMaxFound);
+ }//if
+ } else {
+ jam();
+ cneighbourl = translateDynamicIdToNodeId(signal, tfnLeftFound);
+ }//if
+ if (tfnRightFound == (UintR)-1) {
+ if (tfnMaxFound == 0) {
+ jam();
+ cneighbourh = ZNIL;
+ } else {
+ jam();
+ cneighbourh = translateDynamicIdToNodeId(signal, tfnMinFound);
+ }//if
+ } else {
+ jam();
+ cneighbourh = translateDynamicIdToNodeId(signal, tfnRightFound);
+ }//if
+ if (toldLeftNeighbour != cneighbourl) {
+ jam();
+ if (cneighbourl != ZNIL) {
+ jam();
+ /**-------------------------------------------------------------------*/
+ /* WE ARE SUPERVISING A NEW LEFT NEIGHBOUR. WE START WITH ALARM COUNT
+ * EQUAL TO ZERO.
+ *---------------------------------------------------------------------*/
+ fnNodePtr.i = cneighbourl;
+ ptrCheckGuard(fnNodePtr, MAX_NDB_NODES, nodeRec);
+ fnNodePtr.p->alarmCount = 0;
+ }//if
+ }//if
+
+ signal->theData[0] = NDB_LE_FIND_NEIGHBOURS;
+ signal->theData[1] = getOwnNodeId();
+ signal->theData[2] = cneighbourl;
+ signal->theData[3] = cneighbourh;
+ signal->theData[4] = fnOwnNodePtr.p->ndynamicId;
+ UintR Tlen = 5;
+ for (fnNodePtr.i = 1; fnNodePtr.i < MAX_NDB_NODES; fnNodePtr.i++) {
+ jam();
+ ptrAss(fnNodePtr, nodeRec);
+ if (fnNodePtr.i != fnOwnNodePtr.i) {
+ if (fnNodePtr.p->phase == ZRUNNING) {
+ jam();
+ signal->theData[Tlen] = fnNodePtr.i;
+ signal->theData[Tlen + 1] = fnNodePtr.p->ndynamicId;
+ if (Tlen < 25) {
+ /*----------------------------------------------------------------*/
+ // This code can only report 11 nodes.
+ // We need to update this when increasing the number of nodes
+ // supported.
+ /*-----------------------------------------------------------------*/
+ Tlen += 2;
+ }
+ }//if
+ }//if
+ }//for
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, Tlen, JBB);
+}//Qmgr::findNeighbours()
+
+/*
+4.10.7 INIT_DATA */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+void Qmgr::initData(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRec);
+ nodePtr.p->ndynamicId = 0;
+ if(getNodeInfo(nodePtr.i).m_type == NodeInfo::DB){
+ nodePtr.p->phase = ZINIT;
+ c_definedNodes.set(nodePtr.i);
+ } else {
+ nodePtr.p->phase = ZAPI_INACTIVE;
+ }
+
+ nodePtr.p->alarmCount = 0;
+ nodePtr.p->sendPrepFailReqStatus = Q_NOT_ACTIVE;
+ nodePtr.p->sendCommitFailReqStatus = Q_NOT_ACTIVE;
+ nodePtr.p->sendPresToStatus = Q_NOT_ACTIVE;
+ nodePtr.p->failState = NORMAL;
+ nodePtr.p->rcv[0] = 0;
+ nodePtr.p->rcv[1] = 0;
+ }//for
+ cfailureNr = 1;
+ ccommitFailureNr = 1;
+ cprepareFailureNr = 1;
+ cnoFailedNodes = 0;
+ cnoPrepFailedNodes = 0;
+ creadyDistCom = ZFALSE;
+ cpresident = ZNIL;
+ cpresidentCandidate = ZNIL;
+ cpdistref = 0;
+ cneighbourh = ZNIL;
+ cneighbourl = ZNIL;
+ cdelayRegreq = ZDELAY_REGREQ;
+ cactivateApiCheck = 0;
+ ctoStatus = Q_NOT_ACTIVE;
+
+ interface_check_timer.setDelay(1000);
+ interface_check_timer.reset();
+ clatestTransactionCheck = 0;
+
+ cLqhTimeSignalCount = 0;
+
+ // catch-all for missing initializations
+ memset(&arbitRec, 0, sizeof(arbitRec));
+
+ /**
+ * Timeouts
+ */
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ Uint32 hbDBDB = 1500;
+ Uint32 hbDBAPI = 1500;
+ Uint32 arbitTimeout = 1000;
+ c_restartPartialTimeout = 30000;
+ ndb_mgm_get_int_parameter(p, CFG_DB_HEARTBEAT_INTERVAL, &hbDBDB);
+ ndb_mgm_get_int_parameter(p, CFG_DB_API_HEARTBEAT_INTERVAL, &hbDBAPI);
+ ndb_mgm_get_int_parameter(p, CFG_DB_ARBIT_TIMEOUT, &arbitTimeout);
+ ndb_mgm_get_int_parameter(p, CFG_DB_START_PARTIAL_TIMEOUT,
+ &c_restartPartialTimeout);
+ if(c_restartPartialTimeout == 0){
+ c_restartPartialTimeout = ~0;
+ }
+
+ setHbDelay(hbDBDB);
+ setHbApiDelay(hbDBAPI);
+ setArbitTimeout(arbitTimeout);
+
+ arbitRec.state = ARBIT_NULL; // start state for all nodes
+ arbitRec.apiMask[0].clear(); // prepare for ARBIT_CFG
+
+ ArbitSignalData* const sd = (ArbitSignalData*)&signal->theData[0];
+ for (unsigned rank = 1; rank <= 2; rank++) {
+ sd->sender = getOwnNodeId();
+ sd->code = rank;
+ sd->node = 0;
+ sd->ticket.clear();
+ sd->mask.clear();
+ ndb_mgm_configuration_iterator * iter =
+ theConfiguration.getClusterConfigIterator();
+ for (ndb_mgm_first(iter); ndb_mgm_valid(iter); ndb_mgm_next(iter)) {
+ Uint32 tmp = 0;
+ if (ndb_mgm_get_int_parameter(iter, CFG_NODE_ARBIT_RANK, &tmp) == 0 &&
+ tmp == rank){
+ Uint32 nodeId = 0;
+ ndbrequire(!ndb_mgm_get_int_parameter(iter, CFG_NODE_ID, &nodeId));
+ sd->mask.set(nodeId);
+ }
+ }
+
+ execARBIT_CFG(signal);
+ }
+ setNodeInfo(getOwnNodeId()).m_version = NDB_VERSION;
+}//Qmgr::initData()
+
+
+/**---------------------------------------------------------------------------
+ * HERE WE RECEIVE THE JOB TABLE SIGNAL EVERY 10 MILLISECONDS.
+ * WE WILL USE THIS TO CHECK IF IT IS TIME TO CHECK THE NEIGHBOUR NODE.
+ * WE WILL ALSO SEND A SIGNAL TO BLOCKS THAT NEED A TIME SIGNAL AND
+ * DO NOT WANT TO USE JOB TABLE SIGNALS.
+ *---------------------------------------------------------------------------*/
+void Qmgr::timerHandlingLab(Signal* signal)
+{
+ NDB_TICKS TcurrentTime = NdbTick_CurrentMillisecond();
+ NodeRecPtr myNodePtr;
+ myNodePtr.i = getOwnNodeId();
+ ptrCheckGuard(myNodePtr, MAX_NDB_NODES, nodeRec);
+
+ if (myNodePtr.p->phase == ZRUNNING) {
+ jam();
+ /**---------------------------------------------------------------------
+ * WE ARE ONLY PART OF HEARTBEAT CLUSTER IF WE ARE UP AND RUNNING.
+ *---------------------------------------------------------------------*/
+ if (hb_send_timer.check(TcurrentTime)) {
+ jam();
+ sendHeartbeat(signal);
+ hb_send_timer.reset();
+ }
+ if (hb_check_timer.check(TcurrentTime)) {
+ jam();
+ checkHeartbeat(signal);
+ hb_check_timer.reset();
+ }
+ }
+
+ if (interface_check_timer.check(TcurrentTime)) {
+ jam();
+ interface_check_timer.reset();
+ checkStartInterface(signal);
+ }
+
+ if (cactivateApiCheck != 0) {
+ jam();
+ if (hb_api_timer.check(TcurrentTime)) {
+ jam();
+ hb_api_timer.reset();
+ apiHbHandlingLab(signal);
+ }//if
+ if (clatestTransactionCheck == 0) {
+ //-------------------------------------------------------------
+ // Initialise the Transaction check timer.
+ //-------------------------------------------------------------
+ clatestTransactionCheck = TcurrentTime;
+ }//if
+ int counter = 0;
+ while (TcurrentTime > ((NDB_TICKS)10 + clatestTransactionCheck)) {
+ jam();
+ clatestTransactionCheck += (NDB_TICKS)10;
+ sendSignal(DBTC_REF, GSN_TIME_SIGNAL, signal, 1, JBB);
+ cLqhTimeSignalCount++;
+ if (cLqhTimeSignalCount >= 100) {
+ cLqhTimeSignalCount = 0;
+ sendSignal(DBLQH_REF, GSN_TIME_SIGNAL, signal, 1, JBB);
+ }//if
+ counter++;
+ if (counter > 1) {
+ jam();
+ break;
+ } else {
+ ;
+ }//if
+ }//while
+ }//if
+
+ //--------------------------------------------------
+ // Resend this signal with 10 milliseconds delay.
+ //--------------------------------------------------
+ signal->theData[0] = ZTIMER_HANDLING;
+ sendSignalWithDelay(QMGR_REF, GSN_CONTINUEB, signal, 10, 1);
+ return;
+}//Qmgr::timerHandlingLab()
+
+/*---------------------------------------------------------------------------*/
+/* THIS MODULE HANDLES THE SENDING AND RECEIVING OF HEARTBEATS. */
+/*---------------------------------------------------------------------------*/
+void Qmgr::sendHeartbeat(Signal* signal)
+{
+ NodeRecPtr localNodePtr;
+ localNodePtr.i = cneighbourh;
+ if (localNodePtr.i == ZNIL) {
+ jam();
+ /**---------------------------------------------------------------------
+ * THERE ARE NO NEIGHBOURS. THIS IS POSSIBLE IF WE ARE THE ONLY NODE IN
+ * THE CLUSTER.IN THIS CASE WE DO NOT NEED TO SEND ANY HEARTBEAT SIGNALS.
+ *-----------------------------------------------------------------------*/
+ return;
+ }//if
+ ptrCheckGuard(localNodePtr, MAX_NDB_NODES, nodeRec);
+ signal->theData[0] = getOwnNodeId();
+
+ sendSignal(localNodePtr.p->blockRef, GSN_CM_HEARTBEAT, signal, 1, JBA);
+#ifdef VM_TRACE
+ signal->theData[0] = NDB_LE_SentHeartbeat;
+ signal->theData[1] = localNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+#endif
+}//Qmgr::sendHeartbeat()
+
+void Qmgr::checkHeartbeat(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+
+ nodePtr.i = cneighbourl;
+ if (nodePtr.i == ZNIL) {
+ jam();
+ /**---------------------------------------------------------------------
+ * THERE ARE NO NEIGHBOURS. THIS IS POSSIBLE IF WE ARE THE ONLY NODE IN
+ * THE CLUSTER. IN THIS CASE WE DO NOT NEED TO CHECK ANY HEARTBEATS.
+ *-----------------------------------------------------------------------*/
+ return;
+ }//if
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+
+ nodePtr.p->alarmCount ++;
+ ndbrequire(nodePtr.p->phase == ZRUNNING);
+ ndbrequire(getNodeInfo(nodePtr.i).m_type == NodeInfo::DB);
+
+ if(nodePtr.p->alarmCount > 2){
+ signal->theData[0] = NDB_LE_MissedHeartbeat;
+ signal->theData[1] = nodePtr.i;
+ signal->theData[2] = nodePtr.p->alarmCount - 1;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+ }
+
+ if (nodePtr.p->alarmCount > 4) {
+ jam();
+ /**----------------------------------------------------------------------
+ * OUR LEFT NEIGHBOUR HAVE KEPT QUIET FOR THREE CONSECUTIVE HEARTBEAT
+ * PERIODS. THUS WE DECLARE HIM DOWN.
+ *----------------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_DeadDueToHeartbeat;
+ signal->theData[1] = nodePtr.i;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ failReportLab(signal, nodePtr.i, FailRep::ZHEARTBEAT_FAILURE);
+ return;
+ }//if
+}//Qmgr::checkHeartbeat()
+
+void Qmgr::apiHbHandlingLab(Signal* signal)
+{
+ NodeRecPtr TnodePtr;
+
+ for (TnodePtr.i = 1; TnodePtr.i < MAX_NODES; TnodePtr.i++) {
+ const Uint32 nodeId = TnodePtr.i;
+ ptrAss(TnodePtr, nodeRec);
+
+ const NodeInfo::NodeType type = getNodeInfo(nodeId).getType();
+ if(type == NodeInfo::DB)
+ continue;
+
+ if(type == NodeInfo::INVALID)
+ continue;
+
+ if (TnodePtr.p->phase == ZAPI_ACTIVE){
+ jam();
+ TnodePtr.p->alarmCount ++;
+
+ if(TnodePtr.p->alarmCount > 2){
+ signal->theData[0] = NDB_LE_MissedHeartbeat;
+ signal->theData[1] = nodeId;
+ signal->theData[2] = TnodePtr.p->alarmCount - 1;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 3, JBB);
+ }
+
+ if (TnodePtr.p->alarmCount > 4) {
+ jam();
+ /*------------------------------------------------------------------*/
+ /* THE API NODE HAS NOT SENT ANY HEARTBEAT FOR THREE SECONDS.
+ * WE WILL DISCONNECT FROM IT NOW.
+ *------------------------------------------------------------------*/
+ /*------------------------------------------------------------------*/
+ /* We call node_failed to release all connections for this api node */
+ /*------------------------------------------------------------------*/
+ signal->theData[0] = NDB_LE_DeadDueToHeartbeat;
+ signal->theData[1] = nodeId;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 2, JBB);
+
+ node_failed(signal, nodeId);
+ }//if
+ }//if
+ }//for
+ return;
+}//Qmgr::apiHbHandlingLab()
+
+void Qmgr::checkStartInterface(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ /*------------------------------------------------------------------------*/
+ // This method is called once per second. After a disconnect we wait at
+ // least three seconds before allowing new connects. We will also ensure
+ // that handling of the failure is completed before we allow new connections.
+ /*------------------------------------------------------------------------*/
+ for (nodePtr.i = 1; nodePtr.i < MAX_NODES; nodePtr.i++) {
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZFAIL_CLOSING) {
+ jam();
+ nodePtr.p->alarmCount = nodePtr.p->alarmCount + 1;
+ if (c_connectedNodes.get(nodePtr.i)){
+ jam();
+ /*-------------------------------------------------------------------*/
+ // We need to ensure that the connection is not restored until it has
+ // been disconnected for at least three seconds.
+ /*-------------------------------------------------------------------*/
+ nodePtr.p->alarmCount = 0;
+ }//if
+ if ((nodePtr.p->alarmCount > 3) && (nodePtr.p->failState == NORMAL)) {
+ /**------------------------------------------------------------------
+ * WE HAVE DISCONNECTED THREE SECONDS AGO. WE ARE NOW READY TO
+ * CONNECT AGAIN AND ACCEPT NEW REGISTRATIONS FROM THIS NODE.
+ * WE WILL NOT ALLOW CONNECTIONS OF API NODES UNTIL API FAIL HANDLING
+ * IS COMPLETE.
+ *-------------------------------------------------------------------*/
+ nodePtr.p->failState = NORMAL;
+ if (getNodeInfo(nodePtr.i).m_type != NodeInfo::DB){
+ jam();
+ nodePtr.p->phase = ZAPI_INACTIVE;
+ } else {
+ jam();
+ nodePtr.p->phase = ZINIT;
+ }//if
+
+ nodePtr.p->alarmCount = 0;
+ signal->theData[0] = 0;
+ signal->theData[1] = nodePtr.i;
+ sendSignal(CMVMI_REF, GSN_OPEN_COMREQ, signal, 2, JBA);
+ } else {
+ if(((nodePtr.p->alarmCount + 1) % 60) == 0){
+ char buf[100];
+ BaseString::snprintf(buf, sizeof(buf),
+ "Failure handling of node %d has not completed in %d min."
+ " - state = %d",
+ nodePtr.i,
+ (nodePtr.p->alarmCount + 1)/60,
+ nodePtr.p->failState);
+ warningEvent(buf);
+ }
+ }
+ }//if
+ }//for
+ return;
+}//Qmgr::checkStartInterface()
+
+/**-------------------------------------------------------------------------
+ * This method is called when a DISCONNECT_REP signal arrived which means that
+ * the API node is gone and we want to release resources in TC/DICT blocks.
+ *---------------------------------------------------------------------------*/
+void Qmgr::sendApiFailReq(Signal* signal, Uint16 failedNodeNo)
+{
+ NodeRecPtr failedNodePtr;
+
+ jamEntry();
+ failedNodePtr.i = failedNodeNo;
+ signal->theData[0] = failedNodePtr.i;
+ signal->theData[1] = QMGR_REF;
+
+ ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+
+ ndbrequire(failedNodePtr.p->failState == NORMAL);
+
+ failedNodePtr.p->failState = WAITING_FOR_FAILCONF1;
+ sendSignal(DBTC_REF, GSN_API_FAILREQ, signal, 2, JBA);
+ sendSignal(DBDICT_REF, GSN_API_FAILREQ, signal, 2, JBA);
+ sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBA);
+
+ /**
+ * GREP also need the information that an API node
+ * (actually a REP node) has failed.
+ *
+ * GREP does however NOT send a CONF on this signal, i.e.
+ * the API_FAILREQ signal to GREP is like a REP signal
+ * (i.e. without any confirmation).
+ */
+ sendSignal(GREP_REF, GSN_API_FAILREQ, signal, 2, JBA);
+
+ /**-------------------------------------------------------------------------
+ * THE OTHER NODE WAS AN API NODE. THE COMMUNICATION LINK IS ALREADY
+ * BROKEN AND THUS NO ACTION IS NEEDED TO BREAK THE CONNECTION.
+ * WE ONLY NEED TO SET PARAMETERS TO ENABLE A NEW CONNECTION IN A FEW
+ * SECONDS.
+ *-------------------------------------------------------------------------*/
+ failedNodePtr.p->alarmCount = 0;
+
+ CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
+
+ closeCom->xxxBlockRef = reference();
+ closeCom->failNo = 0;
+ closeCom->noOfNodes = 1;
+ NodeBitmask::clear(closeCom->theNodes);
+ NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
+ sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
+ CloseComReqConf::SignalLength, JBA);
+}//Qmgr::sendApiFailReq()
+
+void Qmgr::execAPI_FAILCONF(Signal* signal)
+{
+ NodeRecPtr failedNodePtr;
+
+ jamEntry();
+ failedNodePtr.i = signal->theData[0];
+ ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+
+ if (failedNodePtr.p->failState == WAITING_FOR_FAILCONF1){
+ jam();
+
+ failedNodePtr.p->rcv[0] = signal->theData[1];
+ failedNodePtr.p->failState = WAITING_FOR_FAILCONF2;
+
+ } else if (failedNodePtr.p->failState == WAITING_FOR_FAILCONF2) {
+ failedNodePtr.p->rcv[1] = signal->theData[1];
+ failedNodePtr.p->failState = NORMAL;
+
+ if (failedNodePtr.p->rcv[0] == failedNodePtr.p->rcv[1]) {
+ jam();
+ systemErrorLab(signal);
+ } else {
+ jam();
+ failedNodePtr.p->rcv[0] = 0;
+ failedNodePtr.p->rcv[1] = 0;
+ }//if
+ } else {
+ jam();
+#ifdef VM_TRACE
+ ndbout << "failedNodePtr.p->failState = "
+ << (Uint32)(failedNodePtr.p->failState) << endl;
+#endif
+ systemErrorLab(signal);
+ }//if
+ return;
+}//Qmgr::execAPI_FAILCONF()
+
+void Qmgr::execNDB_FAILCONF(Signal* signal)
+{
+ NodeRecPtr failedNodePtr;
+ NodeRecPtr nodePtr;
+
+ jamEntry();
+ failedNodePtr.i = signal->theData[0];
+ ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+ if (failedNodePtr.p->failState == WAITING_FOR_NDB_FAILCONF){
+ failedNodePtr.p->failState = NORMAL;
+ } else {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ if (cpresident == getOwnNodeId()) {
+ jam();
+ /**
+ * Prepare a NFCompleteRep and send to all connected API's
+ * They can then abort all transaction waiting for response from
+ * the failed node
+ */
+ NFCompleteRep * const nfComp = (NFCompleteRep *)&signal->theData[0];
+ nfComp->blockNo = QMGR_REF;
+ nfComp->nodeId = getOwnNodeId();
+ nfComp->failedNodeId = failedNodePtr.i;
+
+ for (nodePtr.i = 1; nodePtr.i < MAX_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZAPI_ACTIVE){
+ jam();
+ sendSignal(nodePtr.p->blockRef, GSN_NF_COMPLETEREP, signal,
+ NFCompleteRep::SignalLength, JBA);
+ }//if
+ }//for
+ }
+ return;
+}//Qmgr::execNDB_FAILCONF()
+
+/*******************************/
+/* DISCONNECT_REP */
+/*******************************/
+void Qmgr::execDISCONNECT_REP(Signal* signal)
+{
+ jamEntry();
+ const DisconnectRep * const rep = (DisconnectRep *)&signal->theData[0];
+ const Uint32 nodeId = rep->nodeId;
+ c_connectedNodes.clear(nodeId);
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrCheckGuard(nodePtr, MAX_NODES, nodeRec);
+ switch(nodePtr.p->phase){
+ case ZRUNNING:
+ jam();
+ break;
+ case ZINIT:
+ case ZSTARTING:
+ case ZPREPARE_FAIL:
+ case ZFAIL_CLOSING:
+ case ZAPI_ACTIVE:
+ case ZAPI_INACTIVE:
+ ndbrequire(false);
+ }
+
+ node_failed(signal, nodeId);
+}//DISCONNECT_REP
+
+void Qmgr::node_failed(Signal* signal, Uint16 aFailedNode)
+{
+ NodeRecPtr failedNodePtr;
+ /**------------------------------------------------------------------------
+ * A COMMUNICATION LINK HAS BEEN DISCONNECTED. WE MUST TAKE SOME ACTION
+ * DUE TO THIS.
+ *-----------------------------------------------------------------------*/
+ failedNodePtr.i = aFailedNode;
+ ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+
+ if (getNodeInfo(failedNodePtr.i).getType() == NodeInfo::DB){
+ jam();
+ /**---------------------------------------------------------------------
+ * THE OTHER NODE IS AN NDB NODE, WE HANDLE IT AS IF A HEARTBEAT
+ * FAILURE WAS DISCOVERED.
+ *---------------------------------------------------------------------*/
+ switch(failedNodePtr.p->phase){
+ case ZRUNNING:
+ jam();
+ failReportLab(signal, aFailedNode, FailRep::ZLINK_FAILURE);
+ return;
+ case ZFAIL_CLOSING:
+ jam();
+ return;
+ case ZSTARTING:
+ c_start.reset();
+ // Fall-through
+ default:
+ jam();
+ /*---------------------------------------------------------------------*/
+ // The other node is still not in the cluster but disconnected.
+ // We must restart communication in three seconds.
+ /*---------------------------------------------------------------------*/
+ failedNodePtr.p->failState = NORMAL;
+ failedNodePtr.p->phase = ZFAIL_CLOSING;
+ failedNodePtr.p->alarmCount = 0;
+
+ CloseComReqConf * const closeCom =
+ (CloseComReqConf *)&signal->theData[0];
+
+ closeCom->xxxBlockRef = reference();
+ closeCom->failNo = 0;
+ closeCom->noOfNodes = 1;
+ NodeBitmask::clear(closeCom->theNodes);
+ NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
+ sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
+ CloseComReqConf::SignalLength, JBA);
+ }//if
+ return;
+ }
+
+ /**
+ * API code
+ */
+ jam();
+ if (failedNodePtr.p->phase != ZFAIL_CLOSING){
+ jam();
+ //-------------------------------------------------------------------------
+ // The API was active and has now failed. We need to initiate API failure
+ // handling. If the API had already failed then we can ignore this
+ // discovery.
+ //-------------------------------------------------------------------------
+ failedNodePtr.p->phase = ZFAIL_CLOSING;
+
+ sendApiFailReq(signal, aFailedNode);
+ arbitRec.code = ArbitCode::ApiFail;
+ handleArbitApiFail(signal, aFailedNode);
+ }//if
+ return;
+}//Qmgr::node_failed()
+
+/**--------------------------------------------------------------------------
+ * AN API NODE IS REGISTERING. IF FOR THE FIRST TIME WE WILL ENABLE
+ * COMMUNICATION WITH ALL NDB BLOCKS.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* API_REGREQ */
+/*******************************/
+void Qmgr::execAPI_REGREQ(Signal* signal)
+{
+ jamEntry();
+
+ ApiRegReq* req = (ApiRegReq*)signal->getDataPtr();
+ const Uint32 version = req->version;
+ const BlockReference ref = req->ref;
+
+ NodeRecPtr apiNodePtr;
+ apiNodePtr.i = refToNode(ref);
+ ptrCheckGuard(apiNodePtr, MAX_NODES, nodeRec);
+
+#if 0
+ ndbout_c("Qmgr::execAPI_REGREQ: Recd API_REGREQ (NodeId=%d)", apiNodePtr.i);
+#endif
+
+ bool compatability_check;
+ switch(getNodeInfo(apiNodePtr.i).getType()){
+ case NodeInfo::API:
+ compatability_check = ndbCompatible_ndb_api(NDB_VERSION, version);
+ if (!compatability_check)
+ infoEvent("Connection attempt from api or mysqld id=%d with %s "
+ "incompatible with %s", apiNodePtr.i,
+ getVersionString(version,""), NDB_VERSION_STRING);
+ break;
+ case NodeInfo::MGM:
+ compatability_check = ndbCompatible_ndb_mgmt(NDB_VERSION, version);
+ if (!compatability_check)
+ infoEvent("Connection attempt from management server id=%d with %s "
+ "incompatible with %s", apiNodePtr.i,
+ getVersionString(version,""), NDB_VERSION_STRING);
+ break;
+ case NodeInfo::REP:
+ // compatability_check = ndbCompatible_ndb_api(NDB_VERSION, version);
+ // break;
+ case NodeInfo::DB:
+ case NodeInfo::INVALID:
+ default:
+ sendApiRegRef(signal, ref, ApiRegRef::WrongType);
+ infoEvent("Invalid connection attempt with type %d",
+ getNodeInfo(apiNodePtr.i).getType());
+ return;
+ }
+
+ if (!compatability_check) {
+ jam();
+ apiNodePtr.p->phase = ZAPI_INACTIVE;
+ sendApiRegRef(signal, ref, ApiRegRef::UnsupportedVersion);
+ return;
+ }
+
+ setNodeInfo(apiNodePtr.i).m_version = version;
+
+ apiNodePtr.p->alarmCount = 0;
+
+ ApiRegConf * const apiRegConf = (ApiRegConf *)&signal->theData[0];
+ apiRegConf->qmgrRef = reference();
+ apiRegConf->apiHeartbeatFrequency = (chbApiDelay / 10);
+ apiRegConf->version = NDB_VERSION;
+ apiRegConf->nodeState = getNodeState();
+ {
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+ Uint32 dynamicId = nodePtr.p->ndynamicId;
+
+ if(apiRegConf->nodeState.masterNodeId != getOwnNodeId()){
+ jam();
+ apiRegConf->nodeState.dynamicId = dynamicId;
+ } else {
+ apiRegConf->nodeState.dynamicId = -dynamicId;
+ }
+ }
+ apiRegConf->nodeState.m_connected_nodes.assign(c_connectedNodes);
+
+ sendSignal(ref, GSN_API_REGCONF, signal, ApiRegConf::SignalLength, JBB);
+
+ if ((getNodeState().startLevel == NodeState::SL_STARTED ||
+ getNodeState().getSingleUserMode())
+ && apiNodePtr.p->phase == ZAPI_INACTIVE) {
+ jam();
+ /**----------------------------------------------------------------------
+ * THE API NODE IS REGISTERING. WE WILL ACCEPT IT BY CHANGING STATE AND
+ * SENDING A CONFIRM.
+ *----------------------------------------------------------------------*/
+ apiNodePtr.p->phase = ZAPI_ACTIVE;
+ apiNodePtr.p->blockRef = ref;
+ signal->theData[0] = apiNodePtr.i;
+ sendSignal(CMVMI_REF, GSN_ENABLE_COMORD, signal, 1, JBA);
+ }
+ return;
+}//Qmgr::execAPI_REGREQ()
+
+
+void
+Qmgr::execAPI_VERSION_REQ(Signal * signal) {
+ jamEntry();
+ ApiVersionReq * const req = (ApiVersionReq *)signal->getDataPtr();
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 nodeId = req->nodeId;
+
+ ApiVersionConf * conf = (ApiVersionConf *)req;
+ if(getNodeInfo(nodeId).m_connected)
+ conf->version = getNodeInfo(nodeId).m_version;
+ else
+ conf->version = 0;
+ conf->nodeId = nodeId;
+
+ sendSignal(senderRef,
+ GSN_API_VERSION_CONF,
+ signal,
+ ApiVersionConf::SignalLength, JBB);
+
+
+}
+
+
+#if 0
+bool
+Qmgr::checkAPIVersion(NodeId nodeId,
+ Uint32 apiVersion, Uint32 ownVersion) const {
+ bool ret=true;
+ /**
+ * First implementation...
+ */
+ if ((getMajor(apiVersion) < getMajor(ownVersion) ||
+ getMinor(apiVersion) < getMinor(ownVersion)) &&
+ apiVersion >= API_UPGRADE_VERSION) {
+ jam();
+ if ( getNodeInfo(nodeId).getType() != NodeInfo::MGM ) {
+ jam();
+ ret = false;
+ } else {
+ jam();
+ /* we have a software upgrade situation, mgmtsrvr should be
+ * the highest, let him decide what to do
+ */
+ ;
+ }
+ }
+ return ret;
+}
+#endif
+
+void
+Qmgr::sendApiRegRef(Signal* signal, Uint32 Tref, ApiRegRef::ErrorCode err){
+ ApiRegRef* ref = (ApiRegRef*)signal->getDataPtrSend();
+ ref->ref = reference();
+ ref->version = NDB_VERSION;
+ ref->errorCode = err;
+ sendSignal(Tref, GSN_API_REGREF, signal, ApiRegRef::SignalLength, JBB);
+}
+
+/**--------------------------------------------------------------------------
+ * A NODE HAS BEEN DECLARED AS DOWN. WE WILL CLOSE THE COMMUNICATION TO THIS
+ * NODE IF NOT ALREADY DONE. IF WE ARE PRESIDENT OR BECOMES PRESIDENT BECAUSE
+ * OF A FAILED PRESIDENT THEN WE WILL TAKE FURTHER ACTION.
+ *---------------------------------------------------------------------------*/
+void Qmgr::failReportLab(Signal* signal, Uint16 aFailedNode,
+ FailRep::FailCause aFailCause)
+{
+ NodeRecPtr nodePtr;
+ NodeRecPtr failedNodePtr;
+ NodeRecPtr myNodePtr;
+ UintR TnoFailedNodes;
+
+ failedNodePtr.i = aFailedNode;
+ ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+ if (failedNodePtr.i == getOwnNodeId()) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+
+ myNodePtr.i = getOwnNodeId();
+ ptrCheckGuard(myNodePtr, MAX_NDB_NODES, nodeRec);
+ if (myNodePtr.p->phase != ZRUNNING) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+ TnoFailedNodes = cnoFailedNodes;
+ failReport(signal, failedNodePtr.i, (UintR)ZTRUE, aFailCause);
+ if (cpresident == getOwnNodeId()) {
+ jam();
+ if (ctoStatus == Q_NOT_ACTIVE) {
+ jam();
+ /**--------------------------------------------------------------------
+ * AS PRESIDENT WE ARE REQUIRED TO START THE EXCLUSION PROCESS SUCH THAT
+ * THE APPLICATION SEE NODE FAILURES IN A CONSISTENT ORDER.
+ * IF WE HAVE BECOME PRESIDENT NOW (CTO_STATUS = ACTIVE) THEN WE HAVE
+ * TO COMPLETE THE PREVIOUS COMMIT FAILED NODE PROCESS BEFORE STARTING
+ * A NEW.
+ * CTO_STATUS = ACTIVE CAN ALSO MEAN THAT WE ARE PRESIDENT AND ARE
+ * CURRENTLY COMMITTING A SET OF NODE CRASHES. IN THIS CASE IT IS NOT
+ * ALLOWED TO START PREPARING NEW NODE CRASHES.
+ *---------------------------------------------------------------------*/
+ if (TnoFailedNodes != cnoFailedNodes) {
+ jam();
+ cfailureNr = cfailureNr + 1;
+ for (nodePtr.i = 1;
+ nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ sendPrepFailReq(signal, nodePtr.i);
+ }//if
+ }//for
+ }//if
+ }//if
+ }//if
+ return;
+}//Qmgr::failReportLab()
+
+/**-------------------------------------------------------------------------
+ * WE HAVE RECEIVED A PREPARE TO EXCLUDE A NUMBER OF NODES FROM THE CLUSTER.
+ * WE WILL FIRST CHECK THAT WE HAVE NOT ANY MORE NODES THAT
+ * WE ALSO HAVE EXCLUDED
+ *--------------------------------------------------------------------------*/
+/*******************************/
+/* PREP_FAILREQ */
+/*******************************/
+void Qmgr::execPREP_FAILREQ(Signal* signal)
+{
+ NodeRecPtr myNodePtr;
+ jamEntry();
+
+ PrepFailReqRef * const prepFail = (PrepFailReqRef *)&signal->theData[0];
+
+ BlockReference Tblockref = prepFail->xxxBlockRef;
+ Uint16 TfailureNr = prepFail->failNo;
+ cnoPrepFailedNodes = prepFail->noOfNodes;
+ UintR arrayIndex = 0;
+ Uint32 Tindex;
+ for (Tindex = 0; Tindex < MAX_NDB_NODES; Tindex++) {
+ if (NodeBitmask::get(prepFail->theNodes, Tindex)){
+ cprepFailedNodes[arrayIndex] = Tindex;
+ arrayIndex++;
+ }//if
+ }//for
+ UintR guard0;
+
+ /**
+ * Block commit until node failures has stabilized
+ *
+ * @See RT352
+ */
+ BlockCommitOrd* const block = (BlockCommitOrd *)&signal->theData[0];
+ block->failNo = TfailureNr;
+ EXECUTE_DIRECT(DBDIH, GSN_BLOCK_COMMIT_ORD, signal,
+ BlockCommitOrd::SignalLength);
+
+ myNodePtr.i = getOwnNodeId();
+ ptrCheckGuard(myNodePtr, MAX_NDB_NODES, nodeRec);
+ if (myNodePtr.p->phase != ZRUNNING) {
+ jam();
+ systemErrorLab(signal);
+ return;
+ }//if
+
+ guard0 = cnoPrepFailedNodes - 1;
+ arrGuard(guard0, MAX_NDB_NODES);
+ for (Tindex = 0; Tindex <= guard0; Tindex++) {
+ jam();
+ failReport(signal,
+ cprepFailedNodes[Tindex],
+ (UintR)ZFALSE,
+ FailRep::ZIN_PREP_FAIL_REQ);
+ }//for
+ sendCloseComReq(signal, Tblockref, TfailureNr);
+ cnoCommitFailedNodes = 0;
+ cprepareFailureNr = TfailureNr;
+ return;
+}//Qmgr::execPREP_FAILREQ()
+
+/**---------------------------------------------------------------------------
+ * THE CRASHED NODES HAS BEEN EXCLUDED FROM COMMUNICATION.
+ * WE WILL CHECK WHETHER ANY MORE NODES HAVE FAILED DURING THE PREPARE PROCESS.
+ * IF SO WE WILL REFUSE THE PREPARE PHASE AND EXPECT A NEW PREPARE MESSAGE
+ * WITH ALL FAILED NODES INCLUDED.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* CLOSE_COMCONF */
+/*******************************/
+void Qmgr::execCLOSE_COMCONF(Signal* signal)
+{
+ jamEntry();
+
+ CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
+
+ BlockReference Tblockref = closeCom->xxxBlockRef;
+ Uint16 TfailureNr = closeCom->failNo;
+
+ cnoPrepFailedNodes = closeCom->noOfNodes;
+ UintR arrayIndex = 0;
+ UintR Tindex = 0;
+ for(Tindex = 0; Tindex < MAX_NDB_NODES; Tindex++){
+ if(NodeBitmask::get(closeCom->theNodes, Tindex)){
+ cprepFailedNodes[arrayIndex] = Tindex;
+ arrayIndex++;
+ }
+ }
+ UintR tprepFailConf;
+ UintR Tindex2;
+ UintR guard0;
+ UintR guard1;
+ UintR Tfound;
+ Uint16 TfailedNodeNo;
+
+ tprepFailConf = ZTRUE;
+ if (cnoFailedNodes > 0) {
+ jam();
+ guard0 = cnoFailedNodes - 1;
+ arrGuard(guard0, MAX_NDB_NODES);
+ for (Tindex = 0; Tindex <= guard0; Tindex++) {
+ jam();
+ TfailedNodeNo = cfailedNodes[Tindex];
+ Tfound = ZFALSE;
+ guard1 = cnoPrepFailedNodes - 1;
+ arrGuard(guard1, MAX_NDB_NODES);
+ for (Tindex2 = 0; Tindex2 <= guard1; Tindex2++) {
+ jam();
+ if (TfailedNodeNo == cprepFailedNodes[Tindex2]) {
+ jam();
+ Tfound = ZTRUE;
+ }//if
+ }//for
+ if (Tfound == ZFALSE) {
+ jam();
+ tprepFailConf = ZFALSE;
+ arrGuard(cnoPrepFailedNodes, MAX_NDB_NODES);
+ cprepFailedNodes[cnoPrepFailedNodes] = TfailedNodeNo;
+ cnoPrepFailedNodes = cnoPrepFailedNodes + 1;
+ }//if
+ }//for
+ }//if
+ if (tprepFailConf == ZFALSE) {
+ jam();
+ for (Tindex = 1; Tindex < MAX_NDB_NODES; Tindex++) {
+ cfailedNodes[Tindex] = cprepFailedNodes[Tindex];
+ }//for
+ cnoFailedNodes = cnoPrepFailedNodes;
+ sendPrepFailReqRef(signal,
+ Tblockref,
+ GSN_PREP_FAILREF,
+ reference(),
+ cfailureNr,
+ cnoPrepFailedNodes,
+ cprepFailedNodes);
+ } else {
+ jam();
+ cnoCommitFailedNodes = cnoPrepFailedNodes;
+ guard0 = cnoPrepFailedNodes - 1;
+ arrGuard(guard0, MAX_NDB_NODES);
+ for (Tindex = 0; Tindex <= guard0; Tindex++) {
+ jam();
+ arrGuard(Tindex, MAX_NDB_NODES);
+ ccommitFailedNodes[Tindex] = cprepFailedNodes[Tindex];
+ }//for
+ signal->theData[0] = getOwnNodeId();
+ signal->theData[1] = TfailureNr;
+ sendSignal(Tblockref, GSN_PREP_FAILCONF, signal, 2, JBA);
+ }//if
+ return;
+}//Qmgr::execCLOSE_COMCONF()
+
+/*---------------------------------------------------------------------------*/
+/* WE HAVE RECEIVED A CONFIRM OF THAT THIS NODE HAVE PREPARED THE FAILURE. */
+/*---------------------------------------------------------------------------*/
+/*******************************/
+/* PREP_FAILCONF */
+/*******************************/
+void Qmgr::execPREP_FAILCONF(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ NodeRecPtr replyNodePtr;
+ jamEntry();
+ replyNodePtr.i = signal->theData[0];
+ Uint16 TfailureNr = signal->theData[1];
+ if (TfailureNr != cfailureNr) {
+ jam();
+ /**----------------------------------------------------------------------
+ * WE HAVE ALREADY STARTING A NEW ATTEMPT TO EXCLUDE A NUMBER OF NODES.
+ * IGNORE
+ *----------------------------------------------------------------------*/
+ return;
+ }//if
+ ptrCheckGuard(replyNodePtr, MAX_NDB_NODES, nodeRec);
+ replyNodePtr.p->sendPrepFailReqStatus = Q_NOT_ACTIVE;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ if (nodePtr.p->sendPrepFailReqStatus == Q_ACTIVE) {
+ jam();
+ return;
+ }//if
+ }//if
+ }//for
+ /**
+ * Check node count and groups and invoke arbitrator if necessary.
+ * Continues via sendCommitFailReq() if successful.
+ */
+ arbitRec.failureNr = cfailureNr;
+ const NodeState & s = getNodeState();
+ if(s.startLevel == NodeState::SL_STOPPING_3 && s.stopping.systemShutdown){
+ jam();
+ /**
+ * We're performing a system shutdown,
+ * don't let artibtrator shut us down
+ */
+ return;
+ }
+ handleArbitCheck(signal);
+ return;
+}//Qmgr::execPREP_FAILCONF()
+
+void
+Qmgr::sendCommitFailReq(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ jam();
+ if (arbitRec.failureNr != cfailureNr) {
+ jam();
+ /**----------------------------------------------------------------------
+ * WE HAVE ALREADY STARTING A NEW ATTEMPT TO EXCLUDE A NUMBER OF NODES.
+ * IGNORE
+ *----------------------------------------------------------------------*/
+ return;
+ }//if
+ /**-----------------------------------------------------------------------
+ * WE HAVE SUCCESSFULLY PREPARED A SET OF NODE FAILURES. WE WILL NOW COMMIT
+ * THESE NODE FAILURES.
+ *-------------------------------------------------------------------------*/
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ nodePtr.p->sendCommitFailReqStatus = Q_ACTIVE;
+ signal->theData[0] = cpdistref;
+ signal->theData[1] = cfailureNr;
+ sendSignal(nodePtr.p->blockRef, GSN_COMMIT_FAILREQ, signal, 2, JBA);
+ }//if
+ }//for
+ ctoStatus = Q_ACTIVE;
+ cnoFailedNodes = 0;
+ return;
+}//sendCommitFailReq()
+
+/*---------------------------------------------------------------------------*/
+/* SOME NODE HAVE DISCOVERED A NODE FAILURE THAT WE HAVE NOT YET DISCOVERED. */
+/* WE WILL START ANOTHER ROUND OF PREPARING A SET OF NODE FAILURES. */
+/*---------------------------------------------------------------------------*/
+/*******************************/
+/* PREP_FAILREF */
+/*******************************/
+void Qmgr::execPREP_FAILREF(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ jamEntry();
+
+ PrepFailReqRef * const prepFail = (PrepFailReqRef *)&signal->theData[0];
+
+ Uint16 TfailureNr = prepFail->failNo;
+ cnoPrepFailedNodes = prepFail->noOfNodes;
+
+ UintR arrayIndex = 0;
+ UintR Tindex = 0;
+ for(Tindex = 0; Tindex < MAX_NDB_NODES; Tindex++) {
+ jam();
+ if(NodeBitmask::get(prepFail->theNodes, Tindex)){
+ jam();
+ cprepFailedNodes[arrayIndex] = Tindex;
+ arrayIndex++;
+ }//if
+ }//for
+ if (TfailureNr != cfailureNr) {
+ jam();
+ /**---------------------------------------------------------------------
+ * WE HAVE ALREADY STARTING A NEW ATTEMPT TO EXCLUDE A NUMBER OF NODES.
+ * IGNORE
+ *----------------------------------------------------------------------*/
+ return;
+ }//if
+ UintR guard0;
+ UintR Ti;
+
+ cnoFailedNodes = cnoPrepFailedNodes;
+ guard0 = cnoPrepFailedNodes - 1;
+ arrGuard(guard0, MAX_NDB_NODES);
+ for (Ti = 0; Ti <= guard0; Ti++) {
+ jam();
+ cfailedNodes[Ti] = cprepFailedNodes[Ti];
+ }//for
+ cfailureNr = cfailureNr + 1;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ sendPrepFailReq(signal, nodePtr.i);
+ }//if
+ }//for
+ return;
+}//Qmgr::execPREP_FAILREF()
+
+/*---------------------------------------------------------------------------*/
+/* THE PRESIDENT IS NOW COMMITTING THE PREVIOUSLY PREPARED NODE FAILURE. */
+/*---------------------------------------------------------------------------*/
+/***********************/
+/* COMMIT_FAILREQ */
+/***********************/
+void Qmgr::execCOMMIT_FAILREQ(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ jamEntry();
+ BlockReference Tblockref = signal->theData[0];
+ UintR TfailureNr = signal->theData[1];
+ if (Tblockref != cpdistref) {
+ jam();
+ return;
+ }//if
+ UintR guard0;
+ UintR Tj;
+
+ /**
+ * Block commit until node failures has stabilized
+ *
+ * @See RT352
+ */
+ UnblockCommitOrd* const unblock = (UnblockCommitOrd *)&signal->theData[0];
+ unblock->failNo = TfailureNr;
+ EXECUTE_DIRECT(DBDIH, GSN_UNBLOCK_COMMIT_ORD, signal,
+ UnblockCommitOrd::SignalLength);
+
+ if ((ccommitFailureNr != TfailureNr) &&
+ (cnoCommitFailedNodes > 0)) {
+ jam();
+ /**-----------------------------------------------------------------------
+ * WE ONLY DO THIS PART OF THE COMMIT HANDLING THE FIRST TIME WE HEAR THIS
+ * SIGNAL. WE CAN HEAR IT SEVERAL TIMES IF THE PRESIDENTS KEEP FAILING.
+ *-----------------------------------------------------------------------*/
+ ccommitFailureNr = TfailureNr;
+ NodeFailRep * const nodeFail = (NodeFailRep *)&signal->theData[0];
+
+ nodeFail->failNo = ccommitFailureNr;
+ nodeFail->noOfNodes = cnoCommitFailedNodes;
+ nodeFail->masterNodeId = cpresident;
+ NodeBitmask::clear(nodeFail->theNodes);
+ for(unsigned i = 0; i < cnoCommitFailedNodes; i++) {
+ jam();
+ NodeBitmask::set(nodeFail->theNodes, ccommitFailedNodes[i]);
+ }//if
+ sendSignal(NDBCNTR_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
+ guard0 = cnoCommitFailedNodes - 1;
+ arrGuard(guard0, MAX_NDB_NODES);
+ /**--------------------------------------------------------------------
+ * WE MUST PREPARE TO ACCEPT THE CRASHED NODE INTO THE CLUSTER AGAIN BY
+ * SETTING UP CONNECTIONS AGAIN AFTER THREE SECONDS OF DELAY.
+ *--------------------------------------------------------------------*/
+ for (Tj = 0; Tj <= guard0; Tj++) {
+ jam();
+ nodePtr.i = ccommitFailedNodes[Tj];
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+ nodePtr.p->phase = ZFAIL_CLOSING;
+ nodePtr.p->failState = WAITING_FOR_NDB_FAILCONF;
+ nodePtr.p->alarmCount = 0;
+ c_clusterNodes.clear(nodePtr.i);
+ }//for
+ /*----------------------------------------------------------------------*/
+ /* WE INFORM THE API'S WE HAVE CONNECTED ABOUT THE FAILED NODES. */
+ /*----------------------------------------------------------------------*/
+ for (nodePtr.i = 1; nodePtr.i < MAX_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZAPI_ACTIVE) {
+ jam();
+
+ NodeFailRep * const nodeFail = (NodeFailRep *)&signal->theData[0];
+
+ nodeFail->failNo = ccommitFailureNr;
+ nodeFail->noOfNodes = cnoCommitFailedNodes;
+ NodeBitmask::clear(nodeFail->theNodes);
+ for(unsigned i = 0; i < cnoCommitFailedNodes; i++) {
+ jam();
+ NodeBitmask::set(nodeFail->theNodes, ccommitFailedNodes[i]);
+ }//for
+ sendSignal(nodePtr.p->blockRef, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+ }//if
+ }//for
+ if (cpresident != getOwnNodeId()) {
+ jam();
+ cnoFailedNodes = cnoCommitFailedNodes - cnoFailedNodes;
+ if (cnoFailedNodes > 0) {
+ jam();
+ guard0 = cnoFailedNodes - 1;
+ arrGuard(guard0 + cnoCommitFailedNodes, MAX_NDB_NODES);
+ for (Tj = 0; Tj <= guard0; Tj++) {
+ jam();
+ cfailedNodes[Tj] = cfailedNodes[Tj + cnoCommitFailedNodes];
+ }//for
+ }//if
+ }//if
+ cnoCommitFailedNodes = 0;
+ }//if
+ /**-----------------------------------------------------------------------
+ * WE WILL ALWAYS ACKNOWLEDGE THE COMMIT EVEN WHEN RECEIVING IT MULTIPLE
+ * TIMES SINCE IT WILL ALWAYS COME FROM A NEW PRESIDENT.
+ *------------------------------------------------------------------------*/
+ signal->theData[0] = getOwnNodeId();
+ sendSignal(Tblockref, GSN_COMMIT_FAILCONF, signal, 1, JBA);
+ return;
+}//Qmgr::execCOMMIT_FAILREQ()
+
+/*--------------------------------------------------------------------------*/
+/* WE HAVE RECEIVED A CONFIRM OF THAT THIS NODE HAVE COMMITTED THE FAILURES.*/
+/*--------------------------------------------------------------------------*/
+/*******************************/
+/* COMMIT_FAILCONF */
+/*******************************/
+void Qmgr::execCOMMIT_FAILCONF(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ NodeRecPtr replyNodePtr;
+ jamEntry();
+ replyNodePtr.i = signal->theData[0];
+
+ ptrCheckGuard(replyNodePtr, MAX_NDB_NODES, nodeRec);
+ replyNodePtr.p->sendCommitFailReqStatus = Q_NOT_ACTIVE;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ if (nodePtr.p->sendCommitFailReqStatus == Q_ACTIVE) {
+ jam();
+ return;
+ }//if
+ }//if
+ }//for
+ /*-----------------------------------------------------------------------*/
+ /* WE HAVE SUCCESSFULLY COMMITTED A SET OF NODE FAILURES. */
+ /*-----------------------------------------------------------------------*/
+ ctoStatus = Q_NOT_ACTIVE;
+ if (cnoFailedNodes != 0) {
+ jam();
+ /**----------------------------------------------------------------------
+ * A FAILURE OCCURRED IN THE MIDDLE OF THE COMMIT PROCESS. WE ARE NOW
+ * READY TO START THE FAILED NODE PROCESS FOR THIS NODE.
+ *----------------------------------------------------------------------*/
+ cfailureNr = cfailureNr + 1;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ sendPrepFailReq(signal, nodePtr.i);
+ }//if
+ }//for
+ }//if
+ return;
+}//Qmgr::execCOMMIT_FAILCONF()
+
+/**--------------------------------------------------------------------------
+ * IF THE PRESIDENT FAILS IN THE MIDDLE OF THE COMMIT OF A FAILED NODE THEN
+ * THE NEW PRESIDENT NEEDS TO QUERY THE COMMIT STATUS IN THE RUNNING NODES.
+ *---------------------------------------------------------------------------*/
+/*******************************/
+/* PRES_TOCONF */
+/*******************************/
+void Qmgr::execPRES_TOCONF(Signal* signal)
+{
+ NodeRecPtr nodePtr;
+ NodeRecPtr replyNodePtr;
+ jamEntry();
+ replyNodePtr.i = signal->theData[0];
+ UintR TfailureNr = signal->theData[1];
+ if (ctoFailureNr < TfailureNr) {
+ jam();
+ ctoFailureNr = TfailureNr;
+ }//if
+ ptrCheckGuard(replyNodePtr, MAX_NDB_NODES, nodeRec);
+ replyNodePtr.p->sendPresToStatus = Q_NOT_ACTIVE;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->sendPresToStatus == Q_ACTIVE) {
+ jam();
+ return;
+ }//if
+ }//for
+ /*-------------------------------------------------------------------------*/
+ /* WE ARE NOW READY TO DISCOVER WHETHER THE FAILURE WAS COMMITTED OR NOT. */
+ /*-------------------------------------------------------------------------*/
+ if (ctoFailureNr > ccommitFailureNr) {
+ jam();
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ nodePtr.p->sendCommitFailReqStatus = Q_ACTIVE;
+ signal->theData[0] = cpdistref;
+ signal->theData[1] = ctoFailureNr;
+ sendSignal(nodePtr.p->blockRef, GSN_COMMIT_FAILREQ, signal, 2, JBA);
+ }//if
+ }//for
+ return;
+ }//if
+ /*-------------------------------------------------------------------------*/
+ /* WE ARE NOW READY TO START THE NEW NODE FAILURE PROCESS. */
+ /*-------------------------------------------------------------------------*/
+ ctoStatus = Q_NOT_ACTIVE;
+ cfailureNr = cfailureNr + 1;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ sendPrepFailReq(signal, nodePtr.i);
+ }//if
+ }//for
+ return;
+}//Qmgr::execPRES_TOCONF()
+
+/*--------------------------------------------------------------------------*/
+// Provide information about the configured NDB nodes in the system.
+/*--------------------------------------------------------------------------*/
+void Qmgr::execREAD_NODESREQ(Signal* signal)
+{
+ jamEntry();
+
+ BlockReference TBref = signal->theData[0];
+
+ ReadNodesConf * const readNodes = (ReadNodesConf *)&signal->theData[0];
+
+ NodeRecPtr nodePtr;
+ nodePtr.i = getOwnNodeId();
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+
+ NdbNodeBitmask tmp = c_definedNodes;
+ tmp.bitANDC(c_clusterNodes);
+
+ readNodes->noOfNodes = c_definedNodes.count();
+ readNodes->masterNodeId = cpresident;
+ readNodes->ndynamicId = nodePtr.p->ndynamicId;
+ c_definedNodes.copyto(NdbNodeBitmask::Size, readNodes->definedNodes);
+ c_clusterNodes.copyto(NdbNodeBitmask::Size, readNodes->clusterNodes);
+ tmp.copyto(NdbNodeBitmask::Size, readNodes->inactiveNodes);
+ NdbNodeBitmask::clear(readNodes->startingNodes);
+ NdbNodeBitmask::clear(readNodes->startedNodes);
+
+ sendSignal(TBref, GSN_READ_NODESCONF, signal,
+ ReadNodesConf::SignalLength, JBB);
+}//Qmgr::execREAD_NODESREQ()
+
+void Qmgr::systemErrorBecauseOtherNodeFailed(Signal* signal,
+ NodeId failedNodeId) {
+ jam();
+
+ // Broadcast that this node is failing to other nodes
+ failReport(signal, getOwnNodeId(), (UintR)ZTRUE, FailRep::ZOWN_FAILURE);
+
+ char buf[100];
+ BaseString::snprintf(buf, 100,
+ "Node was shutdown during startup because node %d failed",
+ failedNodeId);
+
+ progError(__LINE__, ERR_SR_OTHERNODEFAILED, buf);
+}
+
+
+void Qmgr::systemErrorLab(Signal* signal, const char * message)
+{
+ jam();
+ // Broadcast that this node is failing to other nodes
+ failReport(signal, getOwnNodeId(), (UintR)ZTRUE, FailRep::ZOWN_FAILURE);
+
+ // If it's known why shutdown occured
+ // an error message has been passed to this function
+ progError(__LINE__, 0, message);
+
+ return;
+}//Qmgr::systemErrorLab()
+
+
+/**---------------------------------------------------------------------------
+ * A FAILURE HAVE BEEN DISCOVERED ON A NODE. WE NEED TO CLEAR A
+ * NUMBER OF VARIABLES.
+ *---------------------------------------------------------------------------*/
+void Qmgr::failReport(Signal* signal,
+ Uint16 aFailedNode,
+ UintR aSendFailRep,
+ FailRep::FailCause aFailCause)
+{
+ UintR tfrMinDynamicId;
+ NodeRecPtr failedNodePtr;
+ NodeRecPtr nodePtr;
+ NodeRecPtr presidentNodePtr;
+
+
+ failedNodePtr.i = aFailedNode;
+ ptrCheckGuard(failedNodePtr, MAX_NDB_NODES, nodeRec);
+ if (failedNodePtr.p->phase == ZRUNNING) {
+ jam();
+/* WE ALSO NEED TO ADD HERE SOME CODE THAT GETS OUR NEW NEIGHBOURS. */
+ if (cpresident == getOwnNodeId()) {
+ jam();
+ if (failedNodePtr.p->sendCommitFailReqStatus == Q_ACTIVE) {
+ jam();
+ signal->theData[0] = failedNodePtr.i;
+ sendSignal(QMGR_REF, GSN_COMMIT_FAILCONF, signal, 1, JBA);
+ }//if
+ if (failedNodePtr.p->sendPresToStatus == Q_ACTIVE) {
+ jam();
+ signal->theData[0] = failedNodePtr.i;
+ signal->theData[1] = ccommitFailureNr;
+ sendSignal(QMGR_REF, GSN_PRES_TOCONF, signal, 2, JBA);
+ }//if
+ }//if
+ failedNodePtr.p->phase = ZPREPARE_FAIL;
+ failedNodePtr.p->sendPrepFailReqStatus = Q_NOT_ACTIVE;
+ failedNodePtr.p->sendCommitFailReqStatus = Q_NOT_ACTIVE;
+ failedNodePtr.p->sendPresToStatus = Q_NOT_ACTIVE;
+ failedNodePtr.p->alarmCount = 0;
+ if (aSendFailRep == ZTRUE) {
+ jam();
+ if (failedNodePtr.i != getOwnNodeId()) {
+ jam();
+ FailRep * const failRep = (FailRep *)&signal->theData[0];
+ failRep->failNodeId = failedNodePtr.i;
+ failRep->failCause = aFailCause;
+ sendSignal(failedNodePtr.p->blockRef, GSN_FAIL_REP, signal,
+ FailRep::SignalLength, JBA);
+ }//if
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ FailRep * const failRep = (FailRep *)&signal->theData[0];
+ failRep->failNodeId = failedNodePtr.i;
+ failRep->failCause = aFailCause;
+ sendSignal(nodePtr.p->blockRef, GSN_FAIL_REP, signal,
+ FailRep::SignalLength, JBA);
+ }//if
+ }//for
+ }//if
+ if (failedNodePtr.i == getOwnNodeId()) {
+ jam();
+ return;
+ }//if
+ failedNodePtr.p->ndynamicId = 0;
+ findNeighbours(signal);
+ if (failedNodePtr.i == cpresident) {
+ jam();
+ /**--------------------------------------------------------------------
+ * IF PRESIDENT HAVE FAILED WE MUST CALCULATE THE NEW PRESIDENT BY
+ * FINDING THE NODE WITH THE MINIMUM DYNAMIC IDENTITY.
+ *---------------------------------------------------------------------*/
+ tfrMinDynamicId = (UintR)-1;
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ if (nodePtr.p->ndynamicId < tfrMinDynamicId) {
+ jam();
+ tfrMinDynamicId = nodePtr.p->ndynamicId;
+ cpresident = nodePtr.i;
+ }//if
+ }//if
+ }//for
+ presidentNodePtr.i = cpresident;
+ ptrCheckGuard(presidentNodePtr, MAX_NDB_NODES, nodeRec);
+ cpdistref = presidentNodePtr.p->blockRef;
+ if (cpresident == getOwnNodeId()) {
+ CRASH_INSERTION(920);
+ cfailureNr = cprepareFailureNr;
+ ctoFailureNr = 0;
+ ctoStatus = Q_ACTIVE;
+ if (cnoCommitFailedNodes > 0) {
+ jam();
+ /**-----------------------------------------------------------------
+ * IN THIS SITUATION WE ARE UNCERTAIN OF WHETHER THE NODE FAILURE
+ * PROCESS WAS COMMITTED. WE NEED TO QUERY THE OTHER NODES ABOUT
+ * THEIR STATUS.
+ *-----------------------------------------------------------------*/
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES;
+ nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ nodePtr.p->sendPresToStatus = Q_ACTIVE;
+ signal->theData[0] = cpdistref;
+ signal->theData[1] = cprepareFailureNr;
+ sendSignal(nodePtr.p->blockRef, GSN_PRES_TOREQ,
+ signal, 1, JBA);
+ }//if
+ }//for
+ } else {
+ jam();
+ /*-----------------------------------------------------------------*/
+ // In this case it could be that a commit process is still ongoing.
+ // If so we must conclude it as the new master.
+ /*-----------------------------------------------------------------*/
+ for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES;
+ nodePtr.i++) {
+ jam();
+ ptrAss(nodePtr, nodeRec);
+ if (nodePtr.p->phase == ZRUNNING) {
+ jam();
+ nodePtr.p->sendCommitFailReqStatus = Q_ACTIVE;
+ signal->theData[0] = cpdistref;
+ signal->theData[1] = ccommitFailureNr;
+ sendSignal(nodePtr.p->blockRef, GSN_COMMIT_FAILREQ, signal,
+ 2, JBA);
+ }//if
+ }//for
+ }//if
+ }//if
+ }//if
+ arrGuard(cnoFailedNodes, MAX_NDB_NODES);
+ cfailedNodes[cnoFailedNodes] = failedNodePtr.i;
+ cnoFailedNodes = cnoFailedNodes + 1;
+ }//if
+}//Qmgr::failReport()
+
+/*---------------------------------------------------------------------------*/
+/* INPUT: TTDI_DYN_ID */
+/* OUTPUT: TTDI_NODE_ID */
+/*---------------------------------------------------------------------------*/
+Uint16 Qmgr::translateDynamicIdToNodeId(Signal* signal, UintR TdynamicId)
+{
+ NodeRecPtr tdiNodePtr;
+ Uint16 TtdiNodeId = ZNIL;
+
+ for (tdiNodePtr.i = 1; tdiNodePtr.i < MAX_NDB_NODES; tdiNodePtr.i++) {
+ jam();
+ ptrAss(tdiNodePtr, nodeRec);
+ if (tdiNodePtr.p->ndynamicId == TdynamicId) {
+ jam();
+ TtdiNodeId = tdiNodePtr.i;
+ break;
+ }//if
+ }//for
+ if (TtdiNodeId == ZNIL) {
+ jam();
+ systemErrorLab(signal);
+ }//if
+ return TtdiNodeId;
+}//Qmgr::translateDynamicIdToNodeId()
+
+/**--------------------------------------------------------------------------
+ * WHEN RECEIVING PREPARE FAILURE REQUEST WE WILL IMMEDIATELY CLOSE
+ * COMMUNICATION WITH ALL THOSE NODES.
+ *--------------------------------------------------------------------------*/
+void Qmgr::sendCloseComReq(Signal* signal, BlockReference TBRef, Uint16 aFailNo)
+{
+ CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
+
+ closeCom->xxxBlockRef = TBRef;
+ closeCom->failNo = aFailNo;
+ closeCom->noOfNodes = cnoPrepFailedNodes;
+
+ NodeBitmask::clear(closeCom->theNodes);
+
+ for(int i = 0; i < cnoPrepFailedNodes; i++) {
+ const NodeId nodeId = cprepFailedNodes[i];
+ jam();
+ NodeBitmask::set(closeCom->theNodes, nodeId);
+ }
+
+ sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal,
+ CloseComReqConf::SignalLength, JBA);
+
+}//Qmgr::sendCloseComReq()
+
+void
+Qmgr::sendPrepFailReqRef(Signal* signal,
+ Uint32 dstBlockRef,
+ GlobalSignalNumber gsn,
+ Uint32 blockRef,
+ Uint32 failNo,
+ Uint32 noOfNodes,
+ const NodeId theNodes[]){
+
+ PrepFailReqRef * const prepFail = (PrepFailReqRef *)&signal->theData[0];
+ prepFail->xxxBlockRef = blockRef;
+ prepFail->failNo = failNo;
+ prepFail->noOfNodes = noOfNodes;
+
+ NodeBitmask::clear(prepFail->theNodes);
+
+ for(Uint32 i = 0; i<noOfNodes; i++){
+ const NodeId nodeId = theNodes[i];
+ NodeBitmask::set(prepFail->theNodes, nodeId);
+ }
+
+ sendSignal(dstBlockRef, gsn, signal, PrepFailReqRef::SignalLength, JBA);
+}
+
+
+/**--------------------------------------------------------------------------
+ * SEND PREPARE FAIL REQUEST FROM PRESIDENT.
+ *---------------------------------------------------------------------------*/
+void Qmgr::sendPrepFailReq(Signal* signal, Uint16 aNode)
+{
+ NodeRecPtr sendNodePtr;
+ sendNodePtr.i = aNode;
+ ptrCheckGuard(sendNodePtr, MAX_NDB_NODES, nodeRec);
+ sendNodePtr.p->sendPrepFailReqStatus = Q_ACTIVE;
+
+ sendPrepFailReqRef(signal,
+ sendNodePtr.p->blockRef,
+ GSN_PREP_FAILREQ,
+ reference(),
+ cfailureNr,
+ cnoFailedNodes,
+ cfailedNodes);
+}//Qmgr::sendPrepFailReq()
+
+/**
+ * Arbitration module. Rest of QMGR calls us only via
+ * the "handle" routines.
+ */
+
+/**
+ * Should < 1/2 nodes die unconditionally. Affects only >= 3-way
+ * replication.
+ */
+static const bool g_ndb_arbit_one_half_rule = false;
+
+/**
+ * Config signals are logically part of CM_INIT.
+ */
+void
+Qmgr::execARBIT_CFG(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ unsigned rank = sd->code;
+ ndbrequire(1 <= rank && rank <= 2);
+ arbitRec.apiMask[0].bitOR(sd->mask);
+ arbitRec.apiMask[rank] = sd->mask;
+}
+
+/**
+ * ContinueB delay (0=JBA 1=JBB)
+ */
+Uint32 Qmgr::getArbitDelay()
+{
+ switch (arbitRec.state) {
+ case ARBIT_NULL:
+ jam();
+ break;
+ case ARBIT_INIT:
+ jam();
+ case ARBIT_FIND:
+ jam();
+ case ARBIT_PREP1:
+ jam();
+ case ARBIT_PREP2:
+ jam();
+ case ARBIT_START:
+ jam();
+ return 100;
+ case ARBIT_RUN:
+ jam();
+ return 1000;
+ case ARBIT_CHOOSE:
+ jam();
+ return 10;
+ case ARBIT_CRASH: // if we could wait
+ jam();
+ return 100;
+ }
+ ndbrequire(false);
+ return (Uint32)-1;
+}
+
+/**
+ * Time to wait for reply. There is only 1 config parameter
+ * (timeout for CHOOSE). XXX The rest are guesses.
+ */
+Uint32 Qmgr::getArbitTimeout()
+{
+ switch (arbitRec.state) {
+ case ARBIT_NULL:
+ jam();
+ break;
+ case ARBIT_INIT: // not used
+ jam();
+ case ARBIT_FIND: // not used
+ jam();
+ return 1000;
+ case ARBIT_PREP1:
+ jam();
+ case ARBIT_PREP2:
+ jam();
+ return 1000 + cnoOfNodes * hb_send_timer.getDelay();
+ case ARBIT_START:
+ jam();
+ return 1000 + arbitRec.timeout;
+ case ARBIT_RUN: // not used (yet)
+ jam();
+ return 1000;
+ case ARBIT_CHOOSE:
+ jam();
+ return arbitRec.timeout;
+ case ARBIT_CRASH: // if we could wait
+ jam();
+ return 100;
+ }
+ ndbrequire(false);
+ return (Uint32)-1;
+}
+
+/**
+ * Start arbitration thread when we are president and database
+ * is opened for the first time.
+ *
+ * XXX Do arbitration check just like on node failure. Since
+ * there is no arbitrator yet, must win on counts alone.
+ */
+void
+Qmgr::handleArbitStart(Signal* signal)
+{
+ jam();
+ ndbrequire(cpresident == getOwnNodeId());
+ ndbrequire(arbitRec.state == ARBIT_NULL);
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ startArbitThread(signal);
+}
+
+/**
+ * Handle API node failure. Called also by non-president nodes.
+ * If we are president go back to INIT state, otherwise to NULL.
+ * Start new thread to save time.
+ */
+void
+Qmgr::handleArbitApiFail(Signal* signal, Uint16 nodeId)
+{
+ if (arbitRec.node != nodeId) {
+ jam();
+ return;
+ }
+ reportArbitEvent(signal, NDB_LE_ArbitState);
+ arbitRec.node = 0;
+ switch (arbitRec.state) {
+ case ARBIT_NULL: // should not happen
+ jam();
+ case ARBIT_INIT:
+ jam();
+ case ARBIT_FIND:
+ jam();
+ break;
+ case ARBIT_PREP1: // start from beginning
+ jam();
+ case ARBIT_PREP2:
+ jam();
+ case ARBIT_START:
+ jam();
+ case ARBIT_RUN:
+ if (cpresident == getOwnNodeId()) {
+ jam();
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ startArbitThread(signal);
+ } else {
+ jam();
+ arbitRec.state = ARBIT_NULL;
+ }
+ break;
+ case ARBIT_CHOOSE: // XXX too late
+ jam();
+ case ARBIT_CRASH:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+}
+
+/**
+ * Handle NDB node add. Ignore if arbitration thread not yet
+ * started. If PREP is not ready, go back to INIT. Otherwise
+ * the new node gets arbitrator and ticket once we reach RUN state.
+ * Start new thread to save time.
+ */
+void
+Qmgr::handleArbitNdbAdd(Signal* signal, Uint16 nodeId)
+{
+ jam();
+ ndbrequire(cpresident == getOwnNodeId());
+ switch (arbitRec.state) {
+ case ARBIT_NULL: // before db opened
+ jam();
+ break;
+ case ARBIT_INIT: // start from beginning
+ jam();
+ case ARBIT_FIND:
+ jam();
+ case ARBIT_PREP1:
+ jam();
+ case ARBIT_PREP2:
+ jam();
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ startArbitThread(signal);
+ break;
+ case ARBIT_START: // process in RUN state
+ jam();
+ case ARBIT_RUN:
+ jam();
+ arbitRec.newMask.set(nodeId);
+ break;
+ case ARBIT_CHOOSE: // XXX too late
+ jam();
+ case ARBIT_CRASH:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+}
+
+/**
+ * Check if current nodeset can survive. The decision is
+ * based on node count, node groups, and on external arbitrator
+ * (if we have one). Always starts a new thread because
+ * 1) CHOOSE cannot wait 2) if we are new president we need
+ * a thread 3) if we are old president it does no harm.
+ */
+void
+Qmgr::handleArbitCheck(Signal* signal)
+{
+ jam();
+ ndbrequire(cpresident == getOwnNodeId());
+ NodeBitmask ndbMask;
+ computeArbitNdbMask(ndbMask);
+ if (g_ndb_arbit_one_half_rule &&
+ 2 * ndbMask.count() < cnoOfNodes) {
+ jam();
+ arbitRec.code = ArbitCode::LoseNodes;
+ } else {
+ jam();
+ CheckNodeGroups* sd = (CheckNodeGroups*)&signal->theData[0];
+ sd->blockRef = reference();
+ sd->requestType = CheckNodeGroups::Direct | CheckNodeGroups::ArbitCheck;
+ sd->mask = ndbMask;
+ EXECUTE_DIRECT(DBDIH, GSN_CHECKNODEGROUPSREQ, signal,
+ CheckNodeGroups::SignalLength);
+ jamEntry();
+ switch (sd->output) {
+ case CheckNodeGroups::Win:
+ jam();
+ arbitRec.code = ArbitCode::WinGroups;
+ break;
+ case CheckNodeGroups::Lose:
+ jam();
+ arbitRec.code = ArbitCode::LoseGroups;
+ break;
+ case CheckNodeGroups::Partitioning:
+ jam();
+ arbitRec.code = ArbitCode::Partitioning;
+ if (g_ndb_arbit_one_half_rule &&
+ 2 * ndbMask.count() > cnoOfNodes) {
+ jam();
+ arbitRec.code = ArbitCode::WinNodes;
+ }
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ }
+ switch (arbitRec.code) {
+ case ArbitCode::LoseNodes:
+ jam();
+ case ArbitCode::LoseGroups:
+ jam();
+ goto crashme;
+ case ArbitCode::WinNodes:
+ jam();
+ case ArbitCode::WinGroups:
+ jam();
+ if (arbitRec.state == ARBIT_RUN) {
+ jam();
+ break;
+ }
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ break;
+ case ArbitCode::Partitioning:
+ if (arbitRec.state == ARBIT_RUN) {
+ jam();
+ arbitRec.state = ARBIT_CHOOSE;
+ arbitRec.newstate = true;
+ break;
+ }
+ if (arbitRec.apiMask[0].count() != 0) {
+ jam();
+ arbitRec.code = ArbitCode::LoseNorun;
+ } else {
+ jam();
+ arbitRec.code = ArbitCode::LoseNocfg;
+ }
+ goto crashme;
+ default:
+ crashme:
+ jam();
+ arbitRec.state = ARBIT_CRASH;
+ arbitRec.newstate = true;
+ break;
+ }
+ reportArbitEvent(signal, NDB_LE_ArbitResult);
+ switch (arbitRec.state) {
+ default:
+ jam();
+ arbitRec.newMask.bitAND(ndbMask); // delete failed nodes
+ arbitRec.recvMask.bitAND(ndbMask);
+ sendCommitFailReq(signal); // start commit of failed nodes
+ break;
+ case ARBIT_CHOOSE:
+ jam();
+ case ARBIT_CRASH:
+ jam();
+ break;
+ }
+ startArbitThread(signal);
+}
+
+/**
+ * Start a new continueB thread. The thread id is incremented
+ * so that any old thread will exit.
+ */
+void
+Qmgr::startArbitThread(Signal* signal)
+{
+ jam();
+ ndbrequire(cpresident == getOwnNodeId());
+ arbitRec.code = ArbitCode::ThreadStart;
+ reportArbitEvent(signal, NDB_LE_ArbitState);
+ signal->theData[1] = ++arbitRec.thread;
+ runArbitThread(signal);
+}
+
+/**
+ * Handle arbitration thread. The initial thread normally ends
+ * up in RUN state. New thread can be started to save time.
+ */
+void
+Qmgr::runArbitThread(Signal* signal)
+{
+#ifdef DEBUG_ARBIT
+ NodeBitmask ndbMask;
+ computeArbitNdbMask(ndbMask);
+ ndbout << "arbit thread:";
+ ndbout << " state=" << arbitRec.state;
+ ndbout << " newstate=" << arbitRec.newstate;
+ ndbout << " thread=" << arbitRec.thread;
+ ndbout << " node=" << arbitRec.node;
+ ndbout << " ticket=" << arbitRec.ticket.getText();
+ ndbout << " ndbmask=" << ndbMask.getText();
+ ndbout << " sendcount=" << arbitRec.sendCount;
+ ndbout << " recvcount=" << arbitRec.recvCount;
+ ndbout << " recvmask=" << arbitRec.recvMask.getText();
+ ndbout << " code=" << arbitRec.code;
+ ndbout << endl;
+#endif
+ if (signal->theData[1] != arbitRec.thread) {
+ jam();
+ return; // old thread dies
+ }
+ switch (arbitRec.state) {
+ case ARBIT_INIT: // main thread
+ jam();
+ stateArbitInit(signal);
+ break;
+ case ARBIT_FIND:
+ jam();
+ stateArbitFind(signal);
+ break;
+ case ARBIT_PREP1:
+ jam();
+ case ARBIT_PREP2:
+ jam();
+ stateArbitPrep(signal);
+ break;
+ case ARBIT_START:
+ jam();
+ stateArbitStart(signal);
+ break;
+ case ARBIT_RUN:
+ jam();
+ stateArbitRun(signal);
+ break;
+ case ARBIT_CHOOSE: // partitition thread
+ jam();
+ stateArbitChoose(signal);
+ break;
+ case ARBIT_CRASH:
+ jam();
+ stateArbitCrash(signal);
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+ signal->theData[0] = ZARBIT_HANDLING;
+ signal->theData[1] = arbitRec.thread;
+ signal->theData[2] = arbitRec.state; // just for signal log
+ Uint32 delay = getArbitDelay();
+ if (delay == 0) {
+ jam();
+ sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 3, JBA);
+ } else if (delay == 1) {
+ jam();
+ sendSignal(QMGR_REF, GSN_CONTINUEB, signal, 3, JBB);
+ } else {
+ jam();
+ sendSignalWithDelay(QMGR_REF, GSN_CONTINUEB, signal, delay, 3);
+ }//if
+}
+
+/**
+ * Handle INIT state. Generate next ticket. Switch to FIND
+ * state without delay.
+ */
+void
+Qmgr::stateArbitInit(Signal* signal)
+{
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+
+ arbitRec.node = 0;
+ arbitRec.ticket.update();
+ arbitRec.newMask.clear();
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+ arbitRec.state = ARBIT_FIND;
+ arbitRec.newstate = true;
+ stateArbitFind(signal);
+}
+
+/**
+ * Handle FIND state. Find first arbitrator which is alive
+ * and invoke PREP state without delay. If none are found,
+ * loop in FIND state. This is forever if no arbitrators
+ * are configured (not the normal case).
+ *
+ * XXX Add adaptive behaviour to avoid getting stuck on API
+ * nodes which are alive but do not respond or die too soon.
+ */
+void
+Qmgr::stateArbitFind(Signal* signal)
+{
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+ NodeRecPtr aPtr;
+ for (unsigned rank = 1; rank <= 2; rank++) {
+ jam();
+ aPtr.i = 0;
+ const unsigned stop = NodeBitmask::NotFound;
+ while ((aPtr.i = arbitRec.apiMask[rank].find(aPtr.i + 1)) != stop) {
+ jam();
+ ptrAss(aPtr, nodeRec);
+ if (aPtr.p->phase != ZAPI_ACTIVE)
+ continue;
+ arbitRec.node = aPtr.i;
+ arbitRec.state = ARBIT_PREP1;
+ arbitRec.newstate = true;
+ stateArbitPrep(signal);
+ return;
+ }
+ }
+}
+
+/**
+ * Handle PREP states. First round nulls any existing tickets.
+ * Second round sends new ticket. When all confirms have been
+ * received invoke START state immediately.
+ */
+void
+Qmgr::stateArbitPrep(Signal* signal)
+{
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+
+ arbitRec.sendCount = 0; // send all at once
+ computeArbitNdbMask(arbitRec.recvMask); // to send and recv
+ arbitRec.recvMask.clear(getOwnNodeId());
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+ if (! arbitRec.sendCount) {
+ jam();
+ NodeRecPtr aPtr;
+ aPtr.i = 0;
+ const unsigned stop = NodeBitmask::NotFound;
+ while ((aPtr.i = arbitRec.recvMask.find(aPtr.i + 1)) != stop) {
+ jam();
+ ptrAss(aPtr, nodeRec);
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ sd->sender = getOwnNodeId();
+ if (arbitRec.state == ARBIT_PREP1) {
+ jam();
+ sd->code = ArbitCode::PrepPart1;
+ } else {
+ jam();
+ sd->code = ArbitCode::PrepPart2;
+ }
+ sd->node = arbitRec.node;
+ sd->ticket = arbitRec.ticket;
+ sd->mask.clear();
+ sendSignal(aPtr.p->blockRef, GSN_ARBIT_PREPREQ, signal,
+ ArbitSignalData::SignalLength, JBB);
+ }
+ arbitRec.setTimestamp(); // send time
+ arbitRec.sendCount = 1;
+ return;
+ }
+ if (arbitRec.code != 0) { // error
+ jam();
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ return;
+ }
+ if (arbitRec.recvMask.count() == 0) { // recv all
+ if (arbitRec.state == ARBIT_PREP1) {
+ jam();
+ arbitRec.state = ARBIT_PREP2;
+ arbitRec.newstate = true;
+ } else {
+ jam();
+ arbitRec.state = ARBIT_START;
+ arbitRec.newstate = true;
+ stateArbitStart(signal);
+ }
+ return;
+ }
+ if (arbitRec.getTimediff() > getArbitTimeout()) {
+ jam();
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ return;
+ }
+}
+
+void
+Qmgr::execARBIT_PREPREQ(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (getOwnNodeId() == cpresident) {
+ jam();
+ return; // wrong state
+ }
+ if (sd->sender != cpresident) {
+ jam();
+ return; // wrong state
+ }
+ NodeRecPtr aPtr;
+ aPtr.i = sd->sender;
+ ptrAss(aPtr, nodeRec);
+ switch (sd->code) {
+ case ArbitCode::PrepPart1: // zero them just to be sure
+ jam();
+ arbitRec.node = 0;
+ arbitRec.ticket.clear();
+ break;
+ case ArbitCode::PrepPart2: // non-president enters RUN state
+ jam();
+ case ArbitCode::PrepAtrun:
+ jam();
+ arbitRec.node = sd->node;
+ arbitRec.ticket = sd->ticket;
+ arbitRec.code = sd->code;
+ reportArbitEvent(signal, NDB_LE_ArbitState);
+ arbitRec.state = ARBIT_RUN;
+ arbitRec.newstate = true;
+ if (sd->code == ArbitCode::PrepAtrun) {
+ jam();
+ return;
+ }
+ break;
+ default:
+ jam();
+ ndbrequire(false);
+ }
+ sd->sender = getOwnNodeId();
+ sd->code = 0;
+ sendSignal(aPtr.p->blockRef, GSN_ARBIT_PREPCONF, signal,
+ ArbitSignalData::SignalLength, JBB);
+}
+
+void
+Qmgr::execARBIT_PREPCONF(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (! arbitRec.match(sd)) {
+ jam();
+ return; // stray signal
+ }
+ if (arbitRec.state != ARBIT_PREP1 && arbitRec.state != ARBIT_PREP2) {
+ jam();
+ return; // wrong state
+ }
+ if (! arbitRec.recvMask.get(sd->sender)) {
+ jam();
+ return; // wrong state
+ }
+ arbitRec.recvMask.clear(sd->sender);
+ if (arbitRec.code == 0 && sd->code != 0) {
+ jam();
+ arbitRec.code = sd->code;
+ }//if
+}
+
+void
+Qmgr::execARBIT_PREPREF(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (sd->code == 0) {
+ jam();
+ sd->code = ArbitCode::ErrUnknown;
+ }
+ execARBIT_PREPCONF(signal);
+}
+
+/**
+ * Handle START state. On first call send start request to
+ * the chosen arbitrator. Then wait for a CONF.
+ */
+void
+Qmgr::stateArbitStart(Signal* signal)
+{
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+
+ arbitRec.sendCount = 0;
+ arbitRec.recvCount = 0;
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+ if (! arbitRec.sendCount) {
+ jam();
+ BlockReference blockRef = calcApiClusterMgrBlockRef(arbitRec.node);
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ sd->sender = getOwnNodeId();
+ sd->code = 0;
+ sd->node = arbitRec.node;
+ sd->ticket = arbitRec.ticket;
+ sd->mask.clear();
+ sendSignal(blockRef, GSN_ARBIT_STARTREQ, signal,
+ ArbitSignalData::SignalLength, JBB);
+ arbitRec.sendCount = 1;
+ arbitRec.setTimestamp(); // send time
+ return;
+ }
+ if (arbitRec.recvCount) {
+ jam();
+ reportArbitEvent(signal, NDB_LE_ArbitState);
+ if (arbitRec.code == ArbitCode::ApiStart) {
+ jam();
+ arbitRec.state = ARBIT_RUN;
+ arbitRec.newstate = true;
+ return;
+ }
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ return;
+ }
+ if (arbitRec.getTimediff() > getArbitTimeout()) {
+ jam();
+ arbitRec.code = ArbitCode::ErrTimeout;
+ reportArbitEvent(signal, NDB_LE_ArbitState);
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ return;
+ }
+}
+
+void
+Qmgr::execARBIT_STARTCONF(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (! arbitRec.match(sd)) {
+ jam();
+ return; // stray signal
+ }
+ if (arbitRec.state != ARBIT_START) {
+ jam();
+ return; // wrong state
+ }
+ if (arbitRec.recvCount) {
+ jam();
+ return; // wrong state
+ }
+ arbitRec.code = sd->code;
+ arbitRec.recvCount = 1;
+}
+
+void
+Qmgr::execARBIT_STARTREF(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (sd->code == 0) {
+ jam();
+ sd->code = ArbitCode::ErrUnknown;
+ }
+ execARBIT_STARTCONF(signal);
+}
+
+/**
+ * Handle RUN state. Send ticket to any new nodes which have
+ * appeared after PREP state. We don't care about a CONF.
+ */
+void
+Qmgr::stateArbitRun(Signal* signal)
+{
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+ NodeRecPtr aPtr;
+ aPtr.i = 0;
+ const unsigned stop = NodeBitmask::NotFound;
+ while ((aPtr.i = arbitRec.newMask.find(aPtr.i + 1)) != stop) {
+ jam();
+ arbitRec.newMask.clear(aPtr.i);
+ ptrAss(aPtr, nodeRec);
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ sd->sender = getOwnNodeId();
+ sd->code = ArbitCode::PrepAtrun;
+ sd->node = arbitRec.node;
+ sd->ticket = arbitRec.ticket;
+ sd->mask.clear();
+ sendSignal(aPtr.p->blockRef, GSN_ARBIT_PREPREQ, signal,
+ ArbitSignalData::SignalLength, JBB);
+ }
+}
+
+/**
+ * Handle CHOOSE state. Entered only from RUN state when
+ * there is a possible network partitioning. Send CHOOSE to
+ * the arbitrator. On win switch to INIT state because a new
+ * ticket must be created.
+ */
+void
+Qmgr::stateArbitChoose(Signal* signal)
+{
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+
+ arbitRec.sendCount = 0;
+ arbitRec.recvCount = 0;
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+ if (! arbitRec.sendCount) {
+ jam();
+ BlockReference blockRef = calcApiClusterMgrBlockRef(arbitRec.node);
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ sd->sender = getOwnNodeId();
+ sd->code = 0;
+ sd->node = arbitRec.node;
+ sd->ticket = arbitRec.ticket;
+ computeArbitNdbMask(sd->mask);
+ sendSignal(blockRef, GSN_ARBIT_CHOOSEREQ, signal,
+ ArbitSignalData::SignalLength, JBA);
+ arbitRec.sendCount = 1;
+ arbitRec.setTimestamp(); // send time
+ return;
+ }
+ if (arbitRec.recvCount) {
+ jam();
+ reportArbitEvent(signal, NDB_LE_ArbitResult);
+ if (arbitRec.code == ArbitCode::WinChoose) {
+ jam();
+ sendCommitFailReq(signal); // start commit of failed nodes
+ arbitRec.state = ARBIT_INIT;
+ arbitRec.newstate = true;
+ return;
+ }
+ arbitRec.state = ARBIT_CRASH;
+ arbitRec.newstate = true;
+ stateArbitCrash(signal); // do it at once
+ return;
+ }
+ if (arbitRec.getTimediff() > getArbitTimeout()) {
+ jam();
+ arbitRec.code = ArbitCode::ErrTimeout;
+ reportArbitEvent(signal, NDB_LE_ArbitState);
+ arbitRec.state = ARBIT_CRASH;
+ arbitRec.newstate = true;
+ stateArbitCrash(signal); // do it at once
+ return;
+ }
+}
+
+void
+Qmgr::execARBIT_CHOOSECONF(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (!arbitRec.match(sd)) {
+ jam();
+ return; // stray signal
+ }
+ if (arbitRec.state != ARBIT_CHOOSE) {
+ jam();
+ return; // wrong state
+ }
+ if (arbitRec.recvCount) {
+ jam();
+ return; // wrong state
+ }
+ arbitRec.recvCount = 1;
+ arbitRec.code = sd->code;
+}
+
+void
+Qmgr::execARBIT_CHOOSEREF(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (sd->code == 0) {
+ jam();
+ sd->code = ArbitCode::ErrUnknown;
+ }
+ execARBIT_CHOOSECONF(signal);
+}
+
+/**
+ * Handle CRASH state. We must crash immediately.
+ * XXX tell other nodes in our party to crash too.
+ */
+void
+Qmgr::stateArbitCrash(Signal* signal)
+{
+ jam();
+ if (arbitRec.newstate) {
+ jam();
+ CRASH_INSERTION((Uint32)910 + arbitRec.state);
+ arbitRec.setTimestamp();
+ arbitRec.code = 0;
+ arbitRec.newstate = false;
+ }
+#ifdef ndb_arbit_crash_wait_for_event_report_to_get_out
+ if (! (arbitRec.getTimediff() > getArbitTimeout()))
+ return;
+#endif
+ progError(__LINE__, ERR_ARBIT_SHUTDOWN, "Arbitrator decided to shutdown this node");
+}
+
+/**
+ * Arbitrator may inform us that it will exit. This lets us
+ * start looking sooner for a new one. Handle it like API node
+ * failure.
+ */
+void
+Qmgr::execARBIT_STOPREP(Signal* signal)
+{
+ jamEntry();
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ if (! arbitRec.match(sd)) {
+ jam();
+ return; // stray signal
+ }
+ arbitRec.code = ArbitCode::ApiExit;
+ handleArbitApiFail(signal, arbitRec.node);
+}
+
+void
+Qmgr::computeArbitNdbMask(NodeBitmask& aMask)
+{
+ NodeRecPtr aPtr;
+ aMask.clear();
+ for (aPtr.i = 1; aPtr.i < MAX_NDB_NODES; aPtr.i++) {
+ jam();
+ ptrAss(aPtr, nodeRec);
+ if (getNodeInfo(aPtr.i).getType() == NodeInfo::DB && aPtr.p->phase == ZRUNNING){
+ jam();
+ aMask.set(aPtr.i);
+ }
+ }
+}
+
+/**
+ * Report arbitration event. We use arbitration signal format
+ * where sender (word 0) is event type.
+ */
+void
+Qmgr::reportArbitEvent(Signal* signal, Ndb_logevent_type type)
+{
+ ArbitSignalData* sd = (ArbitSignalData*)&signal->theData[0];
+ sd->sender = type;
+ sd->code = arbitRec.code | (arbitRec.state << 16);
+ sd->node = arbitRec.node;
+ sd->ticket = arbitRec.ticket;
+ sd->mask.clear();
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal,
+ ArbitSignalData::SignalLength, JBB);
+}
+
+// end of arbitration module
+
+void
+Qmgr::execDUMP_STATE_ORD(Signal* signal)
+{
+ switch (signal->theData[0]) {
+ case 1:
+ infoEvent("creadyDistCom = %d, cpresident = %d\n",
+ creadyDistCom, cpresident);
+ infoEvent("cpresidentAlive = %d, cpresidentCand = %d\n",
+ cpresidentAlive, cpresidentCandidate);
+ infoEvent("ctoStatus = %d\n", ctoStatus);
+ for(Uint32 i = 1; i<MAX_NDB_NODES; i++){
+ if(getNodeInfo(i).getType() == NodeInfo::DB){
+ NodeRecPtr nodePtr;
+ nodePtr.i = i;
+ ptrCheckGuard(nodePtr, MAX_NDB_NODES, nodeRec);
+ char buf[100];
+ switch(nodePtr.p->phase){
+ case ZINIT:
+ sprintf(buf, "Node %d: ZINIT(%d)", i, nodePtr.p->phase);
+ break;
+ case ZSTARTING:
+ sprintf(buf, "Node %d: ZSTARTING(%d)", i, nodePtr.p->phase);
+ break;
+ case ZRUNNING:
+ sprintf(buf, "Node %d: ZRUNNING(%d)", i, nodePtr.p->phase);
+ break;
+ case ZPREPARE_FAIL:
+ sprintf(buf, "Node %d: ZPREPARE_FAIL(%d)", i, nodePtr.p->phase);
+ break;
+ case ZFAIL_CLOSING:
+ sprintf(buf, "Node %d: ZFAIL_CLOSING(%d)", i, nodePtr.p->phase);
+ break;
+ case ZAPI_INACTIVE:
+ sprintf(buf, "Node %d: ZAPI_INACTIVE(%d)", i, nodePtr.p->phase);
+ break;
+ case ZAPI_ACTIVE:
+ sprintf(buf, "Node %d: ZAPI_ACTIVE(%d)", i, nodePtr.p->phase);
+ break;
+ default:
+ sprintf(buf, "Node %d: <UNKNOWN>(%d)", i, nodePtr.p->phase);
+ break;
+ }
+ infoEvent(buf);
+ }
+ }
+ default:
+ ;
+ }//switch
+}//Qmgr::execDUMP_STATE_ORD()
+
+void Qmgr::execSET_VAR_REQ(Signal* signal)
+{
+#if 0
+ SetVarReq* const setVarReq = (SetVarReq*)&signal->theData[0];
+ ConfigParamId var = setVarReq->variable();
+ UintR val = setVarReq->value();
+
+ switch (var) {
+ case HeartbeatIntervalDbDb:
+ setHbDelay(val/10);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case HeartbeatIntervalDbApi:
+ setHbApiDelay(val/10);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ case ArbitTimeout:
+ setArbitTimeout(val);
+ sendSignal(CMVMI_REF, GSN_SET_VAR_CONF, signal, 1, JBB);
+ break;
+
+ default:
+ sendSignal(CMVMI_REF, GSN_SET_VAR_REF, signal, 1, JBB);
+ }// switch
+#endif
+}//execSET_VAR_REQ()
diff --git a/storage/ndb/src/kernel/blocks/qmgr/timer.hpp b/storage/ndb/src/kernel/blocks/qmgr/timer.hpp
new file mode 100644
index 00000000000..9c35a23766c
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/qmgr/timer.hpp
@@ -0,0 +1,72 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/**
+ * @class Timer
+ * @brief A timer class that can't be fooled by NTP:ing the system clock to old time
+ */
+class Timer {
+public:
+ Timer() {
+ m_delay = 10;
+ };
+
+ Timer(NDB_TICKS delay_time) {
+ m_delay = delay_time;
+ }
+
+ /**
+ * Set/Get alarm time of timer
+ */
+ inline void setDelay(NDB_TICKS delay_time) { m_delay = delay_time; }
+ inline NDB_TICKS getDelay() { return m_delay; }
+
+ /**
+ * Start timer
+ */
+ inline void reset() {
+ m_current_time = NdbTick_CurrentMillisecond();
+ m_alarm_time = m_current_time + m_delay;
+ }
+
+ /**
+ * Check for alarm
+ */
+ inline bool check() { return check(NdbTick_CurrentMillisecond()); }
+
+ inline bool check(NDB_TICKS check_time) {
+ /**
+ * Standard alarm check
+ */
+ if (check_time > m_alarm_time) return true;
+
+ /**
+ * Time progressing, but it is not alarm time yet
+ */
+ if (check_time >= m_current_time) return false;
+
+ /**
+ * Time has moved backwards
+ */
+ reset();
+ return false;
+ }
+
+private:
+ NDB_TICKS m_current_time;
+ NDB_TICKS m_alarm_time;
+ NDB_TICKS m_delay;
+};
diff --git a/storage/ndb/src/kernel/blocks/suma/Makefile.am b/storage/ndb/src/kernel/blocks/suma/Makefile.am
new file mode 100644
index 00000000000..5a74dbb74eb
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/suma/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LIBRARIES = libsuma.a
+
+libsuma_a_SOURCES = Suma.cpp SumaInit.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libsuma.dsp
+
+libsuma.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libsuma_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/suma/Suma.cpp b/storage/ndb/src/kernel/blocks/suma/Suma.cpp
new file mode 100644
index 00000000000..ed54505b729
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/suma/Suma.cpp
@@ -0,0 +1,4073 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Suma.hpp"
+
+#include <ndb_version.h>
+
+#include <NdbTCP.h>
+#include <Bitmask.hpp>
+#include <SimpleProperties.hpp>
+
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+
+#include <signaldata/ListTables.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/GetTableId.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <signaldata/ScanFrag.hpp>
+#include <signaldata/TransIdAI.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/AlterTrig.hpp>
+#include <signaldata/DropTrig.hpp>
+#include <signaldata/FireTrigOrd.hpp>
+#include <signaldata/TrigAttrInfo.hpp>
+#include <signaldata/CheckNodeGroups.hpp>
+#include <signaldata/GCPSave.hpp>
+#include <GrepError.hpp>
+
+#include <DebuggerNames.hpp>
+
+//#define HANDOVER_DEBUG
+//#define NODEFAIL_DEBUG
+//#define NODEFAIL_DEBUG2
+//#define DEBUG_SUMA_SEQUENCE
+//#define EVENT_DEBUG
+//#define EVENT_PH3_DEBUG
+//#define EVENT_DEBUG2
+#if 0
+#undef DBUG_ENTER
+#undef DBUG_PRINT
+#undef DBUG_RETURN
+#undef DBUG_VOID_RETURN
+
+#define DBUG_ENTER(a) {ndbout_c("%s:%d >%s", __FILE__, __LINE__, a);}
+#define DBUG_PRINT(a,b) {ndbout << __FILE__ << ":" << __LINE__ << " " << a << ": "; ndbout_c b ;}
+#define DBUG_RETURN(a) { ndbout_c("%s:%d <", __FILE__, __LINE__); return(a); }
+#define DBUG_VOID_RETURN { ndbout_c("%s:%d <", __FILE__, __LINE__); return; }
+#endif
+
+/**
+ * @todo:
+ * SUMA crashes if an index is created at the same time as
+ * global replication. Very easy to reproduce using testIndex.
+ * Note: This only happens occasionally, but is quite easy to reprod.
+ */
+
+Uint32 g_subPtrI = RNIL;
+static const Uint32 SUMA_SEQUENCE = 0xBABEBABE;
+
+
+/**************************************************************
+ *
+ * Start of suma
+ *
+ */
+
+#define PRINT_ONLY 0
+static Uint32 g_TypeOfStart = NodeState::ST_ILLEGAL_TYPE;
+
+void
+Suma::getNodeGroupMembers(Signal* signal) {
+ jam();
+ /**
+ * Ask DIH for nodeGroupMembers
+ */
+ CheckNodeGroups * sd = (CheckNodeGroups*)signal->getDataPtrSend();
+ sd->blockRef = reference();
+ sd->requestType =
+ CheckNodeGroups::Direct |
+ CheckNodeGroups::GetNodeGroupMembers;
+ sd->nodeId = getOwnNodeId();
+ EXECUTE_DIRECT(DBDIH, GSN_CHECKNODEGROUPSREQ, signal,
+ CheckNodeGroups::SignalLength);
+ jamEntry();
+
+ c_nodeGroup = sd->output;
+ c_noNodesInGroup = 0;
+ for (int i = 0; i < MAX_NDB_NODES; i++) {
+ if (sd->mask.get(i)) {
+ if (i == getOwnNodeId()) c_idInNodeGroup = c_noNodesInGroup;
+ c_nodesInGroup[c_noNodesInGroup] = i;
+ c_noNodesInGroup++;
+ }
+ }
+
+ // ndbout_c("c_noNodesInGroup=%d", c_noNodesInGroup);
+ ndbrequire(c_noNodesInGroup > 0); // at least 1 node in the nodegroup
+
+#ifdef NODEFAIL_DEBUG
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ ndbout_c ("Suma: NodeGroup %u, me %u, me in group %u, member[%u] %u",
+ c_nodeGroup, getOwnNodeId(), c_idInNodeGroup,
+ i, c_nodesInGroup[i]);
+ }
+#endif
+}
+
+void
+Suma::execSTTOR(Signal* signal) {
+ jamEntry();
+
+ DBUG_ENTER("Suma::execSTTOR");
+ const Uint32 startphase = signal->theData[1];
+ const Uint32 typeOfStart = signal->theData[7];
+
+ DBUG_PRINT("info",("startphase = %u, typeOfStart = %u", startphase, typeOfStart));
+
+ if(startphase == 1){
+ jam();
+ c_restartLock = true;
+ }
+
+ if(startphase == 3){
+ jam();
+ g_TypeOfStart = typeOfStart;
+ signal->theData[0] = reference();
+ sendSignal(NDBCNTR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+
+#if 0
+
+ /**
+ * Debug
+ */
+
+
+ SubscriptionPtr subPtr;
+ Ptr<SyncRecord> syncPtr;
+ ndbrequire(c_subscriptions.seize(subPtr));
+ ndbrequire(c_syncPool.seize(syncPtr));
+
+
+ ndbout_c("Suma: subPtr.i = %d syncPtr.i = %d", subPtr.i, syncPtr.i);
+
+ subPtr.p->m_syncPtrI = syncPtr.i;
+ subPtr.p->m_subscriptionType = SubCreateReq::DatabaseSnapshot;
+ syncPtr.p->m_subscriptionPtrI = subPtr.i;
+ syncPtr.p->ptrI = syncPtr.i;
+ g_subPtrI = subPtr.i;
+ // sendSTTORRY(signal);
+#endif
+ DBUG_VOID_RETURN;
+ }
+
+ if(startphase == 5) {
+ getNodeGroupMembers(signal);
+ if (g_TypeOfStart == NodeState::ST_NODE_RESTART) {
+ jam();
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ Uint32 ref = calcSumaBlockRef(c_nodesInGroup[i]);
+ if (ref != reference())
+ sendSignal(ref, GSN_SUMA_START_ME, signal,
+ 1 /*SumaStartMe::SignalLength*/, JBB);
+ }
+ }
+ }
+
+ if(startphase == 7) {
+ c_restartLock = false; // may be set false earlier with HANDOVER_REQ
+
+ if (g_TypeOfStart != NodeState::ST_NODE_RESTART) {
+ for( int i = 0; i < NO_OF_BUCKETS; i++) {
+ if (getResponsibleSumaNodeId(i) == refToNode(reference())) {
+ // I'm running this bucket
+ DBUG_PRINT("info",("bucket %u set to true", i));
+ c_buckets[i].active = true;
+ }
+ }
+ }
+
+ if(g_TypeOfStart == NodeState::ST_INITIAL_START &&
+ c_masterNodeId == getOwnNodeId()) {
+ jam();
+ createSequence(signal);
+ DBUG_VOID_RETURN;
+ }//if
+ }//if
+
+
+ sendSTTORRY(signal);
+
+ DBUG_VOID_RETURN;
+}
+
+void
+Suma::createSequence(Signal* signal)
+{
+ jam();
+ DBUG_ENTER("Suma::createSequence");
+
+ UtilSequenceReq * req = (UtilSequenceReq*)signal->getDataPtrSend();
+
+ req->senderData = RNIL;
+ req->sequenceId = SUMA_SEQUENCE;
+ req->requestType = UtilSequenceReq::Create;
+ sendSignal(DBUTIL_REF, GSN_UTIL_SEQUENCE_REQ,
+ signal, UtilSequenceReq::SignalLength, JBB);
+ // execUTIL_SEQUENCE_CONF will call createSequenceReply()
+ DBUG_VOID_RETURN;
+}
+
+void
+Suma::createSequenceReply(Signal* signal,
+ UtilSequenceConf * conf,
+ UtilSequenceRef * ref)
+{
+ jam();
+
+ if (ref != NULL)
+ ndbrequire(false);
+
+ sendSTTORRY(signal);
+}
+
+void
+Suma::execREAD_NODESCONF(Signal* signal){
+ jamEntry();
+ ReadNodesConf * const conf = (ReadNodesConf *)signal->getDataPtr();
+
+ c_aliveNodes.clear();
+ c_preparingNodes.clear();
+
+ Uint32 count = 0;
+ for(Uint32 i = 0; i < MAX_NDB_NODES; i++){
+ if(NodeBitmask::get(conf->allNodes, i)){
+ jam();
+
+ count++;
+
+ NodePtr node;
+ ndbrequire(c_nodes.seize(node));
+
+ node.p->nodeId = i;
+ if(NodeBitmask::get(conf->inactiveNodes, i)){
+ jam();
+ node.p->alive = 0;
+ } else {
+ jam();
+ node.p->alive = 1;
+ c_aliveNodes.set(i);
+ }
+ } else
+ jam();
+ }
+ c_masterNodeId = conf->masterNodeId;
+ ndbrequire(count == conf->noOfNodes);
+
+ sendSTTORRY(signal);
+}
+
+#if 0
+void
+Suma::execREAD_CONFIG_REQ(Signal* signal)
+{
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+ ndbrequire(req->noOfParameters == 0);
+
+ jamEntry();
+
+ const ndb_mgm_configuration_iterator * p =
+ theConfiguration.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_DB_NO_REDOLOG_FILES,
+ &cnoLogFiles));
+ ndbrequire(cnoLogFiles > 0);
+
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_FRAG, &cfragrecFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_TABLE, &ctabrecFileSize));
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_TC_CONNECT,
+ &ctcConnectrecFileSize));
+ clogFileFileSize = 4 * cnoLogFiles;
+ ndbrequire(!ndb_mgm_get_int_parameter(p, CFG_LQH_SCAN, &cscanrecFileSize));
+ cmaxAccOps = cscanrecFileSize * MAX_PARALLEL_SCANS_PER_FRAG;
+
+ initRecords();
+ initialiseRecordsLab(signal, 0, ref, senderData);
+
+ return;
+}//Dblqh::execSIZEALT_REP()
+#endif
+
+void
+Suma::sendSTTORRY(Signal* signal){
+ signal->theData[0] = 0;
+ signal->theData[3] = 1;
+ signal->theData[4] = 3;
+ signal->theData[5] = 5;
+ signal->theData[6] = 7;
+ signal->theData[7] = 255; // No more start phases from missra
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 8, JBB);
+}
+
+void
+Suma::execNDB_STTOR(Signal* signal)
+{
+ jamEntry();
+}
+
+void
+Suma::execCONTINUEB(Signal* signal){
+ jamEntry();
+}
+
+void
+SumaParticipant::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+}
+
+/*****************************************************************************
+ *
+ * Node state handling
+ *
+ *****************************************************************************/
+
+void Suma::execAPI_FAILREQ(Signal* signal)
+{
+ jamEntry();
+ DBUG_ENTER("Suma::execAPI_FAILREQ");
+ Uint32 failedApiNode = signal->theData[0];
+ //BlockReference retRef = signal->theData[1];
+
+ c_failedApiNodes.set(failedApiNode);
+ bool found = removeSubscribersOnNode(signal, failedApiNode);
+
+ if(!found){
+ jam();
+ c_failedApiNodes.clear(failedApiNode);
+ }
+ DBUG_VOID_RETURN;
+}//execAPI_FAILREQ()
+
+bool
+SumaParticipant::removeSubscribersOnNode(Signal *signal, Uint32 nodeId)
+{
+ DBUG_ENTER("SumaParticipant::removeSubscribersOnNode");
+ bool found = false;
+
+ SubscriberPtr i_subbPtr;
+ c_dataSubscribers.first(i_subbPtr);
+ while(!i_subbPtr.isNull()){
+ SubscriberPtr subbPtr = i_subbPtr;
+ c_dataSubscribers.next(i_subbPtr);
+ jam();
+ if (refToNode(subbPtr.p->m_subscriberRef) == nodeId) {
+ jam();
+ c_dataSubscribers.remove(subbPtr);
+ c_removeDataSubscribers.add(subbPtr);
+ found = true;
+ }
+ }
+ if(found){
+ jam();
+ sendSubStopReq(signal);
+ }
+ DBUG_RETURN(found);
+}
+
+void
+SumaParticipant::sendSubStopReq(Signal *signal, bool unlock){
+ DBUG_ENTER("SumaParticipant::sendSubStopReq");
+ static bool remove_lock = false;
+ jam();
+
+ SubscriberPtr subbPtr;
+ c_removeDataSubscribers.first(subbPtr);
+ if (subbPtr.isNull()){
+ jam();
+#if 0
+ signal->theData[0] = failedApiNode;
+ signal->theData[1] = reference();
+ sendSignal(retRef, GSN_API_FAILCONF, signal, 2, JBB);
+#endif
+ c_failedApiNodes.clear();
+
+ remove_lock = false;
+ DBUG_VOID_RETURN;
+ }
+
+ if(remove_lock && !unlock) {
+ jam();
+ DBUG_VOID_RETURN;
+ }
+ remove_lock = true;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subbPtr.p->m_subPtrI);
+
+ SubStopReq * const req = (SubStopReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = subbPtr.i;
+ req->subscriberRef = subbPtr.p->m_subscriberRef;
+ req->subscriberData = subbPtr.p->m_subscriberData;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->part = SubscriptionData::TableData;
+
+ sendSignal(SUMA_REF, GSN_SUB_STOP_REQ, signal, SubStopReq::SignalLength, JBB);
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::execSUB_STOP_CONF(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execSUB_STOP_CONF");
+
+ SubStopConf * const conf = (SubStopConf*)signal->getDataPtr();
+
+ // Uint32 subscriberData = conf->subscriberData;
+ // Uint32 subscriberRef = conf->subscriberRef;
+
+ Subscription key;
+ key.m_subscriptionId = conf->subscriptionId;
+ key.m_subscriptionKey = conf->subscriptionKey;
+
+ SubscriptionPtr subPtr;
+ if(c_subscriptions.find(subPtr, key)) {
+ jam();
+ if (subPtr.p->m_markRemove) {
+ jam();
+ ndbrequire(false);
+ ndbrequire(subPtr.p->m_nSubscribers > 0);
+ subPtr.p->m_nSubscribers--;
+ if (subPtr.p->m_nSubscribers == 0){
+ jam();
+ completeSubRemoveReq(signal, subPtr);
+ }
+ }
+ }
+
+ sendSubStopReq(signal,true);
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::execSUB_STOP_REF(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execSUB_STOP_REF");
+
+ SubStopRef * const ref = (SubStopRef*)signal->getDataPtr();
+
+ Uint32 subscriptionId = ref->subscriptionId;
+ Uint32 subscriptionKey = ref->subscriptionKey;
+ Uint32 part = ref->part;
+ Uint32 subscriberData = ref->subscriberData;
+ Uint32 subscriberRef = ref->subscriberRef;
+ // Uint32 err = ref->err;
+
+ if(!ref->isTemporary()){
+ ndbrequire(false);
+ }
+
+ SubStopReq * const req = (SubStopReq*)signal->getDataPtrSend();
+ req->subscriberRef = subscriberRef;
+ req->subscriberData = subscriberData;
+ req->subscriptionId = subscriptionId;
+ req->subscriptionKey = subscriptionKey;
+ req->part = part;
+
+ sendSignal(SUMA_REF, GSN_SUB_STOP_REQ, signal, SubStopReq::SignalLength, JBB);
+
+ DBUG_VOID_RETURN;
+}
+
+void
+Suma::execNODE_FAILREP(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("Suma::execNODE_FAILREP");
+
+ NodeFailRep * const rep = (NodeFailRep*)signal->getDataPtr();
+
+ bool changed = false;
+
+ NodePtr nodePtr;
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma: nodefailrep");
+#endif
+ c_nodeFailGCI = getFirstGCI(signal);
+
+ for(c_nodes.first(nodePtr); nodePtr.i != RNIL; c_nodes.next(nodePtr)){
+ if(NodeBitmask::get(rep->theNodes, nodePtr.p->nodeId)){
+ if(nodePtr.p->alive){
+ ndbassert(c_aliveNodes.get(nodePtr.p->nodeId));
+ changed = true;
+ jam();
+ } else {
+ ndbassert(!c_aliveNodes.get(nodePtr.p->nodeId));
+ jam();
+ }
+
+ if (c_preparingNodes.get(nodePtr.p->nodeId)) {
+ jam();
+ // we are currently preparing this node that died
+ // it's ok just to clear and go back to waiting for it to start up
+ Restart.resetNode(calcSumaBlockRef(nodePtr.p->nodeId));
+ c_preparingNodes.clear(nodePtr.p->nodeId);
+ } else if (c_handoverToDo) {
+ jam();
+ // TODO what if I'm a SUMA that is currently restarting and the SUMA
+ // responsible for restarting me is the one that died?
+
+ // a node has failed whilst handover is going on
+ // let's check if we're in the process of handover with that node
+ c_handoverToDo = false;
+ for( int i = 0; i < NO_OF_BUCKETS; i++) {
+ if (c_buckets[i].handover) {
+ // I'm doing handover, but is it with the dead node?
+ if (getResponsibleSumaNodeId(i) == nodePtr.p->nodeId) {
+ // so it was the dead node, has handover started?
+ if (c_buckets[i].handover_started) {
+ jam();
+ // we're not ok and will have lost data!
+ // set not active to indicate this -
+ // this will generate takeover behaviour
+ c_buckets[i].active = false;
+ c_buckets[i].handover_started = false;
+ } // else we're ok to revert back to state before
+ c_buckets[i].handover = false;
+ } else {
+ jam();
+ // ok, we're doing handover with a different node
+ c_handoverToDo = true;
+ }
+ }
+ }
+ }
+
+ c_failoverBuffer.nodeFailRep();
+
+ nodePtr.p->alive = 0;
+ c_aliveNodes.clear(nodePtr.p->nodeId); // this has to be done after the loop above
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+void
+Suma::execINCL_NODEREQ(Signal* signal){
+ jamEntry();
+
+ //const Uint32 senderRef = signal->theData[0];
+ const Uint32 inclNode = signal->theData[1];
+
+ NodePtr node;
+ for(c_nodes.first(node); node.i != RNIL; c_nodes.next(node)){
+ jam();
+ const Uint32 nodeId = node.p->nodeId;
+ if(inclNode == nodeId){
+ jam();
+
+ ndbrequire(node.p->alive == 0);
+ ndbrequire(!c_aliveNodes.get(nodeId));
+
+ for (Uint32 j = 0; j < c_noNodesInGroup; j++) {
+ jam();
+ if (c_nodesInGroup[j] == nodeId) {
+ // the starting node is part of my node group
+ jam();
+ c_preparingNodes.set(nodeId); // set as being prepared
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ jam();
+ if (i == c_idInNodeGroup) {
+ jam();
+ // I'm responsible for restarting this SUMA
+ // ALL dict's should have meta data info so it is ok to start
+ Restart.startNode(signal, calcSumaBlockRef(nodeId));
+ break;
+ }//if
+ if (c_aliveNodes.get(c_nodesInGroup[i])) {
+ jam();
+ break; // another Suma takes care of this
+ }//if
+ }//for
+ break;
+ }//if
+ }//for
+
+ node.p->alive = 1;
+ c_aliveNodes.set(nodeId);
+
+ break;
+ }//if
+ }//for
+
+#if 0 // if we include this DIH's got to be prepared, later if needed...
+ signal->theData[0] = reference();
+
+ sendSignal(senderRef, GSN_INCL_NODECONF, signal, 1, JBB);
+#endif
+}
+
+void
+Suma::execSIGNAL_DROPPED_REP(Signal* signal){
+ jamEntry();
+ ndbrequire(false);
+}
+
+/********************************************************************
+ *
+ * Dump state
+ *
+ */
+
+static unsigned
+count_subscribers(const DLList<SumaParticipant::Subscriber> &subs)
+{
+ unsigned n= 0;
+ SumaParticipant::SubscriberPtr i_subbPtr;
+ subs.first(i_subbPtr);
+ while(!i_subbPtr.isNull()){
+ n++;
+ subs.next(i_subbPtr);
+ }
+ return n;
+}
+
+void
+Suma::execDUMP_STATE_ORD(Signal* signal){
+ jamEntry();
+
+ Uint32 tCase = signal->theData[0];
+ if(tCase >= 8000 && tCase <= 8003){
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, g_subPtrI);
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+
+ if(tCase == 8000){
+ syncPtr.p->startMeta(signal);
+ }
+
+ if(tCase == 8001){
+ syncPtr.p->startScan(signal);
+ }
+
+ if(tCase == 8002){
+ syncPtr.p->startTrigger(signal);
+ }
+
+ if(tCase == 8003){
+ subPtr.p->m_subscriptionType = SubCreateReq::SingleTableScan;
+ LocalDataBuffer<15> attrs(c_dataBufferPool, syncPtr.p->m_attributeList);
+ Uint32 tab = 0;
+ Uint32 att[] = { 0, 1, 1 };
+ syncPtr.p->m_tableList.append(&tab, 1);
+ attrs.append(att, 3);
+ }
+ }
+
+ if(tCase == 8004){
+ infoEvent("Suma: c_subscriberPool size: %d free: %d",
+ c_subscriberPool.getSize(),
+ c_subscriberPool.getNoOfFree());
+
+ infoEvent("Suma: c_tablePool size: %d free: %d",
+ c_tablePool_.getSize(),
+ c_tablePool_.getNoOfFree());
+
+ infoEvent("Suma: c_subscriptionPool size: %d free: %d",
+ c_subscriptionPool.getSize(),
+ c_subscriptionPool.getNoOfFree());
+
+ infoEvent("Suma: c_syncPool size: %d free: %d",
+ c_syncPool.getSize(),
+ c_syncPool.getNoOfFree());
+
+ infoEvent("Suma: c_dataBufferPool size: %d free: %d",
+ c_dataBufferPool.getSize(),
+ c_dataBufferPool.getNoOfFree());
+
+ infoEvent("Suma: c_metaSubscribers count: %d",
+ count_subscribers(c_metaSubscribers));
+ infoEvent("Suma: c_dataSubscribers count: %d",
+ count_subscribers(c_dataSubscribers));
+ infoEvent("Suma: c_prepDataSubscribers count: %d",
+ count_subscribers(c_prepDataSubscribers));
+ infoEvent("Suma: c_removeDataSubscribers count: %d",
+ count_subscribers(c_removeDataSubscribers));
+ }
+}
+
+/********************************************************************
+ *
+ * Convert a table name (db+schema+tablename) to tableId
+ *
+ */
+
+#if 0
+void
+SumaParticipant::convertNameToId(SubscriptionPtr subPtr, Signal * signal)
+{
+ jam();
+ if(subPtr.p->m_currentTable < subPtr.p->m_maxTables) {
+ jam();
+
+ GetTableIdReq * req = (GetTableIdReq *)signal->getDataPtrSend();
+ char * tableName = subPtr.p->m_tableNames[subPtr.p->m_currentTable];
+ const Uint32 strLen = strlen(tableName) + 1; // NULL Terminated
+ req->senderRef = reference();
+ req->senderData = subPtr.i;
+ req->len = strLen;
+
+ LinearSectionPtr ptr[1];
+ ptr[0].p = (Uint32*)tableName;
+ ptr[0].sz = strLen;
+
+ sendSignal(DBDICT_REF,
+ GSN_GET_TABLEID_REQ,
+ signal,
+ GetTableIdReq::SignalLength,
+ JBB,
+ ptr,
+ 1);
+ } else {
+ jam();
+ sendSubCreateConf(signal, subPtr.p->m_subscriberRef, subPtr);
+ }
+}
+#endif
+
+
+void
+SumaParticipant::addTableId(Uint32 tableId,
+ SubscriptionPtr subPtr, SyncRecord *psyncRec)
+{
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("SumaParticipant::addTableId(%u,%u,%u), current_table=%u",
+ tableId, subPtr.i, psyncRec, subPtr.p->m_currentTable);
+#endif
+ subPtr.p->m_tables[tableId] = 1;
+ subPtr.p->m_currentTable++;
+ if(psyncRec != NULL)
+ psyncRec->m_tableList.append(&tableId, 1);
+}
+
+#if 0
+void
+SumaParticipant::execGET_TABLEID_CONF(Signal * signal)
+{
+ jamEntry();
+
+ GetTableIdConf* conf = (GetTableIdConf *)signal->getDataPtr();
+ Uint32 tableId = conf->tableId;
+ //Uint32 schemaVersion = conf->schemaVersion;
+ Uint32 senderData = conf->senderData;
+
+ SubscriptionPtr subPtr;
+ Ptr<SyncRecord> syncPtr;
+
+ c_subscriptions.getPtr(subPtr, senderData);
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+
+ /*
+ * add to m_tableList
+ */
+ addTableId(tableId, subPtr, syncPtr.p);
+
+ convertNameToId(subPtr, signal);
+}
+
+void
+SumaParticipant::execGET_TABLEID_REF(Signal * signal)
+{
+ jamEntry();
+ GetTableIdRef const * ref = (GetTableIdRef *)signal->getDataPtr();
+ Uint32 senderData = ref->senderData;
+ // Uint32 err = ref->err;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, senderData);
+ Uint32 subData = subPtr.p->m_subscriberData;
+ SubCreateRef * reff = (SubCreateRef*)ref;
+ /**
+ * @todo: map ref->err to GrepError.
+ */
+ reff->err = GrepError::SELECTED_TABLE_NOT_FOUND;
+ reff->subscriberData = subData;
+ sendSignal(subPtr.p->m_subscriberRef,
+ GSN_SUB_CREATE_REF,
+ signal,
+ SubCreateRef::SignalLength,
+ JBB);
+}
+#endif
+
+
+/*************************************************************
+ *
+ * Creation of subscription id's
+ *
+ ************************************************************/
+
+void
+Suma::execCREATE_SUBID_REQ(Signal* signal)
+{
+ jamEntry();
+
+ CRASH_INSERTION(13001);
+
+ CreateSubscriptionIdReq const * req =
+ (CreateSubscriptionIdReq*)signal->getDataPtr();
+ SubscriberPtr subbPtr;
+ if(!c_subscriberPool.seize(subbPtr)){
+ jam();
+ sendSubIdRef(signal, GrepError::SUBSCRIPTION_ID_NOMEM);
+ return;
+ }
+
+ subbPtr.p->m_subscriberRef = signal->getSendersBlockRef();
+ subbPtr.p->m_senderData = req->senderData;
+ subbPtr.p->m_subscriberData = subbPtr.i;
+
+ UtilSequenceReq * utilReq = (UtilSequenceReq*)signal->getDataPtrSend();
+
+ utilReq->senderData = subbPtr.p->m_subscriberData;
+ utilReq->sequenceId = SUMA_SEQUENCE;
+ utilReq->requestType = UtilSequenceReq::NextVal;
+ sendSignal(DBUTIL_REF, GSN_UTIL_SEQUENCE_REQ,
+ signal, UtilSequenceReq::SignalLength, JBB);
+}
+
+void
+Suma::execUTIL_SEQUENCE_CONF(Signal* signal)
+{
+ jamEntry();
+
+ DBUG_ENTER("Suma::execUTIL_SEQUENCE_CONF");
+ CRASH_INSERTION(13002);
+
+ UtilSequenceConf * conf = (UtilSequenceConf*)signal->getDataPtr();
+ if(conf->requestType == UtilSequenceReq::Create) {
+ jam();
+ createSequenceReply(signal, conf, NULL);
+ DBUG_VOID_RETURN;
+ }
+
+ Uint64 subId;
+ memcpy(&subId,conf->sequenceValue,8);
+ Uint32 subData = conf->senderData;
+
+ SubscriberPtr subbPtr;
+ c_subscriberPool.getPtr(subbPtr,subData);
+
+
+ CreateSubscriptionIdConf * subconf = (CreateSubscriptionIdConf*)conf;
+ subconf->subscriptionId = (Uint32)subId;
+ subconf->subscriptionKey =(getOwnNodeId() << 16) | (Uint32)(subId & 0xFFFF);
+ subconf->subscriberData = subbPtr.p->m_senderData;
+
+ sendSignal(subbPtr.p->m_subscriberRef, GSN_CREATE_SUBID_CONF, signal,
+ CreateSubscriptionIdConf::SignalLength, JBB);
+
+ c_subscriberPool.release(subbPtr);
+
+ DBUG_VOID_RETURN;
+}
+
+void
+Suma::execUTIL_SEQUENCE_REF(Signal* signal)
+{
+ jamEntry();
+ DBUG_ENTER("Suma::execUTIL_SEQUENCE_REF");
+ UtilSequenceRef * ref = (UtilSequenceRef*)signal->getDataPtr();
+
+ if(ref->requestType == UtilSequenceReq::Create) {
+ jam();
+ createSequenceReply(signal, NULL, ref);
+ DBUG_VOID_RETURN;
+ }
+
+ Uint32 subData = ref->senderData;
+
+ SubscriberPtr subbPtr;
+ c_subscriberPool.getPtr(subbPtr,subData);
+ sendSubIdRef(signal, GrepError::SEQUENCE_ERROR);
+ c_subscriberPool.release(subbPtr);
+ DBUG_VOID_RETURN;
+}//execUTIL_SEQUENCE_REF()
+
+
+void
+SumaParticipant::sendSubIdRef(Signal* signal, Uint32 errCode){
+ jam();
+ CreateSubscriptionIdRef * ref =
+ (CreateSubscriptionIdRef *)signal->getDataPtrSend();
+
+ ref->err = errCode;
+ sendSignal(signal->getSendersBlockRef(),
+ GSN_CREATE_SUBID_REF,
+ signal,
+ CreateSubscriptionIdRef::SignalLength,
+ JBB);
+
+ releaseSections(signal);
+ return;
+}
+
+/**********************************************************
+ * Suma participant interface
+ *
+ * Creation of subscriptions
+ */
+
+void
+SumaParticipant::execSUB_CREATE_REQ(Signal* signal) {
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("SumaParticipant::execSUB_CREATE_REQ");
+#endif
+ jamEntry();
+
+ CRASH_INSERTION(13003);
+
+ const SubCreateReq req = *(SubCreateReq*)signal->getDataPtr();
+
+ const Uint32 subId = req.subscriptionId;
+ const Uint32 subKey = req.subscriptionKey;
+ const Uint32 subRef = req.subscriberRef;
+ const Uint32 subData = req.subscriberData;
+ const Uint32 type = req.subscriptionType & SubCreateReq::RemoveFlags;
+ const Uint32 flags = req.subscriptionType & SubCreateReq::GetFlags;
+ const bool addTableFlag = (flags & SubCreateReq::AddTableFlag) != 0;
+ const bool restartFlag = (flags & SubCreateReq::RestartFlag) != 0;
+
+ const Uint32 sender = signal->getSendersBlockRef();
+
+ Subscription key;
+ key.m_subscriptionId = subId;
+ key.m_subscriptionKey = subKey;
+
+ SubscriptionPtr subPtr;
+ Ptr<SyncRecord> syncPtr;
+
+ if (addTableFlag) {
+ ndbrequire(restartFlag); //TODO remove this
+
+ if(!c_subscriptions.find(subPtr, key)) {
+ jam();
+ sendSubCreateRef(signal, req, GrepError::SUBSCRIPTION_NOT_FOUND);
+ return;
+ }
+ jam();
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ } else {
+ // Check that id/key is unique
+ if(c_subscriptions.find(subPtr, key)) {
+ jam();
+ sendSubCreateRef(signal, req, GrepError::SUBSCRIPTION_ID_NOT_UNIQUE);
+ return;
+ }
+ if(!c_subscriptions.seize(subPtr)) {
+ jam();
+ sendSubCreateRef(signal, req, GrepError::NOSPACE_IN_POOL);
+ return;
+ }
+ if(!c_syncPool.seize(syncPtr)) {
+ jam();
+ sendSubCreateRef(signal, req, GrepError::NOSPACE_IN_POOL);
+ return;
+ }
+ jam();
+ subPtr.p->m_subscriberRef = subRef;
+ subPtr.p->m_subscriberData = subData;
+ subPtr.p->m_subscriptionId = subId;
+ subPtr.p->m_subscriptionKey = subKey;
+ subPtr.p->m_subscriptionType = type;
+
+ /**
+ * ok to memset? Support on all compilers
+ * @todo find out if memset is supported by all compilers
+ */
+ memset(subPtr.p->m_tables,0,MAX_TABLES);
+ subPtr.p->m_maxTables = 0;
+ subPtr.p->m_currentTable = 0;
+ subPtr.p->m_syncPtrI = syncPtr.i;
+ subPtr.p->m_markRemove = false;
+ subPtr.p->m_nSubscribers = 0;
+
+ c_subscriptions.add(subPtr);
+
+ syncPtr.p->m_subscriptionPtrI = subPtr.i;
+ syncPtr.p->m_doSendSyncData = true;
+ syncPtr.p->ptrI = syncPtr.i;
+ syncPtr.p->m_locked = false;
+ syncPtr.p->m_error = false;
+ }
+
+ if (restartFlag ||
+ type == SubCreateReq::TableEvent) {
+
+ syncPtr.p->m_doSendSyncData = false;
+
+ ndbrequire(type != SubCreateReq::SingleTableScan);
+ jam();
+
+ if (subPtr.p->m_tables[req.tableId] != 0) {
+ ndbrequire(false); //TODO remove
+ jam();
+ sendSubCreateRef(signal, req, GrepError::SELECTED_TABLE_ALREADY_ADDED);
+ return;
+ }
+ if (addTableFlag) {
+ ndbrequire(type != SubCreateReq::TableEvent);
+ jam();
+ }
+ subPtr.p->m_maxTables++;
+ addTableId(req.tableId, subPtr, syncPtr.p);
+ } else {
+ switch(type){
+ case SubCreateReq::SingleTableScan:
+ {
+ jam();
+ syncPtr.p->m_tableList.append(&req.tableId, 1);
+ if(signal->getNoOfSections() > 0){
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, SubCreateReq::ATTRIBUTE_LIST);
+ LocalDataBuffer<15> attrBuf(c_dataBufferPool,syncPtr.p->m_attributeList);
+ append(attrBuf, ptr, getSectionSegmentPool());
+ }
+ }
+ break;
+#if 0
+ case SubCreateReq::SelectiveTableSnapshot:
+ /**
+ * Tables specified by the user that does not exist
+ * in the database are just ignored. No error message
+ * is given, nor does the db nodes crash
+ * @todo: Memory is not release here (used tableBuf)
+ */
+ {
+ if(signal->getNoOfSections() == 0 ){
+ jam();
+ sendSubCreateRef(signal, req, GrepError::WRONG_NO_OF_SECTIONS);
+ return;
+ }
+
+ jam();
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr,0);// SubCreateReq::TABLE_LIST);
+ SimplePropertiesSectionReader r0(ptr, getSectionSegmentPool());
+ Uint32 i=0;
+ char table[MAX_TAB_NAME_SIZE];
+ r0.reset();
+ r0.first();
+ while(true){
+ if ((r0.getValueType() != SimpleProperties::StringValue) ||
+ (r0.getValueLen() <= 0)) {
+ releaseSections(signal);
+ ndbrequire(false);
+ }
+ r0.getString(table);
+ strcpy(subPtr.p->m_tableNames[i],table);
+ i++;
+ if(!r0.next())
+ break;
+ }
+ releaseSections(signal);
+ subPtr.p->m_maxTables = i;
+ subPtr.p->m_currentTable = 0;
+ releaseSections(signal);
+ convertNameToId(subPtr, signal);
+ return;
+ }
+ break;
+#endif
+ case SubCreateReq::DatabaseSnapshot:
+ {
+ jam();
+ }
+ break;
+ default:
+ ndbrequire(false);
+ }
+ }
+
+ sendSubCreateConf(signal, sender, subPtr);
+
+ return;
+}
+
+void
+SumaParticipant::sendSubCreateConf(Signal* signal, Uint32 sender,
+ SubscriptionPtr subPtr)
+{
+ SubCreateConf * const conf = (SubCreateConf*)signal->getDataPtrSend();
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ conf->subscriberData = subPtr.p->m_subscriberData;
+ sendSignal(sender, GSN_SUB_CREATE_CONF, signal,
+ SubCreateConf::SignalLength, JBB);
+}
+
+void
+SumaParticipant::sendSubCreateRef(Signal* signal, const SubCreateReq& req, Uint32 errCode){
+ jam();
+ SubCreateRef * ref = (SubCreateRef *)signal->getDataPtrSend();
+ ref->subscriberRef = reference();
+ ref->subscriberData = req.subscriberData;
+ ref->err = errCode;
+ releaseSections(signal);
+ sendSignal(signal->getSendersBlockRef(), GSN_SUB_CREATE_REF, signal,
+ SubCreateRef::SignalLength, JBB);
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+Uint32
+SumaParticipant::getFirstGCI(Signal* signal) {
+ if (c_lastCompleteGCI == RNIL) {
+ ndbout_c("WARNING: c_lastCompleteGCI == RNIL");
+ return 0;
+ }
+ return c_lastCompleteGCI+3;
+}
+
+/**********************************************************
+ *
+ * Setting upp trigger for subscription
+ *
+ */
+
+void
+SumaParticipant::execSUB_SYNC_REQ(Signal* signal) {
+ jamEntry();
+
+ CRASH_INSERTION(13004);
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("SumaParticipant::execSUB_SYNC_REQ");
+#endif
+
+ SubSyncReq * const req = (SubSyncReq*)signal->getDataPtr();
+
+ SubscriptionPtr subPtr;
+ Subscription key;
+ key.m_subscriptionId = req->subscriptionId;
+ key.m_subscriptionKey = req->subscriptionKey;
+
+ if(!c_subscriptions.find(subPtr, key)){
+ jam();
+ sendSubSyncRef(signal, GrepError::SUBSCRIPTION_ID_NOT_FOUND);
+ return;
+ }
+
+ /**
+ * @todo Tomas, do you really need to do this?
+ */
+ if(subPtr.p->m_subscriptionType == SubCreateReq::TableEvent) {
+ jam();
+ subPtr.p->m_subscriberData = req->subscriberData;
+ }
+
+ bool ok = false;
+ SubscriptionData::Part part = (SubscriptionData::Part)req->part;
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ switch(part){
+ case SubscriptionData::MetaData:
+ ok = true;
+ jam();
+ if (subPtr.p->m_subscriptionType == SubCreateReq::DatabaseSnapshot) {
+ TableList::DataBufferIterator it;
+ syncPtr.p->m_tableList.first(it);
+ if(it.isNull()) {
+ /**
+ * Get all tables from dict
+ */
+ ListTablesReq * req = (ListTablesReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = syncPtr.i;
+ req->requestData = 0;
+ /**
+ * @todo: accomodate scan of index tables?
+ */
+ req->setTableType(DictTabInfo::UserTable);
+
+ sendSignal(DBDICT_REF, GSN_LIST_TABLES_REQ, signal,
+ ListTablesReq::SignalLength, JBB);
+ break;
+ }
+ }
+
+ syncPtr.p->startMeta(signal);
+ break;
+ case SubscriptionData::TableData: {
+ ok = true;
+ jam();
+ syncPtr.p->startScan(signal);
+ break;
+ }
+ }
+ ndbrequire(ok);
+}
+
+void
+SumaParticipant::sendSubSyncRef(Signal* signal, Uint32 errCode){
+ jam();
+ SubSyncRef * ref =
+ (SubSyncRef *)signal->getDataPtrSend();
+ ref->err = errCode;
+ sendSignal(signal->getSendersBlockRef(),
+ GSN_SUB_SYNC_REF,
+ signal,
+ SubSyncRef::SignalLength,
+ JBB);
+
+ releaseSections(signal);
+ return;
+}
+
+/**********************************************************
+ * Dict interface
+ */
+
+void
+SumaParticipant::execLIST_TABLES_CONF(Signal* signal){
+ jamEntry();
+ CRASH_INSERTION(13005);
+ ListTablesConf* const conf = (ListTablesConf*)signal->getDataPtr();
+ SyncRecord* tmp = c_syncPool.getPtr(conf->senderData);
+ tmp->runLIST_TABLES_CONF(signal);
+}
+
+
+void
+SumaParticipant::execGET_TABINFOREF(Signal* signal){
+ jamEntry();
+ GetTabInfoRef* const ref = (GetTabInfoRef*)signal->getDataPtr();
+ SyncRecord* tmp = c_syncPool.getPtr(ref->senderData);
+ tmp->runGET_TABINFOREF(signal);
+}
+
+void
+SumaParticipant::execGET_TABINFO_CONF(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13006);
+
+ if(!assembleFragments(signal)){
+ return;
+ }
+
+ GetTabInfoConf* conf = (GetTabInfoConf*)signal->getDataPtr();
+
+ Uint32 tableId = conf->tableId;
+ Uint32 senderData = conf->senderData;
+
+ SyncRecord* tmp = c_syncPool.getPtr(senderData);
+ ndbrequire(parseTable(signal, conf, tableId, tmp));
+ tmp->runGET_TABINFO_CONF(signal);
+}
+
+bool
+SumaParticipant::parseTable(Signal* signal, GetTabInfoConf* conf, Uint32 tableId,
+ SyncRecord* syncPtr_p){
+
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, GetTabInfoConf::DICT_TAB_INFO);
+
+ SimplePropertiesSectionReader it(ptr, getSectionSegmentPool());
+
+ SimpleProperties::UnpackStatus s;
+ DictTabInfo::Table tableDesc; tableDesc.init();
+ s = SimpleProperties::unpack(it, &tableDesc,
+ DictTabInfo::TableMapping,
+ DictTabInfo::TableMappingSize,
+ true, true);
+
+ ndbrequire(s == SimpleProperties::Break);
+
+ TablePtr tabPtr;
+ c_tables.find(tabPtr, tableId);
+
+ if(!tabPtr.isNull() &&
+ tabPtr.p->m_schemaVersion != tableDesc.TableVersion){
+ jam();
+
+ tabPtr.p->release(* this);
+
+ // oops wrong schema version in stored tabledesc
+ // we need to find all subscriptions with old table desc
+ // and all subscribers to this
+ // hopefully none
+ c_tables.release(tabPtr);
+ tabPtr.setNull();
+ DLHashTable<SumaParticipant::Subscription>::Iterator i_subPtr;
+ c_subscriptions.first(i_subPtr);
+ SubscriptionPtr subPtr;
+ for(;!i_subPtr.isNull();c_subscriptions.next(i_subPtr)){
+ jam();
+ c_subscriptions.getPtr(subPtr, i_subPtr.curr.i);
+ SyncRecord* tmp = c_syncPool.getPtr(subPtr.p->m_syncPtrI);
+ if (tmp == syncPtr_p) {
+ jam();
+ continue;
+ }
+ if (subPtr.p->m_tables[tableId]) {
+ jam();
+ subPtr.p->m_tables[tableId] = 0; // remove this old table reference
+ TableList::DataBufferIterator it;
+ for(tmp->m_tableList.first(it);!it.isNull();tmp->m_tableList.next(it)) {
+ jam();
+ if (*it.data == tableId){
+ jam();
+ Uint32 *pdata = it.data;
+ tmp->m_tableList.next(it);
+ for(;!it.isNull();tmp->m_tableList.next(it)) {
+ jam();
+ *pdata = *it.data;
+ pdata = it.data;
+ }
+ *pdata = RNIL; // todo remove this last item...
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (tabPtr.isNull()) {
+ jam();
+ /**
+ * Uninitialized table record
+ */
+ ndbrequire(c_tables.seize(tabPtr));
+ new (tabPtr.p) Table;
+ tabPtr.p->m_schemaVersion = RNIL;
+ tabPtr.p->m_tableId = tableId;
+ tabPtr.p->m_hasTriggerDefined[0] = 0;
+ tabPtr.p->m_hasTriggerDefined[1] = 0;
+ tabPtr.p->m_hasTriggerDefined[2] = 0;
+ tabPtr.p->m_triggerIds[0] = ILLEGAL_TRIGGER_ID;
+ tabPtr.p->m_triggerIds[1] = ILLEGAL_TRIGGER_ID;
+ tabPtr.p->m_triggerIds[2] = ILLEGAL_TRIGGER_ID;
+#if 0
+ ndbout_c("Get tab info conf %d", tableId);
+#endif
+ c_tables.add(tabPtr);
+ }
+
+ if(tabPtr.p->m_attributes.getSize() != 0){
+ jam();
+ return true;
+ }
+
+ /**
+ * Initialize table object
+ */
+ Uint32 noAttribs = tableDesc.NoOfAttributes;
+ Uint32 notFixed = (tableDesc.NoOfNullable+tableDesc.NoOfVariable);
+ tabPtr.p->m_schemaVersion = tableDesc.TableVersion;
+
+ // The attribute buffer
+ LocalDataBuffer<15> attrBuf(c_dataBufferPool, tabPtr.p->m_attributes);
+
+ // Temporary buffer
+ DataBuffer<15> theRest(c_dataBufferPool);
+
+ if(!attrBuf.seize(noAttribs)){
+ ndbrequire(false);
+ return false;
+ }
+
+ if(!theRest.seize(notFixed)){
+ ndbrequire(false);
+ return false;
+ }
+
+ DataBuffer<15>::DataBufferIterator attrIt; // Fixed not nullable
+ DataBuffer<15>::DataBufferIterator restIt; // variable + nullable
+ attrBuf.first(attrIt);
+ theRest.first(restIt);
+
+ for(Uint32 i = 0; i < noAttribs; i++) {
+ DictTabInfo::Attribute attrDesc; attrDesc.init();
+ s = SimpleProperties::unpack(it, &attrDesc,
+ DictTabInfo::AttributeMapping,
+ DictTabInfo::AttributeMappingSize,
+ true, true);
+ ndbrequire(s == SimpleProperties::Break);
+
+ if (!attrDesc.AttributeNullableFlag
+ /* && !attrDesc.AttributeVariableFlag */) {
+ jam();
+ * attrIt.data = attrDesc.AttributeId;
+ attrBuf.next(attrIt);
+ } else {
+ jam();
+ * restIt.data = attrDesc.AttributeId;
+ theRest.next(restIt);
+ }
+
+ // Move to next attribute
+ it.next();
+ }
+
+ /**
+ * Put the rest in end of attrBuf
+ */
+ theRest.first(restIt);
+ for(; !restIt.isNull(); theRest.next(restIt)){
+ * attrIt.data = * restIt.data;
+ attrBuf.next(attrIt);
+ }
+
+ theRest.release();
+
+ return true;
+}
+
+void
+SumaParticipant::execDI_FCOUNTCONF(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13007);
+
+ const Uint32 senderData = signal->theData[3];
+ SyncRecord* tmp = c_syncPool.getPtr(senderData);
+ tmp->runDI_FCOUNTCONF(signal);
+}
+
+void
+SumaParticipant::execDIGETPRIMCONF(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13008);
+
+ const Uint32 senderData = signal->theData[1];
+ SyncRecord* tmp = c_syncPool.getPtr(senderData);
+ tmp->runDIGETPRIMCONF(signal);
+}
+
+void
+SumaParticipant::execCREATE_TRIG_CONF(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execCREATE_TRIG_CONF");
+ CRASH_INSERTION(13009);
+
+ CreateTrigConf * const conf = (CreateTrigConf*)signal->getDataPtr();
+
+ const Uint32 senderData = conf->getConnectionPtr();
+ SyncRecord* tmp = c_syncPool.getPtr(senderData);
+ tmp->runCREATE_TRIG_CONF(signal);
+
+ /**
+ * dodido
+ * @todo: I (Johan) dont know what to do here. Jonas, what do you mean?
+ */
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::execCREATE_TRIG_REF(Signal* signal){
+ jamEntry();
+ ndbrequire(false);
+}
+
+void
+SumaParticipant::execDROP_TRIG_CONF(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execDROP_TRIG_CONF");
+ CRASH_INSERTION(13010);
+
+ DropTrigConf * const conf = (DropTrigConf*)signal->getDataPtr();
+
+ const Uint32 senderData = conf->getConnectionPtr();
+ SyncRecord* tmp = c_syncPool.getPtr(senderData);
+ tmp->runDROP_TRIG_CONF(signal);
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::execDROP_TRIG_REF(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execDROP_TRIG_CONF");
+ DropTrigRef * const ref = (DropTrigRef*)signal->getDataPtr();
+
+ const Uint32 senderData = ref->getConnectionPtr();
+ SyncRecord* tmp = c_syncPool.getPtr(senderData);
+ tmp->runDROP_TRIG_CONF(signal);
+ DBUG_VOID_RETURN;
+}
+
+/*************************************************************************
+ *
+ *
+ */
+
+void
+SumaParticipant::SyncRecord::runLIST_TABLES_CONF(Signal* signal){
+ jam();
+
+ ListTablesConf * const conf = (ListTablesConf*)signal->getDataPtr();
+ const Uint32 len = signal->length() - ListTablesConf::HeaderLength;
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+
+ for (unsigned i = 0; i < len; i++) {
+ subPtr.p->m_maxTables++;
+ suma.addTableId(ListTablesConf::getTableId(conf->tableData[i]), subPtr, this);
+ }
+
+ // for (unsigned i = 0; i < len; i++)
+ // conf->tableData[i] = ListTablesConf::getTableId(conf->tableData[i]);
+ // m_tableList.append(&conf->tableData[0], len);
+
+#if 0
+ TableList::DataBufferIterator it;
+ int i = 0;
+ for(m_tableList.first(it);!it.isNull();m_tableList.next(it)) {
+ ndbout_c("%u listtableconf tableid %d", i++, *it.data);
+ }
+#endif
+
+ if(len == ListTablesConf::DataLength){
+ jam();
+ // we expect more LIST_TABLE_CONF
+ return;
+ }
+
+#if 0
+ subPtr.p->m_currentTable = 0;
+ subPtr.p->m_maxTables = 0;
+
+ TableList::DataBufferIterator it;
+ for(m_tableList.first(it); !it.isNull(); m_tableList.next(it)) {
+ subPtr.p->m_maxTables++;
+ suma.addTableId(*it.data, subPtr, NULL);
+#ifdef NODEFAIL_DEBUG
+ ndbout_c(" listtableconf tableid %d",*it.data);
+#endif
+ }
+#endif
+
+ startMeta(signal);
+}
+
+void
+SumaParticipant::SyncRecord::startMeta(Signal* signal){
+ jam();
+ m_currentTable = 0;
+ nextMeta(signal);
+}
+
+/**
+ * m_tableList only contains UserTables
+ */
+void
+SumaParticipant::SyncRecord::nextMeta(Signal* signal){
+ jam();
+
+ TableList::DataBufferIterator it;
+ if(!m_tableList.position(it, m_currentTable)){
+ completeMeta(signal);
+ return;
+ }
+
+ GetTabInfoReq * req = (GetTabInfoReq *)signal->getDataPtrSend();
+ req->senderRef = suma.reference();
+ req->senderData = ptrI;
+ req->requestType =
+ GetTabInfoReq::RequestById | GetTabInfoReq::LongSignalConf;
+ req->tableId = * it.data;
+
+#if 0
+ ndbout_c("GET_TABINFOREQ id %d", req->tableId);
+#endif
+ suma.sendSignal(DBDICT_REF, GSN_GET_TABINFOREQ, signal,
+ GetTabInfoReq::SignalLength, JBB);
+}
+
+void
+SumaParticipant::SyncRecord::runGET_TABINFOREF(Signal* signal)
+{
+ jam();
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+ Uint32 type = subPtr.p->m_subscriptionType;
+
+ bool do_continue = false;
+ switch (type) {
+ case SubCreateReq::TableEvent:
+ jam();
+ break;
+ case SubCreateReq::DatabaseSnapshot:
+ jam();
+ do_continue = true;
+ break;
+ case SubCreateReq::SelectiveTableSnapshot:
+ jam();
+ do_continue = true;
+ break;
+ case SubCreateReq::SingleTableScan:
+ jam();
+ break;
+ default:
+ ndbrequire(false);
+ break;
+ }
+
+ if (! do_continue) {
+ m_error = true;
+ completeMeta(signal);
+ return;
+ }
+
+ m_currentTable++;
+ nextMeta(signal);
+ return;
+
+ // now we need to clean-up
+}
+
+
+void
+SumaParticipant::SyncRecord::runGET_TABINFO_CONF(Signal* signal){
+ jam();
+
+ GetTabInfoConf * const conf = (GetTabInfoConf*)signal->getDataPtr();
+ // const Uint32 gci = conf->gci;
+ const Uint32 tableId = conf->tableId;
+ TableList::DataBufferIterator it;
+
+ ndbrequire(m_tableList.position(it, m_currentTable));
+ ndbrequire(* it.data == tableId);
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, GetTabInfoConf::DICT_TAB_INFO);
+
+ SubMetaData * data = (SubMetaData*)signal->getDataPtrSend();
+ /**
+ * sending lastCompleteGCI. Used by Lars in interval calculations
+ * incremenet by one, since last_CompleteGCI is the not the current gci.
+ */
+ data->gci = suma.c_lastCompleteGCI + 1;
+ data->tableId = tableId;
+ data->senderData = subPtr.p->m_subscriberData;
+#if PRINT_ONLY
+ ndbout_c("GSN_SUB_META_DATA Table %d", tableId);
+#else
+
+ bool okToSend = m_doSendSyncData;
+
+ /*
+ * If it is a selectivetablesnapshot and the table is not part of the
+ * subscription, then do not send anything, just continue.
+ * If it is a tablevent, don't send regardless since the APIs are not
+ * interested in meta data.
+ */
+ if(subPtr.p->m_subscriptionType == SubCreateReq::SelectiveTableSnapshot)
+ if(!subPtr.p->m_tables[tableId])
+ okToSend = false;
+
+ if(okToSend) {
+ if(refToNode(subPtr.p->m_subscriberRef) == 0){
+ jam();
+ suma.EXECUTE_DIRECT(refToBlock(subPtr.p->m_subscriberRef),
+ GSN_SUB_META_DATA,
+ signal,
+ SubMetaData::SignalLength);
+ jamEntry();
+ suma.releaseSections(signal);
+ } else {
+ jam();
+ suma.sendSignal(subPtr.p->m_subscriberRef,
+ GSN_SUB_META_DATA,
+ signal,
+ SubMetaData::SignalLength, JBB);
+ }
+ }
+#endif
+
+ TablePtr tabPtr;
+ ndbrequire(suma.c_tables.find(tabPtr, tableId));
+
+ LocalDataBuffer<15> fragBuf(suma.c_dataBufferPool, tabPtr.p->m_fragments);
+ if(fragBuf.getSize() == 0){
+ /**
+ * We need to gather fragment info
+ */
+ jam();
+ signal->theData[0] = RNIL;
+ signal->theData[1] = tableId;
+ signal->theData[2] = ptrI;
+ suma.sendSignal(DBDIH_REF, GSN_DI_FCOUNTREQ, signal, 3, JBB);
+ return;
+ }
+
+ m_currentTable++;
+ nextMeta(signal);
+}
+
+void
+SumaParticipant::SyncRecord::runDI_FCOUNTCONF(Signal* signal){
+ jam();
+
+ const Uint32 userPtr = signal->theData[0];
+ const Uint32 fragCount = signal->theData[1];
+ const Uint32 tableId = signal->theData[2];
+
+ ndbrequire(userPtr == RNIL && signal->length() == 5);
+
+ TablePtr tabPtr;
+ ndbrequire(suma.c_tables.find(tabPtr, tableId));
+
+ LocalDataBuffer<15> fragBuf(suma.c_dataBufferPool, tabPtr.p->m_fragments);
+ ndbrequire(fragBuf.getSize() == 0);
+
+ m_currentFragment = fragCount;
+ signal->theData[0] = RNIL;
+ signal->theData[1] = ptrI;
+ signal->theData[2] = tableId;
+ signal->theData[3] = 0; // Frag no
+ suma.sendSignal(DBDIH_REF, GSN_DIGETPRIMREQ, signal, 4, JBB);
+}
+
+void
+SumaParticipant::SyncRecord::runDIGETPRIMCONF(Signal* signal){
+ jam();
+
+ const Uint32 userPtr = signal->theData[0];
+ //const Uint32 senderData = signal->theData[1];
+ const Uint32 nodeCount = signal->theData[6];
+ const Uint32 tableId = signal->theData[7];
+ const Uint32 fragNo = signal->theData[8];
+
+ ndbrequire(userPtr == RNIL && signal->length() == 9);
+ ndbrequire(nodeCount > 0 && nodeCount <= MAX_REPLICAS);
+
+ TablePtr tabPtr;
+ ndbrequire(suma.c_tables.find(tabPtr, tableId));
+ LocalDataBuffer<15> fragBuf(suma.c_dataBufferPool, tabPtr.p->m_fragments);
+
+ /**
+ * Add primary node for fragment to list
+ */
+ FragmentDescriptor fd;
+ fd.m_fragDesc.m_nodeId = signal->theData[2];
+ fd.m_fragDesc.m_fragmentNo = fragNo;
+ signal->theData[2] = fd.m_dummy;
+ fragBuf.append(&signal->theData[2], 1);
+
+ const Uint32 nextFrag = fragNo + 1;
+ if(nextFrag == m_currentFragment){
+ /**
+ * Complete frag info for table
+ */
+ m_currentTable++;
+ nextMeta(signal);
+ return;
+ }
+ signal->theData[0] = RNIL;
+ signal->theData[1] = ptrI;
+ signal->theData[2] = tableId;
+ signal->theData[3] = nextFrag; // Frag no
+ suma.sendSignal(DBDIH_REF, GSN_DIGETPRIMREQ, signal, 4, JBB);
+}
+
+void
+SumaParticipant::SyncRecord::completeMeta(Signal* signal){
+ jam();
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+#if PRINT_ONLY
+ ndbout_c("GSN_SUB_SYNC_CONF (meta)");
+#else
+
+ suma.releaseSections(signal);
+
+ if (m_error) {
+ SubSyncRef * const ref = (SubSyncRef*)signal->getDataPtrSend();
+ ref->subscriptionId = subPtr.p->m_subscriptionId;
+ ref->subscriptionKey = subPtr.p->m_subscriptionKey;
+ ref->part = SubscriptionData::MetaData;
+ ref->subscriberData = subPtr.p->m_subscriberData;
+ ref->errorCode = SubSyncRef::Undefined;
+ suma.sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_SYNC_REF, signal,
+ SubSyncRef::SignalLength, JBB);
+ } else {
+ SubSyncConf * const conf = (SubSyncConf*)signal->getDataPtrSend();
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ conf->part = SubscriptionData::MetaData;
+ conf->subscriberData = subPtr.p->m_subscriberData;
+ suma.sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_SYNC_CONF, signal,
+ SubSyncConf::SignalLength, JBB);
+ }
+#endif
+}
+
+/**********************************************************
+ *
+ * Scan interface
+ *
+ */
+
+void
+SumaParticipant::SyncRecord::startScan(Signal* signal){
+ jam();
+
+ /**
+ * Get fraginfo
+ */
+ m_currentTable = 0;
+ m_currentFragment = 0;
+
+ nextScan(signal);
+}
+
+bool
+SumaParticipant::SyncRecord::getNextFragment(TablePtr * tab,
+ FragmentDescriptor * fd){
+ jam();
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ TableList::DataBufferIterator tabIt;
+ DataBuffer<15>::DataBufferIterator fragIt;
+
+ m_tableList.position(tabIt, m_currentTable);
+ for(; !tabIt.curr.isNull(); m_tableList.next(tabIt), m_currentTable++){
+ TablePtr tabPtr;
+ ndbrequire(suma.c_tables.find(tabPtr, * tabIt.data));
+ if(subPtr.p->m_subscriptionType == SubCreateReq::SelectiveTableSnapshot)
+ {
+ if(!subPtr.p->m_tables[tabPtr.p->m_tableId]) {
+ *tab = tabPtr;
+ return true;
+ }
+ }
+ LocalDataBuffer<15> fragBuf(suma.c_dataBufferPool, tabPtr.p->m_fragments);
+
+ fragBuf.position(fragIt, m_currentFragment);
+ for(; !fragIt.curr.isNull(); fragBuf.next(fragIt), m_currentFragment++){
+ FragmentDescriptor tmp;
+ tmp.m_dummy = * fragIt.data;
+ if(tmp.m_fragDesc.m_nodeId == suma.getOwnNodeId()){
+ * fd = tmp;
+ * tab = tabPtr;
+ return true;
+ }
+ }
+ m_currentFragment = 0;
+ }
+ return false;
+}
+
+void
+SumaParticipant::SyncRecord::nextScan(Signal* signal){
+ jam();
+ TablePtr tabPtr;
+ FragmentDescriptor fd;
+ SubscriptionPtr subPtr;
+ if(!getNextFragment(&tabPtr, &fd)){
+ jam();
+ completeScan(signal);
+ return;
+ }
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+ if(subPtr.p->m_subscriptionType == SubCreateReq::SelectiveTableSnapshot) {
+ jam();
+ if(!subPtr.p->m_tables[tabPtr.p->m_tableId]) {
+ /*
+ * table is not part of the subscription. Check next table
+ */
+ m_currentTable++;
+ nextScan(signal);
+ return;
+ }
+ }
+
+ DataBuffer<15>::Head head = m_attributeList;
+ if(head.getSize() == 0){
+ head = tabPtr.p->m_attributes;
+ }
+ LocalDataBuffer<15> attrBuf(suma.c_dataBufferPool, head);
+
+ ScanFragReq * req = (ScanFragReq *)signal->getDataPtrSend();
+ const Uint32 parallelism = 16;
+ const Uint32 attrLen = 5 + attrBuf.getSize();
+
+ req->senderData = m_subscriptionPtrI;
+ req->resultRef = suma.reference();
+ req->tableId = tabPtr.p->m_tableId;
+ req->requestInfo = 0;
+ req->savePointId = 0;
+ ScanFragReq::setLockMode(req->requestInfo, 0);
+ ScanFragReq::setHoldLockFlag(req->requestInfo, 1);
+ ScanFragReq::setKeyinfoFlag(req->requestInfo, 0);
+ ScanFragReq::setAttrLen(req->requestInfo, attrLen);
+ req->fragmentNoKeyLen = fd.m_fragDesc.m_fragmentNo;
+ req->schemaVersion = tabPtr.p->m_schemaVersion;
+ req->transId1 = 0;
+ req->transId2 = (SUMA << 20) + (suma.getOwnNodeId() << 8);
+ req->clientOpPtr = (ptrI << 16);
+ req->batch_size_rows= 16;
+ req->batch_size_bytes= 0;
+ suma.sendSignal(DBLQH_REF, GSN_SCAN_FRAGREQ, signal,
+ ScanFragReq::SignalLength, JBB);
+
+ signal->theData[0] = ptrI;
+ signal->theData[1] = 0;
+ signal->theData[2] = (SUMA << 20) + (suma.getOwnNodeId() << 8);
+
+ // Return all
+ signal->theData[3] = attrBuf.getSize();
+ signal->theData[4] = 0;
+ signal->theData[5] = 0;
+ signal->theData[6] = 0;
+ signal->theData[7] = 0;
+
+ Uint32 dataPos = 8;
+ DataBuffer<15>::DataBufferIterator it;
+ for(attrBuf.first(it); !it.curr.isNull(); attrBuf.next(it)){
+ AttributeHeader::init(&signal->theData[dataPos++], * it.data, 0);
+ if(dataPos == 25){
+ suma.sendSignal(DBLQH_REF, GSN_ATTRINFO, signal, 25, JBB);
+ dataPos = 3;
+ }
+ }
+ if(dataPos != 3){
+ suma.sendSignal(DBLQH_REF, GSN_ATTRINFO, signal, dataPos, JBB);
+ }
+
+ m_currentTableId = tabPtr.p->m_tableId;
+ m_currentNoOfAttributes = attrBuf.getSize();
+}
+
+
+void
+SumaParticipant::execSCAN_FRAGREF(Signal* signal){
+ jamEntry();
+
+// ScanFragRef * const ref = (ScanFragRef*)signal->getDataPtr();
+ ndbrequire(false);
+}
+
+void
+SumaParticipant::execSCAN_FRAGCONF(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13011);
+
+ ScanFragConf * const conf = (ScanFragConf*)signal->getDataPtr();
+
+ const Uint32 completed = conf->fragmentCompleted;
+ const Uint32 senderData = conf->senderData;
+ const Uint32 completedOps = conf->completedOps;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, senderData);
+
+ if(completed != 2){
+ jam();
+
+#if PRINT_ONLY
+ SubSyncContinueConf * const conf =
+ (SubSyncContinueConf*)signal->getDataPtrSend();
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ execSUB_SYNC_CONTINUE_CONF(signal);
+#else
+ SubSyncContinueReq * const req = (SubSyncContinueReq*)signal->getDataPtrSend();
+ req->subscriberData = subPtr.p->m_subscriberData;
+ req->noOfRowsSent = completedOps;
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_SYNC_CONTINUE_REQ, signal,
+ SubSyncContinueReq::SignalLength, JBB);
+#endif
+ return;
+ }
+
+ ndbrequire(completedOps == 0);
+
+ SyncRecord* tmp = c_syncPool.getPtr(subPtr.p->m_syncPtrI);
+
+ tmp->m_currentFragment++;
+ tmp->nextScan(signal);
+}
+
+void
+SumaParticipant::execSUB_SYNC_CONTINUE_CONF(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13012);
+
+ SubSyncContinueConf * const conf =
+ (SubSyncContinueConf*)signal->getDataPtr();
+
+ SubscriptionPtr subPtr;
+ Subscription key;
+ key.m_subscriptionId = conf->subscriptionId;
+ key.m_subscriptionKey = conf->subscriptionKey;
+
+ ndbrequire(c_subscriptions.find(subPtr, key));
+
+ ScanFragNextReq * req = (ScanFragNextReq *)signal->getDataPtrSend();
+ req->senderData = subPtr.i;
+ req->closeFlag = 0;
+ req->transId1 = 0;
+ req->transId2 = (SUMA << 20) + (getOwnNodeId() << 8);
+ req->batch_size_rows = 16;
+ req->batch_size_bytes = 0;
+ sendSignal(DBLQH_REF, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+}
+
+void
+SumaParticipant::SyncRecord::completeScan(Signal* signal){
+ jam();
+ // m_tableList.release();
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+#if PRINT_ONLY
+ ndbout_c("GSN_SUB_SYNC_CONF (data)");
+#else
+ SubSyncConf * const conf = (SubSyncConf*)signal->getDataPtrSend();
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ conf->part = SubscriptionData::TableData;
+ conf->subscriberData = subPtr.p->m_subscriberData;
+ suma.sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_SYNC_CONF, signal,
+ SubSyncConf::SignalLength, JBB);
+#endif
+}
+
+void
+SumaParticipant::execSCAN_HBREP(Signal* signal){
+ jamEntry();
+#if 0
+ ndbout << "execSCAN_HBREP" << endl << hex;
+ for(int i = 0; i<signal->length(); i++){
+ ndbout << signal->theData[i] << " ";
+ if(((i + 1) % 8) == 0)
+ ndbout << endl << hex;
+ }
+ ndbout << endl;
+#endif
+}
+
+/**********************************************************
+ *
+ * Suma participant interface
+ *
+ * Creation of subscriber
+ *
+ */
+
+void
+SumaParticipant::execSUB_START_REQ(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execSUB_START_REQ");
+
+ CRASH_INSERTION(13013);
+
+ if (c_restartLock) {
+ jam();
+ // ndbout_c("c_restartLock");
+ if (RtoI(signal->getSendersBlockRef(), false) == RNIL) {
+ jam();
+ sendSubStartRef(signal, /** Error Code */ 0, true);
+ DBUG_VOID_RETURN;
+ }
+ // only allow other Suma's in the nodegroup to come through for restart purposes
+ }
+
+ Subscription key;
+
+ SubStartReq * const req = (SubStartReq*)signal->getDataPtr();
+
+ Uint32 senderRef = req->senderRef;
+ Uint32 senderData = req->senderData;
+ Uint32 subscriberData = req->subscriberData;
+ Uint32 subscriberRef = req->subscriberRef;
+ SubscriptionData::Part part = (SubscriptionData::Part)req->part;
+ key.m_subscriptionId = req->subscriptionId;
+ key.m_subscriptionKey = req->subscriptionKey;
+
+ SubscriptionPtr subPtr;
+ if(!c_subscriptions.find(subPtr, key)){
+ jam();
+ sendSubStartRef(signal, /** Error Code */ 0);
+ DBUG_VOID_RETURN;
+ }
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ if (syncPtr.p->m_locked) {
+ jam();
+#if 0
+ ndbout_c("Locked");
+#endif
+ sendSubStartRef(signal, /** Error Code */ 0, true);
+ DBUG_VOID_RETURN;
+ }
+ syncPtr.p->m_locked = true;
+
+ SubscriberPtr subbPtr;
+ if(!c_subscriberPool.seize(subbPtr)){
+ jam();
+ syncPtr.p->m_locked = false;
+ sendSubStartRef(signal, /** Error Code */ 0);
+ DBUG_VOID_RETURN;
+ }
+
+ Uint32 type = subPtr.p->m_subscriptionType;
+
+ subbPtr.p->m_senderRef = senderRef;
+ subbPtr.p->m_senderData = senderData;
+
+ switch (type) {
+ case SubCreateReq::TableEvent:
+ jam();
+ // we want the data to return to the API not DICT
+ subbPtr.p->m_subscriberRef = subscriberRef;
+ // ndbout_c("start ref = %u", signal->getSendersBlockRef());
+ // ndbout_c("ref = %u", subbPtr.p->m_subscriberRef);
+ // we use the subscription id for now, should really be API choice
+ subbPtr.p->m_subscriberData = subscriberData;
+
+#if 0
+ if (RtoI(signal->getSendersBlockRef(), false) == RNIL) {
+ jam();
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ Uint32 ref = calcSumaBlockRef(c_nodesInGroup[i]);
+ if (ref != reference()) {
+ jam();
+ sendSubStartReq(subPtr, subbPtr, signal, ref);
+ } else
+ jam();
+ }
+ }
+#endif
+ break;
+ case SubCreateReq::DatabaseSnapshot:
+ case SubCreateReq::SelectiveTableSnapshot:
+ jam();
+ subbPtr.p->m_subscriberRef = GREP_REF;
+ subbPtr.p->m_subscriberData = subPtr.p->m_subscriberData;
+ break;
+ case SubCreateReq::SingleTableScan:
+ jam();
+ subbPtr.p->m_subscriberRef = subPtr.p->m_subscriberRef;
+ subbPtr.p->m_subscriberData = subPtr.p->m_subscriberData;
+ }
+
+ subbPtr.p->m_subPtrI = subPtr.i;
+ subbPtr.p->m_firstGCI = RNIL;
+ if (type == SubCreateReq::TableEvent)
+ subbPtr.p->m_lastGCI = 0;
+ else
+ subbPtr.p->m_lastGCI = RNIL; // disable usage of m_lastGCI
+ bool ok = false;
+
+ switch(part){
+ case SubscriptionData::MetaData:
+ ok = true;
+ jam();
+ c_metaSubscribers.add(subbPtr);
+ sendSubStartComplete(signal, subbPtr, 0, part);
+ break;
+ case SubscriptionData::TableData:
+ ok = true;
+ jam();
+ c_prepDataSubscribers.add(subbPtr);
+ syncPtr.p->startTrigger(signal);
+ break;
+ }
+ ndbrequire(ok);
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::sendSubStartComplete(Signal* signal,
+ SubscriberPtr subbPtr,
+ Uint32 firstGCI,
+ SubscriptionData::Part part){
+ jam();
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subbPtr.p->m_subPtrI);
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ syncPtr.p->m_locked = false;
+
+ SubStartConf * const conf = (SubStartConf*)signal->getDataPtrSend();
+
+ conf->senderRef = reference();
+ conf->senderData = subbPtr.p->m_senderData;
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ conf->firstGCI = firstGCI;
+ conf->part = (Uint32) part;
+
+ conf->subscriberData = subPtr.p->m_subscriberData;
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_START_CONF, signal,
+ SubStartConf::SignalLength, JBB);
+}
+
+#if 0
+void
+SumaParticipant::sendSubStartRef(SubscriptionPtr subPtr,
+ Signal* signal, Uint32 errCode,
+ bool temporary){
+ jam();
+ SubStartRef * ref = (SubStartRef *)signal->getDataPtrSend();
+ xxx ref->senderRef = reference();
+ xxx ref->senderData = subPtr.p->m_senderData;
+ ref->subscriptionId = subPtr.p->m_subscriptionId;
+ ref->subscriptionKey = subPtr.p->m_subscriptionKey;
+ ref->part = (Uint32) subPtr.p->m_subscriptionType;
+ ref->subscriberData = subPtr.p->m_subscriberData;
+ ref->err = errCode;
+ if (temporary) {
+ jam();
+ ref->setTemporary();
+ }
+ releaseSections(signal);
+ sendSignal(subPtr.p->m_subscriberRef, GSN_SUB_START_REF, signal,
+ SubStartRef::SignalLength, JBB);
+}
+#endif
+void
+SumaParticipant::sendSubStartRef(Signal* signal, Uint32 errCode,
+ bool temporary){
+ jam();
+ SubStartRef * ref = (SubStartRef *)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->err = errCode;
+ if (temporary) {
+ jam();
+ ref->setTemporary();
+ }
+ releaseSections(signal);
+ sendSignal(signal->getSendersBlockRef(), GSN_SUB_START_REF, signal,
+ SubStartRef::SignalLength, JBB);
+}
+
+/**********************************************************
+ *
+ * Trigger admin interface
+ *
+ */
+
+void
+SumaParticipant::SyncRecord::startTrigger(Signal* signal){
+ jam();
+ m_currentTable = 0;
+ m_latestTriggerId = RNIL;
+ nextTrigger(signal);
+}
+
+void
+SumaParticipant::SyncRecord::nextTrigger(Signal* signal){
+ jam();
+
+ TableList::DataBufferIterator it;
+
+ if(!m_tableList.position(it, m_currentTable)){
+ completeTrigger(signal);
+ return;
+ }
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+ const Uint32 RT_BREAK = 48;
+ Uint32 latestTriggerId = 0;
+ for(Uint32 i = 0; i<RT_BREAK && !it.isNull(); i++, m_tableList.next(it)){
+ TablePtr tabPtr;
+#if 0
+ ndbout_c("nextTrigger tableid %u", *it.data);
+#endif
+ ndbrequire(suma.c_tables.find(tabPtr, *it.data));
+
+ AttributeMask attrMask;
+ createAttributeMask(attrMask, tabPtr.p);
+
+ for(Uint32 j = 0; j<3; j++){
+ i++;
+ latestTriggerId = (tabPtr.p->m_schemaVersion << 18) |
+ (j << 16) | tabPtr.p->m_tableId;
+ if(tabPtr.p->m_hasTriggerDefined[j] == 0) {
+ ndbrequire(tabPtr.p->m_triggerIds[j] == ILLEGAL_TRIGGER_ID);
+#if 0
+ ndbout_c("DEFINING trigger on table %u[%u]", tabPtr.p->m_tableId, j);
+#endif
+ CreateTrigReq * const req = (CreateTrigReq*)signal->getDataPtrSend();
+ req->setUserRef(SUMA_REF);
+ req->setConnectionPtr(ptrI);
+ req->setTriggerType(TriggerType::SUBSCRIPTION_BEFORE);
+ req->setTriggerActionTime(TriggerActionTime::TA_DETACHED);
+ req->setMonitorReplicas(true);
+ req->setMonitorAllAttributes(false);
+ req->setReceiverRef(SUMA_REF);
+ req->setTriggerId(latestTriggerId);
+ req->setTriggerEvent((TriggerEvent::Value)j);
+ req->setTableId(tabPtr.p->m_tableId);
+ req->setAttributeMask(attrMask);
+ suma.sendSignal(DBTUP_REF, GSN_CREATE_TRIG_REQ,
+ signal, CreateTrigReq::SignalLength, JBB);
+
+ } else {
+ /**
+ * Faking that a trigger has been created in order to
+ * simulate the proper behaviour.
+ * Perhaps this should be a dummy signal instead of
+ * (ab)using CREATE_TRIG_CONF.
+ */
+ CreateTrigConf * conf = (CreateTrigConf*)signal->getDataPtrSend();
+ conf->setConnectionPtr(ptrI);
+ conf->setTableId(tabPtr.p->m_tableId);
+ conf->setTriggerId(latestTriggerId);
+ suma.sendSignal(SUMA_REF,GSN_CREATE_TRIG_CONF,
+ signal, CreateTrigConf::SignalLength, JBB);
+
+ }
+
+ }
+ m_currentTable++;
+ }
+ m_latestTriggerId = latestTriggerId;
+}
+
+void
+SumaParticipant::SyncRecord::createAttributeMask(AttributeMask& mask,
+ Table * table){
+ jam();
+ mask.clear();
+ DataBuffer<15>::DataBufferIterator it;
+ LocalDataBuffer<15> attrBuf(suma.c_dataBufferPool, table->m_attributes);
+ for(attrBuf.first(it); !it.curr.isNull(); attrBuf.next(it)){
+ mask.set(* it.data);
+ }
+}
+
+void
+SumaParticipant::SyncRecord::runCREATE_TRIG_CONF(Signal* signal){
+ jam();
+
+ CreateTrigConf * const conf = (CreateTrigConf*)signal->getDataPtr();
+ const Uint32 triggerId = conf->getTriggerId();
+ Uint32 type = (triggerId >> 16) & 0x3;
+ Uint32 tableId = conf->getTableId();
+
+ TablePtr tabPtr;
+ ndbrequire(suma.c_tables.find(tabPtr, tableId));
+
+ ndbrequire(type < 3);
+ tabPtr.p->m_triggerIds[type] = triggerId;
+ tabPtr.p->m_hasTriggerDefined[type]++;
+
+ if(triggerId == m_latestTriggerId){
+ jam();
+ nextTrigger(signal);
+ }
+}
+
+void
+SumaParticipant::SyncRecord::completeTrigger(Signal* signal){
+ jam();
+ SubscriptionPtr subPtr;
+ CRASH_INSERTION(13013);
+#ifdef EVENT_PH3_DEBUG
+ ndbout_c("SumaParticipant: trigger completed");
+#endif
+ Uint32 gci;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+ SubscriberPtr subbPtr;
+ {
+ bool found = false;
+
+ for(suma.c_prepDataSubscribers.first(subbPtr);
+ !subbPtr.isNull(); suma.c_prepDataSubscribers.next(subbPtr)) {
+ jam();
+ if(subbPtr.p->m_subPtrI == subPtr.i) {
+ jam();
+ found = true;
+ break;
+ }
+ }
+ ndbrequire(found);
+ gci = suma.getFirstGCI(signal);
+ subbPtr.p->m_firstGCI = gci;
+ suma.c_prepDataSubscribers.remove(subbPtr);
+ suma.c_dataSubscribers.add(subbPtr);
+ }
+ suma.sendSubStartComplete(signal, subbPtr, gci, SubscriptionData::TableData);
+}
+
+void
+SumaParticipant::SyncRecord::startDropTrigger(Signal* signal){
+ jam();
+ m_currentTable = 0;
+ m_latestTriggerId = RNIL;
+ nextDropTrigger(signal);
+}
+
+void
+SumaParticipant::SyncRecord::nextDropTrigger(Signal* signal){
+ jam();
+
+ TableList::DataBufferIterator it;
+
+ if(!m_tableList.position(it, m_currentTable)){
+ completeDropTrigger(signal);
+ return;
+ }
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+ const Uint32 RT_BREAK = 48;
+ Uint32 latestTriggerId = 0;
+ for(Uint32 i = 0; i<RT_BREAK && !it.isNull(); i++, m_tableList.next(it)){
+ jam();
+ TablePtr tabPtr;
+#if 0
+ ndbout_c("nextDropTrigger tableid %u", *it.data);
+#endif
+ ndbrequire(suma.c_tables.find(tabPtr, * it.data));
+
+ for(Uint32 j = 0; j<3; j++){
+ jam();
+ ndbrequire(tabPtr.p->m_triggerIds[j] != ILLEGAL_TRIGGER_ID);
+ i++;
+ latestTriggerId = tabPtr.p->m_triggerIds[j];
+ if(tabPtr.p->m_hasTriggerDefined[j] == 1) {
+ jam();
+
+ DropTrigReq * const req = (DropTrigReq*)signal->getDataPtrSend();
+ req->setConnectionPtr(ptrI);
+ req->setUserRef(SUMA_REF); // Sending to myself
+ req->setRequestType(DropTrigReq::RT_USER);
+ req->setTriggerType(TriggerType::SUBSCRIPTION_BEFORE);
+ req->setTriggerActionTime(TriggerActionTime::TA_DETACHED);
+ req->setIndexId(RNIL);
+
+ req->setTableId(tabPtr.p->m_tableId);
+ req->setTriggerId(latestTriggerId);
+ req->setTriggerEvent((TriggerEvent::Value)j);
+
+#if 0
+ ndbout_c("DROPPING trigger %u = %u %u %u on table %u[%u]",
+ latestTriggerId,TriggerType::SUBSCRIPTION_BEFORE,
+ TriggerActionTime::TA_DETACHED, j, tabPtr.p->m_tableId, j);
+#endif
+ suma.sendSignal(DBTUP_REF, GSN_DROP_TRIG_REQ,
+ signal, DropTrigReq::SignalLength, JBB);
+ } else {
+ jam();
+ ndbrequire(tabPtr.p->m_hasTriggerDefined[j] > 1);
+ /**
+ * Faking that a trigger has been dropped in order to
+ * simulate the proper behaviour.
+ * Perhaps this should be a dummy signal instead of
+ * (ab)using DROP_TRIG_CONF.
+ */
+ DropTrigConf * conf = (DropTrigConf*)signal->getDataPtrSend();
+ conf->setConnectionPtr(ptrI);
+ conf->setTableId(tabPtr.p->m_tableId);
+ conf->setTriggerId(latestTriggerId);
+ suma.sendSignal(SUMA_REF,GSN_DROP_TRIG_CONF,
+ signal, DropTrigConf::SignalLength, JBB);
+ }
+ }
+ m_currentTable++;
+ }
+ m_latestTriggerId = latestTriggerId;
+}
+
+void
+SumaParticipant::SyncRecord::runDROP_TRIG_REF(Signal* signal){
+ jam();
+ DropTrigRef * const ref = (DropTrigRef*)signal->getDataPtr();
+ if (ref->getErrorCode() != DropTrigRef::TriggerNotFound){
+ ndbrequire(false);
+ }
+ const Uint32 triggerId = ref->getTriggerId();
+ Uint32 tableId = ref->getTableId();
+ runDropTrig(signal, triggerId, tableId);
+}
+
+void
+SumaParticipant::SyncRecord::runDROP_TRIG_CONF(Signal* signal){
+ jam();
+
+ DropTrigConf * const conf = (DropTrigConf*)signal->getDataPtr();
+ const Uint32 triggerId = conf->getTriggerId();
+ Uint32 tableId = conf->getTableId();
+ runDropTrig(signal, triggerId, tableId);
+}
+
+void
+SumaParticipant::SyncRecord::runDropTrig(Signal* signal,
+ Uint32 triggerId,
+ Uint32 tableId){
+ Uint32 type = (triggerId >> 16) & 0x3;
+
+ TablePtr tabPtr;
+ ndbrequire(suma.c_tables.find(tabPtr, tableId));
+
+ ndbrequire(type < 3);
+ ndbrequire(tabPtr.p->m_triggerIds[type] == triggerId);
+ tabPtr.p->m_hasTriggerDefined[type]--;
+ if (tabPtr.p->m_hasTriggerDefined[type] == 0) {
+ jam();
+ tabPtr.p->m_triggerIds[type] = ILLEGAL_TRIGGER_ID;
+ }
+ if(triggerId == m_latestTriggerId){
+ jam();
+ nextDropTrigger(signal);
+ }
+}
+
+void
+SumaParticipant::SyncRecord::completeDropTrigger(Signal* signal){
+ jam();
+ SubscriptionPtr subPtr;
+ CRASH_INSERTION(13014);
+#if 0
+ ndbout_c("trigger completed");
+#endif
+
+ suma.c_subscriptions.getPtr(subPtr, m_subscriptionPtrI);
+ ndbrequire(subPtr.p->m_syncPtrI == ptrI);
+
+ bool found = false;
+ SubscriberPtr subbPtr;
+ for(suma.c_prepDataSubscribers.first(subbPtr);
+ !subbPtr.isNull(); suma.c_prepDataSubscribers.next(subbPtr)) {
+ jam();
+ if(subbPtr.p->m_subPtrI == subPtr.i) {
+ jam();
+ found = true;
+ break;
+ }
+ }
+ ndbrequire(found);
+ suma.sendSubStopComplete(signal, subbPtr);
+}
+
+/**********************************************************
+ * Scan data interface
+ *
+ * Assumption: one execTRANSID_AI contains all attr info
+ *
+ */
+
+#define SUMA_BUF_SZ1 MAX_KEY_SIZE_IN_WORDS + MAX_TUPLE_SIZE_IN_WORDS
+#define SUMA_BUF_SZ MAX_ATTRIBUTES_IN_TABLE + SUMA_BUF_SZ1
+
+static Uint32 f_bufferLock = 0;
+static Uint32 f_buffer[SUMA_BUF_SZ];
+static Uint32 f_trigBufferSize = 0;
+static Uint32 b_bufferLock = 0;
+static Uint32 b_buffer[SUMA_BUF_SZ];
+static Uint32 b_trigBufferSize = 0;
+
+void
+SumaParticipant::execTRANSID_AI(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13015);
+ TransIdAI * const data = (TransIdAI*)signal->getDataPtr();
+ const Uint32 opPtrI = data->connectPtr;
+ const Uint32 length = signal->length() - 3;
+
+ if(f_bufferLock == 0){
+ f_bufferLock = opPtrI;
+ } else {
+ ndbrequire(f_bufferLock == opPtrI);
+ }
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, (opPtrI >> 16));
+
+ Uint32 sum = 0;
+ Uint32 * dst = f_buffer + MAX_ATTRIBUTES_IN_TABLE;
+ Uint32 * headers = f_buffer;
+ const Uint32 * src = &data->attrData[0];
+ const Uint32 * const end = &src[length];
+
+ const Uint32 attribs = syncPtr.p->m_currentNoOfAttributes;
+ for(Uint32 i = 0; i<attribs; i++){
+ Uint32 tmp = * src++;
+ * headers++ = tmp;
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+
+ memcpy(dst, src, 4 * len);
+ dst += len;
+ src += len;
+ sum += len;
+ }
+
+ ndbrequire(src == end);
+
+ /**
+ * Send data to subscriber
+ */
+ LinearSectionPtr ptr[3];
+ ptr[0].p = f_buffer;
+ ptr[0].sz = attribs;
+
+ ptr[1].p = f_buffer + MAX_ATTRIBUTES_IN_TABLE;
+ ptr[1].sz = sum;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, syncPtr.p->m_subscriptionPtrI);
+
+ /**
+ * Initialize signal
+ */
+ SubTableData * sdata = (SubTableData*)signal->getDataPtrSend();
+ Uint32 ref = subPtr.p->m_subscriberRef;
+ sdata->tableId = syncPtr.p->m_currentTableId;
+ sdata->senderData = subPtr.p->m_subscriberData;
+ sdata->operation = 3; // Scan
+ sdata->gci = 1; // Undefined
+#if PRINT_ONLY
+ ndbout_c("GSN_SUB_TABLE_DATA (scan) #attr: %d len: %d", attribs, sum);
+#else
+ sendSignal(ref,
+ GSN_SUB_TABLE_DATA,
+ signal,
+ SubTableData::SignalLength, JBB,
+ ptr, 2);
+#endif
+
+ /**
+ * Reset f_bufferLock
+ */
+ f_bufferLock = 0;
+}
+
+/**********************************************************
+ *
+ * Trigger data interface
+ *
+ */
+
+void
+SumaParticipant::execTRIG_ATTRINFO(Signal* signal){
+ jamEntry();
+
+ CRASH_INSERTION(13016);
+ TrigAttrInfo* const trg = (TrigAttrInfo*)signal->getDataPtr();
+ const Uint32 trigId = trg->getTriggerId();
+
+ const Uint32 dataLen = signal->length() - TrigAttrInfo::StaticLength;
+
+ if(trg->getAttrInfoType() == TrigAttrInfo::BEFORE_VALUES){
+ jam();
+
+ ndbrequire(b_bufferLock == trigId);
+
+ memcpy(b_buffer + b_trigBufferSize, trg->getData(), 4 * dataLen);
+ b_trigBufferSize += dataLen;
+ // printf("before values %u %u %u\n",trigId, dataLen, b_trigBufferSize);
+ } else {
+ jam();
+
+ if(f_bufferLock == 0){
+ f_bufferLock = trigId;
+ f_trigBufferSize = 0;
+ b_bufferLock = trigId;
+ b_trigBufferSize = 0;
+ } else {
+ ndbrequire(f_bufferLock == trigId);
+ }
+
+ memcpy(f_buffer + f_trigBufferSize, trg->getData(), 4 * dataLen);
+ f_trigBufferSize += dataLen;
+ }
+}
+
+#ifdef NODEFAIL_DEBUG2
+static int theCounts[64] = {0};
+#endif
+
+Uint32
+Suma::getStoreBucket(Uint32 v)
+{
+ // id will contain id to responsible suma or
+ // RNIL if we don't have nodegroup info yet
+
+ const Uint32 N = NO_OF_BUCKETS;
+ const Uint32 D = v % N; // Distibution key
+ return D;
+}
+
+Uint32
+Suma::getResponsibleSumaNodeId(Uint32 D)
+{
+ // id will contain id to responsible suma or
+ // RNIL if we don't have nodegroup info yet
+
+ Uint32 id;
+
+ if (c_restartLock) {
+ jam();
+ // ndbout_c("c_restartLock");
+ id = RNIL;
+ } else {
+ jam();
+ id = RNIL;
+ const Uint32 n = c_noNodesInGroup; // Number nodes in node group
+ const Uint32 C1 = D / n;
+ const Uint32 C2 = D - C1*n; // = D % n;
+ const Uint32 C = C2 + C1 % n;
+ for (Uint32 i = 0; i < n; i++) {
+ jam();
+ id = c_nodesInGroup[(C + i) % n];
+ if (c_aliveNodes.get(id) &&
+ !c_preparingNodes.get(id)) {
+ jam();
+ break;
+ }//if
+ }
+#ifdef NODEFAIL_DEBUG2
+ theCounts[id]++;
+ ndbout_c("Suma:responsible n=%u, D=%u, id = %u, count=%u",
+ n,D, id, theCounts[id]);
+#endif
+ }
+ return id;
+}
+
+Uint32
+SumaParticipant::decideWhoToSend(Uint32 nBucket, Uint32 gci){
+ bool replicaFlag = true;
+ Uint32 nId = RNIL;
+
+ // bucket active/not active set by GCP_COMPLETE
+ if (c_buckets[nBucket].active) {
+ if (c_buckets[nBucket].handover && c_buckets[nBucket].handoverGCI <= gci) {
+ jam();
+ replicaFlag = true; // let the other node send this
+ nId = RNIL;
+ // mark this as started, if we get a node failiure now we have some lost stuff
+ c_buckets[nBucket].handover_started = true;
+ } else {
+ jam();
+ replicaFlag = false;
+ nId = refToNode(reference());
+ }
+ } else {
+ nId = getResponsibleSumaNodeId(nBucket);
+ replicaFlag = !(nId == refToNode(reference()));
+
+ if (!replicaFlag) {
+ if (!c_buckets[nBucket].handover) {
+ jam();
+ // appearently a node has failed and we are taking over sending
+ // from that bucket. Now we need to go back to latest completed
+ // GCI. Handling will depend on Subscriber and Subscription
+
+ // TODO, for now we make an easy takeover
+ if (gci < c_nodeFailGCI)
+ c_lastInconsistentGCI = gci;
+
+ // we now have responsability for this bucket and we're actively
+ // sending from that
+ c_buckets[nBucket].active = true;
+#ifdef HANDOVER_DEBUG
+ ndbout_c("Takeover Bucket %u", nBucket);
+#endif
+ } else if (c_buckets[nBucket].handoverGCI > gci) {
+ jam();
+ replicaFlag = true; // handover going on, but don't start sending yet
+ nId = RNIL;
+ } else {
+ jam();
+#ifdef HANDOVER_DEBUG
+ ndbout_c("Possible error: Will send from GCI = %u", gci);
+#endif
+ }
+ }
+ }
+
+#ifdef NODEFAIL_DEBUG2
+ ndbout_c("Suma:bucket %u, responsible id = %u, replicaFlag = %u",
+ nBucket, nId, (Uint32)replicaFlag);
+#endif
+ return replicaFlag;
+}
+
+void
+SumaParticipant::execFIRE_TRIG_ORD(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execFIRE_TRIG_ORD");
+ CRASH_INSERTION(13016);
+ FireTrigOrd* const trg = (FireTrigOrd*)signal->getDataPtr();
+ const Uint32 trigId = trg->getTriggerId();
+ const Uint32 hashValue = trg->getHashValue();
+ const Uint32 gci = trg->getGCI();
+ const Uint32 event = trg->getTriggerEvent();
+ const Uint32 triggerId = trg->getTriggerId();
+ Uint32 tableId = triggerId & 0xFFFF;
+
+ ndbrequire(f_bufferLock == trigId);
+
+#ifdef EVENT_DEBUG2
+ ndbout_c("SumaParticipant::execFIRE_TRIG_ORD");
+#endif
+
+ Uint32 sz = trg->getNoOfPrimaryKeyWords()+trg->getNoOfAfterValueWords();
+ ndbrequire(sz == f_trigBufferSize);
+
+ /**
+ * Reformat as "all headers" + "all data"
+ */
+ Uint32 dataLen = 0;
+ Uint32 noOfAttrs = 0;
+ Uint32 * src = f_buffer;
+ Uint32 * headers = signal->theData + 25;
+ Uint32 * dst = signal->theData + 25 + MAX_ATTRIBUTES_IN_TABLE;
+
+ LinearSectionPtr ptr[3];
+ int nptr;
+
+ ptr[0].p = headers;
+ ptr[1].p = dst;
+
+ while(sz > 0){
+ jam();
+ Uint32 tmp = * src ++;
+ * headers ++ = tmp;
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ memcpy(dst, src, 4 * len);
+ dst += len;
+ src += len;
+
+ noOfAttrs++;
+ dataLen += len;
+ sz -= (1 + len);
+ }
+ ndbrequire(sz == 0);
+
+ ptr[0].sz = noOfAttrs;
+ ptr[1].sz = dataLen;
+
+ if (b_trigBufferSize > 0) {
+ jam();
+ ptr[2].p = b_buffer;
+ ptr[2].sz = b_trigBufferSize;
+ nptr = 3;
+ } else {
+ jam();
+ nptr = 2;
+ }
+
+ // right now only for tableEvent
+ bool replicaFlag = decideWhoToSend(getStoreBucket(hashValue), gci);
+
+ /**
+ * Signal to subscriber(s)
+ */
+ SubTableData * data = (SubTableData*)signal->getDataPtrSend();//trg;
+ data->gci = gci;
+ data->tableId = tableId;
+ data->operation = event;
+ data->noOfAttributes = noOfAttrs;
+ data->dataSize = dataLen;
+
+ SubscriberPtr subbPtr;
+ for(c_dataSubscribers.first(subbPtr); !subbPtr.isNull();
+ c_dataSubscribers.next(subbPtr)){
+ if (subbPtr.p->m_firstGCI > gci) {
+#ifdef EVENT_DEBUG
+ ndbout_c("m_firstGCI = %u, gci = %u", subbPtr.p->m_firstGCI, gci);
+#endif
+ jam();
+ // we're either restarting or it's a newly created subscriber
+ // and waiting for the right gci
+ continue;
+ }
+
+ jam();
+
+ const Uint32 ref = subbPtr.p->m_subscriberRef;
+ // ndbout_c("ref = %u", ref);
+ const Uint32 subdata = subbPtr.p->m_subscriberData;
+ data->senderData = subdata;
+ /*
+ * get subscription ptr for this subscriber
+ */
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subbPtr.p->m_subPtrI);
+
+ if(!subPtr.p->m_tables[tableId]) {
+ jam();
+ continue;
+ //continue in for-loop if the table is not part of
+ //the subscription. Otherwise, send data to subscriber.
+ }
+
+ if (subPtr.p->m_subscriptionType == SubCreateReq::TableEvent) {
+ if (replicaFlag) {
+ jam();
+ c_failoverBuffer.subTableData(gci,NULL,0);
+ continue;
+ }
+ jam();
+ Uint32 tmp = data->logType;
+ if (c_lastInconsistentGCI == data->gci) {
+ data->setGCINotConsistent();
+ }
+
+#ifdef HANDOVER_DEBUG
+ {
+ static int aLongGCIName = 0;
+ if (data->gci != aLongGCIName) {
+ aLongGCIName = data->gci;
+ ndbout_c("sent from GCI = %u", aLongGCIName);
+ }
+ }
+#endif
+ DBUG_PRINT("info",("GSN_SUB_TABLE_DATA to node %d", refToNode(ref)));
+ sendSignal(ref, GSN_SUB_TABLE_DATA, signal,
+ SubTableData::SignalLength, JBB, ptr, nptr);
+ data->logType = tmp;
+ } else {
+ ndbassert(refToNode(ref) == 0 || refToNode(ref) == getOwnNodeId());
+ jam();
+#if PRINT_ONLY
+ ndbout_c("GSN_SUB_TABLE_DATA to %s: op: %d #attr: %d len: %d",
+ getBlockName(refToBlock(ref)),
+ noOfAttrs, dataLen);
+
+#else
+#ifdef HANDOVER_DEBUG
+ {
+ static int aLongGCIName2 = 0;
+ if (data->gci != aLongGCIName2) {
+ aLongGCIName2 = data->gci;
+ ndbout_c("(EXECUTE_DIRECT) sent from GCI = %u to %u", aLongGCIName2, ref);
+ }
+ }
+#endif
+ EXECUTE_DIRECT(refToBlock(ref), GSN_SUB_TABLE_DATA, signal,
+ SubTableData::SignalLength);
+ jamEntry();
+#endif
+ }
+ }
+
+ /**
+ * Reset f_bufferLock
+ */
+ f_bufferLock = 0;
+ b_bufferLock = 0;
+
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::execSUB_GCP_COMPLETE_REP(Signal* signal){
+ jamEntry();
+
+ SubGcpCompleteRep * rep = (SubGcpCompleteRep*)signal->getDataPtrSend();
+
+ Uint32 gci = rep->gci;
+ c_lastCompleteGCI = gci;
+
+ /**
+ * always send SUB_GCP_COMPLETE_REP to Grep (so
+ * Lars can do funky stuff calculating intervals,
+ * even before the subscription is started
+ */
+ rep->senderRef = reference();
+ rep->senderData = 0; //ignored in grep
+ EXECUTE_DIRECT(refToBlock(GREP_REF), GSN_SUB_GCP_COMPLETE_REP, signal,
+ SubGcpCompleteRep::SignalLength);
+
+ /**
+ * Signal to subscriber(s)
+ */
+
+ SubscriberPtr subbPtr;
+ SubscriptionPtr subPtr;
+ c_dataSubscribers.first(subbPtr);
+ for(; !subbPtr.isNull(); c_dataSubscribers.next(subbPtr)){
+
+ if (subbPtr.p->m_firstGCI > gci) {
+ jam();
+ // we don't send SUB_GCP_COMPLETE_REP for incomplete GCI's
+ continue;
+ }
+
+ const Uint32 ref = subbPtr.p->m_subscriberRef;
+ rep->senderRef = ref;
+ rep->senderData = subbPtr.p->m_subscriberData;
+
+ c_subscriptions.getPtr(subPtr, subbPtr.p->m_subPtrI);
+#if PRINT_ONLY
+ ndbout_c("GSN_SUB_GCP_COMPLETE_REP to %s:",
+ getBlockName(refToBlock(ref)));
+#else
+ /**
+ * Ignore sending to GREP (since we sent earlier)
+ */
+ if (ref == GREP_REF) {
+ jam();
+ continue;
+ }
+
+ CRASH_INSERTION(13018);
+
+ if (subPtr.p->m_subscriptionType == SubCreateReq::TableEvent)
+ {
+ jam();
+ sendSignal(ref, GSN_SUB_GCP_COMPLETE_REP, signal,
+ SubGcpCompleteRep::SignalLength, JBB);
+ }
+ else
+ {
+ jam();
+ ndbassert(refToNode(ref) == 0 || refToNode(ref) == getOwnNodeId());
+ EXECUTE_DIRECT(refToBlock(ref), GSN_SUB_GCP_COMPLETE_REP, signal,
+ SubGcpCompleteRep::SignalLength);
+ jamEntry();
+ }
+#endif
+ }
+
+ if (c_handoverToDo) {
+ jam();
+ c_handoverToDo = false;
+ for( int i = 0; i < NO_OF_BUCKETS; i++) {
+ if (c_buckets[i].handover) {
+ if (c_buckets[i].handoverGCI > gci) {
+ jam();
+ c_handoverToDo = true; // still waiting for the right GCI
+ break; /* since all handover should happen at the same time
+ * we can break here
+ */
+ } else {
+ c_buckets[i].handover = false;
+#ifdef HANDOVER_DEBUG
+ ndbout_c("Handover Bucket %u", i);
+#endif
+ if (getResponsibleSumaNodeId(i) == refToNode(reference())) {
+ // my bucket to be handed over to me
+ ndbrequire(!c_buckets[i].active);
+ jam();
+ c_buckets[i].active = true;
+ } else {
+ // someone else's bucket to handover to
+ ndbrequire(c_buckets[i].active);
+ jam();
+ c_buckets[i].active = false;
+ }
+ }
+ }
+ }
+ }
+}
+
+/***********************************************************
+ *
+ * Embryo to syncronize the Suma's so as to know if a subscriber
+ * has received a GCP_COMPLETE from all suma's or not
+ *
+ */
+
+void
+SumaParticipant::runSUB_GCP_COMPLETE_ACC(Signal* signal){
+ jam();
+
+ SubGcpCompleteAcc * const acc = (SubGcpCompleteAcc*)signal->getDataPtr();
+
+ Uint32 gci = acc->rep.gci;
+
+#ifdef EVENT_DEBUG
+ ndbout_c("SumaParticipant::runSUB_GCP_COMPLETE_ACC gci = %u", gci);
+#endif
+
+ c_failoverBuffer.subGcpCompleteRep(gci);
+}
+
+void
+Suma::execSUB_GCP_COMPLETE_ACC(Signal* signal){
+ jamEntry();
+
+ if (RtoI(signal->getSendersBlockRef(), false) != RNIL) {
+ jam();
+ // Ack from other SUMA
+ runSUB_GCP_COMPLETE_ACC(signal);
+ return;
+ }
+
+ jam();
+ // Ack from User and not an acc from other SUMA, redistribute in nodegroup
+
+ SubGcpCompleteAcc * const acc = (SubGcpCompleteAcc*)signal->getDataPtr();
+ Uint32 gci = acc->rep.gci;
+ Uint32 senderRef = acc->rep.senderRef;
+ Uint32 subscriberData = acc->rep.subscriberData;
+
+#ifdef EVENT_DEBUG
+ ndbout_c("Suma::execSUB_GCP_COMPLETE_ACC gci = %u", gci);
+#endif
+ bool moreToCome = false;
+
+ SubscriberPtr subbPtr;
+ for(c_dataSubscribers.first(subbPtr);
+ !subbPtr.isNull(); c_dataSubscribers.next(subbPtr)){
+#ifdef EVENT_DEBUG
+ ndbout_c("Suma::execSUB_GCP_COMPLETE_ACC %u == %u && %u == %u",
+ subbPtr.p->m_subscriberRef,
+ senderRef,
+ subbPtr.p->m_subscriberData,
+ subscriberData);
+#endif
+ if (subbPtr.p->m_subscriberRef == senderRef &&
+ subbPtr.p->m_subscriberData == subscriberData) {
+ jam();
+#ifdef EVENT_DEBUG
+ ndbout_c("Suma::execSUB_GCP_COMPLETE_ACC gci = FOUND SUBSCRIBER");
+#endif
+ subbPtr.p->m_lastGCI = gci;
+ } else if (subbPtr.p->m_lastGCI < gci) {
+ jam();
+ if (subbPtr.p->m_firstGCI <= gci)
+ moreToCome = true;
+ } else
+ jam();
+ }
+
+ if (!moreToCome) {
+ // tell the other SUMA's that I'm done with this GCI
+ jam();
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ Uint32 id = c_nodesInGroup[i];
+ Uint32 ref = calcSumaBlockRef(id);
+ if ((ref != reference()) && c_aliveNodes.get(id)) {
+ jam();
+ sendSignal(ref, GSN_SUB_GCP_COMPLETE_ACC, signal,
+ SubGcpCompleteAcc::SignalLength, JBB);
+ } else
+ jam();
+ }
+ }
+}
+
+static Uint32 tmpFailoverBuffer[512];
+//SumaParticipant::FailoverBuffer::FailoverBuffer(DataBuffer<15>::DataBufferPool & p)
+// : m_dataList(p),
+SumaParticipant::FailoverBuffer::FailoverBuffer()
+ :
+ c_gcis(tmpFailoverBuffer), c_sz(512), c_first(0), c_next(0), c_full(false)
+{
+}
+
+bool SumaParticipant::FailoverBuffer::subTableData(Uint32 gci, Uint32 *src, int sz)
+{
+ bool ok = true;
+
+ if (c_full) {
+ ok = false;
+#ifdef EVENT_DEBUG
+ ndbout_c("Suma::FailoverBuffer::SubTableData buffer full gci=%u");
+#endif
+ } else {
+ c_gcis[c_next] = gci;
+ c_next++;
+ if (c_next == c_sz) c_next = 0;
+ if (c_next == c_first)
+ c_full = true;
+ // ndbout_c("%u %u %u",c_first,c_next,c_sz);
+ }
+ return ok;
+}
+bool SumaParticipant::FailoverBuffer::subGcpCompleteRep(Uint32 gci)
+{
+ bool ok = true;
+
+ // ndbout_c("Empty");
+ while (true) {
+ if (c_first == c_next && !c_full)
+ break;
+ if (c_gcis[c_first] > gci)
+ break;
+ c_full = false;
+ c_first++;
+ if (c_first == c_sz) c_first = 0;
+ // ndbout_c("%u %u %u : ",c_first,c_next,c_sz);
+ }
+
+ return ok;
+}
+bool SumaParticipant::FailoverBuffer::nodeFailRep()
+{
+ bool ok = true;
+ while (true) {
+ if (c_first == c_next && !c_full)
+ break;
+
+#ifdef EVENT_DEBUG
+ ndbout_c("Suma::FailoverBuffer::NodeFailRep resending gci=%u", c_gcis[c_first]);
+#endif
+ c_full = false;
+ c_first++;
+ if (c_first == c_sz) c_first = 0;
+ }
+ return ok;
+}
+
+/**********************************************************
+ * Suma participant interface
+ *
+ * Stopping and removing of subscriber
+ *
+ */
+
+void
+SumaParticipant::execSUB_STOP_REQ(Signal* signal){
+ jamEntry();
+ DBUG_ENTER("SumaParticipant::execSUB_STOP_REQ");
+
+ CRASH_INSERTION(13019);
+
+ SubStopReq * const req = (SubStopReq*)signal->getDataPtr();
+ Uint32 senderRef = signal->getSendersBlockRef();
+ Uint32 senderData = req->senderData;
+ Uint32 subscriberRef = req->subscriberRef;
+ Uint32 subscriberData = req->subscriberData;
+ SubscriptionPtr subPtr;
+ Subscription key;
+ key.m_subscriptionId = req->subscriptionId;
+ key.m_subscriptionKey = req->subscriptionKey;
+ Uint32 part = req->part;
+
+ if (key.m_subscriptionKey == 0 &&
+ key.m_subscriptionId == 0 &&
+ subscriberData == 0) {
+ SubStopConf* conf = (SubStopConf*)signal->getDataPtrSend();
+
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->subscriptionId = key.m_subscriptionId;
+ conf->subscriptionKey = key.m_subscriptionKey;
+ conf->subscriberData = subscriberData;
+
+ sendSignal(senderRef, GSN_SUB_STOP_CONF, signal,
+ SubStopConf::SignalLength, JBB);
+
+ removeSubscribersOnNode(signal, refToNode(subscriberRef));
+ DBUG_VOID_RETURN;
+ }
+
+ if(!c_subscriptions.find(subPtr, key)){
+ jam();
+ sendSubStopRef(signal, GrepError::SUBSCRIPTION_ID_NOT_FOUND);
+ return;
+ }
+
+ ndbrequire(part == SubscriptionData::TableData);
+
+ SubscriberPtr subbPtr;
+ if (senderRef == reference()){
+ jam();
+ c_subscriberPool.getPtr(subbPtr, senderData);
+ ndbrequire(subbPtr.p->m_subPtrI == subPtr.i &&
+ subbPtr.p->m_subscriberRef == subscriberRef &&
+ subbPtr.p->m_subscriberData == subscriberData);
+ c_removeDataSubscribers.remove(subbPtr);
+ } else {
+ bool found = false;
+ jam();
+ c_dataSubscribers.first(subbPtr);
+ for (;!subbPtr.isNull(); c_dataSubscribers.next(subbPtr)){
+ jam();
+ if (subbPtr.p->m_subPtrI == subPtr.i &&
+ refToNode(subbPtr.p->m_subscriberRef) == refToNode(subscriberRef) &&
+ subbPtr.p->m_subscriberData == subscriberData){
+ // ndbout_c("STOP_REQ: before c_dataSubscribers.release");
+ jam();
+ c_dataSubscribers.remove(subbPtr);
+ found = true;
+ break;
+ }
+ }
+ /**
+ * If we didn't find anyone, send ref
+ */
+ if (!found) {
+ jam();
+ sendSubStopRef(signal, GrepError::SUBSCRIBER_NOT_FOUND);
+ DBUG_VOID_RETURN;
+ }
+ }
+
+ subbPtr.p->m_senderRef = senderRef; // store ref to requestor
+ subbPtr.p->m_senderData = senderData; // store ref to requestor
+ c_prepDataSubscribers.add(subbPtr);
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ if (syncPtr.p->m_locked) {
+ jam();
+ sendSubStopRef(signal, /** Error Code */ 0, true);
+ DBUG_VOID_RETURN;
+ }
+ syncPtr.p->m_locked = true;
+
+ syncPtr.p->startDropTrigger(signal);
+ DBUG_VOID_RETURN;
+}
+
+void
+SumaParticipant::sendSubStopComplete(Signal* signal, SubscriberPtr subbPtr){
+ jam();
+
+ CRASH_INSERTION(13020);
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.getPtr(subPtr, subbPtr.p->m_subPtrI);
+
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ syncPtr.p->m_locked = false;
+
+ SubStopConf * const conf = (SubStopConf*)signal->getDataPtrSend();
+
+ conf->senderRef = reference();
+ conf->senderData = subbPtr.p->m_senderData;
+ conf->subscriptionId = subPtr.p->m_subscriptionId;
+ conf->subscriptionKey = subPtr.p->m_subscriptionKey;
+ conf->subscriberData = subbPtr.p->m_subscriberData;
+ Uint32 senderRef = subbPtr.p->m_senderRef;
+
+ c_prepDataSubscribers.release(subbPtr);
+ sendSignal(senderRef, GSN_SUB_STOP_CONF, signal,
+ SubStopConf::SignalLength, JBB);
+}
+
+void
+SumaParticipant::sendSubStopRef(Signal* signal, Uint32 errCode,
+ bool temporary){
+ jam();
+ SubStopRef * ref = (SubStopRef *)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->errorCode = errCode;
+ if (temporary) {
+ ref->setTemporary();
+ }
+ sendSignal(signal->getSendersBlockRef(),
+ GSN_SUB_STOP_REF,
+ signal,
+ SubStopRef::SignalLength,
+ JBB);
+ return;
+}
+
+/**************************************************************
+ *
+ * Removing subscription
+ *
+ */
+
+void
+SumaParticipant::execSUB_REMOVE_REQ(Signal* signal) {
+ jamEntry();
+
+ Uint32 senderRef = signal->getSendersBlockRef();
+
+ CRASH_INSERTION(13021);
+
+ const SubRemoveReq req = *(SubRemoveReq*)signal->getDataPtr();
+ SubscriptionPtr subPtr;
+ Subscription key;
+ key.m_subscriptionId = req.subscriptionId;
+ key.m_subscriptionKey = req.subscriptionKey;
+
+ if(!c_subscriptions.find(subPtr, key)) {
+ jam();
+ sendSubRemoveRef(signal, req, (Uint32) GrepError::SUBSCRIPTION_ID_NOT_FOUND);
+ return;
+ }
+
+ int count = 0;
+ {
+ jam();
+ SubscriberPtr i_subbPtr;
+ for(c_prepDataSubscribers.first(i_subbPtr);
+ !i_subbPtr.isNull(); c_prepDataSubscribers.next(i_subbPtr)){
+ jam();
+ if( i_subbPtr.p->m_subPtrI == subPtr.i ) {
+ jam();
+ sendSubRemoveRef(signal, req, /* ErrorCode */ 0, true);
+ return;
+ // c_prepDataSubscribers.release(subbPtr);
+ }
+ }
+ c_dataSubscribers.first(i_subbPtr);
+ while(!i_subbPtr.isNull()){
+ jam();
+ SubscriberPtr subbPtr = i_subbPtr;
+ c_dataSubscribers.next(i_subbPtr);
+ if( subbPtr.p->m_subPtrI == subPtr.i ) {
+ jam();
+ sendSubRemoveRef(signal, req, /* ErrorCode */ 0, true);
+ return;
+ /* Unfinished/untested code. If remove should be possible
+ * even if subscribers are left these have to be stopped
+ * first. See m_markRemove, m_nSubscribers. We need also to
+ * block remove for this subscription so that multiple
+ * removes is not possible...
+ */
+ c_dataSubscribers.remove(subbPtr);
+ c_removeDataSubscribers.add(subbPtr);
+ count++;
+ }
+ }
+ c_metaSubscribers.first(i_subbPtr);
+ while(!i_subbPtr.isNull()){
+ jam();
+ SubscriberPtr subbPtr = i_subbPtr;
+ c_metaSubscribers.next(i_subbPtr);
+ if( subbPtr.p->m_subPtrI == subPtr.i ){
+ jam();
+ c_metaSubscribers.release(subbPtr);
+ }
+ }
+ }
+
+ subPtr.p->m_senderRef = senderRef;
+ subPtr.p->m_senderData = req.senderData;
+
+ if (count > 0){
+ jam();
+ ndbrequire(false); // code not finalized
+ subPtr.p->m_markRemove = true;
+ subPtr.p->m_nSubscribers = count;
+ sendSubStopReq(signal);
+ } else {
+ completeSubRemoveReq(signal, subPtr);
+ }
+}
+
+void
+SumaParticipant::completeSubRemoveReq(Signal* signal, SubscriptionPtr subPtr) {
+ Uint32 subscriptionId = subPtr.p->m_subscriptionId;
+ Uint32 subscriptionKey = subPtr.p->m_subscriptionKey;
+ Uint32 senderRef = subPtr.p->m_senderRef;
+ Uint32 senderData = subPtr.p->m_senderData;
+
+ {
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+
+ syncPtr.p->release();
+ c_syncPool.release(syncPtr);
+ }
+
+ // if (subPtr.p->m_subscriptionType != SubCreateReq::TableEvent) {
+ // jam();
+ // senderRef = subPtr.p->m_subscriberRef;
+ // }
+ c_subscriptions.release(subPtr);
+
+ /**
+ * I was the last subscription to be remove so clear c_tables
+ */
+#if 0
+ ndbout_c("c_subscriptionPool.getSize() %d c_subscriptionPool.getNoOfFree()%d",
+ c_subscriptionPool.getSize(),c_subscriptionPool.getNoOfFree());
+#endif
+
+ if(c_subscriptionPool.getSize() == c_subscriptionPool.getNoOfFree()) {
+ jam();
+#if 0
+ ndbout_c("SUB_REMOVE_REQ:Clearing c_tables");
+#endif
+ KeyTable<Table>::Iterator it;
+ for(c_tables.first(it); !it.isNull(); ){
+
+ it.curr.p->release(* this);
+
+ TablePtr tabPtr = it.curr;
+
+ c_tables.next(it);
+ c_tables.release(tabPtr);
+ }
+ }
+
+ SubRemoveConf * const conf = (SubRemoveConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ conf->subscriptionId = subscriptionId;
+ conf->subscriptionKey = subscriptionKey;
+
+ sendSignal(senderRef, GSN_SUB_REMOVE_CONF, signal,
+ SubRemoveConf::SignalLength, JBB);
+}
+
+void
+SumaParticipant::sendSubRemoveRef(Signal* signal, const SubRemoveReq& req,
+ Uint32 errCode, bool temporary){
+ jam();
+ SubRemoveRef * ref = (SubRemoveRef *)signal->getDataPtrSend();
+ ref->senderRef = reference();
+ ref->subscriptionId = req.subscriptionId;
+ ref->subscriptionKey = req.subscriptionKey;
+ ref->senderData = req.senderData;
+ ref->err = errCode;
+ if (temporary)
+ ref->setTemporary();
+ releaseSections(signal);
+ sendSignal(signal->getSendersBlockRef(), GSN_SUB_REMOVE_REF,
+ signal, SubRemoveRef::SignalLength, JBB);
+ return;
+}
+
+void
+SumaParticipant::Table::release(SumaParticipant & suma){
+ jam();
+
+ LocalDataBuffer<15> attrBuf(suma.c_dataBufferPool, m_attributes);
+ attrBuf.release();
+
+ LocalDataBuffer<15> fragBuf(suma.c_dataBufferPool, m_fragments);
+ fragBuf.release();
+}
+
+void
+SumaParticipant::SyncRecord::release(){
+ jam();
+ m_tableList.release();
+
+ LocalDataBuffer<15> attrBuf(suma.c_dataBufferPool, m_attributeList);
+ attrBuf.release();
+}
+
+
+/**************************************************************
+ *
+ * Restarting remote node functions, master functionality
+ * (slave does nothing special)
+ * - triggered on INCL_NODEREQ calling startNode
+ * - included node will issue START_ME when it's ready to start
+ * the subscribers
+ *
+ */
+
+Suma::Restart::Restart(Suma& s) : suma(s) {
+ for (int i = 0; i < MAX_REPLICAS; i++) {
+ c_okToStart[i] = false;
+ c_waitingToStart[i] = false;
+ }
+}
+
+void
+Suma::Restart::resetNode(Uint32 sumaRef)
+{
+ jam();
+ int I = suma.RtoI(sumaRef);
+ c_okToStart[I] = false;
+ c_waitingToStart[I] = false;
+}
+
+void
+Suma::Restart::startNode(Signal* signal, Uint32 sumaRef)
+{
+ jam();
+ resetNode(sumaRef);
+
+ // right now we can only handle restarting one node
+ // at a time in a node group
+
+ createSubscription(signal, sumaRef);
+}
+
+void
+Suma::Restart::createSubscription(Signal* signal, Uint32 sumaRef) {
+ jam();
+ suma.c_subscriptions.first(c_subPtr);
+ nextSubscription(signal, sumaRef);
+}
+
+void
+Suma::Restart::nextSubscription(Signal* signal, Uint32 sumaRef) {
+ jam();
+ if (c_subPtr.isNull()) {
+ jam();
+ completeSubscription(signal, sumaRef);
+ return;
+ }
+ SubscriptionPtr subPtr;
+ subPtr.i = c_subPtr.curr.i;
+ subPtr.p = suma.c_subscriptions.getPtr(subPtr.i);
+
+ suma.c_subscriptions.next(c_subPtr);
+
+ SubCreateReq * req = (SubCreateReq *)signal->getDataPtrSend();
+
+ req->subscriberRef = suma.reference();
+ req->subscriberData = subPtr.i;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->subscriptionType = subPtr.p->m_subscriptionType |
+ SubCreateReq::RestartFlag;
+
+ switch (subPtr.p->m_subscriptionType) {
+ case SubCreateReq::TableEvent:
+ case SubCreateReq::SelectiveTableSnapshot:
+ case SubCreateReq::DatabaseSnapshot: {
+ jam();
+
+ Ptr<SyncRecord> syncPtr;
+ suma.c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+ syncPtr.p->m_tableList.first(syncPtr.p->m_tableList_it);
+
+ ndbrequire(!syncPtr.p->m_tableList_it.isNull());
+
+ req->tableId = *syncPtr.p->m_tableList_it.data;
+
+#if 0
+ for (int i = 0; i < MAX_TABLES; i++)
+ if (subPtr.p->m_tables[i]) {
+ req->tableId = i;
+ break;
+ }
+#endif
+
+ suma.sendSignal(sumaRef, GSN_SUB_CREATE_REQ, signal,
+ SubCreateReq::SignalLength+1 /*to get table Id*/, JBB);
+ return;
+ }
+ case SubCreateReq::SingleTableScan :
+ // TODO
+ jam();
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Suma::execSUB_CREATE_CONF(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUB_CREATE_CONF");
+#endif
+
+ const Uint32 senderRef = signal->senderBlockRef();
+
+ SubCreateConf * const conf = (SubCreateConf *)signal->getDataPtr();
+
+ Subscription key;
+ const Uint32 subscriberData = conf->subscriberData;
+ key.m_subscriptionId = conf->subscriptionId;
+ key.m_subscriptionKey = conf->subscriptionKey;
+
+ SubscriptionPtr subPtr;
+ ndbrequire(c_subscriptions.find(subPtr, key));
+
+ switch(subPtr.p->m_subscriptionType) {
+ case SubCreateReq::TableEvent:
+ case SubCreateReq::SelectiveTableSnapshot:
+ case SubCreateReq::DatabaseSnapshot:
+ {
+ Ptr<SyncRecord> syncPtr;
+ c_syncPool.getPtr(syncPtr, subPtr.p->m_syncPtrI);
+
+ syncPtr.p->m_tableList.next(syncPtr.p->m_tableList_it);
+ if (syncPtr.p->m_tableList_it.isNull()) {
+ jam();
+ SubSyncReq *req = (SubSyncReq *)signal->getDataPtrSend();
+
+ req->subscriptionId = key.m_subscriptionId;
+ req->subscriptionKey = key.m_subscriptionKey;
+ req->subscriberData = subscriberData;
+ req->part = (Uint32) SubscriptionData::MetaData;
+
+ sendSignal(senderRef, GSN_SUB_SYNC_REQ, signal,
+ SubSyncReq::SignalLength, JBB);
+ } else {
+ jam();
+ SubCreateReq * req = (SubCreateReq *)signal->getDataPtrSend();
+
+ req->subscriberRef = reference();
+ req->subscriberData = subPtr.i;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->subscriptionType = subPtr.p->m_subscriptionType |
+ SubCreateReq::RestartFlag |
+ SubCreateReq::AddTableFlag;
+
+ req->tableId = *syncPtr.p->m_tableList_it.data;
+
+ sendSignal(senderRef, GSN_SUB_CREATE_REQ, signal,
+ SubCreateReq::SignalLength+1 /*to get table Id*/, JBB);
+ }
+ }
+ return;
+ case SubCreateReq::SingleTableScan:
+ ndbrequire(false);
+ }
+ ndbrequire(false);
+}
+
+void
+Suma::execSUB_CREATE_REF(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUB_CREATE_REF");
+#endif
+ //ndbrequire(false);
+}
+
+void
+Suma::execSUB_SYNC_CONF(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUB_SYNC_CONF");
+#endif
+ Uint32 sumaRef = signal->getSendersBlockRef();
+
+ SubSyncConf *conf = (SubSyncConf *)signal->getDataPtr();
+ Subscription key;
+
+ key.m_subscriptionId = conf->subscriptionId;
+ key.m_subscriptionKey = conf->subscriptionKey;
+ // SubscriptionData::Part part = (SubscriptionData::Part)conf->part;
+ // const Uint32 subscriberData = conf->subscriberData;
+
+ SubscriptionPtr subPtr;
+ c_subscriptions.find(subPtr, key);
+
+ switch(subPtr.p->m_subscriptionType) {
+ case SubCreateReq::TableEvent:
+ case SubCreateReq::SelectiveTableSnapshot:
+ case SubCreateReq::DatabaseSnapshot:
+ jam();
+ Restart.nextSubscription(signal, sumaRef);
+ return;
+ case SubCreateReq::SingleTableScan:
+ ndbrequire(false);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Suma::execSUB_SYNC_REF(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUB_SYNC_REF");
+#endif
+ //ndbrequire(false);
+}
+
+void
+Suma::execSUMA_START_ME(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUMA_START_ME");
+#endif
+
+ Restart.runSUMA_START_ME(signal, signal->getSendersBlockRef());
+}
+
+void
+Suma::Restart::runSUMA_START_ME(Signal* signal, Uint32 sumaRef) {
+ int I = suma.RtoI(sumaRef);
+
+ // restarting Suma is ready for SUB_START_REQ
+ if (c_waitingToStart[I]) {
+ // we've waited with startSubscriber since restarting suma was not ready
+ c_waitingToStart[I] = false;
+ startSubscriber(signal, sumaRef);
+ } else {
+ // do startSubscriber as soon as its time
+ c_okToStart[I] = true;
+ }
+}
+
+void
+Suma::Restart::completeSubscription(Signal* signal, Uint32 sumaRef) {
+ jam();
+ int I = suma.RtoI(sumaRef);
+
+ if (c_okToStart[I]) {// otherwise will start when START_ME comes
+ c_okToStart[I] = false;
+ startSubscriber(signal, sumaRef);
+ } else {
+ c_waitingToStart[I] = true;
+ }
+}
+
+void
+Suma::Restart::startSubscriber(Signal* signal, Uint32 sumaRef) {
+ jam();
+ suma.c_dataSubscribers.first(c_subbPtr);
+ nextSubscriber(signal, sumaRef);
+}
+
+void
+Suma::Restart::sendSubStartReq(SubscriptionPtr subPtr, SubscriberPtr subbPtr,
+ Signal* signal, Uint32 sumaRef)
+{
+ jam();
+ SubStartReq * req = (SubStartReq *)signal->getDataPtrSend();
+
+ req->senderRef = suma.reference();
+ req->senderData = subbPtr.p->m_senderData;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->part = SubscriptionData::TableData;
+ req->subscriberData = subbPtr.p->m_subscriberData;
+ req->subscriberRef = subbPtr.p->m_subscriberRef;
+
+ // restarting suma will not respond to this until startphase 5
+ // since it is not until then data copying has been completed
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::Restart::sendSubStartReq sending GSN_SUB_START_REQ id=%u key=%u",
+ req->subscriptionId, req->subscriptionKey);
+#endif
+ suma.sendSignal(sumaRef, GSN_SUB_START_REQ,
+ signal, SubStartReq::SignalLength2, JBB);
+}
+
+void
+Suma::execSUB_START_CONF(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUB_START_CONF");
+#endif
+ Uint32 sumaRef = signal->getSendersBlockRef();
+ Restart.nextSubscriber(signal, sumaRef);
+}
+
+void
+Suma::execSUB_START_REF(Signal* signal) {
+ jamEntry();
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUB_START_REF");
+#endif
+ //ndbrequire(false);
+}
+
+void
+Suma::Restart::nextSubscriber(Signal* signal, Uint32 sumaRef) {
+ jam();
+ if (c_subbPtr.isNull()) {
+ jam();
+ completeSubscriber(signal, sumaRef);
+ return;
+ }
+
+ SubscriberPtr subbPtr = c_subbPtr;
+ suma.c_dataSubscribers.next(c_subbPtr);
+
+ /*
+ * get subscription ptr for this subscriber
+ */
+
+ SubscriptionPtr subPtr;
+ suma.c_subscriptions.getPtr(subPtr, subbPtr.p->m_subPtrI);
+ switch (subPtr.p->m_subscriptionType) {
+ case SubCreateReq::TableEvent:
+ case SubCreateReq::SelectiveTableSnapshot:
+ case SubCreateReq::DatabaseSnapshot:
+ {
+ jam();
+ sendSubStartReq(subPtr, subbPtr, signal, sumaRef);
+#if 0
+ SubStartReq * req = (SubStartReq *)signal->getDataPtrSend();
+
+ req->senderRef = reference();
+ req->senderData = subbPtr.p->m_senderData;
+ req->subscriptionId = subPtr.p->m_subscriptionId;
+ req->subscriptionKey = subPtr.p->m_subscriptionKey;
+ req->part = SubscriptionData::TableData;
+ req->subscriberData = subbPtr.p->m_subscriberData;
+ req->subscriberRef = subbPtr.p->m_subscriberRef;
+
+ // restarting suma will not respond to this until startphase 5
+ // since it is not until then data copying has been completed
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::nextSubscriber sending GSN_SUB_START_REQ id=%u key=%u",
+ req->subscriptionId, req->subscriptionKey);
+#endif
+ suma.sendSignal(sumaRef, GSN_SUB_START_REQ,
+ signal, SubStartReq::SignalLength2, JBB);
+#endif
+ }
+ return;
+ case SubCreateReq::SingleTableScan:
+ ndbrequire(false);
+ return;
+ }
+ ndbrequire(false);
+}
+
+void
+Suma::Restart::completeSubscriber(Signal* signal, Uint32 sumaRef) {
+ completeRestartingNode(signal, sumaRef);
+}
+
+void
+Suma::Restart::completeRestartingNode(Signal* signal, Uint32 sumaRef) {
+ jam();
+ SumaHandoverReq * req = (SumaHandoverReq *)signal->getDataPtrSend();
+
+ req->gci = suma.getFirstGCI(signal);
+
+ suma.sendSignal(sumaRef, GSN_SUMA_HANDOVER_REQ, signal,
+ SumaHandoverReq::SignalLength, JBB);
+}
+
+// only run on restarting suma
+
+void
+Suma::execSUMA_HANDOVER_REQ(Signal* signal)
+{
+ jamEntry();
+ // Uint32 sumaRef = signal->getSendersBlockRef();
+ SumaHandoverReq const * req = (SumaHandoverReq *)signal->getDataPtr();
+
+ Uint32 gci = req->gci;
+ Uint32 new_gci = getFirstGCI(signal);
+
+ if (new_gci > gci) {
+ gci = new_gci;
+ }
+
+ { // all recreated subscribers at restarting SUMA start at same GCI
+ SubscriberPtr subbPtr;
+ for(c_dataSubscribers.first(subbPtr);
+ !subbPtr.isNull();
+ c_dataSubscribers.next(subbPtr)){
+ subbPtr.p->m_firstGCI = gci;
+ }
+ }
+
+#ifdef NODEFAIL_DEBUG
+ ndbout_c("Suma::execSUMA_HANDOVER_REQ, gci = %u", gci);
+#endif
+
+ c_handoverToDo = false;
+ c_restartLock = false;
+ {
+#ifdef HANDOVER_DEBUG
+ int c = 0;
+#endif
+ for( int i = 0; i < NO_OF_BUCKETS; i++) {
+ jam();
+ if (getResponsibleSumaNodeId(i) == refToNode(reference())) {
+#ifdef HANDOVER_DEBUG
+ c++;
+#endif
+ jam();
+ c_buckets[i].active = false;
+ c_buckets[i].handoverGCI = gci;
+ c_buckets[i].handover = true;
+ c_buckets[i].handover_started = false;
+ c_handoverToDo = true;
+ }
+ }
+#ifdef HANDOVER_DEBUG
+ ndbout_c("prepared handover of bucket %u buckets", c);
+#endif
+ }
+
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ jam();
+ Uint32 ref = calcSumaBlockRef(c_nodesInGroup[i]);
+ if (ref != reference()) {
+ jam();
+ sendSignal(ref, GSN_SUMA_HANDOVER_CONF, signal,
+ SumaHandoverConf::SignalLength, JBB);
+ }//if
+ }
+}
+
+// only run on all but restarting suma
+void
+Suma::execSUMA_HANDOVER_CONF(Signal* signal) {
+ jamEntry();
+ Uint32 sumaRef = signal->getSendersBlockRef();
+ SumaHandoverConf const * conf = (SumaHandoverConf *)signal->getDataPtr();
+
+ Uint32 gci = conf->gci;
+
+#ifdef HANDOVER_DEBUG
+ ndbout_c("Suma::execSUMA_HANDOVER_CONF, gci = %u", gci);
+#endif
+
+ /* TODO, if we are restarting several SUMA's (>2 in a nodegroup)
+ * we have to collect all these conf's before proceding
+ */
+
+ // restarting node is now prepared and ready
+ c_preparingNodes.clear(refToNode(sumaRef)); /* !! important to do before
+ * below since it affects
+ * getResponsibleSumaNodeId()
+ */
+
+ c_handoverToDo = false;
+ // mark all active buckets really belonging to restarting SUMA
+ for( int i = 0; i < NO_OF_BUCKETS; i++) {
+ if (c_buckets[i].active) {
+ // I'm running this bucket
+ if (getResponsibleSumaNodeId(i) == refToNode(sumaRef)) {
+ // but it should really be the restarted node
+ c_buckets[i].handoverGCI = gci;
+ c_buckets[i].handover = true;
+ c_buckets[i].handover_started = false;
+ c_handoverToDo = true;
+ }
+ }
+ }
+}
+
+template void append(DataBuffer<11>&,SegmentedSectionPtr,SectionSegmentPool&);
+
diff --git a/storage/ndb/src/kernel/blocks/suma/Suma.hpp b/storage/ndb/src/kernel/blocks/suma/Suma.hpp
new file mode 100644
index 00000000000..65869f44423
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/suma/Suma.hpp
@@ -0,0 +1,600 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SUMA_H
+#define SUMA_H
+
+#include <ndb_limits.h>
+#include <SimulatedBlock.hpp>
+
+#include <NodeBitmask.hpp>
+
+#include <SLList.hpp>
+#include <DLList.hpp>
+#include <KeyTable.hpp>
+#include <DataBuffer.hpp>
+#include <SignalCounter.hpp>
+#include <AttributeHeader.hpp>
+#include <AttributeList.hpp>
+
+#include <signaldata/UtilSequence.hpp>
+#include <signaldata/SumaImpl.hpp>
+
+class SumaParticipant : public SimulatedBlock {
+protected:
+ SumaParticipant(const Configuration & conf);
+ virtual ~SumaParticipant();
+ BLOCK_DEFINES(SumaParticipant);
+
+protected:
+ /**
+ * Private interface
+ */
+ void execSUB_CREATE_REQ(Signal* signal);
+ void execSUB_REMOVE_REQ(Signal* signal);
+
+ void execSUB_START_REQ(Signal* signal);
+ void execSUB_STOP_REQ(Signal* signal);
+
+ void execSUB_SYNC_REQ(Signal* signal);
+ void execSUB_ABORT_SYNC_REQ(Signal* signal);
+
+ void execSUB_STOP_CONF(Signal* signal);
+ void execSUB_STOP_REF(Signal* signal);
+
+ /**
+ * Dict interface
+ */
+ void execLIST_TABLES_REF(Signal* signal);
+ void execLIST_TABLES_CONF(Signal* signal);
+ void execGET_TABINFOREF(Signal* signal);
+ void execGET_TABINFO_CONF(Signal* signal);
+#if 0
+ void execGET_TABLEID_CONF(Signal* signal);
+ void execGET_TABLEID_REF(Signal* signal);
+#endif
+ /**
+ * Scan interface
+ */
+ void execSCAN_HBREP(Signal* signal);
+ void execSCAN_FRAGREF(Signal* signal);
+ void execSCAN_FRAGCONF(Signal* signal);
+ void execTRANSID_AI(Signal* signal);
+ void execSUB_SYNC_CONTINUE_REF(Signal* signal);
+ void execSUB_SYNC_CONTINUE_CONF(Signal* signal);
+
+ /**
+ * Trigger logging
+ */
+ void execTRIG_ATTRINFO(Signal* signal);
+ void execFIRE_TRIG_ORD(Signal* signal);
+ void execSUB_GCP_COMPLETE_REP(Signal* signal);
+ void runSUB_GCP_COMPLETE_ACC(Signal* signal);
+
+ /**
+ * DIH signals
+ */
+ void execDI_FCOUNTREF(Signal* signal);
+ void execDI_FCOUNTCONF(Signal* signal);
+ void execDIGETPRIMREF(Signal* signal);
+ void execDIGETPRIMCONF(Signal* signal);
+
+ /**
+ * Trigger administration
+ */
+ void execCREATE_TRIG_REF(Signal* signal);
+ void execCREATE_TRIG_CONF(Signal* signal);
+ void execDROP_TRIG_REF(Signal* signal);
+ void execDROP_TRIG_CONF(Signal* signal);
+
+ /**
+ * continueb
+ */
+ void execCONTINUEB(Signal* signal);
+
+public:
+ typedef DataBuffer<15> TableList;
+
+ union FragmentDescriptor {
+ struct {
+ Uint16 m_fragmentNo;
+ Uint16 m_nodeId;
+ } m_fragDesc;
+ Uint32 m_dummy;
+ };
+
+ /**
+ * Used when sending SCAN_FRAG
+ */
+ union AttributeDescriptor {
+ struct {
+ Uint16 attrId;
+ Uint16 unused;
+ } m_attrDesc;
+ Uint32 m_dummy;
+ };
+
+ struct Table {
+ Table() { m_tableId = ~0; }
+ void release(SumaParticipant&);
+
+ union { Uint32 m_tableId; Uint32 key; };
+ Uint32 m_schemaVersion;
+ Uint32 m_hasTriggerDefined[3]; // Insert/Update/Delete
+ Uint32 m_triggerIds[3]; // Insert/Update/Delete
+
+ /**
+ * Default order in which to ask for attributes during scan
+ * 1) Fixed, not nullable
+ * 2) Rest
+ */
+ DataBuffer<15>::Head m_attributes; // Attribute id's
+
+ /**
+ * Fragments
+ */
+ DataBuffer<15>::Head m_fragments; // Fragment descriptors
+
+ /**
+ * Hash table stuff
+ */
+ Uint32 nextHash;
+ union { Uint32 prevHash; Uint32 nextPool; };
+ Uint32 hashValue() const {
+ return m_tableId;
+ }
+ bool equal(const Table& rec) const {
+ return m_tableId == rec.m_tableId;
+ }
+ };
+ typedef Ptr<Table> TablePtr;
+
+ /**
+ * Subscriptions
+ */
+ struct SyncRecord {
+ SyncRecord(SumaParticipant& s, DataBuffer<15>::DataBufferPool & p)
+ : m_locked(false), m_tableList(p), suma(s)
+#ifdef ERROR_INSERT
+ , cerrorInsert(s.cerrorInsert)
+#endif
+ {}
+
+ void release();
+
+ Uint32 m_subscriptionPtrI;
+ bool m_locked;
+ bool m_doSendSyncData;
+ bool m_error;
+ TableList m_tableList; // Tables to sync (snapshoted at beginning)
+ TableList::DataBufferIterator m_tableList_it;
+
+ /**
+ * Sync meta
+ */
+ void startMeta(Signal*);
+ void nextMeta(Signal*);
+ void completeMeta(Signal*);
+
+ /**
+ * Create triggers
+ */
+ Uint32 m_latestTriggerId;
+ void startTrigger(Signal* signal);
+ void nextTrigger(Signal* signal);
+ void completeTrigger(Signal* signal);
+ void createAttributeMask(AttributeMask&, Table*);
+
+ /**
+ * Drop triggers
+ */
+ void startDropTrigger(Signal* signal);
+ void nextDropTrigger(Signal* signal);
+ void completeDropTrigger(Signal* signal);
+
+ /**
+ * Sync data
+ */
+ Uint32 m_currentTable; // Index in m_tableList
+ Uint32 m_currentFragment; // Index in tabPtr.p->m_fragments
+ DataBuffer<15>::Head m_attributeList; // Attribute if other than default
+ DataBuffer<15>::Head m_tabList; // tables if other than default
+
+ Uint32 m_currentTableId; // Current table
+ Uint32 m_currentNoOfAttributes; // No of attributes for current table
+ void startScan(Signal*);
+ void nextScan(Signal*);
+ bool getNextFragment(TablePtr * tab, FragmentDescriptor * fd);
+ void completeScan(Signal*);
+
+ SumaParticipant & suma;
+#ifdef ERROR_INSERT
+ UintR &cerrorInsert;
+#endif
+ BlockNumber number() const { return suma.number(); }
+ void progError(int line, int cause, const char * extra) {
+ suma.progError(line, cause, extra);
+ }
+
+ void runLIST_TABLES_CONF(Signal* signal);
+ void runGET_TABINFO_CONF(Signal* signal);
+ void runGET_TABINFOREF(Signal* signal);
+
+ void runDI_FCOUNTCONF(Signal* signal);
+ void runDIGETPRIMCONF(Signal* signal);
+
+ void runCREATE_TRIG_CONF(Signal* signal);
+ void runDROP_TRIG_CONF(Signal* signal);
+ void runDROP_TRIG_REF(Signal* signal);
+ void runDropTrig(Signal* signal, Uint32 triggerId, Uint32 tableId);
+
+ union { Uint32 nextPool; Uint32 nextList; Uint32 ptrI; };
+ };
+ friend struct SyncRecord;
+
+ struct Subscription {
+ Uint32 m_subscriberRef;
+ Uint32 m_subscriberData;
+ Uint32 m_senderRef;
+ Uint32 m_senderData;
+ Uint32 m_subscriptionId;
+ Uint32 m_subscriptionKey;
+ Uint32 m_subscriptionType;
+ Uint32 m_coordinatorRef;
+ Uint32 m_syncPtrI; // Active sync operation
+ Uint32 m_nSubscribers;
+ bool m_markRemove;
+
+ Uint32 nextHash;
+ union { Uint32 prevHash; Uint32 nextPool; };
+
+ Uint32 hashValue() const {
+ return m_subscriptionId + m_subscriptionKey;
+ }
+
+ bool equal(const Subscription & s) const {
+ return
+ m_subscriptionId == s.m_subscriptionId &&
+ m_subscriptionKey == s.m_subscriptionKey;
+ }
+ /**
+ * The following holds the table names of tables included
+ * in the subscription.
+ */
+ // TODO we've got to fix this, this is to inefficient. Tomas
+ char m_tables[MAX_TABLES];
+#if 0
+ char m_tableNames[MAX_TABLES][MAX_TAB_NAME_SIZE];
+#endif
+ /**
+ * "Iterator" used to iterate through m_tableNames
+ */
+ Uint32 m_maxTables;
+ Uint32 m_currentTable;
+ };
+ typedef Ptr<Subscription> SubscriptionPtr;
+
+ struct Subscriber {
+ Uint32 m_senderRef;
+ Uint32 m_senderData;
+ Uint32 m_subscriberRef;
+ Uint32 m_subscriberData;
+ Uint32 m_subPtrI; //reference to subscription
+ Uint32 m_firstGCI; // first GCI to send
+ Uint32 m_lastGCI; // last acnowledged GCI
+ Uint32 nextList;
+ union { Uint32 nextPool; Uint32 prevList; };
+ };
+ typedef Ptr<Subscriber> SubscriberPtr;
+
+ struct Bucket {
+ bool active;
+ bool handover;
+ bool handover_started;
+ Uint32 handoverGCI;
+ };
+#define NO_OF_BUCKETS 24
+ struct Bucket c_buckets[NO_OF_BUCKETS];
+ bool c_handoverToDo;
+ Uint32 c_lastCompleteGCI;
+
+ /**
+ *
+ */
+ DLList<Subscriber> c_metaSubscribers;
+ DLList<Subscriber> c_dataSubscribers;
+ DLList<Subscriber> c_prepDataSubscribers;
+ DLList<Subscriber> c_removeDataSubscribers;
+
+ /**
+ * Lists
+ */
+ KeyTable<Table> c_tables;
+ DLHashTable<Subscription> c_subscriptions;
+
+ /**
+ * Pools
+ */
+ ArrayPool<Subscriber> c_subscriberPool;
+ ArrayPool<Table> c_tablePool_;
+ ArrayPool<Subscription> c_subscriptionPool;
+ ArrayPool<SyncRecord> c_syncPool;
+ DataBuffer<15>::DataBufferPool c_dataBufferPool;
+
+ /**
+ * for restarting Suma not to start sending data too early
+ */
+ bool c_restartLock;
+
+ /**
+ * for flagging that a GCI containg inconsistent data
+ * typically due to node failiure
+ */
+
+ Uint32 c_lastInconsistentGCI;
+ Uint32 c_nodeFailGCI;
+
+ NodeBitmask c_failedApiNodes;
+
+ /**
+ * Functions
+ */
+ bool removeSubscribersOnNode(Signal *signal, Uint32 nodeId);
+
+ bool parseTable(Signal* signal, class GetTabInfoConf* conf, Uint32 tableId,
+ SyncRecord* syncPtr_p);
+ bool checkTableTriggers(SegmentedSectionPtr ptr);
+
+ void addTableId(Uint32 TableId,
+ SubscriptionPtr subPtr, SyncRecord *psyncRec);
+
+ void sendSubIdRef(Signal* signal, Uint32 errorCode);
+ void sendSubCreateConf(Signal* signal, Uint32 sender, SubscriptionPtr subPtr);
+ void sendSubCreateRef(Signal* signal, const SubCreateReq& req, Uint32 errorCode);
+ void sendSubStartRef(SubscriptionPtr subPtr, Signal* signal,
+ Uint32 errorCode, bool temporary = false);
+ void sendSubStartRef(Signal* signal,
+ Uint32 errorCode, bool temporary = false);
+ void sendSubStopRef(Signal* signal,
+ Uint32 errorCode, bool temporary = false);
+ void sendSubSyncRef(Signal* signal, Uint32 errorCode);
+ void sendSubRemoveRef(Signal* signal, const SubRemoveReq& ref,
+ Uint32 errorCode, bool temporary = false);
+ void sendSubStartComplete(Signal*, SubscriberPtr, Uint32,
+ SubscriptionData::Part);
+ void sendSubStopComplete(Signal*, SubscriberPtr);
+ void sendSubStopReq(Signal* signal, bool unlock= false);
+
+ void completeSubRemoveReq(Signal* signal, SubscriptionPtr subPtr);
+
+ Uint32 getFirstGCI(Signal* signal);
+ Uint32 decideWhoToSend(Uint32 nBucket, Uint32 gci);
+
+ virtual Uint32 getStoreBucket(Uint32 v) = 0;
+ virtual Uint32 getResponsibleSumaNodeId(Uint32 D) = 0;
+ virtual Uint32 RtoI(Uint32 sumaRef, bool dieOnNotFound = true) = 0;
+
+ struct FailoverBuffer {
+ // FailoverBuffer(DataBuffer<15>::DataBufferPool & p);
+ FailoverBuffer();
+
+ bool subTableData(Uint32 gci, Uint32 *src, int sz);
+ bool subGcpCompleteRep(Uint32 gci);
+ bool nodeFailRep();
+
+ // typedef DataBuffer<15> GCIDataBuffer;
+ // GCIDataBuffer m_GCIDataBuffer;
+ // GCIDataBuffer::DataBufferIterator m_GCIDataBuffer_it;
+
+ Uint32 *c_gcis;
+ int c_sz;
+
+ // Uint32 *c_buf;
+ // int c_buf_sz;
+
+ int c_first;
+ int c_next;
+ bool c_full;
+ } c_failoverBuffer;
+
+ /**
+ * Table admin
+ */
+ void convertNameToId( SubscriptionPtr subPtr, Signal * signal);
+
+
+};
+
+class Suma : public SumaParticipant {
+ BLOCK_DEFINES(Suma);
+public:
+ Suma(const Configuration & conf);
+ virtual ~Suma();
+private:
+ /**
+ * Public interface
+ */
+ void execCREATE_SUBSCRIPTION_REQ(Signal* signal);
+ void execDROP_SUBSCRIPTION_REQ(Signal* signal);
+
+ void execSTART_SUBSCRIPTION_REQ(Signal* signal);
+ void execSTOP_SUBSCRIPTION_REQ(Signal* signal);
+
+ void execSYNC_SUBSCRIPTION_REQ(Signal* signal);
+ void execABORT_SYNC_REQ(Signal* signal);
+
+ /**
+ * Framework signals
+ */
+
+ void getNodeGroupMembers(Signal* signal);
+
+ void execSTTOR(Signal* signal);
+ void sendSTTORRY(Signal*);
+ void execNDB_STTOR(Signal* signal);
+ void execDUMP_STATE_ORD(Signal* signal);
+ void execREAD_NODESCONF(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execINCL_NODEREQ(Signal* signal);
+ void execCONTINUEB(Signal* signal);
+ void execSIGNAL_DROPPED_REP(Signal* signal);
+ void execAPI_FAILREQ(Signal* signal) ;
+
+ void execSUB_GCP_COMPLETE_ACC(Signal* signal);
+
+ /**
+ * Controller interface
+ */
+ void execSUB_CREATE_REF(Signal* signal);
+ void execSUB_CREATE_CONF(Signal* signal);
+
+ void execSUB_DROP_REF(Signal* signal);
+ void execSUB_DROP_CONF(Signal* signal);
+
+ void execSUB_START_REF(Signal* signal);
+ void execSUB_START_CONF(Signal* signal);
+
+ void execSUB_STOP_REF(Signal* signal);
+ void execSUB_STOP_CONF(Signal* signal);
+
+ void execSUB_SYNC_REF(Signal* signal);
+ void execSUB_SYNC_CONF(Signal* signal);
+
+ void execSUB_ABORT_SYNC_REF(Signal* signal);
+ void execSUB_ABORT_SYNC_CONF(Signal* signal);
+
+ void execSUMA_START_ME(Signal* signal);
+ void execSUMA_HANDOVER_REQ(Signal* signal);
+ void execSUMA_HANDOVER_CONF(Signal* signal);
+
+ /**
+ * Subscription generation interface
+ */
+ void createSequence(Signal* signal);
+ void createSequenceReply(Signal* signal,
+ UtilSequenceConf* conf,
+ UtilSequenceRef* ref);
+ void execUTIL_SEQUENCE_CONF(Signal* signal);
+ void execUTIL_SEQUENCE_REF(Signal* signal);
+ void execCREATE_SUBID_REQ(Signal* signal);
+
+ Uint32 getStoreBucket(Uint32 v);
+ Uint32 getResponsibleSumaNodeId(Uint32 D);
+
+ /**
+ * for Suma that is restarting another
+ */
+
+ struct Restart {
+ Restart(Suma& s);
+
+ Suma & suma;
+
+ bool c_okToStart[MAX_REPLICAS];
+ bool c_waitingToStart[MAX_REPLICAS];
+
+ DLHashTable<SumaParticipant::Subscription>::Iterator c_subPtr; // TODO [MAX_REPLICAS]
+ SubscriberPtr c_subbPtr; // TODO [MAX_REPLICAS]
+
+ void progError(int line, int cause, const char * extra) {
+ suma.progError(line, cause, extra);
+ }
+
+ void resetNode(Uint32 sumaRef);
+ void runSUMA_START_ME(Signal*, Uint32 sumaRef);
+ void startNode(Signal*, Uint32 sumaRef);
+
+ void createSubscription(Signal* signal, Uint32 sumaRef);
+ void nextSubscription(Signal* signal, Uint32 sumaRef);
+ void completeSubscription(Signal* signal, Uint32 sumaRef);
+
+ void startSync(Signal* signal, Uint32 sumaRef);
+ void nextSync(Signal* signal, Uint32 sumaRef);
+ void completeSync(Signal* signal, Uint32 sumaRef);
+
+ void sendSubStartReq(SubscriptionPtr subPtr, SubscriberPtr subbPtr,
+ Signal* signal, Uint32 sumaRef);
+ void startSubscriber(Signal* signal, Uint32 sumaRef);
+ void nextSubscriber(Signal* signal, Uint32 sumaRef);
+ void completeSubscriber(Signal* signal, Uint32 sumaRef);
+
+ void completeRestartingNode(Signal* signal, Uint32 sumaRef);
+ } Restart;
+
+private:
+ friend class Restart;
+ struct SubCoordinator {
+ Uint32 m_subscriberRef;
+ Uint32 m_subscriberData;
+
+ Uint32 m_subscriptionId;
+ Uint32 m_subscriptionKey;
+
+ NdbNodeBitmask m_participants;
+
+ Uint32 m_outstandingGsn;
+ SignalCounter m_outstandingRequests;
+
+ Uint32 nextList;
+ union { Uint32 prevList; Uint32 nextPool; };
+ };
+ Ptr<SubCoordinator> SubCoordinatorPtr;
+
+ struct Node {
+ Uint32 nodeId;
+ Uint32 alive;
+ Uint32 nextList;
+ union { Uint32 prevList; Uint32 nextPool; };
+ };
+ typedef Ptr<Node> NodePtr;
+
+ /**
+ * Variables
+ */
+ NodeId c_masterNodeId;
+ SLList<Node> c_nodes;
+ NdbNodeBitmask c_aliveNodes;
+ NdbNodeBitmask c_preparingNodes;
+
+ Uint32 RtoI(Uint32 sumaRef, bool dieOnNotFound = true);
+
+ /**
+ * for all Suma's to keep track of other Suma's in Node group
+ */
+ Uint32 c_nodeGroup;
+ Uint32 c_noNodesInGroup;
+ Uint32 c_idInNodeGroup;
+ NodeId c_nodesInGroup[MAX_REPLICAS];
+
+ /**
+ * don't seem to be used
+ */
+ ArrayPool<Node> c_nodePool;
+ ArrayPool<SubCoordinator> c_subCoordinatorPool;
+ DLList<SubCoordinator> c_runningSubscriptions;
+};
+
+inline Uint32
+Suma::RtoI(Uint32 sumaRef, bool dieOnNotFound) {
+ for (Uint32 i = 0; i < c_noNodesInGroup; i++) {
+ if (sumaRef == calcSumaBlockRef(c_nodesInGroup[i]))
+ return i;
+ }
+ ndbrequire(!dieOnNotFound);
+ return RNIL;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/blocks/suma/Suma.txt b/storage/ndb/src/kernel/blocks/suma/Suma.txt
new file mode 100644
index 00000000000..eba031226ef
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/suma/Suma.txt
@@ -0,0 +1,192 @@
+Protocols involving SUMA:
+
+
+
+USER SUMA UTIL
+========================================================
+CREATE_SUBID_REQ
+------------------------->
+ UTIL_SEQUENCE
+ ---------------------->
+ <----------------------
+CREATE_SUBID_CONF
+<-------------------------
+
+
+
+
+USER SUMA DICT
+========================================================
+SUB_CREATE_REQ
+------------------------->
+ case SelectiveTableSnapshot:
+ GET_TABLEID
+ ---------------------->
+ <----------------------
+SUB_CREATE_CONF
+<-------------------------
+
+
+
+
+
+USER SUMA DICT
+========================================================
+SUB_SYNC_REQ::MetaData
+------------------------->
+ case DatabaseSnapshot:
+ LIST_TABLES
+ ---------------------->
+ <----------------------
+for each table...
+ GET_TABINFO
+ ---------------------->
+ <----------------------
+SUB_META_DATA DIH
+<------------------------- =======
+ DI_FCOUNT
+ ---------------------->
+ <----------------------
+ DI_GETPRIM
+ ---------------------->
+ <----------------------
+..end for each table
+SUB_SYNC_CONF
+<-------------------------
+
+
+
+
+USER SUMA LQH
+========================================================
+SUB_SYNC_REQ::TableData
+------------------------->
+for each table...
+ SCAN_FRAG_REQ
+ ---------------------->
+ ATTRINFO
+ ---------------------->
+ SCAN_FRAG_CONF
+ <----------------------
+SUB_SYNC_CONTINUE
+<-------------------------
+------------------------->
+ SCAN_NEXTREQ
+ ---------------------->
+...end for each table
+
+
+
+??????????
+ SCAN_HBREP
+ <----------------------
+
+
+
+USER SUMA
+===============================
+SUB_START_REQ::MetaData
+------------------------->
+SUB_START_CONF
+<-------------------------
+
+
+
+USER SUMA TUP
+========================================================
+SUB_START_REQ::TableData
+------------------------->
+for each table...
+ CREATE_TRIG
+ ---------------------->
+ <----------------------
+...end for each table
+SUB_START_CONF
+<-------------------------
+
+
+USER SUMA XXX
+========================================================
+ TRANSID_AI
+ <----------------------
+SUB_TABLE_DATA
+<-------------------------
+
+
+
+
+
+USER SUMA XXX
+========================================================
+ TRIG_ATTRINFO
+ <----------------------
+ FIRE_TRIG_ORD
+ <----------------------
+SUB_TABLE_DATA
+<-------------------------
+
+
+
+USER SUMA XXX
+========================================================
+ SUB_GCP_COMPLETE_REP
+ <----------------------
+SUB_GCP_COMPLETE_REP
+<-------------------------
+
+for event only:
+SUB_GCP_COMPLETE_ACK
+------------------------->
+ when all subscribers have sent ACK on gci
+ send to all other suma's in node group:
+ SUB_GCP_COMPLETE_ACK
+ ---------------------->
+
+
+USER SUMA
+===============================
+SUB_STOP_REQ
+------------------------->
+SUB_STOP_CONF
+<-------------------------
+
+
+
+USER SUMA
+===============================
+SUB_REMOVE_REQ
+------------------------->
+SUB_REMOVE_CONF
+<-------------------------
+
+
+
+MASTER SUMA RESTARTING SUMA
+=========================================
+INCL_NODEREQ
+<---------------------------------------------------------
+for each subscription...
+SUB_CREATE_REQ
+------------------------->...
+<-------------------------
+SUB_SYNC_REQ::MetaData
+------------------------->...
+<-------------------------
+... end for each subscription
+
+
+ SUMA_START_ME (sent asynchronously in start phase 5 to all suma's in node group)
+<-------------------------
+ ------------------------->
+
+for each subscriber...
+SUB_START_REQ (not before SUMA_START_ME)
+------------------------->
+<-------------------------
+... end for each subscriber
+
+SUMA_HANDOVER_REQ
+------------------------->
+ SUMA_HANDOVER_CONF (to all suma's in node group)
+<-------------------------
+ ------------------------->
diff --git a/storage/ndb/src/kernel/blocks/suma/SumaInit.cpp b/storage/ndb/src/kernel/blocks/suma/SumaInit.cpp
new file mode 100644
index 00000000000..b5945db3811
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/suma/SumaInit.cpp
@@ -0,0 +1,192 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Suma.hpp"
+
+#include <Properties.hpp>
+#include <Configuration.hpp>
+
+SumaParticipant::SumaParticipant(const Configuration & conf) :
+ SimulatedBlock(SUMA, conf),
+ c_metaSubscribers(c_subscriberPool),
+ c_dataSubscribers(c_subscriberPool),
+ c_prepDataSubscribers(c_subscriberPool),
+ c_removeDataSubscribers(c_subscriberPool),
+ c_tables(c_tablePool_),
+ c_subscriptions(c_subscriptionPool)
+{
+ BLOCK_CONSTRUCTOR(SumaParticipant);
+
+ /**
+ * SUMA participant if
+ */
+ addRecSignal(GSN_SUB_CREATE_REQ, &SumaParticipant::execSUB_CREATE_REQ);
+ addRecSignal(GSN_SUB_REMOVE_REQ, &SumaParticipant::execSUB_REMOVE_REQ);
+ addRecSignal(GSN_SUB_START_REQ, &SumaParticipant::execSUB_START_REQ);
+ addRecSignal(GSN_SUB_STOP_REQ, &SumaParticipant::execSUB_STOP_REQ);
+ addRecSignal(GSN_SUB_SYNC_REQ, &SumaParticipant::execSUB_SYNC_REQ);
+
+ addRecSignal(GSN_SUB_STOP_CONF, &SumaParticipant::execSUB_STOP_CONF);
+ addRecSignal(GSN_SUB_STOP_REF, &SumaParticipant::execSUB_STOP_REF);
+
+ /**
+ * Dict interface
+ */
+ //addRecSignal(GSN_LIST_TABLES_REF, &SumaParticipant::execLIST_TABLES_REF);
+ addRecSignal(GSN_LIST_TABLES_CONF, &SumaParticipant::execLIST_TABLES_CONF);
+ //addRecSignal(GSN_GET_TABINFOREF, &SumaParticipant::execGET_TABINFO_REF);
+ addRecSignal(GSN_GET_TABINFO_CONF, &SumaParticipant::execGET_TABINFO_CONF);
+ addRecSignal(GSN_GET_TABINFOREF, &SumaParticipant::execGET_TABINFOREF);
+#if 0
+ addRecSignal(GSN_GET_TABLEID_CONF, &SumaParticipant::execGET_TABLEID_CONF);
+ addRecSignal(GSN_GET_TABLEID_REF, &SumaParticipant::execGET_TABLEID_REF);
+#endif
+ /**
+ * Dih interface
+ */
+ //addRecSignal(GSN_DI_FCOUNTREF, &SumaParticipant::execDI_FCOUNTREF);
+ addRecSignal(GSN_DI_FCOUNTCONF, &SumaParticipant::execDI_FCOUNTCONF);
+ //addRecSignal(GSN_DIGETPRIMREF, &SumaParticipant::execDIGETPRIMREF);
+ addRecSignal(GSN_DIGETPRIMCONF, &SumaParticipant::execDIGETPRIMCONF);
+
+ /**
+ * Scan interface
+ */
+ addRecSignal(GSN_SCAN_HBREP, &SumaParticipant::execSCAN_HBREP);
+ addRecSignal(GSN_TRANSID_AI, &SumaParticipant::execTRANSID_AI);
+ addRecSignal(GSN_SCAN_FRAGREF, &SumaParticipant::execSCAN_FRAGREF);
+ addRecSignal(GSN_SCAN_FRAGCONF, &SumaParticipant::execSCAN_FRAGCONF);
+#if 0
+ addRecSignal(GSN_SUB_SYNC_CONTINUE_REF,
+ &SumaParticipant::execSUB_SYNC_CONTINUE_REF);
+#endif
+ addRecSignal(GSN_SUB_SYNC_CONTINUE_CONF,
+ &SumaParticipant::execSUB_SYNC_CONTINUE_CONF);
+
+ /**
+ * Trigger stuff
+ */
+ addRecSignal(GSN_TRIG_ATTRINFO, &SumaParticipant::execTRIG_ATTRINFO);
+ addRecSignal(GSN_FIRE_TRIG_ORD, &SumaParticipant::execFIRE_TRIG_ORD);
+
+ addRecSignal(GSN_CREATE_TRIG_REF, &Suma::execCREATE_TRIG_REF);
+ addRecSignal(GSN_CREATE_TRIG_CONF, &Suma::execCREATE_TRIG_CONF);
+ addRecSignal(GSN_DROP_TRIG_REF, &Suma::execDROP_TRIG_REF);
+ addRecSignal(GSN_DROP_TRIG_CONF, &Suma::execDROP_TRIG_CONF);
+
+ addRecSignal(GSN_SUB_GCP_COMPLETE_REP,
+ &SumaParticipant::execSUB_GCP_COMPLETE_REP);
+
+ /**
+ * @todo: fix pool sizes
+ */
+ Uint32 noTables;
+ const ndb_mgm_configuration_iterator * p = conf.getOwnConfigIterator();
+ ndbrequire(p != 0);
+
+ ndb_mgm_get_int_parameter(p, CFG_DB_NO_TABLES,
+ &noTables);
+
+ c_tablePool_.setSize(noTables);
+ c_tables.setSize(noTables);
+
+ c_subscriptions.setSize(20); //10
+ c_subscriberPool.setSize(64);
+
+ c_subscriptionPool.setSize(64); //2
+ c_syncPool.setSize(20); //2
+ c_dataBufferPool.setSize(128);
+
+ {
+ SLList<SyncRecord> tmp(c_syncPool);
+ Ptr<SyncRecord> ptr;
+ while(tmp.seize(ptr))
+ new (ptr.p) SyncRecord(* this, c_dataBufferPool);
+ tmp.release();
+ }
+
+ for( int i = 0; i < NO_OF_BUCKETS; i++) {
+ c_buckets[i].active = false;
+ c_buckets[i].handover = false;
+ c_buckets[i].handover_started = false;
+ c_buckets[i].handoverGCI = 0;
+ }
+ c_handoverToDo = false;
+ c_lastInconsistentGCI = RNIL;
+ c_lastCompleteGCI = RNIL;
+ c_nodeFailGCI = 0;
+
+ c_failedApiNodes.clear();
+}
+
+SumaParticipant::~SumaParticipant()
+{
+}
+
+Suma::Suma(const Configuration & conf) :
+ SumaParticipant(conf),
+ Restart(*this),
+ c_nodes(c_nodePool),
+ c_runningSubscriptions(c_subCoordinatorPool)
+{
+
+ c_nodePool.setSize(MAX_NDB_NODES);
+ c_masterNodeId = getOwnNodeId();
+
+ c_nodeGroup = c_noNodesInGroup = c_idInNodeGroup = 0;
+ for (int i = 0; i < MAX_REPLICAS; i++) {
+ c_nodesInGroup[i] = 0;
+ }
+
+ c_subCoordinatorPool.setSize(10);
+
+ // Add received signals
+ addRecSignal(GSN_STTOR, &Suma::execSTTOR);
+ addRecSignal(GSN_NDB_STTOR, &Suma::execNDB_STTOR);
+ addRecSignal(GSN_DUMP_STATE_ORD, &Suma::execDUMP_STATE_ORD);
+ addRecSignal(GSN_READ_NODESCONF, &Suma::execREAD_NODESCONF);
+ addRecSignal(GSN_API_FAILREQ, &Suma::execAPI_FAILREQ);
+ addRecSignal(GSN_NODE_FAILREP, &Suma::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Suma::execINCL_NODEREQ);
+ addRecSignal(GSN_CONTINUEB, &Suma::execCONTINUEB);
+ addRecSignal(GSN_SIGNAL_DROPPED_REP, &Suma::execSIGNAL_DROPPED_REP, true);
+ addRecSignal(GSN_UTIL_SEQUENCE_CONF, &Suma::execUTIL_SEQUENCE_CONF);
+ addRecSignal(GSN_UTIL_SEQUENCE_REF, &Suma::execUTIL_SEQUENCE_REF);
+ addRecSignal(GSN_CREATE_SUBID_REQ,
+ &Suma::execCREATE_SUBID_REQ);
+
+ addRecSignal(GSN_SUB_CREATE_CONF, &Suma::execSUB_CREATE_CONF);
+ addRecSignal(GSN_SUB_CREATE_REF, &Suma::execSUB_CREATE_REF);
+ addRecSignal(GSN_SUB_SYNC_CONF, &Suma::execSUB_SYNC_CONF);
+ addRecSignal(GSN_SUB_SYNC_REF, &Suma::execSUB_SYNC_REF);
+ addRecSignal(GSN_SUB_START_CONF, &Suma::execSUB_START_CONF);
+ addRecSignal(GSN_SUB_START_REF, &Suma::execSUB_START_REF);
+
+ addRecSignal(GSN_SUMA_START_ME, &Suma::execSUMA_START_ME);
+ addRecSignal(GSN_SUMA_HANDOVER_REQ, &Suma::execSUMA_HANDOVER_REQ);
+ addRecSignal(GSN_SUMA_HANDOVER_CONF, &Suma::execSUMA_HANDOVER_CONF);
+
+ addRecSignal(GSN_SUB_GCP_COMPLETE_ACC,
+ &Suma::execSUB_GCP_COMPLETE_ACC);
+}
+
+Suma::~Suma()
+{
+}
+
+BLOCK_FUNCTIONS(Suma)
+BLOCK_FUNCTIONS(SumaParticipant)
+
diff --git a/storage/ndb/src/kernel/blocks/trix/Makefile.am b/storage/ndb/src/kernel/blocks/trix/Makefile.am
new file mode 100644
index 00000000000..343063a6283
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/trix/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LIBRARIES = libtrix.a
+
+libtrix_a_SOURCES = Trix.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libtrix.dsp
+
+libtrix.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libtrix_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/blocks/trix/Trix.cpp b/storage/ndb/src/kernel/blocks/trix/Trix.cpp
new file mode 100644
index 00000000000..cd11cb4d575
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/trix/Trix.cpp
@@ -0,0 +1,967 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Trix.hpp"
+
+#include <string.h>
+#include <kernel_types.h>
+#include <NdbOut.hpp>
+
+#include <signaldata/ReadNodesConf.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/BuildIndx.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <signaldata/UtilPrepare.hpp>
+#include <signaldata/UtilExecute.hpp>
+#include <signaldata/UtilRelease.hpp>
+#include <SectionReader.hpp>
+#include <AttributeHeader.hpp>
+
+#define CONSTRAINT_VIOLATION 893
+
+#define DEBUG(x) { ndbout << "TRIX::" << x << endl; }
+
+/**
+ *
+ */
+Trix::Trix(const Configuration & conf) :
+ SimulatedBlock(TRIX, conf),
+ c_theNodes(c_theNodeRecPool),
+ c_masterNodeId(0),
+ c_masterTrixRef(0),
+ c_noNodesFailed(0),
+ c_noActiveNodes(0),
+ c_theSubscriptions(c_theSubscriptionRecPool)
+{
+ BLOCK_CONSTRUCTOR(Trix);
+
+ // Add received signals
+ addRecSignal(GSN_STTOR, &Trix::execSTTOR);
+ addRecSignal(GSN_NDB_STTOR, &Trix::execNDB_STTOR); // Forwarded from DICT
+ addRecSignal(GSN_READ_NODESCONF, &Trix::execREAD_NODESCONF);
+ addRecSignal(GSN_READ_NODESREF, &Trix::execREAD_NODESREF);
+ addRecSignal(GSN_NODE_FAILREP, &Trix::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Trix::execINCL_NODEREQ);
+ addRecSignal(GSN_DUMP_STATE_ORD, &Trix::execDUMP_STATE_ORD);
+
+ // Index build
+ addRecSignal(GSN_BUILDINDXREQ, &Trix::execBUILDINDXREQ);
+ // Dump testing
+ addRecSignal(GSN_BUILDINDXCONF, &Trix::execBUILDINDXCONF);
+ addRecSignal(GSN_BUILDINDXREF, &Trix::execBUILDINDXREF);
+
+
+ addRecSignal(GSN_UTIL_PREPARE_CONF, &Trix::execUTIL_PREPARE_CONF);
+ addRecSignal(GSN_UTIL_PREPARE_REF, &Trix::execUTIL_PREPARE_REF);
+ addRecSignal(GSN_UTIL_EXECUTE_CONF, &Trix::execUTIL_EXECUTE_CONF);
+ addRecSignal(GSN_UTIL_EXECUTE_REF, &Trix::execUTIL_EXECUTE_REF);
+ addRecSignal(GSN_UTIL_RELEASE_CONF, &Trix::execUTIL_RELEASE_CONF);
+ addRecSignal(GSN_UTIL_RELEASE_REF, &Trix::execUTIL_RELEASE_REF);
+
+
+ // Suma signals
+ addRecSignal(GSN_SUB_CREATE_CONF, &Trix::execSUB_CREATE_CONF);
+ addRecSignal(GSN_SUB_CREATE_REF, &Trix::execSUB_CREATE_REF);
+ addRecSignal(GSN_SUB_REMOVE_CONF, &Trix::execSUB_REMOVE_CONF);
+ addRecSignal(GSN_SUB_REMOVE_REF, &Trix::execSUB_REMOVE_REF);
+ addRecSignal(GSN_SUB_SYNC_CONF, &Trix::execSUB_SYNC_CONF);
+ addRecSignal(GSN_SUB_SYNC_REF, &Trix::execSUB_SYNC_REF);
+ addRecSignal(GSN_SUB_SYNC_CONTINUE_REQ, &Trix::execSUB_SYNC_CONTINUE_REQ);
+ addRecSignal(GSN_SUB_META_DATA, &Trix::execSUB_META_DATA);
+ addRecSignal(GSN_SUB_TABLE_DATA, &Trix::execSUB_TABLE_DATA);
+
+ // Allocate pool sizes
+ c_theAttrOrderBufferPool.setSize(100);
+ c_theSubscriptionRecPool.setSize(100);
+
+ ArrayList<SubscriptionRecord> subscriptions(c_theSubscriptionRecPool);
+ SubscriptionRecPtr subptr;
+ while(subscriptions.seize(subptr) == true) {
+ new (subptr.p) SubscriptionRecord(c_theAttrOrderBufferPool);
+ }
+ subscriptions.release();
+}
+
+/**
+ *
+ */
+Trix::~Trix()
+{
+}
+
+/**
+ *
+ */
+void Trix::execSTTOR(Signal* signal)
+{
+ jamEntry();
+
+ //const Uint32 startphase = signal->theData[1];
+ const Uint32 theSignalKey = signal->theData[6];
+
+ signal->theData[0] = theSignalKey;
+ signal->theData[3] = 1;
+ signal->theData[4] = 255; // No more start phases from missra
+ sendSignal(NDBCNTR_REF, GSN_STTORRY, signal, 5, JBB);
+ return;
+}//Trix::execSTTOR()
+
+/**
+ *
+ */
+void Trix::execNDB_STTOR(Signal* signal)
+{
+ jamEntry();
+ BlockReference ndbcntrRef = signal->theData[0];
+ Uint16 startphase = signal->theData[2]; /* RESTART PHASE */
+ Uint16 mynode = signal->theData[1];
+ //Uint16 restarttype = signal->theData[3];
+ //UintR configInfo1 = signal->theData[6]; /* CONFIGRATION INFO PART 1 */
+ //UintR configInfo2 = signal->theData[7]; /* CONFIGRATION INFO PART 2 */
+ switch (startphase) {
+ case 3:
+ jam();
+ /* SYMBOLIC START PHASE 4 */
+ /* ABSOLUTE PHASE 5 */
+ /* REQUEST NODE IDENTITIES FROM DBDIH */
+ signal->theData[0] = calcTrixBlockRef(mynode);
+ sendSignal(ndbcntrRef, GSN_READ_NODESREQ, signal, 1, JBB);
+ return;
+ break;
+ case 6:
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ *
+ */
+void Trix::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+
+ ReadNodesConf * const readNodes = (ReadNodesConf *)signal->getDataPtr();
+ //Uint32 noOfNodes = readNodes->noOfNodes;
+ NodeRecPtr nodeRecPtr;
+
+ c_masterNodeId = readNodes->masterNodeId;
+ c_masterTrixRef = RNIL;
+ c_noNodesFailed = 0;
+
+ for(unsigned i = 0; i < MAX_NDB_NODES; i++) {
+ jam();
+ if(NodeBitmask::get(readNodes->allNodes, i)) {
+ // Node is defined
+ jam();
+ ndbrequire(c_theNodes.seizeId(nodeRecPtr, i));
+ nodeRecPtr.p->trixRef = calcTrixBlockRef(i);
+ if (i == c_masterNodeId) {
+ c_masterTrixRef = nodeRecPtr.p->trixRef;
+ }
+ if(NodeBitmask::get(readNodes->inactiveNodes, i)){
+ // Node is not active
+ jam();
+ /**-----------------------------------------------------------------
+ * THIS NODE IS DEFINED IN THE CLUSTER BUT IS NOT ALIVE CURRENTLY.
+ * WE ADD THE NODE TO THE SET OF FAILED NODES AND ALSO SET THE
+ * BLOCKSTATE TO BUSY TO AVOID ADDING TRIGGERS OR INDEXES WHILE
+ * NOT ALL NODES ARE ALIVE.
+ *------------------------------------------------------------------*/
+ arrGuard(c_noNodesFailed, MAX_NDB_NODES);
+ nodeRecPtr.p->alive = false;
+ c_noNodesFailed++;
+ c_blockState = Trix::NODE_FAILURE;
+ }
+ else {
+ // Node is active
+ jam();
+ c_noActiveNodes++;
+ nodeRecPtr.p->alive = true;
+ }
+ }
+ }
+ if (c_noNodesFailed == 0) {
+ c_blockState = Trix::STARTED;
+ }
+}
+
+/**
+ *
+ */
+void Trix::execREAD_NODESREF(Signal* signal)
+{
+ // NYI
+}
+
+/**
+ *
+ */
+void Trix::execNODE_FAILREP(Signal* signal)
+{
+ jamEntry();
+ NodeFailRep * const nodeFail = (NodeFailRep *) signal->getDataPtr();
+
+ //Uint32 failureNr = nodeFail->failNo;
+ //Uint32 numberNodes = nodeFail->noOfNodes;
+ Uint32 masterNodeId = nodeFail->masterNodeId;
+
+ NodeRecPtr nodeRecPtr;
+
+ for(c_theNodes.first(nodeRecPtr);
+ nodeRecPtr.i != RNIL;
+ c_theNodes.next(nodeRecPtr)) {
+ if(NodeBitmask::get(nodeFail->theNodes, nodeRecPtr.i)) {
+ nodeRecPtr.p->alive = false;
+ c_noNodesFailed++;
+ c_noActiveNodes--;
+ }
+ }
+ if (c_masterNodeId != masterNodeId) {
+ c_masterNodeId = masterNodeId;
+ NodeRecord* nodeRec = c_theNodes.getPtr(masterNodeId);
+ c_masterTrixRef = nodeRec->trixRef;
+ }
+}
+
+/**
+ *
+ */
+void Trix::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+ UintR node_id = signal->theData[1];
+ NodeRecord* nodeRec = c_theNodes.getPtr(node_id);
+ nodeRec->alive = true;
+ c_noNodesFailed--;
+ c_noActiveNodes++;
+ nodeRec->trixRef = calcTrixBlockRef(node_id);
+ if (c_noNodesFailed == 0) {
+ c_blockState = Trix::STARTED;
+ }
+}
+
+// Debugging
+void
+Trix::execDUMP_STATE_ORD(Signal* signal)
+{
+ jamEntry();
+
+ DumpStateOrd * dumpStateOrd = (DumpStateOrd *)signal->getDataPtr();
+
+ switch(dumpStateOrd->args[0]) {
+ case(300): {// ok
+ // index2 -T; index2 -I -n10000; index2 -c
+ // all dump 300 0 0 0 0 0 4 2
+ // select_count INDEX0000
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtrSend();
+
+ MEMCOPY_NO_WORDS(buildIndxReq,
+ signal->theData + 1,
+ BuildIndxReq::SignalLength);
+ buildIndxReq->setUserRef(reference()); // return to me
+ buildIndxReq->setParallelism(10);
+ Uint32 indexColumns[1] = {1};
+ Uint32 keyColumns[1] = {0};
+ struct LinearSectionPtr orderPtr[2];
+ buildIndxReq->setColumnOrder(indexColumns, 1, keyColumns, 1, orderPtr);
+ sendSignal(reference(),
+ GSN_BUILDINDXREQ,
+ signal,
+ BuildIndxReq::SignalLength,
+ JBB,
+ orderPtr,
+ BuildIndxReq::NoOfSections);
+ break;
+ }
+ case(301): { // ok
+ // index2 -T; index2 -I -n10000; index2 -c -p
+ // all dump 301 0 0 0 0 0 4 2
+ // select_count INDEX0000
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtrSend();
+
+ MEMCOPY_NO_WORDS(buildIndxReq,
+ signal->theData + 1,
+ BuildIndxReq::SignalLength);
+ buildIndxReq->setUserRef(reference()); // return to me
+ buildIndxReq->setParallelism(10);
+ Uint32 indexColumns[2] = {0, 1};
+ Uint32 keyColumns[1] = {0};
+ struct LinearSectionPtr orderPtr[2];
+ buildIndxReq->setColumnOrder(indexColumns, 2, keyColumns, 1, orderPtr);
+ sendSignal(reference(),
+ GSN_BUILDINDXREQ,
+ signal,
+ BuildIndxReq::SignalLength,
+ JBB,
+ orderPtr,
+ BuildIndxReq::NoOfSections);
+ break;
+ }
+ case(302): { // ok
+ // index -T; index -I -n1000; index -c -p
+ // all dump 302 0 0 0 0 0 4 2
+ // select_count PNUMINDEX0000
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtrSend();
+
+ MEMCOPY_NO_WORDS(buildIndxReq,
+ signal->theData + 1,
+ BuildIndxReq::SignalLength);
+ buildIndxReq->setUserRef(reference()); // return to me
+ buildIndxReq->setParallelism(10);
+ Uint32 indexColumns[3] = {0, 3, 5};
+ Uint32 keyColumns[1] = {0};
+ struct LinearSectionPtr orderPtr[2];
+ buildIndxReq->setColumnOrder(indexColumns, 3, keyColumns, 1, orderPtr);
+ sendSignal(reference(),
+ GSN_BUILDINDXREQ,
+ signal,
+ BuildIndxReq::SignalLength,
+ JBB,
+ orderPtr,
+ BuildIndxReq::NoOfSections);
+ break;
+ }
+ case(303): { // ok
+ // index -T -2; index -I -2 -n1000; index -c -p
+ // all dump 303 0 0 0 0 0 4 2
+ // select_count PNUMINDEX0000
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtrSend();
+
+ MEMCOPY_NO_WORDS(buildIndxReq,
+ signal->theData + 1,
+ BuildIndxReq::SignalLength);
+ buildIndxReq->setUserRef(reference()); // return to me
+ buildIndxReq->setParallelism(10);
+ Uint32 indexColumns[3] = {0, 3, 5};
+ Uint32 keyColumns[2] = {0, 1};
+ struct LinearSectionPtr orderPtr[2];
+ buildIndxReq->setColumnOrder(indexColumns, 3, keyColumns, 2, orderPtr);
+ sendSignal(reference(),
+ GSN_BUILDINDXREQ,
+ signal,
+ BuildIndxReq::SignalLength,
+ JBB,
+ orderPtr,
+ BuildIndxReq::NoOfSections);
+ break;
+ }
+ case(304): { // ok
+ // index -T -L; index -I -L -n1000; index -c -p
+ // all dump 304 0 0 0 0 0 4 2
+ // select_count PNUMINDEX0000
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtrSend();
+
+ MEMCOPY_NO_WORDS(buildIndxReq,
+ signal->theData + 1,
+ BuildIndxReq::SignalLength);
+ buildIndxReq->setUserRef(reference()); // return to me
+ buildIndxReq->setParallelism(10);
+ Uint32 indexColumns[3] = {0, 3, 5};
+ Uint32 keyColumns[1] = {0};
+ struct LinearSectionPtr orderPtr[2];
+ buildIndxReq->setColumnOrder(indexColumns, 3, keyColumns, 1, orderPtr);
+ sendSignal(reference(),
+ GSN_BUILDINDXREQ,
+ signal,
+ BuildIndxReq::SignalLength,
+ JBB,
+ orderPtr,
+ BuildIndxReq::NoOfSections);
+ break;
+ }
+ case(305): { // ok
+ // index -T -2 -L; index -I -2 -L -n1000; index -c -p
+ // all dump 305 0 0 0 0 0 4 2
+ // select_count PNUMINDEX0000
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtrSend();
+
+ MEMCOPY_NO_WORDS(buildIndxReq,
+ signal->theData + 1,
+ BuildIndxReq::SignalLength);
+ buildIndxReq->setUserRef(reference()); // return to me
+ buildIndxReq->setParallelism(10);
+ Uint32 indexColumns[3] = {0, 3, 5};
+ Uint32 keyColumns[2] = {0, 1};
+ struct LinearSectionPtr orderPtr[2];
+ buildIndxReq->setColumnOrder(indexColumns, 3, keyColumns, 2, orderPtr);
+ sendSignal(reference(),
+ GSN_BUILDINDXREQ,
+ signal,
+ BuildIndxReq::SignalLength,
+ JBB,
+ orderPtr,
+ BuildIndxReq::NoOfSections);
+ break;
+ }
+ default: {
+ // Ignore
+ }
+ }
+}
+
+// Build index
+/**
+ *
+ */
+void Trix:: execBUILDINDXREQ(Signal* signal)
+{
+ jamEntry();
+ BuildIndxReq * buildIndxReq = (BuildIndxReq *)signal->getDataPtr();
+
+ // Seize a subscription record
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ if (!c_theSubscriptions.seizeId(subRecPtr, buildIndxReq->getBuildId())) {
+ // Failed to allocate subscription record
+ BuildIndxRef * buildIndxRef = (BuildIndxRef *)signal->getDataPtrSend();
+
+ buildIndxRef->setErrorCode(BuildIndxRef::AllocationFailure);
+ releaseSections(signal);
+ sendSignal(buildIndxReq->getUserRef(),
+ GSN_BUILDINDXREF, signal, BuildIndxRef::SignalLength, JBB);
+ return;
+ }
+ subRec = subRecPtr.p;
+ subRec->errorCode = BuildIndxRef::NoError;
+ subRec->userReference = buildIndxReq->getUserRef();
+ subRec->connectionPtr = buildIndxReq->getConnectionPtr();
+ subRec->subscriptionId = buildIndxReq->getBuildId();
+ subRec->subscriptionKey = buildIndxReq->getBuildKey();
+ subRec->indexType = buildIndxReq->getIndexType();
+ subRec->sourceTableId = buildIndxReq->getTableId();
+ subRec->targetTableId = buildIndxReq->getIndexId();
+ subRec->parallelism = buildIndxReq->getParallelism();
+ subRec->expectedConf = 0;
+ subRec->subscriptionCreated = false;
+ subRec->pendingSubSyncContinueConf = false;
+ subRec->prepareId = RNIL;
+
+ // Get column order segments
+ Uint32 noOfSections = signal->getNoOfSections();
+ if(noOfSections > 0) {
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, BuildIndxReq::INDEX_COLUMNS);
+ append(subRec->attributeOrder, ptr, getSectionSegmentPool());
+ subRec->noOfIndexColumns = ptr.sz;
+ }
+ if(noOfSections > 1) {
+ SegmentedSectionPtr ptr;
+ signal->getSection(ptr, BuildIndxReq::KEY_COLUMNS);
+ append(subRec->attributeOrder, ptr, getSectionSegmentPool());
+ subRec->noOfKeyColumns = ptr.sz;
+ }
+#if 0
+ // Debugging
+ printf("Trix:: execBUILDINDXREQ: Attribute order:\n");
+ subRec->attributeOrder.print(stdout);
+#endif
+ releaseSections(signal);
+ prepareInsertTransactions(signal, subRecPtr);
+}
+
+void Trix:: execBUILDINDXCONF(Signal* signal)
+{
+ printf("Trix:: execBUILDINDXCONF\n");
+}
+
+void Trix:: execBUILDINDXREF(Signal* signal)
+{
+ printf("Trix:: execBUILDINDXREF\n");
+}
+
+void Trix::execUTIL_PREPARE_CONF(Signal* signal)
+{
+ jamEntry();
+ UtilPrepareConf * utilPrepareConf = (UtilPrepareConf *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = utilPrepareConf->senderData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execUTIL_PREPARE_CONF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ subRec->prepareId = utilPrepareConf->prepareId;
+ setupSubscription(signal, subRecPtr);
+}
+
+void Trix::execUTIL_PREPARE_REF(Signal* signal)
+{
+ jamEntry();
+ UtilPrepareRef * utilPrepareRef = (UtilPrepareRef *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = utilPrepareRef->senderData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execUTIL_PREPARE_REF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ subRec->errorCode = BuildIndxRef::InternalError;
+}
+
+void Trix::execUTIL_EXECUTE_CONF(Signal* signal)
+{
+ jamEntry();
+ UtilExecuteConf * utilExecuteConf = (UtilExecuteConf *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = utilExecuteConf->senderData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("rix::execUTIL_EXECUTE_CONF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ subRec->expectedConf--;
+ checkParallelism(signal, subRec);
+ if (subRec->expectedConf == 0)
+ buildComplete(signal, subRecPtr);
+}
+
+void Trix::execUTIL_EXECUTE_REF(Signal* signal)
+{
+ jamEntry();
+ UtilExecuteRef * utilExecuteRef = (UtilExecuteRef *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = utilExecuteRef->senderData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execUTIL_EXECUTE_REF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ ndbrequire(utilExecuteRef->errorCode == UtilExecuteRef::TCError);
+ if(utilExecuteRef->TCErrorCode == CONSTRAINT_VIOLATION)
+ buildFailed(signal, subRecPtr, BuildIndxRef::IndexNotUnique);
+ else
+ buildFailed(signal, subRecPtr, BuildIndxRef::InternalError);
+}
+
+void Trix::execSUB_CREATE_CONF(Signal* signal)
+{
+ jamEntry();
+ SubCreateConf * subCreateConf = (SubCreateConf *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = subCreateConf->subscriberData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execSUB_CREATE_CONF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ ndbrequire(subRec->subscriptionId == subCreateConf->subscriptionId);
+ ndbrequire(subRec->subscriptionKey == subCreateConf->subscriptionKey);
+ subRec->subscriptionCreated = true;
+ subRecPtr.p = subRec;
+ setupTableScan(signal, subRecPtr);
+}
+
+void Trix::execSUB_CREATE_REF(Signal* signal)
+{
+ jamEntry();
+ // THIS SIGNAL IS NEVER SENT FROM SUMA?
+ /*
+ SubCreateRef * subCreateRef = (SubCreateRef *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = subCreateRef->subscriberData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execSUB_CREATE_REF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ buildFailed(signal, subRecPtr, BuildIndxRef::InternalError);
+ */
+}
+
+void Trix::execSUB_SYNC_CONF(Signal* signal)
+{
+ jamEntry();
+ SubSyncConf * subSyncConf = (SubSyncConf *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = subSyncConf->subscriberData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execSUB_SYNC_CONF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ ndbrequire(subRec->subscriptionId == subSyncConf->subscriptionId);
+ ndbrequire(subRec->subscriptionKey == subSyncConf->subscriptionKey);
+ subRecPtr.p = subRec;
+ if(subSyncConf->part == SubscriptionData::MetaData)
+ startTableScan(signal, subRecPtr);
+ else {
+ subRec->expectedConf--;
+ checkParallelism(signal, subRec);
+ if (subRec->expectedConf == 0)
+ buildComplete(signal, subRecPtr);
+ }
+}
+
+void Trix::execSUB_SYNC_REF(Signal* signal)
+{
+ jamEntry();
+ SubSyncRef * subSyncRef = (SubSyncRef *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+
+ subRecPtr.i = subSyncRef->subscriberData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execSUB_SYNC_REF: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ buildFailed(signal, subRecPtr, BuildIndxRef::InternalError);
+}
+
+void Trix::execSUB_SYNC_CONTINUE_REQ(Signal* signal)
+{
+ SubSyncContinueReq * subSyncContinueReq =
+ (SubSyncContinueReq *) signal->getDataPtr();
+
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+ subRecPtr.i = subSyncContinueReq->subscriberData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execSUB_SYNC_CONTINUE_REQ: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ subRec->pendingSubSyncContinueConf = true;
+ checkParallelism(signal, subRec);
+}
+
+void Trix::execSUB_META_DATA(Signal* signal)
+{
+ jamEntry();
+}
+
+void Trix::execSUB_TABLE_DATA(Signal* signal)
+{
+ jamEntry();
+ SubTableData * subTableData = (SubTableData *)signal->getDataPtr();
+ SubscriptionRecPtr subRecPtr;
+ SubscriptionRecord* subRec;
+ subRecPtr.i = subTableData->subscriberData;
+ if ((subRec = c_theSubscriptions.getPtr(subRecPtr.i)) == NULL) {
+ printf("Trix::execSUB_TABLE_DATA: Failed to find subscription data %u\n", subRecPtr.i);
+ return;
+ }
+ subRecPtr.p = subRec;
+ SegmentedSectionPtr headerPtr, dataPtr;
+ if (!signal->getSection(headerPtr, 0)) {
+ printf("Trix::execSUB_TABLE_DATA: Failed to get header section\n");
+ }
+ if (!signal->getSection(dataPtr, 1)) {
+ printf("Trix::execSUB_TABLE_DATA: Failed to get data section\n");
+ }
+ executeInsertTransaction(signal, subRecPtr, headerPtr, dataPtr);
+}
+
+void Trix::setupSubscription(Signal* signal, SubscriptionRecPtr subRecPtr)
+{
+ Uint32 attributeList[MAX_ATTRIBUTES_IN_TABLE * 2];
+ SubCreateReq * subCreateReq = (SubCreateReq *)signal->getDataPtrSend();
+ SubscriptionRecord* subRec = subRecPtr.p;
+// Uint32 listLen = subRec->noOfIndexColumns + subRec->noOfKeyColumns;
+ AttrOrderBuffer::DataBufferIterator iter;
+ Uint32 i = 0;
+
+ jam();
+ bool moreAttributes = subRec->attributeOrder.first(iter);
+ while (moreAttributes) {
+ attributeList[i++] = *iter.data;
+ moreAttributes = subRec->attributeOrder.next(iter);
+ }
+ // Merge index and key column segments
+ struct LinearSectionPtr orderPtr[3];
+ orderPtr[0].p = attributeList;
+ orderPtr[0].sz = subRec->attributeOrder.getSize();
+
+
+ subCreateReq->subscriberRef = reference();
+ subCreateReq->subscriberData = subRecPtr.i;
+ subCreateReq->subscriptionId = subRec->subscriptionId;
+ subCreateReq->subscriptionKey = subRec->subscriptionKey;
+ subCreateReq->tableId = subRec->sourceTableId;
+ subCreateReq->subscriptionType = SubCreateReq::SingleTableScan;
+
+ sendSignal(SUMA_REF, GSN_SUB_CREATE_REQ,
+ signal, SubCreateReq::SignalLength+1, JBB, orderPtr, 1);
+}
+
+void Trix::setupTableScan(Signal* signal, SubscriptionRecPtr subRecPtr)
+{
+ SubSyncReq * subSyncReq = (SubSyncReq *)signal->getDataPtrSend();
+
+ jam();
+ subSyncReq->subscriptionId = subRecPtr.i;
+ subSyncReq->subscriptionKey = subRecPtr.p->subscriptionKey;
+ subSyncReq->part = SubscriptionData::MetaData;
+ sendSignal(SUMA_REF, GSN_SUB_SYNC_REQ,
+ signal, SubSyncReq::SignalLength, JBB);
+}
+
+void Trix::startTableScan(Signal* signal, SubscriptionRecPtr subRecPtr)
+{
+ jam();
+ subRecPtr.p->expectedConf = 1;
+ SubSyncReq * subSyncReq = (SubSyncReq *)signal->getDataPtrSend();
+
+ subSyncReq->subscriptionId = subRecPtr.i;
+ subSyncReq->subscriptionKey = subRecPtr.p->subscriptionKey;
+ subSyncReq->part = SubscriptionData::TableData;
+ sendSignal(SUMA_REF, GSN_SUB_SYNC_REQ,
+ signal, SubSyncReq::SignalLength, JBB);
+}
+
+void Trix::prepareInsertTransactions(Signal* signal,
+ SubscriptionRecPtr subRecPtr)
+{
+ SubscriptionRecord* subRec = subRecPtr.p;
+ UtilPrepareReq * utilPrepareReq =
+ (UtilPrepareReq *)signal->getDataPtrSend();
+
+ jam();
+ utilPrepareReq->senderRef = reference();
+ utilPrepareReq->senderData = subRecPtr.i;
+
+ const Uint32 pageSizeInWords = 128;
+ Uint32 propPage[pageSizeInWords];
+ LinearWriter w(&propPage[0],128);
+ w.first();
+ w.add(UtilPrepareReq::NoOfOperations, 1);
+ w.add(UtilPrepareReq::OperationType, UtilPrepareReq::Write);
+ w.add(UtilPrepareReq::TableId, subRec->targetTableId);
+ // Add index attributes in increasing order and one PK attribute
+ for(Uint32 i = 0; i < subRec->noOfIndexColumns + 1; i++)
+ w.add(UtilPrepareReq::AttributeId, i);
+
+#if 0
+ // Debugging
+ SimplePropertiesLinearReader reader(propPage, w.getWordsUsed());
+ printf("Trix::prepareInsertTransactions: Sent SimpleProperties:\n");
+ reader.printAll(ndbout);
+#endif
+
+ struct LinearSectionPtr sectionsPtr[UtilPrepareReq::NoOfSections];
+ sectionsPtr[UtilPrepareReq::PROPERTIES_SECTION].p = propPage;
+ sectionsPtr[UtilPrepareReq::PROPERTIES_SECTION].sz = w.getWordsUsed();
+ sendSignal(DBUTIL_REF, GSN_UTIL_PREPARE_REQ, signal,
+ UtilPrepareReq::SignalLength, JBB,
+ sectionsPtr, UtilPrepareReq::NoOfSections);
+}
+
+void Trix::executeInsertTransaction(Signal* signal,
+ SubscriptionRecPtr subRecPtr,
+ SegmentedSectionPtr headerPtr,
+ SegmentedSectionPtr dataPtr)
+{
+ jam();
+ SubscriptionRecord* subRec = subRecPtr.p;
+ UtilExecuteReq * utilExecuteReq =
+ (UtilExecuteReq *)signal->getDataPtrSend();
+ Uint32* headerBuffer = signal->theData + 25;
+ Uint32* dataBuffer = headerBuffer + headerPtr.sz;
+
+ utilExecuteReq->senderRef = reference();
+ utilExecuteReq->senderData = subRecPtr.i;
+ utilExecuteReq->prepareId = subRec->prepareId;
+#if 0
+ printf("Header size %u\n", headerPtr.sz);
+ for(int i = 0; i < headerPtr.sz; i++)
+ printf("H'%.8x ", headerBuffer[i]);
+ printf("\n");
+
+ printf("Data size %u\n", dataPtr.sz);
+ for(int i = 0; i < dataPtr.sz; i++)
+ printf("H'%.8x ", dataBuffer[i]);
+ printf("\n");
+#endif
+ // Save scan result in linear buffers
+ copy(headerBuffer, headerPtr);
+ copy(dataBuffer, dataPtr);
+
+ // Calculate packed key size
+ Uint32 noOfKeyData = 0;
+ for(Uint32 i = 0; i < headerPtr.sz; i++) {
+ AttributeHeader* keyAttrHead = (AttributeHeader *) headerBuffer + i;
+
+ // Filter out NULL attributes
+ if (keyAttrHead->isNULL())
+ return;
+
+ if (i < subRec->noOfIndexColumns)
+ // Renumber index attributes in consequtive order
+ keyAttrHead->setAttributeId(i);
+ else
+ // Calculate total size of PK attribute
+ noOfKeyData += keyAttrHead->getDataSize();
+ }
+ // Increase expected CONF count
+ subRec->expectedConf++;
+
+ // Pack key attributes
+ AttributeHeader::init(headerBuffer + subRec->noOfIndexColumns,
+ subRec->noOfIndexColumns,
+ noOfKeyData);
+
+ struct LinearSectionPtr sectionsPtr[UtilExecuteReq::NoOfSections];
+ sectionsPtr[UtilExecuteReq::HEADER_SECTION].p = headerBuffer;
+ sectionsPtr[UtilExecuteReq::HEADER_SECTION].sz =
+ subRec->noOfIndexColumns + 1;
+ sectionsPtr[UtilExecuteReq::DATA_SECTION].p = dataBuffer;
+ sectionsPtr[UtilExecuteReq::DATA_SECTION].sz = dataPtr.sz;
+ sendSignal(DBUTIL_REF, GSN_UTIL_EXECUTE_REQ, signal,
+ UtilExecuteReq::SignalLength, JBB,
+ sectionsPtr, UtilExecuteReq::NoOfSections);
+}
+
+void Trix::buildComplete(Signal* signal, SubscriptionRecPtr subRecPtr)
+{
+ SubRemoveReq * const req = (SubRemoveReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = subRecPtr.i;
+ req->subscriptionId = subRecPtr.p->subscriptionId;
+ req->subscriptionKey = subRecPtr.p->subscriptionKey;
+ sendSignal(SUMA_REF, GSN_SUB_REMOVE_REQ, signal,
+ SubRemoveReq::SignalLength, JBB);
+}
+
+void Trix::buildFailed(Signal* signal,
+ SubscriptionRecPtr subRecPtr,
+ BuildIndxRef::ErrorCode errorCode)
+{
+ SubscriptionRecord* subRec = subRecPtr.p;
+
+ subRec->errorCode = errorCode;
+ // Continue accumulating since we currently cannot stop SUMA
+ subRec->expectedConf--;
+ checkParallelism(signal, subRec);
+ if (subRec->expectedConf == 0)
+ buildComplete(signal, subRecPtr);
+}
+
+void
+Trix::execSUB_REMOVE_REF(Signal* signal){
+ jamEntry();
+ //@todo
+ ndbrequire(false);
+}
+
+void
+Trix::execSUB_REMOVE_CONF(Signal* signal){
+ jamEntry();
+
+ SubRemoveConf * const conf = (SubRemoveConf*)signal->getDataPtrSend();
+
+ SubscriptionRecPtr subRecPtr;
+ c_theSubscriptions.getPtr(subRecPtr, conf->senderData);
+
+ if(subRecPtr.p->prepareId != RNIL){
+ jam();
+
+ UtilReleaseReq * const req = (UtilReleaseReq*)signal->getDataPtrSend();
+ req->prepareId = subRecPtr.p->prepareId;
+ req->senderData = subRecPtr.i;
+
+ sendSignal(DBUTIL_REF, GSN_UTIL_RELEASE_REQ, signal,
+ UtilReleaseReq::SignalLength , JBB);
+ return;
+ }
+
+ {
+ UtilReleaseConf * const conf = (UtilReleaseConf*)signal->getDataPtrSend();
+ conf->senderData = subRecPtr.i;
+ execUTIL_RELEASE_CONF(signal);
+ }
+}
+
+void
+Trix::execUTIL_RELEASE_REF(Signal* signal){
+ jamEntry();
+ ndbrequire(false);
+}
+
+void
+Trix::execUTIL_RELEASE_CONF(Signal* signal){
+
+ UtilReleaseConf * const conf = (UtilReleaseConf*)signal->getDataPtrSend();
+
+ SubscriptionRecPtr subRecPtr;
+ c_theSubscriptions.getPtr(subRecPtr, conf->senderData);
+
+ if(subRecPtr.p->errorCode == BuildIndxRef::NoError){
+ // Build is complete, reply to original sender
+ BuildIndxConf * buildIndxConf = (BuildIndxConf *)signal->getDataPtrSend();
+ buildIndxConf->setUserRef(subRecPtr.p->userReference);
+ buildIndxConf->setConnectionPtr(subRecPtr.p->connectionPtr);
+ buildIndxConf->setRequestType(BuildIndxReq::RT_TRIX);
+ buildIndxConf->setIndexType(subRecPtr.p->indexType);
+ buildIndxConf->setTableId(subRecPtr.p->sourceTableId);
+ buildIndxConf->setIndexId(subRecPtr.p->targetTableId);
+
+ sendSignal(subRecPtr.p->userReference, GSN_BUILDINDXCONF, signal,
+ BuildIndxConf::SignalLength , JBB);
+ } else {
+ // Build failed, reply to original sender
+ BuildIndxRef * buildIndxRef = (BuildIndxRef *)signal->getDataPtrSend();
+ buildIndxRef->setUserRef(subRecPtr.p->userReference);
+ buildIndxRef->setConnectionPtr(subRecPtr.p->connectionPtr);
+ buildIndxRef->setRequestType(BuildIndxReq::RT_TRIX);
+ buildIndxRef->setIndexType(subRecPtr.p->indexType);
+ buildIndxRef->setTableId(subRecPtr.p->sourceTableId);
+ buildIndxRef->setIndexId(subRecPtr.p->targetTableId);
+ buildIndxRef->setErrorCode(subRecPtr.p->errorCode);
+
+ sendSignal(subRecPtr.p->userReference, GSN_BUILDINDXREF, signal,
+ BuildIndxRef::SignalLength , JBB);
+ }
+
+ // Release subscription record
+ subRecPtr.p->attributeOrder.release();
+ c_theSubscriptions.release(subRecPtr.i);
+}
+
+void Trix::checkParallelism(Signal* signal, SubscriptionRecord* subRec)
+{
+ if ((subRec->pendingSubSyncContinueConf) &&
+ (subRec->expectedConf < subRec->parallelism)) {
+ SubSyncContinueConf * subSyncContinueConf =
+ (SubSyncContinueConf *) signal->getDataPtrSend();
+ subSyncContinueConf->subscriptionId = subRec->subscriptionId;
+ subSyncContinueConf->subscriptionKey = subRec->subscriptionKey;
+ sendSignal(SUMA_REF, GSN_SUB_SYNC_CONTINUE_CONF, signal,
+ SubSyncContinueConf::SignalLength , JBB);
+ subRec->pendingSubSyncContinueConf = false;
+ }
+}
+
+BLOCK_FUNCTIONS(Trix)
+
+template void append(DataBuffer<15>&,SegmentedSectionPtr,SectionSegmentPool&);
diff --git a/storage/ndb/src/kernel/blocks/trix/Trix.hpp b/storage/ndb/src/kernel/blocks/trix/Trix.hpp
new file mode 100644
index 00000000000..8dc01375fa1
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/trix/Trix.hpp
@@ -0,0 +1,191 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TRIX_H
+#define TRIX_H
+
+#include <SimulatedBlock.hpp>
+#include <trigger_definitions.h>
+#include <DataBuffer.hpp>
+#include <ArrayList.hpp>
+#include <SimpleProperties.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/CreateTrig.hpp>
+#include <signaldata/BuildIndx.hpp>
+
+// Error codes
+#define INTERNAL_ERROR_ILLEGAL_CALL 4344
+#define INTERNAL_ERROR_TRIX_BUSY 4345
+
+/**
+ * TRIX - This block manages triggers and index (in coop with DICT)
+ */
+class Trix : public SimulatedBlock
+{
+public:
+ Trix(const class Configuration & conf);
+ virtual ~Trix();
+
+public:
+ // Subscription data, when communicating with SUMA
+
+ enum RequestType {
+ TABLE_REORG = 0,
+ INDEX_BUILD = 1
+ };
+ typedef DataBuffer<11> AttrOrderBuffer;
+
+private:
+ // Private attributes
+
+ BLOCK_DEFINES(Trix);
+
+ // Declared but not defined
+ //DBtrix(const Trix &obj);
+ //void operator = (const Trix &);
+
+ // Block state
+ enum BlockState {
+ NOT_STARTED,
+ STARTED,
+ NODE_FAILURE,
+ IDLE,
+ BUSY
+ };
+
+ BlockState c_blockState;
+
+ // Node data needed when communicating with remote TRIX:es
+ struct NodeRecord {
+ bool alive;
+ BlockReference trixRef;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+
+ typedef Ptr<NodeRecord> NodeRecPtr;
+
+ /**
+ * The pool of node records
+ */
+ ArrayPool<NodeRecord> c_theNodeRecPool;
+
+ /**
+ * The list of other NDB nodes
+ */
+ ArrayList<NodeRecord> c_theNodes;
+
+ Uint32 c_masterNodeId;
+ BlockReference c_masterTrixRef;
+ Uint16 c_noNodesFailed;
+ Uint16 c_noActiveNodes;
+
+ AttrOrderBuffer::DataBufferPool c_theAttrOrderBufferPool;
+
+ struct SubscriptionRecord {
+ SubscriptionRecord(AttrOrderBuffer::DataBufferPool & aop):
+ attributeOrder(aop)
+ {}
+ RequestType requestType;
+ BlockReference userReference; // For user
+ Uint32 connectionPtr; // For user
+ Uint32 subscriptionId; // For Suma
+ Uint32 subscriptionKey; // For Suma
+ Uint32 prepareId; // For DbUtil
+ Uint32 indexType;
+ Uint32 sourceTableId;
+ Uint32 targetTableId;
+ AttrOrderBuffer attributeOrder;
+ Uint32 noOfIndexColumns;
+ Uint32 noOfKeyColumns;
+ Uint32 parallelism;
+ BuildIndxRef::ErrorCode errorCode;
+ bool subscriptionCreated;
+ bool pendingSubSyncContinueConf;
+ Uint32 expectedConf; // Count in n UTIL_EXECUTE_CONF + 1 SUB_SYNC_CONF
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+
+ typedef Ptr<SubscriptionRecord> SubscriptionRecPtr;
+
+ /**
+ * The pool of node records
+ */
+ ArrayPool<SubscriptionRecord> c_theSubscriptionRecPool;
+
+ /**
+ * The list of other subscriptions
+ */
+ ArrayList<SubscriptionRecord> c_theSubscriptions;
+
+ // System start
+ void execSTTOR(Signal* signal);
+ void execNDB_STTOR(Signal* signal);
+
+ // Node management
+ void execREAD_NODESCONF(Signal* signal);
+ void execREAD_NODESREF(Signal* signal);
+ void execNODE_FAILREP(Signal* signal);
+ void execINCL_NODEREQ(Signal* signal);
+ // Debugging
+ void execDUMP_STATE_ORD(Signal* signal);
+
+ // Build index
+ void execBUILDINDXREQ(Signal* signal);
+ void execBUILDINDXCONF(Signal* signal);
+ void execBUILDINDXREF(Signal* signal);
+
+ void execUTIL_PREPARE_CONF(Signal* signal);
+ void execUTIL_PREPARE_REF(Signal* signal);
+ void execUTIL_EXECUTE_CONF(Signal* signal);
+ void execUTIL_EXECUTE_REF(Signal* signal);
+ void execUTIL_RELEASE_CONF(Signal* signal);
+ void execUTIL_RELEASE_REF(Signal* signal);
+
+ // Suma signals
+ void execSUB_CREATE_CONF(Signal* signal);
+ void execSUB_CREATE_REF(Signal* signal);
+ void execSUB_REMOVE_CONF(Signal* signal);
+ void execSUB_REMOVE_REF(Signal* signal);
+ void execSUB_SYNC_CONF(Signal* signal);
+ void execSUB_SYNC_REF(Signal* signal);
+ void execSUB_SYNC_CONTINUE_REQ(Signal* signal);
+ void execSUB_META_DATA(Signal* signal);
+ void execSUB_TABLE_DATA(Signal* signal);
+
+ // Utility functions
+ void setupSubscription(Signal* signal, SubscriptionRecPtr subRecPtr);
+ void setupTableScan(Signal* signal, SubscriptionRecPtr subRecPtr);
+ void startTableScan(Signal* signal, SubscriptionRecPtr subRecPtr);
+ void prepareInsertTransactions(Signal* signal, SubscriptionRecPtr subRecPtr);
+ void executeInsertTransaction(Signal* signal, SubscriptionRecPtr subRecPtr,
+ SegmentedSectionPtr headerPtr,
+ SegmentedSectionPtr dataPtr);
+ void buildComplete(Signal* signal, SubscriptionRecPtr subRecPtr);
+ void buildFailed(Signal* signal,
+ SubscriptionRecPtr subRecPtr,
+ BuildIndxRef::ErrorCode);
+ void checkParallelism(Signal* signal, SubscriptionRecord* subRec);
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/error/Error.hpp b/storage/ndb/src/kernel/error/Error.hpp
new file mode 100644
index 00000000000..e19d6782793
--- /dev/null
+++ b/storage/ndb/src/kernel/error/Error.hpp
@@ -0,0 +1,85 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ERROR_H
+#define ERROR_H
+
+/**
+ * Errorcodes for NDB
+ *
+ * These errorcodes should be used whenever a condition
+ * is detected where it's necesssary to shutdown NDB.
+ *
+ * Example: When another node fails while a NDB node are performing
+ * a system restart the node should be shutdown. This
+ * is kind of an error but the cause of the error is known
+ * and a proper errormessage describing the problem should
+ * be printed in error.log. It's therefore important to use
+ * the proper errorcode.
+ *
+ * TODO: In the future the errorcodes should be classified
+ *
+ */
+
+enum ErrorCategory
+{
+ warning,
+ ecError,
+ fatal,
+ assert
+};
+
+const int ERR_BASE = 1000;
+
+// Errorcodes for all blocks except filseystem
+const int ERR_ERR_BASE = ERR_BASE + 1300;
+const int ERR_ERROR_PRGERR = ERR_ERR_BASE+1;
+const int ERR_NODE_NOT_IN_CONFIG = ERR_ERR_BASE+2;
+const int ERR_SYSTEM_ERROR = ERR_ERR_BASE+3;
+const int ERR_INDEX_NOTINRANGE = ERR_ERR_BASE+4;
+const int ERR_ARBIT_SHUTDOWN = ERR_ERR_BASE+5;
+const int ERR_POINTER_NOTINRANGE = ERR_ERR_BASE+6;
+const int ERR_PROGRAMERROR = ERR_ERR_BASE+7;
+const int ERR_SR_OTHERNODEFAILED = ERR_ERR_BASE+8;
+const int ERR_NODE_NOT_DEAD = ERR_ERR_BASE+9;
+const int ERR_SR_REDOLOG = ERR_ERR_BASE+10;
+const int ERR_SR_RESTARTCONFLICT = ERR_ERR_BASE+11;
+const int ERR_NO_MORE_UNDOLOG = ERR_ERR_BASE+12;
+const int ERR_SR_UNDOLOG = ERR_ERR_BASE+13;
+const int ERR_MEMALLOC = ERR_ERR_BASE+27;
+const int BLOCK_ERROR_JBUFCONGESTION = ERR_ERR_BASE+34;
+const int ERROR_TIME_QUEUE_SHORT = ERR_ERR_BASE+35;
+const int ERROR_TIME_QUEUE_LONG = ERR_ERR_BASE+36;
+const int ERROR_TIME_QUEUE_DELAY = ERR_ERR_BASE+37;
+const int ERROR_TIME_QUEUE_INDEX = ERR_ERR_BASE+38;
+const int BLOCK_ERROR_BNR_ZERO = ERR_ERR_BASE+39;
+const int ERROR_WRONG_PRIO_LEVEL = ERR_ERR_BASE+40;
+const int ERR_NDBREQUIRE = ERR_ERR_BASE+41;
+const int ERR_ERROR_INSERT = ERR_ERR_BASE+42;
+const int ERR_INVALID_CONFIG = ERR_ERR_BASE+50;
+const int ERR_OUT_OF_LONG_SIGNAL_MEMORY = ERR_ERR_BASE+51;
+
+// Errorcodes for NDB filesystem
+const int AFS_ERR_BASE = ERR_BASE + 1800;
+const int AFS_ERROR_NOPATH = AFS_ERR_BASE+1;
+const int AFS_ERROR_CHANNALFULL = AFS_ERR_BASE+2;
+const int AFS_ERROR_NOMORETHREADS = AFS_ERR_BASE+3;
+const int AFS_ERROR_PARAMETER = AFS_ERR_BASE+4;
+const int AFS_ERROR_INVALIDPATH = AFS_ERR_BASE+5;
+const int AFS_ERROR_MAXOPEN = AFS_ERR_BASE+6;
+const int AFS_ERROR_ALLREADY_OPEN = AFS_ERR_BASE+7;
+
+#endif // ERROR_H
diff --git a/storage/ndb/src/kernel/error/ErrorHandlingMacros.hpp b/storage/ndb/src/kernel/error/ErrorHandlingMacros.hpp
new file mode 100644
index 00000000000..d8bb7ff759b
--- /dev/null
+++ b/storage/ndb/src/kernel/error/ErrorHandlingMacros.hpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ERRORHANDLINGMACROS_H
+#define ERRORHANDLINGMACROS_H
+
+#include "ErrorReporter.hpp"
+#include "Error.hpp"
+
+extern const char programName[];
+
+#define ERROR_SET_SIGNAL(messageCategory, messageID, problemData, objectRef) \
+ ErrorReporter::handleError(messageCategory, messageID, problemData, objectRef, NST_ErrorHandlerSignal)
+#define ERROR_SET(messageCategory, messageID, problemData, objectRef) \
+ ErrorReporter::handleError(messageCategory, messageID, problemData, objectRef)
+ // Description:
+ // Call ErrorHandler with the supplied arguments. The
+ // ErrorHandler decides how to report the error.
+ // Parameters:
+ // messageCategory IN A hint to the error handler how the
+ // error should be reported. Can be
+ // error, fatal (or warning, use WARNING_SET instead).
+ // messageID IN Code identifying the error. If less
+ // than 1000 a unix error is assumed. If
+ // greater than 1000 the code is treated
+ // as the specific problem code.
+ // problemData IN A (short) text describing the error.
+ // The context information is added to
+ // this text.
+ // objectRef IN The name of the "victim" of the error.
+ // Specify NULL if not applicable.
+ // Return value:
+ // -
+ // Reported errors:
+ // -
+ // Additional information:
+ // -
+
+#endif
diff --git a/storage/ndb/src/kernel/error/ErrorMessages.cpp b/storage/ndb/src/kernel/error/ErrorMessages.cpp
new file mode 100644
index 00000000000..059aa4af61c
--- /dev/null
+++ b/storage/ndb/src/kernel/error/ErrorMessages.cpp
@@ -0,0 +1,75 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ErrorMessages.hpp"
+
+struct ErrStruct {
+ int fauldId;
+ const char* text;
+};
+
+const ErrStruct errArray[] = {
+
+ {2301, "Assertion, probably a programming error"},
+ {2302, "Own Node Id not a NDB node, configuration error"},
+ {2303, "System error"},
+ {2304, "Index too large"},
+ {2305, "Arbitrator shutdown"},
+ {2306, "Pointer too large"},
+ {2307, "Internal program error"},
+ {2308, "Node failed during system restart"},
+ {2309, "Node state conflict"},
+ {2310, "Error while reading the REDO log"},
+ {2311, "Conflict when selecting restart type"},
+ {2312, "No more free UNDO log"},
+ {2313, "Error while reading the datapages and UNDO log"},
+ {2327, "Memory allocation failure"},
+ {2334, "Job buffer congestion"},
+ {2335, "Error in short time queue"},
+ {2336, "Error in long time queue"},
+ {2337, "Error in time queue, too long delay"},
+ {2338, "Time queue index out of range"},
+ {2339, "Send signal error"},
+ {2340, "Wrong prio level when sending signal"},
+ {2341, "Internal program error (failed ndbrequire)"},
+ {2342, "Error insert executed" },
+ {2350, "Invalid Configuration fetched from Management Server" },
+
+ // Ndbfs error messages
+ {2801, "No file system path"},
+ {2802, "Channel is full"},
+ {2803, "No more threads"},
+ {2804, "Bad parameter"},
+ {2805, "Illegal file system path"},
+ {2806, "Max number of open files exceeded"},
+ {2807, "File has already been opened"},
+
+ // Sentinel
+ {0, "No message slogan found"}
+
+};
+
+const unsigned short NO_OF_ERROR_MESSAGES = sizeof(errArray)/sizeof(ErrStruct);
+
+const char* lookupErrorMessage(int faultId)
+{
+ int i = 0;
+ while (errArray[i].fauldId != faultId && errArray[i].fauldId != 0)
+ i++;
+ return errArray[i].text;
+}
+
+
diff --git a/storage/ndb/src/kernel/error/ErrorMessages.hpp b/storage/ndb/src/kernel/error/ErrorMessages.hpp
new file mode 100644
index 00000000000..38c8eec636b
--- /dev/null
+++ b/storage/ndb/src/kernel/error/ErrorMessages.hpp
@@ -0,0 +1,22 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ERROR_MESSAGES_H
+#define ERROR_MESSAGES_H
+
+const char* lookupErrorMessage(int faultId);
+
+#endif
diff --git a/storage/ndb/src/kernel/error/ErrorReporter.cpp b/storage/ndb/src/kernel/error/ErrorReporter.cpp
new file mode 100644
index 00000000000..35cd3f099d9
--- /dev/null
+++ b/storage/ndb/src/kernel/error/ErrorReporter.cpp
@@ -0,0 +1,393 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "Error.hpp"
+#include "ErrorReporter.hpp"
+#include "ErrorMessages.hpp"
+
+#include <FastScheduler.hpp>
+#include <DebuggerNames.hpp>
+#include <NdbHost.h>
+#include <NdbConfig.h>
+#include <Configuration.hpp>
+
+#include <NdbAutoPtr.hpp>
+
+#define MESSAGE_LENGTH 400
+
+const char* errorType[] = {
+ "warning",
+ "error",
+ "fatal",
+ "assert"
+};
+
+
+static int WriteMessage(ErrorCategory thrdType, int thrdMessageID,
+ const char* thrdProblemData,
+ const char* thrdObjRef,
+ Uint32 thrdTheEmulatedJamIndex,
+ Uint8 thrdTheEmulatedJam[]);
+
+static void dumpJam(FILE* jamStream,
+ Uint32 thrdTheEmulatedJamIndex,
+ Uint8 thrdTheEmulatedJam[]);
+
+
+const char*
+ErrorReporter::formatTimeStampString(){
+ TimeModule DateTime; /* To create "theDateTimeString" */
+
+ static char theDateTimeString[39];
+ /* Used to store the generated timestamp */
+ /* ex: "Wednesday 18 September 2000 - 18:54:37" */
+
+ DateTime.setTimeStamp();
+
+ BaseString::snprintf(theDateTimeString, 39, "%s %d %s %d - %s:%s:%s",
+ DateTime.getDayName(), DateTime.getDayOfMonth(),
+ DateTime.getMonthName(), DateTime.getYear(), DateTime.getHour(),
+ DateTime.getMinute(), DateTime.getSecond());
+
+ return (const char *)&theDateTimeString;
+}
+
+int
+ErrorReporter::get_trace_no(){
+
+ FILE *stream;
+ unsigned int traceFileNo;
+
+ char *file_name= NdbConfig_NextTraceFileName(globalData.ownId);
+ NdbAutoPtr<char> tmp_aptr(file_name);
+
+ /*
+ * Read last number from tracefile
+ */
+ stream = fopen(file_name, "r+");
+ if (stream == NULL){
+ traceFileNo = 1;
+ } else {
+ char buf[255];
+ fgets(buf, 255, stream);
+ const int scan = sscanf(buf, "%u", &traceFileNo);
+ if(scan != 1){
+ traceFileNo = 1;
+ }
+ fclose(stream);
+ traceFileNo++;
+ }
+
+ /**
+ * Wrap tracefile no
+ */
+ Uint32 tmp = globalEmulatorData.theConfiguration->maxNoOfErrorLogs();
+ if (traceFileNo > tmp ) {
+ traceFileNo = 1;
+ }
+
+ /**
+ * Save new number to the file
+ */
+ stream = fopen(file_name, "w");
+ if(stream != NULL){
+ fprintf(stream, "%u", traceFileNo);
+ fclose(stream);
+ }
+
+ return traceFileNo;
+}
+
+
+void
+ErrorReporter::formatMessage(ErrorCategory type,
+ int faultID,
+ const char* problemData,
+ const char* objRef,
+ const char* theNameOfTheTraceFile,
+ char* messptr){
+ int processId;
+
+ processId = NdbHost_GetProcessId();
+
+ BaseString::snprintf(messptr, MESSAGE_LENGTH,
+ "Date/Time: %s\nType of error: %s\n"
+ "Message: %s\nFault ID: %d\nProblem data: %s"
+ "\nObject of reference: %s\nProgramName: %s\n"
+ "ProcessID: %d\nTraceFile: %s\n***EOM***\n",
+ formatTimeStampString() ,
+ errorType[type],
+ lookupErrorMessage(faultID),
+ faultID,
+ (problemData == NULL) ? "" : problemData,
+ objRef,
+ my_progname,
+ processId,
+ theNameOfTheTraceFile ? theNameOfTheTraceFile : "<no tracefile>");
+
+ // Add trailing blanks to get a fixed lenght of the message
+ while (strlen(messptr) <= MESSAGE_LENGTH-3){
+ strcat(messptr, " ");
+ }
+
+ strcat(messptr, "\n");
+
+ return;
+}
+
+void
+ErrorReporter::handleAssert(const char* message, const char* file, int line)
+{
+ char refMessage[100];
+
+#ifdef NO_EMULATED_JAM
+ BaseString::snprintf(refMessage, 100, "file: %s lineNo: %d",
+ file, line);
+#else
+ const Uint32 blockNumber = theEmulatedJamBlockNumber;
+ const char *blockName = getBlockName(blockNumber);
+
+ BaseString::snprintf(refMessage, 100, "%s line: %d (block: %s)",
+ file, line, blockName);
+#endif
+ WriteMessage(assert, ERR_ERROR_PRGERR, message, refMessage,
+ theEmulatedJamIndex, theEmulatedJam);
+
+ NdbShutdown(NST_ErrorHandler);
+}
+
+void
+ErrorReporter::handleThreadAssert(const char* message,
+ const char* file,
+ int line)
+{
+ char refMessage[100];
+ BaseString::snprintf(refMessage, 100, "file: %s lineNo: %d - %s",
+ file, line, message);
+
+ NdbShutdown(NST_ErrorHandler);
+}//ErrorReporter::handleThreadAssert()
+
+
+void
+ErrorReporter::handleError(ErrorCategory type, int messageID,
+ const char* problemData,
+ const char* objRef,
+ NdbShutdownType nst)
+{
+ type = ecError;
+ // The value for type is not always set correctly in the calling function.
+ // So, to correct this, we set it set it to the value corresponding to
+ // the function that is called.
+ WriteMessage(type, messageID, problemData,
+ objRef, theEmulatedJamIndex, theEmulatedJam);
+ if(messageID == ERR_ERROR_INSERT){
+ NdbShutdown(NST_ErrorInsert);
+ } else {
+ NdbShutdown(nst);
+ }
+}
+
+int
+WriteMessage(ErrorCategory thrdType, int thrdMessageID,
+ const char* thrdProblemData, const char* thrdObjRef,
+ Uint32 thrdTheEmulatedJamIndex,
+ Uint8 thrdTheEmulatedJam[]){
+ FILE *stream;
+ unsigned offset;
+ unsigned long maxOffset; // Maximum size of file.
+ char theMessage[MESSAGE_LENGTH];
+
+ /**
+ * Format trace file name
+ */
+ char *theTraceFileName= 0;
+ if (globalData.ownId > 0)
+ theTraceFileName= NdbConfig_TraceFileName(globalData.ownId,
+ ErrorReporter::get_trace_no());
+ NdbAutoPtr<char> tmp_aptr1(theTraceFileName);
+
+ // The first 69 bytes is info about the current offset
+ Uint32 noMsg = globalEmulatorData.theConfiguration->maxNoOfErrorLogs();
+
+ maxOffset = (69 + (noMsg * MESSAGE_LENGTH));
+
+ char *theErrorFileName= (char *)NdbConfig_ErrorFileName(globalData.ownId);
+ NdbAutoPtr<char> tmp_aptr2(theErrorFileName);
+
+ stream = fopen(theErrorFileName, "r+");
+ if (stream == NULL) { /* If the file could not be opened. */
+
+ // Create a new file, and skip the first 69 bytes,
+ // which are info about the current offset
+ stream = fopen(theErrorFileName, "w");
+ fprintf(stream, "%s%u%s", "Current byte-offset of file-pointer is: ", 69,
+ " \n\n\n");
+
+ // ...and write the error-message...
+ ErrorReporter::formatMessage(thrdType, thrdMessageID,
+ thrdProblemData, thrdObjRef,
+ theTraceFileName, theMessage);
+ fprintf(stream, "%s", theMessage);
+ fflush(stream);
+
+ /* ...and finally, at the beginning of the file,
+ store the position where to
+ start writing the next message. */
+ offset = ftell(stream);
+ // If we have not reached the maximum number of messages...
+ if (offset <= (maxOffset - MESSAGE_LENGTH)){
+ fseek(stream, 40, SEEK_SET);
+ // ...set the current offset...
+ fprintf(stream,"%d", offset);
+ } else {
+ fseek(stream, 40, SEEK_SET);
+ // ...otherwise, start over from the beginning.
+ fprintf(stream, "%u%s", 69, " ");
+ }
+ } else {
+ // Go to the latest position in the file...
+ fseek(stream, 40, SEEK_SET);
+ fscanf(stream, "%u", &offset);
+ fseek(stream, offset, SEEK_SET);
+
+ // ...and write the error-message there...
+ ErrorReporter::formatMessage(thrdType, thrdMessageID,
+ thrdProblemData, thrdObjRef,
+ theTraceFileName, theMessage);
+ fprintf(stream, "%s", theMessage);
+ fflush(stream);
+
+ /* ...and finally, at the beginning of the file,
+ store the position where to
+ start writing the next message. */
+ offset = ftell(stream);
+
+ // If we have not reached the maximum number of messages...
+ if (offset <= (maxOffset - MESSAGE_LENGTH)){
+ fseek(stream, 40, SEEK_SET);
+ // ...set the current offset...
+ fprintf(stream,"%d", offset);
+ } else {
+ fseek(stream, 40, SEEK_SET);
+ // ...otherwise, start over from the beginning.
+ fprintf(stream, "%u%s", 69, " ");
+ }
+ }
+ fflush(stream);
+ fclose(stream);
+
+ if (theTraceFileName) {
+ // Open the tracefile...
+ FILE *jamStream = fopen(theTraceFileName, "w");
+
+ // ...and "dump the jam" there.
+ // ErrorReporter::dumpJam(jamStream);
+ if(thrdTheEmulatedJam != 0){
+ dumpJam(jamStream, thrdTheEmulatedJamIndex, thrdTheEmulatedJam);
+ }
+
+ /* Dont print the jobBuffers until a way to copy them,
+ like the other variables,
+ is implemented. Otherwise when NDB keeps running,
+ with this function running
+ in the background, the jobBuffers will change during runtime. And when
+ they're printed here, they will not be correct anymore.
+ */
+ globalScheduler.dumpSignalMemory(jamStream);
+
+ fclose(jamStream);
+ }
+
+ return 0;
+}
+
+void
+dumpJam(FILE *jamStream,
+ Uint32 thrdTheEmulatedJamIndex,
+ Uint8 thrdTheEmulatedJam[]) {
+#ifndef NO_EMULATED_JAM
+ // print header
+ const int maxaddr = 8;
+ fprintf(jamStream, "JAM CONTENTS up->down left->right ?=not block entry\n");
+ fprintf(jamStream, "%-7s ", "BLOCK");
+ for (int i = 0; i < maxaddr; i++)
+ fprintf(jamStream, "%-6s ", "ADDR");
+ fprintf(jamStream, "\n");
+
+ // treat as array of Uint32
+ const Uint32 *base = (Uint32 *)thrdTheEmulatedJam;
+ const int first = thrdTheEmulatedJamIndex / sizeof(Uint32); // oldest
+ int cnt, idx;
+
+ // look for first block entry
+ for (cnt = 0, idx = first; cnt < EMULATED_JAM_SIZE; cnt++, idx++) {
+ if (idx >= EMULATED_JAM_SIZE)
+ idx = 0;
+ const Uint32 aJamEntry = base[idx];
+ if (aJamEntry > (1 << 20))
+ break;
+ }
+
+ // 1. if first entry is a block entry, it is printed in the main loop
+ // 2. else if any block entry exists, the jam starts in an unknown block
+ // 3. else if no block entry exists, the block is theEmulatedJamBlockNumber
+ // a "?" indicates first addr is not a block entry
+ if (cnt == 0)
+ ;
+ else if (cnt < EMULATED_JAM_SIZE)
+ fprintf(jamStream, "%-7s?", "");
+ else {
+ const Uint32 aBlockNumber = theEmulatedJamBlockNumber;
+ const char *aBlockName = getBlockName(aBlockNumber);
+ if (aBlockName != 0)
+ fprintf(jamStream, "%-7s?", aBlockName);
+ else
+ fprintf(jamStream, "0x%-5X?", aBlockNumber);
+ }
+
+ // loop over all entries
+ int cntaddr = 0;
+ for (cnt = 0, idx = first; cnt < EMULATED_JAM_SIZE; cnt++, idx++) {
+ globalData.incrementWatchDogCounter(4); // watchdog not to kill us ?
+ if (idx >= EMULATED_JAM_SIZE)
+ idx = 0;
+ const Uint32 aJamEntry = base[idx];
+ if (aJamEntry > (1 << 20)) {
+ const Uint32 aBlockNumber = aJamEntry >> 20;
+ const char *aBlockName = getBlockName(aBlockNumber);
+ if (cnt > 0)
+ fprintf(jamStream, "\n");
+ if (aBlockName != 0)
+ fprintf(jamStream, "%-7s ", aBlockName);
+ else
+ fprintf(jamStream, "0x%-5X ", aBlockNumber);
+ cntaddr = 0;
+ }
+ if (cntaddr == maxaddr) {
+ fprintf(jamStream, "\n%-7s ", "");
+ cntaddr = 0;
+ }
+ fprintf(jamStream, "%06u ", aJamEntry & 0xFFFFF);
+ cntaddr++;
+ }
+ fprintf(jamStream, "\n");
+ fflush(jamStream);
+#endif // ifndef NO_EMULATED_JAM
+}
diff --git a/storage/ndb/src/kernel/error/ErrorReporter.hpp b/storage/ndb/src/kernel/error/ErrorReporter.hpp
new file mode 100644
index 00000000000..2c79f242eea
--- /dev/null
+++ b/storage/ndb/src/kernel/error/ErrorReporter.hpp
@@ -0,0 +1,62 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ERRORREPORTER_H
+#define ERRORREPORTER_H
+
+#include <ndb_global.h>
+
+#include "TimeModule.hpp"
+#include "Error.hpp"
+#include <Emulator.hpp>
+
+class ErrorReporter
+{
+public:
+ static void handleAssert(const char* message,
+ const char* file,
+ int line);
+
+ static void handleThreadAssert(const char* message,
+ const char* file,
+ int line);
+
+ static void handleError(ErrorCategory type,
+ int faultID,
+ const char* problemData,
+ const char* objRef,
+ enum NdbShutdownType = NST_ErrorHandler);
+
+ static void handleWarning(ErrorCategory type,
+ int faultID,
+ const char* problemData,
+ const char* objRef);
+
+ static void formatMessage(ErrorCategory type,
+ int faultID,
+ const char* problemData,
+ const char* objRef,
+ const char* theNameOfTheTraceFile,
+ char* messptr);
+
+ static int get_trace_no();
+
+ static const char* formatTimeStampString();
+
+private:
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/error/Makefile.am b/storage/ndb/src/kernel/error/Makefile.am
new file mode 100644
index 00000000000..54f3de2d76d
--- /dev/null
+++ b/storage/ndb/src/kernel/error/Makefile.am
@@ -0,0 +1,25 @@
+noinst_LIBRARIES = liberror.a
+
+liberror_a_SOURCES = TimeModule.cpp \
+ ErrorReporter.cpp \
+ ErrorMessages.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: liberror.dsp
+
+liberror.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(liberror_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/error/TimeModule.cpp b/storage/ndb/src/kernel/error/TimeModule.cpp
new file mode 100644
index 00000000000..4bd8e3daf99
--- /dev/null
+++ b/storage/ndb/src/kernel/error/TimeModule.cpp
@@ -0,0 +1,109 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <ndb_global.h>
+#include "TimeModule.hpp"
+
+static const char* cMonth[] = { "x", "January", "February", "Mars", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"};
+
+static const char* cDay[] = { "x", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
+ "Saturday", "Sunday"};
+
+static const char* cHour[] = { "00","01","02","03","04","05","06","07","08","09","10","11","12",
+ "13","14","15","16","17","18","19","20","21","22","23"};
+
+static const char* cMinute[] = { "00","01","02","03","04","05","06","07","08","09","10","11","12",
+ "13","14","15","16","17","18","19","20","21","22","23","24","25",
+ "26","27","28","29","30","31","32","33","34","35","36","37","38",
+ "39","40","41","42","43","44","45","46","47","48","49","50","51",
+ "52","53","54","55","56","57","58","59"};
+
+static const char* cSecond[] = { "00","01","02","03","04","05","06","07","08","09","10","11","12",
+ "13","14","15","16","17","18","19","20","21","22","23","24","25",
+ "26","27","28","29","30","31","32","33","34","35","36","37","38",
+ "39","40","41","42","43","44","45","46","47","48","49","50","51",
+ "52","53","54","55","56","57","58","59"};
+
+
+TimeModule::TimeModule(){
+}
+
+TimeModule::~TimeModule(){
+}
+
+void
+TimeModule::setTimeStamp()
+{
+ struct tm* rightnow;
+ time_t now;
+
+ time(&now);
+
+ rightnow = localtime(&now);
+
+ iYear = rightnow->tm_year+1900; // localtime returns current year -1900
+ iMonth = rightnow->tm_mon+1; // and month 0-11
+ iMonthDay = rightnow->tm_mday;
+ iWeekDay = rightnow->tm_wday;
+ iHour = rightnow->tm_hour;
+ iMinute = rightnow->tm_min;
+ iSecond = rightnow->tm_sec;
+}
+
+int
+TimeModule::getYear() const
+{
+ return iYear;
+}
+
+int
+TimeModule::getMonthNumber() const
+{
+ return iMonth;
+}
+
+const char*
+TimeModule::getMonthName() const {
+ return cMonth[iMonth];
+}
+
+int
+TimeModule::getDayOfMonth() const {
+ return iMonthDay;
+}
+
+const char*
+TimeModule::getDayName() const {
+ return cDay[iWeekDay];
+}
+
+const char*
+TimeModule::getHour() const {
+ return cHour[iHour];
+}
+
+const char*
+TimeModule::getMinute() const {
+ return cMinute[iMinute];
+}
+
+const char*
+TimeModule::getSecond() const {
+ return cSecond[iSecond];
+}
diff --git a/storage/ndb/src/kernel/error/TimeModule.hpp b/storage/ndb/src/kernel/error/TimeModule.hpp
new file mode 100644
index 00000000000..f1414c77af3
--- /dev/null
+++ b/storage/ndb/src/kernel/error/TimeModule.hpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 _TimeModule_
+#define _TimeModule_
+
+class TimeModule {
+public:
+ TimeModule();
+ ~TimeModule();
+
+ void setTimeStamp();
+
+ int getYear() const;
+ int getMonthNumber() const;
+ int getDayOfMonth() const;
+ const char* getMonthName() const;
+ const char* getDayName() const;
+ const char* getHour() const;
+ const char* getMinute() const;
+ const char* getSecond() const;
+
+private:
+ int iYear;
+ int iMonth;
+ int iMonthDay;
+ int iWeekDay;
+ int iHour;
+ int iMinute;
+ int iSecond;
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/main.cpp b/storage/ndb/src/kernel/main.cpp
new file mode 100644
index 00000000000..bd15ef37e20
--- /dev/null
+++ b/storage/ndb/src/kernel/main.cpp
@@ -0,0 +1,369 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+
+#include <ndb_version.h>
+#include "Configuration.hpp"
+#include <ConfigRetriever.hpp>
+#include <TransporterRegistry.hpp>
+
+#include "vm/SimBlockList.hpp"
+#include "ThreadConfig.hpp"
+#include <SignalLoggerManager.hpp>
+#include <NdbOut.hpp>
+#include <NdbMain.h>
+#include <NdbDaemon.h>
+#include <NdbSleep.h>
+#include <NdbConfig.h>
+#include <WatchDog.hpp>
+
+#include <LogLevel.hpp>
+#include <EventLogger.hpp>
+
+#include <NdbAutoPtr.hpp>
+
+#include <mgmapi_debug.h>
+
+#if defined NDB_SOLARIS // ok
+#include <sys/processor.h> // For system informatio
+#endif
+
+extern EventLogger g_eventLogger;
+extern NdbMutex * theShutdownMutex;
+
+void catchsigs(bool ignore); // for process signal handling
+
+extern "C" void handler_shutdown(int signum); // for process signal handling
+extern "C" void handler_error(int signum); // for process signal handling
+
+// Shows system information
+void systemInfo(const Configuration & conf,
+ const LogLevel & ll);
+
+int main(int argc, char** argv)
+{
+ NDB_INIT(argv[0]);
+ // Print to stdout/console
+ g_eventLogger.createConsoleHandler();
+ g_eventLogger.setCategory("NDB");
+ g_eventLogger.enable(Logger::LL_ON, Logger::LL_ERROR);
+
+ globalEmulatorData.create();
+
+ // Parse command line options
+ Configuration* theConfig = globalEmulatorData.theConfiguration;
+ if(!theConfig->init(argc, argv)){
+ return NRT_Default;
+ }
+
+ { // Do configuration
+#ifndef NDB_WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ theConfig->fetch_configuration();
+ }
+
+ my_setwd(NdbConfig_get_path(0), MYF(0));
+
+ if (theConfig->getDaemonMode()) {
+ // Become a daemon
+ char *lockfile= NdbConfig_PidFileName(globalData.ownId);
+ char *logfile= NdbConfig_StdoutFileName(globalData.ownId);
+ NdbAutoPtr<char> tmp_aptr1(lockfile), tmp_aptr2(logfile);
+
+ if (NdbDaemon_Make(lockfile, logfile, 0) == -1) {
+ ndbout << "Cannot become daemon: " << NdbDaemon_ErrorText << endl;
+ return 1;
+ }
+ }
+
+#ifndef NDB_WIN32
+ for(pid_t child = fork(); child != 0; child = fork()){
+ /**
+ * Parent
+ */
+ catchsigs(true);
+
+ int status = 0;
+ while(waitpid(child, &status, 0) != child);
+ if(WIFEXITED(status)){
+ switch(WEXITSTATUS(status)){
+ case NRT_Default:
+ g_eventLogger.info("Angel shutting down");
+ exit(0);
+ break;
+ case NRT_NoStart_Restart:
+ theConfig->setInitialStart(false);
+ globalData.theRestartFlag = initial_state;
+ break;
+ case NRT_NoStart_InitialStart:
+ theConfig->setInitialStart(true);
+ globalData.theRestartFlag = initial_state;
+ break;
+ case NRT_DoStart_InitialStart:
+ theConfig->setInitialStart(true);
+ globalData.theRestartFlag = perform_start;
+ break;
+ default:
+ if(theConfig->stopOnError()){
+ /**
+ * Error shutdown && stopOnError()
+ */
+ exit(0);
+ }
+ // Fall-through
+ case NRT_DoStart_Restart:
+ theConfig->setInitialStart(false);
+ globalData.theRestartFlag = perform_start;
+ break;
+ }
+ } else if(theConfig->stopOnError()){
+ /**
+ * Error shutdown && stopOnError()
+ */
+ exit(0);
+ }
+ g_eventLogger.info("Ndb has terminated (pid %d) restarting", child);
+ theConfig->fetch_configuration();
+ }
+
+ g_eventLogger.info("Angel pid: %d ndb pid: %d", getppid(), getpid());
+#else
+ g_eventLogger.info("Ndb started");
+#endif
+ theConfig->setupConfiguration();
+ systemInfo(* theConfig, * theConfig->m_logLevel);
+
+ // Load blocks
+ globalEmulatorData.theSimBlockList->load(* theConfig);
+
+ // Set thread concurrency for Solaris' light weight processes
+ int status;
+ status = NdbThread_SetConcurrencyLevel(30);
+ assert(status == 0);
+
+#ifdef VM_TRACE
+ // Create a signal logger
+ char *buf= NdbConfig_SignalLogFileName(globalData.ownId);
+ NdbAutoPtr<char> tmp_aptr(buf);
+ FILE * signalLog = fopen(buf, "a");
+ globalSignalLoggers.setOwnNodeId(globalData.ownId);
+ globalSignalLoggers.setOutputStream(signalLog);
+#endif
+
+ catchsigs(false);
+
+ /**
+ * Do startup
+ */
+ switch(globalData.theRestartFlag){
+ case initial_state:
+ globalEmulatorData.theThreadConfig->doStart(NodeState::SL_CMVMI);
+ break;
+ case perform_start:
+ globalEmulatorData.theThreadConfig->doStart(NodeState::SL_CMVMI);
+ globalEmulatorData.theThreadConfig->doStart(NodeState::SL_STARTING);
+ break;
+ default:
+ assert("Illegal state globalData.theRestartFlag" == 0);
+ }
+
+ globalTransporterRegistry.startSending();
+ globalTransporterRegistry.startReceiving();
+ if (!globalTransporterRegistry.start_service(*globalEmulatorData.m_socket_server)){
+ ndbout_c("globalTransporterRegistry.start_service() failed");
+ exit(-1);
+ }
+
+ // Re-use the mgm handle as a transporter
+ if(!globalTransporterRegistry.connect_client(
+ theConfig->get_config_retriever()->get_mgmHandlePtr()))
+ ERROR_SET(fatal, ERR_INVALID_CONFIG,
+ "Connection to mgmd terminated before setup was complete",
+ "StopOnError missing");
+
+ if (!globalTransporterRegistry.start_clients()){
+ ndbout_c("globalTransporterRegistry.start_clients() failed");
+ exit(-1);
+ }
+
+ globalEmulatorData.theWatchDog->doStart();
+
+ globalEmulatorData.m_socket_server->startServer();
+
+ // theConfig->closeConfiguration();
+
+ globalEmulatorData.theThreadConfig->ipControlLoop();
+
+ NdbShutdown(NST_Normal);
+
+ return NRT_Default;
+}
+
+
+void
+systemInfo(const Configuration & config, const LogLevel & logLevel){
+#ifdef NDB_WIN32
+ int processors = 0;
+ int speed;
+ SYSTEM_INFO sinfo;
+ GetSystemInfo(&sinfo);
+ processors = sinfo.dwNumberOfProcessors;
+ HKEY hKey;
+ if(ERROR_SUCCESS==RegOpenKeyEx
+ (HKEY_LOCAL_MACHINE,
+ TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
+ 0, KEY_READ, &hKey)) {
+ DWORD dwMHz;
+ DWORD cbData = sizeof(dwMHz);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,
+ "~MHz", 0, 0, (LPBYTE)&dwMHz, &cbData)) {
+ speed = int(dwMHz);
+ }
+ RegCloseKey(hKey);
+ }
+#elif defined NDB_SOLARIS // ok
+ // Search for at max 16 processors among the first 256 processor ids
+ processor_info_t pinfo; memset(&pinfo, 0, sizeof(pinfo));
+ int pid = 0;
+ while(processors < 16 && pid < 256){
+ if(!processor_info(pid++, &pinfo))
+ processors++;
+ }
+ speed = pinfo.pi_clock;
+#endif
+
+ if(logLevel.getLogLevel(LogLevel::llStartUp) > 0){
+ g_eventLogger.info("NDB Cluster -- DB node %d", globalData.ownId);
+ g_eventLogger.info("%s --", NDB_VERSION_STRING);
+ if (config.get_mgmd_host())
+ g_eventLogger.info("Configuration fetched at %s port %d",
+ config.get_mgmd_host(), config.get_mgmd_port());
+#ifdef NDB_SOLARIS // ok
+ g_eventLogger.info("NDB is running on a machine with %d processor(s) at %d MHz",
+ processor, speed);
+#endif
+ }
+ if(logLevel.getLogLevel(LogLevel::llStartUp) > 3){
+ Uint32 t = config.timeBetweenWatchDogCheck();
+ g_eventLogger.info("WatchDog timer is set to %d ms", t);
+ }
+
+}
+
+#define handler_register(signum, handler, ignore)\
+{\
+ if (ignore) {\
+ if(signum != SIGCHLD)\
+ signal(signum, SIG_IGN);\
+ } else\
+ signal(signum, handler);\
+}
+
+void
+catchsigs(bool ignore){
+#if !defined NDB_WIN32 && !defined NDB_SOFTOSE && !defined NDB_OSE
+
+ static const int signals_shutdown[] = {
+#ifdef SIGBREAK
+ SIGBREAK,
+#endif
+ SIGHUP,
+ SIGINT,
+#if defined SIGPWR
+ SIGPWR,
+#elif defined SIGINFO
+ SIGINFO,
+#endif
+ SIGQUIT,
+ SIGTERM,
+#ifdef SIGTSTP
+ SIGTSTP,
+#endif
+ SIGTTIN,
+ SIGTTOU
+ };
+
+ static const int signals_error[] = {
+ SIGABRT,
+ SIGALRM,
+#ifdef SIGBUS
+ SIGBUS,
+#endif
+ SIGCHLD,
+ SIGFPE,
+ SIGILL,
+#ifdef SIGIO
+ SIGIO,
+#endif
+#ifdef SIGPOLL
+ SIGPOLL,
+#endif
+ SIGSEGV,
+#ifdef SIGTRAP
+ SIGTRAP
+#endif
+ };
+
+ static const int signals_ignore[] = {
+ SIGPIPE
+ };
+
+ size_t i;
+ for(i = 0; i < sizeof(signals_shutdown)/sizeof(signals_shutdown[0]); i++)
+ handler_register(signals_shutdown[i], handler_shutdown, ignore);
+ for(i = 0; i < sizeof(signals_error)/sizeof(signals_error[0]); i++)
+ handler_register(signals_error[i], handler_error, ignore);
+ for(i = 0; i < sizeof(signals_ignore)/sizeof(signals_ignore[0]); i++)
+ handler_register(signals_ignore[i], SIG_IGN, ignore);
+#endif
+}
+
+extern "C"
+void
+handler_shutdown(int signum){
+ g_eventLogger.info("Received signal %d. Performing stop.", signum);
+ globalData.theRestartFlag = perform_stop;
+}
+
+extern "C"
+void
+handler_error(int signum){
+ // only let one thread run shutdown
+ static long thread_id= 0;
+
+ if (thread_id != 0 && thread_id == my_thread_id())
+ {
+ // Shutdown thread received signal
+#ifndef NDB_WIN32
+ signal(signum, SIG_DFL);
+ kill(getpid(), signum);
+#endif
+ while(true)
+ NdbSleep_MilliSleep(10);
+ }
+ if(theShutdownMutex && NdbMutex_Trylock(theShutdownMutex) != 0)
+ while(true)
+ NdbSleep_MilliSleep(10);
+ thread_id= my_thread_id();
+ g_eventLogger.info("Received signal %d. Running error handler.", signum);
+ // restart the system
+ char errorData[40];
+ BaseString::snprintf(errorData, 40, "Signal %d received", signum);
+ ERROR_SET_SIGNAL(fatal, 0, errorData, __FILE__);
+}
diff --git a/storage/ndb/src/kernel/vm/Array.hpp b/storage/ndb/src/kernel/vm/Array.hpp
new file mode 100644
index 00000000000..97b0a345cb4
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Array.hpp
@@ -0,0 +1,165 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ARRAY_HPP
+#define ARRAY_HPP
+
+#include "ArrayPool.hpp"
+
+#include <pc.hpp>
+#include <ErrorReporter.hpp>
+
+/**
+ * Template class used for implementing an
+ * array of object retreived from a pool
+ */
+template <class T>
+class Array {
+public:
+ Array(ArrayPool<T> & thePool);
+
+ /**
+ * Allocate an <b>n</b> objects from pool
+ * These can now be addressed with 0 <= ptr.i < n
+ */
+ bool seize(Uint32 i);
+
+ /**
+ * Release all object from array
+ */
+ void release();
+
+ /**
+ * Return current size of array
+ */
+ Uint32 getSize() const;
+
+ /**
+ * empty
+ */
+ inline bool empty() const { return sz == 0;}
+
+ /**
+ * Update i & p value according to <b>i</b>
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+ /**
+ * Update p value for ptr according to i value
+ */
+ void getPtr(Ptr<T> &) const ;
+
+ /**
+ * Get pointer for i value
+ */
+ T * getPtr(Uint32 i) const;
+
+private:
+ Uint32 base, sz;
+ ArrayPool<T> & thePool;
+};
+
+template<class T>
+inline
+Array<T>::Array(ArrayPool<T> & _pool)
+ : thePool(_pool)
+{
+ sz = 0;
+ base = RNIL;
+}
+
+template<class T>
+inline
+bool
+Array<T>::seize(Uint32 n){
+ if(base == RNIL && n > 0){
+ base = thePool.seizeN(n);
+ if(base != RNIL){
+ sz = n;
+ return true;
+ }
+ return false;
+ }
+ ErrorReporter::handleAssert("Array<T>::seize failed", __FILE__, __LINE__);
+ return false;
+}
+
+template<class T>
+inline
+void
+Array<T>::release(){
+ if(base != RNIL){
+ thePool.releaseN(base, sz);
+ sz = 0;
+ base = RNIL;
+ return;
+ }
+}
+
+template<class T>
+inline
+Uint32
+Array<T>::getSize() const {
+ return sz;
+}
+
+template <class T>
+inline
+void
+Array<T>::getPtr(Ptr<T> & p, Uint32 i) const {
+ p.i = i;
+#ifdef ARRAY_GUARD
+ if(i < sz && base != RNIL){
+ p.p = thePool.getPtr(i + base);
+ return;
+ } else {
+ ErrorReporter::handleAssert("Array::getPtr failed", __FILE__, __LINE__);
+ }
+#endif
+ p.p = thePool.getPtr(i + base);
+}
+
+template<class T>
+inline
+void
+Array<T>::getPtr(Ptr<T> & ptr) const {
+#ifdef ARRAY_GUARD
+ if(ptr.i < sz && base != RNIL){
+ ptr.p = thePool.getPtr(ptr.i + base);
+ return;
+ } else {
+ ErrorReporter::handleAssert("Array<T>::getPtr failed", __FILE__, __LINE__);
+ }
+#endif
+ ptr.p = thePool.getPtr(ptr.i + base);
+}
+
+template<class T>
+inline
+T *
+Array<T>::getPtr(Uint32 i) const {
+#ifdef ARRAY_GUARD
+ if(i < sz && base != RNIL){
+ return thePool.getPtr(i + base);
+ } else {
+ ErrorReporter::handleAssert("Array<T>::getPtr failed", __FILE__, __LINE__);
+ }
+#endif
+ return thePool.getPtr(i + base);
+}
+
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/ArrayFifoList.hpp b/storage/ndb/src/kernel/vm/ArrayFifoList.hpp
new file mode 100644
index 00000000000..b21bf449734
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ArrayFifoList.hpp
@@ -0,0 +1,30 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ARRAY_FIFOLIST_HPP
+#define ARRAY_FIFOLIST_HPP
+
+#include "ArrayPool.hpp"
+#include "DLFifoList.hpp"
+#include "Array.hpp"
+
+template <class T>
+class ArrayFifoList : public DLFifoList<T> {
+public:
+ ArrayFifoList(ArrayPool<T> & thePool) : DLFifoList<T>(thePool) { }
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/ArrayList.hpp b/storage/ndb/src/kernel/vm/ArrayList.hpp
new file mode 100644
index 00000000000..4b46347a39b
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ArrayList.hpp
@@ -0,0 +1,30 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ARRAY_LIST_HPP
+#define ARRAY_LIST_HPP
+
+#include "ArrayPool.hpp"
+#include "DLList.hpp"
+#include "Array.hpp"
+
+template <class T>
+class ArrayList : public DLList<T> {
+public:
+ ArrayList(ArrayPool<T> & thePool) : DLList<T>(thePool) { }
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/ArrayPool.hpp b/storage/ndb/src/kernel/vm/ArrayPool.hpp
new file mode 100644
index 00000000000..924ed51ee15
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ArrayPool.hpp
@@ -0,0 +1,856 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ARRAY_POOL_HPP
+#define ARRAY_POOL_HPP
+
+#include <ndb_global.h>
+
+#include <pc.hpp>
+#include <ErrorReporter.hpp>
+#include <NdbMem.h>
+#include <Bitmask.hpp>
+
+template <class T> class Array;
+template <class T> class SLList;
+template <class T> class DLList;
+template <class T> class DLHashTable;
+
+/**
+ * Template class used for implementing an
+ * pool of object (in an array with a free list)
+ */
+template <class T>
+class ArrayPool {
+public:
+ ArrayPool();
+ ~ArrayPool();
+
+ /**
+ * Set the size of the pool
+ *
+ * Note, can currently only be called once
+ */
+ bool setSize(Uint32 noOfElements);
+
+ inline Uint32 getNoOfFree() const {
+ return noOfFree;
+ }
+
+ inline Uint32 getSize() const {
+ return size;
+ }
+
+ /**
+ * Update p value for ptr according to i value
+ */
+ void getPtr(Ptr<T> &);
+ void getPtr(ConstPtr<T> &) const;
+ void getPtr(Ptr<T> &, bool CrashOnBoundaryError);
+ void getPtr(ConstPtr<T> &, bool CrashOnBoundaryError) const;
+
+ /**
+ * Get pointer for i value
+ */
+ T * getPtr(Uint32 i);
+ const T * getConstPtr(Uint32 i) const;
+ T * getPtr(Uint32 i, bool CrashOnBoundaryError);
+ const T * getConstPtr(Uint32 i, bool CrashOnBoundaryError) const;
+
+ /**
+ * Update p & i value for ptr according to <b>i</b> value
+ */
+ void getPtr(Ptr<T> &, Uint32 i);
+ void getPtr(ConstPtr<T> &, Uint32 i) const;
+ void getPtr(Ptr<T> &, Uint32 i, bool CrashOnBoundaryError);
+ void getPtr(ConstPtr<T> &, Uint32 i, bool CrashOnBoundaryError) const;
+
+ /**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seize(Ptr<T> &);
+
+ /**
+ * Allocate object <b>i</b> from pool - update Ptr
+ */
+ bool seizeId(Ptr<T> &, Uint32 i);
+
+ /**
+ * Check if <b>i</b> is allocated.
+ */
+ bool findId(Uint32 i) const;
+
+ /**
+ * Return an object to pool
+ */
+ void release(Uint32 i);
+
+ /**
+ * Return an object to pool
+ */
+ void release(Ptr<T> &);
+
+#ifdef ARRAY_GUARD
+ /**
+ * Checks if i is a correct seized record
+ *
+ * @note Since this is either an expensive method,
+ * or needs bitmask stuff, this method is only
+ * recommended for debugging.
+ *
+ */
+ bool isSeized(Uint32 i) const {
+ if (i>=size) return false;
+ return BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i);
+ }
+#endif
+
+protected:
+ friend class Array<T>;
+ friend class SLList<T>;
+ friend class DLList<T>;
+ friend class DLHashTable<T>;
+
+ /**
+ * Allocate <b>n</b> consecutive object from pool
+ * return base
+ */
+ Uint32 seizeN(Uint32 n);
+
+ /**
+ * Deallocate <b>n<b> consecutive object to pool
+ * starting from base
+ */
+ void releaseN(Uint32 base, Uint32 n);
+
+public:
+ /**
+ * Release a singel linked list in o(1)
+ * @param first i-value of first element in list
+ * @param last i-value of last element in list
+ * @note nextPool must be used as next pointer in list
+ */
+ void releaseList(Uint32 n, Uint32 first, Uint32 last);
+ //private:
+
+#ifdef DEBUG
+ Uint32 getNoOfFree2() const {
+ Uint32 c2 = size;
+ for(Uint32 i = 0; i<((size + 31)>> 5); i++){
+ Uint32 w = theAllocatedBitmask[i];
+ for(Uint32 j = 0; j<32; j++){
+ if((w & 1) == 1){
+ c2--;
+ }
+ w >>= 1;
+ }
+ }
+ return c2;
+ }
+
+ Uint32 getNoOfFree3() const {
+ Uint32 c = 0;
+ Ptr<T> p;
+ p.i = firstFree;
+ while(p.i != RNIL){
+ c++;
+ p.p = &theArray[p.i];
+ p.i = p.p->next;
+ }
+ return c;
+ }
+#endif
+
+protected:
+ Uint32 firstFree;
+ Uint32 size;
+ Uint32 noOfFree;
+ T * theArray;
+ Uint32 bitmaskSz;
+ Uint32 *theAllocatedBitmask;
+};
+
+template <class T>
+inline
+ArrayPool<T>::ArrayPool(){
+ firstFree = RNIL;
+ size = 0;
+ noOfFree = 0;
+ theArray = 0;
+#ifdef ARRAY_GUARD
+ theAllocatedBitmask = 0;
+#endif
+}
+
+template <class T>
+inline
+ArrayPool<T>::~ArrayPool(){
+ if(theArray != 0){
+ NdbMem_Free(theArray);
+ theArray = 0;
+#ifdef ARRAY_GUARD
+ delete []theAllocatedBitmask;
+ theAllocatedBitmask = 0;
+#endif
+ }
+}
+
+/**
+ * Set the size of the pool
+ *
+ * Note, can currently only be called once
+ */
+template <class T>
+inline
+bool
+ArrayPool<T>::setSize(Uint32 noOfElements){
+ if(size == 0){
+ if(noOfElements == 0)
+ return true;
+ theArray = (T *)NdbMem_Allocate(noOfElements * sizeof(T));
+ if(theArray == 0)
+ return false;
+ size = noOfElements;
+ noOfFree = noOfElements;
+
+ /**
+ * Set next pointers
+ */
+ T * t = &theArray[0];
+ for(Uint32 i = 0; i<size; i++){
+ t->nextPool = (i + 1);
+ t++;
+ }
+ theArray[size-1].nextPool = RNIL;
+ firstFree = 0;
+
+#ifdef ARRAY_GUARD
+ bitmaskSz = (noOfElements + 31) >> 5;
+ theAllocatedBitmask = new Uint32[bitmaskSz];
+ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask);
+#endif
+
+ return true;
+ }
+ return false;
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(Ptr<T> & ptr){
+ Uint32 i = ptr.i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(ConstPtr<T> & ptr) const {
+ Uint32 i = ptr.i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(Ptr<T> & ptr, Uint32 i){
+ ptr.i = i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(ConstPtr<T> & ptr, Uint32 i) const {
+ ptr.i = i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+T *
+ArrayPool<T>::getPtr(Uint32 i){
+ if(i < size){
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return &theArray[i];
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ return 0;
+#else
+ return &theArray[i];
+#endif
+ } else {
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ return 0;
+ }
+}
+
+template <class T>
+inline
+const T *
+ArrayPool<T>::getConstPtr(Uint32 i) const {
+ if(i < size){
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return &theArray[i];
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ return 0;
+#else
+ return &theArray[i];
+#endif
+ } else {
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ return 0;
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(Ptr<T> & ptr, bool CrashOnBoundaryError){
+ Uint32 i = ptr.i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ptr.i = RNIL;
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(ConstPtr<T> & ptr, bool CrashOnBoundaryError) const {
+ Uint32 i = ptr.i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ptr.i = RNIL;
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(Ptr<T> & ptr, Uint32 i, bool CrashOnBoundaryError){
+ ptr.i = i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ptr.i = RNIL;
+ }
+}
+
+template <class T>
+inline
+void
+ArrayPool<T>::getPtr(ConstPtr<T> & ptr, Uint32 i,
+ bool CrashOnBoundaryError) const {
+ ptr.i = i;
+ if(i < size){
+ ptr.p = &theArray[i];
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return;
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+#endif
+ } else {
+ ptr.i = RNIL;
+ }
+}
+
+template <class T>
+inline
+T *
+ArrayPool<T>::getPtr(Uint32 i, bool CrashOnBoundaryError){
+ if(i < size){
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return &theArray[i];
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getPtr", __FILE__, __LINE__);
+ return 0;
+#else
+ return &theArray[i];
+#endif
+ } else {
+ return 0;
+ }
+}
+
+template <class T>
+inline
+const T *
+ArrayPool<T>::getConstPtr(Uint32 i, bool CrashOnBoundaryError) const {
+ if(i < size){
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i))
+ return &theArray[i];
+ /**
+ * Getting a non-seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::getConstPtr", __FILE__,__LINE__);
+ return 0;
+#else
+ return &theArray[i];
+#endif
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+ArrayPool<T>::seize(Ptr<T> & ptr){
+ Uint32 ff = firstFree;
+ if(ff != RNIL){
+ firstFree = theArray[ff].nextPool;
+
+ ptr.i = ff;
+ ptr.p = &theArray[ff];
+#ifdef ARRAY_GUARD
+ if(!BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, ff)){
+ BitmaskImpl::set(bitmaskSz, theAllocatedBitmask, ff);
+ noOfFree--;
+ return true;
+ } else {
+ /**
+ * Seizing an already seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::seize", __FILE__, __LINE__);
+ return false;
+ }
+#else
+ noOfFree--;
+ return true;
+#endif
+ }
+ ptr.i = RNIL;
+ ptr.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+ArrayPool<T>::seizeId(Ptr<T> & ptr, Uint32 i){
+ Uint32 ff = firstFree;
+ Uint32 prev = RNIL;
+ while(ff != i && ff != RNIL){
+ prev = ff;
+ ff = theArray[ff].nextPool;
+ }
+
+ if(ff != RNIL){
+ if(prev == RNIL)
+ firstFree = theArray[ff].nextPool;
+ else
+ theArray[prev].nextPool = theArray[ff].nextPool;
+
+ ptr.i = ff;
+ ptr.p = &theArray[ff];
+#ifdef ARRAY_GUARD
+ if(!BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, ff)){
+ BitmaskImpl::set(bitmaskSz, theAllocatedBitmask, ff);
+ noOfFree--;
+ return true;
+ } else {
+ /**
+ * Seizing an already seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::seizeId", __FILE__, __LINE__);
+ return false;
+ }
+#else
+ noOfFree--;
+ return true;
+#endif
+ }
+ ptr.i = RNIL;
+ ptr.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+ArrayPool<T>::findId(Uint32 i) const {
+ if (i >= size)
+ return false;
+ Uint32 ff = firstFree;
+ while(ff != i && ff != RNIL){
+ ff = theArray[ff].nextPool;
+ }
+ return (ff == RNIL);
+}
+
+template<class T>
+Uint32
+ArrayPool<T>::seizeN(Uint32 n){
+ Uint32 curr = firstFree;
+ Uint32 prev = RNIL;
+ Uint32 sz = 0;
+ while(sz < n && curr != RNIL){
+ if(theArray[curr].nextPool == (curr + 1)){
+ sz++;
+ } else {
+ sz = 0;
+ prev = curr;
+ }
+ curr = theArray[curr].nextPool;
+ }
+ if(sz != n){
+ return RNIL;
+ }
+ const Uint32 base = curr - n;
+ if(base == firstFree){
+ firstFree = curr;
+ } else {
+ theArray[prev].nextPool = curr;
+ }
+
+ noOfFree -= n;
+#ifdef ARRAY_GUARD
+ for(Uint32 j = base; j<curr; j++){
+ if(!BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, j)){
+ BitmaskImpl::set(bitmaskSz, theAllocatedBitmask, j);
+ } else {
+ /**
+ * Seizing an already seized element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::seize", __FILE__, __LINE__);
+ return RNIL;
+ }
+ }
+#endif
+ return base;
+}
+
+template<class T>
+inline
+void
+ArrayPool<T>::releaseN(Uint32 base, Uint32 n){
+ Uint32 curr = firstFree;
+ Uint32 prev = RNIL;
+ while(curr < base){
+ prev = curr;
+ curr = theArray[curr].nextPool;
+ }
+ if(curr == firstFree){
+ firstFree = base;
+ } else {
+ theArray[prev].nextPool = base;
+ }
+ const Uint32 end = base + n;
+ for(Uint32 i = base; i<end; i++){
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)){
+ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask, i);
+ } else {
+ /**
+ * Relesing a already released element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::release", __FILE__, __LINE__);
+ return;
+ }
+#endif
+ theArray[i].nextPool = i + 1;
+ }
+ theArray[end-1].nextPool = curr;
+ noOfFree += n;
+}
+
+template<class T>
+inline
+void
+ArrayPool<T>::releaseList(Uint32 n, Uint32 first, Uint32 last){
+
+ if(first < size && last < size){
+ Uint32 ff = firstFree;
+ firstFree = first;
+ theArray[last].nextPool = ff;
+ noOfFree += n;
+
+#ifdef ARRAY_GUARD
+ Uint32 tmp = first;
+ for(Uint32 i = 0; i<n; i++){
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, tmp)){
+ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask, tmp);
+ } else {
+ /**
+ * Relesing a already released element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::releaseList",
+ __FILE__, __LINE__);
+ return;
+ }
+ tmp = theArray[tmp].nextPool;
+ }
+#endif
+ return;
+ }
+ ErrorReporter::handleAssert("ArrayPool<T>::releaseList", __FILE__, __LINE__);
+}
+
+/**
+ * Return an object to pool
+ */
+template <class T>
+inline
+void
+ArrayPool<T>::release(Uint32 _i){
+ const Uint32 i = _i;
+ if(i < size){
+ Uint32 ff = firstFree;
+ theArray[i].nextPool = ff;
+ firstFree = i;
+
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)){
+ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask, i);
+ noOfFree++;
+ return;
+ }
+ /**
+ * Relesing a already released element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::release", __FILE__, __LINE__);
+#endif
+ noOfFree++;
+ return;
+ }
+ ErrorReporter::handleAssert("ArrayPool<T>::release", __FILE__, __LINE__);
+}
+
+/**
+ * Return an object to pool
+ */
+template <class T>
+inline
+void
+ArrayPool<T>::release(Ptr<T> & ptr){
+ Uint32 i = ptr.i;
+ if(i < size){
+ Uint32 ff = firstFree;
+ theArray[i].nextPool = ff;
+ firstFree = i;
+
+#ifdef ARRAY_GUARD
+ if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)){
+ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask, i);
+ //assert(noOfFree() == noOfFree2());
+ noOfFree++;
+ return;
+ }
+ /**
+ * Relesing a already released element
+ */
+ ErrorReporter::handleAssert("ArrayPool<T>::release", __FILE__, __LINE__);
+#endif
+ noOfFree++;
+ return;
+ }
+ ErrorReporter::handleAssert("ArrayPool<T>::release", __FILE__, __LINE__);
+}
+
+template <class T>
+class UnsafeArrayPool : public ArrayPool<T> {
+public:
+ /**
+ * Update p value for ptr according to i value
+ * ignore if it's allocated or not
+ */
+ void getPtrForce(Ptr<T> &);
+ void getPtrForce(ConstPtr<T> &) const;
+ T * getPtrForce(Uint32 i);
+ const T * getConstPtrForce(Uint32 i) const;
+ void getPtrForce(Ptr<T> &, Uint32 i);
+ void getPtrForce(ConstPtr<T> &, Uint32 i) const;
+};
+
+template <class T>
+inline
+void
+UnsafeArrayPool<T>::getPtrForce(Ptr<T> & ptr){
+ Uint32 i = ptr.i;
+ if(i < this->size){
+ ptr.p = &this->theArray[i];
+ } else {
+ ErrorReporter::handleAssert("UnsafeArrayPool<T>::getPtr",
+ __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+void
+UnsafeArrayPool<T>::getPtrForce(ConstPtr<T> & ptr) const{
+ Uint32 i = ptr.i;
+ if(i < this->size){
+ ptr.p = &this->theArray[i];
+ } else {
+ ErrorReporter::handleAssert("UnsafeArrayPool<T>::getPtr",
+ __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+T *
+UnsafeArrayPool<T>::getPtrForce(Uint32 i){
+ if(i < this->size){
+ return &this->theArray[i];
+ } else {
+ ErrorReporter::handleAssert("UnsafeArrayPool<T>::getPtr",
+ __FILE__, __LINE__);
+ return 0;
+ }
+}
+
+template <class T>
+inline
+const T *
+UnsafeArrayPool<T>::getConstPtrForce(Uint32 i) const {
+ if(i < this->size){
+ return &this->theArray[i];
+ } else {
+ ErrorReporter::handleAssert("UnsafeArrayPool<T>::getPtr",
+ __FILE__, __LINE__);
+ return 0;
+ }
+}
+
+template <class T>
+inline
+void
+UnsafeArrayPool<T>::getPtrForce(Ptr<T> & ptr, Uint32 i){
+ ptr.i = i;
+ if(i < this->size){
+ ptr.p = &this->theArray[i];
+ return ;
+ } else {
+ ErrorReporter::handleAssert("UnsafeArrayPool<T>::getPtr",
+ __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+void
+UnsafeArrayPool<T>::getPtrForce(ConstPtr<T> & ptr, Uint32 i) const{
+ ptr.i = i;
+ if(i < this->size){
+ ptr.p = &this->theArray[i];
+ return ;
+ } else {
+ ErrorReporter::handleAssert("UnsafeArrayPool<T>::getPtr",
+ __FILE__, __LINE__);
+ }
+}
+
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/CArray.hpp b/storage/ndb/src/kernel/vm/CArray.hpp
new file mode 100644
index 00000000000..a6e84e2c041
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/CArray.hpp
@@ -0,0 +1,141 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CARRAY_HPP
+#define CARRAY_HPP
+
+/**
+ * Template class used for implementing an c - array
+ */
+template <class T>
+class CArray {
+public:
+ CArray();
+ ~CArray();
+
+ /**
+ * Set the size of the pool
+ *
+ * Note, can currently only be called once
+ */
+ bool setSize(Uint32 noOfElements);
+
+ /**
+ * Get size
+ */
+ Uint32 getSize() const;
+
+ /**
+ * Update p value for ptr according to i value
+ */
+ void getPtr(Ptr<T> &) const;
+
+ /**
+ * Get pointer for i value
+ */
+ T * getPtr(Uint32 i) const;
+
+ /**
+ * Update p & i value for ptr according to <b>i</b> value
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+private:
+ Uint32 size;
+ T * theArray;
+};
+
+template <class T>
+inline
+CArray<T>::CArray(){
+ size = 0;
+ theArray = 0;
+}
+
+template <class T>
+inline
+CArray<T>::~CArray(){
+ if(theArray != 0){
+ NdbMem_Free(theArray);
+ theArray = 0;
+ }
+}
+
+/**
+ * Set the size of the pool
+ *
+ * Note, can currently only be called once
+ */
+template <class T>
+inline
+bool
+CArray<T>::setSize(Uint32 noOfElements){
+ if(size == noOfElements)
+ return true;
+
+ theArray = (T *)NdbMem_Allocate(noOfElements * sizeof(T));
+ if(theArray == 0)
+ return false;
+ size = noOfElements;
+ return true;
+}
+
+template<class T>
+inline
+Uint32
+CArray<T>::getSize() const {
+ return size;
+}
+
+template <class T>
+inline
+void
+CArray<T>::getPtr(Ptr<T> & ptr) const {
+ const Uint32 i = ptr.i;
+ if(i < size){
+ ptr.p = &theArray[i];
+ return;
+ } else {
+ ErrorReporter::handleAssert("CArray<T>::getPtr", __FILE__, __LINE__);
+ }
+}
+
+template <class T>
+inline
+T *
+CArray<T>::getPtr(Uint32 i) const {
+ if(i < size){
+ return &theArray[i];
+ } else {
+ ErrorReporter::handleAssert("CArray<T>::getPtr", __FILE__, __LINE__);
+ return 0;
+ }
+}
+
+template <class T>
+inline
+void
+CArray<T>::getPtr(Ptr<T> & ptr, Uint32 i) const {
+ ptr.i = i;
+ if(i < size){
+ ptr.p = &theArray[i];
+ return;
+ } else {
+ ErrorReporter::handleAssert("CArray<T>::getPtr", __FILE__, __LINE__);
+ }
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/Callback.hpp b/storage/ndb/src/kernel/vm/Callback.hpp
new file mode 100644
index 00000000000..6a619ba7859
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Callback.hpp
@@ -0,0 +1,24 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 BLOCK_CALLBACK_HPP
+#define BLOCK_CALLBACK_HPP
+
+/**
+ * Block callbacks
+ */
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/ClusterConfiguration.cpp b/storage/ndb/src/kernel/vm/ClusterConfiguration.cpp
new file mode 100644
index 00000000000..d5bd03f69d5
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ClusterConfiguration.cpp
@@ -0,0 +1,484 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "ClusterConfiguration.hpp"
+#include <ErrorHandlingMacros.hpp>
+
+#include <pc.hpp>
+#include <BlockNumbers.h>
+#include <signaldata/AccSizeAltReq.hpp>
+#include <signaldata/DictSizeAltReq.hpp>
+#include <signaldata/DihSizeAltReq.hpp>
+#include <signaldata/LqhSizeAltReq.hpp>
+#include <signaldata/TcSizeAltReq.hpp>
+#include <signaldata/TupSizeAltReq.hpp>
+#include <signaldata/TuxSizeAltReq.hpp>
+
+ClusterConfiguration::ClusterConfiguration()
+{
+ for (unsigned i= 0; i< MAX_SIZEALT_BLOCKS; i++) // initialize
+ for (unsigned j= 0; j< MAX_SIZEALT_RECORD; j++) {
+ the_clusterData.SizeAltData.varSize[i][j].valid = false;
+ the_clusterData.SizeAltData.varSize[i][j].nrr = 0;
+ }
+
+ for (unsigned i1 = 0; i1< 5; i1++) // initialize
+ for (unsigned j1= 0; j1< CmvmiCfgConf::NO_OF_WORDS; j1++)
+ the_clusterData.ispValues[i1][j1] = 0;
+
+ the_clusterData.SizeAltData.noOfNodes = 0;
+ the_clusterData.SizeAltData.noOfNDBNodes = 0;
+ the_clusterData.SizeAltData.noOfAPINodes = 0;
+ the_clusterData.SizeAltData.noOfMGMNodes = 0;
+}
+
+ClusterConfiguration::~ClusterConfiguration(){
+}
+
+void
+setValue(VarSize* dst, const int index, UintR variableValue){
+ assert(dst != NULL);
+ assert(index >= 0 && index < MAX_SIZEALT_RECORD);
+
+ dst[index].nrr = variableValue;
+ dst[index].valid = true;
+}
+
+void
+ClusterConfiguration::calcSizeAlteration()
+{
+ SizeAlt *size = &the_clusterData.SizeAltData;
+
+ size->noOfTables++; // Remove impact of system table
+ size->noOfTables += size->noOfIndexes; // Indexes are tables too
+ size->noOfAttributes += 2; // ---"----
+
+ size->noOfTables *= 2; // Remove impact of Dict need 2 ids for each table
+
+ Uint32 noOfDBNodes = size->noOfNDBNodes;
+ if (noOfDBNodes > 15) {
+ noOfDBNodes = 15;
+ }//if
+ Uint32 noOfLocalScanRecords = (noOfDBNodes * size->noOfScanRecords) + 1;
+ Uint32 noOfTCScanRecords = size->noOfScanRecords;
+ {
+ /**
+ * Acc Size Alt values
+ */
+ size->blockNo[ACC] = DBACC;
+
+ VarSize * const acc = &(size->varSize[ACC][0]);
+
+ // Can keep 65536 pages (= 0.5 GByte)
+ setValue(acc, AccSizeAltReq::IND_DIR_RANGE,
+ 4 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(acc, AccSizeAltReq::IND_DIR_ARRAY,
+ (size->noOfIndexPages >> 8) +
+ 4 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(acc, AccSizeAltReq::IND_FRAGMENT,
+ 2 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ /*-----------------------------------------------------------------------*/
+ // The extra operation records added are used by the scan and node
+ // recovery process.
+ // Node recovery process will have its operations dedicated to ensure
+ // that they never have a problem with allocation of the operation record.
+ // The remainder are allowed for use by the scan processes.
+ /*-----------------------------------------------------------------------*/
+ setValue(acc, AccSizeAltReq::IND_OP_RECS,
+ size->noOfReplicas*((16 * size->noOfOperations) / 10 + 50) +
+ (noOfLocalScanRecords * MAX_PARALLEL_SCANS_PER_FRAG) +
+ NODE_RECOVERY_SCAN_OP_RECORDS);
+
+ setValue(acc, AccSizeAltReq::IND_OVERFLOW_RECS,
+ size->noOfIndexPages +
+ 2 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(acc, AccSizeAltReq::IND_PAGE8,
+ size->noOfIndexPages + 32);
+
+ setValue(acc, AccSizeAltReq::IND_ROOT_FRAG,
+ NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(acc, AccSizeAltReq::IND_TABLE,
+ size->noOfTables);
+
+ setValue(acc, AccSizeAltReq::IND_SCAN,
+ noOfLocalScanRecords);
+ }
+
+ {
+ /**
+ * Dict Size Alt values
+ */
+ size->blockNo[DICT] = DBDICT;
+
+ VarSize * const dict = &(size->varSize[DICT][0]);
+
+ setValue(dict, DictSizeAltReq::IND_ATTRIBUTE,
+ size->noOfAttributes);
+
+ setValue(dict, DictSizeAltReq::IND_CONNECT,
+ size->noOfOperations + 32);
+
+ setValue(dict, DictSizeAltReq::IND_FRAG_CONNECT,
+ NO_OF_FRAG_PER_NODE * size->noOfNDBNodes * size->noOfReplicas);
+
+ setValue(dict, DictSizeAltReq::IND_TABLE,
+ size->noOfTables);
+
+ setValue(dict, DictSizeAltReq::IND_TC_CONNECT,
+ 2* size->noOfOperations);
+ }
+
+ {
+ /**
+ * Dih Size Alt values
+ */
+ size->blockNo[DIH] = DBDIH;
+
+ VarSize * const dih = &(size->varSize[DIH][0]);
+
+ setValue(dih, DihSizeAltReq::IND_API_CONNECT,
+ 2 * size->noOfTransactions);
+
+ setValue(dih, DihSizeAltReq::IND_CONNECT,
+ size->noOfOperations + 46);
+
+ setValue(dih, DihSizeAltReq::IND_FRAG_CONNECT,
+ NO_OF_FRAG_PER_NODE * size->noOfTables * size->noOfNDBNodes);
+
+ int temp;
+ temp = size->noOfReplicas - 2;
+ if (temp < 0)
+ temp = 1;
+ else
+ temp++;
+ setValue(dih, DihSizeAltReq::IND_MORE_NODES,
+ temp * NO_OF_FRAG_PER_NODE *
+ size->noOfTables * size->noOfNDBNodes);
+
+ setValue(dih, DihSizeAltReq::IND_REPLICAS,
+ NO_OF_FRAG_PER_NODE * size->noOfTables *
+ size->noOfNDBNodes * size->noOfReplicas);
+
+ setValue(dih, DihSizeAltReq::IND_TABLE,
+ size->noOfTables);
+ }
+
+ {
+ /**
+ * Lqh Size Alt values
+ */
+ size->blockNo[LQH] = DBLQH;
+
+ VarSize * const lqh = &(size->varSize[LQH][0]);
+
+ setValue(lqh, LqhSizeAltReq::IND_FRAG,
+ NO_OF_FRAG_PER_NODE * size->noOfTables * size->noOfReplicas);
+
+ setValue(lqh, LqhSizeAltReq::IND_CONNECT,
+ size->noOfReplicas*((11 * size->noOfOperations) / 10 + 50));
+
+ setValue(lqh, LqhSizeAltReq::IND_TABLE,
+ size->noOfTables);
+
+ setValue(lqh, LqhSizeAltReq::IND_TC_CONNECT,
+ size->noOfReplicas*((16 * size->noOfOperations) / 10 + 50));
+
+ setValue(lqh, LqhSizeAltReq::IND_REPLICAS,
+ size->noOfReplicas);
+
+ setValue(lqh, LqhSizeAltReq::IND_LOG_FILES,
+ (4 * the_clusterData.ispValues[1][4]));
+
+ setValue(lqh, LqhSizeAltReq::IND_SCAN,
+ noOfLocalScanRecords);
+
+ }
+
+ {
+ /**
+ * Tc Size Alt values
+ */
+ size->blockNo[TC] = DBTC;
+
+ VarSize * const tc = &(size->varSize[TC][0]);
+
+ setValue(tc, TcSizeAltReq::IND_API_CONNECT,
+ 3 * size->noOfTransactions);
+
+ setValue(tc, TcSizeAltReq::IND_TC_CONNECT,
+ size->noOfOperations + 16 + size->noOfTransactions);
+
+ setValue(tc, TcSizeAltReq::IND_TABLE,
+ size->noOfTables);
+
+ setValue(tc, TcSizeAltReq::IND_LOCAL_SCAN,
+ noOfLocalScanRecords);
+
+ setValue(tc, TcSizeAltReq::IND_TC_SCAN,
+ noOfTCScanRecords);
+ }
+
+ {
+ /**
+ * Tup Size Alt values
+ */
+ size->blockNo[TUP] = DBTUP;
+
+ VarSize * const tup = &(size->varSize[TUP][0]);
+
+ setValue(tup, TupSizeAltReq::IND_DISK_PAGE_ARRAY,
+ 2 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(tup, TupSizeAltReq::IND_DISK_PAGE_REPRESENT,
+ size->noOfDiskClusters);
+
+ setValue(tup, TupSizeAltReq::IND_FRAG,
+ 2 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(tup, TupSizeAltReq::IND_PAGE_CLUSTER,
+ size->noOfFreeClusters);
+
+ setValue(tup, TupSizeAltReq::IND_LOGIC_PAGE,
+ size->noOfDiskBufferPages + size->noOfDiskClusters);
+
+ setValue(tup, TupSizeAltReq::IND_OP_RECS,
+ size->noOfReplicas*((16 * size->noOfOperations) / 10 + 50));
+
+ setValue(tup, TupSizeAltReq::IND_PAGE,
+ size->noOfDataPages);
+
+ setValue(tup, TupSizeAltReq::IND_PAGE_RANGE,
+ 4 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas);
+
+ setValue(tup, TupSizeAltReq::IND_TABLE,
+ size->noOfTables);
+
+ setValue(tup, TupSizeAltReq::IND_TABLE_DESC,
+ 4 * NO_OF_FRAG_PER_NODE * size->noOfAttributes* size->noOfReplicas +
+ 12 * NO_OF_FRAG_PER_NODE * size->noOfTables* size->noOfReplicas );
+
+ setValue(tup, TupSizeAltReq::IND_DELETED_BLOCKS,
+ size->noOfFreeClusters);
+
+ setValue(tup, TupSizeAltReq::IND_STORED_PROC,
+ noOfLocalScanRecords);
+ }
+
+ {
+ /**
+ * Tux Size Alt values
+ */
+ size->blockNo[TUX] = DBTUX;
+
+ VarSize * const tux = &(size->varSize[TUX][0]);
+
+ setValue(tux, TuxSizeAltReq::IND_INDEX,
+ size->noOfTables);
+
+ setValue(tux, TuxSizeAltReq::IND_FRAGMENT,
+ 2 * NO_OF_FRAG_PER_NODE * size->noOfTables * size->noOfReplicas);
+
+ setValue(tux, TuxSizeAltReq::IND_ATTRIBUTE,
+ size->noOfIndexes * 4);
+
+ setValue(tux, TuxSizeAltReq::IND_SCAN,
+ noOfLocalScanRecords);
+ }
+}
+
+const ClusterConfiguration::ClusterData&
+ClusterConfiguration::clusterData() const
+{
+ return the_clusterData;
+}
+
+void ClusterConfiguration::init(const Properties & p, const Properties & db){
+ const char * msg = "Invalid configuration fetched";
+
+ ClusterData & cd = the_clusterData;
+
+ struct AttribStorage { const char * attrib; Uint32 * storage; };
+ AttribStorage tmp[] = {
+ {"MaxNoOfConcurrentScans", &cd.SizeAltData.noOfScanRecords },
+ {"MaxNoOfTables", &cd.SizeAltData.noOfTables },
+ {"MaxNoOfIndexes", &cd.SizeAltData.noOfIndexes },
+ {"NoOfReplicas", &cd.SizeAltData.noOfReplicas },
+ {"MaxNoOfAttributes", &cd.SizeAltData.noOfAttributes },
+ {"MaxNoOfConcurrentOperations", &cd.SizeAltData.noOfOperations },
+ {"MaxNoOfConcurrentTransactions", &cd.SizeAltData.noOfTransactions },
+ {"NoOfIndexPages", &cd.SizeAltData.noOfIndexPages },
+ {"NoOfDataPages", &cd.SizeAltData.noOfDataPages },
+ {"NoOfDiskBufferPages", &cd.SizeAltData.noOfDiskBufferPages },
+ {"NoOfDiskClusters", &cd.SizeAltData.noOfDiskClusters },
+ {"NoOfFreeDiskClusters", &cd.SizeAltData.noOfFreeClusters },
+ {"TimeToWaitAlive", &cd.ispValues[0][0] },
+ {"HeartbeatIntervalDbDb", &cd.ispValues[0][2] },
+ {"HeartbeatIntervalDbApi", &cd.ispValues[0][3] },
+ {"ArbitrationTimeout", &cd.ispValues[0][5] },
+ {"TimeBetweenLocalCheckpoints", &cd.ispValues[1][2] },
+ {"NoOfFragmentLogFiles", &cd.ispValues[1][4] },
+ {"MaxNoOfConcurrentScans", &cd.SizeAltData.noOfScanRecords },
+ {"NoOfConcurrentCheckpointsDuringRestart", &cd.ispValues[1][5] },
+ {"TransactionDeadlockDetectionTimeout", &cd.ispValues[1][6] },
+ {"NoOfConcurrentProcessesHandleTakeover", &cd.ispValues[1][7] },
+ {"TimeBetweenGlobalCheckpoints", &cd.ispValues[2][3] },
+ {"NoOfConcurrentCheckpointsAfterRestart", &cd.ispValues[2][4] },
+ {"TransactionInactiveTimeout", &cd.ispValues[2][7] },
+ {"NoOfDiskPagesToDiskDuringRestartTUP", &cd.ispValues[3][8] },
+ {"NoOfDiskPagesToDiskAfterRestartTUP", &cd.ispValues[3][9] },
+ {"NoOfDiskPagesToDiskDuringRestartACC", &cd.ispValues[3][10] },
+ {"NoOfDiskPagesToDiskAfterRestartACC", &cd.ispValues[3][11] },
+ {"NoOfDiskClustersPerDiskFile", &cd.ispValues[4][8] },
+ {"NoOfDiskFiles", &cd.ispValues[4][9] },
+ {"NoOfReplicas", &cd.ispValues[2][2] }
+ };
+
+
+ const int sz = sizeof(tmp)/sizeof(AttribStorage);
+ for(int i = 0; i<sz; i++){
+ if(!db.get(tmp[i].attrib, tmp[i].storage)){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf), "%s not found", tmp[i].attrib);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ }
+
+ if(!p.get("NoOfNodes", &cd.SizeAltData.noOfNodes)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, "NoOfNodes missing");
+ }
+
+ Properties::Iterator it(&p);
+ const char * name = 0;
+ Uint32 nodeNo = 0;
+ for(name = it.first(); name != NULL; name = it.next()){
+ if(strncmp(name, "Node_", strlen("Node_")) == 0){
+
+ Uint32 nodeId;
+ const char * nodeType;
+ const Properties * node;
+
+ if(!p.get(name, &node)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, "Node data missing");
+ }
+
+ if(!node->get("Id", &nodeId)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, "Node data (Id) missing");
+ }
+
+ if(!node->get("Type", &nodeType)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, "Node data (Type) missing");
+ }
+
+ if(nodeId > MAX_NODES){
+ char buf[255];
+ snprintf(buf, sizeof(buf),
+ "Maximum DB node id allowed is: %d", MAX_NDB_NODES);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+
+ if(nodeId == 0){
+ char buf[255];
+ snprintf(buf, sizeof(buf),
+ "Minimum node id allowed in the cluster is: 1");
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+
+ for(unsigned j = 0; j<nodeNo; j++){
+ if(cd.nodeData[j].nodeId == nodeId){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf), "Two node can not have the same node id");
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ }
+
+ {
+ for(unsigned j = 0; j<LogLevel::LOGLEVEL_CATEGORIES; j++){
+ Uint32 logLevel;
+ if(db.get(LogLevel::LOGLEVEL_CATEGORY_NAME[j].name, &logLevel)){
+ cd.SizeAltData.logLevel.setLogLevel((LogLevel::EventCategory)j,
+ logLevel);
+ }
+ }
+ }
+
+ cd.nodeData[nodeNo].nodeId = nodeId;
+ const char* tmpApiMgmProperties = 0;
+ if(strcmp("DB", nodeType) == 0){
+ cd.nodeData[nodeNo].nodeType = NodeInfo::DB;
+ cd.SizeAltData.noOfNDBNodes++; // No of NDB processes
+
+ if(nodeId > MAX_NDB_NODES){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf), "Maximum node id for a ndb node is: %d", MAX_NDB_NODES);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ if(cd.SizeAltData.noOfNDBNodes > MAX_NDB_NODES){
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf),
+ "Maximum %d ndb nodes is allowed in the cluster",
+ MAX_NDB_NODES);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ } else if(strcmp("API", nodeType) == 0){
+ cd.nodeData[nodeNo].nodeType = NodeInfo::API;
+ cd.SizeAltData.noOfAPINodes++; // No of API processes
+ tmpApiMgmProperties = "API";
+ } else if(strcmp("REP", nodeType) == 0){
+ cd.nodeData[nodeNo].nodeType = NodeInfo::REP;
+ //cd.SizeAltData.noOfAPINodes++; // No of API processes
+ tmpApiMgmProperties = "REP";
+ } else if(strcmp("MGM", nodeType) == 0){
+ cd.nodeData[nodeNo].nodeType = NodeInfo::MGM;
+ cd.SizeAltData.noOfMGMNodes++; // No of MGM processes
+ tmpApiMgmProperties = "MGM";
+ } else {
+ ERROR_SET(fatal, ERR_INVALID_CONFIG,
+ "Invalid configuration: Unknown node type",
+ nodeType);
+ }
+
+ if (tmpApiMgmProperties) {
+ /*
+ const Properties* q = 0;
+
+ if (!p.get(tmpApiMgmProperties, nodeId, &q)) {
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, tmpApiMgmProperties);
+ } else {
+ */
+ Uint32 rank = 0;
+ if (node->get("ArbitrationRank", &rank) && rank > 0) {
+ cd.nodeData[nodeNo].arbitRank = rank;
+ // }
+ }
+ } else {
+ cd.nodeData[nodeNo].arbitRank = 0;
+ }
+
+ nodeNo++;
+ }
+ }
+ cd.SizeAltData.exist = true;
+ calcSizeAlteration();
+}
+
+
diff --git a/storage/ndb/src/kernel/vm/ClusterConfiguration.hpp b/storage/ndb/src/kernel/vm/ClusterConfiguration.hpp
new file mode 100644
index 00000000000..cc7000a54ef
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ClusterConfiguration.hpp
@@ -0,0 +1,105 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ClusterConfiguration_H
+#define ClusterConfiguration_H
+
+#include <kernel_types.h>
+#include <ndb_limits.h>
+#include <Properties.hpp>
+#include <ErrorReporter.hpp>
+#include <signaldata/CmvmiCfgConf.hpp>
+#include <signaldata/SetLogLevelOrd.hpp>
+#include <NodeInfo.hpp>
+
+// MaxNumber of sizealteration records in each block
+// MaxNumber of blocks with sizealteration, (size of array)
+#define MAX_SIZEALT_RECORD 16
+#define MAX_SIZEALT_BLOCKS 8
+
+enum NdbBlockName { ACC = 0, DICT, DIH, LQH, TC, TUP, TUX, NDB_SIZEALT_OFF };
+// NDB_SIZEALT_OFF is used for block without sizealteration
+// IMPORTANT to assign NDB_SIZEALT_OFF as largest value
+
+struct VarSize {
+ int nrr;
+ bool valid;
+};
+
+struct SizeAlt {
+ unsigned int noOfTables;
+ unsigned int noOfIndexes;
+ unsigned int noOfReplicas;
+ unsigned int noOfNDBNodes;
+ unsigned int noOfAPINodes;
+ unsigned int noOfMGMNodes;
+ unsigned int noOfNodes;
+ unsigned int noOfDiskLessNodes;
+ unsigned int noOfAttributes;
+ unsigned int noOfOperations;
+ unsigned int noOfTransactions;
+ unsigned int noOfIndexPages;
+ unsigned int noOfDataPages;
+ unsigned int noOfDiskBufferPages;
+ unsigned int noOfFreeClusters;
+ unsigned int noOfDiskClusters;
+ unsigned int noOfScanRecords;
+ bool exist;
+ VarSize varSize[MAX_SIZEALT_BLOCKS][MAX_SIZEALT_RECORD];
+ unsigned short blockNo[MAX_SIZEALT_BLOCKS];
+ LogLevel logLevel;
+};
+
+
+class ClusterConfiguration
+{
+public:
+
+ struct NodeData {
+ NodeData() {
+ nodeId = MAX_NODES+1;
+ nodeType = NodeInfo::INVALID;
+ arbitRank = ~0;
+ }
+ NodeId nodeId;
+ NodeInfo::NodeType nodeType;
+ unsigned arbitRank;
+ };
+
+ struct ClusterData
+ {
+ SizeAlt SizeAltData;
+ NodeData nodeData[MAX_NODES];
+ Uint32 ispValues[5][CmvmiCfgConf::NO_OF_WORDS];
+ };
+
+ ClusterConfiguration();
+ ~ClusterConfiguration();
+ const ClusterData& clusterData() const;
+
+ void init(const Properties & p, const Properties & db);
+protected:
+
+private:
+
+ ClusterData the_clusterData;
+
+ void calcSizeAlteration();
+
+};
+
+#endif // ClusterConfiguration_H
+
diff --git a/storage/ndb/src/kernel/vm/Configuration.cpp b/storage/ndb/src/kernel/vm/Configuration.cpp
new file mode 100644
index 00000000000..650d914035f
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Configuration.cpp
@@ -0,0 +1,801 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include "Configuration.hpp"
+#include <ErrorHandlingMacros.hpp>
+#include "GlobalData.hpp"
+
+#include <ConfigRetriever.hpp>
+#include <IPCConfig.hpp>
+#include <ndb_version.h>
+#include <NdbMem.h>
+#include <NdbOut.hpp>
+#include <WatchDog.hpp>
+
+#include <mgmapi_configuration.hpp>
+#include <mgmapi_config_parameters_debug.h>
+#include <kernel_config_parameters.h>
+
+#include <kernel_types.h>
+#include <ndb_limits.h>
+#include <ndbapi_limits.h>
+#include "pc.hpp"
+#include <LogLevel.hpp>
+#include <NdbSleep.h>
+
+extern "C" {
+ void ndbSetOwnVersion();
+}
+
+#include <EventLogger.hpp>
+extern EventLogger g_eventLogger;
+
+enum ndbd_options {
+ OPT_INITIAL = NDB_STD_OPTIONS_LAST,
+ OPT_NODAEMON
+};
+
+NDB_STD_OPTS_VARS;
+static int _daemon, _no_daemon, _initial, _no_start;
+/**
+ * Arguments to NDB process
+ */
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndbd"),
+ { "initial", OPT_INITIAL,
+ "Perform initial start of ndbd, including cleaning the file system. "
+ "Consult documentation before using this",
+ (gptr*) &_initial, (gptr*) &_initial, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "nostart", 'n',
+ "Don't start ndbd immediately. Ndbd will await command from ndb_mgmd",
+ (gptr*) &_no_start, (gptr*) &_no_start, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "daemon", 'd', "Start ndbd as daemon (default)",
+ (gptr*) &_daemon, (gptr*) &_daemon, 0,
+ GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
+ { "nodaemon", OPT_NODAEMON,
+ "Do not start ndbd as daemon, provided for testing purposes",
+ (gptr*) &_no_daemon, (gptr*) &_no_daemon, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void short_usage_sub(void)
+{
+ printf("Usage: %s [OPTIONS]\n", my_progname);
+}
+static void usage()
+{
+ short_usage_sub();
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+bool
+Configuration::init(int argc, char** argv)
+{
+ const char *load_default_groups[]= { "mysql_cluster","ndbd",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndbd.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ exit(ho_error);
+
+ if (_no_daemon) {
+ _daemon= 0;
+ }
+
+ DBUG_PRINT("info", ("no_start=%d", _no_start));
+ DBUG_PRINT("info", ("initial=%d", _initial));
+ DBUG_PRINT("info", ("daemon=%d", _daemon));
+ DBUG_PRINT("info", ("connect_str=%s", opt_connect_str));
+
+ ndbSetOwnVersion();
+
+ // Check the start flag
+ if (_no_start)
+ globalData.theRestartFlag = initial_state;
+ else
+ globalData.theRestartFlag = perform_start;
+
+ // Check the initial flag
+ if (_initial)
+ _initialStart = true;
+
+ // Check connectstring
+ if (opt_connect_str)
+ _connectString = strdup(opt_connect_str);
+
+ // Check daemon flag
+ if (_daemon)
+ _daemonMode = true;
+
+ // Save programname
+ if(argc > 0 && argv[0] != 0)
+ _programName = strdup(argv[0]);
+ else
+ _programName = strdup("");
+
+ globalData.ownId= 0;
+
+ return true;
+}
+
+Configuration::Configuration()
+{
+ _programName = 0;
+ _connectString = 0;
+ _fsPath = 0;
+ _backupPath = 0;
+ _initialStart = false;
+ _daemonMode = false;
+ m_config_retriever= 0;
+ m_clusterConfig= 0;
+ m_clusterConfigIter= 0;
+}
+
+Configuration::~Configuration(){
+ if(_programName != NULL)
+ free(_programName);
+
+ if(_fsPath != NULL)
+ free(_fsPath);
+
+ if(_backupPath != NULL)
+ free(_backupPath);
+
+ if (m_config_retriever) {
+ delete m_config_retriever;
+ }
+}
+
+void
+Configuration::closeConfiguration(){
+ if (m_config_retriever) {
+ delete m_config_retriever;
+ }
+ m_config_retriever= 0;
+}
+
+void
+Configuration::fetch_configuration(){
+ /**
+ * Fetch configuration from management server
+ */
+ if (m_config_retriever) {
+ delete m_config_retriever;
+ }
+
+ m_mgmd_port= 0;
+ m_config_retriever= new ConfigRetriever(getConnectString(),
+ NDB_VERSION, NODE_TYPE_DB);
+
+ if (m_config_retriever->hasError())
+ {
+ ERROR_SET(fatal, ERR_INVALID_CONFIG,
+ "Could not connect initialize handle to management server",
+ m_config_retriever->getErrorString());
+ }
+
+ if(m_config_retriever->do_connect(12,5,1) == -1){
+ const char * s = m_config_retriever->getErrorString();
+ if(s == 0)
+ s = "No error given!";
+ /* Set stop on error to true otherwise NDB will
+ go into an restart loop...
+ */
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Could not connect to ndb_mgmd", s);
+ }
+
+ m_mgmd_port= m_config_retriever->get_mgmd_port();
+ m_mgmd_host.assign(m_config_retriever->get_mgmd_host());
+
+ ConfigRetriever &cr= *m_config_retriever;
+
+ /**
+ * if we have a nodeid set (e.g in a restart situation)
+ * reuse it
+ */
+ if (globalData.ownId)
+ cr.setNodeId(globalData.ownId);
+
+ globalData.ownId = cr.allocNodeId(2 /*retry*/,3 /*delay*/);
+
+ if(globalData.ownId == 0){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG,
+ "Unable to alloc node id", m_config_retriever->getErrorString());
+ }
+
+ ndb_mgm_configuration * p = cr.getConfig();
+ if(p == 0){
+ const char * s = cr.getErrorString();
+ if(s == 0)
+ s = "No error given!";
+
+ /* Set stop on error to true otherwise NDB will
+ go into an restart loop...
+ */
+
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Could not fetch configuration"
+ "/invalid configuration", s);
+ }
+ if(m_clusterConfig)
+ free(m_clusterConfig);
+
+ m_clusterConfig = p;
+
+ ndb_mgm_configuration_iterator iter(* p, CFG_SECTION_NODE);
+ if (iter.find(CFG_NODE_ID, globalData.ownId)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched", "DB missing");
+ }
+
+ if(iter.get(CFG_DB_STOP_ON_ERROR, &_stopOnError)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "StopOnError missing");
+ }
+}
+
+static char * get_and_validate_path(ndb_mgm_configuration_iterator &iter,
+ Uint32 param, const char *param_string)
+{
+ const char* path = NULL;
+ if(iter.get(param, &path)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched missing ",
+ param_string);
+ }
+
+ if(path == 0 || strlen(path) == 0){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG,
+ "Invalid configuration fetched. Configuration does not contain valid ",
+ param_string);
+ }
+
+ // check that it is pointing on a valid directory
+ //
+ char buf2[PATH_MAX];
+ memset(buf2, 0,sizeof(buf2));
+#ifdef NDB_WIN32
+ char* szFilePart;
+ if(!GetFullPathName(path, sizeof(buf2), buf2, &szFilePart) ||
+ (GetFileAttributes(buf2) & FILE_ATTRIBUTE_READONLY))
+#else
+ if((::realpath(path, buf2) == NULL)||
+ (::access(buf2, W_OK) != 0))
+#endif
+ {
+ ERROR_SET(fatal, AFS_ERROR_INVALIDPATH, path, " Filename::init()");
+ }
+
+ if (strcmp(&buf2[strlen(buf2) - 1], DIR_SEPARATOR))
+ strcat(buf2, DIR_SEPARATOR);
+
+ return strdup(buf2);
+}
+
+void
+Configuration::setupConfiguration(){
+
+ DBUG_ENTER("Configuration::setupConfiguration");
+
+ ndb_mgm_configuration * p = m_clusterConfig;
+
+ /**
+ * Configure transporters
+ */
+ {
+ int res = IPCConfig::configureTransporters(globalData.ownId,
+ * p,
+ globalTransporterRegistry);
+ if(res <= 0){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "No transporters configured");
+ }
+ }
+
+ /**
+ * Setup cluster configuration data
+ */
+ ndb_mgm_configuration_iterator iter(* p, CFG_SECTION_NODE);
+ if (iter.find(CFG_NODE_ID, globalData.ownId)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched", "DB missing");
+ }
+
+ unsigned type;
+ if(!(iter.get(CFG_TYPE_OF_SECTION, &type) == 0 && type == NODE_TYPE_DB)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "I'm wrong type of node");
+ }
+
+ if(iter.get(CFG_DB_NO_SAVE_MSGS, &_maxErrorLogs)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "MaxNoOfSavedMessages missing");
+ }
+
+ if(iter.get(CFG_DB_MEMLOCK, &_lockPagesInMainMemory)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "LockPagesInMainMemory missing");
+ }
+
+ if(iter.get(CFG_DB_WATCHDOG_INTERVAL, &_timeBetweenWatchDogCheck)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "TimeBetweenWatchDogCheck missing");
+ }
+
+ /**
+ * Get paths
+ */
+ if (_fsPath)
+ free(_fsPath);
+ _fsPath= get_and_validate_path(iter, CFG_DB_FILESYSTEM_PATH, "FileSystemPath");
+ if (_backupPath)
+ free(_backupPath);
+ _backupPath= get_and_validate_path(iter, CFG_DB_BACKUP_DATADIR, "BackupDataDir");
+
+ if(iter.get(CFG_DB_STOP_ON_ERROR_INSERT, &m_restartOnErrorInsert)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, "Invalid configuration fetched",
+ "RestartOnErrorInsert missing");
+ }
+
+ /**
+ * Create the watch dog thread
+ */
+ {
+ Uint32 t = _timeBetweenWatchDogCheck;
+ t = globalEmulatorData.theWatchDog ->setCheckInterval(t);
+ _timeBetweenWatchDogCheck = t;
+ }
+
+ ConfigValues* cf = ConfigValuesFactory::extractCurrentSection(iter.m_config);
+
+ if(m_clusterConfigIter)
+ ndb_mgm_destroy_iterator(m_clusterConfigIter);
+ m_clusterConfigIter = ndb_mgm_create_configuration_iterator
+ (p, CFG_SECTION_NODE);
+
+ calcSizeAlt(cf);
+
+ DBUG_VOID_RETURN;
+}
+
+bool
+Configuration::lockPagesInMainMemory() const {
+ return _lockPagesInMainMemory;
+}
+
+int
+Configuration::timeBetweenWatchDogCheck() const {
+ return _timeBetweenWatchDogCheck;
+}
+
+void
+Configuration::timeBetweenWatchDogCheck(int value) {
+ _timeBetweenWatchDogCheck = value;
+}
+
+int
+Configuration::maxNoOfErrorLogs() const {
+ return _maxErrorLogs;
+}
+
+void
+Configuration::maxNoOfErrorLogs(int val){
+ _maxErrorLogs = val;
+}
+
+bool
+Configuration::stopOnError() const {
+ return _stopOnError;
+}
+
+void
+Configuration::stopOnError(bool val){
+ _stopOnError = val;
+}
+
+int
+Configuration::getRestartOnErrorInsert() const {
+ return m_restartOnErrorInsert;
+}
+
+void
+Configuration::setRestartOnErrorInsert(int i){
+ m_restartOnErrorInsert = i;
+}
+
+const char *
+Configuration::getConnectString() const {
+ return _connectString;
+}
+
+char *
+Configuration::getConnectStringCopy() const {
+ if(_connectString != 0)
+ return strdup(_connectString);
+ return 0;
+}
+
+const ndb_mgm_configuration_iterator *
+Configuration::getOwnConfigIterator() const {
+ return m_ownConfigIterator;
+}
+
+ndb_mgm_configuration_iterator *
+Configuration::getClusterConfigIterator() const {
+ return m_clusterConfigIter;
+}
+
+void
+Configuration::calcSizeAlt(ConfigValues * ownConfig){
+ const char * msg = "Invalid configuration fetched";
+ char buf[255];
+
+ unsigned int noOfTables = 0;
+ unsigned int noOfUniqueHashIndexes = 0;
+ unsigned int noOfOrderedIndexes = 0;
+ unsigned int noOfTriggers = 0;
+ unsigned int noOfReplicas = 0;
+ unsigned int noOfDBNodes = 0;
+ unsigned int noOfAPINodes = 0;
+ unsigned int noOfMGMNodes = 0;
+ unsigned int noOfNodes = 0;
+ unsigned int noOfAttributes = 0;
+ unsigned int noOfOperations = 0;
+ unsigned int noOfLocalOperations = 0;
+ unsigned int noOfTransactions = 0;
+ unsigned int noOfIndexPages = 0;
+ unsigned int noOfDataPages = 0;
+ unsigned int noOfScanRecords = 0;
+ unsigned int noOfLocalScanRecords = 0;
+ unsigned int noBatchSize = 0;
+ m_logLevel = new LogLevel();
+
+ struct AttribStorage { int paramId; Uint32 * storage; bool computable; };
+ AttribStorage tmp[] = {
+ { CFG_DB_NO_SCANS, &noOfScanRecords, false },
+ { CFG_DB_NO_LOCAL_SCANS, &noOfLocalScanRecords, true },
+ { CFG_DB_BATCH_SIZE, &noBatchSize, false },
+ { CFG_DB_NO_TABLES, &noOfTables, false },
+ { CFG_DB_NO_ORDERED_INDEXES, &noOfOrderedIndexes, false },
+ { CFG_DB_NO_UNIQUE_HASH_INDEXES, &noOfUniqueHashIndexes, false },
+ { CFG_DB_NO_TRIGGERS, &noOfTriggers, true },
+ { CFG_DB_NO_REPLICAS, &noOfReplicas, false },
+ { CFG_DB_NO_ATTRIBUTES, &noOfAttributes, false },
+ { CFG_DB_NO_OPS, &noOfOperations, false },
+ { CFG_DB_NO_LOCAL_OPS, &noOfLocalOperations, true },
+ { CFG_DB_NO_TRANSACTIONS, &noOfTransactions, false }
+ };
+
+ ndb_mgm_configuration_iterator db(*(ndb_mgm_configuration*)ownConfig, 0);
+
+ const int sz = sizeof(tmp)/sizeof(AttribStorage);
+ for(int i = 0; i<sz; i++){
+ if(ndb_mgm_get_int_parameter(&db, tmp[i].paramId, tmp[i].storage)){
+ if (tmp[i].computable) {
+ *tmp[i].storage = 0;
+ } else {
+ BaseString::snprintf(buf, sizeof(buf),"ConfigParam: %d not found", tmp[i].paramId);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ }
+ }
+
+ Uint64 indexMem = 0, dataMem = 0;
+ ndb_mgm_get_int64_parameter(&db, CFG_DB_DATA_MEM, &dataMem);
+ ndb_mgm_get_int64_parameter(&db, CFG_DB_INDEX_MEM, &indexMem);
+ if(dataMem == 0){
+ BaseString::snprintf(buf, sizeof(buf), "ConfigParam: %d not found", CFG_DB_DATA_MEM);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+
+ if(indexMem == 0){
+ BaseString::snprintf(buf, sizeof(buf), "ConfigParam: %d not found", CFG_DB_INDEX_MEM);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+
+ noOfDataPages = (dataMem / 32768);
+ noOfIndexPages = (indexMem / 8192);
+
+ for(unsigned j = 0; j<LogLevel::LOGLEVEL_CATEGORIES; j++){
+ Uint32 tmp;
+ if(!ndb_mgm_get_int_parameter(&db, CFG_MIN_LOGLEVEL+j, &tmp)){
+ m_logLevel->setLogLevel((LogLevel::EventCategory)j, tmp);
+ }
+ }
+
+ // tmp
+ ndb_mgm_configuration_iterator * p = m_clusterConfigIter;
+
+ Uint32 nodeNo = noOfNodes = 0;
+ NodeBitmask nodes;
+ for(ndb_mgm_first(p); ndb_mgm_valid(p); ndb_mgm_next(p), nodeNo++){
+
+ Uint32 nodeId;
+ Uint32 nodeType;
+
+ if(ndb_mgm_get_int_parameter(p, CFG_NODE_ID, &nodeId)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, "Node data (Id) missing");
+ }
+
+ if(ndb_mgm_get_int_parameter(p, CFG_TYPE_OF_SECTION, &nodeType)){
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, "Node data (Type) missing");
+ }
+
+ if(nodeId > MAX_NODES || nodeId == 0){
+ BaseString::snprintf(buf, sizeof(buf),
+ "Invalid node id: %d", nodeId);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+
+ if(nodes.get(nodeId)){
+ BaseString::snprintf(buf, sizeof(buf), "Two node can not have the same node id: %d",
+ nodeId);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ nodes.set(nodeId);
+
+ switch(nodeType){
+ case NODE_TYPE_DB:
+ noOfDBNodes++; // No of NDB processes
+
+ if(nodeId > MAX_NDB_NODES){
+ BaseString::snprintf(buf, sizeof(buf), "Maximum node id for a ndb node is: %d",
+ MAX_NDB_NODES);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ break;
+ case NODE_TYPE_API:
+ noOfAPINodes++; // No of API processes
+ break;
+ case NODE_TYPE_REP:
+ break;
+ case NODE_TYPE_MGM:
+ noOfMGMNodes++; // No of MGM processes
+ break;
+ case NODE_TYPE_EXT_REP:
+ break;
+ default:
+ BaseString::snprintf(buf, sizeof(buf), "Unknown node type: %d", nodeType);
+ ERROR_SET(fatal, ERR_INVALID_CONFIG, msg, buf);
+ }
+ }
+ noOfNodes = nodeNo;
+
+ noOfTables+= 2; // Add System tables
+ noOfAttributes += 9; // Add System table attributes
+
+ ConfigValues::Iterator it2(*ownConfig, db.m_config);
+ it2.set(CFG_DB_NO_TABLES, noOfTables);
+ it2.set(CFG_DB_NO_ATTRIBUTES, noOfAttributes);
+ {
+ Uint32 neededNoOfTriggers = /* types: Insert/Update/Delete/Custom */
+ 3 * noOfUniqueHashIndexes + /* for unique hash indexes, I/U/D */
+ 3 * NDB_MAX_ACTIVE_EVENTS + /* for events in suma, I/U/D */
+ 3 * noOfTables + /* for backup, I/U/D */
+ noOfOrderedIndexes; /* for ordered indexes, C */
+ if (noOfTriggers < neededNoOfTriggers)
+ {
+ noOfTriggers= neededNoOfTriggers;
+ it2.set(CFG_DB_NO_TRIGGERS, noOfTriggers);
+ }
+ }
+
+ /**
+ * Do size calculations
+ */
+ ConfigValuesFactory cfg(ownConfig);
+
+ Uint32 noOfMetaTables= noOfTables + noOfOrderedIndexes +
+ noOfUniqueHashIndexes;
+ Uint32 noOfMetaTablesDict= noOfMetaTables;
+ if (noOfMetaTablesDict > MAX_TABLES)
+ noOfMetaTablesDict= MAX_TABLES;
+
+ {
+ /**
+ * Dict Size Alt values
+ */
+ cfg.put(CFG_DICT_ATTRIBUTE,
+ noOfAttributes);
+
+ cfg.put(CFG_DICT_TABLE,
+ noOfMetaTablesDict);
+ }
+
+
+ if (noOfLocalScanRecords == 0) {
+ noOfLocalScanRecords = (noOfDBNodes * noOfScanRecords) + 1;
+ }
+ if (noOfLocalOperations == 0) {
+ noOfLocalOperations= (11 * noOfOperations) / 10;
+ }
+ Uint32 noOfTCScanRecords = noOfScanRecords;
+
+ {
+ Uint32 noOfAccTables= noOfMetaTables/*noOfTables+noOfUniqueHashIndexes*/;
+ /**
+ * Acc Size Alt values
+ */
+ // Can keep 65536 pages (= 0.5 GByte)
+ cfg.put(CFG_ACC_DIR_RANGE,
+ 4 * NO_OF_FRAG_PER_NODE * noOfAccTables* noOfReplicas);
+
+ cfg.put(CFG_ACC_DIR_ARRAY,
+ (noOfIndexPages >> 8) +
+ 4 * NO_OF_FRAG_PER_NODE * noOfAccTables* noOfReplicas);
+
+ cfg.put(CFG_ACC_FRAGMENT,
+ 2 * NO_OF_FRAG_PER_NODE * noOfAccTables* noOfReplicas);
+
+ /*-----------------------------------------------------------------------*/
+ // The extra operation records added are used by the scan and node
+ // recovery process.
+ // Node recovery process will have its operations dedicated to ensure
+ // that they never have a problem with allocation of the operation record.
+ // The remainder are allowed for use by the scan processes.
+ /*-----------------------------------------------------------------------*/
+ cfg.put(CFG_ACC_OP_RECS,
+ (noOfLocalOperations + 50) +
+ (noOfLocalScanRecords * noBatchSize) +
+ NODE_RECOVERY_SCAN_OP_RECORDS);
+
+ cfg.put(CFG_ACC_OVERFLOW_RECS,
+ noOfIndexPages +
+ 2 * NO_OF_FRAG_PER_NODE * noOfAccTables* noOfReplicas);
+
+ cfg.put(CFG_ACC_PAGE8,
+ noOfIndexPages + 32);
+
+ cfg.put(CFG_ACC_ROOT_FRAG,
+ NO_OF_FRAG_PER_NODE * noOfAccTables* noOfReplicas);
+
+ cfg.put(CFG_ACC_TABLE, noOfAccTables);
+
+ cfg.put(CFG_ACC_SCAN, noOfLocalScanRecords);
+ }
+
+ {
+ /**
+ * Dih Size Alt values
+ */
+ cfg.put(CFG_DIH_API_CONNECT,
+ 2 * noOfTransactions);
+
+ cfg.put(CFG_DIH_CONNECT,
+ noOfOperations + noOfTransactions + 46);
+
+ Uint32 noFragPerTable= ((noOfDBNodes + NO_OF_FRAGS_PER_CHUNK - 1) >>
+ LOG_NO_OF_FRAGS_PER_CHUNK) <<
+ LOG_NO_OF_FRAGS_PER_CHUNK;
+
+ cfg.put(CFG_DIH_FRAG_CONNECT,
+ noFragPerTable * noOfMetaTables);
+
+ int temp;
+ temp = noOfReplicas - 2;
+ if (temp < 0)
+ temp = 1;
+ else
+ temp++;
+ cfg.put(CFG_DIH_MORE_NODES,
+ temp * NO_OF_FRAG_PER_NODE *
+ noOfMetaTables * noOfDBNodes);
+
+ cfg.put(CFG_DIH_REPLICAS,
+ NO_OF_FRAG_PER_NODE * noOfMetaTables *
+ noOfDBNodes * noOfReplicas);
+
+ cfg.put(CFG_DIH_TABLE,
+ noOfMetaTables);
+ }
+
+ {
+ /**
+ * Lqh Size Alt values
+ */
+ cfg.put(CFG_LQH_FRAG,
+ NO_OF_FRAG_PER_NODE * noOfMetaTables * noOfReplicas);
+
+ cfg.put(CFG_LQH_TABLE,
+ noOfMetaTables);
+
+ cfg.put(CFG_LQH_TC_CONNECT,
+ noOfLocalOperations + 50);
+
+ cfg.put(CFG_LQH_SCAN,
+ noOfLocalScanRecords);
+ }
+
+ {
+ /**
+ * Tc Size Alt values
+ */
+ cfg.put(CFG_TC_API_CONNECT,
+ 3 * noOfTransactions);
+
+ cfg.put(CFG_TC_TC_CONNECT,
+ (2 * noOfOperations) + 16 + noOfTransactions);
+
+ cfg.put(CFG_TC_TABLE,
+ noOfMetaTables);
+
+ cfg.put(CFG_TC_LOCAL_SCAN,
+ noOfLocalScanRecords);
+
+ cfg.put(CFG_TC_SCAN,
+ noOfTCScanRecords);
+ }
+
+ {
+ /**
+ * Tup Size Alt values
+ */
+ cfg.put(CFG_TUP_FRAG,
+ 2 * NO_OF_FRAG_PER_NODE * noOfMetaTables* noOfReplicas);
+
+ cfg.put(CFG_TUP_OP_RECS,
+ noOfLocalOperations + 50);
+
+ cfg.put(CFG_TUP_PAGE,
+ noOfDataPages);
+
+ cfg.put(CFG_TUP_PAGE_RANGE,
+ 4 * NO_OF_FRAG_PER_NODE * noOfMetaTables* noOfReplicas);
+
+ cfg.put(CFG_TUP_TABLE,
+ noOfMetaTables);
+
+ cfg.put(CFG_TUP_TABLE_DESC,
+ 2 * 6 * NO_OF_FRAG_PER_NODE * noOfAttributes * noOfReplicas +
+ 2 * 10 * NO_OF_FRAG_PER_NODE * noOfMetaTables * noOfReplicas );
+
+ cfg.put(CFG_TUP_STORED_PROC,
+ noOfLocalScanRecords);
+ }
+
+ {
+ /**
+ * Tux Size Alt values
+ */
+ cfg.put(CFG_TUX_INDEX,
+ noOfMetaTables /*noOfOrderedIndexes*/);
+
+ cfg.put(CFG_TUX_FRAGMENT,
+ 2 * NO_OF_FRAG_PER_NODE * noOfOrderedIndexes * noOfReplicas);
+
+ cfg.put(CFG_TUX_ATTRIBUTE,
+ noOfOrderedIndexes * 4);
+
+ cfg.put(CFG_TUX_SCAN_OP, noOfLocalScanRecords);
+ }
+
+ m_ownConfig = (ndb_mgm_configuration*)cfg.getConfigValues();
+ m_ownConfigIterator = ndb_mgm_create_configuration_iterator
+ (m_ownConfig, 0);
+}
+
+void
+Configuration::setInitialStart(bool val){
+ _initialStart = val;
+}
diff --git a/storage/ndb/src/kernel/vm/Configuration.hpp b/storage/ndb/src/kernel/vm/Configuration.hpp
new file mode 100644
index 00000000000..6ca6d9a1f17
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Configuration.hpp
@@ -0,0 +1,139 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 Configuration_H
+#define Configuration_H
+
+#include <util/BaseString.hpp>
+#include <mgmapi.h>
+#include <ndb_types.h>
+
+class ConfigRetriever;
+
+class Configuration {
+public:
+ Configuration();
+ ~Configuration();
+
+ /**
+ * Returns false if arguments are invalid
+ */
+ bool init(int argc, char** argv);
+
+ void fetch_configuration();
+ void setupConfiguration();
+ void closeConfiguration();
+
+ bool lockPagesInMainMemory() const;
+
+ int timeBetweenWatchDogCheck() const ;
+ void timeBetweenWatchDogCheck(int value);
+
+ int maxNoOfErrorLogs() const ;
+ void maxNoOfErrorLogs(int val);
+
+ bool stopOnError() const;
+ void stopOnError(bool val);
+
+ int getRestartOnErrorInsert() const;
+ void setRestartOnErrorInsert(int);
+
+ // Cluster configuration
+ const char * programName() const;
+ const char * fileSystemPath() const;
+ const char * backupFilePath() const;
+ const char * getConnectString() const;
+ char * getConnectStringCopy() const;
+
+ /**
+ *
+ */
+ bool getInitialStart() const;
+ void setInitialStart(bool val);
+ bool getDaemonMode() const;
+
+ const ndb_mgm_configuration_iterator * getOwnConfigIterator() const;
+
+ Uint32 get_mgmd_port() const {return m_mgmd_port;};
+ const char *get_mgmd_host() const {return m_mgmd_host.c_str();};
+ ConfigRetriever* get_config_retriever() { return m_config_retriever; };
+
+ class LogLevel * m_logLevel;
+private:
+ friend class Cmvmi;
+ friend class Qmgr;
+ ndb_mgm_configuration_iterator * getClusterConfigIterator() const;
+
+ Uint32 _stopOnError;
+ Uint32 m_restartOnErrorInsert;
+ Uint32 _maxErrorLogs;
+ Uint32 _lockPagesInMainMemory;
+ Uint32 _timeBetweenWatchDogCheck;
+
+ ndb_mgm_configuration * m_ownConfig;
+ ndb_mgm_configuration * m_clusterConfig;
+
+ ndb_mgm_configuration_iterator * m_clusterConfigIter;
+ ndb_mgm_configuration_iterator * m_ownConfigIterator;
+
+ ConfigRetriever *m_config_retriever;
+
+ /**
+ * arguments to NDB process
+ */
+ char * _programName;
+ char * _fsPath;
+ char * _backupPath;
+ bool _initialStart;
+ char * _connectString;
+ Uint32 m_mgmd_port;
+ BaseString m_mgmd_host;
+ bool _daemonMode;
+
+ void calcSizeAlt(class ConfigValues * );
+};
+
+inline
+const char *
+Configuration::programName() const {
+ return _programName;
+}
+
+inline
+const char *
+Configuration::fileSystemPath() const {
+ return _fsPath;
+}
+
+inline
+const char *
+Configuration::backupFilePath() const {
+ return _backupPath;
+}
+
+inline
+bool
+Configuration::getInitialStart() const {
+ return _initialStart;
+}
+
+inline
+bool
+Configuration::getDaemonMode() const {
+ return _daemonMode;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/DLFifoList.hpp b/storage/ndb/src/kernel/vm/DLFifoList.hpp
new file mode 100644
index 00000000000..b139ade831d
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/DLFifoList.hpp
@@ -0,0 +1,360 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DLFIFOLIST_HPP
+#define DLFIFOLIST_HPP
+
+#include "ArrayPool.hpp"
+#include <NdbOut.hpp>
+
+/**
+ * Template class used for implementing an
+ * list of object retreived from a pool
+ */
+template <class T>
+class DLFifoList {
+public:
+ /**
+ * List head
+ */
+ struct Head {
+ Head();
+ Uint32 firstItem;
+ Uint32 lastItem;
+ };
+
+ DLFifoList(ArrayPool<T> & thePool);
+
+ /**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seize(Ptr<T> &);
+
+ /**
+ * Allocate object <b>i</b> from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seizeId(Ptr<T> &, Uint32 i);
+
+ /**
+ * Add object to list
+ *
+ * @NOTE MUST be seized from correct pool
+ */
+ void add(Ptr<T> &);
+
+ /**
+ * Remove from list
+ */
+ void remove(Ptr<T> &);
+
+ /**
+ * Return an object to pool
+ */
+ void release(Uint32 i);
+
+ /**
+ * Return an object to pool
+ */
+ void release(Ptr<T> &);
+
+ /**
+ * Return all objects to the pool
+ */
+ void release();
+
+ /**
+ * Update i & p value according to <b>i</b>
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+ /**
+ * Update p value for ptr according to i value
+ */
+ void getPtr(Ptr<T> &) const ;
+
+ /**
+ * Get pointer for i value
+ */
+ T * getPtr(Uint32 i) const ;
+
+ /**
+ * Update ptr to first element in list
+ *
+ * Return i
+ */
+ bool first(Ptr<T> &) const ;
+
+ /**
+ * Get next element
+ *
+ * NOTE ptr must be both p & i
+ */
+ bool next(Ptr<T> &) const ;
+
+ /**
+ * Check if next exists
+ *
+ * NOTE ptr must be both p & i
+ */
+ bool hasNext(const Ptr<T> &) const;
+
+ Uint32 noOfElements() const {
+ Uint32 c = 0;
+ Uint32 i = head.firstItem;
+ while(i != RNIL){
+ c++;
+ const T * t = thePool.getPtr(i);
+ i = t->nextList;
+ }
+ return c;
+ }
+
+ /**
+ * Print
+ * (Run operator NdbOut<< on every element)
+ */
+ void print(NdbOut & out) {
+ Uint32 i = head.firstItem;
+ while(i != RNIL){
+ T * t = thePool.getPtr(i);
+ out << (unsigned int) t << "[" << i << "]:";
+ t->print(out); out << " ";
+ i = t->nextList;
+ }
+ }
+
+ inline bool isEmpty() const { return head.firstItem == RNIL;}
+
+protected:
+ Head head;
+ ArrayPool<T> & thePool;
+};
+
+template<class T>
+class LocalDLFifoList : public DLFifoList<T> {
+public:
+ LocalDLFifoList(ArrayPool<T> & thePool, typename DLFifoList<T>::Head & _src)
+ : DLFifoList<T>(thePool), src(_src)
+ {
+ this->head = src;
+ }
+
+ ~LocalDLFifoList(){
+ src = this->head;
+ }
+private:
+ typename DLFifoList<T>::Head & src;
+};
+
+template <class T>
+inline
+DLFifoList<T>::DLFifoList(ArrayPool<T> & _pool):
+ thePool(_pool){
+}
+
+template<class T>
+inline
+DLFifoList<T>::Head::Head(){
+ firstItem = RNIL;
+ lastItem = RNIL;
+}
+
+/**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+DLFifoList<T>::seize(Ptr<T> & p){
+ thePool.seize(p);
+ if (p.i != RNIL) {
+ add(p);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+/**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+DLFifoList<T>::seizeId(Ptr<T> & p, Uint32 ir){
+ thePool.seizeId(p, ir);
+ if(p.i != RNIL){
+ add(p);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+void
+DLFifoList<T>::add(Ptr<T> & p){
+ T * t = p.p;
+ Uint32 last = head.lastItem;
+
+ if(p.i == RNIL)
+ ErrorReporter::handleAssert("DLFifoList<T>::add", __FILE__, __LINE__);
+
+ t->nextList = RNIL;
+ t->prevList = last;
+ if (head.firstItem == RNIL)
+ head.firstItem = p.i;
+ head.lastItem = p.i;
+
+ if(last != RNIL){
+ T * t2 = thePool.getPtr(last);
+ t2->nextList = p.i;
+ }
+}
+
+/**
+ * Return an object to pool
+ */
+template <class T>
+inline
+void
+DLFifoList<T>::release(Uint32 i){
+ Ptr<T> p;
+ p.i = i;
+ p.p = thePool.getPtr(i);
+ release(p);
+}
+
+template <class T>
+inline
+void
+DLFifoList<T>::remove(Ptr<T> & p){
+ T * t = p.p;
+ Uint32 ni = t->nextList;
+ Uint32 pi = t->prevList;
+
+ if(ni != RNIL){
+ T * t = thePool.getPtr(ni);
+ t->prevList = pi;
+ } else {
+ // We are releasing last
+ head.lastItem = pi;
+ }
+
+ if(pi != RNIL){
+ T * t = thePool.getPtr(pi);
+ t->nextList = ni;
+ } else {
+ // We are releasing first
+ head.firstItem = ni;
+ }
+}
+
+/**
+ * Return an object to pool
+ */
+template <class T>
+inline
+void
+DLFifoList<T>::release(Ptr<T> & p){
+ remove(p);
+ thePool.release(p.i);
+}
+
+template <class T>
+inline
+void
+DLFifoList<T>::release(){
+ Ptr<T> p;
+ while(head.firstItem != RNIL){
+ p.i = head.firstItem;
+ p.p = thePool.getPtr(head.firstItem);
+ T * t = p.p;
+ head.firstItem = t->nextList;
+ release(p);
+ }
+}
+
+template <class T>
+inline
+void
+DLFifoList<T>::getPtr(Ptr<T> & p, Uint32 i) const {
+ p.i = i;
+ p.p = thePool.getPtr(i);
+}
+
+template <class T>
+inline
+void
+DLFifoList<T>::getPtr(Ptr<T> & p) const {
+ thePool.getPtr(p);
+}
+
+template <class T>
+inline
+T *
+DLFifoList<T>::getPtr(Uint32 i) const {
+ return thePool.getPtr(i);
+}
+
+/**
+ * Update ptr to first element in list
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+DLFifoList<T>::first(Ptr<T> & p) const {
+ p.i = head.firstItem;
+ if(p.i != RNIL){
+ p.p = thePool.getPtr(p.i);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+DLFifoList<T>::next(Ptr<T> & p) const {
+ p.i = p.p->nextList;
+ if(p.i != RNIL){
+ p.p = thePool.getPtr(p.i);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+DLFifoList<T>::hasNext(const Ptr<T> & p) const {
+ return p.p->nextList != RNIL;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/DLHashTable.hpp b/storage/ndb/src/kernel/vm/DLHashTable.hpp
new file mode 100644
index 00000000000..13a9632f8da
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/DLHashTable.hpp
@@ -0,0 +1,493 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DL_HASHTABLE_HPP
+#define DL_HASHTABLE_HPP
+
+#include <ndb_global.h>
+#include "ArrayList.hpp"
+
+/**
+ * DLHashTable implements a hashtable using chaining
+ * (with a double linked list)
+ *
+ * The entries in the hashtable must have the following methods:
+ * -# bool equal(const class T &) const;
+ * Which should return equal if the to objects have the same key
+ * -# Uint32 hashValue() const;
+ * Which should return a 32 bit hashvalue
+ */
+template <class T>
+class DLHashTable {
+public:
+ DLHashTable(ArrayPool<T> & thePool);
+ ~DLHashTable();
+
+ /**
+ * Set the no of bucket in the hashtable
+ *
+ * Note, can currently only be called once
+ */
+ bool setSize(Uint32 noOfElements);
+
+ /**
+ * Seize element from pool - return i
+ *
+ * Note must be either added using <b>add</b> or released
+ * using <b>release</b>
+ */
+ bool seize(Ptr<T> &);
+
+ /**
+ * Add an object to the hashtable
+ */
+ void add(Ptr<T> &);
+
+ /**
+ * Find element key in hashtable update Ptr (i & p)
+ * (using key.equal(...))
+ * @return true if found and false otherwise
+ */
+ bool find(Ptr<T> &, const T & key) const;
+
+ /**
+ * Update i & p value according to <b>i</b>
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+ /**
+ * Get element using ptr.i (update ptr.p)
+ */
+ void getPtr(Ptr<T> &) const;
+
+ /**
+ * Get P value for i
+ */
+ T * getPtr(Uint32 i) const;
+
+ /**
+ * Remove element (and set Ptr to removed element)
+ * Note does not return to pool
+ */
+ void remove(Ptr<T> &, const T & key);
+
+ /**
+ * Remove element
+ * Note does not return to pool
+ */
+ void remove(Uint32 i);
+
+ /**
+ * Remove element
+ * Note does not return to pool
+ */
+ void remove(Ptr<T> &);
+
+ /**
+ * Remove all elements, but dont return them to pool
+ */
+ void removeAll();
+
+ /**
+ * Remove element (and set Ptr to removed element)
+ * And return element to pool
+ */
+ void release(Ptr<T> &, const T & key);
+
+ /**
+ * Remove element and return to pool
+ */
+ void release(Uint32 i);
+
+ /**
+ * Remove element and return to pool
+ */
+ void release(Ptr<T> &);
+
+ class Iterator {
+ public:
+ Ptr<T> curr;
+ Uint32 bucket;
+ inline bool isNull() const { return curr.isNull();}
+ inline void setNull() { curr.setNull(); }
+ };
+
+ /**
+ * Sets curr.p according to curr.i
+ */
+ void getPtr(Iterator & iter) const ;
+
+ /**
+ * First element in bucket
+ */
+ bool first(Iterator & iter) const;
+
+ /**
+ * Next Element
+ *
+ * param iter - A "fully set" iterator
+ */
+ bool next(Iterator & iter) const;
+
+ /**
+ * Get next element starting from bucket
+ *
+ * @param bucket - Which bucket to start from
+ * @param iter - An "uninitialized" iterator
+ */
+ bool next(Uint32 bucket, Iterator & iter) const;
+
+private:
+ Uint32 mask;
+ Uint32 * hashValues;
+ ArrayPool<T> & thePool;
+};
+
+template<class T>
+inline
+DLHashTable<T>::DLHashTable(ArrayPool<T> & _pool)
+ : thePool(_pool)
+{
+ mask = 0;
+ hashValues = 0;
+}
+
+template<class T>
+inline
+DLHashTable<T>::~DLHashTable(){
+ if(hashValues != 0)
+ delete [] hashValues;
+}
+
+template<class T>
+inline
+bool
+DLHashTable<T>::setSize(Uint32 size){
+ Uint32 i = 1;
+ while(i < size) i *= 2;
+
+ if(mask == (i - 1)){
+ /**
+ * The size is already set to <b>size</b>
+ */
+ return true;
+ }
+
+ if(mask != 0){
+ /**
+ * The mask is already set
+ */
+ return false;
+ }
+
+ mask = (i - 1);
+ hashValues = new Uint32[i];
+ for(Uint32 j = 0; j<i; j++)
+ hashValues[j] = RNIL;
+
+ return true;
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::add(Ptr<T> & obj){
+ const Uint32 hv = obj.p->hashValue() & mask;
+ const Uint32 i = hashValues[hv];
+
+ if(i == RNIL){
+ hashValues[hv] = obj.i;
+ obj.p->nextHash = RNIL;
+ obj.p->prevHash = RNIL;
+ } else {
+
+ T * tmp = thePool.getPtr(i);
+ tmp->prevHash = obj.i;
+ obj.p->nextHash = i;
+ obj.p->prevHash = RNIL;
+
+ hashValues[hv] = obj.i;
+ }
+}
+
+/**
+ * First element
+ */
+template<class T>
+inline
+bool
+DLHashTable<T>::first(Iterator & iter) const {
+ Uint32 i = 0;
+ while(i <= mask && hashValues[i] == RNIL) i++;
+ if(i <= mask){
+ iter.bucket = i;
+ iter.curr.i = hashValues[i];
+ iter.curr.p = thePool.getPtr(iter.curr.i);
+ return true;
+ } else {
+ iter.curr.i = RNIL;
+ }
+ return false;
+}
+
+template<class T>
+inline
+bool
+DLHashTable<T>::next(Iterator & iter) const {
+ if(iter.curr.p->nextHash == RNIL){
+ Uint32 i = iter.bucket + 1;
+ while(i <= mask && hashValues[i] == RNIL) i++;
+ if(i <= mask){
+ iter.bucket = i;
+ iter.curr.i = hashValues[i];
+ iter.curr.p = thePool.getPtr(iter.curr.i);
+ return true;
+ } else {
+ iter.curr.i = RNIL;
+ return false;
+ }
+ }
+
+ iter.curr.i = iter.curr.p->nextHash;
+ iter.curr.p = thePool.getPtr(iter.curr.i);
+ return true;
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::remove(Ptr<T> & ptr, const T & key){
+ const Uint32 hv = key.hashValue() & mask;
+
+ Uint32 i;
+ T * p;
+ Ptr<T> prev;
+ prev.i = RNIL;
+
+ i = hashValues[hv];
+ while(i != RNIL){
+ p = thePool.getPtr(i);
+ if(key.equal(* p)){
+ const Uint32 next = p->nextHash;
+ if(prev.i == RNIL){
+ hashValues[hv] = next;
+ } else {
+ prev.p->nextHash = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = thePool.getPtr(next);
+ nextP->prevHash = prev.i;
+ }
+
+ ptr.i = i;
+ ptr.p = p;
+ return;
+ }
+ prev.p = p;
+ prev.i = i;
+ i = p->nextHash;
+ }
+ ptr.i = RNIL;
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::release(Ptr<T> & ptr, const T & key){
+ const Uint32 hv = key.hashValue() & mask;
+
+ Uint32 i;
+ T * p;
+ Ptr<T> prev;
+ prev.i = RNIL;
+
+ i = hashValues[hv];
+ while(i != RNIL){
+ p = thePool.getPtr(i);
+ if(key.equal(* p)){
+ const Uint32 next = p->nextHash;
+ if(prev.i == RNIL){
+ hashValues[hv] = next;
+ } else {
+ prev.p->nextHash = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = thePool.getPtr(next);
+ nextP->prevHash = prev.i;
+ }
+
+ thePool.release(i);
+ ptr.i = i;
+ ptr.p = p;
+ return;
+ }
+ prev.p = p;
+ prev.i = i;
+ i = p->nextHash;
+ }
+ ptr.i = RNIL;
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::remove(Uint32 i){
+ Ptr<T> tmp;
+ tmp.i = i;
+ tmp.p = thePool.getPtr(i);
+ remove(tmp);
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::release(Uint32 i){
+ Ptr<T> tmp;
+ tmp.i = i;
+ tmp.p = thePool.getPtr(i);
+ release(tmp);
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::remove(Ptr<T> & ptr){
+ const Uint32 next = ptr.p->nextHash;
+ const Uint32 prev = ptr.p->prevHash;
+
+ if(prev != RNIL){
+ T * prevP = thePool.getPtr(prev);
+ prevP->nextHash = next;
+ } else {
+ const Uint32 hv = ptr.p->hashValue() & mask;
+ hashValues[hv] = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = thePool.getPtr(next);
+ nextP->prevHash = prev;
+ }
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::release(Ptr<T> & ptr){
+ const Uint32 next = ptr.p->nextHash;
+ const Uint32 prev = ptr.p->prevHash;
+
+ if(prev != RNIL){
+ T * prevP = thePool.getPtr(prev);
+ prevP->nextHash = next;
+ } else {
+ const Uint32 hv = ptr.p->hashValue() & mask;
+ hashValues[hv] = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = thePool.getPtr(next);
+ nextP->prevHash = prev;
+ }
+
+ thePool.release(ptr.i);
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::removeAll(){
+ for(Uint32 i = 0; i<=mask; i++)
+ hashValues[i] = RNIL;
+}
+
+template<class T>
+inline
+bool
+DLHashTable<T>::next(Uint32 bucket, Iterator & iter) const {
+ while (bucket <= mask && hashValues[bucket] == RNIL)
+ bucket++;
+
+ if (bucket > mask) {
+ iter.bucket = bucket;
+ iter.curr.i = RNIL;
+ return false;
+ }
+
+ iter.bucket = bucket;
+ iter.curr.i = hashValues[bucket];
+ iter.curr.p = thePool.getPtr(iter.curr.i);
+ return true;
+}
+
+template<class T>
+inline
+bool
+DLHashTable<T>::seize(Ptr<T> & ptr){
+ if(thePool.seize(ptr)){
+ ptr.p->nextHash = ptr.p->prevHash = RNIL;
+ return true;
+ }
+ return false;
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::getPtr(Ptr<T> & ptr, Uint32 i) const {
+ ptr.i = i;
+ ptr.p = thePool.getPtr(i);
+}
+
+template<class T>
+inline
+void
+DLHashTable<T>::getPtr(Ptr<T> & ptr) const {
+ thePool.getPtr(ptr);
+}
+
+template<class T>
+inline
+T *
+DLHashTable<T>::getPtr(Uint32 i) const {
+ return thePool.getPtr(i);
+}
+
+template<class T>
+inline
+bool
+DLHashTable<T>::find(Ptr<T> & ptr, const T & key) const {
+ const Uint32 hv = key.hashValue() & mask;
+
+ Uint32 i;
+ T * p;
+
+ i = hashValues[hv];
+ while(i != RNIL){
+ p = thePool.getPtr(i);
+ if(key.equal(* p)){
+ ptr.i = i;
+ ptr.p = p;
+ return true;
+ }
+ i = p->nextHash;
+ }
+ ptr.i = RNIL;
+ ptr.p = NULL;
+ return false;
+}
+#endif
diff --git a/storage/ndb/src/kernel/vm/DLHashTable2.hpp b/storage/ndb/src/kernel/vm/DLHashTable2.hpp
new file mode 100644
index 00000000000..6b166331631
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/DLHashTable2.hpp
@@ -0,0 +1,500 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DL_HASHTABLE2_HPP
+#define DL_HASHTABLE2_HPP
+
+#include <ndb_global.h>
+
+#include "ArrayList.hpp"
+
+/**
+ * DLHashTable2 is a DLHashTable variant meant for cases where different
+ * DLHashTable instances share a common pool (based on a union U).
+ *
+ * Calls T constructor after seize from pool and T destructor before
+ * release (in all forms) into pool.
+ */
+template <class T, class U>
+class DLHashTable2 {
+public:
+ DLHashTable2(ArrayPool<U> & thePool);
+ ~DLHashTable2();
+
+ /**
+ * Set the no of bucket in the hashtable
+ *
+ * Note, can currently only be called once
+ */
+ bool setSize(Uint32 noOfElements);
+
+ /**
+ * Seize element from pool - return i
+ *
+ * Note must be either added using <b>add</b> or released
+ * using <b>release</b>
+ */
+ bool seize(Ptr<T> &);
+
+ /**
+ * Add an object to the hashtable
+ */
+ void add(Ptr<T> &);
+
+ /**
+ * Find element key in hashtable update Ptr (i & p)
+ * (using key.equal(...))
+ * @return true if found and false otherwise
+ */
+ bool find(Ptr<T> &, const T & key) const;
+
+ /**
+ * Update i & p value according to <b>i</b>
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+ /**
+ * Get element using ptr.i (update ptr.p)
+ */
+ void getPtr(Ptr<T> &) const;
+
+ /**
+ * Get P value for i
+ */
+ T * getPtr(Uint32 i) const;
+
+ /**
+ * Remove element (and set Ptr to removed element)
+ * Note does not return to pool
+ */
+ void remove(Ptr<T> &, const T & key);
+
+ /**
+ * Remove element
+ * Note does not return to pool
+ */
+ void remove(Uint32 i);
+
+ /**
+ * Remove element
+ * Note does not return to pool
+ */
+ void remove(Ptr<T> &);
+
+ /**
+ * Remove all elements, but dont return them to pool
+ */
+ void removeAll();
+
+ /**
+ * Remove element (and set Ptr to removed element)
+ * And return element to pool
+ */
+ void release(Ptr<T> &, const T & key);
+
+ /**
+ * Remove element and return to pool
+ */
+ void release(Uint32 i);
+
+ /**
+ * Remove element and return to pool
+ */
+ void release(Ptr<T> &);
+
+ class Iterator {
+ public:
+ Ptr<T> curr;
+ Uint32 bucket;
+ inline bool isNull() const { return curr.isNull();}
+ inline void setNull() { curr.setNull(); }
+ };
+
+ /**
+ * Sets curr.p according to curr.i
+ */
+ void getPtr(Iterator & iter) const ;
+
+ /**
+ * First element in bucket
+ */
+ bool first(Iterator & iter) const;
+
+ /**
+ * Next Element
+ *
+ * param iter - A "fully set" iterator
+ */
+ bool next(Iterator & iter) const;
+
+ /**
+ * Get next element starting from bucket
+ *
+ * @param bucket - Which bucket to start from
+ * @param iter - An "uninitialized" iterator
+ */
+ bool next(Uint32 bucket, Iterator & iter) const;
+
+private:
+ Uint32 mask;
+ Uint32 * hashValues;
+ ArrayPool<U> & thePool;
+};
+
+template<class T, class U>
+inline
+DLHashTable2<T, U>::DLHashTable2(ArrayPool<U> & _pool)
+ : thePool(_pool)
+{
+ mask = 0;
+ hashValues = 0;
+}
+
+template<class T, class U>
+inline
+DLHashTable2<T, U>::~DLHashTable2(){
+ if(hashValues != 0)
+ delete [] hashValues;
+}
+
+template<class T, class U>
+inline
+bool
+DLHashTable2<T, U>::setSize(Uint32 size){
+ Uint32 i = 1;
+ while(i < size) i *= 2;
+
+ if(mask == (i - 1)){
+ /**
+ * The size is already set to <b>size</b>
+ */
+ return true;
+ }
+
+ if(mask != 0){
+ /**
+ * The mask is already set
+ */
+ return false;
+ }
+
+ mask = (i - 1);
+ hashValues = new Uint32[i];
+ for(Uint32 j = 0; j<i; j++)
+ hashValues[j] = RNIL;
+
+ return true;
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::add(Ptr<T> & obj){
+ const Uint32 hv = obj.p->hashValue() & mask;
+ const Uint32 i = hashValues[hv];
+
+ if(i == RNIL){
+ hashValues[hv] = obj.i;
+ obj.p->nextHash = RNIL;
+ obj.p->prevHash = RNIL;
+ } else {
+
+ T * tmp = (T*)thePool.getPtr(i); // cast
+ tmp->prevHash = obj.i;
+ obj.p->nextHash = i;
+ obj.p->prevHash = RNIL;
+
+ hashValues[hv] = obj.i;
+ }
+}
+
+/**
+ * First element
+ */
+template<class T, class U>
+inline
+bool
+DLHashTable2<T, U>::first(Iterator & iter) const {
+ Uint32 i = 0;
+ while(i <= mask && hashValues[i] == RNIL) i++;
+ if(i <= mask){
+ iter.bucket = i;
+ iter.curr.i = hashValues[i];
+ iter.curr.p = (T*)thePool.getPtr(iter.curr.i); // cast
+ return true;
+ } else {
+ iter.curr.i = RNIL;
+ }
+ return false;
+}
+
+template<class T, class U>
+inline
+bool
+DLHashTable2<T, U>::next(Iterator & iter) const {
+ if(iter.curr.p->nextHash == RNIL){
+ Uint32 i = iter.bucket + 1;
+ while(i <= mask && hashValues[i] == RNIL) i++;
+ if(i <= mask){
+ iter.bucket = i;
+ iter.curr.i = hashValues[i];
+ iter.curr.p = (T*)thePool.getPtr(iter.curr.i); // cast
+ return true;
+ } else {
+ iter.curr.i = RNIL;
+ return false;
+ }
+ }
+
+ iter.curr.i = iter.curr.p->nextHash;
+ iter.curr.p = (T*)thePool.getPtr(iter.curr.i); // cast
+ return true;
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::remove(Ptr<T> & ptr, const T & key){
+ const Uint32 hv = key.hashValue() & mask;
+
+ Uint32 i;
+ T * p;
+ Ptr<T> prev;
+ prev.i = RNIL;
+
+ i = hashValues[hv];
+ while(i != RNIL){
+ p = (T*)thePool.getPtr(i); // cast
+ if(key.equal(* p)){
+ const Uint32 next = p->nextHash;
+ if(prev.i == RNIL){
+ hashValues[hv] = next;
+ } else {
+ prev.p->nextHash = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = (T*)thePool.getPtr(next); // cast
+ nextP->prevHash = prev.i;
+ }
+
+ ptr.i = i;
+ ptr.p = p;
+ return;
+ }
+ prev.p = p;
+ prev.i = i;
+ i = p->nextHash;
+ }
+ ptr.i = RNIL;
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::release(Ptr<T> & ptr, const T & key){
+ const Uint32 hv = key.hashValue() & mask;
+
+ Uint32 i;
+ T * p;
+ Ptr<T> prev;
+ prev.i = RNIL;
+
+ i = hashValues[hv];
+ while(i != RNIL){
+ p = (T*)thePool.getPtr(i); // cast
+ if(key.equal(* p)){
+ const Uint32 next = p->nextHash;
+ if(prev.i == RNIL){
+ hashValues[hv] = next;
+ } else {
+ prev.p->nextHash = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = (T*)thePool.getPtr(next); // cast
+ nextP->prevHash = prev.i;
+ }
+
+ p->~T(); // dtor
+ thePool.release(i);
+ ptr.i = i;
+ ptr.p = p; // invalid
+ return;
+ }
+ prev.p = p;
+ prev.i = i;
+ i = p->nextHash;
+ }
+ ptr.i = RNIL;
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::remove(Uint32 i){
+ Ptr<T> tmp;
+ tmp.i = i;
+ tmp.p = (T*)thePool.getPtr(i); // cast
+ remove(tmp);
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::release(Uint32 i){
+ Ptr<T> tmp;
+ tmp.i = i;
+ tmp.p = (T*)thePool.getPtr(i); // cast
+ release(tmp);
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::remove(Ptr<T> & ptr){
+ const Uint32 next = ptr.p->nextHash;
+ const Uint32 prev = ptr.p->prevHash;
+
+ if(prev != RNIL){
+ T * prevP = (T*)thePool.getPtr(prev); // cast
+ prevP->nextHash = next;
+ } else {
+ const Uint32 hv = ptr.p->hashValue() & mask;
+ hashValues[hv] = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = (T*)thePool.getPtr(next); // cast
+ nextP->prevHash = prev;
+ }
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::release(Ptr<T> & ptr){
+ const Uint32 next = ptr.p->nextHash;
+ const Uint32 prev = ptr.p->prevHash;
+
+ if(prev != RNIL){
+ T * prevP = (T*)thePool.getPtr(prev); // cast
+ prevP->nextHash = next;
+ } else {
+ const Uint32 hv = ptr.p->hashValue() & mask;
+ hashValues[hv] = next;
+ }
+
+ if(next != RNIL){
+ T * nextP = (T*)thePool.getPtr(next); // cast
+ nextP->prevHash = prev;
+ }
+
+ thePool.release(ptr.i);
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::removeAll(){
+ for(Uint32 i = 0; i<=mask; i++)
+ hashValues[i] = RNIL;
+}
+
+template<class T, class U>
+inline
+bool
+DLHashTable2<T, U>::next(Uint32 bucket, Iterator & iter) const {
+ while (bucket <= mask && hashValues[bucket] == RNIL)
+ bucket++;
+
+ if (bucket > mask) {
+ iter.bucket = bucket;
+ iter.curr.i = RNIL;
+ return false;
+ }
+
+ iter.bucket = bucket;
+ iter.curr.i = hashValues[bucket];
+ iter.curr.p = (T*)thePool.getPtr(iter.curr.i); // cast
+ return true;
+}
+
+template<class T, class U>
+inline
+bool
+DLHashTable2<T, U>::seize(Ptr<T> & ptr){
+ Ptr<U> ptr2;
+ thePool.seize(ptr2);
+ ptr.i = ptr2.i;
+ ptr.p = (T*)ptr2.p; // cast
+ if (ptr.p != NULL){
+ ptr.p->nextHash = RNIL;
+ ptr.p->prevHash = RNIL;
+ new (ptr.p) T; // ctor
+ }
+ return !ptr.isNull();
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::getPtr(Ptr<T> & ptr, Uint32 i) const {
+ ptr.i = i;
+ ptr.p = (T*)thePool.getPtr(i); // cast
+}
+
+template<class T, class U>
+inline
+void
+DLHashTable2<T, U>::getPtr(Ptr<T> & ptr) const {
+ Ptr<U> ptr2;
+ thePool.getPtr(ptr2);
+ ptr.i = ptr2.i;
+ ptr.p = (T*)ptr2.p; // cast
+}
+
+template<class T, class U>
+inline
+T *
+DLHashTable2<T, U>::getPtr(Uint32 i) const {
+ return (T*)thePool.getPtr(i); // cast
+}
+
+template<class T, class U>
+inline
+bool
+DLHashTable2<T, U>::find(Ptr<T> & ptr, const T & key) const {
+ const Uint32 hv = key.hashValue() & mask;
+
+ Uint32 i;
+ T * p;
+
+ i = hashValues[hv];
+ while(i != RNIL){
+ p = (T*)thePool.getPtr(i); // cast
+ if(key.equal(* p)){
+ ptr.i = i;
+ ptr.p = p;
+ return true;
+ }
+ i = p->nextHash;
+ }
+ ptr.i = RNIL;
+ ptr.p = NULL;
+ return false;
+}
+#endif
diff --git a/storage/ndb/src/kernel/vm/DLList.hpp b/storage/ndb/src/kernel/vm/DLList.hpp
new file mode 100644
index 00000000000..b7820eb9229
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/DLList.hpp
@@ -0,0 +1,369 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DLLIST_HPP
+#define DLLIST_HPP
+
+#include "ArrayPool.hpp"
+#include <NdbOut.hpp>
+
+/**
+ * Template class used for implementing an
+ * list of object retreived from a pool
+ */
+template <class T>
+class DLList {
+public:
+ /**
+ * List head
+ */
+ struct Head {
+ Head();
+ Uint32 firstItem;
+ inline bool isEmpty() const { return firstItem == RNIL; }
+ };
+
+ DLList(ArrayPool<T> & thePool);
+
+ /**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seize(Ptr<T> &);
+
+ /**
+ * Allocate object <b>i</b> from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seizeId(Ptr<T> &, Uint32 i);
+
+ /**
+ * Check if <b>i</b> is allocated.
+ */
+ bool findId(Uint32 i) const;
+
+ /**
+ * Allocate <b>n</b>objects from pool
+ *
+ * Return i value of first object allocated or RNIL if fails
+ */
+ bool seizeN(Ptr<T> &, Uint32 n);
+
+ /**
+ * Return an object to pool
+ */
+ void release(Uint32 i);
+
+ /**
+ * Return an object to pool
+ */
+ void release(Ptr<T> &);
+
+ /**
+ * Return all objects to the pool
+ */
+ void release();
+
+ /**
+ * Add object to list
+ *
+ * @NOTE MUST be seized from correct pool
+ */
+ void add(Ptr<T> &);
+
+ /**
+ * Remove object from list
+ *
+ * @NOTE Does not return it to pool
+ */
+ void remove(Ptr<T> &);
+
+ /**
+ * Update i & p value according to <b>i</b>
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+ /**
+ * Update p value for ptr according to i value
+ */
+ void getPtr(Ptr<T> &) const ;
+
+ /**
+ * Get pointer for i value
+ */
+ T * getPtr(Uint32 i) const ;
+
+ /**
+ * Update ptr to first element in list
+ *
+ * @return True if element exists, false otherwise
+ */
+ bool first(Ptr<T> &) const ;
+
+ /**
+ * Get next element
+ *
+ * @note ptr must have both p & i values
+ *
+ * @return True if element exists, false otherwise
+ */
+ bool next(Ptr<T> &) const ;
+
+ /**
+ * Check if next exists
+ *
+ * @note ptr must have both p & i values
+ * @return True if element exists, false otherwise
+ */
+ bool hasNext(const Ptr<T> &) const;
+
+ Uint32 noOfElements() const {
+ Uint32 c = 0;
+ Uint32 i = head.firstItem;
+ while(i != RNIL){
+ c++;
+ const T * t = thePool.getPtr(i);
+ i = t->nextList;
+ }
+ return c;
+ }
+
+ /**
+ * Print
+ * (Run operator NdbOut<< on every element)
+ */
+ void print(NdbOut & out) {
+ Uint32 i = head.firstItem;
+ while(i != RNIL){
+ T * t = thePool.getPtr(i);
+ t->print(out); out << " ";
+ i = t->nextList;
+ }
+ }
+
+ inline bool isEmpty() const { return head.firstItem == RNIL;}
+
+protected:
+ Head head;
+ ArrayPool<T> & thePool;
+};
+
+template<class T>
+class LocalDLList : public DLList<T> {
+public:
+ LocalDLList(ArrayPool<T> & thePool, typename DLList<T>::Head & _src)
+ : DLList<T>(thePool), src(_src)
+ {
+ this->head = src;
+ }
+
+ ~LocalDLList(){
+ src = this->head;
+ }
+private:
+ typename DLList<T>::Head & src;
+};
+
+template <class T>
+inline
+DLList<T>::DLList(ArrayPool<T> & _pool):
+ thePool(_pool){
+}
+
+template<class T>
+inline
+DLList<T>::Head::Head(){
+ firstItem = RNIL;
+}
+
+/**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+DLList<T>::seize(Ptr<T> & p){
+ if(thePool.seize(p)){
+ add(p);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+DLList<T>::seizeId(Ptr<T> & p, Uint32 ir){
+ if(thePool.seizeId(p, ir)){
+ add(p);
+ return true;
+ }
+ return false;
+}
+
+template <class T>
+inline
+bool
+DLList<T>::findId(Uint32 i) const {
+ return thePool.findId(i);
+}
+
+template <class T>
+inline
+void
+DLList<T>::add(Ptr<T> & p){
+ T * t = p.p;
+ Uint32 ff = head.firstItem;
+
+ t->nextList = ff;
+ t->prevList = RNIL;
+ head.firstItem = p.i;
+
+ if(ff != RNIL){
+ T * t2 = thePool.getPtr(ff);
+ t2->prevList = p.i;
+ }
+}
+
+template <class T>
+inline
+void
+DLList<T>::remove(Ptr<T> & p){
+ T * t = p.p;
+ Uint32 ni = t->nextList;
+ Uint32 pi = t->prevList;
+
+ if(ni != RNIL){
+ T * t = thePool.getPtr(ni);
+ t->prevList = pi;
+ }
+
+ if(pi != RNIL){
+ T * t = thePool.getPtr(pi);
+ t->nextList = ni;
+ } else {
+ head.firstItem = ni;
+ }
+}
+
+/**
+ * Return an object to pool
+ */
+template <class T>
+inline
+void
+DLList<T>::release(Uint32 i){
+ Ptr<T> p;
+ p.i = i;
+ p.p = thePool.getPtr(i);
+ release(p);
+}
+
+/**
+ * Return an object to pool
+ */
+template <class T>
+inline
+void
+DLList<T>::release(Ptr<T> & p){
+ remove(p);
+ thePool.release(p.i);
+}
+
+template <class T>
+inline
+void
+DLList<T>::release(){
+ while(head.firstItem != RNIL){
+ const T * t = thePool.getPtr(head.firstItem);
+ const Uint32 i = head.firstItem;
+ head.firstItem = t->nextList;
+ thePool.release(i);
+ }
+}
+
+template <class T>
+inline
+void
+DLList<T>::getPtr(Ptr<T> & p, Uint32 i) const {
+ p.i = i;
+ p.p = thePool.getPtr(i);
+}
+
+template <class T>
+inline
+void
+DLList<T>::getPtr(Ptr<T> & p) const {
+ thePool.getPtr(p);
+}
+
+template <class T>
+inline
+T *
+DLList<T>::getPtr(Uint32 i) const {
+ return thePool.getPtr(i);
+}
+
+/**
+ * Update ptr to first element in list
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+DLList<T>::first(Ptr<T> & p) const {
+ Uint32 i = head.firstItem;
+ p.i = i;
+ if(i != RNIL){
+ p.p = thePool.getPtr(i);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+DLList<T>::next(Ptr<T> & p) const {
+ Uint32 i = p.p->nextList;
+ p.i = i;
+ if(i != RNIL){
+ p.p = thePool.getPtr(i);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+DLList<T>::hasNext(const Ptr<T> & p) const {
+ return p.p->nextList != RNIL;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/DataBuffer.hpp b/storage/ndb/src/kernel/vm/DataBuffer.hpp
new file mode 100644
index 00000000000..7f553898eb5
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/DataBuffer.hpp
@@ -0,0 +1,532 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DATA_BUFFER_HPP
+#define DATA_BUFFER_HPP
+
+#include "ArrayPool.hpp"
+
+/**
+ * @class DataBuffer
+ * @brief Buffer of data words
+ *
+ * @note The buffer is divided into segments (of size sz)
+ */
+template <Uint32 sz>
+class DataBuffer {
+public:
+ struct Segment {
+ Uint32 nextPool;
+ Uint32 data[sz];
+ NdbOut& print(NdbOut& out){
+ out << "[DataBuffer<" << sz << ">::Segment this="
+ << this << dec << " nextPool= "
+ << nextPool << " ]";
+ return out;
+ }
+ };
+public:
+ typedef ArrayPool<Segment> DataBufferPool;
+
+ /**
+ * Head/anchor for data buffer
+ */
+ struct Head {
+ Head() ;
+
+ Uint32 used; // Words used
+ Uint32 firstItem; // First segment (or RNIL)
+ Uint32 lastItem; // Last segment (or RNIL)
+
+ /**
+ * Get size of databuffer, in words
+ */
+ Uint32 getSize() const { return used;}
+
+ /**
+ * Get segment size in words (template argument)
+ */
+ static Uint32 getSegmentSize() { return sz;}
+ };
+
+ /** Constructor */
+ DataBuffer(DataBufferPool &);
+
+ /** Seize <b>n</b> words, Release */
+ bool seize(Uint32 n);
+ void release();
+
+ /**
+ * Get size of databuffer, in words
+ */
+ Uint32 getSize() const;
+
+ /**
+ * Check if buffer is empty
+ */
+ bool isEmpty() const;
+
+ /**
+ * Get segment size in words (template argument)
+ */
+ static Uint32 getSegmentSize();
+
+ void print(FILE*) const;
+
+ /* ----------------------------------------------------------------------- */
+
+ struct DataBufferIterator {
+ Ptr<Segment> curr; // Ptr to current segment
+ Uint32* data; // Pointer to current data (word)
+ Uint32 ind; // Word index within a segment
+ Uint32 pos; // Absolute word position within DataBuffer
+
+ void print(FILE* out) {
+ fprintf(out, "[DataBufferIterator curr.i=%d, data=%p, ind=%d, pos=%d]\n",
+ curr.i, (void*) data, ind, pos);
+ };
+
+ inline bool isNull() const { return curr.isNull();}
+ inline void setNull() { curr.setNull(); data = 0; ind = pos = RNIL;}
+ };
+
+ struct ConstDataBufferIterator {
+ ConstPtr<Segment> curr;
+ const Uint32 * data;
+ Uint32 ind;
+ Uint32 pos;
+
+ inline bool isNull() const { return curr.isNull();}
+ inline void setNull() { curr.setNull(); data = 0; ind = pos = RNIL;}
+ };
+
+ /**
+ * Iterator
+ * @parameter hops Number of words to jump forward
+ * @note DataBuffer::next returns false if applied to last word.
+ */
+ bool first(DataBufferIterator &);
+ bool next(DataBufferIterator &);
+ bool next(DataBufferIterator &, Uint32 hops);
+ bool nextPool(DataBufferIterator &);
+
+ /**
+ * Set iterator to position
+ */
+ bool position(DataBufferIterator& it, Uint32 pos);
+
+ /** Iterator */
+ bool first(ConstDataBufferIterator &) const;
+ bool next(ConstDataBufferIterator &) const;
+ bool next(ConstDataBufferIterator &, Uint32 hops) const;
+ bool nextPool(ConstDataBufferIterator &) const;
+
+ /**
+ * Returns true if it is possible to store <em>len</em>
+ * no of words at position given in iterator.
+ */
+ bool importable(const DataBufferIterator, Uint32 len);
+
+ /**
+ * Stores <em>len</em> no of words starting at location <em>src</em> in
+ * databuffer at position given in iterator.
+ *
+ * @return true if success, false otherwise.
+ * @note Iterator is not advanced.
+ */
+ bool import(const DataBufferIterator &, const Uint32* src, Uint32 len);
+
+ /**
+ * Increases size with appends <em>len</em> words
+ * @return true if success, false otherwise.
+ */
+ bool append(const Uint32* src, Uint32 len);
+
+protected:
+ Head head;
+ DataBufferPool & thePool;
+
+private:
+ /**
+ * This is NOT a public method, since the intension is that the import
+ * method using iterators will be more effective in the future
+ */
+ bool import(Uint32 pos, const Uint32* src, Uint32 len);
+};
+
+template<Uint32 sz>
+class LocalDataBuffer : public DataBuffer<sz> {
+public:
+ LocalDataBuffer(typename DataBuffer<sz>::DataBufferPool & thePool,
+ typename DataBuffer<sz>::Head & _src)
+ : DataBuffer<sz>(thePool), src(_src)
+ {
+ this->head = src;
+ }
+
+ ~LocalDataBuffer(){
+ src = this->head;
+ }
+private:
+ typename DataBuffer<sz>::Head & src;
+};
+
+template<Uint32 sz>
+inline
+DataBuffer<sz>::Head::Head(){
+ used = 0;
+ firstItem = RNIL;
+ lastItem = RNIL;
+}
+
+template<Uint32 sz>
+inline
+bool DataBuffer<sz>::importable(const DataBufferIterator it, Uint32 len){
+ return (it.pos + len < head.used);
+}
+
+template<Uint32 sz>
+inline
+bool DataBuffer<sz>::position(DataBufferIterator& it, Uint32 p){
+
+ // TODO: The current implementation is not the most effective one.
+ // A more effective implementation would start at the current
+ // position of the iterator.
+
+ if(!first(it)){
+ return false;
+ }
+ return next(it, p);
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::import(const DataBufferIterator & it,
+ const Uint32* src, Uint32 len){
+
+#if 0
+ DataBufferIterator it;
+ position(it, _it.pos);
+
+ for(; len > 0; len--){
+ Uint32 s = * src;
+ * it.data = s;
+ next(it);
+ src++;
+ }
+ return true;
+#else
+ Uint32 ind = (it.pos % sz);
+ Uint32 left = sz - ind;
+ Segment * p = it.curr.p;
+
+ while(len > left){
+ memcpy(&p->data[ind], src, 4 * left);
+ src += left;
+ len -= left;
+ ind = 0;
+ left = sz;
+ p = thePool.getPtr(p->nextPool);
+ }
+
+ memcpy(&p->data[ind], src, 4 * len);
+ return true;
+#endif
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::append(const Uint32* src, Uint32 len){
+ if(len == 0)
+ return true;
+
+ Uint32 pos = head.used;
+ if(!seize(len)){
+ return false;
+ }
+ DataBufferIterator it;
+
+ if(position(it, pos) && import(it, src, len)){
+ return true;
+ }
+ abort();
+ return false;
+}
+
+template<Uint32 sz>
+inline
+void DataBuffer<sz>::print(FILE* out) const {
+ fprintf(out, "[DataBuffer used=%d words, segmentsize=%d words",
+ head.used, sz);
+
+ if (head.firstItem == RNIL) {
+ fprintf(out, ": No segments seized.]\n");
+ return;
+ } else {
+ fprintf(out, "\n");
+ }
+
+ Ptr<Segment> ptr;
+ ptr.i = head.firstItem;
+
+ Uint32 acc = 0;
+ for(; ptr.i != RNIL; ){
+ thePool.getPtr(ptr);
+ const Uint32 * rest = ptr.p->data;
+ for(Uint32 i = 0; i<sz; i++){
+ fprintf(out, " H'%.8x", rest[i]);
+ if(acc++ == 6){
+ acc = 0;
+ fprintf(out, "\n");
+ }
+ }
+ ptr.i = ptr.p->nextPool;
+ }
+ fprintf(out, " ]\n");
+}
+
+template<Uint32 sz>
+inline
+DataBuffer<sz>::DataBuffer(DataBufferPool & p) : thePool(p){
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::seize(Uint32 n){
+ Uint32 rest; // Free space in last segment (currently)
+ Segment* prevPtr;
+
+ if(head.firstItem == RNIL){
+ rest = 0;
+ prevPtr = (Segment*)&head.firstItem;
+ } else {
+ rest = (sz - (head.used % sz)) % sz;
+ prevPtr = thePool.getPtr(head.lastItem);
+ }
+
+ /**
+ * Check for space
+ */
+ Uint32 free = thePool.getNoOfFree() * sz + rest;
+ if(n > free){
+ release();
+ return false;
+ }
+
+ Uint32 used = head.used + n;
+ Ptr<Segment> currPtr;
+ currPtr.i = head.lastItem;
+
+ while(n >= sz){
+ if(0)
+ ndbout_c("n(%d) %c sz(%d)", n, (n>sz?'>':(n<sz?'<':'=')), sz);
+
+ thePool.seize(currPtr); assert(currPtr.i != RNIL);
+ prevPtr->nextPool = currPtr.i;
+
+ prevPtr = currPtr.p;
+ prevPtr->nextPool = RNIL;
+ n -= sz;
+ }
+
+ if(0){
+ Uint32 pos = rest + n;
+ ndbout_c("rest(%d), n(%d) pos=%d %c sz(%d)",
+ rest, n, pos, (pos>sz?'>':(pos<sz?'<':'=')), sz);
+ }
+
+ if(n > rest){
+ thePool.seize(currPtr);
+ assert(currPtr.i != RNIL);
+ prevPtr->nextPool = currPtr.i;
+ currPtr.p->nextPool = RNIL;
+ }
+
+ head.used = used;
+ head.lastItem = currPtr.i;
+
+#if 0
+ {
+ ndbout_c("Before validate - %d", head.used);
+ if(head.used == 0){
+ assert(head.firstItem == RNIL);
+ assert(head.lastItem == RNIL);
+ } else {
+ Ptr<Segment> tmp;
+ tmp.i = head.firstItem;
+ for(Uint32 i = head.used; i > sz; i -= sz){
+ ndbout << tmp.i << " ";
+ tmp.p = thePool.getPtr(tmp.i);
+ tmp.i = tmp.p->nextPool;
+ }
+ ndbout_c("%d", tmp.i);
+ assert(head.lastItem == tmp.i);
+ }
+ ndbout_c("After validate");
+ }
+#endif
+ return true;
+}
+
+template<Uint32 sz>
+inline
+void
+DataBuffer<sz>::release(){
+ Uint32 used = head.used + sz - 1;
+ if(head.firstItem != RNIL){
+ thePool.releaseList(used / sz, head.firstItem, head.lastItem);
+ head.used = 0;
+ head.firstItem = RNIL;
+ head.lastItem = RNIL;
+ }
+}
+
+template<Uint32 sz>
+inline
+Uint32
+DataBuffer<sz>::getSegmentSize(){
+ return sz;
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::first(DataBufferIterator & it){
+ return first((ConstDataBufferIterator&)it);
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::next(DataBufferIterator & it){
+ return next((ConstDataBufferIterator&)it);
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::next(DataBufferIterator & it, Uint32 hops){
+ return next((ConstDataBufferIterator&)it, hops);
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::first(ConstDataBufferIterator & it) const {
+ it.curr.i = head.firstItem;
+ if(it.curr.i == RNIL){
+ it.setNull();
+ return false;
+ }
+ thePool.getPtr(it.curr);
+ it.data = &it.curr.p->data[0];
+ it.ind = 0;
+ it.pos = 0;
+ return true;
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::next(ConstDataBufferIterator & it) const {
+ it.ind ++;
+ it.data ++;
+ it.pos ++;
+ if(it.ind < sz && it.pos < head.used){
+ return true;
+ }
+
+ if(it.pos < head.used){
+ it.curr.i = it.curr.p->nextPool;
+#ifdef ARRAY_GUARD
+ if(it.curr.i == RNIL){
+ /**
+ * This is actually "internal error"
+ * pos can't be less than head.used and at the same time we can't
+ * find next segment
+ *
+ * Note this must not "really" be checked since thePool.getPtr will
+ * abort when trying to get RNIL. That's why the check is within
+ * ARRAY_GUARD
+ */
+ ErrorReporter::handleAssert("DataBuffer<sz>::next", __FILE__, __LINE__);
+ }
+#endif
+ thePool.getPtr(it.curr);
+ it.data = &it.curr.p->data[0];
+ it.ind = 0;
+ return true;
+ }
+ it.setNull();
+ return false;
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::next(ConstDataBufferIterator & it, Uint32 hops) const {
+#if 0
+ for (Uint32 i=0; i<hops; i++) {
+ if (!this->next(it))
+ return false;
+ }
+ return true;
+#else
+ if(it.pos + hops < head.used){
+ while(hops >= sz){
+ it.curr.i = it.curr.p->nextPool;
+ thePool.getPtr(it.curr);
+ hops -= sz;
+ it.pos += sz;
+ }
+
+ it.ind += hops;
+ it.pos += hops;
+ if(it.ind < sz){
+ it.data = &it.curr.p->data[it.ind];
+ return true;
+ }
+
+ it.curr.i = it.curr.p->nextPool;
+ thePool.getPtr(it.curr);
+ it.ind -= sz;
+ it.data = &it.curr.p->data[it.ind];
+ return true;
+ }
+ it.setNull();
+ return false;
+#endif
+}
+
+template<Uint32 sz>
+inline
+Uint32
+DataBuffer<sz>::getSize() const {
+ return head.used;
+}
+
+template<Uint32 sz>
+inline
+bool
+DataBuffer<sz>::isEmpty() const {
+ return (head.used == 0);
+}
+
+#endif
+
diff --git a/storage/ndb/src/kernel/vm/Emulator.cpp b/storage/ndb/src/kernel/vm/Emulator.cpp
new file mode 100644
index 00000000000..d6ed6c0dafd
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Emulator.cpp
@@ -0,0 +1,269 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "Emulator.hpp"
+#include <FastScheduler.hpp>
+#include <SignalLoggerManager.hpp>
+#include <TransporterRegistry.hpp>
+#include <TimeQueue.hpp>
+
+#include "Configuration.hpp"
+#include "WatchDog.hpp"
+#include "ThreadConfig.hpp"
+#include "SimBlockList.hpp"
+
+#include <NodeState.hpp>
+
+#include <NdbMem.h>
+#include <NdbOut.hpp>
+#include <NdbMutex.h>
+#include <NdbSleep.h>
+
+extern "C" {
+ extern void (* ndb_new_handler)();
+}
+
+/**
+ * Declare the global variables
+ */
+
+#ifndef NO_EMULATED_JAM
+Uint8 theEmulatedJam[EMULATED_JAM_SIZE * 4];
+Uint32 theEmulatedJamIndex = 0;
+Uint32 theEmulatedJamBlockNumber = 0;
+#endif
+
+ GlobalData globalData;
+
+ TimeQueue globalTimeQueue;
+ FastScheduler globalScheduler;
+ TransporterRegistry globalTransporterRegistry;
+
+#ifdef VM_TRACE
+ SignalLoggerManager globalSignalLoggers;
+#endif
+
+EmulatorData globalEmulatorData;
+NdbMutex * theShutdownMutex = 0;
+int simulate_error_during_shutdown= 0;
+
+EmulatorData::EmulatorData(){
+ theConfiguration = 0;
+ theWatchDog = 0;
+ theThreadConfig = 0;
+ theSimBlockList = 0;
+ theShutdownMutex = 0;
+ m_socket_server = 0;
+}
+
+void
+ndb_new_handler_impl(){
+ ERROR_SET(fatal, ERR_MEMALLOC, "New handler", "");
+}
+
+void
+EmulatorData::create(){
+ NdbMem_Create();
+
+ theConfiguration = new Configuration();
+ theWatchDog = new WatchDog();
+ theThreadConfig = new ThreadConfig();
+ theSimBlockList = new SimBlockList();
+ m_socket_server = new SocketServer();
+
+ theShutdownMutex = NdbMutex_Create();
+
+ ndb_new_handler = ndb_new_handler_impl;
+}
+
+void
+EmulatorData::destroy(){
+ if(theConfiguration)
+ delete theConfiguration; theConfiguration = 0;
+ if(theWatchDog)
+ delete theWatchDog; theWatchDog = 0;
+ if(theThreadConfig)
+ delete theThreadConfig; theThreadConfig = 0;
+ if(theSimBlockList)
+ delete theSimBlockList; theSimBlockList = 0;
+ if(m_socket_server)
+ delete m_socket_server; m_socket_server = 0;
+ NdbMem_Destroy();
+}
+
+void
+NdbShutdown(NdbShutdownType type,
+ NdbRestartType restartType){
+
+ if(type == NST_ErrorInsert){
+ type = NST_Restart;
+ restartType = (NdbRestartType)
+ globalEmulatorData.theConfiguration->getRestartOnErrorInsert();
+ if(restartType == NRT_Default){
+ type = NST_ErrorHandler;
+ globalEmulatorData.theConfiguration->stopOnError(true);
+ }
+ }
+
+ if((type == NST_ErrorHandlerSignal) || // Signal handler has already locked mutex
+ (NdbMutex_Trylock(theShutdownMutex) == 0)){
+ globalData.theRestartFlag = perform_stop;
+
+ bool restart = false;
+#if ! ( defined NDB_OSE || defined NDB_SOFTOSE)
+ if((type != NST_Normal &&
+ globalEmulatorData.theConfiguration->stopOnError() == false) ||
+ type == NST_Restart) {
+
+ restart = true;
+ }
+#endif
+
+ const char * shutting = "shutting down";
+ if(restart){
+ shutting = "restarting";
+ }
+
+ switch(type){
+ case NST_Normal:
+ ndbout << "Shutdown initiated" << endl;
+ break;
+ case NST_Watchdog:
+ ndbout << "Watchdog " << shutting << " system" << endl;
+ break;
+ case NST_ErrorHandler:
+ ndbout << "Error handler " << shutting << " system" << endl;
+ break;
+ case NST_ErrorHandlerSignal:
+ ndbout << "Error handler signal " << shutting << " system" << endl;
+ break;
+ case NST_Restart:
+ ndbout << "Restarting system" << endl;
+ break;
+ default:
+ ndbout << "Error handler " << shutting << " system"
+ << " (unknown type: " << (unsigned)type << ")" << endl;
+ type = NST_ErrorHandler;
+ break;
+ }
+
+ const char * exitAbort = 0;
+#if defined VM_TRACE && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) )
+ exitAbort = "aborting";
+#else
+ exitAbort = "exiting";
+#endif
+
+ if(type == NST_Watchdog){
+ /**
+ * Very serious, don't attempt to free, just die!!
+ */
+ ndbout << "Watchdog shutdown completed - " << exitAbort << endl;
+#if defined VM_TRACE && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) )
+ signal(6, SIG_DFL);
+ abort();
+#else
+ exit(-1);
+#endif
+ }
+
+#ifndef NDB_WIN32
+ if (simulate_error_during_shutdown) {
+ kill(getpid(), simulate_error_during_shutdown);
+ while(true)
+ NdbSleep_MilliSleep(10);
+ }
+#endif
+
+ globalEmulatorData.theWatchDog->doStop();
+
+#ifdef VM_TRACE
+ FILE * outputStream = globalSignalLoggers.setOutputStream(0);
+ if(outputStream != 0)
+ fclose(outputStream);
+#endif
+
+ /**
+ * Stop all transporter connection attempts and accepts
+ */
+ globalEmulatorData.m_socket_server->stopServer();
+ globalEmulatorData.m_socket_server->stopSessions();
+ globalTransporterRegistry.stop_clients();
+
+ /**
+ * Stop transporter communication with other nodes
+ */
+ globalTransporterRegistry.stopSending();
+ globalTransporterRegistry.stopReceiving();
+
+ /**
+ * Remove all transporters
+ */
+ globalTransporterRegistry.removeAll();
+
+#ifdef VM_TRACE
+#define UNLOAD (type != NST_ErrorHandler && type != NST_Watchdog)
+#else
+#define UNLOAD true
+#endif
+ if(UNLOAD){
+ globalEmulatorData.theSimBlockList->unload();
+ globalEmulatorData.destroy();
+ }
+
+ if(type != NST_Normal && type != NST_Restart){
+ ndbout << "Error handler shutdown completed - " << exitAbort << endl;
+#if ( defined VM_TRACE || defined ERROR_INSERT ) && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) )
+ signal(6, SIG_DFL);
+ abort();
+#else
+ exit(-1);
+#endif
+ }
+
+ /**
+ * This is a normal restart, depend on angel
+ */
+ if(type == NST_Restart){
+ exit(restartType);
+ }
+
+ ndbout << "Shutdown completed - exiting" << endl;
+ } else {
+ /**
+ * Shutdown is already in progress
+ */
+
+ /**
+ * If this is the watchdog, kill system the hard way
+ */
+ if (type== NST_Watchdog){
+ ndbout << "Watchdog is killing system the hard way" << endl;
+#if defined VM_TRACE && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) )
+ signal(6, SIG_DFL);
+ abort();
+#else
+ exit(-1);
+#endif
+ }
+
+ while(true)
+ NdbSleep_MilliSleep(10);
+ }
+}
+
diff --git a/storage/ndb/src/kernel/vm/Emulator.hpp b/storage/ndb/src/kernel/vm/Emulator.hpp
new file mode 100644
index 00000000000..dba8cb3ab9b
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Emulator.hpp
@@ -0,0 +1,107 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 EMULATOR_H
+#define EMULATOR_H
+
+//===========================================================================
+//
+// .DESCRIPTION
+// This is the main fuction for the AXE VM emulator.
+// It contains some global objects and a run method.
+//
+//===========================================================================
+#include <kernel_types.h>
+#include <TransporterRegistry.hpp>
+
+extern class JobTable globalJobTable;
+extern class TimeQueue globalTimeQueue;
+extern class FastScheduler globalScheduler;
+extern class TransporterRegistry globalTransporterRegistry;
+extern struct GlobalData globalData;
+
+#ifdef VM_TRACE
+extern class SignalLoggerManager globalSignalLoggers;
+#endif
+
+#ifndef NO_EMULATED_JAM
+ #define EMULATED_JAM_SIZE 1024
+ #define JAM_MASK ((EMULATED_JAM_SIZE * 4) - 1)
+
+ extern Uint8 theEmulatedJam[];
+ extern Uint32 theEmulatedJamIndex;
+ // last block entry, used in dumpJam() if jam contains no block entries
+ extern Uint32 theEmulatedJamBlockNumber;
+#else
+ const Uint8 theEmulatedJam[]=0;
+ const Uint32 theEmulatedJamIndex=0;
+#endif
+
+struct EmulatorData {
+ class Configuration * theConfiguration;
+ class WatchDog * theWatchDog;
+ class ThreadConfig * theThreadConfig;
+ class SimBlockList * theSimBlockList;
+ class SocketServer * m_socket_server;
+
+ /**
+ * Constructor
+ *
+ * Sets all the pointers to NULL
+ */
+ EmulatorData();
+
+ /**
+ * Create all the objects
+ */
+ void create();
+
+ /**
+ * Destroys all the objects
+ */
+ void destroy();
+};
+
+extern struct EmulatorData globalEmulatorData;
+
+enum NdbShutdownType {
+ NST_Normal,
+ NST_Watchdog,
+ NST_ErrorHandler,
+ NST_ErrorHandlerSignal,
+ NST_Restart,
+ NST_ErrorInsert
+};
+
+enum NdbRestartType {
+ NRT_Default = 0,
+ NRT_NoStart_Restart = 1, // -n
+ NRT_DoStart_Restart = 2, //
+ NRT_NoStart_InitialStart = 3, // -n -i
+ NRT_DoStart_InitialStart = 4 // -i
+};
+
+/**
+ * Shutdown/restart Ndb
+ *
+ * @param type - Type of shutdown/restart
+ * @param restartType - Type of restart (only valid if type == NST_Restart)
+ */
+void
+NdbShutdown(NdbShutdownType type,
+ NdbRestartType restartType = NRT_Default);
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/FastScheduler.cpp b/storage/ndb/src/kernel/vm/FastScheduler.cpp
new file mode 100644
index 00000000000..a2d806571fe
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/FastScheduler.cpp
@@ -0,0 +1,500 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "FastScheduler.hpp"
+#include "RefConvert.hpp"
+
+#include "Emulator.hpp"
+#include "VMSignal.hpp"
+#include <Error.hpp>
+
+#include <SignalLoggerManager.hpp>
+#include <BlockNumbers.h>
+#include <GlobalSignalNumbers.h>
+#include <signaldata/EventReport.hpp>
+#include "LongSignal.hpp"
+#include <NdbTick.h>
+
+#define MIN_NUMBER_OF_SIG_PER_DO_JOB 64
+#define MAX_NUMBER_OF_SIG_PER_DO_JOB 2048
+#define EXTRA_SIGNALS_PER_DO_JOB 32
+
+FastScheduler::FastScheduler()
+{
+ // These constants work for sun only, but they should be initated from
+ // Emulator.C as soon as VMTime has been initiated.
+ theJobBuffers[0].newBuffer(JBASIZE);
+ theJobBuffers[1].newBuffer(JBBSIZE);
+ theJobBuffers[2].newBuffer(JBCSIZE);
+ theJobBuffers[3].newBuffer(JBDSIZE);
+ clear();
+}
+
+FastScheduler::~FastScheduler()
+{
+}
+
+void
+FastScheduler::clear()
+{
+ int i;
+ // Make sure the restart signals are not sent too early
+ // the prio is set back in 'main' using the 'ready' method.
+ globalData.highestAvailablePrio = LEVEL_IDLE;
+ globalData.sendPackedActivated = 0;
+ globalData.activateSendPacked = 0;
+ for (i = 0; i < JB_LEVELS; i++){
+ theJobBuffers[i].clear();
+ }
+ globalData.JobCounter = 0;
+ globalData.JobLap = 0;
+ globalData.loopMax = 32;
+ globalData.VMSignals[0].header.theSignalId = 0;
+
+ theDoJobTotalCounter = 0;
+ theDoJobCallCounter = 0;
+}
+
+void
+FastScheduler::activateSendPacked()
+{
+ globalData.sendPackedActivated = 1;
+ globalData.activateSendPacked = 0;
+ globalData.loopMax = 2048;
+}//FastScheduler::activateSendPacked()
+
+//------------------------------------------------------------------------
+// sendPacked is executed at the end of the loop.
+// To ensure that we don't send any messages before executing all local
+// packed signals we do another turn in the loop (unless we have already
+// executed too many signals in the loop).
+//------------------------------------------------------------------------
+void
+FastScheduler::doJob()
+{
+ Uint32 loopCount = 0;
+ Uint32 TminLoops = getBOccupancy() + EXTRA_SIGNALS_PER_DO_JOB;
+ Uint32 TloopMax = (Uint32)globalData.loopMax;
+ if (TminLoops < TloopMax) {
+ TloopMax = TminLoops;
+ }//if
+ if (TloopMax < MIN_NUMBER_OF_SIG_PER_DO_JOB) {
+ TloopMax = MIN_NUMBER_OF_SIG_PER_DO_JOB;
+ }//if
+ register Signal* signal = getVMSignals();
+ register Uint32 tHighPrio= globalData.highestAvailablePrio;
+ do{
+ while ((tHighPrio < LEVEL_IDLE) && (loopCount < TloopMax)) {
+ // signal->garbage_register();
+ // To ensure we find bugs quickly
+ register Uint32 gsnbnr = theJobBuffers[tHighPrio].retrieve(signal);
+ register BlockNumber reg_bnr = gsnbnr & 0xFFF;
+ register GlobalSignalNumber reg_gsn = gsnbnr >> 16;
+ globalData.incrementWatchDogCounter(1);
+ if (reg_bnr > 0) {
+ Uint32 tJobCounter = globalData.JobCounter;
+ Uint32 tJobLap = globalData.JobLap;
+ SimulatedBlock* b = globalData.getBlock(reg_bnr);
+ theJobPriority[tJobCounter] = (Uint8)tHighPrio;
+ globalData.JobCounter = (tJobCounter + 1) & 4095;
+ globalData.JobLap = tJobLap + 1;
+
+#ifdef VM_TRACE_TIME
+ Uint32 us1, us2;
+ Uint64 ms1, ms2;
+ NdbTick_CurrentMicrosecond(&ms1, &us1);
+ b->m_currentGsn = reg_gsn;
+#endif
+
+ getSections(signal->header.m_noOfSections, signal->m_sectionPtr);
+#ifdef VM_TRACE
+ {
+ if (globalData.testOn) {
+ signal->header.theVerId_signalNumber = reg_gsn;
+ signal->header.theReceiversBlockNumber = reg_bnr;
+
+ globalSignalLoggers.executeSignal(signal->header,
+ tHighPrio,
+ &signal->theData[0],
+ globalData.ownId,
+ signal->m_sectionPtr,
+ signal->header.m_noOfSections);
+ }//if
+ }
+#endif
+ b->executeFunction(reg_gsn, signal);
+ releaseSections(signal->header.m_noOfSections, signal->m_sectionPtr);
+ signal->header.m_noOfSections = 0;
+#ifdef VM_TRACE_TIME
+ NdbTick_CurrentMicrosecond(&ms2, &us2);
+ Uint64 diff = ms2;
+ diff -= ms1;
+ diff *= 1000000;
+ diff += us2;
+ diff -= us1;
+ b->addTime(reg_gsn, diff);
+#endif
+ tHighPrio = globalData.highestAvailablePrio;
+ } else {
+ tHighPrio++;
+ globalData.highestAvailablePrio = tHighPrio;
+ }//if
+ loopCount++;
+ }//while
+ sendPacked();
+ tHighPrio = globalData.highestAvailablePrio;
+ if(getBOccupancy() > MAX_OCCUPANCY)
+ {
+ if(loopCount != TloopMax)
+ abort();
+ assert( loopCount == TloopMax );
+ TloopMax += 512;
+ }
+ } while ((getBOccupancy() > MAX_OCCUPANCY) ||
+ ((loopCount < TloopMax) &&
+ (tHighPrio < LEVEL_IDLE)));
+
+ theDoJobCallCounter ++;
+ theDoJobTotalCounter += loopCount;
+ if (theDoJobCallCounter == 8192) {
+ reportDoJobStatistics(theDoJobTotalCounter >> 13);
+ theDoJobCallCounter = 0;
+ theDoJobTotalCounter = 0;
+ }//if
+
+}//FastScheduler::doJob()
+
+void FastScheduler::sendPacked()
+{
+ if (globalData.sendPackedActivated == 1) {
+ SimulatedBlock* b_lqh = globalData.getBlock(DBLQH);
+ SimulatedBlock* b_tc = globalData.getBlock(DBTC);
+ SimulatedBlock* b_tup = globalData.getBlock(DBTUP);
+ Signal* signal = getVMSignals();
+ b_lqh->executeFunction(GSN_SEND_PACKED, signal);
+ b_tc->executeFunction(GSN_SEND_PACKED, signal);
+ b_tup->executeFunction(GSN_SEND_PACKED, signal);
+ return;
+ } else if (globalData.activateSendPacked == 0) {
+ return;
+ } else {
+ activateSendPacked();
+ }//if
+ return;
+}//FastScheduler::sendPacked()
+
+Uint32
+APZJobBuffer::retrieve(Signal* signal)
+{
+ Uint32 tOccupancy = theOccupancy;
+ Uint32 myRPtr = rPtr;
+ BufferEntry& buf = buffer[myRPtr];
+ Uint32 gsnbnr;
+ Uint32 cond = (++myRPtr == bufSize) - 1;
+ Uint32 tRecBlockNo = buf.header.theReceiversBlockNumber;
+
+ if (tOccupancy != 0) {
+ if (tRecBlockNo != 0) {
+ // Transform protocol to signal.
+ rPtr = myRPtr & cond;
+ theOccupancy = tOccupancy - 1;
+ gsnbnr = buf.header.theVerId_signalNumber << 16 | tRecBlockNo;
+
+ Uint32 tSignalId = globalData.theSignalId;
+ Uint32 tLength = buf.header.theLength;
+ Uint32 tFirstData = buf.theDataRegister[0];
+ signal->header = buf.header;
+
+ // Recall our signal Id for restart purposes
+ buf.header.theSignalId = tSignalId;
+ globalData.theSignalId = tSignalId + 1;
+
+ Uint32* tDataRegPtr = &buf.theDataRegister[0];
+ Uint32* tSigDataPtr = signal->getDataPtrSend();
+ *tSigDataPtr = tFirstData;
+ tDataRegPtr++;
+ tSigDataPtr++;
+ Uint32 tLengthCopied = 1;
+ while (tLengthCopied < tLength) {
+ Uint32 tData0 = tDataRegPtr[0];
+ Uint32 tData1 = tDataRegPtr[1];
+ Uint32 tData2 = tDataRegPtr[2];
+ Uint32 tData3 = tDataRegPtr[3];
+
+ tDataRegPtr += 4;
+ tLengthCopied += 4;
+
+ tSigDataPtr[0] = tData0;
+ tSigDataPtr[1] = tData1;
+ tSigDataPtr[2] = tData2;
+ tSigDataPtr[3] = tData3;
+ tSigDataPtr += 4;
+ }//while
+
+ /**
+ * Copy sections references (copy all without if-statements)
+ */
+ tDataRegPtr = &buf.theDataRegister[tLength];
+ SegmentedSectionPtr * tSecPtr = &signal->m_sectionPtr[0];
+ Uint32 tData0 = tDataRegPtr[0];
+ Uint32 tData1 = tDataRegPtr[1];
+ Uint32 tData2 = tDataRegPtr[2];
+
+ tSecPtr[0].i = tData0;
+ tSecPtr[1].i = tData1;
+ tSecPtr[2].i = tData2;
+
+ //---------------------------------------------------------
+ // Prefetch of buffer[rPtr] is done here. We prefetch for
+ // read both the first cache line and the next 64 byte
+ // entry
+ //---------------------------------------------------------
+ PREFETCH((void*)&buffer[rPtr]);
+ PREFETCH((void*)(((char*)&buffer[rPtr]) + 64));
+ return gsnbnr;
+ } else {
+ bnr_error();
+ return 0; // Will never come here, simply to keep GCC happy.
+ }//if
+ } else {
+ //------------------------------------------------------------
+ // The Job Buffer was empty, signal this by return zero.
+ //------------------------------------------------------------
+ return 0;
+ }//if
+}//APZJobBuffer::retrieve()
+
+void
+APZJobBuffer::signal2buffer(Signal* signal,
+ BlockNumber bnr, GlobalSignalNumber gsn,
+ BufferEntry& buf)
+{
+ Uint32 tSignalId = globalData.theSignalId;
+ Uint32 tFirstData = signal->theData[0];
+ Uint32 tLength = signal->header.theLength;
+ Uint32 tSigId = buf.header.theSignalId;
+
+ buf.header = signal->header;
+ buf.header.theVerId_signalNumber = gsn;
+ buf.header.theReceiversBlockNumber = bnr;
+ buf.header.theSendersSignalId = tSignalId - 1;
+ buf.header.theSignalId = tSigId;
+ buf.theDataRegister[0] = tFirstData;
+
+ Uint32 tLengthCopied = 1;
+ Uint32* tSigDataPtr = &signal->theData[1];
+ Uint32* tDataRegPtr = &buf.theDataRegister[1];
+ while (tLengthCopied < tLength) {
+ Uint32 tData0 = tSigDataPtr[0];
+ Uint32 tData1 = tSigDataPtr[1];
+ Uint32 tData2 = tSigDataPtr[2];
+ Uint32 tData3 = tSigDataPtr[3];
+
+ tLengthCopied += 4;
+ tSigDataPtr += 4;
+
+ tDataRegPtr[0] = tData0;
+ tDataRegPtr[1] = tData1;
+ tDataRegPtr[2] = tData2;
+ tDataRegPtr[3] = tData3;
+ tDataRegPtr += 4;
+ }//while
+
+ /**
+ * Copy sections references (copy all without if-statements)
+ */
+ tDataRegPtr = &buf.theDataRegister[tLength];
+ SegmentedSectionPtr * tSecPtr = &signal->m_sectionPtr[0];
+ Uint32 tData0 = tSecPtr[0].i;
+ Uint32 tData1 = tSecPtr[1].i;
+ Uint32 tData2 = tSecPtr[2].i;
+ tDataRegPtr[0] = tData0;
+ tDataRegPtr[1] = tData1;
+ tDataRegPtr[2] = tData2;
+}//APZJobBuffer::signal2buffer()
+
+void
+APZJobBuffer::insert(const SignalHeader * const sh,
+ const Uint32 * const theData, const Uint32 secPtrI[3]){
+ Uint32 tOccupancy = theOccupancy + 1;
+ Uint32 myWPtr = wPtr;
+ register BufferEntry& buf = buffer[myWPtr];
+
+ if (tOccupancy < bufSize) {
+ Uint32 cond = (++myWPtr == bufSize) - 1;
+ wPtr = myWPtr & cond;
+ theOccupancy = tOccupancy;
+
+ buf.header = * sh;
+ const Uint32 len = buf.header.theLength;
+ memcpy(buf.theDataRegister, theData, 4 * len);
+ memcpy(&buf.theDataRegister[len], &secPtrI[0], 4 * 3);
+ //---------------------------------------------------------
+ // Prefetch of buffer[wPtr] is done here. We prefetch for
+ // write both the first cache line and the next 64 byte
+ // entry
+ //---------------------------------------------------------
+ WRITEHINT((void*)&buffer[wPtr]);
+ WRITEHINT((void*)(((char*)&buffer[wPtr]) + 64));
+
+ } else {
+ jbuf_error();
+ }//if
+}
+APZJobBuffer::APZJobBuffer()
+ : bufSize(0), buffer(NULL), memRef(NULL)
+{
+ clear();
+}
+
+APZJobBuffer::~APZJobBuffer()
+{
+ delete [] buffer;
+}
+
+void
+APZJobBuffer::newBuffer(int size)
+{
+ buffer = new BufferEntry[size + 1]; // +1 to support "overrrun"
+ if(buffer){
+#ifndef NDB_PURIFY
+ ::memset(buffer, 0, (size * sizeof(BufferEntry)));
+#endif
+ bufSize = size;
+ } else
+ bufSize = 0;
+}
+
+void
+APZJobBuffer::clear()
+{
+ rPtr = 0;
+ wPtr = 0;
+ theOccupancy = 0;
+}
+
+/**
+ * Function prototype for print_restart
+ *
+ * Defined later in this file
+ */
+void print_restart(FILE * output, Signal* signal, Uint32 aLevel);
+
+void FastScheduler::dumpSignalMemory(FILE * output)
+{
+ Signal signal;
+ Uint32 ReadPtr[5];
+ Uint32 tJob;
+ Uint32 tLastJob;
+
+ fprintf(output, "\n");
+
+ if (globalData.JobLap > 4095) {
+ if (globalData.JobCounter != 0)
+ tJob = globalData.JobCounter - 1;
+ else
+ tJob = 4095;
+ tLastJob = globalData.JobCounter;
+ } else {
+ if (globalData.JobCounter == 0)
+ return; // No signals sent
+ else {
+ tJob = globalData.JobCounter - 1;
+ tLastJob = 4095;
+ }
+ }
+ ReadPtr[0] = theJobBuffers[0].getReadPtr();
+ ReadPtr[1] = theJobBuffers[1].getReadPtr();
+ ReadPtr[2] = theJobBuffers[2].getReadPtr();
+ ReadPtr[3] = theJobBuffers[3].getReadPtr();
+
+ do {
+ unsigned char tLevel = theJobPriority[tJob];
+ globalData.incrementWatchDogCounter(4);
+ if (ReadPtr[tLevel] == 0)
+ ReadPtr[tLevel] = theJobBuffers[tLevel].getBufSize() - 1;
+ else
+ ReadPtr[tLevel]--;
+
+ theJobBuffers[tLevel].retrieveDump(&signal, ReadPtr[tLevel]);
+ print_restart(output, &signal, tLevel);
+
+ if (tJob == 0)
+ tJob = 4095;
+ else
+ tJob--;
+
+ } while (tJob != tLastJob);
+ fflush(output);
+}
+
+void
+FastScheduler::prio_level_error()
+{
+ ERROR_SET(ecError, ERROR_WRONG_PRIO_LEVEL,
+ "Wrong Priority Level", "FastScheduler.C");
+}
+
+void
+jbuf_error()
+{
+ ERROR_SET(ecError, BLOCK_ERROR_JBUFCONGESTION,
+ "Job Buffer Full", "APZJobBuffer.C");
+}
+
+void
+bnr_error()
+{
+ ERROR_SET(ecError, BLOCK_ERROR_BNR_ZERO,
+ "Block Number Zero", "FastScheduler.C");
+}
+
+void
+print_restart(FILE * output, Signal* signal, Uint32 aLevel)
+{
+ fprintf(output, "--------------- Signal ----------------\n");
+ SignalLoggerManager::printSignalHeader(output,
+ signal->header,
+ aLevel,
+ globalData.ownId,
+ true);
+ SignalLoggerManager::printSignalData (output,
+ signal->header,
+ &signal->theData[0]);
+}
+
+/**
+ * This method used to be a Cmvmi member function
+ * but is now a "ordinary" function"
+ *
+ * See TransporterCallback.cpp for explanation
+ */
+void
+FastScheduler::reportDoJobStatistics(Uint32 tMeanLoopCount) {
+ Signal signal;
+ memset(&signal.header, 0, sizeof(signal.header));
+
+ signal.theData[0] = NDB_LE_JobStatistic;
+ signal.theData[1] = tMeanLoopCount;
+
+ memset(&signal.header, 0, sizeof(SignalHeader));
+ signal.header.theLength = 2;
+ signal.header.theSendersSignalId = 0;
+ signal.header.theSendersBlockRef = numberToRef(0, 0);
+
+ execute(&signal, JBA, CMVMI, GSN_EVENT_REP);
+}
+
diff --git a/storage/ndb/src/kernel/vm/FastScheduler.hpp b/storage/ndb/src/kernel/vm/FastScheduler.hpp
new file mode 100644
index 00000000000..dc707e47eef
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/FastScheduler.hpp
@@ -0,0 +1,345 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 FastScheduler_H
+#define FastScheduler_H
+
+#include <VMSignal.hpp>
+#include <kernel_types.h>
+#include <Prio.hpp>
+#include <SignalLoggerManager.hpp>
+#include <SimulatedBlock.hpp>
+#include <ErrorHandlingMacros.hpp>
+#include <GlobalData.hpp>
+#include <TransporterDefinitions.hpp>
+#include <prefetch.h>
+
+#define MAX_OCCUPANCY 1024
+
+#define JBASIZE 1280 // Jobs which have dead lines to meet use this level
+#define JBBSIZE 4096 // Most jobs use this level
+#define JBCSIZE 64 // Only used by STTOR and STTORRY currently
+#define JBDSIZE 4096 // Time Queue uses this level for storage, not supported
+ // as priority level
+void bnr_error();
+void jbuf_error();
+class Signal;
+class Block;
+
+class BufferEntry
+{
+public:
+ SignalHeader header;
+ Uint32 theDataRegister[25];
+};
+
+class APZJobBuffer
+{
+public:
+ APZJobBuffer();
+ ~APZJobBuffer();
+
+ void newBuffer(int size);
+
+ void insert(Signal* signal, BlockNumber bnr, GlobalSignalNumber gsn);
+ void insert(const SignalHeader * const sh, const Uint32 * const theData, const Uint32 secPtrI[3]);
+ void insert(Signal* signal, BlockNumber bnr, GlobalSignalNumber gsn,
+ Uint32 myWPtr);
+
+ Uint32 retrieve(Signal *signal);
+ void retrieve(Signal *signal, Uint32 myRptr);
+
+ /**
+ * Used when dumping to trace file
+ */
+ void retrieveDump(Signal *signal, Uint32 myRptr);
+
+ void clear();
+ Uint32 getOccupancy() const;
+
+ Uint32 getReadPtr() const;
+ Uint32 getWritePtr() const;
+ Uint32 getBufSize() const;
+
+private:
+ void signal2buffer(Signal* signal, BlockNumber bnr,
+ GlobalSignalNumber gsn, BufferEntry& buf);
+ Uint32 rPtr;
+ Uint32 wPtr;
+ Uint32 theOccupancy;
+ Uint32 bufSize;
+ BufferEntry* buffer;
+ BufferEntry* memRef;
+};
+
+
+class FastScheduler
+{
+public:
+ FastScheduler();
+ ~FastScheduler();
+
+ void doJob();
+ int checkDoJob();
+
+ void activateSendPacked();
+
+ void execute(Signal* signal,
+ Priority prio,
+ BlockNumber bnr,
+ GlobalSignalNumber gsn);
+
+ void execute(const SignalHeader * const sh,
+ Uint8 prio, const Uint32 * const theData, const Uint32 secPtr[3]);
+
+ void clear();
+ Signal* getVMSignals();
+
+ void dumpSignalMemory(FILE * output);
+ Priority highestAvailablePrio() const;
+ Uint32 getBOccupancy() const;
+ void sendPacked();
+
+ void insertTimeQueue(Signal* aSignal, BlockNumber bnr,
+ GlobalSignalNumber gsn, Uint32 aIndex);
+ void scheduleTimeQueue(Uint32 aIndex);
+
+private:
+ void highestAvailablePrio(Priority prio);
+ void reportJob(Priority aPriority);
+ void prio_level_error();
+
+ Uint32 theDoJobTotalCounter;
+ Uint32 theDoJobCallCounter;
+ Uint8 theJobPriority[4096];
+ APZJobBuffer theJobBuffers[JB_LEVELS];
+
+ void reportDoJobStatistics(Uint32 meanLoopCount);
+};
+
+inline
+Uint32
+FastScheduler::getBOccupancy() const {
+ return theJobBuffers[JBB].getOccupancy();
+}//FastScheduler::getBOccupancy()
+
+inline
+int
+FastScheduler::checkDoJob()
+{
+ /*
+ * Job buffer overload protetction
+ * If the job buffer B is filled over a certain limit start
+ * to execute the signals in the job buffer's
+ */
+ if (getBOccupancy() < MAX_OCCUPANCY) {
+ return 0;
+ } else {
+ doJob();
+ return 1;
+ }//if
+}//FastScheduler::checkDoJob()
+
+inline
+void
+FastScheduler::reportJob(Priority aPriority)
+{
+ Uint32 tJobCounter = globalData.JobCounter;
+ Uint32 tJobLap = globalData.JobLap;
+ theJobPriority[tJobCounter] = (Uint8)aPriority;
+ globalData.JobCounter = (tJobCounter + 1) & 4095;
+ globalData.JobLap = tJobLap + 1;
+}
+
+inline
+Priority
+FastScheduler::highestAvailablePrio() const
+{
+ return (Priority)globalData.highestAvailablePrio;
+}
+
+inline
+void
+FastScheduler::highestAvailablePrio(Priority prio)
+{
+ globalData.highestAvailablePrio = (Uint32)prio;
+}
+
+inline
+Signal*
+FastScheduler::getVMSignals()
+{
+ return &globalData.VMSignals[0];
+}
+
+
+// Inserts of a protocol object into the Job Buffer.
+inline
+void
+FastScheduler::execute(const SignalHeader * const sh, Uint8 prio,
+ const Uint32 * const theData, const Uint32 secPtrI[3]){
+#ifdef VM_TRACE
+ if (prio >= LEVEL_IDLE)
+ prio_level_error();
+#endif
+
+ theJobBuffers[prio].insert(sh, theData, secPtrI);
+ if (prio < (Uint8)highestAvailablePrio())
+ highestAvailablePrio((Priority)prio);
+}
+
+inline
+void
+FastScheduler::execute(Signal* signal, Priority prio,
+ BlockNumber bnr, GlobalSignalNumber gsn)
+{
+#ifdef VM_TRACE
+ if (prio >= LEVEL_IDLE)
+ prio_level_error();
+#endif
+ theJobBuffers[prio].insert(signal, bnr, gsn);
+ if (prio < highestAvailablePrio())
+ highestAvailablePrio(prio);
+}
+
+inline
+void
+FastScheduler::insertTimeQueue(Signal* signal, BlockNumber bnr,
+ GlobalSignalNumber gsn, Uint32 aIndex)
+{
+ theJobBuffers[3].insert(signal, bnr, gsn, aIndex);
+}
+
+inline
+void
+FastScheduler::scheduleTimeQueue(Uint32 aIndex)
+{
+ Signal* signal = getVMSignals();
+ theJobBuffers[3].retrieve(signal, aIndex);
+ theJobBuffers[0].insert
+ (signal,
+ (BlockNumber)signal->header.theReceiversBlockNumber,
+ (GlobalSignalNumber)signal->header.theVerId_signalNumber);
+ if (highestAvailablePrio() > JBA)
+ highestAvailablePrio(JBA);
+}
+
+inline
+Uint32
+APZJobBuffer::getWritePtr() const
+{
+ return wPtr;
+}
+
+inline
+Uint32
+APZJobBuffer::getReadPtr() const
+{
+ return rPtr;
+}
+
+inline
+Uint32
+APZJobBuffer::getOccupancy() const
+{
+ return theOccupancy;
+}
+
+inline
+Uint32
+APZJobBuffer::getBufSize() const
+{
+ return bufSize;
+}
+
+inline
+void
+APZJobBuffer::retrieve(Signal* signal, Uint32 myRptr)
+{
+ register BufferEntry& buf = buffer[myRptr];
+
+ buf.header.theSignalId = globalData.theSignalId++;
+
+ signal->header = buf.header;
+
+ Uint32 *from = (Uint32*) &buf.theDataRegister[0];
+ Uint32 *to = (Uint32*) &signal->theData[0];
+ Uint32 noOfWords = buf.header.theLength;
+ for(; noOfWords; noOfWords--)
+ *to++ = *from++;
+ // Copy sections references (copy all without if-statements)
+ SegmentedSectionPtr * tSecPtr = &signal->m_sectionPtr[0];
+ tSecPtr[0].i = from[0];
+ tSecPtr[1].i = from[1];
+ tSecPtr[2].i = from[2];
+ return;
+}
+
+inline
+void
+APZJobBuffer::retrieveDump(Signal* signal, Uint32 myRptr)
+{
+ /**
+ * Note that signal id is not taken from global data
+ */
+
+ register BufferEntry& buf = buffer[myRptr];
+ signal->header = buf.header;
+
+ Uint32 *from = (Uint32*) &buf.theDataRegister[0];
+ Uint32 *to = (Uint32*) &signal->theData[0];
+ Uint32 noOfWords = buf.header.theLength;
+ for(; noOfWords; noOfWords--)
+ *to++ = *from++;
+ return;
+}
+
+inline
+void
+APZJobBuffer::insert(Signal* signal,
+ BlockNumber bnr, GlobalSignalNumber gsn)
+{
+ Uint32 tOccupancy = theOccupancy + 1;
+ Uint32 myWPtr = wPtr;
+ if (tOccupancy < bufSize) {
+ register BufferEntry& buf = buffer[myWPtr];
+ Uint32 cond = (++myWPtr == bufSize) - 1;
+ wPtr = myWPtr & cond;
+ theOccupancy = tOccupancy;
+ signal2buffer(signal, bnr, gsn, buf);
+ //---------------------------------------------------------
+ // Prefetch of buffer[wPtr] is done here. We prefetch for
+ // write both the first cache line and the next 64 byte
+ // entry
+ //---------------------------------------------------------
+ WRITEHINT((void*)&buffer[wPtr]);
+ WRITEHINT((void*)(((char*)&buffer[wPtr]) + 64));
+ } else {
+ jbuf_error();
+ }//if
+}
+
+
+inline
+void
+APZJobBuffer::insert(Signal* signal, BlockNumber bnr,
+ GlobalSignalNumber gsn, Uint32 myWPtr)
+{
+ register BufferEntry& buf = buffer[myWPtr];
+ signal2buffer(signal, bnr, gsn, buf);
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/GlobalData.hpp b/storage/ndb/src/kernel/vm/GlobalData.hpp
new file mode 100644
index 00000000000..99b65727374
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/GlobalData.hpp
@@ -0,0 +1,117 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 GLOBAL_DATA_H
+#define GLOBAL_DATA_H
+
+#include <ndb_global.h>
+#include <kernel_types.h>
+#include "Prio.hpp"
+#include "VMSignal.hpp"
+
+#include <BlockNumbers.h>
+#include <NodeState.hpp>
+#include <NodeInfo.hpp>
+
+class SimulatedBlock;
+
+enum restartStates {initial_state,
+ perform_start,
+ system_started,
+ perform_stop};
+
+struct GlobalData {
+ NodeInfo m_nodeInfo[MAX_NODES];
+ Signal VMSignals[1]; // Owned by FastScheduler::
+
+ Uint64 internalMillisecCounter; // Owned by ThreadConfig::
+ Uint32 highestAvailablePrio; // Owned by FastScheduler::
+ Uint32 JobCounter; // Owned by FastScheduler
+ Uint64 JobLap; // Owned by FastScheduler
+ Uint32 loopMax; // Owned by FastScheduler
+
+ Uint32 theNextTimerJob; // Owned by TimeQueue::
+ Uint32 theCurrentTimer; // Owned by TimeQueue::
+ Uint32 theShortTQIndex; // Owned by TimeQueue::
+
+ Uint32 theLongTQIndex; // Owned by TimeQueue::
+ Uint32 theCountTimer; // Owned by TimeQueue::
+ Uint32 theFirstFreeTQIndex; // Owned by TimeQueue::
+ Uint32 testOn; // Owned by the Signal Loggers
+
+ NodeId ownId; // Own processor id
+
+ Uint32 theStartLevel;
+ restartStates theRestartFlag;
+ Uint32 theSignalId;
+
+ Uint32 sendPackedActivated;
+ Uint32 activateSendPacked;
+
+ GlobalData(){
+ theSignalId = 0;
+ theStartLevel = NodeState::SL_NOTHING;
+ theRestartFlag = perform_start;
+ }
+ ~GlobalData(){}
+
+ void setBlock(BlockNumber blockNo, SimulatedBlock * block);
+ SimulatedBlock * getBlock(BlockNumber blockNo);
+
+ void incrementWatchDogCounter(Uint32 place);
+ const Uint32 * getWatchDogPtr();
+
+private:
+ Uint32 watchDog;
+ SimulatedBlock* blockTable[NO_OF_BLOCKS]; // Owned by Dispatcher::
+};
+
+extern GlobalData globalData;
+
+#define GLOBAL_TEST_ON (localTestOn)
+#define GET_GLOBAL_TEST_FLAG bool localTestOn = globalData.testOn
+#define SET_GLOBAL_TEST_ON (globalData.testOn = true)
+#define SET_GLOBAL_TEST_OFF (globalData.testOn = false)
+#define TOGGLE_GLOBAL_TEST_FLAG (globalData.testOn = (globalData.testOn == true ? false : true))
+
+inline
+void
+GlobalData::setBlock(BlockNumber blockNo, SimulatedBlock * block){
+ blockNo -= MIN_BLOCK_NO;
+ assert((blockTable[blockNo] == 0) || (blockTable[blockNo] == block));
+ blockTable[blockNo] = block;
+}
+
+inline
+SimulatedBlock *
+GlobalData::getBlock(BlockNumber blockNo){
+ blockNo -= MIN_BLOCK_NO;
+ return blockTable[blockNo];
+}
+
+inline
+void
+GlobalData::incrementWatchDogCounter(Uint32 place){
+ watchDog = place;
+}
+
+inline
+const Uint32 *
+GlobalData::getWatchDogPtr(){
+ return &watchDog;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/KeyTable.hpp b/storage/ndb/src/kernel/vm/KeyTable.hpp
new file mode 100644
index 00000000000..e78837b5c8a
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/KeyTable.hpp
@@ -0,0 +1,43 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 KEY_TABLE_HPP
+#define KEY_TABLE_HPP
+
+#include <DLHashTable.hpp>
+
+/**
+ * KeyTable2 is DLHashTable2 with hardcoded Uint32 key named "key".
+ */
+template <class T>
+class KeyTable : public DLHashTable<T> {
+public:
+ KeyTable(ArrayPool<T>& pool) :
+ DLHashTable<T>(pool) {
+ }
+
+ bool find(Ptr<T>& ptr, const T& rec) const {
+ return DLHashTable<T>::find(ptr, rec);
+ }
+
+ bool find(Ptr<T>& ptr, Uint32 key) const {
+ T rec;
+ rec.key = key;
+ return DLHashTable<T>::find(ptr, rec);
+ }
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/KeyTable2.hpp b/storage/ndb/src/kernel/vm/KeyTable2.hpp
new file mode 100644
index 00000000000..5c2b3096abe
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/KeyTable2.hpp
@@ -0,0 +1,43 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 KEY_TABLE2_HPP
+#define KEY_TABLE2_HPP
+
+#include <DLHashTable2.hpp>
+
+/**
+ * KeyTable2 is DLHashTable2 with hardcoded Uint32 key named "key".
+ */
+template <class T, class U>
+class KeyTable2 : public DLHashTable2<T, U> {
+public:
+ KeyTable2(ArrayPool<U>& pool) :
+ DLHashTable2<T, U>(pool) {
+ }
+
+ bool find(Ptr<T>& ptr, const T& rec) const {
+ return DLHashTable2<T, U>::find(ptr, rec);
+ }
+
+ bool find(Ptr<T>& ptr, Uint32 key) const {
+ T rec;
+ rec.key = key;
+ return DLHashTable2<T, U>::find(ptr, rec);
+ }
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/LongSignal.hpp b/storage/ndb/src/kernel/vm/LongSignal.hpp
new file mode 100644
index 00000000000..9818358011f
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/LongSignal.hpp
@@ -0,0 +1,79 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 LONG_SIGNAL_HPP
+#define LONG_SIGNAL_HPP
+
+#include "pc.hpp"
+#include <ArrayPool.hpp>
+
+/**
+ * Section handling
+ */
+struct SectionSegment {
+
+ STATIC_CONST( DataLength = NDB_SECTION_SEGMENT_SZ );
+
+ Uint32 m_ownerRef;
+ Uint32 m_sz;
+ Uint32 m_lastSegment;
+ union {
+ Uint32 m_nextSegment;
+ Uint32 nextPool;
+ };
+ Uint32 theData[DataLength];
+};
+
+/**
+ * Pool for SectionSegments
+ */
+class SectionSegmentPool : public ArrayPool<SectionSegment> {};
+
+/**
+ * And the instance
+ */
+extern SectionSegmentPool g_sectionSegmentPool;
+
+/**
+ * Function prototypes
+ */
+void print(SegmentedSectionPtr ptr, FILE* out);
+void copy(SegmentedSectionPtr dst, Uint32 * src, Uint32 len);
+void copy(Uint32 * dst, SegmentedSectionPtr src);
+
+extern class SectionSegmentPool g_sectionSegmentPool;
+void getSection(SegmentedSectionPtr & ptr, Uint32 id);
+void linkSegments(Uint32 head, Uint32 tail);
+
+void getSections(Uint32 secCount, SegmentedSectionPtr ptr[3]);
+void releaseSections(Uint32 secCount, SegmentedSectionPtr ptr[3]);
+
+
+#include "DataBuffer.hpp"
+
+template<Uint32 sz>
+void
+append(DataBuffer<sz>& dst, SegmentedSectionPtr ptr, SectionSegmentPool& pool){
+ Uint32 len = ptr.sz;
+ while(len > SectionSegment::DataLength){
+ dst.append(ptr.p->theData, SectionSegment::DataLength);
+ ptr.p = pool.getPtr(ptr.p->m_nextSegment);
+ len -= SectionSegment::DataLength;
+ }
+ dst.append(ptr.p->theData, len);
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/Makefile.am b/storage/ndb/src/kernel/vm/Makefile.am
new file mode 100644
index 00000000000..0dce9285ae3
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Makefile.am
@@ -0,0 +1,43 @@
+#SUBDIRS = testCopy testDataBuffer testSimplePropertiesSection
+#ifneq ($(USE_EDITLINE), N)
+#DIRS += testLongSig
+#endif
+
+noinst_LIBRARIES = libkernel.a
+
+libkernel_a_SOURCES = \
+ SimulatedBlock.cpp \
+ FastScheduler.cpp \
+ TimeQueue.cpp \
+ VMSignal.cpp \
+ ThreadConfig.cpp \
+ TransporterCallback.cpp \
+ Emulator.cpp \
+ Configuration.cpp \
+ WatchDog.cpp \
+ SimplePropertiesSection.cpp \
+ SectionReader.cpp \
+ MetaData.cpp \
+ Mutex.cpp SafeCounter.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/mgmapi
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_kernel.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libkernel.dsp
+
+libkernel.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libkernel_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/kernel/vm/MetaData.cpp b/storage/ndb/src/kernel/vm/MetaData.cpp
new file mode 100644
index 00000000000..51afbf21503
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/MetaData.cpp
@@ -0,0 +1,113 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "MetaData.hpp"
+#include "SimulatedBlock.hpp"
+#include <blocks/dbdict/Dbdict.hpp>
+#include <blocks/dbdih/Dbdih.hpp>
+
+// MetaData::Common
+
+MetaData::Common::Common(Dbdict& dbdict, Dbdih& dbdih) :
+ m_dbdict(dbdict),
+ m_dbdih(dbdih)
+{
+ m_lock[false] = m_lock[true] = 0;
+}
+
+// MetaData::Table
+
+// MetaData
+
+MetaData::MetaData(Common& common) :
+ m_common(common)
+{
+ m_lock[false] = m_lock[true] = 0;
+}
+
+MetaData::MetaData(SimulatedBlock* block) :
+ m_common(*block->getMetaDataCommon())
+{
+ m_lock[false] = m_lock[true] = 0;
+}
+
+MetaData::~MetaData()
+{
+ for (int i = false; i <= true; i++) {
+ assert(m_common.m_lock[i] >= m_lock[i]);
+ m_common.m_lock[i] -= m_lock[i];
+ m_lock[i] = 0;
+ }
+}
+
+int
+MetaData::lock(bool exclusive)
+{
+ if (m_common.m_lock[true] > m_lock[true]) {
+ // locked exclusively by another instance
+ return MetaData::Locked;
+ }
+ m_lock[exclusive]++;
+ m_common.m_lock[exclusive]++;
+ return 0;
+}
+
+int
+MetaData::unlock(bool exclusive)
+{
+ if (m_lock[exclusive] == 0) {
+ return MetaData::NotLocked;
+ }
+ m_lock[exclusive]--;
+ m_common.m_lock[exclusive]--;
+ return 0;
+}
+
+int
+MetaData::getTable(MetaData::Table& table, Uint32 tableId, Uint32 tableVersion)
+{
+ if (m_lock[false] + m_lock[true] == 0) {
+ return MetaData::NotLocked;
+ }
+ return m_common.m_dbdict.getMetaTable(table, tableId, tableVersion);
+}
+
+int
+MetaData::getTable(MetaData::Table& table, const char* tableName)
+{
+ if (m_lock[false] + m_lock[true] == 0) {
+ return MetaData::NotLocked;
+ }
+ return m_common.m_dbdict.getMetaTable(table, tableName);
+}
+
+int
+MetaData::getAttribute(MetaData::Attribute& attribute, const MetaData::Table& table, Uint32 attributeId)
+{
+ if (m_lock[false] + m_lock[true] == 0) {
+ return MetaData::NotLocked;
+ }
+ return m_common.m_dbdict.getMetaAttribute(attribute, table, attributeId);
+}
+
+int
+MetaData::getAttribute(MetaData::Attribute& attribute, const MetaData::Table& table, const char* attributeName)
+{
+ if (m_lock[false] + m_lock[true] == 0) {
+ return MetaData::NotLocked;
+ }
+ return m_common.m_dbdict.getMetaAttribute(attribute, table, attributeName);
+}
diff --git a/storage/ndb/src/kernel/vm/MetaData.hpp b/storage/ndb/src/kernel/vm/MetaData.hpp
new file mode 100644
index 00000000000..1000114a421
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/MetaData.hpp
@@ -0,0 +1,243 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 KERNEL_VM_METADATA_HPP
+#define KERNEL_VM_METADATA_HPP
+
+#include <ndb_types.h>
+#include <ndb_limits.h>
+#include <ErrorReporter.hpp>
+#include <signaldata/DictTabInfo.hpp>
+
+class SimulatedBlock;
+class Dbdict;
+class Dbdih;
+
+/*
+ * Common metadata for all blocks on the node.
+ *
+ * A database node has unique DICT and DIH instances. Parts of their
+ * metadata are described by subclasses of MetaData. Any block can
+ * access the metadata via a MetaData instance.
+ */
+class MetaData {
+public:
+ /*
+ * Methods return < 0 on error.
+ */
+ enum Error {
+ Locked = -1,
+ NotLocked = -2,
+ InvalidArgument = -3,
+ TableNotFound = -4,
+ InvalidTableVersion = -5,
+ AttributeNotFound = -6
+ };
+
+ /*
+ * Common data shared by all metadata instances. Contains DICT and
+ * DIH pointers and counts of shared and exclusive locks.
+ */
+ class Common {
+ public:
+ Common(Dbdict& dbdict, Dbdih& dbdih);
+ private:
+ friend class MetaData;
+ Dbdict& m_dbdict;
+ Dbdih& m_dbdih;
+ unsigned m_lock[2]; // shared: 0 (false), exclusive: 1 (true)
+ };
+
+ /*
+ * Table metadata. A base class of Dbdict::TableRecord. This is
+ * actually fragment metadata but until "alter table" there is no
+ * difference.
+ */
+ class Table {
+ public:
+ /* Table id (array index in DICT and other blocks) */
+ Uint32 tableId;
+
+ /* Table version (incremented when tableId is re-used) */
+ Uint32 tableVersion;
+
+ /* Table name (may not be unique under "alter table") */
+ char tableName[MAX_TAB_NAME_SIZE];
+
+ /* Type of table or index */
+ DictTabInfo::TableType tableType;
+
+ /* Is table or index online (this flag is not used in DICT) */
+ bool online;
+
+ /* Primary table of index otherwise RNIL */
+ Uint32 primaryTableId;
+
+ /* Type of fragmentation (small/medium/large) */
+ DictTabInfo::FragmentType fragmentType;
+
+ /* Global checkpoint identity when table created */
+ Uint32 gciTableCreated;
+
+ /* Number of attibutes in table */
+ Uint16 noOfAttributes;
+
+ /* Number of null attributes in table (should be computed) */
+ Uint16 noOfNullAttr;
+
+ /* Number of primary key attributes (should be computed) */
+ Uint16 noOfPrimkey;
+
+ /* Number of distinct character sets (computed) */
+ Uint16 noOfCharsets;
+
+ /* Length of primary key in words (should be computed) */
+ /* For ordered index this is tree node size in words */
+ Uint16 tupKeyLength;
+
+ /* K value for LH**3 algorithm (only 6 allowed currently) */
+ Uint8 kValue;
+
+ /* Local key length in words (currently 1) */
+ Uint8 localKeyLen;
+
+ /*
+ * Parameter for hash algorithm that specifies the load factor in
+ * percentage of fill level in buckets. A high value means we are
+ * splitting early and that buckets are only lightly used. A high
+ * value means that we have fill the buckets more and get more
+ * likelihood of overflow buckets.
+ */
+ Uint8 maxLoadFactor;
+
+ /*
+ * Used when shrinking to decide when to merge buckets. Hysteresis
+ * is thus possible. Should be smaller but not much smaller than
+ * maxLoadFactor
+ */
+ Uint8 minLoadFactor;
+
+ /* Is the table logged (i.e. data survives system restart) */
+ bool storedTable;
+
+ /* Convenience routines */
+ bool isTable() const;
+ bool isIndex() const;
+ bool isUniqueIndex() const;
+ bool isNonUniqueIndex() const;
+ bool isHashIndex() const;
+ bool isOrderedIndex() const;
+ };
+
+ /*
+ * Attribute metadata. A base class of Dbdict::AttributeRecord.
+ */
+ class Attribute {
+ public:
+ /* Attribute id within table (counted from 0) */
+ Uint16 attributeId;
+
+ /* Attribute number within tuple key (counted from 1) */
+ Uint16 tupleKey;
+
+ /* Attribute name (unique within table) */
+ char attributeName[MAX_ATTR_NAME_SIZE];
+
+ /* Attribute description (old-style packed descriptor) */
+ Uint32 attributeDescriptor;
+
+ /* Extended attributes */
+ Uint32 extPrecision;
+ Uint32 extScale;
+ Uint32 extLength;
+
+ /* Autoincrement flag, only for ODBC/SQL */
+ bool autoIncrement;
+
+ /* Default value as null-terminated string, only for ODBC/SQL */
+ char defaultValue[MAX_ATTR_DEFAULT_VALUE_SIZE];
+ };
+
+ /*
+ * Metadata is accessed via a MetaData instance. The constructor
+ * needs a reference to MetaData::Common which can be obtained via
+ * the block. The destructor releases any leftover locks.
+ */
+ MetaData(Common& common);
+ MetaData(SimulatedBlock* block);
+ ~MetaData();
+
+ /*
+ * Access methods. Locking can be shared (read) or exclusive (write).
+ * Locking can be recursive (a count is kept). Example (in a block):
+ *
+ * MetaData md(this);
+ * MetaData::Table table;
+ * ret = md.lock(false);
+ * ret = md.getTable(table, "SYSTAB_0");
+ * ret = md.unlock();
+ */
+ int lock(bool exclusive);
+ int unlock(bool exclusive);
+ int getTable(MetaData::Table& table, Uint32 tableId, Uint32 tableVersion);
+ int getTable(MetaData::Table& table, const char* tableName);
+ int getAttribute(MetaData::Attribute& attribute, const MetaData::Table& table, Uint32 attributeId);
+ int getAttribute(MetaData::Attribute& attribute, const MetaData::Table& table, const char* attributeName);
+
+private:
+ Common& m_common;
+ unsigned m_lock[2];
+};
+
+// MetaData::Table
+
+inline bool
+MetaData::Table::isTable() const
+{
+ return DictTabInfo::isTable(tableType);
+}
+
+inline bool
+MetaData::Table::isIndex() const
+{
+ return DictTabInfo::isIndex(tableType);
+}
+
+inline bool
+MetaData::Table::isUniqueIndex() const
+{
+ return DictTabInfo::isUniqueIndex(tableType);
+}
+
+inline bool
+MetaData::Table::isNonUniqueIndex() const
+{
+ return DictTabInfo::isNonUniqueIndex(tableType);
+}
+
+inline bool
+MetaData::Table::isHashIndex() const
+{
+ return DictTabInfo::isHashIndex(tableType);
+}
+
+inline bool
+MetaData::Table::isOrderedIndex() const
+{
+ return DictTabInfo::isOrderedIndex(tableType);
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/Mutex.cpp b/storage/ndb/src/kernel/vm/Mutex.cpp
new file mode 100644
index 00000000000..aab9e74312b
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Mutex.cpp
@@ -0,0 +1,287 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "SimulatedBlock.hpp"
+#include "Mutex.hpp"
+#include <signaldata/UtilLock.hpp>
+
+SimulatedBlock::MutexManager::MutexManager(class SimulatedBlock & block)
+ : m_block(block),
+ m_activeMutexes(m_mutexPool) {
+}
+
+bool
+SimulatedBlock::MutexManager::setSize(Uint32 maxNoOfActiveMutexes){
+ return m_mutexPool.setSize(maxNoOfActiveMutexes);
+}
+
+Uint32
+SimulatedBlock::MutexManager::getSize() const {
+ return m_mutexPool.getSize();
+}
+
+bool
+SimulatedBlock::MutexManager::seize(ActiveMutexPtr& ptr){
+ return m_activeMutexes.seize(ptr);
+}
+
+void
+SimulatedBlock::MutexManager::release(Uint32 activeMutexPtrI){
+ m_activeMutexes.release(activeMutexPtrI);
+}
+
+void
+SimulatedBlock::MutexManager::getPtr(ActiveMutexPtr& ptr){
+ m_activeMutexes.getPtr(ptr);
+}
+
+BlockReference
+SimulatedBlock::MutexManager::reference() const {
+ return m_block.reference();
+}
+
+void
+SimulatedBlock::MutexManager::progError(int line,
+ int err_code,
+ const char* extra)
+{
+ m_block.progError(line, err_code, extra);
+}
+
+void
+SimulatedBlock::MutexManager::create(Signal* signal, ActiveMutexPtr& ptr){
+
+ UtilCreateLockReq * req = (UtilCreateLockReq*)signal->getDataPtrSend();
+ req->senderData = ptr.i;
+ req->senderRef = m_block.reference();
+ req->lockId = ptr.p->m_mutexId;
+ req->lockType = UtilCreateLockReq::Mutex;
+
+ m_block.sendSignal(DBUTIL_REF,
+ GSN_UTIL_CREATE_LOCK_REQ,
+ signal,
+ UtilCreateLockReq::SignalLength,
+ JBB);
+
+ ptr.p->m_gsn = GSN_UTIL_CREATE_LOCK_REQ;
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_CREATE_LOCK_REF(Signal* signal){
+
+ UtilCreateLockRef * ref = (UtilCreateLockRef*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, ref->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_CREATE_LOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == ref->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, ref->errorCode);
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_CREATE_LOCK_CONF(Signal* signal){
+
+ UtilCreateLockConf * conf = (UtilCreateLockConf*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, conf->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_CREATE_LOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == conf->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, 0);
+}
+
+
+void
+SimulatedBlock::MutexManager::destroy(Signal* signal, ActiveMutexPtr& ptr){
+
+ UtilDestroyLockReq * req = (UtilDestroyLockReq*)signal->getDataPtrSend();
+ req->senderData = ptr.i;
+ req->senderRef = m_block.reference();
+ req->lockId = ptr.p->m_mutexId;
+ req->lockKey = ptr.p->m_mutexKey;
+
+ m_block.sendSignal(DBUTIL_REF,
+ GSN_UTIL_DESTROY_LOCK_REQ,
+ signal,
+ UtilDestroyLockReq::SignalLength,
+ JBB);
+
+ ptr.p->m_gsn = GSN_UTIL_DESTROY_LOCK_REQ;
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_DESTORY_LOCK_REF(Signal* signal){
+ UtilDestroyLockRef * ref = (UtilDestroyLockRef*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, ref->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_DESTROY_LOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == ref->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, ref->errorCode);
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_DESTORY_LOCK_CONF(Signal* signal){
+ UtilDestroyLockConf * conf = (UtilDestroyLockConf*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, conf->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_DESTROY_LOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == conf->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, 0);
+}
+
+
+void
+SimulatedBlock::MutexManager::lock(Signal* signal, ActiveMutexPtr& ptr){
+
+ UtilLockReq * req = (UtilLockReq*)signal->getDataPtrSend();
+ req->senderData = ptr.i;
+ req->senderRef = m_block.reference();
+ req->lockId = ptr.p->m_mutexId;
+ req->requestInfo = 0;
+
+ m_block.sendSignal(DBUTIL_REF,
+ GSN_UTIL_LOCK_REQ,
+ signal,
+ UtilLockReq::SignalLength,
+ JBB);
+
+ ptr.p->m_gsn = GSN_UTIL_LOCK_REQ;
+}
+
+void
+SimulatedBlock::MutexManager::trylock(Signal* signal, ActiveMutexPtr& ptr){
+
+ UtilLockReq * req = (UtilLockReq*)signal->getDataPtrSend();
+ req->senderData = ptr.i;
+ req->senderRef = m_block.reference();
+ req->lockId = ptr.p->m_mutexId;
+ req->requestInfo = UtilLockReq::TryLock;
+
+ m_block.sendSignal(DBUTIL_REF,
+ GSN_UTIL_LOCK_REQ,
+ signal,
+ UtilLockReq::SignalLength,
+ JBB);
+
+ ptr.p->m_gsn = GSN_UTIL_LOCK_REQ;
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_LOCK_REF(Signal* signal){
+ UtilLockRef * ref = (UtilLockRef*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, ref->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_LOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == ref->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, ref->errorCode);
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_LOCK_CONF(Signal* signal){
+ UtilLockConf * conf = (UtilLockConf*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, conf->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_LOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == conf->lockId);
+
+ ptr.p->m_mutexKey = conf->lockKey;
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, 0);
+}
+
+void
+SimulatedBlock::MutexManager::unlock(Signal* signal, ActiveMutexPtr& ptr){
+ UtilUnlockReq * req = (UtilUnlockReq*)signal->getDataPtrSend();
+ req->senderData = ptr.i;
+ req->senderRef = m_block.reference();
+ req->lockId = ptr.p->m_mutexId;
+ req->lockKey = ptr.p->m_mutexKey;
+
+ m_block.sendSignal(DBUTIL_REF,
+ GSN_UTIL_UNLOCK_REQ,
+ signal,
+ UtilUnlockReq::SignalLength,
+ JBB);
+
+ ptr.p->m_gsn = GSN_UTIL_UNLOCK_REQ;
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_UNLOCK_REF(Signal* signal){
+ UtilUnlockRef * ref = (UtilUnlockRef*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, ref->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_UNLOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == ref->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, ref->errorCode);
+}
+
+void
+SimulatedBlock::MutexManager::execUTIL_UNLOCK_CONF(Signal* signal){
+ UtilUnlockConf * conf = (UtilUnlockConf*)signal->getDataPtr();
+ ActiveMutexPtr ptr;
+ m_activeMutexes.getPtr(ptr, conf->senderData);
+ ndbrequire(ptr.p->m_gsn == GSN_UTIL_UNLOCK_REQ);
+ ndbrequire(ptr.p->m_mutexId == conf->lockId);
+
+ ptr.p->m_gsn = 0;
+ m_block.execute(signal, ptr.p->m_callback, 0);
+}
+
+void
+Mutex::release(SimulatedBlock::MutexManager& mgr,
+ Uint32 activePtrI, Uint32 mutexId){
+ SimulatedBlock::MutexManager::ActiveMutexPtr ptr;
+ ptr.i = activePtrI;
+ mgr.getPtr(ptr);
+ if(ptr.p->m_gsn == 0 && ptr.p->m_mutexId == mutexId){
+ mgr.release(activePtrI);
+ return;
+ }
+
+ if(ptr.p->m_mutexId != mutexId)
+ ErrorReporter::handleAssert("MutexHandle::release invalid handle",
+ __FILE__, __LINE__);
+ ErrorReporter::handleAssert("MutexHandle::release of mutex inuse",
+ __FILE__, __LINE__);
+}
+
+void
+Mutex::unlock(){
+ if(!m_ptr.isNull()){
+ m_mgr.getPtr(m_ptr);
+ if(m_ptr.p->m_mutexId == m_mutexId){
+ SimulatedBlock::Callback c =
+ { &SimulatedBlock::ignoreMutexUnlockCallback, m_ptr.i };
+ m_ptr.p->m_callback = c;
+ m_mgr.unlock(m_signal, m_ptr);
+ m_ptr.setNull(); // Remove reference
+ }
+ }
+}
+
diff --git a/storage/ndb/src/kernel/vm/Mutex.hpp b/storage/ndb/src/kernel/vm/Mutex.hpp
new file mode 100644
index 00000000000..7a16046188c
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Mutex.hpp
@@ -0,0 +1,267 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 BLOCK_MUTEX_HPP
+#define BLOCK_MUTEX_HPP
+
+#include "Callback.hpp"
+#include "SimulatedBlock.hpp"
+
+class Mutex;
+
+/**
+ * MutexHandle - A "reference" to a mutex
+ * - Should be used together with Mutex
+ */
+class MutexHandle {
+ friend class Mutex;
+public:
+ MutexHandle(Uint32 id);
+
+ bool isNull() const;
+ void release(SimulatedBlock::MutexManager & mgr);
+
+private:
+ const Uint32 m_mutexId;
+ Uint32 m_activeMutexPtrI;
+};
+
+/**
+ * MutexHandle2 - A template-based "reference" to a mutex
+ */
+template<Uint32 MutexId>
+class MutexHandle2 {
+ friend class Mutex;
+public:
+ MutexHandle2();
+
+ bool isNull() const;
+ void release(SimulatedBlock::MutexManager & mgr);
+
+private:
+ Uint32 m_activeMutexPtrI;
+};
+
+/**
+ * A mutex - Used together with a MutexHandle to be put on the stack
+ */
+class Mutex {
+public:
+ Mutex(Signal*, SimulatedBlock::MutexManager & mgr, MutexHandle &);
+
+ template<Uint32 MutexId>
+ Mutex(Signal*, SimulatedBlock::MutexManager & mgr, MutexHandle2<MutexId> &);
+
+ ~Mutex();
+
+ void release();
+ bool isNull() const ;
+
+ bool lock(SimulatedBlock::Callback & callback);
+ bool trylock(SimulatedBlock::Callback & callback);
+ void unlock(SimulatedBlock::Callback & callback);
+ void unlock(); // Ignore callback
+
+ bool create(SimulatedBlock::Callback & callback);
+ bool destroy(SimulatedBlock::Callback & callback);
+
+private:
+ Signal* m_signal;
+ SimulatedBlock::MutexManager & m_mgr;
+ const Uint32 m_mutexId;
+ Uint32 & m_srcPtrI;
+ SimulatedBlock::MutexManager::ActiveMutexPtr m_ptr;
+
+public:
+ static void release(SimulatedBlock::MutexManager&,
+ Uint32 activePtrI, Uint32 mutexId);
+};
+
+inline
+MutexHandle::MutexHandle(Uint32 id) : m_mutexId(id) {
+ m_activeMutexPtrI = RNIL;
+}
+
+inline
+bool
+MutexHandle::isNull() const {
+ return m_activeMutexPtrI == RNIL;
+}
+
+inline
+void
+MutexHandle::release(SimulatedBlock::MutexManager & mgr){
+ if(!isNull()){
+ Mutex::release(mgr, m_activeMutexPtrI, m_mutexId);
+ m_activeMutexPtrI = RNIL;
+ }
+}
+
+template<Uint32 MutexId>
+inline
+MutexHandle2<MutexId>::MutexHandle2() {
+ m_activeMutexPtrI = RNIL;
+}
+
+template<Uint32 MutexId>
+inline
+bool
+MutexHandle2<MutexId>::isNull() const {
+ return m_activeMutexPtrI == RNIL;
+}
+
+
+template<Uint32 MutexId>
+inline
+void
+MutexHandle2<MutexId>::release(SimulatedBlock::MutexManager & mgr){
+ if(!isNull()){
+ Mutex::release(mgr, m_activeMutexPtrI, MutexId);
+ m_activeMutexPtrI = RNIL;
+ }
+}
+
+
+inline
+Mutex::Mutex(Signal* signal, SimulatedBlock::MutexManager & mgr,
+ MutexHandle & mh)
+ : m_signal(signal),
+ m_mgr(mgr),
+ m_mutexId(mh.m_mutexId),
+ m_srcPtrI(mh.m_activeMutexPtrI){
+
+ m_ptr.i = m_srcPtrI;
+
+}
+
+template<Uint32 MutexId>
+inline
+Mutex::Mutex(Signal* signal, SimulatedBlock::MutexManager & mgr,
+ MutexHandle2<MutexId> & mh)
+ : m_signal(signal),
+ m_mgr(mgr),
+ m_mutexId(MutexId),
+ m_srcPtrI(mh.m_activeMutexPtrI){
+
+ m_ptr.i = m_srcPtrI;
+
+}
+
+inline
+Mutex::~Mutex(){
+ m_srcPtrI = m_ptr.i;
+}
+
+inline
+void
+Mutex::release(){
+ if(!m_ptr.isNull()){
+ Mutex::release(m_mgr, m_ptr.i, m_mutexId);
+ m_ptr.setNull();
+ }
+}
+
+inline
+bool
+Mutex::isNull() const {
+ return m_ptr.isNull();
+}
+
+inline
+bool
+Mutex::lock(SimulatedBlock::Callback & callback){
+ if(m_ptr.isNull()){
+ if(m_mgr.seize(m_ptr)){
+ m_ptr.p->m_mutexId = m_mutexId;
+ m_ptr.p->m_callback = callback;
+ m_mgr.lock(m_signal, m_ptr);
+ return true;
+ }
+ return false;
+ }
+ ErrorReporter::handleAssert("Mutex::lock mutex alreay inuse",
+ __FILE__, __LINE__);
+ return false;
+}
+
+inline
+bool
+Mutex::trylock(SimulatedBlock::Callback & callback){
+ if(m_ptr.isNull()){
+ if(m_mgr.seize(m_ptr)){
+ m_ptr.p->m_mutexId = m_mutexId;
+ m_ptr.p->m_callback = callback;
+ m_mgr.lock(m_signal, m_ptr);
+ return true;
+ }
+ return false;
+ }
+ ErrorReporter::handleAssert("Mutex::trylock mutex alreay inuse",
+ __FILE__, __LINE__);
+ return false;
+}
+
+inline
+void
+Mutex::unlock(SimulatedBlock::Callback & callback){
+ if(!m_ptr.isNull()){
+ m_mgr.getPtr(m_ptr);
+ if(m_ptr.p->m_mutexId == m_mutexId){
+ m_ptr.p->m_callback = callback;
+ m_mgr.unlock(m_signal, m_ptr);
+ return;
+ }
+ }
+ ErrorReporter::handleAssert("Mutex::unlock invalid mutex",
+ __FILE__, __LINE__);
+}
+
+inline
+bool
+Mutex::create(SimulatedBlock::Callback & callback){
+ if(m_ptr.isNull()){
+ if(m_mgr.seize(m_ptr)){
+ m_ptr.p->m_mutexId = m_mutexId;
+ m_ptr.p->m_callback = callback;
+ m_mgr.create(m_signal, m_ptr);
+ return true;
+ }
+ return false;
+ }
+ ErrorReporter::handleAssert("Mutex::create mutex alreay inuse",
+ __FILE__, __LINE__);
+ return false;
+}
+
+inline
+bool
+Mutex::destroy(SimulatedBlock::Callback & callback){
+ if(m_ptr.isNull()){
+ if(m_mgr.seize(m_ptr)){
+ m_ptr.p->m_mutexId = m_mutexId;
+ m_ptr.p->m_callback = callback;
+ m_mgr.destroy(m_signal, m_ptr);
+ return true;
+ }
+ return false;
+ }
+ ErrorReporter::handleAssert("Mutex::destroy mutex alreay inuse",
+ __FILE__, __LINE__);
+ return false;
+}
+
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/Prio.hpp b/storage/ndb/src/kernel/vm/Prio.hpp
new file mode 100644
index 00000000000..4c9c22b0afe
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/Prio.hpp
@@ -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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 PRIO_H
+#define PRIO_H
+
+enum JobBufferLevel {
+ JBA = 0,
+ JBB = 1,
+ JBC = 2,
+ JBD = 3, LEVEL_IDLE = 3,
+ JB_LEVELS,
+ ILLEGAL_JB_LEVEL
+};
+
+typedef JobBufferLevel Priority;
+
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/RequestTracker.hpp b/storage/ndb/src/kernel/vm/RequestTracker.hpp
new file mode 100644
index 00000000000..5fd1ae7255a
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/RequestTracker.hpp
@@ -0,0 +1,58 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 __REQUEST_TRACKER_HPP
+#define __REQUEST_TRACKER_HPP
+
+#include "SafeCounter.hpp"
+
+class RequestTracker {
+public:
+ RequestTracker(){ init(); }
+
+ void init() { m_confs.clear(); m_nRefs = 0; }
+
+ template<typename SignalClass>
+ void init(SafeCounterManager& mgr,
+ NodeReceiverGroup rg, Uint16 GSN, Uint32 senderData)
+ {
+ init();
+ SafeCounter tmp(mgr, m_sc);
+ tmp.init<SignalClass>(rg, GSN, senderData);
+ }
+
+ bool ignoreRef(SafeCounterManager& mgr, Uint32 nodeId)
+ { return m_sc.clearWaitingFor(mgr, nodeId); }
+
+ bool reportRef(SafeCounterManager& mgr, Uint32 nodeId)
+ { m_nRefs++; return m_sc.clearWaitingFor(mgr, nodeId); }
+
+ bool reportConf(SafeCounterManager& mgr, Uint32 nodeId)
+ { m_confs.set(nodeId); return m_sc.clearWaitingFor(mgr, nodeId); }
+
+ bool hasRef() { return m_nRefs != 0; }
+
+ bool hasConf() { return !m_confs.isclear(); }
+
+ bool done() { return m_sc.done(); }
+
+private:
+ SafeCounterHandle m_sc;
+ NodeBitmask m_confs;
+ Uint8 m_nRefs;
+};
+
+#endif // __REQUEST_TRACKER_HPP
diff --git a/storage/ndb/src/kernel/vm/SLList.hpp b/storage/ndb/src/kernel/vm/SLList.hpp
new file mode 100644
index 00000000000..5fde41aa3e0
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SLList.hpp
@@ -0,0 +1,295 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SLLIST_HPP
+#define SLLIST_HPP
+
+#include "ArrayPool.hpp"
+#include <NdbOut.hpp>
+
+/**
+ * Template class used for implementing an
+ * list of object retreived from a pool
+ */
+template <class T>
+class SLList {
+public:
+ /**
+ * List head
+ */
+ struct Head {
+ Head();
+ Uint32 firstItem;
+ };
+
+ SLList(ArrayPool<T> & thePool);
+
+ /**
+ * Allocate an object from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seize(Ptr<T> &);
+
+ /**
+ * Allocate object <b>i</b> from pool - update Ptr
+ *
+ * Return i
+ */
+ bool seizeId(Ptr<T> &, Uint32 i);
+
+ /**
+ * Allocate <b>n</b>objects from pool
+ *
+ * Return i value of first object allocated or RNIL if fails
+ */
+ bool seizeN(Ptr<T> &, Uint32 n);
+
+ /**
+ * Return all objects to the pool
+ */
+ void release();
+
+ /**
+ * Update i & p value according to <b>i</b>
+ */
+ void getPtr(Ptr<T> &, Uint32 i) const;
+
+ /**
+ * Update p value for ptr according to i value
+ */
+ void getPtr(Ptr<T> &) const ;
+
+ /**
+ * Get pointer for i value
+ */
+ T * getPtr(Uint32 i) const ;
+
+ /**
+ * Update ptr to first element in list
+ *
+ * Return i
+ */
+ bool first(Ptr<T> &) const ;
+
+ /**
+ * Get next element
+ *
+ * NOTE ptr must be both p & i
+ */
+ bool next(Ptr<T> &) const ;
+
+ /**
+ * Check if next exists
+ *
+ * NOTE ptr must be both p & i
+ */
+ bool hasNext(const Ptr<T> &) const;
+
+ Uint32 noOfElements() const {
+ Uint32 c = 0;
+ Uint32 i = head.firstItem;
+ while(i != RNIL){
+ c++;
+ const T * t = thePool.getPtr(i);
+ i = t->nextList;
+ }
+ return c;
+ }
+
+ /**
+ * Print
+ * (Run operator NdbOut<< on every element)
+ */
+ void print(NdbOut & out) {
+ out << "firstItem = " << head.firstItem << endl;
+ Uint32 i = head.firstItem;
+ while(i != RNIL){
+ T * t = thePool.getPtr(i);
+ t->print(out); out << " ";
+ i = t->next;
+ }
+ }
+
+ inline bool empty() const { return head.firstItem == RNIL;}
+
+protected:
+ Head head;
+ ArrayPool<T> & thePool;
+};
+
+template<class T>
+class LocalSLList : public SLList<T> {
+public:
+ LocalSLList(ArrayPool<T> & thePool, typename SLList<T>::Head & _src)
+ : SLList<T>(thePool), src(_src)
+ {
+ this->head = src;
+ }
+
+ ~LocalSLList(){
+ src = this->head;
+ }
+private:
+ typename SLList<T>::Head & src;
+};
+
+template <class T>
+inline
+SLList<T>::SLList(ArrayPool<T> & _pool):
+ thePool(_pool){
+}
+
+template<class T>
+inline
+SLList<T>::Head::Head(){
+ firstItem = RNIL;
+}
+
+template <class T>
+inline
+bool
+SLList<T>::seize(Ptr<T> & p){
+ thePool.seize(p);
+ T * t = p.p;
+ Uint32 ff = head.firstItem;
+ if(p.i != RNIL){
+ t->nextList = ff;
+ head.firstItem = p.i;
+ return true;
+ }
+ return false;
+}
+
+template <class T>
+inline
+bool
+SLList<T>::seizeId(Ptr<T> & p, Uint32 ir){
+ thePool.seizeId(p, ir);
+ T * t = p.p;
+ Uint32 ff = head.firstItem;
+ if(p.i != RNIL){
+ t->nextList = ff;
+ head.firstItem = p.i;
+ return true;
+ }
+ return false;
+}
+
+template <class T>
+inline
+bool
+SLList<T>::seizeN(Ptr<T> & p, Uint32 n){
+ for(Uint32 i = 0; i < n; i++){
+ if(seize(p) == RNIL){
+ /**
+ * Failure
+ */
+ for(; i > 0; i--){
+ const Uint32 tmp = head.firstItem;
+ const T * t = thePool.getPtr(tmp);
+ head.firstItem = t->nextList;
+ thePool.release(tmp);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Success
+ */
+ p.i = head.firstItem;
+ p.p = thePool.getPtr(head.firstItem);
+
+ return true;
+}
+
+
+template <class T>
+inline
+void
+SLList<T>::release(){
+ while(head.firstItem != RNIL){
+ const T * t = thePool.getPtr(head.firstItem);
+ const Uint32 i = head.firstItem;
+ head.firstItem = t->nextList;
+ thePool.release(i);
+ }
+}
+
+template <class T>
+inline
+void
+SLList<T>::getPtr(Ptr<T> & p, Uint32 i) const {
+ p.i = i;
+ p.p = thePool.getPtr(i);
+}
+
+template <class T>
+inline
+void
+SLList<T>::getPtr(Ptr<T> & p) const {
+ thePool.getPtr(p);
+}
+
+template <class T>
+inline
+T *
+SLList<T>::getPtr(Uint32 i) const {
+ return thePool.getPtr(i);
+}
+
+/**
+ * Update ptr to first element in list
+ *
+ * Return i
+ */
+template <class T>
+inline
+bool
+SLList<T>::first(Ptr<T> & p) const {
+ Uint32 i = head.firstItem;
+ p.i = i;
+ if(i != RNIL){
+ p.p = thePool.getPtr(i);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+SLList<T>::next(Ptr<T> & p) const {
+ Uint32 i = p.p->nextList;
+ p.i = i;
+ if(i != RNIL){
+ p.p = thePool.getPtr(i);
+ return true;
+ }
+ p.p = NULL;
+ return false;
+}
+
+template <class T>
+inline
+bool
+SLList<T>::hasNext(const Ptr<T> & p) const {
+ return p.p->nextList != RNIL;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/SafeCounter.cpp b/storage/ndb/src/kernel/vm/SafeCounter.cpp
new file mode 100644
index 00000000000..b09ad08b026
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SafeCounter.cpp
@@ -0,0 +1,159 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "SimulatedBlock.hpp"
+#include "SafeCounter.hpp"
+#include <signaldata/NodeFailRep.hpp>
+
+SafeCounterManager::SafeCounterManager(class SimulatedBlock & block)
+ : m_block(block),
+ m_activeCounters(m_counterPool)
+{}
+
+bool
+SafeCounterManager::setSize(Uint32 maxNoOfActiveMutexes) {
+ return m_counterPool.setSize(maxNoOfActiveMutexes);
+}
+
+Uint32
+SafeCounterManager::getSize() const {
+ return m_counterPool.getSize();
+}
+
+bool
+SafeCounterManager::seize(ActiveCounterPtr& ptr){
+ return m_activeCounters.seize(ptr);
+}
+
+void
+SafeCounterManager::release(ActiveCounterPtr& ptr){
+ m_activeCounters.release(ptr);
+}
+
+void
+SafeCounterManager::getPtr(ActiveCounterPtr& ptr, Uint32 ptrI){
+ m_activeCounters.getPtr(ptr, ptrI);
+}
+
+
+void
+SafeCounterManager::printNODE_FAILREP(){
+ ActiveCounterPtr ptr;
+
+ NodeBitmask nodes;
+ nodes.clear();
+ // nodes.bitORC(nodes);
+
+ for(m_activeCounters.first(ptr); !ptr.isNull(); m_activeCounters.next(ptr)){
+ ActiveCounter::SignalDesc desc = ptr.p->m_signalDesc;
+ ndbout_c("theData[desc.m_senderDataOffset=%u] = %u",
+ desc.m_senderDataOffset, ptr.p->m_senderData);
+ ndbout_c("theData[desc.m_errorCodeOffset=%u] = %u",
+ desc.m_errorCodeOffset, desc.m_nodeFailErrorCode);
+ Uint32 len = MAX(MAX(desc.m_senderDataOffset, desc.m_errorCodeOffset),
+ desc.m_senderRefOffset);
+
+ NodeBitmask overlapping = ptr.p->m_nodes;
+ Uint32 i = 0;
+ while((i = overlapping.find(i)) != NodeBitmask::NotFound){
+ ndbout_c(" theData[desc.m_senderRefOffset=%u] = %x",
+ desc.m_senderRefOffset, numberToRef(desc.m_block, i));
+ ndbout_c(" sendSignal(%x,%u,signal,%u,JBB",
+ m_block.reference(), desc.m_gsn, len+1);
+ i++;
+ }
+ }
+}
+
+void
+SafeCounterManager::execNODE_FAILREP(Signal* signal){
+ Uint32 * theData = signal->getDataPtrSend();
+ ActiveCounterPtr ptr;
+ NodeBitmask nodes;
+ nodes.assign(NodeBitmask::Size,
+ ((const NodeFailRep*)signal->getDataPtr())->theNodes);
+
+ for(m_activeCounters.first(ptr); !ptr.isNull(); m_activeCounters.next(ptr)){
+ if(nodes.overlaps(ptr.p->m_nodes)){
+ ActiveCounter::SignalDesc desc = ptr.p->m_signalDesc;
+ theData[desc.m_senderDataOffset] = ptr.p->m_senderData;
+ theData[desc.m_errorCodeOffset] = desc.m_nodeFailErrorCode;
+ Uint32 len = MAX(MAX(desc.m_senderDataOffset, desc.m_errorCodeOffset),
+ desc.m_senderRefOffset);
+
+ NodeBitmask overlapping = ptr.p->m_nodes;
+ overlapping.bitAND(nodes);
+ Uint32 i = 0;
+ while((i = overlapping.find(i)) != NodeBitmask::NotFound){
+ theData[desc.m_senderRefOffset] = numberToRef(desc.m_block, i);
+ m_block.sendSignal(m_block.reference(), desc.m_gsn, signal, len+1,JBB);
+ i++;
+ }
+ }
+ }
+}
+
+BlockReference
+SafeCounterManager::reference() const {
+ return m_block.reference();
+}
+
+void
+SafeCounterManager::progError(int line, int err_code, const char* extra){
+ m_block.progError(line, err_code, extra);
+}
+
+bool
+SafeCounterHandle::clearWaitingFor(SafeCounterManager& mgr, Uint32 nodeId)
+{
+ SafeCounterManager::ActiveCounterPtr ptr;
+ mgr.getPtr(ptr, m_activeCounterPtrI);
+ ptr.p->m_nodes.clear(nodeId);
+
+ if (ptr.p->m_nodes.isclear()){
+ mgr.release(ptr);
+ m_activeCounterPtrI = RNIL;
+ return true;
+ }
+ return false;
+}
+
+SafeCounter::~SafeCounter(){
+ bool clear = m_count == 0;
+ bool isnull = m_ptr.i == RNIL;
+
+ m_activeCounterPtrI = m_ptr.i;
+
+ if(clear && isnull)
+ return;
+
+ if(clear && !isnull){
+ m_mgr.release(m_ptr);
+ m_activeCounterPtrI = RNIL;
+ return;
+ }
+
+ /**
+ * !clear && !isnull
+ */
+ if(!isnull){
+ m_ptr.p->m_nodes = m_nodes;
+ return;
+ }
+
+ ErrorReporter::handleAssert("~SafeCounter:: wo/ init", __FILE__, __LINE__);
+}
diff --git a/storage/ndb/src/kernel/vm/SafeCounter.hpp b/storage/ndb/src/kernel/vm/SafeCounter.hpp
new file mode 100644
index 00000000000..1f3cc15c2d6
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SafeCounter.hpp
@@ -0,0 +1,301 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 __SAFE_COUNTER_HPP
+#define __SAFE_COUNTER_HPP
+
+/*************************************************************
+ *
+ * SafeCounter "automates" three way to node-fais safe protocols
+ * for "slave" failures. This is done by registing "fake" signals
+ * to be sent in case of nodefailure.
+ *
+ * init<SignalClass>(..., GSN, senderData);
+ *
+ * It is implemented so that one can replace SignalCounter with
+ * SafeCounter (SignalCounter should probably go away with time)
+ * methods:
+ * clearWaitingFor(nodeId);
+ * done();
+ * etc.
+ *
+ * If included in a new block method
+ * SafeCounterManager::execNODE_FAILREP must included in
+ * <block>::execNODE_FAILREP
+ *
+ * the SignalClass must have senderRef, senderData and errorCode
+ * and also ErrorCode::NF_FakeErrorREF, implemented
+ *
+ * SafeCounter consists of 3 parts:
+ * SafeCounterManager which keeps track of active "counters"
+ * SafeCounterHandle to store "i-value" in your "op record"
+ * SafeCounter as a temporary variable only to use on the stack
+ * for operation
+ *
+ */
+
+#include <NodeBitmask.hpp>
+#include "DLList.hpp"
+#include "VMSignal.hpp"
+
+class SimulatedBlock;
+
+/**
+ *
+ */
+class SafeCounterManager {
+ friend class SafeCounter;
+ friend class SafeCounterHandle;
+ friend class SimulatedBlock;
+public:
+ SafeCounterManager(class SimulatedBlock &);
+
+ bool setSize(Uint32 maxNoOfActiveMutexes);
+ Uint32 getSize() const ;
+
+ void execNODE_FAILREP(Signal*);
+ void printNODE_FAILREP();
+
+private:
+ struct ActiveCounter { /** sizeof = 7words = 28bytes */
+ public:
+ Uint32 m_senderData;
+ NodeBitmask m_nodes;
+ struct SignalDesc {
+ public:
+ Uint16 m_gsn;
+ Uint16 m_block;
+ Uint8 m_senderRefOffset;
+ Uint8 m_senderDataOffset;
+ Uint8 m_errorCodeOffset;
+ Uint8 m_nodeFailErrorCode;
+ } m_signalDesc;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+
+ typedef Ptr<ActiveCounter> ActiveCounterPtr;
+
+ bool seize(ActiveCounterPtr& ptr);
+ void release(ActiveCounterPtr& ptr);
+ void getPtr(ActiveCounterPtr& ptr, Uint32 ptrI);
+
+ SimulatedBlock & m_block;
+ ArrayPool<ActiveCounter> m_counterPool;
+ DLList<ActiveCounter> m_activeCounters;
+
+ BlockReference reference() const;
+ void progError(int line, int err_code, const char* extra = 0);
+};
+
+
+class SafeCounterHandle {
+ friend class SafeCounter;
+public:
+ SafeCounterHandle();
+
+ /**
+ * Return if done (no nodes set in bitmask)
+ */
+ bool clearWaitingFor(SafeCounterManager& mgr, Uint32 nodeId);
+
+ bool done() const;
+
+private:
+ Uint32 m_activeCounterPtrI;
+};
+
+class SafeCounter {
+ friend class SafeCounterManager;
+public:
+ SafeCounter(SafeCounterManager&, SafeCounterHandle&);
+
+ template<typename SignalClass>
+ bool init(Uint16 block, Uint16 GSN, Uint32 senderData);
+
+ template<typename SignalClass>
+ bool init(NodeReceiverGroup rg, Uint16 GSN, Uint32 senderData);
+
+ template<typename SignalClass>
+ bool init(NodeReceiverGroup rg, Uint32 senderData);
+
+ ~SafeCounter();
+
+ void clearWaitingFor();
+
+ /**
+ * When sending to different node
+ */
+ void setWaitingFor(Uint32 nodeId);
+ bool clearWaitingFor(Uint32 nodeId);
+ bool forceClearWaitingFor(Uint32 nodeId);
+
+ bool isWaitingFor(Uint32 nodeId) const;
+ bool done() const;
+
+ const char * getText() const; /* ? needed for, some portability issues */
+
+ SafeCounter& operator=(const NdbNodeBitmask&);
+ SafeCounter& operator=(const NodeReceiverGroup&);
+private:
+ Uint32 m_count;
+ NodeBitmask m_nodes;
+
+ SafeCounterManager & m_mgr;
+ SafeCounterManager::ActiveCounterPtr m_ptr;
+
+ Uint32 & m_activeCounterPtrI;
+};
+
+inline
+SafeCounterHandle::SafeCounterHandle(){
+ m_activeCounterPtrI = RNIL;
+}
+
+inline
+bool
+SafeCounterHandle::done() const {
+ return m_activeCounterPtrI == RNIL;
+}
+
+inline
+SafeCounter::SafeCounter(SafeCounterManager& mgr, SafeCounterHandle& handle)
+ : m_mgr(mgr),
+ m_activeCounterPtrI(handle.m_activeCounterPtrI)
+{
+ m_ptr.i = handle.m_activeCounterPtrI;
+ if (m_ptr.i == RNIL) {
+ m_nodes.clear();
+ m_count = 0;
+ } else {
+ m_mgr.getPtr(m_ptr, m_ptr.i);
+ m_nodes = m_ptr.p->m_nodes;
+ m_count = m_nodes.count();
+ }
+}
+
+template<typename Ref>
+inline
+bool
+SafeCounter::init(Uint16 block, Uint16 GSN, Uint32 senderData){
+
+ SafeCounterManager::ActiveCounter::SignalDesc signalDesc;
+ signalDesc.m_gsn = GSN;
+ signalDesc.m_block = block;
+ signalDesc.m_errorCodeOffset = offsetof(Ref, errorCode) >> 2;
+ signalDesc.m_senderRefOffset = offsetof(Ref, senderRef) >> 2;
+ signalDesc.m_senderDataOffset = offsetof(Ref, senderData) >> 2;
+ signalDesc.m_nodeFailErrorCode = Ref::NF_FakeErrorREF;
+ assert(((Uint32)Ref::NF_FakeErrorREF) < 256);
+
+ if(m_ptr.i == RNIL){
+ SafeCounterManager::ActiveCounterPtr ptr;
+ if(m_mgr.seize(ptr)){
+ ptr.p->m_senderData = senderData;
+ ptr.p->m_signalDesc = signalDesc;
+ m_ptr = ptr;
+ return true;
+ }
+ return false;
+ }
+
+ if(m_count == 0){
+ m_ptr.p->m_senderData = senderData;
+ m_ptr.p->m_signalDesc = signalDesc;
+ return true;
+ }
+
+ ErrorReporter::handleAssert("SafeCounter::init twice", __FILE__, __LINE__);
+ return false;
+}
+
+template<typename Ref>
+inline
+bool
+SafeCounter::init(NodeReceiverGroup rg, Uint16 GSN, Uint32 senderData){
+
+ bool b = init<Ref>(rg.m_block, GSN, senderData);
+ m_nodes = rg.m_nodes;
+ m_count = m_nodes.count();
+ return b;
+}
+
+template<typename Ref>
+inline
+bool
+SafeCounter::init(NodeReceiverGroup rg, Uint32 senderData){
+
+ bool b = init<Ref>(rg.m_block, Ref::GSN, senderData);
+ m_nodes = rg.m_nodes;
+ m_count = m_nodes.count();
+ return b;
+}
+
+inline
+void
+SafeCounter::setWaitingFor(Uint32 nodeId) {
+ if(!m_nodes.get(nodeId)){
+ m_nodes.set(nodeId);
+ m_count++;
+ return;
+ }
+ ErrorReporter::handleAssert("SafeCounter::set", __FILE__, __LINE__);
+}
+
+inline
+bool
+SafeCounter::isWaitingFor(Uint32 nodeId) const {
+ return m_nodes.get(nodeId);
+}
+
+inline
+bool
+SafeCounter::done() const {
+ return m_count == 0;
+}
+
+inline
+bool
+SafeCounter::clearWaitingFor(Uint32 nodeId) {
+ if(m_count > 0 && m_nodes.get(nodeId)){
+ m_count--;
+ m_nodes.clear(nodeId);
+ return (m_count == 0);
+ }
+ ErrorReporter::handleAssert("SafeCounter::clear", __FILE__, __LINE__);
+ return false;
+}
+
+inline
+void
+SafeCounter::clearWaitingFor(){
+ m_count = 0;
+ m_nodes.clear();
+}
+
+inline
+bool
+SafeCounter::forceClearWaitingFor(Uint32 nodeId){
+ if(isWaitingFor(nodeId)){
+ return clearWaitingFor(nodeId);
+ }
+ return (m_count == 0);
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/SectionReader.cpp b/storage/ndb/src/kernel/vm/SectionReader.cpp
new file mode 100644
index 00000000000..dd474a49e50
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SectionReader.cpp
@@ -0,0 +1,143 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <SectionReader.hpp>
+#include <TransporterDefinitions.hpp>
+#include "LongSignal.hpp"
+
+#if 0
+ Uint32 m_len;
+ class SectionSegmentPool & m_pool;
+ class SectionSegment * m_head;
+ class SectionSegment * m_currentPos;
+#endif
+
+SectionReader::SectionReader
+(struct SegmentedSectionPtr & ptr, class SectionSegmentPool & pool)
+ : m_pool(pool)
+{
+ if(ptr.p == 0){
+ m_pos = 0;
+ m_len = 0;
+ m_head = 0;
+ m_currentSegment = 0;
+ } else {
+ m_pos = 0;
+ m_len = ptr.p->m_sz;
+ m_head = ptr.p;
+ m_currentSegment = ptr.p;
+ }
+}
+
+void
+SectionReader::reset(){
+ m_pos = 0;
+ m_currentSegment = m_head;
+}
+
+bool
+SectionReader::step(Uint32 len){
+ if(m_pos + len >= m_len) {
+ m_pos++;
+ return false;
+ }
+ while(len > SectionSegment::DataLength){
+ m_currentSegment = m_pool.getPtr(m_currentSegment->m_nextSegment);
+
+ len -= SectionSegment::DataLength;
+ m_pos += SectionSegment::DataLength;
+ }
+
+ Uint32 ind = m_pos % SectionSegment::DataLength;
+ while(len > 0){
+ len--;
+ m_pos++;
+
+ ind++;
+ if(ind == SectionSegment::DataLength){
+ ind = 0;
+ m_currentSegment = m_pool.getPtr(m_currentSegment->m_nextSegment);
+ }
+ }
+ return true;
+}
+
+bool
+SectionReader::getWord(Uint32 * dst){
+ if (peekWord(dst)) {
+ step(1);
+ return true;
+ }
+ return false;
+}
+
+bool
+SectionReader::peekWord(Uint32 * dst) const {
+ if(m_pos < m_len){
+ Uint32 ind = m_pos % SectionSegment::DataLength;
+ * dst = m_currentSegment->theData[ind];
+ return true;
+ }
+ return false;
+}
+
+bool
+SectionReader::peekWords(Uint32 * dst, Uint32 len) const {
+ if(m_pos + len > m_len)
+ return false;
+
+ Uint32 ind = (m_pos % SectionSegment::DataLength);
+ Uint32 left = SectionSegment::DataLength - ind;
+ SectionSegment * p = m_currentSegment;
+
+ while(len > left){
+ memcpy(dst, &p->theData[ind], 4 * left);
+ dst += left;
+ len -= left;
+ ind = 0;
+ left = SectionSegment::DataLength;
+ p = m_pool.getPtr(p->m_nextSegment);
+ }
+
+ memcpy(dst, &p->theData[ind], 4 * len);
+ return true;
+}
+
+bool
+SectionReader::getWords(Uint32 * dst, Uint32 len){
+ if(m_pos + len > m_len)
+ return false;
+
+ Uint32 ind = (m_pos % SectionSegment::DataLength);
+ Uint32 left = SectionSegment::DataLength - ind;
+ SectionSegment * p = m_currentSegment;
+
+ while(len > left){
+ memcpy(dst, &p->theData[ind], 4 * left);
+ dst += left;
+ len -= left;
+ ind = 0;
+ left = SectionSegment::DataLength;
+ p = m_pool.getPtr(p->m_nextSegment);
+ }
+
+ memcpy(dst, &p->theData[ind], 4 * len);
+
+ m_pos += len;
+ m_currentSegment = p;
+ return true;
+}
+
diff --git a/storage/ndb/src/kernel/vm/SectionReader.hpp b/storage/ndb/src/kernel/vm/SectionReader.hpp
new file mode 100644
index 00000000000..b51006b6128
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SectionReader.hpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SECTION_READER_HPP
+#define SECTION_READER_HPP
+
+#include <ndb_types.h>
+
+class SectionReader {
+public:
+ SectionReader(struct SegmentedSectionPtr &,
+ class SectionSegmentPool &);
+
+ void reset();
+ bool step(Uint32 len);
+ bool getWord(Uint32 * dst);
+ bool peekWord(Uint32 * dst) const ;
+ bool peekWords(Uint32 * dst, Uint32 len) const;
+ Uint32 getSize() const;
+ bool getWords(Uint32 * dst, Uint32 len);
+
+private:
+ Uint32 m_pos;
+ Uint32 m_len;
+ class SectionSegmentPool & m_pool;
+ class SectionSegment * m_head;
+ class SectionSegment * m_currentSegment;
+};
+
+inline
+Uint32 SectionReader::getSize() const
+{
+ return m_len;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/SignalCounter.hpp b/storage/ndb/src/kernel/vm/SignalCounter.hpp
new file mode 100644
index 00000000000..62242cb65bd
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SignalCounter.hpp
@@ -0,0 +1,166 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SIGNAL_COUNTER_HPP
+#define SIGNAL_COUNTER_HPP
+
+#include <NodeBitmask.hpp>
+#include <ErrorReporter.hpp>
+
+class SignalCounter {
+ friend struct NodeReceiverGroup;
+
+private:
+ Uint32 m_count;
+ NdbNodeBitmask m_nodes;
+
+public:
+ SignalCounter() { clearWaitingFor();}
+ void clearWaitingFor();
+
+ /**
+ * When sending to different node
+ */
+ void setWaitingFor(Uint32 nodeId);
+ void clearWaitingFor(Uint32 nodeId);
+ void forceClearWaitingFor(Uint32 nodeId);
+
+ bool isWaitingFor(Uint32 nodeId) const;
+ bool done() const;
+
+ const char * getText() const;
+
+ SignalCounter& operator=(const NdbNodeBitmask & bitmask);
+ SignalCounter& operator=(const NodeReceiverGroup& rg) {
+ return (* this) = rg.m_nodes;
+ }
+
+ /**
+ * When sending to same node
+ */
+ SignalCounter& operator=(Uint32 count);
+ SignalCounter& operator--(int);
+ SignalCounter& operator++(int);
+ SignalCounter& operator+=(Uint32);
+ Uint32 getCount() const;
+};
+
+inline
+void
+SignalCounter::setWaitingFor(Uint32 nodeId) {
+ if(!m_nodes.get(nodeId)){
+ m_nodes.set(nodeId);
+ m_count++;
+ return;
+ }
+ ErrorReporter::handleAssert("SignalCounter::set", __FILE__, __LINE__);
+}
+
+inline
+bool
+SignalCounter::isWaitingFor(Uint32 nodeId) const {
+ return m_nodes.get(nodeId);
+}
+
+inline
+bool
+SignalCounter::done() const {
+ return m_count == 0;
+}
+
+inline
+Uint32
+SignalCounter::getCount() const {
+ return m_count;
+}
+
+inline
+void
+SignalCounter::clearWaitingFor(Uint32 nodeId) {
+ if(m_nodes.get(nodeId) && m_count > 0){
+ m_count--;
+ m_nodes.clear(nodeId);
+ return;
+ }
+ ErrorReporter::handleAssert("SignalCounter::clear", __FILE__, __LINE__);
+}
+
+inline
+void
+SignalCounter::clearWaitingFor(){
+ m_count = 0;
+ m_nodes.clear();
+}
+
+inline
+void
+SignalCounter::forceClearWaitingFor(Uint32 nodeId){
+ if(isWaitingFor(nodeId)){
+ clearWaitingFor(nodeId);
+ }
+}
+
+inline
+SignalCounter&
+SignalCounter::operator=(Uint32 count){
+ m_count = count;
+ m_nodes.clear();
+ return * this;
+}
+
+inline
+SignalCounter&
+SignalCounter::operator--(int){
+ if(m_count > 0){
+ m_count--;
+ return * this;
+ }
+ ErrorReporter::handleAssert("SignalCounter::operator--", __FILE__, __LINE__);
+ return * this;
+}
+
+inline
+SignalCounter&
+SignalCounter::operator++(int){
+ m_count++;
+ return * this;
+}
+
+inline
+SignalCounter&
+SignalCounter::operator+=(Uint32 n){
+ m_count += n;
+ return * this;
+}
+
+inline
+const char *
+SignalCounter::getText() const {
+ static char buf[255];
+ static char nodes[NodeBitmask::TextLength+1];
+ BaseString::snprintf(buf, sizeof(buf), "[SignalCounter: m_count=%d %s]", m_count, m_nodes.getText(nodes));
+ return buf;
+}
+
+inline
+SignalCounter&
+SignalCounter::operator=(const NdbNodeBitmask & bitmask){
+ m_nodes = bitmask;
+ m_count = bitmask.count();
+ return * this;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/SimBlockList.hpp b/storage/ndb/src/kernel/vm/SimBlockList.hpp
new file mode 100644
index 00000000000..40485a37425
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SimBlockList.hpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SimBlockList_H
+#define SimBlockList_H
+
+#include <MetaData.hpp>
+#include <SimulatedBlock.hpp>
+
+class Configuration;
+
+class SimBlockList
+{
+public:
+ SimBlockList();
+ ~SimBlockList();
+
+ void load(const Configuration & conf);
+ void unload();
+private:
+ int noOfBlocks;
+ SimulatedBlock** theList;
+ MetaData::Common* ptrMetaDataCommon;
+};
+
+inline
+SimBlockList::SimBlockList(){
+ noOfBlocks = 0;
+ theList = 0;
+ ptrMetaDataCommon = 0;
+}
+
+inline
+SimBlockList::~SimBlockList(){
+ unload();
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/SimplePropertiesSection.cpp b/storage/ndb/src/kernel/vm/SimplePropertiesSection.cpp
new file mode 100644
index 00000000000..070563be36b
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SimplePropertiesSection.cpp
@@ -0,0 +1,223 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <SimpleProperties.hpp>
+#include <TransporterDefinitions.hpp>
+#include "LongSignal.hpp"
+
+SimplePropertiesSectionReader::SimplePropertiesSectionReader
+(struct SegmentedSectionPtr & ptr, class SectionSegmentPool & pool)
+ : m_pool(pool)
+{
+ if(ptr.p == 0){
+ m_pos = 0;
+ m_len = 0;
+ m_head = 0;
+ m_currentSegment = 0;
+ } else {
+ m_pos = 0;
+ m_len = ptr.p->m_sz;
+ m_head = ptr.p;
+ m_currentSegment = ptr.p;
+ }
+ first();
+}
+
+void
+SimplePropertiesSectionReader::reset(){
+ m_pos = 0;
+ m_currentSegment = m_head;
+}
+
+bool
+SimplePropertiesSectionReader::step(Uint32 len){
+ if(m_pos + len >= m_len) {
+ m_pos++;
+ return false;
+ }
+ while(len > SectionSegment::DataLength){
+ m_currentSegment = m_pool.getPtr(m_currentSegment->m_nextSegment);
+
+ len -= SectionSegment::DataLength;
+ m_pos += SectionSegment::DataLength;
+ }
+
+ Uint32 ind = m_pos % SectionSegment::DataLength;
+ while(len > 0){
+ len--;
+ m_pos++;
+
+ ind++;
+ if(ind == SectionSegment::DataLength){
+ ind = 0;
+ m_currentSegment = m_pool.getPtr(m_currentSegment->m_nextSegment);
+ }
+ }
+ return true;
+}
+
+bool
+SimplePropertiesSectionReader::getWord(Uint32 * dst){
+ if (peekWord(dst)) {
+ step(1);
+ return true;
+ }
+ return false;
+}
+
+bool
+SimplePropertiesSectionReader::peekWord(Uint32 * dst) const {
+ if(m_pos < m_len){
+ Uint32 ind = m_pos % SectionSegment::DataLength;
+ * dst = m_currentSegment->theData[ind];
+ return true;
+ }
+ return false;
+}
+
+bool
+SimplePropertiesSectionReader::peekWords(Uint32 * dst, Uint32 len) const {
+ if(m_pos + len > m_len){
+ return false;
+ }
+ Uint32 ind = (m_pos % SectionSegment::DataLength);
+ Uint32 left = (SectionSegment::DataLength - ind);
+ SectionSegment * p = m_currentSegment;
+
+ while(len > left){
+ memcpy(dst, &p->theData[ind], 4 * left);
+ dst += left;
+ len -= left;
+ ind = 0;
+ left = SectionSegment::DataLength;
+ p = m_pool.getPtr(p->m_nextSegment);
+ }
+
+ memcpy(dst, &p->theData[ind], 4 * len);
+ return true;
+}
+
+bool
+SimplePropertiesSectionReader::getWords(Uint32 * dst, Uint32 len){
+ if(peekWords(dst, len)){
+ step(len);
+ return true;
+ }
+ return false;
+}
+
+SimplePropertiesSectionWriter::SimplePropertiesSectionWriter(class SectionSegmentPool & pool)
+ : m_pool(pool)
+{
+ Ptr<SectionSegment> first;
+ if(m_pool.seize(first)){
+ ;
+ } else {
+ m_pos = -1;
+ m_head = 0;
+ m_currentSegment = 0;
+ m_prevPtrI = RNIL;
+ return;
+ }
+ m_sz = 0;
+ m_pos = 0;
+ m_head = first.p;
+ m_head->m_lastSegment = first.i;
+ m_currentSegment = first.p;
+ m_prevPtrI = RNIL;
+}
+
+bool
+SimplePropertiesSectionWriter::reset(){
+ if(m_pos >= 0){
+ m_pos = 0;
+ return true;
+ }
+ return false;
+}
+
+bool
+SimplePropertiesSectionWriter::putWord(Uint32 val){
+ return putWords(&val, 1);
+}
+
+bool
+SimplePropertiesSectionWriter::putWords(const Uint32 * src, Uint32 len){
+ Uint32 left = SectionSegment::DataLength - m_pos;
+
+ while(len >= left){
+ memcpy(&m_currentSegment->theData[m_pos], src, 4 * left);
+ Ptr<SectionSegment> next;
+ if(m_pool.seize(next)){
+
+ m_prevPtrI = m_currentSegment->m_lastSegment;
+ m_currentSegment->m_nextSegment = next.i;
+ next.p->m_lastSegment = next.i;
+ m_currentSegment = next.p;
+
+ len -= left;
+ src += left;
+ m_sz += left;
+
+ left = SectionSegment::DataLength;
+ m_pos = 0;
+ } else {
+ abort();
+ return false;
+ }
+ }
+
+ memcpy(&m_currentSegment->theData[m_pos], src, 4 * len);
+ m_sz += len;
+ m_pos += len;
+
+ assert(m_pos < (int)SectionSegment::DataLength);
+
+ return true;
+}
+
+void
+SimplePropertiesSectionWriter::getPtr(struct SegmentedSectionPtr & dst){
+ // Set last ptr and size
+ if(m_pos >= 0){
+ dst.p = m_head;
+ dst.i = m_head->m_lastSegment;
+ dst.sz = m_sz;
+ m_head->m_sz = m_sz;
+ m_head->m_lastSegment = m_currentSegment->m_lastSegment;
+
+ if((m_pos % SectionSegment::DataLength) == 0){
+ m_pool.release(m_currentSegment->m_lastSegment);
+ m_head->m_lastSegment = m_prevPtrI;
+ }
+
+ m_sz = 0;
+ m_pos = -1;
+ m_head = m_currentSegment = 0;
+ m_prevPtrI = RNIL;
+ return ;
+ }
+ dst.p = 0;
+ dst.sz = 0;
+ dst.i = RNIL;
+
+ m_pool.release(m_head->m_lastSegment);
+
+ m_sz = 0;
+ m_pos = -1;
+ m_head = m_currentSegment = 0;
+ m_prevPtrI = RNIL;
+}
diff --git a/storage/ndb/src/kernel/vm/SimulatedBlock.cpp b/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
new file mode 100644
index 00000000000..35c0781a24d
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
@@ -0,0 +1,1804 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "SimulatedBlock.hpp"
+#include <NdbOut.hpp>
+#include <GlobalData.hpp>
+#include <Emulator.hpp>
+#include <ErrorHandlingMacros.hpp>
+#include <TimeQueue.hpp>
+#include <TransporterRegistry.hpp>
+#include <SignalLoggerManager.hpp>
+#include <FastScheduler.hpp>
+#include <NdbMem.h>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/ContinueFragmented.hpp>
+#include <signaldata/NodeStateSignalData.hpp>
+#include <DebuggerNames.hpp>
+#include "LongSignal.hpp"
+
+#include <Properties.hpp>
+#include "Configuration.hpp"
+
+#define ljamEntry() jamEntryLine(30000 + __LINE__)
+#define ljam() jamLine(30000 + __LINE__)
+
+//
+// Constructor, Destructor
+//
+SimulatedBlock::SimulatedBlock(BlockNumber blockNumber,
+ const class Configuration & conf)
+ : theNodeId(globalData.ownId),
+ theNumber(blockNumber),
+ theReference(numberToRef(blockNumber, globalData.ownId)),
+ theConfiguration(conf),
+ c_fragmentInfoHash(c_fragmentInfoPool),
+ c_linearFragmentSendList(c_fragmentSendPool),
+ c_segmentedFragmentSendList(c_fragmentSendPool),
+ c_mutexMgr(* this),
+ c_counterMgr(* this),
+ c_ptrMetaDataCommon(0)
+{
+ NewVarRef = 0;
+
+ globalData.setBlock(blockNumber, this);
+ c_fragmentIdCounter = 1;
+ c_fragSenderRunning = false;
+
+ Properties tmp;
+ const Properties * p = &tmp;
+ ndbrequire(p != 0);
+
+ Uint32 count = 10;
+ char buf[255];
+
+ count = 10;
+ BaseString::snprintf(buf, 255, "%s.FragmentSendPool", getBlockName(blockNumber));
+ if(!p->get(buf, &count))
+ p->get("FragmentSendPool", &count);
+ c_fragmentSendPool.setSize(count);
+
+ count = 10;
+ BaseString::snprintf(buf, 255, "%s.FragmentInfoPool", getBlockName(blockNumber));
+ if(!p->get(buf, &count))
+ p->get("FragmentInfoPool", &count);
+ c_fragmentInfoPool.setSize(count);
+
+ count = 10;
+ BaseString::snprintf(buf, 255, "%s.FragmentInfoHash", getBlockName(blockNumber));
+ if(!p->get(buf, &count))
+ p->get("FragmentInfoHash", &count);
+ c_fragmentInfoHash.setSize(count);
+
+ count = 5;
+ BaseString::snprintf(buf, 255, "%s.ActiveMutexes", getBlockName(blockNumber));
+ if(!p->get(buf, &count))
+ p->get("ActiveMutexes", &count);
+ c_mutexMgr.setSize(count);
+
+ c_counterMgr.setSize(5);
+
+#ifdef VM_TRACE_TIME
+ clearTimes();
+#endif
+
+ for(GlobalSignalNumber i = 0; i<=MAX_GSN; i++)
+ theExecArray[i] = 0;
+
+ installSimulatedBlockFunctions();
+ UpgradeStartup::installEXEC(this);
+
+ CLEAR_ERROR_INSERT_VALUE;
+
+#ifdef VM_TRACE
+ m_global_variables = new Ptr<void> * [1];
+ m_global_variables[0] = 0;
+#endif
+}
+
+SimulatedBlock::~SimulatedBlock()
+{
+ freeBat();
+#ifdef VM_TRACE_TIME
+ printTimes(stdout);
+#endif
+
+#ifdef VM_TRACE
+ delete [] m_global_variables;
+#endif
+}
+
+void
+SimulatedBlock::installSimulatedBlockFunctions(){
+ ExecFunction * a = theExecArray;
+ a[GSN_NODE_STATE_REP] = &SimulatedBlock::execNODE_STATE_REP;
+ a[GSN_CHANGE_NODE_STATE_REQ] = &SimulatedBlock::execCHANGE_NODE_STATE_REQ;
+ a[GSN_NDB_TAMPER] = &SimulatedBlock::execNDB_TAMPER;
+ a[GSN_SIGNAL_DROPPED_REP] = &SimulatedBlock::execSIGNAL_DROPPED_REP;
+ a[GSN_CONTINUE_FRAGMENTED]= &SimulatedBlock::execCONTINUE_FRAGMENTED;
+ a[GSN_UTIL_CREATE_LOCK_REF] = &SimulatedBlock::execUTIL_CREATE_LOCK_REF;
+ a[GSN_UTIL_CREATE_LOCK_CONF] = &SimulatedBlock::execUTIL_CREATE_LOCK_CONF;
+ a[GSN_UTIL_DESTROY_LOCK_REF] = &SimulatedBlock::execUTIL_DESTORY_LOCK_REF;
+ a[GSN_UTIL_DESTROY_LOCK_CONF] = &SimulatedBlock::execUTIL_DESTORY_LOCK_CONF;
+ a[GSN_UTIL_LOCK_REF] = &SimulatedBlock::execUTIL_LOCK_REF;
+ a[GSN_UTIL_LOCK_CONF] = &SimulatedBlock::execUTIL_LOCK_CONF;
+ a[GSN_UTIL_UNLOCK_REF] = &SimulatedBlock::execUTIL_UNLOCK_REF;
+ a[GSN_UTIL_UNLOCK_CONF] = &SimulatedBlock::execUTIL_UNLOCK_CONF;
+ a[GSN_READ_CONFIG_REQ] = &SimulatedBlock::execREAD_CONFIG_REQ;
+}
+
+void
+SimulatedBlock::addRecSignalImpl(GlobalSignalNumber gsn,
+ ExecFunction f, bool force){
+ if(gsn > MAX_GSN || (!force && theExecArray[gsn] != 0)){
+ char errorMsg[255];
+ BaseString::snprintf(errorMsg, 255,
+ "Illeagal signal (%d %d)", gsn, MAX_GSN);
+ ERROR_SET(fatal, ERR_ERROR_PRGERR, errorMsg, errorMsg);
+ }
+ theExecArray[gsn] = f;
+}
+
+void
+SimulatedBlock::signal_error(Uint32 gsn, Uint32 len, Uint32 recBlockNo,
+ const char* filename, int lineno) const
+{
+ char objRef[255];
+ BaseString::snprintf(objRef, 255, "%s:%d", filename, lineno);
+ char probData[255];
+ BaseString::snprintf(probData, 255,
+ "Signal (GSN: %d, Length: %d, Rec Block No: %d)",
+ gsn, len, recBlockNo);
+
+ ErrorReporter::handleError(ecError,
+ BLOCK_ERROR_BNR_ZERO,
+ probData,
+ objRef);
+}
+
+
+extern class SectionSegmentPool g_sectionSegmentPool;
+
+void
+SimulatedBlock::sendSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jobBuffer) const {
+
+ BlockNumber sendBnr = number();
+ BlockReference sendBRef = reference();
+
+ Uint32 noOfSections = signal->header.m_noOfSections;
+ Uint32 recBlock = refToBlock(ref);
+ Uint32 recNode = refToNode(ref);
+ Uint32 ourProcessor = globalData.ownId;
+
+ signal->header.theLength = length;
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = recBlock;
+
+ Uint32 tSignalId = signal->header.theSignalId;
+
+ if ((length == 0) || (length + noOfSections > 25) || (recBlock == 0)) {
+ signal_error(gsn, length, recBlock, __FILE__, __LINE__);
+ return;
+ }//if
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ Uint16 proc =
+ (recNode == 0 ? globalData.ownId : recNode);
+ signal->header.theSendersBlockRef = sendBRef;
+ globalSignalLoggers.sendSignal(signal->header,
+ jobBuffer,
+ &signal->theData[0],
+ proc,
+ signal->m_sectionPtr,
+ signal->header.m_noOfSections);
+ }
+#endif
+
+ if(recNode == ourProcessor || recNode == 0) {
+ signal->header.theSendersSignalId = tSignalId;
+ signal->header.theSendersBlockRef = sendBRef;
+ signal->header.theLength = length;
+ globalScheduler.execute(signal, jobBuffer, recBlock,
+ gsn);
+ signal->header.m_noOfSections = 0;
+ signal->header.m_fragmentInfo = 0;
+ return;
+ } else {
+ // send distributed Signal
+ SignalHeader sh;
+
+ Uint32 tTrace = signal->getTrace();
+
+ sh.theVerId_signalNumber = gsn;
+ sh.theReceiversBlockNumber = recBlock;
+ sh.theSendersBlockRef = sendBnr;
+ sh.theLength = length;
+ sh.theTrace = tTrace;
+ sh.theSignalId = tSignalId;
+ sh.m_noOfSections = noOfSections;
+ sh.m_fragmentInfo = 0;
+
+#ifdef TRACE_DISTRIBUTED
+ ndbout_c("send: %s(%d) to (%s, %d)",
+ getSignalName(gsn), gsn, getBlockName(recBlock),
+ recNode);
+#endif
+ SendStatus ss = globalTransporterRegistry.prepareSend(&sh, jobBuffer,
+ &signal->theData[0],
+ recNode,
+ g_sectionSegmentPool,
+ signal->m_sectionPtr);
+
+ ndbrequire(ss == SEND_OK || ss == SEND_BLOCKED || ss == SEND_DISCONNECTED);
+ ::releaseSections(noOfSections, signal->m_sectionPtr);
+ signal->header.m_noOfSections = 0;
+ }
+ return;
+}
+
+void
+SimulatedBlock::sendSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jobBuffer) const {
+
+ Uint32 noOfSections = signal->header.m_noOfSections;
+ Uint32 tSignalId = signal->header.theSignalId;
+ Uint32 tTrace = signal->getTrace();
+ Uint32 tFragInf = signal->header.m_fragmentInfo;
+
+ Uint32 ourProcessor = globalData.ownId;
+ Uint32 recBlock = rg.m_block;
+
+ signal->header.theLength = length;
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = recBlock;
+ signal->header.theSendersSignalId = tSignalId;
+ signal->header.theSendersBlockRef = reference();
+
+ if ((length == 0) || (length + noOfSections > 25) || (recBlock == 0)) {
+ signal_error(gsn, length, recBlock, __FILE__, __LINE__);
+ return;
+ }//if
+
+ SignalHeader sh;
+
+ sh.theVerId_signalNumber = gsn;
+ sh.theReceiversBlockNumber = recBlock;
+ sh.theSendersBlockRef = number();
+ sh.theLength = length;
+ sh.theTrace = tTrace;
+ sh.theSignalId = tSignalId;
+ sh.m_noOfSections = noOfSections;
+ sh.m_fragmentInfo = tFragInf;
+
+ /**
+ * Check own node
+ */
+ bool release = true;
+ if(rg.m_nodes.get(0) || rg.m_nodes.get(ourProcessor)){
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ globalSignalLoggers.sendSignal(signal->header,
+ jobBuffer,
+ &signal->theData[0],
+ ourProcessor,
+ signal->m_sectionPtr,
+ signal->header.m_noOfSections);
+ }
+#endif
+ globalScheduler.execute(signal, jobBuffer, recBlock, gsn);
+
+ rg.m_nodes.clear((Uint32)0);
+ rg.m_nodes.clear(ourProcessor);
+ release = false;
+ }
+
+ /**
+ * Do the big loop
+ */
+ Uint32 recNode = 0;
+ while(!rg.m_nodes.isclear()){
+ recNode = rg.m_nodes.find(recNode + 1);
+ rg.m_nodes.clear(recNode);
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ globalSignalLoggers.sendSignal(signal->header,
+ jobBuffer,
+ &signal->theData[0],
+ recNode,
+ signal->m_sectionPtr,
+ signal->header.m_noOfSections);
+ }
+#endif
+
+#ifdef TRACE_DISTRIBUTED
+ ndbout_c("send: %s(%d) to (%s, %d)",
+ getSignalName(gsn), gsn, getBlockName(recBlock),
+ recNode);
+#endif
+
+ SendStatus ss = globalTransporterRegistry.prepareSend(&sh, jobBuffer,
+ &signal->theData[0],
+ recNode,
+ g_sectionSegmentPool,
+ signal->m_sectionPtr);
+ ndbrequire(ss == SEND_OK || ss == SEND_BLOCKED || ss == SEND_DISCONNECTED);
+ }
+
+ if(release){
+ ::releaseSections(noOfSections, signal->m_sectionPtr);
+ }
+
+ signal->header.m_noOfSections = 0;
+ signal->header.m_fragmentInfo = 0;
+
+ return;
+}
+
+bool import(Ptr<SectionSegment> & first, const Uint32 * src, Uint32 len);
+
+void
+SimulatedBlock::sendSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jobBuffer,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections) const {
+
+ BlockNumber sendBnr = number();
+ BlockReference sendBRef = reference();
+
+ Uint32 recBlock = refToBlock(ref);
+ Uint32 recNode = refToNode(ref);
+ Uint32 ourProcessor = globalData.ownId;
+
+ ::releaseSections(signal->header.m_noOfSections, signal->m_sectionPtr);
+
+ signal->header.theLength = length;
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = recBlock;
+ signal->header.m_noOfSections = noOfSections;
+
+ Uint32 tSignalId = signal->header.theSignalId;
+ Uint32 tFragInfo = signal->header.m_fragmentInfo;
+
+ if ((length == 0) || (length + noOfSections > 25) || (recBlock == 0)) {
+ signal_error(gsn, length, recBlock, __FILE__, __LINE__);
+ return;
+ }//if
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ Uint16 proc =
+ (recNode == 0 ? globalData.ownId : recNode);
+ signal->header.theSendersBlockRef = sendBRef;
+ globalSignalLoggers.sendSignal(signal->header,
+ jobBuffer,
+ &signal->theData[0],
+ proc,
+ ptr, noOfSections);
+ }
+#endif
+
+ if(recNode == ourProcessor || recNode == 0) {
+ signal->header.theSendersSignalId = tSignalId;
+ signal->header.theSendersBlockRef = sendBRef;
+
+ /**
+ * We have to copy the data
+ */
+ Ptr<SectionSegment> segptr[3];
+ for(Uint32 i = 0; i<noOfSections; i++){
+ ndbrequire(import(segptr[i], ptr[i].p, ptr[i].sz));
+ signal->m_sectionPtr[i].i = segptr[i].i;
+ }
+
+ globalScheduler.execute(signal, jobBuffer, recBlock,
+ gsn);
+ signal->header.m_noOfSections = 0;
+ return;
+ } else {
+ // send distributed Signal
+ SignalHeader sh;
+
+ Uint32 tTrace = signal->getTrace();
+ Uint32 noOfSections = signal->header.m_noOfSections;
+
+ sh.theVerId_signalNumber = gsn;
+ sh.theReceiversBlockNumber = recBlock;
+ sh.theSendersBlockRef = sendBnr;
+ sh.theLength = length;
+ sh.theTrace = tTrace;
+ sh.theSignalId = tSignalId;
+ sh.m_noOfSections = noOfSections;
+ sh.m_fragmentInfo = tFragInfo;
+
+#ifdef TRACE_DISTRIBUTED
+ ndbout_c("send: %s(%d) to (%s, %d)",
+ getSignalName(gsn), gsn, getBlockName(recBlock),
+ recNode);
+#endif
+
+ SendStatus ss = globalTransporterRegistry.prepareSend(&sh, jobBuffer,
+ &signal->theData[0],
+ recNode,
+ ptr);
+ ndbrequire(ss == SEND_OK || ss == SEND_BLOCKED || ss == SEND_DISCONNECTED);
+ }
+
+ signal->header.m_noOfSections = 0;
+ signal->header.m_fragmentInfo = 0;
+ return;
+}
+
+void
+SimulatedBlock::sendSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jobBuffer,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections) const {
+
+ Uint32 tSignalId = signal->header.theSignalId;
+ Uint32 tTrace = signal->getTrace();
+ Uint32 tFragInfo = signal->header.m_fragmentInfo;
+
+ Uint32 ourProcessor = globalData.ownId;
+ Uint32 recBlock = rg.m_block;
+
+ ::releaseSections(signal->header.m_noOfSections, signal->m_sectionPtr);
+
+ signal->header.theLength = length;
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = recBlock;
+ signal->header.theSendersSignalId = tSignalId;
+ signal->header.theSendersBlockRef = reference();
+ signal->header.m_noOfSections = noOfSections;
+
+ if ((length == 0) || (length + noOfSections > 25) || (recBlock == 0)) {
+ signal_error(gsn, length, recBlock, __FILE__, __LINE__);
+ return;
+ }//if
+
+ SignalHeader sh;
+ sh.theVerId_signalNumber = gsn;
+ sh.theReceiversBlockNumber = recBlock;
+ sh.theSendersBlockRef = number();
+ sh.theLength = length;
+ sh.theTrace = tTrace;
+ sh.theSignalId = tSignalId;
+ sh.m_noOfSections = noOfSections;
+ sh.m_fragmentInfo = tFragInfo;
+
+ /**
+ * Check own node
+ */
+ if(rg.m_nodes.get(0) || rg.m_nodes.get(ourProcessor)){
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ globalSignalLoggers.sendSignal(signal->header,
+ jobBuffer,
+ &signal->theData[0],
+ ourProcessor,
+ ptr, noOfSections);
+ }
+#endif
+ /**
+ * We have to copy the data
+ */
+ Ptr<SectionSegment> segptr[3];
+ for(Uint32 i = 0; i<noOfSections; i++){
+ ndbrequire(import(segptr[i], ptr[i].p, ptr[i].sz));
+ signal->m_sectionPtr[i].i = segptr[i].i;
+ }
+ globalScheduler.execute(signal, jobBuffer, recBlock, gsn);
+
+ rg.m_nodes.clear((Uint32)0);
+ rg.m_nodes.clear(ourProcessor);
+ }
+
+ /**
+ * Do the big loop
+ */
+ Uint32 recNode = 0;
+ while(!rg.m_nodes.isclear()){
+ recNode = rg.m_nodes.find(recNode + 1);
+ rg.m_nodes.clear(recNode);
+
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ globalSignalLoggers.sendSignal(signal->header,
+ jobBuffer,
+ &signal->theData[0],
+ recNode,
+ ptr, noOfSections);
+ }
+#endif
+
+#ifdef TRACE_DISTRIBUTED
+ ndbout_c("send: %s(%d) to (%s, %d)",
+ getSignalName(gsn), gsn, getBlockName(recBlock),
+ recNode);
+#endif
+
+ SendStatus ss = globalTransporterRegistry.prepareSend(&sh, jobBuffer,
+ &signal->theData[0],
+ recNode,
+ ptr);
+ ndbrequire(ss == SEND_OK || ss == SEND_BLOCKED || ss == SEND_DISCONNECTED);
+ }
+
+ signal->header.m_noOfSections = 0;
+ signal->header.m_fragmentInfo = 0;
+
+ return;
+}
+
+void
+SimulatedBlock::sendSignalWithDelay(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 delayInMilliSeconds,
+ Uint32 length) const {
+
+ BlockNumber bnr = refToBlock(ref);
+
+ //BlockNumber sendBnr = number();
+ BlockReference sendBRef = reference();
+
+ if (bnr == 0) {
+ bnr_error();
+ }//if
+
+ signal->header.theLength = length;
+ signal->header.theSendersSignalId = signal->header.theSignalId;
+ signal->header.theSendersBlockRef = sendBRef;
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = bnr;
+
+#ifdef VM_TRACE
+ {
+ if(globalData.testOn){
+ globalSignalLoggers.sendSignalWithDelay(delayInMilliSeconds,
+ signal->header,
+ 0,
+ &signal->theData[0],
+ globalData.ownId,
+ signal->m_sectionPtr,
+ signal->header.m_noOfSections);
+ }
+ }
+#endif
+ globalTimeQueue.insert(signal, bnr, gsn, delayInMilliSeconds);
+
+ signal->header.m_noOfSections = 0;
+ signal->header.m_fragmentInfo = 0;
+
+ // befor 2nd parameter to globalTimeQueue.insert
+ // (Priority)theSendSig[sigIndex].jobBuffer
+}
+
+void
+SimulatedBlock::releaseSections(Signal* signal){
+ ::releaseSections(signal->header.m_noOfSections, signal->m_sectionPtr);
+ signal->header.m_noOfSections = 0;
+}
+
+class SectionSegmentPool&
+SimulatedBlock::getSectionSegmentPool(){
+ return g_sectionSegmentPool;
+}
+
+NewVARIABLE *
+SimulatedBlock::allocateBat(int batSize){
+ NewVARIABLE* bat = NewVarRef;
+ bat = (NewVARIABLE*)realloc(bat, batSize * sizeof(NewVARIABLE));
+ NewVarRef = bat;
+ theBATSize = batSize;
+ return bat;
+}
+
+void
+SimulatedBlock::freeBat(){
+ if(NewVarRef != 0){
+ free(NewVarRef);
+ NewVarRef = 0;
+ }
+}
+
+const NewVARIABLE *
+SimulatedBlock::getBat(Uint16 blockNo){
+ SimulatedBlock * sb = globalData.getBlock(blockNo);
+ if(sb == 0)
+ return 0;
+ return sb->NewVarRef;
+}
+
+Uint16
+SimulatedBlock::getBatSize(Uint16 blockNo){
+ SimulatedBlock * sb = globalData.getBlock(blockNo);
+ if(sb == 0)
+ return 0;
+ return sb->theBATSize;
+}
+
+void*
+SimulatedBlock::allocRecord(const char * type, size_t s, size_t n, bool clear)
+{
+
+ void * p = NULL;
+ size_t size = n*s;
+ refresh_watch_dog();
+ if (size > 0){
+#ifdef VM_TRACE_MEM
+ ndbout_c("%s::allocRecord(%s, %u, %u) = %u bytes",
+ getBlockName(number()),
+ type,
+ s,
+ n,
+ size);
+#endif
+ p = NdbMem_Allocate(size);
+ if (p == NULL){
+ char buf1[255];
+ char buf2[255];
+ BaseString::snprintf(buf1, sizeof(buf1), "%s could not allocate memory for %s",
+ getBlockName(number()), type);
+ BaseString::snprintf(buf2, sizeof(buf2), "Requested: %ux%u = %u bytes",
+ (Uint32)s, (Uint32)n, (Uint32)size);
+ ERROR_SET(fatal, ERR_MEMALLOC, buf1, buf2);
+ }
+
+ if(clear){
+ char * ptr = (char*)p;
+ const Uint32 chunk = 128 * 1024;
+ while(size > chunk){
+ refresh_watch_dog();
+ memset(ptr, 0, chunk);
+ ptr += chunk;
+ size -= chunk;
+ }
+ refresh_watch_dog();
+ memset(ptr, 0, size);
+ }
+ }
+ return p;
+}
+
+void
+SimulatedBlock::deallocRecord(void ** ptr,
+ const char * type, size_t s, size_t n){
+ (void)type;
+ (void)s;
+ (void)n;
+
+ if(* ptr != 0){
+ NdbMem_Free(* ptr);
+ * ptr = 0;
+ }
+}
+
+void
+SimulatedBlock::refresh_watch_dog()
+{
+ globalData.incrementWatchDogCounter(1);
+}
+
+void
+SimulatedBlock::progError(int line, int err_code, const char* extra) const {
+ jamLine(line);
+
+ const char *aBlockName = getBlockName(number(), "VM Kernel");
+
+ // Pack status of interesting config variables
+ // so that we can print them in error.log
+ int magicStatus =
+ (theConfiguration.stopOnError()<<1) +
+ (theConfiguration.getInitialStart()<<2) +
+ (theConfiguration.getDaemonMode()<<3);
+
+
+ /* Add line number to block name */
+ char buf[100];
+ BaseString::snprintf(&buf[0], 100, "%s (Line: %d) 0x%.8x",
+ aBlockName, line, magicStatus);
+
+ ErrorReporter::handleError(ecError, err_code, extra, buf);
+
+}
+
+void
+SimulatedBlock::infoEvent(const char * msg, ...) const {
+ if(msg == 0)
+ return;
+
+ Uint32 theData[25];
+ theData[0] = NDB_LE_InfoEvent;
+ char * buf = (char *)&(theData[1]);
+
+ va_list ap;
+ va_start(ap, msg);
+ BaseString::vsnprintf(buf, 96, msg, ap); // 96 = 100 - 4
+ va_end(ap);
+
+ int len = strlen(buf) + 1;
+ if(len > 96){
+ len = 96;
+ buf[95] = 0;
+ }
+
+ /**
+ * Init and put it into the job buffer
+ */
+ SignalHeader sh;
+ memset(&sh, 0, sizeof(SignalHeader));
+
+ const Signal * signal = globalScheduler.getVMSignals();
+ Uint32 tTrace = signal->header.theTrace;
+ Uint32 tSignalId = signal->header.theSignalId;
+
+ sh.theVerId_signalNumber = GSN_EVENT_REP;
+ sh.theReceiversBlockNumber = CMVMI;
+ sh.theSendersBlockRef = reference();
+ sh.theTrace = tTrace;
+ sh.theSignalId = tSignalId;
+ sh.theLength = ((len+3)/4)+1;
+
+ Uint32 secPtrI[3]; // Dummy
+ globalScheduler.execute(&sh, JBB, theData, secPtrI);
+}
+
+void
+SimulatedBlock::warningEvent(const char * msg, ...) const {
+ if(msg == 0)
+ return;
+
+ Uint32 theData[25];
+ theData[0] = NDB_LE_WarningEvent;
+ char * buf = (char *)&(theData[1]);
+
+ va_list ap;
+ va_start(ap, msg);
+ BaseString::vsnprintf(buf, 96, msg, ap); // 96 = 100 - 4
+ va_end(ap);
+
+ int len = strlen(buf) + 1;
+ if(len > 96){
+ len = 96;
+ buf[95] = 0;
+ }
+
+ /**
+ * Init and put it into the job buffer
+ */
+ SignalHeader sh;
+ memset(&sh, 0, sizeof(SignalHeader));
+
+ const Signal * signal = globalScheduler.getVMSignals();
+ Uint32 tTrace = signal->header.theTrace;
+ Uint32 tSignalId = signal->header.theSignalId;
+
+ sh.theVerId_signalNumber = GSN_EVENT_REP;
+ sh.theReceiversBlockNumber = CMVMI;
+ sh.theSendersBlockRef = reference();
+ sh.theTrace = tTrace;
+ sh.theSignalId = tSignalId;
+ sh.theLength = ((len+3)/4)+1;
+
+ Uint32 secPtrI[3]; // Dummy
+ globalScheduler.execute(&sh, JBB, theData, secPtrI);
+}
+
+void
+SimulatedBlock::execNODE_STATE_REP(Signal* signal){
+ const NodeStateRep * const rep = (NodeStateRep *)&signal->theData[0];
+
+ this->theNodeState = rep->nodeState;
+}
+
+void
+SimulatedBlock::execCHANGE_NODE_STATE_REQ(Signal* signal){
+ const ChangeNodeStateReq * const req =
+ (ChangeNodeStateReq *)&signal->theData[0];
+
+ this->theNodeState = req->nodeState;
+ const Uint32 senderData = req->senderData;
+ const BlockReference senderRef = req->senderRef;
+
+ /**
+ * Pack return signal
+ */
+ ChangeNodeStateConf * const conf =
+ (ChangeNodeStateConf *)&signal->theData[0];
+
+ conf->senderData = senderData;
+
+ sendSignal(senderRef, GSN_CHANGE_NODE_STATE_CONF, signal,
+ ChangeNodeStateConf::SignalLength, JBB);
+}
+
+void
+SimulatedBlock::execNDB_TAMPER(Signal * signal){
+ SET_ERROR_INSERT_VALUE(signal->theData[0]);
+}
+
+void
+SimulatedBlock::execSIGNAL_DROPPED_REP(Signal * signal){
+ ErrorReporter::handleError(ecError,
+ ERR_OUT_OF_LONG_SIGNAL_MEMORY,
+ "Signal lost, out of long signal memory",
+ __FILE__,
+ NST_ErrorHandler);
+}
+
+void
+SimulatedBlock::execCONTINUE_FRAGMENTED(Signal * signal){
+ ljamEntry();
+
+ Ptr<FragmentSendInfo> fragPtr;
+
+ c_segmentedFragmentSendList.first(fragPtr);
+ for(; !fragPtr.isNull();){
+ ljam();
+ Ptr<FragmentSendInfo> copyPtr = fragPtr;
+ c_segmentedFragmentSendList.next(fragPtr);
+
+ sendNextSegmentedFragment(signal, * copyPtr.p);
+ if(copyPtr.p->m_status == FragmentSendInfo::SendComplete){
+ ljam();
+ if(copyPtr.p->m_callback.m_callbackFunction != 0) {
+ ljam();
+ execute(signal, copyPtr.p->m_callback, 0);
+ }//if
+ c_segmentedFragmentSendList.release(copyPtr);
+ }
+ }
+
+ c_linearFragmentSendList.first(fragPtr);
+ for(; !fragPtr.isNull();){
+ ljam();
+ Ptr<FragmentSendInfo> copyPtr = fragPtr;
+ c_linearFragmentSendList.next(fragPtr);
+
+ sendNextLinearFragment(signal, * copyPtr.p);
+ if(copyPtr.p->m_status == FragmentSendInfo::SendComplete){
+ ljam();
+ if(copyPtr.p->m_callback.m_callbackFunction != 0) {
+ ljam();
+ execute(signal, copyPtr.p->m_callback, 0);
+ }//if
+ c_linearFragmentSendList.release(copyPtr);
+ }
+ }
+
+ if(c_segmentedFragmentSendList.isEmpty() &&
+ c_linearFragmentSendList.isEmpty()){
+ ljam();
+ c_fragSenderRunning = false;
+ return;
+ }
+
+ ContinueFragmented * sig = (ContinueFragmented*)signal->getDataPtrSend();
+ sig->line = __LINE__;
+ sendSignal(reference(), GSN_CONTINUE_FRAGMENTED, signal, 1, JBB);
+}
+
+#ifdef VM_TRACE_TIME
+void
+SimulatedBlock::clearTimes() {
+ for(Uint32 i = 0; i <= MAX_GSN; i++){
+ m_timeTrace[i].cnt = 0;
+ m_timeTrace[i].sum = 0;
+ m_timeTrace[i].sub = 0;
+ }
+}
+
+void
+SimulatedBlock::printTimes(FILE * output){
+ fprintf(output, "-- %s --\n", getBlockName(number()));
+ Uint64 sum = 0;
+ for(Uint32 i = 0; i <= MAX_GSN; i++){
+ Uint32 n = m_timeTrace[i].cnt;
+ if(n != 0){
+ double dn = n;
+
+ double avg = m_timeTrace[i].sum;
+ double avg2 = avg - m_timeTrace[i].sub;
+
+ avg /= dn;
+ avg2 /= dn;
+
+ fprintf(output,
+ //name ; cnt ; loc ; acc
+ "%s ; #%d ; %dus ; %dus ; %dms\n",
+ getSignalName(i), n, (Uint32)avg, (Uint32)avg2,
+ (Uint32)((m_timeTrace[i].sum - m_timeTrace[i].sub + 500)/ 1000));
+
+ sum += (m_timeTrace[i].sum - m_timeTrace[i].sub);
+ }
+ }
+ sum = (sum + 500)/ 1000;
+ fprintf(output, "-- %s : %d --\n", getBlockName(number()), sum);
+ fprintf(output, "\n");
+ fflush(output);
+}
+
+#endif
+
+void release(SegmentedSectionPtr & ptr);
+
+SimulatedBlock::FragmentInfo::FragmentInfo(Uint32 fragId, Uint32 sender){
+ m_fragmentId = fragId;
+ m_senderRef = sender;
+ m_sectionPtrI[0] = RNIL;
+ m_sectionPtrI[1] = RNIL;
+ m_sectionPtrI[2] = RNIL;
+}
+
+SimulatedBlock::FragmentSendInfo::FragmentSendInfo()
+{
+}
+
+bool
+SimulatedBlock::assembleFragments(Signal * signal){
+ Uint32 sigLen = signal->length() - 1;
+ Uint32 fragId = signal->theData[sigLen];
+ Uint32 fragInfo = signal->header.m_fragmentInfo;
+ Uint32 senderRef = signal->getSendersBlockRef();
+
+ if(fragInfo == 0){
+ return true;
+ }
+
+ const Uint32 secs = signal->header.m_noOfSections;
+ const Uint32 * const secNos = &signal->theData[sigLen - secs];
+
+ if(fragInfo == 1){
+ /**
+ * First in train
+ */
+ Ptr<FragmentInfo> fragPtr;
+ if(!c_fragmentInfoHash.seize(fragPtr)){
+ ndbrequire(false);
+ return false;
+ }
+
+ new (fragPtr.p)FragmentInfo(fragId, senderRef);
+ c_fragmentInfoHash.add(fragPtr);
+
+ for(Uint32 i = 0; i<secs; i++){
+ Uint32 sectionNo = secNos[i];
+ ndbassert(sectionNo < 3);
+ fragPtr.p->m_sectionPtrI[sectionNo] = signal->m_sectionPtr[i].i;
+ }
+
+ /**
+ * Don't release allocated segments
+ */
+ signal->header.m_noOfSections = 0;
+ return false;
+ }
+
+ FragmentInfo key(fragId, senderRef);
+ Ptr<FragmentInfo> fragPtr;
+ if(c_fragmentInfoHash.find(fragPtr, key)){
+
+ /**
+ * FragInfo == 2 or 3
+ */
+ Uint32 i;
+ for(i = 0; i<secs; i++){
+ Uint32 sectionNo = secNos[i];
+ ndbassert(sectionNo < 3);
+ Uint32 sectionPtrI = signal->m_sectionPtr[i].i;
+ if(fragPtr.p->m_sectionPtrI[sectionNo] != RNIL){
+ linkSegments(fragPtr.p->m_sectionPtrI[sectionNo], sectionPtrI);
+ } else {
+ fragPtr.p->m_sectionPtrI[sectionNo] = sectionPtrI;
+ }
+ }
+
+ /**
+ * fragInfo = 2
+ */
+ if(fragInfo == 2){
+ signal->header.m_noOfSections = 0;
+ return false;
+ }
+
+ /**
+ * fragInfo = 3
+ */
+ for(i = 0; i<3; i++){
+ Uint32 ptrI = fragPtr.p->m_sectionPtrI[i];
+ if(ptrI != RNIL){
+ signal->m_sectionPtr[i].i = ptrI;
+ } else {
+ break;
+ }
+ }
+ signal->setLength(sigLen - i);
+ signal->header.m_noOfSections = i;
+ signal->header.m_fragmentInfo = 0;
+ getSections(i, signal->m_sectionPtr);
+
+ c_fragmentInfoHash.release(fragPtr);
+ return true;
+ }
+
+ /**
+ * Unable to find fragment
+ */
+ ndbrequire(false);
+ return false;
+}
+
+bool
+SimulatedBlock::sendFirstFragment(FragmentSendInfo & info,
+ NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ Uint32 messageSize){
+
+ info.m_sectionPtr[0].m_segmented.i = RNIL;
+ info.m_sectionPtr[1].m_segmented.i = RNIL;
+ info.m_sectionPtr[2].m_segmented.i = RNIL;
+
+ Uint32 totalSize = 0;
+ SectionSegment * p;
+ switch(signal->header.m_noOfSections){
+ case 3:
+ p = signal->m_sectionPtr[2].p;
+ info.m_sectionPtr[2].m_segmented.p = p;
+ info.m_sectionPtr[2].m_segmented.i = signal->m_sectionPtr[2].i;
+ totalSize += p->m_sz;
+ case 2:
+ p = signal->m_sectionPtr[1].p;
+ info.m_sectionPtr[1].m_segmented.p = p;
+ info.m_sectionPtr[1].m_segmented.i = signal->m_sectionPtr[1].i;
+ totalSize += p->m_sz;
+ case 1:
+ p = signal->m_sectionPtr[0].p;
+ info.m_sectionPtr[0].m_segmented.p = p;
+ info.m_sectionPtr[0].m_segmented.i = signal->m_sectionPtr[0].i;
+ totalSize += p->m_sz;
+ }
+
+ if(totalSize <= messageSize + SectionSegment::DataLength){
+ /**
+ * Send signal directly
+ */
+ sendSignal(rg, gsn, signal, length, jbuf);
+ info.m_status = FragmentSendInfo::SendComplete;
+ return true;
+ }
+
+ /**
+ * Consume sections
+ */
+ signal->header.m_noOfSections = 0;
+
+ /**
+ * Setup info object
+ */
+ info.m_status = FragmentSendInfo::SendNotComplete;
+ info.m_prio = (Uint8)jbuf;
+ info.m_gsn = gsn;
+ info.m_fragInfo = 1;
+ info.m_messageSize = messageSize;
+ info.m_fragmentId = c_fragmentIdCounter++;
+ info.m_nodeReceiverGroup = rg;
+ info.m_callback.m_callbackFunction = 0;
+
+ Ptr<SectionSegment> tmp;
+ if(!import(tmp, &signal->theData[0], length)){
+ ndbrequire(false);
+ return false;
+ }
+ info.m_theDataSection.p = &tmp.p->theData[0];
+ info.m_theDataSection.sz = length;
+ tmp.p->theData[length] = tmp.i;
+
+ sendNextSegmentedFragment(signal, info);
+
+ if(c_fragmentIdCounter == 0){
+ /**
+ * Fragment id 0 is invalid
+ */
+ c_fragmentIdCounter = 1;
+ }
+
+ return true;
+}
+
+#if 0
+#define lsout(x) x
+#else
+#define lsout(x)
+#endif
+
+void
+SimulatedBlock::sendNextSegmentedFragment(Signal* signal,
+ FragmentSendInfo & info){
+
+ /**
+ * Store "theData"
+ */
+ const Uint32 sigLen = info.m_theDataSection.sz;
+ memcpy(&signal->theData[0], info.m_theDataSection.p, 4 * sigLen);
+
+ Uint32 sz = 0;
+ Uint32 maxSz = info.m_messageSize;
+
+ Int32 secNo = 2;
+ Uint32 secCount = 0;
+ Uint32 * secNos = &signal->theData[sigLen];
+
+ enum { Unknown = 0, Full = 1 } loop = Unknown;
+ for(; secNo >= 0 && secCount < 3; secNo--){
+ Uint32 ptrI = info.m_sectionPtr[secNo].m_segmented.i;
+ if(ptrI == RNIL)
+ continue;
+
+ info.m_sectionPtr[secNo].m_segmented.i = RNIL;
+
+ SectionSegment * ptrP = info.m_sectionPtr[secNo].m_segmented.p;
+ const Uint32 size = ptrP->m_sz;
+
+ signal->m_sectionPtr[secCount].i = ptrI;
+ signal->m_sectionPtr[secCount].p = ptrP;
+ signal->m_sectionPtr[secCount].sz = size;
+ secNos[secCount] = secNo;
+ secCount++;
+
+ const Uint32 sizeLeft = maxSz - sz;
+ if(size <= sizeLeft){
+ /**
+ * The section fits
+ */
+ sz += size;
+ lsout(ndbout_c("section %d saved as %d", secNo, secCount-1));
+ continue;
+ }
+
+ const Uint32 overflow = size - sizeLeft; // > 0
+ if(overflow <= SectionSegment::DataLength){
+ /**
+ * Only one segment left to send
+ * send even if sizeLeft <= size
+ */
+ lsout(ndbout_c("section %d saved as %d but full over: %d",
+ secNo, secCount-1, overflow));
+ secNo--;
+ break;
+ }
+
+ // size >= 61
+ if(sizeLeft < SectionSegment::DataLength){
+ /**
+ * Less than one segment left (space)
+ * dont bother sending
+ */
+ secCount--;
+ info.m_sectionPtr[secNo].m_segmented.i = ptrI;
+ loop = Full;
+ lsout(ndbout_c("section %d not saved", secNo));
+ break;
+ }
+
+ /**
+ * Split list
+ * 1) Find place to split
+ * 2) Rewrite header (the part that will be sent)
+ * 3) Write new header (for remaining part)
+ * 4) Store new header on FragmentSendInfo - record
+ */
+ // size >= 61 && sizeLeft >= 60
+ Uint32 sum = SectionSegment::DataLength;
+ Uint32 prevPtrI = ptrI;
+ ptrI = ptrP->m_nextSegment;
+ const Uint32 fill = sizeLeft - SectionSegment::DataLength;
+ while(sum < fill){
+ prevPtrI = ptrI;
+ ptrP = g_sectionSegmentPool.getPtr(ptrI);
+ ptrI = ptrP->m_nextSegment;
+ sum += SectionSegment::DataLength;
+ }
+
+ /**
+ * Rewrite header w.r.t size and last
+ */
+ Uint32 prev = secCount - 1;
+ const Uint32 last = signal->m_sectionPtr[prev].p->m_lastSegment;
+ signal->m_sectionPtr[prev].p->m_lastSegment = prevPtrI;
+ signal->m_sectionPtr[prev].p->m_sz = sum;
+ signal->m_sectionPtr[prev].sz = sum;
+
+ /**
+ * Write "new" list header
+ */
+ ptrP = g_sectionSegmentPool.getPtr(ptrI);
+ ptrP->m_lastSegment = last;
+ ptrP->m_sz = size - sum;
+
+ /**
+ * And store it on info-record
+ */
+ info.m_sectionPtr[secNo].m_segmented.i = ptrI;
+ info.m_sectionPtr[secNo].m_segmented.p = ptrP;
+
+ loop = Full;
+ lsout(ndbout_c("section %d split into %d", secNo, prev));
+ break;
+ }
+
+ lsout(ndbout_c("loop: %d secNo: %d secCount: %d sz: %d",
+ loop, secNo, secCount, sz));
+
+ /**
+ * Store fragment id
+ */
+ secNos[secCount] = info.m_fragmentId;
+
+ Uint32 fragInfo = info.m_fragInfo;
+ info.m_fragInfo = 2;
+ switch(loop){
+ case Unknown:
+ if(secNo >= 0){
+ lsout(ndbout_c("Unknown - Full"));
+ /**
+ * Not finished
+ */
+ break;
+ }
+ // Fall through
+ lsout(ndbout_c("Unknown - Done"));
+ info.m_status = FragmentSendInfo::SendComplete;
+ ndbassert(fragInfo == 2);
+ fragInfo = 3;
+ case Full:
+ break;
+ }
+
+ signal->header.m_fragmentInfo = fragInfo;
+ signal->header.m_noOfSections = secCount;
+
+ sendSignal(info.m_nodeReceiverGroup,
+ info.m_gsn,
+ signal,
+ sigLen + secCount + 1,
+ (JobBufferLevel)info.m_prio);
+
+ if(fragInfo == 3){
+ /**
+ * This is the last signal
+ */
+ g_sectionSegmentPool.release(info.m_theDataSection.p[sigLen]);
+ }
+}
+
+bool
+SimulatedBlock::sendFirstFragment(FragmentSendInfo & info,
+ NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections,
+ Uint32 messageSize){
+
+ ::releaseSections(signal->header.m_noOfSections, signal->m_sectionPtr);
+ signal->header.m_noOfSections = 0;
+
+ info.m_sectionPtr[0].m_linear.p = NULL;
+ info.m_sectionPtr[1].m_linear.p = NULL;
+ info.m_sectionPtr[2].m_linear.p = NULL;
+
+ Uint32 totalSize = 0;
+ switch(noOfSections){
+ case 3:
+ info.m_sectionPtr[2].m_linear = ptr[2];
+ totalSize += ptr[2].sz;
+ case 2:
+ info.m_sectionPtr[1].m_linear = ptr[1];
+ totalSize += ptr[1].sz;
+ case 1:
+ info.m_sectionPtr[0].m_linear = ptr[0];
+ totalSize += ptr[0].sz;
+ }
+
+ if(totalSize <= messageSize + SectionSegment::DataLength){
+ /**
+ * Send signal directly
+ */
+ sendSignal(rg, gsn, signal, length, jbuf, ptr, noOfSections);
+ info.m_status = FragmentSendInfo::SendComplete;
+
+ /**
+ * Indicate to sendLinearSignalFragment
+ * that we'r already done
+ */
+ return true;
+ }
+
+ /**
+ * Setup info object
+ */
+ info.m_status = FragmentSendInfo::SendNotComplete;
+ info.m_prio = (Uint8)jbuf;
+ info.m_gsn = gsn;
+ info.m_messageSize = messageSize;
+ info.m_fragInfo = 1;
+ info.m_fragmentId = c_fragmentIdCounter++;
+ info.m_nodeReceiverGroup = rg;
+ info.m_callback.m_callbackFunction = 0;
+
+ Ptr<SectionSegment> tmp;
+ if(!import(tmp, &signal->theData[0], length)){
+ ndbrequire(false);
+ return false;
+ }
+
+ info.m_theDataSection.p = &tmp.p->theData[0];
+ info.m_theDataSection.sz = length;
+ tmp.p->theData[length] = tmp.i;
+
+ sendNextLinearFragment(signal, info);
+
+ if(c_fragmentIdCounter == 0){
+ /**
+ * Fragment id 0 is invalid
+ */
+ c_fragmentIdCounter = 1;
+ }
+
+ return true;
+}
+
+void
+SimulatedBlock::sendNextLinearFragment(Signal* signal,
+ FragmentSendInfo & info){
+
+ /**
+ * Store "theData"
+ */
+ const Uint32 sigLen = info.m_theDataSection.sz;
+ memcpy(&signal->theData[0], info.m_theDataSection.p, 4 * sigLen);
+
+ Uint32 sz = 0;
+ Uint32 maxSz = info.m_messageSize;
+
+ Int32 secNo = 2;
+ Uint32 secCount = 0;
+ Uint32 * secNos = &signal->theData[sigLen];
+ LinearSectionPtr signalPtr[3];
+
+ enum { Unknown = 0, Full = 2 } loop = Unknown;
+ for(; secNo >= 0 && secCount < 3; secNo--){
+ Uint32 * ptrP = info.m_sectionPtr[secNo].m_linear.p;
+ if(ptrP == NULL)
+ continue;
+
+ info.m_sectionPtr[secNo].m_linear.p = NULL;
+ const Uint32 size = info.m_sectionPtr[secNo].m_linear.sz;
+
+ signalPtr[secCount].p = ptrP;
+ signalPtr[secCount].sz = size;
+ secNos[secCount] = secNo;
+ secCount++;
+
+ const Uint32 sizeLeft = maxSz - sz;
+ if(size <= sizeLeft){
+ /**
+ * The section fits
+ */
+ sz += size;
+ lsout(ndbout_c("section %d saved as %d", secNo, secCount-1));
+ continue;
+ }
+
+ const Uint32 overflow = size - sizeLeft; // > 0
+ if(overflow <= SectionSegment::DataLength){
+ /**
+ * Only one segment left to send
+ * send even if sizeLeft <= size
+ */
+ lsout(ndbout_c("section %d saved as %d but full over: %d",
+ secNo, secCount-1, overflow));
+ secNo--;
+ break;
+ }
+
+ // size >= 61
+ if(sizeLeft < SectionSegment::DataLength){
+ /**
+ * Less than one segment left (space)
+ * dont bother sending
+ */
+ secCount--;
+ info.m_sectionPtr[secNo].m_linear.p = ptrP;
+ loop = Full;
+ lsout(ndbout_c("section %d not saved", secNo));
+ break;
+ }
+
+ /**
+ * Split list
+ * 1) Find place to split
+ * 2) Rewrite header (the part that will be sent)
+ * 3) Write new header (for remaining part)
+ * 4) Store new header on FragmentSendInfo - record
+ */
+ Uint32 sum = sizeLeft;
+ sum /= SectionSegment::DataLength;
+ sum *= SectionSegment::DataLength;
+
+ /**
+ * Rewrite header w.r.t size
+ */
+ Uint32 prev = secCount - 1;
+ signalPtr[prev].sz = sum;
+
+ /**
+ * Write/store "new" header
+ */
+ info.m_sectionPtr[secNo].m_linear.p = ptrP + sum;
+ info.m_sectionPtr[secNo].m_linear.sz = size - sum;
+
+ loop = Full;
+ lsout(ndbout_c("section %d split into %d", secNo, prev));
+ break;
+ }
+
+ lsout(ndbout_c("loop: %d secNo: %d secCount: %d sz: %d",
+ loop, secNo, secCount, sz));
+
+ /**
+ * Store fragment id
+ */
+ secNos[secCount] = info.m_fragmentId;
+
+ Uint32 fragInfo = info.m_fragInfo;
+ info.m_fragInfo = 2;
+ switch(loop){
+ case Unknown:
+ if(secNo >= 0){
+ lsout(ndbout_c("Unknown - Full"));
+ /**
+ * Not finished
+ */
+ break;
+ }
+ // Fall through
+ lsout(ndbout_c("Unknown - Done"));
+ info.m_status = FragmentSendInfo::SendComplete;
+ ndbassert(fragInfo == 2);
+ fragInfo = 3;
+ case Full:
+ break;
+ }
+
+ signal->header.m_noOfSections = 0;
+ signal->header.m_fragmentInfo = fragInfo;
+
+ sendSignal(info.m_nodeReceiverGroup,
+ info.m_gsn,
+ signal,
+ sigLen + secCount + 1,
+ (JobBufferLevel)info.m_prio,
+ signalPtr,
+ secCount);
+
+ if(fragInfo == 3){
+ /**
+ * This is the last signal
+ */
+ g_sectionSegmentPool.release(info.m_theDataSection.p[sigLen]);
+ }
+}
+
+void
+SimulatedBlock::sendFragmentedSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ Callback & c,
+ Uint32 messageSize){
+ bool res = true;
+ Ptr<FragmentSendInfo> ptr;
+ res = c_segmentedFragmentSendList.seize(ptr);
+ ndbrequire(res);
+
+ res = sendFirstFragment(* ptr.p,
+ NodeReceiverGroup(ref),
+ gsn,
+ signal,
+ length,
+ jbuf,
+ messageSize);
+ ndbrequire(res);
+
+ if(ptr.p->m_status == FragmentSendInfo::SendComplete){
+ c_segmentedFragmentSendList.release(ptr);
+ if(c.m_callbackFunction != 0)
+ execute(signal, c, 0);
+ return;
+ }
+ ptr.p->m_callback = c;
+
+ if(!c_fragSenderRunning){
+ c_fragSenderRunning = true;
+ ContinueFragmented * sig = (ContinueFragmented*)signal->getDataPtrSend();
+ sig->line = __LINE__;
+ sendSignal(reference(), GSN_CONTINUE_FRAGMENTED, signal, 1, JBB);
+ }
+}
+
+void
+SimulatedBlock::sendFragmentedSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ Callback & c,
+ Uint32 messageSize){
+ bool res = true;
+ Ptr<FragmentSendInfo> ptr;
+ res = c_segmentedFragmentSendList.seize(ptr);
+ ndbrequire(res);
+
+ res = sendFirstFragment(* ptr.p,
+ rg,
+ gsn,
+ signal,
+ length,
+ jbuf,
+ messageSize);
+ ndbrequire(res);
+
+ if(ptr.p->m_status == FragmentSendInfo::SendComplete){
+ c_segmentedFragmentSendList.release(ptr);
+ if(c.m_callbackFunction != 0)
+ execute(signal, c, 0);
+ return;
+ }
+ ptr.p->m_callback = c;
+
+ if(!c_fragSenderRunning){
+ c_fragSenderRunning = true;
+ ContinueFragmented * sig = (ContinueFragmented*)signal->getDataPtrSend();
+ sig->line = __LINE__;
+ sendSignal(reference(), GSN_CONTINUE_FRAGMENTED, signal, 1, JBB);
+ }
+}
+
+SimulatedBlock::Callback SimulatedBlock::TheEmptyCallback = {0, 0};
+
+void
+SimulatedBlock::sendFragmentedSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections,
+ Callback & c,
+ Uint32 messageSize){
+ bool res = true;
+ Ptr<FragmentSendInfo> tmp;
+ res = c_linearFragmentSendList.seize(tmp);
+ ndbrequire(res);
+
+ res = sendFirstFragment(* tmp.p,
+ NodeReceiverGroup(ref),
+ gsn,
+ signal,
+ length,
+ jbuf,
+ ptr,
+ noOfSections,
+ messageSize);
+ ndbrequire(res);
+
+ if(tmp.p->m_status == FragmentSendInfo::SendComplete){
+ c_linearFragmentSendList.release(tmp);
+ if(c.m_callbackFunction != 0)
+ execute(signal, c, 0);
+ return;
+ }
+ tmp.p->m_callback = c;
+
+ if(!c_fragSenderRunning){
+ c_fragSenderRunning = true;
+ ContinueFragmented * sig = (ContinueFragmented*)signal->getDataPtrSend();
+ sig->line = __LINE__;
+ sendSignal(reference(), GSN_CONTINUE_FRAGMENTED, signal, 1, JBB);
+ }
+}
+
+void
+SimulatedBlock::sendFragmentedSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections,
+ Callback & c,
+ Uint32 messageSize){
+ bool res = true;
+ Ptr<FragmentSendInfo> tmp;
+ res = c_linearFragmentSendList.seize(tmp);
+ ndbrequire(res);
+
+ res = sendFirstFragment(* tmp.p,
+ rg,
+ gsn,
+ signal,
+ length,
+ jbuf,
+ ptr,
+ noOfSections,
+ messageSize);
+ ndbrequire(res);
+
+ if(tmp.p->m_status == FragmentSendInfo::SendComplete){
+ c_linearFragmentSendList.release(tmp);
+ if(c.m_callbackFunction != 0)
+ execute(signal, c, 0);
+ return;
+ }
+ tmp.p->m_callback = c;
+
+ if(!c_fragSenderRunning){
+ c_fragSenderRunning = true;
+ ContinueFragmented * sig = (ContinueFragmented*)signal->getDataPtrSend();
+ sig->line = __LINE__;
+ sendSignal(reference(), GSN_CONTINUE_FRAGMENTED, signal, 1, JBB);
+ }
+}
+
+NodeInfo &
+SimulatedBlock::setNodeInfo(NodeId nodeId) {
+ ndbrequire(nodeId > 0 && nodeId < MAX_NODES);
+ return globalData.m_nodeInfo[nodeId];
+}
+
+void
+SimulatedBlock::execUTIL_CREATE_LOCK_REF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_CREATE_LOCK_REF(signal);
+}
+
+void SimulatedBlock::execUTIL_CREATE_LOCK_CONF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_CREATE_LOCK_CONF(signal);
+}
+
+void SimulatedBlock::execUTIL_DESTORY_LOCK_REF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_DESTORY_LOCK_REF(signal);
+}
+
+void SimulatedBlock::execUTIL_DESTORY_LOCK_CONF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_DESTORY_LOCK_CONF(signal);
+}
+
+void SimulatedBlock::execUTIL_LOCK_REF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_LOCK_REF(signal);
+}
+
+void SimulatedBlock::execUTIL_LOCK_CONF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_LOCK_CONF(signal);
+}
+
+void SimulatedBlock::execUTIL_UNLOCK_REF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_UNLOCK_REF(signal);
+}
+
+void SimulatedBlock::execUTIL_UNLOCK_CONF(Signal* signal){
+ ljamEntry();
+ c_mutexMgr.execUTIL_UNLOCK_CONF(signal);
+}
+
+void
+SimulatedBlock::execREAD_CONFIG_REQ(Signal* signal){
+ const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
+
+ Uint32 ref = req->senderRef;
+ Uint32 senderData = req->senderData;
+
+ ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
+ conf->senderRef = reference();
+ conf->senderData = senderData;
+ sendSignal(ref, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+}
+
+void
+SimulatedBlock::ignoreMutexUnlockCallback(Signal* signal,
+ Uint32 ptrI, Uint32 retVal){
+ c_mutexMgr.release(ptrI);
+}
+
+void
+UpgradeStartup::installEXEC(SimulatedBlock* block){
+ SimulatedBlock::ExecFunction * a = block->theExecArray;
+ switch(block->number()){
+ case QMGR:
+ a[UpgradeStartup::GSN_CM_APPCHG] = &SimulatedBlock::execUPGRADE;
+ break;
+ case CNTR:
+ a[UpgradeStartup::GSN_CNTR_MASTERREF] = &SimulatedBlock::execUPGRADE;
+ a[UpgradeStartup::GSN_CNTR_MASTERCONF] = &SimulatedBlock::execUPGRADE;
+ break;
+ }
+}
+
+void
+SimulatedBlock::execUPGRADE(Signal* signal){
+ Uint32 gsn = signal->header.theVerId_signalNumber;
+ switch(gsn){
+ case UpgradeStartup::GSN_CM_APPCHG:
+ UpgradeStartup::execCM_APPCHG(* this, signal);
+ break;
+ case UpgradeStartup::GSN_CNTR_MASTERREF:
+ UpgradeStartup::execCNTR_MASTER_REPLY(* this, signal);
+ break;
+ case UpgradeStartup::GSN_CNTR_MASTERCONF:
+ UpgradeStartup::execCNTR_MASTER_REPLY(* this, signal);
+ break;
+ }
+}
+
+#ifdef VM_TRACE
+void
+SimulatedBlock::clear_global_variables(){
+ Ptr<void> ** tmp = m_global_variables;
+ while(* tmp != 0){
+ (* tmp)->i = RNIL;
+ (* tmp)->p = 0;
+ tmp++;
+ }
+}
+
+void
+SimulatedBlock::init_globals_list(void ** tmp, size_t cnt){
+ m_global_variables = new Ptr<void> * [cnt+1];
+ for(size_t i = 0; i<cnt; i++){
+ m_global_variables[i] = (Ptr<void>*)tmp[i];
+ }
+ m_global_variables[cnt] = 0;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/SimulatedBlock.hpp b/storage/ndb/src/kernel/vm/SimulatedBlock.hpp
new file mode 100644
index 00000000000..787d14ca5cb
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/SimulatedBlock.hpp
@@ -0,0 +1,756 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SIMULATEDBLOCK_H
+#define SIMULATEDBLOCK_H
+
+#include <NdbTick.h>
+#include <kernel_types.h>
+#include <ndb_version.h>
+
+#include "VMSignal.hpp"
+#include <RefConvert.hpp>
+#include <BlockNumbers.h>
+#include <GlobalSignalNumbers.h>
+#include "pc.hpp"
+#include <NodeInfo.hpp>
+#include <NodeState.hpp>
+#include "GlobalData.hpp"
+#include "LongSignal.hpp"
+#include <SignalLoggerManager.hpp>
+
+#include <Error.hpp>
+#include <ErrorReporter.hpp>
+#include <ErrorHandlingMacros.hpp>
+
+#include "DLList.hpp"
+#include "ArrayPool.hpp"
+#include "DLHashTable.hpp"
+#include "Callback.hpp"
+#include "SafeCounter.hpp"
+#include "MetaData.hpp"
+
+#include <mgmapi.h>
+#include <mgmapi_config_parameters.h>
+#include <mgmapi_config_parameters_debug.h>
+#include <kernel_config_parameters.h>
+#include <Configuration.hpp>
+
+#include <signaldata/ReadConfig.hpp>
+#include <signaldata/UpgradeStartup.hpp>
+
+
+/**
+ * Something for filesystem access
+ */
+struct NewBaseAddrBits /* 32 bits */
+{
+ unsigned int q : 4; /* Highest index - 2log */
+ /* Strings are treated as 16 bit indexed */
+ /* variables with the number of characters in */
+ /* index 0, byte 0 */
+ unsigned int v : 3; /* Size in bits - 2log */
+ unsigned int unused : 25 ;
+};
+
+typedef struct NewVar
+{
+ Uint32 * WA;
+ Uint32 nrr;
+ Uint32 ClusterSize; /* Real Cluster size */
+ NewBaseAddrBits bits;
+} NewVARIABLE; /* 128 bits */
+
+class SimulatedBlock {
+ friend class SafeCounter;
+ friend class SafeCounterManager;
+ friend struct UpgradeStartup;
+public:
+ friend class BlockComponent;
+ virtual ~SimulatedBlock();
+
+protected:
+ /**
+ * Constructor
+ */
+ SimulatedBlock(BlockNumber blockNumber,
+ const class Configuration & theConfiguration);
+
+ /**********************************************************
+ * Handling of execFunctions
+ */
+ typedef void (SimulatedBlock::* ExecFunction)(Signal* signal);
+ void addRecSignalImpl(GlobalSignalNumber g, ExecFunction fun, bool f =false);
+ void installSimulatedBlockFunctions();
+ ExecFunction theExecArray[MAX_GSN+1];
+public:
+ /**
+ *
+ */
+ inline void executeFunction(GlobalSignalNumber gsn, Signal* signal);
+public:
+ typedef void (SimulatedBlock::* CallbackFunction)(class Signal*,
+ Uint32 callbackData,
+ Uint32 returnCode);
+ struct Callback {
+ CallbackFunction m_callbackFunction;
+ Uint32 m_callbackData;
+ };
+protected:
+ static Callback TheEmptyCallback;
+ void execute(Signal* signal, Callback & c, Uint32 returnCode);
+
+
+ /**********************************************************
+ * Send signal - dialects
+ */
+
+ void sendSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf ) const ;
+
+ void sendSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf ) const ;
+
+ void sendSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections) const ;
+
+ void sendSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections) const ;
+
+ // Send multiple signal with delay. In this VM the jobbufffer level has
+ // no effect on on delayed signals
+ //
+ void sendSignalWithDelay(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 delayInMilliSeconds,
+ Uint32 length) const ;
+
+ void EXECUTE_DIRECT(Uint32 block,
+ Uint32 gsn,
+ Signal* signal,
+ Uint32 len);
+
+ class SectionSegmentPool& getSectionSegmentPool();
+ void releaseSections(Signal* signal);
+
+ /**********************************************************
+ * Fragmented signals
+ */
+
+ /**
+ * Assemble fragments
+ *
+ * @return true if all fragments has arrived
+ * false otherwise
+ */
+ bool assembleFragments(Signal * signal);
+
+ void sendFragmentedSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ Callback & = TheEmptyCallback,
+ Uint32 messageSize = 240);
+
+ void sendFragmentedSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ Callback & = TheEmptyCallback,
+ Uint32 messageSize = 240);
+
+ void sendFragmentedSignal(BlockReference ref,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections,
+ Callback &,
+ Uint32 messageSize = 240);
+
+ void sendFragmentedSignal(NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections,
+ Callback &,
+ Uint32 messageSize = 240);
+
+ /**********************************************************
+ * Fragmented signals structures
+ */
+
+ /**
+ * Struct used when assembling fragmented long signals at receiver side
+ */
+ struct FragmentInfo {
+ FragmentInfo(Uint32 fragId, Uint32 sender);
+
+ Uint32 m_senderRef;
+ Uint32 m_fragmentId;
+ Uint32 m_sectionPtrI[3];
+ union {
+ Uint32 nextPool;
+ Uint32 nextHash;
+ };
+ Uint32 prevHash;
+
+ inline bool equal(FragmentInfo & p) const {
+ return m_senderRef == p.m_senderRef && m_fragmentId == p.m_fragmentId;
+ }
+
+ inline Uint32 hashValue() const {
+ return m_senderRef + m_fragmentId ;
+ }
+ }; // sizeof() = 32 bytes
+
+ /**
+ * Struct used when sending fragmented signals
+ */
+ struct FragmentSendInfo {
+ FragmentSendInfo();
+
+ enum Status {
+ SendNotComplete = 0,
+ SendComplete = 1
+ };
+ Uint8 m_status;
+ Uint8 m_prio;
+ Uint16 m_fragInfo;
+ Uint16 m_gsn;
+ Uint16 m_messageSize; // Size of each fragment
+ Uint32 m_fragmentId;
+ union {
+ Ptr<struct SectionSegment> m_segmented;
+ LinearSectionPtr m_linear;
+ } m_sectionPtr[3];
+ LinearSectionPtr m_theDataSection;
+ NodeReceiverGroup m_nodeReceiverGroup; // 3
+ Callback m_callback;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+
+ /**
+ * setupFragmentSendInfo
+ * Setup a struct to be used with sendSignalFragment
+ * Used by sendFragmentedSignal
+ */
+ bool sendFirstFragment(FragmentSendInfo & info,
+ NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections,
+ Uint32 messageSize = 240);
+
+ bool sendFirstFragment(FragmentSendInfo & info,
+ NodeReceiverGroup rg,
+ GlobalSignalNumber gsn,
+ Signal* signal,
+ Uint32 length,
+ JobBufferLevel jbuf,
+ Uint32 messageSize = 240);
+
+ /**
+ * Send signal fragment
+ *
+ * @see sendFragmentedSignal
+ */
+ void sendNextSegmentedFragment(Signal* signal, FragmentSendInfo & info);
+
+ /**
+ * Send signal fragment
+ *
+ * @see sendFragmentedSignal
+ */
+ void sendNextLinearFragment(Signal* signal, FragmentSendInfo & info);
+
+ BlockNumber number() const;
+ BlockReference reference() const;
+ NodeId getOwnNodeId() const;
+
+ /**
+ * Refresh Watch Dog in initialising code
+ *
+ */
+ void refresh_watch_dog();
+
+ /**
+ * Prog error
+ * This function should be called when this node should be shutdown
+ * If the cause of the shutdown is known use extradata to add an
+ * errormessage describing the problem
+ */
+ void progError(int line, int err_code, const char* extradata=NULL) const ;
+private:
+ void signal_error(Uint32, Uint32, Uint32, const char*, int) const ;
+ const NodeId theNodeId;
+ const BlockNumber theNumber;
+ const BlockReference theReference;
+
+protected:
+ NewVARIABLE* allocateBat(int batSize);
+ void freeBat();
+ static const NewVARIABLE* getBat (BlockNumber blockNo);
+ static Uint16 getBatSize(BlockNumber blockNo);
+
+ static BlockReference calcTcBlockRef (NodeId aNode);
+ static BlockReference calcLqhBlockRef (NodeId aNode);
+ static BlockReference calcAccBlockRef (NodeId aNode);
+ static BlockReference calcTupBlockRef (NodeId aNode);
+ static BlockReference calcTuxBlockRef (NodeId aNode);
+ static BlockReference calcDihBlockRef (NodeId aNode);
+ static BlockReference calcQmgrBlockRef (NodeId aNode);
+ static BlockReference calcDictBlockRef (NodeId aNode);
+ static BlockReference calcNdbCntrBlockRef (NodeId aNode);
+ static BlockReference calcTrixBlockRef (NodeId aNode);
+ static BlockReference calcBackupBlockRef (NodeId aNode);
+ static BlockReference calcSumaBlockRef (NodeId aNode);
+
+ static BlockReference calcApiClusterMgrBlockRef (NodeId aNode);
+
+ /**
+ * allocRecord
+ * Allocates memory for the datastructures where ndb keeps the data
+ *
+ */
+ void* allocRecord(const char * type, size_t s, size_t n, bool clear = true);
+
+ /**
+ * Deallocate record
+ *
+ * NOTE: Also resets pointer
+ */
+ void deallocRecord(void **, const char * type, size_t s, size_t n);
+
+ /**
+ * General info event (sent to cluster log)
+ */
+ void infoEvent(const char * msg, ...) const ;
+ void warningEvent(const char * msg, ...) const ;
+
+ /**
+ * The configuration object
+ */
+ const class Configuration & theConfiguration;
+
+ /**
+ * Get node state
+ */
+ const NodeState & getNodeState() const;
+
+ /**
+ * Get node info
+ */
+ const NodeInfo & getNodeInfo(NodeId nodeId) const;
+ NodeInfo & setNodeInfo(NodeId);
+
+private:
+ NewVARIABLE* NewVarRef; /* New Base Address Table for block */
+ Uint16 theBATSize; /* # entries in BAT */
+
+ /**
+ * Node state
+ */
+ NodeState theNodeState;
+ void execNDB_TAMPER(Signal * signal);
+ void execNODE_STATE_REP(Signal* signal);
+ void execCHANGE_NODE_STATE_REQ(Signal* signal);
+
+ void execSIGNAL_DROPPED_REP(Signal* signal);
+ void execCONTINUE_FRAGMENTED(Signal* signal);
+
+ Uint32 c_fragmentIdCounter;
+ ArrayPool<FragmentInfo> c_fragmentInfoPool;
+ DLHashTable<FragmentInfo> c_fragmentInfoHash;
+
+ bool c_fragSenderRunning;
+ ArrayPool<FragmentSendInfo> c_fragmentSendPool;
+ DLList<FragmentSendInfo> c_linearFragmentSendList;
+ DLList<FragmentSendInfo> c_segmentedFragmentSendList;
+
+public:
+ class MutexManager {
+ friend class Mutex;
+ friend class SimulatedBlock;
+ friend class DbUtil;
+ public:
+ MutexManager(class SimulatedBlock &);
+
+ bool setSize(Uint32 maxNoOfActiveMutexes);
+ Uint32 getSize() const ; // Get maxNoOfActiveMutexes
+
+ private:
+ /**
+ * core interface
+ */
+ struct ActiveMutex {
+ Uint32 m_gsn; // state
+ Uint32 m_mutexId;
+ Uint32 m_mutexKey;
+ Callback m_callback;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+ typedef Ptr<ActiveMutex> ActiveMutexPtr;
+
+ bool seize(ActiveMutexPtr& ptr);
+ void release(Uint32 activeMutexPtrI);
+
+ void getPtr(ActiveMutexPtr& ptr);
+
+ void create(Signal*, ActiveMutexPtr&);
+ void destroy(Signal*, ActiveMutexPtr&);
+ void lock(Signal*, ActiveMutexPtr&);
+ void trylock(Signal*, ActiveMutexPtr&);
+ void unlock(Signal*, ActiveMutexPtr&);
+
+ private:
+ void execUTIL_CREATE_LOCK_REF(Signal* signal);
+ void execUTIL_CREATE_LOCK_CONF(Signal* signal);
+ void execUTIL_DESTORY_LOCK_REF(Signal* signal);
+ void execUTIL_DESTORY_LOCK_CONF(Signal* signal);
+ void execUTIL_LOCK_REF(Signal* signal);
+ void execUTIL_LOCK_CONF(Signal* signal);
+ void execUTIL_UNLOCK_REF(Signal* signal);
+ void execUTIL_UNLOCK_CONF(Signal* signal);
+
+ SimulatedBlock & m_block;
+ ArrayPool<ActiveMutex> m_mutexPool;
+ DLList<ActiveMutex> m_activeMutexes;
+
+ BlockReference reference() const;
+ void progError(int line, int err_code, const char* extra = 0);
+ };
+
+ friend class MutexManager;
+ MutexManager c_mutexMgr;
+
+ void ignoreMutexUnlockCallback(Signal* signal, Uint32 ptrI, Uint32 retVal);
+
+ SafeCounterManager c_counterMgr;
+private:
+ void execUTIL_CREATE_LOCK_REF(Signal* signal);
+ void execUTIL_CREATE_LOCK_CONF(Signal* signal);
+ void execUTIL_DESTORY_LOCK_REF(Signal* signal);
+ void execUTIL_DESTORY_LOCK_CONF(Signal* signal);
+ void execUTIL_LOCK_REF(Signal* signal);
+ void execUTIL_LOCK_CONF(Signal* signal);
+ void execUTIL_UNLOCK_REF(Signal* signal);
+ void execUTIL_UNLOCK_CONF(Signal* signal);
+
+ void execREAD_CONFIG_REQ(Signal* signal);
+protected:
+ void execUPGRADE(Signal* signal);
+
+ // Variable for storing inserted errors, see pc.H
+ ERROR_INSERT_VARIABLE;
+
+private:
+ // Metadata common part shared by block instances
+ MetaData::Common* c_ptrMetaDataCommon;
+public:
+ void setMetaDataCommon(MetaData::Common* ptr) { c_ptrMetaDataCommon = ptr; }
+ MetaData::Common* getMetaDataCommon() { return c_ptrMetaDataCommon; }
+
+#ifdef VM_TRACE_TIME
+public:
+ void clearTimes();
+ void printTimes(FILE * output);
+ void addTime(Uint32 gsn, Uint64 time);
+ void subTime(Uint32 gsn, Uint64 time);
+ struct TimeTrace {
+ Uint32 cnt;
+ Uint64 sum, sub;
+ } m_timeTrace[MAX_GSN+1];
+ Uint32 m_currentGsn;
+#endif
+
+#ifdef VM_TRACE
+ Ptr<void> **m_global_variables;
+ void clear_global_variables();
+ void init_globals_list(void ** tmp, size_t cnt);
+#endif
+};
+
+inline
+void
+SimulatedBlock::executeFunction(GlobalSignalNumber gsn, Signal* signal){
+ ExecFunction f = theExecArray[gsn];
+ if(gsn <= MAX_GSN && f != 0){
+#ifdef VM_TRACE
+ clear_global_variables();
+#endif
+ (this->*f)(signal);
+ return;
+ }
+
+ /**
+ * This point only passed if an error has occurred
+ */
+ char errorMsg[255];
+ if (!(gsn <= MAX_GSN)) {
+ BaseString::snprintf(errorMsg, 255, "Illegal signal received (GSN %d too high)", gsn);
+ ERROR_SET(fatal, ERR_ERROR_PRGERR, errorMsg, errorMsg);
+ }
+ if (!(theExecArray[gsn] != 0)) {
+ BaseString::snprintf(errorMsg, 255, "Illegal signal received (GSN %d not added)", gsn);
+ ERROR_SET(fatal, ERR_ERROR_PRGERR, errorMsg, errorMsg);
+ }
+ ndbrequire(false);
+}
+
+inline
+void
+SimulatedBlock::execute(Signal* signal, Callback & c, Uint32 returnCode){
+ CallbackFunction fun = c.m_callbackFunction;
+ ndbrequire(fun != 0);
+ c.m_callbackFunction = NULL;
+ (this->*fun)(signal, c.m_callbackData, returnCode);
+}
+
+inline
+BlockNumber
+SimulatedBlock::number() const {
+ return theNumber;
+}
+
+inline
+BlockReference
+SimulatedBlock::reference() const {
+ return theReference;
+}
+
+inline
+NodeId
+SimulatedBlock::getOwnNodeId() const {
+ return theNodeId;
+}
+
+inline
+BlockReference
+SimulatedBlock::calcTcBlockRef (NodeId aNodeId){
+ return numberToRef(DBTC, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcLqhBlockRef (NodeId aNodeId){
+return numberToRef(DBLQH, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcAccBlockRef (NodeId aNodeId){
+ return numberToRef(DBACC, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcTupBlockRef (NodeId aNodeId){
+ return numberToRef(DBTUP, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcTuxBlockRef (NodeId aNodeId){
+ return numberToRef(DBTUX, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcDihBlockRef (NodeId aNodeId){
+ return numberToRef(DBDIH, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcDictBlockRef (NodeId aNodeId){
+ return numberToRef(DBDICT, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcQmgrBlockRef (NodeId aNodeId){
+ return numberToRef(QMGR, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcNdbCntrBlockRef (NodeId aNodeId){
+ return numberToRef(NDBCNTR, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcTrixBlockRef (NodeId aNodeId){
+ return numberToRef(TRIX, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcBackupBlockRef (NodeId aNodeId){
+ return numberToRef(BACKUP, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcSumaBlockRef (NodeId aNodeId){
+ return numberToRef(SUMA, aNodeId);
+}
+
+inline
+BlockReference
+SimulatedBlock::calcApiClusterMgrBlockRef (NodeId aNodeId){
+ return numberToRef(API_CLUSTERMGR, aNodeId);
+}
+
+inline
+const NodeState &
+SimulatedBlock::getNodeState() const {
+ return theNodeState;
+}
+
+inline
+const NodeInfo &
+SimulatedBlock::getNodeInfo(NodeId nodeId) const {
+ ndbrequire(nodeId > 0 && nodeId < MAX_NODES);
+ return globalData.m_nodeInfo[nodeId];
+}
+
+inline
+void
+SimulatedBlock::EXECUTE_DIRECT(Uint32 block,
+ Uint32 gsn,
+ Signal* signal,
+ Uint32 len){
+ signal->setLength(len);
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = block;
+ signal->header.theSendersBlockRef = reference();
+ globalSignalLoggers.executeDirect(signal->header,
+ 0, // in
+ &signal->theData[0],
+ globalData.ownId);
+ }
+#endif
+ SimulatedBlock* b = globalData.getBlock(block);
+#ifdef VM_TRACE_TIME
+ Uint32 us1, us2;
+ Uint64 ms1, ms2;
+ NdbTick_CurrentMicrosecond(&ms1, &us1);
+ Uint32 tGsn = m_currentGsn;
+ b->m_currentGsn = gsn;
+#endif
+ b->executeFunction(gsn, signal);
+#ifdef VM_TRACE_TIME
+ NdbTick_CurrentMicrosecond(&ms2, &us2);
+ Uint64 diff = ms2;
+ diff -= ms1;
+ diff *= 1000000;
+ diff += us2;
+ diff -= us1;
+ b->addTime(gsn, diff);
+ m_currentGsn = tGsn;
+ subTime(tGsn, diff);
+#endif
+#ifdef VM_TRACE
+ if(globalData.testOn){
+ signal->header.theVerId_signalNumber = gsn;
+ signal->header.theReceiversBlockNumber = block;
+ signal->header.theSendersBlockRef = reference();
+ globalSignalLoggers.executeDirect(signal->header,
+ 1, // out
+ &signal->theData[0],
+ globalData.ownId);
+ }
+#endif
+}
+
+#ifdef VM_TRACE_TIME
+inline
+void
+SimulatedBlock::addTime(Uint32 gsn, Uint64 time){
+ m_timeTrace[gsn].cnt ++;
+ m_timeTrace[gsn].sum += time;
+}
+
+inline
+void
+SimulatedBlock::subTime(Uint32 gsn, Uint64 time){
+ m_timeTrace[gsn].sub += time;
+}
+#endif
+
+/**
+ * Defines for backward compatiblility
+ */
+
+#define BLOCK_DEFINES(BLOCK) \
+ typedef void (BLOCK::* ExecSignalLocal) (Signal* signal); \
+ typedef void (BLOCK::* BlockCallback)(Signal*, Uint32 callb, Uint32 retCode); \
+ inline CallbackFunction safe_cast(BlockCallback f){ \
+ return static_cast<CallbackFunction>(f); \
+ } \
+public:\
+private: \
+ void addRecSignal(GlobalSignalNumber gsn, ExecSignalLocal f, bool force = false)
+
+#define BLOCK_CONSTRUCTOR(BLOCK)
+
+#define BLOCK_FUNCTIONS(BLOCK) \
+void \
+BLOCK::addRecSignal(GlobalSignalNumber gsn, ExecSignalLocal f, bool force){ \
+ addRecSignalImpl(gsn, (ExecFunction)f, force);\
+}
+
+#include "Mutex.hpp"
+
+#endif
+
diff --git a/storage/ndb/src/kernel/vm/ThreadConfig.cpp b/storage/ndb/src/kernel/vm/ThreadConfig.cpp
new file mode 100644
index 00000000000..76fcc4ba84f
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ThreadConfig.cpp
@@ -0,0 +1,204 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ThreadConfig.hpp"
+#include "Emulator.hpp"
+#include "GlobalData.hpp"
+#include "TimeQueue.hpp"
+#include "TransporterRegistry.hpp"
+#include "FastScheduler.hpp"
+#include "pc.hpp"
+
+#include <GlobalSignalNumbers.h>
+#include <BlockNumbers.h>
+
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+
+#include <signaldata/StartOrd.hpp>
+
+ThreadConfig::ThreadConfig()
+{
+}
+
+ThreadConfig::~ThreadConfig()
+{
+}
+
+/**
+ * For each millisecond that has passed since this function was last called:
+ * Scan the job buffer and increment the internalMillisecCounter
+ * with 1 to keep track of where we are
+ */
+inline
+void
+ThreadConfig::scanTimeQueue()
+{
+ unsigned int maxCounter;
+ Uint64 currMilliSecond;
+ maxCounter = 0;
+ currMilliSecond = NdbTick_CurrentMillisecond();
+ if (currMilliSecond < globalData.internalMillisecCounter) {
+//--------------------------------------------------------------------
+// This could occur around 2036 or if the operator decides to change
+// time backwards. We cannot know how long time has past since last
+// time and we make a best try with 0 milliseconds.
+//--------------------------------------------------------------------
+#ifdef VM_TRACE
+ ndbout << "Time moved backwards with ";
+ ndbout << (globalData.internalMillisecCounter - currMilliSecond);
+ ndbout << " milliseconds" << endl;
+#endif
+ globalData.internalMillisecCounter = currMilliSecond;
+ }//if
+ if (currMilliSecond > (globalData.internalMillisecCounter + 1500)) {
+//--------------------------------------------------------------------
+// Time has moved forward more than a second. Either it could happen
+// if operator changed the time or if the OS has misbehaved badly.
+// We set the new time to one second from the past.
+//--------------------------------------------------------------------
+#ifdef VM_TRACE
+ ndbout << "Time moved forward with ";
+ ndbout << (currMilliSecond - globalData.internalMillisecCounter);
+ ndbout << " milliseconds" << endl;
+#endif
+ globalData.internalMillisecCounter = currMilliSecond - 1000;
+ }//if
+ while (((currMilliSecond - globalData.internalMillisecCounter) > 0) &&
+ (maxCounter < 20)){
+ globalData.internalMillisecCounter++;
+ maxCounter++;
+ globalTimeQueue.scanTable();
+ }//while
+}//ThreadConfig::scanTimeQueue()
+
+
+//--------------------------------------------------------------------
+// ipControlLoop -- The main loop of ndb.
+// Handles the scheduling of signal execution and input/output
+// One lap in the loop should take approximately 10 milli seconds
+// If the jobbuffer is empty and the laptime is less than 10 milliseconds
+// at the end of the loop
+// the TransporterRegistry is called in order to sleep on the IO ports
+// waiting for another incoming signal to wake us up.
+// The timeout value in this call is calculated as (10 ms - laptime)
+// This would make ndb use less cpu while improving response time.
+//--------------------------------------------------------------------
+void ThreadConfig::ipControlLoop()
+{
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+//--------------------------------------------------------------------
+// To let the Cello Watchdog do it's work NDB must sleep a short
+// period every 10 minutes. If this is not done, the watchdog will
+// reboot the board NDB is running on when the load is high.
+//--------------------------------------------------------------------
+ int loopCounter = 0;
+#endif
+
+//--------------------------------------------------------------------
+// initialise the counter that keeps track of the current millisecond
+//--------------------------------------------------------------------
+ globalData.internalMillisecCounter = NdbTick_CurrentMillisecond();
+ Uint32 i = 0;
+ while (globalData.theRestartFlag != perform_stop) {
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ loopCounter++;
+ if(loopCounter > 1000){
+//--------------------------------------------------------------------
+// This is done to allow OSE do a context switch to let the watchdog
+// do it's stuff.
+//--------------------------------------------------------------------
+ NdbSleep_MilliSleep(1);
+ loopCounter = 0;
+ }
+#endif
+
+ Uint32 timeOutMillis = 0;
+ if (LEVEL_IDLE == globalData.highestAvailablePrio) {
+//--------------------------------------------------------------------
+// The buffers are empty, we need to wait for a while until we continue.
+// We cannot wait forever since we can also have timed events.
+//--------------------------------------------------------------------
+//--------------------------------------------------------------------
+// Set the time we will sleep on the sockets before waking up
+// unconditionally to 10 ms. Will never sleep more than 10 milliseconds
+// on a socket.
+//--------------------------------------------------------------------
+ timeOutMillis = 10;
+ }//if
+//--------------------------------------------------------------------
+// Now it is time to check all interfaces. We will send all buffers
+// plus checking for any received messages.
+//--------------------------------------------------------------------
+ if (i++ >= 20) {
+ globalTransporterRegistry.update_connections();
+ globalData.incrementWatchDogCounter(5);
+ i = 0;
+ }//if
+
+ globalData.incrementWatchDogCounter(6);
+ globalTransporterRegistry.performSend();
+
+ globalData.incrementWatchDogCounter(7);
+ if (globalTransporterRegistry.pollReceive(timeOutMillis)) {
+ globalData.incrementWatchDogCounter(8);
+ globalTransporterRegistry.performReceive();
+ }
+
+//--------------------------------------------------------------------
+// We scan the time queue to see if there are any timed signals that
+// is now ready to be executed.
+//--------------------------------------------------------------------
+ globalData.incrementWatchDogCounter(2);
+ scanTimeQueue();
+
+//--------------------------------------------------------------------
+// This is where the actual execution of signals occur. We execute
+// until all buffers are empty or until we have executed 2048 signals.
+//--------------------------------------------------------------------
+ globalScheduler.doJob();
+ }//while
+
+ globalData.incrementWatchDogCounter(6);
+ globalTransporterRegistry.performSend();
+
+}//ThreadConfig::ipControlLoop()
+
+int
+ThreadConfig::doStart(NodeState::StartLevel startLevel){
+
+ SignalHeader sh;
+ memset(&sh, 0, sizeof(SignalHeader));
+
+ sh.theVerId_signalNumber = GSN_START_ORD;
+ sh.theReceiversBlockNumber = CMVMI;
+ sh.theSendersBlockRef = 0;
+ sh.theTrace = 0;
+ sh.theSignalId = 0;
+ sh.theLength = StartOrd::SignalLength;
+
+ Uint32 theData[25];
+ StartOrd * const startOrd = (StartOrd *)&theData[0];
+ startOrd->restartInfo = 0;
+
+ Uint32 secPtrI[3];
+ globalScheduler.execute(&sh, JBA, theData, secPtrI);
+ return 0;
+}
+
diff --git a/storage/ndb/src/kernel/vm/ThreadConfig.hpp b/storage/ndb/src/kernel/vm/ThreadConfig.hpp
new file mode 100644
index 00000000000..91c2cafe0e0
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/ThreadConfig.hpp
@@ -0,0 +1,39 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ThreadConfig_H
+#define ThreadConfig_H
+
+#include <kernel_types.h>
+#include <ErrorReporter.hpp>
+#include <NodeState.hpp>
+
+class IPCConfig;
+
+class ThreadConfig
+{
+public:
+ ThreadConfig();
+ ~ThreadConfig();
+
+ void ipControlLoop();
+
+ int doStart(NodeState::StartLevel startLevel);
+private:
+
+ void scanTimeQueue();
+};
+#endif // ThreadConfig_H
diff --git a/storage/ndb/src/kernel/vm/TimeQueue.cpp b/storage/ndb/src/kernel/vm/TimeQueue.cpp
new file mode 100644
index 00000000000..56988c2e3da
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/TimeQueue.cpp
@@ -0,0 +1,209 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "TimeQueue.hpp"
+#include <ErrorHandlingMacros.hpp>
+#include <GlobalData.hpp>
+#include <FastScheduler.hpp>
+#include <VMSignal.hpp>
+#include <Error.hpp>
+
+static const int MAX_TIME_QUEUE_VALUE = 32000;
+
+TimeQueue::TimeQueue()
+{
+ clear();
+}
+
+TimeQueue::~TimeQueue()
+{
+}
+
+void
+TimeQueue::clear()
+{
+ globalData.theNextTimerJob = 65535;
+ globalData.theCurrentTimer = 0;
+ globalData.theShortTQIndex = 0;
+ globalData.theLongTQIndex = 0;
+ for (int i = 0; i < MAX_NO_OF_TQ; i++)
+ theFreeIndex[i] = i+1;
+ theFreeIndex[MAX_NO_OF_TQ - 1] = NULL_TQ_ENTRY;
+ globalData.theFirstFreeTQIndex = 0;
+}
+
+void
+TimeQueue::insert(Signal* signal, BlockNumber bnr,
+ GlobalSignalNumber gsn, Uint32 delayTime)
+{
+ if (delayTime == 0)
+ delayTime = 1;
+ register Uint32 regCurrentTime = globalData.theCurrentTimer;
+ register Uint32 i;
+ register Uint32 regSave;
+ register TimerEntry newEntry;
+
+ newEntry.time_struct.delay_time = regCurrentTime + delayTime;
+ newEntry.time_struct.job_index = getIndex();
+ regSave = newEntry.copy_struct;
+
+ globalScheduler.insertTimeQueue(signal, bnr, gsn,
+ newEntry.time_struct.job_index);
+
+ if (newEntry.time_struct.delay_time < globalData.theNextTimerJob)
+ globalData.theNextTimerJob = newEntry.time_struct.delay_time;
+ if (delayTime < 100){
+ register Uint32 regShortIndex = globalData.theShortTQIndex;
+ if (regShortIndex == 0){
+ theShortQueue[0].copy_struct = newEntry.copy_struct;
+ } else if (regShortIndex >= MAX_NO_OF_SHORT_TQ - 1) {
+ ERROR_SET(ecError, ERROR_TIME_QUEUE_SHORT,
+ "Too many in Short Time Queue", "TimeQueue.C" );
+ } else {
+ for (i = 0; i < regShortIndex; i++) {
+ if (theShortQueue[i].time_struct.delay_time >
+ newEntry.time_struct.delay_time) {
+
+ regSave = theShortQueue[i].copy_struct;
+ theShortQueue[i].copy_struct = newEntry.copy_struct;
+ break;
+ }
+ }
+ if (i == regShortIndex) {
+ theShortQueue[regShortIndex].copy_struct = regSave;
+ } else {
+ for (i++; i < regShortIndex; i++) {
+ register Uint32 regTmp = theShortQueue[i].copy_struct;
+ theShortQueue[i].copy_struct = regSave;
+ regSave = regTmp;
+ }
+ theShortQueue[regShortIndex].copy_struct = regSave;
+ }
+ }
+ globalData.theShortTQIndex = regShortIndex + 1;
+ } else if (delayTime <= (unsigned)MAX_TIME_QUEUE_VALUE) {
+ register Uint32 regLongIndex = globalData.theLongTQIndex;
+ if (regLongIndex == 0) {
+ theLongQueue[0].copy_struct = newEntry.copy_struct;
+ } else if (regLongIndex >= MAX_NO_OF_LONG_TQ - 1) {
+ ERROR_SET(ecError, ERROR_TIME_QUEUE_LONG,
+ "Too many in Long Time Queue", "TimeQueue.C" );
+ } else {
+ for (i = 0; i < regLongIndex; i++) {
+ if (theLongQueue[i].time_struct.delay_time >
+ newEntry.time_struct.delay_time) {
+
+ regSave = theLongQueue[i].copy_struct;
+ theLongQueue[i].copy_struct = newEntry.copy_struct;
+ break;
+ }
+ }
+ if (i == regLongIndex) {
+ theLongQueue[regLongIndex].copy_struct = regSave;
+ } else {
+ for (i++; i < regLongIndex; i++) {
+ register Uint32 regTmp = theLongQueue[i].copy_struct;
+ theLongQueue[i].copy_struct = regSave;
+ regSave = regTmp;
+ }
+ theLongQueue[regLongIndex].copy_struct = regSave;
+ }
+ }
+ globalData.theLongTQIndex = regLongIndex + 1;
+ } else {
+ ERROR_SET(ecError, ERROR_TIME_QUEUE_DELAY,
+ "Too long delay for Time Queue", "TimeQueue.C" );
+ }
+}
+
+// executes the expired signals;
+void
+TimeQueue::scanTable()
+{
+ register Uint32 i, j;
+
+ globalData.theCurrentTimer++;
+ if (globalData.theCurrentTimer == 32000)
+ recount_timers();
+ if (globalData.theNextTimerJob > globalData.theCurrentTimer)
+ return;
+ globalData.theNextTimerJob = 65535; // If no more timer jobs
+ for (i = 0; i < globalData.theShortTQIndex; i++) {
+ if (theShortQueue[i].time_struct.delay_time > globalData.theCurrentTimer){
+ break;
+ } else {
+ releaseIndex((Uint32)theShortQueue[i].time_struct.job_index);
+ globalScheduler.scheduleTimeQueue(theShortQueue[i].time_struct.job_index);
+ }
+ }
+ if (i > 0) {
+ for (j = i; j < globalData.theShortTQIndex; j++)
+ theShortQueue[j - i].copy_struct = theShortQueue[j].copy_struct;
+ globalData.theShortTQIndex -= i;
+ }
+ if (globalData.theShortTQIndex != 0) // If not empty
+ globalData.theNextTimerJob = theShortQueue[0].time_struct.delay_time;
+ for (i = 0; i < globalData.theLongTQIndex; i++) {
+ if (theLongQueue[i].time_struct.delay_time > globalData.theCurrentTimer) {
+ break;
+ } else {
+ releaseIndex((Uint32)theLongQueue[i].time_struct.job_index);
+ globalScheduler.scheduleTimeQueue(theLongQueue[i].time_struct.job_index);
+ }
+ }
+ if (i > 0) {
+ for (j = i; j < globalData.theLongTQIndex; j++)
+ theLongQueue[j - i].copy_struct = theLongQueue[j].copy_struct;
+ globalData.theLongTQIndex -= i;
+ }
+ if (globalData.theLongTQIndex != 0) // If not empty
+ if (globalData.theNextTimerJob > theLongQueue[0].time_struct.delay_time)
+ globalData.theNextTimerJob = theLongQueue[0].time_struct.delay_time;
+}
+
+void
+TimeQueue::recount_timers()
+{
+ Uint32 i;
+
+ globalData.theCurrentTimer = 0;
+ globalData.theNextTimerJob -= 32000;
+
+ for (i = 0; i < globalData.theShortTQIndex; i++)
+ theShortQueue[i].time_struct.delay_time -= 32000;
+ for (i = 0; i < globalData.theLongTQIndex; i++)
+ theLongQueue[i].time_struct.delay_time -= 32000;
+}
+
+Uint32
+TimeQueue::getIndex()
+{
+ Uint32 retValue = globalData.theFirstFreeTQIndex;
+ globalData.theFirstFreeTQIndex = (Uint32)theFreeIndex[retValue];
+ if (retValue >= MAX_NO_OF_TQ)
+ ERROR_SET(fatal, ERROR_TIME_QUEUE_INDEX,
+ "Index out of range", "TimeQueue.C" );
+ return retValue;
+}
+
+void
+TimeQueue::releaseIndex(Uint32 aIndex)
+{
+ theFreeIndex[aIndex] = globalData.theFirstFreeTQIndex;
+ globalData.theFirstFreeTQIndex = aIndex;
+}
+
+
diff --git a/storage/ndb/src/kernel/vm/TimeQueue.hpp b/storage/ndb/src/kernel/vm/TimeQueue.hpp
new file mode 100644
index 00000000000..1203ace10f5
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/TimeQueue.hpp
@@ -0,0 +1,62 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TimeQueue_H
+#define TimeQueue_H
+
+#include <kernel_types.h>
+#include "Prio.hpp"
+
+#define MAX_NO_OF_SHORT_TQ 512
+#define MAX_NO_OF_LONG_TQ 512
+#define MAX_NO_OF_TQ (MAX_NO_OF_SHORT_TQ + MAX_NO_OF_LONG_TQ)
+#define NULL_TQ_ENTRY 65535
+
+class Signal;
+
+struct TimeStruct
+{
+ Uint16 delay_time;
+ Uint16 job_index;
+};
+
+union TimerEntry
+{
+ struct TimeStruct time_struct;
+ Uint32 copy_struct;
+};
+
+class TimeQueue
+{
+public:
+ TimeQueue();
+ ~TimeQueue();
+
+ void insert(Signal* signal, BlockNumber bnr,
+ GlobalSignalNumber gsn, Uint32 delayTime);
+ void clear();
+ void scanTable(); // Called once per millisecond
+ Uint32 getIndex();
+ void releaseIndex(Uint32 aIndex);
+ void recount_timers();
+
+private:
+ TimerEntry theShortQueue[MAX_NO_OF_SHORT_TQ];
+ TimerEntry theLongQueue[MAX_NO_OF_LONG_TQ];
+ Uint16 theFreeIndex[MAX_NO_OF_TQ];
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/TransporterCallback.cpp b/storage/ndb/src/kernel/vm/TransporterCallback.cpp
new file mode 100644
index 00000000000..0f292143c21
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/TransporterCallback.cpp
@@ -0,0 +1,454 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <TransporterCallback.hpp>
+#include <TransporterRegistry.hpp>
+#include <FastScheduler.hpp>
+#include <Emulator.hpp>
+#include <ErrorHandlingMacros.hpp>
+
+#include "LongSignal.hpp"
+
+#include <signaldata/EventReport.hpp>
+#include <signaldata/TestOrd.hpp>
+#include <signaldata/SignalDroppedRep.hpp>
+#include <signaldata/DisconnectRep.hpp>
+
+#include "VMSignal.hpp"
+#include <NdbOut.hpp>
+#include "DataBuffer.hpp"
+
+/**
+ * The instance
+ */
+SectionSegmentPool g_sectionSegmentPool;
+
+bool
+import(Ptr<SectionSegment> & first, const Uint32 * src, Uint32 len){
+ /**
+ * Dummy data used when setting prev.m_nextSegment for first segment of a
+ * section
+ */
+ Uint32 dummyPrev[4];
+
+ first.p = 0;
+ if(g_sectionSegmentPool.seize(first)){
+ ;
+ } else {
+ return false;
+ }
+
+ first.p->m_sz = len;
+ first.p->m_ownerRef = 0;
+
+ Ptr<SectionSegment> prevPtr = { (SectionSegment *)&dummyPrev[0], 0 };
+ Ptr<SectionSegment> currPtr = first;
+
+ while(len > SectionSegment::DataLength){
+ prevPtr.p->m_nextSegment = currPtr.i;
+ memcpy(&currPtr.p->theData[0], src, 4 * SectionSegment::DataLength);
+ src += SectionSegment::DataLength;
+ len -= SectionSegment::DataLength;
+ prevPtr = currPtr;
+ if(g_sectionSegmentPool.seize(currPtr)){
+ ;
+ } else {
+ first.p->m_lastSegment = prevPtr.i;
+ return false;
+ }
+ }
+
+ first.p->m_lastSegment = currPtr.i;
+ currPtr.p->m_nextSegment = RNIL;
+ memcpy(&currPtr.p->theData[0], src, 4 * len);
+ return true;
+}
+
+void
+linkSegments(Uint32 head, Uint32 tail){
+
+ Ptr<SectionSegment> headPtr;
+ g_sectionSegmentPool.getPtr(headPtr, head);
+
+ Ptr<SectionSegment> tailPtr;
+ g_sectionSegmentPool.getPtr(tailPtr, tail);
+
+ Ptr<SectionSegment> oldTailPtr;
+ g_sectionSegmentPool.getPtr(oldTailPtr, headPtr.p->m_lastSegment);
+
+ headPtr.p->m_lastSegment = tailPtr.p->m_lastSegment;
+ headPtr.p->m_sz += tailPtr.p->m_sz;
+
+ oldTailPtr.p->m_nextSegment = tailPtr.i;
+}
+
+void
+copy(Uint32 * & insertPtr,
+ class SectionSegmentPool & thePool, const SegmentedSectionPtr & _ptr){
+
+ Uint32 len = _ptr.sz;
+ SectionSegment * ptrP = _ptr.p;
+
+ while(len > 60){
+ memcpy(insertPtr, &ptrP->theData[0], 4 * 60);
+ len -= 60;
+ insertPtr += 60;
+ ptrP = thePool.getPtr(ptrP->m_nextSegment);
+ }
+ memcpy(insertPtr, &ptrP->theData[0], 4 * len);
+ insertPtr += len;
+}
+
+void
+copy(Uint32 * dst, SegmentedSectionPtr src){
+ copy(dst, g_sectionSegmentPool, src);
+}
+
+void
+getSections(Uint32 secCount, SegmentedSectionPtr ptr[3]){
+ Uint32 tSec0 = ptr[0].i;
+ Uint32 tSec1 = ptr[1].i;
+ Uint32 tSec2 = ptr[2].i;
+ SectionSegment * p;
+ switch(secCount){
+ case 3:
+ p = g_sectionSegmentPool.getPtr(tSec2);
+ ptr[2].p = p;
+ ptr[2].sz = p->m_sz;
+ case 2:
+ p = g_sectionSegmentPool.getPtr(tSec1);
+ ptr[1].p = p;
+ ptr[1].sz = p->m_sz;
+ case 1:
+ p = g_sectionSegmentPool.getPtr(tSec0);
+ ptr[0].p = p;
+ ptr[0].sz = p->m_sz;
+ case 0:
+ return;
+ }
+ char msg[40];
+ sprintf(msg, "secCount=%d", secCount);
+ ErrorReporter::handleAssert(msg, __FILE__, __LINE__);
+}
+
+void
+getSection(SegmentedSectionPtr & ptr, Uint32 i){
+ ptr.i = i;
+ SectionSegment * p = g_sectionSegmentPool.getPtr(i);
+ ptr.p = p;
+ ptr.sz = p->m_sz;
+}
+
+#define relSz(x) ((x + SectionSegment::DataLength - 1) / SectionSegment::DataLength)
+
+void
+release(SegmentedSectionPtr & ptr){
+ g_sectionSegmentPool.releaseList(relSz(ptr.sz),
+ ptr.i,
+ ptr.p->m_lastSegment);
+}
+
+void
+releaseSections(Uint32 secCount, SegmentedSectionPtr ptr[3]){
+ Uint32 tSec0 = ptr[0].i;
+ Uint32 tSz0 = ptr[0].sz;
+ Uint32 tSec1 = ptr[1].i;
+ Uint32 tSz1 = ptr[1].sz;
+ Uint32 tSec2 = ptr[2].i;
+ Uint32 tSz2 = ptr[2].sz;
+ switch(secCount){
+ case 3:
+ g_sectionSegmentPool.releaseList(relSz(tSz2), tSec2,
+ ptr[2].p->m_lastSegment);
+ case 2:
+ g_sectionSegmentPool.releaseList(relSz(tSz1), tSec1,
+ ptr[1].p->m_lastSegment);
+ case 1:
+ g_sectionSegmentPool.releaseList(relSz(tSz0), tSec0,
+ ptr[0].p->m_lastSegment);
+ case 0:
+ return;
+ }
+ char msg[40];
+ sprintf(msg, "secCount=%d", secCount);
+ ErrorReporter::handleAssert(msg, __FILE__, __LINE__);
+}
+
+#include <DebuggerNames.hpp>
+
+void
+execute(void * callbackObj,
+ SignalHeader * const header,
+ Uint8 prio,
+ Uint32 * const theData,
+ LinearSectionPtr ptr[3]){
+
+ const Uint32 secCount = header->m_noOfSections;
+ const Uint32 length = header->theLength;
+
+#ifdef TRACE_DISTRIBUTED
+ ndbout_c("recv: %s(%d) from (%s, %d)",
+ getSignalName(header->theVerId_signalNumber),
+ header->theVerId_signalNumber,
+ getBlockName(refToBlock(header->theSendersBlockRef)),
+ refToNode(header->theSendersBlockRef));
+#endif
+
+ bool ok = true;
+ Ptr<SectionSegment> secPtr[3];
+ switch(secCount){
+ case 3:
+ ok &= import(secPtr[2], ptr[2].p, ptr[2].sz);
+ case 2:
+ ok &= import(secPtr[1], ptr[1].p, ptr[1].sz);
+ case 1:
+ ok &= import(secPtr[0], ptr[0].p, ptr[0].sz);
+ }
+
+ /**
+ * Check that we haven't received a too long signal
+ */
+ ok &= (length + secCount <= 25);
+
+ Uint32 secPtrI[3];
+ if(ok){
+ /**
+ * Normal path
+ */
+ secPtrI[0] = secPtr[0].i;
+ secPtrI[1] = secPtr[1].i;
+ secPtrI[2] = secPtr[2].i;
+
+ globalScheduler.execute(header, prio, theData, secPtrI);
+ return;
+ }
+
+ /**
+ * Out of memory
+ */
+ for(Uint32 i = 0; i<secCount; i++){
+ if(secPtr[i].p != 0){
+ g_sectionSegmentPool.releaseList(relSz(ptr[i].sz), secPtr[i].i,
+ secPtr[i].p->m_lastSegment);
+ }
+ }
+ Uint32 gsn = header->theVerId_signalNumber;
+ Uint32 len = header->theLength;
+ Uint32 newLen= (len > 22 ? 22 : len);
+ SignalDroppedRep * rep = (SignalDroppedRep*)theData;
+ memmove(rep->originalData, theData, (4 * newLen));
+ rep->originalGsn = gsn;
+ rep->originalLength = len;
+ rep->originalSectionCount = secCount;
+ header->theVerId_signalNumber = GSN_SIGNAL_DROPPED_REP;
+ header->theLength = newLen + 3;
+ header->m_noOfSections = 0;
+ globalScheduler.execute(header, prio, theData, secPtrI);
+}
+
+NdbOut &
+operator<<(NdbOut& out, const SectionSegment & ss){
+ out << "[ last= " << ss.m_lastSegment << " next= " << ss.nextPool << " ]";
+ return out;
+}
+
+void
+print(SectionSegment * s, Uint32 len, FILE* out){
+ for(Uint32 i = 0; i<len; i++){
+ fprintf(out, "H\'0x%.8x ", s->theData[i]);
+ if(((i + 1) % 6) == 0)
+ fprintf(out, "\n");
+ }
+}
+
+void
+print(SegmentedSectionPtr ptr, FILE* out){
+
+ ptr.p = g_sectionSegmentPool.getPtr(ptr.i);
+ Uint32 len = ptr.p->m_sz;
+
+ fprintf(out, "ptr.i = %d(%p) ptr.sz = %d(%d)\n", ptr.i, ptr.p, len, ptr.sz);
+ while(len > SectionSegment::DataLength){
+ print(ptr.p, SectionSegment::DataLength, out);
+
+ len -= SectionSegment::DataLength;
+ fprintf(out, "ptr.i = %d\n", ptr.p->m_nextSegment);
+ ptr.p = g_sectionSegmentPool.getPtr(ptr.p->m_nextSegment);
+ }
+
+ print(ptr.p, len, out);
+ fprintf(out, "\n");
+}
+
+int
+checkJobBuffer() {
+ /**
+ * Check to see if jobbbuffers are starting to get full
+ * and if so call doJob
+ */
+ return globalScheduler.checkDoJob();
+}
+
+void
+reportError(void * callbackObj, NodeId nodeId, TransporterError errorCode){
+#ifdef DEBUG_TRANSPORTER
+ char buf[255];
+ sprintf(buf, "reportError (%d, 0x%x)", nodeId, errorCode);
+ ndbout << buf << endl;
+#endif
+
+ if(errorCode == TE_SIGNAL_LOST_SEND_BUFFER_FULL){
+ ErrorReporter::handleError(ecError,
+ ERR_PROGRAMERROR,
+ "Signal lost, send buffer full",
+ __FILE__,
+ NST_ErrorHandler);
+ }
+
+ if(errorCode == TE_SIGNAL_LOST){
+ ErrorReporter::handleError(ecError,
+ ERR_PROGRAMERROR,
+ "Signal lost (unknown reason)",
+ __FILE__,
+ NST_ErrorHandler);
+ }
+
+ if(errorCode & 0x8000){
+ reportDisconnect(callbackObj, nodeId, errorCode);
+ }
+
+ Signal signal;
+ memset(&signal.header, 0, sizeof(signal.header));
+
+
+ if(errorCode & 0x8000)
+ signal.theData[0] = NDB_LE_TransporterError;
+ else
+ signal.theData[0] = NDB_LE_TransporterWarning;
+
+ signal.theData[1] = nodeId;
+ signal.theData[2] = errorCode;
+
+ signal.header.theLength = 3;
+ signal.header.theSendersSignalId = 0;
+ signal.header.theSendersBlockRef = numberToRef(0, globalData.ownId);
+ globalScheduler.execute(&signal, JBA, CMVMI, GSN_EVENT_REP);
+}
+
+/**
+ * Report average send length in bytes (4096 last sends)
+ */
+void
+reportSendLen(void * callbackObj,
+ NodeId nodeId, Uint32 count, Uint64 bytes){
+
+ Signal signal;
+ memset(&signal.header, 0, sizeof(signal.header));
+
+ signal.header.theLength = 3;
+ signal.header.theSendersSignalId = 0;
+ signal.header.theSendersBlockRef = numberToRef(0, globalData.ownId);
+ signal.theData[0] = NDB_LE_SendBytesStatistic;
+ signal.theData[1] = nodeId;
+ signal.theData[2] = (bytes/count);
+ globalScheduler.execute(&signal, JBA, CMVMI, GSN_EVENT_REP);
+}
+
+/**
+ * Report average receive length in bytes (4096 last receives)
+ */
+void
+reportReceiveLen(void * callbackObj,
+ NodeId nodeId, Uint32 count, Uint64 bytes){
+
+ Signal signal;
+ memset(&signal.header, 0, sizeof(signal.header));
+
+ signal.header.theLength = 3;
+ signal.header.theSendersSignalId = 0;
+ signal.header.theSendersBlockRef = numberToRef(0, globalData.ownId);
+ signal.theData[0] = NDB_LE_ReceiveBytesStatistic;
+ signal.theData[1] = nodeId;
+ signal.theData[2] = (bytes/count);
+ globalScheduler.execute(&signal, JBA, CMVMI, GSN_EVENT_REP);
+}
+
+/**
+ * Report connection established
+ */
+
+void
+reportConnect(void * callbackObj, NodeId nodeId){
+
+ Signal signal;
+ memset(&signal.header, 0, sizeof(signal.header));
+
+ signal.header.theLength = 1;
+ signal.header.theSendersSignalId = 0;
+ signal.header.theSendersBlockRef = numberToRef(0, globalData.ownId);
+ signal.theData[0] = nodeId;
+
+ globalScheduler.execute(&signal, JBA, CMVMI, GSN_CONNECT_REP);
+}
+
+/**
+ * Report connection broken
+ */
+void
+reportDisconnect(void * callbackObj, NodeId nodeId, Uint32 errNo){
+
+ Signal signal;
+ memset(&signal.header, 0, sizeof(signal.header));
+
+ signal.header.theLength = DisconnectRep::SignalLength;
+ signal.header.theSendersSignalId = 0;
+ signal.header.theSendersBlockRef = numberToRef(0, globalData.ownId);
+ signal.header.theTrace = TestOrd::TraceDisconnect;
+
+ DisconnectRep * const rep = (DisconnectRep *)&signal.theData[0];
+ rep->nodeId = nodeId;
+ rep->err = errNo;
+
+ globalScheduler.execute(&signal, JBA, CMVMI, GSN_DISCONNECT_REP);
+}
+
+void
+SignalLoggerManager::printSegmentedSection(FILE * output,
+ const SignalHeader & sh,
+ const SegmentedSectionPtr ptr[3],
+ unsigned i)
+{
+ fprintf(output, "SECTION %u type=segmented", i);
+ if (i >= 3) {
+ fprintf(output, " *** invalid ***\n");
+ return;
+ }
+ const Uint32 len = ptr[i].sz;
+ SectionSegment * ssp = ptr[i].p;
+ Uint32 pos = 0;
+ fprintf(output, " size=%u\n", (unsigned)len);
+ while (pos < len) {
+ if (pos > 0 && pos % SectionSegment::DataLength == 0) {
+ ssp = g_sectionSegmentPool.getPtr(ssp->m_nextSegment);
+ }
+ printDataWord(output, pos, ssp->theData[pos % SectionSegment::DataLength]);
+ }
+ if (len > 0)
+ putc('\n', output);
+}
+
diff --git a/storage/ndb/src/kernel/vm/VMSignal.cpp b/storage/ndb/src/kernel/vm/VMSignal.cpp
new file mode 100644
index 00000000000..e4eafb47ff7
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/VMSignal.cpp
@@ -0,0 +1,34 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "VMSignal.hpp"
+#include <string.h>
+
+Signal::Signal(){
+ memset(&header, 0, sizeof(header));
+ memset(theData, 0, sizeof(theData));
+}
+
+void
+Signal::garbage_register()
+{
+ int i;
+ theData[0] = 0x13579135;
+ header.theLength = 0x13579135;
+ header.theSendersBlockRef = 0x13579135;
+ for (i = 1; i < 24; i++)
+ theData[i] = 0x13579135;
+}
diff --git a/storage/ndb/src/kernel/vm/VMSignal.hpp b/storage/ndb/src/kernel/vm/VMSignal.hpp
new file mode 100644
index 00000000000..45543c5d174
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/VMSignal.hpp
@@ -0,0 +1,198 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 VMSignal_H
+#define VMSignal_H
+
+#include <ndb_global.h>
+#include <ndb_limits.h>
+#include <kernel_types.h>
+
+#include <ErrorReporter.hpp>
+#include <NodeBitmask.hpp>
+
+#include <RefConvert.hpp>
+#include <TransporterDefinitions.hpp>
+
+/**
+ * Struct used when sending to multiple blocks
+ */
+struct NodeReceiverGroup {
+ NodeReceiverGroup();
+ NodeReceiverGroup(Uint32 blockRef);
+ NodeReceiverGroup(Uint32 blockNo, const NodeBitmask &);
+ NodeReceiverGroup(Uint32 blockNo, const class SignalCounter &);
+
+ NodeReceiverGroup& operator=(BlockReference ref);
+
+ Uint32 m_block;
+ NodeBitmask m_nodes;
+};
+
+/**
+ * class used for passing argumentes to blocks
+ */
+class Signal {
+ friend class SimulatedBlock;
+ friend class APZJobBuffer;
+ friend class FastScheduler;
+public:
+ Signal();
+
+ Uint32 getLength() const;
+ Uint32 getTrace() const;
+ Uint32 getSendersBlockRef() const;
+
+ const Uint32* getDataPtr() const ;
+ Uint32* getDataPtrSend() ;
+
+ void setTrace(Uint32);
+
+ Uint32 getNoOfSections() const;
+ bool getSection(SegmentedSectionPtr & ptr, Uint32 sectionNo);
+ void setSection(SegmentedSectionPtr ptr, Uint32 sectionNo);
+
+ /**
+ * Old depricated methods...
+ */
+ Uint32 length() const { return getLength();}
+ BlockReference senderBlockRef() const { return getSendersBlockRef();}
+
+private:
+ void setLength(Uint32);
+
+public:
+#define VMS_DATA_SIZE \
+ (MAX_ATTRIBUTES_IN_TABLE + MAX_TUPLE_SIZE_IN_WORDS + MAX_KEY_SIZE_IN_WORDS)
+
+#if VMS_DATA_SIZE > 8192
+#error "VMSignal buffer is too small"
+#endif
+
+ SignalHeader header; // 28 bytes
+ SegmentedSectionPtr m_sectionPtr[3];
+ union {
+ Uint32 theData[8192]; // 8192 32-bit words -> 32K Bytes
+ Uint64 dummyAlign;
+ };
+ void garbage_register();
+};
+
+inline
+Uint32
+Signal::getLength() const {
+ return header.theLength;
+}
+
+inline
+Uint32
+Signal::getTrace() const {
+ return header.theTrace;
+}
+
+inline
+Uint32
+Signal::getSendersBlockRef() const {
+ return header.theSendersBlockRef;
+}
+
+inline
+const Uint32*
+Signal::getDataPtr() const {
+ return &theData[0];
+}
+
+inline
+Uint32*
+Signal::getDataPtrSend() {
+ return &theData[0];
+}
+
+inline
+void
+Signal::setLength(Uint32 len){
+ header.theLength = len;
+}
+
+inline
+void
+Signal::setTrace(Uint32 t){
+ header.theTrace = t;
+}
+
+inline
+Uint32
+Signal::getNoOfSections() const {
+ return header.m_noOfSections;
+}
+
+inline
+bool
+Signal::getSection(SegmentedSectionPtr & ptr, Uint32 section){
+ if(section < header.m_noOfSections){
+ ptr = m_sectionPtr[section];
+ return true;
+ }
+ ptr.p = 0;
+ return false;
+}
+
+inline
+void
+Signal::setSection(SegmentedSectionPtr ptr, Uint32 sectionNo){
+ if(sectionNo != header.m_noOfSections || sectionNo > 2){
+ abort();
+ }
+ m_sectionPtr[sectionNo] = ptr;
+ header.m_noOfSections++;
+}
+
+inline
+NodeReceiverGroup::NodeReceiverGroup() : m_block(0){
+ m_nodes.clear();
+}
+
+inline
+NodeReceiverGroup::NodeReceiverGroup(Uint32 blockRef){
+ m_nodes.clear();
+ m_block = refToBlock(blockRef);
+ m_nodes.set(refToNode(blockRef));
+}
+
+inline
+NodeReceiverGroup::NodeReceiverGroup(Uint32 blockNo, const NodeBitmask & nodes){
+ m_block = blockNo;
+ m_nodes = nodes;
+}
+
+#include "SignalCounter.hpp"
+
+inline
+NodeReceiverGroup::NodeReceiverGroup(Uint32 blockNo, const SignalCounter & nodes){
+ m_block = blockNo;
+ m_nodes = nodes.m_nodes;
+}
+
+inline
+NodeReceiverGroup&
+NodeReceiverGroup::operator=(BlockReference blockRef){
+ m_nodes.clear();
+ m_block = refToBlock(blockRef);
+ m_nodes.set(refToNode(blockRef));
+ return * this;
+}
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/WaitQueue.hpp b/storage/ndb/src/kernel/vm/WaitQueue.hpp
new file mode 100644
index 00000000000..4d7240b6866
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/WaitQueue.hpp
@@ -0,0 +1,35 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 WAIT_QUEUE_HPP
+#define WAIT_QUEUE_HPP
+
+#include "ArrayPool.hpp"
+#include "SimulatedBlock.hpp"
+
+template <class Block,
+ class T,
+ void (Block::* Function)(Signal*, Ptr<T>)>
+class WaitQueue {
+public:
+ WaitQueue(Block & block, ArrayPool<T>& pool){
+ }
+
+ void add(Ptr<T>, void (Block::* Callback)(Signal*, Ptr<T>)) {}
+ void complete(Ptr<T>) {}
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/WatchDog.cpp b/storage/ndb/src/kernel/vm/WatchDog.cpp
new file mode 100644
index 00000000000..23475a478d3
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/WatchDog.cpp
@@ -0,0 +1,145 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+
+#include "WatchDog.hpp"
+#include "GlobalData.hpp"
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <ErrorHandlingMacros.hpp>
+
+extern "C"
+void*
+runWatchDog(void* w){
+ ((WatchDog*)w)->run();
+ return NULL;
+}
+
+WatchDog::WatchDog(Uint32 interval) :
+ theIPValue(globalData.getWatchDogPtr())
+{
+ setCheckInterval(interval);
+ theStop = false;
+ theThreadPtr = 0;
+}
+
+WatchDog::~WatchDog(){
+ doStop();
+}
+
+Uint32
+WatchDog::setCheckInterval(Uint32 interval){
+ // An interval of less than 70ms is not acceptable
+ return theInterval = (interval < 70 ? 70 : interval);
+}
+
+void
+WatchDog::doStart(){
+ theStop = false;
+ theThreadPtr = NdbThread_Create(runWatchDog,
+ (void**)this,
+ 32768,
+ "ndb_watchdog",
+ NDB_THREAD_PRIO_HIGH);
+}
+
+void
+WatchDog::doStop(){
+ void *status;
+ theStop = true;
+ if(theThreadPtr){
+ NdbThread_WaitFor(theThreadPtr, &status);
+ NdbThread_Destroy(&theThreadPtr);
+ }
+}
+
+void
+WatchDog::run(){
+ unsigned int anIPValue;
+ unsigned int alerts = 0;
+ unsigned int oldIPValue = 0;
+
+ // WatchDog for the single threaded NDB
+ while(!theStop){
+ Uint32 tmp = theInterval / 500;
+ tmp= (tmp ? tmp : 1);
+
+ while(!theStop && tmp > 0){
+ NdbSleep_MilliSleep(500);
+ tmp--;
+ }
+
+ if(theStop)
+ break;
+
+ // Verify that the IP thread is not stuck in a loop
+ anIPValue = *theIPValue;
+ if(anIPValue != 0) {
+ oldIPValue = anIPValue;
+ globalData.incrementWatchDogCounter(0);
+ alerts = 0;
+ } else {
+ alerts++;
+ ndbout << "Ndb kernel is stuck in: ";
+ switch (oldIPValue) {
+ case 1:
+ ndbout << "Job Handling" << endl;
+ break;
+ case 2:
+ ndbout << "Scanning Timers" << endl;
+ break;
+ case 3:
+ ndbout << "External I/O" << endl;
+ break;
+ case 4:
+ ndbout << "Print Job Buffers at crash" << endl;
+ break;
+ case 5:
+ ndbout << "Checking connections" << endl;
+ break;
+ case 6:
+ ndbout << "Performing Send" << endl;
+ break;
+ case 7:
+ ndbout << "Polling for Receive" << endl;
+ break;
+ case 8:
+ ndbout << "Performing Receive" << endl;
+ break;
+ default:
+ ndbout << "Unknown place" << endl;
+ break;
+ }//switch
+ if(alerts == 3){
+ shutdownSystem();
+ }
+ }
+ }
+ return;
+}
+
+void
+WatchDog::shutdownSystem(){
+
+ ErrorReporter::handleError(ecError,
+ ERR_PROGRAMERROR,
+ "WatchDog terminate",
+ __FILE__,
+ NST_Watchdog);
+}
diff --git a/storage/ndb/src/kernel/vm/WatchDog.hpp b/storage/ndb/src/kernel/vm/WatchDog.hpp
new file mode 100644
index 00000000000..4b44b1a96a2
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/WatchDog.hpp
@@ -0,0 +1,56 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 WatchDog_H
+#define WatchDog_H
+
+#include <kernel_types.h>
+#include <NdbThread.h>
+
+extern "C" void* runWatchDog(void* w);
+
+class WatchDog{
+public:
+ WatchDog(Uint32 interval = 3000);
+ ~WatchDog();
+
+ void doStart();
+ void doStop();
+
+ Uint32 setCheckInterval(Uint32 interval);
+
+protected:
+ /**
+ * Thread function
+ */
+ friend void* runWatchDog(void* w);
+
+ /**
+ * Thread pointer
+ */
+ NdbThread* theThreadPtr;
+
+private:
+ Uint32 theInterval;
+ const Uint32 * theIPValue;
+
+ bool theStop;
+
+ void run();
+ void shutdownSystem();
+};
+
+#endif // WatchDog_H
diff --git a/storage/ndb/src/kernel/vm/al_test/Makefile b/storage/ndb/src/kernel/vm/al_test/Makefile
new file mode 100644
index 00000000000..a7287a341fd
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/al_test/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := al_test
+BIN_TARGET_ARCHIVES := portlib
+
+SOURCES = main.cpp
+
+CFLAGS_main.cpp = -DDEBUG
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/vm/al_test/arrayListTest.cpp b/storage/ndb/src/kernel/vm/al_test/arrayListTest.cpp
new file mode 100644
index 00000000000..bb320106653
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/al_test/arrayListTest.cpp
@@ -0,0 +1,317 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <ndb_global.hpp>
+
+#include <ArrayList.hpp>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+struct A_Listable_Object {
+ Uint32 next;
+ Uint32 prev;
+ char somedata[12];
+
+ void print (NdbOut & out) {
+ out << "ALO: next = " << next
+ << " prev = " << prev << endl;
+ }
+};
+
+extern const int x_AL_Next = offsetof(A_Listable_Object, next);
+extern const int x_AL_Prev = offsetof(A_Listable_Object, prev);
+
+NdbOut &
+operator<<(NdbOut & o, A_Listable_Object & a){
+ a.print(o);
+ return o;
+}
+
+typedef Ptr<A_Listable_Object> A_Listable_ObjectPtr;
+
+#define APool ArrayPool<A_Listable_Object>
+#define AList ArrayList<A_Listable_Object>
+
+APool aGPool;
+AList aGList(aGPool);
+
+class ArrayListTest {
+public:
+ static void tryList0(int listSize){
+ APool aPool;
+ AList aList(aPool);
+
+ if(!aPool.setSize(listSize)){
+ ndbout << "Failed to do aPool.setSize(" << listSize << ")" << endl;
+ return;
+ }
+
+ int * anArray = new int[listSize];
+
+ for(int i = 1; i<listSize; i++){
+ int arrayElements = 0;
+
+
+ for(int j = 0; j<i; j++){
+ A_Listable_ObjectPtr p;
+ const int ret = aList.seize(p);
+ if(ret == RNIL){
+ ndbout << "Failed to seize!!" << endl;
+ ndbout << "Have seized " << j
+ << " out of " << listSize << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ }
+ assert(aList.noOfElements() == i);
+ assert(aPool.noOfFree() == (listSize - i));
+ assert(arrayElements == i);
+
+ for(int j = 0; j<i; j++){
+ aList.release(anArray[j]);
+ }
+
+ assert(aList.noOfElements() == 0);
+ assert(aPool.noOfFree() == listSize);
+ }
+ }
+
+ static void tryList1(int listSize, int iterations){
+ APool aPool;
+ AList aList(aPool);
+
+ if(!aPool.setSize(listSize)){
+ ndbout << "Failed to do aPool.setSize(" << listSize << ")" << endl;
+ return;
+ }
+
+ ndbout << "Seizing/Releaseing " << iterations
+ << " times over list with " << listSize << " elements" << endl;
+
+ int * anArray = new int[listSize];
+ int arrayElements = 0;
+
+ int noOfSeize = 0;
+ int noFailSeize = 0;
+ int noOfRelease = 0;
+
+ for(int i = 0; i<iterations; i++){
+ assert(arrayElements <= listSize);
+ const int r = rand() % (10 * listSize);
+ if(r < (arrayElements - 1)){
+ /**
+ * Release an element
+ */
+ noOfRelease++;
+ aList.release(anArray[r]);
+ arrayElements--;
+ for(int j = r; j<arrayElements; j++)
+ anArray[j] = anArray[j+1];
+
+ } else {
+ /**
+ * Seize an element
+ */
+ A_Listable_ObjectPtr p;
+ const int ret = aList.seize(p);
+ if(ret == RNIL && arrayElements != listSize){
+ ndbout << "Failed to seize!!"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << listSize << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(arrayElements >= listSize && ret != RNIL){
+ ndbout << "Seize did not fail when it should have"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << listSize << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(ret != RNIL){
+ noOfSeize++;
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ } else {
+ noFailSeize++;
+ }
+ }
+ }
+ delete []anArray;
+
+ ndbout << "Seized: " << noOfSeize
+ << " Seized with buffer full: " << noFailSeize
+ << " Release: " << noOfRelease << " --- ";
+ ndbout << "(" << noOfSeize << " + " << noFailSeize << " + " << noOfRelease
+ << " = " << (noOfSeize + noFailSeize + noOfRelease) << ")" << endl;
+ }
+
+ static void tryList2(int size, int iter, int fail){
+ APool aPool;
+ AList aList(aPool);
+
+ if(!aPool.setSize(size)){
+ ndbout << "Failed to do aPool.setSize(" << size << ")" << endl;
+ return;
+ }
+
+ ndbout << "doing getPtr(i) where i > size(" << size << ") "
+ << fail << " times mixed with " << iter
+ << " ordinary seize/release" << endl;
+
+ int * anArray = new int[size];
+ int arrayElements = 0;
+
+ int noOfSeize = 0;
+ int noFailSeize = 0;
+ int noOfRelease = 0;
+
+ for(int i = 0; i<iter; i++){
+ assert(arrayElements <= size);
+ const int r = rand() % (10 * size);
+
+ if((i + 1)%(iter/fail) == 0){
+ aList.getPtr(size + r);
+ continue;
+ }
+
+ if(r < (arrayElements - 1)){
+ /**
+ * Release an element
+ */
+ noOfRelease++;
+ aList.release(anArray[r]);
+ arrayElements--;
+ for(int j = r; j<arrayElements; j++)
+ anArray[j] = anArray[j+1];
+
+ } else {
+ /**
+ * Seize an element
+ */
+ A_Listable_ObjectPtr p;
+ const int ret = aList.seize(p);
+ if(ret == RNIL && arrayElements != size){
+ ndbout << "Failed to seize!!"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(arrayElements >= size && ret != RNIL){
+ ndbout << "Seize did not fail when it should have"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(ret != RNIL){
+ noOfSeize++;
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ } else {
+ noFailSeize++;
+ }
+ }
+ }
+ delete []anArray;
+ }
+
+ static void
+ tryList3(int size, int fail){
+ ndbout << "Failing " << fail << " times " << endl;
+
+ for(int i = 0; i<fail; i++){
+ APool aPool;
+ AList aList(aPool);
+
+ if(!aPool.setSize(size)){
+ ndbout << "Failed to do aPool.setSize(" << size << ")" << endl;
+ return;
+ }
+
+ const int noOfElementsInBufferWhenFail = (i + 1) * (size /(fail + 1));
+
+ int * anArray = new int[size];
+ for(int i = 0; i<size; i++)
+ anArray[i] = i;
+ int arrayElements = 0;
+
+ int noOfSeize = 0;
+ int noFailSeize = 0;
+ int noOfRelease = 0;
+
+ while(true){
+ assert(arrayElements <= size);
+ if(arrayElements == noOfElementsInBufferWhenFail){
+ ndbout << "++ You should get a ErrorReporter::handle... " << endl;
+ aList.release(anArray[arrayElements]);
+ ndbout << "++ Inbetween these lines" << endl << endl;
+ break;
+ }
+ const int r = rand() % (10 * size);
+ if(r < (arrayElements - 1)){
+ /**
+ * Release an element
+ */
+ noOfRelease++;
+ aList.release(anArray[r]);
+ arrayElements--;
+ for(int j = r; j<arrayElements; j++)
+ anArray[j] = anArray[j+1];
+
+ } else {
+ /**
+ * Seize an element
+ */
+ A_Listable_ObjectPtr p;
+ const int ret = aList.seize(p);
+ if(ret == RNIL && arrayElements != size){
+ ndbout << "Failed to seize!!" << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(arrayElements >= size && ret != RNIL){
+ ndbout << "Seize did not fail when it should have" << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(ret != RNIL){
+ noOfSeize++;
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ } else {
+ noFailSeize++;
+ }
+ }
+ }
+ delete []anArray;
+ }
+
+ }
+};
diff --git a/storage/ndb/src/kernel/vm/al_test/arrayPoolTest.cpp b/storage/ndb/src/kernel/vm/al_test/arrayPoolTest.cpp
new file mode 100644
index 00000000000..e80905121e1
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/al_test/arrayPoolTest.cpp
@@ -0,0 +1,298 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+#include <ndb_global.h>
+
+#include <ArrayList.hpp>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+struct A_Poolable_Object {
+ Uint32 next;
+ char somedata[12];
+
+ void print (NdbOut & out) {
+ out << "A_Poolable_Object: next = " << next << endl;
+ }
+
+};
+
+
+NdbOut &
+operator<<(NdbOut & o, A_Poolable_Object & a){
+ a.print(o);
+ return o;
+}
+
+typedef Ptr<A_Poolable_Object> A_Poolable_ObjectPtr;
+#if 1
+#define BPool ArrayPool<A_Poolable_Object>
+#else
+#define BPool ArrayPool(A_Poolable_Object, next)
+#endif
+
+class ArrayPoolTest {
+public:
+ static void tryPool1(int poolSize, int iterations){
+ BPool aPool;
+
+ if(!aPool.setSize(poolSize)){
+ ndbout << "Failed to do aPool.setSize(" << poolSize << ")" << endl;
+ return;
+ }
+
+ ndbout << "Seizing/Releaseing " << iterations
+ << " times over pool with " << poolSize << " elements" << endl;
+
+ int * anArray = new int[poolSize];
+ int arrayElements = 0;
+
+ int noOfSeize = 0;
+ int noFailSeize = 0;
+ int noOfRelease = 0;
+
+ for(int i = 0; i<iterations; i++){
+ if(!((arrayElements <= poolSize) &&
+ (aPool.noOfFree() == aPool.noOfFree2()) &&
+ (aPool.noOfFree() == (poolSize - arrayElements)))){
+ ndbout << "Assertion!!"
+ << " iteration=" << i << endl;
+ const int f1 = aPool.noOfFree();
+ const int f2 = aPool.noOfFree2();
+ ndbout << "noOfFree() = " << f1 << endl;
+ ndbout << "noOfFree2() = " << f2 << endl;
+ ndbout << "poolSize = " << poolSize << endl;
+ ndbout << "arrayElemts = " << arrayElements << endl;
+ aPool.print(ndbout);
+ assert(0);
+ }
+
+ const int r = rand() % (10 * poolSize);
+ if(r < (arrayElements - 1)){
+ /**
+ * Release an element
+ */
+ noOfRelease++;
+ aPool.release(anArray[r]);
+ arrayElements--;
+ for(int j = r; j<arrayElements; j++)
+ anArray[j] = anArray[j+1];
+
+ } else {
+ /**
+ * Seize an element
+ */
+ A_Poolable_ObjectPtr p;
+ const int ret = aPool.seize(p);
+ if(ret == RNIL && arrayElements != poolSize){
+ ndbout << "Failed to seize!!"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << poolSize << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(arrayElements >= poolSize && ret != RNIL){
+ ndbout << "Seize did not fail when it should have"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << poolSize << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(ret != RNIL){
+ noOfSeize++;
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ memset(p.p, i, sizeof(p.p->somedata));
+ } else {
+ noFailSeize++;
+ }
+ }
+ }
+ delete []anArray;
+
+ ndbout << "Seized: " << noOfSeize
+ << " Seized with buffer full: " << noFailSeize
+ << " Release: " << noOfRelease << " --- ";
+ ndbout << "(" << noOfSeize << " + " << noFailSeize << " + " << noOfRelease
+ << " = " << (noOfSeize + noFailSeize + noOfRelease) << ")" << endl;
+ }
+
+ static void tryPool2(int size, int iter, int fail){
+ BPool aPool;
+
+ if(!aPool.setSize(size)){
+ ndbout << "Failed to do aPool.setSize(" << size << ")" << endl;
+ return;
+ }
+
+ ndbout << "doing getPtr(i) where i > size(" << size << ") "
+ << fail << " times mixed with " << iter
+ << " ordinary seize/release" << endl;
+
+ int * anArray = new int[size];
+ int arrayElements = 0;
+
+ int noOfSeize = 0;
+ int noFailSeize = 0;
+ int noOfRelease = 0;
+
+ for(int i = 0; i<iter; i++){
+ if(!((arrayElements <= size) &&
+ (aPool.noOfFree() == aPool.noOfFree2()) &&
+ (aPool.noOfFree() == (size - arrayElements)))){
+ ndbout << "Assertion!!"
+ << " iteration=" << i << endl;
+ const int f1 = aPool.noOfFree();
+ const int f2 = aPool.noOfFree2();
+ ndbout << "noOfFree() = " << f1 << endl;
+ ndbout << "noOfFree2() = " << f2 << endl;
+ ndbout << "poolSize = " << size << endl;
+ ndbout << "arrayElemts = " << arrayElements << endl;
+ aPool.print(ndbout);
+ assert(0);
+ }
+ const int r = rand() % (10 * size);
+
+ if((i + 1)%(iter/fail) == 0){
+ aPool.getPtr(size + r);
+ continue;
+ }
+
+ if(r < (arrayElements - 1)){
+ /**
+ * Release an element
+ */
+ noOfRelease++;
+ aPool.release(anArray[r]);
+ arrayElements--;
+ for(int j = r; j<arrayElements; j++)
+ anArray[j] = anArray[j+1];
+
+ } else {
+ /**
+ * Seize an element
+ */
+ A_Poolable_ObjectPtr p;
+ const int ret = aPool.seize(p);
+ if(ret == RNIL && arrayElements != size){
+ ndbout << "Failed to seize!!"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(arrayElements >= size && ret != RNIL){
+ ndbout << "Seize did not fail when it should have"
+ << " iteration=" << i << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(ret != RNIL){
+ noOfSeize++;
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ memset(p.p, p.i, sizeof(p.p->somedata));
+ } else {
+ noFailSeize++;
+ }
+ }
+ }
+ delete []anArray;
+ }
+
+ static void
+ tryPool3(int size, int fail){
+ ndbout << "Failing " << fail << " times " << endl;
+
+ for(int i = 0; i<fail; i++){
+ BPool aPool;
+ if(!aPool.setSize(size)){
+ ndbout << "Failed to do aPool.setSize(" << size << ")" << endl;
+ return;
+ }
+
+ const int noOfElementsInBufferWhenFail = (i + 1) * (size /(fail + 1));
+
+ int * anArray = new int[size];
+ for(int i = 0; i<size; i++)
+ anArray[i] = i;
+ int arrayElements = 0;
+
+ int noOfSeize = 0;
+ int noFailSeize = 0;
+ int noOfRelease = 0;
+
+ while(true){
+ assert(arrayElements <= size);
+ if(arrayElements == noOfElementsInBufferWhenFail){
+ ndbout << "++ You should get a ErrorReporter::handle... " << endl;
+ aPool.release(anArray[arrayElements]);
+ ndbout << "++ Inbetween these lines" << endl << endl;
+ break;
+ }
+ const int r = rand() % (10 * size);
+ if(r < (arrayElements - 1)){
+ /**
+ * Release an element
+ */
+ noOfRelease++;
+ aPool.release(anArray[r]);
+ arrayElements--;
+ for(int j = r; j<arrayElements; j++)
+ anArray[j] = anArray[j+1];
+
+ } else {
+ /**
+ * Seize an element
+ */
+ A_Poolable_ObjectPtr p;
+ const int ret = aPool.seize(p);
+ if(ret == RNIL && arrayElements != size){
+ ndbout << "Failed to seize!!" << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(arrayElements >= size && ret != RNIL){
+ ndbout << "Seize did not fail when it should have" << endl;
+ ndbout << "Have seized " << arrayElements
+ << " out of " << size << endl;
+ ndbout << "Terminating..." << endl;
+ abort();
+ }
+ if(ret != RNIL){
+ noOfSeize++;
+ anArray[arrayElements] = ret;
+ arrayElements++;
+ } else {
+ noFailSeize++;
+ }
+ }
+ }
+ delete []anArray;
+ }
+
+ }
+};
+
diff --git a/storage/ndb/src/kernel/vm/al_test/main.cpp b/storage/ndb/src/kernel/vm/al_test/main.cpp
new file mode 100644
index 00000000000..23193b50725
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/al_test/main.cpp
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <ArrayList.hpp>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+#include <NdbMain.h>
+
+#include "arrayListTest.cpp"
+#include "arrayPoolTest.cpp"
+
+NDB_COMMAND(al_test, "al_test", "al_test", "al_test", 65535)
+{
+ NdbMem_Create();
+ srand(NdbTick_CurrentMillisecond());
+
+#if 1
+ ndbout << endl << endl << "-- Testing basic basic seize/release" << endl;
+ ArrayListTest::tryList0(10);
+
+ ndbout << endl << endl << "-- Testing basic seize/release" << endl;
+ ArrayListTest::tryList1(1000, 1000);
+
+ ndbout << endl << endl << "-- Testing that seize returns RNIL"
+ << endl;
+ ArrayListTest::tryList1(10, 1000000);
+
+ ndbout << endl << endl << "-- Testing access out of array" << endl;
+ ArrayListTest::tryList2(1000, 100000, 5);
+#endif
+
+#if 1
+ ndbout << endl << endl << "-- Testing basic seize/release" << endl;
+ ArrayPoolTest::tryPool1(1000, 1000);
+
+ ndbout << endl << endl << "-- Testing that seize returns RNIL"
+ << endl;
+ ArrayPoolTest::tryPool1(10, 1000000);
+
+ ndbout << endl << endl << "-- Testing access out of array" << endl;
+ ArrayPoolTest::tryPool2(1000, 100000, 5);
+
+ ndbout << endl << endl << "-- Testing releasing none seized element" << endl;
+ ArrayPoolTest::tryPool3(1000, 5);
+#endif
+}
+
+void
+ErrorReporter::handleBlockAssert(int line)
+{
+ ndbout << "ErrorReporter::handleAssert activated - "
+ << " line= " << line << endl;
+ //assert(0);
+}
diff --git a/storage/ndb/src/kernel/vm/pc.hpp b/storage/ndb/src/kernel/vm/pc.hpp
new file mode 100644
index 00000000000..2d745d26b1c
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/pc.hpp
@@ -0,0 +1,251 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 PC_H
+#define PC_H
+
+
+#include "Emulator.hpp"
+#include <NdbOut.hpp>
+#include <ndb_limits.h>
+
+#ifdef NO_EMULATED_JAM
+
+#define jam()
+#define jamLine(line)
+#define jamEntry()
+#define jamEntryLine(line)
+
+#else
+#ifdef NDB_WIN32
+
+#define jam() { \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)(theEmulatedJam + tEmulatedJamIndex) = __LINE__; \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+#define jamLine(line) { \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)(theEmulatedJam + tEmulatedJamIndex) = line; \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+#define jamEntry() { \
+ theEmulatedJamBlockNumber = number(); \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)(theEmulatedJam + tEmulatedJamIndex) = \
+ ((theEmulatedJamBlockNumber << 20) | __LINE__); \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+#define jamEntryLine(line) { \
+ theEmulatedJamBlockNumber = number(); \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)(theEmulatedJam + tEmulatedJamIndex) = \
+ ((theEmulatedJamBlockNumber << 20) | line); \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+
+#else
+
+#define jam() { \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)((UintPtr)theEmulatedJam + (Uint32)tEmulatedJamIndex) = __LINE__; \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+#define jamLine(line) { \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)((UintPtr)theEmulatedJam + (Uint32)tEmulatedJamIndex) = line; \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+#define jamEntry() { \
+ theEmulatedJamBlockNumber = number(); \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)((UintPtr)theEmulatedJam + (Uint32)tEmulatedJamIndex) = \
+ ((theEmulatedJamBlockNumber << 20) | __LINE__); \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+#define jamEntryLine(line) { \
+ theEmulatedJamBlockNumber = number(); \
+ Uint32 tEmulatedJamIndex = theEmulatedJamIndex; \
+ *(Uint32*)((UintPtr)theEmulatedJam + (Uint32)tEmulatedJamIndex) = \
+ ((theEmulatedJamBlockNumber << 20) | line); \
+ theEmulatedJamIndex = (tEmulatedJamIndex + 4) & JAM_MASK; }
+
+#endif
+
+#endif
+#ifndef NDB_OPT
+#define ptrCheck(ptr, limit, rec) if (ptr.i < (limit)) ptr.p = &rec[ptr.i]; else ptr.p = NULL
+
+/**
+ * Sets the p-value of a ptr-struct to be a pointer to record no i
+ * (where i is the i-value of the ptr-struct)
+ *
+ * @param ptr ptr-struct with a set i-value (the p-value in this gets set)
+ * @param limit max no of records in rec
+ * @param rec pointer to first record in an array of records
+ */
+#define ptrCheckGuard(ptr, limit, rec) {\
+ UintR TxxzLimit; \
+ TxxzLimit = (limit); \
+ UintR TxxxPtr; \
+ TxxxPtr = ptr.i; \
+ ptr.p = &rec[TxxxPtr]; \
+ if (TxxxPtr < (TxxzLimit)) { \
+ ; \
+ } else { \
+ progError(__LINE__, ERR_POINTER_NOTINRANGE, __FILE__); \
+ }}
+
+#define ptrAss(ptr, rec) ptr.p = &rec[ptr.i]
+#define ptrNull(ptr) ptr.p = NULL
+#define ptrGuard(ptr) if (ptr.p == NULL) \
+ progError(__LINE__, ERR_POINTER_NOTINRANGE, __FILE__)
+#define arrGuard(ind, size) if ((ind) >= (size)) \
+ progError(__LINE__, ERR_INDEX_NOTINRANGE, __FILE__)
+#else
+#define ptrCheck(ptr, limit, rec) ptr.p = &rec[ptr.i]
+#define ptrCheckGuard(ptr, limit, rec) ptr.p = &rec[ptr.i]
+#define ptrAss(ptr, rec) ptr.p = &rec[ptr.i]
+#define ptrNull(ptr) ptr.p = NULL
+#define ptrGuard(ptr)
+#define arrGuard(ind, size)
+#endif
+
+// -------- ERROR INSERT MACROS -------
+#ifdef ERROR_INSERT
+#define ERROR_INSERT_VARIABLE UintR cerrorInsert
+#define ERROR_INSERTED(x) (cerrorInsert == (x))
+#define SET_ERROR_INSERT_VALUE(x) cerrorInsert = x
+#define CLEAR_ERROR_INSERT_VALUE cerrorInsert = 0
+#else
+#define ERROR_INSERT_VARIABLE typedef void * cerrorInsert // Will generate compiler error if used
+#define ERROR_INSERTED(x) false
+#define SET_ERROR_INSERT_VALUE(x)
+#define CLEAR_ERROR_INSERT_VALUE
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* COMMONLY USED CONSTANTS. */
+/* ------------------------------------------------------------------------- */
+#define ZFALSE 0
+#define ZTRUE 1
+#define ZSET 1
+#define ZOK 0
+#define ZNOT_OK 1
+#define ZCLOSE_FILE 2
+#define ZNIL 0xffff
+#define Z8NIL 255
+
+/* ------------------------------------------------------------------------- */
+// Number of fragments stored per node. Should be settable on a table basis
+// in future version since small tables want small value and large tables
+// need large value.
+/* ------------------------------------------------------------------------- */
+#define NO_OF_FRAG_PER_NODE 1
+#define MAX_FRAG_PER_NODE 8
+
+/**
+* DIH allocates fragments in chunk for fast find of fragment record.
+* These parameters define chunk size and log of chunk size.
+*/
+#define NO_OF_FRAGS_PER_CHUNK 4
+#define LOG_NO_OF_FRAGS_PER_CHUNK 2
+
+/* ---------------------------------------------------------------- */
+// To avoid synching too big chunks at a time we synch after writing
+// a certain number of data/UNDO pages. (e.g. 2 MBytes).
+/* ---------------------------------------------------------------- */
+#define MAX_PAGES_WITHOUT_SYNCH 64
+#define MAX_REDO_PAGES_WITHOUT_SYNCH 32
+
+/* ------------------------------------------------------------------ */
+// We have these constants to ensure that we can easily change the
+// parallelism of node recovery and the amount of scan
+// operations needed for node recoovery.
+/* ------------------------------------------------------------------ */
+#define MAX_NO_WORDS_OUTSTANDING_COPY_FRAGMENT 6000
+#define MAGIC_CONSTANT 56
+#define NODE_RECOVERY_SCAN_OP_RECORDS \
+ (4 + ((4*MAX_NO_WORDS_OUTSTANDING_COPY_FRAGMENT)/ \
+ ((MAGIC_CONSTANT + 2) * 5)))
+
+#ifdef NO_CHECKPOINT
+#define NO_LCP
+#define NO_GCP
+#endif
+
+/**
+ * Ndb kernel blocks assertion handling
+ *
+ * Two type of assertions:
+ * - ndbassert - Only used when compiling VM_TRACE
+ * - ndbrequire - Always checked
+ *
+ * If a ndbassert/ndbrequire fails, the system will
+ * shutdown and generate an error log
+ *
+ *
+ * NOTE these may only be used within blocks
+ */
+#if defined VM_TRACE
+#define ndbassert(check) \
+ if((check)){ \
+ } else { \
+ progError(__LINE__, ERR_NDBREQUIRE, __FILE__); \
+ }
+
+#define ndbrequire(check) \
+ if((check)){ \
+ } else { \
+ progError(__LINE__, ERR_NDBREQUIRE, __FILE__); \
+ }
+#else
+#define ndbassert(check)
+
+#define ndbrequire(check) \
+ if((check)){ \
+ } else { \
+ progError(__LINE__, ERR_NDBREQUIRE, __FILE__); \
+ }
+#endif
+
+#define CRASH_INSERTION(errorType) \
+ if (!ERROR_INSERTED((errorType))) { \
+ } else { \
+ progError(__LINE__, ERR_ERROR_INSERT, __FILE__); \
+ }
+
+#define CRASH_INSERTION2(errorNum, condition) \
+ if (!(ERROR_INSERTED(errorNum) && condition)) { \
+ } else { \
+ progError(__LINE__, ERR_ERROR_INSERT, __FILE__); \
+ }
+
+#define MEMCOPY_PAGE(to, from, page_size_in_bytes) \
+ memcpy((void*)(to), (void*)(from), (size_t)(page_size_in_bytes));
+#define MEMCOPY_NO_WORDS(to, from, no_of_words) \
+ memcpy((to), (void*)(from), (size_t)(no_of_words << 2));
+
+template <class T>
+struct Ptr {
+ T * p;
+ Uint32 i;
+ inline bool isNull() const { return i == RNIL; }
+ inline void setNull() { i = RNIL; }
+};
+
+template <class T>
+struct ConstPtr {
+ const T * p;
+ Uint32 i;
+ inline bool isNull() const { return i == RNIL; }
+ inline void setNull() { i = RNIL; }
+};
+
+#endif
diff --git a/storage/ndb/src/kernel/vm/testCopy/Makefile b/storage/ndb/src/kernel/vm/testCopy/Makefile
new file mode 100644
index 00000000000..5abd93eb74f
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testCopy/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := util
+
+BIN_TARGET := testCopy
+
+SOURCES = testCopy.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/vm/testCopy/rr.cpp b/storage/ndb/src/kernel/vm/testCopy/rr.cpp
new file mode 100644
index 00000000000..1e8305dfe4c
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testCopy/rr.cpp
@@ -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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <sched.h>
+
+int
+main(int argc, char * const argv[]){
+ struct sched_param p;
+ p.sched_priority = 1;
+
+ int ret = sched_setscheduler(getpid(), SCHED_RR, &p);
+ printf("ref = %d\n", ret);
+
+ execv(argv[1], &argv[1]);
+ return 0;
+}
diff --git a/storage/ndb/src/kernel/vm/testCopy/testCopy.cpp b/storage/ndb/src/kernel/vm/testCopy/testCopy.cpp
new file mode 100644
index 00000000000..78a1dab2619
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testCopy/testCopy.cpp
@@ -0,0 +1,341 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+#ifdef __NDB_FORTE6
+#define HAND
+bool hand = true;
+#else
+bool hand = false;
+#endif
+
+struct Data7 {
+ Uint32 data[7];
+
+#ifdef HAND
+ inline Data7& operator=(const Data7 & o){
+ Uint32 t0 = o.data[0];
+ Uint32 t1 = o.data[1];
+ Uint32 t2 = o.data[2];
+ Uint32 t3 = o.data[3];
+ Uint32 t4 = o.data[4];
+ Uint32 t5 = o.data[5];
+ Uint32 t6 = o.data[6];
+ data[0] = t0;
+ data[1] = t1;
+ data[2] = t2;
+ data[3] = t3;
+ data[4] = t4;
+ data[5] = t5;
+ data[6] = t6;
+ return * this;
+ }
+#endif
+};
+
+struct Data25 {
+ Uint32 data[25];
+};
+
+struct TestSignal {
+
+ Data7 head;
+ Data25 data;
+};
+
+Uint32 g_time = 3000;
+Uint32 g_count = 8*2048;
+
+TestSignal g_signal;
+TestSignal * g_jobBuffer;
+
+template<Uint32 LEN>
+inline
+void
+MEMCOPY(Uint32 * to, const Uint32 * from){
+ Uint32 t0 ;
+ Uint32 t1 ;
+ Uint32 t2 ;
+ Uint32 t3 ;
+ Uint32 len = LEN;
+ while(len > 4){
+ t0 = from[0];
+ t1 = from[1];
+ t2 = from[2];
+ t3 = from[3];
+
+ to[0] = t0;
+ to[1] = t1;
+ to[2] = t2;
+ to[3] = t3;
+
+
+ to += 4;
+ from += 4;
+ len -= 4;
+ }
+
+ //ndbout_c("len = %d", len);
+
+ t0 = from[0];
+ t1 = from[1];
+ t2 = from[2];
+ switch(len & 3){
+ case 3:
+ //ndbout_c("3");
+ to[2] = t2;
+ case 2:
+ //ndbout_c("2");
+ to[1] = t1;
+ case 1:
+ //ndbout_c("1");
+ to[0] = t0;
+ }
+
+}
+
+inline
+void
+MEMCOPY_NO_WORDS(Uint32 * to, const Uint32 * from, Uint32 len){
+ Uint32 t0 ;
+ Uint32 t1 ;
+ Uint32 t2 ;
+ Uint32 t3 ;
+ while(len > 4){
+ t0 = from[0];
+ t1 = from[1];
+ t2 = from[2];
+ t3 = from[3];
+
+ to[0] = t0;
+ to[1] = t1;
+ to[2] = t2;
+ to[3] = t3;
+
+
+ to += 4;
+ from += 4;
+ len -= 4;
+ }
+
+ //ndbout_c("len = %d", len);
+
+ t0 = from[0];
+ t1 = from[1];
+ t2 = from[2];
+ switch(len & 3){
+ case 3:
+ //ndbout_c("3");
+ to[2] = t2;
+ case 2:
+ //ndbout_c("2");
+ to[1] = t1;
+ case 1:
+ //ndbout_c("1");
+ to[0] = t0;
+ }
+}
+
+inline
+void
+copy1(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+ Uint32 t0 = ts.head.data[0];
+ Uint32 t1 = ts.head.data[1];
+ Uint32 t2 = ts.head.data[2];
+ Uint32 t3 = ts.head.data[3];
+ Uint32 t4 = ts.head.data[4];
+ Uint32 t5 = ts.head.data[5];
+ Uint32 t6 = ts.head.data[6];
+
+ dst.head.data[0] = t0;
+ dst.head.data[1] = t1;
+ dst.head.data[2] = t2;
+ dst.head.data[3] = t3;
+ dst.head.data[4] = t4;
+ dst.head.data[5] = t5;
+ dst.head.data[6] = t6;
+}
+
+
+
+inline
+void
+copy2(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+ Uint32 t0 = ts.head.data[0];
+ Uint32 t1 = ts.head.data[1];
+ Uint32 t2 = ts.head.data[2];
+ Uint32 t3 = ts.head.data[3];
+
+ dst.head.data[0] = t0;
+ dst.head.data[1] = t1;
+ dst.head.data[2] = t2;
+ dst.head.data[3] = t3;
+
+ Uint32 t4 = ts.head.data[4];
+ Uint32 t5 = ts.head.data[5];
+ Uint32 t6 = ts.head.data[6];
+
+ dst.head.data[4] = t4;
+ dst.head.data[5] = t5;
+ dst.head.data[6] = t6;
+}
+
+inline
+void
+copy3(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+ dst.head = ts.head;
+}
+
+inline
+void
+copy4(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+ memcpy(&dst.head.data[0], &ts.head.data[0], sizeof(Data7));
+}
+
+inline
+void
+copy5(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+ MEMCOPY_NO_WORDS(&dst.head.data[0], &ts.head.data[0], 7);
+}
+
+inline
+void
+copy6(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+ MEMCOPY<7>(&dst.head.data[0], &ts.head.data[0]);
+}
+
+inline
+void
+copy7(Uint32 i, TestSignal & ts){
+ TestSignal & dst = g_jobBuffer[i];
+
+#if (__GNUC__ >= 3 ) || (__GNUC__ == 2 && __GNUC_MINOR >= 95)
+ __builtin_memcpy(&dst.head.data[0], &ts.head.data[0], sizeof(Data7));
+#else
+ dst.head = ts.head;
+#endif
+}
+
+template<void (* C)(Uint32 i, TestSignal & ts)>
+int
+doTime(Uint32 ms){
+
+ Uint64 ms1, ms2;
+ const Uint32 count = g_count;
+ for(Uint32 i = 0; i<count; i++)
+ C(i, g_signal);
+ for(Uint32 i = 0; i<count; i++)
+ C(i, g_signal);
+
+ Uint32 laps = 0;
+
+ ms1 = NdbTick_CurrentMillisecond();
+ do {
+ for(int j = 100; j>= 0; j--)
+ for(Uint32 i = 0; i<count; i++){
+ C(i, g_signal);
+ }
+ ms2 = NdbTick_CurrentMillisecond();
+ laps += 100;
+ } while((ms2 - ms1) < ms);
+
+ return laps;
+}
+
+
+template<void (* C)(Uint32 i, TestSignal & ts)>
+void doCopyLap(Uint32 laps, const char * title){
+
+ Uint64 ms1, ms2;
+ const Uint32 count = g_count;
+ for(Uint32 i = 0; i<count; i++)
+ C(i, g_signal);
+ laps--;
+ for(Uint32 i = 0; i<count; i++)
+ C(i, g_signal);
+ laps--;
+
+ Uint32 div = laps;
+
+ ms1 = NdbTick_CurrentMillisecond();
+ while(laps > 0){
+ for(Uint32 i = 0; i<count; i++){
+#if (__GNUC__ == 3 && __GNUC_MINOR >= 1)
+ _builtin_prefetch(&g_jobBuffer[i], 1, 0);
+#endif
+ C(i, g_signal);
+ }
+ laps--;
+ }
+ ms2 = NdbTick_CurrentMillisecond();
+
+ ms2 -= ms1;
+ Uint32 diff = ms2;
+ ndbout_c("%s : %d laps in %d millis => %d copies/sec",
+ title, div, diff, (1000*div*g_count+(diff/2))/diff);
+}
+
+int
+main(int argc, const char ** argv){
+
+ if(argc > 1)
+ g_count = atoi(argv[1]);
+
+ if(argc > 2)
+ g_time = atoi(argv[2]);
+
+ ndbout_c("Using %d entries => %d kB ",
+ g_count,
+ g_count * sizeof(TestSignal) / 1024);
+ ndbout_c("Testing for %d ms", g_time);
+
+ ndbout_c("Using %s copy-constructor",
+ (hand ? "hand written" : "compiler generated"));
+
+ g_jobBuffer = new TestSignal[g_count + 1];
+ for(int i = 0; i<10; i++)
+ memset(g_jobBuffer, 0, g_count * sizeof(TestSignal));
+
+ int laps = doTime<copy2>(g_time);
+ ndbout_c("Laps = %d", laps);
+
+ doCopyLap<copy2>(laps, "4 t-variables");
+ doCopyLap<copy3>(laps, "copy constr. ");
+ doCopyLap<copy1>(laps, "7 t-variables");
+ doCopyLap<copy4>(laps, "mem copy ");
+ doCopyLap<copy5>(laps, "mem copy hand");
+ doCopyLap<copy6>(laps, "mem copy temp");
+ doCopyLap<copy7>(laps, "mem copy gcc ");
+
+ delete[] g_jobBuffer;
+ return 0;
+}
diff --git a/storage/ndb/src/kernel/vm/testDataBuffer/Makefile b/storage/ndb/src/kernel/vm/testDataBuffer/Makefile
new file mode 100644
index 00000000000..693989dfe3c
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testDataBuffer/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := testKernelDataBuffer
+BIN_TARGET_ARCHIVES := general portlib
+
+SOURCES = testDataBuffer.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/vm/testDataBuffer/testDataBuffer.cpp b/storage/ndb/src/kernel/vm/testDataBuffer/testDataBuffer.cpp
new file mode 100644
index 00000000000..5ba59418223
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testDataBuffer/testDataBuffer.cpp
@@ -0,0 +1,188 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbTick.h>
+#include <DataBuffer.hpp>
+
+#undef test
+
+struct Buffer {
+ Buffer(Uint32 size){ m_sz = size; buffer = new Uint32[m_sz]; m_len = 0;}
+ ~Buffer(){ delete [] buffer;}
+
+ Uint32 m_sz;
+ Uint32 m_len;
+ Uint32 * buffer;
+};
+
+inline
+void
+require(bool b){
+ if(!b)
+ abort();
+}
+
+template<Uint32 sz>
+void
+compare(DataBuffer<sz> & db, Buffer & buf){
+ typename DataBuffer<sz>::DataBufferIterator it;
+ db.first(it);
+ for(Uint32 i = 0; i<buf.m_len; i++){
+ if(buf.buffer[i] != * it.data){
+ db.print(stdout);
+ abort();
+ }
+ db.next(it);
+ }
+
+ for(Uint32 i = 0; i<buf.m_len; i++){
+ if(!db.position(it, i))
+ abort();
+ if(buf.buffer[i] != * it.data){
+ db.print(stdout);
+ abort();
+ }
+ }
+}
+
+template<Uint32 sz>
+void
+test(Uint32 loops, Uint32 iter){
+
+ ndbout_c("DataBuffer<%d> loops=%d iter=%d", sz, loops, iter);
+
+ while(loops-- > 0){
+ Uint32 size = sz*((10 + (rand() % (10 * sz)) + sz - 1)/sz);
+
+ typename DataBuffer<sz>::DataBufferPool thePool;
+ typename DataBuffer<sz>::DataBufferIterator it;
+ DataBuffer<sz> db(thePool);
+
+ thePool.setSize((size + sz - 1) / sz);
+ Buffer buf(size);
+
+ bool testOverRun = true;
+
+ for(Uint32 i = 0; i<iter; i++){
+ Uint32 c = (rand() % (testOverRun ? 7 : 4));
+ Uint32 free = (size - db.getSize());
+ Uint32 alloc = 0;
+ if(free == 0){
+ c = (testOverRun ? c : 0);
+ if(c >= 1 && c <= 3)
+ c += 3;
+ }
+
+ if(free <= 1)
+ alloc = 1;
+ else
+ alloc = 1 + (rand() % (free - 1));
+
+ //ndbout_c("iter=%d case=%d free=%d alloc=%d", i, c, free, alloc);
+ switch(c){
+ case 0: // Release
+ db.first(it);
+ for(; !it.curr.isNull(); db.next(it))
+ * it.data = 0;
+
+ db.release();
+ buf.m_len = 0;
+ break;
+ case 1:{ // Append (success)
+ for(Uint32 i = 0; i<alloc; i++)
+ buf.buffer[buf.m_len + i] = buf.m_len + i;//rand();
+
+ require(db.append(&buf.buffer[buf.m_len], alloc));
+ buf.m_len += alloc;
+ break;
+ }
+ case 2: { // Seize(1) (success)
+ for(Uint32 i = 0; i<alloc; i++){
+ buf.buffer[buf.m_len + i] = buf.m_len + i;//rand();
+ require(db.seize(1));
+ require(db.position(it, db.getSize()-1));
+ * it.data = buf.buffer[buf.m_len + i];
+ }
+ buf.m_len += alloc;
+ break;
+ }
+ case 3: { // Seize(n) (success)
+ for(Uint32 i = 0; i<alloc; i++){
+ buf.buffer[buf.m_len + i] = buf.m_len + i;//rand();
+ }
+ Uint32 pos = db.getSize();
+ require(db.seize(alloc));
+ require(db.position(it, pos));
+ for(Uint32 i = 0; i<alloc; i++){
+ * it.data = buf.buffer[buf.m_len + i];
+ db.next(it);
+ }
+ buf.m_len += alloc;
+ break;
+ }
+ case 4: { // Append fail
+ require(!db.append(buf.buffer, alloc + free));
+ require(db.getSize() == 0);
+ buf.m_len = 0;
+ break;
+ }
+ case 5: { // Seize(1) - fail
+ for(Uint32 i = 0; i<free; i++){
+ require(db.seize(1));
+ }
+
+ require(!db.seize(1));
+ require(db.getSize() == 0);
+ buf.m_len = 0;
+ break;
+ }
+ case 6: { // Seize(n) - fail
+ require(!db.seize(alloc + free));
+ require(db.getSize() == 0);
+ buf.m_len = 0;
+ break;
+ }
+ }
+ compare(db, buf);
+ }
+ }
+}
+
+int
+main(void){
+
+ srand(NdbTick_CurrentMillisecond());
+
+
+ test<1>(1000, 1000);
+ test<11>(1000, 1000);
+ test<15>(1000, 1000);
+ test<16>(1000, 1000);
+ test<17>(1000, 1000);
+#if 0
+#endif
+ return 0;
+}
+
+void
+ErrorReporter::handleAssert(const char * msg, const char * file, int line)
+{
+ ndbout << "ErrorReporter::handleAssert activated - "
+ << " line= " << line << endl;
+ abort();
+}
diff --git a/storage/ndb/src/kernel/vm/testLongSig/Makefile b/storage/ndb/src/kernel/vm/testLongSig/Makefile
new file mode 100644
index 00000000000..ecf33dca109
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testLongSig/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := signalsender
+
+BIN_TARGET := testLongSig
+
+SOURCES = testLongSig.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/vm/testLongSig/testLongSig.cpp b/storage/ndb/src/kernel/vm/testLongSig/testLongSig.cpp
new file mode 100644
index 00000000000..1d1fb8ebc82
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testLongSig/testLongSig.cpp
@@ -0,0 +1,333 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <editline/editline.h>
+#include <SignalSender.hpp>
+
+void
+print_help(){
+ ndbout << "The test menu" << endl;
+ ndbout << "1 - Sending of long signals w/ segmented sections" << endl;
+ ndbout << "2 - As 1 but using receiver group" << endl;
+ ndbout << "3 - Sending of long signals w/ linear sections" << endl;
+ ndbout << "4 - As 3 but using receiver group" << endl;
+ ndbout << "5 - Sending of manually fragmented signals w/ segmented sections"
+ << endl;
+ ndbout << "6 - As 5 but using receiver group" << endl;
+ ndbout << "7 - Sending of manually fragmented signals w/ linear sections"
+ << endl;
+ ndbout << "8 - As but using receiver group" << endl;
+
+ ndbout << "9 - Sending of CONTINUEB fragmented signals w/ segmented sections"
+ << endl;
+ ndbout << "10 - As 9 but using receiver group" << endl;
+ ndbout << "11 - Sending of CONTINUEB fragmented signals w/ linear sections"
+ << endl;
+ ndbout << "12 - As but using receiver group" << endl;
+ ndbout << "13 - Send 100 * 1000 25 len signals wo/ sections" << endl;
+ ndbout << "r - Recive signal from anyone" << endl;
+ ndbout << "a - Run tests 1 - 12 with variable sizes - 10 loops" << endl;
+ ndbout << "b - Run tests 1 - 12 with variable sizes - 100 loops" << endl;
+ ndbout << "c - Run tests 1 - 12 with variable sizes - 1000k loops" << endl;
+}
+
+void runTest(SignalSender &, Uint32 i, bool verbose);
+
+static
+int
+randRange(Uint32 min, Uint32 max){
+ float r = rand();
+ float f = (max - min + 1);
+ float d = (float)RAND_MAX + 1.0;
+ return min + (int)(f * r / d);
+}
+
+static
+int
+randRange(const Uint32 odds[], Uint32 count){
+ Uint32 val = randRange((Uint32)0, 100);
+
+ Uint32 i = 0;
+ Uint32 sum = 0;
+ while(sum <= val && i < count){
+ sum += odds[i];
+ i++;
+ }
+ return i - 1;
+}
+
+int
+main(void){
+
+ srand(NdbTick_CurrentMillisecond());
+#if 0
+ for(int i = 0; i<100; i++)
+ ndbout_c("randRange(0, 3) = %d", randRange(0, 3));
+ return 0;
+#endif
+ SignalSender ss;
+
+ ndbout << "Connecting...";
+ if(!ss.connect(30)){
+ ndbout << "failed" << endl << "Exiting" << endl;
+ return 0;
+ }
+ ndbout << "done" << endl;
+ ndbout_c("Connected as block=%d node=%d",
+ refToBlock(ss.getOwnRef()), refToNode(ss.getOwnRef()));
+
+ Uint32 data[25];
+ Uint32 sec0[70];
+ Uint32 sec1[123];
+ Uint32 sec2[10];
+
+ data[0] = ss.getOwnRef();
+ data[1] = 1;
+ data[2] = 76;
+ data[3] = 1;
+ data[4] = 1;
+ data[5] = 70;
+ data[6] = 123;
+ data[7] = 10;
+ const Uint32 theDataLen = 18;
+
+ for(Uint32 i = 0; i<70; i++)
+ sec0[i] = i;
+
+ for(Uint32 i = 0; i<123; i++)
+ sec1[i] = 70+i;
+
+ for(Uint32 i = 0; i<10; i++)
+ sec2[i] = (i + 1)*(i + 1);
+
+ SimpleSignal signal1;
+ signal1.set(ss, 0, CMVMI, GSN_TESTSIG, theDataLen + 2);
+ signal1.header.m_noOfSections = 1;
+ signal1.header.m_fragmentInfo = 1;
+
+ memcpy(&signal1.theData[0], data, 4 * theDataLen );
+ signal1.theData[theDataLen + 0] = 0;
+ signal1.theData[theDataLen + 1] = 7; // FragmentId
+
+ signal1.ptr[0].sz = 60;
+ signal1.ptr[0].p = &sec0[0];
+
+ SimpleSignal signal2;
+
+ Uint32 idx = 0;
+ memcpy(&signal2.theData[0], data, 4 * theDataLen );
+ signal2.theData[theDataLen + idx] = 0; idx++;
+ signal2.theData[theDataLen + idx] = 1; idx++;
+ //signal2.theData[theDataLen + idx] = 2; idx++;
+ signal2.theData[theDataLen + idx] = 7; idx++; // FragmentId
+
+ signal2.set(ss, 0, CMVMI, GSN_TESTSIG, theDataLen + idx);
+ signal2.header.m_fragmentInfo = 3;
+ signal2.header.m_noOfSections = idx - 1;
+
+ signal2.ptr[0].sz = 10;
+ signal2.ptr[0].p = &sec0[60];
+
+ signal2.ptr[1].sz = 123;
+ signal2.ptr[1].p = &sec1[0];
+
+ signal2.ptr[2].sz = 10;
+ signal2.ptr[2].p = &sec2[0];
+
+ char * buf;
+ while((buf = readline("Enter command: "))){
+ add_history(buf);
+ data[1] = atoi(buf);
+ if(strcmp(buf, "r") == 0){
+ SimpleSignal * ret1 = ss.waitFor();
+ (* ret1).print();
+ delete ret1;
+ continue;
+ }
+ if(strcmp(buf, "a") == 0){
+ runTest(ss, 10, true);
+ print_help();
+ continue;
+ }
+ if(strcmp(buf, "b") == 0){
+ runTest(ss, 100, false);
+ print_help();
+ continue;
+ }
+ if(strcmp(buf, "c") == 0){
+ runTest(ss, 1000000, false);
+ print_help();
+ continue;
+ }
+
+ if(data[1] >= 1 && data[1] <= 12){
+ Uint32 nodeId = ss.getAliveNode();
+ ndbout_c("Sending 2 fragmented to node %d", nodeId);
+ ss.sendSignal(nodeId, &signal1);
+ ss.sendSignal(nodeId, &signal2);
+
+ if(data[1] >= 5){
+ continue;
+ }
+ ndbout_c("Waiting for signal from %d", nodeId);
+
+ SimpleSignal * ret1 = ss.waitFor((Uint16)nodeId);
+ (* ret1).print();
+ Uint32 count = ret1->theData[4] - 1;
+ delete ret1;
+ while(count > 0){
+ ndbout << "Waiting for " << count << " signals... ";
+ SimpleSignal * ret1 = ss.waitFor();
+ ndbout_c("received from node %d",
+ refToNode(ret1->header.theSendersBlockRef));
+ (* ret1).print();
+ delete ret1;
+ count--;
+ }
+ } else if (data[1] == 13) {
+ const Uint32 count = 3500;
+ const Uint32 loop = 1000;
+
+ signal1.set(ss, 0, CMVMI, GSN_TESTSIG, 25);
+ signal1.header.m_fragmentInfo = 0;
+ signal1.header.m_noOfSections = 0;
+ signal1.theData[1] = 14;
+ signal1.theData[3] = 0; // Print
+ signal1.theData[8] = count;
+ signal1.theData[9] = loop;
+ Uint32 nodeId = ss.getAliveNode();
+ ndbout_c("Sending 25 len signal to node %d", nodeId);
+ ss.sendSignal(nodeId, &signal1);
+
+ Uint32 total;
+ {
+ SimpleSignal * ret1 = ss.waitFor((Uint16)nodeId);
+ ndbout_c("received from node %d",
+ refToNode(ret1->header.theSendersBlockRef));
+ total = ret1->theData[10] - 1;
+ delete ret1;
+ }
+
+ do {
+ ndbout << "Waiting for " << total << " signals... " << flush;
+ SimpleSignal * ret1 = ss.waitFor((Uint16)nodeId);
+ ndbout_c("received from node %d",
+ refToNode(ret1->header.theSendersBlockRef));
+ delete ret1;
+ total --;
+ } while(total > 0);
+ } else {
+ print_help();
+ }
+ }
+ ndbout << "Exiting" << endl;
+};
+
+void
+runTest(SignalSender & ss, Uint32 count, bool verbose){
+
+ SimpleSignal sig;
+ Uint32 sec0[256];
+ Uint32 sec1[256];
+ Uint32 sec2[256];
+ for(Uint32 i = 0; i<256; i++){
+ sec0[i] = i;
+ sec1[i] = i + i;
+ sec2[i] = i * i;
+ }
+
+ sig.theData[0] = ss.getOwnRef();
+ sig.theData[1] = 1; // TestType
+ sig.theData[2] = 128; // FragSize
+ sig.theData[3] = 0; // Print
+ sig.theData[4] = 1; // RetCount
+
+ sig.ptr[0].p = &sec0[0];
+ sig.ptr[1].p = &sec1[0];
+ sig.ptr[2].p = &sec2[0];
+
+ for(unsigned loop = 0; loop < count; loop++){
+ const Uint32 odds[] = { 5, 40, 30, 25 };
+ const Uint32 secs = randRange(odds, 4);
+ sig.ptr[0].sz = randRange(1, 256);
+ sig.ptr[1].sz = randRange(1, 256);
+ sig.ptr[2].sz = randRange(1, 256);
+ sig.header.m_noOfSections = secs;
+ const Uint32 len = 5 + (secs > 0 ? 1 : 0) * (25 - 5 - 7);
+ sig.set(ss, 0, CMVMI, GSN_TESTSIG, len);
+ ndbout << "Loop " << loop << " #secs = " << secs << " sizes = [ ";
+ unsigned min = 256;
+ unsigned max = 0;
+ unsigned sum = 0;
+ for(unsigned i = 0; i<secs; i++){
+ const Uint32 sz = sig.ptr[i].sz;
+ ndbout << sz << " ";
+ min = (min < sz ? min : sz);
+ max = (max > sz ? max : sz);
+ sum += sz;
+ sig.theData[5+i] = sz;
+ }
+ ndbout_c("] len = %d", len);
+ for(int test = 1; test <= 12; test++){
+ sig.theData[1] = test;
+ Uint32 nodeId = ss.getAliveNode();
+ if(verbose){
+ ndbout << " Test " << test << " node " << nodeId << "...";
+ fflush(stdout);
+ }
+ SendStatus r = ss.sendSignal(nodeId, &sig);
+ assert(r == SEND_OK);
+ if(test < 5){
+ SimpleSignal * ret1 = ss.waitFor((Uint16)nodeId);
+ Uint32 count = ret1->theData[4] - 1;
+ delete ret1;
+
+ while(count > 0){
+ SimpleSignal * ret1 = ss.waitFor();
+ delete ret1;
+ count--;
+ }
+ if(verbose)
+ ndbout << "done" << endl;
+ } else {
+ Uint32 nodes = ss.getNoOfConnectedNodes();
+ Uint32 sum2 = 0;
+ if((test & 1) == 1)
+ nodes = 1;
+ while(nodes > 0){
+ SimpleSignal * ret = ss.waitFor();
+ if(ret->header.m_fragmentInfo == 0){
+ for(Uint32 i = 0; i<ret->header.m_noOfSections; i++)
+ sum2 += ret->ptr[i].sz;
+ } else {
+ for(Uint32 i = 0; i<ret->header.m_noOfSections; i++)
+ if(ret->theData[i] != 3)
+ sum2 += ret->ptr[i].sz;
+ }
+ if(ret->header.m_fragmentInfo == 0 ||
+ ret->header.m_fragmentInfo == 3){
+ nodes--;
+ }
+ delete ret;
+ }
+ if(verbose)
+ ndbout_c("done sum=%d sum2=%d", sum, sum2);
+ }
+ }
+ }
+}
diff --git a/storage/ndb/src/kernel/vm/testSimplePropertiesSection/Makefile b/storage/ndb/src/kernel/vm/testSimplePropertiesSection/Makefile
new file mode 100644
index 00000000000..fb3aea00507
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testSimplePropertiesSection/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := testSimplePropertiesSection
+BIN_TARGET_ARCHIVES := general portlib
+
+SOURCES = test.cpp ../SimplePropertiesSection.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/kernel/vm/testSimplePropertiesSection/test.cpp b/storage/ndb/src/kernel/vm/testSimplePropertiesSection/test.cpp
new file mode 100644
index 00000000000..e16870edf11
--- /dev/null
+++ b/storage/ndb/src/kernel/vm/testSimplePropertiesSection/test.cpp
@@ -0,0 +1,171 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbTick.h>
+#include <TransporterDefinitions.hpp>
+#include <SimpleProperties.hpp>
+#include <LongSignal.hpp>
+
+#undef test
+
+struct Buffer {
+ Buffer(Uint32 size){ m_sz = size; buffer = new Uint32[m_sz]; m_len = 0;}
+ ~Buffer(){ delete [] buffer;}
+
+ Uint32 m_sz;
+ Uint32 m_len;
+ Uint32 * buffer;
+};
+
+inline
+void
+require(bool b){
+ if(!b)
+ abort();
+}
+
+#define relSz(x) ((x + SectionSegment::DataLength - 1) / SectionSegment::DataLength)
+
+void
+release(SectionSegmentPool & thePool, SegmentedSectionPtr & ptr){
+ const Uint32 sz = relSz(ptr.sz);
+ thePool.releaseList(sz,
+ ptr.i,
+ ptr.p->m_lastSegment);
+}
+
+void
+compare(SimplePropertiesSectionReader & db, Buffer & buf){
+
+ {
+ bool fail = false;
+ db.reset();
+ for(Uint32 i = 0; i<buf.m_len; i++){
+ Uint32 tmp;
+ if(!db.getWord(&tmp)){
+ ndbout_c("getWord(...) failed i=%d size=%d", i, buf.m_len);
+ abort();
+ }
+
+ if(tmp != buf.buffer[i]){
+ ndbout_c("getWord(...)=%d != buf[%d]=%d size=%d", tmp, i,
+ buf.buffer[i], buf.m_len);
+ fail = true;
+ }
+ }
+ require(!fail);
+ }
+
+ {
+ db.reset();
+ Buffer buf2(buf.m_sz);
+ if(!db.getWords(buf2.buffer, buf.m_len))
+ abort();
+
+ bool fail = false;
+ for(Uint32 i = 0; i<buf.m_len; i++){
+ if(buf.buffer[i] != buf2.buffer[i]){
+ ndbout_c("getWords(...) buf[%d] != buf2[%d] size=%d", i, i, buf.m_len);
+ fail = true;
+ }
+ }
+ require(!fail);
+ }
+}
+
+
+void
+test(Uint32 sz, Uint32 loops, Uint32 iter){
+
+ ndbout_c("SimplePropertiesSection sz=%d loops=%d iter=%d", sz, loops, iter);
+
+ while(loops-- > 0){
+ Uint32 size = sz*((10 + (rand() % (10 * sz)) + sz - 1)/sz);
+
+ Buffer buf(size);
+ SectionSegmentPool thePool; thePool.setSize(size);
+
+ for(Uint32 i = 0; i<iter; i++){
+ Uint32 c = 0 + (rand() % (2));
+
+ const Uint32 alloc = 1 + (rand() % (size - 1));
+ SegmentedSectionPtr dst;
+
+ if(0)
+ ndbout_c("size: %d loops: %d iter: %d c=%d alloc=%d",
+ size, loops, i, c, alloc);
+
+ switch(c){
+ case 0:{
+ for(Uint32 i = 0; i<alloc; i++)
+ buf.buffer[i] = i; //rand();
+ buf.m_len = alloc;
+
+ SimplePropertiesSectionWriter w(thePool);
+ for(Uint32 i = 0; i<alloc; i++){
+ w.putWord(buf.buffer[i]);
+ }
+ w.getPtr(dst);
+ break;
+ }
+ case 1:{
+ for(Uint32 i = 0; i<alloc; i++)
+ buf.buffer[i] = i; //rand();
+ buf.m_len = alloc;
+
+ SimplePropertiesSectionWriter w(thePool);
+ Uint32 i = 0;
+ while(i < alloc){
+ Uint32 sz = rand() % (alloc - i + 1);
+ w.putWords(&buf.buffer[i], sz);
+ i += sz;
+ }
+ w.getPtr(dst);
+ break;
+ }
+ case 2:{
+ break;
+ }
+ }
+ SimplePropertiesSectionReader r(dst, thePool);
+ compare(r, buf);
+ release(thePool, dst);
+ require(thePool.getSize() == thePool.getNoOfFree());
+ }
+ }
+}
+
+int
+main(void){
+
+ srand(NdbTick_CurrentMillisecond());
+
+ //test( 1, 1000, 1000);
+ test(54, 1000, 1000);
+ test(59, 1000, 1000);
+ test(60, 1000, 1000);
+ test(61, 1000, 1000);
+ return 0;
+}
+
+void
+ErrorReporter::handleAssert(const char * msg, const char * file, int line)
+{
+ ndbout << "ErrorReporter::handleAssert activated - "
+ << " line= " << line << endl;
+ abort();
+}
diff --git a/storage/ndb/src/mgmapi/LocalConfig.cpp b/storage/ndb/src/mgmapi/LocalConfig.cpp
new file mode 100644
index 00000000000..75ad8b40a1f
--- /dev/null
+++ b/storage/ndb/src/mgmapi/LocalConfig.cpp
@@ -0,0 +1,319 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "LocalConfig.hpp"
+#include <NdbEnv.h>
+#include <NdbConfig.h>
+#include <NdbAutoPtr.hpp>
+#include <NdbMem.h>
+
+LocalConfig::LocalConfig(){
+ error_line = 0; error_msg[0] = 0;
+ _ownNodeId= 0;
+}
+
+bool
+LocalConfig::init(const char *connectString,
+ const char *fileName)
+{
+ DBUG_ENTER("LocalConfig::init");
+ /**
+ * Escalation:
+ * 1. Check connectString
+ * 2. Check given filename
+ * 3. Check environment variable NDB_CONNECTSTRING
+ * 4. Check Ndb.cfg in NDB_HOME
+ * 5. Check Ndb.cfg in cwd
+ * 6. Check defaultConnectString
+ */
+
+ _ownNodeId= 0;
+
+ //1. Check connectString
+ if(connectString != 0 && connectString[0] != 0){
+ if(readConnectString(connectString, "connect string")){
+ if (ids.size())
+ DBUG_RETURN(true);
+ // only nodeid given, continue to find hosts
+ } else
+ DBUG_RETURN(false);
+ }
+
+ //2. Check given filename
+ if (fileName && strlen(fileName) > 0) {
+ bool fopenError;
+ if(readFile(fileName, fopenError)){
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+ }
+
+ //3. Check environment variable
+ char buf[255];
+ if(NdbEnv_GetEnv("NDB_CONNECTSTRING", buf, sizeof(buf)) &&
+ strlen(buf) != 0){
+ if(readConnectString(buf, "NDB_CONNECTSTRING")){
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+ }
+
+ //4. Check Ndb.cfg in NDB_HOME
+ {
+ bool fopenError;
+ char *buf= NdbConfig_NdbCfgName(1 /*true*/);
+ NdbAutoPtr<char> tmp_aptr(buf);
+ if(readFile(buf, fopenError))
+ DBUG_RETURN(true);
+ if (!fopenError)
+ DBUG_RETURN(false);
+ }
+
+ //5. Check Ndb.cfg in cwd
+ {
+ bool fopenError;
+ char *buf= NdbConfig_NdbCfgName(0 /*false*/);
+ NdbAutoPtr<char> tmp_aptr(buf);
+ if(readFile(buf, fopenError))
+ DBUG_RETURN(true);
+ if (!fopenError)
+ DBUG_RETURN(false);
+ }
+
+ //7. Check
+ {
+ char buf[256];
+ BaseString::snprintf(buf, sizeof(buf), "host=localhost:%s", NDB_PORT);
+ if(readConnectString(buf, "default connect string"))
+ DBUG_RETURN(true);
+ }
+
+ setError(0, "");
+
+ DBUG_RETURN(false);
+}
+
+LocalConfig::~LocalConfig(){
+}
+
+void LocalConfig::setError(int lineNumber, const char * _msg) {
+ error_line = lineNumber;
+ strncpy(error_msg, _msg, sizeof(error_msg));
+}
+
+void LocalConfig::printError() const {
+ ndbout << "Configuration error" << endl;
+ if (error_line)
+ ndbout << "Line: "<< error_line << ", ";
+ ndbout << error_msg << endl << endl;
+}
+
+void LocalConfig::printUsage() const {
+ ndbout << "This node needs information on how to connect"<<endl
+ << "to the NDB Management Server."<<endl
+ << "The information can be supplied in one of the following ways:"
+ << endl;
+
+ ndbout << "1. Put a Ndb.cfg file in the directory where you start"<<endl
+ << " the node. "<< endl
+ << " Ex: Ndb.cfg" << endl
+ << " | host=localhost:"<<NDB_PORT<<endl;
+
+ ndbout << "2. Use the environment variable NDB_CONNECTSTRING to "<<endl
+ << " provide this information." <<endl
+ << " Ex: " << endl
+ << " >export NDB_CONNECTSTRING=\"host=localhost:"<<NDB_PORT<<"\""
+ <<endl<<endl;
+}
+
+const char *nodeIdTokens[] = {
+ "OwnProcessId %i",
+ "nodeid=%i",
+ 0
+};
+
+const char *hostNameTokens[] = {
+ "host://%[^:]:%i",
+ "host=%[^:]:%i",
+ "mgmd=%[^:]:%i",
+ "%[^:^=^ ]:%i",
+ "%s %i",
+ 0
+};
+
+const char *fileNameTokens[] = {
+ "file://%s",
+ "file=%s",
+ 0
+};
+
+bool
+LocalConfig::parseNodeId(const char * buf){
+ for(int i = 0; nodeIdTokens[i] != 0; i++)
+ if (sscanf(buf, nodeIdTokens[i], &_ownNodeId) == 1)
+ return true;
+ return false;
+}
+
+bool
+LocalConfig::parseHostName(const char * buf){
+ char tempString[1024];
+ char tempString2[1024];
+ int port;
+ do {
+ for(int i = 0; hostNameTokens[i] != 0; i++) {
+ if (sscanf(buf, hostNameTokens[i], tempString, &port) == 2) {
+ MgmtSrvrId mgmtSrvrId;
+ mgmtSrvrId.type = MgmId_TCP;
+ mgmtSrvrId.name.assign(tempString);
+ mgmtSrvrId.port = port;
+ ids.push_back(mgmtSrvrId);
+ return true;
+ }
+ }
+ if (buf == tempString2)
+ break;
+ // try to add default port to see if it works
+ snprintf(tempString2, sizeof(tempString2),"%s:%s", buf, NDB_PORT);
+ buf= tempString2;
+ } while(1);
+ return false;
+}
+
+bool
+LocalConfig::parseFileName(const char * buf){
+ char tempString[1024];
+ for(int i = 0; fileNameTokens[i] != 0; i++) {
+ if (sscanf(buf, fileNameTokens[i], tempString) == 1) {
+ MgmtSrvrId mgmtSrvrId;
+ mgmtSrvrId.type = MgmId_File;
+ mgmtSrvrId.name.assign(tempString);
+ ids.push_back(mgmtSrvrId);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+LocalConfig::parseString(const char * connectString, BaseString &err){
+ char * for_strtok;
+ char * copy = strdup(connectString);
+ NdbAutoPtr<char> tmp_aptr(copy);
+
+ for (char *tok = strtok_r(copy,";,",&for_strtok); tok != 0;
+ tok = strtok_r(NULL, ";,", &for_strtok)) {
+ if (tok[0] == '#') continue;
+
+ if (!_ownNodeId) // only one nodeid definition allowed
+ if (parseNodeId(tok))
+ continue;
+ if (parseHostName(tok))
+ continue;
+ if (parseFileName(tok))
+ continue;
+
+ err.assfmt("Unexpected entry: \"%s\"", tok);
+ return false;
+ }
+
+ return true;
+}
+
+bool LocalConfig::readFile(const char * filename, bool &fopenError)
+{
+ char line[1024];
+
+ fopenError = false;
+
+ FILE * file = fopen(filename, "r");
+ if(file == 0){
+ BaseString::snprintf(line, sizeof(line),
+ "Unable to open local config file: %s", filename);
+ setError(0, line);
+ fopenError = true;
+ return false;
+ }
+
+ BaseString theString;
+
+ while(fgets(line, sizeof(line), file)){
+ BaseString tmp(line);
+ tmp.trim(" \t\n\r");
+ if(tmp.length() > 0 && tmp.c_str()[0] != '#'){
+ theString.append(tmp);
+ break;
+ }
+ }
+ while (fgets(line, sizeof(line), file)) {
+ BaseString tmp(line);
+ tmp.trim(" \t\n\r");
+ if(tmp.length() > 0 && tmp.c_str()[0] != '#'){
+ theString.append(";");
+ theString.append(tmp);
+ }
+ }
+
+ BaseString err;
+ bool return_value = parseString(theString.c_str(), err);
+
+ if (!return_value) {
+ BaseString tmp;
+ tmp.assfmt("Reading %s: %s", filename, err.c_str());
+ setError(0, tmp.c_str());
+ }
+
+ fclose(file);
+ return return_value;
+}
+
+bool
+LocalConfig::readConnectString(const char * connectString,
+ const char * info){
+ BaseString err;
+ bool return_value = parseString(connectString, err);
+ if (!return_value) {
+ BaseString err2;
+ err2.assfmt("Reading %d \"%s\": %s", info, connectString, err.c_str());
+ setError(0,err2.c_str());
+ }
+ return return_value;
+}
+
+char *
+LocalConfig::makeConnectString(char *buf, int sz)
+{
+ int p= BaseString::snprintf(buf,sz,"nodeid=%d", _ownNodeId);
+ if (p < sz)
+ for (unsigned i = 0; i < ids.size(); i++)
+ {
+ if (ids[i].type != MgmId_TCP)
+ continue;
+ int new_p= p+BaseString::snprintf(buf+p,sz-p,",%s:%d",
+ ids[i].name.c_str(), ids[i].port);
+ if (new_p < sz)
+ p= new_p;
+ else
+ {
+ buf[p]= 0;
+ break;
+ }
+ }
+ buf[sz-1]=0;
+ return buf;
+}
+
+template class Vector<MgmtSrvrId>;
diff --git a/storage/ndb/src/mgmapi/LocalConfig.hpp b/storage/ndb/src/mgmapi/LocalConfig.hpp
new file mode 100644
index 00000000000..c415ec1be91
--- /dev/null
+++ b/storage/ndb/src/mgmapi/LocalConfig.hpp
@@ -0,0 +1,68 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 LocalConfig_H
+#define LocalConfig_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+//****************************************************************************
+// Description: The class LocalConfig corresponds to the information possible
+// to give in the local configuration file.
+//*****************************************************************************
+
+enum MgmtSrvrId_Type {
+ MgmId_TCP = 0,
+ MgmId_File = 1
+};
+
+struct MgmtSrvrId {
+ MgmtSrvrId_Type type;
+ BaseString name;
+ unsigned int port;
+};
+
+struct LocalConfig {
+
+ int _ownNodeId;
+ Vector<MgmtSrvrId> ids;
+
+ int error_line;
+ char error_msg[256];
+
+ LocalConfig();
+ ~LocalConfig();
+ bool init(const char *connectString = 0,
+ const char *fileName = 0);
+
+ void printError() const;
+ void printUsage() const;
+
+ void setError(int lineNumber, const char * _msg);
+ bool readConnectString(const char *, const char *info);
+ bool readFile(const char * file, bool &fopenError);
+ bool parseLine(char * line, int lineNumber);
+
+ bool parseNodeId(const char *buf);
+ bool parseHostName(const char *buf);
+ bool parseFileName(const char *buf);
+ bool parseString(const char *buf, BaseString &err);
+ char * makeConnectString(char *buf, int sz);
+};
+
+#endif // LocalConfig_H
+
diff --git a/storage/ndb/src/mgmapi/Makefile.am b/storage/ndb/src/mgmapi/Makefile.am
new file mode 100644
index 00000000000..db730bf8c89
--- /dev/null
+++ b/storage/ndb/src/mgmapi/Makefile.am
@@ -0,0 +1,30 @@
+
+noinst_LTLIBRARIES = libmgmapi.la
+
+libmgmapi_la_SOURCES = mgmapi.cpp ndb_logevent.cpp mgmapi_configuration.cpp LocalConfig.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/include/mgmapi
+
+DEFS_LOC = -DNO_DEBUG_MESSAGES -DNDB_PORT="\"@ndb_port@\""
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_util.mk.am
+
+#ndbtest_PROGRAMS = ndb_test_mgmapi
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libmgmapi.dsp
+
+libmgmapi.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libmgmapi_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/mgmapi/mgmapi.cpp b/storage/ndb/src/mgmapi/mgmapi.cpp
new file mode 100644
index 00000000000..fe7909ba5a1
--- /dev/null
+++ b/storage/ndb/src/mgmapi/mgmapi.cpp
@@ -0,0 +1,2251 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_sys.h>
+
+#include <LocalConfig.hpp>
+#include <NdbAutoPtr.hpp>
+
+#include <NdbSleep.h>
+#include <NdbTCP.h>
+#include <mgmapi.h>
+#include <mgmapi_internal.h>
+#include <mgmapi_debug.h>
+#include "mgmapi_configuration.hpp"
+#include <socket_io.h>
+
+#include <NdbOut.hpp>
+#include <SocketServer.hpp>
+#include <SocketClient.hpp>
+#include <Parser.hpp>
+#include <OutputStream.hpp>
+#include <InputStream.hpp>
+#include <Base64.hpp>
+
+
+#define MGM_CMD(name, fun, desc) \
+ { name, \
+ 0, \
+ ParserRow<ParserDummy>::Cmd, \
+ ParserRow<ParserDummy>::String, \
+ ParserRow<ParserDummy>::Optional, \
+ ParserRow<ParserDummy>::IgnoreMinMax, \
+ 0, 0, \
+ fun, \
+ desc, 0 }
+
+#define MGM_ARG(name, type, opt, desc) \
+ { name, \
+ 0, \
+ ParserRow<ParserDummy>::Arg, \
+ ParserRow<ParserDummy>::type, \
+ ParserRow<ParserDummy>::opt, \
+ ParserRow<ParserDummy>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ desc, 0 }
+
+#define MGM_END() \
+ { 0, \
+ 0, \
+ ParserRow<ParserDummy>::Arg, \
+ ParserRow<ParserDummy>::Int, \
+ ParserRow<ParserDummy>::Optional, \
+ ParserRow<ParserDummy>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+class ParserDummy : private SocketServer::Session
+{
+public:
+ ParserDummy(NDB_SOCKET_TYPE sock);
+};
+
+ParserDummy::ParserDummy(NDB_SOCKET_TYPE sock) : SocketServer::Session(sock)
+{
+}
+
+typedef Parser<ParserDummy> Parser_t;
+
+#define NDB_MGM_MAX_ERR_DESC_SIZE 256
+
+struct ndb_mgm_handle {
+ int cfg_i;
+
+ int connected;
+ int last_error;
+ int last_error_line;
+ char last_error_desc[NDB_MGM_MAX_ERR_DESC_SIZE];
+ int read_timeout;
+ int write_timeout;
+
+ NDB_SOCKET_TYPE socket;
+
+ LocalConfig cfg;
+
+#ifdef MGMAPI_LOG
+ FILE* logfile;
+#endif
+};
+
+#define SET_ERROR(h, e, s) setError(h, e, __LINE__, s)
+
+static
+void
+setError(NdbMgmHandle h, int error, int error_line, const char * msg, ...){
+
+ h->last_error = error; \
+ h->last_error_line = error_line;
+
+ va_list ap;
+ va_start(ap, msg);
+ BaseString::vsnprintf(h->last_error_desc, sizeof(h->last_error_desc), msg, ap);
+ va_end(ap);
+}
+
+#define CHECK_HANDLE(handle, ret) \
+ if(handle == 0) { \
+ SET_ERROR(handle, NDB_MGM_ILLEGAL_SERVER_HANDLE, ""); \
+ return ret; \
+ }
+
+#define CHECK_CONNECTED(handle, ret) \
+ if (handle->connected != 1) { \
+ SET_ERROR(handle, NDB_MGM_SERVER_NOT_CONNECTED , ""); \
+ return ret; \
+ }
+
+#define CHECK_REPLY(reply, ret) \
+ if(reply == NULL) { \
+ SET_ERROR(handle, NDB_MGM_ILLEGAL_SERVER_REPLY, ""); \
+ return ret; \
+ }
+
+/*****************************************************************************
+ * Handles
+ *****************************************************************************/
+
+extern "C"
+NdbMgmHandle
+ndb_mgm_create_handle()
+{
+ DBUG_ENTER("ndb_mgm_create_handle");
+ NdbMgmHandle h =
+ (NdbMgmHandle)my_malloc(sizeof(ndb_mgm_handle),MYF(MY_WME));
+ h->connected = 0;
+ h->last_error = 0;
+ h->last_error_line = 0;
+ h->socket = NDB_INVALID_SOCKET;
+ h->read_timeout = 50000;
+ h->write_timeout = 100;
+ h->cfg_i = -1;
+
+ strncpy(h->last_error_desc, "No error", NDB_MGM_MAX_ERR_DESC_SIZE);
+
+ new (&(h->cfg)) LocalConfig;
+ h->cfg.init(0, 0);
+
+#ifdef MGMAPI_LOG
+ h->logfile = 0;
+#endif
+
+ DBUG_PRINT("info", ("handle=0x%x", (UintPtr)h));
+ DBUG_RETURN(h);
+}
+
+extern "C"
+int
+ndb_mgm_set_connectstring(NdbMgmHandle handle, const char * mgmsrv)
+{
+ DBUG_ENTER("ndb_mgm_set_connectstring");
+ DBUG_PRINT("info", ("handle=0x%x", (UintPtr)handle));
+ handle->cfg.~LocalConfig();
+ new (&(handle->cfg)) LocalConfig;
+ if (!handle->cfg.init(mgmsrv, 0) ||
+ handle->cfg.ids.size() == 0)
+ {
+ handle->cfg.~LocalConfig();
+ new (&(handle->cfg)) LocalConfig;
+ handle->cfg.init(0, 0); /* reset the LocalConfig */
+ SET_ERROR(handle, NDB_MGM_ILLEGAL_CONNECT_STRING, "");
+ DBUG_RETURN(-1);
+ }
+ handle->cfg_i= -1;
+ DBUG_RETURN(0);
+}
+
+/**
+ * Destroy a handle
+ */
+extern "C"
+void
+ndb_mgm_destroy_handle(NdbMgmHandle * handle)
+{
+ DBUG_ENTER("ndb_mgm_destroy_handle");
+ if(!handle)
+ DBUG_VOID_RETURN;
+ DBUG_PRINT("info", ("handle=0x%x", (UintPtr)(* handle)));
+ /**
+ * important! only disconnect if connected
+ * other code relies on this
+ */
+ if((* handle)->connected){
+ ndb_mgm_disconnect(* handle);
+ }
+#ifdef MGMAPI_LOG
+ if ((* handle)->logfile != 0){
+ fclose((* handle)->logfile);
+ (* handle)->logfile = 0;
+ }
+#endif
+ (*handle)->cfg.~LocalConfig();
+ my_free((char*)* handle,MYF(MY_ALLOW_ZERO_PTR));
+ * handle = 0;
+ DBUG_VOID_RETURN;
+}
+
+/*****************************************************************************
+ * Error handling
+ *****************************************************************************/
+
+/**
+ * Get latest error associated with a handle
+ */
+extern "C"
+int
+ndb_mgm_get_latest_error(const NdbMgmHandle h)
+{
+ return h->last_error;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_latest_error_desc(const NdbMgmHandle h){
+ return h->last_error_desc;
+}
+
+extern "C"
+int
+ndb_mgm_get_latest_error_line(const NdbMgmHandle h)
+{
+ return h->last_error_line;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_latest_error_msg(const NdbMgmHandle h)
+{
+ for (int i=0; i<ndb_mgm_noOfErrorMsgs; i++) {
+ if (ndb_mgm_error_msgs[i].code == h->last_error)
+ return ndb_mgm_error_msgs[i].msg;
+ }
+
+ return "Error"; // Unknown Error message
+}
+
+/*
+ * Call an operation, and return the reply
+ */
+static const Properties *
+ndb_mgm_call(NdbMgmHandle handle, const ParserRow<ParserDummy> *command_reply,
+ const char *cmd, const Properties *cmd_args)
+{
+ SocketOutputStream out(handle->socket);
+ SocketInputStream in(handle->socket, handle->read_timeout);
+
+ out.println(cmd);
+#ifdef MGMAPI_LOG
+ /**
+ * Print command to log file
+ */
+ FileOutputStream f(handle->logfile);
+ f.println("OUT: %s", cmd);
+#endif
+
+ if(cmd_args != NULL) {
+ Properties::Iterator iter(cmd_args);
+ const char *name;
+ while((name = iter.next()) != NULL) {
+ PropertiesType t;
+ Uint32 val_i;
+ Uint64 val_64;
+ BaseString val_s;
+
+ cmd_args->getTypeOf(name, &t);
+ switch(t) {
+ case PropertiesType_Uint32:
+ cmd_args->get(name, &val_i);
+ out.println("%s: %d", name, val_i);
+ break;
+ case PropertiesType_Uint64:
+ cmd_args->get(name, &val_64);
+ out.println("%s: %Ld", name, val_64);
+ break;
+ case PropertiesType_char:
+ cmd_args->get(name, val_s);
+ out.println("%s: %s", name, val_s.c_str());
+ break;
+ case PropertiesType_Properties:
+ DBUG_PRINT("info",("Ignoring PropertiesType_Properties."));
+ /* Ignore */
+ break;
+ default:
+ DBUG_PRINT("info",("Ignoring PropertiesType: %d.",t));
+ }
+ }
+#ifdef MGMAPI_LOG
+ /**
+ * Print arguments to log file
+ */
+ cmd_args->print(handle->logfile, "OUT: ");
+#endif
+ }
+ out.println("");
+
+ Parser_t::Context ctx;
+ ParserDummy session(handle->socket);
+ Parser_t parser(command_reply, in, true, true, true);
+
+ const Properties* p = parser.parse(ctx, session);
+ if (p == NULL){
+ /**
+ * Print some info about why the parser returns NULL
+ */
+ ndbout << "Error in mgm protocol parser. "
+ << "cmd: '" << cmd
+ << "' status=" << (Uint32)ctx.m_status
+ << ", curr=" << ctx.m_currentToken
+ << endl;
+ DBUG_PRINT("info",("parser.parse returned NULL"));
+ }
+#ifdef MGMAPI_LOG
+ else {
+ /**
+ * Print reply to log file
+ */
+ p->print(handle->logfile, "IN: ");
+ }
+#endif
+ return p;
+}
+
+/**
+ * Returns true if connected
+ */
+extern "C"
+int ndb_mgm_is_connected(NdbMgmHandle handle)
+{
+ if(!handle)
+ return 0;
+ return handle->connected;
+}
+
+/**
+ * Connect to a management server
+ */
+extern "C"
+int
+ndb_mgm_connect(NdbMgmHandle handle, int no_retries,
+ int retry_delay_in_seconds, int verbose)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_connect");
+ CHECK_HANDLE(handle, -1);
+
+ DBUG_ENTER("ndb_mgm_connect");
+#ifdef MGMAPI_LOG
+ /**
+ * Open the log file
+ */
+ char logname[64];
+ BaseString::snprintf(logname, 64, "mgmapi.log");
+ handle->logfile = fopen(logname, "w");
+#endif
+
+ /**
+ * Do connect
+ */
+ LocalConfig &cfg= handle->cfg;
+ NDB_SOCKET_TYPE sockfd= NDB_INVALID_SOCKET;
+ Uint32 i;
+ while (sockfd == NDB_INVALID_SOCKET)
+ {
+ // do all the mgmt servers
+ for (i = 0; i < cfg.ids.size(); i++)
+ {
+ if (cfg.ids[i].type != MgmId_TCP)
+ continue;
+ SocketClient s(cfg.ids[i].name.c_str(), cfg.ids[i].port);
+ sockfd = s.connect();
+ if (sockfd != NDB_INVALID_SOCKET)
+ break;
+ }
+ if (sockfd != NDB_INVALID_SOCKET)
+ break;
+#ifndef DBUG_OFF
+ {
+ char buf[1024];
+ DBUG_PRINT("info",("Unable to connect with connect string: %s",
+ cfg.makeConnectString(buf,sizeof(buf))));
+ }
+#endif
+ if (verbose > 0) {
+ char buf[1024];
+ ndbout_c("Unable to connect with connect string: %s",
+ cfg.makeConnectString(buf,sizeof(buf)));
+ verbose= -1;
+ }
+ if (no_retries == 0) {
+ char buf[1024];
+ setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
+ "Unable to connect with connect string: %s",
+ cfg.makeConnectString(buf,sizeof(buf)));
+ if (verbose == -2)
+ ndbout << ", failed." << endl;
+ DBUG_RETURN(-1);
+ }
+ if (verbose == -1) {
+ ndbout << "Retrying every " << retry_delay_in_seconds << " seconds";
+ if (no_retries > 0)
+ ndbout << ". Attempts left:";
+ else
+ ndbout << ", until connected.";;
+ ndbout << flush;
+ verbose= -2;
+ }
+ if (no_retries > 0) {
+ if (verbose == -2) {
+ ndbout << " " << no_retries;
+ ndbout << flush;
+ }
+ no_retries--;
+ }
+ NdbSleep_SecSleep(retry_delay_in_seconds);
+ }
+ if (verbose == -2)
+ ndbout << endl;
+
+ handle->cfg_i = i;
+
+ handle->socket = sockfd;
+ handle->connected = 1;
+
+ DBUG_RETURN(0);
+}
+
+/**
+ * Disconnect from a mgm server
+ */
+extern "C"
+int
+ndb_mgm_disconnect(NdbMgmHandle handle)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_disconnect");
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ NDB_CLOSE_SOCKET(handle->socket);
+ handle->socket = NDB_INVALID_SOCKET;
+ handle->connected = 0;
+
+ return 0;
+}
+
+struct ndb_mgm_type_atoi
+{
+ const char * str;
+ const char * alias;
+ enum ndb_mgm_node_type value;
+};
+
+static struct ndb_mgm_type_atoi type_values[] =
+{
+ { "NDB", "ndbd", NDB_MGM_NODE_TYPE_NDB},
+ { "API", "mysqld", NDB_MGM_NODE_TYPE_API },
+ { "MGM", "ndb_mgmd", NDB_MGM_NODE_TYPE_MGM }
+};
+
+const int no_of_type_values = (sizeof(type_values) /
+ sizeof(ndb_mgm_type_atoi));
+
+extern "C"
+ndb_mgm_node_type
+ndb_mgm_match_node_type(const char * type)
+{
+ if(type == 0)
+ return NDB_MGM_NODE_TYPE_UNKNOWN;
+
+ for(int i = 0; i<no_of_type_values; i++)
+ if(strcmp(type, type_values[i].str) == 0)
+ return type_values[i].value;
+
+ return NDB_MGM_NODE_TYPE_UNKNOWN;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_node_type_string(enum ndb_mgm_node_type type)
+{
+ for(int i = 0; i<no_of_type_values; i++)
+ if(type_values[i].value == type)
+ return type_values[i].str;
+ return 0;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_node_type_alias_string(enum ndb_mgm_node_type type, const char** str)
+{
+ for(int i = 0; i<no_of_type_values; i++)
+ if(type_values[i].value == type)
+ {
+ if (str)
+ *str= type_values[i].str;
+ return type_values[i].alias;
+ }
+ return 0;
+}
+
+struct ndb_mgm_status_atoi {
+ const char * str;
+ enum ndb_mgm_node_status value;
+};
+
+static struct ndb_mgm_status_atoi status_values[] =
+{
+ { "UNKNOWN", NDB_MGM_NODE_STATUS_UNKNOWN },
+ { "NO_CONTACT", NDB_MGM_NODE_STATUS_NO_CONTACT },
+ { "NOT_STARTED", NDB_MGM_NODE_STATUS_NOT_STARTED },
+ { "STARTING", NDB_MGM_NODE_STATUS_STARTING },
+ { "STARTED", NDB_MGM_NODE_STATUS_STARTED },
+ { "SHUTTING_DOWN", NDB_MGM_NODE_STATUS_SHUTTING_DOWN },
+ { "RESTARTING", NDB_MGM_NODE_STATUS_RESTARTING },
+ { "SINGLE USER MODE", NDB_MGM_NODE_STATUS_SINGLEUSER }
+};
+
+const int no_of_status_values = (sizeof(status_values) /
+ sizeof(ndb_mgm_status_atoi));
+
+extern "C"
+ndb_mgm_node_status
+ndb_mgm_match_node_status(const char * status)
+{
+ if(status == 0)
+ return NDB_MGM_NODE_STATUS_UNKNOWN;
+
+ for(int i = 0; i<no_of_status_values; i++)
+ if(strcmp(status, status_values[i].str) == 0)
+ return status_values[i].value;
+
+ return NDB_MGM_NODE_STATUS_UNKNOWN;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_node_status_string(enum ndb_mgm_node_status status)
+{
+ int i;
+ for(i = 0; i<no_of_status_values; i++)
+ if(status_values[i].value == status)
+ return status_values[i].str;
+
+ for(i = 0; i<no_of_status_values; i++)
+ if(status_values[i].value == NDB_MGM_NODE_STATUS_UNKNOWN)
+ return status_values[i].str;
+
+ return 0;
+}
+
+static int
+status_ackumulate(struct ndb_mgm_node_state * state,
+ const char * field,
+ const char * value)
+{
+ if(strcmp("type", field) == 0){
+ state->node_type = ndb_mgm_match_node_type(value);
+ } else if(strcmp("status", field) == 0){
+ state->node_status = ndb_mgm_match_node_status(value);
+ } else if(strcmp("startphase", field) == 0){
+ state->start_phase = atoi(value);
+ } else if(strcmp("dynamic_id", field) == 0){
+ state->dynamic_id = atoi(value);
+ } else if(strcmp("node_group", field) == 0){
+ state->node_group = atoi(value);
+ } else if(strcmp("version", field) == 0){
+ state->version = atoi(value);
+ } else if(strcmp("connect_count", field) == 0){
+ state->connect_count = atoi(value);
+ } else if(strcmp("address", field) == 0){
+ strncpy(state->connect_address, value, sizeof(state->connect_address));
+ state->connect_address[sizeof(state->connect_address)-1]= 0;
+ } else {
+ ndbout_c("Unknown field: %s", field);
+ }
+ return 0;
+}
+
+/**
+ * Compare function for qsort() that sorts ndb_mgm_node_state in
+ * node_id order
+ */
+static int
+cmp_state(const void *_a, const void *_b)
+{
+ struct ndb_mgm_node_state *a, *b;
+
+ a = (struct ndb_mgm_node_state *)_a;
+ b = (struct ndb_mgm_node_state *)_b;
+
+ if (a->node_id > b->node_id)
+ return 1;
+ return -1;
+}
+
+extern "C"
+struct ndb_mgm_cluster_state *
+ndb_mgm_get_status(NdbMgmHandle handle)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_get_status");
+ CHECK_HANDLE(handle, NULL);
+ CHECK_CONNECTED(handle, NULL);
+
+ SocketOutputStream out(handle->socket);
+ SocketInputStream in(handle->socket, handle->read_timeout);
+
+ out.println("get status");
+ out.println("");
+
+ char buf[1024];
+ in.gets(buf, sizeof(buf));
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+
+ if(strcmp("node status", buf) != 0) {
+ SET_ERROR(handle, NDB_MGM_ILLEGAL_NODE_STATUS, buf);
+ return NULL;
+ }
+
+ in.gets(buf, sizeof(buf));
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+
+ BaseString tmp(buf);
+ Vector<BaseString> split;
+ tmp.split(split, ":");
+ if(split.size() != 2){
+ abort();
+ return NULL;
+ }
+
+ if(!(split[0].trim() == "nodes")){
+ abort();
+ return NULL;
+ }
+
+ const int noOfNodes = atoi(split[1].c_str());
+
+ ndb_mgm_cluster_state *state = (ndb_mgm_cluster_state*)
+ malloc(sizeof(ndb_mgm_cluster_state)+
+ noOfNodes*(sizeof(ndb_mgm_node_state)+sizeof("000.000.000.000#")));
+
+ state->no_of_nodes= noOfNodes;
+ ndb_mgm_node_state * ptr = &state->node_states[0];
+ int nodeId = 0;
+ int i;
+ for (i= 0; i < noOfNodes; i++) {
+ state->node_states[i].connect_address[0]= 0;
+ }
+ i = -1; ptr--;
+ for(; i<noOfNodes; ){
+ in.gets(buf, sizeof(buf));
+ tmp.assign(buf);
+
+ if(tmp.trim() == ""){
+ break;
+ }
+
+ Vector<BaseString> split;
+ tmp.split(split, ":.", 4);
+ if(split.size() != 4)
+ break;
+
+ const int id = atoi(split[1].c_str());
+ if(id != nodeId){
+ ptr++;
+ i++;
+ nodeId = id;
+ ptr->node_id = id;
+ }
+
+ split[3].trim(" \t\n");
+
+ if(status_ackumulate(ptr,split[2].c_str(), split[3].c_str()) != 0) {
+ break;
+ }
+ }
+
+ if(i+1 != noOfNodes){
+ free(state);
+ abort();
+ return NULL;
+ }
+
+ qsort(state->node_states, state->no_of_nodes, sizeof(state->node_states[0]),
+ cmp_state);
+ return state;
+}
+
+extern "C"
+int
+ndb_mgm_enter_single_user(NdbMgmHandle handle,
+ unsigned int nodeId,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_enter_single_user");
+ const ParserRow<ParserDummy> enter_single_reply[] = {
+ MGM_CMD("enter single user reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("nodeId", nodeId);
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, enter_single_reply, "enter single user", &args);
+ CHECK_REPLY(reply, -1);
+
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, NDB_MGM_COULD_NOT_ENTER_SINGLE_USER_MODE,
+ result.c_str());
+ delete reply;
+ return -1;
+ }
+
+ delete reply;
+ return 0;
+}
+
+
+extern "C"
+int
+ndb_mgm_exit_single_user(NdbMgmHandle handle, struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_exit_single_user");
+ const ParserRow<ParserDummy> exit_single_reply[] = {
+ MGM_CMD("exit single user reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, exit_single_reply, "exit single user", 0);
+ CHECK_REPLY(reply, -1);
+
+ const char * buf;
+ reply->get("result", &buf);
+ if(strcmp(buf,"Ok")!=0) {
+ SET_ERROR(handle, NDB_MGM_COULD_NOT_EXIT_SINGLE_USER_MODE, buf);
+ delete reply;
+ return -1;
+ }
+
+ delete reply;
+ return 0;
+}
+
+extern "C"
+int
+ndb_mgm_stop(NdbMgmHandle handle, int no_of_nodes, const int * node_list)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_stop");
+ return ndb_mgm_stop2(handle, no_of_nodes, node_list, 0);
+}
+
+
+extern "C"
+int
+ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list,
+ int abort)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_stop2");
+ const ParserRow<ParserDummy> stop_reply[] = {
+ MGM_CMD("stop reply", NULL, ""),
+ MGM_ARG("stopped", Int, Optional, "No of stopped nodes"),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ if(no_of_nodes < 0){
+ SET_ERROR(handle, NDB_MGM_ILLEGAL_NUMBER_OF_NODES,
+ "Negative number of nodes requested to stop");
+ return -1;
+ }
+
+ Uint32 stoppedNoOfNodes = 0;
+ if(no_of_nodes == 0){
+ /**
+ * All database nodes should be stopped
+ */
+ Properties args;
+ args.put("abort", abort);
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, stop_reply, "stop all", &args);
+ CHECK_REPLY(reply, -1);
+
+ if(!reply->get("stopped", &stoppedNoOfNodes)){
+ SET_ERROR(handle, NDB_MGM_STOP_FAILED,
+ "Could not get number of stopped nodes from mgm server");
+ delete reply;
+ return -1;
+ }
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, NDB_MGM_STOP_FAILED, result.c_str());
+ delete reply;
+ return -1;
+ }
+ delete reply;
+ return stoppedNoOfNodes;
+ }
+
+ /**
+ * A list of database nodes should be stopped
+ */
+ Properties args;
+
+ BaseString node_list_str;
+ node_list_str.assfmt("%d", node_list[0]);
+ for(int node = 1; node < no_of_nodes; node++)
+ node_list_str.appfmt(" %d", node_list[node]);
+
+ args.put("node", node_list_str.c_str());
+ args.put("abort", abort);
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, stop_reply, "stop", &args);
+ CHECK_REPLY(reply, stoppedNoOfNodes);
+ if(!reply->get("stopped", &stoppedNoOfNodes)){
+ SET_ERROR(handle, NDB_MGM_STOP_FAILED,
+ "Could not get number of stopped nodes from mgm server");
+ delete reply;
+ return -1;
+ }
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, NDB_MGM_STOP_FAILED, result.c_str());
+ delete reply;
+ return -1;
+ }
+ delete reply;
+ return stoppedNoOfNodes;
+}
+
+extern "C"
+int
+ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list,
+ int initial, int nostart, int abort)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart2");
+ Uint32 restarted = 0;
+ const ParserRow<ParserDummy> restart_reply[] = {
+ MGM_CMD("restart reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_ARG("restarted", Int, Optional, "No of restarted nodes"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ if(no_of_nodes < 0){
+ SET_ERROR(handle, NDB_MGM_RESTART_FAILED,
+ "Restart requested of negative number of nodes");
+ return -1;
+ }
+
+ if(no_of_nodes == 0) {
+ Properties args;
+ args.put("abort", abort);
+ args.put("initialstart", initial);
+ args.put("nostart", nostart);
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, restart_reply, "restart all", &args);
+ CHECK_REPLY(reply, -1);
+
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, NDB_MGM_RESTART_FAILED, result.c_str());
+ delete reply;
+ return -1;
+ }
+ if(!reply->get("restarted", &restarted)){
+ SET_ERROR(handle, NDB_MGM_RESTART_FAILED,
+ "Could not get restarted number of nodes from mgm server");
+ delete reply;
+ return -1;
+ }
+ delete reply;
+ return restarted;
+ }
+
+ BaseString node_list_str;
+ node_list_str.assfmt("%d", node_list[0]);
+ for(int node = 1; node < no_of_nodes; node++)
+ node_list_str.appfmt(" %d", node_list[node]);
+
+ Properties args;
+
+ args.put("node", node_list_str.c_str());
+ args.put("abort", abort);
+ args.put("initialstart", initial);
+ args.put("nostart", nostart);
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, restart_reply, "restart node", &args);
+ if(reply != NULL) {
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, NDB_MGM_RESTART_FAILED, result.c_str());
+ delete reply;
+ return -1;
+ }
+ reply->get("restarted", &restarted);
+ delete reply;
+ }
+
+ return restarted;
+}
+
+extern "C"
+int
+ndb_mgm_restart(NdbMgmHandle handle, int no_of_nodes, const int *node_list)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart");
+ return ndb_mgm_restart2(handle, no_of_nodes, node_list, 0, 0, 0);
+}
+
+static const char *clusterlog_severity_names[]=
+ { "enabled", "debug", "info", "warning", "error", "critical", "alert" };
+
+struct ndb_mgm_event_severities
+{
+ const char* name;
+ enum ndb_mgm_event_severity severity;
+} clusterlog_severities[] = {
+ { clusterlog_severity_names[0], NDB_MGM_EVENT_SEVERITY_ON },
+ { clusterlog_severity_names[1], NDB_MGM_EVENT_SEVERITY_DEBUG },
+ { clusterlog_severity_names[2], NDB_MGM_EVENT_SEVERITY_INFO },
+ { clusterlog_severity_names[3], NDB_MGM_EVENT_SEVERITY_WARNING },
+ { clusterlog_severity_names[4], NDB_MGM_EVENT_SEVERITY_ERROR },
+ { clusterlog_severity_names[5], NDB_MGM_EVENT_SEVERITY_CRITICAL },
+ { clusterlog_severity_names[6], NDB_MGM_EVENT_SEVERITY_ALERT },
+ { "all", NDB_MGM_EVENT_SEVERITY_ALL },
+ { 0, NDB_MGM_ILLEGAL_EVENT_SEVERITY },
+};
+
+extern "C"
+ndb_mgm_event_severity
+ndb_mgm_match_event_severity(const char * name)
+{
+ if(name == 0)
+ return NDB_MGM_ILLEGAL_EVENT_SEVERITY;
+
+ for(int i = 0; clusterlog_severities[i].name !=0 ; i++)
+ if(strcasecmp(name, clusterlog_severities[i].name) == 0)
+ return clusterlog_severities[i].severity;
+
+ return NDB_MGM_ILLEGAL_EVENT_SEVERITY;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_event_severity_string(enum ndb_mgm_event_severity severity)
+{
+ int i= (int)severity;
+ if (i >= 0 && i < (int)NDB_MGM_EVENT_SEVERITY_ALL)
+ return clusterlog_severity_names[i];
+ for(i = (int)NDB_MGM_EVENT_SEVERITY_ALL; clusterlog_severities[i].name != 0; i++)
+ if(clusterlog_severities[i].severity == severity)
+ return clusterlog_severities[i].name;
+ return 0;
+}
+
+extern "C"
+const unsigned int *
+ndb_mgm_get_clusterlog_severity_filter(NdbMgmHandle handle)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_get_clusterlog_severity_filter");
+ static unsigned int enabled[(int)NDB_MGM_EVENT_SEVERITY_ALL]=
+ {0,0,0,0,0,0,0};
+ const ParserRow<ParserDummy> getinfo_reply[] = {
+ MGM_CMD("clusterlog", NULL, ""),
+ MGM_ARG(clusterlog_severity_names[0], Int, Mandatory, ""),
+ MGM_ARG(clusterlog_severity_names[1], Int, Mandatory, ""),
+ MGM_ARG(clusterlog_severity_names[2], Int, Mandatory, ""),
+ MGM_ARG(clusterlog_severity_names[3], Int, Mandatory, ""),
+ MGM_ARG(clusterlog_severity_names[4], Int, Mandatory, ""),
+ MGM_ARG(clusterlog_severity_names[5], Int, Mandatory, ""),
+ MGM_ARG(clusterlog_severity_names[6], Int, Mandatory, ""),
+ };
+ CHECK_HANDLE(handle, NULL);
+ CHECK_CONNECTED(handle, NULL);
+
+ Properties args;
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, getinfo_reply, "get info clusterlog", &args);
+ CHECK_REPLY(reply, NULL);
+
+ for(int i=0; i < (int)NDB_MGM_EVENT_SEVERITY_ALL; i++) {
+ reply->get(clusterlog_severity_names[i], &enabled[i]);
+ }
+ return enabled;
+}
+
+extern "C"
+int
+ndb_mgm_set_clusterlog_severity_filter(NdbMgmHandle handle,
+ enum ndb_mgm_event_severity severity,
+ int enable,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR,
+ "Executing: ndb_mgm_set_clusterlog_severity_filter");
+ const ParserRow<ParserDummy> filter_reply[] = {
+ MGM_CMD("set logfilter reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int retval = -1;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("level", severity);
+ args.put("enable", enable);
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, filter_reply, "set logfilter", &args);
+ CHECK_REPLY(reply, retval);
+
+ BaseString result;
+ reply->get("result", result);
+
+ if (strcmp(result.c_str(), "1") == 0)
+ retval = 1;
+ else if (strcmp(result.c_str(), "0") == 0)
+ retval = 0;
+ else
+ {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ }
+ delete reply;
+ return retval;
+}
+
+struct ndb_mgm_event_categories
+{
+ const char* name;
+ enum ndb_mgm_event_category category;
+} categories[] = {
+ { "STARTUP", NDB_MGM_EVENT_CATEGORY_STARTUP },
+ { "SHUTDOWN", NDB_MGM_EVENT_CATEGORY_SHUTDOWN },
+ { "STATISTICS", NDB_MGM_EVENT_CATEGORY_STATISTIC },
+ { "NODERESTART", NDB_MGM_EVENT_CATEGORY_NODE_RESTART },
+ { "CONNECTION", NDB_MGM_EVENT_CATEGORY_CONNECTION },
+ { "CHECKPOINT", NDB_MGM_EVENT_CATEGORY_CHECKPOINT },
+ { "DEBUG", NDB_MGM_EVENT_CATEGORY_DEBUG },
+ { "INFO", NDB_MGM_EVENT_CATEGORY_INFO },
+ { "ERROR", NDB_MGM_EVENT_CATEGORY_ERROR },
+ { "BACKUP", NDB_MGM_EVENT_CATEGORY_BACKUP },
+ { "CONGESTION", NDB_MGM_EVENT_CATEGORY_CONGESTION },
+ { 0, NDB_MGM_ILLEGAL_EVENT_CATEGORY }
+};
+
+extern "C"
+ndb_mgm_event_category
+ndb_mgm_match_event_category(const char * status)
+{
+ if(status == 0)
+ return NDB_MGM_ILLEGAL_EVENT_CATEGORY;
+
+ for(int i = 0; categories[i].name !=0 ; i++)
+ if(strcmp(status, categories[i].name) == 0)
+ return categories[i].category;
+
+ return NDB_MGM_ILLEGAL_EVENT_CATEGORY;
+}
+
+extern "C"
+const char *
+ndb_mgm_get_event_category_string(enum ndb_mgm_event_category status)
+{
+ int i;
+ for(i = 0; categories[i].name != 0; i++)
+ if(categories[i].category == status)
+ return categories[i].name;
+
+ return 0;
+}
+
+extern "C"
+int
+ndb_mgm_set_clusterlog_loglevel(NdbMgmHandle handle, int nodeId,
+ enum ndb_mgm_event_category cat,
+ int level,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR,
+ "Executing: ndb_mgm_set_clusterlog_loglevel");
+ const ParserRow<ParserDummy> clusterlog_reply[] = {
+ MGM_CMD("set cluster loglevel reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+ args.put("category", cat);
+ args.put("level", level);
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, clusterlog_reply,
+ "set cluster loglevel", &args);
+ CHECK_REPLY(reply, -1);
+
+ DBUG_ENTER("ndb_mgm_set_clusterlog_loglevel");
+ DBUG_PRINT("enter",("node=%d, category=%d, level=%d", nodeId, cat, level));
+
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ delete reply;
+ DBUG_RETURN(-1);
+ }
+ delete reply;
+ DBUG_RETURN(0);
+}
+
+extern "C"
+int
+ndb_mgm_set_loglevel_node(NdbMgmHandle handle, int nodeId,
+ enum ndb_mgm_event_category category,
+ int level,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_set_loglevel_node");
+ const ParserRow<ParserDummy> loglevel_reply[] = {
+ MGM_CMD("set loglevel reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+ args.put("category", category);
+ args.put("level", level);
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, loglevel_reply, "set loglevel", &args);
+ CHECK_REPLY(reply, -1);
+
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ delete reply;
+ return -1;
+ }
+
+ delete reply;
+ return 0;
+}
+
+int
+ndb_mgm_listen_event_internal(NdbMgmHandle handle, const int filter[],
+ int parsable)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_listen_event");
+ const ParserRow<ParserDummy> stat_reply[] = {
+ MGM_CMD("listen event", NULL, ""),
+ MGM_ARG("result", Int, Mandatory, "Error message"),
+ MGM_ARG("msg", String, Optional, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+
+ const char *hostname= ndb_mgm_get_connected_host(handle);
+ int port= ndb_mgm_get_connected_port(handle);
+ SocketClient s(hostname, port);
+ const NDB_SOCKET_TYPE sockfd = s.connect();
+ if (sockfd == NDB_INVALID_SOCKET) {
+ setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
+ "Unable to connect to");
+ return -1;
+ }
+
+ Properties args;
+
+ if (parsable)
+ args.put("parsable", parsable);
+ {
+ BaseString tmp;
+ for(int i = 0; filter[i] != 0; i += 2){
+ tmp.appfmt("%d=%d ", filter[i+1], filter[i]);
+ }
+ args.put("filter", tmp.c_str());
+ }
+
+ int tmp = handle->socket;
+ handle->socket = sockfd;
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, stat_reply, "listen event", &args);
+
+ handle->socket = tmp;
+
+ if(reply == NULL) {
+ close(sockfd);
+ CHECK_REPLY(reply, -1);
+ }
+ return sockfd;
+}
+
+extern "C"
+int
+ndb_mgm_listen_event(NdbMgmHandle handle, const int filter[])
+{
+ return ndb_mgm_listen_event_internal(handle,filter,0);
+}
+
+extern "C"
+int
+ndb_mgm_get_stat_port(NdbMgmHandle handle, struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_get_stat_port");
+ const ParserRow<ParserDummy> stat_reply[] = {
+ MGM_CMD("error", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_CMD("get statport reply", NULL, ""),
+ MGM_ARG("tcpport", Int, Mandatory, "TCP port for statistics"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, stat_reply, "get statport", &args);
+ CHECK_REPLY(reply, -1);
+
+ Uint32 port;
+ reply->get("tcpport", &port);
+
+ delete reply;
+ return port;
+}
+
+extern "C"
+int
+ndb_mgm_dump_state(NdbMgmHandle handle, int nodeId, int* _args,
+ int _num_args, struct ndb_mgm_reply* /* reply */)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_dump_state");
+ const ParserRow<ParserDummy> dump_state_reply[] = {
+ MGM_CMD("dump state reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ char buf[256];
+ buf[0] = 0;
+ for (int i = 0; i < _num_args; i++){
+ unsigned n = strlen(buf);
+ if (n + 20 > sizeof(buf)) {
+ SET_ERROR(handle, NDB_MGM_USAGE_ERROR, "arguments too long");
+ return -1;
+ }
+ sprintf(buf + n, "%s%d", i ? " " : "", _args[i]);
+ }
+
+ Properties args;
+ args.put("node", nodeId);
+ args.put("args", buf);
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, dump_state_reply, "dump state", &args);
+ CHECK_REPLY(prop, -1);
+
+ BaseString result;
+ prop->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ delete prop;
+ return -1;
+ }
+
+ delete prop;
+ return 0;
+}
+
+extern "C"
+int
+ndb_mgm_start_signallog(NdbMgmHandle handle, int nodeId,
+ struct ndb_mgm_reply* reply)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_start_signallog");
+ const ParserRow<ParserDummy> start_signallog_reply[] = {
+ MGM_CMD("start signallog reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int retval = -1;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle,
+ start_signallog_reply,
+ "start signallog",
+ &args);
+
+ if(prop != NULL) {
+ BaseString result;
+ prop->get("result", result);
+ if(strcmp(result.c_str(), "Ok") == 0) {
+ retval = 0;
+ } else {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ retval = -1;
+ }
+ delete prop;
+ }
+
+ return retval;
+}
+
+extern "C"
+int
+ndb_mgm_stop_signallog(NdbMgmHandle handle, int nodeId,
+ struct ndb_mgm_reply* reply)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_stop_signallog");
+ const ParserRow<ParserDummy> stop_signallog_reply[] = {
+ MGM_CMD("stop signallog reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int retval = -1;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, stop_signallog_reply, "stop signallog", &args);
+
+ if(prop != NULL) {
+ BaseString result;
+ prop->get("result", result);
+ if(strcmp(result.c_str(), "Ok") == 0) {
+ retval = 0;
+ } else {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ retval = -1;
+ }
+ delete prop;
+ }
+
+ return retval;
+}
+
+struct ndb_mgm_signal_log_modes
+{
+ const char* name;
+ enum ndb_mgm_signal_log_mode mode;
+};
+
+extern "C"
+int
+ndb_mgm_log_signals(NdbMgmHandle handle, int nodeId,
+ enum ndb_mgm_signal_log_mode mode,
+ const char* blockNames,
+ struct ndb_mgm_reply* reply)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_log_signals");
+ const ParserRow<ParserDummy> stop_signallog_reply[] = {
+ MGM_CMD("log signals reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int retval = -1;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+ args.put("blocks", blockNames);
+
+ switch(mode) {
+ case NDB_MGM_SIGNAL_LOG_MODE_IN:
+ args.put("in", (Uint32)1);
+ args.put("out", (Uint32)0);
+ break;
+ case NDB_MGM_SIGNAL_LOG_MODE_OUT:
+ args.put("in", (Uint32)0);
+ args.put("out", (Uint32)1);
+ break;
+ case NDB_MGM_SIGNAL_LOG_MODE_INOUT:
+ args.put("in", (Uint32)1);
+ args.put("out", (Uint32)1);
+ break;
+ case NDB_MGM_SIGNAL_LOG_MODE_OFF:
+ args.put("in", (Uint32)0);
+ args.put("out", (Uint32)0);
+ break;
+ }
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, stop_signallog_reply, "log signals", &args);
+
+ if(prop != NULL) {
+ BaseString result;
+ prop->get("result", result);
+ if(strcmp(result.c_str(), "Ok") == 0) {
+ retval = 0;
+ } else {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ retval = -1;
+ }
+ delete prop;
+ }
+
+ return retval;
+}
+
+extern "C"
+int
+ndb_mgm_set_trace(NdbMgmHandle handle, int nodeId, int traceNumber,
+ struct ndb_mgm_reply* reply)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_set_trace");
+ const ParserRow<ParserDummy> set_trace_reply[] = {
+ MGM_CMD("set trace reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int retval = -1;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+ args.put("trace", traceNumber);
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, set_trace_reply, "set trace", &args);
+
+ if(prop != NULL) {
+ BaseString result;
+ prop->get("result", result);
+ if(strcmp(result.c_str(), "Ok") == 0) {
+ retval = 0;
+ } else {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ retval = -1;
+ }
+ delete prop;
+ }
+
+ return retval;
+}
+
+extern "C"
+int
+ndb_mgm_insert_error(NdbMgmHandle handle, int nodeId, int errorCode,
+ struct ndb_mgm_reply* reply)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_insert_error");
+ const ParserRow<ParserDummy> insert_error_reply[] = {
+ MGM_CMD("insert error reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int retval = -1;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("node", nodeId);
+ args.put("error", errorCode);
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, insert_error_reply, "insert error", &args);
+
+ if(prop != NULL) {
+ BaseString result;
+ prop->get("result", result);
+ if(strcmp(result.c_str(), "Ok") == 0) {
+ retval = 0;
+ } else {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ retval = -1;
+ }
+ delete prop;
+ }
+
+ return retval;
+}
+
+extern "C"
+int
+ndb_mgm_start(NdbMgmHandle handle, int no_of_nodes, const int * node_list)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_start");
+ const ParserRow<ParserDummy> start_reply[] = {
+ MGM_CMD("start reply", NULL, ""),
+ MGM_ARG("started", Int, Optional, "No of started nodes"),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ int started = 0;
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ if(no_of_nodes < 0){
+ SET_ERROR(handle, EINVAL, "");
+ return -1;
+ }
+
+ if(no_of_nodes == 0){
+ Properties args;
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, start_reply, "start all", &args);
+ CHECK_REPLY(reply, -1);
+
+ Uint32 count = 0;
+ if(!reply->get("started", &count)){
+ delete reply;
+ return -1;
+ }
+ delete reply;
+ return count;
+ }
+
+ for(int node = 0; node < no_of_nodes; node++) {
+ Properties args;
+ args.put("node", node_list[node]);
+
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, start_reply, "start", &args);
+
+ if(reply != NULL) {
+ BaseString result;
+ reply->get("result", result);
+ if(strcmp(result.c_str(), "Ok") == 0) {
+ started++;
+ } else {
+ SET_ERROR(handle, EINVAL, result.c_str());
+ delete reply;
+ return -1;
+ }
+ }
+ delete reply;
+ }
+
+ return started;
+}
+
+/*****************************************************************************
+ * Backup
+ *****************************************************************************/
+extern "C"
+int
+ndb_mgm_start_backup(NdbMgmHandle handle, int wait_completed,
+ unsigned int* _backup_id,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_start_backup");
+ const ParserRow<ParserDummy> start_backup_reply[] = {
+ MGM_CMD("start backup reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_ARG("id", Int, Optional, "Id of the started backup"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("completed", wait_completed);
+ const Properties *reply;
+ { // start backup can take some time, set timeout high
+ Uint64 old_timeout= handle->read_timeout;
+ if (wait_completed == 2)
+ handle->read_timeout= 30*60*1000; // 30 minutes
+ else if (wait_completed == 1)
+ handle->read_timeout= 5*60*1000; // 5 minutes
+ reply = ndb_mgm_call(handle, start_backup_reply, "start backup", &args);
+ handle->read_timeout= old_timeout;
+ }
+ CHECK_REPLY(reply, -1);
+
+ BaseString result;
+ reply->get("result", result);
+ reply->get("id", _backup_id);
+ if(strcmp(result.c_str(), "Ok") != 0) {
+ SET_ERROR(handle, NDB_MGM_COULD_NOT_START_BACKUP, result.c_str());
+ delete reply;
+ return -1;
+ }
+
+ delete reply;
+ return 0;
+}
+
+extern "C"
+int
+ndb_mgm_abort_backup(NdbMgmHandle handle, unsigned int backupId,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_abort_backup");
+ const ParserRow<ParserDummy> stop_backup_reply[] = {
+ MGM_CMD("abort backup reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("id", backupId);
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, stop_backup_reply, "abort backup", &args);
+ CHECK_REPLY(prop, -1);
+
+ const char * buf;
+ prop->get("result", &buf);
+ if(strcmp(buf,"Ok")!=0) {
+ SET_ERROR(handle, NDB_MGM_COULD_NOT_ABORT_BACKUP, buf);
+ delete prop;
+ return -1;
+ }
+
+ delete prop;
+ return 0;
+}
+
+extern "C"
+struct ndb_mgm_configuration *
+ndb_mgm_get_configuration(NdbMgmHandle handle, unsigned int version) {
+
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+ args.put("version", version);
+
+ const ParserRow<ParserDummy> reply[] = {
+ MGM_CMD("get config reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_ARG("Content-Length", Int, Optional, "Content length in bytes"),
+ MGM_ARG("Content-Type", String, Optional, "Type (octet-stream)"),
+ MGM_ARG("Content-Transfer-Encoding", String, Optional, "Encoding(base64)"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, reply, "get config", &args);
+ CHECK_REPLY(prop, 0);
+
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+
+ buf = "<Unspecified>";
+ if(!prop->get("Content-Type", &buf) ||
+ strcmp(buf, "ndbconfig/octet-stream") != 0){
+ ndbout_c("Unhandled response type: %s", buf);
+ break;
+ }
+
+ buf = "<Unspecified>";
+ if(!prop->get("Content-Transfer-Encoding", &buf)
+ || strcmp(buf, "base64") != 0){
+ ndbout_c("Unhandled encoding: %s", buf);
+ break;
+ }
+
+ buf = "<Content-Length Unspecified>";
+ Uint32 len = 0;
+ if(!prop->get("Content-Length", &len)){
+ ndbout_c("Invalid response: %s\n", buf);
+ break;
+ }
+
+ len += 1; // Trailing \n
+
+ char* buf64 = new char[len];
+ int read = 0;
+ size_t start = 0;
+ do {
+ if((read = read_socket(handle->socket, handle->read_timeout,
+ &buf64[start], len-start)) == -1){
+ delete[] buf64;
+ buf64 = 0;
+ break;
+ }
+ start += read;
+ } while(start < len);
+ if(buf64 == 0)
+ break;
+
+ UtilBuffer tmp;
+ const int res = base64_decode(buf64, len-1, tmp);
+ delete[] buf64;
+ if(res != 0){
+ ndbout_c("Failed to decode buffer");
+ break;
+ }
+
+ ConfigValuesFactory cvf;
+ const int res2 = cvf.unpack(tmp);
+ if(!res2){
+ ndbout_c("Failed to unpack buffer");
+ break;
+ }
+
+ delete prop;
+ return (ndb_mgm_configuration*)cvf.m_cfg;
+ } while(0);
+
+ delete prop;
+ return 0;
+}
+
+extern "C"
+void
+ndb_mgm_destroy_configuration(struct ndb_mgm_configuration *cfg)
+{
+ if (cfg) {
+ ((ConfigValues *)cfg)->~ConfigValues();
+ free((void *)cfg);
+ }
+}
+
+extern "C"
+int
+ndb_mgm_set_configuration_nodeid(NdbMgmHandle handle, int nodeid)
+{
+ CHECK_HANDLE(handle, -1);
+ handle->cfg._ownNodeId= nodeid;
+ return 0;
+}
+
+extern "C"
+int
+ndb_mgm_get_configuration_nodeid(NdbMgmHandle handle)
+{
+ CHECK_HANDLE(handle, 0);
+ return handle->cfg._ownNodeId;
+}
+
+extern "C"
+int ndb_mgm_get_connected_port(NdbMgmHandle handle)
+{
+ if (handle->cfg_i >= 0)
+ return handle->cfg.ids[handle->cfg_i].port;
+ else
+ return 0;
+}
+
+extern "C"
+const char *ndb_mgm_get_connected_host(NdbMgmHandle handle)
+{
+ if (handle->cfg_i >= 0)
+ return handle->cfg.ids[handle->cfg_i].name.c_str();
+ else
+ return 0;
+}
+
+extern "C"
+const char *ndb_mgm_get_connectstring(NdbMgmHandle handle, char *buf, int buf_sz)
+{
+ return handle->cfg.makeConnectString(buf,buf_sz);
+}
+
+extern "C"
+int
+ndb_mgm_alloc_nodeid(NdbMgmHandle handle, unsigned int version, int nodetype)
+{
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+ union { long l; char c[sizeof(long)]; } endian_check;
+
+ endian_check.l = 1;
+
+ int nodeid= handle->cfg._ownNodeId;
+
+ Properties args;
+ args.put("version", version);
+ args.put("nodetype", nodetype);
+ args.put("nodeid", nodeid);
+ args.put("user", "mysqld");
+ args.put("password", "mysqld");
+ args.put("public key", "a public key");
+ args.put("endian", (endian_check.c[sizeof(long)-1])?"big":"little");
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("get nodeid reply", NULL, ""),
+ MGM_ARG("nodeid", Int, Optional, "Error message"),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop= ndb_mgm_call(handle, reply, "get nodeid", &args);
+ CHECK_REPLY(prop, -1);
+
+ nodeid= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ const char *hostname= ndb_mgm_get_connected_host(handle);
+ unsigned port= ndb_mgm_get_connected_port(handle);
+ BaseString err;
+ err.assfmt("Could not alloc node id at %s port %d: %s",
+ hostname, port, buf);
+ setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
+ err.c_str());
+ break;
+ }
+ Uint32 _nodeid;
+ if(!prop->get("nodeid", &_nodeid) != 0){
+ ndbout_c("ERROR Message: <nodeid Unspecified>\n");
+ break;
+ }
+ nodeid= _nodeid;
+ }while(0);
+
+ delete prop;
+ return nodeid;
+}
+
+/*****************************************************************************
+ * Global Replication
+ ******************************************************************************/
+extern "C"
+int
+ndb_mgm_rep_command(NdbMgmHandle handle, unsigned int request,
+ unsigned int* replication_id,
+ struct ndb_mgm_reply* /*reply*/)
+{
+ SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_rep_command");
+ const ParserRow<ParserDummy> replication_reply[] = {
+ MGM_CMD("global replication reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_ARG("id", Int, Optional, "Id of global replication"),
+ MGM_END()
+ };
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -1);
+
+ Properties args;
+ args.put("request", request);
+ const Properties *reply;
+ reply = ndb_mgm_call(handle, replication_reply, "rep", &args);
+ CHECK_REPLY(reply, -1);
+
+ const char * result;
+ reply->get("result", &result);
+ reply->get("id", replication_id);
+ if(strcmp(result,"Ok")!=0) {
+ delete reply;
+ return -1;
+ }
+
+ delete reply;
+ return 0;
+}
+
+extern "C"
+int
+ndb_mgm_set_int_parameter(NdbMgmHandle handle,
+ int node,
+ int param,
+ unsigned value,
+ struct ndb_mgm_reply*){
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+ args.put("node", node);
+ args.put("param", param);
+ args.put("value", value);
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("set parameter reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop= ndb_mgm_call(handle, reply, "set parameter", &args);
+ CHECK_REPLY(prop, -1);
+
+ int res= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+ res= 0;
+ } while(0);
+
+ delete prop;
+ return res;
+}
+
+extern "C"
+int
+ndb_mgm_set_int64_parameter(NdbMgmHandle handle,
+ int node,
+ int param,
+ unsigned long long value,
+ struct ndb_mgm_reply*){
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+ args.put("node", node);
+ args.put("param", param);
+ args.put("value", value);
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("set parameter reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop= ndb_mgm_call(handle, reply, "set parameter", &args);
+
+ if(prop == NULL) {
+ SET_ERROR(handle, EIO, "Unable set parameter");
+ return -1;
+ }
+
+ int res= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+ res= 0;
+ } while(0);
+
+ delete prop;
+ return res;
+}
+
+extern "C"
+int
+ndb_mgm_set_string_parameter(NdbMgmHandle handle,
+ int node,
+ int param,
+ const char * value,
+ struct ndb_mgm_reply*){
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+ args.put("node", node);
+ args.put("parameter", param);
+ args.put("value", value);
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("set parameter reply", NULL, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop= ndb_mgm_call(handle, reply, "set parameter", &args);
+
+ if(prop == NULL) {
+ SET_ERROR(handle, EIO, "Unable set parameter");
+ return -1;
+ }
+
+ int res= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+ res= 0;
+ } while(0);
+
+ delete prop;
+ return res;
+}
+
+extern "C"
+int
+ndb_mgm_purge_stale_sessions(NdbMgmHandle handle, char **purged){
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("purge stale sessions reply", NULL, ""),
+ MGM_ARG("purged", String, Optional, ""),
+ MGM_ARG("result", String, Mandatory, "Error message"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop= ndb_mgm_call(handle, reply, "purge stale sessions", &args);
+
+ if(prop == NULL) {
+ SET_ERROR(handle, EIO, "Unable to purge stale sessions");
+ return -1;
+ }
+
+ int res= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+ if (purged) {
+ if (prop->get("purged", &buf))
+ *purged= strdup(buf);
+ else
+ *purged= 0;
+ }
+ res= 0;
+ } while(0);
+ delete prop;
+ return res;
+}
+
+extern "C"
+int
+ndb_mgm_check_connection(NdbMgmHandle handle){
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+ SocketOutputStream out(handle->socket);
+ SocketInputStream in(handle->socket, handle->read_timeout);
+ char buf[32];
+
+ if (out.println("check connection"))
+ goto ndb_mgm_check_connection_error;
+
+ if (out.println(""))
+ goto ndb_mgm_check_connection_error;
+
+ in.gets(buf, sizeof(buf));
+ if(strcmp("check connection reply\n", buf))
+ goto ndb_mgm_check_connection_error;
+
+ in.gets(buf, sizeof(buf));
+ if(strcmp("result: Ok\n", buf))
+ goto ndb_mgm_check_connection_error;
+
+ in.gets(buf, sizeof(buf));
+ if(strcmp("\n", buf))
+ goto ndb_mgm_check_connection_error;
+
+ return 0;
+
+ndb_mgm_check_connection_error:
+ ndb_mgm_disconnect(handle);
+ return -1;
+}
+
+extern "C"
+int
+ndb_mgm_set_connection_int_parameter(NdbMgmHandle handle,
+ int node1,
+ int node2,
+ int param,
+ int value,
+ struct ndb_mgm_reply* mgmreply){
+ DBUG_ENTER("ndb_mgm_set_connection_int_parameter");
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+ args.put("node1", node1);
+ args.put("node2", node2);
+ args.put("param", param);
+ args.put("value", (Uint32)value);
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("set connection parameter reply", NULL, ""),
+ MGM_ARG("message", String, Mandatory, "Error Message"),
+ MGM_ARG("result", String, Mandatory, "Status Result"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop= ndb_mgm_call(handle, reply, "set connection parameter", &args);
+ CHECK_REPLY(prop, -1);
+
+ int res= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+ res= 0;
+ } while(0);
+
+ delete prop;
+ DBUG_RETURN(res);
+}
+
+extern "C"
+int
+ndb_mgm_get_connection_int_parameter(NdbMgmHandle handle,
+ int node1,
+ int node2,
+ int param,
+ int *value,
+ struct ndb_mgm_reply* mgmreply){
+ DBUG_ENTER("ndb_mgm_get_connection_int_parameter");
+ CHECK_HANDLE(handle, -1);
+ CHECK_CONNECTED(handle, -2);
+
+ Properties args;
+ args.put("node1", node1);
+ args.put("node2", node2);
+ args.put("param", param);
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("get connection parameter reply", NULL, ""),
+ MGM_ARG("value", Int, Mandatory, "Current Value"),
+ MGM_ARG("result", String, Mandatory, "Result"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, reply, "get connection parameter", &args);
+ CHECK_REPLY(prop, -3);
+
+ int res= -1;
+ do {
+ const char * buf;
+ if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ ndbout_c("ERROR Message: %s\n", buf);
+ break;
+ }
+ res= 0;
+ } while(0);
+
+ if(!prop->get("value",(Uint32*)value)){
+ ndbout_c("Unable to get value");
+ res = -4;
+ }
+
+ delete prop;
+ DBUG_RETURN(res);
+}
+
+extern "C"
+NDB_SOCKET_TYPE
+ndb_mgm_convert_to_transporter(NdbMgmHandle *handle)
+{
+ NDB_SOCKET_TYPE s;
+
+ CHECK_HANDLE((*handle), NDB_INVALID_SOCKET);
+ CHECK_CONNECTED((*handle), NDB_INVALID_SOCKET);
+
+ (*handle)->connected= 0; // we pretend we're disconnected
+ s= (*handle)->socket;
+
+ SocketOutputStream s_output(s);
+ s_output.println("transporter connect");
+ s_output.println("");
+
+ ndb_mgm_destroy_handle(handle); // set connected=0, so won't disconnect
+
+ return s;
+}
+
+extern "C"
+Uint32
+ndb_mgm_get_mgmd_nodeid(NdbMgmHandle handle)
+{
+ Uint32 nodeid=0;
+
+ DBUG_ENTER("ndb_mgm_get_mgmd_nodeid");
+ CHECK_HANDLE(handle, 0);
+ CHECK_CONNECTED(handle, 0);
+
+ Properties args;
+
+ const ParserRow<ParserDummy> reply[]= {
+ MGM_CMD("get mgmd nodeid reply", NULL, ""),
+ MGM_ARG("nodeid", Int, Mandatory, "Node ID"),
+ MGM_END()
+ };
+
+ const Properties *prop;
+ prop = ndb_mgm_call(handle, reply, "get mgmd nodeid", &args);
+ CHECK_REPLY(prop, 0);
+
+ if(!prop->get("nodeid",&nodeid)){
+ ndbout_c("Unable to get value");
+ return 0;
+ }
+
+ delete prop;
+ DBUG_RETURN(nodeid);
+}
+
+template class Vector<const ParserRow<ParserDummy>*>;
diff --git a/storage/ndb/src/mgmapi/mgmapi_configuration.cpp b/storage/ndb/src/mgmapi/mgmapi_configuration.cpp
new file mode 100644
index 00000000000..80ab428c05a
--- /dev/null
+++ b/storage/ndb/src/mgmapi/mgmapi_configuration.cpp
@@ -0,0 +1,157 @@
+#include <ndb_types.h>
+#include <mgmapi.h>
+#include "mgmapi_configuration.hpp"
+
+ndb_mgm_configuration_iterator::ndb_mgm_configuration_iterator
+(const ndb_mgm_configuration & conf, unsigned type_of_section)
+ : m_config(conf.m_config)
+{
+ m_sectionNo = ~0;
+ m_typeOfSection = type_of_section;
+ first();
+}
+
+ndb_mgm_configuration_iterator::~ndb_mgm_configuration_iterator(){
+ reset();
+}
+
+void
+ndb_mgm_configuration_iterator::reset(){
+ if(m_sectionNo != (Uint32)~0){
+ m_config.closeSection();
+ }
+}
+
+
+int
+ndb_mgm_configuration_iterator::enter(){
+ bool ok = m_config.openSection(m_typeOfSection, m_sectionNo);
+ if(ok){
+ return 0;
+ }
+
+ reset();
+ m_sectionNo = ~0;
+ return -1;
+}
+
+int
+ndb_mgm_configuration_iterator::first(){
+ reset();
+ m_sectionNo = 0;
+ return enter();
+}
+
+int
+ndb_mgm_configuration_iterator::next(){
+ reset();
+ m_sectionNo++;
+ return enter();
+}
+
+int
+ndb_mgm_configuration_iterator::valid() const {
+ return m_sectionNo != (Uint32)~0;
+}
+
+int
+ndb_mgm_configuration_iterator::find(int param, unsigned search){
+ unsigned val = search + 1;
+
+ while(get(param, &val) == 0 && val != search){
+ if(next() != 0)
+ break;
+ }
+
+ if(val == search)
+ return 0;
+
+ return -1;
+}
+
+int
+ndb_mgm_configuration_iterator::get(int param, unsigned * value) const {
+ return m_config.get(param, value) != true;
+
+}
+
+int
+ndb_mgm_configuration_iterator::get(int param,
+ unsigned long long * value) const{
+ return m_config.get(param, value) != true;
+}
+
+int
+ndb_mgm_configuration_iterator::get(int param, const char ** value) const {
+ return m_config.get(param, value) != true;
+}
+
+/**
+ * Published C interface
+ */
+extern "C"
+ndb_mgm_configuration_iterator*
+ndb_mgm_create_configuration_iterator(ndb_mgm_configuration * conf,
+ unsigned type_of_section){
+ ndb_mgm_configuration_iterator* iter = (ndb_mgm_configuration_iterator*)
+ malloc(sizeof(ndb_mgm_configuration_iterator));
+ if(iter == 0)
+ return 0;
+
+ return new(iter) ndb_mgm_configuration_iterator(* conf, type_of_section);
+}
+
+
+extern "C"
+void ndb_mgm_destroy_iterator(ndb_mgm_configuration_iterator* iter){
+ if(iter != 0){
+ iter->~ndb_mgm_configuration_iterator();
+ free(iter);
+ }
+}
+
+extern "C"
+int
+ndb_mgm_first(ndb_mgm_configuration_iterator* iter){
+ return iter->first();
+}
+
+extern "C"
+int
+ndb_mgm_next(ndb_mgm_configuration_iterator* iter){
+ return iter->next();
+}
+
+extern "C"
+int
+ndb_mgm_valid(const ndb_mgm_configuration_iterator* iter){
+ return iter->valid();
+}
+
+extern "C"
+int
+ndb_mgm_get_int_parameter(const ndb_mgm_configuration_iterator* iter,
+ int param, unsigned * value){
+ return iter->get(param, value);
+}
+
+extern "C"
+int
+ndb_mgm_get_int64_parameter(const ndb_mgm_configuration_iterator* iter,
+ int param, Uint64 * value){
+ return iter->get(param, value);
+}
+
+extern "C"
+int
+ndb_mgm_get_string_parameter(const ndb_mgm_configuration_iterator* iter,
+ int param, const char ** value){
+ return iter->get(param, value);
+}
+
+extern "C"
+int
+ndb_mgm_find(ndb_mgm_configuration_iterator* iter,
+ int param, unsigned search){
+ return iter->find(param, search);
+}
diff --git a/storage/ndb/src/mgmapi/mgmapi_configuration.hpp b/storage/ndb/src/mgmapi/mgmapi_configuration.hpp
new file mode 100644
index 00000000000..7d60a4842a1
--- /dev/null
+++ b/storage/ndb/src/mgmapi/mgmapi_configuration.hpp
@@ -0,0 +1,48 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 MGMAPI_CONFIGURATION_HPP
+#define MGMAPI_CONFIGURATION_HPP
+
+#include <ConfigValues.hpp>
+
+struct ndb_mgm_configuration {
+ ConfigValues m_config;
+};
+
+struct ndb_mgm_configuration_iterator {
+ Uint32 m_sectionNo;
+ Uint32 m_typeOfSection;
+ ConfigValues::ConstIterator m_config;
+
+ ndb_mgm_configuration_iterator(const ndb_mgm_configuration &, unsigned type);
+ ~ndb_mgm_configuration_iterator();
+
+ int first();
+ int next();
+ int valid() const;
+ int find(int param, unsigned value);
+
+ int get(int param, unsigned * value) const ;
+ int get(int param, Uint64 * value) const ;
+ int get(int param, const char ** value) const ;
+
+ //
+ void reset();
+ int enter();
+};
+
+#endif
diff --git a/storage/ndb/src/mgmapi/mgmapi_internal.h b/storage/ndb/src/mgmapi/mgmapi_internal.h
new file mode 100644
index 00000000000..90f93129f2a
--- /dev/null
+++ b/storage/ndb/src/mgmapi/mgmapi_internal.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2005 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 MGMAPI_INTERNAL_H
+#define MGMAPI_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <NdbTCP.h>
+
+ /**
+ * Set an integer parameter for a connection
+ *
+ * @param handle the NDB management handle.
+ * @param node1 the node1 id
+ * @param node2 the node2 id
+ * @param param the parameter (e.g. CFG_CONNECTION_SERVER_PORT)
+ * @param value what to set it to
+ * @param reply from ndb_mgmd
+ */
+ int ndb_mgm_set_connection_int_parameter(NdbMgmHandle handle,
+ int node1,
+ int node2,
+ int param,
+ int value,
+ struct ndb_mgm_reply* reply);
+
+ /**
+ * Get an integer parameter for a connection
+ *
+ * @param handle the NDB management handle.
+ * @param node1 the node1 id
+ * @param node2 the node2 id
+ * @param param the parameter (e.g. CFG_CONNECTION_SERVER_PORT)
+ * @param value where to store the retreived value. In the case of
+ * error, value is not changed.
+ * @param reply from ndb_mgmd
+ * @return 0 on success. < 0 on error.
+ */
+ int ndb_mgm_get_connection_int_parameter(NdbMgmHandle handle,
+ int node1,
+ int node2,
+ int param,
+ int *value,
+ struct ndb_mgm_reply* reply);
+
+ /**
+ * Convert connection to transporter
+ * @param handle NDB management handle.
+ *
+ * @return socket
+ *
+ * @note the socket is now able to be used as a transporter connection
+ */
+ NDB_SOCKET_TYPE ndb_mgm_convert_to_transporter(NdbMgmHandle *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/storage/ndb/src/mgmapi/ndb_logevent.cpp b/storage/ndb/src/mgmapi/ndb_logevent.cpp
new file mode 100644
index 00000000000..2817abcfdbb
--- /dev/null
+++ b/storage/ndb/src/mgmapi/ndb_logevent.cpp
@@ -0,0 +1,483 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_sys.h>
+#include <mgmapi.h>
+
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <socket_io.h>
+#include <InputStream.hpp>
+
+#include <debugger/EventLogger.hpp>
+
+#include "ndb_logevent.hpp"
+
+extern
+int ndb_mgm_listen_event_internal(NdbMgmHandle, const int filter[], int);
+
+struct ndb_logevent_error_msg {
+ enum ndb_logevent_handle_error code;
+ const char *msg;
+};
+
+struct ndb_logevent_error_msg ndb_logevent_error_messages[]= {
+ { NDB_LEH_READ_ERROR, "Read error" },
+ { NDB_LEH_MISSING_EVENT_SPECIFIER, "Missing event specifier" },
+ { NDB_LEH_UNKNOWN_EVENT_VARIABLE, "Unknown event variable" },
+ { NDB_LEH_UNKNOWN_EVENT_TYPE, "Unknown event type" },
+ { NDB_LEH_INTERNAL_ERROR, "Unknown internal error" },
+ { NDB_LEH_NO_ERROR,0}
+};
+
+struct ndb_logevent_handle {
+ NDB_SOCKET_TYPE socket;
+ enum ndb_logevent_handle_error m_error;
+};
+
+extern "C"
+NdbLogEventHandle
+ndb_mgm_create_logevent_handle(NdbMgmHandle mh,
+ const int filter[])
+{
+ int fd= ndb_mgm_listen_event_internal(mh, filter, 1);
+
+ if (fd == -1)
+ return 0;
+
+ NdbLogEventHandle h=
+ (NdbLogEventHandle)my_malloc(sizeof(ndb_logevent_handle),MYF(MY_WME));
+
+ h->socket= fd;
+
+ return h;
+}
+
+extern "C"
+void ndb_mgm_destroy_logevent_handle(NdbLogEventHandle * h)
+{
+ if( !h )
+ return;
+
+ if ( *h )
+ close((*h)->socket);
+
+ my_free((char*)* h,MYF(MY_ALLOW_ZERO_PTR));
+ * h = 0;
+}
+
+#define ROW(a,b,c,d) \
+{ NDB_LE_ ## a, b, c, 0, offsetof(struct ndb_logevent, a.d), \
+ sizeof(((struct ndb_logevent *)0)->a.d) }
+
+#define ROW_FN(a,b,c,d,e) \
+{ NDB_LE_ ## a, b, c, e, offsetof(struct ndb_logevent, a.d), \
+ sizeof(((struct ndb_logevent *)0)->a.d) }
+
+static int ref_to_node(int ref){
+ return ref & 0xFFFF;
+}
+
+struct Ndb_logevent_body_row ndb_logevent_body[]= {
+
+ // Connection
+ ROW( Connected, "node", 1, node),
+
+ ROW( Disconnected, "node", 1, node),
+
+ ROW( CommunicationClosed, "node", 1, node),
+
+ ROW( CommunicationOpened, "node", 1, node),
+
+ ROW( ConnectedApiVersion, "node", 1, node),
+ ROW( ConnectedApiVersion, "version", 2, version),
+
+ /* CHECKPOINT */
+
+ ROW( GlobalCheckpointStarted, "gci", 1, gci),
+
+ ROW( GlobalCheckpointCompleted, "gci", 1, gci),
+
+ ROW( LocalCheckpointStarted, "lci", 1, lci),
+ ROW( LocalCheckpointStarted, "keep_gci", 2, keep_gci),
+ ROW( LocalCheckpointStarted, "restore_gci", 3, restore_gci),
+
+ ROW( LocalCheckpointCompleted, "lci", 1, lci),
+
+ ROW( LCPStoppedInCalcKeepGci, "data", 1, data),
+
+ ROW( LCPFragmentCompleted, "node", 1, node),
+ ROW( LCPFragmentCompleted, "table_id", 2, table_id),
+ ROW( LCPFragmentCompleted, "fragment_id", 3, fragment_id),
+
+ ROW( UndoLogBlocked, "acc_count", 1, acc_count),
+ ROW( UndoLogBlocked, "tup_count", 2, tup_count),
+
+ /* STARTUP */
+ ROW( NDBStartStarted, "version", 1, version),
+
+ ROW( NDBStartCompleted, "version", 1, version),
+
+// ROW( STTORRYRecieved),
+
+ ROW( StartPhaseCompleted, "phase", 1, phase),
+ ROW( StartPhaseCompleted, "starttype", 2, starttype),
+
+ ROW( CM_REGCONF, "own_id", 1, own_id),
+ ROW( CM_REGCONF, "president_id", 2, president_id),
+ ROW( CM_REGCONF, "dynamic_id", 3, dynamic_id),
+
+ ROW( CM_REGREF, "own_id", 1, own_id),
+ ROW( CM_REGREF, "other_id", 2, other_id),
+ ROW( CM_REGREF, "cause", 3, cause),
+
+ ROW( FIND_NEIGHBOURS, "own_id", 1, own_id),
+ ROW( FIND_NEIGHBOURS, "left_id", 3, left_id),
+ ROW( FIND_NEIGHBOURS, "right_id", 3, right_id),
+ ROW( FIND_NEIGHBOURS, "dynamic_id", 4, dynamic_id),
+
+ ROW( NDBStopStarted, "stoptype", 1, stoptype),
+
+// ROW( NDBStopAborted),
+
+ ROW( StartREDOLog, "node", 1, node),
+ ROW( StartREDOLog, "keep_gci", 2, keep_gci),
+ ROW( StartREDOLog, "completed_gci", 3, completed_gci),
+ ROW( StartREDOLog, "restorable_gci", 4, restorable_gci),
+
+ ROW( StartLog, "log_part", 1, log_part),
+ ROW( StartLog, "start_mb", 2, start_mb),
+ ROW( StartLog, "stop_mb", 3, stop_mb),
+ ROW( StartLog, "gci", 4, gci),
+
+ ROW( UNDORecordsExecuted, "block", 1, block),
+ ROW( UNDORecordsExecuted, "data1", 2, data1),
+ ROW( UNDORecordsExecuted, "data2", 3, data2),
+ ROW( UNDORecordsExecuted, "data3", 4, data3),
+ ROW( UNDORecordsExecuted, "data4", 5, data4),
+ ROW( UNDORecordsExecuted, "data5", 6, data5),
+ ROW( UNDORecordsExecuted, "data6", 7, data6),
+ ROW( UNDORecordsExecuted, "data7", 8, data7),
+ ROW( UNDORecordsExecuted, "data8", 9, data8),
+ ROW( UNDORecordsExecuted, "data9", 10, data9),
+ ROW( UNDORecordsExecuted, "data10", 11, data10),
+
+ /* NODERESTART */
+// ROW( NR_CopyDict),
+
+// ROW( NR_CopyDistr),
+
+ ROW( NR_CopyFragsStarted, "dest_node", 1, dest_node),
+
+ ROW( NR_CopyFragDone, "dest_node", 1, dest_node),
+ ROW( NR_CopyFragDone, "table_id", 2, table_id),
+ ROW( NR_CopyFragDone, "fragment_id", 3, fragment_id),
+
+ ROW( NR_CopyFragsCompleted, "dest_node", 1, dest_node),
+
+ ROW( NodeFailCompleted, "block", 1, block), /* 0 = all */
+ ROW( NodeFailCompleted, "failed_node", 2, failed_node),
+ ROW( NodeFailCompleted, "completing_node", 3, completing_node), /* 0 = all */
+
+ ROW( NODE_FAILREP, "failed_node", 1, failed_node),
+ ROW( NODE_FAILREP, "failure_state", 2, failure_state),
+
+ /* TODO */
+ ROW( ArbitState, "code", 1, code),
+ ROW( ArbitState, "arbit_node", 2, arbit_node),
+ ROW( ArbitState, "ticket_0", 3, ticket_0),
+ ROW( ArbitState, "ticket_1", 4, ticket_1),
+
+ /* TODO */
+ ROW( ArbitResult, "code", 1, code),
+ ROW( ArbitResult, "arbit_node", 2, arbit_node),
+ ROW( ArbitResult, "ticket_0", 3, ticket_0),
+ ROW( ArbitResult, "ticket_1", 4, ticket_1),
+
+// ROW( GCP_TakeoverStarted),
+
+// ROW( GCP_TakeoverCompleted),
+
+// ROW( LCP_TakeoverStarted),
+
+ ROW( LCP_TakeoverCompleted, "state", 1, state),
+
+ /* STATISTIC */
+ ROW( TransReportCounters, "trans_count", 1, trans_count),
+ ROW( TransReportCounters, "commit_count", 2, commit_count),
+ ROW( TransReportCounters, "read_count", 3, read_count),
+ ROW( TransReportCounters, "simple_read_count", 4, simple_read_count),
+ ROW( TransReportCounters, "write_count", 5, write_count),
+ ROW( TransReportCounters, "attrinfo_count", 6, attrinfo_count),
+ ROW( TransReportCounters, "conc_op_count", 7, conc_op_count),
+ ROW( TransReportCounters, "abort_count", 8, abort_count),
+ ROW( TransReportCounters, "scan_count", 9, scan_count),
+ ROW( TransReportCounters, "range_scan_count", 10, range_scan_count),
+
+ ROW( OperationReportCounters, "ops", 1, ops),
+
+ ROW( TableCreated, "table_id", 1, table_id),
+
+ ROW( JobStatistic, "mean_loop_count", 1, mean_loop_count),
+
+ ROW( SendBytesStatistic, "to_node", 1, to_node),
+ ROW( SendBytesStatistic, "mean_sent_bytes", 2, mean_sent_bytes),
+
+ ROW( ReceiveBytesStatistic, "from_node", 1, from_node),
+ ROW( ReceiveBytesStatistic, "mean_received_bytes", 2, mean_received_bytes),
+
+ ROW( MemoryUsage, "gth", 1, gth),
+ ROW( MemoryUsage, "page_size_kb", 2, page_size_kb),
+ ROW( MemoryUsage, "pages_used", 3, pages_used),
+ ROW( MemoryUsage, "pages_total", 4, pages_total),
+ ROW( MemoryUsage, "block", 5, block),
+
+ /* ERROR */
+ ROW( TransporterError, "to_node", 1, to_node),
+ ROW( TransporterError, "code", 2, code),
+
+ ROW( TransporterWarning, "to_node", 1, to_node),
+ ROW( TransporterWarning, "code", 2, code),
+
+ ROW( MissedHeartbeat, "node", 1, node),
+ ROW( MissedHeartbeat, "count", 2, count),
+
+ ROW( DeadDueToHeartbeat, "node", 1, node),
+
+ /* TODO */
+// ROW( WarningEvent),
+
+ /* INFO */
+ ROW( SentHeartbeat, "node", 1, node),
+
+ ROW( CreateLogBytes, "node", 1, node),
+
+ /* TODO */
+// ROW( InfoEvent),
+
+ // Backup
+ ROW_FN( BackupStarted, "starting_node", 1, starting_node, ref_to_node),
+ ROW( BackupStarted, "backup_id", 2, backup_id),
+
+ ROW_FN(BackupFailedToStart,"starting_node",1, starting_node, ref_to_node),
+ ROW( BackupFailedToStart, "error", 2, error),
+
+ ROW_FN( BackupCompleted, "starting_node", 1, starting_node, ref_to_node),
+ ROW( BackupCompleted, "backup_id", 2, backup_id),
+ ROW( BackupCompleted, "start_gci", 3, start_gci),
+ ROW( BackupCompleted, "stop_gci", 4, stop_gci),
+ ROW( BackupCompleted, "n_bytes", 5, n_bytes),
+ ROW( BackupCompleted, "n_records", 6, n_records),
+ ROW( BackupCompleted, "n_log_bytes", 7, n_log_bytes),
+ ROW( BackupCompleted, "n_log_records", 8, n_log_records),
+
+ ROW_FN( BackupAborted, "starting_node", 1, starting_node, ref_to_node),
+ ROW( BackupAborted, "backup_id", 2, backup_id),
+ ROW( BackupAborted, "error", 3, error),
+
+ { NDB_LE_ILLEGAL_TYPE, 0, 0, 0, 0, 0}
+};
+
+struct Ndb_logevent_header_row {
+ const char *token; // token to use for text transfer
+ int offset; // offset into struct ndb_logevent
+ int size;
+};
+
+#define ROW2(a,b) \
+{ a, offsetof(struct ndb_logevent, b), \
+ sizeof(((struct ndb_logevent *)0)->b) }
+
+struct Ndb_logevent_header_row ndb_logevent_header[]= {
+ ROW2( "type", type),
+ ROW2( "time", time),
+ ROW2( "source_nodeid", source_nodeid),
+ { 0, 0, 0 }
+};
+
+static int
+insert_row(const char * pair, Properties & p){
+ BaseString tmp(pair);
+
+ tmp.trim(" \t\n\r");
+ Vector<BaseString> split;
+ tmp.split(split, ":=", 2);
+ if(split.size() != 2)
+ return -1;
+ p.put(split[0].trim().c_str(), split[1].trim().c_str());
+
+ return 0;
+}
+
+static
+int memcpy_atoi(void *dst, const char *str, int sz)
+{
+ switch (sz)
+ {
+ case 1:
+ {
+ Int8 val= atoi(str);
+ memcpy(dst,&val,sz);
+ return 0;
+ }
+ case 2:
+ {
+ Int16 val= atoi(str);
+ memcpy(dst,&val,sz);
+ return 0;
+ }
+ case 4:
+ {
+ Int32 val= atoi(str);
+ memcpy(dst,&val,sz);
+ return 0;
+ }
+ case 8:
+ {
+ Int64 val= atoi(str);
+ memcpy(dst,&val,sz);
+ return 0;
+ }
+ default:
+ {
+ return -1;
+ }
+ }
+}
+
+extern "C"
+int ndb_logevent_get_next(const NdbLogEventHandle h,
+ struct ndb_logevent *dst,
+ unsigned timeout_in_milliseconds)
+{
+ SocketInputStream in(h->socket, timeout_in_milliseconds);
+
+ Properties p;
+ char buf[256];
+
+ /* header */
+ while (1) {
+ if (in.gets(buf,sizeof(buf)) == 0)
+ {
+ h->m_error= NDB_LEH_READ_ERROR;
+ return -1;
+ }
+ if ( buf[0] == 0 )
+ {
+ // timed out
+ return 0;
+ }
+ if ( strcmp("log event reply\n", buf) == 0 )
+ break;
+ ndbout_c("skipped: %s", buf);
+ }
+
+ /* read name-value pairs into properties object */
+ while (1)
+ {
+ if (in.gets(buf,sizeof(buf)) == 0)
+ {
+ h->m_error= NDB_LEH_READ_ERROR;
+ return -1;
+ }
+ if ( buf[0] == 0 )
+ {
+ // timed out
+ return 0;
+ }
+ if ( buf[0] == '\n' )
+ {
+ break;
+ }
+ if (insert_row(buf,p))
+ {
+ h->m_error= NDB_LEH_READ_ERROR;
+ return -1;
+ }
+ }
+
+ int i;
+ const char *val;
+
+ dst->type= (enum Ndb_logevent_type)-1;
+ /* fill in header info from p*/
+ for (i= 0; ndb_logevent_header[i].token; i++)
+ {
+ if ( p.get(ndb_logevent_header[i].token, &val) == 0 )
+ {
+ ndbout_c("missing: %s\n", ndb_logevent_header[i].token);
+ h->m_error= NDB_LEH_MISSING_EVENT_SPECIFIER;
+ return -1;
+ }
+ if ( memcpy_atoi((char *)dst+ndb_logevent_header[i].offset, val,
+ ndb_logevent_header[i].size) )
+ {
+ h->m_error= NDB_LEH_INTERNAL_ERROR;
+ return -1;
+ }
+ }
+
+ Uint32 level;
+ LogLevel::EventCategory category;
+ Logger::LoggerLevel severity;
+ EventLoggerBase::EventTextFunction text_fn;
+
+ /* fill in rest of header info event_lookup */
+ if (EventLoggerBase::event_lookup(dst->type,category,level,severity,text_fn))
+ {
+ ndbout_c("unknown type: %d\n", dst->type);
+ h->m_error= NDB_LEH_UNKNOWN_EVENT_TYPE;
+ return -1;
+ }
+ dst->category= (enum ndb_mgm_event_category)category;
+ dst->severity= (enum ndb_mgm_event_severity)severity;
+ dst->level= level;
+
+ /* fill in header info from p */
+ for (i= 0; ndb_logevent_body[i].token; i++)
+ {
+ if ( ndb_logevent_body[i].type != dst->type )
+ continue;
+ if ( p.get(ndb_logevent_body[i].token, &val) == 0 )
+ {
+ h->m_error= NDB_LEH_UNKNOWN_EVENT_VARIABLE;
+ return -1;
+ }
+ if ( memcpy_atoi((char *)dst+ndb_logevent_body[i].offset, val,
+ ndb_logevent_body[i].size) )
+ {
+ h->m_error= NDB_LEH_INTERNAL_ERROR;
+ return -1;
+ }
+ }
+ return 1;
+}
+
+extern "C"
+int ndb_logevent_get_latest_error(const NdbLogEventHandle h)
+{
+ return h->m_error;
+}
+
+extern "C"
+const char *ndb_logevent_get_latest_error_msg(const NdbLogEventHandle h)
+{
+ for (int i= 0; ndb_logevent_error_messages[i].msg; i++)
+ if (ndb_logevent_error_messages[i].code == h->m_error)
+ return ndb_logevent_error_messages[i].msg;
+ return "<unknown error msg>";
+}
diff --git a/storage/ndb/src/mgmapi/ndb_logevent.hpp b/storage/ndb/src/mgmapi/ndb_logevent.hpp
new file mode 100644
index 00000000000..cb1a0e388e5
--- /dev/null
+++ b/storage/ndb/src/mgmapi/ndb_logevent.hpp
@@ -0,0 +1,34 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_LOGEVENT_HPP
+#define NDB_LOGEVENT_HPP
+
+#include <ndb_logevent.h>
+
+struct Ndb_logevent_body_row {
+ enum Ndb_logevent_type type; // type
+ const char *token; // token to use for text transfer
+ int index; // index into theData array
+ int (*index_fn)(int); // conversion function on the data array[index]
+ int offset; // offset into struct ndb_logevent
+ int size; // offset into struct ndb_logevent
+};
+
+extern
+struct Ndb_logevent_body_row ndb_logevent_body[];
+
+#endif
diff --git a/storage/ndb/src/mgmapi/test/Makefile b/storage/ndb/src/mgmapi/test/Makefile
new file mode 100644
index 00000000000..c6d3efa6fcc
--- /dev/null
+++ b/storage/ndb/src/mgmapi/test/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE := mgmapiclient util
+
+BIN_TARGET := testMgmapi
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon)
+#-I$(NDB_TOP)/include/util -I$(NDB_TOP)/include/portlib
+
+# Source files of non-templated classes (.C files)
+SOURCES = keso.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/mgmapi/test/keso.c b/storage/ndb/src/mgmapi/test/keso.c
new file mode 100644
index 00000000000..d2675b2ca8a
--- /dev/null
+++ b/storage/ndb/src/mgmapi/test/keso.c
@@ -0,0 +1,470 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <mgmapi.h>
+
+#ifdef VM_TRACE
+#include <mgmapi_debug.h>
+#endif
+
+#include <NdbOut.hpp>
+
+static int testConnect(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testDisconnect(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+
+static int testStatus(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testGetConfig(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+#ifdef VM_TRACE
+static int testLogSignals(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testStartSignalLog(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testStopSignalLog(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testSetTrace(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testInsertError(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testDumpState(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+#endif
+static int testFilterClusterLog(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testSetLogLevelClusterLog(NdbMgmHandle h,
+ struct ndb_mgm_reply* reply);
+static int testSetLogLevelNode(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testRestartNode(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+static int testGetStatPort(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+
+typedef int (*FUNC)(NdbMgmHandle h, struct ndb_mgm_reply* reply);
+
+struct test_case {
+ char name[255];
+ FUNC func;
+};
+
+struct test_case test_connect_disconnect[] = {
+ {"testConnect", &testConnect},
+ {"testDisconnect", &testDisconnect}
+};
+
+struct test_case tests[] = {
+ { "testStatus", &testStatus },
+ { "testFilterClusterLog", &testFilterClusterLog },
+ /*{ "testSetLogLevelClusterLog", &testSetLogLevelClusterLog },*/
+ /*{ "testSetLogLevelNode", &testSetLogLevelNode },*/
+ { "testRestartNode", &testRestartNode },
+ { "testGetStatPort", &testGetStatPort },
+#ifdef VM_TRACE
+ { "testLogSignals", &testLogSignals },
+ { "testStartSignalLog", &testStartSignalLog },
+ { "testStopSignalLog", &testStopSignalLog },
+ { "testSetTrace", &testSetTrace },
+ { "testDumpState", &testDumpState },
+ { "testInsertError", &testInsertError }
+#endif
+};
+
+static int no_of_tests = sizeof(tests) / sizeof(struct test_case);
+static int testFailed = 0;
+
+static const char * g_connect_string = "localhost:2200";
+
+int
+main(int argc, const char** argv){
+
+ struct ndb_mgm_reply reply;
+ int i = 0;
+ NdbMgmHandle h = NULL;
+
+ if(argc > 1)
+ g_connect_string = argv[1];
+
+ ndbout_c("Using connectstring: %s", g_connect_string);
+
+ for (i = 0; i < 2; i++) {
+ ndbout_c("-- %s --", test_connect_disconnect[i].name);
+ if (test_connect_disconnect[i].func(h, &reply) == 0) {
+ ndbout_c("-- Passed --");
+ } else {
+ testFailed++;
+ ndbout_c("-- Failed --");
+ }
+ }
+ ndbout_c("-- %d passed, %d failed --", (2 - testFailed), testFailed);
+
+
+ h = ndb_mgm_create_handle();
+ ndb_mgm_connect(h, g_connect_string);
+
+ for (i = 0; i < no_of_tests; i ++) {
+ ndbout_c("-- %s --", tests[i].name);
+ if (tests[i].func(h, &reply) == 0) {
+ ndbout_c("-- Passed --");
+ } else {
+ testFailed++;
+ ndbout_c("-- Failed --");
+ ndb_mgm_disconnect(h);
+ ndb_mgm_connect(h, g_connect_string);
+ }
+ }
+ ndbout_c("-- %d passed, %d failed --", (no_of_tests - testFailed),
+ testFailed);
+
+ ndb_mgm_disconnect(h);
+
+ return 0;
+}
+
+static
+int testConnect(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ h = ndb_mgm_create_handle();
+ if (h != NULL) {
+ if (ndb_mgm_connect(h, g_connect_string) == -1) {
+ ndbout_c(g_connect_string);
+ /*ndbout_c("last_error: %d", h->last_error); */
+ return -1;
+ } else {
+ ndbout_c("Connected to localhost:37123");
+ }
+
+ } else {
+ ndbout_c("Unable to create a NdbMgmHandle...");
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+int testDisconnect(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ ndb_mgm_disconnect(h);
+
+ return 0;
+}
+
+static
+int testGetConfig(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int i = 0;
+ struct ndb_mgm_configuration * config = ndb_mgm_get_configuration(h, 0);
+ if (config != NULL) {
+ free(config);
+ } else {
+ ndbout_c("Unable to get config");
+ return -1;
+ }
+ return 0;
+}
+
+static
+int testStatus(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int i = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ ndbout_c("Number of nodes: %d", cluster->no_of_nodes);
+ for (i = 0; i < cluster->no_of_nodes; i++) {
+ struct ndb_mgm_node_state state = cluster->node_states[i];
+ ndbout_c("NodeId: %d (%s)-- %s", state.node_id,
+ ndb_mgm_get_node_type_string(state.node_type),
+ ndb_mgm_get_node_status_string(state.node_status));
+
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+ return 0;
+}
+
+
+#ifdef VM_TRACE
+
+static
+int testLogSignals(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_log_signals(h, nodeId, NDB_MGM_SIGNAL_LOG_MODE_INOUT,
+ "CMVMI QMGR",
+ reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+}
+
+static
+int testStartSignalLog(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_start_signallog(h, nodeId, reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+}
+
+static
+int testStopSignalLog(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_stop_signallog(h, nodeId, reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+
+}
+
+static
+int testSetTrace(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_set_trace(h, nodeId, 2, reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+
+}
+
+static
+int testInsertError(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_insert_error(h, nodeId, 9999, reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+
+}
+
+static
+int testDumpState(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+
+ int rc = 0;
+ int nodeId = 0;
+ int dump[3];
+
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ dump[0] = 1;
+ dump[1] = 2;
+ dump[2] = 3;
+ rc = ndb_mgm_dump_state(h, nodeId, dump, 3, reply);
+
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+}
+
+#endif
+
+static
+int testFilterClusterLog(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int rc = 0;
+
+ rc = ndb_mgm_filter_clusterlog(h, NDB_MGM_CLUSTERLOG_INFO, reply);
+ if (rc == -1) {
+ ndbout_c("rc = %d", reply->return_code);
+ ndbout_c("%s", reply->message);
+ return -1;
+ }
+
+ ndbout_c("%s", reply->message);
+
+ rc = ndb_mgm_filter_clusterlog(h, NDB_MGM_CLUSTERLOG_DEBUG, reply);
+ if (rc == -1) {
+ ndbout_c("rc = %d", reply->return_code);
+ ndbout_c("%s", reply->message);
+ return -1;
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+
+}
+
+static
+int testSetLogLevelClusterLog(NdbMgmHandle h,
+ struct ndb_mgm_reply* reply) {
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_set_loglevel_clusterlog(h, nodeId,
+ NDB_MGM_EVENT_CATEGORY_CHECKPOINT,
+ 5,
+ reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+
+}
+
+static
+int testSetLogLevelNode(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+
+ int rc = 0;
+ int nodeId = 0;
+ struct ndb_mgm_cluster_state* cluster = ndb_mgm_get_status(h);
+ if (cluster != NULL) {
+ if (cluster->no_of_nodes != 0) {
+ nodeId = cluster->node_states[0].node_id;
+ }
+ free(cluster);
+ } else {
+ ndbout_c("Unable to get node status.");
+ return -1;
+ }
+
+ rc = ndb_mgm_set_loglevel_node(h, nodeId,
+ NDB_MGM_EVENT_CATEGORY_STATISTIC,
+ 15,
+ reply);
+ if (rc != 0) {
+ ndbout_c("rc = %d", reply->return_code);
+ }
+
+ ndbout_c("%s", reply->message);
+
+ return rc;
+
+}
+
+static int
+testRestartNode(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+ int restarts = 0;
+
+ restarts = ndb_mgm_restart(h, 0, 0); /* Restart all */
+ if (restarts == 0) {
+ ndbout_c("No nodes restarted...");
+ return -1;
+ } else {
+ ndbout_c("%d nodes restarted...", restarts);
+ }
+
+ return 0;
+}
+
+static
+int testGetStatPort(NdbMgmHandle h, struct ndb_mgm_reply* reply) {
+
+ int rc = 0;
+ rc = ndb_mgm_get_stat_port(h, reply);
+ if (rc == 0) {
+ ndbout_c("stat port %s", reply->message);
+ } else {
+ ndbout_c("failed");
+ }
+
+ return rc;
+}
diff --git a/storage/ndb/src/mgmapi/test/mgmSrvApi.cpp b/storage/ndb/src/mgmapi/test/mgmSrvApi.cpp
new file mode 100644
index 00000000000..4a8e38c9ba5
--- /dev/null
+++ b/storage/ndb/src/mgmapi/test/mgmSrvApi.cpp
@@ -0,0 +1,126 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/****************************************************
+ * Name:
+ * mgmSrvApi.cpp
+ *
+ * Description:
+ * Test the bahaviour of the Management Server
+ * API based on the tests specified in the
+ * "Test Specification for the Management
+ * Server API" document
+ *
+ *****************************************************/
+#include <ndb_global.h>
+#include "mgmapi.h"
+#include "mgmapi_commands.h"
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+
+/**
+ * The pupose of this test program is to
+ * verify that the Management Server
+ * API is functioning properly, i.e. a handle
+ * can be created/destroyed properly, the
+ * connection to the NDB nodes is established
+ * correctly, and all the errors are handled in
+ * a proper way.
+ * USE: mgmSrvApi -n -i
+ *
+ * @param n Number of nodes to crash
+ *
+ **/
+NDB_COMMAND(mgmSrvApi, "mgmSrvApi", "mgmSrvApi -n <Number of nodes to crash> -i <Node ID to be crashed> -p <IP address:port number>", "Management Server API testing", 65535){
+
+ const char *Addr = 0;
+ int i;
+ int nodesNo = 0; // Number of nodes to crash
+ int ndbID = 0; // Node ID to be crashed
+
+ i = 1;
+ while (argc > 1) {
+ if (strcmp(argv[i], "-n") == 0)
+ nodesNo = atoi(argv[i+1]);
+
+ if (strcmp(argv[i], "-i") == 0)
+ ndbID = atoi(argv[i+1]);
+
+ if (strcmp(argv[i], "-p") == 0)
+ Addr = argv[i+1];
+
+ argc -= 1;
+ i = i + 1;
+ }
+
+ /*
+ * Create a handle
+ */
+ ndbout << "Creating handle..." << endl;
+ NdbMgmHandle h = ndb_mgm_create_handle();
+
+ /*
+ * Perfom the connection
+ */
+ ndbout << "Connecting..." << endl;
+ if (ndb_mgm_connect(h, Addr) == -1) {
+ ndbout << "Connection to " << Addr << " failed" << endl;
+ exit(-1);
+ }
+
+ /*
+ * Get status of a node
+ */
+ ndbout << "Getting status..." << endl;
+
+ struct ndb_mgm_cluster_state * status;
+ struct ndb_mgm_node_state * nodes;
+
+ status = ndb_mgm_get_status(h);
+ nodes = status->node_states;
+ if (nodes->node_status == 1) {
+ ndbout << "No contact established" << endl;
+ // exit(-1);
+ }
+
+ /*
+ * Stop the NDB nodes
+ */
+ ndbout << "Stopping the node(s)" << endl;
+ const int * list;
+
+ if (nodesNo == 0) // all nodes stopped by definition
+ ndbID = 0;
+
+ list = &ndbID;
+ if (ndb_mgm_stop(h, nodesNo, list) != 1) {
+ ndbout << nodesNo << " NDB node(s) not stopped " << endl;
+ }
+
+ /*
+ * Disconnect from the management server
+ */
+ ndbout << "Disconnecting..." << endl;
+ ndb_mgm_disconnect(h);
+
+ /*
+ * Destroy the handle
+ */
+ ndbout << "Destroying the handle..." << endl;
+ ndb_mgm_destroy_handle(&h);
+
+ return 0;
+}
diff --git a/storage/ndb/src/mgmclient/CommandInterpreter.cpp b/storage/ndb/src/mgmclient/CommandInterpreter.cpp
new file mode 100644
index 00000000000..389fc08b97c
--- /dev/null
+++ b/storage/ndb/src/mgmclient/CommandInterpreter.cpp
@@ -0,0 +1,2297 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_sys.h>
+
+//#define HAVE_GLOBAL_REPLICATION
+
+#include <Vector.hpp>
+#ifdef HAVE_GLOBAL_REPLICATION
+#include "../rep/repapi/repapi.h"
+#endif
+
+#include <mgmapi.h>
+
+class MgmtSrvr;
+
+/**
+ * @class CommandInterpreter
+ * @brief Reads command line in management client
+ *
+ * This class has one public method which reads a command line
+ * from a stream. It then interpret that commmand line and calls a suitable
+ * method in the MgmtSrvr class which executes the command.
+ *
+ * For command syntax, see the HELP command.
+ */
+class CommandInterpreter {
+public:
+ /**
+ * Constructor
+ * @param mgmtSrvr: Management server to use when executing commands
+ */
+ CommandInterpreter(const char *, int verbose);
+ ~CommandInterpreter();
+
+ /**
+ * Reads one line from the stream, parse the line to find
+ * a command and then calls a suitable method which executes
+ * the command.
+ *
+ * @return true until quit/bye/exit has been typed
+ */
+ int execute(const char *_line, int _try_reconnect=-1, int *error= 0);
+
+private:
+ void printError();
+ int execute_impl(const char *_line);
+
+ /**
+ * Analyse the command line, after the first token.
+ *
+ * @param processId: DB process id to send command to or -1 if
+ * command will be sent to all DB processes.
+ * @param allAfterFirstToken: What the client gave after the
+ * first token on the command line
+ */
+ void analyseAfterFirstToken(int processId, char* allAfterFirstTokenCstr);
+
+ /**
+ * Parse the block specification part of the LOG* commands,
+ * things after LOG*: [BLOCK = {ALL|<blockName>+}]
+ *
+ * @param allAfterLog: What the client gave after the second token
+ * (LOG*) on the command line
+ * @param blocks, OUT: ALL or name of all the blocks
+ * @return: true if correct syntax, otherwise false
+ */
+ bool parseBlockSpecification(const char* allAfterLog,
+ Vector<const char*>& blocks);
+
+ /**
+ * A bunch of execute functions: Executes one of the commands
+ *
+ * @param processId: DB process id to send command to
+ * @param parameters: What the client gave after the command name
+ * on the command line.
+ * For example if complete input from user is: "1 LOGLEVEL 22" then the
+ * parameters argument is the string with everything after LOGLEVEL, in
+ * this case "22". Each function is responsible to check the parameters
+ * argument.
+ */
+ void executeHelp(char* parameters);
+ void executeShow(char* parameters);
+ void executeConnect(char* parameters);
+ void executePurge(char* parameters);
+ int executeShutdown(char* parameters);
+ void executeRun(char* parameters);
+ void executeInfo(char* parameters);
+ void executeClusterLog(char* parameters);
+
+public:
+ void executeStop(int processId, const char* parameters, bool all);
+ void executeEnterSingleUser(char* parameters);
+ void executeExitSingleUser(char* parameters);
+ void executeStart(int processId, const char* parameters, bool all);
+ void executeRestart(int processId, const char* parameters, bool all);
+ void executeLogLevel(int processId, const char* parameters, bool all);
+ void executeError(int processId, const char* parameters, bool all);
+ void executeLog(int processId, const char* parameters, bool all);
+ void executeLogIn(int processId, const char* parameters, bool all);
+ void executeLogOut(int processId, const char* parameters, bool all);
+ void executeLogOff(int processId, const char* parameters, bool all);
+ void executeTestOn(int processId, const char* parameters, bool all);
+ void executeTestOff(int processId, const char* parameters, bool all);
+ void executeSet(int processId, const char* parameters, bool all);
+ void executeGetStat(int processId, const char* parameters, bool all);
+ void executeStatus(int processId, const char* parameters, bool all);
+ void executeEventReporting(int processId, const char* parameters, bool all);
+ void executeDumpState(int processId, const char* parameters, bool all);
+ int executeStartBackup(char * parameters);
+ void executeAbortBackup(char * parameters);
+
+ void executeRep(char* parameters);
+
+ void executeCpc(char * parameters);
+
+public:
+ bool connect();
+ bool disconnect();
+
+ /**
+ * A execute function definition
+ */
+public:
+ typedef void (CommandInterpreter::* ExecuteFunction)(int processId,
+ const char * param,
+ bool all);
+
+ struct CommandFunctionPair {
+ const char * command;
+ ExecuteFunction executeFunction;
+ };
+private:
+ /**
+ *
+ */
+ void executeForAll(const char * cmd,
+ ExecuteFunction fun,
+ const char * param);
+
+ NdbMgmHandle m_mgmsrv;
+ NdbMgmHandle m_mgmsrv2;
+ bool m_connected;
+ int m_verbose;
+ int try_reconnect;
+ int m_error;
+#ifdef HAVE_GLOBAL_REPLICATION
+ NdbRepHandle m_repserver;
+ const char *rep_host;
+ bool rep_connected;
+#endif
+ struct NdbThread* m_event_thread;
+};
+
+
+/*
+ * Facade object for CommandInterpreter
+ */
+
+#include "ndb_mgmclient.hpp"
+#include "ndb_mgmclient.h"
+
+Ndb_mgmclient::Ndb_mgmclient(const char *host,int verbose)
+{
+ m_cmd= new CommandInterpreter(host,verbose);
+}
+Ndb_mgmclient::~Ndb_mgmclient()
+{
+ delete m_cmd;
+}
+int Ndb_mgmclient::execute(const char *_line, int _try_reconnect, int *error)
+{
+ return m_cmd->execute(_line,_try_reconnect,error);
+}
+int
+Ndb_mgmclient::disconnect()
+{
+ return m_cmd->disconnect();
+}
+
+extern "C" {
+ Ndb_mgmclient_handle ndb_mgmclient_handle_create(const char *connect_string)
+ {
+ return (Ndb_mgmclient_handle) new Ndb_mgmclient(connect_string);
+ }
+ int ndb_mgmclient_execute(Ndb_mgmclient_handle h, int argc, char** argv)
+ {
+ return ((Ndb_mgmclient*)h)->execute(argc, argv, 1);
+ }
+ int ndb_mgmclient_handle_destroy(Ndb_mgmclient_handle h)
+ {
+ delete (Ndb_mgmclient*)h;
+ return 0;
+ }
+}
+/*
+ * The CommandInterpreter
+ */
+
+#include <mgmapi.h>
+#include <mgmapi_debug.h>
+#include <version.h>
+#include <NdbAutoPtr.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbMem.h>
+#include <EventLogger.hpp>
+#include <signaldata/SetLogLevelOrd.hpp>
+#include <signaldata/GrepImpl.hpp>
+#ifdef HAVE_GLOBAL_REPLICATION
+
+#endif // HAVE_GLOBAL_REPLICATION
+#include "MgmtErrorReporter.hpp"
+#include <Parser.hpp>
+#include <SocketServer.hpp>
+#include <util/InputStream.hpp>
+#include <util/OutputStream.hpp>
+
+int Ndb_mgmclient::execute(int argc, char** argv, int _try_reconnect, int *error)
+{
+ if (argc <= 0)
+ return 0;
+ BaseString _line(argv[0]);
+ for (int i= 1; i < argc; i++)
+ {
+ _line.appfmt(" %s", argv[i]);
+ }
+ return m_cmd->execute(_line.c_str(),_try_reconnect, error);
+}
+
+/*****************************************************************************
+ * HELP
+ *****************************************************************************/
+static const char* helpText =
+"---------------------------------------------------------------------------\n"
+" NDB Cluster -- Management Client -- Help\n"
+"---------------------------------------------------------------------------\n"
+"HELP Print help text\n"
+"HELP SHOW Help for SHOW command\n"
+#ifdef HAVE_GLOBAL_REPLICATION
+"HELP REPLICATION Help for global replication\n"
+#endif // HAVE_GLOBAL_REPLICATION
+#ifdef VM_TRACE // DEBUG ONLY
+"HELP DEBUG Help for debug compiled version\n"
+#endif
+"SHOW Print information about cluster\n"
+#if 0
+"SHOW CONFIG Print configuration\n"
+"SHOW PARAMETERS Print configuration parameters\n"
+#endif
+"START BACKUP [NOWAIT | WAIT STARTED | WAIT COMPLETED]\n"
+" Start backup (default WAIT COMPLETED)\n"
+"ABORT BACKUP <backup id> Abort backup\n"
+"SHUTDOWN Shutdown all processes in cluster\n"
+"CLUSTERLOG ON [<severity>] ... Enable Cluster logging\n"
+"CLUSTERLOG OFF [<severity>] ... Disable Cluster logging\n"
+"CLUSTERLOG TOGGLE [<severity>] ... Toggle severity filter on/off\n"
+"CLUSTERLOG INFO Print cluster log information\n"
+"<id> START Start DB node (started with -n)\n"
+"<id> RESTART [-n] [-i] Restart DB node\n"
+"<id> STOP Stop DB node\n"
+"ENTER SINGLE USER MODE <api-node> Enter single user mode\n"
+"EXIT SINGLE USER MODE Exit single user mode\n"
+"<id> STATUS Print status\n"
+"<id> CLUSTERLOG {<category>=<level>}+ Set log level for cluster log\n"
+#ifdef HAVE_GLOBAL_REPLICATION
+"REP CONNECT <host:port> Connect to REP server on host:port\n"
+#endif
+"PURGE STALE SESSIONS Reset reserved nodeid's in the mgmt server\n"
+"CONNECT [<connectstring>] Connect to management server (reconnect if already connected)\n"
+"QUIT Quit management client\n"
+;
+
+static const char* helpTextShow =
+"---------------------------------------------------------------------------\n"
+" NDB Cluster -- Management Client -- Help for SHOW command\n"
+"---------------------------------------------------------------------------\n"
+"SHOW prints NDB Cluster information\n\n"
+"SHOW Print information about cluster\n"
+#if 0
+"SHOW CONFIG Print configuration (in initial config file format)\n"
+"SHOW PARAMETERS Print information about configuration parameters\n\n"
+#endif
+;
+
+#ifdef HAVE_GLOBAL_REPLICATION
+static const char* helpTextRep =
+"---------------------------------------------------------------------------\n"
+" NDB Cluster -- Management Client -- Help for Global Replication\n"
+"---------------------------------------------------------------------------\n"
+"Commands should be executed on the standby NDB Cluster\n"
+"These features are in an experimental release state.\n"
+"\n"
+"Simple Commands:\n"
+"REP START Start Global Replication\n"
+"REP START REQUESTOR Start Global Replication Requestor\n"
+"REP STATUS Show Global Replication status\n"
+"REP STOP Stop Global Replication\n"
+"REP STOP REQUESTOR Stop Global Replication Requestor\n"
+"\n"
+"Advanced Commands:\n"
+"REP START <protocol> Starts protocol\n"
+"REP STOP <protocol> Stops protocol\n"
+"<protocol> = TRANSFER | APPLY | DELETE\n"
+"\n"
+#ifdef VM_TRACE // DEBUG ONLY
+"Debugging commands:\n"
+"REP DELETE Removes epochs stored in primary and standy systems\n"
+"REP DROP <tableid> Drop a table in SS identified by table id\n"
+"REP SLOWSTOP Stop Replication (Tries to synchonize with primary)\n"
+"REP FASTSTOP Stop Replication (Stops in consistent state)\n"
+"<component> = SUBSCRIPTION\n"
+" METALOG | METASCAN | DATALOG | DATASCAN\n"
+" REQUESTOR | TRANSFER | APPLY | DELETE\n"
+#endif
+;
+#endif // HAVE_GLOBAL_REPLICATION
+
+#ifdef VM_TRACE // DEBUG ONLY
+static const char* helpTextDebug =
+"---------------------------------------------------------------------------\n"
+" NDB Cluster -- Management Client -- Help for Debugging (Internal use only)\n"
+"---------------------------------------------------------------------------\n"
+"SHOW PROPERTIES Print config properties object\n"
+"<id> LOGLEVEL {<category>=<level>}+ Set log level\n"
+#ifdef ERROR_INSERT
+"<id> ERROR <errorNo> Inject error into NDB node\n"
+#endif
+"<id> LOG [BLOCK = {ALL|<block>+}] Set logging on in & out signals\n"
+"<id> LOGIN [BLOCK = {ALL|<block>+}] Set logging on in signals\n"
+"<id> LOGOUT [BLOCK = {ALL|<block>+}] Set logging on out signals\n"
+"<id> LOGOFF [BLOCK = {ALL|<block>+}] Unset signal logging\n"
+"<id> TESTON Start signal logging\n"
+"<id> TESTOFF Stop signal logging\n"
+"<id> SET <configParamName> <value> Update configuration variable\n"
+"<id> DUMP <arg> Dump system state to cluster.log\n"
+"<id> GETSTAT Print statistics\n"
+"\n"
+"<id> = ALL | Any database node id\n"
+;
+#endif
+
+static bool
+convert(const char* s, int& val) {
+
+ if (s == NULL)
+ return false;
+
+ if (strlen(s) == 0)
+ return false;
+
+ errno = 0;
+ char* p;
+ long v = strtol(s, &p, 10);
+ if (errno != 0)
+ return false;
+
+ if (p != &s[strlen(s)])
+ return false;
+
+ val = v;
+ return true;
+}
+
+/*
+ * Constructor
+ */
+CommandInterpreter::CommandInterpreter(const char *_host,int verbose)
+ : m_verbose(verbose)
+{
+ m_mgmsrv = ndb_mgm_create_handle();
+ if(m_mgmsrv == NULL) {
+ ndbout_c("Cannot create handle to management server.");
+ exit(-1);
+ }
+ m_mgmsrv2 = ndb_mgm_create_handle();
+ if(m_mgmsrv2 == NULL) {
+ ndbout_c("Cannot create 2:nd handle to management server.");
+ exit(-1);
+ }
+ if (ndb_mgm_set_connectstring(m_mgmsrv, _host))
+ {
+ printError();
+ exit(-1);
+ }
+
+ m_connected= false;
+ m_event_thread= 0;
+ try_reconnect = 0;
+#ifdef HAVE_GLOBAL_REPLICATION
+ rep_host = NULL;
+ m_repserver = NULL;
+ rep_connected = false;
+#endif
+}
+
+/*
+ * Destructor
+ */
+CommandInterpreter::~CommandInterpreter()
+{
+ disconnect();
+ ndb_mgm_destroy_handle(&m_mgmsrv);
+ ndb_mgm_destroy_handle(&m_mgmsrv2);
+}
+
+static bool
+emptyString(const char* s)
+{
+ if (s == NULL) {
+ return true;
+ }
+
+ for (unsigned int i = 0; i < strlen(s); ++i) {
+ if (! isspace(s[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+CommandInterpreter::printError()
+{
+ if (ndb_mgm_check_connection(m_mgmsrv))
+ {
+ m_connected= false;
+ disconnect();
+ }
+ ndbout_c("* %5d: %s",
+ ndb_mgm_get_latest_error(m_mgmsrv),
+ ndb_mgm_get_latest_error_msg(m_mgmsrv));
+ ndbout_c("* %s", ndb_mgm_get_latest_error_desc(m_mgmsrv));
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+static int do_event_thread;
+static void*
+event_thread_run(void* m)
+{
+ NdbMgmHandle handle= *(NdbMgmHandle*)m;
+
+ int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP, 0 };
+ int fd = ndb_mgm_listen_event(handle, filter);
+ if (fd > 0)
+ {
+ do_event_thread= 1;
+ char *tmp= 0;
+ char buf[1024];
+ SocketInputStream in(fd,10);
+ do {
+ if (tmp == 0) NdbSleep_MilliSleep(10);
+ if((tmp = in.gets(buf, 1024)))
+ ndbout << tmp;
+ } while(do_event_thread);
+ }
+ else
+ {
+ do_event_thread= -1;
+ }
+
+ return NULL;
+}
+
+bool
+CommandInterpreter::connect()
+{
+ DBUG_ENTER("CommandInterpreter::connect");
+ if(!m_connected)
+ {
+ if(!ndb_mgm_connect(m_mgmsrv, try_reconnect-1, 5, 1))
+ {
+ const char *host= ndb_mgm_get_connected_host(m_mgmsrv);
+ unsigned port= ndb_mgm_get_connected_port(m_mgmsrv);
+ BaseString constr;
+ constr.assfmt("%s:%d",host,port);
+ if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) &&
+ !ndb_mgm_connect(m_mgmsrv2, try_reconnect-1, 5, 1))
+ {
+ DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d",
+ host, port));
+ assert(m_event_thread == 0);
+ assert(do_event_thread == 0);
+ do_event_thread= 0;
+ m_event_thread = NdbThread_Create(event_thread_run,
+ (void**)&m_mgmsrv2,
+ 32768,
+ "CommandInterpreted_event_thread",
+ NDB_THREAD_PRIO_LOW);
+ if (m_event_thread != 0)
+ {
+ DBUG_PRINT("info",("Thread created ok, waiting for started..."));
+ int iter= 1000; // try for 30 seconds
+ while(do_event_thread == 0 &&
+ iter-- > 0)
+ NdbSleep_MilliSleep(30);
+ }
+ if (m_event_thread == 0 ||
+ do_event_thread == 0 ||
+ do_event_thread == -1)
+ {
+ DBUG_PRINT("warning",("thread not started"));
+ printf("Warning, event thread startup failed, degraded printouts as result\n");
+ do_event_thread= 0;
+ }
+ }
+ else
+ {
+ DBUG_PRINT("warning",
+ ("Could not do 2:nd connect to mgmtserver for event listening"));
+ DBUG_PRINT("info", ("code: %d, msg: %s",
+ ndb_mgm_get_latest_error(m_mgmsrv2),
+ ndb_mgm_get_latest_error_msg(m_mgmsrv2)));
+ printf("Warning, event connect failed, degraded printouts as result\n");
+ printf("code: %d, msg: %s\n",
+ ndb_mgm_get_latest_error(m_mgmsrv2),
+ ndb_mgm_get_latest_error_msg(m_mgmsrv2));
+ }
+ m_connected= true;
+ DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port));
+ if (m_verbose)
+ {
+ printf("Connected to Management Server at: %s:%d\n",
+ host, port);
+ }
+ }
+ }
+ DBUG_RETURN(m_connected);
+}
+
+bool
+CommandInterpreter::disconnect()
+{
+ if (m_event_thread) {
+ void *res;
+ do_event_thread= 0;
+ NdbThread_WaitFor(m_event_thread, &res);
+ NdbThread_Destroy(&m_event_thread);
+ m_event_thread= 0;
+ ndb_mgm_disconnect(m_mgmsrv2);
+ }
+ if (m_connected)
+ {
+ if (ndb_mgm_disconnect(m_mgmsrv) == -1) {
+ ndbout_c("Could not disconnect from management server");
+ printError();
+ }
+ m_connected= false;
+ }
+ return true;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+int
+CommandInterpreter::execute(const char *_line, int _try_reconnect,
+ int *error)
+{
+ if (_try_reconnect >= 0)
+ try_reconnect=_try_reconnect;
+ int result= execute_impl(_line);
+ if (error)
+ *error= m_error;
+ return result;
+}
+
+static void
+invalid_command(const char *cmd)
+{
+ ndbout << "Invalid command: " << cmd << endl;
+ ndbout << "Type HELP for help." << endl << endl;
+}
+
+int
+CommandInterpreter::execute_impl(const char *_line)
+{
+ DBUG_ENTER("CommandInterpreter::execute_impl");
+ DBUG_PRINT("enter",("line=\"%s\"",_line));
+ m_error= 0;
+
+ char * line;
+ if(_line == NULL) {
+ DBUG_RETURN(false);
+ }
+ line = my_strdup(_line,MYF(MY_WME));
+ My_auto_ptr<char> ptr(line);
+
+ int do_continue;
+ do {
+ do_continue= 0;
+ BaseString::trim(line," \t");
+ if (line[0] == 0 ||
+ line[0] == '#')
+ {
+ DBUG_RETURN(true);
+ }
+ // for mysql client compatability remove trailing ';'
+ {
+ unsigned last= strlen(line)-1;
+ if (line[last] == ';')
+ {
+ line[last]= 0;
+ do_continue= 1;
+ }
+ }
+ } while (do_continue);
+ // if there is anything in the line proceed
+ char* firstToken = strtok(line, " ");
+ char* allAfterFirstToken = strtok(NULL, "");
+
+ if (strcasecmp(firstToken, "HELP") == 0 ||
+ strcasecmp(firstToken, "?") == 0) {
+ executeHelp(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if (strcasecmp(firstToken, "CONNECT") == 0) {
+ executeConnect(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if (strcasecmp(firstToken, "SLEEP") == 0) {
+ if (allAfterFirstToken)
+ sleep(atoi(allAfterFirstToken));
+ DBUG_RETURN(true);
+ }
+ else if((strcasecmp(firstToken, "QUIT") == 0 ||
+ strcasecmp(firstToken, "EXIT") == 0 ||
+ strcasecmp(firstToken, "BYE") == 0) &&
+ allAfterFirstToken == NULL){
+ DBUG_RETURN(false);
+ }
+
+ if (!connect())
+ DBUG_RETURN(true);
+
+ if (strcasecmp(firstToken, "SHOW") == 0) {
+ executeShow(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if (strcasecmp(firstToken, "SHUTDOWN") == 0) {
+ m_error= executeShutdown(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if (strcasecmp(firstToken, "CLUSTERLOG") == 0){
+ executeClusterLog(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if(strcasecmp(firstToken, "START") == 0 &&
+ allAfterFirstToken != NULL &&
+ strncasecmp(allAfterFirstToken, "BACKUP", sizeof("BACKUP") - 1) == 0){
+ m_error= executeStartBackup(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if(strcasecmp(firstToken, "ABORT") == 0 &&
+ allAfterFirstToken != NULL &&
+ strncasecmp(allAfterFirstToken, "BACKUP", sizeof("BACKUP") - 1) == 0){
+ executeAbortBackup(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if (strcasecmp(firstToken, "PURGE") == 0) {
+ executePurge(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+#ifdef HAVE_GLOBAL_REPLICATION
+ else if(strcasecmp(firstToken, "REPLICATION") == 0 ||
+ strcasecmp(firstToken, "REP") == 0) {
+ executeRep(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+#endif // HAVE_GLOBAL_REPLICATION
+ else if(strcasecmp(firstToken, "ENTER") == 0 &&
+ allAfterFirstToken != NULL &&
+ strncasecmp(allAfterFirstToken, "SINGLE USER MODE ",
+ sizeof("SINGLE USER MODE") - 1) == 0){
+ executeEnterSingleUser(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if(strcasecmp(firstToken, "EXIT") == 0 &&
+ allAfterFirstToken != NULL &&
+ strncasecmp(allAfterFirstToken, "SINGLE USER MODE ",
+ sizeof("SINGLE USER MODE") - 1) == 0){
+ executeExitSingleUser(allAfterFirstToken);
+ DBUG_RETURN(true);
+ }
+ else if (strcasecmp(firstToken, "ALL") == 0) {
+ analyseAfterFirstToken(-1, allAfterFirstToken);
+ } else {
+ /**
+ * First token should be a digit, node ID
+ */
+ int nodeId;
+
+ if (! convert(firstToken, nodeId)) {
+ invalid_command(_line);
+ DBUG_RETURN(true);
+ }
+
+ if (nodeId <= 0) {
+ ndbout << "Invalid node ID: " << firstToken << "." << endl;
+ DBUG_RETURN(true);
+ }
+
+ analyseAfterFirstToken(nodeId, allAfterFirstToken);
+
+ }
+ DBUG_RETURN(true);
+}
+
+
+/**
+ * List of commands used as second command argument
+ */
+static const CommandInterpreter::CommandFunctionPair commands[] = {
+ { "START", &CommandInterpreter::executeStart }
+ ,{ "RESTART", &CommandInterpreter::executeRestart }
+ ,{ "STOP", &CommandInterpreter::executeStop }
+ ,{ "STATUS", &CommandInterpreter::executeStatus }
+ ,{ "LOGLEVEL", &CommandInterpreter::executeLogLevel }
+ ,{ "CLUSTERLOG", &CommandInterpreter::executeEventReporting }
+#ifdef ERROR_INSERT
+ ,{ "ERROR", &CommandInterpreter::executeError }
+#endif
+ ,{ "LOG", &CommandInterpreter::executeLog }
+ ,{ "LOGIN", &CommandInterpreter::executeLogIn }
+ ,{ "LOGOUT", &CommandInterpreter::executeLogOut }
+ ,{ "LOGOFF", &CommandInterpreter::executeLogOff }
+ ,{ "TESTON", &CommandInterpreter::executeTestOn }
+ ,{ "TESTOFF", &CommandInterpreter::executeTestOff }
+ ,{ "SET", &CommandInterpreter::executeSet }
+ ,{ "GETSTAT", &CommandInterpreter::executeGetStat }
+ ,{ "DUMP", &CommandInterpreter::executeDumpState }
+};
+
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::analyseAfterFirstToken(int processId,
+ char* allAfterFirstToken) {
+
+ if (emptyString(allAfterFirstToken)) {
+ ndbout << "Expected a command after "
+ << ((processId == -1) ? "ALL." : "node ID.") << endl;
+ return;
+ }
+
+ char* secondToken = strtok(allAfterFirstToken, " ");
+ char* allAfterSecondToken = strtok(NULL, "\0");
+
+ const int tmpSize = sizeof(commands)/sizeof(CommandFunctionPair);
+ ExecuteFunction fun = 0;
+ const char * command = 0;
+ for(int i = 0; i<tmpSize; i++){
+ if(strcasecmp(secondToken, commands[i].command) == 0){
+ fun = commands[i].executeFunction;
+ command = commands[i].command;
+ break;
+ }
+ }
+
+ if(fun == 0){
+ invalid_command(secondToken);
+ return;
+ }
+
+ if(processId == -1){
+ executeForAll(command, fun, allAfterSecondToken);
+ } else {
+ (this->*fun)(processId, allAfterSecondToken, false);
+ }
+ ndbout << endl;
+}
+
+/**
+ * Get next nodeid larger than the give node_id. node_id will be
+ * set to the next node_id in the list. node_id should be set
+ * to 0 (zero) on the first call.
+ *
+ * @param handle the NDB management handle
+ * @param node_id last node_id retreived, 0 at first call
+ * @param type type of node to look for
+ * @return 1 if a node was found, 0 if no more node exist
+ */
+static
+int
+get_next_nodeid(struct ndb_mgm_cluster_state *cl,
+ int *node_id,
+ enum ndb_mgm_node_type type)
+{
+ int i;
+
+ if(cl == NULL)
+ return 0;
+
+ i=0;
+ while((i < cl->no_of_nodes)) {
+ if((*node_id < cl->node_states[i].node_id) &&
+ (cl->node_states[i].node_type == type)) {
+
+ if(i >= cl->no_of_nodes)
+ return 0;
+
+ *node_id = cl->node_states[i].node_id;
+ return 1;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+void
+CommandInterpreter::executeForAll(const char * cmd, ExecuteFunction fun,
+ const char * allAfterSecondToken)
+{
+ int nodeId = 0;
+ if(strcasecmp(cmd, "STOP") == 0) {
+ ndbout_c("Executing STOP on all nodes.");
+ (this->*fun)(nodeId, allAfterSecondToken, true);
+ } else if(strcasecmp(cmd, "RESTART") == 0) {
+ ndbout_c("Executing RESTART on all nodes.");
+ ndbout_c("Starting shutdown. This may take a while. Please wait...");
+ (this->*fun)(nodeId, allAfterSecondToken, true);
+ ndbout_c("Trying to start all nodes of system.");
+ ndbout_c("Use ALL STATUS to see the system start-up phases.");
+ } else {
+ struct ndb_mgm_cluster_state *cl= ndb_mgm_get_status(m_mgmsrv);
+ if(cl == 0){
+ ndbout_c("Unable get status from management server");
+ printError();
+ return;
+ }
+ NdbAutoPtr<char> ap1((char*)cl);
+ while(get_next_nodeid(cl, &nodeId, NDB_MGM_NODE_TYPE_NDB))
+ (this->*fun)(nodeId, allAfterSecondToken, true);
+ }
+}
+
+//*****************************************************************************
+//*****************************************************************************
+bool
+CommandInterpreter::parseBlockSpecification(const char* allAfterLog,
+ Vector<const char*>& blocks)
+{
+ // Parse: [BLOCK = {ALL|<blockName>+}]
+
+ if (emptyString(allAfterLog)) {
+ return true;
+ }
+
+ // Copy allAfterLog since strtok will modify it
+ char* newAllAfterLog = my_strdup(allAfterLog,MYF(MY_WME));
+ My_auto_ptr<char> ap1(newAllAfterLog);
+ char* firstTokenAfterLog = strtok(newAllAfterLog, " ");
+ for (unsigned int i = 0; i < strlen(firstTokenAfterLog); ++i) {
+ firstTokenAfterLog[i] = toupper(firstTokenAfterLog[i]);
+ }
+
+ if (strcasecmp(firstTokenAfterLog, "BLOCK") != 0) {
+ ndbout << "Unexpected value: " << firstTokenAfterLog
+ << ". Expected BLOCK." << endl;
+ return false;
+ }
+
+ char* allAfterFirstToken = strtok(NULL, "\0");
+ if (emptyString(allAfterFirstToken)) {
+ ndbout << "Expected =." << endl;
+ return false;
+ }
+
+ char* secondTokenAfterLog = strtok(allAfterFirstToken, " ");
+ if (strcasecmp(secondTokenAfterLog, "=") != 0) {
+ ndbout << "Unexpected value: " << secondTokenAfterLog
+ << ". Expected =." << endl;
+ return false;
+ }
+
+ char* blockName = strtok(NULL, " ");
+ bool all = false;
+ if (blockName != NULL && (strcasecmp(blockName, "ALL") == 0)) {
+ all = true;
+ }
+ while (blockName != NULL) {
+ blocks.push_back(strdup(blockName));
+ blockName = strtok(NULL, " ");
+ }
+
+ if (blocks.size() == 0) {
+ ndbout << "No block specified." << endl;
+ return false;
+ }
+ if (blocks.size() > 1 && all) {
+ // More than "ALL" specified
+ ndbout << "Nothing expected after ALL." << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+
+/*****************************************************************************
+ * HELP
+ *****************************************************************************/
+void
+CommandInterpreter::executeHelp(char* parameters)
+{
+ if (emptyString(parameters)) {
+ ndbout << helpText;
+
+ ndbout << endl
+ << "<severity> = "
+ << "ALERT | CRITICAL | ERROR | WARNING | INFO | DEBUG"
+ << endl;
+
+ ndbout << "<category> = ";
+ for(int i = CFG_MIN_LOGLEVEL; i <= CFG_MAX_LOGLEVEL; i++){
+ const char *str= ndb_mgm_get_event_category_string((ndb_mgm_event_category)i);
+ if (str) {
+ if (i != CFG_MIN_LOGLEVEL)
+ ndbout << " | ";
+ ndbout << str;
+ }
+ }
+ ndbout << endl;
+
+ ndbout << "<level> = " << "0 - 15" << endl;
+ ndbout << "<id> = " << "ALL | Any database node id" << endl;
+ ndbout << endl;
+ } else if (strcasecmp(parameters, "SHOW") == 0) {
+ ndbout << helpTextShow;
+#ifdef HAVE_GLOBAL_REPLICATION
+ } else if (strcasecmp(parameters, "REPLICATION") == 0 ||
+ strcasecmp(parameters, "REP") == 0) {
+ ndbout << helpTextRep;
+#endif // HAVE_GLOBAL_REPLICATION
+#ifdef VM_TRACE // DEBUG ONLY
+ } else if (strcasecmp(parameters, "DEBUG") == 0) {
+ ndbout << helpTextDebug;
+#endif
+ } else {
+ invalid_command(parameters);
+ }
+}
+
+
+/*****************************************************************************
+ * SHUTDOWN
+ *****************************************************************************/
+
+int
+CommandInterpreter::executeShutdown(char* parameters)
+{
+ ndb_mgm_cluster_state *state = ndb_mgm_get_status(m_mgmsrv);
+ if(state == NULL) {
+ ndbout_c("Could not get status");
+ printError();
+ return 1;
+ }
+ NdbAutoPtr<char> ap1((char*)state);
+
+ int result = 0;
+ result = ndb_mgm_stop(m_mgmsrv, 0, 0);
+ if (result < 0) {
+ ndbout << "Shutdown off NDB Cluster storage node(s) failed." << endl;
+ printError();
+ return result;
+ }
+
+ ndbout << result << " NDB Cluster storage node(s) have shutdown." << endl;
+
+ int mgm_id= 0;
+ for(int i=0; i < state->no_of_nodes; i++) {
+ if(state->node_states[i].node_type == NDB_MGM_NODE_TYPE_MGM &&
+ state->node_states[i].version != 0){
+ if (mgm_id == 0)
+ mgm_id= state->node_states[i].node_id;
+ else {
+ ndbout << "Unable to locate management server, "
+ << "shutdown manually with <id> STOP"
+ << endl;
+ return 1;
+ }
+ }
+ }
+
+ result = ndb_mgm_stop(m_mgmsrv, 1, &mgm_id);
+ if (result <= 0) {
+ ndbout << "Shutdown of NDB Cluster management server failed." << endl;
+ printError();
+ if (result == 0)
+ return 1;
+ return result;
+ }
+
+ m_connected= false;
+ disconnect();
+ ndbout << "NDB Cluster management server shutdown." << endl;
+ return 0;
+}
+
+/*****************************************************************************
+ * SHOW
+ *****************************************************************************/
+
+
+static
+const char *status_string(ndb_mgm_node_status status)
+{
+ switch(status){
+ case NDB_MGM_NODE_STATUS_NO_CONTACT:
+ return "not connected";
+ case NDB_MGM_NODE_STATUS_NOT_STARTED:
+ return "not started";
+ case NDB_MGM_NODE_STATUS_STARTING:
+ return "starting";
+ case NDB_MGM_NODE_STATUS_STARTED:
+ return "started";
+ case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
+ return "shutting down";
+ case NDB_MGM_NODE_STATUS_RESTARTING:
+ return "restarting";
+ case NDB_MGM_NODE_STATUS_SINGLEUSER:
+ return "single user mode";
+ default:
+ return "unknown state";
+ }
+}
+
+static void
+print_nodes(ndb_mgm_cluster_state *state, ndb_mgm_configuration_iterator *it,
+ const char *proc_name, int no_proc, ndb_mgm_node_type type,
+ int master_id)
+{
+ int i;
+ ndbout << "[" << proc_name
+ << "(" << ndb_mgm_get_node_type_string(type) << ")]\t"
+ << no_proc << " node(s)" << endl;
+ for(i=0; i < state->no_of_nodes; i++) {
+ struct ndb_mgm_node_state *node_state= &(state->node_states[i]);
+ if(node_state->node_type == type) {
+ int node_id= node_state->node_id;
+ ndbout << "id=" << node_id;
+ if(node_state->version != 0) {
+ const char *hostname= node_state->connect_address;
+ if (hostname == 0
+ || strlen(hostname) == 0
+ || strcasecmp(hostname,"0.0.0.0") == 0)
+ ndbout << " ";
+ else
+ ndbout << "\t@" << hostname;
+ ndbout << " (Version: "
+ << getMajor(node_state->version) << "."
+ << getMinor(node_state->version) << "."
+ << getBuild(node_state->version);
+ if (type == NDB_MGM_NODE_TYPE_NDB) {
+ if (node_state->node_status != NDB_MGM_NODE_STATUS_STARTED) {
+ ndbout << ", " << status_string(node_state->node_status);
+ }
+ if (node_state->node_group >= 0) {
+ ndbout << ", Nodegroup: " << node_state->node_group;
+ if (node_state->dynamic_id == master_id)
+ ndbout << ", Master";
+ }
+ }
+ ndbout << ")" << endl;
+ } else {
+ ndb_mgm_first(it);
+ if(ndb_mgm_find(it, CFG_NODE_ID, node_id) == 0){
+ const char *config_hostname= 0;
+ ndb_mgm_get_string_parameter(it, CFG_NODE_HOST, &config_hostname);
+ if (config_hostname == 0 || config_hostname[0] == 0)
+ config_hostname= "any host";
+ ndbout_c(" (not connected, accepting connect from %s)",
+ config_hostname);
+ }
+ else
+ {
+ ndbout_c("Unable to find node with id: %d", node_id);
+ }
+ }
+ }
+ }
+ ndbout << endl;
+}
+
+void
+CommandInterpreter::executePurge(char* parameters)
+{
+ int command_ok= 0;
+ do {
+ if (emptyString(parameters))
+ break;
+ char* firstToken = strtok(parameters, " ");
+ char* nextToken = strtok(NULL, " \0");
+ if (strcasecmp(firstToken,"STALE") == 0 &&
+ nextToken &&
+ strcasecmp(nextToken, "SESSIONS") == 0) {
+ command_ok= 1;
+ break;
+ }
+ } while(0);
+
+ if (!command_ok) {
+ ndbout_c("Unexpected command, expected: PURGE STALE SESSIONS");
+ return;
+ }
+
+ int i;
+ char *str;
+
+ if (ndb_mgm_purge_stale_sessions(m_mgmsrv, &str)) {
+ ndbout_c("Command failed");
+ return;
+ }
+ if (str) {
+ ndbout_c("Purged sessions with node id's: %s", str);
+ free(str);
+ }
+ else
+ {
+ ndbout_c("No sessions purged");
+ }
+}
+
+void
+CommandInterpreter::executeShow(char* parameters)
+{
+ int i;
+ if (emptyString(parameters)) {
+ ndb_mgm_cluster_state *state = ndb_mgm_get_status(m_mgmsrv);
+ if(state == NULL) {
+ ndbout_c("Could not get status");
+ printError();
+ return;
+ }
+ NdbAutoPtr<char> ap1((char*)state);
+
+ ndb_mgm_configuration * conf = ndb_mgm_get_configuration(m_mgmsrv,0);
+ if(conf == 0){
+ ndbout_c("Could not get configuration");
+ printError();
+ return;
+ }
+
+ ndb_mgm_configuration_iterator * it;
+ it = ndb_mgm_create_configuration_iterator((struct ndb_mgm_configuration *)conf, CFG_SECTION_NODE);
+
+ if(it == 0){
+ ndbout_c("Unable to create config iterator");
+ return;
+ }
+ NdbAutoPtr<ndb_mgm_configuration_iterator> ptr(it);
+
+ int
+ master_id= 0,
+ ndb_nodes= 0,
+ api_nodes= 0,
+ mgm_nodes= 0;
+
+ for(i=0; i < state->no_of_nodes; i++) {
+ if(state->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB &&
+ state->node_states[i].version != 0){
+ master_id= state->node_states[i].dynamic_id;
+ break;
+ }
+ }
+
+ for(i=0; i < state->no_of_nodes; i++) {
+ switch(state->node_states[i].node_type) {
+ case NDB_MGM_NODE_TYPE_API:
+ api_nodes++;
+ break;
+ case NDB_MGM_NODE_TYPE_NDB:
+ if (state->node_states[i].dynamic_id < master_id)
+ master_id= state->node_states[i].dynamic_id;
+ ndb_nodes++;
+ break;
+ case NDB_MGM_NODE_TYPE_MGM:
+ mgm_nodes++;
+ break;
+ case NDB_MGM_NODE_TYPE_UNKNOWN:
+ ndbout << "Error: Unknown Node Type" << endl;
+ return;
+ case NDB_MGM_NODE_TYPE_REP:
+ abort();
+ }
+ }
+
+ ndbout << "Cluster Configuration" << endl
+ << "---------------------" << endl;
+ print_nodes(state, it, "ndbd", ndb_nodes, NDB_MGM_NODE_TYPE_NDB, master_id);
+ print_nodes(state, it, "ndb_mgmd", mgm_nodes, NDB_MGM_NODE_TYPE_MGM, 0);
+ print_nodes(state, it, "mysqld", api_nodes, NDB_MGM_NODE_TYPE_API, 0);
+ // ndbout << helpTextShow;
+ return;
+ } else if (strcasecmp(parameters, "PROPERTIES") == 0 ||
+ strcasecmp(parameters, "PROP") == 0) {
+ ndbout << "SHOW PROPERTIES is not yet implemented." << endl;
+ // ndbout << "_mgmtSrvr.getConfig()->print();" << endl; /* XXX */
+ } else if (strcasecmp(parameters, "CONFIGURATION") == 0 ||
+ strcasecmp(parameters, "CONFIG") == 0){
+ ndbout << "SHOW CONFIGURATION is not yet implemented." << endl;
+ //nbout << "_mgmtSrvr.getConfig()->printConfigFile();" << endl; /* XXX */
+ } else if (strcasecmp(parameters, "PARAMETERS") == 0 ||
+ strcasecmp(parameters, "PARAMS") == 0 ||
+ strcasecmp(parameters, "PARAM") == 0) {
+ ndbout << "SHOW PARAMETERS is not yet implemented." << endl;
+ // ndbout << "_mgmtSrvr.getConfig()->getConfigInfo()->print();"
+ // << endl; /* XXX */
+ } else {
+ ndbout << "Invalid argument." << endl;
+ }
+}
+
+void
+CommandInterpreter::executeConnect(char* parameters)
+{
+ disconnect();
+ if (!emptyString(parameters)) {
+ if (ndb_mgm_set_connectstring(m_mgmsrv,
+ BaseString(parameters).trim().c_str()))
+ {
+ printError();
+ return;
+ }
+ }
+ connect();
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeClusterLog(char* parameters)
+{
+ DBUG_ENTER("CommandInterpreter::executeClusterLog");
+ int i;
+ if (emptyString(parameters))
+ {
+ ndbout << "Missing argument." << endl;
+ DBUG_VOID_RETURN;
+ }
+
+ enum ndb_mgm_event_severity severity = NDB_MGM_EVENT_SEVERITY_ALL;
+
+ char * tmpString = my_strdup(parameters,MYF(MY_WME));
+ My_auto_ptr<char> ap1(tmpString);
+ char * tmpPtr = 0;
+ char * item = strtok_r(tmpString, " ", &tmpPtr);
+ int enable;
+
+ const unsigned int *enabled= ndb_mgm_get_logfilter(m_mgmsrv);
+ if(enabled == NULL) {
+ ndbout << "Couldn't get status" << endl;
+ printError();
+ DBUG_VOID_RETURN;
+ }
+
+ /********************
+ * CLUSTERLOG INFO
+ ********************/
+ if (strcasecmp(item, "INFO") == 0) {
+ DBUG_PRINT("info",("INFO"));
+ if(enabled[0] == 0)
+ {
+ ndbout << "Cluster logging is disabled." << endl;
+ DBUG_VOID_RETURN;
+ }
+#if 0
+ for(i = 0; i<7;i++)
+ printf("enabled[%d] = %d\n", i, enabled[i]);
+#endif
+ ndbout << "Severities enabled: ";
+ for(i = 1; i < (int)NDB_MGM_EVENT_SEVERITY_ALL; i++) {
+ const char *str= ndb_mgm_get_event_severity_string((ndb_mgm_event_severity)i);
+ if (str == 0)
+ {
+ DBUG_ASSERT(false);
+ continue;
+ }
+ if(enabled[i])
+ ndbout << BaseString(str).ndb_toupper() << " ";
+ }
+ ndbout << endl;
+ DBUG_VOID_RETURN;
+
+ }
+ else if (strcasecmp(item, "FILTER") == 0 ||
+ strcasecmp(item, "TOGGLE") == 0)
+ {
+ DBUG_PRINT("info",("TOGGLE"));
+ enable= -1;
+ }
+ else if (strcasecmp(item, "OFF") == 0)
+ {
+ DBUG_PRINT("info",("OFF"));
+ enable= 0;
+ } else if (strcasecmp(item, "ON") == 0) {
+ DBUG_PRINT("info",("ON"));
+ enable= 1;
+ } else {
+ ndbout << "Invalid argument." << endl;
+ DBUG_VOID_RETURN;
+ }
+
+ int res_enable;
+ item = strtok_r(NULL, " ", &tmpPtr);
+ if (item == NULL) {
+ res_enable=
+ ndb_mgm_set_clusterlog_severity_filter(m_mgmsrv,
+ NDB_MGM_EVENT_SEVERITY_ON,
+ enable, NULL);
+ if (res_enable < 0)
+ {
+ ndbout << "Couldn't set filter" << endl;
+ printError();
+ DBUG_VOID_RETURN;
+ }
+ ndbout << "Cluster logging is " << (res_enable ? "enabled.":"disabled") << endl;
+ DBUG_VOID_RETURN;
+ }
+
+ do {
+ severity= NDB_MGM_ILLEGAL_EVENT_SEVERITY;
+ if (strcasecmp(item, "ALL") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_ALL;
+ } else if (strcasecmp(item, "ALERT") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_ALERT;
+ } else if (strcasecmp(item, "CRITICAL") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_CRITICAL;
+ } else if (strcasecmp(item, "ERROR") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_ERROR;
+ } else if (strcasecmp(item, "WARNING") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_WARNING;
+ } else if (strcasecmp(item, "INFO") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_INFO;
+ } else if (strcasecmp(item, "DEBUG") == 0) {
+ severity = NDB_MGM_EVENT_SEVERITY_DEBUG;
+ } else if (strcasecmp(item, "OFF") == 0 ||
+ strcasecmp(item, "ON") == 0) {
+ if (enable < 0) // only makes sense with toggle
+ severity = NDB_MGM_EVENT_SEVERITY_ON;
+ }
+ if (severity == NDB_MGM_ILLEGAL_EVENT_SEVERITY) {
+ ndbout << "Invalid severity level: " << item << endl;
+ DBUG_VOID_RETURN;
+ }
+
+ res_enable= ndb_mgm_set_clusterlog_severity_filter(m_mgmsrv, severity,
+ enable, NULL);
+ if (res_enable < 0)
+ {
+ ndbout << "Couldn't set filter" << endl;
+ printError();
+ DBUG_VOID_RETURN;
+ }
+ ndbout << BaseString(item).ndb_toupper().c_str() << " " << (res_enable ? "enabled":"disabled") << endl;
+
+ item = strtok_r(NULL, " ", &tmpPtr);
+ } while(item != NULL);
+
+ DBUG_VOID_RETURN;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+void
+CommandInterpreter::executeStop(int processId, const char *, bool all)
+{
+ int result = 0;
+ if(all) {
+ result = ndb_mgm_stop(m_mgmsrv, 0, 0);
+ } else {
+ result = ndb_mgm_stop(m_mgmsrv, 1, &processId);
+ }
+ if (result < 0) {
+ ndbout << "Shutdown failed." << endl;
+ printError();
+ } else
+ {
+ if(all)
+ ndbout << "NDB Cluster has shutdown." << endl;
+ else
+ ndbout << "Node " << processId << " has shutdown." << endl;
+ }
+}
+
+void
+CommandInterpreter::executeEnterSingleUser(char* parameters)
+{
+ strtok(parameters, " ");
+ struct ndb_mgm_reply reply;
+ char* id = strtok(NULL, " ");
+ id = strtok(NULL, " ");
+ id = strtok(NULL, "\0");
+ int nodeId = -1;
+ if(id == 0 || sscanf(id, "%d", &nodeId) != 1){
+ ndbout_c("Invalid arguments: expected <NodeId>");
+ ndbout_c("Use SHOW to see what API nodes are configured");
+ return;
+ }
+ int result = ndb_mgm_enter_single_user(m_mgmsrv, nodeId, &reply);
+
+ if (result != 0) {
+ ndbout_c("Entering single user mode for node %d failed", nodeId);
+ printError();
+ } else {
+ ndbout_c("Entering single user mode");
+ ndbout_c("Access will be granted for API node %d only.", nodeId);
+ ndbout_c("Use ALL STATUS to see when single user mode has been entered.");
+ }
+}
+
+void
+CommandInterpreter::executeExitSingleUser(char* parameters)
+{
+ int result = ndb_mgm_exit_single_user(m_mgmsrv, 0);
+ if (result != 0) {
+ ndbout_c("Exiting single user mode failed.");
+ printError();
+ } else {
+ ndbout_c("Exiting single user mode in progress.");
+ ndbout_c("Use ALL STATUS to see when single user mode has been exited.");
+ }
+}
+
+void
+CommandInterpreter::executeStart(int processId, const char* parameters,
+ bool all)
+{
+ int result;
+ if(all) {
+ result = ndb_mgm_start(m_mgmsrv, 0, 0);
+ } else {
+ result = ndb_mgm_start(m_mgmsrv, 1, &processId);
+ }
+
+ if (result <= 0) {
+ ndbout << "Start failed." << endl;
+ printError();
+ } else
+ {
+ if(all)
+ ndbout_c("NDB Cluster is being started.");
+ else
+ ndbout_c("Database node %d is being started.", processId);
+ }
+}
+
+void
+CommandInterpreter::executeRestart(int processId, const char* parameters,
+ bool all)
+{
+ int result;
+ int nostart = 0;
+ int initialstart = 0;
+ int abort = 0;
+
+ if(parameters != 0 && strlen(parameters) != 0){
+ char * tmpString = my_strdup(parameters,MYF(MY_WME));
+ My_auto_ptr<char> ap1(tmpString);
+ char * tmpPtr = 0;
+ char * item = strtok_r(tmpString, " ", &tmpPtr);
+ while(item != NULL){
+ if(strcasecmp(item, "-N") == 0)
+ nostart = 1;
+ if(strcasecmp(item, "-I") == 0)
+ initialstart = 1;
+ if(strcasecmp(item, "-A") == 0)
+ abort = 1;
+ item = strtok_r(NULL, " ", &tmpPtr);
+ }
+ }
+
+ if(all) {
+ result = ndb_mgm_restart2(m_mgmsrv, 0, NULL, initialstart, nostart, abort);
+ } else {
+ int v[1];
+ v[0] = processId;
+ result = ndb_mgm_restart2(m_mgmsrv, 1, v, initialstart, nostart, abort);
+ }
+
+ if (result <= 0) {
+ ndbout.println("Restart failed.", result);
+ printError();
+ } else
+ {
+ if(all)
+ ndbout << "NDB Cluster is being restarted." << endl;
+ else
+ ndbout_c("Node %d is being restarted.", processId);
+ }
+}
+
+void
+CommandInterpreter::executeDumpState(int processId, const char* parameters,
+ bool all)
+{
+ if(emptyString(parameters)){
+ ndbout << "Expected argument" << endl;
+ return;
+ }
+
+ Uint32 no = 0;
+ int pars[25];
+
+ char * tmpString = my_strdup(parameters,MYF(MY_WME));
+ My_auto_ptr<char> ap1(tmpString);
+ char * tmpPtr = 0;
+ char * item = strtok_r(tmpString, " ", &tmpPtr);
+ while(item != NULL){
+ if (0x0 <= strtoll(item, NULL, 0) && strtoll(item, NULL, 0) <= 0xffffffff){
+ pars[no] = strtoll(item, NULL, 0);
+ } else {
+ ndbout << "Illegal value in argument to signal." << endl
+ << "(Value must be between 0 and 0xffffffff.)"
+ << endl;
+ return;
+ }
+ no++;
+ item = strtok_r(NULL, " ", &tmpPtr);
+ }
+ ndbout << "Sending dump signal with data:" << endl;
+ for (Uint32 i=0; i<no; i++) {
+ ndbout.setHexFormat(1) << pars[i] << " ";
+ if (!(i+1 & 0x3)) ndbout << endl;
+ }
+
+ struct ndb_mgm_reply reply;
+ ndb_mgm_dump_state(m_mgmsrv, processId, pars, no, &reply);
+}
+
+void
+CommandInterpreter::executeStatus(int processId,
+ const char* parameters, bool all)
+{
+ if (! emptyString(parameters)) {
+ ndbout_c("No parameters expected to this command.");
+ return;
+ }
+
+ ndb_mgm_node_status status;
+ Uint32 startPhase, version;
+ bool system;
+
+ struct ndb_mgm_cluster_state *cl;
+ cl = ndb_mgm_get_status(m_mgmsrv);
+ if(cl == NULL) {
+ ndbout_c("Cannot get status of node %d.", processId);
+ printError();
+ return;
+ }
+ NdbAutoPtr<char> ap1((char*)cl);
+
+ int i = 0;
+ while((i < cl->no_of_nodes) && cl->node_states[i].node_id != processId)
+ i++;
+ if(cl->node_states[i].node_id != processId) {
+ ndbout << processId << ": Node not found" << endl;
+ return;
+ }
+ status = cl->node_states[i].node_status;
+ startPhase = cl->node_states[i].start_phase;
+ version = cl->node_states[i].version;
+
+ ndbout << "Node " << processId << ": " << status_string(status);
+ switch(status){
+ case NDB_MGM_NODE_STATUS_STARTING:
+ ndbout << " (Phase " << startPhase << ")";
+ break;
+ case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
+ ndbout << " (Phase " << startPhase << ")";
+ break;
+ default:
+ break;
+ }
+ if(status != NDB_MGM_NODE_STATUS_NO_CONTACT)
+ ndbout_c(" (Version %d.%d.%d)",
+ getMajor(version) ,
+ getMinor(version),
+ getBuild(version));
+ else
+ ndbout << endl;
+}
+
+
+//*****************************************************************************
+//*****************************************************************************
+
+void
+CommandInterpreter::executeLogLevel(int processId, const char* parameters,
+ bool all)
+{
+ (void) all;
+ if (emptyString(parameters)) {
+ ndbout << "Expected argument" << endl;
+ return;
+ }
+ BaseString tmp(parameters);
+ Vector<BaseString> spec;
+ tmp.split(spec, "=");
+ if(spec.size() != 2){
+ ndbout << "Invalid loglevel specification: " << parameters << endl;
+ return;
+ }
+
+ spec[0].trim().ndb_toupper();
+ int category = ndb_mgm_match_event_category(spec[0].c_str());
+ if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
+ category = atoi(spec[0].c_str());
+ if(category < NDB_MGM_MIN_EVENT_CATEGORY ||
+ category > NDB_MGM_MAX_EVENT_CATEGORY){
+ ndbout << "Unknown category: \"" << spec[0].c_str() << "\"" << endl;
+ return;
+ }
+ }
+
+ int level = atoi(spec[1].c_str());
+ if(level < 0 || level > 15){
+ ndbout << "Invalid level: " << spec[1].c_str() << endl;
+ return;
+ }
+
+ ndbout << "Executing LOGLEVEL on node " << processId << flush;
+
+ struct ndb_mgm_reply reply;
+ int result;
+ result = ndb_mgm_set_loglevel_node(m_mgmsrv,
+ processId,
+ (ndb_mgm_event_category)category,
+ level,
+ &reply);
+
+ if (result < 0) {
+ ndbout_c(" failed.");
+ printError();
+ } else {
+ ndbout_c(" OK!");
+ }
+
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void CommandInterpreter::executeError(int processId,
+ const char* parameters, bool /* all */)
+{
+ if (emptyString(parameters)) {
+ ndbout << "Missing error number." << endl;
+ return;
+ }
+
+ // Copy parameters since strtok will modify it
+ char* newpar = my_strdup(parameters,MYF(MY_WME));
+ My_auto_ptr<char> ap1(newpar);
+ char* firstParameter = strtok(newpar, " ");
+
+ int errorNo;
+ if (! convert(firstParameter, errorNo)) {
+ ndbout << "Expected an integer." << endl;
+ return;
+ }
+
+ char* allAfterFirstParameter = strtok(NULL, "\0");
+ if (! emptyString(allAfterFirstParameter)) {
+ ndbout << "Nothing expected after error number." << endl;
+ return;
+ }
+
+ ndb_mgm_insert_error(m_mgmsrv, processId, errorNo, NULL);
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+void
+CommandInterpreter::executeLog(int processId,
+ const char* parameters, bool all)
+{
+ struct ndb_mgm_reply reply;
+ Vector<const char *> blocks;
+ if (! parseBlockSpecification(parameters, blocks)) {
+ return;
+ }
+ int len=1;
+ Uint32 i;
+ for(i=0; i<blocks.size(); i++) {
+ len += strlen(blocks[i]) + 1;
+ }
+ char * blockNames = (char*)my_malloc(len,MYF(MY_WME));
+ My_auto_ptr<char> ap1(blockNames);
+
+ blockNames[0] = 0;
+ for(i=0; i<blocks.size(); i++) {
+ strcat(blockNames, blocks[i]);
+ strcat(blockNames, "|");
+ }
+
+ int result = ndb_mgm_log_signals(m_mgmsrv,
+ processId,
+ NDB_MGM_SIGNAL_LOG_MODE_INOUT,
+ blockNames,
+ &reply);
+ if (result != 0) {
+ ndbout_c("Execute LOG on node %d failed.", processId);
+ printError();
+ }
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeLogIn(int /* processId */,
+ const char* parameters, bool /* all */)
+{
+ ndbout << "Command LOGIN not implemented." << endl;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeLogOut(int /*processId*/,
+ const char* parameters, bool /*all*/)
+{
+ ndbout << "Command LOGOUT not implemented." << endl;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeLogOff(int /*processId*/,
+ const char* parameters, bool /*all*/)
+{
+ ndbout << "Command LOGOFF not implemented." << endl;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeTestOn(int processId,
+ const char* parameters, bool /*all*/)
+{
+ if (! emptyString(parameters)) {
+ ndbout << "No parameters expected to this command." << endl;
+ return;
+ }
+ struct ndb_mgm_reply reply;
+ int result = ndb_mgm_start_signallog(m_mgmsrv, processId, &reply);
+ if (result != 0) {
+ ndbout_c("Execute TESTON failed.");
+ printError();
+ }
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeTestOff(int processId,
+ const char* parameters, bool /*all*/)
+{
+ if (! emptyString(parameters)) {
+ ndbout << "No parameters expected to this command." << endl;
+ return;
+ }
+ struct ndb_mgm_reply reply;
+ int result = ndb_mgm_stop_signallog(m_mgmsrv, processId, &reply);
+ if (result != 0) {
+ ndbout_c("Execute TESTOFF failed.");
+ printError();
+ }
+}
+
+
+//*****************************************************************************
+//*****************************************************************************
+void
+CommandInterpreter::executeSet(int /*processId*/,
+ const char* parameters, bool /*all*/)
+{
+ if (emptyString(parameters)) {
+ ndbout << "Missing parameter name." << endl;
+ return;
+ }
+#if 0
+ // Copy parameters since strtok will modify it
+ char* newpar = my_strdup(parameters,MYF(MY_WME));
+ My_auto_ptr<char> ap1(newpar);
+ char* configParameterName = strtok(newpar, " ");
+
+ char* allAfterParameterName = strtok(NULL, "\0");
+ if (emptyString(allAfterParameterName)) {
+ ndbout << "Missing parameter value." << endl;
+ return;
+ }
+
+ char* value = strtok(allAfterParameterName, " ");
+
+ char* allAfterValue = strtok(NULL, "\0");
+ if (! emptyString(allAfterValue)) {
+ ndbout << "Nothing expected after parameter value." << endl;
+ return;
+ }
+
+ bool configBackupFileUpdated;
+ bool configPrimaryFileUpdated;
+
+ // TODO The handling of the primary and backup config files should be
+ // analysed further.
+ // How it should be handled if only the backup is possible to write.
+
+ int result = _mgmtSrvr.updateConfigParam(processId, configParameterName,
+ value, configBackupFileUpdated,
+ configPrimaryFileUpdated);
+ if (result == 0) {
+ if (configBackupFileUpdated && configPrimaryFileUpdated) {
+ ndbout << "The configuration is updated." << endl;
+ }
+ else if (configBackupFileUpdated && !configPrimaryFileUpdated) {
+ ndbout << "The configuration is updated but it was only possible "
+ << "to update the backup configuration file, not the primary."
+ << endl;
+ }
+ else {
+ assert(false);
+ }
+ }
+ else {
+ ndbout << get_error_text(result) << endl;
+ if (configBackupFileUpdated && configPrimaryFileUpdated) {
+ ndbout << "The configuration files are however updated and "
+ << "the value will be used next time the process is restarted."
+ << endl;
+ }
+ else if (configBackupFileUpdated && !configPrimaryFileUpdated) {
+ ndbout << "It was only possible to update the backup "
+ << "configuration file, not the primary." << endl;
+ }
+ else if (!configBackupFileUpdated && !configPrimaryFileUpdated) {
+ ndbout << "The configuration files are not updated." << endl;
+ }
+ else {
+ // The primary is not tried to write if the write of backup file fails
+ abort();
+ }
+ }
+#endif
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void CommandInterpreter::executeGetStat(int /*processId*/,
+ const char* parameters, bool /*all*/)
+{
+ if (! emptyString(parameters)) {
+ ndbout << "No parameters expected to this command." << endl;
+ return;
+ }
+
+#if 0
+ MgmtSrvr::Statistics statistics;
+ int result = _mgmtSrvr.getStatistics(processId, statistics);
+ if (result != 0) {
+ ndbout << get_error_text(result) << endl;
+ return;
+ }
+#endif
+ // Print statistic...
+ /*
+ ndbout << "Number of GETSTAT commands: "
+ << statistics._test1 << endl;
+ */
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+void
+CommandInterpreter::executeEventReporting(int processId,
+ const char* parameters,
+ bool all)
+{
+ if (emptyString(parameters)) {
+ ndbout << "Expected argument" << endl;
+ return;
+ }
+ BaseString tmp(parameters);
+ Vector<BaseString> spec;
+ tmp.split(spec, "=");
+ if(spec.size() != 2){
+ ndbout << "Invalid loglevel specification: " << parameters << endl;
+ return;
+ }
+
+ spec[0].trim().ndb_toupper();
+ int category = ndb_mgm_match_event_category(spec[0].c_str());
+ if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
+ if(!convert(spec[0].c_str(), category) ||
+ category < NDB_MGM_MIN_EVENT_CATEGORY ||
+ category > NDB_MGM_MAX_EVENT_CATEGORY){
+ ndbout << "Unknown category: \"" << spec[0].c_str() << "\"" << endl;
+ return;
+ }
+ }
+
+ int level;
+ if (!convert(spec[1].c_str(),level))
+ {
+ ndbout << "Invalid level: " << spec[1].c_str() << endl;
+ return;
+ }
+
+ ndbout << "Executing CLUSTERLOG on node " << processId << flush;
+
+ struct ndb_mgm_reply reply;
+ int result;
+ result = ndb_mgm_set_loglevel_clusterlog(m_mgmsrv,
+ processId,
+ (ndb_mgm_event_category)category,
+ level,
+ &reply);
+
+ if (result != 0) {
+ ndbout_c(" failed.");
+ printError();
+ } else {
+ ndbout_c(" OK!");
+ }
+}
+
+/*****************************************************************************
+ * Backup
+ *****************************************************************************/
+int
+CommandInterpreter::executeStartBackup(char* parameters)
+{
+ struct ndb_mgm_reply reply;
+ unsigned int backupId;
+#if 0
+ int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP, 0 };
+ int fd = ndb_mgm_listen_event(m_mgmsrv, filter);
+ if (fd < 0)
+ {
+ ndbout << "Initializing start of backup failed" << endl;
+ printError();
+ return fd;
+ }
+#endif
+ Vector<BaseString> args;
+ {
+ BaseString(parameters).split(args);
+ for (unsigned i= 0; i < args.size(); i++)
+ if (args[i].length() == 0)
+ args.erase(i--);
+ else
+ args[i].ndb_toupper();
+ }
+ int sz= args.size();
+
+ int result;
+ if (sz == 2 &&
+ args[1] == "NOWAIT")
+ {
+ result = ndb_mgm_start_backup(m_mgmsrv, 0, &backupId, &reply);
+ }
+ else if (sz == 1 ||
+ (sz == 3 &&
+ args[1] == "WAIT" &&
+ args[2] == "COMPLETED"))
+ {
+ ndbout_c("Waiting for completed, this may take several minutes");
+ result = ndb_mgm_start_backup(m_mgmsrv, 2, &backupId, &reply);
+ }
+ else if (sz == 3 &&
+ args[1] == "WAIT" &&
+ args[2] == "STARTED")
+ {
+ ndbout_c("Waiting for started, this may take several minutes");
+ result = ndb_mgm_start_backup(m_mgmsrv, 1, &backupId, &reply);
+ }
+ else
+ {
+ invalid_command(parameters);
+ return -1;
+ }
+
+ if (result != 0) {
+ ndbout << "Start of backup failed" << endl;
+ printError();
+#if 0
+ close(fd);
+#endif
+ return result;
+ }
+#if 0
+ ndbout_c("Waiting for completed, this may take several minutes");
+ char *tmp;
+ char buf[1024];
+ {
+ SocketInputStream in(fd);
+ int count = 0;
+ do {
+ tmp = in.gets(buf, 1024);
+ if(tmp)
+ {
+ ndbout << tmp;
+ unsigned int id;
+ if(sscanf(tmp, "%*[^:]: Backup %d ", &id) == 1 && id == backupId){
+ count++;
+ }
+ }
+ } while(count < 2);
+ }
+
+ SocketInputStream in(fd, 10);
+ do {
+ tmp = in.gets(buf, 1024);
+ if(tmp && tmp[0] != 0)
+ {
+ ndbout << tmp;
+ }
+ } while(tmp && tmp[0] != 0);
+
+ close(fd);
+#endif
+ return 0;
+}
+
+void
+CommandInterpreter::executeAbortBackup(char* parameters)
+{
+ int bid = -1;
+ struct ndb_mgm_reply reply;
+ if (emptyString(parameters))
+ goto executeAbortBackupError1;
+
+ {
+ strtok(parameters, " ");
+ char* id = strtok(NULL, "\0");
+ if(id == 0 || sscanf(id, "%d", &bid) != 1)
+ goto executeAbortBackupError1;
+ }
+ {
+ int result= ndb_mgm_abort_backup(m_mgmsrv, bid, &reply);
+ if (result != 0) {
+ ndbout << "Abort of backup " << bid << " failed" << endl;
+ printError();
+ } else {
+ ndbout << "Abort of backup " << bid << " ordered" << endl;
+ }
+ }
+ return;
+ executeAbortBackupError1:
+ ndbout << "Invalid arguments: expected <BackupId>" << endl;
+ return;
+}
+
+#ifdef HAVE_GLOBAL_REPLICATION
+/*****************************************************************************
+ * Global Replication
+ *
+ * For information about the different commands, see
+ * GrepReq::Request in file signaldata/grepImpl.cpp.
+ *
+ * Below are commands as of 2003-07-05 (may change!):
+ * START = 0, ///< Start Global Replication (all phases)
+ * START_METALOG = 1, ///< Start Global Replication (all phases)
+ * START_METASCAN = 2, ///< Start Global Replication (all phases)
+ * START_DATALOG = 3, ///< Start Global Replication (all phases)
+ * START_DATASCAN = 4, ///< Start Global Replication (all phases)
+ * START_REQUESTOR = 5, ///< Start Global Replication (all phases)
+ * ABORT = 6, ///< Immediate stop (removes subscription)
+ * SLOW_STOP = 7, ///< Stop after finishing applying current GCI epoch
+ * FAST_STOP = 8, ///< Stop after finishing applying all PS GCI epochs
+ * START_TRANSFER = 9, ///< Start SS-PS transfer
+ * STOP_TRANSFER = 10, ///< Stop SS-PS transfer
+ * START_APPLY = 11, ///< Start applying GCI epochs in SS
+ * STOP_APPLY = 12, ///< Stop applying GCI epochs in SS
+ * STATUS = 13, ///< Status
+ * START_SUBSCR = 14,
+ * REMOVE_BUFFERS = 15,
+ * DROP_TABLE = 16
+
+ *****************************************************************************/
+
+void
+CommandInterpreter::executeRep(char* parameters)
+{
+ if (emptyString(parameters)) {
+ ndbout << helpTextRep;
+ return;
+ }
+
+ char * line = my_strdup(parameters,MYF(MY_WME));
+ My_auto_ptr<char> ap1((char*)line);
+ char * firstToken = strtok(line, " ");
+
+ struct ndb_rep_reply reply;
+ unsigned int repId;
+
+
+ if (!strcasecmp(firstToken, "CONNECT")) {
+ char * host = strtok(NULL, "\0");
+ for (unsigned int i = 0; i < strlen(host); ++i) {
+ host[i] = tolower(host[i]);
+ }
+
+ if(host == NULL)
+ {
+ ndbout_c("host:port must be specified.");
+ return;
+ }
+
+ if(rep_connected) {
+ if(m_repserver != NULL) {
+ ndb_rep_disconnect(m_repserver);
+ rep_connected = false;
+ }
+ }
+
+ if(m_repserver == NULL)
+ m_repserver = ndb_rep_create_handle();
+ if(ndb_rep_connect(m_repserver, host) < 0)
+ ndbout_c("Failed to connect to %s", host);
+ else
+ rep_connected=true;
+ return;
+
+ if(!rep_connected) {
+ ndbout_c("Not connected to REP server");
+ }
+ }
+
+ /********
+ * START
+ ********/
+ if (!strcasecmp(firstToken, "START")) {
+
+ unsigned int req;
+ char *startType = strtok(NULL, "\0");
+
+ if (startType == NULL) {
+ req = GrepReq::START;
+ } else if (!strcasecmp(startType, "SUBSCRIPTION")) {
+ req = GrepReq::START_SUBSCR;
+ } else if (!strcasecmp(startType, "METALOG")) {
+ req = GrepReq::START_METALOG;
+ } else if (!strcasecmp(startType, "METASCAN")) {
+ req = GrepReq::START_METASCAN;
+ } else if (!strcasecmp(startType, "DATALOG")) {
+ req = GrepReq::START_DATALOG;
+ } else if (!strcasecmp(startType, "DATASCAN")) {
+ req = GrepReq::START_DATASCAN;
+ } else if (!strcasecmp(startType, "REQUESTOR")) {
+ req = GrepReq::START_REQUESTOR;
+ } else if (!strcasecmp(startType, "TRANSFER")) {
+ req = GrepReq::START_TRANSFER;
+ } else if (!strcasecmp(startType, "APPLY")) {
+ req = GrepReq::START_APPLY;
+ } else if (!strcasecmp(startType, "DELETE")) {
+ req = GrepReq::START_DELETE;
+ } else {
+ ndbout_c("Illegal argument to command 'REPLICATION START'");
+ return;
+ }
+
+ int result = ndb_rep_command(m_repserver, req, &repId, &reply);
+
+ if (result != 0) {
+ ndbout << "Start of Global Replication failed" << endl;
+ } else {
+ ndbout << "Start of Global Replication ordered" << endl;
+ }
+ return;
+ }
+
+ /********
+ * STOP
+ ********/
+ if (!strcasecmp(firstToken, "STOP")) {
+ unsigned int req;
+ char *startType = strtok(NULL, " ");
+ unsigned int epoch = 0;
+
+ if (startType == NULL) {
+ /**
+ * Stop immediately
+ */
+ req = GrepReq::STOP;
+ } else if (!strcasecmp(startType, "EPOCH")) {
+ char *strEpoch = strtok(NULL, "\0");
+ if(strEpoch == NULL) {
+ ndbout_c("Epoch expected!");
+ return;
+ }
+ req = GrepReq::STOP;
+ epoch=atoi(strEpoch);
+ } else if (!strcasecmp(startType, "SUBSCRIPTION")) {
+ req = GrepReq::STOP_SUBSCR;
+ } else if (!strcasecmp(startType, "METALOG")) {
+ req = GrepReq::STOP_METALOG;
+ } else if (!strcasecmp(startType, "METASCAN")) {
+ req = GrepReq::STOP_METASCAN;
+ } else if (!strcasecmp(startType, "DATALOG")) {
+ req = GrepReq::STOP_DATALOG;
+ } else if (!strcasecmp(startType, "DATASCAN")) {
+ req = GrepReq::STOP_DATASCAN;
+ } else if (!strcasecmp(startType, "REQUESTOR")) {
+ req = GrepReq::STOP_REQUESTOR;
+ } else if (!strcasecmp(startType, "TRANSFER")) {
+ req = GrepReq::STOP_TRANSFER;
+ } else if (!strcasecmp(startType, "APPLY")) {
+ req = GrepReq::STOP_APPLY;
+ } else if (!strcasecmp(startType, "DELETE")) {
+ req = GrepReq::STOP_DELETE;
+ } else {
+ ndbout_c("Illegal argument to command 'REPLICATION STOP'");
+ return;
+ }
+ int result = ndb_rep_command(m_repserver, req, &repId, &reply, epoch);
+
+ if (result != 0) {
+ ndbout << "Stop command failed" << endl;
+ } else {
+ ndbout << "Stop ordered" << endl;
+ }
+ return;
+ }
+
+ /*********
+ * STATUS
+ *********/
+ if (!strcasecmp(firstToken, "STATUS")) {
+ struct rep_state repstate;
+ int result =
+ ndb_rep_get_status(m_repserver, &repId, &reply, &repstate);
+
+ if (result != 0) {
+ ndbout << "Status request of Global Replication failed" << endl;
+ } else {
+ ndbout << "Status request of Global Replication ordered" << endl;
+ ndbout << "See printout at one of the DB nodes" << endl;
+ ndbout << "(Better status report is under development.)" << endl;
+ ndbout << " SubscriptionId " << repstate.subid
+ << " SubscriptionKey " << repstate.subkey << endl;
+ }
+ return;
+ }
+
+ /*********
+ * QUERY (see repapi.h for querable counters)
+ *********/
+ if (!strcasecmp(firstToken, "QUERY")) {
+ char *query = strtok(NULL, "\0");
+ int queryCounter=-1;
+ if(query != NULL) {
+ queryCounter = atoi(query);
+ }
+ struct rep_state repstate;
+ unsigned repId = 0;
+ int result = ndb_rep_query(m_repserver, (QueryCounter)queryCounter,
+ &repId, &reply, &repstate);
+
+ if (result != 0) {
+ ndbout << "Query repserver failed" << endl;
+ } else {
+ ndbout << "Query repserver sucessful" << endl;
+ ndbout_c("repstate : QueryCounter %d, f=%d l=%d"
+ " nodegroups %d" ,
+ repstate.queryCounter,
+ repstate.first[0], repstate.last[0],
+ repstate.no_of_nodegroups );
+ }
+ return;
+ }
+}
+#endif // HAVE_GLOBAL_REPLICATION
+
+template class Vector<char const*>;
diff --git a/storage/ndb/src/mgmclient/Makefile.am b/storage/ndb/src/mgmclient/Makefile.am
new file mode 100644
index 00000000000..c63e8d1bff8
--- /dev/null
+++ b/storage/ndb/src/mgmclient/Makefile.am
@@ -0,0 +1,58 @@
+
+noinst_LTLIBRARIES = libndbmgmclient.la
+ndbtools_PROGRAMS = ndb_mgm
+
+libndbmgmclient_la_SOURCES = CommandInterpreter.cpp
+libndbmgmclient_la_LIBADD = ../mgmapi/libmgmapi.la \
+ ../common/logger/liblogger.la \
+ ../common/portlib/libportlib.la \
+ ../common/util/libgeneral.la \
+ ../common/portlib/libportlib.la
+
+
+ndb_mgm_SOURCES = main.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
+
+INCLUDES += -I$(top_srcdir)/ndb/include/mgmapi \
+ -I$(top_srcdir)/ndb/src/common/mgmcommon
+
+LDADD_LOC = $(noinst_LTLIBRARIES) \
+ ../common/portlib/libportlib.la \
+ @readline_link@ \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/strings/libmystrings.a \
+ @TERMCAP_LIB@ @NDB_SCI_LIBS@
+
+ndb_mgm_LDFLAGS = @ndb_bin_am_ldflags@
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: ndb_mgm.dsp libndbmgmclient.dsp
+
+ndb_mgm.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(ndbtools_PROGRAMS)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_mgm_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+libndbmgmclient.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libndbmgmclient_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB
diff --git a/storage/ndb/src/mgmclient/main.cpp b/storage/ndb/src/mgmclient/main.cpp
new file mode 100644
index 00000000000..19c84f6ec8d
--- /dev/null
+++ b/storage/ndb/src/mgmclient/main.cpp
@@ -0,0 +1,169 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+// copied from mysql.cc to get readline
+extern "C" {
+#if defined( __WIN__) || defined(OS2)
+#include <conio.h>
+#elif !defined(__NETWARE__)
+#include <readline/readline.h>
+extern "C" int add_history(const char *command); /* From readline directory */
+#define HAVE_READLINE
+#endif
+}
+
+#include <NdbMain.h>
+#include <NdbHost.h>
+#include <BaseString.hpp>
+#include <NdbOut.hpp>
+#include <mgmapi.h>
+#include <ndb_version.h>
+
+#include "ndb_mgmclient.hpp"
+
+const char *progname = "ndb_mgm";
+
+
+static Ndb_mgmclient* com;
+
+extern "C"
+void
+handler(int sig){
+ switch(sig){
+ case SIGPIPE:
+ /**
+ * Will happen when connection to mgmsrv is broken
+ * Reset connected flag
+ */
+ com->disconnect();
+ break;
+ }
+}
+
+NDB_STD_OPTS_VARS;
+
+static const char default_prompt[]= "ndb_mgm> ";
+static unsigned _try_reconnect;
+static const char *prompt= default_prompt;
+static char *opt_execute_str= 0;
+
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_mgm"),
+ { "execute", 'e',
+ "execute command and exit",
+ (gptr*) &opt_execute_str, (gptr*) &opt_execute_str, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "try-reconnect", 't',
+ "Specify number of tries for connecting to ndb_mgmd (0 = infinite)",
+ (gptr*) &_try_reconnect, (gptr*) &_try_reconnect, 0,
+ GET_UINT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+static void short_usage_sub(void)
+{
+ printf("Usage: %s [OPTIONS] [hostname [port]]\n", my_progname);
+}
+static void usage()
+{
+ short_usage_sub();
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+static int
+read_and_execute(int _try_reconnect)
+{
+ static char *line_read = (char *)NULL;
+
+ /* If the buffer has already been allocated, return the memory
+ to the free pool. */
+ if (line_read)
+ {
+ free (line_read);
+ line_read = (char *)NULL;
+ }
+#ifdef HAVE_READLINE
+ /* Get a line from the user. */
+ line_read = readline (prompt);
+ /* If the line has any text in it, save it on the history. */
+ if (line_read && *line_read)
+ add_history (line_read);
+#else
+ static char linebuffer[254];
+ fputs(prompt, stdout);
+ linebuffer[sizeof(linebuffer)-1]=0;
+ line_read = fgets(linebuffer, sizeof(linebuffer)-1, stdin);
+ if (line_read == linebuffer) {
+ char *q=linebuffer;
+ while (*q > 31) q++;
+ *q=0;
+ line_read= strdup(linebuffer);
+ }
+#endif
+ return com->execute(line_read,_try_reconnect);
+}
+
+int main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *_host = 0;
+ int _port = 0;
+ const char *load_default_groups[]= { "mysql_cluster","ndb_mgm",0 };
+
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_mgm.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ exit(ho_error);
+
+ char buf[MAXHOSTNAMELEN+10];
+ if(argc == 1) {
+ BaseString::snprintf(buf, sizeof(buf), "%s", argv[0]);
+ opt_connect_str= buf;
+ } else if (argc >= 2) {
+ BaseString::snprintf(buf, sizeof(buf), "%s:%s", argv[0], argv[1]);
+ opt_connect_str= buf;
+ }
+
+ if (!isatty(0) || opt_execute_str)
+ {
+ prompt= 0;
+ }
+
+ signal(SIGPIPE, handler);
+ com = new Ndb_mgmclient(opt_connect_str,1);
+ int ret= 0;
+ if (!opt_execute_str)
+ {
+ ndbout << "-- NDB Cluster -- Management Client --" << endl;
+ while(read_and_execute(_try_reconnect));
+ }
+ else
+ {
+ com->execute(opt_execute_str,_try_reconnect, &ret);
+ }
+ delete com;
+
+ return ret;
+}
+
diff --git a/storage/ndb/src/mgmclient/ndb_mgmclient.h b/storage/ndb/src/mgmclient/ndb_mgmclient.h
new file mode 100644
index 00000000000..b62a33999a3
--- /dev/null
+++ b/storage/ndb/src/mgmclient/ndb_mgmclient.h
@@ -0,0 +1,33 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_mgmclient_h
+#define Ndb_mgmclient_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* Ndb_mgmclient_handle;
+Ndb_mgmclient_handle ndb_mgmclient_handle_create(const char *connect_string);
+int ndb_mgmclient_execute(Ndb_mgmclient_handle, int argc, char** argv);
+int ndb_mgmclient_handle_destroy(Ndb_mgmclient_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Ndb_mgmclient_h */
diff --git a/storage/ndb/src/mgmclient/ndb_mgmclient.hpp b/storage/ndb/src/mgmclient/ndb_mgmclient.hpp
new file mode 100644
index 00000000000..bffdf69f920
--- /dev/null
+++ b/storage/ndb/src/mgmclient/ndb_mgmclient.hpp
@@ -0,0 +1,33 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_mgmclient_hpp
+#define Ndb_mgmclient_hpp
+
+class CommandInterpreter;
+class Ndb_mgmclient
+{
+public:
+ Ndb_mgmclient(const char*,int verbose=0);
+ ~Ndb_mgmclient();
+ int execute(const char *_line, int _try_reconnect=-1, int *error= 0);
+ int execute(int argc, char** argv, int _try_reconnect=-1, int *error= 0);
+ int disconnect();
+private:
+ CommandInterpreter *m_cmd;
+};
+
+#endif // Ndb_mgmclient_hpp
diff --git a/storage/ndb/src/mgmclient/test_cpcd/Makefile b/storage/ndb/src/mgmclient/test_cpcd/Makefile
new file mode 100644
index 00000000000..4ced10cfc59
--- /dev/null
+++ b/storage/ndb/src/mgmclient/test_cpcd/Makefile
@@ -0,0 +1,17 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := test_cpcd
+BIN_TARGET_LIBS := general
+
+
+# Source files of non-templated classes (.cpp files)
+SOURCES = test_cpcd.cpp ../CpcClient.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/include/mgmapi) \
+ -I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon)
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/src/mgmclient/test_cpcd/test_cpcd.cpp b/storage/ndb/src/mgmclient/test_cpcd/test_cpcd.cpp
new file mode 100644
index 00000000000..32f0adbcf26
--- /dev/null
+++ b/storage/ndb/src/mgmclient/test_cpcd/test_cpcd.cpp
@@ -0,0 +1,157 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include "../CpcClient.hpp"
+#include <Vector.hpp>
+
+SimpleCpcClient g_client("localhost", 1234);
+Vector<SimpleCpcClient::Process> g_procs;
+
+void define();
+void start(SimpleCpcClient::Process & p);
+void stop(SimpleCpcClient::Process & p);
+void undefine(SimpleCpcClient::Process & p);
+void list();
+SimpleCpcClient::Process* find(int id);
+
+#define ABORT() {ndbout_c("ABORT"); while(true); abort();}
+
+int name = 0;
+
+int
+main(void){
+
+ g_client.connect();
+
+ srand(time(0));
+ for(int i = 0; i<1000; i++){
+ int sz = g_procs.size();
+ int test = rand() % 100;
+ if(sz == 0 || test < 10){
+ define();
+ continue;
+ }
+
+ list();
+
+ int proc = rand() % g_procs.size();
+ SimpleCpcClient::Process & p = g_procs[proc];
+ if(p.m_status == "running" && test > 50){
+ ndbout_c("undefine %d: %s (running)", p.m_id, p.m_name.c_str());
+ undefine(p);
+ g_procs.erase(proc);
+ continue;
+ }
+ if(p.m_status == "running" && test <= 50){
+ ndbout_c("stop %d: %s(running)", p.m_id, p.m_name.c_str());
+ stop(p);
+ continue;
+ }
+ if(p.m_status == "stopped" && test > 50){
+ ndbout_c("undefine %d: %s(stopped)", p.m_id, p.m_name.c_str());
+ undefine(p);
+ g_procs.erase(proc);
+ continue;
+ }
+ if(p.m_status == "stopped" && test <= 50){
+ ndbout_c("start %d %s(stopped)", p.m_id, p.m_name.c_str());
+ start(p);
+ continue;
+ }
+ ndbout_c("Unknown: %s", p.m_status.c_str());
+ }
+}
+
+void define(){
+ SimpleCpcClient::Process m_proc;
+ m_proc.m_id = -1;
+ m_proc.m_type = "temporary";
+ m_proc.m_owner = "atrt";
+ m_proc.m_group = "group";
+ //m_proc.m_cwd
+ //m_proc.m_env
+ //proc.m_proc.m_stdout = "log.out";
+ //proc.m_proc.m_stderr = "2>&1";
+ //proc.m_proc.m_runas = proc.m_host->m_user;
+ m_proc.m_ulimit = "c:unlimited";
+ if((rand() & 15) >= 0){
+ m_proc.m_name.assfmt("%d-%d-%s", getpid(), name++, "sleep");
+ m_proc.m_path.assign("/bin/sleep");
+ m_proc.m_args = "600";
+ } else {
+ m_proc.m_name.assfmt("%d-%d-%s", getpid(), name++, "test.sh");
+ m_proc.m_path.assign("/home/jonas/run/cpcd/test.sh");
+ m_proc.m_args = "600";
+ }
+ g_procs.push_back(m_proc);
+
+ Properties reply;
+ if(g_client.define_process(g_procs.back(), reply) != 0){
+ ndbout_c("define %s -> ERR", m_proc.m_name.c_str());
+ reply.print();
+ ABORT();
+ }
+ ndbout_c("define %s -> %d", m_proc.m_name.c_str(), m_proc.m_id);
+}
+
+void start(SimpleCpcClient::Process & p){
+ Properties reply;
+ if(g_client.start_process(p.m_id, reply) != 0){
+ reply.print();
+ ABORT();
+ }
+}
+
+void stop(SimpleCpcClient::Process & p){
+ Properties reply;
+ if(g_client.stop_process(p.m_id, reply) != 0){
+ reply.print();
+ ABORT();
+ }
+}
+
+void undefine(SimpleCpcClient::Process & p){
+ Properties reply;
+ if(g_client.undefine_process(p.m_id, reply) != 0){
+ reply.print();
+ ABORT();
+ }
+}
+
+void list(){
+ Properties reply;
+ Vector<SimpleCpcClient::Process> procs;
+ if(g_client.list_processes(procs, reply) != 0){
+ reply.print();
+ ABORT();
+ }
+
+ for(Uint32 i = 0; i<procs.size(); i++){
+ SimpleCpcClient::Process * p = find(procs[i].m_id);
+ if(p != 0){
+ p->m_status = procs[i].m_status;
+ }
+ }
+}
+SimpleCpcClient::Process* find(int id){
+ for(Uint32 i = 0; i<g_procs.size(); i++){
+ if(g_procs[i].m_id == id)
+ return &g_procs[i];
+ }
+ return 0;
+}
diff --git a/storage/ndb/src/mgmsrv/Config.cpp b/storage/ndb/src/mgmsrv/Config.cpp
new file mode 100644
index 00000000000..5ff9cbe04ad
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/Config.cpp
@@ -0,0 +1,268 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Config.hpp"
+#include <ctype.h>
+#include <string.h>
+#include "MgmtErrorReporter.hpp"
+#include <Properties.hpp>
+
+//*****************************************************************************
+// Ctor / Dtor
+//*****************************************************************************
+
+Config::Config() {
+ m_oldConfig = 0;
+ m_configValues = 0;
+}
+
+Config::~Config() {
+ if(m_configValues != 0){
+ free(m_configValues);
+ }
+
+ if(m_oldConfig != 0)
+ delete m_oldConfig;
+}
+
+/*****************************************************************************/
+
+void
+Config::printAllNameValuePairs(NdbOut &out,
+ const Properties *prop,
+ const char* s) const {
+ Properties::Iterator it(prop);
+ const Properties * section = m_info.getInfo(s);
+ for (const char* n = it.first(); n != NULL; n = it.next()) {
+ Uint32 int_value;
+ const char* str_value;
+ Uint64 int_64;
+
+ if(!section->contains(n))
+ continue;
+ if (m_info.getStatus(section, n) == ConfigInfo::CI_INTERNAL)
+ continue;
+ if (m_info.getStatus(section, n) == ConfigInfo::CI_DEPRICATED)
+ continue;
+ if (m_info.getStatus(section, n) == ConfigInfo::CI_NOTIMPLEMENTED)
+ continue;
+
+ out << n << ": ";
+
+ switch (m_info.getType(section, n)) {
+ case ConfigInfo::CI_INT:
+ MGM_REQUIRE(prop->get(n, &int_value));
+ out << int_value;
+ break;
+
+ case ConfigInfo::CI_INT64:
+ MGM_REQUIRE(prop->get(n, &int_64));
+ out << int_64;
+ break;
+
+ case ConfigInfo::CI_BOOL:
+ MGM_REQUIRE(prop->get(n, &int_value));
+ if (int_value) {
+ out << "Y";
+ } else {
+ out << "N";
+ }
+ break;
+ case ConfigInfo::CI_STRING:
+ MGM_REQUIRE(prop->get(n, &str_value));
+ out << str_value;
+ break;
+ case ConfigInfo::CI_SECTION:
+ out << "SECTION";
+ break;
+ }
+ out << endl;
+ }
+}
+
+/*****************************************************************************/
+
+void Config::printConfigFile(NdbOut &out) const {
+#if 0
+ Uint32 noOfNodes, noOfConnections, noOfComputers;
+ MGM_REQUIRE(get("NoOfNodes", &noOfNodes));
+ MGM_REQUIRE(get("NoOfConnections", &noOfConnections));
+ MGM_REQUIRE(get("NoOfComputers", &noOfComputers));
+
+ out <<
+ "######################################################################" <<
+ endl <<
+ "#" << endl <<
+ "# NDB Cluster System configuration" << endl <<
+ "#" << endl <<
+ "######################################################################" <<
+ endl <<
+ "# No of nodes (DB, API or MGM): " << noOfNodes << endl <<
+ "# No of connections: " << noOfConnections << endl <<
+ "######################################################################" <<
+ endl;
+
+ /**************************
+ * Print COMPUTER configs *
+ **************************/
+ const char * name;
+ Properties::Iterator it(this);
+ for(name = it.first(); name != NULL; name = it.next()){
+ if(strncasecmp("Computer_", name, 9) == 0){
+
+ const Properties *prop;
+ out << endl << "[COMPUTER]" << endl;
+ MGM_REQUIRE(get(name, &prop));
+ printAllNameValuePairs(out, prop, "COMPUTER");
+
+ out << endl <<
+ "###################################################################" <<
+ endl;
+
+ } else if(strncasecmp("Node_", name, 5) == 0){
+ /**********************
+ * Print NODE configs *
+ **********************/
+ const Properties *prop;
+ const char *s;
+
+ MGM_REQUIRE(get(name, &prop));
+ MGM_REQUIRE(prop->get("Type", &s));
+ out << endl << "[" << s << "]" << endl;
+ printAllNameValuePairs(out, prop, s);
+
+ out << endl <<
+ "###################################################################" <<
+ endl;
+ } else if(strncasecmp("Connection_", name, 11) == 0){
+ /****************************
+ * Print CONNECTION configs *
+ ****************************/
+ const Properties *prop;
+ const char *s;
+
+ MGM_REQUIRE(get(name, &prop));
+ MGM_REQUIRE(prop->get("Type", &s));
+ out << endl << "[" << s << "]" << endl;
+ printAllNameValuePairs(out, prop, s);
+
+ out << endl <<
+ "###################################################################" <<
+ endl;
+ } else if(strncasecmp("SYSTEM", name, strlen("SYSTEM")) == 0) {
+ /************************
+ * Print SYSTEM configs *
+ ************************/
+ const Properties *prop;
+
+ MGM_REQUIRE(get(name, &prop));
+ out << endl << "[SYSTEM]" << endl;
+ printAllNameValuePairs(out, prop, "SYSTEM");
+
+ out << endl <<
+ "###################################################################" <<
+ endl;
+ }
+ }
+#endif
+}
+
+Uint32
+Config::getGenerationNumber() const {
+#if 0
+ Uint32 ret;
+ const Properties *prop = NULL;
+
+ get("SYSTEM", &prop);
+
+ if(prop != NULL)
+ if(prop->get("ConfigGenerationNumber", &ret))
+ return ret;
+
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+int
+Config::setGenerationNumber(Uint32 gen) {
+#if 0
+ Properties *prop = NULL;
+
+ getCopy("SYSTEM", &prop);
+
+ if(prop != NULL) {
+ MGM_REQUIRE(prop->put("ConfigGenerationNumber", gen, true));
+ MGM_REQUIRE(put("SYSTEM", prop, true));
+ return 0;
+ }
+ return -1;
+#else
+ return -1;
+#endif
+}
+
+bool
+Config::change(const BaseString &section,
+ const BaseString &param,
+ const BaseString &value) {
+#if 0
+ const char *name;
+ Properties::Iterator it(this);
+
+ for(name = it.first(); name != NULL; name = it.next()) {
+ Properties *prop = NULL;
+ if(strcasecmp(section.c_str(), name) == 0) {
+ getCopy(name, &prop);
+ if(prop == NULL) /* doesn't exist */
+ return false;
+ if(value == "") {
+ prop->remove(param.c_str());
+ put(section.c_str(), prop, true);
+ } else {
+ PropertiesType t;
+ if(!prop->getTypeOf(param.c_str(), &t)) /* doesn't exist */
+ return false;
+ switch(t) {
+ case PropertiesType_Uint32:
+ long val;
+ char *ep;
+ errno = 0;
+ val = strtol(value.c_str(), &ep, 0);
+ if(value.length() == 0 || *ep != '\0') /* not a number */
+ return false;
+ if(errno == ERANGE)
+ return false;
+ prop->put(param.c_str(), (unsigned int)val, true);
+ put(section.c_str(), prop, true);
+ break;
+ case PropertiesType_char:
+ prop->put(param.c_str(), value.c_str(), true);
+ put(section.c_str(), prop, true);
+ break;
+ default:
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ return true;
+#else
+ return false;
+#endif
+}
diff --git a/storage/ndb/src/mgmsrv/Config.hpp b/storage/ndb/src/mgmsrv/Config.hpp
new file mode 100644
index 00000000000..b5e1e17b027
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/Config.hpp
@@ -0,0 +1,92 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 Config_H
+#define Config_H
+
+#include <LogLevel.hpp>
+
+#include <kernel_types.h>
+
+#include <NdbOut.hpp>
+#include <ndb_limits.h>
+#include <Properties.hpp>
+#include <ConfigInfo.hpp>
+
+class ConfigInfo;
+
+/**
+ * @class Config
+ * @brief Cluster Configuration (corresponds to initial configuration file)
+ *
+ * Contains all cluster configuration parameters.
+ *
+ * The information includes all configurable parameters for a NDB cluster:
+ * - DB, API and MGM nodes with all their properties,
+ * - Connections between nodes and computers the nodes will execute on.
+ *
+ * The following categories (sections) of configuration parameters exists:
+ * - COMPUTER, DB, MGM, API, TCP, SCI, SHM, OSE
+ *
+ */
+
+class Config {
+public:
+ /**
+ * Constructor which loads the object with an Properties object
+ */
+ Config();
+ virtual ~Config();
+
+ /**
+ * Prints the configuration in configfile format
+ */
+ void printConfigFile(NdbOut &out = ndbout) const;
+ void printConfigFile(OutputStream &out) const {
+ NdbOut ndb(out);
+ printConfigFile(ndb);
+ }
+
+ Uint32 getGenerationNumber() const;
+ int setGenerationNumber(Uint32);
+
+ /** Change configuration
+ */
+ bool change(const BaseString &section,
+ const BaseString &param,
+ const BaseString &value);
+
+
+ /**
+ * Info
+ */
+ const ConfigInfo * getConfigInfo() const { return &m_info;}
+private:
+ ConfigInfo m_info;
+
+ void printAllNameValuePairs(NdbOut &out,
+ const Properties *prop,
+ const char* section) const;
+
+ /**
+ * Information about parameters (min, max values etc)
+ */
+public:
+ Properties * m_oldConfig;
+ struct ndb_mgm_configuration * m_configValues;
+};
+
+#endif // Config_H
diff --git a/storage/ndb/src/mgmsrv/ConfigInfo.cpp b/storage/ndb/src/mgmsrv/ConfigInfo.cpp
new file mode 100644
index 00000000000..67bf09fab10
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/ConfigInfo.cpp
@@ -0,0 +1,3876 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_opt_defaults.h>
+
+#include <NdbTCP.h>
+#include "ConfigInfo.hpp"
+#include <mgmapi_config_parameters.h>
+#include <ndb_limits.h>
+#include "InitConfigFileParser.hpp"
+#include <m_string.h>
+
+extern my_bool opt_ndb_shm;
+
+#define MAX_LINE_LENGTH 255
+#define KEY_INTERNAL 0
+#define MAX_INT_RNIL 0xfffffeff
+
+#define _STR_VALUE(x) #x
+#define STR_VALUE(x) _STR_VALUE(x)
+
+/****************************************************************************
+ * Section names
+ ****************************************************************************/
+
+#define DB_TOKEN_PRINT "ndbd(DB)"
+#define MGM_TOKEN_PRINT "ndb_mgmd(MGM)"
+#define API_TOKEN_PRINT "mysqld(API)"
+
+#define DB_TOKEN "DB"
+#define MGM_TOKEN "MGM"
+#define API_TOKEN "API"
+
+const ConfigInfo::AliasPair
+ConfigInfo::m_sectionNameAliases[]={
+ {API_TOKEN, "MYSQLD"},
+ {DB_TOKEN, "NDBD"},
+ {MGM_TOKEN, "NDB_MGMD"},
+ {0, 0}
+};
+
+const char*
+ConfigInfo::m_sectionNames[]={
+ "SYSTEM",
+ "EXTERNAL SYSTEM",
+ "COMPUTER",
+
+ DB_TOKEN,
+ MGM_TOKEN,
+ API_TOKEN,
+ "REP",
+ "EXTERNAL REP",
+
+ "TCP",
+ "SCI",
+ "SHM",
+ "OSE"
+};
+const int ConfigInfo::m_noOfSectionNames =
+sizeof(m_sectionNames)/sizeof(char*);
+
+
+/****************************************************************************
+ * Section Rules declarations
+ ****************************************************************************/
+static bool transformComputer(InitConfigFileParser::Context & ctx, const char *);
+static bool transformSystem(InitConfigFileParser::Context & ctx, const char *);
+static bool transformExternalSystem(InitConfigFileParser::Context & ctx, const char *);
+static bool transformNode(InitConfigFileParser::Context & ctx, const char *);
+static bool transformExtNode(InitConfigFileParser::Context & ctx, const char *);
+static bool checkConnectionSupport(InitConfigFileParser::Context & ctx, const char *);
+static bool transformConnection(InitConfigFileParser::Context & ctx, const char *);
+static bool applyDefaultValues(InitConfigFileParser::Context & ctx, const char *);
+static bool checkMandatory(InitConfigFileParser::Context & ctx, const char *);
+static bool fixPortNumber(InitConfigFileParser::Context & ctx, const char *);
+static bool fixShmKey(InitConfigFileParser::Context & ctx, const char *);
+static bool checkDbConstraints(InitConfigFileParser::Context & ctx, const char *);
+static bool checkConnectionConstraints(InitConfigFileParser::Context &, const char *);
+static bool checkTCPConstraints(InitConfigFileParser::Context &, const char *);
+static bool fixNodeHostname(InitConfigFileParser::Context & ctx, const char * data);
+static bool fixHostname(InitConfigFileParser::Context & ctx, const char * data);
+static bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data);
+static bool fixExtConnection(InitConfigFileParser::Context & ctx, const char * data);
+static bool fixDepricated(InitConfigFileParser::Context & ctx, const char *);
+static bool saveInConfigValues(InitConfigFileParser::Context & ctx, const char *);
+static bool fixFileSystemPath(InitConfigFileParser::Context & ctx, const char * data);
+static bool fixBackupDataDir(InitConfigFileParser::Context & ctx, const char * data);
+static bool fixShmUniqueId(InitConfigFileParser::Context & ctx, const char * data);
+
+const ConfigInfo::SectionRule
+ConfigInfo::m_SectionRules[] = {
+ { "SYSTEM", transformSystem, 0 },
+ { "EXTERNAL SYSTEM", transformExternalSystem, 0 },
+ { "COMPUTER", transformComputer, 0 },
+
+ { DB_TOKEN, transformNode, 0 },
+ { API_TOKEN, transformNode, 0 },
+ { MGM_TOKEN, transformNode, 0 },
+ { "REP", transformNode, 0 },
+ { "EXTERNAL REP", transformExtNode, 0 },
+
+ { MGM_TOKEN, fixShmUniqueId, 0 },
+
+ { "TCP", checkConnectionSupport, 0 },
+ { "SHM", checkConnectionSupport, 0 },
+ { "SCI", checkConnectionSupport, 0 },
+ { "OSE", checkConnectionSupport, 0 },
+
+ { "TCP", transformConnection, 0 },
+ { "SHM", transformConnection, 0 },
+ { "SCI", transformConnection, 0 },
+ { "OSE", transformConnection, 0 },
+
+ { DB_TOKEN, fixNodeHostname, 0 },
+ { API_TOKEN, fixNodeHostname, 0 },
+ { MGM_TOKEN, fixNodeHostname, 0 },
+ { "REP", fixNodeHostname, 0 },
+ //{ "EXTERNAL REP", fixNodeHostname, 0 },
+
+ { "TCP", fixNodeId, "NodeId1" },
+ { "TCP", fixNodeId, "NodeId2" },
+ { "SHM", fixNodeId, "NodeId1" },
+ { "SHM", fixNodeId, "NodeId2" },
+ { "SCI", fixNodeId, "NodeId1" },
+ { "SCI", fixNodeId, "NodeId2" },
+ { "OSE", fixNodeId, "NodeId1" },
+ { "OSE", fixNodeId, "NodeId2" },
+
+ { "TCP", fixHostname, "HostName1" },
+ { "TCP", fixHostname, "HostName2" },
+ { "SHM", fixHostname, "HostName1" },
+ { "SHM", fixHostname, "HostName2" },
+ { "SCI", fixHostname, "HostName1" },
+ { "SCI", fixHostname, "HostName2" },
+ { "SHM", fixHostname, "HostName1" },
+ { "SHM", fixHostname, "HostName2" },
+ { "OSE", fixHostname, "HostName1" },
+ { "OSE", fixHostname, "HostName2" },
+
+ { "TCP", fixPortNumber, 0 }, // has to come after fixHostName
+ { "SHM", fixPortNumber, 0 }, // has to come after fixHostName
+ { "SCI", fixPortNumber, 0 }, // has to come after fixHostName
+ { "SHM", fixShmKey, 0 },
+
+ /**
+ * fixExtConnection must be after fixNodeId
+ */
+ { "TCP", fixExtConnection, 0 },
+ { "SHM", fixExtConnection, 0 },
+ { "SCI", fixExtConnection, 0 },
+ { "OSE", fixExtConnection, 0 },
+
+ { "*", applyDefaultValues, "user" },
+ { "*", fixDepricated, 0 },
+ { "*", applyDefaultValues, "system" },
+
+ { DB_TOKEN, fixFileSystemPath, 0 },
+ { DB_TOKEN, fixBackupDataDir, 0 },
+
+ { DB_TOKEN, checkDbConstraints, 0 },
+
+ /**
+ * checkConnectionConstraints must be after fixExtConnection
+ */
+ { "TCP", checkConnectionConstraints, 0 },
+ { "SHM", checkConnectionConstraints, 0 },
+ { "SCI", checkConnectionConstraints, 0 },
+ { "OSE", checkConnectionConstraints, 0 },
+
+ { "TCP", checkTCPConstraints, "HostName1" },
+ { "TCP", checkTCPConstraints, "HostName2" },
+ { "SCI", checkTCPConstraints, "HostName1" },
+ { "SCI", checkTCPConstraints, "HostName2" },
+ { "SHM", checkTCPConstraints, "HostName1" },
+ { "SHM", checkTCPConstraints, "HostName2" },
+
+ { "*", checkMandatory, 0 },
+
+ { DB_TOKEN, saveInConfigValues, 0 },
+ { API_TOKEN, saveInConfigValues, 0 },
+ { MGM_TOKEN, saveInConfigValues, 0 },
+ { "REP", saveInConfigValues, 0 },
+
+ { "TCP", saveInConfigValues, 0 },
+ { "SHM", saveInConfigValues, 0 },
+ { "SCI", saveInConfigValues, 0 },
+ { "OSE", saveInConfigValues, 0 }
+};
+const int ConfigInfo::m_NoOfRules = sizeof(m_SectionRules)/sizeof(SectionRule);
+
+/****************************************************************************
+ * Config Rules declarations
+ ****************************************************************************/
+static bool sanity_checks(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data);
+static bool add_node_connections(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data);
+static bool set_connection_priorities(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data);
+static bool add_server_ports(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data);
+static bool check_node_vs_replicas(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data);
+
+const ConfigInfo::ConfigRule
+ConfigInfo::m_ConfigRules[] = {
+ { sanity_checks, 0 },
+ { add_node_connections, 0 },
+ { set_connection_priorities, 0 },
+ { add_server_ports, 0 },
+ { check_node_vs_replicas, 0 },
+ { 0, 0 }
+};
+
+struct DepricationTransform {
+ const char * m_section;
+ const char * m_oldName;
+ const char * m_newName;
+ double m_add;
+ double m_mul;
+};
+
+static
+const DepricationTransform f_deprication[] = {
+ { DB_TOKEN, "Discless", "Diskless", 0, 1 },
+ { 0, 0, 0, 0, 0}
+};
+
+/**
+ * The default constructors create objects with suitable values for the
+ * configuration parameters.
+ *
+ * Some are however given the value MANDATORY which means that the value
+ * must be specified in the configuration file.
+ *
+ * Min and max values are also given for some parameters.
+ * - Attr1: Name in file (initial config file)
+ * - Attr2: Name in prop (properties object)
+ * - Attr3: Name of Section (in init config file)
+ * - Attr4: Updateable
+ * - Attr5: Type of parameter (INT or BOOL)
+ * - Attr6: Default Value (number only)
+ * - Attr7: Min value
+ * - Attr8: Max value
+ *
+ * Parameter constraints are coded in file Config.cpp.
+ *
+ * *******************************************************************
+ * Parameters used under development should be marked "NOTIMPLEMENTED"
+ * *******************************************************************
+ */
+
+const ConfigInfo::ParamInfo ConfigInfo::m_ParamInfo[] = {
+
+ /****************************************************************************
+ * COMPUTER
+ ***************************************************************************/
+ {
+ KEY_INTERNAL,
+ "COMPUTER",
+ "COMPUTER",
+ "Computer section",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_SECTION,
+ 0,
+ 0, 0 },
+
+ {
+ KEY_INTERNAL,
+ "Id",
+ "COMPUTER",
+ "Name of computer",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ KEY_INTERNAL,
+ "HostName",
+ "COMPUTER",
+ "Hostname of computer (e.g. mysql.com)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ KEY_INTERNAL,
+ "ByteOrder",
+ "COMPUTER",
+ 0,
+ ConfigInfo::CI_DEPRICATED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0,
+ 0 },
+
+ /****************************************************************************
+ * SYSTEM
+ ***************************************************************************/
+ {
+ CFG_SECTION_SYSTEM,
+ "SYSTEM",
+ "SYSTEM",
+ "System section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)CFG_SECTION_SYSTEM,
+ 0, 0 },
+
+ {
+ CFG_SYS_NAME,
+ "Name",
+ "SYSTEM",
+ "Name of system (NDB Cluster)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ CFG_SYS_REPLICATION_ROLE,
+ "ReplicationRole",
+ "SYSTEM",
+ "Role in Global Replication (None, Primary, or Standby)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_SYS_PRIMARY_MGM_NODE,
+ "PrimaryMGMNode",
+ "SYSTEM",
+ "Node id of Primary "MGM_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SYS_CONFIG_GENERATION,
+ "ConfigGenerationNumber",
+ "SYSTEM",
+ "Configuration generation number",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ /***************************************************************************
+ * DB
+ ***************************************************************************/
+ {
+ CFG_SECTION_NODE,
+ DB_TOKEN,
+ DB_TOKEN,
+ "Node section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)NODE_TYPE_DB,
+ 0, 0
+ },
+
+ {
+ CFG_NODE_HOST,
+ "HostName",
+ DB_TOKEN,
+ "Name of computer for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_SYSTEM,
+ "System",
+ DB_TOKEN,
+ "Name of system for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_ID,
+ "Id",
+ DB_TOKEN,
+ "Number identifying the database node ("DB_TOKEN_PRINT")",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "1",
+ STR_VALUE(MAX_NODES) },
+
+ {
+ KEY_INTERNAL,
+ "ServerPort",
+ DB_TOKEN,
+ "Port used to setup transporter",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ UNDEFINED,
+ "1",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_REPLICAS,
+ "NoOfReplicas",
+ DB_TOKEN,
+ "Number of copies of all data in the database (1-4)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "1",
+ "4" },
+
+ {
+ CFG_DB_NO_ATTRIBUTES,
+ "MaxNoOfAttributes",
+ DB_TOKEN,
+ "Total number of attributes stored in database. I.e. sum over all tables",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1000",
+ "32",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_TABLES,
+ "MaxNoOfTables",
+ DB_TOKEN,
+ "Total number of tables stored in the database",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "128",
+ "8",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_ORDERED_INDEXES,
+ "MaxNoOfOrderedIndexes",
+ DB_TOKEN,
+ "Total number of ordered indexes that can be defined in the system",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "128",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_UNIQUE_HASH_INDEXES,
+ "MaxNoOfUniqueHashIndexes",
+ DB_TOKEN,
+ "Total number of unique hash indexes that can be defined in the system",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "64",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_INDEXES,
+ "MaxNoOfIndexes",
+ DB_TOKEN,
+ "Total number of indexes that can be defined in the system",
+ ConfigInfo::CI_DEPRICATED,
+ false,
+ ConfigInfo::CI_INT,
+ "128",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_INDEX_OPS,
+ "MaxNoOfConcurrentIndexOperations",
+ DB_TOKEN,
+ "Total number of index operations that can execute simultaneously on one "DB_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "8K",
+ "0",
+ STR_VALUE(MAX_INT_RNIL)
+ },
+
+ {
+ CFG_DB_NO_TRIGGERS,
+ "MaxNoOfTriggers",
+ DB_TOKEN,
+ "Total number of triggers that can be defined in the system",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "768",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_TRIGGER_OPS,
+ "MaxNoOfFiredTriggers",
+ DB_TOKEN,
+ "Total number of triggers that can fire simultaneously in one "DB_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "4000",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ KEY_INTERNAL,
+ "ExecuteOnComputer",
+ DB_TOKEN,
+ "String referencing an earlier defined COMPUTER",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_DB_NO_SAVE_MSGS,
+ "MaxNoOfSavedMessages",
+ DB_TOKEN,
+ "Max number of error messages in error log and max number of trace files",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "25",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_MEMLOCK,
+ "LockPagesInMainMemory",
+ DB_TOKEN,
+ "If set to yes, then NDB Cluster data will not be swapped out to disk",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true" },
+
+ {
+ CFG_DB_WATCHDOG_INTERVAL,
+ "TimeBetweenWatchDogCheck",
+ DB_TOKEN,
+ "Time between execution checks inside a database node",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "6000",
+ "70",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_STOP_ON_ERROR,
+ "StopOnError",
+ DB_TOKEN,
+ "If set to N, "DB_TOKEN_PRINT" automatically restarts/recovers in case of node failure",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_BOOL,
+ "true",
+ "false",
+ "true" },
+
+ {
+ CFG_DB_STOP_ON_ERROR_INSERT,
+ "RestartOnErrorInsert",
+ DB_TOKEN,
+ "See src/kernel/vm/Emulator.hpp NdbRestartType for details",
+ ConfigInfo::CI_INTERNAL,
+ true,
+ ConfigInfo::CI_INT,
+ "2",
+ "0",
+ "4" },
+
+ {
+ CFG_DB_NO_OPS,
+ "MaxNoOfConcurrentOperations",
+ DB_TOKEN,
+ "Max number of operation records in transaction coordinator",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "32k",
+ "32",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_LOCAL_OPS,
+ "MaxNoOfLocalOperations",
+ DB_TOKEN,
+ "Max number of operation records defined in the local storage node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ UNDEFINED,
+ "32",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_LOCAL_SCANS,
+ "MaxNoOfLocalScans",
+ DB_TOKEN,
+ "Max number of fragment scans in parallel in the local storage node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ UNDEFINED,
+ "32",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_BATCH_SIZE,
+ "BatchSizePerLocalScan",
+ DB_TOKEN,
+ "Used to calculate the number of lock records for scan with hold lock",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ STR_VALUE(DEF_BATCH_SIZE),
+ "1",
+ STR_VALUE(MAX_PARALLEL_OP_PER_SCAN) },
+
+ {
+ CFG_DB_NO_TRANSACTIONS,
+ "MaxNoOfConcurrentTransactions",
+ DB_TOKEN,
+ "Max number of transaction executing concurrently on the "DB_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "4096",
+ "32",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_NO_SCANS,
+ "MaxNoOfConcurrentScans",
+ DB_TOKEN,
+ "Max number of scans executing concurrently on the "DB_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "256",
+ "2",
+ "500" },
+
+ {
+ CFG_DB_TRANS_BUFFER_MEM,
+ "TransactionBufferMemory",
+ DB_TOKEN,
+ "Dynamic buffer space (in bytes) for key and attribute data allocated for each "DB_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1M",
+ "1K",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_INDEX_MEM,
+ "IndexMemory",
+ DB_TOKEN,
+ "Number bytes on each "DB_TOKEN_PRINT" node allocated for storing indexes",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT64,
+ "18M",
+ "1M",
+ "1024G" },
+
+ {
+ CFG_DB_DATA_MEM,
+ "DataMemory",
+ DB_TOKEN,
+ "Number bytes on each "DB_TOKEN_PRINT" node allocated for storing data",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT64,
+ "80M",
+ "1M",
+ "1024G" },
+
+ {
+ CFG_DB_UNDO_INDEX_BUFFER,
+ "UndoIndexBuffer",
+ DB_TOKEN,
+ "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing UNDO logs for index part",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "2M",
+ "1M",
+ STR_VALUE(MAX_INT_RNIL)},
+
+ {
+ CFG_DB_UNDO_DATA_BUFFER,
+ "UndoDataBuffer",
+ DB_TOKEN,
+ "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing UNDO logs for data part",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "16M",
+ "1M",
+ STR_VALUE(MAX_INT_RNIL)},
+
+ {
+ CFG_DB_REDO_BUFFER,
+ "RedoBuffer",
+ DB_TOKEN,
+ "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing REDO logs",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "8M",
+ "1M",
+ STR_VALUE(MAX_INT_RNIL)},
+
+ {
+ CFG_DB_LONG_SIGNAL_BUFFER,
+ "LongMessageBuffer",
+ DB_TOKEN,
+ "Number bytes on each "DB_TOKEN_PRINT" node allocated for internal long messages",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1M",
+ "512k",
+ STR_VALUE(MAX_INT_RNIL)},
+
+ {
+ CFG_DB_START_PARTIAL_TIMEOUT,
+ "StartPartialTimeout",
+ DB_TOKEN,
+ "Time to wait before trying to start wo/ all nodes. 0=Wait forever",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "30000",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_START_PARTITION_TIMEOUT,
+ "StartPartitionedTimeout",
+ DB_TOKEN,
+ "Time to wait before trying to start partitioned. 0=Wait forever",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "60000",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_START_FAILURE_TIMEOUT,
+ "StartFailureTimeout",
+ DB_TOKEN,
+ "Time to wait before terminating. 0=Wait forever",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_HEARTBEAT_INTERVAL,
+ "HeartbeatIntervalDbDb",
+ DB_TOKEN,
+ "Time between "DB_TOKEN_PRINT"-"DB_TOKEN_PRINT" heartbeats. "DB_TOKEN_PRINT" considered dead after 3 missed HBs",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "1500",
+ "10",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_API_HEARTBEAT_INTERVAL,
+ "HeartbeatIntervalDbApi",
+ DB_TOKEN,
+ "Time between "API_TOKEN_PRINT"-"DB_TOKEN_PRINT" heartbeats. "API_TOKEN_PRINT" connection closed after 3 missed HBs",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "1500",
+ "100",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_LCP_INTERVAL,
+ "TimeBetweenLocalCheckpoints",
+ DB_TOKEN,
+ "Time between taking snapshots of the database (expressed in 2log of bytes)",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "20",
+ "0",
+ "31" },
+
+ {
+ CFG_DB_GCP_INTERVAL,
+ "TimeBetweenGlobalCheckpoints",
+ DB_TOKEN,
+ "Time between doing group commit of transactions to disk",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "2000",
+ "10",
+ "32000" },
+
+ {
+ CFG_DB_NO_REDOLOG_FILES,
+ "NoOfFragmentLogFiles",
+ DB_TOKEN,
+ "No of 16 Mbyte Redo log files in each of 4 file sets belonging to "DB_TOKEN_PRINT" node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "8",
+ "1",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_MAX_OPEN_FILES,
+ "MaxNoOfOpenFiles",
+ DB_TOKEN,
+ "Max number of files open per "DB_TOKEN_PRINT" node.(One thread is created per file)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "40",
+ "20",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_TRANSACTION_CHECK_INTERVAL,
+ "TimeBetweenInactiveTransactionAbortCheck",
+ DB_TOKEN,
+ "Time between inactive transaction checks",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "1000",
+ "1000",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_TRANSACTION_INACTIVE_TIMEOUT,
+ "TransactionInactiveTimeout",
+ DB_TOKEN,
+ "Time application can wait before executing another transaction part (ms).\n"
+ "This is the time the transaction coordinator waits for the application\n"
+ "to execute or send another part (query, statement) of the transaction.\n"
+ "If the application takes too long time, the transaction gets aborted.\n"
+ "Timeout set to 0 means that we don't timeout at all on application wait.",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ STR_VALUE(MAX_INT_RNIL),
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_TRANSACTION_DEADLOCK_TIMEOUT,
+ "TransactionDeadlockDetectionTimeout",
+ DB_TOKEN,
+ "Time transaction can be executing in a DB node (ms).\n"
+ "This is the time the transaction coordinator waits for each database node\n"
+ "of the transaction to execute a request. If the database node takes too\n"
+ "long time, the transaction gets aborted.",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "1200",
+ "50",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ KEY_INTERNAL,
+ "NoOfDiskPagesToDiskDuringRestartTUP",
+ DB_TOKEN,
+ "?",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "40",
+ "1",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ KEY_INTERNAL,
+ "NoOfDiskPagesToDiskAfterRestartTUP",
+ DB_TOKEN,
+ "?",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "40",
+ "1",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ KEY_INTERNAL,
+ "NoOfDiskPagesToDiskDuringRestartACC",
+ DB_TOKEN,
+ "?",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "20",
+ "1",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ KEY_INTERNAL,
+ "NoOfDiskPagesToDiskAfterRestartACC",
+ DB_TOKEN,
+ "?",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "20",
+ "1",
+ STR_VALUE(MAX_INT_RNIL) },
+
+
+ {
+ CFG_DB_DISCLESS,
+ "Diskless",
+ DB_TOKEN,
+ "Run wo/ disk",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true"},
+
+ {
+ KEY_INTERNAL,
+ "Discless",
+ DB_TOKEN,
+ "Diskless",
+ ConfigInfo::CI_DEPRICATED,
+ true,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true"},
+
+
+
+ {
+ CFG_DB_ARBIT_TIMEOUT,
+ "ArbitrationTimeout",
+ DB_TOKEN,
+ "Max time (milliseconds) database partion waits for arbitration signal",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "3000",
+ "10",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_NODE_DATADIR,
+ "DataDir",
+ DB_TOKEN,
+ "Data directory for this node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MYSQLCLUSTERDIR,
+ 0, 0 },
+
+ {
+ CFG_DB_FILESYSTEM_PATH,
+ "FileSystemPath",
+ DB_TOKEN,
+ "Path to directory where the "DB_TOKEN_PRINT" node stores its data (directory must exist)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_LOGLEVEL_STARTUP,
+ "LogLevelStartup",
+ DB_TOKEN,
+ "Node startup info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_SHUTDOWN,
+ "LogLevelShutdown",
+ DB_TOKEN,
+ "Node shutdown info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_STATISTICS,
+ "LogLevelStatistic",
+ DB_TOKEN,
+ "Transaction, operation, transporter info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_CHECKPOINT,
+ "LogLevelCheckpoint",
+ DB_TOKEN,
+ "Local and Global checkpoint info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_NODERESTART,
+ "LogLevelNodeRestart",
+ DB_TOKEN,
+ "Node restart, node failure info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_CONNECTION,
+ "LogLevelConnection",
+ DB_TOKEN,
+ "Node connect/disconnect info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_CONGESTION,
+ "LogLevelCongestion",
+ DB_TOKEN,
+ "Congestion info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_ERROR,
+ "LogLevelError",
+ DB_TOKEN,
+ "Transporter, heartbeat errors printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ {
+ CFG_LOGLEVEL_INFO,
+ "LogLevelInfo",
+ DB_TOKEN,
+ "Heartbeat and log info printed on stdout",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "15" },
+
+ /**
+ * Backup
+ */
+ {
+ CFG_DB_PARALLEL_BACKUPS,
+ "ParallelBackups",
+ DB_TOKEN,
+ "Maximum number of parallel backups",
+ ConfigInfo::CI_NOTIMPLEMENTED,
+ false,
+ ConfigInfo::CI_INT,
+ "1",
+ "1",
+ "1" },
+
+ {
+ CFG_DB_BACKUP_DATADIR,
+ "BackupDataDir",
+ DB_TOKEN,
+ "Path to where to store backups",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_DB_BACKUP_MEM,
+ "BackupMemory",
+ DB_TOKEN,
+ "Total memory allocated for backups per node (in bytes)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "4M", // sum of BackupDataBufferSize and BackupLogBufferSize
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_BACKUP_DATA_BUFFER_MEM,
+ "BackupDataBufferSize",
+ DB_TOKEN,
+ "Default size of databuffer for a backup (in bytes)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "2M", // remember to change BackupMemory
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_BACKUP_LOG_BUFFER_MEM,
+ "BackupLogBufferSize",
+ DB_TOKEN,
+ "Default size of logbuffer for a backup (in bytes)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "2M", // remember to change BackupMemory
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_DB_BACKUP_WRITE_SIZE,
+ "BackupWriteSize",
+ DB_TOKEN,
+ "Default size of filesystem writes made by backup (in bytes)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "32K",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ /***************************************************************************
+ * REP
+ ***************************************************************************/
+ {
+ CFG_SECTION_NODE,
+ "REP",
+ "REP",
+ "Node section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)NODE_TYPE_REP,
+ 0, 0
+ },
+
+ {
+ CFG_NODE_HOST,
+ "HostName",
+ "REP",
+ "Name of computer for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_SYSTEM,
+ "System",
+ "REP",
+ "Name of system for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_ID,
+ "Id",
+ "REP",
+ "Number identifying replication node (REP)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "1",
+ STR_VALUE(MAX_NODES) },
+
+ {
+ KEY_INTERNAL,
+ "ExecuteOnComputer",
+ "REP",
+ "String referencing an earlier defined COMPUTER",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ CFG_REP_HEARTBEAT_INTERVAL,
+ "HeartbeatIntervalRepRep",
+ "REP",
+ "Time between REP-REP heartbeats. Connection closed after 3 missed HBs",
+ ConfigInfo::CI_USED,
+ true,
+ ConfigInfo::CI_INT,
+ "3000",
+ "100",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ /***************************************************************************
+ * API
+ ***************************************************************************/
+ {
+ CFG_SECTION_NODE,
+ API_TOKEN,
+ API_TOKEN,
+ "Node section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)NODE_TYPE_API,
+ 0, 0
+ },
+
+ {
+ CFG_NODE_HOST,
+ "HostName",
+ API_TOKEN,
+ "Name of computer for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_SYSTEM,
+ "System",
+ API_TOKEN,
+ "Name of system for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_ID,
+ "Id",
+ API_TOKEN,
+ "Number identifying application node ("API_TOKEN_PRINT")",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "1",
+ STR_VALUE(MAX_NODES) },
+
+ {
+ KEY_INTERNAL,
+ "ExecuteOnComputer",
+ API_TOKEN,
+ "String referencing an earlier defined COMPUTER",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_ARBIT_RANK,
+ "ArbitrationRank",
+ API_TOKEN,
+ "If 0, then "API_TOKEN_PRINT" is not arbitrator. Kernel selects arbitrators in order 1, 2",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ "2" },
+
+ {
+ CFG_NODE_ARBIT_DELAY,
+ "ArbitrationDelay",
+ API_TOKEN,
+ "When asked to arbitrate, arbitrator waits this long before voting (msec)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_MAX_SCAN_BATCH_SIZE,
+ "MaxScanBatchSize",
+ "API",
+ "The maximum collective batch size for one scan",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ STR_VALUE(MAX_SCAN_BATCH_SIZE),
+ "32k",
+ "16M" },
+
+ {
+ CFG_BATCH_BYTE_SIZE,
+ "BatchByteSize",
+ "API",
+ "The default batch size in bytes",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ STR_VALUE(SCAN_BATCH_SIZE),
+ "1k",
+ "1M" },
+
+ {
+ CFG_BATCH_SIZE,
+ "BatchSize",
+ "API",
+ "The default batch size in number of records",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ STR_VALUE(DEF_BATCH_SIZE),
+ "1",
+ STR_VALUE(MAX_PARALLEL_OP_PER_SCAN) },
+
+ /****************************************************************************
+ * MGM
+ ***************************************************************************/
+ {
+ CFG_SECTION_NODE,
+ MGM_TOKEN,
+ MGM_TOKEN,
+ "Node section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)NODE_TYPE_MGM,
+ 0, 0
+ },
+
+ {
+ CFG_NODE_HOST,
+ "HostName",
+ MGM_TOKEN,
+ "Name of computer for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_DATADIR,
+ "DataDir",
+ MGM_TOKEN,
+ "Data directory for this node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MYSQLCLUSTERDIR,
+ 0, 0 },
+
+ {
+ CFG_NODE_SYSTEM,
+ "System",
+ MGM_TOKEN,
+ "Name of system for this node",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_NODE_ID,
+ "Id",
+ MGM_TOKEN,
+ "Number identifying the management server node ("MGM_TOKEN_PRINT")",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "1",
+ STR_VALUE(MAX_NODES) },
+
+ {
+ CFG_LOG_DESTINATION,
+ "LogDestination",
+ MGM_TOKEN,
+ "String describing where logmessages are sent",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ 0,
+ 0, 0 },
+
+ {
+ KEY_INTERNAL,
+ "ExecuteOnComputer",
+ MGM_TOKEN,
+ "String referencing an earlier defined COMPUTER",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ 0,
+ 0, 0 },
+
+ {
+ KEY_INTERNAL,
+ "MaxNoOfSavedEvents",
+ MGM_TOKEN,
+ "",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "100",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_MGM_PORT,
+ "PortNumber",
+ MGM_TOKEN,
+ "Port number to give commands to/fetch configurations from management server",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ NDB_PORT,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ KEY_INTERNAL,
+ "PortNumberStats",
+ MGM_TOKEN,
+ "Port number used to get statistical information from a management server",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ UNDEFINED,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_NODE_ARBIT_RANK,
+ "ArbitrationRank",
+ MGM_TOKEN,
+ "If 0, then "MGM_TOKEN_PRINT" is not arbitrator. Kernel selects arbitrators in order 1, 2",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1",
+ "0",
+ "2" },
+
+ {
+ CFG_NODE_ARBIT_DELAY,
+ "ArbitrationDelay",
+ MGM_TOKEN,
+ "",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ /****************************************************************************
+ * TCP
+ ***************************************************************************/
+ {
+ CFG_SECTION_CONNECTION,
+ "TCP",
+ "TCP",
+ "Connection section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)CONNECTION_TYPE_TCP,
+ 0, 0
+ },
+
+ {
+ CFG_CONNECTION_HOSTNAME_1,
+ "HostName1",
+ "TCP",
+ "Name/IP of computer on one side of the connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_HOSTNAME_2,
+ "HostName2",
+ "TCP",
+ "Name/IP of computer on one side of the connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_1,
+ "NodeId1",
+ "TCP",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_2,
+ "NodeId2",
+ "TCP",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_GROUP,
+ "Group",
+ "TCP",
+ "",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "55",
+ "0", "200" },
+
+ {
+ CFG_CONNECTION_SEND_SIGNAL_ID,
+ "SendSignalId",
+ "TCP",
+ "Sends id in each signal. Used in trace files.",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "true",
+ "false",
+ "true" },
+
+
+ {
+ CFG_CONNECTION_CHECKSUM,
+ "Checksum",
+ "TCP",
+ "If checksum is enabled, all signals between nodes are checked for errors",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true" },
+
+ {
+ CFG_CONNECTION_SERVER_PORT,
+ "PortNumber",
+ "TCP",
+ "Port used for this transporter",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_TCP_SEND_BUFFER_SIZE,
+ "SendBufferMemory",
+ "TCP",
+ "Bytes of buffer for signals sent from this node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "256K",
+ "16K",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_TCP_RECEIVE_BUFFER_SIZE,
+ "ReceiveBufferMemory",
+ "TCP",
+ "Bytes of buffer for signals received by this node",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "64K",
+ "16K",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_TCP_PROXY,
+ "Proxy",
+ "TCP",
+ "",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_1_SYSTEM,
+ "NodeId1_System",
+ "TCP",
+ "System for node 1 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_2_SYSTEM,
+ "NodeId2_System",
+ "TCP",
+ "System for node 2 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+
+ /****************************************************************************
+ * SHM
+ ***************************************************************************/
+ {
+ CFG_SECTION_CONNECTION,
+ "SHM",
+ "SHM",
+ "Connection section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)CONNECTION_TYPE_SHM,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_HOSTNAME_1,
+ "HostName1",
+ "SHM",
+ "Name/IP of computer on one side of the connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_HOSTNAME_2,
+ "HostName2",
+ "SHM",
+ "Name/IP of computer on one side of the connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_SERVER_PORT,
+ "PortNumber",
+ "SHM",
+ "Port used for this transporter",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SHM_SIGNUM,
+ "Signum",
+ "SHM",
+ "Signum to be used for signalling",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ UNDEFINED,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_NODE_1,
+ "NodeId1",
+ "SHM",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_2,
+ "NodeId2",
+ "SHM",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_GROUP,
+ "Group",
+ "SHM",
+ "",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "35",
+ "0", "200" },
+
+ {
+ CFG_CONNECTION_SEND_SIGNAL_ID,
+ "SendSignalId",
+ "SHM",
+ "Sends id in each signal. Used in trace files.",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true" },
+
+
+ {
+ CFG_CONNECTION_CHECKSUM,
+ "Checksum",
+ "SHM",
+ "If checksum is enabled, all signals between nodes are checked for errors",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "true",
+ "false",
+ "true" },
+
+ {
+ CFG_SHM_KEY,
+ "ShmKey",
+ "SHM",
+ "A shared memory key",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SHM_BUFFER_MEM,
+ "ShmSize",
+ "SHM",
+ "Size of shared memory segment",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1M",
+ "4K",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_NODE_1_SYSTEM,
+ "NodeId1_System",
+ "SHM",
+ "System for node 1 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_2_SYSTEM,
+ "NodeId2_System",
+ "SHM",
+ "System for node 2 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ /****************************************************************************
+ * SCI
+ ***************************************************************************/
+ {
+ CFG_SECTION_CONNECTION,
+ "SCI",
+ "SCI",
+ "Connection section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)CONNECTION_TYPE_SCI,
+ 0, 0
+ },
+
+ {
+ CFG_CONNECTION_NODE_1,
+ "NodeId1",
+ "SCI",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_NODE_2,
+ "NodeId2",
+ "SCI",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_GROUP,
+ "Group",
+ "SCI",
+ "",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "15",
+ "0", "200" },
+
+ {
+ CFG_CONNECTION_HOSTNAME_1,
+ "HostName1",
+ "SCI",
+ "Name/IP of computer on one side of the connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_HOSTNAME_2,
+ "HostName2",
+ "SCI",
+ "Name/IP of computer on one side of the connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_SERVER_PORT,
+ "PortNumber",
+ "SCI",
+ "Port used for this transporter",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SCI_HOST1_ID_0,
+ "Host1SciId0",
+ "SCI",
+ "SCI-node id for adapter 0 on Host1 (a computer can have two adapters)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SCI_HOST1_ID_1,
+ "Host1SciId1",
+ "SCI",
+ "SCI-node id for adapter 1 on Host1 (a computer can have two adapters)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SCI_HOST2_ID_0,
+ "Host2SciId0",
+ "SCI",
+ "SCI-node id for adapter 0 on Host2 (a computer can have two adapters)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_SCI_HOST2_ID_1,
+ "Host2SciId1",
+ "SCI",
+ "SCI-node id for adapter 1 on Host2 (a computer can have two adapters)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "0",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_SEND_SIGNAL_ID,
+ "SendSignalId",
+ "SCI",
+ "Sends id in each signal. Used in trace files.",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "true",
+ "false",
+ "true" },
+
+ {
+ CFG_CONNECTION_CHECKSUM,
+ "Checksum",
+ "SCI",
+ "If checksum is enabled, all signals between nodes are checked for errors",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true" },
+
+ {
+ CFG_SCI_SEND_LIMIT,
+ "SendLimit",
+ "SCI",
+ "Transporter send buffer contents are sent when this no of bytes is buffered",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "8K",
+ "128",
+ "32K" },
+
+ {
+ CFG_SCI_BUFFER_MEM,
+ "SharedBufferSize",
+ "SCI",
+ "Size of shared memory segment",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1M",
+ "64K",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_NODE_1_SYSTEM,
+ "NodeId1_System",
+ "SCI",
+ "System for node 1 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_2_SYSTEM,
+ "NodeId2_System",
+ "SCI",
+ "System for node 2 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ /****************************************************************************
+ * OSE
+ ***************************************************************************/
+ {
+ CFG_SECTION_CONNECTION,
+ "OSE",
+ "OSE",
+ "Connection section",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_SECTION,
+ (const char *)CONNECTION_TYPE_OSE,
+ 0, 0
+ },
+
+ {
+ CFG_CONNECTION_HOSTNAME_1,
+ "HostName1",
+ "OSE",
+ "Name of computer on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_HOSTNAME_2,
+ "HostName2",
+ "OSE",
+ "Name of computer on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_1,
+ "NodeId1",
+ "OSE",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ MANDATORY,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_NODE_2,
+ "NodeId2",
+ "OSE",
+ "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ UNDEFINED,
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_SEND_SIGNAL_ID,
+ "SendSignalId",
+ "OSE",
+ "Sends id in each signal. Used in trace files.",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "true",
+ "false",
+ "true" },
+
+ {
+ CFG_CONNECTION_CHECKSUM,
+ "Checksum",
+ "OSE",
+ "If checksum is enabled, all signals between nodes are checked for errors",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_BOOL,
+ "false",
+ "false",
+ "true" },
+
+ {
+ CFG_OSE_PRIO_A_SIZE,
+ "PrioASignalSize",
+ "OSE",
+ "Size of priority A signals (in bytes)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1000",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_OSE_PRIO_B_SIZE,
+ "PrioBSignalSize",
+ "OSE",
+ "Size of priority B signals (in bytes)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "1000",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_OSE_RECEIVE_ARRAY_SIZE,
+ "ReceiveArraySize",
+ "OSE",
+ "Number of OSE signals checked for correct ordering (in no of OSE signals)",
+ ConfigInfo::CI_USED,
+ false,
+ ConfigInfo::CI_INT,
+ "10",
+ "0",
+ STR_VALUE(MAX_INT_RNIL) },
+
+ {
+ CFG_CONNECTION_NODE_1_SYSTEM,
+ "NodeId1_System",
+ "OSE",
+ "System for node 1 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+
+ {
+ CFG_CONNECTION_NODE_2_SYSTEM,
+ "NodeId2_System",
+ "OSE",
+ "System for node 2 in connection",
+ ConfigInfo::CI_INTERNAL,
+ false,
+ ConfigInfo::CI_STRING,
+ UNDEFINED,
+ 0, 0 },
+};
+
+const int ConfigInfo::m_NoOfParams = sizeof(m_ParamInfo) / sizeof(ParamInfo);
+
+
+/****************************************************************************
+ * Ctor
+ ****************************************************************************/
+static void require(bool v) { if(!v) abort();}
+
+ConfigInfo::ConfigInfo()
+ : m_info(true), m_systemDefaults(true)
+{
+ int i;
+ Properties *section;
+ const Properties *oldpinfo;
+
+ for (i=0; i<m_NoOfParams; i++) {
+ const ParamInfo & param = m_ParamInfo[i];
+ Uint64 default_uint64;
+ bool default_bool;
+
+ // Create new section if it did not exist
+ if (!m_info.getCopy(param._section, &section)) {
+ Properties newsection(true);
+ m_info.put(param._section, &newsection);
+ }
+
+ // Get copy of section
+ m_info.getCopy(param._section, &section);
+
+ // Create pinfo (parameter info) entry
+ Properties pinfo(true);
+ pinfo.put("Id", param._paramId);
+ pinfo.put("Fname", param._fname);
+ pinfo.put("Description", param._description);
+ pinfo.put("Updateable", param._updateable);
+ pinfo.put("Type", param._type);
+ pinfo.put("Status", param._status);
+
+ if(param._default == MANDATORY){
+ pinfo.put("Mandatory", (Uint32)1);
+ }
+
+ switch (param._type) {
+ case CI_BOOL:
+ {
+ bool tmp_bool;
+ require(InitConfigFileParser::convertStringToBool(param._min, tmp_bool));
+ pinfo.put64("Min", tmp_bool);
+ require(InitConfigFileParser::convertStringToBool(param._max, tmp_bool));
+ pinfo.put64("Max", tmp_bool);
+ break;
+ }
+ case CI_INT:
+ case CI_INT64:
+ {
+ Uint64 tmp_uint64;
+ require(InitConfigFileParser::convertStringToUint64(param._min, tmp_uint64));
+ pinfo.put64("Min", tmp_uint64);
+ require(InitConfigFileParser::convertStringToUint64(param._max, tmp_uint64));
+ pinfo.put64("Max", tmp_uint64);
+ break;
+ }
+ case CI_SECTION:
+ pinfo.put("SectionType", (Uint32)UintPtr(param._default));
+ break;
+ case CI_STRING:
+ break;
+ }
+
+ // Check that pinfo is really new
+ if (section->get(param._fname, &oldpinfo)) {
+ ndbout << "Error: Parameter " << param._fname
+ << " defined twice in section " << param._section
+ << "." << endl;
+ exit(-1);
+ }
+
+ // Add new pinfo to section
+ section->put(param._fname, &pinfo);
+
+ // Replace section with modified section
+ m_info.put(param._section, section, true);
+
+ if(param._type != ConfigInfo::CI_SECTION){
+ Properties * p;
+ if(!m_systemDefaults.getCopy(param._section, &p)){
+ p = new Properties(true);
+ }
+ if(param._default != UNDEFINED &&
+ param._default != MANDATORY){
+ switch (param._type)
+ {
+ case CI_SECTION:
+ break;
+ case CI_STRING:
+ require(p->put(param._fname, param._default));
+ break;
+ case CI_BOOL:
+ {
+ bool tmp_bool;
+ require(InitConfigFileParser::convertStringToBool(param._default, default_bool));
+ require(p->put(param._fname, default_bool));
+ break;
+ }
+ case CI_INT:
+ case CI_INT64:
+ {
+ Uint64 tmp_uint64;
+ require(InitConfigFileParser::convertStringToUint64(param._default, default_uint64));
+ require(p->put(param._fname, default_uint64));
+ break;
+ }
+ }
+ }
+ require(m_systemDefaults.put(param._section, p, true));
+ delete p;
+ }
+ }
+
+ for (i=0; i<m_NoOfParams; i++) {
+ if(m_ParamInfo[i]._section == NULL){
+ ndbout << "Check that each entry has a section failed." << endl;
+ ndbout << "Parameter \"" << m_ParamInfo[i]._fname << endl;
+ ndbout << "Edit file " << __FILE__ << "." << endl;
+ exit(-1);
+ }
+
+ if(m_ParamInfo[i]._type == ConfigInfo::CI_SECTION)
+ continue;
+
+ const Properties * p = getInfo(m_ParamInfo[i]._section);
+ if (!p || !p->contains(m_ParamInfo[i]._fname)) {
+ ndbout << "Check that each pname has an fname failed." << endl;
+ ndbout << "Parameter \"" << m_ParamInfo[i]._fname
+ << "\" does not exist in section \""
+ << m_ParamInfo[i]._section << "\"." << endl;
+ ndbout << "Edit file " << __FILE__ << "." << endl;
+ exit(-1);
+ }
+ }
+}
+
+/****************************************************************************
+ * Getters
+ ****************************************************************************/
+inline void warning(const char * src, const char * arg){
+ ndbout << "Illegal call to ConfigInfo::" << src << "() - " << arg << endl;
+ abort();
+}
+
+const Properties *
+ConfigInfo::getInfo(const char * section) const {
+ const Properties * p;
+ if(!m_info.get(section, &p)){
+ return 0;
+ // warning("getInfo", section);
+ }
+ return p;
+}
+
+const Properties *
+ConfigInfo::getDefaults(const char * section) const {
+ const Properties * p;
+ if(!m_systemDefaults.get(section, &p)){
+ return 0;
+ //warning("getDefaults", section);
+ }
+ return p;
+}
+
+static
+Uint64
+getInfoInt(const Properties * section,
+ const char* fname, const char * type){
+ Uint32 val32;
+ const Properties * p;
+ if (section->get(fname, &p) && p->get(type, &val32)) {
+ return val32;
+ }
+
+ Uint64 val64;
+ if(p && p->get(type, &val64)){
+ return val64;
+ }
+
+ section->print();
+ if(section->get(fname, &p)){
+ p->print();
+ }
+
+ warning(type, fname);
+ return 0;
+}
+
+static
+const char *
+getInfoString(const Properties * section,
+ const char* fname, const char * type){
+ const char* val;
+ const Properties * p;
+ if (section->get(fname, &p) && p->get(type, &val)) {
+ return val;
+ }
+ warning(type, fname);
+ return val;
+}
+
+Uint64
+ConfigInfo::getMax(const Properties * section, const char* fname) const {
+ return getInfoInt(section, fname, "Max");
+}
+
+Uint64
+ConfigInfo::getMin(const Properties * section, const char* fname) const {
+ return getInfoInt(section, fname, "Min");
+}
+
+Uint64
+ConfigInfo::getDefault(const Properties * section, const char* fname) const {
+ return getInfoInt(section, fname, "Default");
+}
+
+const char*
+ConfigInfo::getDescription(const Properties * section,
+ const char* fname) const {
+ return getInfoString(section, fname, "Description");
+}
+
+bool
+ConfigInfo::isSection(const char * section) const {
+ for (int i = 0; i<m_noOfSectionNames; i++) {
+ if(!strcasecmp(section, m_sectionNames[i])) return true;
+ }
+ return false;
+}
+
+const char*
+ConfigInfo::getAlias(const char * section) const {
+ for (int i = 0; m_sectionNameAliases[i].name != 0; i++)
+ if(!strcasecmp(section, m_sectionNameAliases[i].alias))
+ return m_sectionNameAliases[i].name;
+ return 0;
+}
+
+bool
+ConfigInfo::verify(const Properties * section, const char* fname,
+ Uint64 value) const {
+ Uint64 min, max;
+
+ min = getInfoInt(section, fname, "Min");
+ max = getInfoInt(section, fname, "Max");
+ if(min > max){
+ warning("verify", fname);
+ }
+ if (value >= min && value <= max)
+ return true;
+ else
+ return false;
+}
+
+ConfigInfo::Type
+ConfigInfo::getType(const Properties * section, const char* fname) const {
+ return (ConfigInfo::Type) getInfoInt(section, fname, "Type");
+}
+
+ConfigInfo::Status
+ConfigInfo::getStatus(const Properties * section, const char* fname) const {
+ return (ConfigInfo::Status) getInfoInt(section, fname, "Status");
+}
+
+/****************************************************************************
+ * Printers
+ ****************************************************************************/
+
+void ConfigInfo::print() const {
+ Properties::Iterator it(&m_info);
+ for (const char* n = it.first(); n != NULL; n = it.next()) {
+ print(n);
+ }
+}
+
+void ConfigInfo::print(const char* section) const {
+ ndbout << "****** " << section << " ******" << endl << endl;
+ const Properties * sec = getInfo(section);
+ Properties::Iterator it(sec);
+ for (const char* n = it.first(); n != NULL; n = it.next()) {
+ // Skip entries with different F- and P-names
+ if (getStatus(sec, n) == ConfigInfo::CI_INTERNAL) continue;
+ if (getStatus(sec, n) == ConfigInfo::CI_DEPRICATED) continue;
+ if (getStatus(sec, n) == ConfigInfo::CI_NOTIMPLEMENTED) continue;
+ print(sec, n);
+ }
+}
+
+void ConfigInfo::print(const Properties * section,
+ const char* parameter) const {
+ ndbout << parameter;
+ // ndbout << getDescription(section, parameter) << endl;
+ switch (getType(section, parameter)) {
+ case ConfigInfo::CI_BOOL:
+ ndbout << " (Boolean value)" << endl;
+ ndbout << getDescription(section, parameter) << endl;
+ if (getDefault(section, parameter) == false) {
+ ndbout << "Default: N (Legal values: Y, N)" << endl;
+ } else if (getDefault(section, parameter) == true) {
+ ndbout << "Default: Y (Legal values: Y, N)" << endl;
+ } else if (getDefault(section, parameter) == (UintPtr)MANDATORY) {
+ ndbout << "MANDATORY (Legal values: Y, N)" << endl;
+ } else {
+ ndbout << "UNKNOWN" << endl;
+ }
+ ndbout << endl;
+ break;
+
+ case ConfigInfo::CI_INT:
+ case ConfigInfo::CI_INT64:
+ ndbout << " (Non-negative Integer)" << endl;
+ ndbout << getDescription(section, parameter) << endl;
+ if (getDefault(section, parameter) == (UintPtr)MANDATORY) {
+ ndbout << "MANDATORY (";
+ } else if (getDefault(section, parameter) == (UintPtr)UNDEFINED) {
+ ndbout << "UNDEFINED (";
+ } else {
+ ndbout << "Default: " << getDefault(section, parameter) << " (";
+ }
+ ndbout << "Min: " << getMin(section, parameter) << ", ";
+ ndbout << "Max: " << getMax(section, parameter) << ")" << endl;
+ ndbout << endl;
+ break;
+
+ case ConfigInfo::CI_STRING:
+ ndbout << " (String)" << endl;
+ ndbout << getDescription(section, parameter) << endl;
+ if (getDefault(section, parameter) == (UintPtr)MANDATORY) {
+ ndbout << "MANDATORY" << endl;
+ } else {
+ ndbout << "No default value" << endl;
+ }
+ ndbout << endl;
+ break;
+ case ConfigInfo::CI_SECTION:
+ break;
+ }
+}
+
+/****************************************************************************
+ * Section Rules
+ ****************************************************************************/
+
+/**
+ * Node rule: Add "Type" and update "NoOfNodes"
+ */
+bool
+transformNode(InitConfigFileParser::Context & ctx, const char * data){
+
+ Uint32 id;
+ if(!ctx.m_currentSection->get("Id", &id)){
+ Uint32 nextNodeId= 1;
+ ctx.m_userProperties.get("NextNodeId", &nextNodeId);
+ id= nextNodeId;
+ while (ctx.m_userProperties.get("AllocatedNodeId_", id, &id))
+ id++;
+ ctx.m_userProperties.put("NextNodeId", id+1, true);
+ ctx.m_currentSection->put("Id", id);
+#if 0
+ ctx.reportError("Mandatory parameter Id missing from section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+#endif
+ } else if(ctx.m_userProperties.get("AllocatedNodeId_", id, &id)) {
+ ctx.reportError("Duplicate Id in section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ ctx.m_userProperties.put("AllocatedNodeId_", id, id);
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "Node_%d", id);
+
+ ctx.m_currentSection->put("Type", ctx.fname);
+
+ Uint32 nodes = 0;
+ ctx.m_userProperties.get("NoOfNodes", &nodes);
+ ctx.m_userProperties.put("NoOfNodes", ++nodes, true);
+
+ /**
+ * Update count (per type)
+ */
+ nodes = 0;
+ ctx.m_userProperties.get(ctx.fname, &nodes);
+ ctx.m_userProperties.put(ctx.fname, ++nodes, true);
+
+ return true;
+}
+
+static bool checkLocalhostHostnameMix(InitConfigFileParser::Context & ctx)
+{
+ DBUG_ENTER("checkLocalhostHostnameMix");
+ const char * hostname= 0;
+ ctx.m_currentSection->get("HostName", &hostname);
+ if (hostname == 0 || hostname[0] == 0)
+ DBUG_RETURN(true);
+
+ Uint32 localhost_used= 0;
+ if(!strcmp(hostname, "localhost") || !strcmp(hostname, "127.0.0.1")){
+ localhost_used= 1;
+ ctx.m_userProperties.put("$computer-localhost-used", localhost_used);
+ if(!ctx.m_userProperties.get("$computer-localhost", &hostname))
+ DBUG_RETURN(true);
+ } else {
+ ctx.m_userProperties.get("$computer-localhost-used", &localhost_used);
+ ctx.m_userProperties.put("$computer-localhost", hostname);
+ }
+
+ if (localhost_used) {
+ ctx.reportError("Mixing of localhost with other hostname(%s) is illegal",
+ hostname);
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
+bool
+fixNodeHostname(InitConfigFileParser::Context & ctx, const char * data){
+
+ const char * hostname;
+ if (ctx.m_currentSection->get("HostName", &hostname))
+ return checkLocalhostHostnameMix(ctx);
+
+ const char * compId;
+ if(!ctx.m_currentSection->get("ExecuteOnComputer", &compId)){
+ const char * type;
+ if(ctx.m_currentSection->get("Type", &type) && strcmp(type,DB_TOKEN) == 0)
+ require(ctx.m_currentSection->put("HostName", "localhost"));
+ else
+ require(ctx.m_currentSection->put("HostName", ""));
+ return checkLocalhostHostnameMix(ctx);
+ }
+
+ const Properties * computer;
+ char tmp[255];
+ BaseString::snprintf(tmp, sizeof(tmp), "Computer_%s", compId);
+ if(!ctx.m_config->get(tmp, &computer)){
+ ctx.reportError("Computer \"%s\" not declared"
+ "- [%s] starting at line: %d",
+ compId, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ if(!computer->get("HostName", &hostname)){
+ ctx.reportError("HostName missing in [COMPUTER] (Id: %d) "
+ " - [%s] starting at line: %d",
+ compId, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ require(ctx.m_currentSection->put("HostName", hostname));
+ return checkLocalhostHostnameMix(ctx);
+}
+
+bool
+fixFileSystemPath(InitConfigFileParser::Context & ctx, const char * data){
+ DBUG_ENTER("fixFileSystemPath");
+
+ const char * path;
+ if (ctx.m_currentSection->get("FileSystemPath", &path))
+ DBUG_RETURN(true);
+
+ if (ctx.m_currentSection->get("DataDir", &path)) {
+ require(ctx.m_currentSection->put("FileSystemPath", path));
+ DBUG_RETURN(true);
+ }
+
+ require(false);
+ DBUG_RETURN(false);
+}
+
+bool
+fixBackupDataDir(InitConfigFileParser::Context & ctx, const char * data){
+
+ const char * path;
+ if (ctx.m_currentSection->get("BackupDataDir", &path))
+ return true;
+
+ if (ctx.m_currentSection->get("FileSystemPath", &path)) {
+ require(ctx.m_currentSection->put("BackupDataDir", path));
+ return true;
+ }
+
+ require(false);
+ return false;
+}
+
+bool
+transformExtNode(InitConfigFileParser::Context & ctx, const char * data){
+
+ Uint32 id;
+ const char * systemName;
+
+ if(!ctx.m_currentSection->get("Id", &id)){
+ ctx.reportError("Mandatory parameter 'Id' missing from section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ if(!ctx.m_currentSection->get("System", &systemName)){
+ ctx.reportError("Mandatory parameter 'System' missing from section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ ctx.m_currentSection->put("Type", ctx.fname);
+
+ Uint32 nodes = 0;
+ ctx.m_userProperties.get("ExtNoOfNodes", &nodes);
+ require(ctx.m_userProperties.put("ExtNoOfNodes",++nodes, true));
+
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "EXTERNAL SYSTEM_%s:Node_%d",
+ systemName, id);
+
+ return true;
+}
+
+/**
+ * Connection rule: Check support of connection
+ */
+bool
+checkConnectionSupport(InitConfigFileParser::Context & ctx, const char * data)
+{
+ int error= 0;
+ if (strcasecmp("TCP",ctx.fname) == 0)
+ {
+ // always enabled
+ }
+ else if (strcasecmp("SHM",ctx.fname) == 0)
+ {
+#ifndef NDB_SHM_TRANSPORTER
+ error= 1;
+#endif
+ }
+ else if (strcasecmp("SCI",ctx.fname) == 0)
+ {
+#ifndef NDB_SCI_TRANSPORTER
+ error= 1;
+#endif
+ }
+ else if (strcasecmp("OSE",ctx.fname) == 0)
+ {
+#ifndef NDB_OSE_TRANSPORTER
+ error= 1;
+#endif
+ }
+ if (error)
+ {
+ ctx.reportError("Binary not compiled with this connection support, "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Connection rule: Update "NoOfConnections"
+ */
+bool
+transformConnection(InitConfigFileParser::Context & ctx, const char * data)
+{
+ Uint32 connections = 0;
+ ctx.m_userProperties.get("NoOfConnections", &connections);
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "Connection_%d", connections);
+ ctx.m_userProperties.put("NoOfConnections", ++connections, true);
+
+ ctx.m_currentSection->put("Type", ctx.fname);
+ return true;
+}
+
+/**
+ * System rule: Just add it
+ */
+bool
+transformSystem(InitConfigFileParser::Context & ctx, const char * data){
+
+ const char * name;
+ if(!ctx.m_currentSection->get("Name", &name)){
+ ctx.reportError("Mandatory parameter Name missing from section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ ndbout << "transformSystem " << name << endl;
+
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "SYSTEM_%s", name);
+
+ return true;
+}
+
+/**
+ * External system rule: Just add it
+ */
+bool
+transformExternalSystem(InitConfigFileParser::Context & ctx, const char * data){
+ const char * name;
+ if(!ctx.m_currentSection->get("Name", &name)){
+ ctx.reportError("Mandatory parameter Name missing from section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "EXTERNAL SYSTEM_%s", name);
+
+ return true;
+}
+
+/**
+ * Computer rule: Update "NoOfComputers", add "Type"
+ */
+bool
+transformComputer(InitConfigFileParser::Context & ctx, const char * data){
+ const char * id;
+ if(!ctx.m_currentSection->get("Id", &id)){
+ ctx.reportError("Mandatory parameter Id missing from section "
+ "[%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "Computer_%s", id);
+
+ Uint32 computers = 0;
+ ctx.m_userProperties.get("NoOfComputers", &computers);
+ ctx.m_userProperties.put("NoOfComputers", ++computers, true);
+
+ const char * hostname = 0;
+ ctx.m_currentSection->get("HostName", &hostname);
+ if(!hostname){
+ return true;
+ }
+
+ return checkLocalhostHostnameMix(ctx);
+}
+
+/**
+ * Apply default values
+ */
+void
+applyDefaultValues(InitConfigFileParser::Context & ctx,
+ const Properties * defaults){
+ if(defaults != NULL){
+ Properties::Iterator it(defaults);
+
+ for(const char * name = it.first(); name != NULL; name = it.next()){
+ ConfigInfo::Status st = ctx.m_info->getStatus(ctx.m_currentInfo, name);
+ if(!ctx.m_currentSection->contains(name)){
+ switch (ctx.m_info->getType(ctx.m_currentInfo, name)){
+ case ConfigInfo::CI_INT:
+ case ConfigInfo::CI_BOOL:{
+ Uint32 val = 0;
+ ::require(defaults->get(name, &val));
+ ctx.m_currentSection->put(name, val);
+ break;
+ }
+ case ConfigInfo::CI_INT64:{
+ Uint64 val = 0;
+ ::require(defaults->get(name, &val));
+ ctx.m_currentSection->put64(name, val);
+ break;
+ }
+ case ConfigInfo::CI_STRING:{
+ const char * val;
+ ::require(defaults->get(name, &val));
+ ctx.m_currentSection->put(name, val);
+ break;
+ }
+ case ConfigInfo::CI_SECTION:
+ break;
+ }
+ }
+ }
+ }
+}
+
+bool
+applyDefaultValues(InitConfigFileParser::Context & ctx, const char * data){
+
+ if(strcmp(data, "user") == 0)
+ applyDefaultValues(ctx, ctx.m_userDefaults);
+ else if (strcmp(data, "system") == 0)
+ applyDefaultValues(ctx, ctx.m_systemDefaults);
+ else
+ return false;
+
+ return true;
+}
+
+/**
+ * Check that a section contains all MANDATORY parameters
+ */
+bool
+checkMandatory(InitConfigFileParser::Context & ctx, const char * data){
+
+ Properties::Iterator it(ctx.m_currentInfo);
+ for(const char * name = it.first(); name != NULL; name = it.next()){
+ const Properties * info = NULL;
+ ::require(ctx.m_currentInfo->get(name, &info));
+ Uint32 val;
+ if(info->get("Mandatory", &val)){
+ const char * fname;
+ ::require(info->get("Fname", &fname));
+ if(!ctx.m_currentSection->contains(fname)){
+ ctx.reportError("Mandatory parameter %s missing from section "
+ "[%s] starting at line: %d",
+ fname, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Connection rule: Fix node id
+ *
+ * Transform a string "NodeidX" (e.g. "uppsala.32")
+ * into a Uint32 "NodeIdX" (e.g. 32) and a string "SystemX" (e.g. "uppsala").
+ */
+static bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data)
+{
+ char buf[] = "NodeIdX"; buf[6] = data[sizeof("NodeI")];
+ char sysbuf[] = "SystemX"; sysbuf[6] = data[sizeof("NodeI")];
+ const char* nodeId;
+ require(ctx.m_currentSection->get(buf, &nodeId));
+
+ char tmpLine[MAX_LINE_LENGTH];
+ strncpy(tmpLine, nodeId, MAX_LINE_LENGTH);
+ char* token1 = strtok(tmpLine, ".");
+ char* token2 = strtok(NULL, ".");
+ Uint32 id;
+
+ if (token2 == NULL) { // Only a number given
+ errno = 0;
+ char* p;
+ id = strtol(token1, &p, 10);
+ if (errno != 0) warning("STRTOK1", nodeId);
+ require(ctx.m_currentSection->put(buf, id, true));
+ } else { // A pair given (e.g. "uppsala.32")
+ errno = 0;
+ char* p;
+ id = strtol(token2, &p, 10);
+ if (errno != 0) warning("STRTOK2", nodeId);
+ require(ctx.m_currentSection->put(buf, id, true));
+ require(ctx.m_currentSection->put(sysbuf, token1));
+ }
+ return true;
+}
+
+/**
+ * @returns true if connection is external (one node is external)
+ * Also returns:
+ * - name of external system in parameter extSystemName, and
+ * - nodeId of external node in parameter extSystemNodeId.
+ */
+static bool
+isExtConnection(InitConfigFileParser::Context & ctx,
+ const char **extSystemName, Uint32 * extSystemNodeId){
+
+ Uint32 nodeId1, nodeId2;
+
+ if (ctx.m_currentSection->contains("System1") &&
+ ctx.m_currentSection->get("System1", extSystemName) &&
+ ctx.m_currentSection->get("NodeId1", &nodeId1)) {
+ *extSystemNodeId = nodeId1;
+ return true;
+ }
+
+ if (ctx.m_currentSection->contains("System2") &&
+ ctx.m_currentSection->get("System2", extSystemName) &&
+ ctx.m_currentSection->get("NodeId2", &nodeId2)) {
+ *extSystemNodeId = nodeId2;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * External Connection Rule:
+ * If connection is to an external system, then move connection into
+ * external system configuration (i.e. a sub-property).
+ */
+static bool
+fixExtConnection(InitConfigFileParser::Context & ctx, const char * data){
+
+ const char * extSystemName;
+ Uint32 extSystemNodeId;
+
+ if (isExtConnection(ctx, &extSystemName, &extSystemNodeId)) {
+
+ Uint32 connections = 0;
+ ctx.m_userProperties.get("ExtNoOfConnections", &connections);
+ require(ctx.m_userProperties.put("ExtNoOfConnections",++connections, true));
+
+ char tmpLine1[MAX_LINE_LENGTH];
+ BaseString::snprintf(tmpLine1, MAX_LINE_LENGTH, "Connection_%d", connections-1);
+
+ /**
+ * Section: EXTERNAL SYSTEM_<Ext System Name>
+ */
+ char extSystemPropName[MAX_LINE_LENGTH];
+ strncpy(extSystemPropName, "EXTERNAL SYSTEM_", MAX_LINE_LENGTH);
+ strncat(extSystemPropName, extSystemName, MAX_LINE_LENGTH);
+ strncat(extSystemPropName, ":", MAX_LINE_LENGTH);
+ strncat(extSystemPropName, tmpLine1, MAX_LINE_LENGTH);
+
+ /**
+ * Increase number of external connections for the system
+ *
+ * @todo Limitation: Only one external system is allowed
+ */
+ require(ctx.m_userProperties.put("ExtSystem", extSystemName, true));
+
+ /**
+ * Make sure section is stored in right place
+ */
+ strncpy(ctx.pname, extSystemPropName, MAX_LINE_LENGTH);
+
+ /**
+ * Since this is an external connection,
+ * decrease number of internal connections
+ */
+ require(ctx.m_userProperties.get("NoOfConnections", &connections));
+ require(ctx.m_userProperties.put("NoOfConnections", --connections, true));
+ }
+
+ return true;
+}
+
+/**
+ * Connection rule: Fix hostname
+ *
+ * Unless Hostname is not already specified, do steps:
+ * -# Via Connection's NodeId lookup Node
+ * -# Via Node's ExecuteOnComputer lookup Hostname
+ * -# Add HostName to Connection
+ */
+static bool
+fixHostname(InitConfigFileParser::Context & ctx, const char * data){
+
+ char buf[] = "NodeIdX"; buf[6] = data[sizeof("HostNam")];
+ char sysbuf[] = "SystemX"; sysbuf[6] = data[sizeof("HostNam")];
+
+ if(!ctx.m_currentSection->contains(data)){
+ Uint32 id = 0;
+ require(ctx.m_currentSection->get(buf, &id));
+
+ const Properties * node;
+ if(!ctx.m_config->get("Node", id, &node))
+ {
+ ctx.reportError("Unknown node: \"%d\" specified in connection "
+ "[%s] starting at line: %d",
+ id, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ const char * hostname;
+ require(node->get("HostName", &hostname));
+ require(ctx.m_currentSection->put(data, hostname));
+ }
+ return true;
+}
+
+/**
+ * Connection rule: Fix port number (using a port number adder)
+ */
+static bool
+fixPortNumber(InitConfigFileParser::Context & ctx, const char * data){
+
+ DBUG_ENTER("fixPortNumber");
+
+ Uint32 id1= 0, id2= 0;
+ const char *hostName1;
+ const char *hostName2;
+ require(ctx.m_currentSection->get("NodeId1", &id1));
+ require(ctx.m_currentSection->get("NodeId2", &id2));
+ require(ctx.m_currentSection->get("HostName1", &hostName1));
+ require(ctx.m_currentSection->get("HostName2", &hostName2));
+ DBUG_PRINT("info",("NodeId1=%d HostName1=\"%s\"",id1,hostName1));
+ DBUG_PRINT("info",("NodeId2=%d HostName2=\"%s\"",id2,hostName2));
+
+ if (id1 > id2) {
+ Uint32 tmp= id1;
+ const char *tmp_name= hostName1;
+ hostName1= hostName2;
+ id1= id2;
+ hostName2= tmp_name;
+ id2= tmp;
+ }
+
+ const Properties * node;
+ require(ctx.m_config->get("Node", id1, &node));
+
+ BaseString hostname(hostName1);
+
+ if (hostname.c_str()[0] == 0) {
+ ctx.reportError("Hostname required on nodeid %d since it will "
+ "act as server.", id1);
+ DBUG_RETURN(false);
+ }
+
+ Uint32 port= 0;
+ const char * type1;
+ const char * type2;
+ const Properties * node2;
+
+ node->get("Type", &type1);
+ ctx.m_config->get("Node", id2, &node2);
+ node2->get("Type", &type2);
+
+ if(strcmp(type1, MGM_TOKEN)==0)
+ node->get("PortNumber",&port);
+ else if(strcmp(type2, MGM_TOKEN)==0)
+ node2->get("PortNumber",&port);
+
+ if (!port &&
+ !node->get("ServerPort", &port) &&
+ !ctx.m_userProperties.get("ServerPort_", id1, &port))
+ {
+ Uint32 base= 0;
+ /*
+ * If the connection doesn't involve an mgm server,
+ * and a default port number has been set, behave the old
+ * way of allocating port numbers for transporters.
+ */
+ if(ctx.m_userDefaults && ctx.m_userDefaults->get("PortNumber", &base))
+ {
+ Uint32 adder= 0;
+ {
+ BaseString server_port_adder(hostname);
+ server_port_adder.append("_ServerPortAdder");
+ ctx.m_userProperties.get(server_port_adder.c_str(), &adder);
+ ctx.m_userProperties.put(server_port_adder.c_str(), adder+1, true);
+ }
+
+ if (!ctx.m_userProperties.get("ServerPortBase", &base)){
+ if(!(ctx.m_userDefaults &&
+ ctx.m_userDefaults->get("PortNumber", &base)) &&
+ !ctx.m_systemDefaults->get("PortNumber", &base)) {
+ base= strtoll(NDB_TCP_BASE_PORT,0,0);
+ }
+ ctx.m_userProperties.put("ServerPortBase", base);
+ }
+
+ port= base + adder;
+ ctx.m_userProperties.put("ServerPort_", id1, port);
+ }
+ }
+
+ if(ctx.m_currentSection->contains("PortNumber")) {
+ ndbout << "PortNumber should no longer be specificied "
+ << "per connection, please remove from config. "
+ << "Will be changed to " << port << endl;
+ ctx.m_currentSection->put("PortNumber", port, true);
+ }
+ else
+ {
+ ctx.m_currentSection->put("PortNumber", port);
+ }
+
+ DBUG_PRINT("info", ("connection %d-%d port %d host %s",
+ id1, id2, port, hostname.c_str()));
+
+ DBUG_RETURN(true);
+}
+
+static bool
+fixShmUniqueId(InitConfigFileParser::Context & ctx, const char * data)
+{
+ DBUG_ENTER("fixShmUniqueId");
+ Uint32 nodes= 0;
+ ctx.m_userProperties.get(ctx.fname, &nodes);
+ if (nodes == 1) // first management server
+ {
+ Uint32 portno= atoi(NDB_PORT);
+ ctx.m_currentSection->get("PortNumber", &portno);
+ ctx.m_userProperties.put("ShmUniqueId", portno);
+ }
+ DBUG_RETURN(true);
+}
+
+static
+bool
+fixShmKey(InitConfigFileParser::Context & ctx, const char *)
+{
+ DBUG_ENTER("fixShmKey");
+ {
+ static int last_signum= -1;
+ Uint32 signum;
+ if(!ctx.m_currentSection->get("Signum", &signum))
+ {
+ signum= OPT_NDB_SHM_SIGNUM_DEFAULT;
+ if (signum <= 0)
+ {
+ ctx.reportError("Unable to set default parameter for [SHM]Signum"
+ " please specify [SHM DEFAULT]Signum");
+ return false;
+ }
+ ctx.m_currentSection->put("Signum", signum);
+ DBUG_PRINT("info",("Added Signum=%u", signum));
+ }
+ if ( last_signum != (int)signum && last_signum >= 0 )
+ {
+ ctx.reportError("All shared memory transporters must have same [SHM]Signum defined."
+ " Use [SHM DEFAULT]Signum");
+ return false;
+ }
+ last_signum= (int)signum;
+ }
+ {
+ Uint32 id1= 0, id2= 0, key= 0;
+ require(ctx.m_currentSection->get("NodeId1", &id1));
+ require(ctx.m_currentSection->get("NodeId2", &id2));
+ if(!ctx.m_currentSection->get("ShmKey", &key))
+ {
+ require(ctx.m_userProperties.get("ShmUniqueId", &key));
+ key= key << 16 | (id1 > id2 ? id1 << 8 | id2 : id2 << 8 | id1);
+ ctx.m_currentSection->put("ShmKey", key);
+ DBUG_PRINT("info",("Added ShmKey=0x%x", key));
+ }
+ }
+ DBUG_RETURN(true);
+}
+
+/**
+ * DB Node rule: Check various constraints
+ */
+static bool
+checkDbConstraints(InitConfigFileParser::Context & ctx, const char *){
+
+ Uint32 t1 = 0, t2 = 0;
+ ctx.m_currentSection->get("MaxNoOfConcurrentOperations", &t1);
+ ctx.m_currentSection->get("MaxNoOfConcurrentTransactions", &t2);
+
+ if (t1 < t2) {
+ ctx.reportError("MaxNoOfConcurrentOperations must be greater than "
+ "MaxNoOfConcurrentTransactions - [%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ Uint32 replicas = 0, otherReplicas;
+ ctx.m_currentSection->get("NoOfReplicas", &replicas);
+ if(ctx.m_userProperties.get("NoOfReplicas", &otherReplicas)){
+ if(replicas != otherReplicas){
+ ctx.reportError("NoOfReplicas defined differently on different nodes"
+ " - [%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ } else {
+ ctx.m_userProperties.put("NoOfReplicas", replicas);
+ }
+
+ return true;
+}
+
+/**
+ * Connection rule: Check varius constraints
+ */
+static bool
+checkConnectionConstraints(InitConfigFileParser::Context & ctx, const char *){
+
+ Uint32 id1 = 0, id2 = 0;
+ ctx.m_currentSection->get("NodeId1", &id1);
+ ctx.m_currentSection->get("NodeId2", &id2);
+
+ // If external connection, just accept it
+ if (ctx.m_currentSection->contains("System1") ||
+ ctx.m_currentSection->contains("System2"))
+ return true;
+
+ if(id1 == id2){
+ ctx.reportError("Illegal connection from node to itself"
+ " - [%s] starting at line: %d",
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ const Properties * node1;
+ if(!ctx.m_config->get("Node", id1, &node1)){
+ ctx.reportError("Connection refering to undefined node: %d"
+ " - [%s] starting at line: %d",
+ id1, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ const Properties * node2;
+ if(!ctx.m_config->get("Node", id2, &node2)){
+ ctx.reportError("Connection refering to undefined node: %d"
+ " - [%s] starting at line: %d",
+ id2, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ const char * type1;
+ const char * type2;
+ require(node1->get("Type", &type1));
+ require(node2->get("Type", &type2));
+
+ /**
+ * Report error if the following are true
+ * -# None of the nodes is of type DB
+ * -# Not both of them are MGMs
+ * -# None of them contain a "SystemX" name
+ */
+ if((strcmp(type1, DB_TOKEN) != 0 && strcmp(type2, DB_TOKEN) != 0) &&
+ !(strcmp(type1, MGM_TOKEN) == 0 && strcmp(type2, MGM_TOKEN) == 0) &&
+ !ctx.m_currentSection->contains("System1") &&
+ !ctx.m_currentSection->contains("System2")){
+ ctx.reportError("Invalid connection between node %d (%s) and node %d (%s)"
+ " - [%s] starting at line: %d",
+ id1, type1, id2, type2,
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+checkTCPConstraints(InitConfigFileParser::Context & ctx, const char * data){
+
+ const char * host;
+ struct in_addr addr;
+ if(ctx.m_currentSection->get(data, &host) && strlen(host) &&
+ Ndb_getInAddr(&addr, host)){
+ ctx.reportError("Unable to lookup/illegal hostname %s"
+ " - [%s] starting at line: %d",
+ host, ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ return true;
+}
+
+static
+bool
+transform(InitConfigFileParser::Context & ctx,
+ Properties & dst,
+ const char * oldName,
+ const char * newName,
+ double add, double mul){
+
+ if(ctx.m_currentSection->contains(newName)){
+ ctx.reportError("Both %s and %s specified"
+ " - [%s] starting at line: %d",
+ oldName, newName,
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ PropertiesType oldType;
+ require(ctx.m_currentSection->getTypeOf(oldName, &oldType));
+ ConfigInfo::Type newType = ctx.m_info->getType(ctx.m_currentInfo, newName);
+ if(!((oldType == PropertiesType_Uint32 || oldType == PropertiesType_Uint64)
+ && (newType == ConfigInfo::CI_INT || newType == ConfigInfo::CI_INT64 || newType == ConfigInfo::CI_BOOL))){
+ ndbout << "oldType: " << (int)oldType << ", newType: " << (int)newType << endl;
+ ctx.reportError("Unable to handle type conversion w.r.t deprication %s %s"
+ "- [%s] starting at line: %d",
+ oldName, newName,
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+ Uint64 oldVal;
+ require(ctx.m_currentSection->get(oldName, &oldVal));
+
+ Uint64 newVal = (Uint64)((Int64)oldVal * mul + add);
+ if(!ctx.m_info->verify(ctx.m_currentInfo, newName, newVal)){
+ ctx.reportError("Unable to handle deprication, new value not within bounds"
+ "%s %s - [%s] starting at line: %d",
+ oldName, newName,
+ ctx.fname, ctx.m_sectionLineno);
+ return false;
+ }
+
+ if(newType == ConfigInfo::CI_INT || newType == ConfigInfo::CI_BOOL){
+ require(dst.put(newName, (Uint32)newVal));
+ } else if(newType == ConfigInfo::CI_INT64) {
+ require(dst.put64(newName, newVal));
+ }
+ return true;
+}
+
+static bool
+fixDepricated(InitConfigFileParser::Context & ctx, const char * data){
+ const char * name;
+ /**
+ * Transform old values to new values
+ * Transform new values to old values (backward compatible)
+ */
+ Properties tmp(true);
+ Properties::Iterator it(ctx.m_currentSection);
+ for (name = it.first(); name != NULL; name = it.next()) {
+ const DepricationTransform * p = &f_deprication[0];
+ while(p->m_section != 0){
+ if(strcmp(p->m_section, ctx.fname) == 0){
+ double mul = p->m_mul;
+ double add = p->m_add;
+ if(strcmp(name, p->m_oldName) == 0){
+ if(!transform(ctx, tmp, name, p->m_newName, add, mul)){
+ return false;
+ }
+ } else if(strcmp(name, p->m_newName) == 0) {
+ if(!transform(ctx, tmp, name, p->m_oldName, -add/mul,1.0/mul)){
+ return false;
+ }
+ }
+ }
+ p++;
+ }
+ }
+
+ Properties::Iterator it2(&tmp);
+ for (name = it2.first(); name != NULL; name = it2.next()) {
+ PropertiesType type;
+ require(tmp.getTypeOf(name, &type));
+ switch(type){
+ case PropertiesType_Uint32:{
+ Uint32 val;
+ require(tmp.get(name, &val));
+ ::require(ctx.m_currentSection->put(name, val));
+ break;
+ }
+ case PropertiesType_char:{
+ const char * val;
+ require(tmp.get(name, &val));
+ ::require(ctx.m_currentSection->put(name, val));
+ break;
+ }
+ case PropertiesType_Uint64:{
+ Uint64 val;
+ require(tmp.get(name, &val));
+ ::require(ctx.m_currentSection->put64(name, val));
+ break;
+ }
+ case PropertiesType_Properties:
+ default:
+ abort();
+ }
+ }
+ return true;
+}
+
+static bool
+saveInConfigValues(InitConfigFileParser::Context & ctx, const char * data){
+ const Properties * sec;
+ if(!ctx.m_currentInfo->get(ctx.fname, &sec)){
+ abort();
+ return false;
+ }
+
+ do {
+ const char *secName;
+ Uint32 id, status, typeVal;
+ require(sec->get("Fname", &secName));
+ require(sec->get("Id", &id));
+ require(sec->get("Status", &status));
+ require(sec->get("SectionType", &typeVal));
+
+ if(id == KEY_INTERNAL || status == ConfigInfo::CI_INTERNAL){
+ ndbout_c("skipping section %s", ctx.fname);
+ break;
+ }
+
+ Uint32 no = 0;
+ ctx.m_userProperties.get("$Section", id, &no);
+ ctx.m_userProperties.put("$Section", id, no+1, true);
+
+ ctx.m_configValues.openSection(id, no);
+ ctx.m_configValues.put(CFG_TYPE_OF_SECTION, typeVal);
+
+ Properties::Iterator it(ctx.m_currentSection);
+ for (const char* n = it.first(); n != NULL; n = it.next()) {
+ const Properties * info;
+ if(!ctx.m_currentInfo->get(n, &info))
+ continue;
+
+ Uint32 id = 0;
+ info->get("Id", &id);
+
+ if(id == KEY_INTERNAL)
+ continue;
+
+ bool ok = true;
+ PropertiesType type;
+ require(ctx.m_currentSection->getTypeOf(n, &type));
+ switch(type){
+ case PropertiesType_Uint32:{
+ Uint32 val;
+ require(ctx.m_currentSection->get(n, &val));
+ ok = ctx.m_configValues.put(id, val);
+ break;
+ }
+ case PropertiesType_Uint64:{
+ Uint64 val;
+ require(ctx.m_currentSection->get(n, &val));
+ ok = ctx.m_configValues.put64(id, val);
+ break;
+ }
+ case PropertiesType_char:{
+ const char * val;
+ require(ctx.m_currentSection->get(n, &val));
+ ok = ctx.m_configValues.put(id, val);
+ break;
+ }
+ default:
+ abort();
+ }
+ require(ok);
+ }
+ ctx.m_configValues.closeSection();
+ } while(0);
+ return true;
+}
+
+static bool
+sanity_checks(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data)
+{
+ Uint32 db_nodes = 0;
+ Uint32 mgm_nodes = 0;
+ Uint32 api_nodes = 0;
+ if (!ctx.m_userProperties.get("DB", &db_nodes)) {
+ ctx.reportError("At least one database node should be defined in config file");
+ return false;
+ }
+ if (!ctx.m_userProperties.get("MGM", &mgm_nodes)) {
+ ctx.reportError("At least one management server node should be defined in config file");
+ return false;
+ }
+ if (!ctx.m_userProperties.get("API", &api_nodes)) {
+ ctx.reportError("At least one application node (for the mysqld) should be defined in config file");
+ return false;
+ }
+ return true;
+}
+
+static void
+add_a_connection(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ Uint32 nodeId1, Uint32 nodeId2, bool use_shm)
+{
+ ConfigInfo::ConfigRuleSection s;
+ const char *hostname1= 0, *hostname2= 0;
+ const Properties *tmp;
+
+ require(ctx.m_config->get("Node", nodeId1, &tmp));
+ tmp->get("HostName", &hostname1);
+
+ require(ctx.m_config->get("Node", nodeId2, &tmp));
+ tmp->get("HostName", &hostname2);
+
+ char buf[16];
+ s.m_sectionData= new Properties(true);
+ BaseString::snprintf(buf, sizeof(buf), "%u", nodeId1);
+ s.m_sectionData->put("NodeId1", buf);
+ BaseString::snprintf(buf, sizeof(buf), "%u", nodeId2);
+ s.m_sectionData->put("NodeId2", buf);
+
+ if (use_shm &&
+ hostname1 && hostname1[0] &&
+ hostname2 && hostname2[0] &&
+ strcmp(hostname1,hostname2) == 0)
+ {
+ s.m_sectionType= BaseString("SHM");
+ DBUG_PRINT("info",("adding SHM connection %d %d",nodeId1,nodeId2));
+ }
+ else
+ {
+ s.m_sectionType= BaseString("TCP");
+ DBUG_PRINT("info",("adding TCP connection %d %d",nodeId1,nodeId2));
+ }
+
+ sections.push_back(s);
+}
+
+static bool
+add_node_connections(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data)
+{
+ DBUG_ENTER("add_node_connections");
+ Uint32 i;
+ Properties * props= ctx.m_config;
+ Properties p_connections(true);
+ Properties p_connections2(true);
+
+ for (i = 0;; i++){
+ const Properties * tmp;
+ Uint32 nodeId1, nodeId2;
+
+ if(!props->get("Connection", i, &tmp)) break;
+
+ if(!tmp->get("NodeId1", &nodeId1)) continue;
+ p_connections.put("", nodeId1, nodeId1);
+ if(!tmp->get("NodeId2", &nodeId2)) continue;
+ p_connections.put("", nodeId2, nodeId2);
+
+ p_connections2.put("", nodeId1 + nodeId2<<16, nodeId1);
+ p_connections2.put("", nodeId2 + nodeId1<<16, nodeId2);
+ }
+
+ Uint32 nNodes;
+ ctx.m_userProperties.get("NoOfNodes", &nNodes);
+
+ Properties p_db_nodes(true);
+ Properties p_api_nodes(true);
+ Properties p_mgm_nodes(true);
+
+ Uint32 i_db= 0, i_api= 0, i_mgm= 0, n;
+ for (i= 0, n= 0; n < nNodes; i++){
+ const Properties * tmp;
+ if(!props->get("Node", i, &tmp)) continue;
+ n++;
+
+ const char * type;
+ if(!tmp->get("Type", &type)) continue;
+
+ if (strcmp(type,DB_TOKEN) == 0)
+ p_db_nodes.put("", i_db++, i);
+ else if (strcmp(type,API_TOKEN) == 0)
+ p_api_nodes.put("", i_api++, i);
+ else if (strcmp(type,MGM_TOKEN) == 0)
+ p_mgm_nodes.put("", i_mgm++, i);
+ }
+
+ Uint32 nodeId1, nodeId2, dummy;
+
+ for (i= 0; p_db_nodes.get("", i, &nodeId1); i++){
+ for (Uint32 j= i+1;; j++){
+ if(!p_db_nodes.get("", j, &nodeId2)) break;
+ if(!p_connections2.get("", nodeId1+nodeId2<<16, &dummy)) {
+ add_a_connection(sections,ctx,nodeId1,nodeId2,opt_ndb_shm);
+ }
+ }
+ }
+
+ for (i= 0; p_api_nodes.get("", i, &nodeId1); i++){
+ if(!p_connections.get("", nodeId1, &dummy)) {
+ for (Uint32 j= 0;; j++){
+ if(!p_db_nodes.get("", j, &nodeId2)) break;
+ add_a_connection(sections,ctx,nodeId1,nodeId2,opt_ndb_shm);
+ }
+ }
+ }
+
+ for (i= 0; p_mgm_nodes.get("", i, &nodeId1); i++){
+ if(!p_connections.get("", nodeId1, &dummy)) {
+ for (Uint32 j= 0;; j++){
+ if(!p_db_nodes.get("", j, &nodeId2)) break;
+ add_a_connection(sections,ctx,nodeId1,nodeId2,0);
+ }
+ }
+ }
+
+ DBUG_RETURN(true);
+}
+
+static bool set_connection_priorities(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data)
+{
+ DBUG_ENTER("set_connection_priorities");
+ DBUG_RETURN(true);
+}
+
+static bool add_server_ports(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data)
+{
+#if 0
+ Properties * props= ctx.m_config;
+ Properties computers(true);
+ Uint32 port_base = NDB_TCP_BASE_PORT;
+
+ Uint32 nNodes;
+ ctx.m_userProperties.get("NoOfNodes", &nNodes);
+
+ for (Uint32 i= 0, n= 0; n < nNodes; i++){
+ Properties * tmp;
+ if(!props->get("Node", i, &tmp)) continue;
+ n++;
+
+ const char * type;
+ if(!tmp->get("Type", &type)) continue;
+
+ Uint32 port;
+ if (tmp->get("ServerPort", &port)) continue;
+
+ Uint32 computer;
+ if (!tmp->get("ExecuteOnComputer", &computer)) continue;
+
+ Uint32 adder= 0;
+ computers.get("",computer, &adder);
+
+ if (strcmp(type,DB_TOKEN) == 0) {
+ adder++;
+ tmp->put("ServerPort", port_base+adder);
+ computers.put("",computer, adder);
+ }
+ }
+#endif
+ return true;
+}
+
+static bool
+check_node_vs_replicas(Vector<ConfigInfo::ConfigRuleSection>&sections,
+ struct InitConfigFileParser::Context &ctx,
+ const char * rule_data)
+{
+ Uint32 db_nodes= 0;
+ Uint32 replicas= 0;
+ Uint32 db_host_count= 0;
+ ctx.m_userProperties.get(DB_TOKEN, &db_nodes);
+ ctx.m_userProperties.get("NoOfReplicas", &replicas);
+ if((db_nodes % replicas) != 0){
+ ctx.reportError("Invalid no of db nodes wrt no of replicas.\n"
+ "No of nodes must be dividable with no or replicas");
+ return false;
+ }
+ // check that node groups and arbitrators are ok
+ // just issue warning if not
+ if(replicas > 1){
+ Properties * props= ctx.m_config;
+ Properties p_db_hosts(true); // store hosts which db nodes run on
+ Properties p_arbitrators(true); // store hosts which arbitrators run on
+ // arbitrator should not run together with db node on same host
+ Uint32 i, n, group= 0, i_group= 0;
+ Uint32 n_nodes;
+ BaseString node_group_warning, arbitration_warning;
+ const char *arbit_warn_fmt=
+ "\n arbitrator with id %d and db node with id %d on same host %s";
+ const char *arbit_warn_fmt2=
+ "\n arbitrator with id %d has no hostname specified";
+
+ ctx.m_userProperties.get("NoOfNodes", &n_nodes);
+ for (i= 0, n= 0; n < n_nodes; i++){
+ const Properties * tmp;
+ if(!props->get("Node", i, &tmp)) continue;
+ n++;
+
+ const char * type;
+ if(!tmp->get("Type", &type)) continue;
+
+ const char* host= 0;
+ tmp->get("HostName", &host);
+
+ if (strcmp(type,DB_TOKEN) == 0)
+ {
+ {
+ Uint32 ii;
+ if (!p_db_hosts.get(host,&ii))
+ db_host_count++;
+ p_db_hosts.put(host,i);
+ if (p_arbitrators.get(host,&ii))
+ {
+ arbitration_warning.appfmt(arbit_warn_fmt, ii, i, host);
+ p_arbitrators.remove(host); // only one warning per db node
+ }
+ }
+ {
+ unsigned j;
+ BaseString str, str2;
+ str.assfmt("#group%d_",group);
+ p_db_hosts.put(str.c_str(),i_group,host);
+ str2.assfmt("##group%d_",group);
+ p_db_hosts.put(str2.c_str(),i_group,i);
+ for (j= 0; j < i_group; j++)
+ {
+ const char *other_host;
+ p_db_hosts.get(str.c_str(),j,&other_host);
+ if (strcmp(host,other_host) == 0) {
+ unsigned int other_i, c= 0;
+ p_db_hosts.get(str2.c_str(),j,&other_i);
+ p_db_hosts.get(str.c_str(),&c);
+ if (c == 0) // first warning in this node group
+ node_group_warning.appfmt(" Node group %d", group);
+ c|= 1 << j;
+ p_db_hosts.put(str.c_str(),c);
+
+ node_group_warning.appfmt(",\n db node with id %d and id %d "
+ "on same host %s", other_i, i, host);
+ }
+ }
+ i_group++;
+ DBUG_ASSERT(i_group <= replicas);
+ if (i_group == replicas)
+ {
+ unsigned c= 0;
+ p_db_hosts.get(str.c_str(),&c);
+ if (c+1 == (1u << (replicas-1))) // all nodes on same machine
+ node_group_warning.append(".\n Host failure will "
+ "cause complete cluster shutdown.");
+ else if (c > 0)
+ node_group_warning.append(".\n Host failure may "
+ "cause complete cluster shutdown.");
+ group++;
+ i_group= 0;
+ }
+ }
+ }
+ else if (strcmp(type,API_TOKEN) == 0 ||
+ strcmp(type,MGM_TOKEN) == 0)
+ {
+ Uint32 rank;
+ if(tmp->get("ArbitrationRank", &rank) && rank > 0)
+ {
+ if(host && host[0] != 0)
+ {
+ Uint32 ii;
+ p_arbitrators.put(host,i);
+ if (p_db_hosts.get(host,&ii))
+ {
+ arbitration_warning.appfmt(arbit_warn_fmt, i, ii, host);
+ }
+ }
+ else
+ {
+ arbitration_warning.appfmt(arbit_warn_fmt2, i);
+ }
+ }
+ }
+ }
+ if (db_host_count > 1 && node_group_warning.length() > 0)
+ ndbout_c("Cluster configuration warning:\n%s",node_group_warning.c_str());
+ if (db_host_count > 1 && arbitration_warning.length() > 0)
+ ndbout_c("Cluster configuration warning:%s%s",arbitration_warning.c_str(),
+ "\n Running arbitrator on the same host as a database node may"
+ "\n cause complete cluster shutdown in case of host failure.");
+ }
+ return true;
+}
+
+template class Vector<ConfigInfo::ConfigRuleSection>;
diff --git a/storage/ndb/src/mgmsrv/ConfigInfo.hpp b/storage/ndb/src/mgmsrv/ConfigInfo.hpp
new file mode 100644
index 00000000000..dff8b34bf4a
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/ConfigInfo.hpp
@@ -0,0 +1,142 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ConfigInfo_H
+#define ConfigInfo_H
+
+#include <kernel_types.h>
+#include <Properties.hpp>
+#include <ndb_limits.h>
+#include <NdbOut.hpp>
+#include "InitConfigFileParser.hpp"
+
+/**
+ * A MANDATORY parameters must be specified in the config file
+ * An UNDEFINED parameter may or may not be specified in the config file
+ */
+static const char* MANDATORY = (char*)~(UintPtr)0;// Default value for mandatory params.
+static const char* UNDEFINED = 0; // Default value for undefined params.
+
+/**
+ * @class ConfigInfo
+ * @brief Metainformation about ALL cluster configuration parameters
+ *
+ * Use the getters to find out metainformation about parameters.
+ */
+class ConfigInfo {
+public:
+ enum Type { CI_BOOL, CI_INT, CI_INT64, CI_STRING, CI_SECTION };
+ enum Status { CI_USED, ///< Active
+ CI_DEPRICATED, ///< Can be, but shouldn't
+ CI_NOTIMPLEMENTED, ///< Is ignored.
+ CI_INTERNAL ///< Not configurable by the user
+ };
+
+ /**
+ * Entry for one configuration parameter
+ */
+ struct ParamInfo {
+ Uint32 _paramId;
+ const char* _fname;
+ const char* _section;
+ const char* _description;
+ Status _status;
+ bool _updateable;
+ Type _type;
+ const char* _default;
+ const char* _min;
+ const char* _max;
+ };
+
+ struct AliasPair{
+ const char * name;
+ const char * alias;
+ };
+
+ /**
+ * Entry for one section rule
+ */
+ struct SectionRule {
+ const char * m_section;
+ bool (* m_sectionRule)(struct InitConfigFileParser::Context &,
+ const char * m_ruleData);
+ const char * m_ruleData;
+ };
+
+ /**
+ * Entry for config rule
+ */
+ struct ConfigRuleSection {
+ BaseString m_sectionType;
+ Properties * m_sectionData;
+ };
+
+ struct ConfigRule {
+ bool (* m_configRule)(Vector<ConfigRuleSection>&,
+ struct InitConfigFileParser::Context &,
+ const char * m_ruleData);
+ const char * m_ruleData;
+ };
+
+ ConfigInfo();
+
+ /**
+ * Checks if the suggested value is valid for the suggested parameter
+ * (i.e. if it is >= than min and <= than max).
+ *
+ * @param section Init Config file section name
+ * @param fname Name of parameter
+ * @param value Value to check
+ * @return true if parameter value is valid.
+ *
+ * @note Result is not defined if section/name are wrong!
+ */
+ bool verify(const Properties* secti, const char* fname, Uint64 value) const;
+ const char* getAlias(const char*) const;
+ bool isSection(const char*) const;
+
+ const char* getDescription(const Properties * sec, const char* fname) const;
+ Type getType(const Properties * section, const char* fname) const;
+ Status getStatus(const Properties* section, const char* fname) const;
+ Uint64 getMin(const Properties * section, const char* fname) const;
+ Uint64 getMax(const Properties * section, const char* fname) const;
+ Uint64 getDefault(const Properties * section, const char* fname) const;
+
+ const Properties * getInfo(const char * section) const;
+ const Properties * getDefaults(const char * section) const;
+
+ void print() const;
+ void print(const char* section) const;
+ void print(const Properties * section, const char* parameter) const;
+
+private:
+ Properties m_info;
+ Properties m_systemDefaults;
+
+ static const ParamInfo m_ParamInfo[];
+ static const int m_NoOfParams;
+
+ static const AliasPair m_sectionNameAliases[];
+ static const char* m_sectionNames[];
+ static const int m_noOfSectionNames;
+
+public:
+ static const SectionRule m_SectionRules[];
+ static const ConfigRule m_ConfigRules[];
+ static const int m_NoOfRules;
+};
+
+#endif // ConfigInfo_H
diff --git a/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp b/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp
new file mode 100644
index 00000000000..822e10c89aa
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp
@@ -0,0 +1,590 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include "InitConfigFileParser.hpp"
+#include "Config.hpp"
+#include "MgmtErrorReporter.hpp"
+#include <NdbOut.hpp>
+#include "ConfigInfo.hpp"
+#include <m_string.h>
+
+const int MAX_LINE_LENGTH = 1024; // Max length of line of text in config file
+static void trim(char *);
+
+static void require(bool v) { if(!v) abort();}
+
+//****************************************************************************
+// Ctor / Dtor
+//****************************************************************************
+InitConfigFileParser::InitConfigFileParser(){
+ m_info = new ConfigInfo();
+}
+
+InitConfigFileParser::~InitConfigFileParser() {
+ delete m_info;
+}
+
+//****************************************************************************
+// Read Config File
+//****************************************************************************
+InitConfigFileParser::Context::Context(const ConfigInfo * info)
+ : m_userProperties(true), m_configValues(1000, 20) {
+
+ m_config = new Properties(true);
+ m_defaults = new Properties(true);
+}
+
+InitConfigFileParser::Context::~Context(){
+ if(m_config != 0)
+ delete m_config;
+
+ if(m_defaults != 0)
+ delete m_defaults;
+}
+
+Config *
+InitConfigFileParser::parseConfig(const char * filename) {
+ FILE * file = fopen(filename, "r");
+ if(file == 0){
+ ndbout << "Error opening file: " << filename << endl;
+ return 0;
+ }
+
+ Config * ret = parseConfig(file);
+ fclose(file);
+ return ret;
+}
+
+Config *
+InitConfigFileParser::parseConfig(FILE * file) {
+
+ char line[MAX_LINE_LENGTH];
+
+ Context ctx(m_info);
+ ctx.m_lineno = 0;
+ ctx.m_currentSection = 0;
+
+ /*************
+ * Open file *
+ *************/
+ if (file == NULL) {
+ return 0;
+ }
+
+ /***********************
+ * While lines to read *
+ ***********************/
+ while (fgets(line, MAX_LINE_LENGTH, file)) {
+ ctx.m_lineno++;
+
+ trim(line);
+
+ if (isEmptyLine(line)) // Skip if line is empty or comment
+ continue;
+
+ // End with NULL instead of newline
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ /********************************
+ * 1. Parse new default section *
+ ********************************/
+ if (char* section = parseDefaultSectionHeader(line)) {
+ if(!storeSection(ctx)){
+ free(section);
+ ctx.reportError("Could not store previous default section "
+ "of configuration file.");
+ return 0;
+ }
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section); free(section);
+ ctx.type = InitConfigFileParser::DefaultSection;
+ ctx.m_sectionLineno = ctx.m_lineno;
+ ctx.m_currentSection = new Properties(true);
+ ctx.m_userDefaults = NULL;
+ require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
+ require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
+ continue;
+ }
+
+ /************************
+ * 2. Parse new section *
+ ************************/
+ if (char* section = parseSectionHeader(line)) {
+ if(!storeSection(ctx)){
+ free(section);
+ ctx.reportError("Could not store previous section "
+ "of configuration file.");
+ return 0;
+ }
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section);
+ free(section);
+ ctx.type = InitConfigFileParser::Section;
+ ctx.m_sectionLineno = ctx.m_lineno;
+ ctx.m_currentSection = new Properties(true);
+ ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
+ require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
+ require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
+ continue;
+ }
+
+ /****************************
+ * 3. Parse name-value pair *
+ ****************************/
+ if (!parseNameValuePair(ctx, line)) {
+ ctx.reportError("Could not parse name-value pair in config file.");
+ return 0;
+ }
+ }
+
+ if (ferror(file)){
+ ctx.reportError("Failure in reading");
+ return 0;
+ }
+
+ if(!storeSection(ctx)) {
+ ctx.reportError("Could not store section of configuration file.");
+ return 0;
+ }
+ for(size_t i = 0; ConfigInfo::m_ConfigRules[i].m_configRule != 0; i++){
+ ctx.type = InitConfigFileParser::Undefined;
+ ctx.m_currentSection = 0;
+ ctx.m_userDefaults = 0;
+ ctx.m_currentInfo = 0;
+ ctx.m_systemDefaults = 0;
+
+ Vector<ConfigInfo::ConfigRuleSection> tmp;
+ if(!(* ConfigInfo::m_ConfigRules[i].m_configRule)(tmp, ctx,
+ ConfigInfo::m_ConfigRules[i].m_ruleData))
+ return 0;
+
+ for(size_t j = 0; j<tmp.size(); j++){
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), tmp[j].m_sectionType.c_str());
+ ctx.type = InitConfigFileParser::Section;
+ ctx.m_currentSection = tmp[j].m_sectionData;
+ ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
+ require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
+ require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
+ if(!storeSection(ctx))
+ return 0;
+ }
+ }
+
+ Uint32 nConnections = 0;
+ Uint32 nComputers = 0;
+ Uint32 nNodes = 0;
+ Uint32 nExtConnections = 0;
+ const char * system = "?";
+ ctx.m_userProperties.get("NoOfConnections", &nConnections);
+ ctx.m_userProperties.get("NoOfComputers", &nComputers);
+ ctx.m_userProperties.get("NoOfNodes", &nNodes);
+ ctx.m_userProperties.get("ExtNoOfConnections", &nExtConnections);
+ ctx.m_userProperties.get("ExtSystem", &system);
+ ctx.m_config->put("NoOfConnections", nConnections);
+ ctx.m_config->put("NoOfComputers", nComputers);
+ ctx.m_config->put("NoOfNodes", nNodes);
+
+ char tmpLine[MAX_LINE_LENGTH];
+ BaseString::snprintf(tmpLine, MAX_LINE_LENGTH, "EXTERNAL SYSTEM_");
+ strncat(tmpLine, system, MAX_LINE_LENGTH);
+ strncat(tmpLine, ":NoOfConnections", MAX_LINE_LENGTH);
+ ctx.m_config->put(tmpLine, nExtConnections);
+
+ Config * ret = new Config();
+ ret->m_configValues = (struct ndb_mgm_configuration*)ctx.m_configValues.getConfigValues();
+ ret->m_oldConfig = ctx.m_config; ctx.m_config = 0;
+ return ret;
+}
+
+//****************************************************************************
+// Parse Name-Value Pair
+//****************************************************************************
+
+bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line)
+{
+ if (ctx.m_currentSection == NULL){
+ ctx.reportError("Value specified outside section");
+ return false;
+ }
+
+ // *************************************
+ // Split string at first occurrence of
+ // '=' or ':'
+ // *************************************
+
+ Vector<BaseString> tmp_string_split;
+ if (BaseString(line).split(tmp_string_split,
+ "=:", 2) != 2)
+ {
+ ctx.reportError("Parse error");
+ return false;
+ }
+
+ // *************************************
+ // Remove all after #
+ // *************************************
+
+ Vector<BaseString> tmp_string_split2;
+ tmp_string_split[1].split(tmp_string_split2,
+ "#", 2);
+ tmp_string_split[1]=tmp_string_split2[0];
+
+ // *************************************
+ // Remove leading and trailing chars
+ // *************************************
+ {
+ for (int i = 0; i < 2; i++)
+ tmp_string_split[i].trim("\r\n \t");
+ }
+
+ // *************************************
+ // First in split is fname
+ // *************************************
+
+ const char *fname= tmp_string_split[0].c_str();
+
+ if (!ctx.m_currentInfo->contains(fname)) {
+ ctx.reportError("[%s] Unknown parameter: %s", ctx.fname, fname);
+ return false;
+ }
+ ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname);
+ if (status == ConfigInfo::CI_NOTIMPLEMENTED) {
+ ctx.reportWarning("[%s] %s not yet implemented", ctx.fname, fname);
+ }
+ if (status == ConfigInfo::CI_DEPRICATED) {
+ const char * desc = m_info->getDescription(ctx.m_currentInfo, fname);
+ if(desc){
+ ctx.reportWarning("[%s] %s is depricated, use %s instead",
+ ctx.fname, fname, desc);
+ } else {
+ ctx.reportWarning("[%s] %s is depricated", ctx.fname, fname);
+ }
+ }
+
+ // ***********************
+ // Store name-value pair
+ // ***********************
+
+ return storeNameValuePair(ctx, fname, tmp_string_split[1].c_str());
+}
+
+
+//****************************************************************************
+// STORE NAME-VALUE pair in properties section
+//****************************************************************************
+
+bool
+InitConfigFileParser::storeNameValuePair(Context& ctx,
+ const char* fname,
+ const char* value) {
+
+ const char * pname = fname;
+
+ if (ctx.m_currentSection->contains(pname)) {
+ ctx.reportError("[%s] Parameter %s specified twice", ctx.fname, fname);
+ return false;
+ }
+
+ // ***********************
+ // Store name-value pair
+ // ***********************
+
+ const ConfigInfo::Type type = m_info->getType(ctx.m_currentInfo, fname);
+ switch(type){
+ case ConfigInfo::CI_BOOL: {
+ bool value_bool;
+ if (!convertStringToBool(value, value_bool)) {
+ ctx.reportError("Illegal boolean value for parameter %s", fname);
+ return false;
+ }
+ MGM_REQUIRE(ctx.m_currentSection->put(pname, value_bool));
+ break;
+ }
+ case ConfigInfo::CI_INT:
+ case ConfigInfo::CI_INT64:{
+ Uint64 value_int;
+ if (!convertStringToUint64(value, value_int)) {
+ ctx.reportError("Illegal integer value for parameter %s", fname);
+ return false;
+ }
+ if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) {
+ ctx.reportError("Illegal value %s for parameter %s.\n"
+ "Legal values are between %Lu and %Lu", value, fname,
+ m_info->getMin(ctx.m_currentInfo, fname),
+ m_info->getMax(ctx.m_currentInfo, fname));
+ return false;
+ }
+ if(type == ConfigInfo::CI_INT){
+ MGM_REQUIRE(ctx.m_currentSection->put(pname, (Uint32)value_int));
+ } else {
+ MGM_REQUIRE(ctx.m_currentSection->put64(pname, value_int));
+ }
+ break;
+ }
+ case ConfigInfo::CI_STRING:
+ MGM_REQUIRE(ctx.m_currentSection->put(pname, value));
+ break;
+ case ConfigInfo::CI_SECTION:
+ abort();
+ }
+ return true;
+}
+
+//****************************************************************************
+// Is Empty Line
+//****************************************************************************
+
+bool InitConfigFileParser::isEmptyLine(const char* line) const {
+ int i;
+
+ // Check if it is a comment line
+ if (line[0] == '#') return true;
+
+ // Check if it is a line with only spaces
+ for (i = 0; i < MAX_LINE_LENGTH && line[i] != '\n' && line[i] != '\0'; i++) {
+ if (line[i] != ' ' && line[i] != '\t') return false;
+ }
+ return true;
+}
+
+//****************************************************************************
+// Convert String to Int
+//****************************************************************************
+bool InitConfigFileParser::convertStringToUint64(const char* s,
+ Uint64& val,
+ Uint32 log10base) {
+ if (s == NULL)
+ return false;
+ if (strlen(s) == 0)
+ return false;
+
+ errno = 0;
+ char* p;
+ Int64 v = strtoll(s, &p, log10base);
+ if (errno != 0)
+ return false;
+
+ long mul = 0;
+ if (p != &s[strlen(s)]){
+ char * tmp = strdup(p);
+ trim(tmp);
+ switch(tmp[0]){
+ case 'k':
+ case 'K':
+ mul = 10;
+ break;
+ case 'M':
+ mul = 20;
+ break;
+ case 'G':
+ mul = 30;
+ break;
+ default:
+ free(tmp);
+ return false;
+ }
+ free(tmp);
+ }
+
+ val = (v << mul);
+ return true;
+}
+
+bool InitConfigFileParser::convertStringToBool(const char* s, bool& val) {
+ if (s == NULL) return false;
+ if (strlen(s) == 0) return false;
+
+ if (!strcmp(s, "Y") || !strcmp(s, "y") ||
+ !strcmp(s, "Yes") || !strcmp(s, "YES") || !strcmp(s, "yes") ||
+ !strcmp(s, "True") || !strcmp(s, "TRUE") || !strcmp(s, "true") ||
+ !strcmp(s, "1")) {
+ val = true;
+ return true;
+ }
+
+ if (!strcmp(s, "N") || !strcmp(s, "n") ||
+ !strcmp(s, "No") || !strcmp(s, "NO") || !strcmp(s, "no") ||
+ !strcmp(s, "False") || !strcmp(s, "FALSE") || !strcmp(s, "false") ||
+ !strcmp(s, "0")) {
+ val = false;
+ return true;
+ }
+
+ return false; // Failure to convert
+}
+
+//****************************************************************************
+// Parse Section Header
+//****************************************************************************
+static void
+trim(char * str){
+ int len = strlen(str);
+ for(len--;
+ (str[len] == '\r' || str[len] == '\n' ||
+ str[len] == ' ' || str[len] == '\t') &&
+ len > 0;
+ len--)
+ str[len] = 0;
+
+ int pos = 0;
+ while(str[pos] == ' ' || str[pos] == '\t')
+ pos++;
+
+ if(str[pos] == '\"' && str[len] == '\"') {
+ pos++;
+ str[len] = 0;
+ len--;
+ }
+
+ memmove(str, &str[pos], len - pos + 2);
+}
+
+char*
+InitConfigFileParser::parseSectionHeader(const char* line) const {
+ char * tmp = strdup(line);
+
+ if(tmp[0] != '['){
+ free(tmp);
+ return NULL;
+ }
+
+ if(tmp[strlen(tmp)-1] != ']'){
+ free(tmp);
+ return NULL;
+ }
+ tmp[strlen(tmp)-1] = 0;
+
+ tmp[0] = ' ';
+ trim(tmp);
+
+ // Get the correct header name if an alias
+ {
+ const char *tmp_alias= m_info->getAlias(tmp);
+ if (tmp_alias) {
+ free(tmp);
+ tmp= strdup(tmp_alias);
+ }
+ }
+
+ // Lookup token among sections
+ if(!m_info->isSection(tmp)) {
+ free(tmp);
+ return NULL;
+ }
+ if(m_info->getInfo(tmp)) return tmp;
+
+ free(tmp);
+ return NULL;
+}
+
+//****************************************************************************
+// Parse Default Section Header
+//****************************************************************************
+
+char*
+InitConfigFileParser::parseDefaultSectionHeader(const char* line) const {
+ static char token1[MAX_LINE_LENGTH], token2[MAX_LINE_LENGTH];
+
+ int no = sscanf(line, "[%120[A-Z_a-z] %120[A-Z_a-z]]", token1, token2);
+
+ // Not correct no of tokens
+ if (no != 2) return NULL;
+
+ // Not correct keyword at end
+ if (!strcasecmp(token2, "DEFAULT") == 0) return NULL;
+
+ const char *token1_alias= m_info->getAlias(token1);
+ if (token1_alias == 0)
+ token1_alias= token1;
+
+ if(m_info->getInfo(token1_alias)){
+ return strdup(token1_alias);
+ }
+
+ // Did not find section
+ return NULL;
+}
+
+const Properties *
+InitConfigFileParser::getSection(const char * name, const Properties * src){
+ const Properties * p;
+ if(src && src->get(name, &p))
+ return p;
+
+ return 0;
+}
+
+//****************************************************************************
+// STORE section
+//****************************************************************************
+bool
+InitConfigFileParser::storeSection(Context& ctx){
+ if(ctx.m_currentSection == NULL)
+ return true;
+ for(int i = strlen(ctx.fname) - 1; i>=0; i--){
+ ctx.fname[i] = toupper(ctx.fname[i]);
+ }
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), ctx.fname);
+ char buf[255];
+ if(ctx.type == InitConfigFileParser::Section)
+ BaseString::snprintf(buf, sizeof(buf), "%s", ctx.fname);
+ if(ctx.type == InitConfigFileParser::DefaultSection)
+ BaseString::snprintf(buf, sizeof(buf), "%s DEFAULT", ctx.fname);
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), buf);
+ if(ctx.type == InitConfigFileParser::Section){
+ for(int i = 0; i<m_info->m_NoOfRules; i++){
+ const ConfigInfo::SectionRule & rule = m_info->m_SectionRules[i];
+ if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)){
+ if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)){
+ return false;
+ }
+ }
+ }
+ }
+ if(ctx.type == InitConfigFileParser::DefaultSection)
+ require(ctx.m_defaults->put(ctx.pname, ctx.m_currentSection));
+ if(ctx.type == InitConfigFileParser::Section)
+ require(ctx.m_config->put(ctx.pname, ctx.m_currentSection));
+ delete ctx.m_currentSection; ctx.m_currentSection = NULL;
+ return true;
+}
+
+void
+InitConfigFileParser::Context::reportError(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << "Error line " << m_lineno << ": " << buf << endl;
+ va_end(ap);
+
+ //m_currentSection->print();
+}
+
+void
+InitConfigFileParser::Context::reportWarning(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << "Warning line " << m_lineno << ": " << buf << endl;
+ va_end(ap);
+}
diff --git a/storage/ndb/src/mgmsrv/InitConfigFileParser.hpp b/storage/ndb/src/mgmsrv/InitConfigFileParser.hpp
new file mode 100644
index 00000000000..1ea0a094ccd
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/InitConfigFileParser.hpp
@@ -0,0 +1,127 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 InitConfigFileParser_H
+#define InitConfigFileParser_H
+
+#include <ndb_global.h>
+
+#include <Properties.hpp>
+#include <ConfigValues.hpp>
+
+class Config;
+class ConfigInfo;
+
+/**
+ * @class InitConfigFileParser
+ * @brief Reads initial config file and returns Config object
+ *
+ * This class contains one public method InitConfigFileParser::parseConfig,
+ * which reads an initial configuration file and returns a Config
+ * object if the config file has correct syntax and semantic.
+ */
+class InitConfigFileParser {
+public:
+ /**
+ * Constructor
+ */
+ InitConfigFileParser();
+ ~InitConfigFileParser();
+
+ /**
+ * Reads the initial configuration file, checks syntax and semantic
+ * and stores internally the values of all parameters.
+ *
+ * @returns Config or NULL on failure
+ * @note must be freed by caller
+ */
+ Config * parseConfig(FILE * file);
+ Config * parseConfig(const char * filename);
+
+ /**
+ * Parser context struct
+ */
+ enum ContextSectionType { Undefined, Section, DefaultSection };
+
+ /**
+ * Context = Which section in init config file we are currently parsing
+ */
+ struct Context {
+ Context(const ConfigInfo *);
+ ~Context();
+
+ ContextSectionType type; ///< Section type (e.g. default section,section)
+ char fname[256]; ///< Section name occuring in init config file
+ char pname[256]; ///< Section name stored in properties object
+ Uint32 m_lineno; ///< Current line no in config file
+ Uint32 m_sectionLineno; ///< Where did current section start
+
+ const ConfigInfo * m_info; // The config info
+ Properties * m_config; // The config object
+ Properties * m_defaults; // The user defaults
+
+ Properties * m_currentSection; // The current section I'm in
+ const Properties * m_userDefaults; // The defaults of this section
+ const Properties * m_systemDefaults; // The syst. defaults for this section
+ const Properties * m_currentInfo; // The "info" for this section
+
+ Properties m_userProperties; // User properties (temporary values)
+ ConfigValuesFactory m_configValues; //
+
+ public:
+ void reportError(const char * msg, ...);
+ void reportWarning(const char * msg, ...);
+ };
+
+ static bool convertStringToUint64(const char* s, Uint64& val, Uint32 log10base = 0);
+ static bool convertStringToBool(const char* s, bool& val);
+
+private:
+ /**
+ * Check if line only contains space/comments
+ * @param line: The line to check
+ * @return true if spaces/comments only, false otherwise
+ */
+ bool isEmptyLine(const char* line) const;
+
+ /**
+ * Checks if line contains a section header
+ * @param line: String to search
+ * @return section header if matching some section header, NULL otherwise
+ */
+ char* parseSectionHeader(const char* line) const;
+
+ /**
+ * Checks if line contains a default header
+ * @param line: String to search
+ * @return section header if matching some section header, NULL otherwise
+ */
+ char* parseDefaultSectionHeader(const char* line) const;
+
+ bool parseNameValuePair(Context&, const char* line);
+ bool storeNameValuePair(Context&, const char* fname, const char* value);
+
+ bool storeSection(Context&);
+
+ const Properties* getSection(const char * name, const Properties* src);
+
+ /**
+ * Information about parameters (min, max values etc)
+ */
+ ConfigInfo* m_info;
+};
+
+#endif // InitConfigFileParser_H
diff --git a/storage/ndb/src/mgmsrv/Makefile.am b/storage/ndb/src/mgmsrv/Makefile.am
new file mode 100644
index 00000000000..7fd3fa66b43
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/Makefile.am
@@ -0,0 +1,60 @@
+MYSQLDATAdir = $(localstatedir)
+MYSQLSHAREdir = $(pkgdatadir)
+MYSQLBASEdir= $(prefix)
+#MYSQLCLUSTERdir= $(prefix)/mysql-cluster
+MYSQLCLUSTERdir= .
+
+ndbbin_PROGRAMS = ndb_mgmd
+
+ndb_mgmd_SOURCES = \
+ MgmtSrvr.cpp \
+ MgmtSrvrGeneralSignalHandling.cpp \
+ main.cpp \
+ Services.cpp \
+ convertStrToInt.cpp \
+ SignalQueue.cpp \
+ MgmtSrvrConfig.cpp \
+ ConfigInfo.cpp \
+ InitConfigFileParser.cpp \
+ Config.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/ndbapi \
+ -I$(top_srcdir)/ndb/src/mgmapi \
+ -I$(top_srcdir)/ndb/src/common/mgmcommon \
+ -I$(top_srcdir)/ndb/src/mgmclient
+
+LDADD_LOC = $(top_srcdir)/ndb/src/mgmclient/CommandInterpreter.o \
+ $(top_builddir)/ndb/src/libndbclient.la \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/strings/libmystrings.a \
+ @readline_link@ \
+ @NDB_SCI_LIBS@ \
+ @TERMCAP_LIB@
+
+DEFS_LOC = -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
+ -DDATADIR="\"$(MYSQLDATAdir)\"" \
+ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
+ -DMYSQLCLUSTERDIR="\"$(MYSQLCLUSTERdir)\""
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
+
+ndb_mgmd_LDFLAGS = @ndb_bin_am_ldflags@
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: ndb_mgmd.dsp
+
+ndb_mgmd.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-prg.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-prg.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(ndbbin_PROGRAMS)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(ndb_mgmd_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
new file mode 100644
index 00000000000..21c3c4d614d
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
@@ -0,0 +1,2933 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+
+#include "MgmtSrvr.hpp"
+#include "MgmtErrorReporter.hpp"
+#include <ConfigRetriever.hpp>
+
+#include <NdbOut.hpp>
+#include <NdbApiSignal.hpp>
+#include <kernel_types.h>
+#include <RefConvert.hpp>
+#include <BlockNumbers.h>
+#include <GlobalSignalNumbers.h>
+#include <signaldata/TestOrd.hpp>
+#include <signaldata/TamperOrd.hpp>
+#include <signaldata/StartOrd.hpp>
+#include <signaldata/ApiVersion.hpp>
+#include <signaldata/ResumeReq.hpp>
+#include <signaldata/SetLogLevelOrd.hpp>
+#include <signaldata/EventSubscribeReq.hpp>
+#include <signaldata/EventReport.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <signaldata/BackupSignalData.hpp>
+#include <signaldata/GrepImpl.hpp>
+#include <signaldata/ManagementServer.hpp>
+#include <NdbSleep.h>
+#include <EventLogger.hpp>
+#include <DebuggerNames.hpp>
+#include <ndb_version.h>
+
+#include <SocketServer.hpp>
+#include <NdbConfig.h>
+
+#include <NdbAutoPtr.hpp>
+
+#include <ndberror.h>
+
+#include <mgmapi.h>
+#include <mgmapi_configuration.hpp>
+#include <mgmapi_config_parameters.h>
+#include <m_string.h>
+
+//#define MGM_SRV_DEBUG
+#ifdef MGM_SRV_DEBUG
+#define DEBUG(x) do ndbout << x << endl; while(0)
+#else
+#define DEBUG(x)
+#endif
+
+extern int global_flag_send_heartbeat_now;
+extern int g_no_nodeid_checks;
+
+void *
+MgmtSrvr::logLevelThread_C(void* m)
+{
+ MgmtSrvr *mgm = (MgmtSrvr*)m;
+ mgm->logLevelThreadRun();
+ return 0;
+}
+
+void *
+MgmtSrvr::signalRecvThread_C(void *m)
+{
+ MgmtSrvr *mgm = (MgmtSrvr*)m;
+ mgm->signalRecvThreadRun();
+ return 0;
+}
+
+class SigMatch
+{
+public:
+ int gsn;
+ void (MgmtSrvr::* function)(NdbApiSignal *signal);
+
+ SigMatch() { gsn = 0; function = NULL; };
+
+ SigMatch(int _gsn,
+ void (MgmtSrvr::* _function)(NdbApiSignal *signal)) {
+ gsn = _gsn;
+ function = _function;
+ };
+
+ bool check(NdbApiSignal *signal) {
+ if(signal->readSignalNumber() == gsn)
+ return true;
+ return false;
+ };
+
+};
+
+void
+MgmtSrvr::signalRecvThreadRun()
+{
+ Vector<SigMatch> siglist;
+ siglist.push_back(SigMatch(GSN_MGM_LOCK_CONFIG_REQ,
+ &MgmtSrvr::handle_MGM_LOCK_CONFIG_REQ));
+ siglist.push_back(SigMatch(GSN_MGM_UNLOCK_CONFIG_REQ,
+ &MgmtSrvr::handle_MGM_UNLOCK_CONFIG_REQ));
+
+ while(!_isStopThread) {
+ SigMatch *handler = NULL;
+ NdbApiSignal *signal = NULL;
+ if(m_signalRecvQueue.waitFor(siglist, &handler, &signal, DEFAULT_TIMEOUT)) {
+ if(handler->function != 0)
+ (this->*handler->function)(signal);
+ }
+ }
+}
+
+extern EventLogger g_eventLogger;
+
+static NdbOut&
+operator<<(NdbOut& out, const LogLevel & ll)
+{
+ out << "[LogLevel: ";
+ for(size_t i = 0; i<LogLevel::LOGLEVEL_CATEGORIES; i++)
+ out << ll.getLogLevel((LogLevel::EventCategory)i) << " ";
+ out << "]";
+ return out;
+}
+
+void
+MgmtSrvr::logLevelThreadRun()
+{
+ while (!_isStopThread) {
+ /**
+ * Handle started nodes
+ */
+ EventSubscribeReq req;
+ req = m_event_listner[0].m_logLevel;
+ req.blockRef = _ownReference;
+
+ SetLogLevelOrd ord;
+
+ m_started_nodes.lock();
+ while(m_started_nodes.size() > 0){
+ Uint32 node = m_started_nodes[0];
+ m_started_nodes.erase(0, false);
+ m_started_nodes.unlock();
+
+ setEventReportingLevelImpl(node, req);
+
+ ord = m_nodeLogLevel[node];
+ setNodeLogLevelImpl(node, ord);
+
+ m_started_nodes.lock();
+ }
+ m_started_nodes.unlock();
+
+ m_log_level_requests.lock();
+ while(m_log_level_requests.size() > 0){
+ req = m_log_level_requests[0];
+ m_log_level_requests.erase(0, false);
+ m_log_level_requests.unlock();
+
+ LogLevel tmp;
+ tmp = req;
+
+ if(req.blockRef == 0){
+ req.blockRef = _ownReference;
+ setEventReportingLevelImpl(0, req);
+ } else {
+ ord = req;
+ setNodeLogLevelImpl(req.blockRef, ord);
+ }
+ m_log_level_requests.lock();
+ }
+ m_log_level_requests.unlock();
+ NdbSleep_MilliSleep(_logLevelThreadSleep);
+ }
+}
+
+void
+MgmtSrvr::startEventLog()
+{
+ NdbMutex_Lock(m_configMutex);
+
+ g_eventLogger.setCategory("MgmSrvr");
+
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_NODE);
+
+ if(iter.find(CFG_NODE_ID, _ownNodeId) != 0){
+ NdbMutex_Unlock(m_configMutex);
+ return;
+ }
+
+ const char * tmp;
+ BaseString logdest;
+ char *clusterLog= NdbConfig_ClusterLogFileName(_ownNodeId);
+ NdbAutoPtr<char> tmp_aptr(clusterLog);
+
+ if(iter.get(CFG_LOG_DESTINATION, &tmp) == 0){
+ logdest.assign(tmp);
+ }
+ NdbMutex_Unlock(m_configMutex);
+
+ if(logdest.length() == 0 || logdest == "") {
+ logdest.assfmt("FILE:filename=%s,maxsize=1000000,maxfiles=6",
+ clusterLog);
+ }
+ if(!g_eventLogger.addHandler(logdest)) {
+ ndbout << "Warning: could not add log destination \""
+ << logdest.c_str() << "\"" << endl;
+ }
+}
+
+void
+MgmtSrvr::stopEventLog()
+{
+ // Nothing yet
+}
+
+class ErrorItem
+{
+public:
+ int _errorCode;
+ const char * _errorText;
+};
+
+bool
+MgmtSrvr::setEventLogFilter(int severity, int enable)
+{
+ Logger::LoggerLevel level = (Logger::LoggerLevel)severity;
+ if (enable > 0) {
+ g_eventLogger.enable(level);
+ } else if (enable == 0) {
+ g_eventLogger.disable(level);
+ } else if (g_eventLogger.isEnable(level)) {
+ g_eventLogger.disable(level);
+ } else {
+ g_eventLogger.enable(level);
+ }
+ return g_eventLogger.isEnable(level);
+}
+
+bool
+MgmtSrvr::isEventLogFilterEnabled(int severity)
+{
+ return g_eventLogger.isEnable((Logger::LoggerLevel)severity);
+}
+
+static ErrorItem errorTable[] =
+{
+ {MgmtSrvr::NO_CONTACT_WITH_PROCESS, "No contact with the process (dead ?)."},
+ {MgmtSrvr::PROCESS_NOT_CONFIGURED, "The process is not configured."},
+ {MgmtSrvr::WRONG_PROCESS_TYPE,
+ "The process has wrong type. Expected a DB process."},
+ {MgmtSrvr::COULD_NOT_ALLOCATE_MEMORY, "Could not allocate memory."},
+ {MgmtSrvr::SEND_OR_RECEIVE_FAILED, "Send to process or receive failed."},
+ {MgmtSrvr::INVALID_LEVEL, "Invalid level. Should be between 1 and 30."},
+ {MgmtSrvr::INVALID_ERROR_NUMBER, "Invalid error number. Should be >= 0."},
+ {MgmtSrvr::INVALID_TRACE_NUMBER, "Invalid trace number."},
+ {MgmtSrvr::NOT_IMPLEMENTED, "Not implemented."},
+ {MgmtSrvr::INVALID_BLOCK_NAME, "Invalid block name"},
+
+ {MgmtSrvr::CONFIG_PARAM_NOT_EXIST,
+ "The configuration parameter does not exist for the process type."},
+ {MgmtSrvr::CONFIG_PARAM_NOT_UPDATEABLE,
+ "The configuration parameter is not possible to update."},
+ {MgmtSrvr::VALUE_WRONG_FORMAT_INT_EXPECTED,
+ "Incorrect value. Expected integer."},
+ {MgmtSrvr::VALUE_TOO_LOW, "Value is too low."},
+ {MgmtSrvr::VALUE_TOO_HIGH, "Value is too high."},
+ {MgmtSrvr::VALUE_WRONG_FORMAT_BOOL_EXPECTED,
+ "Incorrect value. Expected TRUE or FALSE."},
+
+ {MgmtSrvr::CONFIG_FILE_OPEN_WRITE_ERROR,
+ "Could not open configuration file for writing."},
+ {MgmtSrvr::CONFIG_FILE_OPEN_READ_ERROR,
+ "Could not open configuration file for reading."},
+ {MgmtSrvr::CONFIG_FILE_WRITE_ERROR,
+ "Write error when writing configuration file."},
+ {MgmtSrvr::CONFIG_FILE_READ_ERROR,
+ "Read error when reading configuration file."},
+ {MgmtSrvr::CONFIG_FILE_CLOSE_ERROR, "Could not close configuration file."},
+
+ {MgmtSrvr::CONFIG_CHANGE_REFUSED_BY_RECEIVER,
+ "The change was refused by the receiving process."},
+ {MgmtSrvr::COULD_NOT_SYNC_CONFIG_CHANGE_AGAINST_PHYSICAL_MEDIUM,
+ "The change could not be synced against physical medium."},
+ {MgmtSrvr::CONFIG_FILE_CHECKSUM_ERROR,
+ "The config file is corrupt. Checksum error."},
+ {MgmtSrvr::NOT_POSSIBLE_TO_SEND_CONFIG_UPDATE_TO_PROCESS_TYPE,
+ "It is not possible to send an update of a configuration variable "
+ "to this kind of process."},
+ {5026, "Node shutdown in progress" },
+ {5027, "System shutdown in progress" },
+ {5028, "Node shutdown would cause system crash" },
+ {5029, "Only one shutdown at a time is possible via mgm server" },
+ {5060, "Operation not allowed in single user mode." },
+ {5061, "DB is not in single user mode." },
+ {5062, "The specified node is not an API node." },
+ {5063,
+ "Cannot enter single user mode. DB nodes in inconsistent startlevel."},
+ {MgmtSrvr::NO_CONTACT_WITH_DB_NODES, "No contact with database nodes" }
+};
+
+int MgmtSrvr::translateStopRef(Uint32 errCode)
+{
+ switch(errCode){
+ case StopRef::NodeShutdownInProgress:
+ return 5026;
+ break;
+ case StopRef::SystemShutdownInProgress:
+ return 5027;
+ break;
+ case StopRef::NodeShutdownWouldCauseSystemCrash:
+ return 5028;
+ break;
+ }
+ return 4999;
+}
+
+static int noOfErrorCodes = sizeof(errorTable) / sizeof(ErrorItem);
+
+int
+MgmtSrvr::getNodeCount(enum ndb_mgm_node_type type) const
+{
+ int count = 0;
+ NodeId nodeId = 0;
+
+ while (getNextNodeId(&nodeId, type)) {
+ count++;
+ }
+ return count;
+}
+
+int
+MgmtSrvr::getPort() const
+{
+ if(NdbMutex_Lock(m_configMutex))
+ return 0;
+
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_NODE);
+
+ if(iter.find(CFG_NODE_ID, getOwnNodeId()) != 0){
+ ndbout << "Could not retrieve configuration for Node "
+ << getOwnNodeId() << " in config file." << endl
+ << "Have you set correct NodeId for this node?" << endl;
+ NdbMutex_Unlock(m_configMutex);
+ return 0;
+ }
+
+ unsigned type;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0 ||
+ type != NODE_TYPE_MGM){
+ ndbout << "Local node id " << getOwnNodeId()
+ << " is not defined as management server" << endl
+ << "Have you set correct NodeId for this node?" << endl;
+ NdbMutex_Unlock(m_configMutex);
+ return 0;
+ }
+
+ Uint32 port = 0;
+ if(iter.get(CFG_MGM_PORT, &port) != 0){
+ ndbout << "Could not find PortNumber in the configuration file." << endl;
+ NdbMutex_Unlock(m_configMutex);
+ return 0;
+ }
+
+ NdbMutex_Unlock(m_configMutex);
+
+ return port;
+}
+
+/* Constructor */
+int MgmtSrvr::init()
+{
+ if ( _ownNodeId > 0)
+ return 0;
+ return -1;
+}
+
+MgmtSrvr::MgmtSrvr(SocketServer *socket_server,
+ const char *config_filename,
+ const char *connect_string) :
+ _blockNumber(1), // Hard coded block number since it makes it easy to send
+ // signals to other management servers.
+ m_socket_server(socket_server),
+ _ownReference(0),
+ theSignalIdleList(NULL),
+ theWaitState(WAIT_SUBSCRIBE_CONF),
+ m_event_listner(this)
+{
+
+ DBUG_ENTER("MgmtSrvr::MgmtSrvr");
+
+ _ownNodeId= 0;
+
+ _config = NULL;
+
+ _isStopThread = false;
+ _logLevelThread = NULL;
+ _logLevelThreadSleep = 500;
+ m_signalRecvThread = NULL;
+
+ theFacade = 0;
+
+ m_newConfig = NULL;
+ if (config_filename)
+ m_configFilename.assign(config_filename);
+ else
+ m_configFilename.assign("config.ini");
+
+ m_nextConfigGenerationNumber = 0;
+
+ m_config_retriever= new ConfigRetriever(connect_string,
+ NDB_VERSION, NDB_MGM_NODE_TYPE_MGM);
+ // if connect_string explicitly given or
+ // no config filename is given then
+ // first try to allocate nodeid from another management server
+ if ((connect_string || config_filename == NULL) &&
+ (m_config_retriever->do_connect(0,0,0) == 0))
+ {
+ int tmp_nodeid= 0;
+ tmp_nodeid= m_config_retriever->allocNodeId(0 /*retry*/,0 /*delay*/);
+ if (tmp_nodeid == 0)
+ {
+ ndbout_c(m_config_retriever->getErrorString());
+ exit(-1);
+ }
+ // read config from other managent server
+ _config= fetchConfig();
+ if (_config == 0)
+ {
+ ndbout << m_config_retriever->getErrorString() << endl;
+ exit(-1);
+ }
+ _ownNodeId= tmp_nodeid;
+ }
+
+ if (_ownNodeId == 0)
+ {
+ // read config locally
+ _config= readConfig();
+ if (_config == 0) {
+ ndbout << "Unable to read config file" << endl;
+ exit(-1);
+ }
+ }
+
+ theMgmtWaitForResponseCondPtr = NdbCondition_Create();
+
+ m_configMutex = NdbMutex_Create();
+
+ /**
+ * Fill the nodeTypes array
+ */
+ for(Uint32 i = 0; i<MAX_NODES; i++) {
+ nodeTypes[i] = (enum ndb_mgm_node_type)-1;
+ m_connect_address[i].s_addr= 0;
+ }
+
+ {
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_NODE);
+
+ for(iter.first(); iter.valid(); iter.next()){
+ unsigned type, id;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0)
+ continue;
+
+ if(iter.get(CFG_NODE_ID, &id) != 0)
+ continue;
+
+ MGM_REQUIRE(id < MAX_NODES);
+
+ switch(type){
+ case NODE_TYPE_DB:
+ nodeTypes[id] = NDB_MGM_NODE_TYPE_NDB;
+ break;
+ case NODE_TYPE_API:
+ nodeTypes[id] = NDB_MGM_NODE_TYPE_API;
+ break;
+ case NODE_TYPE_MGM:
+ nodeTypes[id] = NDB_MGM_NODE_TYPE_MGM;
+ break;
+ case NODE_TYPE_REP:
+ nodeTypes[id] = NDB_MGM_NODE_TYPE_REP;
+ break;
+ case NODE_TYPE_EXT_REP:
+ default:
+ break;
+ }
+ }
+ }
+
+ _props = NULL;
+ BaseString error_string;
+
+ if ((m_node_id_mutex = NdbMutex_Create()) == 0)
+ {
+ ndbout << "mutex creation failed line = " << __LINE__ << endl;
+ exit(-1);
+ }
+
+ if (_ownNodeId == 0) // we did not get node id from other server
+ {
+ NodeId tmp= m_config_retriever->get_configuration_nodeid();
+
+ if (!alloc_node_id(&tmp, NDB_MGM_NODE_TYPE_MGM,
+ 0, 0, error_string)){
+ ndbout << "Unable to obtain requested nodeid: "
+ << error_string.c_str() << endl;
+ exit(-1);
+ }
+ _ownNodeId = tmp;
+ }
+
+ {
+ DBUG_PRINT("info", ("verifyConfig"));
+ if (!m_config_retriever->verifyConfig(_config->m_configValues,
+ _ownNodeId))
+ {
+ ndbout << m_config_retriever->getErrorString() << endl;
+ exit(-1);
+ }
+ }
+
+ // Setup clusterlog as client[0] in m_event_listner
+ {
+ Ndb_mgmd_event_service::Event_listener se;
+ se.m_socket = NDB_INVALID_SOCKET;
+ for(size_t t = 0; t<LogLevel::LOGLEVEL_CATEGORIES; t++){
+ se.m_logLevel.setLogLevel((LogLevel::EventCategory)t, 7);
+ }
+ se.m_logLevel.setLogLevel(LogLevel::llError, 15);
+ se.m_logLevel.setLogLevel(LogLevel::llConnection, 8);
+ se.m_logLevel.setLogLevel(LogLevel::llBackup, 15);
+ m_event_listner.m_clients.push_back(se);
+ m_event_listner.m_logLevel = se.m_logLevel;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+//****************************************************************************
+//****************************************************************************
+bool
+MgmtSrvr::check_start()
+{
+ if (_config == 0) {
+ DEBUG("MgmtSrvr.cpp: _config is NULL.");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+MgmtSrvr::start(BaseString &error_string)
+{
+ if (_props == NULL) {
+ if (!check_start()) {
+ error_string.append("MgmtSrvr.cpp: check_start() failed.");
+ return false;
+ }
+ }
+ theFacade= TransporterFacade::theFacadeInstance
+ = new TransporterFacade();
+
+ if(theFacade == 0) {
+ DEBUG("MgmtSrvr.cpp: theFacade is NULL.");
+ error_string.append("MgmtSrvr.cpp: theFacade is NULL.");
+ return false;
+ }
+ if ( theFacade->start_instance
+ (_ownNodeId, (ndb_mgm_configuration*)_config->m_configValues) < 0) {
+ DEBUG("MgmtSrvr.cpp: TransporterFacade::start_instance < 0.");
+ return false;
+ }
+
+ MGM_REQUIRE(_blockNumber == 1);
+
+ // Register ourself at TransporterFacade to be able to receive signals
+ // and to be notified when a database process has died.
+ _blockNumber = theFacade->open(this,
+ signalReceivedNotification,
+ nodeStatusNotification);
+
+ if(_blockNumber == -1){
+ DEBUG("MgmtSrvr.cpp: _blockNumber is -1.");
+ error_string.append("MgmtSrvr.cpp: _blockNumber is -1.");
+ theFacade->stop_instance();
+ theFacade = 0;
+ return false;
+ }
+
+ TransporterRegistry *reg = theFacade->get_registry();
+ for(unsigned int i=0;i<reg->m_transporter_interface.size();i++) {
+ BaseString msg;
+ DBUG_PRINT("info",("Setting dynamic port %d->%d : %d",
+ reg->get_localNodeId(),
+ reg->m_transporter_interface[i].m_remote_nodeId,
+ reg->m_transporter_interface[i].m_s_service_port
+ )
+ );
+ int res = setConnectionDbParameter((int)reg->get_localNodeId(),
+ (int)reg->m_transporter_interface[i]
+ .m_remote_nodeId,
+ (int)CFG_CONNECTION_SERVER_PORT,
+ reg->m_transporter_interface[i]
+ .m_s_service_port,
+ msg);
+ DBUG_PRINT("info",("Set result: %d: %s",res,msg.c_str()));
+ }
+
+
+ _ownReference = numberToRef(_blockNumber, _ownNodeId);
+
+ startEventLog();
+ // Set the initial confirmation count for subscribe requests confirm
+ // from NDB nodes in the cluster.
+ //
+ // Loglevel thread
+ _logLevelThread = NdbThread_Create(logLevelThread_C,
+ (void**)this,
+ 32768,
+ "MgmtSrvr_Loglevel",
+ NDB_THREAD_PRIO_LOW);
+
+ m_signalRecvThread = NdbThread_Create(signalRecvThread_C,
+ (void **)this,
+ 32768,
+ "MgmtSrvr_Service",
+ NDB_THREAD_PRIO_LOW);
+
+ return true;
+}
+
+
+//****************************************************************************
+//****************************************************************************
+MgmtSrvr::~MgmtSrvr()
+{
+ while (theSignalIdleList != NULL) {
+ freeSignal();
+ }
+
+ if(theFacade != 0){
+ theFacade->stop_instance();
+ theFacade = 0;
+ }
+
+ stopEventLog();
+
+ NdbMutex_Destroy(m_node_id_mutex);
+ NdbCondition_Destroy(theMgmtWaitForResponseCondPtr);
+ NdbMutex_Destroy(m_configMutex);
+
+ if(m_newConfig != NULL)
+ free(m_newConfig);
+
+ if(_config != NULL)
+ delete _config;
+
+ // End set log level thread
+ void* res = 0;
+ _isStopThread = true;
+
+ if (_logLevelThread != NULL) {
+ NdbThread_WaitFor(_logLevelThread, &res);
+ NdbThread_Destroy(&_logLevelThread);
+ }
+
+ if (m_signalRecvThread != NULL) {
+ NdbThread_WaitFor(m_signalRecvThread, &res);
+ NdbThread_Destroy(&m_signalRecvThread);
+ }
+ if (m_config_retriever)
+ delete m_config_retriever;
+}
+
+//****************************************************************************
+//****************************************************************************
+
+int MgmtSrvr::okToSendTo(NodeId processId, bool unCond)
+{
+ if(processId == 0)
+ return 0;
+
+ if (getNodeType(processId) != NDB_MGM_NODE_TYPE_NDB)
+ return WRONG_PROCESS_TYPE;
+
+ // Check if we have contact with it
+ if(unCond){
+ if(theFacade->theClusterMgr->getNodeInfo(processId).connected)
+ return 0;
+ return NO_CONTACT_WITH_PROCESS;
+ }
+ if (theFacade->get_node_alive(processId) == 0) {
+ return NO_CONTACT_WITH_PROCESS;
+ } else {
+ return 0;
+ }
+}
+
+/*****************************************************************************
+ * Starting and stopping database nodes
+ ****************************************************************************/
+
+int
+MgmtSrvr::start(int processId)
+{
+ int result;
+
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ StartOrd* const startOrd = CAST_PTR(StartOrd, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_START_ORD, StartOrd::SignalLength);
+
+ startOrd->restartInfo = 0;
+
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * Restart one database node
+ */
+int
+MgmtSrvr::restartNode(int processId, bool nostart,
+ bool initalStart, bool abort,
+ StopCallback callback, void * anyData)
+{
+ int result;
+
+ if(m_stopRec.singleUserMode)
+ return 5060;
+
+ if(m_stopRec.inUse){
+ return 5029;
+ }
+
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ StopReq* const stopReq = CAST_PTR(StopReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, NDBCNTR, GSN_STOP_REQ, StopReq::SignalLength);
+
+ stopReq->requestInfo = 0;
+ StopReq::setSystemStop(stopReq->requestInfo, false);
+ StopReq::setPerformRestart(stopReq->requestInfo, true);
+ StopReq::setNoStart(stopReq->requestInfo, nostart);
+ StopReq::setInitialStart(stopReq->requestInfo, initalStart);
+ StopReq::setStopAbort(stopReq->requestInfo, abort);
+ stopReq->singleuser = 0;
+ stopReq->apiTimeout = 5000;
+ stopReq->transactionTimeout = 1000;
+ stopReq->readOperationTimeout = 1000;
+ stopReq->operationTimeout = 1000;
+ stopReq->senderData = 12;
+ stopReq->senderRef = _ownReference;
+
+ m_stopRec.singleUserMode = false;
+ m_stopRec.sentCount = 1;
+ m_stopRec.reply = 0;
+ m_stopRec.nodeId = processId;
+ m_stopRec.anyData = anyData;
+ m_stopRec.callback = callback;
+ m_stopRec.inUse = true;
+
+ if(callback == NULL){
+ Uint32 timeOut = 0;
+ timeOut += stopReq->apiTimeout;
+ timeOut += stopReq->transactionTimeout;
+ timeOut += stopReq->readOperationTimeout;
+ timeOut += stopReq->operationTimeout;
+ timeOut *= 3;
+ result = sendRecSignal(processId, WAIT_STOP, signal, true, timeOut);
+ } else {
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ }
+
+ if (result == -1) {
+ m_stopRec.inUse = false;
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ if(callback == 0){
+ m_stopRec.inUse = false;
+ return m_stopRec.reply;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Restart all database nodes
+ */
+int
+MgmtSrvr::restart(bool nostart, bool initalStart, bool abort,
+ int * stopCount, StopCallback callback, void * anyData)
+{
+ if(m_stopRec.singleUserMode)
+ return 5060;
+
+ if(m_stopRec.inUse){
+ return 5029;
+ }
+
+ m_stopRec.singleUserMode = false;
+ m_stopRec.sentCount = 0;
+ m_stopRec.reply = 0;
+ m_stopRec.nodeId = 0;
+ m_stopRec.anyData = anyData;
+ m_stopRec.callback = callback;
+ m_stopRec.inUse = true;
+
+ /**
+ * Restart all database nodes into idle ("no-started") state
+ */
+ Uint32 timeOut = 0;
+ NodeId nodeId = 0;
+ NodeBitmask nodes;
+ while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){
+ if(okToSendTo(nodeId, true) == 0){
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ StopReq* const stopReq = CAST_PTR(StopReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, NDBCNTR, GSN_STOP_REQ,
+ StopReq::SignalLength);
+
+ stopReq->requestInfo = 0;
+ stopReq->singleuser = 0;
+ StopReq::setSystemStop(stopReq->requestInfo, true);
+ StopReq::setPerformRestart(stopReq->requestInfo, true);
+ if (callback == 0) {
+ // Start node in idle ("no-started") state
+ StopReq::setNoStart(stopReq->requestInfo, 1);
+ } else {
+ StopReq::setNoStart(stopReq->requestInfo, nostart);
+ }
+ StopReq::setInitialStart(stopReq->requestInfo, initalStart);
+ StopReq::setStopAbort(stopReq->requestInfo, abort);
+
+ stopReq->apiTimeout = 5000;
+ stopReq->transactionTimeout = 1000;
+ stopReq->readOperationTimeout = 1000;
+ stopReq->operationTimeout = 1000;
+ stopReq->senderData = 12;
+ stopReq->senderRef = _ownReference;
+
+ timeOut += stopReq->apiTimeout;
+ timeOut += stopReq->transactionTimeout;
+ timeOut += stopReq->readOperationTimeout;
+ timeOut += stopReq->operationTimeout;
+ timeOut *= 3;
+
+ m_stopRec.sentCount++;
+ int res;
+ if(callback == 0){
+ res = sendSignal(nodeId, WAIT_STOP, signal, true);
+ } else {
+ res = sendSignal(nodeId, NO_WAIT, signal, true);
+ }
+
+ if(res != -1){
+ nodes.set(nodeId);
+ }
+ }
+ }
+
+ if(stopCount != 0){
+ * stopCount = m_stopRec.sentCount;
+ }
+
+ if(m_stopRec.sentCount == 0){
+ m_stopRec.inUse = false;
+ return 0;
+ }
+
+ if(callback != 0){
+ return 0;
+ }
+
+ theFacade->lock_mutex();
+ int waitTime = timeOut/m_stopRec.sentCount;
+ if (receiveOptimisedResponse(waitTime) != 0) {
+ m_stopRec.inUse = false;
+ return -1;
+ }
+
+ /**
+ * Here all nodes were correctly stopped,
+ * so we wait for all nodes to be contactable
+ */
+ nodeId = 0;
+ NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime;
+ while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB) && nodes.get(nodeId)) {
+ enum ndb_mgm_node_status s;
+ s = NDB_MGM_NODE_STATUS_NO_CONTACT;
+ while (s != NDB_MGM_NODE_STATUS_NOT_STARTED && waitTime > 0) {
+ Uint32 startPhase = 0, version = 0, dynamicId = 0, nodeGroup = 0;
+ Uint32 connectCount = 0;
+ bool system;
+ status(nodeId, &s, &version, &startPhase,
+ &system, &dynamicId, &nodeGroup, &connectCount);
+ NdbSleep_MilliSleep(100);
+ waitTime = (maxTime - NdbTick_CurrentMillisecond());
+ }
+ }
+
+ if(nostart){
+ m_stopRec.inUse = false;
+ return 0;
+ }
+
+ /**
+ * Now we start all database nodes (i.e. we make them non-idle)
+ * We ignore the result we get from the start command.
+ */
+ nodeId = 0;
+ while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB) && nodes.get(nodeId)) {
+ int result;
+ result = start(nodeId);
+ DEBUG("Starting node " << nodeId << " with result " << result);
+ /**
+ * Errors from this call are deliberately ignored.
+ * Maybe the user only wanted to restart a subset of the nodes.
+ * It is also easy for the user to check which nodes have
+ * started and which nodes have not.
+ *
+ * if (result != 0) {
+ * m_stopRec.inUse = false;
+ * return result;
+ * }
+ */
+ }
+
+ m_stopRec.inUse = false;
+ return 0;
+}
+
+/*****************************************************************************
+ * Version handling
+ *****************************************************************************/
+
+int
+MgmtSrvr::versionNode(int processId, bool abort,
+ VersionCallback callback, void * anyData)
+{
+ int version;
+
+ if(m_versionRec.inUse)
+ return OPERATION_IN_PROGRESS;
+
+ m_versionRec.callback = callback;
+ m_versionRec.inUse = true ;
+
+ if (getOwnNodeId() == processId)
+ {
+ version= NDB_VERSION;
+ }
+ else if (getNodeType(processId) == NDB_MGM_NODE_TYPE_NDB)
+ {
+ ClusterMgr::Node node= theFacade->theClusterMgr->getNodeInfo(processId);
+ if(node.connected)
+ version= node.m_info.m_version;
+ else
+ version= 0;
+ }
+ else if (getNodeType(processId) == NDB_MGM_NODE_TYPE_API ||
+ getNodeType(processId) == NDB_MGM_NODE_TYPE_MGM)
+ {
+ return sendVersionReq(processId);
+ }
+ else
+ version= 0;
+
+ if(m_versionRec.callback != 0)
+ m_versionRec.callback(processId, version, this,0);
+ m_versionRec.inUse = false ;
+
+ m_versionRec.version[processId]= version;
+
+ return 0;
+}
+
+int
+MgmtSrvr::sendVersionReq(int processId)
+{
+ Uint32 ndbnode=0;
+ int result;
+ for(Uint32 i = 0; i<MAX_NODES; i++) {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_NDB) {
+ if(okToSendTo(i, true) == 0)
+ {
+ ndbnode = i;
+ break;
+ }
+ }
+ }
+
+ if (ndbnode == 0) {
+ m_versionRec.inUse = false;
+ if(m_versionRec.callback != 0)
+ m_versionRec.callback(processId, 0, this,0);
+ return NO_CONTACT_WITH_CLUSTER;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ m_versionRec.inUse = false;
+ if(m_versionRec.callback != 0)
+ m_versionRec.callback(processId, 0, this,0);
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+ ApiVersionReq* req = CAST_PTR(ApiVersionReq, signal->getDataPtrSend());
+ req->senderRef = _ownReference;
+ req->nodeId = processId;
+
+ signal->set(TestOrd::TraceAPI, QMGR, GSN_API_VERSION_REQ,
+ ApiVersionReq::SignalLength);
+
+
+ // if(m_versionRec.callback == 0){
+ Uint32 timeOut = 0;
+ timeOut = 10000;
+ result = sendRecSignal(ndbnode, WAIT_VERSION, signal, true, timeOut);
+ //} else {
+ //result = sendSignal(processId, NO_WAIT, signal, true);
+ // }
+
+ if (result == -1) {
+ m_versionRec.inUse = false;
+ if(m_versionRec.callback != 0)
+ m_versionRec.callback(processId, 0, this,0);
+ m_versionRec.version[processId] = 0;
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ m_versionRec.inUse = false;
+ return 0;
+}
+
+int
+MgmtSrvr::version(int * stopCount, bool abort,
+ VersionCallback callback, void * anyData)
+{
+ ClusterMgr::Node node;
+ int version;
+
+ if(m_versionRec.inUse)
+ return 1;
+
+ m_versionRec.callback = callback;
+ m_versionRec.inUse = true ;
+ Uint32 i;
+ for(i = 0; i<MAX_NODES; i++) {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_MGM) {
+ m_versionRec.callback(i, NDB_VERSION, this,0);
+ }
+ }
+ for(i = 0; i<MAX_NODES; i++) {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_NDB) {
+ node = theFacade->theClusterMgr->getNodeInfo(i);
+ version = node.m_info.m_version;
+ if(theFacade->theClusterMgr->getNodeInfo(i).connected)
+ m_versionRec.callback(i, version, this,0);
+ else
+ m_versionRec.callback(i, 0, this,0);
+
+ }
+ }
+ for(i = 0; i<MAX_NODES; i++) {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_API) {
+ return sendVersionReq(i);
+ }
+ }
+
+ return 0;
+}
+
+int
+MgmtSrvr::stopNode(int processId, bool abort, StopCallback callback,
+ void * anyData)
+
+{
+ if(m_stopRec.singleUserMode)
+ return 5060;
+
+ if(m_stopRec.inUse)
+ return 5029;
+
+ int result;
+
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ StopReq* const stopReq = CAST_PTR(StopReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, NDBCNTR, GSN_STOP_REQ, StopReq::SignalLength);
+
+ stopReq->requestInfo = 0;
+ stopReq->singleuser = 0;
+ StopReq::setPerformRestart(stopReq->requestInfo, false);
+ StopReq::setSystemStop(stopReq->requestInfo, false);
+ StopReq::setStopAbort(stopReq->requestInfo, abort);
+
+ stopReq->apiTimeout = 5000;
+ stopReq->transactionTimeout = 1000;
+ stopReq->readOperationTimeout = 1000;
+ stopReq->operationTimeout = 1000;
+ stopReq->senderData = 12;
+ stopReq->senderRef = _ownReference;
+
+ m_stopRec.sentCount = 1;
+ m_stopRec.reply = 0;
+ m_stopRec.nodeId = processId;
+ m_stopRec.anyData = anyData;
+ m_stopRec.callback = callback;
+ m_stopRec.inUse = true;
+
+ if(callback == NULL){
+ Uint32 timeOut = 0;
+ timeOut += stopReq->apiTimeout;
+ timeOut += stopReq->transactionTimeout;
+ timeOut += stopReq->readOperationTimeout;
+ timeOut += stopReq->operationTimeout;
+ timeOut *= 3;
+ result = sendRecSignal(processId, WAIT_STOP, signal, true, timeOut);
+ } else {
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ }
+
+ if (result == -1) {
+ m_stopRec.inUse = false;
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ if(callback == 0){
+ m_stopRec.inUse = false;
+ return m_stopRec.reply;
+ } else {
+ return 0;
+ }
+}
+
+int
+MgmtSrvr::stop(int * stopCount, bool abort, StopCallback callback,
+ void * anyData)
+{
+ if(m_stopRec.singleUserMode)
+ return 5060;
+
+ if(m_stopRec.inUse){
+ return 5029;
+ }
+
+ m_stopRec.singleUserMode = false;
+ m_stopRec.sentCount = 0;
+ m_stopRec.reply = 0;
+ m_stopRec.nodeId = 0;
+ m_stopRec.anyData = anyData;
+ m_stopRec.callback = callback;
+ m_stopRec.inUse = true;
+
+ NodeId nodeId = 0;
+ Uint32 timeOut = 0;
+ while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){
+ if(okToSendTo(nodeId, true) == 0){
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ StopReq* const stopReq = CAST_PTR(StopReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, NDBCNTR, GSN_STOP_REQ,
+ StopReq::SignalLength);
+
+ stopReq->requestInfo = 0;
+ stopReq->singleuser = 0;
+ StopReq::setSystemStop(stopReq->requestInfo, true);
+ StopReq::setPerformRestart(stopReq->requestInfo, false);
+ StopReq::setStopAbort(stopReq->requestInfo, abort);
+
+ stopReq->apiTimeout = 5000;
+ stopReq->transactionTimeout = 1000;
+ stopReq->readOperationTimeout = 1000;
+ stopReq->operationTimeout = 1000;
+ stopReq->senderData = 12;
+ stopReq->senderRef = _ownReference;
+
+ timeOut += stopReq->apiTimeout;
+ timeOut += stopReq->transactionTimeout;
+ timeOut += stopReq->readOperationTimeout;
+ timeOut += stopReq->operationTimeout;
+ timeOut *= 3;
+
+ m_stopRec.sentCount++;
+ if(callback == 0)
+ sendSignal(nodeId, WAIT_STOP, signal, true);
+ else
+ sendSignal(nodeId, NO_WAIT, signal, true);
+ }
+ }
+
+ if(stopCount != 0)
+ * stopCount = m_stopRec.sentCount;
+
+ if(m_stopRec.sentCount > 0){
+ if(callback == 0){
+ theFacade->lock_mutex();
+ receiveOptimisedResponse(timeOut / m_stopRec.sentCount);
+ } else {
+ return 0;
+ }
+ }
+
+ m_stopRec.inUse = false;
+ return m_stopRec.reply;
+}
+
+/*****************************************************************************
+ * Single user mode
+ ****************************************************************************/
+
+int
+MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId,
+ EnterSingleCallback callback, void * anyData)
+{
+ if(m_stopRec.singleUserMode) {
+ return 5060;
+ }
+
+ if (getNodeType(singleUserNodeId) != NDB_MGM_NODE_TYPE_API) {
+ return 5062;
+ }
+ ClusterMgr::Node node;
+
+ for(Uint32 i = 0; i<MAX_NODES; i++) {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_NDB) {
+ node = theFacade->theClusterMgr->getNodeInfo(i);
+ if((node.m_state.startLevel != NodeState::SL_STARTED) &&
+ (node.m_state.startLevel != NodeState::SL_NOTHING)) {
+ return 5063;
+ }
+ }
+ }
+
+ if(m_stopRec.inUse){
+ return 5029;
+ }
+
+ if(singleUserNodeId == 0)
+ return 1;
+ m_stopRec.singleUserMode = true;
+ m_stopRec.sentCount = 0;
+ m_stopRec.reply = 0;
+ m_stopRec.nodeId = 0;
+ m_stopRec.anyData = anyData;
+ m_stopRec.callback = callback;
+ m_stopRec.inUse = true;
+
+ NodeId nodeId = 0;
+ Uint32 timeOut = 0;
+ while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){
+ if(okToSendTo(nodeId, true) == 0){
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ StopReq* const stopReq = CAST_PTR(StopReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, NDBCNTR, GSN_STOP_REQ,
+ StopReq::SignalLength);
+
+ stopReq->requestInfo = 0;
+ stopReq->singleuser = 1;
+ stopReq->singleUserApi = singleUserNodeId;
+ StopReq::setSystemStop(stopReq->requestInfo, false);
+ StopReq::setPerformRestart(stopReq->requestInfo, false);
+ StopReq::setStopAbort(stopReq->requestInfo, false);
+
+ stopReq->apiTimeout = 5000;
+ stopReq->transactionTimeout = 1000;
+ stopReq->readOperationTimeout = 1000;
+ stopReq->operationTimeout = 1000;
+ stopReq->senderData = 12;
+ stopReq->senderRef = _ownReference;
+ timeOut += stopReq->apiTimeout;
+ timeOut += stopReq->transactionTimeout;
+ timeOut += stopReq->readOperationTimeout;
+ timeOut += stopReq->operationTimeout;
+ timeOut *= 3;
+
+ m_stopRec.sentCount++;
+ if(callback == 0)
+ sendSignal(nodeId, WAIT_STOP, signal, true);
+ else
+ sendSignal(nodeId, NO_WAIT, signal, true);
+ }
+ }
+
+ if(stopCount != 0)
+ * stopCount = m_stopRec.sentCount;
+
+ if(callback == 0){
+ m_stopRec.inUse = false;
+ return 0;
+ // return m_stopRec.reply;
+ } else {
+ return 0;
+ }
+
+ m_stopRec.inUse = false;
+ return m_stopRec.reply;
+}
+
+int
+MgmtSrvr::exitSingleUser(int * stopCount, bool abort,
+ ExitSingleCallback callback, void * anyData)
+{
+ m_stopRec.sentCount = 0;
+ m_stopRec.reply = 0;
+ m_stopRec.nodeId = 0;
+ m_stopRec.anyData = anyData;
+ m_stopRec.callback = callback;
+ m_stopRec.inUse = true;
+
+ NodeId nodeId = 0;
+ while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){
+ if(okToSendTo(nodeId, true) == 0){
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ ResumeReq* const resumeReq =
+ CAST_PTR(ResumeReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, NDBCNTR, GSN_RESUME_REQ,
+ StopReq::SignalLength);
+ resumeReq->senderData = 12;
+ resumeReq->senderRef = _ownReference;
+
+ m_stopRec.sentCount++;
+ if(callback == 0)
+ sendSignal(nodeId, WAIT_STOP, signal, true);
+ else
+ sendSignal(nodeId, NO_WAIT, signal, true);
+ }
+ }
+
+ m_stopRec.singleUserMode = false;
+
+ if(stopCount != 0)
+ * stopCount = m_stopRec.sentCount;
+
+
+ if(callback == 0){
+ m_stopRec.inUse = false;
+ return m_stopRec.reply;
+ } else {
+ return 0;
+ }
+
+ m_stopRec.inUse = false;
+ return m_stopRec.reply;
+}
+
+
+/*****************************************************************************
+ * Status
+ ****************************************************************************/
+
+#include <ClusterMgr.hpp>
+
+int
+MgmtSrvr::status(int processId,
+ ndb_mgm_node_status * _status,
+ Uint32 * version,
+ Uint32 * _phase,
+ bool * _system,
+ Uint32 * dynamic,
+ Uint32 * nodegroup,
+ Uint32 * connectCount)
+{
+ if (getNodeType(processId) == NDB_MGM_NODE_TYPE_API ||
+ getNodeType(processId) == NDB_MGM_NODE_TYPE_MGM) {
+ if(versionNode(processId, false,0,0) ==0)
+ * version = m_versionRec.version[processId];
+ else
+ * version = 0;
+ }
+
+ const ClusterMgr::Node node =
+ theFacade->theClusterMgr->getNodeInfo(processId);
+
+ if(!node.connected){
+ * _status = NDB_MGM_NODE_STATUS_NO_CONTACT;
+ return 0;
+ }
+
+ if (getNodeType(processId) == NDB_MGM_NODE_TYPE_NDB) {
+ * version = node.m_info.m_version;
+ }
+
+ * dynamic = node.m_state.dynamicId;
+ * nodegroup = node.m_state.nodeGroup;
+ * connectCount = node.m_info.m_connectCount;
+
+ switch(node.m_state.startLevel){
+ case NodeState::SL_CMVMI:
+ * _status = NDB_MGM_NODE_STATUS_NOT_STARTED;
+ * _phase = 0;
+ return 0;
+ break;
+ case NodeState::SL_STARTING:
+ * _status = NDB_MGM_NODE_STATUS_STARTING;
+ * _phase = node.m_state.starting.startPhase;
+ return 0;
+ break;
+ case NodeState::SL_STARTED:
+ * _status = NDB_MGM_NODE_STATUS_STARTED;
+ * _phase = 0;
+ return 0;
+ break;
+ case NodeState::SL_STOPPING_1:
+ * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN;
+ * _phase = 1;
+ * _system = node.m_state.stopping.systemShutdown != 0;
+ return 0;
+ break;
+ case NodeState::SL_STOPPING_2:
+ * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN;
+ * _phase = 2;
+ * _system = node.m_state.stopping.systemShutdown != 0;
+ return 0;
+ break;
+ case NodeState::SL_STOPPING_3:
+ * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN;
+ * _phase = 3;
+ * _system = node.m_state.stopping.systemShutdown != 0;
+ return 0;
+ break;
+ case NodeState::SL_STOPPING_4:
+ * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN;
+ * _phase = 4;
+ * _system = node.m_state.stopping.systemShutdown != 0;
+ return 0;
+ break;
+ case NodeState::SL_SINGLEUSER:
+ * _status = NDB_MGM_NODE_STATUS_SINGLEUSER;
+ * _phase = 0;
+ return 0;
+ break;
+ default:
+ * _status = NDB_MGM_NODE_STATUS_UNKNOWN;
+ * _phase = 0;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+MgmtSrvr::setEventReportingLevelImpl(int processId,
+ const EventSubscribeReq& ll)
+{
+
+ int result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal signal(_ownReference);
+
+ EventSubscribeReq * dst =
+ CAST_PTR(EventSubscribeReq, signal.getDataPtrSend());
+
+ * dst = ll;
+
+ signal.set(TestOrd::TraceAPI, CMVMI, GSN_EVENT_SUBSCRIBE_REQ,
+ EventSubscribeReq::SignalLength);
+
+ theFacade->lock_mutex();
+ send(&signal, processId, NODE_TYPE_DB);
+ theFacade->unlock_mutex();
+
+ return 0;
+}
+
+//****************************************************************************
+//****************************************************************************
+int
+MgmtSrvr::setNodeLogLevelImpl(int processId, const SetLogLevelOrd & ll)
+{
+ int result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal signal(_ownReference);
+
+ SetLogLevelOrd * dst = CAST_PTR(SetLogLevelOrd, signal.getDataPtrSend());
+
+ * dst = ll;
+
+ signal.set(TestOrd::TraceAPI, CMVMI, GSN_SET_LOGLEVELORD,
+ SetLogLevelOrd::SignalLength);
+
+ theFacade->lock_mutex();
+ theFacade->sendSignalUnCond(&signal, processId);
+ theFacade->unlock_mutex();
+
+ return 0;
+}
+
+int
+MgmtSrvr::send(NdbApiSignal* signal, Uint32 node, Uint32 node_type){
+ Uint32 max = (node == 0) ? MAX_NODES : node + 1;
+
+ for(; node < max; node++){
+ while(nodeTypes[node] != (int)node_type && node < max) node++;
+ if(nodeTypes[node] != (int)node_type)
+ break;
+ theFacade->sendSignalUnCond(signal, node);
+ }
+ return 0;
+}
+
+//****************************************************************************
+//****************************************************************************
+
+int
+MgmtSrvr::insertError(int processId, int errorNo)
+{
+ if (errorNo < 0) {
+ return INVALID_ERROR_NUMBER;
+ }
+
+ int result;
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ TamperOrd* const tamperOrd = CAST_PTR(TamperOrd, signal->getDataPtrSend());
+ tamperOrd->errorNo = errorNo;
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_TAMPER_ORD,
+ TamperOrd::SignalLength);
+
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+
+
+//****************************************************************************
+//****************************************************************************
+
+int
+MgmtSrvr::setTraceNo(int processId, int traceNo)
+{
+ if (traceNo < 0) {
+ return INVALID_TRACE_NUMBER;
+ }
+
+ int result;
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ TestOrd* const testOrd = CAST_PTR(TestOrd, signal->getDataPtrSend());
+ testOrd->clear();
+
+ // Assume TRACE command causes toggling. Not really defined... ? TODO
+ testOrd->setTraceCommand(TestOrd::Toggle,
+ (TestOrd::TraceSpecification)traceNo);
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength);
+
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+//****************************************************************************
+//****************************************************************************
+
+int
+MgmtSrvr::getBlockNumber(const BaseString &blockName)
+{
+ short bno = getBlockNo(blockName.c_str());
+ if(bno != 0)
+ return bno;
+ return -1;
+}
+
+//****************************************************************************
+//****************************************************************************
+
+int
+MgmtSrvr::setSignalLoggingMode(int processId, LogMode mode,
+ const Vector<BaseString>& blocks)
+{
+ int result;
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ // Convert from MgmtSrvr format...
+
+ TestOrd::Command command;
+ if (mode == Off) {
+ command = TestOrd::Off;
+ }
+ else {
+ command = TestOrd::On;
+ }
+
+ TestOrd::SignalLoggerSpecification logSpec;
+ switch (mode) {
+ case In:
+ logSpec = TestOrd::InputSignals;
+ break;
+ case Out:
+ logSpec = TestOrd::OutputSignals;
+ break;
+ case InOut:
+ logSpec = TestOrd::InputOutputSignals;
+ break;
+ case Off:
+ // In MgmtSrvr interface it's just possible to switch off all logging, both
+ // "in" and "out" (this should probably be changed).
+ logSpec = TestOrd::InputOutputSignals;
+ break;
+ default:
+ ndbout_c("Unexpected value %d, MgmtSrvr::setSignalLoggingMode, line %d",
+ (unsigned)mode, __LINE__);
+ assert(false);
+ return -1;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ TestOrd* const testOrd = CAST_PTR(TestOrd, signal->getDataPtrSend());
+ testOrd->clear();
+
+ if (blocks.size() == 0 || blocks[0] == "ALL") {
+ // Logg command for all blocks
+ testOrd->addSignalLoggerCommand(command, logSpec);
+ } else {
+ for(unsigned i = 0; i < blocks.size(); i++){
+ int blockNumber = getBlockNumber(blocks[i]);
+ if (blockNumber == -1) {
+ releaseSignal(signal);
+ return INVALID_BLOCK_NAME;
+ }
+ testOrd->addSignalLoggerCommand(blockNumber, command, logSpec);
+ } // for
+ } // else
+
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength);
+ result = sendSignal(processId, NO_WAIT, signal, true);
+
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+ return 0;
+}
+
+
+/*****************************************************************************
+ * Signal tracing
+ *****************************************************************************/
+int MgmtSrvr::startSignalTracing(int processId)
+{
+ int result;
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+
+ TestOrd* const testOrd = CAST_PTR(TestOrd, signal->getDataPtrSend());
+ testOrd->clear();
+ testOrd->setTestCommand(TestOrd::On);
+
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength);
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+int
+MgmtSrvr::stopSignalTracing(int processId)
+{
+ int result;
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ TestOrd* const testOrd = CAST_PTR(TestOrd, signal->getDataPtrSend());
+ testOrd->clear();
+ testOrd->setTestCommand(TestOrd::Off);
+
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength);
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ * Dump state
+ *****************************************************************************/
+
+int
+MgmtSrvr::dumpState(int processId, const char* args)
+{
+ // Convert the space separeted args
+ // string to an int array
+ Uint32 args_array[25];
+ Uint32 numArgs = 0;
+
+ char buf[10];
+ int b = 0;
+ memset(buf, 0, 10);
+ for (size_t i = 0; i <= strlen(args); i++){
+ if (args[i] == ' ' || args[i] == 0){
+ args_array[numArgs] = atoi(buf);
+ numArgs++;
+ memset(buf, 0, 10);
+ b = 0;
+ } else {
+ buf[b] = args[i];
+ b++;
+ }
+ }
+
+ return dumpState(processId, args_array, numArgs);
+}
+
+int
+MgmtSrvr::dumpState(int processId, const Uint32 args[], Uint32 no)
+{
+ int result;
+
+ result = okToSendTo(processId, true);
+ if (result != 0) {
+ return result;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ const Uint32 len = no > 25 ? 25 : no;
+
+ DumpStateOrd * const dumpOrd =
+ CAST_PTR(DumpStateOrd, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, CMVMI, GSN_DUMP_STATE_ORD, len);
+ for(Uint32 i = 0; i<25; i++){
+ if (i < len)
+ dumpOrd->args[i] = args[i];
+ else
+ dumpOrd->args[i] = 0;
+ }
+
+ result = sendSignal(processId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+
+//****************************************************************************
+//****************************************************************************
+
+const char* MgmtSrvr::getErrorText(int errorCode, char *buf, int buf_sz)
+{
+
+ for (int i = 0; i < noOfErrorCodes; ++i) {
+ if (errorCode == errorTable[i]._errorCode) {
+ BaseString::snprintf(buf, buf_sz, errorTable[i]._errorText);
+ buf[buf_sz-1]= 0;
+ return buf;
+ }
+ }
+
+ ndb_error_string(errorCode, buf, buf_sz);
+ buf[buf_sz-1]= 0;
+
+ return buf;
+}
+
+void
+MgmtSrvr::handleReceivedSignal(NdbApiSignal* signal)
+{
+ // The way of handling a received signal is taken from the Ndb class.
+ int gsn = signal->readSignalNumber();
+
+ switch (gsn) {
+ case GSN_API_VERSION_CONF: {
+ if (theWaitState == WAIT_VERSION) {
+ const ApiVersionConf * const conf =
+ CAST_CONSTPTR(ApiVersionConf, signal->getDataPtr());
+ if(m_versionRec.callback != 0)
+ m_versionRec.callback(conf->nodeId, conf->version, this, 0);
+ else {
+ m_versionRec.version[conf->nodeId]=conf->version;
+ }
+ } else return;
+ theWaitState = NO_WAIT;
+ }
+ break;
+
+ case GSN_EVENT_SUBSCRIBE_CONF:
+ break;
+
+ case GSN_EVENT_REP:
+ eventReport(refToNode(signal->theSendersBlockRef), signal->getDataPtr());
+ break;
+
+ case GSN_STOP_REF:{
+ const StopRef * const ref = CAST_CONSTPTR(StopRef, signal->getDataPtr());
+ const NodeId nodeId = refToNode(signal->theSendersBlockRef);
+ handleStopReply(nodeId, ref->errorCode);
+ return;
+ }
+ break;
+
+ case GSN_BACKUP_CONF:{
+ const BackupConf * const conf =
+ CAST_CONSTPTR(BackupConf, signal->getDataPtr());
+ BackupEvent event;
+ event.Event = BackupEvent::BackupStarted;
+ event.Started.BackupId = conf->backupId;
+ event.Nodes = conf->nodes;
+#ifdef VM_TRACE
+ ndbout_c("Backup master is %d", refToNode(signal->theSendersBlockRef));
+#endif
+ backupCallback(event);
+ }
+ break;
+
+ case GSN_BACKUP_REF:{
+ const BackupRef * const ref =
+ CAST_CONSTPTR(BackupRef, signal->getDataPtr());
+ Uint32 errCode = ref->errorCode;
+ if(ref->errorCode == BackupRef::IAmNotMaster){
+ const Uint32 aNodeId = refToNode(ref->masterRef);
+#ifdef VM_TRACE
+ ndbout_c("I'm not master resending to %d", aNodeId);
+#endif
+ NdbApiSignal aSignal(_ownReference);
+ BackupReq* req = CAST_PTR(BackupReq, aSignal.getDataPtrSend());
+ aSignal.set(TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ,
+ BackupReq::SignalLength);
+ req->senderData = 19;
+ req->backupDataLen = 0;
+
+ int i = theFacade->sendSignalUnCond(&aSignal, aNodeId);
+ if(i == 0){
+ return;
+ }
+ errCode = 5030;
+ }
+ BackupEvent event;
+ event.Event = BackupEvent::BackupFailedToStart;
+ event.FailedToStart.ErrorCode = errCode;
+ backupCallback(event);
+ break;
+ }
+
+ case GSN_BACKUP_ABORT_REP:{
+ const BackupAbortRep * const rep =
+ CAST_CONSTPTR(BackupAbortRep, signal->getDataPtr());
+ BackupEvent event;
+ event.Event = BackupEvent::BackupAborted;
+ event.Aborted.Reason = rep->reason;
+ event.Aborted.BackupId = rep->backupId;
+ backupCallback(event);
+ }
+ break;
+
+ case GSN_BACKUP_COMPLETE_REP:{
+ const BackupCompleteRep * const rep =
+ CAST_CONSTPTR(BackupCompleteRep, signal->getDataPtr());
+ BackupEvent event;
+ event.Event = BackupEvent::BackupCompleted;
+ event.Completed.BackupId = rep->backupId;
+
+ event.Completed.NoOfBytes = rep->noOfBytes;
+ event.Completed.NoOfLogBytes = rep->noOfLogBytes;
+ event.Completed.NoOfRecords = rep->noOfRecords;
+ event.Completed.NoOfLogRecords = rep->noOfLogRecords;
+ event.Completed.stopGCP = rep->stopGCP;
+ event.Completed.startGCP = rep->startGCP;
+ event.Nodes = rep->nodes;
+
+ backupCallback(event);
+ }
+ break;
+
+ case GSN_MGM_LOCK_CONFIG_REP:
+ case GSN_MGM_LOCK_CONFIG_REQ:
+ case GSN_MGM_UNLOCK_CONFIG_REP:
+ case GSN_MGM_UNLOCK_CONFIG_REQ: {
+ m_signalRecvQueue.receive(new NdbApiSignal(*signal));
+ break;
+ }
+
+ default:
+ g_eventLogger.error("Unknown signal received. SignalNumber: "
+ "%i from (%d, %x)",
+ gsn,
+ refToNode(signal->theSendersBlockRef),
+ refToBlock(signal->theSendersBlockRef));
+ }
+
+ if (theWaitState == NO_WAIT) {
+ NdbCondition_Signal(theMgmtWaitForResponseCondPtr);
+ }
+}
+
+/**
+ * A database node was either stopped or there was some error
+ */
+void
+MgmtSrvr::handleStopReply(NodeId nodeId, Uint32 errCode)
+{
+ /**
+ * If we are in single user mode and get a stop reply from a
+ * DB node, then we have had a node crash.
+ * If all DB nodes are gone, and we are still in single user mode,
+ * the set m_stopRec.singleUserMode = false;
+ */
+ if(m_stopRec.singleUserMode) {
+ ClusterMgr::Node node;
+ bool failure = true;
+ for(Uint32 i = 0; i<MAX_NODES; i++) {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_NDB) {
+ node = theFacade->theClusterMgr->getNodeInfo(i);
+ if((node.m_state.startLevel == NodeState::SL_NOTHING))
+ failure = true;
+ else
+ failure = false;
+ }
+ }
+ if(failure) {
+ m_stopRec.singleUserMode = false;
+ }
+ }
+ if(m_stopRec.inUse == false)
+ return;
+
+ if(!(m_stopRec.nodeId == 0 || m_stopRec.nodeId == nodeId))
+ goto error;
+
+ if(m_stopRec.sentCount <= 0)
+ goto error;
+
+ if(!(theWaitState == WAIT_STOP || m_stopRec.callback != 0))
+ goto error;
+
+ if(errCode != 0)
+ m_stopRec.reply = translateStopRef(errCode);
+
+ m_stopRec.sentCount --;
+ if(m_stopRec.sentCount == 0){
+ if(theWaitState == WAIT_STOP){
+ theWaitState = NO_WAIT;
+ NdbCondition_Signal(theMgmtWaitForResponseCondPtr);
+ return;
+ }
+ if(m_stopRec.callback != 0){
+ m_stopRec.inUse = false;
+ StopCallback callback = m_stopRec.callback;
+ m_stopRec.callback = NULL;
+ (* callback)(m_stopRec.nodeId,
+ m_stopRec.anyData,
+ m_stopRec.reply);
+ return;
+ }
+ }
+ return;
+
+ error:
+ if(errCode != 0){
+ g_eventLogger.error("Unexpected signal received. SignalNumber: %i from %d",
+ GSN_STOP_REF, nodeId);
+ }
+}
+
+void
+MgmtSrvr::handleStatus(NodeId nodeId, bool alive)
+{
+ DBUG_ENTER("MgmtSrvr::handleStatus");
+ Uint32 theData[25];
+ theData[1] = nodeId;
+ if (alive) {
+ m_started_nodes.push_back(nodeId);
+ theData[0] = NDB_LE_Connected;
+ } else {
+ handleStopReply(nodeId, 0);
+ theData[0] = NDB_LE_Disconnected;
+ }
+ eventReport(_ownNodeId, theData);
+ DBUG_VOID_RETURN;
+}
+
+//****************************************************************************
+//****************************************************************************
+
+void
+MgmtSrvr::signalReceivedNotification(void* mgmtSrvr,
+ NdbApiSignal* signal,
+ LinearSectionPtr ptr[3])
+{
+ ((MgmtSrvr*)mgmtSrvr)->handleReceivedSignal(signal);
+}
+
+
+//****************************************************************************
+//****************************************************************************
+void
+MgmtSrvr::nodeStatusNotification(void* mgmSrv, Uint32 nodeId,
+ bool alive, bool nfComplete)
+{
+ DBUG_ENTER("MgmtSrvr::nodeStatusNotification");
+ DBUG_PRINT("enter",("nodeid= %d, alive= %d, nfComplete= %d", nodeId, alive, nfComplete));
+ if(!(!alive && nfComplete))
+ ((MgmtSrvr*)mgmSrv)->handleStatus(nodeId, alive);
+ DBUG_VOID_RETURN;
+}
+
+enum ndb_mgm_node_type
+MgmtSrvr::getNodeType(NodeId nodeId) const
+{
+ if(nodeId >= MAX_NODES)
+ return (enum ndb_mgm_node_type)-1;
+
+ return nodeTypes[nodeId];
+}
+
+void
+MgmtSrvr::get_connected_nodes(NodeBitmask &connected_nodes) const
+{
+ if (theFacade && theFacade->theClusterMgr)
+ {
+ for(Uint32 i = 0; i < MAX_NODES; i++)
+ {
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_NDB)
+ {
+ const ClusterMgr::Node &node= theFacade->theClusterMgr->getNodeInfo(i);
+ connected_nodes.bitOR(node.m_state.m_connected_nodes);
+ }
+ }
+ }
+}
+
+bool
+MgmtSrvr::alloc_node_id(NodeId * nodeId,
+ enum ndb_mgm_node_type type,
+ struct sockaddr *client_addr,
+ SOCKET_SIZE_TYPE *client_addr_len,
+ BaseString &error_string)
+{
+ DBUG_ENTER("MgmtSrvr::alloc_node_id");
+ DBUG_PRINT("enter", ("nodeid=%d, type=%d, client_addr=%d",
+ *nodeId, type, client_addr));
+ if (g_no_nodeid_checks) {
+ if (*nodeId == 0) {
+ error_string.appfmt("no-nodeid-checks set in management server.\n"
+ "node id must be set explicitly in connectstring");
+ DBUG_RETURN(false);
+ }
+ DBUG_RETURN(true);
+ }
+ Guard g(m_node_id_mutex);
+ int no_mgm= 0;
+ NodeBitmask connected_nodes(m_reserved_nodes);
+ get_connected_nodes(connected_nodes);
+ {
+ for(Uint32 i = 0; i < MAX_NODES; i++)
+ if (getNodeType(i) == NDB_MGM_NODE_TYPE_MGM)
+ no_mgm++;
+ }
+ bool found_matching_id= false;
+ bool found_matching_type= false;
+ bool found_free_node= false;
+ unsigned id_found= 0;
+ const char *config_hostname= 0;
+ struct in_addr config_addr= {0};
+ int r_config_addr= -1;
+ unsigned type_c= 0;
+
+ if(NdbMutex_Lock(m_configMutex))
+ {
+ error_string.appfmt("unable to lock configuration mutex");
+ return false;
+ }
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_NODE);
+ for(iter.first(); iter.valid(); iter.next()) {
+ unsigned tmp= 0;
+ if(iter.get(CFG_NODE_ID, &tmp)) abort();
+ if (*nodeId && *nodeId != tmp)
+ continue;
+ found_matching_id= true;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type_c)) abort();
+ if(type_c != (unsigned)type)
+ continue;
+ found_matching_type= true;
+ if (connected_nodes.get(tmp))
+ continue;
+ found_free_node= true;
+ if(iter.get(CFG_NODE_HOST, &config_hostname)) abort();
+ if (config_hostname && config_hostname[0] == 0)
+ config_hostname= 0;
+ else if (client_addr) {
+ // check hostname compatability
+ const void *tmp_in= &(((sockaddr_in*)client_addr)->sin_addr);
+ if((r_config_addr= Ndb_getInAddr(&config_addr, config_hostname)) != 0
+ || memcmp(&config_addr, tmp_in, sizeof(config_addr)) != 0) {
+ struct in_addr tmp_addr;
+ if(Ndb_getInAddr(&tmp_addr, "localhost") != 0
+ || memcmp(&tmp_addr, tmp_in, sizeof(config_addr)) != 0) {
+ // not localhost
+#if 0
+ ndbout << "MgmtSrvr::getFreeNodeId compare failed for \""
+ << config_hostname
+ << "\" id=" << tmp << endl;
+#endif
+ continue;
+ }
+ // connecting through localhost
+ // check if config_hostname is local
+ if (!SocketServer::tryBind(0,config_hostname)) {
+ continue;
+ }
+ }
+ } else { // client_addr == 0
+ if (!SocketServer::tryBind(0,config_hostname)) {
+ continue;
+ }
+ }
+ if (*nodeId != 0 ||
+ type != NDB_MGM_NODE_TYPE_MGM ||
+ no_mgm == 1) { // any match is ok
+
+ if (config_hostname == 0 &&
+ *nodeId == 0 &&
+ type != NDB_MGM_NODE_TYPE_MGM)
+ {
+ if (!id_found) // only set if not set earlier
+ id_found= tmp;
+ continue; /* continue looking for a nodeid with specified
+ * hostname
+ */
+ }
+ assert(id_found == 0);
+ id_found= tmp;
+ break;
+ }
+ if (id_found) { // mgmt server may only have one match
+ error_string.appfmt("Ambiguous node id's %d and %d.\n"
+ "Suggest specifying node id in connectstring,\n"
+ "or specifying unique host names in config file.",
+ id_found, tmp);
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(false);
+ }
+ if (config_hostname == 0) {
+ error_string.appfmt("Ambiguity for node id %d.\n"
+ "Suggest specifying node id in connectstring,\n"
+ "or specifying unique host names in config file,\n"
+ "or specifying just one mgmt server in config file.",
+ tmp);
+ DBUG_RETURN(false);
+ }
+ id_found= tmp; // mgmt server matched, check for more matches
+ }
+ NdbMutex_Unlock(m_configMutex);
+
+ if (id_found)
+ {
+ *nodeId= id_found;
+ DBUG_PRINT("info", ("allocating node id %d",*nodeId));
+ {
+ int r= 0;
+ if (client_addr)
+ m_connect_address[id_found]=
+ ((struct sockaddr_in *)client_addr)->sin_addr;
+ else if (config_hostname)
+ r= Ndb_getInAddr(&(m_connect_address[id_found]), config_hostname);
+ else {
+ char name[256];
+ r= gethostname(name, sizeof(name));
+ if (r == 0) {
+ name[sizeof(name)-1]= 0;
+ r= Ndb_getInAddr(&(m_connect_address[id_found]), name);
+ }
+ }
+ if (r)
+ m_connect_address[id_found].s_addr= 0;
+ }
+ m_reserved_nodes.set(id_found);
+ char tmp_str[128];
+ m_reserved_nodes.getText(tmp_str);
+ g_eventLogger.info("Mgmt server state: nodeid %d reserved for ip %s, m_reserved_nodes %s.",
+ id_found, get_connect_address(id_found), tmp_str);
+ DBUG_RETURN(true);
+ }
+
+ if (found_matching_type && !found_free_node) {
+ // we have a temporary error which might be due to that
+ // we have got the latest connect status from db-nodes. Force update.
+ global_flag_send_heartbeat_now= 1;
+ }
+
+ BaseString type_string, type_c_string;
+ {
+ const char *alias, *str;
+ alias= ndb_mgm_get_node_type_alias_string(type, &str);
+ type_string.assfmt("%s(%s)", alias, str);
+ alias= ndb_mgm_get_node_type_alias_string((enum ndb_mgm_node_type)type_c,
+ &str);
+ type_c_string.assfmt("%s(%s)", alias, str);
+ }
+
+ if (*nodeId == 0) {
+ if (found_matching_id)
+ if (found_matching_type)
+ if (found_free_node)
+ error_string.appfmt("Connection done from wrong host ip %s.",
+ (client_addr)?
+ inet_ntoa(((struct sockaddr_in *)
+ (client_addr))->sin_addr):"");
+ else
+ error_string.appfmt("No free node id found for %s.",
+ type_string.c_str());
+ else
+ error_string.appfmt("No %s node defined in config file.",
+ type_string.c_str());
+ else
+ error_string.append("No nodes defined in config file.");
+ } else {
+ if (found_matching_id)
+ if (found_matching_type)
+ if (found_free_node) {
+ // have to split these into two since inet_ntoa overwrites itself
+ error_string.appfmt("Connection with id %d done from wrong host ip %s,",
+ *nodeId, inet_ntoa(((struct sockaddr_in *)
+ (client_addr))->sin_addr));
+ error_string.appfmt(" expected %s(%s).", config_hostname,
+ r_config_addr ?
+ "lookup failed" : inet_ntoa(config_addr));
+ } else
+ error_string.appfmt("Id %d already allocated by another node.",
+ *nodeId);
+ else
+ error_string.appfmt("Id %d configured as %s, connect attempted as %s.",
+ *nodeId, type_c_string.c_str(),
+ type_string.c_str());
+ else
+ error_string.appfmt("No node defined with id=%d in config file.",
+ *nodeId);
+ }
+
+ g_eventLogger.warning("Allocate nodeid (%d) failed. Connection from ip %s. "
+ "Returned error string \"%s\"",
+ *nodeId,
+ client_addr != 0 ? inet_ntoa(((struct sockaddr_in *)(client_addr))->sin_addr) : "<none>",
+ error_string.c_str());
+
+ NodeBitmask connected_nodes2;
+ get_connected_nodes(connected_nodes2);
+ {
+ BaseString tmp_connected, tmp_not_connected;
+ for(Uint32 i = 0; i < MAX_NODES; i++)
+ {
+ if (connected_nodes2.get(i))
+ {
+ if (!m_reserved_nodes.get(i))
+ tmp_connected.appfmt(" %d", i);
+ }
+ else if (m_reserved_nodes.get(i))
+ {
+ tmp_not_connected.appfmt(" %d", i);
+ }
+ }
+ if (tmp_connected.length() > 0)
+ g_eventLogger.info("Mgmt server state: node id's %s connected but not reserved",
+ tmp_connected.c_str());
+ if (tmp_not_connected.length() > 0)
+ g_eventLogger.info("Mgmt server state: node id's %s not connected but reserved",
+ tmp_not_connected.c_str());
+ }
+ DBUG_RETURN(false);
+}
+
+bool
+MgmtSrvr::getNextNodeId(NodeId * nodeId, enum ndb_mgm_node_type type) const
+{
+ NodeId tmp = * nodeId;
+
+ tmp++;
+ while(nodeTypes[tmp] != type && tmp < MAX_NODES)
+ tmp++;
+
+ if(tmp == MAX_NODES){
+ return false;
+ }
+
+ * nodeId = tmp;
+ return true;
+}
+
+#include "Services.hpp"
+
+void
+MgmtSrvr::eventReport(NodeId nodeId, const Uint32 * theData)
+{
+ const EventReport * const eventReport = (EventReport *)&theData[0];
+
+ Ndb_logevent_type type = eventReport->getEventType();
+ // Log event
+ g_eventLogger.log(type, theData, nodeId,
+ &m_event_listner[0].m_logLevel);
+ m_event_listner.log(type, theData, nodeId);
+}
+
+/***************************************************************************
+ * Backup
+ ***************************************************************************/
+int
+MgmtSrvr::startBackup(Uint32& backupId, int waitCompleted)
+{
+ bool next;
+ NodeId nodeId = 0;
+ while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true &&
+ theFacade->get_node_alive(nodeId) == false);
+
+ if(!next) return NO_CONTACT_WITH_DB_NODES;
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ BackupReq* req = CAST_PTR(BackupReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ,
+ BackupReq::SignalLength);
+
+ req->senderData = 19;
+ req->backupDataLen = 0;
+
+ int result;
+ if (waitCompleted == 2) {
+ result = sendRecSignal(nodeId, WAIT_BACKUP_COMPLETED,
+ signal, true, 30*60*1000 /*30 secs*/);
+ }
+ else if (waitCompleted == 1) {
+ result = sendRecSignal(nodeId, WAIT_BACKUP_STARTED,
+ signal, true, 5*60*1000 /*5 mins*/);
+ }
+ else {
+ result = sendRecSignal(nodeId, NO_WAIT, signal, true);
+ }
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ if (waitCompleted){
+ switch(m_lastBackupEvent.Event){
+ case BackupEvent::BackupCompleted:
+ backupId = m_lastBackupEvent.Completed.BackupId;
+ break;
+ case BackupEvent::BackupStarted:
+ backupId = m_lastBackupEvent.Started.BackupId;
+ break;
+ case BackupEvent::BackupFailedToStart:
+ return m_lastBackupEvent.FailedToStart.ErrorCode;
+ case BackupEvent::BackupAborted:
+ return m_lastBackupEvent.Aborted.ErrorCode;
+ default:
+ return -1;
+ break;
+ }
+ } else {
+ switch(m_lastBackupEvent.Event){
+ case BackupEvent::BackupCompleted:
+ backupId = m_lastBackupEvent.Completed.BackupId;
+ break;
+ case BackupEvent::BackupStarted:
+ backupId = m_lastBackupEvent.Started.BackupId;
+ break;
+ case BackupEvent::BackupFailedToStart:
+ return m_lastBackupEvent.FailedToStart.ErrorCode;
+ case BackupEvent::BackupAborted:
+ return m_lastBackupEvent.Aborted.ErrorCode;
+ default:
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+MgmtSrvr::abortBackup(Uint32 backupId)
+{
+ bool next;
+ NodeId nodeId = 0;
+ while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true &&
+ theFacade->get_node_alive(nodeId) == false);
+
+ if(!next){
+ return NO_CONTACT_WITH_DB_NODES;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ AbortBackupOrd* ord = CAST_PTR(AbortBackupOrd, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, BACKUP, GSN_ABORT_BACKUP_ORD,
+ AbortBackupOrd::SignalLength);
+
+ ord->requestType = AbortBackupOrd::ClientAbort;
+ ord->senderData = 19;
+ ord->backupId = backupId;
+
+ int result = sendSignal(nodeId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ return 0;
+}
+
+void
+MgmtSrvr::backupCallback(BackupEvent & event)
+{
+ DBUG_ENTER("MgmtSrvr::backupCallback");
+ m_lastBackupEvent = event;
+ switch(event.Event){
+ case BackupEvent::BackupFailedToStart:
+ DBUG_PRINT("info",("BackupEvent::BackupFailedToStart"));
+ theWaitState = NO_WAIT;
+ break;
+ case BackupEvent::BackupAborted:
+ DBUG_PRINT("info",("BackupEvent::BackupAborted"));
+ theWaitState = NO_WAIT;
+ break;
+ case BackupEvent::BackupCompleted:
+ DBUG_PRINT("info",("BackupEvent::BackupCompleted"));
+ theWaitState = NO_WAIT;
+ break;
+ case BackupEvent::BackupStarted:
+ if(theWaitState == WAIT_BACKUP_STARTED)
+ {
+ DBUG_PRINT("info",("BackupEvent::BackupStarted NO_WAIT"));
+ theWaitState = NO_WAIT;
+ } else {
+ DBUG_PRINT("info",("BackupEvent::BackupStarted"));
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*****************************************************************************
+ * Global Replication
+ *****************************************************************************/
+
+int
+MgmtSrvr::repCommand(Uint32* repReqId, Uint32 request, bool waitCompleted)
+{
+ bool next;
+ NodeId nodeId = 0;
+
+ while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true &&
+ theFacade->get_node_alive(nodeId) == false);
+
+ if(!next){
+ return NO_CONTACT_WITH_DB_NODES;
+ }
+
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ return COULD_NOT_ALLOCATE_MEMORY;
+ }
+
+ GrepReq* req = CAST_PTR(GrepReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI, GREP, GSN_GREP_REQ, GrepReq::SignalLength);
+ req->senderRef = _ownReference;
+ req->request = request;
+
+ int result;
+ if (waitCompleted)
+ result = sendRecSignal(nodeId, NO_WAIT, signal, true);
+ else
+ result = sendRecSignal(nodeId, NO_WAIT, signal, true);
+ if (result == -1) {
+ return SEND_OR_RECEIVE_FAILED;
+ }
+
+ /**
+ * @todo
+ * Maybe add that we should receive a confirmation that the
+ * request was received ok.
+ * Then we should give the user the correct repReqId.
+ */
+
+ *repReqId = 4711;
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ * Area 51 ???
+ *****************************************************************************/
+
+MgmtSrvr::Area51
+MgmtSrvr::getStuff()
+{
+ Area51 ret;
+ ret.theFacade = theFacade;
+ ret.theRegistry = theFacade->theTransporterRegistry;
+ return ret;
+}
+
+NodeId
+MgmtSrvr::getPrimaryNode() const {
+#if 0
+ Uint32 tmp;
+ const Properties *prop = NULL;
+
+ getConfig()->get("SYSTEM", &prop);
+ if(prop == NULL)
+ return 0;
+
+ prop->get("PrimaryMGMNode", &tmp);
+
+ return tmp;
+#else
+ return 0;
+#endif
+}
+
+
+MgmtSrvr::Allocated_resources::Allocated_resources(MgmtSrvr &m)
+ : m_mgmsrv(m)
+{
+}
+
+MgmtSrvr::Allocated_resources::~Allocated_resources()
+{
+ Guard g(m_mgmsrv.m_node_id_mutex);
+ if (!m_reserved_nodes.isclear()) {
+ m_mgmsrv.m_reserved_nodes.bitANDC(m_reserved_nodes);
+ // node has been reserved, force update signal to ndb nodes
+ global_flag_send_heartbeat_now= 1;
+
+ char tmp_str[128];
+ m_mgmsrv.m_reserved_nodes.getText(tmp_str);
+ g_eventLogger.info("Mgmt server state: nodeid %d freed, m_reserved_nodes %s.",
+ get_nodeid(), tmp_str);
+ }
+}
+
+void
+MgmtSrvr::Allocated_resources::reserve_node(NodeId id)
+{
+ m_reserved_nodes.set(id);
+}
+
+NodeId
+MgmtSrvr::Allocated_resources::get_nodeid() const
+{
+ for(Uint32 i = 0; i < MAX_NODES; i++)
+ {
+ if (m_reserved_nodes.get(i))
+ return i;
+ }
+ return 0;
+}
+
+int
+MgmtSrvr::setDbParameter(int node, int param, const char * value,
+ BaseString& msg){
+
+ if(NdbMutex_Lock(m_configMutex))
+ return -1;
+
+ /**
+ * Check parameter
+ */
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_NODE);
+ if(iter.first() != 0){
+ msg.assign("Unable to find node section (iter.first())");
+ NdbMutex_Unlock(m_configMutex);
+ return -1;
+ }
+
+ Uint32 type = NODE_TYPE_DB + 1;
+ if(node != 0){
+ if(iter.find(CFG_NODE_ID, node) != 0){
+ msg.assign("Unable to find node (iter.find())");
+ NdbMutex_Unlock(m_configMutex);
+ return -1;
+ }
+ if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0){
+ msg.assign("Unable to get node type(iter.get(CFG_TYPE_OF_SECTION))");
+ NdbMutex_Unlock(m_configMutex);
+ return -1;
+ }
+ } else {
+ do {
+ if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0){
+ msg.assign("Unable to get node type(iter.get(CFG_TYPE_OF_SECTION))");
+ NdbMutex_Unlock(m_configMutex);
+ return -1;
+ }
+ if(type == NODE_TYPE_DB)
+ break;
+ } while(iter.next() == 0);
+ }
+
+ if(type != NODE_TYPE_DB){
+ msg.assfmt("Invalid node type or no such node (%d %d)",
+ type, NODE_TYPE_DB);
+ NdbMutex_Unlock(m_configMutex);
+ return -1;
+ }
+
+ int p_type;
+ unsigned val_32;
+ Uint64 val_64;
+ const char * val_char;
+ do {
+ p_type = 0;
+ if(iter.get(param, &val_32) == 0){
+ val_32 = atoi(value);
+ break;
+ }
+
+ p_type++;
+ if(iter.get(param, &val_64) == 0){
+ val_64 = strtoll(value, 0, 10);
+ break;
+ }
+ p_type++;
+ if(iter.get(param, &val_char) == 0){
+ val_char = value;
+ break;
+ }
+ msg.assign("Could not get parameter");
+ NdbMutex_Unlock(m_configMutex);
+ return -1;
+ } while(0);
+
+ bool res = false;
+ do {
+ int ret = iter.get(CFG_TYPE_OF_SECTION, &type);
+ assert(ret == 0);
+
+ if(type != NODE_TYPE_DB)
+ continue;
+
+ Uint32 node;
+ ret = iter.get(CFG_NODE_ID, &node);
+ assert(ret == 0);
+
+ ConfigValues::Iterator i2(_config->m_configValues->m_config,
+ iter.m_config);
+ switch(p_type){
+ case 0:
+ res = i2.set(param, val_32);
+ ndbout_c("Updating node %d param: %d to %d", node, param, val_32);
+ break;
+ case 1:
+ res = i2.set(param, val_64);
+ ndbout_c("Updating node %d param: %d to %Ld", node, param, val_32);
+ break;
+ case 2:
+ res = i2.set(param, val_char);
+ ndbout_c("Updating node %d param: %d to %s", node, param, val_char);
+ break;
+ default:
+ abort();
+ }
+ assert(res);
+ } while(node == 0 && iter.next() == 0);
+
+ msg.assign("Success");
+ NdbMutex_Unlock(m_configMutex);
+ return 0;
+}
+
+int
+MgmtSrvr::setConnectionDbParameter(int node1,
+ int node2,
+ int param,
+ int value,
+ BaseString& msg){
+ Uint32 current_value,new_value;
+
+ DBUG_ENTER("MgmtSrvr::setConnectionDbParameter");
+
+ if(NdbMutex_Lock(m_configMutex))
+ {
+ DBUG_RETURN(-1);
+ }
+
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_CONNECTION);
+
+ if(iter.first() != 0){
+ msg.assign("Unable to find connection section (iter.first())");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-1);
+ }
+
+ for(;iter.valid();iter.next()) {
+ Uint32 n1,n2;
+ iter.get(CFG_CONNECTION_NODE_1, &n1);
+ iter.get(CFG_CONNECTION_NODE_2, &n2);
+ if((n1 == (unsigned)node1 && n2 == (unsigned)node2)
+ || (n1 == (unsigned)node2 && n2 == (unsigned)node1))
+ break;
+ }
+ if(!iter.valid()) {
+ msg.assign("Unable to find connection between nodes");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-2);
+ }
+
+ if(iter.get(param, &current_value) != 0) {
+ msg.assign("Unable to get current value of parameter");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-3);
+ }
+
+ ConfigValues::Iterator i2(_config->m_configValues->m_config,
+ iter.m_config);
+
+ if(i2.set(param, (unsigned)value) == false) {
+ msg.assign("Unable to set new value of parameter");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-4);
+ }
+
+ if(iter.get(param, &new_value) != 0) {
+ msg.assign("Unable to get parameter after setting it.");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-5);
+ }
+
+ msg.assfmt("%u -> %u",current_value,new_value);
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(1);
+}
+
+
+int
+MgmtSrvr::getConnectionDbParameter(int node1,
+ int node2,
+ int param,
+ int *value,
+ BaseString& msg){
+ DBUG_ENTER("MgmtSrvr::getConnectionDbParameter");
+
+ if(NdbMutex_Lock(m_configMutex))
+ {
+ DBUG_RETURN(-1);
+ }
+
+ ndb_mgm_configuration_iterator
+ iter(* _config->m_configValues, CFG_SECTION_CONNECTION);
+
+ if(iter.first() != 0){
+ msg.assign("Unable to find connection section (iter.first())");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-1);
+ }
+
+ for(;iter.valid();iter.next()) {
+ Uint32 n1=0,n2=0;
+ iter.get(CFG_CONNECTION_NODE_1, &n1);
+ iter.get(CFG_CONNECTION_NODE_2, &n2);
+ if((n1 == (unsigned)node1 && n2 == (unsigned)node2)
+ || (n1 == (unsigned)node2 && n2 == (unsigned)node1))
+ break;
+ }
+ if(!iter.valid()) {
+ msg.assign("Unable to find connection between nodes");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-1);
+ }
+
+ if(iter.get(param, (Uint32*)value) != 0) {
+ msg.assign("Unable to get current value of parameter");
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(-1);
+ }
+
+ msg.assfmt("%d",*value);
+ NdbMutex_Unlock(m_configMutex);
+ DBUG_RETURN(1);
+}
+
+void MgmtSrvr::transporter_connect(NDB_SOCKET_TYPE sockfd)
+{
+ if (theFacade->get_registry()->connect_server(sockfd))
+ {
+ /**
+ * Force an update_connections() so that the
+ * ClusterMgr and TransporterFacade is up to date
+ * with the new connection.
+ * Important for correct node id reservation handling
+ */
+ NdbMutex_Lock(theFacade->theMutexPtr);
+ theFacade->get_registry()->update_connections();
+ NdbMutex_Unlock(theFacade->theMutexPtr);
+ }
+}
+
+int MgmtSrvr::set_connect_string(const char *str)
+{
+ return ndb_mgm_set_connectstring(m_config_retriever->get_mgmHandle(),str);
+}
+
+
+template class Vector<SigMatch>;
+#if __SUNPRO_CC != 0x560
+template bool SignalQueue::waitFor<SigMatch>(Vector<SigMatch>&, SigMatch**, NdbApiSignal**, unsigned);
+#endif
+
+template class MutexVector<unsigned short>;
+template class MutexVector<Ndb_mgmd_event_service::Event_listener>;
+template class MutexVector<EventSubscribeReq>;
diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp
new file mode 100644
index 00000000000..95298630230
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp
@@ -0,0 +1,794 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 MgmtSrvr_H
+#define MgmtSrvr_H
+
+#include <kernel_types.h>
+#include "Config.hpp"
+#include <NdbCondition.h>
+#include <mgmapi.h>
+
+
+#include <Vector.hpp>
+#include <NodeBitmask.hpp>
+#include <signaldata/ManagementServer.hpp>
+#include "SignalQueue.hpp"
+#include <ndb_version.h>
+#include <EventLogger.hpp>
+#include <signaldata/EventSubscribeReq.hpp>
+
+/**
+ * @desc Block number for Management server.
+ * @todo This should probably be somewhere else. I don't know where atm.
+ */
+#define MGMSRV 1
+
+class ConfigInfoServer;
+class NdbApiSignal;
+class Config;
+class SetLogLevelOrd;
+class SocketServer;
+
+class Ndb_mgmd_event_service : public EventLoggerBase
+{
+ friend class MgmtSrvr;
+public:
+ struct Event_listener : public EventLoggerBase {
+ NDB_SOCKET_TYPE m_socket;
+ Uint32 m_parsable;
+ };
+
+private:
+ class MgmtSrvr * m_mgmsrv;
+ MutexVector<Event_listener> m_clients;
+public:
+ Ndb_mgmd_event_service(class MgmtSrvr * m) : m_clients(5) {
+ m_mgmsrv = m;
+ }
+
+ void add_listener(const Event_listener&);
+ void update_max_log_level(const LogLevel&);
+ void update_log_level(const LogLevel&);
+
+ void log(int eventType, const Uint32* theData, NodeId nodeId);
+
+ void stop_sessions();
+
+ Event_listener& operator[](unsigned i) { return m_clients[i]; }
+ const Event_listener& operator[](unsigned i) const { return m_clients[i]; }
+ void lock() { m_clients.lock(); }
+ void unlock(){ m_clients.unlock(); }
+};
+
+/**
+ * @class MgmtSrvr
+ * @brief Main class for the management server.
+ *
+ * It has one interface to be used by a local client.
+ * With the methods it's possible to send different kind of commands to
+ * DB processes, as log level, set trace number etc.
+ *
+ * A MgmtSrvr creates a ConfigInfoServer which serves request on TCP sockets.
+ * The requests come typical from DB and API processes which want
+ * to fetch its configuration parameters. The MgmtSrvr knows about the
+ * configuration by reading a configuration file.
+ *
+ * The MgmtSrvr class corresponds in some ways to the Ndb class in API.
+ * It creates a TransporterFacade, receives signals and defines signals
+ * to send and receive.
+ */
+class MgmtSrvr {
+
+public:
+ // some compilers need all of this
+ class Allocated_resources;
+ friend class Allocated_resources;
+ class Allocated_resources {
+ public:
+ Allocated_resources(class MgmtSrvr &m);
+ ~Allocated_resources();
+ // methods to reserve/allocate resources which
+ // will be freed when running destructor
+ void reserve_node(NodeId id);
+ bool is_reserved(NodeId nodeId) { return m_reserved_nodes.get(nodeId); }
+ bool is_reserved(NodeBitmask mask) { return !mask.bitAND(m_reserved_nodes).isclear(); }
+ bool isclear() { return m_reserved_nodes.isclear(); }
+ NodeId get_nodeid() const;
+ private:
+ MgmtSrvr &m_mgmsrv;
+ NodeBitmask m_reserved_nodes;
+ };
+ NdbMutex *m_node_id_mutex;
+
+ /**
+ * Start/initate the event log.
+ */
+ void startEventLog();
+
+ /**
+ * Stop the event log.
+ */
+ void stopEventLog();
+
+ /**
+ * Enable/disable eventlog log levels/severities.
+ *
+ * @param serverity the log level/serverity.
+ * @return true if the severity was enabled.
+ */
+ bool setEventLogFilter(int severity, int enable);
+
+ /**
+ * Returns true if the log level/severity is enabled.
+ *
+ * @param severity the severity level.
+ */
+ bool isEventLogFilterEnabled(int severity);
+
+ STATIC_CONST( NO_CONTACT_WITH_PROCESS = 5000 );
+ STATIC_CONST( PROCESS_NOT_CONFIGURED = 5001 );
+ STATIC_CONST( WRONG_PROCESS_TYPE = 5002 );
+ STATIC_CONST( COULD_NOT_ALLOCATE_MEMORY = 5003 );
+ STATIC_CONST( SEND_OR_RECEIVE_FAILED = 5005 );
+ STATIC_CONST( INVALID_LEVEL = 5006 );
+ STATIC_CONST( INVALID_ERROR_NUMBER = 5007 );
+ STATIC_CONST( INVALID_TRACE_NUMBER = 5008 );
+ STATIC_CONST( NOT_IMPLEMENTED = 5009 );
+ STATIC_CONST( INVALID_BLOCK_NAME = 5010 );
+
+ STATIC_CONST( CONFIG_PARAM_NOT_EXIST = 5011 );
+ STATIC_CONST( CONFIG_PARAM_NOT_UPDATEABLE = 5012 );
+ STATIC_CONST( VALUE_WRONG_FORMAT_INT_EXPECTED = 5013 );
+ STATIC_CONST( VALUE_TOO_LOW = 5014 );
+ STATIC_CONST( VALUE_TOO_HIGH = 5015 );
+ STATIC_CONST( VALUE_WRONG_FORMAT_BOOL_EXPECTED = 5016 );
+
+ STATIC_CONST( CONFIG_FILE_OPEN_WRITE_ERROR = 5017 );
+ STATIC_CONST( CONFIG_FILE_OPEN_READ_ERROR = 5018 );
+ STATIC_CONST( CONFIG_FILE_WRITE_ERROR = 5019 );
+ STATIC_CONST( CONFIG_FILE_READ_ERROR = 5020 );
+ STATIC_CONST( CONFIG_FILE_CLOSE_ERROR = 5021 );
+
+ STATIC_CONST( CONFIG_CHANGE_REFUSED_BY_RECEIVER = 5022 );
+ STATIC_CONST( COULD_NOT_SYNC_CONFIG_CHANGE_AGAINST_PHYSICAL_MEDIUM = 5023 );
+ STATIC_CONST( CONFIG_FILE_CHECKSUM_ERROR = 5024 );
+ STATIC_CONST( NOT_POSSIBLE_TO_SEND_CONFIG_UPDATE_TO_PROCESS_TYPE = 5025 );
+
+ STATIC_CONST( NODE_SHUTDOWN_IN_PROGESS = 5026 );
+ STATIC_CONST( SYSTEM_SHUTDOWN_IN_PROGRESS = 5027 );
+ STATIC_CONST( NODE_SHUTDOWN_WOULD_CAUSE_SYSTEM_CRASH = 5028 );
+ STATIC_CONST( NO_CONTACT_WITH_CLUSTER = 6666 );
+ STATIC_CONST( OPERATION_IN_PROGRESS = 6667 );
+
+ STATIC_CONST( NO_CONTACT_WITH_DB_NODES = 5030 );
+ /**
+ * This enum specifies the different signal loggig modes possible to set
+ * with the setSignalLoggingMode method.
+ */
+ enum LogMode {In, Out, InOut, Off};
+
+ /* Constructor */
+
+ MgmtSrvr(SocketServer *socket_server,
+ const char *config_filename, /* Where to save config */
+ const char *connect_string);
+ int init();
+ NodeId getOwnNodeId() const {return _ownNodeId;};
+
+ /**
+ * Read (initial) config file, create TransporterFacade,
+ * define signals, create ConfigInfoServer.
+ * @return true if succeeded, otherwise false
+ */
+ bool check_start(); // may be run before start to check that some things are ok
+ bool start(BaseString &error_string);
+
+ ~MgmtSrvr();
+
+ int status(int processId,
+ ndb_mgm_node_status * status,
+ Uint32 * version,
+ Uint32 * phase,
+ bool * systemShutdown,
+ Uint32 * dynamicId,
+ Uint32 * nodeGroup,
+ Uint32 * connectCount);
+
+ // All the functions below may return any of this error codes:
+ // NO_CONTACT_WITH_PROCESS, PROCESS_NOT_CONFIGURED, WRONG_PROCESS_TYPE,
+ // COULD_NOT_ALLOCATE_MEMORY, SEND_OR_RECEIVE_FAILED
+
+
+ typedef void (* StopCallback)(int nodeId, void * anyData, int errorCode);
+
+ typedef void (* VersionCallback)(int nodeId, int version,
+ void * anyData, int errorCode);
+
+
+ typedef void (* EnterSingleCallback)(int nodeId, void * anyData,
+ int errorCode);
+ typedef void (* ExitSingleCallback)(int nodeId, void * anyData,
+ int errorCode);
+
+ /**
+ * Lock configuration
+ */
+ int lockConf();
+
+ /**
+ * Unlock configuration, and commit it if commit is true
+ */
+ int unlockConf(bool commit);
+
+ /**
+ * Commit new configuration
+ */
+ int commitConfig();
+
+ /**
+ * Rollback configuration
+ */
+ int rollbackConfig();
+
+ /**
+ * Save a configuration to permanent storage
+ */
+ int saveConfig(const Config *);
+
+ /**
+ * Save the running configuration
+ */
+ int saveConfig() {
+ return saveConfig(_config);
+ };
+
+ /**
+ * Read configuration from file, or from another MGM server
+ */
+ Config *readConfig();
+
+ /**
+ * Fetch configuration from another MGM server
+ */
+ Config *fetchConfig();
+
+ /**
+ * Stop a node
+ *
+ * @param processId: Id of the DB process to stop
+ * @return 0 if succeeded, otherwise: as stated above, plus:
+ */
+ int stopNode(int nodeId, bool abort = false, StopCallback = 0, void *any= 0);
+
+ /**
+ * Stop the system
+ */
+ int stop(int * cnt = 0, bool abort = false, StopCallback = 0, void *any = 0);
+
+ /**
+ * print version info about a node
+ *
+ * @param processId: Id of the DB process to stop
+ * @return 0 if succeeded, otherwise: as stated above, plus:
+ */
+ int versionNode(int nodeId, bool abort = false,
+ VersionCallback = 0, void *any= 0);
+
+ /**
+ * print version info about all node in the system
+ */
+ int version(int * cnt = 0, bool abort = false,
+ VersionCallback = 0, void *any = 0);
+
+ /**
+ * Maintenance on the system
+ */
+ int enterSingleUser(int * cnt = 0, Uint32 singleuserNodeId = 0,
+ EnterSingleCallback = 0, void *any = 0);
+
+
+ /**
+ * Resume from maintenance on the system
+ */
+ int exitSingleUser(int * cnt = 0, bool abort = false,
+ ExitSingleCallback = 0, void *any = 0);
+
+ /**
+ * Start DB process.
+ * @param processId: Id of the DB process to start
+ * @return 0 if succeeded, otherwise: as stated above, plus:
+ */
+ int start(int processId);
+
+ /**
+ * Restart a node
+ * @param processId: Id of the DB process to start
+ */
+ int restartNode(int processId, bool nostart, bool initialStart,
+ bool abort = false,
+ StopCallback = 0, void * anyData = 0);
+
+ /**
+ * Restart the system
+ */
+ int restart(bool nostart, bool initialStart,
+ bool abort = false,
+ int * stopCount = 0, StopCallback = 0, void * anyData = 0);
+
+ struct BackupEvent {
+ enum Event {
+ BackupStarted = 1,
+ BackupFailedToStart = 2,
+ BackupCompleted = 3,
+ BackupAborted = 4
+ } Event;
+
+ NdbNodeBitmask Nodes;
+ union {
+ struct {
+ Uint32 BackupId;
+ } Started ;
+ struct {
+ Uint32 ErrorCode;
+ } FailedToStart ;
+ struct {
+ Uint32 BackupId;
+ Uint32 NoOfBytes;
+ Uint32 NoOfRecords;
+ Uint32 NoOfLogBytes;
+ Uint32 NoOfLogRecords;
+ Uint32 startGCP;
+ Uint32 stopGCP;
+ } Completed ;
+ struct {
+ Uint32 BackupId;
+ Uint32 Reason;
+ Uint32 ErrorCode;
+ } Aborted ;
+ };
+ };
+
+ /**
+ * Backup functionallity
+ */
+ int startBackup(Uint32& backupId, int waitCompleted= 2);
+ int abortBackup(Uint32 backupId);
+ int performBackup(Uint32* backupId);
+
+ /**
+ * Global Replication
+ */
+ int repCommand(Uint32* repReqId, Uint32 request, bool waitCompleted = false);
+
+ //**************************************************************************
+ // Description: Set event report level for a DB process
+ // Parameters:
+ // processId: Id of the DB process
+ // level: Event report level
+ // isResend: Flag to indicate for resending log levels during node restart
+ // Returns: 0 if succeeded, otherwise: as stated above, plus:
+ // INVALID_LEVEL
+ //**************************************************************************
+
+ int setEventReportingLevelImpl(int processId, const EventSubscribeReq& ll);
+ int setNodeLogLevelImpl(int processId, const SetLogLevelOrd & ll);
+
+ /**
+ * Insert an error in a DB process.
+ * @param processId: Id of the DB process
+ * @param errorNo: The error number. > 0.
+ * @return 0 if succeeded, otherwise: as stated above, plus:
+ * INVALID_ERROR_NUMBER
+ */
+ int insertError(int processId, int errorNo);
+
+
+
+ int setTraceNo(int processId, int traceNo);
+ //**************************************************************************
+ // Description: Set trace number in a DB process.
+ // Parameters:
+ // processId: Id of the DB process
+ // trace: Trace number
+ // Returns: 0 if succeeded, otherwise: as stated above, plus:
+ // INVALID_TRACE_NUMBER
+ //**************************************************************************
+
+
+ int setSignalLoggingMode(int processId, LogMode mode,
+ const Vector<BaseString> &blocks);
+
+ int setSignalLoggingMode(int processId, LogMode mode,
+ BaseString &block) {
+ Vector<BaseString> v;
+ v.push_back(block);
+ return setSignalLoggingMode(processId, mode, v);
+ }
+ //**************************************************************************
+ // Description: Set signal logging mode for blocks in a DB process.
+ // Parameters:
+ // processId: Id of the DB process
+ // mode: The log mode
+ // blocks: Which blocks to be affected (container of strings)
+ // Returns: 0 if succeeded, otherwise: as stated above, plus:
+ // INVALID_BLOCK_NAME
+ //**************************************************************************
+
+
+ int startSignalTracing(int processId);
+ //**************************************************************************
+ // Description: Start signal tracing for a DB process.
+ // Parameters:
+ // processId: Id of the DB process
+ // Returns: 0 if succeeded, otherwise: as stated above.
+ //**************************************************************************
+
+
+ int stopSignalTracing(int processId);
+ //**************************************************************************
+ // Description: Stop signal tracing for a DB process.
+ // Parameters:
+ // processId: Id of the DB process
+ // Returns: 0 if succeeded, otherwise: as stated above.
+ //**************************************************************************
+
+ /**
+ * Dump State
+ */
+ int dumpState(int processId, const Uint32 args[], Uint32 argNo);
+ int dumpState(int processId, const char* args);
+
+ /**
+ * Get next node id (node id gt that _nodeId)
+ * of specified type and save it in _nodeId
+ *
+ * @return false if none found
+ */
+ bool getNextNodeId(NodeId * _nodeId, enum ndb_mgm_node_type type) const ;
+ bool alloc_node_id(NodeId * _nodeId, enum ndb_mgm_node_type type,
+ struct sockaddr *client_addr, SOCKET_SIZE_TYPE *client_addr_len,
+ BaseString &error_string);
+
+ /**
+ *
+ */
+ enum ndb_mgm_node_type getNodeType(NodeId) const;
+
+ /**
+ * Get error text
+ *
+ * @param errorCode: Error code to get a match error text for.
+ * @return The error text.
+ */
+ const char* getErrorText(int errorCode, char *buf, int buf_sz);
+
+ /**
+ * Get configuration
+ */
+ const Config * getConfig() const;
+
+ /**
+ * Change configuration paramter
+ */
+ bool changeConfig(const BaseString &section,
+ const BaseString &param,
+ const BaseString &value);
+
+ /**
+ * Returns the node count for the specified node type.
+ *
+ * @param type The node type.
+ * @return The number of nodes of the specified type.
+ */
+ int getNodeCount(enum ndb_mgm_node_type type) const;
+
+ /**
+ * Returns the nodeId of the management master
+ */
+ NodeId getPrimaryNode() const;
+
+ /**
+ * Returns the port number.
+ * @return port number.
+ */
+ int getPort() const;
+
+ int setDbParameter(int node, int parameter, const char * value, BaseString&);
+ int setConnectionDbParameter(int node1, int node2, int param, int value,
+ BaseString& msg);
+ int getConnectionDbParameter(int node1, int node2, int param,
+ int *value, BaseString& msg);
+
+ int set_connect_string(const char *str);
+
+ void transporter_connect(NDB_SOCKET_TYPE sockfd);
+
+ ConfigRetriever *get_config_retriever() { return m_config_retriever; };
+
+ const char *get_connect_address(Uint32 node_id) { return inet_ntoa(m_connect_address[node_id]); }
+ void get_connected_nodes(NodeBitmask &connected_nodes) const;
+ SocketServer *get_socket_server() { return m_socket_server; }
+
+ //**************************************************************************
+private:
+ //**************************************************************************
+
+ int setEventReportingLevel(int processId, LogLevel::EventCategory, Uint32);
+
+ /**
+ * Check if it is possible to send a signal to a (DB) process
+ *
+ * @param processId: Id of the process to send to
+ * @return 0 OK, 1 process dead, 2 API or MGMT process, 3 not configured
+ */
+ int okToSendTo(NodeId nodeId, bool unCond = false);
+
+ /**
+ * Get block number for a block
+ *
+ * @param blockName: Block to get number for
+ * @return -1 if block not found, otherwise block number
+ */
+ int getBlockNumber(const BaseString &blockName);
+
+ //**************************************************************************
+
+ int _blockNumber;
+ NodeId _ownNodeId;
+ SocketServer *m_socket_server;
+
+ BlockReference _ownReference;
+ NdbMutex *m_configMutex;
+ const Config * _config;
+ Config * m_newConfig;
+ BaseString m_configFilename;
+ Uint32 m_nextConfigGenerationNumber;
+
+ NodeBitmask m_reserved_nodes;
+ struct in_addr m_connect_address[MAX_NODES];
+
+ //**************************************************************************
+ // Specific signal handling methods
+ //**************************************************************************
+
+ static void defineSignals(int blockNumber);
+ //**************************************************************************
+ // Description: Define all signals to be sent or received for a block
+ // Parameters:
+ // blockNumber: The block number send/receive
+ // Returns: -
+ //**************************************************************************
+
+ void handleReceivedSignal(NdbApiSignal* signal);
+ //**************************************************************************
+ // Description: This method is called from "another" thread when a signal
+ // is received. If expect the received signal and succeed to handle it
+ // we signal with a condition variable to the waiting
+ // thread (receiveOptimisedResponse) that the signal has arrived.
+ // Parameters:
+ // signal: The recieved signal
+ // Returns: -
+ //**************************************************************************
+
+ void handleStatus(NodeId nodeId, bool alive);
+ //**************************************************************************
+ // Description: Handle the death of a process
+ // Parameters:
+ // processId: Id of the dead process.
+ // Returns: -
+ //**************************************************************************
+
+ void handle_MGM_LOCK_CONFIG_REQ(NdbApiSignal *signal);
+ void handle_MGM_UNLOCK_CONFIG_REQ(NdbApiSignal *signal);
+
+ //**************************************************************************
+ // Specific signal handling data
+ //**************************************************************************
+
+
+ //**************************************************************************
+ //**************************************************************************
+ // General signal handling methods
+ // This functions are more or less copied from the Ndb class.
+
+
+ /**
+ * WaitSignalType defines states where each state define a set of signals
+ * we accept to receive.
+ * The state is set after we have sent a signal.
+ * When a signal arrives we first check current state (handleReceivedSignal)
+ * to verify that we expect the arrived signal.
+ * It's only then we are in state accepting the arrived signal
+ * we handle the signal.
+ */
+ enum WaitSignalType {
+ NO_WAIT, // We don't expect to receive any signal
+ WAIT_SET_VAR, // Accept SET_VAR_CONF and SET_VAR_REF
+ WAIT_SUBSCRIBE_CONF, // Accept event subscription confirmation
+ WAIT_STOP,
+ WAIT_BACKUP_STARTED,
+ WAIT_BACKUP_COMPLETED,
+ WAIT_VERSION
+ };
+
+ /**
+ * Get an unused signal
+ * @return A signal if succeeded, NULL otherwise
+ */
+ NdbApiSignal* getSignal();
+
+ /**
+ * Add a signal to the list of unused signals
+ * @param signal: The signal to add
+ */
+ void releaseSignal(NdbApiSignal* signal);
+
+ /**
+ * Remove a signal from the list of unused signals and delete
+ * the memory for it.
+ */
+ void freeSignal();
+
+ /**
+ * Send a signal
+ * @param processId: Id of the receiver process
+ * @param waitState: State denoting a set of signals we accept to receive
+ * @param signal: The signal to send
+ * @return 0 if succeeded, -1 otherwise
+ */
+ int sendSignal(Uint16 processId, WaitSignalType waitState,
+ NdbApiSignal* signal, bool force = false);
+
+ /**
+ * Send a signal and wait for an answer signal
+ * @param processId: Id of the receiver process
+ * @param waitState: State denoting a set of signals we accept to receive.
+ * @param signal: The signal to send
+ * @return 0 if succeeded, -1 otherwise (for example failed to send or
+ * failed to receive expected signal).
+ */
+ int sendRecSignal(Uint16 processId, WaitSignalType waitState,
+ NdbApiSignal* signal, bool force = false,
+ int waitTime = WAIT_FOR_RESPONSE_TIMEOUT);
+
+ /**
+ * Wait for a signal to arrive.
+ * @return 0 if signal arrived, -1 otherwise
+ */
+ int receiveOptimisedResponse(int waitTime);
+
+ /**
+ * This function is called from "outside" of MgmtSrvr
+ * when a signal is sent to MgmtSrvr.
+ * @param mgmtSrvr: The MgmtSrvr object which shall recieve the signal.
+ * @param signal: The received signal.
+ */
+ static void signalReceivedNotification(void* mgmtSrvr,
+ NdbApiSignal* signal,
+ struct LinearSectionPtr ptr[3]);
+
+ /**
+ * Called from "outside" of MgmtSrvr when a DB process has died.
+ * @param mgmtSrvr: The MgmtSrvr object wreceiveOptimisedResponsehich
+ * shall receive the notification.
+ * @param processId: Id of the dead process.
+ */
+ static void nodeStatusNotification(void* mgmSrv, Uint32 nodeId,
+ bool alive, bool nfCompleted);
+
+ /**
+ * An event from <i>nodeId</i> has arrived
+ */
+ void eventReport(NodeId nodeId, const Uint32 * theData);
+
+
+ //**************************************************************************
+ //**************************************************************************
+ // General signal handling data
+
+ STATIC_CONST( WAIT_FOR_RESPONSE_TIMEOUT = 300000 ); // Milliseconds
+ // Max time to wait for a signal to arrive
+
+ NdbApiSignal* theSignalIdleList;
+ // List of unused signals
+
+ WaitSignalType theWaitState;
+ // State denoting a set of signals we accept to recieve.
+
+ NdbCondition* theMgmtWaitForResponseCondPtr;
+ // Condition variable used when we wait for a signal to arrive/a
+ // signal arrives.
+ // We wait in receiveOptimisedResponse and signal in handleReceivedSignal.
+
+ class TransporterFacade * theFacade;
+
+ class SignalQueue m_signalRecvQueue;
+
+ struct StopRecord {
+ StopRecord(){ inUse = false; callback = 0; singleUserMode = false;}
+ bool inUse;
+ bool singleUserMode;
+ int sentCount;
+ int reply;
+ int nodeId;
+ void * anyData;
+ StopCallback callback;
+ };
+ StopRecord m_stopRec;
+
+ struct VersionRecord {
+ VersionRecord(){ inUse = false; callback = 0;}
+ bool inUse;
+ Uint32 version[MAX_NODES];
+ VersionCallback callback;
+ };
+ VersionRecord m_versionRec;
+ int sendVersionReq( int processId);
+
+
+ void handleStopReply(NodeId nodeId, Uint32 errCode);
+ int translateStopRef(Uint32 errCode);
+
+ bool _isStopThread;
+ int _logLevelThreadSleep;
+ MutexVector<NodeId> m_started_nodes;
+ MutexVector<EventSubscribeReq> m_log_level_requests;
+ LogLevel m_nodeLogLevel[MAX_NODES];
+ enum ndb_mgm_node_type nodeTypes[MAX_NODES];
+ friend class MgmApiSession;
+ friend class Ndb_mgmd_event_service;
+ Ndb_mgmd_event_service m_event_listner;
+
+ /**
+ * Handles the thread wich upon a 'Node is started' event will
+ * set the node's previous loglevel settings.
+ */
+ struct NdbThread* _logLevelThread;
+ static void *logLevelThread_C(void *);
+ void logLevelThreadRun();
+
+ struct NdbThread *m_signalRecvThread;
+ static void *signalRecvThread_C(void *);
+ void signalRecvThreadRun();
+
+ void backupCallback(BackupEvent &);
+ BackupEvent m_lastBackupEvent;
+
+ Config *_props;
+
+ int send(class NdbApiSignal* signal, Uint32 node, Uint32 node_type);
+
+ ConfigRetriever *m_config_retriever;
+
+public:
+ /**
+ * This method does not exist
+ */
+ struct Area51 {
+ class TransporterFacade * theFacade;
+ class TransporterRegistry * theRegistry;
+ };
+ Area51 getStuff();
+};
+
+inline
+const Config *
+MgmtSrvr::getConfig() const {
+ return _config;
+}
+
+#endif // MgmtSrvr_H
diff --git a/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp b/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
new file mode 100644
index 00000000000..6c4b4e9ae3c
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
@@ -0,0 +1,299 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <signaldata/TestOrd.hpp>
+#include <OutputStream.hpp>
+
+#include "MgmtSrvr.hpp"
+#include "SignalQueue.hpp"
+#include <InitConfigFileParser.hpp>
+#include <ConfigRetriever.hpp>
+#include <ndb_version.h>
+
+void
+MgmtSrvr::handle_MGM_LOCK_CONFIG_REQ(NdbApiSignal *signal) {
+ NodeId sender = refToNode(signal->theSendersBlockRef);
+ const MgmLockConfigReq * const req = CAST_CONSTPTR(MgmLockConfigReq, signal->getDataPtr());
+
+ NdbApiSignal *reply = getSignal();
+ if(signal == NULL)
+ return; /** @todo handle allocation failure */
+
+ reply->set(TestOrd::TraceAPI,
+ MGMSRV,
+ GSN_MGM_LOCK_CONFIG_REP,
+ MgmLockConfigRep::SignalLength);
+
+ MgmLockConfigRep *lockRep = CAST_PTR(MgmLockConfigRep, reply->getDataPtrSend());
+
+ lockRep->errorCode = MgmLockConfigRep::UNKNOWN_ERROR;
+
+ if(req->newConfigGeneration < m_nextConfigGenerationNumber) {
+ lockRep->errorCode = MgmLockConfigRep::GENERATION_MISMATCH;
+ goto done;
+ }
+ NdbMutex_Lock(m_configMutex);
+
+ m_nextConfigGenerationNumber = req->newConfigGeneration+1;
+
+ lockRep->errorCode = MgmLockConfigRep::OK;
+
+ done:
+ sendSignal(sender, NO_WAIT, reply, true);
+ NdbMutex_Unlock(m_configMutex);
+ return;
+}
+
+void
+MgmtSrvr::handle_MGM_UNLOCK_CONFIG_REQ(NdbApiSignal *signal) {
+ NodeId sender = refToNode(signal->theSendersBlockRef);
+ const MgmUnlockConfigReq * const req = CAST_CONSTPTR(MgmUnlockConfigReq, signal->getDataPtr());
+ MgmUnlockConfigRep *unlockRep;
+
+ NdbApiSignal *reply = getSignal();
+ if(signal == NULL)
+ goto error; /** @todo handle allocation failure */
+
+ reply->set(TestOrd::TraceAPI,
+ MGMSRV,
+ GSN_MGM_UNLOCK_CONFIG_REP,
+ MgmUnlockConfigRep::SignalLength);
+
+ unlockRep = CAST_PTR(MgmUnlockConfigRep, reply->getDataPtrSend());
+
+ unlockRep->errorCode = MgmUnlockConfigRep::UNKNOWN_ERROR;
+
+
+ NdbMutex_Lock(m_configMutex);
+
+ if(req->commitConfig == 1) {
+ m_newConfig = fetchConfig();
+ commitConfig();
+ } else
+ rollbackConfig();
+
+ unlockRep->errorCode = MgmUnlockConfigRep::OK;
+
+ sendSignal(sender, NO_WAIT, reply, true);
+ error:
+ NdbMutex_Unlock(m_configMutex);
+ return;
+}
+
+
+/**
+ * Prepare all MGM nodes for configuration changes
+ *
+ * @returns 0 on success, or -1 on failure
+ */
+int
+MgmtSrvr::lockConf() {
+ int result = -1;
+ MgmLockConfigReq* lockReq;
+ NodeId node = 0;
+
+ /* Check if this is the master node */
+ if(getPrimaryNode() != _ownNodeId)
+ goto done;
+
+ if(NdbMutex_Trylock(m_configMutex) != 0)
+ return -1;
+
+ m_newConfig = new Config(*_config); /* copy the existing config */
+ _config = m_newConfig;
+
+ m_newConfig = new Config(*_config);
+
+ m_nextConfigGenerationNumber++;
+
+ /* Make sure the new configuration _always_ is at least one step older */
+ if(m_nextConfigGenerationNumber < m_newConfig->getGenerationNumber()+1)
+ m_nextConfigGenerationNumber = _config->getGenerationNumber()+1;
+
+ m_newConfig->setGenerationNumber(m_nextConfigGenerationNumber);
+
+ node = 0;
+ while(getNextNodeId(&node, NDB_MGM_NODE_TYPE_MGM)) {
+ if(node != _ownNodeId) {
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ result = COULD_NOT_ALLOCATE_MEMORY;
+ goto done;
+ }
+
+ lockReq = CAST_PTR(MgmLockConfigReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI,
+ MGMSRV,
+ GSN_MGM_LOCK_CONFIG_REQ,
+ MgmLockConfigReq::SignalLength);
+
+ lockReq->newConfigGeneration = m_nextConfigGenerationNumber;
+
+ result = sendSignal(node, NO_WAIT, signal, true);
+
+ NdbApiSignal *reply =
+ m_signalRecvQueue.waitFor(GSN_MGM_LOCK_CONFIG_REP, 0);
+
+ if(reply == NULL) {
+ /** @todo handle timeout/error */
+ ndbout << __FILE__ << ":" << __LINE__ << endl;
+ result = -1;
+ goto done;
+ }
+
+ }
+ }
+
+ done:
+ NdbMutex_Unlock(m_configMutex);
+ return result;
+}
+
+/**
+ * Unlocks configuration
+ *
+ * @returns 0 on success, ! 0 on error
+ */
+int
+MgmtSrvr::unlockConf(bool commit) {
+ int result = -1;
+ MgmUnlockConfigReq* unlockReq;
+ NodeId node = 0;
+
+ /* Check if this is the master node */
+ if(getPrimaryNode() != _ownNodeId)
+ goto done;
+
+ errno = 0;
+ if(NdbMutex_Lock(m_configMutex) != 0)
+ return -1;
+
+ if(commit)
+ commitConfig();
+ else
+ rollbackConfig();
+
+ node = 0;
+ while(getNextNodeId(&node, NDB_MGM_NODE_TYPE_MGM)) {
+ if(node != _ownNodeId) {
+ NdbApiSignal* signal = getSignal();
+ if (signal == NULL) {
+ result = COULD_NOT_ALLOCATE_MEMORY;
+ goto done;
+ }
+
+ unlockReq = CAST_PTR(MgmUnlockConfigReq, signal->getDataPtrSend());
+ signal->set(TestOrd::TraceAPI,
+ MGMSRV,
+ GSN_MGM_UNLOCK_CONFIG_REQ,
+ MgmUnlockConfigReq::SignalLength);
+ unlockReq->commitConfig = commit;
+
+ result = sendSignal(node, NO_WAIT, signal, true);
+
+ NdbApiSignal *reply =
+ m_signalRecvQueue.waitFor(GSN_MGM_UNLOCK_CONFIG_REP, 0);
+
+ if(reply == NULL) {
+ /** @todo handle timeout/error */
+ result = -1;
+ goto done;
+ }
+
+ }
+ }
+
+ done:
+ NdbMutex_Unlock(m_configMutex);
+ return result;
+}
+
+/**
+ * Commit the new configuration
+ */
+int
+MgmtSrvr::commitConfig() {
+ int ret = saveConfig(m_newConfig);
+ delete _config;
+ _config = m_newConfig;
+ m_newConfig = NULL;
+ ndbout << "commit " << ret << endl;
+ return ret;
+}
+
+/**
+ * Rollback to the old configuration
+ */
+int
+MgmtSrvr::rollbackConfig() {
+ delete m_newConfig;
+ m_newConfig = NULL;
+ ndbout << "rollback" << endl;
+ return saveConfig(_config);
+}
+
+/**
+ * Save a configuration to the running configuration file
+ */
+int
+MgmtSrvr::saveConfig(const Config *conf) {
+ BaseString newfile;
+ newfile.appfmt("%s.new", m_configFilename.c_str());
+
+ /* Open and write to the new config file */
+ FILE *f = fopen(newfile.c_str(), "w");
+ if(f == NULL) {
+ /** @todo Send something apropriate to the log */
+ return -1;
+ }
+ FileOutputStream stream(f);
+ conf->printConfigFile(stream);
+
+ fclose(f);
+
+ /* Rename file to real name */
+ rename(newfile.c_str(), m_configFilename.c_str());
+
+ return 0;
+}
+
+Config *
+MgmtSrvr::readConfig() {
+ Config *conf;
+ InitConfigFileParser parser;
+ conf = parser.parseConfig(m_configFilename.c_str());
+ return conf;
+}
+
+Config *
+MgmtSrvr::fetchConfig() {
+ struct ndb_mgm_configuration * tmp = m_config_retriever->getConfig();
+ if(tmp != 0){
+ Config * conf = new Config();
+ conf->m_configValues = tmp;
+ return conf;
+ }
+ return 0;
+}
+
+bool
+MgmtSrvr::changeConfig(const BaseString &section,
+ const BaseString &param,
+ const BaseString &value) {
+ if(m_newConfig == NULL)
+ return false;
+ return m_newConfig->change(section, param, value);
+}
diff --git a/storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp b/storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp
new file mode 100644
index 00000000000..2126c9d358d
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp
@@ -0,0 +1,140 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//******************************************************************************
+// General signal handling methods
+// All implementations stolen from the Ndb class.
+// Some kind of reuse should be preferred.
+//******************************************************************************
+
+#include "MgmtSrvr.hpp"
+#include <NdbApiSignal.hpp>
+#include <NdbTick.h>
+
+
+NdbApiSignal*
+MgmtSrvr::getSignal()
+{
+ NdbApiSignal* tSignal;
+ tSignal = theSignalIdleList;
+ if (tSignal != NULL){
+ NdbApiSignal* tSignalNext = tSignal->next();
+ tSignal->next(NULL);
+ theSignalIdleList = tSignalNext;
+ return tSignal;
+ } else
+ {
+ tSignal = new NdbApiSignal(_ownReference);
+ if (tSignal != NULL)
+ tSignal->next(NULL);
+ }
+ return tSignal;
+}
+
+
+void
+MgmtSrvr::releaseSignal(NdbApiSignal* aSignal)
+{
+ aSignal->next(theSignalIdleList);
+ theSignalIdleList = aSignal;
+}
+
+
+void
+MgmtSrvr::freeSignal()
+{
+ NdbApiSignal* tSignal = theSignalIdleList;
+ theSignalIdleList = tSignal->next();
+ delete tSignal;
+}
+
+
+int
+MgmtSrvr::sendSignal(Uint16 aNodeId,
+ WaitSignalType aWaitState,
+ NdbApiSignal* aSignal,
+ bool force)
+{
+ int tReturnCode;
+ theFacade->lock_mutex();
+ if(force){
+ tReturnCode = theFacade->sendSignalUnCond(aSignal,
+ aNodeId);
+ } else {
+ tReturnCode = theFacade->sendSignal(aSignal,
+ aNodeId);
+ }
+ releaseSignal(aSignal);
+ if (tReturnCode == -1) {
+ theFacade->unlock_mutex();
+ return -1;
+ }
+ theWaitState = aWaitState;
+ theFacade->unlock_mutex();
+ return 0;
+}
+
+
+int
+MgmtSrvr::sendRecSignal(Uint16 aNodeId,
+ WaitSignalType aWaitState,
+ NdbApiSignal* aSignal,
+ bool force,
+ int waitTime)
+{
+ int tReturnCode;
+ theFacade->lock_mutex();
+ if(force){
+ tReturnCode = theFacade->sendSignalUnCond(aSignal, aNodeId);
+ } else {
+ tReturnCode = theFacade->sendSignalUnCond(aSignal, aNodeId);
+ }
+ releaseSignal(aSignal);
+ if (tReturnCode == -1) {
+ theFacade->unlock_mutex();
+ return -1;
+ }
+ theWaitState = aWaitState;
+ return receiveOptimisedResponse(waitTime);
+}
+
+
+int
+MgmtSrvr::receiveOptimisedResponse(int waitTime)
+{
+ int tResultCode;
+ theFacade->checkForceSend(_blockNumber);
+ NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime;
+
+ while (theWaitState != NO_WAIT && waitTime > 0) {
+ NdbCondition_WaitTimeout(theMgmtWaitForResponseCondPtr,
+ theFacade->theMutexPtr,
+ waitTime);
+ if(theWaitState == NO_WAIT)
+ break;
+ waitTime = (maxTime - NdbTick_CurrentMillisecond());
+ }//while
+
+ if(theWaitState == NO_WAIT) {
+ tResultCode = 0;
+ } else {
+ tResultCode = -1;
+ }
+ theFacade->unlock_mutex();
+ return tResultCode;
+}
+
+
diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp
new file mode 100644
index 00000000000..bbd3db1e734
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/Services.cpp
@@ -0,0 +1,1568 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ctype.h>
+
+#include <uucode.h>
+#include <socket_io.h>
+#include <ndb_version.h>
+#include <mgmapi.h>
+#include <EventLogger.hpp>
+#include <signaldata/SetLogLevelOrd.hpp>
+#include <LogLevel.hpp>
+#include <BaseString.hpp>
+#include <Base64.hpp>
+
+#include <ConfigValues.hpp>
+#include <mgmapi_configuration.hpp>
+#include <Vector.hpp>
+#include "Services.hpp"
+#include "../mgmapi/ndb_logevent.hpp"
+
+extern bool g_StopServer;
+
+static const unsigned int MAX_READ_TIMEOUT = 1000 ;
+static const unsigned int MAX_WRITE_TIMEOUT = 100 ;
+
+/**
+ const char * name;
+ const char * realName;
+ const Type type;
+ const ArgType argType;
+ const ArgRequired argRequired;
+ const ArgMinMax argMinMax;
+ const int minVal;
+ const int maxVal;
+ void (T::* function)(const class Properties & args);
+ const char * description;
+*/
+
+#define MGM_CMD(name, fun, desc) \
+ { name, \
+ 0, \
+ ParserRow<MgmApiSession>::Cmd, \
+ ParserRow<MgmApiSession>::String, \
+ ParserRow<MgmApiSession>::Optional, \
+ ParserRow<MgmApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ fun, \
+ desc, 0 }
+
+#define MGM_ARG(name, type, opt, desc) \
+ { name, \
+ 0, \
+ ParserRow<MgmApiSession>::Arg, \
+ ParserRow<MgmApiSession>::type, \
+ ParserRow<MgmApiSession>::opt, \
+ ParserRow<MgmApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ desc, 0 }
+
+#define MGM_ARG2(name, type, opt, min, max, desc) \
+ { name, \
+ 0, \
+ ParserRow<MgmApiSession>::Arg, \
+ ParserRow<MgmApiSession>::type, \
+ ParserRow<MgmApiSession>::opt, \
+ ParserRow<MgmApiSession>::IgnoreMinMax, \
+ min, max, \
+ 0, \
+ desc, 0 }
+
+#define MGM_END() \
+ { 0, \
+ 0, \
+ ParserRow<MgmApiSession>::Arg, \
+ ParserRow<MgmApiSession>::Int, \
+ ParserRow<MgmApiSession>::Optional, \
+ ParserRow<MgmApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+#define MGM_CMD_ALIAS(name, realName, fun) \
+ { name, \
+ realName, \
+ ParserRow<MgmApiSession>::CmdAlias, \
+ ParserRow<MgmApiSession>::Int, \
+ ParserRow<MgmApiSession>::Optional, \
+ ParserRow<MgmApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+#define MGM_ARG_ALIAS(name, realName, fun) \
+ { name, \
+ realName, \
+ ParserRow<MgmApiSession>::ArgAlias, \
+ ParserRow<MgmApiSession>::Int, \
+ ParserRow<MgmApiSession>::Optional, \
+ ParserRow<MgmApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+const
+ParserRow<MgmApiSession> commands[] = {
+ MGM_CMD("get statport", &MgmApiSession::getStatPort, ""),
+
+ MGM_CMD("get config", &MgmApiSession::getConfig, ""),
+ MGM_ARG("version", Int, Mandatory, "Configuration version number"),
+ MGM_ARG("node", Int, Optional, "Node ID"),
+
+ MGM_CMD("get nodeid", &MgmApiSession::get_nodeid, ""),
+ MGM_ARG("version", Int, Mandatory, "Configuration version number"),
+ MGM_ARG("nodetype", Int, Mandatory, "Node type"),
+ MGM_ARG("transporter", String, Optional, "Transporter type"),
+ MGM_ARG("nodeid", Int, Optional, "Node ID"),
+ MGM_ARG("user", String, Mandatory, "Password"),
+ MGM_ARG("password", String, Mandatory, "Password"),
+ MGM_ARG("public key", String, Mandatory, "Public key"),
+ MGM_ARG("endian", String, Optional, "Endianness"),
+
+ MGM_CMD("get version", &MgmApiSession::getVersion, ""),
+
+ MGM_CMD("get status", &MgmApiSession::getStatus, ""),
+
+ MGM_CMD("get info clusterlog", &MgmApiSession::getInfoClusterLog, ""),
+
+ MGM_CMD("restart node", &MgmApiSession::restart, ""),
+ MGM_ARG("node", String, Mandatory, "Nodes to restart"),
+ MGM_ARG("initialstart", Int, Optional, "Initial start"),
+ MGM_ARG("nostart", Int, Optional, "No start"),
+ MGM_ARG("abort", Int, Optional, "Abort"),
+
+ MGM_CMD("restart all", &MgmApiSession::restartAll, ""),
+ MGM_ARG("initialstart", Int, Optional, "Initial start"),
+ MGM_ARG("nostart", Int, Optional, "No start"),
+ MGM_ARG("abort", Int, Optional, "Abort"),
+
+ MGM_CMD("insert error", &MgmApiSession::insertError, ""),
+ MGM_ARG("node", Int, Mandatory, "Node to receive error"),
+ MGM_ARG("error", Int, Mandatory, "Errorcode to insert"),
+
+ MGM_CMD("set trace", &MgmApiSession::setTrace, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+ MGM_ARG("trace", Int, Mandatory, "Trace number"),
+
+ MGM_CMD("log signals", &MgmApiSession::logSignals, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+ MGM_ARG("blocks", String, Mandatory, "Blocks (space separated)"),
+ MGM_ARG("in", Int, Mandatory, "Log input signals"),
+ MGM_ARG("out", Int, Mandatory, "Log output signals"),
+
+ MGM_CMD("start signallog", &MgmApiSession::startSignalLog, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+
+ MGM_CMD("stop signallog", &MgmApiSession::stopSignalLog, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+
+ MGM_CMD("dump state", &MgmApiSession::dumpState, ""),
+ MGM_ARG("node", Int, Mandatory ,"Node"),
+ MGM_ARG("args", String, Mandatory, "Args(space separated int's)"),
+
+ MGM_CMD("start backup", &MgmApiSession::startBackup, ""),
+ MGM_ARG("completed", Int, Optional ,"Wait until completed"),
+
+ MGM_CMD("abort backup", &MgmApiSession::abortBackup, ""),
+ MGM_ARG("id", Int, Mandatory, "Backup id"),
+
+ /**
+ * Global Replication
+ */
+ MGM_CMD("rep", &MgmApiSession::repCommand, ""),
+ MGM_ARG("request", Int, Mandatory, "Command"),
+
+ MGM_CMD("stop", &MgmApiSession::stop, ""),
+ MGM_ARG("node", String, Mandatory, "Node"),
+ MGM_ARG("abort", Int, Mandatory, "Node"),
+
+ MGM_CMD("stop all", &MgmApiSession::stopAll, ""),
+ MGM_ARG("abort", Int, Mandatory, "Node"),
+
+ MGM_CMD("enter single user", &MgmApiSession::enterSingleUser, ""),
+ MGM_ARG("nodeId", Int, Mandatory, "Node"),
+
+ MGM_CMD("exit single user", &MgmApiSession::exitSingleUser, ""),
+
+
+ MGM_CMD("start", &MgmApiSession::start, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+
+ MGM_CMD("start all", &MgmApiSession::startAll, ""),
+
+ MGM_CMD("bye", &MgmApiSession::bye, ""),
+
+ MGM_CMD("set loglevel", &MgmApiSession::setLogLevel, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+ MGM_ARG("category", Int, Mandatory, "Event category"),
+ MGM_ARG("level", Int, Mandatory, "Log level (0-15)"),
+
+ MGM_CMD("set cluster loglevel", &MgmApiSession::setClusterLogLevel, ""),
+ MGM_ARG("node", Int, Mandatory, "Node"),
+ MGM_ARG("category", Int, Mandatory, "Event category"),
+ MGM_ARG("level", Int, Mandatory, "Log level (0-15)"),
+
+ MGM_CMD("set logfilter", &MgmApiSession::setLogFilter, ""),
+ MGM_ARG("level", Int, Mandatory, "Severety level"),
+ MGM_ARG("enable", Int, Mandatory, "1=disable, 0=enable, -1=toggle"),
+
+ MGM_CMD("config lock", &MgmApiSession::configLock, ""),
+
+ MGM_CMD("config unlock", &MgmApiSession::configUnlock, ""),
+ MGM_ARG("commit", Int, Mandatory, "Commit changes"),
+
+ MGM_CMD("config change", &MgmApiSession::configChange, ""),
+ MGM_ARG("section", String, Mandatory, "Section"),
+ MGM_ARG("parameter", String, Mandatory, "Parameter"),
+ MGM_ARG("value", String, Mandatory, "Value"),
+
+ MGM_CMD("config lock", &MgmApiSession::configLock, ""),
+
+ MGM_CMD("config unlock", &MgmApiSession::configUnlock, ""),
+ MGM_ARG("commit", Int, Mandatory, "Commit changes"),
+
+ MGM_CMD("set parameter", &MgmApiSession::setParameter, ""),
+ MGM_ARG("node", String, Mandatory, "Node"),
+ MGM_ARG("parameter", String, Mandatory, "Parameter"),
+ MGM_ARG("value", String, Mandatory, "Value"),
+
+ MGM_CMD("set connection parameter",
+ &MgmApiSession::setConnectionParameter, ""),
+ MGM_ARG("node1", String, Mandatory, "Node1 ID"),
+ MGM_ARG("node2", String, Mandatory, "Node2 ID"),
+ MGM_ARG("param", String, Mandatory, "Parameter"),
+ MGM_ARG("value", String, Mandatory, "Value"),
+
+ MGM_CMD("get connection parameter",
+ &MgmApiSession::getConnectionParameter, ""),
+ MGM_ARG("node1", String, Mandatory, "Node1 ID"),
+ MGM_ARG("node2", String, Mandatory, "Node2 ID"),
+ MGM_ARG("param", String, Mandatory, "Parameter"),
+
+ MGM_CMD("listen event", &MgmApiSession::listen_event, ""),
+ MGM_ARG("node", Int, Optional, "Node"),
+ MGM_ARG("parsable", Int, Optional, "Parsable"),
+ MGM_ARG("filter", String, Mandatory, "Event category"),
+
+ MGM_CMD("purge stale sessions", &MgmApiSession::purge_stale_sessions, ""),
+
+ MGM_CMD("check connection", &MgmApiSession::check_connection, ""),
+
+ MGM_CMD("transporter connect", &MgmApiSession::transporter_connect, ""),
+
+ MGM_CMD("get mgmd nodeid", &MgmApiSession::get_mgmd_nodeid, ""),
+
+ MGM_END()
+};
+
+MgmApiSession::MgmApiSession(class MgmtSrvr & mgm, NDB_SOCKET_TYPE sock)
+ : SocketServer::Session(sock), m_mgmsrv(mgm) {
+ m_input = new SocketInputStream(sock);
+ m_output = new SocketOutputStream(sock);
+ m_parser = new Parser_t(commands, *m_input, true, true, true);
+ m_allocated_resources= new MgmtSrvr::Allocated_resources(m_mgmsrv);
+}
+
+MgmApiSession::~MgmApiSession()
+{
+ if (m_input)
+ delete m_input;
+ if (m_output)
+ delete m_output;
+ if (m_parser)
+ delete m_parser;
+ if (m_allocated_resources)
+ delete m_allocated_resources;
+}
+
+void
+MgmApiSession::runSession() {
+ Parser_t::Context ctx;
+ while(!m_stop) {
+ m_parser->run(ctx, *this);
+
+ if(ctx.m_currentToken == 0)
+ break;
+
+ switch(ctx.m_status) {
+ case Parser_t::UnknownCommand:
+#ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT
+ /* Backwards compatibility for old NDBs that still use
+ * the old "GET CONFIG" command.
+ */
+ size_t i;
+ for(i=0; i<strlen(ctx.m_currentToken); i++)
+ ctx.m_currentToken[i] = toupper(ctx.m_currentToken[i]);
+
+ if(strncmp("GET CONFIG ",
+ ctx.m_currentToken,
+ strlen("GET CONFIG ")) == 0)
+ getConfig_old(ctx);
+#endif /* MGM_GET_CONFIG_BACKWARDS_COMPAT */
+ break;
+ default:
+ break;
+ }
+ }
+ if(m_socket != NDB_INVALID_SOCKET)
+ NDB_CLOSE_SOCKET(m_socket);
+}
+
+#ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT
+void
+MgmApiSession::getConfig_old(Parser_t::Context &ctx) {
+ Properties args;
+
+ Uint32 version, node;
+
+ if(sscanf(ctx.m_currentToken, "GET CONFIG %d %d",
+ (int *)&version, (int *)&node) != 2) {
+ m_output->println("Expected 2 arguments for GET CONFIG");
+ return;
+ }
+
+ /* Put arguments in properties object so we can call the real function */
+ args.put("version", version);
+ args.put("node", node);
+ getConfig_common(ctx, args, true);
+}
+#endif /* MGM_GET_CONFIG_BACKWARDS_COMPAT */
+
+inline void require(bool b){ if(!b) abort(); }
+
+void
+MgmApiSession::getConfig(Parser_t::Context &ctx,
+ const class Properties &args) {
+ getConfig_common(ctx, args);
+}
+
+static Properties *
+backward(const char * base, const Properties* reply){
+ Properties * ret = new Properties();
+ Properties::Iterator it(reply);
+ for(const char * name = it.first(); name != 0; name=it.next()){
+ PropertiesType type;
+ reply->getTypeOf(name, &type);
+ switch(type){
+ case PropertiesType_Uint32:{
+ Uint32 val;
+ reply->get(name, &val);
+ ret->put(name, val);
+ }
+ break;
+ case PropertiesType_char:
+ {
+ const char * val;
+ reply->get(name, &val);
+ ret->put(name, val);
+ if(!strcmp(name, "Type") && !strcmp(val, "DB")){
+ ret->put("NoOfDiskBufferPages", (unsigned)0);
+ ret->put("NoOfDiskFiles", (unsigned)0);
+ ret->put("NoOfDiskClusters", (unsigned)0);
+ ret->put("NoOfFreeDiskClusters", (unsigned)0);
+ ret->put("NoOfDiskClustersPerDiskFile", (unsigned)0);
+ ret->put("NoOfConcurrentCheckpointsDuringRestart", (unsigned)1);
+ ret->put("NoOfConcurrentCheckpointsAfterRestart", (unsigned)1);
+ ret->put("NoOfConcurrentProcessesHandleTakeover", (unsigned)1);
+ }
+ }
+ break;
+ case PropertiesType_Properties:
+ {
+ const Properties * recurse;
+ reply->get(name, &recurse);
+ Properties * val = backward(name, recurse);
+ ret->put(name, val);
+ }
+ break;
+ case PropertiesType_Uint64:
+ break;
+ }
+ }
+ return ret;
+}
+
+void
+MgmApiSession::get_nodeid(Parser_t::Context &,
+ const class Properties &args)
+{
+ const char *cmd= "get nodeid reply";
+ Uint32 version, nodeid= 0, nodetype= 0xff;
+ const char * transporter;
+ const char * user;
+ const char * password;
+ const char * public_key;
+ const char * endian= NULL;
+ union { long l; char c[sizeof(long)]; } endian_check;
+
+ args.get("version", &version);
+ args.get("nodetype", &nodetype);
+ args.get("transporter", &transporter);
+ args.get("nodeid", &nodeid);
+ args.get("user", &user);
+ args.get("password", &password);
+ args.get("public key", &public_key);
+ args.get("endian", &endian);
+
+ endian_check.l = 1;
+ if(endian
+ && strcmp(endian,(endian_check.c[sizeof(long)-1])?"big":"little")!=0) {
+ m_output->println(cmd);
+ m_output->println("result: Node does not have the same endianness as the management server.");
+ m_output->println("");
+ return;
+ }
+
+ bool compatible;
+ switch (nodetype) {
+ case NODE_TYPE_MGM:
+ case NODE_TYPE_API:
+ compatible = ndbCompatible_mgmt_api(NDB_VERSION, version);
+ break;
+ case NODE_TYPE_DB:
+ compatible = ndbCompatible_mgmt_ndb(NDB_VERSION, version);
+ break;
+ default:
+ m_output->println(cmd);
+ m_output->println("result: unknown nodetype %d", nodetype);
+ m_output->println("");
+ return;
+ }
+
+ struct sockaddr addr;
+ SOCKET_SIZE_TYPE addrlen= sizeof(addr);
+ int r = getpeername(m_socket, &addr, &addrlen);
+ if (r != 0 ) {
+ m_output->println(cmd);
+ m_output->println("result: getpeername(%d) failed, err= %d", m_socket, r);
+ m_output->println("");
+ return;
+ }
+
+ NodeId tmp= nodeid;
+ if(tmp == 0 || !m_allocated_resources->is_reserved(tmp)){
+ BaseString error_string;
+ if (!m_mgmsrv.alloc_node_id(&tmp, (enum ndb_mgm_node_type)nodetype,
+ &addr, &addrlen, error_string)){
+ const char *alias;
+ const char *str;
+ alias= ndb_mgm_get_node_type_alias_string((enum ndb_mgm_node_type)
+ nodetype, &str);
+ m_output->println(cmd);
+ m_output->println("result: %s", error_string.c_str());
+ m_output->println("");
+ return;
+ }
+ }
+
+#if 0
+ if (!compatible){
+ m_output->println(cmd);
+ m_output->println("result: incompatible version mgmt 0x%x and node 0x%x",
+ NDB_VERSION, version);
+ m_output->println("");
+ return;
+ }
+#endif
+
+ m_output->println(cmd);
+ m_output->println("nodeid: %u", tmp);
+ m_output->println("result: Ok");
+ m_output->println("");
+ m_allocated_resources->reserve_node(tmp);
+
+ return;
+}
+
+void
+MgmApiSession::getConfig_common(Parser_t::Context &,
+ const class Properties &args,
+ bool compat) {
+ Uint32 version, node = 0;
+
+ args.get("version", &version);
+ args.get("node", &node);
+
+ const Config *conf = m_mgmsrv.getConfig();
+ if(conf == NULL) {
+ m_output->println("get config reply");
+ m_output->println("result: Could not fetch configuration");
+ m_output->println("");
+ return;
+ }
+
+ if(version > 0 && version < makeVersion(3, 5, 0) && compat){
+ Properties *reply = backward("", conf->m_oldConfig);
+ reply->put("Version", version);
+ reply->put("LocalNodeId", node);
+
+ backward("", reply);
+ //reply->print();
+
+ const Uint32 size = reply->getPackedSize();
+ Uint32 *buffer = new Uint32[size/4+1];
+
+ reply->pack(buffer);
+ delete reply;
+
+ const int uurows = (size + 44)/45;
+ char * uubuf = new char[uurows * 62+5];
+
+ const int uusz = uuencode_mem(uubuf, (char *)buffer, size);
+ delete[] buffer;
+
+ m_output->println("GET CONFIG %d %d %d %d %d",
+ 0, version, node, size, uusz);
+
+ m_output->println("begin 664 Ndb_cfg.bin");
+
+ /* XXX Need to write directly to the socket, because the uubuf is not
+ * NUL-terminated. This could/should probably be done in a nicer way.
+ */
+ write_socket(m_socket, MAX_WRITE_TIMEOUT, uubuf, uusz);
+ delete[] uubuf;
+
+ m_output->println("end");
+ m_output->println("");
+ return;
+ }
+
+ if(compat){
+ m_output->println("GET CONFIG %d %d %d %d %d",1, version, 0, 0, 0);
+ return;
+ }
+
+ if(node != 0){
+ bool compatible;
+ switch (m_mgmsrv.getNodeType(node)) {
+ case NDB_MGM_NODE_TYPE_NDB:
+ compatible = ndbCompatible_mgmt_ndb(NDB_VERSION, version);
+ break;
+ case NDB_MGM_NODE_TYPE_API:
+ case NDB_MGM_NODE_TYPE_MGM:
+ compatible = ndbCompatible_mgmt_api(NDB_VERSION, version);
+ break;
+ default:
+ m_output->println("get config");
+ m_output->println("result: unrecognignized node type");
+ m_output->println("");
+ return;
+ }
+
+ if (!compatible){
+ m_output->println("get config");
+ m_output->println("result: incompatible version mgmt 0x%x and node 0x%x",
+ NDB_VERSION, version);
+ m_output->println("");
+ return;
+ }
+ }
+
+ NdbMutex_Lock(m_mgmsrv.m_configMutex);
+ const ConfigValues * cfg = &conf->m_configValues->m_config;
+ const Uint32 size = cfg->getPackedSize();
+
+ UtilBuffer src;
+ cfg->pack(src);
+ NdbMutex_Unlock(m_mgmsrv.m_configMutex);
+
+ BaseString str;
+ int res = base64_encode(src, str);
+
+ m_output->println("get config reply");
+ m_output->println("result: Ok");
+ m_output->println("Content-Length: %d", str.length());
+ m_output->println("Content-Type: ndbconfig/octet-stream");
+ m_output->println("Content-Transfer-Encoding: base64");
+ m_output->println("");
+ m_output->println(str.c_str());
+
+ return;
+}
+
+void
+MgmApiSession::getStatPort(Parser_t::Context &,
+ const class Properties &) {
+
+ m_output->println("get statport reply");
+ m_output->println("tcpport: %d", 0);
+ m_output->println("");
+}
+
+void
+MgmApiSession::insertError(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node = 0, error = 0;
+
+ args.get("node", &node);
+ args.get("error", &error);
+
+ int result = m_mgmsrv.insertError(node, error);
+
+ m_output->println("insert error reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::setTrace(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node = 0, trace = 0;
+
+ args.get("node", &node);
+ args.get("trace", &trace);
+
+ int result = m_mgmsrv.setTraceNo(node, trace);
+
+ m_output->println("set trace reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::getVersion(Parser<MgmApiSession>::Context &,
+ Properties const &) {
+ m_output->println("version");
+ m_output->println("id: %d", NDB_VERSION);
+ m_output->println("major: %d", getMajor(NDB_VERSION));
+ m_output->println("minor: %d", getMinor(NDB_VERSION));
+ m_output->println("string: %s", NDB_VERSION_STRING);
+ m_output->println("");
+}
+
+void
+MgmApiSession::startBackup(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ DBUG_ENTER("MgmApiSession::startBackup");
+ unsigned backupId;
+ Uint32 completed= 2;
+ int result;
+
+ args.get("completed", &completed);
+
+ result = m_mgmsrv.startBackup(backupId, completed);
+
+ m_output->println("start backup reply");
+ if(result != 0)
+ {
+ m_output->println("result: %s", get_error_text(result));
+ }
+ else{
+ m_output->println("result: Ok");
+ m_output->println("id: %d", backupId);
+ }
+ m_output->println("");
+ DBUG_VOID_RETURN;
+}
+
+void
+MgmApiSession::abortBackup(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 id = 0;
+
+ args.get("id", &id);
+
+ int result = m_mgmsrv.abortBackup(id);
+
+ m_output->println("abort backup reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+/*****************************************************************************
+ * Global Replication
+ *****************************************************************************/
+
+void
+MgmApiSession::repCommand(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+
+ Uint32 request = 0;
+ args.get("request", &request);
+
+ Uint32 repReqId;
+ int result = m_mgmsrv.repCommand(&repReqId, request, true);
+
+ m_output->println("global replication reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else{
+ m_output->println("result: Ok");
+ m_output->println("id: %d", repReqId);
+ }
+ m_output->println("");
+}
+
+/*****************************************************************************/
+
+void
+MgmApiSession::dumpState(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node;
+ BaseString args_str;
+
+ args.get("node", &node);
+ args.get("args", args_str);
+
+ int result = m_mgmsrv.dumpState(node, args_str.c_str());
+ m_output->println("dump state reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+
+void
+MgmApiSession::bye(Parser<MgmApiSession>::Context &,
+ Properties const &) {
+ m_stop = true;
+}
+
+void
+MgmApiSession::setClusterLogLevel(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ const char *reply= "set cluster loglevel reply";
+ Uint32 node, level, cat;
+ BaseString errorString;
+ SetLogLevelOrd logLevel;
+ int result;
+ DBUG_ENTER("MgmApiSession::setClusterLogLevel");
+ args.get("node", &node);
+ args.get("category", &cat);
+ args.get("level", &level);
+
+ DBUG_PRINT("enter",("node=%d, category=%d, level=%d", node, cat, level));
+
+ /* XXX should use constants for this value */
+ if(level > 15) {
+ m_output->println(reply);
+ m_output->println("result: Invalid loglevel %d", level);
+ m_output->println("");
+ DBUG_VOID_RETURN;
+ }
+
+ LogLevel::EventCategory category=
+ (LogLevel::EventCategory)(cat-(int)CFG_MIN_LOGLEVEL);
+
+ m_mgmsrv.m_event_listner.lock();
+ if (m_mgmsrv.m_event_listner[0].m_logLevel.setLogLevel(category,level))
+ {
+ m_output->println(reply);
+ m_output->println("result: Invalid category %d", category);
+ m_output->println("");
+ m_mgmsrv.m_event_listner.unlock();
+ DBUG_VOID_RETURN;
+ }
+ m_mgmsrv.m_event_listner.unlock();
+
+ {
+ LogLevel ll;
+ ll.setLogLevel(category,level);
+ m_mgmsrv.m_event_listner.update_max_log_level(ll);
+ }
+
+ m_output->println(reply);
+ m_output->println("result: Ok");
+ m_output->println("");
+ DBUG_VOID_RETURN;
+}
+
+void
+MgmApiSession::setLogLevel(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node = 0, level = 0, cat;
+ BaseString errorString;
+ SetLogLevelOrd logLevel;
+ int result;
+ logLevel.clear();
+ args.get("node", &node);
+ args.get("category", &cat);
+ args.get("level", &level);
+
+ /* XXX should use constants for this value */
+ if(level > 15) {
+ m_output->println("set loglevel reply");
+ m_output->println("result: Invalid loglevel", errorString.c_str());
+ m_output->println("");
+ return;
+ }
+
+ LogLevel::EventCategory category=
+ (LogLevel::EventCategory)(cat-(int)CFG_MIN_LOGLEVEL);
+
+ {
+ LogLevel ll;
+ ll.setLogLevel(category,level);
+ m_mgmsrv.m_event_listner.update_max_log_level(ll);
+ }
+
+ m_output->println("set loglevel reply");
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::stopSignalLog(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node;
+
+ args.get("node", &node);
+
+ int result = m_mgmsrv.stopSignalTracing(node);
+
+ m_output->println("stop signallog");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::restart(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32
+ nostart = 0,
+ initialstart = 0,
+ abort = 0;
+ char *nodes_str;
+ Vector<NodeId> nodes;
+
+ args.get("initialstart", &initialstart);
+ args.get("nostart", &nostart);
+ args.get("abort", &abort);
+ args.get("node", (const char **)&nodes_str);
+
+ char *p, *last;
+ for((p = strtok_r(nodes_str, " ", &last));
+ p;
+ (p = strtok_r(NULL, " ", &last))) {
+ nodes.push_back(atoi(p));
+ }
+
+ int restarted = 0;
+ int result = 0;
+
+ for(size_t i = 0; i < nodes.size(); i++)
+ if((result = m_mgmsrv.restartNode(nodes[i],
+ nostart != 0,
+ initialstart != 0,
+ abort != 0)) == 0)
+ restarted++;
+
+ m_output->println("restart reply");
+ if(result != 0){
+ m_output->println("result: %d-%s", result, get_error_text(result));
+ } else
+ m_output->println("result: Ok");
+ m_output->println("restarted: %d", restarted);
+ m_output->println("");
+}
+
+void
+MgmApiSession::restartAll(Parser<MgmApiSession>::Context &,
+ Properties const &args)
+{
+ Uint32 nostart = 0;
+ Uint32 initialstart = 0;
+ Uint32 abort = 0;
+
+ args.get("initialstart", &initialstart);
+ args.get("abort", &abort);
+ args.get("nostart", &nostart);
+
+ int count = 0;
+ int result = m_mgmsrv.restart(nostart, initialstart, abort, &count);
+
+ m_output->println("restart reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("restarted: %d", count);
+ m_output->println("");
+}
+
+static void
+printNodeStatus(OutputStream *output,
+ MgmtSrvr &mgmsrv,
+ enum ndb_mgm_node_type type) {
+ NodeId nodeId = 0;
+ while(mgmsrv.getNextNodeId(&nodeId, type)) {
+ enum ndb_mgm_node_status status;
+ Uint32 startPhase = 0,
+ version = 0,
+ dynamicId = 0,
+ nodeGroup = 0,
+ connectCount = 0;
+ bool system;
+ mgmsrv.status(nodeId, &status, &version, &startPhase,
+ &system, &dynamicId, &nodeGroup, &connectCount);
+ output->println("node.%d.type: %s",
+ nodeId,
+ ndb_mgm_get_node_type_string(type));
+ output->println("node.%d.status: %s",
+ nodeId,
+ ndb_mgm_get_node_status_string(status));
+ output->println("node.%d.version: %d", nodeId, version);
+ output->println("node.%d.startphase: %d", nodeId, startPhase);
+ output->println("node.%d.dynamic_id: %d", nodeId, dynamicId);
+ output->println("node.%d.node_group: %d", nodeId, nodeGroup);
+ output->println("node.%d.connect_count: %d", nodeId, connectCount);
+ output->println("node.%d.address: %s", nodeId, mgmsrv.get_connect_address(nodeId));
+ }
+
+}
+
+void
+MgmApiSession::getStatus(Parser<MgmApiSession>::Context &,
+ Properties const &) {
+ int noOfNodes = 0;
+
+ NodeId nodeId = 0;
+ while(m_mgmsrv.getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){
+ noOfNodes++;
+ }
+ nodeId = 0;
+ while(m_mgmsrv.getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_API)){
+ noOfNodes++;
+ }
+ nodeId = 0;
+ while(m_mgmsrv.getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)){
+ noOfNodes++;
+ }
+
+ m_output->println("node status");
+ m_output->println("nodes: %d", noOfNodes);
+ printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_NDB);
+ printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_MGM);
+ printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_API);
+
+ nodeId = 0;
+
+ m_output->println("");
+}
+
+void
+MgmApiSession::getInfoClusterLog(Parser<MgmApiSession>::Context &,
+ Properties const &) {
+ const char* names[] = { "enabled",
+ "debug",
+ "info",
+ "warning",
+ "error",
+ "critical",
+ "alert" };
+
+ m_output->println("clusterlog");
+ for(int i = 0; i < 7; i++) {
+ m_output->println("%s: %d",
+ names[i], m_mgmsrv.isEventLogFilterEnabled(i));
+ }
+ m_output->println("");
+}
+
+void
+MgmApiSession::stop(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 abort;
+ char *nodes_str;
+ Vector<NodeId> nodes;
+
+ args.get("node", (const char **)&nodes_str);
+ if(nodes_str == NULL)
+ return;
+ args.get("abort", &abort);
+
+ char *p, *last;
+ for((p = strtok_r(nodes_str, " ", &last));
+ p;
+ (p = strtok_r(NULL, " ", &last))) {
+ nodes.push_back(atoi(p));
+ }
+
+ int stop_self= 0;
+ size_t i;
+
+ for(i=0; i < nodes.size(); i++) {
+ if (nodes[i] == m_mgmsrv.getOwnNodeId()) {
+ stop_self= 1;
+ if (i != nodes.size()-1) {
+ m_output->println("stop reply");
+ m_output->println("result: server must be stopped last");
+ m_output->println("");
+ return;
+ }
+ }
+ }
+
+ int stopped = 0, result = 0;
+
+ for(i=0; i < nodes.size(); i++)
+ if (nodes[i] != m_mgmsrv.getOwnNodeId()) {
+ if((result = m_mgmsrv.stopNode(nodes[i], abort != 0)) == 0)
+ stopped++;
+ } else
+ stopped++;
+
+ m_output->println("stop reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("stopped: %d", stopped);
+ m_output->println("");
+
+ if (stop_self)
+ g_StopServer= true;
+}
+
+
+void
+MgmApiSession::stopAll(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ int stopped = 0;
+ Uint32 abort;
+ args.get("abort", &abort);
+
+ int result = m_mgmsrv.stop(&stopped, abort != 0);
+
+ m_output->println("stop reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("stopped: %d", stopped);
+ m_output->println("");
+}
+
+void
+MgmApiSession::enterSingleUser(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ int stopped = 0;
+ Uint32 nodeId = 0;
+ args.get("nodeId", &nodeId);
+ int result = m_mgmsrv.enterSingleUser(&stopped, nodeId);
+ m_output->println("enter single user reply");
+ if(result != 0) {
+ m_output->println("result: %s", get_error_text(result));
+ }
+ else {
+ m_output->println("result: Ok");
+ }
+ m_output->println("");
+}
+
+void
+MgmApiSession::exitSingleUser(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ int stopped = 0;
+ int result = m_mgmsrv.exitSingleUser(&stopped, false);
+ m_output->println("exit single user reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+
+void
+MgmApiSession::startSignalLog(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node;
+
+ args.get("node", &node);
+
+ int result = m_mgmsrv.startSignalTracing(node);
+
+ m_output->println("start signallog reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::logSignals(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node = 0, in = 0, out = 0;
+ // BaseString blocks;
+ BaseString blockList;
+ char * blockName;
+ args.get("node", &node);
+ args.get("in", &in);
+ args.get("out", &out);
+ args.get("blocks", blockList);
+ // fast fix - pekka
+ char buf[200];
+ BaseString::snprintf(buf, 200, "%s", blockList.c_str());
+ Vector<BaseString> blocks;
+
+ blockName=strtok(buf,"|");
+ while( blockName != NULL)
+ {
+ blocks.push_back(blockName);
+ blockName=strtok(NULL,"|");
+ }
+
+
+ if(in > 1 || out > 1)
+ return; /* Invalid arguments */
+
+ const MgmtSrvr::LogMode modes[] = {
+ MgmtSrvr::Off,
+ MgmtSrvr::Out,
+ MgmtSrvr::In,
+ MgmtSrvr::InOut,
+ };
+ MgmtSrvr::LogMode mode = modes[in<<1 | out];
+
+ int result = m_mgmsrv.setSignalLoggingMode(node, mode, blocks);
+
+ m_output->println("log signals reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::start(Parser<MgmApiSession>::Context &,
+ Properties const &args) {
+ Uint32 node;
+
+ args.get("node", &node);
+
+ int result = m_mgmsrv.start(node);
+
+ m_output->println("start reply");
+ if(result != 0)
+ m_output->println("result: %s", get_error_text(result));
+ else
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::startAll(Parser<MgmApiSession>::Context &,
+ Properties const &) {
+ NodeId node = 0;
+ int started = 0;
+
+ while(m_mgmsrv.getNextNodeId(&node, NDB_MGM_NODE_TYPE_NDB))
+ if(m_mgmsrv.start(node) == 0)
+ started++;
+
+ m_output->println("start reply");
+ m_output->println("result: Ok");
+ m_output->println("started: %d", started);
+ m_output->println("");
+}
+
+void
+MgmApiSession::setLogFilter(Parser_t::Context &ctx,
+ const class Properties &args) {
+ Uint32 severity;
+ Uint32 enable;
+
+ args.get("level", &severity);
+ args.get("enable", &enable);
+
+ int result = m_mgmsrv.setEventLogFilter(severity, enable);
+
+ m_output->println("set logfilter reply");
+ m_output->println("result: %d", result);
+ m_output->println("");
+}
+
+void
+MgmApiSession::configLock(Parser_t::Context &,
+ Properties const &) {
+ int ret = m_mgmsrv.lockConf();
+ m_output->println("config lock reply");
+ m_output->println("result: %d", ret);
+ m_output->println("");
+}
+
+void
+MgmApiSession::configUnlock(Parser_t::Context &,
+ Properties const &args) {
+ Uint32 commit;
+ args.get("commit", &commit);
+ int ret = m_mgmsrv.unlockConf(commit == 1);
+ m_output->println("config unlock reply");
+ m_output->println("result: %d", ret);
+ m_output->println("");
+}
+
+void
+MgmApiSession::configChange(Parser_t::Context &,
+ Properties const &args) {
+ BaseString section, param, value;
+ args.get("section", section);
+ args.get("parameter", param);
+ args.get("value", value);
+
+ int ret = m_mgmsrv.changeConfig(section.c_str(),
+ param.c_str(),
+ value.c_str());
+ m_output->println("config change reply");
+ m_output->println("result: %d", ret);
+ m_output->println("");
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const LogLevel & ll)
+{
+ out << "[LogLevel: ";
+ for(size_t i = 0; i<LogLevel::LOGLEVEL_CATEGORIES; i++)
+ out << ll.getLogLevel((LogLevel::EventCategory)i) << " ";
+ out << "]";
+ return out;
+}
+
+void
+Ndb_mgmd_event_service::log(int eventType, const Uint32* theData, NodeId nodeId){
+
+ Uint32 threshold;
+ LogLevel::EventCategory cat;
+ Logger::LoggerLevel severity;
+ EventLoggerBase::EventTextFunction textF;
+ int i;
+ DBUG_ENTER("Ndb_mgmd_event_service::log");
+ DBUG_PRINT("enter",("eventType=%d, nodeid=%d", eventType, nodeId));
+
+ if (EventLoggerBase::event_lookup(eventType,cat,threshold,severity,textF))
+ DBUG_VOID_RETURN;
+
+ char m_text[256];
+ EventLogger::getText(m_text, sizeof(m_text),
+ textF, theData, nodeId);
+
+ BaseString str("log event reply\n");
+ str.appfmt("type=%d\n", eventType);
+ str.appfmt("time=%d\n", 0);
+ str.appfmt("source_nodeid=%d\n", nodeId);
+ for (i= 0; ndb_logevent_body[i].token; i++)
+ {
+ if ( ndb_logevent_body[i].type != eventType)
+ continue;
+ int val= theData[ndb_logevent_body[i].index];
+ if (ndb_logevent_body[i].index_fn)
+ val= (*(ndb_logevent_body[i].index_fn))(val);
+ str.appfmt("%s=%d\n",ndb_logevent_body[i].token, val);
+ }
+
+ Vector<NDB_SOCKET_TYPE> copy;
+ m_clients.lock();
+ for(i = m_clients.size() - 1; i >= 0; i--){
+ if(threshold <= m_clients[i].m_logLevel.getLogLevel(cat)){
+ if(m_clients[i].m_socket != NDB_INVALID_SOCKET)
+ {
+ int r;
+ if (m_clients[i].m_parsable)
+ r= println_socket(m_clients[i].m_socket,
+ MAX_WRITE_TIMEOUT, str.c_str());
+ else
+ r= println_socket(m_clients[i].m_socket,
+ MAX_WRITE_TIMEOUT, m_text);
+ if (r == -1) {
+ copy.push_back(m_clients[i].m_socket);
+ m_clients.erase(i, false);
+ }
+ }
+ }
+ }
+ m_clients.unlock();
+
+ for(i = 0; (unsigned)i < copy.size(); i++){
+ NDB_CLOSE_SOCKET(copy[i]);
+ }
+
+ if(copy.size()){
+ LogLevel tmp; tmp.clear();
+ m_clients.lock();
+ for(i = 0; (unsigned)i < m_clients.size(); i++){
+ tmp.set_max(m_clients[i].m_logLevel);
+ }
+ m_clients.unlock();
+ update_log_level(tmp);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void
+Ndb_mgmd_event_service::update_max_log_level(const LogLevel &log_level)
+{
+ LogLevel tmp= m_logLevel;
+ tmp.set_max(log_level);
+ update_log_level(tmp);
+}
+
+void
+Ndb_mgmd_event_service::update_log_level(const LogLevel &tmp)
+{
+ if(!(tmp == m_logLevel)){
+ m_logLevel = tmp;
+ EventSubscribeReq req;
+ req = tmp;
+ req.blockRef = 0;
+ m_mgmsrv->m_log_level_requests.push_back(req);
+ }
+}
+
+void
+Ndb_mgmd_event_service::add_listener(const Event_listener& client){
+ m_clients.push_back(client);
+ update_max_log_level(client.m_logLevel);
+}
+
+void
+Ndb_mgmd_event_service::stop_sessions(){
+ m_clients.lock();
+ for(int i = m_clients.size() - 1; i >= 0; i--){
+ if(m_clients[i].m_socket != NDB_INVALID_SOCKET){
+ NDB_CLOSE_SOCKET(m_clients[i].m_socket);
+ m_clients.erase(i);
+ }
+ }
+ m_clients.unlock();
+}
+
+void
+MgmApiSession::setParameter(Parser_t::Context &,
+ Properties const &args) {
+ BaseString node, param, value;
+ args.get("node", node);
+ args.get("parameter", param);
+ args.get("value", value);
+
+ BaseString result;
+ int ret = m_mgmsrv.setDbParameter(atoi(node.c_str()),
+ atoi(param.c_str()),
+ value.c_str(),
+ result);
+
+ m_output->println("set parameter reply");
+ m_output->println("message: %s", result.c_str());
+ m_output->println("result: %d", ret);
+ m_output->println("");
+}
+
+void
+MgmApiSession::setConnectionParameter(Parser_t::Context &ctx,
+ Properties const &args) {
+ BaseString node1, node2, param, value;
+ args.get("node1", node1);
+ args.get("node2", node2);
+ args.get("param", param);
+ args.get("value", value);
+
+ BaseString result;
+ int ret = m_mgmsrv.setConnectionDbParameter(atoi(node1.c_str()),
+ atoi(node2.c_str()),
+ atoi(param.c_str()),
+ atoi(value.c_str()),
+ result);
+
+ m_output->println("set connection parameter reply");
+ m_output->println("message: %s", result.c_str());
+ m_output->println("result: %s", (ret>0)?"Ok":"Failed");
+ m_output->println("");
+}
+
+void
+MgmApiSession::getConnectionParameter(Parser_t::Context &ctx,
+ Properties const &args) {
+ BaseString node1, node2, param;
+ int value = 0;
+
+ args.get("node1", node1);
+ args.get("node2", node2);
+ args.get("param", param);
+
+ BaseString result;
+ int ret = m_mgmsrv.getConnectionDbParameter(atoi(node1.c_str()),
+ atoi(node2.c_str()),
+ atoi(param.c_str()),
+ &value,
+ result);
+
+ m_output->println("get connection parameter reply");
+ m_output->println("value: %d", value);
+ m_output->println("result: %s", (ret>0)?"Ok":result.c_str());
+ m_output->println("");
+}
+
+void
+MgmApiSession::listen_event(Parser<MgmApiSession>::Context & ctx,
+ Properties const & args) {
+ Uint32 parsable= 0;
+ BaseString node, param, value;
+ args.get("node", node);
+ args.get("filter", param);
+ args.get("parsable", &parsable);
+
+ int result = 0;
+ BaseString msg;
+
+ Ndb_mgmd_event_service::Event_listener le;
+ le.m_parsable = parsable;
+ le.m_socket = m_socket;
+
+ Vector<BaseString> list;
+ param.trim();
+ param.split(list, " ,");
+ for(size_t i = 0; i<list.size(); i++){
+ Vector<BaseString> spec;
+ list[i].trim();
+ list[i].split(spec, "=:");
+ if(spec.size() != 2){
+ msg.appfmt("Invalid filter specification: >%s< >%s< %d",
+ param.c_str(), list[i].c_str(), spec.size());
+ result = -1;
+ goto done;
+ }
+
+ spec[0].trim().ndb_toupper();
+ int category = ndb_mgm_match_event_category(spec[0].c_str());
+ if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
+ category = atoi(spec[0].c_str());
+ if(category < NDB_MGM_MIN_EVENT_CATEGORY ||
+ category > NDB_MGM_MAX_EVENT_CATEGORY){
+ msg.appfmt("Unknown category: >%s<", spec[0].c_str());
+ result = -1;
+ goto done;
+ }
+ }
+
+ int level = atoi(spec[1].c_str());
+ if(level < 0 || level > 15){
+ msg.appfmt("Invalid level: >%s<", spec[1].c_str());
+ result = -1;
+ goto done;
+ }
+ category -= CFG_MIN_LOGLEVEL;
+ le.m_logLevel.setLogLevel((LogLevel::EventCategory)category, level);
+ }
+
+ if(list.size() == 0){
+ msg.appfmt("Empty filter specification");
+ result = -1;
+ goto done;
+ }
+
+ m_mgmsrv.m_event_listner.add_listener(le);
+
+ m_stop = true;
+ m_socket = NDB_INVALID_SOCKET;
+
+done:
+ m_output->println("listen event");
+ m_output->println("result: %d", result);
+ if(result != 0)
+ m_output->println("msg: %s", msg.c_str());
+ m_output->println("");
+}
+
+struct PurgeStruct
+{
+ NodeBitmask free_nodes;/* free nodes as reported
+ * by ndbd in apiRegReqConf
+ */
+ BaseString *str;
+};
+
+void
+MgmApiSession::stop_session_if_not_connected(SocketServer::Session *_s, void *data)
+{
+ MgmApiSession *s= (MgmApiSession *)_s;
+ struct PurgeStruct &ps= *(struct PurgeStruct *)data;
+ if (s->m_allocated_resources->is_reserved(ps.free_nodes))
+ {
+ ps.str->appfmt(" %d", s->m_allocated_resources->get_nodeid());
+ s->stopSession();
+ }
+}
+
+void
+MgmApiSession::purge_stale_sessions(Parser_t::Context &ctx,
+ const class Properties &args)
+{
+ struct PurgeStruct ps;
+ BaseString str;
+ ps.str = &str;
+
+ m_mgmsrv.get_connected_nodes(ps.free_nodes);
+ ps.free_nodes.bitXORC(NodeBitmask()); // invert connected_nodes to get free nodes
+
+ m_mgmsrv.get_socket_server()->foreachSession(stop_session_if_not_connected,&ps);
+
+ m_output->println("purge stale sessions reply");
+ if (str.length() > 0)
+ m_output->println("purged:%s",str.c_str());
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::check_connection(Parser_t::Context &ctx,
+ const class Properties &args)
+{
+ m_output->println("check connection reply");
+ m_output->println("result: Ok");
+ m_output->println("");
+}
+
+void
+MgmApiSession::transporter_connect(Parser_t::Context &ctx,
+ Properties const &args)
+{
+ m_mgmsrv.transporter_connect(m_socket);
+
+ m_stop= true;
+ m_stopped= true; // force a stop (no closing socket)
+ m_socket= NDB_INVALID_SOCKET; // so nobody closes it
+}
+
+void
+MgmApiSession::get_mgmd_nodeid(Parser_t::Context &ctx,
+ Properties const &args)
+{
+ m_output->println("get mgmd nodeid reply");
+ m_output->println("nodeid:%u",m_mgmsrv.getOwnNodeId());
+ m_output->println("");
+}
+
+template class MutexVector<int>;
+template class Vector<ParserRow<MgmApiSession> const*>;
diff --git a/storage/ndb/src/mgmsrv/Services.hpp b/storage/ndb/src/mgmsrv/Services.hpp
new file mode 100644
index 00000000000..ff9008b05a8
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/Services.hpp
@@ -0,0 +1,124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 MGMAPI_SERVICE_HPP
+#define MGMAPI_SERVICE_HPP
+
+#include <SocketServer.hpp>
+#include <NdbSleep.h>
+#include <Parser.hpp>
+#include <OutputStream.hpp>
+#include <InputStream.hpp>
+
+#include "MgmtSrvr.hpp"
+
+/** Undefine this to remove backwards compatibility for "GET CONFIG". */
+#define MGM_GET_CONFIG_BACKWARDS_COMPAT
+
+class MgmApiSession : public SocketServer::Session
+{
+ static void stop_session_if_not_connected(SocketServer::Session *_s, void *data);
+private:
+ typedef Parser<MgmApiSession> Parser_t;
+
+ class MgmtSrvr & m_mgmsrv;
+ InputStream *m_input;
+ OutputStream *m_output;
+ Parser_t *m_parser;
+ MgmtSrvr::Allocated_resources *m_allocated_resources;
+ char m_err_str[1024];
+
+ void getConfig_common(Parser_t::Context &ctx,
+ const class Properties &args,
+ bool compat = false);
+ const char *get_error_text(int err_no)
+ { return m_mgmsrv.getErrorText(err_no, m_err_str, sizeof(m_err_str)); }
+
+public:
+ MgmApiSession(class MgmtSrvr & mgm, NDB_SOCKET_TYPE sock);
+ virtual ~MgmApiSession();
+ void runSession();
+
+ void getStatPort(Parser_t::Context &ctx, const class Properties &args);
+ void getConfig(Parser_t::Context &ctx, const class Properties &args);
+#ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT
+ void getConfig_old(Parser_t::Context &ctx);
+#endif /* MGM_GET_CONFIG_BACKWARDS_COMPAT */
+
+ void get_nodeid(Parser_t::Context &ctx, const class Properties &args);
+ void getVersion(Parser_t::Context &ctx, const class Properties &args);
+ void getStatus(Parser_t::Context &ctx, const class Properties &args);
+ void getInfoClusterLog(Parser_t::Context &ctx, const class Properties &args);
+ void restart(Parser_t::Context &ctx, const class Properties &args);
+ void restartAll(Parser_t::Context &ctx, const class Properties &args);
+ void insertError(Parser_t::Context &ctx, const class Properties &args);
+ void setTrace(Parser_t::Context &ctx, const class Properties &args);
+ void logSignals(Parser_t::Context &ctx, const class Properties &args);
+ void startSignalLog(Parser_t::Context &ctx, const class Properties &args);
+ void stopSignalLog(Parser_t::Context &ctx, const class Properties &args);
+ void dumpState(Parser_t::Context &ctx, const class Properties &args);
+ void startBackup(Parser_t::Context &ctx, const class Properties &args);
+ void abortBackup(Parser_t::Context &ctx, const class Properties &args);
+ void enterSingleUser(Parser_t::Context &ctx, const class Properties &args);
+ void exitSingleUser(Parser_t::Context &ctx, const class Properties &args);
+ void stop(Parser_t::Context &ctx, const class Properties &args);
+ void stopAll(Parser_t::Context &ctx, const class Properties &args);
+ void start(Parser_t::Context &ctx, const class Properties &args);
+ void startAll(Parser_t::Context &ctx, const class Properties &args);
+ void bye(Parser_t::Context &ctx, const class Properties &args);
+ void setLogLevel(Parser_t::Context &ctx, const class Properties &args);
+ void setClusterLogLevel(Parser_t::Context &ctx,
+ const class Properties &args);
+ void setLogFilter(Parser_t::Context &ctx, const class Properties &args);
+ void configLock(Parser_t::Context &ctx, const class Properties &args);
+ void configUnlock(Parser_t::Context &ctx, const class Properties &args);
+ void configChange(Parser_t::Context &ctx, const class Properties &args);
+
+ void setParameter(Parser_t::Context &ctx, const class Properties &args);
+ void setConnectionParameter(Parser_t::Context &ctx,
+ const class Properties &args);
+ void getConnectionParameter(Parser_t::Context &ctx,
+ Properties const &args);
+
+ void listen_event(Parser_t::Context &ctx, const class Properties &args);
+
+ void purge_stale_sessions(Parser_t::Context &ctx, const class Properties &args);
+ void check_connection(Parser_t::Context &ctx, const class Properties &args);
+
+ void transporter_connect(Parser_t::Context &ctx, Properties const &args);
+
+ void get_mgmd_nodeid(Parser_t::Context &ctx, Properties const &args);
+
+ void repCommand(Parser_t::Context &ctx, const class Properties &args);
+};
+
+class MgmApiService : public SocketServer::Service {
+ class MgmtSrvr * m_mgmsrv;
+public:
+ MgmApiService(){
+ m_mgmsrv = 0;
+ }
+
+ void setMgm(class MgmtSrvr * mgmsrv){
+ m_mgmsrv = mgmsrv;
+ }
+
+ SocketServer::Session * newSession(NDB_SOCKET_TYPE socket){
+ return new MgmApiSession(* m_mgmsrv, socket);
+ }
+};
+
+#endif
diff --git a/storage/ndb/src/mgmsrv/SignalQueue.cpp b/storage/ndb/src/mgmsrv/SignalQueue.cpp
new file mode 100644
index 00000000000..08ad5f363a6
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/SignalQueue.cpp
@@ -0,0 +1,104 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "SignalQueue.hpp"
+
+SignalQueue::SignalQueue() {
+ m_mutex = NdbMutex_Create();
+ m_cond = NdbCondition_Create();
+ m_signalQueueHead = NULL;
+}
+
+SignalQueue::~SignalQueue() {
+ {
+ Guard g(m_mutex);
+ while(m_signalQueueHead != NULL)
+ delete pop();
+ }
+ NdbMutex_Destroy(m_mutex);
+ m_mutex = NULL;
+ NdbCondition_Destroy(m_cond);
+ m_cond = NULL;
+}
+
+NdbApiSignal *
+SignalQueue::pop() {
+ NdbApiSignal *ret;
+
+ if(m_signalQueueHead == NULL)
+ return NULL;
+
+ ret = m_signalQueueHead->signal;
+
+ QueueEntry *old = m_signalQueueHead;
+ m_signalQueueHead = m_signalQueueHead->next;
+
+ delete old;
+
+ return ret;
+}
+
+void
+SignalQueue::receive(void *me, NdbApiSignal *signal) {
+ SignalQueue *q = (SignalQueue *)me;
+ q->receive(signal);
+}
+
+void
+SignalQueue::receive(NdbApiSignal *signal) {
+ QueueEntry *n = new QueueEntry();
+ n->signal = signal;
+ n->next = NULL;
+
+ Guard guard(m_mutex);
+
+ if(m_signalQueueHead == NULL) {
+ m_signalQueueHead = n;
+ NdbCondition_Broadcast(m_cond);
+ return;
+ }
+
+ QueueEntry *cur = m_signalQueueHead;
+
+ while(cur->next != NULL)
+ cur = cur->next;
+
+ cur->next = n;
+
+ NdbCondition_Broadcast(m_cond);
+}
+
+NdbApiSignal *
+SignalQueue::waitFor(int gsn, NodeId nodeid, Uint32 timeout) {
+ Guard g(m_mutex);
+
+ if(m_signalQueueHead == NULL)
+ NdbCondition_WaitTimeout(m_cond, m_mutex, timeout);
+
+ if(m_signalQueueHead == NULL)
+ return NULL;
+
+ if(gsn != 0 &&
+ m_signalQueueHead->signal->readSignalNumber() != gsn)
+ return NULL;
+
+ if(nodeid != 0 &&
+ refToNode(m_signalQueueHead->signal->theSendersBlockRef) != nodeid)
+ return NULL;
+
+ return pop();
+}
diff --git a/storage/ndb/src/mgmsrv/SignalQueue.hpp b/storage/ndb/src/mgmsrv/SignalQueue.hpp
new file mode 100644
index 00000000000..bacbad53415
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/SignalQueue.hpp
@@ -0,0 +1,100 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 __SIGNALQUEUE_HPP_INCLUDED__
+#define __SIGNALQUEUE_HPP_INCLUDED__
+
+#include <NdbApiSignal.hpp>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <Vector.hpp>
+
+/* XXX Look for an already existing definition */
+#define DEFAULT_TIMEOUT 5000
+
+class SignalQueue {
+public:
+ typedef void (* SignalHandler)(void *obj, int gsn, NdbApiSignal *signal);
+
+ SignalQueue();
+ ~SignalQueue();
+
+ /**
+ * Static wrapper making it possible to call receive without knowing the
+ * type of the receiver
+ */
+ static void receive(void *me, NdbApiSignal *signal);
+
+ /**
+ * Enqueues a signal, and notifies any thread waiting for signals.
+ */
+ void receive(NdbApiSignal *signal);
+
+ NdbApiSignal *waitFor(int gsn,
+ NodeId nodeid = 0,
+ Uint32 timeout = DEFAULT_TIMEOUT);
+ template<class T> bool waitFor(Vector<T> &t,
+ T **handler,
+ NdbApiSignal **signal,
+ Uint32 timeout = DEFAULT_TIMEOUT);
+private:
+ NdbMutex *m_mutex; /* Locks all data in SignalQueue */
+ NdbCondition *m_cond; /* Notifies about new signal in the queue */
+
+ /**
+ * Returns the last recently received signal. Must be called with
+ * m_mutex locked.
+ * The caller takes responsibility for deleting the returned object.
+ *
+ * @returns NULL if failed, or a received signal
+ */
+ NdbApiSignal *pop();
+
+ class QueueEntry {
+ public:
+ NdbApiSignal *signal;
+ QueueEntry *next;
+ };
+ QueueEntry *m_signalQueueHead; /** Head of the queue.
+ * New entries added on the tail
+ */
+};
+
+template<class T> bool
+SignalQueue::waitFor(Vector<T> &t,
+ T **handler,
+ NdbApiSignal **signal,
+ Uint32 timeout) {
+ Guard g(m_mutex);
+
+ if(m_signalQueueHead == NULL)
+ NdbCondition_WaitTimeout(m_cond, m_mutex, timeout);
+
+ if(m_signalQueueHead == NULL)
+ return false;
+
+ for(size_t i = 0; i < t.size(); i++) {
+ if(t[i].check(m_signalQueueHead->signal)) {
+ * handler = &t[i];
+ * signal = pop();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#endif /* !__SIGNALQUEUE_HPP_INCLUDED__ */
diff --git a/storage/ndb/src/mgmsrv/convertStrToInt.cpp b/storage/ndb/src/mgmsrv/convertStrToInt.cpp
new file mode 100644
index 00000000000..e5216047d10
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/convertStrToInt.cpp
@@ -0,0 +1,43 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+bool convert(const char* s, int& val) {
+
+ if (s == NULL) {
+ return false;
+ }
+
+ if (strlen(s) == 0) {
+ return false;
+ }
+
+ errno = 0;
+ char* p;
+ long v = strtol(s, &p, 10);
+ if (errno != 0) {
+ return false;
+ }
+ if (p != &s[strlen(s)]) {
+ return false;
+ }
+
+ val = v;
+ return true;
+}
+
+
diff --git a/storage/ndb/src/mgmsrv/convertStrToInt.hpp b/storage/ndb/src/mgmsrv/convertStrToInt.hpp
new file mode 100644
index 00000000000..0b2a96ed0bf
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/convertStrToInt.hpp
@@ -0,0 +1,25 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+//******************************************************************************
+// Description:
+//
+// Author: Peter Lind
+//******************************************************************************
+
+extern bool convert(const char* s, int& val);
+
+
diff --git a/storage/ndb/src/mgmsrv/main.cpp b/storage/ndb/src/mgmsrv/main.cpp
new file mode 100644
index 00000000000..3335fdc827c
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/main.cpp
@@ -0,0 +1,382 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include "MgmtSrvr.hpp"
+#include "EventLogger.hpp"
+#include <Config.hpp>
+#include "InitConfigFileParser.hpp"
+#include <SocketServer.hpp>
+#include "Services.hpp"
+#include <version.h>
+#include <kernel_types.h>
+#include <Properties.hpp>
+#include <NdbOut.hpp>
+#include <NdbMain.h>
+#include <NdbDaemon.h>
+#include <NdbConfig.h>
+#include <NdbHost.h>
+#include <ndb_version.h>
+#include <ConfigRetriever.hpp>
+#include <mgmapi_config_parameters.h>
+
+#include <NdbAutoPtr.hpp>
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+#include <efs.h>
+#else
+#include <ndb_mgmclient.hpp>
+#endif
+
+#undef DEBUG
+#define DEBUG(x) ndbout << x << endl;
+
+const char progname[] = "mgmtsrvr";
+
+// copied from mysql.cc to get readline
+extern "C" {
+#if defined( __WIN__) || defined(OS2)
+#include <conio.h>
+#elif !defined(__NETWARE__)
+#include <readline/readline.h>
+extern "C" int add_history(const char *command); /* From readline directory */
+#define HAVE_READLINE
+#endif
+}
+
+static int
+read_and_execute(Ndb_mgmclient* com, const char * prompt, int _try_reconnect)
+{
+ static char *line_read = (char *)NULL;
+
+ /* If the buffer has already been allocated, return the memory
+ to the free pool. */
+ if (line_read)
+ {
+ free (line_read);
+ line_read = (char *)NULL;
+ }
+#ifdef HAVE_READLINE
+ /* Get a line from the user. */
+ line_read = readline (prompt);
+ /* If the line has any text in it, save it on the history. */
+ if (line_read && *line_read)
+ add_history (line_read);
+#else
+ static char linebuffer[254];
+ fputs(prompt, stdout);
+ linebuffer[sizeof(linebuffer)-1]=0;
+ line_read = fgets(linebuffer, sizeof(linebuffer)-1, stdin);
+ if (line_read == linebuffer) {
+ char *q=linebuffer;
+ while (*q > 31) q++;
+ *q=0;
+ line_read= strdup(linebuffer);
+ }
+#endif
+ return com->execute(line_read,_try_reconnect);
+}
+
+/**
+ * @struct MgmGlobals
+ * @brief Global Variables used in the management server
+ *****************************************************************************/
+struct MgmGlobals {
+ MgmGlobals();
+ ~MgmGlobals();
+
+ /** Command line arguments */
+ int daemon; // NOT bool, bool need not be int
+ int non_interactive;
+ int interactive;
+ const char * config_filename;
+
+ /** Stuff found in environment or in local config */
+ NodeId localNodeId;
+ bool use_specific_ip;
+ char * interface_name;
+ short unsigned int port;
+
+ /** The Mgmt Server */
+ MgmtSrvr * mgmObject;
+
+ /** The Socket Server */
+ SocketServer * socketServer;
+};
+
+int g_no_nodeid_checks= 0;
+static MgmGlobals glob;
+
+/******************************************************************************
+ * Function prototypes
+ ******************************************************************************/
+/**
+ * Global variables
+ */
+bool g_StopServer;
+extern EventLogger g_eventLogger;
+
+extern int global_mgmt_server_check;
+
+enum ndb_mgmd_options {
+ OPT_INTERACTIVE = NDB_STD_OPTIONS_LAST,
+ OPT_NO_NODEID_CHECKS,
+ OPT_NO_DAEMON
+};
+NDB_STD_OPTS_VARS;
+
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS("ndb_mgmd"),
+ { "config-file", 'f', "Specify cluster configuration file",
+ (gptr*) &glob.config_filename, (gptr*) &glob.config_filename, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+ { "daemon", 'd', "Run ndb_mgmd in daemon mode (default)",
+ (gptr*) &glob.daemon, (gptr*) &glob.daemon, 0,
+ GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
+ { "interactive", OPT_INTERACTIVE,
+ "Run interactive. Not supported but provided for testing purposes",
+ (gptr*) &glob.interactive, (gptr*) &glob.interactive, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "no-nodeid-checks", OPT_NO_NODEID_CHECKS,
+ "Do not provide any node id checks",
+ (gptr*) &g_no_nodeid_checks, (gptr*) &g_no_nodeid_checks, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "nodaemon", OPT_NO_DAEMON,
+ "Don't run as daemon, but don't read from stdin",
+ (gptr*) &glob.non_interactive, (gptr*) &glob.non_interactive, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static void short_usage_sub(void)
+{
+ printf("Usage: %s [OPTIONS]\n", my_progname);
+}
+static void usage()
+{
+ short_usage_sub();
+ ndb_std_print_version();
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+/*
+ * MAIN
+ */
+int main(int argc, char** argv)
+{
+ NDB_INIT(argv[0]);
+
+ /**
+ * OSE specific. Enable shared ownership of file system resources.
+ * This is needed in order to use the cluster log since the events
+ * from the cluster is written from the 'ndb_receive'(NDBAPI) thread/process.
+ */
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ efs_segment_share();
+#endif
+
+ global_mgmt_server_check = 1;
+
+ const char *load_default_groups[]= { "mysql_cluster","ndb_mgmd",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:O,/tmp/ndb_mgmd.trace";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ exit(ho_error);
+
+ if (glob.interactive ||
+ glob.non_interactive) {
+ glob.daemon= 0;
+ }
+
+ glob.socketServer = new SocketServer();
+
+ MgmApiService * mapi = new MgmApiService();
+
+ glob.mgmObject = new MgmtSrvr(glob.socketServer,
+ glob.config_filename,
+ opt_connect_str);
+
+ if (glob.mgmObject->init())
+ goto error_end;
+
+ my_setwd(NdbConfig_get_path(0), MYF(0));
+
+ glob.localNodeId= glob.mgmObject->getOwnNodeId();
+ if (glob.localNodeId == 0) {
+ goto error_end;
+ }
+
+ glob.port= glob.mgmObject->getPort();
+
+ if (glob.port == 0)
+ goto error_end;
+
+ glob.interface_name = 0;
+ glob.use_specific_ip = false;
+
+ if(!glob.use_specific_ip){
+ int count= 5; // no of retries for tryBind
+ while(!glob.socketServer->tryBind(glob.port, glob.interface_name)){
+ if (--count > 0) {
+ NdbSleep_MilliSleep(1000);
+ continue;
+ }
+ ndbout_c("Unable to setup port: %s:%d!\n"
+ "Please check if the port is already used,\n"
+ "(perhaps a ndb_mgmd is already running),\n"
+ "and if you are executing on the correct computer",
+ (glob.interface_name ? glob.interface_name : "*"), glob.port);
+ goto error_end;
+ }
+ free(glob.interface_name);
+ glob.interface_name = 0;
+ }
+
+ if(!glob.socketServer->setup(mapi, &glob.port, glob.interface_name)){
+ ndbout_c("Unable to setup management port: %d!\n"
+ "Please check if the port is already used,\n"
+ "(perhaps a ndb_mgmd is already running),\n"
+ "and if you are executing on the correct computer",
+ glob.port);
+ delete mapi;
+ goto error_end;
+ }
+
+ /* Construct a fake connectstring to connect back to ourselves */
+ char connect_str[20];
+ if(!opt_connect_str) {
+ snprintf(connect_str,20,"localhost:%u",glob.mgmObject->getPort());
+ opt_connect_str= connect_str;
+ }
+ glob.mgmObject->set_connect_string(opt_connect_str);
+
+ if(!glob.mgmObject->check_start()){
+ ndbout_c("Unable to check start management server.");
+ ndbout_c("Probably caused by illegal initial configuration file.");
+ goto error_end;
+ }
+
+ /*
+ * Connect back to ourselves so we can use mgmapi to fetch
+ * config info
+ */
+ int mgm_connect_result;
+ mgm_connect_result = glob.mgmObject->get_config_retriever()->
+ do_connect(0,0,0);
+
+ if(mgm_connect_result<0) {
+ ndbout_c("Unable to connect to our own ndb_mgmd (Error %d)",
+ mgm_connect_result);
+ ndbout_c("This is probably a bug.");
+ }
+
+
+ if (glob.daemon) {
+ // Become a daemon
+ char *lockfile= NdbConfig_PidFileName(glob.localNodeId);
+ char *logfile= NdbConfig_StdoutFileName(glob.localNodeId);
+ NdbAutoPtr<char> tmp_aptr1(lockfile), tmp_aptr2(logfile);
+
+ if (NdbDaemon_Make(lockfile, logfile, 0) == -1) {
+ ndbout << "Cannot become daemon: " << NdbDaemon_ErrorText << endl;
+ return 1;
+ }
+ }
+
+#ifndef NDB_WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ {
+ BaseString error_string;
+ if(!glob.mgmObject->start(error_string)){
+ ndbout_c("Unable to start management server.");
+ ndbout_c("Probably caused by illegal initial configuration file.");
+ ndbout_c(error_string.c_str());
+ goto error_end;
+ }
+ }
+
+ //glob.mgmObject->saveConfig();
+ mapi->setMgm(glob.mgmObject);
+
+ char msg[256];
+ BaseString::snprintf(msg, sizeof(msg),
+ "NDB Cluster Management Server. %s", NDB_VERSION_STRING);
+ ndbout_c(msg);
+ g_eventLogger.info(msg);
+
+ BaseString::snprintf(msg, 256, "Id: %d, Command port: %d",
+ glob.localNodeId, glob.port);
+ ndbout_c(msg);
+ g_eventLogger.info(msg);
+
+ g_StopServer = false;
+ glob.socketServer->startServer();
+
+#if ! defined NDB_OSE && ! defined NDB_SOFTOSE
+ if(glob.interactive) {
+ BaseString con_str;
+ if(glob.interface_name)
+ con_str.appfmt("host=%s:%d", glob.interface_name, glob.port);
+ else
+ con_str.appfmt("localhost:%d", glob.port);
+ Ndb_mgmclient com(con_str.c_str(), 1);
+ while(g_StopServer != true && read_and_execute(&com, "ndb_mgm> ", 1));
+ } else
+#endif
+ {
+ while(g_StopServer != true)
+ NdbSleep_MilliSleep(500);
+ }
+
+ g_eventLogger.info("Shutting down server...");
+ glob.socketServer->stopServer();
+ glob.socketServer->stopSessions();
+ g_eventLogger.info("Shutdown complete");
+ return 0;
+ error_end:
+ return 1;
+}
+
+MgmGlobals::MgmGlobals(){
+ // Default values
+ port = 0;
+ config_filename = NULL;
+ interface_name = 0;
+ daemon = 1;
+ non_interactive = 0;
+ interactive = 0;
+ socketServer = 0;
+ mgmObject = 0;
+}
+
+MgmGlobals::~MgmGlobals(){
+ if (socketServer)
+ delete socketServer;
+ if (mgmObject)
+ delete mgmObject;
+ if (interface_name)
+ free(interface_name);
+}
diff --git a/storage/ndb/src/mgmsrv/mkconfig/Makefile b/storage/ndb/src/mgmsrv/mkconfig/Makefile
new file mode 100644
index 00000000000..43574eefbd1
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/mkconfig/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+BIN_TARGET := mkconfig
+BIN_TARGET_ARCHIVES := logger trace mgmsrvcommon portlib general
+
+SOURCES := mkconfig.cpp
+
+CCFLAGS_LOC += -I.. -I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon)
+CFLAGS_mkconfig.cpp := -I$(call fixpath,$(NDB_TOP)/src/mgmapi)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp b/storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp
new file mode 100644
index 00000000000..28823aaa35e
--- /dev/null
+++ b/storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp
@@ -0,0 +1,61 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <ndb_version.h>
+#include <mgmapi_configuration.hpp>
+
+#include <NdbMain.h>
+#include <Properties.hpp>
+
+#include "InitConfigFileParser.hpp"
+#include <Config.hpp>
+
+void usage(const char * prg){
+ ndbout << "Usage " << prg << ": <Init config> <Binary file>" << endl;
+
+}
+
+NDB_COMMAND(mkconfig,
+ "mkconfig", "mkconfig",
+ "Make a binary configuration from a config file", 16384){
+ ndb_init();
+ if(argc < 3){
+ usage(argv[0]);
+ return 0;
+ }
+
+ InitConfigFileParser parser;
+ Config* _cp;
+
+ if ((_cp = parser.parseConfig(argv[1])) == 0)
+ return false;
+
+ ConfigValues* cp = &_cp->m_configValues->m_config;
+ Uint32 sz = cp->getPackedSize();
+ UtilBuffer buf;
+ if(!cp->pack(buf))
+ return -1;
+
+ FILE * f = fopen(argv[2], "w");
+ if(fwrite(buf.get_data(), 1, buf.length(), f) != sz){
+ fclose(f);
+ unlink(argv[2]);
+ return -1;
+ }
+ fclose(f);
+ return 0;
+}
diff --git a/storage/ndb/src/ndbapi/API.hpp b/storage/ndb/src/ndbapi/API.hpp
new file mode 100644
index 00000000000..05e2d863cb6
--- /dev/null
+++ b/storage/ndb/src/ndbapi/API.hpp
@@ -0,0 +1,26 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 API_H
+#define API_H
+
+#include <BlockNumbers.h>
+#include <GlobalSignalNumbers.h>
+#include <RefConvert.hpp>
+#include "NdbImpl.hpp"
+#include "NdbDictionaryImpl.hpp"
+
+#endif
diff --git a/storage/ndb/src/ndbapi/ClusterMgr.cpp b/storage/ndb/src/ndbapi/ClusterMgr.cpp
new file mode 100644
index 00000000000..71938e27037
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ClusterMgr.cpp
@@ -0,0 +1,820 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+#include <ndb_limits.h>
+#include <ndb_version.h>
+
+#include "TransporterFacade.hpp"
+#include "ClusterMgr.hpp"
+#include <IPCConfig.hpp>
+#include "NdbApiSignal.hpp"
+#include "API.hpp"
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/ApiRegSignalData.hpp>
+
+#include <mgmapi.h>
+#include <mgmapi_configuration.hpp>
+#include <mgmapi_config_parameters.h>
+
+int global_flag_send_heartbeat_now= 0;
+
+// Just a C wrapper for threadMain
+extern "C"
+void*
+runClusterMgr_C(void * me)
+{
+ ((ClusterMgr*) me)->threadMain();
+ /**
+ * Sleep to allow another thread that is not exiting to take control
+ * of signals allocated by this thread
+ *
+ * see Ndb::~Ndb() in Ndbinit.cpp
+ */
+#ifdef NDB_OSE
+ NdbSleep_MilliSleep(50);
+#endif
+ return NULL;
+}
+
+extern "C" {
+ void ndbSetOwnVersion();
+}
+ClusterMgr::ClusterMgr(TransporterFacade & _facade):
+ theStop(0),
+ theFacade(_facade)
+{
+ ndbSetOwnVersion();
+ clusterMgrThreadMutex = NdbMutex_Create();
+ noOfConnectedNodes= 0;
+ theClusterMgrThread= 0;
+}
+
+ClusterMgr::~ClusterMgr(){
+ doStop();
+ NdbMutex_Destroy(clusterMgrThreadMutex);
+}
+
+void
+ClusterMgr::init(ndb_mgm_configuration_iterator & iter){
+ for(iter.first(); iter.valid(); iter.next()){
+ Uint32 tmp = 0;
+ if(iter.get(CFG_NODE_ID, &tmp))
+ continue;
+
+ theNodes[tmp].defined = true;
+#if 0
+ ndbout << "--------------------------------------" << endl;
+ ndbout << "--------------------------------------" << endl;
+ ndbout_c("ClusterMgr: Node %d defined as %s", tmp, config.getNodeType(tmp));
+#endif
+
+ unsigned type;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type))
+ continue;
+
+ switch(type){
+ case NODE_TYPE_DB:
+ theNodes[tmp].m_info.m_type = NodeInfo::DB;
+ break;
+ case NODE_TYPE_API:
+ theNodes[tmp].m_info.m_type = NodeInfo::API;
+ break;
+ case NODE_TYPE_MGM:
+ theNodes[tmp].m_info.m_type = NodeInfo::MGM;
+ break;
+ case NODE_TYPE_REP:
+ theNodes[tmp].m_info.m_type = NodeInfo::REP;
+ break;
+ case NODE_TYPE_EXT_REP:
+ theNodes[tmp].m_info.m_type = NodeInfo::REP;
+ {
+ Uint32 hbFreq = 10000;
+ //ndb_mgm_get_int_parameter(iter, CFG_, &hbFreq);
+ theNodes[tmp].hbFrequency = hbFreq;
+ assert(100 <= hbFreq && hbFreq < 60 * 60 * 1000);
+ }
+ break;
+ default:
+ type = type;
+#if 0
+ ndbout_c("ClusterMgr: Unknown node type: %d", type);
+#endif
+ }
+ }
+}
+
+void
+ClusterMgr::startThread() {
+ NdbMutex_Lock(clusterMgrThreadMutex);
+
+ theStop = 0;
+
+ theClusterMgrThread = NdbThread_Create(runClusterMgr_C,
+ (void**)this,
+ 32768,
+ "ndb_clustermgr",
+ NDB_THREAD_PRIO_LOW);
+ NdbMutex_Unlock(clusterMgrThreadMutex);
+}
+
+void
+ClusterMgr::doStop( ){
+ DBUG_ENTER("ClusterMgr::doStop");
+ NdbMutex_Lock(clusterMgrThreadMutex);
+ if(theStop){
+ NdbMutex_Unlock(clusterMgrThreadMutex);
+ DBUG_VOID_RETURN;
+ }
+ void *status;
+ theStop = 1;
+ if (theClusterMgrThread) {
+ NdbThread_WaitFor(theClusterMgrThread, &status);
+ NdbThread_Destroy(&theClusterMgrThread);
+ theClusterMgrThread= 0;
+ }
+ NdbMutex_Unlock(clusterMgrThreadMutex);
+ DBUG_VOID_RETURN;
+}
+
+void
+ClusterMgr::threadMain( ){
+ NdbApiSignal signal(numberToRef(API_CLUSTERMGR, theFacade.ownId()));
+
+ signal.theVerId_signalNumber = GSN_API_REGREQ;
+ signal.theReceiversBlockNumber = QMGR;
+ signal.theTrace = 0;
+ signal.theLength = ApiRegReq::SignalLength;
+
+ ApiRegReq * req = CAST_PTR(ApiRegReq, signal.getDataPtrSend());
+ req->ref = numberToRef(API_CLUSTERMGR, theFacade.ownId());
+ req->version = NDB_VERSION;
+
+
+ Uint32 timeSlept = 100;
+ Uint64 now = NdbTick_CurrentMillisecond();
+
+ while(!theStop){
+ /**
+ * Start of Secure area for use of Transporter
+ */
+ int send_heartbeat_now= global_flag_send_heartbeat_now;
+ global_flag_send_heartbeat_now= 0;
+
+ theFacade.lock_mutex();
+ for (int i = 1; i < MAX_NODES; i++){
+ /**
+ * Send register request (heartbeat) to all available nodes
+ * at specified timing intervals
+ */
+ const NodeId nodeId = i;
+ Node & theNode = theNodes[nodeId];
+
+ if (!theNode.defined)
+ continue;
+
+ if (theNode.connected == false){
+ theFacade.doConnect(nodeId);
+ continue;
+ }
+
+ if (!theNode.compatible){
+ continue;
+ }
+
+ theNode.hbCounter += timeSlept;
+ if (theNode.hbCounter >= theNode.hbFrequency ||
+ send_heartbeat_now) {
+ /**
+ * It is now time to send a new Heartbeat
+ */
+ if (theNode.hbCounter >= theNode.hbFrequency) {
+ theNode.hbSent++;
+ theNode.hbCounter = 0;
+ }
+
+ /**
+ * If the node is of type REP,
+ * then the receiver of the signal should be API_CLUSTERMGR
+ */
+ if (theNode.m_info.m_type == NodeInfo::REP) {
+ signal.theReceiversBlockNumber = API_CLUSTERMGR;
+ }
+#if 0
+ ndbout_c("ClusterMgr: Sending API_REGREQ to node %d", (int)nodeId);
+#endif
+ theFacade.sendSignalUnCond(&signal, nodeId);
+ }//if
+
+ if (theNode.hbSent == 4 && theNode.hbFrequency > 0){
+ reportNodeFailed(i);
+ }//if
+ }
+
+ /**
+ * End of secure area. Let other threads in
+ */
+ theFacade.unlock_mutex();
+
+ // Sleep for 100 ms between each Registration Heartbeat
+ Uint64 before = now;
+ NdbSleep_MilliSleep(100);
+ now = NdbTick_CurrentMillisecond();
+ timeSlept = (now - before);
+ }
+}
+
+#if 0
+void
+ClusterMgr::showState(NodeId nodeId){
+ ndbout << "-- ClusterMgr - NodeId = " << nodeId << endl;
+ ndbout << "theNodeList = " << theNodeList[nodeId] << endl;
+ ndbout << "theNodeState = " << theNodeState[nodeId] << endl;
+ ndbout << "theNodeCount = " << theNodeCount[nodeId] << endl;
+ ndbout << "theNodeStopDelay = " << theNodeStopDelay[nodeId] << endl;
+ ndbout << "theNodeSendDelay = " << theNodeSendDelay[nodeId] << endl;
+}
+#endif
+
+ClusterMgr::Node::Node()
+ : m_state(NodeState::SL_NOTHING) {
+ compatible = nfCompleteRep = true;
+ connected = defined = m_alive = false;
+ m_state.m_connected_nodes.clear();
+}
+
+/******************************************************************************
+ * API_REGREQ and friends
+ ******************************************************************************/
+
+void
+ClusterMgr::execAPI_REGREQ(const Uint32 * theData){
+ const ApiRegReq * const apiRegReq = (ApiRegReq *)&theData[0];
+ const NodeId nodeId = refToNode(apiRegReq->ref);
+
+#if 0
+ ndbout_c("ClusterMgr: Recd API_REGREQ from node %d", nodeId);
+#endif
+
+ assert(nodeId > 0 && nodeId < MAX_NODES);
+
+ Node & node = theNodes[nodeId];
+ assert(node.defined == true);
+ assert(node.connected == true);
+
+ if(node.m_info.m_version != apiRegReq->version){
+ node.m_info.m_version = apiRegReq->version;
+
+ if (getMajor(node.m_info.m_version) < getMajor(NDB_VERSION) ||
+ getMinor(node.m_info.m_version) < getMinor(NDB_VERSION)) {
+ node.compatible = false;
+ } else {
+ node.compatible = true;
+ }
+ }
+
+ NdbApiSignal signal(numberToRef(API_CLUSTERMGR, theFacade.ownId()));
+ signal.theVerId_signalNumber = GSN_API_REGCONF;
+ signal.theReceiversBlockNumber = API_CLUSTERMGR;
+ signal.theTrace = 0;
+ signal.theLength = ApiRegConf::SignalLength;
+
+ ApiRegConf * const conf = CAST_PTR(ApiRegConf, signal.getDataPtrSend());
+ conf->qmgrRef = numberToRef(API_CLUSTERMGR, theFacade.ownId());
+ conf->version = NDB_VERSION;
+ conf->apiHeartbeatFrequency = node.hbFrequency;
+ theFacade.sendSignalUnCond(&signal, nodeId);
+}
+
+int global_mgmt_server_check = 0; // set to one in mgmtsrvr main;
+
+void
+ClusterMgr::execAPI_REGCONF(const Uint32 * theData){
+ const ApiRegConf * const apiRegConf = (ApiRegConf *)&theData[0];
+ const NodeId nodeId = refToNode(apiRegConf->qmgrRef);
+
+#if 0
+ ndbout_c("ClusterMgr: Recd API_REGCONF from node %d", nodeId);
+#endif
+
+ assert(nodeId > 0 && nodeId < MAX_NODES);
+
+ Node & node = theNodes[nodeId];
+ assert(node.defined == true);
+ assert(node.connected == true);
+
+ if(node.m_info.m_version != apiRegConf->version){
+ node.m_info.m_version = apiRegConf->version;
+ if (global_mgmt_server_check == 1)
+ node.compatible = ndbCompatible_mgmt_ndb(NDB_VERSION,
+ node.m_info.m_version);
+ else
+ node.compatible = ndbCompatible_api_ndb(NDB_VERSION,
+ node.m_info.m_version);
+ }
+
+ node.m_state = apiRegConf->nodeState;
+ if (node.compatible && (node.m_state.startLevel == NodeState::SL_STARTED ||
+ node.m_state.startLevel == NodeState::SL_SINGLEUSER)){
+ node.m_alive = true;
+ } else {
+ node.m_alive = false;
+ }//if
+ node.hbSent = 0;
+ node.hbCounter = 0;
+ if (node.m_info.m_type != NodeInfo::REP) {
+ node.hbFrequency = (apiRegConf->apiHeartbeatFrequency * 10) - 50;
+ }
+}
+
+void
+ClusterMgr::execAPI_REGREF(const Uint32 * theData){
+
+ ApiRegRef * ref = (ApiRegRef*)theData;
+
+ const NodeId nodeId = refToNode(ref->ref);
+
+ assert(nodeId > 0 && nodeId < MAX_NODES);
+
+ Node & node = theNodes[nodeId];
+ assert(node.connected == true);
+ assert(node.defined == true);
+
+ node.compatible = false;
+ node.m_alive = false;
+ node.m_state = NodeState::SL_NOTHING;
+ node.m_info.m_version = ref->version;
+
+ switch(ref->errorCode){
+ case ApiRegRef::WrongType:
+ ndbout_c("Node %d reports that this node should be a NDB node", nodeId);
+ abort();
+ case ApiRegRef::UnsupportedVersion:
+ default:
+ break;
+ }
+}
+
+void
+ClusterMgr::execNODE_FAILREP(const Uint32 * theData){
+ NodeFailRep * const nodeFail = (NodeFailRep *)&theData[0];
+ for(int i = 1; i<MAX_NODES; i++){
+ if(NodeBitmask::get(nodeFail->theNodes, i)){
+ reportNodeFailed(i);
+ }
+ }
+}
+
+void
+ClusterMgr::execNF_COMPLETEREP(const Uint32 * theData){
+ NFCompleteRep * const nfComp = (NFCompleteRep *)theData;
+
+ const NodeId nodeId = nfComp->failedNodeId;
+ assert(nodeId > 0 && nodeId < MAX_NODES);
+
+ theFacade.ReportNodeFailureComplete(nodeId);
+ theNodes[nodeId].nfCompleteRep = true;
+}
+
+void
+ClusterMgr::reportConnected(NodeId nodeId){
+ /**
+ * Ensure that we are sending heartbeat every 100 ms
+ * until we have got the first reply from NDB providing
+ * us with the real time-out period to use.
+ */
+ assert(nodeId > 0 && nodeId < MAX_NODES);
+
+ noOfConnectedNodes++;
+
+ Node & theNode = theNodes[nodeId];
+ theNode.connected = true;
+ theNode.hbSent = 0;
+ theNode.hbCounter = 0;
+
+ /**
+ * make sure the node itself is marked connected even
+ * if first API_REGCONF has not arrived
+ */
+ theNode.m_state.m_connected_nodes.set(nodeId);
+
+ if (theNode.m_info.m_type != NodeInfo::REP) {
+ theNode.hbFrequency = 0;
+ }
+ theNode.m_info.m_version = 0;
+ theNode.compatible = true;
+ theNode.nfCompleteRep = true;
+
+ theFacade.ReportNodeAlive(nodeId);
+}
+
+void
+ClusterMgr::reportDisconnected(NodeId nodeId){
+ assert(nodeId > 0 && nodeId < MAX_NODES);
+ assert(noOfConnectedNodes > 0);
+
+ noOfConnectedNodes--;
+ theNodes[nodeId].connected = false;
+
+ theNodes[nodeId].m_state.m_connected_nodes.clear();
+
+ reportNodeFailed(nodeId);
+}
+
+void
+ClusterMgr::reportNodeFailed(NodeId nodeId){
+
+ Node & theNode = theNodes[nodeId];
+
+ theNode.m_alive = false;
+ theNode.m_info.m_connectCount ++;
+
+ if(theNode.connected)
+ {
+ theFacade.doDisconnect(nodeId);
+ }
+ const bool report = (theNode.m_state.startLevel != NodeState::SL_NOTHING);
+ theNode.m_state.startLevel = NodeState::SL_NOTHING;
+
+ if(report)
+ {
+ theFacade.ReportNodeDead(nodeId);
+ }
+
+ theNode.nfCompleteRep = false;
+
+ if(noOfConnectedNodes == 0){
+ NFCompleteRep rep;
+ for(Uint32 i = 1; i<MAX_NODES; i++){
+ if(theNodes[i].defined && theNodes[i].nfCompleteRep == false){
+ rep.failedNodeId = i;
+ execNF_COMPLETEREP((Uint32*)&rep);
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ * Arbitrator
+ ******************************************************************************/
+ArbitMgr::ArbitMgr(TransporterFacade & _fac)
+ : theFacade(_fac)
+{
+ theThreadMutex = NdbMutex_Create();
+ theInputCond = NdbCondition_Create();
+ theInputMutex = NdbMutex_Create();
+
+ theRank = 0;
+ theDelay = 0;
+ theThread = 0;
+
+ theInputTimeout = 0;
+ theInputFull = false;
+ memset(&theInputFull, 0, sizeof(theInputFull));
+ theState = StateInit;
+
+ memset(&theStartReq, 0, sizeof(theStartReq));
+ memset(&theChooseReq1, 0, sizeof(theChooseReq1));
+ memset(&theChooseReq2, 0, sizeof(theChooseReq2));
+ memset(&theStopOrd, 0, sizeof(theStopOrd));
+}
+
+ArbitMgr::~ArbitMgr()
+{
+ NdbMutex_Destroy(theThreadMutex);
+ NdbCondition_Destroy(theInputCond);
+ NdbMutex_Destroy(theInputMutex);
+}
+
+// Start arbitrator thread. This is kernel request.
+// First stop any previous thread since it is a left-over
+// which was never used and which now has wrong ticket.
+void
+ArbitMgr::doStart(const Uint32* theData)
+{
+ ArbitSignal aSignal;
+ NdbMutex_Lock(theThreadMutex);
+ if (theThread != NULL) {
+ aSignal.init(GSN_ARBIT_STOPORD, NULL);
+ aSignal.data.code = StopRestart;
+ sendSignalToThread(aSignal);
+ void* value;
+ NdbThread_WaitFor(theThread, &value);
+ theThread = NULL;
+ theState = StateInit;
+ theInputFull = false;
+ }
+ aSignal.init(GSN_ARBIT_STARTREQ, theData);
+ sendSignalToThread(aSignal);
+ theThread = NdbThread_Create(
+ runArbitMgr_C, (void**)this, 32768, "ndb_arbitmgr",
+ NDB_THREAD_PRIO_HIGH);
+ NdbMutex_Unlock(theThreadMutex);
+}
+
+// The "choose me" signal from a candidate.
+void
+ArbitMgr::doChoose(const Uint32* theData)
+{
+ ArbitSignal aSignal;
+ aSignal.init(GSN_ARBIT_CHOOSEREQ, theData);
+ sendSignalToThread(aSignal);
+}
+
+// Stop arbitrator thread via stop signal from the kernel
+// or when exiting API program.
+void
+ArbitMgr::doStop(const Uint32* theData)
+{
+ DBUG_ENTER("ArbitMgr::doStop");
+ ArbitSignal aSignal;
+ NdbMutex_Lock(theThreadMutex);
+ if (theThread != NULL) {
+ aSignal.init(GSN_ARBIT_STOPORD, theData);
+ if (theData == 0) {
+ aSignal.data.code = StopExit;
+ } else {
+ aSignal.data.code = StopRequest;
+ }
+ sendSignalToThread(aSignal);
+ void* value;
+ NdbThread_WaitFor(theThread, &value);
+ theThread = NULL;
+ theState = StateInit;
+ }
+ NdbMutex_Unlock(theThreadMutex);
+ DBUG_VOID_RETURN;
+}
+
+// private methods
+
+extern "C"
+void*
+runArbitMgr_C(void* me)
+{
+ ((ArbitMgr*) me)->threadMain();
+ return NULL;
+}
+
+void
+ArbitMgr::sendSignalToThread(ArbitSignal& aSignal)
+{
+#ifdef DEBUG_ARBIT
+ char buf[17] = "";
+ ndbout << "arbit recv: ";
+ ndbout << " gsn=" << aSignal.gsn;
+ ndbout << " send=" << aSignal.data.sender;
+ ndbout << " code=" << aSignal.data.code;
+ ndbout << " node=" << aSignal.data.node;
+ ndbout << " ticket=" << aSignal.data.ticket.getText(buf, sizeof(buf));
+ ndbout << " mask=" << aSignal.data.mask.getText(buf, sizeof(buf));
+ ndbout << endl;
+#endif
+ aSignal.setTimestamp(); // signal arrival time
+ NdbMutex_Lock(theInputMutex);
+ while (theInputFull) {
+ NdbCondition_WaitTimeout(theInputCond, theInputMutex, 1000);
+ }
+ theInputBuffer = aSignal;
+ theInputFull = true;
+ NdbCondition_Signal(theInputCond);
+ NdbMutex_Unlock(theInputMutex);
+}
+
+void
+ArbitMgr::threadMain()
+{
+ ArbitSignal aSignal;
+ aSignal = theInputBuffer;
+ threadStart(aSignal);
+ bool stop = false;
+ while (! stop) {
+ NdbMutex_Lock(theInputMutex);
+ while (! theInputFull) {
+ NdbCondition_WaitTimeout(theInputCond, theInputMutex, theInputTimeout);
+ threadTimeout();
+ }
+ aSignal = theInputBuffer;
+ theInputFull = false;
+ NdbCondition_Signal(theInputCond);
+ NdbMutex_Unlock(theInputMutex);
+ switch (aSignal.gsn) {
+ case GSN_ARBIT_CHOOSEREQ:
+ threadChoose(aSignal);
+ break;
+ case GSN_ARBIT_STOPORD:
+ stop = true;
+ break;
+ }
+ }
+ threadStop(aSignal);
+}
+
+// handle events in the thread
+
+void
+ArbitMgr::threadStart(ArbitSignal& aSignal)
+{
+ theStartReq = aSignal;
+ sendStartConf(theStartReq, ArbitCode::ApiStart);
+ theState = StateStarted;
+ theInputTimeout = 1000;
+}
+
+void
+ArbitMgr::threadChoose(ArbitSignal& aSignal)
+{
+ switch (theState) {
+ case StateStarted: // first REQ
+ if (! theStartReq.data.match(aSignal.data)) {
+ sendChooseRef(aSignal, ArbitCode::ErrTicket);
+ break;
+ }
+ theChooseReq1 = aSignal;
+ if (theDelay == 0) {
+ sendChooseConf(aSignal, ArbitCode::WinChoose);
+ theState = StateFinished;
+ theInputTimeout = 1000;
+ break;
+ }
+ theState = StateChoose1;
+ theInputTimeout = 1;
+ return;
+ case StateChoose1: // second REQ within Delay
+ if (! theStartReq.data.match(aSignal.data)) {
+ sendChooseRef(aSignal, ArbitCode::ErrTicket);
+ break;
+ }
+ theChooseReq2 = aSignal;
+ theState = StateChoose2;
+ theInputTimeout = 1;
+ return;
+ case StateChoose2: // too many REQs - refuse all
+ if (! theStartReq.data.match(aSignal.data)) {
+ sendChooseRef(aSignal, ArbitCode::ErrTicket);
+ break;
+ }
+ sendChooseRef(theChooseReq1, ArbitCode::ErrToomany);
+ sendChooseRef(theChooseReq2, ArbitCode::ErrToomany);
+ sendChooseRef(aSignal, ArbitCode::ErrToomany);
+ theState = StateFinished;
+ theInputTimeout = 1000;
+ return;
+ default:
+ sendChooseRef(aSignal, ArbitCode::ErrState);
+ break;
+ }
+}
+
+void
+ArbitMgr::threadTimeout()
+{
+ switch (theState) {
+ case StateStarted:
+ break;
+ case StateChoose1:
+ if (theChooseReq1.getTimediff() < theDelay)
+ break;
+ sendChooseConf(theChooseReq1, ArbitCode::WinChoose);
+ theState = StateFinished;
+ theInputTimeout = 1000;
+ break;
+ case StateChoose2:
+ sendChooseConf(theChooseReq1, ArbitCode::WinChoose);
+ sendChooseConf(theChooseReq2, ArbitCode::LoseChoose);
+ theState = StateFinished;
+ theInputTimeout = 1000;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+ArbitMgr::threadStop(ArbitSignal& aSignal)
+{
+ switch (aSignal.data.code) {
+ case StopExit:
+ switch (theState) {
+ case StateStarted:
+ sendStopRep(theStartReq, 0);
+ break;
+ case StateChoose1: // just in time
+ sendChooseConf(theChooseReq1, ArbitCode::WinChoose);
+ break;
+ case StateChoose2:
+ sendChooseConf(theChooseReq1, ArbitCode::WinChoose);
+ sendChooseConf(theChooseReq2, ArbitCode::LoseChoose);
+ break;
+ case StateInit:
+ case StateFinished:
+ //??
+ break;
+ }
+ break;
+ case StopRequest:
+ break;
+ case StopRestart:
+ break;
+ }
+}
+
+// output routines
+
+void
+ArbitMgr::sendStartConf(ArbitSignal& aSignal, Uint32 code)
+{
+ ArbitSignal copySignal = aSignal;
+ copySignal.gsn = GSN_ARBIT_STARTCONF;
+ copySignal.data.code = code;
+ sendSignalToQmgr(copySignal);
+}
+
+void
+ArbitMgr::sendChooseConf(ArbitSignal& aSignal, Uint32 code)
+{
+ ArbitSignal copySignal = aSignal;
+ copySignal.gsn = GSN_ARBIT_CHOOSECONF;
+ copySignal.data.code = code;
+ sendSignalToQmgr(copySignal);
+}
+
+void
+ArbitMgr::sendChooseRef(ArbitSignal& aSignal, Uint32 code)
+{
+ ArbitSignal copySignal = aSignal;
+ copySignal.gsn = GSN_ARBIT_CHOOSEREF;
+ copySignal.data.code = code;
+ sendSignalToQmgr(copySignal);
+}
+
+void
+ArbitMgr::sendStopRep(ArbitSignal& aSignal, Uint32 code)
+{
+ ArbitSignal copySignal = aSignal;
+ copySignal.gsn = GSN_ARBIT_STOPREP;
+ copySignal.data.code = code;
+ sendSignalToQmgr(copySignal);
+}
+
+/**
+ * Send signal to QMGR. The input includes signal number and
+ * signal data. The signal data is normally a copy of a received
+ * signal so it contains expected arbitrator node id and ticket.
+ * The sender in signal data is the QMGR node id.
+ */
+void
+ArbitMgr::sendSignalToQmgr(ArbitSignal& aSignal)
+{
+ NdbApiSignal signal(numberToRef(API_CLUSTERMGR, theFacade.ownId()));
+
+ signal.theVerId_signalNumber = aSignal.gsn;
+ signal.theReceiversBlockNumber = QMGR;
+ signal.theTrace = 0;
+ signal.theLength = ArbitSignalData::SignalLength;
+
+ ArbitSignalData* sd = CAST_PTR(ArbitSignalData, signal.getDataPtrSend());
+
+ sd->sender = numberToRef(API_CLUSTERMGR, theFacade.ownId());
+ sd->code = aSignal.data.code;
+ sd->node = aSignal.data.node;
+ sd->ticket = aSignal.data.ticket;
+ sd->mask = aSignal.data.mask;
+
+#ifdef DEBUG_ARBIT
+ char buf[17] = "";
+ ndbout << "arbit send: ";
+ ndbout << " gsn=" << aSignal.gsn;
+ ndbout << " recv=" << aSignal.data.sender;
+ ndbout << " code=" << aSignal.data.code;
+ ndbout << " node=" << aSignal.data.node;
+ ndbout << " ticket=" << aSignal.data.ticket.getText(buf, sizeof(buf));
+ ndbout << " mask=" << aSignal.data.mask.getText(buf, sizeof(buf));
+ ndbout << endl;
+#endif
+
+ theFacade.lock_mutex();
+ theFacade.sendSignalUnCond(&signal, aSignal.data.sender);
+ theFacade.unlock_mutex();
+}
+
diff --git a/storage/ndb/src/ndbapi/ClusterMgr.hpp b/storage/ndb/src/ndbapi/ClusterMgr.hpp
new file mode 100644
index 00000000000..cc3cf66c8aa
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ClusterMgr.hpp
@@ -0,0 +1,215 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ClusterMgr_H
+#define ClusterMgr_H
+
+#include "API.hpp"
+#include <ndb_limits.h>
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <signaldata/ArbitSignalData.hpp>
+#include <signaldata/NodeStateSignalData.hpp>
+#include <NodeInfo.hpp>
+#include <NodeState.hpp>
+
+extern "C" void* runClusterMgr_C(void * me);
+
+
+/**
+ * @class ClusterMgr
+ */
+class ClusterMgr {
+ friend void* runClusterMgr_C(void * me);
+ friend void execute(void *, struct SignalHeader * const,
+ Uint8, Uint32 * const, LinearSectionPtr ptr[3]);
+public:
+ ClusterMgr(class TransporterFacade &);
+ ~ClusterMgr();
+ void init(struct ndb_mgm_configuration_iterator & config);
+
+ void reportConnected(NodeId nodeId);
+ void reportDisconnected(NodeId nodeId);
+
+ bool checkUpgradeCompatability(Uint32 nodeVersion);
+
+ void doStop();
+ void startThread();
+
+private:
+ void threadMain();
+
+ int theStop;
+ class TransporterFacade & theFacade;
+
+public:
+ struct Node {
+ Node();
+ bool defined;
+ bool connected; // Transporter connected
+ bool compatible; // Version is compatible
+ bool nfCompleteRep; // NF Complete Rep has arrived
+ bool m_alive; // Node is alive
+
+ NodeInfo m_info;
+ NodeState m_state;
+
+ /**
+ * Heartbeat stuff
+ */
+ Uint32 hbFrequency; // Heartbeat frequence
+ Uint32 hbCounter; // # milliseconds passed since last hb sent
+ Uint32 hbSent; // # heartbeats sent (without answer)
+ };
+
+ const Node & getNodeInfo(NodeId) const;
+ Uint32 getNoOfConnectedNodes() const;
+
+private:
+ Uint32 noOfConnectedNodes;
+ Node theNodes[MAX_NODES];
+ NdbThread* theClusterMgrThread;
+
+ /**
+ * Used for controlling start/stop of the thread
+ */
+ NdbMutex* clusterMgrThreadMutex;
+
+ void showState(NodeId nodeId);
+ void reportNodeFailed(NodeId nodeId);
+
+ /**
+ * Signals received
+ */
+ void execAPI_REGREQ (const Uint32 * theData);
+ void execAPI_REGCONF (const Uint32 * theData);
+ void execAPI_REGREF (const Uint32 * theData);
+ void execNODE_FAILREP (const Uint32 * theData);
+ void execNF_COMPLETEREP(const Uint32 * theData);
+};
+
+inline
+const ClusterMgr::Node &
+ClusterMgr::getNodeInfo(NodeId nodeId) const {
+ return theNodes[nodeId];
+}
+
+inline
+Uint32
+ClusterMgr::getNoOfConnectedNodes() const {
+ return noOfConnectedNodes;
+}
+
+/*****************************************************************************/
+
+/**
+ * @class ArbitMgr
+ * Arbitration manager. Runs in separate thread.
+ * Started only by a request from the kernel.
+ */
+
+extern "C" void* runArbitMgr_C(void* me);
+
+class ArbitMgr
+{
+public:
+ ArbitMgr(class TransporterFacade &);
+ ~ArbitMgr();
+
+ inline void setRank(unsigned n) { theRank = n; }
+ inline void setDelay(unsigned n) { theDelay = n; }
+
+ void doStart(const Uint32* theData);
+ void doChoose(const Uint32* theData);
+ void doStop(const Uint32* theData);
+
+ friend void* runArbitMgr_C(void* me);
+
+private:
+ class TransporterFacade & theFacade;
+ unsigned theRank;
+ unsigned theDelay;
+
+ void threadMain();
+ NdbThread* theThread;
+ NdbMutex* theThreadMutex; // not really needed
+
+ struct ArbitSignal {
+ GlobalSignalNumber gsn;
+ ArbitSignalData data;
+ NDB_TICKS timestamp;
+
+ inline void init(GlobalSignalNumber aGsn, const Uint32* aData) {
+ gsn = aGsn;
+ if (aData != NULL)
+ memcpy(&data, aData, sizeof(data));
+ else
+ memset(&data, 0, sizeof(data));
+ }
+
+ inline void setTimestamp() {
+ timestamp = NdbTick_CurrentMillisecond();
+ }
+
+ inline NDB_TICKS getTimediff() {
+ NDB_TICKS now = NdbTick_CurrentMillisecond();
+ return now < timestamp ? 0 : now - timestamp;
+ }
+ };
+
+ NdbMutex* theInputMutex;
+ NdbCondition* theInputCond;
+ int theInputTimeout;
+ bool theInputFull; // the predicate
+ ArbitSignal theInputBuffer; // shared buffer
+
+ void sendSignalToThread(ArbitSignal& aSignal);
+
+ enum State { // thread states
+ StateInit,
+ StateStarted, // thread started
+ StateChoose1, // received one valid REQ
+ StateChoose2, // received two valid REQs
+ StateFinished // finished one way or other
+ };
+ State theState;
+
+ enum Stop { // stop code in ArbitSignal.data.code
+ StopExit = 1, // at API exit
+ StopRequest = 2, // request from kernel
+ StopRestart = 3 // stop before restart
+ };
+
+ void threadStart(ArbitSignal& aSignal); // handle thread events
+ void threadChoose(ArbitSignal& aSignal);
+ void threadTimeout();
+ void threadStop(ArbitSignal& aSignal);
+
+ ArbitSignal theStartReq;
+ ArbitSignal theChooseReq1;
+ ArbitSignal theChooseReq2;
+ ArbitSignal theStopOrd;
+
+ void sendStartConf(ArbitSignal& aSignal, Uint32);
+ void sendChooseRef(ArbitSignal& aSignal, Uint32);
+ void sendChooseConf(ArbitSignal& aSignal, Uint32);
+ void sendStopRep(ArbitSignal& aSignal, Uint32);
+
+ void sendSignalToQmgr(ArbitSignal& aSignal);
+};
+
+#endif
diff --git a/storage/ndb/src/ndbapi/DictCache.cpp b/storage/ndb/src/ndbapi/DictCache.cpp
new file mode 100644
index 00000000000..da9d5b70d47
--- /dev/null
+++ b/storage/ndb/src/ndbapi/DictCache.cpp
@@ -0,0 +1,277 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "DictCache.hpp"
+#include "NdbDictionaryImpl.hpp"
+#include <NdbTick.h>
+#include <NdbCondition.h>
+#include <NdbSleep.h>
+
+Ndb_local_table_info *
+Ndb_local_table_info::create(NdbTableImpl *table_impl, Uint32 sz)
+{
+ Uint32 tot_size= sizeof(NdbTableImpl *) + ((sz+7) & ~7); // round to Uint64
+ void *data= malloc(tot_size);
+ if (data == 0)
+ return 0;
+ memset(data, 0, tot_size);
+ new (data) Ndb_local_table_info(table_impl);
+ return (Ndb_local_table_info *) data;
+}
+
+void Ndb_local_table_info::destroy(Ndb_local_table_info *info)
+{
+ free((void *)info);
+}
+
+Ndb_local_table_info::Ndb_local_table_info(NdbTableImpl *table_impl)
+{
+ m_table_impl= table_impl;
+}
+
+Ndb_local_table_info::~Ndb_local_table_info()
+{
+}
+
+LocalDictCache::LocalDictCache(){
+ m_tableHash.createHashTable();
+}
+
+LocalDictCache::~LocalDictCache(){
+ m_tableHash.releaseHashTable();
+}
+
+Ndb_local_table_info *
+LocalDictCache::get(const char * name){
+ const Uint32 len = strlen(name);
+ return m_tableHash.getData(name, len);
+}
+
+void
+LocalDictCache::put(const char * name, Ndb_local_table_info * tab_info){
+ const Uint32 id = tab_info->m_table_impl->m_tableId;
+
+ m_tableHash.insertKey(name, strlen(name), id, tab_info);
+}
+
+void
+LocalDictCache::drop(const char * name){
+ Ndb_local_table_info *info= m_tableHash.deleteKey(name, strlen(name));
+ DBUG_ASSERT(info != 0);
+ Ndb_local_table_info::destroy(info);
+}
+
+/*****************************************************************
+ * Global cache
+ */
+GlobalDictCache::GlobalDictCache(){
+ m_tableHash.createHashTable();
+ m_waitForTableCondition = NdbCondition_Create();
+}
+
+GlobalDictCache::~GlobalDictCache(){
+ NdbElement_t<Vector<TableVersion> > * curr = m_tableHash.getNext(0);
+ while(curr != 0){
+ Vector<TableVersion> * vers = curr->theData;
+ const unsigned sz = vers->size();
+ for(unsigned i = 0; i<sz ; i++){
+ if((* vers)[i].m_impl != 0)
+ delete (* vers)[i].m_impl;
+ }
+ delete curr->theData;
+ curr = m_tableHash.getNext(curr);
+ }
+
+ m_tableHash.releaseHashTable();
+ NdbCondition_Destroy(m_waitForTableCondition);
+}
+
+#include <NdbOut.hpp>
+
+NdbTableImpl *
+GlobalDictCache::get(const char * name)
+{
+ const Uint32 len = strlen(name);
+ Vector<TableVersion> * versions = 0;
+ versions = m_tableHash.getData(name, len);
+ if(versions == 0){
+ versions = new Vector<TableVersion>(2);
+ m_tableHash.insertKey(name, len, 0, versions);
+ }
+
+ int waitTime = 100;
+
+ bool retreive = false;
+ while(versions->size() > 0 && !retreive){
+ TableVersion * ver = & versions->back();
+ switch(ver->m_status){
+ case OK:
+ ver->m_refCount++;
+ return ver->m_impl;
+ case DROPPED:
+ retreive = true; // Break loop
+ break;
+ case RETREIVING:
+ NdbCondition_WaitTimeout(m_waitForTableCondition, m_mutex, waitTime);
+ continue;
+ }
+ }
+
+ /**
+ * Create new...
+ */
+ TableVersion tmp;
+ tmp.m_version = 0;
+ tmp.m_impl = 0;
+ tmp.m_status = RETREIVING;
+ tmp.m_refCount = 1; // The one retreiving it
+ versions->push_back(tmp);
+ return 0;
+}
+
+NdbTableImpl *
+GlobalDictCache::put(const char * name, NdbTableImpl * tab)
+{
+ const Uint32 len = strlen(name);
+ Vector<TableVersion> * vers = m_tableHash.getData(name, len);
+ if(vers == 0){
+ // Should always tried to retreive it first
+ // and then there should be a record
+ abort();
+ }
+
+ const Uint32 sz = vers->size();
+ if(sz == 0){
+ // Should always tried to retreive it first
+ // and then there should be a record
+ abort();
+ }
+
+ TableVersion & ver = vers->back();
+ if(ver.m_status != RETREIVING ||
+ ver.m_impl != 0 ||
+ ver.m_version != 0 ||
+ ver.m_refCount == 0){
+ abort();
+ }
+
+ if(tab == 0){
+ // No table found in db
+ vers->erase(sz - 1);
+ } else {
+ ver.m_impl = tab;
+ ver.m_version = tab->m_version;
+ ver.m_status = OK;
+ }
+
+ NdbCondition_Broadcast(m_waitForTableCondition);
+ return tab;
+}
+
+void
+GlobalDictCache::drop(NdbTableImpl * tab)
+{
+ unsigned i;
+ const Uint32 len = strlen(tab->m_internalName.c_str());
+ Vector<TableVersion> * vers =
+ m_tableHash.getData(tab->m_internalName.c_str(), len);
+ if(vers == 0){
+ // Should always tried to retreive it first
+ // and then there should be a record
+ abort();
+ }
+
+ const Uint32 sz = vers->size();
+ if(sz == 0){
+ // Should always tried to retreive it first
+ // and then there should be a record
+ abort();
+ }
+
+ for(i = 0; i < sz; i++){
+ TableVersion & ver = (* vers)[i];
+ if(ver.m_impl == tab){
+ if(ver.m_refCount == 0 || ver.m_status == RETREIVING ||
+ ver.m_version != tab->m_version){
+ ndbout_c("Dropping with refCount=%d status=%d impl=%p",
+ ver.m_refCount, ver.m_status, ver.m_impl);
+ break;
+ }
+
+ ver.m_refCount--;
+ ver.m_status = DROPPED;
+ if(ver.m_refCount == 0){
+ delete ver.m_impl;
+ vers->erase(i);
+ }
+ return;
+ }
+ }
+
+ for(i = 0; i<sz; i++){
+ TableVersion & ver = (* vers)[i];
+ ndbout_c("%d: version: %d refCount: %d status: %d impl: %p",
+ i, ver.m_version, ver.m_refCount, ver.m_status, ver.m_impl);
+ }
+
+ abort();
+}
+
+void
+GlobalDictCache::release(NdbTableImpl * tab){
+ unsigned i;
+ const Uint32 len = strlen(tab->m_internalName.c_str());
+ Vector<TableVersion> * vers =
+ m_tableHash.getData(tab->m_internalName.c_str(), len);
+ if(vers == 0){
+ // Should always tried to retreive it first
+ // and then there should be a record
+ abort();
+ }
+
+ const Uint32 sz = vers->size();
+ if(sz == 0){
+ // Should always tried to retreive it first
+ // and then there should be a record
+ abort();
+ }
+
+ for(i = 0; i < sz; i++){
+ TableVersion & ver = (* vers)[i];
+ if(ver.m_impl == tab){
+ if(ver.m_refCount == 0 || ver.m_status == RETREIVING ||
+ ver.m_version != tab->m_version){
+ ndbout_c("Releasing with refCount=%d status=%d impl=%p",
+ ver.m_refCount, ver.m_status, ver.m_impl);
+ break;
+ }
+
+ ver.m_refCount--;
+ return;
+ }
+ }
+
+ for(i = 0; i<sz; i++){
+ TableVersion & ver = (* vers)[i];
+ ndbout_c("%d: version: %d refCount: %d status: %d impl: %p",
+ i, ver.m_version, ver.m_refCount, ver.m_status, ver.m_impl);
+ }
+
+ abort();
+}
+
+template class Vector<GlobalDictCache::TableVersion>;
diff --git a/storage/ndb/src/ndbapi/DictCache.hpp b/storage/ndb/src/ndbapi/DictCache.hpp
new file mode 100644
index 00000000000..58c08a93e61
--- /dev/null
+++ b/storage/ndb/src/ndbapi/DictCache.hpp
@@ -0,0 +1,92 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DictCache_H
+#define DictCache_H
+
+#include <ndb_types.h>
+#include <kernel_types.h>
+#include <NdbError.hpp>
+#include <BaseString.hpp>
+#include <Vector.hpp>
+#include <UtilBuffer.hpp>
+#include <NdbDictionary.hpp>
+#include <Ndb.hpp>
+#include <NdbCondition.h>
+#include "NdbLinHash.hpp"
+
+class Ndb_local_table_info {
+public:
+ static Ndb_local_table_info *create(NdbTableImpl *table_impl, Uint32 sz=0);
+ static void destroy(Ndb_local_table_info *);
+ NdbTableImpl *m_table_impl;
+ Uint64 m_local_data[1];
+private:
+ Ndb_local_table_info(NdbTableImpl *table_impl);
+ ~Ndb_local_table_info();
+};
+
+/**
+ * A non thread safe dict cache
+ */
+class LocalDictCache {
+public:
+ LocalDictCache();
+ ~LocalDictCache();
+
+ Ndb_local_table_info * get(const char * name);
+
+ void put(const char * name, Ndb_local_table_info *);
+ void drop(const char * name);
+
+ NdbLinHash<Ndb_local_table_info> m_tableHash; // On name
+};
+
+/**
+ * A thread safe dict cache
+ */
+class GlobalDictCache : public NdbLockable {
+public:
+ GlobalDictCache();
+ ~GlobalDictCache();
+
+ NdbTableImpl * get(const char * name);
+
+ NdbTableImpl* put(const char * name, NdbTableImpl *);
+ void drop(NdbTableImpl *);
+ void release(NdbTableImpl *);
+public:
+ enum Status {
+ OK = 0,
+ DROPPED = 1,
+ RETREIVING = 2
+ };
+
+private:
+ struct TableVersion {
+ Uint32 m_version;
+ Uint32 m_refCount;
+ NdbTableImpl * m_impl;
+ Status m_status;
+ };
+
+ NdbLinHash<Vector<TableVersion> > m_tableHash;
+ NdbCondition * m_waitForTableCondition;
+};
+
+#endif
+
+
diff --git a/storage/ndb/src/ndbapi/Makefile.am b/storage/ndb/src/ndbapi/Makefile.am
new file mode 100644
index 00000000000..b734e058b87
--- /dev/null
+++ b/storage/ndb/src/ndbapi/Makefile.am
@@ -0,0 +1,62 @@
+#SUBDIRS = signal-sender
+
+noinst_LTLIBRARIES = libndbapi.la
+
+libndbapi_la_SOURCES = \
+ TransporterFacade.cpp \
+ ClusterMgr.cpp \
+ Ndb.cpp \
+ NdbPoolImpl.cpp \
+ NdbPool.cpp \
+ Ndblist.cpp \
+ Ndbif.cpp \
+ Ndbinit.cpp \
+ Ndberr.cpp \
+ ndberror.c \
+ NdbErrorOut.cpp \
+ NdbTransaction.cpp \
+ NdbTransactionScan.cpp \
+ NdbOperation.cpp \
+ NdbOperationSearch.cpp \
+ NdbOperationScan.cpp \
+ NdbOperationInt.cpp \
+ NdbOperationDefine.cpp \
+ NdbOperationExec.cpp \
+ NdbScanOperation.cpp NdbScanFilter.cpp \
+ NdbIndexOperation.cpp \
+ NdbEventOperation.cpp \
+ NdbEventOperationImpl.cpp \
+ NdbApiSignal.cpp \
+ NdbRecAttr.cpp \
+ NdbUtil.cpp \
+ NdbReceiver.cpp \
+ NdbDictionary.cpp \
+ NdbDictionaryImpl.cpp \
+ DictCache.cpp \
+ ndb_cluster_connection.cpp \
+ NdbBlob.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/mgmapi
+
+# Ndbapi cannot handle -O3
+NDB_CXXFLAGS_RELEASE_LOC = -O2
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libndbapi.dsp
+
+libndbapi.dsp: Makefile \
+ $(top_srcdir)/ndb/config/win-lib.am \
+ $(top_srcdir)/ndb/config/win-name \
+ $(top_srcdir)/ndb/config/win-includes \
+ $(top_srcdir)/ndb/config/win-sources \
+ $(top_srcdir)/ndb/config/win-libraries
+ cat $(top_srcdir)/ndb/config/win-lib.am > $@
+ @$(top_srcdir)/ndb/config/win-name $@ $(noinst_LTLIBRARIES)
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(libndbapi_la_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB $(LDADD)
diff --git a/storage/ndb/src/ndbapi/Ndb.cpp b/storage/ndb/src/ndbapi/Ndb.cpp
new file mode 100644
index 00000000000..b047ae1bd1a
--- /dev/null
+++ b/storage/ndb/src/ndbapi/Ndb.cpp
@@ -0,0 +1,1317 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+
+
+/*****************************************************************************
+Name: Ndb.cpp
+******************************************************************************/
+
+#include <ndb_global.h>
+
+
+#include "NdbApiSignal.hpp"
+#include "NdbImpl.hpp"
+#include <NdbOperation.hpp>
+#include <NdbTransaction.hpp>
+#include <NdbEventOperation.hpp>
+#include <NdbRecAttr.hpp>
+#include <md5_hash.hpp>
+#include <NdbSleep.h>
+#include <NdbOut.hpp>
+#include <ndb_limits.h>
+#include "API.hpp"
+#include <NdbEnv.h>
+#include <BaseString.hpp>
+
+/****************************************************************************
+void connect();
+
+Connect to any node which has no connection at the moment.
+****************************************************************************/
+NdbTransaction* Ndb::doConnect(Uint32 tConNode)
+{
+ Uint32 tNode;
+ Uint32 tAnyAlive = 0;
+ int TretCode= 0;
+
+ DBUG_ENTER("Ndb::doConnect");
+
+ if (tConNode != 0) {
+ TretCode = NDB_connect(tConNode);
+ if ((TretCode == 1) || (TretCode == 2)) {
+//****************************************************************************
+// We have connections now to the desired node. Return
+//****************************************************************************
+ DBUG_RETURN(getConnectedNdbTransaction(tConNode));
+ } else if (TretCode != 0) {
+ tAnyAlive = 1;
+ }//if
+ }//if
+//****************************************************************************
+// We will connect to any node. Make sure that we have connections to all
+// nodes.
+//****************************************************************************
+ if (theImpl->m_optimized_node_selection)
+ {
+ Ndb_cluster_connection_node_iter &node_iter=
+ theImpl->m_node_iter;
+ theImpl->m_ndb_cluster_connection.init_get_next_node(node_iter);
+ while ((tNode= theImpl->m_ndb_cluster_connection.get_next_node(node_iter)))
+ {
+ TretCode= NDB_connect(tNode);
+ if ((TretCode == 1) ||
+ (TretCode == 2))
+ {
+//****************************************************************************
+// We have connections now to the desired node. Return
+//****************************************************************************
+ DBUG_RETURN(getConnectedNdbTransaction(tNode));
+ } else if (TretCode != 0) {
+ tAnyAlive= 1;
+ }//if
+ DBUG_PRINT("info",("tried node %d, TretCode %d, error code %d, %s",
+ tNode, TretCode, getNdbError().code,
+ getNdbError().message));
+ }
+ }
+ else // just do a regular round robin
+ {
+ Uint32 tNoOfDbNodes= theImpl->theNoOfDBnodes;
+ Uint32 &theCurrentConnectIndex= theImpl->theCurrentConnectIndex;
+ UintR Tcount = 0;
+ do {
+ theCurrentConnectIndex++;
+ if (theCurrentConnectIndex >= tNoOfDbNodes)
+ theCurrentConnectIndex = 0;
+
+ Tcount++;
+ tNode= theImpl->theDBnodes[theCurrentConnectIndex];
+ TretCode= NDB_connect(tNode);
+ if ((TretCode == 1) ||
+ (TretCode == 2))
+ {
+//****************************************************************************
+// We have connections now to the desired node. Return
+//****************************************************************************
+ DBUG_RETURN(getConnectedNdbTransaction(tNode));
+ } else if (TretCode != 0) {
+ tAnyAlive= 1;
+ }//if
+ DBUG_PRINT("info",("tried node %d TretCode %d", tNode, TretCode));
+ } while (Tcount < tNoOfDbNodes);
+ }
+//****************************************************************************
+// We were unable to find a free connection. If no node alive we will report
+// error code for cluster failure otherwise connection failure.
+//****************************************************************************
+ if (tAnyAlive == 1) {
+#ifdef VM_TRACE
+ ndbout << "TretCode = " << TretCode << endl;
+#endif
+ theError.code = 4006;
+ } else {
+ theError.code = 4009;
+ }//if
+ DBUG_RETURN(NULL);
+}
+
+int
+Ndb::NDB_connect(Uint32 tNode)
+{
+//****************************************************************************
+// We will perform seize of a transaction record in DBTC in the specified node.
+//***************************************************************************
+
+ int tReturnCode;
+ TransporterFacade *tp = TransporterFacade::instance();
+
+ DBUG_ENTER("Ndb::NDB_connect");
+
+ bool nodeAvail = tp->get_node_alive(tNode);
+ if(nodeAvail == false){
+ DBUG_RETURN(0);
+ }
+
+ NdbTransaction * tConArray = theConnectionArray[tNode];
+ if (tConArray != NULL) {
+ DBUG_RETURN(2);
+ }
+
+ NdbTransaction * tNdbCon = getNdbCon(); // Get free connection object.
+ if (tNdbCon == NULL) {
+ DBUG_RETURN(4);
+ }//if
+ NdbApiSignal* tSignal = getSignal(); // Get signal object
+ if (tSignal == NULL) {
+ releaseNdbCon(tNdbCon);
+ DBUG_RETURN(4);
+ }//if
+ if (tSignal->setSignal(GSN_TCSEIZEREQ) == -1) {
+ releaseNdbCon(tNdbCon);
+ releaseSignal(tSignal);
+ DBUG_RETURN(4);
+ }//if
+ tSignal->setData(tNdbCon->ptr2int(), 1);
+//************************************************
+// Set connection pointer as NdbTransaction object
+//************************************************
+ tSignal->setData(theMyRef, 2); // Set my block reference
+ tNdbCon->Status(NdbTransaction::Connecting); // Set status to connecting
+ Uint32 nodeSequence;
+ { // send and receive signal
+ Guard guard(tp->theMutexPtr);
+ nodeSequence = tp->getNodeSequence(tNode);
+ bool node_is_alive = tp->get_node_alive(tNode);
+ if (node_is_alive) {
+ tReturnCode = tp->sendSignal(tSignal, tNode);
+ releaseSignal(tSignal);
+ if (tReturnCode != -1) {
+ theImpl->theWaiter.m_node = tNode;
+ theImpl->theWaiter.m_state = WAIT_TC_SEIZE;
+ tReturnCode = receiveResponse();
+ }//if
+ } else {
+ releaseSignal(tSignal);
+ tReturnCode = -1;
+ }//if
+ }
+ if ((tReturnCode == 0) && (tNdbCon->Status() == NdbTransaction::Connected)) {
+ //************************************************
+ // Send and receive was successful
+ //************************************************
+ NdbTransaction* tPrevFirst = theConnectionArray[tNode];
+ tNdbCon->setConnectedNodeId(tNode, nodeSequence);
+
+ tNdbCon->setMyBlockReference(theMyRef);
+ theConnectionArray[tNode] = tNdbCon;
+ tNdbCon->theNext = tPrevFirst;
+ DBUG_RETURN(1);
+ } else {
+ releaseNdbCon(tNdbCon);
+//****************************************************************************
+// Unsuccessful connect is indicated by 3.
+//****************************************************************************
+ DBUG_PRINT("info",
+ ("unsuccessful connect tReturnCode %d, tNdbCon->Status() %d",
+ tReturnCode, tNdbCon->Status()));
+ DBUG_RETURN(3);
+ }//if
+}//Ndb::NDB_connect()
+
+NdbTransaction *
+Ndb::getConnectedNdbTransaction(Uint32 nodeId){
+ NdbTransaction* next = theConnectionArray[nodeId];
+ theConnectionArray[nodeId] = next->theNext;
+ next->theNext = NULL;
+
+ return next;
+}//Ndb::getConnectedNdbTransaction()
+
+/*****************************************************************************
+disconnect();
+
+Remark: Disconnect all connections to the database.
+*****************************************************************************/
+void
+Ndb::doDisconnect()
+{
+ DBUG_ENTER("Ndb::doDisconnect");
+ NdbTransaction* tNdbCon;
+ CHECK_STATUS_MACRO_VOID;
+
+ Uint32 tNoOfDbNodes = theImpl->theNoOfDBnodes;
+ Uint8 *theDBnodes= theImpl->theDBnodes;
+ DBUG_PRINT("info", ("theNoOfDBnodes=%d", tNoOfDbNodes));
+ UintR i;
+ for (i = 0; i < tNoOfDbNodes; i++) {
+ Uint32 tNode = theDBnodes[i];
+ tNdbCon = theConnectionArray[tNode];
+ while (tNdbCon != NULL) {
+ NdbTransaction* tmpNdbCon = tNdbCon;
+ tNdbCon = tNdbCon->theNext;
+ releaseConnectToNdb(tmpNdbCon);
+ }//while
+ }//for
+ tNdbCon = theTransactionList;
+ while (tNdbCon != NULL) {
+ NdbTransaction* tmpNdbCon = tNdbCon;
+ tNdbCon = tNdbCon->theNext;
+ releaseConnectToNdb(tmpNdbCon);
+ }//while
+ DBUG_VOID_RETURN;
+}//Ndb::disconnect()
+
+/*****************************************************************************
+int waitUntilReady(int timeout);
+
+Return Value: Returns 0 if the Ndb is ready within timeout seconds.
+ Returns -1 otherwise.
+Remark: Waits until a node has status != 0
+*****************************************************************************/
+int
+Ndb::waitUntilReady(int timeout)
+{
+ DBUG_ENTER("Ndb::waitUntilReady");
+ int secondsCounter = 0;
+ int milliCounter = 0;
+ int noChecksSinceFirstAliveFound = 0;
+ int id;
+
+ if (theInitState != Initialised) {
+ // Ndb::init is not called
+ theError.code = 4256;
+ DBUG_RETURN(-1);
+ }
+
+ while (theNode == 0) {
+ if (secondsCounter >= timeout)
+ {
+ theError.code = 4269;
+ DBUG_RETURN(-1);
+ }
+ NdbSleep_MilliSleep(100);
+ milliCounter += 100;
+ if (milliCounter >= 1000) {
+ secondsCounter++;
+ milliCounter = 0;
+ }//if
+ }
+
+ if (theImpl->m_ndb_cluster_connection.wait_until_ready
+ (timeout-secondsCounter,30) < 0)
+ {
+ theError.code = 4009;
+ DBUG_RETURN(-1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+/*****************************************************************************
+NdbTransaction* startTransaction();
+
+Return Value: Returns a pointer to a connection object.
+ Return NULL otherwise.
+Remark: Start transaction. Synchronous.
+*****************************************************************************/
+NdbTransaction*
+Ndb::startTransaction(const NdbDictionary::Table *table,
+ const char * keyData, Uint32 keyLen)
+{
+ DBUG_ENTER("Ndb::startTransaction");
+
+ if (theInitState == Initialised) {
+ theError.code = 0;
+ checkFailedNode();
+ /**
+ * If the user supplied key data
+ * We will make a qualified quess to which node is the primary for the
+ * the fragment and contact that node
+ */
+ Uint32 nodeId;
+ NdbTableImpl* impl;
+ if(table != 0 && keyData != 0 && (impl= &NdbTableImpl::getImpl(*table)))
+ {
+ Uint32 hashValue;
+ {
+ Uint32 buf[4];
+ if((UintPtr(keyData) & 7) == 0 && (keyLen & 3) == 0)
+ {
+ md5_hash(buf, (const Uint64*)keyData, keyLen >> 2);
+ }
+ else
+ {
+ Uint64 tmp[1000];
+ tmp[keyLen/8] = 0;
+ memcpy(tmp, keyData, keyLen);
+ md5_hash(buf, tmp, (keyLen+3) >> 2);
+ }
+ hashValue= buf[1];
+ }
+ const Uint16 *nodes;
+ Uint32 cnt= impl->get_nodes(hashValue, &nodes);
+ if(cnt)
+ nodeId= nodes[0];
+ else
+ nodeId= 0;
+ } else {
+ nodeId = 0;
+ }//if
+
+ {
+ NdbTransaction *trans= startTransactionLocal(0, nodeId);
+ DBUG_PRINT("exit",("start trans: 0x%x transid: 0x%llx",
+ trans, trans ? trans->getTransactionId() : 0));
+ DBUG_RETURN(trans);
+ }
+ } else {
+ DBUG_RETURN(NULL);
+ }//if
+}//Ndb::startTransaction()
+
+/*****************************************************************************
+NdbTransaction* hupp(NdbTransaction* pBuddyTrans);
+
+Return Value: Returns a pointer to a connection object.
+ Connected to the same node as pBuddyTrans
+ and also using the same transction id
+Remark: Start transaction. Synchronous.
+*****************************************************************************/
+NdbTransaction*
+Ndb::hupp(NdbTransaction* pBuddyTrans)
+{
+ DBUG_ENTER("Ndb::hupp");
+
+ DBUG_PRINT("enter", ("trans: 0x%x",pBuddyTrans));
+
+ Uint32 aPriority = 0;
+ if (pBuddyTrans == NULL){
+ DBUG_RETURN(startTransaction());
+ }
+
+ if (theInitState == Initialised) {
+ theError.code = 0;
+ checkFailedNode();
+
+ Uint32 nodeId = pBuddyTrans->getConnectedNodeId();
+ NdbTransaction* pCon = startTransactionLocal(aPriority, nodeId);
+ if(pCon == NULL)
+ DBUG_RETURN(NULL);
+
+ if (pCon->getConnectedNodeId() != nodeId){
+ // We could not get a connection to the desired node
+ // release the connection and return NULL
+ closeTransaction(pCon);
+ theError.code = 4006;
+ DBUG_RETURN(NULL);
+ }
+ pCon->setTransactionId(pBuddyTrans->getTransactionId());
+ pCon->setBuddyConPtr((Uint32)pBuddyTrans->getTC_ConnectPtr());
+ DBUG_PRINT("exit", ("hupp trans: 0x%x transid: 0x%llx",
+ pCon, pCon ? pCon->getTransactionId() : 0));
+ DBUG_RETURN(pCon);
+ } else {
+ DBUG_RETURN(NULL);
+ }//if
+}//Ndb::hupp()
+
+NdbTransaction*
+Ndb::startTransactionLocal(Uint32 aPriority, Uint32 nodeId)
+{
+#ifdef VM_TRACE
+ char buf[255];
+ const char* val = NdbEnv_GetEnv("NDB_TRANSACTION_NODE_ID", buf, 255);
+ if(val != 0){
+ nodeId = atoi(val);
+ }
+#endif
+
+ DBUG_ENTER("Ndb::startTransactionLocal");
+ DBUG_PRINT("enter", ("nodeid: %d", nodeId));
+
+ NdbTransaction* tConnection;
+ Uint64 tFirstTransId = theFirstTransId;
+ tConnection = doConnect(nodeId);
+ if (tConnection == NULL) {
+ DBUG_RETURN(NULL);
+ }//if
+ NdbTransaction* tConNext = theTransactionList;
+ tConnection->init();
+ theTransactionList = tConnection; // into a transaction list.
+ tConnection->next(tConNext); // Add the active connection object
+ tConnection->setTransactionId(tFirstTransId);
+ tConnection->thePriority = aPriority;
+ if ((tFirstTransId & 0xFFFFFFFF) == 0xFFFFFFFF) {
+ //---------------------------------------------------
+// Transaction id rolling round. We will start from
+// consecutive identity 0 again.
+//---------------------------------------------------
+ theFirstTransId = ((tFirstTransId >> 32) << 32);
+ } else {
+ theFirstTransId = tFirstTransId + 1;
+ }//if
+#ifdef VM_TRACE
+ if (tConnection->theListState != NdbTransaction::NotInList) {
+ printState("startTransactionLocal %x", tConnection);
+ abort();
+ }
+#endif
+ DBUG_RETURN(tConnection);
+}//Ndb::startTransactionLocal()
+
+/*****************************************************************************
+void closeTransaction(NdbTransaction* aConnection);
+
+Parameters: aConnection: the connection used in the transaction.
+Remark: Close transaction by releasing the connection and all operations.
+*****************************************************************************/
+void
+Ndb::closeTransaction(NdbTransaction* aConnection)
+{
+ DBUG_ENTER("Ndb::closeTransaction");
+ NdbTransaction* tCon;
+ NdbTransaction* tPreviousCon;
+
+ if (aConnection == NULL) {
+//-----------------------------------------------------
+// closeTransaction called on NULL pointer, destructive
+// application behaviour.
+//-----------------------------------------------------
+#ifdef VM_TRACE
+ printf("NULL into closeTransaction\n");
+#endif
+ DBUG_VOID_RETURN;
+ }//if
+ CHECK_STATUS_MACRO_VOID;
+
+ tCon = theTransactionList;
+
+ DBUG_PRINT("info",("close trans: 0x%x transid: 0x%llx",
+ aConnection, aConnection->getTransactionId()));
+ DBUG_PRINT("info",("magic number: 0x%x TCConPtr: 0x%x theMyRef: 0x%x 0x%x",
+ aConnection->theMagicNumber, aConnection->theTCConPtr,
+ aConnection->theMyRef, getReference()));
+
+ if (aConnection == tCon) { // Remove the active connection object
+ theTransactionList = tCon->next(); // from the transaction list.
+ } else {
+ while (aConnection != tCon) {
+ if (tCon == NULL) {
+//-----------------------------------------------------
+// closeTransaction called on non-existing transaction
+//-----------------------------------------------------
+
+ if(aConnection->theError.code == 4008){
+ /**
+ * When a SCAN timed-out, returning the NdbTransaction leads
+ * to reuse. And TC crashes when the API tries to reuse it to
+ * something else...
+ */
+#ifdef VM_TRACE
+ printf("Scan timeout:ed NdbTransaction-> "
+ "not returning it-> memory leak\n");
+#endif
+ DBUG_VOID_RETURN;
+ }
+
+#ifdef VM_TRACE
+ printf("Non-existing transaction into closeTransaction\n");
+ abort();
+#endif
+ DBUG_VOID_RETURN;
+ }//if
+ tPreviousCon = tCon;
+ tCon = tCon->next();
+ }//while
+ tPreviousCon->next(tCon->next());
+ }//if
+
+ aConnection->release();
+
+ if(aConnection->theError.code == 4008){
+ /**
+ * Something timed-out, returning the NdbTransaction leads
+ * to reuse. And TC crashes when the API tries to reuse it to
+ * something else...
+ */
+#ifdef VM_TRACE
+ printf("Con timeout:ed NdbTransaction-> not returning it-> memory leak\n");
+#endif
+ DBUG_VOID_RETURN;
+ }
+
+ if (aConnection->theReleaseOnClose == false) {
+ /**
+ * Put it back in idle list for that node
+ */
+ Uint32 nodeId = aConnection->getConnectedNodeId();
+ aConnection->theNext = theConnectionArray[nodeId];
+ theConnectionArray[nodeId] = aConnection;
+ DBUG_VOID_RETURN;
+ } else {
+ aConnection->theReleaseOnClose = false;
+ releaseNdbCon(aConnection);
+ }//if
+ DBUG_VOID_RETURN;
+}//Ndb::closeTransaction()
+
+/*****************************************************************************
+int* NdbTamper(int aAction, int aNode);
+
+Parameters: aAction Specifies what action to be taken
+ 1: Lock global checkpointing Can only be sent to master DIH, Parameter aNode ignored.
+ 2: UnLock global checkpointing Can only be sent to master DIH, Parameter aNode ignored.
+ 3: Crash node
+
+ aNode Specifies which node the action will be taken
+ -1: Master DIH
+ 0-16: Nodnumber
+
+Return Value: -1 Error .
+
+Remark: Sends a signal to DIH.
+*****************************************************************************/
+int
+Ndb::NdbTamper(TamperType aAction, int aNode)
+{
+ NdbTransaction* tNdbConn;
+ NdbApiSignal tSignal(theMyRef);
+ int tNode;
+ int tAction;
+ int ret_code;
+
+#ifdef CUSTOMER_RELEASE
+ return -1;
+#else
+ CHECK_STATUS_MACRO;
+ checkFailedNode();
+
+ theRestartGCI = 0;
+ switch (aAction) {
+// Translate enum to integer. This is done because the SCI layer
+// expects integers.
+ case LockGlbChp:
+ tAction = 1;
+ break;
+ case UnlockGlbChp:
+ tAction = 2;
+ break;
+ case CrashNode:
+ tAction = 3;
+ break;
+ case ReadRestartGCI:
+ tAction = 4;
+ break;
+ default:
+ theError.code = 4102;
+ return -1;
+ }
+
+ tNdbConn = getNdbCon(); // Get free connection object
+ if (tNdbConn == NULL) {
+ theError.code = 4000;
+ return -1;
+ }
+ tSignal.setSignal(GSN_DIHNDBTAMPER);
+ tSignal.setData (tAction, 1);
+ tSignal.setData(tNdbConn->ptr2int(),2);
+ tSignal.setData(theMyRef,3); // Set return block reference
+ tNdbConn->Status(NdbTransaction::Connecting); // Set status to connecting
+ TransporterFacade *tp = TransporterFacade::instance();
+ if (tAction == 3) {
+ tp->lock_mutex();
+ tp->sendSignal(&tSignal, aNode);
+ tp->unlock_mutex();
+ releaseNdbCon(tNdbConn);
+ } else if ( (tAction == 2) || (tAction == 1) ) {
+ tp->lock_mutex();
+ tNode = tp->get_an_alive_node();
+ if (tNode == 0) {
+ theError.code = 4002;
+ releaseNdbCon(tNdbConn);
+ return -1;
+ }//if
+ ret_code = tp->sendSignal(&tSignal,aNode);
+ tp->unlock_mutex();
+ releaseNdbCon(tNdbConn);
+ return ret_code;
+ } else {
+ do {
+ tp->lock_mutex();
+ // Start protected area
+ tNode = tp->get_an_alive_node();
+ tp->unlock_mutex();
+ // End protected area
+ if (tNode == 0) {
+ theError.code = 4009;
+ releaseNdbCon(tNdbConn);
+ return -1;
+ }//if
+ ret_code = sendRecSignal(tNode, WAIT_NDB_TAMPER, &tSignal, 0);
+ if (ret_code == 0) {
+ if (tNdbConn->Status() != NdbTransaction::Connected) {
+ theRestartGCI = 0;
+ }//if
+ releaseNdbCon(tNdbConn);
+ return theRestartGCI;
+ } else if ((ret_code == -5) || (ret_code == -2)) {
+ TRACE_DEBUG("Continue DIHNDBTAMPER when node failed/stopping");
+ } else {
+ return -1;
+ }//if
+ } while (1);
+ }
+ return 0;
+#endif
+}
+#if 0
+/****************************************************************************
+NdbSchemaCon* startSchemaTransaction();
+
+Return Value: Returns a pointer to a schema connection object.
+ Return NULL otherwise.
+Remark: Start schema transaction. Synchronous.
+****************************************************************************/
+NdbSchemaCon*
+Ndb::startSchemaTransaction()
+{
+ NdbSchemaCon* tSchemaCon;
+ if (theSchemaConToNdbList != NULL) {
+ theError.code = 4321;
+ return NULL;
+ }//if
+ tSchemaCon = new NdbSchemaCon(this);
+ if (tSchemaCon == NULL) {
+ theError.code = 4000;
+ return NULL;
+ }//if
+ theSchemaConToNdbList = tSchemaCon;
+ return tSchemaCon;
+}
+/*****************************************************************************
+void closeSchemaTransaction(NdbSchemaCon* aSchemaCon);
+
+Parameters: aSchemaCon: the schemacon used in the transaction.
+Remark: Close transaction by releasing the schemacon and all schemaop.
+*****************************************************************************/
+void
+Ndb::closeSchemaTransaction(NdbSchemaCon* aSchemaCon)
+{
+ if (theSchemaConToNdbList != aSchemaCon) {
+ abort();
+ return;
+ }//if
+ aSchemaCon->release();
+ delete aSchemaCon;
+ theSchemaConToNdbList = NULL;
+ return;
+}//Ndb::closeSchemaTransaction()
+#endif
+
+/*****************************************************************************
+void RestartGCI(int aRestartGCI);
+
+Remark: Set theRestartGCI on the NDB object
+*****************************************************************************/
+void
+Ndb::RestartGCI(int aRestartGCI)
+{
+ theRestartGCI = aRestartGCI;
+}
+
+/****************************************************************************
+int getBlockNumber(void);
+
+Remark:
+****************************************************************************/
+int
+Ndb::getBlockNumber()
+{
+ return theNdbBlockNumber;
+}
+
+NdbDictionary::Dictionary *
+Ndb::getDictionary() const {
+ return theDictionary;
+}
+
+/****************************************************************************
+int getNodeId();
+
+Remark:
+****************************************************************************/
+int
+Ndb::getNodeId()
+{
+ return theNode;
+}
+
+/****************************************************************************
+Uint64 getTupleIdFromNdb( Uint32 aTableId, Uint32 cacheSize );
+
+Parameters: aTableId : The TableId.
+ cacheSize: Prefetch this many values
+Remark: Returns a new TupleId to the application.
+ The TupleId comes from SYSTAB_0 where SYSKEY_0 = TableId.
+ It is initialized to (TableId << 48) + 1 in NdbcntrMain.cpp.
+****************************************************************************/
+#define DEBUG_TRACE(msg) \
+// ndbout << __FILE__ << " line: " << __LINE__ << " msg: " << msg << endl
+
+Uint64
+Ndb::getAutoIncrementValue(const char* aTableName, Uint32 cacheSize)
+{
+ DEBUG_TRACE("getAutoIncrementValue");
+ const char * internalTableName = internalizeTableName(aTableName);
+ Ndb_local_table_info *info=
+ theDictionary->get_local_table_info(internalTableName, false);
+ if (info == 0)
+ return ~0;
+ const NdbTableImpl *table= info->m_table_impl;
+ Uint64 tupleId = getTupleIdFromNdb(table->m_tableId, cacheSize);
+ return tupleId;
+}
+
+Uint64
+Ndb::getAutoIncrementValue(const NdbDictionary::Table * aTable, Uint32 cacheSize)
+{
+ DEBUG_TRACE("getAutoIncrementValue");
+ if (aTable == 0)
+ return ~0;
+ const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
+ Uint64 tupleId = getTupleIdFromNdb(table->m_tableId, cacheSize);
+ return tupleId;
+}
+
+Uint64
+Ndb::getTupleIdFromNdb(const char* aTableName, Uint32 cacheSize)
+{
+ const NdbTableImpl* table = theDictionary->getTable(aTableName);
+ if (table == 0)
+ return ~0;
+ return getTupleIdFromNdb(table->m_tableId, cacheSize);
+}
+
+Uint64
+Ndb::getTupleIdFromNdb(Uint32 aTableId, Uint32 cacheSize)
+{
+ if ( theFirstTupleId[aTableId] != theLastTupleId[aTableId] )
+ {
+ theFirstTupleId[aTableId]++;
+ return theFirstTupleId[aTableId];
+ }
+ else // theFirstTupleId == theLastTupleId
+ {
+ return opTupleIdOnNdb(aTableId, cacheSize, 0);
+ }
+}
+
+Uint64
+Ndb::readAutoIncrementValue(const char* aTableName)
+{
+ DEBUG_TRACE("readtAutoIncrementValue");
+ const NdbTableImpl* table = theDictionary->getTable(aTableName);
+ if (table == 0) {
+ theError= theDictionary->getNdbError();
+ return ~0;
+ }
+ Uint64 tupleId = readTupleIdFromNdb(table->m_tableId);
+ return tupleId;
+}
+
+Uint64
+Ndb::readAutoIncrementValue(const NdbDictionary::Table * aTable)
+{
+ DEBUG_TRACE("readtAutoIncrementValue");
+ if (aTable == 0)
+ return ~0;
+ const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
+ Uint64 tupleId = readTupleIdFromNdb(table->m_tableId);
+ return tupleId;
+}
+
+Uint64
+Ndb::readTupleIdFromNdb(Uint32 aTableId)
+{
+ if ( theFirstTupleId[aTableId] == theLastTupleId[aTableId] )
+ // Cache is empty, check next in database
+ return opTupleIdOnNdb(aTableId, 0, 3);
+
+ return theFirstTupleId[aTableId] + 1;
+}
+
+bool
+Ndb::setAutoIncrementValue(const char* aTableName, Uint64 val, bool increase)
+{
+ DEBUG_TRACE("setAutoIncrementValue " << val);
+ const char * internalTableName= internalizeTableName(aTableName);
+ Ndb_local_table_info *info=
+ theDictionary->get_local_table_info(internalTableName, false);
+ if (info == 0) {
+ theError= theDictionary->getNdbError();
+ return false;
+ }
+ const NdbTableImpl* table= info->m_table_impl;
+ return setTupleIdInNdb(table->m_tableId, val, increase);
+}
+
+bool
+Ndb::setAutoIncrementValue(const NdbDictionary::Table * aTable, Uint64 val, bool increase)
+{
+ DEBUG_TRACE("setAutoIncrementValue " << val);
+ if (aTable == 0)
+ return ~0;
+ const NdbTableImpl* table = & NdbTableImpl::getImpl(*aTable);
+ return setTupleIdInNdb(table->m_tableId, val, increase);
+}
+
+bool
+Ndb::setTupleIdInNdb(const char* aTableName, Uint64 val, bool increase )
+{
+ DEBUG_TRACE("setTupleIdInNdb");
+ const NdbTableImpl* table = theDictionary->getTable(aTableName);
+ if (table == 0) {
+ theError= theDictionary->getNdbError();
+ return false;
+ }
+ return setTupleIdInNdb(table->m_tableId, val, increase);
+}
+
+bool
+Ndb::setTupleIdInNdb(Uint32 aTableId, Uint64 val, bool increase )
+{
+ DEBUG_TRACE("setTupleIdInNdb");
+ if (increase)
+ {
+ if (theFirstTupleId[aTableId] != theLastTupleId[aTableId])
+ {
+ // We have a cache sequence
+ if (val <= theFirstTupleId[aTableId]+1)
+ return false;
+ if (val <= theLastTupleId[aTableId])
+ {
+ theFirstTupleId[aTableId] = val - 1;
+ return true;
+ }
+ // else continue;
+ }
+ return (opTupleIdOnNdb(aTableId, val, 2) == val);
+ }
+ else
+ return (opTupleIdOnNdb(aTableId, val, 1) == val);
+}
+
+Uint64
+Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op)
+{
+ DBUG_ENTER("Ndb::opTupleIdOnNdb");
+ DBUG_PRINT("enter", ("table=%u value=%llu op=%u", aTableId, opValue, op));
+
+ NdbTransaction* tConnection;
+ NdbOperation* tOperation= 0; // Compiler warning if not initialized
+ Uint64 tValue;
+ NdbRecAttr* tRecAttrResult;
+ int result;
+ Uint64 ret;
+
+ CHECK_STATUS_MACRO_ZERO;
+
+ BaseString currentDb(getDatabaseName());
+ BaseString currentSchema(getDatabaseSchemaName());
+
+ setDatabaseName("sys");
+ setDatabaseSchemaName("def");
+ tConnection = this->startTransaction();
+ if (tConnection == NULL)
+ goto error_return;
+
+ if (usingFullyQualifiedNames())
+ tOperation = tConnection->getNdbOperation("SYSTAB_0");
+ else
+ tOperation = tConnection->getNdbOperation("sys/def/SYSTAB_0");
+ if (tOperation == NULL)
+ goto error_handler;
+
+ switch (op)
+ {
+ case 0:
+ tOperation->interpretedUpdateTuple();
+ tOperation->equal("SYSKEY_0", aTableId );
+ tOperation->incValue("NEXTID", opValue);
+ tRecAttrResult = tOperation->getValue("NEXTID");
+
+ if (tConnection->execute( Commit ) == -1 )
+ goto error_handler;
+
+ tValue = tRecAttrResult->u_64_value();
+
+ theFirstTupleId[aTableId] = tValue - opValue;
+ theLastTupleId[aTableId] = tValue - 1;
+ ret = theFirstTupleId[aTableId];
+ break;
+ case 1:
+ tOperation->updateTuple();
+ tOperation->equal("SYSKEY_0", aTableId );
+ tOperation->setValue("NEXTID", opValue);
+
+ if (tConnection->execute( Commit ) == -1 )
+ goto error_handler;
+
+ theFirstTupleId[aTableId] = ~(Uint64)0;
+ theLastTupleId[aTableId] = ~(Uint64)0;
+ ret = opValue;
+ break;
+ case 2:
+ tOperation->interpretedUpdateTuple();
+ tOperation->equal("SYSKEY_0", aTableId );
+ tOperation->load_const_u64(1, opValue);
+ tOperation->read_attr("NEXTID", 2);
+ tOperation->branch_le(2, 1, 0);
+ tOperation->write_attr("NEXTID", 1);
+ tOperation->interpret_exit_ok();
+ tOperation->def_label(0);
+ tOperation->interpret_exit_nok(9999);
+
+ if ( (result = tConnection->execute( Commit )) == -1 )
+ goto error_handler;
+
+ if (result == 9999)
+ ret = ~(Uint64)0;
+ else
+ {
+ theFirstTupleId[aTableId] = theLastTupleId[aTableId] = opValue - 1;
+ ret = opValue;
+ }
+ break;
+ case 3:
+ tOperation->readTuple();
+ tOperation->equal("SYSKEY_0", aTableId );
+ tRecAttrResult = tOperation->getValue("NEXTID");
+ if (tConnection->execute( Commit ) == -1 )
+ goto error_handler;
+ ret = tRecAttrResult->u_64_value();
+ break;
+ default:
+ goto error_handler;
+ }
+
+ this->closeTransaction(tConnection);
+
+ // Restore current name space
+ setDatabaseName(currentDb.c_str());
+ setDatabaseSchemaName(currentSchema.c_str());
+
+ DBUG_RETURN(ret);
+
+ error_handler:
+ theError.code = tConnection->theError.code;
+ this->closeTransaction(tConnection);
+ error_return:
+ // Restore current name space
+ setDatabaseName(currentDb.c_str());
+ setDatabaseSchemaName(currentSchema.c_str());
+
+ DBUG_PRINT("error", ("ndb=%d con=%d op=%d",
+ theError.code,
+ tConnection ? tConnection->theError.code : -1,
+ tOperation ? tOperation->theError.code : -1));
+ DBUG_RETURN(~0);
+}
+
+Uint32
+convertEndian(Uint32 Data)
+{
+#ifdef WORDS_BIGENDIAN
+ Uint32 t1, t2, t3, t4;
+ t4 = (Data >> 24) & 255;
+ t3 = (Data >> 16) & 255;
+ t4 = t4 + (t3 << 8);
+ t2 = (Data >> 8) & 255;
+ t4 = t4 + (t2 << 16);
+ t1 = Data & 255;
+ t4 = t4 + (t1 << 24);
+ return t4;
+#else
+ return Data;
+#endif
+}
+const char * Ndb::getCatalogName() const
+{
+ return theDataBase;
+}
+
+void Ndb::setCatalogName(const char * a_catalog_name)
+{
+ if (a_catalog_name) {
+ BaseString::snprintf(theDataBase, sizeof(theDataBase), "%s",
+ a_catalog_name ? a_catalog_name : "");
+
+ int len = BaseString::snprintf(prefixName, sizeof(prefixName), "%s%c%s%c",
+ theDataBase, table_name_separator,
+ theDataBaseSchema, table_name_separator);
+ prefixEnd = prefixName + (len < (int) sizeof(prefixName) ? len :
+ sizeof(prefixName) - 1);
+ }
+}
+
+const char * Ndb::getSchemaName() const
+{
+ return theDataBaseSchema;
+}
+
+void Ndb::setSchemaName(const char * a_schema_name)
+{
+ if (a_schema_name) {
+ BaseString::snprintf(theDataBaseSchema, sizeof(theDataBase), "%s",
+ a_schema_name ? a_schema_name : "");
+
+ int len = BaseString::snprintf(prefixName, sizeof(prefixName), "%s%c%s%c",
+ theDataBase, table_name_separator,
+ theDataBaseSchema, table_name_separator);
+ prefixEnd = prefixName + (len < (int) sizeof(prefixName) ? len :
+ sizeof(prefixName) - 1);
+ }
+}
+
+/*
+Deprecated functions
+*/
+const char * Ndb::getDatabaseName() const
+{
+ return getCatalogName();
+}
+
+void Ndb::setDatabaseName(const char * a_catalog_name)
+{
+ setCatalogName(a_catalog_name);
+}
+
+const char * Ndb::getDatabaseSchemaName() const
+{
+ return getSchemaName();
+}
+
+void Ndb::setDatabaseSchemaName(const char * a_schema_name)
+{
+ setSchemaName(a_schema_name);
+}
+
+bool Ndb::usingFullyQualifiedNames()
+{
+ return fullyQualifiedNames;
+}
+
+const char *
+Ndb::externalizeTableName(const char * internalTableName, bool fullyQualifiedNames)
+{
+ if (fullyQualifiedNames) {
+ register const char *ptr = internalTableName;
+
+ // Skip database name
+ while (*ptr && *ptr++ != table_name_separator);
+ // Skip schema name
+ while (*ptr && *ptr++ != table_name_separator);
+ return ptr;
+ }
+ else
+ return internalTableName;
+}
+
+const char *
+Ndb::externalizeTableName(const char * internalTableName)
+{
+ return externalizeTableName(internalTableName, usingFullyQualifiedNames());
+}
+
+const char *
+Ndb::externalizeIndexName(const char * internalIndexName, bool fullyQualifiedNames)
+{
+ if (fullyQualifiedNames) {
+ register const char *ptr = internalIndexName;
+
+ // Scan name from the end
+ while (*ptr++); ptr--; // strend
+ while (ptr >= internalIndexName && *ptr != table_name_separator)
+ ptr--;
+
+ return ptr + 1;
+ }
+ else
+ return internalIndexName;
+}
+
+const char *
+Ndb::externalizeIndexName(const char * internalIndexName)
+{
+ return externalizeIndexName(internalIndexName, usingFullyQualifiedNames());
+}
+
+const char *
+Ndb::internalizeTableName(const char * externalTableName)
+{
+ if (fullyQualifiedNames) {
+ strncpy(prefixEnd, externalTableName, NDB_MAX_TAB_NAME_SIZE);
+ return prefixName;
+ }
+ else
+ return externalTableName;
+}
+
+const char *
+Ndb::internalizeIndexName(const NdbTableImpl * table,
+ const char * externalIndexName)
+{
+ if (fullyQualifiedNames) {
+ char tableId[10];
+ sprintf(tableId, "%d", table->m_tableId);
+ Uint32 tabIdLen = strlen(tableId);
+ strncpy(prefixEnd, tableId, tabIdLen);
+ prefixEnd[tabIdLen] = table_name_separator;
+ strncpy(prefixEnd + tabIdLen + 1,
+ externalIndexName, NDB_MAX_TAB_NAME_SIZE);
+ return prefixName;
+ }
+ else
+ return externalIndexName;
+}
+
+const BaseString
+Ndb::getDatabaseFromInternalName(const char * internalName)
+{
+ char * databaseName = new char[strlen(internalName) + 1];
+ strcpy(databaseName, internalName);
+ register char *ptr = databaseName;
+
+ /* Scan name for the first table_name_separator */
+ while (*ptr && *ptr != table_name_separator)
+ ptr++;
+ *ptr = '\0';
+ BaseString ret = BaseString(databaseName);
+ delete [] databaseName;
+ return ret;
+}
+
+const BaseString
+Ndb::getSchemaFromInternalName(const char * internalName)
+{
+ char * schemaName = new char[strlen(internalName)];
+ register const char *ptr1 = internalName;
+
+ /* Scan name for the second table_name_separator */
+ while (*ptr1 && *ptr1 != table_name_separator)
+ ptr1++;
+ strcpy(schemaName, ptr1 + 1);
+ register char *ptr = schemaName;
+ while (*ptr && *ptr != table_name_separator)
+ ptr++;
+ *ptr = '\0';
+ BaseString ret = BaseString(schemaName);
+ delete [] schemaName;
+ return ret;
+}
+
+NdbEventOperation* Ndb::createEventOperation(const char* eventName,
+ const int bufferLength)
+{
+ NdbEventOperation* tOp;
+
+ tOp = new NdbEventOperation(this, eventName, bufferLength);
+
+ if (tOp == 0)
+ {
+ theError.code= 4000;
+ return NULL;
+ }
+
+ if (tOp->getState() != NdbEventOperation::EO_CREATED) {
+ theError.code= tOp->getNdbError().code;
+ delete tOp;
+ tOp = NULL;
+ }
+
+ //now we have to look up this event in dict
+
+ return tOp;
+}
+
+int Ndb::dropEventOperation(NdbEventOperation* op) {
+ delete op;
+ return 0;
+}
+
+NdbGlobalEventBufferHandle* Ndb::getGlobalEventBufferHandle()
+{
+ return theGlobalEventBufferHandle;
+}
+
+//void Ndb::monitorEvent(NdbEventOperation *op, NdbEventCallback cb, void* rs)
+//{
+//}
+
+int
+Ndb::pollEvents(int aMillisecondNumber)
+{
+ return NdbEventOperation::wait(theGlobalEventBufferHandle,
+ aMillisecondNumber);
+}
+
+#ifdef VM_TRACE
+#include <NdbMutex.h>
+extern NdbMutex *ndb_print_state_mutex;
+
+static bool
+checkdups(NdbTransaction** list, unsigned no)
+{
+ for (unsigned i = 0; i < no; i++)
+ for (unsigned j = i + 1; j < no; j++)
+ if (list[i] == list[j])
+ return true;
+ return false;
+}
+void
+Ndb::printState(const char* fmt, ...)
+{
+ char buf[200];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+ NdbMutex_Lock(ndb_print_state_mutex);
+ bool dups = false;
+ unsigned i;
+ ndbout << buf << " ndb=" << hex << this << dec;
+#ifndef NDB_WIN32
+ ndbout << " thread=" << (int)pthread_self();
+#endif
+ ndbout << endl;
+ for (unsigned n = 0; n < MAX_NDB_NODES; n++) {
+ NdbTransaction* con = theConnectionArray[n];
+ if (con != 0) {
+ ndbout << "conn " << n << ":" << endl;
+ while (con != 0) {
+ con->printState();
+ con = con->theNext;
+ }
+ }
+ }
+ ndbout << "prepared: " << theNoOfPreparedTransactions<< endl;
+ if (checkdups(thePreparedTransactionsArray, theNoOfPreparedTransactions)) {
+ ndbout << "!! DUPS !!" << endl;
+ dups = true;
+ }
+ for (i = 0; i < theNoOfPreparedTransactions; i++)
+ thePreparedTransactionsArray[i]->printState();
+ ndbout << "sent: " << theNoOfSentTransactions<< endl;
+ if (checkdups(theSentTransactionsArray, theNoOfSentTransactions)) {
+ ndbout << "!! DUPS !!" << endl;
+ dups = true;
+ }
+ for (i = 0; i < theNoOfSentTransactions; i++)
+ theSentTransactionsArray[i]->printState();
+ ndbout << "completed: " << theNoOfCompletedTransactions<< endl;
+ if (checkdups(theCompletedTransactionsArray, theNoOfCompletedTransactions)) {
+ ndbout << "!! DUPS !!" << endl;
+ dups = true;
+ }
+ for (i = 0; i < theNoOfCompletedTransactions; i++)
+ theCompletedTransactionsArray[i]->printState();
+ NdbMutex_Unlock(ndb_print_state_mutex);
+}
+#endif
+
+
diff --git a/storage/ndb/src/ndbapi/NdbApiSignal.cpp b/storage/ndb/src/ndbapi/NdbApiSignal.cpp
new file mode 100644
index 00000000000..b1671e593e1
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbApiSignal.cpp
@@ -0,0 +1,279 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "API.hpp"
+#include "NdbApiSignal.hpp"
+
+/**
+ * The following include includes
+ * definitions directly from the kernel
+ *
+ * Definitions that is shared between kernel and the API
+ */
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/KeyInfo.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/TestOrd.hpp>
+#include <signaldata/CreateIndx.hpp>
+#include <signaldata/DropIndx.hpp>
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/IndxKeyInfo.hpp>
+#include <signaldata/IndxAttrInfo.hpp>
+#include <signaldata/TcHbRep.hpp>
+#include <signaldata/ScanTab.hpp>
+
+#include <NdbOut.hpp>
+
+/******************************************************************************
+NdbApiSignal();
+
+Return Value: None
+Remark: Creates a NdbApiSignal object.
+******************************************************************************/
+NdbApiSignal::NdbApiSignal(BlockReference ref)
+{
+ theVerId_signalNumber = 0; // 4 bit ver id - 16 bit gsn
+ theReceiversBlockNumber = 0; // Only 16 bit blocknum
+ theSendersBlockRef = refToBlock(ref);
+ theLength = 0;
+ theSendersSignalId = 0;
+ theSignalId = 0;
+ theTrace = 0;
+ m_noOfSections = 0;
+ m_fragmentInfo = 0;
+ for (int i = 0; i < 25; i++)
+ theData[i] = 0x13579753;
+
+ setDataPtr(&theData[0]);
+ theNextSignal = 0;
+}
+
+/**
+ * Copy constructor
+ */
+NdbApiSignal::NdbApiSignal(const NdbApiSignal &src) {
+ copyFrom(&src);
+}
+/******************************************************************************
+~NdbApiSignal();
+
+Return Value: None
+Remark: Delete a NdbApiSignal object.
+******************************************************************************/
+NdbApiSignal::~NdbApiSignal()
+{
+}
+/******************************************************************************
+int setSignal(NdbSignalType aNdbSignalType);
+
+Return Value: Return 0 : setSignal was successful.
+ Return tErrorCode In all other case.
+Parameters: aNdbSignalType: Type of signal.
+Remark: Set signal header and allocate 128 byte.
+******************************************************************************/
+int
+NdbApiSignal::setSignal(int aNdbSignalType)
+{
+ theSendersSignalId = 0;
+ switch (aNdbSignalType)
+ {
+ case GSN_DIHNDBTAMPER:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBDIH;
+ theVerId_signalNumber = GSN_DIHNDBTAMPER;
+ theLength = 3;
+ }
+ break;
+
+ case GSN_TCSEIZEREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TCSEIZEREQ;
+ theLength = 2;
+ }
+ break;
+
+ case GSN_TCKEYREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TCKEYREQ;
+ theLength = TcKeyReq::SignalLength;
+ }
+ break;
+
+ case GSN_TCRELEASEREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TCRELEASEREQ;
+ theLength = 3;
+ }
+ break;
+
+ case GSN_ATTRINFO:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_ATTRINFO;
+ theLength = AttrInfo::MaxSignalLength;
+ }
+ break;
+
+ case GSN_KEYINFO:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_KEYINFO;
+ theLength = KeyInfo::MaxSignalLength;
+ }
+ break;
+
+ case GSN_TCROLLBACKREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TCROLLBACKREQ;
+ theLength = 3;
+ }
+ break;
+
+ case GSN_TC_HBREP:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TC_HBREP;
+ theLength = TcHbRep::SignalLength;
+ }
+ break;
+
+ case GSN_TC_COMMITREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TC_COMMITREQ;
+ theLength = 3;
+ }
+ break;
+
+ case GSN_SCAN_TABREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_SCAN_TABREQ;
+ theLength = ScanTabReq::StaticLength;
+ }
+ break;
+
+ case GSN_SCAN_NEXTREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_SCAN_NEXTREQ;
+ theLength = ScanNextReq::SignalLength;
+ }
+ break;
+
+ case GSN_CREATE_INDX_REQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBDICT;
+ theVerId_signalNumber = GSN_CREATE_INDX_REQ;
+ theLength = CreateIndxReq::SignalLength;
+ }
+ break;
+
+ case GSN_DROP_INDX_REQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBDICT;
+ theVerId_signalNumber = GSN_DROP_INDX_REQ;
+ theLength = DropIndxReq::SignalLength;
+ }
+ break;
+
+ case GSN_TCINDXREQ:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_TCINDXREQ;
+ theLength = TcKeyReq::SignalLength;
+ }
+ break;
+
+ case GSN_INDXKEYINFO:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_INDXKEYINFO;
+ theLength = IndxKeyInfo::MaxSignalLength;
+ }
+ break;
+
+ case GSN_INDXATTRINFO:
+ {
+ theTrace = TestOrd::TraceAPI;
+ theReceiversBlockNumber = DBTC;
+ theVerId_signalNumber = GSN_INDXATTRINFO;
+ theLength = IndxAttrInfo::MaxSignalLength;
+ }
+ break;
+
+ default:
+ {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+NdbApiSignal::set(Uint8 trace,
+ Uint16 receiversBlockNumber,
+ Uint16 signalNumber,
+ Uint32 length){
+
+ theTrace = trace;
+ theReceiversBlockNumber = receiversBlockNumber;
+ theVerId_signalNumber = signalNumber;
+ theLength = length;
+}
+
+void
+NdbApiSignal::copyFrom(const NdbApiSignal * src){
+ theVerId_signalNumber = src->theVerId_signalNumber;
+ theReceiversBlockNumber = src->theReceiversBlockNumber;
+ theSendersBlockRef = src->theSendersBlockRef;
+ theLength = src->theLength;
+ theTrace = src->theTrace;
+
+ Uint32 * dstData = getDataPtrSend();
+ const Uint32 * srcData = src->getDataPtr();
+ for (Uint32 i = 0; i < theLength; i++)
+ dstData[i] = srcData[i];
+
+ setDataPtr(dstData);
+
+ /**
+ * NOTE that theSignalId is used as data ptr
+ * and should not be copied
+ * NOTE that theSendersSignalId is used as next pointer
+ * and should not be copied
+ */
+}
diff --git a/storage/ndb/src/ndbapi/NdbApiSignal.hpp b/storage/ndb/src/ndbapi/NdbApiSignal.hpp
new file mode 100644
index 00000000000..353c575d420
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbApiSignal.hpp
@@ -0,0 +1,224 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/**********************************************************************
+ * Name: NdbApiSignal.H
+ * Include:
+ * Link:
+ * Author: UABMNST Mona Natterkvist UAB/B/SD
+ * Date: 97----
+ * Version: 0.1
+ * Description: Interface between TIS and NDB
+ * Documentation:
+ * Adjust: 971204 UABMNST First version.
+ * Adjust: 000705 QABANAB Changes in Protocol2
+ * Comment:
+ *****************************************************************************/
+#ifndef NdbApiSignal_H
+#define NdbApiSignal_H
+
+#include <kernel_types.h>
+#include "TransporterFacade.hpp"
+#include <TransporterDefinitions.hpp>
+#include "Ndb.hpp"
+
+#define CAST_PTR(X,Y) static_cast<X*>(static_cast<void*>(Y))
+#define CAST_CONSTPTR(X,Y) static_cast<const X*>(static_cast<const void*>(Y))
+
+/**
+ * A NdbApiSignal : public SignalHeader
+ *
+ * Stores the address to theData in theSignalId
+ */
+class NdbApiSignal : public SignalHeader
+ {
+public:
+ NdbApiSignal(BlockReference myRef);
+ NdbApiSignal(const NdbApiSignal &);
+ NdbApiSignal(const SignalHeader &header)
+ : SignalHeader(header), theNextSignal(0), theRealData(0) {};
+ ~NdbApiSignal();
+
+ void set(Uint8 trace,
+ Uint16 receiversBlockNumber,
+ Uint16 signalNumber,
+ Uint32 length);
+
+
+ void setData(Uint32 aWord, Uint32 aDataNo);
+ Uint32 readData(Uint32 aDataNo) const; // Read word in signal
+
+ int setSignal(int NdbSignalType); // Set signal header
+ int readSignalNumber(); // Read signal number
+ Uint32 getLength() const;
+ void setLength(Uint32 aLength);
+ void next(NdbApiSignal* anApiSignal);
+ NdbApiSignal* next();
+
+ const Uint32 * getDataPtr() const;
+ Uint32 * getDataPtrSend();
+
+ NodeId get_sender_node();
+
+ /**
+ * Fragmentation
+ */
+ bool isFirstFragment() const { return m_fragmentInfo <= 1;}
+ bool isLastFragment() const {
+ return m_fragmentInfo == 0 || m_fragmentInfo == 3;
+ }
+
+ Uint32 getFragmentId() const {
+ return (m_fragmentInfo == 0 ? 0 : getDataPtr()[theLength - 1]);
+ }
+
+private:
+ friend void execute(void * callbackObj,
+ struct SignalHeader * const header,
+ Uint8 prio, Uint32 * const theData,
+ LinearSectionPtr ptr[3]);
+
+ void setDataPtr(Uint32 *);
+
+ friend class NdbTransaction;
+ friend class NdbScanReceiver;
+ friend class Table;
+ void copyFrom(const NdbApiSignal * src);
+
+ /**
+ * Only used when creating a signal in the api
+ */
+ Uint32 theData[25];
+ NdbApiSignal *theNextSignal;
+ Uint32 *theRealData;
+};
+/**********************************************************************
+NodeId get_sender_node
+Remark: Get the node id of the sender
+***********************************************************************/
+inline
+NodeId
+NdbApiSignal::get_sender_node()
+{
+ return refToNode(theSendersBlockRef);
+}
+
+/**********************************************************************
+void getLength
+Remark: Get the length of the signal.
+******************************************************************************/
+inline
+Uint32
+NdbApiSignal::getLength() const{
+ return theLength;
+}
+
+/**********************************************************************
+void setLength
+Parameters: aLength: Signal length
+Remark: Set the length in the signal.
+******************************************************************************/
+inline
+void
+NdbApiSignal::setLength(Uint32 aLength){
+ theLength = aLength;
+}
+
+/**********************************************************************
+void next(NdbApiSignal* aSignal);
+
+Parameters: aSignal: Signal object.
+Remark: Insert signal rear in a linked list.
+*****************************************************************************/
+inline
+void
+NdbApiSignal::next(NdbApiSignal* aSignal){
+ theNextSignal = aSignal;
+}
+/**********************************************************************
+NdbApiSignal* next();
+
+Return Value: Return theNext signal object if the next was successful.
+ Return NULL: In all other case.
+Remark: Read the theNext in signal.
+*****************************************************************************/
+inline
+NdbApiSignal*
+NdbApiSignal::next(){
+ return theNextSignal;
+}
+/**********************************************************************
+int readSignalNo();
+
+Return Value: Return the signalNumber.
+Remark: Read signal number
+*****************************************************************************/
+inline
+int
+NdbApiSignal::readSignalNumber()
+{
+ return (int)theVerId_signalNumber;
+}
+/**********************************************************************
+Uint32 readData(Uint32 aDataNo);
+
+Return Value: Return Data word in a signal.
+ Return -1: In all other case.
+ aDataNo: Data number in signal.
+Remark: Return the dataWord information in a signal for a dataNo.
+******************************************************************************/
+inline
+Uint32
+NdbApiSignal::readData(Uint32 aDataNo) const {
+ return getDataPtr()[aDataNo-1];
+}
+/**********************************************************************
+int setData(Uint32 aWord, int aDataNo);
+
+Return Value: Return 0 : setData was successful.
+ Return -1: In all other case.
+Parameters: aWord: Data word.
+ aDataNo: Data number in signal.
+Remark: Set Data word in signal 1 - 25
+******************************************************************************/
+inline
+void
+NdbApiSignal::setData(Uint32 aWord, Uint32 aDataNo){
+ getDataPtrSend()[aDataNo -1] = aWord;
+}
+
+/**
+ * Return pointer to data structure
+ */
+inline
+const Uint32 *
+NdbApiSignal::getDataPtr() const {
+ return theRealData;
+}
+
+inline
+Uint32 *
+NdbApiSignal::getDataPtrSend(){
+ return (Uint32*)&theData[0];
+}
+
+inline
+void
+NdbApiSignal::setDataPtr(Uint32 * ptr){
+ theRealData = ptr;
+}
+
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp
new file mode 100644
index 00000000000..0638f6e4c51
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbBlob.cpp
@@ -0,0 +1,1602 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <Ndb.hpp>
+#include <NdbDictionaryImpl.hpp>
+#include <NdbTransaction.hpp>
+#include <NdbOperation.hpp>
+#include <NdbIndexOperation.hpp>
+#include <NdbRecAttr.hpp>
+#include <NdbBlob.hpp>
+#include "NdbBlobImpl.hpp"
+#include <NdbScanOperation.hpp>
+
+#ifdef NDB_BLOB_DEBUG
+#define DBG(x) \
+ do { \
+ static const char* p = getenv("NDB_BLOB_DEBUG"); \
+ if (p == 0 || *p == 0 || *p == '0') break; \
+ static char* prefix = "BLOB"; \
+ const char* cname = theColumn == NULL ? "-" : theColumn->m_name.c_str(); \
+ ndbout << prefix << " " << hex << (void*)this << " " << cname; \
+ ndbout << " " << dec << __LINE__ << " " << x << " " << *this << endl; \
+ } while (0)
+
+static char*
+ndb_blob_debug(const Uint32* data, unsigned size)
+{
+ static char buf[200]; // MT irrelevant
+ buf[0] = 0;
+ for (unsigned i = 0; i < size; i++) {
+ unsigned n = strlen(buf);
+ if (n + 10 < sizeof(buf))
+ sprintf(buf + n, "%*s%08x", i != 0, "", data[i]);
+ }
+ return buf;
+}
+
+#else
+#define DBG(x)
+#endif
+
+/*
+ * Reading index table directly (as a table) is faster but there are
+ * bugs or limitations. Keep the code and make possible to choose.
+ */
+static const bool g_ndb_blob_ok_to_read_index_table = false;
+
+// state (inline)
+
+inline void
+NdbBlob::setState(State newState)
+{
+ DBG("setState " << newState);
+ theState = newState;
+}
+
+// define blob table
+
+int
+NdbBlob::getBlobTableName(char* btname, Ndb* anNdb, const char* tableName, const char* columnName)
+{
+ NdbTableImpl* t = anNdb->theDictionary->m_impl.getTable(tableName);
+ if (t == NULL)
+ return -1;
+ NdbColumnImpl* c = t->getColumn(columnName);
+ if (c == NULL)
+ return -1;
+ getBlobTableName(btname, t, c);
+ return 0;
+}
+
+void
+NdbBlob::getBlobTableName(char* btname, const NdbTableImpl* t, const NdbColumnImpl* c)
+{
+ assert(t != 0 && c != 0 && c->getBlobType());
+ memset(btname, 0, NdbBlobImpl::BlobTableNameSize);
+ sprintf(btname, "NDB$BLOB_%d_%d", (int)t->m_tableId, (int)c->m_attrId);
+}
+
+void
+NdbBlob::getBlobTable(NdbTableImpl& bt, const NdbTableImpl* t, const NdbColumnImpl* c)
+{
+ char btname[NdbBlobImpl::BlobTableNameSize];
+ getBlobTableName(btname, t, c);
+ bt.setName(btname);
+ bt.setLogging(t->getLogging());
+ bt.setFragmentType(t->getFragmentType());
+ { NdbDictionary::Column bc("PK");
+ bc.setType(NdbDictionary::Column::Unsigned);
+ assert(t->m_keyLenInWords != 0);
+ bc.setLength(t->m_keyLenInWords);
+ bc.setPrimaryKey(true);
+ bc.setDistributionKey(true);
+ bt.addColumn(bc);
+ }
+ { NdbDictionary::Column bc("DIST");
+ bc.setType(NdbDictionary::Column::Unsigned);
+ bc.setPrimaryKey(true);
+ bc.setDistributionKey(true);
+ bt.addColumn(bc);
+ }
+ { NdbDictionary::Column bc("PART");
+ bc.setType(NdbDictionary::Column::Unsigned);
+ bc.setPrimaryKey(true);
+ bc.setDistributionKey(false);
+ bt.addColumn(bc);
+ }
+ { NdbDictionary::Column bc("DATA");
+ switch (c->m_type) {
+ case NdbDictionary::Column::Blob:
+ bc.setType(NdbDictionary::Column::Binary);
+ break;
+ case NdbDictionary::Column::Text:
+ bc.setType(NdbDictionary::Column::Char);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ bc.setLength(c->getPartSize());
+ bt.addColumn(bc);
+ }
+}
+
+// initialization
+
+NdbBlob::NdbBlob()
+{
+ init();
+}
+
+void
+NdbBlob::init()
+{
+ theState = Idle;
+ theNdb = NULL;
+ theNdbCon = NULL;
+ theNdbOp = NULL;
+ theTable = NULL;
+ theAccessTable = NULL;
+ theBlobTable = NULL;
+ theColumn = NULL;
+ theFillChar = 0;
+ theInlineSize = 0;
+ thePartSize = 0;
+ theStripeSize = 0;
+ theGetFlag = false;
+ theGetBuf = NULL;
+ theSetFlag = false;
+ theSetBuf = NULL;
+ theGetSetBytes = 0;
+ thePendingBlobOps = 0;
+ theActiveHook = NULL;
+ theActiveHookArg = NULL;
+ theHead = NULL;
+ theInlineData = NULL;
+ theHeadInlineRecAttr = NULL;
+ theHeadInlineReadOp = NULL;
+ theHeadInlineUpdateFlag = false;
+ theNullFlag = -1;
+ theLength = 0;
+ thePos = 0;
+ theNext = NULL;
+}
+
+void
+NdbBlob::release()
+{
+ setState(Idle);
+}
+
+// buffers
+
+NdbBlob::Buf::Buf() :
+ data(NULL),
+ size(0),
+ maxsize(0)
+{
+}
+
+NdbBlob::Buf::~Buf()
+{
+ delete [] data;
+}
+
+void
+NdbBlob::Buf::alloc(unsigned n)
+{
+ size = n;
+ if (maxsize < n) {
+ delete [] data;
+ // align to Uint64
+ if (n % 8 != 0)
+ n += 8 - n % 8;
+ data = new char [n];
+ maxsize = n;
+ }
+#ifdef VM_TRACE
+ memset(data, 'X', maxsize);
+#endif
+}
+
+void
+NdbBlob::Buf::copyfrom(const NdbBlob::Buf& src)
+{
+ assert(size == src.size);
+ memcpy(data, src.data, size);
+}
+
+// classify operations (inline)
+
+inline bool
+NdbBlob::isTableOp()
+{
+ return theTable == theAccessTable;
+}
+
+inline bool
+NdbBlob::isIndexOp()
+{
+ return theTable != theAccessTable;
+}
+
+inline bool
+NdbBlob::isKeyOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::InsertRequest ||
+ theNdbOp->theOperationType == NdbOperation::UpdateRequest ||
+ theNdbOp->theOperationType == NdbOperation::WriteRequest ||
+ theNdbOp->theOperationType == NdbOperation::ReadRequest ||
+ theNdbOp->theOperationType == NdbOperation::ReadExclusive ||
+ theNdbOp->theOperationType == NdbOperation::DeleteRequest;
+}
+
+inline bool
+NdbBlob::isReadOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::ReadRequest ||
+ theNdbOp->theOperationType == NdbOperation::ReadExclusive;
+}
+
+inline bool
+NdbBlob::isInsertOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::InsertRequest;
+}
+
+inline bool
+NdbBlob::isUpdateOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::UpdateRequest;
+}
+
+inline bool
+NdbBlob::isWriteOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::WriteRequest;
+}
+
+inline bool
+NdbBlob::isDeleteOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::DeleteRequest;
+}
+
+inline bool
+NdbBlob::isScanOp()
+{
+ return
+ theNdbOp->theOperationType == NdbOperation::OpenScanRequest ||
+ theNdbOp->theOperationType == NdbOperation::OpenRangeScanRequest;
+}
+
+// computations (inline)
+
+inline Uint32
+NdbBlob::getPartNumber(Uint64 pos)
+{
+ assert(thePartSize != 0 && pos >= theInlineSize);
+ return (pos - theInlineSize) / thePartSize;
+}
+
+inline Uint32
+NdbBlob::getPartCount()
+{
+ if (theLength <= theInlineSize)
+ return 0;
+ return 1 + getPartNumber(theLength - 1);
+}
+
+inline Uint32
+NdbBlob::getDistKey(Uint32 part)
+{
+ assert(theStripeSize != 0);
+ return (part / theStripeSize) % theStripeSize;
+}
+
+// getters and setters
+
+int
+NdbBlob::getTableKeyValue(NdbOperation* anOp)
+{
+ Uint32* data = (Uint32*)theKeyBuf.data;
+ unsigned pos = 0;
+ DBG("getTableKeyValue");
+ for (unsigned i = 0; i < theTable->m_columns.size(); i++) {
+ NdbColumnImpl* c = theTable->m_columns[i];
+ assert(c != NULL);
+ if (c->m_pk) {
+ unsigned len = c->m_attrSize * c->m_arraySize;
+ if (anOp->getValue_impl(c, (char*)&data[pos]) == NULL) {
+ setErrorCode(anOp);
+ return -1;
+ }
+ // odd bytes receive no data and must be zeroed
+ while (len % 4 != 0) {
+ char* p = (char*)&data[pos] + len++;
+ *p = 0;
+ }
+ pos += len / 4;
+ }
+ }
+ assert(pos == theKeyBuf.size / 4);
+ return 0;
+}
+
+int
+NdbBlob::setTableKeyValue(NdbOperation* anOp)
+{
+ const Uint32* data = (const Uint32*)theKeyBuf.data;
+ DBG("setTableKeyValue key=" << ndb_blob_debug(data, theTable->m_keyLenInWords));
+ const unsigned columns = theTable->m_columns.size();
+ unsigned pos = 0;
+ for (unsigned i = 0; i < columns; i++) {
+ NdbColumnImpl* c = theTable->m_columns[i];
+ assert(c != NULL);
+ if (c->m_pk) {
+ unsigned len = c->m_attrSize * c->m_arraySize;
+ if (anOp->equal_impl(c, (const char*)&data[pos], len) == -1) {
+ setErrorCode(anOp);
+ return -1;
+ }
+ pos += (len + 3) / 4;
+ }
+ }
+ assert(pos == theKeyBuf.size / 4);
+ return 0;
+}
+
+int
+NdbBlob::setAccessKeyValue(NdbOperation* anOp)
+{
+ const Uint32* data = (const Uint32*)theAccessKeyBuf.data;
+ DBG("setAccessKeyValue key=" << ndb_blob_debug(data, theAccessTable->m_keyLenInWords));
+ const unsigned columns = theAccessTable->m_columns.size();
+ unsigned pos = 0;
+ for (unsigned i = 0; i < columns; i++) {
+ NdbColumnImpl* c = theAccessTable->m_columns[i];
+ assert(c != NULL);
+ if (c->m_pk) {
+ unsigned len = c->m_attrSize * c->m_arraySize;
+ if (anOp->equal_impl(c, (const char*)&data[pos], len) == -1) {
+ setErrorCode(anOp);
+ return -1;
+ }
+ pos += (len + 3) / 4;
+ }
+ }
+ assert(pos == theAccessKeyBuf.size / 4);
+ return 0;
+}
+
+int
+NdbBlob::setPartKeyValue(NdbOperation* anOp, Uint32 part)
+{
+ Uint32* data = (Uint32*)theKeyBuf.data;
+ unsigned size = theTable->m_keyLenInWords;
+ DBG("setPartKeyValue dist=" << getDistKey(part) << " part=" << part << " key=" << ndb_blob_debug(data, size));
+ // TODO use attr ids after compatibility with 4.1.7 not needed
+ if (anOp->equal("PK", theKeyBuf.data) == -1 ||
+ anOp->equal("DIST", getDistKey(part)) == -1 ||
+ anOp->equal("PART", part) == -1) {
+ setErrorCode(anOp);
+ return -1;
+ }
+ return 0;
+}
+
+int
+NdbBlob::getHeadInlineValue(NdbOperation* anOp)
+{
+ DBG("getHeadInlineValue");
+ theHeadInlineRecAttr = anOp->getValue_impl(theColumn, theHeadInlineBuf.data);
+ if (theHeadInlineRecAttr == NULL) {
+ setErrorCode(anOp);
+ return -1;
+ }
+ return 0;
+}
+
+void
+NdbBlob::getHeadFromRecAttr()
+{
+ assert(theHeadInlineRecAttr != NULL);
+ theNullFlag = theHeadInlineRecAttr->isNULL();
+ assert(theNullFlag != -1);
+ theLength = ! theNullFlag ? theHead->length : 0;
+ DBG("getHeadFromRecAttr [out]");
+}
+
+int
+NdbBlob::setHeadInlineValue(NdbOperation* anOp)
+{
+ DBG("setHeadInlineValue");
+ theHead->length = theLength;
+ if (theLength < theInlineSize)
+ memset(theInlineData + theLength, 0, theInlineSize - theLength);
+ assert(theNullFlag != -1);
+ const char* aValue = theNullFlag ? 0 : theHeadInlineBuf.data;
+ if (anOp->setValue(theColumn, aValue, theHeadInlineBuf.size) == -1) {
+ setErrorCode(anOp);
+ return -1;
+ }
+ theHeadInlineUpdateFlag = false;
+ return 0;
+}
+
+// getValue/setValue
+
+int
+NdbBlob::getValue(void* data, Uint32 bytes)
+{
+ DBG("getValue data=" << hex << data << " bytes=" << dec << bytes);
+ if (theGetFlag || theState != Prepared) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ if (! isReadOp() && ! isScanOp()) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ if (data == NULL && bytes != 0) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ theGetFlag = true;
+ theGetBuf = static_cast<char*>(data);
+ theGetSetBytes = bytes;
+ return 0;
+}
+
+int
+NdbBlob::setValue(const void* data, Uint32 bytes)
+{
+ DBG("setValue data=" << hex << data << " bytes=" << dec << bytes);
+ if (theSetFlag || theState != Prepared) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ if (! isInsertOp() && ! isUpdateOp() && ! isWriteOp()) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ if (data == NULL && bytes != 0) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ theSetFlag = true;
+ theSetBuf = static_cast<const char*>(data);
+ theGetSetBytes = bytes;
+ if (isInsertOp()) {
+ // write inline part now
+ if (theSetBuf != NULL) {
+ Uint32 n = theGetSetBytes;
+ if (n > theInlineSize)
+ n = theInlineSize;
+ assert(thePos == 0);
+ if (writeDataPrivate(theSetBuf, n) == -1)
+ return -1;
+ } else {
+ theNullFlag = true;
+ theLength = 0;
+ }
+ if (setHeadInlineValue(theNdbOp) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+// activation hook
+
+int
+NdbBlob::setActiveHook(ActiveHook activeHook, void* arg)
+{
+ DBG("setActiveHook hook=" << hex << (void*)activeHook << " arg=" << hex << arg);
+ if (theState != Prepared) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ theActiveHook = activeHook;
+ theActiveHookArg = arg;
+ return 0;
+}
+
+// misc operations
+
+int
+NdbBlob::getNull(bool& isNull)
+{
+ DBG("getNull");
+ if (theState == Prepared && theSetFlag) {
+ isNull = (theSetBuf == NULL);
+ return 0;
+ }
+ if (theNullFlag == -1) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ isNull = theNullFlag;
+ return 0;
+}
+
+int
+NdbBlob::setNull()
+{
+ DBG("setNull");
+ if (theNullFlag == -1) {
+ if (theState == Prepared) {
+ return setValue(0, 0);
+ }
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ if (theNullFlag)
+ return 0;
+ if (deleteParts(0, getPartCount()) == -1)
+ return -1;
+ theNullFlag = true;
+ theLength = 0;
+ theHeadInlineUpdateFlag = true;
+ return 0;
+}
+
+int
+NdbBlob::getLength(Uint64& len)
+{
+ DBG("getLength");
+ if (theState == Prepared && theSetFlag) {
+ len = theGetSetBytes;
+ return 0;
+ }
+ if (theNullFlag == -1) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ len = theLength;
+ return 0;
+}
+
+int
+NdbBlob::truncate(Uint64 length)
+{
+ DBG("truncate [in] length=" << length);
+ if (theNullFlag == -1) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ if (theLength > length) {
+ if (length > theInlineSize) {
+ Uint32 part1 = getPartNumber(length - 1);
+ Uint32 part2 = getPartNumber(theLength - 1);
+ assert(part2 >= part1);
+ if (part2 > part1 && deleteParts(part1 + 1, part2 - part1) == -1)
+ return -1;
+ } else {
+ if (deleteParts(0, getPartCount()) == -1)
+ return -1;
+ }
+ theLength = length;
+ theHeadInlineUpdateFlag = true;
+ if (thePos > length)
+ thePos = length;
+ }
+ DBG("truncate [out]");
+ return 0;
+}
+
+int
+NdbBlob::getPos(Uint64& pos)
+{
+ DBG("getPos");
+ if (theNullFlag == -1) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ pos = thePos;
+ return 0;
+}
+
+int
+NdbBlob::setPos(Uint64 pos)
+{
+ DBG("setPos pos=" << pos);
+ if (theNullFlag == -1) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ if (pos > theLength) {
+ setErrorCode(NdbBlobImpl::ErrSeek);
+ return -1;
+ }
+ thePos = pos;
+ return 0;
+}
+
+// read/write
+
+int
+NdbBlob::readData(void* data, Uint32& bytes)
+{
+ if (theState != Active) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ char* buf = static_cast<char*>(data);
+ return readDataPrivate(buf, bytes);
+}
+
+int
+NdbBlob::readDataPrivate(char* buf, Uint32& bytes)
+{
+ DBG("readData [in] bytes=" << bytes);
+ assert(thePos <= theLength);
+ Uint64 pos = thePos;
+ if (bytes > theLength - pos)
+ bytes = theLength - pos;
+ Uint32 len = bytes;
+ if (len > 0) {
+ // inline part
+ if (pos < theInlineSize) {
+ Uint32 n = theInlineSize - pos;
+ if (n > len)
+ n = len;
+ memcpy(buf, theInlineData + pos, n);
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ }
+ if (len > 0 && thePartSize == 0) {
+ setErrorCode(NdbBlobImpl::ErrSeek);
+ return -1;
+ }
+ if (len > 0) {
+ assert(pos >= theInlineSize);
+ Uint32 off = (pos - theInlineSize) % thePartSize;
+ // partial first block
+ if (off != 0) {
+ DBG("partial first block pos=" << pos << " len=" << len);
+ Uint32 part = (pos - theInlineSize) / thePartSize;
+ if (readParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ // need result now
+ if (executePendingBlobReads() == -1)
+ return -1;
+ Uint32 n = thePartSize - off;
+ if (n > len)
+ n = len;
+ memcpy(buf, thePartBuf.data + off, n);
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ }
+ if (len > 0) {
+ assert((pos - theInlineSize) % thePartSize == 0);
+ // complete blocks in the middle
+ if (len >= thePartSize) {
+ Uint32 part = (pos - theInlineSize) / thePartSize;
+ Uint32 count = len / thePartSize;
+ if (readParts(buf, part, count) == -1)
+ return -1;
+ Uint32 n = thePartSize * count;
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ }
+ if (len > 0) {
+ // partial last block
+ DBG("partial last block pos=" << pos << " len=" << len);
+ assert((pos - theInlineSize) % thePartSize == 0 && len < thePartSize);
+ Uint32 part = (pos - theInlineSize) / thePartSize;
+ if (readParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ // need result now
+ if (executePendingBlobReads() == -1)
+ return -1;
+ memcpy(buf, thePartBuf.data, len);
+ Uint32 n = len;
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ assert(len == 0);
+ thePos = pos;
+ assert(thePos <= theLength);
+ DBG("readData [out]");
+ return 0;
+}
+
+int
+NdbBlob::writeData(const void* data, Uint32 bytes)
+{
+ if (theState != Active) {
+ setErrorCode(NdbBlobImpl::ErrState);
+ return -1;
+ }
+ const char* buf = static_cast<const char*>(data);
+ return writeDataPrivate(buf, bytes);
+}
+
+int
+NdbBlob::writeDataPrivate(const char* buf, Uint32 bytes)
+{
+ DBG("writeData [in] bytes=" << bytes);
+ assert(thePos <= theLength);
+ Uint64 pos = thePos;
+ Uint32 len = bytes;
+ // any write makes blob not NULL
+ if (theNullFlag) {
+ theNullFlag = false;
+ theHeadInlineUpdateFlag = true;
+ }
+ if (len > 0) {
+ // inline part
+ if (pos < theInlineSize) {
+ Uint32 n = theInlineSize - pos;
+ if (n > len)
+ n = len;
+ memcpy(theInlineData + pos, buf, n);
+ theHeadInlineUpdateFlag = true;
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ }
+ if (len > 0 && thePartSize == 0) {
+ setErrorCode(NdbBlobImpl::ErrSeek);
+ return -1;
+ }
+ if (len > 0) {
+ assert(pos >= theInlineSize);
+ Uint32 off = (pos - theInlineSize) % thePartSize;
+ // partial first block
+ if (off != 0) {
+ DBG("partial first block pos=" << pos << " len=" << len);
+ // flush writes to guarantee correct read
+ if (executePendingBlobWrites() == -1)
+ return -1;
+ Uint32 part = (pos - theInlineSize) / thePartSize;
+ if (readParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ // need result now
+ if (executePendingBlobReads() == -1)
+ return -1;
+ Uint32 n = thePartSize - off;
+ if (n > len) {
+ memset(thePartBuf.data + off + len, theFillChar, n - len);
+ n = len;
+ }
+ memcpy(thePartBuf.data + off, buf, n);
+ if (updateParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ }
+ if (len > 0) {
+ assert((pos - theInlineSize) % thePartSize == 0);
+ // complete blocks in the middle
+ if (len >= thePartSize) {
+ Uint32 part = (pos - theInlineSize) / thePartSize;
+ Uint32 count = len / thePartSize;
+ for (unsigned i = 0; i < count; i++) {
+ if (part + i < getPartCount()) {
+ if (updateParts(buf, part + i, 1) == -1)
+ return -1;
+ } else {
+ if (insertParts(buf, part + i, 1) == -1)
+ return -1;
+ }
+ Uint32 n = thePartSize;
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ }
+ }
+ if (len > 0) {
+ // partial last block
+ DBG("partial last block pos=" << pos << " len=" << len);
+ assert((pos - theInlineSize) % thePartSize == 0 && len < thePartSize);
+ Uint32 part = (pos - theInlineSize) / thePartSize;
+ if (theLength > pos + len) {
+ // flush writes to guarantee correct read
+ if (executePendingBlobWrites() == -1)
+ return -1;
+ if (readParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ // need result now
+ if (executePendingBlobReads() == -1)
+ return -1;
+ memcpy(thePartBuf.data, buf, len);
+ if (updateParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ } else {
+ memcpy(thePartBuf.data, buf, len);
+ memset(thePartBuf.data + len, theFillChar, thePartSize - len);
+ if (part < getPartCount()) {
+ if (updateParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ } else {
+ if (insertParts(thePartBuf.data, part, 1) == -1)
+ return -1;
+ }
+ }
+ Uint32 n = len;
+ pos += n;
+ buf += n;
+ len -= n;
+ }
+ assert(len == 0);
+ if (theLength < pos) {
+ theLength = pos;
+ theHeadInlineUpdateFlag = true;
+ }
+ thePos = pos;
+ assert(thePos <= theLength);
+ DBG("writeData [out]");
+ return 0;
+}
+
+int
+NdbBlob::readParts(char* buf, Uint32 part, Uint32 count)
+{
+ DBG("readParts [in] part=" << part << " count=" << count);
+ Uint32 n = 0;
+ while (n < count) {
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theBlobTable);
+ if (tOp == NULL ||
+ tOp->committedRead() == -1 ||
+ setPartKeyValue(tOp, part + n) == -1 ||
+ tOp->getValue((Uint32)3, buf) == NULL) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ tOp->m_abortOption = NdbTransaction::AbortOnError;
+ buf += thePartSize;
+ n++;
+ thePendingBlobOps |= (1 << NdbOperation::ReadRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::ReadRequest);
+ }
+ return 0;
+}
+
+int
+NdbBlob::insertParts(const char* buf, Uint32 part, Uint32 count)
+{
+ DBG("insertParts [in] part=" << part << " count=" << count);
+ Uint32 n = 0;
+ while (n < count) {
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theBlobTable);
+ if (tOp == NULL ||
+ tOp->insertTuple() == -1 ||
+ setPartKeyValue(tOp, part + n) == -1 ||
+ tOp->setValue((Uint32)3, buf) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ tOp->m_abortOption = NdbTransaction::AbortOnError;
+ buf += thePartSize;
+ n++;
+ thePendingBlobOps |= (1 << NdbOperation::InsertRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::InsertRequest);
+ }
+ return 0;
+}
+
+int
+NdbBlob::updateParts(const char* buf, Uint32 part, Uint32 count)
+{
+ DBG("updateParts [in] part=" << part << " count=" << count);
+ Uint32 n = 0;
+ while (n < count) {
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theBlobTable);
+ if (tOp == NULL ||
+ tOp->updateTuple() == -1 ||
+ setPartKeyValue(tOp, part + n) == -1 ||
+ tOp->setValue((Uint32)3, buf) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ tOp->m_abortOption = NdbTransaction::AbortOnError;
+ buf += thePartSize;
+ n++;
+ thePendingBlobOps |= (1 << NdbOperation::UpdateRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::UpdateRequest);
+ }
+ return 0;
+}
+
+int
+NdbBlob::deleteParts(Uint32 part, Uint32 count)
+{
+ DBG("deleteParts [in] part=" << part << " count=" << count);
+ Uint32 n = 0;
+ while (n < count) {
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theBlobTable);
+ if (tOp == NULL ||
+ tOp->deleteTuple() == -1 ||
+ setPartKeyValue(tOp, part + n) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ tOp->m_abortOption = NdbTransaction::AbortOnError;
+ n++;
+ thePendingBlobOps |= (1 << NdbOperation::DeleteRequest);
+ theNdbCon->thePendingBlobOps |= (1 << NdbOperation::DeleteRequest);
+ }
+ return 0;
+}
+
+/*
+ * Number of blob parts not known. Used to check for race condition
+ * when writeTuple is used for insert. Deletes all parts found.
+ */
+int
+NdbBlob::deletePartsUnknown(Uint32 part)
+{
+ DBG("deletePartsUnknown [in] part=" << part << " count=all");
+ static const unsigned maxbat = 256;
+ static const unsigned minbat = 1;
+ unsigned bat = minbat;
+ NdbOperation* tOpList[maxbat];
+ Uint32 count = 0;
+ while (true) {
+ Uint32 n;
+ n = 0;
+ while (n < bat) {
+ NdbOperation*& tOp = tOpList[n]; // ref
+ tOp = theNdbCon->getNdbOperation(theBlobTable);
+ if (tOp == NULL ||
+ tOp->deleteTuple() == -1 ||
+ setPartKeyValue(tOp, part + count + n) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ tOp->m_abortOption= NdbTransaction::AO_IgnoreError;
+ n++;
+ }
+ DBG("deletePartsUnknown: executeNoBlobs [in] bat=" << bat);
+ if (theNdbCon->executeNoBlobs(NdbTransaction::NoCommit) == -1)
+ return -1;
+ DBG("deletePartsUnknown: executeNoBlobs [out]");
+ n = 0;
+ while (n < bat) {
+ NdbOperation* tOp = tOpList[n];
+ if (tOp->theError.code != 0) {
+ if (tOp->theError.code != 626) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ // first non-existent part
+ DBG("deletePartsUnknown [out] count=" << count);
+ return 0;
+ }
+ n++;
+ count++;
+ }
+ bat *= 4;
+ if (bat > maxbat)
+ bat = maxbat;
+ }
+}
+
+// pending ops
+
+int
+NdbBlob::executePendingBlobReads()
+{
+ Uint8 flags = (1 << NdbOperation::ReadRequest);
+ if (thePendingBlobOps & flags) {
+ DBG("executePendingBlobReads: executeNoBlobs [in]");
+ if (theNdbCon->executeNoBlobs(NdbTransaction::NoCommit) == -1)
+ return -1;
+ DBG("executePendingBlobReads: executeNoBlobs [out]");
+ thePendingBlobOps = 0;
+ theNdbCon->thePendingBlobOps = 0;
+ }
+ return 0;
+}
+
+int
+NdbBlob::executePendingBlobWrites()
+{
+ Uint8 flags = 0xFF & ~(1 << NdbOperation::ReadRequest);
+ if (thePendingBlobOps & flags) {
+ DBG("executePendingBlobWrites: executeNoBlobs [in]");
+ if (theNdbCon->executeNoBlobs(NdbTransaction::NoCommit) == -1)
+ return -1;
+ DBG("executePendingBlobWrites: executeNoBlobs [out]");
+ thePendingBlobOps = 0;
+ theNdbCon->thePendingBlobOps = 0;
+ }
+ return 0;
+}
+
+// callbacks
+
+int
+NdbBlob::invokeActiveHook()
+{
+ DBG("invokeActiveHook [in]");
+ assert(theState == Active && theActiveHook != NULL);
+ int ret = (*theActiveHook)(this, theActiveHookArg);
+ DBG("invokeActiveHook [out] ret=" << ret);
+ if (ret != 0) {
+ // no error is set on blob level
+ return -1;
+ }
+ return 0;
+}
+
+// blob handle maintenance
+
+/*
+ * Prepare blob handle linked to an operation. Checks blob table.
+ * Allocates buffers. For key operation fetches key data from signal
+ * data. For read operation adds read of head+inline.
+ */
+int
+NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl* aColumn)
+{
+ assert(theState == Idle);
+ // ndb api stuff
+ theNdb = anOp->theNdb;
+ theNdbCon = aCon; // for scan, this is the real transaction (m_transConnection)
+ theNdbOp = anOp;
+ theTable = anOp->m_currentTable;
+ theAccessTable = anOp->m_accessTable;
+ theColumn = aColumn;
+ DBG("atPrepare [in]");
+ NdbDictionary::Column::Type partType = NdbDictionary::Column::Undefined;
+ switch (theColumn->getType()) {
+ case NdbDictionary::Column::Blob:
+ partType = NdbDictionary::Column::Binary;
+ theFillChar = 0x0;
+ break;
+ case NdbDictionary::Column::Text:
+ partType = NdbDictionary::Column::Char;
+ theFillChar = 0x20;
+ break;
+ default:
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ // sizes
+ theInlineSize = theColumn->getInlineSize();
+ thePartSize = theColumn->getPartSize();
+ theStripeSize = theColumn->getStripeSize();
+ // sanity check
+ assert((NDB_BLOB_HEAD_SIZE << 2) == sizeof(Head));
+ assert(theColumn->m_attrSize * theColumn->m_arraySize == sizeof(Head) + theInlineSize);
+ if (thePartSize > 0) {
+ const NdbDictionary::Table* bt = NULL;
+ const NdbDictionary::Column* bc = NULL;
+ if (theStripeSize == 0 ||
+ (bt = theColumn->getBlobTable()) == NULL ||
+ (bc = bt->getColumn("DATA")) == NULL ||
+ bc->getType() != partType ||
+ bc->getLength() != (int)thePartSize) {
+ setErrorCode(NdbBlobImpl::ErrTable);
+ return -1;
+ }
+ theBlobTable = &NdbTableImpl::getImpl(*bt);
+ }
+ // buffers
+ theKeyBuf.alloc(theTable->m_keyLenInWords << 2);
+ theAccessKeyBuf.alloc(theAccessTable->m_keyLenInWords << 2);
+ theHeadInlineBuf.alloc(sizeof(Head) + theInlineSize);
+ theHeadInlineCopyBuf.alloc(sizeof(Head) + theInlineSize);
+ thePartBuf.alloc(thePartSize);
+ theHead = (Head*)theHeadInlineBuf.data;
+ theInlineData = theHeadInlineBuf.data + sizeof(Head);
+ // handle different operation types
+ bool supportedOp = false;
+ if (isKeyOp()) {
+ if (isTableOp()) {
+ // get table key
+ Uint32* data = (Uint32*)theKeyBuf.data;
+ unsigned size = theTable->m_keyLenInWords;
+ if (theNdbOp->getKeyFromTCREQ(data, size) == -1) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ }
+ if (isIndexOp()) {
+ // get index key
+ Uint32* data = (Uint32*)theAccessKeyBuf.data;
+ unsigned size = theAccessTable->m_keyLenInWords;
+ if (theNdbOp->getKeyFromTCREQ(data, size) == -1) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ }
+ if (isReadOp()) {
+ // add read of head+inline in this op
+ if (getHeadInlineValue(theNdbOp) == -1)
+ return -1;
+ }
+ if (isInsertOp()) {
+ // becomes NULL unless set before execute
+ theNullFlag = true;
+ theLength = 0;
+ }
+ if (isWriteOp()) {
+ // becomes NULL unless set before execute
+ theNullFlag = true;
+ theLength = 0;
+ theHeadInlineUpdateFlag = true;
+ }
+ supportedOp = true;
+ }
+ if (isScanOp()) {
+ // add read of head+inline in this op
+ if (getHeadInlineValue(theNdbOp) == -1)
+ return -1;
+ supportedOp = true;
+ }
+ if (! supportedOp) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ setState(Prepared);
+ DBG("atPrepare [out]");
+ return 0;
+}
+
+/*
+ * Before execute of prepared operation. May add new operations before
+ * this one. May ask that this operation and all before it (a "batch")
+ * is executed immediately in no-commit mode. In this case remaining
+ * prepared operations are saved in a separate list. They are added
+ * back after postExecute.
+ */
+int
+NdbBlob::preExecute(NdbTransaction::ExecType anExecType, bool& batch)
+{
+ DBG("preExecute [in]");
+ if (theState == Invalid)
+ return -1;
+ assert(theState == Prepared);
+ // handle different operation types
+ assert(isKeyOp());
+ if (isReadOp()) {
+ if (theGetFlag && theGetSetBytes > theInlineSize) {
+ // need blob head before proceeding
+ batch = true;
+ }
+ }
+ if (isInsertOp()) {
+ if (theSetFlag && theGetSetBytes > theInlineSize) {
+ // add ops to write rest of a setValue
+ assert(theSetBuf != NULL);
+ const char* buf = theSetBuf + theInlineSize;
+ Uint32 bytes = theGetSetBytes - theInlineSize;
+ assert(thePos == theInlineSize);
+ if (writeDataPrivate(buf, bytes) == -1)
+ return -1;
+ if (theHeadInlineUpdateFlag) {
+ // add an operation to update head+inline
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
+ if (tOp == NULL ||
+ tOp->updateTuple() == -1 ||
+ setTableKeyValue(tOp) == -1 ||
+ setHeadInlineValue(tOp) == -1) {
+ setErrorCode(NdbBlobImpl::ErrAbort);
+ return -1;
+ }
+ DBG("add op to update head+inline");
+ }
+ }
+ }
+ if (isTableOp()) {
+ if (isUpdateOp() || isWriteOp() || isDeleteOp()) {
+ // add operation before this one to read head+inline
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theTable, theNdbOp);
+ if (tOp == NULL ||
+ tOp->readTuple() == -1 ||
+ setTableKeyValue(tOp) == -1 ||
+ getHeadInlineValue(tOp) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ if (isWriteOp()) {
+ tOp->m_abortOption = NdbTransaction::AO_IgnoreError;
+ }
+ theHeadInlineReadOp = tOp;
+ // execute immediately
+ batch = true;
+ DBG("add op before to read head+inline");
+ }
+ }
+ if (isIndexOp()) {
+ // add op before this one to read table key
+ NdbBlob* tFirstBlob = theNdbOp->theBlobList;
+ if (this == tFirstBlob) {
+ // first blob does it for all
+ if (g_ndb_blob_ok_to_read_index_table) {
+ Uint32 pkAttrId = theAccessTable->getNoOfColumns() - 1;
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theAccessTable, theNdbOp);
+ if (tOp == NULL ||
+ tOp->readTuple() == -1 ||
+ setAccessKeyValue(tOp) == -1 ||
+ tOp->getValue(pkAttrId, theKeyBuf.data) == NULL) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ } else {
+ NdbIndexOperation* tOp = theNdbCon->getNdbIndexOperation(theAccessTable->m_index, theTable, theNdbOp);
+ if (tOp == NULL ||
+ tOp->readTuple() == -1 ||
+ setAccessKeyValue(tOp) == -1 ||
+ getTableKeyValue(tOp) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ }
+ }
+ DBG("added op before to read table key");
+ if (isUpdateOp() || isDeleteOp()) {
+ // add op before this one to read head+inline via index
+ NdbIndexOperation* tOp = theNdbCon->getNdbIndexOperation(theAccessTable->m_index, theTable, theNdbOp);
+ if (tOp == NULL ||
+ tOp->readTuple() == -1 ||
+ setAccessKeyValue(tOp) == -1 ||
+ getHeadInlineValue(tOp) == -1) {
+ setErrorCode(tOp);
+ return -1;
+ }
+ if (isWriteOp()) {
+ tOp->m_abortOption = NdbTransaction::AO_IgnoreError;
+ }
+ theHeadInlineReadOp = tOp;
+ // execute immediately
+ batch = true;
+ DBG("added index op before to read head+inline");
+ }
+ if (isWriteOp()) {
+ // XXX until IgnoreError fixed for index op
+ batch = true;
+ }
+ }
+ if (isWriteOp()) {
+ if (theSetFlag) {
+ // write head+inline now
+ theNullFlag = true;
+ theLength = 0;
+ if (theSetBuf != NULL) {
+ Uint32 n = theGetSetBytes;
+ if (n > theInlineSize)
+ n = theInlineSize;
+ assert(thePos == 0);
+ if (writeDataPrivate(theSetBuf, n) == -1)
+ return -1;
+ }
+ if (setHeadInlineValue(theNdbOp) == -1)
+ return -1;
+ // the read op before us may overwrite
+ theHeadInlineCopyBuf.copyfrom(theHeadInlineBuf);
+ }
+ }
+ if (theActiveHook != NULL) {
+ // need blob head for callback
+ batch = true;
+ }
+ DBG("preExecute [out] batch=" << batch);
+ return 0;
+}
+
+/*
+ * After execute, for any operation. If already Active, this routine
+ * has been done previously. Operations which requested a no-commit
+ * batch can add new operations after this one. They are added before
+ * any remaining prepared operations.
+ */
+int
+NdbBlob::postExecute(NdbTransaction::ExecType anExecType)
+{
+ DBG("postExecute [in] type=" << anExecType);
+ if (theState == Invalid)
+ return -1;
+ if (theState == Active) {
+ setState(anExecType == NdbTransaction::NoCommit ? Active : Closed);
+ DBG("postExecute [skip]");
+ return 0;
+ }
+ assert(theState == Prepared);
+ setState(anExecType == NdbTransaction::NoCommit ? Active : Closed);
+ assert(isKeyOp());
+ if (isIndexOp()) {
+ NdbBlob* tFirstBlob = theNdbOp->theBlobList;
+ if (this != tFirstBlob) {
+ // copy key from first blob
+ assert(theKeyBuf.size == tFirstBlob->theKeyBuf.size);
+ memcpy(theKeyBuf.data, tFirstBlob->theKeyBuf.data, tFirstBlob->theKeyBuf.size);
+ }
+ }
+ if (isReadOp()) {
+ getHeadFromRecAttr();
+ if (setPos(0) == -1)
+ return -1;
+ if (theGetFlag) {
+ assert(theGetSetBytes == 0 || theGetBuf != 0);
+ assert(theGetSetBytes <= theInlineSize ||
+ anExecType == NdbTransaction::NoCommit);
+ Uint32 bytes = theGetSetBytes;
+ if (readDataPrivate(theGetBuf, bytes) == -1)
+ return -1;
+ }
+ }
+ if (isUpdateOp()) {
+ assert(anExecType == NdbTransaction::NoCommit);
+ getHeadFromRecAttr();
+ if (theSetFlag) {
+ // setValue overwrites everything
+ if (theSetBuf != NULL) {
+ if (truncate(0) == -1)
+ return -1;
+ assert(thePos == 0);
+ if (writeDataPrivate(theSetBuf, theGetSetBytes) == -1)
+ return -1;
+ } else {
+ if (setNull() == -1)
+ return -1;
+ }
+ }
+ }
+ if (isWriteOp() && isTableOp()) {
+ assert(anExecType == NdbTransaction::NoCommit);
+ if (theHeadInlineReadOp->theError.code == 0) {
+ int tNullFlag = theNullFlag;
+ Uint64 tLength = theLength;
+ Uint64 tPos = thePos;
+ getHeadFromRecAttr();
+ DBG("tuple found");
+ if (truncate(0) == -1)
+ return -1;
+ // restore previous head+inline
+ theHeadInlineBuf.copyfrom(theHeadInlineCopyBuf);
+ theNullFlag = tNullFlag;
+ theLength = tLength;
+ thePos = tPos;
+ } else {
+ if (theHeadInlineReadOp->theError.code != 626) {
+ setErrorCode(theHeadInlineReadOp);
+ return -1;
+ }
+ DBG("tuple not found");
+ /*
+ * Read found no tuple but it is possible that a tuple was
+ * created after the read by another transaction. Delete all
+ * blob parts which may exist.
+ */
+ if (deletePartsUnknown(0) == -1)
+ return -1;
+ }
+ if (theSetFlag && theGetSetBytes > theInlineSize) {
+ assert(theSetBuf != NULL);
+ const char* buf = theSetBuf + theInlineSize;
+ Uint32 bytes = theGetSetBytes - theInlineSize;
+ assert(thePos == theInlineSize);
+ if (writeDataPrivate(buf, bytes) == -1)
+ return -1;
+ }
+ }
+ if (isWriteOp() && isIndexOp()) {
+ // XXX until IgnoreError fixed for index op
+ if (deletePartsUnknown(0) == -1)
+ return -1;
+ if (theSetFlag && theGetSetBytes > theInlineSize) {
+ assert(theSetBuf != NULL);
+ const char* buf = theSetBuf + theInlineSize;
+ Uint32 bytes = theGetSetBytes - theInlineSize;
+ assert(thePos == theInlineSize);
+ if (writeDataPrivate(buf, bytes) == -1)
+ return -1;
+ }
+ }
+ if (isDeleteOp()) {
+ assert(anExecType == NdbTransaction::NoCommit);
+ getHeadFromRecAttr();
+ if (deleteParts(0, getPartCount()) == -1)
+ return -1;
+ }
+ setState(anExecType == NdbTransaction::NoCommit ? Active : Closed);
+ // activation callback
+ if (theActiveHook != NULL) {
+ if (invokeActiveHook() == -1)
+ return -1;
+ }
+ if (anExecType == NdbTransaction::NoCommit && theHeadInlineUpdateFlag) {
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
+ if (tOp == NULL ||
+ tOp->updateTuple() == -1 ||
+ setTableKeyValue(tOp) == -1 ||
+ setHeadInlineValue(tOp) == -1) {
+ setErrorCode(NdbBlobImpl::ErrAbort);
+ return -1;
+ }
+ tOp->m_abortOption = NdbTransaction::AbortOnError;
+ DBG("added op to update head+inline");
+ }
+ DBG("postExecute [out]");
+ return 0;
+}
+
+/*
+ * Before commit of completed operation. For write add operation to
+ * update head+inline.
+ */
+int
+NdbBlob::preCommit()
+{
+ DBG("preCommit [in]");
+ if (theState == Invalid)
+ return -1;
+ assert(theState == Active);
+ assert(isKeyOp());
+ if (isInsertOp() || isUpdateOp() || isWriteOp()) {
+ if (theHeadInlineUpdateFlag) {
+ // add an operation to update head+inline
+ NdbOperation* tOp = theNdbCon->getNdbOperation(theTable);
+ if (tOp == NULL ||
+ tOp->updateTuple() == -1 ||
+ setTableKeyValue(tOp) == -1 ||
+ setHeadInlineValue(tOp) == -1) {
+ setErrorCode(NdbBlobImpl::ErrAbort);
+ return -1;
+ }
+ tOp->m_abortOption = NdbTransaction::AbortOnError;
+ DBG("added op to update head+inline");
+ }
+ }
+ DBG("preCommit [out]");
+ return 0;
+}
+
+/*
+ * After next scan result. Handle like read op above.
+ */
+int
+NdbBlob::atNextResult()
+{
+ DBG("atNextResult [in]");
+ if (theState == Invalid)
+ return -1;
+ assert(isScanOp());
+ // get primary key
+ { Uint32* data = (Uint32*)theKeyBuf.data;
+ unsigned size = theTable->m_keyLenInWords;
+ if (((NdbScanOperation*)theNdbOp)->getKeyFromKEYINFO20(data, size) == -1) {
+ setErrorCode(NdbBlobImpl::ErrUsage);
+ return -1;
+ }
+ }
+ getHeadFromRecAttr();
+ if (setPos(0) == -1)
+ return -1;
+ if (theGetFlag) {
+ assert(theGetSetBytes == 0 || theGetBuf != 0);
+ Uint32 bytes = theGetSetBytes;
+ if (readDataPrivate(theGetBuf, bytes) == -1)
+ return -1;
+ }
+ setState(Active);
+ // activation callback
+ if (theActiveHook != NULL) {
+ if (invokeActiveHook() == -1)
+ return -1;
+ }
+ DBG("atNextResult [out]");
+ return 0;
+}
+
+// misc
+
+const NdbDictionary::Column*
+NdbBlob::getColumn()
+{
+ return theColumn;
+}
+
+// errors
+
+void
+NdbBlob::setErrorCode(int anErrorCode, bool invalidFlag)
+{
+ DBG("setErrorCode code=" << anErrorCode);
+ theError.code = anErrorCode;
+ // conditionally copy error to operation level
+ if (theNdbOp != NULL && theNdbOp->theError.code == 0)
+ theNdbOp->setErrorCode(theError.code);
+ if (invalidFlag)
+ setState(Invalid);
+}
+
+void
+NdbBlob::setErrorCode(NdbOperation* anOp, bool invalidFlag)
+{
+ int code = 0;
+ if (anOp != NULL && (code = anOp->theError.code) != 0)
+ ;
+ else if ((code = theNdbCon->theError.code) != 0)
+ ;
+ else if ((code = theNdb->theError.code) != 0)
+ ;
+ else
+ code = NdbBlobImpl::ErrUnknown;
+ setErrorCode(code, invalidFlag);
+}
+
+void
+NdbBlob::setErrorCode(NdbTransaction* aCon, bool invalidFlag)
+{
+ int code = 0;
+ if (theNdbCon != NULL && (code = theNdbCon->theError.code) != 0)
+ ;
+ else if ((code = theNdb->theError.code) != 0)
+ ;
+ else
+ code = NdbBlobImpl::ErrUnknown;
+ setErrorCode(code, invalidFlag);
+}
+
+// info about all blobs in this operation
+
+NdbBlob*
+NdbBlob::blobsFirstBlob()
+{
+ return theNdbOp->theBlobList;
+}
+
+NdbBlob*
+NdbBlob::blobsNextBlob()
+{
+ return theNext;
+}
+
+// debug
+
+#ifdef VM_TRACE
+inline int
+NdbBlob::getOperationType() const
+{
+ return theNdbOp != NULL ? theNdbOp->theOperationType : -1;
+}
+
+NdbOut&
+operator<<(NdbOut& out, const NdbBlob& blob)
+{
+ ndbout << dec << "o=" << blob.getOperationType();
+ ndbout << dec << " s=" << (Uint32) blob.theState;
+ ndbout << dec << " n=" << blob.theNullFlag;;
+ ndbout << dec << " l=" << blob.theLength;
+ ndbout << dec << " p=" << blob.thePos;
+ ndbout << dec << " u=" << (Uint32)blob.theHeadInlineUpdateFlag;
+ ndbout << dec << " g=" << (Uint32)blob.theGetSetBytes;
+ return out;
+}
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbBlobImpl.hpp b/storage/ndb/src/ndbapi/NdbBlobImpl.hpp
new file mode 100644
index 00000000000..0030e910c52
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbBlobImpl.hpp
@@ -0,0 +1,39 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NdbBlobImpl_H
+#define NdbBlobImpl_H
+
+class NdbBlobImpl {
+public:
+ STATIC_CONST( BlobTableNameSize = 40 );
+ // "Invalid blob attributes or invalid blob parts table"
+ STATIC_CONST( ErrTable = 4263 );
+ // "Invalid usage of blob attribute"
+ STATIC_CONST( ErrUsage = 4264 );
+ // "Method is not valid in current blob state"
+ STATIC_CONST( ErrState = 4265 );
+ // "Invalid blob seek position"
+ STATIC_CONST( ErrSeek = 4266 );
+ // "Corrupted blob value"
+ STATIC_CONST( ErrCorrupt = 4267 );
+ // "Error in blob head update forced rollback of transaction"
+ STATIC_CONST( ErrAbort = 4268 );
+ // "Unknown blob error"
+ STATIC_CONST( ErrUnknown = 4269 );
+};
+
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbDictionary.cpp b/storage/ndb/src/ndbapi/NdbDictionary.cpp
new file mode 100644
index 00000000000..4cc47543cec
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbDictionary.cpp
@@ -0,0 +1,1050 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbDictionary.hpp>
+#include "NdbDictionaryImpl.hpp"
+#include <NdbOut.hpp>
+
+/*****************************************************************
+ * Column facade
+ */
+NdbDictionary::Column::Column(const char * name)
+ : m_impl(* new NdbColumnImpl(* this))
+{
+ setName(name);
+}
+
+NdbDictionary::Column::Column(const NdbDictionary::Column & org)
+ : m_impl(* new NdbColumnImpl(* this))
+{
+ m_impl = org.m_impl;
+}
+
+NdbDictionary::Column::Column(NdbColumnImpl& impl)
+ : m_impl(impl)
+{
+}
+
+NdbDictionary::Column::~Column(){
+ NdbColumnImpl * tmp = &m_impl;
+ if(this != tmp){
+ delete tmp;
+ }
+}
+
+NdbDictionary::Column&
+NdbDictionary::Column::operator=(const NdbDictionary::Column& column)
+{
+ m_impl = column.m_impl;
+
+ return *this;
+}
+
+void
+NdbDictionary::Column::setName(const char * name){
+ m_impl.m_name.assign(name);
+}
+
+const char*
+NdbDictionary::Column::getName() const {
+ return m_impl.m_name.c_str();
+}
+
+void
+NdbDictionary::Column::setType(Type t){
+ m_impl.init(t);
+}
+
+NdbDictionary::Column::Type
+NdbDictionary::Column::getType() const {
+ return m_impl.m_type;
+}
+
+void
+NdbDictionary::Column::setPrecision(int val){
+ m_impl.m_precision = val;
+}
+
+int
+NdbDictionary::Column::getPrecision() const {
+ return m_impl.m_precision;
+}
+
+void
+NdbDictionary::Column::setScale(int val){
+ m_impl.m_scale = val;
+}
+
+int
+NdbDictionary::Column::getScale() const{
+ return m_impl.m_scale;
+}
+
+void
+NdbDictionary::Column::setLength(int length){
+ m_impl.m_length = length;
+}
+
+int
+NdbDictionary::Column::getLength() const{
+ return m_impl.m_length;
+}
+
+void
+NdbDictionary::Column::setInlineSize(int size)
+{
+ m_impl.m_precision = size;
+}
+
+void
+NdbDictionary::Column::setCharset(CHARSET_INFO* cs)
+{
+ m_impl.m_cs = cs;
+}
+
+CHARSET_INFO*
+NdbDictionary::Column::getCharset() const
+{
+ return m_impl.m_cs;
+}
+
+int
+NdbDictionary::Column::getInlineSize() const
+{
+ return m_impl.m_precision;
+}
+
+void
+NdbDictionary::Column::setPartSize(int size)
+{
+ m_impl.m_scale = size;
+}
+
+int
+NdbDictionary::Column::getPartSize() const
+{
+ return m_impl.m_scale;
+}
+
+void
+NdbDictionary::Column::setStripeSize(int size)
+{
+ m_impl.m_length = size;
+}
+
+int
+NdbDictionary::Column::getStripeSize() const
+{
+ return m_impl.m_length;
+}
+
+int
+NdbDictionary::Column::getSize() const{
+ return m_impl.m_attrSize;
+}
+
+void
+NdbDictionary::Column::setNullable(bool val){
+ m_impl.m_nullable = val;
+}
+
+bool
+NdbDictionary::Column::getNullable() const {
+ return m_impl.m_nullable;
+}
+
+void
+NdbDictionary::Column::setPrimaryKey(bool val){
+ m_impl.m_pk = val;
+}
+
+bool
+NdbDictionary::Column::getPrimaryKey() const {
+ return m_impl.m_pk;
+}
+
+void
+NdbDictionary::Column::setPartitionKey(bool val){
+ m_impl.m_distributionKey = val;
+}
+
+bool
+NdbDictionary::Column::getPartitionKey() const{
+ return m_impl.m_distributionKey;
+}
+
+const NdbDictionary::Table *
+NdbDictionary::Column::getBlobTable() const {
+ NdbTableImpl * t = m_impl.m_blobTable;
+ if (t)
+ return t->m_facade;
+ return 0;
+}
+
+void
+NdbDictionary::Column::setAutoIncrement(bool val){
+ m_impl.m_autoIncrement = val;
+}
+
+bool
+NdbDictionary::Column::getAutoIncrement() const {
+ return m_impl.m_autoIncrement;
+}
+
+void
+NdbDictionary::Column::setAutoIncrementInitialValue(Uint64 val){
+ m_impl.m_autoIncrementInitialValue = val;
+}
+
+void
+NdbDictionary::Column::setDefaultValue(const char* defaultValue)
+{
+ m_impl.m_defaultValue.assign(defaultValue);
+}
+
+const char*
+NdbDictionary::Column::getDefaultValue() const
+{
+ return m_impl.m_defaultValue.c_str();
+}
+
+int
+NdbDictionary::Column::getColumnNo() const {
+ return m_impl.m_attrId;
+}
+
+bool
+NdbDictionary::Column::equal(const NdbDictionary::Column & col) const {
+ return m_impl.equal(col.m_impl);
+}
+
+int
+NdbDictionary::Column::getSizeInBytes() const
+{
+ return m_impl.m_attrSize * m_impl.m_arraySize;
+}
+
+/*****************************************************************
+ * Table facade
+ */
+NdbDictionary::Table::Table(const char * name)
+ : m_impl(* new NdbTableImpl(* this))
+{
+ setName(name);
+}
+
+NdbDictionary::Table::Table(const NdbDictionary::Table & org)
+ : NdbDictionary::Object(),
+ m_impl(* new NdbTableImpl(* this))
+{
+ m_impl.assign(org.m_impl);
+}
+
+NdbDictionary::Table::Table(NdbTableImpl & impl)
+ : m_impl(impl)
+{
+}
+
+NdbDictionary::Table::~Table(){
+ NdbTableImpl * tmp = &m_impl;
+ if(this != tmp){
+ delete tmp;
+ }
+}
+
+NdbDictionary::Table&
+NdbDictionary::Table::operator=(const NdbDictionary::Table& table)
+{
+ m_impl.assign(table.m_impl);
+
+ m_impl.m_facade = this;
+ return *this;
+}
+
+void
+NdbDictionary::Table::setName(const char * name){
+ m_impl.setName(name);
+}
+
+const char *
+NdbDictionary::Table::getName() const {
+ return m_impl.getName();
+}
+
+int
+NdbDictionary::Table::getTableId() const {
+ return m_impl.m_tableId;
+}
+
+void
+NdbDictionary::Table::addColumn(const Column & c){
+ NdbColumnImpl* col = new NdbColumnImpl;
+ (* col) = NdbColumnImpl::getImpl(c);
+ m_impl.m_columns.push_back(col);
+ if(c.getPrimaryKey()){
+ m_impl.m_noOfKeys++;
+ }
+ if (col->getBlobType()) {
+ m_impl.m_noOfBlobs++;
+ }
+ m_impl.buildColumnHash();
+}
+
+const NdbDictionary::Column*
+NdbDictionary::Table::getColumn(const char * name) const {
+ return m_impl.getColumn(name);
+}
+
+const NdbDictionary::Column*
+NdbDictionary::Table::getColumn(const int attrId) const {
+ return m_impl.getColumn(attrId);
+}
+
+NdbDictionary::Column*
+NdbDictionary::Table::getColumn(const char * name)
+{
+ return m_impl.getColumn(name);
+}
+
+NdbDictionary::Column*
+NdbDictionary::Table::getColumn(const int attrId)
+{
+ return m_impl.getColumn(attrId);
+}
+
+void
+NdbDictionary::Table::setLogging(bool val){
+ m_impl.m_logging = val;
+}
+
+bool
+NdbDictionary::Table::getLogging() const {
+ return m_impl.m_logging;
+}
+
+void
+NdbDictionary::Table::setFragmentType(FragmentType ft){
+ m_impl.m_fragmentType = ft;
+}
+
+NdbDictionary::Object::FragmentType
+NdbDictionary::Table::getFragmentType() const {
+ return m_impl.m_fragmentType;
+}
+
+void
+NdbDictionary::Table::setKValue(int kValue){
+ m_impl.m_kvalue = kValue;
+}
+
+int
+NdbDictionary::Table::getKValue() const {
+ return m_impl.m_kvalue;
+}
+
+void
+NdbDictionary::Table::setMinLoadFactor(int lf){
+ m_impl.m_minLoadFactor = lf;
+}
+
+int
+NdbDictionary::Table::getMinLoadFactor() const {
+ return m_impl.m_minLoadFactor;
+}
+
+void
+NdbDictionary::Table::setMaxLoadFactor(int lf){
+ m_impl.m_maxLoadFactor = lf;
+}
+
+int
+NdbDictionary::Table::getMaxLoadFactor() const {
+ return m_impl.m_maxLoadFactor;
+}
+
+int
+NdbDictionary::Table::getNoOfColumns() const {
+ return m_impl.m_columns.size();
+}
+
+int
+NdbDictionary::Table::getNoOfPrimaryKeys() const {
+ return m_impl.m_noOfKeys;
+}
+
+const char*
+NdbDictionary::Table::getPrimaryKey(int no) const {
+ int count = 0;
+ for (unsigned i = 0; i < m_impl.m_columns.size(); i++) {
+ if (m_impl.m_columns[i]->m_pk) {
+ if (count++ == no)
+ return m_impl.m_columns[i]->m_name.c_str();
+ }
+ }
+ return 0;
+}
+
+const void*
+NdbDictionary::Table::getFrmData() const {
+ return m_impl.m_frm.get_data();
+}
+
+Uint32
+NdbDictionary::Table::getFrmLength() const {
+ return m_impl.m_frm.length();
+}
+
+void
+NdbDictionary::Table::setFrm(const void* data, Uint32 len){
+ m_impl.m_frm.assign(data, len);
+}
+
+NdbDictionary::Object::Status
+NdbDictionary::Table::getObjectStatus() const {
+ return m_impl.m_status;
+}
+
+int
+NdbDictionary::Table::getObjectVersion() const {
+ return m_impl.m_version;
+}
+
+bool
+NdbDictionary::Table::equal(const NdbDictionary::Table & col) const {
+ return m_impl.equal(col.m_impl);
+}
+
+int
+NdbDictionary::Table::getRowSizeInBytes() const {
+ int sz = 0;
+ for(int i = 0; i<getNoOfColumns(); i++){
+ const NdbDictionary::Column * c = getColumn(i);
+ sz += (c->getSizeInBytes()+ 3) / 4;
+ }
+ return sz * 4;
+}
+
+int
+NdbDictionary::Table::getReplicaCount() const {
+ return m_impl.m_replicaCount;
+}
+
+int
+NdbDictionary::Table::createTableInDb(Ndb* pNdb, bool equalOk) const {
+ const NdbDictionary::Table * pTab =
+ pNdb->getDictionary()->getTable(getName());
+ if(pTab != 0 && equal(* pTab))
+ return 0;
+ if(pTab != 0 && !equal(* pTab))
+ return -1;
+ return pNdb->getDictionary()->createTable(* this);
+}
+
+/*****************************************************************
+ * Index facade
+ */
+NdbDictionary::Index::Index(const char * name)
+ : m_impl(* new NdbIndexImpl(* this))
+{
+ setName(name);
+}
+
+NdbDictionary::Index::Index(NdbIndexImpl & impl)
+ : m_impl(impl)
+{
+}
+
+NdbDictionary::Index::~Index(){
+ NdbIndexImpl * tmp = &m_impl;
+ if(this != tmp){
+ delete tmp;
+ }
+}
+
+void
+NdbDictionary::Index::setName(const char * name){
+ m_impl.setName(name);
+}
+
+const char *
+NdbDictionary::Index::getName() const {
+ return m_impl.getName();
+}
+
+void
+NdbDictionary::Index::setTable(const char * table){
+ m_impl.setTable(table);
+}
+
+const char *
+NdbDictionary::Index::getTable() const {
+ return m_impl.getTable();
+}
+
+unsigned
+NdbDictionary::Index::getNoOfColumns() const {
+ return m_impl.m_columns.size();
+}
+
+int
+NdbDictionary::Index::getNoOfIndexColumns() const {
+ return m_impl.m_columns.size();
+}
+
+const NdbDictionary::Column *
+NdbDictionary::Index::getColumn(unsigned no) const {
+ if(no < m_impl.m_columns.size())
+ return m_impl.m_columns[no];
+ return NULL;
+}
+
+const char *
+NdbDictionary::Index::getIndexColumn(int no) const {
+ const NdbDictionary::Column* col = getColumn(no);
+
+ if (col)
+ return col->getName();
+ else
+ return NULL;
+}
+
+void
+NdbDictionary::Index::addColumn(const Column & c){
+ NdbColumnImpl* col = new NdbColumnImpl;
+ (* col) = NdbColumnImpl::getImpl(c);
+ m_impl.m_columns.push_back(col);
+}
+
+void
+NdbDictionary::Index::addColumnName(const char * name){
+ const Column c(name);
+ addColumn(c);
+}
+
+void
+NdbDictionary::Index::addIndexColumn(const char * name){
+ const Column c(name);
+ addColumn(c);
+}
+
+void
+NdbDictionary::Index::addColumnNames(unsigned noOfNames, const char ** names){
+ for(unsigned i = 0; i < noOfNames; i++) {
+ const Column c(names[i]);
+ addColumn(c);
+ }
+}
+
+void
+NdbDictionary::Index::addIndexColumns(int noOfNames, const char ** names){
+ for(int i = 0; i < noOfNames; i++) {
+ const Column c(names[i]);
+ addColumn(c);
+ }
+}
+
+void
+NdbDictionary::Index::setType(NdbDictionary::Index::Type t){
+ m_impl.m_type = t;
+}
+
+NdbDictionary::Index::Type
+NdbDictionary::Index::getType() const {
+ return m_impl.m_type;
+}
+
+void
+NdbDictionary::Index::setLogging(bool val){
+ m_impl.m_logging = val;
+}
+
+bool
+NdbDictionary::Index::getLogging() const {
+ return m_impl.m_logging;
+}
+
+NdbDictionary::Object::Status
+NdbDictionary::Index::getObjectStatus() const {
+ return m_impl.m_status;
+}
+
+int
+NdbDictionary::Index::getObjectVersion() const {
+ return m_impl.m_version;
+}
+
+/*****************************************************************
+ * Event facade
+ */
+NdbDictionary::Event::Event(const char * name)
+ : m_impl(* new NdbEventImpl(* this))
+{
+ setName(name);
+}
+
+NdbDictionary::Event::Event(const char * name, const Table& table)
+ : m_impl(* new NdbEventImpl(* this))
+{
+ setName(name);
+ setTable(table);
+}
+
+NdbDictionary::Event::Event(NdbEventImpl & impl)
+ : m_impl(impl)
+{
+}
+
+NdbDictionary::Event::~Event()
+{
+ NdbEventImpl * tmp = &m_impl;
+ if(this != tmp){
+ delete tmp;
+ }
+}
+
+void
+NdbDictionary::Event::setName(const char * name)
+{
+ m_impl.setName(name);
+}
+
+const char *
+NdbDictionary::Event::getName() const
+{
+ return m_impl.getName();
+}
+
+void
+NdbDictionary::Event::setTable(const Table& table)
+{
+ m_impl.setTable(table);
+}
+
+void
+NdbDictionary::Event::setTable(const char * table)
+{
+ m_impl.setTable(table);
+}
+
+const char*
+NdbDictionary::Event::getTableName() const
+{
+ return m_impl.getTableName();
+}
+
+void
+NdbDictionary::Event::addTableEvent(const TableEvent t)
+{
+ m_impl.addTableEvent(t);
+}
+
+void
+NdbDictionary::Event::setDurability(EventDurability d)
+{
+ m_impl.setDurability(d);
+}
+
+NdbDictionary::Event::EventDurability
+NdbDictionary::Event::getDurability() const
+{
+ return m_impl.getDurability();
+}
+
+void
+NdbDictionary::Event::addColumn(const Column & c){
+ NdbColumnImpl* col = new NdbColumnImpl;
+ (* col) = NdbColumnImpl::getImpl(c);
+ m_impl.m_columns.push_back(col);
+}
+
+void
+NdbDictionary::Event::addEventColumn(unsigned attrId)
+{
+ m_impl.m_attrIds.push_back(attrId);
+}
+
+void
+NdbDictionary::Event::addEventColumn(const char * name)
+{
+ const Column c(name);
+ addColumn(c);
+}
+
+void
+NdbDictionary::Event::addEventColumns(int n, const char ** names)
+{
+ for (int i = 0; i < n; i++)
+ addEventColumn(names[i]);
+}
+
+int NdbDictionary::Event::getNoOfEventColumns() const
+{
+ return m_impl.getNoOfEventColumns();
+}
+
+NdbDictionary::Object::Status
+NdbDictionary::Event::getObjectStatus() const
+{
+ return m_impl.m_status;
+}
+
+int
+NdbDictionary::Event::getObjectVersion() const
+{
+ return m_impl.m_version;
+}
+
+void NdbDictionary::Event::print()
+{
+ m_impl.print();
+}
+
+/*****************************************************************
+ * Dictionary facade
+ */
+NdbDictionary::Dictionary::Dictionary(Ndb & ndb)
+ : m_impl(* new NdbDictionaryImpl(ndb, *this))
+{
+}
+
+NdbDictionary::Dictionary::Dictionary(NdbDictionaryImpl & impl)
+ : m_impl(impl)
+{
+}
+NdbDictionary::Dictionary::~Dictionary(){
+ NdbDictionaryImpl * tmp = &m_impl;
+ if(this != tmp){
+ delete tmp;
+ }
+}
+
+int
+NdbDictionary::Dictionary::createTable(const Table & t){
+ return m_impl.createTable(NdbTableImpl::getImpl(t));
+}
+
+int
+NdbDictionary::Dictionary::dropTable(Table & t){
+ return m_impl.dropTable(NdbTableImpl::getImpl(t));
+}
+
+int
+NdbDictionary::Dictionary::dropTable(const char * name){
+ return m_impl.dropTable(name);
+}
+
+int
+NdbDictionary::Dictionary::alterTable(const Table & t){
+ return m_impl.alterTable(NdbTableImpl::getImpl(t));
+}
+
+const NdbDictionary::Table *
+NdbDictionary::Dictionary::getTable(const char * name, void **data) const
+{
+ NdbTableImpl * t = m_impl.getTable(name, data);
+ if(t)
+ return t->m_facade;
+ return 0;
+}
+
+void NdbDictionary::Dictionary::set_local_table_data_size(unsigned sz)
+{
+ m_impl.m_local_table_data_size= sz;
+}
+
+const NdbDictionary::Table *
+NdbDictionary::Dictionary::getTable(const char * name) const
+{
+ return getTable(name, 0);
+}
+
+void
+NdbDictionary::Dictionary::invalidateTable(const char * name){
+ NdbTableImpl * t = m_impl.getTable(name);
+ if(t)
+ m_impl.invalidateObject(* t);
+}
+
+void
+NdbDictionary::Dictionary::removeCachedTable(const char * name){
+ NdbTableImpl * t = m_impl.getTable(name);
+ if(t)
+ m_impl.removeCachedObject(* t);
+}
+
+int
+NdbDictionary::Dictionary::createIndex(const Index & ind)
+{
+ return m_impl.createIndex(NdbIndexImpl::getImpl(ind));
+}
+
+int
+NdbDictionary::Dictionary::dropIndex(const char * indexName,
+ const char * tableName)
+{
+ return m_impl.dropIndex(indexName, tableName);
+}
+
+const NdbDictionary::Index *
+NdbDictionary::Dictionary::getIndex(const char * indexName,
+ const char * tableName) const
+{
+ NdbIndexImpl * i = m_impl.getIndex(indexName, tableName);
+ if(i)
+ return i->m_facade;
+ return 0;
+}
+
+void
+NdbDictionary::Dictionary::invalidateIndex(const char * indexName,
+ const char * tableName){
+ NdbIndexImpl * i = m_impl.getIndex(indexName, tableName);
+ if(i) {
+ assert(i->m_table != 0);
+ m_impl.invalidateObject(* i->m_table);
+ }
+}
+
+void
+NdbDictionary::Dictionary::removeCachedIndex(const char * indexName,
+ const char * tableName){
+ NdbIndexImpl * i = m_impl.getIndex(indexName, tableName);
+ if(i) {
+ assert(i->m_table != 0);
+ m_impl.removeCachedObject(* i->m_table);
+ }
+}
+
+const NdbDictionary::Table *
+NdbDictionary::Dictionary::getIndexTable(const char * indexName,
+ const char * tableName) const
+{
+ NdbIndexImpl * i = m_impl.getIndex(indexName, tableName);
+ NdbTableImpl * t = m_impl.getTable(tableName);
+ if(i && t) {
+ NdbTableImpl * it = m_impl.getIndexTable(i, t);
+ return it->m_facade;
+ }
+ return 0;
+}
+
+
+int
+NdbDictionary::Dictionary::createEvent(const Event & ev)
+{
+ return m_impl.createEvent(NdbEventImpl::getImpl(ev));
+}
+
+int
+NdbDictionary::Dictionary::dropEvent(const char * eventName)
+{
+ return m_impl.dropEvent(eventName);
+}
+
+const NdbDictionary::Event *
+NdbDictionary::Dictionary::getEvent(const char * eventName)
+{
+ NdbEventImpl * t = m_impl.getEvent(eventName);
+ if(t)
+ return t->m_facade;
+ return 0;
+}
+
+int
+NdbDictionary::Dictionary::listObjects(List& list, Object::Type type)
+{
+ return m_impl.listObjects(list, type);
+}
+
+int
+NdbDictionary::Dictionary::listObjects(List& list, Object::Type type) const
+{
+ return m_impl.listObjects(list, type);
+}
+
+int
+NdbDictionary::Dictionary::listIndexes(List& list, const char * tableName)
+{
+ const NdbDictionary::Table* tab= getTable(tableName);
+ if(tab == 0)
+ {
+ return -1;
+ }
+ return m_impl.listIndexes(list, tab->getTableId());
+}
+
+int
+NdbDictionary::Dictionary::listIndexes(List& list,
+ const char * tableName) const
+{
+ const NdbDictionary::Table* tab= getTable(tableName);
+ if(tab == 0)
+ {
+ return -1;
+ }
+ return m_impl.listIndexes(list, tab->getTableId());
+}
+
+const struct NdbError &
+NdbDictionary::Dictionary::getNdbError() const {
+ return m_impl.getNdbError();
+}
+
+// printers
+
+NdbOut&
+operator<<(NdbOut& out, const NdbDictionary::Column& col)
+{
+ const CHARSET_INFO *cs = col.getCharset();
+ const char *csname = cs ? cs->name : "?";
+ out << col.getName() << " ";
+ switch (col.getType()) {
+ case NdbDictionary::Column::Tinyint:
+ out << "Tinyint";
+ break;
+ case NdbDictionary::Column::Tinyunsigned:
+ out << "Tinyunsigned";
+ break;
+ case NdbDictionary::Column::Smallint:
+ out << "Smallint";
+ break;
+ case NdbDictionary::Column::Smallunsigned:
+ out << "Smallunsigned";
+ break;
+ case NdbDictionary::Column::Mediumint:
+ out << "Mediumint";
+ break;
+ case NdbDictionary::Column::Mediumunsigned:
+ out << "Mediumunsigned";
+ break;
+ case NdbDictionary::Column::Int:
+ out << "Int";
+ break;
+ case NdbDictionary::Column::Unsigned:
+ out << "Unsigned";
+ break;
+ case NdbDictionary::Column::Bigint:
+ out << "Bigint";
+ break;
+ case NdbDictionary::Column::Bigunsigned:
+ out << "Bigunsigned";
+ break;
+ case NdbDictionary::Column::Float:
+ out << "Float";
+ break;
+ case NdbDictionary::Column::Double:
+ out << "Double";
+ break;
+ case NdbDictionary::Column::Olddecimal:
+ out << "Olddecimal(" << col.getPrecision() << "," << col.getScale() << ")";
+ break;
+ case NdbDictionary::Column::Olddecimalunsigned:
+ out << "Olddecimalunsigned(" << col.getPrecision() << "," << col.getScale() << ")";
+ break;
+ case NdbDictionary::Column::Decimal:
+ out << "Decimal(" << col.getPrecision() << "," << col.getScale() << ")";
+ break;
+ case NdbDictionary::Column::Decimalunsigned:
+ out << "Decimalunsigned(" << col.getPrecision() << "," << col.getScale() << ")";
+ break;
+ case NdbDictionary::Column::Char:
+ out << "Char(" << col.getLength() << ";" << csname << ")";
+ break;
+ case NdbDictionary::Column::Varchar:
+ out << "Varchar(" << col.getLength() << ";" << csname << ")";
+ break;
+ case NdbDictionary::Column::Binary:
+ out << "Binary(" << col.getLength() << ")";
+ break;
+ case NdbDictionary::Column::Varbinary:
+ out << "Varbinary(" << col.getLength() << ")";
+ break;
+ case NdbDictionary::Column::Datetime:
+ out << "Datetime";
+ break;
+ case NdbDictionary::Column::Date:
+ out << "Date";
+ break;
+ case NdbDictionary::Column::Blob:
+ out << "Blob(" << col.getInlineSize() << "," << col.getPartSize()
+ << ";" << col.getStripeSize() << ")";
+ break;
+ case NdbDictionary::Column::Text:
+ out << "Text(" << col.getInlineSize() << "," << col.getPartSize()
+ << ";" << col.getStripeSize() << ";" << csname << ")";
+ break;
+ case NdbDictionary::Column::Time:
+ out << "Time";
+ break;
+ case NdbDictionary::Column::Year:
+ out << "Year";
+ break;
+ case NdbDictionary::Column::Timestamp:
+ out << "Timestamp";
+ break;
+ case NdbDictionary::Column::Undefined:
+ out << "Undefined";
+ break;
+ case NdbDictionary::Column::Bit:
+ out << "Bit(" << col.getLength() << ")";
+ break;
+ case NdbDictionary::Column::Longvarchar:
+ out << "Longvarchar(" << col.getLength() << ";" << csname << ")";
+ break;
+ case NdbDictionary::Column::Longvarbinary:
+ out << "Longvarbinary(" << col.getLength() << ")";
+ break;
+ default:
+ out << "Type" << (Uint32)col.getType();
+ break;
+ }
+ // show unusual (non-MySQL) array size
+ if (col.getLength() != 1) {
+ switch (col.getType()) {
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Binary:
+ case NdbDictionary::Column::Varbinary:
+ case NdbDictionary::Column::Blob:
+ case NdbDictionary::Column::Text:
+ case NdbDictionary::Column::Bit:
+ case NdbDictionary::Column::Longvarchar:
+ case NdbDictionary::Column::Longvarbinary:
+ break;
+ default:
+ out << " [" << col.getLength() << "]";
+ break;
+ }
+ }
+ if (col.getPrimaryKey())
+ out << " PRIMARY KEY";
+ else if (! col.getNullable())
+ out << " NOT NULL";
+ else
+ out << " NULL";
+
+ if(col.getDistributionKey())
+ out << " DISTRIBUTION KEY";
+
+ return out;
+}
+
+const NdbDictionary::Column * NdbDictionary::Column::FRAGMENT = 0;
+const NdbDictionary::Column * NdbDictionary::Column::FRAGMENT_MEMORY = 0;
+const NdbDictionary::Column * NdbDictionary::Column::ROW_COUNT = 0;
+const NdbDictionary::Column * NdbDictionary::Column::COMMIT_COUNT = 0;
+const NdbDictionary::Column * NdbDictionary::Column::ROW_SIZE = 0;
+const NdbDictionary::Column * NdbDictionary::Column::RANGE_NO = 0;
diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
new file mode 100644
index 00000000000..ff87a72f636
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
@@ -0,0 +1,3124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "NdbDictionaryImpl.hpp"
+#include "API.hpp"
+#include <NdbOut.hpp>
+#include "NdbApiSignal.hpp"
+#include "TransporterFacade.hpp"
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/CreateTable.hpp>
+#include <signaldata/CreateIndx.hpp>
+#include <signaldata/CreateEvnt.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <signaldata/DropTable.hpp>
+#include <signaldata/AlterTable.hpp>
+#include <signaldata/DropIndx.hpp>
+#include <signaldata/ListTables.hpp>
+#include <SimpleProperties.hpp>
+#include <Bitmask.hpp>
+#include <AttributeList.hpp>
+#include <NdbEventOperation.hpp>
+#include "NdbEventOperationImpl.hpp"
+#include <NdbBlob.hpp>
+#include "NdbBlobImpl.hpp"
+#include <AttributeHeader.hpp>
+#include <my_sys.h>
+
+#define DEBUG_PRINT 0
+#define INCOMPATIBLE_VERSION -2
+
+//#define EVENT_DEBUG
+
+/**
+ * Column
+ */
+NdbColumnImpl::NdbColumnImpl()
+ : NdbDictionary::Column(* this), m_attrId(-1), m_facade(this)
+{
+ init();
+}
+
+NdbColumnImpl::NdbColumnImpl(NdbDictionary::Column & f)
+ : NdbDictionary::Column(* this), m_attrId(-1), m_facade(&f)
+{
+ init();
+}
+
+NdbColumnImpl&
+NdbColumnImpl::operator=(const NdbColumnImpl& col)
+{
+ m_attrId = col.m_attrId;
+ m_name = col.m_name;
+ m_type = col.m_type;
+ m_precision = col.m_precision;
+ m_cs = col.m_cs;
+ m_scale = col.m_scale;
+ m_length = col.m_length;
+ m_pk = col.m_pk;
+ m_distributionKey = col.m_distributionKey;
+ m_nullable = col.m_nullable;
+ m_autoIncrement = col.m_autoIncrement;
+ m_autoIncrementInitialValue = col.m_autoIncrementInitialValue;
+ m_defaultValue = col.m_defaultValue;
+ m_attrSize = col.m_attrSize;
+ m_arraySize = col.m_arraySize;
+ m_keyInfoPos = col.m_keyInfoPos;
+ m_blobTable = col.m_blobTable;
+ // Do not copy m_facade !!
+
+ return *this;
+}
+
+void
+NdbColumnImpl::init(Type t)
+{
+ // do not use default_charset_info as it may not be initialized yet
+ // use binary collation until NDB tests can handle charsets
+ CHARSET_INFO* default_cs = &my_charset_bin;
+ m_type = t;
+ switch (m_type) {
+ case Tinyint:
+ case Tinyunsigned:
+ case Smallint:
+ case Smallunsigned:
+ case Mediumint:
+ case Mediumunsigned:
+ case Int:
+ case Unsigned:
+ case Bigint:
+ case Bigunsigned:
+ case Float:
+ case Double:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1;
+ m_cs = NULL;
+ break;
+ case Olddecimal:
+ case Olddecimalunsigned:
+ case Decimal:
+ case Decimalunsigned:
+ m_precision = 10;
+ m_scale = 0;
+ m_length = 1;
+ m_cs = NULL;
+ break;
+ case Char:
+ case Varchar:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1;
+ m_cs = default_cs;
+ break;
+ case Binary:
+ case Varbinary:
+ case Datetime:
+ case Date:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1;
+ m_cs = NULL;
+ break;
+ case Blob:
+ m_precision = 256;
+ m_scale = 8000;
+ m_length = 4;
+ m_cs = NULL;
+ break;
+ case Text:
+ m_precision = 256;
+ m_scale = 8000;
+ m_length = 4;
+ m_cs = default_cs;
+ break;
+ case Time:
+ case Year:
+ case Timestamp:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1;
+ m_cs = NULL;
+ break;
+ case Bit:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1;
+ m_cs = NULL;
+ break;
+ case Longvarchar:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1; // legal
+ m_cs = default_cs;
+ break;
+ case Longvarbinary:
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 1; // legal
+ m_cs = NULL;
+ break;
+ case Undefined:
+ assert(false);
+ break;
+ }
+ m_pk = false;
+ m_nullable = false;
+ m_distributionKey = false;
+ m_keyInfoPos = 0;
+ // next 2 are set at run time
+ m_attrSize = 0;
+ m_arraySize = 0;
+ m_autoIncrement = false;
+ m_autoIncrementInitialValue = 1;
+ m_blobTable = NULL;
+}
+
+NdbColumnImpl::~NdbColumnImpl()
+{
+}
+
+bool
+NdbColumnImpl::equal(const NdbColumnImpl& col) const
+{
+ DBUG_ENTER("NdbColumnImpl::equal");
+ if(strcmp(m_name.c_str(), col.m_name.c_str()) != 0){
+ DBUG_RETURN(false);
+ }
+ if(m_type != col.m_type){
+ DBUG_RETURN(false);
+ }
+ if(m_pk != col.m_pk){
+ DBUG_RETURN(false);
+ }
+ if(m_nullable != col.m_nullable){
+ DBUG_RETURN(false);
+ }
+ if(m_pk){
+ if(m_distributionKey != col.m_distributionKey){
+ DBUG_RETURN(false);
+ }
+ }
+ if (m_precision != col.m_precision ||
+ m_scale != col.m_scale ||
+ m_length != col.m_length ||
+ m_cs != col.m_cs) {
+ DBUG_RETURN(false);
+ }
+ if (m_autoIncrement != col.m_autoIncrement){
+ DBUG_RETURN(false);
+ }
+ if(strcmp(m_defaultValue.c_str(), col.m_defaultValue.c_str()) != 0){
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
+NdbDictionary::Column *
+NdbColumnImpl::create_psuedo(const char * name){
+ NdbDictionary::Column * col = new NdbDictionary::Column();
+ col->setName(name);
+ if(!strcmp(name, "NDB$FRAGMENT")){
+ col->setType(NdbDictionary::Column::Unsigned);
+ col->m_impl.m_attrId = AttributeHeader::FRAGMENT;
+ col->m_impl.m_attrSize = 4;
+ col->m_impl.m_arraySize = 1;
+ } else if(!strcmp(name, "NDB$FRAGMENT_MEMORY")){
+ col->setType(NdbDictionary::Column::Bigunsigned);
+ col->m_impl.m_attrId = AttributeHeader::FRAGMENT_MEMORY;
+ col->m_impl.m_attrSize = 8;
+ col->m_impl.m_arraySize = 1;
+ } else if(!strcmp(name, "NDB$ROW_COUNT")){
+ col->setType(NdbDictionary::Column::Bigunsigned);
+ col->m_impl.m_attrId = AttributeHeader::ROW_COUNT;
+ col->m_impl.m_attrSize = 8;
+ col->m_impl.m_arraySize = 1;
+ } else if(!strcmp(name, "NDB$COMMIT_COUNT")){
+ col->setType(NdbDictionary::Column::Bigunsigned);
+ col->m_impl.m_attrId = AttributeHeader::COMMIT_COUNT;
+ col->m_impl.m_attrSize = 8;
+ col->m_impl.m_arraySize = 1;
+ } else if(!strcmp(name, "NDB$ROW_SIZE")){
+ col->setType(NdbDictionary::Column::Unsigned);
+ col->m_impl.m_attrId = AttributeHeader::ROW_SIZE;
+ col->m_impl.m_attrSize = 4;
+ col->m_impl.m_arraySize = 1;
+ } else if(!strcmp(name, "NDB$RANGE_NO")){
+ col->setType(NdbDictionary::Column::Unsigned);
+ col->m_impl.m_attrId = AttributeHeader::RANGE_NO;
+ col->m_impl.m_attrSize = 4;
+ col->m_impl.m_arraySize = 1;
+ } else {
+ abort();
+ }
+ return col;
+}
+
+/**
+ * NdbTableImpl
+ */
+
+NdbTableImpl::NdbTableImpl()
+ : NdbDictionary::Table(* this), m_facade(this)
+{
+ init();
+}
+
+NdbTableImpl::NdbTableImpl(NdbDictionary::Table & f)
+ : NdbDictionary::Table(* this), m_facade(&f)
+{
+ init();
+}
+
+NdbTableImpl::~NdbTableImpl()
+{
+ if (m_index != 0) {
+ delete m_index;
+ m_index = 0;
+ }
+ for (unsigned i = 0; i < m_columns.size(); i++)
+ delete m_columns[i];
+}
+
+void
+NdbTableImpl::init(){
+ clearNewProperties();
+ m_frm.clear();
+ m_fragmentType = NdbDictionary::Object::FragAllSmall;
+ m_logging = true;
+ m_kvalue = 6;
+ m_minLoadFactor = 78;
+ m_maxLoadFactor = 80;
+
+ m_index = 0;
+ m_indexType = NdbDictionary::Index::Undefined;
+
+ m_noOfKeys = 0;
+ m_noOfDistributionKeys = 0;
+ m_fragmentCount = 0;
+ m_keyLenInWords = 0;
+ m_noOfBlobs = 0;
+}
+
+bool
+NdbTableImpl::equal(const NdbTableImpl& obj) const
+{
+ DBUG_ENTER("NdbTableImpl::equal");
+ if ((m_internalName.c_str() == NULL) ||
+ (strcmp(m_internalName.c_str(), "") == 0) ||
+ (obj.m_internalName.c_str() == NULL) ||
+ (strcmp(obj.m_internalName.c_str(), "") == 0)) {
+ // Shallow equal
+ if(strcmp(getName(), obj.getName()) != 0){
+ DBUG_PRINT("info",("name %s != %s",getName(),obj.getName()));
+ DBUG_RETURN(false);
+ }
+ } else
+ // Deep equal
+ if(strcmp(m_internalName.c_str(), obj.m_internalName.c_str()) != 0){
+ {
+ DBUG_PRINT("info",("m_internalName %s != %s",
+ m_internalName.c_str(),obj.m_internalName.c_str()));
+ DBUG_RETURN(false);
+ }
+ }
+ if(m_fragmentType != obj.m_fragmentType){
+ DBUG_PRINT("info",("m_fragmentType %d != %d",m_fragmentType,obj.m_fragmentType));
+ DBUG_RETURN(false);
+ }
+ if(m_columns.size() != obj.m_columns.size()){
+ DBUG_PRINT("info",("m_columns.size %d != %d",m_columns.size(),obj.m_columns.size()));
+ DBUG_RETURN(false);
+ }
+
+ for(unsigned i = 0; i<obj.m_columns.size(); i++){
+ if(!m_columns[i]->equal(* obj.m_columns[i])){
+ DBUG_PRINT("info",("m_columns [%d] != [%d]",i,i));
+ DBUG_RETURN(false);
+ }
+ }
+
+ if(m_logging != obj.m_logging){
+ DBUG_PRINT("info",("m_logging %d != %d",m_logging,obj.m_logging));
+ DBUG_RETURN(false);
+ }
+
+ if(m_kvalue != obj.m_kvalue){
+ DBUG_PRINT("info",("m_kvalue %d != %d",m_kvalue,obj.m_kvalue));
+ DBUG_RETURN(false);
+ }
+
+ if(m_minLoadFactor != obj.m_minLoadFactor){
+ DBUG_PRINT("info",("m_minLoadFactor %d != %d",m_minLoadFactor,obj.m_minLoadFactor));
+ DBUG_RETURN(false);
+ }
+
+ if(m_maxLoadFactor != obj.m_maxLoadFactor){
+ DBUG_PRINT("info",("m_maxLoadFactor %d != %d",m_maxLoadFactor,obj.m_maxLoadFactor));
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
+void
+NdbTableImpl::assign(const NdbTableImpl& org)
+{
+ m_tableId = org.m_tableId;
+ m_internalName.assign(org.m_internalName);
+ m_externalName.assign(org.m_externalName);
+ m_newExternalName.assign(org.m_newExternalName);
+ m_frm.assign(org.m_frm.get_data(), org.m_frm.length());
+ m_fragmentType = org.m_fragmentType;
+ m_fragmentCount = org.m_fragmentCount;
+
+ for(unsigned i = 0; i<org.m_columns.size(); i++){
+ NdbColumnImpl * col = new NdbColumnImpl();
+ const NdbColumnImpl * iorg = org.m_columns[i];
+ (* col) = (* iorg);
+ m_columns.push_back(col);
+ }
+
+ m_logging = org.m_logging;
+ m_kvalue = org.m_kvalue;
+ m_minLoadFactor = org.m_minLoadFactor;
+ m_maxLoadFactor = org.m_maxLoadFactor;
+
+ if (m_index != 0)
+ delete m_index;
+ m_index = org.m_index;
+
+ m_noOfDistributionKeys = org.m_noOfDistributionKeys;
+ m_noOfKeys = org.m_noOfKeys;
+ m_keyLenInWords = org.m_keyLenInWords;
+ m_noOfBlobs = org.m_noOfBlobs;
+
+ m_version = org.m_version;
+ m_status = org.m_status;
+}
+
+void NdbTableImpl::setName(const char * name)
+{
+ m_newExternalName.assign(name);
+}
+
+const char *
+NdbTableImpl::getName() const
+{
+ if (m_newExternalName.empty())
+ return m_externalName.c_str();
+ else
+ return m_newExternalName.c_str();
+}
+
+void NdbTableImpl::clearNewProperties()
+{
+ m_newExternalName.assign("");
+ m_changeMask = 0;
+}
+
+void NdbTableImpl::copyNewProperties()
+{
+ if (!m_newExternalName.empty()) {
+ m_externalName.assign(m_newExternalName);
+ AlterTableReq::setNameFlag(m_changeMask, true);
+ }
+}
+
+void
+NdbTableImpl::buildColumnHash(){
+ const Uint32 size = m_columns.size();
+
+ int i;
+ for(i = 31; i >= 0; i--){
+ if(((1 << i) & size) != 0){
+ m_columnHashMask = (1 << (i + 1)) - 1;
+ break;
+ }
+ }
+
+ Vector<Uint32> hashValues;
+ Vector<Vector<Uint32> > chains; chains.fill(size, hashValues);
+ for(i = 0; i< (int) size; i++){
+ Uint32 hv = Hash(m_columns[i]->getName()) & 0xFFFE;
+ Uint32 bucket = hv & m_columnHashMask;
+ bucket = (bucket < size ? bucket : bucket - size);
+ assert(bucket < size);
+ hashValues.push_back(hv);
+ chains[bucket].push_back(i);
+ }
+
+ m_columnHash.clear();
+ Uint32 tmp = 1;
+ m_columnHash.fill((unsigned)size-1, tmp); // Default no chaining
+
+ Uint32 pos = 0; // In overflow vector
+ for(i = 0; i< (int) size; i++){
+ Uint32 sz = chains[i].size();
+ if(sz == 1){
+ Uint32 col = chains[i][0];
+ Uint32 hv = hashValues[col];
+ Uint32 bucket = hv & m_columnHashMask;
+ bucket = (bucket < size ? bucket : bucket - size);
+ m_columnHash[bucket] = (col << 16) | hv | 1;
+ } else if(sz > 1){
+ Uint32 col = chains[i][0];
+ Uint32 hv = hashValues[col];
+ Uint32 bucket = hv & m_columnHashMask;
+ bucket = (bucket < size ? bucket : bucket - size);
+ m_columnHash[bucket] = (sz << 16) | (((size - bucket) + pos) << 1);
+ for(size_t j = 0; j<sz; j++, pos++){
+ Uint32 col = chains[i][j];
+ Uint32 hv = hashValues[col];
+ m_columnHash.push_back((col << 16) | hv);
+ }
+ }
+ }
+
+ m_columnHash.push_back(0); // Overflow when looping in end of array
+
+#if 0
+ for(size_t i = 0; i<m_columnHash.size(); i++){
+ Uint32 tmp = m_columnHash[i];
+ int col = -1;
+ if(i < size && (tmp & 1) == 1){
+ col = (tmp >> 16);
+ } else if(i >= size){
+ col = (tmp >> 16);
+ }
+ ndbout_c("m_columnHash[%d] %s = %x",
+ i, col > 0 ? m_columns[col]->getName() : "" , m_columnHash[i]);
+ }
+#endif
+}
+
+Uint32
+NdbTableImpl::get_nodes(Uint32 hashValue, const Uint16 ** nodes) const
+{
+ if(m_replicaCount > 0)
+ {
+ Uint32 fragmentId = hashValue & m_hashValueMask;
+ if(fragmentId < m_hashpointerValue)
+ {
+ fragmentId = hashValue & ((m_hashValueMask << 1) + 1);
+ }
+ Uint32 pos = fragmentId * m_replicaCount;
+ if(pos + m_replicaCount <= m_fragments.size())
+ {
+ * nodes = m_fragments.getBase()+pos;
+ return m_replicaCount;
+ }
+ }
+ return 0;
+}
+
+/**
+ * NdbIndexImpl
+ */
+
+NdbIndexImpl::NdbIndexImpl() :
+ NdbDictionary::Index(* this),
+ m_facade(this)
+{
+ m_logging = true;
+}
+
+NdbIndexImpl::NdbIndexImpl(NdbDictionary::Index & f) :
+ NdbDictionary::Index(* this),
+ m_facade(&f)
+{
+ m_logging = true;
+}
+
+NdbIndexImpl::~NdbIndexImpl(){
+ for (unsigned i = 0; i < m_columns.size(); i++)
+ delete m_columns[i];
+}
+
+void NdbIndexImpl::setName(const char * name)
+{
+ m_externalName.assign(name);
+}
+
+const char *
+NdbIndexImpl::getName() const
+{
+ return m_externalName.c_str();
+}
+
+void
+NdbIndexImpl::setTable(const char * table)
+{
+ m_tableName.assign(table);
+}
+
+const char *
+NdbIndexImpl::getTable() const
+{
+ return m_tableName.c_str();
+}
+
+const NdbTableImpl *
+NdbIndexImpl::getIndexTable() const
+{
+ return m_table;
+}
+
+/**
+ * NdbEventImpl
+ */
+
+NdbEventImpl::NdbEventImpl() :
+ NdbDictionary::Event(* this),
+ m_facade(this)
+{
+ mi_type = 0;
+ m_dur = NdbDictionary::Event::ED_UNDEFINED;
+ eventOp = NULL;
+ m_tableImpl = NULL;
+}
+
+NdbEventImpl::NdbEventImpl(NdbDictionary::Event & f) :
+ NdbDictionary::Event(* this),
+ m_facade(&f)
+{
+ mi_type = 0;
+ m_dur = NdbDictionary::Event::ED_UNDEFINED;
+ eventOp = NULL;
+ m_tableImpl = NULL;
+}
+
+NdbEventImpl::~NdbEventImpl()
+{
+ for (unsigned i = 0; i < m_columns.size(); i++)
+ delete m_columns[i];
+}
+
+void NdbEventImpl::setName(const char * name)
+{
+ m_externalName.assign(name);
+}
+
+const char *NdbEventImpl::getName() const
+{
+ return m_externalName.c_str();
+}
+
+void
+NdbEventImpl::setTable(const NdbDictionary::Table& table)
+{
+ m_tableImpl= &NdbTableImpl::getImpl(table);
+ m_tableName.assign(m_tableImpl->getName());
+}
+
+void
+NdbEventImpl::setTable(const char * table)
+{
+ m_tableName.assign(table);
+}
+
+const char *
+NdbEventImpl::getTableName() const
+{
+ return m_tableName.c_str();
+}
+
+void
+NdbEventImpl::addTableEvent(const NdbDictionary::Event::TableEvent t = NdbDictionary::Event::TE_ALL)
+{
+ switch (t) {
+ case NdbDictionary::Event::TE_INSERT : mi_type |= 1; break;
+ case NdbDictionary::Event::TE_DELETE : mi_type |= 2; break;
+ case NdbDictionary::Event::TE_UPDATE : mi_type |= 4; break;
+ default: mi_type = 4 | 2 | 1; // all types
+ }
+}
+
+void
+NdbEventImpl::setDurability(NdbDictionary::Event::EventDurability d)
+{
+ m_dur = d;
+}
+
+NdbDictionary::Event::EventDurability
+NdbEventImpl::getDurability() const
+{
+ return m_dur;
+}
+
+int NdbEventImpl::getNoOfEventColumns() const
+{
+ return m_attrIds.size() + m_columns.size();
+}
+
+/**
+ * NdbDictionaryImpl
+ */
+
+NdbDictionaryImpl::NdbDictionaryImpl(Ndb &ndb)
+ : NdbDictionary::Dictionary(* this),
+ m_facade(this),
+ m_receiver(m_error),
+ m_ndb(ndb)
+{
+ m_globalHash = 0;
+ m_local_table_data_size= 0;
+}
+
+NdbDictionaryImpl::NdbDictionaryImpl(Ndb &ndb,
+ NdbDictionary::Dictionary & f)
+ : NdbDictionary::Dictionary(* this),
+ m_facade(&f),
+ m_receiver(m_error),
+ m_ndb(ndb)
+{
+ m_globalHash = 0;
+ m_local_table_data_size= 0;
+}
+
+static int f_dictionary_count = 0;
+
+NdbDictionaryImpl::~NdbDictionaryImpl()
+{
+ NdbElement_t<Ndb_local_table_info> * curr = m_localHash.m_tableHash.getNext(0);
+ if(m_globalHash){
+ while(curr != 0){
+ m_globalHash->lock();
+ m_globalHash->release(curr->theData->m_table_impl);
+ Ndb_local_table_info::destroy(curr->theData);
+ m_globalHash->unlock();
+
+ curr = m_localHash.m_tableHash.getNext(curr);
+ }
+
+ m_globalHash->lock();
+ if(--f_dictionary_count == 0){
+ delete NdbDictionary::Column::FRAGMENT;
+ delete NdbDictionary::Column::FRAGMENT_MEMORY;
+ delete NdbDictionary::Column::ROW_COUNT;
+ delete NdbDictionary::Column::COMMIT_COUNT;
+ delete NdbDictionary::Column::ROW_SIZE;
+ NdbDictionary::Column::FRAGMENT= 0;
+ NdbDictionary::Column::FRAGMENT_MEMORY= 0;
+ NdbDictionary::Column::ROW_COUNT= 0;
+ NdbDictionary::Column::COMMIT_COUNT= 0;
+ NdbDictionary::Column::ROW_SIZE= 0;
+ }
+ m_globalHash->unlock();
+ } else {
+ assert(curr == 0);
+ }
+}
+
+Ndb_local_table_info *
+NdbDictionaryImpl::fetchGlobalTableImpl(const char * internalTableName)
+{
+ NdbTableImpl *impl;
+
+ m_globalHash->lock();
+ impl = m_globalHash->get(internalTableName);
+ m_globalHash->unlock();
+
+ if (impl == 0){
+ impl = m_receiver.getTable(internalTableName,
+ m_ndb.usingFullyQualifiedNames());
+ m_globalHash->lock();
+ m_globalHash->put(internalTableName, impl);
+ m_globalHash->unlock();
+
+ if(impl == 0){
+ return 0;
+ }
+ }
+
+ Ndb_local_table_info *info=
+ Ndb_local_table_info::create(impl, m_local_table_data_size);
+
+ m_localHash.put(internalTableName, info);
+
+ m_ndb.theFirstTupleId[impl->getTableId()] = ~0;
+ m_ndb.theLastTupleId[impl->getTableId()] = ~0;
+
+ return info;
+}
+
+#if 0
+bool
+NdbDictionaryImpl::setTransporter(class TransporterFacade * tf)
+{
+ if(tf != 0){
+ m_globalHash = &tf->m_globalDictCache;
+ return m_receiver.setTransporter(tf);
+ }
+
+ return false;
+}
+#endif
+
+bool
+NdbDictionaryImpl::setTransporter(class Ndb* ndb,
+ class TransporterFacade * tf)
+{
+ m_globalHash = &tf->m_globalDictCache;
+ if(m_receiver.setTransporter(ndb, tf)){
+ m_globalHash->lock();
+ if(f_dictionary_count++ == 0){
+ NdbDictionary::Column::FRAGMENT=
+ NdbColumnImpl::create_psuedo("NDB$FRAGMENT");
+ NdbDictionary::Column::FRAGMENT_MEMORY=
+ NdbColumnImpl::create_psuedo("NDB$FRAGMENT_MEMORY");
+ NdbDictionary::Column::ROW_COUNT=
+ NdbColumnImpl::create_psuedo("NDB$ROW_COUNT");
+ NdbDictionary::Column::COMMIT_COUNT=
+ NdbColumnImpl::create_psuedo("NDB$COMMIT_COUNT");
+ NdbDictionary::Column::ROW_SIZE=
+ NdbColumnImpl::create_psuedo("NDB$ROW_SIZE");
+ NdbDictionary::Column::RANGE_NO=
+ NdbColumnImpl::create_psuedo("NDB$RANGE_NO");
+ }
+ m_globalHash->unlock();
+ return true;
+ }
+ return false;
+}
+
+NdbTableImpl *
+NdbDictionaryImpl::getIndexTable(NdbIndexImpl * index,
+ NdbTableImpl * table)
+{
+ const char * internalName =
+ m_ndb.internalizeIndexName(table, index->getName());
+
+ return getTable(m_ndb.externalizeTableName(internalName));
+}
+
+#if 0
+bool
+NdbDictInterface::setTransporter(class TransporterFacade * tf)
+{
+ if(tf == 0)
+ return false;
+
+ Guard g(tf->theMutexPtr);
+
+ m_blockNumber = tf->open(this,
+ execSignal,
+ execNodeStatus);
+
+ if ( m_blockNumber == -1 ) {
+ m_error.code= 4105;
+ return false; // no more free blocknumbers
+ }//if
+ Uint32 theNode = tf->ownId();
+ m_reference = numberToRef(m_blockNumber, theNode);
+ m_transporter = tf;
+ m_waiter.m_mutex = tf->theMutexPtr;
+
+ return true;
+}
+#endif
+
+bool
+NdbDictInterface::setTransporter(class Ndb* ndb, class TransporterFacade * tf)
+{
+ m_reference = ndb->getReference();
+ m_transporter = tf;
+ m_waiter.m_mutex = tf->theMutexPtr;
+
+ return true;
+}
+
+NdbDictInterface::~NdbDictInterface()
+{
+}
+
+void
+NdbDictInterface::execSignal(void* dictImpl,
+ class NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3])
+{
+ NdbDictInterface * tmp = (NdbDictInterface*)dictImpl;
+
+ const Uint32 gsn = signal->readSignalNumber();
+ switch(gsn){
+ case GSN_GET_TABINFOREF:
+ tmp->execGET_TABINFO_REF(signal, ptr);
+ break;
+ case GSN_GET_TABINFO_CONF:
+ tmp->execGET_TABINFO_CONF(signal, ptr);
+ break;
+ case GSN_CREATE_TABLE_REF:
+ tmp->execCREATE_TABLE_REF(signal, ptr);
+ break;
+ case GSN_CREATE_TABLE_CONF:
+ tmp->execCREATE_TABLE_CONF(signal, ptr);
+ break;
+ case GSN_DROP_TABLE_REF:
+ tmp->execDROP_TABLE_REF(signal, ptr);
+ break;
+ case GSN_DROP_TABLE_CONF:
+ tmp->execDROP_TABLE_CONF(signal, ptr);
+ break;
+ case GSN_ALTER_TABLE_REF:
+ tmp->execALTER_TABLE_REF(signal, ptr);
+ break;
+ case GSN_ALTER_TABLE_CONF:
+ tmp->execALTER_TABLE_CONF(signal, ptr);
+ break;
+ case GSN_CREATE_INDX_REF:
+ tmp->execCREATE_INDX_REF(signal, ptr);
+ break;
+ case GSN_CREATE_INDX_CONF:
+ tmp->execCREATE_INDX_CONF(signal, ptr);
+ break;
+ case GSN_DROP_INDX_REF:
+ tmp->execDROP_INDX_REF(signal, ptr);
+ break;
+ case GSN_DROP_INDX_CONF:
+ tmp->execDROP_INDX_CONF(signal, ptr);
+ break;
+ case GSN_CREATE_EVNT_REF:
+ tmp->execCREATE_EVNT_REF(signal, ptr);
+ break;
+ case GSN_CREATE_EVNT_CONF:
+ tmp->execCREATE_EVNT_CONF(signal, ptr);
+ break;
+ case GSN_SUB_START_CONF:
+ tmp->execSUB_START_CONF(signal, ptr);
+ break;
+ case GSN_SUB_START_REF:
+ tmp->execSUB_START_REF(signal, ptr);
+ break;
+ case GSN_SUB_TABLE_DATA:
+ tmp->execSUB_TABLE_DATA(signal, ptr);
+ break;
+ case GSN_SUB_GCP_COMPLETE_REP:
+ tmp->execSUB_GCP_COMPLETE_REP(signal, ptr);
+ break;
+ case GSN_SUB_STOP_CONF:
+ tmp->execSUB_STOP_CONF(signal, ptr);
+ break;
+ case GSN_SUB_STOP_REF:
+ tmp->execSUB_STOP_REF(signal, ptr);
+ break;
+ case GSN_DROP_EVNT_REF:
+ tmp->execDROP_EVNT_REF(signal, ptr);
+ break;
+ case GSN_DROP_EVNT_CONF:
+ tmp->execDROP_EVNT_CONF(signal, ptr);
+ break;
+ case GSN_LIST_TABLES_CONF:
+ tmp->execLIST_TABLES_CONF(signal, ptr);
+ break;
+ default:
+ abort();
+ }
+}
+
+void
+NdbDictInterface::execNodeStatus(void* dictImpl, Uint32 aNode,
+ bool alive, bool nfCompleted)
+{
+ NdbDictInterface * tmp = (NdbDictInterface*)dictImpl;
+
+ if(!alive && !nfCompleted){
+ return;
+ }
+
+ if (!alive && nfCompleted){
+ tmp->m_waiter.nodeFail(aNode);
+ }
+}
+
+int
+NdbDictInterface::dictSignal(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3],int noLSP,
+ const int useMasterNodeId,
+ const Uint32 RETRIES,
+ const WaitSignalType wst,
+ const int theWait,
+ const int *errcodes,
+ const int noerrcodes,
+ const int temporaryMask)
+{
+ DBUG_ENTER("NdbDictInterface::dictSignal");
+ DBUG_PRINT("enter", ("useMasterNodeId: %d", useMasterNodeId));
+ for(Uint32 i = 0; i<RETRIES; i++){
+ //if (useMasterNodeId == 0)
+ m_buffer.clear();
+
+ // Protected area
+ m_transporter->lock_mutex();
+ Uint32 aNodeId;
+ if (useMasterNodeId) {
+ if ((m_masterNodeId == 0) ||
+ (!m_transporter->get_node_alive(m_masterNodeId))) {
+ m_masterNodeId = m_transporter->get_an_alive_node();
+ }//if
+ aNodeId = m_masterNodeId;
+ } else {
+ aNodeId = m_transporter->get_an_alive_node();
+ }
+ if(aNodeId == 0){
+ m_error.code= 4009;
+ m_transporter->unlock_mutex();
+ DBUG_RETURN(-1);
+ }
+ {
+ int r;
+ if (ptr) {
+#ifdef EVENT_DEBUG
+ printf("Long signal %d ptr", noLSP);
+ for (int q=0;q<noLSP;q++) {
+ printf(" sz %d", ptr[q].sz);
+ }
+ printf("\n");
+#endif
+ r = m_transporter->sendFragmentedSignal(signal, aNodeId, ptr, noLSP);
+ } else {
+#ifdef EVENT_DEBUG
+ printf("Short signal\n");
+#endif
+ r = m_transporter->sendSignal(signal, aNodeId);
+ }
+ if(r != 0){
+ m_transporter->unlock_mutex();
+ continue;
+ }
+ }
+
+ m_error.code= 0;
+
+ m_waiter.m_node = aNodeId;
+ m_waiter.m_state = wst;
+
+ m_waiter.wait(theWait);
+ m_transporter->unlock_mutex();
+ // End of Protected area
+
+ if(m_waiter.m_state == NO_WAIT && m_error.code == 0){
+ // Normal return
+ DBUG_RETURN(0);
+ }
+
+ /**
+ * Handle error codes
+ */
+ if(m_waiter.m_state == WAIT_NODE_FAILURE)
+ continue;
+
+ if(m_waiter.m_state == WST_WAIT_TIMEOUT)
+ {
+ m_error.code = 4008;
+ DBUG_RETURN(-1);
+ }
+
+ if ( (temporaryMask & m_error.code) != 0 ) {
+ continue;
+ }
+ if (errcodes) {
+ int doContinue = 0;
+ for (int j=0; j < noerrcodes; j++)
+ if(m_error.code == errcodes[j]) {
+ doContinue = 1;
+ break;
+ }
+ if (doContinue)
+ continue;
+ }
+
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(-1);
+}
+
+/*****************************************************************
+ * get tab info
+ */
+NdbTableImpl *
+NdbDictInterface::getTable(int tableId, bool fullyQualifiedNames)
+{
+ NdbApiSignal tSignal(m_reference);
+ GetTabInfoReq * const req = CAST_PTR(GetTabInfoReq, tSignal.getDataPtrSend());
+
+ req->senderRef = m_reference;
+ req->senderData = 0;
+ req->requestType =
+ GetTabInfoReq::RequestById | GetTabInfoReq::LongSignalConf;
+ req->tableId = tableId;
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_GET_TABINFOREQ;
+ tSignal.theLength = GetTabInfoReq::SignalLength;
+
+ return getTable(&tSignal, 0, 0, fullyQualifiedNames);
+}
+
+NdbTableImpl *
+NdbDictInterface::getTable(const char * name, bool fullyQualifiedNames)
+{
+ NdbApiSignal tSignal(m_reference);
+ GetTabInfoReq * const req = CAST_PTR(GetTabInfoReq, tSignal.getDataPtrSend());
+
+ const Uint32 strLen = strlen(name) + 1; // NULL Terminated
+ if(strLen > MAX_TAB_NAME_SIZE) {//sizeof(req->tableName)){
+ m_error.code= 4307;
+ return 0;
+ }
+
+ req->senderRef = m_reference;
+ req->senderData = 0;
+ req->requestType =
+ GetTabInfoReq::RequestByName | GetTabInfoReq::LongSignalConf;
+ req->tableNameLen = strLen;
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_GET_TABINFOREQ;
+ // tSignal.theLength = GetTabInfoReq::HeaderLength + ((strLen + 3) / 4);
+ tSignal.theLength = GetTabInfoReq::SignalLength;
+ LinearSectionPtr ptr[1];
+ ptr[0].p = (Uint32*)name;
+ ptr[0].sz = strLen;
+
+ return getTable(&tSignal, ptr, 1, fullyQualifiedNames);
+}
+
+NdbTableImpl *
+NdbDictInterface::getTable(class NdbApiSignal * signal,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections, bool fullyQualifiedNames)
+{
+ //GetTabInfoReq * const req = CAST_PTR(GetTabInfoReq, signal->getDataPtrSend());
+ int errCodes[] = {GetTabInfoRef::Busy };
+
+ int r = dictSignal(signal,ptr,noOfSections,
+ 0/*do not use masternode id*/,
+ 100,
+ WAIT_GET_TAB_INFO_REQ,
+ WAITFOR_RESPONSE_TIMEOUT,
+ errCodes, 1);
+ if (r) return 0;
+
+ NdbTableImpl * rt = 0;
+ m_error.code= parseTableInfo(&rt,
+ (Uint32*)m_buffer.get_data(),
+ m_buffer.length() / 4, fullyQualifiedNames);
+ rt->buildColumnHash();
+ return rt;
+}
+
+void
+NdbDictInterface::execGET_TABINFO_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const GetTabInfoConf* conf = CAST_CONSTPTR(GetTabInfoConf, signal->getDataPtr());
+ if(signal->isFirstFragment()){
+ m_fragmentId = signal->getFragmentId();
+ m_buffer.grow(4 * conf->totalLen);
+ } else {
+ if(m_fragmentId != signal->getFragmentId()){
+ abort();
+ }
+ }
+
+ const Uint32 i = GetTabInfoConf::DICT_TAB_INFO;
+ m_buffer.append(ptr[i].p, 4 * ptr[i].sz);
+
+ if(!signal->isLastFragment()){
+ return;
+ }
+
+ m_waiter.signal(NO_WAIT);
+}
+
+void
+NdbDictInterface::execGET_TABINFO_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const GetTabInfoRef* ref = CAST_CONSTPTR(GetTabInfoRef, signal->getDataPtr());
+
+ m_error.code= ref->errorCode;
+ m_waiter.signal(NO_WAIT);
+}
+
+/*****************************************************************
+ * Pack/Unpack tables
+ */
+struct ApiKernelMapping {
+ Int32 kernelConstant;
+ Int32 apiConstant;
+};
+
+Uint32
+getApiConstant(Int32 kernelConstant, const ApiKernelMapping map[], Uint32 def)
+{
+ int i = 0;
+ while(map[i].kernelConstant != kernelConstant){
+ if(map[i].kernelConstant == -1 &&
+ map[i].apiConstant == -1){
+ return def;
+ }
+ i++;
+ }
+ return map[i].apiConstant;
+}
+
+Uint32
+getKernelConstant(Int32 apiConstant, const ApiKernelMapping map[], Uint32 def)
+{
+ int i = 0;
+ while(map[i].apiConstant != apiConstant){
+ if(map[i].kernelConstant == -1 &&
+ map[i].apiConstant == -1){
+ return def;
+ }
+ i++;
+ }
+ return map[i].kernelConstant;
+}
+
+static const
+ApiKernelMapping
+fragmentTypeMapping[] = {
+ { DictTabInfo::AllNodesSmallTable, NdbDictionary::Object::FragAllSmall },
+ { DictTabInfo::AllNodesMediumTable, NdbDictionary::Object::FragAllMedium },
+ { DictTabInfo::AllNodesLargeTable, NdbDictionary::Object::FragAllLarge },
+ { DictTabInfo::SingleFragment, NdbDictionary::Object::FragSingle },
+ { -1, -1 }
+};
+
+static const
+ApiKernelMapping
+objectTypeMapping[] = {
+ { DictTabInfo::SystemTable, NdbDictionary::Object::SystemTable },
+ { DictTabInfo::UserTable, NdbDictionary::Object::UserTable },
+ { DictTabInfo::UniqueHashIndex, NdbDictionary::Object::UniqueHashIndex },
+ { DictTabInfo::OrderedIndex, NdbDictionary::Object::OrderedIndex },
+ { DictTabInfo::HashIndexTrigger, NdbDictionary::Object::HashIndexTrigger },
+ { DictTabInfo::IndexTrigger, NdbDictionary::Object::IndexTrigger },
+ { DictTabInfo::SubscriptionTrigger,NdbDictionary::Object::SubscriptionTrigger },
+ { DictTabInfo::ReadOnlyConstraint ,NdbDictionary::Object::ReadOnlyConstraint },
+ { -1, -1 }
+};
+
+static const
+ApiKernelMapping
+objectStateMapping[] = {
+ { DictTabInfo::StateOffline, NdbDictionary::Object::StateOffline },
+ { DictTabInfo::StateBuilding, NdbDictionary::Object::StateBuilding },
+ { DictTabInfo::StateDropping, NdbDictionary::Object::StateDropping },
+ { DictTabInfo::StateOnline, NdbDictionary::Object::StateOnline },
+ { DictTabInfo::StateBroken, NdbDictionary::Object::StateBroken },
+ { -1, -1 }
+};
+
+static const
+ApiKernelMapping
+objectStoreMapping[] = {
+ { DictTabInfo::StoreTemporary, NdbDictionary::Object::StoreTemporary },
+ { DictTabInfo::StorePermanent, NdbDictionary::Object::StorePermanent },
+ { -1, -1 }
+};
+
+static const
+ApiKernelMapping
+indexTypeMapping[] = {
+ { DictTabInfo::UniqueHashIndex, NdbDictionary::Index::UniqueHashIndex },
+ { DictTabInfo::OrderedIndex, NdbDictionary::Index::OrderedIndex },
+ { -1, -1 }
+};
+
+int
+NdbDictInterface::parseTableInfo(NdbTableImpl ** ret,
+ const Uint32 * data, Uint32 len,
+ bool fullyQualifiedNames)
+{
+ DBUG_ENTER("NdbDictInterface::parseTableInfo");
+
+ SimplePropertiesLinearReader it(data, len);
+ DictTabInfo::Table tableDesc; tableDesc.init();
+ SimpleProperties::UnpackStatus s;
+ s = SimpleProperties::unpack(it, &tableDesc,
+ DictTabInfo::TableMapping,
+ DictTabInfo::TableMappingSize,
+ true, true);
+
+ if(s != SimpleProperties::Break){
+ DBUG_RETURN(703);
+ }
+ const char * internalName = tableDesc.TableName;
+ const char * externalName = Ndb::externalizeTableName(internalName, fullyQualifiedNames);
+
+ NdbTableImpl * impl = new NdbTableImpl();
+ impl->m_tableId = tableDesc.TableId;
+ impl->m_version = tableDesc.TableVersion;
+ impl->m_status = NdbDictionary::Object::Retrieved;
+ impl->m_internalName.assign(internalName);
+ impl->m_externalName.assign(externalName);
+
+ impl->m_frm.assign(tableDesc.FrmData, tableDesc.FrmLen);
+
+ impl->m_fragmentType = (NdbDictionary::Object::FragmentType)
+ getApiConstant(tableDesc.FragmentType,
+ fragmentTypeMapping,
+ (Uint32)NdbDictionary::Object::FragUndefined);
+
+ impl->m_logging = tableDesc.TableLoggedFlag;
+ impl->m_kvalue = tableDesc.TableKValue;
+ impl->m_minLoadFactor = tableDesc.MinLoadFactor;
+ impl->m_maxLoadFactor = tableDesc.MaxLoadFactor;
+
+ impl->m_indexType = (NdbDictionary::Index::Type)
+ getApiConstant(tableDesc.TableType,
+ indexTypeMapping,
+ NdbDictionary::Index::Undefined);
+
+ if(impl->m_indexType == NdbDictionary::Index::Undefined){
+ } else {
+ const char * externalPrimary =
+ Ndb::externalizeTableName(tableDesc.PrimaryTable, fullyQualifiedNames);
+ impl->m_primaryTable.assign(externalPrimary);
+ }
+
+ Uint32 keyInfoPos = 0;
+ Uint32 keyCount = 0;
+ Uint32 blobCount = 0;
+ Uint32 distKeys = 0;
+
+ Uint32 i;
+ for(i = 0; i < tableDesc.NoOfAttributes; i++) {
+ DictTabInfo::Attribute attrDesc; attrDesc.init();
+ s = SimpleProperties::unpack(it,
+ &attrDesc,
+ DictTabInfo::AttributeMapping,
+ DictTabInfo::AttributeMappingSize,
+ true, true);
+ if(s != SimpleProperties::Break){
+ delete impl;
+ DBUG_RETURN(703);
+ }
+
+ NdbColumnImpl * col = new NdbColumnImpl();
+ col->m_attrId = attrDesc.AttributeId;
+ col->setName(attrDesc.AttributeName);
+
+ // check type and compute attribute size and array size
+ if (! attrDesc.translateExtType()) {
+ delete impl;
+ DBUG_RETURN(703);
+ }
+ col->m_type = (NdbDictionary::Column::Type)attrDesc.AttributeExtType;
+ col->m_precision = (attrDesc.AttributeExtPrecision & 0xFFFF);
+ col->m_scale = attrDesc.AttributeExtScale;
+ col->m_length = attrDesc.AttributeExtLength;
+ // charset in upper half of precision
+ unsigned cs_number = (attrDesc.AttributeExtPrecision >> 16);
+ // charset is defined exactly for char types
+ if (col->getCharType() != (cs_number != 0)) {
+ delete impl;
+ DBUG_RETURN(703);
+ }
+ if (col->getCharType()) {
+ col->m_cs = get_charset(cs_number, MYF(0));
+ if (col->m_cs == NULL) {
+ delete impl;
+ DBUG_RETURN(743);
+ }
+ }
+ col->m_attrSize = (1 << attrDesc.AttributeSize) / 8;
+ col->m_arraySize = attrDesc.AttributeArraySize;
+ if(attrDesc.AttributeSize == 0)
+ {
+ col->m_attrSize = 4;
+ col->m_arraySize = (attrDesc.AttributeArraySize + 31) >> 5;
+ }
+
+ col->m_pk = attrDesc.AttributeKeyFlag;
+ col->m_distributionKey = attrDesc.AttributeDKey;
+ col->m_nullable = attrDesc.AttributeNullableFlag;
+ col->m_autoIncrement = (attrDesc.AttributeAutoIncrement ? true : false);
+ col->m_autoIncrementInitialValue = ~0;
+ col->m_defaultValue.assign(attrDesc.AttributeDefaultValue);
+
+ if(attrDesc.AttributeKeyFlag){
+ col->m_keyInfoPos = keyInfoPos + 1;
+ keyInfoPos += ((col->m_attrSize * col->m_arraySize + 3) / 4);
+ keyCount++;
+
+ if(attrDesc.AttributeDKey)
+ distKeys++;
+ } else {
+ col->m_keyInfoPos = 0;
+ }
+ if (col->getBlobType())
+ blobCount++;
+ NdbColumnImpl * null = 0;
+ impl->m_columns.fill(attrDesc.AttributeId, null);
+ if(impl->m_columns[attrDesc.AttributeId] != 0){
+ delete col;
+ delete impl;
+ DBUG_RETURN(703);
+ }
+ impl->m_columns[attrDesc.AttributeId] = col;
+ it.next();
+ }
+
+ impl->m_noOfKeys = keyCount;
+ impl->m_keyLenInWords = keyInfoPos;
+ impl->m_noOfBlobs = blobCount;
+ impl->m_noOfDistributionKeys = distKeys;
+
+ if(tableDesc.FragmentDataLen > 0)
+ {
+ Uint32 replicaCount = tableDesc.FragmentData[0];
+ Uint32 fragCount = tableDesc.FragmentData[1];
+
+ impl->m_replicaCount = replicaCount;
+ impl->m_fragmentCount = fragCount;
+
+ for(i = 0; i<(fragCount*replicaCount); i++)
+ {
+ impl->m_fragments.push_back(tableDesc.FragmentData[i+2]);
+ }
+
+ Uint32 topBit = (1 << 31);
+ for(; topBit && !(fragCount & topBit); ){
+ topBit >>= 1;
+ }
+ impl->m_hashValueMask = topBit - 1;
+ impl->m_hashpointerValue = fragCount - (impl->m_hashValueMask + 1);
+ }
+ else
+ {
+ impl->m_fragmentCount = tableDesc.FragmentCount;
+ impl->m_replicaCount = 0;
+ impl->m_hashValueMask = 0;
+ impl->m_hashpointerValue = 0;
+ }
+
+ if(distKeys == 0)
+ {
+ for(i = 0; i < tableDesc.NoOfAttributes; i++)
+ {
+ if(impl->m_columns[i]->getPrimaryKey())
+ impl->m_columns[i]->m_distributionKey = true;
+ }
+ }
+
+ * ret = impl;
+
+ DBUG_RETURN(0);
+}
+
+/*****************************************************************
+ * Create table and alter table
+ */
+int
+NdbDictionaryImpl::createTable(NdbTableImpl &t)
+{
+ if (m_receiver.createTable(m_ndb, t) != 0)
+ return -1;
+ if (t.m_noOfBlobs == 0)
+ return 0;
+ // update table def from DICT
+ Ndb_local_table_info *info=
+ get_local_table_info(t.m_internalName.c_str(),false);
+ if (info == NULL) {
+ m_error.code= 709;
+ return -1;
+ }
+ if (createBlobTables(*(info->m_table_impl)) != 0) {
+ int save_code = m_error.code;
+ (void)dropTable(t);
+ m_error.code= save_code;
+ return -1;
+ }
+ return 0;
+}
+
+int
+NdbDictionaryImpl::createBlobTables(NdbTableImpl &t)
+{
+ for (unsigned i = 0; i < t.m_columns.size(); i++) {
+ NdbColumnImpl & c = *t.m_columns[i];
+ if (! c.getBlobType() || c.getPartSize() == 0)
+ continue;
+ NdbTableImpl bt;
+ NdbBlob::getBlobTable(bt, &t, &c);
+ if (createTable(bt) != 0)
+ return -1;
+ // Save BLOB table handle
+ Ndb_local_table_info *info=
+ get_local_table_info(bt.m_internalName.c_str(),false);
+ if (info == 0) {
+ return -1;
+ }
+ c.m_blobTable = info->m_table_impl;
+ }
+
+ return 0;
+}
+
+int
+NdbDictionaryImpl::addBlobTables(NdbTableImpl &t)
+{
+ unsigned n= t.m_noOfBlobs;
+ // optimized for blob column being the last one
+ // and not looking for more than one if not neccessary
+ for (unsigned i = t.m_columns.size(); i > 0 && n > 0;) {
+ i--;
+ NdbColumnImpl & c = *t.m_columns[i];
+ if (! c.getBlobType() || c.getPartSize() == 0)
+ continue;
+ n--;
+ char btname[NdbBlobImpl::BlobTableNameSize];
+ NdbBlob::getBlobTableName(btname, &t, &c);
+ // Save BLOB table handle
+ NdbTableImpl * cachedBlobTable = getTable(btname);
+ if (cachedBlobTable == 0) {
+ return -1;
+ }
+ c.m_blobTable = cachedBlobTable;
+ }
+
+ return 0;
+}
+
+int
+NdbDictInterface::createTable(Ndb & ndb,
+ NdbTableImpl & impl)
+{
+ return createOrAlterTable(ndb, impl, false);
+}
+
+int NdbDictionaryImpl::alterTable(NdbTableImpl &impl)
+{
+ BaseString internalName = impl.m_internalName;
+ const char * originalInternalName = internalName.c_str();
+ BaseString externalName = impl.m_externalName;
+
+ DBUG_ENTER("NdbDictionaryImpl::alterTable");
+ if(!get_local_table_info(originalInternalName, false)){
+ m_error.code= 709;
+ DBUG_RETURN(-1);
+ }
+ // Alter the table
+ int ret = m_receiver.alterTable(m_ndb, impl);
+ if(ret == 0){
+ // Remove cached information and let it be refreshed at next access
+ if (m_localHash.get(originalInternalName) != NULL) {
+ m_localHash.drop(originalInternalName);
+ m_globalHash->lock();
+ NdbTableImpl * cachedImpl = m_globalHash->get(originalInternalName);
+ // If in local cache it must be in global
+ if (!cachedImpl)
+ abort();
+ m_globalHash->drop(cachedImpl);
+ m_globalHash->unlock();
+ }
+ }
+ DBUG_RETURN(ret);
+}
+
+int
+NdbDictInterface::alterTable(Ndb & ndb,
+ NdbTableImpl & impl)
+{
+ return createOrAlterTable(ndb, impl, true);
+}
+
+int
+NdbDictInterface::createOrAlterTable(Ndb & ndb,
+ NdbTableImpl & impl,
+ bool alter)
+{
+ DBUG_ENTER("NdbDictInterface::createOrAlterTable");
+ unsigned i;
+ if((unsigned)impl.getNoOfPrimaryKeys() > NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY){
+ m_error.code= 4317;
+ DBUG_RETURN(-1);
+ }
+ unsigned sz = impl.m_columns.size();
+ if (sz > NDB_MAX_ATTRIBUTES_IN_TABLE){
+ m_error.code= 4318;
+ DBUG_RETURN(-1);
+ }
+
+ impl.copyNewProperties();
+ //validate();
+ //aggregate();
+
+ const char * internalName =
+ ndb.internalizeTableName(impl.m_externalName.c_str());
+ impl.m_internalName.assign(internalName);
+ UtilBufferWriter w(m_buffer);
+ DictTabInfo::Table tmpTab; tmpTab.init();
+ BaseString::snprintf(tmpTab.TableName,
+ sizeof(tmpTab.TableName),
+ internalName);
+
+ bool haveAutoIncrement = false;
+ Uint64 autoIncrementValue = 0;
+ for(i = 0; i<sz; i++){
+ const NdbColumnImpl * col = impl.m_columns[i];
+ if(col == 0)
+ continue;
+ if (col->m_autoIncrement) {
+ if (haveAutoIncrement) {
+ m_error.code= 4335;
+ DBUG_RETURN(-1);
+ }
+ haveAutoIncrement = true;
+ autoIncrementValue = col->m_autoIncrementInitialValue;
+ }
+ }
+
+ // Check max length of frm data
+ if (impl.m_frm.length() > MAX_FRM_DATA_SIZE){
+ m_error.code= 1229;
+ DBUG_RETURN(-1);
+ }
+ tmpTab.FrmLen = impl.m_frm.length();
+ memcpy(tmpTab.FrmData, impl.m_frm.get_data(), impl.m_frm.length());
+
+ tmpTab.TableLoggedFlag = impl.m_logging;
+ tmpTab.TableKValue = impl.m_kvalue;
+ tmpTab.MinLoadFactor = impl.m_minLoadFactor;
+ tmpTab.MaxLoadFactor = impl.m_maxLoadFactor;
+ tmpTab.TableType = DictTabInfo::UserTable;
+ tmpTab.NoOfAttributes = sz;
+
+ tmpTab.FragmentType = getKernelConstant(impl.m_fragmentType,
+ fragmentTypeMapping,
+ DictTabInfo::AllNodesSmallTable);
+ tmpTab.TableVersion = rand();
+
+ SimpleProperties::UnpackStatus s;
+ s = SimpleProperties::pack(w,
+ &tmpTab,
+ DictTabInfo::TableMapping,
+ DictTabInfo::TableMappingSize, true);
+
+ if(s != SimpleProperties::Eof){
+ abort();
+ }
+
+ int distKeys= impl.m_noOfDistributionKeys;
+ for(i = 0; i<sz; i++){
+ const NdbColumnImpl * col = impl.m_columns[i];
+ if(col == 0)
+ continue;
+
+ DictTabInfo::Attribute tmpAttr; tmpAttr.init();
+ BaseString::snprintf(tmpAttr.AttributeName, sizeof(tmpAttr.AttributeName),
+ col->m_name.c_str());
+ tmpAttr.AttributeId = i;
+ tmpAttr.AttributeKeyFlag = col->m_pk;
+ tmpAttr.AttributeNullableFlag = col->m_nullable;
+ tmpAttr.AttributeDKey = col->m_distributionKey;
+
+ tmpAttr.AttributeExtType = (Uint32)col->m_type;
+ tmpAttr.AttributeExtPrecision = ((unsigned)col->m_precision & 0xFFFF);
+ tmpAttr.AttributeExtScale = col->m_scale;
+ tmpAttr.AttributeExtLength = col->m_length;
+
+ // check type and compute attribute size and array size
+ if (! tmpAttr.translateExtType()) {
+ m_error.code= 703;
+ DBUG_RETURN(-1);
+ }
+ // charset is defined exactly for char types
+ if (col->getCharType() != (col->m_cs != NULL)) {
+ m_error.code= 703;
+ DBUG_RETURN(-1);
+ }
+ // primary key type check
+ if (col->m_pk && ! NdbSqlUtil::usable_in_pk(col->m_type, col->m_cs)) {
+ m_error.code= (col->m_cs != 0 ? 743 : 739);
+ DBUG_RETURN(-1);
+ }
+ // distribution key not supported for Char attribute
+ if (distKeys && col->m_distributionKey && col->m_cs != NULL) {
+ m_error.code= 745;
+ DBUG_RETURN(-1);
+ }
+ // charset in upper half of precision
+ if (col->getCharType()) {
+ tmpAttr.AttributeExtPrecision |= (col->m_cs->number << 16);
+ }
+
+ tmpAttr.AttributeAutoIncrement = col->m_autoIncrement;
+ BaseString::snprintf(tmpAttr.AttributeDefaultValue,
+ sizeof(tmpAttr.AttributeDefaultValue),
+ col->m_defaultValue.c_str());
+ s = SimpleProperties::pack(w,
+ &tmpAttr,
+ DictTabInfo::AttributeMapping,
+ DictTabInfo::AttributeMappingSize, true);
+ w.add(DictTabInfo::AttributeEnd, 1);
+ }
+
+ NdbApiSignal tSignal(m_reference);
+ tSignal.theReceiversBlockNumber = DBDICT;
+
+ LinearSectionPtr ptr[3];
+ ptr[0].p = (Uint32*)m_buffer.get_data();
+ ptr[0].sz = m_buffer.length() / 4;
+ int ret;
+ if (alter)
+ {
+ AlterTableReq * const req =
+ CAST_PTR(AlterTableReq, tSignal.getDataPtrSend());
+
+ req->senderRef = m_reference;
+ req->senderData = 0;
+ req->changeMask = impl.m_changeMask;
+ req->tableId = impl.m_tableId;
+ req->tableVersion = impl.m_version;;
+ tSignal.theVerId_signalNumber = GSN_ALTER_TABLE_REQ;
+ tSignal.theLength = AlterTableReq::SignalLength;
+ ret= alterTable(&tSignal, ptr);
+ }
+ else
+ {
+ CreateTableReq * const req =
+ CAST_PTR(CreateTableReq, tSignal.getDataPtrSend());
+
+ req->senderRef = m_reference;
+ req->senderData = 0;
+ tSignal.theVerId_signalNumber = GSN_CREATE_TABLE_REQ;
+ tSignal.theLength = CreateTableReq::SignalLength;
+ ret= createTable(&tSignal, ptr);
+
+ if (ret)
+ DBUG_RETURN(ret);
+
+ if (haveAutoIncrement) {
+ if (!ndb.setAutoIncrementValue(impl.m_externalName.c_str(),
+ autoIncrementValue)) {
+ if (ndb.theError.code == 0) {
+ m_error.code= 4336;
+ ndb.theError = m_error;
+ } else
+ m_error= ndb.theError;
+ ret = -1; // errorcode set in initialize_autoincrement
+ }
+ }
+ }
+ DBUG_RETURN(ret);
+}
+
+int
+NdbDictInterface::createTable(NdbApiSignal* signal, LinearSectionPtr ptr[3])
+{
+#if DEBUG_PRINT
+ ndbout_c("BufferLen = %d", ptr[0].sz);
+ SimplePropertiesLinearReader r(ptr[0].p, ptr[0].sz);
+ r.printAll(ndbout);
+#endif
+ const int noErrCodes = 2;
+ int errCodes[noErrCodes] =
+ {CreateTableRef::Busy,
+ CreateTableRef::NotMaster};
+ return dictSignal(signal,ptr,1,
+ 1/*use masternode id*/,
+ 100,
+ WAIT_CREATE_INDX_REQ,
+ WAITFOR_RESPONSE_TIMEOUT,
+ errCodes,noErrCodes);
+}
+
+
+void
+NdbDictInterface::execCREATE_TABLE_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+#if 0
+ const CreateTableConf* const conf=
+ CAST_CONSTPTR(CreateTableConf, signal->getDataPtr());
+ Uint32 tableId= conf->tableId;
+ Uint32 tableVersion= conf->tableVersion;
+#endif
+ m_waiter.signal(NO_WAIT);
+}
+
+void
+NdbDictInterface::execCREATE_TABLE_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const CreateTableRef* const ref=
+ CAST_CONSTPTR(CreateTableRef, signal->getDataPtr());
+ m_error.code= ref->errorCode;
+ m_masterNodeId = ref->masterNodeId;
+ m_waiter.signal(NO_WAIT);
+}
+
+int
+NdbDictInterface::alterTable(NdbApiSignal* signal, LinearSectionPtr ptr[3])
+{
+#if DEBUG_PRINT
+ ndbout_c("BufferLen = %d", ptr[0].sz);
+ SimplePropertiesLinearReader r(ptr[0].p, ptr[0].sz);
+ r.printAll(ndbout);
+#endif
+ const int noErrCodes = 2;
+ int errCodes[noErrCodes] =
+ {AlterTableRef::NotMaster,
+ AlterTableRef::Busy};
+ int r = dictSignal(signal,ptr,1,
+ 1/*use masternode id*/,
+ 100,WAIT_ALTER_TAB_REQ,
+ WAITFOR_RESPONSE_TIMEOUT,
+ errCodes, noErrCodes);
+ if(m_error.code == AlterTableRef::InvalidTableVersion) {
+ // Clear caches and try again
+ return INCOMPATIBLE_VERSION;
+ }
+
+ return r;
+}
+
+void
+NdbDictInterface::execALTER_TABLE_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ //AlterTableConf* const conf = CAST_CONSTPTR(AlterTableConf, signal->getDataPtr());
+ m_waiter.signal(NO_WAIT);
+}
+
+void
+NdbDictInterface::execALTER_TABLE_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const AlterTableRef * const ref =
+ CAST_CONSTPTR(AlterTableRef, signal->getDataPtr());
+ m_error.code= ref->errorCode;
+ m_masterNodeId = ref->masterNodeId;
+ m_waiter.signal(NO_WAIT);
+}
+
+/*****************************************************************
+ * Drop table
+ */
+int
+NdbDictionaryImpl::dropTable(const char * name)
+{
+ DBUG_ENTER("NdbDictionaryImpl::dropTable");
+ DBUG_PRINT("enter",("name: %s", name));
+ NdbTableImpl * tab = getTable(name);
+ if(tab == 0){
+ DBUG_RETURN(-1);
+ }
+ int ret = dropTable(* tab);
+ // If table stored in cache is incompatible with the one in the kernel
+ // we must clear the cache and try again
+ if (ret == INCOMPATIBLE_VERSION) {
+ const char * internalTableName = m_ndb.internalizeTableName(name);
+
+ DBUG_PRINT("info",("INCOMPATIBLE_VERSION internal_name: %s", internalTableName));
+ m_localHash.drop(internalTableName);
+
+ m_globalHash->lock();
+ m_globalHash->drop(tab);
+ m_globalHash->unlock();
+ DBUG_RETURN(dropTable(name));
+ }
+
+ DBUG_RETURN(ret);
+}
+
+int
+NdbDictionaryImpl::dropTable(NdbTableImpl & impl)
+{
+ int res;
+ const char * name = impl.getName();
+ if(impl.m_status == NdbDictionary::Object::New){
+ return dropTable(name);
+ }
+
+ if (impl.m_indexType != NdbDictionary::Index::Undefined) {
+ m_receiver.m_error.code= 1228;
+ return -1;
+ }
+
+ List list;
+ if ((res = listIndexes(list, impl.m_tableId)) == -1){
+ return -1;
+ }
+ for (unsigned i = 0; i < list.count; i++) {
+ const List::Element& element = list.elements[i];
+ if ((res = dropIndex(element.name, name)) == -1)
+ {
+ return -1;
+ }
+ }
+
+ if (impl.m_noOfBlobs != 0) {
+ if (dropBlobTables(impl) != 0){
+ return -1;
+ }
+ }
+
+ int ret = m_receiver.dropTable(impl);
+ if(ret == 0 || m_error.code == 709){
+ const char * internalTableName = impl.m_internalName.c_str();
+
+ m_localHash.drop(internalTableName);
+
+ m_globalHash->lock();
+ m_globalHash->drop(&impl);
+ m_globalHash->unlock();
+
+ return 0;
+ }
+
+ return ret;
+}
+
+int
+NdbDictionaryImpl::dropBlobTables(NdbTableImpl & t)
+{
+ DBUG_ENTER("NdbDictionaryImpl::dropBlobTables");
+ for (unsigned i = 0; i < t.m_columns.size(); i++) {
+ NdbColumnImpl & c = *t.m_columns[i];
+ if (! c.getBlobType() || c.getPartSize() == 0)
+ continue;
+ char btname[NdbBlobImpl::BlobTableNameSize];
+ NdbBlob::getBlobTableName(btname, &t, &c);
+ if (dropTable(btname) != 0) {
+ if (m_error.code != 709){
+ DBUG_PRINT("exit",("error %u - exiting",m_error.code));
+ DBUG_RETURN(-1);
+ }
+ DBUG_PRINT("info",("error %u - continuing",m_error.code));
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+int
+NdbDictInterface::dropTable(const NdbTableImpl & impl)
+{
+ NdbApiSignal tSignal(m_reference);
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_DROP_TABLE_REQ;
+ tSignal.theLength = DropTableReq::SignalLength;
+
+ DropTableReq * const req = CAST_PTR(DropTableReq, tSignal.getDataPtrSend());
+ req->senderRef = m_reference;
+ req->senderData = 0;
+ req->tableId = impl.m_tableId;
+ req->tableVersion = impl.m_version;
+
+ return dropTable(&tSignal, 0);
+}
+
+int
+NdbDictInterface::dropTable(NdbApiSignal* signal, LinearSectionPtr ptr[3])
+{
+ const int noErrCodes = 3;
+ int errCodes[noErrCodes] =
+ {DropTableRef::NoDropTableRecordAvailable,
+ DropTableRef::NotMaster,
+ DropTableRef::Busy};
+ int r = dictSignal(signal,NULL,0,
+ 1/*use masternode id*/,
+ 100,WAIT_DROP_TAB_REQ,
+ WAITFOR_RESPONSE_TIMEOUT,
+ errCodes, noErrCodes);
+ if(m_error.code == DropTableRef::InvalidTableVersion) {
+ // Clear caches and try again
+ return INCOMPATIBLE_VERSION;
+ }
+ return r;
+}
+
+void
+NdbDictInterface::execDROP_TABLE_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execDROP_TABLE_CONF");
+ //DropTableConf* const conf = CAST_CONSTPTR(DropTableConf, signal->getDataPtr());
+
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execDROP_TABLE_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execDROP_TABLE_REF");
+ const DropTableRef* const ref = CAST_CONSTPTR(DropTableRef, signal->getDataPtr());
+ m_error.code= ref->errorCode;
+ m_masterNodeId = ref->masterNodeId;
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+int
+NdbDictionaryImpl::invalidateObject(NdbTableImpl & impl)
+{
+ const char * internalTableName = impl.m_internalName.c_str();
+
+ m_localHash.drop(internalTableName);
+ m_globalHash->lock();
+ m_globalHash->drop(&impl);
+ m_globalHash->unlock();
+ return 0;
+}
+
+int
+NdbDictionaryImpl::removeCachedObject(NdbTableImpl & impl)
+{
+ const char * internalTableName = impl.m_internalName.c_str();
+
+ m_localHash.drop(internalTableName);
+ m_globalHash->lock();
+ m_globalHash->release(&impl);
+ m_globalHash->unlock();
+ return 0;
+}
+
+/*****************************************************************
+ * Get index info
+ */
+NdbIndexImpl*
+NdbDictionaryImpl::getIndexImpl(const char * externalName,
+ const char * internalName)
+{
+ Ndb_local_table_info * info = get_local_table_info(internalName,
+ false);
+ if(info == 0){
+ m_error.code = 4243;
+ return 0;
+ }
+ NdbTableImpl * tab = info->m_table_impl;
+
+ if(tab->m_indexType == NdbDictionary::Index::Undefined){
+ // Not an index
+ m_error.code = 4243;
+ return 0;
+ }
+
+ NdbTableImpl* prim = getTable(tab->m_primaryTable.c_str());
+ if(prim == 0){
+ m_error.code = 4243;
+ return 0;
+ }
+
+ /**
+ * Create index impl
+ */
+ NdbIndexImpl* idx;
+ if(NdbDictInterface::create_index_obj_from_table(&idx, tab, prim) == 0){
+ idx->m_table = tab;
+ idx->m_externalName.assign(externalName);
+ idx->m_internalName.assign(internalName);
+ // TODO Assign idx to tab->m_index
+ // Don't do it right now since assign can't asign a table with index
+ // tab->m_index = idx;
+ return idx;
+ }
+ return 0;
+}
+
+int
+NdbDictInterface::create_index_obj_from_table(NdbIndexImpl** dst,
+ NdbTableImpl* tab,
+ const NdbTableImpl* prim){
+ NdbIndexImpl *idx = new NdbIndexImpl();
+ idx->m_version = tab->m_version;
+ idx->m_status = tab->m_status;
+ idx->m_indexId = tab->m_tableId;
+ idx->m_externalName.assign(tab->getName());
+ idx->m_tableName.assign(prim->m_externalName);
+ NdbDictionary::Index::Type type = idx->m_type = tab->m_indexType;
+ idx->m_logging = tab->m_logging;
+ // skip last attribute (NDB$PK or NDB$TNODE)
+
+ const Uint32 distKeys = prim->m_noOfDistributionKeys;
+ Uint32 keyCount = (distKeys ? distKeys : prim->m_noOfKeys);
+
+ unsigned i;
+ for(i = 0; i+1<tab->m_columns.size(); i++){
+ NdbColumnImpl* org = tab->m_columns[i];
+
+ NdbColumnImpl* col = new NdbColumnImpl;
+ // Copy column definition
+ *col = * org;
+ idx->m_columns.push_back(col);
+
+ /**
+ * reverse map
+ */
+ const NdbColumnImpl* primCol = prim->getColumn(col->getName());
+ int key_id = primCol->getColumnNo();
+ int fill = -1;
+ idx->m_key_ids.fill(key_id, fill);
+ idx->m_key_ids[key_id] = i;
+ col->m_keyInfoPos = key_id;
+
+ if(type == NdbDictionary::Index::OrderedIndex &&
+ (primCol->m_distributionKey ||
+ (distKeys == 0 && primCol->getPrimaryKey())))
+ {
+ keyCount--;
+ org->m_distributionKey = 1;
+ }
+ }
+
+ if(keyCount == 0)
+ {
+ tab->m_noOfDistributionKeys = (distKeys ? distKeys : prim->m_noOfKeys);
+ }
+ else
+ {
+ for(i = 0; i+1<tab->m_columns.size(); i++)
+ tab->m_columns[i]->m_distributionKey = 0;
+ }
+
+ * dst = idx;
+ return 0;
+}
+
+/*****************************************************************
+ * Create index
+ */
+int
+NdbDictionaryImpl::createIndex(NdbIndexImpl &ix)
+{
+ NdbTableImpl* tab = getTable(ix.getTable());
+ if(tab == 0){
+ m_error.code = 4249;
+ return -1;
+ }
+
+ return m_receiver.createIndex(m_ndb, ix, * tab);
+}
+
+int
+NdbDictInterface::createIndex(Ndb & ndb,
+ NdbIndexImpl & impl,
+ const NdbTableImpl & table)
+{
+ //validate();
+ //aggregate();
+ unsigned i;
+ UtilBufferWriter w(m_buffer);
+ const size_t len = strlen(impl.m_externalName.c_str()) + 1;
+ if(len > MAX_TAB_NAME_SIZE) {
+ m_error.code = 4241;
+ return -1;
+ }
+ const char * internalName =
+ ndb.internalizeIndexName(&table, impl.getName());
+
+ impl.m_internalName.assign(internalName);
+
+ w.add(DictTabInfo::TableName, internalName);
+ w.add(DictTabInfo::TableLoggedFlag, impl.m_logging);
+
+ NdbApiSignal tSignal(m_reference);
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_CREATE_INDX_REQ;
+ tSignal.theLength = CreateIndxReq::SignalLength;
+
+ CreateIndxReq * const req = CAST_PTR(CreateIndxReq, tSignal.getDataPtrSend());
+
+ req->setUserRef(m_reference);
+ req->setConnectionPtr(0);
+ req->setRequestType(CreateIndxReq::RT_USER);
+
+ Uint32 it = getKernelConstant(impl.m_type,
+ indexTypeMapping,
+ DictTabInfo::UndefTableType);
+
+ if(it == DictTabInfo::UndefTableType){
+ m_error.code = 4250;
+ return -1;
+ }
+ req->setIndexType((DictTabInfo::TableType) it);
+
+ req->setTableId(table.m_tableId);
+ req->setOnline(true);
+ AttributeList attributeList;
+ attributeList.sz = impl.m_columns.size();
+ for(i = 0; i<attributeList.sz; i++){
+ const NdbColumnImpl* col =
+ table.getColumn(impl.m_columns[i]->m_name.c_str());
+ if(col == 0){
+ m_error.code = 4247;
+ return -1;
+ }
+ // Copy column definition
+ *impl.m_columns[i] = *col;
+
+ // index key type check
+ if (it == DictTabInfo::UniqueHashIndex &&
+ ! NdbSqlUtil::usable_in_hash_index(col->m_type, col->m_cs) ||
+ it == DictTabInfo::OrderedIndex &&
+ ! NdbSqlUtil::usable_in_ordered_index(col->m_type, col->m_cs)) {
+ m_error.code = 743;
+ return -1;
+ }
+ attributeList.id[i] = col->m_attrId;
+ }
+ LinearSectionPtr ptr[3];
+ ptr[0].p = (Uint32*)&attributeList;
+ ptr[0].sz = 1 + attributeList.sz;
+ ptr[1].p = (Uint32*)m_buffer.get_data();
+ ptr[1].sz = m_buffer.length() >> 2; //BUG?
+ return createIndex(&tSignal, ptr);
+}
+
+int
+NdbDictInterface::createIndex(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3])
+{
+ const int noErrCodes = 2;
+ int errCodes[noErrCodes] = {CreateIndxRef::Busy, CreateIndxRef::NotMaster};
+ return dictSignal(signal,ptr,2,
+ 1 /*use masternode id*/,
+ 100,
+ WAIT_CREATE_INDX_REQ,
+ -1,
+ errCodes,noErrCodes);
+}
+
+void
+NdbDictInterface::execCREATE_INDX_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ //CreateTableConf* const conf = CAST_CONSTPTR(CreateTableConf, signal->getDataPtr());
+
+ m_waiter.signal(NO_WAIT);
+}
+
+void
+NdbDictInterface::execCREATE_INDX_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const CreateIndxRef* const ref = CAST_CONSTPTR(CreateIndxRef, signal->getDataPtr());
+ m_error.code = ref->getErrorCode();
+ if(m_error.code == ref->NotMaster)
+ m_masterNodeId= ref->masterNodeId;
+ m_waiter.signal(NO_WAIT);
+}
+
+/*****************************************************************
+ * Drop index
+ */
+int
+NdbDictionaryImpl::dropIndex(const char * indexName,
+ const char * tableName)
+{
+ NdbIndexImpl * idx = getIndex(indexName, tableName);
+ if (idx == 0) {
+ m_error.code = 4243;
+ return -1;
+ }
+ int ret = dropIndex(*idx, tableName);
+ // If index stored in cache is incompatible with the one in the kernel
+ // we must clear the cache and try again
+ if (ret == INCOMPATIBLE_VERSION) {
+ const char * internalIndexName = (tableName)
+ ?
+ m_ndb.internalizeIndexName(getTable(tableName), indexName)
+ :
+ m_ndb.internalizeTableName(indexName); // Index is also a table
+
+ m_localHash.drop(internalIndexName);
+
+ m_globalHash->lock();
+ m_globalHash->drop(idx->m_table);
+ m_globalHash->unlock();
+ return dropIndex(indexName, tableName);
+ }
+
+ return ret;
+}
+
+int
+NdbDictionaryImpl::dropIndex(NdbIndexImpl & impl, const char * tableName)
+{
+ const char * indexName = impl.getName();
+ if (tableName || m_ndb.usingFullyQualifiedNames()) {
+ NdbTableImpl * timpl = impl.m_table;
+
+ if (timpl == 0) {
+ m_error.code = 709;
+ return -1;
+ }
+
+ const char * internalIndexName = (tableName)
+ ?
+ m_ndb.internalizeIndexName(getTable(tableName), indexName)
+ :
+ m_ndb.internalizeTableName(indexName); // Index is also a table
+
+ if(impl.m_status == NdbDictionary::Object::New){
+ return dropIndex(indexName, tableName);
+ }
+
+ int ret = m_receiver.dropIndex(impl, *timpl);
+ if(ret == 0){
+ m_localHash.drop(internalIndexName);
+
+ m_globalHash->lock();
+ m_globalHash->drop(impl.m_table);
+ m_globalHash->unlock();
+ }
+ return ret;
+ }
+
+ m_error.code = 4243;
+ return -1;
+}
+
+int
+NdbDictInterface::dropIndex(const NdbIndexImpl & impl,
+ const NdbTableImpl & timpl)
+{
+ NdbApiSignal tSignal(m_reference);
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_DROP_INDX_REQ;
+ tSignal.theLength = DropIndxReq::SignalLength;
+
+ DropIndxReq * const req = CAST_PTR(DropIndxReq, tSignal.getDataPtrSend());
+ req->setUserRef(m_reference);
+ req->setConnectionPtr(0);
+ req->setRequestType(DropIndxReq::RT_USER);
+ req->setTableId(~0); // DICT overwrites
+ req->setIndexId(timpl.m_tableId);
+ req->setIndexVersion(timpl.m_version);
+
+ return dropIndex(&tSignal, 0);
+}
+
+int
+NdbDictInterface::dropIndex(NdbApiSignal* signal, LinearSectionPtr ptr[3])
+{
+ const int noErrCodes = 2;
+ int errCodes[noErrCodes] = {DropIndxRef::Busy, DropIndxRef::NotMaster};
+ int r = dictSignal(signal,NULL,0,
+ 1/*Use masternode id*/,
+ 100,
+ WAIT_DROP_INDX_REQ,
+ WAITFOR_RESPONSE_TIMEOUT,
+ errCodes,noErrCodes);
+ if(m_error.code == DropIndxRef::InvalidIndexVersion) {
+ // Clear caches and try again
+ return INCOMPATIBLE_VERSION;
+ }
+ return r;
+}
+
+void
+NdbDictInterface::execDROP_INDX_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ m_waiter.signal(NO_WAIT);
+}
+
+void
+NdbDictInterface::execDROP_INDX_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const DropIndxRef* const ref = CAST_CONSTPTR(DropIndxRef, signal->getDataPtr());
+ m_error.code = ref->getErrorCode();
+ if(m_error.code == ref->NotMaster)
+ m_masterNodeId= ref->masterNodeId;
+ m_waiter.signal(NO_WAIT);
+}
+
+/*****************************************************************
+ * Create event
+ */
+
+int
+NdbDictionaryImpl::createEvent(NdbEventImpl & evnt)
+{
+ int i;
+ NdbTableImpl* tab = getTable(evnt.getTableName());
+
+ if(tab == 0){
+#ifdef EVENT_DEBUG
+ ndbout_c("NdbDictionaryImpl::createEvent: table not found: %s",
+ evnt.getTableName());
+#endif
+ return -1;
+ }
+
+ evnt.m_tableId = tab->m_tableId;
+ evnt.m_tableImpl = tab;
+#ifdef EVENT_DEBUG
+ ndbout_c("Event on tableId=%d", evnt.m_tableId);
+#endif
+
+ NdbTableImpl &table = *evnt.m_tableImpl;
+
+
+ int attributeList_sz = evnt.m_attrIds.size();
+
+ for (i = 0; i < attributeList_sz; i++) {
+ NdbColumnImpl *col_impl = table.getColumn(evnt.m_attrIds[i]);
+ if (col_impl) {
+ evnt.m_facade->addColumn(*(col_impl->m_facade));
+ } else {
+ ndbout_c("Attr id %u in table %s not found", evnt.m_attrIds[i],
+ evnt.getTableName());
+ m_error.code= 4713;
+ return -1;
+ }
+ }
+
+ evnt.m_attrIds.clear();
+
+ attributeList_sz = evnt.m_columns.size();
+#ifdef EVENT_DEBUG
+ ndbout_c("creating event %s", evnt.m_externalName.c_str());
+ ndbout_c("no of columns %d", evnt.m_columns.size());
+#endif
+ int pk_count = 0;
+ evnt.m_attrListBitmask.clear();
+
+ for(i = 0; i<attributeList_sz; i++){
+ const NdbColumnImpl* col =
+ table.getColumn(evnt.m_columns[i]->m_name.c_str());
+ if(col == 0){
+ m_error.code= 4247;
+ return -1;
+ }
+ // Copy column definition
+ *evnt.m_columns[i] = *col;
+
+ if(col->m_pk){
+ pk_count++;
+ }
+
+ evnt.m_attrListBitmask.set(col->m_attrId);
+ }
+
+ // Sort index attributes according to primary table (using insertion sort)
+ for(i = 1; i < attributeList_sz; i++) {
+ NdbColumnImpl* temp = evnt.m_columns[i];
+ unsigned int j = i;
+ while((j > 0) && (evnt.m_columns[j - 1]->m_attrId > temp->m_attrId)) {
+ evnt.m_columns[j] = evnt.m_columns[j - 1];
+ j--;
+ }
+ evnt.m_columns[j] = temp;
+ }
+ // Check for illegal duplicate attributes
+ for(i = 1; i<attributeList_sz; i++) {
+ if (evnt.m_columns[i-1]->m_attrId == evnt.m_columns[i]->m_attrId) {
+ m_error.code= 4258;
+ return -1;
+ }
+ }
+
+#ifdef EVENT_DEBUG
+ char buf[128] = {0};
+ evnt.m_attrListBitmask.getText(buf);
+ ndbout_c("createEvent: mask = %s", buf);
+#endif
+
+ // NdbDictInterface m_receiver;
+ return m_receiver.createEvent(m_ndb, evnt, 0 /* getFlag unset */);
+}
+
+int
+NdbDictInterface::createEvent(class Ndb & ndb,
+ NdbEventImpl & evnt,
+ int getFlag)
+{
+ NdbApiSignal tSignal(m_reference);
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_CREATE_EVNT_REQ;
+ if (getFlag)
+ tSignal.theLength = CreateEvntReq::SignalLengthGet;
+ else
+ tSignal.theLength = CreateEvntReq::SignalLengthCreate;
+
+ CreateEvntReq * const req = CAST_PTR(CreateEvntReq, tSignal.getDataPtrSend());
+
+ req->setUserRef(m_reference);
+ req->setUserData(0);
+
+ if (getFlag) {
+ // getting event from Dictionary
+ req->setRequestType(CreateEvntReq::RT_USER_GET);
+ } else {
+ // creating event in Dictionary
+ req->setRequestType(CreateEvntReq::RT_USER_CREATE);
+ req->setTableId(evnt.m_tableId);
+ req->setAttrListBitmask(evnt.m_attrListBitmask);
+ req->setEventType(evnt.mi_type);
+ }
+
+ UtilBufferWriter w(m_buffer);
+
+ const size_t len = strlen(evnt.m_externalName.c_str()) + 1;
+ if(len > MAX_TAB_NAME_SIZE) {
+ m_error.code= 4241;
+ return -1;
+ }
+
+ w.add(SimpleProperties::StringValue, evnt.m_externalName.c_str());
+
+ if (getFlag == 0)
+ w.add(SimpleProperties::StringValue,
+ ndb.internalizeTableName(evnt.m_tableName.c_str()));
+
+ LinearSectionPtr ptr[3];
+ ptr[0].p = (Uint32*)m_buffer.get_data();
+ ptr[0].sz = (m_buffer.length()+3) >> 2;
+
+ int ret = createEvent(&tSignal, ptr, 1);
+
+ if (ret) {
+ return ret;
+ }
+
+ char *dataPtr = (char *)m_buffer.get_data();
+ unsigned int lenCreateEvntConf = *((unsigned int *)dataPtr);
+ dataPtr += sizeof(lenCreateEvntConf);
+ CreateEvntConf const * evntConf = (CreateEvntConf *)dataPtr;
+ dataPtr += lenCreateEvntConf;
+
+ // NdbEventImpl *evntImpl = (NdbEventImpl *)evntConf->getUserData();
+
+ if (getFlag) {
+ evnt.m_tableId = evntConf->getTableId();
+ evnt.m_attrListBitmask = evntConf->getAttrListBitmask();
+ evnt.mi_type = evntConf->getEventType();
+ evnt.setTable(dataPtr);
+ } else {
+ if (evnt.m_tableId != evntConf->getTableId() ||
+ //evnt.m_attrListBitmask != evntConf->getAttrListBitmask() ||
+ evnt.mi_type != evntConf->getEventType()) {
+ ndbout_c("ERROR*************");
+ return 1;
+ }
+ }
+
+ evnt.m_eventId = evntConf->getEventId();
+ evnt.m_eventKey = evntConf->getEventKey();
+
+ return ret;
+}
+
+int
+NdbDictInterface::createEvent(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3], int noLSP)
+{
+ const int noErrCodes = 1;
+ int errCodes[noErrCodes] = {CreateEvntRef::Busy};
+ return dictSignal(signal,ptr,noLSP,
+ 1 /*use masternode id*/,
+ 100,
+ WAIT_CREATE_INDX_REQ /*WAIT_CREATE_EVNT_REQ*/,
+ -1,
+ errCodes,noErrCodes, CreateEvntRef::Temporary);
+}
+
+int
+NdbDictionaryImpl::executeSubscribeEvent(NdbEventImpl & ev)
+{
+ // NdbDictInterface m_receiver;
+ return m_receiver.executeSubscribeEvent(m_ndb, ev);
+}
+
+int
+NdbDictInterface::executeSubscribeEvent(class Ndb & ndb,
+ NdbEventImpl & evnt)
+{
+ DBUG_ENTER("NdbDictInterface::executeSubscribeEvent");
+ NdbApiSignal tSignal(m_reference);
+ // tSignal.theReceiversBlockNumber = SUMA;
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_SUB_START_REQ;
+ tSignal.theLength = SubStartReq::SignalLength2;
+
+ SubStartReq * sumaStart = CAST_PTR(SubStartReq, tSignal.getDataPtrSend());
+
+ sumaStart->subscriptionId = evnt.m_eventId;
+ sumaStart->subscriptionKey = evnt.m_eventKey;
+ sumaStart->part = SubscriptionData::TableData;
+ sumaStart->subscriberData = evnt.m_bufferId & 0xFF;
+ sumaStart->subscriberRef = m_reference;
+
+ DBUG_RETURN(executeSubscribeEvent(&tSignal, NULL));
+}
+
+int
+NdbDictInterface::executeSubscribeEvent(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3])
+{
+ return dictSignal(signal,NULL,0,
+ 1 /*use masternode id*/,
+ 100,
+ WAIT_CREATE_INDX_REQ /*WAIT_CREATE_EVNT_REQ*/,
+ -1,
+ NULL,0);
+}
+
+int
+NdbDictionaryImpl::stopSubscribeEvent(NdbEventImpl & ev)
+{
+ // NdbDictInterface m_receiver;
+ return m_receiver.stopSubscribeEvent(m_ndb, ev);
+}
+
+int
+NdbDictInterface::stopSubscribeEvent(class Ndb & ndb,
+ NdbEventImpl & evnt)
+{
+ DBUG_ENTER("NdbDictInterface::stopSubscribeEvent");
+
+ NdbApiSignal tSignal(m_reference);
+ // tSignal.theReceiversBlockNumber = SUMA;
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_SUB_STOP_REQ;
+ tSignal.theLength = SubStopReq::SignalLength;
+
+ SubStopReq * sumaStop = CAST_PTR(SubStopReq, tSignal.getDataPtrSend());
+
+ sumaStop->subscriptionId = evnt.m_eventId;
+ sumaStop->subscriptionKey = evnt.m_eventKey;
+ sumaStop->subscriberData = evnt.m_bufferId & 0xFF;
+ sumaStop->part = (Uint32) SubscriptionData::TableData;
+ sumaStop->subscriberRef = m_reference;
+
+ DBUG_RETURN(stopSubscribeEvent(&tSignal, NULL));
+}
+
+int
+NdbDictInterface::stopSubscribeEvent(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3])
+{
+ return dictSignal(signal,NULL,0,
+ 1 /*use masternode id*/,
+ 100,
+ WAIT_CREATE_INDX_REQ /*WAIT_SUB_STOP__REQ*/,
+ -1,
+ NULL,0);
+}
+
+NdbEventImpl *
+NdbDictionaryImpl::getEvent(const char * eventName)
+{
+ NdbEventImpl *ev = new NdbEventImpl();
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ev->setName(eventName);
+
+ int ret = m_receiver.createEvent(m_ndb, *ev, 1 /* getFlag set */);
+
+ if (ret) {
+ delete ev;
+ return NULL;
+ }
+
+ // We only have the table name with internal name
+ ev->setTable(m_ndb.externalizeTableName(ev->getTableName()));
+ ev->m_tableImpl = getTable(ev->getTableName());
+
+ // get the columns from the attrListBitmask
+
+ NdbTableImpl &table = *ev->m_tableImpl;
+ AttributeMask & mask = ev->m_attrListBitmask;
+ int attributeList_sz = mask.count();
+ int id = -1;
+
+#ifdef EVENT_DEBUG
+ ndbout_c("NdbDictionaryImpl::getEvent attributeList_sz = %d",
+ attributeList_sz);
+ char buf[128] = {0};
+ mask.getText(buf);
+ ndbout_c("mask = %s", buf);
+#endif
+
+ for(int i = 0; i < attributeList_sz; i++) {
+ id++; while (!mask.get(id)) id++;
+
+ const NdbColumnImpl* col = table.getColumn(id);
+ if(col == 0) {
+#ifdef EVENT_DEBUG
+ ndbout_c("NdbDictionaryImpl::getEvent could not find column id %d", id);
+#endif
+ m_error.code= 4247;
+ delete ev;
+ return NULL;
+ }
+ NdbColumnImpl* new_col = new NdbColumnImpl;
+ // Copy column definition
+ *new_col = *col;
+
+ ev->m_columns.push_back(new_col);
+ }
+
+ return ev;
+}
+
+void
+NdbDictInterface::execCREATE_EVNT_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execCREATE_EVNT_CONF");
+
+ m_buffer.clear();
+ unsigned int len = signal->getLength() << 2;
+ m_buffer.append((char *)&len, sizeof(len));
+ m_buffer.append(signal->getDataPtr(), len);
+
+ if (signal->m_noOfSections > 0) {
+ m_buffer.append((char *)ptr[0].p, strlen((char *)ptr[0].p)+1);
+ }
+
+ const CreateEvntConf * const createEvntConf=
+ CAST_CONSTPTR(CreateEvntConf, signal->getDataPtr());
+
+ Uint32 subscriptionId = createEvntConf->getEventId();
+ Uint32 subscriptionKey = createEvntConf->getEventKey();
+
+ DBUG_PRINT("info",("subscriptionId=%d,subscriptionKey=%d",
+ subscriptionId,subscriptionKey));
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execCREATE_EVNT_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execCREATE_EVNT_REF");
+
+ const CreateEvntRef* const ref=
+ CAST_CONSTPTR(CreateEvntRef, signal->getDataPtr());
+ m_error.code= ref->getErrorCode();
+ DBUG_PRINT("error",("error=%d,line=%d,node=%d",ref->getErrorCode(),
+ ref->getErrorLine(),ref->getErrorNode()));
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execSUB_STOP_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execSUB_STOP_CONF");
+ const SubStopConf * const subStopConf=
+ CAST_CONSTPTR(SubStopConf, signal->getDataPtr());
+
+ Uint32 subscriptionId = subStopConf->subscriptionId;
+ Uint32 subscriptionKey = subStopConf->subscriptionKey;
+ Uint32 subscriberData = subStopConf->subscriberData;
+
+ DBUG_PRINT("info",("subscriptionId=%d,subscriptionKey=%d,subscriberData=%d",
+ subscriptionId,subscriptionKey,subscriberData));
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execSUB_STOP_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execSUB_STOP_REF");
+ const SubStopRef * const subStopRef=
+ CAST_CONSTPTR(SubStopRef, signal->getDataPtr());
+
+ Uint32 subscriptionId = subStopRef->subscriptionId;
+ Uint32 subscriptionKey = subStopRef->subscriptionKey;
+ Uint32 subscriberData = subStopRef->subscriberData;
+ m_error.code= subStopRef->errorCode;
+
+ DBUG_PRINT("error",("subscriptionId=%d,subscriptionKey=%d,subscriberData=%d,error=%d",
+ subscriptionId,subscriptionKey,subscriberData,m_error.code));
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execSUB_START_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execSUB_START_CONF");
+ const SubStartConf * const subStartConf=
+ CAST_CONSTPTR(SubStartConf, signal->getDataPtr());
+
+ Uint32 subscriptionId = subStartConf->subscriptionId;
+ Uint32 subscriptionKey = subStartConf->subscriptionKey;
+ SubscriptionData::Part part =
+ (SubscriptionData::Part)subStartConf->part;
+ Uint32 subscriberData = subStartConf->subscriberData;
+
+ switch(part) {
+ case SubscriptionData::MetaData: {
+ DBUG_PRINT("error",("SubscriptionData::MetaData"));
+ m_error.code= 1;
+ break;
+ }
+ case SubscriptionData::TableData: {
+ DBUG_PRINT("info",("SubscriptionData::TableData"));
+ break;
+ }
+ default: {
+ DBUG_PRINT("error",("wrong data"));
+ m_error.code= 2;
+ break;
+ }
+ }
+ DBUG_PRINT("info",("subscriptionId=%d,subscriptionKey=%d,subscriberData=%d",
+ subscriptionId,subscriptionKey,subscriberData));
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execSUB_START_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execSUB_START_REF");
+ const SubStartRef * const subStartRef=
+ CAST_CONSTPTR(SubStartRef, signal->getDataPtr());
+ m_error.code= subStartRef->errorCode;
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+void
+NdbDictInterface::execSUB_GCP_COMPLETE_REP(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ const SubGcpCompleteRep * const rep=
+ CAST_CONSTPTR(SubGcpCompleteRep, signal->getDataPtr());
+
+ const Uint32 gci = rep->gci;
+ // const Uint32 senderRef = rep->senderRef;
+ const Uint32 subscriberData = rep->subscriberData;
+
+ const Uint32 bufferId = subscriberData;
+
+ const Uint32 ref = signal->theSendersBlockRef;
+
+ NdbApiSignal tSignal(m_reference);
+ SubGcpCompleteAcc * acc=
+ CAST_PTR(SubGcpCompleteAcc, tSignal.getDataPtrSend());
+
+ acc->rep = *rep;
+
+ tSignal.theReceiversBlockNumber = refToBlock(ref);
+ tSignal.theVerId_signalNumber = GSN_SUB_GCP_COMPLETE_ACC;
+ tSignal.theLength = SubGcpCompleteAcc::SignalLength;
+
+ Uint32 aNodeId = refToNode(ref);
+
+ // m_transporter->lock_mutex();
+ int r;
+ r = m_transporter->sendSignal(&tSignal, aNodeId);
+ // m_transporter->unlock_mutex();
+
+ NdbGlobalEventBufferHandle::latestGCI(bufferId, gci);
+}
+
+void
+NdbDictInterface::execSUB_TABLE_DATA(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+#ifdef EVENT_DEBUG
+ const char * FNAME = "NdbDictInterface::execSUB_TABLE_DATA";
+#endif
+ //TODO
+ const SubTableData * const sdata = CAST_CONSTPTR(SubTableData, signal->getDataPtr());
+
+ // const Uint32 gci = sdata->gci;
+ // const Uint32 operation = sdata->operation;
+ // const Uint32 tableId = sdata->tableId;
+ // const Uint32 noOfAttrs = sdata->noOfAttributes;
+ // const Uint32 dataLen = sdata->dataSize;
+ const Uint32 subscriberData = sdata->subscriberData;
+ // const Uint32 logType = sdata->logType;
+
+ for (int i=signal->m_noOfSections;i < 3; i++) {
+ ptr[i].p = NULL;
+ ptr[i].sz = 0;
+ }
+#ifdef EVENT_DEBUG
+ ndbout_c("%s: senderData %d, gci %d, operation %d, tableId %d, noOfAttrs %d, dataLen %d",
+ FNAME, subscriberData, gci, operation, tableId, noOfAttrs, dataLen);
+ ndbout_c("ptr[0] %u %u ptr[1] %u %u ptr[2] %u %u\n",
+ ptr[0].p,ptr[0].sz,ptr[1].p,ptr[1].sz,ptr[2].p,ptr[2].sz);
+#endif
+ const Uint32 bufferId = subscriberData;
+
+ NdbGlobalEventBufferHandle::insertDataL(bufferId,
+ sdata, ptr);
+}
+
+/*****************************************************************
+ * Drop event
+ */
+int
+NdbDictionaryImpl::dropEvent(const char * eventName)
+{
+ NdbEventImpl *ev= new NdbEventImpl();
+ ev->setName(eventName);
+ int ret= m_receiver.dropEvent(*ev);
+ delete ev;
+
+ // printf("__________________RET %u\n", ret);
+ return ret;
+}
+
+int
+NdbDictInterface::dropEvent(const NdbEventImpl &evnt)
+{
+ NdbApiSignal tSignal(m_reference);
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_DROP_EVNT_REQ;
+ tSignal.theLength = DropEvntReq::SignalLength;
+
+ DropEvntReq * const req = CAST_PTR(DropEvntReq, tSignal.getDataPtrSend());
+
+ req->setUserRef(m_reference);
+ req->setUserData(0);
+
+ UtilBufferWriter w(m_buffer);
+
+ w.add(SimpleProperties::StringValue, evnt.m_externalName.c_str());
+
+ LinearSectionPtr ptr[1];
+ ptr[0].p = (Uint32*)m_buffer.get_data();
+ ptr[0].sz = (m_buffer.length()+3) >> 2;
+
+ return dropEvent(&tSignal, ptr, 1);
+}
+
+int
+NdbDictInterface::dropEvent(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3], int noLSP)
+{
+ //TODO
+ const int noErrCodes = 1;
+ int errCodes[noErrCodes] = {DropEvntRef::Busy};
+ return dictSignal(signal,ptr,noLSP,
+ 1 /*use masternode id*/,
+ 100,
+ WAIT_CREATE_INDX_REQ /*WAIT_CREATE_EVNT_REQ*/,
+ -1,
+ errCodes,noErrCodes, DropEvntRef::Temporary);
+}
+void
+NdbDictInterface::execDROP_EVNT_CONF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execDROP_EVNT_CONF");
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbDictInterface::execDROP_EVNT_REF(NdbApiSignal * signal,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbDictInterface::execDROP_EVNT_REF");
+ const DropEvntRef* const ref=
+ CAST_CONSTPTR(DropEvntRef, signal->getDataPtr());
+ m_error.code= ref->getErrorCode();
+
+ DBUG_PRINT("info",("ErrorCode=%u Errorline=%u ErrorNode=%u",
+ ref->getErrorCode(), ref->getErrorLine(), ref->getErrorNode()));
+
+ m_waiter.signal(NO_WAIT);
+ DBUG_VOID_RETURN;
+}
+
+/*****************************************************************
+ * List objects or indexes
+ */
+int
+NdbDictionaryImpl::listObjects(List& list, NdbDictionary::Object::Type type)
+{
+ ListTablesReq req;
+ req.requestData = 0;
+ req.setTableType(getKernelConstant(type, objectTypeMapping, 0));
+ req.setListNames(true);
+ return m_receiver.listObjects(list, req.requestData, m_ndb.usingFullyQualifiedNames());
+}
+
+int
+NdbDictionaryImpl::listIndexes(List& list, Uint32 indexId)
+{
+ ListTablesReq req;
+ req.requestData = 0;
+ req.setTableId(indexId);
+ req.setListNames(true);
+ req.setListIndexes(true);
+ return m_receiver.listObjects(list, req.requestData, m_ndb.usingFullyQualifiedNames());
+}
+
+int
+NdbDictInterface::listObjects(NdbDictionary::Dictionary::List& list,
+ Uint32 requestData, bool fullyQualifiedNames)
+{
+ NdbApiSignal tSignal(m_reference);
+ ListTablesReq* const req = CAST_PTR(ListTablesReq, tSignal.getDataPtrSend());
+ req->senderRef = m_reference;
+ req->senderData = 0;
+ req->requestData = requestData;
+ tSignal.theReceiversBlockNumber = DBDICT;
+ tSignal.theVerId_signalNumber = GSN_LIST_TABLES_REQ;
+ tSignal.theLength = ListTablesReq::SignalLength;
+ if (listObjects(&tSignal) != 0)
+ return -1;
+ // count
+ const Uint32* data = (const Uint32*)m_buffer.get_data();
+ const unsigned length = m_buffer.length() / 4;
+ list.count = 0;
+ bool ok = true;
+ unsigned pos, count;
+ pos = count = 0;
+ while (pos < length) {
+ // table id - name length - name
+ pos++;
+ if (pos >= length) {
+ ok = false;
+ break;
+ }
+ Uint32 n = (data[pos++] + 3) >> 2;
+ pos += n;
+ if (pos > length) {
+ ok = false;
+ break;
+ }
+ count++;
+ }
+ if (! ok) {
+ // bad signal data
+ m_error.code= 4213;
+ return -1;
+ }
+ list.count = count;
+ list.elements = new NdbDictionary::Dictionary::List::Element[count];
+ pos = count = 0;
+ while (pos < length) {
+ NdbDictionary::Dictionary::List::Element& element = list.elements[count];
+ Uint32 d = data[pos++];
+ element.id = ListTablesConf::getTableId(d);
+ element.type = (NdbDictionary::Object::Type)
+ getApiConstant(ListTablesConf::getTableType(d), objectTypeMapping, 0);
+ element.state = (NdbDictionary::Object::State)
+ getApiConstant(ListTablesConf::getTableState(d), objectStateMapping, 0);
+ element.store = (NdbDictionary::Object::Store)
+ getApiConstant(ListTablesConf::getTableStore(d), objectStoreMapping, 0);
+ // table or index name
+ Uint32 n = (data[pos++] + 3) >> 2;
+ BaseString databaseName;
+ BaseString schemaName;
+ BaseString objectName;
+ if ((element.type == NdbDictionary::Object::UniqueHashIndex) ||
+ (element.type == NdbDictionary::Object::OrderedIndex)) {
+ char * indexName = new char[n << 2];
+ memcpy(indexName, &data[pos], n << 2);
+ databaseName = Ndb::getDatabaseFromInternalName(indexName);
+ schemaName = Ndb::getSchemaFromInternalName(indexName);
+ objectName = BaseString(Ndb::externalizeIndexName(indexName, fullyQualifiedNames));
+ delete [] indexName;
+ } else if ((element.type == NdbDictionary::Object::SystemTable) ||
+ (element.type == NdbDictionary::Object::UserTable)) {
+ char * tableName = new char[n << 2];
+ memcpy(tableName, &data[pos], n << 2);
+ databaseName = Ndb::getDatabaseFromInternalName(tableName);
+ schemaName = Ndb::getSchemaFromInternalName(tableName);
+ objectName = BaseString(Ndb::externalizeTableName(tableName, fullyQualifiedNames));
+ delete [] tableName;
+ }
+ else {
+ char * otherName = new char[n << 2];
+ memcpy(otherName, &data[pos], n << 2);
+ objectName = BaseString(otherName);
+ delete [] otherName;
+ }
+ element.database = new char[databaseName.length() + 1];
+ strcpy(element.database, databaseName.c_str());
+ element.schema = new char[schemaName.length() + 1];
+ strcpy(element.schema, schemaName.c_str());
+ element.name = new char[objectName.length() + 1];
+ strcpy(element.name, objectName.c_str());
+ pos += n;
+ count++;
+ }
+ return 0;
+}
+
+int
+NdbDictInterface::listObjects(NdbApiSignal* signal)
+{
+ const Uint32 RETRIES = 100;
+ for (Uint32 i = 0; i < RETRIES; i++) {
+ m_buffer.clear();
+ // begin protected
+ m_transporter->lock_mutex();
+ Uint16 aNodeId = m_transporter->get_an_alive_node();
+ if (aNodeId == 0) {
+ m_error.code= 4009;
+ m_transporter->unlock_mutex();
+ return -1;
+ }
+ if (m_transporter->sendSignal(signal, aNodeId) != 0) {
+ m_transporter->unlock_mutex();
+ continue;
+ }
+ m_error.code= 0;
+ m_waiter.m_node = aNodeId;
+ m_waiter.m_state = WAIT_LIST_TABLES_CONF;
+ m_waiter.wait(WAITFOR_RESPONSE_TIMEOUT);
+ m_transporter->unlock_mutex();
+ // end protected
+ if (m_waiter.m_state == NO_WAIT && m_error.code == 0)
+ return 0;
+ if (m_waiter.m_state == WAIT_NODE_FAILURE)
+ continue;
+ return -1;
+ }
+ return -1;
+}
+
+void
+NdbDictInterface::execLIST_TABLES_CONF(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3])
+{
+ const unsigned off = ListTablesConf::HeaderLength;
+ const unsigned len = (signal->getLength() - off);
+ m_buffer.append(signal->getDataPtr() + off, len << 2);
+ if (signal->getLength() < ListTablesConf::SignalLength) {
+ // last signal has less than full length
+ m_waiter.signal(NO_WAIT);
+ }
+}
+
+template class Vector<int>;
+template class Vector<Uint16>;
+template class Vector<Uint32>;
+template class Vector<Vector<Uint32> >;
+template class Vector<NdbTableImpl*>;
+template class Vector<NdbColumnImpl*>;
+
diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp
new file mode 100644
index 00000000000..59a5956715a
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp
@@ -0,0 +1,704 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NdbDictionaryImpl_H
+#define NdbDictionaryImpl_H
+
+#include <ndb_types.h>
+#include <kernel_types.h>
+#include <ndb_limits.h>
+#include <NdbError.hpp>
+#include <BaseString.hpp>
+#include <Vector.hpp>
+#include <UtilBuffer.hpp>
+#include <NdbDictionary.hpp>
+#include <Bitmask.hpp>
+#include <AttributeList.hpp>
+#include <Ndb.hpp>
+#include "NdbWaiter.hpp"
+#include "DictCache.hpp"
+
+class NdbDictObjectImpl {
+public:
+ Uint32 m_version;
+ NdbDictionary::Object::Status m_status;
+
+ bool change();
+protected:
+ NdbDictObjectImpl() :
+ m_status(NdbDictionary::Object::New) {
+ }
+};
+
+/**
+ * Column
+ */
+class NdbColumnImpl : public NdbDictionary::Column {
+public:
+ NdbColumnImpl();
+ NdbColumnImpl(NdbDictionary::Column &); // This is not a copy constructor
+ ~NdbColumnImpl();
+ NdbColumnImpl& operator=(const NdbColumnImpl&);
+ void init(Type t = Unsigned);
+
+ int m_attrId;
+ BaseString m_name;
+ NdbDictionary::Column::Type m_type;
+ int m_precision;
+ int m_scale;
+ int m_length;
+ CHARSET_INFO * m_cs; // not const in MySQL
+
+ bool m_pk;
+ bool m_distributionKey;
+ bool m_nullable;
+ bool m_autoIncrement;
+ Uint64 m_autoIncrementInitialValue;
+ BaseString m_defaultValue;
+ NdbTableImpl * m_blobTable;
+
+ /**
+ * Internal types and sizes, and aggregates
+ */
+ Uint32 m_attrSize; // element size (size when arraySize==1)
+ Uint32 m_arraySize; // length or length+2 for Var* types
+ Uint32 m_keyInfoPos;
+ // TODO: use bits in attr desc 2
+ bool getInterpretableType() const ;
+ bool getCharType() const;
+ bool getStringType() const;
+ bool getBlobType() const;
+
+ /**
+ * Equality/assign
+ */
+ bool equal(const NdbColumnImpl&) const;
+
+ static NdbColumnImpl & getImpl(NdbDictionary::Column & t);
+ static const NdbColumnImpl & getImpl(const NdbDictionary::Column & t);
+ NdbDictionary::Column * m_facade;
+
+ static NdbDictionary::Column * create_psuedo(const char *);
+};
+
+class NdbTableImpl : public NdbDictionary::Table, public NdbDictObjectImpl {
+public:
+ NdbTableImpl();
+ NdbTableImpl(NdbDictionary::Table &);
+ ~NdbTableImpl();
+
+ void init();
+ void setName(const char * name);
+ const char * getName() const;
+
+ Uint32 m_changeMask;
+ Uint32 m_tableId;
+ BaseString m_internalName;
+ BaseString m_externalName;
+ BaseString m_newExternalName; // Used for alter table
+ UtilBuffer m_frm;
+ NdbDictionary::Object::FragmentType m_fragmentType;
+
+ /**
+ *
+ */
+ Uint32 m_columnHashMask;
+ Vector<Uint32> m_columnHash;
+ Vector<NdbColumnImpl *> m_columns;
+ void buildColumnHash();
+
+ /**
+ * Fragment info
+ */
+ Uint32 m_hashValueMask;
+ Uint32 m_hashpointerValue;
+ Vector<Uint16> m_fragments;
+
+ bool m_logging;
+ int m_kvalue;
+ int m_minLoadFactor;
+ int m_maxLoadFactor;
+ Uint16 m_keyLenInWords;
+ Uint16 m_fragmentCount;
+
+ NdbDictionaryImpl * m_dictionary;
+ NdbIndexImpl * m_index;
+ NdbColumnImpl * getColumn(unsigned attrId);
+ NdbColumnImpl * getColumn(const char * name);
+ const NdbColumnImpl * getColumn(unsigned attrId) const;
+ const NdbColumnImpl * getColumn(const char * name) const;
+
+ /**
+ * Index only stuff
+ */
+ BaseString m_primaryTable;
+ NdbDictionary::Index::Type m_indexType;
+
+ /**
+ * Aggregates
+ */
+ Uint8 m_noOfKeys;
+ Uint8 m_noOfDistributionKeys;
+ Uint8 m_noOfBlobs;
+
+ Uint8 m_replicaCount;
+
+ /**
+ * Equality/assign
+ */
+ bool equal(const NdbTableImpl&) const;
+ void assign(const NdbTableImpl&);
+ void clearNewProperties();
+ void copyNewProperties();
+
+ static NdbTableImpl & getImpl(NdbDictionary::Table & t);
+ static NdbTableImpl & getImpl(const NdbDictionary::Table & t);
+ NdbDictionary::Table * m_facade;
+
+ /**
+ * Return count
+ */
+ Uint32 get_nodes(Uint32 hashValue, const Uint16** nodes) const ;
+};
+
+class NdbIndexImpl : public NdbDictionary::Index, public NdbDictObjectImpl {
+public:
+ NdbIndexImpl();
+ NdbIndexImpl(NdbDictionary::Index &);
+ ~NdbIndexImpl();
+
+ void setName(const char * name);
+ const char * getName() const;
+ void setTable(const char * table);
+ const char * getTable() const;
+ const NdbTableImpl * getIndexTable() const;
+
+ Uint32 m_indexId;
+ BaseString m_internalName;
+ BaseString m_externalName;
+ BaseString m_tableName;
+ Vector<NdbColumnImpl *> m_columns;
+ Vector<int> m_key_ids;
+ NdbDictionary::Index::Type m_type;
+
+ bool m_logging;
+
+ NdbTableImpl * m_table;
+
+ static NdbIndexImpl & getImpl(NdbDictionary::Index & t);
+ static NdbIndexImpl & getImpl(const NdbDictionary::Index & t);
+ NdbDictionary::Index * m_facade;
+};
+
+class NdbEventImpl : public NdbDictionary::Event, public NdbDictObjectImpl {
+public:
+ NdbEventImpl();
+ NdbEventImpl(NdbDictionary::Event &);
+ ~NdbEventImpl();
+
+ void setName(const char * name);
+ const char * getName() const;
+ void setTable(const NdbDictionary::Table& table);
+ void setTable(const char * table);
+ const char * getTableName() const;
+ void addTableEvent(const NdbDictionary::Event::TableEvent t);
+ void setDurability(NdbDictionary::Event::EventDurability d);
+ NdbDictionary::Event::EventDurability getDurability() const;
+ void addEventColumn(const NdbColumnImpl &c);
+ int getNoOfEventColumns() const;
+
+ void print() {
+ ndbout_c("NdbEventImpl: id=%d, key=%d",
+ m_eventId,
+ m_eventKey);
+ };
+
+ Uint32 m_eventId;
+ Uint32 m_eventKey;
+ Uint32 m_tableId;
+ AttributeMask m_attrListBitmask;
+ //BaseString m_internalName;
+ BaseString m_externalName;
+ Uint32 mi_type;
+ NdbDictionary::Event::EventDurability m_dur;
+
+
+ NdbTableImpl *m_tableImpl;
+ BaseString m_tableName;
+ Vector<NdbColumnImpl *> m_columns;
+ Vector<unsigned> m_attrIds;
+
+ int m_bufferId;
+
+ NdbEventOperation *eventOp;
+
+ static NdbEventImpl & getImpl(NdbDictionary::Event & t);
+ static NdbEventImpl & getImpl(const NdbDictionary::Event & t);
+ NdbDictionary::Event * m_facade;
+};
+
+
+class NdbDictInterface {
+public:
+ NdbDictInterface(NdbError& err) : m_error(err) {
+ m_reference = 0;
+ m_masterNodeId = 0;
+ m_transporter= NULL;
+ }
+ ~NdbDictInterface();
+
+ bool setTransporter(class Ndb * ndb, class TransporterFacade * tf);
+ bool setTransporter(class TransporterFacade * tf);
+
+ // To abstract the stuff thats made in all create/drop/lists below
+ int
+ dictSignal(NdbApiSignal* signal,
+ LinearSectionPtr ptr[3], int noLPTR,
+ const int useMasterNodeId,
+ const Uint32 RETRIES,
+ const WaitSignalType wst,
+ const int theWait,
+ const int *errcodes,
+ const int noerrcodes,
+ const int temporaryMask = 0);
+
+ int createOrAlterTable(class Ndb & ndb, NdbTableImpl &, bool alter);
+
+ int createTable(class Ndb & ndb, NdbTableImpl &);
+ int createTable(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int alterTable(class Ndb & ndb, NdbTableImpl &);
+ int alterTable(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int createIndex(class Ndb & ndb,
+ NdbIndexImpl &,
+ const NdbTableImpl &);
+ int createIndex(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int createEvent(class Ndb & ndb, NdbEventImpl &, int getFlag);
+ int createEvent(NdbApiSignal* signal, LinearSectionPtr ptr[3], int noLSP);
+
+ int dropTable(const NdbTableImpl &);
+ int dropTable(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int dropIndex(const NdbIndexImpl &, const NdbTableImpl &);
+ int dropIndex(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int dropEvent(const NdbEventImpl &);
+ int dropEvent(NdbApiSignal* signal, LinearSectionPtr ptr[3], int noLSP);
+
+ int executeSubscribeEvent(class Ndb & ndb, NdbEventImpl &);
+ int executeSubscribeEvent(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int stopSubscribeEvent(class Ndb & ndb, NdbEventImpl &);
+ int stopSubscribeEvent(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ int listObjects(NdbDictionary::Dictionary::List& list, Uint32 requestData, bool fullyQualifiedNames);
+ int listObjects(NdbApiSignal* signal);
+
+ NdbTableImpl * getTable(int tableId, bool fullyQualifiedNames);
+ NdbTableImpl * getTable(const char * name, bool fullyQualifiedNames);
+ NdbTableImpl * getTable(class NdbApiSignal * signal,
+ LinearSectionPtr ptr[3],
+ Uint32 noOfSections, bool fullyQualifiedNames);
+
+ static int parseTableInfo(NdbTableImpl ** dst,
+ const Uint32 * data, Uint32 len,
+ bool fullyQualifiedNames);
+
+ static int create_index_obj_from_table(NdbIndexImpl ** dst,
+ NdbTableImpl* index_table,
+ const NdbTableImpl* primary_table);
+
+ NdbError & m_error;
+private:
+ Uint32 m_reference;
+ Uint32 m_masterNodeId;
+
+ NdbWaiter m_waiter;
+ class TransporterFacade * m_transporter;
+
+ friend class Ndb;
+ static void execSignal(void* dictImpl,
+ class NdbApiSignal* signal,
+ struct LinearSectionPtr ptr[3]);
+
+ static void execNodeStatus(void* dictImpl, Uint32,
+ bool alive, bool nfCompleted);
+
+ void execGET_TABINFO_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execGET_TABINFO_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execCREATE_TABLE_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execCREATE_TABLE_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execALTER_TABLE_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execALTER_TABLE_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+
+ void execCREATE_INDX_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execCREATE_INDX_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execDROP_INDX_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execDROP_INDX_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+
+ void execCREATE_EVNT_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execCREATE_EVNT_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execSUB_START_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execSUB_START_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execSUB_TABLE_DATA(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execSUB_GCP_COMPLETE_REP(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execSUB_STOP_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execSUB_STOP_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execDROP_EVNT_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execDROP_EVNT_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+
+ void execDROP_TABLE_REF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execDROP_TABLE_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+ void execLIST_TABLES_CONF(NdbApiSignal *, LinearSectionPtr ptr[3]);
+
+ Uint32 m_fragmentId;
+ UtilBuffer m_buffer;
+};
+
+class NdbDictionaryImpl : public NdbDictionary::Dictionary {
+public:
+ NdbDictionaryImpl(Ndb &ndb);
+ NdbDictionaryImpl(Ndb &ndb, NdbDictionary::Dictionary & f);
+ ~NdbDictionaryImpl();
+
+ bool setTransporter(class Ndb * ndb, class TransporterFacade * tf);
+ bool setTransporter(class TransporterFacade * tf);
+
+ int createTable(NdbTableImpl &t);
+ int createBlobTables(NdbTableImpl &);
+ int addBlobTables(NdbTableImpl &);
+ int alterTable(NdbTableImpl &t);
+ int dropTable(const char * name);
+ int dropTable(NdbTableImpl &);
+ int dropBlobTables(NdbTableImpl &);
+ int invalidateObject(NdbTableImpl &);
+ int removeCachedObject(NdbTableImpl &);
+
+ int createIndex(NdbIndexImpl &ix);
+ int dropIndex(const char * indexName,
+ const char * tableName);
+ int dropIndex(NdbIndexImpl &, const char * tableName);
+ NdbTableImpl * getIndexTable(NdbIndexImpl * index,
+ NdbTableImpl * table);
+
+ int createEvent(NdbEventImpl &);
+ int dropEvent(const char * eventName);
+
+ int executeSubscribeEvent(NdbEventImpl &);
+ int stopSubscribeEvent(NdbEventImpl &);
+
+ int listObjects(List& list, NdbDictionary::Object::Type type);
+ int listIndexes(List& list, Uint32 indexId);
+
+ NdbTableImpl * getTable(const char * tableName, void **data= 0);
+ Ndb_local_table_info * get_local_table_info(const char * internalName,
+ bool do_add_blob_tables);
+ NdbIndexImpl * getIndex(const char * indexName,
+ const char * tableName);
+ NdbIndexImpl * getIndexImpl(const char * name, const char * internalName);
+ NdbEventImpl * getEvent(const char * eventName);
+ NdbEventImpl * getEventImpl(const char * internalName);
+
+ const NdbError & getNdbError() const;
+ NdbError m_error;
+ Uint32 m_local_table_data_size;
+
+ LocalDictCache m_localHash;
+ GlobalDictCache * m_globalHash;
+
+ static NdbDictionaryImpl & getImpl(NdbDictionary::Dictionary & t);
+ static const NdbDictionaryImpl & getImpl(const NdbDictionary::Dictionary &t);
+ NdbDictionary::Dictionary * m_facade;
+
+ NdbDictInterface m_receiver;
+ Ndb & m_ndb;
+private:
+ Ndb_local_table_info * fetchGlobalTableImpl(const char * internalName);
+};
+
+inline
+NdbEventImpl &
+NdbEventImpl::getImpl(const NdbDictionary::Event & t){
+ return t.m_impl;
+}
+
+inline
+NdbEventImpl &
+NdbEventImpl::getImpl(NdbDictionary::Event & t){
+ return t.m_impl;
+}
+
+inline
+NdbColumnImpl &
+NdbColumnImpl::getImpl(NdbDictionary::Column & t){
+ return t.m_impl;
+}
+
+inline
+const NdbColumnImpl &
+NdbColumnImpl::getImpl(const NdbDictionary::Column & t){
+ return t.m_impl;
+}
+
+inline
+bool
+NdbColumnImpl::getInterpretableType() const {
+ return (m_type == NdbDictionary::Column::Unsigned ||
+ m_type == NdbDictionary::Column::Bigunsigned);
+}
+
+inline
+bool
+NdbColumnImpl::getCharType() const {
+ return (m_type == NdbDictionary::Column::Char ||
+ m_type == NdbDictionary::Column::Varchar ||
+ m_type == NdbDictionary::Column::Text ||
+ m_type == NdbDictionary::Column::Longvarchar);
+}
+
+inline
+bool
+NdbColumnImpl::getStringType() const {
+ return (m_type == NdbDictionary::Column::Char ||
+ m_type == NdbDictionary::Column::Varchar ||
+ m_type == NdbDictionary::Column::Longvarchar ||
+ m_type == NdbDictionary::Column::Binary ||
+ m_type == NdbDictionary::Column::Varbinary ||
+ m_type == NdbDictionary::Column::Longvarbinary);
+}
+
+inline
+bool
+NdbColumnImpl::getBlobType() const {
+ return (m_type == NdbDictionary::Column::Blob ||
+ m_type == NdbDictionary::Column::Text);
+}
+
+inline
+NdbTableImpl &
+NdbTableImpl::getImpl(NdbDictionary::Table & t){
+ return t.m_impl;
+}
+
+inline
+NdbTableImpl &
+NdbTableImpl::getImpl(const NdbDictionary::Table & t){
+ return t.m_impl;
+}
+
+inline
+NdbColumnImpl *
+NdbTableImpl::getColumn(unsigned attrId){
+ if(m_columns.size() > attrId){
+ return m_columns[attrId];
+ }
+ return 0;
+}
+
+inline
+Uint32
+Hash( const char* str ){
+ Uint32 h = 0;
+ Uint32 len = strlen(str);
+ while(len >= 4){
+ h = (h << 5) + h + str[0];
+ h = (h << 5) + h + str[1];
+ h = (h << 5) + h + str[2];
+ h = (h << 5) + h + str[3];
+ len -= 4;
+ str += 4;
+ }
+
+ switch(len){
+ case 3:
+ h = (h << 5) + h + *str++;
+ case 2:
+ h = (h << 5) + h + *str++;
+ case 1:
+ h = (h << 5) + h + *str++;
+ }
+ return h + h;
+}
+
+
+inline
+NdbColumnImpl *
+NdbTableImpl::getColumn(const char * name){
+
+ Uint32 sz = m_columns.size();
+ NdbColumnImpl** cols = m_columns.getBase();
+ const Uint32 * hashtable = m_columnHash.getBase();
+
+ if(sz > 5 && false){
+ Uint32 hashValue = Hash(name) & 0xFFFE;
+ Uint32 bucket = hashValue & m_columnHashMask;
+ bucket = (bucket < sz ? bucket : bucket - sz);
+ hashtable += bucket;
+ Uint32 tmp = * hashtable;
+ if((tmp & 1) == 1 ){ // No chaining
+ sz = 1;
+ } else {
+ sz = (tmp >> 16);
+ hashtable += (tmp & 0xFFFE) >> 1;
+ tmp = * hashtable;
+ }
+ do {
+ if(hashValue == (tmp & 0xFFFE)){
+ NdbColumnImpl* col = cols[tmp >> 16];
+ if(strncmp(name, col->m_name.c_str(), NDB_MAX_ATTR_NAME_SIZE-1) == 0){
+ return col;
+ }
+ }
+ hashtable++;
+ tmp = * hashtable;
+ } while(--sz > 0);
+#if 0
+ Uint32 dir = m_columnHash[bucket];
+ Uint32 pos = bucket + ((dir & 0xFFFE) >> 1);
+ Uint32 cnt = dir >> 16;
+ ndbout_c("col: %s hv: %x bucket: %d dir: %x pos: %d cnt: %d tmp: %d -> 0",
+ name, hashValue, bucket, dir, pos, cnt, tmp);
+#endif
+ return 0;
+ } else {
+ for(Uint32 i = 0; i<sz; i++){
+ NdbColumnImpl* col = * cols++;
+ if(col != 0 && strncmp(name, col->m_name.c_str(), NDB_MAX_ATTR_NAME_SIZE-1) == 0)
+ return col;
+ }
+ }
+ return 0;
+}
+
+inline
+const NdbColumnImpl *
+NdbTableImpl::getColumn(unsigned attrId) const {
+ if(m_columns.size() > attrId){
+ return m_columns[attrId];
+ }
+ return 0;
+}
+
+inline
+const NdbColumnImpl *
+NdbTableImpl::getColumn(const char * name) const {
+ Uint32 sz = m_columns.size();
+ NdbColumnImpl* const * cols = m_columns.getBase();
+ for(Uint32 i = 0; i<sz; i++, cols++){
+ NdbColumnImpl* col = * cols;
+ if(col != 0 && strcmp(name, col->m_name.c_str()) == 0)
+ return col;
+ }
+ return 0;
+}
+
+inline
+NdbIndexImpl &
+NdbIndexImpl::getImpl(NdbDictionary::Index & t){
+ return t.m_impl;
+}
+
+inline
+NdbIndexImpl &
+NdbIndexImpl::getImpl(const NdbDictionary::Index & t){
+ return t.m_impl;
+}
+
+inline
+NdbDictionaryImpl &
+NdbDictionaryImpl::getImpl(NdbDictionary::Dictionary & t){
+ return t.m_impl;
+}
+
+inline
+const NdbDictionaryImpl &
+NdbDictionaryImpl::getImpl(const NdbDictionary::Dictionary & t){
+ return t.m_impl;
+}
+
+/*****************************************************************
+ * Inline:d getters
+ */
+
+inline
+NdbTableImpl *
+NdbDictionaryImpl::getTable(const char * tableName, void **data)
+{
+ Ndb_local_table_info *info=
+ get_local_table_info(m_ndb.internalizeTableName(tableName), true);
+ if (info == 0) {
+ return 0;
+ }
+ if (data) {
+ *data= info->m_local_data;
+ }
+ return info->m_table_impl;
+}
+
+inline
+Ndb_local_table_info *
+NdbDictionaryImpl::get_local_table_info(const char * internalTableName,
+ bool do_add_blob_tables)
+{
+ Ndb_local_table_info *info= m_localHash.get(internalTableName);
+ if (info == 0) {
+ info= fetchGlobalTableImpl(internalTableName);
+ if (info == 0) {
+ return 0;
+ }
+ }
+ if (do_add_blob_tables && info->m_table_impl->m_noOfBlobs)
+ addBlobTables(*(info->m_table_impl));
+
+ return info; // autoincrement already initialized
+}
+
+inline
+NdbIndexImpl *
+NdbDictionaryImpl::getIndex(const char * indexName,
+ const char * tableName)
+{
+ if (tableName || m_ndb.usingFullyQualifiedNames()) {
+ const char * internalIndexName = 0;
+ if (tableName) {
+ NdbTableImpl * t = getTable(tableName);
+ if (t != 0)
+ internalIndexName = m_ndb.internalizeIndexName(t, indexName);
+ } else {
+ internalIndexName =
+ m_ndb.internalizeTableName(indexName); // Index is also a table
+ }
+ if (internalIndexName) {
+ Ndb_local_table_info * info = get_local_table_info(internalIndexName,
+ false);
+ if (info) {
+ NdbTableImpl * tab = info->m_table_impl;
+ if (tab->m_index == 0)
+ tab->m_index = getIndexImpl(indexName, internalIndexName);
+ if (tab->m_index != 0)
+ tab->m_index->m_table = tab;
+ return tab->m_index;
+ }
+ }
+ }
+
+ m_error.code = 4243;
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbErrorOut.cpp b/storage/ndb/src/ndbapi/NdbErrorOut.cpp
new file mode 100644
index 00000000000..07e0b2fe6e8
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbErrorOut.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbError.hpp>
+
+/**
+ * operators
+ */
+NdbOut &
+operator<<(NdbOut & out, const NdbError & error){
+ if(error.message != 0)
+ out << error.code << ": " << error.message;
+ else
+ out << error.code << ": ";
+ return out;
+}
+
+NdbOut &
+operator<<(NdbOut & out, const NdbError::Status & status){
+ return out << ndberror_status_message((ndberror_status)status);
+}
+
+NdbOut &
+operator<<(NdbOut & out, const NdbError::Classification & classification){
+ return out << ndberror_classification_message((ndberror_classification)classification);
+}
+
diff --git a/storage/ndb/src/ndbapi/NdbEventOperation.cpp b/storage/ndb/src/ndbapi/NdbEventOperation.cpp
new file mode 100644
index 00000000000..e99cad918c5
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbEventOperation.cpp
@@ -0,0 +1,115 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <Ndb.hpp>
+#include <NdbError.hpp>
+#include <portlib/NdbMem.h>
+#include "NdbEventOperationImpl.hpp"
+#include "NdbDictionaryImpl.hpp"
+
+NdbEventOperation::NdbEventOperation(Ndb *theNdb,
+ const char* eventName,
+ int bufferLength)
+ : m_impl(* new NdbEventOperationImpl(*this,theNdb,
+ eventName,
+ bufferLength))
+{
+}
+
+NdbEventOperation::~NdbEventOperation()
+{
+ NdbEventOperationImpl * tmp = &m_impl;
+ if (this != tmp)
+ delete tmp;
+}
+
+NdbEventOperation::State NdbEventOperation::getState()
+{
+ return m_impl.getState();
+}
+
+NdbRecAttr *
+NdbEventOperation::getValue(const char *colName, char *aValue)
+{
+ return m_impl.getValue(colName, aValue, 0);
+}
+
+NdbRecAttr *
+NdbEventOperation::getPreValue(const char *colName, char *aValue)
+{
+ return m_impl.getValue(colName, aValue, 1);
+}
+
+int
+NdbEventOperation::execute()
+{
+ return m_impl.execute();
+}
+
+int
+NdbEventOperation::next(int *pOverrun)
+{
+ return m_impl.next(pOverrun);
+}
+
+bool
+NdbEventOperation::isConsistent()
+{
+ return m_impl.isConsistent();
+}
+
+Uint32
+NdbEventOperation::getGCI()
+{
+ return m_impl.getGCI();
+}
+
+Uint32
+NdbEventOperation::getLatestGCI()
+{
+ return m_impl.getLatestGCI();
+}
+
+NdbDictionary::Event::TableEvent
+NdbEventOperation::getEventType()
+{
+ return m_impl.getEventType();
+}
+
+void
+NdbEventOperation::print()
+{
+ m_impl.print();
+}
+
+/*
+ * Private members
+ */
+
+int
+NdbEventOperation::wait(void *p, int aMillisecondNumber)
+{
+ return NdbEventOperationImpl::wait(p, aMillisecondNumber);
+}
+
+NdbEventOperation::NdbEventOperation(NdbEventOperationImpl& impl)
+ : m_impl(impl) {}
+
+const struct NdbError &
+NdbEventOperation::getNdbError() const {
+ return m_impl.getNdbError();
+}
diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp
new file mode 100644
index 00000000000..208525bfc15
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp
@@ -0,0 +1,1366 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <kernel_types.h>
+
+#include "NdbDictionaryImpl.hpp"
+#include "API.hpp"
+#include <NdbOut.hpp>
+#include "NdbApiSignal.hpp"
+#include "TransporterFacade.hpp"
+#include <signaldata/CreateEvnt.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <SimpleProperties.hpp>
+#include <Bitmask.hpp>
+#include <AttributeHeader.hpp>
+#include <AttributeList.hpp>
+#include <NdbError.hpp>
+#include <BaseString.hpp>
+#include <UtilBuffer.hpp>
+#include <NdbDictionary.hpp>
+#include <Ndb.hpp>
+#include "NdbImpl.hpp"
+#include "DictCache.hpp"
+#include <portlib/NdbMem.h>
+#include <NdbRecAttr.hpp>
+#include <NdbEventOperation.hpp>
+#include "NdbEventOperationImpl.hpp"
+
+/*
+ * Class NdbEventOperationImpl
+ *
+ *
+ */
+
+//#define EVENT_DEBUG
+
+
+NdbEventOperationImpl::NdbEventOperationImpl(NdbEventOperation &N,
+ Ndb *theNdb,
+ const char* eventName,
+ const int bufferLength)
+ : NdbEventOperation(*this), m_ndb(theNdb),
+ m_state(EO_ERROR), m_bufferL(bufferLength)
+{
+ m_eventId = 0;
+ theFirstPkAttrs[0] = NULL;
+ theCurrentPkAttrs[0] = NULL;
+ theFirstPkAttrs[1] = NULL;
+ theCurrentPkAttrs[1] = NULL;
+ theFirstDataAttrs[0] = NULL;
+ theCurrentDataAttrs[0] = NULL;
+ theFirstDataAttrs[1] = NULL;
+ theCurrentDataAttrs[1] = NULL;
+ sdata = NULL;
+ ptr[0].p = NULL;
+ ptr[1].p = NULL;
+ ptr[2].p = NULL;
+
+ // we should lookup id in Dictionary, TODO
+ // also make sure we only have one listener on each event
+
+ if (!m_ndb) abort();
+
+ NdbDictionary::Dictionary *myDict = m_ndb->getDictionary();
+ if (!myDict) { m_error.code= m_ndb->getNdbError().code; return; }
+
+ const NdbDictionary::Event *myEvnt = myDict->getEvent(eventName);
+ if (!myEvnt) { m_error.code= myDict->getNdbError().code; return; }
+
+ m_eventImpl = &myEvnt->m_impl;
+
+ m_eventId = m_eventImpl->m_eventId;
+
+ m_bufferHandle = m_ndb->getGlobalEventBufferHandle();
+ if (m_bufferHandle->m_bufferL > 0)
+ m_bufferL =m_bufferHandle->m_bufferL;
+ else
+ m_bufferHandle->m_bufferL = m_bufferL;
+
+ m_state = EO_CREATED;
+}
+
+NdbEventOperationImpl::~NdbEventOperationImpl()
+{
+ int i;
+ if (sdata) NdbMem_Free((char*)sdata);
+ for (i=0 ; i<2; i++) {
+ NdbRecAttr *p = theFirstPkAttrs[i];
+ while (p) {
+ NdbRecAttr *p_next = p->next();
+ m_ndb->releaseRecAttr(p);
+ p = p_next;
+ }
+ }
+ for (i=0 ; i<2; i++) {
+ NdbRecAttr *p = theFirstDataAttrs[i];
+ while (p) {
+ NdbRecAttr *p_next = p->next();
+ m_ndb->releaseRecAttr(p);
+ p = p_next;
+ }
+ }
+ if (m_state == EO_EXECUTING) {
+ stop();
+ // m_bufferHandle->dropSubscribeEvent(m_bufferId);
+ ; // We should send stop signal here
+ }
+}
+
+NdbEventOperation::State
+NdbEventOperationImpl::getState()
+{
+ return m_state;
+}
+
+NdbRecAttr*
+NdbEventOperationImpl::getValue(const char *colName, char *aValue, int n)
+{
+ DBUG_ENTER("NdbEventOperationImpl::getValue");
+ if (m_state != EO_CREATED) {
+ ndbout_c("NdbEventOperationImpl::getValue may only be called between instantiation and execute()");
+ DBUG_RETURN(NULL);
+ }
+
+ NdbColumnImpl *tAttrInfo = m_eventImpl->m_tableImpl->getColumn(colName);
+
+ if (tAttrInfo == NULL) {
+ ndbout_c("NdbEventOperationImpl::getValue attribute %s not found",colName);
+ DBUG_RETURN(NULL);
+ }
+
+ DBUG_RETURN(NdbEventOperationImpl::getValue(tAttrInfo, aValue, n));
+}
+
+NdbRecAttr*
+NdbEventOperationImpl::getValue(const NdbColumnImpl *tAttrInfo, char *aValue, int n)
+{
+ DBUG_ENTER("NdbEventOperationImpl::getValue");
+ // Insert Attribute Id into ATTRINFO part.
+
+ NdbRecAttr **theFirstAttr;
+ NdbRecAttr **theCurrentAttr;
+
+ if (tAttrInfo->getPrimaryKey())
+ {
+ theFirstAttr = &theFirstPkAttrs[n];
+ theCurrentAttr = &theCurrentPkAttrs[n];
+ }
+ else
+ {
+ theFirstAttr = &theFirstDataAttrs[n];
+ theCurrentAttr = &theCurrentDataAttrs[n];
+ }
+
+ /************************************************************************
+ * Get a Receive Attribute object and link it into the operation object.
+ ************************************************************************/
+ NdbRecAttr *tAttr = m_ndb->getRecAttr();
+ if (tAttr == NULL) {
+ exit(-1);
+ //setErrorCodeAbort(4000);
+ DBUG_RETURN(NULL);
+ }
+
+ /**********************************************************************
+ * Now set the attribute identity and the pointer to the data in
+ * the RecAttr object
+ * Also set attribute size, array size and attribute type
+ ********************************************************************/
+ if (tAttr->setup(tAttrInfo, aValue)) {
+ //setErrorCodeAbort(4000);
+ m_ndb->releaseRecAttr(tAttr);
+ exit(-1);
+ DBUG_RETURN(NULL);
+ }
+ //theErrorLine++;
+
+ tAttr->setUNDEFINED();
+
+ // We want to keep the list sorted to make data insertion easier later
+
+ if (*theFirstAttr == NULL) {
+ *theFirstAttr = tAttr;
+ *theCurrentAttr = tAttr;
+ tAttr->next(NULL);
+ } else {
+ Uint32 tAttrId = tAttrInfo->m_attrId;
+ if (tAttrId > (*theCurrentAttr)->attrId()) { // right order
+ (*theCurrentAttr)->next(tAttr);
+ tAttr->next(NULL);
+ *theCurrentAttr = tAttr;
+ } else if ((*theFirstAttr)->next() == NULL || // only one in list
+ (*theFirstAttr)->attrId() > tAttrId) {// or first
+ tAttr->next(*theFirstAttr);
+ *theFirstAttr = tAttr;
+ } else { // at least 2 in list and not first and not last
+ NdbRecAttr *p = *theFirstAttr;
+ NdbRecAttr *p_next = p->next();
+ while (tAttrId > p_next->attrId()) {
+ p = p_next;
+ p_next = p->next();
+ }
+ if (tAttrId == p_next->attrId()) { // Using same attribute twice
+ tAttr->release(); // do I need to do this?
+ m_ndb->releaseRecAttr(tAttr);
+ exit(-1);
+ DBUG_RETURN(NULL);
+ }
+ // this is it, between p and p_next
+ p->next(tAttr);
+ tAttr->next(p_next);
+ }
+ }
+ DBUG_RETURN(tAttr);
+}
+
+int
+NdbEventOperationImpl::execute()
+{
+ DBUG_ENTER("NdbEventOperationImpl::execute");
+ NdbDictionary::Dictionary *myDict = m_ndb->getDictionary();
+ if (!myDict) {
+ m_error.code= m_ndb->getNdbError().code;
+ DBUG_RETURN(-1);
+ }
+
+ if (theFirstPkAttrs[0] == NULL &&
+ theFirstDataAttrs[0] == NULL) { // defaults to get all
+
+ }
+
+ NdbDictionaryImpl & myDictImpl = NdbDictionaryImpl::getImpl(*myDict);
+
+
+ int hasSubscriber;
+ int r= m_bufferHandle->prepareAddSubscribeEvent(this,
+ hasSubscriber /*return value*/);
+ m_error.code= 4709;
+
+ if (r < 0)
+ {
+ DBUG_RETURN(-1);
+ }
+
+ m_eventImpl->m_bufferId = m_bufferId = (Uint32)r;
+
+ r = -1;
+ if (m_bufferId >= 0) {
+ // now we check if there's already a subscriber
+
+ if (hasSubscriber == 0) { // only excute if there's no other subscribers
+ r = myDictImpl.executeSubscribeEvent(*m_eventImpl);
+ } else {
+ r = 0;
+ }
+ if (r) {
+ //Error
+ m_bufferHandle->unprepareAddSubscribeEvent(m_bufferId);
+ m_state = EO_ERROR;
+ } else {
+ m_bufferHandle->addSubscribeEvent(m_bufferId, this);
+ m_state = EO_EXECUTING;
+ }
+ } else {
+ //Error
+ m_state = EO_ERROR;
+ }
+ DBUG_RETURN(r);
+}
+
+int
+NdbEventOperationImpl::stop()
+{
+ DBUG_ENTER("NdbEventOperationImpl::stop");
+ if (m_state != EO_EXECUTING)
+ {
+ DBUG_RETURN(-1);
+ }
+
+ // ndbout_c("NdbEventOperation::stopping()");
+
+ NdbDictionary::Dictionary *myDict = m_ndb->getDictionary();
+ if (!myDict) {
+ m_error.code= m_ndb->getNdbError().code;
+ DBUG_RETURN(-1);
+ }
+
+ NdbDictionaryImpl & myDictImpl = NdbDictionaryImpl::getImpl(*myDict);
+
+ int hasSubscriber;
+ int ret =
+ m_bufferHandle->prepareDropSubscribeEvent(m_bufferId,
+ hasSubscriber /* return value */);
+
+ if (ret < 0) {
+ m_error.code= 4712;
+ DBUG_RETURN(-1);
+ }
+ // m_eventImpl->m_bufferId = m_bufferId;
+
+ int r = -1;
+
+ if (hasSubscriber == 0) { // only excute if there's no other subscribers
+ r = myDictImpl.stopSubscribeEvent(*m_eventImpl);
+#ifdef EVENT_DEBUG
+ ndbout_c("NdbEventOperation::stopping() done");
+#endif
+ } else
+ r = 0;
+
+ if (r) {
+ //Error
+ m_bufferHandle->unprepareDropSubscribeEvent(m_bufferId);
+ m_error.code= myDictImpl.m_error.code;
+ m_state = EO_ERROR;
+ } else {
+#ifdef EVENT_DEBUG
+ ndbout_c("NdbEventOperation::dropping()");
+#endif
+ m_bufferHandle->dropSubscribeEvent(m_bufferId);
+ m_state = EO_CREATED;
+ }
+
+ DBUG_RETURN(r);
+}
+
+bool
+NdbEventOperationImpl::isConsistent()
+{
+ return sdata->isGCIConsistent();
+}
+
+Uint32
+NdbEventOperationImpl::getGCI()
+{
+ return sdata->gci;
+}
+
+Uint32
+NdbEventOperationImpl::getLatestGCI()
+{
+ return NdbGlobalEventBufferHandle::getLatestGCI();
+}
+
+int
+NdbEventOperationImpl::next(int *pOverrun)
+{
+ DBUG_ENTER("NdbEventOperationImpl::next");
+ int nr = 10000; // a high value
+ int tmpOverrun = 0;
+ int *ptmpOverrun;
+ if (pOverrun) {
+ ptmpOverrun = &tmpOverrun;
+ } else
+ ptmpOverrun = NULL;
+
+ while (nr > 0) {
+ int r=NdbGlobalEventBufferHandle::getDataL(m_bufferId, sdata,
+ ptr, pOverrun);
+ if (pOverrun) {
+ tmpOverrun += *pOverrun;
+ *pOverrun = tmpOverrun;
+ }
+
+ if (r <= 0)
+ {
+ DBUG_RETURN(r); // no data
+ }
+
+ if (r < nr) r = nr; else nr--; // we don't want to be stuck here forever
+
+#ifdef EVENT_DEBUG
+ ndbout_c("!!!!!!!sdata->operation %u", (Uint32)sdata->operation);
+#endif
+
+ // now move the data into the RecAttrs
+ if ((theFirstPkAttrs[0] == NULL) &&
+ (theFirstPkAttrs[1] == NULL) &&
+ (theFirstDataAttrs[0] == NULL) &&
+ (theFirstDataAttrs[1] == NULL))
+ {
+ DBUG_RETURN(r);
+ }
+ // no copying since no RecAttr's
+
+
+ Uint32 *aAttrPtr = ptr[0].p;
+ Uint32 *aAttrEndPtr = aAttrPtr + ptr[0].sz;
+ Uint32 *aDataPtr = ptr[1].p;
+
+#ifdef EVENT_DEBUG
+ int i;
+ printf("after values sz=%u\n", ptr[1].sz);
+ for(i=0; i < (int)ptr[1].sz; i++)
+ printf ("H'%.8X ",ptr[1].p[i]);
+ printf("\n");
+ printf("before values sz=%u\n", ptr[2].sz);
+ for(i=0; i < (int)ptr[2].sz; i++)
+ printf ("H'%.8X ",ptr[2].p[i]);
+ printf("\n");
+#endif
+
+ // copy data into the RecAttr's
+ // we assume that the respective attribute lists are sorted
+
+ // first the pk's
+ {
+ NdbRecAttr *tAttr= theFirstPkAttrs[0];
+ while(tAttr)
+ {
+ assert(aAttrPtr < aAttrEndPtr);
+ unsigned tDataSz= AttributeHeader(*aAttrPtr).getDataSize();
+ assert(tAttr->attrId() ==
+ AttributeHeader(*aAttrPtr).getAttributeId());
+ assert(tAttr->receive_data(aDataPtr, tDataSz));
+ // next
+ aAttrPtr++;
+ aDataPtr+= tDataSz;
+ tAttr= tAttr->next();
+ }
+ }
+
+ NdbRecAttr *tWorkingRecAttr = theFirstDataAttrs[0];
+
+ Uint32 tRecAttrId;
+ Uint32 tAttrId;
+ Uint32 tDataSz;
+ int hasSomeData=0;
+ while ((aAttrPtr < aAttrEndPtr) && (tWorkingRecAttr != NULL)) {
+ tRecAttrId = tWorkingRecAttr->attrId();
+ tAttrId = AttributeHeader(*aAttrPtr).getAttributeId();
+ tDataSz = AttributeHeader(*aAttrPtr).getDataSize();
+
+ while (tAttrId > tRecAttrId) {
+ //printf("[%u] %u %u [%u]\n", tAttrId, tDataSz, *aDataPtr, tRecAttrId);
+ tWorkingRecAttr->setUNDEFINED();
+ tWorkingRecAttr = tWorkingRecAttr->next();
+ if (tWorkingRecAttr == NULL)
+ break;
+ tRecAttrId = tWorkingRecAttr->attrId();
+ }
+ if (tWorkingRecAttr == NULL)
+ break;
+
+ //printf("[%u] %u %u [%u]\n", tAttrId, tDataSz, *aDataPtr, tRecAttrId);
+
+ if (tAttrId == tRecAttrId) {
+ hasSomeData++;
+
+ //printf("set!\n");
+
+ assert(tWorkingRecAttr->receive_data(aDataPtr, tDataSz));
+ tWorkingRecAttr = tWorkingRecAttr->next();
+ }
+ aAttrPtr++;
+ aDataPtr += tDataSz;
+ }
+
+ while (tWorkingRecAttr != NULL) {
+ tRecAttrId = tWorkingRecAttr->attrId();
+ //printf("set undefined [%u] %u %u [%u]\n", tAttrId, tDataSz, *aDataPtr, tRecAttrId);
+ tWorkingRecAttr->setUNDEFINED();
+ tWorkingRecAttr = tWorkingRecAttr->next();
+ }
+
+ tWorkingRecAttr = theFirstDataAttrs[1];
+ aDataPtr = ptr[2].p;
+ Uint32 *aDataEndPtr = aDataPtr + ptr[2].sz;
+ while ((aDataPtr < aDataEndPtr) && (tWorkingRecAttr != NULL)) {
+ tRecAttrId = tWorkingRecAttr->attrId();
+ tAttrId = AttributeHeader(*aDataPtr).getAttributeId();
+ tDataSz = AttributeHeader(*aDataPtr).getDataSize();
+ aDataPtr++;
+ while (tAttrId > tRecAttrId) {
+ tWorkingRecAttr->setUNDEFINED();
+ tWorkingRecAttr = tWorkingRecAttr->next();
+ if (tWorkingRecAttr == NULL)
+ break;
+ tRecAttrId = tWorkingRecAttr->attrId();
+ }
+ if (tWorkingRecAttr == NULL)
+ break;
+ if (tAttrId == tRecAttrId) {
+ assert(!m_eventImpl->m_tableImpl->getColumn(tRecAttrId)->getPrimaryKey());
+ hasSomeData++;
+
+ assert(tWorkingRecAttr->receive_data(aDataPtr, tDataSz));
+ tWorkingRecAttr = tWorkingRecAttr->next();
+ }
+ aDataPtr += tDataSz;
+ }
+ while (tWorkingRecAttr != NULL) {
+ tWorkingRecAttr->setUNDEFINED();
+ tWorkingRecAttr = tWorkingRecAttr->next();
+ }
+
+ if (hasSomeData)
+ {
+ DBUG_RETURN(r);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+NdbDictionary::Event::TableEvent
+NdbEventOperationImpl::getEventType()
+{
+ switch (sdata->operation) {
+ case TriggerEvent::TE_INSERT:
+ return NdbDictionary::Event::TE_INSERT;
+ case TriggerEvent::TE_DELETE:
+ return NdbDictionary::Event::TE_DELETE;
+ case TriggerEvent::TE_UPDATE:
+ return NdbDictionary::Event::TE_UPDATE;
+ default:
+ return NdbDictionary::Event::TE_ALL;
+ }
+}
+
+
+
+void
+NdbEventOperationImpl::print()
+{
+ int i;
+ ndbout << "EventId " << m_eventId << "\n";
+
+ for (i = 0; i < 2; i++) {
+ NdbRecAttr *p = theFirstPkAttrs[i];
+ ndbout << " %u " << i;
+ while (p) {
+ ndbout << " : " << p->attrId() << " = " << *p;
+ p = p->next();
+ }
+ ndbout << "\n";
+ }
+ for (i = 0; i < 2; i++) {
+ NdbRecAttr *p = theFirstDataAttrs[i];
+ ndbout << " %u " << i;
+ while (p) {
+ ndbout << " : " << p->attrId() << " = " << *p;
+ p = p->next();
+ }
+ ndbout << "\n";
+ }
+}
+
+void
+NdbEventOperationImpl::printAll()
+{
+ Uint32 *aAttrPtr = ptr[0].p;
+ Uint32 *aAttrEndPtr = aAttrPtr + ptr[0].sz;
+ Uint32 *aDataPtr = ptr[1].p;
+
+ //tRecAttr->setup(tAttrInfo, aValue)) {
+
+ Uint32 tAttrId;
+ Uint32 tDataSz;
+ for (; aAttrPtr < aAttrEndPtr; ) {
+ tAttrId = AttributeHeader(*aAttrPtr).getAttributeId();
+ tDataSz = AttributeHeader(*aAttrPtr).getDataSize();
+
+ aAttrPtr++;
+ aDataPtr += tDataSz;
+ }
+}
+
+
+int NdbEventOperationImpl::wait(void *p, int aMillisecondNumber)
+{
+ return ((NdbGlobalEventBufferHandle*)p)->wait(aMillisecondNumber);
+}
+
+/*
+ * Global variable ndbGlobalEventBuffer
+ * Class NdbGlobalEventBufferHandle
+ * Class NdbGlobalEventBuffer
+ *
+ */
+
+#define ADD_DROP_LOCK_GUARDR(TYPE, FN) \
+{ \
+ ndbGlobalEventBuffer->add_drop_lock(); \
+ ndbGlobalEventBuffer->lock(); \
+ TYPE r = ndbGlobalEventBuffer->FN; \
+ ndbGlobalEventBuffer->unlock(); \
+ if (r < 0) { \
+ ndbGlobalEventBuffer->add_drop_unlock(); \
+ } \
+ return r;\
+}
+#define GUARDR(TYPE, FN) \
+{ \
+ ndbGlobalEventBuffer->lock(); \
+ TYPE r = ndbGlobalEventBuffer->FN; \
+ ndbGlobalEventBuffer->unlock(); \
+ return r;\
+}
+#define GUARD(FN) \
+{ \
+ ndbGlobalEventBuffer->lock(); \
+ ndbGlobalEventBuffer->FN; \
+ ndbGlobalEventBuffer->unlock(); \
+}
+#define ADD_DROP_UNLOCK_GUARD(FN) \
+{ \
+ GUARD(FN); \
+ ndbGlobalEventBuffer->add_drop_unlock(); \
+}
+#define GUARDBLOCK(BLOCK) \
+{ \
+ ndbGlobalEventBuffer->lock(); \
+ BLOCK \
+ ndbGlobalEventBuffer->unlock(); \
+}
+
+/*
+ * Global variable ndbGlobalEventBuffer
+ *
+ */
+
+extern NdbMutex * ndb_global_event_buffer_mutex;
+static NdbGlobalEventBuffer *ndbGlobalEventBuffer=NULL;
+
+/*
+ * Class NdbGlobalEventBufferHandle
+ * Each Ndb object has a Handle. This Handle is used to access the
+ * global NdbGlobalEventBuffer instance ndbGlobalEventBuffer
+ */
+
+NdbGlobalEventBufferHandle *
+NdbGlobalEventBuffer_init(int n)
+{
+ return new NdbGlobalEventBufferHandle(n);
+ // return NdbGlobalEventBufferHandle::init(n);
+}
+
+void
+NdbGlobalEventBuffer_drop(NdbGlobalEventBufferHandle *h)
+{
+ delete h;
+}
+
+NdbGlobalEventBufferHandle::NdbGlobalEventBufferHandle
+(int MAX_NUMBER_ACTIVE_EVENTS) : m_bufferL(0), m_nids(0)
+{
+ if ((p_cond = NdbCondition_Create()) == NULL) {
+ ndbout_c("NdbGlobalEventBufferHandle: NdbCondition_Create() failed");
+ exit(-1);
+ }
+
+ NdbMutex_Lock(ndb_global_event_buffer_mutex);
+ if (ndbGlobalEventBuffer == NULL) {
+ if (ndbGlobalEventBuffer == NULL) {
+ ndbGlobalEventBuffer = new NdbGlobalEventBuffer();
+ if (!ndbGlobalEventBuffer) {
+ NdbMutex_Unlock(ndb_global_event_buffer_mutex);
+ ndbout_c("NdbGlobalEventBufferHandle:: failed to allocate ndbGlobalEventBuffer");
+ exit(-1);
+ }
+ }
+ }
+ NdbMutex_Unlock(ndb_global_event_buffer_mutex);
+
+ GUARD(real_init(this,MAX_NUMBER_ACTIVE_EVENTS));
+}
+
+NdbGlobalEventBufferHandle::~NdbGlobalEventBufferHandle()
+{
+ NdbCondition_Destroy(p_cond);
+
+ ndbGlobalEventBuffer->lock();
+ ndbGlobalEventBuffer->real_remove(this);
+ ndbGlobalEventBuffer->unlock();
+
+ NdbMutex_Lock(ndb_global_event_buffer_mutex);
+ if (ndbGlobalEventBuffer->m_handlers.size() == 0) {
+ delete ndbGlobalEventBuffer;
+ ndbGlobalEventBuffer = NULL;
+ }
+ NdbMutex_Unlock(ndb_global_event_buffer_mutex);
+}
+
+void
+NdbGlobalEventBufferHandle::addBufferId(int bufferId)
+{
+ DBUG_ENTER("NdbGlobalEventBufferHandle::addBufferId");
+ DBUG_PRINT("enter",("bufferId=%d",bufferId));
+ if (m_nids >= NDB_MAX_ACTIVE_EVENTS) {
+ ndbout_c("NdbGlobalEventBufferHandle::addBufferId error in paramerer setting");
+ exit(-1);
+ }
+ m_bufferIds[m_nids] = bufferId;
+ m_nids++;
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbGlobalEventBufferHandle::dropBufferId(int bufferId)
+{
+ DBUG_ENTER("NdbGlobalEventBufferHandle::dropBufferId");
+ DBUG_PRINT("enter",("bufferId=%d",bufferId));
+ for (int i = 0; i < m_nids; i++)
+ if (m_bufferIds[i] == bufferId) {
+ m_nids--;
+ for (; i < m_nids; i++)
+ m_bufferIds[i] = m_bufferIds[i+1];
+ DBUG_VOID_RETURN;
+ }
+ ndbout_c("NdbGlobalEventBufferHandle::dropBufferId %d does not exist",
+ bufferId);
+ exit(-1);
+}
+/*
+NdbGlobalEventBufferHandle *
+NdbGlobalEventBufferHandle::init (int MAX_NUMBER_ACTIVE_EVENTS)
+{
+ return new NdbGlobalEventBufferHandle();
+}
+void
+NdbGlobalEventBufferHandle::drop(NdbGlobalEventBufferHandle *handle)
+{
+ delete handle;
+}
+*/
+int
+NdbGlobalEventBufferHandle::prepareAddSubscribeEvent
+(NdbEventOperationImpl *eventOp, int& hasSubscriber)
+{
+ ADD_DROP_LOCK_GUARDR(int,real_prepareAddSubscribeEvent(this, eventOp,
+ hasSubscriber));
+}
+void
+NdbGlobalEventBufferHandle::addSubscribeEvent
+(int bufferId, NdbEventOperationImpl *ndbEventOperationImpl)
+{
+ ADD_DROP_UNLOCK_GUARD(real_addSubscribeEvent(bufferId, ndbEventOperationImpl));
+}
+void
+NdbGlobalEventBufferHandle::unprepareAddSubscribeEvent(int bufferId)
+{
+ ADD_DROP_UNLOCK_GUARD(real_unprepareAddSubscribeEvent(bufferId));
+}
+
+int
+NdbGlobalEventBufferHandle::prepareDropSubscribeEvent(int bufferId,
+ int& hasSubscriber)
+{
+ ADD_DROP_LOCK_GUARDR(int,real_prepareDropSubscribeEvent(bufferId, hasSubscriber));
+}
+
+void
+NdbGlobalEventBufferHandle::unprepareDropSubscribeEvent(int bufferId)
+{
+ ADD_DROP_UNLOCK_GUARD(real_unprepareDropSubscribeEvent(bufferId));
+}
+
+void
+NdbGlobalEventBufferHandle::dropSubscribeEvent(int bufferId)
+{
+ ADD_DROP_UNLOCK_GUARD(real_dropSubscribeEvent(bufferId));
+}
+
+int
+NdbGlobalEventBufferHandle::insertDataL(int bufferId,
+ const SubTableData * const sdata,
+ LinearSectionPtr ptr[3])
+{
+ GUARDR(int,real_insertDataL(bufferId,sdata,ptr));
+}
+
+void
+NdbGlobalEventBufferHandle::latestGCI(int bufferId, Uint32 gci)
+{
+ GUARD(real_latestGCI(bufferId,gci));
+}
+
+Uint32
+NdbGlobalEventBufferHandle::getLatestGCI()
+{
+ GUARDR(Uint32, real_getLatestGCI());
+}
+
+inline void
+NdbGlobalEventBufferHandle::group_lock()
+{
+ ndbGlobalEventBuffer->group_lock();
+}
+
+inline void
+NdbGlobalEventBufferHandle::group_unlock()
+{
+ ndbGlobalEventBuffer->group_unlock();
+}
+
+int
+NdbGlobalEventBufferHandle::wait(int aMillisecondNumber)
+{
+ GUARDR(int, real_wait(this, aMillisecondNumber));
+}
+
+int NdbGlobalEventBufferHandle::getDataL(const int bufferId,
+ SubTableData * &sdata,
+ LinearSectionPtr ptr[3],
+ int *pOverrun)
+{
+ GUARDR(int,real_getDataL(bufferId,sdata,ptr,pOverrun));
+}
+
+/*
+ * Class NdbGlobalEventBuffer
+ *
+ *
+ */
+
+
+void
+NdbGlobalEventBuffer::lock()
+{
+ if (!m_group_lock_flag)
+ NdbMutex_Lock(ndb_global_event_buffer_mutex);
+}
+void
+NdbGlobalEventBuffer::unlock()
+{
+ if (!m_group_lock_flag)
+ NdbMutex_Unlock(ndb_global_event_buffer_mutex);
+}
+void
+NdbGlobalEventBuffer::add_drop_lock()
+{
+ NdbMutex_Lock(p_add_drop_mutex);
+}
+void
+NdbGlobalEventBuffer::add_drop_unlock()
+{
+ NdbMutex_Unlock(p_add_drop_mutex);
+}
+inline void
+NdbGlobalEventBuffer::group_lock()
+{
+ lock();
+ m_group_lock_flag = 1;
+}
+
+inline void
+NdbGlobalEventBuffer::group_unlock()
+{
+ m_group_lock_flag = 0;
+ unlock();
+}
+
+void
+NdbGlobalEventBuffer::lockB(int bufferId)
+{
+ NdbMutex_Lock(m_buf[ID(bufferId)].p_buf_mutex);
+}
+void
+NdbGlobalEventBuffer::unlockB(int bufferId)
+{
+ NdbMutex_Lock(m_buf[ID(bufferId)].p_buf_mutex);
+}
+
+// Private methods
+
+NdbGlobalEventBuffer::NdbGlobalEventBuffer() :
+ m_handlers(),
+ m_group_lock_flag(0),
+ m_latestGCI(0),
+ m_no(0) // must start at ZERO!
+{
+ if ((p_add_drop_mutex = NdbMutex_Create()) == NULL) {
+ ndbout_c("NdbGlobalEventBuffer: NdbMutex_Create() failed");
+ exit(-1);
+ }
+}
+
+NdbGlobalEventBuffer::~NdbGlobalEventBuffer()
+{
+ NdbMutex_Destroy(p_add_drop_mutex);
+ // NdbMem_Deallocate(m_eventBufferIdToEventId);
+}
+void
+NdbGlobalEventBuffer::real_init (NdbGlobalEventBufferHandle *h,
+ int MAX_NUMBER_ACTIVE_EVENTS)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_init");
+ DBUG_PRINT("enter",("m_handles.size()=%u %u", m_handlers.size(), h));
+ if (m_handlers.size() == 0)
+ { // First init
+ DBUG_PRINT("info",("first to come"));
+ m_max = MAX_NUMBER_ACTIVE_EVENTS;
+ m_buf = new BufItem[m_max];
+ for (int i=0; i<m_max; i++) {
+ m_buf[i].gId= 0;
+ }
+ }
+ assert(m_max == MAX_NUMBER_ACTIVE_EVENTS);
+ // TODO make sure we don't hit roof
+ m_handlers.push_back(h);
+ DBUG_VOID_RETURN;
+}
+void
+NdbGlobalEventBuffer::real_remove(NdbGlobalEventBufferHandle *h)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_remove");
+ DBUG_PRINT("enter",("m_handles.size()=%u %u", m_handlers.size(), h));
+ for (Uint32 i=0 ; i < m_handlers.size(); i++)
+ {
+ DBUG_PRINT("info",("m_handlers[%u] %u", i, m_handlers[i]));
+ if (m_handlers[i] == h)
+ {
+ m_handlers.erase(i);
+ if (m_handlers.size() == 0)
+ {
+ DBUG_PRINT("info",("last to go"));
+ delete[] m_buf;
+ m_buf = NULL;
+ }
+ DBUG_VOID_RETURN;
+ }
+ }
+ ndbout_c("NdbGlobalEventBuffer::real_remove() non-existing handle");
+ DBUG_PRINT("error",("non-existing handle"));
+ abort();
+ DBUG_VOID_RETURN;
+}
+
+int
+NdbGlobalEventBuffer::real_prepareAddSubscribeEvent
+(NdbGlobalEventBufferHandle *aHandle, NdbEventOperationImpl *eventOp,
+ int& hasSubscriber)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_prepareAddSubscribeEvent");
+ int i;
+ int bufferId= -1;
+ Uint32 eventId= eventOp->m_eventId;
+
+ DBUG_PRINT("enter",("eventId: %u", eventId));
+ // add_drop_lock(); // only one thread can do add or drop at a time
+
+ // Find place where eventId already set
+ for (i=0; i<m_no; i++) {
+ if (m_buf[i].gId == eventId) {
+ bufferId= i;
+ break;
+ }
+ }
+ if (bufferId < 0) {
+ // find space for new bufferId
+ for (i=0; i<m_no; i++) {
+ if (m_buf[i].gId == 0) {
+ bufferId= i; // we found an empty spot
+ goto found_bufferId;
+ }
+ }
+ if (bufferId < 0 &&
+ m_no < m_max) {
+ // room for more so get that
+ bufferId= m_no;
+ m_buf[m_no].gId= 0;
+ m_no++;
+ } else {
+ // add_drop_unlock();
+ DBUG_PRINT("error",("Can't accept more subscribers:"
+ " bufferId=%d, m_no=%d, m_max=%d",
+ bufferId, m_no, m_max));
+ DBUG_RETURN(-1);
+ }
+ }
+found_bufferId:
+
+ BufItem &b= m_buf[ID(bufferId)];
+
+ if (b.gId == 0) { // first subscriber needs some initialization
+
+ bufferId= NO_ID(0, bufferId);
+
+ b.gId= eventId;
+ b.eventType= (Uint32)eventOp->m_eventImpl->mi_type;
+
+ if ((b.p_buf_mutex= NdbMutex_Create()) == NULL) {
+ ndbout_c("NdbGlobalEventBuffer: NdbMutex_Create() failed");
+ abort();
+ }
+
+ b.subs= 0;
+ b.f= 0;
+ b.sz= 0;
+ b.max_sz= aHandle->m_bufferL;
+ b.data=
+ (BufItem::Data *)NdbMem_Allocate(b.max_sz*sizeof(BufItem::Data));
+ for (int i = 0; i < b.max_sz; i++) {
+ b.data[i].sdata= NULL;
+ b.data[i].ptr[0].p= NULL;
+ b.data[i].ptr[1].p= NULL;
+ b.data[i].ptr[2].p= NULL;
+ }
+ } else {
+ DBUG_PRINT("info",
+ ("TRYING handle one subscriber per event b.subs=%u",b.subs));
+ int ni = -1;
+ for(int i=0; i < b.subs;i++) {
+ if (b.ps[i].theHandle == NULL) {
+ ni = i;
+ break;
+ }
+ }
+ if (ni < 0) {
+ if (b.subs < MAX_SUBSCRIBERS_PER_EVENT) {
+ ni = b.subs;
+ } else {
+ DBUG_PRINT("error",
+ ("Can't accept more subscribers: b.subs=%d",b.subs));
+ // add_drop_unlock();
+ DBUG_RETURN(-1);
+ }
+ }
+ bufferId = NO_ID(ni, bufferId);
+ }
+
+ // initialize BufItem::Ps
+ {
+ int n = NO(bufferId);
+ NdbGlobalEventBuffer::BufItem::Ps &e = b.ps[n];
+ e.theHandle = aHandle;
+ e.b=0;
+ e.bufferempty = 1;
+ e.overrun=0; // set to -1 to handle first insert
+ }
+
+ if (b.subs > 0)
+ hasSubscriber = 1;
+ else
+ hasSubscriber = 0;
+
+ DBUG_PRINT("info",("handed out bufferId=%d for eventId=%d hasSubscriber=%d",
+ bufferId, eventId, hasSubscriber));
+
+ /* we now have a lock on the prepare so that no one can mess with this
+ * unlock comes in unprepareAddSubscribeEvent or addSubscribeEvent
+ */
+ DBUG_RETURN(bufferId);
+}
+
+void
+NdbGlobalEventBuffer::real_unprepareAddSubscribeEvent(int bufferId)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_unprepareAddSubscribeEvent");
+ BufItem &b = m_buf[ID(bufferId)];
+ int n = NO(bufferId);
+
+ DBUG_PRINT("enter", ("bufferId=%d,ID(bufferId)=%d,NO(bufferId)=%d",
+ bufferId, ID(bufferId), NO(bufferId)));
+
+ b.ps[n].theHandle = NULL;
+
+ // remove subscribers from the end,
+ // we have to keep gaps since the position
+ // has been handed out in bufferId
+ for (int i = b.subs-1; i >= 0; i--)
+ if (b.ps[i].theHandle == NULL)
+ b.subs--;
+ else
+ break;
+
+ if (b.subs == 0) {
+ DBUG_PRINT("info",("no more subscribers left on eventId %d", b.gId));
+ b.gId= 0; // We don't have any subscribers, reuse BufItem
+ if (b.data) {
+ NdbMem_Free((void *)b.data);
+ b.data = NULL;
+ }
+ if (b.p_buf_mutex) {
+ NdbMutex_Destroy(b.p_buf_mutex);
+ b.p_buf_mutex = NULL;
+ }
+ }
+ // add_drop_unlock();
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbGlobalEventBuffer::real_addSubscribeEvent(int bufferId,
+ void *ndbEventOperation)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_addSubscribeEvent");
+ BufItem &b = m_buf[ID(bufferId)];
+ int n = NO(bufferId);
+
+ b.subs++;
+ b.ps[n].theHandle->addBufferId(bufferId);
+
+ // add_drop_unlock();
+ DBUG_PRINT("info",("added bufferId %d", bufferId));
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbGlobalEventBuffer::real_unprepareDropSubscribeEvent(int bufferId)
+{
+ // add_drop_unlock(); // only one thread can do add or drop at a time
+}
+
+int
+NdbGlobalEventBuffer::real_prepareDropSubscribeEvent(int bufferId,
+ int& hasSubscriber)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_prepareDropSubscribeEvent");
+ // add_drop_lock(); // only one thread can do add or drop at a time
+
+ BufItem &b = m_buf[ID(bufferId)];
+
+ int n = 0;
+ for(int i=0; i < b.subs;i++) {
+ if (b.ps[i].theHandle != NULL)
+ n++;
+ }
+
+ if (n > 1)
+ hasSubscriber = 1;
+ else if (n == 1)
+ hasSubscriber = 0;
+ else
+ {
+ DBUG_RETURN(-1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+void
+NdbGlobalEventBuffer::real_dropSubscribeEvent(int bufferId)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_dropSubscribeEvent");
+ // add_drop_lock(); // only one thread can do add-drop at a time
+
+ BufItem &b = m_buf[ID(bufferId)];
+ int n = NO(bufferId);
+
+ b.ps[n].overrun=0;
+ b.ps[n].bufferempty=1;
+ b.ps[n].b=0;
+ b.ps[n].theHandle->dropBufferId(bufferId);
+
+ real_unprepareAddSubscribeEvent(bufferId); // does add_drop_unlock();
+
+#ifdef EVENT_DEBUG
+ ndbout_c("dropSubscribeEvent:: dropped bufferId %d", bufferId);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbGlobalEventBuffer::real_latestGCI(int bufferId, Uint32 gci)
+{
+ if (gci > m_latestGCI)
+ m_latestGCI = gci;
+ else if ((m_latestGCI-gci) > 0xffff) // If NDB stays up :-)
+ m_latestGCI = gci;
+}
+
+Uint32
+NdbGlobalEventBuffer::real_getLatestGCI()
+{
+ return m_latestGCI;
+}
+
+int
+NdbGlobalEventBuffer::real_insertDataL(int bufferId,
+ const SubTableData * const sdata,
+ LinearSectionPtr ptr[3])
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_insertDataL");
+ BufItem &b = m_buf[ID(bufferId)];
+#ifdef EVENT_DEBUG
+ int n = NO(bufferId);
+#endif
+
+ if ( b.eventType & (1 << (Uint32)sdata->operation) )
+ {
+ if (b.subs) {
+#ifdef EVENT_DEBUG
+ ndbout_c("data insertion in buffer %d with eventId %d", bufferId, b.gId);
+#endif
+ // move front forward
+ if (copy_data_alloc(sdata, ptr,
+ b.data[b.f].sdata, b.data[b.f].ptr))
+ {
+ DBUG_RETURN(-1);
+ }
+ for (int i=0; i < b.subs; i++) {
+ NdbGlobalEventBuffer::BufItem::Ps &e = b.ps[i];
+ if (e.theHandle) { // active subscriber
+ if (b.f == e.b) { // next-to-read == written
+ if (e.bufferempty == 0) {
+ e.overrun++; // another item has been overwritten
+ e.b++; // move next-to-read next since old item was overwritten
+ if (e.b == b.max_sz) e.b= 0; // start from beginning
+ }
+ }
+ e.bufferempty = 0;
+ // signal subscriber that there's more to get
+ NdbCondition_Signal(e.theHandle->p_cond);
+ }
+ }
+ b.f++; // move next-to-write
+ if (b.f == b.max_sz) b.f = 0; // start from beginning
+#ifdef EVENT_DEBUG
+ ndbout_c("Front= %d Back = %d overun = %d", b.f,
+ b.ps[n].b, b.ps[n].overrun);
+#endif
+ } else {
+#ifdef EVENT_DEBUG
+ ndbout_c("Data arrived before ready eventId", b.gId);
+#endif
+ }
+ }
+ else
+ {
+#ifdef EVENT_DEBUG
+ ndbout_c("skipped");
+#endif
+ }
+
+ DBUG_RETURN(0);
+}
+
+int NdbGlobalEventBuffer::hasData(int bufferId) {
+ DBUG_ENTER("NdbGlobalEventBuffer::hasData");
+ BufItem &b = m_buf[ID(bufferId)];
+ int n = NO(bufferId);
+ NdbGlobalEventBuffer::BufItem::Ps &e = b.ps[n];
+
+ if(e.bufferempty)
+ {
+ DBUG_RETURN(0);
+ }
+
+ if (b.f <= e.b)
+ {
+ DBUG_RETURN(b.max_sz-e.b + b.f);
+ }
+ else
+ {
+ DBUG_RETURN(b.f-e.b);
+ }
+}
+
+int NdbGlobalEventBuffer::real_getDataL(const int bufferId,
+ SubTableData * &sdata,
+ LinearSectionPtr ptr[3],
+ int *pOverrun)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_getDataL");
+ BufItem &b = m_buf[ID(bufferId)];
+ int n = NO(bufferId);
+ NdbGlobalEventBuffer::BufItem::Ps &e = b.ps[n];
+
+ if (pOverrun) {
+ *pOverrun = e.overrun;
+ e.overrun = 0; // if pOverrun is returned to user reset e.overrun
+ }
+
+ if (e.bufferempty)
+ {
+ DBUG_RETURN(0); // nothing to get
+ }
+
+ DBUG_PRINT("info",("ID(bufferId) %d NO(bufferId) %d e.b %d",
+ ID(bufferId), NO(bufferId), e.b));
+
+ if (copy_data_alloc(b.data[e.b].sdata, b.data[e.b].ptr,
+ sdata, ptr))
+ {
+ DBUG_RETURN(-1);
+ }
+
+ e.b++; if (e.b == b.max_sz) e.b= 0; // move next-to-read forward
+
+ if (b.f == e.b) // back has cought up with front
+ e.bufferempty = 1;
+
+#ifdef EVENT_DEBUG
+ ndbout_c("getting data from buffer %d with eventId %d", bufferId, b.gId);
+#endif
+
+ DBUG_RETURN(hasData(bufferId)+1);
+}
+int
+NdbGlobalEventBuffer::copy_data_alloc(const SubTableData * const f_sdata,
+ LinearSectionPtr f_ptr[3],
+ SubTableData * &t_sdata,
+ LinearSectionPtr t_ptr[3])
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::copy_data_alloc");
+ unsigned sz4= (sizeof(SubTableData)+3)>>2;
+ Uint32 *ptr= (Uint32*)NdbMem_Allocate((sz4 +
+ f_ptr[0].sz +
+ f_ptr[1].sz +
+ f_ptr[2].sz) * sizeof(Uint32));
+ if (t_sdata)
+ NdbMem_Free((char*)t_sdata);
+ t_sdata= (SubTableData *)ptr;
+ memcpy(t_sdata,f_sdata,sizeof(SubTableData));
+ ptr+= sz4;
+
+ for (int i = 0; i < 3; i++) {
+ LinearSectionPtr & f_p = f_ptr[i];
+ LinearSectionPtr & t_p = t_ptr[i];
+ if (f_p.sz > 0) {
+ t_p.p= (Uint32 *)ptr;
+ memcpy(t_p.p, f_p.p, sizeof(Uint32)*f_p.sz);
+ ptr+= f_p.sz;
+ t_p.sz= f_p.sz;
+ } else {
+ t_p.p= NULL;
+ t_p.sz= 0;
+ }
+ }
+ DBUG_RETURN(0);
+}
+int
+NdbGlobalEventBuffer::real_wait(NdbGlobalEventBufferHandle *h,
+ int aMillisecondNumber)
+{
+ DBUG_ENTER("NdbGlobalEventBuffer::real_wait");
+ // check if there are anything in any of the buffers
+ int i;
+ int n = 0;
+ for (i = 0; i < h->m_nids; i++)
+ n += hasData(h->m_bufferIds[i]);
+ if (n)
+ {
+ DBUG_RETURN(n);
+ }
+
+ int r = NdbCondition_WaitTimeout(h->p_cond, ndb_global_event_buffer_mutex,
+ aMillisecondNumber);
+ if (r > 0)
+ {
+ DBUG_RETURN(-1);
+ }
+
+ n = 0;
+ for (i = 0; i < h->m_nids; i++)
+ n += hasData(h->m_bufferIds[i]);
+ DBUG_RETURN(n);
+}
+
+template class Vector<NdbGlobalEventBufferHandle*>;
diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp
new file mode 100644
index 00000000000..96958979c76
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp
@@ -0,0 +1,204 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NdbEventOperationImpl_H
+#define NdbEventOperationImpl_H
+
+#include <NdbEventOperation.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <transporter/TransporterDefinitions.hpp>
+
+class NdbGlobalEventBufferHandle;
+class NdbEventOperationImpl : public NdbEventOperation {
+public:
+ NdbEventOperationImpl(NdbEventOperation &N,
+ Ndb *theNdb,
+ const char* eventName,
+ const int bufferLength);
+ ~NdbEventOperationImpl();
+
+ NdbEventOperation::State getState();
+
+ int execute();
+ int stop();
+ NdbRecAttr *getValue(const char *colName, char *aValue, int n);
+ NdbRecAttr *getValue(const NdbColumnImpl *, char *aValue, int n);
+ static int wait(void *p, int aMillisecondNumber);
+ int next(int *pOverrun);
+ bool isConsistent();
+ Uint32 getGCI();
+ Uint32 getLatestGCI();
+
+ NdbDictionary::Event::TableEvent getEventType();
+
+ /*
+ getOperation();
+ getGCI();
+ getLogType();
+ */
+
+ void print();
+ void printAll();
+
+ const NdbError & getNdbError() const;
+ NdbError m_error;
+
+ Ndb *m_ndb;
+ NdbEventImpl *m_eventImpl;
+ NdbGlobalEventBufferHandle *m_bufferHandle;
+
+ NdbRecAttr *theFirstPkAttrs[2];
+ NdbRecAttr *theCurrentPkAttrs[2];
+ NdbRecAttr *theFirstDataAttrs[2];
+ NdbRecAttr *theCurrentDataAttrs[2];
+
+ NdbEventOperation::State m_state;
+ Uint32 m_eventId;
+ int m_bufferId;
+ int m_bufferL;
+ SubTableData *sdata;
+ LinearSectionPtr ptr[3];
+};
+
+class NdbGlobalEventBuffer;
+class NdbGlobalEventBufferHandle {
+public:
+ NdbGlobalEventBufferHandle (int MAX_NUMBER_ACTIVE_EVENTS);
+ ~NdbGlobalEventBufferHandle ();
+ //static NdbGlobalEventBufferHandle *init(int MAX_NUMBER_ACTIVE_EVENTS);
+
+ // returns bufferId 0-N if ok otherwise -1
+ int prepareAddSubscribeEvent(NdbEventOperationImpl *, int& hasSubscriber);
+ void unprepareAddSubscribeEvent(int bufferId);
+ void addSubscribeEvent(int bufferId,
+ NdbEventOperationImpl *ndbEventOperationImpl);
+
+ void unprepareDropSubscribeEvent(int bufferId);
+ int prepareDropSubscribeEvent(int bufferId, int& hasSubscriber);
+ void dropSubscribeEvent(int bufferId);
+
+ static int getDataL(const int bufferId,
+ SubTableData * &sdata,
+ LinearSectionPtr ptr[3],
+ int *pOverrun);
+ static int insertDataL(int bufferId,
+ const SubTableData * const sdata,
+ LinearSectionPtr ptr[3]);
+ static void latestGCI(int bufferId, Uint32 gci);
+ static Uint32 getLatestGCI();
+ static Uint32 getEventId(int bufferId);
+
+ void group_lock();
+ void group_unlock();
+ int wait(int aMillisecondNumber);
+ int m_bufferL;
+private:
+ friend class NdbGlobalEventBuffer;
+ void addBufferId(int bufferId);
+ void dropBufferId(int bufferId);
+
+ struct NdbCondition *p_cond;
+ int m_nids;
+ int m_bufferIds[NDB_MAX_ACTIVE_EVENTS];
+};
+
+class NdbGlobalEventBuffer {
+private:
+ friend class NdbGlobalEventBufferHandle;
+ void lockB(int bufferId);
+ void unlockB(int bufferId);
+ void group_lock();
+ void group_unlock();
+ void lock();
+ void unlock();
+ void add_drop_lock();
+ void add_drop_unlock();
+
+ NdbGlobalEventBuffer();
+ ~NdbGlobalEventBuffer();
+
+ void real_remove(NdbGlobalEventBufferHandle *h);
+ void real_init(NdbGlobalEventBufferHandle *h,
+ int MAX_NUMBER_ACTIVE_EVENTS);
+
+ int real_prepareAddSubscribeEvent(NdbGlobalEventBufferHandle *h,
+ NdbEventOperationImpl *,
+ int& hasSubscriber);
+ void real_unprepareAddSubscribeEvent(int bufferId);
+ void real_addSubscribeEvent(int bufferId, void *ndbEventOperation);
+
+ void real_unprepareDropSubscribeEvent(int bufferId);
+ int real_prepareDropSubscribeEvent(int bufferId,
+ int& hasSubscriber);
+ void real_dropSubscribeEvent(int bufferId);
+
+ int real_getDataL(const int bufferId,
+ SubTableData * &sdata,
+ LinearSectionPtr ptr[3],
+ int *pOverrun);
+ int real_insertDataL(int bufferId,
+ const SubTableData * const sdata,
+ LinearSectionPtr ptr[3]);
+ void real_latestGCI(int bufferId, Uint32 gci);
+ Uint32 real_getLatestGCI();
+ int copy_data_alloc(const SubTableData * const f_sdata,
+ LinearSectionPtr f_ptr[3],
+ SubTableData * &t_sdata,
+ LinearSectionPtr t_ptr[3]);
+
+ int real_wait(NdbGlobalEventBufferHandle *, int aMillisecondNumber);
+ int hasData(int bufferId);
+ int ID (int bufferId) {return bufferId & 0xFF;};
+ int NO (int bufferId) {return bufferId >> 16;};
+ int NO_ID (int n, int bufferId) {return (n << 16) | ID(bufferId);};
+
+ Vector<NdbGlobalEventBufferHandle*> m_handlers;
+
+ // Global Mutex used for some things
+ NdbMutex *p_add_drop_mutex;
+
+ int m_group_lock_flag;
+ Uint32 m_latestGCI;
+
+ int m_no;
+ int m_max;
+#define MAX_SUBSCRIBERS_PER_EVENT 16
+ struct BufItem {
+ // local mutex for each event/buffer
+ NdbMutex *p_buf_mutex;
+ Uint32 gId;
+ Uint32 eventType;
+ struct Data {
+ SubTableData *sdata;
+ LinearSectionPtr ptr[3];
+ } * data;
+
+ struct Ps {
+ NdbGlobalEventBufferHandle *theHandle;
+ int b;
+ int overrun;
+ int bufferempty;
+ //void *ndbEventOperation;
+ } ps[MAX_SUBSCRIBERS_PER_EVENT]; // only supports 1 subscriber so far
+
+ int subs;
+ int f;
+ int sz;
+ int max_sz;
+ };
+ BufItem *m_buf;
+};
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbImpl.hpp b/storage/ndb/src/ndbapi/NdbImpl.hpp
new file mode 100644
index 00000000000..d649b39c5eb
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbImpl.hpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_IMPL_HPP
+#define NDB_IMPL_HPP
+
+#include <ndb_global.h>
+#include <Ndb.hpp>
+#include <NdbOut.hpp>
+#include <NdbError.hpp>
+#include <NdbCondition.h>
+#include <NdbReceiver.hpp>
+#include <NdbOperation.hpp>
+#include <kernel/ndb_limits.h>
+
+#include <NdbTick.h>
+
+#include "ndb_cluster_connection_impl.hpp"
+#include "NdbDictionaryImpl.hpp"
+#include "ObjectMap.hpp"
+
+/**
+ * Private parts of the Ndb object (corresponding to Ndb.hpp in public API)
+ */
+class NdbImpl {
+public:
+ NdbImpl(Ndb_cluster_connection *, Ndb&);
+ ~NdbImpl();
+
+ Ndb_cluster_connection_impl &m_ndb_cluster_connection;
+
+ NdbDictionaryImpl m_dictionary;
+
+ // Ensure good distribution of connects
+ Uint32 theCurrentConnectIndex;
+ Ndb_cluster_connection_node_iter m_node_iter;
+
+ NdbObjectIdMap theNdbObjectIdMap;
+
+ Uint32 theNoOfDBnodes; // The number of DB nodes
+ Uint8 theDBnodes[MAX_NDB_NODES]; // The node number of the DB nodes
+
+ // 1 indicates to release all connections to node
+ Uint32 the_release_ind[MAX_NDB_NODES];
+
+ NdbWaiter theWaiter;
+
+ int m_optimized_node_selection;
+};
+
+#ifdef VM_TRACE
+#define TRACE_DEBUG(x) ndbout << x << endl;
+#else
+#define TRACE_DEBUG(x)
+#endif
+
+#define CHECK_STATUS_MACRO \
+ {if (checkInitState() == -1) { theError.code = 4100; return -1;}}
+#define CHECK_STATUS_MACRO_VOID \
+ {if (checkInitState() == -1) { theError.code = 4100; return;}}
+#define CHECK_STATUS_MACRO_ZERO \
+ {if (checkInitState() == -1) { theError.code = 4100; return 0;}}
+#define CHECK_STATUS_MACRO_NULL \
+ {if (checkInitState() == -1) { theError.code = 4100; return NULL;}}
+
+inline
+void *
+Ndb::int2void(Uint32 val){
+ return theImpl->theNdbObjectIdMap.getObject(val);
+}
+
+inline
+NdbReceiver *
+Ndb::void2rec(void* val){
+ return (NdbReceiver*)val;
+}
+
+inline
+NdbTransaction *
+Ndb::void2con(void* val){
+ return (NdbTransaction*)val;
+}
+
+inline
+NdbOperation*
+Ndb::void2rec_op(void* val){
+ return (NdbOperation*)(void2rec(val)->getOwner());
+}
+
+inline
+NdbIndexOperation*
+Ndb::void2rec_iop(void* val){
+ return (NdbIndexOperation*)(void2rec(val)->getOwner());
+}
+
+inline
+NdbTransaction *
+NdbReceiver::getTransaction(){
+ return ((NdbOperation*)m_owner)->theNdbCon;
+}
+
+
+inline
+int
+Ndb::checkInitState()
+{
+ theError.code = 0;
+
+ if (theInitState != Initialised)
+ return -1;
+ return 0;
+}
+
+Uint32 convertEndian(Uint32 Data);
+
+enum LockMode {
+ Read,
+ Update,
+ Insert,
+ Delete
+};
+
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbIndexOperation.cpp b/storage/ndb/src/ndbapi/NdbIndexOperation.cpp
new file mode 100644
index 00000000000..4cedffed4a2
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbIndexOperation.cpp
@@ -0,0 +1,412 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbIndexOperation.hpp>
+#include <Ndb.hpp>
+#include <NdbTransaction.hpp>
+#include "NdbApiSignal.hpp"
+#include <AttributeHeader.hpp>
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/IndxKeyInfo.hpp>
+#include <signaldata/IndxAttrInfo.hpp>
+
+NdbIndexOperation::NdbIndexOperation(Ndb* aNdb) :
+ NdbOperation(aNdb),
+ m_theIndex(NULL)
+{
+ m_tcReqGSN = GSN_TCINDXREQ;
+ m_attrInfoGSN = GSN_INDXATTRINFO;
+ m_keyInfoGSN = GSN_INDXKEYINFO;
+
+ /**
+ * Change receiver type
+ */
+ theReceiver.init(NdbReceiver::NDB_INDEX_OPERATION, this);
+}
+
+NdbIndexOperation::~NdbIndexOperation()
+{
+}
+
+/*****************************************************************************
+ * int indxInit();
+ *
+ * Return Value: Return 0 : init was successful.
+ * Return -1: In all other case.
+ * Remark: Initiates operation record after allocation.
+ *****************************************************************************/
+int
+NdbIndexOperation::indxInit(const NdbIndexImpl * anIndex,
+ const NdbTableImpl * aTable,
+ NdbTransaction* myConnection)
+{
+ NdbOperation::init(aTable, myConnection);
+
+ switch (anIndex->m_type) {
+ case(NdbDictionary::Index::UniqueHashIndex):
+ break;
+ case(NdbDictionary::Index::Undefined):
+ case(NdbDictionary::Index::OrderedIndex):
+ setErrorCodeAbort(4003);
+ return -1;
+ }
+ m_theIndex = anIndex;
+ m_accessTable = anIndex->m_table;
+ theNoOfTupKeyLeft = m_accessTable->getNoOfPrimaryKeys();
+ return 0;
+}
+
+int NdbIndexOperation::readTuple(NdbOperation::LockMode lm)
+{
+ switch(lm) {
+ case LM_Read:
+ return readTuple();
+ break;
+ case LM_Exclusive:
+ return readTupleExclusive();
+ break;
+ case LM_CommittedRead:
+ return readTuple();
+ break;
+ default:
+ return -1;
+ };
+}
+
+int NdbIndexOperation::insertTuple()
+{
+ setErrorCode(4200);
+ return -1;
+}
+
+int NdbIndexOperation::readTuple()
+{
+ // First check that index is unique
+
+ return NdbOperation::readTuple();
+}
+
+int NdbIndexOperation::readTupleExclusive()
+{
+ // First check that index is unique
+
+ return NdbOperation::readTupleExclusive();
+}
+
+int NdbIndexOperation::simpleRead()
+{
+ // First check that index is unique
+
+ return NdbOperation::readTuple();
+}
+
+int NdbIndexOperation::dirtyRead()
+{
+ // First check that index is unique
+
+ return NdbOperation::readTuple();
+}
+
+int NdbIndexOperation::committedRead()
+{
+ // First check that index is unique
+
+ return NdbOperation::readTuple();
+}
+
+int NdbIndexOperation::updateTuple()
+{
+ // First check that index is unique
+
+ return NdbOperation::updateTuple();
+}
+
+int NdbIndexOperation::deleteTuple()
+{
+ // First check that index is unique
+
+ return NdbOperation::deleteTuple();
+}
+
+int NdbIndexOperation::dirtyUpdate()
+{
+ // First check that index is unique
+
+ return NdbOperation::dirtyUpdate();
+}
+
+int NdbIndexOperation::interpretedUpdateTuple()
+{
+ // First check that index is unique
+
+ return NdbOperation::interpretedUpdateTuple();
+}
+
+int NdbIndexOperation::interpretedDeleteTuple()
+{
+ // First check that index is unique
+
+ return NdbOperation::interpretedDeleteTuple();
+}
+
+int
+NdbIndexOperation::prepareSend(Uint32 aTC_ConnectPtr, Uint64 aTransactionId)
+{
+ Uint32 tTransId1, tTransId2;
+ Uint32 tReqInfo;
+ Uint32 tSignalCount = 0;
+ Uint32 tInterpretInd = theInterpretIndicator;
+
+ theErrorLine = 0;
+
+ if (tInterpretInd != 1) {
+ OperationType tOpType = theOperationType;
+ OperationStatus tStatus = theStatus;
+ if ((tOpType == UpdateRequest) ||
+ (tOpType == InsertRequest) ||
+ (tOpType == WriteRequest)) {
+ if (tStatus != SetValue) {
+ setErrorCodeAbort(4506);
+ return -1;
+ }//if
+ } else if ((tOpType == ReadRequest) || (tOpType == ReadExclusive) ||
+ (tOpType == DeleteRequest)) {
+ if (tStatus != GetValue) {
+ setErrorCodeAbort(4506);
+ return -1;
+ }//if
+ } else {
+ setErrorCodeAbort(4507);
+ return -1;
+ }//if
+ } else {
+ if (prepareSendInterpreted() == -1) {
+ return -1;
+ }//if
+ }//if
+
+//-------------------------------------------------------------
+// We start by filling in the first 8 unconditional words of the
+// TCINDXREQ signal.
+//-------------------------------------------------------------
+ TcKeyReq * tcKeyReq =
+ CAST_PTR(TcKeyReq, theTCREQ->getDataPtrSend());
+
+ Uint32 tTotalCurrAI_Len = theTotalCurrAI_Len;
+ Uint32 tIndexId = m_theIndex->m_indexId;
+ Uint32 tSchemaVersion = m_theIndex->m_version;
+
+ tcKeyReq->apiConnectPtr = aTC_ConnectPtr;
+ tcKeyReq->senderData = ptr2int();
+ tcKeyReq->attrLen = tTotalCurrAI_Len;
+ tcKeyReq->tableId = tIndexId;
+ tcKeyReq->tableSchemaVersion = tSchemaVersion;
+
+ tTransId1 = (Uint32) aTransactionId;
+ tTransId2 = (Uint32) (aTransactionId >> 32);
+
+//-------------------------------------------------------------
+// Simple is simple if simple or both start and commit is set.
+//-------------------------------------------------------------
+// Temporarily disable simple stuff
+ Uint8 tSimpleIndicator = 0;
+// Uint8 tSimpleIndicator = theSimpleIndicator;
+ Uint8 tCommitIndicator = theCommitIndicator;
+ Uint8 tStartIndicator = theStartIndicator;
+// if ((theNdbCon->theLastOpInList == this) && (theCommitIndicator == 0))
+// abort();
+// Temporarily disable simple stuff
+ Uint8 tSimpleAlt = 0;
+// Uint8 tSimpleAlt = tStartIndicator & tCommitIndicator;
+ tSimpleIndicator = tSimpleIndicator | tSimpleAlt;
+
+//-------------------------------------------------------------
+// Simple state is set if start and commit is set and it is
+// a read request. Otherwise it is set to zero.
+//-------------------------------------------------------------
+ Uint8 tReadInd = (theOperationType == ReadRequest);
+ Uint8 tSimpleState = tReadInd & tSimpleAlt;
+ //theNdbCon->theSimpleState = tSimpleState;
+
+ tcKeyReq->transId1 = tTransId1;
+ tcKeyReq->transId2 = tTransId2;
+
+ tReqInfo = 0;
+
+ if (tTotalCurrAI_Len <= TcKeyReq::MaxAttrInfo) {
+ tcKeyReq->setAIInTcKeyReq(tReqInfo, tTotalCurrAI_Len);
+ } else {
+ tcKeyReq->setAIInTcKeyReq(tReqInfo, TcKeyReq::MaxAttrInfo);
+ }//if
+
+ tcKeyReq->setSimpleFlag(tReqInfo, tSimpleIndicator);
+ tcKeyReq->setCommitFlag(tReqInfo, tCommitIndicator);
+ tcKeyReq->setStartFlag(tReqInfo, tStartIndicator);
+ const Uint8 tInterpretIndicator = theInterpretIndicator;
+ tcKeyReq->setInterpretedFlag(tReqInfo, tInterpretIndicator);
+
+ Uint8 tDirtyIndicator = theDirtyIndicator;
+ OperationType tOperationType = theOperationType;
+ Uint32 tIndexLen = theTupKeyLen;
+ Uint8 abortOption = theNdbCon->m_abortOption;
+
+ tcKeyReq->setDirtyFlag(tReqInfo, tDirtyIndicator);
+ tcKeyReq->setOperationType(tReqInfo, tOperationType);
+ tcKeyReq->setKeyLength(tReqInfo, tIndexLen);
+ tcKeyReq->setAbortOption(tReqInfo, abortOption);
+
+ Uint8 tDistrKeyIndicator = theDistrKeyIndicator_;
+ Uint8 tScanIndicator = theScanInfo & 1;
+
+ tcKeyReq->setDistributionKeyFlag(tReqInfo, tDistrKeyIndicator);
+ tcKeyReq->setScanIndFlag(tReqInfo, tScanIndicator);
+
+ tcKeyReq->requestInfo = tReqInfo;
+
+//-------------------------------------------------------------
+// The next step is to fill in the upto three conditional words.
+//-------------------------------------------------------------
+ Uint32* tOptionalDataPtr = &tcKeyReq->scanInfo;
+ Uint32 tDistrGHIndex = tScanIndicator;
+ Uint32 tDistrKeyIndex = tDistrGHIndex;
+
+ Uint32 tScanInfo = theScanInfo;
+ Uint32 tDistrKey = theDistributionKey;
+
+ tOptionalDataPtr[0] = tScanInfo;
+ tOptionalDataPtr[tDistrKeyIndex] = tDistrKey;
+
+//-------------------------------------------------------------
+// The next is step is to compress the key data part of the
+// TCKEYREQ signal.
+//-------------------------------------------------------------
+ Uint32 tKeyIndex = tDistrKeyIndex + tDistrKeyIndicator;
+ Uint32* tKeyDataPtr = &tOptionalDataPtr[tKeyIndex];
+ Uint32 Tdata1 = tcKeyReq->keyInfo[0];
+ Uint32 Tdata2 = tcKeyReq->keyInfo[1];
+ Uint32 Tdata3 = tcKeyReq->keyInfo[2];
+ Uint32 Tdata4 = tcKeyReq->keyInfo[3];
+ Uint32 Tdata5;
+
+ tKeyDataPtr[0] = Tdata1;
+ tKeyDataPtr[1] = Tdata2;
+ tKeyDataPtr[2] = Tdata3;
+ tKeyDataPtr[3] = Tdata4;
+ if (tIndexLen > 4) {
+ Tdata1 = tcKeyReq->keyInfo[4];
+ Tdata2 = tcKeyReq->keyInfo[5];
+ Tdata3 = tcKeyReq->keyInfo[6];
+ Tdata4 = tcKeyReq->keyInfo[7];
+
+ tKeyDataPtr[4] = Tdata1;
+ tKeyDataPtr[5] = Tdata2;
+ tKeyDataPtr[6] = Tdata3;
+ tKeyDataPtr[7] = Tdata4;
+ }//if
+//-------------------------------------------------------------
+// Finally we also compress the INDXATTRINFO part of the signal.
+// We optimise by using the if-statement for sending INDXKEYINFO
+// signals to calculating the new Attrinfo Index.
+//-------------------------------------------------------------
+ Uint32 tAttrInfoIndex;
+
+ if (tIndexLen > TcKeyReq::MaxKeyInfo) {
+ /**
+ * Set transid and TC connect ptr in the INDXKEYINFO signals
+ */
+ NdbApiSignal* tSignal = theTCREQ->next();
+ Uint32 remainingKey = tIndexLen - TcKeyReq::MaxKeyInfo;
+
+ do {
+ Uint32* tSigDataPtr = tSignal->getDataPtrSend();
+ NdbApiSignal* tnextSignal = tSignal->next();
+ tSignalCount++;
+ tSigDataPtr[0] = aTC_ConnectPtr;
+ tSigDataPtr[1] = tTransId1;
+ tSigDataPtr[2] = tTransId2;
+ if (remainingKey > IndxKeyInfo::DataLength) {
+ // The signal is full
+ tSignal->setLength(IndxKeyInfo::MaxSignalLength);
+ remainingKey -= IndxKeyInfo::DataLength;
+ }
+ else {
+ // Last signal
+ tSignal->setLength(IndxKeyInfo::HeaderLength + remainingKey);
+ remainingKey = 0;
+ }
+ tSignal = tnextSignal;
+ } while (tSignal != NULL);
+ tAttrInfoIndex = tKeyIndex + TcKeyReq::MaxKeyInfo;
+ } else {
+ tAttrInfoIndex = tKeyIndex + tIndexLen;
+ }//if
+
+//-------------------------------------------------------------
+// Perform the Attrinfo packing in the TCKEYREQ signal started
+// above.
+//-------------------------------------------------------------
+ Uint32* tAIDataPtr = &tOptionalDataPtr[tAttrInfoIndex];
+ Tdata1 = tcKeyReq->attrInfo[0];
+ Tdata2 = tcKeyReq->attrInfo[1];
+ Tdata3 = tcKeyReq->attrInfo[2];
+ Tdata4 = tcKeyReq->attrInfo[3];
+ Tdata5 = tcKeyReq->attrInfo[4];
+
+ theTCREQ->setLength(tcKeyReq->getAIInTcKeyReq(tReqInfo) +
+ tAttrInfoIndex + TcKeyReq::StaticLength);
+ tAIDataPtr[0] = Tdata1;
+ tAIDataPtr[1] = Tdata2;
+ tAIDataPtr[2] = Tdata3;
+ tAIDataPtr[3] = Tdata4;
+ tAIDataPtr[4] = Tdata5;
+
+/***************************************************
+* Send the INDXATTRINFO signals.
+***************************************************/
+ if (tTotalCurrAI_Len > 5) {
+ // Set the last signal's length.
+ NdbApiSignal* tSignal = theFirstATTRINFO;
+ theCurrentATTRINFO->setLength(theAI_LenInCurrAI);
+ do {
+ Uint32* tSigDataPtr = tSignal->getDataPtrSend();
+ NdbApiSignal* tnextSignal = tSignal->next();
+ tSignalCount++;
+ tSigDataPtr[0] = aTC_ConnectPtr;
+ tSigDataPtr[1] = tTransId1;
+ tSigDataPtr[2] = tTransId2;
+ tSignal = tnextSignal;
+ } while (tSignal != NULL);
+ }//if
+ theStatus = WaitResponse;
+ theReceiver.prepareSend();
+ return 0;
+}
+
+/***************************************************************************
+int receiveTCINDXREF( NdbApiSignal* aSignal)
+
+Return Value: Return 0 : send was succesful.
+ Return -1: In all other case.
+Parameters: aSignal: the signal object that contains the TCINDXREF signal from TC.
+Remark: Handles the reception of the TCKEYREF signal.
+***************************************************************************/
+int
+NdbIndexOperation::receiveTCINDXREF( NdbApiSignal* aSignal)
+{
+ return receiveTCKEYREF(aSignal);
+}//NdbIndexOperation::receiveTCINDXREF()
diff --git a/storage/ndb/src/ndbapi/NdbLinHash.hpp b/storage/ndb/src/ndbapi/NdbLinHash.hpp
new file mode 100644
index 00000000000..f245a261a04
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbLinHash.hpp
@@ -0,0 +1,447 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NdbLinHash_H
+#define NdbLinHash_H
+
+#include <ndb_types.h>
+
+#define SEGMENTSIZE 64
+#define SEGMENTLOGSIZE 6
+#define DIRECTORYSIZE 64
+#define DIRINDEX(adress) ((adress) >> SEGMENTLOGSIZE)
+#define SEGINDEX(adress) ((adress) & (SEGMENTSIZE-1))
+
+#if !defined(MAXLOADFCTR)
+#define MAXLOADFCTR 2
+#endif
+#if !defined(MINLOADFCTR)
+#define MINLOADFCTR (MAXLOADFCTR/2)
+#endif
+
+template<class C>
+class NdbElement_t {
+public:
+ NdbElement_t();
+ ~NdbElement_t();
+
+ Uint32 len;
+ Uint32 hash;
+ Uint32 localkey1;
+ Uint32 *str;
+ NdbElement_t<C> *next;
+ C* theData;
+private:
+ NdbElement_t(const NdbElement_t<C> & aElement_t);
+ NdbElement_t & operator = (const NdbElement_t<C> & aElement_t);
+};
+
+
+template <class C>
+class NdbLinHash {
+public:
+ NdbLinHash();
+ ~NdbLinHash();
+ void createHashTable(void);
+ void releaseHashTable(void);
+
+ int insertKey(const char * str, Uint32 len, Uint32 lkey1, C* data);
+ C *deleteKey(const char * str, Uint32 len);
+
+ C* getData(const char *, Uint32);
+ Uint32* getKey(const char *, Uint32);
+
+ void shrinkTable(void);
+ void expandHashTable(void);
+
+ Uint32 Hash(const char *str, Uint32 len);
+ Uint32 Hash(Uint32 h);
+
+ NdbElement_t<C> * getNext(NdbElement_t<C> * curr);
+
+private:
+ void getBucket(Uint32 hash, int * dirindex, int * segindex);
+
+ struct Segment_t {
+ NdbElement_t<C> * elements[SEGMENTSIZE];
+ };
+
+ Uint32 p; /*bucket to be split*/
+ Uint32 max; /*max is the upper bound*/
+ Int32 slack; /*number of insertions before splits*/
+ Segment_t * directory[DIRECTORYSIZE];
+
+ NdbLinHash(const NdbLinHash<C> & aLinHash);
+ NdbLinHash<C> & operator = (const NdbLinHash<C> & aLinHash);
+};
+
+// All template methods must be inline
+
+template <class C>
+inline
+NdbLinHash<C>::NdbLinHash() {
+}
+
+template <class C>
+inline
+NdbLinHash<C>::NdbLinHash(const NdbLinHash<C>& aLinHash)
+{
+}
+
+template <class C>
+inline
+NdbLinHash<C>::~NdbLinHash()
+{
+}
+
+template <class C>
+inline
+Uint32
+NdbLinHash<C>::Hash( const char* str, Uint32 len )
+{
+ Uint32 h = 0;
+ while(len >= 4){
+ h = (h << 5) + h + str[0];
+ h = (h << 5) + h + str[1];
+ h = (h << 5) + h + str[2];
+ h = (h << 5) + h + str[3];
+ len -= 4;
+ str += 4;
+ }
+
+ while(len > 0){
+ h = (h << 5) + h + *str++;
+ len--;
+ }
+ return h;
+}
+
+template <class C>
+inline
+Uint32
+NdbLinHash<C>::Hash( Uint32 h ){
+ return h;
+}
+
+template <class C>
+inline
+NdbElement_t<C>::NdbElement_t() :
+ len(0),
+ hash(0),
+ localkey1(0),
+ str(NULL),
+ next(NULL),
+ theData(NULL)
+{
+}
+
+template <class C>
+inline
+NdbElement_t<C>::~NdbElement_t()
+{
+ delete []str;
+}
+
+
+/* Initialize the hashtable HASH_T */
+template <class C>
+inline
+void
+NdbLinHash<C>::createHashTable() {
+ p = 0;
+ max = SEGMENTSIZE - 1;
+ slack = SEGMENTSIZE * MAXLOADFCTR;
+ directory[0] = new Segment_t();
+ int i;
+
+ /* The first segment cleared before used */
+ for(i = 0; i < SEGMENTSIZE; i++ )
+ directory[0]->elements[i] = 0;
+
+ /* clear the rest of the directory */
+ for(i = 1; i < DIRECTORYSIZE; i++)
+ directory[i] = 0;
+}
+
+template <class C>
+inline
+void
+NdbLinHash<C>::getBucket(Uint32 hash, int * dir, int * seg){
+ Uint32 adress = hash & max;
+ if(adress < p)
+ adress = hash & (2 * max + 1);
+
+ * dir = DIRINDEX(adress);
+ * seg = SEGINDEX(adress);
+}
+
+template <class C>
+inline
+Int32
+NdbLinHash<C>::insertKey( const char* str, Uint32 len, Uint32 lkey1, C* data )
+{
+ const Uint32 hash = Hash(str, len);
+ int dir, seg;
+ getBucket(hash, &dir, &seg);
+
+ NdbElement_t<C> **chainp = &directory[dir]->elements[seg];
+
+ /**
+ * Check if the string already are in the hash table
+ * chain=chainp will copy the contents of HASH_T into chain
+ */
+ NdbElement_t<C> * oldChain = 0;
+ NdbElement_t<C> * chain;
+ for(chain = *chainp; chain != 0; chain = chain->next){
+ if(chain->len == len && !memcmp(chain->str, str, len))
+ return -1; /* Element already exists */
+ else
+ oldChain = chain;
+ }
+
+ /* New entry */
+ chain = new NdbElement_t<C>();
+ chain->len = len;
+ chain->hash = hash;
+ chain->localkey1 = lkey1;
+ chain->next = 0;
+ chain->theData = data;
+ chain->str = new Uint32[((len + 3) >> 2)];
+ memcpy( &chain->str[0], str, len );
+ if (oldChain != 0)
+ oldChain->next = chain;
+ else
+ *chainp = chain;
+
+#if 0
+ if(--(slack) < 0)
+ expandHashTable();
+#endif
+
+ return chain->localkey1;
+}
+
+
+template <class C>
+inline
+Uint32*
+NdbLinHash<C>::getKey( const char* str, Uint32 len )
+{
+ const Uint32 tHash = Hash(str, len);
+ int dir, seg;
+ getBucket(tHash, &dir, &seg);
+
+ NdbElement_t<C> ** keyp = &directory[dir]->elements[seg];
+
+ /*Check if the string are in the hash table*/
+ for(NdbElement_t<C> * key = *keyp; key != 0; key = key->next ) {
+ if(key->len == len && !memcmp(key->str, str, len)) {
+ return &key->localkey1;
+ }
+ }
+ return NULL ; /* The key was not found */
+}
+
+template <class C>
+inline
+C*
+NdbLinHash<C>::getData( const char* str, Uint32 len ){
+
+ const Uint32 tHash = Hash(str, len);
+ int dir, seg;
+ getBucket(tHash, &dir, &seg);
+
+ NdbElement_t<C> ** keyp = &directory[dir]->elements[seg];
+
+ /*Check if the string are in the hash table*/
+ for(NdbElement_t<C> * key = *keyp; key != 0; key = key->next ) {
+ if(key->len == len && !memcmp(key->str, str, len)) {
+ return key->theData;
+ }
+ }
+ return NULL ; /* The key was not found */
+}
+
+template <class C>
+inline
+C *
+NdbLinHash<C>::deleteKey ( const char* str, Uint32 len){
+ const Uint32 hash = Hash(str, len);
+ int dir, seg;
+ getBucket(hash, &dir, &seg);
+
+ NdbElement_t<C> *oldChain = 0;
+ NdbElement_t<C> **chainp = &directory[dir]->elements[seg];
+ for(NdbElement_t<C> * chain = *chainp; chain != 0; chain = chain->next){
+ if(chain->len == len && !memcmp(chain->str, str, len)){
+ C *data= chain->theData;
+ if (oldChain == 0) {
+ * chainp = chain->next;
+ } else {
+ oldChain->next = chain->next;
+ }
+ delete chain;
+ return data;
+ } else {
+ oldChain = chain;
+ }
+ }
+ return 0; /* Element doesn't exist */
+}
+
+template <class C>
+inline
+void
+NdbLinHash<C>::releaseHashTable( void ){
+ NdbElement_t<C>* tNextElement;
+ NdbElement_t<C>* tElement;
+
+ //Traverse the whole directory structure
+ for(int countd = 0; countd < DIRECTORYSIZE;countd++ ){
+ if (directory[countd] != 0) {
+ //Traverse whole hashtable
+ for(int counts = 0; counts < SEGMENTSIZE; counts++ )
+ if (directory[countd]->elements[counts] != 0) {
+ tElement = directory[countd]->elements[counts];
+ //Delete all elements even those who is linked
+ do {
+ tNextElement = tElement->next;
+ delete tElement;
+ tElement = tNextElement;
+ } while (tNextElement != 0);
+ }
+ delete directory[countd];
+ }
+ }
+}
+
+template <class C>
+inline
+void
+NdbLinHash<C>::shrinkTable( void )
+{
+ Segment_t *lastseg;
+ NdbElement_t<C> **chainp;
+ Uint32 oldlast = p + max;
+
+ if( oldlast == 0 )
+ return;
+
+ // Adjust the state variables.
+ if( p == 0 ) {
+ max >>= 1;
+ p = max;
+ }
+ else
+ --(p);
+
+ // Update slack after shrink.
+
+ slack -= MAXLOADFCTR;
+
+ // Insert the chain oldlast at the end of chain p.
+
+ chainp = &directory[DIRINDEX(p)]->elements[SEGINDEX(p)];
+ while( *chainp != 0 ) {
+ chainp = &((*chainp)->next);
+ lastseg = directory[DIRINDEX(oldlast)];
+ *chainp = lastseg->elements[SEGINDEX(oldlast)];
+
+ // If necessary free last segment.
+ if( SEGINDEX(oldlast) == 0)
+ delete lastseg;
+ }
+}
+
+template <class C>
+inline
+void
+NdbLinHash<C>::expandHashTable( void )
+{
+
+ NdbElement_t<C> **oldbucketp, *chain, *headofold, *headofnew, *next;
+ Uint32 maxp = max + 1;
+ Uint32 newadress = maxp + p;
+
+
+ // Still room in the adress space?
+ if( newadress >= DIRECTORYSIZE * SEGMENTSIZE ) {
+ return;
+ }
+
+ // If necessary, create a new segment.
+ if( SEGINDEX(newadress) == 0 )
+ directory[DIRINDEX(newadress)] = new Segment_t();
+
+ // Locate the old (to be split) bucket.
+ oldbucketp = &directory[DIRINDEX(p)]->elements[SEGINDEX(p)];
+
+ // Adjust the state variables.
+ p++;
+ if( p > max ) {
+ max = 2 *max + 1;
+ p = 0;
+ }
+
+ // Update slack after expandation.
+ slack += MAXLOADFCTR;
+
+ // Relocate records to the new bucket.
+ headofold = 0;
+ headofnew = 0;
+
+ for( chain = *oldbucketp; chain != 0; chain = next ) {
+ next = chain->next;
+ if( chain->hash & maxp ) {
+ chain->next = headofnew;
+ headofnew = chain;
+ }
+ else {
+ chain->next = headofold;
+ headofold = chain;
+ }
+ }
+ *oldbucketp = headofold;
+ directory[DIRINDEX(newadress)]->elements[SEGINDEX(newadress)] = headofnew;
+}
+
+template <class C>
+inline
+NdbElement_t<C> *
+NdbLinHash<C>::getNext(NdbElement_t<C> * curr){
+ if(curr != 0 && curr->next != 0)
+ return curr->next;
+
+ int dir = 0, seg = 0;
+
+ if(curr != 0){
+ getBucket(curr->hash, &dir, &seg);
+ }
+
+ for(int countd = dir; countd < DIRECTORYSIZE;countd++ ){
+ if (directory[countd] != 0) {
+ for(int counts = seg + 1; counts < SEGMENTSIZE; counts++ ){
+ if (directory[countd]->elements[counts] != 0) {
+ return directory[countd]->elements[counts];
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbOperation.cpp b/storage/ndb/src/ndbapi/NdbOperation.cpp
new file mode 100644
index 00000000000..c9143444908
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbOperation.cpp
@@ -0,0 +1,400 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbTransaction.hpp>
+#include <NdbOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include "NdbRecAttr.hpp"
+#include "NdbUtil.hpp"
+#include "NdbBlob.hpp"
+#include "ndbapi_limits.h"
+#include <signaldata/TcKeyReq.hpp>
+#include "NdbDictionaryImpl.hpp"
+
+#include "API.hpp"
+#include <NdbOut.hpp>
+
+
+/******************************************************************************
+ * NdbOperation(Ndb* aNdb, Table* aTable);
+ *
+ * Return Value: None
+ * Parameters: aNdb: Pointers to the Ndb object.
+ * aTable: Pointers to the Table object
+ * Remark: Creat an object of NdbOperation.
+ ****************************************************************************/
+NdbOperation::NdbOperation(Ndb* aNdb) :
+ theReceiver(aNdb),
+ theErrorLine(0),
+ theNdb(aNdb),
+ //theTable(aTable),
+ theNdbCon(NULL),
+ theNext(NULL),
+ theTCREQ(NULL),
+ theFirstATTRINFO(NULL),
+ theCurrentATTRINFO(NULL),
+ theTotalCurrAI_Len(0),
+ theAI_LenInCurrAI(0),
+ theLastKEYINFO(NULL),
+
+ theFirstLabel(NULL),
+ theLastLabel(NULL),
+ theFirstBranch(NULL),
+ theLastBranch(NULL),
+ theFirstCall(NULL),
+ theLastCall(NULL),
+ theFirstSubroutine(NULL),
+ theLastSubroutine(NULL),
+ theNoOfLabels(0),
+ theNoOfSubroutines(0),
+
+ m_currentTable(NULL), //theTableId(0xFFFF),
+ m_accessTable(NULL), //theAccessTableId(0xFFFF),
+ //theSchemaVersion(0),
+ theTotalNrOfKeyWordInSignal(8),
+ theTupKeyLen(0),
+ theNoOfTupKeyLeft(0),
+ theOperationType(NotDefined),
+ theStatus(Init),
+ theMagicNumber(0xFE11D0),
+ theScanInfo(0),
+ m_tcReqGSN(GSN_TCKEYREQ),
+ m_keyInfoGSN(GSN_KEYINFO),
+ m_attrInfoGSN(GSN_ATTRINFO),
+ theBlobList(NULL),
+ m_abortOption(-1)
+{
+ theReceiver.init(NdbReceiver::NDB_OPERATION, this);
+ theError.code = 0;
+}
+/*****************************************************************************
+ * ~NdbOperation();
+ *
+ * Remark: Delete tables for connection pointers (id).
+ *****************************************************************************/
+NdbOperation::~NdbOperation( )
+{
+}
+/******************************************************************************
+ *void setErrorCode(int anErrorCode);
+ *
+ * Remark: Set an Error Code on operation and
+ * on connection set an error status.
+ *****************************************************************************/
+void
+NdbOperation::setErrorCode(int anErrorCode)
+{
+ theError.code = anErrorCode;
+ theNdbCon->theErrorLine = theErrorLine;
+ theNdbCon->theErrorOperation = this;
+ theNdbCon->setOperationErrorCode(anErrorCode);
+}
+
+/******************************************************************************
+ * void setErrorCodeAbort(int anErrorCode);
+ *
+ * Remark: Set an Error Code on operation and on connection set
+ * an error status.
+ *****************************************************************************/
+void
+NdbOperation::setErrorCodeAbort(int anErrorCode)
+{
+ theError.code = anErrorCode;
+ theNdbCon->theErrorLine = theErrorLine;
+ theNdbCon->theErrorOperation = this;
+ theNdbCon->setOperationErrorCodeAbort(anErrorCode);
+}
+
+/*****************************************************************************
+ * int init();
+ *
+ * Return Value: Return 0 : init was successful.
+ * Return -1: In all other case.
+ * Remark: Initiates operation record after allocation.
+ *****************************************************************************/
+
+int
+NdbOperation::init(const NdbTableImpl* tab, NdbTransaction* myConnection){
+ NdbApiSignal* tSignal;
+ theStatus = Init;
+ theError.code = 0;
+ theErrorLine = 1;
+ m_currentTable = m_accessTable = tab;
+
+ theNdbCon = myConnection;
+ for (Uint32 i=0; i<NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY; i++)
+ for (int j=0; j<3; j++)
+ theTupleKeyDefined[i][j] = 0;
+
+ theFirstATTRINFO = NULL;
+ theCurrentATTRINFO = NULL;
+ theLastKEYINFO = NULL;
+
+
+ theTupKeyLen = 0;
+ theNoOfTupKeyLeft = tab->getNoOfPrimaryKeys();
+
+ theTotalCurrAI_Len = 0;
+ theAI_LenInCurrAI = 0;
+ theStartIndicator = 0;
+ theCommitIndicator = 0;
+ theSimpleIndicator = 0;
+ theDirtyIndicator = 0;
+ theInterpretIndicator = 0;
+ theDistrKeyIndicator_ = 0;
+ theScanInfo = 0;
+ theTotalNrOfKeyWordInSignal = 8;
+ theMagicNumber = 0xABCDEF01;
+ theBlobList = NULL;
+ m_abortOption = -1;
+
+ tSignal = theNdb->getSignal();
+ if (tSignal == NULL)
+ {
+ setErrorCode(4000);
+ return -1;
+ }
+ theTCREQ = tSignal;
+ theTCREQ->setSignal(m_tcReqGSN);
+
+ theAI_LenInCurrAI = 20;
+ TcKeyReq * const tcKeyReq = CAST_PTR(TcKeyReq, theTCREQ->getDataPtrSend());
+ tcKeyReq->scanInfo = 0;
+ theKEYINFOptr = &tcKeyReq->keyInfo[0];
+ theATTRINFOptr = &tcKeyReq->attrInfo[0];
+ theReceiver.init(NdbReceiver::NDB_OPERATION, this);
+ return 0;
+}
+
+
+/******************************************************************************
+ * void release();
+ *
+ * Remark: Release all objects connected to the operation object.
+ *****************************************************************************/
+void
+NdbOperation::release()
+{
+ NdbApiSignal* tSignal;
+ NdbApiSignal* tSaveSignal;
+ NdbBranch* tBranch;
+ NdbBranch* tSaveBranch;
+ NdbLabel* tLabel;
+ NdbLabel* tSaveLabel;
+ NdbCall* tCall;
+ NdbCall* tSaveCall;
+ NdbSubroutine* tSubroutine;
+ NdbSubroutine* tSaveSubroutine;
+ NdbBlob* tBlob;
+ NdbBlob* tSaveBlob;
+
+ tSignal = theTCREQ;
+ while (tSignal != NULL)
+ {
+ tSaveSignal = tSignal;
+ tSignal = tSignal->next();
+ theNdb->releaseSignal(tSaveSignal);
+ }
+ theTCREQ = NULL;
+ theLastKEYINFO = NULL;
+
+ tSignal = theFirstATTRINFO;
+ while (tSignal != NULL)
+ {
+ tSaveSignal = tSignal;
+ tSignal = tSignal->next();
+ theNdb->releaseSignal(tSaveSignal);
+ }
+ theFirstATTRINFO = NULL;
+ theCurrentATTRINFO = NULL;
+
+ if (theInterpretIndicator == 1)
+ {
+ tBranch = theFirstBranch;
+ while (tBranch != NULL)
+ {
+ tSaveBranch = tBranch;
+ tBranch = tBranch->theNext;
+ theNdb->releaseNdbBranch(tSaveBranch);
+ }
+ tLabel = theFirstLabel;
+ while (tLabel != NULL)
+ {
+ tSaveLabel = tLabel;
+ tLabel = tLabel->theNext;
+ theNdb->releaseNdbLabel(tSaveLabel);
+ }
+ tCall = theFirstCall;
+ while (tCall != NULL)
+ {
+ tSaveCall = tCall;
+ tCall = tCall->theNext;
+ theNdb->releaseNdbCall(tSaveCall);
+ }
+ tSubroutine = theFirstSubroutine;
+ while (tSubroutine != NULL)
+ {
+ tSaveSubroutine = tSubroutine;
+ tSubroutine = tSubroutine->theNext;
+ theNdb->releaseNdbSubroutine(tSaveSubroutine);
+ }
+ }
+ tBlob = theBlobList;
+ while (tBlob != NULL)
+ {
+ tSaveBlob = tBlob;
+ tBlob = tBlob->theNext;
+ theNdb->releaseNdbBlob(tSaveBlob);
+ }
+ theBlobList = NULL;
+ theReceiver.release();
+}
+
+NdbRecAttr*
+NdbOperation::getValue(const char* anAttrName, char* aValue)
+{
+ return getValue_impl(m_currentTable->getColumn(anAttrName), aValue);
+}
+
+NdbRecAttr*
+NdbOperation::getValue(Uint32 anAttrId, char* aValue)
+{
+ return getValue_impl(m_currentTable->getColumn(anAttrId), aValue);
+}
+
+NdbRecAttr*
+NdbOperation::getValue(const NdbDictionary::Column* col, char* aValue)
+{
+ return getValue_impl(&NdbColumnImpl::getImpl(*col), aValue);
+}
+
+int
+NdbOperation::equal(const char* anAttrName,
+ const char* aValuePassed,
+ Uint32 aVariableKeyLen)
+{
+ return equal_impl(m_accessTable->getColumn(anAttrName), aValuePassed, aVariableKeyLen);
+}
+
+int
+NdbOperation::equal(Uint32 anAttrId,
+ const char* aValuePassed,
+ Uint32 aVariableKeyLen)
+{
+ return equal_impl(m_accessTable->getColumn(anAttrId), aValuePassed, aVariableKeyLen);
+}
+
+int
+NdbOperation::setValue( const char* anAttrName,
+ const char* aValuePassed,
+ Uint32 len)
+{
+ return setValue(m_currentTable->getColumn(anAttrName), aValuePassed, len);
+}
+
+
+int
+NdbOperation::setValue( Uint32 anAttrId,
+ const char* aValuePassed,
+ Uint32 len)
+{
+ return setValue(m_currentTable->getColumn(anAttrId), aValuePassed, len);
+}
+
+NdbBlob*
+NdbOperation::getBlobHandle(const char* anAttrName)
+{
+ return getBlobHandle(theNdbCon, m_currentTable->getColumn(anAttrName));
+}
+
+NdbBlob*
+NdbOperation::getBlobHandle(Uint32 anAttrId)
+{
+ return getBlobHandle(theNdbCon, m_currentTable->getColumn(anAttrId));
+}
+
+int
+NdbOperation::incValue(const char* anAttrName, Uint32 aValue)
+{
+ return incValue(m_currentTable->getColumn(anAttrName), aValue);
+}
+
+int
+NdbOperation::incValue(const char* anAttrName, Uint64 aValue)
+{
+ return incValue(m_currentTable->getColumn(anAttrName), aValue);
+}
+
+int
+NdbOperation::incValue(Uint32 anAttrId, Uint32 aValue)
+{
+ return incValue(m_currentTable->getColumn(anAttrId), aValue);
+}
+
+int
+NdbOperation::incValue(Uint32 anAttrId, Uint64 aValue)
+{
+ return incValue(m_currentTable->getColumn(anAttrId), aValue);
+}
+
+int
+NdbOperation::subValue( const char* anAttrName, Uint32 aValue)
+{
+ return subValue(m_currentTable->getColumn(anAttrName), aValue);
+}
+
+int
+NdbOperation::subValue(Uint32 anAttrId, Uint32 aValue)
+{
+ return subValue(m_currentTable->getColumn(anAttrId), aValue);
+}
+
+int
+NdbOperation::read_attr(const char* anAttrName, Uint32 RegDest)
+{
+ return read_attr(m_currentTable->getColumn(anAttrName), RegDest);
+}
+
+int
+NdbOperation::read_attr(Uint32 anAttrId, Uint32 RegDest)
+{
+ return read_attr(m_currentTable->getColumn(anAttrId), RegDest);
+}
+
+int
+NdbOperation::write_attr(const char* anAttrName, Uint32 RegDest)
+{
+ return write_attr(m_currentTable->getColumn(anAttrName), RegDest);
+}
+
+int
+NdbOperation::write_attr(Uint32 anAttrId, Uint32 RegDest)
+{
+ return write_attr(m_currentTable->getColumn(anAttrId), RegDest);
+}
+
+const char*
+NdbOperation::getTableName() const
+{
+ return m_currentTable->m_externalName.c_str();
+}
+
+const NdbDictionary::Table*
+NdbOperation::getTable() const
+{
+ return m_currentTable;
+}
diff --git a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
new file mode 100644
index 00000000000..835e33dfb40
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
@@ -0,0 +1,723 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include <NdbTransaction.hpp>
+#include <Ndb.hpp>
+#include <NdbRecAttr.hpp>
+#include "NdbUtil.hpp"
+#include "NdbOut.hpp"
+#include "NdbImpl.hpp"
+#include <NdbIndexScanOperation.hpp>
+#include <NdbBlob.hpp>
+
+#include <Interpreter.hpp>
+
+#include <AttributeHeader.hpp>
+#include <signaldata/TcKeyReq.hpp>
+
+/*****************************************************************************
+ * int insertTuple();
+ *****************************************************************************/
+int
+NdbOperation::insertTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ theOperationType = InsertRequest;
+ tNdbCon->theSimpleState = 0;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Exclusive;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::insertTuple()
+/******************************************************************************
+ * int updateTuple();
+ *****************************************************************************/
+int
+NdbOperation::updateTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = UpdateRequest;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Exclusive;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::updateTuple()
+/*****************************************************************************
+ * int writeTuple();
+ *****************************************************************************/
+int
+NdbOperation::writeTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = WriteRequest;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Exclusive;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::writeTuple()
+/******************************************************************************
+ * int readTuple();
+ *****************************************************************************/
+int
+NdbOperation::readTuple(NdbOperation::LockMode lm)
+{
+ switch(lm) {
+ case LM_Read:
+ return readTuple();
+ break;
+ case LM_Exclusive:
+ return readTupleExclusive();
+ break;
+ case LM_CommittedRead:
+ return committedRead();
+ break;
+ default:
+ return -1;
+ };
+}
+/******************************************************************************
+ * int readTuple();
+ *****************************************************************************/
+int
+NdbOperation::readTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = ReadRequest;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Read;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::readTuple()
+
+/*****************************************************************************
+ * int deleteTuple();
+ *****************************************************************************/
+int
+NdbOperation::deleteTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = DeleteRequest;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Exclusive;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::deleteTuple()
+
+/******************************************************************************
+ * int readTupleExclusive();
+ *****************************************************************************/
+int
+NdbOperation::readTupleExclusive()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = ReadExclusive;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Exclusive;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::readTupleExclusive()
+
+/*****************************************************************************
+ * int simpleRead();
+ *****************************************************************************/
+int
+NdbOperation::simpleRead()
+{
+ /**
+ * Currently/still disabled
+ */
+ return readTuple();
+#if 0
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ theOperationType = ReadRequest;
+ theSimpleIndicator = 1;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_Read;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+#endif
+}//NdbOperation::simpleRead()
+
+/*****************************************************************************
+ * int dirtyRead();
+ *****************************************************************************/
+int
+NdbOperation::dirtyRead()
+{
+ return committedRead();
+}//NdbOperation::dirtyRead()
+
+/*****************************************************************************
+ * int committedRead();
+ *****************************************************************************/
+int
+NdbOperation::committedRead()
+{
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ theOperationType = ReadRequest;
+ theSimpleIndicator = 1;
+ theDirtyIndicator = 1;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_CommittedRead;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::committedRead()
+
+/*****************************************************************************
+ * int dirtyUpdate();
+ ****************************************************************************/
+int
+NdbOperation::dirtyUpdate()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ theOperationType = UpdateRequest;
+ tNdbCon->theSimpleState = 0;
+ theSimpleIndicator = 1;
+ theDirtyIndicator = 1;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_CommittedRead;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::dirtyUpdate()
+
+/******************************************************************************
+ * int dirtyWrite();
+ *****************************************************************************/
+int
+NdbOperation::dirtyWrite()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ theOperationType = WriteRequest;
+ tNdbCon->theSimpleState = 0;
+ theSimpleIndicator = 1;
+ theDirtyIndicator = 1;
+ theErrorLine = tErrorLine++;
+ theLockMode = LM_CommittedRead;
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::dirtyWrite()
+
+/******************************************************************************
+ * int interpretedUpdateTuple();
+ ****************************************************************************/
+int
+NdbOperation::interpretedUpdateTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = UpdateRequest;
+ theAI_LenInCurrAI = 25;
+ theLockMode = LM_Exclusive;
+ theErrorLine = tErrorLine++;
+ initInterpreter();
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::interpretedUpdateTuple()
+
+/*****************************************************************************
+ * int interpretedDeleteTuple();
+ *****************************************************************************/
+int
+NdbOperation::interpretedDeleteTuple()
+{
+ NdbTransaction* tNdbCon = theNdbCon;
+ int tErrorLine = theErrorLine;
+ if (theStatus == Init) {
+ theStatus = OperationDefined;
+ tNdbCon->theSimpleState = 0;
+ theOperationType = DeleteRequest;
+
+ theErrorLine = tErrorLine++;
+ theAI_LenInCurrAI = 25;
+ theLockMode = LM_Exclusive;
+ initInterpreter();
+ return 0;
+ } else {
+ setErrorCode(4200);
+ return -1;
+ }//if
+}//NdbOperation::interpretedDeleteTuple()
+
+
+
+/******************************************************************************
+ * int getValue(AttrInfo* tAttrInfo, char* aRef )
+ *
+ * Return Value Return 0 : GetValue was successful.
+ * Return -1: In all other case.
+ * Parameters: tAttrInfo : Attribute object of the retrieved attribute
+ * value.
+ * Remark: Define an attribute to retrieve in query.
+ *****************************************************************************/
+NdbRecAttr*
+NdbOperation::getValue_impl(const NdbColumnImpl* tAttrInfo, char* aValue)
+{
+ NdbRecAttr* tRecAttr;
+ if ((tAttrInfo != NULL) &&
+ (theStatus != Init)){
+ if (theStatus != GetValue) {
+ if (theInterpretIndicator == 1) {
+ if (theStatus == FinalGetValue) {
+ ; // Simply continue with getValue
+ } else if (theStatus == ExecInterpretedValue) {
+ if (insertATTRINFO(Interpreter::EXIT_OK) == -1)
+ return NULL;
+ theInterpretedSize = theTotalCurrAI_Len -
+ (theInitialReadSize + 5);
+ } else if (theStatus == SetValueInterpreted) {
+ theFinalUpdateSize = theTotalCurrAI_Len -
+ (theInitialReadSize + theInterpretedSize + 5);
+ } else {
+ setErrorCodeAbort(4230);
+ return NULL;
+ }//if
+ // MASV - How would execution come here?
+ theStatus = FinalGetValue;
+ } else {
+ setErrorCodeAbort(4230);
+ return NULL;
+ }//if
+ }//if
+ Uint32 ah;
+ AttributeHeader::init(&ah, tAttrInfo->m_attrId, 0);
+ if (insertATTRINFO(ah) != -1) {
+ // Insert Attribute Id into ATTRINFO part.
+
+ /************************************************************************
+ * Get a Receive Attribute object and link it into the operation object.
+ ***********************************************************************/
+ if((tRecAttr = theReceiver.getValue(tAttrInfo, aValue)) != 0){
+ theErrorLine++;
+ return tRecAttr;
+ } else {
+ setErrorCodeAbort(4000);
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }//if insertATTRINFO failure
+ } else {
+ if (tAttrInfo == NULL) {
+ setErrorCodeAbort(4004);
+ return NULL;
+ }//if
+ }//if
+ setErrorCodeAbort(4200);
+ return NULL;
+}
+
+/*****************************************************************************
+ * int setValue(AttrInfo* tAttrInfo, char* aValue, Uint32 len)
+ *
+ * Return Value: Return 0 : SetValue was succesful.
+ * Return -1: In all other case.
+ * Parameters: tAttrInfo : Attribute object where the attribute
+ * info exists.
+ * aValue : Reference to the variable with the new value.
+ * len : Length of the value
+ * Remark: Define a attribute to set in a query.
+******************************************************************************/
+int
+NdbOperation::setValue( const NdbColumnImpl* tAttrInfo,
+ const char* aValuePassed, Uint32 len)
+{
+ DBUG_ENTER("NdbOperation::setValue");
+ DBUG_PRINT("enter", ("col=%s op=%d val=0x%x len=%u",
+ tAttrInfo->m_name.c_str(),
+ theOperationType,
+ aValuePassed, len));
+ if (aValuePassed != NULL)
+ DBUG_DUMP("value", (char*)aValuePassed, len);
+
+ int tReturnCode;
+ Uint32 tAttrId;
+ Uint32 tData;
+ Uint32 tempData[2000];
+ OperationType tOpType = theOperationType;
+ OperationStatus tStatus = theStatus;
+
+
+ if ((tOpType == UpdateRequest) ||
+ (tOpType == WriteRequest)) {
+ if (theInterpretIndicator == 0) {
+ if (tStatus == SetValue) {
+ ;
+ } else {
+ setErrorCodeAbort(4234);
+ DBUG_RETURN(-1);
+ }//if
+ } else {
+ if (tStatus == GetValue) {
+ theInitialReadSize = theTotalCurrAI_Len - 5;
+ } else if (tStatus == ExecInterpretedValue) {
+ //--------------------------------------------------------------------
+ // We insert an exit from interpretation since we are now starting
+ // to set values in the tuple by setValue.
+ //--------------------------------------------------------------------
+ if (insertATTRINFO(Interpreter::EXIT_OK) == -1){
+ DBUG_RETURN(-1);
+ }
+ theInterpretedSize = theTotalCurrAI_Len -
+ (theInitialReadSize + 5);
+ } else if (tStatus == SetValueInterpreted) {
+ ; // Simply continue adding new setValue
+ } else {
+ //--------------------------------------------------------------------
+ // setValue used in the wrong context. Application coding error.
+ //-------------------------------------------------------------------
+ setErrorCodeAbort(4234); //Wrong error code
+ DBUG_RETURN(-1);
+ }//if
+ theStatus = SetValueInterpreted;
+ }//if
+ } else if (tOpType == InsertRequest) {
+ if ((theStatus != SetValue) && (theStatus != OperationDefined)) {
+ setErrorCodeAbort(4234);
+ DBUG_RETURN(-1);
+ }//if
+ } else if (tOpType == ReadRequest || tOpType == ReadExclusive) {
+ setErrorCodeAbort(4504);
+ DBUG_RETURN(-1);
+ } else if (tOpType == DeleteRequest) {
+ setErrorCodeAbort(4504);
+ DBUG_RETURN(-1);
+ } else if (tOpType == OpenScanRequest || tOpType == OpenRangeScanRequest) {
+ setErrorCodeAbort(4228);
+ DBUG_RETURN(-1);
+ } else {
+ //---------------------------------------------------------------------
+ // setValue with undefined operation type.
+ // Probably application coding error.
+ //---------------------------------------------------------------------
+ setErrorCodeAbort(4108);
+ DBUG_RETURN(-1);
+ }//if
+ if (tAttrInfo == NULL) {
+ setErrorCodeAbort(4004);
+ DBUG_RETURN(-1);
+ }//if
+ if (tAttrInfo->m_pk) {
+ if (theOperationType == InsertRequest) {
+ DBUG_RETURN(equal_impl(tAttrInfo, aValuePassed, len));
+ } else {
+ setErrorCodeAbort(4202);
+ DBUG_RETURN(-1);
+ }//if
+ }//if
+ if (len > 8000) {
+ setErrorCodeAbort(4216);
+ DBUG_RETURN(-1);
+ }//if
+
+ tAttrId = tAttrInfo->m_attrId;
+ const char *aValue = aValuePassed;
+ Uint32 ahValue;
+ if (aValue == NULL) {
+ if (tAttrInfo->m_nullable) {
+ AttributeHeader& ah = AttributeHeader::init(&ahValue, tAttrId, 0);
+ ah.setNULL();
+ insertATTRINFO(ahValue);
+ // Insert Attribute Id with the value
+ // NULL into ATTRINFO part.
+ DBUG_RETURN(0);
+ } else {
+ /***********************************************************************
+ * Setting a NULL value on a NOT NULL attribute is not allowed.
+ **********************************************************************/
+ setErrorCodeAbort(4203);
+ DBUG_RETURN(-1);
+ }//if
+ }//if
+
+ // Insert Attribute Id into ATTRINFO part.
+ const Uint32 sizeInBytes = tAttrInfo->m_attrSize * tAttrInfo->m_arraySize;
+
+#if 0
+ tAttrSize = tAttrInfo->theAttrSize;
+ tArraySize = tAttrInfo->theArraySize;
+ if (tArraySize == 0) {
+ setErrorCodeAbort(4201);
+ return -1;
+ }//if
+ tAttrSizeInBits = tAttrSize*tArraySize;
+ tAttrSizeInWords = tAttrSizeInBits >> 5;
+#endif
+ const Uint32 bitsInLastWord = 8 * (sizeInBytes & 3) ;
+ if (len != sizeInBytes && (len != 0)) {
+ setErrorCodeAbort(4209);
+ DBUG_RETURN(-1);
+ }//if
+ const Uint32 totalSizeInWords = (sizeInBytes + 3)/4; // Including bits in last word
+ const Uint32 sizeInWords = sizeInBytes / 4; // Excluding bits in last word
+ AttributeHeader& ah = AttributeHeader::init(&ahValue, tAttrId,
+ totalSizeInWords);
+ insertATTRINFO( ahValue );
+
+ /***********************************************************************
+ * Check if the pointer of the value passed is aligned on a 4 byte boundary.
+ * If so only assign the pointer to the internal variable aValue.
+ * If it is not aligned then we start by copying the value to tempData and
+ * use this as aValue instead.
+ *************************************************************************/
+ const int attributeSize = sizeInBytes;
+ const int slack = sizeInBytes & 3;
+
+ if (((UintPtr)aValue & 3) != 0 || (slack != 0)){
+ memcpy(&tempData[0], aValue, attributeSize);
+ aValue = (char*)&tempData[0];
+ if(slack != 0) {
+ char * tmp = (char*)&tempData[0];
+ memset(&tmp[attributeSize], 0, (4 - slack));
+ }//if
+ }//if
+
+ tReturnCode = insertATTRINFOloop((Uint32*)aValue, sizeInWords);
+ if (tReturnCode == -1) {
+ DBUG_RETURN(tReturnCode);
+ }//if
+ if (bitsInLastWord != 0) {
+ tData = *(Uint32*)(aValue + sizeInWords*4);
+ tData = convertEndian(tData);
+ tData = tData & ((1 << bitsInLastWord) - 1);
+ tData = convertEndian(tData);
+ tReturnCode = insertATTRINFO(tData);
+ if (tReturnCode == -1) {
+ DBUG_RETURN(tReturnCode);
+ }//if
+ }//if
+ theErrorLine++;
+ DBUG_RETURN(0);
+}//NdbOperation::setValue()
+
+NdbBlob*
+NdbOperation::getBlobHandle(NdbTransaction* aCon, const NdbColumnImpl* tAttrInfo)
+{
+ NdbBlob* tBlob = theBlobList;
+ NdbBlob* tLastBlob = NULL;
+ while (tBlob != NULL) {
+ if (tBlob->theColumn == tAttrInfo)
+ return tBlob;
+ tLastBlob = tBlob;
+ tBlob = tBlob->theNext;
+ }
+ tBlob = theNdb->getNdbBlob();
+ if (tBlob == NULL)
+ return NULL;
+ if (tBlob->atPrepare(aCon, this, tAttrInfo) == -1) {
+ theNdb->releaseNdbBlob(tBlob);
+ return NULL;
+ }
+ if (tLastBlob == NULL)
+ theBlobList = tBlob;
+ else
+ tLastBlob->theNext = tBlob;
+ tBlob->theNext = NULL;
+ theNdbCon->theBlobFlag = true;
+ return tBlob;
+}
+
+/****************************************************************************
+ * int insertATTRINFO( Uint32 aData );
+ *
+ * Return Value: Return 0 : insertATTRINFO was succesful.
+ * Return -1: In all other case.
+ * Parameters: aData: the data to insert into ATTRINFO.
+ * Remark: Puts the the data into either TCKEYREQ signal or
+ * ATTRINFO signal.
+ *****************************************************************************/
+int
+NdbOperation::insertATTRINFO( Uint32 aData )
+{
+ NdbApiSignal* tSignal;
+ register Uint32 tAI_LenInCurrAI = theAI_LenInCurrAI;
+ register Uint32* tAttrPtr = theATTRINFOptr;
+ register Uint32 tTotCurrAILen = theTotalCurrAI_Len;
+
+ if (tAI_LenInCurrAI >= 25) {
+ Ndb* tNdb = theNdb;
+ NdbApiSignal* tFirstAttrinfo = theFirstATTRINFO;
+ tAI_LenInCurrAI = 3;
+ tSignal = tNdb->getSignal();
+ if (tSignal != NULL) {
+ tSignal->setSignal(m_attrInfoGSN);
+ tAttrPtr = &tSignal->getDataPtrSend()[3];
+ if (tFirstAttrinfo == NULL) {
+ tSignal->next(NULL);
+ theFirstATTRINFO = tSignal;
+ theCurrentATTRINFO = tSignal;
+ } else {
+ NdbApiSignal* tCurrentAttrinfoBeforeUpdate = theCurrentATTRINFO;
+ tSignal->next(NULL);
+ theCurrentATTRINFO = tSignal;
+ tCurrentAttrinfoBeforeUpdate->next(tSignal);
+ }//if
+ } else {
+ goto insertATTRINFO_error1;
+ }//if
+ }//if
+ *tAttrPtr = aData;
+ tAttrPtr++;
+ tTotCurrAILen++;
+ tAI_LenInCurrAI++;
+ theTotalCurrAI_Len = tTotCurrAILen;
+ theAI_LenInCurrAI = tAI_LenInCurrAI;
+ theATTRINFOptr = tAttrPtr;
+ return 0;
+
+insertATTRINFO_error1:
+ setErrorCodeAbort(4000);
+ return -1;
+
+}//NdbOperation::insertATTRINFO()
+
+/*****************************************************************************
+ * int insertATTRINFOloop(Uint32* aDataPtr, Uint32 aLength );
+ *
+ * Return Value: Return 0 : insertATTRINFO was succesful.
+ * Return -1: In all other case.
+ * Parameters: aDataPtr: Pointer to the data to insert into ATTRINFO.
+ * aLength: Length of data to be copied
+ * Remark: Puts the the data into either TCKEYREQ signal or
+ * ATTRINFO signal.
+ *****************************************************************************/
+int
+NdbOperation::insertATTRINFOloop(register const Uint32* aDataPtr,
+ register Uint32 aLength)
+{
+ NdbApiSignal* tSignal;
+ register Uint32 tAI_LenInCurrAI = theAI_LenInCurrAI;
+ register Uint32 tTotCurrAILen = theTotalCurrAI_Len;
+ register Uint32* tAttrPtr = theATTRINFOptr;
+ Ndb* tNdb = theNdb;
+
+ while (aLength > 0) {
+ if (tAI_LenInCurrAI >= 25) {
+ NdbApiSignal* tFirstAttrinfo = theFirstATTRINFO;
+ tAI_LenInCurrAI = 3;
+ tSignal = tNdb->getSignal();
+ if (tSignal != NULL) {
+ tSignal->setSignal(m_attrInfoGSN);
+ tAttrPtr = &tSignal->getDataPtrSend()[3];
+ if (tFirstAttrinfo == NULL) {
+ tSignal->next(NULL);
+ theFirstATTRINFO = tSignal;
+ theCurrentATTRINFO = tSignal;
+ } else {
+ NdbApiSignal* tCurrentAttrinfoBeforeUpdate = theCurrentATTRINFO;
+ tSignal->next(NULL);
+ theCurrentATTRINFO = tSignal;
+ tCurrentAttrinfoBeforeUpdate->next(tSignal);
+ }//if
+ } else {
+ goto insertATTRINFO_error1;
+ }//if
+ }//if
+ {
+ register Uint32 tData = *aDataPtr;
+ aDataPtr++;
+ aLength--;
+ tAI_LenInCurrAI++;
+ *tAttrPtr = tData;
+ tAttrPtr++;
+ tTotCurrAILen++;
+ }
+ }//while
+ theATTRINFOptr = tAttrPtr;
+ theTotalCurrAI_Len = tTotCurrAILen;
+ theAI_LenInCurrAI = tAI_LenInCurrAI;
+ return 0;
+
+insertATTRINFO_error1:
+ setErrorCodeAbort(4000);
+ return -1;
+
+}//NdbOperation::insertATTRINFOloop()
+
+
+
+
diff --git a/storage/ndb/src/ndbapi/NdbOperationExec.cpp b/storage/ndb/src/ndbapi/NdbOperationExec.cpp
new file mode 100644
index 00000000000..4200300615d
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbOperationExec.cpp
@@ -0,0 +1,580 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbOperation.hpp>
+#include <NdbTransaction.hpp>
+#include "NdbApiSignal.hpp"
+#include <Ndb.hpp>
+#include <NdbRecAttr.hpp>
+#include "NdbUtil.hpp"
+
+#include "Interpreter.hpp"
+#include <AttributeHeader.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/KeyInfo.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/ScanTab.hpp>
+
+#include <ndb_version.h>
+
+#include "API.hpp"
+#include <NdbOut.hpp>
+
+
+
+void
+NdbOperation::setLastFlag(NdbApiSignal* signal, Uint32 lastFlag)
+{
+ TcKeyReq * const req = CAST_PTR(TcKeyReq, signal->getDataPtrSend());
+ TcKeyReq::setExecuteFlag(req->requestInfo, lastFlag);
+}
+
+/******************************************************************************
+int doSend()
+
+Return Value: Return >0 : send was succesful, returns number of signals sent
+ Return -1: In all other case.
+Parameters: aProcessorId: Receiving processor node
+Remark: Sends the TCKEYREQ signal and optional KEYINFO and ATTRINFO
+ signals.
+******************************************************************************/
+int
+NdbOperation::doSend(int aNodeId, Uint32 lastFlag)
+{
+ int tReturnCode;
+ int tSignalCount = 0;
+ assert(theTCREQ != NULL);
+ setLastFlag(theTCREQ, lastFlag);
+ TransporterFacade *tp = TransporterFacade::instance();
+ tReturnCode = tp->sendSignal(theTCREQ, aNodeId);
+ tSignalCount++;
+ if (tReturnCode == -1) {
+ return -1;
+ }
+ NdbApiSignal *tSignal = theTCREQ->next();
+ while (tSignal != NULL) {
+ NdbApiSignal* tnextSignal = tSignal->next();
+ tReturnCode = tp->sendSignal(tSignal, aNodeId);
+ tSignal = tnextSignal;
+ if (tReturnCode == -1) {
+ return -1;
+ }
+ tSignalCount++;
+ }//while
+ tSignal = theFirstATTRINFO;
+ while (tSignal != NULL) {
+ NdbApiSignal* tnextSignal = tSignal->next();
+ tReturnCode = tp->sendSignal(tSignal, aNodeId);
+ tSignal = tnextSignal;
+ if (tReturnCode == -1) {
+ return -1;
+ }
+ tSignalCount++;
+ }//while
+ theNdbCon->OpSent();
+ return tSignalCount;
+}//NdbOperation::doSend()
+
+/***************************************************************************
+int prepareSend(Uint32 aTC_ConnectPtr,
+ Uint64 aTransactionId)
+
+Return Value: Return 0 : preparation of send was succesful.
+ Return -1: In all other case.
+Parameters: aTC_ConnectPtr: the Connect pointer to TC.
+ aTransactionId: the Transaction identity of the transaction.
+Remark: Puts the the data into TCKEYREQ signal and optional KEYINFO and ATTRINFO signals.
+***************************************************************************/
+int
+NdbOperation::prepareSend(Uint32 aTC_ConnectPtr, Uint64 aTransId)
+{
+ Uint32 tTransId1, tTransId2;
+ Uint32 tReqInfo;
+ Uint32 tInterpretInd = theInterpretIndicator;
+
+ theErrorLine = 0;
+
+ if (tInterpretInd != 1) {
+ OperationType tOpType = theOperationType;
+ OperationStatus tStatus = theStatus;
+ if ((tOpType == UpdateRequest) ||
+ (tOpType == InsertRequest) ||
+ (tOpType == WriteRequest)) {
+ if (tStatus != SetValue) {
+ setErrorCodeAbort(4116);
+ return -1;
+ }//if
+ } else if ((tOpType == ReadRequest) || (tOpType == ReadExclusive) ||
+ (tOpType == DeleteRequest)) {
+ if (tStatus != GetValue) {
+ setErrorCodeAbort(4116);
+ return -1;
+ }//if
+ } else {
+ setErrorCodeAbort(4005);
+ return -1;
+ }//if
+ } else {
+ if (prepareSendInterpreted() == -1) {
+ return -1;
+ }//if
+ }//if
+
+//-------------------------------------------------------------
+// We start by filling in the first 9 unconditional words of the
+// TCKEYREQ signal.
+//-------------------------------------------------------------
+ TcKeyReq * const tcKeyReq = CAST_PTR(TcKeyReq, theTCREQ->getDataPtrSend());
+
+ Uint32 tTotalCurrAI_Len = theTotalCurrAI_Len;
+ Uint32 tTableId = m_currentTable->m_tableId;
+ Uint32 tSchemaVersion = m_currentTable->m_version;
+
+ tcKeyReq->apiConnectPtr = aTC_ConnectPtr;
+ tcKeyReq->apiOperationPtr = ptr2int();
+ // Check if too much attrinfo have been defined
+ if (tTotalCurrAI_Len > TcKeyReq::MaxTotalAttrInfo){
+ setErrorCodeAbort(4257);
+ return -1;
+ }
+ Uint32 TattrLen = 0;
+ tcKeyReq->setAttrinfoLen(TattrLen, tTotalCurrAI_Len);
+ tcKeyReq->setAPIVersion(TattrLen, NDB_VERSION);
+ tcKeyReq->attrLen = TattrLen;
+
+ tcKeyReq->tableId = tTableId;
+ tcKeyReq->tableSchemaVersion = tSchemaVersion;
+ tTransId1 = (Uint32) aTransId;
+ tTransId2 = (Uint32) (aTransId >> 32);
+
+ Uint8 tSimpleIndicator = theSimpleIndicator;
+ Uint8 tCommitIndicator = theCommitIndicator;
+ Uint8 tStartIndicator = theStartIndicator;
+ Uint8 tInterpretIndicator = theInterpretIndicator;
+
+//-------------------------------------------------------------
+// Simple state is set if start and commit is set and it is
+// a read request. Otherwise it is set to zero.
+//-------------------------------------------------------------
+ Uint8 tReadInd = (theOperationType == ReadRequest);
+ Uint8 tSimpleState = tReadInd & tSimpleIndicator;
+
+ tcKeyReq->transId1 = tTransId1;
+ tcKeyReq->transId2 = tTransId2;
+
+ tReqInfo = 0;
+ if (tTotalCurrAI_Len <= TcKeyReq::MaxAttrInfo) {
+ tcKeyReq->setAIInTcKeyReq(tReqInfo, tTotalCurrAI_Len);
+ } else {
+ tcKeyReq->setAIInTcKeyReq(tReqInfo, TcKeyReq::MaxAttrInfo);
+ }//if
+
+ tcKeyReq->setSimpleFlag(tReqInfo, tSimpleIndicator);
+ tcKeyReq->setCommitFlag(tReqInfo, tCommitIndicator);
+ tcKeyReq->setStartFlag(tReqInfo, tStartIndicator);
+ tcKeyReq->setInterpretedFlag(tReqInfo, tInterpretIndicator);
+
+ Uint8 tDirtyIndicator = theDirtyIndicator;
+ OperationType tOperationType = theOperationType;
+ Uint32 tTupKeyLen = theTupKeyLen;
+ Uint8 abortOption =
+ m_abortOption != -1 ? m_abortOption : theNdbCon->m_abortOption;
+
+ tcKeyReq->setDirtyFlag(tReqInfo, tDirtyIndicator);
+ tcKeyReq->setOperationType(tReqInfo, tOperationType);
+ tcKeyReq->setKeyLength(tReqInfo, tTupKeyLen);
+
+ // A simple read is always ignore error
+ abortOption = tSimpleIndicator ? AO_IgnoreError : abortOption;
+ tcKeyReq->setAbortOption(tReqInfo, abortOption);
+
+ Uint8 tDistrKeyIndicator = theDistrKeyIndicator_;
+ Uint8 tScanIndicator = theScanInfo & 1;
+
+ tcKeyReq->setDistributionKeyFlag(tReqInfo, tDistrKeyIndicator);
+ tcKeyReq->setScanIndFlag(tReqInfo, tScanIndicator);
+
+ tcKeyReq->requestInfo = tReqInfo;
+
+//-------------------------------------------------------------
+// The next step is to fill in the upto three conditional words.
+//-------------------------------------------------------------
+ Uint32* tOptionalDataPtr = &tcKeyReq->scanInfo;
+ Uint32 tDistrGHIndex = tScanIndicator;
+ Uint32 tDistrKeyIndex = tDistrGHIndex;
+
+ Uint32 tScanInfo = theScanInfo;
+ Uint32 tDistrKey = theDistributionKey;
+
+ tOptionalDataPtr[0] = tScanInfo;
+ tOptionalDataPtr[tDistrKeyIndex] = tDistrKey;
+
+//-------------------------------------------------------------
+// The next is step is to compress the key data part of the
+// TCKEYREQ signal.
+//-------------------------------------------------------------
+ Uint32 tKeyIndex = tDistrKeyIndex + tDistrKeyIndicator;
+ Uint32* tKeyDataPtr = &tOptionalDataPtr[tKeyIndex];
+ Uint32 Tdata1 = tcKeyReq->keyInfo[0];
+ Uint32 Tdata2 = tcKeyReq->keyInfo[1];
+ Uint32 Tdata3 = tcKeyReq->keyInfo[2];
+ Uint32 Tdata4 = tcKeyReq->keyInfo[3];
+ Uint32 Tdata5;
+
+ tKeyDataPtr[0] = Tdata1;
+ tKeyDataPtr[1] = Tdata2;
+ tKeyDataPtr[2] = Tdata3;
+ tKeyDataPtr[3] = Tdata4;
+ if (tTupKeyLen > 4) {
+ Tdata1 = tcKeyReq->keyInfo[4];
+ Tdata2 = tcKeyReq->keyInfo[5];
+ Tdata3 = tcKeyReq->keyInfo[6];
+ Tdata4 = tcKeyReq->keyInfo[7];
+
+ tKeyDataPtr[4] = Tdata1;
+ tKeyDataPtr[5] = Tdata2;
+ tKeyDataPtr[6] = Tdata3;
+ tKeyDataPtr[7] = Tdata4;
+ }//if
+//-------------------------------------------------------------
+// Finally we also compress the ATTRINFO part of the signal.
+// We optimise by using the if-statement for sending KEYINFO
+// signals to calculating the new Attrinfo Index.
+//-------------------------------------------------------------
+ Uint32 tAttrInfoIndex;
+
+ if (tTupKeyLen > TcKeyReq::MaxKeyInfo) {
+ /**
+ * Set transid, TC connect ptr and length in the KEYINFO signals
+ */
+ NdbApiSignal* tSignal = theTCREQ->next();
+ Uint32 remainingKey = tTupKeyLen - TcKeyReq::MaxKeyInfo;
+ do {
+ Uint32* tSigDataPtr = tSignal->getDataPtrSend();
+ NdbApiSignal* tnextSignal = tSignal->next();
+ tSigDataPtr[0] = aTC_ConnectPtr;
+ tSigDataPtr[1] = tTransId1;
+ tSigDataPtr[2] = tTransId2;
+ if (remainingKey > KeyInfo::DataLength) {
+ // The signal is full
+ tSignal->setLength(KeyInfo::MaxSignalLength);
+ remainingKey -= KeyInfo::DataLength;
+ }
+ else {
+ // Last signal
+ tSignal->setLength(KeyInfo::HeaderLength + remainingKey);
+ remainingKey = 0;
+ }
+ tSignal = tnextSignal;
+ } while (tSignal != NULL);
+ tAttrInfoIndex = tKeyIndex + TcKeyReq::MaxKeyInfo;
+ } else {
+ tAttrInfoIndex = tKeyIndex + tTupKeyLen;
+ }//if
+
+//-------------------------------------------------------------
+// Perform the Attrinfo packing in the TCKEYREQ signal started
+// above.
+//-------------------------------------------------------------
+ Uint32* tAIDataPtr = &tOptionalDataPtr[tAttrInfoIndex];
+ Tdata1 = tcKeyReq->attrInfo[0];
+ Tdata2 = tcKeyReq->attrInfo[1];
+ Tdata3 = tcKeyReq->attrInfo[2];
+ Tdata4 = tcKeyReq->attrInfo[3];
+ Tdata5 = tcKeyReq->attrInfo[4];
+
+ theTCREQ->setLength(tcKeyReq->getAIInTcKeyReq(tReqInfo) +
+ tAttrInfoIndex + TcKeyReq::StaticLength);
+
+ tAIDataPtr[0] = Tdata1;
+ tAIDataPtr[1] = Tdata2;
+ tAIDataPtr[2] = Tdata3;
+ tAIDataPtr[3] = Tdata4;
+ tAIDataPtr[4] = Tdata5;
+
+/***************************************************
+* Send the ATTRINFO signals.
+***************************************************/
+ if (tTotalCurrAI_Len > 5) {
+ // Set the last signal's length.
+ NdbApiSignal* tSignal = theFirstATTRINFO;
+ theCurrentATTRINFO->setLength(theAI_LenInCurrAI);
+ do {
+ Uint32* tSigDataPtr = tSignal->getDataPtrSend();
+ NdbApiSignal* tnextSignal = tSignal->next();
+ tSigDataPtr[0] = aTC_ConnectPtr;
+ tSigDataPtr[1] = tTransId1;
+ tSigDataPtr[2] = tTransId2;
+ tSignal = tnextSignal;
+ } while (tSignal != NULL);
+ }//if
+ theStatus = WaitResponse;
+ theReceiver.prepareSend();
+ return 0;
+}//NdbOperation::prepareSend()
+
+/***************************************************************************
+int prepareSendInterpreted()
+
+Make preparations to send an interpreted operation.
+Return Value: Return 0 : succesful.
+ Return -1: In all other case.
+***************************************************************************/
+int
+NdbOperation::prepareSendInterpreted()
+{
+ Uint32 tTotalCurrAI_Len = theTotalCurrAI_Len;
+ Uint32 tInitReadSize = theInitialReadSize;
+ if (theStatus == ExecInterpretedValue) {
+ if (insertATTRINFO(Interpreter::EXIT_OK) != -1) {
+//-------------------------------------------------------------------------
+// Since we read the total length before inserting the last entry in the
+// signals we need to add one to the total length.
+//-------------------------------------------------------------------------
+
+ theInterpretedSize = (tTotalCurrAI_Len + 1) -
+ (tInitReadSize + 5);
+
+ } else {
+ return -1;
+ }//if
+ } else if (theStatus == FinalGetValue) {
+
+ theFinalReadSize = tTotalCurrAI_Len -
+ (tInitReadSize + theInterpretedSize + theFinalUpdateSize + 5);
+
+ } else if (theStatus == SetValueInterpreted) {
+
+ theFinalUpdateSize = tTotalCurrAI_Len -
+ (tInitReadSize + theInterpretedSize + 5);
+
+ } else if (theStatus == SubroutineEnd) {
+
+ theSubroutineSize = tTotalCurrAI_Len -
+ (tInitReadSize + theInterpretedSize +
+ theFinalUpdateSize + theFinalReadSize + 5);
+
+ } else if (theStatus == GetValue) {
+ theInitialReadSize = tTotalCurrAI_Len - 5;
+ } else {
+ setErrorCodeAbort(4116);
+ return -1;
+ }
+
+ while (theFirstBranch != NULL) {
+ Uint32 tRelAddress;
+ Uint32 tLabelAddress = 0;
+ int tAddress = -1;
+ NdbBranch* tNdbBranch = theFirstBranch;
+ Uint32 tBranchLabel = tNdbBranch->theBranchLabel;
+ NdbLabel* tNdbLabel = theFirstLabel;
+ if (tBranchLabel >= theNoOfLabels) {
+ setErrorCodeAbort(4221);
+ return -1;
+ }//if
+
+ // Find the label address
+ while (tNdbLabel != NULL) {
+ for(tLabelAddress = 0; tLabelAddress<16; tLabelAddress++){
+ const Uint32 labelNo = tNdbLabel->theLabelNo[tLabelAddress];
+ if(tBranchLabel == labelNo){
+ tAddress = tNdbLabel->theLabelAddress[tLabelAddress];
+ break;
+ }
+ }
+
+ if(tAddress != -1)
+ break;
+ tNdbLabel = tNdbLabel->theNext;
+ }//while
+ if (tAddress == -1) {
+//-------------------------------------------------------------------------
+// We were unable to find any label which the branch refers to. This means
+// that the application have not programmed the interpreter program correctly.
+//-------------------------------------------------------------------------
+ setErrorCodeAbort(4222);
+ return -1;
+ }//if
+ if (tNdbLabel->theSubroutine[tLabelAddress] != tNdbBranch->theSubroutine) {
+ setErrorCodeAbort(4224);
+ return -1;
+ }//if
+ // Now it is time to update the signal data with the relative branch jump.
+ if (tAddress < int(tNdbBranch->theBranchAddress)) {
+ tRelAddress = (tNdbBranch->theBranchAddress - tAddress) << 16;
+
+ // Indicate backward jump direction
+ tRelAddress = tRelAddress + (1 << 31);
+
+ } else if (tAddress > int(tNdbBranch->theBranchAddress)) {
+ tRelAddress = (tAddress - tNdbBranch->theBranchAddress) << 16;
+ } else {
+ setErrorCodeAbort(4223);
+ return -1;
+ }//if
+ NdbApiSignal* tSignal = tNdbBranch->theSignal;
+ Uint32 tReadData = tSignal->readData(tNdbBranch->theSignalAddress);
+ tSignal->setData((tRelAddress + tReadData), tNdbBranch->theSignalAddress);
+
+ theFirstBranch = theFirstBranch->theNext;
+ theNdb->releaseNdbBranch(tNdbBranch);
+ }//while
+
+ while (theFirstCall != NULL) {
+ Uint32 tSubroutineCount = 0;
+ int tAddress = -1;
+ NdbSubroutine* tNdbSubroutine;
+ NdbCall* tNdbCall = theFirstCall;
+ if (tNdbCall->theSubroutine >= theNoOfSubroutines) {
+ setErrorCodeAbort(4221);
+ return -1;
+ }//if
+// Find the subroutine address
+ tNdbSubroutine = theFirstSubroutine;
+ while (tNdbSubroutine != NULL) {
+ tSubroutineCount += 16;
+ if (tNdbCall->theSubroutine < tSubroutineCount) {
+// Subroutine Found
+ Uint32 tSubroutineAddress = tNdbCall->theSubroutine - (tSubroutineCount - 16);
+ tAddress = tNdbSubroutine->theSubroutineAddress[tSubroutineAddress];
+ break;
+ }//if
+ tNdbSubroutine = tNdbSubroutine->theNext;
+ }//while
+ if (tAddress == -1) {
+ setErrorCodeAbort(4222);
+ return -1;
+ }//if
+// Now it is time to update the signal data with the relative branch jump.
+ NdbApiSignal* tSignal = tNdbCall->theSignal;
+ Uint32 tReadData = tSignal->readData(tNdbCall->theSignalAddress);
+ tSignal->setData(((tAddress << 16) + tReadData), tNdbCall->theSignalAddress);
+
+ theFirstCall = theFirstCall->theNext;
+ theNdb->releaseNdbCall(tNdbCall);
+ }//while
+
+ Uint32 tInitialReadSize = theInitialReadSize;
+ Uint32 tInterpretedSize = theInterpretedSize;
+ Uint32 tFinalUpdateSize = theFinalUpdateSize;
+ Uint32 tFinalReadSize = theFinalReadSize;
+ Uint32 tSubroutineSize = theSubroutineSize;
+ if (theOperationType != OpenScanRequest &&
+ theOperationType != OpenRangeScanRequest) {
+ TcKeyReq * const tcKeyReq = CAST_PTR(TcKeyReq, theTCREQ->getDataPtrSend());
+
+ tcKeyReq->attrInfo[0] = tInitialReadSize;
+ tcKeyReq->attrInfo[1] = tInterpretedSize;
+ tcKeyReq->attrInfo[2] = tFinalUpdateSize;
+ tcKeyReq->attrInfo[3] = tFinalReadSize;
+ tcKeyReq->attrInfo[4] = tSubroutineSize;
+ } else {
+ // If a scan is defined we use the first ATTRINFO instead of TCKEYREQ.
+ theFirstATTRINFO->setData(tInitialReadSize, 4);
+ theFirstATTRINFO->setData(tInterpretedSize, 5);
+ theFirstATTRINFO->setData(tFinalUpdateSize, 6);
+ theFirstATTRINFO->setData(tFinalReadSize, 7);
+ theFirstATTRINFO->setData(tSubroutineSize, 8);
+ }//if
+ theReceiver.prepareSend();
+ return 0;
+}//NdbOperation::prepareSendInterpreted()
+
+int
+NdbOperation::checkState_TransId(NdbApiSignal* aSignal)
+{
+ Uint64 tRecTransId, tCurrTransId;
+ Uint32 tTmp1, tTmp2;
+
+ if (theStatus != WaitResponse) {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ return -1;
+ }//if
+
+ tTmp1 = aSignal->readData(2);
+ tTmp2 = aSignal->readData(3);
+
+ tRecTransId = (Uint64)tTmp1 + ((Uint64)tTmp2 << 32);
+ tCurrTransId = theNdbCon->getTransactionId();
+ if (tCurrTransId != tRecTransId) {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ return -1;
+ }//if
+ return 0;
+}//NdbOperation::checkState_TransId()
+
+/***************************************************************************
+int receiveTCKEYREF( NdbApiSignal* aSignal)
+
+Return Value: Return 0 : send was succesful.
+ Return -1: In all other case.
+Parameters: aSignal: the signal object that contains the TCKEYREF signal from TC.
+Remark: Handles the reception of the TCKEYREF signal.
+***************************************************************************/
+int
+NdbOperation::receiveTCKEYREF( NdbApiSignal* aSignal)
+{
+ if (checkState_TransId(aSignal) == -1) {
+ return -1;
+ }//if
+
+ AbortOption ao = (AbortOption)
+ (m_abortOption != -1 ? m_abortOption : theNdbCon->m_abortOption);
+ theReceiver.m_received_result_length = ~0;
+
+ theStatus = Finished;
+ // blobs want this
+ if (m_abortOption != AO_IgnoreError)
+ {
+ theNdbCon->theReturnStatus = NdbTransaction::ReturnFailure;
+ }
+ theError.code = aSignal->readData(4);
+ theNdbCon->setOperationErrorCodeAbort(aSignal->readData(4), ao);
+
+ if(theOperationType != ReadRequest || !theSimpleIndicator) // not simple read
+ return theNdbCon->OpCompleteFailure(ao, m_abortOption != AO_IgnoreError);
+
+ /**
+ * If TCKEYCONF has arrived
+ * op has completed (maybe trans has completed)
+ */
+ if(theReceiver.m_expected_result_length)
+ {
+ return theNdbCon->OpCompleteFailure(AbortOnError);
+ }
+
+ return -1;
+}
+
+
+void
+NdbOperation::handleFailedAI_ElemLen()
+{
+ NdbRecAttr* tRecAttr = theReceiver.theFirstRecAttr;
+ while (tRecAttr != NULL) {
+ tRecAttr->setNULL();
+ tRecAttr = tRecAttr->next();
+ }//while
+}//NdbOperation::handleFailedAI_ElemLen()
+
+
+
+
diff --git a/storage/ndb/src/ndbapi/NdbOperationInt.cpp b/storage/ndb/src/ndbapi/NdbOperationInt.cpp
new file mode 100644
index 00000000000..41e0cb1d140
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbOperationInt.cpp
@@ -0,0 +1,1168 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include <NdbTransaction.hpp>
+#include <Ndb.hpp>
+#include <NdbRecAttr.hpp>
+#include "NdbUtil.hpp"
+#include "Interpreter.hpp"
+#include <NdbIndexScanOperation.hpp>
+
+#ifdef VM_TRACE
+#include <NdbEnv.h>
+#define INT_DEBUG(x) \
+ { const char* tmp = NdbEnv_GetEnv("INT_DEBUG", (char*)0, 0); \
+ if (tmp != 0 && strlen(tmp) != 0) { ndbout << "INT:"; ndbout_c x; } }
+#else
+#define INT_DEBUG(x)
+#endif
+
+void
+NdbOperation::initInterpreter(){
+ theFirstLabel = NULL;
+ theLastLabel = NULL;
+ theFirstBranch = NULL;
+ theLastBranch = NULL;
+
+ theFirstCall = NULL;
+ theLastCall = NULL;
+ theFirstSubroutine = NULL;
+ theLastSubroutine = NULL;
+
+ theNoOfLabels = 0;
+ theNoOfSubroutines = 0;
+
+ theSubroutineSize = 0;
+ theInitialReadSize = 0;
+ theInterpretedSize = 0;
+ theFinalUpdateSize = 0;
+ theFinalReadSize = 0;
+ theInterpretIndicator = 1;
+
+ theTotalCurrAI_Len = 5;
+}
+
+int
+NdbOperation::incCheck(const NdbColumnImpl* tNdbColumnImpl)
+{
+ if ((theInterpretIndicator == 1)) {
+ if ((tNdbColumnImpl == NULL) ||
+ (theOperationType == OpenScanRequest ||
+ theOperationType == OpenRangeScanRequest))
+ goto inc_check_error1;
+ if ((tNdbColumnImpl->getInterpretableType() != true) ||
+ (tNdbColumnImpl->m_pk != false) ||
+ (tNdbColumnImpl->m_nullable))
+ goto inc_check_error2;
+ if (theStatus == ExecInterpretedValue) {
+ ; // Simply continue with interpretation
+ } else if (theStatus == GetValue) {
+ theInitialReadSize = theTotalCurrAI_Len - 5;
+ theStatus = ExecInterpretedValue;
+ } else if (theStatus == SubroutineExec) {
+ ; // Simply continue with interpretation
+ } else {
+ setErrorCodeAbort(4231);
+ return -1;
+ }
+ return tNdbColumnImpl->m_attrId;
+ } else {
+ if (theNdbCon->theCommitStatus == NdbTransaction::Started)
+ setErrorCodeAbort(4200);
+ }
+ return -1;
+
+ inc_check_error1:
+ if (theOperationType == OpenScanRequest ||
+ theOperationType == OpenRangeScanRequest) {
+ setErrorCodeAbort(4228);
+ return -1;
+ }
+ setErrorCodeAbort(4004);
+ return -1;
+
+ inc_check_error2:
+ if (tNdbColumnImpl->m_pk){
+ setErrorCodeAbort(4202);
+ return -1;
+ }//if
+ if (!tNdbColumnImpl->getInterpretableType()){
+ setErrorCodeAbort(4217);
+ return -1;
+ }//if
+ if (tNdbColumnImpl->m_nullable){
+ setErrorCodeAbort(4218);
+ return -1;
+ }//if
+ setErrorCodeAbort(4219);
+ return -1;
+}
+
+int
+NdbOperation::write_attrCheck(const NdbColumnImpl* tNdbColumnImpl)
+{
+ if ((theInterpretIndicator == 1)) {
+ if ((tNdbColumnImpl == NULL) ||
+ (theOperationType == OpenScanRequest ||
+ theOperationType == OpenRangeScanRequest))
+ goto write_attr_check_error1;
+ if ((tNdbColumnImpl->getInterpretableType() == false) ||
+ (tNdbColumnImpl->m_pk))
+ goto write_attr_check_error2;
+ if (theStatus == ExecInterpretedValue) {
+ ; // Simply continue with interpretation
+ } else if (theStatus == SubroutineExec) {
+ ; // Simply continue with interpretation
+ } else {
+ setErrorCodeAbort(4231);
+ return -1;
+ }
+ return tNdbColumnImpl->m_attrId;
+ } else {
+ if (theNdbCon->theCommitStatus == NdbTransaction::Started)
+ setErrorCodeAbort(4200);
+ }
+ return -1;
+
+write_attr_check_error1:
+ if (theOperationType == OpenScanRequest ||
+ theOperationType == OpenRangeScanRequest) {
+ setErrorCodeAbort(4228);
+ return -1;
+ }
+ setErrorCodeAbort(4004);
+ return -1;
+
+write_attr_check_error2:
+ if (tNdbColumnImpl->m_pk) {
+ setErrorCodeAbort(4202);
+ return -1;
+ }//if
+ if (tNdbColumnImpl->getInterpretableType() == false){
+ setErrorCodeAbort(4217);
+ return -1;
+ }//if
+ setErrorCodeAbort(4219);
+ return -1;
+}
+
+int
+NdbOperation::read_attrCheck(const NdbColumnImpl* tNdbColumnImpl)
+{
+ if ((theInterpretIndicator == 1)) {
+ if (tNdbColumnImpl == NULL)
+ goto read_attr_check_error1;
+ if (tNdbColumnImpl->getInterpretableType() == false)
+ goto read_attr_check_error2;
+ if (theStatus == ExecInterpretedValue) {
+ ; // Simply continue with interpretation
+ } else if (theStatus == GetValue) {
+ theInitialReadSize = theTotalCurrAI_Len - 5;
+ theStatus = ExecInterpretedValue;
+ } else if (theStatus == SubroutineExec) {
+ ; // Simply continue with interpretation
+ } else {
+ setErrorCodeAbort(4231);
+ return -1;
+ }
+ return tNdbColumnImpl->m_attrId;
+ } else {
+ if (theNdbCon->theCommitStatus == NdbTransaction::Started)
+ setErrorCodeAbort(4200);
+ }
+ return -1;
+
+ read_attr_check_error1:
+ setErrorCodeAbort(4004);
+ return -1;
+
+ read_attr_check_error2:
+ if (tNdbColumnImpl->getInterpretableType() == false)
+ setErrorCodeAbort(4217);
+ else
+ setErrorCodeAbort(4219);
+ return -1;
+
+}
+
+int
+NdbOperation::initial_interpreterCheck()
+{
+ if ((theInterpretIndicator == 1)) {
+ if (theStatus == ExecInterpretedValue) {
+ return 0; // Simply continue with interpretation
+ } else if (theStatus == GetValue) {
+ theInitialReadSize = theTotalCurrAI_Len - 5;
+ theStatus = ExecInterpretedValue;
+ return 0;
+ } else if (theStatus == SubroutineExec) {
+ return 0; // Simply continue with interpretation
+ } else {
+ setErrorCodeAbort(4231);
+ return -1;
+ }
+ return 0;
+ } else {
+ if (theNdbCon->theCommitStatus == NdbTransaction::Started)
+ setErrorCodeAbort(4200);
+ }
+ return -1;
+}
+
+int
+NdbOperation::labelCheck()
+{
+ if ((theInterpretIndicator == 1)) {
+ if (theStatus == ExecInterpretedValue) {
+ return 0; // Simply continue with interpretation
+ } else if (theStatus == GetValue) {
+ theInitialReadSize = theTotalCurrAI_Len - 5;
+ theStatus = ExecInterpretedValue;
+ return 0;
+ } else if (theStatus == SubroutineExec) {
+ return 0; // Simply continue with interpretation
+ } else if (theStatus == SubroutineEnd) {
+ theStatus = SubroutineExec;
+ } else {
+ setErrorCodeAbort(4231);
+ return -1;
+ }
+ return 0;
+ } else {
+ if (theNdbCon->theCommitStatus == NdbTransaction::Started)
+ setErrorCodeAbort(4200);
+ }
+ return -1;
+}
+
+int
+NdbOperation::intermediate_interpreterCheck()
+{
+ if ((theInterpretIndicator == 1)) {
+ if (theStatus == ExecInterpretedValue) {
+ return 0; // Simply continue with interpretation
+ } else if (theStatus == SubroutineExec) {
+ return 0; // Simply continue with interpretation
+ } else {
+ setErrorCodeAbort(4231);
+ return -1;
+ }
+ return 0;
+ } else {
+ if (theNdbCon->theCommitStatus == NdbTransaction::Started)
+ setErrorCodeAbort(4200);
+ }
+ return -1;
+}
+
+/*****************************************************************************
+ * int incValue(const char* anAttrName, char* aValue, Uint32 aValue)
+ *
+ * Return Value: Return 0 : incValue was succesful.
+ * Return -1: In all other case.
+ * Parameters: anAttrName : Attribute name where the attribute value
+ * will be save.
+ * aValue : The constant to increment the attribute value with.
+ *****************************************************************************/
+int
+NdbOperation::incValue(const NdbColumnImpl* tNdbColumnImpl, Uint32 aValue)
+{
+ INT_DEBUG(("incValue32 %d %u", tNdbColumnImpl->m_attrId, aValue));
+ int tAttrId;
+
+ tAttrId = incCheck(tNdbColumnImpl);
+ if (tAttrId == -1)
+ goto incValue_error1;
+
+// Load Attribute into register 6
+
+ if (insertATTRINFO( Interpreter::Read(tAttrId, 6)) == -1)
+ goto incValue_error1;
+// Load aValue into register 7
+ if (aValue < 65536)
+ {
+ if (insertATTRINFO(Interpreter::LoadConst16(7, aValue)) == -1)
+ goto incValue_error1;
+ } else {
+ if (insertATTRINFO(Interpreter::LoadConst32(7)) == -1)
+ goto incValue_error1;
+ if (insertATTRINFO(aValue) == -1)
+ goto incValue_error1;
+ }
+ // Add register 6 and 7 and put result in register 7
+
+ if (insertATTRINFO( Interpreter::Add(7, 6, 7)) == -1)
+ goto incValue_error1;
+ if (insertATTRINFO( Interpreter::Write(tAttrId, 7)) == -1)
+ goto incValue_error1;
+
+ theErrorLine++;
+ return 0;
+
+incValue_error1:
+ return -1;
+}
+
+/*****************************************************************************
+ * int subValue(const char* anAttrName, char* aValue, Uint32 aValue)
+ *
+ * Return Value: Return 0 : incValue was succesful.
+ * Return -1: In all other case.
+ * Parameters: anAttrName : Attribute name where the attribute value
+ * will be save.
+ * aValue : The constant to increment the attribute value with.
+******************************************************************************/
+int
+NdbOperation::subValue(const NdbColumnImpl* tNdbColumnImpl, Uint32 aValue)
+{
+ INT_DEBUG(("subValue32 %d %u", tNdbColumnImpl->m_attrId, aValue));
+ int tAttrId;
+
+ tAttrId = incCheck(tNdbColumnImpl);
+ if (tAttrId == -1)
+ goto subValue_error1;
+
+// Load Attribute into register 6
+
+ if (insertATTRINFO( Interpreter::Read(tAttrId, 6)) == -1)
+ goto subValue_error1;
+// Load aValue into register 7
+ if (aValue < 65536)
+ {
+ if (insertATTRINFO( Interpreter::LoadConst16(7, aValue)) == -1)
+ goto subValue_error1;
+ } else {
+ if (insertATTRINFO( Interpreter::LoadConst32(7)) == -1)
+ goto subValue_error1;
+ if (insertATTRINFO(aValue) == -1)
+ goto subValue_error1;
+ }
+ // Subtract register 6 and 7 and put result in register 7
+
+ if (insertATTRINFO( Interpreter::Sub(7, 6, 7)) == -1)
+ goto subValue_error1;
+ if (insertATTRINFO( Interpreter::Write(tAttrId, 7)) == -1)
+ goto subValue_error1;
+
+ theErrorLine++;
+ return 0;
+
+ subValue_error1:
+ return -1;
+}
+
+/******************************************************************************
+ * int incValue(const char* anAttrName, char* aValue, Uint64 aValue)
+ *
+ * Return Value: Return 0 : incValue was succesful.
+ * Return -1: In all other case.
+ * Parameters: anAttrName : Attribute name where the attribute value will
+ * be save.
+ * aValue : The constant to increment the attribute value with.
+ *****************************************************************************/
+int
+NdbOperation::incValue(const NdbColumnImpl* tNdbColumnImpl, Uint64 aValue)
+{
+ INT_DEBUG(("incValue64 %d %llu", tNdbColumnImpl->m_attrId, aValue));
+ int tAttrId;
+
+ tAttrId = incCheck(tNdbColumnImpl);
+ if (tAttrId == -1)
+ goto incValue_error1;
+
+// Load Attribute into register 6
+
+ if (insertATTRINFO( Interpreter::Read(tAttrId, 6)) == -1)
+ goto incValue_error1;
+// Load aValue into register 7
+ if (insertATTRINFO( Interpreter::LoadConst64(7)) == -1)
+ goto incValue_error1;
+ if (insertATTRINFOloop((Uint32*)&aValue, 2) == -1)
+ goto incValue_error1;
+ // Add register 6 and 7 and put result in register 7
+ if (insertATTRINFO( Interpreter::Add(7, 6, 7)) == -1)
+ goto incValue_error1;
+ if (insertATTRINFO( Interpreter::Write(tAttrId, 7)) == -1)
+ goto incValue_error1;
+
+ theErrorLine++;
+ return 0;
+
+incValue_error1:
+ return -1;
+}
+
+/*****************************************************************************
+ * int subValue(const char* anAttrName, char* aValue, Uint64 aValue)
+ *
+ * Return Value: Return 0 : incValue was succesful.
+ * Return -1: In all other case.
+ * Parameters: anAttrName : Attribute name where the attribute value will
+ * be save.
+ * aValue : The constant to increment the attribute value with.
+******************************************************************************/
+int
+NdbOperation::subValue(const NdbColumnImpl* tNdbColumnImpl, Uint64 aValue)
+{
+ INT_DEBUG(("subValue64 %d %llu", tNdbColumnImpl->m_attrId, aValue));
+ int tAttrId;
+
+ tAttrId = incCheck(tNdbColumnImpl);
+ if (tAttrId == -1)
+ goto subValue_error1;
+
+// Load Attribute into register 6
+
+ if (insertATTRINFO( Interpreter::Read(6, tAttrId)) == -1)
+ goto subValue_error1;
+// Load aValue into register 7
+ if (insertATTRINFO( Interpreter::LoadConst64(7)) == -1)
+ goto subValue_error1;
+ if (insertATTRINFOloop((Uint32*)&aValue, 2) == -1)
+ goto subValue_error1;
+// Subtract register 6 and 7 and put result in register 7
+ if (insertATTRINFO( Interpreter::Sub(7, 6, 7)) == -1)
+ goto subValue_error1;
+ if (insertATTRINFO( Interpreter::Write(tAttrId, 7)) == -1)
+ goto subValue_error1;
+
+ theErrorLine++;
+ return 0;
+
+subValue_error1:
+ return -1;
+}
+
+/*****************************************************************************
+ * int NdbOperation::def_label()
+ *****************************************************************************/
+int
+NdbOperation::def_label(int tLabelNo)
+{
+ INT_DEBUG(("def_label %d", tLabelNo));
+ Uint32 tLabelIndex;
+ if (labelCheck() == -1)
+ return -1;
+
+ tLabelIndex = theNoOfLabels - ((theNoOfLabels >> 4) << 4);
+ if (tLabelIndex == 0)
+ {
+ NdbLabel* tNdbLabel = theNdb->getNdbLabel();
+ if (tNdbLabel == NULL)
+ {
+ setErrorCodeAbort(4000);
+ return -1;
+ }
+ if (theFirstLabel == NULL)
+ theFirstLabel = tNdbLabel;
+ else
+ theLastLabel->theNext = tNdbLabel;
+
+ theLastLabel = tNdbLabel;
+ tNdbLabel->theNext = NULL;
+ }
+
+ /**
+ * Here we set the address that the label should point to (jump address),
+ * the first 5 words are excluded since they are length specifications and
+ * not part of the data.
+ * We need to add 1 to the current ATTRINFO length since the last inserted
+ * item is not where we want to jump to.
+ * Later on we will update the branch items with this address, this is done in
+ * the NdbOperation::prepareSendInterpreted method.
+ */
+
+ theLastLabel->theLabelNo[tLabelIndex] = tLabelNo;
+ theLastLabel->theLabelAddress[tLabelIndex] = (theTotalCurrAI_Len + 1) - (theInitialReadSize + 5);
+ theLastLabel->theSubroutine[tLabelIndex] = theNoOfSubroutines;
+ theNoOfLabels++;
+ theErrorLine++;
+ return (theNoOfLabels - 1);
+}
+
+/************************************************************************************************
+int NdbOperation::def_subroutine()
+
+************************************************************************************************/
+int
+NdbOperation::def_subroutine(int tSubNo)
+{
+ INT_DEBUG(("def_subroutine %d", tSubNo));
+ Uint32 tSubroutineIndex;
+
+ if (theInterpretIndicator != 1)
+ {
+ setErrorCodeAbort(4200);
+ return -1;
+ }
+
+ if (int(theNoOfSubroutines) != tSubNo)
+ {
+ setErrorCodeAbort(4227);
+ return -1;
+ }
+ if (theStatus == FinalGetValue)
+ {
+ theFinalReadSize = theTotalCurrAI_Len -
+ (theInitialReadSize + theInterpretedSize +
+ theFinalUpdateSize + 5);
+
+ } else if (theStatus == SubroutineEnd)
+ {
+ ; // Correct Status, last call was ret_sub()
+ } else if (theStatus == ExecInterpretedValue)
+ {
+ if (insertATTRINFO(Interpreter::EXIT_OK) == -1)
+ return -1;
+ theInterpretedSize = theTotalCurrAI_Len - (theInitialReadSize + 5);
+ } else if (theStatus == SetValueInterpreted)
+ {
+ theFinalUpdateSize = theTotalCurrAI_Len -
+ (theInitialReadSize + theInterpretedSize + 5);
+
+ } else if (theStatus == GetValue)
+ {
+
+ theInitialReadSize = theTotalCurrAI_Len - 5;
+
+ } else
+ {
+ setErrorCodeAbort(4200);
+ return -1;
+ }
+ theStatus = SubroutineExec;
+ tSubroutineIndex = theNoOfSubroutines - ((theNoOfSubroutines >> 4) << 4);
+ if (tSubroutineIndex == 0)
+ {
+ NdbSubroutine* tNdbSubroutine = theNdb->getNdbSubroutine();
+ if (tNdbSubroutine == NULL)
+ {
+ setErrorCodeAbort(4000);
+ return -1;
+ }
+ if (theFirstSubroutine == NULL)
+ theFirstSubroutine = tNdbSubroutine;
+ else
+ theLastSubroutine->theNext = tNdbSubroutine;
+
+ theLastSubroutine = tNdbSubroutine;
+ tNdbSubroutine->theNext = NULL;
+ }
+ theLastSubroutine->theSubroutineAddress[tSubroutineIndex] = theTotalCurrAI_Len -
+ (theInitialReadSize + theInterpretedSize +
+ theFinalUpdateSize + theFinalReadSize);
+ theNoOfSubroutines++;
+ theErrorLine++;
+ return (theNoOfSubroutines - 1);
+}
+
+/************************************************************************************************
+int NdbOperation::add_reg(Uint32 RegSource1, Uint32 RegSource2, Uint32 RegDest)
+
+************************************************************************************************/
+int
+NdbOperation::add_reg(Uint32 RegSource1, Uint32 RegSource2, Uint32 RegDest)
+{
+ INT_DEBUG(("add_reg %u %u %u", RegSource1, RegSource2, RegDest));
+ if (intermediate_interpreterCheck() == -1)
+ return -1;
+
+ if (RegSource1 >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (RegSource2 >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (RegDest >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (insertATTRINFO( Interpreter::Add(RegDest, RegSource1, RegSource2)) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+/************************************************************************************************
+int NdbOperation::sub_reg(Uint32 RegSource1, Uint32 RegSource2, Uint32 RegDest)
+
+************************************************************************************************/
+int
+NdbOperation::sub_reg(Uint32 RegSource1, Uint32 RegSource2, Uint32 RegDest)
+{
+ INT_DEBUG(("sub_reg %u %u %u", RegSource1, RegSource2, RegDest));
+ if (intermediate_interpreterCheck() == -1)
+ return -1;
+
+ if (RegSource1 >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (RegSource2 >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (RegDest >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (insertATTRINFO( Interpreter::Sub(RegDest, RegSource1, RegSource2)) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+/************************************************************************************************
+int NdbOperation::load_const_u32(Uint32 RegDest, Uint32 Constant)
+
+************************************************************************************************/
+int
+NdbOperation::load_const_u32(Uint32 RegDest, Uint32 Constant)
+{
+ INT_DEBUG(("load_const_u32 %u %u", RegDest, Constant));
+ if (initial_interpreterCheck() == -1)
+ goto l_u32_error1;
+ if (RegDest >= 8)
+ goto l_u32_error2;
+ if (insertATTRINFO( Interpreter::LoadConst32(RegDest)) == -1)
+ goto l_u32_error1;
+ if (insertATTRINFO(Constant) == -1)
+ goto l_u32_error1;
+ theErrorLine++;
+ return 0;
+
+ l_u32_error1:
+ return -1;
+
+ l_u32_error2:
+ setErrorCodeAbort(4229);
+ return -1;
+}
+
+/************************************************************************************************
+int NdbOperation::load_const_u64(Uint32 RegDest, Uint64 Constant)
+
+************************************************************************************************/
+int
+NdbOperation::load_const_u64(Uint32 RegDest, Uint64 Constant)
+{
+ INT_DEBUG(("load_const_u64 %u %llu", RegDest, Constant));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (RegDest >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+
+ // 64 bit value
+ if (insertATTRINFO( Interpreter::LoadConst64(RegDest)) == -1)
+ return -1;
+ if (insertATTRINFOloop((Uint32*)&Constant, 2) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+/************************************************************************************************
+int NdbOperation::load_const_null(Uint32 RegDest)
+
+************************************************************************************************/
+int
+NdbOperation::load_const_null(Uint32 RegDest)
+{
+ INT_DEBUG(("load_const_null %u", RegDest));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (RegDest >= 8)
+ {
+ setErrorCodeAbort(4229);
+ return -1;
+ }
+ if (insertATTRINFO( Interpreter::LOAD_CONST_NULL) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+/************************************************************************************************
+int NdbOperation::read_attr(const char* anAttrName, Uint32 RegDest)
+
+************************************************************************************************/
+int
+NdbOperation::read_attr(const NdbColumnImpl* anAttrObject, Uint32 RegDest)
+{
+ INT_DEBUG(("read_attr %d %u", anAttrObject->m_attrId, RegDest));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+
+ int tAttrId = read_attrCheck(anAttrObject);
+ if (tAttrId == -1)
+ goto read_attr_error1;
+ if (RegDest >= 8)
+ goto read_attr_error2;
+ if (insertATTRINFO( Interpreter::Read(tAttrId, RegDest)) != -1) {
+ return 0;
+ theErrorLine++;
+ }//if
+ return -1;
+
+read_attr_error1:
+ return -1;
+
+read_attr_error2:
+ setErrorCodeAbort(4229);
+ return -1;
+}
+
+/************************************************************************************************
+int NdbOperation::write_attr(const char* anAttrName, Uint32 RegSource)
+
+************************************************************************************************/
+int
+NdbOperation::write_attr(const NdbColumnImpl* anAttrObject, Uint32 RegSource)
+{
+ INT_DEBUG(("write_attr %d %u", anAttrObject->m_attrId, RegSource));
+ int tAttrId = write_attrCheck(anAttrObject);
+ if (tAttrId == -1)
+ return -1;
+ if (insertATTRINFO( Interpreter::Write(tAttrId, RegSource)) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::branch_reg_reg(Uint32 type,
+ Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ if (intermediate_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO(Interpreter::Branch(type, RegLvalue, RegRvalue)) == -1)
+ return -1;
+ if (insertBranch(Label) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::branch_ge(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_ge %u %u %u", RegLvalue, RegRvalue, Label));
+ return branch_reg_reg(Interpreter::BRANCH_GE_REG_REG,
+ RegLvalue, RegRvalue, Label);
+}
+
+int
+NdbOperation::branch_gt(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_gt %u %u %u", RegLvalue, RegRvalue, Label));
+ return branch_reg_reg(Interpreter::BRANCH_GT_REG_REG,
+ RegLvalue, RegRvalue, Label);
+}
+
+int
+NdbOperation::branch_le(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_le %u %u %u", RegLvalue, RegRvalue, Label));
+ return branch_reg_reg(Interpreter::BRANCH_LE_REG_REG,
+ RegLvalue, RegRvalue, Label);
+}
+
+int
+NdbOperation::branch_lt(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_lt %u %u %u", RegLvalue, RegRvalue, Label));
+ return branch_reg_reg(Interpreter::BRANCH_LT_REG_REG,
+ RegLvalue, RegRvalue, Label);
+}
+
+int
+NdbOperation::branch_eq(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_eq %u %u %u", RegLvalue, RegRvalue, Label));
+ return branch_reg_reg(Interpreter::BRANCH_EQ_REG_REG,
+ RegLvalue, RegRvalue, Label);
+}
+
+int
+NdbOperation::branch_ne(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_ne %u %u %u", RegLvalue, RegRvalue, Label));
+ return branch_reg_reg(Interpreter::BRANCH_NE_REG_REG,
+ RegLvalue, RegRvalue, Label);
+}
+
+int
+NdbOperation::branch_ne_null(Uint32 RegLvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_ne_null %u %u", RegLvalue, Label));
+ if (intermediate_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO((RegLvalue << 6) + Interpreter::BRANCH_REG_NE_NULL) == -1)
+ return -1;
+ if (insertBranch(Label) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::branch_eq_null(Uint32 RegLvalue, Uint32 Label)
+{
+ INT_DEBUG(("branch_eq_null %u %u", RegLvalue, Label));
+ if (intermediate_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO((RegLvalue << 6) + Interpreter::BRANCH_REG_EQ_NULL) == -1)
+ return -1;
+ if (insertBranch(Label) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::branch_label(Uint32 Label)
+{
+ INT_DEBUG(("branch_label %u", Label));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO(Interpreter::BRANCH) == -1)
+ return -1;
+ if (insertBranch(Label) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+/************************************************************************************************
+int NdbOperation::interpret_exit_ok()
+
+************************************************************************************************/
+int
+NdbOperation::interpret_exit_ok()
+{
+ INT_DEBUG(("interpret_exit_ok"));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO(Interpreter::EXIT_OK) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::interpret_exit_last_row()
+{
+ INT_DEBUG(("interpret_exit_last_row"));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO(Interpreter::EXIT_OK_LAST) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+/************************************************************************************************
+int NdbOperation::interpret_exit_nok(Uint32 ErrorCode)
+
+************************************************************************************************/
+int
+NdbOperation::interpret_exit_nok(Uint32 ErrorCode)
+{
+ INT_DEBUG(("interpret_exit_nok %u", ErrorCode));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO( (ErrorCode << 16) + Interpreter::EXIT_REFUSE) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::interpret_exit_nok()
+{
+ INT_DEBUG(("interpret_exit_nok"));
+ Uint32 ErrorCode = 899;
+
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO( (ErrorCode << 16) + Interpreter::EXIT_REFUSE) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::call_sub(Uint32 Subroutine)
+{
+ INT_DEBUG(("call_sub %u", Subroutine));
+ if (initial_interpreterCheck() == -1)
+ return -1;
+ if (insertATTRINFO( (Subroutine << 16) + Interpreter::CALL) == -1)
+ return -1;
+ if (insertCall(Subroutine) == -1)
+ return -1;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::ret_sub()
+{
+ INT_DEBUG(("ret_sub"));
+ if (theInterpretIndicator != 1)
+ {
+ setErrorCodeAbort(4200);
+ return -1;
+ }
+ if (theStatus == SubroutineExec)
+ {
+ ; // Simply continue with interpretation
+ } else
+ {
+ setErrorCodeAbort(4200);
+ return -1;
+ }
+ if (insertATTRINFO(Interpreter::RETURN) == -1)
+ return -1;
+ theStatus = SubroutineEnd;
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::insertBranch(Uint32 aLabel)
+{
+ Uint32 tAddress;
+ NdbBranch* tBranch = theNdb->getNdbBranch();
+ if (tBranch == NULL)
+ goto insertBranch_error1;
+ if (theFirstBranch == NULL)
+ theFirstBranch = tBranch;
+ else
+ theLastBranch->theNext = tBranch;
+ theLastBranch = tBranch;
+ if (theNoOfSubroutines == 0)
+ tAddress = theTotalCurrAI_Len -
+ (theInitialReadSize + 5);
+ else
+ tAddress = theTotalCurrAI_Len -
+ (theInitialReadSize + theInterpretedSize +
+ theFinalUpdateSize + theFinalReadSize + 5);
+
+ tBranch->theBranchAddress = tAddress;
+ tBranch->theSignal = theCurrentATTRINFO;
+ tBranch->theSignalAddress = theAI_LenInCurrAI; // + 1; theAI_LenInCurrAI has already been updated in
+ tBranch->theSubroutine = theNoOfSubroutines; // insertATTRINFO which was done before insertBranch!!
+ tBranch->theBranchLabel = aLabel;
+ return 0;
+
+insertBranch_error1:
+ setErrorCodeAbort(4000);
+ return -1;
+}
+
+int
+NdbOperation::insertCall(Uint32 aCall)
+{
+ NdbCall* tCall = theNdb->getNdbCall();
+ if (tCall == NULL)
+ {
+ setErrorCodeAbort(4000);
+ return -1;
+ }
+ if (theFirstCall == NULL)
+ theFirstCall = tCall;
+ else
+ theLastCall->theNext = tCall;
+ theLastCall = tCall;
+
+ tCall->theSignal = theCurrentATTRINFO;
+ tCall->theSignalAddress = theAI_LenInCurrAI;
+ tCall->theSubroutine = aCall;
+ return 0;
+}
+
+int
+NdbOperation::branch_col(Uint32 type,
+ Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+
+ DBUG_ENTER("NdbOperation::branch_col");
+ DBUG_PRINT("enter", ("type=%u col=%u val=0x%x len=%u label=%u",
+ type, ColId, val, len, Label));
+ if (val != NULL)
+ DBUG_DUMP("value", (char*)val, len);
+
+ if (initial_interpreterCheck() == -1)
+ DBUG_RETURN(-1);
+
+ Interpreter::BinaryCondition c = (Interpreter::BinaryCondition)type;
+
+ const NdbColumnImpl * col =
+ m_currentTable->getColumn(ColId);
+
+ if(col == 0){
+ abort();
+ }
+
+ if (val == NULL)
+ len = 0;
+ else {
+ if (! col->getStringType()) {
+ // prevent assert in NdbSqlUtil on length error
+ Uint32 sizeInBytes = col->m_attrSize * col->m_arraySize;
+ if (len != 0 && len != sizeInBytes)
+ {
+ setErrorCodeAbort(4209);
+ DBUG_RETURN(-1);
+ }
+ len = sizeInBytes;
+ }
+ }
+
+ Uint32 tempData[2000];
+ if (((UintPtr)val & 3) != 0) {
+ memcpy(tempData, val, len);
+ val = tempData;
+ }
+
+ if (insertATTRINFO(Interpreter::BranchCol(c, 0, 0, false)) == -1)
+ DBUG_RETURN(-1);
+
+ if (insertBranch(Label) == -1)
+ DBUG_RETURN(-1);
+
+ if (insertATTRINFO(Interpreter::BranchCol_2(ColId, len)))
+ DBUG_RETURN(-1);
+
+ Uint32 len2 = Interpreter::mod4(len);
+ if(len2 == len){
+ insertATTRINFOloop((Uint32*)val, len2 >> 2);
+ } else {
+ len2 -= 4;
+ insertATTRINFOloop((Uint32*)val, len2 >> 2);
+ Uint32 tmp = 0;
+ for (Uint32 i = 0; i < len-len2; i++) {
+ char* p = (char*)&tmp;
+ p[i] = ((char*)val)[len2+i];
+ }
+ insertATTRINFO(tmp);
+ }
+
+ theErrorLine++;
+ DBUG_RETURN(0);
+}
+
+int
+NdbOperation::branch_col_eq(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_eq %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::EQ, ColId, val, len, nopad, Label);
+}
+
+int
+NdbOperation::branch_col_ne(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_ne %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::NE, ColId, val, len, nopad, Label);
+}
+int
+NdbOperation::branch_col_lt(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_lt %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::LT, ColId, val, len, nopad, Label);
+}
+int
+NdbOperation::branch_col_le(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_le %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::LE, ColId, val, len, nopad, Label);
+}
+int
+NdbOperation::branch_col_gt(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_gt %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::GT, ColId, val, len, nopad, Label);
+}
+
+int
+NdbOperation::branch_col_ge(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_ge %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::GE, ColId, val, len, nopad, Label);
+}
+
+int
+NdbOperation::branch_col_like(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_like %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label));
+ return branch_col(Interpreter::LIKE, ColId, val, len, nopad, Label);
+}
+
+int
+NdbOperation::branch_col_notlike(Uint32 ColId, const void * val, Uint32 len,
+ bool nopad, Uint32 Label){
+ INT_DEBUG(("branch_col_notlike %u %.*s(%u,%d) -> %u", ColId,len,val,len,nopad,Label));
+ return branch_col(Interpreter::NOT_LIKE, ColId, val, len, nopad, Label);
+}
+
+int
+NdbOperation::branch_col_null(Uint32 type, Uint32 ColId, Uint32 Label){
+
+ if (initial_interpreterCheck() == -1)
+ return -1;
+
+ if (insertATTRINFO(type) == -1)
+ return -1;
+
+ if (insertBranch(Label) == -1)
+ return -1;
+
+ if (insertATTRINFO(Interpreter::BranchCol_2(ColId)))
+ return -1;
+
+ theErrorLine++;
+ return 0;
+}
+
+int
+NdbOperation::branch_col_eq_null(Uint32 ColId, Uint32 Label){
+
+ INT_DEBUG(("branch_col_eq_null %u -> %u", ColId, Label));
+ return branch_col_null(Interpreter::BRANCH_ATTR_EQ_NULL, ColId, Label);
+}
+
+int
+NdbOperation::branch_col_ne_null(Uint32 ColId, Uint32 Label){
+
+ INT_DEBUG(("branch_col_ne_null %u -> %u", ColId, Label));
+ return branch_col_null(Interpreter::BRANCH_ATTR_NE_NULL, ColId, Label);
+}
+
diff --git a/storage/ndb/src/ndbapi/NdbOperationScan.cpp b/storage/ndb/src/ndbapi/NdbOperationScan.cpp
new file mode 100644
index 00000000000..283eb591bdb
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbOperationScan.cpp
@@ -0,0 +1,16 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
diff --git a/storage/ndb/src/ndbapi/NdbOperationSearch.cpp b/storage/ndb/src/ndbapi/NdbOperationSearch.cpp
new file mode 100644
index 00000000000..06d8ddd412b
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbOperationSearch.cpp
@@ -0,0 +1,592 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+/******************************************************************************
+Name: NdbOperationSearch.C
+Include:
+Link:
+Author: UABMNST Mona Natterkvist UAB/B/SD
+Date: 970829
+Version: 0.1
+Description: Interface between TIS and NDB
+Documentation:
+Adjust: 971022 UABMNST First version.
+ 971206 UABRONM
+ *****************************************************************************/
+#include "API.hpp"
+
+#include <NdbOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include <NdbTransaction.hpp>
+#include <Ndb.hpp>
+#include "NdbImpl.hpp"
+#include <NdbOut.hpp>
+
+#include <AttributeHeader.hpp>
+#include <signaldata/TcKeyReq.hpp>
+#include <signaldata/KeyInfo.hpp>
+#include "NdbDictionaryImpl.hpp"
+#include <md5_hash.hpp>
+
+/******************************************************************************
+CondIdType equal(const char* anAttrName, char* aValue, Uint32 aVarKeylen);
+
+Return Value Return 0 : Equal was successful.
+ Return -1: In all other case.
+Parameters: anAttrName : Attribute name for search condition..
+ aValue : Referense to the search value.
+ aVariableKeylen : The length of key in bytes
+Remark: Defines search condition with equality anAttrName.
+******************************************************************************/
+int
+NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo,
+ const char* aValuePassed,
+ Uint32 aVariableKeyLen)
+{
+ DBUG_ENTER("NdbOperation::equal_impl");
+ DBUG_PRINT("enter", ("col=%s op=%d val=0x%x len=%u",
+ tAttrInfo->m_name.c_str(),
+ theOperationType,
+ aValuePassed, aVariableKeyLen));
+ if (aValuePassed != NULL)
+ DBUG_DUMP("value", (char*)aValuePassed, aVariableKeyLen);
+
+ register Uint32 tAttrId;
+
+ Uint32 tData;
+ Uint32 tKeyInfoPosition;
+ const char* aValue = aValuePassed;
+ Uint64 tempData[512];
+
+ if ((theStatus == OperationDefined) &&
+ (aValue != NULL) &&
+ (tAttrInfo != NULL )) {
+/******************************************************************************
+ * Start by checking that the attribute is a tuple key.
+ * This value is also the word order in the tuple key of this
+ * tuple key attribute.
+ * Then check that this tuple key has not already been defined.
+ * Finally check if all tuple key attributes have been defined. If
+ * this is true then set Operation state to tuple key defined.
+ *****************************************************************************/
+ tAttrId = tAttrInfo->m_attrId;
+ tKeyInfoPosition = tAttrInfo->m_keyInfoPos;
+ bool tDistrKey = tAttrInfo->m_distributionKey;
+
+ Uint32 i = 0;
+ if (tAttrInfo->m_pk) {
+ Uint32 tKeyDefined = theTupleKeyDefined[0][2];
+ Uint32 tKeyAttrId = theTupleKeyDefined[0][0];
+ do {
+ if (tKeyDefined == false) {
+ goto keyEntryFound;
+ } else {
+ if (tKeyAttrId != tAttrId) {
+ /******************************************************************
+ * We read the key defined variable in advance.
+ * It could potentially read outside its area when
+ * i = MAXNROFTUPLEKEY - 1,
+ * it is not a problem as long as the variable
+ * theTupleKeyDefined is defined
+ * in the middle of the object.
+ * Reading wrong data and not using it causes no problems.
+ *****************************************************************/
+ i++;
+ tKeyAttrId = theTupleKeyDefined[i][0];
+ tKeyDefined = theTupleKeyDefined[i][2];
+ continue;
+ } else {
+ goto equal_error2;
+ }//if
+ }//if
+ } while (i < NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY);
+ goto equal_error2;
+ } else {
+ goto equal_error1;
+ }
+ /*************************************************************************
+ * Now it is time to retrieve the tuple key data from the pointer supplied
+ * by the application.
+ * We have to retrieve the size of the attribute in words and bits.
+ *************************************************************************/
+ keyEntryFound:
+ theTupleKeyDefined[i][0] = tAttrId;
+ theTupleKeyDefined[i][1] = tKeyInfoPosition;
+ theTupleKeyDefined[i][2] = true;
+
+ OperationType tOpType = theOperationType;
+ Uint32 sizeInBytes = tAttrInfo->m_attrSize * tAttrInfo->m_arraySize;
+
+ {
+ /************************************************************************
+ * Check if the pointer of the value passed is aligned on a 4 byte
+ * boundary. If so only assign the pointer to the internal variable
+ * aValue. If it is not aligned then we start by copying the value to
+ * tempData and use this as aValue instead.
+ ***********************************************************************/
+ const int attributeSize = sizeInBytes;
+ const int slack = sizeInBytes & 3;
+ const int align = UintPtr(aValue) & 7;
+
+ if (((align & 3) != 0) || (slack != 0) || (tDistrKey && (align != 0)))
+ {
+ ((Uint32*)tempData)[attributeSize >> 2] = 0;
+ memcpy(&tempData[0], aValue, attributeSize);
+ aValue = (char*)&tempData[0];
+ }//if
+ }
+
+ Uint32 totalSizeInWords = (sizeInBytes + 3)/4; // Inc. bits in last word
+
+ if (true){ //tArraySize != 0) {
+ Uint32 tTupKeyLen = theTupKeyLen;
+
+ theTupKeyLen = tTupKeyLen + totalSizeInWords;
+ if ((aVariableKeyLen == sizeInBytes) ||
+ (aVariableKeyLen == 0)) {
+ ;
+ } else {
+ goto equal_error3;
+ }
+ }
+#if 0
+ else {
+ /************************************************************************
+ * The attribute is a variable array. We need to use the length parameter
+ * to know the size of this attribute in the key information and
+ * variable area. A key is however not allowed to be larger than 4
+ * kBytes and this is checked for variable array attributes
+ * used as keys.
+ ************************************************************************/
+ Uint32 tMaxVariableKeyLenInWord = (MAXTUPLEKEYLENOFATTERIBUTEINWORD -
+ tKeyInfoPosition);
+ tAttrSizeInBits = aVariableKeyLen << 3;
+ tAttrSizeInWords = tAttrSizeInBits >> 5;
+ tAttrBitsInLastWord = tAttrSizeInBits - (tAttrSizeInWords << 5);
+ tAttrLenInWords = ((tAttrSizeInBits + 31) >> 5);
+ if (tAttrLenInWords > tMaxVariableKeyLenInWord) {
+ setErrorCodeAbort(4207);
+ return -1;
+ }//if
+ theTupKeyLen = theTupKeyLen + tAttrLenInWords;
+ }//if
+#endif
+
+ /**************************************************************************
+ * If the operation is an insert request and the attribute is stored then
+ * we also set the value in the stored part through putting the
+ * information in the ATTRINFO signals.
+ *************************************************************************/
+ if ((tOpType == InsertRequest) ||
+ (tOpType == WriteRequest)) {
+ Uint32 ahValue;
+ const Uint32 sz = totalSizeInWords;
+
+ // XXX
+ if(m_accessTable == m_currentTable)
+ {
+ AttributeHeader::init(&ahValue, tAttrId, sz);
+ }
+ else
+ {
+ assert(m_accessTable->m_index);
+ int attr_id_current_table =
+ m_accessTable->m_index->m_columns[tAttrId]->m_keyInfoPos;
+ AttributeHeader::init(&ahValue, attr_id_current_table, sz);
+ }
+
+ insertATTRINFO( ahValue );
+ insertATTRINFOloop((Uint32*)aValue, sz);
+ }//if
+
+ /**************************************************************************
+ * Store the Key information in the TCKEYREQ and KEYINFO signals.
+ *************************************************************************/
+ if (insertKEYINFO(aValue, tKeyInfoPosition, totalSizeInWords) != -1) {
+ /************************************************************************
+ * Add one to number of tuple key attributes defined.
+ * If all have been defined then set the operation state to indicate
+ * that tuple key is defined.
+ * Thereby no more search conditions are allowed in this version.
+ ***********************************************************************/
+ Uint32 tNoKeysDef = theNoOfTupKeyLeft - 1;
+ Uint32 tErrorLine = theErrorLine;
+ unsigned char tInterpretInd = theInterpretIndicator;
+ theNoOfTupKeyLeft = tNoKeysDef;
+ tErrorLine++;
+ theErrorLine = tErrorLine;
+
+ if (tNoKeysDef == 0) {
+ if (tOpType == UpdateRequest) {
+ if (tInterpretInd == 1) {
+ theStatus = GetValue;
+ } else {
+ theStatus = SetValue;
+ }//if
+ DBUG_RETURN(0);
+ } else if ((tOpType == ReadRequest) || (tOpType == DeleteRequest) ||
+ (tOpType == ReadExclusive)) {
+ theStatus = GetValue;
+ // create blob handles automatically
+ if (tOpType == DeleteRequest && m_currentTable->m_noOfBlobs != 0) {
+ for (unsigned i = 0; i < m_currentTable->m_columns.size(); i++) {
+ NdbColumnImpl* c = m_currentTable->m_columns[i];
+ assert(c != 0);
+ if (c->getBlobType()) {
+ if (getBlobHandle(theNdbCon, c) == NULL)
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+ } else if ((tOpType == InsertRequest) || (tOpType == WriteRequest)) {
+ theStatus = SetValue;
+ DBUG_RETURN(0);
+ } else {
+ setErrorCodeAbort(4005);
+ DBUG_RETURN(-1);
+ }//if
+ DBUG_RETURN(0);
+ }//if
+ } else {
+ DBUG_RETURN(-1);
+ }//if
+ DBUG_RETURN(0);
+ }
+
+ if (aValue == NULL) {
+ // NULL value in primary key
+ setErrorCodeAbort(4505);
+ DBUG_RETURN(-1);
+ }//if
+
+ if ( tAttrInfo == NULL ) {
+ // Attribute name not found in table
+ setErrorCodeAbort(4004);
+ DBUG_RETURN(-1);
+ }//if
+
+ if (theStatus == GetValue || theStatus == SetValue){
+ // All pk's defined
+ setErrorCodeAbort(4225);
+ DBUG_RETURN(-1);
+ }//if
+
+ ndbout_c("theStatus: %d", theStatus);
+
+ // If we come here, set a general errorcode
+ // and exit
+ setErrorCodeAbort(4200);
+ DBUG_RETURN(-1);
+
+ equal_error1:
+ setErrorCodeAbort(4205);
+ DBUG_RETURN(-1);
+
+ equal_error2:
+ setErrorCodeAbort(4206);
+ DBUG_RETURN(-1);
+
+ equal_error3:
+ setErrorCodeAbort(4209);
+ DBUG_RETURN(-1);
+}
+
+/******************************************************************************
+ * int insertKEYINFO(const char* aValue, aStartPosition,
+ * anAttrSizeInWords, Uint32 anAttrBitsInLastWord);
+ *
+ * Return Value: Return 0 : insertKEYINFO was succesful.
+ * Return -1: In all other case.
+ * Parameters: aValue: the data to insert into KEYINFO.
+ * aStartPosition : Start position for Tuplekey in
+ * KEYINFO (TCKEYREQ).
+ * aKeyLenInByte : Length of tuplekey or part of tuplekey
+ * anAttrBitsInLastWord : Nr of bits in last word.
+ * Remark: Puts the the data into either TCKEYREQ signal
+ * or KEYINFO signal.
+ *****************************************************************************/
+int
+NdbOperation::insertKEYINFO(const char* aValue,
+ register Uint32 aStartPosition,
+ register Uint32 anAttrSizeInWords)
+{
+ NdbApiSignal* tSignal;
+ NdbApiSignal* tCurrentKEYINFO;
+ //register NdbApiSignal* tTCREQ = theTCREQ;
+ register Uint32 tAttrPos;
+ Uint32 tPosition;
+ Uint32 tEndPos;
+ Uint32 tPos;
+ Uint32 signalCounter;
+ Uint32 tData;
+
+/*****************************************************************************
+ * Calculate the end position of the attribute in the key information. *
+ * Since the first attribute starts at position one we need to subtract *
+ * one to get the correct end position. *
+ * We must also remember the last word with only partial information. *
+ *****************************************************************************/
+ tEndPos = aStartPosition + anAttrSizeInWords - 1;
+
+ if ((tEndPos < 9)) {
+ register Uint32 tkeyData = *(Uint32*)aValue;
+ //TcKeyReq* tcKeyReq = CAST_PTR(TcKeyReq, tTCREQ->getDataPtrSend());
+ register Uint32* tDataPtr = (Uint32*)aValue;
+ tAttrPos = 1;
+ register Uint32* tkeyDataPtr = theKEYINFOptr + aStartPosition - 1;
+ // (Uint32*)&tcKeyReq->keyInfo[aStartPosition - 1];
+ do {
+ tDataPtr++;
+ *tkeyDataPtr = tkeyData;
+ if (tAttrPos < anAttrSizeInWords) {
+ ;
+ } else {
+ return 0;
+ }//if
+ tkeyData = *tDataPtr;
+ tkeyDataPtr++;
+ tAttrPos++;
+ } while (1);
+ return 0;
+ }//if
+/*****************************************************************************
+ * Allocate all the KEYINFO signals needed for this key before starting *
+ * to fill the signals with data. This simplifies error handling and *
+ * avoids duplication of code. *
+ *****************************************************************************/
+ tAttrPos = 0;
+ signalCounter = 1;
+ while(tEndPos > theTotalNrOfKeyWordInSignal)
+ {
+ tSignal = theNdb->getSignal();
+ if (tSignal == NULL)
+ {
+ setErrorCodeAbort(4000);
+ return -1;
+ }
+ if (tSignal->setSignal(m_keyInfoGSN) == -1)
+ {
+ setErrorCodeAbort(4001);
+ return -1;
+ }
+ if (theTCREQ->next() != NULL)
+ theLastKEYINFO->next(tSignal);
+ else
+ theTCREQ->next(tSignal);
+
+ theLastKEYINFO = tSignal;
+ theLastKEYINFO->next(NULL);
+ theTotalNrOfKeyWordInSignal += 20;
+ }
+
+/*****************************************************************************
+ * Change to variable tPosition for more appropriate naming of rest of *
+ * the code. We must set up current KEYINFO already here if the last *
+ * word is a word which is set at LastWordLabel and at the same time *
+ * this is the first word in a KEYINFO signal. *
+ *****************************************************************************/
+ tPosition = aStartPosition;
+ tCurrentKEYINFO = theTCREQ->next();
+
+/*****************************************************************************
+ * Start by filling up Key information in the 8 words allocated in the *
+ * TC[KEY/INDX]REQ signal. *
+ *****************************************************************************/
+ while (tPosition < 9)
+ {
+ theKEYINFOptr[tPosition-1] = * (Uint32*)(aValue + (tAttrPos << 2));
+ tAttrPos++;
+ if (anAttrSizeInWords == tAttrPos)
+ goto LastWordLabel;
+ tPosition++;
+ }
+
+/*****************************************************************************
+ * We must set up the start position of the writing of Key information *
+ * before we start the writing of KEYINFO signals. If the start is not *
+ * the first word of the first KEYINFO signals then we must step forward*
+ * to the proper KEYINFO signal and set the signalCounter properly. *
+ * signalCounter is set to the actual position in the signal ( = 4 for *
+ * first key word in KEYINFO signal. *
+ *****************************************************************************/
+ tPos = 8;
+ while ((tPosition - tPos) > 20)
+ {
+ tCurrentKEYINFO = tCurrentKEYINFO->next();
+ tPos += 20;
+ }
+ signalCounter = tPosition - tPos + 3;
+
+/*****************************************************************************
+ * The loop that actually fills in the Key information into the KEYINFO *
+ * signals. Can be optimised by writing larger chunks than 4 bytes at a *
+ * time. *
+ *****************************************************************************/
+ do
+ {
+ if (signalCounter > 23)
+ {
+ tCurrentKEYINFO = tCurrentKEYINFO->next();
+ signalCounter = 4;
+ }
+ tCurrentKEYINFO->setData(*(Uint32*)(aValue + (tAttrPos << 2)),
+ signalCounter);
+ tAttrPos++;
+ if (anAttrSizeInWords == tAttrPos)
+ goto LastWordLabel;
+ tPosition++;
+ signalCounter++;
+ } while (1);
+
+LastWordLabel:
+ return 0;
+}
+
+int
+NdbOperation::getKeyFromTCREQ(Uint32* data, unsigned size)
+{
+ assert(m_accessTable != 0 && m_accessTable->m_keyLenInWords != 0);
+ assert(m_accessTable->m_keyLenInWords == size);
+ unsigned pos = 0;
+ while (pos < 8 && pos < size) {
+ data[pos] = theKEYINFOptr[pos];
+ pos++;
+ }
+ NdbApiSignal* tSignal = theTCREQ->next();
+ unsigned n = 0;
+ while (pos < size) {
+ if (n == 20) {
+ tSignal = tSignal->next();
+ n = 0;
+ }
+ data[pos++] = tSignal->getDataPtrSend()[3 + n++];
+ }
+ return 0;
+}
+
+int
+NdbOperation::handle_distribution_key(const Uint64* value, Uint32 len)
+{
+ if(theDistrKeyIndicator_ == 1 ||
+ (theNoOfTupKeyLeft > 0 && m_accessTable->m_noOfDistributionKeys > 1))
+ {
+ return 0;
+ }
+
+ if(m_accessTable->m_noOfDistributionKeys == 1)
+ {
+ setPartitionHash(value, len);
+ }
+ else if(theTCREQ->readSignalNumber() == GSN_TCKEYREQ)
+ {
+ // No support for combined distribution key and scan
+
+ /**
+ * Copy distribution key to linear memory
+ */
+ NdbColumnImpl* const * cols = m_accessTable->m_columns.getBase();
+ Uint32 len = 0;
+ Uint64 tmp[1000];
+
+ Uint32 chunk = 8;
+ Uint32* dst = (Uint32*)tmp;
+ NdbApiSignal* tSignal = theTCREQ;
+ Uint32* src = ((TcKeyReq*)tSignal->getDataPtrSend())->keyInfo;
+ if(tSignal->readSignalNumber() == GSN_SCAN_TABREQ)
+ {
+ tSignal = tSignal->next();
+ src = ((KeyInfo*)tSignal->getDataPtrSend())->keyData;
+ chunk = KeyInfo::DataLength;
+ }
+
+ for(unsigned i = m_accessTable->m_columns.size(); i>0; cols++, i--)
+ {
+ if (!(* cols)->getPrimaryKey())
+ continue;
+
+ NdbColumnImpl* tAttrInfo = * cols;
+ Uint32 sizeInBytes = tAttrInfo->m_attrSize * tAttrInfo->m_arraySize;
+ Uint32 currLen = (sizeInBytes + 3) >> 2;
+ if (tAttrInfo->getDistributionKey())
+ {
+ while (currLen >= chunk)
+ {
+ memcpy(dst, src, 4*chunk);
+ dst += chunk;
+ tSignal = tSignal->next();
+ src = ((KeyInfo*)tSignal->getDataPtrSend())->keyData;
+ currLen -= chunk;
+ chunk = KeyInfo::DataLength;
+ }
+
+ memcpy(dst, src, 4*currLen);
+ dst += currLen;
+ src += currLen;
+ chunk -= currLen;
+ }
+ else
+ {
+ while (currLen >= chunk)
+ {
+ tSignal = tSignal->next();
+ src = ((KeyInfo*)tSignal->getDataPtrSend())->keyData;
+ currLen -= chunk;
+ chunk = KeyInfo::DataLength;
+ }
+
+ src += currLen;
+ chunk -= currLen;
+ }
+ }
+ setPartitionHash(tmp, dst - (Uint32*)tmp);
+ }
+ return 0;
+}
+
+void
+NdbOperation::setPartitionHash(Uint32 value)
+{
+ union {
+ Uint32 tmp32;
+ Uint64 tmp64;
+ };
+
+ tmp32 = value;
+ setPartitionHash(&tmp64, 1);
+}
+
+void
+NdbOperation::setPartitionHash(const Uint64* value, Uint32 len)
+{
+ Uint32 buf[4];
+ md5_hash(buf, value, len);
+ setPartitionId(buf[1]);
+}
+
+void
+NdbOperation::setPartitionId(Uint32 value)
+{
+ theDistributionKey = value;
+ theDistrKeyIndicator_ = 1;
+}
+
+Uint32
+NdbOperation::getPartitionId() const
+{
+ return theDistributionKey;
+}
diff --git a/storage/ndb/src/ndbapi/NdbPool.cpp b/storage/ndb/src/ndbapi/NdbPool.cpp
new file mode 100644
index 00000000000..a8263f564f1
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbPool.cpp
@@ -0,0 +1,71 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <Ndb.hpp>
+#include "NdbPoolImpl.hpp"
+#include <NdbPool.hpp>
+
+static NdbPool* m_pool = 0;
+
+bool
+create_instance(Ndb_cluster_connection* cc,
+ Uint32 max_ndb_objects,
+ Uint32 no_conn_obj,
+ Uint32 init_no_ndb_objects)
+{
+ if (m_pool != NULL) {
+ return false;
+ }
+ m_pool = NdbPool::create_instance(cc,
+ max_ndb_objects,
+ no_conn_obj,
+ init_no_ndb_objects);
+ if (m_pool == NULL) {
+ return false;
+ }
+ return true;
+}
+
+void
+drop_instance()
+{
+ if (m_pool == NULL) {
+ return;
+ }
+ NdbPool::drop_instance();
+ m_pool = NULL;
+}
+
+Ndb*
+get_ndb_object(Uint32 &hint_id,
+ const char* a_catalog_name,
+ const char* a_schema_name)
+{
+ if (m_pool == NULL) {
+ return NULL;
+ }
+ return m_pool->get_ndb_object(hint_id, a_catalog_name, a_schema_name);
+}
+
+void
+return_ndb_object(Ndb* returned_object, Uint32 id)
+{
+ if (m_pool == NULL) {
+ return;
+ }
+ m_pool->return_ndb_object(returned_object, id);
+}
+
diff --git a/storage/ndb/src/ndbapi/NdbPoolImpl.cpp b/storage/ndb/src/ndbapi/NdbPoolImpl.cpp
new file mode 100644
index 00000000000..32e0a6f1410
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbPoolImpl.cpp
@@ -0,0 +1,528 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "NdbPoolImpl.hpp"
+
+NdbMutex *NdbPool::pool_mutex = NULL;
+NdbPool *the_pool = NULL;
+
+NdbPool*
+NdbPool::create_instance(Ndb_cluster_connection* cc,
+ Uint32 max_ndb_obj,
+ Uint32 no_conn_obj,
+ Uint32 init_no_ndb_objects)
+{
+ if (!initPoolMutex()) {
+ return NULL;
+ }
+ NdbMutex_Lock(pool_mutex);
+ NdbPool* a_pool;
+ if (the_pool != NULL) {
+ a_pool = NULL;
+ } else {
+ the_pool = new NdbPool(cc, max_ndb_obj, no_conn_obj);
+ if (!the_pool->init(init_no_ndb_objects)) {
+ delete the_pool;
+ the_pool = NULL;
+ }
+ a_pool = the_pool;
+ }
+ NdbMutex* temp = pool_mutex;
+ if (a_pool == NULL) {
+ pool_mutex = NULL;
+ }
+ NdbMutex_Unlock(pool_mutex);
+ if (a_pool == NULL) {
+ NdbMutex_Destroy(temp);
+ }
+ return a_pool;
+}
+
+void
+NdbPool::drop_instance()
+{
+ if (pool_mutex == NULL) {
+ return;
+ }
+ NdbMutex_Lock(pool_mutex);
+ the_pool->release_all();
+ delete the_pool;
+ the_pool = NULL;
+ NdbMutex* temp = pool_mutex;
+ NdbMutex_Unlock(temp);
+ NdbMutex_Destroy(temp);
+}
+
+bool
+NdbPool::initPoolMutex()
+{
+ bool ret_result = false;
+ if (pool_mutex == NULL) {
+ pool_mutex = NdbMutex_Create();
+ ret_result = ((pool_mutex == NULL) ? false : true);
+ }
+ return ret_result;
+}
+
+NdbPool::NdbPool(Ndb_cluster_connection* cc,
+ Uint32 max_no_objects,
+ Uint32 no_conn_objects)
+{
+ if (no_conn_objects > 1024) {
+ no_conn_objects = 1024;
+ }
+ if (max_no_objects > MAX_NDB_OBJECTS) {
+ max_no_objects = MAX_NDB_OBJECTS;
+ } else if (max_no_objects == 0) {
+ max_no_objects = 1;
+ }
+ m_max_ndb_objects = max_no_objects;
+ m_no_of_conn_objects = no_conn_objects;
+ m_no_of_objects = 0;
+ m_waiting = 0;
+ m_pool_reference = NULL;
+ m_hash_entry = NULL;
+ m_first_free = NULL_POOL;
+ m_first_not_in_use = NULL_POOL;
+ m_last_free = NULL_POOL;
+ input_pool_cond = NULL;
+ output_pool_cond = NULL;
+ m_output_queue = 0;
+ m_input_queue = 0;
+ m_signal_count = 0;
+ m_cluster_connection = cc;
+}
+
+NdbPool::~NdbPool()
+{
+ NdbCondition_Destroy(input_pool_cond);
+ NdbCondition_Destroy(output_pool_cond);
+}
+
+void
+NdbPool::release_all()
+{
+ int i;
+ for (i = 0; i < m_max_ndb_objects + 1; i++) {
+ if (m_pool_reference[i].ndb_reference != NULL) {
+ assert(m_pool_reference[i].in_use);
+ assert(m_pool_reference[i].free_entry);
+ delete m_pool_reference[i].ndb_reference;
+ }
+ }
+ delete [] m_pool_reference;
+ delete [] m_hash_entry;
+ m_pool_reference = NULL;
+ m_hash_entry = NULL;
+}
+
+bool
+NdbPool::init(Uint32 init_no_objects)
+{
+ bool ret_result = false;
+ int i;
+ do {
+ input_pool_cond = NdbCondition_Create();
+ output_pool_cond = NdbCondition_Create();
+ if (input_pool_cond == NULL || output_pool_cond == NULL) {
+ break;
+ }
+ if (init_no_objects > m_max_ndb_objects) {
+ init_no_objects = m_max_ndb_objects;
+ }
+ if (init_no_objects == 0) {
+ init_no_objects = 1;
+ }
+ m_pool_reference = new NdbPool::POOL_STRUCT[m_max_ndb_objects + 1];
+ m_hash_entry = new Uint8[POOL_HASH_TABLE_SIZE];
+
+ if ((m_pool_reference == NULL) || (m_hash_entry == NULL)) {
+ delete [] m_pool_reference;
+ delete [] m_hash_entry;
+ break;
+ }
+ for (i = 0; i < m_max_ndb_objects + 1; i++) {
+ m_pool_reference[i].ndb_reference = NULL;
+ m_pool_reference[i].in_use = false;
+ m_pool_reference[i].next_free_object = i+1;
+ m_pool_reference[i].prev_free_object = i-1;
+ m_pool_reference[i].next_db_object = NULL_POOL;
+ m_pool_reference[i].prev_db_object = NULL_POOL;
+ }
+ for (i = 0; i < POOL_HASH_TABLE_SIZE; i++) {
+ m_hash_entry[i] = NULL_HASH;
+ }
+ m_pool_reference[m_max_ndb_objects].next_free_object = NULL_POOL;
+ m_pool_reference[1].prev_free_object = NULL_POOL;
+ m_first_not_in_use = 1;
+ m_no_of_objects = init_no_objects;
+ for (i = init_no_objects; i > 0 ; i--) {
+ Uint32 fake_id;
+ if (!allocate_ndb(fake_id, (const char*)NULL, (const char*)NULL)) {
+ release_all();
+ break;
+ }
+ }
+ ret_result = true;
+ break;
+ } while (1);
+ return ret_result;
+}
+
+/*
+Get an Ndb object.
+Input:
+hint_id: 0 = no hint, otherwise a hint of which Ndb object the thread
+ used the last time.
+a_db_name: NULL = don't check for database specific Ndb object, otherwise
+ a hint of which database is preferred.
+Output:
+hint_id: Returns id of Ndb object returned
+Return value: Ndb object pointer
+*/
+Ndb*
+NdbPool::get_ndb_object(Uint32 &hint_id,
+ const char* a_catalog_name,
+ const char* a_schema_name)
+{
+ Ndb* ret_ndb = NULL;
+ Uint32 hash_entry = compute_hash(a_schema_name);
+ NdbMutex_Lock(pool_mutex);
+ while (1) {
+ /*
+ We start by checking if we can use the hinted Ndb object.
+ */
+ if ((ret_ndb = get_hint_ndb(hint_id, hash_entry)) != NULL) {
+ break;
+ }
+ /*
+ The hinted Ndb object was not free. We need to allocate another object.
+ We start by checking for a free Ndb object connected to the same database.
+ */
+ if (a_schema_name && (ret_ndb = get_db_hash(hint_id,
+ hash_entry,
+ a_catalog_name,
+ a_schema_name))) {
+ break;
+ }
+ /*
+ No Ndb object connected to the preferred database was found.
+ We look for a free Ndb object in general.
+ */
+ if ((ret_ndb = get_free_list(hint_id, hash_entry)) != NULL) {
+ break;
+ }
+ /*
+ No free Ndb object was found. If we haven't allocated objects up until the
+ maximum number yet then we can allocate a new Ndb object here.
+ */
+ if (m_no_of_objects < m_max_ndb_objects) {
+ if (allocate_ndb(hint_id, a_catalog_name, a_schema_name)) {
+ assert((ret_ndb = get_hint_ndb(hint_id, hash_entry)) != NULL);
+ break;
+ }
+ }
+ /*
+ We need to wait until an Ndb object becomes
+ available.
+ */
+ if ((ret_ndb = wait_free_ndb(hint_id)) != NULL) {
+ break;
+ }
+ /*
+ Not even after waiting were we able to get hold of an Ndb object. We
+ return NULL to indicate this problem.
+ */
+ ret_ndb = NULL;
+ break;
+ }
+ NdbMutex_Unlock(pool_mutex);
+ if (ret_ndb != NULL) {
+ /*
+ We need to set the catalog and schema name of the Ndb object before
+ returning it to the caller.
+ */
+ ret_ndb->setCatalogName(a_catalog_name);
+ ret_ndb->setSchemaName(a_schema_name);
+ }
+ return ret_ndb;
+}
+
+void
+NdbPool::return_ndb_object(Ndb* returned_ndb, Uint32 id)
+{
+ NdbMutex_Lock(pool_mutex);
+ assert(id <= m_max_ndb_objects);
+ assert(id != 0);
+ assert(returned_ndb == m_pool_reference[id].ndb_reference);
+ bool wait_cond = m_waiting;
+ if (wait_cond) {
+ NdbCondition* pool_cond;
+ if (m_signal_count > 0) {
+ pool_cond = output_pool_cond;
+ m_signal_count--;
+ } else {
+ pool_cond = input_pool_cond;
+ }
+ add_wait_list(id);
+ NdbMutex_Unlock(pool_mutex);
+ NdbCondition_Signal(pool_cond);
+ } else {
+ add_free_list(id);
+ add_db_hash(id);
+ NdbMutex_Unlock(pool_mutex);
+ }
+}
+
+bool
+NdbPool::allocate_ndb(Uint32 &id,
+ const char* a_catalog_name,
+ const char* a_schema_name)
+{
+ Ndb* a_ndb;
+ if (m_first_not_in_use == NULL_POOL) {
+ return false;
+ }
+ if (a_schema_name) {
+ a_ndb = new Ndb(m_cluster_connection, a_schema_name, a_catalog_name);
+ } else {
+ a_ndb = new Ndb(m_cluster_connection, "");
+ }
+ if (a_ndb == NULL) {
+ return false;
+ }
+ a_ndb->init(m_no_of_conn_objects);
+ m_no_of_objects++;
+
+ id = m_first_not_in_use;
+ Uint32 allocated_id = m_first_not_in_use;
+ m_first_not_in_use = m_pool_reference[allocated_id].next_free_object;
+
+ m_pool_reference[allocated_id].ndb_reference = a_ndb;
+ m_pool_reference[allocated_id].in_use = true;
+ m_pool_reference[allocated_id].free_entry = false;
+
+ add_free_list(allocated_id);
+ add_db_hash(allocated_id);
+ return true;
+}
+
+void
+NdbPool::add_free_list(Uint32 id)
+{
+ assert(!m_pool_reference[id].free_entry);
+ assert(m_pool_reference[id].in_use);
+ m_pool_reference[id].free_entry = true;
+ m_pool_reference[id].next_free_object = m_first_free;
+ m_pool_reference[id].prev_free_object = (Uint8)NULL_POOL;
+ m_first_free = (Uint8)id;
+ if (m_last_free == (Uint8)NULL_POOL) {
+ m_last_free = (Uint8)id;
+ }
+}
+
+void
+NdbPool::add_db_hash(Uint32 id)
+{
+ Ndb* t_ndb = m_pool_reference[id].ndb_reference;
+ const char* schema_name = t_ndb->getSchemaName();
+ Uint32 hash_entry = compute_hash(schema_name);
+ Uint8 next_db_entry = m_hash_entry[hash_entry];
+ m_pool_reference[id].next_db_object = next_db_entry;
+ m_pool_reference[id].prev_db_object = (Uint8)NULL_HASH;
+ m_hash_entry[hash_entry] = (Uint8)id;
+}
+
+Ndb*
+NdbPool::get_free_list(Uint32 &id, Uint32 hash_entry)
+{
+ if (m_first_free == NULL_POOL) {
+ return NULL;
+ }
+ id = m_first_free;
+ Ndb* ret_ndb = get_hint_ndb(m_first_free, hash_entry);
+ assert(ret_ndb != NULL);
+ return ret_ndb;
+}
+
+Ndb*
+NdbPool::get_db_hash(Uint32 &id,
+ Uint32 hash_entry,
+ const char *a_catalog_name,
+ const char *a_schema_name)
+{
+ Uint32 entry_id = m_hash_entry[hash_entry];
+ bool found = false;
+ while (entry_id != NULL_HASH) {
+ Ndb* t_ndb = m_pool_reference[entry_id].ndb_reference;
+ const char *a_ndb_catalog_name = t_ndb->getCatalogName();
+ if (strcmp(a_catalog_name, a_ndb_catalog_name) == 0) {
+ const char *a_ndb_schema_name = t_ndb->getSchemaName();
+ if (strcmp(a_schema_name, a_ndb_schema_name) == 0) {
+ found = true;
+ break;
+ }
+ }
+ entry_id = m_pool_reference[entry_id].next_db_object;
+ }
+ if (found) {
+ id = entry_id;
+ Ndb* ret_ndb = get_hint_ndb(entry_id, hash_entry);
+ assert(ret_ndb != NULL);
+ return ret_ndb;
+ }
+ return NULL;
+}
+
+Ndb*
+NdbPool::get_hint_ndb(Uint32 hint_id, Uint32 hash_entry)
+{
+ Ndb* ret_ndb = NULL;
+ do {
+ if ((hint_id != 0) &&
+ (hint_id <= m_max_ndb_objects) &&
+ (m_pool_reference[hint_id].in_use) &&
+ (m_pool_reference[hint_id].free_entry)) {
+ ret_ndb = m_pool_reference[hint_id].ndb_reference;
+ if (ret_ndb != NULL) {
+ break;
+ } else {
+ assert(false);
+ }
+ }
+ return NULL;
+ } while (1);
+ /*
+ This is where we remove the entry from the free list and from the db hash
+ table.
+ */
+ remove_free_list(hint_id);
+ remove_db_hash(hint_id, hash_entry);
+ return ret_ndb;
+}
+
+void
+NdbPool::remove_free_list(Uint32 id)
+{
+ Uint8 next_free_entry = m_pool_reference[id].next_free_object;
+ Uint8 prev_free_entry = m_pool_reference[id].prev_free_object;
+ if (prev_free_entry == (Uint8)NULL_POOL) {
+ m_first_free = next_free_entry;
+ } else {
+ m_pool_reference[prev_free_entry].next_free_object = next_free_entry;
+ }
+ if (next_free_entry == (Uint8)NULL_POOL) {
+ m_last_free = prev_free_entry;
+ } else {
+ m_pool_reference[next_free_entry].prev_free_object = prev_free_entry;
+ }
+ m_pool_reference[id].next_free_object = NULL_POOL;
+ m_pool_reference[id].prev_free_object = NULL_POOL;
+ m_pool_reference[id].free_entry = false;
+}
+
+void
+NdbPool::remove_db_hash(Uint32 id, Uint32 hash_entry)
+{
+ Uint8 next_free_entry = m_pool_reference[id].next_db_object;
+ Uint8 prev_free_entry = m_pool_reference[id].prev_db_object;
+ if (prev_free_entry == (Uint8)NULL_HASH) {
+ m_hash_entry[hash_entry] = next_free_entry;
+ } else {
+ m_pool_reference[prev_free_entry].next_db_object = next_free_entry;
+ }
+ if (next_free_entry == (Uint8)NULL_HASH) {
+ ;
+ } else {
+ m_pool_reference[next_free_entry].prev_db_object = prev_free_entry;
+ }
+ m_pool_reference[id].next_db_object = NULL_HASH;
+ m_pool_reference[id].prev_db_object = NULL_HASH;
+}
+
+Uint32
+NdbPool::compute_hash(const char *a_schema_name)
+{
+ Uint32 len = strlen(a_schema_name);
+ Uint32 h = 147;
+ for (Uint32 i = 0; i < len; i++) {
+ Uint32 c = a_schema_name[i];
+ h = (h << 5) + h + c;
+ }
+ h &= (POOL_HASH_TABLE_SIZE - 1);
+ return h;
+}
+
+Ndb*
+NdbPool::wait_free_ndb(Uint32 &id)
+{
+ int res;
+ int time_out = 3500;
+ do {
+ NdbCondition* tmp = input_pool_cond;
+ m_waiting++;
+ m_input_queue++;
+ time_out -= 500;
+ res = NdbCondition_WaitTimeout(input_pool_cond, pool_mutex, time_out);
+ if (tmp == input_pool_cond) {
+ m_input_queue--;
+ } else {
+ m_output_queue--;
+ if (m_output_queue == 0) {
+ switch_condition_queue();
+ }
+ }
+ m_waiting--;
+ } while (res == 0 && m_first_wait == NULL_POOL);
+ if (res != 0 && m_first_wait == NULL_POOL) {
+ return NULL;
+ }
+ id = m_first_wait;
+ remove_wait_list();
+ assert(m_waiting != 0 || m_first_wait == NULL_POOL);
+ return m_pool_reference[id].ndb_reference;
+}
+
+void
+NdbPool::remove_wait_list()
+{
+ Uint32 id = m_first_wait;
+ m_first_wait = m_pool_reference[id].next_free_object;
+ m_pool_reference[id].next_free_object = NULL_POOL;
+ m_pool_reference[id].prev_free_object = NULL_POOL;
+ m_pool_reference[id].free_entry = false;
+}
+
+void
+NdbPool::add_wait_list(Uint32 id)
+{
+ m_pool_reference[id].next_free_object = m_first_wait;
+ m_first_wait = id;
+}
+
+void
+NdbPool::switch_condition_queue()
+{
+ m_signal_count = m_input_queue;
+ Uint8 move_queue = m_input_queue;
+ m_input_queue = m_output_queue;
+ m_output_queue = move_queue;
+
+ NdbCondition* move_cond = input_pool_cond;
+ input_pool_cond = output_pool_cond;
+ output_pool_cond = move_cond;
+}
+
diff --git a/storage/ndb/src/ndbapi/NdbPoolImpl.hpp b/storage/ndb/src/ndbapi/NdbPoolImpl.hpp
new file mode 100644
index 00000000000..cd36f30e90b
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbPoolImpl.hpp
@@ -0,0 +1,166 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/**
+ @section ndbPool Pooling of NDB objects
+ This class implements pooling of NDB objects to support MySQL, ODBC and
+ any application with a great number of threads.
+
+ The general idea is to let the NdbPool class administer all Ndb objects.
+ When a thread needs a Ndb object it request a Ndb object from the Pool.
+ This interface contains some hints to ensure that the proper Ndb object
+ is returned.
+
+ The object contains an array of references to all Ndb objects together with
+ an indication of whether the object is free or not.
+
+ The idea is that the thread should keep track of the Ndb object it used the
+ last time. If this object is still free it will simply get this object
+ back. If the number of threads do not exceed the number of Ndb objects this
+ will always be successful. In certain situations the number of threads will
+ be much greater than the number of Ndb objects. In this situation the Pool
+ will attempt to provide an object that is attached to the same schema
+ as the thread is connected to. If this is not possible it will attempt to
+ get any free Ndb object. If not even this is possible the Pool will wait
+ until an Ndb object becomes free. If an Ndb object becomes available in
+ time it will deliver this Ndb object. In the worst case the call will
+ time-out and return NULL to indicate no free Ndb object was found in time.
+
+ The implementation uses an array of structs which contain a reference to a
+ Ndb object, whether it is in use currently and a number of references to
+ set up linked lists of
+ 1) Free objects on a schema
+ 2) Free objects in LIFO order
+
+ Usage:
+ The class is a singleton.
+ The first step is to call create_instance(..). If successful this will
+ create the NdbPool object and return a reference to it. When completed
+ drop_instance is called to remove the NdbPool object and all memory and
+ other resources attached to it.
+
+ After initialising the NdbPool object all threads can now start using the
+ NdbPool. There are two methods in normal usage mode. The first
+ get_ndb_object gets a Ndb object and the second return_ndb_object returns
+ an Ndb object. The user of the NdbPool must keep track of the identity
+ of the Ndb object. The idea is that this identity can also be used to
+ find the object quickly again unless another thread have taken it. If the
+ user wants any Ndb object it requests identity 0 which means any here.
+
+ When constructing the NdbPool one can set the number of NdbConnection
+ objects which are allowed in all Ndb objects. For use in synchronous
+ applications such as the MySQL server 4 objects should be enough. When
+ using the NdbPool for asynchronous applications one should use 1024 to
+ enable a high level of parallelism. It is also possible to set the
+ maximum number of Ndb objects in the pool and the initial number of
+ Ndb objects allocated.
+*/
+
+#ifndef NdbPool_H
+#define NdbPool_H
+
+#include <Ndb.hpp>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <NdbOut.hpp>
+
+class NdbPool {
+#define NULL_POOL 0
+#define NULL_HASH 0xFF
+#define POOL_HASH_TABLE_SIZE 32
+#define MAX_NDB_OBJECTS 240
+ struct POOL_STRUCT {
+ Ndb* ndb_reference;
+ bool in_use;
+ bool free_entry;
+ Uint16 next_free_object;
+ Uint16 prev_free_object;
+ Uint16 next_db_object;
+ Uint16 prev_db_object;
+ };
+ public:
+ static NdbPool* create_instance(Ndb_cluster_connection*,
+ Uint32 max_ndb_objects = 240,
+ Uint32 no_conn_obj = 4,
+ Uint32 init_no_ndb_objects = 8);
+ static void drop_instance();
+ Ndb* get_ndb_object(Uint32 &hint_id,
+ const char* a_catalog_name,
+ const char* a_schema_name);
+ void return_ndb_object(Ndb* returned_object, Uint32 id);
+ private:
+ bool init(Uint32 initial_no_of_ndb_objects = 8);
+ void release_all();
+ static bool initPoolMutex();
+ NdbPool(Ndb_cluster_connection*,
+ Uint32 max_no_of_ndb_objects, Uint32 no_conn_objects);
+ ~NdbPool();
+ /*
+ We have three lists:
+ 1) A list for entries not in use
+ 2) A list for free entries
+ 3) A hash table with schema name and database name as key
+
+ These lists are all initialised in the init call.
+ The list for entries not in use is very simple since the current
+ implementation have not yet any handling of dropping Ndb objects
+ until all Ndb objects are dropped.
+ */
+ void add_free_list(Uint32 id);
+ void remove_free_list(Uint32 id);
+ Ndb* get_free_list(Uint32 &id, Uint32 hash_entry);
+
+ void add_db_hash(Uint32 id);
+ void remove_db_hash(Uint32 id, Uint32 hash_entry);
+ Ndb* get_db_hash(Uint32 &id,
+ Uint32 hash_entry,
+ const char* a_catalog_name,
+ const char* a_schema_name);
+
+ bool allocate_ndb(Uint32 &id,
+ const char* a_catalog_name,
+ const char* a_schema_name);
+ Ndb* get_hint_ndb(Uint32 id, Uint32 hash_entry);
+ Ndb* wait_free_ndb(Uint32 &id);
+ Uint32 compute_hash(const char *a_schema_name);
+ void add_wait_list(Uint32 id);
+ void remove_wait_list();
+ void switch_condition_queue();
+
+ static NdbMutex *pool_mutex;
+ struct NdbCondition *input_pool_cond;
+ struct NdbCondition *output_pool_cond;
+
+ POOL_STRUCT *m_pool_reference;
+ Uint8 *m_hash_entry;
+
+ bool m_inited;
+ Uint32 m_no_of_conn_objects;
+
+ Uint16 m_no_of_objects;
+ Uint16 m_max_ndb_objects;
+ Uint16 m_first_free;
+ Uint16 m_last_free;
+ Uint16 m_first_not_in_use;
+ Uint16 m_waiting;
+ Uint16 m_first_wait;
+ Uint16 m_input_queue;
+ Uint16 m_output_queue;
+ Uint16 m_signal_count;
+
+ Ndb_cluster_connection * m_cluster_connection;
+};
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbRecAttr.cpp b/storage/ndb/src/ndbapi/NdbRecAttr.cpp
new file mode 100644
index 00000000000..5e5306fc33a
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbRecAttr.cpp
@@ -0,0 +1,415 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include <NdbRecAttr.hpp>
+#include <NdbBlob.hpp>
+#include "NdbDictionaryImpl.hpp"
+#include <NdbTCP.h>
+
+NdbRecAttr::NdbRecAttr()
+{
+ init();
+}
+
+NdbRecAttr::~NdbRecAttr()
+{
+ release();
+}
+
+int
+NdbRecAttr::setup(const class NdbDictionary::Column* col, char* aValue)
+{
+ return setup(&(col->m_impl), aValue);
+}
+int
+NdbRecAttr::setup(const NdbColumnImpl* anAttrInfo, char* aValue)
+{
+ Uint32 tAttrSize = anAttrInfo->m_attrSize;
+ Uint32 tArraySize = anAttrInfo->m_arraySize;
+ Uint32 tAttrByteSize = tAttrSize * tArraySize;
+
+ m_column = anAttrInfo;
+
+ theAttrId = anAttrInfo->m_attrId;
+ theAttrSize = tAttrSize;
+ theArraySize = tArraySize;
+ theValue = aValue;
+ theNULLind = 0;
+ m_nullable = anAttrInfo->m_nullable;
+
+ // check alignment to signal data
+ // a future version could check alignment per data type as well
+
+ if (aValue != NULL && (UintPtr(aValue)&3) == 0 && (tAttrByteSize&3) == 0) {
+ theStorageX = NULL;
+ theRef = aValue;
+ return 0;
+ }
+ if (tAttrByteSize <= 32) {
+ theStorageX = NULL;
+ theStorage[0] = 0;
+ theStorage[1] = 0;
+ theStorage[2] = 0;
+ theStorage[3] = 0;
+ theRef = theStorage;
+ return 0;
+ }
+ Uint32 tSize = (tAttrByteSize + 7) >> 3;
+ Uint64* tRef = new Uint64[tSize];
+ if (tRef != NULL) {
+ for (Uint32 i = 0; i < tSize; i++) {
+ tRef[i] = 0;
+ }
+ theStorageX = tRef;
+ theRef = tRef;
+ return 0;
+ }
+ return -1;
+}
+
+void
+NdbRecAttr::copyout()
+{
+ char* tRef = (char*)theRef;
+ char* tValue = theValue;
+ if (tRef != tValue && tRef != NULL && tValue != NULL) {
+ Uint32 n = theAttrSize * theArraySize;
+ while (n-- > 0) {
+ *tValue++ = *tRef++;
+ }
+ }
+}
+
+NdbRecAttr *
+NdbRecAttr::clone() const {
+ NdbRecAttr * ret = new NdbRecAttr();
+
+ ret->theAttrId = theAttrId;
+ ret->theNULLind = theNULLind;
+ ret->theAttrSize = theAttrSize;
+ ret->theArraySize = theArraySize;
+ ret->m_column = m_column;
+
+ Uint32 n = theAttrSize * theArraySize;
+ if(n <= 32){
+ ret->theRef = (char*)&ret->theStorage[0];
+ ret->theStorageX = 0;
+ ret->theValue = 0;
+ } else {
+ ret->theStorageX = new Uint64[((n + 7) >> 3)];
+ ret->theRef = (char*)ret->theStorageX;
+ ret->theValue = 0;
+ }
+ memcpy(ret->theRef, theRef, n);
+ return ret;
+}
+
+bool
+NdbRecAttr::receive_data(const Uint32 * data, Uint32 sz){
+ const Uint32 n = (theAttrSize * theArraySize + 3) >> 2;
+ if(n == sz){
+ theNULLind = 0;
+ if(!copyoutRequired())
+ memcpy(theRef, data, 4 * sz);
+ else
+ memcpy(theValue, data, theAttrSize * theArraySize);
+ return true;
+ } else if(sz == 0){
+ setNULL();
+ return true;
+ }
+ return false;
+}
+
+static void
+ndbrecattr_print_string(NdbOut& out, const char *type,
+ const char *aref, unsigned sz)
+{
+ const unsigned char* ref = (const unsigned char*)aref;
+ int i, len, printable= 1;
+ // trailing zeroes are not printed
+ for (i=sz-1; i >= 0; i--)
+ if (ref[i] == 0) sz--;
+ else break;
+ if (sz == 0) return; // empty
+
+ for (len=0; len < (int)sz && ref[i] != 0; len++)
+ if (printable && !isprint((int)ref[i]))
+ printable= 0;
+
+ if (printable)
+ out.print("%.*s", len, ref);
+ else
+ {
+ out.print("0x");
+ for (i=0; i < len; i++)
+ out.print("%02X", (int)ref[i]);
+ }
+ if (len != (int)sz)
+ {
+ out.print("[");
+ for (i= len+1; ref[i] != 0; i++)
+ out.print("%u]",len-i);
+ assert((int)sz > i);
+ ndbrecattr_print_string(out,type,aref+i,sz-i);
+ }
+}
+
+NdbOut& operator<<(NdbOut& out, const NdbRecAttr &r)
+{
+ if (r.isNULL())
+ {
+ out << "[NULL]";
+ return out;
+ }
+
+ const NdbDictionary::Column* c = r.getColumn();
+ uint length = c->getLength();
+ if (length > 1)
+ out << "[";
+
+ for (Uint32 j = 0; j < length; j++)
+ {
+ if (j > 0)
+ out << " ";
+
+ switch(r.getType())
+ {
+ case NdbDictionary::Column::Bigunsigned:
+ out << r.u_64_value();
+ break;
+ case NdbDictionary::Column::Bit:
+ out << hex << "H'" << r.u_32_value() << dec;
+ break;
+ case NdbDictionary::Column::Unsigned:
+ out << r.u_32_value();
+ break;
+ case NdbDictionary::Column::Smallunsigned:
+ out << r.u_short_value();
+ break;
+ case NdbDictionary::Column::Tinyunsigned:
+ out << (unsigned) r.u_char_value();
+ break;
+ case NdbDictionary::Column::Bigint:
+ out << r.int64_value();
+ break;
+ case NdbDictionary::Column::Int:
+ out << r.int32_value();
+ break;
+ case NdbDictionary::Column::Smallint:
+ out << r.short_value();
+ break;
+ case NdbDictionary::Column::Tinyint:
+ out << (int) r.char_value();
+ break;
+ case NdbDictionary::Column::Binary:
+ ndbrecattr_print_string(out,"Binary",r.aRef(),r.arraySize());
+ j = r.arraySize();
+ break;
+ case NdbDictionary::Column::Char:
+ ndbrecattr_print_string(out,"Char",r.aRef(),r.arraySize());
+ j = length;
+ break;
+ case NdbDictionary::Column::Varchar:
+ {
+ unsigned len = *(const unsigned char*)r.aRef();
+ ndbrecattr_print_string(out,"Varchar", r.aRef()+1,len);
+ j = length;
+ }
+ break;
+ case NdbDictionary::Column::Float:
+ out << r.float_value();
+ break;
+ case NdbDictionary::Column::Double:
+ out << r.double_value();
+ break;
+ case NdbDictionary::Column::Olddecimal:
+ {
+ short len = 1 + c->getPrecision() + (c->getScale() > 0);
+ out.print("%.*s", len, r.aRef());
+ }
+ break;
+ case NdbDictionary::Column::Olddecimalunsigned:
+ {
+ short len = 0 + c->getPrecision() + (c->getScale() > 0);
+ out.print("%.*s", len, r.aRef());
+ }
+ break;
+ case NdbDictionary::Column::Decimal:
+ case NdbDictionary::Column::Decimalunsigned:
+ goto unknown; // TODO
+ break;
+ // for dates cut-and-paste from field.cc
+ case NdbDictionary::Column::Datetime:
+ {
+ ulonglong tmp=r.u_64_value();
+ long part1,part2,part3;
+ part1=(long) (tmp/LL(1000000));
+ part2=(long) (tmp - (ulonglong) part1*LL(1000000));
+ char buf[40];
+ char* pos=(char*) buf+19;
+ *pos--=0;
+ *pos--= (char) ('0'+(char) (part2%10)); part2/=10;
+ *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
+ *pos--= ':';
+ *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
+ *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
+ *pos--= ':';
+ *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
+ *pos--= (char) ('0'+(char) part3);
+ *pos--= '/';
+ *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
+ *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
+ *pos--= '-';
+ *pos--= (char) ('0'+(char) (part1%10)); part1/=10;
+ *pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10);
+ *pos--= '-';
+ *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
+ *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
+ *pos--= (char) ('0'+(char) (part3%10)); part3/=10;
+ *pos=(char) ('0'+(char) part3);
+ out << buf;
+ }
+ break;
+ case NdbDictionary::Column::Date:
+ {
+ uint32 tmp=(uint32) uint3korr(r.aRef());
+ int part;
+ char buf[40];
+ char *pos=(char*) buf+10;
+ *pos--=0;
+ part=(int) (tmp & 31);
+ *pos--= (char) ('0'+part%10);
+ *pos--= (char) ('0'+part/10);
+ *pos--= '-';
+ part=(int) (tmp >> 5 & 15);
+ *pos--= (char) ('0'+part%10);
+ *pos--= (char) ('0'+part/10);
+ *pos--= '-';
+ part=(int) (tmp >> 9);
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos= (char) ('0'+part);
+ out << buf;
+ }
+ break;
+ case NdbDictionary::Column::Time:
+ {
+ long tmp=(long) sint3korr(r.aRef());
+ int hour=(uint) (tmp/10000);
+ int minute=(uint) (tmp/100 % 100);
+ int second=(uint) (tmp % 100);
+ char buf[40];
+ sprintf(buf, "%02d:%02d:%02d", hour, minute, second);
+ out << buf;
+ }
+ break;
+ case NdbDictionary::Column::Year:
+ {
+ uint year = 1900 + r.u_char_value();
+ char buf[40];
+ sprintf(buf, "%04d", year);
+ out << buf;
+ }
+ break;
+ case NdbDictionary::Column::Timestamp:
+ {
+ time_t time = r.u_32_value();
+ out << (uint)time;
+ }
+ break;
+ case NdbDictionary::Column::Blob:
+ {
+ const NdbBlob::Head* h = (const NdbBlob::Head*)r.aRef();
+ out << h->length << ":";
+ const unsigned char* p = (const unsigned char*)(h + 1);
+ unsigned n = r.arraySize() - sizeof(*h);
+ for (unsigned k = 0; k < n && k < h->length; k++)
+ out.print("%02X", (int)p[k]);
+ j = length;
+ }
+ break;
+ case NdbDictionary::Column::Text:
+ {
+ const NdbBlob::Head* h = (const NdbBlob::Head*)r.aRef();
+ out << h->length << ":";
+ const unsigned char* p = (const unsigned char*)(h + 1);
+ unsigned n = r.arraySize() - sizeof(*h);
+ for (unsigned k = 0; k < n && k < h->length; k++)
+ out.print("%c", (int)p[k]);
+ j = length;
+ }
+ break;
+ case NdbDictionary::Column::Longvarchar:
+ {
+ unsigned len = uint2korr(r.aRef());
+ ndbrecattr_print_string(out,"Longvarchar", r.aRef()+2,len);
+ j = length;
+ }
+ break;
+ unknown:
+ default: /* no print functions for the rest, just print type */
+ out << (int) r.getType();
+ j = length;
+ if (j > 1)
+ out << " " << j << " times";
+ break;
+ }
+ }
+
+ if (length > 1)
+ {
+ out << "]";
+ }
+
+ return out;
+}
+
+Int64
+NdbRecAttr::int64_value() const
+{
+ Int64 val;
+ memcpy(&val,theRef,8);
+ return val;
+}
+
+Uint64
+NdbRecAttr::u_64_value() const
+{
+ Uint64 val;
+ memcpy(&val,theRef,8);
+ return val;
+}
+
+float
+NdbRecAttr::float_value() const
+{
+ float val;
+ memcpy(&val,theRef,sizeof(val));
+ return val;
+}
+
+double
+NdbRecAttr::double_value() const
+{
+ double val;
+ memcpy(&val,theRef,sizeof(val));
+ return val;
+}
diff --git a/storage/ndb/src/ndbapi/NdbReceiver.cpp b/storage/ndb/src/ndbapi/NdbReceiver.cpp
new file mode 100644
index 00000000000..df16ae66915
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbReceiver.cpp
@@ -0,0 +1,295 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "NdbImpl.hpp"
+#include <NdbReceiver.hpp>
+#include "NdbDictionaryImpl.hpp"
+#include <NdbRecAttr.hpp>
+#include <AttributeHeader.hpp>
+#include <NdbTransaction.hpp>
+#include <TransporterFacade.hpp>
+#include <signaldata/TcKeyConf.hpp>
+
+NdbReceiver::NdbReceiver(Ndb *aNdb) :
+ theMagicNumber(0),
+ m_ndb(aNdb),
+ m_id(NdbObjectIdMap::InvalidId),
+ m_type(NDB_UNINITIALIZED),
+ m_owner(0)
+{
+ theCurrentRecAttr = theFirstRecAttr = 0;
+ m_defined_rows = 0;
+ m_rows = new NdbRecAttr*[0];
+}
+
+NdbReceiver::~NdbReceiver()
+{
+ DBUG_ENTER("NdbReceiver::~NdbReceiver");
+ if (m_id != NdbObjectIdMap::InvalidId) {
+ m_ndb->theImpl->theNdbObjectIdMap.unmap(m_id, this);
+ }
+ delete[] m_rows;
+ DBUG_VOID_RETURN;
+}
+
+void
+NdbReceiver::init(ReceiverType type, void* owner)
+{
+ theMagicNumber = 0x11223344;
+ m_type = type;
+ m_owner = owner;
+ if (m_id == NdbObjectIdMap::InvalidId) {
+ if (m_ndb)
+ m_id = m_ndb->theImpl->theNdbObjectIdMap.map(this);
+ }
+
+ theFirstRecAttr = NULL;
+ theCurrentRecAttr = NULL;
+}
+
+void
+NdbReceiver::release(){
+ NdbRecAttr* tRecAttr = theFirstRecAttr;
+ while (tRecAttr != NULL)
+ {
+ NdbRecAttr* tSaveRecAttr = tRecAttr;
+ tRecAttr = tRecAttr->next();
+ m_ndb->releaseRecAttr(tSaveRecAttr);
+ }
+ theFirstRecAttr = NULL;
+ theCurrentRecAttr = NULL;
+}
+
+NdbRecAttr *
+NdbReceiver::getValue(const NdbColumnImpl* tAttrInfo, char * user_dst_ptr){
+ NdbRecAttr* tRecAttr = m_ndb->getRecAttr();
+ if(tRecAttr && !tRecAttr->setup(tAttrInfo, user_dst_ptr)){
+ if (theFirstRecAttr == NULL)
+ theFirstRecAttr = tRecAttr;
+ else
+ theCurrentRecAttr->next(tRecAttr);
+ theCurrentRecAttr = tRecAttr;
+ tRecAttr->next(NULL);
+ return tRecAttr;
+ }
+ if(tRecAttr){
+ m_ndb->releaseRecAttr(tRecAttr);
+ }
+ return 0;
+}
+
+#define KEY_ATTR_ID (~(Uint32)0)
+
+void
+NdbReceiver::calculate_batch_size(Uint32 key_size,
+ Uint32 parallelism,
+ Uint32& batch_size,
+ Uint32& batch_byte_size,
+ Uint32& first_batch_size)
+{
+ TransporterFacade *tp= TransporterFacade::instance();
+ Uint32 max_scan_batch_size= tp->get_scan_batch_size();
+ Uint32 max_batch_byte_size= tp->get_batch_byte_size();
+ Uint32 max_batch_size= tp->get_batch_size();
+ Uint32 tot_size= (key_size ? (key_size + 32) : 0); //key + signal overhead
+ NdbRecAttr *rec_attr= theFirstRecAttr;
+ while (rec_attr != NULL) {
+ Uint32 attr_size= rec_attr->attrSize() * rec_attr->arraySize();
+ attr_size= ((attr_size + 7) >> 2) << 2; //Even to word + overhead
+ tot_size+= attr_size;
+ rec_attr= rec_attr->next();
+ }
+ tot_size+= 32; //include signal overhead
+
+ /**
+ * Now we calculate the batch size by trying to get upto SCAN_BATCH_SIZE
+ * bytes sent for each batch from each node. We do however ensure that
+ * no more than MAX_SCAN_BATCH_SIZE is sent from all nodes in total per
+ * batch.
+ */
+ batch_byte_size= max_batch_byte_size;
+ if (batch_byte_size * parallelism > max_scan_batch_size) {
+ batch_byte_size= max_scan_batch_size / parallelism;
+ }
+ batch_size= batch_byte_size / tot_size;
+ if (batch_size == 0) {
+ batch_size= 1;
+ } else {
+ if (batch_size > max_batch_size) {
+ batch_size= max_batch_size;
+ } else if (batch_size > MAX_PARALLEL_OP_PER_SCAN) {
+ batch_size= MAX_PARALLEL_OP_PER_SCAN;
+ }
+ }
+ first_batch_size= batch_size;
+ return;
+}
+
+void
+NdbReceiver::do_get_value(NdbReceiver * org,
+ Uint32 rows,
+ Uint32 key_size,
+ Uint32 range_no){
+ if(rows > m_defined_rows){
+ delete[] m_rows;
+ m_defined_rows = rows;
+ m_rows = new NdbRecAttr*[rows + 1];
+ }
+ m_rows[rows] = 0;
+
+ NdbColumnImpl key;
+ if(key_size){
+ key.m_attrId = KEY_ATTR_ID;
+ key.m_arraySize = key_size+1;
+ key.m_attrSize = 4;
+ key.m_nullable = true; // So that receive works w.r.t KEYINFO20
+ }
+ m_hidden_count = (key_size ? 1 : 0) + range_no ;
+
+ for(Uint32 i = 0; i<rows; i++){
+ NdbRecAttr * prev = theCurrentRecAttr;
+ assert(prev == 0 || i > 0);
+
+ // Put key-recAttr fir on each row
+ if(key_size && !getValue(&key, (char*)0)){
+ abort();
+ return ; // -1
+ }
+
+ if(range_no &&
+ !getValue(&NdbColumnImpl::getImpl(* NdbDictionary::Column::RANGE_NO),0))
+ {
+ abort();
+ }
+
+ NdbRecAttr* tRecAttr = org->theFirstRecAttr;
+ while(tRecAttr != 0){
+ if(getValue(&NdbColumnImpl::getImpl(*tRecAttr->m_column), (char*)0) != 0)
+ tRecAttr = tRecAttr->next();
+ else
+ break;
+ }
+
+ if(tRecAttr){
+ abort();
+ return ;// -1;
+ }
+
+ // Store first recAttr for each row in m_rows[i]
+ if(prev){
+ m_rows[i] = prev->next();
+ } else {
+ m_rows[i] = theFirstRecAttr;
+ }
+ }
+
+ prepareSend();
+ return;
+}
+
+NdbRecAttr*
+NdbReceiver::copyout(NdbReceiver & dstRec){
+ NdbRecAttr *src = m_rows[m_current_row++];
+ NdbRecAttr *dst = dstRec.theFirstRecAttr;
+ NdbRecAttr *start = src;
+ Uint32 tmp = m_hidden_count;
+ while(tmp--)
+ src = src->next();
+
+ while(dst){
+ Uint32 len = ((src->theAttrSize * src->theArraySize)+3)/4;
+ dst->receive_data((Uint32*)src->aRef(), src->isNULL() ? 0 : len);
+ src = src->next();
+ dst = dst->next();
+ }
+
+ return start;
+}
+
+int
+NdbReceiver::execTRANSID_AI(const Uint32* aDataPtr, Uint32 aLength)
+{
+ bool ok = true;
+ NdbRecAttr* currRecAttr = theCurrentRecAttr;
+
+ for (Uint32 used = 0; used < aLength ; used++){
+ AttributeHeader ah(* aDataPtr++);
+ const Uint32 tAttrId = ah.getAttributeId();
+ const Uint32 tAttrSize = ah.getDataSize();
+
+ /**
+ * Set all results to NULL if not found...
+ */
+ while(currRecAttr && currRecAttr->attrId() != tAttrId){
+ ok &= currRecAttr->setNULL();
+ currRecAttr = currRecAttr->next();
+ }
+
+ if(ok && currRecAttr && currRecAttr->receive_data(aDataPtr, tAttrSize)){
+ used += tAttrSize;
+ aDataPtr += tAttrSize;
+ currRecAttr = currRecAttr->next();
+ } else {
+ ndbout_c("%p: ok: %d tAttrId: %d currRecAttr: %p",
+ this,ok, tAttrId, currRecAttr);
+ currRecAttr = theCurrentRecAttr;
+ while(currRecAttr != 0){
+ ndbout_c("%d ", currRecAttr->attrId());
+ currRecAttr = currRecAttr->next();
+ }
+ abort();
+ return -1;
+ }
+ }
+
+ theCurrentRecAttr = currRecAttr;
+
+ /**
+ * Update m_received_result_length
+ */
+ Uint32 exp = m_expected_result_length;
+ Uint32 tmp = m_received_result_length + aLength;
+ m_received_result_length = tmp;
+
+ return (tmp == exp || (exp > TcKeyConf::SimpleReadBit) ? 1 : 0);
+}
+
+int
+NdbReceiver::execKEYINFO20(Uint32 info, const Uint32* aDataPtr, Uint32 aLength)
+{
+ NdbRecAttr* currRecAttr = m_rows[m_current_row++];
+ assert(currRecAttr->attrId() == KEY_ATTR_ID);
+ currRecAttr->receive_data(aDataPtr, aLength + 1);
+
+ /**
+ * Save scanInfo in the end of keyinfo
+ */
+ ((Uint32*)currRecAttr->aRef())[aLength] = info;
+
+ Uint32 tmp = m_received_result_length + aLength;
+ m_received_result_length = tmp;
+
+ return (tmp == m_expected_result_length ? 1 : 0);
+}
+
+void
+NdbReceiver::setErrorCode(int code)
+{
+ theMagicNumber = 0;
+ NdbOperation* op = (NdbOperation*)getOwner();
+ op->setErrorCode(code);
+}
diff --git a/storage/ndb/src/ndbapi/NdbScanFilter.cpp b/storage/ndb/src/ndbapi/NdbScanFilter.cpp
new file mode 100644
index 00000000000..b39fd10fe95
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbScanFilter.cpp
@@ -0,0 +1,577 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbScanFilter.hpp>
+#include <NdbOperation.hpp>
+#include "NdbDictionaryImpl.hpp"
+#include <Vector.hpp>
+#include <NdbOut.hpp>
+#include <Interpreter.hpp>
+
+#ifdef VM_TRACE
+#include <NdbEnv.h>
+#define INT_DEBUG(x) \
+ { const char* tmp = NdbEnv_GetEnv("INT_DEBUG", (char*)0, 0); \
+ if (tmp != 0 && strlen(tmp) != 0) { ndbout << "INT:"; ndbout_c x; } }
+#else
+#define INT_DEBUG(x)
+#endif
+
+class NdbScanFilterImpl {
+public:
+ struct State {
+ NdbScanFilter::Group m_group;
+ Uint32 m_popCount;
+ Uint32 m_ownLabel;
+ Uint32 m_trueLabel;
+ Uint32 m_falseLabel;
+ };
+
+ int m_label;
+ State m_current;
+ Vector<State> m_stack;
+ NdbOperation * m_operation;
+ Uint32 m_latestAttrib;
+
+ int cond_col(Interpreter::UnaryCondition, Uint32 attrId);
+
+ int cond_col_const(Interpreter::BinaryCondition, Uint32 attrId,
+ const void * value, Uint32 len);
+};
+
+const Uint32 LabelExit = ~0;
+
+
+NdbScanFilter::NdbScanFilter(class NdbOperation * op)
+ : m_impl(* new NdbScanFilterImpl())
+{
+ m_impl.m_current.m_group = (NdbScanFilter::Group)0;
+ m_impl.m_current.m_popCount = 0;
+ m_impl.m_current.m_ownLabel = 0;
+ m_impl.m_current.m_trueLabel = ~0;
+ m_impl.m_current.m_falseLabel = ~0;
+ m_impl.m_label = 0;
+ m_impl.m_latestAttrib = ~0;
+ m_impl.m_operation = op;
+}
+
+NdbScanFilter::~NdbScanFilter(){
+ delete &m_impl;
+}
+
+int
+NdbScanFilter::begin(Group group){
+
+ switch(group){
+ case NdbScanFilter::AND:
+ INT_DEBUG(("Begin(AND)"));
+ break;
+ case NdbScanFilter::OR:
+ INT_DEBUG(("Begin(OR)"));
+ break;
+ case NdbScanFilter::NAND:
+ INT_DEBUG(("Begin(NAND)"));
+ break;
+ case NdbScanFilter::NOR:
+ INT_DEBUG(("Begin(NOR)"));
+ break;
+ }
+
+ if(group == m_impl.m_current.m_group){
+ switch(group){
+ case NdbScanFilter::AND:
+ case NdbScanFilter::OR:
+ m_impl.m_current.m_popCount++;
+ return 0;
+ case NdbScanFilter::NOR:
+ case NdbScanFilter::NAND:
+ break;
+ }
+ }
+
+ NdbScanFilterImpl::State tmp = m_impl.m_current;
+ m_impl.m_stack.push_back(m_impl.m_current);
+ m_impl.m_current.m_group = group;
+ m_impl.m_current.m_ownLabel = m_impl.m_label++;
+ m_impl.m_current.m_popCount = 0;
+
+ switch(group){
+ case NdbScanFilter::AND:
+ case NdbScanFilter::NAND:
+ m_impl.m_current.m_falseLabel = m_impl.m_current.m_ownLabel;
+ m_impl.m_current.m_trueLabel = tmp.m_trueLabel;
+ break;
+ case NdbScanFilter::OR:
+ case NdbScanFilter::NOR:
+ m_impl.m_current.m_falseLabel = tmp.m_falseLabel;
+ m_impl.m_current.m_trueLabel = m_impl.m_current.m_ownLabel;
+ break;
+ default:
+ m_impl.m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+NdbScanFilter::end(){
+
+ switch(m_impl.m_current.m_group){
+ case NdbScanFilter::AND:
+ INT_DEBUG(("End(AND pc=%d)", m_impl.m_current.m_popCount));
+ break;
+ case NdbScanFilter::OR:
+ INT_DEBUG(("End(OR pc=%d)", m_impl.m_current.m_popCount));
+ break;
+ case NdbScanFilter::NAND:
+ INT_DEBUG(("End(NAND pc=%d)", m_impl.m_current.m_popCount));
+ break;
+ case NdbScanFilter::NOR:
+ INT_DEBUG(("End(NOR pc=%d)", m_impl.m_current.m_popCount));
+ break;
+ }
+
+ if(m_impl.m_current.m_popCount > 0){
+ m_impl.m_current.m_popCount--;
+ return 0;
+ }
+
+ NdbScanFilterImpl::State tmp = m_impl.m_current;
+ m_impl.m_current = m_impl.m_stack.back();
+ m_impl.m_stack.erase(m_impl.m_stack.size() - 1);
+
+ switch(tmp.m_group){
+ case NdbScanFilter::AND:
+ if(tmp.m_trueLabel == (Uint32)~0){
+ m_impl.m_operation->interpret_exit_ok();
+ } else {
+ m_impl.m_operation->branch_label(tmp.m_trueLabel);
+ }
+ break;
+ case NdbScanFilter::NAND:
+ if(tmp.m_trueLabel == (Uint32)~0){
+ m_impl.m_operation->interpret_exit_nok();
+ } else {
+ m_impl.m_operation->branch_label(tmp.m_falseLabel);
+ }
+ break;
+ case NdbScanFilter::OR:
+ if(tmp.m_falseLabel == (Uint32)~0){
+ m_impl.m_operation->interpret_exit_nok();
+ } else {
+ m_impl.m_operation->branch_label(tmp.m_falseLabel);
+ }
+ break;
+ case NdbScanFilter::NOR:
+ if(tmp.m_falseLabel == (Uint32)~0){
+ m_impl.m_operation->interpret_exit_ok();
+ } else {
+ m_impl.m_operation->branch_label(tmp.m_trueLabel);
+ }
+ break;
+ default:
+ m_impl.m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ m_impl.m_operation->def_label(tmp.m_ownLabel);
+
+ if(m_impl.m_stack.size() == 0){
+ switch(tmp.m_group){
+ case NdbScanFilter::AND:
+ case NdbScanFilter::NOR:
+ m_impl.m_operation->interpret_exit_nok();
+ break;
+ case NdbScanFilter::OR:
+ case NdbScanFilter::NAND:
+ m_impl.m_operation->interpret_exit_ok();
+ break;
+ default:
+ m_impl.m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+NdbScanFilter::istrue(){
+ if(m_impl.m_current.m_group < NdbScanFilter::AND ||
+ m_impl.m_current.m_group > NdbScanFilter::NOR){
+ m_impl.m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ if(m_impl.m_current.m_trueLabel == (Uint32)~0){
+ return m_impl.m_operation->interpret_exit_ok();
+ } else {
+ return m_impl.m_operation->branch_label(m_impl.m_current.m_trueLabel);
+ }
+}
+
+int
+NdbScanFilter::isfalse(){
+ if(m_impl.m_current.m_group < NdbScanFilter::AND ||
+ m_impl.m_current.m_group > NdbScanFilter::NOR){
+ m_impl.m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ if(m_impl.m_current.m_falseLabel == (Uint32)~0){
+ return m_impl.m_operation->interpret_exit_nok();
+ } else {
+ return m_impl.m_operation->branch_label(m_impl.m_current.m_falseLabel);
+ }
+}
+
+
+#define action(x, y, z)
+
+
+typedef int (NdbOperation:: * Branch1)(Uint32, Uint32 label);
+typedef int (NdbOperation:: * StrBranch2)(Uint32, const void*, Uint32, bool, Uint32);
+
+struct tab2 {
+ Branch1 m_branches[5];
+};
+
+static const tab2 table2[] = {
+ /**
+ * IS NULL
+ */
+ { { 0,
+ &NdbOperation::branch_col_ne_null,
+ &NdbOperation::branch_col_eq_null,
+ &NdbOperation::branch_col_eq_null,
+ &NdbOperation::branch_col_ne_null } }
+
+ /**
+ * IS NOT NULL
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_eq_null,
+ &NdbOperation::branch_col_ne_null,
+ &NdbOperation::branch_col_ne_null,
+ &NdbOperation::branch_col_eq_null } }
+};
+
+const int tab2_sz = sizeof(table2)/sizeof(table2[0]);
+
+int
+NdbScanFilterImpl::cond_col(Interpreter::UnaryCondition op, Uint32 AttrId){
+
+ if(op < 0 || op >= tab2_sz){
+ m_operation->setErrorCodeAbort(4262);
+ return -1;
+ }
+
+ if(m_current.m_group < NdbScanFilter::AND ||
+ m_current.m_group > NdbScanFilter::NOR){
+ m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ Branch1 branch = table2[op].m_branches[m_current.m_group];
+ (m_operation->* branch)(AttrId, m_current.m_ownLabel);
+ return 0;
+}
+
+int
+NdbScanFilter::isnull(int AttrId){
+ return m_impl.cond_col(Interpreter::IS_NULL, AttrId);
+}
+
+int
+NdbScanFilter::isnotnull(int AttrId){
+ return m_impl.cond_col(Interpreter::IS_NOT_NULL, AttrId);
+}
+
+struct tab3 {
+ StrBranch2 m_branches[5];
+};
+
+static const tab3 table3[] = {
+ /**
+ * EQ (AND, OR, NAND, NOR)
+ */
+ { { 0,
+ &NdbOperation::branch_col_ne,
+ &NdbOperation::branch_col_eq,
+ &NdbOperation::branch_col_ne,
+ &NdbOperation::branch_col_eq } }
+
+ /**
+ * NEQ
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_eq,
+ &NdbOperation::branch_col_ne,
+ &NdbOperation::branch_col_eq,
+ &NdbOperation::branch_col_ne } }
+
+ /**
+ * LT
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_le,
+ &NdbOperation::branch_col_gt,
+ &NdbOperation::branch_col_le,
+ &NdbOperation::branch_col_gt } }
+
+ /**
+ * LE
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_lt,
+ &NdbOperation::branch_col_ge,
+ &NdbOperation::branch_col_lt,
+ &NdbOperation::branch_col_ge } }
+
+ /**
+ * GT
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_ge,
+ &NdbOperation::branch_col_lt,
+ &NdbOperation::branch_col_ge,
+ &NdbOperation::branch_col_lt } }
+
+ /**
+ * GE
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_gt,
+ &NdbOperation::branch_col_le,
+ &NdbOperation::branch_col_gt,
+ &NdbOperation::branch_col_le } }
+
+ /**
+ * LIKE
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_notlike,
+ &NdbOperation::branch_col_like,
+ &NdbOperation::branch_col_notlike,
+ &NdbOperation::branch_col_like } }
+
+ /**
+ * NOT LIKE
+ */
+ ,{ { 0,
+ &NdbOperation::branch_col_like,
+ &NdbOperation::branch_col_notlike,
+ &NdbOperation::branch_col_like,
+ &NdbOperation::branch_col_notlike } }
+};
+
+const int tab3_sz = sizeof(table3)/sizeof(table3[0]);
+
+int
+NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition op,
+ Uint32 AttrId,
+ const void * value, Uint32 len){
+ if(op < 0 || op >= tab3_sz){
+ m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ if(m_current.m_group < NdbScanFilter::AND ||
+ m_current.m_group > NdbScanFilter::NOR){
+ m_operation->setErrorCodeAbort(4260);
+ return -1;
+ }
+
+ StrBranch2 branch = table3[op].m_branches[m_current.m_group];
+ const NdbDictionary::Column * col =
+ m_operation->m_currentTable->getColumn(AttrId);
+
+ if(col == 0){
+ m_operation->setErrorCodeAbort(4261);
+ return -1;
+ }
+
+ int ret = (m_operation->* branch)(AttrId, value, len, false, m_current.m_ownLabel);
+ return ret;
+}
+
+int
+NdbScanFilter::cmp(BinaryCondition cond, int ColId,
+ const void *val, Uint32 len)
+{
+ switch(cond){
+ case COND_LE:
+ return m_impl.cond_col_const(Interpreter::LE, ColId, val, len);
+ case COND_LT:
+ return m_impl.cond_col_const(Interpreter::LT, ColId, val, len);
+ case COND_GE:
+ return m_impl.cond_col_const(Interpreter::GE, ColId, val, len);
+ case COND_GT:
+ return m_impl.cond_col_const(Interpreter::GT, ColId, val, len);
+ case COND_EQ:
+ return m_impl.cond_col_const(Interpreter::EQ, ColId, val, len);
+ case COND_NE:
+ return m_impl.cond_col_const(Interpreter::NE, ColId, val, len);
+ case COND_LIKE:
+ return m_impl.cond_col_const(Interpreter::LIKE, ColId, val, len);
+ case COND_NOT_LIKE:
+ return m_impl.cond_col_const(Interpreter::NOT_LIKE, ColId, val, len);
+ }
+ return -1;
+}
+
+
+#if 0
+int
+main(void){
+ if(0)
+ {
+ ndbout << "a > 7 AND b < 9 AND c = 4" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::AND);
+ f.gt(0, 7);
+ f.lt(1, 9);
+ f.eq(2, 4);
+ f.end();
+ ndbout << endl;
+ }
+
+ if(0)
+ {
+ ndbout << "a > 7 OR b < 9 OR c = 4" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::OR);
+ f.gt(0, 7);
+ f.lt(1, 9);
+ f.eq(2, 4);
+ f.end();
+ ndbout << endl;
+ }
+
+ if(0)
+ {
+ ndbout << "a > 7 AND (b < 9 OR c = 4)" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::AND);
+ f.gt(0, 7);
+ f.begin(NdbScanFilter::OR);
+ f.lt(1, 9);
+ f.eq(2, 4);
+ f.end();
+ f.end();
+ ndbout << endl;
+ }
+
+ if(0)
+ {
+ ndbout << "a > 7 AND (b < 9 AND c = 4)" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::AND);
+ f.gt(0, 7);
+ f.begin(NdbScanFilter::AND);
+ f.lt(1, 9);
+ f.eq(2, 4);
+ f.end();
+ f.end();
+ ndbout << endl;
+ }
+
+ if(0)
+ {
+ ndbout << "(a > 7 AND b < 9) AND c = 4" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::AND);
+ f.begin(NdbScanFilter::AND);
+ f.gt(0, 7);
+ f.lt(1, 9);
+ f.end();
+ f.eq(2, 4);
+ f.end();
+ ndbout << endl;
+ }
+
+ if(1)
+ {
+ ndbout << "(a > 7 OR b < 9) AND (c = 4 OR c = 5)" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::AND);
+ f.begin(NdbScanFilter::OR);
+ f.gt(0, 7);
+ f.lt(1, 9);
+ f.end();
+ f.begin(NdbScanFilter::OR);
+ f.eq(2, 4);
+ f.eq(2, 5);
+ f.end();
+ f.end();
+ ndbout << endl;
+ }
+
+ if(1)
+ {
+ ndbout << "(a > 7 AND b < 9) OR (c = 4 AND c = 5)" << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::OR);
+ f.begin(NdbScanFilter::AND);
+ f.gt(0, 7);
+ f.lt(1, 9);
+ f.end();
+ f.begin(NdbScanFilter::AND);
+ f.eq(2, 4);
+ f.eq(2, 5);
+ f.end();
+ f.end();
+ ndbout << endl;
+ }
+
+ if(1)
+ {
+ ndbout <<
+ "((a > 7 AND b < 9) OR (c = 4 AND d = 5)) AND "
+ "((e > 6 AND f < 8) OR (g = 2 AND h = 3)) " << endl;
+ NdbScanFilter f(0);
+ f.begin(NdbScanFilter::AND);
+ f.begin(NdbScanFilter::OR);
+ f.begin(NdbScanFilter::AND);
+ f.gt(0, 7);
+ f.lt(1, 9);
+ f.end();
+ f.begin(NdbScanFilter::AND);
+ f.eq(2, 4);
+ f.eq(3, 5);
+ f.end();
+ f.end();
+
+ f.begin(NdbScanFilter::OR);
+ f.begin(NdbScanFilter::AND);
+ f.gt(4, 6);
+ f.lt(5, 8);
+ f.end();
+ f.begin(NdbScanFilter::AND);
+ f.eq(6, 2);
+ f.eq(7, 3);
+ f.end();
+ f.end();
+ f.end();
+ }
+
+ return 0;
+}
+#endif
+
+template class Vector<NdbScanFilterImpl::State>;
+
diff --git a/storage/ndb/src/ndbapi/NdbScanOperation.cpp b/storage/ndb/src/ndbapi/NdbScanOperation.cpp
new file mode 100644
index 00000000000..9cd78ec721b
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbScanOperation.cpp
@@ -0,0 +1,1669 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <Ndb.hpp>
+#include <NdbScanOperation.hpp>
+#include <NdbIndexScanOperation.hpp>
+#include <NdbTransaction.hpp>
+#include "NdbApiSignal.hpp"
+#include <NdbOut.hpp>
+#include "NdbDictionaryImpl.hpp"
+#include <NdbBlob.hpp>
+
+#include <NdbRecAttr.hpp>
+#include <NdbReceiver.hpp>
+
+#include <stdlib.h>
+#include <NdbSqlUtil.hpp>
+
+#include <signaldata/ScanTab.hpp>
+#include <signaldata/KeyInfo.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <signaldata/TcKeyReq.hpp>
+
+#define DEBUG_NEXT_RESULT 0
+
+NdbScanOperation::NdbScanOperation(Ndb* aNdb) :
+ NdbOperation(aNdb),
+ m_transConnection(NULL)
+{
+ theParallelism = 0;
+ m_allocated_receivers = 0;
+ m_prepared_receivers = 0;
+ m_api_receivers = 0;
+ m_conf_receivers = 0;
+ m_sent_receivers = 0;
+ m_receivers = 0;
+ m_array = new Uint32[1]; // skip if on delete in fix_receivers
+ theSCAN_TABREQ = 0;
+}
+
+NdbScanOperation::~NdbScanOperation()
+{
+ for(Uint32 i = 0; i<m_allocated_receivers; i++){
+ m_receivers[i]->release();
+ theNdb->releaseNdbScanRec(m_receivers[i]);
+ }
+ delete[] m_array;
+}
+
+void
+NdbScanOperation::setErrorCode(int aErrorCode){
+ NdbTransaction* tmp = theNdbCon;
+ theNdbCon = m_transConnection;
+ NdbOperation::setErrorCode(aErrorCode);
+ theNdbCon = tmp;
+}
+
+void
+NdbScanOperation::setErrorCodeAbort(int aErrorCode){
+ NdbTransaction* tmp = theNdbCon;
+ theNdbCon = m_transConnection;
+ NdbOperation::setErrorCodeAbort(aErrorCode);
+ theNdbCon = tmp;
+}
+
+
+/*****************************************************************************
+ * int init();
+ *
+ * Return Value: Return 0 : init was successful.
+ * Return -1: In all other case.
+ * Remark: Initiates operation record after allocation.
+ *****************************************************************************/
+int
+NdbScanOperation::init(const NdbTableImpl* tab, NdbTransaction* myConnection)
+{
+ m_transConnection = myConnection;
+ //NdbTransaction* aScanConnection = theNdb->startTransaction(myConnection);
+ NdbTransaction* aScanConnection = theNdb->hupp(myConnection);
+ if (!aScanConnection){
+ setErrorCodeAbort(theNdb->getNdbError().code);
+ return -1;
+ }
+
+ // NOTE! The hupped trans becomes the owner of the operation
+ if(NdbOperation::init(tab, aScanConnection) != 0){
+ return -1;
+ }
+
+ initInterpreter();
+
+ theStatus = GetValue;
+ theOperationType = OpenScanRequest;
+ theNdbCon->theMagicNumber = 0xFE11DF;
+ theNoOfTupKeyLeft = tab->m_noOfDistributionKeys;
+ m_read_range_no = 0;
+ return 0;
+}
+
+int
+NdbScanOperation::readTuples(NdbScanOperation::LockMode lm,
+ Uint32 batch,
+ Uint32 parallel)
+{
+ m_ordered = m_descending = false;
+ Uint32 fragCount = m_currentTable->m_fragmentCount;
+
+ if (parallel > fragCount || parallel == 0) {
+ parallel = fragCount;
+ }
+
+ // It is only possible to call openScan if
+ // 1. this transcation don't already contain another scan operation
+ // 2. this transaction don't already contain other operations
+ // 3. theScanOp contains a NdbScanOperation
+ if (theNdbCon->theScanningOp != NULL){
+ setErrorCode(4605);
+ return -1;
+ }
+
+ theNdbCon->theScanningOp = this;
+ theLockMode = lm;
+
+ bool lockExcl, lockHoldMode, readCommitted;
+ switch(lm){
+ case NdbScanOperation::LM_Read:
+ lockExcl = false;
+ lockHoldMode = true;
+ readCommitted = false;
+ break;
+ case NdbScanOperation::LM_Exclusive:
+ lockExcl = true;
+ lockHoldMode = true;
+ readCommitted = false;
+ break;
+ case NdbScanOperation::LM_CommittedRead:
+ lockExcl = false;
+ lockHoldMode = false;
+ readCommitted = true;
+ break;
+ default:
+ setErrorCode(4003);
+ return -1;
+ }
+
+ m_keyInfo = lockExcl ? 1 : 0;
+
+ bool range = false;
+ if (m_accessTable->m_indexType == NdbDictionary::Index::OrderedIndex)
+ {
+ if (m_currentTable == m_accessTable){
+ // Old way of scanning indexes, should not be allowed
+ m_currentTable = theNdb->theDictionary->
+ getTable(m_currentTable->m_primaryTable.c_str());
+ assert(m_currentTable != NULL);
+ }
+ assert (m_currentTable != m_accessTable);
+ // Modify operation state
+ theStatus = GetValue;
+ theOperationType = OpenRangeScanRequest;
+ range = true;
+ }
+
+ theParallelism = parallel;
+
+ if(fix_receivers(parallel) == -1){
+ setErrorCodeAbort(4000);
+ return -1;
+ }
+
+ theSCAN_TABREQ = (!theSCAN_TABREQ ? theNdb->getSignal() : theSCAN_TABREQ);
+ if (theSCAN_TABREQ == NULL) {
+ setErrorCodeAbort(4000);
+ return -1;
+ }//if
+
+ theSCAN_TABREQ->setSignal(GSN_SCAN_TABREQ);
+ ScanTabReq * req = CAST_PTR(ScanTabReq, theSCAN_TABREQ->getDataPtrSend());
+ req->apiConnectPtr = theNdbCon->theTCConPtr;
+ req->tableId = m_accessTable->m_tableId;
+ req->tableSchemaVersion = m_accessTable->m_version;
+ req->storedProcId = 0xFFFF;
+ req->buddyConPtr = theNdbCon->theBuddyConPtr;
+
+ Uint32 reqInfo = 0;
+ ScanTabReq::setParallelism(reqInfo, parallel);
+ ScanTabReq::setScanBatch(reqInfo, 0);
+ ScanTabReq::setLockMode(reqInfo, lockExcl);
+ ScanTabReq::setHoldLockFlag(reqInfo, lockHoldMode);
+ ScanTabReq::setReadCommittedFlag(reqInfo, readCommitted);
+ ScanTabReq::setRangeScanFlag(reqInfo, range);
+ req->requestInfo = reqInfo;
+
+ Uint64 transId = theNdbCon->getTransactionId();
+ req->transId1 = (Uint32) transId;
+ req->transId2 = (Uint32) (transId >> 32);
+
+ NdbApiSignal* tSignal = theSCAN_TABREQ->next();
+ if(!tSignal)
+ {
+ theSCAN_TABREQ->next(tSignal = theNdb->getSignal());
+ }
+ theLastKEYINFO = tSignal;
+
+ tSignal->setSignal(GSN_KEYINFO);
+ theKEYINFOptr = ((KeyInfo*)tSignal->getDataPtrSend())->keyData;
+ theTotalNrOfKeyWordInSignal= 0;
+
+ getFirstATTRINFOScan();
+ return 0;
+}
+
+int
+NdbScanOperation::fix_receivers(Uint32 parallel){
+ assert(parallel > 0);
+ if(parallel > m_allocated_receivers){
+ const Uint32 sz = parallel * (4*sizeof(char*)+sizeof(Uint32));
+
+ Uint64 * tmp = new Uint64[(sz+7)/8];
+ // Save old receivers
+ memcpy(tmp, m_receivers, m_allocated_receivers*sizeof(char*));
+ delete[] m_array;
+ m_array = (Uint32*)tmp;
+
+ m_receivers = (NdbReceiver**)tmp;
+ m_api_receivers = m_receivers + parallel;
+ m_conf_receivers = m_api_receivers + parallel;
+ m_sent_receivers = m_conf_receivers + parallel;
+ m_prepared_receivers = (Uint32*)(m_sent_receivers + parallel);
+
+ // Only get/init "new" receivers
+ NdbReceiver* tScanRec;
+ for (Uint32 i = m_allocated_receivers; i < parallel; i ++) {
+ tScanRec = theNdb->getNdbScanRec();
+ if (tScanRec == NULL) {
+ setErrorCodeAbort(4000);
+ return -1;
+ }//if
+ m_receivers[i] = tScanRec;
+ tScanRec->init(NdbReceiver::NDB_SCANRECEIVER, this);
+ }
+ m_allocated_receivers = parallel;
+ }
+
+ reset_receivers(parallel, 0);
+ return 0;
+}
+
+/**
+ * Move receiver from send array to conf:ed array
+ */
+void
+NdbScanOperation::receiver_delivered(NdbReceiver* tRec){
+ if(theError.code == 0){
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("receiver_delivered");
+
+ Uint32 idx = tRec->m_list_index;
+ Uint32 last = m_sent_receivers_count - 1;
+ if(idx != last){
+ NdbReceiver * move = m_sent_receivers[last];
+ m_sent_receivers[idx] = move;
+ move->m_list_index = idx;
+ }
+ m_sent_receivers_count = last;
+
+ last = m_conf_receivers_count;
+ m_conf_receivers[last] = tRec;
+ m_conf_receivers_count = last + 1;
+ tRec->m_list_index = last;
+ tRec->m_current_row = 0;
+ }
+}
+
+/**
+ * Remove receiver as it's completed
+ */
+void
+NdbScanOperation::receiver_completed(NdbReceiver* tRec){
+ if(theError.code == 0){
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("receiver_completed");
+
+ Uint32 idx = tRec->m_list_index;
+ Uint32 last = m_sent_receivers_count - 1;
+ if(idx != last){
+ NdbReceiver * move = m_sent_receivers[last];
+ m_sent_receivers[idx] = move;
+ move->m_list_index = idx;
+ }
+ m_sent_receivers_count = last;
+ }
+}
+
+/*****************************************************************************
+ * int getFirstATTRINFOScan( U_int32 aData )
+ *
+ * Return Value: Return 0: Successful
+ * Return -1: All other cases
+ * Parameters: None: Only allocate the first signal.
+ * Remark: When a scan is defined we need to use this method instead
+ * of insertATTRINFO for the first signal.
+ * This is because we need not to mess up the code in
+ * insertATTRINFO with if statements since we are not
+ * interested in the TCKEYREQ signal.
+ *****************************************************************************/
+int
+NdbScanOperation::getFirstATTRINFOScan()
+{
+ NdbApiSignal* tSignal;
+
+ tSignal = theNdb->getSignal();
+ if (tSignal == NULL){
+ setErrorCodeAbort(4000);
+ return -1;
+ }
+ tSignal->setSignal(m_attrInfoGSN);
+ theAI_LenInCurrAI = 8;
+ theATTRINFOptr = &tSignal->getDataPtrSend()[8];
+ theFirstATTRINFO = tSignal;
+ theCurrentATTRINFO = tSignal;
+ theCurrentATTRINFO->next(NULL);
+
+ return 0;
+}
+
+/**
+ * Constats for theTupleKeyDefined[][0]
+ */
+#define SETBOUND_EQ 1
+#define FAKE_PTR 2
+#define API_PTR 3
+
+#define WAITFOR_SCAN_TIMEOUT 120000
+
+int
+NdbScanOperation::executeCursor(int nodeId){
+ NdbTransaction * tCon = theNdbCon;
+ TransporterFacade* tp = TransporterFacade::instance();
+ Guard guard(tp->theMutexPtr);
+
+ Uint32 magic = tCon->theMagicNumber;
+ Uint32 seq = tCon->theNodeSequence;
+
+ if (tp->get_node_alive(nodeId) &&
+ (tp->getNodeSequence(nodeId) == seq)) {
+
+ /**
+ * Only call prepareSendScan first time (incase of restarts)
+ * - check with theMagicNumber
+ */
+ tCon->theMagicNumber = 0x37412619;
+ if(magic != 0x37412619 &&
+ prepareSendScan(tCon->theTCConPtr, tCon->theTransactionId) == -1)
+ return -1;
+
+
+ if (doSendScan(nodeId) == -1)
+ return -1;
+
+ return 0;
+ } else {
+ if (!(tp->get_node_stopping(nodeId) &&
+ (tp->getNodeSequence(nodeId) == seq))){
+ TRACE_DEBUG("The node is hard dead when attempting to start a scan");
+ setErrorCode(4029);
+ tCon->theReleaseOnClose = true;
+ } else {
+ TRACE_DEBUG("The node is stopping when attempting to start a scan");
+ setErrorCode(4030);
+ }//if
+ tCon->theCommitStatus = NdbTransaction::Aborted;
+ }//if
+ return -1;
+}
+
+
+int NdbScanOperation::nextResult(bool fetchAllowed, bool forceSend)
+{
+ int res;
+ if ((res = nextResultImpl(fetchAllowed, forceSend)) == 0) {
+ // handle blobs
+ NdbBlob* tBlob = theBlobList;
+ while (tBlob != 0) {
+ if (tBlob->atNextResult() == -1)
+ return -1;
+ tBlob = tBlob->theNext;
+ }
+ /*
+ * Flush blob part ops on behalf of user because
+ * - nextResult is analogous to execute(NoCommit)
+ * - user is likely to want blob value before next execute
+ */
+ if (m_transConnection->executePendingBlobOps() == -1)
+ return -1;
+ return 0;
+ }
+ return res;
+}
+
+int NdbScanOperation::nextResultImpl(bool fetchAllowed, bool forceSend)
+{
+ if(m_ordered)
+ return ((NdbIndexScanOperation*)this)->next_result_ordered(fetchAllowed,
+ forceSend);
+
+ /**
+ * Check current receiver
+ */
+ int retVal = 2;
+ Uint32 idx = m_current_api_receiver;
+ Uint32 last = m_api_receivers_count;
+ m_curr_row = 0;
+
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("nextResult(%d) idx=%d last=%d", fetchAllowed, idx, last);
+
+ /**
+ * Check next buckets
+ */
+ for(; idx < last; idx++){
+ NdbReceiver* tRec = m_api_receivers[idx];
+ if(tRec->nextResult()){
+ m_curr_row = tRec->copyout(theReceiver);
+ retVal = 0;
+ break;
+ }
+ }
+
+ /**
+ * We have advanced atleast one bucket
+ */
+ if(!fetchAllowed || !retVal){
+ m_current_api_receiver = idx;
+ if(DEBUG_NEXT_RESULT) ndbout_c("return %d", retVal);
+ return retVal;
+ }
+
+ Uint32 nodeId = theNdbCon->theDBnode;
+ TransporterFacade* tp = TransporterFacade::instance();
+ Guard guard(tp->theMutexPtr);
+ if(theError.code)
+ return -1;
+
+ Uint32 seq = theNdbCon->theNodeSequence;
+ if(seq == tp->getNodeSequence(nodeId) && send_next_scan(idx, false,
+ forceSend) == 0){
+
+ idx = m_current_api_receiver;
+ last = m_api_receivers_count;
+
+ do {
+ if(theError.code){
+ setErrorCode(theError.code);
+ if(DEBUG_NEXT_RESULT) ndbout_c("return -1");
+ return -1;
+ }
+
+ Uint32 cnt = m_conf_receivers_count;
+ Uint32 sent = m_sent_receivers_count;
+
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("idx=%d last=%d cnt=%d sent=%d", idx, last, cnt, sent);
+
+ if(cnt > 0){
+ /**
+ * Just move completed receivers
+ */
+ memcpy(m_api_receivers+last, m_conf_receivers, cnt * sizeof(char*));
+ last += cnt;
+ m_conf_receivers_count = 0;
+ } else if(retVal == 2 && sent > 0){
+ /**
+ * No completed...
+ */
+ theNdb->theImpl->theWaiter.m_node = nodeId;
+ theNdb->theImpl->theWaiter.m_state = WAIT_SCAN;
+ int return_code = theNdb->receiveResponse(WAITFOR_SCAN_TIMEOUT);
+ if (return_code == 0 && seq == tp->getNodeSequence(nodeId)) {
+ continue;
+ } else {
+ idx = last;
+ retVal = -2; //return_code;
+ }
+ } else if(retVal == 2){
+ /**
+ * No completed & no sent -> EndOfData
+ */
+ theError.code = -1; // make sure user gets error if he tries again
+ if(DEBUG_NEXT_RESULT) ndbout_c("return 1");
+ return 1;
+ }
+
+ if(retVal == 0)
+ break;
+
+ for(; idx < last; idx++){
+ NdbReceiver* tRec = m_api_receivers[idx];
+ if(tRec->nextResult()){
+ m_curr_row = tRec->copyout(theReceiver);
+ retVal = 0;
+ break;
+ }
+ }
+ } while(retVal == 2);
+ } else {
+ retVal = -3;
+ }
+
+ m_api_receivers_count = last;
+ m_current_api_receiver = idx;
+
+ switch(retVal){
+ case 0:
+ case 1:
+ case 2:
+ if(DEBUG_NEXT_RESULT) ndbout_c("return %d", retVal);
+ return retVal;
+ case -1:
+ setErrorCode(4008); // Timeout
+ break;
+ case -2:
+ setErrorCode(4028); // Node fail
+ break;
+ case -3: // send_next_scan -> return fail (set error-code self)
+ if(theError.code == 0)
+ setErrorCode(4028); // seq changed = Node fail
+ break;
+ }
+
+ theNdbCon->theTransactionIsStarted = false;
+ theNdbCon->theReleaseOnClose = true;
+ if(DEBUG_NEXT_RESULT) ndbout_c("return -1", retVal);
+ return -1;
+}
+
+int
+NdbScanOperation::send_next_scan(Uint32 cnt, bool stopScanFlag,
+ bool forceSend){
+ if(cnt > 0){
+ NdbApiSignal tSignal(theNdb->theMyRef);
+ tSignal.setSignal(GSN_SCAN_NEXTREQ);
+
+ Uint32* theData = tSignal.getDataPtrSend();
+ theData[0] = theNdbCon->theTCConPtr;
+ theData[1] = stopScanFlag == true ? 1 : 0;
+ Uint64 transId = theNdbCon->theTransactionId;
+ theData[2] = transId;
+ theData[3] = (Uint32) (transId >> 32);
+
+ /**
+ * Prepare ops
+ */
+ Uint32 last = m_sent_receivers_count;
+ Uint32 * prep_array = (cnt > 21 ? m_prepared_receivers : theData + 4);
+ Uint32 sent = 0;
+ for(Uint32 i = 0; i<cnt; i++){
+ NdbReceiver * tRec = m_api_receivers[i];
+ if((prep_array[sent] = tRec->m_tcPtrI) != RNIL)
+ {
+ m_sent_receivers[last+sent] = tRec;
+ tRec->m_list_index = last+sent;
+ tRec->prepareSend();
+ sent++;
+ }
+ }
+ memmove(m_api_receivers, m_api_receivers+cnt,
+ (theParallelism-cnt) * sizeof(char*));
+
+ int ret = 0;
+ if(sent)
+ {
+ Uint32 nodeId = theNdbCon->theDBnode;
+ TransporterFacade * tp = TransporterFacade::instance();
+ if(cnt > 21){
+ tSignal.setLength(4);
+ LinearSectionPtr ptr[3];
+ ptr[0].p = prep_array;
+ ptr[0].sz = sent;
+ ret = tp->sendSignal(&tSignal, nodeId, ptr, 1);
+ } else {
+ tSignal.setLength(4+sent);
+ ret = tp->sendSignal(&tSignal, nodeId);
+ }
+ }
+
+ if (!ret) checkForceSend(forceSend);
+
+ m_sent_receivers_count = last + sent;
+ m_api_receivers_count -= cnt;
+ m_current_api_receiver = 0;
+
+ return ret;
+ }
+ return 0;
+}
+
+void NdbScanOperation::checkForceSend(bool forceSend)
+{
+ if (forceSend) {
+ TransporterFacade::instance()->forceSend(theNdb->theNdbBlockNumber);
+ } else {
+ TransporterFacade::instance()->checkForceSend(theNdb->theNdbBlockNumber);
+ }//if
+}
+
+int
+NdbScanOperation::prepareSend(Uint32 TC_ConnectPtr, Uint64 TransactionId)
+{
+ printf("NdbScanOperation::prepareSend\n");
+ abort();
+ return 0;
+}
+
+int
+NdbScanOperation::doSend(int ProcessorId)
+{
+ printf("NdbScanOperation::doSend\n");
+ return 0;
+}
+
+void NdbScanOperation::close(bool forceSend)
+{
+ if(m_transConnection){
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("close() theError.code = %d "
+ "m_api_receivers_count = %d "
+ "m_conf_receivers_count = %d "
+ "m_sent_receivers_count = %d",
+ theError.code,
+ m_api_receivers_count,
+ m_conf_receivers_count,
+ m_sent_receivers_count);
+
+ TransporterFacade* tp = TransporterFacade::instance();
+ Guard guard(tp->theMutexPtr);
+ close_impl(tp, forceSend);
+
+ } while(0);
+
+ theNdbCon->theScanningOp = 0;
+ theNdb->closeTransaction(theNdbCon);
+
+ theNdbCon = 0;
+ m_transConnection = NULL;
+}
+
+void
+NdbScanOperation::execCLOSE_SCAN_REP(){
+ m_conf_receivers_count = 0;
+ m_sent_receivers_count = 0;
+}
+
+void NdbScanOperation::release()
+{
+ if(theNdbCon != 0 || m_transConnection != 0){
+ close();
+ }
+ for(Uint32 i = 0; i<m_allocated_receivers; i++){
+ m_receivers[i]->release();
+ }
+
+ NdbOperation::release();
+
+ if(theSCAN_TABREQ)
+ {
+ theNdb->releaseSignal(theSCAN_TABREQ);
+ theSCAN_TABREQ = 0;
+ }
+}
+
+/***************************************************************************
+int prepareSendScan(Uint32 aTC_ConnectPtr,
+ Uint64 aTransactionId)
+
+Return Value: Return 0 : preparation of send was succesful.
+ Return -1: In all other case.
+Parameters: aTC_ConnectPtr: the Connect pointer to TC.
+ aTransactionId: the Transaction identity of the transaction.
+Remark: Puts the the final data into ATTRINFO signal(s) after this
+ we know the how many signal to send and their sizes
+***************************************************************************/
+int NdbScanOperation::prepareSendScan(Uint32 aTC_ConnectPtr,
+ Uint64 aTransactionId){
+
+ if (theInterpretIndicator != 1 ||
+ (theOperationType != OpenScanRequest &&
+ theOperationType != OpenRangeScanRequest)) {
+ setErrorCodeAbort(4005);
+ return -1;
+ }
+
+ theErrorLine = 0;
+
+ // In preapareSendInterpreted we set the sizes (word 4-8) in the
+ // first ATTRINFO signal.
+ if (prepareSendInterpreted() == -1)
+ return -1;
+
+ if(m_ordered){
+ ((NdbIndexScanOperation*)this)->fix_get_values();
+ }
+
+ theCurrentATTRINFO->setLength(theAI_LenInCurrAI);
+
+ /**
+ * Prepare all receivers
+ */
+ theReceiver.prepareSend();
+ bool keyInfo = m_keyInfo;
+ Uint32 key_size = keyInfo ? m_currentTable->m_keyLenInWords : 0;
+ /**
+ * The number of records sent by each LQH is calculated and the kernel
+ * is informed of this number by updating the SCAN_TABREQ signal
+ */
+ Uint32 batch_size, batch_byte_size, first_batch_size;
+ theReceiver.calculate_batch_size(key_size,
+ theParallelism,
+ batch_size,
+ batch_byte_size,
+ first_batch_size);
+ ScanTabReq * req = CAST_PTR(ScanTabReq, theSCAN_TABREQ->getDataPtrSend());
+ ScanTabReq::setScanBatch(req->requestInfo, batch_size);
+ req->batch_byte_size= batch_byte_size;
+ req->first_batch_size= first_batch_size;
+
+ /**
+ * Set keyinfo flag
+ * (Always keyinfo when using blobs)
+ */
+ Uint32 reqInfo = req->requestInfo;
+ ScanTabReq::setKeyinfoFlag(reqInfo, keyInfo);
+ req->requestInfo = reqInfo;
+
+ for(Uint32 i = 0; i<theParallelism; i++){
+ m_receivers[i]->do_get_value(&theReceiver, batch_size,
+ key_size,
+ m_read_range_no);
+ }
+ return 0;
+}
+
+/*****************************************************************************
+int doSend()
+
+Return Value: Return >0 : send was succesful, returns number of signals sent
+ Return -1: In all other case.
+Parameters: aProcessorId: Receiving processor node
+Remark: Sends the ATTRINFO signal(s)
+*****************************************************************************/
+int
+NdbScanOperation::doSendScan(int aProcessorId)
+{
+ Uint32 tSignalCount = 0;
+ NdbApiSignal* tSignal;
+
+ if (theInterpretIndicator != 1 ||
+ (theOperationType != OpenScanRequest &&
+ theOperationType != OpenRangeScanRequest)) {
+ setErrorCodeAbort(4005);
+ return -1;
+ }
+
+ assert(theSCAN_TABREQ != NULL);
+ tSignal = theSCAN_TABREQ;
+
+ Uint32 tupKeyLen = theTupKeyLen;
+ Uint32 len = theTotalNrOfKeyWordInSignal;
+ Uint32 aTC_ConnectPtr = theNdbCon->theTCConPtr;
+ Uint64 transId = theNdbCon->theTransactionId;
+
+ // Update the "attribute info length in words" in SCAN_TABREQ before
+ // sending it. This could not be done in openScan because
+ // we created the ATTRINFO signals after the SCAN_TABREQ signal.
+ ScanTabReq * const req = CAST_PTR(ScanTabReq, tSignal->getDataPtrSend());
+ req->attrLenKeyLen = (tupKeyLen << 16) | theTotalCurrAI_Len;
+ Uint32 tmp = req->requestInfo;
+ ScanTabReq::setDistributionKeyFlag(tmp, theDistrKeyIndicator_);
+ req->distributionKey = theDistributionKey;
+ req->requestInfo = tmp;
+ tSignal->setLength(ScanTabReq::StaticLength + theDistrKeyIndicator_);
+
+ TransporterFacade *tp = TransporterFacade::instance();
+ LinearSectionPtr ptr[3];
+ ptr[0].p = m_prepared_receivers;
+ ptr[0].sz = theParallelism;
+ if (tp->sendSignal(tSignal, aProcessorId, ptr, 1) == -1) {
+ setErrorCode(4002);
+ return -1;
+ }
+
+ if (tupKeyLen > 0){
+ // must have at least one signal since it contains attrLen for bounds
+ assert(theLastKEYINFO != NULL);
+ tSignal = theLastKEYINFO;
+ tSignal->setLength(KeyInfo::HeaderLength + theTotalNrOfKeyWordInSignal);
+
+ assert(theSCAN_TABREQ->next() != NULL);
+ tSignal = theSCAN_TABREQ->next();
+
+ NdbApiSignal* last;
+ do {
+ KeyInfo * keyInfo = CAST_PTR(KeyInfo, tSignal->getDataPtrSend());
+ keyInfo->connectPtr = aTC_ConnectPtr;
+ keyInfo->transId[0] = Uint32(transId);
+ keyInfo->transId[1] = Uint32(transId >> 32);
+
+ if (tp->sendSignal(tSignal,aProcessorId) == -1){
+ setErrorCode(4002);
+ return -1;
+ }
+
+ tSignalCount++;
+ last = tSignal;
+ tSignal = tSignal->next();
+ } while(last != theLastKEYINFO);
+ }
+
+ tSignal = theFirstATTRINFO;
+ while (tSignal != NULL) {
+ AttrInfo * attrInfo = CAST_PTR(AttrInfo, tSignal->getDataPtrSend());
+ attrInfo->connectPtr = aTC_ConnectPtr;
+ attrInfo->transId[0] = Uint32(transId);
+ attrInfo->transId[1] = Uint32(transId >> 32);
+
+ if (tp->sendSignal(tSignal,aProcessorId) == -1){
+ setErrorCode(4002);
+ return -1;
+ }
+ tSignalCount++;
+ tSignal = tSignal->next();
+ }
+ theStatus = WaitResponse;
+
+ m_curr_row = 0;
+ m_sent_receivers_count = theParallelism;
+ if(m_ordered)
+ {
+ m_current_api_receiver = theParallelism;
+ m_api_receivers_count = theParallelism;
+ }
+
+ return tSignalCount;
+}//NdbOperation::doSendScan()
+
+/*****************************************************************************
+ * NdbOperation* takeOverScanOp(NdbTransaction* updateTrans);
+ *
+ * Parameters: The update transactions NdbTransaction pointer.
+ * Return Value: A reference to the transferred operation object
+ * or NULL if no success.
+ * Remark: Take over the scanning transactions NdbOperation
+ * object for a tuple to an update transaction,
+ * which is the last operation read in nextScanResult()
+ * (theNdbCon->thePreviousScanRec)
+ *
+ * FUTURE IMPLEMENTATION: (This note was moved from header file.)
+ * In the future, it will even be possible to transfer
+ * to a NdbTransaction on another Ndb-object.
+ * In this case the receiving NdbTransaction-object must call
+ * a method receiveOpFromScan to actually receive the information.
+ * This means that the updating transactions can be placed
+ * in separate threads and thus increasing the parallelism during
+ * the scan process.
+ ****************************************************************************/
+int
+NdbScanOperation::getKeyFromKEYINFO20(Uint32* data, unsigned size)
+{
+ NdbRecAttr * tRecAttr = m_curr_row;
+ if(tRecAttr)
+ {
+ const Uint32 * src = (Uint32*)tRecAttr->aRef();
+ memcpy(data, src, 4*size);
+ return 0;
+ }
+ return -1;
+}
+
+NdbOperation*
+NdbScanOperation::takeOverScanOp(OperationType opType, NdbTransaction* pTrans)
+{
+
+ NdbRecAttr * tRecAttr = m_curr_row;
+ if(tRecAttr)
+ {
+ NdbOperation * newOp = pTrans->getNdbOperation(m_currentTable);
+ if (newOp == NULL){
+ return NULL;
+ }
+ pTrans->theSimpleState = 0;
+
+ const Uint32 len = (tRecAttr->attrSize() * tRecAttr->arraySize() + 3)/4-1;
+
+ newOp->theTupKeyLen = len;
+ newOp->theOperationType = opType;
+ if (opType == DeleteRequest) {
+ newOp->theStatus = GetValue;
+ } else {
+ newOp->theStatus = SetValue;
+ }
+
+ const Uint32 * src = (Uint32*)tRecAttr->aRef();
+ const Uint32 tScanInfo = src[len] & 0x3FFFF;
+ const Uint32 tTakeOverFragment = src[len] >> 20;
+ {
+ UintR scanInfo = 0;
+ TcKeyReq::setTakeOverScanFlag(scanInfo, 1);
+ TcKeyReq::setTakeOverScanFragment(scanInfo, tTakeOverFragment);
+ TcKeyReq::setTakeOverScanInfo(scanInfo, tScanInfo);
+ newOp->theScanInfo = scanInfo;
+ newOp->theDistrKeyIndicator_ = 1;
+ newOp->theDistributionKey = tTakeOverFragment;
+ }
+
+ // Copy the first 8 words of key info from KEYINF20 into TCKEYREQ
+ TcKeyReq * tcKeyReq = CAST_PTR(TcKeyReq,newOp->theTCREQ->getDataPtrSend());
+ Uint32 i = 0;
+ for (i = 0; i < TcKeyReq::MaxKeyInfo && i < len; i++) {
+ tcKeyReq->keyInfo[i] = * src++;
+ }
+
+ if(i < len){
+ NdbApiSignal* tSignal = theNdb->getSignal();
+ newOp->theTCREQ->next(tSignal);
+
+ Uint32 left = len - i;
+ while(tSignal && left > KeyInfo::DataLength){
+ tSignal->setSignal(GSN_KEYINFO);
+ KeyInfo * keyInfo = CAST_PTR(KeyInfo, tSignal->getDataPtrSend());
+ memcpy(keyInfo->keyData, src, 4 * KeyInfo::DataLength);
+ src += KeyInfo::DataLength;
+ left -= KeyInfo::DataLength;
+
+ tSignal->next(theNdb->getSignal());
+ tSignal = tSignal->next();
+ }
+
+ if(tSignal && left > 0){
+ tSignal->setSignal(GSN_KEYINFO);
+ KeyInfo * keyInfo = CAST_PTR(KeyInfo, tSignal->getDataPtrSend());
+ memcpy(keyInfo->keyData, src, 4 * left);
+ }
+ }
+ // create blob handles automatically
+ if (opType == DeleteRequest && m_currentTable->m_noOfBlobs != 0) {
+ for (unsigned i = 0; i < m_currentTable->m_columns.size(); i++) {
+ NdbColumnImpl* c = m_currentTable->m_columns[i];
+ assert(c != 0);
+ if (c->getBlobType()) {
+ if (newOp->getBlobHandle(pTrans, c) == NULL)
+ return NULL;
+ }
+ }
+ }
+
+ return newOp;
+ }
+ return 0;
+}
+
+NdbBlob*
+NdbScanOperation::getBlobHandle(const char* anAttrName)
+{
+ m_keyInfo = 1;
+ return NdbOperation::getBlobHandle(m_transConnection,
+ m_currentTable->getColumn(anAttrName));
+}
+
+NdbBlob*
+NdbScanOperation::getBlobHandle(Uint32 anAttrId)
+{
+ m_keyInfo = 1;
+ return NdbOperation::getBlobHandle(m_transConnection,
+ m_currentTable->getColumn(anAttrId));
+}
+
+NdbIndexScanOperation::NdbIndexScanOperation(Ndb* aNdb)
+ : NdbScanOperation(aNdb)
+{
+}
+
+NdbIndexScanOperation::~NdbIndexScanOperation(){
+}
+
+int
+NdbIndexScanOperation::setBound(const char* anAttrName, int type,
+ const void* aValue, Uint32 len)
+{
+ return setBound(m_accessTable->getColumn(anAttrName), type, aValue, len);
+}
+
+int
+NdbIndexScanOperation::setBound(Uint32 anAttrId, int type,
+ const void* aValue, Uint32 len)
+{
+ return setBound(m_accessTable->getColumn(anAttrId), type, aValue, len);
+}
+
+int
+NdbIndexScanOperation::equal_impl(const NdbColumnImpl* anAttrObject,
+ const char* aValue,
+ Uint32 len){
+ return setBound(anAttrObject, BoundEQ, aValue, len);
+}
+
+NdbRecAttr*
+NdbIndexScanOperation::getValue_impl(const NdbColumnImpl* attrInfo,
+ char* aValue){
+ if(!m_ordered){
+ return NdbScanOperation::getValue_impl(attrInfo, aValue);
+ }
+
+ int id = attrInfo->m_attrId; // In "real" table
+ assert(m_accessTable->m_index);
+ int sz = (int)m_accessTable->m_index->m_key_ids.size();
+ if(id >= sz || (id = m_accessTable->m_index->m_key_ids[id]) == -1){
+ return NdbScanOperation::getValue_impl(attrInfo, aValue);
+ }
+
+ assert(id < NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY);
+ Uint32 marker = theTupleKeyDefined[id][0];
+
+ if(marker == SETBOUND_EQ){
+ return NdbScanOperation::getValue_impl(attrInfo, aValue);
+ } else if(marker == API_PTR){
+ return NdbScanOperation::getValue_impl(attrInfo, aValue);
+ }
+
+ assert(marker == FAKE_PTR);
+
+ UintPtr oldVal;
+ oldVal = theTupleKeyDefined[id][1];
+#if (SIZEOF_CHARP == 8)
+ oldVal = oldVal | (((UintPtr)theTupleKeyDefined[id][2]) << 32);
+#endif
+ theTupleKeyDefined[id][0] = API_PTR;
+
+ NdbRecAttr* tmp = (NdbRecAttr*)oldVal;
+ tmp->setup(attrInfo, aValue);
+
+ return tmp;
+}
+
+#include <AttributeHeader.hpp>
+/*
+ * Define bound on index column in range scan.
+ */
+int
+NdbIndexScanOperation::setBound(const NdbColumnImpl* tAttrInfo,
+ int type, const void* aValue, Uint32 len)
+{
+ if (theOperationType == OpenRangeScanRequest &&
+ (0 <= type && type <= 4) &&
+ len <= 8000) {
+ // insert bound type
+ Uint32 currLen = theTotalNrOfKeyWordInSignal;
+ Uint32 remaining = KeyInfo::DataLength - currLen;
+ Uint32 sizeInBytes = tAttrInfo->m_attrSize * tAttrInfo->m_arraySize;
+ bool tDistrKey = tAttrInfo->m_distributionKey;
+
+ len = aValue != NULL ? sizeInBytes : 0;
+ if (len != sizeInBytes && (len != 0)) {
+ setErrorCodeAbort(4209);
+ return -1;
+ }
+
+ // insert attribute header
+ Uint32 tIndexAttrId = tAttrInfo->m_attrId;
+ Uint32 sizeInWords = (len + 3) / 4;
+ AttributeHeader ah(tIndexAttrId, sizeInWords);
+ const Uint32 ahValue = ah.m_value;
+
+ const Uint32 align = (UintPtr(aValue) & 7);
+ const bool aligned = (tDistrKey && type == BoundEQ) ?
+ (align == 0) : (align & 3) == 0;
+
+ const bool nobytes = (len & 0x3) == 0;
+ const Uint32 totalLen = 2 + sizeInWords;
+ Uint32 tupKeyLen = theTupKeyLen;
+ if(remaining > totalLen && aligned && nobytes){
+ Uint32 * dst = theKEYINFOptr + currLen;
+ * dst ++ = type;
+ * dst ++ = ahValue;
+ memcpy(dst, aValue, 4 * sizeInWords);
+ theTotalNrOfKeyWordInSignal = currLen + totalLen;
+ } else {
+ if(!aligned || !nobytes){
+ Uint32 tempData[2000];
+ tempData[0] = type;
+ tempData[1] = ahValue;
+ tempData[2 + (len >> 2)] = 0;
+ memcpy(tempData+2, aValue, len);
+
+ insertBOUNDS(tempData, 2+sizeInWords);
+ } else {
+ Uint32 buf[2] = { type, ahValue };
+ insertBOUNDS(buf, 2);
+ insertBOUNDS((Uint32*)aValue, sizeInWords);
+ }
+ }
+ theTupKeyLen = tupKeyLen + totalLen;
+
+ /**
+ * Do sorted stuff
+ */
+
+ /**
+ * The primary keys for an ordered index is defined in the beginning
+ * so it's safe to use [tIndexAttrId]
+ * (instead of looping as is NdbOperation::equal_impl)
+ */
+ if(type == BoundEQ && tDistrKey)
+ {
+ theNoOfTupKeyLeft--;
+ return handle_distribution_key((Uint64*)aValue, sizeInWords);
+ }
+ return 0;
+ } else {
+ setErrorCodeAbort(4228); // XXX wrong code
+ return -1;
+ }
+}
+
+int
+NdbIndexScanOperation::insertBOUNDS(Uint32 * data, Uint32 sz){
+ Uint32 len;
+ Uint32 remaining = KeyInfo::DataLength - theTotalNrOfKeyWordInSignal;
+ Uint32 * dst = theKEYINFOptr + theTotalNrOfKeyWordInSignal;
+ do {
+ len = (sz < remaining ? sz : remaining);
+ memcpy(dst, data, 4 * len);
+
+ if(sz >= remaining){
+ NdbApiSignal* tCurr = theLastKEYINFO;
+ tCurr->setLength(KeyInfo::MaxSignalLength);
+ NdbApiSignal* tSignal = tCurr->next();
+ if(tSignal)
+ ;
+ else if((tSignal = theNdb->getSignal()) != 0)
+ {
+ tCurr->next(tSignal);
+ tSignal->setSignal(GSN_KEYINFO);
+ } else {
+ goto error;
+ }
+ theLastKEYINFO = tSignal;
+ theKEYINFOptr = dst = ((KeyInfo*)tSignal->getDataPtrSend())->keyData;
+ remaining = KeyInfo::DataLength;
+ sz -= len;
+ data += len;
+ } else {
+ len = (KeyInfo::DataLength - remaining) + len;
+ break;
+ }
+ } while(true);
+ theTotalNrOfKeyWordInSignal = len;
+ return 0;
+
+error:
+ setErrorCodeAbort(4228); // XXX wrong code
+ return -1;
+}
+
+int
+NdbIndexScanOperation::readTuples(LockMode lm,
+ Uint32 batch,
+ Uint32 parallel,
+ bool order_by,
+ bool order_desc,
+ bool read_range_no){
+ int res = NdbScanOperation::readTuples(lm, batch, 0);
+ if(!res && read_range_no)
+ {
+ m_read_range_no = 1;
+ Uint32 word = 0;
+ AttributeHeader::init(&word, AttributeHeader::RANGE_NO, 0);
+ if(insertATTRINFO(word) == -1)
+ res = -1;
+ }
+ if(!res && order_by){
+ m_ordered = true;
+ if (order_desc) {
+ m_descending = true;
+ ScanTabReq * req = CAST_PTR(ScanTabReq, theSCAN_TABREQ->getDataPtrSend());
+ ScanTabReq::setDescendingFlag(req->requestInfo, true);
+ }
+ Uint32 cnt = m_accessTable->getNoOfColumns() - 1;
+ m_sort_columns = cnt; // -1 for NDB$NODE
+ m_current_api_receiver = m_sent_receivers_count;
+ m_api_receivers_count = m_sent_receivers_count;
+
+ m_sort_columns = cnt;
+ for(Uint32 i = 0; i<cnt; i++){
+ const NdbColumnImpl* key = m_accessTable->m_index->m_columns[i];
+ const NdbColumnImpl* col = m_currentTable->getColumn(key->m_keyInfoPos);
+ NdbRecAttr* tmp = NdbScanOperation::getValue_impl(col, (char*)-1);
+ UintPtr newVal = UintPtr(tmp);
+ theTupleKeyDefined[i][0] = FAKE_PTR;
+ theTupleKeyDefined[i][1] = (newVal & 0xFFFFFFFF);
+#if (SIZEOF_CHARP == 8)
+ theTupleKeyDefined[i][2] = (newVal >> 32);
+#endif
+ }
+ }
+ m_this_bound_start = 0;
+ m_first_bound_word = theKEYINFOptr;
+
+ return res;
+}
+
+void
+NdbIndexScanOperation::fix_get_values(){
+ /**
+ * Loop through all getValues and set buffer pointer to "API" pointer
+ */
+ NdbRecAttr * curr = theReceiver.theFirstRecAttr;
+ Uint32 cnt = m_accessTable->getNoOfColumns() - 1;
+ assert(cnt < NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY);
+
+ const NdbIndexImpl * idx = m_accessTable->m_index;
+ const NdbTableImpl * tab = m_currentTable;
+ for(Uint32 i = 0; i<cnt; i++){
+ Uint32 val = theTupleKeyDefined[i][0];
+ switch(val){
+ case FAKE_PTR:
+ curr->setup(curr->m_column, 0);
+ case API_PTR:
+ curr = curr->next();
+ break;
+ case SETBOUND_EQ:
+ break;
+#ifdef VM_TRACE
+ default:
+ abort();
+#endif
+ }
+ }
+}
+
+int
+NdbIndexScanOperation::compare(Uint32 skip, Uint32 cols,
+ const NdbReceiver* t1,
+ const NdbReceiver* t2){
+
+ NdbRecAttr * r1 = t1->m_rows[t1->m_current_row];
+ NdbRecAttr * r2 = t2->m_rows[t2->m_current_row];
+
+ r1 = (skip ? r1->next() : r1);
+ r2 = (skip ? r2->next() : r2);
+ const int jdir = 1 - 2 * (int)m_descending;
+ assert(jdir == 1 || jdir == -1);
+ while(cols > 0){
+ Uint32 * d1 = (Uint32*)r1->aRef();
+ Uint32 * d2 = (Uint32*)r2->aRef();
+ unsigned r1_null = r1->isNULL();
+ if((r1_null ^ (unsigned)r2->isNULL())){
+ return (r1_null ? -1 : 1) * jdir;
+ }
+ const NdbColumnImpl & col = NdbColumnImpl::getImpl(* r1->m_column);
+ Uint32 len = r1->theAttrSize * r1->theArraySize;
+ if(!r1_null){
+ const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(col.m_type);
+ int r = (*sqlType.m_cmp)(col.m_cs, d1, len, d2, len, true);
+ if(r){
+ assert(r != NdbSqlUtil::CmpUnknown);
+ return r * jdir;
+ }
+ }
+ cols--;
+ r1 = r1->next();
+ r2 = r2->next();
+ }
+ return 0;
+}
+
+int
+NdbIndexScanOperation::next_result_ordered(bool fetchAllowed,
+ bool forceSend){
+
+ m_curr_row = 0;
+ Uint32 u_idx = 0, u_last = 0;
+ Uint32 s_idx = m_current_api_receiver; // first sorted
+ Uint32 s_last = theParallelism; // last sorted
+
+ NdbReceiver** arr = m_api_receivers;
+ NdbReceiver* tRec = arr[s_idx];
+
+ if(DEBUG_NEXT_RESULT) ndbout_c("nextOrderedResult(%d) nextResult: %d",
+ fetchAllowed,
+ (s_idx < s_last ? tRec->nextResult() : 0));
+
+ if(DEBUG_NEXT_RESULT) ndbout_c("u=[%d %d] s=[%d %d]",
+ u_idx, u_last,
+ s_idx, s_last);
+
+ bool fetchNeeded = (s_idx == s_last) || !tRec->nextResult();
+
+ if(fetchNeeded){
+ if(fetchAllowed){
+ if(DEBUG_NEXT_RESULT) ndbout_c("performing fetch...");
+ TransporterFacade* tp = TransporterFacade::instance();
+ Guard guard(tp->theMutexPtr);
+ if(theError.code)
+ return -1;
+ Uint32 seq = theNdbCon->theNodeSequence;
+ Uint32 nodeId = theNdbCon->theDBnode;
+ if(seq == tp->getNodeSequence(nodeId) &&
+ !send_next_scan_ordered(s_idx, forceSend)){
+ Uint32 tmp = m_sent_receivers_count;
+ s_idx = m_current_api_receiver;
+ while(m_sent_receivers_count > 0 && !theError.code){
+ theNdb->theImpl->theWaiter.m_node = nodeId;
+ theNdb->theImpl->theWaiter.m_state = WAIT_SCAN;
+ int return_code = theNdb->receiveResponse(WAITFOR_SCAN_TIMEOUT);
+ if (return_code == 0 && seq == tp->getNodeSequence(nodeId)) {
+ continue;
+ }
+ if(DEBUG_NEXT_RESULT) ndbout_c("return -1");
+ setErrorCode(4028);
+ return -1;
+ }
+
+ if(theError.code){
+ setErrorCode(theError.code);
+ if(DEBUG_NEXT_RESULT) ndbout_c("return -1");
+ return -1;
+ }
+
+ u_idx = 0;
+ u_last = m_conf_receivers_count;
+ m_conf_receivers_count = 0;
+ memcpy(arr, m_conf_receivers, u_last * sizeof(char*));
+
+ if(DEBUG_NEXT_RESULT) ndbout_c("sent: %d recv: %d", tmp, u_last);
+ } else {
+ setErrorCode(4028);
+ return -1;
+ }
+ } else {
+ if(DEBUG_NEXT_RESULT) ndbout_c("return 2");
+ return 2;
+ }
+ } else {
+ u_idx = s_idx;
+ u_last = s_idx + 1;
+ s_idx++;
+ }
+
+ if(DEBUG_NEXT_RESULT) ndbout_c("u=[%d %d] s=[%d %d]",
+ u_idx, u_last,
+ s_idx, s_last);
+
+
+ Uint32 cols = m_sort_columns + m_read_range_no;
+ Uint32 skip = m_keyInfo;
+ while(u_idx < u_last){
+ u_last--;
+ tRec = arr[u_last];
+
+ // Do binary search instead to find place
+ Uint32 place = s_idx;
+ for(; place < s_last; place++){
+ if(compare(skip, cols, tRec, arr[place]) <= 0){
+ break;
+ }
+ }
+
+ if(place != s_idx){
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("memmove(%d, %d, %d)", s_idx-1, s_idx, (place - s_idx));
+ memmove(arr+s_idx-1, arr+s_idx, sizeof(char*)*(place - s_idx));
+ }
+
+ if(DEBUG_NEXT_RESULT) ndbout_c("putting %d @ %d", u_last, place - 1);
+ m_api_receivers[place-1] = tRec;
+ s_idx--;
+ }
+
+ if(DEBUG_NEXT_RESULT) ndbout_c("u=[%d %d] s=[%d %d]",
+ u_idx, u_last,
+ s_idx, s_last);
+
+ m_current_api_receiver = s_idx;
+
+ if(DEBUG_NEXT_RESULT)
+ for(Uint32 i = s_idx; i<s_last; i++)
+ ndbout_c("%p", arr[i]);
+
+ tRec = m_api_receivers[s_idx];
+ if(s_idx < s_last && tRec->nextResult()){
+ m_curr_row = tRec->copyout(theReceiver);
+ if(DEBUG_NEXT_RESULT) ndbout_c("return 0");
+ return 0;
+ }
+
+ theError.code = -1;
+ if(DEBUG_NEXT_RESULT) ndbout_c("return 1");
+ return 1;
+}
+
+int
+NdbIndexScanOperation::send_next_scan_ordered(Uint32 idx, bool forceSend){
+ if(idx == theParallelism)
+ return 0;
+
+ NdbReceiver* tRec = m_api_receivers[idx];
+ NdbApiSignal tSignal(theNdb->theMyRef);
+ tSignal.setSignal(GSN_SCAN_NEXTREQ);
+
+ Uint32 last = m_sent_receivers_count;
+ Uint32* theData = tSignal.getDataPtrSend();
+ Uint32* prep_array = theData + 4;
+
+ m_current_api_receiver = idx + 1;
+ if((prep_array[0] = tRec->m_tcPtrI) == RNIL)
+ {
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("receiver completed, don't send");
+ return 0;
+ }
+
+ theData[0] = theNdbCon->theTCConPtr;
+ theData[1] = 0;
+ Uint64 transId = theNdbCon->theTransactionId;
+ theData[2] = transId;
+ theData[3] = (Uint32) (transId >> 32);
+
+ /**
+ * Prepare ops
+ */
+ m_sent_receivers[last] = tRec;
+ tRec->m_list_index = last;
+ tRec->prepareSend();
+ m_sent_receivers_count = last + 1;
+
+ Uint32 nodeId = theNdbCon->theDBnode;
+ TransporterFacade * tp = TransporterFacade::instance();
+ tSignal.setLength(4+1);
+ int ret= tp->sendSignal(&tSignal, nodeId);
+ if (!ret) checkForceSend(forceSend);
+ return ret;
+}
+
+int
+NdbScanOperation::close_impl(TransporterFacade* tp, bool forceSend){
+ Uint32 seq = theNdbCon->theNodeSequence;
+ Uint32 nodeId = theNdbCon->theDBnode;
+
+ if(seq != tp->getNodeSequence(nodeId))
+ {
+ theNdbCon->theReleaseOnClose = true;
+ return -1;
+ }
+
+ /**
+ * Wait for outstanding
+ */
+ while(theError.code == 0 && m_sent_receivers_count)
+ {
+ theNdb->theImpl->theWaiter.m_node = nodeId;
+ theNdb->theImpl->theWaiter.m_state = WAIT_SCAN;
+ int return_code = theNdb->receiveResponse(WAITFOR_SCAN_TIMEOUT);
+ switch(return_code){
+ case 0:
+ break;
+ case -1:
+ setErrorCode(4008);
+ case -2:
+ m_api_receivers_count = 0;
+ m_conf_receivers_count = 0;
+ m_sent_receivers_count = 0;
+ theNdbCon->theReleaseOnClose = true;
+ return -1;
+ }
+ }
+
+ if(theError.code)
+ {
+ m_api_receivers_count = 0;
+ m_current_api_receiver = m_ordered ? theParallelism : 0;
+ }
+
+
+ /**
+ * move all conf'ed into api
+ * so that send_next_scan can check if they needs to be closed
+ */
+ Uint32 api = m_api_receivers_count;
+ Uint32 conf = m_conf_receivers_count;
+
+ if(m_ordered)
+ {
+ /**
+ * Ordered scan, keep the m_api_receivers "to the right"
+ */
+ memmove(m_api_receivers, m_api_receivers+m_current_api_receiver,
+ (theParallelism - m_current_api_receiver) * sizeof(char*));
+ api = (theParallelism - m_current_api_receiver);
+ m_api_receivers_count = api;
+ }
+
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("close_impl: [order api conf sent curr parr] %d %d %d %d %d %d",
+ m_ordered, api, conf,
+ m_sent_receivers_count, m_current_api_receiver, theParallelism);
+
+ if(api+conf)
+ {
+ /**
+ * There's something to close
+ * setup m_api_receivers (for send_next_scan)
+ */
+ memcpy(m_api_receivers+api, m_conf_receivers, conf * sizeof(char*));
+ m_api_receivers_count = api + conf;
+ m_conf_receivers_count = 0;
+ }
+
+ // Send close scan
+ if(send_next_scan(api+conf, true, forceSend) == -1)
+ {
+ theNdbCon->theReleaseOnClose = true;
+ return -1;
+ }
+
+ /**
+ * wait for close scan conf
+ */
+ while(m_sent_receivers_count+m_api_receivers_count+m_conf_receivers_count)
+ {
+ theNdb->theImpl->theWaiter.m_node = nodeId;
+ theNdb->theImpl->theWaiter.m_state = WAIT_SCAN;
+ int return_code = theNdb->receiveResponse(WAITFOR_SCAN_TIMEOUT);
+ switch(return_code){
+ case 0:
+ break;
+ case -1:
+ setErrorCode(4008);
+ case -2:
+ m_api_receivers_count = 0;
+ m_conf_receivers_count = 0;
+ m_sent_receivers_count = 0;
+ theNdbCon->theReleaseOnClose = true;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void
+NdbScanOperation::reset_receivers(Uint32 parallell, Uint32 ordered){
+ for(Uint32 i = 0; i<parallell; i++){
+ m_receivers[i]->m_list_index = i;
+ m_prepared_receivers[i] = m_receivers[i]->getId();
+ m_sent_receivers[i] = m_receivers[i];
+ m_conf_receivers[i] = 0;
+ m_api_receivers[i] = 0;
+ m_receivers[i]->prepareSend();
+ }
+
+ m_api_receivers_count = 0;
+ m_current_api_receiver = 0;
+ m_sent_receivers_count = 0;
+ m_conf_receivers_count = 0;
+}
+
+int
+NdbScanOperation::restart(bool forceSend)
+{
+
+ TransporterFacade* tp = TransporterFacade::instance();
+ Guard guard(tp->theMutexPtr);
+ Uint32 nodeId = theNdbCon->theDBnode;
+
+ {
+ int res;
+ if((res= close_impl(tp, forceSend)))
+ {
+ return res;
+ }
+ }
+
+ /**
+ * Reset receivers
+ */
+ reset_receivers(theParallelism, m_ordered);
+
+ theError.code = 0;
+ if (doSendScan(nodeId) == -1)
+ return -1;
+
+ return 0;
+}
+
+int
+NdbIndexScanOperation::reset_bounds(bool forceSend){
+ int res;
+
+ {
+ TransporterFacade* tp = TransporterFacade::instance();
+ Guard guard(tp->theMutexPtr);
+ res= close_impl(tp, forceSend);
+ }
+
+ if(!res)
+ {
+ theError.code = 0;
+ reset_receivers(theParallelism, m_ordered);
+
+ theLastKEYINFO = theSCAN_TABREQ->next();
+ theKEYINFOptr = ((KeyInfo*)theLastKEYINFO->getDataPtrSend())->keyData;
+ theTupKeyLen = 0;
+ theTotalNrOfKeyWordInSignal = 0;
+ theNoOfTupKeyLeft = m_accessTable->m_noOfDistributionKeys;
+ theDistrKeyIndicator_ = 0;
+ m_this_bound_start = 0;
+ m_first_bound_word = theKEYINFOptr;
+ m_transConnection
+ ->remove_list((NdbOperation*&)m_transConnection->m_firstExecutedScanOp,
+ this);
+ m_transConnection->define_scan_op(this);
+ return 0;
+ }
+ return res;
+}
+
+int
+NdbIndexScanOperation::end_of_bound(Uint32 no)
+{
+ if(no < (1 << 13)) // Only 12-bits no of ranges
+ {
+ Uint32 bound_head = * m_first_bound_word;
+ bound_head |= (theTupKeyLen - m_this_bound_start) << 16 | (no << 4);
+ * m_first_bound_word = bound_head;
+
+ m_first_bound_word = theKEYINFOptr + theTotalNrOfKeyWordInSignal;;
+ m_this_bound_start = theTupKeyLen;
+ return 0;
+ }
+ return -1;
+}
+
+int
+NdbIndexScanOperation::get_range_no()
+{
+ NdbRecAttr* tRecAttr = m_curr_row;
+ if(m_read_range_no && tRecAttr)
+ {
+ if(m_keyInfo)
+ tRecAttr = tRecAttr->next();
+ Uint32 ret = *(Uint32*)tRecAttr->aRef();
+ return ret;
+ }
+ return -1;
+}
diff --git a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp
new file mode 100644
index 00000000000..67581e4a0f8
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp
@@ -0,0 +1,2124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include <NdbTransaction.hpp>
+#include <NdbOperation.hpp>
+#include <NdbScanOperation.hpp>
+#include <NdbIndexScanOperation.hpp>
+#include <NdbIndexOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include "TransporterFacade.hpp"
+#include "API.hpp"
+#include "NdbBlob.hpp"
+#include <ndb_limits.h>
+
+#include <signaldata/TcKeyConf.hpp>
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/TcCommit.hpp>
+#include <signaldata/TcKeyFailConf.hpp>
+#include <signaldata/TcHbRep.hpp>
+
+/*****************************************************************************
+NdbTransaction( Ndb* aNdb );
+
+Return Value: None
+Parameters: aNdb: Pointers to the Ndb object
+Remark: Creates a connection object.
+*****************************************************************************/
+NdbTransaction::NdbTransaction( Ndb* aNdb ) :
+ theSendStatus(NotInit),
+ theCallbackFunction(NULL),
+ theCallbackObject(NULL),
+ theTransArrayIndex(0),
+ theStartTransTime(0),
+ theErrorLine(0),
+ theErrorOperation(NULL),
+ theNdb(aNdb),
+ theNext(NULL),
+ theFirstOpInList(NULL),
+ theLastOpInList(NULL),
+ theFirstExecOpInList(NULL),
+ theLastExecOpInList(NULL),
+ theCompletedFirstOp(NULL),
+ theCompletedLastOp(NULL),
+ theNoOfOpSent(0),
+ theNoOfOpCompleted(0),
+ theNoOfOpFetched(0),
+ theMyRef(0),
+ theTCConPtr(0),
+ theTransactionId(0),
+ theGlobalCheckpointId(0),
+ theStatus(NotConnected),
+ theCompletionStatus(NotCompleted),
+ theCommitStatus(NotStarted),
+ theMagicNumber(0xFE11DC),
+ theTransactionIsStarted(false),
+ theDBnode(0),
+ theReleaseOnClose(false),
+ // Scan operations
+ m_waitForReply(true),
+ m_theFirstScanOperation(NULL),
+ m_theLastScanOperation(NULL),
+ m_firstExecutedScanOp(NULL),
+ // Scan operations
+ theScanningOp(NULL),
+ theBuddyConPtr(0xFFFFFFFF),
+ theBlobFlag(false),
+ thePendingBlobOps(0)
+{
+ theListState = NotInList;
+ theError.code = 0;
+ theId = theNdb->theImpl->theNdbObjectIdMap.map(this);
+
+#define CHECK_SZ(mask, sz) assert((sizeof(mask)/sizeof(mask[0])) == sz)
+
+ CHECK_SZ(m_db_nodes, NdbNodeBitmask::Size);
+ CHECK_SZ(m_failed_db_nodes, NdbNodeBitmask::Size);
+}//NdbTransaction::NdbTransaction()
+
+/*****************************************************************************
+~NdbTransaction();
+
+Remark: Deletes the connection object.
+*****************************************************************************/
+NdbTransaction::~NdbTransaction()
+{
+ DBUG_ENTER("NdbTransaction::~NdbTransaction");
+ theNdb->theImpl->theNdbObjectIdMap.unmap(theId, this);
+ DBUG_VOID_RETURN;
+}//NdbTransaction::~NdbTransaction()
+
+/*****************************************************************************
+void init();
+
+Remark: Initialise connection object for new transaction.
+*****************************************************************************/
+void
+NdbTransaction::init()
+{
+ theListState = NotInList;
+ theInUseState = true;
+ theTransactionIsStarted = false;
+ theNext = NULL;
+
+ theFirstOpInList = NULL;
+ theLastOpInList = NULL;
+
+ theScanningOp = NULL;
+
+ theFirstExecOpInList = NULL;
+ theLastExecOpInList = NULL;
+
+ theCompletedFirstOp = NULL;
+ theCompletedLastOp = NULL;
+
+ theGlobalCheckpointId = 0;
+ theCommitStatus = Started;
+ theCompletionStatus = NotCompleted;
+ m_abortOption = AbortOnError;
+
+ theError.code = 0;
+ theErrorLine = 0;
+ theErrorOperation = NULL;
+
+ theReleaseOnClose = false;
+ theSimpleState = true;
+ theSendStatus = InitState;
+ theMagicNumber = 0x37412619;
+ // Scan operations
+ m_waitForReply = true;
+ m_theFirstScanOperation = NULL;
+ m_theLastScanOperation = NULL;
+ m_firstExecutedScanOp = 0;
+ theBuddyConPtr = 0xFFFFFFFF;
+ //
+ theBlobFlag = false;
+ thePendingBlobOps = 0;
+}//NdbTransaction::init()
+
+/*****************************************************************************
+setOperationErrorCode(int error);
+
+Remark: Sets an error code on the connection object from an
+ operation object.
+*****************************************************************************/
+void
+NdbTransaction::setOperationErrorCode(int error)
+{
+ DBUG_ENTER("NdbTransaction::setOperationErrorCode");
+ setErrorCode(error);
+ DBUG_VOID_RETURN;
+}
+
+/*****************************************************************************
+setOperationErrorCodeAbort(int error);
+
+Remark: Sets an error code on the connection object from an
+ operation object.
+*****************************************************************************/
+void
+NdbTransaction::setOperationErrorCodeAbort(int error, int abortOption)
+{
+ DBUG_ENTER("NdbTransaction::setOperationErrorCodeAbort");
+ if (abortOption == -1)
+ abortOption = m_abortOption;
+ if (theTransactionIsStarted == false) {
+ theCommitStatus = Aborted;
+ } else if ((abortOption == AbortOnError) &&
+ (theCommitStatus != Committed) &&
+ (theCommitStatus != Aborted)) {
+ theCommitStatus = NeedAbort;
+ }//if
+ setErrorCode(error);
+ DBUG_VOID_RETURN;
+}
+
+/*****************************************************************************
+setErrorCode(int anErrorCode);
+
+Remark: Sets an error indication on the connection object.
+*****************************************************************************/
+void
+NdbTransaction::setErrorCode(int error)
+{
+ DBUG_ENTER("NdbTransaction::setErrorCode");
+ DBUG_PRINT("enter", ("error: %d, theError.code: %d", error, theError.code));
+
+ if (theError.code == 0)
+ theError.code = error;
+
+ DBUG_VOID_RETURN;
+}//NdbTransaction::setErrorCode()
+
+int
+NdbTransaction::restart(){
+ DBUG_ENTER("NdbTransaction::restart");
+ if(theCompletionStatus == CompletedSuccess){
+ releaseCompletedOperations();
+ Uint64 tTransid = theNdb->theFirstTransId;
+ theTransactionId = tTransid;
+ if ((tTransid & 0xFFFFFFFF) == 0xFFFFFFFF) {
+ theNdb->theFirstTransId = (tTransid >> 32) << 32;
+ } else {
+ theNdb->theFirstTransId = tTransid + 1;
+ }
+ theCommitStatus = Started;
+ theCompletionStatus = NotCompleted;
+ theTransactionIsStarted = false;
+ DBUG_RETURN(0);
+ }
+ DBUG_PRINT("error",("theCompletionStatus != CompletedSuccess"));
+ DBUG_RETURN(-1);
+}
+
+/*****************************************************************************
+void handleExecuteCompletion(void);
+
+Remark: Handle time-out on a transaction object.
+*****************************************************************************/
+void
+NdbTransaction::handleExecuteCompletion()
+{
+ /***************************************************************************
+ * Move the NdbOperation objects from the list of executing
+ * operations to list of completed
+ **************************************************************************/
+ NdbOperation* tFirstExecOp = theFirstExecOpInList;
+ NdbOperation* tLastExecOp = theLastExecOpInList;
+ if (tLastExecOp != NULL) {
+ tLastExecOp->next(theCompletedFirstOp);
+ theCompletedFirstOp = tFirstExecOp;
+ if (theCompletedLastOp == NULL)
+ theCompletedLastOp = tLastExecOp;
+ theFirstExecOpInList = NULL;
+ theLastExecOpInList = NULL;
+ }//if
+ theSendStatus = InitState;
+ return;
+}//NdbTransaction::handleExecuteCompletion()
+
+/*****************************************************************************
+int execute(ExecType aTypeOfExec, CommitType aTypeOfCommit, int forceSend);
+
+Return Value: Return 0 : execute was successful.
+ Return -1: In all other case.
+Parameters : aTypeOfExec: Type of execute.
+Remark: Initialise connection object for new transaction.
+*****************************************************************************/
+int
+NdbTransaction::execute(ExecType aTypeOfExec,
+ AbortOption abortOption,
+ int forceSend)
+{
+ DBUG_ENTER("NdbTransaction::execute");
+ DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d",
+ aTypeOfExec, abortOption));
+
+ if (! theBlobFlag)
+ DBUG_RETURN(executeNoBlobs(aTypeOfExec, abortOption, forceSend));
+
+ /*
+ * execute prepared ops in batches, as requested by blobs
+ * - blob error does not terminate execution
+ * - blob error sets error on operation
+ * - if error on operation skip blob calls
+ */
+
+ ExecType tExecType;
+ NdbOperation* tPrepOp;
+ NdbOperation* tCompletedFirstOp = NULL;
+ NdbOperation* tCompletedLastOp = NULL;
+
+ int ret = 0;
+ do {
+ tExecType = aTypeOfExec;
+ tPrepOp = theFirstOpInList;
+ while (tPrepOp != NULL) {
+ if (tPrepOp->theError.code == 0) {
+ bool batch = false;
+ NdbBlob* tBlob = tPrepOp->theBlobList;
+ while (tBlob != NULL) {
+ if (tBlob->preExecute(tExecType, batch) == -1)
+ ret = -1;
+ tBlob = tBlob->theNext;
+ }
+ if (batch) {
+ // blob asked to execute all up to here now
+ tExecType = NoCommit;
+ break;
+ }
+ }
+ tPrepOp = tPrepOp->next();
+ }
+
+ // save rest of prepared ops if batch
+ NdbOperation* tRestOp= 0;
+ NdbOperation* tLastOp= 0;
+ if (tPrepOp != NULL) {
+ tRestOp = tPrepOp->next();
+ tPrepOp->next(NULL);
+ tLastOp = theLastOpInList;
+ theLastOpInList = tPrepOp;
+ }
+
+ if (tExecType == Commit) {
+ NdbOperation* tOp = theCompletedFirstOp;
+ while (tOp != NULL) {
+ if (tOp->theError.code == 0) {
+ NdbBlob* tBlob = tOp->theBlobList;
+ while (tBlob != NULL) {
+ if (tBlob->preCommit() == -1)
+ ret = -1;
+ tBlob = tBlob->theNext;
+ }
+ }
+ tOp = tOp->next();
+ }
+ }
+
+ // completed ops are in unspecified order
+ if (theCompletedFirstOp != NULL) {
+ if (tCompletedFirstOp == NULL) {
+ tCompletedFirstOp = theCompletedFirstOp;
+ tCompletedLastOp = theCompletedLastOp;
+ } else {
+ tCompletedLastOp->next(theCompletedFirstOp);
+ tCompletedLastOp = theCompletedLastOp;
+ }
+ theCompletedFirstOp = NULL;
+ theCompletedLastOp = NULL;
+ }
+
+ if (executeNoBlobs(tExecType, abortOption, forceSend) == -1)
+ ret = -1;
+#ifdef ndb_api_crash_on_complex_blob_abort
+ assert(theFirstOpInList == NULL && theLastOpInList == NULL);
+#else
+ theFirstOpInList = theLastOpInList = NULL;
+#endif
+
+ {
+ NdbOperation* tOp = theCompletedFirstOp;
+ while (tOp != NULL) {
+ if (tOp->theError.code == 0) {
+ NdbBlob* tBlob = tOp->theBlobList;
+ while (tBlob != NULL) {
+ // may add new operations if batch
+ if (tBlob->postExecute(tExecType) == -1)
+ ret = -1;
+ tBlob = tBlob->theNext;
+ }
+ }
+ tOp = tOp->next();
+ }
+ }
+
+ // add saved prepared ops if batch
+ if (tPrepOp != NULL && tRestOp != NULL) {
+ if (theFirstOpInList == NULL)
+ theFirstOpInList = tRestOp;
+ else
+ theLastOpInList->next(tRestOp);
+ theLastOpInList = tLastOp;
+ }
+ assert(theFirstOpInList == NULL || tExecType == NoCommit);
+ } while (theFirstOpInList != NULL || tExecType != aTypeOfExec);
+
+ if (tCompletedFirstOp != NULL) {
+ tCompletedLastOp->next(theCompletedFirstOp);
+ theCompletedFirstOp = tCompletedFirstOp;
+ if (theCompletedLastOp == NULL)
+ theCompletedLastOp = tCompletedLastOp;
+ }
+#if ndb_api_count_completed_ops_after_blob_execute
+ { NdbOperation* tOp; unsigned n = 0;
+ for (tOp = theCompletedFirstOp; tOp != NULL; tOp = tOp->next()) n++;
+ ndbout << "completed ops: " << n << endl;
+ }
+#endif
+ DBUG_RETURN(ret);
+}
+
+int
+NdbTransaction::executeNoBlobs(ExecType aTypeOfExec,
+ AbortOption abortOption,
+ int forceSend)
+{
+ DBUG_ENTER("NdbTransaction::executeNoBlobs");
+ DBUG_PRINT("enter", ("aTypeOfExec: %d, abortOption: %d",
+ aTypeOfExec, abortOption));
+
+//------------------------------------------------------------------------
+// We will start by preparing all operations in the transaction defined
+// since last execute or since beginning. If this works ok we will continue
+// by calling the poll with wait method. This method will return when
+// the NDB kernel has completed its task or when 10 seconds have passed.
+// The NdbTransactionCallBack-method will receive the return code of the
+// transaction. The normal methods of reading error codes still apply.
+//------------------------------------------------------------------------
+ Ndb* tNdb = theNdb;
+
+ m_waitForReply = false;
+ executeAsynchPrepare(aTypeOfExec, NULL, NULL, abortOption);
+ if (m_waitForReply){
+ while (1) {
+ int noOfComp = tNdb->sendPollNdb((3 * WAITFOR_RESPONSE_TIMEOUT),
+ 1, forceSend);
+ if (noOfComp == 0) {
+ /**
+ * This timeout situation can occur if NDB crashes.
+ */
+ ndbout << "This timeout should never occur, execute(..)" << endl;
+ setOperationErrorCodeAbort(4012); // Error code for "Cluster Failure"
+ DBUG_RETURN(-1);
+ }//if
+
+ /*
+ * Check that the completed transactions include this one. There
+ * could be another thread running asynchronously. Even in pure
+ * async case rollback is done synchronously.
+ */
+ if (theListState != NotInList)
+ continue;
+#ifdef VM_TRACE
+ unsigned anyway = 0;
+ for (unsigned i = 0; i < theNdb->theNoOfPreparedTransactions; i++)
+ anyway += theNdb->thePreparedTransactionsArray[i] == this;
+ for (unsigned i = 0; i < theNdb->theNoOfSentTransactions; i++)
+ anyway += theNdb->theSentTransactionsArray[i] == this;
+ for (unsigned i = 0; i < theNdb->theNoOfCompletedTransactions; i++)
+ anyway += theNdb->theCompletedTransactionsArray[i] == this;
+ if (anyway) {
+ theNdb->printState("execute %x", this);
+ abort();
+ }
+#endif
+ if (theReturnStatus == ReturnFailure) {
+ DBUG_RETURN(-1);
+ }//if
+ break;
+ }
+ }
+ thePendingBlobOps = 0;
+ DBUG_RETURN(0);
+}//NdbTransaction::execute()
+
+/*****************************************************************************
+void executeAsynchPrepare(ExecType aTypeOfExec,
+ NdbAsynchCallback callBack,
+ void* anyObject,
+ CommitType aTypeOfCommit);
+
+Return Value: No return value
+Parameters : aTypeOfExec: Type of execute.
+ anyObject: An object provided in the callback method
+ callBack: The callback method
+ aTypeOfCommit: What to do when read/updated/deleted records
+ are missing or inserted records already exist.
+
+Remark: Prepare a part of a transaction in an asynchronous manner.
+*****************************************************************************/
+void
+NdbTransaction::executeAsynchPrepare( ExecType aTypeOfExec,
+ NdbAsynchCallback aCallback,
+ void* anyObject,
+ AbortOption abortOption)
+{
+ DBUG_ENTER("NdbTransaction::executeAsynchPrepare");
+ DBUG_PRINT("enter", ("aTypeOfExec: %d, aCallback: %x, anyObject: %x",
+ aTypeOfExec, aCallback, anyObject));
+
+ /**
+ * Reset error.code on execute
+ */
+ if (theError.code != 0)
+ DBUG_PRINT("enter", ("Resetting error %d on execute", theError.code));
+ theError.code = 0;
+ NdbScanOperation* tcOp = m_theFirstScanOperation;
+ if (tcOp != 0){
+ // Execute any cursor operations
+ while (tcOp != NULL) {
+ int tReturnCode;
+ tReturnCode = tcOp->executeCursor(theDBnode);
+ if (tReturnCode == -1) {
+ DBUG_VOID_RETURN;
+ }//if
+ tcOp = (NdbScanOperation*)tcOp->next();
+ } // while
+ m_theLastScanOperation->next(m_firstExecutedScanOp);
+ m_firstExecutedScanOp = m_theFirstScanOperation;
+ // Discard cursor operations, since these are also
+ // in the complete operations list we do not need
+ // to release them.
+ m_theFirstScanOperation = m_theLastScanOperation = NULL;
+ }
+
+ bool tTransactionIsStarted = theTransactionIsStarted;
+ NdbOperation* tLastOp = theLastOpInList;
+ Ndb* tNdb = theNdb;
+ CommitStatusType tCommitStatus = theCommitStatus;
+ Uint32 tnoOfPreparedTransactions = tNdb->theNoOfPreparedTransactions;
+
+ theReturnStatus = ReturnSuccess;
+ theCallbackFunction = aCallback;
+ theCallbackObject = anyObject;
+ m_abortOption = abortOption;
+ m_waitForReply = true;
+ tNdb->thePreparedTransactionsArray[tnoOfPreparedTransactions] = this;
+ theTransArrayIndex = tnoOfPreparedTransactions;
+ theListState = InPreparedList;
+ tNdb->theNoOfPreparedTransactions = tnoOfPreparedTransactions + 1;
+
+ if ((tCommitStatus != Started) ||
+ (aTypeOfExec == Rollback)) {
+/*****************************************************************************
+ * Rollback have been ordered on a started transaction. Call rollback.
+ * Could also be state problem or previous problem which leads to the
+ * same action.
+ ****************************************************************************/
+ if (aTypeOfExec == Rollback) {
+ if (theTransactionIsStarted == false || theSimpleState) {
+ theCommitStatus = Aborted;
+ theSendStatus = sendCompleted;
+ } else {
+ theSendStatus = sendABORT;
+ }
+ } else {
+ theSendStatus = sendABORTfail;
+ }//if
+ if (theCommitStatus == Aborted){
+ DBUG_PRINT("exit", ("theCommitStatus: Aborted"));
+ setErrorCode(4350);
+ }
+ DBUG_VOID_RETURN;
+ }//if
+ if (tTransactionIsStarted == true) {
+ if (tLastOp != NULL) {
+ if (aTypeOfExec == Commit) {
+/*****************************************************************************
+ * Set commit indicator on last operation when commit has been ordered
+ * and also a number of operations.
+******************************************************************************/
+ tLastOp->theCommitIndicator = 1;
+ }//if
+ } else {
+ if (aTypeOfExec == Commit && !theSimpleState) {
+ /**********************************************************************
+ * A Transaction have been started and no more operations exist.
+ * We will use the commit method.
+ *********************************************************************/
+ theSendStatus = sendCOMMITstate;
+ DBUG_VOID_RETURN;
+ } else {
+ /**********************************************************************
+ * We need to put it into the array of completed transactions to
+ * ensure that we report the completion in a proper way.
+ * We cannot do this here since that would endanger the completed
+ * transaction array since that is also updated from the receiver
+ * thread and thus we need to do it under mutex lock and thus we
+ * set the sendStatus to ensure that the send method will
+ * put it into the completed array.
+ **********************************************************************/
+ theSendStatus = sendCompleted;
+ DBUG_VOID_RETURN; // No Commit with no operations is OK
+ }//if
+ }//if
+ } else if (tTransactionIsStarted == false) {
+ NdbOperation* tFirstOp = theFirstOpInList;
+ if (tLastOp != NULL) {
+ tFirstOp->setStartIndicator();
+ if (aTypeOfExec == Commit) {
+ tLastOp->theCommitIndicator = 1;
+ }//if
+ } else {
+ /***********************************************************************
+ * No operations are defined and we have not started yet.
+ * Simply return OK. Set commit status if Commit.
+ ***********************************************************************/
+ if (aTypeOfExec == Commit) {
+ theCommitStatus = Committed;
+ }//if
+ /***********************************************************************
+ * We need to put it into the array of completed transactions to
+ * ensure that we report the completion in a proper way. We
+ * cannot do this here since that would endanger the completed
+ * transaction array since that is also updated from the
+ * receiver thread and thus we need to do it under mutex lock
+ * and thus we set the sendStatus to ensure that the send method
+ * will put it into the completed array.
+ ***********************************************************************/
+ theSendStatus = sendCompleted;
+ DBUG_VOID_RETURN;
+ }//if
+ }
+
+ NdbOperation* tOp = theFirstOpInList;
+ theCompletionStatus = NotCompleted;
+ while (tOp) {
+ int tReturnCode;
+ NdbOperation* tNextOp = tOp->next();
+
+ tReturnCode = tOp->prepareSend(theTCConPtr, theTransactionId);
+ if (tReturnCode == -1) {
+ theSendStatus = sendABORTfail;
+ DBUG_VOID_RETURN;
+ }//if
+
+ /*************************************************************************
+ * Now that we have successfully prepared the send of this operation we
+ * move it to the list of executing operations and remove it from the
+ * list of defined operations.
+ ************************************************************************/
+ tOp = tNextOp;
+ }
+
+ NdbOperation* tLastOpInList = theLastOpInList;
+ NdbOperation* tFirstOpInList = theFirstOpInList;
+
+ theFirstOpInList = NULL;
+ theLastOpInList = NULL;
+ theFirstExecOpInList = tFirstOpInList;
+ theLastExecOpInList = tLastOpInList;
+
+ theCompletionStatus = CompletedSuccess;
+ theNoOfOpSent = 0;
+ theNoOfOpCompleted = 0;
+ theSendStatus = sendOperations;
+ NdbNodeBitmask::clear(m_db_nodes);
+ NdbNodeBitmask::clear(m_failed_db_nodes);
+ DBUG_VOID_RETURN;
+}//NdbTransaction::executeAsynchPrepare()
+
+void NdbTransaction::close()
+{
+ theNdb->closeTransaction(this);
+}
+
+int NdbTransaction::refresh(){
+ return sendTC_HBREP();
+}
+
+/*****************************************************************************
+int sendTC_HBREP();
+
+Return Value: No return value.
+Parameters : None.
+Remark: Order NDB to refresh the timeout counter of the transaction.
+******************************************************************************/
+int
+NdbTransaction::sendTC_HBREP() // Send a TC_HBREP signal;
+{
+ NdbApiSignal* tSignal;
+ Ndb* tNdb = theNdb;
+ Uint32 tTransId1, tTransId2;
+
+ tSignal = tNdb->getSignal();
+ if (tSignal == NULL) {
+ return -1;
+ }
+
+ if (tSignal->setSignal(GSN_TC_HBREP) == -1) {
+ return -1;
+ }
+
+ TcHbRep * const tcHbRep = CAST_PTR(TcHbRep, tSignal->getDataPtrSend());
+
+ tcHbRep->apiConnectPtr = theTCConPtr;
+
+ tTransId1 = (Uint32) theTransactionId;
+ tTransId2 = (Uint32) (theTransactionId >> 32);
+ tcHbRep->transId1 = tTransId1;
+ tcHbRep->transId2 = tTransId2;
+
+ TransporterFacade *tp = TransporterFacade::instance();
+ tp->lock_mutex();
+ const int res = tp->sendSignal(tSignal,theDBnode);
+ tp->unlock_mutex();
+ tNdb->releaseSignal(tSignal);
+
+ if (res == -1){
+ return -1;
+ }
+
+ return 0;
+}//NdbTransaction::sendTC_HBREP()
+
+/*****************************************************************************
+int doSend();
+
+Return Value: Return 0 : send was successful.
+ Return -1: In all other case.
+Remark: Send all operations belonging to this connection.
+ The caller of this method has the responsibility to remove the
+ object from the prepared transactions array on the Ndb-object.
+*****************************************************************************/
+int
+NdbTransaction::doSend()
+{
+ DBUG_ENTER("NdbTransaction::doSend");
+
+ /*
+ This method assumes that at least one operation have been defined. This
+ is ensured by the caller of this routine (=execute).
+ */
+
+ switch(theSendStatus){
+ case sendOperations: {
+ NdbOperation * tOp = theFirstExecOpInList;
+ do {
+ NdbOperation* tNextOp = tOp->next();
+ const Uint32 lastFlag = ((tNextOp == NULL) ? 1 : 0);
+ const int tReturnCode = tOp->doSend(theDBnode, lastFlag);
+ if (tReturnCode == -1) {
+ theReturnStatus = ReturnFailure;
+ break;
+ }//if
+ tOp = tNextOp;
+ } while (tOp != NULL);
+ Ndb* tNdb = theNdb;
+ theSendStatus = sendTC_OP;
+ theTransactionIsStarted = true;
+ tNdb->insert_sent_list(this);
+ DBUG_RETURN(0);
+ }//case
+ case sendABORT:
+ case sendABORTfail:{
+ /***********************************************************************
+ * Rollback have been ordered on a not started transaction.
+ * Simply return OK and set abort status.
+ ***********************************************************************/
+ if (theSendStatus == sendABORTfail) {
+ theReturnStatus = ReturnFailure;
+ }//if
+ if (sendROLLBACK() == 0) {
+ DBUG_RETURN(0);
+ }//if
+ break;
+ }//case
+ case sendCOMMITstate:
+ if (sendCOMMIT() == 0) {
+ DBUG_RETURN(0);
+ }//if
+ break;
+ case sendCompleted:
+ theNdb->insert_completed_list(this);
+ DBUG_RETURN(0);
+ default:
+ ndbout << "Inconsistent theSendStatus = "
+ << (Uint32) theSendStatus << endl;
+ abort();
+ break;
+ }//switch
+ setOperationErrorCodeAbort(4002);
+ theReleaseOnClose = true;
+ theTransactionIsStarted = false;
+ theCommitStatus = Aborted;
+ DBUG_RETURN(-1);
+}//NdbTransaction::doSend()
+
+/**************************************************************************
+int sendROLLBACK();
+
+Return Value: Return -1 if send unsuccessful.
+Parameters : None.
+Remark: Order NDB to rollback the transaction.
+**************************************************************************/
+int
+NdbTransaction::sendROLLBACK() // Send a TCROLLBACKREQ signal;
+{
+ Ndb* tNdb = theNdb;
+ if ((theTransactionIsStarted == true) &&
+ (theCommitStatus != Committed) &&
+ (theCommitStatus != Aborted)) {
+/**************************************************************************
+ * The user did not perform any rollback but simply closed the
+ * transaction. We must rollback Ndb since Ndb have been contacted.
+ *************************************************************************/
+ NdbApiSignal tSignal(tNdb->theMyRef);
+ Uint32 tTransId1, tTransId2;
+ TransporterFacade *tp = TransporterFacade::instance();
+ int tReturnCode;
+
+ tTransId1 = (Uint32) theTransactionId;
+ tTransId2 = (Uint32) (theTransactionId >> 32);
+ tSignal.setSignal(GSN_TCROLLBACKREQ);
+ tSignal.setData(theTCConPtr, 1);
+ tSignal.setData(tTransId1, 2);
+ tSignal.setData(tTransId2, 3);
+ tReturnCode = tp->sendSignal(&tSignal,theDBnode);
+ if (tReturnCode != -1) {
+ theSendStatus = sendTC_ROLLBACK;
+ tNdb->insert_sent_list(this);
+ return 0;
+ }//if
+ /*********************************************************************
+ * It was not possible to abort the transaction towards the NDB kernel
+ * and thus we put it into the array of completed transactions that
+ * are ready for reporting to the application.
+ *********************************************************************/
+ return -1;
+ } else {
+ /*
+ It is not necessary to abort the transaction towards the NDB kernel and
+ thus we put it into the array of completed transactions that are ready
+ for reporting to the application.
+ */
+ theSendStatus = sendCompleted;
+ tNdb->insert_completed_list(this);
+ return 0;
+ ;
+ }//if
+}//NdbTransaction::sendROLLBACK()
+
+/***************************************************************************
+int sendCOMMIT();
+
+Return Value: Return 0 : send was successful.
+ Return -1: In all other case.
+Parameters : None.
+Remark: Order NDB to commit the transaction.
+***************************************************************************/
+int
+NdbTransaction::sendCOMMIT() // Send a TC_COMMITREQ signal;
+{
+ NdbApiSignal tSignal(theNdb->theMyRef);
+ Uint32 tTransId1, tTransId2;
+ TransporterFacade *tp = TransporterFacade::instance();
+ int tReturnCode;
+
+ tTransId1 = (Uint32) theTransactionId;
+ tTransId2 = (Uint32) (theTransactionId >> 32);
+ tSignal.setSignal(GSN_TC_COMMITREQ);
+ tSignal.setData(theTCConPtr, 1);
+ tSignal.setData(tTransId1, 2);
+ tSignal.setData(tTransId2, 3);
+
+ tReturnCode = tp->sendSignal(&tSignal,theDBnode);
+ if (tReturnCode != -1) {
+ theSendStatus = sendTC_COMMIT;
+ theNdb->insert_sent_list(this);
+ return 0;
+ } else {
+ return -1;
+ }//if
+}//NdbTransaction::sendCOMMIT()
+
+/******************************************************************************
+void release();
+
+Remark: Release all operations.
+******************************************************************************/
+void
+NdbTransaction::release(){
+ releaseOperations();
+ if ( (theTransactionIsStarted == true) &&
+ ((theCommitStatus != Committed) &&
+ (theCommitStatus != Aborted))) {
+ /************************************************************************
+ * The user did not perform any rollback but simply closed the
+ * transaction. We must rollback Ndb since Ndb have been contacted.
+ ************************************************************************/
+ execute(Rollback);
+ }//if
+ theMagicNumber = 0xFE11DC;
+ theInUseState = false;
+#ifdef VM_TRACE
+ if (theListState != NotInList) {
+ theNdb->printState("release %x", this);
+ abort();
+ }
+#endif
+}//NdbTransaction::release()
+
+void
+NdbTransaction::releaseOps(NdbOperation* tOp){
+ while (tOp != NULL) {
+ NdbOperation* tmp = tOp;
+ tOp->release();
+ tOp = tOp->next();
+ theNdb->releaseOperation(tmp);
+ }//while
+}
+
+/******************************************************************************
+void releaseOperations();
+
+Remark: Release all operations.
+******************************************************************************/
+void
+NdbTransaction::releaseOperations()
+{
+ // Release any open scans
+ releaseScanOperations(m_theFirstScanOperation);
+ releaseScanOperations(m_firstExecutedScanOp);
+
+ releaseOps(theCompletedFirstOp);
+ releaseOps(theFirstOpInList);
+ releaseOps(theFirstExecOpInList);
+
+ theCompletedFirstOp = NULL;
+ theCompletedLastOp = NULL;
+ theFirstOpInList = NULL;
+ theFirstExecOpInList = NULL;
+ theLastOpInList = NULL;
+ theLastExecOpInList = NULL;
+ theScanningOp = NULL;
+ m_theFirstScanOperation = NULL;
+ m_theLastScanOperation = NULL;
+ m_firstExecutedScanOp = NULL;
+}//NdbTransaction::releaseOperations()
+
+void
+NdbTransaction::releaseCompletedOperations()
+{
+ releaseOps(theCompletedFirstOp);
+ theCompletedFirstOp = NULL;
+ theCompletedLastOp = NULL;
+}//NdbTransaction::releaseOperations()
+
+/******************************************************************************
+void releaseScanOperations();
+
+Remark: Release all cursor operations.
+ (NdbScanOperation and NdbIndexOperation)
+******************************************************************************/
+void
+NdbTransaction::releaseScanOperations(NdbIndexScanOperation* cursorOp)
+{
+ while(cursorOp != 0){
+ NdbIndexScanOperation* next = (NdbIndexScanOperation*)cursorOp->next();
+ cursorOp->release();
+ theNdb->releaseScanOperation(cursorOp);
+ cursorOp = next;
+ }
+}//NdbTransaction::releaseScanOperations()
+
+/*****************************************************************************
+NdbOperation* getNdbOperation(const char* aTableName);
+
+Return Value Return a pointer to a NdbOperation object if getNdbOperation
+ was succesful.
+ Return NULL : In all other case.
+Parameters: aTableName : Name of the database table.
+Remark: Get an operation from NdbOperation idlelist and get the
+ NdbTransaction object
+ who was fetch by startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbOperation object.
+ synchronous
+******************************************************************************/
+NdbOperation*
+NdbTransaction::getNdbOperation(const char* aTableName)
+{
+ if (theCommitStatus == Started){
+ NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName);
+ if (table != 0){
+ return getNdbOperation(table);
+ } else {
+ setErrorCode(theNdb->theDictionary->getNdbError().code);
+ return NULL;
+ }//if
+ }
+
+ setOperationErrorCodeAbort(4114);
+
+ return NULL;
+}//NdbTransaction::getNdbOperation()
+
+/*****************************************************************************
+NdbOperation* getNdbOperation(int aTableId);
+
+Return Value Return a pointer to a NdbOperation object if getNdbOperation
+ was succesful.
+ Return NULL: In all other case.
+Parameters: tableId : Id of the database table beeing deleted.
+Remark: Get an operation from NdbOperation object idlelist and
+ get the NdbTransaction object who was fetch by
+ startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbOperation
+ object, synchronous.
+*****************************************************************************/
+NdbOperation*
+NdbTransaction::getNdbOperation(const NdbTableImpl * tab, NdbOperation* aNextOp)
+{
+ NdbOperation* tOp;
+
+ if (theScanningOp != NULL){
+ setErrorCode(4607);
+ return NULL;
+ }
+
+ tOp = theNdb->getOperation();
+ if (tOp == NULL)
+ goto getNdbOp_error1;
+ if (aNextOp == NULL) {
+ if (theLastOpInList != NULL) {
+ theLastOpInList->next(tOp);
+ theLastOpInList = tOp;
+ } else {
+ theLastOpInList = tOp;
+ theFirstOpInList = tOp;
+ }//if
+ tOp->next(NULL);
+ } else {
+ // add before the given op
+ if (theFirstOpInList == aNextOp) {
+ theFirstOpInList = tOp;
+ } else {
+ NdbOperation* aLoopOp = theFirstOpInList;
+ while (aLoopOp != NULL && aLoopOp->next() != aNextOp)
+ aLoopOp = aLoopOp->next();
+ assert(aLoopOp != NULL);
+ aLoopOp->next(tOp);
+ }
+ tOp->next(aNextOp);
+ }
+ if (tOp->init(tab, this) != -1) {
+ return tOp;
+ } else {
+ theNdb->releaseOperation(tOp);
+ }//if
+ return NULL;
+
+ getNdbOp_error1:
+ setOperationErrorCodeAbort(4000);
+ return NULL;
+}//NdbTransaction::getNdbOperation()
+
+NdbOperation* NdbTransaction::getNdbOperation(const NdbDictionary::Table * table)
+{
+ if (table)
+ return getNdbOperation(& NdbTableImpl::getImpl(*table));
+ else
+ return NULL;
+}//NdbTransaction::getNdbOperation()
+
+// NdbScanOperation
+/*****************************************************************************
+NdbScanOperation* getNdbScanOperation(const char* aTableName);
+
+Return Value Return a pointer to a NdbScanOperation object if getNdbScanOperation was succesful.
+ Return NULL : In all other case.
+Parameters: aTableName : Name of the database table.
+Remark: Get an operation from NdbScanOperation idlelist and get the NdbTransaction object
+ who was fetch by startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbOperation object.synchronous
+******************************************************************************/
+NdbScanOperation*
+NdbTransaction::getNdbScanOperation(const char* aTableName)
+{
+ if (theCommitStatus == Started){
+ NdbTableImpl* tab = theNdb->theDictionary->getTable(aTableName);
+ if (tab != 0){
+ return getNdbScanOperation(tab);
+ } else {
+ setOperationErrorCodeAbort(theNdb->theDictionary->m_error.code);
+ return NULL;
+ }//if
+ }
+
+ setOperationErrorCodeAbort(4114);
+ return NULL;
+}//NdbTransaction::getNdbScanOperation()
+
+/*****************************************************************************
+NdbScanOperation* getNdbIndexScanOperation(const char* anIndexName, const char* aTableName);
+
+Return Value Return a pointer to a NdbIndexScanOperation object if getNdbIndexScanOperation was succesful.
+ Return NULL : In all other case.
+Parameters: anIndexName : Name of the index to use.
+ aTableName : Name of the database table.
+Remark: Get an operation from NdbIndexScanOperation idlelist and get the NdbTransaction object
+ who was fetch by startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbIndexScanOperation object.synchronous
+******************************************************************************/
+NdbIndexScanOperation*
+NdbTransaction::getNdbIndexScanOperation(const char* anIndexName,
+ const char* aTableName)
+{
+ NdbIndexImpl* index =
+ theNdb->theDictionary->getIndex(anIndexName, aTableName);
+ if (index == 0)
+ {
+ setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
+ return 0;
+ }
+ NdbTableImpl* table = theNdb->theDictionary->getTable(aTableName);
+ if (table == 0)
+ {
+ setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
+ return 0;
+ }
+
+ return getNdbIndexScanOperation(index, table);
+}
+
+NdbIndexScanOperation*
+NdbTransaction::getNdbIndexScanOperation(const NdbIndexImpl* index,
+ const NdbTableImpl* table)
+{
+ if (theCommitStatus == Started){
+ const NdbTableImpl * indexTable = index->getIndexTable();
+ if (indexTable != 0){
+ NdbIndexScanOperation* tOp = getNdbScanOperation(indexTable);
+ if(tOp)
+ {
+ tOp->m_currentTable = table;
+ }
+ return tOp;
+ } else {
+ setOperationErrorCodeAbort(4271);
+ return NULL;
+ }//if
+ }
+
+ setOperationErrorCodeAbort(4114);
+ return NULL;
+}//NdbTransaction::getNdbIndexScanOperation()
+
+NdbIndexScanOperation*
+NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index)
+{
+ if (index)
+ {
+ const NdbDictionary::Table *table=
+ theNdb->theDictionary->getTable(index->getTable());
+
+ if (table)
+ return getNdbIndexScanOperation(index, table);
+
+ setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
+ return NULL;
+ }
+ setOperationErrorCodeAbort(4271);
+ return NULL;
+}
+
+NdbIndexScanOperation*
+NdbTransaction::getNdbIndexScanOperation(const NdbDictionary::Index * index,
+ const NdbDictionary::Table * table)
+{
+ if (index && table)
+ return getNdbIndexScanOperation(& NdbIndexImpl::getImpl(*index),
+ & NdbTableImpl::getImpl(*table));
+ setOperationErrorCodeAbort(4271);
+ return NULL;
+}//NdbTransaction::getNdbIndexScanOperation()
+
+/*****************************************************************************
+NdbScanOperation* getNdbScanOperation(int aTableId);
+
+Return Value Return a pointer to a NdbScanOperation object if getNdbScanOperation was succesful.
+ Return NULL: In all other case.
+Parameters: tableId : Id of the database table beeing deleted.
+Remark: Get an operation from NdbScanOperation object idlelist and get the NdbTransaction
+ object who was fetch by startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbScanOperation object, synchronous.
+*****************************************************************************/
+NdbIndexScanOperation*
+NdbTransaction::getNdbScanOperation(const NdbTableImpl * tab)
+{
+ NdbIndexScanOperation* tOp;
+
+ tOp = theNdb->getScanOperation();
+ if (tOp == NULL)
+ goto getNdbOp_error1;
+
+ if (tOp->init(tab, this) != -1) {
+ define_scan_op(tOp);
+ return tOp;
+ } else {
+ theNdb->releaseScanOperation(tOp);
+ }//if
+ return NULL;
+
+getNdbOp_error1:
+ setOperationErrorCodeAbort(4000);
+ return NULL;
+}//NdbTransaction::getNdbScanOperation()
+
+void
+NdbTransaction::remove_list(NdbOperation*& list, NdbOperation* op){
+ NdbOperation* tmp= list;
+ if(tmp == op)
+ list = op->next();
+ else {
+ while(tmp && tmp->next() != op) tmp = tmp->next();
+ if(tmp)
+ tmp->next(op->next());
+ }
+ op->next(NULL);
+}
+
+void
+NdbTransaction::define_scan_op(NdbIndexScanOperation * tOp){
+ // Link scan operation into list of cursor operations
+ if (m_theLastScanOperation == NULL)
+ m_theFirstScanOperation = m_theLastScanOperation = tOp;
+ else {
+ m_theLastScanOperation->next(tOp);
+ m_theLastScanOperation = tOp;
+ }
+ tOp->next(NULL);
+}
+
+NdbScanOperation*
+NdbTransaction::getNdbScanOperation(const NdbDictionary::Table * table)
+{
+ if (table)
+ return getNdbScanOperation(& NdbTableImpl::getImpl(*table));
+ else
+ return NULL;
+}//NdbTransaction::getNdbScanOperation()
+
+
+// IndexOperation
+/*****************************************************************************
+NdbIndexOperation* getNdbIndexOperation(const char* anIndexName,
+ const char* aTableName);
+
+Return Value Return a pointer to a NdbOperation object if getNdbIndexOperation was succesful.
+ Return NULL : In all other case.
+Parameters: aTableName : Name of the database table.
+Remark: Get an operation from NdbIndexOperation idlelist and get the NdbTransaction object
+ who was fetch by startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbIndexOperation object.synchronous
+******************************************************************************/
+NdbIndexOperation*
+NdbTransaction::getNdbIndexOperation(const char* anIndexName,
+ const char* aTableName)
+{
+ if (theCommitStatus == Started) {
+ NdbTableImpl * table = theNdb->theDictionary->getTable(aTableName);
+ NdbIndexImpl * index;
+
+ if (table == 0)
+ {
+ setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
+ return NULL;
+ }
+
+ if (table->m_frm.get_data())
+ {
+ // This unique index is defined from SQL level
+ static const char* uniqueSuffix= "$unique";
+ char uniqueIndexName[MAX_TAB_NAME_SIZE];
+
+ strxnmov(uniqueIndexName, MAX_TAB_NAME_SIZE, anIndexName, uniqueSuffix, NullS);
+ index = theNdb->theDictionary->getIndex(uniqueIndexName,
+ aTableName);
+ }
+ else
+ index = theNdb->theDictionary->getIndex(anIndexName,
+ aTableName);
+ if(table != 0 && index != 0){
+ return getNdbIndexOperation(index, table);
+ }
+
+ if(index == 0){
+ setOperationErrorCodeAbort(4243);
+ return NULL;
+ }
+
+ setOperationErrorCodeAbort(4243);
+ return NULL;
+ }
+
+ setOperationErrorCodeAbort(4114);
+ return 0;
+}//NdbTransaction::getNdbIndexOperation()
+
+/*****************************************************************************
+NdbIndexOperation* getNdbIndexOperation(int anIndexId, int aTableId);
+
+Return Value Return a pointer to a NdbIndexOperation object if getNdbIndexOperation was succesful.
+ Return NULL: In all other case.
+Parameters: tableId : Id of the database table beeing deleted.
+Remark: Get an operation from NdbIndexOperation object idlelist and get the NdbTransaction
+ object who was fetch by startTransaction pointing to this operation
+ getOperation will set the theTableId in the NdbIndexOperation object, synchronous.
+*****************************************************************************/
+NdbIndexOperation*
+NdbTransaction::getNdbIndexOperation(const NdbIndexImpl * anIndex,
+ const NdbTableImpl * aTable,
+ NdbOperation* aNextOp)
+{
+ NdbIndexOperation* tOp;
+
+ tOp = theNdb->getIndexOperation();
+ if (tOp == NULL)
+ goto getNdbOp_error1;
+ if (aNextOp == NULL) {
+ if (theLastOpInList != NULL) {
+ theLastOpInList->next(tOp);
+ theLastOpInList = tOp;
+ } else {
+ theLastOpInList = tOp;
+ theFirstOpInList = tOp;
+ }//if
+ tOp->next(NULL);
+ } else {
+ // add before the given op
+ if (theFirstOpInList == aNextOp) {
+ theFirstOpInList = tOp;
+ } else {
+ NdbOperation* aLoopOp = theFirstOpInList;
+ while (aLoopOp != NULL && aLoopOp->next() != aNextOp)
+ aLoopOp = aLoopOp->next();
+ assert(aLoopOp != NULL);
+ aLoopOp->next(tOp);
+ }
+ tOp->next(aNextOp);
+ }
+ if (tOp->indxInit(anIndex, aTable, this)!= -1) {
+ return tOp;
+ } else {
+ theNdb->releaseOperation(tOp);
+ }//if
+ return NULL;
+
+ getNdbOp_error1:
+ setOperationErrorCodeAbort(4000);
+ return NULL;
+}//NdbTransaction::getNdbIndexOperation()
+
+NdbIndexOperation*
+NdbTransaction::getNdbIndexOperation(const NdbDictionary::Index * index)
+{
+ if (index)
+ {
+ const NdbDictionary::Table *table=
+ theNdb->theDictionary->getTable(index->getTable());
+
+ if (table)
+ return getNdbIndexOperation(index, table);
+
+ setOperationErrorCodeAbort(theNdb->theDictionary->getNdbError().code);
+ return NULL;
+ }
+ setOperationErrorCodeAbort(4271);
+ return NULL;
+}
+
+NdbIndexOperation*
+NdbTransaction::getNdbIndexOperation(const NdbDictionary::Index * index,
+ const NdbDictionary::Table * table)
+{
+ if (index && table)
+ return getNdbIndexOperation(& NdbIndexImpl::getImpl(*index),
+ & NdbTableImpl::getImpl(*table));
+
+ setOperationErrorCodeAbort(4271);
+ return NULL;
+}//NdbTransaction::getNdbIndexOperation()
+
+
+/*******************************************************************************
+int receiveDIHNDBTAMPER(NdbApiSignal* aSignal)
+
+Return Value: Return 0 : receiveDIHNDBTAMPER was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark: Sets theRestartGCI in the NDB object.
+*******************************************************************************/
+int
+NdbTransaction::receiveDIHNDBTAMPER(NdbApiSignal* aSignal)
+{
+ if (theStatus != Connecting) {
+ return -1;
+ } else {
+ theNdb->RestartGCI((Uint32)aSignal->readData(2));
+ theStatus = Connected;
+ }//if
+ return 0;
+}//NdbTransaction::receiveDIHNDBTAMPER()
+
+/*******************************************************************************
+int receiveTCSEIZECONF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTCSEIZECONF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark: Sets TC Connect pointer at reception of TCSEIZECONF.
+*******************************************************************************/
+int
+NdbTransaction::receiveTCSEIZECONF(NdbApiSignal* aSignal)
+{
+ if (theStatus != Connecting)
+ {
+ return -1;
+ } else
+ {
+ theTCConPtr = (Uint32)aSignal->readData(2);
+ theStatus = Connected;
+ }
+ return 0;
+}//NdbTransaction::receiveTCSEIZECONF()
+
+/*******************************************************************************
+int receiveTCSEIZEREF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTCSEIZEREF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark: Sets TC Connect pointer.
+*******************************************************************************/
+int
+NdbTransaction::receiveTCSEIZEREF(NdbApiSignal* aSignal)
+{
+ DBUG_ENTER("NdbTransaction::receiveTCSEIZEREF");
+ if (theStatus != Connecting)
+ {
+ DBUG_RETURN(-1);
+ } else
+ {
+ theStatus = ConnectFailure;
+ theNdb->theError.code = aSignal->readData(2);
+ DBUG_PRINT("info",("error code %d, %s",
+ theNdb->getNdbError().code,
+ theNdb->getNdbError().message));
+ DBUG_RETURN(0);
+ }
+}//NdbTransaction::receiveTCSEIZEREF()
+
+/*******************************************************************************
+int receiveTCRELEASECONF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTCRELEASECONF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark: DisConnect TC Connect pointer to NDBAPI.
+*******************************************************************************/
+int
+NdbTransaction::receiveTCRELEASECONF(NdbApiSignal* aSignal)
+{
+ if (theStatus != DisConnecting)
+ {
+ return -1;
+ } else
+ {
+ theStatus = NotConnected;
+ }
+ return 0;
+}//NdbTransaction::receiveTCRELEASECONF()
+
+/*******************************************************************************
+int receiveTCRELEASEREF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTCRELEASEREF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark: DisConnect TC Connect pointer to NDBAPI Failure.
+*******************************************************************************/
+int
+NdbTransaction::receiveTCRELEASEREF(NdbApiSignal* aSignal)
+{
+ if (theStatus != DisConnecting) {
+ return -1;
+ } else {
+ theStatus = ConnectFailure;
+ theNdb->theError.code = aSignal->readData(2);
+ return 0;
+ }//if
+}//NdbTransaction::receiveTCRELEASEREF()
+
+/******************************************************************************
+int receiveTC_COMMITCONF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTC_COMMITCONF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark:
+******************************************************************************/
+int
+NdbTransaction::receiveTC_COMMITCONF(const TcCommitConf * commitConf)
+{
+ if(checkState_TransId(&commitConf->transId1)){
+ theCommitStatus = Committed;
+ theCompletionStatus = CompletedSuccess;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+ return -1;
+}//NdbTransaction::receiveTC_COMMITCONF()
+
+/******************************************************************************
+int receiveTC_COMMITREF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTC_COMMITREF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark:
+******************************************************************************/
+int
+NdbTransaction::receiveTC_COMMITREF(NdbApiSignal* aSignal)
+{
+ const TcCommitRef * ref = CAST_CONSTPTR(TcCommitRef, aSignal->getDataPtr());
+ if(checkState_TransId(&ref->transId1)){
+ setOperationErrorCodeAbort(ref->errorCode);
+ theCommitStatus = Aborted;
+ theCompletionStatus = CompletedFailure;
+ theReturnStatus = ReturnFailure;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTC_COMMITREF()
+
+/******************************************************************************
+int receiveTCROLLBACKCONF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTCROLLBACKCONF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark:
+******************************************************************************/
+int
+NdbTransaction::receiveTCROLLBACKCONF(NdbApiSignal* aSignal)
+{
+ if(checkState_TransId(aSignal->getDataPtr() + 1)){
+ theCommitStatus = Aborted;
+ theCompletionStatus = CompletedSuccess;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTCROLLBACKCONF()
+
+/*******************************************************************************
+int receiveTCROLLBACKREF(NdbApiSignal* aSignal);
+
+Return Value: Return 0 : receiveTCROLLBACKREF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark:
+*******************************************************************************/
+int
+NdbTransaction::receiveTCROLLBACKREF(NdbApiSignal* aSignal)
+{
+ if(checkState_TransId(aSignal->getDataPtr() + 1)){
+ setOperationErrorCodeAbort(aSignal->readData(4));
+ theCommitStatus = Aborted;
+ theCompletionStatus = CompletedFailure;
+ theReturnStatus = ReturnFailure;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTCROLLBACKREF()
+
+/*****************************************************************************
+int receiveTCROLLBACKREP( NdbApiSignal* aSignal)
+
+Return Value: Return 0 : send was succesful.
+ Return -1: In all other case.
+Parameters: aSignal: the signal object that contains the
+ TCROLLBACKREP signal from TC.
+Remark: Handles the reception of the ROLLBACKREP signal.
+*****************************************************************************/
+int
+NdbTransaction::receiveTCROLLBACKREP( NdbApiSignal* aSignal)
+{
+ /****************************************************************************
+Check that we are expecting signals from this transaction and that it doesn't
+belong to a transaction already completed. Simply ignore messages from other
+transactions.
+ ****************************************************************************/
+ if(checkState_TransId(aSignal->getDataPtr() + 1)){
+ theError.code = aSignal->readData(4);// Override any previous errors
+
+ /**********************************************************************/
+ /* A serious error has occured. This could be due to deadlock or */
+ /* lack of resources or simply a programming error in NDB. This */
+ /* transaction will be aborted. Actually it has already been */
+ /* and we only need to report completion and return with the */
+ /* error code to the application. */
+ /**********************************************************************/
+ theCompletionStatus = CompletedFailure;
+ theCommitStatus = Aborted;
+ theReturnStatus = ReturnFailure;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTCROLLBACKREP()
+
+/*******************************************************************************
+int receiveTCKEYCONF(NdbApiSignal* aSignal, Uint32 long_short_ind);
+
+Return Value: Return 0 : receiveTCKEYCONF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark:
+*******************************************************************************/
+int
+NdbTransaction::receiveTCKEYCONF(const TcKeyConf * keyConf, Uint32 aDataLength)
+{
+ NdbReceiver* tOp;
+ const Uint32 tTemp = keyConf->confInfo;
+ /***************************************************************************
+Check that we are expecting signals from this transaction and that it
+doesn't belong to a transaction already completed. Simply ignore messages
+from other transactions.
+ ***************************************************************************/
+ if(checkState_TransId(&keyConf->transId1)){
+
+ const Uint32 tNoOfOperations = TcKeyConf::getNoOfOperations(tTemp);
+ const Uint32 tCommitFlag = TcKeyConf::getCommitFlag(tTemp);
+
+ const Uint32* tPtr = (Uint32 *)&keyConf->operations[0];
+ Uint32 tNoComp = theNoOfOpCompleted;
+ for (Uint32 i = 0; i < tNoOfOperations ; i++) {
+ tOp = theNdb->void2rec(theNdb->int2void(*tPtr++));
+ const Uint32 tAttrInfoLen = *tPtr++;
+ if (tOp && tOp->checkMagicNumber()) {
+ Uint32 done = tOp->execTCOPCONF(tAttrInfoLen);
+ if(tAttrInfoLen > TcKeyConf::SimpleReadBit){
+ Uint32 node = tAttrInfoLen & (~TcKeyConf::SimpleReadBit);
+ NdbNodeBitmask::set(m_db_nodes, node);
+ if(NdbNodeBitmask::get(m_failed_db_nodes, node) && !done)
+ {
+ done = 1;
+ tOp->setErrorCode(4119);
+ theCompletionStatus = CompletedFailure;
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ }
+ }
+ tNoComp += done;
+ } else {
+ return -1;
+ }//if
+ }//for
+ Uint32 tNoSent = theNoOfOpSent;
+ theNoOfOpCompleted = tNoComp;
+ Uint32 tGCI = keyConf->gci;
+ if (tCommitFlag == 1) {
+ theCommitStatus = Committed;
+ theGlobalCheckpointId = tGCI;
+ } else if ((tNoComp >= tNoSent) &&
+ (theLastExecOpInList->theCommitIndicator == 1)){
+
+
+ if (m_abortOption == AO_IgnoreError && theError.code != 0){
+ /**
+ * There's always a TCKEYCONF when using IgnoreError
+ */
+ return -1;
+ }
+/**********************************************************************/
+// We sent the transaction with Commit flag set and received a CONF with
+// no Commit flag set. This is clearly an anomaly.
+/**********************************************************************/
+ theError.code = 4011;
+ theCompletionStatus = CompletedFailure;
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ theCommitStatus = Aborted;
+ return 0;
+ }//if
+ if (tNoComp >= tNoSent) {
+ return 0; // No more operations to wait for
+ }//if
+ // Not completed the reception yet.
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTCKEYCONF()
+
+/*****************************************************************************
+int receiveTCKEY_FAILCONF( NdbApiSignal* aSignal)
+
+Return Value: Return 0 : receive was completed.
+ Return -1: In all other case.
+Parameters: aSignal: the signal object that contains the
+ TCKEY_FAILCONF signal from TC.
+Remark: Handles the reception of the TCKEY_FAILCONF signal.
+*****************************************************************************/
+int
+NdbTransaction::receiveTCKEY_FAILCONF(const TcKeyFailConf * failConf)
+{
+ NdbOperation* tOp;
+ /*
+ Check that we are expecting signals from this transaction and that it
+ doesn't belong to a transaction already completed. Simply ignore
+ messages from other transactions.
+ */
+ if(checkState_TransId(&failConf->transId1)){
+ /*
+ A node failure of the TC node occured. The transaction has
+ been committed.
+ */
+ theCommitStatus = Committed;
+ tOp = theFirstExecOpInList;
+ while (tOp != NULL) {
+ /*
+ * Check if the transaction expected read values...
+ * If it did some of them might have gotten lost even if we succeeded
+ * in committing the transaction.
+ */
+ switch(tOp->theOperationType){
+ case NdbOperation::UpdateRequest:
+ case NdbOperation::InsertRequest:
+ case NdbOperation::DeleteRequest:
+ case NdbOperation::WriteRequest:
+ tOp = tOp->next();
+ break;
+ case NdbOperation::ReadRequest:
+ case NdbOperation::ReadExclusive:
+ case NdbOperation::OpenScanRequest:
+ case NdbOperation::OpenRangeScanRequest:
+ theCompletionStatus = CompletedFailure;
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ setOperationErrorCodeAbort(4115);
+ tOp = NULL;
+ break;
+ case NdbOperation::NotDefined:
+ case NdbOperation::NotDefined2:
+ assert(false);
+ break;
+ }//if
+ }//while
+ theReleaseOnClose = true;
+ return 0;
+ } else {
+#ifdef VM_TRACE
+ ndbout_c("Recevied TCKEY_FAILCONF wo/ operation");
+#endif
+ }
+ return -1;
+}//NdbTransaction::receiveTCKEY_FAILCONF()
+
+/*************************************************************************
+int receiveTCKEY_FAILREF( NdbApiSignal* aSignal)
+
+Return Value: Return 0 : receive was completed.
+ Return -1: In all other case.
+Parameters: aSignal: the signal object that contains the
+ TCKEY_FAILREF signal from TC.
+Remark: Handles the reception of the TCKEY_FAILREF signal.
+**************************************************************************/
+int
+NdbTransaction::receiveTCKEY_FAILREF(NdbApiSignal* aSignal)
+{
+ /*
+ Check that we are expecting signals from this transaction and
+ that it doesn't belong to a transaction already
+ completed. Simply ignore messages from other transactions.
+ */
+ if(checkState_TransId(aSignal->getDataPtr()+1)){
+ /*
+ We received an indication of that this transaction was aborted due to a
+ node failure.
+ */
+ if (theSendStatus == NdbTransaction::sendTC_ROLLBACK) {
+ /*
+ We were in the process of sending a rollback anyways. We will
+ report it as a success.
+ */
+ theCompletionStatus = NdbTransaction::CompletedSuccess;
+ } else {
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ theCompletionStatus = NdbTransaction::CompletedFailure;
+ theError.code = 4031;
+ }//if
+ theReleaseOnClose = true;
+ theCommitStatus = NdbTransaction::Aborted;
+ return 0;
+ } else {
+#ifdef VM_TRACE
+ ndbout_c("Recevied TCKEY_FAILREF wo/ operation");
+#endif
+ }
+ return -1;
+}//NdbTransaction::receiveTCKEY_FAILREF()
+
+/******************************************************************************
+int receiveTCINDXCONF(NdbApiSignal* aSignal, Uint32 long_short_ind);
+
+Return Value: Return 0 : receiveTCINDXCONF was successful.
+ Return -1: In all other case.
+Parameters: aSignal: The signal object pointer.
+Remark:
+******************************************************************************/
+int
+NdbTransaction::receiveTCINDXCONF(const TcIndxConf * indxConf,
+ Uint32 aDataLength)
+{
+ if(checkState_TransId(&indxConf->transId1)){
+ const Uint32 tTemp = indxConf->confInfo;
+ const Uint32 tNoOfOperations = TcIndxConf::getNoOfOperations(tTemp);
+ const Uint32 tCommitFlag = TcKeyConf::getCommitFlag(tTemp);
+
+ const Uint32* tPtr = (Uint32 *)&indxConf->operations[0];
+ Uint32 tNoComp = theNoOfOpCompleted;
+ for (Uint32 i = 0; i < tNoOfOperations ; i++) {
+ NdbReceiver* tOp = theNdb->void2rec(theNdb->int2void(*tPtr));
+ tPtr++;
+ const Uint32 tAttrInfoLen = *tPtr;
+ tPtr++;
+ if (tOp && tOp->checkMagicNumber()) {
+ tNoComp += tOp->execTCOPCONF(tAttrInfoLen);
+ } else {
+ return -1;
+ }//if
+ }//for
+ Uint32 tNoSent = theNoOfOpSent;
+ Uint32 tGCI = indxConf->gci;
+ theNoOfOpCompleted = tNoComp;
+ if (tCommitFlag == 1) {
+ theCommitStatus = Committed;
+ theGlobalCheckpointId = tGCI;
+ } else if ((tNoComp >= tNoSent) &&
+ (theLastExecOpInList->theCommitIndicator == 1)){
+ /**********************************************************************/
+ // We sent the transaction with Commit flag set and received a CONF with
+ // no Commit flag set. This is clearly an anomaly.
+ /**********************************************************************/
+ theError.code = 4011;
+ theCompletionStatus = NdbTransaction::CompletedFailure;
+ theCommitStatus = NdbTransaction::Aborted;
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ return 0;
+ }//if
+ if (tNoComp >= tNoSent) {
+ return 0; // No more operations to wait for
+ }//if
+ // Not completed the reception yet.
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTCINDXCONF()
+
+/*****************************************************************************
+int receiveTCINDXREF( NdbApiSignal* aSignal)
+
+Return Value: Return 0 : send was succesful.
+ Return -1: In all other case.
+Parameters: aSignal: the signal object that contains the
+ TCINDXREF signal from TC.
+Remark: Handles the reception of the TCINDXREF signal.
+*****************************************************************************/
+int
+NdbTransaction::receiveTCINDXREF( NdbApiSignal* aSignal)
+{
+ if(checkState_TransId(aSignal->getDataPtr()+1)){
+ theError.code = aSignal->readData(4); // Override any previous errors
+
+ /**********************************************************************/
+ /* A serious error has occured. This could be due to deadlock or */
+ /* lack of resources or simply a programming error in NDB. This */
+ /* transaction will be aborted. Actually it has already been */
+ /* and we only need to report completion and return with the */
+ /* error code to the application. */
+ /**********************************************************************/
+ theCompletionStatus = NdbTransaction::CompletedFailure;
+ theCommitStatus = NdbTransaction::Aborted;
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}//NdbTransaction::receiveTCINDXREF()
+
+/*******************************************************************************
+int OpCompletedFailure();
+
+Return Value: Return 0 : OpCompleteSuccess was successful.
+ Return -1: In all other case.
+Parameters: aErrorCode: The error code.
+Remark: An operation was completed with failure.
+*******************************************************************************/
+int
+NdbTransaction::OpCompleteFailure(Uint8 abortOption, bool setFailure)
+{
+ Uint32 tNoComp = theNoOfOpCompleted;
+ Uint32 tNoSent = theNoOfOpSent;
+ if (setFailure)
+ theCompletionStatus = NdbTransaction::CompletedFailure;
+ tNoComp++;
+ theNoOfOpCompleted = tNoComp;
+ if (tNoComp == tNoSent) {
+ //------------------------------------------------------------------------
+ //If the transaction consists of only simple reads we can set
+ //Commit state Aborted. Otherwise this simple operation cannot
+ //decide the success of the whole transaction since a simple
+ //operation is not really part of that transaction.
+ //------------------------------------------------------------------------
+ if (abortOption == AO_IgnoreError){
+ /**
+ * There's always a TCKEYCONF when using IgnoreError
+ */
+ return -1;
+ }
+
+ return 0; // Last operation received
+ } else if (tNoComp > tNoSent) {
+ setOperationErrorCodeAbort(4113); // Too many operations,
+ // stop waiting for more
+ return 0;
+ } else {
+ return -1; // Continue waiting for more signals
+ }//if
+}//NdbTransaction::OpCompleteFailure()
+
+/******************************************************************************
+int OpCompleteSuccess();
+
+Return Value: Return 0 : OpCompleteSuccess was successful.
+ Return -1: In all other case.
+Remark: An operation was completed with success.
+*******************************************************************************/
+int
+NdbTransaction::OpCompleteSuccess()
+{
+ Uint32 tNoComp = theNoOfOpCompleted;
+ Uint32 tNoSent = theNoOfOpSent;
+ tNoComp++;
+ theNoOfOpCompleted = tNoComp;
+ if (tNoComp == tNoSent) { // Last operation completed
+ return 0;
+ } else if (tNoComp < tNoSent) {
+ return -1; // Continue waiting for more signals
+ } else {
+ setOperationErrorCodeAbort(4113); // Too many operations,
+ // stop waiting for more
+ theCompletionStatus = NdbTransaction::CompletedFailure;
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ return 0;
+ }//if
+}//NdbTransaction::OpCompleteSuccess()
+
+/******************************************************************************
+ int getGCI();
+
+Remark: Get global checkpoint identity of the transaction
+*******************************************************************************/
+int
+NdbTransaction::getGCI()
+{
+ if (theCommitStatus == NdbTransaction::Committed) {
+ return theGlobalCheckpointId;
+ }//if
+ return 0;
+}//NdbTransaction::getGCI()
+
+/*******************************************************************************
+Uint64 getTransactionId(void);
+
+Remark: Get the transaction identity.
+*******************************************************************************/
+Uint64
+NdbTransaction::getTransactionId()
+{
+ return theTransactionId;
+}//NdbTransaction::getTransactionId()
+
+NdbTransaction::CommitStatusType
+NdbTransaction::commitStatus()
+{
+ return theCommitStatus;
+}//NdbTransaction::commitStatus()
+
+int
+NdbTransaction::getNdbErrorLine()
+{
+ return theErrorLine;
+}
+
+NdbOperation*
+NdbTransaction::getNdbErrorOperation()
+{
+ return theErrorOperation;
+}//NdbTransaction::getNdbErrorOperation()
+
+const NdbOperation *
+NdbTransaction::getNextCompletedOperation(const NdbOperation * current) const {
+ if(current == 0)
+ return theCompletedFirstOp;
+ return current->theNext;
+}
+
+#ifdef VM_TRACE
+#define CASE(x) case x: ndbout << " " << #x; break
+void
+NdbTransaction::printState()
+{
+ ndbout << "con=" << hex << this << dec;
+ ndbout << " node=" << getConnectedNodeId();
+ switch (theStatus) {
+ CASE(NotConnected);
+ CASE(Connecting);
+ CASE(Connected);
+ CASE(DisConnecting);
+ CASE(ConnectFailure);
+ default: ndbout << (Uint32) theStatus;
+ }
+ switch (theListState) {
+ CASE(NotInList);
+ CASE(InPreparedList);
+ CASE(InSendList);
+ CASE(InCompletedList);
+ default: ndbout << (Uint32) theListState;
+ }
+ switch (theSendStatus) {
+ CASE(NotInit);
+ CASE(InitState);
+ CASE(sendOperations);
+ CASE(sendCompleted);
+ CASE(sendCOMMITstate);
+ CASE(sendABORT);
+ CASE(sendABORTfail);
+ CASE(sendTC_ROLLBACK);
+ CASE(sendTC_COMMIT);
+ CASE(sendTC_OP);
+ default: ndbout << (Uint32) theSendStatus;
+ }
+ switch (theCommitStatus) {
+ CASE(NotStarted);
+ CASE(Started);
+ CASE(Committed);
+ CASE(Aborted);
+ CASE(NeedAbort);
+ default: ndbout << (Uint32) theCommitStatus;
+ }
+ switch (theCompletionStatus) {
+ CASE(NotCompleted);
+ CASE(CompletedSuccess);
+ CASE(CompletedFailure);
+ CASE(DefinitionFailure);
+ default: ndbout << (Uint32) theCompletionStatus;
+ }
+ ndbout << endl;
+}
+#undef CASE
+#endif
+
+int
+NdbTransaction::report_node_failure(Uint32 id){
+ NdbNodeBitmask::set(m_failed_db_nodes, id);
+ if(!NdbNodeBitmask::get(m_db_nodes, id))
+ {
+ return 0;
+ }
+
+ /**
+ * Arrived
+ * TCKEYCONF TRANSIDAI
+ * 1) - -
+ * 2) - X
+ * 3) X -
+ * 4) X X
+ */
+ NdbOperation* tmp = theFirstExecOpInList;
+ const Uint32 len = TcKeyConf::SimpleReadBit | id;
+ Uint32 tNoComp = theNoOfOpCompleted;
+ Uint32 tNoSent = theNoOfOpSent;
+ Uint32 count = 0;
+ while(tmp != 0)
+ {
+ if(tmp->theReceiver.m_expected_result_length == len &&
+ tmp->theReceiver.m_received_result_length == 0)
+ {
+ count++;
+ tmp->theError.code = 4119;
+ }
+ tmp = tmp->next();
+ }
+ tNoComp += count;
+ theNoOfOpCompleted = tNoComp;
+ if(count)
+ {
+ theReturnStatus = NdbTransaction::ReturnFailure;
+ if(tNoComp == tNoSent)
+ {
+ theError.code = 4119;
+ theCompletionStatus = NdbTransaction::CompletedFailure;
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/storage/ndb/src/ndbapi/NdbTransactionScan.cpp b/storage/ndb/src/ndbapi/NdbTransactionScan.cpp
new file mode 100644
index 00000000000..4c507f6ab8c
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbTransactionScan.cpp
@@ -0,0 +1,122 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include <Ndb.hpp>
+#include <NdbTransaction.hpp>
+#include <NdbOperation.hpp>
+#include <NdbScanOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include "TransporterFacade.hpp"
+#include "NdbUtil.hpp"
+#include "API.hpp"
+#include "NdbImpl.hpp"
+
+#include <signaldata/ScanTab.hpp>
+
+#include <NdbOut.hpp>
+
+
+/***************************************************************************
+ * int receiveSCAN_TABREF(NdbApiSignal* aSignal)
+ *
+ * This means the scan could not be started, set status(s) to indicate
+ * the failure
+ *
+ ****************************************************************************/
+int
+NdbTransaction::receiveSCAN_TABREF(NdbApiSignal* aSignal){
+ const ScanTabRef * ref = CAST_CONSTPTR(ScanTabRef, aSignal->getDataPtr());
+
+ if(checkState_TransId(&ref->transId1)){
+ theScanningOp->setErrorCode(ref->errorCode);
+ theScanningOp->execCLOSE_SCAN_REP();
+ if(!ref->closeNeeded){
+ return 0;
+ }
+
+ /**
+ * Setup so that close_impl will actually perform a close
+ * and not "close scan"-optimze it away
+ */
+ theScanningOp->m_conf_receivers_count++;
+ theScanningOp->m_conf_receivers[0] = theScanningOp->m_receivers[0];
+ theScanningOp->m_conf_receivers[0]->m_tcPtrI = ~0;
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}
+
+/*****************************************************************************
+ * int receiveSCAN_TABCONF(NdbApiSignal* aSignal)
+ *
+ * Receive SCAN_TABCONF
+ * If scanStatus == 0 there is more records to read. Since signals may be
+ * received in any order we have to go through the lists with saved signals
+ * and check if all expected signals are there so that we can start to
+ * execute them.
+ *
+ * If scanStatus > 0 this indicates that the scan is finished and there are
+ * no more data to be read.
+ *
+ *****************************************************************************/
+int
+NdbTransaction::receiveSCAN_TABCONF(NdbApiSignal* aSignal,
+ const Uint32 * ops, Uint32 len)
+{
+ const ScanTabConf * conf = CAST_CONSTPTR(ScanTabConf, aSignal->getDataPtr());
+ if(checkState_TransId(&conf->transId1)){
+
+ if (conf->requestInfo == ScanTabConf::EndOfData) {
+ theScanningOp->execCLOSE_SCAN_REP();
+ return 0;
+ }
+
+ for(Uint32 i = 0; i<len; i += 3){
+ Uint32 opCount, totalLen;
+ Uint32 ptrI = * ops++;
+ Uint32 tcPtrI = * ops++;
+ Uint32 info = * ops++;
+ opCount = ScanTabConf::getRows(info);
+ totalLen = ScanTabConf::getLength(info);
+
+ void * tPtr = theNdb->int2void(ptrI);
+ assert(tPtr); // For now
+ NdbReceiver* tOp = theNdb->void2rec(tPtr);
+ if (tOp && tOp->checkMagicNumber())
+ {
+ if (tcPtrI == RNIL && opCount == 0)
+ theScanningOp->receiver_completed(tOp);
+ else if (tOp->execSCANOPCONF(tcPtrI, totalLen, opCount))
+ theScanningOp->receiver_delivered(tOp);
+ }
+ }
+ return 0;
+ } else {
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+ }
+
+ return -1;
+}
diff --git a/storage/ndb/src/ndbapi/NdbUtil.cpp b/storage/ndb/src/ndbapi/NdbUtil.cpp
new file mode 100644
index 00000000000..5c74d251ff9
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbUtil.cpp
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+/************************************************************************************************
+Name: NdbUtil.C
+Include:
+Link:
+Author: UABRONM Mikael Ronström UAB/B/SD
+Date: 991029
+Version: 0.4
+Description: Utility classes for NDB API
+Documentation:
+Adjust: 991029 UABRONM First version.
+Comment:
+************************************************************************************************/
+
+#include "NdbUtil.hpp"
+
+NdbLabel::NdbLabel() :
+ theNext(NULL)
+{
+}
+
+NdbLabel::~NdbLabel()
+{
+}
+
+NdbSubroutine::NdbSubroutine() :
+ theNext(NULL)
+{
+}
+
+NdbSubroutine::~NdbSubroutine()
+{
+}
+
+NdbBranch::NdbBranch() :
+ theSignal(NULL),
+ theNext(NULL)
+{
+}
+
+NdbBranch::~NdbBranch()
+{
+}
+
+NdbCall::NdbCall() :
+ theSignal(NULL),
+ theNext(NULL)
+{
+}
+
+NdbCall::~NdbCall()
+{
+}
diff --git a/storage/ndb/src/ndbapi/NdbUtil.hpp b/storage/ndb/src/ndbapi/NdbUtil.hpp
new file mode 100644
index 00000000000..80fc15ddd8c
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbUtil.hpp
@@ -0,0 +1,97 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/************************************************************************************************
+Name: NdbUtil.H
+Include:
+Link:
+Author: UABRONM Mikael Ronström UAB/B/SD
+Date: 991029
+Version: 0.4
+Description: Utility classes for NDB API
+Documentation:
+Adjust: 991029 UABRONM First version.
+Comment:
+************************************************************************************************/
+#ifndef NdbUtil_H
+#define NdbUtil_H
+
+#include <ndb_global.h>
+
+class NdbApiSignal;
+class NdbOperation;
+
+class NdbLabel
+{
+friend class NdbOperation;
+friend class Ndb;
+
+private:
+ NdbLabel();
+ ~NdbLabel();
+
+ NdbLabel* theNext;
+ Uint32 theSubroutine[16];
+ Uint32 theLabelAddress[16];
+ Uint32 theLabelNo[16];
+};
+
+class NdbSubroutine
+{
+friend class NdbOperation;
+friend class Ndb;
+
+private:
+ NdbSubroutine();
+ ~NdbSubroutine();
+
+ NdbSubroutine* theNext;
+ Uint32 theSubroutineAddress[16];
+};
+
+class NdbBranch
+{
+friend class NdbOperation;
+friend class Ndb;
+
+private:
+ NdbBranch();
+ ~NdbBranch();
+
+ NdbApiSignal* theSignal;
+ Uint32 theSignalAddress;
+ Uint32 theBranchAddress;
+ Uint32 theBranchLabel;
+ Uint32 theSubroutine;
+ NdbBranch* theNext;
+};
+
+class NdbCall
+{
+friend class NdbOperation;
+friend class Ndb;
+
+private:
+ NdbCall();
+ ~NdbCall();
+
+ NdbApiSignal* theSignal;
+ Uint32 theSignalAddress;
+ Uint32 theSubroutine;
+ NdbCall* theNext;
+};
+
+#endif
diff --git a/storage/ndb/src/ndbapi/NdbWaiter.hpp b/storage/ndb/src/ndbapi/NdbWaiter.hpp
new file mode 100644
index 00000000000..8b7b2a75879
--- /dev/null
+++ b/storage/ndb/src/ndbapi/NdbWaiter.hpp
@@ -0,0 +1,102 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_WAITER_HPP
+#define NDB_WAITER_HPP
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include <NdbError.hpp>
+#include <NdbCondition.h>
+#include <NdbReceiver.hpp>
+#include <NdbOperation.hpp>
+#include <kernel/ndb_limits.h>
+
+#include <NdbTick.h>
+
+enum WaitSignalType {
+ NO_WAIT = 0,
+ WAIT_NODE_FAILURE = 1, // Node failure during wait
+ WST_WAIT_TIMEOUT = 2, // Timeout during wait
+
+ WAIT_TC_SEIZE = 3,
+ WAIT_TC_RELEASE = 4,
+ WAIT_NDB_TAMPER = 5,
+ WAIT_SCAN = 6,
+
+ // DICT stuff
+ WAIT_GET_TAB_INFO_REQ = 11,
+ WAIT_CREATE_TAB_REQ = 12,
+ WAIT_DROP_TAB_REQ = 13,
+ WAIT_ALTER_TAB_REQ = 14,
+ WAIT_CREATE_INDX_REQ = 15,
+ WAIT_DROP_INDX_REQ = 16,
+ WAIT_LIST_TABLES_CONF = 17
+};
+
+class NdbWaiter {
+public:
+ NdbWaiter();
+ ~NdbWaiter();
+
+ void wait(int waitTime);
+ void nodeFail(Uint32 node);
+ void signal(Uint32 state);
+
+ Uint32 m_node;
+ Uint32 m_state;
+ void * m_mutex;
+ struct NdbCondition * m_condition;
+};
+
+inline
+void
+NdbWaiter::wait(int waitTime)
+{
+ const bool forever = (waitTime == -1);
+ const NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime;
+ while (1) {
+ if (m_state == NO_WAIT || m_state == WAIT_NODE_FAILURE)
+ break;
+ if (forever) {
+ NdbCondition_Wait(m_condition, (NdbMutex*)m_mutex);
+ } else {
+ if (waitTime <= 0) {
+ m_state = WST_WAIT_TIMEOUT;
+ break;
+ }
+ NdbCondition_WaitTimeout(m_condition, (NdbMutex*)m_mutex, waitTime);
+ waitTime = maxTime - NdbTick_CurrentMillisecond();
+ }
+ }
+}
+
+inline
+void
+NdbWaiter::nodeFail(Uint32 aNodeId){
+ if (m_state != NO_WAIT && m_node == aNodeId){
+ m_state = WAIT_NODE_FAILURE;
+ NdbCondition_Signal(m_condition);
+ }
+}
+
+inline
+void
+NdbWaiter::signal(Uint32 state){
+ m_state = state;
+ NdbCondition_Signal(m_condition);
+}
+
+#endif
diff --git a/storage/ndb/src/ndbapi/Ndberr.cpp b/storage/ndb/src/ndbapi/Ndberr.cpp
new file mode 100644
index 00000000000..b05818de6f1
--- /dev/null
+++ b/storage/ndb/src/ndbapi/Ndberr.cpp
@@ -0,0 +1,82 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <NdbError.hpp>
+#include "NdbImpl.hpp"
+#include "NdbDictionaryImpl.hpp"
+#include <NdbOperation.hpp>
+#include <NdbTransaction.hpp>
+#include <NdbBlob.hpp>
+#include "NdbEventOperationImpl.hpp"
+
+static void
+update(const NdbError & _err){
+ NdbError & error = (NdbError &) _err;
+ ndberror_struct ndberror = (ndberror_struct)error;
+ ndberror_update(&ndberror);
+ error = NdbError(ndberror);
+}
+
+const
+NdbError &
+Ndb::getNdbError(int code){
+ theError.code = code;
+ update(theError);
+ return theError;
+}
+
+const
+NdbError &
+Ndb::getNdbError() const {
+ update(theError);
+ return theError;
+}
+
+const
+NdbError &
+NdbDictionaryImpl::getNdbError() const {
+ update(m_error);
+ return m_error;
+}
+
+const
+NdbError &
+NdbTransaction::getNdbError() const {
+ update(theError);
+ return theError;
+}
+
+const
+NdbError &
+NdbOperation::getNdbError() const {
+ update(theError);
+ return theError;
+}
+
+const
+NdbError &
+NdbBlob::getNdbError() const {
+ update(theError);
+ return theError;
+}
+
+const
+NdbError &
+NdbEventOperationImpl::getNdbError() const {
+ update(m_error);
+ return m_error;
+}
diff --git a/storage/ndb/src/ndbapi/Ndbif.cpp b/storage/ndb/src/ndbapi/Ndbif.cpp
new file mode 100644
index 00000000000..40aaa1e3daa
--- /dev/null
+++ b/storage/ndb/src/ndbapi/Ndbif.cpp
@@ -0,0 +1,1368 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "NdbApiSignal.hpp"
+#include "NdbImpl.hpp"
+#include <NdbTransaction.hpp>
+#include <NdbOperation.hpp>
+#include <NdbIndexOperation.hpp>
+#include <NdbScanOperation.hpp>
+#include <NdbRecAttr.hpp>
+#include <NdbReceiver.hpp>
+#include "API.hpp"
+
+#include <signaldata/TcCommit.hpp>
+#include <signaldata/TcKeyFailConf.hpp>
+#include <signaldata/TcKeyConf.hpp>
+#include <signaldata/TestOrd.hpp>
+#include <signaldata/CreateIndx.hpp>
+#include <signaldata/DropIndx.hpp>
+#include <signaldata/TcIndx.hpp>
+#include <signaldata/TransIdAI.hpp>
+#include <signaldata/ScanFrag.hpp>
+#include <signaldata/ScanTab.hpp>
+
+#include <ndb_limits.h>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+
+/******************************************************************************
+ * int init( int aNrOfCon, int aNrOfOp );
+ *
+ * Return Value: Return 0 : init was successful.
+ * Return -1: In all other case.
+ * Parameters: aNrOfCon : Number of connections offered to the application.
+ * aNrOfOp : Number of operations offered to the application.
+ * Remark: Create pointers and idle list Synchronous.
+ ****************************************************************************/
+int
+Ndb::init(int aMaxNoOfTransactions)
+{
+ DBUG_ENTER("Ndb::init");
+
+ int i;
+ int aNrOfCon;
+ int aNrOfOp;
+ int tMaxNoOfTransactions;
+ NdbApiSignal* tSignal[16]; // Initiate free list of 16 signal objects
+ if (theInitState != NotInitialised) {
+ switch(theInitState){
+ case InitConfigError:
+ theError.code = 4117;
+ break;
+ default:
+ theError.code = 4104;
+ break;
+ }
+ DBUG_RETURN(-1);
+ }//if
+ theInitState = StartingInit;
+ TransporterFacade * theFacade = TransporterFacade::instance();
+ theFacade->lock_mutex();
+
+ const int tBlockNo = theFacade->open(this,
+ executeMessage,
+ statusMessage);
+ if ( tBlockNo == -1 ) {
+ theError.code = 4105;
+ theFacade->unlock_mutex();
+ DBUG_RETURN(-1); // no more free blocknumbers
+ }//if
+
+ theNdbBlockNumber = tBlockNo;
+
+ theFacade->unlock_mutex();
+
+ theDictionary->setTransporter(this, theFacade);
+
+ aNrOfCon = theImpl->theNoOfDBnodes;
+ aNrOfOp = 2*theImpl->theNoOfDBnodes;
+
+ // Create connection object in a linked list
+ if((createConIdleList(aNrOfCon)) == -1){
+ theError.code = 4000;
+ goto error_handler;
+ }
+
+ // Create operations in a linked list
+ if((createOpIdleList(aNrOfOp)) == -1){
+ theError.code = 4000;
+ goto error_handler;
+ }
+
+ tMaxNoOfTransactions = aMaxNoOfTransactions * 3;
+ if (tMaxNoOfTransactions > 1024) {
+ tMaxNoOfTransactions = 1024;
+ }//if
+ theMaxNoOfTransactions = tMaxNoOfTransactions;
+
+ thePreparedTransactionsArray = new NdbTransaction* [tMaxNoOfTransactions];
+ theSentTransactionsArray = new NdbTransaction* [tMaxNoOfTransactions];
+ theCompletedTransactionsArray = new NdbTransaction* [tMaxNoOfTransactions];
+
+ if ((thePreparedTransactionsArray == NULL) ||
+ (theSentTransactionsArray == NULL) ||
+ (theCompletedTransactionsArray == NULL)) {
+ goto error_handler;
+ }//if
+
+ for (i = 0; i < tMaxNoOfTransactions; i++) {
+ thePreparedTransactionsArray[i] = NULL;
+ theSentTransactionsArray[i] = NULL;
+ theCompletedTransactionsArray[i] = NULL;
+ }//for
+ for (i = 0; i < 16; i++){
+ tSignal[i] = getSignal();
+ if(tSignal[i] == NULL) {
+ theError.code = 4000;
+ goto error_handler;
+ }
+ }
+ for (i = 0; i < 16; i++)
+ releaseSignal(tSignal[i]);
+ theInitState = Initialised;
+ DBUG_RETURN(0);
+
+error_handler:
+ ndbout << "error_handler" << endl;
+ releaseTransactionArrays();
+ while ( theConIdleList != NULL )
+ freeNdbCon();
+ while ( theSignalIdleList != NULL )
+ freeSignal();
+ while (theRecAttrIdleList != NULL)
+ freeRecAttr();
+ while (theOpIdleList != NULL)
+ freeOperation();
+
+ delete theDictionary;
+ TransporterFacade::instance()->close(theNdbBlockNumber, 0);
+ DBUG_RETURN(-1);
+}
+
+void
+Ndb::releaseTransactionArrays()
+{
+ DBUG_ENTER("Ndb::releaseTransactionArrays");
+ if (thePreparedTransactionsArray != NULL) {
+ delete [] thePreparedTransactionsArray;
+ }//if
+ if (theSentTransactionsArray != NULL) {
+ delete [] theSentTransactionsArray;
+ }//if
+ if (theCompletedTransactionsArray != NULL) {
+ delete [] theCompletedTransactionsArray;
+ }//if
+ DBUG_VOID_RETURN;
+}//Ndb::releaseTransactionArrays()
+
+void
+Ndb::executeMessage(void* NdbObject,
+ NdbApiSignal * aSignal,
+ LinearSectionPtr ptr[3])
+{
+ Ndb* tNdb = (Ndb*)NdbObject;
+ tNdb->handleReceivedSignal(aSignal, ptr);
+}
+
+void Ndb::connected(Uint32 ref)
+{
+ theMyRef= ref;
+ Uint32 tmpTheNode= refToNode(ref);
+ Uint64 tBlockNo= refToBlock(ref);
+ if (theNdbBlockNumber >= 0){
+ assert(theMyRef == numberToRef(theNdbBlockNumber, tmpTheNode));
+ }
+
+ TransporterFacade * theFacade = TransporterFacade::instance();
+ int i, n= 0;
+ for (i = 1; i < MAX_NDB_NODES; i++){
+ if (theFacade->getIsDbNode(i)){
+ theImpl->theDBnodes[n] = i;
+ n++;
+ }
+ }
+ theImpl->theNoOfDBnodes= n;
+ theFirstTransId = ((Uint64)tBlockNo << 52)+
+ ((Uint64)tmpTheNode << 40);
+ theFirstTransId += theFacade->m_max_trans_id;
+ // assert(0);
+ DBUG_PRINT("info",("connected with ref=%x, id=%d, no_db_nodes=%d, first_trans_id=%lx",
+ theMyRef,
+ tmpTheNode,
+ theImpl->theNoOfDBnodes,
+ theFirstTransId));
+ theCommitAckSignal = new NdbApiSignal(theMyRef);
+
+ theDictionary->m_receiver.m_reference= theMyRef;
+ theNode= tmpTheNode; // flag that Ndb object is initialized
+}
+
+void
+Ndb::statusMessage(void* NdbObject, Uint32 a_node, bool alive, bool nfComplete)
+{
+ DBUG_ENTER("Ndb::statusMessage");
+ Ndb* tNdb = (Ndb*)NdbObject;
+ if (alive) {
+ if (nfComplete) {
+ tNdb->connected(a_node);
+ DBUG_VOID_RETURN;
+ }//if
+ } else {
+ if (nfComplete) {
+ tNdb->report_node_failure_completed(a_node);
+ } else {
+ tNdb->report_node_failure(a_node);
+ }//if
+ }//if
+ NdbDictInterface::execNodeStatus(&tNdb->theDictionary->m_receiver,
+ a_node, alive, nfComplete);
+ DBUG_VOID_RETURN;
+}
+
+void
+Ndb::report_node_failure(Uint32 node_id)
+{
+ /**
+ * We can only set the state here since this object can execute
+ * simultaneously.
+ *
+ * This method is only called by ClusterMgr (via lots of methods)
+ */
+ theImpl->the_release_ind[node_id] = 1;
+ // must come after
+ theImpl->the_release_ind[0] = 1;
+ theImpl->theWaiter.nodeFail(node_id);
+ return;
+}//Ndb::report_node_failure()
+
+
+void
+Ndb::report_node_failure_completed(Uint32 node_id)
+{
+ abortTransactionsAfterNodeFailure(node_id);
+
+}//Ndb::report_node_failure_completed()
+
+/***************************************************************************
+void abortTransactionsAfterNodeFailure();
+
+Remark: Abort all transactions in theSentTransactionsArray after connection
+ to one node has failed
+****************************************************************************/
+void
+Ndb::abortTransactionsAfterNodeFailure(Uint16 aNodeId)
+{
+ Uint32 tNoSentTransactions = theNoOfSentTransactions;
+ for (int i = tNoSentTransactions - 1; i >= 0; i--) {
+ NdbTransaction* localCon = theSentTransactionsArray[i];
+ if (localCon->getConnectedNodeId() == aNodeId) {
+ const NdbTransaction::SendStatusType sendStatus = localCon->theSendStatus;
+ if (sendStatus == NdbTransaction::sendTC_OP ||
+ sendStatus == NdbTransaction::sendTC_COMMIT) {
+ /*
+ A transaction was interrupted in the prepare phase by a node
+ failure. Since the transaction was not found in the phase
+ after the node failure it cannot have been committed and
+ we report a normal node failure abort.
+ */
+ localCon->setOperationErrorCodeAbort(4010);
+ localCon->theCompletionStatus = NdbTransaction::CompletedFailure;
+ } else if (sendStatus == NdbTransaction::sendTC_ROLLBACK) {
+ /*
+ We aimed for abort and abort we got even if it was by a node
+ failure. We will thus report it as a success.
+ */
+ localCon->theCompletionStatus = NdbTransaction::CompletedSuccess;
+ } else {
+#ifdef VM_TRACE
+ printState("abortTransactionsAfterNodeFailure %x", this);
+ abort();
+#endif
+ }
+ /*
+ All transactions arriving here have no connection to the kernel
+ intact since the node was failing and they were aborted. Thus we
+ set commit state to Aborted and set state to release on close.
+ */
+ localCon->theReturnStatus = NdbTransaction::ReturnFailure;
+ localCon->theCommitStatus = NdbTransaction::Aborted;
+ localCon->theReleaseOnClose = true;
+ completedTransaction(localCon);
+ }
+ else if(localCon->report_node_failure(aNodeId))
+ {
+ completedTransaction(localCon);
+ }
+ }//for
+ return;
+}//Ndb::abortTransactionsAfterNodeFailure()
+
+/****************************************************************************
+void handleReceivedSignal(NdbApiSignal* aSignal);
+
+Parameters: aSignal: The signal object.
+Remark: Send all operations belonging to this connection.
+*****************************************************************************/
+void
+Ndb::handleReceivedSignal(NdbApiSignal* aSignal, LinearSectionPtr ptr[3])
+{
+ NdbOperation* tOp;
+ NdbIndexOperation* tIndexOp;
+ NdbTransaction* tCon;
+ int tReturnCode = -1;
+ const Uint32* tDataPtr = aSignal->getDataPtr();
+ const Uint32 tWaitState = theImpl->theWaiter.m_state;
+ const Uint32 tSignalNumber = aSignal->readSignalNumber();
+ const Uint32 tFirstData = *tDataPtr;
+ const Uint32 tLen = aSignal->getLength();
+ void * tFirstDataPtr;
+
+ /*
+ In order to support 64 bit processes in the application we need to use
+ id's rather than a direct pointer to the object used. It is also a good
+ idea that one cannot corrupt the application code by sending a corrupt
+ memory pointer.
+
+ All signals received by the API requires the first data word to be such
+ an id to the receiving object.
+ */
+
+ switch (tSignalNumber){
+ case GSN_TCKEYCONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ const TcKeyConf * const keyConf = (TcKeyConf *)tDataPtr;
+ const BlockReference aTCRef = aSignal->theSendersBlockRef;
+
+ tCon = void2con(tFirstDataPtr);
+ if ((tCon->checkMagicNumber() == 0) &&
+ (tCon->theSendStatus == NdbTransaction::sendTC_OP)) {
+ tReturnCode = tCon->receiveTCKEYCONF(keyConf, tLen);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+
+ if(TcKeyConf::getMarkerFlag(keyConf->confInfo)){
+ NdbTransaction::sendTC_COMMIT_ACK(theCommitAckSignal,
+ keyConf->transId1,
+ keyConf->transId2,
+ aTCRef);
+ }
+
+ return;
+ }//if
+ goto InvalidSignal;
+
+ return;
+ }
+ case GSN_TRANSID_AI:{
+ tFirstDataPtr = int2void(tFirstData);
+ NdbReceiver* tRec;
+ if (tFirstDataPtr && (tRec = void2rec(tFirstDataPtr)) &&
+ tRec->checkMagicNumber() && (tCon = tRec->getTransaction()) &&
+ tCon->checkState_TransId(((const TransIdAI*)tDataPtr)->transId)){
+ Uint32 com;
+ if(aSignal->m_noOfSections > 0){
+ com = tRec->execTRANSID_AI(ptr[0].p, ptr[0].sz);
+ } else {
+ com = tRec->execTRANSID_AI(tDataPtr + TransIdAI::HeaderLength,
+ tLen - TransIdAI::HeaderLength);
+ }
+
+ if(com == 0)
+ return;
+
+ switch(tRec->getType()){
+ case NdbReceiver::NDB_OPERATION:
+ case NdbReceiver::NDB_INDEX_OPERATION:
+ if(tCon->OpCompleteSuccess() != -1){
+ completedTransaction(tCon);
+ }
+ return;
+ case NdbReceiver::NDB_SCANRECEIVER:
+ tCon->theScanningOp->receiver_delivered(tRec);
+ theImpl->theWaiter.m_state = (((WaitSignalType) tWaitState) == WAIT_SCAN ?
+ (Uint32) NO_WAIT : tWaitState);
+ break;
+ default:
+ goto InvalidSignal;
+ }
+ break;
+ } else {
+ /**
+ * This is ok as transaction can have been aborted before TRANSID_AI
+ * arrives (if TUP on other node than TC)
+ */
+ return;
+ }
+ }
+ case GSN_TCKEY_FAILCONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ const TcKeyFailConf * failConf = (TcKeyFailConf *)tDataPtr;
+ const BlockReference aTCRef = aSignal->theSendersBlockRef;
+ if (tFirstDataPtr != 0){
+ tOp = void2rec_op(tFirstDataPtr);
+
+ if (tOp->checkMagicNumber(false) == 0) {
+ tCon = tOp->theNdbCon;
+ if (tCon != NULL) {
+ if ((tCon->theSendStatus == NdbTransaction::sendTC_OP) ||
+ (tCon->theSendStatus == NdbTransaction::sendTC_COMMIT)) {
+ tReturnCode = tCon->receiveTCKEY_FAILCONF(failConf);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ }//if
+ }
+ }
+ } else {
+#ifdef VM_TRACE
+ ndbout_c("Recevied TCKEY_FAILCONF wo/ operation");
+#endif
+ }
+ if(tFirstData & 1){
+ NdbTransaction::sendTC_COMMIT_ACK(theCommitAckSignal,
+ failConf->transId1,
+ failConf->transId2,
+ aTCRef);
+ }
+ return;
+ }
+ case GSN_TCKEY_FAILREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if(tFirstDataPtr != 0){
+ tOp = void2rec_op(tFirstDataPtr);
+ if (tOp->checkMagicNumber() == 0) {
+ tCon = tOp->theNdbCon;
+ if (tCon != NULL) {
+ if ((tCon->theSendStatus == NdbTransaction::sendTC_OP) ||
+ (tCon->theSendStatus == NdbTransaction::sendTC_ROLLBACK)) {
+ tReturnCode = tCon->receiveTCKEY_FAILREF(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ return;
+ }//if
+ }//if
+ }//if
+ }//if
+ } else {
+#ifdef VM_TRACE
+ ndbout_c("Recevied TCKEY_FAILREF wo/ operation");
+#endif
+ }
+ break;
+ }
+ case GSN_TCKEYREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tOp = void2rec_op(tFirstDataPtr);
+ if (tOp->checkMagicNumber() == 0) {
+ tCon = tOp->theNdbCon;
+ if (tCon != NULL) {
+ if (tCon->theSendStatus == NdbTransaction::sendTC_OP) {
+ tReturnCode = tOp->receiveTCKEYREF(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ return;
+ }//if
+ break;
+ }//if
+ }//if
+ } //if
+ goto InvalidSignal;
+ return;
+ }
+ case GSN_TC_COMMITCONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ const TcCommitConf * const commitConf = (TcCommitConf *)tDataPtr;
+ const BlockReference aTCRef = aSignal->theSendersBlockRef;
+
+ tCon = void2con(tFirstDataPtr);
+ if ((tCon->checkMagicNumber() == 0) &&
+ (tCon->theSendStatus == NdbTransaction::sendTC_COMMIT)) {
+ tReturnCode = tCon->receiveTC_COMMITCONF(commitConf);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+
+ if(tFirstData & 1){
+ NdbTransaction::sendTC_COMMIT_ACK(theCommitAckSignal,
+ commitConf->transId1,
+ commitConf->transId2,
+ aTCRef);
+ }
+ return;
+ }
+ goto InvalidSignal;
+ return;
+ }
+
+ case GSN_TC_COMMITREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tCon = void2con(tFirstDataPtr);
+ if ((tCon->checkMagicNumber() == 0) &&
+ (tCon->theSendStatus == NdbTransaction::sendTC_COMMIT)) {
+ tReturnCode = tCon->receiveTC_COMMITREF(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ }//if
+ return;
+ }
+ case GSN_TCROLLBACKCONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tCon = void2con(tFirstDataPtr);
+ if ((tCon->checkMagicNumber() == 0) &&
+ (tCon->theSendStatus == NdbTransaction::sendTC_ROLLBACK)) {
+ tReturnCode = tCon->receiveTCROLLBACKCONF(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ }//if
+ return;
+ }
+ case GSN_TCROLLBACKREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tCon = void2con(tFirstDataPtr);
+ if ((tCon->checkMagicNumber() == 0) &&
+ (tCon->theSendStatus == NdbTransaction::sendTC_ROLLBACK)) {
+ tReturnCode = tCon->receiveTCROLLBACKREF(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ }//if
+ return;
+ }
+ case GSN_TCROLLBACKREP:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tCon = void2con(tFirstDataPtr);
+ if (tCon->checkMagicNumber() == 0) {
+ tReturnCode = tCon->receiveTCROLLBACKREP(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ }//if
+ return;
+ }
+ case GSN_TCSEIZECONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ if (tWaitState != WAIT_TC_SEIZE) {
+ goto InvalidSignal;
+ }//if
+ tCon = void2con(tFirstDataPtr);
+ if (tCon->checkMagicNumber() != 0) {
+ goto InvalidSignal;
+ }//if
+ tReturnCode = tCon->receiveTCSEIZECONF(aSignal);
+ if (tReturnCode != -1) {
+ theImpl->theWaiter.m_state = NO_WAIT;
+ } else {
+ goto InvalidSignal;
+ }//if
+ break;
+ }
+ case GSN_TCSEIZEREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ if (tWaitState != WAIT_TC_SEIZE) {
+ return;
+ }//if
+ tCon = void2con(tFirstDataPtr);
+ if (tCon->checkMagicNumber() != 0) {
+ return;
+ }//if
+ tReturnCode = tCon->receiveTCSEIZEREF(aSignal);
+ if (tReturnCode != -1) {
+ theImpl->theWaiter.m_state = NO_WAIT;
+ } else {
+ return;
+ }//if
+ break;
+ }
+ case GSN_TCRELEASECONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ if (tWaitState != WAIT_TC_RELEASE) {
+ goto InvalidSignal;
+ }//if
+ tCon = void2con(tFirstDataPtr);
+ if (tCon->checkMagicNumber() != 0) {
+ goto InvalidSignal;
+ }//if
+ tReturnCode = tCon->receiveTCRELEASECONF(aSignal);
+ if (tReturnCode != -1) {
+ theImpl->theWaiter.m_state = NO_WAIT;
+ }//if
+ break;
+ }
+ case GSN_TCRELEASEREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ if (tWaitState != WAIT_TC_RELEASE) {
+ goto InvalidSignal;
+ }//if
+ tCon = void2con(tFirstDataPtr);
+ if (tCon->checkMagicNumber() != 0) {
+ goto InvalidSignal;
+ }//if
+ tReturnCode = tCon->receiveTCRELEASEREF(aSignal);
+ if (tReturnCode != -1) {
+ theImpl->theWaiter.m_state = NO_WAIT;
+ }//if
+ break;
+ }
+
+ case GSN_GET_TABINFOREF:
+ case GSN_GET_TABINFO_CONF:
+ case GSN_CREATE_TABLE_REF:
+ case GSN_CREATE_TABLE_CONF:
+ case GSN_DROP_TABLE_CONF:
+ case GSN_DROP_TABLE_REF:
+ case GSN_ALTER_TABLE_CONF:
+ case GSN_ALTER_TABLE_REF:
+ case GSN_CREATE_INDX_CONF:
+ case GSN_CREATE_INDX_REF:
+ case GSN_DROP_INDX_CONF:
+ case GSN_DROP_INDX_REF:
+ case GSN_CREATE_EVNT_CONF:
+ case GSN_CREATE_EVNT_REF:
+ case GSN_DROP_EVNT_CONF:
+ case GSN_DROP_EVNT_REF:
+ case GSN_LIST_TABLES_CONF:
+ NdbDictInterface::execSignal(&theDictionary->m_receiver,
+ aSignal, ptr);
+ break;
+
+ case GSN_SUB_META_DATA:
+ case GSN_SUB_REMOVE_CONF:
+ case GSN_SUB_REMOVE_REF:
+ break; // ignore these signals
+ case GSN_SUB_GCP_COMPLETE_REP:
+ case GSN_SUB_START_CONF:
+ case GSN_SUB_START_REF:
+ case GSN_SUB_TABLE_DATA:
+ case GSN_SUB_STOP_CONF:
+ case GSN_SUB_STOP_REF:
+ NdbDictInterface::execSignal(&theDictionary->m_receiver,
+ aSignal, ptr);
+ break;
+
+ case GSN_DIHNDBTAMPER:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ if (tWaitState != WAIT_NDB_TAMPER)
+ return;
+ tCon = void2con(tFirstDataPtr);
+ if (tCon->checkMagicNumber() != 0)
+ return;
+ tReturnCode = tCon->receiveDIHNDBTAMPER(aSignal);
+ if (tReturnCode != -1)
+ theImpl->theWaiter.m_state = NO_WAIT;
+ break;
+ }
+ case GSN_SCAN_TABCONF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ assert(tFirstDataPtr);
+ assert(void2con(tFirstDataPtr));
+ assert(void2con(tFirstDataPtr)->checkMagicNumber() == 0);
+ if(tFirstDataPtr &&
+ (tCon = void2con(tFirstDataPtr)) && (tCon->checkMagicNumber() == 0)){
+
+ if(aSignal->m_noOfSections > 0){
+ tReturnCode = tCon->receiveSCAN_TABCONF(aSignal,
+ ptr[0].p, ptr[0].sz);
+ } else {
+ tReturnCode =
+ tCon->receiveSCAN_TABCONF(aSignal,
+ tDataPtr + ScanTabConf::SignalLength,
+ tLen - ScanTabConf::SignalLength);
+ }
+ if (tReturnCode != -1 && tWaitState == WAIT_SCAN)
+ theImpl->theWaiter.m_state = NO_WAIT;
+ break;
+ } else {
+ goto InvalidSignal;
+ }
+ }
+ case GSN_SCAN_TABREF:
+ {
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tCon = void2con(tFirstDataPtr);
+
+ assert(tFirstDataPtr != 0 &&
+ void2con(tFirstDataPtr)->checkMagicNumber() == 0);
+
+ if (tCon->checkMagicNumber() == 0){
+ tReturnCode = tCon->receiveSCAN_TABREF(aSignal);
+ if (tReturnCode != -1 && tWaitState == WAIT_SCAN){
+ theImpl->theWaiter.m_state = NO_WAIT;
+ }
+ break;
+ }
+ goto InvalidSignal;
+ }
+ case GSN_KEYINFO20: {
+ tFirstDataPtr = int2void(tFirstData);
+ NdbReceiver* tRec;
+ if (tFirstDataPtr && (tRec = void2rec(tFirstDataPtr)) &&
+ tRec->checkMagicNumber() && (tCon = tRec->getTransaction()) &&
+ tCon->checkState_TransId(&((const KeyInfo20*)tDataPtr)->transId1)){
+
+ Uint32 len = ((const KeyInfo20*)tDataPtr)->keyLen;
+ Uint32 info = ((const KeyInfo20*)tDataPtr)->scanInfo_Node;
+ int com = -1;
+ if(aSignal->m_noOfSections > 0 && len == ptr[0].sz){
+ com = tRec->execKEYINFO20(info, ptr[0].p, len);
+ } else if(len == tLen - KeyInfo20::HeaderLength){
+ com = tRec->execKEYINFO20(info, tDataPtr+KeyInfo20::HeaderLength, len);
+ }
+
+ switch(com){
+ case 1:
+ tCon->theScanningOp->receiver_delivered(tRec);
+ theImpl->theWaiter.m_state = (((WaitSignalType) tWaitState) == WAIT_SCAN ?
+ (Uint32) NO_WAIT : tWaitState);
+ break;
+ case 0:
+ break;
+ case -1:
+ goto InvalidSignal;
+ }
+ break;
+ } else {
+ /**
+ * This is ok as transaction can have been aborted before KEYINFO20
+ * arrives (if TUP on other node than TC)
+ */
+ return;
+ }
+ }
+ case GSN_TCINDXCONF:{
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ const TcIndxConf * const indxConf = (TcIndxConf *)tDataPtr;
+ const BlockReference aTCRef = aSignal->theSendersBlockRef;
+ tCon = void2con(tFirstDataPtr);
+ if ((tCon->checkMagicNumber() == 0) &&
+ (tCon->theSendStatus == NdbTransaction::sendTC_OP)) {
+ tReturnCode = tCon->receiveTCINDXCONF(indxConf, tLen);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ }//if
+
+ if(TcIndxConf::getMarkerFlag(indxConf->confInfo)){
+ NdbTransaction::sendTC_COMMIT_ACK(theCommitAckSignal,
+ indxConf->transId1,
+ indxConf->transId2,
+ aTCRef);
+ }
+ return;
+ }
+ case GSN_TCINDXREF:{
+ tFirstDataPtr = int2void(tFirstData);
+ if (tFirstDataPtr == 0) goto InvalidSignal;
+
+ tIndexOp = void2rec_iop(tFirstDataPtr);
+ if (tIndexOp->checkMagicNumber() == 0) {
+ tCon = tIndexOp->theNdbCon;
+ if (tCon != NULL) {
+ if (tCon->theSendStatus == NdbTransaction::sendTC_OP) {
+ tReturnCode = tIndexOp->receiveTCINDXREF(aSignal);
+ if (tReturnCode != -1) {
+ completedTransaction(tCon);
+ }//if
+ return;
+ }//if
+ }//if
+ }//if
+ goto InvalidSignal;
+ return;
+ }
+ default:
+ goto InvalidSignal;
+ }//switch
+
+ if (theImpl->theWaiter.m_state == NO_WAIT) {
+ // Wake up the thread waiting for response
+ NdbCondition_Signal(theImpl->theWaiter.m_condition);
+ }//if
+ return;
+
+ InvalidSignal:
+#ifdef VM_TRACE
+ ndbout_c("Ndbif: Error Ndb::handleReceivedSignal "
+ "(GSN=%d, theImpl->theWaiter.m_state=%d)"
+ " sender = (Block: %d Node: %d)",
+ tSignalNumber,
+ tWaitState,
+ refToBlock(aSignal->theSendersBlockRef),
+ refToNode(aSignal->theSendersBlockRef));
+#endif
+#ifdef NDB_NO_DROPPED_SIGNAL
+ abort();
+#endif
+
+ return;
+}//Ndb::handleReceivedSignal()
+
+
+/*****************************************************************************
+void completedTransaction(NdbTransaction* aCon);
+
+Remark: One transaction has been completed.
+ Remove it from send array and put it into the completed
+ transaction array. Finally check if it is time to wake
+ up a poller.
+******************************************************************************/
+void
+Ndb::completedTransaction(NdbTransaction* aCon)
+{
+ Uint32 tTransArrayIndex = aCon->theTransArrayIndex;
+ Uint32 tNoSentTransactions = theNoOfSentTransactions;
+ Uint32 tNoCompletedTransactions = theNoOfCompletedTransactions;
+ if ((tNoSentTransactions > 0) && (aCon->theListState == NdbTransaction::InSendList) &&
+ (tTransArrayIndex < tNoSentTransactions)) {
+ NdbTransaction* tMoveCon = theSentTransactionsArray[tNoSentTransactions - 1];
+
+ theCompletedTransactionsArray[tNoCompletedTransactions] = aCon;
+ aCon->theTransArrayIndex = tNoCompletedTransactions;
+ if (tMoveCon != aCon) {
+ tMoveCon->theTransArrayIndex = tTransArrayIndex;
+ theSentTransactionsArray[tTransArrayIndex] = tMoveCon;
+ }//if
+ theSentTransactionsArray[tNoSentTransactions - 1] = NULL;
+ theNoOfCompletedTransactions = tNoCompletedTransactions + 1;
+
+ theNoOfSentTransactions = tNoSentTransactions - 1;
+ aCon->theListState = NdbTransaction::InCompletedList;
+ aCon->handleExecuteCompletion();
+ if ((theMinNoOfEventsToWakeUp != 0) &&
+ (theNoOfCompletedTransactions >= theMinNoOfEventsToWakeUp)) {
+ theMinNoOfEventsToWakeUp = 0;
+ NdbCondition_Signal(theImpl->theWaiter.m_condition);
+ return;
+ }//if
+ } else {
+ ndbout << "theNoOfSentTransactions = " << (int) theNoOfSentTransactions;
+ ndbout << " theListState = " << (int) aCon->theListState;
+ ndbout << " theTransArrayIndex = " << aCon->theTransArrayIndex;
+ ndbout << endl << flush;
+#ifdef VM_TRACE
+ printState("completedTransaction abort");
+ abort();
+#endif
+ }//if
+}//Ndb::completedTransaction()
+
+/*****************************************************************************
+void reportCallback(NdbTransaction** aCopyArray, Uint32 aNoOfCompletedTrans);
+
+Remark: Call the callback methods of the completed transactions.
+******************************************************************************/
+void
+Ndb::reportCallback(NdbTransaction** aCopyArray, Uint32 aNoOfCompletedTrans)
+{
+ Uint32 i;
+ if (aNoOfCompletedTrans > 0) {
+ for (i = 0; i < aNoOfCompletedTrans; i++) {
+ void* anyObject = aCopyArray[i]->theCallbackObject;
+ NdbAsynchCallback aCallback = aCopyArray[i]->theCallbackFunction;
+ int tResult = 0;
+ if (aCallback != NULL) {
+ if (aCopyArray[i]->theReturnStatus == NdbTransaction::ReturnFailure) {
+ tResult = -1;
+ }//if
+ (*aCallback)(tResult, aCopyArray[i], anyObject);
+ }//if
+ }//for
+ }//if
+}//Ndb::reportCallback()
+
+/*****************************************************************************
+Uint32 pollCompleted(NdbTransaction** aCopyArray);
+
+Remark: Transfer the data from the completed transaction to a local array.
+ This support is used by a number of the poll-methods.
+******************************************************************************/
+Uint32
+Ndb::pollCompleted(NdbTransaction** aCopyArray)
+{
+ check_send_timeout();
+ Uint32 i;
+ Uint32 tNoCompletedTransactions = theNoOfCompletedTransactions;
+ if (tNoCompletedTransactions > 0) {
+ for (i = 0; i < tNoCompletedTransactions; i++) {
+ aCopyArray[i] = theCompletedTransactionsArray[i];
+ if (aCopyArray[i]->theListState != NdbTransaction::InCompletedList) {
+ ndbout << "pollCompleted error ";
+ ndbout << (int) aCopyArray[i]->theListState << endl;
+ abort();
+ }//if
+ theCompletedTransactionsArray[i] = NULL;
+ aCopyArray[i]->theListState = NdbTransaction::NotInList;
+ }//for
+ }//if
+ theNoOfCompletedTransactions = 0;
+ return tNoCompletedTransactions;
+}//Ndb::pollCompleted()
+
+void
+Ndb::check_send_timeout()
+{
+ NDB_TICKS current_time = NdbTick_CurrentMillisecond();
+ if (current_time - the_last_check_time > 1000) {
+ the_last_check_time = current_time;
+ Uint32 no_of_sent = theNoOfSentTransactions;
+ for (Uint32 i = 0; i < no_of_sent; i++) {
+ NdbTransaction* a_con = theSentTransactionsArray[i];
+ if ((current_time - a_con->theStartTransTime) >
+ WAITFOR_RESPONSE_TIMEOUT) {
+#ifdef VM_TRACE
+ a_con->printState();
+ Uint32 t1 = a_con->theTransactionId;
+ Uint32 t2 = a_con->theTransactionId >> 32;
+ ndbout_c("[%.8x %.8x]", t1, t2);
+ abort();
+#endif
+ a_con->setOperationErrorCodeAbort(4012);
+ a_con->theCommitStatus = NdbTransaction::Aborted;
+ a_con->theCompletionStatus = NdbTransaction::CompletedFailure;
+ a_con->handleExecuteCompletion();
+ remove_sent_list(i);
+ insert_completed_list(a_con);
+ no_of_sent--;
+ i--;
+ }//if
+ }//for
+ }//if
+}
+
+void
+Ndb::remove_sent_list(Uint32 list_index)
+{
+ Uint32 last_index = theNoOfSentTransactions - 1;
+ if (list_index < last_index) {
+ NdbTransaction* t_con = theSentTransactionsArray[last_index];
+ theSentTransactionsArray[list_index] = t_con;
+ }//if
+ theNoOfSentTransactions = last_index;
+ theSentTransactionsArray[last_index] = 0;
+}
+
+Uint32
+Ndb::insert_completed_list(NdbTransaction* a_con)
+{
+ Uint32 no_of_comp = theNoOfCompletedTransactions;
+ theCompletedTransactionsArray[no_of_comp] = a_con;
+ theNoOfCompletedTransactions = no_of_comp + 1;
+ a_con->theListState = NdbTransaction::InCompletedList;
+ a_con->theTransArrayIndex = no_of_comp;
+ return no_of_comp;
+}
+
+Uint32
+Ndb::insert_sent_list(NdbTransaction* a_con)
+{
+ Uint32 no_of_sent = theNoOfSentTransactions;
+ theSentTransactionsArray[no_of_sent] = a_con;
+ theNoOfSentTransactions = no_of_sent + 1;
+ a_con->theListState = NdbTransaction::InSendList;
+ a_con->theTransArrayIndex = no_of_sent;
+ return no_of_sent;
+}
+
+/*****************************************************************************
+void sendPrepTrans(int forceSend);
+
+Remark: Send a batch of transactions prepared for sending to the NDB kernel.
+******************************************************************************/
+void
+Ndb::sendPrepTrans(int forceSend)
+{
+ // Always called when holding mutex on TransporterFacade
+ /*
+ We will send a list of transactions to the NDB kernel. Before
+ sending we check the following.
+ 1) Node connected to is still alive
+ Checked by both checking node status and node sequence
+ 2) Send buffer can handle the size of messages we are planning to send
+ So far this is just a fake check but will soon be a real check
+ When the connected node has failed we abort the transaction without
+ responding anymore to the node since the kernel will clean up
+ automatically.
+ When sendBuffer cannot handle anymore messages then we will also abort
+ transaction but by communicating to the kernel since it is still alive
+ and we keep a small space for messages like that.
+ */
+ Uint32 i;
+ TransporterFacade* tp = TransporterFacade::instance();
+ Uint32 no_of_prep_trans = theNoOfPreparedTransactions;
+ for (i = 0; i < no_of_prep_trans; i++) {
+ NdbTransaction * a_con = thePreparedTransactionsArray[i];
+ thePreparedTransactionsArray[i] = NULL;
+ Uint32 node_id = a_con->getConnectedNodeId();
+ if ((tp->getNodeSequence(node_id) == a_con->theNodeSequence) &&
+ tp->get_node_alive(node_id) ||
+ (tp->get_node_stopping(node_id) &&
+ ((a_con->theSendStatus == NdbTransaction::sendABORT) ||
+ (a_con->theSendStatus == NdbTransaction::sendABORTfail) ||
+ (a_con->theSendStatus == NdbTransaction::sendCOMMITstate) ||
+ (a_con->theSendStatus == NdbTransaction::sendCompleted)))) {
+ /*
+ We will send if
+ 1) Node is alive and sequences are correct OR
+ 2) Node is stopping and we only want to commit or abort
+ In a graceful stop situation we want to ensure quick aborts
+ of all transactions and commits and thus we allow aborts and
+ commits to continue but not normal operations.
+ */
+ if (tp->check_send_size(node_id, a_con->get_send_size())) {
+ if (a_con->doSend() == 0) {
+ NDB_TICKS current_time = NdbTick_CurrentMillisecond();
+ a_con->theStartTransTime = current_time;
+ continue;
+ } else {
+ /*
+ Although all precautions we did not manage to send the operations
+ Must have been a dropped connection on the transporter side.
+ We don't expect to be able to continue using this connection so
+ we will treat it as a node failure.
+ */
+ TRACE_DEBUG("Send problem even after checking node status");
+ }//if
+ } else {
+ /*
+ The send buffer is currently full or at least close to. We will
+ not allow a send to continue. We will set the connection so that
+ it is indicated that we need to abort the transaction. If we were
+ trying to commit or abort and got a send buffer we will not try
+ again and will thus set the state to Aborted to avoid a more or
+ less eternal loop of tries.
+ */
+ if (a_con->theSendStatus == NdbTransaction::sendOperations) {
+ a_con->setOperationErrorCodeAbort(4021);
+ a_con->theCommitStatus = NdbTransaction::NeedAbort;
+ TRACE_DEBUG("Send buffer full and sendOperations");
+ } else {
+ a_con->setOperationErrorCodeAbort(4026);
+ a_con->theCommitStatus = NdbTransaction::Aborted;
+ TRACE_DEBUG("Send buffer full, set state to Aborted");
+ }//if
+ }//if
+ } else {
+#ifdef VM_TRACE
+ a_con->printState();
+#endif
+ if ((tp->getNodeSequence(node_id) == a_con->theNodeSequence) &&
+ tp->get_node_stopping(node_id)) {
+ /*
+ The node we are connected to is currently in an early stopping phase
+ of a graceful stop. We will not send the prepared transactions. We
+ will simply refuse and let the application code handle the abort.
+ */
+ TRACE_DEBUG("Abort a transaction when stopping a node");
+ a_con->setOperationErrorCodeAbort(4023);
+ a_con->theCommitStatus = NdbTransaction::NeedAbort;
+ } else {
+ /*
+ The node is hard dead and we cannot continue. We will also release
+ the connection to the free pool.
+ */
+ TRACE_DEBUG("The node was stone dead, inform about abort");
+ a_con->setOperationErrorCodeAbort(4025);
+ a_con->theReleaseOnClose = true;
+ a_con->theTransactionIsStarted = false;
+ a_con->theCommitStatus = NdbTransaction::Aborted;
+ }//if
+ }//if
+ a_con->theReturnStatus = NdbTransaction::ReturnFailure;
+ a_con->theCompletionStatus = NdbTransaction::CompletedFailure;
+ a_con->handleExecuteCompletion();
+ insert_completed_list(a_con);
+ }//for
+ theNoOfPreparedTransactions = 0;
+ if (forceSend == 0) {
+ tp->checkForceSend(theNdbBlockNumber);
+ } else if (forceSend == 1) {
+ tp->forceSend(theNdbBlockNumber);
+ }//if
+ return;
+}//Ndb::sendPrepTrans()
+
+/*****************************************************************************
+void waitCompletedTransactions(int aMilliSecondsToWait, int noOfEventsToWaitFor);
+
+Remark: First send all prepared operations and then check if there are any
+ transactions already completed. Do not wait for not completed
+ transactions.
+******************************************************************************/
+void
+Ndb::waitCompletedTransactions(int aMilliSecondsToWait,
+ int noOfEventsToWaitFor)
+{
+ theImpl->theWaiter.m_state = NO_WAIT;
+ /**
+ * theImpl->theWaiter.m_state = NO_WAIT;
+ * To ensure no messup with synchronous node fail handling
+ * (see ReportFailure)
+ */
+ int waitTime = aMilliSecondsToWait;
+ NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + (NDB_TICKS)waitTime;
+ theMinNoOfEventsToWakeUp = noOfEventsToWaitFor;
+ do {
+ if (waitTime < 1000) waitTime = 1000;
+ NdbCondition_WaitTimeout(theImpl->theWaiter.m_condition,
+ (NdbMutex*)theImpl->theWaiter.m_mutex,
+ waitTime);
+ if (theNoOfCompletedTransactions >= (Uint32)noOfEventsToWaitFor) {
+ break;
+ }//if
+ theMinNoOfEventsToWakeUp = noOfEventsToWaitFor;
+ waitTime = (int)(maxTime - NdbTick_CurrentMillisecond());
+ } while (waitTime > 0);
+ return;
+}//Ndb::waitCompletedTransactions()
+
+/*****************************************************************************
+void sendPreparedTransactions(int forceSend = 0);
+
+Remark: First send all prepared operations and then check if there are any
+ transactions already completed. Do not wait for not completed
+ transactions.
+******************************************************************************/
+void
+Ndb::sendPreparedTransactions(int forceSend)
+{
+ TransporterFacade::instance()->lock_mutex();
+ sendPrepTrans(forceSend);
+ TransporterFacade::instance()->unlock_mutex();
+ return;
+}//Ndb::sendPreparedTransactions()
+
+/*****************************************************************************
+int sendPollNdb(int aMillisecondNumber, int minNoOfEventsToWakeup = 1, int forceSend = 0);
+
+Remark: First send all prepared operations and then check if there are any
+ transactions already completed. Wait for not completed
+ transactions until the specified number have completed or until the
+ timeout has occured. Timeout zero means no waiting time.
+******************************************************************************/
+int
+Ndb::sendPollNdb(int aMillisecondNumber, int minNoOfEventsToWakeup, int forceSend)
+{
+ NdbTransaction* tConArray[1024];
+ Uint32 tNoCompletedTransactions;
+
+ //theCurrentConnectCounter = 0;
+ //theCurrentConnectIndex++;
+ TransporterFacade::instance()->lock_mutex();
+ sendPrepTrans(forceSend);
+ if ((minNoOfEventsToWakeup <= 0) ||
+ ((Uint32)minNoOfEventsToWakeup > theNoOfSentTransactions)) {
+ minNoOfEventsToWakeup = theNoOfSentTransactions;
+ }//if
+ if ((theNoOfCompletedTransactions < (Uint32)minNoOfEventsToWakeup) &&
+ (aMillisecondNumber > 0)) {
+ waitCompletedTransactions(aMillisecondNumber, minNoOfEventsToWakeup);
+ tNoCompletedTransactions = pollCompleted(tConArray);
+ } else {
+ tNoCompletedTransactions = pollCompleted(tConArray);
+ }//if
+ TransporterFacade::instance()->unlock_mutex();
+ reportCallback(tConArray, tNoCompletedTransactions);
+ return tNoCompletedTransactions;
+}//Ndb::sendPollNdb()
+
+/*****************************************************************************
+int pollNdb(int aMillisecondNumber, int minNoOfEventsToWakeup);
+
+Remark: Check if there are any transactions already completed. Wait for not
+ completed transactions until the specified number have completed or
+ until the timeout has occured. Timeout zero means no waiting time.
+******************************************************************************/
+int
+Ndb::pollNdb(int aMillisecondNumber, int minNoOfEventsToWakeup)
+{
+ NdbTransaction* tConArray[1024];
+ Uint32 tNoCompletedTransactions;
+
+ //theCurrentConnectCounter = 0;
+ //theCurrentConnectIndex++;
+ TransporterFacade::instance()->lock_mutex();
+ if ((minNoOfEventsToWakeup == 0) ||
+ ((Uint32)minNoOfEventsToWakeup > theNoOfSentTransactions)) {
+ minNoOfEventsToWakeup = theNoOfSentTransactions;
+ }//if
+ if ((theNoOfCompletedTransactions < (Uint32)minNoOfEventsToWakeup) &&
+ (aMillisecondNumber > 0)) {
+ waitCompletedTransactions(aMillisecondNumber, minNoOfEventsToWakeup);
+ tNoCompletedTransactions = pollCompleted(tConArray);
+ } else {
+ tNoCompletedTransactions = pollCompleted(tConArray);
+ }//if
+ TransporterFacade::instance()->unlock_mutex();
+ reportCallback(tConArray, tNoCompletedTransactions);
+ return tNoCompletedTransactions;
+}//Ndb::sendPollNdbWithoutWait()
+
+/*****************************************************************************
+int receiveOptimisedResponse();
+
+Return: 0 - Response received
+ -1 - Timeout occured waiting for response
+ -2 - Node failure interupted wait for response
+
+******************************************************************************/
+int
+Ndb::receiveResponse(int waitTime){
+ int tResultCode;
+ TransporterFacade::instance()->checkForceSend(theNdbBlockNumber);
+
+ theImpl->theWaiter.wait(waitTime);
+
+ if(theImpl->theWaiter.m_state == NO_WAIT) {
+ tResultCode = 0;
+ } else {
+
+#ifdef VM_TRACE
+ ndbout << "ERR: receiveResponse - theImpl->theWaiter.m_state = ";
+ ndbout << theImpl->theWaiter.m_state << endl;
+#endif
+
+ if (theImpl->theWaiter.m_state == WAIT_NODE_FAILURE){
+ tResultCode = -2;
+ } else {
+ tResultCode = -1;
+ }
+ theImpl->theWaiter.m_state = NO_WAIT;
+ }
+ return tResultCode;
+}//Ndb::receiveResponse()
+
+int
+Ndb::sendRecSignal(Uint16 node_id,
+ Uint32 aWaitState,
+ NdbApiSignal* aSignal,
+ Uint32 conn_seq)
+{
+ /*
+ In most situations 0 is returned.
+ In error cases we have 5 different cases
+ -1: Send ok, time out in waiting for reply
+ -2: Node has failed
+ -3: Send buffer not full, send failed yet
+ -4: Send buffer full
+ -5: Node is currently stopping
+ */
+
+ int return_code;
+ TransporterFacade* tp = TransporterFacade::instance();
+ Uint32 send_size = 1; // Always sends one signal only
+ tp->lock_mutex();
+ // Protected area
+ if ((tp->get_node_alive(node_id)) &&
+ ((tp->getNodeSequence(node_id) == conn_seq) ||
+ (conn_seq == 0))) {
+ if (tp->check_send_size(node_id, send_size)) {
+ return_code = tp->sendSignal(aSignal, node_id);
+ if (return_code != -1) {
+ theImpl->theWaiter.m_node = node_id;
+ theImpl->theWaiter.m_state = aWaitState;
+ return_code = receiveResponse();
+ } else {
+ return_code = -3;
+ }
+ } else {
+ return_code = -4;
+ }//if
+ } else {
+ if ((tp->get_node_stopping(node_id)) &&
+ ((tp->getNodeSequence(node_id) == conn_seq) ||
+ (conn_seq == 0))) {
+ return_code = -5;
+ } else {
+ return_code = -2;
+ }//if
+ }//if
+ tp->unlock_mutex();
+ // End of protected area
+ return return_code;
+}//Ndb::sendRecSignal()
+
+void
+NdbTransaction::sendTC_COMMIT_ACK(NdbApiSignal * aSignal,
+ Uint32 transId1, Uint32 transId2,
+ Uint32 aTCRef){
+#ifdef MARKER_TRACE
+ ndbout_c("Sending TC_COMMIT_ACK(0x%.8x, 0x%.8x) to -> %d",
+ transId1,
+ transId2,
+ refToNode(aTCRef));
+#endif
+ TransporterFacade *tp = TransporterFacade::instance();
+ aSignal->theTrace = TestOrd::TraceAPI;
+ aSignal->theReceiversBlockNumber = DBTC;
+ aSignal->theVerId_signalNumber = GSN_TC_COMMIT_ACK;
+ aSignal->theLength = 2;
+
+ Uint32 * dataPtr = aSignal->getDataPtrSend();
+ dataPtr[0] = transId1;
+ dataPtr[1] = transId2;
+
+ tp->sendSignal(aSignal, refToNode(aTCRef));
+}
diff --git a/storage/ndb/src/ndbapi/Ndbinit.cpp b/storage/ndb/src/ndbapi/Ndbinit.cpp
new file mode 100644
index 00000000000..4db9d05b59c
--- /dev/null
+++ b/storage/ndb/src/ndbapi/Ndbinit.cpp
@@ -0,0 +1,254 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "NdbApiSignal.hpp"
+#include "NdbImpl.hpp"
+#include <NdbOperation.hpp>
+#include <NdbTransaction.hpp>
+#include <NdbRecAttr.hpp>
+#include <IPCConfig.hpp>
+#include "TransporterFacade.hpp"
+#include <ConfigRetriever.hpp>
+#include <ndb_limits.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include "ObjectMap.hpp"
+
+class NdbGlobalEventBufferHandle;
+NdbGlobalEventBufferHandle *NdbGlobalEventBuffer_init(int);
+void NdbGlobalEventBuffer_drop(NdbGlobalEventBufferHandle *);
+
+Ndb::Ndb( Ndb_cluster_connection *ndb_cluster_connection,
+ const char* aDataBase , const char* aSchema)
+ : theImpl(NULL)
+{
+ DBUG_ENTER("Ndb::Ndb()");
+ DBUG_PRINT("enter",("Ndb::Ndb this=0x%x", this));
+ setup(ndb_cluster_connection, aDataBase, aSchema);
+ DBUG_VOID_RETURN;
+}
+
+void Ndb::setup(Ndb_cluster_connection *ndb_cluster_connection,
+ const char* aDataBase , const char* aSchema)
+{
+ DBUG_ENTER("Ndb::setup");
+
+ assert(theImpl == NULL);
+ theImpl= new NdbImpl(ndb_cluster_connection,*this);
+ theDictionary= &(theImpl->m_dictionary);
+
+ thePreparedTransactionsArray= NULL;
+ theSentTransactionsArray= NULL;
+ theCompletedTransactionsArray= NULL;
+ theNoOfPreparedTransactions= 0;
+ theNoOfSentTransactions= 0;
+ theNoOfCompletedTransactions= 0;
+ theNoOfAllocatedTransactions= 0;
+ theMaxNoOfTransactions= 0;
+ theMinNoOfEventsToWakeUp= 0;
+ prefixEnd= NULL;
+ theConIdleList= NULL;
+ theOpIdleList= NULL;
+ theScanOpIdleList= NULL;
+ theIndexOpIdleList= NULL;
+ theTransactionList= NULL;
+ theConnectionArray= NULL;
+ theRecAttrIdleList= NULL;
+ theSignalIdleList= NULL;
+ theLabelList= NULL;
+ theBranchList= NULL;
+ theSubroutineList= NULL;
+ theCallList= NULL;
+ theScanList= NULL;
+ theNdbBlobIdleList= NULL;
+ the_last_check_time= 0;
+ theFirstTransId= 0;
+ theRestartGCI= 0;
+ theNdbBlockNumber= -1;
+ theInitState= NotConstructed;
+
+ theNode= 0;
+ theFirstTransId= 0;
+ theMyRef= 0;
+
+ fullyQualifiedNames = true;
+
+#ifdef POORMANSPURIFY
+ cgetSignals =0;
+ cfreeSignals = 0;
+ cnewSignals = 0;
+ creleaseSignals = 0;
+#endif
+
+ theError.code = 0;
+
+ theConnectionArray = new NdbConnection * [MAX_NDB_NODES];
+ theCommitAckSignal = NULL;
+
+ int i;
+ for (i = 0; i < MAX_NDB_NODES ; i++) {
+ theConnectionArray[i] = NULL;
+ }//forg
+ for (i = 0; i < 2048 ; i++) {
+ theFirstTupleId[i] = 0;
+ theLastTupleId[i] = 0;
+ }//for
+
+ BaseString::snprintf(theDataBase, sizeof(theDataBase), "%s",
+ aDataBase ? aDataBase : "");
+ BaseString::snprintf(theDataBaseSchema, sizeof(theDataBaseSchema), "%s",
+ aSchema ? aSchema : "");
+
+ int len = BaseString::snprintf(prefixName, sizeof(prefixName), "%s%c%s%c",
+ theDataBase, table_name_separator,
+ theDataBaseSchema, table_name_separator);
+ prefixEnd = prefixName + (len < (int) sizeof(prefixName) ? len :
+ sizeof(prefixName) - 1);
+
+ theImpl->theWaiter.m_mutex = TransporterFacade::instance()->theMutexPtr;
+
+ // Signal that the constructor has finished OK
+ if (theInitState == NotConstructed)
+ theInitState = NotInitialised;
+
+ {
+ NdbGlobalEventBufferHandle *h=
+ NdbGlobalEventBuffer_init(NDB_MAX_ACTIVE_EVENTS);
+ if (h == NULL) {
+ ndbout_c("Failed NdbGlobalEventBuffer_init(%d)",NDB_MAX_ACTIVE_EVENTS);
+ exit(-1);
+ }
+ theGlobalEventBufferHandle = h;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*****************************************************************************
+ * ~Ndb();
+ *
+ * Remark: Disconnect with the database.
+ *****************************************************************************/
+Ndb::~Ndb()
+{
+ DBUG_ENTER("Ndb::~Ndb()");
+ DBUG_PRINT("enter",("Ndb::~Ndb this=0x%x",this));
+ doDisconnect();
+
+ NdbGlobalEventBuffer_drop(theGlobalEventBufferHandle);
+
+ if (TransporterFacade::instance() != NULL && theNdbBlockNumber > 0){
+ TransporterFacade::instance()->close(theNdbBlockNumber, theFirstTransId);
+ }
+
+// if (theSchemaConToNdbList != NULL)
+// closeSchemaTransaction(theSchemaConToNdbList);
+ while ( theConIdleList != NULL )
+ freeNdbCon();
+ while (theOpIdleList != NULL)
+ freeOperation();
+ while (theScanOpIdleList != NULL)
+ freeScanOperation();
+ while (theIndexOpIdleList != NULL)
+ freeIndexOperation();
+ while (theLabelList != NULL)
+ freeNdbLabel();
+ while (theBranchList != NULL)
+ freeNdbBranch();
+ while (theSubroutineList != NULL)
+ freeNdbSubroutine();
+ while (theCallList != NULL)
+ freeNdbCall();
+ while (theScanList != NULL)
+ freeNdbScanRec();
+ while (theNdbBlobIdleList != NULL)
+ freeNdbBlob();
+ while (theRecAttrIdleList != NULL)
+ freeRecAttr();
+ while ( theSignalIdleList != NULL )
+ freeSignal();
+
+ releaseTransactionArrays();
+
+ delete []theConnectionArray;
+ if(theCommitAckSignal != NULL){
+ delete theCommitAckSignal;
+ theCommitAckSignal = NULL;
+ }
+
+ delete theImpl;
+
+ /**
+ * This sleep is to make sure that the transporter
+ * send thread will come in and send any
+ * signal buffers that this thread may have allocated.
+ * If that doesn't happen an error will occur in OSE
+ * when trying to restore a signal buffer allocated by a thread
+ * that have been killed.
+ */
+#ifdef NDB_OSE
+ NdbSleep_MilliSleep(50);
+#endif
+
+#ifdef POORMANSPURIFY
+#ifdef POORMANSGUI
+ ndbout << "cnewSignals=" << cnewSignals << endl;
+ ndbout << "cfreeSignals=" << cfreeSignals << endl;
+ ndbout << "cgetSignals=" << cgetSignals << endl;
+ ndbout << "creleaseSignals=" << creleaseSignals << endl;
+#endif
+ // Poor mans purifier
+ assert(cnewSignals == cfreeSignals);
+ assert(cgetSignals == creleaseSignals);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+NdbWaiter::NdbWaiter(){
+ m_node = 0;
+ m_state = NO_WAIT;
+ m_mutex = 0;
+ m_condition = NdbCondition_Create();
+}
+
+NdbWaiter::~NdbWaiter(){
+ NdbCondition_Destroy(m_condition);
+}
+
+NdbImpl::NdbImpl(Ndb_cluster_connection *ndb_cluster_connection,
+ Ndb& ndb)
+ : m_ndb_cluster_connection(ndb_cluster_connection->m_impl),
+ m_dictionary(ndb),
+ theCurrentConnectIndex(0),
+ theNdbObjectIdMap(1024,1024),
+ theNoOfDBnodes(0)
+{
+ int i;
+ for (i = 0; i < MAX_NDB_NODES; i++) {
+ the_release_ind[i] = 0;
+ }
+ m_optimized_node_selection=
+ m_ndb_cluster_connection.m_optimized_node_selection;
+}
+
+NdbImpl::~NdbImpl()
+{
+}
+
diff --git a/storage/ndb/src/ndbapi/Ndblist.cpp b/storage/ndb/src/ndbapi/Ndblist.cpp
new file mode 100644
index 00000000000..df90f3378ab
--- /dev/null
+++ b/storage/ndb/src/ndbapi/Ndblist.cpp
@@ -0,0 +1,824 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbOut.hpp>
+#include <Ndb.hpp>
+#include <NdbOperation.hpp>
+#include <NdbIndexOperation.hpp>
+#include <NdbIndexScanOperation.hpp>
+#include "NdbApiSignal.hpp"
+#include <NdbRecAttr.hpp>
+#include "NdbUtil.hpp"
+#include "API.hpp"
+#include "NdbBlob.hpp"
+
+void
+Ndb::checkFailedNode()
+{
+ DBUG_ENTER("Ndb::checkFailedNode");
+ Uint32 *the_release_ind= theImpl->the_release_ind;
+ if (the_release_ind[0] == 0)
+ {
+ DBUG_VOID_RETURN;
+ }
+ Uint32 tNoOfDbNodes = theImpl->theNoOfDBnodes;
+ Uint8 *theDBnodes= theImpl->theDBnodes;
+
+ DBUG_PRINT("enter", ("theNoOfDBnodes: %d", tNoOfDbNodes));
+
+ DBUG_ASSERT(tNoOfDbNodes < MAX_NDB_NODES);
+ for (Uint32 i = 0; i < tNoOfDbNodes; i++){
+ const NodeId node_id = theDBnodes[i];
+ DBUG_PRINT("info", ("i: %d, node_id: %d", i, node_id));
+
+ DBUG_ASSERT(node_id < MAX_NDB_NODES);
+ if (the_release_ind[node_id] == 1){
+
+ /**
+ * Release all connections in idle list (for node)
+ */
+ NdbTransaction * tNdbCon = theConnectionArray[node_id];
+ theConnectionArray[node_id] = NULL;
+ while (tNdbCon != NULL) {
+ NdbTransaction* tempNdbCon = tNdbCon;
+ tNdbCon = tNdbCon->next();
+ releaseNdbCon(tempNdbCon);
+ }
+ the_release_ind[node_id] = 0;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+/***************************************************************************
+ * int createConIdleList(int aNrOfCon);
+ *
+ * Return Value: Return the number of created connection object
+ * if createConIdleList was succesful
+ * Return -1: In all other case.
+ * Parameters: aNrOfCon : Number of connections offered to the application.
+ * Remark: Create connection idlelist with NdbTransaction objects.
+ ***************************************************************************/
+int
+Ndb::createConIdleList(int aNrOfCon)
+{
+ for (int i = 0; i < aNrOfCon; i++)
+ {
+ NdbTransaction* tNdbCon = new NdbTransaction(this);
+ if (tNdbCon == NULL)
+ {
+ return -1;
+ }
+ if (theConIdleList == NULL)
+ {
+ theConIdleList = tNdbCon;
+ theConIdleList->next(NULL);
+ } else
+ {
+ tNdbCon->next(theConIdleList);
+ theConIdleList = tNdbCon;
+ }
+ tNdbCon->Status(NdbTransaction::NotConnected);
+ }
+ theNoOfAllocatedTransactions = aNrOfCon;
+ return aNrOfCon;
+}
+
+/***************************************************************************
+ * int createOpIdleList(int aNrOfOp);
+ *
+ * Return Value: Return the number of created operation object if
+ * createOpIdleList was succesful.
+ * Return -1: In all other case.
+ * Parameters: aNrOfOp: Number of operations offered to the application.
+ * Remark: Create operation idlelist with NdbOperation objects..
+ ***************************************************************************/
+int
+Ndb::createOpIdleList(int aNrOfOp)
+{
+ for (int i = 0; i < aNrOfOp; i++){
+ NdbOperation* tOp = new NdbOperation(this);
+ if ( tOp == NULL ){
+ return -1;
+ }
+ if (theOpIdleList == NULL){
+ theOpIdleList = tOp;
+ theOpIdleList->next(NULL);
+ } else{
+ tOp->next(theOpIdleList);
+ theOpIdleList = tOp;
+ }
+ }
+ return aNrOfOp;
+}
+
+/***************************************************************************
+ * NdbBranch* NdbBranch();
+ *
+ * Return Value: Return a NdbBranch if the getNdbBranch was successful.
+ * Return NULL : In all other case.
+ * Remark: Get a NdbBranch from theBranchList and return the object .
+ ***************************************************************************/
+NdbBranch*
+Ndb::getNdbBranch()
+{
+ NdbBranch* tNdbBranch;
+ if ( theBranchList == NULL )
+ {
+ tNdbBranch = new NdbBranch;
+ if (tNdbBranch == NULL)
+ {
+ return NULL;
+ }
+ tNdbBranch->theNext = NULL;
+ } else
+ {
+ tNdbBranch = theBranchList;
+ theBranchList = tNdbBranch->theNext;
+ tNdbBranch->theNext = NULL;
+ }
+ return tNdbBranch;
+}
+
+/***************************************************************************
+ * NdbCall* NdbCall();
+ *
+ * Return Value: Return a NdbCall if the getNdbCall was successful.
+ * Return NULL : In all other case.
+ * Remark: Get a NdbCall from theCallList and return the object .
+ ***************************************************************************/
+NdbCall*
+Ndb::getNdbCall()
+{
+ NdbCall* tNdbCall;
+ if ( theCallList == NULL )
+ {
+ tNdbCall = new NdbCall;
+ if (tNdbCall == NULL)
+ {
+ return NULL;
+ }
+ tNdbCall->theNext = NULL;
+ } else
+ {
+ tNdbCall = theCallList;
+ theCallList = tNdbCall->theNext;
+ tNdbCall->theNext = NULL;
+ }
+ return tNdbCall;
+}
+
+/***************************************************************************
+ * NdbTransaction* getNdbCon();
+ *
+ * Return Value: Return a connection if the getNdbCon was successful.
+ * Return NULL : In all other case.
+ * Remark: Get a connection from theConIdleList and return the object .
+ ***************************************************************************/
+NdbTransaction*
+Ndb::getNdbCon()
+{
+ NdbTransaction* tNdbCon;
+ if ( theConIdleList == NULL ) {
+ if (theNoOfAllocatedTransactions < theMaxNoOfTransactions) {
+ tNdbCon = new NdbTransaction(this);
+ if (tNdbCon == NULL) {
+ return NULL;
+ }//if
+ theNoOfAllocatedTransactions++;
+ } else {
+ ndbout << "theNoOfAllocatedTransactions = " << theNoOfAllocatedTransactions << " theMaxNoOfTransactions = " << theMaxNoOfTransactions << endl;
+ return NULL;
+ }//if
+ tNdbCon->next(NULL);
+ } else
+ {
+ tNdbCon = theConIdleList;
+ theConIdleList = tNdbCon->next();
+ tNdbCon->next(NULL);
+ }
+ tNdbCon->theMagicNumber = 0x37412619;
+ return tNdbCon;
+}
+
+/***************************************************************************
+ * NdbLabel* getNdbLabel();
+ *
+ * Return Value: Return a NdbLabel if the getNdbLabel was successful.
+ * Return NULL : In all other case.
+ * Remark: Get a NdbLabel from theLabelList and return the object .
+ ***************************************************************************/
+NdbLabel*
+Ndb::getNdbLabel()
+{
+ NdbLabel* tNdbLabel;
+ if ( theLabelList == NULL )
+ {
+ tNdbLabel = new NdbLabel;
+ if (tNdbLabel == NULL)
+ {
+ return NULL;
+ }
+ tNdbLabel->theNext = NULL;
+ } else
+ {
+ tNdbLabel = theLabelList;
+ theLabelList = tNdbLabel->theNext;
+ tNdbLabel->theNext = NULL;
+ }
+ return tNdbLabel;
+}
+
+/***************************************************************************
+ * NdbScanReceiver* getNdbScanRec()
+ *
+ * Return Value: Return a NdbScanReceiver
+ * Return NULL : In all other case.
+ * Remark: Get a NdbScanReceiver from theScanRecList and return the
+ * object .
+ ****************************************************************************/
+NdbReceiver*
+Ndb::getNdbScanRec()
+{
+ NdbReceiver* tNdbScanRec;
+ if ( theScanList == NULL )
+ {
+ tNdbScanRec = new NdbReceiver(this);
+ if (tNdbScanRec == NULL)
+ {
+ return NULL;
+ }
+ tNdbScanRec->next(NULL);
+ } else
+ {
+ tNdbScanRec = theScanList;
+ theScanList = tNdbScanRec->next();
+ tNdbScanRec->next(NULL);
+ }
+
+ return tNdbScanRec;
+}
+
+/***************************************************************************
+ * NdbSubroutine* getNdbSubroutine();
+ *
+ * Return Value: Return a NdbSubroutine if the getNdbSubroutine was successful.
+ * Return NULL : In all other case.
+ * Remark: Get a NdbSubroutine from theSubroutineList and return the object .
+ ***************************************************************************/
+NdbSubroutine*
+Ndb::getNdbSubroutine()
+{
+ NdbSubroutine* tNdbSubroutine;
+ if ( theSubroutineList == NULL )
+ {
+ tNdbSubroutine = new NdbSubroutine;
+ if (tNdbSubroutine == NULL)
+ {
+ return NULL;
+ }
+ tNdbSubroutine->theNext = NULL;
+ } else
+ {
+ tNdbSubroutine = theSubroutineList;
+ theSubroutineList = tNdbSubroutine->theNext;
+ tNdbSubroutine->theNext = NULL;
+ }
+ return tNdbSubroutine;
+}
+
+/***************************************************************************
+NdbOperation* getOperation();
+
+Return Value: Return theOpList : if the getOperation was succesful.
+ Return NULL : In all other case.
+Remark: Get an operation from theOpIdleList and return the object .
+***************************************************************************/
+NdbOperation*
+Ndb::getOperation()
+{
+ NdbOperation* tOp = theOpIdleList;
+ if (tOp != NULL ) {
+ NdbOperation* tOpNext = tOp->next();
+ tOp->next(NULL);
+ theOpIdleList = tOpNext;
+ return tOp;
+ } else {
+ tOp = new NdbOperation(this);
+ if (tOp != NULL)
+ tOp->next(NULL);
+ }
+ return tOp;
+}
+
+/***************************************************************************
+NdbScanOperation* getScanOperation();
+
+Return Value: Return theOpList : if the getScanOperation was succesful.
+ Return NULL : In all other case.
+Remark: Get an operation from theScanOpIdleList and return the object .
+***************************************************************************/
+NdbIndexScanOperation*
+Ndb::getScanOperation()
+{
+ NdbIndexScanOperation* tOp = theScanOpIdleList;
+ if (tOp != NULL ) {
+ NdbIndexScanOperation* tOpNext = (NdbIndexScanOperation*)tOp->next();
+ tOp->next(NULL);
+ theScanOpIdleList = tOpNext;
+ return tOp;
+ } else {
+ tOp = new NdbIndexScanOperation(this);
+ if (tOp != NULL)
+ tOp->next(NULL);
+ }
+ return tOp;
+}
+
+/***************************************************************************
+NdbIndexOperation* getIndexOperation();
+
+Return Value: Return theOpList : if the getIndexOperation was succesful.
+ Return NULL : In all other case.
+Remark: Get an operation from theIndexOpIdleList and return the object .
+***************************************************************************/
+NdbIndexOperation*
+Ndb::getIndexOperation()
+{
+ NdbIndexOperation* tOp = theIndexOpIdleList;
+ if (tOp != NULL ) {
+ NdbIndexOperation* tOpNext = (NdbIndexOperation*) tOp->next();
+ tOp->next(NULL);
+ theIndexOpIdleList = tOpNext;
+ return tOp;
+ } else {
+ tOp = new NdbIndexOperation(this);
+ if (tOp != NULL)
+ tOp->next(NULL);
+ }
+ return tOp;
+}
+
+/***************************************************************************
+NdbRecAttr* getRecAttr();
+
+Return Value: Return a reference to a receive attribute object.
+ Return NULL if it's not possible to get a receive attribute object.
+***************************************************************************/
+NdbRecAttr*
+Ndb::getRecAttr()
+{
+ NdbRecAttr* tRecAttr;
+ tRecAttr = theRecAttrIdleList;
+ if (tRecAttr != NULL) {
+ NdbRecAttr* tRecAttrNext = tRecAttr->next();
+ tRecAttr->init();
+ theRecAttrIdleList = tRecAttrNext;
+ return tRecAttr;
+ } else {
+ tRecAttr = new NdbRecAttr;
+ if (tRecAttr == NULL)
+ return NULL;
+ tRecAttr->next(NULL);
+ }//if
+ tRecAttr->init();
+ return tRecAttr;
+}
+
+/***************************************************************************
+NdbApiSignal* getSignal();
+
+Return Value: Return a reference to a signal object.
+ Return NULL if not possible to get a signal object.
+***************************************************************************/
+NdbApiSignal*
+Ndb::getSignal()
+{
+ NdbApiSignal* tSignal = theSignalIdleList;
+ if (tSignal != NULL){
+ NdbApiSignal* tSignalNext = tSignal->next();
+ tSignal->next(NULL);
+ theSignalIdleList = tSignalNext;
+ } else {
+ tSignal = new NdbApiSignal(theMyRef);
+#ifdef POORMANSPURIFY
+ cnewSignals++;
+#endif
+ if (tSignal != NULL)
+ tSignal->next(NULL);
+ }
+#ifdef POORMANSPURIFY
+ cgetSignals++;
+#endif
+ return tSignal;
+}
+
+NdbBlob*
+Ndb::getNdbBlob()
+{
+ NdbBlob* tBlob = theNdbBlobIdleList;
+ if (tBlob != NULL) {
+ theNdbBlobIdleList = tBlob->theNext;
+ tBlob->init();
+ } else {
+ tBlob = new NdbBlob;
+ }
+ return tBlob;
+}
+
+/***************************************************************************
+void releaseNdbBranch(NdbBranch* aNdbBranch);
+
+Parameters: NdbBranch: The NdbBranch object.
+Remark: Add a NdbBranch object into the Branch idlelist.
+***************************************************************************/
+void
+Ndb::releaseNdbBranch(NdbBranch* aNdbBranch)
+{
+ aNdbBranch->theNext = theBranchList;
+ theBranchList = aNdbBranch;
+}
+
+/***************************************************************************
+void releaseNdbCall(NdbCall* aNdbCall);
+
+Parameters: NdbBranch: The NdbBranch object.
+Remark: Add a NdbBranch object into the Branch idlelist.
+***************************************************************************/
+void
+Ndb::releaseNdbCall(NdbCall* aNdbCall)
+{
+ aNdbCall->theNext = theCallList;
+ theCallList = aNdbCall;
+}
+
+/***************************************************************************
+void releaseNdbCon(NdbTransaction* aNdbCon);
+
+Parameters: aNdbCon: The NdbTransaction object.
+Remark: Add a Connection object into the signal idlelist.
+***************************************************************************/
+void
+Ndb::releaseNdbCon(NdbTransaction* aNdbCon)
+{
+ aNdbCon->next(theConIdleList);
+ aNdbCon->theMagicNumber = 0xFE11DD;
+ theConIdleList = aNdbCon;
+}
+
+/***************************************************************************
+void releaseNdbLabel(NdbLabel* aNdbLabel);
+
+Parameters: NdbLabel: The NdbLabel object.
+Remark: Add a NdbLabel object into the Label idlelist.
+***************************************************************************/
+void
+Ndb::releaseNdbLabel(NdbLabel* aNdbLabel)
+{
+ aNdbLabel->theNext = theLabelList;
+ theLabelList = aNdbLabel;
+}
+
+/***************************************************************************
+void releaseNdbScanRec(NdbScanReceiver* aNdbScanRec);
+
+Parameters: aNdbScanRec: The NdbScanReceiver object.
+Remark: Add a NdbScanReceiver object into the Scan idlelist.
+***************************************************************************/
+void
+Ndb::releaseNdbScanRec(NdbReceiver* aNdbScanRec)
+{
+ aNdbScanRec->next(theScanList);
+ theScanList = aNdbScanRec;
+}
+
+/***************************************************************************
+void releaseNdbSubroutine(NdbSubroutine* aNdbSubroutine);
+
+Parameters: NdbSubroutine: The NdbSubroutine object.
+Remark: Add a NdbSubroutine object into theSubroutine idlelist.
+***************************************************************************/
+void
+Ndb::releaseNdbSubroutine(NdbSubroutine* aNdbSubroutine)
+{
+ aNdbSubroutine->theNext = theSubroutineList;
+ theSubroutineList = aNdbSubroutine;
+}
+
+/***************************************************************************
+void releaseOperation(NdbOperation* anOperation);
+
+Parameters: anOperation : The released NdbOperation object.
+Remark: Add a NdbOperation object into the signal idlelist.
+***************************************************************************/
+void
+Ndb::releaseOperation(NdbOperation* anOperation)
+{
+ if(anOperation->m_tcReqGSN == GSN_TCKEYREQ){
+ anOperation->next(theOpIdleList);
+ anOperation->theNdbCon = NULL;
+ anOperation->theMagicNumber = 0xFE11D0;
+ theOpIdleList = anOperation;
+ } else {
+ assert(anOperation->m_tcReqGSN == GSN_TCINDXREQ);
+ anOperation->next(theIndexOpIdleList);
+ anOperation->theNdbCon = NULL;
+ anOperation->theMagicNumber = 0xFE11D1;
+ theIndexOpIdleList = (NdbIndexOperation*)anOperation;
+ }
+}
+
+/***************************************************************************
+void releaseScanOperation(NdbScanOperation* aScanOperation);
+
+Parameters: aScanOperation : The released NdbScanOperation object.
+Remark: Add a NdbScanOperation object into the signal idlelist.
+***************************************************************************/
+void
+Ndb::releaseScanOperation(NdbIndexScanOperation* aScanOperation)
+{
+ aScanOperation->next(theScanOpIdleList);
+ aScanOperation->theNdbCon = NULL;
+ aScanOperation->theMagicNumber = 0xFE11D2;
+ theScanOpIdleList = aScanOperation;
+}
+
+/***************************************************************************
+void releaseRecAttr(NdbRecAttr* aRecAttr);
+
+Parameters: aRecAttr : The released NdbRecAttr object.
+Remark: Add a NdbRecAttr object into the RecAtt idlelist.
+***************************************************************************/
+void
+Ndb::releaseRecAttr(NdbRecAttr* aRecAttr)
+{
+ aRecAttr->release();
+ aRecAttr->next(theRecAttrIdleList);
+ theRecAttrIdleList = aRecAttr;
+}
+
+/***************************************************************************
+void releaseSignal(NdbApiSignal* aSignal);
+
+Parameters: aSignal : The released NdbApiSignal object.
+Remark: Add a NdbApiSignal object into the signal idlelist.
+***************************************************************************/
+void
+Ndb::releaseSignal(NdbApiSignal* aSignal)
+{
+#if defined VM_TRACE
+ // Check that signal is not null
+ assert(aSignal != NULL);
+#if 0
+ // Check that signal is not already in list
+ NdbApiSignal* tmp = theSignalIdleList;
+ while (tmp != NULL){
+ assert(tmp != aSignal);
+ tmp = tmp->next();
+ }
+#endif
+#endif
+#ifdef POORMANSPURIFY
+ creleaseSignals++;
+#endif
+ aSignal->next(theSignalIdleList);
+ theSignalIdleList = aSignal;
+}
+
+void
+Ndb::releaseSignalsInList(NdbApiSignal** pList){
+ NdbApiSignal* tmp;
+ while (*pList != NULL){
+ tmp = *pList;
+ *pList = (*pList)->next();
+ releaseSignal(tmp);
+ }
+}
+
+void
+Ndb::releaseNdbBlob(NdbBlob* aBlob)
+{
+ aBlob->release();
+ aBlob->theNext = theNdbBlobIdleList;
+ theNdbBlobIdleList = aBlob;
+}
+
+/***************************************************************************
+void freeOperation();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeOperation()
+{
+ NdbOperation* tOp = theOpIdleList;
+ theOpIdleList = theOpIdleList->next();
+ delete tOp;
+}
+
+/***************************************************************************
+void freeScanOperation();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeScanOperation()
+{
+ NdbIndexScanOperation* tOp = theScanOpIdleList;
+ theScanOpIdleList = (NdbIndexScanOperation *)tOp->next();
+ delete tOp;
+}
+
+/***************************************************************************
+void freeIndexOperation();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeIndexOperation()
+{
+ NdbIndexOperation* tOp = theIndexOpIdleList;
+ theIndexOpIdleList = (NdbIndexOperation *) theIndexOpIdleList->next();
+ delete tOp;
+}
+
+/***************************************************************************
+void freeNdbBranch();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeNdbBranch()
+{
+ NdbBranch* tNdbBranch = theBranchList;
+ theBranchList = theBranchList->theNext;
+ delete tNdbBranch;
+}
+
+/***************************************************************************
+void freeNdbCall();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeNdbCall()
+{
+ NdbCall* tNdbCall = theCallList;
+ theCallList = theCallList->theNext;
+ delete tNdbCall;
+}
+
+/***************************************************************************
+void freeNdbScanRec();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeNdbScanRec()
+{
+ NdbReceiver* tNdbScanRec = theScanList;
+ theScanList = theScanList->next();
+ delete tNdbScanRec;
+}
+
+/***************************************************************************
+void freeNdbCon();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeNdbCon()
+{
+ NdbTransaction* tNdbCon = theConIdleList;
+ theConIdleList = theConIdleList->next();
+ delete tNdbCon;
+}
+
+/***************************************************************************
+void freeNdbLabel();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeNdbLabel()
+{
+ NdbLabel* tNdbLabel = theLabelList;
+ theLabelList = theLabelList->theNext;
+ delete tNdbLabel;
+}
+
+/***************************************************************************
+void freeNdbSubroutine();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeNdbSubroutine()
+{
+ NdbSubroutine* tNdbSubroutine = theSubroutineList;
+ theSubroutineList = theSubroutineList->theNext;
+ delete tNdbSubroutine;
+}
+
+/***************************************************************************
+void freeRecAttr();
+
+Remark: Always release the first item in the free list
+***************************************************************************/
+void
+Ndb::freeRecAttr()
+{
+ NdbRecAttr* tRecAttr = theRecAttrIdleList;
+ theRecAttrIdleList = theRecAttrIdleList->next();
+ delete tRecAttr;
+}
+
+/***************************************************************************
+void freeSignal();
+
+Remark: Delete a signal object from the signal idlelist.
+***************************************************************************/
+void
+Ndb::freeSignal()
+{
+ NdbApiSignal* tSignal = theSignalIdleList;
+ theSignalIdleList = tSignal->next();
+ delete tSignal;
+#ifdef POORMANSPURIFY
+ cfreeSignals++;
+#endif
+}
+
+void
+Ndb::freeNdbBlob()
+{
+ NdbBlob* tBlob = theNdbBlobIdleList;
+ theNdbBlobIdleList = tBlob->theNext;
+ delete tBlob;
+}
+
+/****************************************************************************
+int releaseConnectToNdb(NdbTransaction* aConnectConnection);
+
+Return Value: -1 if error
+Parameters: aConnectConnection : Seized schema connection to DBTC
+Remark: Release and disconnect from DBTC a connection and seize it to theConIdleList.
+*****************************************************************************/
+void
+Ndb::releaseConnectToNdb(NdbTransaction* a_con)
+{
+ DBUG_ENTER("Ndb::releaseConnectToNdb");
+ NdbApiSignal tSignal(theMyRef);
+ int tConPtr;
+
+// I need to close the connection irrespective of whether I
+// manage to reach NDB or not.
+
+ if (a_con == NULL)
+ DBUG_VOID_RETURN;
+
+ Uint32 node_id = a_con->getConnectedNodeId();
+ Uint32 conn_seq = a_con->theNodeSequence;
+ tSignal.setSignal(GSN_TCRELEASEREQ);
+ tSignal.setData((tConPtr = a_con->getTC_ConnectPtr()), 1);
+ tSignal.setData(theMyRef, 2);
+ tSignal.setData(a_con->ptr2int(), 3);
+ a_con->Status(NdbTransaction::DisConnecting);
+ a_con->theMagicNumber = 0x37412619;
+ int ret_code = sendRecSignal(node_id,
+ WAIT_TC_RELEASE,
+ &tSignal,
+ conn_seq);
+ if (ret_code == 0) {
+ ;
+ } else if (ret_code == -1) {
+ TRACE_DEBUG("Time-out when TCRELEASE sent");
+ } else if (ret_code == -2) {
+ TRACE_DEBUG("Node failed when TCRELEASE sent");
+ } else if (ret_code == -3) {
+ TRACE_DEBUG("Send failed when TCRELEASE sent");
+ } else if (ret_code == -4) {
+ TRACE_DEBUG("Send buffer full when TCRELEASE sent");
+ } else if (ret_code == -5) {
+ TRACE_DEBUG("Node stopping when TCRELEASE sent");
+ } else {
+ ndbout << "Impossible return from sendRecSignal when TCRELEASE" << endl;
+ abort();
+ }//if
+ releaseNdbCon(a_con);
+ DBUG_VOID_RETURN;
+}
+
diff --git a/storage/ndb/src/ndbapi/ObjectMap.hpp b/storage/ndb/src/ndbapi/ObjectMap.hpp
new file mode 100644
index 00000000000..21407279f0b
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ObjectMap.hpp
@@ -0,0 +1,151 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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_OBJECT_ID_MAP_HPP
+#define NDB_OBJECT_ID_MAP_HPP
+
+#include <ndb_global.h>
+//#include <NdbMutex.h>
+#include <NdbOut.hpp>
+
+//#define DEBUG_OBJECTMAP
+
+/**
+ * Global ObjectMap
+ */
+class NdbObjectIdMap //: NdbLockable
+{
+public:
+ STATIC_CONST( InvalidId = ~(Uint32)0 );
+ NdbObjectIdMap(Uint32 initalSize = 128, Uint32 expandSize = 10);
+ ~NdbObjectIdMap();
+
+ Uint32 map(void * object);
+ void * unmap(Uint32 id, void *object);
+
+ void * getObject(Uint32 id);
+private:
+ Uint32 m_size;
+ Uint32 m_expandSize;
+ Uint32 m_firstFree;
+ union MapEntry {
+ Uint32 m_next;
+ void * m_obj;
+ } * m_map;
+
+ void expand(Uint32 newSize);
+};
+
+inline
+NdbObjectIdMap::NdbObjectIdMap(Uint32 sz, Uint32 eSz) {
+ m_size = 0;
+ m_firstFree = InvalidId;
+ m_map = 0;
+ m_expandSize = eSz;
+ expand(sz);
+#ifdef DEBUG_OBJECTMAP
+ ndbout_c("NdbObjectIdMap:::NdbObjectIdMap(%u)", sz);
+#endif
+}
+
+inline
+NdbObjectIdMap::~NdbObjectIdMap(){
+ free(m_map);
+}
+
+inline
+Uint32
+NdbObjectIdMap::map(void * object){
+
+ // lock();
+
+ if(m_firstFree == InvalidId){
+ expand(m_expandSize);
+ }
+
+ Uint32 ff = m_firstFree;
+ m_firstFree = m_map[ff].m_next;
+ m_map[ff].m_obj = object;
+
+ // unlock();
+
+#ifdef DEBUG_OBJECTMAP
+ ndbout_c("NdbObjectIdMap::map(0x%x) %u", object, ff<<2);
+#endif
+
+ return ff<<2;
+}
+
+inline
+void *
+NdbObjectIdMap::unmap(Uint32 id, void *object){
+
+ Uint32 i = id>>2;
+
+ // lock();
+ if(i < m_size){
+ void * obj = m_map[i].m_obj;
+ if (object == obj) {
+ m_map[i].m_next = m_firstFree;
+ m_firstFree = i;
+ } else {
+ ndbout_c("Error: NdbObjectIdMap::::unmap(%u, 0x%x) obj=0x%x", id, object, obj);
+ return 0;
+ }
+
+ // unlock();
+
+#ifdef DEBUG_OBJECTMAP
+ ndbout_c("NdbObjectIdMap::unmap(%u) obj=0x%x", id, obj);
+#endif
+
+ return obj;
+ }
+ return 0;
+}
+
+inline void *
+NdbObjectIdMap::getObject(Uint32 id){
+#ifdef DEBUG_OBJECTMAP
+ ndbout_c("NdbObjectIdMap::getObject(%u) obj=0x%x", id, m_map[id>>2].m_obj);
+#endif
+ id >>= 2;
+ if(id < m_size){
+ return m_map[id].m_obj;
+ }
+ return 0;
+}
+
+inline void
+NdbObjectIdMap::expand(Uint32 incSize){
+ Uint32 newSize = m_size + incSize;
+ MapEntry * tmp = (MapEntry*)malloc(newSize * sizeof(MapEntry));
+
+ if (m_map) {
+ memcpy(tmp, m_map, m_size * sizeof(MapEntry));
+ free((void*)m_map);
+ }
+ m_map = tmp;
+
+ for(Uint32 i = m_size; i<newSize; i++){
+ m_map[i].m_next = i + 1;
+ }
+ m_firstFree = m_size;
+ m_map[newSize-1].m_next = InvalidId;
+ m_size = newSize;
+}
+
+#endif
diff --git a/storage/ndb/src/ndbapi/ScanOperation.txt b/storage/ndb/src/ndbapi/ScanOperation.txt
new file mode 100644
index 00000000000..27e4e8c1755
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ScanOperation.txt
@@ -0,0 +1,56 @@
+x) NdbConnection (main)
+m_theFirstCursorOperation -> y
+
+y) NdbScanOperation
+m_transConnection -> x
+theNdbCon -> z
+
+z) NdbConnection (scan)
+theScanningOp -> y
+theFirstOpInList -> y (until after openScan)
+
+# SU
+
+ScanOpLen: includes KeyInfo
+New protocol
+
+# -- Impl.
+
+1) Scan uses one NdbReceiver per "parallelism"
+2) Each NdbReceiver can handle up to "batch size" rows
+3) API send one "pointer" per parallelism (prev. was one per row)
+4) API handles each receiver independently.
+ It can "nextResult"-one, receive one and close-one
+5) When a recevier has been "nextResult"-ed, the API can fetch from it again
+6) After doing "openScan"-req, no wait is performed
+ (only possible to block on nextResult(true) or closeScan)
+
+7) Instead of "ack"-ing each row with length,
+* Each row is sent in one lonw signal (unless to short)
+* Each NdbReceiver is ack-ed with #rows and sum(#length)
+* KeyInfo20 is one signal and included in sum(#length)
+
+8) The API receive(s) the data into NdbRecAttr-objects
+ (prev. it copied signals using new/delete)
+9) KeyInfo20 is also received into a NdbRecAttr-object
+10)
+
+# -- Close of scan
+
+1) Each NdbReciver gets a signal when it's complete
+ (0 rows is ack-ed)
+2) The API then "closes" this receiver
+3) The API can at any time close then scan for other reason(s)
+ (example dying)
+4) This is signal:ed via a NEXT_SCANREQ (close = 1)
+5) TC responds with a SCAN_TABCONF (close = 1)
+
+
+# -- Sorted
+
+1) The sorted scan is transparent to TC
+ It's a API only impl.
+2) The API makes the following adjustements:
+* Scan all fragments simultaniously (max parallelism)
+* Never return a row to the API if a NdbReciver is "outstanding"
+* Sort Receivers (only top row as they already are sorted within)
diff --git a/storage/ndb/src/ndbapi/TransporterFacade.cpp b/storage/ndb/src/ndbapi/TransporterFacade.cpp
new file mode 100644
index 00000000000..7f1e68a42d3
--- /dev/null
+++ b/storage/ndb/src/ndbapi/TransporterFacade.cpp
@@ -0,0 +1,1108 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+#include <ndb_limits.h>
+#include "TransporterFacade.hpp"
+#include "ClusterMgr.hpp"
+#include <IPCConfig.hpp>
+#include <TransporterCallback.hpp>
+#include <TransporterRegistry.hpp>
+#include "NdbApiSignal.hpp"
+#include <NdbOut.hpp>
+#include <NdbEnv.h>
+#include <NdbSleep.h>
+
+#include "API.hpp"
+#include <ConfigRetriever.hpp>
+#include <mgmapi_config_parameters.h>
+#include <mgmapi_configuration.hpp>
+#include <NdbConfig.h>
+#include <ndb_version.h>
+#include <SignalLoggerManager.hpp>
+#include <kernel/ndb_limits.h>
+
+//#define REPORT_TRANSPORTER
+//#define API_TRACE;
+
+static int numberToIndex(int number)
+{
+ return number - MIN_API_BLOCK_NO;
+}
+
+static int indexToNumber(int index)
+{
+ return index + MIN_API_BLOCK_NO;
+}
+
+#if defined DEBUG_TRANSPORTER
+#define TRP_DEBUG(t) ndbout << __FILE__ << ":" << __LINE__ << ":" << t << endl;
+#else
+#define TRP_DEBUG(t)
+#endif
+
+TransporterFacade* TransporterFacade::theFacadeInstance = NULL;
+
+/*****************************************************************************
+ * Call back functions
+ *****************************************************************************/
+
+void
+reportError(void * callbackObj, NodeId nodeId, TransporterError errorCode){
+#ifdef REPORT_TRANSPORTER
+ ndbout_c("REPORT_TRANSP: reportError (nodeId=%d, errorCode=%d)",
+ (int)nodeId, (int)errorCode);
+#endif
+ if(errorCode & 0x8000) {
+ ndbout_c("reportError (%d, %d)\n", (int)nodeId, (int)errorCode);
+ ((TransporterFacade*)(callbackObj))->doDisconnect(nodeId);
+ }
+}
+
+/**
+ * Report average send length in bytes (4096 last sends)
+ */
+void
+reportSendLen(void * callbackObj, NodeId nodeId, Uint32 count, Uint64 bytes){
+#ifdef REPORT_TRANSPORTER
+ ndbout_c("REPORT_TRANSP: reportSendLen (nodeId=%d, bytes/count=%d)",
+ (int)nodeId, (Uint32)(bytes/count));
+#endif
+ (void)nodeId;
+ (void)count;
+ (void)bytes;
+}
+
+/**
+ * Report average receive length in bytes (4096 last receives)
+ */
+void
+reportReceiveLen(void * callbackObj,
+ NodeId nodeId, Uint32 count, Uint64 bytes){
+#ifdef REPORT_TRANSPORTER
+ ndbout_c("REPORT_TRANSP: reportReceiveLen (nodeId=%d, bytes/count=%d)",
+ (int)nodeId, (Uint32)(bytes/count));
+#endif
+ (void)nodeId;
+ (void)count;
+ (void)bytes;
+}
+
+/**
+ * Report connection established
+ */
+void
+reportConnect(void * callbackObj, NodeId nodeId){
+#ifdef REPORT_TRANSPORTER
+ ndbout_c("REPORT_TRANSP: API reportConnect (nodeId=%d)", (int)nodeId);
+#endif
+ ((TransporterFacade*)(callbackObj))->reportConnected(nodeId);
+ // TransporterFacade::instance()->reportConnected(nodeId);
+}
+
+/**
+ * Report connection broken
+ */
+void
+reportDisconnect(void * callbackObj, NodeId nodeId, Uint32 error){
+#ifdef REPORT_TRANSPORTER
+ ndbout_c("REPORT_TRANSP: API reportDisconnect (nodeId=%d)", (int)nodeId);
+#endif
+ ((TransporterFacade*)(callbackObj))->reportDisconnected(nodeId);
+ //TransporterFacade::instance()->reportDisconnected(nodeId);
+}
+
+
+/****************************************************************************
+ *
+ *****************************************************************************/
+
+/**
+ * Report connection broken
+ */
+int checkJobBuffer() {
+ return 0;
+}
+
+#ifdef API_TRACE
+static const char * API_SIGNAL_LOG = "API_SIGNAL_LOG";
+static const char * apiSignalLog = 0;
+static SignalLoggerManager signalLogger;
+static
+inline
+bool
+setSignalLog(){
+ signalLogger.flushSignalLog();
+
+ const char * tmp = NdbEnv_GetEnv(API_SIGNAL_LOG, (char *)0, 0);
+ if(tmp != 0 && apiSignalLog != 0 && strcmp(tmp,apiSignalLog) == 0){
+ return true;
+ } else if(tmp == 0 && apiSignalLog == 0){
+ return false;
+ } else if(tmp == 0 && apiSignalLog != 0){
+ signalLogger.setOutputStream(0);
+ apiSignalLog = tmp;
+ return false;
+ } else if(tmp !=0){
+ if (strcmp(tmp, "-") == 0)
+ signalLogger.setOutputStream(stdout);
+ else
+ signalLogger.setOutputStream(fopen(tmp, "w"));
+ apiSignalLog = tmp;
+ return true;
+ }
+ return false;
+}
+#ifdef TRACE_APIREGREQ
+#define TRACE_GSN(gsn) true
+#else
+#define TRACE_GSN(gsn) (gsn != GSN_API_REGREQ && gsn != GSN_API_REGCONF)
+#endif
+#endif
+
+/**
+ * The execute function : Handle received signal
+ */
+void
+execute(void * callbackObj, SignalHeader * const header,
+ Uint8 prio, Uint32 * const theData,
+ LinearSectionPtr ptr[3]){
+
+ TransporterFacade * theFacade = (TransporterFacade*)callbackObj;
+ TransporterFacade::ThreadData::Object_Execute oe;
+ Uint32 tRecBlockNo = header->theReceiversBlockNumber;
+
+#ifdef API_TRACE
+ if(setSignalLog() && TRACE_GSN(header->theVerId_signalNumber)){
+ signalLogger.executeSignal(* header,
+ prio,
+ theData,
+ theFacade->ownId(),
+ ptr, header->m_noOfSections);
+ signalLogger.flushSignalLog();
+ }
+#endif
+
+ if (tRecBlockNo >= MIN_API_BLOCK_NO) {
+ oe = theFacade->m_threads.get(tRecBlockNo);
+ if (oe.m_object != 0 && oe.m_executeFunction != 0) {
+ /**
+ * Handle received signal immediately to avoid any unnecessary
+ * copying of data, allocation of memory and other things. Copying
+ * of data could be interesting to support several priority levels
+ * and to support a special memory structure when executing the
+ * signals. Neither of those are interesting when receiving data
+ * in the NDBAPI. The NDBAPI will thus read signal data directly as
+ * it was written by the sender (SCI sender is other node, Shared
+ * memory sender is other process and TCP/IP sender is the OS that
+ * writes the TCP/IP message into a message buffer).
+ */
+ NdbApiSignal tmpSignal(*header);
+ NdbApiSignal * tSignal = &tmpSignal;
+ tSignal->setDataPtr(theData);
+ (* oe.m_executeFunction) (oe.m_object, tSignal, ptr);
+ }//if
+ } else if (tRecBlockNo == API_PACKED) {
+ /**
+ * Block number == 2047 is used to signal a signal that consists of
+ * multiple instances of the same signal. This is an effort to
+ * package the signals so as to avoid unnecessary communication
+ * overhead since TCP/IP has a great performance impact.
+ */
+ Uint32 Tlength = header->theLength;
+ Uint32 Tsent = 0;
+ /**
+ * Since it contains at least two data packets we will first
+ * copy the signal data to safe place.
+ */
+ while (Tsent < Tlength) {
+ Uint32 Theader = theData[Tsent];
+ Tsent++;
+ Uint32 TpacketLen = (Theader & 0x1F) + 3;
+ tRecBlockNo = Theader >> 16;
+ if (TpacketLen <= 25) {
+ if ((TpacketLen + Tsent) <= Tlength) {
+ /**
+ * Set the data length of the signal and the receivers block
+ * reference and then call the API.
+ */
+ header->theLength = TpacketLen;
+ header->theReceiversBlockNumber = tRecBlockNo;
+ Uint32* tDataPtr = &theData[Tsent];
+ Tsent += TpacketLen;
+ if (tRecBlockNo >= MIN_API_BLOCK_NO) {
+ oe = theFacade->m_threads.get(tRecBlockNo);
+ if(oe.m_object != 0 && oe.m_executeFunction != 0){
+ NdbApiSignal tmpSignal(*header);
+ NdbApiSignal * tSignal = &tmpSignal;
+ tSignal->setDataPtr(tDataPtr);
+ (*oe.m_executeFunction)(oe.m_object, tSignal, 0);
+ }
+ }
+ }
+ }
+ }
+ return;
+ } else if (tRecBlockNo == API_CLUSTERMGR) {
+ /**
+ * The signal was aimed for the Cluster Manager.
+ * We handle it immediately here.
+ */
+ ClusterMgr * clusterMgr = theFacade->theClusterMgr;
+ const Uint32 gsn = header->theVerId_signalNumber;
+
+ switch (gsn){
+ case GSN_API_REGREQ:
+ clusterMgr->execAPI_REGREQ(theData);
+ break;
+
+ case GSN_API_REGCONF:
+ clusterMgr->execAPI_REGCONF(theData);
+ break;
+
+ case GSN_API_REGREF:
+ clusterMgr->execAPI_REGREF(theData);
+ break;
+
+ case GSN_NODE_FAILREP:
+ clusterMgr->execNODE_FAILREP(theData);
+ break;
+
+ case GSN_NF_COMPLETEREP:
+ clusterMgr->execNF_COMPLETEREP(theData);
+ break;
+
+ case GSN_ARBIT_STARTREQ:
+ if (theFacade->theArbitMgr != NULL)
+ theFacade->theArbitMgr->doStart(theData);
+ break;
+
+ case GSN_ARBIT_CHOOSEREQ:
+ if (theFacade->theArbitMgr != NULL)
+ theFacade->theArbitMgr->doChoose(theData);
+ break;
+
+ case GSN_ARBIT_STOPORD:
+ if(theFacade->theArbitMgr != NULL)
+ theFacade->theArbitMgr->doStop(theData);
+ break;
+
+ default:
+ break;
+
+ }
+ return;
+ } else {
+ ; // Ignore all other block numbers.
+ if(header->theVerId_signalNumber!=3) {
+ TRP_DEBUG( "TransporterFacade received signal to unknown block no." );
+ ndbout << "BLOCK NO: " << tRecBlockNo << " sig "
+ << header->theVerId_signalNumber << endl;
+ abort();
+ }
+ }
+}
+
+// These symbols are needed, but not used in the API
+void
+SignalLoggerManager::printSegmentedSection(FILE *, const SignalHeader &,
+ const SegmentedSectionPtr ptr[3],
+ unsigned i){
+ abort();
+}
+
+void
+copy(Uint32 * & insertPtr,
+ class SectionSegmentPool & thePool, const SegmentedSectionPtr & _ptr){
+ abort();
+}
+
+/**
+ * Note that this function need no locking since its
+ * only called from the constructor of Ndb (the NdbObject)
+ *
+ * Which is protected by a mutex
+ */
+
+int
+TransporterFacade::start_instance(int nodeId,
+ const ndb_mgm_configuration* props)
+{
+ if (! theFacadeInstance->init(nodeId, props)) {
+ return -1;
+ }
+
+ /**
+ * Install signal handler for SIGPIPE
+ *
+ * This due to the fact that a socket connection might have
+ * been closed in between a select and a corresponding send
+ */
+#if !defined NDB_OSE && !defined NDB_SOFTOSE && !defined NDB_WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ return 0;
+}
+
+/**
+ * Note that this function need no locking since its
+ * only called from the destructor of Ndb (the NdbObject)
+ *
+ * Which is protected by a mutex
+ */
+void
+TransporterFacade::stop_instance(){
+ DBUG_ENTER("TransporterFacade::stop_instance");
+ if(theFacadeInstance)
+ theFacadeInstance->doStop();
+ DBUG_VOID_RETURN;
+}
+
+void
+TransporterFacade::doStop(){
+ DBUG_ENTER("TransporterFacade::doStop");
+ /**
+ * First stop the ClusterMgr because it needs to send one more signal
+ * and also uses theFacadeInstance to lock/unlock theMutexPtr
+ */
+ if (theClusterMgr != NULL) theClusterMgr->doStop();
+ if (theArbitMgr != NULL) theArbitMgr->doStop(NULL);
+
+ /**
+ * Now stop the send and receive threads
+ */
+ void *status;
+ theStopReceive = 1;
+ if (theReceiveThread) {
+ NdbThread_WaitFor(theReceiveThread, &status);
+ NdbThread_Destroy(&theReceiveThread);
+ theReceiveThread= 0;
+ }
+ if (theSendThread) {
+ NdbThread_WaitFor(theSendThread, &status);
+ NdbThread_Destroy(&theSendThread);
+ theSendThread= 0;
+ }
+ DBUG_VOID_RETURN;
+}
+
+extern "C"
+void*
+runSendRequest_C(void * me)
+{
+ ((TransporterFacade*) me)->threadMainSend();
+ return 0;
+}
+
+void TransporterFacade::threadMainSend(void)
+{
+ theTransporterRegistry->startSending();
+ if (!theTransporterRegistry->start_clients()){
+ ndbout_c("Unable to start theTransporterRegistry->start_clients");
+ exit(0);
+ }
+
+ m_socket_server.startServer();
+
+ while(!theStopReceive) {
+ NdbSleep_MilliSleep(10);
+ NdbMutex_Lock(theMutexPtr);
+ if (sendPerformedLastInterval == 0) {
+ theTransporterRegistry->performSend();
+ }
+ sendPerformedLastInterval = 0;
+ NdbMutex_Unlock(theMutexPtr);
+ }
+ theTransporterRegistry->stopSending();
+
+ m_socket_server.stopServer();
+ m_socket_server.stopSessions();
+
+ theTransporterRegistry->stop_clients();
+}
+
+extern "C"
+void*
+runReceiveResponse_C(void * me)
+{
+ ((TransporterFacade*) me)->threadMainReceive();
+ return 0;
+}
+
+void TransporterFacade::threadMainReceive(void)
+{
+ theTransporterRegistry->startReceiving();
+ NdbMutex_Lock(theMutexPtr);
+ theTransporterRegistry->update_connections();
+ NdbMutex_Unlock(theMutexPtr);
+ while(!theStopReceive) {
+ for(int i = 0; i<10; i++){
+ const int res = theTransporterRegistry->pollReceive(10);
+ if(res > 0){
+ NdbMutex_Lock(theMutexPtr);
+ theTransporterRegistry->performReceive();
+ NdbMutex_Unlock(theMutexPtr);
+ }
+ }
+ NdbMutex_Lock(theMutexPtr);
+ theTransporterRegistry->update_connections();
+ NdbMutex_Unlock(theMutexPtr);
+ }//while
+ theTransporterRegistry->stopReceiving();
+}
+
+TransporterFacade::TransporterFacade() :
+ theTransporterRegistry(0),
+ theStopReceive(0),
+ theSendThread(NULL),
+ theReceiveThread(NULL),
+ m_fragmented_signal_id(0)
+{
+ theOwnId = 0;
+
+ theMutexPtr = NdbMutex_Create();
+ sendPerformedLastInterval = 0;
+
+ checkCounter = 4;
+ currentSendLimit = 1;
+ theClusterMgr = NULL;
+ theArbitMgr = NULL;
+ theStartNodeId = 1;
+ m_scan_batch_size= MAX_SCAN_BATCH_SIZE;
+ m_batch_byte_size= SCAN_BATCH_SIZE;
+ m_batch_size= DEF_BATCH_SIZE;
+ m_max_trans_id = 0;
+
+ theClusterMgr = new ClusterMgr(* this);
+}
+
+bool
+TransporterFacade::init(Uint32 nodeId, const ndb_mgm_configuration* props)
+{
+ theOwnId = nodeId;
+ theTransporterRegistry = new TransporterRegistry(this);
+
+ const int res = IPCConfig::configureTransporters(nodeId,
+ * props,
+ * theTransporterRegistry);
+ if(res <= 0){
+ TRP_DEBUG( "configureTransporters returned 0 or less" );
+ return false;
+ }
+
+ ndb_mgm_configuration_iterator iter(* props, CFG_SECTION_NODE);
+ iter.first();
+ theClusterMgr->init(iter);
+
+ iter.first();
+ if(iter.find(CFG_NODE_ID, nodeId)){
+ TRP_DEBUG( "Node info missing from config." );
+ return false;
+ }
+
+ Uint32 rank = 0;
+ if(!iter.get(CFG_NODE_ARBIT_RANK, &rank) && rank>0){
+ theArbitMgr = new ArbitMgr(* this);
+ theArbitMgr->setRank(rank);
+ Uint32 delay = 0;
+ iter.get(CFG_NODE_ARBIT_DELAY, &delay);
+ theArbitMgr->setDelay(delay);
+ }
+ Uint32 scan_batch_size= 0;
+ if (!iter.get(CFG_MAX_SCAN_BATCH_SIZE, &scan_batch_size)) {
+ m_scan_batch_size= scan_batch_size;
+ }
+ Uint32 batch_byte_size= 0;
+ if (!iter.get(CFG_BATCH_BYTE_SIZE, &batch_byte_size)) {
+ m_batch_byte_size= batch_byte_size;
+ }
+ Uint32 batch_size= 0;
+ if (!iter.get(CFG_BATCH_SIZE, &batch_size)) {
+ m_batch_size= batch_size;
+ }
+
+ if (!theTransporterRegistry->start_service(m_socket_server)){
+ ndbout_c("Unable to start theTransporterRegistry->start_service");
+ return false;
+ }
+
+ theReceiveThread = NdbThread_Create(runReceiveResponse_C,
+ (void**)this,
+ 32768,
+ "ndb_receive",
+ NDB_THREAD_PRIO_LOW);
+
+ theSendThread = NdbThread_Create(runSendRequest_C,
+ (void**)this,
+ 32768,
+ "ndb_send",
+ NDB_THREAD_PRIO_LOW);
+ theClusterMgr->startThread();
+
+#ifdef API_TRACE
+ signalLogger.logOn(true, 0, SignalLoggerManager::LogInOut);
+#endif
+
+ return true;
+}
+
+
+void
+TransporterFacade::connected()
+{
+ DBUG_ENTER("TransporterFacade::connected");
+ Uint32 sz = m_threads.m_statusNext.size();
+ for (Uint32 i = 0; i < sz ; i ++) {
+ if (m_threads.getInUse(i)){
+ void * obj = m_threads.m_objectExecute[i].m_object;
+ NodeStatusFunction RegPC = m_threads.m_statusFunction[i];
+ (*RegPC) (obj, numberToRef(indexToNumber(i), theOwnId), true, true);
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+void
+TransporterFacade::ReportNodeDead(NodeId tNodeId)
+{
+ /**
+ * When a node fails we must report this to each Ndb object.
+ * The function that is used for communicating node failures is called.
+ * This is to ensure that the Ndb objects do not think their connections
+ * are correct after a failure followed by a restart.
+ * After the restart the node is up again and the Ndb object
+ * might not have noticed the failure.
+ */
+ Uint32 sz = m_threads.m_statusNext.size();
+ for (Uint32 i = 0; i < sz ; i ++) {
+ if (m_threads.getInUse(i)){
+ void * obj = m_threads.m_objectExecute[i].m_object;
+ NodeStatusFunction RegPC = m_threads.m_statusFunction[i];
+ (*RegPC) (obj, tNodeId, false, false);
+ }
+ }
+}
+
+void
+TransporterFacade::ReportNodeFailureComplete(NodeId tNodeId)
+{
+ /**
+ * When a node fails we must report this to each Ndb object.
+ * The function that is used for communicating node failures is called.
+ * This is to ensure that the Ndb objects do not think their connections
+ * are correct after a failure followed by a restart.
+ * After the restart the node is up again and the Ndb object
+ * might not have noticed the failure.
+ */
+
+ DBUG_ENTER("TransporterFacade::ReportNodeFailureComplete");
+ DBUG_PRINT("enter",("nodeid= %d", tNodeId));
+ Uint32 sz = m_threads.m_statusNext.size();
+ for (Uint32 i = 0; i < sz ; i ++) {
+ if (m_threads.getInUse(i)){
+ void * obj = m_threads.m_objectExecute[i].m_object;
+ NodeStatusFunction RegPC = m_threads.m_statusFunction[i];
+ (*RegPC) (obj, tNodeId, false, true);
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+void
+TransporterFacade::ReportNodeAlive(NodeId tNodeId)
+{
+ /**
+ * When a node fails we must report this to each Ndb object.
+ * The function that is used for communicating node failures is called.
+ * This is to ensure that the Ndb objects do not think there connections
+ * are correct after a failure
+ * followed by a restart.
+ * After the restart the node is up again and the Ndb object
+ * might not have noticed the failure.
+ */
+ Uint32 sz = m_threads.m_statusNext.size();
+ for (Uint32 i = 0; i < sz ; i ++) {
+ if (m_threads.getInUse(i)){
+ void * obj = m_threads.m_objectExecute[i].m_object;
+ NodeStatusFunction RegPC = m_threads.m_statusFunction[i];
+ (*RegPC) (obj, tNodeId, true, false);
+ }
+ }
+}
+
+int
+TransporterFacade::close(BlockNumber blockNumber, Uint64 trans_id)
+{
+ NdbMutex_Lock(theMutexPtr);
+ Uint32 low_bits = (Uint32)trans_id;
+ m_max_trans_id = m_max_trans_id > low_bits ? m_max_trans_id : low_bits;
+ close_local(blockNumber);
+ NdbMutex_Unlock(theMutexPtr);
+ return 0;
+}
+
+int
+TransporterFacade::close_local(BlockNumber blockNumber){
+ m_threads.close(blockNumber);
+ return 0;
+}
+
+int
+TransporterFacade::open(void* objRef,
+ ExecuteFunction fun,
+ NodeStatusFunction statusFun)
+{
+ DBUG_ENTER("TransporterFacade::open");
+ int r= m_threads.open(objRef, fun, statusFun);
+ if (r < 0)
+ DBUG_RETURN(r);
+#if 1
+ if (theOwnId > 0) {
+ (*statusFun)(objRef, numberToRef(r, theOwnId), true, true);
+ }
+#endif
+ DBUG_RETURN(r);
+}
+
+TransporterFacade::~TransporterFacade(){
+
+ NdbMutex_Lock(theMutexPtr);
+ delete theClusterMgr;
+ delete theArbitMgr;
+ delete theTransporterRegistry;
+ NdbMutex_Unlock(theMutexPtr);
+ NdbMutex_Destroy(theMutexPtr);
+#ifdef API_TRACE
+ signalLogger.setOutputStream(0);
+#endif
+}
+
+void
+TransporterFacade::calculateSendLimit()
+{
+ Uint32 Ti;
+ Uint32 TthreadCount = 0;
+
+ Uint32 sz = m_threads.m_statusNext.size();
+ for (Ti = 0; Ti < sz; Ti++) {
+ if (m_threads.m_statusNext[Ti] == (ThreadData::ACTIVE)){
+ TthreadCount++;
+ m_threads.m_statusNext[Ti] = ThreadData::INACTIVE;
+ }
+ }
+ currentSendLimit = TthreadCount;
+ if (currentSendLimit == 0) {
+ currentSendLimit = 1;
+ }
+ checkCounter = currentSendLimit << 2;
+}
+
+
+//-------------------------------------------------
+// Force sending but still report the sending to the
+// adaptive algorithm.
+//-------------------------------------------------
+void TransporterFacade::forceSend(Uint32 block_number) {
+ checkCounter--;
+ m_threads.m_statusNext[numberToIndex(block_number)] = ThreadData::ACTIVE;
+ sendPerformedLastInterval = 1;
+ if (checkCounter < 0) {
+ calculateSendLimit();
+ }
+ theTransporterRegistry->forceSendCheck(0);
+}
+
+//-------------------------------------------------
+// Improving API performance
+//-------------------------------------------------
+void
+TransporterFacade::checkForceSend(Uint32 block_number) {
+ m_threads.m_statusNext[numberToIndex(block_number)] = ThreadData::ACTIVE;
+ //-------------------------------------------------
+ // This code is an adaptive algorithm to discover when
+ // the API should actually send its buffers. The reason
+ // is that the performance is highly dependent on the
+ // size of the writes over the communication network.
+ // Thus we try to ensure that the send size is as big
+ // as possible. At the same time we don't want response
+ // time to increase so therefore we have to keep track of
+ // how the users are performing adaptively.
+ //-------------------------------------------------
+
+ if (theTransporterRegistry->forceSendCheck(currentSendLimit) == 1) {
+ sendPerformedLastInterval = 1;
+ }
+ checkCounter--;
+ if (checkCounter < 0) {
+ calculateSendLimit();
+ }
+}
+
+
+/******************************************************************************
+ * SEND SIGNAL METHODS
+ *****************************************************************************/
+int
+TransporterFacade::sendSignal(NdbApiSignal * aSignal, NodeId aNode){
+ Uint32* tDataPtr = aSignal->getDataPtrSend();
+ Uint32 Tlen = aSignal->theLength;
+ Uint32 TBno = aSignal->theReceiversBlockNumber;
+ if(getIsNodeSendable(aNode) == true){
+#ifdef API_TRACE
+ if(setSignalLog() && TRACE_GSN(aSignal->theVerId_signalNumber)){
+ Uint32 tmp = aSignal->theSendersBlockRef;
+ aSignal->theSendersBlockRef = numberToRef(tmp, theOwnId);
+ LinearSectionPtr ptr[3];
+ signalLogger.sendSignal(* aSignal,
+ 1,
+ aSignal->getDataPtr(),
+ aNode, ptr, 0);
+ signalLogger.flushSignalLog();
+ aSignal->theSendersBlockRef = tmp;
+ }
+#endif
+ if ((Tlen != 0) && (Tlen <= 25) && (TBno != 0)) {
+ SendStatus ss = theTransporterRegistry->prepareSend(aSignal,
+ 1, // JBB
+ tDataPtr,
+ aNode,
+ 0);
+ //if (ss != SEND_OK) ndbout << ss << endl;
+ return (ss == SEND_OK ? 0 : -1);
+ } else {
+ ndbout << "ERR: SigLen = " << Tlen << " BlockRec = " << TBno;
+ ndbout << " SignalNo = " << aSignal->theVerId_signalNumber << endl;
+ assert(0);
+ }//if
+ }
+ //const ClusterMgr::Node & node = theClusterMgr->getNodeInfo(aNode);
+ //const Uint32 startLevel = node.m_state.startLevel;
+ return -1; // Node Dead
+}
+
+int
+TransporterFacade::sendSignalUnCond(NdbApiSignal * aSignal, NodeId aNode){
+#ifdef API_TRACE
+ if(setSignalLog() && TRACE_GSN(aSignal->theVerId_signalNumber)){
+ Uint32 tmp = aSignal->theSendersBlockRef;
+ aSignal->theSendersBlockRef = numberToRef(tmp, theOwnId);
+ LinearSectionPtr ptr[3];
+ signalLogger.sendSignal(* aSignal,
+ 0,
+ aSignal->getDataPtr(),
+ aNode, ptr, 0);
+ signalLogger.flushSignalLog();
+ aSignal->theSendersBlockRef = tmp;
+ }
+#endif
+ assert((aSignal->theLength != 0) &&
+ (aSignal->theLength <= 25) &&
+ (aSignal->theReceiversBlockNumber != 0));
+ SendStatus ss = theTransporterRegistry->prepareSend(aSignal,
+ 0,
+ aSignal->getDataPtr(),
+ aNode,
+ 0);
+
+ return (ss == SEND_OK ? 0 : -1);
+}
+
+#define CHUNK_SZ NDB_SECTION_SEGMENT_SZ*64 // related to MAX_MESSAGE_SIZE
+int
+TransporterFacade::sendFragmentedSignal(NdbApiSignal* aSignal, NodeId aNode,
+ LinearSectionPtr ptr[3], Uint32 secs)
+{
+ if(getIsNodeSendable(aNode) != true)
+ return -1;
+
+#ifdef API_TRACE
+ if(setSignalLog() && TRACE_GSN(aSignal->theVerId_signalNumber)){
+ Uint32 tmp = aSignal->theSendersBlockRef;
+ aSignal->theSendersBlockRef = numberToRef(tmp, theOwnId);
+ signalLogger.sendSignal(* aSignal,
+ 1,
+ aSignal->getDataPtrSend(),
+ aNode,
+ ptr, secs);
+ aSignal->theSendersBlockRef = tmp;
+ }
+#endif
+
+ NdbApiSignal tmp_signal(*(SignalHeader*)aSignal);
+ LinearSectionPtr tmp_ptr[3];
+ Uint32 unique_id= m_fragmented_signal_id++; // next unique id
+ unsigned i;
+ for (i= 0; i < secs; i++)
+ tmp_ptr[i]= ptr[i];
+
+ unsigned start_i= 0;
+ unsigned chunk_sz= 0;
+ unsigned fragment_info= 0;
+ Uint32 *tmp_data= tmp_signal.getDataPtrSend();
+ for (i= 0; i < secs;) {
+ unsigned save_sz= tmp_ptr[i].sz;
+ tmp_data[i-start_i]= i;
+ if (chunk_sz + save_sz > CHUNK_SZ) {
+ // truncate
+ unsigned send_sz= CHUNK_SZ - chunk_sz;
+ if (i != start_i) // first piece of a new section has to be a multiple of NDB_SECTION_SEGMENT_SZ
+ {
+ send_sz=
+ NDB_SECTION_SEGMENT_SZ
+ *(send_sz+NDB_SECTION_SEGMENT_SZ-1)
+ /NDB_SECTION_SEGMENT_SZ;
+ if (send_sz > save_sz)
+ send_sz= save_sz;
+ }
+ tmp_ptr[i].sz= send_sz;
+
+ if (fragment_info < 2) // 1 = first fragment, 2 = middle fragments
+ fragment_info++;
+
+ // send tmp_signal
+ tmp_data[i-start_i+1]= unique_id;
+ tmp_signal.setLength(i-start_i+2);
+ tmp_signal.m_fragmentInfo= fragment_info;
+ tmp_signal.m_noOfSections= i-start_i+1;
+ // do prepare send
+ {
+ SendStatus ss = theTransporterRegistry->prepareSend
+ (&tmp_signal,
+ 1, /*JBB*/
+ tmp_data,
+ aNode,
+ &tmp_ptr[start_i]);
+ assert(ss != SEND_MESSAGE_TOO_BIG);
+ if (ss != SEND_OK) return -1;
+ }
+ // setup variables for next signal
+ start_i= i;
+ chunk_sz= 0;
+ tmp_ptr[i].sz= save_sz-send_sz;
+ tmp_ptr[i].p+= send_sz;
+ if (tmp_ptr[i].sz == 0)
+ i++;
+ }
+ else
+ {
+ chunk_sz+=save_sz;
+ i++;
+ }
+ }
+
+ unsigned a_sz= aSignal->getLength();
+
+ if (fragment_info > 0) {
+ // update the original signal to include section info
+ Uint32 *a_data= aSignal->getDataPtrSend();
+ unsigned tmp_sz= i-start_i;
+ memcpy(a_data+a_sz,
+ tmp_data,
+ tmp_sz*sizeof(Uint32));
+ a_data[a_sz+tmp_sz]= unique_id;
+ aSignal->setLength(a_sz+tmp_sz+1);
+
+ // send last fragment
+ aSignal->m_fragmentInfo= 3; // 3 = last fragment
+ aSignal->m_noOfSections= i-start_i;
+ } else {
+ aSignal->m_noOfSections= secs;
+ }
+
+ // send aSignal
+ int ret;
+ {
+ SendStatus ss = theTransporterRegistry->prepareSend
+ (aSignal,
+ 1/*JBB*/,
+ aSignal->getDataPtrSend(),
+ aNode,
+ &tmp_ptr[start_i]);
+ assert(ss != SEND_MESSAGE_TOO_BIG);
+ ret = (ss == SEND_OK ? 0 : -1);
+ }
+ aSignal->m_noOfSections = 0;
+ aSignal->m_fragmentInfo = 0;
+ aSignal->setLength(a_sz);
+ return ret;
+}
+
+int
+TransporterFacade::sendSignal(NdbApiSignal* aSignal, NodeId aNode,
+ LinearSectionPtr ptr[3], Uint32 secs){
+ aSignal->m_noOfSections = secs;
+ if(getIsNodeSendable(aNode) == true){
+#ifdef API_TRACE
+ if(setSignalLog() && TRACE_GSN(aSignal->theVerId_signalNumber)){
+ Uint32 tmp = aSignal->theSendersBlockRef;
+ aSignal->theSendersBlockRef = numberToRef(tmp, theOwnId);
+ signalLogger.sendSignal(* aSignal,
+ 1,
+ aSignal->getDataPtrSend(),
+ aNode,
+ ptr, secs);
+ signalLogger.flushSignalLog();
+ aSignal->theSendersBlockRef = tmp;
+ }
+#endif
+ SendStatus ss = theTransporterRegistry->prepareSend
+ (aSignal,
+ 1, // JBB
+ aSignal->getDataPtrSend(),
+ aNode,
+ ptr);
+ assert(ss != SEND_MESSAGE_TOO_BIG);
+ aSignal->m_noOfSections = 0;
+ return (ss == SEND_OK ? 0 : -1);
+ }
+ aSignal->m_noOfSections = 0;
+ return -1;
+}
+
+/******************************************************************************
+ * CONNECTION METHODS Etc
+ ******************************************************************************/
+
+void
+TransporterFacade::doConnect(int aNodeId){
+ theTransporterRegistry->setIOState(aNodeId, NoHalt);
+ theTransporterRegistry->do_connect(aNodeId);
+}
+
+void
+TransporterFacade::doDisconnect(int aNodeId)
+{
+ theTransporterRegistry->do_disconnect(aNodeId);
+}
+
+void
+TransporterFacade::reportConnected(int aNodeId)
+{
+ theClusterMgr->reportConnected(aNodeId);
+ return;
+}
+
+void
+TransporterFacade::reportDisconnected(int aNodeId)
+{
+ theClusterMgr->reportDisconnected(aNodeId);
+ return;
+}
+
+NodeId
+TransporterFacade::ownId() const
+{
+ return theOwnId;
+}
+
+bool
+TransporterFacade::isConnected(NodeId aNodeId){
+ return theTransporterRegistry->is_connected(aNodeId);
+}
+
+NodeId
+TransporterFacade::get_an_alive_node()
+{
+ DBUG_ENTER("TransporterFacade::get_an_alive_node");
+ DBUG_PRINT("enter", ("theStartNodeId: %d", theStartNodeId));
+#ifdef VM_TRACE
+ const char* p = NdbEnv_GetEnv("NDB_ALIVE_NODE_ID", (char*)0, 0);
+ if (p != 0 && *p != 0)
+ return atoi(p);
+#endif
+ NodeId i;
+ for (i = theStartNodeId; i < MAX_NDB_NODES; i++) {
+ if (get_node_alive(i)){
+ DBUG_PRINT("info", ("Node %d is alive", i));
+ theStartNodeId = ((i + 1) % MAX_NDB_NODES);
+ DBUG_RETURN(i);
+ }
+ }
+ for (i = 1; i < theStartNodeId; i++) {
+ if (get_node_alive(i)){
+ DBUG_PRINT("info", ("Node %d is alive", i));
+ theStartNodeId = ((i + 1) % MAX_NDB_NODES);
+ DBUG_RETURN(i);
+ }
+ }
+ DBUG_RETURN((NodeId)0);
+}
+
+TransporterFacade::ThreadData::ThreadData(Uint32 size){
+ m_firstFree = END_OF_LIST;
+ expand(size);
+}
+
+void
+TransporterFacade::ThreadData::expand(Uint32 size){
+ Object_Execute oe = { 0 ,0 };
+ NodeStatusFunction fun = 0;
+
+ const Uint32 sz = m_statusNext.size();
+ m_objectExecute.fill(sz + size, oe);
+ m_statusFunction.fill(sz + size, fun);
+ for(Uint32 i = 0; i<size; i++){
+ m_statusNext.push_back(sz + i + 1);
+ }
+
+ m_statusNext.back() = m_firstFree;
+ m_firstFree = m_statusNext.size() - size;
+}
+
+
+int
+TransporterFacade::ThreadData::open(void* objRef,
+ ExecuteFunction fun,
+ NodeStatusFunction fun2)
+{
+ Uint32 nextFree = m_firstFree;
+
+ if(m_statusNext.size() >= MAX_NO_THREADS && nextFree == END_OF_LIST){
+ return -1;
+ }
+
+ if(nextFree == END_OF_LIST){
+ expand(10);
+ nextFree = m_firstFree;
+ }
+
+ m_firstFree = m_statusNext[nextFree];
+
+ Object_Execute oe = { objRef , fun };
+
+ m_statusNext[nextFree] = INACTIVE;
+ m_objectExecute[nextFree] = oe;
+ m_statusFunction[nextFree] = fun2;
+
+ return indexToNumber(nextFree);
+}
+
+int
+TransporterFacade::ThreadData::close(int number){
+ number= numberToIndex(number);
+ assert(getInUse(number));
+ m_statusNext[number] = m_firstFree;
+ m_firstFree = number;
+ Object_Execute oe = { 0, 0 };
+ m_objectExecute[number] = oe;
+ m_statusFunction[number] = 0;
+ return 0;
+}
+
+template class Vector<NodeStatusFunction>;
+template class Vector<TransporterFacade::ThreadData::Object_Execute>;
diff --git a/storage/ndb/src/ndbapi/TransporterFacade.hpp b/storage/ndb/src/ndbapi/TransporterFacade.hpp
new file mode 100644
index 00000000000..e74f4b51e00
--- /dev/null
+++ b/storage/ndb/src/ndbapi/TransporterFacade.hpp
@@ -0,0 +1,363 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TransporterFacade_H
+#define TransporterFacade_H
+
+#include <kernel_types.h>
+#include <ndb_limits.h>
+#include <NdbThread.h>
+#include <TransporterRegistry.hpp>
+#include <NdbMutex.h>
+#include "DictCache.hpp"
+#include <BlockNumbers.h>
+#include <mgmapi.h>
+
+class ClusterMgr;
+class ArbitMgr;
+class IPCConfig;
+struct ndb_mgm_configuration;
+class ConfigRetriever;
+
+class Ndb;
+class NdbApiSignal;
+
+typedef void (* ExecuteFunction)(void *, NdbApiSignal *, LinearSectionPtr ptr[3]);
+typedef void (* NodeStatusFunction)(void *, Uint32, bool nodeAlive, bool nfComplete);
+
+extern "C" {
+ void* runSendRequest_C(void*);
+ void* runReceiveResponse_C(void*);
+ void atexit_stop_instance();
+}
+
+class TransporterFacade
+{
+public:
+ TransporterFacade();
+ virtual ~TransporterFacade();
+ bool init(Uint32, const ndb_mgm_configuration *);
+
+ static TransporterFacade* instance();
+ int start_instance(int, const ndb_mgm_configuration*);
+ static void stop_instance();
+
+ /**
+ * Register this block for sending/receiving signals
+ * @return BlockNumber or -1 for failure
+ */
+ int open(void* objRef, ExecuteFunction, NodeStatusFunction);
+
+ // Close this block number
+ int close(BlockNumber blockNumber, Uint64 trans_id);
+
+ // Only sends to nodes which are alive
+ int sendSignal(NdbApiSignal * signal, NodeId nodeId);
+ int sendSignal(NdbApiSignal*, NodeId,
+ LinearSectionPtr ptr[3], Uint32 secs);
+ int sendFragmentedSignal(NdbApiSignal*, NodeId,
+ LinearSectionPtr ptr[3], Uint32 secs);
+
+ // Is node available for running transactions
+ bool get_node_alive(NodeId nodeId) const;
+ bool get_node_stopping(NodeId nodeId) const;
+ bool getIsDbNode(NodeId nodeId) const;
+ bool getIsNodeSendable(NodeId nodeId) const;
+ Uint32 getNodeGrp(NodeId nodeId) const;
+ Uint32 getNodeSequence(NodeId nodeId) const;
+
+ // Is there space in sendBuffer to send messages
+ bool check_send_size(Uint32 node_id, Uint32 send_size);
+
+ // My own processor id
+ NodeId ownId() const;
+
+ void connected();
+
+ void doConnect(int NodeId);
+ void reportConnected(int NodeId);
+ void doDisconnect(int NodeId);
+ void reportDisconnected(int NodeId);
+
+ NodeId get_an_alive_node();
+ void ReportNodeAlive(NodeId nodeId);
+ void ReportNodeDead(NodeId nodeId);
+ void ReportNodeFailureComplete(NodeId nodeId);
+
+ void lock_mutex();
+ void unlock_mutex();
+
+ // Improving the API performance
+ void forceSend(Uint32 block_number);
+ void checkForceSend(Uint32 block_number);
+
+ // Close this block number
+ int close_local(BlockNumber blockNumber);
+
+ // Scan batch configuration parameters
+ Uint32 get_scan_batch_size();
+ Uint32 get_batch_byte_size();
+ Uint32 get_batch_size();
+
+ TransporterRegistry* get_registry() { return theTransporterRegistry;};
+
+private:
+ /**
+ * Send a signal unconditional of node status (used by ClusterMgr)
+ */
+ friend class ClusterMgr;
+ friend class ArbitMgr;
+ friend class MgmtSrvr;
+ friend class SignalSender;
+ friend class GrepPS;
+ friend class ExtSender; ///< @todo Hack to be able to sendSignalUnCond
+ friend class GrepSS;
+ friend class Ndb;
+ friend class Ndb_cluster_connection_impl;
+
+ int sendSignalUnCond(NdbApiSignal *, NodeId nodeId);
+
+ bool isConnected(NodeId aNodeId);
+ void doStop();
+
+ TransporterRegistry* theTransporterRegistry;
+ SocketServer m_socket_server;
+ int sendPerformedLastInterval;
+ int theOwnId;
+
+ NodeId theStartNodeId;
+
+ ClusterMgr* theClusterMgr;
+ ArbitMgr* theArbitMgr;
+
+ // Improving the API response time
+ int checkCounter;
+ Uint32 currentSendLimit;
+
+ void calculateSendLimit();
+
+ // Scan batch configuration parameters
+ Uint32 m_scan_batch_size;
+ Uint32 m_batch_byte_size;
+ Uint32 m_batch_size;
+
+ // Declarations for the receive and send thread
+ int theStopReceive;
+
+ void threadMainSend(void);
+ NdbThread* theSendThread;
+ void threadMainReceive(void);
+ NdbThread* theReceiveThread;
+
+ friend void* runSendRequest_C(void*);
+ friend void* runReceiveResponse_C(void*);
+ friend void atexit_stop_instance();
+
+ /**
+ * Block number handling
+ */
+public:
+ /**
+ * Max number of Ndb objects.
+ * (Ndb objects should not be shared by different threads.)
+ */
+ STATIC_CONST( MAX_NO_THREADS = 4711 );
+private:
+
+ struct ThreadData {
+ STATIC_CONST( ACTIVE = (1 << 16) | 1 );
+ STATIC_CONST( INACTIVE = (1 << 16) );
+ STATIC_CONST( END_OF_LIST = MAX_NO_THREADS + 1 );
+
+ ThreadData(Uint32 initialSize = 32);
+
+ /**
+ * Split "object" into 3 list
+ * This to improve locality
+ * when iterating over lists
+ */
+ struct Object_Execute {
+ void * m_object;
+ ExecuteFunction m_executeFunction;
+ };
+ struct NodeStatus_NextFree {
+ NodeStatusFunction m_statusFunction;
+ };
+
+ Uint32 m_firstFree;
+ Vector<Uint32> m_statusNext;
+ Vector<Object_Execute> m_objectExecute;
+ Vector<NodeStatusFunction> m_statusFunction;
+
+ int open(void* objRef, ExecuteFunction, NodeStatusFunction);
+ int close(int number);
+ void expand(Uint32 size);
+
+ inline Object_Execute get(Uint16 blockNo) const {
+ blockNo -= MIN_API_BLOCK_NO;
+ if(blockNo < m_objectExecute.size()){
+ return m_objectExecute[blockNo];
+ }
+ Object_Execute oe = { 0, 0 };
+ return oe;
+ }
+
+ /**
+ * Is the block number used currently
+ */
+ inline bool getInUse(Uint16 index) const {
+ return (m_statusNext[index] & (1 << 16)) != 0;
+ }
+ } m_threads;
+
+ Uint32 m_max_trans_id;
+ Uint32 m_fragmented_signal_id;
+
+ /**
+ * execute function
+ */
+ friend void execute(void * callbackObj, SignalHeader * const header,
+ Uint8 prio,
+ Uint32 * const theData, LinearSectionPtr ptr[3]);
+
+public:
+ NdbMutex* theMutexPtr;
+private:
+ static TransporterFacade* theFacadeInstance;
+
+public:
+ GlobalDictCache m_globalDictCache;
+};
+
+inline
+TransporterFacade*
+TransporterFacade::instance()
+{
+ return theFacadeInstance;
+}
+
+inline
+void
+TransporterFacade::lock_mutex()
+{
+ NdbMutex_Lock(theMutexPtr);
+}
+
+inline
+void
+TransporterFacade::unlock_mutex()
+{
+ NdbMutex_Unlock(theMutexPtr);
+}
+
+#include "ClusterMgr.hpp"
+
+inline
+bool
+TransporterFacade::check_send_size(Uint32 node_id, Uint32 send_size)
+{
+ return true;
+}
+
+inline
+bool
+TransporterFacade::getIsDbNode(NodeId n) const {
+ return
+ theClusterMgr->getNodeInfo(n).defined &&
+ theClusterMgr->getNodeInfo(n).m_info.m_type == NodeInfo::DB;
+}
+
+inline
+Uint32
+TransporterFacade::getNodeGrp(NodeId n) const {
+ return theClusterMgr->getNodeInfo(n).m_state.nodeGroup;
+}
+
+
+inline
+bool
+TransporterFacade::get_node_alive(NodeId n) const {
+
+ const ClusterMgr::Node & node = theClusterMgr->getNodeInfo(n);
+ return node.m_alive;
+}
+
+inline
+bool
+TransporterFacade::get_node_stopping(NodeId n) const {
+ const ClusterMgr::Node & node = theClusterMgr->getNodeInfo(n);
+ return ((node.m_state.startLevel == NodeState::SL_STOPPING_1) ||
+ (node.m_state.startLevel == NodeState::SL_STOPPING_2));
+}
+
+inline
+bool
+TransporterFacade::getIsNodeSendable(NodeId n) const {
+ const ClusterMgr::Node & node = theClusterMgr->getNodeInfo(n);
+ const Uint32 startLevel = node.m_state.startLevel;
+
+ if (node.m_info.m_type == NodeInfo::DB) {
+ if(node.m_state.singleUserMode &&
+ ownId() == node.m_state.singleUserApi) {
+ return (node.compatible &&
+ (node.m_state.startLevel == NodeState::SL_STOPPING_1 ||
+ node.m_state.startLevel == NodeState::SL_STARTED ||
+ node.m_state.startLevel == NodeState::SL_SINGLEUSER));
+ }
+ else
+ return node.compatible && (startLevel == NodeState::SL_STARTED ||
+ startLevel == NodeState::SL_STOPPING_1);
+ } else if (node.m_info.m_type == NodeInfo::REP) {
+ /**
+ * @todo Check that REP node actually has received API_REG_REQ
+ */
+ return node.compatible;
+ } else {
+ ndbout_c("TransporterFacade::getIsNodeSendable: Illegal node type: "
+ "%d of node: %d",
+ node.m_info.m_type, n);
+ abort();
+ return false; // to remove compiler warning
+ }
+}
+
+inline
+Uint32
+TransporterFacade::getNodeSequence(NodeId n) const {
+ return theClusterMgr->getNodeInfo(n).m_info.m_connectCount;
+}
+
+inline
+Uint32
+TransporterFacade::get_scan_batch_size() {
+ return m_scan_batch_size;
+}
+
+inline
+Uint32
+TransporterFacade::get_batch_byte_size() {
+ return m_batch_byte_size;
+}
+
+inline
+Uint32
+TransporterFacade::get_batch_size() {
+ return m_batch_size;
+}
+
+
+
+#endif // TransporterFacade_H
diff --git a/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp
new file mode 100644
index 00000000000..49aded8e0ac
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp
@@ -0,0 +1,550 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <my_pthread.h>
+#include <my_sys.h>
+
+#include "ndb_cluster_connection_impl.hpp"
+#include <mgmapi_configuration.hpp>
+#include <mgmapi_config_parameters.h>
+#include <TransporterFacade.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbThread.h>
+#include <ndb_limits.h>
+#include <ConfigRetriever.hpp>
+#include <ndb_version.h>
+#include <mgmapi_debug.h>
+#include <mgmapi_internal.h>
+#include <md5_hash.hpp>
+
+#include <EventLogger.hpp>
+EventLogger g_eventLogger;
+
+static int g_run_connect_thread= 0;
+
+#include <NdbMutex.h>
+NdbMutex *ndb_global_event_buffer_mutex= NULL;
+#ifdef VM_TRACE
+NdbMutex *ndb_print_state_mutex= NULL;
+#endif
+
+/*
+ * Ndb_cluster_connection
+ */
+
+Ndb_cluster_connection::Ndb_cluster_connection(const char *connect_string)
+ : m_impl(* new Ndb_cluster_connection_impl(connect_string))
+{
+}
+
+Ndb_cluster_connection::Ndb_cluster_connection
+(Ndb_cluster_connection_impl& impl) : m_impl(impl)
+{
+}
+
+Ndb_cluster_connection::~Ndb_cluster_connection()
+{
+ Ndb_cluster_connection_impl *tmp = &m_impl;
+ if (this != tmp)
+ delete tmp;
+}
+
+int Ndb_cluster_connection::get_connected_port() const
+{
+ if (m_impl.m_config_retriever)
+ return m_impl.m_config_retriever->get_mgmd_port();
+ return -1;
+}
+
+const char *Ndb_cluster_connection::get_connected_host() const
+{
+ if (m_impl.m_config_retriever)
+ return m_impl.m_config_retriever->get_mgmd_host();
+ return 0;
+}
+
+const char *Ndb_cluster_connection::get_connectstring(char *buf,
+ int buf_sz) const
+{
+ if (m_impl.m_config_retriever)
+ return m_impl.m_config_retriever->get_connectstring(buf,buf_sz);
+ return 0;
+}
+
+extern "C" pthread_handler_decl(run_ndb_cluster_connection_connect_thread, me)
+{
+ g_run_connect_thread= 1;
+ ((Ndb_cluster_connection_impl*) me)->connect_thread();
+ return me;
+}
+
+int Ndb_cluster_connection::start_connect_thread(int (*connect_callback)(void))
+{
+ int r;
+ DBUG_ENTER("Ndb_cluster_connection::start_connect_thread");
+ m_impl.m_connect_callback= connect_callback;
+ if ((r = connect(0,0,0)) == 1)
+ {
+ DBUG_PRINT("info",("starting thread"));
+ m_impl.m_connect_thread=
+ NdbThread_Create(run_ndb_cluster_connection_connect_thread,
+ (void**)&m_impl, 32768, "ndb_cluster_connection",
+ NDB_THREAD_PRIO_LOW);
+ }
+ else if (r < 0)
+ {
+ DBUG_RETURN(-1);
+ }
+ else if (m_impl.m_connect_callback)
+ {
+ (*m_impl.m_connect_callback)();
+ }
+ DBUG_RETURN(0);
+}
+
+void Ndb_cluster_connection::set_optimized_node_selection(int val)
+{
+ m_impl.m_optimized_node_selection= val;
+}
+
+void
+Ndb_cluster_connection_impl::init_get_next_node
+(Ndb_cluster_connection_node_iter &iter)
+{
+ if (iter.scan_state != (Uint8)~0)
+ iter.cur_pos= iter.scan_state;
+ if (iter.cur_pos >= no_db_nodes())
+ iter.cur_pos= 0;
+ iter.init_pos= iter.cur_pos;
+ iter.scan_state= 0;
+ // fprintf(stderr,"[init %d]",iter.init_pos);
+ return;
+}
+
+Uint32
+Ndb_cluster_connection_impl::get_next_node(Ndb_cluster_connection_node_iter &iter)
+{
+ Uint32 cur_pos= iter.cur_pos;
+ if (cur_pos >= no_db_nodes())
+ return 0;
+
+ Ndb_cluster_connection_impl::Node *nodes= m_impl.m_all_nodes.getBase();
+ Ndb_cluster_connection_impl::Node &node= nodes[cur_pos];
+
+ if (iter.scan_state != (Uint8)~0)
+ {
+ assert(iter.scan_state < no_db_nodes());
+ if (nodes[iter.scan_state].group == node.group)
+ iter.scan_state= ~0;
+ else
+ return nodes[iter.scan_state++].id;
+ }
+
+ // fprintf(stderr,"[%d]",node.id);
+
+ cur_pos++;
+ Uint32 init_pos= iter.init_pos;
+ if (cur_pos == node.next_group)
+ {
+ cur_pos= nodes[init_pos].this_group;
+ }
+
+ // fprintf(stderr,"[cur_pos %d]",cur_pos);
+ if (cur_pos != init_pos)
+ iter.cur_pos= cur_pos;
+ else
+ {
+ iter.cur_pos= node.next_group;
+ iter.init_pos= node.next_group;
+ }
+ return node.id;
+}
+
+unsigned
+Ndb_cluster_connection::no_db_nodes()
+{
+ return m_impl.m_all_nodes.size();
+}
+
+unsigned
+Ndb_cluster_connection::node_id()
+{
+ return m_impl.m_transporter_facade->ownId();
+}
+
+
+int
+Ndb_cluster_connection::wait_until_ready(int timeout,
+ int timeout_after_first_alive)
+{
+ DBUG_ENTER("Ndb_cluster_connection::wait_until_ready");
+ TransporterFacade *tp = TransporterFacade::instance();
+ if (tp == 0)
+ {
+ DBUG_RETURN(-1);
+ }
+ if (tp->ownId() == 0)
+ {
+ DBUG_RETURN(-1);
+ }
+ int secondsCounter = 0;
+ int milliCounter = 0;
+ int noChecksSinceFirstAliveFound = 0;
+ do {
+ unsigned int foundAliveNode = 0;
+ tp->lock_mutex();
+ for(unsigned i= 0; i < no_db_nodes(); i++)
+ {
+ //************************************************
+ // If any node is answering, ndb is answering
+ //************************************************
+ if (tp->get_node_alive(m_impl.m_all_nodes[i].id) != 0) {
+ foundAliveNode++;
+ }
+ }
+ tp->unlock_mutex();
+
+ if (foundAliveNode == no_db_nodes())
+ {
+ DBUG_RETURN(0);
+ }
+ else if (foundAliveNode > 0)
+ {
+ noChecksSinceFirstAliveFound++;
+ // 100 ms delay -> 10*
+ if (noChecksSinceFirstAliveFound > 10*timeout_after_first_alive)
+ DBUG_RETURN(1);
+ }
+ else if (secondsCounter >= timeout)
+ { // no alive nodes and timed out
+ DBUG_RETURN(-1);
+ }
+ NdbSleep_MilliSleep(100);
+ milliCounter += 100;
+ if (milliCounter >= 1000) {
+ secondsCounter++;
+ milliCounter = 0;
+ }//if
+ } while (1);
+}
+
+
+
+/*
+ * Ndb_cluster_connection_impl
+ */
+
+Ndb_cluster_connection_impl::Ndb_cluster_connection_impl(const char *
+ connect_string)
+ : Ndb_cluster_connection(*this),
+ m_optimized_node_selection(1)
+{
+ DBUG_ENTER("Ndb_cluster_connection");
+ DBUG_PRINT("enter",("Ndb_cluster_connection this=0x%x", this));
+
+ g_eventLogger.createConsoleHandler();
+ g_eventLogger.setCategory("NdbApi");
+ g_eventLogger.enable(Logger::LL_ON, Logger::LL_ERROR);
+
+ m_connect_thread= 0;
+ m_connect_callback= 0;
+
+ if (ndb_global_event_buffer_mutex == NULL)
+ {
+ ndb_global_event_buffer_mutex= NdbMutex_Create();
+ }
+#ifdef VM_TRACE
+ if (ndb_print_state_mutex == NULL)
+ {
+ ndb_print_state_mutex= NdbMutex_Create();
+ }
+#endif
+ m_config_retriever=
+ new ConfigRetriever(connect_string, NDB_VERSION, NODE_TYPE_API);
+ if (m_config_retriever->hasError())
+ {
+ printf("Could not connect initialize handle to management server: %s",
+ m_config_retriever->getErrorString());
+ delete m_config_retriever;
+ m_config_retriever= 0;
+ }
+
+ m_transporter_facade=
+ TransporterFacade::theFacadeInstance=
+ new TransporterFacade();
+
+ DBUG_VOID_RETURN;
+}
+
+Ndb_cluster_connection_impl::~Ndb_cluster_connection_impl()
+{
+ DBUG_ENTER("~Ndb_cluster_connection");
+ DBUG_PRINT("enter",("~Ndb_cluster_connection this=0x%x", this));
+ TransporterFacade::stop_instance();
+ if (m_connect_thread)
+ {
+ void *status;
+ g_run_connect_thread= 0;
+ NdbThread_WaitFor(m_connect_thread, &status);
+ NdbThread_Destroy(&m_connect_thread);
+ m_connect_thread= 0;
+ }
+ if (m_transporter_facade != 0)
+ {
+ delete m_transporter_facade;
+ if (m_transporter_facade != TransporterFacade::theFacadeInstance)
+ abort();
+ TransporterFacade::theFacadeInstance= 0;
+ }
+ if (m_config_retriever)
+ delete m_config_retriever;
+
+ // fragmentToNodeMap.release();
+
+ DBUG_VOID_RETURN;
+}
+
+void
+Ndb_cluster_connection_impl::init_nodes_vector(Uint32 nodeid,
+ const ndb_mgm_configuration
+ &config)
+{
+ DBUG_ENTER("Ndb_cluster_connection_impl::init_nodes_vector");
+ ndb_mgm_configuration_iterator iter(config, CFG_SECTION_CONNECTION);
+
+ for(iter.first(); iter.valid(); iter.next())
+ {
+ Uint32 nodeid1, nodeid2, remoteNodeId, group= 5;
+ const char * remoteHostName= 0, * localHostName= 0;
+ if(iter.get(CFG_CONNECTION_NODE_1, &nodeid1)) continue;
+ if(iter.get(CFG_CONNECTION_NODE_2, &nodeid2)) continue;
+
+ if(nodeid1 != nodeid && nodeid2 != nodeid) continue;
+ remoteNodeId = (nodeid == nodeid1 ? nodeid2 : nodeid1);
+
+ iter.get(CFG_CONNECTION_GROUP, &group);
+
+ {
+ const char * host1= 0, * host2= 0;
+ iter.get(CFG_CONNECTION_HOSTNAME_1, &host1);
+ iter.get(CFG_CONNECTION_HOSTNAME_2, &host2);
+ localHostName = (nodeid == nodeid1 ? host1 : host2);
+ remoteHostName = (nodeid == nodeid1 ? host2 : host1);
+ }
+
+ Uint32 type = ~0;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type)) continue;
+
+ switch(type){
+ case CONNECTION_TYPE_SHM:{
+ break;
+ }
+ case CONNECTION_TYPE_SCI:{
+ break;
+ }
+ case CONNECTION_TYPE_TCP:{
+ // connecting through localhost
+ // check if config_hostname is local
+ if (SocketServer::tryBind(0,remoteHostName))
+ group--; // upgrade group value
+ break;
+ }
+ case CONNECTION_TYPE_OSE:{
+ break;
+ }
+ }
+ m_impl.m_all_nodes.push_back(Node(group,remoteNodeId));
+ DBUG_PRINT("info",("saved %d %d", group,remoteNodeId));
+ for (int i= m_impl.m_all_nodes.size()-2;
+ i >= 0 && m_impl.m_all_nodes[i].group > m_impl.m_all_nodes[i+1].group;
+ i--)
+ {
+ Node tmp= m_impl.m_all_nodes[i];
+ m_impl.m_all_nodes[i]= m_impl.m_all_nodes[i+1];
+ m_impl.m_all_nodes[i+1]= tmp;
+ }
+ }
+
+ int i;
+ Uint32 cur_group, i_group= 0;
+ cur_group= ~0;
+ for (i= (int)m_impl.m_all_nodes.size()-1; i >= 0; i--)
+ {
+ if (m_impl.m_all_nodes[i].group != cur_group)
+ {
+ cur_group= m_impl.m_all_nodes[i].group;
+ i_group= i+1;
+ }
+ m_impl.m_all_nodes[i].next_group= i_group;
+ }
+ cur_group= ~0;
+ for (i= 0; i < (int)m_impl.m_all_nodes.size(); i++)
+ {
+ if (m_impl.m_all_nodes[i].group != cur_group)
+ {
+ cur_group= m_impl.m_all_nodes[i].group;
+ i_group= i;
+ }
+ m_impl.m_all_nodes[i].this_group= i_group;
+ }
+#if 0
+ for (i= 0; i < (int)m_impl.m_all_nodes.size(); i++)
+ {
+ fprintf(stderr, "[%d] %d %d %d %d\n",
+ i,
+ m_impl.m_all_nodes[i].id,
+ m_impl.m_all_nodes[i].group,
+ m_impl.m_all_nodes[i].this_group,
+ m_impl.m_all_nodes[i].next_group);
+ }
+
+ do_test();
+#endif
+ DBUG_VOID_RETURN;
+}
+
+void
+Ndb_cluster_connection_impl::do_test()
+{
+ Ndb_cluster_connection_node_iter iter;
+ int n= no_db_nodes()+5;
+ Uint32 *nodes= new Uint32[n+1];
+
+ for (int g= 0; g < n; g++)
+ {
+ for (int h= 0; h < n; h++)
+ {
+ Uint32 id;
+ Ndb_cluster_connection_node_iter iter2;
+ {
+ for (int j= 0; j < g; j++)
+ {
+ nodes[j]= get_next_node(iter2);
+ }
+ }
+
+ for (int i= 0; i < n; i++)
+ {
+ init_get_next_node(iter);
+ fprintf(stderr, "%d dead:(", g);
+ id= 0;
+ while (id == 0)
+ {
+ if ((id= get_next_node(iter)) == 0)
+ break;
+ for (int j= 0; j < g; j++)
+ {
+ if (nodes[j] == id)
+ {
+ fprintf(stderr, " %d", id);
+ id= 0;
+ break;
+ }
+ }
+ }
+ fprintf(stderr, ")");
+ if (id == 0)
+ {
+ break;
+ }
+ fprintf(stderr, " %d\n", id);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ delete [] nodes;
+}
+
+int Ndb_cluster_connection::connect(int no_retries, int retry_delay_in_seconds,
+ int verbose)
+{
+ struct ndb_mgm_reply mgm_reply;
+
+ DBUG_ENTER("Ndb_cluster_connection::connect");
+ const char* error = 0;
+ do {
+ if (m_impl.m_config_retriever == 0)
+ DBUG_RETURN(-1);
+ if (m_impl.m_config_retriever->do_connect(no_retries,
+ retry_delay_in_seconds,
+ verbose))
+ DBUG_RETURN(1); // mgmt server not up yet
+
+ Uint32 nodeId = m_impl.m_config_retriever->allocNodeId(4/*retries*/,
+ 3/*delay*/);
+ if(nodeId == 0)
+ break;
+ ndb_mgm_configuration * props = m_impl.m_config_retriever->getConfig();
+ if(props == 0)
+ break;
+
+ m_impl.m_transporter_facade->start_instance(nodeId, props);
+ m_impl.init_nodes_vector(nodeId, *props);
+
+ for(unsigned i=0;
+ i<m_impl.m_transporter_facade->get_registry()->m_transporter_interface.size();
+ i++)
+ ndb_mgm_set_connection_int_parameter(m_impl.m_config_retriever->get_mgmHandle(),
+ nodeId,
+ m_impl.m_transporter_facade->get_registry()
+ ->m_transporter_interface[i]
+ .m_remote_nodeId,
+ CFG_CONNECTION_SERVER_PORT,
+ m_impl.m_transporter_facade->get_registry()
+ ->m_transporter_interface[i]
+ .m_s_service_port,
+ &mgm_reply);
+
+ ndb_mgm_destroy_configuration(props);
+ m_impl.m_transporter_facade->connected();
+ DBUG_RETURN(0);
+ } while(0);
+
+ ndbout << "Configuration error: ";
+ const char* erString = m_impl.m_config_retriever->getErrorString();
+ if (erString == 0) {
+ erString = "No error specified!";
+ }
+ ndbout << erString << endl;
+ DBUG_RETURN(-1);
+}
+
+void Ndb_cluster_connection_impl::connect_thread()
+{
+ DBUG_ENTER("Ndb_cluster_connection_impl::connect_thread");
+ int r;
+ do {
+ NdbSleep_SecSleep(1);
+ if ((r = connect(0,0,0)) == 0)
+ break;
+ if (r == -1) {
+ printf("Ndb_cluster_connection::connect_thread error\n");
+ DBUG_ASSERT(false);
+ g_run_connect_thread= 0;
+ } else {
+ // Wait before making a new connect attempt
+ NdbSleep_SecSleep(1);
+ }
+ } while (g_run_connect_thread);
+ if (m_connect_callback)
+ (*m_connect_callback)();
+ DBUG_VOID_RETURN;
+}
+
+template class Vector<Ndb_cluster_connection_impl::Node>;
+
diff --git a/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp b/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp
new file mode 100644
index 00000000000..05652f3316a
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp
@@ -0,0 +1,82 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CLUSTER_CONNECTION_IMPL_HPP
+#define CLUSTER_CONNECTION_IMPL_HPP
+
+#include <ndb_cluster_connection.hpp>
+#include <Vector.hpp>
+
+class TransporterFacade;
+class ConfigRetriever;
+class NdbThread;
+class ndb_mgm_configuration;
+
+struct Ndb_cluster_connection_node_iter {
+ Ndb_cluster_connection_node_iter() : scan_state(~0),
+ init_pos(0),
+ cur_pos(0) {};
+ Uint8 scan_state;
+ Uint8 init_pos;
+ Uint8 cur_pos;
+};
+
+extern "C" {
+ void* run_ndb_cluster_connection_connect_thread(void*);
+}
+
+class Ndb_cluster_connection_impl : public Ndb_cluster_connection
+{
+ Ndb_cluster_connection_impl(const char *connectstring);
+ ~Ndb_cluster_connection_impl();
+
+ void do_test();
+
+ void init_get_next_node(Ndb_cluster_connection_node_iter &iter);
+ Uint32 get_next_node(Ndb_cluster_connection_node_iter &iter);
+
+private:
+ friend class Ndb;
+ friend class NdbImpl;
+ friend void* run_ndb_cluster_connection_connect_thread(void*);
+ friend class Ndb_cluster_connection;
+
+ struct Node
+ {
+ Node(Uint32 _g= 0, Uint32 _id= 0) : this_group(0),
+ next_group(0),
+ group(_g),
+ id(_id) {};
+ Uint32 this_group;
+ Uint32 next_group;
+ Uint32 group;
+ Uint32 id;
+ };
+
+ Vector<Node> m_all_nodes;
+ void init_nodes_vector(Uint32 nodeid, const ndb_mgm_configuration &config);
+ void connect_thread();
+
+ TransporterFacade *m_transporter_facade;
+ ConfigRetriever *m_config_retriever;
+ NdbThread *m_connect_thread;
+ int (*m_connect_callback)(void);
+
+ int m_optimized_node_selection;
+};
+
+#endif
diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c
new file mode 100644
index 00000000000..b033d81fa33
--- /dev/null
+++ b/storage/ndb/src/ndbapi/ndberror.c
@@ -0,0 +1,692 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+#include <ndberror.h>
+#include <m_string.h>
+
+typedef struct ErrorBundle {
+ int code;
+ ndberror_classification classification;
+ const char * message;
+} ErrorBundle;
+
+/**
+ * Shorter names in table below
+ */
+
+#define ST_S ndberror_st_success
+#define ST_P ndberror_st_permanent
+#define ST_T ndberror_st_temporary
+#define ST_U ndberror_st_unknown
+
+#define NE ndberror_cl_none
+#define AE ndberror_cl_application
+#define CE ndberror_cl_configuration
+#define ND ndberror_cl_no_data_found
+#define CV ndberror_cl_constraint_violation
+#define SE ndberror_cl_schema_error
+#define UD ndberror_cl_user_defined
+
+#define IS ndberror_cl_insufficient_space
+#define TR ndberror_cl_temporary_resource
+#define NR ndberror_cl_node_recovery
+#define OL ndberror_cl_overload
+#define TO ndberror_cl_timeout_expired
+#define NS ndberror_cl_node_shutdown
+
+#define UR ndberror_cl_unknown_result
+
+#define IE ndberror_cl_internal_error
+#define NI ndberror_cl_function_not_implemented
+#define UE ndberror_cl_unknown_error_code
+
+#define OE ndberror_cl_schema_object_already_exists
+
+static const char REDO_BUFFER_MSG[]=
+"REDO log buffers overloaded, consult online manual (increase RedoBuffer, and|or decrease TimeBetweenLocalCheckpoints, and|or increase NoOfFragmentLogFiles)";
+
+static const char* empty_string = "";
+
+/*
+ * Error code ranges are reserved for respective block
+ *
+ * 200 - TC
+ * 300 - DIH
+ * 400 - LQH
+ * 600 - ACC
+ * 700 - DICT
+ * 800 - TUP
+ * 900 - TUX
+ * 1200 - LQH
+ * 1300 - BACKUP
+ * 4000 - API
+ * 4100 - ""
+ * 4200 - ""
+ * 4300 - ""
+ * 4400 - ""
+ * 4500 - ""
+ * 4600 - ""
+ * 4700 - "" Event
+ * 5000 - Management server
+ */
+
+static
+const
+ErrorBundle ErrorCodes[] = {
+ /**
+ * No error
+ */
+ { 0, NE, "No error" },
+
+ /**
+ * NoDataFound
+ */
+ { 626, ND, "Tuple did not exist" },
+
+ /**
+ * ConstraintViolation
+ */
+ { 630, CV, "Tuple already existed when attempting to insert" },
+ { 840, CV, "Trying to set a NOT NULL attribute to NULL" },
+ { 893, CV, "Constraint violation e.g. duplicate value in unique index" },
+
+ /**
+ * Node recovery errors
+ */
+ { 286, NR, "Node failure caused abort of transaction" },
+ { 250, NR, "Node where lock was held crashed, restart scan transaction" },
+ { 499, NR, "Scan take over error, restart scan transaction" },
+ { 1204, NR, "Temporary failure, distribution changed" },
+ { 4002, NR, "Send to NDB failed" },
+ { 4010, NR, "Node failure caused abort of transaction" },
+ { 4025, NR, "Node failure caused abort of transaction" },
+ { 4027, NR, "Node failure caused abort of transaction" },
+ { 4028, NR, "Node failure caused abort of transaction" },
+ { 4029, NR, "Node failure caused abort of transaction" },
+ { 4031, NR, "Node failure caused abort of transaction" },
+ { 4033, NR, "Send to NDB failed" },
+ { 4115, NR,
+ "Transaction was committed but all read information was not "
+ "received due to node crash" },
+ { 4119, NR, "Simple/dirty read failed due to node failure" },
+
+ /**
+ * Node shutdown
+ */
+ { 280, NS, "Transaction aborted due to node shutdown" },
+ /* This scan trans had an active fragment scan in a LQH which have crashed */
+ { 270, NS, "Transaction aborted due to node shutdown" },
+ { 1223, NS, "Read operation aborted due to node shutdown" },
+ { 4023, NS, "Transaction aborted due to node shutdown" },
+ { 4030, NS, "Transaction aborted due to node shutdown" },
+ { 4034, NS, "Transaction aborted due to node shutdown" },
+
+
+
+ /**
+ * Unknown result
+ */
+ { 4008, UR, "Receive from NDB failed" },
+ { 4009, UR, "Cluster Failure" },
+ { 4012, UR,
+ "Time-out, most likely caused by simple read or cluster failure" },
+ { 4024, UR,
+ "Time-out, most likely caused by simple read or cluster failure" },
+
+ /**
+ * TemporaryResourceError
+ */
+ { 217, TR, "217" },
+ { 218, TR, "218" },
+ { 219, TR, "219" },
+ { 233, TR,
+ "Out of operation records in transaction coordinator (increase MaxNoOfConcurrentOperations)" },
+ { 275, TR, "275" },
+ { 279, TR, "Out of transaction markers in transaction coordinator" },
+ { 414, TR, "414" },
+ { 418, TR, "Out of transaction buffers in LQH" },
+ { 419, TR, "419" },
+ { 245, TR, "Too many active scans" },
+ { 488, TR, "Too many active scans" },
+ { 490, TR, "Too many active scans" },
+ { 805, TR, "Out of attrinfo records in tuple manager" },
+ { 830, TR, "Out of add fragment operation records" },
+ { 873, TR, "Out of attrinfo records for scan in tuple manager" },
+ { 1217, TR, "Out of operation records in local data manager (increase MaxNoOfLocalOperations)" },
+ { 1220, TR, REDO_BUFFER_MSG },
+ { 1222, TR, "Out of transaction markers in LQH" },
+ { 4021, TR, "Out of Send Buffer space in NDB API" },
+ { 4022, TR, "Out of Send Buffer space in NDB API" },
+ { 4032, TR, "Out of Send Buffer space in NDB API" },
+
+ /**
+ * InsufficientSpace
+ */
+ { 623, IS, "623" },
+ { 624, IS, "624" },
+ { 625, IS, "Out of memory in Ndb Kernel, index part (increase IndexMemory)" },
+ { 640, IS, "Too many hash indexes (should not happen)" },
+ { 826, IS, "Too many tables and attributes (increase MaxNoOfAttributes or MaxNoOfTables)" },
+ { 827, IS, "Out of memory in Ndb Kernel, data part (increase DataMemory)" },
+ { 902, IS, "Out of memory in Ndb Kernel, data part (increase DataMemory)" },
+ { 903, IS, "Too many ordered indexes (increase MaxNoOfOrderedIndexes)" },
+ { 904, IS, "Out of fragment records (increase MaxNoOfOrderedIndexes)" },
+ { 905, IS, "Out of attribute records (increase MaxNoOfAttributes)" },
+
+ /**
+ * TimeoutExpired
+ */
+ { 266, TO, "Time-out in NDB, probably caused by deadlock" },
+ { 274, TO, "Time-out in NDB, probably caused by deadlock" }, /* Scan trans timeout */
+ { 296, TO, "Time-out in NDB, probably caused by deadlock" }, /* Scan trans timeout */
+ { 297, TO, "Time-out in NDB, probably caused by deadlock" }, /* Scan trans timeout, temporary!! */
+ { 237, TO, "Transaction had timed out when trying to commit it" },
+
+ /**
+ * OverloadError
+ */
+ { 410, OL, REDO_BUFFER_MSG },
+ { 677, OL, "Index UNDO buffers overloaded (increase UndoIndexBuffer)" },
+ { 891, OL, "Data UNDO buffers overloaded (increase UndoDataBuffer)" },
+ { 1221, OL, REDO_BUFFER_MSG },
+ { 4006, OL, "Connect failure - out of connection objects (increase MaxNoOfConcurrentTransactions)" },
+
+
+
+ /**
+ * Internal errors
+ */
+ { 892, IE, "Inconsistent hash index. The index needs to be dropped and recreated" },
+ { 896, IE, "Tuple corrupted - wrong checksum or column data in invalid format" },
+ { 901, IE, "Inconsistent ordered index. The index needs to be dropped and recreated" },
+ { 202, IE, "202" },
+ { 203, IE, "203" },
+ { 207, IE, "207" },
+ { 208, IE, "208" },
+ { 209, IE, "Communication problem, signal error" },
+ { 220, IE, "220" },
+ { 230, IE, "230" },
+ { 232, IE, "232" },
+ { 238, IE, "238" },
+ { 271, IE, "Simple Read transaction without any attributes to read" },
+ { 272, IE, "Update operation without any attributes to update" },
+ { 276, IE, "276" },
+ { 277, IE, "277" },
+ { 278, IE, "278" },
+ { 287, IE, "Index corrupted" },
+ { 631, IE, "631" },
+ { 632, IE, "632" },
+ { 702, IE, "Request to non-master" },
+ { 706, IE, "Inconsistency during table creation" },
+ { 809, IE, "809" },
+ { 812, IE, "812" },
+ { 829, IE, "829" },
+ { 833, IE, "833" },
+ { 839, IE, "Illegal null attribute" },
+ { 871, IE, "871" },
+ { 882, IE, "882" },
+ { 883, IE, "883" },
+ { 887, IE, "887" },
+ { 888, IE, "888" },
+ { 890, IE, "890" },
+ { 4000, IE, "MEMORY ALLOCATION ERROR" },
+ { 4001, IE, "Signal Definition Error" },
+ { 4005, IE, "Internal Error in NdbApi" },
+ { 4011, IE, "Internal Error in NdbApi" },
+ { 4107, IE, "Simple Transaction and Not Start" },
+ { 4108, IE, "Faulty operation type" },
+ { 4109, IE, "Faulty primary key attribute length" },
+ { 4110, IE, "Faulty length in ATTRINFO signal" },
+ { 4111, IE, "Status Error in NdbConnection" },
+ { 4113, IE, "Too many operations received" },
+ { 4320, IE, "Cannot use the same object twice to create table" },
+ { 4321, IE, "Trying to start two schema transactions" },
+ { 4344, IE, "Only DBDICT and TRIX can send requests to TRIX" },
+ { 4345, IE, "TRIX block is not available yet, probably due to node failure" },
+ { 4346, IE, "Internal error at index create/build" },
+ { 4347, IE, "Bad state at alter index" },
+ { 4348, IE, "Inconsistency detected at alter index" },
+ { 4349, IE, "Inconsistency detected at index usage" },
+ { 4350, IE, "Transaction already aborted" },
+
+ /**
+ * Application error
+ */
+ { 823, AE, "Too much attrinfo from application in tuple manager" },
+ { 831, AE, "Too many nullable/bitfields in table definition" },
+ { 876, AE, "876" },
+ { 877, AE, "877" },
+ { 878, AE, "878" },
+ { 879, AE, "879" },
+ { 880, AE, "Tried to read too much - too many getValue calls" },
+ { 884, AE, "Stack overflow in interpreter" },
+ { 885, AE, "Stack underflow in interpreter" },
+ { 886, AE, "More than 65535 instructions executed in interpreter" },
+ { 897, AE, "Update attempt of primary key via ndbcluster internal api (if this occurs via the MySQL server it is a bug, please report)" },
+ { 4256, AE, "Must call Ndb::init() before this function" },
+ { 4257, AE, "Tried to read too much - too many getValue calls" },
+
+ /**
+ * Scan application errors
+ */
+ { 242, AE, "Zero concurrency in scan"},
+ { 244, AE, "Too high concurrency in scan"},
+ { 269, AE, "No condition and attributes to read in scan"},
+ { 4600, AE, "Transaction is already started"},
+ { 4601, AE, "Transaction is not started"},
+ { 4602, AE, "You must call getNdbOperation before executeScan" },
+ { 4603, AE, "There can only be ONE operation in a scan transaction" },
+ { 4604, AE, "takeOverScanOp, opType must be UpdateRequest or DeleteRequest" },
+ { 4605, AE, "You may only call openScanRead or openScanExclusive once for each operation"},
+ { 4607, AE, "There may only be one operation in a scan transaction"},
+ { 4608, AE, "You can not takeOverScan unless you have used openScanExclusive"},
+ { 4609, AE, "You must call nextScanResult before trying to takeOverScan"},
+ { 4232, AE, "Parallelism can only be between 1 and 240" },
+ { 290, AE, "Scan not started or has been closed by kernel due to timeout" },
+
+ /**
+ * Event schema errors
+ */
+
+ { 4713, SE, "Column defined in event does not exist in table"},
+
+ /**
+ * Event application errors
+ */
+
+ { 4707, AE, "Too many event have been defined"},
+ { 4708, AE, "Event name is too long"},
+ { 4709, AE, "Can't accept more subscribers"},
+ { 746, OE, "Event name already exists"},
+ { 4710, AE, "Event not found"},
+ { 4711, AE, "Creation of event failed"},
+ { 4712, AE, "Stopped event operation does not exist. Already stopped?"},
+
+ /**
+ * Event internal errors
+ */
+
+ { 4731, IE, "Event not found"},
+
+ /**
+ * SchemaError
+ */
+ { 701, SE, "System busy with other schema operation" },
+ { 703, SE, "Invalid table format" },
+ { 704, SE, "Attribute name too long" },
+ { 705, SE, "Table name too long" },
+ { 707, SE, "No more table metadata records" },
+ { 708, SE, "No more attribute metadata records" },
+ { 709, SE, "No such table existed" },
+ { 721, OE, "Table or index with given name already exists" },
+ { 723, SE, "No such table existed" },
+ { 736, SE, "Unsupported array size" },
+ { 737, SE, "Attribute array size too big" },
+ { 738, SE, "Record too big" },
+ { 739, SE, "Unsupported primary key length" },
+ { 740, SE, "Nullable primary key not supported" },
+ { 741, SE, "Unsupported alter table" },
+ { 743, SE, "Unsupported character set in table or index" },
+ { 744, SE, "Character string is invalid for given character set" },
+ { 745, SE, "Distribution key not supported for char attribute (use binary attribute)" },
+ { 241, SE, "Invalid schema object version" },
+ { 283, SE, "Table is being dropped" },
+ { 284, SE, "Table not defined in transaction coordinator" },
+ { 285, SE, "Unknown table error in transaction coordinator" },
+ { 881, SE, "Unable to create table, out of data pages (increase DataMemory) " },
+ { 906, SE, "Unsupported attribute type in index" },
+ { 907, SE, "Unsupported character set in table or index" },
+ { 908, IS, "Invalid ordered index tree node size" },
+ { 1225, SE, "Table not defined in local query handler" },
+ { 1226, SE, "Table is being dropped" },
+ { 1228, SE, "Cannot use drop table for drop index" },
+ { 1229, SE, "Too long frm data supplied" },
+
+ /**
+ * FunctionNotImplemented
+ */
+ { 4003, NI, "Function not implemented yet" },
+
+ /**
+ * Backup error codes
+ */
+
+ { 1300, IE, "Undefined error" },
+ { 1301, IE, "Backup issued to not master (reissue command to master)" },
+ { 1302, IE, "Out of backup record" },
+ { 1303, IS, "Out of resources" },
+ { 1304, IE, "Sequence failure" },
+ { 1305, IE, "Backup definition not implemented" },
+ { 1306, AE, "Backup not supported in diskless mode (change Diskless)" },
+
+ { 1321, IE, "Backup aborted by application" },
+ { 1322, IE, "Backup already completed" },
+ { 1323, IE, "1323" },
+ { 1324, IE, "Backup log buffer full" },
+ { 1325, IE, "File or scan error" },
+ { 1326, IE, "Backup abortet due to node failure" },
+ { 1327, IE, "1327" },
+
+ { 1340, IE, "Backup undefined error" },
+ { 1342, AE, "Backup failed to allocate buffers (check configuration)" },
+ { 1343, AE, "Backup failed to setup fs buffers (check configuration)" },
+ { 1344, AE, "Backup failed to allocate tables (check configuration)" },
+ { 1345, AE, "Backup failed to insert file header (check configuration)" },
+ { 1346, AE, "Backup failed to insert table list (check configuration)" },
+ { 1347, AE, "Backup failed to allocate table memory (check configuration)" },
+ { 1348, AE, "Backup failed to allocate file record (check configuration)" },
+ { 1349, AE, "Backup failed to allocate attribute record (check configuration)" },
+
+ /**
+ * Still uncategorized
+ */
+ { 720, AE, "Attribute name reused in table definition" },
+ { 4004, AE, "Attribute name not found in the Table" },
+
+ { 4100, AE, "Status Error in NDB" },
+ { 4101, AE, "No connections to NDB available and connect failed" },
+ { 4102, AE, "Type in NdbTamper not correct" },
+ { 4103, AE, "No schema connections to NDB available and connect failed" },
+ { 4104, AE, "Ndb Init in wrong state, destroy Ndb object and create a new" },
+ { 4105, AE, "Too many Ndb objects" },
+ { 4106, AE, "All Not NULL attribute have not been defined" },
+ { 4114, AE, "Transaction is already completed" },
+ { 4116, AE, "Operation was not defined correctly, probably missing a key" },
+ { 4117, AE, "Could not start transporter, configuration error"},
+ { 4118, AE, "Parameter error in API call" },
+ { 4300, AE, "Tuple Key Type not correct" },
+ { 4301, AE, "Fragment Type not correct" },
+ { 4302, AE, "Minimum Load Factor not correct" },
+ { 4303, AE, "Maximum Load Factor not correct" },
+ { 4304, AE, "Maximum Load Factor smaller than Minimum" },
+ { 4305, AE, "K value must currently be set to 6" },
+ { 4306, AE, "Memory Type not correct" },
+ { 4307, AE, "Invalid table name" },
+ { 4308, AE, "Attribute Size not correct" },
+ { 4309, AE, "Fixed array too large, maximum 64000 bytes" },
+ { 4310, AE, "Attribute Type not correct" },
+ { 4311, AE, "Storage Mode not correct" },
+ { 4312, AE, "Null Attribute Type not correct" },
+ { 4313, AE, "Index only storage for non-key attribute" },
+ { 4314, AE, "Storage Type of attribute not correct" },
+ { 4315, AE, "No more key attributes allowed after defining variable length key attribute" },
+ { 4316, AE, "Key attributes are not allowed to be NULL attributes" },
+ { 4317, AE, "Too many primary keys defined in table" },
+ { 4318, AE, "Invalid attribute name" },
+ { 4319, AE, "createAttribute called at erroneus place" },
+ { 4322, AE, "Attempt to define distribution key when not prepared to" },
+ { 4323, AE, "Distribution Key set on table but not defined on first attribute" },
+ { 4324, AE, "Attempt to define distribution group when not prepared to" },
+ { 4325, AE, "Distribution Group set on table but not defined on first attribute" },
+ { 4326, AE, "Distribution Group with erroneus number of bits" },
+ { 4327, AE, "Distribution Group with 1 byte attribute is not allowed" },
+ { 4328, AE, "Disk memory attributes not yet supported" },
+ { 4329, AE, "Variable stored attributes not yet supported" },
+
+ { 4400, AE, "Status Error in NdbSchemaCon" },
+ { 4401, AE, "Only one schema operation per schema transaction" },
+ { 4402, AE, "No schema operation defined before calling execute" },
+
+ { 4501, AE, "Insert in hash table failed when getting table information from Ndb" },
+ { 4502, AE, "GetValue not allowed in Update operation" },
+ { 4503, AE, "GetValue not allowed in Insert operation" },
+ { 4504, AE, "SetValue not allowed in Read operation" },
+ { 4505, AE, "NULL value not allowed in primary key search" },
+ { 4506, AE, "Missing getValue/setValue when calling execute" },
+ { 4507, AE, "Missing operation request when calling execute" },
+
+ { 4200, AE, "Status Error when defining an operation" },
+ { 4201, AE, "Variable Arrays not yet supported" },
+ { 4202, AE, "Set value on tuple key attribute is not allowed" },
+ { 4203, AE, "Trying to set a NOT NULL attribute to NULL" },
+ { 4204, AE, "Set value and Read/Delete Tuple is incompatible" },
+ { 4205, AE, "No Key attribute used to define tuple" },
+ { 4206, AE, "Not allowed to equal key attribute twice" },
+ { 4207, AE, "Key size is limited to 4092 bytes" },
+ { 4208, AE, "Trying to read a non-stored attribute" },
+ { 4209, AE, "Length parameter in equal/setValue is incorrect" },
+ { 4210, AE, "Ndb sent more info than the length he specified" },
+ { 4211, AE, "Inconsistency in list of NdbRecAttr-objects" },
+ { 4212, AE, "Ndb reports NULL value on Not NULL attribute" },
+ { 4213, AE, "Not all data of an attribute has been received" },
+ { 4214, AE, "Not all attributes have been received" },
+ { 4215, AE, "More data received than reported in TCKEYCONF message" },
+ { 4216, AE, "More than 8052 bytes in setValue cannot be handled" },
+ { 4217, AE, "It is not allowed to increment any other than unsigned ints" },
+ { 4218, AE, "Currently not allowed to increment NULL-able attributes" },
+ { 4219, AE, "Maximum size of interpretative attributes are 64 bits" },
+ { 4220, AE, "Maximum size of interpretative attributes are 64 bits" },
+ { 4221, AE, "Trying to jump to a non-defined label" },
+ { 4222, AE, "Label was not found, internal error" },
+ { 4223, AE, "Not allowed to create jumps to yourself" },
+ { 4224, AE, "Not allowed to jump to a label in a different subroutine" },
+ { 4225, AE, "All primary keys defined, call setValue/getValue"},
+ { 4226, AE, "Bad number when defining a label" },
+ { 4227, AE, "Bad number when defining a subroutine" },
+ { 4228, AE, "Illegal interpreter function in scan definition" },
+ { 4229, AE, "Illegal register in interpreter function definition" },
+ { 4230, AE, "Illegal state when calling getValue, probably not a read" },
+ { 4231, AE, "Illegal state when calling interpreter routine" },
+ { 4233, AE, "Calling execute (synchronous) when already prepared asynchronous transaction exists" },
+ { 4234, AE, "Illegal to call setValue in this state" },
+ { 4235, AE, "No callback from execute" },
+ { 4236, AE, "Trigger name too long" },
+ { 4237, AE, "Too many triggers" },
+ { 4238, AE, "Trigger not found" },
+ { 4239, AE, "Trigger with given name already exists"},
+ { 4240, AE, "Unsupported trigger type"},
+ { 4241, AE, "Index name too long" },
+ { 4242, AE, "Too many indexes" },
+ { 4243, AE, "Index not found" },
+ { 4244, OE, "Index or table with given name already exists" },
+ { 4247, AE, "Illegal index/trigger create/drop/alter request" },
+ { 4248, AE, "Trigger/index name invalid" },
+ { 4249, AE, "Invalid table" },
+ { 4250, AE, "Invalid index type or index logging option" },
+ { 4251, AE, "Cannot create unique index, duplicate keys found" },
+ { 4252, AE, "Failed to allocate space for index" },
+ { 4253, AE, "Failed to create index table" },
+ { 4254, AE, "Table not an index table" },
+ { 4255, AE, "Hash index attributes must be specified in same order as table attributes" },
+ { 4258, AE, "Cannot create unique index, duplicate attributes found in definition" },
+ { 4259, AE, "Invalid set of range scan bounds" },
+ { 4260, UD, "NdbScanFilter: Operator is not defined in NdbScanFilter::Group"},
+ { 4261, UD, "NdbScanFilter: Column is NULL"},
+ { 4262, UD, "NdbScanFilter: Condition is out of bounds"},
+ { 4263, IE, "Invalid blob attributes or invalid blob parts table" },
+ { 4264, AE, "Invalid usage of blob attribute" },
+ { 4265, AE, "Method is not valid in current blob state" },
+ { 4266, AE, "Invalid blob seek position" },
+ { 4267, IE, "Corrupted blob value" },
+ { 4268, IE, "Error in blob head update forced rollback of transaction" },
+ { 4269, IE, "No connection to ndb management server" },
+ { 4270, IE, "Unknown blob error" },
+ { 4335, AE, "Only one autoincrement column allowed per table. Having a table without primary key uses an autoincremented hidden key, i.e. a table without a primary key can not have an autoincremented column" },
+ { 4271, AE, "Invalid index object, not retrieved via getIndex()" }
+};
+
+static
+const
+int NbErrorCodes = sizeof(ErrorCodes)/sizeof(ErrorBundle);
+
+typedef struct ErrorStatusMessage {
+ ndberror_status status;
+ const char * message;
+} ErrorStatusMessage;
+
+typedef struct ErrorStatusClassification {
+ ndberror_status status;
+ ndberror_classification classification;
+ const char * message;
+} ErrorStatusClassification;
+
+/**
+ * Mapping between classification and status
+ */
+static
+const
+ErrorStatusMessage StatusMessageMapping[] = {
+ { ST_S, "Success"},
+ { ST_P, "Permanent error"},
+ { ST_T, "Temporary error"},
+ { ST_U ,"Unknown result"}
+};
+
+static
+const
+int NbStatus = sizeof(StatusMessageMapping)/sizeof(ErrorStatusMessage);
+
+static
+const
+ErrorStatusClassification StatusClassificationMapping[] = {
+ { ST_S, NE, "No error"},
+ { ST_P, AE, "Application error"},
+ { ST_P, CE, "Configuration or application error"},
+ { ST_P, ND, "No data found"},
+ { ST_P, CV, "Constraint violation"},
+ { ST_P, SE, "Schema error"},
+ { ST_P, UD, "User defined error"},
+ { ST_P, IS, "Insufficient space"},
+
+ { ST_T, TR, "Temporary Resource error"},
+ { ST_T, NR, "Node Recovery error"},
+ { ST_T, OL, "Overload error"},
+ { ST_T, TO, "Timeout expired"},
+ { ST_T, NS, "Node shutdown"},
+
+ { ST_U , UR, "Unknown result error"},
+ { ST_U , UE, "Unknown error code"},
+
+ { ST_P, IE, "Internal error"},
+ { ST_P, NI, "Function not implemented"}
+};
+
+static
+const
+int NbClassification = sizeof(StatusClassificationMapping)/sizeof(ErrorStatusClassification);
+
+#ifdef NOT_USED
+/**
+ * Complete all fields of an NdbError given the error code
+ * and details
+ */
+static
+void
+set(ndberror_struct * error, int code, const char * details, ...){
+ error->code = code;
+ {
+ va_list ap;
+ va_start(ap, details);
+ vsnprintf(error->details, sizeof(error->details), details, ap);
+ va_end(ap);
+ }
+}
+#endif
+
+void
+ndberror_update(ndberror_struct * error){
+
+ int found = 0;
+ int i;
+
+ for(i = 0; i<NbErrorCodes; i++){
+ if(ErrorCodes[i].code == error->code){
+ error->classification = ErrorCodes[i].classification;
+ error->message = ErrorCodes[i].message;
+ found = 1;
+ break;
+ }
+ }
+
+ if(!found){
+ error->classification = UE;
+ error->message = "Unknown error code";
+ }
+
+ found = 0;
+ for(i = 0; i<NbClassification; i++){
+ if(StatusClassificationMapping[i].classification == error->classification){
+ error->status = StatusClassificationMapping[i].status;
+ found = 1;
+ break;
+ }
+ }
+ if(!found){
+ error->status = ST_U;
+ }
+
+ error->details = 0;
+}
+
+int
+checkErrorCodes(){
+ int i, j;
+ for(i = 0; i<NbErrorCodes; i++)
+ for(j = i+1; j<NbErrorCodes; j++)
+ if(ErrorCodes[i].code == ErrorCodes[j].code){
+ printf("ErrorCode %d is defined multiple times!!\n",
+ ErrorCodes[i].code);
+ assert(0);
+ }
+
+ return 1;
+}
+
+/*static const int a = checkErrorCodes();*/
+
+#if CHECK_ERRORCODES
+int main(void){
+ checkErrorCodes();
+ return 0;
+}
+#endif
+
+const char *ndberror_status_message(ndberror_status status)
+{
+ int i;
+ for (i= 0; i < NbStatus; i++)
+ if (StatusMessageMapping[i].status == status)
+ return StatusMessageMapping[i].message;
+ return empty_string;
+}
+
+const char *ndberror_classification_message(ndberror_classification classification)
+{
+ int i;
+ for (i= 0; i < NbClassification; i++)
+ if (StatusClassificationMapping[i].classification == classification)
+ return StatusClassificationMapping[i].message;
+ return empty_string;
+}
+
+int ndb_error_string(int err_no, char *str, unsigned int size)
+{
+ ndberror_struct error;
+ unsigned int len;
+
+ error.code = err_no;
+ ndberror_update(&error);
+
+ len =
+ my_snprintf(str, size-1, "%s: %s: %s", error.message,
+ ndberror_status_message(error.status),
+ ndberror_classification_message(error.classification));
+ str[size-1]= '\0';
+
+ return len;
+}
diff --git a/storage/ndb/src/ndbapi/signal-sender/Makefile b/storage/ndb/src/ndbapi/signal-sender/Makefile
new file mode 100644
index 00000000000..56e6ce1eac0
--- /dev/null
+++ b/storage/ndb/src/ndbapi/signal-sender/Makefile
@@ -0,0 +1,19 @@
+include .defs.mk
+
+TYPE := ndbapi
+
+NONPIC_ARCHIVE := Y
+ARCHIVE_TARGET := signal-sender
+
+BIN_TARGET_LIBS := # -lkalle
+BIN_TARGET_ARCHIVES := portlib # $(NDB_TOP)/lib/libkalle.a
+
+# Source files of non-templated classes (.cpp files)
+SOURCES = SignalSender.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/src/ndbapi)
+
+include $(NDB_TOP)/Epilogue.mk
+
+###
+# Backward compatible
diff --git a/storage/ndb/src/ndbapi/signal-sender/SignalSender.cpp b/storage/ndb/src/ndbapi/signal-sender/SignalSender.cpp
new file mode 100644
index 00000000000..680d0c23b4a
--- /dev/null
+++ b/storage/ndb/src/ndbapi/signal-sender/SignalSender.cpp
@@ -0,0 +1,237 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "SignalSender.hpp"
+#include "ConfigRetriever.hpp"
+#include <NdbSleep.h>
+#include <SignalLoggerManager.hpp>
+
+SimpleSignal::SimpleSignal(bool dealloc){
+ memset(this, 0, sizeof(* this));
+ deallocSections = dealloc;
+}
+
+SimpleSignal::~SimpleSignal(){
+ if(!deallocSections)
+ return;
+ if(ptr[0].p != 0) delete []ptr[0].p;
+ if(ptr[1].p != 0) delete []ptr[1].p;
+ if(ptr[2].p != 0) delete []ptr[2].p;
+}
+
+void
+SimpleSignal::set(class SignalSender& ss,
+ Uint8 trace, Uint16 recBlock, Uint16 gsn, Uint32 len){
+
+ header.theTrace = trace;
+ header.theReceiversBlockNumber = recBlock;
+ header.theVerId_signalNumber = gsn;
+ header.theLength = len;
+ header.theSendersBlockRef = refToBlock(ss.getOwnRef());
+}
+
+void
+SimpleSignal::print(FILE * out){
+ fprintf(out, "---- Signal ----------------\n");
+ SignalLoggerManager::printSignalHeader(out, header, 0, 0, false);
+ SignalLoggerManager::printSignalData(out, header, theData);
+ for(Uint32 i = 0; i<header.m_noOfSections; i++){
+ Uint32 len = ptr[i].sz;
+ fprintf(out, " --- Section %d size=%d ---\n", i, len);
+ Uint32 * signalData = ptr[i].p;
+ while(len >= 7){
+ fprintf(out,
+ " H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x H\'%.8x\n",
+ signalData[0], signalData[1], signalData[2], signalData[3],
+ signalData[4], signalData[5], signalData[6]);
+ len -= 7;
+ signalData += 7;
+ }
+ if(len > 0){
+ fprintf(out, " H\'%.8x", signalData[0]);
+ for(Uint32 i = 1; i<len; i++)
+ fprintf(out, " H\'%.8x", signalData[i]);
+ fprintf(out, "\n");
+ }
+ }
+}
+
+SignalSender::SignalSender(const char * connectString){
+ m_cond = NdbCondition_Create();
+ theFacade = TransporterFacade::start_instance(connectString);
+ m_blockNo = theFacade->open(this, execSignal, execNodeStatus);
+ assert(m_blockNo > 0);
+}
+
+SignalSender::~SignalSender(){
+ theFacade->close(m_blockNo);
+ theFacade->stop_instance();
+ NdbCondition_Destroy(m_cond);
+}
+
+Uint32
+SignalSender::getOwnRef() const {
+ return numberToRef(m_blockNo, theFacade->ownId());
+}
+
+bool
+SignalSender::connectOne(Uint32 timeOutMillis){
+ NDB_TICKS start = NdbTick_CurrentMillisecond();
+ NDB_TICKS now = start;
+ while(theFacade->theClusterMgr->getNoOfConnectedNodes() == 0 &&
+ (timeOutMillis == 0 || (now - start) < timeOutMillis)){
+ NdbSleep_MilliSleep(100);
+ }
+ return theFacade->theClusterMgr->getNoOfConnectedNodes() > 0;
+}
+
+bool
+SignalSender::connectAll(Uint32 timeOutMillis){
+ NDB_TICKS start = NdbTick_CurrentMillisecond();
+ NDB_TICKS now = start;
+ while(theFacade->theClusterMgr->getNoOfConnectedNodes() < 1 &&
+ (timeOutMillis == 0 || (now - start) < timeOutMillis)){
+ NdbSleep_MilliSleep(100);
+ }
+ return theFacade->theClusterMgr->getNoOfConnectedNodes() >= 1;
+}
+
+
+Uint32
+SignalSender::getAliveNode(){
+ return theFacade->get_an_alive_node();
+}
+
+const ClusterMgr::Node &
+SignalSender::getNodeInfo(Uint16 nodeId) const {
+ return theFacade->theClusterMgr->getNodeInfo(nodeId);
+}
+
+Uint32
+SignalSender::getNoOfConnectedNodes() const {
+ return theFacade->theClusterMgr->getNoOfConnectedNodes();
+}
+
+SendStatus
+SignalSender::sendSignal(Uint16 nodeId, const SimpleSignal * s){
+ return theFacade->theTransporterRegistry->prepareSend(&s->header,
+ 1, // JBB
+ &s->theData[0],
+ nodeId,
+ &s->ptr[0]);
+}
+
+template<class T>
+SimpleSignal *
+SignalSender::waitFor(Uint32 timeOutMillis, T & t){
+
+ Guard g(theFacade->theMutexPtr);
+
+ SimpleSignal * s = t.check(m_jobBuffer);
+ if(s != 0){
+ return s;
+ }
+
+ NDB_TICKS now = NdbTick_CurrentMillisecond();
+ NDB_TICKS stop = now + timeOutMillis;
+ Uint32 wait = (timeOutMillis == 0 ? 10 : timeOutMillis);
+ do {
+ NdbCondition_WaitTimeout(m_cond,
+ theFacade->theMutexPtr,
+ wait);
+
+
+ SimpleSignal * s = t.check(m_jobBuffer);
+ if(s != 0){
+ return s;
+ }
+
+ now = NdbTick_CurrentMillisecond();
+ wait = (timeOutMillis == 0 ? 10 : stop - now);
+ } while(stop > now || timeOutMillis == 0);
+
+ return 0;
+}
+
+class WaitForAny {
+public:
+ SimpleSignal * check(Vector<SimpleSignal*> & m_jobBuffer){
+ if(m_jobBuffer.size() > 0){
+ SimpleSignal * s = m_jobBuffer[0];
+ m_jobBuffer.erase(0);
+ return s;
+ }
+ return 0;
+ }
+};
+
+SimpleSignal *
+SignalSender::waitFor(Uint32 timeOutMillis){
+
+ WaitForAny w;
+ return waitFor(timeOutMillis, w);
+}
+
+class WaitForNode {
+public:
+ Uint32 m_nodeId;
+ SimpleSignal * check(Vector<SimpleSignal*> & m_jobBuffer){
+ Uint32 len = m_jobBuffer.size();
+ for(Uint32 i = 0; i<len; i++){
+ if(refToNode(m_jobBuffer[i]->header.theSendersBlockRef) == m_nodeId){
+ SimpleSignal * s = m_jobBuffer[i];
+ m_jobBuffer.erase(i);
+ return s;
+ }
+ }
+ return 0;
+ }
+};
+
+SimpleSignal *
+SignalSender::waitFor(Uint16 nodeId, Uint32 timeOutMillis){
+
+ WaitForNode w;
+ w.m_nodeId = nodeId;
+ return waitFor(timeOutMillis, w);
+}
+
+#include <NdbApiSignal.hpp>
+
+void
+SignalSender::execSignal(void* signalSender,
+ NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]){
+ SimpleSignal * s = new SimpleSignal(true);
+ s->header = * signal;
+ memcpy(&s->theData[0], signal->getDataPtr(), 4 * s->header.theLength);
+ for(Uint32 i = 0; i<s->header.m_noOfSections; i++){
+ s->ptr[i].p = new Uint32[ptr[i].sz];
+ s->ptr[i].sz = ptr[i].sz;
+ memcpy(s->ptr[i].p, ptr[i].p, 4 * ptr[i].sz);
+ }
+ SignalSender * ss = (SignalSender*)signalSender;
+ ss->m_jobBuffer.push_back(s);
+ NdbCondition_Signal(ss->m_cond);
+}
+
+void
+SignalSender::execNodeStatus(void* signalSender,
+ Uint16 NodeId,
+ bool alive,
+ bool nfCompleted){
+}
+
diff --git a/storage/ndb/src/ndbapi/signal-sender/SignalSender.hpp b/storage/ndb/src/ndbapi/signal-sender/SignalSender.hpp
new file mode 100644
index 00000000000..e4e6c1931d2
--- /dev/null
+++ b/storage/ndb/src/ndbapi/signal-sender/SignalSender.hpp
@@ -0,0 +1,82 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 SIGNAL_SENDER_HPP
+#define SIGNAL_SENDER_HPP
+
+#include <ndb_global.h>
+#include <TransporterDefinitions.hpp>
+#include <TransporterFacade.hpp>
+#include <ClusterMgr.hpp>
+#include <Vector.hpp>
+
+struct SimpleSignal {
+public:
+ SimpleSignal(bool dealloc = false);
+ ~SimpleSignal();
+
+ void set(class SignalSender&,
+ Uint8 trace, Uint16 recBlock, Uint16 gsn, Uint32 len);
+
+ struct SignalHeader header;
+ Uint32 theData[25];
+ LinearSectionPtr ptr[3];
+
+ void print(FILE * out = stdout);
+private:
+ bool deallocSections;
+};
+
+class SignalSender {
+public:
+ SignalSender(const char * connectString = 0);
+ virtual ~SignalSender();
+
+ bool connectOne(Uint32 timeOutMillis = 0);
+ bool connectAll(Uint32 timeOutMillis = 0);
+ bool connect(Uint32 timeOutMillis = 0) { return connectAll(timeOutMillis);}
+
+ Uint32 getOwnRef() const;
+
+ Uint32 getAliveNode();
+ Uint32 getNoOfConnectedNodes() const;
+ const ClusterMgr::Node & getNodeInfo(Uint16 nodeId) const;
+
+ SendStatus sendSignal(Uint16 nodeId, const SimpleSignal *);
+
+ SimpleSignal * waitFor(Uint32 timeOutMillis = 0);
+ SimpleSignal * waitFor(Uint16 nodeId, Uint32 timeOutMillis = 0);
+ SimpleSignal * waitFor(Uint16 nodeId, Uint16 gsn, Uint32 timeOutMillis = 0);
+
+private:
+ int m_blockNo;
+ TransporterFacade * theFacade;
+
+ static void execSignal(void* signalSender,
+ NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]);
+
+ static void execNodeStatus(void* signalSender, NodeId,
+ bool alive, bool nfCompleted);
+
+ struct NdbCondition * m_cond;
+ Vector<SimpleSignal *> m_jobBuffer;
+
+ template<class T>
+ SimpleSignal * waitFor(Uint32 timeOutMillis, T & t);
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/client/Makefile b/storage/ndb/src/old_files/client/Makefile
new file mode 100644
index 00000000000..1751a98bdfe
--- /dev/null
+++ b/storage/ndb/src/old_files/client/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+DIRS =
+
+ifneq ($(NDB_ODBC),N)
+DIRS += odbc
+endif
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/client/odbc/Extra.mk b/storage/ndb/src/old_files/client/odbc/Extra.mk
new file mode 100644
index 00000000000..762fb0bedd0
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/Extra.mk
@@ -0,0 +1,59 @@
+# before Epilogue.mk
+
+CCFLAGS_LOC += -I..
+
+CCFLAGS_LOC += \
+ -I$(call fixpath,$(NDB_TOP)/include/ndbapi) \
+ -I$(call fixpath,$(NDB_TOP)/include/util) \
+ -I$(call fixpath,$(NDB_TOP)/include/portlib)
+
+ifeq ($(NDB_OS),SOLARIS)
+
+CCFLAGS_LOC += -I/usr/local/include
+
+ifeq ($(NDB_COMPILER),GCC)
+LIBS_LOC += -Wl,-z,text
+CCFLAGS_WARNINGS += -Wno-unused -Wformat
+CCFLAGS_TOP += -D__STL_PTHREADS
+endif
+
+ifeq ($(NDB_COMPILER),FORTE6)
+LIBS_LOC += -z text
+LIBS_SPEC += /usr/lib/libCrun.so.1
+endif
+
+LIB_TARGET_LIBS += -lthread -lrt
+
+endif
+ifneq ($(filter $(NDB_OS), LINUX MACOSX IBMAIX TRU64X),)
+
+LIBS_LOC += -Wl,-z,text
+CCFLAGS_WARNINGS += -Wno-unused -Wformat
+GCC_VER := $(shell $(CC) --version)
+
+ifeq ($(GCC_VER),2.96)
+CCFLAGS_TOP += -D__STL_PTHREADS
+CCFLAGS_TOP += -fmessage-length=0
+endif
+
+CCFLAGS_TOP += -fno-rtti
+
+LIB_TARGET_LIBS += -lpthread
+
+endif
+
+ifeq ($(NDB_OS),WIN32)
+ifeq (RELEASE, $(NDB_VERSION))
+CCFLAGS_WIN += /MT /GR /GS /Zi -D_WINDOWS -D_USRDLL -DNDBODBC_EXPORTS -DNO_COMMAND_HANDLER -D_MBCS -D_WINDLL -U_LIB
+else
+ifeq (RELEASE_TRACE, $(NDB_VERSION))
+CCFLAGS_WIN += /MT /GR /GS /Zi -D_WINDOWS -D_USRDLL -DNDBODBC_EXPORTS -DNO_COMMAND_HANDLER -D_MBCS -D_WINDLL -U_LIB
+else
+CCFLAGS_WIN += /MT /GR /GS /Zi -D_WINDOWS -D_USRDLL -DNDBODBC_EXPORTS -DNO_COMMAND_HANDLER -D_MBCS -D_WINDLL -U_LIB
+endif
+endif
+endif
+
+CCFLAGS_TOP += -DYYDEBUG=0 -fexceptions
+
+CCFLAGS_TOP += -DHAVE_LONG_LONG
diff --git a/storage/ndb/src/old_files/client/odbc/Makefile b/storage/ndb/src/old_files/client/odbc/Makefile
new file mode 100644
index 00000000000..2da683e7d86
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/Makefile
@@ -0,0 +1,75 @@
+include .defs.mk
+
+TYPE = *
+
+A_LIB = N
+PIC_LIB = Y
+SO_LIB = Y
+
+LIB_TARGET = NDB_ODBC
+
+LIB_TARGET_ARCHIVES = $(LIB_DIRS:%=odbc%) NDB_API
+
+# Overide Defs.mk
+LDFLAGS_LAST = -lstdc++ -lm
+
+XXX = \
+ ndbapi \
+ mgmsrvcommon \
+ transporter \
+ general \
+ signaldataprint \
+ portlib \
+ logger \
+ trace
+
+ifeq ($(NDB_OS),WIN32)
+
+LIB_DIRS = \
+ handles \
+ dictionary \
+ codegen \
+ executor \
+ common
+
+SOURCES += NdbOdbc.cpp
+CFLAGS_NdbOdbc.cpp += -I. -I$(call fixpath,driver)
+
+PIC_ARCHIVE := Y
+NONPIC_ARCHIVE := N
+ARCHIVE_TARGET := ndb_odbcwin32
+LIB_TARGET_ARCHIVES += ndb_odbcwin32
+
+ifeq (RELEASE, $(NDB_VERSION))
+WIN_LIBS += /VERSION:2.0x /NODEFAULTLIB:"odbc32" /INCREMENTAL:NO /DLL /SUBSYSTEM:WINDOWS /MACHINE:IX86 /OPT:REF /OPT:ICF /DEF:NdbOdbc.def odbc32.lib odbccp32.lib user32.lib
+else
+ifeq (RELEASE_TRACE, $(NDB_VERSION))
+WIN_LIBS += /VERSION:2.0x /NODEFAULTLIB:"odbc32" /INCREMENTAL:NO /DLL /SUBSYSTEM:WINDOWS /MACHINE:IX86 /OPT:REF /OPT:ICF /DEF:NdbOdbc.def odbc32.lib odbccp32.lib user32.lib
+else
+WIN_LIBS += /VERSION:2.0x /NODEFAULTLIB:"odbc32" /INCREMENTAL /DLL /DEBUG /SUBSYSTEM:WINDOWS /MACHINE:IX86 /DEF:NdbOdbc.def odbc32.lib odbccp32.lib user32.lib
+endif
+endif
+
+else
+
+LIB_DIRS = \
+ driver \
+ handles \
+ dictionary \
+ codegen \
+ executor \
+ common
+
+endif
+
+include Extra.mk
+include $(NDB_TOP)/Epilogue.mk
+
+# yo
+
+test:
+ $(MAKE) -j4
+ $(MAKE) -C $(NDB_TOP)/tools/ndbsql
+ $(MAKE) -C $(NDB_TOP)/test/odbc/driver tidy
+ $(MAKE) -C $(NDB_TOP)/test/odbc/driver
+ $(MAKE) -C $(NDB_TOP)/test/ndbapi/testOIBasic
diff --git a/storage/ndb/src/old_files/client/odbc/NdbOdbc.cpp b/storage/ndb/src/old_files/client/odbc/NdbOdbc.cpp
new file mode 100755
index 00000000000..67c6b5e0004
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/NdbOdbc.cpp
@@ -0,0 +1,78 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <NdbUnistd.h>
+#include <odbcinst.h>
+
+#include "driver.cpp"
+
+
+BOOL APIENTRY DllMain( HANDLE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+
+BOOL INSTAPI ConfigDSN(
+ HWND hwndParent,
+ WORD fRequest,
+ LPCSTR lpszDriver,
+ LPCSTR lpszAttributes)
+{
+ const char* szDSN = "NDB";
+
+ switch(fRequest)
+ {
+ case ODBC_ADD_DSN:
+ SQLWriteDSNToIni(szDSN, lpszDriver);
+ break;
+
+ case ODBC_CONFIG_DSN:
+ break;
+
+ case ODBC_REMOVE_DSN:
+ SQLRemoveDSNFromIni(szDSN);
+ break;
+ }
+
+ return TRUE;
+}
+
+
+int FAR PASCAL
+DriverConnectProc(HWND hdlg, WORD wMsg, WPARAM wParam, LPARAM lParam)
+{
+ return FALSE;
+}
+
+void __declspec( dllexport) FAR PASCAL LoadByOrdinal(void);
+/* Entry point to cause DM to load using ordinals */
+void __declspec( dllexport) FAR PASCAL LoadByOrdinal(void)
+{
+}
+
diff --git a/storage/ndb/src/old_files/client/odbc/NdbOdbc.def b/storage/ndb/src/old_files/client/odbc/NdbOdbc.def
new file mode 100755
index 00000000000..85619b91915
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/NdbOdbc.def
@@ -0,0 +1,85 @@
+LIBRARY NdbOdbc.DLL
+VERSION 03.51.00
+EXPORTS
+SQLAllocConnect
+SQLAllocEnv
+SQLAllocHandle
+SQLAllocHandleStd
+SQLAllocStmt
+SQLBindCol
+SQLBindParam
+SQLBindParameter
+SQLBrowseConnect
+SQLBulkOperations
+SQLCancel
+SQLCloseCursor
+SQLColAttribute
+SQLColAttributes
+SQLColumnPrivileges
+SQLColumns
+SQLConnect
+SQLCopyDesc
+SQLDataSources
+SQLDescribeCol
+SQLDescribeParam
+SQLDisconnect
+SQLDriverConnect
+SQLDrivers
+SQLEndTran
+SQLError
+SQLExecDirect
+SQLExecute
+SQLExtendedFetch
+SQLFetch
+SQLFetchScroll
+SQLForeignKeys
+SQLFreeConnect
+SQLFreeEnv
+SQLFreeHandle
+SQLFreeStmt
+SQLGetConnectAttr
+SQLGetConnectOption
+SQLGetCursorName
+SQLGetData
+SQLGetDescField
+SQLGetDescRec
+SQLGetDiagField
+SQLGetDiagRec
+SQLGetEnvAttr
+SQLGetFunctions
+SQLGetInfo
+SQLGetStmtAttr
+SQLGetStmtOption
+SQLGetTypeInfo
+SQLMoreResults
+SQLNativeSql
+SQLNumParams
+SQLNumResultCols
+SQLParamData
+SQLParamOptions
+SQLPrepare
+SQLPrimaryKeys
+SQLProcedureColumns
+SQLProcedures
+SQLPutData
+SQLRowCount
+SQLSetConnectAttr
+SQLSetConnectOption
+SQLSetCursorName
+SQLSetDescField
+SQLSetDescRec
+SQLSetEnvAttr
+SQLSetParam
+SQLSetPos
+SQLSetScrollOptions
+SQLSetStmtAttr
+SQLSetStmtOption
+SQLSpecialColumns
+SQLStatistics
+SQLTablePrivileges
+SQLTables
+SQLTransact
+DllMain
+DriverConnectProc
+ConfigDSN
+LoadByOrdinal
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/CodeGen.cpp b/storage/ndb/src/old_files/client/odbc/codegen/CodeGen.cpp
new file mode 100644
index 00000000000..6be78b62bd9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/CodeGen.cpp
@@ -0,0 +1,229 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <common/CodeTree.hpp>
+#include <executor/Executor.hpp>
+#include "CodeGen.hpp"
+#include "Code_root.hpp"
+
+#include <FlexLexer.h>
+#include "SimpleParser.hpp"
+
+void
+CodeGen::prepare(Ctx& ctx)
+{
+ parse(ctx);
+ if (! ctx.ok())
+ return;
+ analyze(ctx);
+ if (! ctx.ok())
+ return;
+ describe(ctx);
+}
+
+void
+CodeGen::execute(Ctx& ctx)
+{
+ DescArea& ipd = m_stmtArea.descArea(Desc_usage_IPD);
+ if (m_stmtArea.m_unbound) {
+ analyze(ctx);
+ if (! ctx.ok())
+ return;
+ describe(ctx);
+ if (! ctx.ok())
+ return;
+ if (m_stmtArea.m_unbound) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "%u input parameters have unbound SQL type", m_stmtArea.m_unbound);
+ return;
+ }
+ ipd.setBound(true);
+ }
+ if (! ipd.isBound()) {
+ ctx_log2(("IPD changed between executes - reanalyze"));
+ // jdbc can change parameter length at each execute
+ analyze(ctx);
+ if (! ctx.ok())
+ return;
+ describe(ctx);
+ if (! ctx.ok())
+ return;
+ freeExec(ctx);
+ codegen(ctx);
+ if (! ctx.ok())
+ return;
+ alloc(ctx);
+ if (! ctx.ok())
+ return;
+ ipd.setBound(true);
+ }
+ if (m_stmtArea.m_execTree == 0) {
+ codegen(ctx);
+ if (! ctx.ok())
+ return;
+ alloc(ctx);
+ if (! ctx.ok())
+ return;
+ }
+ Executor executor(m_stmtArea);
+ executor.execute(ctx);
+}
+
+void
+CodeGen::fetch(Ctx& ctx)
+{
+ // XXX parameter types are not checked any more
+ ctx_assert(! m_stmtArea.m_unbound);
+ Executor executor(m_stmtArea);
+ executor.fetch(ctx);
+}
+
+void
+CodeGen::parse(Ctx& ctx)
+{
+ Plan_root* planRoot = new Plan_root(m_stmtArea);
+ SimpleParser simpleParser(ctx, m_stmtArea, planRoot);
+ simpleParser.yyparse();
+ if (! ctx.ok())
+ return;
+ planRoot->m_paramList.resize(1 + simpleParser.paramNumber());
+ ctx_log2(("CodeGen: parse done - plan tree follows"));
+ if (ctx.logLevel() >= 2)
+ planRoot->print(ctx);
+ m_stmtArea.m_planTree = planRoot;
+}
+
+void
+CodeGen::analyze(Ctx& ctx)
+{
+ Plan_root* planRoot = static_cast<Plan_root*>(m_stmtArea.m_planTree);
+ ctx_assert(planRoot != 0);
+ Plan_base::Ctl ctl(0);
+ planRoot->analyze(ctx, ctl); // returns itself
+ if (! ctx.ok())
+ return;
+ ctx_log2(("CodeGen: analyze done - plan tree follows"));
+ if (ctx.logLevel() >= 2)
+ planRoot->print(ctx);
+}
+
+void
+CodeGen::describe(Ctx& ctx)
+{
+ Plan_root* planRoot = static_cast<Plan_root*>(m_stmtArea.m_planTree);
+ ctx_assert(planRoot != 0);
+ planRoot->describe(ctx);
+ ctx_log2(("CodeGen: describe done"));
+}
+
+void
+CodeGen::codegen(Ctx& ctx)
+{
+ Plan_root* planRoot = static_cast<Plan_root*>(m_stmtArea.m_planTree);
+ ctx_assert(planRoot != 0);
+ Plan_base::Ctl ctl(0);
+ Exec_root* execRoot = static_cast<Exec_root*>(planRoot->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return;
+ ctx_assert(execRoot != 0);
+ ctx_log2(("CodeGen: codegen done - code tree follows"));
+ if (ctx.logLevel() >= 2)
+ execRoot->print(ctx);
+ m_stmtArea.m_execTree = execRoot;
+}
+
+void
+CodeGen::alloc(Ctx& ctx)
+{
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ Exec_base::Ctl ctl(0);
+ execRoot->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_log2(("CodeGen: alloc done"));
+}
+
+void
+CodeGen::close(Ctx& ctx)
+{
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ if (execRoot != 0) {
+ execRoot->close(ctx);
+ ctx_log2(("CodeGen: close done"));
+ }
+}
+
+void
+CodeGen::free(Ctx& ctx)
+{
+ freePlan(ctx);
+ freeExec(ctx);
+}
+
+void
+CodeGen::freePlan(Ctx & ctx)
+{
+ if (m_stmtArea.m_planTree != 0) {
+ Plan_root* planRoot = static_cast<Plan_root*>(m_stmtArea.m_planTree);
+ ctx_assert(planRoot != 0);
+ unsigned count = 1 + planRoot->m_nodeList.size();
+ planRoot->freeNodeList();
+ delete planRoot;
+ m_stmtArea.m_planTree = 0;
+ ctx_log3(("CodeGen: freed %u plan tree nodes", count));
+ }
+}
+
+void
+CodeGen::freeExec(Ctx & ctx)
+{
+ if (m_stmtArea.m_execTree != 0) {
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ unsigned count = 1 + execRoot->m_nodeList.size();
+ execRoot->freeNodeList();
+ delete execRoot;
+ m_stmtArea.m_execTree = 0;
+ ctx_log3(("CodeGen: freed %u exec tree nodes", count));
+ }
+}
+
+// odbc support
+
+void
+CodeGen::sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ execRoot->sqlGetData(ctx, columnNumber, targetType, targetValue, bufferLength, strlen_or_Ind);
+}
+
+void
+CodeGen::sqlParamData(Ctx& ctx, SQLPOINTER* value)
+{
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ execRoot->sqlParamData(ctx, value);
+}
+
+void
+CodeGen::sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind)
+{
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ execRoot->sqlPutData(ctx, data, strlen_or_Ind);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/CodeGen.hpp b/storage/ndb/src/old_files/client/odbc/codegen/CodeGen.hpp
new file mode 100644
index 00000000000..ae61dab0c2a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/CodeGen.hpp
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_CodeGen_hpp
+#define ODBC_CODEGEN_CodeGen_hpp
+
+#include <common/common.hpp>
+
+class StmtArea;
+class SqlField;
+class ExtField;
+
+/**
+ * @class CodeGen
+ * @brief Compiles SQL text into ExecTree::Code
+ */
+class CodeGen {
+public:
+ CodeGen(StmtArea& stmtArea);
+ ~CodeGen();
+ // parse and analyze SQL statement
+ void prepare(Ctx& ctx);
+ // these are passed to Executor
+ void execute(Ctx& ctx);
+ void fetch(Ctx& ctx);
+ // close statement (mainly scan)
+ void close(Ctx& ctx);
+ // free data structures
+ void free(Ctx& ctx);
+ // odbc support
+ void sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind);
+ void sqlParamData(Ctx& ctx, SQLPOINTER* value);
+ void sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind);
+private:
+ void parse(Ctx& ctx);
+ void analyze(Ctx& ctx);
+ void describe(Ctx& ctx);
+ void codegen(Ctx& ctx);
+ void alloc(Ctx& ctx);
+ void freePlan(Ctx& ctx);
+ void freeExec(Ctx& ctx);
+ StmtArea& m_stmtArea;
+};
+
+inline
+CodeGen::CodeGen(StmtArea& stmtArea) :
+ m_stmtArea(stmtArea)
+{
+}
+
+inline
+CodeGen::~CodeGen()
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_base.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_base.cpp
new file mode 100644
index 00000000000..dc02e071156
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_base.cpp
@@ -0,0 +1,167 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_base.hpp"
+#include "Code_root.hpp"
+
+// Plan_base
+
+Plan_base::~Plan_base()
+{
+}
+
+StmtArea&
+Plan_base::stmtArea() const
+{
+ ctx_assert(m_root != 0);
+ return m_root->m_stmtArea;
+}
+
+DescArea&
+Plan_base::descArea(DescUsage u) const
+{
+ return stmtArea().descArea(u);
+}
+
+ConnArea&
+Plan_base::connArea() const
+{
+ return stmtArea().connArea();
+}
+
+DictCatalog&
+Plan_base::dictCatalog() const
+{
+ return connArea().dictCatalog();
+}
+
+DictSchema&
+Plan_base::dictSchema() const
+{
+ return connArea().dictSchema();
+}
+
+Ndb*
+Plan_base::ndbObject() const
+{
+ Ndb* ndb = connArea().ndbObject();
+ ctx_assert(ndb != 0);
+ return ndb;
+}
+
+NdbSchemaCon*
+Plan_base::ndbSchemaCon() const
+{
+ NdbSchemaCon* ndbSchemaCon = connArea().ndbSchemaCon();
+ ctx_assert(ndbSchemaCon != 0);
+ return ndbSchemaCon;
+}
+
+NdbConnection*
+Plan_base::ndbConnection() const
+{
+ NdbConnection* ndbConnection = connArea().ndbConnection();
+ ctx_assert(ndbConnection != 0);
+ return ndbConnection;
+}
+
+void
+Plan_base::printList(Ctx& ctx, Plan_base* a[], unsigned n)
+{
+ for (unsigned i = 0; i < n; i++) {
+ if (a[i] == 0)
+ ctx.print(" -");
+ else
+ a[i]->print(ctx);
+ }
+}
+
+// Exec_base
+
+Exec_base::Code::~Code()
+{
+}
+
+Exec_base::Data::~Data()
+{
+}
+
+Exec_base::~Exec_base()
+{
+ delete m_code; // remove when code becomes shared
+ m_code = 0;
+ delete m_data;
+ m_data = 0;
+}
+
+StmtArea&
+Exec_base::stmtArea() const
+{
+ ctx_assert(m_root != 0);
+ return m_root->m_stmtArea;
+}
+
+DescArea&
+Exec_base::descArea(DescUsage u) const
+{
+ return stmtArea().descArea(u);
+}
+
+ConnArea&
+Exec_base::connArea() const
+{
+ return stmtArea().connArea();
+}
+
+DictSchema&
+Exec_base::dictSchema() const
+{
+ return connArea().dictSchema();
+}
+
+Ndb*
+Exec_base::ndbObject() const
+{
+ Ndb* ndb = connArea().ndbObject();
+ ctx_assert(ndb != 0);
+ return ndb;
+}
+
+NdbSchemaCon*
+Exec_base::ndbSchemaCon() const
+{
+ NdbSchemaCon* ndbSchemaCon = connArea().ndbSchemaCon();
+ ctx_assert(ndbSchemaCon != 0);
+ return ndbSchemaCon;
+}
+
+NdbConnection*
+Exec_base::ndbConnection() const
+{
+ NdbConnection* ndbConnection = connArea().ndbConnection();
+ ctx_assert(ndbConnection != 0);
+ return ndbConnection;
+}
+
+void
+Exec_base::printList(Ctx& ctx, Exec_base* a[], unsigned n)
+{
+ for (unsigned i = 0; i < n; i++) {
+ ctx_assert(a[i] != 0);
+ a[i]->print(ctx);
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_base.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_base.hpp
new file mode 100644
index 00000000000..c67c0ca7adb
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_base.hpp
@@ -0,0 +1,237 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_base_hpp
+#define ODBC_CODEGEN_Code_base_hpp
+
+#include <set>
+#include <list>
+#include <vector>
+#include <common/common.hpp>
+#include <common/CodeTree.hpp>
+#include <common/DescArea.hpp>
+
+class Ctx;
+class ConnArea;
+class StmtArea;
+class DescArea;
+class DictCatalog;
+class DictSchema;
+class ResultArea;
+class ResultSet;
+class SpecRow;
+class Ndb;
+class NdbSchemaCon;
+class NdbConnection;
+class NdbOperation;
+class NdbScanFilter;
+
+class Plan_root;
+class Plan_table;
+class Plan_column;
+class Plan_expr;
+class Plan_expr_param;
+class Plan_pred;
+class Plan_dml_row;
+class Plan_dml_column;
+class Plan_ddl_column;
+class Plan_ddl_constr;
+class Plan_idx_column;
+class Exec_root;
+class Exec_base;
+class Exec_query;
+class Exec_expr;
+class Exec_expr_row;
+class Exec_expr_param;
+
+/**
+ * @class Plan_base
+ * @brief Base class for plan trees
+ */
+class Plan_base : public PlanTree {
+public:
+ Plan_base(Plan_root* root);
+ virtual ~Plan_base() = 0;
+ // get references to StmtArea via Plan_root
+ StmtArea& stmtArea() const;
+ DescArea& descArea(DescUsage u) const;
+ ConnArea& connArea() const;
+ // catalogs
+ DictCatalog& dictCatalog() const;
+ DictSchema& dictSchema() const;
+ // ndb
+ Ndb* ndbObject() const;
+ NdbSchemaCon* ndbSchemaCon() const;
+ NdbConnection* ndbConnection() const;
+ // containers for Plan classes
+ typedef std::vector<Plan_table*> TableVector;
+ typedef std::vector<Plan_column*> ColumnVector;
+ typedef std::vector<Plan_dml_column*> DmlColumnVector;
+ typedef std::vector<Plan_ddl_column*> DdlColumnVector;
+ typedef std::vector<Plan_ddl_constr*> DdlConstrVector;
+ typedef std::vector<Plan_idx_column*> IdxColumnVector;
+ typedef std::vector<Plan_expr*> ExprVector;
+ typedef std::list<Plan_expr*> ExprList;
+ typedef std::vector<ExprList> ExprListVector;
+ typedef std::list<Plan_pred*> PredList;
+ typedef std::set<Plan_table*> TableSet;
+ typedef std::vector<Plan_expr_param*> ParamVector;
+ // control area on the stack XXX needs to be designed
+ struct Ctl {
+ Ctl(Ctl* up);
+ Ctl* m_up; // up the stack
+ // analyze
+ TableVector m_tableList; // resolve column names
+ bool m_topand; // in top-level where clause
+ bool m_extra; // anything but single pk=expr
+ bool m_aggrok; // aggregate allowed
+ bool m_aggrin; // within aggregate args
+ bool m_const; // only constants in set clause
+ PredList m_topcomp; // top level comparisons
+ Plan_dml_row *m_dmlRow; // row type to convert to
+ Plan_table* m_topTable; // top level table for interpreted progs
+ bool m_having; // in having-predicate
+ // codegen
+ Exec_root* m_execRoot; // root of Exec tree
+ const Exec_query* m_execQuery; // pass to column
+ };
+ // semantic analysis and optimization
+ virtual Plan_base* analyze(Ctx& ctx, Ctl& ctl) = 0;
+ // generate "executable" code
+ virtual Exec_base* codegen(Ctx& ctx, Ctl& ctl) = 0;
+ // misc
+ virtual void print(Ctx& ctx) = 0;
+protected:
+ Plan_root* m_root;
+ void printList(Ctx& ctx, Plan_base* a[], unsigned n);
+};
+
+inline
+Plan_base::Plan_base(Plan_root* root) :
+ m_root(root)
+{
+ ctx_assert(m_root != 0);
+}
+
+inline
+Plan_base::Ctl::Ctl(Ctl* up) :
+ m_up(up),
+ m_tableList(1), // 1-based
+ m_topand(false),
+ m_extra(false),
+ m_aggrok(false),
+ m_aggrin(false),
+ m_dmlRow(0),
+ m_topTable(0),
+ m_having(false),
+ m_execRoot(0),
+ m_execQuery(0)
+{
+}
+
+/**
+ * @class Exec_base
+ * @brief Base class for exec trees
+ */
+class Exec_base : public ExecTree {
+public:
+ class Code : public ExecTree::Code {
+ public:
+ virtual ~Code() = 0;
+ };
+ class Data : public ExecTree::Data {
+ public:
+ virtual ~Data() = 0;
+ };
+ Exec_base(Exec_root* root);
+ virtual ~Exec_base() = 0;
+ // get references to StmtArea via Exec_root
+ virtual StmtArea& stmtArea() const;
+ DescArea& descArea(DescUsage u) const;
+ ConnArea& connArea() const;
+ // catalogs
+ DictSchema& dictSchema() const;
+ // ndb
+ Ndb* ndbObject() const;
+ NdbSchemaCon* ndbSchemaCon() const;
+ NdbConnection* ndbConnection() const;
+ // containers for Exec classes
+ typedef std::vector<Exec_expr*> ExprVector;
+ typedef std::vector<Exec_expr_param*> ParamVector;
+ // control area on the stack
+ struct Ctl {
+ Ctl(Ctl* up);
+ Ctl* m_up; // up the stack
+ const Exec_query* m_query; // pass Data
+ ExprVector m_exprList; // pass Data
+ NdbOperation* m_scanOp; // scan operation
+ bool m_postEval; // for rownum
+ unsigned m_groupIndex; // for group by
+ bool m_groupInit; // first in group
+ Exec_expr_row* m_sortRow; // from sort to group by
+ NdbScanFilter* m_scanFilter; // scan filter
+ };
+ // allocate and deallocate Data instances
+ virtual void alloc(Ctx& ctx, Ctl& ctl) = 0;
+ virtual void close(Ctx& ctx) = 0;
+ // set Code and Data
+ void setCode(const Code& code);
+ void setData(Data& data);
+ // misc
+ virtual void print(Ctx& ctx) = 0;
+protected:
+ const Code* m_code;
+ Data* m_data;
+ Exec_root* m_root;
+ void printList(Ctx& ctx, Exec_base* a[], unsigned n);
+};
+
+inline
+Exec_base::Exec_base(Exec_root* root) :
+ m_code(0),
+ m_data(0),
+ m_root(root)
+{
+ ctx_assert(m_root != 0);
+}
+
+inline void
+Exec_base::setCode(const Code& code)
+{
+ ctx_assert(m_code == 0);
+ m_code = &code;
+}
+
+inline void
+Exec_base::setData(Data& data)
+{
+ ctx_assert(m_data == 0);
+ m_data = &data;
+}
+
+inline
+Exec_base::Ctl::Ctl(Ctl* up) :
+ m_up(up),
+ m_scanOp(0),
+ m_postEval(false),
+ m_groupIndex(0),
+ m_groupInit(false),
+ m_sortRow(0),
+ m_scanFilter(0)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_column.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_column.cpp
new file mode 100644
index 00000000000..c4c0480a5e7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_column.cpp
@@ -0,0 +1,72 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_column.hpp"
+#include "Code_table_list.hpp"
+#include "Code_table.hpp"
+
+// Plan_column
+
+Plan_column::~Plan_column()
+{
+}
+
+void
+Plan_column::analyzeColumn(Ctx& ctx, Plan_base::Ctl& ctl)
+{
+ if (m_resTable != 0) // done on previous pass
+ return;
+ if (! (ctl.m_tableList.size() > 1)) {
+ ctx.pushStatus(Sqlstate::_42000, Error::Gen, "column %s not allowed here", getPrintName());
+ return;
+ }
+ unsigned resCount = 0;
+ for (unsigned i = 1; i < ctl.m_tableList.size(); i++) {
+ Plan_table* table = ctl.m_tableList[i];
+ ctx_assert(table != 0);
+ int ret = table->resolveColumn(ctx, this);
+ if (ret < 0)
+ return;
+ if (ret)
+ resCount++;
+ }
+ if (resCount == 0) {
+ // XXX try to strip "schema name" from table name
+ for (unsigned i = 1; i < ctl.m_tableList.size(); i++) {
+ Plan_table* table = ctl.m_tableList[i];
+ ctx_assert(table != 0);
+ int ret = table->resolveColumn(ctx, this, true);
+ if (ret < 0)
+ return;
+ if (ret)
+ resCount++;
+ }
+ }
+ if (resCount == 0) {
+ ctx.pushStatus(Sqlstate::_42S22, Error::Gen, "column %s not found", getPrintName());
+ return;
+ }
+ if (resCount > 1) {
+ ctx.pushStatus(Error::Gen, "column %s is ambiguous", getPrintName());
+ return;
+ }
+ // copy SQL type
+ m_sqlType = dictColumn().sqlType();
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_column.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_column.hpp
new file mode 100644
index 00000000000..af0dcea690d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_column.hpp
@@ -0,0 +1,122 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_column_hpp
+#define ODBC_CODEGEN_Code_column_hpp
+
+#include <common/common.hpp>
+#include <common/DataType.hpp>
+#include "Code_base.hpp"
+
+class DictColumn;
+class Plan_table;
+
+/**
+ * @class Plan_column
+ * @brief Abstract base class for columns
+ */
+class Plan_column {
+public:
+ enum Type {
+ Type_expr = 1,
+ Type_dml = 2,
+ Type_ddl = 3, // new columns in create table
+ Type_idx = 4 // old columns in create index
+ };
+ Plan_column(Type type, const BaseString& name);
+ virtual ~Plan_column() = 0;
+ void analyzeColumn(Ctx& ctx, Plan_base::Ctl& ctl);
+ // attributes
+ const BaseString& getName() const;
+ const BaseString& getCname() const;
+ const char* getPrintName() const;
+ void setCname(const BaseString& cname);
+ const DictColumn& dictColumn() const;
+ const SqlType& sqlType() const;
+protected:
+ friend class Plan_table;
+ friend class Plan_comp_op;
+ Type m_type;
+ BaseString m_name;
+ BaseString m_cname;
+ BaseString m_printName;
+ DictColumn* m_dictColumn;
+ /**
+ * Resolve to table and operational position (for example
+ * column number in scan query).
+ */
+ Plan_table* m_resTable;
+ unsigned m_resPos;
+ SqlType m_sqlType;
+};
+
+inline
+Plan_column::Plan_column(Type type, const BaseString& name) :
+ m_type(type),
+ m_name(name),
+ m_printName(name),
+ m_dictColumn(0),
+ m_resTable(0),
+ m_resPos(0)
+{
+}
+
+inline const BaseString&
+Plan_column::getName() const
+{
+ return m_name;
+}
+
+inline const BaseString&
+Plan_column::getCname() const
+{
+ return m_cname;
+}
+
+inline const char*
+Plan_column::getPrintName() const
+{
+ return m_printName.c_str();
+}
+
+inline void
+Plan_column::setCname(const BaseString& cname)
+{
+ m_cname.assign(cname);
+ if (m_cname.empty())
+ m_printName.assign(m_name);
+ else {
+ m_printName.assign(m_cname);
+ m_printName.append(".");
+ m_printName.append(m_name);
+ }
+}
+
+inline const DictColumn&
+Plan_column::dictColumn() const
+{
+ ctx_assert(m_dictColumn != 0);
+ return *m_dictColumn;
+}
+
+inline const SqlType&
+Plan_column::sqlType() const
+{
+ ctx_assert(m_sqlType.type() != SqlType::Undef);
+ return m_sqlType;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.cpp
new file mode 100644
index 00000000000..7782ed1ea2a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.cpp
@@ -0,0 +1,485 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <dictionary/DictColumn.hpp>
+#include "Code_pred.hpp"
+#include "Code_comp_op.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_expr_column.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+// Comp_op
+
+const char*
+Comp_op::name() const
+{
+ switch (m_opcode) {
+ case Eq:
+ return "=";
+ case Noteq:
+ return "!=";
+ case Lt:
+ return "<";
+ case Lteq:
+ return "<=";
+ case Gt:
+ return ">";
+ case Gteq:
+ return ">=";
+ case Like:
+ return "like";
+ case Notlike:
+ return "not like";
+ case Isnull:
+ return "is null";
+ case Isnotnull:
+ return "is not null";
+ }
+ ctx_assert(false);
+ return "";
+}
+
+unsigned
+Comp_op::arity() const
+{
+ switch (m_opcode) {
+ case Eq:
+ case Noteq:
+ case Lt:
+ case Lteq:
+ case Gt:
+ case Gteq:
+ case Like:
+ case Notlike:
+ return 2;
+ case Isnull:
+ case Isnotnull:
+ return 1;
+ }
+ ctx_assert(false);
+ return 0;
+}
+
+// Plan_comp_op
+
+Plan_comp_op::~Plan_comp_op()
+{
+}
+
+Plan_base*
+Plan_comp_op::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ const unsigned arity = m_op.arity();
+ // analyze operands
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ m_expr[i]->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // for each operand, find type to convert to
+ SqlType con[1 + 2];
+ if (arity == 1) {
+ const SqlType& t1 = m_expr[1]->sqlType();
+ switch (t1.type()) {
+ case SqlType::Char:
+ case SqlType::Varchar:
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ case SqlType::Real:
+ case SqlType::Double:
+ case SqlType::Datetime:
+ case SqlType::Null:
+ case SqlType::Unbound:
+ con[1] = t1;
+ break;
+ default:
+ break;
+ }
+ if (con[1].type() == SqlType::Undef) {
+ char b1[40];
+ t1.print(b1, sizeof(b1));
+ ctx.pushStatus(Error::Gen, "type mismatch in comparison: %s %s", b1, m_op.name());
+ return 0;
+ }
+ } else if (arity == 2) {
+ const SqlType& t1 = m_expr[1]->sqlType();
+ const SqlType& t2 = m_expr[2]->sqlType();
+ switch (t1.type()) {
+ case SqlType::Char:
+ switch (t2.type()) {
+ case SqlType::Char:
+ case SqlType::Varchar:
+ case SqlType::Null:
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Varchar:
+ switch (t2.type()) {
+ case SqlType::Char:
+ case SqlType::Varchar:
+ case SqlType::Null:
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ switch (t2.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ // conversion would mask primary key optimization
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Real:
+ case SqlType::Double:
+ con[1].setType(ctx, SqlType::Double);
+ con[2] = con[1];
+ break;
+ case SqlType::Null:
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Real:
+ case SqlType::Double:
+ switch (t2.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ case SqlType::Real:
+ case SqlType::Double:
+ con[1].setType(ctx, SqlType::Double);
+ con[2] = con[1];
+ break;
+ case SqlType::Null:
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Datetime:
+ switch (t2.type()) {
+ case SqlType::Datetime:
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Null:
+ switch (t2.type()) {
+ case SqlType::Char:
+ case SqlType::Varchar:
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ case SqlType::Real:
+ case SqlType::Double:
+ case SqlType::Datetime:
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t2;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Unbound:
+ con[1] = con[2] = t1;
+ break;
+ default:
+ break;
+ }
+ if (con[1].type() == SqlType::Undef || con[2].type() == SqlType::Undef) {
+ char b1[40], b2[40];
+ t1.print(b1, sizeof(b1));
+ t2.print(b2, sizeof(b2));
+ ctx.pushStatus(Error::Gen, "type mismatch in comparison: %s %s %s", b1, m_op.name(), b2);
+ return 0;
+ }
+ } else {
+ ctx_assert(false);
+ return 0;
+ }
+ if (! ctx.ok())
+ return 0;
+ // insert required conversions
+ for (unsigned i = 1; i <= arity; i++) {
+ if (con[i].type() == SqlType::Unbound) {
+ continue;
+ }
+ Plan_expr_conv* exprConv = new Plan_expr_conv(m_root, con[i]);
+ m_root->saveNode(exprConv);
+ exprConv->setExpr(m_expr[i]);
+ m_expr[i] = static_cast<Plan_expr*>(exprConv->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_expr[i] != 0);
+ }
+ // look for column=expr
+ if (ctl.m_topand && m_op.m_opcode == Comp_op::Eq) {
+ ctx_assert(arity == 2);
+ for (unsigned i = 1, j = 2; i <= 2; i++, j--) {
+ if (m_expr[i]->type() != Plan_expr::TypeColumn)
+ continue;
+ Plan_expr_column* column = static_cast<Plan_expr_column*>(m_expr[i]);
+ if (! column->resolveEq(ctx, m_expr[j]))
+ ctl.m_extra = true;
+ }
+ } else {
+ ctl.m_extra = true;
+ }
+ // save top level comparison on list
+ if (ctl.m_topand) {
+ ctl.m_topcomp.push_back(this);
+ }
+ // table dependencies are union from operands
+ m_tableSet.clear();
+ for (unsigned i = 1; i <= arity; i++) {
+ const TableSet& ts = m_expr[i]->tableSet();
+ m_tableSet.insert(ts.begin(), ts.end());
+ }
+ // set of tables for which interpreter cannot be used
+ m_noInterp.clear();
+ // convenient
+#undef ustype
+#define ustype(b, n) (((b) ? 1 : 0) * 100 + (n))
+ if (arity == 1) {
+ for (unsigned i = 1; i <= 1; i++) {
+ const SqlType t1 = m_expr[i]->sqlType();
+ switch (m_op.m_opcode) {
+ case Comp_op::Isnull:
+ case Comp_op::Isnotnull:
+ if (m_expr[i]->type() == Plan_expr::TypeColumn) {
+ switch (ustype(t1.unSigned(), t1.type())) {
+ // all types accepted now
+ default:
+ {
+ Plan_expr_column* column = static_cast<Plan_expr_column*>(m_expr[i]);
+ ctx_assert(column->m_resTable != 0);
+ m_interpColumn[i] = column;
+ continue; // ok
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ const TableSet& ts = m_expr[i]->tableSet();
+ m_noInterp.insert(ts.begin(), ts.end());
+ }
+ } else if (arity == 2) {
+ for (unsigned i = 1, j = 2; i <= 2; i++, j--) {
+ const SqlType t1 = m_expr[i]->sqlType();
+ switch (m_op.m_opcode) {
+ case Comp_op::Like:
+ case Comp_op::Notlike:
+ if (i == 2) // col like val but not val like col
+ break;
+ /*FALLTHRU*/
+ case Comp_op::Eq:
+ case Comp_op::Noteq:
+ case Comp_op::Lt:
+ case Comp_op::Lteq:
+ case Comp_op::Gt:
+ case Comp_op::Gteq:
+ if (m_expr[i]->type() == Plan_expr::TypeColumn) {
+ switch (ustype(t1.unSigned(), t1.type())) {
+ case ustype(false, SqlType::Char):
+ case ustype(false, SqlType::Varchar):
+ case ustype(true, SqlType::Smallint):
+ case ustype(true, SqlType::Integer):
+ case ustype(true, SqlType::Bigint):
+ {
+ Plan_expr_column* column = static_cast<Plan_expr_column*>(m_expr[i]);
+ ctx_assert(column->m_resTable != 0);
+ const TableSet& ts = m_expr[j]->tableSet();
+ if (ts.find(column->m_resTable) == ts.end()) {
+ // candidate for column=const
+ m_interpColumn[i] = column;
+ continue; // ok
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ const TableSet& ts = m_expr[i]->tableSet();
+ m_noInterp.insert(ts.begin(), ts.end());
+ }
+ } else {
+ ctx_assert(false);
+ return 0;
+ }
+#undef ustype
+ return this;
+}
+
+Exec_base*
+Plan_comp_op::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ const unsigned arity = m_op.arity();
+ Exec_comp_op* exec = new Exec_comp_op(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // create code for operands
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ Exec_expr* execExpr = static_cast<Exec_expr*>(m_expr[i]->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ exec->setExpr(i, execExpr);
+ }
+ // create the code
+ Exec_comp_op::Code& code = *new Exec_comp_op::Code(m_op);
+ // interpreted column=const
+ if (! ctl.m_having) {
+ ctx_assert(ctl.m_topTable != 0);
+ for (unsigned i = 1; i <= arity; i++) {
+ Plan_expr_column* column = m_interpColumn[i];
+ if (column == 0)
+ continue;
+ ctx_assert(column->m_resTable != 0);
+ if (column->m_resTable != ctl.m_topTable)
+ continue;
+ ctx_assert(code.m_interpColumn == 0);
+ code.m_interpColumn = i;
+ code.m_interpAttrId = column->dictColumn().getAttrId();
+ ctx_log2(("can use interpreter on %s", column->getPrintName()));
+ }
+ }
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_comp_op::print(Ctx& ctx)
+{
+ ctx.print(" [%s", m_op.name());
+ Plan_base* a[] = { m_expr[1], m_expr[2] };
+ printList(ctx, a, m_op.arity());
+ ctx.print("]");
+}
+
+bool
+Plan_comp_op::isGroupBy(const Plan_expr_row* row) const
+{
+ const unsigned arity = m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ if (! m_expr[i]->isGroupBy(row))
+ return false;
+ }
+ return true;
+}
+
+// Code_comp_op
+
+Exec_comp_op::Code::~Code()
+{
+}
+
+Exec_comp_op::Data::~Data()
+{
+}
+
+Exec_comp_op::~Exec_comp_op()
+{
+}
+
+void
+Exec_comp_op::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // allocate subexpressions
+ unsigned arity = code.m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ m_expr[i]->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_comp_op::close(Ctx& ctx)
+{
+ const Code& code = getCode();
+ unsigned arity = code.m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ m_expr[i]->close(ctx);
+ }
+}
+
+void
+Exec_comp_op::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [%s", code.m_op.name());
+ Exec_base* a[] = { m_expr[1], m_expr[2] };
+ printList(ctx, a, code.m_op.arity());
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.hpp
new file mode 100644
index 00000000000..0585ab1dabf
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.hpp
@@ -0,0 +1,172 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_comp_op_hpp
+#define ODBC_CODEGEN_Code_comp_op_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_pred.hpp"
+#include "Code_expr.hpp"
+#include "Code_expr_column.hpp"
+
+/**
+ * @class Comp_op
+ * @brief Comparison operations
+ */
+struct Comp_op {
+ enum Opcode {
+ Eq = 1, // binary
+ Noteq,
+ Lt,
+ Lteq,
+ Gt,
+ Gteq,
+ Like,
+ Notlike,
+ Isnull, // unary
+ Isnotnull
+ };
+ Comp_op(Opcode opcode);
+ const char* name() const;
+ unsigned arity() const;
+ Opcode m_opcode;
+};
+
+inline
+Comp_op::Comp_op(Opcode opcode) :
+ m_opcode(opcode)
+{
+}
+
+/**
+ * @class Plan_comp_op
+ * @brief Comparison operator node in PlanTree
+ */
+class Plan_comp_op : public Plan_pred {
+public:
+ Plan_comp_op(Plan_root* root, Comp_op op);
+ virtual ~Plan_comp_op();
+ void setExpr(unsigned i, Plan_expr* expr);
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ virtual bool isGroupBy(const Plan_expr_row* row) const;
+protected:
+ Comp_op m_op;
+ Plan_expr* m_expr[1 + 2];
+ Plan_expr_column* m_interpColumn[1 + 2]; // candidates
+};
+
+inline
+Plan_comp_op::Plan_comp_op(Plan_root* root, Comp_op op) :
+ Plan_pred(root, TypeComp),
+ m_op(op)
+{
+ m_expr[0] = m_expr[1] = m_expr[2] = 0;
+ m_interpColumn[0] = m_interpColumn[1] = m_interpColumn[2] = 0;
+}
+
+inline void
+Plan_comp_op::setExpr(unsigned i, Plan_expr* expr)
+{
+ ctx_assert(1 <= i && i <= 2);
+ m_expr[i] = expr;
+}
+
+/**
+ * @class Exec_comp_op
+ * @brief Comparison operator node in ExecTree
+ */
+class Exec_comp_op : public Exec_pred {
+public:
+ class Code : public Exec_pred::Code {
+ public:
+ Code(Comp_op op);
+ virtual ~Code();
+ protected:
+ friend class Plan_comp_op;
+ friend class Exec_comp_op;
+ Comp_op m_op;
+ unsigned m_interpColumn; // 1 or 2 if interpreted column, 0 if both constant
+ NdbAttrId m_interpAttrId;
+ };
+ class Data : public Exec_pred::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_comp_op;
+ };
+ Exec_comp_op(Exec_root* root);
+ virtual ~Exec_comp_op();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execInterp(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setExpr(unsigned i, Exec_expr* expr);
+protected:
+ Exec_expr* m_expr[1 + 2];
+};
+
+inline
+Exec_comp_op::Code::Code(Comp_op op) :
+ m_op(op),
+ m_interpColumn(0),
+ m_interpAttrId((NdbAttrId)-1)
+{
+}
+
+inline
+Exec_comp_op::Data::Data()
+{
+}
+
+inline
+Exec_comp_op::Exec_comp_op(Exec_root* root) :
+ Exec_pred(root)
+{
+ m_expr[0] = m_expr[1] = m_expr[2] = 0;
+}
+
+// children
+
+inline const Exec_comp_op::Code&
+Exec_comp_op::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_comp_op::Data&
+Exec_comp_op::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_comp_op::setExpr(unsigned i, Exec_expr* expr)
+{
+ ctx_assert(1 <= i && i <= 2 && m_expr[i] == 0);
+ m_expr[i] = expr;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.cpp
new file mode 100644
index 00000000000..84f319338a4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.cpp
@@ -0,0 +1,124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_create_index.hpp"
+#include "Code_root.hpp"
+
+// Plan_create_index
+
+Plan_create_index::~Plan_create_index()
+{
+}
+
+Plan_base*
+Plan_create_index::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_create_index);
+ // analyze the table
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ // analyze the columns
+ ctl.m_tableList.resize(1 + 1); // indexed from 1
+ ctl.m_tableList[1] = m_table;
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_idx_column* column = getColumn(i);
+ column->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ return this;
+}
+
+void
+Plan_create_index::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "CREATE INDEX", SQL_DIAG_CREATE_INDEX);
+}
+
+Exec_base*
+Plan_create_index::codegen(Ctx& ctx, Ctl& ctl)
+{
+ Exec_create_index* exec = new Exec_create_index(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ const unsigned count = countColumn();
+ const char** attrList = new const char* [1 + count];
+ attrList[0] = 0; // unused
+ for (unsigned i = 1; i <= count; i++) {
+ Plan_idx_column* column = getColumn(i);
+ const char* cname = column->getName().c_str();
+ attrList[i] = strcpy(new char[strlen(cname) + 1], cname);
+ }
+ Exec_create_index::Code& code = *new Exec_create_index::Code(m_name, m_table->getName(), m_type, count, attrList);
+ exec->setCode(code);
+ code.m_fragmentType = m_fragmentType;
+ code.m_logging = m_logging;
+ return exec;
+}
+
+void
+Plan_create_index::print(Ctx& ctx)
+{
+ ctx.print(" [create_index name=%s table=%s type=%d", m_name.c_str(), m_table->getName().c_str(), (int)m_type);
+ ctx.print(" [");
+ for (unsigned i = 1; i <= countColumn(); i++) {
+ Plan_idx_column* column = getColumn(i);
+ if (i > 1)
+ ctx.print(" ");
+ column->print(ctx);
+ }
+ ctx.print("]");
+}
+
+// Exec_create_index
+
+Exec_create_index::Code::~Code()
+{
+ for (unsigned i = 1; i <= m_attrCount; i++) {
+ delete[] m_attrList[i];
+ m_attrList[i] = 0;
+ }
+ delete[] m_attrList;
+}
+
+Exec_create_index::Data::~Data()
+{
+}
+
+Exec_create_index::~Exec_create_index()
+{
+}
+
+void
+Exec_create_index::alloc(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_create_index::close(Ctx& ctx)
+{
+}
+
+void
+Exec_create_index::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [create_index %s]", code.m_tableName.c_str());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.hpp
new file mode 100644
index 00000000000..ebd757e1118
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.hpp
@@ -0,0 +1,203 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_create_index_hpp
+#define ODBC_CODEGEN_Code_create_index_hpp
+
+#include <vector>
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include "Code_ddl.hpp"
+#include "Code_table.hpp"
+#include "Code_idx_column.hpp"
+
+class DictTable;
+class DictColumn;
+
+/**
+ * @class Plan_create_index
+ * @brief Create table in PlanTree
+ */
+class Plan_create_index : public Plan_ddl {
+public:
+ Plan_create_index(Plan_root* root, const BaseString& name);
+ virtual ~Plan_create_index();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx & ctx);
+ void print(Ctx& ctx);
+ // attributes
+ const BaseString& getName() const;
+ // children
+ void setType(NdbDictionary::Object::Type type);
+ void setTable(Plan_table* table);
+ unsigned countColumn() const;
+ void addColumn(Plan_idx_column* column);
+ Plan_idx_column* getColumn(unsigned i) const;
+ void setFragmentType(NdbDictionary::Object::FragmentType fragmentType);
+ void setLogging(bool logging);
+protected:
+ BaseString m_name;
+ NdbDictionary::Object::Type m_type;
+ Plan_table* m_table;
+ IdxColumnVector m_columnList;
+ NdbDictionary::Object::FragmentType m_fragmentType;
+ bool m_logging;
+};
+
+inline
+Plan_create_index::Plan_create_index(Plan_root* root, const BaseString& name) :
+ Plan_ddl(root),
+ m_name(name),
+ m_type(NdbDictionary::Object::TypeUndefined),
+ m_columnList(1),
+ m_fragmentType(NdbDictionary::Object::FragUndefined),
+ m_logging(true)
+{
+}
+
+inline const BaseString&
+Plan_create_index::getName() const
+{
+ return m_name;
+}
+
+// children
+
+inline void
+Plan_create_index::setType(NdbDictionary::Object::Type type)
+{
+ m_type = type;
+}
+
+inline void
+Plan_create_index::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline unsigned
+Plan_create_index::countColumn() const
+{
+ return m_columnList.size() - 1;
+}
+
+inline void
+Plan_create_index::addColumn(Plan_idx_column* column)
+{
+ ctx_assert(column != 0);
+ m_columnList.push_back(column);
+}
+
+inline Plan_idx_column*
+Plan_create_index::getColumn(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= countColumn() && m_columnList[i] != 0);
+ return m_columnList[i];
+}
+
+inline void
+Plan_create_index::setFragmentType(NdbDictionary::Object::FragmentType fragmentType)
+{
+ m_fragmentType = fragmentType;
+}
+
+inline void
+Plan_create_index::setLogging(bool logging)
+{
+ m_logging = logging;
+}
+
+/**
+ * @class Exec_create_index
+ * @brief Create table in ExecTree
+ */
+class Exec_create_index : public Exec_ddl {
+public:
+ class Code : public Exec_ddl::Code {
+ public:
+ Code(const BaseString& indexName, const BaseString& tableName, NdbDictionary::Object::Type type, unsigned attrCount, const char** attrList);
+ virtual ~Code();
+ protected:
+ friend class Plan_create_index;
+ friend class Exec_create_index;
+ const BaseString m_indexName;
+ const BaseString m_tableName;
+ NdbDictionary::Object::Type m_type;
+ const unsigned m_attrCount;
+ const char** m_attrList;
+ NdbDictionary::Object::FragmentType m_fragmentType;
+ bool m_logging;
+ };
+ class Data : public Exec_ddl::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_create_index;
+ };
+ Exec_create_index(Exec_root* root);
+ virtual ~Exec_create_index();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execute(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_create_index::Code::Code(const BaseString& indexName, const BaseString& tableName, NdbDictionary::Object::Type type, unsigned attrCount, const char** attrList) :
+ m_indexName(indexName),
+ m_tableName(tableName),
+ m_type(type),
+ m_attrCount(attrCount),
+ m_attrList(attrList),
+ m_fragmentType(NdbDictionary::Object::FragUndefined),
+ m_logging(true)
+{
+}
+
+inline
+Exec_create_index::Data::Data()
+{
+}
+
+inline
+Exec_create_index::Exec_create_index(Exec_root* root) :
+ Exec_ddl(root)
+{
+}
+
+// children
+
+inline const Exec_create_index::Code&
+Exec_create_index::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_create_index::Data&
+Exec_create_index::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.cpp
new file mode 100644
index 00000000000..5b90b658ed7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.cpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_create_row.hpp"
+#include "Code_root.hpp"
+
+Plan_create_row::~Plan_create_row()
+{
+}
+
+Plan_base*
+Plan_create_row::analyze(Ctx& ctx, Ctl& ctl)
+{
+ // check for duplicate column name
+ for (unsigned i = 1, n = countColumn(); i < n; i++) {
+ const BaseString& a = getColumn(i)->getName();
+ for (unsigned i2 = i + 1; i2 <= n; i2++) {
+ const BaseString& a2 = getColumn(i2)->getName();
+ if (strcmp(a.c_str(), a2.c_str()) == 0) {
+ ctx.pushStatus(Error::Gen, "duplicate column %s", a.c_str());
+ return 0;
+ }
+ }
+ }
+ // move single-column primary key constraint to constraint list
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = getColumn(i);
+ if (column->m_primaryKey) {
+ Plan_ddl_row* ddlRow = new Plan_ddl_row(m_root);
+ m_root->saveNode(ddlRow);
+ ddlRow->addColumn(column);
+ Plan_ddl_constr* constr = new Plan_ddl_constr(m_root);
+ m_root->saveNode(constr);
+ constr->setRow(ddlRow);
+ addConstr(constr);
+ column->m_primaryKey = false; // will be set again
+ }
+ }
+ // check primary key constraints
+ if (countConstr() < 1) {
+ ctx.pushStatus(Error::Gen, "table must have a primary key");
+ return 0;
+ }
+ if (countConstr() > 1) {
+ ctx.pushStatus(Error::Gen, "table can have only one primary key");
+ return 0;
+ }
+ Plan_ddl_row* ddlRow = getConstr(1)->getRow();
+ for (unsigned i = 1, n = ddlRow->countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = ddlRow->getColumn(i);
+ const BaseString& a = column->getName();
+ bool found = false;
+ for (unsigned i2 = 1, n2 = countColumn(); i2 <= n2; i2++) {
+ Plan_ddl_column* column2 = getColumn(i2);
+ const BaseString& a2 = column2->getName();
+ if (strcmp(a.c_str(), a2.c_str()) != 0)
+ continue;
+ if (column2->getPrimaryKey()) {
+ ctx.pushStatus(Error::Gen, "duplicate primary key constraint on %s", a.c_str());
+ return 0;
+ }
+ column2->setPrimaryKey();
+ found = true;
+ break;
+ }
+ if (! found) {
+ ctx.pushStatus(Error::Gen, "undefined primary key column %s", a.c_str());
+ return 0;
+ }
+ }
+ // analyze column types
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ getColumn(i)->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // check TupleId
+ unsigned tupleId = 0;
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = getColumn(i);
+ if (! column->getTupleId())
+ continue;
+ if (i != 1) {
+ ctx.pushStatus(Error::Gen, "tuple id column %u is not first column", i);
+ return 0;
+ }
+ if (tupleId != 0) { // cannot happen now since attr name is fixed
+ ctx.pushStatus(Error::Gen, "duplicate tuple id column %u", i);
+ return 0;
+ }
+ tupleId = i;
+ }
+ if (tupleId != 0) {
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = getColumn(i);
+ if (i == tupleId)
+ continue;
+ if (! column->getPrimaryKey())
+ continue;
+ ctx.pushStatus(Error::Gen, "cannot have both tuple id and other primary key column %u", i);
+ return 0;
+ }
+ }
+ // check auto-increment
+ unsigned autoIncrement = 0;
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = getColumn(i);
+ if (! column->getAutoIncrement())
+ continue;
+ if (autoIncrement != 0) {
+ ctx.pushStatus(Error::Gen, "duplicate auto-increment column %u", i);
+ return 0;
+ }
+ autoIncrement = i;
+ }
+ if (autoIncrement != 0) {
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = getColumn(i);
+ if (i == autoIncrement)
+ continue;
+ if (! column->getPrimaryKey())
+ continue;
+ ctx.pushStatus(Error::Gen, "cannot have both auto-increment column and other primary key column %u", i);
+ return 0;
+ }
+ }
+ return this;
+}
+
+Exec_base*
+Plan_create_row::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_create_row::print(Ctx& ctx)
+{
+ ctx.print(" [create_row");
+ for (unsigned i = 1; i <= countColumn(); i++) {
+ Plan_base* a = m_columnList[i];
+ printList(ctx, &a, 1);
+ }
+ for (unsigned i = 1; i <= countConstr(); i++) {
+ Plan_base* a = m_constrList[i];
+ printList(ctx, &a, 1);
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.hpp
new file mode 100644
index 00000000000..f03455ff28e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.hpp
@@ -0,0 +1,99 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_create_row_hpp
+#define ODBC_CODEGEN_Code_create_row_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_ddl_column.hpp"
+#include "Code_ddl_constr.hpp"
+
+/**
+ * @class Plan_create_row
+ * @brief Row of columns and constraints in create statement
+ */
+class Plan_create_row : public Plan_base {
+public:
+ Plan_create_row(Plan_root* root);
+ virtual ~Plan_create_row();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ unsigned countColumn() const;
+ void addColumn(Plan_ddl_column* column);
+ Plan_ddl_column* getColumn(unsigned i) const;
+ unsigned countConstr() const;
+ void addConstr(Plan_ddl_constr* constr);
+ Plan_ddl_constr* getConstr(unsigned i) const;
+protected:
+ DdlColumnVector m_columnList;
+ DdlConstrVector m_constrList;
+};
+
+inline
+Plan_create_row::Plan_create_row(Plan_root* root) :
+ Plan_base(root),
+ m_columnList(1),
+ m_constrList(1)
+{
+}
+
+// children
+
+inline unsigned
+Plan_create_row::countColumn() const
+{
+ return m_columnList.size() - 1;
+}
+
+inline void
+Plan_create_row::addColumn(Plan_ddl_column* column)
+{
+ ctx_assert(column != 0);
+ m_columnList.push_back(column);
+}
+
+inline Plan_ddl_column*
+Plan_create_row::getColumn(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_columnList.size() && m_columnList[i] != 0);
+ return m_columnList[i];
+}
+
+inline unsigned
+Plan_create_row::countConstr() const
+{
+ return m_constrList.size() - 1;
+}
+
+inline void
+Plan_create_row::addConstr(Plan_ddl_constr* constr)
+{
+ ctx_assert(constr != 0);
+ m_constrList.push_back(constr);
+}
+
+inline Plan_ddl_constr*
+Plan_create_row::getConstr(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_constrList.size() && m_constrList[i] != 0);
+ return m_constrList[i];
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.cpp
new file mode 100644
index 00000000000..14e4abbd7fe
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.cpp
@@ -0,0 +1,137 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_create_table.hpp"
+#include "Code_root.hpp"
+
+// Plan_create_table
+
+Plan_create_table::~Plan_create_table()
+{
+}
+
+Plan_base*
+Plan_create_table::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_create_table);
+ // analyze the create row
+ ctx_assert(m_createRow != 0);
+ m_createRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_create_table::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "CREATE TABLE", SQL_DIAG_CREATE_TABLE);
+}
+
+Exec_base*
+Plan_create_table::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_createRow != 0);
+ Exec_create_table* exec = new Exec_create_table(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ const unsigned count = m_createRow->countColumn();
+ Exec_create_table::Code::Attr* attrList = new Exec_create_table::Code::Attr[1 + count];
+ unsigned tupleId = 0;
+ unsigned autoIncrement = 0;
+ for (unsigned i = 1; i <= count; i++) {
+ Plan_ddl_column* column = m_createRow->getColumn(i);
+ Exec_create_table::Code::Attr& attr = attrList[i];
+ attr.m_attrName.assign(column->getName());
+ attr.m_sqlType = column->sqlType();
+ attr.m_tupleKey = column->getPrimaryKey();
+ attr.m_tupleId = column->getTupleId();
+ attr.m_autoIncrement = column->getAutoIncrement();
+ if (attr.m_tupleId)
+ tupleId = i;
+ if (attr.m_autoIncrement)
+ autoIncrement = i;
+ attr.m_defaultValue = 0;
+ Plan_expr* expr;
+ if ((expr = column->getDefaultValue()) != 0) {
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ attr.m_defaultValue = execExpr;
+ }
+ }
+ Exec_create_table::Code& code = *new Exec_create_table::Code(m_name, count, attrList, tupleId, autoIncrement);
+ exec->setCode(code);
+ code.m_fragmentType = m_fragmentType;
+ code.m_logging = m_logging;
+ return exec;
+}
+
+void
+Plan_create_table::print(Ctx& ctx)
+{
+ ctx.print(" [create_table '%s'", m_name.c_str());
+ Plan_base* a[] = { m_createRow };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_create_table
+
+Exec_create_table::Code::~Code()
+{
+ delete[] m_attrList;
+}
+
+Exec_create_table::Data::~Data()
+{
+}
+
+Exec_create_table::~Exec_create_table()
+{
+}
+
+void
+Exec_create_table::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const Code::Attr& attr = code.m_attrList[i];
+ if (attr.m_defaultValue != 0)
+ attr.m_defaultValue->alloc(ctx, ctl);
+ }
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_create_table::close(Ctx& ctx)
+{
+ const Code& code = getCode();
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const Code::Attr& attr = code.m_attrList[i];
+ if (attr.m_defaultValue != 0)
+ attr.m_defaultValue->close(ctx);
+ }
+}
+
+void
+Exec_create_table::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [create_table %s]", code.m_tableName.c_str());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.hpp
new file mode 100644
index 00000000000..cbb2189d8ce
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.hpp
@@ -0,0 +1,178 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_create_table_hpp
+#define ODBC_CODEGEN_Code_create_table_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include "Code_ddl.hpp"
+#include "Code_ddl_row.hpp"
+#include "Code_create_row.hpp"
+
+class DictTable;
+class DictColumn;
+
+/**
+ * @class Plan_create_table
+ * @brief Create table in PlanTree
+ */
+class Plan_create_table : public Plan_ddl {
+public:
+ Plan_create_table(Plan_root* root, const BaseString& name);
+ virtual ~Plan_create_table();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx & ctx);
+ void print(Ctx& ctx);
+ // attributes
+ const BaseString& getName() const;
+ // children
+ void setCreateRow(Plan_create_row* createRow);
+ void setFragmentType(NdbDictionary::Object::FragmentType fragmentType);
+ void setLogging(bool logging);
+protected:
+ BaseString m_name;
+ Plan_create_row* m_createRow;
+ NdbDictionary::Object::FragmentType m_fragmentType;
+ bool m_logging;
+};
+
+inline
+Plan_create_table::Plan_create_table(Plan_root* root, const BaseString& name) :
+ Plan_ddl(root),
+ m_name(name),
+ m_createRow(0),
+ m_fragmentType(NdbDictionary::Object::FragUndefined),
+ m_logging(true)
+{
+}
+
+inline const BaseString&
+Plan_create_table::getName() const
+{
+ return m_name;
+}
+
+// children
+
+inline void
+Plan_create_table::setCreateRow(Plan_create_row* createRow)
+{
+ ctx_assert(createRow != 0);
+ m_createRow = createRow;
+}
+
+inline void
+Plan_create_table::setFragmentType(NdbDictionary::Object::FragmentType fragmentType)
+{
+ m_fragmentType = fragmentType;
+}
+
+inline void
+Plan_create_table::setLogging(bool logging)
+{
+ m_logging = logging;
+}
+
+/**
+ * @class Exec_create_table
+ * @brief Create table in ExecTree
+ */
+class Exec_create_table : public Exec_ddl {
+public:
+ class Code : public Exec_ddl::Code {
+ public:
+ struct Attr {
+ Attr() : m_defaultValue(0) {}
+ BaseString m_attrName;
+ SqlType m_sqlType;
+ bool m_tupleKey;
+ bool m_tupleId;
+ bool m_autoIncrement;
+ Exec_expr* m_defaultValue;
+ };
+ Code(const BaseString& tableName, unsigned attrCount, const Attr* attrList, unsigned tupleId, unsigned autoIncrement);
+ virtual ~Code();
+ protected:
+ friend class Plan_create_table;
+ friend class Exec_create_table;
+ const BaseString m_tableName;
+ const unsigned m_attrCount;
+ const Attr* const m_attrList;
+ unsigned m_tupleId;
+ unsigned m_autoIncrement;
+ NdbDictionary::Object::FragmentType m_fragmentType;
+ bool m_logging;
+ };
+ class Data : public Exec_ddl::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_create_table;
+ };
+ Exec_create_table(Exec_root* root);
+ virtual ~Exec_create_table();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execute(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_create_table::Code::Code(const BaseString& tableName, unsigned attrCount, const Attr* attrList, unsigned tupleId, unsigned autoIncrement) :
+ m_tableName(tableName),
+ m_attrCount(attrCount),
+ m_attrList(attrList),
+ m_tupleId(tupleId),
+ m_autoIncrement(autoIncrement),
+ m_fragmentType(NdbDictionary::Object::FragUndefined),
+ m_logging(true)
+{
+}
+
+inline
+Exec_create_table::Data::Data()
+{
+}
+
+inline
+Exec_create_table::Exec_create_table(Exec_root* root) :
+ Exec_ddl(root)
+{
+}
+
+// children
+
+inline const Exec_create_table::Code&
+Exec_create_table::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_create_table::Data&
+Exec_create_table::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.cpp
new file mode 100644
index 00000000000..1ff0fcebcbe
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.cpp
@@ -0,0 +1,44 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include "Code_data_type.hpp"
+
+// Plan_data_type
+
+Plan_data_type::~Plan_data_type()
+{
+}
+
+Plan_base*
+Plan_data_type::analyze(Ctx& ctx, Ctl& ctl)
+{
+ return this;
+}
+
+Exec_base*
+Plan_data_type::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_data_type::print(Ctx& ctx)
+{
+ ctx.print(" [data_type]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.hpp
new file mode 100644
index 00000000000..735dc05014f
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.hpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_data_type_hpp
+#define ODBC_CODEGEN_Code_data_type_hpp
+
+#include <common/common.hpp>
+#include <common/DataType.hpp>
+#include "Code_base.hpp"
+
+/**
+ * @class Plan_data_type
+ * @brief Data type in DDL statement
+ *
+ * This is pure plan node.
+ */
+class Plan_data_type : public Plan_base {
+public:
+ Plan_data_type(Plan_root* root, const SqlType& sqlType);
+ virtual ~Plan_data_type();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+private:
+ friend class Plan_ddl_column;
+ SqlType m_sqlType;
+};
+
+inline
+Plan_data_type::Plan_data_type(Plan_root* root, const SqlType& sqlType) :
+ Plan_base(root),
+ m_sqlType(sqlType)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.cpp
new file mode 100644
index 00000000000..2ba4291a0e8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.cpp
@@ -0,0 +1,37 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_ddl.hpp"
+
+// Plan_ddl
+
+Plan_ddl::~Plan_ddl()
+{
+}
+
+// Exec_ddl
+
+Exec_ddl::Code::~Code()
+{
+}
+
+Exec_ddl::Data::~Data()
+{
+}
+
+Exec_ddl::~Exec_ddl()
+{
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.hpp
new file mode 100644
index 00000000000..1ceca62d55d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.hpp
@@ -0,0 +1,63 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_ddl_hpp
+#define ODBC_CODEGEN_Code_ddl_hpp
+
+#include <common/common.hpp>
+#include "Code_stmt.hpp"
+
+/**
+ * @class Plan_ddl
+ * @brief Base class for DDL statements in PlanTree
+ */
+class Plan_ddl : public Plan_stmt {
+public:
+ Plan_ddl(Plan_root* root);
+ virtual ~Plan_ddl() = 0;
+};
+
+inline
+Plan_ddl::Plan_ddl(Plan_root* root) :
+ Plan_stmt(root)
+{
+}
+
+/**
+ * @class Exec_ddl
+ * @brief Base class for DDL statements in ExecTree
+ */
+class Exec_ddl : public Exec_stmt {
+public:
+ class Code : public Exec_stmt::Code {
+ public:
+ virtual ~Code() = 0;
+ };
+ class Data : public Exec_stmt::Data {
+ public:
+ virtual ~Data() = 0;
+ };
+ Exec_ddl(Exec_root* root);
+ virtual ~Exec_ddl() = 0;
+};
+
+inline
+Exec_ddl::Exec_ddl(Exec_root* root) :
+ Exec_stmt(root)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.cpp
new file mode 100644
index 00000000000..ee037e54c1f
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.cpp
@@ -0,0 +1,104 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include "Code_ddl_column.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_root.hpp"
+
+// Plan_ddl_column
+
+Plan_ddl_column::~Plan_ddl_column()
+{
+}
+
+Plan_base*
+Plan_ddl_column::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_type != 0);
+ if (! m_type->m_sqlType.nullable()) {
+ m_nullable = false;
+ }
+ m_sqlType = m_type->m_sqlType;
+ m_sqlType.nullable(m_nullable);
+ const BaseString& name = getName();
+ if (m_unSigned) {
+ switch (m_sqlType.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "invalid unsigned qualifier on column %s", name.c_str());
+ return 0;
+ }
+ m_sqlType.unSigned(true);
+ }
+ if (strcmp(name.c_str(), "NDB$TID") == 0) {
+ if (! m_primaryKey) {
+ ctx.pushStatus(Error::Gen, "column %s must be a primary key", name.c_str());
+ return 0;
+ }
+ if (sqlType().type() != SqlType::Bigint || ! sqlType().unSigned()) {
+ ctx.pushStatus(Error::Gen, "tuple id %s must have type BIGINT UNSIGNED", name.c_str());
+ return 0;
+ }
+ setTupleId();
+ }
+ if (m_autoIncrement) {
+ if (! m_primaryKey) {
+ ctx.pushStatus(Error::Gen, "auto-increment column %s must be a primary key", name.c_str());
+ return 0;
+ }
+ if (sqlType().type() != SqlType::Smallint && sqlType().type() != SqlType::Integer && sqlType().type() != SqlType::Bigint) {
+ ctx.pushStatus(Error::Gen, "auto-increment column %s must have an integral type", name.c_str());
+ return 0;
+ }
+ }
+ if (m_defaultValue != 0) {
+ if (m_primaryKey) {
+ ctx.pushStatus(Sqlstate::_42000, Error::Gen, "default value not allowed on primary key column %s", name.c_str());
+ return 0;
+ }
+ m_defaultValue->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ // insert conversion node
+ Plan_expr_conv* exprConv = new Plan_expr_conv(m_root, sqlType());
+ m_root->saveNode(exprConv);
+ exprConv->setExpr(m_defaultValue);
+ Plan_expr* expr = static_cast<Plan_expr*>(exprConv->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(expr != 0);
+ m_defaultValue = expr;
+ }
+ return this;
+}
+
+Exec_base*
+Plan_ddl_column::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_ddl_column::print(Ctx& ctx)
+{
+ ctx.print(" [ddl_column %s key=%d id=%d]", getPrintName(), m_primaryKey, m_tupleId);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.hpp
new file mode 100644
index 00000000000..7d089d37440
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.hpp
@@ -0,0 +1,150 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_ddl_column_hpp
+#define ODBC_CODEGEN_Code_ddl_column_hpp
+
+#include <common/common.hpp>
+#include "Code_column.hpp"
+#include "Code_data_type.hpp"
+#include "Code_expr.hpp"
+
+class DictColumn;
+class Plan_table;
+
+/**
+ * @class Plan_ddl_column
+ * @brief Column in DDL statement
+ */
+class Plan_ddl_column : public Plan_base, public Plan_column {
+public:
+ Plan_ddl_column(Plan_root* root, const BaseString& name);
+ virtual ~Plan_ddl_column();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // attributes
+ void setNotNull();
+ void setUnSigned();
+ void setPrimaryKey();
+ bool getPrimaryKey() const;
+ void setTupleId();
+ bool getTupleId() const;
+ void setAutoIncrement();
+ bool getAutoIncrement() const;
+ // children
+ void setType(Plan_data_type* type);
+ void setDefaultValue(Plan_expr* defaultValue);
+ Plan_expr* getDefaultValue() const;
+protected:
+ friend class Plan_create_row;
+ Plan_data_type* m_type;
+ Plan_expr* m_defaultValue;
+ bool m_nullable;
+ bool m_unSigned;
+ bool m_primaryKey;
+ bool m_tupleId;
+ bool m_autoIncrement;
+};
+
+inline
+Plan_ddl_column::Plan_ddl_column(Plan_root* root, const BaseString& name) :
+ Plan_base(root),
+ Plan_column(Type_ddl, name),
+ m_type(0),
+ m_defaultValue(0),
+ m_nullable(true),
+ m_unSigned(false),
+ m_primaryKey(false),
+ m_tupleId(false),
+ m_autoIncrement(false)
+{
+}
+
+inline void
+Plan_ddl_column::setNotNull()
+{
+ m_nullable = false;
+}
+
+inline void
+Plan_ddl_column::setUnSigned()
+{
+ m_unSigned = true;
+}
+
+inline void
+Plan_ddl_column::setPrimaryKey()
+{
+ m_nullable = false;
+ m_primaryKey = true;
+}
+
+inline bool
+Plan_ddl_column::getPrimaryKey() const
+{
+ return m_primaryKey;
+}
+
+inline void
+Plan_ddl_column::setTupleId()
+{
+ m_nullable = false;
+ m_tupleId = true;
+}
+
+inline bool
+Plan_ddl_column::getTupleId() const
+{
+ return m_tupleId;
+}
+
+inline void
+Plan_ddl_column::setAutoIncrement()
+{
+ m_nullable = false;
+ m_autoIncrement = true;
+}
+
+inline bool
+Plan_ddl_column::getAutoIncrement() const
+{
+ return m_autoIncrement;
+}
+
+// children
+
+inline void
+Plan_ddl_column::setType(Plan_data_type* type)
+{
+ ctx_assert(type != 0);
+ m_type = type;
+}
+
+inline void
+Plan_ddl_column::setDefaultValue(Plan_expr* defaultValue)
+{
+ ctx_assert(defaultValue != 0);
+ m_defaultValue = defaultValue;
+}
+
+inline Plan_expr*
+Plan_ddl_column::getDefaultValue() const
+{
+ return m_defaultValue;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.cpp
new file mode 100644
index 00000000000..78c23e38d97
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include "Code_ddl_constr.hpp"
+
+// Plan_ddl_constr
+
+Plan_ddl_constr::~Plan_ddl_constr()
+{
+}
+
+Plan_base*
+Plan_ddl_constr::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_ddlRow != 0);
+ m_ddlRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_ddl_constr::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_ddl_constr::print(Ctx& ctx)
+{
+ ctx.print(" [ddl_constr");
+ Plan_base* a[] = { m_ddlRow };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.hpp
new file mode 100644
index 00000000000..ea7808b37cb
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.hpp
@@ -0,0 +1,65 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_ddl_constr_hpp
+#define ODBC_CODEGEN_Code_ddl_constr_hpp
+
+#include <common/common.hpp>
+#include "Code_ddl_row.hpp"
+
+/**
+ * @class Plan_ddl_constr
+ * @brief Constraint in DDL statement
+ *
+ * Only unnamed primary key constraint exists.
+ */
+class Plan_ddl_constr : public Plan_base {
+public:
+ Plan_ddl_constr(Plan_root* root);
+ virtual ~Plan_ddl_constr();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setRow(Plan_ddl_row* ddlRow);
+ Plan_ddl_row* getRow() const;
+protected:
+ Plan_ddl_row* m_ddlRow;
+};
+
+inline
+Plan_ddl_constr::Plan_ddl_constr(Plan_root* root) :
+ Plan_base(root)
+{
+}
+
+// children
+
+inline void
+Plan_ddl_constr::setRow(Plan_ddl_row* ddlRow)
+{
+ ctx_assert(ddlRow != 0);
+ m_ddlRow = ddlRow;
+}
+
+inline Plan_ddl_row*
+Plan_ddl_constr::getRow() const
+{
+ ctx_assert(m_ddlRow != 0);
+ return m_ddlRow;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.cpp
new file mode 100644
index 00000000000..87589ebbaa0
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.cpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_ddl_row.hpp"
+#include "Code_ddl_column.hpp"
+#include "Code_ddl_constr.hpp"
+
+Plan_ddl_row::~Plan_ddl_row()
+{
+}
+
+Plan_base*
+Plan_ddl_row::analyze(Ctx& ctx, Ctl& ctl)
+{
+ // analyze the columns
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_ddl_column* column = getColumn(i);
+ column->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // node was not replaced
+ return this;
+}
+
+Exec_base*
+Plan_ddl_row::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_ddl_row::print(Ctx& ctx)
+{
+ ctx.print(" [ddl_row");
+ for (unsigned i = 1, n = countColumn(); i <= n; i++) {
+ Plan_base* a = m_columnList[i];
+ printList(ctx, &a, 1);
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.hpp
new file mode 100644
index 00000000000..ac3eded1b2e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.hpp
@@ -0,0 +1,72 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_ddl_row_hpp
+#define ODBC_CODEGEN_Code_ddl_row_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_ddl_column.hpp"
+
+/**
+ * @class Plan_ddl_row
+ * @brief Row of columns in create statement
+ */
+class Plan_ddl_row : public Plan_base {
+public:
+ Plan_ddl_row(Plan_root* root);
+ virtual ~Plan_ddl_row();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ unsigned countColumn() const;
+ void addColumn(Plan_ddl_column* column);
+ Plan_ddl_column* getColumn(unsigned i) const;
+protected:
+ DdlColumnVector m_columnList;
+};
+
+inline
+Plan_ddl_row::Plan_ddl_row(Plan_root* root) :
+ Plan_base(root),
+ m_columnList(1)
+{
+}
+
+// children
+
+inline unsigned
+Plan_ddl_row::countColumn() const
+{
+ return m_columnList.size() - 1;
+}
+
+inline void
+Plan_ddl_row::addColumn(Plan_ddl_column* column)
+{
+ ctx_assert(column != 0);
+ m_columnList.push_back(column);
+}
+
+inline Plan_ddl_column*
+Plan_ddl_row::getColumn(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= countColumn() && m_columnList[i] != 0);
+ return m_columnList[i];
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete.cpp
new file mode 100644
index 00000000000..35b3daa1aca
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete.cpp
@@ -0,0 +1,205 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_delete.hpp"
+#include "Code_delete_lookup.hpp"
+#include "Code_delete_index.hpp"
+#include "Code_delete_scan.hpp"
+#include "Code_query_filter.hpp"
+#include "Code_query_lookup.hpp"
+#include "Code_query_index.hpp"
+#include "Code_query_scan.hpp"
+#include "Code_query_range.hpp"
+#include "Code_query_repeat.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+Plan_delete::~Plan_delete()
+{
+}
+
+Plan_base*
+Plan_delete::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_delete);
+ // analyze the table
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ // set name resolution scope
+ ctl.m_tableList.resize(1 + 1); // indexed from 1
+ ctl.m_tableList[1] = m_table;
+ Plan_dml* stmt = 0;
+ if (m_pred != 0) {
+ // analyze the predicate
+ ctl.m_topand = true;
+ ctl.m_extra = false;
+ m_pred = static_cast<Plan_pred*>(m_pred->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_pred != 0);
+ // check for key match
+ Plan_table::Index* indexBest = 0;
+ for (unsigned i = 0; i <= m_table->indexCount(); i++) {
+ Plan_table::Index& index = m_table->m_indexList[i];
+ TableSet tsDone;
+ m_table->resolveSet(ctx, index, tsDone);
+ if (! ctx.ok())
+ return 0;
+ if (! index.m_keyFound)
+ continue;
+ // prefer smaller rank, less unused keys
+ int k;
+ (k = (indexBest == 0)) ||
+ (k = (indexBest->m_rank - index.m_rank)) ||
+ (k = (indexBest->m_keyCountUnused - index.m_keyCountUnused));
+ if (k > 0)
+ indexBest = &index;
+ }
+ if (indexBest != 0) {
+ const bool exactKey = indexBest->m_rank <= 1 ? m_table->exactKey(ctx, indexBest) : false;
+ const bool direct = ! ctl.m_extra && exactKey;
+ ctx_log3(("delete direct=%d: extra=%d exact=%d", direct, ctl.m_extra, exactKey));
+ if (indexBest->m_rank == 0) {
+ // primary key
+ Plan_delete_lookup* deleteLookup = new Plan_delete_lookup(m_root);
+ m_root->saveNode(deleteLookup);
+ deleteLookup->setTable(m_table);
+ if (direct) {
+ // key match with no extra conditions
+ Plan_query_repeat* queryRepeat = new Plan_query_repeat(m_root, 1);
+ m_root->saveNode(queryRepeat);
+ deleteLookup->setQuery(queryRepeat);
+ } else {
+ // key match with extra conditions
+ Plan_query_lookup* queryLookup = new Plan_query_lookup(m_root);
+ m_root->saveNode(queryLookup);
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ queryLookup->setTable(m_table);
+ queryFilter->setQuery(queryLookup);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ deleteLookup->setQuery(queryFilter);
+ }
+ stmt = deleteLookup;
+ } else if (indexBest->m_rank == 1) {
+ // hash index
+ Plan_delete_index* deleteIndex = new Plan_delete_index(m_root);
+ m_root->saveNode(deleteIndex);
+ deleteIndex->setTable(m_table, indexBest);
+ if (direct) {
+ // key match with no extra conditions
+ Plan_query_repeat* queryRepeat = new Plan_query_repeat(m_root, 1);
+ m_root->saveNode(queryRepeat);
+ deleteIndex->setQuery(queryRepeat);
+ } else {
+ // key match with extra conditions
+ Plan_query_index* queryIndex = new Plan_query_index(m_root);
+ m_root->saveNode(queryIndex);
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ queryIndex->setTable(m_table, indexBest);
+ queryFilter->setQuery(queryIndex);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ deleteIndex->setQuery(queryFilter);
+ }
+ stmt = deleteIndex;
+ } else if (indexBest->m_rank == 2) {
+ // ordered index
+ Plan_delete_scan* deleteScan = new Plan_delete_scan(m_root);
+ m_root->saveNode(deleteScan);
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ Plan_query_range* queryRange = new Plan_query_range(m_root);
+ m_root->saveNode(queryRange);
+ queryRange->setTable(m_table, indexBest);
+ queryRange->setExclusive();
+ queryFilter->setQuery(queryRange);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ const TableSet& ts2 = m_pred->noInterp();
+ ctx_assert(ts2.size() <= 1);
+ if (ts2.size() == 0) {
+ queryRange->setInterp(m_pred);
+ }
+ deleteScan->setQuery(queryFilter);
+ stmt = deleteScan;
+ } else {
+ ctx_assert(false);
+ }
+ } else {
+ // scan delete with filter
+ Plan_delete_scan* deleteScan = new Plan_delete_scan(m_root);
+ m_root->saveNode(deleteScan);
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ Plan_query_scan* queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(m_table);
+ queryScan->setExclusive();
+ queryFilter->setQuery(queryScan);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ // interpeter
+ const TableSet& ts2 = m_pred->noInterp();
+ ctx_assert(ts2.size() <= 1);
+ if (ts2.size() == 0) {
+ queryScan->setInterp(m_pred);
+ }
+ deleteScan->setQuery(queryFilter);
+ stmt = deleteScan;
+ }
+ } else {
+ // scan delete without filter
+ Plan_delete_scan* deleteScan = new Plan_delete_scan(m_root);
+ m_root->saveNode(deleteScan);
+ Plan_query_scan* queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(m_table);
+ queryScan->setExclusive();
+ deleteScan->setQuery(queryScan);
+ stmt = deleteScan;
+ }
+ // set base for column position offsets
+ m_table->m_resOff = 1;
+ return stmt;
+}
+
+void
+Plan_delete::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "DELETE WHERE", SQL_DIAG_DELETE_WHERE);
+}
+
+Exec_base*
+Plan_delete::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_delete::print(Ctx& ctx)
+{
+ ctx.print(" [delete");
+ Plan_base* a[] = { m_table, m_pred };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete.hpp
new file mode 100644
index 00000000000..c7fa245497b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete.hpp
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_delete_hpp
+#define ODBC_CODEGEN_Code_delete_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_dml.hpp"
+#include "Code_table.hpp"
+#include "Code_query.hpp"
+#include "Code_pred.hpp"
+
+/**
+ * @class Plan_delete
+ * @brief Delete in PlanTree
+ */
+class Plan_delete : public Plan_dml {
+public:
+ Plan_delete(Plan_root* root);
+ virtual ~Plan_delete();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+ void setPred(Plan_pred* pred);
+protected:
+ Plan_table* m_table;
+ Plan_pred* m_pred;
+};
+
+inline
+Plan_delete::Plan_delete(Plan_root* root) :
+ Plan_dml(root),
+ m_table(0),
+ m_pred(0)
+{
+}
+
+inline void
+Plan_delete::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline void
+Plan_delete::setPred(Plan_pred* pred)
+{
+ ctx_assert(pred != 0);
+ m_pred = pred;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.cpp
new file mode 100644
index 00000000000..8f2c3be2848
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.cpp
@@ -0,0 +1,164 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include "Code_expr.hpp"
+#include "Code_delete_index.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+Plan_delete_index::~Plan_delete_index()
+{
+}
+
+Plan_base*
+Plan_delete_index::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_delete_index::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "DELETE WHERE", SQL_DIAG_DELETE_WHERE);
+}
+
+Exec_base*
+Plan_delete_index::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the query
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // set up
+ ctx_assert(m_table != 0 && m_index != 0);
+ const BaseString& tableName = m_table->getName();
+ ctx_assert(m_index->m_dictIndex != 0);
+ const DictIndex& dictIndex = *m_index->m_dictIndex;
+ const BaseString& indexName = dictIndex.getName();
+ const unsigned keyCount = m_index->m_keyCount;
+ // create the code
+ Exec_delete_index::Code& code = *new Exec_delete_index::Code(keyCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ code.m_indexName = strcpy(new char[indexName.length() + 1], indexName.c_str());
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictIndex.getColumn(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = k - 1; // index column order
+ }
+ // matching expressions
+ ctx_assert(m_index->m_keyFound);
+ const ExprVector& keyEq = m_index->m_keyEq;
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // create the exec
+ Exec_delete_index* exec = new Exec_delete_index(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_delete_index::print(Ctx& ctx)
+{
+ ctx.print(" [delete_index");
+ Plan_base* a[] = { m_query, m_table };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_delete_index
+
+Exec_delete_index::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+}
+
+Exec_delete_index::Data::~Data()
+{
+}
+
+Exec_delete_index::~Exec_delete_index()
+{
+}
+
+void
+Exec_delete_index::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ // create data
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_delete_index::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+}
+
+void
+Exec_delete_index::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [delete_index");
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ printList(ctx, (Exec_base**)&code.m_keyMatch[1], code.m_keyCount);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.hpp
new file mode 100644
index 00000000000..1aaaa18abcb
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.hpp
@@ -0,0 +1,156 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_delete_index_hpp
+#define ODBC_CODEGEN_Code_delete_index_hpp
+
+#include <common/common.hpp>
+#include "Code_dml.hpp"
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+
+/**
+ * @class Plan_delete_index
+ * @brief Delete by primary key
+ */
+class Plan_delete_index : public Plan_dml {
+public:
+ Plan_delete_index(Plan_root* root);
+ virtual ~Plan_delete_index();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setTable(Plan_table* table, Plan_table::Index* index);
+protected:
+ Plan_query* m_query;
+ Plan_table* m_table;
+ Plan_table::Index* m_index;
+};
+
+inline
+Plan_delete_index::Plan_delete_index(Plan_root* root) :
+ Plan_dml(root),
+ m_query(0),
+ m_table(0)
+{
+}
+
+inline void
+Plan_delete_index::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_delete_index::setTable(Plan_table* table, Plan_table::Index* index)
+{
+ ctx_assert(table != 0 && index != 0 && index == &table->m_indexList[index->m_pos] && index->m_pos != 0);
+ m_table = table;
+ m_index = index;
+}
+
+/**
+ * @class Exec_delete_index
+ * @brief Delete by primary key
+ */
+class Exec_delete_index : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code(unsigned keyCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_delete_index;
+ friend class Exec_delete_index;
+ const char* m_tableName;
+ const char* m_indexName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_delete_index;
+ };
+ Exec_delete_index(Exec_root* root);
+ virtual ~Exec_delete_index();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+protected:
+ Exec_query* m_query;
+};
+
+inline
+Exec_delete_index::Code::Code(unsigned keyCount) :
+ m_tableName(0),
+ m_indexName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_keyMatch(0)
+{
+}
+
+inline
+Exec_delete_index::Data::Data()
+{
+}
+
+inline
+Exec_delete_index::Exec_delete_index(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_delete_index::Code&
+Exec_delete_index::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_delete_index::Data&
+Exec_delete_index::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_delete_index::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0 && m_query == 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.cpp
new file mode 100644
index 00000000000..4a6dec64654
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.cpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include "Code_expr.hpp"
+#include "Code_delete_lookup.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+Plan_delete_lookup::~Plan_delete_lookup()
+{
+}
+
+Plan_base*
+Plan_delete_lookup::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_delete_lookup::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "DELETE WHERE", SQL_DIAG_DELETE_WHERE);
+}
+
+Exec_base*
+Plan_delete_lookup::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the query
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // set up
+ ctx_assert(m_table != 0);
+ const BaseString& tableName = m_table->getName();
+ const DictTable& dictTable = m_table->dictTable();
+ const unsigned keyCount = dictTable.keyCount();
+ // create the code
+ Exec_delete_lookup::Code& code = *new Exec_delete_lookup::Code(keyCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictTable.getKey(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = keyColumn->getAttrId();
+ }
+ // matching expressions
+ const Plan_table::Index& index = m_table->m_indexList[0];
+ ctx_assert(index.m_keyFound);
+ const ExprVector& keyEq = index.m_keyEq;
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // create the exec
+ Exec_delete_lookup* exec = new Exec_delete_lookup(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_delete_lookup::print(Ctx& ctx)
+{
+ ctx.print(" [delete_lookup");
+ Plan_base* a[] = { m_query, m_table };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_delete_lookup
+
+Exec_delete_lookup::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+}
+
+Exec_delete_lookup::Data::~Data()
+{
+}
+
+Exec_delete_lookup::~Exec_delete_lookup()
+{
+}
+
+void
+Exec_delete_lookup::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ // create data
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_delete_lookup::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+}
+
+void
+Exec_delete_lookup::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [delete_lookup");
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ printList(ctx, (Exec_base**)&code.m_keyMatch[1], code.m_keyCount);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.hpp
new file mode 100644
index 00000000000..4138baefa4c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.hpp
@@ -0,0 +1,152 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_delete_lookup_hpp
+#define ODBC_CODEGEN_Code_delete_lookup_hpp
+
+#include <common/common.hpp>
+#include "Code_dml.hpp"
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+
+/**
+ * @class Plan_delete_lookup
+ * @brief Delete by primary key
+ */
+class Plan_delete_lookup : public Plan_dml {
+public:
+ Plan_delete_lookup(Plan_root* root);
+ virtual ~Plan_delete_lookup();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setTable(Plan_table* table);
+protected:
+ Plan_query* m_query;
+ Plan_table* m_table;
+};
+
+inline
+Plan_delete_lookup::Plan_delete_lookup(Plan_root* root) :
+ Plan_dml(root),
+ m_query(0),
+ m_table(0)
+{
+}
+
+inline void
+Plan_delete_lookup::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_delete_lookup::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+/**
+ * @class Exec_delete_lookup
+ * @brief Delete by primary key
+ */
+class Exec_delete_lookup : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code(unsigned keyCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_delete_lookup;
+ friend class Exec_delete_lookup;
+ char* m_tableName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_delete_lookup;
+ };
+ Exec_delete_lookup(Exec_root* root);
+ virtual ~Exec_delete_lookup();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+protected:
+ Exec_query* m_query;
+};
+
+inline
+Exec_delete_lookup::Code::Code(unsigned keyCount) :
+ m_tableName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_keyMatch(0)
+{
+}
+
+inline
+Exec_delete_lookup::Data::Data()
+{
+}
+
+inline
+Exec_delete_lookup::Exec_delete_lookup(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_delete_lookup::Code&
+Exec_delete_lookup::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_delete_lookup::Data&
+Exec_delete_lookup::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_delete_lookup::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0 && m_query == 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.cpp
new file mode 100644
index 00000000000..fed7244a026
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.cpp
@@ -0,0 +1,110 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_delete_scan.hpp"
+#include "Code_root.hpp"
+
+Plan_delete_scan::~Plan_delete_scan()
+{
+}
+
+Plan_base*
+Plan_delete_scan::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_delete_scan::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "DELETE WHERE", SQL_DIAG_DELETE_WHERE);
+}
+
+Exec_base*
+Plan_delete_scan::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // create the code
+ Exec_delete_scan* exec = new Exec_delete_scan(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ Exec_delete_scan::Code& code = *new Exec_delete_scan::Code;
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_delete_scan::print(Ctx& ctx)
+{
+ ctx.print(" [delete_scan");
+ Plan_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_delete_scan
+
+Exec_delete_scan::Code::~Code()
+{
+}
+
+Exec_delete_scan::Data::~Data()
+{
+}
+
+Exec_delete_scan::~Exec_delete_scan()
+{
+}
+
+void
+Exec_delete_scan::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // create data
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_delete_scan::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+}
+
+void
+Exec_delete_scan::print(Ctx& ctx)
+{
+ ctx.print(" [delete_scan");
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.hpp
new file mode 100644
index 00000000000..eb013a8257e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.hpp
@@ -0,0 +1,130 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_delete_scan_hpp
+#define ODBC_CODEGEN_Code_delete_scan_hpp
+
+#include <common/common.hpp>
+#include "Code_dml.hpp"
+#include "Code_query.hpp"
+
+/**
+ * @class Plan_delete_scan
+ * @brief Scan delete
+ */
+class Plan_delete_scan : public Plan_dml {
+public:
+ Plan_delete_scan(Plan_root* root);
+ virtual ~Plan_delete_scan();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+protected:
+ Plan_query* m_query;
+};
+
+inline
+Plan_delete_scan::Plan_delete_scan(Plan_root* root) :
+ Plan_dml(root),
+ m_query(0)
+{
+}
+
+inline void
+Plan_delete_scan::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+/**
+ * @class Exec_delete_scan
+ * @brief Scan delete
+ */
+class Exec_delete_scan : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code();
+ virtual ~Code();
+ protected:
+ friend class Exec_delete_scan;
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_delete_scan;
+ };
+ Exec_delete_scan(Exec_root* root);
+ virtual ~Exec_delete_scan();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+protected:
+ Exec_query* m_query;
+};
+
+inline
+Exec_delete_scan::Code::Code()
+{
+}
+
+inline
+Exec_delete_scan::Data::Data()
+{
+}
+
+inline
+Exec_delete_scan::Exec_delete_scan(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_delete_scan::Code&
+Exec_delete_scan::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_delete_scan::Data&
+Exec_delete_scan::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_delete_scan::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0 && m_query == 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_dml.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml.cpp
new file mode 100644
index 00000000000..44fd4478646
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_dml.hpp"
+
+// Plan_dml
+
+Plan_dml::~Plan_dml()
+{
+}
+
+// Exec_dml
+
+Exec_dml::Code::~Code()
+{
+}
+
+Exec_dml::Data::~Data()
+{
+}
+
+Exec_dml::~Exec_dml()
+{
+}
+
+void
+Exec_dml::execute(Ctx& ctx, Ctl& ctl)
+{
+ execImpl(ctx, ctl);
+ if (m_topLevel) {
+ if (ctx.ok()) {
+ if (stmtArea().getRowCount() == 0) {
+ ctx.setCode(SQL_NO_DATA);
+ }
+ }
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_dml.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml.hpp
new file mode 100644
index 00000000000..0618f583984
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml.hpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_dml_hpp
+#define ODBC_CODEGEN_Code_dml_hpp
+
+#include <common/common.hpp>
+#include <common/ResultArea.hpp>
+#include "Code_stmt.hpp"
+
+/**
+ * @class Plan_dml
+ * @brief Base class for DML statements in PlanTree
+ */
+class Plan_dml : public Plan_stmt {
+public:
+ Plan_dml(Plan_root* root);
+ virtual ~Plan_dml() = 0;
+};
+
+inline
+Plan_dml::Plan_dml(Plan_root* root) :
+ Plan_stmt(root)
+{
+}
+
+/**
+ * @class Exec_dml
+ * @brief Base class for DML statements in ExecTree
+ */
+class Exec_dml : public Exec_stmt {
+public:
+ class Code : public Exec_stmt::Code {
+ public:
+ virtual ~Code() = 0;
+ };
+ class Data : public Exec_stmt::Data, public ResultArea {
+ public:
+ virtual ~Data() = 0;
+ };
+ Exec_dml(Exec_root* root);
+ virtual ~Exec_dml() = 0;
+ void execute(Ctx& ctx, Ctl& ctl);
+protected:
+ virtual void execImpl(Ctx& ctx, Ctl& ctl) = 0;
+};
+
+inline
+Exec_dml::Exec_dml(Exec_root* root) :
+ Exec_stmt(root)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.cpp
new file mode 100644
index 00000000000..808e2ac8c4b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include "Code_dml_column.hpp"
+
+// Plan_dml_column
+
+Plan_dml_column::~Plan_dml_column()
+{
+}
+
+Plan_base*
+Plan_dml_column::analyze(Ctx& ctx, Ctl& ctl)
+{
+ analyzeColumn(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_dml_column::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_dml_column::print(Ctx& ctx)
+{
+ ctx.print(" [dml_column %s]", getPrintName());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.hpp
new file mode 100644
index 00000000000..0fb33944a3a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.hpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_dml_column_hpp
+#define ODBC_CODEGEN_Code_dml_column_hpp
+
+#include <common/common.hpp>
+#include "Code_column.hpp"
+
+class DictColumn;
+class Plan_table;
+
+/**
+ * @class Plan_dml_column
+ * @brief Column in query expression
+ */
+class Plan_dml_column : public Plan_base, public Plan_column {
+public:
+ Plan_dml_column(Plan_root* root, const BaseString& name);
+ virtual ~Plan_dml_column();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+};
+
+inline
+Plan_dml_column::Plan_dml_column(Plan_root* root, const BaseString& name) :
+ Plan_base(root),
+ Plan_column(Type_dml, name)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.cpp
new file mode 100644
index 00000000000..ceb63a9f7b9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.cpp
@@ -0,0 +1,56 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_dml_row.hpp"
+#include "Code_dml_column.hpp"
+
+Plan_dml_row::~Plan_dml_row()
+{
+}
+
+Plan_base*
+Plan_dml_row::analyze(Ctx& ctx, Ctl& ctl)
+{
+ unsigned size = getSize();
+ // analyze the columns
+ for (unsigned i = 1; i <= size; i++) {
+ Plan_dml_column* column = getColumn(i);
+ column->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // node was not replaced
+ return this;
+}
+
+Exec_base*
+Plan_dml_row::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_dml_row::print(Ctx& ctx)
+{
+ unsigned size = getSize();
+ ctx.print(" [dml_row");
+ for (unsigned i = 1; i <= size; i++) {
+ Plan_base* a = m_columnList[i];
+ a == 0 ? ctx.print(" -") : a->print(ctx);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.hpp
new file mode 100644
index 00000000000..6c7e46ba9af
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.hpp
@@ -0,0 +1,76 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_dml_row_hpp
+#define ODBC_CODEGEN_Code_dml_row_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include <common/DataRow.hpp>
+#include "Code_base.hpp"
+#include "Code_dml_column.hpp"
+
+class Plan_dml_column;
+
+/**
+ * @class Plan_dml_row
+ * @brief Row of lvalue columns in insert or update
+ */
+class Plan_dml_row : public Plan_base {
+public:
+ Plan_dml_row(Plan_root* root);
+ virtual ~Plan_dml_row();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ unsigned getSize() const;
+ void addColumn(Plan_dml_column* column);
+ Plan_dml_column* getColumn(unsigned i) const;
+protected:
+ DmlColumnVector m_columnList;
+};
+
+inline
+Plan_dml_row::Plan_dml_row(Plan_root* root) :
+ Plan_base(root),
+ m_columnList(1)
+{
+}
+
+// children
+
+inline unsigned
+Plan_dml_row::getSize() const
+{
+ return m_columnList.size() - 1;
+}
+
+inline void
+Plan_dml_row::addColumn(Plan_dml_column* column)
+{
+ ctx_assert(column != 0);
+ m_columnList.push_back(column);
+}
+
+inline Plan_dml_column*
+Plan_dml_row::getColumn(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_columnList.size() && m_columnList[i] != 0);
+ return m_columnList[i];
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.cpp
new file mode 100644
index 00000000000..b6bae88e270
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.cpp
@@ -0,0 +1,87 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_drop_index.hpp"
+#include "Code_root.hpp"
+
+// Plan_drop_index
+
+Plan_drop_index::~Plan_drop_index()
+{
+}
+
+Plan_base*
+Plan_drop_index::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_drop_index);
+ return this;
+}
+
+void
+Plan_drop_index::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "DROP INDEX", SQL_DIAG_DROP_INDEX);
+}
+
+Exec_base*
+Plan_drop_index::codegen(Ctx& ctx, Ctl& ctl)
+{
+ Exec_drop_index* exec = new Exec_drop_index(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ Exec_drop_index::Code& code = *new Exec_drop_index::Code(m_name, m_tableName);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_drop_index::print(Ctx& ctx)
+{
+ ctx.print(" [drop_index %s]", m_name.c_str());
+}
+
+// Exec_drop_index
+
+Exec_drop_index::Code::~Code()
+{
+}
+
+Exec_drop_index::Data::~Data()
+{
+}
+
+Exec_drop_index::~Exec_drop_index()
+{
+}
+
+void
+Exec_drop_index::alloc(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_drop_index::close(Ctx& ctx)
+{
+}
+
+void
+Exec_drop_index::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [drop_index %s]", code.m_indexName.c_str());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.hpp
new file mode 100644
index 00000000000..99891c9a52f
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.hpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_drop_index_hpp
+#define ODBC_CODEGEN_Code_drop_index_hpp
+
+#include <vector>
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include "Code_ddl.hpp"
+
+class DictTable;
+class DictColumn;
+
+/**
+ * @class Plan_drop_index
+ * @brief Drop index in PlanTree
+ */
+class Plan_drop_index : public Plan_ddl {
+public:
+ Plan_drop_index(Plan_root* root, const BaseString& name);
+ Plan_drop_index(Plan_root* root, const BaseString& name, const BaseString& tableName);
+ virtual ~Plan_drop_index();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx & ctx);
+ void print(Ctx& ctx);
+ // attributes
+ const BaseString& getName() const;
+protected:
+ BaseString m_name;
+ BaseString m_tableName;
+};
+
+inline
+Plan_drop_index::Plan_drop_index(Plan_root* root, const BaseString& name) :
+ Plan_ddl(root),
+ m_name(name)
+{
+}
+
+inline
+Plan_drop_index::Plan_drop_index(Plan_root* root, const BaseString& name, const BaseString& tableName) :
+ Plan_ddl(root),
+ m_name(name),
+ m_tableName(tableName)
+{
+}
+
+inline const BaseString&
+Plan_drop_index::getName() const
+{
+ return m_name;
+}
+
+/**
+ * @class Exec_drop_index
+ * @brief Drop index in ExecTree
+ */
+class Exec_drop_index : public Exec_ddl {
+public:
+ class Code : public Exec_ddl::Code {
+ public:
+ Code(const BaseString& indexName, const BaseString& tableName);
+ virtual ~Code();
+ protected:
+ friend class Exec_drop_index;
+ const BaseString m_indexName;
+ const BaseString m_tableName;
+ };
+ class Data : public Exec_ddl::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_drop_index;
+ };
+ Exec_drop_index(Exec_root* root);
+ virtual ~Exec_drop_index();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execute(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_drop_index::Code::Code(const BaseString& indexName, const BaseString& tableName) :
+ m_indexName(indexName),
+ m_tableName(tableName)
+{
+}
+
+inline
+Exec_drop_index::Data::Data()
+{
+}
+
+inline
+Exec_drop_index::Exec_drop_index(Exec_root* root) :
+ Exec_ddl(root)
+{
+}
+
+// children
+
+inline const Exec_drop_index::Code&
+Exec_drop_index::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_drop_index::Data&
+Exec_drop_index::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.cpp
new file mode 100644
index 00000000000..f20bf9fdae0
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.cpp
@@ -0,0 +1,87 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_drop_table.hpp"
+#include "Code_root.hpp"
+
+// Plan_drop_table
+
+Plan_drop_table::~Plan_drop_table()
+{
+}
+
+Plan_base*
+Plan_drop_table::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_drop_table);
+ return this;
+}
+
+void
+Plan_drop_table::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "DROP TABLE", SQL_DIAG_DROP_TABLE);
+}
+
+Exec_base*
+Plan_drop_table::codegen(Ctx& ctx, Ctl& ctl)
+{
+ Exec_drop_table* exec = new Exec_drop_table(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ Exec_drop_table::Code& code = *new Exec_drop_table::Code(m_name);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_drop_table::print(Ctx& ctx)
+{
+ ctx.print(" [drop_table %s]", m_name.c_str());
+}
+
+// Exec_drop_table
+
+Exec_drop_table::Code::~Code()
+{
+}
+
+Exec_drop_table::Data::~Data()
+{
+}
+
+Exec_drop_table::~Exec_drop_table()
+{
+}
+
+void
+Exec_drop_table::alloc(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_drop_table::close(Ctx& ctx)
+{
+}
+
+void
+Exec_drop_table::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [drop_table %s]", code.m_tableName.c_str());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.hpp
new file mode 100644
index 00000000000..849a472ed94
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.hpp
@@ -0,0 +1,124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_drop_table_hpp
+#define ODBC_CODEGEN_Code_drop_table_hpp
+
+#include <vector>
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include "Code_ddl.hpp"
+
+class DictTable;
+class DictColumn;
+
+/**
+ * @class Plan_drop_table
+ * @brief Drop table in PlanTree
+ */
+class Plan_drop_table : public Plan_ddl {
+public:
+ Plan_drop_table(Plan_root* root, const BaseString& name);
+ virtual ~Plan_drop_table();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx & ctx);
+ void print(Ctx& ctx);
+ // attributes
+ const BaseString& getName() const;
+protected:
+ BaseString m_name;
+};
+
+inline
+Plan_drop_table::Plan_drop_table(Plan_root* root, const BaseString& name) :
+ Plan_ddl(root),
+ m_name(name)
+{
+}
+
+inline const BaseString&
+Plan_drop_table::getName() const
+{
+ return m_name;
+}
+
+/**
+ * @class Exec_drop_table
+ * @brief Drop table in ExecTree
+ */
+class Exec_drop_table : public Exec_ddl {
+public:
+ class Code : public Exec_ddl::Code {
+ public:
+ Code(const BaseString& tableName);
+ virtual ~Code();
+ protected:
+ friend class Exec_drop_table;
+ const BaseString m_tableName;
+ };
+ class Data : public Exec_ddl::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_drop_table;
+ };
+ Exec_drop_table(Exec_root* root);
+ virtual ~Exec_drop_table();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execute(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_drop_table::Code::Code(const BaseString& tableName) :
+ m_tableName(tableName)
+{
+}
+
+inline
+Exec_drop_table::Data::Data()
+{
+}
+
+inline
+Exec_drop_table::Exec_drop_table(Exec_root* root) :
+ Exec_ddl(root)
+{
+}
+
+// children
+
+inline const Exec_drop_table::Code&
+Exec_drop_table::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_drop_table::Data&
+Exec_drop_table::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr.cpp
new file mode 100644
index 00000000000..4afa75986a0
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr.cpp
@@ -0,0 +1,79 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_expr.hpp"
+#include "Code_expr_row.hpp"
+
+// Plan_expr
+
+Plan_expr::~Plan_expr()
+{
+}
+
+bool
+Plan_expr::isEqual(const Plan_expr* expr) const
+{
+ return false;
+}
+
+bool
+Plan_expr::isAnyEqual(const Plan_expr_row* row) const
+{
+ ctx_assert(row != 0);
+ const unsigned size = row->getSize();
+ for (unsigned i = 1; i <= size; i++) {
+ if (isEqual(row->getExpr(i)))
+ return true;
+ }
+ return false;
+}
+
+bool
+Plan_expr::isGroupBy(const Plan_expr_row* row) const
+{
+ return false;
+}
+
+// Exec_expr
+
+Exec_expr::Code::~Code()
+{
+}
+
+Exec_expr::Data::~Data()
+{
+}
+
+Exec_expr::~Exec_expr()
+{
+}
+
+SqlField&
+Exec_expr::Data::groupField(const SqlType& sqlType, unsigned i, bool initFlag)
+{
+ if (m_groupField.size() == 0) {
+ m_groupField.resize(1);
+ }
+ if (initFlag) {
+ //unsigned i2 = m_groupField.size();
+ //ctx_assert(i == i2);
+ const SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ const SqlField sqlField(sqlSpec);
+ m_groupField.push_back(sqlField);
+ }
+ ctx_assert(i != 0 && i < m_groupField.size());
+ return m_groupField[i];
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr.hpp
new file mode 100644
index 00000000000..b6f07471b4d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr.hpp
@@ -0,0 +1,219 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_hpp
+#define ODBC_CODEGEN_Code_expr_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_base.hpp"
+
+class Ctx;
+class Plan_expr_row;
+class Exec_expr;
+
+/**
+ * @class Plan_expr
+ * @brief Base class for expressions in PlanTree
+ */
+class Plan_expr : public Plan_base {
+public:
+ // type is convenient since RTTI cannot be used
+ enum Type {
+ TypeUndefined = 0,
+ TypeColumn,
+ TypeConst,
+ TypeConv,
+ TypeFunc,
+ TypeOp,
+ TypeParam,
+ TypeValue
+ };
+ Plan_expr(Plan_root* root, Type type);
+ virtual ~Plan_expr() = 0;
+ Type type() const;
+ const SqlType& sqlType() const; // data type set by analyze
+ const TableSet& tableSet() const;
+ const BaseString& getAlias() const;
+ bool isAggr() const;
+ bool isBound() const;
+ bool isAnyEqual(const Plan_expr_row* row) const;
+ virtual bool isEqual(const Plan_expr* expr) const;
+ virtual bool isGroupBy(const Plan_expr_row* row) const;
+protected:
+ friend class Plan_expr_row;
+ friend class Plan_expr_op;
+ friend class Plan_expr_func;
+ friend class Plan_comp_op;
+ const Type m_type;
+ SqlType m_sqlType; // subclass must set
+ BaseString m_alias; // for row expression alias
+ TableSet m_tableSet; // depends on these tables
+ bool m_isAggr; // contains an aggregate expression
+ bool m_isBound; // only constants and aggregates
+ Exec_expr* m_exec; // XXX wrong move
+};
+
+inline
+Plan_expr::Plan_expr(Plan_root* root, Type type) :
+ Plan_base(root),
+ m_type(type),
+ m_isAggr(false),
+ m_isBound(false),
+ m_exec(0)
+{
+}
+
+inline Plan_expr::Type
+Plan_expr::type() const
+{
+ return m_type;
+}
+
+inline const SqlType&
+Plan_expr::sqlType() const
+{
+ ctx_assert(m_sqlType.type() != SqlType::Undef);
+ return m_sqlType;
+}
+
+inline const Plan_base::TableSet&
+Plan_expr::tableSet() const
+{
+ return m_tableSet;
+}
+
+inline const BaseString&
+Plan_expr::getAlias() const
+{
+ return m_alias;
+}
+
+inline bool
+Plan_expr::isAggr() const
+{
+ return m_isAggr;
+}
+
+inline bool
+Plan_expr::isBound() const
+{
+ return m_isBound;
+}
+
+/**
+ * @class Exec_expr
+ * @brief Base class for expressions in ExecTree
+ */
+class Exec_expr : public Exec_base {
+public:
+ /**
+ * Exec_expr::Code includes reference to SqlSpec which
+ * specifies data type and access method.
+ */
+ class Code : public Exec_base::Code {
+ public:
+ Code(const SqlSpec& sqlSpec);
+ virtual ~Code() = 0;
+ const SqlSpec& sqlSpec() const;
+ protected:
+ friend class Exec_expr;
+ const SqlSpec& m_sqlSpec; // subclass must contain
+ };
+ /**
+ * Exec_expr::Data includes reference to SqlField which
+ * contains specification and data address.
+ */
+ class Data : public Exec_base::Data {
+ public:
+ Data(const SqlField& sqlField);
+ virtual ~Data() = 0;
+ const SqlField& sqlField() const;
+ const SqlField& groupField(unsigned i) const;
+ protected:
+ friend class Exec_expr;
+ const SqlField& m_sqlField; // subclass must contain
+ // group-by data
+ typedef std::vector<SqlField> GroupField;
+ GroupField m_groupField;
+ SqlField& groupField(const SqlType& sqlType, unsigned i, bool initFlag);
+ };
+ Exec_expr(Exec_root* root);
+ virtual ~Exec_expr() = 0;
+ /**
+ * Evaluate the expression. Must be implemented by all
+ * subclasses. Check ctx.ok() for errors.
+ */
+ virtual void evaluate(Ctx& ctx, Ctl& ctl) = 0;
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_expr::Code::Code(const SqlSpec& sqlSpec) :
+ m_sqlSpec(sqlSpec)
+{
+}
+
+inline const SqlSpec&
+Exec_expr::Code::sqlSpec() const {
+ return m_sqlSpec;
+}
+
+inline
+Exec_expr::Data::Data(const SqlField& sqlField) :
+ m_sqlField(sqlField)
+{
+}
+
+inline const SqlField&
+Exec_expr::Data::sqlField() const
+{
+ return m_sqlField;
+}
+
+inline const SqlField&
+Exec_expr::Data::groupField(unsigned i) const
+{
+ ctx_assert(i != 0 && i < m_groupField.size());
+ return m_groupField[i];
+}
+
+inline
+Exec_expr::Exec_expr(Exec_root* root) :
+ Exec_base(root)
+{
+}
+
+// children
+
+inline const Exec_expr::Code&
+Exec_expr::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr::Data&
+Exec_expr::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.cpp
new file mode 100644
index 00000000000..17a9a502d4c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.cpp
@@ -0,0 +1,160 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+#include "Code_expr_column.hpp"
+#include "Code_root.hpp"
+
+// Plan_expr_column
+
+Plan_expr_column::~Plan_expr_column()
+{
+}
+
+Plan_base*
+Plan_expr_column::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ analyzeColumn(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ Plan_expr::m_sqlType = Plan_column::m_sqlType;
+ // depends on one table
+ m_tableSet.insert(m_resTable);
+ // not constant as set-value
+ ctl.m_const = false;
+ // set alias name
+ m_alias = m_name;
+ return this;
+}
+
+Exec_base*
+Plan_expr_column::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ // connect column to query column
+ const Exec_query* execQuery = ctl.m_execQuery;
+ ctx_assert(execQuery != 0);
+ const Exec_query::Code& codeQuery = execQuery->getCode();
+ const SqlSpec sqlSpec(Plan_expr::m_sqlType, SqlSpec::Reference);
+ // offset in final output row
+ ctx_assert(m_resTable != 0 && m_resTable->m_resOff != 0 && m_resPos != 0);
+ unsigned resOff = m_resTable->m_resOff + (m_resPos - 1);
+ // create the code
+ Exec_expr_column* exec = new Exec_expr_column(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ Exec_expr_column::Code& code = *new Exec_expr_column::Code(sqlSpec, resOff);
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+bool
+Plan_expr_column::resolveEq(Ctx& ctx, Plan_expr* expr)
+{
+ ctx_assert(m_resTable != 0 && expr != 0);
+ return m_resTable->resolveEq(ctx, this, expr);
+}
+
+void
+Plan_expr_column::print(Ctx& ctx)
+{
+ ctx.print(" [expr_column %s]", getPrintName());
+}
+
+bool
+Plan_expr_column::isEqual(const Plan_expr* expr) const
+{
+ ctx_assert(expr != 0);
+ if (expr->type() != Plan_expr::TypeColumn)
+ return false;
+ const Plan_expr_column* expr2 = static_cast<const Plan_expr_column*>(expr);
+ ctx_assert(m_resTable != 0 && expr2->m_resTable != 0);
+ if (m_resTable != expr2->m_resTable)
+ return false;
+ ctx_assert(m_dictColumn != 0 && expr2->m_dictColumn != 0);
+ if (m_dictColumn != expr2->m_dictColumn)
+ return false;
+ return true;
+}
+
+bool
+Plan_expr_column::isGroupBy(const Plan_expr_row* row) const
+{
+ if (isAnyEqual(row))
+ return true;
+ return false;
+}
+
+// Exec_expr_column
+
+Exec_expr_column::Code::~Code()
+{
+}
+
+Exec_expr_column::Data::~Data()
+{
+}
+
+Exec_expr_column::~Exec_expr_column()
+{
+}
+
+void
+Exec_expr_column::alloc(Ctx& ctx, Ctl& ctl)
+{
+ if (m_data != 0)
+ return;
+ const Code& code = getCode();
+ // connect column to query column
+ ctx_assert(ctl.m_query != 0);
+ const SqlRow& sqlRow = ctl.m_query->getData().sqlRow();
+ SqlField& sqlField = sqlRow.getEntry(code.m_resOff);
+ // create the data
+ Data& data = *new Data(sqlField);
+ setData(data);
+}
+
+void
+Exec_expr_column::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ if (ctl.m_groupIndex != 0) {
+ SqlField& out = data.groupField(code.sqlSpec().sqlType(), ctl.m_groupIndex, ctl.m_groupInit);
+ data.sqlField().copy(ctx, out);
+ }
+}
+
+void
+Exec_expr_column::close(Ctx& ctx)
+{
+ Data& data = getData();
+ data.m_groupField.clear();
+}
+
+void
+Exec_expr_column::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [column %d]", code.m_resOff);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.hpp
new file mode 100644
index 00000000000..2ce7c441e45
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.hpp
@@ -0,0 +1,120 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_column_hpp
+#define ODBC_CODEGEN_Code_expr_column_hpp
+
+#include <common/common.hpp>
+#include "Code_column.hpp"
+#include "Code_expr.hpp"
+
+class DictColumn;
+class Plan_table;
+
+/**
+ * @class Plan_expr_column
+ * @brief Column in query expression
+ */
+class Plan_expr_column : public Plan_expr, public Plan_column {
+public:
+ Plan_expr_column(Plan_root* root, const BaseString& name);
+ virtual ~Plan_expr_column();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ bool resolveEq(Ctx& ctx, Plan_expr* expr);
+ void print(Ctx& ctx);
+ bool isEqual(const Plan_expr* expr) const;
+ bool isGroupBy(const Plan_expr_row* row) const;
+};
+
+inline
+Plan_expr_column::Plan_expr_column(Plan_root* root, const BaseString& name) :
+ Plan_expr(root, TypeColumn),
+ Plan_column(Type_expr, name)
+{
+}
+
+/**
+ * @class Exec_expr_column
+ * @brief Column in query expression
+ */
+class Exec_expr_column : public Exec_expr {
+public:
+ class Code : public Exec_expr::Code {
+ public:
+ Code(const SqlSpec& sqlSpec, unsigned resOff);
+ virtual ~Code();
+ protected:
+ friend class Exec_expr_column;
+ SqlSpec m_sqlSpec;
+ unsigned m_resOff;
+ };
+ class Data : public Exec_expr::Data {
+ public:
+ Data(SqlField& sqlField);
+ virtual ~Data();
+ protected:
+ friend class Exec_expr_column;
+ // set reference to SqlField in query
+ };
+ Exec_expr_column(Exec_root* root);
+ virtual ~Exec_expr_column();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_expr_column::Code::Code(const SqlSpec& sqlSpec, unsigned resOff) :
+ Exec_expr::Code(m_sqlSpec),
+ m_sqlSpec(sqlSpec),
+ m_resOff(resOff)
+{
+}
+
+inline
+Exec_expr_column::Data::Data(SqlField& sqlField) :
+ Exec_expr::Data(sqlField)
+{
+}
+
+inline
+Exec_expr_column::Exec_expr_column(Exec_root* root) :
+ Exec_expr(root)
+{
+}
+
+// children
+
+inline const Exec_expr_column::Code&
+Exec_expr_column::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_column::Data&
+Exec_expr_column::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.cpp
new file mode 100644
index 00000000000..564d307a4f8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.cpp
@@ -0,0 +1,138 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_expr_const.hpp"
+#include "Code_root.hpp"
+
+// Plan_expr_const
+
+Plan_expr_const::~Plan_expr_const()
+{
+}
+
+Plan_base*
+Plan_expr_const::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ // convert data type
+ m_lexType.convert(ctx, m_sqlType, m_string.length());
+ if (! ctx.ok())
+ return 0;
+ // depends on no tables
+ // set alias name
+ m_alias = m_string;
+ // node was not changed
+ return this;
+}
+
+Exec_base*
+Plan_expr_const::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ // convert data
+ SqlSpec sqlSpec(m_sqlType, SqlSpec::Physical);
+ SqlField sqlField(sqlSpec);
+ LexSpec lexSpec(m_lexType);
+ lexSpec.convert(ctx, m_string, sqlField);
+ if (! ctx.ok())
+ return 0;
+ // create code
+ Exec_expr_const* exec = new Exec_expr_const(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ Exec_expr_const::Code& code = *new Exec_expr_const::Code(sqlField);
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_expr_const::print(Ctx& ctx)
+{
+ ctx.print(" [const %s]", m_string.c_str());
+}
+
+bool
+Plan_expr_const::isEqual(const Plan_expr* expr) const
+{
+ ctx_assert(expr != 0);
+ if (expr->type() != Plan_expr::TypeConst)
+ return false;
+ const Plan_expr_const* expr2 = static_cast<const Plan_expr_const*>(expr);
+ if (strcmp(m_string.c_str(), expr2->m_string.c_str()) != 0)
+ return false;
+ return true;
+}
+
+bool
+Plan_expr_const::isGroupBy(const Plan_expr_row* row) const
+{
+ return true;
+}
+
+// Exec_expr_const
+
+Exec_expr_const::Code::~Code()
+{
+}
+
+Exec_expr_const::Data::~Data()
+{
+}
+
+Exec_expr_const::~Exec_expr_const()
+{
+}
+
+void
+Exec_expr_const::alloc(Ctx& ctx, Ctl& ctl)
+{
+ if (m_data != 0)
+ return;
+ // copy the value for const correctness reasons
+ SqlField sqlField(getCode().m_sqlField);
+ Data& data = *new Data(sqlField);
+ setData(data);
+}
+
+void
+Exec_expr_const::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ if (ctl.m_groupIndex != 0) {
+ SqlField& out = data.groupField(code.sqlSpec().sqlType(), ctl.m_groupIndex, ctl.m_groupInit);
+ data.sqlField().copy(ctx, out);
+ }
+}
+
+void
+Exec_expr_const::close(Ctx& ctx)
+{
+ Data& data = getData();
+ data.m_groupField.clear();
+}
+
+void
+Exec_expr_const::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [");
+ char buf[500];
+ code.m_sqlField.print(buf, sizeof(buf));
+ ctx.print("%s", buf);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.hpp
new file mode 100644
index 00000000000..2e26c637a23
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.hpp
@@ -0,0 +1,120 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_const_hpp
+#define ODBC_CODEGEN_Code_expr_const_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_expr.hpp"
+
+/**
+ * @class Plan_expr_const
+ * @brief Constant expression value in PlanTree
+ */
+class Plan_expr_const : public Plan_expr {
+public:
+ Plan_expr_const(Plan_root* root, LexType lexType, const char* value);
+ virtual ~Plan_expr_const();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ bool isEqual(const Plan_expr* expr) const;
+ bool isGroupBy(const Plan_expr_row* row) const;
+protected:
+ // lexical type and token set by the parser
+ LexType m_lexType;
+ BaseString m_string;
+};
+
+inline
+Plan_expr_const::Plan_expr_const(Plan_root* root, LexType lexType, const char* value) :
+ Plan_expr(root, TypeConst),
+ m_lexType(lexType),
+ m_string(value)
+{
+}
+
+/**
+ * @class Exec_expr_const
+ * @brief Constant expression value in ExecTree
+ */
+class Exec_expr_const : public Exec_expr {
+public:
+ class Code : public Exec_expr::Code {
+ public:
+ Code(const SqlField& sqlField);
+ virtual ~Code();
+ protected:
+ friend class Exec_expr_const;
+ const SqlField m_sqlField;
+ };
+ class Data : public Exec_expr::Data {
+ public:
+ Data(SqlField& sqlField);
+ virtual ~Data();
+ protected:
+ friend class Exec_expr_const;
+ SqlField m_sqlField;
+ };
+ Exec_expr_const(Exec_root* root);
+ virtual ~Exec_expr_const();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_expr_const::Code::Code(const SqlField& sqlField) :
+ Exec_expr::Code(m_sqlField.sqlSpec()),
+ m_sqlField(sqlField)
+{
+}
+
+inline
+Exec_expr_const::Data::Data(SqlField& sqlField) :
+ Exec_expr::Data(m_sqlField),
+ m_sqlField(sqlField)
+{
+}
+
+inline
+Exec_expr_const::Exec_expr_const(Exec_root* root) :
+ Exec_expr(root)
+{
+}
+
+// children
+
+inline const Exec_expr_const::Code&
+Exec_expr_const::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_const::Data&
+Exec_expr_const::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.cpp
new file mode 100644
index 00000000000..bc89482fedc
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.cpp
@@ -0,0 +1,273 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_expr.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_root.hpp"
+
+// Plan_expr_conv
+
+Plan_expr_conv::~Plan_expr_conv()
+{
+}
+
+Plan_base*
+Plan_expr_conv::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ const SqlType& t1 = sqlType();
+ ctx_assert(m_expr != 0);
+ m_expr->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ const SqlType& t2 = m_expr->sqlType();
+ if (t2.type() == SqlType::Unbound) {
+ return m_expr;
+ }
+ if (t1.equal(t2)) {
+ return m_expr;
+ }
+ // XXX move to runtime or make table-driven
+ bool ok = false;
+ if (t2.type() == SqlType::Null) {
+ ok = true;
+ } else if (t1.type() == SqlType::Char) {
+ if (t2.type() == SqlType::Char) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varchar) {
+ ok = true;
+ } else if (t2.type() == SqlType::Binary) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varbinary) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Varchar) {
+ if (t2.type() == SqlType::Char) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varchar) {
+ ok = true;
+ } else if (t2.type() == SqlType::Binary) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varbinary) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Binary) {
+ if (t2.type() == SqlType::Char) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varchar) {
+ ok = true;
+ } else if (t2.type() == SqlType::Binary) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varbinary) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Varbinary) {
+ if (t2.type() == SqlType::Char) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varchar) {
+ ok = true;
+ } else if (t2.type() == SqlType::Binary) {
+ ok = true;
+ } else if (t2.type() == SqlType::Varbinary) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Smallint) {
+ if (t2.type() == SqlType::Smallint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Integer) {
+ ok = true;
+ } else if (t2.type() == SqlType::Bigint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Real) {
+ ok = true;
+ } else if (t2.type() == SqlType::Double) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Integer) {
+ if (t2.type() == SqlType::Smallint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Integer) {
+ ok = true;
+ } else if (t2.type() == SqlType::Bigint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Real) {
+ ok = true;
+ } else if (t2.type() == SqlType::Double) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Bigint) {
+ if (t2.type() == SqlType::Smallint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Integer) {
+ ok = true;
+ } else if (t2.type() == SqlType::Bigint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Real) {
+ ok = true;
+ } else if (t2.type() == SqlType::Double) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Real) {
+ if (t2.type() == SqlType::Smallint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Integer) {
+ ok = true;
+ } else if (t2.type() == SqlType::Bigint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Real) {
+ ok = true;
+ } else if (t2.type() == SqlType::Double) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Double) {
+ if (t2.type() == SqlType::Smallint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Integer) {
+ ok = true;
+ } else if (t2.type() == SqlType::Bigint) {
+ ok = true;
+ } else if (t2.type() == SqlType::Real) {
+ ok = true;
+ } else if (t2.type() == SqlType::Double) {
+ ok = true;
+ }
+ } else if (t1.type() == SqlType::Datetime) {
+ if (t2.type() == SqlType::Datetime) {
+ ok = true;
+ }
+ }
+ if (! ok) {
+ char b1[40], b2[40];
+ t1.print(b1, sizeof(b1));
+ t2.print(b2, sizeof(b2));
+ ctx.pushStatus(Error::Gen, "cannot convert %s to %s", b2, b1);
+ return 0;
+ }
+ // depend on same tables
+ const TableSet& ts = m_expr->tableSet();
+ m_tableSet.insert(ts.begin(), ts.end());
+ // set alias
+ m_alias = m_expr->getAlias();
+ return this;
+}
+
+Exec_base*
+Plan_expr_conv::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ Exec_expr_conv* exec = new Exec_expr_conv(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // create code for subexpression
+ ctx_assert(m_expr != 0);
+ Exec_expr* execExpr = static_cast<Exec_expr*>(m_expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ exec->setExpr(execExpr);
+ // create the code
+ SqlSpec sqlSpec(sqlType(), SqlSpec::Physical);
+ Exec_expr_conv::Code& code = *new Exec_expr_conv::Code(sqlSpec);
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_expr_conv::print(Ctx& ctx)
+{
+ ctx.print(" [expr_conv ");
+ char buf[100];
+ m_sqlType.print(buf, sizeof(buf));
+ ctx.print("%s", buf);
+ Plan_base* a[] = { m_expr };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+bool
+Plan_expr_conv::isEqual(const Plan_expr* expr) const
+{
+ ctx_assert(expr != 0);
+ if (expr->type() != Plan_expr::TypeConv)
+ return false;
+ const Plan_expr_conv* expr2 = static_cast<const Plan_expr_conv*>(expr);
+ if (! m_sqlType.equal(expr2->m_sqlType))
+ return false;
+ ctx_assert(m_expr != 0 && expr2->m_expr != 0);
+ if (! m_expr->isEqual(expr2->m_expr))
+ return false;
+ return true;
+}
+
+bool
+Plan_expr_conv::isGroupBy(const Plan_expr_row* row) const
+{
+ if (isAnyEqual(row))
+ return true;
+ ctx_assert(m_expr != 0);
+ if (m_expr->isGroupBy(row))
+ return true;
+ return false;
+}
+
+// Code_expr_conv
+
+Exec_expr_conv::Code::~Code()
+{
+}
+
+Exec_expr_conv::Data::~Data()
+{
+}
+
+Exec_expr_conv::~Exec_expr_conv()
+{
+}
+
+void
+Exec_expr_conv::alloc(Ctx& ctx, Ctl& ctl)
+{
+ if (m_data != 0)
+ return;
+ const Code& code = getCode();
+ // allocate subexpression
+ ctx_assert(m_expr != 0);
+ m_expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ SqlField sqlField(code.m_sqlSpec);
+ Data& data = *new Data(sqlField);
+ setData(data);
+}
+
+void
+Exec_expr_conv::close(Ctx& ctx)
+{
+ ctx_assert(m_expr != 0);
+ m_expr->close(ctx);
+ Data& data = getData();
+ data.m_groupField.clear();
+}
+
+void
+Exec_expr_conv::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [expr_conv");
+ Exec_base* a[] = { m_expr };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.hpp
new file mode 100644
index 00000000000..3294960c7b3
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.hpp
@@ -0,0 +1,141 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_conv_hpp
+#define ODBC_CODEGEN_Code_expr_conv_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_expr.hpp"
+
+/**
+ * @class Plan_expr_conv
+ * @brief Data type conversion in PlanTree
+ *
+ * Inserted to convert value to another compatible type.
+ */
+class Plan_expr_conv : public Plan_expr {
+public:
+ Plan_expr_conv(Plan_root* root, const SqlType& sqlType);
+ virtual ~Plan_expr_conv();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ bool isEqual(const Plan_expr* expr) const;
+ bool isGroupBy(const Plan_expr_row* row) const;
+ // children
+ void setExpr(Plan_expr* expr);
+protected:
+ Plan_expr* m_expr;
+};
+
+inline
+Plan_expr_conv::Plan_expr_conv(Plan_root* root, const SqlType& sqlType) :
+ Plan_expr(root, TypeConv),
+ m_expr(0)
+{
+ ctx_assert(sqlType.type() != SqlType::Undef);
+ m_sqlType = sqlType;
+}
+
+inline void
+Plan_expr_conv::setExpr(Plan_expr* expr)
+{
+ ctx_assert(expr != 0);
+ m_expr = expr;
+}
+
+/**
+ * @class Exec_expr_conv
+ * @brief Data type conversion in ExecTree
+ */
+class Exec_expr_conv : public Exec_expr {
+public:
+ class Code : public Exec_expr::Code {
+ public:
+ Code(const SqlSpec& spec);
+ virtual ~Code();
+ protected:
+ friend class Exec_expr_conv;
+ const SqlSpec m_sqlSpec;
+ };
+ class Data : public Exec_expr::Data {
+ public:
+ Data(const SqlField& sqlField);
+ virtual ~Data();
+ protected:
+ friend class Exec_expr_conv;
+ SqlField m_sqlField;
+ };
+ Exec_expr_conv(Exec_root* root);
+ virtual ~Exec_expr_conv();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setExpr(Exec_expr* expr);
+protected:
+ Exec_expr* m_expr;
+};
+
+inline
+Exec_expr_conv::Code::Code(const SqlSpec& sqlSpec) :
+ Exec_expr::Code(m_sqlSpec),
+ m_sqlSpec(sqlSpec)
+{
+}
+
+inline
+Exec_expr_conv::Data::Data(const SqlField& sqlField) :
+ Exec_expr::Data(m_sqlField),
+ m_sqlField(sqlField)
+{
+}
+
+inline
+Exec_expr_conv::Exec_expr_conv(Exec_root* root) :
+ Exec_expr(root),
+ m_expr(0)
+{
+}
+
+// children
+
+inline const Exec_expr_conv::Code&
+Exec_expr_conv::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_conv::Data&
+Exec_expr_conv::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_expr_conv::setExpr(Exec_expr* expr)
+{
+ ctx_assert(m_expr == 0 && expr != 0);
+ m_expr = expr;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.cpp
new file mode 100644
index 00000000000..96b461a72d9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.cpp
@@ -0,0 +1,401 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_expr.hpp"
+#include "Code_expr_func.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_root.hpp"
+#include "PortDefs.h"
+
+
+// Expr_func
+
+static const struct { const char* alias; const char* name; }
+expr_func_alias[] = {
+ { "SUBSTRING", "SUBSTR" },
+ { 0, 0 }
+};
+
+static const Expr_func
+expr_func[] = {
+ Expr_func(Expr_func::Substr, "SUBSTR", false ),
+ Expr_func(Expr_func::Left, "LEFT", false ),
+ Expr_func(Expr_func::Right, "RIGHT", false ),
+ Expr_func(Expr_func::Count, "COUNT", true ),
+ Expr_func(Expr_func::Max, "MAX", true ),
+ Expr_func(Expr_func::Min, "MIN", true ),
+ Expr_func(Expr_func::Sum, "SUM", true ),
+ Expr_func(Expr_func::Avg, "AVG", true ),
+ Expr_func(Expr_func::Rownum, "ROWNUM", false ),
+ Expr_func(Expr_func::Sysdate, "SYSDATE", false ),
+ Expr_func(Expr_func::Undef, 0, false )
+};
+
+const Expr_func&
+Expr_func::find(const char* name)
+{
+ for (unsigned i = 0; expr_func_alias[i].alias != 0; i++) {
+ if (strcasecmp(expr_func_alias[i].alias, name) == 0) {
+ name = expr_func_alias[i].name;
+ break;
+ }
+ }
+ const Expr_func* p;
+ for (p = expr_func; p->m_name != 0; p++) {
+ if (strcasecmp(p->m_name, name) == 0)
+ break;
+ }
+ return *p;
+}
+
+// Plan_expr_func
+
+Plan_expr_func::~Plan_expr_func()
+{
+ delete[] m_conv;
+ m_conv = 0;
+}
+
+Plan_base*
+Plan_expr_func::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ ctx_assert(m_narg == 0 || m_args != 0);
+ // aggregate check
+ if (m_func.m_aggr) {
+ if (! ctl.m_aggrok) {
+ ctx.pushStatus(Error::Gen, "%s: invalid use of aggregate function", m_func.m_name);
+ return 0;
+ }
+ if (ctl.m_aggrin) {
+ // XXX actually possible with group-by but too hard
+ ctx.pushStatus(Error::Gen, "%s: nested aggregate function", m_func.m_name);
+ return 0;
+ }
+ ctl.m_aggrin = true;
+ m_isAggr = true;
+ m_isBound = true;
+ }
+ // analyze argument expressions
+ if (m_func.m_code != Expr_func::Rownum)
+ m_isBound = true;
+ for (unsigned i = 1; i <= m_narg; i++) {
+ Plan_expr* expr = m_args->getExpr(i);
+ expr = static_cast<Plan_expr*>(expr->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(expr != 0);
+ if (expr->m_isAggr)
+ m_isAggr = true;
+ if (! m_func.m_aggr && ! expr->m_isBound)
+ m_isBound = false;
+ }
+ if (m_func.m_aggr)
+ ctl.m_aggrin = false;
+ // find result type and conversion types
+ SqlType res;
+ const Expr_func::Code fc = m_func.m_code;
+ const char* const invalidArgCount = "%s: argument count %u is invalid";
+ const char* const invalidArgType = "%s: argument %u has invalid type";
+ if (fc == Expr_func::Substr || fc == Expr_func::Left || fc == Expr_func::Right) {
+ if ((m_narg != (unsigned)2) && (m_narg != (unsigned)(fc == Expr_func::Substr ? 3 : 2))) {
+ ctx.pushStatus(Error::Gen, invalidArgCount, m_func.m_name, m_narg);
+ return 0;
+ }
+ const SqlType& t1 = m_args->getExpr(1)->sqlType();
+ switch (t1.type()) {
+ case SqlType::Char:
+ {
+ // XXX convert to varchar for now to get length right
+ SqlType tx(SqlType::Varchar, t1.length());
+ res = m_conv[1] = tx;
+ }
+ break;
+ case SqlType::Varchar:
+ case SqlType::Unbound:
+ res = m_conv[1] = t1;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, invalidArgType, m_func.m_name, 1);
+ return 0;
+ }
+ for (unsigned i = 2; i <= m_narg; i++) {
+ const SqlType& t2 = m_args->getExpr(i)->sqlType();
+ switch (t2.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ case SqlType::Unbound:
+ m_conv[i] = t2;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, invalidArgType, m_func.m_name, i);
+ return 0;
+ }
+ }
+ } else if (fc == Expr_func::Count) {
+ ctx_assert(m_args != 0);
+ if (m_args->getAsterisk()) {
+ ctx_assert(m_narg == 0);
+ } else {
+ if (m_narg != 1) {
+ ctx.pushStatus(Error::Gen, invalidArgCount, m_func.m_name, m_narg);
+ return 0;
+ }
+ m_conv[1] = m_args->getExpr(1)->sqlType();
+ }
+ res.setType(ctx, SqlType::Bigint);
+ } else if (fc == Expr_func::Min || fc == Expr_func::Max) {
+ if (m_narg != 1) {
+ ctx.pushStatus(Error::Gen, invalidArgCount, m_func.m_name, m_narg);
+ return 0;
+ }
+ const SqlType& t1 = m_args->getExpr(1)->sqlType();
+ res = m_conv[1] = t1;
+ } else if (fc == Expr_func::Sum) {
+ if (m_narg != 1) {
+ ctx.pushStatus(Error::Gen, invalidArgCount, m_func.m_name, m_narg);
+ return 0;
+ }
+ const SqlType& t1 = m_args->getExpr(1)->sqlType();
+ switch (t1.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ res.setType(ctx, SqlType::Bigint);
+ m_conv[1] = res;
+ break;
+ case SqlType::Real:
+ case SqlType::Double:
+ res.setType(ctx, SqlType::Double);
+ m_conv[1] = res;
+ break;
+ case SqlType::Unbound:
+ res = m_conv[1] = t1;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, invalidArgType, m_func.m_name, 1);
+ return 0;
+ }
+ } else if (fc == Expr_func::Avg) {
+ if (m_narg != 1) {
+ ctx.pushStatus(Error::Gen, invalidArgCount, m_func.m_name, m_narg);
+ return 0;
+ }
+ const SqlType& t1 = m_args->getExpr(1)->sqlType();
+ switch (t1.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ case SqlType::Real:
+ case SqlType::Double:
+ res.setType(ctx, SqlType::Double);
+ m_conv[1] = res;
+ break;
+ case SqlType::Unbound:
+ res = m_conv[1] = t1;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, invalidArgType, m_func.m_name, 1);
+ return 0;
+ }
+ } else if (fc == Expr_func::Rownum) {
+ ctx_assert(m_narg == 0 && m_args == 0);
+ res.setType(ctx, SqlType::Bigint);
+ } else if (fc == Expr_func::Sysdate) {
+ ctx_assert(m_narg == 0 && m_args == 0);
+ res.setType(ctx, SqlType::Datetime);
+ } else {
+ ctx_assert(false);
+ }
+ // insert required conversions
+ for (unsigned i = 1; i <= m_narg; i++) {
+ if (m_conv[i].type() == SqlType::Unbound) {
+ // parameter type not yet bound
+ continue;
+ }
+ Plan_expr_conv* exprConv = new Plan_expr_conv(m_root, m_conv[i]);
+ m_root->saveNode(exprConv);
+ exprConv->setExpr(m_args->getExpr(i));
+ Plan_expr* expr = static_cast<Plan_expr*>(exprConv->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ m_args->setExpr(i, expr);
+ }
+ // set result type
+ m_sqlType = res;
+ // set table dependencies
+ for (unsigned i = 1; i <= m_narg; i++) {
+ const TableSet& ts = m_args->getExpr(i)->tableSet();
+ m_tableSet.insert(ts.begin(), ts.end());
+ }
+ // set alias name
+ m_alias.assign(m_func.m_name);
+ if (m_narg == 0) {
+ if (fc == Expr_func::Count)
+ m_alias.append("(*)");
+ } else {
+ m_alias.append("(");
+ for (unsigned i = 1; i <= m_narg; i++) {
+ if (i > 1)
+ m_alias.append(",");
+ m_alias.append(m_args->getExpr(i)->getAlias());
+ }
+ m_alias.append(")");
+ }
+ return this;
+}
+
+Exec_base*
+Plan_expr_func::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ Exec_expr_func* exec = new Exec_expr_func(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ SqlSpec sqlSpec(sqlType(), SqlSpec::Physical);
+ Exec_expr_func::Code& code = *new Exec_expr_func::Code(m_func, sqlSpec);
+ exec->setCode(code);
+ code.m_narg = m_narg;
+ code.m_args = new Exec_expr* [1 + m_narg];
+ for (unsigned i = 0; i <= m_narg; i++)
+ code.m_args[i] = 0;
+ // create code for arguments
+ for (unsigned i = 1; i <= m_narg; i++) {
+ Plan_expr* expr = m_args->getExpr(i);
+ ctx_assert(expr != 0);
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_args[i] = execExpr;
+ }
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_expr_func::print(Ctx& ctx)
+{
+ ctx.print(" [%s", m_func.m_name);
+ Plan_base* a[] = { m_args };
+ printList(ctx, a, sizeof(a)/sizeof(a[1]));
+ ctx.print("]");
+}
+
+bool
+Plan_expr_func::isEqual(const Plan_expr* expr) const
+{
+ ctx_assert(expr != 0);
+ if (expr->type() != Plan_expr::TypeFunc)
+ return false;
+ const Plan_expr_func* expr2 = static_cast<const Plan_expr_func*>(expr);
+ if (m_func.m_code != expr2->m_func.m_code)
+ return false;
+ if (m_narg != expr2->m_narg)
+ return false;
+ ctx_assert(m_args != 0 && expr2->m_args != 0);
+ for (unsigned i = 1; i <= m_narg; i++) {
+ if (! m_args->getExpr(i)->isEqual(expr2->m_args->getExpr(i)))
+ return false;
+ }
+ return true;
+}
+
+bool
+Plan_expr_func::isGroupBy(const Plan_expr_row* row) const
+{
+ if (m_func.m_aggr)
+ return true;
+ switch (m_func.m_code) {
+ case Expr_func::Substr:
+ case Expr_func::Left:
+ case Expr_func::Right:
+ ctx_assert(m_narg >= 1);
+ if (m_args->getExpr(1)->isGroupBy(row))
+ return true;
+ break;
+ case Expr_func::Sysdate:
+ return true;
+ default:
+ break;
+ }
+ if (isAnyEqual(row))
+ return true;
+ return false;
+}
+
+// Exec_expr_func
+
+Exec_expr_func::Code::~Code()
+{
+ delete[] m_args;
+ m_args = 0;
+}
+
+Exec_expr_func::Data::~Data()
+{
+}
+
+Exec_expr_func::~Exec_expr_func()
+{
+}
+
+void
+Exec_expr_func::alloc(Ctx& ctx, Ctl& ctl)
+{
+ if (m_data != 0)
+ return;
+ const Code& code = getCode();
+ // allocate arguments
+ for (unsigned i = 1; i <= code.m_narg; i++) {
+ ctx_assert(code.m_args != 0 && code.m_args[i] != 0);
+ code.m_args[i]->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ SqlField sqlField(code.m_sqlSpec);
+ Data& data = *new Data(sqlField);
+ setData(data);
+ ctx_assert(ctl.m_groupIndex == 0);
+ init(ctx, ctl);
+}
+
+void
+Exec_expr_func::close(Ctx& ctx)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ for (unsigned i = 1; i <= code.m_narg; i++) {
+ ctx_assert(code.m_args != 0 && code.m_args[i] != 0);
+ code.m_args[i]->close(ctx);
+ }
+ data.m_groupField.clear();
+ Ctl ctl(0);
+ init(ctx, ctl);
+}
+
+void
+Exec_expr_func::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [%s", code.m_func.m_name);
+ for (unsigned i = 1; i <= code.m_narg; i++) {
+ Exec_base* a[] = { code.m_args[i] };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.hpp
new file mode 100644
index 00000000000..856d7529875
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.hpp
@@ -0,0 +1,193 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_func_hpp
+#define ODBC_CODEGEN_Code_expr_func_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_expr.hpp"
+#include "Code_expr_row.hpp"
+
+/**
+ * @class Expr_func
+ * @brief Specifies a function
+ */
+struct Expr_func {
+ enum Code {
+ Undef = 0,
+ Substr,
+ Left,
+ Right,
+ Count,
+ Max,
+ Min,
+ Sum,
+ Avg,
+ Rownum,
+ Sysdate
+ };
+ Expr_func(Code code, const char* name, bool aggr);
+ Code m_code;
+ const char* m_name;
+ bool m_aggr;
+ static const Expr_func& find(const char* name);
+};
+
+inline
+Expr_func::Expr_func(Code code, const char* name, bool aggr) :
+ m_code(code),
+ m_name(name),
+ m_aggr(aggr)
+{
+}
+
+/**
+ * @class Plan_expr_func
+ * @brief Function node in an expression in PlanTree
+ */
+class Plan_expr_func : public Plan_expr {
+public:
+ Plan_expr_func(Plan_root* root, const Expr_func& func);
+ virtual ~Plan_expr_func();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ bool isEqual(const Plan_expr* expr) const;
+ bool isGroupBy(const Plan_expr_row* row) const;
+ // children
+ void setArgs(Plan_expr_row* args);
+protected:
+ const Expr_func& m_func;
+ Plan_expr_row* m_args;
+ unsigned m_narg;
+ SqlType* m_conv; // temp work area
+};
+
+inline
+Plan_expr_func::Plan_expr_func(Plan_root* root, const Expr_func& func) :
+ Plan_expr(root, TypeFunc),
+ m_func(func),
+ m_args(0),
+ m_narg(0),
+ m_conv(0)
+{
+}
+
+inline void
+Plan_expr_func::setArgs(Plan_expr_row* args)
+{
+ if (args == 0) {
+ m_args = 0;
+ m_narg = 0;
+ } else {
+ m_args = args;
+ m_narg = m_args->getSize();
+ delete[] m_conv;
+ m_conv = new SqlType[1 + m_narg];
+ }
+}
+
+/**
+ * @class Exec_expr_func
+ * @brief Function node in an expression in ExecTree
+ */
+class Exec_expr_func : public Exec_expr {
+public:
+ class Code : public Exec_expr::Code {
+ public:
+ Code(const Expr_func& func, const SqlSpec& spec);
+ virtual ~Code();
+ protected:
+ friend class Plan_expr_func;
+ friend class Exec_expr_func;
+ const Expr_func& m_func;
+ const SqlSpec m_sqlSpec;
+ unsigned m_narg;
+ Exec_expr** m_args; // XXX pointers for now
+ };
+ class Data : public Exec_expr::Data {
+ public:
+ Data(const SqlField& sqlField);
+ virtual ~Data();
+ protected:
+ friend class Exec_expr_func;
+ SqlField m_sqlField;
+ struct Acc { // accumulators etc
+ SqlBigint m_count; // current row count
+ union {
+ SqlBigint m_bigint;
+ SqlDouble m_double;
+ SqlDatetime m_sysdate;
+ };
+ };
+ // group-by extra accumulators (default in entry 0)
+ typedef std::vector<Acc> GroupAcc;
+ GroupAcc m_groupAcc;
+ };
+ Exec_expr_func(Exec_root* root);
+ virtual ~Exec_expr_func();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+protected:
+ void init(Ctx &ctx, Ctl& ctl); // initialize values
+};
+
+inline
+Exec_expr_func::Code::Code(const Expr_func& func, const SqlSpec& sqlSpec) :
+ Exec_expr::Code(m_sqlSpec),
+ m_func(func),
+ m_sqlSpec(sqlSpec),
+ m_args(0)
+{
+}
+
+inline
+Exec_expr_func::Data::Data(const SqlField& sqlField) :
+ Exec_expr::Data(m_sqlField),
+ m_sqlField(sqlField),
+ m_groupAcc(1)
+{
+}
+
+inline
+Exec_expr_func::Exec_expr_func(Exec_root* root) :
+ Exec_expr(root)
+{
+}
+
+// children
+
+inline const Exec_expr_func::Code&
+Exec_expr_func::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_func::Data&
+Exec_expr_func::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.cpp
new file mode 100644
index 00000000000..7e8314c1741
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.cpp
@@ -0,0 +1,424 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_expr.hpp"
+#include "Code_expr_op.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_root.hpp"
+
+// Expr_op
+
+const char*
+Expr_op::name() const
+{
+ switch (m_opcode) {
+ case Add:
+ return "+";
+ case Subtract:
+ return "-";
+ case Multiply:
+ return "*";
+ case Divide:
+ return "/";
+ case Plus:
+ return "+";
+ case Minus:
+ return "-";
+ }
+ ctx_assert(false);
+ return "";
+}
+
+unsigned
+Expr_op::arity() const
+{
+ switch (m_opcode) {
+ case Add:
+ case Subtract:
+ case Multiply:
+ case Divide:
+ return 2;
+ case Plus:
+ case Minus:
+ return 1;
+ }
+ ctx_assert(false);
+ return 0;
+}
+
+// Plan_expr_op
+
+Plan_expr_op::~Plan_expr_op()
+{
+}
+
+Plan_base*
+Plan_expr_op::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ unsigned arity = m_op.arity();
+ // analyze operands
+ m_isAggr = false;
+ m_isBound = true;
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ m_expr[i]->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ if (m_expr[i]->m_isAggr)
+ m_isAggr = true;
+ if (! m_expr[i]->m_isBound)
+ m_isBound = false;
+ }
+ // find result type and conversion types (currently same)
+ SqlType res;
+ SqlType con[1 + 2];
+ if (arity == 1) {
+ const SqlType& t1 = m_expr[1]->sqlType();
+ switch (t1.type()) {
+ case SqlType::Char:
+ case SqlType::Varchar:
+ break;
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ res.setType(ctx, SqlType::Bigint);
+ con[1] = res;
+ break;
+ case SqlType::Real:
+ case SqlType::Double:
+ res.setType(ctx, SqlType::Double);
+ con[1] = res;
+ break;
+ case SqlType::Null:
+ res.setType(ctx, SqlType::Null);
+ con[1] = res;
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = res;
+ default:
+ break;
+ }
+ if (con[1].type() == SqlType::Undef) {
+ char b1[40];
+ t1.print(b1, sizeof(b1));
+ ctx.pushStatus(Error::Gen, "type mismatch in operation: %s %s", m_op.name(), b1);
+ return 0;
+ }
+ } else if (arity == 2) {
+ const SqlType& t1 = m_expr[1]->sqlType();
+ const SqlType& t2 = m_expr[2]->sqlType();
+ switch (t1.type()) {
+ case SqlType::Char: // handle char types as in oracle
+ switch (t2.type()) {
+ case SqlType::Char:
+ res.setType(ctx, SqlType::Char, t1.length() + t2.length());
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Varchar:
+ res.setType(ctx, SqlType::Varchar, t1.length() + t2.length());
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Null:
+ res.setType(ctx, SqlType::Varchar, t1.length());
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = con[2] = res;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Varchar:
+ switch (t2.type()) {
+ case SqlType::Char:
+ res.setType(ctx, SqlType::Varchar, t1.length() + t2.length());
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Varchar:
+ res.setType(ctx, SqlType::Varchar, t1.length() + t2.length());
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Null:
+ res.setType(ctx, SqlType::Varchar, t1.length());
+ con[1] = t1;
+ con[2] = t2;
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = con[2] = res;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ switch (t2.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ res.setType(ctx, SqlType::Bigint);
+ con[1] = con[2] = res;
+ if (t1.unSigned() || t2.unSigned()) {
+ con[1].unSigned(true);
+ con[2].unSigned(true);
+ }
+ break;
+ case SqlType::Real:
+ case SqlType::Double:
+ res.setType(ctx, SqlType::Double);
+ con[1] = con[2] = res;
+ break;
+ case SqlType::Null:
+ res.setType(ctx, SqlType::Null);
+ con[1] = con[2] = res;
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = con[2] = res;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Real:
+ case SqlType::Double:
+ switch (t2.type()) {
+ case SqlType::Smallint:
+ case SqlType::Integer:
+ case SqlType::Bigint:
+ case SqlType::Real:
+ case SqlType::Double:
+ res.setType(ctx, SqlType::Double);
+ con[1] = con[2] = res;
+ break;
+ case SqlType::Null:
+ res.setType(ctx, SqlType::Null);
+ con[1] = con[2] = res;
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = con[2] = res;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SqlType::Null:
+ switch (t2.type()) {
+ case SqlType::Char:
+ case SqlType::Varchar:
+ res.setType(ctx, SqlType::Varchar, t2.length());
+ con[1] = con[2] = res;
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = con[2] = res;
+ break;
+ default:
+ res.setType(ctx, SqlType::Null);
+ con[1] = con[2] = res;
+ break;
+ }
+ break;
+ case SqlType::Unbound:
+ res.setType(ctx, SqlType::Unbound);
+ con[1] = con[2] = res;
+ break;
+ default:
+ break;
+ }
+ if (con[1].type() == SqlType::Undef || con[2].type() == SqlType::Undef) {
+ char b1[40], b2[40];
+ t1.print(b1, sizeof(b1));
+ t2.print(b2, sizeof(b2));
+ ctx.pushStatus(Error::Gen, "type mismatch in operation: %s %s %s", b1, m_op.name(), b2);
+ return 0;
+ }
+ } else {
+ ctx_assert(false);
+ return 0;
+ }
+ if (! ctx.ok())
+ return 0;
+ // insert required conversions
+ for (unsigned i = 1; i <= arity; i++) {
+ if (con[i].type() == SqlType::Undef) {
+ ctx.pushStatus(Error::Gen, "mismatched types in operation");
+ return 0;
+ }
+ if (con[i].type() == SqlType::Unbound) {
+ // parameter type not yet bound
+ continue;
+ }
+ Plan_expr_conv* exprConv = new Plan_expr_conv(m_root, con[i]);
+ m_root->saveNode(exprConv);
+ exprConv->setExpr(m_expr[i]);
+ m_expr[i] = static_cast<Plan_expr*>(exprConv->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_expr[i] != 0);
+ }
+ // set result type
+ m_sqlType = res;
+ // table dependencies are union from operands
+ for (unsigned i = 1; i <= arity; i++) {
+ const TableSet& ts = m_expr[i]->tableSet();
+ m_tableSet.insert(ts.begin(), ts.end());
+ }
+ // set alias name XXX misses operator precedence
+ if (arity == 1) {
+ m_alias.assign(m_op.name());
+ m_alias.append(m_expr[1]->m_alias);
+ } else if (arity == 2) {
+ m_alias.assign(m_expr[1]->m_alias);
+ m_alias.append(m_op.name());
+ m_alias.append(m_expr[2]->m_alias);
+ }
+ return this;
+}
+
+Exec_base*
+Plan_expr_op::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ unsigned arity = m_op.arity();
+ Exec_expr_op* exec = new Exec_expr_op(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // create code for operands
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ Exec_expr* execExpr = static_cast<Exec_expr*>(m_expr[i]->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ exec->setExpr(i, execExpr);
+ }
+ // create the code
+ SqlSpec sqlSpec(sqlType(), SqlSpec::Physical);
+ Exec_expr_op::Code& code = *new Exec_expr_op::Code(m_op, sqlSpec);
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_expr_op::print(Ctx& ctx)
+{
+ ctx.print(" [%s", m_op.name());
+ Plan_base* a[] = { m_expr[1], m_expr[2] };
+ printList(ctx, a, m_op.arity());
+ ctx.print("]");
+}
+
+bool
+Plan_expr_op::isEqual(const Plan_expr* expr) const
+{
+ ctx_assert(expr != 0);
+ if (expr->type() != Plan_expr::TypeOp)
+ return false;
+ const Plan_expr_op* expr2 = static_cast<const Plan_expr_op*>(expr);
+ if (m_op.m_opcode != expr2->m_op.m_opcode)
+ return false;
+ const unsigned arity = m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ if (! m_expr[i]->isEqual(expr2->m_expr[i]))
+ return false;
+ }
+ return true;
+}
+
+bool
+Plan_expr_op::isGroupBy(const Plan_expr_row* row) const
+{
+ if (isAnyEqual(row))
+ return true;
+ const unsigned arity = m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ if (! m_expr[i]->isGroupBy(row))
+ return false;
+ }
+ return true;
+}
+
+// Code_expr_op
+
+Exec_expr_op::Code::~Code()
+{
+}
+
+Exec_expr_op::Data::~Data()
+{
+}
+
+Exec_expr_op::~Exec_expr_op()
+{
+}
+
+void
+Exec_expr_op::alloc(Ctx& ctx, Ctl& ctl)
+{
+ if (m_data != 0)
+ return;
+ const Code& code = getCode();
+ // allocate subexpressions
+ unsigned arity = code.m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ m_expr[i]->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ SqlField sqlField(code.m_sqlSpec);
+ Data& data = *new Data(sqlField);
+ setData(data);
+}
+
+void
+Exec_expr_op::close(Ctx& ctx)
+{
+ const Code& code = getCode();
+ unsigned arity = code.m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_expr[i] != 0);
+ m_expr[i]->close(ctx);
+ }
+ Data& data = getData();
+ data.m_groupField.clear();
+}
+
+void
+Exec_expr_op::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [%s", code.m_op.name());
+ Exec_base* a[] = { m_expr[1], m_expr[2] };
+ printList(ctx, a, code.m_op.arity());
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.hpp
new file mode 100644
index 00000000000..f9686cad151
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.hpp
@@ -0,0 +1,166 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_op_hpp
+#define ODBC_CODEGEN_Code_expr_op_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_expr.hpp"
+
+/**
+ * @class Expr_op
+ * @brief Arithmetic and string operators
+ */
+struct Expr_op {
+ enum Opcode {
+ Add = 1, // binary
+ Subtract,
+ Multiply,
+ Divide,
+ Plus, // unary
+ Minus
+ };
+ Expr_op(Opcode opcode);
+ const char* name() const;
+ unsigned arity() const;
+ Opcode m_opcode;
+};
+
+inline
+Expr_op::Expr_op(Opcode opcode) :
+ m_opcode(opcode)
+{
+}
+
+/**
+ * @class Plan_expr_op
+ * @brief Operator node in an expression in PlanTree
+ */
+class Plan_expr_op : public Plan_expr {
+public:
+ Plan_expr_op(Plan_root* root, Expr_op op);
+ virtual ~Plan_expr_op();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ bool isEqual(const Plan_expr* expr) const;
+ bool isGroupBy(const Plan_expr_row* row) const;
+ // children
+ void setExpr(unsigned i, Plan_expr* expr);
+protected:
+ Expr_op m_op;
+ Plan_expr* m_expr[1 + 2];
+};
+
+inline
+Plan_expr_op::Plan_expr_op(Plan_root* root, Expr_op op) :
+ Plan_expr(root, TypeOp),
+ m_op(op)
+{
+ m_expr[0] = m_expr[1] = m_expr[2] = 0;
+}
+
+inline void
+Plan_expr_op::setExpr(unsigned i, Plan_expr* expr)
+{
+ ctx_assert(1 <= i && i <= 2 && expr != 0);
+ m_expr[i] = expr;
+}
+
+/**
+ * @class Exec_expr_op
+ * @brief Operator node in an expression in ExecTree
+ */
+class Exec_expr_op : public Exec_expr {
+public:
+ class Code : public Exec_expr::Code {
+ public:
+ Code(Expr_op op, const SqlSpec& spec);
+ virtual ~Code();
+ protected:
+ friend class Exec_expr_op;
+ Expr_op m_op;
+ const SqlSpec m_sqlSpec;
+ };
+ class Data : public Exec_expr::Data {
+ public:
+ Data(const SqlField& sqlField);
+ virtual ~Data();
+ protected:
+ friend class Exec_expr_op;
+ SqlField m_sqlField;
+ };
+ Exec_expr_op(Exec_root* root);
+ virtual ~Exec_expr_op();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setExpr(unsigned i, Exec_expr* expr);
+protected:
+ Exec_expr* m_expr[1 + 2];
+};
+
+inline
+Exec_expr_op::Code::Code(Expr_op op, const SqlSpec& sqlSpec) :
+ Exec_expr::Code(m_sqlSpec),
+ m_op(op),
+ m_sqlSpec(sqlSpec)
+{
+}
+
+inline
+Exec_expr_op::Data::Data(const SqlField& sqlField) :
+ Exec_expr::Data(m_sqlField),
+ m_sqlField(sqlField)
+{
+}
+
+inline
+Exec_expr_op::Exec_expr_op(Exec_root* root) :
+ Exec_expr(root)
+{
+ m_expr[0] = m_expr[1] = m_expr[2] = 0;
+}
+
+// children
+
+inline const Exec_expr_op::Code&
+Exec_expr_op::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_op::Data&
+Exec_expr_op::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_expr_op::setExpr(unsigned i, Exec_expr* expr)
+{
+ ctx_assert(1 <= i && i <= 2 && m_expr[i] == 0);
+ m_expr[i] = expr;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.cpp
new file mode 100644
index 00000000000..93892cae5e6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.cpp
@@ -0,0 +1,279 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_expr_param.hpp"
+#include "Code_root.hpp"
+
+// Plan_expr_param
+
+Plan_expr_param::~Plan_expr_param()
+{
+}
+
+Plan_base*
+Plan_expr_param::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ ctx_assert(m_paramNumber != 0);
+ ctx_assert(m_paramNumber < m_root->m_paramList.size());
+ m_root->m_paramList[m_paramNumber] = this;
+ m_sqlType.setType(ctx, SqlType::Unbound);
+ // check if type is bound now
+ DescArea& ipd = descArea(Desc_usage_IPD);
+ if (m_paramNumber <= ipd.getCount()) {
+ DescRec& rec = ipd.getRecord(m_paramNumber);
+ OdbcData descData;
+ rec.getField(ctx, SQL_DESC_TYPE, descData);
+ if (descData.type() != OdbcData::Undef) {
+ SQLSMALLINT desc_TYPE = descData.smallint();
+ // XXX wrong but fixes sun.jdbc.odbc
+ if (desc_TYPE == SQL_CHAR)
+ desc_TYPE = SQL_VARCHAR;
+ if (desc_TYPE == SQL_CHAR) {
+ rec.getField(ctx, SQL_DESC_LENGTH, descData);
+ if (descData.type() != OdbcData::Undef) {
+ unsigned desc_LENGTH = descData.uinteger();
+ m_sqlType.setType(ctx, SqlType::Char, desc_LENGTH);
+ }
+ } else if (desc_TYPE == SQL_VARCHAR) {
+ rec.getField(ctx, SQL_DESC_LENGTH, descData);
+ if (descData.type() != OdbcData::Undef) {
+ unsigned desc_LENGTH = descData.uinteger();
+ m_sqlType.setType(ctx, SqlType::Varchar, desc_LENGTH);
+ }
+ } else if (desc_TYPE == SQL_BINARY) {
+ rec.getField(ctx, SQL_DESC_LENGTH, descData);
+ if (descData.type() != OdbcData::Undef) {
+ unsigned desc_LENGTH = descData.uinteger();
+ m_sqlType.setType(ctx, SqlType::Binary, desc_LENGTH);
+ }
+ } else if (desc_TYPE == SQL_VARBINARY) {
+ rec.getField(ctx, SQL_DESC_LENGTH, descData);
+ if (descData.type() != OdbcData::Undef) {
+ unsigned desc_LENGTH = descData.uinteger();
+ m_sqlType.setType(ctx, SqlType::Varbinary, desc_LENGTH);
+ } else {
+ // XXX BLOB hack
+ unsigned desc_LENGTH = FAKE_BLOB_SIZE;
+ m_sqlType.setType(ctx, SqlType::Varbinary, desc_LENGTH);
+ }
+ } else if (desc_TYPE == SQL_SMALLINT) {
+ m_sqlType.setType(ctx, SqlType::Smallint);
+ } else if (desc_TYPE == SQL_INTEGER) {
+ m_sqlType.setType(ctx, SqlType::Integer);
+ } else if (desc_TYPE == SQL_BIGINT) {
+ m_sqlType.setType(ctx, SqlType::Bigint);
+ } else if (desc_TYPE == SQL_REAL) {
+ m_sqlType.setType(ctx, SqlType::Real);
+ } else if (desc_TYPE == SQL_DOUBLE) {
+ m_sqlType.setType(ctx, SqlType::Double);
+ } else if (desc_TYPE == SQL_TYPE_TIMESTAMP) {
+ m_sqlType.setType(ctx, SqlType::Datetime);
+ // XXX BLOB hack
+ } else if (desc_TYPE == SQL_LONGVARBINARY) {
+ m_sqlType.setType(ctx, SqlType::Varbinary, (unsigned)FAKE_BLOB_SIZE);
+ } else {
+ ctx.pushStatus(Error::Gen, "parameter %u unsupported SQL type %d", m_paramNumber, (int)desc_TYPE);
+ return 0;
+ }
+ char buf[100];
+ m_sqlType.print(buf, sizeof(buf));
+ ctx_log2(("parameter %u SQL type bound to %s", m_paramNumber, buf));
+ }
+ }
+ return this;
+}
+
+void
+Plan_expr_param::describe(Ctx& ctx)
+{
+ DescArea& ipd = descArea(Desc_usage_IPD);
+ if (ipd.getCount() < m_paramNumber)
+ ipd.setCount(ctx, m_paramNumber);
+ // XXX describe if possible
+ DescRec& rec = ipd.getRecord(m_paramNumber);
+}
+
+Exec_base*
+Plan_expr_param::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ SqlSpec sqlSpec(m_sqlType, SqlSpec::Physical);
+ // create code
+ Exec_expr_param* exec = new Exec_expr_param(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ ctx_assert(m_paramNumber != 0);
+ Exec_expr_param::Code& code = *new Exec_expr_param::Code(sqlSpec, m_paramNumber);
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_expr_param::print(Ctx& ctx)
+{
+ ctx.print(" [param %u]", m_paramNumber);
+}
+
+bool
+Plan_expr_param::isEqual(const Plan_expr* expr) const
+{
+ ctx_assert(expr != 0);
+ if (expr->type() != Plan_expr::TypeParam)
+ return false;
+ const Plan_expr_param* expr2 = static_cast<const Plan_expr_param*>(expr);
+ // params are not equal ever
+ return false;
+}
+
+bool
+Plan_expr_param::isGroupBy(const Plan_expr_row* row) const
+{
+ // params are constants
+ return true;
+}
+
+// Exec_expr_param
+
+Exec_expr_param::Code::~Code()
+{
+}
+
+Exec_expr_param::Data::~Data()
+{
+ delete m_extField;
+ m_extField = 0;
+}
+
+Exec_expr_param::~Exec_expr_param()
+{
+}
+
+void
+Exec_expr_param::alloc(Ctx& ctx, Ctl& ctl)
+{
+ if (m_data != 0)
+ return;
+ const Code& code = getCode();
+ SqlField sqlField(code.sqlSpec());
+ Data& data = *new Data(sqlField);
+ setData(data);
+}
+
+void
+Exec_expr_param::bind(Ctx& ctx)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ DescArea& apd = descArea(Desc_usage_APD);
+ if (apd.getCount() < code.m_paramNumber) {
+ ctx_log1(("parameter %u is not bound", code.m_paramNumber));
+ return;
+ }
+ const unsigned paramNumber = code.m_paramNumber;
+ DescRec& rec = apd.getRecord(paramNumber);
+ OdbcData descData;
+ // create type
+ rec.getField(ctx, SQL_DESC_TYPE, descData);
+ if (descData.type() == OdbcData::Undef) {
+ ctx.pushStatus(Error::Gen, "parameter %u external type not defined", paramNumber);
+ return;
+ }
+ ExtType extType;
+ SQLSMALLINT desc_TYPE = descData.smallint();
+ switch (desc_TYPE) {
+ case SQL_C_CHAR:
+ case SQL_C_SHORT: // for sun.jdbc.odbc
+ case SQL_C_SSHORT:
+ case SQL_C_USHORT:
+ case SQL_C_LONG: // for sun.jdbc.odbc
+ case SQL_C_SLONG:
+ case SQL_C_ULONG:
+ case SQL_C_SBIGINT:
+ case SQL_C_UBIGINT:
+ case SQL_C_FLOAT:
+ case SQL_C_DOUBLE:
+ case SQL_C_TYPE_TIMESTAMP:
+ case SQL_C_BINARY: // XXX BLOB hack
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "parameter %u unsupported external type %d", paramNumber, (int)desc_TYPE);
+ return;
+ }
+ extType.setType(ctx, static_cast<ExtType::Type>(desc_TYPE));
+ ExtSpec extSpec(extType);
+ // create data field
+ rec.getField(ctx, SQL_DESC_DATA_PTR, descData);
+ if (descData.type() == OdbcData::Undef) {
+ ctx.pushStatus(Error::Gen, "parameter %u data address not defined", paramNumber);
+ return;
+ }
+ SQLPOINTER desc_DATA_PTR = descData.pointer();
+ rec.getField(ctx, SQL_DESC_OCTET_LENGTH, descData);
+ if (descData.type() == OdbcData::Undef) {
+ ctx.pushStatus(Error::Gen, "parameter %u data length not defined", paramNumber);
+ return;
+ }
+ SQLINTEGER desc_OCTET_LENGTH = descData.integer();
+ rec.getField(ctx, SQL_DESC_INDICATOR_PTR, descData);
+ if (descData.type() == OdbcData::Undef) {
+ ctx.pushStatus(Error::Gen, "parameter %u indicator address not defined", paramNumber);
+ return;
+ }
+ SQLINTEGER* desc_INDICATOR_PTR = descData.integerPtr();
+ ctx_log4(("parameter %u bind to 0x%x %d 0x%x", paramNumber, (unsigned)desc_DATA_PTR, (int)desc_OCTET_LENGTH, (unsigned)desc_INDICATOR_PTR));
+ ExtField& extField = *new ExtField(extSpec, desc_DATA_PTR, desc_OCTET_LENGTH, desc_INDICATOR_PTR, paramNumber);
+ data.m_atExec = false;
+ if (desc_INDICATOR_PTR != 0 && *desc_INDICATOR_PTR < 0) {
+ if (*desc_INDICATOR_PTR == SQL_NULL_DATA) {
+ ;
+ } else if (*desc_INDICATOR_PTR == SQL_DATA_AT_EXEC) {
+ data.m_atExec = true;
+ } else if (*desc_INDICATOR_PTR <= SQL_LEN_DATA_AT_EXEC(0)) {
+ data.m_atExec = true;
+ }
+ }
+ delete data.m_extField;
+ data.m_extField = &extField;
+}
+
+void
+Exec_expr_param::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ if (ctl.m_postEval)
+ return;
+ const Code& code = getCode();
+ Data& data = getData();
+ if (data.m_atExec)
+ return;
+ ctx_assert(data.m_extField != 0);
+ data.m_sqlField.copyin(ctx, *data.m_extField);
+}
+
+void
+Exec_expr_param::close(Ctx& ctx)
+{
+ Data& data = getData();
+ data.m_extPos = -1;
+}
+
+void
+Exec_expr_param::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [param %u]", code.m_paramNumber);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.hpp
new file mode 100644
index 00000000000..783e5c087b4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.hpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_param_hpp
+#define ODBC_CODEGEN_Code_expr_param_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_expr.hpp"
+
+/**
+ * @class Plan_expr_param
+ * @brief Constant expression value in PlanTree
+ */
+class Plan_expr_param : public Plan_expr {
+public:
+ Plan_expr_param(Plan_root* root, unsigned paramNumber);
+ virtual ~Plan_expr_param();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ bool isEqual(const Plan_expr* expr) const;
+ bool isGroupBy(const Plan_expr_row* row) const;
+protected:
+ const unsigned m_paramNumber;
+};
+
+inline
+Plan_expr_param::Plan_expr_param(Plan_root* root, unsigned paramNumber) :
+ Plan_expr(root, TypeParam),
+ m_paramNumber(paramNumber)
+{
+}
+
+/**
+ * @class Exec_expr_param
+ * @brief Constant expression value in ExecTree
+ */
+class Exec_expr_param : public Exec_expr {
+public:
+ class Code : public Exec_expr::Code {
+ public:
+ Code(const SqlSpec& sqlSpec, unsigned paramNumber);
+ virtual ~Code();
+ protected:
+ friend class Plan_expr_param;
+ friend class Exec_expr_param;
+ const SqlSpec m_sqlSpec;
+ const unsigned m_paramNumber;
+ };
+ class Data : public Exec_expr::Data {
+ public:
+ Data(SqlField& sqlField);
+ virtual ~Data();
+ ExtField* extField() const;
+ protected:
+ friend class Exec_expr_param;
+ friend class Exec_root;
+ SqlField m_sqlField;
+ ExtField* m_extField; // input binding
+ bool m_atExec; // data at exec
+ int m_extPos; // position for SQLPutData (-1 before first call)
+ };
+ Exec_expr_param(Exec_root* root);
+ virtual ~Exec_expr_param();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void bind(Ctx& ctx);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_expr_param::Code::Code(const SqlSpec& sqlSpec, unsigned paramNumber) :
+ Exec_expr::Code(m_sqlSpec),
+ m_sqlSpec(sqlSpec),
+ m_paramNumber(paramNumber)
+{
+}
+
+inline
+Exec_expr_param::Data::Data(SqlField& sqlField) :
+ Exec_expr::Data(m_sqlField),
+ m_sqlField(sqlField),
+ m_extField(0),
+ m_atExec(false),
+ m_extPos(-1)
+{
+}
+
+inline ExtField*
+Exec_expr_param::Data::extField() const
+{
+ return m_extField;
+}
+
+inline
+Exec_expr_param::Exec_expr_param(Exec_root* root) :
+ Exec_expr(root)
+{
+}
+
+// children
+
+inline const Exec_expr_param::Code&
+Exec_expr_param::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_param::Data&
+Exec_expr_param::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.cpp
new file mode 100644
index 00000000000..da1751d41d1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.cpp
@@ -0,0 +1,204 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/DataType.hpp>
+#include "Code_expr_row.hpp"
+#include "Code_expr.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_dml_row.hpp"
+#include "Code_root.hpp"
+
+// Plan_expr_row
+
+Plan_expr_row::~Plan_expr_row()
+{
+}
+
+Plan_base*
+Plan_expr_row::analyze(Ctx& ctx, Ctl& ctl)
+{
+ unsigned size = getSize();
+ // analyze subexpressions
+ m_anyAggr = false;
+ m_allBound = true;
+ for (unsigned i = 1; i <= size; i++) {
+ Plan_expr* expr1 = getExpr(i);
+ Plan_expr* expr2 = static_cast<Plan_expr*>(expr1->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ setExpr(i, expr2);
+ if (expr2->isAggr())
+ m_anyAggr = true;
+ if (! expr2->isBound())
+ m_allBound = false;
+ }
+ // insert conversions if requested XXX ugly hack
+ if (ctl.m_dmlRow != 0) {
+ if (ctl.m_dmlRow->getSize() > getSize()) {
+ ctx.pushStatus(Sqlstate::_21S01, Error::Gen, "not enough values (%u > %u)", ctl.m_dmlRow->getSize(), getSize());
+ return 0;
+ }
+ if (ctl.m_dmlRow->getSize() < getSize()) {
+ ctx.pushStatus(Sqlstate::_21S01, Error::Gen, "too many values (%u < %u)", ctl.m_dmlRow->getSize(), getSize());
+ return 0;
+ }
+ for (unsigned i = 1; i <= size; i++) {
+ const SqlType& sqlType = ctl.m_dmlRow->getColumn(i)->sqlType();
+ Plan_expr_conv* exprConv = new Plan_expr_conv(m_root, sqlType);
+ m_root->saveNode(exprConv);
+ exprConv->setExpr(getExpr(i));
+ Plan_expr* expr = static_cast<Plan_expr*>(exprConv->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(expr != 0);
+ setExpr(i, expr);
+ }
+ }
+ // set aliases
+ m_aliasList.resize(1 + size);
+ for (unsigned i = 1; i <= size; i++) {
+ if (m_aliasList[i].empty()) {
+ setAlias(i, getExpr(i)->getAlias());
+ }
+ }
+ // node was not replaced
+ return this;
+}
+
+Exec_base*
+Plan_expr_row::codegen(Ctx& ctx, Ctl& ctl)
+{
+ unsigned size = getSize();
+ Exec_expr_row* exec = new Exec_expr_row(ctl.m_execRoot, size);
+ ctl.m_execRoot->saveNode(exec);
+ SqlSpecs sqlSpecs(size);
+ // create code for subexpressions
+ for (unsigned i = 1; i <= size; i++) {
+ Plan_expr* planExpr = getExpr(i);
+ Exec_expr* execExpr = static_cast<Exec_expr*>(planExpr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ exec->setExpr(i, execExpr);
+ const SqlSpec sqlSpec(execExpr->getCode().sqlSpec(), SqlSpec::Reference);
+ sqlSpecs.setEntry(i, sqlSpec);
+ }
+ // create alias list
+ Exec_expr_row::Code::Alias* aliasList = new Exec_expr_row::Code::Alias[1 + size];
+ strcpy(aliasList[0], "?");
+ for (unsigned i = 1; i <= size; i++) {
+ const char* s = m_aliasList[i].c_str();
+ if (strlen(s) == 0)
+ s = getExpr(i)->getAlias().c_str();
+ unsigned n = strlen(s);
+ if (n >= sizeof(Exec_expr_row::Code::Alias))
+ n = sizeof(Exec_expr_row::Code::Alias) - 1;
+ strncpy(aliasList[i], s, n);
+ aliasList[i][n] = 0;
+ }
+ // create the code
+ Exec_expr_row::Code& code = *new Exec_expr_row::Code(sqlSpecs, aliasList);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_expr_row::print(Ctx& ctx)
+{
+ const unsigned size = getSize();
+ ctx.print(" [expr_row");
+ for (unsigned i = 1; i <= size; i++) {
+ Plan_base* a = m_exprList[i];
+ a == 0 ? ctx.print(" -") : a->print(ctx);
+ }
+ ctx.print("]");
+}
+
+bool
+Plan_expr_row::isAllGroupBy(const Plan_expr_row* row) const
+{
+ const unsigned size = getSize();
+ for (unsigned i = 1; i <= size; i++) {
+ if (! getExpr(i)->isGroupBy(row))
+ return false;
+ }
+ return true;
+}
+
+// Exec_expr_row
+
+Exec_expr_row::Code::~Code()
+{
+ delete[] m_aliasList;
+}
+
+Exec_expr_row::Data::~Data()
+{
+}
+
+Exec_expr_row::~Exec_expr_row()
+{
+ delete[] m_expr;
+}
+
+void
+Exec_expr_row::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate subexpressions
+ for (unsigned i = 1; i <= m_size; i++) {
+ getExpr(i)->alloc(ctx, ctl);
+ }
+ // construct SqlRow of references
+ const Code& code = getCode();
+ SqlRow sqlRow(getCode().m_sqlSpecs);
+ for (unsigned i = 1; i <= m_size; i++) {
+ const Exec_expr::Data& dataExpr = getExpr(i)->getData();
+ const SqlSpec& sqlSpec = code.m_sqlSpecs.getEntry(i);
+ const SqlField sqlField(sqlSpec, &dataExpr.sqlField());
+ sqlRow.setEntry(i, sqlField);
+ }
+ // create the data
+ Data& data = *new Data(sqlRow);
+ setData(data);
+}
+
+void
+Exec_expr_row::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ for (unsigned i = 1; i <= m_size; i++) {
+ getExpr(i)->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+}
+
+void
+Exec_expr_row::close(Ctx& ctx)
+{
+ for (unsigned i = 1; i <= m_size; i++) {
+ getExpr(i)->close(ctx);
+ }
+}
+
+void
+Exec_expr_row::print(Ctx& ctx)
+{
+ ctx.print(" [expr_row");
+ for (unsigned i = 1; i <= m_size; i++) {
+ getExpr(i)->print(ctx);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.hpp
new file mode 100644
index 00000000000..94527931dba
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.hpp
@@ -0,0 +1,272 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_expr_row_hpp
+#define ODBC_CODEGEN_Code_expr_row_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include <common/DataRow.hpp>
+#include "Code_base.hpp"
+#include "Code_expr.hpp"
+
+class Plan_expr;
+
+/**
+ * @class Plan_expr_row
+ * @brief Row of expressions in PlanTree
+ *
+ * Used for select, value, and order by rows.
+ */
+class Plan_expr_row : public Plan_base {
+public:
+ Plan_expr_row(Plan_root* root);
+ virtual ~Plan_expr_row();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setAsterisk();
+ bool getAsterisk() const;
+ unsigned getSize() const;
+ Plan_expr* getExpr(unsigned i) const;
+ void setExpr(unsigned i, Plan_expr* expr);
+ void addExpr(Plan_expr* expr);
+ void addExpr(Plan_expr* expr, const BaseString& alias);
+ void setAlias(unsigned i, const BaseString& alias);
+ void addExpr(Plan_expr* expr, bool asc);
+ bool anyAggr() const;
+ bool allBound() const;
+ bool isAllGroupBy(const Plan_expr_row* row) const;
+protected:
+ friend class Plan_query;
+ friend class Plan_query_sort;
+ bool m_asterisk; // early plan node type
+ ExprVector m_exprList;
+ typedef std::vector<BaseString> AliasList;
+ AliasList m_aliasList;
+ typedef std::vector<bool> AscList;
+ AscList m_ascList;
+ bool m_anyAggr; // at least one aggreate
+ bool m_allBound; // all bound
+};
+
+inline
+Plan_expr_row::Plan_expr_row(Plan_root* root) :
+ Plan_base(root),
+ m_asterisk(false),
+ m_exprList(1),
+ m_aliasList(1),
+ m_ascList(1),
+ m_anyAggr(false),
+ m_allBound(false)
+{
+}
+
+// children
+
+inline void
+Plan_expr_row::setAsterisk()
+{
+ m_asterisk = true;
+}
+
+inline bool
+Plan_expr_row::getAsterisk() const
+{
+ return m_asterisk;
+}
+
+inline unsigned
+Plan_expr_row::getSize() const
+{
+ ctx_assert(m_exprList.size() >= 1);
+ return m_exprList.size() - 1;
+}
+
+inline void
+Plan_expr_row::addExpr(Plan_expr* expr)
+{
+ ctx_assert(expr != 0);
+ addExpr(expr, expr->m_alias);
+}
+
+inline void
+Plan_expr_row::addExpr(Plan_expr* expr, const BaseString& alias)
+{
+ ctx_assert(expr != 0);
+ m_exprList.push_back(expr);
+ m_aliasList.push_back(alias);
+}
+
+inline void
+Plan_expr_row::addExpr(Plan_expr* expr, bool asc)
+{
+ ctx_assert(expr != 0);
+ m_exprList.push_back(expr);
+ m_ascList.push_back(asc);
+}
+
+inline void
+Plan_expr_row::setExpr(unsigned i, Plan_expr* expr)
+{
+ ctx_assert(1 <= i && i < m_exprList.size() && expr != 0);
+ m_exprList[i] = expr;
+}
+
+inline Plan_expr*
+Plan_expr_row::getExpr(unsigned i) const
+{
+ ctx_assert(1 <= i && i < m_exprList.size() && m_exprList[i] != 0);
+ return m_exprList[i];
+}
+
+inline void
+Plan_expr_row::setAlias(unsigned i, const BaseString& alias)
+{
+ ctx_assert(1 <= i && i < m_aliasList.size());
+ m_aliasList[i] = alias;
+}
+
+inline bool
+Plan_expr_row::anyAggr() const
+{
+ return m_anyAggr;
+}
+
+inline bool
+Plan_expr_row::allBound() const
+{
+ return m_allBound;
+}
+
+/**
+ * @class Expr_expr_row
+ * @brief Row of expressions in ExecTree
+ */
+class Exec_expr_row : public Exec_base {
+public:
+ class Code : public Exec_base::Code {
+ public:
+ typedef char Alias[40];
+ Code(const SqlSpecs& sqlSpecs, const Alias* aliasList);
+ virtual ~Code();
+ const SqlSpecs& sqlSpecs() const;
+ const char* getAlias(unsigned i) const;
+ protected:
+ friend class Exec_expr_row;
+ const SqlSpecs m_sqlSpecs;
+ const Alias* m_aliasList;
+ };
+ class Data : public Exec_base::Data {
+ public:
+ Data(const SqlRow& sqlRow);
+ virtual ~Data();
+ const SqlRow& sqlRow() const;
+ protected:
+ friend class Exec_expr_row;
+ SqlRow m_sqlRow;
+ };
+ Exec_expr_row(Exec_root* root, unsigned size);
+ virtual ~Exec_expr_row();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ Exec_expr* getExpr(unsigned i) const;
+ void setExpr(unsigned i, Exec_expr* expr);
+protected:
+ Exec_expr** m_expr; // numbered from 1
+ unsigned m_size;
+};
+
+inline
+Exec_expr_row::Code::Code(const SqlSpecs& sqlSpecs, const Alias* aliasList) :
+ m_sqlSpecs(sqlSpecs),
+ m_aliasList(aliasList)
+{
+}
+
+inline const SqlSpecs&
+Exec_expr_row::Code::sqlSpecs() const
+{
+ return m_sqlSpecs;
+}
+
+inline const char*
+Exec_expr_row::Code::getAlias(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_sqlSpecs.count() && m_aliasList != 0);
+ return m_aliasList[i];
+}
+
+inline
+Exec_expr_row::Data::Data(const SqlRow& sqlRow) :
+ m_sqlRow(sqlRow)
+{
+}
+
+inline const SqlRow&
+Exec_expr_row::Data::sqlRow() const
+{
+ return m_sqlRow;
+}
+
+inline
+Exec_expr_row::Exec_expr_row(Exec_root* root, unsigned size) :
+ Exec_base(root),
+ m_expr(new Exec_expr* [1 + size]),
+ m_size(size)
+{
+ m_expr[0] = (Exec_expr*)-1;
+ for (unsigned i = 1; i <= m_size; i++)
+ m_expr[i] = 0;
+}
+
+// children
+
+inline const Exec_expr_row::Code&
+Exec_expr_row::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_expr_row::Data&
+Exec_expr_row::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline Exec_expr*
+Exec_expr_row::getExpr(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_size && m_expr != 0 && m_expr[i] != 0);
+ return m_expr[i];
+}
+
+inline void
+Exec_expr_row::setExpr(unsigned i, Exec_expr* expr)
+{
+ ctx_assert(1 <= i && i <= m_size && m_expr != 0 && m_expr[i] == 0);
+ m_expr[i] = expr;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.cpp
new file mode 100644
index 00000000000..584ffef3e01
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include "Code_idx_column.hpp"
+#include "Code_expr_conv.hpp"
+#include "Code_root.hpp"
+
+// Plan_idx_column
+
+Plan_idx_column::~Plan_idx_column()
+{
+}
+
+Plan_base*
+Plan_idx_column::analyze(Ctx& ctx, Ctl& ctl)
+{
+ analyzeColumn(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_idx_column::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_idx_column::print(Ctx& ctx)
+{
+ ctx.print(" [idx_column %s]", getPrintName());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.hpp
new file mode 100644
index 00000000000..209ed705b48
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.hpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_idx_column_hpp
+#define ODBC_CODEGEN_Code_idx_column_hpp
+
+#include <common/common.hpp>
+#include "Code_column.hpp"
+#include "Code_data_type.hpp"
+#include "Code_expr.hpp"
+
+class DictColumn;
+class Plan_table;
+
+/**
+ * @class Plan_idx_column
+ * @brief Column in create index statement
+ */
+class Plan_idx_column : public Plan_base, public Plan_column {
+public:
+ Plan_idx_column(Plan_root* root, const BaseString& name);
+ virtual ~Plan_idx_column();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+protected:
+ friend class Plan_create_row;
+};
+
+inline
+Plan_idx_column::Plan_idx_column(Plan_root* root, const BaseString& name) :
+ Plan_base(root),
+ Plan_column(Type_idx, name)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_insert.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_insert.cpp
new file mode 100644
index 00000000000..c442186c181
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_insert.cpp
@@ -0,0 +1,253 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_insert.hpp"
+#include "Code_query_repeat.hpp"
+#include "Code_query_project.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+// Plan_insert
+
+Plan_insert::~Plan_insert()
+{
+}
+
+Plan_base*
+Plan_insert::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_insert);
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ // handle MySql syntax
+ if (m_mysqlRow != 0) {
+ setDmlRow(m_mysqlRow->m_dmlRow);
+ setExprRow(m_mysqlRow->m_exprRow);
+ m_mysqlRow = 0;
+ }
+ if (m_dmlRow == 0) {
+ // construct column list
+ setDmlRow(new Plan_dml_row(m_root));
+ m_root->saveNode(m_dmlRow);
+ const DictTable& dictTable = m_table->dictTable();
+ unsigned n = dictTable.getSize();
+ for (unsigned i = 1; i <= n; i++) {
+ DictColumn* dictColumn = dictTable.getColumn(i);
+ Plan_dml_column* column = new Plan_dml_column(m_root, dictColumn->getName());
+ m_root->saveNode(column);
+ m_dmlRow->addColumn(column);
+ }
+ }
+ // set name resolution scope
+ ctl.m_tableList.resize(1 + 1); // indexed from 1
+ ctl.m_tableList[1] = m_table;
+ // analyze the dml columns
+ m_dmlRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctl.m_dmlRow = m_dmlRow; // row type to convert to
+ if (m_query != 0) {
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ } else if (m_select == 0) {
+ // analyze the expression row
+ m_exprRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ // transform the row into query
+ Plan_query_repeat* queryRepeat = new Plan_query_repeat(m_root, 1);
+ m_root->saveNode(queryRepeat);
+ Plan_query_project* queryProject = new Plan_query_project(m_root);
+ m_root->saveNode(queryProject);
+ queryProject->setQuery(queryRepeat);
+ queryProject->setRow(m_exprRow);
+ setQuery(queryProject);
+ } else {
+ // analyze the select into query
+ Plan_query* query = static_cast<Plan_query*>(m_select->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ setQuery(query);
+ }
+ return this;
+}
+
+void
+Plan_insert::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "INSERT", SQL_DIAG_INSERT);
+}
+
+Exec_base*
+Plan_insert::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the query
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // set up
+ ctx_assert(m_table != 0);
+ const BaseString& tableName = m_table->getName();
+ const DictTable& dictTable = m_table->dictTable();
+ const ColumnVector& columns = m_table->dmlColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_insert::Code& code = *new Exec_insert::Code();
+ code.m_insertOp = m_insertOp;
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ code.m_attrCount = attrCount;
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_isKey = new bool[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ code.m_tupleId = dictTable.tupleId(); // maybe 0
+ code.m_autoIncrement = dictTable.autoIncrement(); // maybe 0
+ unsigned k;
+ if ((k = code.m_tupleId) != 0 || (k = code.m_autoIncrement) != 0) {
+ const DictColumn& dictColumn = *dictTable.getColumn(k);
+ code.m_idType = dictColumn.sqlType();
+ }
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ code.m_attrId[i] = dictColumn.getAttrId();
+ code.m_isKey[i] = dictColumn.isKey();
+ }
+ // default values XXX a mess
+ code.m_defaultCount = 0;
+ for (unsigned j = 1; j <= dictTable.getSize(); j++) {
+ const DictColumn& dictColumn = *dictTable.getColumn(j);
+ if (dictColumn.getDefaultValue() != 0 && ! code.findAttrId(dictColumn.getAttrId()))
+ code.m_defaultCount++;
+ }
+ if (code.m_defaultCount != 0) {
+ code.m_defaultId = new NdbAttrId[1 + code.m_defaultCount];
+ for (unsigned i = 0, j = 1; j <= dictTable.getSize(); j++) {
+ const DictColumn& dictColumn = *dictTable.getColumn(j);
+ if (dictColumn.getDefaultValue() != 0 && ! code.findAttrId(dictColumn.getAttrId()))
+ code.m_defaultId[++i] = dictColumn.getAttrId();
+ }
+ SqlSpecs sqlSpecs(code.m_defaultCount);
+ for (unsigned i = 0, j = 1; j <= dictTable.getSize(); j++) {
+ const DictColumn& dictColumn = *dictTable.getColumn(j);
+ if (dictColumn.getDefaultValue() != 0 && ! code.findAttrId(dictColumn.getAttrId())) {
+ SqlSpec sqlSpec(dictColumn.sqlType(), SqlSpec::Physical);
+ sqlSpecs.setEntry(++i, sqlSpec);
+ }
+ }
+ code.m_defaultValue = new SqlRow(sqlSpecs);
+ for (unsigned i = 0, j = 1; j <= dictTable.getSize(); j++) {
+ const DictColumn& dictColumn = *dictTable.getColumn(j);
+ if (dictColumn.getDefaultValue() != 0 && ! code.findAttrId(dictColumn.getAttrId())) {
+ const char* defaultValue = dictColumn.getDefaultValue();
+ ExtType extType(ExtType::Char);
+ ExtSpec extSpec(extType);
+ SQLINTEGER ind = SQL_NTS;
+ ExtField extField(extSpec, (SQLPOINTER)defaultValue, 0, &ind);
+ SqlField& f = code.m_defaultValue->getEntry(++i);
+ f.copyin(ctx, extField);
+ if (! ctx.ok())
+ return 0;
+ }
+ }
+ }
+ // create the exec
+ Exec_insert* exec = new Exec_insert(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_insert::print(Ctx& ctx)
+{
+ ctx.print(" [%s", m_insertOp == Insert_op_insert ? "insert" : "write");
+ Plan_base* a[] = { m_table, m_dmlRow, m_exprRow, m_query };
+ printList(ctx, a, 4);
+ ctx.print("]");
+}
+
+// Exec_insert
+
+Exec_insert::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_attrId;
+ delete[] m_isKey;
+ delete[] m_defaultId;
+ delete m_defaultValue;
+}
+
+bool
+Exec_insert::Code::findAttrId(NdbAttrId attrId) const
+{
+ for (unsigned i = 1; i <= m_attrCount; i++) {
+ if (m_attrId[i] == attrId)
+ return true;
+ }
+ return false;
+}
+
+Exec_insert::Data::~Data()
+{
+}
+
+Exec_insert::~Exec_insert()
+{
+}
+
+void
+Exec_insert::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the query
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ // create data
+ Data& data = *new Data();
+ setData(data);
+}
+
+void
+Exec_insert::close(Ctx& ctx)
+{
+}
+
+void
+Exec_insert::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx_assert(m_query != 0);
+ ctx.print(" [insert");
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ ctx.print(" table=%s", code.m_tableName);
+ m_query->print(ctx);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_insert.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_insert.hpp
new file mode 100644
index 00000000000..748b092e33a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_insert.hpp
@@ -0,0 +1,229 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_insert_hpp
+#define ODBC_CODEGEN_Code_insert_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_dml.hpp"
+#include "Code_dml_row.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_select.hpp"
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+#include "Code_set_row.hpp"
+
+enum Insert_op {
+ Insert_op_undef = 0,
+ Insert_op_insert = 1,
+ Insert_op_write = 2
+};
+
+/**
+ * @class Plan_insert
+ * @brief Insert in PlanTree
+ *
+ * Insert. Becomes directly executable.
+ */
+class Plan_insert : public Plan_dml {
+public:
+ Plan_insert(Plan_root* root, Insert_op insertOp);
+ virtual ~Plan_insert();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+ Plan_dml_row* getDmlRow() const;
+ void setDmlRow(Plan_dml_row* dmlRow);
+ void setExprRow(Plan_expr_row* exprRow);
+ void setSelect(Plan_select* select);
+ void setQuery(Plan_query* query);
+ void setMysqlRow(Plan_set_row* mysqlRow);
+protected:
+ Insert_op m_insertOp;
+ Plan_table* m_table;
+ Plan_dml_row* m_dmlRow;
+ Plan_expr_row* m_exprRow;
+ Plan_select* m_select;
+ Plan_query* m_query;
+ Plan_set_row* m_mysqlRow;
+};
+
+inline
+Plan_insert::Plan_insert(Plan_root* root, Insert_op insertOp) :
+ Plan_dml(root),
+ m_insertOp(insertOp),
+ m_table(0),
+ m_dmlRow(0),
+ m_exprRow(0),
+ m_select(0),
+ m_query(0),
+ m_mysqlRow(0)
+{
+}
+
+// children
+
+inline void
+Plan_insert::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline void
+Plan_insert::setDmlRow(Plan_dml_row* dmlRow)
+{
+ ctx_assert(dmlRow != 0);
+ m_dmlRow = dmlRow;
+}
+
+inline Plan_dml_row*
+Plan_insert::getDmlRow() const
+{
+ ctx_assert(m_dmlRow != 0);
+ return m_dmlRow;
+}
+
+inline void
+Plan_insert::setExprRow(Plan_expr_row* exprRow)
+{
+ ctx_assert(exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+inline void
+Plan_insert::setSelect(Plan_select* select)
+{
+ ctx_assert(select != 0);
+ m_select = select;
+}
+
+inline void
+Plan_insert::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_insert::setMysqlRow(Plan_set_row* mysqlRow)
+{
+ ctx_assert(mysqlRow != 0);
+ m_mysqlRow = mysqlRow;
+}
+
+/**
+ * @class Exec_insert
+ * @brief Executable insert
+ */
+class Exec_insert : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code();
+ virtual ~Code();
+ protected:
+ friend class Plan_insert;
+ friend class Exec_insert;
+ Insert_op m_insertOp;
+ char* m_tableName;
+ unsigned m_attrCount;
+ NdbAttrId* m_attrId;
+ bool* m_isKey;
+ unsigned m_tupleId; // position of tuple id
+ unsigned m_autoIncrement; // position of ai key
+ SqlType m_idType; // type of tuple id or ai key
+ unsigned m_defaultCount;
+ NdbAttrId* m_defaultId;
+ SqlRow* m_defaultValue;
+ bool findAttrId(NdbAttrId attrId) const;
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_insert;
+ };
+ Exec_insert(Exec_root* root);
+ virtual ~Exec_insert();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+private:
+ Exec_query* m_query;
+};
+
+inline
+Exec_insert::Code::Code() :
+ m_insertOp(Insert_op_undef),
+ m_tableName(0),
+ m_attrCount(0),
+ m_attrId(0),
+ m_isKey(0),
+ m_tupleId(0),
+ m_autoIncrement(0),
+ m_defaultCount(0),
+ m_defaultId(0),
+ m_defaultValue(0)
+{
+}
+
+inline
+Exec_insert::Data::Data()
+{
+}
+
+inline
+Exec_insert::Exec_insert(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_insert::Code&
+Exec_insert::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_insert::Data&
+Exec_insert::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_insert::setQuery(Exec_query* query)
+{
+ ctx_assert(m_query == 0 && query != 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_pred.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred.cpp
new file mode 100644
index 00000000000..fe7cac7606e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred.cpp
@@ -0,0 +1,70 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_pred.hpp"
+#include "Code_pred_op.hpp"
+#include "Code_root.hpp"
+
+// Plan_pred
+
+Plan_pred::~Plan_pred()
+{
+}
+
+bool
+Plan_pred::isGroupBy(const Plan_expr_row* row) const
+{
+ return false;
+}
+
+Plan_pred*
+Plan_pred::opAnd(Plan_pred* pred2)
+{
+ Plan_pred_op* predAnd = new Plan_pred_op(m_root, Pred_op::And);
+ m_root->saveNode(predAnd);
+ predAnd->setPred(1, this);
+ predAnd->setPred(2, pred2);
+ return predAnd;
+}
+
+// Exec_pred
+
+Exec_pred::Code::~Code()
+{
+}
+
+Exec_pred::Data::~Data()
+{
+}
+
+Exec_pred::~Exec_pred()
+{
+}
+
+Pred_value&
+Exec_pred::Data::groupValue(unsigned i, bool initFlag)
+{
+ if (m_groupValue.size() == 0) {
+ m_groupValue.resize(1);
+ }
+ if (initFlag) {
+ //unsigned i2 = m_groupValue.size();
+ //ctx_assert(i == i2);
+ m_groupValue.push_back(Pred_value_unknown);
+ }
+ ctx_assert(i != 0 && i < m_groupValue.size());
+ return m_groupValue[i];
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_pred.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred.hpp
new file mode 100644
index 00000000000..a77c1161fa1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred.hpp
@@ -0,0 +1,172 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_pred_hpp
+#define ODBC_CODEGEN_Code_pred_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_base.hpp"
+
+enum Pred_value {
+ Pred_value_unknown = -1,
+ Pred_value_false = 0,
+ Pred_value_true = 1
+};
+
+class Ctx;
+class Plan_expr_row;
+class Exec_pred;
+
+/**
+ * @class Plan_pred
+ * @brief Base class for predicates in PlanTree
+ *
+ * Predicate represents a boolean value.
+ */
+class Plan_pred : public Plan_base {
+public:
+ // type is convenient since RTTI cannot be used
+ enum Type {
+ TypeUndefined = 0,
+ TypeComp = 1,
+ TypeOp = 2
+ };
+ Plan_pred(Plan_root* root, Type type);
+ virtual ~Plan_pred() = 0;
+ Type type() const;
+ const TableSet& tableSet() const;
+ const TableSet& noInterp() const;
+ virtual bool isGroupBy(const Plan_expr_row* row) const;
+ // helpers
+ Plan_pred* opAnd(Plan_pred* pred2);
+protected:
+ const Type m_type;
+ TableSet m_tableSet; // depends on these tables
+ TableSet m_noInterp; // cannot use interpreted TUP program on these tables
+ Exec_pred* m_exec; // probably stupid
+};
+
+inline
+Plan_pred::Plan_pred(Plan_root* root, Type type) :
+ Plan_base(root),
+ m_type(type),
+ m_exec(0)
+{
+}
+
+inline Plan_pred::Type
+Plan_pred::type() const
+{
+ return m_type;
+}
+
+inline const Plan_pred::TableSet&
+Plan_pred::tableSet() const
+{
+ return m_tableSet;
+}
+
+inline const Plan_pred::TableSet&
+Plan_pred::noInterp() const
+{
+ return m_noInterp;
+}
+
+/**
+ * @class Exec_pred
+ * @brief Base class for predicates in ExecTree
+ */
+class Exec_pred : public Exec_base {
+public:
+ class Code : public Exec_base::Code {
+ public:
+ Code();
+ virtual ~Code() = 0;
+ protected:
+ friend class Exec_pred;
+ };
+ class Data : public Exec_base::Data {
+ public:
+ Data();
+ virtual ~Data() = 0;
+ Pred_value getValue() const;
+ Pred_value groupValue(unsigned i) const;
+ protected:
+ friend class Exec_pred;
+ Pred_value m_value; // the value
+ // group-by data
+ typedef std::vector<Pred_value> GroupValue;
+ GroupValue m_groupValue;
+ Pred_value& groupValue(unsigned i, bool initFlag);
+ };
+ Exec_pred(Exec_root* root);
+ virtual ~Exec_pred() = 0;
+ virtual void execInterp(Ctx& ctx, Ctl& ctl) = 0;
+ virtual void evaluate(Ctx& ctx, Ctl& ctl) = 0;
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_pred::Code::Code()
+{
+}
+
+inline
+Exec_pred::Data::Data() :
+ m_value(Pred_value_unknown)
+{
+}
+
+inline Pred_value
+Exec_pred::Data::getValue() const
+{
+ return m_value;
+}
+
+inline Pred_value
+Exec_pred::Data::groupValue(unsigned i) const
+{
+ ctx_assert(i != 0 && i < m_groupValue.size());
+ return m_groupValue[i];
+}
+
+inline
+Exec_pred::Exec_pred(Exec_root* root) :
+ Exec_base(root)
+{
+}
+
+// children
+
+inline const Exec_pred::Code&
+Exec_pred::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_pred::Data&
+Exec_pred::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.cpp
new file mode 100644
index 00000000000..29736e45818
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.cpp
@@ -0,0 +1,188 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_pred.hpp"
+#include "Code_pred_op.hpp"
+#include "Code_root.hpp"
+
+// Pred_op
+
+const char*
+Pred_op::name() const
+{
+ switch (m_opcode) {
+ case And:
+ return "and";
+ case Or:
+ return "or";
+ case Not:
+ return "not";
+ }
+ ctx_assert(false);
+ return "";
+}
+
+unsigned
+Pred_op::arity() const
+{
+ switch (m_opcode) {
+ case And:
+ case Or:
+ return 2;
+ case Not:
+ return 1;
+ }
+ ctx_assert(false);
+ return 0;
+}
+
+// Plan_pred_op
+
+Plan_pred_op::~Plan_pred_op()
+{
+}
+
+Plan_base*
+Plan_pred_op::analyze(Ctx& ctx, Ctl& ctl)
+{
+ m_exec = 0;
+ unsigned arity = m_op.arity();
+ // check if we remain in top-level AND-clause
+ const bool topand = ctl.m_topand;
+ if (m_op.m_opcode != Pred_op::And)
+ ctl.m_topand = false;
+ // analyze sub-predicates
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_pred[i] != 0);
+ m_pred[i]->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // save top level predicate on list
+ if (topand && ! ctl.m_topand) {
+ ctl.m_topcomp.push_back(this);
+ }
+ ctl.m_topand = topand;
+ // table dependencies are union from operands
+ m_tableSet.clear();
+ for (unsigned i = 1; i <= arity; i++) {
+ const TableSet& ts = m_pred[i]->tableSet();
+ m_tableSet.insert(ts.begin(), ts.end());
+ }
+ // set of tables for which interpreter cannot be used
+ m_noInterp.clear();
+ for (unsigned i = 1; i <= arity; i++) {
+ const TableSet& ts = m_pred[i]->noInterp();
+ m_noInterp.insert(ts.begin(), ts.end());
+ }
+ return this;
+}
+
+Exec_base*
+Plan_pred_op::codegen(Ctx& ctx, Ctl& ctl)
+{
+ if (m_exec != 0)
+ return m_exec;
+ unsigned arity = m_op.arity();
+ Exec_pred_op* exec = new Exec_pred_op(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // create code for operands
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_pred[i] != 0);
+ Exec_pred* execPred = static_cast<Exec_pred*>(m_pred[i]->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execPred != 0);
+ exec->setPred(i, execPred);
+ }
+ // create the code
+ Exec_pred_op::Code& code = *new Exec_pred_op::Code(m_op);
+ exec->setCode(code);
+ m_exec = exec;
+ return exec;
+}
+
+void
+Plan_pred_op::print(Ctx& ctx)
+{
+ ctx.print(" [%s", m_op.name());
+ Plan_base* a[] = { m_pred[1], m_pred[2] };
+ printList(ctx, a, m_op.arity());
+ ctx.print("]");
+}
+
+bool
+Plan_pred_op::isGroupBy(const Plan_expr_row* row) const
+{
+ const unsigned arity = m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_pred[i] != 0);
+ if (! m_pred[i]->isGroupBy(row))
+ return false;
+ }
+ return true;
+}
+
+// Code_pred_op
+
+Exec_pred_op::Code::~Code()
+{
+}
+
+Exec_pred_op::Data::~Data()
+{
+}
+
+Exec_pred_op::~Exec_pred_op()
+{
+}
+
+void
+Exec_pred_op::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // allocate sub-predicates
+ unsigned arity = code.m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_pred[i] != 0);
+ m_pred[i]->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_pred_op::close(Ctx& ctx)
+{
+ const Code& code = getCode();
+ unsigned arity = code.m_op.arity();
+ for (unsigned i = 1; i <= arity; i++) {
+ ctx_assert(m_pred[i] != 0);
+ m_pred[i]->close(ctx);
+ }
+}
+
+void
+Exec_pred_op::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [%s", code.m_op.name());
+ Exec_base* a[] = { m_pred[1], m_pred[2] };
+ printList(ctx, a, code.m_op.arity());
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.hpp
new file mode 100644
index 00000000000..9130bc3cb81
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.hpp
@@ -0,0 +1,158 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_pred_op_hpp
+#define ODBC_CODEGEN_Code_pred_op_hpp
+
+#include <common/common.hpp>
+#include <common/DataField.hpp>
+#include "Code_pred.hpp"
+
+/**
+ * @class Pred_op
+ * @brief Boolean operators
+ */
+struct Pred_op {
+ enum Opcode {
+ And = 1, // binary
+ Or,
+ Not // unary
+ };
+ Pred_op(Opcode opcode);
+ const char* name() const;
+ unsigned arity() const;
+ Opcode m_opcode;
+};
+
+inline
+Pred_op::Pred_op(Opcode opcode) :
+ m_opcode(opcode)
+{
+}
+
+/**
+ * @class Plan_pred_op
+ * @brief Operator node in a predicate in PlanTree
+ */
+class Plan_pred_op : public Plan_pred {
+public:
+ Plan_pred_op(Plan_root* root, Pred_op op);
+ virtual ~Plan_pred_op();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ bool isGroupBy(const Plan_expr_row* row) const;
+ // children
+ void setPred(unsigned i, Plan_pred* pred);
+protected:
+ friend class Plan_pred;
+ Pred_op m_op;
+ Plan_pred* m_pred[1 + 2];
+};
+
+inline
+Plan_pred_op::Plan_pred_op(Plan_root* root, Pred_op op) :
+ Plan_pred(root, TypeOp),
+ m_op(op)
+{
+ m_pred[0] = m_pred[1] = m_pred[2] = 0;
+}
+
+inline void
+Plan_pred_op::setPred(unsigned i, Plan_pred* pred)
+{
+ ctx_assert(1 <= i && i <= m_op.arity() && pred != 0);
+ m_pred[i] = pred;
+}
+
+/**
+ * @class Exec_pred_op
+ * @brief Operator node in a predicate in ExecTree
+ */
+class Exec_pred_op : public Exec_pred {
+public:
+ class Code : public Exec_pred::Code {
+ public:
+ Code(Pred_op op);
+ virtual ~Code();
+ protected:
+ friend class Exec_pred_op;
+ Pred_op m_op;
+ };
+ class Data : public Exec_pred::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_pred_op;
+ };
+ Exec_pred_op(Exec_root* root);
+ virtual ~Exec_pred_op();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execInterp(Ctx& ctx, Ctl& ctl);
+ void evaluate(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setPred(unsigned i, Exec_pred* pred);
+protected:
+ Exec_pred* m_pred[1 + 2];
+};
+
+inline
+Exec_pred_op::Code::Code(Pred_op op) :
+ m_op(op)
+{
+}
+
+inline
+Exec_pred_op::Data::Data()
+{
+}
+
+inline
+Exec_pred_op::Exec_pred_op(Exec_root* root) :
+ Exec_pred(root)
+{
+ m_pred[0] = m_pred[1] = m_pred[2] = 0;
+}
+
+// children
+
+inline const Exec_pred_op::Code&
+Exec_pred_op::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_pred_op::Data&
+Exec_pred_op::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_pred_op::setPred(unsigned i, Exec_pred* pred)
+{
+ ctx_assert(1 <= i && i <= 2 && m_pred[i] == 0);
+ m_pred[i] = pred;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query.cpp
new file mode 100644
index 00000000000..9e983942601
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query.cpp
@@ -0,0 +1,299 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_query.hpp"
+#include "Code_query_project.hpp"
+#include "Code_query_count.hpp"
+
+// Plan_query
+
+Plan_query::~Plan_query()
+{
+}
+
+Plan_expr_row*
+Plan_query::getRow()
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_query::describe(Ctx& ctx)
+{
+ const Plan_expr_row* exprRow = getRow();
+ const unsigned count = exprRow->getSize();
+ // create IRD
+ DescArea& ird = descArea(Desc_usage_IRD);
+ ird.setCount(ctx, count);
+ for (unsigned i = 1; i <= count; i++) {
+ DescRec& rec = ird.getRecord(i);
+ const Plan_expr* expr = exprRow->m_exprList[i];
+ const SqlType& sqlType = expr->sqlType();
+ // data type
+ SQLSMALLINT desc_TYPE = sqlType.type();
+ rec.setField(SQL_DESC_TYPE, desc_TYPE);
+ SQLSMALLINT desc_CONCISE_TYPE = desc_TYPE;
+ rec.setField(SQL_DESC_CONCISE_TYPE, desc_CONCISE_TYPE);
+ SQLSMALLINT desc_DESC_DATETIME_INTERVAL_CODE = 0;
+ rec.setField(SQL_DESC_DATETIME_INTERVAL_CODE, desc_DESC_DATETIME_INTERVAL_CODE);
+ // nullable
+ SQLSMALLINT desc_NULLABLE = sqlType.nullable() ? SQL_NULLABLE : SQL_NO_NULLS;
+ rec.setField(SQL_DESC_NULLABLE, desc_NULLABLE);
+ // unsigned
+ SQLSMALLINT desc_UNSIGNED;
+ switch (sqlType.type()) {
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIGINT:
+ desc_UNSIGNED = sqlType.unSigned() ? SQL_TRUE : SQL_FALSE;
+ break;
+ default:
+ desc_UNSIGNED = SQL_TRUE; // thus spake microsoft
+ break;
+ }
+ rec.setField(SQL_DESC_UNSIGNED, desc_UNSIGNED);
+ // sizes
+ SQLUINTEGER desc_LENGTH = sqlType.length();
+ rec.setField(SQL_DESC_LENGTH, desc_LENGTH);
+ SQLINTEGER desc_OCTET_LENGTH = sqlType.size();
+ rec.setField(SQL_DESC_OCTET_LENGTH, desc_OCTET_LENGTH);
+ SQLINTEGER desc_DISPLAY_SIZE = sqlType.displaySize();
+ rec.setField(SQL_DESC_DISPLAY_SIZE, desc_DISPLAY_SIZE);
+ // name
+ ctx_assert(i < exprRow->m_aliasList.size());
+ const char* desc_NAME = exprRow->m_aliasList[i].c_str();
+ rec.setField(SQL_DESC_NAME, desc_NAME);
+ }
+ ctx_log3(("describe %u columns done", count));
+ stmtArea().setFunction(ctx, "SELECT CURSOR", SQL_DIAG_SELECT_CURSOR);
+}
+
+// Exec_query
+
+Exec_query::Code::~Code()
+{
+}
+
+Exec_query::Data::~Data()
+{
+ delete m_extRow;
+ m_extRow = 0;
+ delete[] m_extPos;
+ m_extPos = 0;
+}
+
+Exec_query::~Exec_query()
+{
+}
+
+const Exec_query*
+Exec_query::getRawQuery() const
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Exec_query::bind(Ctx& ctx)
+{
+ const Code& code = getCode();
+ const SqlSpecs& sqlSpecs = code.sqlSpecs();
+ const unsigned count = sqlSpecs.count();
+ // read ARD
+ DescArea& ard = descArea(Desc_usage_ARD);
+ const unsigned ardCount = ard.getCount();
+ // create specification row
+ ExtSpecs extSpecs(count);
+ for (unsigned i = 1; i <= count; i++) {
+ ExtType extType;
+ if (i <= ardCount) {
+ OdbcData descData;
+ DescRec& rec = ard.getRecord(i);
+ // check for unbound column
+ rec.getField(ctx, SQL_DESC_DATA_PTR, descData);
+ SQLPOINTER desc_DATA_PTR = descData.type() != OdbcData::Undef ? descData.pointer() : 0;
+ if (desc_DATA_PTR == 0) {
+ extType.setType(ctx, ExtType::Unbound);
+ } else {
+ rec.getField(ctx, SQL_DESC_TYPE, descData);
+ if (descData.type() == OdbcData::Undef) {
+ ctx.pushStatus(Error::Gen, "query column %u: external type not defined", i);
+ return;
+ }
+ SQLSMALLINT desc_TYPE = descData.smallint();
+ if (desc_TYPE == SQL_C_DEFAULT) {
+ if (i <= code.m_sqlSpecs.count())
+ desc_TYPE = code.m_sqlSpecs.getEntry(i).sqlType().sqlcdefault(ctx);
+ }
+ switch (desc_TYPE) {
+ case SQL_C_CHAR:
+ case SQL_C_BINARY:
+ case SQL_C_SHORT: // for sun.jdbc.odbc
+ case SQL_C_SSHORT:
+ case SQL_C_USHORT:
+ case SQL_C_LONG: // for sun.jdbc.odbc
+ case SQL_C_SLONG:
+ case SQL_C_ULONG:
+ case SQL_C_SBIGINT:
+ case SQL_C_UBIGINT:
+ case SQL_C_FLOAT:
+ case SQL_C_DOUBLE:
+ case SQL_C_TYPE_TIMESTAMP:
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "query column %u: unsupported external type %d", i, (int)desc_TYPE);
+ return;
+ }
+ extType.setType(ctx, static_cast<ExtType::Type>(desc_TYPE));
+ }
+ } else {
+ extType.setType(ctx, ExtType::Unbound);
+ }
+ const ExtSpec extSpec(extType);
+ extSpecs.setEntry(i, extSpec);
+ }
+ // create data row
+ ExtRow& extRow = *new ExtRow(extSpecs);
+ unsigned boundCount = 0;
+ for (unsigned i = 1; i <= count; i++) {
+ const ExtSpec& extSpec = extSpecs.getEntry(i);
+ if (extSpec.extType().type() != ExtType::Unbound) {
+ OdbcData descData;
+ DescRec& rec = ard.getRecord(i);
+ rec.getField(ctx, SQL_DESC_DATA_PTR, descData);
+ SQLPOINTER desc_DATA_PTR = descData.type() != OdbcData::Undef ? descData.pointer() : 0;
+ rec.getField(ctx, SQL_DESC_OCTET_LENGTH, descData);
+ SQLINTEGER desc_OCTET_LENGTH = descData.type() != OdbcData::Undef ? descData.integer() : 0;
+ rec.getField(ctx, SQL_DESC_INDICATOR_PTR, descData);
+ SQLINTEGER* desc_INDICATOR_PTR = descData.type() != OdbcData::Undef ? descData.integerPtr() : 0;
+ ctx_log4(("column %u: bind to 0x%x %d 0x%x", i, (unsigned)desc_DATA_PTR, (int)desc_OCTET_LENGTH, (unsigned)desc_INDICATOR_PTR));
+ ExtField extField(extSpec, desc_DATA_PTR, desc_OCTET_LENGTH, desc_INDICATOR_PTR, i);
+ extRow.setEntry(i, extField);
+ boundCount++;
+ } else {
+ ExtField extField(extSpec, i);
+ extRow.setEntry(i, extField);
+ }
+ }
+ Data& data = getData();
+ delete data.m_extRow;
+ data.m_extRow = &extRow;
+ ctx_log3(("bound %u out of %u columns", boundCount, count));
+}
+
+// execute and fetch
+
+void
+Exec_query::execute(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ execImpl(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ data.initState();
+ if (m_topLevel) {
+ stmtArea().setRowCount(ctx, data.getCount());
+ }
+}
+
+bool
+Exec_query::fetch(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ if (data.fetch(ctx, ctl)) {
+ if (m_topLevel) {
+ stmtArea().setRowCount(ctx, data.getCount());
+ }
+ if (data.m_extRow != 0) {
+ data.sqlRow().copyout(ctx, *data.m_extRow);
+ if (! ctx.ok())
+ return false;
+ }
+ if (data.m_extPos != 0) {
+ const unsigned count = code.sqlSpecs().count();
+ for (unsigned i = 0; i <= count; i++) {
+ data.m_extPos[i] = 0;
+ }
+ }
+ return true;
+ }
+ if (m_topLevel) {
+ stmtArea().setRowCount(ctx, data.getCount());
+ if (ctx.ok()) {
+ ctx.setCode(SQL_NO_DATA);
+ }
+ }
+ return false;
+}
+
+// odbc support
+
+void
+Exec_query::sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ const SqlSpecs& sqlSpecs = code.m_sqlSpecs;
+ const unsigned count = sqlSpecs.count();
+ if (columnNumber == 0 || columnNumber > count) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "column index %u is not within 1 to %u", (unsigned)columnNumber, count);
+ return;
+ }
+ // create positions array on first use
+ if (data.m_extPos == 0) {
+ data.m_extPos = new int[1 + count];
+ for (unsigned i = 0; i <= count; i++) {
+ data.m_extPos[i] = 0;
+ }
+ }
+ if (targetType == SQL_ARD_TYPE) {
+ // get type from ARD
+ DescArea& ard = descArea(Desc_usage_ARD);
+ const unsigned ardCount = ard.getCount();
+ if (columnNumber <= ardCount) {
+ OdbcData descData;
+ DescRec& rec = ard.getRecord(columnNumber);
+ rec.getField(ctx, SQL_DESC_CONCISE_TYPE, descData);
+ if (descData.type() != OdbcData::Undef) {
+ targetType = descData.smallint();
+ }
+ }
+ if (targetType == SQL_ARD_TYPE) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "output column %u type not bound - cannot use SQL_ARD_TYPE", (unsigned)columnNumber);
+ return;
+ }
+ }
+ ExtType extType;
+ if (targetValue != 0) {
+ extType.setType(ctx, static_cast<ExtType::Type>(targetType));
+ // check if supported
+ if (! ctx.ok())
+ return;
+ } else {
+ extType.setType(ctx, ExtType::Unbound);
+ }
+ ExtSpec extSpec(extType);
+ ExtField extField(extSpec, targetValue, bufferLength, strlen_or_Ind, columnNumber);
+ // copy out and update position
+ extField.setPos(data.m_extPos[columnNumber]);
+ const SqlRow& sqlRow = data.sqlRow();
+ const SqlField& sqlField = sqlRow.getEntry(columnNumber);
+ sqlField.copyout(ctx, extField);
+ data.m_extPos[columnNumber] = extField.getPos();
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query.hpp
new file mode 100644
index 00000000000..97f98f859ff
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query.hpp
@@ -0,0 +1,155 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_hpp
+#define ODBC_CODEGEN_Code_query_hpp
+
+#include <common/common.hpp>
+#include <common/DataRow.hpp>
+#include <common/ResultArea.hpp>
+#include "Code_stmt.hpp"
+
+class Plan_expr_row;
+class Plan_table;
+class Exec_expr_row;
+
+/**
+ * @class Plan_query
+ * @brief Base class for queries in PlanTree
+ */
+class Plan_query : public Plan_stmt {
+public:
+ Plan_query(Plan_root* root);
+ virtual ~Plan_query() = 0;
+ void describe(Ctx& ctx);
+ virtual Plan_expr_row* getRow();
+};
+
+inline
+Plan_query::Plan_query(Plan_root* root) :
+ Plan_stmt(root)
+{
+}
+
+/**
+ * @class Exec_query
+ * @brief Base class for executable queries.
+ *
+ * Executable queriable statement.
+ */
+class Exec_query : public Exec_stmt {
+public:
+ class Code : public Exec_stmt::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code() = 0;
+ const SqlSpecs& sqlSpecs() const;
+ protected:
+ friend class Exec_query;
+ const SqlSpecs& m_sqlSpecs; // subclass must contain
+ };
+ class Data : public Exec_stmt::Data, public ResultSet {
+ public:
+ Data(Exec_query* node, const SqlRow& sqlRow);
+ virtual ~Data() = 0;
+ const SqlRow& sqlRow() const;
+ ExtRow* extRow() const;
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ protected:
+ friend class Exec_query;
+ Exec_query* const m_node;
+ ExtRow* m_extRow; // output bindings
+ int* m_extPos; // positions for SQLGetData
+ };
+ Exec_query(Exec_root* root);
+ virtual ~Exec_query() = 0;
+ void bind(Ctx& ctx);
+ void execute(Ctx& ctx, Ctl& ctl);
+ bool fetch(Ctx& ctx, Ctl& ctl);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ virtual const Exec_query* getRawQuery() const;
+ // odbc support
+ void sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind);
+protected:
+ friend class Data;
+ virtual void execImpl(Ctx& ctx, Ctl& ctl) = 0;
+ virtual bool fetchImpl(Ctx& ctx, Ctl& ctl) = 0;
+};
+
+inline
+Exec_query::Code::Code(const SqlSpecs& sqlSpecs) :
+ m_sqlSpecs(sqlSpecs)
+{
+}
+
+inline const SqlSpecs&
+Exec_query::Code::sqlSpecs() const
+{
+ return m_sqlSpecs;
+}
+
+inline
+Exec_query::Data::Data(Exec_query* node, const SqlRow& sqlRow) :
+ ResultSet(sqlRow),
+ m_node(node),
+ m_extRow(0),
+ m_extPos(0)
+{
+}
+
+inline const SqlRow&
+Exec_query::Data::sqlRow() const
+{
+ return static_cast<const SqlRow&>(m_dataRow);
+}
+
+inline ExtRow*
+Exec_query::Data::extRow() const
+{
+ return m_extRow;
+}
+
+inline bool
+Exec_query::Data::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ return m_node->fetchImpl(ctx, ctl);
+}
+
+inline
+Exec_query::Exec_query(Exec_root* root) :
+ Exec_stmt(root)
+{
+}
+
+// children
+
+inline const Exec_query::Code&
+Exec_query::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query::Data&
+Exec_query::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.cpp
new file mode 100644
index 00000000000..f52c41df802
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.cpp
@@ -0,0 +1,177 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query_count.hpp"
+#include "Code_column.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_count
+
+Plan_query_count::~Plan_query_count()
+{
+}
+
+Plan_expr_row*
+Plan_query_count::getRow()
+{
+ ctx_assert(m_exprRow != 0);
+ return m_exprRow;
+}
+
+Plan_base*
+Plan_query_count::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_exprRow != 0);
+ ctl.m_aggrok = true;
+ ctl.m_aggrin = false;
+ m_exprRow->analyze(ctx, ctl);
+ ctl.m_aggrok = false;
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_count::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // create code for the row based on query code
+ ctx_assert(m_exprRow != 0);
+ ctl.m_execQuery = execQuery;
+ Exec_expr_row* execRow = static_cast<Exec_expr_row*>(m_exprRow->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execRow != 0);
+ Exec_query_count* exec = new Exec_query_count(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // re-use SqlSpecs from the row
+ const SqlSpecs& sqlSpecs = execRow->getCode().sqlSpecs();
+ Exec_query_count::Code& code = *new Exec_query_count::Code(sqlSpecs);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ exec->setRow(execRow);
+ return exec;
+}
+
+void
+Plan_query_count::print(Ctx& ctx)
+{
+ ctx.print(" [query_count");
+ Plan_base* a[] = { m_query, m_exprRow };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ ctx.print("]");
+}
+
+// Exec_query_count
+
+Exec_query_count::Code::~Code()
+{
+}
+
+Exec_query_count::Data::~Data()
+{
+}
+
+Exec_query_count::~Exec_query_count()
+{
+}
+
+const Exec_query*
+Exec_query_count::getRawQuery() const
+{
+ ctx_assert(m_query != 0);
+ return m_query;
+}
+
+void
+Exec_query_count::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ // allocate the row based on subquery data
+ ctx_assert(m_exprRow != 0);
+ ctl.m_query = m_query;
+ m_exprRow->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // re-use SqlRow from the expression row
+ const SqlRow& sqlRow = m_exprRow->getData().sqlRow();
+ Data& data = *new Data(this, sqlRow);
+ setData(data);
+}
+
+void
+Exec_query_count::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ // zero counters
+ ctx_assert(m_exprRow != 0);
+ m_exprRow->close(ctx);
+ data.m_done = false;
+ // execute the subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+}
+
+bool
+Exec_query_count::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ // returns one row only
+ if (data.m_done)
+ return false;
+ ctx_assert(m_query != 0 && m_exprRow != 0);
+ while (m_query->fetch(ctx, ctl)) {
+ // accumulate values
+ m_exprRow->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ }
+ data.m_done = true;
+ return true;
+}
+
+void
+Exec_query_count::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+ ctx_assert(m_exprRow != 0);
+ m_exprRow->close(ctx);
+}
+
+void
+Exec_query_count::print(Ctx& ctx)
+{
+ ctx.print(" [query_count");
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.hpp
new file mode 100644
index 00000000000..a094eba4519
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.hpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_count_hpp
+#define ODBC_CODEGEN_Code_query_count_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_root.hpp"
+
+class Ctx;
+
+/**
+ * @class Plan_query_count
+ * @brief Select count and other aggregates (no group by)
+ */
+class Plan_query_count : public Plan_query {
+public:
+ Plan_query_count(Plan_root* root);
+ virtual ~Plan_query_count();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setRow(Plan_expr_row* exprRow);
+protected:
+ Plan_expr_row* getRow();
+ Plan_query* m_query;
+ Plan_expr_row* m_exprRow;
+};
+
+inline
+Plan_query_count::Plan_query_count(Plan_root* root) :
+ Plan_query(root),
+ m_query(0),
+ m_exprRow(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_count::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_query_count::setRow(Plan_expr_row* exprRow)
+{
+ ctx_assert(exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+/**
+ * @class Exec_query_count
+ * @brief Select count and other aggregates (no group by)
+ */
+class Exec_query_count : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code();
+ protected:
+ friend class Exec_query_count;
+ // sets reference to Sqlspecs from the row
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_count* node, const SqlRow& sqlRow);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_count;
+ // sets reference to SqlRow from the row
+ bool m_done; // returns one row
+ };
+ Exec_query_count(Exec_root* root);
+ virtual ~Exec_query_count();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+ void setRow(Exec_expr_row* exprRow);
+ const Exec_query* getRawQuery() const;
+protected:
+ Exec_query* m_query;
+ Exec_expr_row* m_exprRow;
+};
+
+inline
+Exec_query_count::Code::Code(const SqlSpecs& sqlSpecs) :
+ Exec_query::Code(sqlSpecs)
+{
+}
+
+inline
+Exec_query_count::Data::Data(Exec_query_count* node, const SqlRow& sqlRow) :
+ Exec_query::Data(node, sqlRow)
+{
+}
+
+inline
+Exec_query_count::Exec_query_count(Exec_root* root) :
+ Exec_query(root),
+ m_query(0),
+ m_exprRow(0)
+{
+}
+
+// children
+
+inline const Exec_query_count::Code&
+Exec_query_count::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_count::Data&
+Exec_query_count::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_count::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Exec_query_count::setRow(Exec_expr_row* exprRow)
+{
+ ctx_assert(m_exprRow == 0 && exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.cpp
new file mode 100644
index 00000000000..4cbfbfe812d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.cpp
@@ -0,0 +1,204 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <algorithm>
+#include "Code_query_distinct.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_distinct
+
+Plan_query_distinct::~Plan_query_distinct()
+{
+}
+
+Plan_expr_row*
+Plan_query_distinct::getRow()
+{
+ ctx_assert(m_query != 0);
+ return m_query->getRow();
+}
+
+Plan_base*
+Plan_query_distinct::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_distinct::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // the exec node
+ Exec_query_distinct* exec = new Exec_query_distinct(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // re-use SqlSpecs from subquery
+ const Exec_query::Code& codeQuery = execQuery->getCode();
+ const SqlSpecs& sqlSpecs = codeQuery.sqlSpecs();
+ Exec_query_distinct::Code& code = *new Exec_query_distinct::Code(sqlSpecs);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_query_distinct::print(Ctx& ctx)
+{
+ ctx.print(" [query_distinct");
+ Plan_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_query_distinct
+
+Exec_query_distinct::Code::~Code()
+{
+}
+
+Exec_query_distinct::Data::~Data()
+{
+ for (DistinctList::iterator i = m_groupList.begin(); i != m_groupList.end(); i++) {
+ delete (*i).first;
+ }
+}
+
+Exec_query_distinct::~Exec_query_distinct()
+{
+}
+
+const Exec_query*
+Exec_query_distinct::getRawQuery() const
+{
+ ctx_assert(m_query != 0);
+ return m_query->getRawQuery();
+}
+
+void
+Exec_query_distinct::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ Data& data = *new Data(this, getCode().sqlSpecs());
+ setData(data);
+}
+
+void
+Exec_query_distinct::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+}
+
+bool
+DistinctLess::operator()(const SqlRow* s1, const SqlRow* s2) const
+{
+ ctx_assert(s1 != 0 && s2 != 0);
+ const SqlRow& r1 = *s1;
+ const SqlRow& r2 = *s2;
+ for (unsigned i = 1; i <= r1.count(); i++) {
+ const SqlField& f1 = r1.getEntry(i);
+ const SqlField& f2 = r2.getEntry(i);
+ // nulls last is default in oracle
+ const bool f1null = f1.sqlNull();
+ const bool f2null = f2.sqlNull();
+ if (f1null && f2null)
+ continue;
+ if (! f1null && f2null)
+ return true;
+ if (f1null && ! f2null)
+ return false;
+ if (f1.less(f2))
+ return true;
+ if (f2.less(f1))
+ return false;
+ }
+ return false;
+}
+
+bool
+Exec_query_distinct::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ ctx_assert(m_query != 0);
+ if (! data.m_grouped) {
+ // read and group all rows
+ while (m_query->fetch(ctx, ctl)) {
+ const SqlRow* dataRow = &m_query->getData().sqlRow();
+ DistinctList::iterator i = data.m_groupList.find(dataRow);
+ if (i != data.m_groupList.end())
+ continue;
+ unsigned index = data.m_count++;
+ dataRow = dataRow->copy();
+ const DistinctList::value_type groupPair(dataRow, index);
+ data.m_groupList.insert(groupPair);
+ data.m_groupVector.push_back(dataRow);
+ }
+ if (! ctx.ok())
+ return false;
+ data.m_index = 0;
+ data.m_grouped = true;
+ }
+ ctx_assert(data.m_count == data.m_groupVector.size());
+ if (data.m_index < data.m_count) {
+ const SqlRow* currRow = data.m_groupVector[data.m_index];
+ // make our SqlRow reference to it
+ for (unsigned i = 1; i <= data.m_sqlRow.count(); i++) {
+ const SqlField& currField = currRow->getEntry(i);
+ SqlSpec sqlSpec(currField.sqlSpec(), SqlSpec::Reference);
+ SqlField sqlField(sqlSpec, &currField);
+ data.m_sqlRow.setEntry(i, sqlField);
+ }
+ data.m_index++;
+ return true;
+ }
+ return false;
+}
+
+void
+Exec_query_distinct::close(Ctx& ctx)
+{
+ Data& data = getData();
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+ data.m_grouped = false;
+ data.m_count = 0;
+ for (DistinctList::iterator i = data.m_groupList.begin(); i != data.m_groupList.end(); i++) {
+ delete (*i).first;
+ }
+ data.m_groupList.clear();
+ data.m_groupVector.clear();
+}
+
+void
+Exec_query_distinct::print(Ctx& ctx)
+{
+ ctx.print(" [query_distinct");
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.hpp
new file mode 100644
index 00000000000..62c46bda901
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.hpp
@@ -0,0 +1,165 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_distinct_hpp
+#define ODBC_CODEGEN_Code_query_distinct_hpp
+
+#include <functional>
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_pred.hpp"
+
+/**
+ * @class Plan_query_distinct
+ * @brief Group-by node in PlanTree
+ */
+class Plan_query_distinct : public Plan_query {
+public:
+ Plan_query_distinct(Plan_root* root);
+ virtual ~Plan_query_distinct();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ Plan_expr_row* getRow();
+protected:
+ Plan_query* m_query;
+};
+
+inline
+Plan_query_distinct::Plan_query_distinct(Plan_root* root) :
+ Plan_query(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_distinct::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+/**
+ * Distinct preserves order of input rows so we use 2 data structures:
+ * map<row> = index and vector<index> = row (index >= 0).
+ */
+
+class Exec_query_distinct;
+
+struct DistinctLess : std::binary_function<const SqlRow*, const SqlRow*, bool> {
+ bool operator()(const SqlRow* s1, const SqlRow* s2) const;
+};
+
+typedef std::map<const SqlRow*, unsigned, DistinctLess> DistinctList;
+
+typedef std::vector<const SqlRow*> DistinctVector;
+
+/**
+ * @class Exec_query_distinct
+ * @brief Group-by node in ExecTree
+ */
+class Exec_query_distinct : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code();
+ protected:
+ friend class Exec_query_distinct;
+ // sets reference to Sqlspecs from subquery
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_distinct* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_distinct;
+ SqlRow m_sqlRow; // current row
+ bool m_grouped; // fetch and group-by done
+ unsigned m_count;
+ DistinctList m_groupList;
+ DistinctVector m_groupVector;
+ unsigned m_index;
+ };
+ Exec_query_distinct(Exec_root* root);
+ virtual ~Exec_query_distinct();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+ const Exec_query* getRawQuery() const;
+protected:
+ friend class Exec_query;
+ Exec_query* m_query;
+};
+
+inline
+Exec_query_distinct::Code::Code(const SqlSpecs& sqlSpecs) :
+ Exec_query::Code(sqlSpecs)
+{
+}
+
+inline
+Exec_query_distinct::Data::Data(Exec_query_distinct* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_grouped(false),
+ m_count(0),
+ m_index(0)
+{
+}
+
+inline
+Exec_query_distinct::Exec_query_distinct(Exec_root* root) :
+ Exec_query(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_query_distinct::Code&
+Exec_query_distinct::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_distinct::Data&
+Exec_query_distinct::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_distinct::setQuery(Exec_query* query)
+{
+ ctx_assert(m_query == 0 && query != 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.cpp
new file mode 100644
index 00000000000..934a24d182d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.cpp
@@ -0,0 +1,161 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_query_filter.hpp"
+#include "Code_query_join.hpp"
+#include "Code_query_scan.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_filter
+
+Plan_query_filter::~Plan_query_filter()
+{
+}
+
+Plan_base*
+Plan_query_filter::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_pred != 0);
+ m_pred->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_filter::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // generate code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // create code for the predicate based on query code
+ Exec_pred* execPred = 0;
+ ctl.m_execQuery = execQuery;
+ ctx_assert(m_topTable != 0);
+ ctl.m_topTable = m_topTable;
+ ctx_assert(m_pred != 0);
+ execPred = static_cast<Exec_pred*>(m_pred->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execPred != 0);
+ ctl.m_topTable = 0;
+ // re-use SqlSpecs from subquery
+ const Exec_query::Code& codeQuery = execQuery->getCode();
+ const SqlSpecs& sqlSpecs = codeQuery.sqlSpecs();
+ Exec_query_filter* exec = new Exec_query_filter(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ Exec_query_filter::Code& code = *new Exec_query_filter::Code(sqlSpecs);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ exec->setPred(execPred);
+ return exec;
+}
+
+void
+Plan_query_filter::print(Ctx& ctx)
+{
+ ctx.print(" [query_filter");
+ Plan_base* a[] = { m_query, m_pred };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_query_filter
+
+Exec_query_filter::Code::~Code()
+{
+}
+
+Exec_query_filter::Data::~Data()
+{
+}
+
+Exec_query_filter::~Exec_query_filter()
+{
+}
+
+void
+Exec_query_filter::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // allocate the predicate
+ ctl.m_query = m_query;
+ ctx_assert(m_pred != 0);
+ m_pred->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // re-use SqlRow from subquery
+ Exec_query::Data& dataQuery = m_query->getData();
+ Data& data = *new Data(this, dataQuery.sqlRow());
+ setData(data);
+}
+
+void
+Exec_query_filter::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+}
+
+bool
+Exec_query_filter::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ // invoke fetch on subquery until predicate is true
+ ctx_assert(m_query != 0);
+ while (m_query->fetch(ctx, ctl)) {
+ ctx_assert(m_pred != 0);
+ m_pred->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ if (m_pred->getData().getValue() == Pred_value_true) {
+ ctl.m_postEval = true;
+ m_pred->evaluate(ctx, ctl);
+ ctl.m_postEval = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+Exec_query_filter::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+ ctx_assert(m_pred != 0);
+ m_pred->close(ctx);
+}
+
+void
+Exec_query_filter::print(Ctx& ctx)
+{
+ ctx.print(" [query_filter");
+ Exec_base* a[] = { m_query, m_pred };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.hpp
new file mode 100644
index 00000000000..60cbf0f86a7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.hpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_filter_hpp
+#define ODBC_CODEGEN_Code_query_filter_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_table_list.hpp"
+#include "Code_pred.hpp"
+
+/**
+ * @class Plan_query_filter
+ * @brief Filter node in PlanTree
+ */
+class Plan_query_filter : public Plan_query {
+public:
+ Plan_query_filter(Plan_root* root);
+ virtual ~Plan_query_filter();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setPred(Plan_pred* pred);
+protected:
+ friend class Plan_select;
+ friend class Plan_update;
+ friend class Plan_delete;
+ Plan_query* m_query;
+ Plan_pred* m_pred;
+ Plan_table* m_topTable; // top level table for interpreted progs
+};
+
+inline
+Plan_query_filter::Plan_query_filter(Plan_root* root) :
+ Plan_query(root),
+ m_query(0),
+ m_pred(0),
+ m_topTable(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_filter::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_query_filter::setPred(Plan_pred* pred)
+{
+ ctx_assert(pred != 0);
+ m_pred = pred;
+}
+
+/**
+ * @class Exec_query_filter
+ * @brief Filter node in ExecTree
+ */
+class Exec_query_filter : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code();
+ protected:
+ friend class Exec_query_filter;
+ // sets reference to SqlSpecs from subquery
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_filter* node, const SqlRow& sqlRow);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_filter;
+ // sets reference to SqlRow from subquery
+ };
+ Exec_query_filter(Exec_root* root);
+ virtual ~Exec_query_filter();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+ void setPred(Exec_pred* pred);
+protected:
+ Exec_query* m_query;
+ Exec_pred* m_pred;
+};
+
+inline
+Exec_query_filter::Code::Code(const SqlSpecs& sqlSpecs) :
+ Exec_query::Code(sqlSpecs)
+{
+}
+
+inline
+Exec_query_filter::Data::Data(Exec_query_filter* node, const SqlRow& sqlRow) :
+ Exec_query::Data(node, sqlRow)
+{
+}
+
+inline
+Exec_query_filter::Exec_query_filter(Exec_root* root) :
+ Exec_query(root),
+ m_query(0),
+ m_pred(0)
+{
+}
+
+// children
+
+inline const Exec_query_filter::Code&
+Exec_query_filter::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_filter::Data&
+Exec_query_filter::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_filter::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Exec_query_filter::setPred(Exec_pred* pred)
+{
+ ctx_assert(pred != 0);
+ m_pred = pred;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.cpp
new file mode 100644
index 00000000000..c3019efaa85
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.cpp
@@ -0,0 +1,301 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <algorithm>
+#include "Code_query_group.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_group
+
+Plan_query_group::~Plan_query_group()
+{
+}
+
+Plan_expr_row*
+Plan_query_group::getRow()
+{
+ ctx_assert(m_dataRow != 0);
+ return m_dataRow;
+}
+
+Plan_base*
+Plan_query_group::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_dataRow != 0);
+ m_dataRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_groupRow != 0);
+ m_groupRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ if (m_havingPred != 0) {
+ ctl.m_having = true;
+ m_havingPred->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctl.m_having = false;
+ }
+ return this;
+}
+
+Exec_base*
+Plan_query_group::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // create code for the rows based on query code
+ ctl.m_execQuery = execQuery;
+ ctx_assert(m_dataRow != 0);
+ Exec_expr_row* execDataRow = static_cast<Exec_expr_row*>(m_dataRow->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execDataRow != 0);
+ ctx_assert(m_groupRow != 0);
+ Exec_expr_row* execGroupRow = static_cast<Exec_expr_row*>(m_groupRow->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execGroupRow != 0);
+ Exec_pred* execHavingPred = 0;
+ if (m_havingPred != 0) {
+ ctl.m_having = true;
+ execHavingPred = static_cast<Exec_pred*>(m_havingPred->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execHavingPred != 0);
+ ctl.m_having = false;
+ }
+ // the exec node
+ Exec_query_group* exec = new Exec_query_group(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // re-use SqlSpecs from data row
+ const SqlSpecs& sqlSpecs = execDataRow->getCode().sqlSpecs();
+ Exec_query_group::Code& code = *new Exec_query_group::Code(sqlSpecs);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ exec->setDataRow(execDataRow);
+ exec->setGroupRow(execGroupRow);
+ if (execHavingPred != 0)
+ exec->setHavingPred(execHavingPred);
+ return exec;
+}
+
+void
+Plan_query_group::print(Ctx& ctx)
+{
+ ctx.print(" [query_group");
+ Plan_base* a[] = { m_query, m_dataRow, m_groupRow };
+ printList(ctx, a, 3);
+ ctx.print("]");
+}
+
+// Exec_query_group
+
+Exec_query_group::Code::~Code()
+{
+}
+
+Exec_query_group::Data::~Data()
+{
+ for (GroupList::iterator i = m_groupList.begin(); i != m_groupList.end(); i++) {
+ delete (*i).first;
+ }
+}
+
+Exec_query_group::~Exec_query_group()
+{
+}
+
+const Exec_query*
+Exec_query_group::getRawQuery() const
+{
+ ctx_assert(m_query != 0);
+ return m_query;
+}
+
+void
+Exec_query_group::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // allocate rows based on subquery data
+ ctl.m_query = m_query;
+ ctx_assert(m_dataRow != 0);
+ m_dataRow->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_assert(m_groupRow != 0);
+ m_groupRow->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (m_havingPred != 0) {
+ m_havingPred->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ Data& data = *new Data(this, getCode().sqlSpecs());
+ setData(data);
+}
+
+void
+Exec_query_group::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+}
+
+bool
+GroupLess::operator()(const SqlRow* s1, const SqlRow* s2) const
+{
+ ctx_assert(s1 != 0 && s2 != 0);
+ const SqlRow& r1 = *s1;
+ const SqlRow& r2 = *s2;
+ for (unsigned i = 1; i <= r1.count(); i++) {
+ const SqlField& f1 = r1.getEntry(i);
+ const SqlField& f2 = r2.getEntry(i);
+ // nulls last is default in oracle
+ const bool f1null = f1.sqlNull();
+ const bool f2null = f2.sqlNull();
+ if (f1null && f2null)
+ continue;
+ if (! f1null && f2null)
+ return true;
+ if (f1null && ! f2null)
+ return false;
+ if (f1.less(f2))
+ return true;
+ if (f2.less(f1))
+ return false;
+ }
+ return false;
+}
+
+bool
+Exec_query_group::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ ctx_assert(m_query != 0 && m_groupRow != 0);
+ if (! data.m_grouped) {
+ // read and group all rows
+ while (m_query->fetch(ctx, ctl)) {
+ // evaluate and insert group-by values
+ m_groupRow->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ const SqlRow* groupRow = 0;
+ unsigned index = 0;
+ bool init;
+ GroupList::iterator i = data.m_groupList.find(&m_groupRow->getData().sqlRow());
+ if (i == data.m_groupList.end()) {
+ groupRow = m_groupRow->getData().sqlRow().copy();
+ index = ++data.m_count;
+ const GroupList::value_type groupPair(groupRow, index);
+ data.m_groupList.insert(groupPair);
+ init = true;
+ } else {
+ groupRow = (*i).first;
+ index = (*i).second;
+ ctx_assert(groupRow != 0 && index != 0);
+ init = false;
+ }
+ // evaluate rows saving expression values at index position
+ ctl.m_groupIndex = index;
+ ctl.m_groupInit = init;
+ m_dataRow->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ if (m_havingPred != 0) {
+ m_havingPred->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ }
+ if (ctl.m_sortRow != 0) {
+ ctl.m_sortRow->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ }
+ ctl.m_groupIndex = 0;
+ }
+ if (! ctx.ok())
+ return false;
+ data.m_iterator = data.m_groupList.begin();
+ data.m_grouped = true;
+ }
+ while (data.m_iterator != data.m_groupList.end()) {
+ const SqlRow* groupRow = (*data.m_iterator).first;
+ const unsigned index = (*data.m_iterator).second;
+ ctx_assert(groupRow != 0 && index != 0);
+ if (m_havingPred != 0) {
+ Pred_value v = m_havingPred->getData().groupValue(index);
+ if (v != Pred_value_true) {
+ data.m_iterator++;
+ continue;
+ }
+ }
+ // make our SqlRow reference to the saved values
+ for (unsigned i = 1; i <= data.m_sqlRow.count(); i++) {
+ const SqlField& currField = m_dataRow->getExpr(i)->getData().groupField(index);
+ SqlSpec sqlSpec(currField.sqlSpec(), SqlSpec::Reference);
+ SqlField sqlField(sqlSpec, &currField);
+ data.m_sqlRow.setEntry(i, sqlField);
+ }
+ // send group index up for possible order by
+ ctl.m_groupIndex = index;
+ data.m_iterator++;
+ return true;
+ }
+ return false;
+}
+
+void
+Exec_query_group::close(Ctx& ctx)
+{
+ Data& data = getData();
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+ ctx_assert(m_dataRow != 0);
+ m_dataRow->close(ctx);
+ ctx_assert(m_groupRow != 0);
+ m_groupRow->close(ctx);
+ if (m_havingPred != 0)
+ m_havingPred->close(ctx);
+ data.m_grouped = false;
+ data.m_count = 0;
+ for (GroupList::iterator i = data.m_groupList.begin(); i != data.m_groupList.end(); i++) {
+ delete (*i).first;
+ }
+ data.m_groupList.clear();
+}
+
+void
+Exec_query_group::print(Ctx& ctx)
+{
+ ctx.print(" [query_group");
+ Exec_base* a[] = { m_query, m_dataRow, m_groupRow };
+ printList(ctx, a, 3);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.hpp
new file mode 100644
index 00000000000..e79022c5284
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.hpp
@@ -0,0 +1,221 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_group_hpp
+#define ODBC_CODEGEN_Code_query_group_hpp
+
+#include <functional>
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_pred.hpp"
+
+/**
+ * @class Plan_query_group
+ * @brief Group-by node in PlanTree
+ */
+class Plan_query_group : public Plan_query {
+public:
+ Plan_query_group(Plan_root* root);
+ virtual ~Plan_query_group();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setDataRow(Plan_expr_row* dataRow);
+ void setGroupRow(Plan_expr_row* groupRow);
+ void setHavingPred(Plan_pred* havingPred);
+ Plan_expr_row* getRow();
+protected:
+ Plan_query* m_query;
+ Plan_expr_row* m_dataRow;
+ Plan_expr_row* m_groupRow;
+ Plan_pred* m_havingPred;
+};
+
+inline
+Plan_query_group::Plan_query_group(Plan_root* root) :
+ Plan_query(root),
+ m_query(0),
+ m_dataRow(0),
+ m_groupRow(0),
+ m_havingPred(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_group::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_query_group::setDataRow(Plan_expr_row* dataRow)
+{
+ ctx_assert(dataRow != 0);
+ m_dataRow = dataRow;
+}
+
+inline void
+Plan_query_group::setGroupRow(Plan_expr_row* groupRow)
+{
+ ctx_assert(groupRow != 0);
+ m_groupRow = groupRow;
+}
+
+inline void
+Plan_query_group::setHavingPred(Plan_pred* havingPred)
+{
+ ctx_assert(havingPred != 0);
+ m_havingPred = havingPred;
+}
+
+/**
+ * Group-by uses a std::map. Key is values grouped by. Data is unique index
+ * (starting at 1) into arrays in expression data.
+ */
+
+class Exec_query_group;
+
+struct GroupLess : std::binary_function<const SqlRow*, const SqlRow*, bool> {
+ bool operator()(const SqlRow* s1, const SqlRow* s2) const;
+};
+
+typedef std::map<const SqlRow*, unsigned, GroupLess> GroupList;
+
+/**
+ * @class Exec_query_group
+ * @brief Group-by node in ExecTree
+ */
+class Exec_query_group : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code();
+ protected:
+ friend class Exec_query_group;
+ // sets reference to Sqlspecs from subquery
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_group* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_group;
+ SqlRow m_sqlRow; // current row
+ bool m_grouped; // fetch and group-by done
+ unsigned m_count;
+ GroupList m_groupList;
+ GroupList::iterator m_iterator;
+ };
+ Exec_query_group(Exec_root* root);
+ virtual ~Exec_query_group();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+ void setDataRow(Exec_expr_row* dataRow);
+ void setGroupRow(Exec_expr_row* groupRow);
+ void setHavingPred(Exec_pred* havingPred);
+ const Exec_query* getRawQuery() const;
+protected:
+ friend class Exec_query;
+ Exec_query* m_query;
+ Exec_expr_row* m_dataRow;
+ Exec_expr_row* m_groupRow;
+ Exec_pred* m_havingPred;
+};
+
+inline
+Exec_query_group::Code::Code(const SqlSpecs& sqlSpecs) :
+ Exec_query::Code(sqlSpecs)
+{
+}
+
+inline
+Exec_query_group::Data::Data(Exec_query_group* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_grouped(false),
+ m_count(0)
+{
+}
+
+inline
+Exec_query_group::Exec_query_group(Exec_root* root) :
+ Exec_query(root),
+ m_query(0),
+ m_dataRow(0),
+ m_groupRow(0),
+ m_havingPred(0)
+{
+}
+
+// children
+
+inline const Exec_query_group::Code&
+Exec_query_group::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_group::Data&
+Exec_query_group::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_group::setQuery(Exec_query* query)
+{
+ ctx_assert(m_query == 0 && query != 0);
+ m_query = query;
+}
+
+inline void
+Exec_query_group::setDataRow(Exec_expr_row* dataRow)
+{
+ ctx_assert(m_dataRow == 0 && dataRow != 0);
+ m_dataRow = dataRow;
+}
+
+inline void
+Exec_query_group::setGroupRow(Exec_expr_row* groupRow)
+{
+ ctx_assert(m_groupRow == 0 && groupRow != 0);
+ m_groupRow = groupRow;
+}
+
+inline void
+Exec_query_group::setHavingPred(Exec_pred* havingPred)
+{
+ ctx_assert(m_havingPred == 0 && havingPred != 0);
+ m_havingPred = havingPred;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.cpp
new file mode 100644
index 00000000000..ee19d6123cc
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.cpp
@@ -0,0 +1,186 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query_index.hpp"
+#include "Code_column.hpp"
+#include "Code_expr.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_index
+
+Plan_query_index::~Plan_query_index()
+{
+}
+
+Plan_base*
+Plan_query_index::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_index::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // set up
+ ctx_assert(m_table != 0 && m_index != 0);
+ const BaseString& tableName = m_table->getName();
+ ctx_assert(m_index->m_dictIndex != 0);
+ const DictIndex& dictIndex = *m_index->m_dictIndex;
+ const BaseString& indexName = dictIndex.getName();
+ const unsigned keyCount = m_index->m_keyCount;
+ const ColumnVector& columns = m_table->exprColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_query_index::Code& code = *new Exec_query_index::Code(keyCount, attrCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ code.m_indexName = strcpy(new char[indexName.length() + 1], indexName.c_str());
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictIndex.getColumn(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = k - 1; // index column order
+ }
+ // matching expressions
+ ctx_assert(m_index->m_keyFound);
+ const ExprVector& keyEq = m_index->m_keyEq;
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // queried attributes
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ const SqlType& sqlType = dictColumn.sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_sqlSpecs.setEntry(i, sqlSpec);
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_query_index* exec = new Exec_query_index(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_query_index::print(Ctx& ctx)
+{
+ ctx.print(" [query_index");
+ Plan_base* a[] = { m_table };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_query_index
+
+Exec_query_index::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+ delete[] m_attrId;
+}
+
+Exec_query_index::Data::~Data()
+{
+ delete[] m_recAttr;
+}
+
+Exec_query_index::~Exec_query_index()
+{
+}
+
+void
+Exec_query_index::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // create data
+ Data& data = *new Data(this, code.sqlSpecs());
+ setData(data);
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ // needed for isNULL
+ data.m_recAttr = new NdbRecAttr* [1 + code.m_attrCount];
+ for (unsigned i = 0; i <= code.m_attrCount; i++) {
+ data.m_recAttr[i] = 0;
+ }
+}
+
+void
+Exec_query_index::close(Ctx& ctx)
+{
+ Data& data = getData();
+ if (data.m_con != 0) {
+ Ndb* const ndb = ndbObject();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ data.m_done = true;
+ ctx_log2(("lookup closed at statement close"));
+ }
+}
+
+void
+Exec_query_index::print(Ctx& ctx)
+{
+ ctx.print(" [query_index");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" keyId=");
+ for (unsigned i = 1; i <= code.m_keyCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_keyId[i]);
+ }
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ ctx.print(" table=%s", code.m_tableName);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.hpp
new file mode 100644
index 00000000000..87affd50580
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.hpp
@@ -0,0 +1,160 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_index_hpp
+#define ODBC_CODEGEN_Code_query_index_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+
+class Ctx;
+class StmtArea;
+class NdbConnection;
+class NdbOperation;
+class NdbRecAttr;
+
+/**
+ * @class Plan_query_index
+ * @brief Full select (no where clause)
+ */
+class Plan_query_index : public Plan_query {
+public:
+ Plan_query_index(Plan_root* root);
+ virtual ~Plan_query_index();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table, Plan_table::Index* index);
+protected:
+ Plan_table* m_table;
+ Plan_table::Index* m_index;
+};
+
+inline
+Plan_query_index::Plan_query_index(Plan_root* root) :
+ Plan_query(root),
+ m_table(0),
+ m_index(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_index::setTable(Plan_table* table, Plan_table::Index* index)
+{
+ ctx_assert(table != 0 && index != 0 && index == &table->m_indexList[index->m_pos] && index->m_pos != 0);
+ m_table = table;
+ m_index = index;
+}
+
+/**
+ * @class Exec_query_index
+ * @brief Full select (no where clause)
+ */
+class Exec_query_index : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(unsigned keyCount, unsigned attrCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_query_index;
+ friend class Exec_query_index;
+ const char* m_tableName;
+ const char* m_indexName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ unsigned m_attrCount;
+ SqlSpecs m_sqlSpecs;
+ NdbAttrId* m_attrId;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_index* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_index;
+ SqlRow m_sqlRow;
+ NdbConnection* m_con;
+ NdbOperation* m_op;
+ NdbRecAttr** m_recAttr;
+ bool m_done; // returns one row
+ };
+ Exec_query_index(Exec_root* root);
+ virtual ~Exec_query_index();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_query_index::Code::Code(unsigned keyCount, unsigned attrCount) :
+ Exec_query::Code(m_sqlSpecs),
+ m_tableName(0),
+ m_indexName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_attrCount(attrCount),
+ m_sqlSpecs(attrCount),
+ m_attrId(0)
+{
+}
+
+inline
+Exec_query_index::Data::Data(Exec_query_index* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_con(0),
+ m_op(0),
+ m_recAttr(0),
+ m_done(false)
+{
+}
+
+inline
+Exec_query_index::Exec_query_index(Exec_root* root) :
+ Exec_query(root)
+{
+}
+
+// children
+
+inline const Exec_query_index::Code&
+Exec_query_index::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_index::Data&
+Exec_query_index::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.cpp
new file mode 100644
index 00000000000..89aafe13610
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.cpp
@@ -0,0 +1,192 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_query.hpp"
+#include "Code_query_join.hpp"
+#include "Code_query_scan.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_join
+
+Plan_query_join::~Plan_query_join()
+{
+}
+
+Plan_base*
+Plan_query_join::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_inner != 0);
+ m_inner->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_outer != 0);
+ m_outer->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_join::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // generate code for subqueries
+ ctx_assert(m_inner != 0);
+ Exec_query* execInner = static_cast<Exec_query*>(m_inner->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execInner != 0);
+ ctx_assert(m_outer != 0);
+ ctl.m_execQuery = execInner;
+ Exec_query* execOuter = static_cast<Exec_query*>(m_outer->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execOuter != 0);
+ // combine sql specs from subqueries
+ const SqlSpecs& specsInner = execInner->getCode().sqlSpecs();
+ const SqlSpecs& specsOuter = execOuter->getCode().sqlSpecs();
+ SqlSpecs sqlSpecs(specsInner.count() + specsOuter.count());
+ for (unsigned i = 1; i <= specsInner.count(); i++) {
+ const SqlSpec sqlSpec(specsInner.getEntry(i), SqlSpec::Reference);
+ sqlSpecs.setEntry(i, sqlSpec);
+ }
+ for (unsigned i = 1; i <= specsOuter.count(); i++) {
+ const SqlSpec sqlSpec(specsOuter.getEntry(i), SqlSpec::Reference);
+ sqlSpecs.setEntry(specsInner.count() + i, sqlSpec);
+ }
+ // create the code
+ Exec_query_join* exec = new Exec_query_join(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setInner(execInner);
+ exec->setOuter(execOuter);
+ Exec_query_join::Code& code = *new Exec_query_join::Code(sqlSpecs);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_query_join::print(Ctx& ctx)
+{
+ ctx.print(" [query_join");
+ Plan_base* a[] = { m_inner, m_outer };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_query_join
+
+Exec_query_join::Code::~Code()
+{
+}
+
+Exec_query_join::Data::~Data()
+{
+}
+
+Exec_query_join::~Exec_query_join()
+{
+}
+
+void
+Exec_query_join::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the subqueries
+ ctx_assert(m_inner != 0);
+ m_inner->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_assert(m_outer != 0);
+ ctl.m_query = m_inner;
+ m_outer->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // combine data rows from subqueries
+ const Code& code = getCode();
+ const SqlRow& rowInner = m_inner->getData().sqlRow();
+ const SqlRow& rowOuter = m_outer->getData().sqlRow();
+ SqlRow sqlRow(code.m_sqlSpecs);
+ for (unsigned i = 1; i <= rowInner.count(); i++) {
+ const SqlSpec& sqlSpec = code.m_sqlSpecs.getEntry(i);
+ const SqlField sqlField(sqlSpec, &rowInner.getEntry(i));
+ sqlRow.setEntry(i, sqlField);
+ }
+ for (unsigned i = 1; i <= rowOuter.count(); i++) {
+ const SqlSpec& sqlSpec = code.m_sqlSpecs.getEntry(rowInner.count() + i);
+ const SqlField sqlField(sqlSpec, &rowOuter.getEntry(i));
+ sqlRow.setEntry(rowInner.count() + i, sqlField);
+ }
+ // create the data
+ Data& data = *new Data(this, sqlRow);
+ setData(data);
+}
+
+void
+Exec_query_join::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ // execute only inner query
+ ctx_assert(m_inner != 0);
+ m_inner->execute(ctx, ctl);
+}
+
+bool
+Exec_query_join::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_inner != 0);
+ ctx_assert(m_outer != 0);
+ if (getData().getState() == ResultSet::State_init) {
+ // fetch first row from inner
+ if (! m_inner->fetch(ctx, ctl))
+ return false;
+ }
+ while (1) {
+ if (m_outer->getData().getState() == ResultSet::State_end) {
+ // execute or re-execute outer
+ Ctl ctl(0);
+ m_outer->close(ctx);
+ if (! ctx.ok())
+ return false;
+ m_outer->execute(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ }
+ if (! m_outer->fetch(ctx, ctl)) {
+ if (! ctx.ok())
+ return false;
+ // fetch next row from inner
+ if (! m_inner->fetch(ctx, ctl))
+ return false;
+ }
+ else
+ return true;
+ }
+}
+
+void
+Exec_query_join::close(Ctx& ctx)
+{
+ ctx_assert(m_inner != 0);
+ m_inner->close(ctx);
+ ctx_assert(m_outer != 0);
+ m_outer->close(ctx);
+}
+
+void
+Exec_query_join::print(Ctx& ctx)
+{
+ ctx.print(" [query_join");
+ Exec_base* a[] = { m_inner, m_outer };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.hpp
new file mode 100644
index 00000000000..f6ac9205329
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.hpp
@@ -0,0 +1,159 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_join_hpp
+#define ODBC_CODEGEN_Code_query_join_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_table_list.hpp"
+#include "Code_pred.hpp"
+
+/**
+ * @class Plan_query_join
+ * @brief Filter node in PlanTree
+ */
+class Plan_query_join : public Plan_query {
+public:
+ Plan_query_join(Plan_root* root);
+ virtual ~Plan_query_join();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setInner(Plan_query* query);
+ void setOuter(Plan_query* query);
+protected:
+ Plan_query* m_inner;
+ Plan_query* m_outer;
+};
+
+inline
+Plan_query_join::Plan_query_join(Plan_root* root) :
+ Plan_query(root),
+ m_inner(0),
+ m_outer(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_join::setInner(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_inner = query;
+}
+
+inline void
+Plan_query_join::setOuter(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_outer = query;
+}
+
+/**
+ * @class Exec_query_join
+ * @brief Filter node in ExecTree
+ */
+class Exec_query_join : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code();
+ protected:
+ friend class Exec_query_join;
+ SqlSpecs m_sqlSpecs;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_join* node, const SqlRow& sqlRow);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_join;
+ SqlRow m_sqlRow;
+ };
+ Exec_query_join(Exec_root* root);
+ virtual ~Exec_query_join();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setInner(Exec_query* query);
+ void setOuter(Exec_query* query);
+protected:
+ Exec_query* m_inner;
+ Exec_query* m_outer;
+};
+
+inline
+Exec_query_join::Code::Code(const SqlSpecs& sqlSpecs) :
+ Exec_query::Code(m_sqlSpecs),
+ m_sqlSpecs(sqlSpecs)
+{
+}
+
+inline
+Exec_query_join::Data::Data(Exec_query_join* node, const SqlRow& sqlRow) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlRow)
+{
+}
+
+inline
+Exec_query_join::Exec_query_join(Exec_root* root) :
+ Exec_query(root),
+ m_inner(0),
+ m_outer(0)
+{
+}
+
+// children
+
+inline const Exec_query_join::Code&
+Exec_query_join::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_join::Data&
+Exec_query_join::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_join::setInner(Exec_query* query)
+{
+ ctx_assert(query != 0);
+ m_inner = query;
+}
+
+inline void
+Exec_query_join::setOuter(Exec_query* query)
+{
+ ctx_assert(query != 0);
+ m_outer = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.cpp
new file mode 100644
index 00000000000..bad4199190b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.cpp
@@ -0,0 +1,184 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query_lookup.hpp"
+#include "Code_column.hpp"
+#include "Code_expr.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_lookup
+
+Plan_query_lookup::~Plan_query_lookup()
+{
+}
+
+Plan_base*
+Plan_query_lookup::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_lookup::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // set up
+ ctx_assert(m_table != 0);
+ const BaseString& tableName = m_table->getName();
+ const DictTable& dictTable = m_table->dictTable();
+ const unsigned keyCount = dictTable.keyCount();
+ const ColumnVector& columns = m_table->exprColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_query_lookup::Code& code = *new Exec_query_lookup::Code(keyCount, attrCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictTable.getKey(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = keyColumn->getAttrId();
+ }
+ // matching expressions
+ const Plan_table::Index& index = m_table->m_indexList[0];
+ ctx_assert(index.m_keyFound);
+ const ExprVector& keyEq = index.m_keyEq;
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // queried attributes
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ const SqlType& sqlType = dictColumn.sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_sqlSpecs.setEntry(i, sqlSpec);
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_query_lookup* exec = new Exec_query_lookup(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_query_lookup::print(Ctx& ctx)
+{
+ ctx.print(" [query_lookup");
+ Plan_base* a[] = { m_table };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_query_lookup
+
+Exec_query_lookup::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+ delete[] m_attrId;
+}
+
+Exec_query_lookup::Data::~Data()
+{
+ delete[] m_recAttr;
+}
+
+Exec_query_lookup::~Exec_query_lookup()
+{
+}
+
+void
+Exec_query_lookup::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // create data
+ Data& data = *new Data(this, code.sqlSpecs());
+ setData(data);
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ // needed for isNULL
+ data.m_recAttr = new NdbRecAttr* [1 + code.m_attrCount];
+ for (unsigned i = 0; i <= code.m_attrCount; i++) {
+ data.m_recAttr[i] = 0;
+ }
+}
+
+void
+Exec_query_lookup::close(Ctx& ctx)
+{
+ Data& data = getData();
+ if (data.m_con != 0) {
+ Ndb* const ndb = ndbObject();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ data.m_done = true;
+ ctx_log2(("lookup closed at statement close"));
+ }
+}
+
+void
+Exec_query_lookup::print(Ctx& ctx)
+{
+ ctx.print(" [query_lookup");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" keyId=");
+ for (unsigned i = 1; i <= code.m_keyCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_keyId[i]);
+ }
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ ctx.print(" table=%s", code.m_tableName);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.hpp
new file mode 100644
index 00000000000..e66623d4030
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.hpp
@@ -0,0 +1,155 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_lookup_hpp
+#define ODBC_CODEGEN_Code_query_lookup_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+
+class Ctx;
+class StmtArea;
+class NdbConnection;
+class NdbOperation;
+class NdbRecAttr;
+
+/**
+ * @class Plan_query_lookup
+ * @brief Full select (no where clause)
+ */
+class Plan_query_lookup : public Plan_query {
+public:
+ Plan_query_lookup(Plan_root* root);
+ virtual ~Plan_query_lookup();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+protected:
+ Plan_table* m_table;
+};
+
+inline
+Plan_query_lookup::Plan_query_lookup(Plan_root* root) :
+ Plan_query(root),
+ m_table(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_lookup::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+/**
+ * @class Exec_query_lookup
+ * @brief Full select (no where clause)
+ */
+class Exec_query_lookup : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(unsigned keyCount, unsigned attrCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_query_lookup;
+ friend class Exec_query_lookup;
+ char* m_tableName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ unsigned m_attrCount;
+ SqlSpecs m_sqlSpecs;
+ NdbAttrId* m_attrId;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_lookup* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_lookup;
+ SqlRow m_sqlRow;
+ NdbConnection* m_con;
+ NdbOperation* m_op;
+ NdbRecAttr** m_recAttr;
+ bool m_done; // returns one row
+ };
+ Exec_query_lookup(Exec_root* root);
+ virtual ~Exec_query_lookup();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_query_lookup::Code::Code(unsigned keyCount, unsigned attrCount) :
+ Exec_query::Code(m_sqlSpecs),
+ m_tableName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_attrCount(attrCount),
+ m_sqlSpecs(attrCount),
+ m_attrId(0)
+{
+}
+
+inline
+Exec_query_lookup::Data::Data(Exec_query_lookup* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_con(0),
+ m_op(0),
+ m_recAttr(0),
+ m_done(false)
+{
+}
+
+inline
+Exec_query_lookup::Exec_query_lookup(Exec_root* root) :
+ Exec_query(root)
+{
+}
+
+// children
+
+inline const Exec_query_lookup::Code&
+Exec_query_lookup::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_lookup::Data&
+Exec_query_lookup::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.cpp
new file mode 100644
index 00000000000..54043ce3d5d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.cpp
@@ -0,0 +1,184 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_query_project.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_project
+
+Plan_query_project::~Plan_query_project()
+{
+}
+
+Plan_base*
+Plan_query_project::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_exprRow != 0);
+ ctl.m_aggrok = true;
+ ctl.m_aggrin = false;
+ m_exprRow->analyze(ctx, ctl);
+ ctl.m_aggrok = false;
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Plan_expr_row*
+Plan_query_project::getRow()
+{
+ ctx_assert(m_exprRow != 0);
+ return m_exprRow;
+}
+
+Exec_base*
+Plan_query_project::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // create code for the row based on query code
+ ctx_assert(m_exprRow != 0);
+ ctl.m_execQuery = execQuery;
+ Exec_expr_row* execRow = static_cast<Exec_expr_row*>(m_exprRow->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execRow != 0);
+ Exec_query_project* exec = new Exec_query_project(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // re-use SqlSpecs from the row
+ const SqlSpecs& sqlSpecs = execRow->getCode().sqlSpecs();
+ Exec_query_project::Code& code = *new Exec_query_project::Code(sqlSpecs);
+ code.m_limitOff = m_limitOff;
+ code.m_limitCnt = m_limitCnt;
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ exec->setRow(execRow);
+ return exec;
+}
+
+void
+Plan_query_project::print(Ctx& ctx)
+{
+ ctx.print(" [query_project");
+ Plan_base* a[] = { m_query, m_exprRow };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_query_project
+
+Exec_query_project::Code::~Code()
+{
+}
+
+Exec_query_project::Data::~Data()
+{
+}
+
+Exec_query_project::~Exec_query_project()
+{
+}
+
+const Exec_query*
+Exec_query_project::getRawQuery() const
+{
+ ctx_assert(m_query != 0);
+ return m_query;
+}
+
+void
+Exec_query_project::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // allocate the row based on subquery data
+ ctx_assert(m_exprRow != 0);
+ ctl.m_query = m_query;
+ m_exprRow->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // re-use SqlRow from the expression row
+ const SqlRow& sqlRow = m_exprRow->getData().sqlRow();
+ Data& data = *new Data(this, sqlRow);
+ setData(data);
+}
+
+void
+Exec_query_project::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+}
+
+bool
+Exec_query_project::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ ctx_assert(m_query != 0);
+ while (1) {
+ if (! m_query->fetch(ctx, ctl))
+ return false;
+ ctx_assert(m_exprRow != 0);
+ m_exprRow->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ ctl.m_postEval = true;
+ m_exprRow->evaluate(ctx, ctl);
+ ctl.m_postEval = false;
+ const int n = ++data.m_cnt;
+ const int o = code.m_limitOff <= 0 ? 0 : code.m_limitOff;
+ const int c = code.m_limitCnt;
+ if (n <= o)
+ continue;
+ if (c < 0)
+ break;
+ if (n - o <= c)
+ break;
+ return false;
+ }
+ return true;
+}
+
+void
+Exec_query_project::close(Ctx& ctx)
+{
+ Data& data = getData();
+ data.m_cnt = 0;
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+ ctx_assert(m_exprRow != 0);
+ m_exprRow->close(ctx);
+}
+
+void
+Exec_query_project::print(Ctx& ctx)
+{
+ ctx.print(" [query_project");
+ Exec_base* a[] = { m_query, m_exprRow };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.hpp
new file mode 100644
index 00000000000..545685ab9df
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.hpp
@@ -0,0 +1,178 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_project_hpp
+#define ODBC_CODEGEN_Code_query_project_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_expr_row.hpp"
+
+/**
+ * @class Plan_query_project
+ * @brief Project node in PlanTree
+ */
+class Plan_query_project : public Plan_query {
+public:
+ Plan_query_project(Plan_root* root);
+ virtual ~Plan_query_project();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setRow(Plan_expr_row* exprRow);
+ void setLimit(int off, int cnt);
+protected:
+ Plan_expr_row* getRow();
+ Plan_query* m_query;
+ Plan_expr_row* m_exprRow;
+ int m_limitOff;
+ int m_limitCnt;
+};
+
+inline
+Plan_query_project::Plan_query_project(Plan_root* root) :
+ Plan_query(root),
+ m_query(0),
+ m_exprRow(0),
+ m_limitOff(0),
+ m_limitCnt(-1)
+{
+}
+
+// children
+
+inline void
+Plan_query_project::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_query_project::setRow(Plan_expr_row* exprRow)
+{
+ ctx_assert(exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+inline void
+Plan_query_project::setLimit(int off, int cnt)
+{
+ m_limitOff = off;
+ m_limitCnt = cnt;
+}
+
+/**
+ * @class Exec_query_project
+ * @brief Project node in ExecTree
+ */
+class Exec_query_project : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs);
+ virtual ~Code();
+ protected:
+ friend class Plan_query_project;
+ friend class Exec_query_project;
+ // sets reference to Sqlspecs from the row
+ int m_limitOff;
+ int m_limitCnt;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_project* node, const SqlRow& sqlRow);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_project;
+ // sets reference to SqlRow from the row
+ unsigned m_cnt;
+ };
+ Exec_query_project(Exec_root* root);
+ virtual ~Exec_query_project();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+ void setRow(Exec_expr_row* exprRow);
+ const Exec_query* getRawQuery() const;
+protected:
+ friend class Exec_query;
+ Exec_query* m_query;
+ Exec_expr_row* m_exprRow;
+};
+
+inline
+Exec_query_project::Code::Code(const SqlSpecs& sqlSpecs) :
+ Exec_query::Code(sqlSpecs),
+ m_limitOff(0),
+ m_limitCnt(-1)
+{
+}
+
+inline
+Exec_query_project::Data::Data(Exec_query_project* node, const SqlRow& sqlRow) :
+ Exec_query::Data(node, sqlRow),
+ m_cnt(0)
+{
+}
+
+inline
+Exec_query_project::Exec_query_project(Exec_root* root) :
+ Exec_query(root),
+ m_query(0),
+ m_exprRow(0)
+{
+}
+
+// children
+
+inline const Exec_query_project::Code&
+Exec_query_project::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_project::Data&
+Exec_query_project::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_project::setQuery(Exec_query* query)
+{
+ ctx_assert(m_query == 0 && query != 0);
+ m_query = query;
+}
+
+inline void
+Exec_query_project::setRow(Exec_expr_row* exprRow)
+{
+ ctx_assert(m_exprRow == 0 && exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.cpp
new file mode 100644
index 00000000000..5d29c5af315
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.cpp
@@ -0,0 +1,211 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query_range.hpp"
+#include "Code_column.hpp"
+#include "Code_expr.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_range
+
+Plan_query_range::~Plan_query_range()
+{
+}
+
+Plan_base*
+Plan_query_range::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_range::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // set up
+ ctx_assert(m_table != 0 && m_index != 0);
+ const BaseString& tableName = m_table->getName();
+ ctx_assert(m_index->m_dictIndex != 0);
+ const DictIndex& dictIndex = *m_index->m_dictIndex;
+ const BaseString& indexName = dictIndex.getName();
+ const unsigned keyCount = m_index->m_keyCountUsed;
+ const ColumnVector& columns = m_table->exprColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_query_range::Code& code = *new Exec_query_range::Code(keyCount, attrCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ code.m_indexName = strcpy(new char[indexName.length() + 1], indexName.c_str());
+ code.m_exclusive = m_exclusive;
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictIndex.getColumn(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = k - 1; // index column order
+ }
+ // matching expressions
+ ctx_assert(m_index->m_keyFound);
+ const ExprVector& keyEq = m_index->m_keyEq;
+ // check size matches
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // queried attributes
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ const SqlType& sqlType = dictColumn.sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_sqlSpecs.setEntry(i, sqlSpec);
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_query_range* exec = new Exec_query_range(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ // interpreter
+ ctl.m_execQuery = exec;
+ Exec_pred* execInterp = 0;
+ ctl.m_topTable = m_table;
+ if (m_interp != 0) {
+ execInterp = static_cast<Exec_pred*>(m_interp->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execInterp != 0);
+ }
+ ctl.m_topTable = 0;
+ if (m_interp != 0)
+ exec->setInterp(execInterp);
+ return exec;
+}
+
+void
+Plan_query_range::print(Ctx& ctx)
+{
+ ctx.print(" [query_range");
+ Plan_base* a[] = { m_table };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_query_range
+
+Exec_query_range::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+ delete[] m_attrId;
+}
+
+Exec_query_range::Data::~Data()
+{
+ delete[] m_recAttr;
+}
+
+Exec_query_range::~Exec_query_range()
+{
+}
+
+void
+Exec_query_range::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // create data
+ Data& data = *new Data(this, code.sqlSpecs());
+ setData(data);
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+ // needed for isNULL
+ data.m_recAttr = new NdbRecAttr* [1 + code.m_attrCount];
+ for (unsigned i = 0; i <= code.m_attrCount; i++) {
+ data.m_recAttr[i] = 0;
+ }
+ // parallel
+ data.m_parallel = code.m_exclusive ? 1 : 240; // best supported
+ // interpreter
+ if (m_interp != 0) {
+ //m_interp->alloc(ctx, ctl); XXX
+ if (! ctx.ok())
+ return;
+ }
+}
+
+void
+Exec_query_range::close(Ctx& ctx)
+{
+ Data& data = getData();
+ if (data.m_con != 0) {
+ Ndb* const ndb = ndbObject();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ data.m_done = true;
+ ctx_log2(("lookup closed at statement close"));
+ }
+ // if (m_interp != 0)
+ // m_interp->close(ctx);
+}
+
+void
+Exec_query_range::print(Ctx& ctx)
+{
+ ctx.print(" [query_range");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" keyId=");
+ for (unsigned i = 1; i <= code.m_keyCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_keyId[i]);
+ }
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ ctx.print(" table=%s", code.m_tableName);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.hpp
new file mode 100644
index 00000000000..4438189522c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.hpp
@@ -0,0 +1,186 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_range_hpp
+#define ODBC_CODEGEN_Code_query_range_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+#include "Code_pred.hpp"
+
+class Ctx;
+class StmtArea;
+class NdbConnection;
+class NdbOperation;
+class NdbRecAttr;
+
+/*
+ * Range scan via ordered index. We implement only the case of equality
+ * on an initial sequence of index keys.
+ */
+
+class Plan_query_range : public Plan_query {
+public:
+ Plan_query_range(Plan_root* root);
+ virtual ~Plan_query_range();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ void setTable(Plan_table* table, Plan_table::Index* index);
+ void setInterp(Plan_pred* interp);
+ void setExclusive();
+protected:
+ Plan_table* m_table;
+ Plan_table::Index* m_index;
+ Plan_pred* m_interp;
+ bool m_exclusive;
+};
+
+inline
+Plan_query_range::Plan_query_range(Plan_root* root) :
+ Plan_query(root),
+ m_table(0),
+ m_index(0),
+ m_interp(0),
+ m_exclusive(false)
+{
+}
+
+inline void
+Plan_query_range::setTable(Plan_table* table, Plan_table::Index* index)
+{
+ ctx_assert(table != 0 && index != 0 && index == &table->m_indexList[index->m_pos] && index->m_pos != 0);
+ m_table = table;
+ m_index = index;
+}
+
+inline void
+Plan_query_range::setInterp(Plan_pred* interp)
+{
+ ctx_assert(interp != 0);
+ m_interp = interp;
+}
+
+inline void
+Plan_query_range::setExclusive()
+{
+ m_exclusive = true;
+}
+
+class Exec_query_range : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(unsigned keyCount, unsigned attrCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_query_range;
+ friend class Exec_query_range;
+ const char* m_tableName;
+ const char* m_indexName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ unsigned m_attrCount;
+ SqlSpecs m_sqlSpecs;
+ NdbAttrId* m_attrId;
+ bool m_exclusive;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_range* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_range;
+ SqlRow m_sqlRow;
+ NdbConnection* m_con;
+ NdbOperation* m_op;
+ NdbRecAttr** m_recAttr;
+ unsigned m_parallel;
+ bool m_done; // if no match possible due to range
+ };
+ Exec_query_range(Exec_root* root);
+ virtual ~Exec_query_range();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ const Code& getCode() const;
+ Data& getData() const;
+ void setInterp(Exec_pred* interp);
+protected:
+ Exec_pred* m_interp;
+};
+
+inline
+Exec_query_range::Code::Code(unsigned keyCount, unsigned attrCount) :
+ Exec_query::Code(m_sqlSpecs),
+ m_tableName(0),
+ m_indexName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_attrCount(attrCount),
+ m_sqlSpecs(attrCount),
+ m_attrId(0),
+ m_exclusive(false)
+{
+}
+
+inline
+Exec_query_range::Data::Data(Exec_query_range* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_con(0),
+ m_op(0),
+ m_recAttr(0),
+ m_parallel(1),
+ m_done(false)
+{
+}
+
+inline
+Exec_query_range::Exec_query_range(Exec_root* root) :
+ Exec_query(root),
+ m_interp(0)
+{
+}
+
+inline const Exec_query_range::Code&
+Exec_query_range::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_range::Data&
+Exec_query_range::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_range::setInterp(Exec_pred* interp)
+{
+ ctx_assert(interp != 0);
+ m_interp = interp;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.cpp
new file mode 100644
index 00000000000..8b295a97916
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.cpp
@@ -0,0 +1,109 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_query_repeat.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_repeat
+
+Plan_query_repeat::~Plan_query_repeat()
+{
+}
+
+Plan_base*
+Plan_query_repeat::analyze(Ctx& ctx, Ctl& ctl)
+{
+ return this;
+}
+
+Exec_base*
+Plan_query_repeat::codegen(Ctx& ctx, Ctl& ctl)
+{
+ Exec_query_repeat* exec = new Exec_query_repeat(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // SqlSpecs is empty
+ const SqlSpecs sqlSpecs(0);
+ Exec_query_repeat::Code& code = *new Exec_query_repeat::Code(sqlSpecs, m_forever, m_maxcount);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_query_repeat::print(Ctx& ctx)
+{
+ ctx.print(" [query_repeat");
+ if (! m_forever)
+ ctx.print(" %ld", (long)m_maxcount);
+ ctx.print("]");
+}
+
+// Exec_query_repeat
+
+Exec_query_repeat::Code::~Code()
+{
+}
+
+Exec_query_repeat::Data::~Data()
+{
+}
+
+Exec_query_repeat::~Exec_query_repeat()
+{
+}
+
+void
+Exec_query_repeat::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // SqlRow is empty
+ Data& data = *new Data(this, code.sqlSpecs());
+ setData(data);
+}
+
+void
+Exec_query_repeat::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ data.m_count = 0;
+}
+
+bool
+Exec_query_repeat::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ // fetch until count is up
+ if (code.m_forever || data.m_count < code.m_maxcount) {
+ data.m_count++;
+ return true;
+ }
+ return false;
+}
+
+void
+Exec_query_repeat::close(Ctx& ctx)
+{
+}
+
+void
+Exec_query_repeat::print(Ctx& ctx)
+{
+ const Code& code = getCode();
+ ctx.print(" [query_repeat");
+ if (! code.m_forever)
+ ctx.print(" %ld", (long)code.m_maxcount);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.hpp
new file mode 100644
index 00000000000..90d6ef55104
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.hpp
@@ -0,0 +1,133 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_repeat_hpp
+#define ODBC_CODEGEN_Code_query_repeat_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_expr_row.hpp"
+
+/**
+ * @class Plan_query_repeat
+ * @brief Constant query node in PlanTree
+ */
+class Plan_query_repeat : public Plan_query {
+public:
+ Plan_query_repeat(Plan_root* root);
+ Plan_query_repeat(Plan_root* root, CountType maxcount);
+ virtual ~Plan_query_repeat();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+private:
+ bool m_forever;
+ CountType m_maxcount;
+};
+
+inline
+Plan_query_repeat::Plan_query_repeat(Plan_root* root) :
+ Plan_query(root),
+ m_forever(true),
+ m_maxcount(0)
+{
+}
+
+inline
+Plan_query_repeat::Plan_query_repeat(Plan_root* root, CountType maxcount) :
+ Plan_query(root),
+ m_forever(false),
+ m_maxcount(maxcount)
+{
+}
+
+/**
+ * @class Exec_query_repeat
+ * @brief Constant query node in ExecTree
+ */
+class Exec_query_repeat : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs, bool forever, CountType maxcount);
+ virtual ~Code();
+ protected:
+ friend class Exec_query_repeat;
+ SqlSpecs m_sqlSpecs;
+ bool m_forever;
+ CountType m_maxcount;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_repeat* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_repeat;
+ SqlRow m_sqlRow;
+ CountType m_count;
+ };
+ Exec_query_repeat(Exec_root* root);
+ virtual ~Exec_query_repeat();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_query_repeat::Code::Code(const SqlSpecs& sqlSpecs, bool forever, CountType maxcount) :
+ Exec_query::Code(m_sqlSpecs),
+ m_sqlSpecs(sqlSpecs),
+ m_forever(forever),
+ m_maxcount(maxcount)
+{
+}
+
+inline
+Exec_query_repeat::Data::Data(Exec_query_repeat* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_count(0)
+{
+}
+
+inline
+Exec_query_repeat::Exec_query_repeat(Exec_root* root) :
+ Exec_query(root)
+{
+}
+
+// children
+
+inline const Exec_query_repeat::Code&
+Exec_query_repeat::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_repeat::Data&
+Exec_query_repeat::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.cpp
new file mode 100644
index 00000000000..1c0f58980e5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.cpp
@@ -0,0 +1,177 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query_scan.hpp"
+#include "Code_column.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_scan
+
+Plan_query_scan::~Plan_query_scan()
+{
+}
+
+Plan_base*
+Plan_query_scan::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ if (m_interp != 0) {
+ m_interp = static_cast<Plan_pred*>(m_interp->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_interp != 0);
+ }
+ return this;
+}
+
+Exec_base*
+Plan_query_scan::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // set up
+ ctx_assert(m_table != 0);
+ const BaseString& tableName = m_table->getName();
+ const DictTable& dictTable = m_table->dictTable();
+ const ColumnVector& columns = m_table->exprColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_query_scan::Code& code = *new Exec_query_scan::Code(attrCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ code.m_exclusive = m_exclusive;
+ // queried attributes
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ const SqlType& sqlType = dictColumn.sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_sqlSpecs.setEntry(i, sqlSpec);
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_query_scan* exec = new Exec_query_scan(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ // interpreter
+ Exec_pred* execInterp = 0;
+ ctl.m_execQuery = exec;
+ ctl.m_topTable = m_table;
+ if (m_interp != 0) {
+ execInterp = static_cast<Exec_pred*>(m_interp->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execInterp != 0);
+ }
+ ctl.m_topTable = 0;
+ if (m_interp != 0)
+ exec->setInterp(execInterp);
+ return exec;
+}
+
+void
+Plan_query_scan::print(Ctx& ctx)
+{
+ ctx.print(" [query_scan");
+ Plan_base* a[] = { m_table, m_interp };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_query_scan
+
+Exec_query_scan::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_attrId;
+}
+
+Exec_query_scan::Data::~Data()
+{
+ delete[] m_recAttr;
+}
+
+Exec_query_scan::~Exec_query_scan()
+{
+}
+
+void
+Exec_query_scan::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // create data
+ Data& data = *new Data(this, code.sqlSpecs());
+ // needed for isNULL
+ data.m_recAttr = new NdbRecAttr* [1 + code.m_attrCount];
+ for (unsigned i = 0; i <= code.m_attrCount; i++) {
+ data.m_recAttr[i] = 0;
+ }
+ data.m_parallel = code.m_exclusive ? 1 : 240; // best supported
+ setData(data);
+ // interpreter
+ ctl.m_query = this;
+ if (m_interp != 0) {
+ //m_interp->alloc(ctx, ctl); XXX
+ if (! ctx.ok())
+ return;
+ }
+}
+
+void
+Exec_query_scan::close(Ctx& ctx)
+{
+ Data& data = getData();
+ if (data.m_con != 0) {
+ Ndb* const ndb = ndbObject();
+ int ret = data.m_con->stopScan();
+ if (ret == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "stopScan");
+ }
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("scan closed at statement close"));
+ }
+ if (m_interp != 0)
+ m_interp->close(ctx);
+}
+
+void
+Exec_query_scan::print(Ctx& ctx)
+{
+ ctx.print(" [query_scan");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ ctx.print(" table=%s", code.m_tableName);
+ }
+ if (m_interp != 0)
+ m_interp->print(ctx);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.hpp
new file mode 100644
index 00000000000..d6d1630ddf8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.hpp
@@ -0,0 +1,174 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_scan_hpp
+#define ODBC_CODEGEN_Code_query_scan_hpp
+
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+#include "Code_pred.hpp"
+
+class Ctx;
+class StmtArea;
+class NdbConnection;
+class NdbOperation;
+class NdbRecAttr;
+
+/*
+ * Table scan.
+ */
+
+class Plan_query_scan : public Plan_query {
+public:
+ Plan_query_scan(Plan_root* root);
+ virtual ~Plan_query_scan();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ void setTable(Plan_table* table);
+ void setInterp(Plan_pred* interp);
+ void setExclusive();
+protected:
+ Plan_table* m_table;
+ Plan_pred* m_interp;
+ bool m_exclusive; // exclusive
+};
+
+inline
+Plan_query_scan::Plan_query_scan(Plan_root* root) :
+ Plan_query(root),
+ m_table(0),
+ m_interp(0),
+ m_exclusive(false)
+{
+}
+
+inline void
+Plan_query_scan::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline void
+Plan_query_scan::setInterp(Plan_pred* interp)
+{
+ ctx_assert(interp != 0);
+ m_interp = interp;
+}
+
+inline void
+Plan_query_scan::setExclusive()
+{
+ m_exclusive = true;
+}
+
+class Exec_query_scan : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(unsigned attrCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_query_scan;
+ friend class Exec_query_scan;
+ char* m_tableName;
+ unsigned m_attrCount;
+ SqlSpecs m_sqlSpecs;
+ NdbAttrId* m_attrId;
+ bool m_exclusive;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_scan* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_scan;
+ SqlRow m_sqlRow;
+ NdbConnection* m_con;
+ NdbOperation* m_op;
+ NdbRecAttr** m_recAttr;
+ unsigned m_parallel; // parallelism could be runtime option
+ };
+ Exec_query_scan(Exec_root* root);
+ virtual ~Exec_query_scan();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setInterp(Exec_pred* interp);
+protected:
+ Exec_pred* m_interp;
+};
+
+inline
+Exec_query_scan::Code::Code(unsigned attrCount) :
+ Exec_query::Code(m_sqlSpecs),
+ m_tableName(0),
+ m_attrCount(attrCount),
+ m_sqlSpecs(attrCount),
+ m_attrId(0),
+ m_exclusive(false)
+{
+}
+
+inline
+Exec_query_scan::Data::Data(Exec_query_scan* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_con(0),
+ m_op(0),
+ m_recAttr(0),
+ m_parallel(1)
+{
+}
+
+inline
+Exec_query_scan::Exec_query_scan(Exec_root* root) :
+ Exec_query(root),
+ m_interp(0)
+{
+}
+
+// children
+
+inline const Exec_query_scan::Code&
+Exec_query_scan::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_scan::Data&
+Exec_query_scan::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_scan::setInterp(Exec_pred* interp)
+{
+ ctx_assert(interp != 0);
+ m_interp = interp;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.cpp
new file mode 100644
index 00000000000..4ea6db8c4e2
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.cpp
@@ -0,0 +1,239 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <algorithm>
+#include "Code_query_sort.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_sort
+
+Plan_query_sort::~Plan_query_sort()
+{
+}
+
+Plan_expr_row*
+Plan_query_sort::getRow()
+{
+ ctx_assert(m_query != 0);
+ return m_query->getRow();
+}
+
+Plan_base*
+Plan_query_sort::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_sortRow != 0);
+ m_sortRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_sort::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // create code for the subquery
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // create code for the row based on query code
+ ctx_assert(m_sortRow != 0);
+ ctl.m_execQuery = execQuery->getRawQuery();
+ Exec_expr_row* execRow = static_cast<Exec_expr_row*>(m_sortRow->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execRow != 0);
+ Exec_query_sort* exec = new Exec_query_sort(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ // re-use SqlSpecs from subquery
+ const Exec_query::Code& codeQuery = execQuery->getCode();
+ const SqlSpecs& sqlSpecs = codeQuery.sqlSpecs();
+ // make asc
+ unsigned size = m_sortRow->getSize();
+ bool* asc = new bool[1 + size];
+ for (unsigned i = 1; i <= size; i++) {
+ asc[i] = m_sortRow->m_ascList[i];
+ }
+ Exec_query_sort::Code& code = *new Exec_query_sort::Code(sqlSpecs, asc);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ exec->setRow(execRow);
+ return exec;
+}
+
+void
+Plan_query_sort::print(Ctx& ctx)
+{
+ ctx.print(" [query_sort");
+ Plan_base* a[] = { m_query, m_sortRow };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
+
+// Exec_query_sort
+
+Exec_query_sort::Code::~Code()
+{
+}
+
+Exec_query_sort::Data::~Data()
+{
+ for (unsigned i = 0; i < m_sortList.size(); i++) {
+ SortItem& sortItem = m_sortList[i];
+ delete sortItem.m_dataRow;
+ delete sortItem.m_sortRow;
+ }
+}
+
+Exec_query_sort::~Exec_query_sort()
+{
+}
+
+void
+Exec_query_sort::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // allocate sort row based on subquery data
+ ctx_assert(m_sortRow != 0);
+ ctl.m_query = m_query->getRawQuery();
+ m_sortRow->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ Data& data = *new Data(this, getCode().sqlSpecs());
+ setData(data);
+}
+
+void
+Exec_query_sort::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_query != 0 && m_sortRow != 0);
+ ctl.m_sortRow = m_sortRow;
+ m_query->execute(ctx, ctl);
+}
+
+bool
+SortLess::operator()(SortItem s1, SortItem s2) const
+{
+ const Exec_query_sort::Code& code = m_node->getCode();
+ const SqlRow& r1 = *s1.m_sortRow;
+ const SqlRow& r2 = *s2.m_sortRow;
+ for (unsigned i = 1; i <= r1.count(); i++) {
+ const SqlField& f1 = r1.getEntry(i);
+ const SqlField& f2 = r2.getEntry(i);
+ // nulls last is default in oracle
+ bool f1null = f1.sqlNull();
+ bool f2null = f2.sqlNull();
+ if (f1null && f2null)
+ continue;
+ if (! f1null && f2null)
+ return code.getAsc(i) ? true : false;
+ if (f1null && ! f2null)
+ return code.getAsc(i) ? false : true;
+ if (f1.less(f2))
+ return code.getAsc(i) ? true : false;
+ if (f2.less(f1))
+ return code.getAsc(i) ? false : true;
+ }
+ return false;
+}
+
+bool
+Exec_query_sort::fetchImpl(Ctx& ctx, Ctl& ctl)
+{
+ Data& data = getData();
+ ctx_assert(m_query != 0 && m_sortRow != 0);
+ ctl.m_sortRow = m_sortRow;
+ if (! data.m_sorted) {
+ // read and cache all rows
+ data.m_count = 0;
+ while (m_query->fetch(ctx, ctl)) {
+ const SqlRow* dataRow = m_query->getData().sqlRow().copy();
+ const SqlRow* sortRow = 0;
+ if (ctl.m_groupIndex == 0) {
+ // evaluate sort row
+ m_sortRow->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return false;
+ sortRow = m_sortRow->getData().sqlRow().copy();
+ } else {
+ // evaluate done by group-by
+ SqlRow tmpSortRow(m_sortRow->getCode().sqlSpecs());
+ for (unsigned i = 1; i <= tmpSortRow.count(); i++) {
+ tmpSortRow.setEntry(i, m_sortRow->getExpr(i)->getData().groupField(ctl.m_groupIndex));
+ }
+ sortRow = tmpSortRow.copy();
+ }
+ SortItem sortItem(dataRow, sortRow);
+ data.m_sortList.push_back(sortItem);
+ data.m_count++;
+ }
+ data.m_index = 0;
+ if (! ctx.ok())
+ return false;
+ // sort the rows XXX use iterated stable_sort
+ SortLess sortLess(this);
+ std::sort(data.m_sortList.begin(), data.m_sortList.end(), sortLess);
+ data.m_sorted = true;
+ }
+ if (data.m_index < data.m_count) {
+ // make our SqlRow reference to current row
+ const SqlRow& currRow = *data.m_sortList[data.m_index].m_dataRow;
+ for (unsigned i = 1; i <= data.m_sqlRow.count(); i++) {
+ const SqlField& currField = currRow.getEntry(i);
+ SqlSpec sqlSpec(currField.sqlSpec(), SqlSpec::Reference);
+ SqlField sqlField(sqlSpec, &currField);
+ data.m_sqlRow.setEntry(i, sqlField);
+ }
+ data.m_index++;
+ return true;
+ }
+ return false;
+}
+
+void
+Exec_query_sort::close(Ctx& ctx)
+{
+ Data& data = getData();
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+ data.m_sorted = false;
+ for (unsigned i = 0; i < data.m_sortList.size(); i++) {
+ SortItem& sortItem = data.m_sortList[i];
+ delete sortItem.m_dataRow;
+ delete sortItem.m_sortRow;
+ }
+ data.m_sortList.clear();
+ data.m_count = 0;
+ data.m_index = 0;
+}
+
+void
+Exec_query_sort::print(Ctx& ctx)
+{
+ ctx.print(" [query_sort");
+ Exec_base* a[] = { m_query, m_sortRow };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.hpp
new file mode 100644
index 00000000000..d1aa03d9aef
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.hpp
@@ -0,0 +1,208 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_sort_hpp
+#define ODBC_CODEGEN_Code_query_sort_hpp
+
+#include <functional>
+#include <common/common.hpp>
+#include "Code_query.hpp"
+#include "Code_expr_row.hpp"
+
+/**
+ * @class Plan_query_sort
+ * @brief Project node in PlanTree
+ */
+class Plan_query_sort : public Plan_query {
+public:
+ Plan_query_sort(Plan_root* root);
+ virtual ~Plan_query_sort();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setQuery(Plan_query* query);
+ void setRow(Plan_expr_row* sortRow);
+protected:
+ Plan_expr_row* getRow();
+ Plan_query* m_query;
+ Plan_expr_row* m_sortRow;
+};
+
+inline
+Plan_query_sort::Plan_query_sort(Plan_root* root) :
+ Plan_query(root),
+ m_query(0),
+ m_sortRow(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_sort::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+inline void
+Plan_query_sort::setRow(Plan_expr_row* sortRow)
+{
+ ctx_assert(sortRow != 0);
+ m_sortRow = sortRow;
+}
+
+/**
+ * Item to sort includes data row and sort row.
+ */
+struct SortItem {
+ SortItem(const SqlRow* dataRow, const SqlRow* sortRow);
+ const SqlRow* m_dataRow; // copy of fetched row from subquery
+ const SqlRow* m_sortRow; // copy of values to sort on
+};
+
+typedef std::vector<SortItem> SortList;
+
+class Exec_query_sort;
+
+struct SortLess : std::binary_function<SortItem, SortItem, bool> {
+ SortLess(const Exec_query_sort* node);
+ const Exec_query_sort* m_node;
+ bool operator()(SortItem s1, SortItem s2) const;
+};
+
+inline
+SortItem::SortItem(const SqlRow* dataRow, const SqlRow* sortRow) :
+ m_dataRow(dataRow),
+ m_sortRow(sortRow)
+{
+}
+
+inline
+SortLess::SortLess(const Exec_query_sort* node) :
+ m_node(node)
+{
+}
+
+/**
+ * @class Exec_query_sort
+ * @brief Project node in ExecTree
+ */
+class Exec_query_sort : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(const SqlSpecs& sqlSpecs, bool* asc);
+ virtual ~Code();
+ bool getAsc(unsigned i) const;
+ protected:
+ friend class Exec_query_sort;
+ const bool* const m_asc;
+ // sets reference to Sqlspecs from subquery
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_sort* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_sort;
+ SqlRow m_sqlRow; // current row
+ bool m_sorted; // fetch and sort done
+ SortList m_sortList;
+ unsigned m_count; // number of rows
+ unsigned m_index; // current fetch index
+ };
+ Exec_query_sort(Exec_root* root);
+ virtual ~Exec_query_sort();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+ void setRow(Exec_expr_row* sortRow);
+protected:
+ friend class Exec_query;
+ Exec_query* m_query;
+ Exec_expr_row* m_sortRow;
+};
+
+inline
+Exec_query_sort::Code::Code(const SqlSpecs& sqlSpecs, bool* asc) :
+ Exec_query::Code(sqlSpecs),
+ m_asc(asc)
+{
+}
+
+inline bool
+Exec_query_sort::Code::getAsc(unsigned i) const
+{
+ return m_asc[i];
+}
+
+inline
+Exec_query_sort::Data::Data(Exec_query_sort* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs),
+ m_sorted(false),
+ m_count(0),
+ m_index(0)
+{
+}
+
+inline
+Exec_query_sort::Exec_query_sort(Exec_root* root) :
+ Exec_query(root),
+ m_query(0),
+ m_sortRow(0)
+{
+}
+
+// children
+
+inline const Exec_query_sort::Code&
+Exec_query_sort::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_sort::Data&
+Exec_query_sort::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_query_sort::setQuery(Exec_query* query)
+{
+ ctx_assert(m_query == 0 && query != 0);
+ m_query = query;
+}
+
+inline void
+Exec_query_sort::setRow(Exec_expr_row* sortRow)
+{
+ ctx_assert(m_sortRow == 0 && sortRow != 0);
+ m_sortRow = sortRow;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.cpp
new file mode 100644
index 00000000000..affe3dc1264
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.cpp
@@ -0,0 +1,130 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_query_sys.hpp"
+#include "Code_column.hpp"
+#include "Code_root.hpp"
+
+// Plan_query_sys
+
+Plan_query_sys::~Plan_query_sys()
+{
+}
+
+Plan_base*
+Plan_query_sys::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+Exec_base*
+Plan_query_sys::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // set up
+ ctx_assert(m_table != 0);
+ const DictTable& dictTable = m_table->dictTable();
+ const ColumnVector& columns = m_table->exprColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_query_sys::Code& code = *new Exec_query_sys::Code(attrCount);
+ code.m_sysId = dictTable.sysId();
+ // queried attributes
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ const SqlType& sqlType = dictColumn.sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_sqlSpecs.setEntry(i, sqlSpec);
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_query_sys* exec = new Exec_query_sys(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ return exec;
+}
+
+void
+Plan_query_sys::print(Ctx& ctx)
+{
+ ctx.print(" [query_sys");
+ Plan_base* a[] = { m_table };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
+
+// Exec_query_sys
+
+Exec_query_sys::Code::~Code()
+{
+ delete[] m_attrId;
+}
+
+Exec_query_sys::Data::~Data()
+{
+}
+
+Exec_query_sys::~Exec_query_sys()
+{
+}
+
+void
+Exec_query_sys::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // create data
+ Data& data = *new Data(this, code.sqlSpecs());
+ setData(data);
+}
+
+void
+Exec_query_sys::close(Ctx& ctx)
+{
+ Data& data = getData();
+ data.m_rowPos = 0;
+ data.m_tablePos = 0;
+ data.m_attrPos = 0;
+ data.m_keyPos = 0;
+}
+
+void
+Exec_query_sys::print(Ctx& ctx)
+{
+ ctx.print(" [query_sys");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ ctx.print(" sysId=%u", (unsigned)code.m_sysId);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.hpp
new file mode 100644
index 00000000000..8eb069d0413
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.hpp
@@ -0,0 +1,148 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_query_sys_hpp
+#define ODBC_CODEGEN_Code_query_sys_hpp
+
+#include <common/common.hpp>
+#include <dictionary/DictSys.hpp>
+#include "Code_query.hpp"
+#include "Code_table.hpp"
+
+class Ctx;
+class StmtArea;
+class NdbConnection;
+class NdbOperation;
+class NdbRecAttr;
+
+/**
+ * @class Plan_query_sys
+ * @brief Full select (no where clause)
+ */
+class Plan_query_sys : public Plan_query {
+public:
+ Plan_query_sys(Plan_root* root);
+ virtual ~Plan_query_sys();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+protected:
+ Plan_table* m_table;
+};
+
+inline
+Plan_query_sys::Plan_query_sys(Plan_root* root) :
+ Plan_query(root),
+ m_table(0)
+{
+}
+
+// children
+
+inline void
+Plan_query_sys::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+/**
+ * @class Exec_query_sys
+ * @brief Full select (no where clause)
+ */
+class Exec_query_sys : public Exec_query {
+public:
+ class Code : public Exec_query::Code {
+ public:
+ Code(unsigned attrCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_query_sys;
+ friend class Exec_query_sys;
+ DictSys::Id m_sysId;
+ unsigned m_attrCount;
+ SqlSpecs m_sqlSpecs;
+ NdbAttrId* m_attrId;
+ };
+ class Data : public Exec_query::Data {
+ public:
+ Data(Exec_query_sys* node, const SqlSpecs& sqlSpecs);
+ virtual ~Data();
+ protected:
+ friend class Exec_query_sys;
+ SqlRow m_sqlRow;
+ // for typeinfo
+ unsigned m_rowPos;
+ // for tables and columns
+ NdbDictionary::Dictionary::List m_objectList;
+ unsigned m_tablePos;
+ unsigned m_attrPos;
+ unsigned m_keyPos;
+ };
+ Exec_query_sys(Exec_root* root);
+ virtual ~Exec_query_sys();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ bool fetchImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+};
+
+inline
+Exec_query_sys::Code::Code(unsigned attrCount) :
+ Exec_query::Code(m_sqlSpecs),
+ m_sysId(DictSys::Undef),
+ m_attrCount(attrCount),
+ m_sqlSpecs(attrCount),
+ m_attrId(0)
+{
+}
+
+inline
+Exec_query_sys::Data::Data(Exec_query_sys* node, const SqlSpecs& sqlSpecs) :
+ Exec_query::Data(node, m_sqlRow),
+ m_sqlRow(sqlSpecs)
+{
+}
+
+inline
+Exec_query_sys::Exec_query_sys(Exec_root* root) :
+ Exec_query(root)
+{
+}
+
+// children
+
+inline const Exec_query_sys::Code&
+Exec_query_sys::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_query_sys::Data&
+Exec_query_sys::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_root.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_root.cpp
new file mode 100644
index 00000000000..4f45bdffdaf
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_root.cpp
@@ -0,0 +1,307 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include "Code_root.hpp"
+#include "Code_stmt.hpp"
+#include "Code_query.hpp"
+#include "Code_expr_param.hpp"
+#include "Code_root.hpp"
+
+// Plan_root
+
+Plan_root::~Plan_root()
+{
+}
+
+Plan_base*
+Plan_root::analyze(Ctx& ctx, Ctl& ctl)
+{
+ // analyze statement
+ ctx_assert(m_stmt != 0);
+ m_stmt = static_cast<Plan_stmt*>(m_stmt->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_stmt != 0);
+ // analyze parameters
+ ctx_assert(m_paramList.size() > 0);
+ const unsigned paramCount = m_paramList.size() - 1;
+ DescArea& ipd = descArea(Desc_usage_IPD);
+ ipd.setCount(ctx, paramCount);
+ for (unsigned i = 1; i <= paramCount; i++) {
+ Plan_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ // analyze the parameter
+ param->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // must return self
+ return this;
+}
+
+void
+Plan_root::describe(Ctx& ctx)
+{
+ // describe statement
+ ctx_assert(m_stmt != 0);
+ m_stmt->describe(ctx);
+ // describe parameters
+ ctx_assert(m_paramList.size() > 0);
+ const unsigned paramCount = m_paramList.size() - 1;
+ DescArea& ipd = descArea(Desc_usage_IPD);
+ ipd.setCount(ctx, paramCount);
+ unsigned unbound = 0;
+ for (unsigned i = 1; i <= paramCount; i++) {
+ Plan_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ // describe the parameter
+ param->describe(ctx);
+ // check if SQL type is bound
+ ctx_assert(param->sqlType().type() != SqlType::Undef);
+ if (param->sqlType().type() == SqlType::Unbound)
+ unbound++;
+ }
+ if (unbound > 0)
+ ctx_log2(("%u out of %u params have unbound SQL type", unbound, paramCount));
+ m_stmtArea.m_unbound = unbound;
+}
+
+Exec_base*
+Plan_root::codegen(Ctx& ctx, Ctl& ctl)
+{
+ Exec_root* execRoot = new Exec_root(m_stmtArea);
+ Exec_root::Code& code = *new Exec_root::Code;
+ execRoot->setCode(code);
+ // set root in helper struct
+ ctl.m_execRoot = execRoot;
+ // generate code for the statement
+ ctx_assert(m_stmt != 0);
+ Exec_stmt* execStmt = static_cast<Exec_stmt*>(m_stmt->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ execRoot->setStmt(execStmt);
+ // create parameters list
+ execRoot->m_paramList.resize(m_paramList.size());
+ for (unsigned i = 1; i < m_paramList.size(); i++) {
+ Plan_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ Exec_expr_param* execParam = static_cast<Exec_expr_param*>(param->codegen(ctx, ctl));
+ ctx_assert(execParam != 0);
+ execRoot->m_paramList[i] = execParam;
+ }
+ return execRoot;
+}
+
+void
+Plan_root::print(Ctx& ctx)
+{
+ ctx.print("[root");
+ Plan_base* a[] = { m_stmt };
+ printList(ctx, a, 1);
+ ctx.print("]\n");
+}
+
+void
+Plan_root::saveNode(Plan_base* node)
+{
+ ctx_assert(node != 0);
+ m_nodeList.push_back(node);
+}
+
+void
+Plan_root::freeNodeList()
+{
+ for (NodeList::iterator i = m_nodeList.begin(); i != m_nodeList.end(); i++) {
+ Plan_base* node = *i;
+ *i = 0;
+ delete node;
+ }
+ m_nodeList.clear();
+}
+
+// Exec_root
+
+Exec_root::Code::~Code()
+{
+}
+
+Exec_root::Data::~Data()
+{
+}
+
+Exec_root::~Exec_root()
+{
+}
+
+StmtArea&
+Exec_root::stmtArea() const
+{
+ return m_stmtArea;
+}
+
+void
+Exec_root::alloc(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_stmt != 0);
+ m_stmt->alloc(ctx, ctl);
+}
+
+void
+Exec_root::bind(Ctx& ctx)
+{
+ // bind output cols
+ ctx_assert(m_stmt != 0);
+ m_stmt->bind(ctx);
+ // bind input params
+ for (unsigned i = 1; i < m_paramList.size(); i++) {
+ Exec_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ param->bind(ctx);
+ if (! ctx.ok())
+ return;
+ }
+}
+
+void
+Exec_root::execute(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_stmt != 0);
+ // check if data is needed
+ for (unsigned i = 1; i < m_paramList.size(); i++) {
+ Exec_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ Exec_expr_param::Data& paramData = param->getData();
+ if (paramData.m_atExec && paramData.m_extPos == -1) {
+ ctx.setCode(SQL_NEED_DATA);
+ return;
+ }
+ }
+ m_stmt->execute(ctx, ctl);
+}
+
+void
+Exec_root::fetch(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(m_stmt != 0);
+ Exec_query* query = static_cast<Exec_query*>(m_stmt);
+ ctx_assert(query != 0);
+ query->fetch(ctx, ctl);
+}
+
+void
+Exec_root::close(Ctx& ctx)
+{
+ ctx_assert(m_stmt != 0);
+ m_stmt->close(ctx);
+ for (unsigned i = 1; i < m_paramList.size(); i++) {
+ Exec_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ param->close(ctx);
+ }
+}
+
+void
+Exec_root::print(Ctx& ctx)
+{
+ ctx.print("[root");
+ Exec_base* a[] = { m_stmt };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ ctx.print("]\n");
+}
+
+void
+Exec_root::saveNode(Exec_base* node)
+{
+ ctx_assert(node != 0);
+ m_nodeList.push_back(node);
+}
+
+void
+Exec_root::freeNodeList()
+{
+ for (NodeList::iterator i = m_nodeList.begin(); i != m_nodeList.end(); i++) {
+ Exec_base* node = *i;
+ *i = 0;
+ delete node;
+ }
+ m_nodeList.clear();
+}
+
+// odbc support
+
+void
+Exec_root::sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ ctx_assert(m_stmt != 0);
+ Exec_query* query = static_cast<Exec_query*>(m_stmt);
+ ctx_assert(query != 0);
+ query->sqlGetData(ctx, columnNumber, targetType, targetValue, bufferLength, strlen_or_Ind);
+}
+
+void
+Exec_root::sqlParamData(Ctx& ctx, SQLPOINTER* value)
+{
+ ctx_assert(m_paramList.size() > 0);
+ unsigned count = m_paramList.size() - 1;
+ for (unsigned i = 1; i <= count; i++) {
+ Exec_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ Exec_expr_param::Data& paramData = param->getData();
+ if (! paramData.m_atExec || paramData.m_extPos >= 0)
+ continue;
+ ctx_assert(paramData.m_extField != 0);
+ ExtField& extField = *paramData.m_extField;
+ if (value != 0)
+ *value = extField.m_dataPtr;
+ m_paramData = i;
+ ctx.setCode(SQL_NEED_DATA);
+ return;
+ }
+}
+
+void
+Exec_root::sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind)
+{
+ ctx_assert(m_paramList.size() > 0);
+ unsigned count = m_paramList.size() - 1;
+ unsigned i = m_paramData;
+ if (i == 0) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "missing call to SQLParamData");
+ return;
+ }
+ if (i > count) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "parameter %u out of range 1 to %u", i, count);
+ return;
+ }
+ Exec_expr_param* param = m_paramList[i];
+ ctx_assert(param != 0);
+ Exec_expr_param::Data& paramData = param->getData();
+ if (! paramData.m_atExec) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "parameter %u not marked for data-at-exec", i);
+ return;
+ }
+ ctx_assert(paramData.m_extField != 0);
+ ExtField extField(paramData.m_extField->extSpec(), data, 0, &strlen_or_Ind, i);
+ if (paramData.m_extPos == -1)
+ paramData.m_extPos = 0;
+ extField.setPos(paramData.m_extPos);
+ // copy in and update position
+ SqlField& sqlField = paramData.m_sqlField;
+ sqlField.copyin(ctx, extField);
+ paramData.m_extPos = extField.getPos();
+ ctx_log4(("parameter %u data received", i));
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_root.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_root.hpp
new file mode 100644
index 00000000000..4f0f96725e3
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_root.hpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_root_hpp
+#define ODBC_CODEGEN_Code_root_hpp
+
+#include <list>
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_stmt.hpp"
+
+class SqlField;
+class ExtField;
+
+/**
+ * @class Plan_root
+ * @brief Root node above top level statement node
+ */
+class Plan_root : public Plan_base {
+public:
+ Plan_root(StmtArea& stmtArea);
+ virtual ~Plan_root();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setStmt(Plan_stmt* stmt);
+ // save and free nodes
+ void saveNode(Plan_base* node);
+ void freeNodeList();
+private:
+ friend class CodeGen;
+ friend class Plan_base;
+ friend class Plan_expr_param;
+ StmtArea& m_stmtArea;
+ Plan_stmt* m_stmt;
+ ParamVector m_paramList;
+ typedef std::list<Plan_base*> NodeList;
+ NodeList m_nodeList;
+};
+
+inline
+Plan_root::Plan_root(StmtArea& stmtArea) :
+ Plan_base(this),
+ m_stmtArea(stmtArea),
+ m_stmt(0)
+{
+}
+
+inline void
+Plan_root::setStmt(Plan_stmt* stmt)
+{
+ ctx_assert(stmt != 0);
+ m_stmt = stmt;
+}
+
+/**
+ * @class Exec_root
+ * @brief Root node above top level statement node
+ */
+class Exec_root : public Exec_base {
+public:
+ class Code : public Exec_base::Code {
+ public:
+ Code();
+ virtual ~Code();
+ };
+ class Data : public Exec_base::Data {
+ public:
+ Data();
+ virtual ~Data();
+ };
+ Exec_root(StmtArea& stmtArea);
+ virtual ~Exec_root();
+ StmtArea& stmtArea() const;
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void bind(Ctx& ctx);
+ void execute(Ctx& ctx, Ctl& ctl);
+ void fetch(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setStmt(Exec_stmt* stmt);
+ // save and free nodes
+ void saveNode(Exec_base* node);
+ void freeNodeList();
+ // odbc support
+ void sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind);
+ void sqlParamData(Ctx& ctx, SQLPOINTER* value);
+ void sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind);
+private:
+ friend class Plan_root;
+ friend class Exec_base;
+ friend class CodeGen;
+ StmtArea& m_stmtArea;
+ Exec_stmt* m_stmt;
+ ParamVector m_paramList;
+ unsigned m_paramData; // position of SQLParamData
+ typedef std::list<Exec_base*> NodeList;
+ NodeList m_nodeList;
+};
+
+inline
+Exec_root::Code::Code()
+{
+}
+
+inline
+Exec_root::Data::Data()
+{
+}
+
+inline
+Exec_root::Exec_root(StmtArea& stmtArea) :
+ Exec_base(this),
+ m_stmtArea(stmtArea),
+ m_stmt(0),
+ m_paramData(0)
+{
+}
+
+// children
+
+inline const Exec_root::Code&
+Exec_root::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_root::Data&
+Exec_root::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_root::setStmt(Exec_stmt* stmt)
+{
+ ctx_assert(stmt != 0);
+ m_stmt = stmt;
+ m_stmt->m_topLevel = true;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_select.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_select.cpp
new file mode 100644
index 00000000000..611b491968d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_select.cpp
@@ -0,0 +1,406 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <algorithm>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include "Code_select.hpp"
+#include "Code_query_lookup.hpp"
+#include "Code_query_index.hpp"
+#include "Code_query_scan.hpp"
+#include "Code_query_range.hpp"
+#include "Code_query_sys.hpp"
+#include "Code_query_project.hpp"
+#include "Code_query_filter.hpp"
+#include "Code_query_join.hpp"
+#include "Code_query_count.hpp"
+#include "Code_query_sort.hpp"
+#include "Code_query_group.hpp"
+#include "Code_query_distinct.hpp"
+#include "Code_expr_column.hpp"
+#include "Code_expr_const.hpp"
+#include "Code_pred_op.hpp"
+#include "Code_root.hpp"
+
+Plan_select::~Plan_select()
+{
+}
+
+Plan_base*
+Plan_select::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_select);
+ // analyze tables
+ ctx_assert(m_tableList != 0);
+ for (unsigned i = 1; i <= m_tableList->countTable(); i++) {
+ Plan_table* table = m_tableList->getTable(i);
+ table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ ctx_assert(m_exprRow != 0);
+ if (m_exprRow->getAsterisk()) {
+ // expand unqualified asterisk to table-qualified columns
+ setRow(new Plan_expr_row(m_root));
+ m_root->saveNode(m_exprRow);
+ for (unsigned i = 1; i <= m_tableList->countTable(); i++) {
+ const Plan_table* table = m_tableList->getTable(i);
+ const DictTable& dictTable = table->dictTable();
+ for (unsigned i = 1; i <= dictTable.getSize(); i++) {
+ DictColumn* dictColumn = dictTable.getColumn(i);
+ Plan_expr_column* column = new Plan_expr_column(m_root, dictColumn->getName());
+ m_root->saveNode(column);
+ column->setCname(table->getCname());
+ m_exprRow->addExpr(column);
+ }
+ }
+ }
+ // set name resolution scope
+ ctl.m_tableList = m_tableList->m_tableList;
+ ctx_assert(ctl.m_tableList.size() >= 1 + 1);
+ ctl.m_aggrin = false;
+ // analyze select row
+ ctl.m_aggrok = true;
+ ctx_assert(m_exprRow != 0);
+ m_exprRow = static_cast<Plan_expr_row*>(m_exprRow->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_exprRow != 0);
+ // analyze group by row
+ ctl.m_aggrok = false;
+ if (m_groupRow != 0) {
+ m_groupRow = static_cast<Plan_expr_row*>(m_groupRow->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_groupRow != 0);
+ }
+ // analyze having predicate
+ ctl.m_aggrok = true;
+ if (m_havingPred != 0) {
+ m_havingPred = static_cast<Plan_pred*>(m_havingPred->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_havingPred != 0);
+ }
+ // ana|yze order by row
+ ctl.m_aggrok = true;
+ if (m_sortRow != 0) {
+ m_sortRow = static_cast<Plan_expr_row*>(m_sortRow->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_sortRow != 0);
+ }
+ // analyze the predicate
+ ctl.m_aggrok = false;
+ ctl.m_topand = true;
+ ctl.m_extra = false;
+ if (m_pred != 0) {
+ m_pred = static_cast<Plan_pred*>(m_pred->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_pred != 0);
+ }
+ // check if group by required
+ if (m_exprRow->anyAggr() && ! m_exprRow->allBound() && m_groupRow == 0) {
+ ctx.pushStatus(Error::Gen, "missing GROUP BY clause");
+ return 0;
+ }
+ // in special cases add "group by 1"
+ if (m_groupRow == 0) {
+ bool addgb = false;
+ if (m_havingPred != 0) {
+ // allowed by oracle but nearly useless
+ addgb = true;
+ } else if (m_exprRow->anyAggr() && m_sortRow != 0) {
+ // allowed by oracle but useless
+ ctx_assert(m_exprRow->allBound());
+ addgb = true;
+ }
+ if (addgb) {
+ ctx_log2(("adding 'group by 1'"));
+ m_groupRow = new Plan_expr_row(m_root);
+ m_root->saveNode(m_groupRow);
+ LexType type(LexType::Integer);
+ Plan_expr* expr = new Plan_expr_const(m_root, type, "1");
+ m_root->saveNode(expr);
+ m_groupRow->addExpr(expr);
+ m_groupRow = static_cast<Plan_expr_row*>(m_groupRow->analyze(ctx, ctl));
+ ctx_assert(ctx.ok());
+ ctx_assert(m_groupRow != 0);
+ }
+ }
+ // check group by allowed
+ if (m_groupRow != 0) {
+ if (! m_exprRow->isAllGroupBy(m_groupRow)) {
+ ctx.pushStatus(Error::Gen, "invalid GROUP BY expression in SELECT list");
+ return 0;
+ }
+ if (m_havingPred != 0) {
+ if (! m_havingPred->isGroupBy(m_groupRow)) {
+ ctx.pushStatus(Error::Gen, "invalid GROUP BY expression in HAVING clause");
+ return 0;
+ }
+ }
+ if (m_sortRow != 0) {
+ if (! m_sortRow->isAllGroupBy(m_groupRow)) {
+ ctx.pushStatus(Error::Gen, "invalid GROUP BY expression in ORDER BY clause");
+ return 0;
+ }
+ }
+ }
+ // log top level predicate
+ {
+ unsigned n = 0;
+ for (PredList::iterator i = ctl.m_topcomp.begin(); i != ctl.m_topcomp.end(); i++)
+ ctx_log2(("top level pred %u: count tables = %u, not interp = %u",
+ ++n,
+ (unsigned)(*i)->tableSet().size(),
+ (unsigned)(*i)->noInterp().size()));
+ }
+ // compose the raw query from lookups and scans
+ Plan_query* queryRaw = 0;
+ TableVector tableVector(1);
+ TableSet tsDone;
+ while (tableVector.size() < ctl.m_tableList.size()) {
+ Plan_table* tableBest = 0;
+ Plan_table::Index* indexBest = 0;
+ for (unsigned n = 1; n < ctl.m_tableList.size(); n++) {
+ Plan_table* table = ctl.m_tableList[n];
+ if (tsDone.find(table) != tsDone.end())
+ continue;
+ // get system table out of the way
+ if (table->dictTable().sysId()) {
+ tableBest = table;
+ break;
+ }
+ // find best match for primary key or index
+ for (unsigned i = 0; i <= table->indexCount(); i++) {
+ Plan_table::Index& index = table->m_indexList[i];
+ table->resolveSet(ctx, index, tsDone);
+ if (! ctx.ok())
+ return 0;
+ if (! index.m_keyFound)
+ continue;
+ // prefer smaller dependency set, smaller rank, less unused keys
+ int k;
+ (k = (indexBest == 0)) ||
+ (k = (indexBest->m_keySet.size() - index.m_keySet.size())) ||
+ (k = (indexBest->m_rank - index.m_rank)) ||
+ (k = (indexBest->m_keyCountUnused - index.m_keyCountUnused));
+ if (k > 0) {
+ tableBest = table;
+ indexBest = &index;
+ }
+ }
+ }
+ Plan_query* queryNext = 0;
+ Plan_table* tableNext = 0;
+ Plan_query_scan* queryScan = 0; // for pushing interpreted program
+ Plan_query_range* queryRange = 0; // ditto
+ if (tableBest == 0) {
+ // scan first unprocessed table
+ for (unsigned n = 1; n < ctl.m_tableList.size(); n++) {
+ Plan_table* table = ctl.m_tableList[n];
+ if (tsDone.find(table) != tsDone.end())
+ continue;
+ tableNext = table;
+ break;
+ }
+ ctx_assert(tableNext != 0);
+ queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(tableNext);
+ queryNext = queryScan;
+ ctx_log2(("optim: scan %s", tableNext->getPrintName()));
+ } else if (tableBest->dictTable().sysId()) {
+ // "scan" system table
+ tableNext = tableBest;
+ Plan_query_sys* querySys = new Plan_query_sys(m_root);
+ m_root->saveNode(querySys);
+ querySys->setTable(tableNext);
+ queryNext = querySys;
+ ctx_log2(("optim: scan %s", tableNext->getPrintName()));
+ } else if (indexBest->m_keySet.size() > 0) {
+ // scan first table this one depends on
+ const TableSet& keySet = indexBest->m_keySet;
+ for (unsigned n = 1; n < ctl.m_tableList.size(); n++) {
+ Plan_table* table = ctl.m_tableList[n];
+ if (keySet.find(table) == keySet.end())
+ continue;
+ ctx_assert(tsDone.find(table) == tsDone.end());
+ tableNext = table;
+ break;
+ }
+ ctx_assert(tableNext != 0);
+ queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(tableNext);
+ queryNext = queryScan;
+ ctx_log2(("optim: scan %s for %s", tableNext->getPrintName(), tableBest->getPrintName()));
+ } else if (indexBest->m_rank == 0) {
+ // primary key depends only on processed tables
+ tableNext = tableBest;
+ Plan_query_lookup* queryLookup = new Plan_query_lookup(m_root);
+ m_root->saveNode(queryLookup);
+ queryLookup->setTable(tableNext);
+ queryNext = queryLookup;
+ ctx_log2(("optim: lookup %s", tableNext->getPrintName()));
+ } else if (indexBest->m_rank == 1) {
+ // hash index key depends only on processed tables
+ tableNext = tableBest;
+ Plan_query_index* queryIndex = new Plan_query_index(m_root);
+ m_root->saveNode(queryIndex);
+ queryIndex->setTable(tableNext, indexBest);
+ queryNext = queryIndex;
+ ctx_log2(("optim: lookup %s via index %s", tableNext->getPrintName(), indexBest->m_dictIndex->getName().c_str()));
+ } else if (indexBest->m_rank == 2) {
+ // ordered index key depends only on processed tables
+ tableNext = tableBest;
+ queryRange = new Plan_query_range(m_root);
+ m_root->saveNode(queryRange);
+ queryRange->setTable(tableNext, indexBest);
+ queryNext = queryRange;
+ ctx_log2(("optim: range scan %s via index %s", tableNext->getPrintName(), indexBest->m_dictIndex->getName().c_str()));
+ } else {
+ ctx_assert(false);
+ }
+ if (queryRaw == 0) {
+ queryRaw = queryNext;
+ } else {
+ Plan_query_join* queryJoin = new Plan_query_join(m_root);
+ m_root->saveNode(queryJoin);
+ queryJoin->setInner(queryRaw);
+ queryJoin->setOuter(queryNext);
+ queryRaw = queryJoin;
+ }
+ tableVector.push_back(tableNext);
+ tsDone.insert(tableNext);
+ // push down part of top level predicate to table scan or range scan
+ Plan_pred* predPush = 0;
+ Plan_pred* predInterp = 0;
+ PredList::iterator i = ctl.m_topcomp.begin();
+ while (i != ctl.m_topcomp.end()) {
+ const TableSet& ts = (*i)->tableSet();
+ if (! std::includes(tsDone.begin(), tsDone.end(), ts.begin(), ts.end())) {
+ i++;
+ continue;
+ }
+ predPush = predPush == 0 ? *i : predPush->opAnd(*i);
+ if (queryScan != 0) {
+ const TableSet& ts2 = (*i)->noInterp();
+ if (ts2.find(tableNext) == ts2.end())
+ predInterp = predInterp == 0 ? *i : predInterp->opAnd(*i);
+ }
+ if (queryRange != 0) {
+ const TableSet& ts2 = (*i)->noInterp();
+ if (ts2.find(tableNext) == ts2.end())
+ predInterp = predInterp == 0 ? *i : predInterp->opAnd(*i);
+ }
+ // remove it from top level predicate
+ PredList::iterator j = i;
+ i++;
+ ctl.m_topcomp.erase(j);
+ }
+ if (predPush != 0) {
+ Plan_query_filter* queryPush = new Plan_query_filter(m_root);
+ m_root->saveNode(queryPush);
+ queryPush->setQuery(queryRaw);
+ queryPush->setPred(predPush);
+ queryPush->m_topTable = tableNext;
+ queryRaw = queryPush;
+ }
+ if (predInterp != 0) {
+ if (queryScan != 0)
+ queryScan->setInterp(predInterp);
+ else if (queryRange != 0)
+ queryRange->setInterp(predInterp);
+ else
+ ctx_assert(false);
+ }
+ }
+ ctx_assert(ctl.m_topcomp.empty());
+ // set base for column position offsets
+ for (unsigned n = 1; n < tableVector.size(); n++) {
+ Plan_table* table = tableVector[n];
+ if (n == 1) {
+ table->m_resOff = 1;
+ } else {
+ Plan_table* tablePrev = tableVector[n - 1];
+ table->m_resOff = tablePrev->m_resOff + tablePrev->m_exprColumns.size() - 1;
+ }
+ }
+ // next level up is one of project, count, group by
+ Plan_query* queryTop;
+ if (m_groupRow == 0) {
+ if (! m_exprRow->anyAggr()) {
+ Plan_query_project* queryProject = new Plan_query_project(m_root);
+ m_root->saveNode(queryProject);
+ queryProject->setQuery(queryRaw);
+ queryProject->setRow(m_exprRow);
+ queryProject->setLimit(m_limitOff, m_limitCnt);
+ queryTop = queryProject;
+ } else {
+ ctx_assert(m_exprRow->allBound());
+ Plan_query_count* queryCount = new Plan_query_count(m_root);
+ m_root->saveNode(queryCount);
+ queryCount->setQuery(queryRaw);
+ queryCount->setRow(m_exprRow);
+ queryTop = queryCount;
+ }
+ } else {
+ Plan_query_group* queryGroup = new Plan_query_group(m_root);
+ m_root->saveNode(queryGroup);
+ queryGroup->setQuery(queryRaw);
+ queryGroup->setDataRow(m_exprRow);
+ queryGroup->setGroupRow(m_groupRow);
+ if (m_havingPred != 0)
+ queryGroup->setHavingPred(m_havingPred);
+ queryTop = queryGroup;
+ }
+ // optional sort becomes new top level
+ if (m_sortRow != 0) {
+ Plan_query_sort* querySort = new Plan_query_sort(m_root);
+ m_root->saveNode(querySort);
+ querySort->setQuery(queryTop);
+ querySort->setRow(m_sortRow);
+ queryTop = querySort;
+ }
+ // optional distinct becomes new top level
+ if (m_distinct) {
+ Plan_query_distinct* queryDistinct = new Plan_query_distinct(m_root);
+ m_root->saveNode(queryDistinct);
+ queryDistinct->setQuery(queryTop);
+ queryTop = queryDistinct;
+ }
+ // return top node
+ return queryTop;
+}
+
+Exec_base*
+Plan_select::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_select::print(Ctx& ctx)
+{
+ ctx.print(" [select");
+ Plan_base* a[] = { m_tableList, m_exprRow, m_pred, m_groupRow, m_havingPred };
+ printList(ctx, a, 5);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_select.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_select.hpp
new file mode 100644
index 00000000000..eaa9b801f29
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_select.hpp
@@ -0,0 +1,132 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_select_hpp
+#define ODBC_CODEGEN_Code_select_hpp
+
+#include <common/common.hpp>
+#include "Code_stmt.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_table_list.hpp"
+#include "Code_pred.hpp"
+
+/**
+ * @class Plan_select
+ * @brief General select in PlanTree
+ *
+ * General select. An initial PlanTree node.
+ */
+class Plan_select : public Plan_stmt {
+public:
+ Plan_select(Plan_root* root);
+ virtual ~Plan_select();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setList(Plan_table_list* tableList);
+ void setRow(Plan_expr_row* exprRow);
+ void setPred(Plan_pred* pred);
+ void setSort(Plan_expr_row* sortRow);
+ void setDistinct(bool distinct);
+ void setGroup(Plan_expr_row* groupRow);
+ void setHaving(Plan_pred* havingPred);
+ void setLimit(int off, int cnt);
+protected:
+ Plan_table_list* m_tableList;
+ Plan_expr_row* m_exprRow;
+ Plan_pred* m_pred;
+ Plan_expr_row* m_sortRow;
+ bool m_distinct;
+ Plan_expr_row* m_groupRow;
+ Plan_pred* m_havingPred;
+ int m_limitOff;
+ int m_limitCnt;
+};
+
+inline
+Plan_select::Plan_select(Plan_root* root) :
+ Plan_stmt(root),
+ m_tableList(0),
+ m_exprRow(0),
+ m_pred(0),
+ m_sortRow(0),
+ m_distinct(false),
+ m_groupRow(0),
+ m_havingPred(0),
+ m_limitOff(0),
+ m_limitCnt(-1)
+{
+}
+
+// children
+
+inline void
+Plan_select::setList(Plan_table_list* tableList)
+{
+ ctx_assert(tableList != 0);
+ m_tableList = tableList;
+}
+
+inline void
+Plan_select::setRow(Plan_expr_row* exprRow)
+{
+ ctx_assert(exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+inline void
+Plan_select::setPred(Plan_pred* pred)
+{
+ ctx_assert(pred != 0);
+ m_pred = pred;
+}
+
+inline void
+Plan_select::setSort(Plan_expr_row* sortRow)
+{
+ ctx_assert(sortRow != 0);
+ m_sortRow = sortRow;
+}
+
+inline void
+Plan_select::setDistinct(bool distinct)
+{
+ m_distinct = distinct;
+}
+
+inline void
+Plan_select::setGroup(Plan_expr_row* groupRow)
+{
+ ctx_assert(groupRow != 0);
+ m_groupRow = groupRow;
+}
+
+inline void
+Plan_select::setHaving(Plan_pred* havingPred)
+{
+ ctx_assert(havingPred != 0);
+ m_havingPred = havingPred;
+}
+
+inline void
+Plan_select::setLimit(int off, int cnt)
+{
+ m_limitOff = off;
+ m_limitCnt = cnt;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.cpp
new file mode 100644
index 00000000000..dd13ba0c3f7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.cpp
@@ -0,0 +1,44 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_set_row.hpp"
+#include "Code_dml_column.hpp"
+
+Plan_set_row::~Plan_set_row()
+{
+}
+
+Plan_base*
+Plan_set_row::analyze(Ctx& ctx, Ctl& ctl)
+{
+ return this;
+}
+
+Exec_base*
+Plan_set_row::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_set_row::print(Ctx& ctx)
+{
+ ctx.print(" [set_row");
+ Plan_base* a[] = { m_dmlRow, m_exprRow };
+ printList(ctx, a, 2);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.hpp
new file mode 100644
index 00000000000..10d62826ac7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.hpp
@@ -0,0 +1,76 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_set_row_hpp
+#define ODBC_CODEGEN_Code_set_row_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include <common/DataRow.hpp>
+#include "Code_base.hpp"
+#include "Code_dml_row.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_root.hpp"
+
+/**
+ * @class Plan_set_row
+ * @brief Row of column assigments in update
+ *
+ * Used only in parse. The column and expression rows are moved
+ * to the update node immediately after parse.
+ */
+class Plan_set_row : public Plan_base {
+public:
+ Plan_set_row(Plan_root* root);
+ virtual ~Plan_set_row();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void addColumn(Plan_dml_column* column);
+ void addExpr(Plan_expr* expr);
+protected:
+ friend class Plan_update;
+ friend class Plan_insert; // for MySql
+ Plan_dml_row* m_dmlRow;
+ Plan_expr_row* m_exprRow;
+};
+
+inline
+Plan_set_row::Plan_set_row(Plan_root* root) :
+ Plan_base(root)
+{
+ m_dmlRow = new Plan_dml_row(root);
+ root->saveNode(m_dmlRow);
+ m_exprRow = new Plan_expr_row(root);
+ root->saveNode(m_exprRow);
+}
+
+// children
+
+inline void
+Plan_set_row::addColumn(Plan_dml_column* column)
+{
+ m_dmlRow->addColumn(column);
+}
+
+inline void
+Plan_set_row::addExpr(Plan_expr* expr)
+{
+ m_exprRow->addExpr(expr);
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.cpp
new file mode 100644
index 00000000000..d790f667b84
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_stmt.hpp"
+
+// Plan_stmt
+
+Plan_stmt::~Plan_stmt()
+{
+}
+
+// XXX remove
+void
+Plan_stmt::describe(Ctx& ctx)
+{
+ ctx_log1(("unimplemented describe"));
+}
+
+// Exec_stmt
+
+Exec_stmt::Code::~Code()
+{
+}
+
+Exec_stmt::Data::~Data()
+{
+}
+
+Exec_stmt::~Exec_stmt()
+{
+}
+
+void
+Exec_stmt::bind(Ctx& ctx)
+{
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.hpp
new file mode 100644
index 00000000000..20b7fb965fb
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.hpp
@@ -0,0 +1,76 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_stmt_hpp
+#define ODBC_CODEGEN_Code_stmt_hpp
+
+#include <common/common.hpp>
+#include <common/DataType.hpp>
+#include "Code_base.hpp"
+
+class Ctx;
+
+/**
+ * @class Plan_stmt
+ * @brief Base class for statements in PlanTree
+ *
+ * A statement is a complete or partial SQL statement which can
+ * be optimized into executable statements Exec_stmt.
+ */
+class Plan_stmt : public Plan_base {
+public:
+ Plan_stmt(Plan_root* root);
+ virtual ~Plan_stmt() = 0;
+ virtual void describe(Ctx& ctx);
+};
+
+inline
+Plan_stmt::Plan_stmt(Plan_root* root) :
+ Plan_base(root)
+{
+}
+
+/**
+ * @class Exec_stmt
+ * @brief Base class for statements in ExecTree
+ */
+class Exec_stmt : public Exec_base {
+public:
+ class Code : public Exec_base::Code {
+ public:
+ virtual ~Code() = 0;
+ };
+ class Data : public Exec_base::Data {
+ public:
+ virtual ~Data() = 0;
+ };
+ Exec_stmt(Exec_root* root);
+ virtual ~Exec_stmt() = 0;
+ virtual void bind(Ctx& ctx);
+ virtual void execute(Ctx& ctx, Ctl& ctl) = 0;
+protected:
+ friend class Exec_root;
+ bool m_topLevel;
+};
+
+inline
+Exec_stmt::Exec_stmt(Exec_root* root) :
+ Exec_base(root),
+ m_topLevel(false)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_table.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_table.cpp
new file mode 100644
index 00000000000..ee3c2a2ed07
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_table.cpp
@@ -0,0 +1,254 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_table.hpp"
+#include "Code_column.hpp"
+#include "Code_expr_column.hpp"
+
+Plan_table::~Plan_table()
+{
+}
+
+Plan_base*
+Plan_table::analyze(Ctx& ctx, Ctl& ctl)
+{
+ if (m_dictTable != 0) // already done
+ return this;
+ DictTable* table = dictSchema().findTable(m_name);
+ if (table == 0) {
+ table = dictSchema().loadTable(ctx, m_name);
+ if (table == 0) {
+ ctx.pushStatus(Sqlstate::_42S02, Error::Gen, "table %s not found", m_name.c_str());
+ return 0;
+ }
+ }
+ m_dictTable = table;
+ // indexes
+ m_indexList.resize(1 + m_dictTable->indexCount());
+ for (unsigned i = 0; i <= indexCount(); i++) {
+ Index& index = m_indexList[i];
+ index.m_pos = i;
+ if (index.m_pos == 0) {
+ index.m_keyCount = m_dictTable->keyCount();
+ index.m_rank = 0;
+ } else {
+ index.m_dictIndex = m_dictTable->getIndex(i);
+ index.m_keyCount = index.m_dictIndex->getSize();
+ if (index.m_dictIndex->getType() == NdbDictionary::Object::UniqueHashIndex) {
+ index.m_rank = 1;
+ } else if (index.m_dictIndex->getType() == NdbDictionary::Object::OrderedIndex) {
+ index.m_rank = 2;
+ } else {
+ ctx_assert(false);
+ }
+ }
+ index.m_keyEqList.resize(1 + index.m_keyCount);
+ }
+ return this;
+}
+
+int
+Plan_table::resolveColumn(Ctx& ctx, Plan_column* column, bool stripSchemaName)
+{
+ ctx_assert(column != 0);
+ bool dml, unq;
+ switch (column->m_type) {
+ case Plan_column::Type_expr:
+ dml = false;
+ unq = false;
+ break;
+ case Plan_column::Type_dml:
+ dml = true;
+ unq = true;
+ break;
+ case Plan_column::Type_idx:
+ dml = false;
+ unq = true;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ ColumnVector& columns = ! dml ? m_exprColumns : m_dmlColumns;
+ const BaseString& name = column->m_name;
+ const BaseString& cname = column->m_cname;
+ ctx_log3(("resolve %s column %s in table %s", ! dml ? "expr" : "dml", column->getPrintName(), getPrintName()));
+ // find column in table
+ DictColumn* dictColumn = dictTable().findColumn(name);
+ if (dictColumn == 0)
+ return 0;
+ // qualified column must match table correlation name
+ if (! cname.empty()) {
+ const char* str;
+ if (! m_cname.empty()) {
+ str = m_cname.c_str();
+ } else {
+ str = m_name.c_str();
+ if (stripSchemaName && strrchr(str, '.') != 0)
+ str = strrchr(str, '.') + 1;
+ }
+ if (strcmp(cname.c_str(), str) != 0)
+ return 0;
+ }
+ // find in positional list or add to it
+ unsigned resPos;
+ for (resPos = 1; resPos < columns.size(); resPos++) {
+ if (strcmp(columns[resPos]->getName().c_str(), name.c_str()) != 0)
+ continue;
+ // these columns must be unique
+ if (unq) {
+ ctx.pushStatus(Error::Gen, "duplicate column %s", column->getName().c_str());
+ return -1;
+ }
+ break;
+ }
+ if (resPos >= columns.size()) {
+ columns.push_back(column);
+ }
+ ctx_log3(("resolve to attrId %u pos %u", (unsigned)dictColumn->getAttrId(), resPos));
+ column->m_dictColumn = dictColumn;
+ column->m_resTable = this;
+ column->m_resPos = resPos;
+ // found
+ return 1;
+}
+
+bool
+Plan_table::resolveEq(Ctx& ctx, Plan_expr_column* column, Plan_expr* expr)
+{
+ ctx_assert(m_dictTable != 0);
+ const TableSet& ts = expr->tableSet();
+ TableSet::const_iterator i = ts.find(this);
+ if (i != ts.end())
+ return false;
+ unsigned found = 0;
+ for (unsigned i = 0; i <= indexCount(); i++) {
+ Index& index = m_indexList[i];
+ for (unsigned n = 1, cnt = 0; n <= index.m_keyCount; n++) {
+ const DictColumn* dictColumn = 0;
+ if (index.m_pos == 0) {
+ ctx_assert(m_dictTable != 0);
+ dictColumn = m_dictTable->getKey(n);
+ } else {
+ ctx_assert(index.m_dictIndex != 0);
+ dictColumn = index.m_dictIndex->getColumn(n);
+ }
+ if (dictColumn != &column->dictColumn())
+ continue;
+ ctx_assert(++cnt == 1);
+ index.m_keyEqList[n].push_back(expr);
+ if (index.m_pos == 0)
+ ctx_log2(("%s: found match to primary key column %s pos %u", getPrintName(), column->getPrintName(), n));
+ else
+ ctx_log2(("%s: found match to index %s column %s pos %u", getPrintName(), index.m_dictIndex->getName().c_str(), column->getPrintName(), n));
+ found++;
+ }
+ }
+ return (found != 0);
+}
+
+void
+Plan_table::resolveSet(Ctx& ctx, Index& index, const TableSet& tsDone)
+{
+ index.m_keyFound = false;
+ ExprVector keyEq;
+ keyEq.resize(1 + index.m_keyCount);
+ resolveSet(ctx, index, tsDone, keyEq, 1);
+}
+
+void
+Plan_table::resolveSet(Ctx& ctx, Index& index, const TableSet& tsDone, ExprVector& keyEq, unsigned n)
+{
+ if (n <= index.m_keyCount) {
+ // building up combinations
+ ExprList& keyEqList = index.m_keyEqList[n];
+ for (ExprList::iterator i = keyEqList.begin(); i != keyEqList.end(); i++) {
+ keyEq[n] = *i;
+ resolveSet(ctx, index, tsDone, keyEq, n + 1);
+ }
+ if (! keyEqList.empty() || index.m_rank <= 1 || n == 1)
+ return;
+ // ordered index with maximal initial key match
+ }
+ TableSet keySet;
+ for (unsigned i = 1; i <= n - 1; i++) {
+ const TableSet& tableSet = keyEq[i]->tableSet();
+ for (TableSet::const_iterator j = tableSet.begin(); j != tableSet.end(); j++) {
+ if (tsDone.find(*j) == tsDone.end())
+ keySet.insert(*j);
+ }
+ }
+ if (! index.m_keyFound || index.m_keySet.size() > keySet.size()) {
+ index.m_keyFound = true;
+ index.m_keyEq = keyEq;
+ index.m_keySet = keySet;
+ index.m_keyCountUsed = n - 1;
+ index.m_keyCountUnused = index.m_keyCount - index.m_keyCountUsed;
+ // set matching size
+ index.m_keyEq.resize(1 + index.m_keyCountUsed);
+ }
+}
+
+bool
+Plan_table::exactKey(Ctx& ctx, const Index* indexKey) const
+{
+ ctx_assert(indexKey != 0 && indexKey == &m_indexList[indexKey->m_pos]);
+ for (unsigned i = 0; i <= indexCount(); i++) {
+ const Index& index = m_indexList[i];
+ const ExprListVector& keyEqList = index.m_keyEqList;
+ for (unsigned n = 1; n <= index.m_keyCount; n++) {
+ if (index.m_pos == indexKey->m_pos) {
+ ctx_assert(keyEqList[n].size() >= 1);
+ if (keyEqList[n].size() > 1) {
+ ctx_log2(("index %u not exact: column %u has %u > 1 matches",
+ indexKey->m_pos,
+ n,
+ (unsigned)keyEqList[n].size()));
+ return false;
+ }
+ } else {
+ if (keyEqList[n].size() > 0) {
+ ctx_log2(("index %u not exact: index %u column %u has %u > 0 matches",
+ indexKey->m_pos,
+ index.m_pos,
+ n,
+ (unsigned)keyEqList[n].size()));
+ return false;
+ }
+ }
+ }
+ }
+ ctx_log2(("index %u is exact", indexKey->m_pos));
+ return true;
+}
+
+Exec_base*
+Plan_table::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_table::print(Ctx& ctx)
+{
+ ctx.print(" [table %s]", getPrintName());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_table.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_table.hpp
new file mode 100644
index 00000000000..8a95b8fa26c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_table.hpp
@@ -0,0 +1,202 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_table_hpp
+#define ODBC_CODEGEN_Code_table_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include "Code_base.hpp"
+
+class DictTable;
+class DictColumn;
+class DictIndex;
+class Plan_query_filter;
+class Plan_query_lookup;
+class Plan_query_range;
+class Plan_column;
+class Plan_expr_column;
+class Plan_select;
+class Plan_delete;
+class Plan_delete_lookup;
+class Plan_update;
+class Plan_update_lookup;
+
+/**
+ * @class Plan_table
+ * @brief Table node in PlanTree
+ *
+ * This is a pure Plan node. Final executable nodes have table
+ * information built-in.
+ */
+class Plan_table : public Plan_base {
+public:
+ Plan_table(Plan_root* root, const BaseString& name);
+ virtual ~Plan_table();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // attributes
+ const BaseString& getName() const;
+ const BaseString& getCname() const;
+ const char* getPrintName() const;
+ void setCname(const BaseString& cname);
+ const DictTable& dictTable() const;
+ unsigned indexCount() const;
+ // resolve
+ const ColumnVector& exprColumns() const;
+ const ColumnVector& dmlColumns() const;
+protected:
+ friend class Plan_column;
+ friend class Plan_query_filter;
+ friend class Plan_query_lookup;
+ friend class Plan_query_index;
+ friend class Plan_query_range;
+ friend class Plan_expr_column;
+ friend class Plan_select;
+ friend class Plan_delete;
+ friend class Plan_delete_lookup;
+ friend class Plan_delete_index;
+ friend class Plan_update;
+ friend class Plan_update_lookup;
+ friend class Plan_update_index;
+ BaseString m_name;
+ BaseString m_cname;
+ BaseString m_printName;
+ DictTable* m_dictTable;
+ /*
+ * Resolve column. Returns 1 on found, 0 on not found, and -1 on error.
+ * Modifies both table and column data.
+ */
+ int resolveColumn(Ctx& ctx, Plan_column* column, bool stripSchemaName = false);
+ ColumnVector m_exprColumns;
+ ColumnVector m_dmlColumns;
+ /*
+ * Offset for resolved columns in join. This is sum over m_exprColumns
+ * lengths for all preceding tables.
+ */
+ unsigned m_resOff;
+ /*
+ * Each column in primary key and unique hash index has list of
+ * expressions it is set equal to in the where-clause (at top level).
+ */
+ bool resolveEq(Ctx& ctx, Plan_expr_column* column, Plan_expr* expr);
+ /*
+ * Index struct for primary key and indexes.
+ */
+ struct Index {
+ Index() :
+ m_pos(0),
+ m_keyFound(false),
+ m_dictIndex(0),
+ m_rank(~0),
+ m_keyCount(0),
+ m_keyCountUsed(0) {
+ }
+ unsigned m_pos;
+ ExprListVector m_keyEqList;
+ bool m_keyFound;
+ ExprVector m_keyEq;
+ TableSet m_keySet;
+ const DictIndex* m_dictIndex; // for index only
+ unsigned m_rank; // 0-pk 1-hash index 2-ordered index
+ unsigned m_keyCount; // number of columns
+ unsigned m_keyCountUsed; // may be less for ordered index
+ unsigned m_keyCountUnused; // m_keyCount - m_keyCountUsed
+ };
+ typedef std::vector<Index> IndexList; // primary key is entry 0
+ IndexList m_indexList;
+ /*
+ * Find set of additional tables (maybe empty) required to resolve the key
+ * columns.
+ */
+ void resolveSet(Ctx& ctx, Index& index, const TableSet& tsDone);
+ void resolveSet(Ctx& ctx, Index& index, const TableSet& tsDone, ExprVector& keyEq, unsigned n);
+ /*
+ * Check for exactly one key or index match.
+ */
+ bool exactKey(Ctx& ctx, const Index* indexKey) const;
+};
+
+inline
+Plan_table::Plan_table(Plan_root* root, const BaseString& name) :
+ Plan_base(root),
+ m_name(name),
+ m_printName(name),
+ m_dictTable(0),
+ m_exprColumns(1), // 1-based
+ m_dmlColumns(1), // 1-based
+ m_resOff(0),
+ m_indexList(1)
+{
+}
+
+inline const BaseString&
+Plan_table::getName() const
+{
+ return m_name;
+}
+
+inline const BaseString&
+Plan_table::getCname() const
+{
+ return m_cname;
+}
+
+inline const char*
+Plan_table::getPrintName() const
+{
+ return m_printName.c_str();
+}
+
+inline void
+Plan_table::setCname(const BaseString& cname)
+{
+ m_cname.assign(cname);
+ m_printName.assign(m_name);
+ if (! m_cname.empty()) {
+ m_printName.append(" ");
+ m_printName.append(m_cname);
+ }
+}
+
+inline const DictTable&
+Plan_table::dictTable() const
+{
+ ctx_assert(m_dictTable != 0);
+ return *m_dictTable;
+}
+
+inline unsigned
+Plan_table::indexCount() const
+{
+ ctx_assert(m_indexList.size() > 0);
+ return m_indexList.size() - 1;
+}
+
+inline const Plan_table::ColumnVector&
+Plan_table::exprColumns() const
+{
+ return m_exprColumns;
+}
+
+inline const Plan_table::ColumnVector&
+Plan_table::dmlColumns() const
+{
+ return m_dmlColumns;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.cpp
new file mode 100644
index 00000000000..ea9f4fdc26e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.cpp
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Code_table_list.hpp"
+
+Plan_table_list::~Plan_table_list()
+{
+}
+
+Plan_base*
+Plan_table_list::analyze(Ctx& ctx, Ctl& ctl)
+{
+ // analyze the tables
+ for (unsigned i = 1, n = countTable(); i <= n; i++) {
+ Plan_table* table = getTable(i);
+ table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ // node was not replaced
+ return this;
+}
+
+Exec_base*
+Plan_table_list::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_table_list::print(Ctx& ctx)
+{
+ ctx.print(" [table_list");
+ for (unsigned i = 1, n = countTable(); i <= n; i++) {
+ Plan_base* a[] = { m_tableList[i] };
+ printList(ctx, a, 1);
+ }
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.hpp
new file mode 100644
index 00000000000..47989166cac
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.hpp
@@ -0,0 +1,73 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_table_list_hpp
+#define ODBC_CODEGEN_Code_table_list_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_table.hpp"
+
+/**
+ * @class Plan_table_list
+ * @brief List of tables in select statement
+ */
+class Plan_table_list : public Plan_base {
+public:
+ Plan_table_list(Plan_root* root);
+ virtual ~Plan_table_list();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ unsigned countTable() const;
+ void addTable(Plan_table* table);
+ Plan_table* getTable(unsigned i) const;
+protected:
+ friend class Plan_select;
+ TableVector m_tableList;
+};
+
+inline
+Plan_table_list::Plan_table_list(Plan_root* root) :
+ Plan_base(root),
+ m_tableList(1)
+{
+}
+
+// children
+
+inline unsigned
+Plan_table_list::countTable() const
+{
+ return m_tableList.size() - 1;
+}
+
+inline void
+Plan_table_list::addTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_tableList.push_back(table);
+}
+
+inline Plan_table*
+Plan_table_list::getTable(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= countTable() && m_tableList[i] != 0);
+ return m_tableList[i];
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update.cpp
new file mode 100644
index 00000000000..0b33cd628b4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update.cpp
@@ -0,0 +1,246 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_update.hpp"
+#include "Code_update_lookup.hpp"
+#include "Code_update_index.hpp"
+#include "Code_update_scan.hpp"
+#include "Code_table.hpp"
+#include "Code_query_project.hpp"
+#include "Code_query_filter.hpp"
+#include "Code_query_scan.hpp"
+#include "Code_query_lookup.hpp"
+#include "Code_query_index.hpp"
+#include "Code_query_range.hpp"
+#include "Code_query_repeat.hpp"
+#include "Code_root.hpp"
+
+// Plan_update
+
+Plan_update::~Plan_update()
+{
+}
+
+Plan_base*
+Plan_update::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_update);
+ // analyze the table
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ // get column and expression rows
+ ctx_assert(m_setRow != 0);
+ setDmlRow(m_setRow->m_dmlRow);
+ setExprRow(m_setRow->m_exprRow);
+ m_setRow = 0;
+ // implied by parse
+ ctx_assert(m_dmlRow->getSize() == m_exprRow->getSize());
+ // set name resolution scope
+ ctl.m_tableList.resize(1 + 1); // indexed from 1
+ ctl.m_tableList[1] = m_table;
+ // analyze the rows
+ m_dmlRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctl.m_dmlRow = m_dmlRow; // row type to convert to
+ ctl.m_const = true; // set to constants
+ m_exprRow->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ bool setConst = ctl.m_const;
+ ctl.m_dmlRow = 0;
+ Plan_dml* stmt = 0;
+ // top level query is a project
+ Plan_query_project* queryProject = new Plan_query_project(m_root);
+ m_root->saveNode(queryProject);
+ queryProject->setRow(m_exprRow);
+ if (m_pred != 0) {
+ // analyze the predicate
+ ctl.m_topand = true;
+ ctl.m_extra = false;
+ m_pred = static_cast<Plan_pred*>(m_pred->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_pred != 0);
+ // check for key match
+ Plan_table::Index* indexBest = 0;
+ for (unsigned i = 0; i <= m_table->indexCount(); i++) {
+ Plan_table::Index& index = m_table->m_indexList[i];
+ TableSet tsDone;
+ m_table->resolveSet(ctx, index, tsDone);
+ if (! ctx.ok())
+ return 0;
+ if (! index.m_keyFound)
+ continue;
+ // prefer smaller rank, less unused keys
+ int k;
+ (k = (indexBest == 0)) ||
+ (k = (indexBest->m_rank - index.m_rank)) ||
+ (k = (indexBest->m_keyCountUnused - index.m_keyCountUnused));
+ if (k > 0)
+ indexBest = &index;
+ }
+ if (indexBest != 0) {
+ const bool exactKey = indexBest->m_rank <= 1 ? m_table->exactKey(ctx, indexBest) : false;
+ const bool direct = setConst && ! ctl.m_extra && exactKey;
+ ctx_log3(("update direct=%d: const=%d extra=%d exact=%d", direct, setConst, ctl.m_extra, exactKey));
+ if (indexBest->m_rank == 0) {
+ // primary key
+ Plan_update_lookup* updateLookup = new Plan_update_lookup(m_root);
+ m_root->saveNode(updateLookup);
+ updateLookup->setTable(m_table);
+ updateLookup->setDmlRow(m_dmlRow);
+ if (direct) {
+ // constant values and exact key match
+ Plan_query_repeat* queryRepeat = new Plan_query_repeat(m_root, 1);
+ m_root->saveNode(queryRepeat);
+ queryProject->setQuery(queryRepeat);
+ } else {
+ // more conditions or non-constant values
+ Plan_query_lookup* queryLookup = new Plan_query_lookup(m_root);
+ m_root->saveNode(queryLookup);
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ queryLookup->setTable(m_table);
+ queryFilter->setQuery(queryLookup);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ queryProject->setQuery(queryFilter);
+ }
+ updateLookup->setQuery(queryProject);
+ stmt = updateLookup;
+ } else if (indexBest->m_rank == 1) {
+ // hash index
+ Plan_update_index* updateIndex = new Plan_update_index(m_root);
+ m_root->saveNode(updateIndex);
+ updateIndex->setTable(m_table, indexBest);
+ updateIndex->setDmlRow(m_dmlRow);
+ if (direct) {
+ // constant values and exact key match
+ Plan_query_repeat* queryRepeat = new Plan_query_repeat(m_root, 1);
+ m_root->saveNode(queryRepeat);
+ queryProject->setQuery(queryRepeat);
+ } else {
+ // more conditions or non-constant values
+ Plan_query_index* queryIndex = new Plan_query_index(m_root);
+ m_root->saveNode(queryIndex);
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ queryIndex->setTable(m_table, indexBest);
+ queryFilter->setQuery(queryIndex);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ queryProject->setQuery(queryFilter);
+ }
+ updateIndex->setQuery(queryProject);
+ stmt = updateIndex;
+ } else if (indexBest->m_rank == 2) {
+ // ordered index
+ Plan_update_scan* updateScan = new Plan_update_scan(m_root);
+ m_root->saveNode(updateScan);
+ updateScan->setTable(m_table);
+ updateScan->setDmlRow(m_dmlRow);
+ Plan_query_range* queryRange = new Plan_query_range(m_root);
+ m_root->saveNode(queryRange);
+ queryRange->setTable(m_table, indexBest);
+ queryRange->setExclusive();
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ queryFilter->setQuery(queryRange);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ // interpeter
+ const TableSet& ts2 = m_pred->noInterp();
+ ctx_assert(ts2.size() <= 1);
+ if (ts2.size() == 0) {
+ queryRange->setInterp(m_pred);
+ }
+ queryProject->setQuery(queryFilter);
+ updateScan->setQuery(queryProject);
+ stmt = updateScan;
+ } else {
+ ctx_assert(false);
+ }
+ } else {
+ // scan update with filter
+ Plan_update_scan* updateScan = new Plan_update_scan(m_root);
+ m_root->saveNode(updateScan);
+ updateScan->setTable(m_table);
+ updateScan->setDmlRow(m_dmlRow);
+ Plan_query_scan* queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(m_table);
+ queryScan->setExclusive();
+ Plan_query_filter* queryFilter = new Plan_query_filter(m_root);
+ m_root->saveNode(queryFilter);
+ queryFilter->setQuery(queryScan);
+ queryFilter->setPred(m_pred);
+ queryFilter->m_topTable = m_table;
+ // interpeter
+ const TableSet& ts2 = m_pred->noInterp();
+ ctx_assert(ts2.size() <= 1);
+ if (ts2.size() == 0) {
+ queryScan->setInterp(m_pred);
+ }
+ queryProject->setQuery(queryFilter);
+ updateScan->setQuery(queryProject);
+ stmt = updateScan;
+ }
+ } else {
+ // scan update without filter
+ Plan_update_scan* updateScan = new Plan_update_scan(m_root);
+ m_root->saveNode(updateScan);
+ updateScan->setTable(m_table);
+ updateScan->setDmlRow(m_dmlRow);
+ Plan_query_scan* queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(m_table);
+ queryScan->setExclusive();
+ queryProject->setQuery(queryScan);
+ updateScan->setQuery(queryProject);
+ stmt = updateScan;
+ }
+ // set base for column position offsets
+ m_table->m_resOff = 1;
+ return stmt;
+}
+
+void
+Plan_update::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "UPDATE WHERE", SQL_DIAG_UPDATE_WHERE);
+}
+
+Exec_base*
+Plan_update::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_update::print(Ctx& ctx)
+{
+ ctx.print(" [update");
+ Plan_base* a[] = { m_table, m_setRow, m_dmlRow, m_exprRow };
+ printList(ctx, a, 4);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update.hpp
new file mode 100644
index 00000000000..380b651518b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update.hpp
@@ -0,0 +1,102 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_update_hpp
+#define ODBC_CODEGEN_Code_update_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_dml.hpp"
+#include "Code_set_row.hpp"
+#include "Code_table.hpp"
+#include "Code_pred.hpp"
+#include "Code_query.hpp"
+
+/**
+ * @class Plan_update
+ * @brief Update in PlanTree
+ */
+class Plan_update : public Plan_dml {
+public:
+ Plan_update(Plan_root* root);
+ virtual ~Plan_update();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+ void setRow(Plan_set_row* setRow);
+ void setDmlRow(Plan_dml_row* dmlRow);
+ void setExprRow(Plan_expr_row* exprRow);
+ void setPred(Plan_pred* pred);
+protected:
+ Plan_table* m_table;
+ Plan_set_row* m_setRow;
+ Plan_dml_row* m_dmlRow;
+ Plan_expr_row* m_exprRow;
+ Plan_pred* m_pred;
+};
+
+inline
+Plan_update::Plan_update(Plan_root* root) :
+ Plan_dml(root),
+ m_table(0),
+ m_setRow(0),
+ m_dmlRow(0),
+ m_exprRow(0),
+ m_pred(0)
+{
+}
+
+// children
+
+inline void
+Plan_update::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline void
+Plan_update::setRow(Plan_set_row* setRow)
+{
+ ctx_assert(setRow != 0);
+ m_setRow = setRow;
+}
+
+inline void
+Plan_update::setDmlRow(Plan_dml_row* dmlRow)
+{
+ ctx_assert(dmlRow != 0);
+ m_dmlRow = dmlRow;
+}
+
+inline void
+Plan_update::setExprRow(Plan_expr_row* exprRow)
+{
+ ctx_assert(exprRow != 0);
+ m_exprRow = exprRow;
+}
+
+inline void
+Plan_update::setPred(Plan_pred* pred)
+{
+ ctx_assert(pred != 0);
+ m_pred = pred;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.cpp
new file mode 100644
index 00000000000..6f74db0d913
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.cpp
@@ -0,0 +1,196 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_dml_column.hpp"
+#include "Code_expr.hpp"
+#include "Code_update_index.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+// Plan_update_index
+
+Plan_update_index::~Plan_update_index()
+{
+}
+
+Plan_base*
+Plan_update_index::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctl.m_dmlRow = m_dmlRow; // row type to convert to
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_update_index::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "UPDATE WHERE", SQL_DIAG_UPDATE_WHERE);
+}
+
+Exec_base*
+Plan_update_index::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // generate code for the query
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // set up
+ ctx_assert(m_table != 0 && m_index != 0);
+ const BaseString& tableName = m_table->getName();
+ ctx_assert(m_index->m_dictIndex != 0);
+ const DictIndex& dictIndex = *m_index->m_dictIndex;
+ const BaseString& indexName = dictIndex.getName();
+ const unsigned keyCount = m_index->m_keyCount;
+ const ColumnVector& columns = m_table->dmlColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_update_index::Code& code = *new Exec_update_index::Code(keyCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ code.m_indexName = strcpy(new char[indexName.length() + 1], indexName.c_str());
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictIndex.getColumn(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = k - 1; // index column order
+ }
+ // matching expressions
+ ctx_assert(m_index->m_keyFound);
+ const ExprVector& keyEq = m_index->m_keyEq;
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // updated attributes
+ code.m_attrCount = attrCount;
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_update_index* exec = new Exec_update_index(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_update_index::print(Ctx& ctx)
+{
+ ctx.print(" [update_index");
+ Plan_base* a[] = { m_table, m_query };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ ctx.print("]");
+}
+
+// Exec_delete
+
+Exec_update_index::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+ delete[] m_attrId;
+}
+
+Exec_update_index::Data::~Data()
+{
+}
+
+Exec_update_index::~Exec_update_index()
+{
+}
+
+void
+Exec_update_index::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // create data
+ Data& data = *new Data;
+ setData(data);
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+}
+
+void
+Exec_update_index::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+}
+
+void
+Exec_update_index::print(Ctx& ctx)
+{
+ ctx.print(" [update_index");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" keyId=");
+ for (unsigned i = 1; i <= code.m_keyCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_keyId[i]);
+ }
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ }
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.hpp
new file mode 100644
index 00000000000..bbad822650a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.hpp
@@ -0,0 +1,171 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_update_index_hpp
+#define ODBC_CODEGEN_Code_update_index_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_dml.hpp"
+#include "Code_table.hpp"
+#include "Code_query.hpp"
+
+/**
+ * @class Plan_update_index
+ * @brief Update in PlanTree
+ */
+class Plan_update_index : public Plan_dml {
+public:
+ Plan_update_index(Plan_root* root);
+ virtual ~Plan_update_index();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table, Plan_table::Index* index);
+ void setDmlRow(Plan_dml_row* dmlRow);
+ void setQuery(Plan_query* query);
+protected:
+ Plan_table* m_table;
+ Plan_table::Index* m_index;
+ Plan_dml_row* m_dmlRow;
+ Plan_query* m_query;
+};
+
+inline
+Plan_update_index::Plan_update_index(Plan_root* root) :
+ Plan_dml(root),
+ m_table(0),
+ m_dmlRow(0),
+ m_query(0)
+{
+}
+
+inline void
+Plan_update_index::setTable(Plan_table* table, Plan_table::Index* index)
+{
+ ctx_assert(table != 0 && index != 0 && index == &table->m_indexList[index->m_pos] && index->m_pos != 0);
+ m_table = table;
+ m_index = index;
+}
+
+inline void
+Plan_update_index::setDmlRow(Plan_dml_row* dmlRow)
+{
+ ctx_assert(dmlRow != 0);
+ m_dmlRow = dmlRow;
+}
+
+inline void
+Plan_update_index::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+/**
+ * @class Exec_update_index
+ * @brief Insert in ExecTree
+ */
+class Exec_update_index : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code(unsigned keyCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_update_index;
+ friend class Exec_update_index;
+ const char* m_tableName;
+ const char* m_indexName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ unsigned m_attrCount;
+ NdbAttrId* m_attrId;
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_update_index;
+ };
+ Exec_update_index(Exec_root* root);
+ virtual ~Exec_update_index();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+protected:
+ Exec_query* m_query;
+};
+
+inline
+Exec_update_index::Code::Code(unsigned keyCount) :
+ m_tableName(0),
+ m_indexName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_keyMatch(0),
+ m_attrCount(0),
+ m_attrId(0)
+{
+}
+
+inline
+Exec_update_index::Data::Data()
+{
+}
+
+inline
+Exec_update_index::Exec_update_index(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_update_index::Code&
+Exec_update_index::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_update_index::Data&
+Exec_update_index::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_update_index::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0 && m_query == 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.cpp
new file mode 100644
index 00000000000..7525fb72692
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.cpp
@@ -0,0 +1,194 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_dml_column.hpp"
+#include "Code_expr.hpp"
+#include "Code_update_lookup.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+// Plan_update_lookup
+
+Plan_update_lookup::~Plan_update_lookup()
+{
+}
+
+Plan_base*
+Plan_update_lookup::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctl.m_dmlRow = m_dmlRow; // row type to convert to
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_update_lookup::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "UPDATE WHERE", SQL_DIAG_UPDATE_WHERE);
+}
+
+Exec_base*
+Plan_update_lookup::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // generate code for the query
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // set up
+ ctx_assert(m_table != 0);
+ const BaseString& tableName = m_table->getName();
+ const DictTable& dictTable = m_table->dictTable();
+ const unsigned keyCount = dictTable.keyCount();
+ const ColumnVector& columns = m_table->dmlColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_update_lookup::Code& code = *new Exec_update_lookup::Code(keyCount);
+ code.m_tableName = strcpy(new char[tableName.length() + 1], tableName.c_str());
+ // key attributes
+ code.m_keyId = new NdbAttrId[1 + keyCount];
+ code.m_keyId[0] = (NdbAttrId)-1;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ const DictColumn* keyColumn = dictTable.getKey(k);
+ const SqlType& sqlType = keyColumn->sqlType();
+ SqlSpec sqlSpec(sqlType, SqlSpec::Physical);
+ code.m_keySpecs.setEntry(k, sqlSpec);
+ code.m_keyId[k] = keyColumn->getAttrId();
+ }
+ // matching expressions
+ const Plan_table::Index& index = m_table->m_indexList[0];
+ ctx_assert(index.m_keyFound);
+ const ExprVector& keyEq = index.m_keyEq;
+ ctx_assert(keyEq.size() == 1 + keyCount);
+ code.m_keyMatch = new Exec_expr* [1 + keyCount];
+ code.m_keyMatch[0] = 0;
+ for (unsigned k = 1; k <= keyCount; k++) {
+ Plan_expr* expr = keyEq[k];
+ Exec_expr* execExpr = static_cast<Exec_expr*>(expr->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execExpr != 0);
+ code.m_keyMatch[k] = execExpr;
+ }
+ // updated attributes
+ code.m_attrCount = attrCount;
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_update_lookup* exec = new Exec_update_lookup(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_update_lookup::print(Ctx& ctx)
+{
+ ctx.print(" [update_lookup");
+ Plan_base* a[] = { m_table, m_query };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ ctx.print("]");
+}
+
+// Exec_delete
+
+Exec_update_lookup::Code::~Code()
+{
+ delete[] m_tableName;
+ delete[] m_keyId;
+ delete[] m_keyMatch;
+ delete[] m_attrId;
+}
+
+Exec_update_lookup::Data::~Data()
+{
+}
+
+Exec_update_lookup::~Exec_update_lookup()
+{
+}
+
+void
+Exec_update_lookup::alloc(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // create data
+ Data& data = *new Data;
+ setData(data);
+ // allocate matching expressions
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* expr = code.m_keyMatch[k];
+ ctx_assert(expr != 0);
+ expr->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ }
+}
+
+void
+Exec_update_lookup::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+}
+
+void
+Exec_update_lookup::print(Ctx& ctx)
+{
+ ctx.print(" [update_lookup");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" keyId=");
+ for (unsigned i = 1; i <= code.m_keyCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_keyId[i]);
+ }
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ }
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.hpp
new file mode 100644
index 00000000000..fc4341880dd
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.hpp
@@ -0,0 +1,167 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_update_lookup_hpp
+#define ODBC_CODEGEN_Code_update_lookup_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_dml.hpp"
+#include "Code_table.hpp"
+#include "Code_query.hpp"
+
+/**
+ * @class Plan_update_lookup
+ * @brief Update in PlanTree
+ */
+class Plan_update_lookup : public Plan_dml {
+public:
+ Plan_update_lookup(Plan_root* root);
+ virtual ~Plan_update_lookup();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+ void setDmlRow(Plan_dml_row* dmlRow);
+ void setQuery(Plan_query* query);
+protected:
+ Plan_table* m_table;
+ Plan_dml_row* m_dmlRow;
+ Plan_query* m_query;
+};
+
+inline
+Plan_update_lookup::Plan_update_lookup(Plan_root* root) :
+ Plan_dml(root),
+ m_table(0),
+ m_dmlRow(0),
+ m_query(0)
+{
+}
+
+inline void
+Plan_update_lookup::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline void
+Plan_update_lookup::setDmlRow(Plan_dml_row* dmlRow)
+{
+ ctx_assert(dmlRow != 0);
+ m_dmlRow = dmlRow;
+}
+
+inline void
+Plan_update_lookup::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+/**
+ * @class Exec_update_lookup
+ * @brief Insert in ExecTree
+ */
+class Exec_update_lookup : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code(unsigned keyCount);
+ virtual ~Code();
+ protected:
+ friend class Plan_update_lookup;
+ friend class Exec_update_lookup;
+ char* m_tableName;
+ unsigned m_keyCount;
+ SqlSpecs m_keySpecs; // key types
+ NdbAttrId* m_keyId;
+ Exec_expr** m_keyMatch; // XXX pointers for now
+ unsigned m_attrCount;
+ NdbAttrId* m_attrId;
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_update_lookup;
+ };
+ Exec_update_lookup(Exec_root* root);
+ virtual ~Exec_update_lookup();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+protected:
+ Exec_query* m_query;
+};
+
+inline
+Exec_update_lookup::Code::Code(unsigned keyCount) :
+ m_tableName(0),
+ m_keyCount(keyCount),
+ m_keySpecs(keyCount),
+ m_keyId(0),
+ m_keyMatch(0),
+ m_attrCount(0),
+ m_attrId(0)
+{
+}
+
+inline
+Exec_update_lookup::Data::Data()
+{
+}
+
+inline
+Exec_update_lookup::Exec_update_lookup(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_update_lookup::Code&
+Exec_update_lookup::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_update_lookup::Data&
+Exec_update_lookup::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_update_lookup::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0 && m_query == 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.cpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.cpp
new file mode 100644
index 00000000000..9fac1728469
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.cpp
@@ -0,0 +1,146 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include <dictionary/DictColumn.hpp>
+#include "Code_dml_column.hpp"
+#include "Code_update_scan.hpp"
+#include "Code_table.hpp"
+#include "Code_root.hpp"
+
+// Plan_update_scan
+
+Plan_update_scan::~Plan_update_scan()
+{
+}
+
+Plan_base*
+Plan_update_scan::analyze(Ctx& ctx, Ctl& ctl)
+{
+ ctl.m_dmlRow = m_dmlRow; // row type to convert to
+ ctx_assert(m_query != 0);
+ m_query->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_table != 0);
+ m_table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ return this;
+}
+
+void
+Plan_update_scan::describe(Ctx& ctx)
+{
+ stmtArea().setFunction(ctx, "UPDATE WHERE", SQL_DIAG_UPDATE_WHERE);
+}
+
+Exec_base*
+Plan_update_scan::codegen(Ctx& ctx, Ctl& ctl)
+{
+ // generate code for the query
+ ctx_assert(m_query != 0);
+ Exec_query* execQuery = static_cast<Exec_query*>(m_query->codegen(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(execQuery != 0);
+ // set up
+ ctx_assert(m_table != 0);
+ const ColumnVector& columns = m_table->dmlColumns();
+ ctx_assert(columns.size() > 0);
+ const unsigned attrCount = columns.size() - 1;
+ // create the code
+ Exec_update_scan::Code& code = *new Exec_update_scan::Code();
+ // updated attributes
+ code.m_attrCount = attrCount;
+ code.m_attrId = new NdbAttrId[1 + attrCount];
+ code.m_attrId[0] = (NdbAttrId)-1;
+ for (unsigned i = 1; i <= attrCount; i++) {
+ Plan_column* column = columns[i];
+ ctx_assert(column != 0);
+ const DictColumn& dictColumn = column->dictColumn();
+ code.m_attrId[i] = dictColumn.getAttrId();
+ }
+ // create the exec
+ Exec_update_scan* exec = new Exec_update_scan(ctl.m_execRoot);
+ ctl.m_execRoot->saveNode(exec);
+ exec->setCode(code);
+ exec->setQuery(execQuery);
+ return exec;
+}
+
+void
+Plan_update_scan::print(Ctx& ctx)
+{
+ ctx.print(" [update_scan");
+ Plan_base* a[] = { m_table, m_query };
+ printList(ctx, a, sizeof(a)/sizeof(a[0]));
+ ctx.print("]");
+}
+
+// Exec_delete
+
+Exec_update_scan::Code::~Code()
+{
+ delete[] m_attrId;
+}
+
+Exec_update_scan::Data::~Data()
+{
+}
+
+Exec_update_scan::~Exec_update_scan()
+{
+}
+
+void
+Exec_update_scan::alloc(Ctx& ctx, Ctl& ctl)
+{
+ // allocate the subquery
+ ctx_assert(m_query != 0);
+ m_query->alloc(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // create data
+ Data& data = *new Data;
+ setData(data);
+}
+
+void
+Exec_update_scan::close(Ctx& ctx)
+{
+ ctx_assert(m_query != 0);
+ m_query->close(ctx);
+}
+
+void
+Exec_update_scan::print(Ctx& ctx)
+{
+ ctx.print(" [update_scan");
+ if (m_code != 0) {
+ const Code& code = getCode();
+ ctx.print(" attrId=");
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ if (i > 1)
+ ctx.print(",");
+ ctx.print("%u", (unsigned)code.m_attrId[i]);
+ }
+ }
+ Exec_base* a[] = { m_query };
+ printList(ctx, a, 1);
+ ctx.print("]");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.hpp b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.hpp
new file mode 100644
index 00000000000..d742883e561
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.hpp
@@ -0,0 +1,160 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_Code_update_scan_hpp
+#define ODBC_CODEGEN_Code_update_scan_hpp
+
+#include <common/common.hpp>
+#include "Code_base.hpp"
+#include "Code_dml.hpp"
+#include "Code_table.hpp"
+#include "Code_pred.hpp"
+#include "Code_query.hpp"
+
+/**
+ * @class Plan_update_scan
+ * @brief Update in PlanTree
+ */
+class Plan_update_scan : public Plan_dml {
+public:
+ Plan_update_scan(Plan_root* root);
+ virtual ~Plan_update_scan();
+ Plan_base* analyze(Ctx& ctx, Ctl& ctl);
+ void describe(Ctx& ctx);
+ Exec_base* codegen(Ctx& ctx, Ctl& ctl);
+ void print(Ctx& ctx);
+ // children
+ void setTable(Plan_table* table);
+ void setDmlRow(Plan_dml_row* dmlRow);
+ void setQuery(Plan_query* query);
+protected:
+ Plan_table* m_table;
+ Plan_dml_row* m_dmlRow;
+ Plan_query* m_query;
+};
+
+inline
+Plan_update_scan::Plan_update_scan(Plan_root* root) :
+ Plan_dml(root),
+ m_table(0),
+ m_dmlRow(0),
+ m_query(0)
+{
+}
+
+// children
+
+inline void
+Plan_update_scan::setTable(Plan_table* table)
+{
+ ctx_assert(table != 0);
+ m_table = table;
+}
+
+inline void
+Plan_update_scan::setDmlRow(Plan_dml_row* dmlRow)
+{
+ ctx_assert(dmlRow != 0);
+ m_dmlRow = dmlRow;
+}
+
+inline void
+Plan_update_scan::setQuery(Plan_query* query)
+{
+ ctx_assert(query != 0);
+ m_query = query;
+}
+
+/**
+ * @class Exec_update_scan
+ * @brief Insert in ExecTree
+ */
+class Exec_update_scan : public Exec_dml {
+public:
+ class Code : public Exec_dml::Code {
+ public:
+ Code();
+ virtual ~Code();
+ protected:
+ friend class Plan_update_scan;
+ friend class Exec_update_scan;
+ unsigned m_attrCount;
+ NdbAttrId* m_attrId;
+ };
+ class Data : public Exec_dml::Data {
+ public:
+ Data();
+ virtual ~Data();
+ protected:
+ friend class Exec_update_scan;
+ };
+ Exec_update_scan(Exec_root* root);
+ virtual ~Exec_update_scan();
+ void alloc(Ctx& ctx, Ctl& ctl);
+ void execImpl(Ctx& ctx, Ctl& ctl);
+ void close(Ctx& ctx);
+ void print(Ctx& ctx);
+ // children
+ const Code& getCode() const;
+ Data& getData() const;
+ void setQuery(Exec_query* query);
+protected:
+ Exec_query* m_query;
+};
+
+inline
+Exec_update_scan::Code::Code() :
+ m_attrCount(0),
+ m_attrId(0)
+{
+}
+
+inline
+Exec_update_scan::Data::Data()
+{
+}
+
+inline
+Exec_update_scan::Exec_update_scan(Exec_root* root) :
+ Exec_dml(root),
+ m_query(0)
+{
+}
+
+// children
+
+inline const Exec_update_scan::Code&
+Exec_update_scan::getCode() const
+{
+ const Code* code = static_cast<const Code*>(m_code);
+ return *code;
+}
+
+inline Exec_update_scan::Data&
+Exec_update_scan::getData() const
+{
+ Data* data = static_cast<Data*>(m_data);
+ return *data;
+}
+
+inline void
+Exec_update_scan::setQuery(Exec_query* query)
+{
+ ctx_assert(query != 0 && m_query == 0);
+ m_query = query;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/Makefile b/storage/ndb/src/old_files/client/odbc/codegen/Makefile
new file mode 100644
index 00000000000..49e5439556d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/Makefile
@@ -0,0 +1,104 @@
+include .defs.mk
+
+TYPE = *
+
+NONPIC_ARCHIVE = N
+
+PIC_ARCHIVE = Y
+
+ARCHIVE_TARGET = odbccodegen
+
+SOURCES = \
+ SimpleScan.lpp \
+ SimpleGram.ypp \
+ SimpleParser.cpp \
+ CodeGen.cpp \
+ Code_base.cpp \
+ Code_root.cpp \
+ Code_stmt.cpp \
+ Code_query.cpp \
+ Code_dml.cpp \
+ Code_ddl.cpp \
+ Code_select.cpp \
+ Code_pred.cpp \
+ Code_pred_op.cpp \
+ Code_comp_op.cpp \
+ Code_query_project.cpp \
+ Code_query_filter.cpp \
+ Code_query_join.cpp \
+ Code_query_lookup.cpp \
+ Code_query_index.cpp \
+ Code_query_scan.cpp \
+ Code_query_range.cpp \
+ Code_query_sys.cpp \
+ Code_query_repeat.cpp \
+ Code_query_count.cpp \
+ Code_query_sort.cpp \
+ Code_query_group.cpp \
+ Code_query_distinct.cpp \
+ Code_expr_row.cpp \
+ Code_expr.cpp \
+ Code_expr_op.cpp \
+ Code_expr_func.cpp \
+ Code_expr_conv.cpp \
+ Code_expr_column.cpp \
+ Code_expr_const.cpp \
+ Code_expr_param.cpp \
+ Code_update.cpp \
+ Code_update_lookup.cpp \
+ Code_update_index.cpp \
+ Code_update_scan.cpp \
+ Code_set_row.cpp \
+ Code_insert.cpp \
+ Code_dml_row.cpp \
+ Code_dml_column.cpp \
+ Code_delete.cpp \
+ Code_delete_lookup.cpp \
+ Code_delete_index.cpp \
+ Code_delete_scan.cpp \
+ Code_column.cpp \
+ Code_table_list.cpp \
+ Code_table.cpp \
+ Code_create_table.cpp \
+ Code_create_index.cpp \
+ Code_create_row.cpp \
+ Code_ddl_row.cpp \
+ Code_ddl_column.cpp \
+ Code_ddl_constr.cpp \
+ Code_idx_column.cpp \
+ Code_data_type.cpp \
+ Code_drop_table.cpp \
+ Code_drop_index.cpp
+
+ifeq ($(NDB_OS),WIN32)
+CCFLAGS += -I$(call fixpath,.)
+
+_libs:: FlexLexer.h unistd.h
+
+endif
+
+include ../Extra.mk
+include $(NDB_TOP)/Epilogue.mk
+
+ifeq ($(NDB_OS),LINUX)
+FLEXHACK = perl -i -pe 's/\bisatty\b/ouencunbwdb2y1bdc/g'
+BISONHACK = perl -i -pe 's/^\s*__attribute__\s*\(\(.*\)\)//'
+endif
+
+ifeq ($(NDB_OS),MACOSX)
+FLEXHACK = perl -i -pe 's/\bisatty\b/ouencunbwdb2y1bdc/g'
+BISONHACK = perl -i -pe 's/^\s*__attribute__\s*\(\(.*\)\)//'
+endif
+
+ifeq ($(NDB_OS),SOLARIS)
+BISONHACK = perl -i -pe 's/^\s*__attribute__\s*\(\(.*\)\)//'
+endif
+
+ifeq ($(NDB_OS),WIN32)
+unistd.h:
+ touch unistd.h
+
+FlexLexer.h:
+ cp /usr/include/FlexLexer.h .
+
+endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/SimpleGram.ypp b/storage/ndb/src/old_files/client/odbc/codegen/SimpleGram.ypp
new file mode 100644
index 00000000000..07d8017e5ed
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/SimpleGram.ypp
@@ -0,0 +1,1649 @@
+%{
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/DataRow.hpp>
+#include "CodeGen.hpp"
+#include <FlexLexer.h>
+#include "SimpleParser.hpp"
+
+/* redefine globals after headers */
+#define yyparse SimpleParser_yyparse
+#if YYDEBUG
+#define yydebug SimpleParser_yydebug
+#endif
+
+#define YYLEX_PARAM simpleParserPtr
+#define YYPARSE_PARAM simpleParserPtr
+#define simpleParser (*static_cast<SimpleParser*>(simpleParserPtr))
+
+static int yylex(YYSTYPE* lvalp, void* simpleParserPtr);
+
+#define yyerror(s) simpleParser.parseError(s)
+
+#if YYDEBUG
+// does not work in bison 1.75
+#undef stderr
+#define stderr 0
+#define YYFPRINTF simpleParser.ctx().print
+#endif
+
+// scanner states
+
+#define pushState(sc) simpleParser.pushState(sc)
+#define popState() simpleParser.popState()
+
+#define StateEval SimpleParser_stateEval
+#define StateType SimpleParser_stateType
+#define StatePhys SimpleParser_statePhys
+extern int SimpleParser_stateEval;
+extern int SimpleParser_stateType;
+extern int SimpleParser_statePhys;
+
+struct LimitPair { int off; int cnt; };
+
+struct PhysAttr { int storage; int logging; };
+
+%}
+
+%defines
+%pure-parser
+%verbose
+
+%union {
+ Plan_root* m_root;
+ Plan_stmt* m_stmt;
+ Plan_select* m_select;
+ Insert_op m_insert_op;
+ Plan_insert* m_insert;
+ Plan_update* m_update;
+ Plan_delete* m_delete;
+ Plan_create_table* m_create_table;
+ Plan_create_index* m_create_index;
+ NdbDictionary::Object::Type m_index_type;
+ Plan_create_row* m_create_row;
+ Plan_ddl_row* m_ddl_row;
+ Plan_ddl_column* m_ddl_column;
+ Plan_ddl_constr* m_ddl_constr;
+ Plan_idx_column* m_idx_column;
+ Plan_data_type* m_data_type;
+ Plan_drop_table* m_drop_table;
+ Plan_drop_index* m_drop_index;
+ Plan_set_row* m_set_row;
+ Plan_expr_row* m_expr_row;
+ bool m_asc_desc;
+ Plan_pred* m_pred;
+ Pred_op::Opcode m_pred_opcode;
+ Comp_op::Opcode m_comp_opcode;
+ Plan_expr* m_expr;
+ Expr_op::Opcode m_expr_opcode;
+ Plan_dml_row* m_dml_row;
+ Plan_dml_column* m_dml_column;
+ Plan_table* m_table;
+ Plan_table_list* m_table_list;
+ const char* m_string;
+ struct LimitPair* m_limit;
+ int m_signed_integer;
+ bool m_distinct;
+ struct PhysAttr* m_phys_attr;
+ NdbDictionary::Object::FragmentType m_storage_attr;
+ bool m_logging_attr;
+ SqlType::Type m_sql_type;
+}
+
+/* keywords */
+%token
+ T_AND
+ T_ASC
+ T_AUTO_INCREMENT
+ T_BIGINT
+ T_BINARY
+ T_BLOB
+ T_BY
+ T_CHAR
+ T_CLOB
+ T_CONSTRAINT
+ T_CREATE
+ T_DATETIME
+ T_DEFAULT
+ T_DELETE
+ T_DESC
+ T_DISTINCT
+ T_DOUBLE
+ T_DROP
+ T_FLOAT
+ T_FOREIGN
+ T_FROM
+ T_GROUP
+ T_HASH
+ T_HAVING
+ T_IN
+ T_INDEX
+ T_INSERT
+ T_INT
+ T_INTEGER
+ T_INTO
+ T_IS
+ T_KEY
+ T_LARGE
+ T_LIKE
+ T_LIMIT
+ T_LOGGING
+ T_LONGBLOB
+ T_LONGCLOB
+ T_MEDIUM
+ T_NOLOGGING
+ T_NOT
+ T_NULL
+ T_OFFSET
+ T_ON
+ T_OR
+ T_ORDER
+ T_PRECISION
+ T_PRIMARY
+ T_REAL
+ T_REFERENCES
+ T_ROWNUM
+ T_SELECT
+ T_SET
+ T_SINGLE
+ T_SMALL
+ T_SMALLINT
+ T_STORAGE
+ T_SYSDATE
+ T_TABLE
+ T_UNIQUE
+ T_UNSIGNED
+ T_UPDATE
+ T_VALUES
+ T_VARBINARY
+ T_VARCHAR
+ T_WHERE
+ T_WRITE
+
+/* identifiers and constants */
+%token
+ <m_string> T_IDENTIFIER
+ <m_string> T_LINTEGER
+ <m_string> T_LDECIMAL
+ <m_string> T_LREAL
+ <m_string> T_STRING
+
+/* expressions and predicates */
+%token
+ T_PLUS
+ T_MINUS
+ T_TIMES
+ T_DIVIDE
+ T_EQ
+ T_NOTEQ
+ T_LT
+ T_LTEQ
+ T_GT
+ T_GTEQ
+ T_QUES
+
+/* common special symbols */
+%token
+ T_PERIOD
+ T_COMMA
+ T_PARENLEFT
+ T_PARENRIGHT
+ T_ASTERISK
+ T_ASSIGN
+
+%type <m_root> root
+%type <m_stmt> stmt
+%type <m_select> stmt_select
+%type <m_insert> stmt_insert
+%type <m_insert_op> insert_op
+%type <m_update> stmt_update
+%type <m_delete> stmt_delete
+%type <m_create_table> create_table
+%type <m_create_index> create_index
+%type <m_index_type> index_type
+%type <m_create_row> create_row
+%type <m_ddl_column> create_column
+%type <m_ddl_constr> create_constr
+%type <m_idx_column> idx_column
+%type <m_data_type> data_type
+%type <m_ddl_row> ddl_row
+%type <m_ddl_column> ddl_column
+%type <m_drop_table> drop_table
+%type <m_drop_index> drop_index
+%type <m_asc_desc> asc_desc
+%type <m_set_row> set_row
+%type <m_expr_row> expr_row
+%type <m_expr_row> sort_row
+%type <m_pred> where_clause
+%type <m_expr_row> order_clause
+%type <m_pred> pred
+%type <m_pred> pred1
+%type <m_pred_opcode> pred1_op
+%type <m_pred> pred2
+%type <m_pred_opcode> pred2_op
+%type <m_pred> pred3
+%type <m_pred_opcode> pred3_op
+%type <m_pred> pred4
+%type <m_comp_opcode> comp_op
+%type <m_expr> expr
+%type <m_expr> expr1
+%type <m_expr_opcode> expr1_op
+%type <m_expr> expr2
+%type <m_expr_opcode> expr2_op
+%type <m_expr> expr3
+%type <m_expr_opcode> expr3_op
+%type <m_expr> expr4
+%type <m_expr> expr_column
+%type <m_dml_row> dml_row
+%type <m_dml_column> dml_column
+%type <m_expr_row> value_row
+%type <m_expr> value_expr
+%type <m_table> table
+%type <m_table_list> table_list
+%type <m_string> dot_identifier
+%type <m_limit> limit_clause
+%type <m_signed_integer> signed_integer
+%type <m_distinct> distinct_clause
+%type <m_expr_row> group_clause
+%type <m_pred> having_clause
+%type <m_phys_attr> phys_attr
+%type <m_phys_attr> phys_attr2
+%type <m_storage_attr> storage_attr
+%type <m_logging_attr> logging_attr
+%type <m_sql_type> blob_type
+
+%%
+
+root:
+ stmt
+ {
+ Plan_root* root = simpleParser.root();
+ root->setStmt($1);
+ }
+ ;
+stmt:
+ stmt_select
+ {
+ $$ = $1;
+ }
+ |
+ stmt_insert
+ {
+ $$ = $1;
+ }
+ |
+ stmt_update
+ {
+ $$ = $1;
+ }
+ |
+ stmt_delete
+ {
+ $$ = $1;
+ }
+ |
+ create_table
+ {
+ $$ = $1;
+ }
+ |
+ create_index
+ {
+ $$ = $1;
+ }
+ |
+ drop_table
+ {
+ $$ = $1;
+ }
+ |
+ drop_index
+ {
+ $$ = $1;
+ }
+ ;
+stmt_select:
+ T_SELECT distinct_clause expr_row T_FROM table_list where_clause group_clause having_clause order_clause limit_clause
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_select* stmt = new Plan_select(root);
+ root->saveNode(stmt);
+ stmt->setDistinct($2);
+ stmt->setRow($3);
+ stmt->setList($5);
+ if ($6 != 0)
+ stmt->setPred($6);
+ if ($7 != 0)
+ stmt->setGroup($7);
+ if ($8 != 0)
+ stmt->setHaving($8);
+ if ($9 != 0)
+ stmt->setSort($9);
+ if ($10 != 0) {
+ stmt->setLimit($10->off, $10->cnt);
+ delete $10;
+ }
+ $$ = stmt;
+ }
+ ;
+stmt_insert:
+ insert_op T_INTO table T_VALUES T_PARENLEFT value_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_insert* stmt = new Plan_insert(root, $1);
+ root->saveNode(stmt);
+ stmt->setTable($3);
+ stmt->setExprRow($6);
+ $$ = stmt;
+ }
+ |
+ insert_op T_INTO table T_PARENLEFT dml_row T_PARENRIGHT T_VALUES T_PARENLEFT value_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_insert* stmt = new Plan_insert(root, $1);
+ root->saveNode(stmt);
+ stmt->setTable($3);
+ stmt->setDmlRow($5);
+ stmt->setExprRow($9);
+ $$ = stmt;
+ }
+ |
+ insert_op T_INTO table stmt_select
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_insert* stmt = new Plan_insert(root, $1);
+ root->saveNode(stmt);
+ stmt->setTable($3);
+ stmt->setSelect($4);
+ $$ = stmt;
+ }
+ |
+ insert_op T_INTO table T_PARENLEFT dml_row T_PARENRIGHT stmt_select
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_insert* stmt = new Plan_insert(root, $1);
+ root->saveNode(stmt);
+ stmt->setTable($3);
+ stmt->setDmlRow($5);
+ stmt->setSelect($7);
+ $$ = stmt;
+ }
+ |
+ insert_op T_INTO table T_SET set_row
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_insert* stmt = new Plan_insert(root, $1);
+ root->saveNode(stmt);
+ stmt->setTable($3);
+ stmt->setMysqlRow($5);
+ $$ = stmt;
+ }
+ ;
+insert_op:
+ T_INSERT
+ {
+ $$ = Insert_op_insert;
+ }
+ |
+ T_WRITE
+ {
+ $$ = Insert_op_write;
+ }
+ ;
+stmt_update:
+ T_UPDATE table T_SET set_row where_clause
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_update* stmt = new Plan_update(root);
+ root->saveNode(stmt);
+ stmt->setTable($2);
+ stmt->setRow($4);
+ if ($5 != 0)
+ stmt->setPred($5);
+ $$ = stmt;
+ }
+ ;
+stmt_delete:
+ T_DELETE T_FROM table where_clause
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_delete* stmt = new Plan_delete(root);
+ root->saveNode(stmt);
+ stmt->setTable($3);
+ if ($4 != 0)
+ stmt->setPred($4);
+ $$ = stmt;
+ }
+ ;
+create_table:
+ T_CREATE T_TABLE dot_identifier T_PARENLEFT create_row T_PARENRIGHT phys_attr
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_create_table* stmt = new Plan_create_table(root, $3);
+ root->saveNode(stmt);
+ delete[] $3;
+ stmt->setCreateRow($5);
+ if ($7->storage != -1)
+ stmt->setFragmentType((NdbDictionary::Object::FragmentType)$7->storage);
+ if ($7->logging != -1)
+ stmt->setLogging($7->logging);
+ delete $7;
+ $$ = stmt;
+ }
+ ;
+create_row:
+ create_column
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_create_row* createRow = new Plan_create_row(root);
+ root->saveNode(createRow);
+ createRow->addColumn($1);
+ $$ = createRow;
+ }
+ |
+ create_constr
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_create_row* createRow = new Plan_create_row(root);
+ root->saveNode(createRow);
+ createRow->addConstr($1);
+ $$ = createRow;
+ }
+ |
+ create_row T_COMMA create_column
+ {
+ Plan_create_row* createRow = $1;
+ createRow->addColumn($3);
+ $$ = createRow;
+ }
+ |
+ create_row T_COMMA create_constr
+ {
+ Plan_create_row* createRow = $1;
+ createRow->addConstr($3);
+ $$ = createRow;
+ }
+ |
+ create_row T_COMMA create_ignore
+ {
+ $$ = $1;
+ }
+ ;
+create_column:
+ T_IDENTIFIER { pushState(StateType); } data_type { popState(); }
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_ddl_column* ddlColumn = new Plan_ddl_column(root, $1);
+ root->saveNode(ddlColumn);
+ delete[] $1;
+ ddlColumn->setType($3);
+ simpleParser.curr(ddlColumn);
+ }
+ create_column_rest
+ {
+ $$ = simpleParser.curr((Plan_ddl_column*)0);
+ }
+ ;
+data_type:
+ T_CHAR T_PARENLEFT T_LINTEGER T_PARENRIGHT dummy_binary
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(simpleParser.ctx(), SqlType::Char, atoi($3), true);
+ delete[] $3;
+ if (! simpleParser.ctx().ok()) {
+ YYABORT;
+ }
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_BINARY T_PARENLEFT T_LINTEGER T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(simpleParser.ctx(), SqlType::Binary, atoi($3), true);
+ delete[] $3;
+ if (! simpleParser.ctx().ok()) {
+ YYABORT;
+ }
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_VARCHAR T_PARENLEFT T_LINTEGER T_PARENRIGHT dummy_binary
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(simpleParser.ctx(), SqlType::Varchar, atoi($3), true);
+ delete[] $3;
+ if (! simpleParser.ctx().ok()) {
+ YYABORT;
+ }
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_VARBINARY T_PARENLEFT T_LINTEGER T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(simpleParser.ctx(), SqlType::Varbinary, atoi($3), true);
+ delete[] $3;
+ if (! simpleParser.ctx().ok()) {
+ YYABORT;
+ }
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_SMALLINT
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Smallint, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_INTEGER
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Integer, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_INT
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Integer, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_BIGINT
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Bigint, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_REAL
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Real, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_FLOAT
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Double, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_DOUBLE T_PRECISION
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Double, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ T_DATETIME
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType(SqlType::Datetime, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ |
+ blob_type
+ {
+ Plan_root* root = simpleParser.root();
+ SqlType sqlType($1, true);
+ Plan_data_type* dataType = new Plan_data_type(root, sqlType);
+ root->saveNode(dataType);
+ $$ = dataType;
+ }
+ ;
+dummy_binary:
+ /* empty */
+ |
+ T_BINARY
+ ;
+blob_type:
+ T_BLOB
+ {
+ $$ = SqlType::Blob;
+ }
+ |
+ T_LONGBLOB
+ {
+ $$ = SqlType::Blob;
+ }
+ |
+ T_CLOB
+ {
+ $$ = SqlType::Clob;
+ }
+ |
+ T_LONGCLOB
+ {
+ $$ = SqlType::Clob;
+ }
+ ;
+create_column_rest:
+ /* empty */
+ |
+ data_constr_list
+ ;
+data_constr_list:
+ data_constr
+ |
+ data_constr_list data_constr
+ ;
+data_constr:
+ T_NULL
+ |
+ T_NOT T_NULL
+ {
+ Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
+ ddlColumn->setNotNull();
+ }
+ |
+ T_UNSIGNED
+ {
+ Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
+ ddlColumn->setUnSigned();
+ }
+ |
+ T_PRIMARY T_KEY
+ {
+ Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
+ ddlColumn->setPrimaryKey();
+ }
+ |
+ T_AUTO_INCREMENT
+ {
+ Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
+ ddlColumn->setAutoIncrement();
+ }
+ |
+ T_DEFAULT expr
+ {
+ Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
+ ddlColumn->setDefaultValue($2);
+ }
+ ;
+create_constr:
+ T_PRIMARY T_KEY T_PARENLEFT ddl_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_ddl_constr* ddlConstr = new Plan_ddl_constr(root);
+ root->saveNode(ddlConstr);
+ ddlConstr->setRow($4);
+ $$ = ddlConstr;
+ }
+ |
+ T_CONSTRAINT dot_identifier T_PRIMARY T_KEY T_PARENLEFT ddl_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_ddl_constr* ddlConstr = new Plan_ddl_constr(root);
+ root->saveNode(ddlConstr);
+ ddlConstr->setRow($6);
+ $$ = ddlConstr;
+ }
+ ;
+create_ignore:
+ T_INDEX dot_identifier T_PARENLEFT ddl_row T_PARENRIGHT
+ |
+ T_FOREIGN T_KEY T_PARENLEFT ddl_row T_PARENRIGHT T_REFERENCES dot_identifier T_PARENLEFT ddl_row T_PARENRIGHT
+ ;
+ddl_row:
+ ddl_column
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_ddl_row* ddlRow = new Plan_ddl_row(root);
+ root->saveNode(ddlRow);
+ ddlRow->addColumn($1);
+ $$ = ddlRow;
+ }
+ |
+ ddl_row T_COMMA ddl_column
+ {
+ Plan_ddl_row* ddlRow = $1;
+ ddlRow->addColumn($3);
+ $$ = ddlRow;
+ }
+ ;
+ddl_column:
+ T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_ddl_column* column = new Plan_ddl_column(root, $1);
+ root->saveNode(column);
+ delete[] $1;
+ $$ = column;
+ }
+ ;
+create_index:
+ T_CREATE index_type T_INDEX dot_identifier T_ON table
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_create_index* stmt = new Plan_create_index(root, $4);
+ root->saveNode(stmt);
+ delete[] $4;
+ stmt->setType($2);
+ stmt->setTable($6);
+ simpleParser.curr(stmt);
+ }
+ T_PARENLEFT idx_row T_PARENRIGHT phys_attr
+ {
+ $$ = simpleParser.curr((Plan_create_index*)0);
+ if ($11->storage != -1)
+ $$->setFragmentType((NdbDictionary::Object::FragmentType)$11->storage);
+ if ($11->logging != -1)
+ $$->setLogging($11->logging);
+ delete $11;
+ }
+ ;
+index_type:
+ T_HASH
+ {
+ $$ = NdbDictionary::Object::HashIndex;
+ }
+ |
+ T_UNIQUE T_HASH
+ {
+ $$ = NdbDictionary::Object::UniqueHashIndex;
+ }
+ |
+ /* empty */
+ {
+ $$ = NdbDictionary::Object::OrderedIndex;
+ }
+ |
+ T_UNIQUE
+ {
+ $$ = NdbDictionary::Object::UniqueOrderedIndex;
+ }
+ ;
+idx_row:
+ idx_column
+ {
+ }
+ |
+ idx_row T_COMMA idx_column
+ {
+ }
+ ;
+idx_column:
+ T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_idx_column* column = new Plan_idx_column(root, $1);
+ root->saveNode(column);
+ delete[] $1;
+ Plan_create_index* stmt = simpleParser.curr((Plan_create_index*)0);
+ stmt->addColumn(column);
+ }
+ ;
+phys_attr:
+ { pushState(StatePhys); } phys_attr2 { popState(); }
+ {
+ $$ = $2;
+ }
+ ;
+phys_attr2:
+ /* empty */
+ {
+ $$ = new PhysAttr();
+ $$->storage = $$->logging = -1;
+ }
+ |
+ phys_attr2 storage_attr
+ {
+ if ($1->storage != -1 && $1->storage != $2) {
+ simpleParser.ctx().pushStatus(Sqlstate::_42000, Error::Gen, "conflicting STORAGE clauses");
+ YYABORT;
+ }
+ $$->storage = $2;
+ }
+ |
+ phys_attr2 logging_attr
+ {
+ if ($1->logging != -1 && $1->logging != $2) {
+ simpleParser.ctx().pushStatus(Sqlstate::_42000, Error::Gen, "conflicting LOGGING clauses");
+ YYABORT;
+ }
+ $$->logging = $2;
+ }
+ ;
+logging_attr:
+ T_LOGGING
+ {
+ $$ = true;
+ }
+ |
+ T_NOLOGGING
+ {
+ $$ = false;
+ }
+ ;
+storage_attr:
+ T_STORAGE T_PARENLEFT T_SINGLE T_PARENRIGHT
+ {
+ $$ = NdbDictionary::Object::FragSingle;
+ }
+ |
+ T_STORAGE T_PARENLEFT T_SMALL T_PARENRIGHT
+ {
+ $$ = NdbDictionary::Object::FragAllSmall;
+ }
+ |
+ T_STORAGE T_PARENLEFT T_MEDIUM T_PARENRIGHT
+ {
+ $$ = NdbDictionary::Object::FragAllMedium;
+ }
+ |
+ T_STORAGE T_PARENLEFT T_LARGE T_PARENRIGHT
+ {
+ $$ = NdbDictionary::Object::FragAllLarge;
+ }
+ ;
+drop_table:
+ T_DROP T_TABLE dot_identifier
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_drop_table* stmt = new Plan_drop_table(root, $3);
+ root->saveNode(stmt);
+ delete[] $3;
+ $$ = stmt;
+ }
+ ;
+drop_index:
+ T_DROP T_INDEX dot_identifier
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_drop_index* stmt = new Plan_drop_index(root, $3);
+ root->saveNode(stmt);
+ delete[] $3;
+ $$ = stmt;
+ }
+ |
+ T_DROP T_INDEX dot_identifier T_ON dot_identifier
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_drop_index* stmt = new Plan_drop_index(root, $3, $5);
+ root->saveNode(stmt);
+ delete[] $3;
+ delete[] $5;
+ $$ = stmt;
+ }
+ ;
+distinct_clause:
+ /* empty */
+ {
+ $$ = false;
+ }
+ |
+ T_DISTINCT
+ {
+ $$ = true;
+ }
+ ;
+where_clause:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ |
+ T_WHERE pred
+ {
+ $$ = $2;
+ }
+ ;
+group_clause:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ |
+ T_GROUP T_BY value_row
+ {
+ $$ = $3;
+ }
+ ;
+having_clause:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ |
+ T_HAVING pred
+ {
+ $$ = $2;
+ }
+ ;
+order_clause:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ |
+ T_ORDER T_BY sort_row
+ {
+ $$ = $3;
+ }
+ ;
+limit_clause:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ |
+ T_LIMIT signed_integer
+ {
+ LimitPair* p = new LimitPair;
+ p->off = 0;
+ p->cnt = $2;
+ $$ = p;
+ }
+ |
+ T_LIMIT signed_integer T_COMMA signed_integer
+ {
+ LimitPair* p = new LimitPair;
+ p->off = $2,
+ p->cnt = $4;
+ $$ = p;
+ }
+ |
+ T_LIMIT signed_integer T_OFFSET signed_integer
+ {
+ LimitPair* p = new LimitPair;
+ p->off = $4;
+ p->cnt = $2;
+ $$ = p;
+ }
+ ;
+signed_integer:
+ T_LINTEGER
+ {
+ $$ = atoi($1);
+ delete[] $1;
+ }
+ |
+ T_MINUS T_LINTEGER
+ {
+ $$ = (-1) * atoi($2);
+ delete[] $2;
+ }
+ ;
+set_row:
+ dml_column T_ASSIGN expr
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_set_row* row = new Plan_set_row(root);
+ root->saveNode(row);
+ row->addColumn($1);
+ row->addExpr($3);
+ $$ = row;
+ }
+ |
+ set_row T_COMMA dml_column T_ASSIGN expr
+ {
+ Plan_set_row* row = $1;
+ row->addColumn($3);
+ row->addExpr($5);
+ $$ = row;
+ }
+ ;
+dml_row:
+ dml_column
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_dml_row* row = new Plan_dml_row(root);
+ root->saveNode(row);
+ row->addColumn($1);
+ $$ = row;
+ }
+ |
+ dml_row T_COMMA dml_column
+ {
+ Plan_dml_row* row = $1;
+ row->addColumn($3);
+ $$ = row;
+ }
+ ;
+dml_column:
+ T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_dml_column* column = new Plan_dml_column(root, $1);
+ root->saveNode(column);
+ delete[] $1;
+ $$ = column;
+ }
+ ;
+value_row:
+ value_expr
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_row* row = new Plan_expr_row(root);
+ root->saveNode(row);
+ row->addExpr($1);
+ $$ = row;
+ }
+ |
+ value_row T_COMMA value_expr
+ {
+ Plan_expr_row* row = $1;
+ row->addExpr($3);
+ $$ = row;
+ }
+ ;
+value_expr:
+ expr
+ {
+ $$ = $1;
+ }
+ ;
+sort_row:
+ expr asc_desc
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_row* row = new Plan_expr_row(root);
+ root->saveNode(row);
+ row->addExpr($1, $2);
+ $$ = row;
+ }
+ |
+ sort_row T_COMMA expr asc_desc
+ {
+ Plan_expr_row* row = $1;
+ row->addExpr($3, $4);
+ $$ = row;
+ }
+ ;
+asc_desc:
+ /* empty */
+ {
+ $$ = true;
+ }
+ |
+ T_ASC
+ {
+ $$ = true;
+ }
+ |
+ T_DESC
+ {
+ $$ = false;
+ }
+ ;
+expr_row:
+ T_ASTERISK
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_row* row = new Plan_expr_row(root);
+ root->saveNode(row);
+ row->setAsterisk();
+ $$ = row;
+ }
+ |
+ T_TIMES /* XXX fix scanner state */
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_row* row = new Plan_expr_row(root);
+ root->saveNode(row);
+ row->setAsterisk();
+ $$ = row;
+ }
+ |
+ expr
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_row* row = new Plan_expr_row(root);
+ root->saveNode(row);
+ row->addExpr($1);
+ $$ = row;
+ }
+ |
+ expr T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_row* row = new Plan_expr_row(root);
+ root->saveNode(row);
+ row->addExpr($1, BaseString($2));
+ $$ = row;
+ }
+ |
+ expr_row T_COMMA expr
+ {
+ Plan_expr_row* row = $1;
+ row->addExpr($3);
+ $$ = row;
+ }
+ |
+ expr_row T_COMMA expr T_IDENTIFIER
+ {
+ Plan_expr_row* row = $1;
+ row->addExpr($3, BaseString($4));
+ $$ = row;
+ }
+ ;
+pred:
+ { pushState(StateEval); } pred1 { popState(); }
+ {
+ $$ = $2;
+ }
+ ;
+pred1:
+ pred2
+ {
+ $$ = $1;
+ }
+ |
+ pred1 pred1_op pred2
+ {
+ Plan_root* root = simpleParser.root();
+ Pred_op op($2);
+ Plan_pred_op* pred = new Plan_pred_op(root, op);
+ root->saveNode(pred);
+ pred->setPred(1, $1);
+ pred->setPred(2, $3);
+ $$ = pred;
+ }
+ ;
+pred1_op:
+ T_OR
+ {
+ $$ = Pred_op::Or;
+ }
+ ;
+pred2:
+ pred3
+ {
+ $$ = $1;
+ }
+ |
+ pred2 pred2_op pred3
+ {
+ Plan_root* root = simpleParser.root();
+ Pred_op op($2);
+ Plan_pred_op* pred = new Plan_pred_op(root, op);
+ root->saveNode(pred);
+ pred->setPred(1, $1);
+ pred->setPred(2, $3);
+ $$ = pred;
+ }
+ ;
+pred2_op:
+ T_AND
+ {
+ $$ = Pred_op::And;
+ }
+ ;
+pred3:
+ pred4
+ {
+ $$ = $1;
+ }
+ |
+ pred3_op pred3
+ {
+ Plan_root* root = simpleParser.root();
+ Pred_op op($1);
+ Plan_pred_op* pred = new Plan_pred_op(root, op);
+ root->saveNode(pred);
+ pred->setPred(1, $2);
+ $$ = pred;
+ }
+ ;
+pred3_op:
+ T_NOT
+ {
+ $$ = Pred_op::Not;
+ }
+ ;
+pred4:
+ T_PARENLEFT pred1 T_PARENRIGHT
+ {
+ $$ = $2;
+ }
+ |
+ expr1 comp_op expr1
+ {
+ Plan_root* root = simpleParser.root();
+ Comp_op op($2);
+ Plan_comp_op* comp = new Plan_comp_op(root, op);
+ root->saveNode(comp);
+ comp->setExpr(1, $1);
+ comp->setExpr(2, $3);
+ $$ = comp;
+ }
+ |
+ expr1 T_IS T_NULL
+ {
+ Plan_root* root = simpleParser.root();
+ Comp_op op(Comp_op::Isnull);
+ Plan_comp_op* comp = new Plan_comp_op(root, op);
+ root->saveNode(comp);
+ comp->setExpr(1, $1);
+ $$ = comp;
+ }
+ |
+ expr1 T_IS T_NOT T_NULL
+ {
+ Plan_root* root = simpleParser.root();
+ Comp_op op(Comp_op::Isnotnull);
+ Plan_comp_op* comp = new Plan_comp_op(root, op);
+ root->saveNode(comp);
+ comp->setExpr(1, $1);
+ $$ = comp;
+ }
+ |
+ expr1 T_IN T_PARENLEFT value_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_pred* predOut = 0; // hack directly into Or of Eq
+ Plan_expr* exprLeft = $1;
+ Plan_expr_row* row = $4;
+ for (unsigned i = row->getSize(); i >= 1; i--) {
+ Plan_expr* exprRight = row->getExpr(i);
+ Plan_comp_op* comp = new Plan_comp_op(root, Comp_op::Eq);
+ root->saveNode(comp);
+ comp->setExpr(1, exprLeft);
+ comp->setExpr(2, exprRight);
+ if (predOut == 0) {
+ predOut = comp;
+ } else {
+ Plan_pred_op* pred = new Plan_pred_op(root, Pred_op::Or);
+ root->saveNode(pred);
+ pred->setPred(1, predOut);
+ pred->setPred(2, comp);
+ predOut = pred;
+ }
+ }
+ $$ = predOut;
+ }
+ |
+ expr1 T_NOT T_IN T_PARENLEFT value_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_pred* predOut = 0; // hack directly into And of Noteq
+ Plan_expr* exprLeft = $1;
+ Plan_expr_row* row = $5;
+ for (unsigned i = row->getSize(); i >= 1; i--) {
+ Plan_expr* exprRight = row->getExpr(i);
+ Plan_comp_op* comp = new Plan_comp_op(root, Comp_op::Noteq);
+ root->saveNode(comp);
+ comp->setExpr(1, exprLeft);
+ comp->setExpr(2, exprRight);
+ if (predOut == 0) {
+ predOut = comp;
+ } else {
+ Plan_pred_op* pred = new Plan_pred_op(root, Pred_op::And);
+ root->saveNode(pred);
+ pred->setPred(1, predOut);
+ pred->setPred(2, comp);
+ predOut = pred;
+ }
+ }
+ $$ = predOut;
+ }
+ ;
+comp_op:
+ T_EQ
+ {
+ $$ = Comp_op::Eq;
+ }
+ |
+ T_NOTEQ
+ {
+ $$ = Comp_op::Noteq;
+ }
+ |
+ T_LT
+ {
+ $$ = Comp_op::Lt;
+ }
+ |
+ T_LTEQ
+ {
+ $$ = Comp_op::Lteq;
+ }
+ |
+ T_GT
+ {
+ $$ = Comp_op::Gt;
+ }
+ |
+ T_GTEQ
+ {
+ $$ = Comp_op::Gteq;
+ }
+ |
+ T_LIKE
+ {
+ $$ = Comp_op::Like;
+ }
+ |
+ T_NOT T_LIKE
+ {
+ $$ = Comp_op::Notlike;
+ }
+ ;
+expr:
+ { pushState(StateEval); } expr1 { popState(); }
+ {
+ $$ = $2;
+ }
+ ;
+expr1:
+ expr2
+ {
+ $$ = $1;
+ }
+ |
+ expr1 expr1_op expr2
+ {
+ Plan_root* root = simpleParser.root();
+ Expr_op op($2);
+ Plan_expr_op* expr = new Plan_expr_op(root, op);
+ root->saveNode(expr);
+ expr->setExpr(1, $1);
+ expr->setExpr(2, $3);
+ $$ = expr;
+ }
+ ;
+expr1_op:
+ T_PLUS
+ {
+ $$ = Expr_op::Add;
+ }
+ |
+ T_MINUS
+ {
+ $$ = Expr_op::Subtract;
+ }
+ ;
+expr2:
+ expr3
+ {
+ $$ = $1;
+ }
+ |
+ expr2 expr2_op expr3
+ {
+ Plan_root* root = simpleParser.root();
+ Expr_op op($2);
+ Plan_expr_op* expr = new Plan_expr_op(root, op);
+ root->saveNode(expr);
+ expr->setExpr(1, $1);
+ expr->setExpr(2, $3);
+ $$ = expr;
+ }
+ ;
+expr2_op:
+ T_TIMES
+ {
+ $$ = Expr_op::Multiply;
+ }
+ |
+ T_DIVIDE
+ {
+ $$ = Expr_op::Divide;
+ }
+ ;
+expr3:
+ expr4
+ {
+ $$ = $1;
+ }
+ |
+ expr3_op expr3
+ {
+ Plan_root* root = simpleParser.root();
+ Expr_op op($1);
+ Plan_expr_op* expr = new Plan_expr_op(root, op);
+ root->saveNode(expr);
+ expr->setExpr(1, $2);
+ $$ = expr;
+ }
+ ;
+expr3_op:
+ T_PLUS
+ {
+ $$ = Expr_op::Plus;
+ }
+ |
+ T_MINUS
+ {
+ $$ = Expr_op::Minus;
+ }
+ ;
+expr4:
+ T_PARENLEFT expr1 T_PARENRIGHT
+ {
+ $$ = $2;
+ }
+ |
+ T_IDENTIFIER T_PARENLEFT expr_row T_PARENRIGHT
+ {
+ Plan_root* root = simpleParser.root();
+ const Expr_func& spec = Expr_func::find($1);
+ if (spec.m_name == 0) {
+ simpleParser.ctx().pushStatus(Sqlstate::_42000, Error::Gen, "unknown function %s", $1);
+ delete[] $1;
+ YYABORT;
+ }
+ Plan_expr_func* func = new Plan_expr_func(root, spec);
+ root->saveNode(func);
+ delete[] $1;
+ func->setArgs($3);
+ $$ = func;
+ }
+ |
+ T_ROWNUM
+ {
+ Plan_root* root = simpleParser.root();
+ const Expr_func& spec = Expr_func::find("ROWNUM");
+ ctx_assert(spec.m_name != 0);
+ Plan_expr_func* func = new Plan_expr_func(root, spec);
+ root->saveNode(func);
+ func->setArgs(0);
+ $$ = func;
+ }
+ |
+ T_SYSDATE
+ {
+ Plan_root* root = simpleParser.root();
+ const Expr_func& spec = Expr_func::find("SYSDATE");
+ ctx_assert(spec.m_name != 0);
+ Plan_expr_func* func = new Plan_expr_func(root, spec);
+ root->saveNode(func);
+ func->setArgs(0);
+ $$ = func;
+ }
+ |
+ expr_column
+ {
+ $$ = $1;
+ }
+ |
+ T_STRING
+ {
+ Plan_root* root = simpleParser.root();
+ LexType lexType(LexType::Char);
+ Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
+ root->saveNode(expr);
+ delete[] $1;
+ $$ = expr;
+ }
+ |
+ T_LINTEGER
+ {
+ Plan_root* root = simpleParser.root();
+ LexType lexType(LexType::Integer);
+ Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
+ root->saveNode(expr);
+ delete[] $1;
+ $$ = expr;
+ }
+ |
+ T_LDECIMAL
+ {
+ Plan_root* root = simpleParser.root();
+ LexType lexType(LexType::Float);
+ Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
+ root->saveNode(expr);
+ delete[] $1;
+ $$ = expr;
+ }
+ |
+ T_LREAL
+ {
+ Plan_root* root = simpleParser.root();
+ LexType lexType(LexType::Float);
+ Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
+ root->saveNode(expr);
+ delete[] $1;
+ $$ = expr;
+ }
+ |
+ T_NULL
+ {
+ Plan_root* root = simpleParser.root();
+ LexType lexType(LexType::Null);
+ Plan_expr_const* expr = new Plan_expr_const(root, lexType, "");
+ root->saveNode(expr);
+ $$ = expr;
+ }
+ |
+ T_QUES
+ {
+ Plan_root* root = simpleParser.root();
+ unsigned paramNumber = simpleParser.paramNumber();
+ ctx_assert(paramNumber != 0);
+ Plan_expr_param* expr = new Plan_expr_param(root, paramNumber);
+ root->saveNode(expr);
+ $$ = expr;
+ }
+ ;
+expr_column:
+ T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_column* column = new Plan_expr_column(root, $1);
+ root->saveNode(column);
+ delete[] $1;
+ $$ = column;
+ }
+ |
+ T_IDENTIFIER T_PERIOD T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_expr_column* column = new Plan_expr_column(root, $3);
+ root->saveNode(column);
+ delete[] $3;
+ column->setCname($1);
+ $$ = column;
+ }
+ |
+ T_IDENTIFIER T_PERIOD T_IDENTIFIER T_PERIOD T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ BaseString str;
+ str.append($1);
+ str.append(".");
+ str.append($3);
+ delete[] $1;
+ delete[] $3;
+ Plan_expr_column* column = new Plan_expr_column(root, $5);
+ root->saveNode(column);
+ delete[] $5;
+ column->setCname(str);
+ $$ = column;
+ }
+ ;
+table_list:
+ table
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_table_list* tableList = new Plan_table_list(root);
+ root->saveNode(tableList);
+ tableList->addTable($1);
+ $$ = tableList;
+ }
+ |
+ table_list T_COMMA table
+ {
+ Plan_table_list* tableList = $1;
+ tableList->addTable($3);
+ $$ = tableList;
+ }
+ ;
+table:
+ dot_identifier
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_table* table = new Plan_table(root, $1);
+ root->saveNode(table);
+ delete[] $1;
+ $$ = table;
+ }
+ |
+ dot_identifier T_IDENTIFIER
+ {
+ Plan_root* root = simpleParser.root();
+ Plan_table* table = new Plan_table(root, $1);
+ root->saveNode(table);
+ delete[] $1;
+ table->setCname($2);
+ delete[] $2;
+ $$ = table;
+ }
+ ;
+dot_identifier:
+ T_IDENTIFIER
+ {
+ $$ = $1;
+ }
+ |
+ T_IDENTIFIER T_PERIOD T_IDENTIFIER
+ {
+ char* s = new char[strlen($1) + 1 + strlen($3) + 1];
+ strcpy(s, $1);
+ strcat(s, ".");
+ strcat(s, $3);
+ delete[] $1;
+ delete[] $3;
+ $$ = s;
+ }
+ ;
+
+%%
+
+static int
+yylex(YYSTYPE* lvalp, void* simpleParserPtr)
+{
+ int ret = simpleParser.yylex();
+ *lvalp = simpleParser.yylval();
+ return ret;
+}
+
+/* vim: set filetype=yacc: */
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.cpp b/storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.cpp
new file mode 100644
index 00000000000..a2418f49e37
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.cpp
@@ -0,0 +1,96 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/common.hpp>
+#include <NdbMutex.h>
+#include <common/StmtArea.hpp>
+#include <FlexLexer.h>
+#include "SimpleParser.hpp"
+
+SimpleParser::~SimpleParser()
+{
+}
+
+#ifdef NDB_WIN32
+static NdbMutex & parse_mutex = * NdbMutex_Create();
+#else
+static NdbMutex parse_mutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+void
+SimpleParser::yyparse()
+{
+ Ctx& ctx = this->ctx();
+ NdbMutex_Lock(&parse_mutex);
+ ctx_log2(("parse: %s", stmtArea().sqlText().c_str()));
+#if YYDEBUG
+ SimpleParser_yydebug = (m_ctx.logLevel() >= 5);
+#endif
+ SimpleParser_yyparse((void*)this);
+ NdbMutex_Unlock(&parse_mutex);
+}
+
+void
+SimpleParser::pushState(int sc)
+{
+ yy_push_state(sc);
+ m_stacksize++;
+}
+
+void
+SimpleParser::popState()
+{
+ ctx_assert(m_stacksize > 0);
+ yy_pop_state();
+ m_stacksize--;
+}
+
+void
+SimpleParser::parseError(const char* msg)
+{
+ ctx().pushStatus(Sqlstate::_42000, Error::Gen, "%s at '%*s' position %u", msg, yyleng, yytext, m_parsePos - yyleng);
+}
+
+int
+SimpleParser::LexerInput(char* buf, int max_size)
+{
+ const BaseString& text = stmtArea().sqlText();
+ int n = 0;
+ const char* const t = text.c_str();
+ const unsigned m = text.length();
+ while (n < max_size && m_textPos < m) {
+ buf[n++] = t[m_textPos++];
+ m_parsePos++; // XXX simple hack
+ break;
+ }
+ return n;
+}
+
+// XXX just a catch-all (scanner should match all input)
+void
+SimpleParser::LexerOutput(const char* buf, int size)
+{
+ if (! ctx().ok())
+ return;
+ const char* msg = "unrecognized input";
+ ctx().pushStatus(Sqlstate::_42000, Error::Gen, "%s at '%*s' position %u", msg, size, buf, m_parsePos);
+}
+
+void
+SimpleParser::LexerError(const char* msg)
+{
+ ctx().pushStatus(Sqlstate::_42000, Error::Gen, "%s at '%*s' position %u", msg, yyleng, yytext, m_parsePos);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.hpp b/storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.hpp
new file mode 100644
index 00000000000..abadae8f905
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.hpp
@@ -0,0 +1,161 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_SimpleParser_hpp
+#define ODBC_CODEGEN_SimpleParser_hpp
+
+#include <common/common.hpp>
+#include "Code_root.hpp"
+#include "Code_stmt.hpp"
+#include "Code_select.hpp"
+#include "Code_pred.hpp"
+#include "Code_pred_op.hpp"
+#include "Code_comp_op.hpp"
+#include "Code_expr_row.hpp"
+#include "Code_expr.hpp"
+#include "Code_expr_op.hpp"
+#include "Code_expr_func.hpp"
+#include "Code_expr_column.hpp"
+#include "Code_expr_const.hpp"
+#include "Code_expr_param.hpp"
+#include "Code_update.hpp"
+#include "Code_set_row.hpp"
+#include "Code_insert.hpp"
+#include "Code_dml_row.hpp"
+#include "Code_dml_column.hpp"
+#include "Code_delete.hpp"
+#include "Code_table_list.hpp"
+#include "Code_table.hpp"
+#include "Code_create_table.hpp"
+#include "Code_create_index.hpp"
+#include "Code_ddl_row.hpp"
+#include "Code_ddl_column.hpp"
+#include "Code_ddl_constr.hpp"
+#include "Code_data_type.hpp"
+#include "Code_drop_table.hpp"
+#include "Code_drop_index.hpp"
+
+#include "SimpleGram.tab.hpp"
+
+class StmtArea;
+class Plan_root;
+
+class SimpleParser : public yyFlexLexer {
+public:
+ SimpleParser(Ctx& ctx, StmtArea& stmtArea, Plan_root* root);
+ ~SimpleParser();
+ Ctx& ctx();
+ StmtArea& stmtArea();
+ Plan_root* root();
+ void yyparse(); // calls real yyparse
+ int yylex(); // generated by flex
+ YYSTYPE yylval();
+ void pushState(int sc); // push start condition
+ void popState(); // pop start condition
+ unsigned paramNumber() const;
+ void parseError(const char* msg);
+ // parser helpers - to avoid creating new Plan tree types
+ Plan_ddl_column* curr(Plan_ddl_column* p);
+ Plan_create_index* curr(Plan_create_index* p);
+protected:
+ virtual int LexerInput(char* buf, int max_size);
+ virtual void LexerOutput(const char* buf, int size);
+ virtual void LexerError(const char* msg);
+private:
+ Ctx& m_ctx;
+ StmtArea& m_stmtArea;
+ Plan_root* const m_root;
+ unsigned m_textPos; // position in sql text
+ unsigned m_parsePos; // parse position, to report error
+ YYSTYPE m_yylval; // token value
+ BaseString m_string; // storage for edited string token
+ unsigned m_stacksize; // state stack size
+ unsigned m_paramNumber; // parameter number
+ // parser helpers
+ Plan_ddl_column* m_ddl_column;
+ Plan_create_index* m_create_index;
+};
+
+extern int SimpleParser_yyparse(void* simpleParserPtr);
+#if YYDEBUG
+extern int SimpleParser_yydebug;
+#endif
+
+inline
+SimpleParser::SimpleParser(Ctx& ctx, StmtArea& stmtArea, Plan_root* root) :
+ m_ctx(ctx),
+ m_stmtArea(stmtArea),
+ m_root(root),
+ m_textPos(0),
+ m_parsePos(0),
+ m_stacksize(0),
+ m_paramNumber(0),
+ // parser helpers
+ m_ddl_column(0)
+{
+}
+
+inline Ctx&
+SimpleParser::ctx()
+{
+ return m_ctx;
+}
+
+inline StmtArea&
+SimpleParser::stmtArea()
+{
+ return m_stmtArea;
+}
+
+inline Plan_root*
+SimpleParser::root()
+{
+ return m_root;
+}
+
+inline YYSTYPE
+SimpleParser::yylval()
+{
+ return m_yylval;
+}
+
+inline unsigned
+SimpleParser::paramNumber() const
+{
+ return m_paramNumber;
+}
+
+// parser helpers
+
+inline Plan_ddl_column*
+SimpleParser::curr(Plan_ddl_column* p)
+{
+ if (p != 0)
+ m_ddl_column = p;
+ ctx_assert(m_ddl_column != 0);
+ return m_ddl_column;
+}
+
+inline Plan_create_index*
+SimpleParser::curr(Plan_create_index* p)
+{
+ if (p != 0)
+ m_create_index = p;
+ ctx_assert(m_create_index != 0);
+ return m_create_index;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/codegen/SimpleScan.lpp b/storage/ndb/src/old_files/client/odbc/codegen/SimpleScan.lpp
new file mode 100644
index 00000000000..29aa876f669
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/codegen/SimpleScan.lpp
@@ -0,0 +1,243 @@
+%{
+#include <ctype.h>
+#include "SimpleParser.hpp"
+
+struct SqlKeyword {
+ const char* m_name;
+ int m_value;
+ int m_state;
+ static const SqlKeyword* find(Ctx& ctx, const char* name, int state);
+};
+
+%}
+
+%option c++
+%option yyclass="SimpleParser"
+%option stack
+%option noyywrap
+
+space [\040\t\n\r\f]
+digit [0-9]
+letter [A-Za-z_]
+special ("$")
+identifier ({letter}({letter}|{digit}|{special})*)
+integer {digit}++
+decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
+real ((({digit}*\.{digit}+)|({digit}+\.{digit}*)|({digit}+))([Ee][-+]?{digit}+))
+
+%s StateEval
+%s StateType
+%s StatePhys
+%x StateString
+%x StateQuoted
+
+%%
+
+{space} {
+ }
+{identifier} {
+ const SqlKeyword* key = SqlKeyword::find(m_ctx, (char*)yytext, YYSTATE);
+ if (key != 0)
+ return key->m_value;
+ for (unsigned char* a = (unsigned char*)yytext; *a != 0; a++) {
+ if (islower(*a))
+ *a = toupper(*a);
+ }
+ m_yylval.m_string = strcpy(new char[yyleng + 1], yytext);
+ return T_IDENTIFIER;
+ }
+{integer} {
+ m_yylval.m_string = strcpy(new char[yyleng + 1], yytext);
+ return T_LINTEGER;
+ }
+{decimal} {
+ m_yylval.m_string = strcpy(new char[yyleng + 1], yytext);
+ return T_LDECIMAL;
+ }
+{real} {
+ m_yylval.m_string = strcpy(new char[yyleng + 1], yytext);
+ return T_LREAL;
+ }
+"--".* {
+ }
+"/*" {
+ int c = 0, d;
+ while (1) {
+ d = c;
+ if ((c = yyinput()) == EOF) {
+ parseError("unterminated comment");
+ yyterminate();
+ }
+ if (d == '*' && c == '/')
+ break;
+ }
+ }
+<StateEval>{
+"+" return T_PLUS;
+"-" return T_MINUS;
+"*" return T_TIMES;
+"/" return T_DIVIDE;
+"=" return T_EQ;
+"!=" return T_NOTEQ;
+"^=" return T_NOTEQ;
+"<>" return T_NOTEQ;
+"<" return T_LT;
+"<=" return T_LTEQ;
+">" return T_GT;
+">=" return T_GTEQ;
+"?" m_paramNumber++; return T_QUES;
+}
+
+"." return T_PERIOD;
+"," return T_COMMA;
+"(" return T_PARENLEFT;
+")" return T_PARENRIGHT;
+"*" return T_ASTERISK;
+"=" return T_ASSIGN;
+
+"'" {
+ pushState(StateString);
+ m_string.assign("");
+ }
+<StateString>{
+[^']* {
+ m_string.append(yytext);
+ }
+"''" {
+ m_string.append("'");
+ }
+"'" {
+ m_yylval.m_string = strcpy(new char[m_string.length() + 1], m_string.c_str());
+ popState();
+ return T_STRING;
+ }
+}
+
+\" {
+ pushState(StateQuoted);
+ m_string.assign("");
+ }
+<StateQuoted>{
+[^"]* {
+ m_string.append(yytext);
+ }
+\\\" {
+ m_string.append("\"");
+ }
+\" {
+ m_yylval.m_string = strcpy(new char[m_string.length() + 1], m_string.c_str());
+ popState();
+ return T_IDENTIFIER;
+ }
+}
+
+%%
+
+// scan states
+int SimpleParser_stateEval = StateEval;
+int SimpleParser_stateType = StateType;
+int SimpleParser_statePhys = StatePhys;
+
+// keep sorted
+
+static const SqlKeyword sqlKeyword[] = {
+ { "AND", T_AND, -1 },
+ { "ASC", T_ASC, -1 },
+ { "AUTO_INCREMENT", T_AUTO_INCREMENT, -1 },
+ { "BIGINT", T_BIGINT, StateType },
+ { "BINARY", T_BINARY, StateType },
+ { "BLOB", T_BLOB, StateType },
+ { "BY", T_BY, -1 },
+ { "CHAR", T_CHAR, StateType },
+ { "CLOB", T_CLOB, StateType },
+ { "CONSTRAINT", T_CONSTRAINT, -1 },
+ { "CREATE", T_CREATE, -1 },
+ { "DATETIME", T_DATETIME, StateType },
+ { "DEFAULT", T_DEFAULT, -1 },
+ { "DELETE", T_DELETE, -1 },
+ { "DESC", T_DESC, -1 },
+ { "DISTINCT", T_DISTINCT, -1 },
+ { "DOUBLE", T_DOUBLE, StateType },
+ { "DROP", T_DROP, -1 },
+ { "FLOAT", T_FLOAT, StateType },
+ { "FOREIGN", T_FOREIGN, -1 },
+ { "FROM", T_FROM, -1 },
+ { "GROUP", T_GROUP, -1 },
+ { "HASH", T_HASH, -1 },
+ { "HAVING", T_HAVING, -1 },
+ { "IN", T_IN, -1 },
+ { "INDEX", T_INDEX, -1 },
+ { "INSERT", T_INSERT, -1 },
+ { "INT", T_INT, StateType },
+ { "INTEGER", T_INTEGER, StateType },
+ { "INTO", T_INTO, -1 },
+ { "IS", T_IS, -1 },
+ { "KEY", T_KEY, -1 },
+ { "LARGE", T_LARGE, StatePhys },
+ { "LIKE", T_LIKE, -1 },
+ { "LIMIT", T_LIMIT, -1 },
+ { "LOGGING", T_LOGGING, StatePhys },
+ { "LONGBLOB", T_LONGBLOB, StateType },
+ { "LONGCLOB", T_LONGCLOB, StateType },
+ { "MEDIUM", T_MEDIUM, StatePhys },
+ { "NOLOGGING", T_NOLOGGING, StatePhys },
+ { "NOT", T_NOT, -1 },
+ { "NULL", T_NULL, -1 },
+ { "OFFSET", T_OFFSET, -1 },
+ { "ON", T_ON, -1 },
+ { "OR", T_OR, -1 },
+ { "ORDER", T_ORDER, -1 },
+ { "PRECISION", T_PRECISION, StateType },
+ { "PRIMARY", T_PRIMARY, -1 },
+ { "REAL", T_REAL, StateType },
+ { "REFERENCES", T_REFERENCES, -1 },
+ { "ROWNUM", T_ROWNUM, -1 },
+ { "SELECT", T_SELECT, -1 },
+ { "SET", T_SET, -1 },
+ { "SINGLE", T_SINGLE, StatePhys },
+ { "SMALL", T_SMALL, StatePhys },
+ { "SMALLINT", T_SMALLINT, StateType },
+ { "STORAGE", T_STORAGE, StatePhys },
+ { "SYSDATE", T_SYSDATE, -1 },
+ { "TABLE", T_TABLE, -1 },
+ { "UNIQUE", T_UNIQUE, -1 },
+ { "UNSIGNED", T_UNSIGNED, -1 },
+ { "UPDATE", T_UPDATE, -1 },
+ { "VALUES", T_VALUES, -1 },
+ { "VARBINARY", T_VARBINARY, StateType },
+ { "VARCHAR", T_VARCHAR, StateType },
+ { "WHERE", T_WHERE, -1 },
+ { "WRITE", T_WRITE, -1 }
+};
+
+static const unsigned sqlKeywordCount = sizeof(sqlKeyword) / sizeof(sqlKeyword[0]);
+
+const SqlKeyword*
+SqlKeyword::find(Ctx& ctx, const char* name, int state)
+{
+ ctx_log4(("find keyword '%s' lex state = %d", name, state));
+ const unsigned maxlen = 99;
+ char buf[maxlen + 1];
+ char* a = buf;
+ const char* b = name;
+ while (*b != 0) {
+ if (a >= buf + maxlen) // will not be found
+ break;
+ char c = *b++;
+ if ('a' <= c && c <= 'z') // locale independent
+ c -= 'a' - 'A';
+ *a++ = c;
+ }
+ *a = 0;
+ for (unsigned i = 0; i < sqlKeywordCount; i++) {
+ const SqlKeyword* key = &sqlKeyword[i];
+ if (strcmp(key->m_name, buf) == 0) {
+ if (key->m_state != -1 && key->m_state != state)
+ return 0;
+ return key;
+ }
+ }
+ return 0;
+}
+
+/* vim: set filetype=lex: */
diff --git a/storage/ndb/src/old_files/client/odbc/common/AttrArea.cpp b/storage/ndb/src/old_files/client/odbc/common/AttrArea.cpp
new file mode 100644
index 00000000000..ff9e085a7f6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/AttrArea.cpp
@@ -0,0 +1,91 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "AttrArea.hpp"
+
+// AttrSpec
+
+// AttrField
+
+// AttrArea
+
+AttrArea::AttrArea(const AttrSpec* specList) :
+ m_specList(specList)
+{
+}
+
+AttrArea::~AttrArea()
+{
+}
+
+const AttrSpec&
+AttrArea::findSpec(int id) const
+{
+ const AttrSpec* p;
+ for (p = m_specList; p->m_mode != Attr_mode_undef; p++) {
+ if (p->m_id == id)
+ break;
+ }
+ return *p;
+}
+
+void
+AttrArea::setAttr(Ctx& ctx, int id, const OdbcData& data)
+{
+ const AttrSpec& spec = findSpec(id);
+ if (spec.m_mode == Attr_mode_undef) {
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "undefined attribute id %d", id);
+ return;
+ }
+ ctx_assert(spec.m_type == data.type());
+ ctx_assert(spec.m_set != 0);
+ spec.m_set(ctx, m_handle, data);
+ if (! ctx.ok())
+ return;
+ Fields::iterator iter;
+ if (ctx.logLevel() >= 3) {
+ char buf[100];
+ data.print(buf, sizeof(buf));
+ ctx_log3(("attr handle 0x%x set id %d = %s", (unsigned)m_handle, id, buf));
+ }
+ iter = m_fields.find(id);
+ if (iter != m_fields.end()) {
+ AttrField& field = (*iter).second;
+ field.setData(data);
+ return;
+ }
+ AttrField field(spec, data);
+ m_fields.insert(Fields::value_type(id, field));
+}
+
+void
+AttrArea::getAttr(Ctx& ctx, int id, OdbcData& data)
+{
+ const AttrSpec& spec = findSpec(id);
+ if (spec.m_mode == Attr_mode_undef) {
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "undefined attribute id %d", id);
+ return;
+ }
+ Fields::iterator iter;
+ iter = m_fields.find(id);
+ if (iter != m_fields.end()) {
+ AttrField& field = (*iter).second;
+ data.setValue(field.getData());
+ return;
+ }
+ ctx_assert(spec.m_default != 0);
+ spec.m_default(ctx, m_handle, data);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/AttrArea.hpp b/storage/ndb/src/old_files/client/odbc/common/AttrArea.hpp
new file mode 100644
index 00000000000..050cce719bf
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/AttrArea.hpp
@@ -0,0 +1,135 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_AttrArea_hpp
+#define ODBC_HANDLES_AttrArea_hpp
+
+#include <map>
+#include <common/common.hpp>
+#include "OdbcData.hpp"
+
+class HandleBase;
+
+enum AttrMode {
+ Attr_mode_undef,
+ Attr_mode_readonly,
+ Attr_mode_writeonly,
+ Attr_mode_readwrite
+};
+
+/**
+ * @struct AttrSpec
+ * @brief Attribute specifier
+ *
+ * Each handle class has a list of attribute specifiers.
+ */
+struct AttrSpec {
+ int m_id; // SQL_ATTR_ identifier
+ OdbcData::Type m_type; // data type
+ AttrMode m_mode; // access mode, undef indicates end of list
+ /**
+ * Callback for checks and side effects. Called before the
+ * attribute is stored. May set error status.
+ */
+ typedef void CallbackSet(Ctx& ctx, HandleBase* self, const OdbcData& data);
+ CallbackSet* m_set;
+ /**
+ * Callback to set default value. May set error status.
+ */
+ typedef void CallbackDefault(Ctx& ctx, HandleBase* self, OdbcData& data);
+ CallbackDefault* m_default;
+};
+
+/**
+ * @class AttrField
+ * @brief Attribute value (stored as OdbcData)
+ */
+class AttrField {
+public:
+ AttrField(const AttrSpec& attrSpec, const OdbcData& data);
+ AttrField(const AttrField& field);
+ ~AttrField();
+ void setData(const OdbcData& data);
+ const OdbcData& getData();
+private:
+ const AttrSpec& m_spec;
+ OdbcData m_data;
+};
+
+inline
+AttrField::AttrField(const AttrSpec& spec, const OdbcData& data) :
+ m_spec(spec),
+ m_data(data)
+{
+}
+
+inline
+AttrField::AttrField(const AttrField& field) :
+ m_spec(field.m_spec),
+ m_data(field.m_data)
+{
+}
+
+inline
+AttrField::~AttrField()
+{
+}
+
+inline void
+AttrField::setData(const OdbcData& data)
+{
+ ctx_assert(m_spec.m_type == data.type());
+ m_data.setValue(data);
+}
+
+inline const OdbcData&
+AttrField::getData()
+{
+ ctx_assert(m_data.type() != OdbcData::Undef);
+ return m_data;
+}
+
+/**
+ * @class AttrArea
+ * @brief Handle attributes
+ *
+ * Each handle instance has a list of attribute values stored
+ * under an AttrArea. Callbacks to handle code provide for
+ * default values, extra checks, and side-effects.
+ */
+class AttrArea {
+public:
+ AttrArea(const AttrSpec* specList);
+ ~AttrArea();
+ void setHandle(HandleBase* handle);
+ const AttrSpec& findSpec(int id) const;
+ void setAttr(Ctx& ctx, int id, const OdbcData& data);
+ void getAttr(Ctx& ctx, int id, OdbcData& data);
+private:
+ HandleBase* m_handle;
+ const AttrSpec* const m_specList;
+ typedef std::map<int, AttrField> Fields;
+ Fields m_fields;
+};
+
+inline void
+AttrArea::setHandle(HandleBase* handle)
+{
+ ctx_assert(handle != 0);
+ m_handle = handle;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/CodeTree.cpp b/storage/ndb/src/old_files/client/odbc/common/CodeTree.cpp
new file mode 100644
index 00000000000..ebe4840c5f6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/CodeTree.cpp
@@ -0,0 +1,37 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "CodeTree.hpp"
+
+// PlanTree
+
+PlanTree::~PlanTree()
+{
+}
+
+// ExecTree
+
+ExecTree::Code::~Code()
+{
+}
+
+ExecTree::Data::~Data()
+{
+}
+
+ExecTree::~ExecTree()
+{
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/CodeTree.hpp b/storage/ndb/src/old_files/client/odbc/common/CodeTree.hpp
new file mode 100644
index 00000000000..1b0ae3199af
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/CodeTree.hpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_CODEGEN_CodeTree_hpp
+#define ODBC_CODEGEN_CodeTree_hpp
+
+#include <common/common.hpp>
+
+/*
+ * Intermediary statement evalution plan. Starts as parse tree. Final
+ * format maps directly to ExecTree.
+ */
+class PlanTree {
+public:
+ virtual ~PlanTree() = 0;
+};
+
+/*
+ * Executable code and runtime data. Currently looks like PlanTree.
+ * Later may change code format to linear "byte code" and move execution
+ * to a SQL block in NDB kernel.
+ */
+class ExecTree {
+public:
+ class Code {
+ public:
+ virtual ~Code() = 0;
+ };
+ class Data {
+ public:
+ virtual ~Data() = 0;
+ };
+ virtual ~ExecTree() = 0;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/ConnArea.cpp b/storage/ndb/src/old_files/client/odbc/common/ConnArea.cpp
new file mode 100644
index 00000000000..d4d3be52a3c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/ConnArea.cpp
@@ -0,0 +1,109 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <dictionary/DictCatalog.hpp>
+#include <dictionary/DictSchema.hpp>
+#include "ConnArea.hpp"
+
+ConnArea::ConnArea() :
+ m_state(Free),
+ m_ndbObject(0),
+ m_ndbSchemaCon(0),
+ m_ndbConnection(0),
+ m_useSchemaCon(0),
+ m_useConnection(0),
+ m_autocommit(true),
+ m_uncommitted(false)
+{
+ // initialize connection catalog
+ m_catalog = new DictCatalog(*this);
+ m_schema = new DictSchema(*this, "NDB");
+ m_catalog->addSchema(m_schema);
+}
+
+ConnArea::~ConnArea()
+{
+ delete m_catalog;
+}
+
+bool
+ConnArea::useSchemaCon(Ctx& ctx, bool use)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "not connected");
+ return false;
+ }
+ Ndb* ndb = m_ndbObject;
+ ctx_assert(ndb != 0);
+ NdbSchemaCon* scon = m_ndbSchemaCon;
+ if (use) {
+ if (scon == 0) {
+ ctx_assert(m_useSchemaCon == 0);
+ scon = ndb->startSchemaTransaction();
+ if (scon == 0) {
+ ctx.pushStatus(ndb, scon, 0, "startSchemaTransaction");
+ return false;
+ }
+ m_ndbSchemaCon = scon;
+ }
+ m_useSchemaCon++;
+ } else {
+ ctx_assert(scon != 0 && m_useSchemaCon != 0);
+ if (--m_useSchemaCon == 0) {
+ ndb->closeSchemaTransaction(scon);
+ m_ndbSchemaCon = 0;
+ }
+ }
+ return true;
+}
+
+bool
+ConnArea::useConnection(Ctx& ctx, bool use)
+{
+ ctx_log3(("useConnection: count before=%u on-off=%d", m_useConnection, (int)use));
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "not connected");
+ return false;
+ }
+ Ndb* ndb = m_ndbObject;
+ ctx_assert(ndb != 0);
+ NdbConnection* tcon = m_ndbConnection;
+ if (use) {
+ if (tcon == 0) {
+ ctx_assert(m_useConnection == 0);
+ tcon = ndb->startTransaction();
+ if (tcon == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "startTransaction");
+ return false;
+ }
+ m_ndbConnection = tcon;
+ m_state = Transacting;
+ ctx_log2(("transaction opened"));
+ }
+ m_useConnection++;
+ } else {
+ ctx_assert(tcon != 0 && m_useConnection != 0);
+ if (--m_useConnection == 0) {
+ ndb->closeTransaction(tcon);
+ m_ndbConnection = 0;
+ m_uncommitted = false;
+ m_state = Connected;
+ ctx_log2(("transaction closed"));
+ }
+ }
+ return true;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/ConnArea.hpp b/storage/ndb/src/old_files/client/odbc/common/ConnArea.hpp
new file mode 100644
index 00000000000..36367a39bae
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/ConnArea.hpp
@@ -0,0 +1,135 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_ConnArea_hpp
+#define ODBC_COMMON_ConnArea_hpp
+
+#include <common/common.hpp>
+
+class Ctx;
+class Ndb;
+class NdbSchemaCon;
+class NdbConnection;
+class DictCatalog;
+class DictSchema;
+
+/**
+ * @class ConnArea
+ * @brief Public part of connection handle
+ */
+class ConnArea {
+public:
+ // state between ODBC function calls
+ enum State {
+ Free = 1, // not in use, no Ndb object
+ Connected = 2, // has Ndb object but no Ndb connection
+ Transacting = 3 // has Ndb connection
+ };
+ State getState() const;
+ DictCatalog& dictCatalog() const;
+ DictSchema& dictSchema() const;
+ Ndb* ndbObject() const;
+ NdbSchemaCon* ndbSchemaCon() const;
+ NdbConnection* ndbConnection() const;
+ // called from StmtArea
+ bool useSchemaCon(Ctx& ctx, bool use);
+ bool useConnection(Ctx& ctx, bool use);
+ // these just get and set the flag - no semantics
+ bool autocommit() const;
+ void autocommit(bool flag);
+ bool uncommitted() const;
+ void uncommitted(bool flag);
+protected:
+ ConnArea();
+ ~ConnArea();
+ State m_state;
+ DictCatalog* m_catalog;
+ DictSchema* m_schema;
+ Ndb* m_ndbObject;
+ NdbSchemaCon* m_ndbSchemaCon;
+ NdbConnection* m_ndbConnection;
+ unsigned m_useSchemaCon;
+ unsigned m_useConnection;
+ bool m_autocommit;
+ bool m_uncommitted; // has uncommitted changes
+};
+
+inline ConnArea::State
+ConnArea::getState() const
+{
+ return m_state;
+}
+
+inline DictCatalog&
+ConnArea::dictCatalog() const
+{
+ ctx_assert(m_catalog != 0);
+ return *m_catalog;
+}
+
+inline DictSchema&
+ConnArea::dictSchema() const
+{
+ ctx_assert(m_schema != 0);
+ return *m_schema;
+}
+
+inline Ndb*
+ConnArea::ndbObject() const
+{
+ ctx_assert(m_ndbObject != 0);
+ return m_ndbObject;
+}
+
+inline NdbSchemaCon*
+ConnArea::ndbSchemaCon() const
+{
+ ctx_assert(m_ndbSchemaCon != 0);
+ return m_ndbSchemaCon;
+}
+
+inline NdbConnection*
+ConnArea::ndbConnection() const
+{
+ ctx_assert(m_ndbConnection != 0);
+ return m_ndbConnection;
+}
+
+inline bool
+ConnArea::autocommit() const
+{
+ return m_autocommit;
+}
+
+inline void
+ConnArea::autocommit(bool flag)
+{
+ m_autocommit = flag;
+}
+
+inline bool
+ConnArea::uncommitted() const
+{
+ return m_uncommitted;
+}
+
+inline void
+ConnArea::uncommitted(bool flag)
+{
+ m_uncommitted = flag;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/Ctx.cpp b/storage/ndb/src/old_files/client/odbc/common/Ctx.cpp
new file mode 100644
index 00000000000..d6faa5cba77
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/Ctx.cpp
@@ -0,0 +1,355 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include "DiagArea.hpp"
+
+// ctor
+
+Ctx::Ctx() :
+ m_diagArea(0) // create on demand
+{
+ const char* p;
+ if ((p = getenv("NDB_ODBC_TRACE")) != 0)
+ m_logLevel = atoi(p);
+ if ((p = getenv("NDB_ODBC_TRACE_FILE")) != 0 && *p != 0)
+ strcpy(m_szTraceFile, p);
+}
+
+Ctx::~Ctx()
+{
+ delete m_diagArea;
+ m_diagArea = 0;
+}
+
+// handle exceptions
+
+CtxAssert::CtxAssert(const char* file, int line) :
+ m_file(file),
+ m_line(line)
+{
+ const char* p;
+ if ((p = getenv("NDB_ODBC_DEBUG")) != 0 && atoi(p) != 0) {
+ char buf[200];
+ snprintf(buf, sizeof(buf), "%s, line %d: assert failed\n", m_file, m_line);
+ if ((p = getenv("NDB_ODBC_TRACE_FILE")) != 0 && *p != 0) {
+ FILE* pFile = fopen(p, "a");
+ fprintf(pFile, buf);
+ fflush(pFile);
+ fclose(pFile);
+ } else {
+ fprintf(stderr, buf);
+ fflush(stderr);
+ }
+ abort();
+ exit(1);
+ }
+}
+
+void
+Ctx::handleEx(CtxAssert& ctxAssert)
+{
+ pushStatus(Sqlstate::_IM001, Error::Gen, "exception at %s line %d", ctxAssert.m_file, ctxAssert.m_line);
+}
+
+// logging methods
+
+int Ctx::m_logLevel = 0;
+char Ctx::m_szTraceFile[MAX_PATH];
+
+void
+Ctx::log(const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (m_szTraceFile[0]) {
+ FILE* pFile = fopen(m_szTraceFile, "a");
+ fprintf(pFile, "[NdbOdbc] ");
+ vfprintf(pFile, fmt, ap);
+ fprintf(pFile, "\n");
+ fflush(pFile);
+ fclose(pFile);
+ } else {
+ printf("[NdbOdbc] ");
+ vprintf(fmt, ap);
+ printf("\n");
+ fflush(stdout);
+ }
+ va_end(ap);
+}
+
+void
+Ctx::logSqlEnter(const char* sqlFunction)
+{
+ Ctx& ctx = *this;
+ snprintf(m_sqlFunction, sizeof(m_sqlFunction), "%s", sqlFunction);
+ ctx_log3(("%s", m_sqlFunction));
+}
+
+void
+Ctx::logSqlExit()
+{
+ Ctx& ctx = *this;
+ if (m_diagArea == 0) {
+ ctx_log3(("%s ret=%d", m_sqlFunction, getCode()));
+ return;
+ }
+ int logLevel = diagArea().numStatus() != 0 ? 2 : 3;
+ ctx_logN(logLevel, ("%s ret=%d diag=%d", m_sqlFunction, diagArea().getCode(), diagArea().numStatus()));
+ for (unsigned i = 1; i <= diagArea().numStatus(); i++) {
+ OdbcData state;
+ OdbcData message;
+ diagArea().getRecord(ctx, i, SQL_DIAG_SQLSTATE, state);
+ diagArea().getRecord(ctx, i, SQL_DIAG_MESSAGE_TEXT, message);
+ ctx_logN(logLevel, ("diag %u: %s - %s", i, state.sqlstate().state(), message.sqlchar()));
+ }
+}
+
+void
+Ctx::print(const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (m_szTraceFile[0]) {
+ FILE* pFile = fopen(m_szTraceFile, "a");
+ vfprintf(pFile, fmt, ap);
+ unsigned n = strlen(fmt);
+ if (n > 0 && fmt[n-1] == '\n')
+ fflush(pFile);
+ fclose(pFile);
+ } else {
+ vprintf(fmt, ap);
+ unsigned n = strlen(fmt);
+ if (n > 0 && fmt[n-1] == '\n')
+ fflush(stdout);
+ }
+ va_end(ap);
+}
+
+void
+Ctx::print(int level, const char* fmt, ...)
+{
+ if (level > m_logLevel)
+ return;
+ va_list ap;
+ va_start(ap, fmt);
+ if (m_szTraceFile[0]) {
+ FILE* pFile = fopen(m_szTraceFile, "a");
+ vfprintf(pFile, fmt, ap);
+ unsigned n = strlen(fmt);
+ if (n > 0 && fmt[n-1] == '\n')
+ fflush(pFile);
+ fclose(pFile);
+ } else {
+ vprintf(fmt, ap);
+ unsigned n = strlen(fmt);
+ if (n > 0 && fmt[n-1] == '\n')
+ fflush(stdout);
+ }
+ va_end(ap);
+}
+
+// diagnostics
+
+static const unsigned MessageSize = 512;
+
+DiagArea&
+Ctx::diagArea() const
+{
+ ctx_assert(m_diagArea != 0);
+ return *m_diagArea;
+}
+
+DiagArea&
+Ctx::diagArea()
+{
+ if (m_diagArea == 0)
+ m_diagArea = new DiagArea;
+ return *m_diagArea;
+}
+
+SQLRETURN
+Ctx::getCode() const
+{
+ if (m_diagArea == 0)
+ return SQL_SUCCESS;
+ return diagArea().getCode();
+}
+
+void
+Ctx::setCode(SQLRETURN ret)
+{
+ diagArea().setCode(ret);
+}
+
+void
+Ctx::pushStatus(const Sqlstate& state, SQLINTEGER code, const char* fmt, ...)
+{
+ char message[MessageSize];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ Error error(state);
+ error.m_status = NdbError::PermanentError;
+ error.m_classification = NdbError::ApplicationError;
+ error.m_code = code;
+ error.m_message = message;
+ error.m_sqlFunction = m_sqlFunction;
+ diagArea().pushStatus(error);
+}
+
+void
+Ctx::pushStatus(SQLINTEGER code, const char* fmt, ...)
+{
+ char message[MessageSize];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ Error error(Sqlstate::_IM000);
+ error.m_status = NdbError::PermanentError;
+ error.m_classification = NdbError::ApplicationError;
+ error.m_code = code;
+ error.m_message = message;
+ error.m_sqlFunction = m_sqlFunction;
+ diagArea().pushStatus(error);
+}
+
+void
+Ctx::pushStatus(const NdbError& ndbError, const char* fmt, ...)
+{
+ char message[MessageSize];
+ va_list ap;
+ va_start(ap, fmt);
+ snprintf(message, sizeof(message), "%s", ndbError.message);
+ snprintf(message + strlen(message), sizeof(message) - strlen(message), "%s", " - at ");
+ vsnprintf(message + strlen(message), sizeof(message) - strlen(message), fmt, ap);
+ va_end(ap);
+ Error error(Sqlstate::_IM000);
+ error.m_status = ndbError.status;
+ error.m_classification = ndbError.classification;
+ error.m_code = ndbError.code;
+ error.m_message = message;
+ error.m_sqlFunction = m_sqlFunction;
+ diagArea().pushStatus(error);
+}
+
+void
+Ctx::pushStatus(const Ndb* ndb, const char* fmt, ...)
+{
+ char message[MessageSize];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ bool found = false;
+ if (ndb != 0) {
+ const NdbError& ndbError = ndb->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (! found) {
+ pushStatus(Error::Gen, "unknown NDB error");
+ }
+}
+
+void
+Ctx::pushStatus(const Ndb* ndb, const NdbConnection* tcon, const NdbOperation* op, const char* fmt, ...)
+{
+ char message[MessageSize];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ bool found = false;
+ if (op != 0) {
+ const NdbError& ndbError = op->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (tcon != 0) {
+ const NdbError& ndbError = tcon->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (ndb != 0) {
+ const NdbError& ndbError = ndb->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (! found) {
+ pushStatus(Error::Gen, "unknown NDB error");
+ }
+}
+
+void
+Ctx::pushStatus(const Ndb* ndb, const NdbSchemaCon* scon, const NdbSchemaOp* op, const char* fmt, ...)
+{
+ char message[MessageSize];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ bool found = false;
+ if (op != 0) {
+ const NdbError& ndbError = op->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (scon != 0) {
+ const NdbError& ndbError = scon->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (ndb != 0) {
+ const NdbError& ndbError = ndb->getNdbError();
+ if (ndbError.code != 0) {
+ pushStatus(ndbError, "%s", message);
+ found = true;
+ }
+ }
+ if (! found) {
+ pushStatus(Error::Gen, "unknown NDB error");
+ }
+}
+
+// check for error
+
+bool
+Ctx::ok()
+{
+ if (m_diagArea == 0)
+ return true;
+ if (diagArea().getCode() == SQL_SUCCESS)
+ return true;
+ if (diagArea().getCode() == SQL_SUCCESS_WITH_INFO)
+ return true;
+ return false;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/Ctx.hpp b/storage/ndb/src/old_files/client/odbc/common/Ctx.hpp
new file mode 100644
index 00000000000..d25d45ff0c7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/Ctx.hpp
@@ -0,0 +1,182 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_Ctx_hpp
+#define ODBC_COMMON_Ctx_hpp
+
+#include <NdbDictionary.hpp>
+
+class Ndb;
+class NdbConnection;
+class NdbOperation;
+class NdbSchemaCon;
+class NdbSchemaOp;
+class NdbError;
+
+class Sqlstate;
+class DiagArea;
+class CtxOwner;
+
+#ifndef MAX_PATH
+#define MAX_PATH 1024
+#endif
+
+/**
+ * @class Error
+ * @brief Sql state, error codes, and message
+ */
+struct Error {
+ enum {
+ Gen = NDB_ODBC_ERROR_MIN + 1 // unclassified
+ };
+ explicit Error(const Sqlstate& sqlstate);
+ const Sqlstate& m_sqlstate;
+ int m_status;
+ int m_classification;
+ int m_code;
+ const char* m_message;
+ const char* m_sqlFunction;
+ bool driverError() const;
+};
+
+inline
+Error::Error(const Sqlstate& sqlstate) :
+ m_sqlstate(sqlstate),
+ m_status(0),
+ m_classification(0),
+ m_code(0),
+ m_sqlFunction(0)
+{
+}
+
+inline bool
+Error::driverError() const
+{
+ return NDB_ODBC_ERROR_MIN <= m_code && m_code < NDB_ODBC_ERROR_MAX;
+}
+
+#define ctx_assert(x) \
+ do { if (x) break; throw CtxAssert(__FILE__, __LINE__); } while (0)
+
+/**
+ * @class Assert
+ * @brief Assert thrown
+ */
+class CtxAssert {
+public:
+ CtxAssert(const char* file, int line);
+ const char* const m_file;
+ const int m_line;
+};
+
+/**
+ * @class Ctx
+ * @brief Context for one ODBC SQL function
+ *
+ * Local to the function (not member of the handle) because methods on
+ * a handle can call methods on other handles. Created in driver/
+ * before method calls and saved under the handle on return. Contains
+ * diag area for the function. Also used as logger.
+ */
+class Ctx {
+public:
+ Ctx();
+ ~Ctx();
+ // handle exceptions
+ void handleEx(CtxAssert& ctxAssert);
+ // logging methods
+ int logLevel() const;
+ void log(const char* fmt, ...) PRINTFLIKE(2,3);
+ void logSqlEnter(const char* sqlFunction);
+ void logSqlExit();
+ const char* sqlFunction() const;
+ void print(const char* fmt, ...) PRINTFLIKE(2,3);
+ void print(int level, const char* fmt, ...) PRINTFLIKE(3,4);
+ // diagnostics area.
+ DiagArea& diagArea() const;
+ DiagArea& diagArea();
+ SQLRETURN getCode() const;
+ void setCode(SQLRETURN ret);
+ // push diagnostic record
+ void pushStatus(const Sqlstate& state, SQLINTEGER code, const char* fmt, ...) PRINTFLIKE(4,5);
+ void pushStatus(SQLINTEGER code, const char* fmt, ...) PRINTFLIKE(3,4);
+ void pushStatus(const NdbError& ndbError, const char* fmt, ...) PRINTFLIKE(3,4);
+ void pushStatus(const Ndb* ndb, const char* fmt, ...) PRINTFLIKE(3,4);
+ void pushStatus(const Ndb* ndb, const NdbConnection* tcon, const NdbOperation* op, const char* fmt, ...) PRINTFLIKE(5,6);
+ void pushStatus(const Ndb* ndb, const NdbSchemaCon* scon, const NdbSchemaOp* op, const char* fmt, ...) PRINTFLIKE(5,6);
+ // check if we should continue executing.
+ bool ok();
+private:
+ static int m_logLevel;
+ static char m_szTraceFile[MAX_PATH];
+ char m_sqlFunction[32]; // max needed is 20
+ DiagArea* m_diagArea;
+};
+
+inline int
+Ctx::logLevel() const
+{
+ return m_logLevel;
+}
+
+inline const char*
+Ctx::sqlFunction() const
+{
+ return m_sqlFunction;
+}
+
+// logging macros can be used only when ctx is in scope
+
+#define ctx_logN(n, x) \
+ do { if (ctx.logLevel() < (n)) break; ctx.log x; } while (0)
+
+#if NDB_ODBC_MAX_LOG_LEVEL >= 0
+#define ctx_log0(x) ctx_logN(0, x)
+#else
+#define ctx_log0(x)
+#endif
+
+#if NDB_ODBC_MAX_LOG_LEVEL >= 1
+#define ctx_log1(x) ctx_logN(1, x)
+#else
+#define ctx_log1(x)
+#endif
+
+#if NDB_ODBC_MAX_LOG_LEVEL >= 2
+#define ctx_log2(x) ctx_logN(2, x)
+#else
+#define ctx_log2(x)
+#endif
+
+#if NDB_ODBC_MAX_LOG_LEVEL >= 3
+#define ctx_log3(x) ctx_logN(3, x)
+#else
+#define ctx_log3(x)
+#endif
+
+#if NDB_ODBC_MAX_LOG_LEVEL >= 4
+#define ctx_log4(x) ctx_logN(4, x)
+#else
+#define ctx_log4(x)
+#endif
+
+#if NDB_ODBC_MAX_LOG_LEVEL >= 5
+#define ctx_log5(x) ctx_logN(5, x)
+#else
+#define ctx_log5(x)
+#endif
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/DataField.cpp b/storage/ndb/src/old_files/client/odbc/common/DataField.cpp
new file mode 100644
index 00000000000..11aae7d893b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DataField.cpp
@@ -0,0 +1,3023 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "DataField.hpp"
+
+#ifndef INT_MAX
+#define INT_MAX (2147483647)
+#endif
+
+#ifndef INT_MIN
+#define INT_MIN (-INT_MAX - 1)
+#endif
+
+#ifndef UINT_MAX
+#define UINT_MAX 4294967295U
+#endif
+
+#ifndef FLT_MAX
+#define FLT_MAX (3.402823466E+38F)
+#endif
+#ifndef FLT_MIN
+#define FLT_MIN (1.175494351E-38F)
+#endif
+
+#ifdef NDB_WIN32
+#define FMT_I64 "%I64d"
+#define FMT_U64 "%I64u"
+#else
+#define FMT_I64 "%lld"
+#define FMT_U64 "%llu"
+#endif
+
+#ifdef NDB_WIN32
+#define strtoll(str, endptr, base) strtoint64(str, endptr, base)
+#define strtoull(str, endptr, base) strtouint64(str, endptr, base)
+
+static Int64
+strtoint64(const char *str, char **endptr, int base)
+{
+ Int64 x = 0;
+ while (*str == ' ')
+ str++;
+ const char* p = str;
+ while ('0' <= *p && *p <= '9')
+ x = 10 * x + *p++ - '0';
+ if (p == str) {
+ *endptr = 0;
+ return 0;
+ }
+ *endptr = (char*)p;
+ return x;
+}
+
+static Uint64
+strtouint64(const char *str, char **endptr, int base)
+{
+ Uint64 x = 0;
+ while (*str == ' ')
+ str++;
+ const char* p = str;
+ while ('0' <= *p && *p <= '9')
+ x = 10 * x + *p++ - '0';
+ if (p == str) {
+ *endptr = 0;
+ return 0;
+ }
+ *endptr = (char*)p;
+ return x;
+}
+#endif
+
+// LexSpec
+
+void
+LexSpec::convert(Ctx& ctx, const BaseString& value, SqlField& out)
+{
+ const SqlSpec& sqlSpec = out.sqlSpec();
+ const SqlType& sqlType = sqlSpec.sqlType();
+ out.alloc();
+ if (sqlType.type() == SqlType::Char) {
+ const SqlChar* s = (const SqlChar*)value.c_str();
+ out.sqlChar(s, SQL_NTS);
+ return;
+ }
+ if (sqlType.type() == SqlType::Bigint) {
+ char* endptr = 0;
+ SqlBigint n = static_cast<SqlBigint>(strtoll(value.c_str(), &endptr, 10));
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Error::Gen, "cannot convert '%s' to integer", value.c_str());
+ return;
+ }
+ out.sqlBigint(n);
+ return;
+ }
+ if (sqlType.type() == SqlType::Double) {
+ char* endptr = 0;
+ SqlDouble x = static_cast<SqlDouble>(strtod(value.c_str(), &endptr));
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Error::Gen, "cannot convert '%s' to number", value.c_str());
+ return;
+ }
+ out.sqlDouble(x);
+ return;
+ }
+ if (sqlType.type() == SqlType::Null) {
+ out.u_null.m_nullFlag = true;
+ return;
+ }
+ ctx_assert(false);
+}
+
+// SqlField
+
+void
+SqlField::alloc()
+{
+ ctx_assert(sqlSpec().store() == SqlSpec::Physical);
+ const SqlType& sqlType = sqlSpec().sqlType();
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varchar)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ u_data.m_sqlChar = new SqlChar[n];
+ }
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varbinary)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ u_data.m_sqlChar = new SqlChar[n];
+ }
+ }
+}
+
+void
+SqlField::alloc(const SqlField& sqlField)
+{
+ alloc();
+ const SqlType& sqlType = sqlSpec().sqlType();
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varchar)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ memcpy(u_data.m_sqlChar, sqlField.u_data.m_sqlChar, n);
+ }
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varbinary)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ memcpy(u_data.m_sqlChar, sqlField.u_data.m_sqlChar, n);
+ }
+ }
+}
+
+const void*
+SqlField::addr() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->addr();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varchar)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ return static_cast<const void*>(u_data.m_sqlChar);
+ }
+ return static_cast<const void*>(u_data.m_sqlCharSmall);
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varbinary)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ return static_cast<const void*>(u_data.m_sqlChar);
+ }
+ return static_cast<const void*>(u_data.m_sqlCharSmall);
+ }
+ if (sqlType.type() == SqlType::Smallint) {
+ return static_cast<const void*>(&u_data.m_sqlSmallint);
+ }
+ if (sqlType.type() == SqlType::Integer) {
+ return static_cast<const void*>(&u_data.m_sqlInteger);
+ }
+ if (sqlType.type() == SqlType::Bigint) {
+ return static_cast<const void*>(&u_data.m_sqlBigint);
+ }
+ if (sqlType.type() == SqlType::Real) {
+ return static_cast<const void*>(&u_data.m_sqlReal);
+ }
+ if (sqlType.type() == SqlType::Double) {
+ return static_cast<const void*>(&u_data.m_sqlDouble);
+ }
+ if (sqlType.type() == SqlType::Datetime) {
+ return static_cast<const void*>(&u_data.m_sqlDatetime);
+ }
+ ctx_assert(false); // SqlType::Null has no address
+ return 0;
+}
+
+void*
+SqlField::addr()
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varchar)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ return static_cast<void*>(u_data.m_sqlChar);
+ }
+ return static_cast<void*>(u_data.m_sqlCharSmall);
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varbinary)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ return static_cast<void*>(u_data.m_sqlChar);
+ }
+ return static_cast<void*>(u_data.m_sqlCharSmall);
+ }
+ if (sqlType.type() == SqlType::Smallint) {
+ return static_cast<void*>(&u_data.m_sqlSmallint);
+ }
+ if (sqlType.type() == SqlType::Integer) {
+ return static_cast<void*>(&u_data.m_sqlInteger);
+ }
+ if (sqlType.type() == SqlType::Bigint) {
+ return static_cast<void*>(&u_data.m_sqlBigint);
+ }
+ if (sqlType.type() == SqlType::Real) {
+ return static_cast<void*>(&u_data.m_sqlReal);
+ }
+ if (sqlType.type() == SqlType::Double) {
+ return static_cast<void*>(&u_data.m_sqlDouble);
+ }
+ if (sqlType.type() == SqlType::Datetime) {
+ return static_cast<void*>(&u_data.m_sqlDatetime);
+ }
+ ctx_assert(false); // SqlType::Null has no address
+ return 0;
+}
+
+unsigned
+SqlField::allocSize() const
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ unsigned n = sqlType.size();
+ if (sqlType.type() == SqlType::Varchar || sqlType.type() == SqlType::Varbinary) {
+ n += 2;
+ }
+ return n;
+}
+
+void
+SqlField::free()
+{
+ ctx_assert(sqlSpec().store() == SqlSpec::Physical);
+ const SqlType& sqlType = sqlSpec().sqlType();
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varchar)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ delete[] u_data.m_sqlChar;
+ u_data.m_sqlChar = 0; // safety since dtor used explicitly
+ }
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned n = sqlType.length();
+ if (sqlType.type() == SqlType::Varbinary)
+ n += 2;
+ if (n > SqlField_CharSmall) {
+ delete[] u_data.m_sqlChar;
+ u_data.m_sqlChar = 0; // safety since dtor used explicitly
+ }
+ }
+}
+
+// get
+
+const SqlChar*
+SqlField::sqlChar() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlChar();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Char);
+ if (sqlType.length() > SqlField_CharSmall)
+ return u_data.m_sqlChar;
+ return u_data.m_sqlCharSmall;
+}
+
+const SqlChar*
+SqlField::sqlVarchar(unsigned* length) const
+{
+#if NDB_VERSION_MAJOR >= 3
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlVarchar(length);
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varchar);
+ const SqlChar* sqlChar;
+ unsigned n = sqlType.length();
+ if (2 + n > SqlField_CharSmall)
+ sqlChar = u_data.m_sqlChar;
+ else
+ sqlChar = u_data.m_sqlCharSmall;
+ if (length != 0)
+ *length = (sqlChar[0] << 8) | sqlChar[1]; // big-endian
+ return sqlChar + 2;
+#else
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlVarchar(length);
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varchar);
+ const SqlChar* sqlChar;
+ unsigned n = sqlType.length();
+ if (n + 2 > SqlField_CharSmall)
+ sqlChar = u_data.m_sqlChar;
+ else
+ sqlChar = u_data.m_sqlCharSmall;
+ if (length != 0)
+ *length = (sqlChar[n + 0] << 8) | sqlChar[n + 1]; // big-endian
+ return sqlChar;
+#endif
+}
+
+const SqlChar*
+SqlField::sqlBinary() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlChar();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Binary);
+ if (sqlType.length() > SqlField_CharSmall)
+ return u_data.m_sqlChar;
+ return u_data.m_sqlCharSmall;
+}
+
+const SqlChar*
+SqlField::sqlVarbinary(unsigned* length) const
+{
+#if NDB_VERSION_MAJOR >= 3
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlVarchar(length);
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varbinary);
+ const SqlChar* sqlChar;
+ unsigned n = sqlType.length();
+ if (2 + n > SqlField_CharSmall)
+ sqlChar = u_data.m_sqlChar;
+ else
+ sqlChar = u_data.m_sqlCharSmall;
+ if (length != 0)
+ *length = (sqlChar[0] << 8) | sqlChar[1]; // big-endian
+ return sqlChar + 2;
+#else
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlVarchar(length);
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varbinary);
+ const SqlChar* sqlChar;
+ unsigned n = sqlType.length();
+ if (n + 2 > SqlField_CharSmall)
+ sqlChar = u_data.m_sqlChar;
+ else
+ sqlChar = u_data.m_sqlCharSmall;
+ if (length != 0)
+ *length = (sqlChar[n + 0] << 8) | sqlChar[n + 1]; // big-endian
+ return sqlChar;
+#endif
+}
+
+SqlSmallint
+SqlField::sqlSmallint() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlSmallint();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Smallint);
+ return u_data.m_sqlSmallint;
+}
+
+SqlInteger
+SqlField::sqlInteger() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlInteger();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Integer);
+ return u_data.m_sqlInteger;
+}
+
+SqlBigint
+SqlField::sqlBigint() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlBigint();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Bigint);
+ return u_data.m_sqlBigint;
+}
+
+SqlReal
+SqlField::sqlReal() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlReal();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Real);
+ return u_data.m_sqlReal;
+}
+
+SqlDouble
+SqlField::sqlDouble() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlDouble();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Double);
+ return u_data.m_sqlDouble;
+}
+
+SqlDatetime
+SqlField::sqlDatetime() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlDatetime();
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Datetime);
+ return u_data.m_sqlDatetime;
+}
+
+// set
+
+void
+SqlField::sqlChar(const SqlChar* value, int length)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Char);
+ unsigned n = sqlType.length();
+ SqlChar* p = n > SqlField_CharSmall ? u_data.m_sqlChar : u_data.m_sqlCharSmall;
+ const SqlChar* q = value;
+ unsigned m = length == SQL_NTS ? strlen((const char*)q) : length;
+ ctx_assert(m <= n);
+ for (unsigned i = 0; i < m; i++)
+ *p++ = *q++;
+ for (unsigned i = m; i < n; i++)
+ *p++ = 0x20; // space
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlVarchar(const SqlChar* value, int length)
+{
+#if NDB_VERSION_MAJOR >= 3
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varchar);
+ unsigned n = sqlType.length();
+ SqlChar* p = 2 + n > SqlField_CharSmall ? u_data.m_sqlChar : u_data.m_sqlCharSmall;
+ const SqlChar* q = value;
+ unsigned m = length == SQL_NTS ? strlen((const char*)q) : length;
+ ctx_assert(m <= n);
+ *p++ = (m >> 8) & 0xff; // big-endian
+ *p++ = (m & 0xff);
+ for (unsigned i = 0; i < m; i++)
+ *p++ = *q++;
+ for (unsigned i = m; i < n; i++)
+ *p++ = 0x0; // null
+ u_null.m_nullFlag = false;
+#else
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varchar);
+ unsigned n = sqlType.length();
+ SqlChar* p = n + 2 > SqlField_CharSmall ? u_data.m_sqlChar : u_data.m_sqlCharSmall;
+ const SqlChar* q = value;
+ unsigned m = length == SQL_NTS ? strlen((const char*)q) : length;
+ ctx_assert(m <= n);
+ for (unsigned i = 0; i < m; i++)
+ *p++ = *q++;
+ for (unsigned i = m; i < n; i++)
+ *p++ = 0x0; // null
+ *p++ = (m >> 8) & 0xff; // big-endian
+ *p++ = (m & 0xff);
+ u_null.m_nullFlag = false;
+#endif
+}
+
+void
+SqlField::sqlBinary(const SqlChar* value, int length)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Binary);
+ unsigned n = sqlType.length();
+ SqlChar* p = n > SqlField_CharSmall ? u_data.m_sqlChar : u_data.m_sqlCharSmall;
+ const SqlChar* q = value;
+ unsigned m = length;
+ ctx_assert(m <= n);
+ for (unsigned i = 0; i < m; i++)
+ *p++ = *q++;
+ for (unsigned i = m; i < n; i++)
+ *p++ = 0x0; // null
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlVarbinary(const SqlChar* value, int length)
+{
+#if NDB_VERSION_MAJOR >= 3
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varbinary);
+ unsigned n = sqlType.length();
+ SqlChar* p = 2 + n > SqlField_CharSmall ? u_data.m_sqlChar : u_data.m_sqlCharSmall;
+ const SqlChar* q = value;
+ unsigned m = length;
+ ctx_assert(m <= n);
+ *p++ = (m >> 8) & 0xff; // big-endian
+ *p++ = (m & 0xff);
+ for (unsigned i = 0; i < m; i++)
+ *p++ = *q++;
+ for (unsigned i = m; i < n; i++)
+ *p++ = 0x0; // null
+ u_null.m_nullFlag = false;
+#else
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Varbinary);
+ unsigned n = sqlType.length();
+ SqlChar* p = n + 2 > SqlField_CharSmall ? u_data.m_sqlChar : u_data.m_sqlCharSmall;
+ const SqlChar* q = value;
+ unsigned m = length;
+ ctx_assert(m <= n);
+ for (unsigned i = 0; i < m; i++)
+ *p++ = *q++;
+ for (unsigned i = m; i < n; i++)
+ *p++ = 0x0; // null
+ *p++ = (m >> 8) & 0xff; // big-endian
+ *p++ = (m & 0xff);
+ u_null.m_nullFlag = false;
+#endif
+}
+
+void
+SqlField::sqlSmallint(SqlSmallint value)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Smallint);
+ u_data.m_sqlSmallint = value;
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlInteger(SqlInteger value)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Integer);
+ u_data.m_sqlInteger = value;
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlBigint(SqlBigint value)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Bigint);
+ u_data.m_sqlBigint = value;
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlReal(SqlReal value)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Real);
+ u_data.m_sqlReal = value;
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlDouble(SqlDouble value)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Double);
+ u_data.m_sqlDouble = value;
+ u_null.m_nullFlag = false;
+}
+
+void
+SqlField::sqlDatetime(SqlDatetime value)
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ ctx_assert(sqlType.type() == SqlType::Datetime);
+ u_data.m_sqlDatetime = value;
+ u_null.m_nullFlag = false;
+}
+
+// get and and set null
+
+bool
+SqlField::sqlNull() const
+{
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ return u_data.m_sqlField->sqlNull();
+ }
+ return u_null.m_nullFlag;
+}
+
+void
+SqlField::sqlNull(bool value)
+{
+ u_null.m_nullFlag = value;
+}
+
+unsigned
+SqlField::trim() const
+{
+ const SqlType& sqlType = sqlSpec().sqlType();
+ unsigned n = 0;
+ const SqlChar* s = 0;
+ if (sqlType.type() == SqlType::Char) {
+ n = sqlType.length();
+ s = sqlChar();
+ } else if (sqlType.type() == SqlType::Varchar) {
+ s = sqlVarchar(&n);
+ } else {
+ ctx_assert(false);
+ return 0;
+ }
+ while (n > 0 && s[n - 1] == 0x20)
+ n--;
+ return n;
+}
+
+void
+SqlField::copy(Ctx& ctx, SqlField& out) const
+{
+ const SqlField& f1 = *this;
+ SqlField& f2 = out;
+ const SqlType& t1 = f1.sqlSpec().sqlType();
+ const SqlType& t2 = f2.sqlSpec().sqlType();
+ ctx_assert(t1.type() == t2.type());
+ if (f1.sqlNull()) {
+ f2.sqlNull(true);
+ return;
+ }
+ if (t1.type() == SqlType::Char) {
+ f2.sqlChar(f1.sqlChar(), t1.length());
+ return;
+ }
+ if (t1.type() == SqlType::Varchar) {
+ unsigned length;
+ const SqlChar* s1 = f1.sqlVarchar(&length);
+ f2.sqlVarchar(s1, length);
+ return;
+ }
+ if (t1.type() == SqlType::Binary) {
+ f2.sqlBinary(f1.sqlBinary(), t1.length());
+ return;
+ }
+ if (t1.type() == SqlType::Varbinary) {
+ unsigned length;
+ const SqlChar* s1 = f1.sqlVarbinary(&length);
+ f2.sqlVarbinary(s1, length);
+ return;
+ }
+ if (t1.type() == SqlType::Smallint) {
+ f2.sqlSmallint(f1.sqlSmallint());
+ return;
+ }
+ if (t1.type() == SqlType::Integer) {
+ f2.sqlInteger(f1.sqlInteger());
+ return;
+ }
+ if (t1.type() == SqlType::Bigint) {
+ f2.sqlBigint(f1.sqlBigint());
+ return;
+ }
+ if (t1.type() == SqlType::Real) {
+ f2.sqlReal(f1.sqlReal());
+ return;
+ }
+ if (t1.type() == SqlType::Double) {
+ f2.sqlDouble(f1.sqlDouble());
+ return;
+ }
+ if (t1.type() == SqlType::Datetime) {
+ f2.sqlDatetime(f1.sqlDatetime());
+ return;
+ }
+ ctx_assert(false);
+}
+
+bool
+SqlField::cast(Ctx& ctx, SqlField& out) const
+{
+ const SqlField& f1 = *this;
+ SqlField& f2 = out;
+ if (f1.sqlNull()) {
+ f2.sqlNull(true);
+ return true;
+ }
+ const SqlType& t1 = f1.sqlSpec().sqlType();
+ const SqlType& t2 = f2.sqlSpec().sqlType();
+ if (t1.type() == SqlType::Char) {
+ if (t2.type() == SqlType::Char) {
+ unsigned n1 = f1.trim();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlChar(f1.sqlChar(), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Varchar) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlVarchar(f1.sqlChar(), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Binary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlBinary(f1.sqlChar(), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Varbinary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlVarbinary(f1.sqlChar(), n1);
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Varchar) {
+ if (t2.type() == SqlType::Char) {
+ unsigned n1 = f1.trim();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlChar(f1.sqlVarchar(0), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Varchar) {
+ unsigned n1 = f1.trim();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlVarchar(f1.sqlVarchar(0), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Binary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlBinary(f1.sqlVarchar(0), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Varbinary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlVarbinary(f1.sqlVarchar(0), n1);
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Binary) {
+ if (t2.type() == SqlType::Binary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlBinary(f1.sqlBinary(), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Varbinary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlVarbinary(f1.sqlBinary(), n1);
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Varbinary) {
+ if (t2.type() == SqlType::Binary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlBinary(f1.sqlVarbinary(0), n1);
+ return true;
+ }
+ if (t2.type() == SqlType::Varbinary) {
+ unsigned n1 = t1.length();
+ if (n1 > t2.length())
+ return false;
+ f2.sqlVarbinary(f1.sqlVarbinary(0), n1);
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Smallint) {
+ if (! t2.unSigned()) {
+ SqlSmallint x1 = f1.sqlSmallint();
+ if (t2.type() == SqlType::Smallint) {
+ f2.sqlSmallint(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ SqlInteger x2 = static_cast<SqlInteger>(x1);
+ f2.sqlInteger(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ SqlBigint x2 = static_cast<SqlBigint>(x1);
+ f2.sqlBigint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Real) {
+ SqlReal x2 = static_cast<SqlReal>(x1);
+ f2.sqlReal(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Double) {
+ SqlDouble x2 = static_cast<SqlDouble>(x1);
+ f2.sqlDouble(x2);
+ return true;
+ }
+ } else {
+ SqlUsmallint x1 = f1.sqlSmallint();
+ if (t2.type() == SqlType::Smallint) {
+ f2.sqlSmallint(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ SqlUinteger x2 = static_cast<SqlUinteger>(x1);
+ f2.sqlInteger(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ SqlUbigint x2 = static_cast<SqlUbigint>(x1);
+ f2.sqlBigint(x2);
+ return true;
+ }
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Integer) {
+ if (! t2.unSigned()) {
+ SqlInteger x1 = f1.sqlInteger();
+ if (t2.type() == SqlType::Smallint) {
+ SqlSmallint x2 = static_cast<SqlSmallint>(x1);
+ if (x1 != static_cast<SqlInteger>(x2))
+ return false;
+ f2.sqlSmallint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ f2.sqlInteger(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ SqlBigint x2 = static_cast<SqlBigint>(x1);
+ f2.sqlBigint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Real) {
+ SqlReal x2 = static_cast<SqlReal>(x1);
+ f2.sqlReal(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Double) {
+ SqlDouble x2 = static_cast<SqlDouble>(x1);
+ f2.sqlDouble(x2);
+ return true;
+ }
+ } else {
+ SqlUinteger x1 = f1.sqlInteger();
+ if (t2.type() == SqlType::Smallint) {
+ SqlUsmallint x2 = static_cast<SqlUsmallint>(x1);
+ if (x1 != static_cast<SqlUinteger>(x2))
+ return false;
+ f2.sqlSmallint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ f2.sqlInteger(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ SqlUbigint x2 = static_cast<SqlUbigint>(x1);
+ f2.sqlBigint(x2);
+ return true;
+ }
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Bigint) {
+ if (! t2.unSigned()) {
+ SqlBigint x1 = f1.sqlBigint();
+ if (t2.type() == SqlType::Smallint) {
+ SqlSmallint x2 = static_cast<SqlSmallint>(x1);
+ if (x1 != static_cast<SqlBigint>(x2))
+ return false;
+ f2.sqlSmallint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ SqlInteger x2 = static_cast<SqlInteger>(x1);
+ if (x1 != static_cast<SqlBigint>(x2))
+ return false;
+ f2.sqlInteger(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ f2.sqlBigint(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Real) {
+ SqlReal x2 = static_cast<SqlReal>(x1);
+ f2.sqlReal(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Double) {
+ SqlDouble x2 = static_cast<SqlDouble>(x1);
+ f2.sqlDouble(x2);
+ return true;
+ }
+ } else {
+ SqlUbigint x1 = f1.sqlBigint();
+ if (t2.type() == SqlType::Smallint) {
+ SqlUsmallint x2 = static_cast<SqlUsmallint>(x1);
+ if (x1 != static_cast<SqlUbigint>(x2))
+ return false;
+ f2.sqlSmallint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ SqlUinteger x2 = static_cast<SqlUinteger>(x1);
+ if (x1 != static_cast<SqlUbigint>(x2))
+ return false;
+ f2.sqlInteger(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ f2.sqlBigint(x1);
+ return true;
+ }
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Real) {
+ SqlReal x1 = f1.sqlReal();
+ int off = 0;
+ if (x1 > 0.0 && x1 - floor(x1) >= 0.5)
+ off = 1;
+ if (x1 < 0.0 && x1 - floor(x1) <= 0.5)
+ off = -1;
+ bool b = (x1 - floor(x1) < 0.5);
+ if (t2.type() == SqlType::Smallint) {
+ SqlSmallint x2 = static_cast<SqlSmallint>(x1) + off;
+ if (fabs(x1 - static_cast<SqlReal>(x2)) >= 1.0)
+ return false;
+ f2.sqlSmallint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ SqlInteger x2 = static_cast<SqlInteger>(x1) + off;
+ if (fabs(x1 - static_cast<SqlReal>(x2)) >= 1.0)
+ return false;
+ f2.sqlInteger(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ SqlBigint x2 = static_cast<SqlBigint>(x1) + off;
+ if (fabs(x1 - static_cast<SqlReal>(x2)) >= 1.0)
+ return false;
+ f2.sqlBigint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Real) {
+ f2.sqlReal(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Double) {
+ SqlDouble x2 = static_cast<SqlDouble>(x1);
+ f2.sqlDouble(x2);
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+ }
+ if (t1.type() == SqlType::Double) {
+ SqlDouble x1 = f1.sqlDouble();
+ int off = 0;
+ if (x1 > 0.0 && x1 - floor(x1) >= 0.5)
+ off = 1;
+ if (x1 < 0.0 && x1 - floor(x1) <= 0.5)
+ off = -1;
+ bool b = (x1 - floor(x1) < 0.5);
+ if (t2.type() == SqlType::Smallint) {
+ SqlSmallint x2 = static_cast<SqlSmallint>(x1) + off;
+ if (fabs(x1 - static_cast<SqlDouble>(x2)) >= 1.0)
+ return false;
+ f2.sqlSmallint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Integer) {
+ SqlInteger x2 = static_cast<SqlInteger>(x1) + off;
+ if (fabs(x1 - static_cast<SqlDouble>(x2)) >= 1.0)
+ return false;
+ f2.sqlInteger(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Bigint) {
+ SqlBigint x2 = static_cast<SqlBigint>(x1) + off;
+ if (fabs(x1 - static_cast<SqlDouble>(x2)) >= 1.0)
+ return false;
+ f2.sqlBigint(x2);
+ return true;
+ }
+ if (t2.type() == SqlType::Real) {
+ SqlReal x2 = static_cast<SqlReal>(x1);
+ if (fabs(x1 - static_cast<SqlDouble>(x2)) >= 1.0) // XXX
+ return false;
+ f2.sqlReal(x1);
+ return true;
+ }
+ if (t2.type() == SqlType::Double) {
+ f2.sqlDouble(x1);
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+ }
+ ctx_assert(false);
+ return false;
+}
+
+bool
+SqlField::less(const SqlField& sqlField) const
+{
+ const SqlField& f1 = *this;
+ const SqlField& f2 = sqlField;
+ const SqlType& t1 = f1.sqlSpec().sqlType();
+ const SqlType& t2 = f2.sqlSpec().sqlType();
+ ctx_assert(t1.type() == t2.type());
+ if (t1.type() == SqlType::Char) {
+ const SqlChar* s1 = f1.sqlChar();
+ const SqlChar* s2 = f2.sqlChar();
+ unsigned n1 = t1.length();
+ unsigned n2 = t2.length();
+ SqlChar c1 = 0;
+ SqlChar c2 = 0;
+ unsigned i = 0;
+ while (i < n1 || i < n2) {
+ c1 = i < n1 ? s1[i] : 0x20;
+ c2 = i < n2 ? s2[i] : 0x20;
+ if (c1 != c2)
+ break;
+ i++;
+ }
+ return (c1 < c2);
+ }
+ if (t1.type() == SqlType::Varchar) {
+ unsigned n1, n2;
+ const SqlChar* s1 = f1.sqlVarchar(&n1);
+ const SqlChar* s2 = f2.sqlVarchar(&n2);
+ SqlChar c1 = 0;
+ SqlChar c2 = 0;
+ unsigned i = 0;
+ while (i < n1 || i < n2) {
+ c1 = i < n1 ? s1[i] : 0x0;
+ c2 = i < n2 ? s2[i] : 0x0;
+ if (c1 != c2)
+ break;
+ i++;
+ }
+ return (c1 < c2);
+ }
+ if (t1.type() == SqlType::Smallint) {
+ ctx_assert(t1.unSigned() == t2.unSigned());
+ if (! t1.unSigned()) {
+ SqlSmallint x1 = f1.sqlSmallint();
+ SqlSmallint x2 = f2.sqlSmallint();
+ return (x1 < x2);
+ } else {
+ SqlUsmallint x1 = f1.sqlSmallint();
+ SqlUsmallint x2 = f2.sqlSmallint();
+ return (x1 < x2);
+ }
+ }
+ if (t1.type() == SqlType::Integer) {
+ ctx_assert(t1.unSigned() == t2.unSigned());
+ if (! t1.unSigned()) {
+ SqlInteger x1 = f1.sqlInteger();
+ SqlInteger x2 = f2.sqlInteger();
+ return (x1 < x2);
+ } else {
+ SqlUinteger x1 = f1.sqlInteger();
+ SqlUinteger x2 = f2.sqlInteger();
+ return (x1 < x2);
+ }
+ }
+ if (t1.type() == SqlType::Bigint) {
+ ctx_assert(t1.unSigned() == t2.unSigned());
+ if (! t1.unSigned()) {
+ SqlBigint x1 = f1.sqlBigint();
+ SqlBigint x2 = f2.sqlBigint();
+ return (x1 < x2);
+ } else {
+ SqlUbigint x1 = f1.sqlBigint();
+ SqlUbigint x2 = f2.sqlBigint();
+ return (x1 < x2);
+ }
+ }
+ if (t1.type() == SqlType::Real) {
+ SqlReal x1 = f1.sqlReal();
+ SqlReal x2 = f2.sqlReal();
+ return (x1 < x2);
+ }
+ if (t1.type() == SqlType::Double) {
+ SqlDouble x1 = f1.sqlDouble();
+ SqlDouble x2 = f2.sqlDouble();
+ return (x1 < x2);
+ }
+ if (t1.type() == SqlType::Datetime) {
+ SqlDatetime x1 = f1.sqlDatetime();
+ SqlDatetime x2 = f2.sqlDatetime();
+ return x1.less(x2);
+ }
+ ctx_assert(false);
+}
+
+// copy from external
+
+static bool
+copyin_char_char(Ctx& ctx, char* value, unsigned n, const char* ptr, const SQLINTEGER* ind, int* off, SqlChar* addr, int fieldId)
+{
+ if (off != 0 && *off >= 0) {
+ if ((unsigned)*off > n) {
+ ctx.pushStatus(Sqlstate::_22001, Error::Gen, "input parameter %d truncated (%u > %u)", fieldId, (unsigned)*off, n);
+ return false;
+ }
+ value += *off;
+ n -= *off;
+ }
+ unsigned m;
+ if (ind == 0 || *ind == SQL_NTS)
+ m = strlen(ptr);
+ else
+ m = *ind;
+ if (m > n) {
+ ctx.pushStatus(Sqlstate::_22001, Error::Gen, "input parameter %d truncated (%u > %u)", fieldId, m, n);
+ return false;
+ }
+ for (unsigned i = 0; i < m; i++)
+ value[i] = ptr[i];
+ if (off != 0 && *off >= 0)
+ *off += m;
+ for (unsigned i = m; i < n; i++)
+ value[i] = addr == 0 ? 0x20 : 0x0;
+ if (addr != 0) {
+ if (off != 0 && *off >= 0)
+ m = *off;
+ addr[0] = (m >> 8) & 0xff;
+ addr[1] = (m & 0xff);
+ }
+ return true;
+}
+
+static bool
+copyin_binary_binary(Ctx& ctx, char* value, unsigned n, const char* ptr, const SQLINTEGER* ind, int* off, SqlChar* addr, int fieldId)
+{
+ if (off != 0 && *off >= 0) {
+ if ((unsigned)*off > n) {
+ ctx.pushStatus(Sqlstate::_22001, Error::Gen, "input parameter %d truncated (%u > %u)", fieldId, (unsigned)*off, n);
+ return false;
+ }
+ value += *off;
+ n -= *off;
+ }
+ if (ind == 0) {
+ ctx.pushStatus(Sqlstate::_22001, Error::Gen, "input parameter %d missing length", fieldId);
+ return false;
+ }
+ if (*ind < 0) {
+ ctx.pushStatus(Sqlstate::_22001, Error::Gen, "input parameter %d invalid length %d", fieldId, (int)*ind);
+ return false;
+ }
+ unsigned m;
+ m = *ind;
+ if (m > n) {
+ ctx.pushStatus(Sqlstate::_22001, Error::Gen, "input parameter %d truncated (%u > %u)", fieldId, m, n);
+ return false;
+ }
+ for (unsigned i = 0; i < m; i++)
+ value[i] = ptr[i];
+ if (off != 0 && *off >= 0)
+ *off += m;
+ for (unsigned i = m; i < n; i++)
+ value[i] = addr == 0 ? 0x0 : 0x0; // just null
+ if (addr != 0) {
+ if (off != 0 && *off >= 0)
+ m = *off;
+ addr[0] = (m >> 8) & 0xff;
+ addr[1] = (m & 0xff);
+ }
+ return true;
+}
+
+static bool
+copyin_signed_char(Ctx& ctx, SqlBigint* value, const char* ptr, int fieldId)
+{
+ errno = 0;
+ char* endptr = 0;
+ SqlBigint x = strtoll(ptr, &endptr, 10);
+ if (endptr == 0 || *endptr != 0) {
+ errno = 0;
+ endptr = 0;
+ double y = strtod(ptr, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "input parameter %d value %s not numeric", fieldId, ptr);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "input parameter %d value %s overflow", fieldId, ptr);
+ return false;
+ }
+ // XXX should handle 123.000
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "input parameter %d value %s truncated", fieldId, ptr);
+ x = static_cast<SqlBigint>(y);
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "input parameter %d value %s overflow", fieldId, ptr);
+ return false;
+ }
+ *value = x;
+ return true;
+}
+
+static bool
+copyin_double_char(Ctx& ctx, SqlDouble* value, const char* ptr, int fieldId)
+{
+ errno = 0;
+ char* endptr = 0;
+ double x = strtod(ptr, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "input parameter %d value %s not numeric", fieldId, ptr);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "input parameter %d value %s overflow", fieldId, ptr);
+ return false;
+ }
+ *value = x;
+ return true;
+}
+
+void
+SqlField::copyin(Ctx& ctx, ExtField& extField)
+{
+ ctx_assert(extField.extSpec().extType().type() != ExtType::Unbound);
+ ctx_assert(sqlSpec().store() == SqlSpec::Physical);
+ SQLINTEGER* indPtr = extField.m_indPtr;
+ const int fieldId = extField.fieldId();
+ if (indPtr != 0 && *indPtr == SQL_NULL_DATA) {
+ sqlNull(true);
+ return;
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ const ExtType& extType = extField.extSpec().extType();
+ if (extField.m_pos > 0) {
+ if (sqlType.type() == SqlType::Char && extType.type() == ExtType::Char)
+ ;
+ else if (sqlType.type() == SqlType::Varchar && extType.type() == ExtType::Char)
+ ;
+ else {
+ char buf[40];
+ sqlType.print(buf, sizeof(buf));
+ ctx.pushStatus(Sqlstate::_HY019, Error::Gen, "cannot send %s data in pieces", buf);
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned length = 0;
+ char* value = 0;
+ SqlChar* laddr = 0; // Varchar length address
+ if (sqlType.type() == SqlType::Char) {
+ length = sqlType.length();
+ if (length > SqlField_CharSmall)
+ value = reinterpret_cast<char *>(u_data.m_sqlChar);
+ else
+ value = reinterpret_cast<char *>(u_data.m_sqlCharSmall);
+ laddr = 0;
+ } else {
+#if NDB_VERSION_MAJOR >= 3
+ length = sqlType.length();
+ if (2 + length > SqlField_CharSmall)
+ value = reinterpret_cast<char *>(u_data.m_sqlChar + 2);
+ else
+ value = reinterpret_cast<char *>(u_data.m_sqlCharSmall + 2);
+ laddr = (SqlChar*)value - 2;
+#else
+ length = sqlType.length();
+ if (length + 2 > SqlField_CharSmall)
+ value = reinterpret_cast<char *>(u_data.m_sqlChar);
+ else
+ value = reinterpret_cast<char *>(u_data.m_sqlCharSmall);
+ laddr = (SqlChar*)value + length;
+#endif
+ }
+ if (extType.type() == ExtType::Char) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ int* off = 0;
+ if (extField.m_pos >= 0)
+ off = &extField.m_pos;
+ if (! copyin_char_char(ctx, value, length, dataPtr, indPtr, off, laddr, fieldId))
+ return;
+ sqlNull(false);
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ const short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%hd", *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ const unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%hu", *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ const long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%ld", *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ const unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%lu", *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ const SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, FMT_I64, *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ const SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, FMT_U64, *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ const float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%.7f", (double)*dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ const double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%.14f", *dataPtr);
+ if (! copyin_char_char(ctx, value, length, buf, indPtr, 0, laddr, fieldId))
+ return;
+ sqlNull(false);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned length = 0;
+ char* value = 0;
+ SqlChar* laddr = 0; // Varbinary length address
+ if (sqlType.type() == SqlType::Binary) {
+ length = sqlType.length();
+ if (length > SqlField_CharSmall)
+ value = reinterpret_cast<char *>(u_data.m_sqlChar);
+ else
+ value = reinterpret_cast<char *>(u_data.m_sqlCharSmall);
+ laddr = 0;
+ } else {
+#if NDB_VERSION_MAJOR >= 3
+ length = sqlType.length();
+ if (2 + length > SqlField_CharSmall)
+ value = reinterpret_cast<char *>(u_data.m_sqlChar + 2);
+ else
+ value = reinterpret_cast<char *>(u_data.m_sqlCharSmall + 2);
+ laddr = (SqlChar*)value - 2;
+#else
+ length = sqlType.length();
+ if (length + 2 > SqlField_CharSmall)
+ value = reinterpret_cast<char *>(u_data.m_sqlChar);
+ else
+ value = reinterpret_cast<char *>(u_data.m_sqlCharSmall);
+ laddr = (SqlChar*)value + length;
+#endif
+ }
+ if (extType.type() == ExtType::Binary) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ int* off = 0;
+ if (extField.m_pos >= 0)
+ off = &extField.m_pos;
+ if (! copyin_binary_binary(ctx, value, length, dataPtr, indPtr, off, laddr, fieldId))
+ return;
+ sqlNull(false);
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Smallint) {
+ SqlSmallint value;
+ if (extType.type() == ExtType::Char) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ SqlBigint x;
+ if (! copyin_signed_char(ctx, &x, dataPtr, fieldId))
+ return;
+ value = x;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ value = (SqlSmallint)*dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ value = (SqlSmallint)*dataPtr;
+ sqlSmallint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Integer) {
+ SqlInteger value;
+ if (extType.type() == ExtType::Char) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ SqlBigint x;
+ if (! copyin_signed_char(ctx, &x, dataPtr, fieldId))
+ return;
+ value = x;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ value = (SqlInteger)*dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ value = (SqlInteger)*dataPtr;
+ sqlInteger(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Bigint) {
+ SqlBigint value;
+ if (extType.type() == ExtType::Char) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ SqlBigint x;
+ if (! copyin_signed_char(ctx, &x, dataPtr, fieldId))
+ return;
+ value = x;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ value = (SqlBigint)*dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ value = (SqlBigint)*dataPtr;
+ sqlBigint(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Real) {
+ SqlReal value;
+ if (extType.type() == ExtType::Char) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ SqlDouble x;
+ if (! copyin_double_char(ctx, &x, dataPtr, fieldId))
+ return;
+ value = x;
+ sqlReal(x);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlReal(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Double) {
+ SqlDouble value;
+ if (extType.type() == ExtType::Char) {
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ SqlDouble x;
+ if (! copyin_double_char(ctx, &x, dataPtr, fieldId))
+ return;
+ value = x;
+ sqlDouble(x);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ value = *dataPtr;
+ sqlDouble(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Datetime) {
+ SqlDatetime value;
+ if (extType.type() == ExtType::Char) {
+ // XXX replace sscanf by manual scan or regex
+ const char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ int cc = 0;
+ unsigned yy = 0, mm = 0, dd = 0, HH = 0, MM = 0, SS = 0, ff = 0;
+ bool setdate = false;
+ char dummy[10];
+ if (sscanf(dataPtr, "%2d%2u-%2u-%2u %2u:%2u:%2u.%4u%1s", &cc, &yy, &mm, &dd, &HH, &MM, &SS, &ff, dummy) == 8) {
+ ;
+ } else if (sscanf(dataPtr, "%2d%2u-%2u-%2u %2u:%2u:%2u%1s", &cc, &yy, &mm, &dd, &HH, &MM, &SS, dummy) == 7) {
+ ;
+ } else if (sscanf(dataPtr, "%2d%2u-%2u-%2u%1s", &cc, &yy, &mm, &dd, dummy) == 4) {
+ ;
+ } else if (sscanf(dataPtr, "%2u:%2u:%2u.%4u%1s", &HH, &MM, &SS, &ff, dummy) == 4) {
+ setdate = true;
+ } else if (sscanf(dataPtr, "%2u:%2u:%2u%1s", &HH, &MM, &SS, dummy) == 3) {
+ setdate = true;
+ } else {
+ ctx.pushStatus(Sqlstate::_22008, Error::Gen, "invalid timestamp format '%s'", dataPtr);
+ return;
+ }
+ if (setdate) {
+ time_t clock = time(0);
+ struct tm* t = localtime(&clock);
+ cc = (1900 + t->tm_year) / 100;
+ yy = (1900 + t->tm_year) % 100;
+ mm = 1 + t->tm_mon;
+ dd = t->tm_mday;
+ }
+ value.cc(cc);
+ value.yy(yy);
+ value.mm(mm);
+ value.dd(dd);
+ value.HH(HH);
+ value.MM(MM);
+ value.SS(SS);
+ value.ff(ff);
+ // XXX write date routines later
+ if (! value.valid()) {
+ ctx.pushStatus(Sqlstate::_22008, Error::Gen, "invalid timestamp values '%s'", dataPtr);
+ return;
+ }
+ sqlDatetime(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Timestamp) {
+ SQL_TIMESTAMP_STRUCT* dataPtr = static_cast<SQL_TIMESTAMP_STRUCT*>(extField.m_dataPtr);
+ // XXX assume same datatype
+ value.cc(dataPtr->year / 100);
+ value.yy(dataPtr->year / 100);
+ value.mm(dataPtr->month);
+ value.dd(dataPtr->day);
+ value.HH(dataPtr->hour);
+ value.MM(dataPtr->minute);
+ value.SS(dataPtr->second);
+ value.ff(dataPtr->fraction);
+ if (! value.valid()) {
+ ctx.pushStatus(Sqlstate::_22008, Error::Gen, "invalid timestamp struct");
+ return;
+ }
+ sqlDatetime(value);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ ctx_assert(false); // SqlType::Null not applicable
+}
+
+// copy to external
+
+static bool
+copyout_char_char(Ctx& ctx, const char* value, unsigned n, char* ptr, unsigned len, SQLINTEGER* ind, int* off)
+{
+ unsigned n2 = n;
+ if (off != 0 && *off >= 0) {
+ ctx_assert((unsigned)*off <= n2);
+ value += *off;
+ n2 -= *off;
+ if (len < n2 + 1) {
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "more data at offset %d, current fetch %u, available %u", *off, len, n2);
+ n2 = len - 1;
+ }
+ } else {
+ if (len < n + 1) { // room for null byte
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "char value '%.*s' overflow (%u < %u)", (int)n, value, (unsigned)len, (unsigned)(len + 1));
+ return false;
+ }
+ }
+ memcpy(ptr, value, n2);
+ ptr[n2] = 0;
+ if (off != 0 && *off >= 0) {
+ if (ind != 0)
+ *ind = n - *off;
+ *off += n2;
+ } else {
+ if (ind != 0)
+ *ind = n;
+ }
+ return true;
+}
+
+static bool
+copyout_binary_binary(Ctx& ctx, const char* value, unsigned n, char* ptr, unsigned len, SQLINTEGER* ind, int* off)
+{
+ unsigned n2 = n;
+ if (off != 0 && *off >= 0) {
+ ctx_assert((unsigned)*off <= n2);
+ value += *off;
+ n2 -= *off;
+ if (len < n2 + 1) {
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "more data at offset %d, current fetch %u, available %u", *off, len, n2);
+ n2 = len - 1;
+ }
+ } else {
+ if (len < n) { // no room for null byte
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "binary value '%.*s' overflow (%u < %u)", (int)n, value, (unsigned)len, (unsigned)n);
+ return false;
+ }
+ }
+ memcpy(ptr, value, n2);
+ ptr[n2] = 0;
+ if (off != 0 && *off >= 0) {
+ if (ind != 0)
+ *ind = n - *off;
+ *off += n2;
+ } else {
+ if (ind != 0)
+ *ind = n;
+ }
+ return true;
+}
+
+static bool
+copyout_char_signed(Ctx& ctx, const char* value, unsigned n, long* ptr)
+{
+ while (n > 0 && value[0] == 0x20) {
+ value++;
+ n--;
+ }
+ char buf[200];
+ if (n >= 200)
+ n = 200 - 1;
+ memcpy(buf, value, n);
+ buf[n] = 0;
+ errno = 0;
+ char* endptr = 0;
+ long x = strtol(buf, &endptr, 10);
+ if (endptr == 0 || *endptr != 0) {
+ errno = 0;
+ endptr = 0;
+ double y = strtod(buf, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "value %s not numeric", buf);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ // XXX should handle 123.000
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "value %s truncated", buf);
+ x = static_cast<long>(y);
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ *ptr = x;
+ return true;
+}
+
+static bool
+copyout_char_bigsigned(Ctx& ctx, const char* value, unsigned n, SQLBIGINT* ptr)
+{
+ while (n > 0 && value[0] == 0x20) {
+ value++;
+ n--;
+ }
+ char buf[200];
+ if (n >= 200)
+ n = 200 - 1;
+ memcpy(buf, value, n);
+ buf[n] = 0;
+ errno = 0;
+ char* endptr = 0;
+ SQLBIGINT x = strtoll(buf, &endptr, 10);
+ if (endptr == 0 || *endptr != 0) {
+ errno = 0;
+ endptr = 0;
+ double y = strtod(buf, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "value %s not numeric", buf);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ // XXX should handle 123.000
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "value %s truncated", buf);
+ x = static_cast<long>(y);
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ *ptr = x;
+ return true;
+}
+
+static bool
+copyout_char_unsigned(Ctx& ctx, const char* value, unsigned n, unsigned long* ptr)
+{
+ while (n > 0 && value[0] == 0x20) {
+ value++;
+ n--;
+ }
+ char buf[200];
+ if (n >= 200)
+ n = 200 - 1;
+ memcpy(buf, value, n);
+ buf[n] = 0;
+ errno = 0;
+ char* endptr = 0;
+ unsigned long x = strtoul(buf, &endptr, 10);
+ if (endptr == 0 || *endptr != 0) {
+ errno = 0;
+ endptr = 0;
+ double y = strtod(buf, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "value %s not numeric", buf);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ // XXX should handle 123.000
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "value %s truncated", buf);
+ x = static_cast<unsigned long>(y);
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ *ptr = x;
+ return true;
+}
+
+static bool
+copyout_char_bigunsigned(Ctx& ctx, const char* value, unsigned n, SQLUBIGINT* ptr)
+{
+ while (n > 0 && value[0] == 0x20) {
+ value++;
+ n--;
+ }
+ char buf[200];
+ if (n >= 200)
+ n = 200 - 1;
+ memcpy(buf, value, n);
+ buf[n] = 0;
+ errno = 0;
+ char* endptr = 0;
+ SQLUBIGINT x = strtoull(buf, &endptr, 10);
+ if (endptr == 0 || *endptr != 0) {
+ errno = 0;
+ endptr = 0;
+ double y = strtod(buf, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "value %s not numeric", buf);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ // XXX should handle 123.000
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "value %s truncated", buf);
+ x = static_cast<unsigned long>(y);
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ *ptr = x;
+ return true;
+}
+
+static bool
+copyout_char_double(Ctx& ctx, const char* value, unsigned n, double* ptr)
+{
+ while (n > 0 && value[0] == 0x20) {
+ value++;
+ n--;
+ }
+ char buf[200];
+ if (n >= 200)
+ n = 200 - 1;
+ memcpy(buf, value, n);
+ buf[n] = 0;
+ errno = 0;
+ char* endptr = 0;
+ double x = strtod(value, &endptr);
+ if (endptr == 0 || *endptr != 0) {
+ ctx.pushStatus(Sqlstate::_22005, Error::Gen, "value %s not numeric", value);
+ return false;
+ } else if (errno != 0) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", value);
+ return false;
+ }
+ *ptr = x;
+ return true;
+}
+
+static bool
+copyout_signed_char(Ctx& ctx, Int64 value, char* ptr, int len, SQLINTEGER* ind)
+{
+ char buf[100];
+ sprintf(buf, FMT_I64, value);
+ unsigned n = strlen(buf);
+ if (len <= 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid output buffer length %d", len);
+ return false;
+ }
+ if ((unsigned)len < n + 1) { // room for null byte
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ strcpy(ptr, buf);
+ if (ind != 0)
+ *ind = n;
+ return true;
+}
+
+static bool
+copyout_unsigned_char(Ctx& ctx, Uint64 uvalue, char* ptr, int len, SQLINTEGER* ind)
+{
+ char buf[100];
+ sprintf(buf, FMT_U64, uvalue);
+ unsigned n = strlen(buf);
+ if (len <= 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid output buffer length %d", len);
+ return false;
+ }
+ if ((unsigned)len < n + 1) { // room for null byte
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ strcpy(ptr, buf);
+ if (ind != 0)
+ *ind = n;
+ return true;
+}
+
+static bool
+copyout_double_char(Ctx& ctx, double value, unsigned prec, char* ptr, int len, SQLINTEGER* ind)
+{
+ char buf[100];
+ sprintf(buf, "%.*f", (int)prec, value);
+ char* p = buf + strlen(buf);
+ while (p > buf + prec)
+ *--p = 0;
+ while (p > buf && *(p - 1) == '0')
+ *--p = 0;
+ if (p > buf && *(p - 1) == '.') {
+ *p++ = '0';
+ *p = 0;
+ }
+ unsigned n = strlen(buf);
+ if (len <= 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid output buffer length %d", len);
+ return false;
+ }
+ if ((unsigned)len < n + 1) { // room for null byte
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", buf);
+ return false;
+ }
+ strcpy(ptr, buf);
+ if (ind != 0)
+ *ind = n;
+ return true;
+}
+
+void
+SqlField::copyout(Ctx& ctx, ExtField& extField) const
+{
+ if (extField.extSpec().extType().type() == ExtType::Unbound) {
+ return; // output buffer may be unbound
+ }
+ if (sqlSpec().store() == SqlSpec::Reference) {
+ ctx_assert(u_data.m_sqlField != 0);
+ u_data.m_sqlField->copyout(ctx, extField);
+ return;
+ }
+ SQLINTEGER* indPtr = extField.m_indPtr;
+ if (u_null.m_nullFlag) {
+ if (extField.m_pos > 0) { // second time from SQLGetData
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ if (indPtr == 0) {
+ ctx.pushStatus(Sqlstate::_22002, Error::Gen, "indicator variable required");
+ return;
+ }
+ *indPtr = SQL_NULL_DATA;
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ const SqlType& sqlType = sqlSpec().sqlType();
+ const ExtType& extType = extField.extSpec().extType();
+ if (sqlType.type() == SqlType::Char || sqlType.type() == SqlType::Varchar) {
+ unsigned n = 0;
+ const char* value = 0;
+ if (sqlType.type() == SqlType::Char) {
+ n = sqlType.length();
+ value = reinterpret_cast<const char*>(sqlChar());
+ } else {
+ value = reinterpret_cast<const char*>(sqlVarchar(&n));
+ }
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (extField.m_dataLen <= 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid output buffer length %d", (int)extField.m_dataLen);
+ return;
+ }
+ int* off = 0;
+ if (extField.m_pos >= 0) {
+ off = &extField.m_pos;
+ if ((unsigned)*off >= n) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ }
+ if (! copyout_char_char(ctx, value, n, dataPtr, extField.m_dataLen, indPtr, off))
+ return;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ long x;
+ if (! copyout_char_signed(ctx, value, n, &x))
+ return;
+ if (x < SHRT_MIN || x > SHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", value);
+ return;
+ }
+ *dataPtr = static_cast<short>(x);
+ if (indPtr != 0)
+ *indPtr = sizeof(short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ unsigned long x;
+ if (! copyout_char_unsigned(ctx, value, n, &x))
+ return;
+ if (x > USHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", value);
+ return;
+ }
+ *dataPtr = static_cast<unsigned short>(x);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ if (! copyout_char_signed(ctx, value, n, dataPtr))
+ return;
+ if (indPtr != 0)
+ *indPtr = sizeof(long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ if (! copyout_char_unsigned(ctx, value, n, dataPtr))
+ return;
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ if (! copyout_char_bigsigned(ctx, value, n, dataPtr))
+ return;
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ if (! copyout_char_bigunsigned(ctx, value, n, dataPtr))
+ return;
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLUBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ double x;
+ if (! copyout_char_double(ctx, value, n, &x))
+ return;
+ if (fabs(x) < FLT_MIN || fabs(x) > FLT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %s overflow", value);
+ return;
+ }
+ *dataPtr = static_cast<float>(x);
+ if (indPtr != 0)
+ *indPtr = sizeof(float);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ double x;
+ if (! copyout_char_double(ctx, value, n, &x))
+ return;
+ *dataPtr = static_cast<double>(x);
+ if (indPtr != 0)
+ *indPtr = sizeof(double);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Binary || sqlType.type() == SqlType::Varbinary) {
+ unsigned n = 0;
+ const char* value = 0;
+ if (sqlType.type() == SqlType::Binary) {
+ n = sqlType.length();
+ value = reinterpret_cast<const char*>(sqlBinary());
+ } else {
+ value = reinterpret_cast<const char*>(sqlVarbinary(&n));
+ }
+ if (extType.type() == ExtType::Binary) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (extField.m_dataLen <= 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid output buffer length %d", (int)extField.m_dataLen);
+ return;
+ }
+ int* off = 0;
+ if (extField.m_pos >= 0) {
+ off = &extField.m_pos;
+ if ((unsigned)*off >= n) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ }
+ if (! copyout_binary_binary(ctx, value, n, dataPtr, extField.m_dataLen, indPtr, off))
+ return;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Smallint) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ const SqlSmallint value = sqlSmallint();
+ const SqlUsmallint uvalue = value;
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (! sqlType.unSigned()) {
+ if (! copyout_signed_char(ctx, value, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ } else {
+ if (! copyout_unsigned_char(ctx, uvalue, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ }
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ *dataPtr = static_cast<short>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ *dataPtr = static_cast<unsigned short>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<long>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<unsigned long>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLBIGINT>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLUBIGINT>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLUBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ *dataPtr = static_cast<float>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(float);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ *dataPtr = static_cast<double>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(double);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Integer) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ const SqlInteger value = sqlInteger();
+ const SqlUinteger uvalue = value;
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (! sqlType.unSigned()) {
+ if (! copyout_signed_char(ctx, value, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ } else {
+ if (! copyout_unsigned_char(ctx, uvalue, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ }
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ if (value < SHRT_MIN || value > SHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %d overflow", (int)value);
+ return;
+ }
+ *dataPtr = static_cast<short>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ if (uvalue > USHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %u overflow", uvalue);
+ return;
+ }
+ *dataPtr = static_cast<unsigned short>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<long>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<unsigned long>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLBIGINT>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLUBIGINT>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLUBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ *dataPtr = static_cast<float>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(float);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ *dataPtr = static_cast<double>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(double);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Bigint) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ const SqlBigint value = sqlBigint();
+ const SqlUbigint uvalue = value;
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (! sqlType.unSigned()) {
+ if (! copyout_signed_char(ctx, value, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ } else {
+ if (! copyout_unsigned_char(ctx, uvalue, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ }
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ if (value < SHRT_MIN || value > SHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value " FMT_I64 " overflow", (Int64)value);
+ return;
+ }
+ *dataPtr = static_cast<short>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ if (uvalue > USHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value " FMT_U64 " overflow", (Uint64)uvalue);
+ return;
+ }
+ *dataPtr = static_cast<short>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ if (value < INT_MIN || value > INT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value " FMT_I64 " overflow", (Int64)value);
+ return;
+ }
+ *dataPtr = static_cast<long>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ if (uvalue > UINT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value " FMT_U64 " overflow", (Uint64)uvalue);
+ return;
+ }
+ *dataPtr = static_cast<unsigned long>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLBIGINT>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLUBIGINT>(uvalue);
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLUBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ *dataPtr = static_cast<float>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(float);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ *dataPtr = static_cast<double>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(double);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Real) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ const SqlReal value = sqlReal();
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (! copyout_double_char(ctx, value, 7, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ *dataPtr = static_cast<short>(value); // XXX todo
+ if (indPtr != 0)
+ *indPtr = sizeof(short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ if (value < 0 || value > USHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %g overflow", (double)value);
+ return;
+ }
+ *dataPtr = static_cast<short>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<long>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<unsigned long>(value); // XXX todo
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLBIGINT>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLUBIGINT>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLUBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ *dataPtr = static_cast<float>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(float);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ *dataPtr = static_cast<double>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(double);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Double) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ SqlDouble value = sqlDouble();
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ if (! copyout_double_char(ctx, value, 14, dataPtr, extField.m_dataLen, indPtr))
+ return;
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Short || extType.type() == ExtType::Sshort) {
+ short* dataPtr = static_cast<short*>(extField.m_dataPtr);
+ *dataPtr = static_cast<short>(value); // XXX todo
+ if (indPtr != 0)
+ *indPtr = sizeof(short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ushort) {
+ unsigned short* dataPtr = static_cast<unsigned short*>(extField.m_dataPtr);
+ if (value < 0 || value > USHRT_MAX) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "value %g overflow", (double)value);
+ return;
+ }
+ *dataPtr = static_cast<short>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned short);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Long || extType.type() == ExtType::Slong) {
+ long* dataPtr = static_cast<long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<long>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ulong) {
+ unsigned long* dataPtr = static_cast<unsigned long*>(extField.m_dataPtr);
+ *dataPtr = static_cast<unsigned long>(value); // XXX todo
+ if (indPtr != 0)
+ *indPtr = sizeof(unsigned long);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Sbigint) {
+ SQLBIGINT* dataPtr = static_cast<SQLBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLBIGINT>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Ubigint) {
+ SQLUBIGINT* dataPtr = static_cast<SQLUBIGINT*>(extField.m_dataPtr);
+ *dataPtr = static_cast<SQLUBIGINT>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(SQLUBIGINT);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Float) {
+ float* dataPtr = static_cast<float*>(extField.m_dataPtr);
+ *dataPtr = static_cast<float>(value); // big enough
+ if (indPtr != 0)
+ *indPtr = sizeof(float);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Double) {
+ double* dataPtr = static_cast<double*>(extField.m_dataPtr);
+ *dataPtr = static_cast<double>(value);
+ if (indPtr != 0)
+ *indPtr = sizeof(double);
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ }
+ if (sqlType.type() == SqlType::Datetime) {
+ if (extField.m_pos > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ SqlDatetime value = sqlDatetime();
+ if (extType.type() == ExtType::Char) {
+ char* dataPtr = static_cast<char*>(extField.m_dataPtr);
+ char buf[100];
+ sprintf(buf, "%02d%02u-%02u-%02u\040%02u:%02u:%02u.%09u", value.cc(), value.yy(), value.mm(), value.dd(), value.HH(), value.MM(), value.SS(), value.ff());
+ int n = strlen(buf);
+ if (extField.m_dataLen < 20) {
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "buffer too small for timestamp %s", buf);
+ return;
+ }
+ if (extField.m_dataLen < n) {
+ ctx.pushStatus(Sqlstate::_01004, Error::Gen, "truncating fractional part of timestamp %s", buf);
+ n = extField.m_dataLen;
+ }
+ if (! copyout_char_char(ctx, buf, n, dataPtr, extField.m_dataLen, indPtr, 0))
+ return;
+ if (extField.m_pos >= 0)
+ extField.m_pos = 1;
+ return;
+ }
+ if (extType.type() == ExtType::Timestamp) {
+ SQL_TIMESTAMP_STRUCT* dataPtr = static_cast<SQL_TIMESTAMP_STRUCT*>(extField.m_dataPtr);
+ // XXX assume same datatype
+ dataPtr->year = value.cc() * 100 + value.yy();
+ dataPtr->month = value.mm();
+ dataPtr->day = value.dd();
+ dataPtr->hour = value.HH();
+ dataPtr->minute = value.MM();
+ dataPtr->second = value.SS();
+ dataPtr->fraction = value.ff();
+ return;
+ }
+ }
+ ctx_assert(false); // SqlType::Null not applicable
+}
+
+void
+SqlField::print(char* buf, unsigned size) const
+{
+ Ctx ctx;
+ unsigned n = sqlSpec().sqlType().displaySize();
+ SQLINTEGER ind = 0;
+ ExtType extType(ExtType::Char);
+ ExtSpec extSpec(extType);
+ ExtField extField(extSpec, (SQLPOINTER)buf, size, &ind);
+ buf[0] = 0;
+ copyout(ctx, extField);
+ if (ind == SQL_NULL_DATA)
+ snprintf(buf, size, "NULL");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/DataField.hpp b/storage/ndb/src/old_files/client/odbc/common/DataField.hpp
new file mode 100644
index 00000000000..65138df25f1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DataField.hpp
@@ -0,0 +1,446 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_DataField_hpp
+#define ODBC_COMMON_DataField_hpp
+
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include "DataType.hpp"
+
+/**
+ * @class SqlSpec
+ * @brief Specification of data in SQL format
+ */
+class SqlSpec {
+public:
+ enum Store {
+ Undef = 0,
+ Reference = 1, // reference to read-only SqlField of same type
+ Physical = 2 // stored within or in allocated storage
+ };
+ SqlSpec();
+ SqlSpec(const SqlType& sqlType, Store store);
+ SqlSpec(const SqlSpec& sqlSpec);
+ SqlSpec(const SqlSpec& sqlSpec, Store store);
+ const SqlType& sqlType() const;
+ const Store store() const;
+ unsigned size() const;
+private:
+ //SqlSpec& operator=(const SqlSpec& sqlSpec); // disallowed
+ SqlType m_sqlType;
+ Store m_store;
+};
+
+/**
+ * @class ExtSpec
+ * @brief Specification of data in external format
+ */
+class ExtSpec {
+public:
+ ExtSpec();
+ ExtSpec(const ExtType& extType);
+ ExtSpec(const ExtSpec& extSpec);
+ const ExtType& extType() const;
+ unsigned size() const;
+ void setValue(const ExtType& extType);
+private:
+ ExtType m_extType;
+};
+
+/**
+ * @class LexSpec
+ * @brief Specification of lexical data
+ *
+ * Used only for converting lexical data to SQL data.
+ */
+class LexSpec {
+public:
+ LexSpec();
+ LexSpec(const LexType& lexType);
+ /**
+ * Lexical data is represented as string. Following
+ * converts it to SQL data.
+ */
+ void convert(Ctx& ctx, const BaseString& value, class SqlField& out);
+private:
+ LexType m_lexType;
+};
+
+// SqlSpec
+
+inline
+SqlSpec::SqlSpec() :
+ m_store(Undef)
+{
+}
+
+inline
+SqlSpec::SqlSpec(const SqlType& sqlType, Store store) :
+ m_sqlType(sqlType),
+ m_store(store)
+{
+}
+
+inline
+SqlSpec::SqlSpec(const SqlSpec& sqlSpec) :
+ m_sqlType(sqlSpec.m_sqlType),
+ m_store(sqlSpec.m_store)
+{
+}
+
+inline
+SqlSpec::SqlSpec(const SqlSpec& sqlSpec, Store store) :
+ m_sqlType(sqlSpec.m_sqlType),
+ m_store(store)
+{
+}
+
+inline const SqlType&
+SqlSpec::sqlType() const
+{
+ return m_sqlType;
+}
+
+inline const SqlSpec::Store
+SqlSpec::store() const
+{
+ return m_store;
+}
+
+inline unsigned
+SqlSpec::size() const
+{
+ return sqlType().size();
+}
+
+// ExtSpec
+
+inline
+ExtSpec::ExtSpec()
+{
+}
+
+inline
+ExtSpec::ExtSpec(const ExtType& extType) :
+ m_extType(extType)
+{
+}
+
+inline
+ExtSpec::ExtSpec(const ExtSpec& extSpec) :
+ m_extType(extSpec.m_extType)
+{
+}
+
+inline const ExtType&
+ExtSpec::extType() const
+{
+ return m_extType;
+}
+
+inline unsigned
+ExtSpec::size() const
+{
+ return m_extType.size();
+}
+
+inline void
+ExtSpec::setValue(const ExtType& extType)
+{
+ m_extType = extType;
+}
+
+// LexSpec
+
+inline
+LexSpec::LexSpec(const LexType& lexType) :
+ m_lexType(lexType)
+{
+}
+
+/**
+ * @class SqlField
+ * @brief Sql data field accessor
+ */
+class SqlField {
+public:
+ SqlField();
+ SqlField(const SqlSpec& sqlSpec);
+ SqlField(const SqlSpec& sqlSpec, const SqlField* sqlField);
+ SqlField(const SqlField& sqlField);
+ ~SqlField();
+ const SqlSpec& sqlSpec() const;
+ const void* addr() const; // address of data
+ void* addr();
+ unsigned allocSize() const;
+ const SqlChar* sqlChar() const; // get
+ const SqlChar* sqlVarchar(unsigned* length) const;
+ const SqlChar* sqlBinary() const;
+ const SqlChar* sqlVarbinary(unsigned* length) const;
+ SqlSmallint sqlSmallint() const;
+ SqlInteger sqlInteger() const;
+ SqlBigint sqlBigint() const;
+ SqlReal sqlReal() const;
+ SqlDouble sqlDouble() const;
+ SqlDatetime sqlDatetime() const;
+ void sqlChar(const char* value, int length); // set
+ void sqlChar(const SqlChar* value, int length);
+ void sqlVarchar(const char* value, int length);
+ void sqlVarchar(const SqlChar* value, int length);
+ void sqlBinary(const char* value, int length);
+ void sqlBinary(const SqlChar* value, int length);
+ void sqlVarbinary(const char* value, int length);
+ void sqlVarbinary(const SqlChar* value, int length);
+ void sqlSmallint(SqlSmallint value);
+ void sqlInteger(SqlInteger value);
+ void sqlBigint(SqlBigint value);
+ void sqlReal(SqlReal value);
+ void sqlDouble(SqlDouble value);
+ void sqlDatetime(SqlDatetime value);
+ bool sqlNull() const; // get and set null
+ void sqlNull(bool value);
+ unsigned trim() const; // right trimmed length
+ void copy(Ctx& ctx, SqlField& out) const;
+ bool cast(Ctx& ctx, SqlField& out) const;
+ bool less(const SqlField& sqlField) const;
+ // application input and output
+ void copyin(Ctx& ctx, class ExtField& extField);
+ void copyout(Ctx& ctx, class ExtField& extField) const;
+ // print for debugging
+ void print(char* buf, unsigned size) const;
+ // public for forte6
+ //enum { CharSmall = 20 };
+#define SqlField_CharSmall 20 // redhat-6.2 (egcs-2.91.66)
+private:
+ friend class LexSpec;
+ friend class SqlRow;
+ void alloc(); // allocate Physical
+ void alloc(const SqlField& sqlField);
+ void free(); // free Physical
+ SqlSpec m_sqlSpec;
+ union Data {
+ Data();
+ Data(const SqlField* sqlField);
+ const SqlField* m_sqlField;
+ // Physical
+ SqlChar* m_sqlChar; // all char types
+ SqlChar m_sqlCharSmall[SqlField_CharSmall];
+ SqlSmallint m_sqlSmallint;
+ SqlInteger m_sqlInteger;
+ SqlBigint m_sqlBigint;
+ SqlReal m_sqlReal;
+ SqlDouble m_sqlDouble;
+ SqlDatetime m_sqlDatetime;
+ } u_data;
+ union Null {
+ Null();
+ bool m_nullFlag;
+ } u_null;
+};
+
+/**
+ * @class ExtField
+ * @brief External data field accessor
+ */
+class ExtField {
+public:
+ ExtField();
+ ExtField(const ExtSpec& extSpec, int fieldId = 0);
+ ExtField(const ExtSpec& extSpec, SQLPOINTER dataPtr, SQLINTEGER dataLen, SQLINTEGER* indPtr, int fieldId = 0);
+ ~ExtField();
+ const ExtSpec& extSpec() const;
+ void setValue(SQLPOINTER dataPtr, SQLINTEGER dataLen);
+ void setValue(const ExtSpec& extSpec, SQLPOINTER dataPtr, SQLINTEGER dataLen, SQLINTEGER* indPtr);
+ int fieldId() const;
+ void setPos(int pos);
+ int getPos() const;
+private:
+ friend class SqlField;
+ friend class Exec_root;
+ ExtSpec m_extSpec;
+ SQLPOINTER m_dataPtr; // data buffer
+ SQLINTEGER m_dataLen; // data buffer length
+ SQLINTEGER* m_indPtr; // null indicator or length
+ int m_fieldId; // field id > 0 for error messages
+ int m_pos; // output position for SQLGetData (if != -1)
+};
+
+inline int
+ExtField::fieldId() const
+{
+ return m_fieldId;
+}
+
+inline void
+ExtField::setPos(int pos)
+{
+ m_pos = pos;
+}
+
+inline int
+ExtField::getPos() const
+{
+ return m_pos;
+}
+
+// SqlField
+
+inline
+SqlField::SqlField()
+{
+}
+
+inline
+SqlField::SqlField(const SqlSpec& sqlSpec) :
+ m_sqlSpec(sqlSpec)
+{
+ if (m_sqlSpec.store() == SqlSpec::Physical)
+ alloc();
+}
+
+inline
+SqlField::SqlField(const SqlSpec& sqlSpec, const SqlField* sqlField) :
+ m_sqlSpec(sqlSpec),
+ u_data(sqlField)
+{
+ ctx_assert(m_sqlSpec.store() == SqlSpec::Reference);
+}
+
+inline
+SqlField::SqlField(const SqlField& sqlField) :
+ m_sqlSpec(sqlField.m_sqlSpec),
+ u_data(sqlField.u_data),
+ u_null(sqlField.u_null)
+{
+ if (m_sqlSpec.store() == SqlSpec::Physical)
+ alloc(sqlField);
+}
+
+inline
+SqlField::Data::Data()
+{
+}
+
+inline
+SqlField::Data::Data(const SqlField* sqlField)
+{
+ m_sqlField = sqlField;
+}
+
+inline
+SqlField::Null::Null()
+{
+}
+
+inline
+SqlField::~SqlField()
+{
+ if (m_sqlSpec.store() == SqlSpec::Physical)
+ free();
+}
+
+inline const SqlSpec&
+SqlField::sqlSpec() const
+{
+ return m_sqlSpec;
+}
+
+inline void
+SqlField::sqlChar(const char* value, int length)
+{
+ sqlChar(reinterpret_cast<const SqlChar*>(value), length);
+}
+
+inline void
+SqlField::sqlVarchar(const char* value, int length)
+{
+ sqlVarchar(reinterpret_cast<const SqlChar*>(value), length);
+}
+
+inline void
+SqlField::sqlBinary(const char* value, int length)
+{
+ sqlBinary(reinterpret_cast<const SqlChar*>(value), length);
+}
+
+inline void
+SqlField::sqlVarbinary(const char* value, int length)
+{
+ sqlVarbinary(reinterpret_cast<const SqlChar*>(value), length);
+}
+
+// ExtField
+
+inline
+ExtField::ExtField() :
+ m_dataPtr(0),
+ m_dataLen(0),
+ m_indPtr(0),
+ m_pos(-1)
+{
+}
+
+inline
+ExtField::ExtField(const ExtSpec& extSpec, int fieldId) :
+ m_extSpec(extSpec),
+ m_dataPtr(0),
+ m_dataLen(0),
+ m_indPtr(0),
+ m_fieldId(fieldId),
+ m_pos(-1)
+{
+}
+
+inline
+ExtField::ExtField(const ExtSpec& extSpec, SQLPOINTER dataPtr, SQLINTEGER dataLen, SQLINTEGER* indPtr, int fieldId) :
+ m_extSpec(extSpec),
+ m_dataPtr(dataPtr),
+ m_dataLen(dataLen),
+ m_indPtr(indPtr),
+ m_fieldId(fieldId),
+ m_pos(-1)
+{
+}
+
+inline
+ExtField::~ExtField()
+{
+}
+
+inline const ExtSpec&
+ExtField::extSpec() const
+{
+ return m_extSpec;
+}
+
+inline void
+ExtField::setValue(SQLPOINTER dataPtr, SQLINTEGER dataLen)
+{
+ m_dataPtr = dataPtr;
+ m_dataLen = dataLen;
+}
+
+inline void
+ExtField::setValue(const ExtSpec& extSpec, SQLPOINTER dataPtr, SQLINTEGER dataLen, SQLINTEGER* indPtr)
+{
+ m_extSpec.setValue(extSpec.extType());
+ m_dataPtr = dataPtr;
+ m_dataLen = dataLen;
+ m_indPtr = indPtr;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/DataRow.cpp b/storage/ndb/src/old_files/client/odbc/common/DataRow.cpp
new file mode 100644
index 00000000000..509f2673e0d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DataRow.cpp
@@ -0,0 +1,140 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "DataRow.hpp"
+
+// SqlSpecs
+
+SqlSpecs::SqlSpecs(unsigned count) :
+ m_count(count)
+{
+ m_sqlSpec = new SqlSpec[1 + count];
+}
+
+SqlSpecs::SqlSpecs(const SqlSpecs& sqlSpecs) :
+ m_count(sqlSpecs.m_count)
+{
+ m_sqlSpec = new SqlSpec[1 + m_count];
+ for (unsigned i = 1; i <= m_count; i++) {
+ void* place = static_cast<void*>(&m_sqlSpec[i]);
+ new (place) SqlSpec(sqlSpecs.m_sqlSpec[i]);
+ }
+}
+
+SqlSpecs::~SqlSpecs()
+{
+ delete[] m_sqlSpec;
+}
+
+// ExtSpecs
+
+ExtSpecs::ExtSpecs(unsigned count) :
+ m_count(count)
+{
+ m_extSpec = new ExtSpec[1 + count];
+}
+
+ExtSpecs::ExtSpecs(const ExtSpecs& extSpecs) :
+ m_count(extSpecs.m_count)
+{
+ m_extSpec = new ExtSpec[1 + m_count];
+ for (unsigned i = 1; i <= m_count; i++) {
+ void* place = static_cast<void*>(&m_extSpec[i]);
+ new (place) ExtSpec(extSpecs.m_extSpec[i]);
+ }
+}
+
+ExtSpecs::~ExtSpecs()
+{
+ delete[] m_extSpec;
+}
+
+// SqlRow
+
+SqlRow::SqlRow(const SqlSpecs& sqlSpecs) :
+ m_sqlSpecs(sqlSpecs)
+{
+ m_sqlField = new SqlField[1 + count()];
+ for (unsigned i = 1; i <= count(); i++) {
+ SqlField sqlField(m_sqlSpecs.getEntry(i));
+ setEntry(i, sqlField);
+ }
+}
+
+SqlRow::SqlRow(const SqlRow& sqlRow) :
+ m_sqlSpecs(sqlRow.m_sqlSpecs)
+{
+ m_sqlField = new SqlField[1 + count()];
+ for (unsigned i = 1; i <= count(); i++) {
+ void* place = static_cast<void*>(&m_sqlField[i]);
+ new (place) SqlField(sqlRow.getEntry(i));
+ }
+}
+
+SqlRow::~SqlRow()
+{
+ for (unsigned i = 1; i <= count(); i++) {
+ m_sqlField[i].~SqlField();
+ }
+ delete[] m_sqlField;
+}
+
+SqlRow*
+SqlRow::copy() const
+{
+ SqlRow* copyRow = new SqlRow(m_sqlSpecs);
+ for (unsigned i = 1; i <= count(); i++) {
+ const SqlField* sqlField = &m_sqlField[i];
+ while (sqlField->sqlSpec().store() == SqlSpec::Reference) {
+ sqlField = sqlField->u_data.m_sqlField;
+ }
+ copyRow->setEntry(i, *sqlField);
+ }
+ return copyRow;
+}
+
+void
+SqlRow::copyout(Ctx& ctx, class ExtRow& extRow) const
+{
+ for (unsigned i = 1; i <= count(); i++) {
+ const SqlField& sqlField = getEntry(i);
+ ExtField& extField = extRow.getEntry(i);
+ sqlField.copyout(ctx, extField);
+ }
+}
+
+// ExtRow
+
+ExtRow::ExtRow(const ExtSpecs& extSpecs) :
+ m_extSpecs(extSpecs)
+{
+ m_extField = new ExtField[1 + count()];
+}
+
+ExtRow::ExtRow(const ExtRow& extRow) :
+ m_extSpecs(extRow.m_extSpecs)
+{
+ m_extField = new ExtField[1 + count()];
+ for (unsigned i = 1; i <= count(); i++) {
+ void* place = static_cast<void*>(&m_extField[i]);
+ new (place) ExtField(extRow.getEntry(i));
+ }
+}
+
+ExtRow::~ExtRow()
+{
+ delete[] m_extField;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/DataRow.hpp b/storage/ndb/src/old_files/client/odbc/common/DataRow.hpp
new file mode 100644
index 00000000000..4a5a1e905b9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DataRow.hpp
@@ -0,0 +1,185 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_DataRow_hpp
+#define ODBC_COMMON_DataRow_hpp
+
+#include <new>
+#include <common/common.hpp>
+#include "DataField.hpp"
+
+class Ctx;
+
+/**
+ * @class SqlSpecs
+ * @brief Specification of row of SQL data
+ */
+class SqlSpecs {
+public:
+ SqlSpecs(unsigned count);
+ SqlSpecs(const SqlSpecs& sqlSpecs);
+ ~SqlSpecs();
+ unsigned count() const;
+ void setEntry(unsigned i, const SqlSpec& sqlSpec);
+ const SqlSpec& getEntry(unsigned i) const;
+private:
+ SqlSpecs& operator=(const SqlSpecs& sqlSpecs); // disallowed
+ const unsigned m_count;
+ SqlSpec* m_sqlSpec;
+};
+
+inline unsigned
+SqlSpecs::count() const
+{
+ return m_count;
+}
+
+inline void
+SqlSpecs::setEntry(unsigned i, const SqlSpec& sqlSpec)
+{
+ ctx_assert(m_sqlSpec != 0 && 1 <= i && i <= m_count);
+ void* place = static_cast<void*>(&m_sqlSpec[i]);
+ new (place) SqlSpec(sqlSpec);
+}
+
+inline const SqlSpec&
+SqlSpecs::getEntry(unsigned i) const
+{
+ ctx_assert(m_sqlSpec != 0 && 1 <= i && i <= m_count);
+ return m_sqlSpec[i];
+}
+
+/**
+ * @class ExtSpecs
+ * @brief Specification of row of external data
+ */
+class ExtSpecs {
+public:
+ ExtSpecs(unsigned count);
+ ExtSpecs(const ExtSpecs& extSpecs);
+ ~ExtSpecs();
+ unsigned count() const;
+ void setEntry(unsigned i, const ExtSpec& extSpec);
+ const ExtSpec& getEntry(unsigned i) const;
+private:
+ ExtSpecs& operator=(const ExtSpecs& extSpecs); // disallowed
+ const unsigned m_count;
+ ExtSpec* m_extSpec;
+};
+
+inline unsigned
+ExtSpecs::count() const
+{
+ return m_count;
+}
+
+inline void
+ExtSpecs::setEntry(unsigned i, const ExtSpec& extSpec)
+{
+ ctx_assert(m_extSpec != 0 && 1 <= i && i <= m_count);
+ void* place = static_cast<void*>(&m_extSpec[i]);
+ new (place) ExtSpec(extSpec);
+}
+
+inline const ExtSpec&
+ExtSpecs::getEntry(unsigned i) const
+{
+ ctx_assert(m_extSpec != 0 && 1 <= i && i <= m_count);
+ return m_extSpec[i];
+}
+
+/**
+ * @class SqlRow
+ * @brief Sql data row
+ */
+class SqlRow {
+public:
+ SqlRow(const SqlSpecs& sqlSpecs);
+ SqlRow(const SqlRow& sqlRow);
+ ~SqlRow();
+ unsigned count() const;
+ void setEntry(unsigned i, const SqlField& sqlField);
+ SqlField& getEntry(unsigned i) const;
+ SqlRow* copy() const;
+ void copyout(Ctx& ctx, class ExtRow& extRow) const;
+private:
+ SqlRow& operator=(const SqlRow& sqlRow); // disallowed
+ SqlSpecs m_sqlSpecs;
+ SqlField* m_sqlField;
+};
+
+inline unsigned
+SqlRow::count() const
+{
+ return m_sqlSpecs.count();
+}
+
+inline void
+SqlRow::setEntry(unsigned i, const SqlField& sqlField)
+{
+ ctx_assert(1 <= i && i <= count() && m_sqlField != 0);
+ m_sqlField[i].~SqlField();
+ void* place = static_cast<void*>(&m_sqlField[i]);
+ new (place) SqlField(sqlField);
+}
+
+inline SqlField&
+SqlRow::getEntry(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= count() && m_sqlField != 0);
+ return m_sqlField[i];
+}
+
+/**
+ * @class ExtRow
+ * @brief External data row
+ */
+class ExtRow {
+public:
+ ExtRow(const ExtSpecs& extSpecs);
+ ExtRow(const ExtRow& extRow);
+ ~ExtRow();
+ unsigned count() const;
+ void setEntry(unsigned i, const ExtField& extField);
+ ExtField& getEntry(unsigned i) const;
+private:
+ ExtRow& operator=(const ExtRow& extRow); // disallowed
+ ExtSpecs m_extSpecs;
+ ExtField* m_extField;
+};
+
+inline unsigned
+ExtRow::count() const
+{
+ return m_extSpecs.count();
+}
+
+inline void
+ExtRow::setEntry(unsigned i, const ExtField& extField)
+{
+ ctx_assert(1 <= i && i <= count() && m_extField != 0);
+ void* place = static_cast<void*>(&m_extField[i]);
+ new (place) ExtField(extField);
+}
+
+inline ExtField&
+ExtRow::getEntry(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= count() && m_extField != 0);
+ return m_extField[i];
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/DataType.cpp b/storage/ndb/src/old_files/client/odbc/common/DataType.cpp
new file mode 100644
index 00000000000..96f6a6e0877
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DataType.cpp
@@ -0,0 +1,551 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "DataType.hpp"
+
+// SqlType
+
+SqlType::SqlType() :
+ m_type(Undef)
+{
+}
+
+SqlType::SqlType(Type type, bool nullable)
+{
+ Ctx ctx;
+ setType(ctx, type, nullable);
+ ctx_assert(ctx.ok());
+}
+
+SqlType::SqlType(Type type, unsigned length, bool nullable)
+{
+ Ctx ctx;
+ setType(ctx, type, length, nullable);
+ ctx_assert(ctx.ok());
+}
+
+SqlType::SqlType(Type type, unsigned precision, unsigned scale, bool nullable)
+{
+ Ctx ctx;
+ setType(ctx, type, precision, scale, nullable);
+ ctx_assert(ctx.ok());
+}
+
+SqlType::SqlType(Ctx& ctx, Type type, bool nullable)
+{
+ setType(ctx, type, nullable);
+}
+
+SqlType::SqlType(Ctx& ctx, Type type, unsigned length, bool nullable)
+{
+ setType(ctx, type, length, nullable);
+}
+
+SqlType::SqlType(Ctx& ctx, Type type, unsigned precision, unsigned scale, bool nullable)
+{
+ setType(ctx, type, precision, scale, nullable);
+}
+
+SqlType::SqlType(Ctx& ctx, const NdbDictionary::Column* ndbColumn)
+{
+ setType(ctx, ndbColumn);
+}
+
+void
+SqlType::setType(Ctx& ctx, Type type, bool nullable)
+{
+ switch (type) {
+ case Smallint:
+ case Integer:
+ case Bigint:
+ case Real:
+ case Double:
+ case Datetime:
+ break;
+ case Blob:
+ setType(ctx, Varbinary, FAKE_BLOB_SIZE, nullable); // XXX BLOB hack
+ return;
+ case Clob:
+ setType(ctx, Varchar, FAKE_BLOB_SIZE, nullable); // XXX BLOB hack
+ return;
+ case Null:
+ case Unbound:
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ m_type = type;
+ m_precision = 0;
+ m_scale = 0;
+ m_length = 0;
+ m_nullable = nullable;
+ m_unSigned = false;
+}
+
+void
+SqlType::setType(Ctx& ctx, Type type, unsigned length, bool nullable)
+{
+ switch (type) {
+ case Char:
+ case Varchar:
+ case Binary:
+ case Varbinary:
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ m_type = type;
+ m_precision = 0;
+ m_scale = 0;
+ m_length = length;
+ m_nullable = nullable;
+ m_unSigned = false;
+}
+
+void
+SqlType::setType(Ctx& ctx, Type type, unsigned precision, unsigned scale, bool nullable)
+{
+ ctx_assert(false); // not yet
+}
+
+void
+SqlType::setType(Ctx& ctx, const NdbDictionary::Column* ndbColumn)
+{
+ NdbDictionary::Column::Type type = ndbColumn->getType();
+ unsigned length = ndbColumn->getLength();
+ unsigned precision = ndbColumn->getPrecision();
+ unsigned scale = ndbColumn->getScale();
+ bool nullable = ndbColumn->getNullable();
+ switch (type) {
+ case NdbDictionary::Column::Undefined:
+ break;
+ case NdbDictionary::Column::Int:
+ if (length == 1)
+ setType(ctx, Integer, nullable);
+ else
+ setType(ctx, Binary, length * sizeof(SqlInteger), nullable);
+ return;
+ case NdbDictionary::Column::Unsigned:
+ if (length == 1) {
+ setType(ctx, Integer, nullable);
+ unSigned(true);
+ } else
+ setType(ctx, Binary, length * sizeof(SqlUinteger), nullable);
+ return;
+ case NdbDictionary::Column::Bigint:
+ if (length == 1)
+ setType(ctx, Bigint, nullable);
+ else
+ setType(ctx, Binary, length * sizeof(SqlBigint), nullable);
+ return;
+ case NdbDictionary::Column::Bigunsigned:
+ if (length == 1) {
+ setType(ctx, Bigint, nullable);
+ unSigned(true);
+ } else
+ setType(ctx, Binary, length * sizeof(SqlBigint), nullable);
+ return;
+ case NdbDictionary::Column::Float:
+ if (length == 1)
+ setType(ctx, Real, nullable);
+ else
+ setType(ctx, Binary, length * sizeof(SqlReal), nullable);
+ return;
+ case NdbDictionary::Column::Double:
+ if (length == 1)
+ setType(ctx, Double, nullable);
+ else
+ setType(ctx, Binary, length * sizeof(SqlDouble), nullable);
+ return;
+ case NdbDictionary::Column::Decimal:
+ setType(ctx, Decimal, precision, scale, nullable);
+ return;
+ case NdbDictionary::Column::Char:
+ setType(ctx, Char, length, nullable);
+ return;
+ case NdbDictionary::Column::Varchar:
+ setType(ctx, Varchar, length, nullable);
+ return;
+ case NdbDictionary::Column::Binary:
+ setType(ctx, Binary, length, nullable);
+ return;
+ case NdbDictionary::Column::Varbinary:
+ setType(ctx, Varbinary, length, nullable);
+ return;
+ case NdbDictionary::Column::Datetime:
+ // XXX not yet
+ break;
+ case NdbDictionary::Column::Timespec:
+ setType(ctx, Datetime, nullable);
+ return;
+ case NdbDictionary::Column::Blob:
+ setType(ctx, Blob, nullable);
+ return;
+ case NdbDictionary::Column::Clob:
+ setType(ctx, Clob, nullable);
+ return;
+ default:
+ break;
+ }
+ ctx.pushStatus(Error::Gen, "unsupported NDB type %d", (signed)type);
+}
+
+bool
+SqlType::equal(const SqlType& sqlType) const
+{
+ return
+ m_type == sqlType.m_type &&
+ m_precision == sqlType.m_precision &&
+ m_scale == sqlType.m_scale &&
+ m_length == sqlType.m_length;
+}
+
+unsigned
+SqlType::size() const
+{
+ switch (m_type) {
+ case Char:
+ case Varchar:
+ case Binary:
+ case Varbinary:
+ return m_length;
+ case Smallint:
+ return sizeof(SqlSmallint);
+ case Integer:
+ return sizeof(SqlInteger);
+ case Bigint:
+ return sizeof(SqlBigint);
+ case Real:
+ return sizeof(SqlReal);
+ case Double:
+ return sizeof(SqlDouble);
+ case Datetime:
+ return sizeof(SqlDatetime);
+ case Null:
+ return 0;
+ default:
+ break;
+ }
+ ctx_assert(false);
+ return 0;
+}
+
+unsigned
+SqlType::displaySize() const
+{
+ switch (m_type) {
+ case Char:
+ case Varchar:
+ return m_length;
+ case Binary:
+ case Varbinary:
+ return m_length;
+ case Smallint:
+ return m_unSigned ? 5 : 6;
+ case Integer:
+ return m_unSigned ? 10 : 11;
+ case Bigint:
+ return m_unSigned ? 20 : 21;
+ case Real:
+ return 10;
+ case Double:
+ return 20;
+ case Datetime:
+ return 30;
+ case Null:
+ return 0;
+ default:
+ break;
+ }
+ ctx_assert(false);
+ return 0;
+}
+
+void
+SqlType::getType(Ctx& ctx, NdbDictionary::Column* ndbColumn) const
+{
+ switch (m_type) {
+ case Char:
+ ndbColumn->setType(NdbDictionary::Column::Char);
+ ndbColumn->setLength(m_length);
+ break;
+ case Varchar:
+ ndbColumn->setType(NdbDictionary::Column::Varchar);
+ ndbColumn->setLength(m_length);
+ break;
+ case Binary:
+ ndbColumn->setType(NdbDictionary::Column::Binary);
+ ndbColumn->setLength(m_length);
+ break;
+ case Varbinary:
+ ndbColumn->setType(NdbDictionary::Column::Varbinary);
+ ndbColumn->setLength(m_length);
+ break;
+ case Smallint:
+ break; // XXX
+ case Integer:
+ if (! m_unSigned)
+ ndbColumn->setType(NdbDictionary::Column::Int);
+ else
+ ndbColumn->setType(NdbDictionary::Column::Unsigned);
+ ndbColumn->setLength(1);
+ break;
+ case Bigint:
+ if (! m_unSigned)
+ ndbColumn->setType(NdbDictionary::Column::Bigint);
+ else
+ ndbColumn->setType(NdbDictionary::Column::Bigunsigned);
+ ndbColumn->setLength(1);
+ break;
+ case Real:
+ ndbColumn->setType(NdbDictionary::Column::Float);
+ ndbColumn->setLength(1);
+ break;
+ case Double:
+ ndbColumn->setType(NdbDictionary::Column::Double);
+ ndbColumn->setLength(1);
+ break;
+ case Datetime:
+ ndbColumn->setType(NdbDictionary::Column::Timespec);
+ ndbColumn->setLength(1);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ ndbColumn->setNullable(m_nullable);
+}
+
+const char*
+SqlType::typeName() const
+{
+ switch (m_type) {
+ case Char:
+ return "CHAR";
+ case Varchar:
+ return "VARCHAR";
+ case Binary:
+ return "BINARY";
+ case Varbinary:
+ return "VARBINARY";
+ case Smallint:
+ return "SMALLINT";
+ case Integer:
+ return "INTEGER";
+ case Bigint:
+ return "BIGINT";
+ case Real:
+ return "REAL";
+ case Double:
+ return "FLOAT";
+ case Datetime:
+ return "DATETIME";
+ default:
+ break;
+ }
+ return "UNKNOWN";
+}
+
+void
+SqlType::print(char* buf, unsigned size) const
+{
+ switch (m_type) {
+ case Char:
+ snprintf(buf, size, "char(%d)", m_length);
+ break;
+ case Varchar:
+ snprintf(buf, size, "varchar(%d)", m_length);
+ break;
+ case Binary:
+ snprintf(buf, size, "binary(%d)", m_length);
+ break;
+ case Varbinary:
+ snprintf(buf, size, "varbinary(%d)", m_length);
+ break;
+ case Smallint:
+ snprintf(buf, size, "smallint%s", m_unSigned ? " unsigned" : "");
+ break;
+ case Integer:
+ snprintf(buf, size, "integer%s", m_unSigned ? " unsigned" : "");
+ break;
+ case Bigint:
+ snprintf(buf, size, "bigint%s", m_unSigned ? " unsigned" : "");
+ break;
+ case Real:
+ snprintf(buf, size, "real");
+ break;
+ case Double:
+ snprintf(buf, size, "double");
+ break;
+ case Datetime:
+ snprintf(buf, size, "datetime");
+ break;
+ case Null:
+ snprintf(buf, size, "null");
+ break;
+ case Unbound:
+ snprintf(buf, size, "unbound");
+ break;
+ default:
+ snprintf(buf, size, "sqltype(%d)", (int)m_type);
+ break;
+ }
+}
+
+// ExtType
+
+ExtType::ExtType() :
+ m_type(Undef)
+{
+}
+
+ExtType::ExtType(Type type)
+{
+ Ctx ctx;
+ setType(ctx, type);
+ ctx_assert(ctx.ok());
+}
+
+ExtType::ExtType(Ctx& ctx, Type type)
+{
+ setType(ctx, type);
+}
+
+void
+ExtType::setType(Ctx& ctx, Type type)
+{
+ switch (type) {
+ case Char:
+ case Short:
+ case Sshort:
+ case Ushort:
+ case Long:
+ case Slong:
+ case Ulong:
+ case Sbigint:
+ case Ubigint:
+ case Float:
+ case Double:
+ case Timestamp:
+ case Binary: // XXX BLOB hack
+ case Unbound:
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "unsupported external type %d", (int)type);
+ return;
+ }
+ m_type = type;
+}
+
+unsigned
+ExtType::size() const
+{
+ ctx_assert(false);
+ return 0;
+}
+
+// LexType
+
+LexType::LexType() :
+ m_type(Undef)
+{
+}
+
+LexType::LexType(Type type)
+{
+ Ctx ctx;
+ setType(ctx, type);
+ ctx_assert(ctx.ok());
+}
+
+LexType::LexType(Ctx& ctx, Type type)
+{
+ setType(ctx, type);
+}
+
+void
+LexType::setType(Ctx& ctx, Type type)
+{
+ switch (type) {
+ case Char:
+ case Integer:
+ case Float:
+ case Null:
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ m_type = type;
+}
+
+// convert types
+
+SQLSMALLINT
+SqlType::sqlcdefault(Ctx& ctx) const
+{
+ switch (m_type) {
+ case Char:
+ return SQL_C_CHAR;
+ case Varchar:
+ return SQL_C_CHAR;
+ case Binary:
+ return SQL_C_BINARY;
+ case Varbinary:
+ return SQL_C_BINARY;
+ case Smallint:
+ return m_unSigned ? SQL_C_USHORT : SQL_C_SSHORT;
+ case Integer:
+ return m_unSigned ? SQL_C_ULONG : SQL_C_SLONG;
+ case Bigint:
+ return SQL_C_CHAR;
+ // or maybe this
+ return m_unSigned ? SQL_C_UBIGINT : SQL_C_SBIGINT;
+ case Real:
+ return SQL_C_FLOAT;
+ case Double:
+ return SQL_C_DOUBLE;
+ case Datetime:
+ return SQL_C_TYPE_TIMESTAMP;
+ default:
+ break;
+ }
+ return SQL_C_DEFAULT; // no default
+}
+
+void
+LexType::convert(Ctx& ctx, SqlType& out, unsigned length) const
+{
+ switch (m_type) {
+ case Char:
+ out.setType(ctx, SqlType::Char, length, true);
+ return;
+ case Integer:
+ out.setType(ctx, SqlType::Bigint, false);
+ return;
+ case Float:
+ out.setType(ctx, SqlType::Double, false);
+ return;
+ case Null:
+ out.setType(ctx, SqlType::Null, true);
+ return;
+ default:
+ break;
+ }
+ ctx.pushStatus(Error::Gen, "unsupported lexical to SQL type conversion");
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/DataType.hpp b/storage/ndb/src/old_files/client/odbc/common/DataType.hpp
new file mode 100644
index 00000000000..e03e445cf05
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DataType.hpp
@@ -0,0 +1,293 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_DataType_hpp
+#define ODBC_COMMON_DataType_hpp
+
+#include <map>
+#include <ndb_types.h>
+#include <AttrType.hpp>
+#include <NdbDictionary.hpp>
+#include <common/common.hpp>
+
+/**
+ * Sql data exists in several formats:
+ *
+ * - as NDB data at the bottom
+ * - as SQL data during intermediary processing
+ * - as external data in user input and output buffers
+ * - as lexical constants in SQL statement text
+ *
+ * Each data format has specific types (e.g. number) and each
+ * type has specific attributes (e.g. precision).
+ */
+enum DataFormat {
+ Undef_format = 0,
+ Ndb_format = 1, // not used in NDB version >= v2.10
+ Sql_format = 2,
+ Ext_format = 3,
+ Lex_format = 4
+};
+
+#define UndefDataType 990
+#define NullDataType 991
+#define UnboundDataType 992
+
+class SqlType;
+class ExtType;
+class LexType;
+
+/**
+ * @class SqlType
+ * @brief Sql data type
+ */
+class SqlType {
+public:
+ enum Type {
+ Undef = UndefDataType,
+ Char = SQL_CHAR,
+ Varchar = SQL_VARCHAR,
+ Longvarchar = SQL_LONGVARCHAR,
+ Binary = SQL_BINARY,
+ Varbinary = SQL_VARBINARY,
+ Longvarbinary = SQL_LONGVARBINARY,
+ Decimal = SQL_DECIMAL,
+ Tinyint = SQL_TINYINT,
+ Smallint = SQL_SMALLINT,
+ Integer = SQL_INTEGER,
+ Bigint = SQL_BIGINT,
+ Real = SQL_REAL,
+ Double = SQL_DOUBLE,
+ Date = SQL_DATE,
+ Datetime = SQL_TYPE_TIMESTAMP,
+ Blob = SQL_BLOB,
+ Clob = SQL_CLOB,
+ Null = NullDataType, // not an ODBC SQL type
+ Unbound = UnboundDataType // special for placeholders
+ };
+ SqlType();
+ SqlType(Type type, bool nullable = true);
+ SqlType(Type type, unsigned length, bool nullable = true);
+ SqlType(Type type, unsigned precision, unsigned scale, bool nullable = true);
+ SqlType(Ctx& ctx, Type type, bool nullable = true);
+ SqlType(Ctx& ctx, Type type, unsigned length, bool nullable = true);
+ SqlType(Ctx& ctx, Type type, unsigned precision, unsigned scale, bool nullable = true);
+ SqlType(Ctx& ctx, const NdbDictionary::Column* ndbColumn);
+ Type type() const;
+ void setType(Ctx& ctx, Type type, bool nullable = true);
+ void setType(Ctx& ctx, Type type, unsigned length, bool nullable = true);
+ void setType(Ctx& ctx, Type type, unsigned precision, unsigned scale, bool nullable = true);
+ void setType(Ctx& ctx, const NdbDictionary::Column* ndbColumn);
+ bool equal(const SqlType& sqlType) const;
+ unsigned size() const;
+ unsigned displaySize() const;
+ const char* typeName() const;
+ unsigned length() const;
+ bool nullable() const;
+ void nullable(bool value);
+ bool unSigned() const;
+ void unSigned(bool value);
+ // forwards compatible
+ void getType(Ctx& ctx, NdbDictionary::Column* ndbColumn) const;
+ // type conversion
+ SQLSMALLINT sqlcdefault(Ctx& ctx) const;
+ // print for debugging
+ void print(char* buf, unsigned size) const;
+private:
+ friend class LexType;
+ Type m_type;
+ unsigned m_precision;
+ unsigned m_scale;
+ unsigned m_length;
+ bool m_nullable;
+ bool m_unSigned; // qualifier instead of separate types
+};
+
+inline SqlType::Type
+SqlType::type() const
+{
+ return m_type;
+}
+
+inline unsigned
+SqlType::length() const
+{
+ return m_length;
+}
+
+inline bool
+SqlType::nullable() const
+{
+ return m_nullable;
+}
+
+inline void
+SqlType::nullable(bool value)
+{
+ m_nullable = value;
+}
+
+inline bool
+SqlType::unSigned() const
+{
+ return m_unSigned;
+}
+
+inline void
+SqlType::unSigned(bool value)
+{
+ ctx_assert(m_type == Smallint || m_type == Integer || m_type == Bigint);
+ m_unSigned = value;
+}
+
+/**
+ * Actual SQL datatypes.
+ */
+typedef unsigned char SqlChar; // Char and Varchar via pointer
+typedef Int16 SqlSmallint;
+typedef Int32 SqlInteger;
+typedef Int64 SqlBigint;
+typedef Uint16 SqlUsmallint;
+typedef Uint32 SqlUinteger;
+typedef Uint64 SqlUbigint;
+typedef float SqlReal;
+typedef double SqlDouble;
+
+// datetime cc yy mm dd HH MM SS 00 ff ff ff ff stored as String(12)
+struct SqlDatetime {
+ int cc() const { return *(signed char*)&m_data[0]; }
+ void cc(int x) { *(signed char*)&m_data[0] = x; }
+ unsigned yy() const { return *(unsigned char*)&m_data[1]; }
+ void yy(unsigned x) { *(unsigned char*)&m_data[1] = x; }
+ unsigned mm() const { return *(unsigned char*)&m_data[2]; }
+ void mm(unsigned x) { *(unsigned char*)&m_data[2] = x; }
+ unsigned dd() const { return *(unsigned char*)&m_data[3]; }
+ void dd(unsigned x) { *(unsigned char*)&m_data[3] = x; }
+ unsigned HH() const { return *(unsigned char*)&m_data[4]; }
+ void HH(unsigned x) { *(unsigned char*)&m_data[4] = x; }
+ unsigned MM() const { return *(unsigned char*)&m_data[5]; }
+ void MM(unsigned x) { *(unsigned char*)&m_data[5] = x; }
+ unsigned SS() const { return *(unsigned char*)&m_data[6]; }
+ void SS(unsigned x) { *(unsigned char*)&m_data[6] = x; }
+ unsigned ff() const {
+ const unsigned char* p = (unsigned char*)&m_data[8];
+ unsigned x = 0;
+ x += *p++ << 24;
+ x += *p++ << 16;
+ x += *p++ << 8;
+ x += *p++;
+ return x;
+ }
+ void ff(unsigned x) {
+ unsigned char* p = (unsigned char*)&m_data[8];
+ *p++ = (x >> 24) & 0xff;
+ *p++ = (x >> 16) & 0xff;
+ *p++ = (x >> 8) & 0xff;
+ *p++ = x & 0xff;
+ }
+ bool valid() { return true; } // XXX later
+ bool less(const SqlDatetime t) const {
+ if (cc() != t.cc())
+ return cc() < t.cc();
+ if (yy() != t.yy())
+ return yy() < t.yy();
+ if (mm() != t.mm())
+ return mm() < t.mm();
+ if (dd() != t.dd())
+ return dd() < t.dd();
+ if (HH() != t.HH())
+ return HH() < t.HH();
+ if (MM() != t.MM())
+ return MM() < t.MM();
+ if (SS() != t.SS())
+ return SS() < t.SS();
+ if (ff() != t.ff())
+ return ff() < t.ff();
+ return false;
+ }
+private:
+ char m_data[12]; // use array to avoid gaps
+};
+
+/**
+ * @class ExtType
+ * @brief External data type
+ */
+class ExtType {
+public:
+ enum Type {
+ Undef = UndefDataType,
+ Char = SQL_C_CHAR,
+ Short = SQL_C_SHORT,
+ Sshort = SQL_C_SSHORT,
+ Ushort = SQL_C_USHORT,
+ Long = SQL_C_LONG, // for sun.jdbc.odbc
+ Slong = SQL_C_SLONG,
+ Ulong = SQL_C_ULONG,
+ Sbigint = SQL_C_SBIGINT,
+ Ubigint = SQL_C_UBIGINT,
+ Float = SQL_C_FLOAT,
+ Double = SQL_C_DOUBLE,
+ Timestamp = SQL_C_TYPE_TIMESTAMP,
+ Binary = SQL_C_BINARY, // XXX BLOB hack
+ Unbound = UnboundDataType
+ };
+ ExtType();
+ ExtType(Type type);
+ ExtType(Ctx& ctx, Type type);
+ Type type() const;
+ void setType(Ctx& ctx, Type type);
+ unsigned size() const;
+private:
+ Type m_type;
+};
+
+inline ExtType::Type
+ExtType::type() const
+{
+ return m_type;
+}
+
+/**
+ * @class LexType
+ * @class Lexical data type
+ */
+class LexType {
+public:
+ enum Type {
+ Undef = UndefDataType,
+ Char = 1,
+ Integer = 2,
+ Float = 3,
+ Null = 4
+ };
+ LexType();
+ LexType(Type type);
+ LexType(Ctx& ctx, Type type);
+ Type type() const;
+ void setType(Ctx& ctx, Type type);
+ void convert(Ctx& ctx, SqlType& out, unsigned length = 0) const;
+private:
+ Type m_type;
+};
+
+inline LexType::Type
+LexType::type() const
+{
+ return m_type;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/DescArea.cpp b/storage/ndb/src/old_files/client/odbc/common/DescArea.cpp
new file mode 100644
index 00000000000..bad9f23d3ef
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DescArea.cpp
@@ -0,0 +1,167 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <vector>
+#include "DescArea.hpp"
+
+// DescField
+
+// DescRec
+
+void
+DescRec::setField(int id, const OdbcData& data)
+{
+ Ctx ctx;
+ setField(ctx, id, data);
+ ctx_assert(ctx.ok());
+}
+
+void
+DescRec::getField(int id, OdbcData& data)
+{
+ Ctx ctx;
+ getField(ctx, id, data);
+ ctx_assert(ctx.ok());
+}
+
+void
+DescRec::setField(Ctx& ctx, int id, const OdbcData& data)
+{
+ Fields::iterator iter;
+ iter = m_fields.find(id);
+ if (ctx.logLevel() >= 3) {
+ char buf[100];
+ data.print(buf, sizeof(buf));
+ ctx_log3(("set %s rec %d id %d = %s", DescArea::nameUsage(m_area->getUsage()), m_num, id, buf));
+ }
+ if (iter != m_fields.end()) {
+ DescField& field = (*iter).second;
+ field.setData(data);
+ m_area->setBound(false); // XXX could compare data values
+ return;
+ }
+ const DescSpec& spec = m_area->findSpec(id);
+ if (spec.m_pos != Desc_pos_end) {
+ DescField field(spec, data);
+ m_fields.insert(Fields::value_type(id, field));
+ m_area->setBound(false);
+ return;
+ }
+ ctx_assert(false);
+}
+
+void
+DescRec::getField(Ctx& ctx, int id, OdbcData& data)
+{
+ Fields::iterator iter;
+ iter = m_fields.find(id);
+ if (iter != m_fields.end()) {
+ DescField& field = (*iter).second;
+ data.setValue(field.getData());
+ return;
+ }
+ const DescSpec& spec = m_area->findSpec(id);
+ if (spec.m_pos != Desc_pos_end) {
+ data.setValue();
+ return; // XXX default value
+ }
+ ctx_assert(false);
+}
+
+// DescArea
+
+DescArea::DescArea(HandleBase* handle, const DescSpec* specList) :
+ m_handle(handle),
+ m_specList(specList),
+ m_alloc(Desc_alloc_undef),
+ m_usage(Desc_usage_undef),
+ m_bound(true) // no bind necessary since empty
+{
+ m_header.m_area = this;
+ m_header.m_num = -1;
+ DescRec rec;
+ rec.m_area = this;
+ rec.m_num = m_recs.size();
+ m_recs.push_back(rec); // add bookmark record
+ SQLSMALLINT count = 0;
+ getHeader().setField(SQL_DESC_COUNT, count);
+ m_bound = true;
+}
+
+DescArea::~DescArea()
+{
+}
+
+const DescSpec&
+DescArea::findSpec(int id)
+{
+ const DescSpec* p;
+ for (p = m_specList; p->m_pos != Desc_pos_end; p++) {
+ if (p->m_id == id)
+ break;
+ }
+ return *p;
+}
+
+unsigned
+DescArea::getCount() const
+{
+ ctx_assert(m_recs.size() > 0);
+ return m_recs.size() - 1;
+}
+
+void
+DescArea::setCount(Ctx& ctx, unsigned count)
+{
+ if (m_recs.size() - 1 == count)
+ return;
+ ctx_log3(("set %s count %d to %d",
+ DescArea::nameUsage(m_usage),
+ (unsigned)(m_recs.size() - 1),
+ count));
+ m_recs.resize(1 + count);
+ for (unsigned i = 0; i <= count; i++) {
+ m_recs[i].m_area = this;
+ m_recs[i].m_num = i;
+ }
+ getHeader().setField(SQL_DESC_COUNT, static_cast<SQLSMALLINT>(count));
+}
+
+DescRec&
+DescArea::pushRecord()
+{
+ ctx_assert(m_recs.size() > 0);
+ DescRec rec;
+ rec.m_area = this;
+ rec.m_num = m_recs.size();
+ m_recs.push_back(rec);
+ SQLSMALLINT count = m_recs.size() - 1;
+ getHeader().setField(SQL_DESC_COUNT, count);
+ return m_recs.back();
+}
+
+DescRec&
+DescArea::getHeader()
+{
+ return m_header;
+}
+
+DescRec&
+DescArea::getRecord(unsigned num)
+{
+ ctx_assert(num < m_recs.size());
+ return m_recs[num];
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/DescArea.hpp b/storage/ndb/src/old_files/client/odbc/common/DescArea.hpp
new file mode 100644
index 00000000000..e9f552d758d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DescArea.hpp
@@ -0,0 +1,266 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_DescArea_hpp
+#define ODBC_COMMON_DescArea_hpp
+
+#include <map>
+#include <vector>
+#include <common/common.hpp>
+#include "OdbcData.hpp"
+
+/**
+ * Descriptor records. Contains:
+ * -# header, not called a "record" in this context
+ * -# bookmark record at index position 0
+ * -# descriptor records at index positions starting from 1
+ *
+ * These classes are in common/ since the code is general.
+ * However each area is associated with a HandleDesc.
+ *
+ * DescField - field identified by an SQL_DESC_* constant
+ * DescRec - header or record, a list of fields
+ * DescArea - header and all records
+ */
+
+class HandleBase;
+class DescField;
+class DescRec;
+class DescArea;
+
+enum DescPos {
+ Desc_pos_undef = 0,
+ Desc_pos_header,
+ Desc_pos_record,
+ Desc_pos_end
+};
+
+enum DescMode {
+ Desc_mode_undef = 0,
+ Desc_mode_readonly,
+ Desc_mode_writeonly,
+ Desc_mode_readwrite,
+ Desc_mode_unused
+};
+
+struct DescSpec {
+ DescPos m_pos; // header or record
+ int m_id; // SQL_DESC_ identifier
+ OdbcData::Type m_type; // data type
+ DescMode m_mode[1+4]; // access mode IPD APD IRD ARD
+ // called before setting value
+ typedef void CallbackSet(Ctx& ctx, HandleBase* self, const OdbcData& data);
+ CallbackSet* m_set;
+ // called to get default value
+ typedef void CallbackDefault(Ctx& ctx, HandleBase* self, OdbcData& data);
+ CallbackDefault* m_default;
+};
+
+enum DescAlloc {
+ Desc_alloc_undef = 0,
+ Desc_alloc_auto,
+ Desc_alloc_user
+};
+
+enum DescUsage {
+ Desc_usage_undef = 0,
+ Desc_usage_IPD = 1, // these must be 1-4
+ Desc_usage_IRD = 2,
+ Desc_usage_APD = 3,
+ Desc_usage_ARD = 4
+};
+
+/**
+ * @class DescField
+ * @brief Field identified by an SQL_DESC_* constant
+ */
+class DescField {
+public:
+ DescField(const DescSpec& spec, const OdbcData& data);
+ DescField(const DescField& field);
+ ~DescField();
+private:
+ friend class DescRec;
+ void setData(const OdbcData& data);
+ const OdbcData& getData();
+ const DescSpec& m_spec;
+ OdbcData m_data;
+};
+
+inline
+DescField::DescField(const DescSpec& spec, const OdbcData& data) :
+ m_spec(spec),
+ m_data(data)
+{
+}
+
+inline
+DescField::DescField(const DescField& field) :
+ m_spec(field.m_spec),
+ m_data(field.m_data)
+{
+}
+
+inline
+DescField::~DescField()
+{
+}
+
+inline void
+DescField::setData(const OdbcData& data)
+{
+ ctx_assert(m_spec.m_type == data.type());
+ m_data.setValue(data);
+}
+
+inline const OdbcData&
+DescField::getData()
+{
+ ctx_assert(m_data.type() != OdbcData::Undef);
+ return m_data;
+}
+
+/**
+ * @class DescRec
+ * @brief Descriptor record, a list of fields
+ */
+class DescRec {
+ friend class DescArea;
+public:
+ DescRec();
+ ~DescRec();
+ void setField(int id, const OdbcData& data);
+ void getField(int id, OdbcData& data);
+ void setField(Ctx& ctx, int id, const OdbcData& data);
+ void getField(Ctx& ctx, int id, OdbcData& data);
+private:
+ DescArea* m_area;
+ int m_num; // for logging only -1 = header 0 = bookmark
+ typedef std::map<int, DescField> Fields;
+ Fields m_fields;
+};
+
+inline
+DescRec::DescRec() :
+ m_area(0)
+{
+}
+
+inline
+DescRec::~DescRec()
+{
+}
+
+/**
+ * @class DescArea
+ * @brief All records, including header (record 0)
+ *
+ * Descriptor area includes a header (record 0)
+ * and zero or more records at position >= 1.
+ * Each of these describes one parameter or one column.
+ *
+ * - DescArea : Collection of records
+ * - DescRec : Collection of fields
+ * - DescField : Contains data of type OdbcData
+ */
+class DescArea {
+public:
+ DescArea(HandleBase* handle, const DescSpec* specList);
+ ~DescArea();
+ void setAlloc(DescAlloc alloc);
+ DescAlloc getAlloc() const;
+ void setUsage(DescUsage usage);
+ DescUsage getUsage() const;
+ static const char* nameUsage(DescUsage u);
+ // find specifier
+ const DescSpec& findSpec(int id);
+ // get or set number of records (record 0 not counted)
+ unsigned getCount() const;
+ void setCount(Ctx& ctx, unsigned count);
+ // paush new record (record 0 exists always)
+ DescRec& pushRecord();
+ // get ref to header or to any record
+ DescRec& getHeader();
+ DescRec& getRecord(unsigned num);
+ // modified since last bind
+ void setBound(bool bound);
+ bool isBound() const;
+private:
+ HandleBase* m_handle;
+ const DescSpec* const m_specList;
+ DescRec m_header;
+ typedef std::vector<DescRec> Recs;
+ Recs m_recs;
+ DescAlloc m_alloc;
+ DescUsage m_usage;
+ bool m_bound;
+};
+
+inline void
+DescArea::setAlloc(DescAlloc alloc)
+{
+ m_alloc = alloc;
+}
+
+inline DescAlloc
+DescArea::getAlloc() const
+{
+ return m_alloc;
+}
+
+inline void
+DescArea::setUsage(DescUsage usage)
+{
+ m_usage = usage;
+}
+
+inline DescUsage
+DescArea::getUsage() const
+{
+ return m_usage;
+}
+
+inline const char*
+DescArea::nameUsage(DescUsage u)
+{
+ switch (u) {
+ case Desc_usage_undef:
+ break;
+ case Desc_usage_IPD:
+ return "IPD";
+ case Desc_usage_IRD:
+ return "IRD";
+ case Desc_usage_APD:
+ return "APD";
+ case Desc_usage_ARD:
+ return "ARD";
+ }
+ return "?";
+}
+
+inline void
+DescArea::setBound(bool bound)
+{
+ m_bound = bound;
+}
+
+inline bool
+DescArea::isBound() const
+{
+ return m_bound;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/DiagArea.cpp b/storage/ndb/src/old_files/client/odbc/common/DiagArea.cpp
new file mode 100644
index 00000000000..06e8da89495
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DiagArea.cpp
@@ -0,0 +1,284 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <stdio.h>
+#include "OdbcData.hpp"
+#include "DiagArea.hpp"
+
+// DiagSpec
+
+static const DiagSpec
+diag_spec_list[] = {
+ { Diag_pos_header,
+ SQL_DIAG_CURSOR_ROW_COUNT,
+ OdbcData::Integer,
+ Odbc_handle_stmt
+ },
+ { Diag_pos_header,
+ SQL_DIAG_DYNAMIC_FUNCTION,
+ OdbcData::Sqlchar,
+ Odbc_handle_stmt
+ },
+ { Diag_pos_header,
+ SQL_DIAG_DYNAMIC_FUNCTION_CODE,
+ OdbcData::Integer,
+ Odbc_handle_stmt
+ },
+ { Diag_pos_header,
+ SQL_DIAG_NUMBER,
+ OdbcData::Integer,
+ Odbc_handle_all
+ },
+ { Diag_pos_header,
+ SQL_DIAG_RETURNCODE,
+ OdbcData::Smallint,
+ Odbc_handle_all
+ },
+ { Diag_pos_header,
+ SQL_DIAG_ROW_COUNT,
+ OdbcData::Integer,
+ Odbc_handle_stmt
+ },
+ { Diag_pos_status,
+ SQL_DIAG_CLASS_ORIGIN,
+ OdbcData::Sqlchar,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_COLUMN_NUMBER,
+ OdbcData::Integer,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_CONNECTION_NAME,
+ OdbcData::Sqlchar,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_MESSAGE_TEXT,
+ OdbcData::Sqlchar,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_NATIVE,
+ OdbcData::Integer,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_ROW_NUMBER,
+ OdbcData::Integer,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_SERVER_NAME,
+ OdbcData::Sqlchar,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_SQLSTATE,
+ OdbcData::Sqlchar,
+ Odbc_handle_all
+ },
+ { Diag_pos_status,
+ SQL_DIAG_SUBCLASS_ORIGIN,
+ OdbcData::Sqlchar,
+ Odbc_handle_all
+ },
+ { Diag_pos_end,
+ 0,
+ OdbcData::Undef,
+ 0
+ }
+};
+
+const DiagSpec&
+DiagSpec::find(int id)
+{
+ const DiagSpec* p;
+ for (p = diag_spec_list; p->m_pos != Diag_pos_end; p++) {
+ if (p->m_id == id)
+ break;
+ }
+ return *p;
+}
+
+// DiagField
+
+// DiagRec
+
+void
+DiagRec::setField(int id, const OdbcData& data)
+{
+ Fields::iterator iter;
+ iter = m_fields.find(id);
+ if (iter != m_fields.end()) {
+ DiagField& field = (*iter).second;
+ field.setData(data);
+ return;
+ }
+ const DiagSpec& spec = DiagSpec::find(id);
+ if (spec.m_pos != Diag_pos_end) {
+ DiagField field(spec, data);
+ m_fields.insert(Fields::value_type(id, field));
+ return;
+ }
+ ctx_assert(false);
+}
+
+void
+DiagRec::getField(Ctx& ctx, int id, OdbcData& data)
+{
+ Fields::iterator iter;
+ iter = m_fields.find(id);
+ if (iter != m_fields.end()) {
+ DiagField& field = (*iter).second;
+ data.setValue(field.getData());
+ return;
+ }
+ const DiagSpec& spec = DiagSpec::find(id);
+ if (spec.m_pos != Diag_pos_end) {
+ // success and undefined value says the MS doc
+ data.setValue();
+ return;
+ }
+ ctx_assert(false);
+}
+
+// DiagArea
+
+DiagArea::DiagArea() :
+ m_recs(1), // add header
+ m_code(SQL_SUCCESS),
+ m_recNumber(0)
+{
+ setHeader(SQL_DIAG_NUMBER, (SQLINTEGER)0);
+}
+
+DiagArea::~DiagArea() {
+}
+
+unsigned
+DiagArea::numStatus()
+{
+ ctx_assert(m_recs.size() > 0);
+ return m_recs.size() - 1;
+}
+
+void
+DiagArea::pushStatus()
+{
+ ctx_assert(m_recs.size() > 0);
+ DiagRec rec;
+ m_recs.push_back(rec);
+ SQLINTEGER diagNumber = m_recs.size() - 1;
+ setHeader(SQL_DIAG_NUMBER, diagNumber);
+}
+
+void
+DiagArea::setHeader(int id, const OdbcData& data)
+{
+ ctx_assert(m_recs.size() > 0);
+ getHeader().setField(id, data);
+}
+
+// set status
+
+void
+DiagArea::setStatus(int id, const OdbcData& data)
+{
+ getStatus().setField(id, data);
+}
+
+void
+DiagArea::setStatus(const Sqlstate& state)
+{
+ getStatus().setField(SQL_DIAG_SQLSTATE, state);
+ setCode(state.getCode(m_code));
+}
+
+void
+DiagArea::setStatus(const Error& error)
+{
+ BaseString message("");
+ // bracketed prefixes
+ message.append(NDB_ODBC_COMPONENT_VENDOR);
+ message.append(NDB_ODBC_COMPONENT_DRIVER);
+ if (! error.driverError())
+ message.append(NDB_ODBC_COMPONENT_DATABASE);
+ // native error code
+ char nativeString[20];
+ sprintf(nativeString, "%02d%02d%04d",
+ (unsigned)error.m_status % 100,
+ (unsigned)error.m_classification % 100,
+ (unsigned)error.m_code % 10000);
+ SQLINTEGER native = atoi(nativeString);
+ message.appfmt("NDB-%s", nativeString);
+ // message text
+ message.append(" ");
+ message.append(error.m_message);
+ if (error.m_sqlFunction != 0)
+ message.appfmt(" (in %s)", error.m_sqlFunction);
+ // set diag fields
+ setStatus(error.m_sqlstate);
+ setStatus(SQL_DIAG_NATIVE, native);
+ setStatus(SQL_DIAG_MESSAGE_TEXT, message.c_str());
+}
+
+// push status
+
+void
+DiagArea::pushStatus(const Error& error)
+{
+ pushStatus();
+ setStatus(error);
+}
+
+// record access
+
+DiagRec&
+DiagArea::getHeader()
+{
+ ctx_assert(m_recs.size() > 0);
+ return m_recs[0];
+}
+
+DiagRec&
+DiagArea::getStatus()
+{
+ ctx_assert(m_recs.size() > 1);
+ return m_recs.back();
+}
+
+DiagRec&
+DiagArea::getRecord(unsigned num)
+{
+ ctx_assert(num < m_recs.size());
+ return m_recs[num];
+}
+
+void
+DiagArea::getRecord(Ctx& ctx, unsigned num, int id, OdbcData& data)
+{
+ DiagRec& rec = getRecord(num);
+ rec.getField(ctx, id, data);
+}
+
+void
+DiagArea::setCode(SQLRETURN code)
+{
+ m_code = code;
+ getHeader().setField(SQL_DIAG_RETURNCODE, (SQLSMALLINT)code);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/DiagArea.hpp b/storage/ndb/src/old_files/client/odbc/common/DiagArea.hpp
new file mode 100644
index 00000000000..79c03de6623
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/DiagArea.hpp
@@ -0,0 +1,196 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_DiagArea_hpp
+#define ODBC_COMMON_DiagArea_hpp
+
+#include <map>
+#include <vector>
+#include <common/common.hpp>
+#include "OdbcData.hpp"
+
+enum DiagPos {
+ Diag_pos_undef = 0,
+ Diag_pos_header,
+ Diag_pos_status,
+ Diag_pos_end
+};
+
+/**
+ * @class DiagSpec
+ * @brief Field specification
+ */
+struct DiagSpec {
+ DiagPos m_pos; // header or status
+ int m_id; // SQL_DIAG_ identifier
+ OdbcData::Type m_type; // data type
+ unsigned m_handles; // defined for these handle types
+ // find the spec
+ static const DiagSpec& find(int id);
+};
+
+/**
+ * @class DiagField
+ * @brief Field identified by an SQL_DIAG_* constant
+ */
+class DiagField {
+public:
+ DiagField(const DiagSpec& spec, const OdbcData& data);
+ DiagField(const DiagField& field);
+ ~DiagField();
+ void setData(const OdbcData& data);
+ const OdbcData& getData();
+private:
+ const DiagSpec& m_spec;
+ OdbcData m_data;
+};
+
+inline
+DiagField::DiagField(const DiagSpec& spec, const OdbcData& data) :
+ m_spec(spec),
+ m_data(data)
+{
+}
+
+inline
+DiagField::DiagField(const DiagField& field) :
+ m_spec(field.m_spec),
+ m_data(field.m_data)
+{
+}
+
+inline
+DiagField::~DiagField()
+{
+}
+
+inline void
+DiagField::setData(const OdbcData& data)
+{
+ ctx_assert(m_spec.m_type == data.type());
+ m_data.setValue(data);
+}
+
+inline const OdbcData&
+DiagField::getData()
+{
+ ctx_assert(m_data.type() != OdbcData::Undef);
+ return m_data;
+}
+
+/**
+ * @class DiagRec
+ * @brief One diagnostic record, a list of fields
+ */
+class DiagRec {
+public:
+ DiagRec();
+ ~DiagRec();
+ void setField(int id, const OdbcData& data);
+ void getField(Ctx& ctx, int id, OdbcData& data);
+private:
+ typedef std::map<int, DiagField> Fields;
+ Fields m_fields;
+};
+
+inline
+DiagRec::DiagRec()
+{
+}
+
+inline
+DiagRec::~DiagRec()
+{
+}
+
+/**
+ * @class DiagArea
+ * @brief All records, including header (record 0)
+ *
+ * Diagnostic area includes a header (record 0) and zero or more
+ * status records at positions >= 1.
+ */
+class DiagArea {
+public:
+ DiagArea();
+ ~DiagArea();
+ /**
+ * Get number of status records.
+ */
+ unsigned numStatus();
+ /**
+ * Push new status record.
+ */
+ void pushStatus();
+ /**
+ * Set field in header.
+ */
+ void setHeader(int id, const OdbcData& data);
+ /**
+ * Set field in latest status record. The arguments can
+ * also be plain int, char*, Sqlstate. The NDB and other
+ * native errors set Sqlstate _IM000 automatically.
+ */
+ void setStatus(int id, const OdbcData& data);
+ void setStatus(const Sqlstate& state);
+ void setStatus(const Error& error);
+ /**
+ * Convenience methods to push new status record and set
+ * some common fields in it. Sqlstate is set always.
+ */
+ void pushStatus(const Error& error);
+ /**
+ * Get refs to various records.
+ */
+ DiagRec& getHeader();
+ DiagRec& getStatus();
+ DiagRec& getRecord(unsigned num);
+ /**
+ * Get diag values.
+ */
+ void getRecord(Ctx& ctx, unsigned num, int id, OdbcData& data);
+ /**
+ * Get or set return code.
+ */
+ SQLRETURN getCode() const;
+ void setCode(SQLRETURN ret);
+ /**
+ * Get "next" record number (0 when no more).
+ * Used only by the deprecated SQLError function.
+ */
+ unsigned nextRecNumber();
+private:
+ typedef std::vector<DiagRec> Recs;
+ Recs m_recs;
+ SQLRETURN m_code;
+ unsigned m_recNumber; // for SQLError
+};
+
+inline SQLRETURN
+DiagArea::getCode() const
+{
+ return m_code;
+}
+
+inline unsigned
+DiagArea::nextRecNumber()
+{
+ if (m_recNumber >= numStatus())
+ return 0;
+ return ++m_recNumber;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/Makefile b/storage/ndb/src/old_files/client/odbc/common/Makefile
new file mode 100644
index 00000000000..7ee29738d86
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/Makefile
@@ -0,0 +1,29 @@
+include .defs.mk
+
+TYPE = *
+
+NONPIC_ARCHIVE = N
+
+PIC_ARCHIVE = Y
+
+ARCHIVE_TARGET = odbccommon
+
+SOURCES = \
+ common.cpp \
+ Ctx.cpp \
+ Sqlstate.cpp \
+ OdbcData.cpp \
+ DiagArea.cpp \
+ AttrArea.cpp \
+ DescArea.cpp \
+ ConnArea.cpp \
+ StmtInfo.cpp \
+ StmtArea.cpp \
+ CodeTree.cpp \
+ ResultArea.cpp \
+ DataType.cpp \
+ DataField.cpp \
+ DataRow.cpp
+
+include ../Extra.mk
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/client/odbc/common/OdbcData.cpp b/storage/ndb/src/old_files/client/odbc/common/OdbcData.cpp
new file mode 100644
index 00000000000..32400e07c7a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/OdbcData.cpp
@@ -0,0 +1,560 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "OdbcData.hpp"
+
+OdbcData::OdbcData() :
+ m_type(Undef)
+{
+}
+
+OdbcData::OdbcData(Type type) :
+ m_type(type)
+{
+ switch (m_type) {
+ case Smallint:
+ m_smallint = 0;
+ break;
+ case Usmallint:
+ m_usmallint = 0;
+ break;
+ case Integer:
+ m_integer = 0;
+ break;
+ case Uinteger:
+ m_uinteger = 0;
+ break;
+ case Pointer:
+ m_pointer = 0;
+ break;
+ case SmallintPtr:
+ m_smallintPtr = 0;
+ break;
+ case UsmallintPtr:
+ m_usmallintPtr = 0;
+ break;
+ case IntegerPtr:
+ m_integerPtr = 0;
+ break;
+ case UintegerPtr:
+ m_uintegerPtr = 0;
+ break;
+ case PointerPtr:
+ m_pointerPtr = 0;
+ break;
+ case Sqlchar:
+ m_sqlchar = 0;
+ break;
+ case Sqlstate:
+ m_sqlstate = 0;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ };
+}
+
+OdbcData::OdbcData(const OdbcData& odbcData) :
+ m_type(odbcData.m_type)
+{
+ switch (m_type) {
+ case Smallint:
+ m_smallint = odbcData.m_smallint;
+ break;
+ case Usmallint:
+ m_usmallint = odbcData.m_usmallint;
+ break;
+ case Integer:
+ m_integer = odbcData.m_integer;
+ break;
+ case Uinteger:
+ m_uinteger = odbcData.m_uinteger;
+ break;
+ case Pointer:
+ m_pointer = odbcData.m_pointer;
+ break;
+ case SmallintPtr:
+ m_smallintPtr = odbcData.m_smallintPtr;
+ break;
+ case UsmallintPtr:
+ m_usmallintPtr = odbcData.m_usmallintPtr;
+ break;
+ case IntegerPtr:
+ m_integerPtr = odbcData.m_integerPtr;
+ break;
+ case UintegerPtr:
+ m_uintegerPtr = odbcData.m_uintegerPtr;
+ break;
+ case PointerPtr:
+ m_pointerPtr = odbcData.m_pointerPtr;
+ break;
+ case Sqlchar: {
+ unsigned n = strlen(odbcData.m_sqlchar);
+ m_sqlchar = new char[n + 1];
+ memcpy(m_sqlchar, odbcData.m_sqlchar, n + 1);
+ break;
+ }
+ case Sqlstate:
+ m_sqlstate = odbcData.m_sqlstate;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ };
+}
+
+OdbcData::~OdbcData()
+{
+ switch (m_type) {
+ case Sqlchar:
+ delete[] m_sqlchar;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+OdbcData::setValue()
+{
+ m_type = Undef;
+}
+
+void
+OdbcData::setValue(Type type)
+{
+ if (m_type == Sqlchar) {
+ delete[] m_sqlchar;
+ m_sqlchar = 0;
+ }
+ switch (m_type) {
+ case Smallint:
+ m_smallint = 0;
+ break;
+ case Usmallint:
+ m_usmallint = 0;
+ break;
+ case Integer:
+ m_integer = 0;
+ break;
+ case Uinteger:
+ m_uinteger = 0;
+ break;
+ case Pointer:
+ m_pointer = 0;
+ break;
+ case SmallintPtr:
+ m_smallintPtr = 0;
+ break;
+ case UsmallintPtr:
+ m_usmallintPtr = 0;
+ break;
+ case IntegerPtr:
+ m_integerPtr = 0;
+ break;
+ case UintegerPtr:
+ m_uintegerPtr = 0;
+ break;
+ case PointerPtr:
+ m_pointerPtr = 0;
+ break;
+ case Sqlchar:
+ m_sqlchar = 0;
+ break;
+ case Sqlstate:
+ m_sqlstate = 0;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ };
+}
+
+void
+OdbcData::setValue(const OdbcData odbcData)
+{
+ if (m_type == Sqlchar) {
+ delete[] m_sqlchar;
+ m_sqlchar = 0;
+ }
+ m_type = odbcData.m_type;
+ switch (m_type) {
+ case Smallint:
+ m_smallint = odbcData.m_smallint;
+ break;
+ case Usmallint:
+ m_usmallint = odbcData.m_usmallint;
+ break;
+ case Integer:
+ m_integer = odbcData.m_integer;
+ break;
+ case Uinteger:
+ m_uinteger = odbcData.m_uinteger;
+ break;
+ case Pointer:
+ m_pointer = odbcData.m_pointer;
+ break;
+ case SmallintPtr:
+ m_smallintPtr = odbcData.m_smallintPtr;
+ break;
+ case UsmallintPtr:
+ m_usmallintPtr = odbcData.m_usmallintPtr;
+ break;
+ case IntegerPtr:
+ m_integerPtr = odbcData.m_integerPtr;
+ break;
+ case UintegerPtr:
+ m_uintegerPtr = odbcData.m_uintegerPtr;
+ break;
+ case PointerPtr:
+ m_pointerPtr = odbcData.m_pointerPtr;
+ break;
+ case Sqlchar: {
+ unsigned n = strlen(odbcData.m_sqlchar);
+ m_sqlchar = new char[n + 1];
+ memcpy(m_sqlchar, odbcData.m_sqlchar, n + 1);
+ break;
+ }
+ case Sqlstate:
+ m_sqlstate = odbcData.m_sqlstate;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ };
+}
+
+// copy in from user buffer
+
+void
+OdbcData::copyin(Ctx& ctx, Type type, SQLPOINTER buf, SQLINTEGER length)
+{
+ if (m_type == Sqlchar) {
+ delete[] m_sqlchar;
+ m_sqlchar = 0;
+ }
+ m_type = type;
+ switch (m_type) {
+ case Smallint: {
+ SQLSMALLINT val = 0;
+ switch (length) {
+ case 0:
+ case SQL_IS_SMALLINT:
+ val = (SQLSMALLINT)(SQLINTEGER)buf;
+ break;
+ case SQL_IS_USMALLINT:
+ val = (SQLUSMALLINT)(SQLUINTEGER)buf;
+ break;
+ case SQL_IS_INTEGER:
+ val = (SQLINTEGER)buf;
+ break;
+ case SQL_IS_UINTEGER:
+ val = (SQLUINTEGER)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "smallint input - invalid length %d", (int)length);
+ return;
+ }
+ m_smallint = val;
+ break;
+ }
+ case Usmallint: {
+ SQLUSMALLINT val = 0;
+ switch (length) {
+ case SQL_IS_SMALLINT:
+ val = (SQLSMALLINT)(SQLINTEGER)buf;
+ break;
+ case 0:
+ case SQL_IS_USMALLINT:
+ val = (SQLUSMALLINT)(SQLUINTEGER)buf;
+ break;
+ case SQL_IS_INTEGER:
+ val = (SQLINTEGER)buf;
+ break;
+ case SQL_IS_UINTEGER:
+ val = (SQLUINTEGER)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "unsigned smallint input - invalid length %d", (int)length);
+ return;
+ }
+ m_usmallint = val;
+ break;
+ }
+ case Integer: {
+ SQLINTEGER val = 0;
+ switch (length) {
+ case SQL_IS_SMALLINT:
+ val = (SQLSMALLINT)(SQLINTEGER)buf;
+ break;
+ case SQL_IS_USMALLINT:
+ val = (SQLUSMALLINT)(SQLUINTEGER)buf;
+ break;
+ case 0:
+ case SQL_IS_INTEGER:
+ val = (SQLINTEGER)buf;
+ break;
+ case SQL_IS_UINTEGER:
+ val = (SQLUINTEGER)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "integer input - invalid length %d", (int)length);
+ return;
+ }
+ m_integer = val;
+ break;
+ }
+ case Uinteger: {
+ SQLUINTEGER val = 0;
+ switch (length) {
+ case SQL_IS_SMALLINT:
+ val = (SQLSMALLINT)(SQLINTEGER)buf;
+ break;
+ case SQL_IS_USMALLINT:
+ val = (SQLUSMALLINT)(SQLUINTEGER)buf;
+ break;
+ case SQL_IS_INTEGER:
+ val = (SQLINTEGER)buf;
+ break;
+ case 0:
+ case SQL_IS_UINTEGER:
+ val = (SQLUINTEGER)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "unsigned integer input - invalid length %d", (int)length);
+ return;
+ }
+ m_uinteger = val;
+ break;
+ }
+ case Pointer: {
+ SQLPOINTER val = 0;
+ switch (length) {
+ case 0:
+ case SQL_IS_POINTER:
+ val = (SQLPOINTER)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "pointer input - invalid length %d", (int)length);
+ return;
+ }
+ m_pointer = val;
+ break;
+ }
+ case SmallintPtr: {
+ SQLSMALLINT* val = 0;
+ switch (length) {
+ case 0:
+ case SQL_IS_POINTER:
+ val = (SQLSMALLINT*)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "smallint pointer input - invalid length %d", (int)length);
+ return;
+ }
+ m_smallintPtr = val;
+ break;
+ }
+ case UsmallintPtr: {
+ SQLUSMALLINT* val = 0;
+ switch (length) {
+ case 0:
+ case SQL_IS_POINTER:
+ val = (SQLUSMALLINT*)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "unsigned smallint pointer input - invalid length %d", (int)length);
+ return;
+ }
+ m_usmallintPtr = val;
+ break;
+ }
+ case IntegerPtr: {
+ SQLINTEGER* val = 0;
+ switch (length) {
+ case 0:
+ case SQL_IS_POINTER:
+ val = (SQLINTEGER*)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "integer pointer input - invalid length %d", (int)length);
+ return;
+ }
+ m_integerPtr = val;
+ break;
+ }
+ case UintegerPtr: {
+ SQLUINTEGER* val = 0;
+ switch (length) {
+ case 0:
+ case SQL_IS_POINTER:
+ val = (SQLUINTEGER*)buf;
+ break;
+ default:
+ ctx.pushStatus(Error::Gen, "unsigned integer pointer input - invalid length %d", (int)length);
+ return;
+ }
+ m_uintegerPtr = val;
+ break;
+ }
+ case Sqlchar: {
+ const char* val = (char*)buf;
+ if (val == 0) {
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "null string input");
+ return;
+ }
+ if (length < 0 && length != SQL_NTS) {
+ ctx.pushStatus(Error::Gen, "string input - invalid length %d", (int)length);
+ return;
+ }
+ if (length == SQL_NTS) {
+ m_sqlchar = strcpy(new char[strlen(val) + 1], val);
+ } else {
+ m_sqlchar = (char*)memcpy(new char[length + 1], val, length);
+ m_sqlchar[length] = 0;
+ }
+ break;
+ }
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+// copy out to user buffer
+
+void
+OdbcData::copyout(Ctx& ctx, SQLPOINTER buf, SQLINTEGER length, SQLINTEGER* total, SQLSMALLINT* total2)
+{
+ if (buf == 0) {
+ ctx.setCode(SQL_ERROR);
+ return;
+ }
+ switch (m_type) {
+ case Smallint: {
+ SQLSMALLINT* ptr = static_cast<SQLSMALLINT*>(buf);
+ *ptr = m_smallint;
+ break;
+ }
+ case Usmallint: {
+ SQLUSMALLINT* ptr = static_cast<SQLUSMALLINT*>(buf);
+ *ptr = m_usmallint;
+ break;
+ }
+ case Integer: {
+ SQLINTEGER* ptr = static_cast<SQLINTEGER*>(buf);
+ *ptr = m_integer;
+ break;
+ }
+ case Uinteger: {
+ SQLUINTEGER* ptr = static_cast<SQLUINTEGER*>(buf);
+ *ptr = m_uinteger;
+ break;
+ }
+ case Pointer: {
+ SQLPOINTER* ptr = static_cast<SQLPOINTER*>(buf);
+ *ptr = m_pointer;
+ break;
+ }
+ case Sqlchar: {
+ char* ptr = static_cast<char*>(buf);
+ if (length < 0 && length != SQL_NTS) {
+ ctx.setCode(SQL_ERROR);
+ return;
+ }
+ if (length == SQL_NTS) {
+ strcpy(ptr, m_sqlchar);
+ } else {
+ strncpy(ptr, m_sqlchar, length);
+ }
+ if (total != 0)
+ *total = strlen(m_sqlchar);
+ if (total2 != 0)
+ *total2 = strlen(m_sqlchar);
+ break;
+ }
+ case Sqlstate: {
+ char* ptr = static_cast<char*>(buf);
+ const char* state = m_sqlstate->state();
+ if (length < 0 && length != SQL_NTS) {
+ ctx.setCode(SQL_ERROR);
+ return;
+ }
+ if (length == SQL_NTS) {
+ strcpy(ptr, state);
+ } else {
+ strncpy(ptr, state, length);
+ }
+ if (total != 0)
+ *total = strlen(state);
+ if (total2 != 0)
+ *total2 = strlen(state);
+ break;
+ }
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+OdbcData::print(char* buf, unsigned size) const
+{
+ switch (m_type) {
+ case Undef:
+ snprintf(buf, size, "undef");
+ break;
+ case Smallint:
+ snprintf(buf, size, "%d", (int)m_smallint);
+ break;
+ case Usmallint:
+ snprintf(buf, size, "%u", (unsigned)m_usmallint);
+ break;
+ case Integer:
+ snprintf(buf, size, "%ld", (long)m_integer);
+ break;
+ case Uinteger:
+ snprintf(buf, size, "%lu", (unsigned long)m_uinteger);
+ break;
+ case Pointer:
+ snprintf(buf, size, "0x%lx", (unsigned long)m_pointer);
+ break;
+ case SmallintPtr:
+ snprintf(buf, size, "0x%lx", (unsigned long)m_smallintPtr);
+ break;
+ case UsmallintPtr:
+ snprintf(buf, size, "0x%lx", (unsigned long)m_usmallintPtr);
+ break;
+ case IntegerPtr:
+ snprintf(buf, size, "0x%lx", (unsigned long)m_integerPtr);
+ break;
+ case UintegerPtr:
+ snprintf(buf, size, "0x%lx", (unsigned long)m_uintegerPtr);
+ break;
+ case PointerPtr:
+ snprintf(buf, size, "0x%lx", (unsigned long)m_pointerPtr);
+ break;
+ case Sqlchar:
+ snprintf(buf, size, "%s", m_sqlchar);
+ break;
+ case Sqlstate:
+ snprintf(buf, size, "%s", m_sqlstate->state());
+ break;
+ default:
+ snprintf(buf, size, "data(%d)", (int)m_type);
+ break;
+ };
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/OdbcData.hpp b/storage/ndb/src/old_files/client/odbc/common/OdbcData.hpp
new file mode 100644
index 00000000000..c1884507cfe
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/OdbcData.hpp
@@ -0,0 +1,283 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_OdbcData_hpp
+#define ODBC_COMMON_OdbcData_hpp
+
+#include <ndb_types.h>
+#include <common/common.hpp>
+
+/**
+ * @class OdbcData
+ * @brief Odbc data types and storage
+ *
+ * Stores diagnostics, attributes, and descriptors. Also used
+ * for converting to and from driver function arguments.
+ */
+class OdbcData {
+public:
+ enum Type {
+ Undef = 0,
+ Smallint,
+ Usmallint,
+ Integer,
+ Uinteger,
+ Pointer,
+ SmallintPtr,
+ UsmallintPtr,
+ IntegerPtr,
+ UintegerPtr,
+ PointerPtr,
+ Sqlchar,
+ Sqlstate
+ };
+ OdbcData();
+ OdbcData(Type type);
+ OdbcData(const OdbcData& odbcData);
+ OdbcData(SQLSMALLINT smallint);
+ OdbcData(SQLUSMALLINT usmallint);
+ OdbcData(SQLINTEGER integer);
+ OdbcData(SQLUINTEGER uinteger);
+ OdbcData(SQLPOINTER pointer);
+ OdbcData(SQLSMALLINT* smallintPtr);
+ OdbcData(SQLUSMALLINT* usmallintPtr);
+ OdbcData(SQLINTEGER* integerPtr);
+ OdbcData(SQLUINTEGER* uintegerPtr);
+ OdbcData(SQLPOINTER* pointerPtr);
+ OdbcData(const char* sqlchar);
+ OdbcData(const ::Sqlstate& sqlstate);
+ ~OdbcData();
+ Type type() const;
+ void setValue();
+ void setValue(Type type);
+ void setValue(const OdbcData odbcData);
+ // get value
+ SQLSMALLINT smallint() const;
+ SQLUSMALLINT usmallint() const;
+ SQLINTEGER integer() const;
+ SQLUINTEGER uinteger() const;
+ SQLPOINTER pointer() const;
+ SQLSMALLINT* smallintPtr() const;
+ SQLUSMALLINT* usmallintPtr() const;
+ SQLINTEGER* integerPtr() const;
+ SQLUINTEGER* uintegerPtr() const;
+ SQLPOINTER* pointerPtr() const;
+ const char* sqlchar() const;
+ const ::Sqlstate& sqlstate() const;
+ // copy in from user buffer
+ void copyin(Ctx& ctx, Type type, SQLPOINTER buf, SQLINTEGER length);
+ // copy out to user buffer
+ void copyout(Ctx& ctx, SQLPOINTER buf, SQLINTEGER length, SQLINTEGER* total, SQLSMALLINT* total2 = 0);
+ // logging
+ void print(char* buf, unsigned size) const;
+private:
+ OdbcData& operator=(const OdbcData& odbcData); // disallowed
+ Type m_type;
+ union {
+ SQLSMALLINT m_smallint;
+ SQLUSMALLINT m_usmallint;
+ SQLINTEGER m_integer;
+ SQLUINTEGER m_uinteger;
+ SQLPOINTER m_pointer;
+ SQLSMALLINT* m_smallintPtr;
+ SQLUSMALLINT* m_usmallintPtr;
+ SQLINTEGER* m_integerPtr;
+ SQLUINTEGER* m_uintegerPtr;
+ SQLPOINTER* m_pointerPtr;
+ char* m_sqlchar;
+ const ::Sqlstate* m_sqlstate;
+ };
+};
+
+inline OdbcData::Type
+OdbcData::type() const
+{
+ return m_type;
+}
+
+inline
+OdbcData::OdbcData(SQLSMALLINT smallint) :
+ m_type(Smallint),
+ m_smallint(smallint)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLUSMALLINT usmallint) :
+ m_type(Usmallint),
+ m_usmallint(usmallint)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLINTEGER integer) :
+ m_type(Integer),
+ m_integer(integer)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLUINTEGER uinteger) :
+ m_type(Uinteger),
+ m_uinteger(uinteger)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLPOINTER pointer) :
+ m_type(Pointer),
+ m_pointer(pointer)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLSMALLINT* smallintPtr) :
+ m_type(SmallintPtr),
+ m_smallintPtr(smallintPtr)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLUSMALLINT* usmallintPtr) :
+ m_type(UsmallintPtr),
+ m_usmallintPtr(usmallintPtr)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLINTEGER* integerPtr) :
+ m_type(IntegerPtr),
+ m_integerPtr(integerPtr)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLUINTEGER* uintegerPtr) :
+ m_type(UintegerPtr),
+ m_uintegerPtr(uintegerPtr)
+{
+}
+
+inline
+OdbcData::OdbcData(SQLPOINTER* pointerPtr) :
+ m_type(PointerPtr),
+ m_pointerPtr(pointerPtr)
+{
+}
+
+inline
+OdbcData::OdbcData(const char* sqlchar) :
+ m_type(Sqlchar)
+{
+ unsigned n = strlen(sqlchar);
+ m_sqlchar = new char[n + 1];
+ strcpy(m_sqlchar, sqlchar);
+}
+
+inline
+OdbcData::OdbcData(const ::Sqlstate& sqlstate) :
+ m_type(Sqlstate),
+ m_sqlstate(&sqlstate)
+{
+}
+
+// get value
+
+inline SQLSMALLINT
+OdbcData::smallint() const
+{
+ ctx_assert(m_type == Smallint);
+ return m_smallint;
+}
+
+inline SQLUSMALLINT
+OdbcData::usmallint() const
+{
+ ctx_assert(m_type == Usmallint);
+ return m_usmallint;
+}
+
+inline SQLINTEGER
+OdbcData::integer() const
+{
+ ctx_assert(m_type == Integer);
+ return m_integer;
+}
+
+inline SQLUINTEGER
+OdbcData::uinteger() const
+{
+ ctx_assert(m_type == Uinteger);
+ return m_uinteger;
+}
+
+inline SQLPOINTER
+OdbcData::pointer() const
+{
+ ctx_assert(m_type == Pointer);
+ return m_pointer;
+}
+
+inline SQLSMALLINT*
+OdbcData::smallintPtr() const
+{
+ ctx_assert(m_type == SmallintPtr);
+ return m_smallintPtr;
+}
+
+inline SQLUSMALLINT*
+OdbcData::usmallintPtr() const
+{
+ ctx_assert(m_type == UsmallintPtr);
+ return m_usmallintPtr;
+}
+
+inline SQLINTEGER*
+OdbcData::integerPtr() const
+{
+ ctx_assert(m_type == IntegerPtr);
+ return m_integerPtr;
+}
+
+inline SQLUINTEGER*
+OdbcData::uintegerPtr() const
+{
+ ctx_assert(m_type == UintegerPtr);
+ return m_uintegerPtr;
+}
+
+inline SQLPOINTER*
+OdbcData::pointerPtr() const
+{
+ ctx_assert(m_type == PointerPtr);
+ return m_pointerPtr;
+}
+
+inline const char*
+OdbcData::sqlchar() const
+{
+ ctx_assert(m_type == Sqlchar);
+ return m_sqlchar;
+}
+
+inline const ::Sqlstate&
+OdbcData::sqlstate() const
+{
+ ctx_assert(m_type == Sqlstate);
+ return *m_sqlstate;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/ResultArea.cpp b/storage/ndb/src/old_files/client/odbc/common/ResultArea.cpp
new file mode 100644
index 00000000000..79d7fb0ccc4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/ResultArea.cpp
@@ -0,0 +1,29 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ResultArea.hpp"
+
+// ResultArea
+
+ResultArea::~ResultArea()
+{
+}
+
+// ResultSet
+
+ResultSet::~ResultSet()
+{
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/ResultArea.hpp b/storage/ndb/src/old_files/client/odbc/common/ResultArea.hpp
new file mode 100644
index 00000000000..d4890c44d99
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/ResultArea.hpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_ResultArea_hpp
+#define ODBC_COMMON_ResultArea_hpp
+
+#include <common/common.hpp>
+#include <codegen/Code_base.hpp>
+
+class SqlRow;
+
+/**
+ * @class ResultArea
+ * @brief Execution result
+ *
+ * ResultArea contains general results from executing SQL
+ * statements. Data rows from queries are fetched via derived
+ * class ResultSet.
+ */
+class ResultArea {
+public:
+ ResultArea();
+ virtual ~ResultArea();
+ /**
+ * Get number of rows:
+ *
+ * - for queries: number of rows fetched so far
+ * - for DML statements: number of rows affected
+ * - for DDL and other statements: 0
+ */
+ CountType getCount() const;
+protected:
+ void setCount(CountType count);
+ void addCount(unsigned count = 1);
+private:
+ CountType m_count;
+};
+
+inline
+ResultArea::ResultArea() :
+ m_count(0)
+{
+}
+
+inline CountType
+ResultArea::getCount() const
+{
+ return m_count;
+}
+
+inline void
+ResultArea::setCount(CountType count)
+{
+ m_count = count;
+}
+
+inline void
+ResultArea::addCount(unsigned count)
+{
+ m_count += count;
+}
+
+/**
+ * @class ResultSet
+ * @brief Data rows from queries
+ *
+ * ResultSet is an interface implemented by SQL queries and
+ * virtual queries (such as SQLTables). It has an associated
+ * data row accessor SqlRow.
+ */
+class ResultSet : public ResultArea {
+public:
+ ResultSet(const SqlRow& dataRow);
+ virtual ~ResultSet();
+ enum State {
+ State_init = 1, // before first fetch
+ State_ok = 2, // last fetch succeeded
+ State_end = 3 // end of fetch or error
+ };
+ void initState();
+ State getState() const;
+ /**
+ * Get data accessor. Can be retrieved once and used after
+ * each successful ResultSet::fetch().
+ */
+ const SqlRow& dataRow() const;
+ /**
+ * Try to fetch one more row from this result set.
+ * - returns true if a row was fetched
+ * - returns false on end of fetch
+ * - returns false on error and sets error status
+ * It is a fatal error to call fetch after end of fetch.
+ */
+ bool fetch(Ctx& ctx, Exec_base::Ctl& ctl);
+protected:
+ /**
+ * Implementation of ResultSet::fetch() must be provided by
+ * each concrete subclass.
+ */
+ virtual bool fetchImpl(Ctx& ctx, Exec_base::Ctl& ctl) = 0;
+ const SqlRow& m_dataRow;
+ State m_state;
+};
+
+inline
+ResultSet::ResultSet(const SqlRow& dataRow) :
+ m_dataRow(dataRow),
+ m_state(State_end) // explicit initState() is required
+{
+}
+
+inline const SqlRow&
+ResultSet::dataRow() const
+{
+ return m_dataRow;
+}
+
+inline void
+ResultSet::initState()
+{
+ m_state = State_init;
+ setCount(0);
+}
+
+inline ResultSet::State
+ResultSet::getState() const
+{
+ return m_state;
+}
+
+inline bool
+ResultSet::fetch(Ctx& ctx, Exec_base::Ctl& ctl)
+{
+ if (! (m_state == State_init || m_state == State_ok)) {
+ // should not happen but return error instead of assert
+ ctx.pushStatus(Error::Gen, "invalid fetch state %d", (int)m_state);
+ m_state = State_end;
+ return false;
+ }
+ if (fetchImpl(ctx, ctl)) {
+ m_state = State_ok;
+ addCount();
+ return true;
+ }
+ m_state = State_end;
+ return false;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/Sqlstate.cpp b/storage/ndb/src/old_files/client/odbc/common/Sqlstate.cpp
new file mode 100644
index 00000000000..2d625a7c159
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/Sqlstate.cpp
@@ -0,0 +1,93 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/common.hpp>
+
+// Initialize Sqlstate records statically.
+// They are not used by other static initializers.
+
+#define make_Sqlstate(state, code) \
+ const Sqlstate Sqlstate::_##state(#state, code)
+
+make_Sqlstate(00000, SQL_SUCCESS);
+make_Sqlstate(01004, SQL_SUCCESS_WITH_INFO);
+make_Sqlstate(01S02, SQL_SUCCESS_WITH_INFO);
+make_Sqlstate(07009, SQL_ERROR);
+make_Sqlstate(08003, SQL_ERROR);
+make_Sqlstate(21S01, SQL_ERROR);
+make_Sqlstate(22001, SQL_ERROR);
+make_Sqlstate(22002, SQL_ERROR);
+make_Sqlstate(22003, SQL_ERROR);
+make_Sqlstate(22005, SQL_ERROR);
+make_Sqlstate(22008, SQL_ERROR);
+make_Sqlstate(22012, SQL_ERROR);
+make_Sqlstate(24000, SQL_ERROR);
+make_Sqlstate(25000, SQL_ERROR);
+make_Sqlstate(42000, SQL_ERROR);
+make_Sqlstate(42S02, SQL_ERROR);
+make_Sqlstate(42S22, SQL_ERROR);
+make_Sqlstate(HY004, SQL_ERROR);
+make_Sqlstate(HY009, SQL_ERROR);
+make_Sqlstate(HY010, SQL_ERROR);
+make_Sqlstate(HY011, SQL_ERROR);
+make_Sqlstate(HY012, SQL_ERROR);
+make_Sqlstate(HY014, SQL_ERROR);
+make_Sqlstate(HY019, SQL_ERROR);
+make_Sqlstate(HY024, SQL_ERROR);
+make_Sqlstate(HY090, SQL_ERROR);
+make_Sqlstate(HY091, SQL_ERROR);
+make_Sqlstate(HY092, SQL_ERROR);
+make_Sqlstate(HY095, SQL_ERROR);
+make_Sqlstate(HY096, SQL_ERROR);
+make_Sqlstate(HYC00, SQL_ERROR);
+make_Sqlstate(HYT00, SQL_ERROR);
+make_Sqlstate(IM000, SQL_ERROR); // consider all to be errors for now
+make_Sqlstate(IM001, SQL_ERROR);
+make_Sqlstate(IM999, SQL_ERROR);
+
+SQLRETURN
+Sqlstate::getCode(SQLRETURN code) const
+{
+ int codes[2];
+ int ranks[2];
+ codes[0] = code;
+ codes[1] = m_code;
+ for (int i = 0; i < 2; i++) {
+ switch (codes[i]) {
+ case SQL_SUCCESS:
+ ranks[i] = 0;
+ break;
+ case SQL_SUCCESS_WITH_INFO:
+ ranks[i] = 1;
+ break;
+ case SQL_NO_DATA:
+ ranks[i] = 2;
+ break;
+ case SQL_NEED_DATA:
+ ranks[i] = 3;
+ break;
+ case SQL_ERROR:
+ ranks[i] = 9;
+ break;
+ default:
+ ctx_assert(false);
+ ranks[i] = 9;
+ }
+ }
+ if (ranks[0] < ranks[1])
+ code = m_code;
+ return code;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/Sqlstate.hpp b/storage/ndb/src/old_files/client/odbc/common/Sqlstate.hpp
new file mode 100644
index 00000000000..3b4665dc6ca
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/Sqlstate.hpp
@@ -0,0 +1,85 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_SqlState_hpp
+#define ODBC_COMMON_SqlState_hpp
+
+#include <string.h>
+
+/**
+ * SQL states.
+ */
+class Sqlstate {
+public:
+ static const Sqlstate _00000; // no state
+ static const Sqlstate _01004; // data converted with truncation
+ static const Sqlstate _01S02; // option value changed
+ static const Sqlstate _07009; // invalid descriptor index
+ static const Sqlstate _08003; // connection not open
+ static const Sqlstate _21S01; // insert value list does not match column list
+ static const Sqlstate _22001; // string data, right truncation
+ static const Sqlstate _22002; // indicator variable required but not supplied
+ static const Sqlstate _22003; // data overflow
+ static const Sqlstate _22005; // data is not numeric-literal
+ static const Sqlstate _22008; // data value is not a valid timestamp
+ static const Sqlstate _22012; // division by zero
+ static const Sqlstate _24000; // invalid cursor state
+ static const Sqlstate _25000; // invalid transaction state
+ static const Sqlstate _42000; // syntax error or access violation
+ static const Sqlstate _42S02; // base table or view not found
+ static const Sqlstate _42S22; // column not found
+ static const Sqlstate _HY004; // invalid SQL data type
+ static const Sqlstate _HY009; // invalid use of null pointer
+ static const Sqlstate _HY010; // function sequence error
+ static const Sqlstate _HY011; // attribute cannot be set now
+ static const Sqlstate _HY012; // invalid transaction operation code
+ static const Sqlstate _HY014; // limit on number of handles exceeded
+ static const Sqlstate _HY019; // non-character and non-binary data sent in pieces
+ static const Sqlstate _HY024; // invalid attribute value
+ static const Sqlstate _HY090; // invalid string or buffer length
+ static const Sqlstate _HY091; // invalid descriptor field identifier
+ static const Sqlstate _HY092; // invalid attribute/option identifier
+ static const Sqlstate _HY095; // function type out of range
+ static const Sqlstate _HY096; // information type out of range
+ static const Sqlstate _HYC00; // optional feature not implemented
+ static const Sqlstate _HYT00; // timeout expired
+ static const Sqlstate _IM000; // implementation defined
+ static const Sqlstate _IM001; // driver does not support this function
+ static const Sqlstate _IM999; // fill in the right state please
+ // get the 5-char text string
+ const char* state() const;
+ // get code or "upgrade" existing code
+ SQLRETURN getCode(SQLRETURN code = SQL_SUCCESS) const;
+private:
+ Sqlstate(const char* state, const SQLRETURN code);
+ const char* const m_state;
+ const SQLRETURN m_code;
+};
+
+inline const char*
+Sqlstate::state() const
+{
+ return m_state;
+}
+
+inline
+Sqlstate::Sqlstate(const char* state, const SQLRETURN code) :
+ m_state(state),
+ m_code(code)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/StmtArea.cpp b/storage/ndb/src/old_files/client/odbc/common/StmtArea.cpp
new file mode 100644
index 00000000000..5ce2d47d31a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/StmtArea.cpp
@@ -0,0 +1,112 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "DiagArea.hpp"
+#include "StmtArea.hpp"
+#include <codegen/CodeGen.hpp>
+
+StmtArea::StmtArea(ConnArea& connArea) :
+ m_connArea(connArea),
+ m_state(Free),
+ m_useSchemaCon(false),
+ m_useConnection(false),
+ m_planTree(0),
+ m_execTree(0),
+ m_unbound(0),
+ m_rowCount(0),
+ m_tuplesFetched(0)
+{
+ for (unsigned i = 0; i <= 4; i++)
+ m_descArea[i] = 0;
+}
+
+StmtArea::~StmtArea()
+{
+}
+
+void
+StmtArea::free(Ctx &ctx)
+{
+ CodeGen codegen(*this);
+ codegen.close(ctx);
+ codegen.free(ctx);
+ m_sqlText.assign("");
+ m_nativeText.assign("");
+ useSchemaCon(ctx, false);
+ useConnection(ctx, false);
+ m_stmtInfo.free(ctx);
+ m_planTree = 0;
+ m_execTree = 0;
+ m_rowCount = 0;
+ m_tuplesFetched = 0;
+ m_unbound = 0;
+ m_state = Free;
+}
+
+void
+StmtArea::setRowCount(Ctx& ctx, CountType rowCount)
+{
+ m_rowCount = rowCount;
+ // location
+ DescArea& ird = descArea(Desc_usage_IRD);
+ OdbcData data;
+ ird.getHeader().getField(ctx, SQL_DESC_ROWS_PROCESSED_PTR, data);
+ if (data.type() != OdbcData::Undef) {
+ SQLUINTEGER* countPtr = data.uintegerPtr();
+ if (countPtr != 0) {
+ *countPtr = static_cast<SQLUINTEGER>(m_rowCount);
+ }
+ }
+ // diagnostic
+ SQLINTEGER count = static_cast<SQLINTEGER>(m_rowCount);
+ ctx.diagArea().setHeader(SQL_DIAG_ROW_COUNT, count);
+}
+
+void
+StmtArea::setFunction(Ctx& ctx, const char* function, SQLINTEGER functionCode)
+{
+ m_stmtInfo.m_function = function;
+ m_stmtInfo.m_functionCode = functionCode;
+}
+
+void
+StmtArea::setFunction(Ctx& ctx)
+{
+ OdbcData function(m_stmtInfo.m_function);
+ ctx.diagArea().setHeader(SQL_DIAG_DYNAMIC_FUNCTION, function);
+ OdbcData functionCode(m_stmtInfo.m_functionCode);
+ ctx.diagArea().setHeader(SQL_DIAG_DYNAMIC_FUNCTION_CODE, functionCode);
+}
+
+bool
+StmtArea::useSchemaCon(Ctx& ctx, bool use)
+{
+ if (m_useSchemaCon != use)
+ if (! m_connArea.useSchemaCon(ctx, use))
+ return false;
+ m_useSchemaCon = use;
+ return true;
+}
+
+bool
+StmtArea::useConnection(Ctx& ctx, bool use)
+{
+ if (m_useConnection != use)
+ if (! m_connArea.useConnection(ctx, use))
+ return false;
+ m_useConnection = use;
+ return true;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/StmtArea.hpp b/storage/ndb/src/old_files/client/odbc/common/StmtArea.hpp
new file mode 100644
index 00000000000..a88c6d36e6d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/StmtArea.hpp
@@ -0,0 +1,157 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_StmtArea_hpp
+#define ODBC_COMMON_StmtArea_hpp
+
+#include <common/common.hpp>
+#include "ConnArea.hpp"
+#include "StmtInfo.hpp"
+#include "DescArea.hpp"
+
+class PlanTree;
+class ExecTree;
+
+/**
+ * @class StmtArea
+ * @brief Public part of statement handle
+ */
+class StmtArea {
+public:
+ // state between ODBC function calls
+ enum State {
+ Free = 1, // not in use
+ Prepared = 2, // statement prepared, maybe unbound
+ NeedData = 3, // executed, SQLParamData expected
+ Open = 4 // cursor open
+ };
+ // connection area shared by all statements
+ ConnArea& connArea();
+ State getState() const;
+ // SQL text
+ const BaseString& sqlText();
+ BaseString& nativeText();
+ // allocate or unallocate connections if necessary
+ bool useSchemaCon(Ctx& ctx, bool use);
+ bool useConnection(Ctx& ctx, bool use);
+ // statement info
+ StmtInfo& stmtInfo();
+ DescArea& descArea(DescUsage u);
+ unsigned unbound() const;
+ // set row count here and in diagnostics
+ void setRowCount(Ctx& ctx, CountType rowCount);
+ CountType getRowCount() const;
+ // raw tuple count (tuples fetched from NDB)
+ void resetTuplesFetched();
+ void incTuplesFetched();
+ CountType getTuplesFetched() const;
+ // set dynamic function in StmtInfo only (at prepare)
+ void setFunction(Ctx& ctx, const char* function, SQLINTEGER functionCode);
+ // set dynamic function in diagnostics (at execute)
+ void setFunction(Ctx& ctx);
+protected:
+ friend class CodeGen;
+ friend class Executor;
+ friend class Plan_root;
+ StmtArea(ConnArea& connArea);
+ ~StmtArea();
+ void free(Ctx& ctx);
+ ConnArea& m_connArea;
+ State m_state;
+ BaseString m_sqlText;
+ BaseString m_nativeText;
+ bool m_useSchemaCon;
+ bool m_useConnection;
+ StmtInfo m_stmtInfo;
+ // plan tree output from parser and rewritten by analyze
+ PlanTree* m_planTree;
+ // exec tree output from analyze
+ ExecTree* m_execTree;
+ // pointers within HandleDesc allocated via HandleStmt
+ DescArea* m_descArea[1+4];
+ // parameters with unbound SQL type
+ unsigned m_unbound;
+ CountType m_rowCount;
+ CountType m_tuplesFetched;
+};
+
+inline ConnArea&
+StmtArea::connArea()
+{
+ return m_connArea;
+}
+
+inline StmtArea::State
+StmtArea::getState() const
+{
+ return m_state;
+}
+
+inline const BaseString&
+StmtArea::sqlText() {
+ return m_sqlText;
+}
+
+inline BaseString&
+StmtArea::nativeText() {
+ return m_nativeText;
+}
+
+inline StmtInfo&
+StmtArea::stmtInfo()
+{
+ return m_stmtInfo;
+}
+
+inline DescArea&
+StmtArea::descArea(DescUsage u)
+{
+ ctx_assert(1 <= u && u <= 4);
+ ctx_assert(m_descArea[u] != 0);
+ return *m_descArea[u];
+}
+
+inline unsigned
+StmtArea::unbound() const
+{
+ return m_unbound;
+}
+
+inline CountType
+StmtArea::getRowCount() const
+{
+ return m_rowCount;
+}
+
+inline void
+StmtArea::resetTuplesFetched()
+{
+ m_tuplesFetched = 0;
+}
+
+inline void
+StmtArea::incTuplesFetched()
+{
+ m_tuplesFetched++;
+}
+
+inline CountType
+StmtArea::getTuplesFetched() const
+{
+ return m_tuplesFetched;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/StmtInfo.cpp b/storage/ndb/src/old_files/client/odbc/common/StmtInfo.cpp
new file mode 100644
index 00000000000..3467fb5023e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/StmtInfo.cpp
@@ -0,0 +1,78 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "StmtInfo.hpp"
+
+const char*
+StmtInfo::getDesc() const
+{
+ switch (m_name) {
+ case Stmt_name_select:
+ return "select";
+ case Stmt_name_insert:
+ return "insert";
+ case Stmt_name_update:
+ return "update";
+ case Stmt_name_delete:
+ return "delete";
+ case Stmt_name_create_table:
+ return "create table";
+ case Stmt_name_create_index:
+ return "create index";
+ case Stmt_name_drop_table:
+ return "drop table";
+ case Stmt_name_drop_index:
+ return "drop index";
+ default:
+ ctx_assert(false);
+ break;
+ }
+ return "";
+}
+
+StmtType
+StmtInfo::getType() const
+{
+ StmtType type = Stmt_type_undef;
+ switch (m_name) {
+ case Stmt_name_select: // query
+ type = Stmt_type_query;
+ break;
+ case Stmt_name_insert: // DML
+ case Stmt_name_update:
+ case Stmt_name_delete:
+ type = Stmt_type_DML;
+ break;
+ case Stmt_name_create_table: // DDL
+ case Stmt_name_create_index:
+ case Stmt_name_drop_table:
+ case Stmt_name_drop_index:
+ type = Stmt_type_DDL;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ return type;
+}
+
+void
+StmtInfo::free(Ctx& ctx)
+{
+ m_name = Stmt_name_undef;
+ m_function = "";
+ m_functionCode = SQL_DIAG_UNKNOWN_STATEMENT;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/common/StmtInfo.hpp b/storage/ndb/src/old_files/client/odbc/common/StmtInfo.hpp
new file mode 100644
index 00000000000..9cd489be6da
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/StmtInfo.hpp
@@ -0,0 +1,86 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_StmtInfo_hpp
+#define ODBC_COMMON_StmtInfo_hpp
+
+#include <common/common.hpp>
+
+// general type (determined by SQL command)
+enum StmtType {
+ Stmt_type_undef = 0,
+ Stmt_type_query, // select
+ Stmt_type_DML, // insert, update, delete
+ Stmt_type_DDL, // create, drop, alter table
+ Stmt_type_info // virtual query
+};
+
+// specific SQL command (first 1-2 words)
+enum StmtName {
+ Stmt_name_undef = 0,
+ Stmt_name_select,
+ Stmt_name_insert,
+ Stmt_name_update,
+ Stmt_name_delete,
+ Stmt_name_create_table,
+ Stmt_name_create_index,
+ Stmt_name_drop_table,
+ Stmt_name_drop_index
+};
+
+/**
+ * @class StmtInfo
+ * @brief Statement Info
+ *
+ * Statement info. This is a separate class which could
+ * be used in cases not tied to statement execution.
+ */
+class StmtInfo {
+public:
+ StmtInfo();
+ void setName(StmtName name);
+ StmtName getName() const;
+ const char* getDesc() const;
+ StmtType getType() const;
+ void free(Ctx& ctx);
+private:
+ friend class StmtArea;
+ StmtName m_name;
+ const char* m_function; // not allocated
+ SQLINTEGER m_functionCode;
+};
+
+inline
+StmtInfo::StmtInfo() :
+ m_name(Stmt_name_undef),
+ m_function(""),
+ m_functionCode(SQL_DIAG_UNKNOWN_STATEMENT)
+{
+}
+
+inline void
+StmtInfo::setName(StmtName name)
+{
+ m_name = name;
+}
+
+inline StmtName
+StmtInfo::getName() const
+{
+ return m_name;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/common/common.cpp b/storage/ndb/src/old_files/client/odbc/common/common.cpp
new file mode 100644
index 00000000000..73d14c82efe
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/common.cpp
@@ -0,0 +1,17 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "common.hpp"
diff --git a/storage/ndb/src/old_files/client/odbc/common/common.hpp b/storage/ndb/src/old_files/client/odbc/common/common.hpp
new file mode 100644
index 00000000000..d2f243b6437
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/common/common.hpp
@@ -0,0 +1,124 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_COMMON_common_hpp
+#define ODBC_COMMON_common_hpp
+
+#define stpcpy stpcpy
+#include <ndb_global.h>
+#undef swap
+
+// misc defs
+
+#ifdef NDB_GCC // only for odbc
+#define PRINTFLIKE(i,j) __attribute__ ((format (printf, i, j)))
+#else
+#define PRINTFLIKE(i,j)
+#endif
+
+// odbc defs
+
+#define ODBCVER 0x0351
+
+#ifdef NDB_WIN32
+#include <windows.h>
+#endif
+
+extern "C" {
+#include <sqlext.h>
+}
+// some types which may be missing
+#ifndef SQL_BLOB
+#define SQL_BLOB 30
+#endif
+#ifndef SQL_BLOB_LOCATOR
+#define SQL_BLOB_LOCATOR 31
+#endif
+#ifndef SQL_CLOB
+#define SQL_CLOB 40
+#endif
+#ifndef SQL_CLOB_LOCATOR
+#define SQL_CLOB_LOCATOR 41
+#endif
+
+// until real blobs use Varchar of this size
+#define FAKE_BLOB_SIZE 2000
+
+#define SQL_HANDLE_ROOT 0 // assume real handles != 0
+
+enum OdbcHandle {
+ Odbc_handle_root = 0, // not an odbc handle
+ Odbc_handle_env = 1,
+ Odbc_handle_dbc = 2,
+ Odbc_handle_stmt = 4,
+ Odbc_handle_desc = 8,
+ Odbc_handle_all = (1|2|4|8)
+};
+
+// ndb defs
+
+#undef BOOL
+#include <ndb_types.h>
+// this info not yet on api side
+#include <kernel/ndb_limits.h>
+#include <ndb_version.h>
+
+#ifndef MAX_TAB_NAME_SIZE
+#define MAX_TAB_NAME_SIZE 128
+#endif
+
+#ifndef MAX_ATTR_NAME_SIZE
+#define MAX_ATTR_NAME_SIZE 32
+#endif
+
+#ifndef MAX_ATTR_DEFAULT_VALUE_SIZE
+#define MAX_ATTR_DEFAULT_VALUE_SIZE 128
+#endif
+
+typedef Uint32 NdbAttrId;
+typedef Uint64 CountType;
+
+// ndb odbc defs
+
+#define NDB_ODBC_COMPONENT_VENDOR "[MySQL]"
+#define NDB_ODBC_COMPONENT_DRIVER "[ODBC driver]"
+#define NDB_ODBC_COMPONENT_DATABASE "[NDB Cluster]"
+
+#define NDB_ODBC_VERSION_MAJOR 0
+#define NDB_ODBC_VERSION_MINOR 22
+#define NDB_ODBC_VERSION_STRING "0.22"
+
+// reserved error codes for non-NDB errors
+#define NDB_ODBC_ERROR_MIN 5000
+#define NDB_ODBC_ERROR_MAX 5100
+
+// maximum log level compiled in
+#ifdef VM_TRACE
+#define NDB_ODBC_MAX_LOG_LEVEL 5
+#else
+#define NDB_ODBC_MAX_LOG_LEVEL 3
+#endif
+
+// driver specific statement attribute for number of NDB tuples fetched
+#define SQL_ATTR_NDB_TUPLES_FETCHED 66601
+
+#include <BaseString.hpp>
+#include <common/Sqlstate.hpp>
+#include <common/Ctx.hpp>
+
+#undef assert
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.cpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.cpp
new file mode 100644
index 00000000000..433347c9a70
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.cpp
@@ -0,0 +1,42 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/ConnArea.hpp>
+#include "DictCatalog.hpp"
+#include "DictSchema.hpp"
+
+DictCatalog::~DictCatalog()
+{
+ for (Schemas::iterator i = m_schemas.begin(); i != m_schemas.end(); i++) {
+ delete *i;
+ *i = 0;
+ }
+}
+
+DictSchema*
+DictCatalog::findSchema(Ctx& ctx, const BaseString& name)
+{
+ for (Schemas::iterator i = m_schemas.begin(); i != m_schemas.end(); i++) {
+ DictSchema* schema = *i;
+ ctx_assert(schema != 0);
+ if (strcmp(schema->getName().c_str(), name.c_str()) == 0)
+ return schema;
+ }
+ ctx_assert(strcmp(name.c_str(), "NDB") == 0);
+ DictSchema* schema = new DictSchema(m_connArea, "NDB");
+ m_schemas.push_back(schema);
+ return schema;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.hpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.hpp
new file mode 100644
index 00000000000..5452990a51b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.hpp
@@ -0,0 +1,64 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DICTIONARY_DictCatalog_hpp
+#define ODBC_DICTIONARY_DictCatalog_hpp
+
+#include <list>
+#include <common/common.hpp>
+#include "DictSchema.hpp"
+
+class Ctx;
+class ConnArea;
+class DictSchema;
+
+/**
+ * @class DictCatalog
+ * @brief Collection of schemas
+ */
+class DictCatalog {
+public:
+ DictCatalog(const ConnArea& connArea);
+ ~DictCatalog();
+ const ConnArea& connArea() const;
+ DictSchema* findSchema(Ctx& ctx, const BaseString& name);
+ void addSchema(DictSchema* schema);
+protected:
+ const ConnArea& m_connArea;
+ typedef std::list<DictSchema*> Schemas;
+ Schemas m_schemas;
+};
+
+inline
+DictCatalog::DictCatalog(const ConnArea& connArea) :
+ m_connArea(connArea)
+{
+}
+
+inline const ConnArea&
+DictCatalog::connArea() const
+{
+ return m_connArea;
+}
+
+inline void
+DictCatalog::addSchema(DictSchema* schema)
+{
+ m_schemas.push_back(schema);
+ schema->setParent(this);
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.cpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.cpp
new file mode 100644
index 00000000000..fa0128f1ddb
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.cpp
@@ -0,0 +1,23 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "DictColumn.hpp"
+
+DictColumn::~DictColumn()
+{
+ delete[] m_defaultValue;
+ m_defaultValue = 0;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.hpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.hpp
new file mode 100644
index 00000000000..945fb86367b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.hpp
@@ -0,0 +1,143 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DICTIONARY_DictColumn_hpp
+#define ODBC_DICTIONARY_DictColumn_hpp
+
+#include <common/common.hpp>
+#include <common/DataType.hpp>
+
+class Ctx;
+class SqlType;
+class ConnArea;
+class DictTable;
+
+/**
+ * @class DictColumn
+ * @brief Table column
+ */
+class DictColumn {
+public:
+ DictColumn(const ConnArea& connArea, const BaseString& name, const SqlType& sqlType);
+ ~DictColumn();
+ const BaseString& getName() const;
+ const SqlType& sqlType() const;
+ void setPosition(unsigned position);
+ unsigned getPosition() const;
+ void setParent(DictTable* parent);
+ DictTable* getParent() const;
+ NdbAttrId getAttrId() const;
+ bool isKey() const;
+ bool isTupleId() const;
+ bool isAutoIncrement() const;
+ const char* getDefaultValue() const;
+protected:
+ friend class DictSys;
+ friend class DictTable;
+ const ConnArea& m_connArea;
+ const BaseString m_name;
+ SqlType m_sqlType;
+ unsigned m_position;
+ DictTable* m_parent;
+ bool m_key; // part of key
+ bool m_tupleId; // the tuple id
+ bool m_autoIncrement;
+ const char* m_defaultValue;
+};
+
+inline
+DictColumn::DictColumn(const ConnArea& connArea, const BaseString& name, const SqlType& sqlType) :
+ m_connArea(connArea),
+ m_name(name),
+ m_sqlType(sqlType),
+ m_position(0),
+ m_parent(0),
+ m_key(false),
+ m_tupleId(false),
+ m_autoIncrement(false),
+ m_defaultValue(0)
+{
+}
+
+inline const SqlType&
+DictColumn::sqlType() const
+{
+ return m_sqlType;
+}
+
+inline void
+DictColumn::setPosition(unsigned position)
+{
+ ctx_assert(position != 0);
+ m_position = position;
+}
+
+inline unsigned
+DictColumn::getPosition() const
+{
+ return m_position;
+}
+
+inline void
+DictColumn::setParent(DictTable* parent)
+{
+ m_parent = parent;
+}
+
+inline DictTable*
+DictColumn::getParent() const
+{
+ return m_parent;
+}
+
+inline const BaseString&
+DictColumn::getName() const
+{
+ return m_name;
+}
+
+inline NdbAttrId
+DictColumn::getAttrId() const
+{
+ ctx_assert(m_position != 0);
+ return static_cast<NdbAttrId>(m_position - 1);
+}
+
+inline bool
+DictColumn::isKey() const
+{
+ return m_key;
+}
+
+inline bool
+DictColumn::isTupleId() const
+{
+ return m_tupleId;
+}
+
+inline bool
+DictColumn::isAutoIncrement() const
+{
+ return m_autoIncrement;
+}
+
+inline const char*
+DictColumn::getDefaultValue() const
+{
+ return m_defaultValue;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.cpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.cpp
new file mode 100644
index 00000000000..95d93318902
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.cpp
@@ -0,0 +1,29 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "DictTable.hpp"
+#include "DictIndex.hpp"
+
+DictIndex::~DictIndex()
+{
+}
+
+void
+DictIndex::setColumn(unsigned i, DictColumn* column)
+{
+ ctx_assert(1 <= i && i <= m_size);
+ m_columns[i] = column;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.hpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.hpp
new file mode 100644
index 00000000000..7ba46daaae3
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.hpp
@@ -0,0 +1,108 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DICTIONARY_DictIndex_hpp
+#define ODBC_DICTIONARY_DictIndex_hpp
+
+#include <vector>
+#include <common/common.hpp>
+#include "DictColumn.hpp"
+
+class Ctx;
+class ConnArea;
+class DictTable;
+class DictColumn;
+class DictIndex;
+
+/**
+ * @class DictIndex
+ * @brief Database table
+ */
+class DictIndex {
+ friend class DictSchema;
+public:
+ DictIndex(const ConnArea& connArea, const BaseString& name, NdbDictionary::Object::Type type, unsigned size);
+ ~DictIndex();
+ NdbDictionary::Object::Type getType() const;
+ unsigned getSize() const;
+ void setTable(DictTable* table);
+ DictTable* getTable() const;
+ void setColumn(unsigned i, DictColumn* column);
+ DictColumn* getColumn(unsigned i) const;
+ const BaseString& getName() const;
+ DictColumn* findColumn(const BaseString& name) const;
+protected:
+ const ConnArea& m_connArea;
+ const BaseString m_name;
+ const NdbDictionary::Object::Type m_type;
+ const unsigned m_size;
+ DictSchema* m_parent;
+ DictTable* m_table;
+ typedef std::vector<DictColumn*> Columns; // pointers to table columns
+ Columns m_columns;
+};
+
+inline
+DictIndex::DictIndex(const ConnArea& connArea, const BaseString& name, NdbDictionary::Object::Type type, unsigned size) :
+ m_connArea(connArea),
+ m_name(name),
+ m_type(type),
+ m_size(size),
+ m_parent(0),
+ m_columns(1 + size)
+{
+}
+
+inline NdbDictionary::Object::Type
+DictIndex::getType() const
+{
+ return m_type;
+}
+
+inline unsigned
+DictIndex::getSize() const
+{
+ ctx_assert(m_columns.size() == 1 + m_size);
+ return m_size;
+}
+
+inline void
+DictIndex::setTable(DictTable* table)
+{
+ m_table = table;
+}
+
+inline DictTable*
+DictIndex::getTable() const
+{
+ return m_table;
+}
+
+inline DictColumn*
+DictIndex::getColumn(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_size);
+ ctx_assert(m_columns[i] != 0);
+ return m_columns[i];
+}
+
+inline const BaseString&
+DictIndex::getName() const
+{
+ return m_name;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.cpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.cpp
new file mode 100644
index 00000000000..91939cb2f26
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.cpp
@@ -0,0 +1,155 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/ConnArea.hpp>
+#include "DictCatalog.hpp"
+#include "DictSchema.hpp"
+#include "DictTable.hpp"
+#include "DictTable.hpp"
+#include "DictColumn.hpp"
+#include "DictIndex.hpp"
+#include "DictSys.hpp"
+
+DictSchema::~DictSchema()
+{
+ for (Tables::iterator i = m_tables.begin(); i != m_tables.end(); i++) {
+ delete *i;
+ *i = 0;
+ }
+}
+
+DictTable*
+DictSchema::findTable(const BaseString& name)
+{
+ for (Tables::iterator i = m_tables.begin(); i != m_tables.end(); i++) {
+ DictTable* table = *i;
+ ctx_assert(table != 0);
+ if (strcmp(table->getName().c_str(), name.c_str()) == 0)
+ return table;
+ }
+ return 0;
+}
+
+void
+DictSchema::deleteTable(Ctx& ctx, const BaseString& name)
+{
+ Tables::iterator i = m_tables.begin();
+ while (i != m_tables.end()) {
+ DictTable* table = *i;
+ ctx_assert(table != 0);
+ if (strcmp(table->getName().c_str(), name.c_str()) == 0) {
+ ctx_log2(("purge table %s from dictionary", name.c_str()));
+ delete table;
+ Tables::iterator j = i;
+ i++;
+ m_tables.erase(j);
+ break;
+ }
+ i++;
+ }
+}
+
+void
+DictSchema::deleteTableByIndex(Ctx& ctx, const BaseString& indexName)
+{
+ DictTable* foundTable = 0;
+ for (Tables::iterator i = m_tables.begin(); i != m_tables.end(); i++) {
+ DictTable* table = *i;
+ ctx_assert(table != 0);
+ for (unsigned k = 1; k <= table->indexCount(); k++) {
+ const DictIndex* index = table->getIndex(k);
+ if (strcmp(index->getName().c_str(), indexName.c_str()) == 0) {
+ foundTable = table;
+ break;
+ }
+ }
+ if (foundTable != 0)
+ break;
+ }
+ if (foundTable != 0)
+ deleteTable(ctx, foundTable->getName());
+}
+
+DictTable*
+DictSchema::loadTable(Ctx& ctx, const BaseString& name)
+{
+ ctx_log4(("%s: load from NDB", name.c_str()));
+ Ndb* ndb = m_connArea.ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return 0;
+ }
+ const NdbDictionary::Table* ndbTable = ndbDictionary->getTable(name.c_str());
+ if (ndbTable == 0) {
+ const NdbError& ndbError = ndbDictionary->getNdbError();
+ if (ndbError.code == 709) {
+ // try built-in system table
+ DictTable* table = DictSys::loadTable(ctx, this, name);
+ if (table != 0) {
+ return table;
+ }
+ ctx_log3(("%s: not found in NDB", name.c_str()));
+ return 0;
+ }
+ ctx.pushStatus(ndbDictionary->getNdbError(), "getTable");
+ return 0;
+ }
+ int nattr = ndbTable->getNoOfColumns();
+ DictTable* table = new DictTable(m_connArea, name, nattr);
+ for (unsigned position = 1; position <= (unsigned)nattr; position++) {
+ DictColumn* column = table->loadColumn(ctx, position);
+ if (column == 0)
+ return 0;
+ ctx_log4(("add column %u %s", column->getPosition(), column->getName().c_str()));
+ }
+ // load indexes
+ NdbDictionary::Dictionary::List list;
+ if (ndbDictionary->listIndexes(list, name.c_str()) == -1) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "listIndexes");
+ return 0;
+ }
+ for (unsigned i = 0; i < list.count; i++) {
+ const NdbDictionary::Dictionary::List::Element& elt = list.elements[i];
+ if (elt.state != NdbDictionary::Object::StateOnline) {
+ ctx_log1(("%s: skip broken index %s", name.c_str(), elt.name));
+ continue;
+ }
+ if (elt.type != NdbDictionary::Object::UniqueHashIndex && elt.type != NdbDictionary::Object::OrderedIndex) {
+ ctx_log1(("%s: skip unknown index type %s", name.c_str(), elt.name));
+ continue;
+ }
+ const NdbDictionary::Index* ndbIndex = ndbDictionary->getIndex(elt.name, name.c_str());
+ if (ndbIndex == 0) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "table %s getIndex %s", name.c_str(), elt.name);
+ return 0;
+ }
+ DictIndex* index = new DictIndex(m_connArea, elt.name, elt.type, ndbIndex->getNoOfIndexColumns());
+ for (unsigned j = 0; j < index->getSize(); j++) {
+ const char* cname = ndbIndex->getIndexColumn(j);
+ ctx_assert(cname != 0);
+ DictColumn* icolumn = table->findColumn(cname);
+ ctx_assert(icolumn != 0);
+ index->setColumn(1 + j, icolumn);
+ }
+ table->addIndex(index);
+ ctx_log3(("%s: index %s: load from NDB done", name.c_str(), elt.name));
+ }
+ addTable(table);
+ ctx_log3(("%s: load from NDB done", name.c_str()));
+ return table;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.hpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.hpp
new file mode 100644
index 00000000000..099352edbb9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.hpp
@@ -0,0 +1,89 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DICTIONARY_DictSchema_hpp
+#define ODBC_DICTIONARY_DictSchema_hpp
+
+#include <list>
+#include <common/common.hpp>
+#include "DictTable.hpp"
+
+class Ctx;
+class ConnArea;
+class DictCatalog;
+class DictTable;
+
+/**
+ * @class DictSchema
+ * @brief Collection of tables
+ */
+class DictSchema {
+public:
+ DictSchema(const ConnArea& connArea, const BaseString& name);
+ ~DictSchema();
+ const BaseString& getName() const;
+ void setParent(DictCatalog* parent);
+ DictCatalog* getParent() const;
+ void addTable(DictTable* table);
+ DictTable* findTable(const BaseString& name);
+ DictTable* loadTable(Ctx& ctx, const BaseString& name);
+ void deleteTable(Ctx& ctx, const BaseString& name);
+ void deleteTableByIndex(Ctx& ctx, const BaseString& indexName);
+protected:
+ friend class DictCatalog;
+ friend class DictSys;
+ const ConnArea& m_connArea;
+ BaseString m_name;
+ DictCatalog* m_parent;
+ typedef std::list<DictTable*> Tables;
+ Tables m_tables;
+};
+
+inline
+DictSchema::DictSchema(const ConnArea& connArea, const BaseString& name) :
+ m_connArea(connArea),
+ m_name(name),
+ m_parent(0)
+{
+ ctx_assert(strcmp(name.c_str(), "NDB") == 0);
+}
+
+inline const BaseString&
+DictSchema::getName() const
+{
+ return m_name;
+}
+
+inline void
+DictSchema::setParent(DictCatalog* parent)
+{
+ m_parent = parent;
+}
+
+inline DictCatalog*
+DictSchema::getParent() const
+{
+ return m_parent;
+}
+
+inline void
+DictSchema::addTable(DictTable* table)
+{
+ m_tables.push_back(table);
+ table->setParent(this);
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictSys.cpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictSys.cpp
new file mode 100644
index 00000000000..1ceef66ee57
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictSys.cpp
@@ -0,0 +1,433 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/ConnArea.hpp>
+#include "DictSchema.hpp"
+#include "DictTable.hpp"
+#include "DictColumn.hpp"
+#include "DictSys.hpp"
+
+#define arraySize(x) sizeof(x)/sizeof(x[0])
+
+#define MAX_SCHEMA_NAME_LENGTH 32
+#define MAX_REMARKS_LENGTH 256
+
+// typeinfo
+
+static DictSys::Column
+column_ODBC_TYPEINFO[] = {
+ DictSys::Column(
+ 1,
+ "TYPE_NAME",
+ false,
+ SqlType(SqlType::Varchar, 20, false)
+ ),
+ DictSys::Column(
+ 2,
+ "DATA_TYPE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 3,
+ "COLUMN_SIZE",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 4,
+ "LITERAL_PREFIX",
+ false,
+ SqlType(SqlType::Varchar, 1, true)
+ ),
+ DictSys::Column(
+ 5,
+ "LITERAL_SUFFIX",
+ false,
+ SqlType(SqlType::Varchar, 1, true)
+ ),
+ DictSys::Column(
+ 6,
+ "CREATE_PARAMS",
+ false,
+ SqlType(SqlType::Varchar, 20, true)
+ ),
+ DictSys::Column(
+ 7,
+ "NULLABLE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 8,
+ "CASE_SENSITIVE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 9,
+ "SEARCHABLE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 10,
+ "UNSIGNED_ATTRIBUTE",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 11,
+ "FIXED_PREC_SCALE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 12,
+ "AUTO_UNIQUE_VALUE",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 13,
+ "LOCAL_TYPE_NAME",
+ false,
+ SqlType(SqlType::Varchar, 20, true)
+ ),
+ DictSys::Column(
+ 14,
+ "MINIMUM_SCALE",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 15,
+ "MAXIMUM_SCALE",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 16,
+ "SQL_DATA_TYPE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 17,
+ "SQL_DATETIME_SUB",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 18,
+ "NUM_PREC_RADIX",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 19,
+ "INTERVAL_PRECISION",
+ false,
+ SqlType(SqlType::Integer, true)
+ )
+};
+
+static DictSys::Table
+table_ODBC_TYPEINFO(
+ DictSys::OdbcTypeinfo,
+ "ODBC$TYPEINFO",
+ column_ODBC_TYPEINFO,
+ arraySize(column_ODBC_TYPEINFO)
+);
+
+// tables
+
+static DictSys::Column
+column_ODBC_TABLES[] = {
+ // perl docs/systables.pl tables -c
+ DictSys::Column(
+ 1,
+ "TABLE_CAT",
+ false,
+ SqlType(SqlType::Varchar, MAX_SCHEMA_NAME_LENGTH, true)
+ ),
+ DictSys::Column(
+ 2,
+ "TABLE_SCHEM",
+ false,
+ SqlType(SqlType::Varchar, MAX_SCHEMA_NAME_LENGTH, true)
+ ),
+ DictSys::Column(
+ 3,
+ "TABLE_NAME",
+ false,
+ SqlType(SqlType::Varchar, MAX_TAB_NAME_SIZE, false)
+ ),
+ DictSys::Column(
+ 4,
+ "TABLE_TYPE",
+ false,
+ SqlType(SqlType::Varchar, 20, false)
+ ),
+ DictSys::Column(
+ 5,
+ "REMARKS",
+ false,
+ SqlType(SqlType::Varchar, MAX_REMARKS_LENGTH, true)
+ )
+};
+
+static DictSys::Table
+table_ODBC_TABLES(
+ DictSys::OdbcTables,
+ "ODBC$TABLES",
+ column_ODBC_TABLES,
+ arraySize(column_ODBC_TABLES)
+);
+
+// columns
+
+static DictSys::Column
+column_ODBC_COLUMNS[] = {
+ // perl docs/systables.pl columns -c
+ DictSys::Column(
+ 1,
+ "TABLE_CAT",
+ false,
+ SqlType(SqlType::Varchar, MAX_SCHEMA_NAME_LENGTH, true)
+ ),
+ DictSys::Column(
+ 2,
+ "TABLE_SCHEM",
+ false,
+ SqlType(SqlType::Varchar, MAX_SCHEMA_NAME_LENGTH, true)
+ ),
+ DictSys::Column(
+ 3,
+ "TABLE_NAME",
+ false,
+ SqlType(SqlType::Varchar, MAX_TAB_NAME_SIZE, false)
+ ),
+ DictSys::Column(
+ 4,
+ "COLUMN_NAME",
+ false,
+ SqlType(SqlType::Varchar, MAX_ATTR_NAME_SIZE, false)
+ ),
+ DictSys::Column(
+ 5,
+ "DATA_TYPE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 6,
+ "TYPE_NAME",
+ false,
+ SqlType(SqlType::Varchar, 20, false)
+ ),
+ DictSys::Column(
+ 7,
+ "COLUMN_SIZE",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 8,
+ "BUFFER_LENGTH",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 9,
+ "DECIMAL_DIGITS",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 10,
+ "NUM_PREC_RADIX",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 11,
+ "NULLABLE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 12,
+ "REMARKS",
+ false,
+ SqlType(SqlType::Varchar, MAX_REMARKS_LENGTH, true)
+ ),
+ DictSys::Column(
+ 13,
+ "COLUMN_DEF",
+ false,
+ SqlType(SqlType::Varchar, MAX_ATTR_DEFAULT_VALUE_SIZE, true)
+ ),
+ DictSys::Column(
+ 14,
+ "SQL_DATA_TYPE",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 15,
+ "SQL_DATETIME_SUB",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 16,
+ "CHAR_OCTET_LENGTH",
+ false,
+ SqlType(SqlType::Integer, true)
+ ),
+ DictSys::Column(
+ 17,
+ "ORDINAL_POSITION",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 18,
+ "IS_NULLABLE",
+ false,
+ SqlType(SqlType::Varchar, 3, true)
+ )
+};
+
+static DictSys::Table
+table_ODBC_COLUMNS(
+ DictSys::OdbcColumns,
+ "ODBC$COLUMNS",
+ column_ODBC_COLUMNS,
+ arraySize(column_ODBC_COLUMNS)
+);
+
+// primarykeys
+
+static DictSys::Column
+column_ODBC_PRIMARYKEYS[] = {
+ DictSys::Column(
+ 1,
+ "TABLE_CAT",
+ false,
+ SqlType(SqlType::Varchar, MAX_SCHEMA_NAME_LENGTH, true)
+ ),
+ DictSys::Column(
+ 2,
+ "TABLE_SCHEM",
+ false,
+ SqlType(SqlType::Varchar, MAX_SCHEMA_NAME_LENGTH, true)
+ ),
+ DictSys::Column(
+ 3,
+ "TABLE_NAME",
+ false,
+ SqlType(SqlType::Varchar, MAX_TAB_NAME_SIZE, false)
+ ),
+ DictSys::Column(
+ 4,
+ "COLUMN_NAME",
+ false,
+ SqlType(SqlType::Varchar, MAX_ATTR_NAME_SIZE, false)
+ ),
+ DictSys::Column(
+ 5,
+ "KEY_SEQ",
+ false,
+ SqlType(SqlType::Integer, false)
+ ),
+ DictSys::Column(
+ 6,
+ "PK_NAME",
+ false,
+ SqlType(SqlType::Varchar, MAX_ATTR_NAME_SIZE, true)
+ )
+};
+
+static DictSys::Table
+table_ODBC_PRIMARYKEYS(
+ DictSys::OdbcPrimarykeys,
+ "ODBC$PRIMARYKEYS",
+ column_ODBC_PRIMARYKEYS,
+ arraySize(column_ODBC_PRIMARYKEYS)
+);
+
+static DictSys::Column
+column_DUAL[] = {
+ DictSys::Column(
+ 1,
+ "DUMMY",
+ false,
+ SqlType(SqlType::Varchar, 1, false)
+ )
+};
+
+static DictSys::Table
+table_DUAL(
+ DictSys::Dual,
+ "DUAL",
+ column_DUAL,
+ arraySize(column_DUAL)
+);
+
+// all tables
+
+static const DictSys::Table*
+tableList[] = {
+ &table_ODBC_TYPEINFO,
+ &table_ODBC_TABLES,
+ &table_ODBC_COLUMNS,
+ &table_ODBC_PRIMARYKEYS,
+ &table_DUAL
+};
+
+static const unsigned tableCount = arraySize(tableList);
+
+DictTable*
+DictSys::loadTable(Ctx& ctx, DictSchema* schema, const BaseString& name)
+{
+ const Table* tp = 0;
+ for (unsigned i = 0; i < tableCount; i++) {
+ if (strcmp(tableList[i]->m_name, name.c_str()) == 0) {
+ tp = tableList[i];
+ break;
+ }
+ }
+ if (tp == 0)
+ return 0;
+ DictTable* table = new DictTable(schema->m_connArea, tp->m_name, tp->m_columnCount);
+ table->sysId(tp->m_id);
+ schema->addTable(table);
+ for (unsigned position = 1; position <= tp->m_columnCount; position++) {
+ const Column* cp = &tp->m_columnList[position - 1];
+ ctx_assert(cp->m_position == position);
+ const SqlType& sqlType = cp->m_sqlType;
+ DictColumn* column = new DictColumn(table->m_connArea, cp->m_name, sqlType);
+ table->setColumn(position, column);
+ column->m_key = cp->m_key;
+ if (column->m_key)
+ table->m_keys.push_back(column);
+ }
+ ctx_log3(("%s: system table defined", name.c_str()));
+ return table;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictSys.hpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictSys.hpp
new file mode 100644
index 00000000000..e6fa661fd59
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictSys.hpp
@@ -0,0 +1,77 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DICTIONARY_DictSys_hpp
+#define ODBC_DICTIONARY_DictSys_hpp
+
+#include <common/common.hpp>
+#include <common/DataType.hpp>
+
+class Ctx;
+class DictSchema;
+class DictTable;
+class SqlType;
+
+/**
+ * @class DictSys
+ * @brief Built-in tables (replaced later by real systables)
+ */
+class DictSys {
+public:
+ enum Id {
+ Undef = 0,
+ OdbcTypeinfo = 1,
+ OdbcTables = 2,
+ OdbcColumns = 3,
+ OdbcPrimarykeys = 4,
+ Dual = 5
+ };
+ struct Column {
+ Column(unsigned position, const char* name, bool key, const SqlType& sqlType);
+ const unsigned m_position;
+ const char* const m_name;
+ const bool m_key;
+ const SqlType m_sqlType;
+ };
+ struct Table {
+ Table(Id id, const char* name, const Column* columnList, unsigned columnCount);
+ const Id m_id;
+ const char* m_name;
+ const Column* const m_columnList;
+ const unsigned m_columnCount;
+ };
+ static DictTable* loadTable(Ctx& ctx, DictSchema* schema, const BaseString& name);
+};
+
+inline
+DictSys::Column::Column(unsigned position, const char* name, bool key, const SqlType& sqlType) :
+ m_position(position),
+ m_name(name),
+ m_key(key),
+ m_sqlType(sqlType)
+{
+}
+
+inline
+DictSys::Table::Table(DictSys::Id id, const char* name, const Column* columnList, unsigned columnCount) :
+ m_id(id),
+ m_name(name),
+ m_columnList(columnList),
+ m_columnCount(columnCount)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictTable.cpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictTable.cpp
new file mode 100644
index 00000000000..4db7d3b3aec
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictTable.cpp
@@ -0,0 +1,91 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include <common/Ctx.hpp>
+#include <common/ConnArea.hpp>
+#include "DictSchema.hpp"
+#include "DictTable.hpp"
+#include "DictColumn.hpp"
+#include "DictColumn.hpp"
+
+DictTable::~DictTable()
+{
+ for (Columns::iterator i = m_columns.begin(); i != m_columns.end(); i++) {
+ delete *i;
+ *i = 0;
+ }
+ for (Indexes::iterator i = m_indexes.begin(); i != m_indexes.end(); i++) {
+ delete *i;
+ *i = 0;
+ }
+}
+
+DictColumn*
+DictTable::findColumn(const BaseString& name) const
+{
+ for (unsigned i = 1; i <= getSize(); i++) {
+ DictColumn* column = m_columns[i];
+ ctx_assert(column != 0);
+ if (strcmp(column->getName().c_str(), name.c_str()) == 0)
+ return column;
+ }
+ return 0;
+}
+
+DictColumn*
+DictTable::loadColumn(Ctx& ctx, unsigned position)
+{
+ Ndb* ndb = m_connArea.ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return 0;
+ }
+ const NdbDictionary::Table* ndbTable = ndbDictionary->getTable(m_name.c_str());
+ ctx_assert(ndbTable != 0);
+ ctx_assert(position != 0);
+ NdbAttrId attrId = position - 1;
+ const NdbDictionary::Column* ndbColumn = ndbTable->getColumn(attrId);
+ ctx_assert(ndbColumn != 0);
+ SqlType sqlType(ctx, ndbColumn);
+ if (! ctx.ok())
+ return 0;
+ DictColumn* column = new DictColumn(m_connArea, ndbColumn->getName(), sqlType);
+ setColumn(position, column);
+ column->m_key = column->m_tupleId = false;
+ if (ndbColumn->getPrimaryKey())
+ column->m_key = true;
+ if (ndbColumn->getTupleKey())
+ column->m_key = column->m_tupleId = true;
+ if (column->m_key)
+ m_keys.push_back(column);
+ // props
+ const char* value;
+ column->m_autoIncrement = false;
+ if (ndbColumn->getAutoIncrement())
+ column->m_autoIncrement = true;
+ column->m_defaultValue = 0;
+ if ((value = ndbColumn->getDefaultValue()) != 0 && strlen(value) != 0)
+ column->m_defaultValue = strcpy(new char[strlen(value) + 1], value);
+ ctx_log4(("column %u %s keyFlag=%d idFlag=%d", position, ndbColumn->getName(), column->m_key, column->m_tupleId));
+ if (column->m_tupleId)
+ m_tupleId = position;
+ if (column->m_autoIncrement)
+ m_autoIncrement = position;
+ return column;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/DictTable.hpp b/storage/ndb/src/old_files/client/odbc/dictionary/DictTable.hpp
new file mode 100644
index 00000000000..5cecfff9562
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/DictTable.hpp
@@ -0,0 +1,192 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DICTIONARY_DictTable_hpp
+#define ODBC_DICTIONARY_DictTable_hpp
+
+#include <vector>
+#include <list>
+#include <common/common.hpp>
+#include "DictColumn.hpp"
+#include "DictIndex.hpp"
+#include "DictSys.hpp"
+
+class Ctx;
+class ConnArea;
+class DictSchema;
+class DictColumn;
+class DictIndex;
+
+/**
+ * @class DictTable
+ * @brief Database table
+ */
+class DictTable {
+ friend class DictSchema;
+public:
+ DictTable(const ConnArea& connArea, const BaseString& name, unsigned size);
+ ~DictTable();
+ unsigned getSize() const;
+ void setParent(DictSchema* parent);
+ DictSchema* getParent() const;
+ void setColumn(unsigned i, DictColumn* column);
+ DictColumn* getColumn(unsigned i) const;
+ const BaseString& getName() const;
+ DictColumn* findColumn(const BaseString& name) const;
+ DictColumn* loadColumn(Ctx& ctx, unsigned position);
+ unsigned keyCount() const;
+ DictColumn* getKey(unsigned i) const;
+ unsigned tupleId() const;
+ unsigned autoIncrement() const;
+ void sysId(DictSys::Id id);
+ DictSys::Id sysId() const;
+ // indexes
+ void addIndex(DictIndex* index);
+ unsigned indexCount() const;
+ const DictIndex* getIndex(unsigned i) const; // indexed from 1
+protected:
+ friend class DictSys;
+ const ConnArea& m_connArea;
+ const BaseString m_name;
+ unsigned m_size;
+ DictSchema* m_parent;
+ typedef std::vector<DictColumn*> Columns;
+ Columns m_columns;
+ Columns m_keys;
+ unsigned m_tupleId; // tuple id column
+ unsigned m_autoIncrement; // autoincrement key
+ DictSys::Id m_sysId; // built-in system table id (if non-zero)
+ typedef std::vector<DictIndex*> Indexes;
+ Indexes m_indexes;
+};
+
+inline
+DictTable::DictTable(const ConnArea& connArea, const BaseString& name, unsigned size) :
+ m_connArea(connArea),
+ m_name(name),
+ m_size(size),
+ m_parent(0),
+ m_columns(1 + size),
+ m_keys(1), // indexed from 1
+ m_tupleId(0),
+ m_autoIncrement(0),
+ m_sysId(DictSys::Undef),
+ m_indexes(1)
+{
+}
+
+inline unsigned
+DictTable::getSize() const
+{
+ ctx_assert(m_columns.size() == 1 + m_size);
+ return m_size;
+}
+
+inline void
+DictTable::setParent(DictSchema* parent)
+{
+ m_parent = parent;
+}
+
+inline DictSchema*
+DictTable::getParent() const
+{
+ return m_parent;
+}
+
+inline void
+DictTable::setColumn(unsigned i, DictColumn* column)
+{
+ ctx_assert(1 <= i && i <= m_size);
+ m_columns[i] = column;
+ column->setPosition(i);
+ column->setParent(this);
+}
+
+inline DictColumn*
+DictTable::getColumn(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_size);
+ ctx_assert(m_columns[i] != 0);
+ return m_columns[i];
+}
+
+inline const BaseString&
+DictTable::getName() const
+{
+ return m_name;
+}
+
+inline unsigned
+DictTable::keyCount() const
+{
+ ctx_assert(m_keys.size() >= 1);
+ return m_keys.size() - 1;
+}
+
+inline DictColumn*
+DictTable::getKey(unsigned i) const
+{
+ ctx_assert(1 <= i && i <= m_keys.size() && m_keys[i] != 0);
+ return m_keys[i];
+}
+
+inline unsigned
+DictTable::tupleId() const
+{
+ return m_tupleId;
+}
+
+inline unsigned
+DictTable::autoIncrement() const
+{
+ return m_autoIncrement;
+}
+
+inline void
+DictTable::sysId(DictSys::Id id)
+{
+ m_sysId = id;
+}
+
+inline DictSys::Id
+DictTable::sysId() const
+{
+ return m_sysId;
+}
+
+inline void
+DictTable::addIndex(DictIndex* index)
+{
+ m_indexes.push_back(index);
+ index->setTable(this);
+}
+
+inline unsigned
+DictTable::indexCount() const
+{
+ ctx_assert(m_indexes.size() >= 1);
+ return m_indexes.size() - 1;
+}
+
+inline const DictIndex*
+DictTable::getIndex(unsigned i) const
+{
+ ctx_assert(1 <= i && i < m_indexes.size() && m_indexes[i] != 0);
+ return m_indexes[i];
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/dictionary/Makefile b/storage/ndb/src/old_files/client/odbc/dictionary/Makefile
new file mode 100644
index 00000000000..cdfd3b6ea0c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/dictionary/Makefile
@@ -0,0 +1,20 @@
+include .defs.mk
+
+TYPE = *
+
+NONPIC_ARCHIVE = N
+
+PIC_ARCHIVE = Y
+
+ARCHIVE_TARGET = odbcdictionary
+
+SOURCES = \
+ DictCatalog.cpp \
+ DictSchema.cpp \
+ DictTable.cpp \
+ DictColumn.cpp \
+ DictIndex.cpp \
+ DictSys.cpp
+
+include ../Extra.mk
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/client/odbc/docs/class.fig b/storage/ndb/src/old_files/client/odbc/docs/class.fig
new file mode 100644
index 00000000000..38c24c1fba4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/class.fig
@@ -0,0 +1,332 @@
+#FIG 3.2
+Landscape
+Flush left
+Inches
+A4
+100.00
+Single
+-2
+1200 2
+6 600 6600 1500 9600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 600 6600 1500 6600 1500 7200 600 7200 600 6600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 600 7800 1500 7800 1500 8400 600 8400 600 7800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 600 9000 1500 9000 1500 9600 600 9600 600 9000
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 900 9000 900 8400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 900 7800 900 7200
+4 0 0 50 0 12 12 0.0000 4 180 420 750 6825 Diag\001
+4 0 0 50 0 12 12 0.0000 4 180 420 750 8025 Diag\001
+4 0 0 50 0 12 12 0.0000 4 135 630 750 8325 Record\001
+4 0 0 50 0 12 12 0.0000 4 180 420 750 9225 Diag\001
+4 0 0 50 0 12 12 0.0000 4 135 525 750 9525 Field\001
+4 0 0 50 0 12 12 0.0000 4 120 420 750 7125 Area\001
+-6
+6 2700 6600 3600 9600
+6 2700 6600 3600 9600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 6600 3600 6600 3600 7200 2700 7200 2700 6600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2700 9000 3600 9000 3600 9600 2700 9600 2700 9000
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3000 9000 3000 7200
+4 0 0 50 0 12 12 0.0000 4 120 420 2850 6825 Attr\001
+4 0 0 50 0 12 12 0.0000 4 120 420 2850 9225 Attr\001
+4 0 0 50 0 12 12 0.0000 4 135 525 2850 9525 Field\001
+4 0 0 50 0 12 12 0.0000 4 120 420 2850 7125 Area\001
+-6
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3300 3900 4200 3900 4200 4500 3300 4500 3300 3900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3300 2700 4200 2700 4200 3300 3300 3300 3300 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3300 1500 4200 1500 4200 2100 3300 2100 3300 1500
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3300 300 4200 300 4200 900 3300 900 3300 300
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 1800 2700 2700 2700 2700 3300 1800 3300 1800 2700
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 1 2
+ 1 1 1.00 60.00 120.00
+ 3300 1800 3000 1800
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 0 1 2
+ 1 1 1.00 60.00 120.00
+ 3300 3000 3000 3000
+2 1 0 1 0 7 50 0 -1 0.000 0 0 7 0 1 2
+ 1 1 1.00 60.00 120.00
+ 3300 4200 3000 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 1 2
+ 1 1 1.00 60.00 120.00
+ 1 1 1.00 60.00 120.00
+ 4200 4200 4800 4200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3600 3900 3600 3300
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3600 2700 3600 2100
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3600 1500 3600 900
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4800 5100 5700 5100 5700 5700 4800 5700 4800 5100
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4800 2700 5700 2700 5700 3300 4800 3300 4800 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4800 3900 5700 3900 5700 4500 4800 4500 4800 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 1 2
+ 1 1 1.00 60.00 120.00
+ 1 1 1.00 60.00 120.00
+ 5100 6600 5100 5700
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 6
+ 1 1 1.00 60.00 120.00
+ 5100 5100 5100 4800 4500 4800 4500 3600 3900 3600 3900 3300
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 1 1 1.00 60.00 120.00
+ 3600 4500 3600 5400 4800 5400
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4800 6600 5700 6600 5700 7200 4800 7200 4800 6600
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4800 7800 5700 7800 5700 8400 4800 8400 4800 7800
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4800 9000 5700 9000 5700 9600 4800 9600 4800 9000
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 5100 9000 5100 8400
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 5100 7800 5100 7200
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 1 2
+ 1 1 1.00 60.00 120.00
+ 1 1 1.00 60.00 120.00
+ 4200 3000 4800 3000
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 6900 2700 7800 2700 7800 3300 6900 3300 6900 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 6900 1500 7800 1500 7800 2100 6900 2100 6900 1500
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 7200 3300 7200 4050 5700 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 11700 3300 11700 4350 5700 4350
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 5100 3900 5100 3300
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 8400 300 9300 300 9300 900 8400 900 8400 300
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 5100 2700 5100 900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2
+ 3000 6600 3000 1800
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 6900 5100 7800 5100 7800 6000 6900 6000 6900 5100
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 8400 5100 9300 5100 9300 6000 8400 6000 8400 5100
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 9900 5100 10800 5100 10800 6000 9900 6000 9900 5100
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 11400 5100 12300 5100 12300 6000 11400 6000 11400 5100
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7800 5550 8400 5550
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9300 5550 9900 5550
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7500 5100 7500 3300
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 10500 5100 10500 3300
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 12000 5100 12000 3300
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 9900 2700 10800 2700 10800 3300 9900 3300 9900 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 11400 2700 12300 2700 12300 3300 11400 3300 11400 2700
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 11400 1500 12300 1500 12300 2100 11400 2100 11400 1500
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 9900 1500 10800 1500 10800 2100 9900 2100 9900 1500
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 6900 6600 7800 6600 7800 7200 6900 7200 6900 6600
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 6900 7800 7800 7800 7800 8400 6900 8400 6900 7800
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 8400 6600 9300 6600 9300 7200 8400 7200 8400 6600
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 8400 7800 9300 7800 9300 8400 8400 8400 8400 7800
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 4
+ 0 0 1.00 60.00 120.00
+ 5700 3000 6300 3000 6300 6900 6900 6900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 4
+ 0 0 1.00 60.00 120.00
+ 5700 4200 6000 4200 6000 8100 6900 8100
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 8400 6900 7800 6900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 8400 8100 7800 8100
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9900 6900 9300 6900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9900 8100 9300 8100
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 11400 6900 10800 6900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 11400 8100 10800 8100
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 11400 6600 12300 6600 12300 7200 11400 7200 11400 6600
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 11400 7800 12300 7800 12300 8400 11400 8400 11400 7800
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 9900 6600 10800 6600 10800 7200 9900 7200 9900 6600
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 9900 7800 10800 7800 10800 8400 9900 8400 9900 7800
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 8400 2700 9300 2700 9300 3300 8400 3300 8400 2700
+2 2 0 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 8400 1500 9300 1500 9300 2100 8400 2100 8400 1500
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9000 5100 9000 3300
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7800 3000 8400 3000
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9300 3000 9900 3000
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7800 1800 8400 1800
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 9300 1800 9900 1800
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 11400 3000 10800 3000
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 11400 1800 10800 1800
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 11400 5550 10800 5550
+2 4 0 2 0 7 50 0 -1 6.000 0 0 7 0 0 5
+ 5700 900 5700 300 4800 300 4800 900 5700 900
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 12900 6600 13800 6600 13800 7200 12900 7200 12900 6600
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 12900 5100 13800 5100 13800 5700 12900 5700 12900 5100
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 13200 7800 13200 7200
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 13200 6600 13200 5700
+2 2 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 5
+ 12900 7800 13800 7800 13800 8400 12900 8400 12900 7800
+2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 3
+ 0 0 1.00 60.00 120.00
+ 13200 5100 13200 1800 12300 1800
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 1 4
+ 0 0 1.00 60.00 120.00
+ 0 0 1.00 60.00 120.00
+ 5700 7050 6600 7050 6600 7875 6900 7875
+4 0 0 50 0 12 12 0.0000 4 135 630 3375 525 Handle\001
+4 0 0 50 0 12 12 0.0000 4 135 630 3375 1725 Handle\001
+4 0 0 50 0 12 12 0.0000 4 135 630 3375 2925 Handle\001
+4 0 0 50 0 12 12 0.0000 4 135 630 3375 4125 Handle\001
+4 0 0 50 0 12 12 0.0000 4 120 420 3450 825 Root\001
+4 0 0 50 0 12 12 0.0000 4 120 315 3450 2025 Env\001
+4 0 0 50 0 12 12 0.0000 4 135 315 3450 3225 Dbc\001
+4 0 0 50 0 12 12 0.0000 4 120 420 3450 4425 Stmt\001
+4 0 0 50 0 12 12 0.0000 4 135 630 1875 2925 Handle\001
+4 0 0 50 0 12 12 0.0000 4 120 420 1950 3225 Base\001
+4 0 0 50 0 12 12 0.0000 4 135 630 4875 5325 Handle\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4950 5625 Desc\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4875 3225 Area\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4875 2925 Conn\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4875 4425 Area\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4875 4125 Stmt\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4950 6825 Desc\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4950 8025 Desc\001
+4 0 0 50 0 12 12 0.0000 4 135 630 4950 8325 Record\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4950 9225 Desc\001
+4 0 0 50 0 12 12 0.0000 4 120 420 4950 7125 Area\001
+4 0 0 50 0 12 12 0.0000 4 135 525 4950 9525 Field\001
+4 0 0 50 0 12 12 0.0000 4 135 735 3675 5925 ird ard\001
+4 0 0 50 0 12 12 0.0000 4 180 735 3675 5625 ipd apd\001
+4 0 0 50 0 12 12 0.0000 4 135 420 6975 2925 Plan\001
+4 0 0 50 0 12 12 0.0000 4 120 420 6975 3225 root\001
+4 0 0 50 0 12 12 0.0000 4 135 420 6975 1725 Plan\001
+4 0 0 50 0 12 12 0.0000 4 120 420 6975 2025 Tree\001
+4 0 0 50 0 12 12 0.0000 4 120 420 8475 525 Base\001
+4 0 0 50 0 12 12 0.0000 4 120 420 8475 825 Tree\001
+4 0 0 50 0 12 12 0.0000 4 120 315 5025 675 NDB\001
+4 0 0 50 0 14 14 0.0000 4 195 1755 300 525 Class Diagram\001
+4 0 0 50 0 12 12 0.0000 4 135 420 6975 5325 Plan\001
+4 0 0 50 0 12 12 0.0000 4 135 630 6975 5625 delete\001
+4 0 0 50 0 12 12 0.0000 4 135 840 6975 5925 searched\001
+4 0 0 50 0 12 12 0.0000 4 135 420 8475 5325 Plan\001
+4 0 0 50 0 12 12 0.0000 4 135 630 8475 5625 delete\001
+4 0 0 50 0 12 12 0.0000 4 135 420 8475 5925 full\001
+4 0 0 50 0 12 12 0.0000 4 135 420 9975 5325 Code\001
+4 0 0 50 0 12 12 0.0000 4 135 630 9975 5625 delete\001
+4 0 0 50 0 12 12 0.0000 4 135 420 9975 5925 full\001
+4 0 0 50 0 12 12 0.0000 4 120 315 11475 5325 Run\001
+4 0 0 50 0 12 12 0.0000 4 135 630 11475 5625 delete\001
+4 0 0 50 0 12 12 0.0000 4 135 420 11475 5925 full\001
+4 0 0 50 0 12 12 0.0000 4 120 420 9975 3225 root\001
+4 0 0 50 0 12 12 0.0000 4 120 315 11475 2925 Run\001
+4 0 0 50 0 12 12 0.0000 4 120 420 11475 3225 root\001
+4 0 0 50 0 12 12 0.0000 4 120 315 11475 1725 Run\001
+4 0 0 50 0 12 12 0.0000 4 120 420 11475 2025 Tree\001
+4 0 0 50 0 12 12 0.0000 4 135 420 9975 1725 Code\001
+4 0 0 50 0 12 12 0.0000 4 120 420 9975 2025 Tree\001
+4 0 0 50 0 12 12 0.0000 4 135 420 9975 2925 Code\001
+4 0 0 50 0 12 12 0.0000 4 120 420 6975 6825 Conn\001
+4 0 0 50 0 12 12 0.0000 4 180 735 6975 7125 Catalog\001
+4 0 0 50 0 12 12 0.0000 4 120 420 6975 8025 Stmt\001
+4 0 0 50 0 12 12 0.0000 4 180 735 6975 8325 Catalog\001
+4 0 0 50 0 12 12 0.0000 4 120 420 8475 6825 Conn\001
+4 0 0 50 0 12 12 0.0000 4 120 420 8475 8025 Stmt\001
+4 0 0 50 0 12 12 0.0000 4 135 630 8475 7125 Schema\001
+4 0 0 50 0 12 12 0.0000 4 135 630 8475 8325 Schema\001
+4 0 0 50 0 12 12 0.0000 4 120 420 11475 6825 Conn\001
+4 0 0 50 0 12 12 0.0000 4 120 420 11475 8025 Stmt\001
+4 0 0 50 0 12 12 0.0000 4 135 630 11475 7125 Column\001
+4 0 0 50 0 12 12 0.0000 4 135 525 11475 8325 Field\001
+4 0 0 50 0 12 12 0.0000 4 120 420 9975 6825 Conn\001
+4 0 0 50 0 12 12 0.0000 4 120 420 9975 8025 Stmt\001
+4 0 0 50 0 12 12 0.0000 4 135 525 9975 7125 Table\001
+4 0 0 50 0 12 12 0.0000 4 120 315 9975 8325 Row\001
+4 0 0 50 0 12 12 0.0000 4 135 420 8475 1725 Plan\001
+4 0 0 50 0 12 12 0.0000 4 120 420 8475 2025 Tree\001
+4 0 0 50 0 12 12 0.0000 4 135 420 8475 2925 Plan\001
+4 0 0 50 0 12 12 0.0000 4 120 420 8475 3225 root\001
+4 0 0 50 0 14 11 0.0000 4 180 840 675 825 (prelim)\001
+4 0 0 50 0 12 12 0.0000 4 180 525 6375 1350 input\001
+4 0 0 50 0 12 12 0.0000 4 180 840 7725 1350 optimize\001
+4 0 0 50 0 12 12 0.0000 4 165 630 9375 1350 output\001
+4 0 0 50 0 12 12 0.0000 4 120 735 10650 1350 execute\001
+4 0 0 50 0 12 12 0.0000 4 135 525 12075 1350 fetch\001
+4 0 0 50 0 12 12 0.0000 4 135 630 13050 5325 Result\001
+4 0 0 50 0 12 12 0.0000 4 120 315 13050 5625 Set\001
+4 0 0 50 0 12 12 0.0000 4 135 630 13050 6825 Result\001
+4 0 0 50 0 12 12 0.0000 4 120 315 13125 7125 Row\001
+4 0 0 50 0 12 12 0.0000 4 135 630 13050 8025 Result\001
+4 0 0 50 0 12 12 0.0000 4 135 525 13050 8325 Field\001
diff --git a/storage/ndb/src/old_files/client/odbc/docs/descfield.pl b/storage/ndb/src/old_files/client/odbc/docs/descfield.pl
new file mode 100644
index 00000000000..80fef22f303
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/descfield.pl
@@ -0,0 +1,1482 @@
+# usage perl Desc.data
+# prints template for DescSpec.cpp
+use strict;
+my $order = 0;
+
+# XXX do it later
+
+#
+# odbcsqlsetdescfield.htm
+#
+my $descSpec = {
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLSetDescField</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlsetdescfield"></A>SQLSetDescField</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 3.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLSetDescField</B> sets the value of a single field of a descriptor record.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLSetDescField</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHDESC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>DescriptorHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>RecNumber</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>FieldIdentifier</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLPOINTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ValuePtr</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>BufferLength</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>DescriptorHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Descriptor handle.</dd>
+#
+# <DT><I>RecNumber</I></DT>
+#
+# <DD>[Input]<BR>
+# Indicates the descriptor record containing the field that the application seeks to set. Descriptor records are numbered from 0, with record number 0 being the bookmark record. The <I>RecNumber</I> argument is ignored for header fields.</dd>
+#
+# <DT><I>FieldIdentifier</I></DT>
+#
+# <DD>[Input]<BR>
+# Indicates the field of the descriptor whose value is to be set. For more information, see "<I>FieldIdentifier</I> Argument" in the "Comments" section.</dd>
+#
+# <DT><I>ValuePtr</I></DT>
+#
+# <DD>[Input]<BR>
+# Pointer to a buffer containing the descriptor information, or a 4-byte value. The data type depends on the value of <I>FieldIdentifier</I>. If <I>ValuePtr</I> is a 4-byte value, either all four of the bytes are used or just two of the four are used, depending on the value of the <I>FieldIdentifier</I> argument.</dd>
+#
+# <DT><I>BufferLength</I></DT>
+#
+# <DD>[Input]<BR>
+# If <I>FieldIdentifier</I> is an ODBC-defined field and <I>ValuePtr</I> points to a character string or a binary buffer, this argument should be the length of *<I>ValuePtr</I>. If <I>FieldIdentifier</I> is an ODBC-defined field and <I>ValuePtr</I> is an integer, <I>BufferLength</I> is ignored.
+#
+# <P>If <I>FieldIdentifier</I> is a driver-defined field, the application indicates the nature of the field to the Driver Manager by setting the <I>BufferLength</I> argument. <I>BufferLength</I> can have the following values:
+#
+#
+# <UL type=disc>
+# <LI>If <I>ValuePtr</I> is a pointer to a character string, then <I>BufferLength</I> is the length of the string or SQL_NTS.</li>
+#
+# <LI>If <I>ValuePtr</I> is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(<I>length</I>) macro in <I>BufferLength</I>. This places a negative value in <I>BufferLength</I>.</li>
+#
+# <LI>If <I>ValuePtr</I> is a pointer to a value other than a character string or a binary string, then <I>BufferLength</I> should have the value SQL_IS_POINTER. </li>
+#
+# <LI>If <I>ValuePtr</I> contains a fixed-length value, then <I>BufferLength</I> is either SQL_IS_INTEGER, SQL_IS_UINTEGER, SQL_IS_SMALLINT, or SQL_IS_USMALLINT, as appropriate.</li>
+# </UL>
+# </dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLSetDescField</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_DESC and a <I>Handle</I> of <I>DescriptorHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLSetDescField</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=16%>SQLSTATE</TH>
+# <TH width=30%>Error</TH>
+# <TH width=54%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>01000</TD>
+# <TD width=30%>General warning</TD>
+# <TD width=54%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>01S02</TD>
+# <TD width=30%>Option value changed</TD>
+# <TD width=54%>The driver did not support the value specified in <I>*ValuePtr</I> (if <I>ValuePtr</I> was a pointer) or the value in <I>ValuePtr</I> (if <I>ValuePtr </I>was a 4-byte value), or <I>*ValuePtr</I> was invalid because of implementation working conditions, so the driver substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>07009</TD>
+# <TD width=30%>Invalid descriptor index</TD>
+# <TD width=54%>The <I>FieldIdentifier</I> argument was a record field, the <I>RecNumber</I> argument was 0, and the <I>DescriptorHandle</I> argument referred to an IPD handle.
+# <P>The <I>RecNumber</I> argument was less than 0, and the <I>DescriptorHandle</I> argument referred to an ARD or an APD.</P>
+#
+# <P>The <I>RecNumber</I> argument was greater than the maximum number of columns or parameters that the data source can support, and the <I>DescriptorHandle</I> argument referred to an APD or ARD.</P>
+#
+# <P>(DM) The <I>FieldIdentifier</I> argument was SQL_DESC_COUNT, and <I>*ValuePtr</I> argument was less than 0.</P>
+#
+# <P>The <I>RecNumber</I> argument was equal to 0, and the <I>DescriptorHandle</I> argument referred to an implicitly allocated APD. (This error does not occur with an explicitly allocated application descriptor, because it is not known whether an explicitly allocated application descriptor is an APD or ARD until execute time.)</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>08S01</TD>
+# <TD width=30%>Communication link failure</TD>
+# <TD width=54%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>22001</TD>
+# <TD width=30%>String data, right <BR>
+# truncated</TD>
+# <TD width=54%>The <I>FieldIdentifier</I> argument was SQL_DESC_NAME, and the <I>BufferLength</I> argument was a value larger than SQL_MAX_IDENTIFIER_LEN.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY000</TD>
+# <TD width=30%>General error</TD>
+# <TD width=54%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY001</TD>
+# <TD width=30%>Memory allocation <BR>
+# error</TD>
+# <TD width=54%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY010</TD>
+# <TD width=30%>Function sequence error</TD>
+# <TD width=54%>(DM) The <I>DescriptorHandle</I> was associated with a <I>StatementHandle</I> for which an asynchronously executing function (not this one) was called and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> with which the <I>DescriptorHandle</I> was associated and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY013</TD>
+# <TD width=30%>Memory management error</TD>
+# <TD width=54%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY016</TD>
+# <TD width=30%>Cannot modify an implementation row descriptor</TD>
+# <TD width=54%>The <I>DescriptorHandle</I> argument was associated with an IRD, and the <I>FieldIdentifier</I> argument was not SQL_DESC_ARRAY_STATUS_PTR or SQL_DESC_ROWS_PROCESSED_PTR.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY021</TD>
+# <TD width=30%>Inconsistent descriptor information</TD>
+# <TD width=54%>The SQL_DESC_TYPE and SQL_DESC_DATETIME_INTERVAL_CODE fields do not form a valid ODBC SQL type or a valid driver-specific SQL type (for IPDs) or a valid ODBC C type (for APDs or ARDs).
+# <P>Descriptor information checked during a consistency check was not consistent. (See "Consistency Check" in <B>SQLSetDescRec</B>.)</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY090</TD>
+# <TD width=30%>Invalid string or buffer length</TD>
+# <TD width=54%>(DM) <I>*ValuePtr</I> is a character string, and <I>BufferLength</I> was less than zero but was not equal to SQL_NTS.
+# <P>(DM) The driver was an ODBC 2<I>.x</I> driver, the descriptor was an ARD, the <I>ColumnNumber</I> argument was set to 0, and the value specified for the argument <I>BufferLength</I> was not equal to 4. </P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY091</TD>
+# <TD width=30%>Invalid descriptor field identifier</TD>
+# <TD width=54%>The value specified for the <I>FieldIdentifier</I> argument was not an ODBC-defined field and was not an implementation-defined value.
+# <P>The <I>FieldIdentifier</I> argument was invalid for the <I>DescriptorHandle</I> argument.</P>
+#
+# <P>The <I>FieldIdentifier</I> argument was a read-only, ODBC-defined field.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY092</TD>
+# <TD width=30%>Invalid attribute/option identifier</TD>
+# <TD width=54%>The value in <I>*ValuePtr</I> was not valid for the <I>FieldIdentifier</I> argument.
+# <P>The <I>FieldIdentifier</I> argument was SQL_DESC_UNNAMED, and <I>ValuePtr</I> was SQL_NAMED.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HY105</TD>
+# <TD width=30%>Invalid parameter type</TD>
+# <TD width=54%>(DM) The value specified for the SQL_DESC_PARAMETER_TYPE field was invalid. (For more information, see the "<I>InputOutputType</I> Argument" section in <B>SQLBindParameter</B>.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>HYT01</TD>
+# <TD width=30%>Connection timeout expired</TD>
+# <TD width=54%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=16%>IM001</TD>
+# <TD width=30%>Driver does not support this function</TD>
+# <TD width=54%>(DM) The driver associated with the <I>DescriptorHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P>An application can call <B>SQLSetDescField</B> to set any descriptor field one at a time. One call to <B>SQLSetDescField</B> sets a single field in a single descriptor. This function can be called to set any field in any descriptor type, provided the field can be set. (See the table later in this section.)</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;If a call to <B>SQLSetDescField</B> fails, the contents of the descriptor record identified by the <I>RecNumber</I> argument are undefined.</P>
+#
+# <P>Other functions can be called to set multiple descriptor fields with a single call of the function. The <B>SQLSetDescRec</B> function sets a variety of fields that affect the data type and buffer bound to a column or parameter (the SQL_DESC_TYPE, SQL_DESC_DATETIME_INTERVAL_CODE, SQL_DESC_OCTET_LENGTH, SQL_DESC_PRECISION, SQL_DESC_SCALE, SQL_DESC_DATA_PTR, SQL_DESC_OCTET_LENGTH_PTR, and SQL_DESC_INDICATOR_PTR fields). <B>SQLBindCol </B>or <B>SQLBindParameter</B> can be used to make a complete specification for the binding of a column or parameter. These functions set a specific group of descriptor fields with one function call.</P>
+#
+# <P><B>SQLSetDescField</B> can be called to change the binding buffers by adding an offset to the binding pointers (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, or SQL_DESC_OCTET_LENGTH_PTR). This changes the binding buffers without calling <B>SQLBindCol </B>or <B>SQLBindParameter</B>, which allows an application to change SQL_DESC_DATA_PTR without changing other fields, such as SQL_DESC_DATA_TYPE.</P>
+#
+# <P>If an application calls <B>SQLSetDescField</B> to set any field other than SQL_DESC_COUNT or the deferred fields SQL_DESC_DATA_PTR, SQL_DESC_OCTET_LENGTH_PTR, or SQL_DESC_INDICATOR_PTR, the record becomes unbound.</P>
+#
+# <P>Descriptor header fields are set by calling <B>SQLSetDescField </B>with the appropriate <I>FieldIdentifier</I>. Many header fields are also statement attributes, so they can also be set by a call to <B>SQLSetStmtAttr</B>. This allows applications to set a descriptor field without first obtaining a descriptor handle. When <B>SQLSetDescField</B> is called to set a header field, the <I>RecNumber</I> argument is ignored.</P>
+#
+# <P>A <I>RecNumber</I> of 0 is used to set bookmark fields.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;The statement attribute SQL_ATTR_USE_BOOKMARKS should always be set before calling <B>SQLSetDescField</B> to set bookmark fields. While this is not mandatory, it is strongly recommended.</P>
+#
+# <H1>Sequence of Setting Descriptor Fields</H1>
+#
+# <P>When setting descriptor fields by calling <B>SQLSetDescField</B>, the application must follow a specific sequence:
+#
+# <OL type=1>
+# <LI>The application must first set the SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE, or SQL_DESC_DATETIME_INTERVAL_CODE field. </li>
+#
+# <LI>After one of these fields has been set, the application can set an attribute of a data type, and the driver sets data type attribute fields to the appropriate default values for the data type. Automatic defaulting of type attribute fields ensures that the descriptor is always ready to use once the application has specified a data type. If the application explicitly sets a data type attribute, it is overriding the default attribute.</li>
+#
+# <LI>After one of the fields listed in step 1 has been set, and data type attributes have been set, the application can set SQL_DESC_DATA_PTR. This prompts a consistency check of descriptor fields. If the application changes the data type or attributes after setting the SQL_DESC_DATA_PTR field, the driver sets SQL_DESC_DATA_PTR to a null pointer, unbinding the record. This forces the application to complete the proper steps in sequence, before the descriptor record is usable.</li>
+# </OL>
+#
+# <H1>Initialization of Descriptor Fields</H1>
+#
+# <P>When a descriptor is allocated, the fields in the descriptor can be initialized to a default value, be initialized without a default value, or be undefined for the type of descriptor. The following tables indicate the initialization of each field for each type of descriptor, with "D" indicating that the field is initialized with a default, and "ND" indicating that the field is initialized without a default. If a number is shown, the default value of the field is that number. The tables also indicate whether a field is read/write (R/W) or read-only (R). </P>
+#
+# <P>The fields of an IRD have a default value only after the statement has been prepared or executed and the IRD has been populated, not when the statement handle or descriptor has been allocated. Until the IRD has been populated, any attempt to gain access to a field of an IRD will return an error.</P>
+#
+# <P>Some descriptor fields are defined for one or more, but not all, of the descriptor types (ARDs and IRDs, and APDs and IPDs). When a field is undefined for a type of descriptor, it is not needed by any of the functions that use that descriptor.</P>
+#
+# <P>The fields that can be accessed by <B>SQLGetDescField</B> cannot necessarily be set by <B>SQLSetDescField</B>. Fields that can be set by <B>SQLSetDescField</B> are listed in the following tables.</P>
+#
+# <P>The initialization of header fields is outlined in the table that follows.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=27%>Header field name</TH>
+# <TH width=21%>Type</TH>
+# <TH width=19%>R/W</TH>
+# <TH width=33%>Default</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_ALLOC_TYPE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R<BR>
+# APD: R<BR>
+# IRD: R<BR>
+# IPD: R </TD>
+# <TD width=33%>ARD: SQL_DESC_ALLOC_AUTO for implicit or SQL_DESC_ALLOC_USER for explicit
+# <P>APD: SQL_DESC_ALLOC_AUTO for implicit or SQL_DESC_ALLOC_USER for explicit</P>
+#
+# <P>IRD: SQL_DESC_ALLOC_AUTO</P>
+#
+# <P>IPD: SQL_DESC_ALLOC_AUTO</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_ARRAY_SIZE</TD>
+# <TD width=21%>SQLUINTEGER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD:<SUP>[1]</SUP><BR>
+# APD:<SUP>[1]</SUP><BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_ARRAY_STATUS_PTR</TD>
+# <TD width=21%>SQLUSMALLINT*</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R/W<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: Null ptr<BR>
+# APD: Null ptr<BR>
+# IRD: Null ptr<BR>
+# IPD: Null ptr</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_BIND_OFFSET_PTR</TD>
+# <TD width=21%>SQLINTEGER*</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Null ptr<BR>
+# APD: Null ptr<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_BIND_TYPE</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: SQL_BIND_BY_COLUMN
+# <P>APD: SQL_BIND_BY_COLUMN</P>
+#
+# <P>IRD: Unused</P>
+#
+# <P>IPD: Unused</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_COUNT</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: 0<BR>
+# APD: 0<BR>
+# IRD: D<BR>
+# IPD: 0</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_ROWS_PROCESSED_PTR</TD>
+# <TD width=21%>SQLUINTEGER*</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R/W<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: Null ptr<BR>
+# IPD: Null ptr</TD>
+# </TR>
+# </table></div>
+#
+# <P class="fineprint">[1]&nbsp;&nbsp;&nbsp;These fields are defined only when the IPD is automatically populated by the driver. If not, they are undefined. If an application attempts to set these fields, SQLSTATE HY091 (Invalid descriptor field identifier) will be returned.</p>
+# <P>The initialization of record fields is as shown in the following table.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=27%>Record field name</TH>
+# <TH width=21%>Type</TH>
+# <TH width=19%>R/W</TH>
+# <TH width=33%>Default</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_AUTO_UNIQUE_VALUE</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_BASE_COLUMN_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_BASE_TABLE_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_CASE_SENSITIVE</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: D<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_CATALOG_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_CONCISE_TYPE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: SQL_C_<BR>
+# DEFAULT<BR>
+# APD: SQL_C_<BR>
+# DEFAULT<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_DATA_PTR</TD>
+# <TD width=21%>SQLPOINTER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Null ptr<BR>
+# APD: Null ptr<BR>
+# IRD: Unused<BR>
+# IPD: Unused<SUP>[2]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_DATETIME_INTERVAL_CODE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_DATETIME_INTERVAL_PRECISION</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_DISPLAY_SIZE</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_FIXED_PREC_SCALE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: D<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_INDICATOR_PTR</TD>
+# <TD width=21%>SQLINTEGER *</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Null ptr<BR>
+# APD: Null ptr<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_LABEL</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_LENGTH</TD>
+# <TD width=21%>SQLUINTEGER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_LITERAL_PREFIX</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_LITERAL_SUFFIX</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_LOCAL_TYPE_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: D<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_NULLABLE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_NUM_PREC_RADIX</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_OCTET_LENGTH</TD>
+# <TD width=21%>SQLINTEGER</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_OCTET_LENGTH_PTR</TD>
+# <TD width=21%>SQLINTEGER *</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Null ptr<BR>
+# APD: Null ptr<BR>
+# IRD: Unused<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_PARAMETER_TYPE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: Unused<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: Unused<BR>
+# IPD: D=SQL_PARAM_INPUT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_PRECISION</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_ROWVER</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused
+# <P>APD: Unused</P>
+#
+# <P>IRD: R</P>
+#
+# <P>IPD: R</P>
+# </TD>
+# <TD width=33%>ARD: Unused
+# <P>APD: Unused</P>
+#
+# <P>IRD: ND</P>
+#
+# <P>IPD: ND</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_SCALE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_SCHEMA_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_SEARCHABLE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_TABLE_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_TYPE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: R/W<BR>
+# APD: R/W<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: SQL_C_DEFAULT<BR>
+# APD: SQL_C_DEFAULT<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_TYPE_NAME</TD>
+# <TD width=21%>SQLCHAR *</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: D<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_UNNAMED</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R/W</TD>
+# <TD width=33%>ARD: ND<BR>
+# APD: ND<BR>
+# IRD: D<BR>
+# IPD: ND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_UNSIGNED</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: R</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: D<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>SQL_DESC_UPDATABLE</TD>
+# <TD width=21%>SQLSMALLINT</TD>
+# <TD width=19%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: R<BR>
+# IPD: Unused</TD>
+# <TD width=33%>ARD: Unused<BR>
+# APD: Unused<BR>
+# IRD: D<BR>
+# IPD: Unused</TD>
+# </TR>
+# </table></div>
+#
+# <P class="fineprint">[1]&nbsp;&nbsp;&nbsp;These fields are defined only when the IPD is automatically populated by the driver. If not, they are undefined. If an application attempts to set these fields, SQLSTATE HY091 (Invalid descriptor field identifier) will be returned.</p>
+# <P class="fineprint">[2]&nbsp;&nbsp;&nbsp;The SQL_DESC_DATA_PTR field in the IPD can be set to force a consistency check. In a subsequent call to <B>SQLGetDescField</B> or <B>SQLGetDescRec</B>, the driver is not required to return the value that SQL_DESC_DATA_PTR was set to.</p>
+# <P class="label"><B><I>FieldIdentifier</I> Argument</B></P>
+#
+# <P>The <I>FieldIdentifier</I> argument indicates the descriptor field to be set. A descriptor contains the <I>descriptor header,</I> consisting of the header fields described in the next section, "Header Fields," and zero or more <I>descriptor records,</I> consisting of the record fields described in the section following the "Header Fields" section.</P>
+#
+# <H1>Header Fields</H1>
+#
+# <P>Each descriptor has a header consisting of the following fields:
+#
+# <DL>
+# <DT><B>SQL_DESC_ALLOC_TYPE [All]</B></DT>
+#
+# <DD>This read-only SQLSMALLINT header field specifies whether the descriptor was allocated automatically by the driver or explicitly by the application. The application can obtain, but not modify, this field. The field is set to SQL_DESC_ALLOC_AUTO by the driver if the descriptor was automatically allocated by the driver. It is set to SQL_DESC_ALLOC_USER by the driver if the descriptor was explicitly allocated by the application.</dd>
+#
+# <DT><B>SQL_DESC_ARRAY_SIZE [Application descriptors]</B></DT>
+#
+# <DD>In ARDs, this SQLUINTEGER header field specifies the number of rows in the rowset. This is the number of rows to be returned by a call to <B>SQLFetch</B> or <B>SQLFetchScroll</B> or to be operated on by a call to <B>SQLBulkOperations</B> or <B>SQLSetPos</B>.
+#
+# <P>In APDs, this SQLUINTEGER header field specifies the number of values for each parameter.
+#
+#
+# <P>The default value of this field is 1. If SQL_DESC_ARRAY_SIZE is greater than 1, SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR of the APD or ARD point to arrays. The cardinality of each array is equal to the value of this field.
+#
+#
+# <P>This field in the ARD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_ROW_ARRAY_SIZE attribute. This field in the APD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_PARAMSET_SIZE attribute.
+# </dd>
+#
+# <DT><B>SQL_DESC_ARRAY_STATUS_PTR [All]</B></DT>
+#
+# <DD>For each descriptor type, this SQLUSMALLINT * header field points to an array of SQLUSMALLINT values. These arrays are named as follows: row status array (IRD), parameter status array (IPD), row operation array (ARD), and parameter operation array (APD).
+#
+# <P>In the IRD, this header field points to a row status array containing status values after a call to <B>SQLBulkOperations</B>, <B>SQLFetch</B>, <B>SQLFetchScroll</B>, or <B>SQLSetPos</B>. The array has as many elements as there are rows in the rowset. The application must allocate an array of SQLUSMALLINTs and set this field to point to the array. The field is set to a null pointer by default. The driver will populate the array&#0151;unless the SQL_DESC_ARRAY_STATUS_PTR field is set to a null pointer, in which case no status values are generated and the array is not populated.
+#
+#
+# <P class="indent"><b class="le">Caution</b>&nbsp;&nbsp;&nbsp;Driver behavior is undefined if the application sets the elements of the row status array pointed to by the SQL_DESC_ARRAY_STATUS_PTR field of the IRD.
+#
+#
+# <P>The array is initially populated by a call to <B>SQLBulkOperations</B>, <B>SQLFetch</B>, <B>SQLFetchScroll</B>, or <B>SQLSetPos</B>. If the call did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the array pointed to by this field are undefined. The elements in the array can contain the following values:
+#
+#
+# <UL type=disc>
+# <LI>SQL_ROW_SUCCESS: The row was successfully fetched and has not changed since it was last fetched.</li>
+#
+# <LI>SQL_ROW_SUCCESS_WITH_INFO: The row was successfully fetched and has not changed since it was last fetched. However, a warning was returned about the row.</li>
+#
+# <LI>SQL_ROW_ERROR: An error occurred while fetching the row.</li>
+#
+# <LI>SQL_ROW_UPDATED: The row was successfully fetched and has been updated since it was last fetched. If the row is fetched again, its status is SQL_ROW_SUCCESS.</li>
+#
+# <LI>SQL_ROW_DELETED: The row has been deleted since it was last fetched.</li>
+#
+# <LI>SQL_ROW_ADDED: The row was inserted by <B>SQLBulkOperations</B>. If the row is fetched again, its status is SQL_ROW_SUCCESS.</li>
+#
+# <LI>SQL_ROW_NOROW: The rowset overlapped the end of the result set, and no row was returned that corresponded to this element of the row status array.</li>
+# </UL>
+#
+#
+# <P>This field in the IRD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_ROW_STATUS_PTR attribute.
+#
+#
+# <P>The SQL_DESC_ARRAY_STATUS_PTR field of the IRD is valid only after SQL_SUCCESS or SQL_SUCCESS_WITH_INFO has been returned. If the return code is not one of these, the location pointed to by SQL_DESC_ROWS_PROCESSED_PTR is undefined.
+#
+#
+# <P>In the IPD, this header field points to a parameter status array containing status information for each set of parameter values after a call to <B>SQLExecute</B> or <B>SQLExecDirect</B>. If the call to <B>SQLExecute</B> or <B>SQLExecDirect</B> did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the array pointed to by this field are undefined. The application must allocate an array of SQLUSMALLINTs and set this field to point to the array. The driver will populate the array&#0151;unless the SQL_DESC_ARRAY_STATUS_PTR field is set to a null pointer, in which case no status values are generated and the array is not populated. The elements in the array can contain the following values:
+#
+#
+# <UL type=disc>
+# <LI>SQL_PARAM_SUCCESS: The SQL statement was successfully executed for this set of parameters.</li>
+#
+# <LI>SQL_PARAM_SUCCESS_WITH_INFO: The SQL statement was successfully executed for this set of parameters; however, warning information is available in the diagnostics data structure.</li>
+#
+# <LI>SQL_PARAM_ERROR: An error occurred in processing this set of parameters. Additional error information is available in the diagnostics data structure.</li>
+#
+# <LI>SQL_PARAM_UNUSED: This parameter set was unused, possibly due to the fact that some previous parameter set caused an error that aborted further processing, or because SQL_PARAM_IGNORE was set for that set of parameters in the array specified by the SQL_DESC_ARRAY_STATUS_PTR field of the APD.</li>
+#
+# <LI>SQL_PARAM_DIAG_UNAVAILABLE: Diagnostic information is not available. An example of this is when the driver treats arrays of parameters as a monolithic unit and so does not generate this level of error information.</li>
+# </UL>
+#
+#
+# <P>This field in the IPD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_PARAM_STATUS_PTR attribute.
+#
+#
+# <P>In the ARD, this header field points to a row operation array of values that can be set by the application to indicate whether this row is to be ignored for <B>SQLSetPos</B> operations. The elements in the array can contain the following values:
+#
+#
+# <UL type=disc>
+# <LI>SQL_ROW_PROCEED: The row is included in the bulk operation using <B>SQLSetPos</B>. (This setting does not guarantee that the operation will occur on the row. If the row has the status SQL_ROW_ERROR in the IRD row status array, the driver might not be able to perform the operation in the row.)</li>
+#
+# <LI>SQL_ROW_IGNORE: The row is excluded from the bulk operation using <B>SQLSetPos</B>.</li>
+# </UL>
+#
+#
+# <P>If no elements of the array are set, all rows are included in the bulk operation. If the value in the SQL_DESC_ARRAY_STATUS_PTR field of the ARD is a null pointer, all rows are included in the bulk operation; the interpretation is the same as if the pointer pointed to a valid array and all elements of the array were SQL_ROW_PROCEED. If an element in the array is set to SQL_ROW_IGNORE, the value in the row status array for the ignored row is not changed.
+#
+#
+# <P>This field in the ARD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_ROW_OPERATION_PTR attribute.
+#
+#
+# <P>In the APD, this header field points to a parameter operation array of values that can be set by the application to indicate whether this set of parameters is to be ignored when <B>SQLExecute</B> or<B> SQLExecDirect</B> is called. The elements in the array can contain the following values:
+#
+#
+# <UL type=disc>
+# <LI>SQL_PARAM_PROCEED: The set of parameters is included in the <B>SQLExecute</B> or <B>SQLExecDirect</B> call.</li>
+#
+# <LI>SQL_PARAM_IGNORE: The set of parameters is excluded from the <B>SQLExecute</B> or <B>SQLExecDirect</B> call.</li>
+# </UL>
+#
+#
+# <P>If no elements of the array are set, all sets of parameters in the array are used in the <B>SQLExecute</B> or <B>SQLExecDirect</B> calls. If the value in the SQL_DESC_ARRAY_STATUS_PTR field of the APD is a null pointer, all sets of parameters are used; the interpretation is the same as if the pointer pointed to a valid array and all elements of the array were SQL_PARAM_PROCEED.
+#
+#
+# <P>This field in the APD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_PARAM_OPERATION_PTR attribute.
+# </dd>
+#
+# <DT><B>SQL_DESC_BIND_OFFSET_PTR [Application descriptors]</B></DT>
+#
+# <DD>This SQLINTEGER * header field points to the binding offset. It is set to a null pointer by default. If this field is not a null pointer, the driver dereferences the pointer and adds the dereferenced value to each of the deferred fields that has a non-null value in the descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR) at fetch time and uses the new pointer values when binding.
+#
+# <P>The binding offset is always added directly to the values in the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields. If the offset is changed to a different value, the new value is still added directly to the value in each descriptor field. The new offset is not added to the field value plus any earlier offset.
+#
+#
+# <P>This field is a <I>deferred field</I>: It is not used at the time it is set but is used at a later time by the driver when it needs to determine addresses for data buffers.
+#
+#
+# <P>This field in the ARD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_ROW_BIND_OFFSET_PTR attribute. This field in the ARD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_PARAM_BIND_OFFSET_PTR attribute.
+#
+#
+# <P>For more information, see the description of row-wise binding in <A HREF="odbcsqlfetchscroll.htm">SQLFetchScroll</A> and <A HREF="odbcsqlbindparameter.htm">SQLBindParameter</A>.
+# </dd>
+#
+# <DT><B>SQL_DESC_BIND_TYPE [Application descriptors]</B></DT>
+#
+# <DD>This SQLUINTEGER header field sets the binding orientation to be used for binding either columns or parameters.
+#
+# <P>In ARDs, this field specifies the binding orientation when <B>SQLFetchScroll</B> or <B>SQLFetch</B> is called on the associated statement handle.
+#
+#
+# <P>To select column-wise binding for columns, this field is set to SQL_BIND_BY_COLUMN (the default).
+#
+#
+# <P>This field in the ARD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_ROW_BIND_TYPE <I>Attribute</I>.
+#
+#
+# <P>In APDs, this field specifies the binding orientation to be used for dynamic parameters.
+#
+#
+# <P>To select column-wise binding for parameters, this field is set to SQL_BIND_BY_COLUMN (the default).
+#
+#
+# <P>This field in the APD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_PARAM_BIND_TYPE <I>Attribute</I>.
+# </dd>
+#
+# <DT><B>SQL_DESC_COUNT [All]</B></DT>
+#
+# <DD>This SQLSMALLINT header field specifies the 1-based index of the highest-numbered record that contains data. When the driver sets the data structure for the descriptor, it must also set the SQL_DESC_COUNT field to show how many records are significant. When an application allocates an instance of this data structure, it does not have to specify how many records to reserve room for. As the application specifies the contents of the records, the driver takes any required action to ensure that the descriptor handle refers to a data structure of the adequate size.
+#
+# <P>SQL_DESC_COUNT is not a count of all data columns that are bound (if the field is in an ARD) or of all parameters that are bound (if the field is in an APD), but the number of the highest-numbered record. If the highest-numbered column or parameter is unbound, then SQL_DESC_COUNT is changed to the number of the next highest-numbered column or parameter. If a column or a parameter with a number that is less than the number of the highest-numbered column is unbound (by calling <B>SQLBindCol</B> with the <I>TargetValuePtr</I> argument set to a null pointer, or <B>SQLBindParameter</B> with the <I>ParameterValuePtr</I> argument set to a null pointer), SQL_DESC_COUNT is not changed. If additional columns or parameters are bound with numbers greater than the highest-numbered record that contains data, the driver automatically increases the value in the SQL_DESC_COUNT field. If all columns are unbound by calling <B>SQLFreeStmt</B> with the SQL_UNBIND option, the SQL_DESC_COUNT fields in the ARD and IRD are set to 0. If <B>SQLFreeStmt</B> is called with the SQL_RESET_PARAMS option, the SQL_DESC_COUNT fields in the APD and IPD are set to 0.
+#
+#
+# <P>The value in SQL_DESC_COUNT can be set explicitly by an application by calling <B>SQLSetDescField</B>. If the value in SQL_DESC_COUNT is explicitly decreased, all records with numbers greater than the new value in SQL_DESC_COUNT are effectively removed. If the value in SQL_DESC_COUNT is explicitly set to 0 and the field is in an ARD, all data buffers except a bound bookmark column are released.
+#
+#
+# <P>The record count in this field of an ARD does not include a bound bookmark column. The only way to unbind a bookmark column is to set the SQL_DESC_DATA_PTR field to a null pointer.
+# </dd>
+#
+# <DT><B>SQL_DESC_ROWS_PROCESSED_PTR [Implementation descriptors]</B></DT>
+#
+# <DD>In an IRD, this SQLUINTEGER * header field points to a buffer containing the number of rows fetched after a call to <B>SQLFetch</B> or <B>SQLFetchScroll</B>, or the number of rows affected in a bulk operation performed by a call to <B>SQLBulkOperations</B> or <B>SQLSetPos</B>, including error rows.
+#
+# <P>In an IPD, this SQLUINTEGER * header field points to a buffer containing the number of sets of parameters that have been processed, including error sets. No number will be returned if this is a null pointer.
+#
+#
+# <P>SQL_DESC_ROWS_PROCESSED_PTR is valid only after SQL_SUCCESS or SQL_SUCCESS_WITH_INFO has been returned after a call to <B>SQLFetch</B> or <B>SQLFetchScroll </B>(for an IRD field) or <B>SQLExecute</B>, <B>SQLExecDirect</B>, or <B>SQLParamData</B> (for an IPD field). If the call that fills in the buffer pointed to by this field does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined, unless it returns SQL_NO_DATA, in which case the value in the buffer is set to 0.
+#
+#
+# <P>This field in the ARD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_ROWS_FETCHED_PTR attribute. This field in the APD can also be set by calling <B>SQLSetStmtAttr</B> with the SQL_ATTR_PARAMS_PROCESSED_PTR attribute.
+#
+#
+# <P>The buffer pointed to by this field is allocated by the application. It is a deferred output buffer that is set by the driver. It is set to a null pointer by default.
+# </dd>
+# </DL>
+#
+# <H1>Record Fields</H1>
+#
+# <P>Each descriptor contains one or more records consisting of fields that define either column data or dynamic parameters, depending on the type of descriptor. Each record is a complete definition of a single column or parameter.
+#
+# <DL>
+# <DT><B>SQL_DESC_AUTO_UNIQUE_VALUE [IRDs]</B></DT>
+#
+# <DD>This read-only SQLINTEGER record field contains SQL_TRUE if the column is an auto-incrementing column, or SQL_FALSE if the column is not an auto-incrementing column. This field is read-only, but the underlying auto-incrementing column is not necessarily read-only.</dd>
+#
+# <DT><B>SQL_DESC_BASE_COLUMN_NAME [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the base column name for the result set column. If a base column name does not exist (as in the case of columns that are expressions), this variable contains an empty string.</dd>
+#
+# <DT><B>SQL_DESC_BASE_TABLE_NAME [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the base table name for the result set column. If a base table name cannot be defined or is not applicable, this variable contains an empty string.</dd>
+#
+# <DT><B>SQL_DESC_CASE_SENSITIVE [Implementation descriptors]</B></DT>
+#
+# <DD>This read-only SQLINTEGER record field contains SQL_TRUE if the column or parameter is treated as case-sensitive for collations and comparisons, or SQL_FALSE if the column is not treated as case-sensitive for collations and comparisons or if it is a noncharacter column.</dd>
+#
+# <DT><B>SQL_DESC_CATALOG_NAME [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the catalog for the base table that contains the column. The return value is driver-dependent if the column is an expression or if the column is part of a view. If the data source does not support catalogs or the catalog cannot be determined, this variable contains an empty string.</dd>
+#
+# <DT><B>SQL_DESC_CONCISE_TYPE [All]</B></DT>
+#
+# <DD>This SQLSMALLINT header field specifies the concise data type for all data types, including the datetime and interval data types.
+#
+# <P>The values in the SQL_DESC_CONCISE_TYPE, SQL_DESC_TYPE, and SQL_DESC_DATETIME_INTERVAL_CODE fields are interdependent. Each time one of the fields is set, the other must also be set. SQL_DESC_CONCISE_TYPE can be set by a call to <B>SQLBindCol</B> or <B>SQLBindParameter</B>, or <B>SQLSetDescField</B>. SQL_DESC_TYPE can be set by a call to <B>SQLSetDescField</B> or <B>SQLSetDescRec</B>.
+#
+#
+# <P>If SQL_DESC_CONCISE_TYPE is set to a concise data type other than an interval or datetime data type, the SQL_DESC_TYPE field is set to the same value and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to 0.
+#
+#
+# <P>If SQL_DESC_CONCISE_TYPE is set to the concise datetime or interval data type, the SQL_DESC_TYPE field is set to the corresponding verbose type (SQL_DATETIME or SQL_INTERVAL) and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to the appropriate subcode.
+# </dd>
+#
+# <DT><B>SQL_DESC_DATA_PTR [Application descriptors and IPDs]</B></DT>
+#
+# <DD>This SQLPOINTER record field points to a variable that will contain the parameter value (for APDs) or the column value (for ARDs). This field is a <I>deferred field</I>. It is not used at the time it is set but is used at a later time by the driver to retrieve data.
+#
+# <P>The column specified by the SQL_DESC_DATA_PTR field of the ARD is unbound if the <I>TargetValuePtr</I> argument in a call to <B>SQLBindCol</B> is a null pointer or if the SQL_DESC_DATA_PTR field in the ARD is set by a call to <B>SQLSetDescField</B> or <B>SQLSetDescRec</B> to a null pointer. Other fields are not affected if the SQL_DESC_DATA_PTR field is set to a null pointer.
+#
+#
+# <P>If the call to <B>SQLFetch</B> or <B>SQLFetchScroll </B>that fills in the buffer pointed to by this field did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.
+#
+#
+# <P>Whenever the SQL_DESC_DATA_PTR field of an APD, ARD, or IPD is set, the driver checks that the value in the SQL_DESC_TYPE field contains one of the valid ODBC C data types or a driver-specific data type, and that all other fields affecting the data types are consistent. Prompting a consistency check is the only use of the SQL_DESC_DATA_PTR field of an IPD. Specifically, if an application sets the SQL_DESC_DATA_PTR field of an IPD and later calls <B>SQLGetDescField</B> on this field, it is not necessarily returned the value that it had set. For more information, see "Consistency Checks" in <A HREF="odbcsqlsetdescrec.htm">SQLSetDescRec</A>.
+# </dd>
+#
+# <DT><B>SQL_DESC_DATETIME_INTERVAL_CODE [All]</B></DT>
+#
+# <DD>This SQLSMALLINT record field contains the subcode for the specific datetime or interval data type when the SQL_DESC_TYPE field is SQL_DATETIME or SQL_INTERVAL. This is true for both SQL and C data types. The code consists of the data type name with "CODE" substituted for either "TYPE" or "C_TYPE" (for datetime types), or "CODE" substituted for "INTERVAL" or "C_INTERVAL" (for interval types).
+#
+# <P>If SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE in an application descriptor are set to SQL_C_DEFAULT and the descriptor is not associated with a statement handle, the contents of SQL_DESC_DATETIME_INTERVAL_CODE are undefined.
+#
+#
+# <P>This field can be set for the datetime data types listed in the following table.
+#
+# <!--TS:-->
+# <div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>Datetime types</TH>
+# <TH width=50%>DATETIME_INTERVAL_CODE</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TYPE_DATE/SQL_C_TYPE_DATE</TD>
+# <TD width=50%>SQL_CODE_DATE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TYPE_TIME/SQL_C_TYPE_TIME</TD>
+# <TD width=50%>SQL_CODE_TIME</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TYPE_TIMESTAMP/<BR>
+# SQL_C_TYPE_TIMESTAMP</TD>
+# <TD width=50%>SQL_CODE_TIMESTAMP</TD>
+# </TR>
+# </table></div>
+#
+# <!--TS:-->
+#
+# <P>This field can be set for the interval data types listed in the following table.
+#
+# <!--TS:-->
+# <div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>Interval type</TH>
+# <TH width=50%>DATETIME_INTERVAL_CODE</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_DAY/<BR>
+# SQL_C_INTERVAL_DAY</TD>
+# <TD width=50%>SQL_CODE_DAY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_DAY_TO_HOUR/<BR>
+# SQL_C_INTERVAL_DAY_TO_HOUR</TD>
+# <TD width=50%>SQL_CODE_DAY_TO_HOUR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_DAY_TO_MINUTE/<BR>
+# SQL_C_INTERVAL_DAY_TO_MINUTE</TD>
+# <TD width=50%>SQL_CODE_DAY_TO_MINUTE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_DAY_TO_SECOND/<BR>
+# SQL_C_INTERVAL_DAY_TO_SECOND</TD>
+# <TD width=50%>SQL_CODE_DAY_TO_SECOND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_HOUR/<BR>
+# SQL_C_INTERVAL_HOUR</TD>
+# <TD width=50%>SQL_CODE_HOUR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_HOUR_TO_MINUTE/<BR>
+# SQL_C_INTERVAL_HOUR_TO_MINUTE</TD>
+# <TD width=50%>SQL_CODE_HOUR_TO_MINUTE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_HOUR_TO_SECOND/<BR>
+# SQL_C_INTERVAL_HOUR_TO_SECOND</TD>
+# <TD width=50%>SQL_CODE_HOUR_TO_SECOND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_MINUTE/<BR>
+# SQL_C_INTERVAL_MINUTE</TD>
+# <TD width=50%>SQL_CODE_MINUTE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_MINUTE_TO_SECOND/<BR>
+# SQL_C_INTERVAL_MINUTE_TO_SECOND</TD>
+# <TD width=50%>SQL_CODE_MINUTE_TO_SECOND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_MONTH/<BR>
+# SQL_C_INTERVAL_MONTH</TD>
+# <TD width=50%>SQL_CODE_MONTH</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_SECOND/<BR>
+# SQL_C_INTERVAL_SECOND</TD>
+# <TD width=50%>SQL_CODE_SECOND</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_YEAR/<BR>
+# SQL_C_INTERVAL_YEAR</TD>
+# <TD width=50%>SQL_CODE_YEAR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTERVAL_YEAR_TO_MONTH/<BR>
+# SQL_C_INTERVAL_YEAR_TO_MONTH</TD>
+# <TD width=50%>SQL_CODE_YEAR_TO_MONTH</TD>
+# </TR>
+# </table></div>
+#
+# <!--TS:-->
+#
+# <P>For more information about the data intervals and this field, see "<A HREF="odbcdata_type_identifiers_and_descriptors.htm">Data Type Identifiers and Descriptors</A>" in Appendix D: Data Types.
+# </dd>
+#
+# <DT><B>SQL_DESC_DATETIME_INTERVAL_PRECISION [All]</B></DT>
+#
+# <DD>This SQLINTEGER record field contains the interval leading precision if the SQL_DESC_TYPE field is SQL_INTERVAL. When the SQL_DESC_DATETIME_INTERVAL_CODE field is set to an interval data type, this field is set to the default interval leading precision.</dd>
+#
+# <DT><B>SQL_DESC_DISPLAY_SIZE [IRDs]</B></DT>
+#
+# <DD>This read-only SQLINTEGER record field contains the maximum number of characters required to display the data from the column. </dd>
+#
+# <DT><B>SQL_DESC_FIXED_PREC_SCALE [Implementation descriptors]</B></DT>
+#
+# <DD>This read-only SQLSMALLINT record field is set to SQL_TRUE if the column is an exact numeric column and has a fixed precision and nonzero scale, or to SQL_FALSE if the column is not an exact numeric column with a fixed precision and scale.</dd>
+#
+# <DT><B>SQL_DESC_INDICATOR_PTR [Application descriptors]</B></DT>
+#
+# <DD>In ARDs, this SQLINTEGER * record field points to the indicator variable. This variable contains SQL_NULL_DATA if the column value is a NULL. For APDs, the indicator variable is set to SQL_NULL_DATA to specify NULL dynamic arguments. Otherwise, the variable is zero (unless the values in SQL_DESC_INDICATOR_PTR and SQL_DESC_OCTET_LENGTH_PTR are the same pointer).
+#
+# <P>If the SQL_DESC_INDICATOR_PTR field in an ARD is a null pointer, the driver is prevented from returning information about whether the column is NULL or not. If the column is NULL and SQL_DESC_INDICATOR_PTR is a null pointer, SQLSTATE 22002 (Indicator variable required but not supplied) is returned when the driver attempts to populate the buffer after a call to <B>SQLFetch</B> or <B>SQLFetchScroll</B>. If the call to <B>SQLFetch</B> or <B>SQLFetchScroll </B>did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.
+#
+#
+# <P>The SQL_DESC_INDICATOR_PTR field determines whether the field pointed to by SQL_DESC_OCTET_LENGTH_PTR is set. If the data value for a column is NULL, the driver sets the indicator variable to SQL_NULL_DATA. The field pointed to by SQL_DESC_OCTET_LENGTH_PTR is then not set. If a NULL value is not encountered during the fetch, the buffer pointed to by SQL_DESC_INDICATOR_PTR is set to zero and the buffer pointed to by SQL_DESC_OCTET_LENGTH_PTR is set to the length of the data.
+#
+#
+# <P>If the SQL_DESC_INDICATOR_PTR field in an APD is a null pointer, the application cannot use this descriptor record to specify NULL arguments.
+#
+#
+# <P>This field is a <I>deferred field</I>: It is not used at the time it is set but is used at a later time by the driver to indicate nullability (for ARDs) or to determine nullability (for APDs).
+# </dd>
+#
+# <DT><B>SQL_DESC_LABEL [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the column label or title. If the column does not have a label, this variable contains the column name. If the column is unnamed and unlabeled, this variable contains an empty string.</dd>
+#
+# <DT><B>SQL_DESC_LENGTH [All]</B></DT>
+#
+# <DD>This SQLUINTEGER record field is either the maximum or actual length of a character string in characters or a binary data type in bytes. It is the maximum length for a fixed-length data type, or the actual length for a variable-length data type. Its value always excludes the null-termination character that ends the character string. For values whose type is SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, or one of the SQL interval data types, this field has the length in characters of the character string representation of the datetime or interval value.
+#
+# <P>The value in this field may be different from the value for "length" as defined in ODBC 2<I>.x</I>. For more information, see <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.
+# </dd>
+#
+# <DT><B>SQL_DESC_LITERAL_PREFIX [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the character or characters that the driver recognizes as a prefix for a literal of this data type. This variable contains an empty string for a data type for which a literal prefix is not applicable.</dd>
+#
+# <DT><B>SQL_DESC_LITERAL_SUFFIX [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the character or characters that the driver recognizes as a suffix for a literal of this data type. This variable contains an empty string for a data type for which a literal suffix is not applicable.</dd>
+#
+# <DT><B>SQL_DESC_LOCAL_TYPE_NAME [Implementation descriptors]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains any localized (native language) name for the data type that may be different from the regular name of the data type. If there is no localized name, an empty string is returned. This field is for display purposes only.</dd>
+#
+# <DT><B>SQL_DESC_NAME [Implementation descriptors]</B></DT>
+#
+# <DD>This SQLCHAR * record field in a row descriptor contains the column alias, if it applies. If the column alias does not apply, the column name is returned. In either case, the driver sets the SQL_DESC_UNNAMED field to SQL_NAMED when it sets the SQL_DESC_NAME field. If there is no column name or a column alias, the driver returns an empty string in the SQL_DESC_NAME field and sets the SQL_DESC_UNNAMED field to SQL_UNNAMED.
+#
+# <P>An application can set the SQL_DESC_NAME field of an IPD to a parameter name or alias to specify stored procedure parameters by name. (For more information, see "<A HREF="odbcbinding_parameters_by_name__named_parameters_.htm">Binding Parameters by Name (Named Parameters)</A>" in Chapter 9: Executing Statements.) The SQL_DESC_NAME field of an IRD is a read-only field; SQLSTATE HY091 (Invalid descriptor field identifier) will be returned if an application attempts to set it.
+#
+#
+# <P>In IPDs, this field is undefined if the driver does not support named parameters. If the driver supports named parameters and is capable of describing parameters, the parameter name is returned in this field.
+# </dd>
+#
+# <DT><B>SQL_DESC_NULLABLE [Implementation descriptors]</B></DT>
+#
+# <DD>In IRDs, this read-only SQLSMALLINT record field is SQL_NULLABLE if the column can have NULL values, SQL_NO_NULLS if the column does not have NULL values, or SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values. This field pertains to the result set column, not the base column.
+#
+# <P>In IPDs, this field is always set to SQL_NULLABLE because dynamic parameters are always nullable and cannot be set by an application.
+# </dd>
+#
+# <DT><B>SQL_DESC_NUM_PREC_RADIX [All]</B></DT>
+#
+# <DD>This SQLINTEGER field contains a value of 2 if the data type in the SQL_DESC_TYPE field is an approximate numeric data type, because the SQL_DESC_PRECISION field contains the number of bits. This field contains a value of 10 if the data type in the SQL_DESC_TYPE field is an exact numeric data type, because the SQL_DESC_PRECISION field contains the number of decimal digits. This field is set to 0 for all non-numeric data types.</dd>
+#
+# <DT><B>SQL_DESC_OCTET_LENGTH [All]</B></DT>
+#
+# <DD>This SQLINTEGER record field contains the length, in bytes, of a character string or binary data type. For fixed-length character or binary types, this is the actual length in bytes. For variable-length character or binary types, this is the maximum length in bytes. This value always excludes space for the null-termination character for implementation descriptors and always includes space for the null-termination character for application descriptors. For application data, this field contains the size of the buffer. For APDs, this field is defined only for output or input/output parameters.</dd>
+#
+# <DT><B>SQL_DESC_OCTET_LENGTH_PTR [Application descriptors]</B></DT>
+#
+# <DD>This SQLINTEGER * record field points to a variable that will contain the total length in bytes of a dynamic argument (for parameter descriptors) or of a bound column value (for row descriptors).
+#
+# <P>For an APD, this value is ignored for all arguments except character string and binary; if this field points to SQL_NTS, the dynamic argument must be null-terminated. To indicate that a bound parameter will be a data-at-execution parameter, an application sets this field in the appropriate record of the APD to a variable that, at execute time, will contain the value SQL_DATA_AT_EXEC or the result of the SQL_LEN_DATA_AT_EXEC macro. If there is more than one such field, SQL_DESC_DATA_PTR can be set to a value uniquely identifying the parameter to help the application determine which parameter is being requested.
+#
+#
+# <P>If the OCTET_LENGTH_PTR field of an ARD is a null pointer, the driver does not return length information for the column. If the SQL_DESC_OCTET_LENGTH_PTR field of an APD is a null pointer, the driver assumes that character strings and binary values are null-terminated. (Binary values should not be null-terminated but should be given a length to avoid truncation.)
+#
+#
+# <P>If the call to <B>SQLFetch</B> or <B>SQLFetchScroll</B> that fills in the buffer pointed to by this field did not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined. This field is a <I>deferred field</I>. It is not used at the time it is set but is used at a later time by the driver to determine or indicate the octet length of the data.
+# </dd>
+#
+# <DT><B>SQL_DESC_PARAMETER_TYPE [IPDs]</B></DT>
+#
+# <DD>This SQLSMALLINT record field is set to SQL_PARAM_INPUT for an input parameter, SQL_PARAM_INPUT_OUTPUT for an input/output parameter, or SQL_PARAM_OUTPUT for an output parameter. It is set to SQL_PARAM_INPUT by default.
+#
+# <P>For an IPD, the field is set to SQL_PARAM_INPUT by default if the IPD is not automatically populated by the driver (the SQL_ATTR_ENABLE_AUTO_IPD statement attribute is SQL_FALSE). An application should set this field in the IPD for parameters that are not input parameters.
+# </dd>
+#
+# <DT><B>SQL_DESC_PRECISION [All]</B></DT>
+#
+# <DD>This SQLSMALLINT record field contains the number of digits for an exact numeric type, the number of bits in the mantissa (binary precision) for an approximate numeric type, or the numbers of digits in the fractional seconds component for the SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, or SQL_INTERVAL_SECOND data type. This field is undefined for all other data types.
+#
+# <P>The value in this field may be different from the value for "precision" as defined in ODBC 2<I>.x</I>. For more information, see <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.
+# </dd>
+#
+# <DT><B>SQL_DESC_ROWVER [Implementation descriptors]</B></DT>
+#
+# <DD>This SQLSMALLINT<B> </B>record field indicates whether a column is automatically modified by the DBMS when a row is updated (for example, a column of the type "timestamp" in SQL Server). The value of this record field is set to SQL_TRUE if the column is a row versioning column, and to SQL_FALSE otherwise. This column attribute is similar to calling <B>SQLSpecialColumns</B> with IdentifierType of SQL_ROWVER to determine whether a column is automatically updated.</dd>
+#
+# <DT><B>SQL_DESC_SCALE [All]</B></DT>
+#
+# <DD>This SQLSMALLINT record field contains the defined scale for decimal and numeric data types. The field is undefined for all other data types.
+#
+# <P>The value in this field may be different from the value for "scale" as defined in ODBC 2<I>.x</I>. For more information, see <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.
+# </dd>
+#
+# <DT><B>SQL_DESC_SCHEMA_NAME [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the schema name of the base table that contains the column. The return value is driver-dependent if the column is an expression or if the column is part of a view. If the data source does not support schemas or the schema name cannot be determined, this variable contains an empty string.</dd>
+#
+# <DT><B>SQL_DESC_SEARCHABLE [IRDs]</B></DT>
+#
+# <DD>This read-only SQLSMALLINT record field is set to one of the following values:
+#
+# <UL type=disc>
+# <LI>SQL_PRED_NONE if the column cannot be used in a <B>WHERE</B> clause. (This is the same as the SQL_UNSEARCHABLE value in ODBC 2<I>.x</I>.)</li>
+#
+# <LI>SQL_PRED_CHAR if the column can be used in a <B>WHERE</B> clause but only with the <B>LIKE</B> predicate. (This is the same as the SQL_LIKE_ONLY value in ODBC 2<I>.x</I>.)</li>
+#
+# <LI>SQL_PRED_BASIC if the column can be used in a <B>WHERE</B> clause with all the comparison operators except <B>LIKE</B>. (This is the same as the SQL_EXCEPT_LIKE value in ODBC 2<I>.x</I>.)</li>
+#
+# <LI>SQL_PRED_SEARCHABLE if the column can be used in a <B>WHERE</B> clause with any comparison operator.</li>
+# </UL>
+# </dd>
+#
+# <DT><B>SQL_DESC_TABLE_NAME [IRDs]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the name of the base table that contains this column. The return value is driver-dependent if the column is an expression or if the column is part of a view.</dd>
+#
+# <DT><B>SQL_DESC_TYPE [All]</B></DT>
+#
+# <DD>This SQLSMALLINT record field specifies the concise SQL or C data type for all data types except datetime and interval data types. For the datetime and interval data types, this field specifies the verbose data type, which is SQL_DATETIME or SQL_INTERVAL.
+#
+# <P>Whenever this field contains SQL_DATETIME or SQL_INTERVAL, the SQL_DESC_DATETIME_INTERVAL_CODE field must contain the appropriate subcode for the concise type. For datetime data types, SQL_DESC_TYPE contains SQL_DATETIME, and the SQL_DESC_DATETIME_INTERVAL_CODE field contains a subcode for the specific datetime data type. For interval data types, SQL_DESC_TYPE contains SQL_INTERVAL and the SQL_DESC_DATETIME_INTERVAL_CODE field contains a subcode for the specific interval data type.
+#
+#
+# <P>The values in the SQL_DESC_TYPE and SQL_DESC_CONCISE_TYPE fields are interdependent. Each time one of the fields is set, the other must also be set. SQL_DESC_TYPE can be set by a call to <B>SQLSetDescField</B> or <B>SQLSetDescRec</B>. SQL_DESC_CONCISE_TYPE can be set by a call to <B>SQLBindCol</B> or <B>SQLBindParameter</B>, or <B>SQLSetDescField</B>.
+#
+#
+# <P>If SQL_DESC_TYPE is set to a concise data type other than an interval or datetime data type, the SQL_DESC_CONCISE_TYPE field is set to the same value and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to 0.
+#
+#
+# <P>If SQL_DESC_TYPE is set to the verbose datetime or interval data type (SQL_DATETIME or SQL_INTERVAL) and the SQL_DESC_DATETIME_INTERVAL_CODE field is set to the appropriate subcode, the SQL_DESC_CONCISE TYPE field is set to the corresponding concise type. Trying to set SQL_DESC_TYPE to one of the concise datetime or interval types will return SQLSTATE HY021 (Inconsistent descriptor information).
+#
+#
+# <P>When the SQL_DESC_TYPE field is set by a call to <B>SQLBindCol</B>, <B>SQLBindParameter</B>, or <B>SQLSetDescField</B>, the following fields are set to the following default values, as shown in the table below. The values of the remaining fields of the same record are undefined.
+#
+# <!--TS:-->
+# <div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>Value of SQL_DESC_TYPE</TH>
+# <TH width=52%>Other fields implicitly set</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>SQL_CHAR, SQL_VARCHAR, SQL_C_CHAR, SQL_C_VARCHAR</TD>
+# <TD width=52%>SQL_DESC_LENGTH is set to 1. SQL_DESC_PRECISION is set to 0.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>SQL_DATETIME</TD>
+# <TD width=52%>When SQL_DESC_DATETIME_INTERVAL_CODE is set to SQL_CODE_DATE or SQL_CODE_TIME, SQL_DESC_PRECISION is set to 0. When it is set to SQL_DESC_TIMESTAMP, SQL_DESC_PRECISION is set to 6.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>SQL_DECIMAL, SQL_NUMERIC,<BR>
+# SQL_C_NUMERIC</TD>
+# <TD width=52%>SQL_DESC_SCALE is set to 0. SQL_DESC_PRECISION is set to the implementation-defined precision for the respective data type.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>SQL_FLOAT, SQL_C_FLOAT</TD>
+# <TD width=52%>SQL_DESC_PRECISION is set to the implementation-defined default precision for SQL_FLOAT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>SQL_INTERVAL</TD>
+# <TD width=52%>When SQL_DESC_DATETIME_INTERVAL_CODE is set to an interval data type, SQL_DESC_DATETIME_INTERVAL_PRECISION is set to 2 (the default interval leading precision). When the interval has a seconds component, SQL_DESC_PRECISION is set to 6 (the default interval seconds precision).</TD>
+# </TR>
+# </table></div>
+#
+# <!--TS:-->
+#
+# <P>When an application calls <B>SQLSetDescField</B> to set fields of a descriptor rather than calling <B>SQLSetDescRec</B>, the application must first declare the data type. When it does, the other fields indicated in the previous table are implicitly set. If any of the values implicitly set are unacceptable, the application can then call <B>SQLSetDescField</B> or <B>SQLSetDescRec</B> to set the unacceptable value explicitly.
+# </dd>
+#
+# <DT><B>SQL_DESC_TYPE_NAME [Implementation descriptors]</B></DT>
+#
+# <DD>This read-only SQLCHAR * record field contains the data source&#0150;dependent type name (for example, "CHAR", "VARCHAR", and so on). If the data type name is unknown, this variable contains an empty string.</dd>
+#
+# <DT><B>SQL_DESC_UNNAMED [Implementation descriptors]</B></DT>
+#
+# <DD>This SQLSMALLINT record field in a row descriptor is set by the driver to either SQL_NAMED or SQL_UNNAMED when it sets the SQL_DESC_NAME field. If the SQL_DESC_NAME field contains a column alias or if the column alias does not apply, the driver sets the SQL_DESC_UNNAMED field to SQL_NAMED. If an application sets the SQL_DESC_NAME field of an IPD to a parameter name or alias, the driver sets the SQL_DESC_UNNAMED field of the IPD to SQL_NAMED. If there is no column name or a column alias, the driver sets the SQL_DESC_UNNAMED field to SQL_UNNAMED.
+#
+# <P>An application can set the SQL_DESC_UNNAMED field of an IPD to SQL_UNNAMED. A driver returns SQLSTATE HY091 (Invalid descriptor field identifier) if an application attempts to set the SQL_DESC_UNNAMED field of an IPD to SQL_NAMED. The SQL_DESC_UNNAMED field of an IRD is read-only; SQLSTATE HY091 (Invalid descriptor field identifier) will be returned if an application attempts to set it.
+# </dd>
+#
+# <DT><B>SQL_DESC_UNSIGNED [Implementation descriptors]</B></DT>
+#
+# <DD>This read-only SQLSMALLINT record field is set to SQL_TRUE if the column type is unsigned or non-numeric, or SQL_FALSE if the column type is signed.</dd>
+#
+# <DT><B>SQL_DESC_UPDATABLE [IRDs]</B></DT>
+#
+# <DD>This read-only SQLSMALLINT record field is set to one of the following values:
+#
+# <UL type=disc>
+# <LI>SQL_ATTR_READ_ONLY if the result set column is read-only.</li>
+#
+# <LI>SQL_ATTR_WRITE if the result set column is read-write.</li>
+#
+# <LI>SQL_ATTR_READWRITE_UNKNOWN if it is not known whether the result set column is updatable or not.</li>
+# </UL>
+#
+#
+# <P>SQL_DESC_UPDATABLE describes the updatability of the column in the result set, not the column in the base table. The updatability of the column in the base table on which this result set column is based may be different than the value in this field. Whether a column is updatable can be based on the data type, user privileges, and the definition of the result set itself. If it is unclear whether a column is updatable, SQL_ATTR_READWRITE_UNKNOWN should be returned.
+# </dd>
+# </DL>
+#
+# <H1>Consistency Checks</H1>
+#
+# <P>A consistency check is performed by the driver automatically whenever an application passes in a value for the SQL_DESC_DATA_PTR field of the ARD, APD, or IPD. If any of the fields is inconsistent with other fields, <B>SQLSetDescField</B> will return SQLSTATE HY021 (Inconsistent descriptor information). For more information, see "Consistency Check" in <A HREF="odbcsqlsetdescrec.htm">SQLSetDescRec</A>.</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>For information about</TH>
+# <TH width=52%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Binding a column</TD>
+# <TD width=52%><A HREF="odbcsqlbindcol.htm">SQLBindCol</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Binding a parameter</TD>
+# <TD width=52%><A HREF="odbcsqlbindparameter.htm">SQLBindParameter</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Getting a descriptor field</TD>
+# <TD width=52%><A HREF="odbcsqlgetdescfield.htm">SQLGetDescField</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Getting multiple descriptor fields</TD>
+# <TD width=52%><A HREF="odbcsqlgetdescrec.htm">SQLGetDescRec</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Setting multiple descriptor fields</TD>
+# <TD width=52%><A HREF="odbcsqlsetdescrec.htm">SQLSetDescRec</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+};
diff --git a/storage/ndb/src/old_files/client/odbc/docs/diag.txt b/storage/ndb/src/old_files/client/odbc/docs/diag.txt
new file mode 100644
index 00000000000..a9a0e0f42d0
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/diag.txt
@@ -0,0 +1,48 @@
+# Header Fields
+
+SQL_DIAG_CURSOR_ROW_COUNT
+SQLINTEGER
+
+SQL_DIAG_DYNAMIC_FUNCTION
+SQLCHAR *
+
+SQL_DIAG_DYNAMIC_FUNCTION_CODE
+SQLINTEGER
+
+SQL_DIAG_NUMBER
+SQLINTEGER
+
+SQL_DIAG_RETURNCODE
+SQLRETURN
+
+SQL_DIAG_ROW_COUNT
+SQLINTEGER
+
+# Record Fields
+
+SQL_DIAG_CLASS_ORIGIN
+SQLCHAR *
+
+SQL_DIAG_COLUMN_NUMBER
+SQLINTEGER
+
+SQL_DIAG_CONNECTION_NAME
+SQLCHAR *
+
+SQL_DIAG_MESSAGE_TEXT
+SQLCHAR *
+
+SQL_DIAG_NATIVE
+SQLINTEGER
+
+SQL_DIAG_ROW_NUMBER
+SQLINTEGER
+
+SQL_DIAG_SERVER_NAME
+SQLCHAR *
+
+SQL_DIAG_SQLSTATE
+SQLCHAR *
+
+SQL_DIAG_SUBCLASS_ORIGIN
+SQLCHAR *
diff --git a/storage/ndb/src/old_files/client/odbc/docs/getinfo.pl b/storage/ndb/src/old_files/client/odbc/docs/getinfo.pl
new file mode 100644
index 00000000000..34e26b47bab
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/getinfo.pl
@@ -0,0 +1,3676 @@
+#
+use strict;
+
+#
+# odbcsqlgetinfo.htm
+#
+my $info = {
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLGetInfo</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlgetinfo"></A>SQLGetInfo</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 1.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLGetInfo</B> returns general information about the driver and data source associated with a connection.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLGetInfo</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHDBC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ConnectionHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLUSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>InfoType</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLPOINTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>InfoValuePtr</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>BufferLength</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StringLengthPtr</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>ConnectionHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Connection handle.</dd>
+#
+# <DT><I>InfoType</I></DT>
+#
+# <DD>[Input]<BR>
+# Type of information.</dd>
+#
+# <DT><I>InfoValuePtr</I></DT>
+#
+# <DD>[Output]<BR>
+# Pointer to a buffer in which to return the information. Depending on the <I>InfoType</I> requested, the information returned will be one of the following: a null-terminated character string, an SQLUSMALLINT value, an SQLUINTEGER bitmask, an SQLUINTEGER flag, or a SQLUINTEGER binary value.
+#
+# <P>If the <I>InfoType</I> argument is SQL_DRIVER_HDESC or SQL_DRIVER_HSTMT, the <I>InfoValuePtr</I> argument is both input and output. (See the SQL_DRIVER_HDESC or SQL_DRIVER_HSTMT descriptors later in this function description for more information.)
+# </dd>
+#
+# <DT><I>BufferLength</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of the *<I>InfoValuePtr</I> buffer. If the value in <I>*InfoValuePtr</I> is not a character string or if <I>InfoValuePtr</I> is a null pointer,<I> </I>the <I>BufferLength</I> argument is ignored. The driver assumes that the size of <I>*InfoValuePtr</I> is SQLUSMALLINT or SQLUINTEGER, based on the <I>InfoType</I>. If <I>*InfoValuePtr</I> is a Unicode string (when calling <B>SQLGetInfoW</B>), the <I>BufferLength</I> argument must be an even number; if not, SQLSTATE HY090 (Invalid string or buffer length) is returned. </dd>
+#
+# <DT><I>StringLengthPtr</I></DT>
+#
+# <DD>[Output]<BR>
+# Pointer to a buffer in which to return the total number of bytes (excluding the null-termination character for character data) available to return in *<I>InfoValuePtr</I>.
+#
+# <P>For character data, if the number of bytes available to return is greater than or equal to <I>BufferLength</I>, the information in *<I>InfoValuePtr</I> is truncated to <I>BufferLength</I> bytes minus the length of a null-termination character and is null-terminated by the driver.
+#
+#
+# <P>For all other types of data, the value of <I>BufferLength</I> is ignored and the driver assumes the size of *<I>InfoValuePtr</I> is SQLUSMALLINT or SQLUINTEGER, depending on the <I>InfoType</I>.
+# </dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLGetInfo</B> returns either SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_DBC and a <I>Handle</I> of <I>ConnectionHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLGetInfo</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=23%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=51%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=51%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>01004</TD>
+# <TD width=26%>String data, right truncated</TD>
+# <TD width=51%>The buffer *<I>InfoValuePtr</I> was not large enough to return all of the requested information, so the information was truncated. The length of the requested information in its untruncated form is returned in *<I>StringLengthPtr</I>. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>08003</TD>
+# <TD width=26%>Connection does not exist</TD>
+# <TD width=51%>(DM) The type of information requested in <I>InfoType</I> requires an open connection. Of the information types reserved by ODBC, only SQL_ODBC_VER can be returned without an open connection.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=51%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=51%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HY001</TD>
+# <TD width=26%>Memory allocation <BR>
+# error</TD>
+# <TD width=51%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=51%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HY024</TD>
+# <TD width=26%>Invalid attribute value</TD>
+# <TD width=51%>(DM) The <I>InfoType</I> argument was SQL_DRIVER_HSTMT, and the value pointed to by <I>InfoValuePtr</I> was not a valid statement handle.
+# <P>(DM) The <I>InfoType</I> argument was SQL_DRIVER_HDESC, and the value pointed to by <I>InfoValuePtr</I> was not a valid descriptor handle.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=51%>(DM) The value specified for argument <I>BufferLength</I> was less than 0.
+# <P>(DM) The value specified for <I>BufferLength</I> was an odd number, and <I>*InfoValuePtr </I>was of a Unicode data type.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HY096</TD>
+# <TD width=26%>Information type out of range</TD>
+# <TD width=51%>The value specified for the argument <I>InfoType</I> was not valid for the version of ODBC supported by the driver.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HYC00</TD>
+# <TD width=26%>Optional field not implemented</TD>
+# <TD width=51%>The value specified for the argument <I>InfoType</I> was a driver-specific value that is not supported by the driver.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=51%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=23%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=51%>(DM) The driver corresponding to the <I>ConnectionHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P>The currently defined information types are shown in "Information Types," later in this section; it is expected that more will be defined to take advantage of different data sources. A range of information types is reserved by ODBC; driver developers must reserve values for their own driver-specific use from X/Open. <B>SQLGetInfo</B> performs no Unicode conversion or <I>thunking</I> (see <A HREF="odbcodbc_error_codes.htm">Appendix A</A> of the <I>ODBC Programmer's Reference</I>) for driver-defined <I>InfoTypes</I>. For more information, see "<A HREF="odbcdriver_specific_data_types__descriptor_types__information_types.htm">Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes</A>" in Chapter 17: Programming Considerations. The format of the information returned in *<I>InfoValuePtr</I> depends on the <I>InfoType</I> requested. <B>SQLGetInfo</B> will return information in one of five different formats:
+#
+# <UL type=disc>
+# <LI>A null-terminated character string</li>
+#
+# <LI>An SQLUSMALLINT value</li>
+#
+# <LI>An SQLUINTEGER bitmask</li>
+#
+# <LI>An SQLUINTEGER value</li>
+#
+# <LI>A SQLUINTEGER binary value</li>
+# </UL>
+#
+# <P>The format of each of the following information types is noted in the type's description. The application must cast the value returned in *<I>InfoValuePtr</I> accordingly. For an example of how an application could retrieve data from a SQLUINTEGER bitmask, see "Code Example."</P>
+#
+# <P>A driver must return a value for each of the information types defined in the tables below. If an information type does not apply to the driver or data source, the driver returns one of the values listed in the following table.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=49%>Format of *<I>InfoValuePtr</I></TH>
+# <TH width=51%>Returned value</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>Character string ("Y" or "N")</TD>
+# <TD width=51%>"N"</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>Character string (not "Y" or "N")</TD>
+# <TD width=51%>Empty string</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQLUSMALLINT</TD>
+# <TD width=51%>0</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQLUINTEGER bitmask or SQLUINTEGER binary value</TD>
+# <TD width=51%>0L</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>For example, if a data source does not support procedures, <B>SQLGetInfo</B> returns the values listed in the following table for the values of <I>InfoType</I> that are related to procedures.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=49%><I>InfoType</I></TH>
+# <TH width=51%>Returned value</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_PROCEDURES</TD>
+# <TD width=51%>"N"</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ACCESSIBLE_PROCEDURES</TD>
+# <TD width=51%>"N"</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_PROCEDURE_NAME_LEN</TD>
+# <TD width=51%>0</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_PROCEDURE_TERM</TD>
+# <TD width=51%>Empty string</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P><B>SQLGetInfo</B> returns SQLSTATE HY096 (Invalid argument value) for values of <I>InfoType</I> that are in the range of information types reserved for use by ODBC but are not defined by the version of ODBC supported by the driver. To determine what version of ODBC a driver conforms to, an application calls <B>SQLGetInfo</B> with the SQL_DRIVER_ODBC_VER information type. <B>SQLGetInfo</B> returns SQLSTATE HYC00 (Optional feature not implemented) for values of <I>InfoType</I> that are in the range of information types reserved for driver-specific use but are not supported by the driver.</P>
+#
+# <P>All calls to <B>SQLGetInfo</B> require an open connection, except when the <I>InfoType</I> is SQL_ODBC_VER, which returns the version of the Driver Manager.</P>
+#
+# <H1>Information Types</H1>
+#
+# <P>This section lists the information types supported by <B>SQLGetInfo</B>. Information types are grouped categorically and listed alphabetically. Information types that were added or renamed for ODBC 3<I>.x</I> are also listed.</P>
+#
+# <H2>Driver Information</H2>
+#
+# <P>The following values of the <I>InfoType</I> argument return information about the ODBC driver, such as the number of active statements, the data source name, and the interface standards compliance level:</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_ACTIVE_ENVIRONMENTS</TD>
+# <TD width=57%>SQL_GETDATA_EXTENSIONS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_ASYNC_MODE</TD>
+# <TD width=57%>SQL_INFO_SCHEMA_VIEWS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_BATCH_ROW_COUNT</TD>
+# <TD width=57%>SQL_KEYSET_CURSOR_ATTRIBUTES1</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_BATCH_SUPPORT</TD>
+# <TD width=57%>SQL_KEYSET_CURSOR_ATTRIBUTES2</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DATA_SOURCE_NAME</TD>
+# <TD width=57%>SQL_MAX_ASYNC_CONCURRENT_STATEMENTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_HDBC</TD>
+# <TD width=57%>SQL_MAX_CONCURRENT_ACTIVITIES</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_HDESC</TD>
+# <TD width=57%>SQL_MAX_DRIVER_CONNECTIONS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_HENV</TD>
+# <TD width=57%>SQL_ODBC_INTERFACE_CONFORMANCE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_HLIB</TD>
+# <TD width=57%>SQL_ODBC_STANDARD_CLI_CONFORMANCE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_HSTMT</TD>
+# <TD width=57%>SQL_ODBC_VER</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_NAME</TD>
+# <TD width=57%>SQL_PARAM_ARRAY_ROW_COUNTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_ODBC_VER</TD>
+# <TD width=57%>SQL_PARAM_ARRAY_SELECTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DRIVER_VER</TD>
+# <TD width=57%>SQL_ROW_UPDATES</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DYNAMIC_CURSOR_ATTRIBUTES1</TD>
+# <TD width=57%>SQL_SEARCH_PATTERN_ESCAPE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_DYNAMIC_CURSOR_ATTRIBUTES2</TD>
+# <TD width=57%>SQL_SERVER_NAME</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1</TD>
+# <TD width=57%>SQL_STATIC_CURSOR_ATTRIBUTES1</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2</TD>
+# <TD width=57%>SQL_STATIC_CURSOR_ATTRIBUTES2</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>SQL_FILE_USAGE</TD>
+# <TD width=57%>&nbsp;</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <H2>DBMS Product Information</H2>
+#
+# <P>The following values of the <I>InfoType</I> argument return information about the DBMS product, such as the DBMS name and version:</P>
+#
+# <P>SQL_DATABASE_NAME<BR>
+# SQL_DBMS_NAME<BR>
+# SQL_DBMS_VER</P>
+#
+# <H2>Data Source Information</H2>
+#
+# <P>The following values of the <I>InfoType</I> argument return information about the data source, such as cursor characteristics and transaction capabilities:</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_ACCESSIBLE_PROCEDURES</TD>
+# <TD width=46%>SQL_MULT_RESULT_SETS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_ACCESSIBLE_TABLES</TD>
+# <TD width=46%>SQL_MULTIPLE_ACTIVE_TXN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_BOOKMARK_PERSISTENCE</TD>
+# <TD width=46%>SQL_NEED_LONG_DATA_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_CATALOG_TERM</TD>
+# <TD width=46%>SQL_NULL_COLLATION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_COLLATION_SEQ</TD>
+# <TD width=46%>SQL_PROCEDURE_TERM</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_CONCAT_NULL_BEHAVIOR</TD>
+# <TD width=46%>SQL_SCHEMA_TERM</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_CURSOR_COMMIT_BEHAVIOR</TD>
+# <TD width=46%>SQL_SCROLL_OPTIONS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_CURSOR_ROLLBACK_BEHAVIOR</TD>
+# <TD width=46%>SQL_TABLE_TERM</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_CURSOR_SENSITIVITY</TD>
+# <TD width=46%>SQL_TXN_CAPABLE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_DATA_SOURCE_READ_ONLY</TD>
+# <TD width=46%>SQL_TXN_ISOLATION_OPTION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_DEFAULT_TXN_ISOLATION</TD>
+# <TD width=46%>SQL_USER_NAME</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=54%>SQL_DESCRIBE_PARAMETER</TD>
+# <TD width=46%>&nbsp;</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <H2>Supported SQL</H2>
+#
+# <P>The following values of the <I>InfoType</I> argument return information about the SQL statements supported by the data source. The SQL syntax of each feature described by these information types is the SQL-92 syntax. These information types do not exhaustively describe the entire SQL-92 grammar. Instead, they describe those parts of the grammar for which data sources commonly offer different levels of support. Specifically, most of the DDL statements in SQL-92 are covered.</P>
+#
+# <P>Applications should determine the general level of supported grammar from the SQL_SQL_CONFORMANCE information type and use the other information types to determine variations from the stated standards compliance level.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_AGGREGATE_FUNCTIONS</TD>
+# <TD width=51%>SQL_DROP_TABLE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ALTER_DOMAIN</TD>
+# <TD width=51%>SQL_DROP_TRANSLATION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ALTER_SCHEMA</TD>
+# <TD width=51%>SQL_DROP_VIEW</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ALTER_TABLE</TD>
+# <TD width=51%>SQL_EXPRESSIONS_IN_ORDERBY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ANSI_SQL_DATETIME_LITERALS</TD>
+# <TD width=51%>SQL_GROUP_BY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CATALOG_LOCATION </TD>
+# <TD width=51%>SQL_IDENTIFIER_CASE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CATALOG_NAME</TD>
+# <TD width=51%>SQL_IDENTIFIER_QUOTE_CHAR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CATALOG_NAME_SEPARATOR</TD>
+# <TD width=51%>SQL_INDEX_KEYWORDS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CATALOG_USAGE</TD>
+# <TD width=51%>SQL_INSERT_STATEMENT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_COLUMN_ALIAS</TD>
+# <TD width=51%>SQL_INTEGRITY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CORRELATION_NAME</TD>
+# <TD width=51%>SQL_KEYWORDS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_ASSERTION</TD>
+# <TD width=51%>SQL_LIKE_ESCAPE_CLAUSE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_CHARACTER_SET</TD>
+# <TD width=51%>SQL_NON_NULLABLE_COLUMNS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_COLLATION</TD>
+# <TD width=51%>SQL_SQL_CONFORMANCE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_DOMAIN</TD>
+# <TD width=51%>SQL_OJ_CAPABILITIES</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_SCHEMA</TD>
+# <TD width=51%>SQL_ORDER_BY_COLUMNS_IN_SELECT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_TABLE</TD>
+# <TD width=51%>SQL_OUTER_JOINS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_TRANSLATION</TD>
+# <TD width=51%>SQL_PROCEDURES</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DDL_INDEX</TD>
+# <TD width=51%>SQL_QUOTED_IDENTIFIER_CASE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DROP_ASSERTION</TD>
+# <TD width=51%>SQL_SCHEMA_USAGE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DROP_CHARACTER_SET</TD>
+# <TD width=51%>SQL_SPECIAL_CHARACTERS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DROP_COLLATION</TD>
+# <TD width=51%>SQL_SUBQUERIES</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DROP_DOMAIN</TD>
+# <TD width=51%>SQL_UNION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DROP_SCHEMA</TD>
+# <TD width=51%>&nbsp;</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>SQL Limits</B></P>
+#
+# <P>The following values of the <I>InfoType</I> argument return information about the limits applied to identifiers and clauses in SQL statements, such as the maximum lengths of identifiers and the maximum number of columns in a select list. Limitations can be imposed by either the driver or the data source.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_BINARY_LITERAL_LEN</TD>
+# <TD width=51%>SQL_MAX_IDENTIFIER_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_CATALOG_NAME_LEN</TD>
+# <TD width=51%>SQL_MAX_INDEX_SIZE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_CHAR_LITERAL_LEN</TD>
+# <TD width=51%>SQL_MAX_PROCEDURE_NAME_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_COLUMN_NAME_LEN</TD>
+# <TD width=51%>SQL_MAX_ROW_SIZE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_COLUMNS_IN_GROUP_BY</TD>
+# <TD width=51%>SQL_MAX_ROW_SIZE_INCLUDES_LONG</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_COLUMNS_IN_INDEX</TD>
+# <TD width=51%>SQL_MAX_SCHEMA_NAME_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_COLUMNS_IN_ORDER_BY</TD>
+# <TD width=51%>SQL_MAX_STATEMENT_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_COLUMNS_IN_SELECT</TD>
+# <TD width=51%>SQL_MAX_TABLE_NAME_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_COLUMNS_IN_TABLE</TD>
+# <TD width=51%>SQL_MAX_TABLES_IN_SELECT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_MAX_CURSOR_NAME_LEN</TD>
+# <TD width=51%>SQL_MAX_USER_NAME_LEN</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Scalar Function Information</B></P>
+#
+# <P>The following values of the <I>InfoType</I> argument return information about the scalar functions supported by the data source and the driver. For more information about scalar functions, see <A HREF="odbcscalar_functions.htm">Appendix E: Scalar Functions</A>.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CONVERT_FUNCTIONS</TD>
+# <TD width=51%>SQL_TIMEDATE_ADD_INTERVALS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_NUMERIC_FUNCTIONS</TD>
+# <TD width=51%>SQL_TIMEDATE_DIFF_INTERVALS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_STRING_FUNCTIONS</TD>
+# <TD width=51%>SQL_TIMEDATE_FUNCTIONS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_SYSTEM_FUNCTIONS</TD>
+# <TD width=51%>&nbsp;</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Conversion Information</B></P>
+#
+# <P>The following values of the <I>InfoType</I> argument return a list of the SQL data types to which the data source can convert the specified SQL data type with the <B>CONVERT</B> scalar function:</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_BIGINT</TD>
+# <TD width=47%>SQL_CONVERT_LONGVARBINARY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_BINARY</TD>
+# <TD width=47%>SQL_CONVERT_LONGVARCHAR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_BIT</TD>
+# <TD width=47%>SQL_CONVERT_NUMERIC</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_CHAR</TD>
+# <TD width=47%>SQL_CONVERT_REAL</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_DATE</TD>
+# <TD width=47%>SQL_CONVERT_SMALLINT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_DECIMAL</TD>
+# <TD width=47%>SQL_CONVERT_TIME</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_DOUBLE</TD>
+# <TD width=47%>SQL_CONVERT_TIMESTAMP</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_FLOAT</TD>
+# <TD width=47%>SQL_CONVERT_TINYINT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_INTEGER</TD>
+# <TD width=47%>SQL_CONVERT_VARBINARY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_INTERVAL_YEAR_MONTH</TD>
+# <TD width=47%>SQL_CONVERT_VARCHAR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=53%>SQL_CONVERT_INTERVAL_DAY_TIME</TD>
+# <TD width=47%>&nbsp;</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Information Types Added for ODBC 3<I>.x</I></B></P>
+#
+# <P>The following values of the <I>InfoType</I> argument have been added for ODBC 3<I>.x</I>:</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ACTIVE_ENVIRONMENTS</TD>
+# <TD width=51%>SQL_DROP_ASSERTION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_AGGREGATE_FUNCTIONS</TD>
+# <TD width=51%>SQL_DROP_CHARACTER_SET</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ALTER_DOMAIN</TD>
+# <TD width=51%>SQL_DROP_COLLATION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ALTER_SCHEMA</TD>
+# <TD width=51%>SQL_DROP_DOMAIN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ANSI_SQL_DATETIME_LITERALS</TD>
+# <TD width=51%>SQL_DROP_SCHEMA</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_ASYNC_MODE</TD>
+# <TD width=51%>SQL_DROP_TABLE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_BATCH_ROW_COUNT</TD>
+# <TD width=51%>SQL_DROP_TRANSLATION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_BATCH_SUPPORT</TD>
+# <TD width=51%>SQL_DROP_VIEW</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CATALOG_NAME</TD>
+# <TD width=51%>SQL_DYNAMIC_CURSOR_ATTRIBUTES1</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_COLLATION_SEQ</TD>
+# <TD width=51%>SQL_DYNAMIC_CURSOR_ATTRIBUTES2</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CONVERT_INTERVAL_YEAR_MONTH</TD>
+# <TD width=51%>SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CONVERT_INTERVAL_DAY_TIME</TD>
+# <TD width=51%>SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_ASSERTION</TD>
+# <TD width=51%>SQL_INFO_SCHEMA_VIEWS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_CHARACTER_SET</TD>
+# <TD width=51%>SQL_INSERT_STATEMENT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_COLLATION</TD>
+# <TD width=51%>SQL_KEYSET_CURSOR_ATTRIBUTES1</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_DOMAIN</TD>
+# <TD width=51%>SQL_KEYSET_CURSOR_ATTRIBUTES2</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_SCHEMA</TD>
+# <TD width=51%>SQL_MAX_ASYNC_CONCURRENT_STATEMENTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_TABLE</TD>
+# <TD width=51%>SQL_MAX_IDENTIFIER_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CREATE_TRANSLATION</TD>
+# <TD width=51%>SQL_PARAM_ARRAY_ROW_COUNTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_CURSOR_SENSITIVITY</TD>
+# <TD width=51%>SQL_PARAM_ARRAY_SELECTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DDL_INDEX</TD>
+# <TD width=51%>SQL_STATIC_CURSOR_ATTRIBUTES1</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DESCRIBE_PARAMETER</TD>
+# <TD width=51%>SQL_STATIC_CURSOR_ATTRIBUTES2</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DM_VER</TD>
+# <TD width=51%>SQL_XOPEN_CLI_YEAR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=49%>SQL_DRIVER_HDESC</TD>
+# <TD width=51%>&nbsp;</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Information Types Renamed for ODBC 3<I>.x</I></B></P>
+#
+# <P>The following values of the <I>InfoType</I> argument have been renamed for ODBC 3<I>.x</I>.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=55%>ODBC 2.0 <I>InfoType</I></TH>
+# <TH width=45%>ODBC 3<I>.x</I> <I>InfoType</I></TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_ACTIVE_CONNECTIONS</TD>
+# <TD width=45%>SQL_MAX_DRIVER_CONNECTIONS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_ACTIVE_STATEMENTS</TD>
+# <TD width=45%>SQL_MAX_CONCURRENT_ACTIVITIES</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_MAX_OWNER_NAME_LEN</TD>
+# <TD width=45%>SQL_MAX_SCHEMA_NAME_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_MAX_QUALIFIER_NAME_LEN</TD>
+# <TD width=45%>SQL_MAX_CATALOG_NAME_LEN</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_ODBC_SQL_OPT_IEF</TD>
+# <TD width=45%>SQL_INTEGRITY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_OWNER_TERM</TD>
+# <TD width=45%>SQL_SCHEMA_TERM</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_OWNER_USAGE</TD>
+# <TD width=45%>SQL_SCHEMA_USAGE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_QUALIFIER_LOCATION</TD>
+# <TD width=45%>SQL_CATALOG_LOCATION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_QUALIFIER_NAME_SEPARATOR</TD>
+# <TD width=45%>SQL_CATALOG_NAME_SEPARATOR</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_QUALIFIER_TERM</TD>
+# <TD width=45%>SQL_CATALOG_TERM</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=55%>SQL_QUALIFIER_USAGE</TD>
+# <TD width=45%>SQL_CATALOG_USAGE</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Information Types Deprecated in ODBC 3<I>.x</I></B></P>
+#
+# <P>The following values of the <I>InfoType</I> argument have been deprecated in ODBC 3<I>.x</I>. ODBC 3<I>.x</I> drivers must continue to support these information types for backward compatibility with ODBC 2<I>.x</I> applications. (For more information on these types, see "<A HREF="odbcsqlgetinfo_support.htm">SQLGetInfo Support</A>" in Appendix G: Driver Guidelines for Backward Compatibility.)</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=51%>SQL_FETCH_DIRECTION</TD>
+# <TD width=49%>SQL_POS_OPERATIONS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=51%>SQL_LOCK_TYPES</TD>
+# <TD width=49%>SQL_POSITIONED_STATEMENTS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=51%>SQL_ODBC_API_CONFORMANCE</TD>
+# <TD width=49%>SQL_SCROLL_CONCURRENCY</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=51%>SQL_ODBC_SQL_CONFORMANCE</TD>
+# <TD width=49%>SQL_STATIC_SENSITIVITY</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <H2>Information Type Descriptions</H2>
+#
+# <P>The following table alphabetically lists each information type, the version of ODBC in which it was introduced, and its description.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%><I>InfoType</I></TH>
+# <TH width=50%>Returns</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ACCESSIBLE_PROCEDURES<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the user can execute all procedures returned by <B>SQLProcedures</B>; "N" if there may be procedures returned that the user cannot execute.</TD>
+# </TR>
+ SQL_ACCESSIBLE_PROCEDURES => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ACCESSIBLE_TABLES<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the user is guaranteed <B>SELECT</B> privileges to all tables returned by <B>SQLTables</B>; "N" if there may be tables returned that the user cannot access.</TD>
+# </TR>
+ SQL_ACCESSIBLE_TABLES => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ACTIVE_ENVIRONMENTS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of active environments that the driver can support. If there is no specified limit or the limit is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_ACTIVE_ENVIRONMENTS => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_AGGREGATE_FUNCTIONS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating support for aggregation functions:
+# <P>SQL_AF_ALL<BR>
+# SQL_AF_AVG<BR>
+# SQL_AF_COUNT<BR>
+# SQL_AF_DISTINCT<BR>
+# SQL_AF_MAX<BR>
+# SQL_AF_MIN<BR>
+# SQL_AF_SUM </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_AGGREGATE_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ALTER_DOMAIN<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>ALTER DOMAIN</B> statement, as defined in SQL-92, supported by the data source. An SQL-92 Full level&#0150;compliant driver will always return all of the bitmasks. A return value of "0" means that the <B>ALTER DOMAIN</B> statement is not supported.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_AD_ADD_DOMAIN_CONSTRAINT = Adding a domain constraint is supported (Full level)</P>
+#
+# <P>SQL_AD_ADD_DOMAIN_DEFAULT = &lt;alter domain&gt; &lt;set domain default clause&gt; is supported (Full level)</P>
+#
+# <P>SQL_AD_CONSTRAINT_NAME_DEFINITION = &lt;constraint name definition clause&gt; is supported for naming domain constraint (Intermediate level)</P>
+#
+# <P>SQL_AD_DROP_DOMAIN_CONSTRAINT = &lt;drop domain constraint clause&gt; is supported (Full level)</P>
+#
+# <P>SQL_AD_DROP_DOMAIN_DEFAULT = &lt;alter domain&gt; &lt;drop domain default clause&gt; is supported (Full level)</P>
+#
+# <P>The following bits specify the supported &lt;constraint attributes&gt; if &lt;add domain constraint&gt; is supported (the SQL_AD_ADD_DOMAIN_CONSTRAINT bit is set):</P>
+#
+# <P>SQL_AD_ADD_CONSTRAINT_DEFERRABLE (Full level)<BR>
+# SQL_AD_ADD_CONSTRAINT_NON_DEFERRABLE (Full level)<BR>
+# SQL_AD_ADD_CONSTRAINT_INITIALLY_DEFERRED (Full level)<BR>
+# SQL_AD_ADD_CONSTRAINT_INITIALLY_IMMEDIATE (Full level)</P>
+# </TD>
+# </TR>
+ SQL_ALTER_DOMAIN => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ALTER_TABLE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>ALTER TABLE</B> statement supported by the data source.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_AT_ADD_COLUMN_COLLATION = &lt;add column&gt; clause is supported, with facility to specify column collation (Full level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_ADD_COLUMN_DEFAULT = &lt;add column&gt; clause is supported, with facility to specify column defaults (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_ADD_COLUMN_SINGLE = &lt;add column&gt; is supported (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_ADD_CONSTRAINT = &lt;add column&gt; clause is supported, with facility to specify column constraints (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_ADD_TABLE_CONSTRAINT = &lt;add table constraint&gt; clause is supported (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_CONSTRAINT_NAME_DEFINITION = &lt;constraint name definition&gt; is supported for naming column and table constraints (Intermediate level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_DROP_COLUMN_CASCADE = &lt;drop column&gt; CASCADE is supported (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_DROP_COLUMN_DEFAULT = &lt;alter column&gt; &lt;drop column default clause&gt; is supported (Intermediate level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_DROP_COLUMN_RESTRICT = &lt;drop column&gt; RESTRICT is supported (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE (ODBC 3.0)</P>
+#
+# <P>SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT = &lt;drop column&gt; RESTRICT is supported (FIPS Transitional level) (ODBC 3.0)</P>
+#
+# <P>SQL_AT_SET_COLUMN_DEFAULT = &lt;alter column&gt; &lt;set column default clause&gt; is supported (Intermediate level) (ODBC 3.0)</P>
+#
+# <P>The following bits specify the support &lt;constraint attributes&gt; if specifying column or table constraints is supported (the SQL_AT_ADD_CONSTRAINT bit is set):</P>
+#
+# <P>SQL_AT_CONSTRAINT_INITIALLY_DEFERRED (Full level) (ODBC 3.0)<BR>
+# SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE (Full level) (ODBC 3.0)<BR>
+# SQL_AT_CONSTRAINT_DEFERRABLE (Full level) (ODBC 3.0)<BR>
+# SQL_AT_CONSTRAINT_NON_DEFERRABLE (Full level) (ODBC 3.0)</P>
+# </TD>
+# </TR>
+ SQL_ALTER_TABLE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ASYNC_MODE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value indicating the level of asynchronous support in the driver:
+# <P>SQL_AM_CONNECTION = Connection level asynchronous execution is supported. Either all statement handles associated with a given connection handle are in asynchronous mode or all are in synchronous mode. A statement handle on a connection cannot be in asynchronous mode while another statement handle on the same connection is in synchronous mode, and vice versa.</P>
+#
+# <P>SQL_AM_STATEMENT = Statement level asynchronous execution is supported. Some statement handles associated with a connection handle can be in asynchronous mode, while other statement handles on the same connection are in synchronous mode.</P>
+#
+# <P>SQL_AM_NONE = Asynchronous mode is not supported.</P>
+# </TD>
+# </TR>
+ SQL_ASYNC_MODE => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_BATCH_ROW_COUNT <BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the behavior of the driver with respect to the availability of row counts. The following bitmasks are used in conjunction with the information type:
+# <P>SQL_BRC_ROLLED_UP = Row counts for consecutive INSERT, DELETE, or UPDATE statements are rolled up into one. If this bit is not set, then row counts are available for each individual statement.</P>
+#
+# <P>SQL_BRC_PROCEDURES = Row counts, if any, are available when a batch is executed in a stored procedure. If row counts are available, they can be rolled up or individually available, depending on the SQL_BRC_ROLLED_UP bit.</P>
+#
+# <P>SQL_BRC_EXPLICIT = Row counts, if any, are available when a batch is executed directly by calling <B>SQLExecute</B> or <B>SQLExecDirect</B>. If row counts are available, they can be rolled up or individually available, depending on the SQL_BRC_ROLLED_UP bit.</P>
+# </TD>
+# </TR>
+ SQL_BATCH_ROW_COUNT => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_BATCH_SUPPORT <BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the driver's support for batches. The following bitmasks are used to determine which level is supported:
+# <P>SQL_BS_SELECT_EXPLICIT = The driver supports explicit batches that can have result-set generating statements.</P>
+#
+# <P>SQL_BS_ROW_COUNT_EXPLICIT = The driver supports explicit batches that can have row-count generating statements.</P>
+#
+# <P>SQL_BS_SELECT_PROC = The driver supports explicit procedures that can have result-set generating statements.</P>
+#
+# <P>SQL_BS_ROW_COUNT_PROC = The driver supports explicit procedures that can have row-count generating statements.</P>
+# </TD>
+# </TR>
+ SQL_BATCH_SUPPORT => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_BOOKMARK_PERSISTENCE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the operations through which bookmarks persist.
+# <P>The following bitmasks are used in conjunction with the flag to determine through which options bookmarks persist:</P>
+#
+# <P>SQL_BP_CLOSE = Bookmarks are valid after an application calls <B>SQLFreeStmt</B> with the SQL_CLOSE option, or <B>SQLCloseCursor</B> to close the cursor associated with a statement.</P>
+#
+# <P>SQL_BP_DELETE = The bookmark for a row is valid after that row has been deleted.</P>
+#
+# <P>SQL_BP_DROP = Bookmarks are valid after an application calls <B>SQLFreeHandle</B> with a <I>HandleType</I> of SQL_HANDLE_STMT to drop a statement.</P>
+#
+# <P>SQL_BP_TRANSACTION = Bookmarks are valid after an application commits or rolls back a transaction.</P>
+#
+# <P>SQL_BP_UPDATE = The bookmark for a row is valid after any column in that row has been updated, including key columns.</P>
+#
+# <P>SQL_BP_OTHER_HSTMT = A bookmark associated with one statement can be used with another statement. Unless SQL_BP_CLOSE or SQL_BP_DROP is specified, the cursor on the first statement must be open.</P>
+# </TD>
+# </TR>
+ SQL_BOOKMARK_PERSISTENCE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CATALOG_LOCATION<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value indicating the position of the catalog in a qualified table name:
+# <P>SQL_CL_START<BR>
+# SQL_CL_END</P>
+#
+# <P>For example, an Xbase driver returns SQL_CL_START because the directory (catalog) name is at the start of the table name, as in \EMPDATA\EMP.DBF. An ORACLE Server driver returns SQL_CL_END because the catalog is at the end of the table name, as in ADMIN.EMP@EMPDATA.</P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return SQL_CL_START. A value of 0 is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls <B>SQLGetInfo</B> with the SQL_CATALOG_NAME information type.</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_QUALIFIER_LOCATION.</P>
+# </TD>
+# </TR>
+ SQL_CATALOG_LOCATION => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CATALOG_NAME<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>A character string: "Y" if the server supports catalog names, or "N" if it does not.
+# <P>An SQL-92 Full level&#0150;conformant driver will always return "Y".</P>
+# </TD>
+# </TR>
+ SQL_CATALOG_NAME => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CATALOG_NAME_SEPARATOR<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: the character or characters that the data source defines as the separator between a catalog name and the qualified name element that follows or precedes it.
+# <P>An empty string is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls <B>SQLGetInfo</B> with the SQL_CATALOG_NAME information type. An SQL-92 Full level&#0150;conformant driver will always return ".".</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_QUALIFIER_NAME_SEPARATOR.</P>
+# </TD>
+# </TR>
+ SQL_CATALOG_NAME_SEPARATOR => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CATALOG_TERM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the data source vendor's name for a catalog; for example, "database" or "directory". This string can be in upper, lower, or mixed case.
+# <P>An empty string is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls <B>SQLGetInfo</B> with the SQL_CATALOG_NAME information type. An SQL-92 Full level&#0150;conformant driver will always return "catalog".</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_QUALIFIER_TERM.</P>
+# </TD>
+# </TR>
+ SQL_CATALOG_TERM => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CATALOG_USAGE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the statements in which catalogs can be used.
+# <P>The following bitmasks are used to determine where catalogs can be used:</P>
+#
+# <P>SQL_CU_DML_STATEMENTS = Catalogs are supported in all Data Manipulation Language statements: <B>SELECT</B>, <B>INSERT</B>, <B>UPDATE</B>, <B>DELETE</B>, and if supported, <B>SELECT FOR UPDATE</B> and positioned update and delete statements.</P>
+#
+# <P>SQL_CU_PROCEDURE_INVOCATION = Catalogs are supported in the ODBC procedure invocation statement.</P>
+#
+# <P>SQL_CU_TABLE_DEFINITION = Catalogs are supported in all table definition statements: <B>CREATE TABLE</B>, <B>CREATE VIEW</B>, <B>ALTER TABLE</B>, <B>DROP TABLE</B>, and <B>DROP VIEW</B>.</P>
+#
+# <P>SQL_CU_INDEX_DEFINITION = Catalogs are supported in all index definition statements: <B>CREATE INDEX</B> and <B>DROP INDEX</B>.</P>
+#
+# <P>SQL_CU_PRIVILEGE_DEFINITION = Catalogs are supported in all privilege definition statements: <B>GRANT</B> and <B>REVOKE</B>. </P>
+#
+# <P>A value of 0 is returned if catalogs are not supported by the data source. To find out whether catalogs are supported, an application calls <B>SQLGetInfo</B> with the SQL_CATALOG_NAME information type. An SQL-92 Full level&#0150;conformant driver will always return a bitmask with all of these bits set.</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_QUALIFIER_USAGE.</P>
+# </TD>
+# </TR>
+ SQL_CATALOG_USAGE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_COLLATION_SEQ<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>The name of the collation sequence. This is a character string that indicates the name of the default collation for the default character set for this server (for example, 'ISO 8859-1' or EBCDIC). If this is unknown, an empty string will be returned. An SQL-92 Full level&#0150;conformant driver will always return a non-empty string.</TD>
+# </TR>
+ SQL_COLLATION_SEQ => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_COLUMN_ALIAS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source supports column aliases; otherwise, "N".
+# <P>A column alias is an alternate name that can be specified for a column in the select list by using an AS clause. An SQL-92 Entry level&#0150;conformant driver will always return "Y".</P>
+# </TD>
+# </TR>
+ SQL_COLUMN_ALIAS => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CONCAT_NULL_BEHAVIOR<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value indicating how the data source handles the concatenation of NULL valued character data type columns with non-NULL valued character data type columns:
+# <P>SQL_CB_NULL = Result is NULL valued.</P>
+#
+# <P>SQL_CB_NON_NULL = Result is concatenation of non-NULL valued column or columns. </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return SQL_CB_NULL.</P>
+# </TD>
+# </TR>
+ SQL_CONCAT_NULL_BEHAVIOR => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CONVERT_BIGINT<BR>
+ SQL_CONVERT_BIGINT => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_BINARY<BR>
+ SQL_CONVERT_BINARY => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_BIT <BR>
+ SQL_CONVERT_BIT => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_CHAR <BR>
+ SQL_CONVERT_CHAR => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_GUID<BR>
+ SQL_CONVERT_GUID => {
+ type => q(Bitmask),
+ omit => 1,
+ },
+# SQL_CONVERT_DATE<BR>
+ SQL_CONVERT_DATE => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_DECIMAL<BR>
+ SQL_CONVERT_DECIMAL => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_DOUBLE<BR>
+ SQL_CONVERT_DOUBLE => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_FLOAT<BR>
+ SQL_CONVERT_FLOAT => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_INTEGER<BR>
+ SQL_CONVERT_INTEGER => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_INTERVAL_YEAR_MONTH<BR>
+ SQL_CONVERT_INTERVAL_YEAR_MONTH => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_INTERVAL_DAY_TIME<BR>
+ SQL_CONVERT_INTERVAL_DAY_TIME => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_LONGVARBINARY<BR>
+ SQL_CONVERT_LONGVARBINARY => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_LONGVARCHAR<BR>
+ SQL_CONVERT_LONGVARCHAR => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_NUMERIC<BR>
+ SQL_CONVERT_NUMERIC => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_REAL<BR>
+ SQL_CONVERT_REAL => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_SMALLINT<BR>
+ SQL_CONVERT_SMALLINT => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_TIME<BR>
+ SQL_CONVERT_TIME => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_TIMESTAMP<BR>
+ SQL_CONVERT_TIMESTAMP => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_TINYINT<BR>
+ SQL_CONVERT_TINYINT => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_VARBINARY<BR>
+ SQL_CONVERT_VARBINARY => {
+ type => q(Bitmask),
+ },
+# SQL_CONVERT_VARCHAR <BR>
+ SQL_CONVERT_VARCHAR => {
+ type => q(Bitmask),
+ },
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask. The bitmask indicates the conversions supported by the data source with the <B>CONVERT</B> scalar function for data of the type named in the <I>InfoType</I>. If the bitmask equals zero, the data source does not support any conversions from data of the named type, including conversion to the same data type.
+# <P>For example, to find out if a data source supports the conversion of SQL_INTEGER data to the SQL_BIGINT data type, an application calls <B>SQLGetInfo</B> with the <I>InfoType</I> of SQL_CONVERT_INTEGER. The application performs an <B>AND</B> operation with the returned bitmask and SQL_CVT_BIGINT. If the resulting value is nonzero, the conversion is supported. </P>
+#
+# <P>The following bitmasks are used to determine which conversions are supported:</P>
+#
+# <P>SQL_CVT_BIGINT (ODBC 1.0)<BR>
+# SQL_CVT_BINARY (ODBC 1.0)<BR>
+# SQL_CVT_BIT (ODBC 1.0) <BR>
+# SQL_CVT_GUID (ODBC 3.5)<BR>
+# SQL_CVT_CHAR (ODBC 1.0) <BR>
+# SQL_CVT_DATE (ODBC 1.0)<BR>
+# SQL_CVT_DECIMAL (ODBC 1.0)<BR>
+# SQL_CVT_DOUBLE (ODBC 1.0)<BR>
+# SQL_CVT_FLOAT (ODBC 1.0)<BR>
+# SQL_CVT_INTEGER (ODBC 1.0)<BR>
+# SQL_CVT_INTERVAL_YEAR_MONTH (ODBC 3.0)<BR>
+# SQL_CVT_INTERVAL_DAY_TIME (ODBC 3.0)<BR>
+# SQL_CVT_LONGVARBINARY (ODBC 1.0)<BR>
+# SQL_CVT_LONGVARCHAR (ODBC 1.0)<BR>
+# SQL_CVT_NUMERIC (ODBC 1.0)<BR>
+# SQL_CVT_REAL ODBC 1.0)<BR>
+# SQL_CVT_SMALLINT (ODBC 1.0)<BR>
+# SQL_CVT_TIME (ODBC 1.0)<BR>
+# SQL_CVT_TIMESTAMP (ODBC 1.0)<BR>
+# SQL_CVT_TINYINT (ODBC 1.0)<BR>
+# SQL_CVT_VARBINARY (ODBC 1.0)<BR>
+# SQL_CVT_VARCHAR (ODBC 1.0)</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CONVERT_FUNCTIONS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the scalar conversion functions supported by the driver and associated data source.
+# <P>The following bitmask is used to determine which conversion functions are supported:</P>
+#
+# <P>SQL_FN_CVT_CAST<BR>
+# SQL_FN_CVT_CONVERT</P>
+# </TD>
+# </TR>
+ SQL_CONVERT_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CORRELATION_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value indicating whether table correlation names are supported:
+# <P>SQL_CN_NONE = Correlation names are not supported.</P>
+#
+# <P>SQL_CN_DIFFERENT = Correlation names are supported but must differ from the names of the tables they represent.</P>
+#
+# <P>SQL_CN_ANY = Correlation names are supported and can be any valid user-defined name. </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return SQL_CN_ANY.</P>
+# </TD>
+# </TR>
+ SQL_CORRELATION_NAME => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_ASSERTION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE ASSERTION</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_CA_CREATE_ASSERTION</P>
+#
+# <P>The following bits specify the supported constraint attribute if the ability to specify constraint attributes explicitly is supported (see the SQL_ALTER_TABLE and SQL_CREATE_TABLE information types):</P>
+#
+# <P>SQL_CA_CONSTRAINT_INITIALLY_DEFERRED<BR>
+# SQL_CA_CONSTRAINT_INITIALLY_IMMEDIATE<BR>
+# SQL_CA_CONSTRAINT_DEFERRABLE<BR>
+# SQL_CA_CONSTRAINT_NON_DEFERRABLE</P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return all of these options as supported. A return value of "0" means that the <B>CREATE ASSERTION</B> statement is not supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_ASSERTION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_CHARACTER_SET<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE CHARACTER SET</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_CCS_CREATE_CHARACTER_SET<BR>
+# SQL_CCS_COLLATE_CLAUSE<BR>
+# SQL_CCS_LIMITED_COLLATION</P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return all of these options as supported. A return value of "0" means that the <B>CREATE CHARACTER SET</B> statement is not supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_CHARACTER_SET => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_COLLATION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE COLLATION</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmask is used to determine which clauses are supported:</P>
+#
+# <P>SQL_CCOL_CREATE_COLLATION </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return this option as supported. A return value of "0" means that the <B>CREATE COLLATION</B> statement is not supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_COLLATION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_DOMAIN<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE DOMAIN</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_CDO_CREATE_DOMAIN = The CREATE DOMAIN statement is supported (Intermediate level).</P>
+#
+# <P>SQL_CDO_CONSTRAINT_NAME_DEFINITION = &lt;constraint name definition&gt; is supported for naming domain constraints (Intermediate level).</P>
+#
+# <P>The following bits specify the ability to create column constraints:<BR>
+# SQL_CDO_DEFAULT = Specifying domain constraints is supported (Intermediate level)<BR>
+# SQL_CDO_CONSTRAINT = Specifying domain defaults is supported (Intermediate level)<BR>
+# SQL_CDO_COLLATION = Specifying domain collation is supported (Full level)</P>
+#
+# <P>The following bits specify the supported constraint attributes if specifying domain constraints is supported (SQL_CDO_DEFAULT is set):</P>
+#
+# <P>SQL_CDO_CONSTRAINT_INITIALLY_DEFERRED (Full level)<BR>
+# SQL_CDO_CONSTRAINT_INITIALLY_IMMEDIATE (Full level)<BR>
+# SQL_CDO_CONSTRAINT_DEFERRABLE (Full level)<BR>
+# SQL_CDO_CONSTRAINT_NON_DEFERRABLE (Full level)</P>
+#
+# <P>A return value of "0" means that the <B>CREATE DOMAIN</B> statement is not supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_DOMAIN => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_SCHEMA<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE SCHEMA</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_CS_CREATE_SCHEMA<BR>
+# SQL_CS_AUTHORIZATION<BR>
+# SQL_CS_DEFAULT_CHARACTER_SET </P>
+#
+# <P>An SQL-92 Intermediate level&#0150;conformant driver will always return the SQL_CS_CREATE_SCHEMA and SQL_CS_AUTHORIZATION options as supported. These must also be supported at the SQL-92 Entry level, but not necessarily as SQL statements. An SQL-92 Full level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_SCHEMA => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_TABLE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE TABLE</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_CT_CREATE_TABLE = The CREATE TABLE statement is supported. (Entry level)</P>
+#
+# <P>SQL_CT_TABLE_CONSTRAINT = Specifying table constraints is supported (FIPS Transitional level)</P>
+#
+# <P>SQL_CT_CONSTRAINT_NAME_DEFINITION = The &lt;constraint name definition&gt; clause is supported for naming column and table constraints (Intermediate level)</P>
+#
+# <P>The following bits specify the ability to create temporary tables:</P>
+#
+# <P>SQL_CT_COMMIT_PRESERVE = Deleted rows are preserved on commit. (Full level)<BR>
+# SQL_CT_COMMIT_DELETE = Deleted rows are deleted on commit. (Full level)<BR>
+# SQL_CT_GLOBAL_TEMPORARY = Global temporary tables can be created. (Full level)<BR>
+# SQL_CT_LOCAL_TEMPORARY = Local temporary tables can be created. (Full level)</P>
+#
+# <P>The following bits specify the ability to create column constraints:</P>
+#
+# <P>SQL_CT_COLUMN_CONSTRAINT = Specifying column constraints is supported (FIPS Transitional level)<BR>
+# SQL_CT_COLUMN_DEFAULT = Specifying column defaults is supported (FIPS Transitional level)<BR>
+# SQL_CT_COLUMN_COLLATION = Specifying column collation is supported (Full level)</P>
+#
+# <P>The following bits specify the supported constraint attributes if specifying column or table constraints is supported:</P>
+#
+# <P>SQL_CT_CONSTRAINT_INITIALLY_DEFERRED (Full level)<BR>
+# SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE (Full level)<BR>
+# SQL_CT_CONSTRAINT_DEFERRABLE (Full level)<BR>
+# SQL_CT_CONSTRAINT_NON_DEFERRABLE (Full level)</P>
+# </TD>
+# </TR>
+ SQL_CREATE_TABLE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_TRANSLATION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE TRANSLATION</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmask is used to determine which clauses are supported:</P>
+#
+# <P>SQL_CTR_CREATE_TRANSLATION</P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return these options as supported. A return value of "0" means that the <B>CREATE TRANSLATION</B> statement is not supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_TRANSLATION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CREATE_VIEW<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>CREATE VIEW</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_CV_CREATE_VIEW<BR>
+# SQL_CV_CHECK_OPTION<BR>
+# SQL_CV_CASCADED<BR>
+# SQL_CV_LOCAL </P>
+#
+# <P>A return value of "0" means that the <B>CREATE VIEW</B> statement is not supported.</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return the SQL_CV_CREATE_VIEW and SQL_CV_CHECK_OPTION options as supported. </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_CREATE_VIEW => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CURSOR_COMMIT_BEHAVIOR<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value indicating how a <B>COMMIT</B> operation affects cursors and prepared statements in the data source:
+# <P>SQL_CB_DELETE = Close cursors and delete prepared statements. To use the cursor again, the application must reprepare and reexecute the statement.</P>
+#
+# <P>SQL_CB_CLOSE = Close cursors. For prepared statements, the application can call <B>SQLExecute</B> on the statement without calling <B>SQLPrepare</B> again.</P>
+#
+# <P>SQL_CB_PRESERVE = Preserve cursors in the same position as before the <B>COMMIT</B> operation. The application can continue to fetch data, or it can close the cursor and reexecute the statement without repreparing it.</P>
+# </TD>
+# </TR>
+ SQL_CURSOR_COMMIT_BEHAVIOR => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CURSOR_ROLLBACK_BEHAVIOR<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value indicating how a <B>ROLLBACK</B> operation affects cursors and prepared statements in the data source:
+# <P>SQL_CB_DELETE = Close cursors and delete prepared statements. To use the cursor again, the application must reprepare and reexecute the statement.</P>
+#
+# <P>SQL_CB_CLOSE = Close cursors. For prepared statements, the application can call <B>SQLExecute</B> on the statement without calling <B>SQLPrepare</B> again.</P>
+#
+# <P>SQL_CB_PRESERVE = Preserve cursors in the same position as before the <B>ROLLBACK</B> operation. The application can continue to fetch data, or it can close the cursor and reexecute the statement without repreparing it.</P>
+# </TD>
+# </TR>
+ SQL_CURSOR_ROLLBACK_BEHAVIOR => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_CURSOR_ROLLBACK_SQL_CURSOR_SENSITIVITY<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value indicating the support for cursor sensitivity:
+# <P>SQL_INSENSITIVE = All cursors on the statement handle show the result set without reflecting any changes made to it by any other cursor within the same transaction.</P>
+#
+# <P>SQL_UNSPECIFIED = It is unspecified whether cursors on the statement handle make visible the changes made to a result set by another cursor within the same transaction. Cursors on the statement handle may make visible none, some, or all such changes.</P>
+#
+# <P>SQL_SENSITIVE = Cursors are sensitive to changes made by other cursors within the same transaction.</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return the SQL_UNSPECIFIED option as supported. </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return the SQL_INSENSITIVE option as supported.</P>
+# </TD>
+# </TR>
+ SQL_CURSOR_SENSITIVITY => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DATA_SOURCE_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the data source name used during connection. If the application called <B>SQLConnect</B>, this is the value of the <I>szDSN</I> argument. If the application called <B>SQLDriverConnect</B> or <B>SQLBrowseConnect</B>, this is the value of the DSN keyword in the connection string passed to the driver. If the connection string did not contain the <B>DSN</B> keyword (such as when it contains the <B>DRIVER</B> keyword), this is an empty string.</TD>
+# </TR>
+ SQL_DATA_SOURCE_NAME => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DATA_SOURCE_READ_ONLY<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string. "Y" if the data source is set to READ ONLY mode, "N" if it is otherwise.
+# <P>This characteristic pertains only to the data source itself; it is not a characteristic of the driver that enables access to the data source. A driver that is read/write can be used with a data source that is read-only. If a driver is read-only, all of its data sources must be read-only and must return SQL_DATA_SOURCE_READ_ONLY.</P>
+# </TD>
+# </TR>
+ SQL_DATA_SOURCE_READ_ONLY => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DATABASE_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the name of the current database in use, if the data source defines a named object called "database".
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;In ODBC 3<I>.x</I>, the value returned for this <I>InfoType</I> can also be returned by calling <B>SQLGetConnectAttr</B> with an <I>Attribute</I> argument of SQL_ATTR_CURRENT_CATALOG.</P>
+# </TD>
+# </TR>
+ SQL_DATABASE_NAME => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DATETIME_LITERALS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the SQL-92 datetime literals supported by the data source. Note that these are the datetime literals listed in the SQL-92 specification and are separate from the datetime literal escape clauses defined by ODBC. For more information about the ODBC datetime literal escape clauses, see "Date, Time, Timestamp, and Datetime Interval Literals" in Chapter 8: SQL Statements.
+# <P>A FIPS Transitional level&#0150;conformant driver will always return the "1" value in the bitmask for the bits listed below. A value of "0" means that SQL-92 datetime literals are not supported.</P>
+#
+# <P>The following bitmasks are used to determine which literals are supported:</P>
+#
+# <P>SQL_DL_SQL92_DATE<BR>
+# SQL_DL_SQL92_TIME<BR>
+# SQL_DL_SQL92_TIMESTAMP<BR>
+# SQL_DL_SQL92_INTERVAL_YEAR<BR>
+# SQL_DL_SQL92_INTERVAL_MONTH<BR>
+# SQL_DL_SQL92_INTERVAL_DAY<BR>
+# SQL_DL_SQL92_INTERVAL_HOUR<BR>
+# SQL_DL_SQL92_INTERVAL_MINUTE<BR>
+# SQL_DL_SQL92_INTERVAL_SECOND<BR>
+# SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH<BR>
+# SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR</P>
+#
+# <P>SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE<BR>
+# SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND<BR>
+# SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE<BR>
+# SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND<BR>
+# SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND</P>
+# </TD>
+# </TR>
+ SQL_DATETIME_LITERALS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DBMS_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the name of the DBMS product accessed by the driver.</TD>
+# </TR>
+ SQL_DBMS_NAME => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DBMS_VER<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string indicating the version of the DBMS product accessed by the driver. The version is of the form ##.##.####, where the first two digits are the major version, the next two digits are the minor version, and the last four digits are the release version. The driver must render the DBMS product version in this form but can also append the DBMS product-specific version as well. For example, "04.01.0000 Rdb 4.1".</TD>
+# </TR>
+ SQL_DBMS_VER => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DDL_INDEX<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value that indicates support for creation and dropping of indexes:
+# <P>SQL_DI_CREATE_INDEX<BR>
+# SQL_DI_DROP_INDEX </P>
+# </TD>
+# </TR>
+ SQL_DDL_INDEX => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DEFAULT_TXN_ISOLATION<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER value that indicates the default transaction isolation level supported by the driver or data source, or zero if the data source does not support transactions. The following terms are used to define transaction isolation levels:
+# <P><B>Dirty Read </B>Transaction 1 changes a row. Transaction 2 reads the changed row before transaction 1 commits the change. If transaction 1 rolls back the change, transaction 2 will have read a row that is considered to have never existed.</P>
+#
+# <P><B>Nonrepeatable Read </B>Transaction 1 reads a row. Transaction 2 updates or deletes that row and commits this change. If transaction 1 attempts to reread the row, it will receive different row values or discover that the row has been deleted.</P>
+#
+# <P><B>Phantom </B>Transaction 1 reads a set of rows that satisfy some search criteria. Transaction 2 generates one or more rows (through either inserts or updates) that match the search criteria. If transaction 1 reexecutes the statement that reads the rows, it receives a different set of rows.</P>
+#
+# <P>If the data source supports transactions, the driver returns one of the following bitmasks:</P>
+#
+# <P>SQL_TXN_READ_UNCOMMITTED = Dirty reads, nonrepeatable reads, and phantoms are possible.</P>
+#
+# <P>SQL_TXN_READ_COMMITTED = Dirty reads are not possible. Nonrepeatable reads and phantoms are possible.</P>
+#
+# <P>SQL_TXN_REPEATABLE_READ = Dirty reads and nonrepeatable reads are not possible. Phantoms are possible.</P>
+#
+# <P>SQL_TXN_SERIALIZABLE = Transactions are serializable. Serializable transactions do not allow dirty reads, nonrepeatable reads, or phantoms.</P>
+# </TD>
+# </TR>
+ SQL_DEFAULT_TXN_ISOLATION => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DESCRIBE_PARAMETER<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>A character string: "Y" if parameters can be described; "N", if not.
+# <P>An SQL-92 Full level&#0150;conformant driver will usually return "Y" because it will support the <B>DESCRIBE INPUT</B> statement. Because this does not directly specify the underlying SQL support, however, describing parameters might not be supported, even in a SQL-92 Full level&#0150;conformant driver.</P>
+# </TD>
+# </TR>
+ SQL_DESCRIBE_PARAMETER => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DM_VER<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>A character string with the version of the Driver Manager. The version is of the form ##.##.####.####, where:
+# <P>The first set of two digits is the major ODBC version, as given by the constant SQL_SPEC_MAJOR.</P>
+#
+# <P>The second set of two digits is the minor ODBC version, as given by the constant SQL_SPEC_MINOR.</P>
+#
+# <P>The third set of four digits is the Driver Manager major build number.</P>
+#
+# <P>The last set of four digits is the Driver Manager minor build number.</P>
+# </TD>
+# </TR>
+ SQL_DM_VER => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_HDBC<BR>
+# SQL_DRIVER_HENV<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER value, the driver's environment handle or connection handle, determined by the argument <I>InfoType</I>.
+# <P>These information types are implemented by the Driver Manager alone.</P>
+# </TD>
+# </TR>
+ SQL_DRIVER_HDBC => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_HDESC<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value, the driver's descriptor handle determined by the Driver Manager's descriptor handle, which must be passed on input in *<I>InfoValuePtr</I> from the application. In this case, <I>InfoValuePtr</I> is both an input and output argument. The input descriptor handle passed in *<I>InfoValuePtr</I> must have been either explicitly or implicitly allocated on the <I>ConnectionHandle</I>.
+# <P>The application should make a copy of the Driver Manager's descriptor handle before calling <B>SQLGetInfo</B> with this information type, to ensure that the handle is not overwritten on output.</P>
+#
+# <P>This information type is implemented by the Driver Manager alone.</P>
+# </TD>
+# </TR>
+ SQL_DRIVER_HDESC => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_HLIB<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER value, the <I>hinst</I> from the load library returned to the Driver Manager when it loaded the driver DLL (on a Microsoft&reg; Windows&reg; platform) or equivalent on a non-Windows platform. The handle is valid only for the connection handle specified in the call to <B>SQLGetInfo</B>.
+# <P>This information type is implemented by the Driver Manager alone.</P>
+# </TD>
+# </TR>
+ SQL_DRIVER_HLIB => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_HSTMT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER value, the driver's statement handle determined by the Driver Manager statement handle, which must be passed on input in *<I>InfoValuePtr</I> from the application. In this case, <I>InfoValuePtr</I> is both an input and an output argument. The input statement handle passed in *<I>InfoValuePtr</I> must have been allocated on the argument <I>ConnectionHandle</I>.
+# <P>The application should make a copy of the Driver Manager's statement handle before calling <B>SQLGetInfo</B> with this information type, to ensure that the handle is not overwritten on output.</P>
+#
+# <P>This information type is implemented by the Driver Manager alone.</P>
+# </TD>
+# </TR>
+ SQL_DRIVER_HSTMT => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the file name of the driver used to access the data source.</TD>
+# </TR>
+ SQL_DRIVER_NAME => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_ODBC_VER<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string with the version of ODBC that the driver supports. The version is of the form ##.##, where the first two digits are the major version and the next two digits are the minor version. SQL_SPEC_MAJOR and SQL_SPEC_MINOR define the major and minor version numbers. For the version of ODBC described in this manual, these are 3 and 0, and the driver should return "03.00".</TD>
+# </TR>
+ SQL_DRIVER_ODBC_VER => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DRIVER_VER<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the version of the driver and optionally, a description of the driver. At a minimum, the version is of the form ##.##.####, where the first two digits are the major version, the next two digits are the minor version, and the last four digits are the release version.</TD>
+# </TR>
+ SQL_DRIVER_VER => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_ASSERTION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP ASSERTION</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmask is used to determine which clauses are supported:</P>
+#
+# <P>SQL_DA_DROP_ASSERTION </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return this option as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_ASSERTION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_CHARACTER_SET<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP CHARACTER SET</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmask is used to determine which clauses are supported:</P>
+#
+# <P>SQL_DCS_DROP_CHARACTER_SET </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return this option as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_CHARACTER_SET => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_COLLATION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP COLLATION</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmask is used to determine which clauses are supported:</P>
+#
+# <P>SQL_DC_DROP_COLLATION </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return this option as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_COLLATION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_DOMAIN<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP DOMAIN</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_DD_DROP_DOMAIN<BR>
+# SQL_DD_CASCADE<BR>
+# SQL_DD_RESTRICT </P>
+#
+# <P>An SQL-92 Intermediate level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_DOMAIN => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_SCHEMA<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP SCHEMA</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_DS_DROP_SCHEMA<BR>
+# SQL_DS_CASCADE<BR>
+# SQL_DS_RESTRICT </P>
+#
+# <P>An SQL-92 Intermediate level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_SCHEMA => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_TABLE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP TABLE</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_DT_DROP_TABLE<BR>
+# SQL_DT_CASCADE<BR>
+# SQL_DT_RESTRICT </P>
+#
+# <P>An FIPS Transitional level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_TABLE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_TRANSLATION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP TRANSLATION</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmask is used to determine which clauses are supported:</P>
+#
+# <P>SQL_DTR_DROP_TRANSLATION </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return this option as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_TRANSLATION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DROP_VIEW<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses in the <B>DROP VIEW</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The following bitmasks are used to determine which clauses are supported:</P>
+#
+# <P>SQL_DV_DROP_VIEW<BR>
+# SQL_DV_CASCADE<BR>
+# SQL_DV_RESTRICT </P>
+#
+# <P>An FIPS Transitional level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_DROP_VIEW => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DYNAMIC_CURSOR_ATTRIBUTES1<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a dynamic cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_DYNAMIC_CURSOR_ATTRIBUTES2.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA1_NEXT = A <I>FetchOrientation</I> argument of SQL_FETCH_NEXT is supported in a call to <B>SQLFetchScroll</B> when the cursor is a dynamic cursor.</P>
+#
+# <P>SQL_CA1_ABSOLUTE = <I>FetchOrientation</I> arguments of SQL_FETCH_FIRST, SQL_FETCH_LAST, and SQL_FETCH_ABSOLUTE are supported in a call to <B>SQLFetchScroll</B> when the cursor is a dynamic cursor. (The rowset that will be fetched is independent of the current cursor position.) </P>
+#
+# <P>SQL_CA1_RELATIVE = <I>FetchOrientation</I> arguments of SQL_FETCH_PRIOR and SQL_FETCH_RELATIVE are supported in a call to <B>SQLFetchScroll</B> when the cursor is a dynamic cursor. (The rowset that will be fetched is dependent on the current cursor position. Note that this is separated from SQL_FETCH_NEXT because in a forward-only cursor, only SQL_FETCH_NEXT is supported.) </P>
+#
+# <P>SQL_CA1_BOOKMARK = A <I>FetchOrientation</I> argument of SQL_FETCH_BOOKMARK is supported in a call to <B>SQLFetchScroll</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_LOCK_EXCLUSIVE = A <I>LockType</I> argument of SQL_LOCK_EXCLUSIVE is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor.</P>
+#
+# <P>SQL_CA1_LOCK_NO_CHANGE = A <I>LockType</I> argument of SQL_LOCK_NO_CHANGE is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_LOCK_UNLOCK = A <I>LockType</I> argument of SQL_LOCK_UNLOCK is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor.</P>
+#
+# <P>SQL_CA1_POS_POSITION = An <I>Operation</I> argument of SQL_POSITION is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor.</P>
+#
+# <P>SQL_CA1_POS_UPDATE = An <I>Operation</I> argument of SQL_UPDATE is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_POS_DELETE = An <I>Operation</I> argument of SQL_DELETE is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_POS_REFRESH = An <I>Operation</I> argument of SQL_REFRESH is supported in a call to <B>SQLSetPos</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_POSITIONED_UPDATE = An UPDATE WHERE CURRENT OF SQL statement is supported when the cursor is a dynamic cursor. (An SQL-92 Entry level&#0150;conformant driver will always return this option as supported.)</P>
+#
+# <P>SQL_CA1_POSITIONED_DELETE = A DELETE WHERE CURRENT OF SQL statement is supported when the cursor is a dynamic cursor. (An SQL-92 Entry level&#0150;conformant driver will always return this option as supported.)</P>
+#
+# <P>SQL_CA1_SELECT_FOR_UPDATE = A SELECT FOR UPDATE SQL statement is supported when the cursor is a dynamic cursor. (An SQL-92 Entry level&#0150;conformant driver will always return this option as supported.)</P>
+#
+# <P>SQL_CA1_BULK_ADD = An <I>Operation</I> argument of SQL_ADD is supported in a call to <B>SQLBulkOperations</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_BULK_UPDATE_BY_BOOKMARK = An <I>Operation</I> argument of SQL_UPDATE_BY_BOOKMARK is supported in a call to <B>SQLBulkOperations</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_BULK_DELETE_BY_BOOKMARK = An <I>Operation</I> argument of SQL_DELETE_BY_BOOKMARK is supported in a call to <B>SQLBulkOperations</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA1_BULK_FETCH_BY_BOOKMARK = An <I>Operation</I> argument of SQL_FETCH_BY_BOOKMARK is supported in a call to <B>SQLBulkOperations</B> when the cursor is a dynamic cursor. </P>
+#
+# <P>An SQL-92 Intermediate level&#0150;conformant driver will usually return the SQL_CA1_NEXT, SQL_CA1_ABSOLUTE, and SQL_CA1_RELATIVE options as supported, because it supports scrollable cursors through the embedded SQL FETCH statement. Because this does not directly determine the underlying SQL support, however, scrollable cursors may not be supported, even for an SQL-92 Intermediate level&#0150;conformant driver.</P>
+# </TD>
+# </TR>
+ SQL_DYNAMIC_CURSOR_ATTRIBUTES1 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_DYNAMIC_CURSOR_ATTRIBUTES2<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a dynamic cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA2_READ_ONLY_CONCURRENCY = A read-only dynamic cursor, in which no updates are allowed, is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_READ_ONLY for a dynamic cursor). </P>
+#
+# <P>SQL_CA2_LOCK_CONCURRENCY = A dynamic cursor that uses the lowest level of locking sufficient to ensure that the row can be updated is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_LOCK for a dynamic cursor.) These locks must be consistent with the transaction isolation level set by the SQL_ATTR_TXN_ISOLATION connection attribute.</P>
+#
+# <P>SQL_CA2_OPT_ROWVER_CONCURRENCY = A dynamic cursor that uses the optimistic concurrency control comparing row versions is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_ROWVER for a dynamic cursor.) </P>
+#
+# <P>SQL_CA2_OPT_VALUES_CONCURRENCY = A dynamic cursor that uses the optimistic concurrency control comparing values is supported. (The SQL_ATTR_CONCURRENCY statement attribute can be SQL_CONCUR_VALUES for a dynamic cursor.) </P>
+#
+# <P>SQL_CA2_SENSITIVITY_ADDITIONS = Added rows are visible to a dynamic cursor; the cursor can scroll to those rows. (Where these rows are added to the cursor is driver-dependent.) </P>
+#
+# <P>SQL_CA2_SENSITIVITY_DELETIONS = Deleted rows are no longer available to a dynamic cursor, and do not leave a "hole" in the result set; after the dynamic cursor scrolls from a deleted row, it cannot return to that row. </P>
+#
+# <P>SQL_CA2_SENSITIVITY_UPDATES = Updates to rows are visible to a dynamic cursor; if the dynamic cursor scrolls from and returns to an updated row, the data returned by the cursor is the updated data, not the original data. </P>
+#
+# <P>SQL_CA2_MAX_ROWS_SELECT = The SQL_ATTR_MAX_ROWS statement attribute affects <B>SELECT</B> statements when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_MAX_ROWS_INSERT = The SQL_ATTR_MAX_ROWS statement attribute affects <B>INSERT</B> statements when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_MAX_ROWS_DELETE = The SQL_ATTR_MAX_ROWS statement attribute affects <B>DELETE</B> statements when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_MAX_ROWS_UPDATE = The SQL_ATTR_MAX_ROWS statement attribute affects <B>UPDATE</B> statements when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_MAX_ROWS_CATALOG = The SQL_ATTR_MAX_ROWS statement attribute affects <B>CATALOG</B> result sets when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_MAX_ROWS_AFFECTS_ALL = The SQL_ATTR_MAX_ROWS statement attribute affects <B>SELECT</B>, <B>INSERT</B>, <B>DELETE</B>, and <B>UPDATE</B> statements, and <B>CATALOG</B> result sets, when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_CRC_EXACT = The exact row count is available in the SQL_DIAG_CURSOR_ROW_COUNT diagnostic field when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_CRC_APPROXIMATE = An approximate row count is available in the SQL_DIAG_CURSOR_ROW_COUNT diagnostic field when the cursor is a dynamic cursor. </P>
+#
+# <P>SQL_CA2_SIMULATE_NON_UNIQUE = The driver does not guarantee that simulated positioned update or delete statements will affect only one row when the cursor is a dynamic cursor; it is the application's responsibility to guarantee this. (If a statement affects more than one row, <B>SQLExecute</B> or <B>SQLExecDirect</B> returns SQLSTATE 01001 [Cursor operation conflict].) To set this behavior, the application calls <B>SQLSetStmtAttr</B> with the SQL_ATTR_SIMULATE_CURSOR attribute set to SQL_SC_NON_UNIQUE. </P>
+#
+# <P>SQL_CA2_SIMULATE_TRY_UNIQUE = The driver attempts to guarantee that simulated positioned update or delete statements will affect only one row when the cursor is a dynamic cursor. The driver always executes such statements, even if they might affect more than one row, such as when there is no unique key. (If a statement affects more than one row, <B>SQLExecute</B> or <B>SQLExecDirect</B> returns SQLSTATE 01001 [Cursor operation conflict].) To set this behavior, the application calls <B>SQLSetStmtAttr</B> with the SQL_ATTR_SIMULATE_CURSOR attribute set to SQL_SC_TRY_UNIQUE. </P>
+#
+# <P>SQL_CA2_SIMULATE_UNIQUE = The driver guarantees that simulated positioned update or delete statements will affect only one row when the cursor is a dynamic cursor. If the driver cannot guarantee this for a given statement, <B>SQLExecDirect</B> or <B>SQLPrepare</B> return SQLSTATE 01001 (Cursor operation conflict). To set this behavior, the application calls <B>SQLSetStmtAttr</B> with the SQL_ATTR_SIMULATE_CURSOR attribute set to SQL_SC_UNIQUE.</P>
+# </TD>
+# </TR>
+ SQL_DYNAMIC_CURSOR_ATTRIBUTES2 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_EXPRESSIONS_IN_ORDERBY<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source supports expressions in the <B>ORDER BY</B> list; "N" if it does not.</TD>
+# </TR>
+ SQL_EXPRESSIONS_IN_ORDERBY => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_FILE_USAGE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value indicating how a single-tier driver directly treats files in a data source:
+# <P>SQL_FILE_NOT_SUPPORTED = The driver is not a single-tier driver. For example, an ORACLE driver is a two-tier driver.</P>
+#
+# <P>SQL_FILE_TABLE = A single-tier driver treats files in a data source as tables. For example, an Xbase driver treats each Xbase file as a table.</P>
+#
+# <P>SQL_FILE_CATALOG = A single-tier driver treats files in a data source as a catalog. For example, a Microsoft&reg; Access driver treats each Microsoft Access file as a complete database.</P>
+#
+# <P>An application might use this to determine how users will select data. For example, Xbase users often think of data as stored in files, while ORACLE and MicrosoftAccess users generally think of data as stored in tables.</P>
+#
+# <P>When a user selects an Xbase data source, the application could display the Windows <B>File Open</B> common dialog box; when the user selects a Microsoft Access or ORACLE data source, the application could display a custom <B>Select Table</B> dialog box.</P>
+# </TD>
+# </TR>
+ SQL_FILE_USAGE => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a forward-only cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA1_NEXT<BR>
+# SQL_CA1_LOCK_EXCLUSIVE<BR>
+# SQL_CA1_LOCK_NO_CHANGE<BR>
+# SQL_CA1_LOCK_UNLOCK<BR>
+# SQL_CA1_POS_POSITION<BR>
+# SQL_CA1_POS_UPDATE<BR>
+# SQL_CA1_POS_DELETE<BR>
+# SQL_CA1_POS_REFRESH<BR>
+# SQL_CA1_POSITIONED_UPDATE<BR>
+# SQL_CA1_POSITIONED_DELETE<BR>
+# SQL_CA1_SELECT_FOR_UPDATE<BR>
+# SQL_CA1_BULK_ADD<BR>
+# SQL_CA1_BULK_UPDATE_BY_BOOKMARK<BR>
+# SQL_CA1_BULK_DELETE_BY_BOOKMARK<BR>
+# SQL_CA1_BULK_FETCH_BY_BOOKMARK</P>
+#
+# <P>For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "forward-only cursor" for "dynamic cursor" in the descriptions). </P>
+# </TD>
+# </TR>
+ SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a forward-only cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA2_READ_ONLY_CONCURRENCY<BR>
+# SQL_CA2_LOCK_CONCURRENCY<BR>
+# SQL_CA2_OPT_ROWVER_CONCURRENCY<BR>
+# SQL_CA2_OPT_VALUES_CONCURRENCY<BR>
+# SQL_CA2_SENSITIVITY_ADDITIONS<BR>
+# SQL_CA2_SENSITIVITY_DELETIONS<BR>
+# SQL_CA2_SENSITIVITY_UPDATES<BR>
+# SQL_CA2_MAX_ROWS_SELECT<BR>
+# SQL_CA2_MAX_ROWS_INSERT<BR>
+# SQL_CA2_MAX_ROWS_DELETE<BR>
+# SQL_CA2_MAX_ROWS_UPDATE<BR>
+# SQL_CA2_MAX_ROWS_CATALOG<BR>
+# SQL_CA2_MAX_ROWS_AFFECTS_ALL<BR>
+# SQL_CA2_CRC_EXACT<BR>
+# SQL_CA2_CRC_APPROXIMATE<BR>
+# SQL_CA2_SIMULATE_NON_UNIQUE<BR>
+# SQL_CA2_SIMULATE_TRY_UNIQUE<BR>
+# SQL_CA2_SIMULATE_UNIQUE </P>
+#
+# <P>For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES2 (and substitute "forward-only cursor" for "dynamic cursor" in the descriptions).</P>
+# </TD>
+# </TR>
+ SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_GETDATA_EXTENSIONS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating extensions to <B>SQLGetData</B>.
+# <P>The following bitmasks are used in conjunction with the flag to determine what common extensions the driver supports for <B>SQLGetData</B>:</P>
+#
+# <P>SQL_GD_ANY_COLUMN = <B>SQLGetData</B> can be called for any unbound column, including those before the last bound column. Note that the columns must be called in order of ascending column number unless SQL_GD_ANY_ORDER is also returned.</P>
+#
+# <P>SQL_GD_ANY_ORDER = <B>SQLGetData</B> can be called for unbound columns in any order. Note that <B>SQLGetData</B> can be called only for columns after the last bound column unless SQL_GD_ANY_COLUMN is also returned.</P>
+#
+# <P>SQL_GD_BLOCK = <B>SQLGetData</B> can be called for an unbound column in any row in a block (where the rowset size is greater than 1) of data after positioning to that row with <B>SQLSetPos</B>.</P>
+#
+# <P>SQL_GD_BOUND = <B>SQLGetData</B> can be called for bound columns as well as unbound columns. A driver cannot return this value unless it also returns SQL_GD_ANY_COLUMN.</P>
+#
+# <P><B>SQLGetData</B> is required to return data only from unbound columns that occur after the last bound column, are called in order of increasing column number, and are not in a row in a block of rows.</P>
+#
+# <P>If a driver supports bookmarks (either fixed-length or variable-length), it must support calling <B>SQLGetData</B> on column 0. This support is required regardless of what the driver returns for a call to <B>SQLGetInfo</B> with the SQL_GETDATA_EXTENSIONS <I>InfoType</I>.</P>
+# </TD>
+# </TR>
+ SQL_GETDATA_EXTENSIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_GROUP_BY<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the relationship between the columns in the <B>GROUP BY</B> clause and the nonaggregated columns in the select list:
+# <P>SQL_GB_COLLATE = A <B>COLLATE</B> clause can be specified at the end of each grouping column. (ODBC 3.0)</P>
+#
+# <P>SQL_GB_NOT_SUPPORTED = <B>GROUP BY</B> clauses are not supported. (ODBC 2.0)</P>
+#
+# <P>SQL_GB_GROUP_BY_EQUALS_SELECT = The <B>GROUP BY</B> clause must contain all nonaggregated columns in the select list. It cannot contain any other columns. For example, <B>SELECT DEPT, MAX(SALARY) FROM EMPLOYEE GROUP BY DEPT</B>. (ODBC 2.0)</P>
+#
+# <P>SQL_GB_GROUP_BY_CONTAINS_SELECT = The <B>GROUP BY</B> clause must contain all nonaggregated columns in the select list. It can contain columns that are not in the select list. For example, <B>SELECT DEPT, MAX(SALARY) FROM EMPLOYEE GROUP BY DEPT, AGE</B>. (ODBC 2.0)</P>
+#
+# <P>SQL_GB_NO_RELATION = The columns in the <B>GROUP BY</B> clause and the select list are not related. The meaning of nongrouped, nonaggregated columns in the select list is data source&#0150;dependent. For example, <B>SELECT DEPT, SALARY FROM EMPLOYEE GROUP BY DEPT, AGE</B>. (ODBC 2.0)</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return the SQL_GB_GROUP_BY_EQUALS_SELECT option as supported. An SQL-92 Full level&#0150;conformant driver will always return the SQL_GB_COLLATE option as supported. If none of the options is supported, the <B>GROUP BY</B> clause is not supported by the data source.</P>
+# </TD>
+# </TR>
+ SQL_GROUP_BY => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_IDENTIFIER_CASE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value as follows:
+# <P>SQL_IC_UPPER = Identifiers in SQL are not case-sensitive and are stored in uppercase in system catalog.</P>
+#
+# <P>SQL_IC_LOWER = Identifiers in SQL are not case-sensitive and are stored in lowercase in system catalog.</P>
+#
+# <P>SQL_IC_SENSITIVE = Identifiers in SQL are case-sensitive and are stored in mixed case in system catalog.</P>
+#
+# <P>SQL_IC_MIXED = Identifiers in SQL are not case-sensitive and are stored in mixed case in system catalog. </P>
+#
+# <P>Because identifiers in SQL-92 are never case-sensitive, a driver that conforms strictly to SQL-92 (any level) will never return the SQL_IC_SENSITIVE option as supported.</P>
+# </TD>
+# </TR>
+ SQL_IDENTIFIER_CASE => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_IDENTIFIER_QUOTE_CHAR<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>The character string used as the starting and ending delimiter of a quoted (delimited) identifier in SQL statements. (Identifiers passed as arguments to ODBC functions do not need to be quoted.) If the data source does not support quoted identifiers, a blank is returned.
+# <P>This character string can also be used for quoting catalog function arguments when the connection attribute SQL_ATTR_METADATA_ID is set to SQL_TRUE.</P>
+#
+# <P>Because the identifier quote character in SQL-92 is the double quotation mark ("), a driver that conforms strictly to SQL-92 will always return the double quotation mark character.</P>
+# </TD>
+# </TR>
+ SQL_IDENTIFIER_QUOTE_CHAR => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INDEX_KEYWORDS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that enumerates keywords in the CREATE INDEX statement that are supported by the driver:
+# <P>SQL_IK_NONE = None of the keywords is supported.</P>
+#
+# <P>SQL_IK_ASC = ASC keyword is supported.</P>
+#
+# <P>SQL_IK_DESC = DESC keyword is supported.</P>
+#
+# <P>SQL_IK_ALL = All keywords are supported.</P>
+#
+# <P>To see if the CREATE INDEX statement is supported, an application calls <B>SQLGetInfo</B> with the SQL_DLL_INDEX information type.</P>
+# </TD>
+# </TR>
+ SQL_INDEX_KEYWORDS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INFO_SCHEMA_VIEWS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the views in the INFORMATION_SCHEMA that are supported by the driver. The views in, and the contents of, INFORMATION_SCHEMA are as defined in SQL-92.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which views are supported:</P>
+#
+# <P>SQL_ISV_ASSERTIONS = Identifies the catalog's assertions that are owned by a given user. (Full level)</P>
+#
+# <P>SQL_ISV_CHARACTER_SETS = Identifies the catalog's character sets that are accessible to a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_CHECK_CONSTRAINTS = Identifies the CHECK constraints that are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_COLLATIONS = Identifies the character collations for the catalog that are accessible to a given user. (Full level)</P>
+#
+# <P>SQL_ISV_COLUMN_DOMAIN_USAGE = Identifies columns for the catalog that are dependent on domains defined in the catalog and are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_COLUMN_PRIVILEGES = Identifies the privileges on columns of persistent tables that are available to or granted by a given user. (FIPS Transitional level)</P>
+#
+# <P>SQL_ISV_COLUMNS = Identifies the columns of persistent tables that are accessible to a given user. (FIPS Transitional level)</P>
+#
+# <P>SQL_ISV_CONSTRAINT_COLUMN_USAGE = Similar to CONSTRAINT_TABLE_USAGE view, columns are identified for the various constraints that are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_CONSTRAINT_TABLE_USAGE = Identifies the tables that are used by constraints (referential, unique, and assertions), and are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_DOMAIN_CONSTRAINTS = Identifies the domain constraints (of the domains in the catalog) that are accessible to a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_DOMAINS = Identifies the domains defined in a catalog that are accessible to the user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_KEY_COLUMN_USAGE = Identifies columns defined in the catalog that are constrained as keys by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_REFERENTIAL_CONSTRAINTS = Identifies the referential constraints that are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_SCHEMATA = Identifies the schemas that are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_SQL_LANGUAGES = Identifies the SQL conformance levels, options, and dialects supported by the SQL implementation. (Intermediate level)</P>
+#
+# <P>SQL_ISV_TABLE_CONSTRAINTS = Identifies the table constraints that are owned by a given user. (Intermediate level)</P>
+#
+# <P>SQL_ISV_TABLE_PRIVILEGES = Identifies the privileges on persistent tables that are available to or granted by a given user. (FIPS Transitional level)</P>
+#
+# <P>SQL_ISV_TABLES = Identifies the persistent tables defined in a catalog that are accessible to a given user. (FIPS Transitional level)</P>
+#
+# <P>SQL_ISV_TRANSLATIONS = Identifies character translations for the catalog that are accessible to a given user. (Full level) </P>
+#
+# <P>SQL_ISV_USAGE_PRIVILEGES = Identifies the USAGE privileges on catalog objects that are available to or owned by a given user. (FIPS Transitional level)</P>
+#
+# <P>SQL_ISV_VIEW_COLUMN_USAGE = Identifies the columns on which the catalog's views that are owned by a given user are dependent. (Intermediate level)</P>
+#
+# <P>SQL_ISV_VIEW_TABLE_USAGE = Identifies the tables on which the catalog's views that are owned by a given user are dependent. (Intermediate level) </P>
+#
+# <P>SQL_ISV_VIEWS = Identifies the viewed tables defined in this catalog that are accessible to a given user. (FIPS Transitional level)</P>
+# </TD>
+# </TR>
+ SQL_INFO_SCHEMA_VIEWS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INSERT_STATEMENT<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that indicates support for <B>INSERT</B> statements:
+# <P>SQL_IS_INSERT_LITERALS</P>
+#
+# <P>SQL_IS_INSERT_SEARCHED</P>
+#
+# <P>SQL_IS_SELECT_INTO </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_INSERT_STATEMENT => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_INTEGRITY<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source supports the Integrity Enhancement Facility; "N" if it does not.
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_ODBC_SQL_OPT_IEF.</P>
+# </TD>
+# </TR>
+ SQL_INTEGRITY => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_KEYSET_CURSOR_ATTRIBUTES1<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a keyset cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_KEYSET_CURSOR_ATTRIBUTES2.
+# <P>The following bitmasks are used to determine which attributes are supported: </P>
+#
+# <P>SQL_CA1_NEXT<BR>
+# SQL_CA1_ABSOLUTE<BR>
+# SQL_CA1_RELATIVE<BR>
+# SQL_CA1_BOOKMARK<BR>
+# SQL_CA1_LOCK_EXCLUSIVE<BR>
+# SQL_CA1_LOCK_NO_CHANGE<BR>
+# SQL_CA1_LOCK_UNLOCK<BR>
+# SQL_CA1_POS_POSITION<BR>
+# SQL_CA1_POS_UPDATE<BR>
+# SQL_CA1_POS_DELETE<BR>
+# SQL_CA1_POS_REFRESH<BR>
+# SQL_CA1_POSITIONED_UPDATE<BR>
+# SQL_CA1_POSITIONED_DELETE<BR>
+# SQL_CA1_SELECT_FOR_UPDATE<BR>
+# SQL_CA1_BULK_ADD<BR>
+# SQL_CA1_BULK_UPDATE_BY_BOOKMARK<BR>
+# SQL_CA1_BULK_DELETE_BY_BOOKMARK<BR>
+# SQL_CA1_BULK_FETCH_BY_BOOKMARK</P>
+#
+# <P>For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "keyset-driven cursor" for "dynamic cursor" in the descriptions).</P>
+#
+# <P>An SQL-92 Intermediate level&#0150;conformant driver will usually return the SQL_CA1_NEXT, SQL_CA1_ABSOLUTE, and SQL_CA1_RELATIVE options as supported, because the driver supports scrollable cursors through the embedded SQL FETCH statement. Because this does not directly determine the underlying SQL support, however, scrollable cursors may not be supported, even for an SQL-92 Intermediate level&#0150;conformant driver.</P>
+# </TD>
+# </TR>
+ SQL_KEYSET_CURSOR_ATTRIBUTES1 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_KEYSET_CURSOR_ATTRIBUTES2<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a keyset cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_KEYSET_CURSOR_ATTRIBUTES1.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA2_READ_ONLY_CONCURRENCY<BR>
+# SQL_CA2_LOCK_CONCURRENCY<BR>
+# SQL_CA2_OPT_ROWVER_CONCURRENCY<BR>
+# SQL_CA2_OPT_VALUES_CONCURRENCY<BR>
+# SQL_CA2_SENSITIVITY_ADDITIONS<BR>
+# SQL_CA2_SENSITIVITY_DELETIONS<BR>
+# SQL_CA2_SENSITIVITY_UPDATES<BR>
+# SQL_CA2_MAX_ROWS_SELECT<BR>
+# SQL_CA2_MAX_ROWS_INSERT<BR>
+# SQL_CA2_MAX_ROWS_DELETE<BR>
+# SQL_CA2_MAX_ROWS_UPDATE<BR>
+# SQL_CA2_MAX_ROWS_CATALOG<BR>
+# SQL_CA2_MAX_ROWS_AFFECTS_ALL<BR>
+# SQL_CA2_CRC_EXACT<BR>
+# SQL_CA2_CRC_APPROXIMATE<BR>
+# SQL_CA2_SIMULATE_NON_UNIQUE<BR>
+# SQL_CA2_SIMULATE_TRY_UNIQUE<BR>
+# SQL_CA2_SIMULATE_UNIQUE</P>
+#
+# <P>For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "keyset-driven cursor" for "dynamic cursor" in the descriptions).</P>
+# </TD>
+# </TR>
+ SQL_KEYSET_CURSOR_ATTRIBUTES2 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_KEYWORDS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string containing a comma-separated list of all data source&#0150;specific keywords. This list does not contain keywords specific to ODBC or keywords used by both the data source and ODBC. This list represents all the reserved keywords; interoperable applications should not use these words in object names.
+# <P>For a list of ODBC keywords, see "<A HREF="odbclist_of_reserved_keywords.htm">List of Reserved Keywords</A>" in Appendix C, "SQL Grammar." The <B>#define</B> value SQL_ODBC_KEYWORDS contains a comma-separated list of ODBC keywords.</P>
+# </TD>
+# </TR>
+ SQL_KEYWORDS => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_LIKE_ESCAPE_CLAUSE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source supports an escape character for the percent character (%) and underscore character (_) in a <B>LIKE</B> predicate and the driver supports the ODBC syntax for defining a <B>LIKE</B> predicate escape character; "N" otherwise.</TD>
+# </TR>
+ SQL_LIKE_ESCAPE_CLAUSE => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_ASYNC_CONCURRENT_STATEMENTS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value specifying the maximum number of active concurrent statements in asynchronous mode that the driver can support on a given connection. If there is no specific limit or the limit is unknown, this value is zero.</TD>
+# </TR>
+ SQL_MAX_ASYNC_CONCURRENT_STATEMENTS => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_BINARY_LITERAL_LEN<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER value specifying the maximum length (number of hexadecimal characters, excluding the literal prefix and suffix returned by <B>SQLGetTypeInfo</B>) of a binary literal in an SQL statement. For example, the binary literal 0xFFAA has a length of 4. If there is no maximum length or the length is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_BINARY_LITERAL_LEN => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_CATALOG_NAME_LEN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a catalog name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
+# <P>An FIPS Full level&#0150;conformant driver will return at least 128.</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_MAX_QUALIFIER_NAME_LEN.</P>
+# </TD>
+# </TR>
+ SQL_MAX_CATALOG_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_CHAR_LITERAL_LEN<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER value specifying the maximum length (number of characters, excluding the literal prefix and suffix returned by <B>SQLGetTypeInfo</B>) of a character literal in an SQL statement. If there is no maximum length or the length is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_CHAR_LITERAL_LEN => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_COLUMN_NAME_LEN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a column name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 18. An FIPS Intermediate level&#0150;conformant driver will return at least 128.</P>
+# </TD>
+# </TR>
+ SQL_MAX_COLUMN_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_COLUMNS_IN_GROUP_BY<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of columns allowed in a <B>GROUP BY</B> clause. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 6. An FIPS Intermediate level&#0150;conformant driver will return at least 15.</P>
+# </TD>
+# </TR>
+ SQL_MAX_COLUMNS_IN_GROUP_BY => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_COLUMNS_IN_INDEX<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of columns allowed in an index. If there is no specified limit or the limit is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_COLUMNS_IN_INDEX => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_COLUMNS_IN_ORDER_BY<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of columns allowed in an <B>ORDER BY</B> clause. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 6. An FIPS Intermediate level&#0150;conformant driver will return at least 15.</P>
+# </TD>
+# </TR>
+ SQL_MAX_COLUMNS_IN_ORDER_BY => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_COLUMNS_IN_SELECT<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of columns allowed in a select list. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 100. An FIPS Intermediate level&#0150;conformant driver will return at least 250.</P>
+# </TD>
+# </TR>
+ SQL_MAX_COLUMNS_IN_SELECT => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_COLUMNS_IN_TABLE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of columns allowed in a table. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 100. An FIPS Intermediate level&#0150;conformant driver will return at least 250.</P>
+# </TD>
+# </TR>
+ SQL_MAX_COLUMNS_IN_TABLE => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_CONCURRENT_ACTIVITIES<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of active statements that the driver can support for a connection. A statement is defined as active if it has results pending, with the term "results" meaning rows from a <B>SELECT</B> operation or rows affected by an <B>INSERT</B>, <B>UPDATE</B>, or <B>DELETE</B> operation (such as a row count), or if it is in a NEED_DATA state. This value can reflect a limitation imposed by either the driver or the data source. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_ACTIVE_STATEMENTS.</P>
+# </TD>
+# </TR>
+ SQL_MAX_CONCURRENT_ACTIVITIES => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_CURSOR_NAME_LEN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a cursor name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 18. An FIPS Intermediate level&#0150;conformant driver will return at least 128.</P>
+# </TD>
+# </TR>
+ SQL_MAX_CURSOR_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_DRIVER_CONNECTIONS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of active connections that the driver can support for an environment. This value can reflect a limitation imposed by either the driver or the data source. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_ACTIVE_CONNECTIONS.</P>
+# </TD>
+# </TR>
+ SQL_MAX_DRIVER_CONNECTIONS => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_IDENTIFIER_LEN<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUSMALLINT that indicates the maximum size in characters that the data source supports for user-defined names.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 18. An FIPS Intermediate level&#0150;conformant driver will return at least 128.</P>
+# </TD>
+# </TR>
+ SQL_MAX_IDENTIFIER_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_INDEX_SIZE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER value specifying the maximum number of bytes allowed in the combined fields of an index. If there is no specified limit or the limit is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_INDEX_SIZE => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_PROCEDURE_NAME_LEN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a procedure name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_PROCEDURE_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_ROW_SIZE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER value specifying the maximum length of a single row in a table. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 2,000. An FIPS Intermediate level&#0150;conformant driver will return at least 8,000.</P>
+# </TD>
+# </TR>
+ SQL_MAX_ROW_SIZE => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_ROW_SIZE_INCLUDES_LONG<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>A character string: "Y" if the maximum row size returned for the SQL_MAX_ROW_SIZE information type includes the length of all SQL_LONGVARCHAR and SQL_LONGVARBINARY columns in the row; "N" otherwise.</TD>
+# </TR>
+ SQL_MAX_ROW_SIZE_INCLUDES_LONG => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_SCHEMA_NAME_LEN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a schema name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 18. An FIPS Intermediate level&#0150;conformant driver will return at least 128.</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_MAX_OWNER_NAME_LEN.</P>
+# </TD>
+# </TR>
+ SQL_MAX_SCHEMA_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_STATEMENT_LEN<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER value specifying the maximum length (number of characters, including white space) of an SQL statement. If there is no maximum length or the length is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_STATEMENT_LEN => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_TABLE_NAME_LEN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a table name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 18. An FIPS Intermediate level&#0150;conformant driver will return at least 128.</P>
+# </TD>
+# </TR>
+ SQL_MAX_TABLE_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_TABLES_IN_SELECT<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum number of tables allowed in the <B>FROM</B> clause of a <B>SELECT </B>statement. If there is no specified limit or the limit is unknown, this value is set to zero.
+# <P>An FIPS Entry level&#0150;conformant driver will return at least 15. An FIPS Intermediate level&#0150;conformant driver will return at least 50.</P>
+# </TD>
+# </TR>
+ SQL_MAX_TABLES_IN_SELECT => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MAX_USER_NAME_LEN<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying the maximum length of a user name in the data source. If there is no maximum length or the length is unknown, this value is set to zero.</TD>
+# </TR>
+ SQL_MAX_USER_NAME_LEN => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MULT_RESULT_SETS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source supports multiple result sets, "N" if it does not.
+# <P>For more information on multiple result sets, see "<A HREF="odbcmultiple_results.htm">Multiple Results</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+# </TD>
+# </TR>
+ SQL_MULT_RESULT_SETS => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_MULTIPLE_ACTIVE_TXN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the driver supports more than one active transaction at the same time, "N" if only one transaction can be active at any time.
+# <P>The information returned for this information type does not apply in the case of distributed transactions.</P>
+# </TD>
+# </TR>
+ SQL_MULTIPLE_ACTIVE_TXN => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_NEED_LONG_DATA_LEN<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source needs the length of a long data value (the data type is SQL_LONGVARCHAR, SQL_LONGVARBINARY, or a long data source&#0150;specific data type) before that value is sent to the data source, "N" if it does not. For more information, see <B>SQLBindParameter</B> and <B>SQLSetPos</B>.</TD>
+# </TR>
+ SQL_NEED_LONG_DATA_LEN => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_NON_NULLABLE_COLUMNS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying whether the data source supports NOT NULL in columns:
+# <P>SQL_NNC_NULL = All columns must be nullable.</P>
+#
+# <P>SQL_NNC_NON_NULL = Columns cannot be nullable. (The data source supports the <B>NOT NULL</B> column constraint in <B>CREATE TABLE</B> statements.) </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will return SQL_NNC_NON_NULL.</P>
+# </TD>
+# </TR>
+ SQL_NON_NULLABLE_COLUMNS => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_NULL_COLLATION<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value specifying where NULLs are sorted in a result set:
+# <P>SQL_NC_END = NULLs are sorted at the end of the result set, regardless of the ASC or DESC keywords.</P>
+#
+# <P>SQL_NC_HIGH = NULLs are sorted at the high end of the result set, depending on the ASC or DESC keywords.</P>
+#
+# <P>SQL_NC_LOW = NULLs are sorted at the low end of the result set, depending on the ASC or DESC keywords.</P>
+#
+# <P>SQL_NC_START = NULLs are sorted at the start of the result set, regardless of the ASC or DESC keywords.</P>
+# </TD>
+# </TR>
+ SQL_NULL_COLLATION => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_NUMERIC_FUNCTIONS<BR>
+# (ODBC 1.0)
+# <P>The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.</P>
+# </TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the scalar numeric functions supported by the driver and associated data source.
+# <P>The following bitmasks are used to determine which numeric functions are supported:</P>
+#
+# <P>SQL_FN_NUM_ABS (ODBC 1.0)<BR>
+# SQL_FN_NUM_ACOS (ODBC 1.0)<BR>
+# SQL_FN_NUM_ASIN (ODBC 1.0)<BR>
+# SQL_FN_NUM_ATAN (ODBC 1.0)<BR>
+# SQL_FN_NUM_ATAN2 (ODBC 1.0)<BR>
+# SQL_FN_NUM_CEILING (ODBC 1.0)<BR>
+# SQL_FN_NUM_COS (ODBC 1.0)<BR>
+# SQL_FN_NUM_COT (ODBC 1.0)<BR>
+# SQL_FN_NUM_DEGREES (ODBC 2.0)<BR>
+# SQL_FN_NUM_EXP (ODBC 1.0)<BR>
+# SQL_FN_NUM_FLOOR (ODBC 1.0)<BR>
+# SQL_FN_NUM_LOG (ODBC 1.0)<BR>
+# SQL_FN_NUM_LOG10 (ODBC 2.0)<BR>
+# SQL_FN_NUM_MOD (ODBC 1.0)<BR>
+# SQL_FN_NUM_PI (ODBC 1.0)<BR>
+# SQL_FN_NUM_POWER (ODBC 2.0)<BR>
+# SQL_FN_NUM_RADIANS (ODBC 2.0)<BR>
+# SQL_FN_NUM_RAND (ODBC 1.0)<BR>
+# SQL_FN_NUM_ROUND (ODBC 2.0)<BR>
+# SQL_FN_NUM_SIGN (ODBC 1.0)<BR>
+# SQL_FN_NUM_SIN (ODBC 1.0)<BR>
+# SQL_FN_NUM_SQRT (ODBC 1.0)<BR>
+# SQL_FN_NUM_TAN (ODBC 1.0)<BR>
+# SQL_FN_NUM_TRUNCATE (ODBC 2.0)</P>
+# </TD>
+# </TR>
+ SQL_NUMERIC_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ODBC_INTERFACE_CONFORMANCE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value indicating the level of the ODBC 3<I>.x</I> interface that the driver conforms to.
+# <P>SQL_OIC_CORE: The minimum level that all ODBC drivers are expected to conform to. This level includes basic interface elements such as connection functions, functions for preparing and executing an SQL statement, basic result set metadata functions, basic catalog functions, and so on.</P>
+#
+# <P>SQL_OIC_LEVEL1: A level including the core standards compliance level functionality, plus scrollable cursors, bookmarks, positioned updates and deletes, and so on.</P>
+#
+# <P>SQL_OIC_LEVEL2: A level including level 1 standards compliance level functionality, plus advanced features such as sensitive cursors; update, delete, and refresh by bookmarks; stored procedure support; catalog functions for primary and foreign keys; multicatalog support; and so on.</P>
+#
+# <P>For more information, see "<A HREF="odbcinterface_conformance_levels.htm">Interface Conformance Levels</A>" in Chapter 4: ODBC Fundamentals.</P>
+# </TD>
+# </TR>
+ SQL_ODBC_INTERFACE_CONFORMANCE => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ODBC_VER<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the version of ODBC to which the Driver Manager conforms. The version is of the form ##.##.0000, where the first two digits are the major version and the next two digits are the minor version. This is implemented solely in the Driver Manager.</TD>
+# </TR>
+ SQL_ODBC_VER => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_OJ_CAPABILITIES<BR>
+# (ODBC 2.01)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the types of outer joins supported by the driver and data source. The following bitmasks are used to determine which types are supported:
+# <P>SQL_OJ_LEFT = Left outer joins are supported.</P>
+#
+# <P>SQL_OJ_RIGHT = Right outer joins are supported.</P>
+#
+# <P>SQL_OJ_FULL = Full outer joins are supported.</P>
+#
+# <P>SQL_OJ_NESTED = Nested outer joins are supported.</P>
+#
+# <P>SQL_OJ_NOT_ORDERED = The column names in the ON clause of the outer join do not have to be in the same order as their respective table names in the <B>OUTER JOIN </B>clause.</P>
+#
+# <P>SQL_OJ_INNER = The inner table (the right table in a left outer join or the left table in a right outer join) can also be used in an inner join. This does not apply to full outer joins, which do not have an inner table.</P>
+#
+# <P>SQL_OJ_ALL_COMPARISON_OPS = The comparison operator in the ON clause can be any of the ODBC comparison operators. If this bit is not set, only the equals (=) comparison operator can be used in outer joins.</P>
+#
+# <P>If none of these options is returned as supported, no outer join clause is supported.</P>
+#
+# <P>For information on the support of relational join operators in a SELECT statement, as defined by SQL-92, see SQL_SQL92_RELATIONAL_JOIN_OPERATORS.</P>
+# </TD>
+# </TR>
+ SQL_OJ_CAPABILITIES => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ORDER_BY_COLUMNS_IN_SELECT<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string: "Y" if the columns in the <B>ORDER BY</B> clause must be in the select list; otherwise, "N".</TD>
+# </TR>
+ SQL_ORDER_BY_COLUMNS_IN_SELECT => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_PARAM_ARRAY_ROW_COUNTS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER enumerating the driver's properties regarding the availability of row counts in a parameterized execution. Has the following values:
+# <P>SQL_PARC_BATCH = Individual row counts are available for each set of parameters. This is conceptually equivalent to the driver generating a batch of SQL statements, one for each parameter set in the array. Extended error information can be retrieved by using the SQL_PARAM_STATUS_PTR descriptor field.</P>
+#
+# <P>SQL_PARC_NO_BATCH = There is only one row count available, which is the cumulative row count resulting from the execution of the statement for the entire array of parameters. This is conceptually equivalent to treating the statement along with the entire parameter array as one atomic unit. Errors are handled the same as if one statement were executed.</P>
+# </TD>
+# </TR>
+ SQL_PARAM_ARRAY_ROW_COUNTS => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_PARAM_ARRAY_SELECTS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER enumerating the driver's properties regarding the availability of result sets in a parameterized execution. Has the following values:
+# <P>SQL_PAS_BATCH = There is one result set available per set of parameters. This is conceptually equivalent to the driver generating a batch of SQL statements, one for each parameter set in the array.</P>
+#
+# <P>SQL_PAS_NO_BATCH = There is only one result set available, which represents the cumulative result set resulting from the execution of the statement for the entire array of parameters. This is conceptually equivalent to treating the statement along with the entire parameter array as one atomic unit.</P>
+#
+# <P>SQL_PAS_NO_SELECT = A driver does not allow a result-set generating statement to be executed with an array of parameters.</P>
+# </TD>
+# </TR>
+ SQL_PARAM_ARRAY_SELECTS => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_PROCEDURE_TERM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the data source vendor's name for a procedure; for example, "database procedure", "stored procedure", "procedure", "package", or "stored query".</TD>
+# </TR>
+ SQL_PROCEDURE_TERM => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_PROCEDURES<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if the data source supports procedures and the driver supports the ODBC procedure invocation syntax; "N" otherwise.</TD>
+# </TR>
+ SQL_PROCEDURES => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_POS_OPERATIONS (ODBC 2.0)</TD>
+# <TD width=50%>An SQLINTEGER bitmask enumerating the support operations in <B>SQLSetPos</B>.
+# <P>The following bitmasks are used in conjunction with the flag to determine which options are supported.</P>
+#
+# <P>SQL_POS_POSITION (ODBC 2.0) SQL_POS_REFRESH (ODBC 2.0) SQL_POS_UPDATE (ODBC 2.0) SQL_POS_DELETE (ODBC 2.0) SQL_POS_ADD (ODBC 2.0) </P>
+# </TD>
+# </TR>
+ SQL_POS_OPERATIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_QUOTED_IDENTIFIER_CASE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUSMALLINT value as follows:
+# <P>SQL_IC_UPPER = Quoted identifiers in SQL are not case-sensitive and are stored in uppercase in the system catalog.</P>
+#
+# <P>SQL_IC_LOWER = Quoted identifiers in SQL are not case-sensitive and are stored in lowercase in the system catalog.</P>
+#
+# <P>SQL_IC_SENSITIVE = Quoted identifiers in SQL are case-sensitive and are stored in mixed case in the system catalog. (In an SQL-92&#0150;compliant database, quoted identifiers are always case-sensitive.)</P>
+#
+# <P>SQL_IC_MIXED = Quoted identifiers in SQL are not case-sensitive and are stored in mixed case in the system catalog.</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return SQL_IC_SENSITIVE.</P>
+# </TD>
+# </TR>
+ SQL_QUOTED_IDENTIFIER_CASE => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ROW_UPDATES<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string: "Y" if a keyset-driven or mixed cursor maintains row versions or values for all fetched rows and therefore can detect any updates made to a row by any user since the row was last fetched. (This applies only to updates, not to deletions or insertions.) The driver can return the SQL_ROW_UPDATED flag to the row status array when <B>SQLFetchScroll</B> is called. Otherwise, "N".</TD>
+# </TR>
+ SQL_ROW_UPDATES => {
+ type => q(YesNo),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SCHEMA_TERM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the data source vendor's name for an schema; for example, "owner", "Authorization ID", or "Schema".
+# <P>The character string can be returned in upper, lower, or mixed case.</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return "schema".</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_OWNER_TERM.</P>
+# </TD>
+# </TR>
+ SQL_SCHEMA_TERM => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SCHEMA_USAGE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the statements in which schemas can be used:
+# <P>SQL_SU_DML_STATEMENTS = Schemas are supported in all Data Manipulation Language statements: <B>SELECT</B>, <B>INSERT</B>, <B>UPDATE</B>, <B>DELETE</B>, and if supported, <B>SELECT FOR UPDATE</B> and positioned update and delete statements.</P>
+#
+# <P>SQL_SU_PROCEDURE_INVOCATION = Schemas are supported in the ODBC procedure invocation statement.</P>
+#
+# <P>SQL_SU_TABLE_DEFINITION = Schemas are supported in all table definition statements: <B>CREATE TABLE</B>, <B>CREATE VIEW</B>, <B>ALTER TABLE</B>, <B>DROP TABLE</B>, and <B>DROP VIEW</B>.</P>
+#
+# <P>SQL_SU_INDEX_DEFINITION = Schemas are supported in all index definition statements: <B>CREATE INDEX</B> and <B>DROP INDEX</B>.</P>
+#
+# <P>SQL_SU_PRIVILEGE_DEFINITION = Schemas are supported in all privilege definition statements: <B>GRANT</B> and <B>REVOKE</B>. </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return the SQL_SU_DML_STATEMENTS, SQL_SU_TABLE_DEFINITION, and SQL_SU_PRIVILEGE_DEFINITION options, as supported.</P>
+#
+# <P>This <I>InfoType</I> has been renamed for ODBC 3.0 from the ODBC 2.0 <I>InfoType</I> SQL_OWNER_USAGE.</P>
+# </TD>
+# </TR>
+ SQL_SCHEMA_USAGE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SCROLL_OPTIONS<BR>
+# (ODBC 1.0)
+# <P>The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.</P>
+# </TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the scroll options supported for scrollable cursors.
+# <P>The following bitmasks are used to determine which options are supported:</P>
+#
+# <P>SQL_SO_FORWARD_ONLY = The cursor only scrolls forward. (ODBC 1.0)</P>
+#
+# <P>SQL_SO_STATIC = The data in the result set is static. (ODBC 2.0)</P>
+#
+# <P>SQL_SO_KEYSET_DRIVEN = The driver saves and uses the keys for every row in the result set. (ODBC 1.0)</P>
+#
+# <P>SQL_SO_DYNAMIC = The driver keeps the keys for every row in the rowset (the keyset size is the same as the rowset size). (ODBC 1.0)</P>
+#
+# <P>SQL_SO_MIXED = The driver keeps the keys for every row in the keyset, and the keyset size is greater than the rowset size. The cursor is keyset-driven inside the keyset and dynamic outside the keyset. (ODBC 1.0)</P>
+#
+# <P>For information about scrollable cursors, see "<A HREF="odbcscrollable_cursors.htm">Scrollable Cursors</A>" in Chapter 11: Retrieving Results (Advanced)</P>
+# </TD>
+# </TR>
+ SQL_SCROLL_OPTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SEARCH_PATTERN_ESCAPE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string specifying what the driver supports as an escape character that permits the use of the pattern match metacharacters underscore (_) and percent sign (%) as valid characters in search patterns. This escape character applies only for those catalog function arguments that support search strings. If this string is empty, the driver does not support a search-pattern escape character.
+# <P>Because this information type does not indicate general support of the escape character in the <B>LIKE</B> predicate, SQL-92 does not include requirements for this character string.</P>
+#
+# <P>This <I>InfoType</I> is limited to catalog functions. For a description of the use of the escape character in search pattern strings, see "<A HREF="odbcpattern_value_arguments.htm">Pattern Value Arguments</A>" in Chapter 7: Catalog Functions.</P>
+# </TD>
+# </TR>
+ SQL_SEARCH_PATTERN_ESCAPE => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SERVER_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the actual data source&#0150;specific server name; useful when a data source name is used during <B>SQLConnect</B>, <B>SQLDriverConnect</B>, and<B> SQLBrowseConnect</B>.</TD>
+# </TR>
+ SQL_SERVER_NAME => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SPECIAL_CHARACTERS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>A character string containing all special characters (that is, all characters except a through z, A through Z, 0 through 9, and underscore) that can be used in an identifier name, such as a table name, column column name, or index name, on the data source. For example, "#$^". If an identifier contains one or more of these characters, the identifier must be a delimited identifier.</TD>
+# </TR>
+ SQL_SPECIAL_CHARACTERS => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL_CONFORMANCE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER value indicating the level of SQL-92 supported by the driver:
+# <P>SQL_SC_SQL92_ENTRY = Entry level SQL-92 compliant.</P>
+#
+# <P>SQL_SC_FIPS127_2_TRANSITIONAL = FIPS 127-2 transitional level compliant.</P>
+#
+# <P>SQL_SC_SQL92_FULL = Full level SQL-92 compliant.</P>
+#
+# <P>SQL_SC_ SQL92_INTERMEDIATE = Intermediate level SQL-92 compliant.</P>
+# </TD>
+# </TR>
+ SQL_SQL_CONFORMANCE => {
+ type => q(Long),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_DATETIME_FUNCTIONS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the datetime scalar functions that are supported by the driver and the associated data source, as defined in SQL-92.
+# <P>The following bitmasks are used to determine which datetime functions are supported:</P>
+#
+# <P>SQL_SDF_CURRENT_DATE<BR>
+# SQL_SDF_CURRENT_TIME<BR>
+# SQL_SDF_CURRENT_TIMESTAMP</P>
+# </TD>
+# </TR>
+ SQL_SQL92_DATETIME_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_FOREIGN_KEY_DELETE_RULE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the rules supported for a foreign key in a <B>DELETE</B> statement, as defined in SQL-92.
+# <P>The following bitmasks are used to determine which clauses are supported by the data source:</P>
+#
+# <P>SQL_SFKD_CASCADE<BR>
+# SQL_SFKD_NO_ACTION<BR>
+# SQL_SFKD_SET_DEFAULT<BR>
+# SQL_SFKD_SET_NULL</P>
+#
+# <P>An FIPS Transitional level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_SQL92_FOREIGN_KEY_DELETE_RULE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_FOREIGN_KEY_UPDATE_RULE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the rules supported for a foreign key in an <B>UPDATE</B> statement, as defined in SQL-92.
+# <P>The following bitmasks are used to determine which clauses are supported by the data source:</P>
+#
+# <P>SQL_SFKU_CASCADE<BR>
+# SQL_SFKU_NO_ACTION<BR>
+# SQL_SFKU_SET_DEFAULT<BR>
+# SQL_SFKU_SET_NULL </P>
+#
+# <P>An SQL-92 Full level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_SQL92_FOREIGN_KEY_UPDATE_RULE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_GRANT<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses supported in the <B>GRANT</B> statement, as defined in SQL-92.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which clauses are supported by the data source:</P>
+#
+# <P>SQL_SG_DELETE_TABLE (Entry level)<BR>
+# SQL_SG_INSERT_COLUMN (Intermediate level)<BR>
+# SQL_SG_INSERT_TABLE (Entry level) <BR>
+# SQL_SG_REFERENCES_TABLE (Entry level)<BR>
+# SQL_SG_REFERENCES_COLUMN (Entry level)<BR>
+# SQL_SG_SELECT_TABLE (Entry level)<BR>
+# SQL_SG_UPDATE_COLUMN (Entry level)<BR>
+# SQL_SG_UPDATE_TABLE (Entry level) <BR>
+# SQL_SG_USAGE_ON_DOMAIN (FIPS Transitional level)<BR>
+# SQL_SG_USAGE_ON_CHARACTER_SET (FIPS Transitional level)<BR>
+# SQL_SG_USAGE_ON_COLLATION (FIPS Transitional level)<BR>
+# SQL_SG_USAGE_ON_TRANSLATION (FIPS Transitional level)<BR>
+# SQL_SG_WITH_GRANT_OPTION (Entry level)</P>
+# </TD>
+# </TR>
+ SQL_SQL92_GRANT => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_NUMERIC_VALUE_FUNCTIONS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the numeric value scalar functions that are supported by the driver and the associated data source, as defined in SQL-92.
+# <P>The following bitmasks are used to determine which numeric functions are supported:</P>
+#
+# <P>SQL_SNVF_BIT_LENGTH<BR>
+# SQL_SNVF_CHAR_LENGTH<BR>
+# SQL_SNVF_CHARACTER_LENGTH<BR>
+# SQL_SNVF_EXTRACT<BR>
+# SQL_SNVF_OCTET_LENGTH<BR>
+# SQL_SNVF_POSITION</P>
+# </TD>
+# </TR>
+ SQL_SQL92_NUMERIC_VALUE_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_PREDICATES<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the predicates supported in a <B>SELECT</B> statement, as defined in SQL-92.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which options are supported by the data source:</P>
+#
+# <P>SQL_SP_BETWEEN (Entry level)<BR>
+# SQL_SP_COMPARISON (Entry level)<BR>
+# SQL_SP_EXISTS (Entry level)<BR>
+# SQL_SP_IN (Entry level)<BR>
+# SQL_SP_ISNOTNULL (Entry level)<BR>
+# SQL_SP_ISNULL (Entry level)<BR>
+# SQL_SP_LIKE (Entry level)<BR>
+# SQL_SP_MATCH_FULL (Full level)<BR>
+# SQL_SP_MATCH_PARTIAL(Full level)<BR>
+# SQL_SP_MATCH_UNIQUE_FULL (Full level)<BR>
+# SQL_SP_MATCH_UNIQUE_PARTIAL (Full level)<BR>
+# SQL_SP_OVERLAPS (FIPS Transitional level)<BR>
+# SQL_SP_QUANTIFIED_COMPARISON (Entry level)<BR>
+# SQL_SP_UNIQUE (Entry level)</P>
+# </TD>
+# </TR>
+ SQL_SQL92_PREDICATES => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_RELATIONAL_JOIN_OPERATORS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the relational join operators supported in a <B>SELECT</B> statement, as defined in SQL-92.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which options are supported by the data source:</P>
+#
+# <P>SQL_SRJO_CORRESPONDING_CLAUSE (Intermediate level)<BR>
+# SQL_SRJO_CROSS_JOIN (Full level)<BR>
+# SQL_SRJO_EXCEPT_JOIN (Intermediate level)<BR>
+# SQL_SRJO_FULL_OUTER_JOIN (Intermediate level) <BR>
+# SQL_SRJO_INNER_JOIN (FIPS Transitional level)<BR>
+# SQL_SRJO_INTERSECT_JOIN (Intermediate level)<BR>
+# SQL_SRJO_LEFT_OUTER_JOIN (FIPS Transitional level)<BR>
+# SQL_SRJO_NATURAL_JOIN (FIPS Transitional level)<BR>
+# SQL_SRJO_RIGHT_OUTER_JOIN (FIPS Transitional level)<BR>
+# SQL_SRJO_UNION_JOIN (Full level)</P>
+#
+# <P>SQL_SRJO_INNER_JOIN indicates support for the <B>INNER JOIN </B>syntax, not for the inner join capability. Support for the <B>INNER JOIN</B> syntax is FIPS TRANSITIONAL, while support for the inner join capability is <B>ENTRY</B>.</P>
+# </TD>
+# </TR>
+ SQL_SQL92_RELATIONAL_JOIN_OPERATORS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_REVOKE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the clauses supported in the <B>REVOKE</B> statement, as defined in SQL-92, supported by the data source.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which clauses are supported by the data source:</P>
+#
+# <P>SQL_SR_CASCADE (FIPS Transitional level) <BR>
+# SQL_SR_DELETE_TABLE (Entry level)<BR>
+# SQL_SR_GRANT_OPTION_FOR (Intermediate level) <BR>
+# SQL_SR_INSERT_COLUMN (Intermediate level)<BR>
+# SQL_SR_INSERT_TABLE (Entry level)<BR>
+# SQL_SR_REFERENCES_COLUMN (Entry level)<BR>
+# SQL_SR_REFERENCES_TABLE (Entry level)<BR>
+# SQL_SR_RESTRICT (FIPS Transitional level)<BR>
+# SQL_SR_SELECT_TABLE (Entry level)<BR>
+# SQL_SR_UPDATE_COLUMN (Entry level)<BR>
+# SQL_SR_UPDATE_TABLE (Entry level)<BR>
+# SQL_SR_USAGE_ON_DOMAIN (FIPS Transitional level)<BR>
+# SQL_SR_USAGE_ON_CHARACTER_SET (FIPS Transitional level)<BR>
+# SQL_SR_USAGE_ON_COLLATION (FIPS Transitional level)<BR>
+# SQL_SR_USAGE_ON_TRANSLATION (FIPS Transitional level)</P>
+# </TD>
+# </TR>
+ SQL_SQL92_REVOKE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_ROW_VALUE_CONSTRUCTOR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the row value constructor expressions supported in a <B>SELECT</B> statement, as defined in SQL-92. The following bitmasks are used to determine which options are supported by the data source:
+# <P>SQL_SRVC_VALUE_EXPRESSION <BR>
+# SQL_SRVC_NULL <BR>
+# SQL_SRVC_DEFAULT <BR>
+# SQL_SRVC_ROW_SUBQUERY</P>
+# </TD>
+# </TR>
+ SQL_SQL92_ROW_VALUE_CONSTRUCTOR => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_STRING_FUNCTIONS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the string scalar functions that are supported by the driver and the associated data source, as defined in SQL-92.
+# <P>The following bitmasks are used to determine which string functions are supported:</P>
+#
+# <P>SQL_SSF_CONVERT<BR>
+# SQL_SSF_LOWER<BR>
+# SQL_SSF_UPPER<BR>
+# SQL_SSF_SUBSTRING<BR>
+# SQL_SSF_TRANSLATE<BR>
+# SQL_SSF_TRIM_BOTH<BR>
+# SQL_SSF_TRIM_LEADING<BR>
+# SQL_SSF_TRIM_TRAILING</P>
+# </TD>
+# </TR>
+ SQL_SQL92_STRING_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SQL92_VALUE_EXPRESSIONS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the value expressions supported, as defined in SQL-92.
+# <P>The SQL-92 or FIPS conformance level at which this feature needs to be supported is shown in parentheses next to each bitmask.</P>
+#
+# <P>The following bitmasks are used to determine which options are supported by the data source:</P>
+#
+# <P>SQL_SVE_CASE (Intermediate level)<BR>
+# SQL_SVE_CAST (FIPS Transitional level)<BR>
+# SQL_SVE_COALESCE (Intermediate level)<BR>
+# SQL_SVE_NULLIF (Intermediate level)</P>
+# </TD>
+# </TR>
+ SQL_SQL92_VALUE_EXPRESSIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_STANDARD_CLI_CONFORMANCE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the CLI standard or standards to which the driver conforms. The following bitmasks are used to determine which levels the driver conforms to:
+# <P>SQL_SCC_XOPEN_CLI_VERSION1: The driver conforms to the X/Open CLI version 1.</P>
+#
+# <P>SQL_SCC_ISO92_CLI: The driver conforms to the ISO 92 CLI.</P>
+# </TD>
+# </TR>
+ SQL_STANDARD_CLI_CONFORMANCE => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_STATIC_CURSOR_ATTRIBUTES1<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a static cursor that are supported by the driver. This bitmask contains the first subset of attributes; for the second subset, see SQL_STATIC_CURSOR_ATTRIBUTES2.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA1_NEXT<BR>
+# SQL_CA1_ABSOLUTE<BR>
+# SQL_CA1_RELATIVE<BR>
+# SQL_CA1_BOOKMARK<BR>
+# SQL_CA1_LOCK_NO_CHANGE<BR>
+# SQL_CA1_LOCK_EXCLUSIVE<BR>
+# SQL_CA1_LOCK_UNLOCK<BR>
+# SQL_CA1_POS_POSITION<BR>
+# SQL_CA1_POS_UPDATE<BR>
+# SQL_CA1_POS_DELETE<BR>
+# SQL_CA1_POS_REFRESH<BR>
+# SQL_CA1_POSITIONED_UPDATE<BR>
+# SQL_CA1_POSITIONED_DELETE<BR>
+# SQL_CA1_SELECT_FOR_UPDATE<BR>
+# SQL_CA1_BULK_ADD<BR>
+# SQL_CA1_BULK_UPDATE_BY_BOOKMARK<BR>
+# SQL_CA1_BULK_DELETE_BY_BOOKMARK<BR>
+# SQL_CA1_BULK_FETCH_BY_BOOKMARK</P>
+#
+# <P>For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES1 (and substitute "static cursor" for "dynamic cursor" in the descriptions).</P>
+#
+# <P>An SQL-92 Intermediate level&#0150;conformant driver will usually return the SQL_CA1_NEXT, SQL_CA1_ABSOLUTE, and SQL_CA1_RELATIVE options as supported, because the driver supports scrollable cursors through the embedded SQL FETCH statement. Because this does not directly determine the underlying SQL support, however, scrollable cursors may not be supported, even for an SQL-92 Intermediate level&#0150;conformant driver.</P>
+# </TD>
+# </TR>
+ SQL_STATIC_CURSOR_ATTRIBUTES1 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_STATIC_CURSOR_ATTRIBUTES2<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask that describes the attributes of a static cursor that are supported by the driver. This bitmask contains the second subset of attributes; for the first subset, see SQL_STATIC_CURSOR_ATTRIBUTES1.
+# <P>The following bitmasks are used to determine which attributes are supported:</P>
+#
+# <P>SQL_CA2_READ_ONLY_CONCURRENCY<BR>
+# SQL_CA2_LOCK_CONCURRENCY<BR>
+# SQL_CA2_OPT_ROWVER_CONCURRENCY<BR>
+# SQL_CA2_OPT_VALUES_CONCURRENCY<BR>
+# SQL_CA2_SENSITIVITY_ADDITIONS<BR>
+# SQL_CA2_SENSITIVITY_DELETIONS<BR>
+# SQL_CA2_SENSITIVITY_UPDATES<BR>
+# SQL_CA2_MAX_ROWS_SELECT<BR>
+# SQL_CA2_MAX_ROWS_INSERT<BR>
+# SQL_CA2_MAX_ROWS_DELETE<BR>
+# SQL_CA2_MAX_ROWS_UPDATE<BR>
+# SQL_CA2_MAX_ROWS_CATALOG<BR>
+# SQL_CA2_MAX_ROWS_AFFECTS_ALL<BR>
+# SQL_CA2_CRC_EXACT<BR>
+# SQL_CA2_CRC_APPROXIMATE<BR>
+# SQL_CA2_SIMULATE_NON_UNIQUE<BR>
+# SQL_CA2_SIMULATE_TRY_UNIQUE<BR>
+# SQL_CA2_SIMULATE_UNIQUE</P>
+#
+# <P>For descriptions of these bitmasks, see SQL_DYNAMIC_CURSOR_ATTRIBUTES2 (and substitute "static cursor" for "dynamic cursor" in the descriptions).</P>
+# </TD>
+# </TR>
+ SQL_STATIC_CURSOR_ATTRIBUTES2 => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_STRING_FUNCTIONS<BR>
+# (ODBC 1.0)
+# <P>The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.</P>
+# </TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the scalar string functions supported by the driver and associated data source.
+# <P>The following bitmasks are used to determine which string functions are supported:</P>
+#
+# <P>SQL_FN_STR_ASCII (ODBC 1.0)<BR>
+# SQL_FN_STR_BIT_LENGTH (ODBC 3.0)<BR>
+# SQL_FN_STR_CHAR (ODBC 1.0)<BR>
+# SQL_FN_STR_CHAR_<BR>
+# LENGTH (ODBC 3.0)<BR>
+# SQL_FN_STR_CHARACTER_<BR>
+# LENGTH (ODBC 3.0)<BR>
+# SQL_FN_STR_CONCAT (ODBC 1.0)<BR>
+# SQL_FN_STR_DIFFERENCE (ODBC 2.0)<BR>
+# SQL_FN_STR_INSERT (ODBC 1.0)<BR>
+# SQL_FN_STR_LCASE (ODBC 1.0)<BR>
+# SQL_FN_STR_LEFT (ODBC 1.0)<BR>
+# SQL_FN_STR_LENGTH (ODBC 1.0)<BR>
+# SQL_FN_STR_LOCATE (ODBC 1.0)<BR>
+# SQL_FN_STR_LTRIM (ODBC 1.0) <BR>
+# SQL_FN_STR_OCTET_<BR>
+# LENGTH (ODBC 3.0) <BR>
+# SQL_FN_STR_POSITION (ODBC 3.0)<BR>
+# SQL_FN_STR_REPEAT (ODBC 1.0)<BR>
+# SQL_FN_STR_REPLACE (ODBC 1.0)<BR>
+# SQL_FN_STR_RIGHT (ODBC 1.0)<BR>
+# SQL_FN_STR_RTRIM (ODBC 1.0)<BR>
+# SQL_FN_STR_SOUNDEX (ODBC 2.0)<BR>
+# SQL_FN_STR_SPACE (ODBC 2.0)<BR>
+# SQL_FN_STR_SUBSTRING (ODBC 1.0)<BR>
+# SQL_FN_STR_UCASE (ODBC 1.0)</P>
+#
+# <P>If an application can call the <B>LOCATE</B> scalar function with the <I>string_exp1</I>, <I>string_exp2</I>, and <I>start</I> arguments, the driver returns the SQL_FN_STR_LOCATE bitmask. If an application can call the LOCATE scalar function with only the <I>string_exp1</I> and <I>string_exp2</I> arguments, the driver returns the SQL_FN_STR_LOCATE_2 bitmask. Drivers that fully support the <B>LOCATE</B> scalar function return both bitmasks.</P>
+#
+# <P>(For more information, see <A HREF="odbcstring_functions.htm">String Functions</A> in Appendix E, "Scalar Functions.")</P>
+# </TD>
+# </TR>
+ SQL_STRING_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SUBQUERIES<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the predicates that support subqueries:
+# <P>SQL_SQ_CORRELATED_SUBQUERIES<BR>
+# SQL_SQ_COMPARISON<BR>
+# SQL_SQ_EXISTS<BR>
+# SQL_SQ_IN<BR>
+# SQL_SQ_QUANTIFIED</P>
+#
+# <P>The SQL_SQ_CORRELATED_SUBQUERIES bitmask indicates that all predicates that support subqueries support correlated subqueries.</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return a bitmask in which all of these bits are set.</P>
+# </TD>
+# </TR>
+ SQL_SUBQUERIES => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_SYSTEM_FUNCTIONS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the scalar system functions supported by the driver and associated data source.
+# <P>The following bitmasks are used to determine which system functions are supported:</P>
+#
+# <P>SQL_FN_SYS_DBNAME<BR>
+# SQL_FN_SYS_IFNULL<BR>
+# SQL_FN_SYS_USERNAME</P>
+# </TD>
+# </TR>
+ SQL_SYSTEM_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TABLE_TERM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the data source vendor's name for a table; for example, "table" or "file".
+# <P>This character string can be in upper, lower, or mixed case. </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return "table".</P>
+# </TD>
+# </TR>
+ SQL_TABLE_TERM => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TIMEDATE_ADD_INTERVALS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the timestamp intervals supported by the driver and associated data source for the TIMESTAMPADD scalar function.
+# <P>The following bitmasks are used to determine which intervals are supported:</P>
+#
+# <P>SQL_FN_TSI_FRAC_SECOND<BR>
+# SQL_FN_TSI_SECOND<BR>
+# SQL_FN_TSI_MINUTE<BR>
+# SQL_FN_TSI_HOUR<BR>
+# SQL_FN_TSI_DAY<BR>
+# SQL_FN_TSI_WEEK<BR>
+# SQL_FN_TSI_MONTH<BR>
+# SQL_FN_TSI_QUARTER<BR>
+# SQL_FN_TSI_YEAR</P>
+#
+# <P>An FIPS Transitional level&#0150;conformant driver will always return a bitmask in which all of these bits are set.</P>
+# </TD>
+# </TR>
+ SQL_TIMEDATE_ADD_INTERVALS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TIMEDATE_DIFF_INTERVALS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the timestamp intervals supported by the driver and associated data source for the TIMESTAMPDIFF scalar function.
+# <P>The following bitmasks are used to determine which intervals are supported:</P>
+#
+# <P>SQL_FN_TSI_FRAC_SECOND<BR>
+# SQL_FN_TSI_SECOND<BR>
+# SQL_FN_TSI_MINUTE<BR>
+# SQL_FN_TSI_HOUR<BR>
+# SQL_FN_TSI_DAY<BR>
+# SQL_FN_TSI_WEEK<BR>
+# SQL_FN_TSI_MONTH<BR>
+# SQL_FN_TSI_QUARTER<BR>
+# SQL_FN_TSI_YEAR </P>
+#
+# <P>An FIPS Transitional level&#0150;conformant driver will always return a bitmask in which all of these bits are set.</P>
+# </TD>
+# </TR>
+ SQL_TIMEDATE_DIFF_INTERVALS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TIMEDATE_FUNCTIONS<BR>
+# (ODBC 1.0)
+# <P>The information type was introduced in ODBC 1.0; each bitmask is labeled with the version in which it was introduced.</P>
+# </TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the scalar date and time functions supported by the driver and associated data source.
+# <P>The following bitmasks are used to determine which date and time functions are supported:</P>
+#
+# <P>SQL_FN_TD_CURRENT_DATE ODBC 3.0)<BR>
+# SQL_FN_TD_CURRENT_TIME (ODBC 3.0)<BR>
+# SQL_FN_TD_CURRENT_TIMESTAMP (ODBC 3.0)<BR>
+# SQL_FN_TD_CURDATE (ODBC 1.0)<BR>
+# SQL_FN_TD_CURTIME (ODBC 1.0) <BR>
+# SQL_FN_TD_DAYNAME (ODBC 2.0)<BR>
+# SQL_FN_TD_DAYOFMONTH (ODBC 1.0)<BR>
+# SQL_FN_TD_DAYOFWEEK (ODBC 1.0)<BR>
+# SQL_FN_TD_DAYOFYEAR (ODBC 1.0) <BR>
+# SQL_FN_TD_EXTRACT (ODBC 3.0)<BR>
+# SQL_FN_TD_HOUR (ODBC 1.0)<BR>
+# SQL_FN_TD_MINUTE (ODBC 1.0)<BR>
+# SQL_FN_TD_MONTH (ODBC 1.0)<BR>
+# SQL_FN_TD_MONTHNAME (ODBC 2.0)<BR>
+# SQL_FN_TD_NOW (ODBC 1.0)<BR>
+# SQL_FN_TD_QUARTER (ODBC 1.0)<BR>
+# SQL_FN_TD_SECOND (ODBC 1.0)<BR>
+# SQL_FN_TD_TIMESTAMPADD (ODBC 2.0)<BR>
+# SQL_FN_TD_TIMESTAMPDIFF (ODBC 2.0)<BR>
+# SQL_FN_TD_WEEK (ODBC 1.0)<BR>
+# SQL_FN_TD_YEAR (ODBC 1.0)</P>
+# </TD>
+# </TR>
+ SQL_TIMEDATE_FUNCTIONS => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TXN_CAPABLE<BR>
+# (ODBC 1.0)
+# <P>The information type was introduced in ODBC 1.0; each return value is labeled with the version in which it was introduced.</P>
+# </TD>
+# <TD width=50%>An SQLUSMALLINT value describing the transaction support in the driver or data source:
+# <P>SQL_TC_NONE = Transactions not supported. (ODBC 1.0)</P>
+#
+# <P>SQL_TC_DML = Transactions can contain only Data Manipulation Language (DML) statements (<B>SELECT</B>, <B>INSERT</B>, <B>UPDATE</B>, <B>DELETE</B>). Data Definition Language (DDL) statements encountered in a transaction cause an error. (ODBC 1.0)</P>
+#
+# <P>SQL_TC_DDL_COMMIT = Transactions can contain only DML statements. DDL statements (<B>CREATE TABLE</B>, <B>DROP INDEX</B>, and so on) encountered in a transaction cause the transaction to be committed. (ODBC 2.0)</P>
+#
+# <P>SQL_TC_DDL_IGNORE = Transactions can contain only DML statements. DDL statements encountered in a transaction are ignored. (ODBC 2.0)</P>
+#
+# <P>SQL_TC_ALL = Transactions can contain DDL statements and DML statements in any order. (ODBC 1.0) </P>
+#
+# <P>(Because support of transactions is mandatory in SQL-92, an SQL-92 conformant driver [any level] will never return SQL_TC_NONE.)</P>
+# </TD>
+# </TR>
+ SQL_TXN_CAPABLE => {
+ type => q(Short),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_TXN_ISOLATION_OPTION<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the transaction isolation levels available from the driver or data source.
+# <P>The following bitmasks are used in conjunction with the flag to determine which options are supported:</P>
+#
+# <P>SQL_TXN_READ_UNCOMMITTED<BR>
+# SQL_TXN_READ_COMMITTED<BR>
+# SQL_TXN_REPEATABLE_READ<BR>
+# SQL_TXN_SERIALIZABLE</P>
+#
+# <P>For descriptions of these isolation levels, see the description of SQL_DEFAULT_TXN_ISOLATION.</P>
+#
+# <P>To set the transaction isolation level, an application calls <B>SQLSetConnectAttr</B> to set the SQL_ATTR_TXN_ISOLATION attribute. For more information, see <B>SQLSetConnectAttr</B>. </P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return SQL_TXN_SERIALIZABLE as supported. A FIPS Transitional level&#0150;conformant driver will always return all of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_TXN_ISOLATION_OPTION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_UNION<BR>
+# (ODBC 2.0)</TD>
+# <TD width=50%>An SQLUINTEGER bitmask enumerating the support for the <B>UNION</B> clause:
+# <P>SQL_U_UNION = The data source supports the <B>UNION</B> clause.</P>
+#
+# <P>SQL_U_UNION_ALL = The data source supports the <B>ALL</B> keyword in the <B>UNION</B> clause. (<B>SQLGetInfo</B> returns both SQL_U_UNION and SQL_U_UNION_ALL in this case.)</P>
+#
+# <P>An SQL-92 Entry level&#0150;conformant driver will always return both of these options as supported.</P>
+# </TD>
+# </TR>
+ SQL_UNION => {
+ type => q(Bitmask),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_USER_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=50%>A character string with the name used in a particular database, which can be different from the login name.</TD>
+# </TR>
+ SQL_USER_NAME => {
+ type => q(Char),
+ },
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_XOPEN_CLI_YEAR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=50%>A character string that indicates the year of publication of the X/Open specification with which the version of the ODBC Driver Manager fully complies.</TD>
+# </TR>
+ SQL_XOPEN_CLI_YEAR => {
+ type => q(Char),
+ },
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Code Example</B></P>
+#
+# <P><B>SQLGetInfo</B> returns lists of supported options as an SQLUINTEGER bitmask in *<I>InfoValuePtr</I>. The bitmask for each option is used in conjunction with the flag to determine whether the option is supported.</P>
+#
+# <P>For example, an application could use the following code to determine whether the SUBSTRING scalar function is supported by the driver associated with the connection:</P>
+#
+# <PRE class="code">SQLUINTEGER fFuncs;
+#
+# SQLGetInfo(hdbc,
+# SQL_STRING_FUNCTIONS,
+# (SQLPOINTER)&amp;fFuncs,
+# sizeof(fFuncs),
+# NULL);
+#
+# if (fFuncs &amp; SQL_FN_STR_SUBSTRING)&nbsp;&nbsp;&nbsp;/* SUBSTRING supported */
+# ;
+# else&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* SUBSTRING not supported */
+# ;</PRE>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>For information about</TH>
+# <TH width=50%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning the setting of a connection attribute</TD>
+# <TD width=50%><A HREF="odbcsqlgetconnectattr.htm">SQLGetConnectAttr</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Determining whether a driver supports a function</TD>
+# <TD width=50%><A HREF="odbcsqlgetfunctions.htm">SQLGetFunctions</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning the setting of a statement attribute</TD>
+# <TD width=50%><A HREF="odbcsqlgetstmtattr.htm">SQLGetStmtAttr</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning information about a data source's data types</TD>
+# <TD width=50%><A HREF="odbcsqlgettypeinfo.htm">SQLGetTypeInfo</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+};
+
+print <<END;
+#include "HandleDbc.hpp"
+
+HandleDbc::InfoTab
+HandleDbc::m_infoTab[] = {
+END
+
+my @name = sort keys %$info;
+for my $name (@name) {
+ my $p = $info->{$name};
+ my $type = $p->{type};
+ $type =~ /^(Char|YesNo|Short|Long|Bitmask)$/
+ or die "$name: bad type $type";
+ my $defstr = $type eq "YesNo" ? q("N") : $type eq "Char" ? q("") : q(0);
+ my $s = <<END;
+ { $name,
+ InfoTab\::$type,
+ 0L,
+ $defstr
+ },
+END
+ if ($p->{omit}) {
+ $s ="#if 0\n" . $s . "#endif\n";
+ }
+ print $s;
+};
+
+print <<END;
+ { 0,
+ InfoTab::End,
+ 0L,
+ 0
+ }
+};
+END
+
+# vim: set sw=4:
diff --git a/storage/ndb/src/old_files/client/odbc/docs/gettypeinfo.pl b/storage/ndb/src/old_files/client/odbc/docs/gettypeinfo.pl
new file mode 100644
index 00000000000..0a999fd7249
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/gettypeinfo.pl
@@ -0,0 +1,645 @@
+# usage: perl TypeInfo.data
+# prints template for typeinfo
+use strict;
+my $position = 0;
+
+#
+# odbcsqlgettypeinfo.htm
+#
+my @typeinfo = (
+ { name => "UNDEF",
+ type => q(Undef),
+ nullable => q(true),
+ position => 0,
+ },
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLGetTypeInfo</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlgettypeinfo"></A>SQLGetTypeInfo</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 1.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLGetTypeInfo</B> returns information about data types supported by the data source. The driver returns the information in the form of an SQL result set. The data types are intended for use in Data Definition Language (DDL) statements.</P>
+#
+# <P class="indent"><b class="le">Important&nbsp;&nbsp;&nbsp;</b>Applications must use the type names returned in the TYPE_NAME column of the <B>SQLGetTypeInfo</B> result set in <B>ALTER TABLE</B> and <B>CREATE TABLE</B> statements. <B>SQLGetTypeInfo</B> may return more than one row with the same value in the DATA_TYPE column.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLGetTypeInfo</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StatementHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>DataType</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>StatementHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Statement handle for the result set.</dd>
+#
+# <DT><I>DataType</I></DT>
+#
+# <DD>[Input]<BR>
+# The SQL data type. This must be one of the values in the "<A HREF="odbcsql_data_types.htm">SQL Data Types</A>" section of Appendix D: Data Types, or a driver-specific SQL data type. SQL_ALL_TYPES specifies that information about all data types should be returned.</dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLGetTypeInfo</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_STMT and a <I>Handle</I> of <I>StatementHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLGetTypeInfo </B>and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01S02</TD>
+# <TD width=26%>Option value changed</TD>
+# <TD width=52%>A specified statement attribute was invalid because of implementation working conditions, so a similar value was temporarily substituted. (Call <B>SQLGetStmtAttr</B> to determine the temporarily substituted value.) The substitute value is valid for the <I>StatementHandle</I> until the cursor is closed. The statement attributes that can be changed are: SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_KEYSET_SIZE, SQL_ATTR_MAX_LENGTH, SQL_ATTR_MAX_ROWS, SQL_ATTR_QUERY_TIMEOUT, and SQL_ATTR_SIMULATE_CURSOR. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>A cursor was open on the <I>StatementHandle,</I> and <B>SQLFetch</B> or <B>SQLFetchScroll</B> had been called. This error is returned by the Driver Manager if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has not returned SQL_NO_DATA, and is returned by the driver if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has returned SQL_NO_DATA.
+# <P>A result set was open on the <I>StatementHandle</I>, but <B>SQLFetch</B> or <B>SQLFetchScroll</B> had not been called.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40001</TD>
+# <TD width=26%>Serialization failure</TD>
+# <TD width=52%>The transaction was rolled back due to a resource deadlock with another transaction.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40003</TD>
+# <TD width=26%>Statement completion unknown</TD>
+# <TD width=52%>The associated connection failed during the execution of this function and the state of the transaction cannot be determined.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause. </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY004</TD>
+# <TD width=26%>Invalid SQL data type</TD>
+# <TD width=52%>The value specified for the argument <I>DataType</I> was neither a valid ODBC SQL data type identifier nor a driver-specific data type identifier supported by the driver.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY008</TD>
+# <TD width=26%>Operation canceled</TD>
+# <TD width=52%>Asynchronous processing was enabled for the <I>StatementHandle</I>, then the function was called and, before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I>. Then the function was called again on the <I>StatementHandle</I>.
+# <P>The function was called and, before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I> from a different thread in a multithread application.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function (not this one) was called for the <I>StatementHandle</I> and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.
+# <P>The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT00</TD>
+# <TD width=26%>Timeout expired</TD>
+# <TD width=52%>The query timeout period expired before the data source returned the result set. The timeout period is set through <B>SQLSetStmtAttr</B>, SQL_ATTR_QUERY_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver corresponding to the <I>StatementHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P><B>SQLGetTypeInfo</B> returns the results as a standard result set, ordered by DATA_TYPE and then by how closely the data type maps to the corresponding ODBC SQL data type. Data types defined by the data source take precedence over user-defined data types. Consequently, the sort order is not necessarily consistent but can be generalized as DATA_TYPE first, followed by TYPE_NAME, both ascending. For example, suppose that a data source defined INTEGER and COUNTER data types, where COUNTER is auto-incrementing, and that a user-defined data type WHOLENUM has also been defined. These would be returned in the order INTEGER, WHOLENUM, and COUNTER, because WHOLENUM maps closely to the ODBC SQL data type SQL_INTEGER, while the auto-incrementing data type, even though supported by the data source, does not map closely to an ODBC SQL data type. For information about how this information might be used, see "<A HREF="odbcddl_statements.htm">DDL Statements</A>" in Chapter 8: SQL Statements.</P>
+#
+# <P>If the <I>DataType</I> argument specifies a data type which is valid for the version of ODBC supported by the driver, but is not supported by the driver, then it will return an empty result set. </P>
+#
+# <P class="indent"><b class="le">Note&nbsp;&nbsp;&nbsp;</b>For more information about the general use, arguments, and returned data of ODBC catalog functions, see <A HREF="odbccatalog_functions.htm">Chapter 7: Catalog Functions</A>.</P>
+#
+# <P>The following columns have been renamed for ODBC 3.<I>x</I>. The column name changes do not affect backward compatibility because applications bind by column number.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>ODBC 2.0 column</TH>
+# <TH width=52%>ODBC 3.<I>x</I> column</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>PRECISION</TD>
+# <TD width=52%>COLUMN_SIZE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>MONEY</TD>
+# <TD width=52%>FIXED_PREC_SCALE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>AUTO_INCREMENT</TD>
+# <TD width=52%>AUTO_UNIQUE_VALUE</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>The following columns have been added to the results set returned by <B>SQLGetTypeInfo</B> for ODBC 3.<I>x</I>:
+#
+# <UL type=disc>
+# <LI>SQL_DATA_TYPE</li>
+#
+# <LI>INTERVAL_PRECISION</li>
+#
+# <LI>SQL_DATETIME_SUB</li>
+#
+# <LI>NUM_PREC_RADIX</li>
+# </UL>
+#
+# <P>The following table lists the columns in the result set. Additional columns beyond column 19 (INTERVAL_PRECISION) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "<A HREF="odbcdata_returned_by_catalog_functions.htm">Data Returned by Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;<B>SQLGetTypeInfo</B> might not return all data types. For example, a driver might not return user-defined data types. Applications can use any valid data type, regardless of whether it is returned by <B>SQLGetTypeInfo</B>.</P>
+#
+# <P class="indent">The data types returned by <B>SQLGetTypeInfo</B> are those supported by the data source. They are intended for use in Data Definition Language (DDL) statements. Drivers can return result-set data using data types other than the types returned by <B>SQLGetTypeInfo</B>. In creating the result set for a catalog function, the driver might use a data type that is not supported by the data source. </P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=33%><BR>
+# Column name</TH>
+# <TH width=14%>Column <BR>
+# number</TH>
+# <TH width=15%><BR>
+# Data type</TH>
+# <TH width=38%><BR>
+# Comments</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=33%>TYPE_NAME<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>1</TD>
+# <TD width=15%>Varchar<BR>
+# not NULL</TD>
+# <TD width=38%>Data source&#0150;dependent data-type name; for example, "CHAR()", "VARCHAR()", "MONEY", "LONG VARBINARY", or "CHAR ( ) FOR BIT DATA". Applications must use this name in <B>CREATE TABLE</B> and <B>ALTER TABLE</B> statements.</TD>
+# </TR>
+ { name => "TYPE_NAME",
+ type => q(Varchar),
+ length => 20,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>DATA_TYPE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>2</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For datetime or interval data types, this column returns the concise data type (such as SQL_TYPE_TIME or SQL_INTERVAL_YEAR_TO_MONTH). For a list of valid ODBC SQL data types, see "<A HREF="odbcsql_data_types.htm">SQL Data Types</A>" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver’s documentation.</TD>
+# </TR>
+ { name => "DATA_TYPE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>COLUMN_SIZE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>3</TD>
+# <TD width=15%>Integer</TD>
+# <TD width=38%>The maximum column size that the server supports for this data type. For numeric data, this is the maximum precision. For string data, this is the length in characters. For datetime data types, this is the length in characters of the string representation (assuming the maximum allowed precision of the fractional seconds component). NULL is returned for data types where column size is not applicable. For interval data types, this is the number of characters in the character representation of the interval literal (as defined by the interval leading precision; see "<A HREF="odbcinterval_data_type_length.htm">Interval Data Type Length</A>" in Appendix D: Data Types).
+# <P>For more information on column size, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</P>
+# </TD>
+# </TR>
+ { name => "COLUMN_SIZE",
+ type => q(Integer),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>LITERAL_PREFIX<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>4</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>Character or characters used to prefix a literal; for example, a single quotation mark (') for character data types or 0x for binary data types; NULL is returned for data types where a literal prefix is not applicable.</TD>
+# </TR>
+ { name => "LITERAL_PREFIX",
+ type => q(Varchar),
+ length => 1,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>LITERAL_SUFFIX<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>5</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>Character or characters used to terminate a literal; for example, a single quotation mark (') for character data types; NULL is returned for data types where a literal suffix is not applicable.</TD>
+# </TR>
+ { name => "LITERAL_SUFFIX",
+ type => q(Varchar),
+ length => 1,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>CREATE_PARAMS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>6</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>A list of keywords, separated by commas, corresponding to each parameter that the application may specify in parentheses when using the name that is returned in the TYPE_NAME field. The keywords in the list can be any of the following: length, precision, or scale. They appear in the order that the syntax requires them to be used. For example, CREATE_PARAMS for DECIMAL would be "precision,scale"; CREATE_PARAMS for VARCHAR would equal "length." NULL is returned if there are no parameters for the data type definition; for example, INTEGER.
+# <P>The driver supplies the CREATE_PARAMS text in the language of the country where it is used.</P>
+# </TD>
+# </TR>
+ { name => "CREATE_PARAMS",
+ type => q(Varchar),
+ length => 20,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>NULLABLE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>7</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>Whether the data type accepts a NULL value:
+# <P>SQL_NO_NULLS if the data type does not accept NULL values.</P>
+#
+# <P>SQL_NULLABLE if the data type accepts NULL values.</P>
+#
+# <P>SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values.</P>
+# </TD>
+# </TR>
+ { name => "NULLABLE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>CASE_SENSITIVE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>8</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>Whether a character data type is case-sensitive in collations and comparisons:
+# <P>SQL_TRUE if the data type is a character data type and is case-sensitive.</P>
+#
+# <P>SQL_FALSE if the data type is not a character data type or is not case-sensitive.</P>
+# </TD>
+# </TR>
+ { name => "CASE_SENSITIVE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>SEARCHABLE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>9</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>How the data type is used in a <B>WHERE</B> clause:
+# <P>SQL_PRED_NONE if the column cannot be used in a <B>WHERE</B> clause. (This is the same as the SQL_UNSEARCHABLE value in ODBC 2.<I>x</I>.)</P>
+#
+# <P>SQL_PRED_CHAR if the column can be used in a <B>WHERE</B> clause, but only with the <B>LIKE</B> predicate. (This is the same as the SQL_LIKE_ONLY value in ODBC 2.<I>x</I>.)</P>
+#
+# <P>SQL_PRED_BASIC if the column can be used in a <B>WHERE</B> clause with all the comparison operators except <B>LIKE</B> (comparison, quantified comparison, <B>BETWEEN</B>, <B>DISTINCT</B>, <B>IN</B>, <B>MATCH</B>, and <B>UNIQUE</B>). (This is the same as the SQL_ALL_EXCEPT_LIKE value in ODBC 2.<I>x</I>.)</P>
+#
+# <P>SQL_SEARCHABLE if the column can be used in a <B>WHERE</B> clause with any comparison operator.</P>
+# </TD>
+# </TR>
+ { name => "SEARCHABLE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>UNSIGNED_ATTRIBUTE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>10</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>Whether the data type is unsigned:
+# <P>SQL_TRUE if the data type is unsigned.</P>
+#
+# <P>SQL_FALSE if the data type is signed.</P>
+#
+# <P>NULL is returned if the attribute is not applicable to the data type or the data type is not numeric.</P>
+# </TD>
+# </TR>
+ { name => "UNSIGNED_ATTRIBUTE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>FIXED_PREC_SCALE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>11</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>Whether the data type has predefined fixed precision and scale (which are data source&#0150;specific), such as a money data type:
+# <P>SQL_TRUE if it has predefined fixed precision and scale.</P>
+#
+# <P>SQL_FALSE if it does not have predefined fixed precision and scale.</P>
+# </TD>
+# </TR>
+ { name => "FIXED_PREC_SCALE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>AUTO_UNIQUE_VALUE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>12</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>Whether the data type is autoincrementing:
+# <P>SQL_TRUE if the data type is autoincrementing.</P>
+#
+# <P>SQL_FALSE if the data type is not autoincrementing.</P>
+#
+# <P>NULL is returned if the attribute is not applicable to the data type or the data type is not numeric.</P>
+#
+# <P>An application can insert values into a column having this attribute, but typically cannot update the values in the column. </P>
+#
+# <P>When an insert is made into an auto-increment column, a unique value is inserted into the column at insert time. The increment is not defined, but is data source&#0150;specific. An application should not assume that an auto-increment column starts at any particular point or increments by any particular value.</P>
+# </TD>
+# </TR>
+ { name => "AUTO_UNIQUE_VALUE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>LOCAL_TYPE_NAME<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>13</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>Localized version of the data source&#0150;dependent name of the data type. NULL is returned if a localized name is not supported by the data source. This name is intended for display only, such as in dialog boxes.</TD>
+# </TR>
+ { name => "LOCAL_TYPE_NAME",
+ type => q(Varchar),
+ length => 20,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>MINIMUM_SCALE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>14</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>The minimum scale of the data type on the data source. If a data type has a fixed scale, the MINIMUM_SCALE and MAXIMUM_SCALE columns both contain this value. For example, an SQL_TYPE_TIMESTAMP column might have a fixed scale for fractional seconds. NULL is returned where scale is not applicable. For more information, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</TD>
+# </TR>
+ { name => "MINIMUM_SCALE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>MAXIMUM_SCALE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>15</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>The maximum scale of the data type on the data source. NULL is returned where scale is not applicable. If the maximum scale is not defined separately on the data source, but is instead defined to be the same as the maximum precision, this column contains the same value as the COLUMN_SIZE column. For more information, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</TD>
+# </TR>
+ { name => "MAXIMUM_SCALE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>SQL_DATA_TYPE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>16</TD>
+# <TD width=15%>Smallint NOT NULL</TD>
+# <TD width=38%>The value of the SQL data type as it appears in the SQL_DESC_TYPE field of the descriptor. This column is the same as the DATA_TYPE column, except for interval and datetime data types.
+# <P>For interval and datetime data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.) </P>
+# </TD>
+# </TR>
+ { name => "SQL_DATA_TYPE",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(false),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>SQL_DATETIME_SUB<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>17</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>When the value of SQL_DATA_TYPE is SQL_DATETIME or SQL_INTERVAL, this column contains the datetime/interval subcode. For data types other than datetime and interval, this field is NULL.
+# <P>For interval or datetime data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.)</P>
+# </TD>
+# </TR>
+ { name => "SQL_DATETIME_SUB",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>NUM_PREC_RADIX<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>18</TD>
+# <TD width=15%>Integer</TD>
+# <TD width=38%>If the data type is an approximate numeric type, this column contains the value 2 to indicate that COLUMN_SIZE specifies a number of bits. For exact numeric types, this column contains the value 10 to indicate that COLUMN_SIZE specifies a number of decimal digits. Otherwise, this column is NULL.</TD>
+# </TR>
+ { name => "NUM_PREC_RADIX",
+ type => q(Integer),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>INTERVAL_PRECISION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>19</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>If the data type is an interval data type, then this column contains the value of the interval leading precision. (See "<A HREF="odbcinterval_data_type_precision.htm">Interval Data Type Precision</A>" in Appendix D: Data Types.) Otherwise, this column is NULL.</TD>
+# </TR>
+ { name => "INTERVAL_PRECISION",
+ type => q(Smallint),
+ length => undef,
+ nullable => q(true),
+ position => ++$position,
+ },
+# </table></div>
+# <!--TS:-->
+# <P>Attribute information can apply to data types or to specific columns in a result set. <B>SQLGetTypeInfo</B> returns information about attributes associated with data types; <B>SQLColAttribute</B> returns information about attributes associated with columns in a result set.</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>For information about</TH>
+# <TH width=50%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Binding a buffer to a column in a result set</TD>
+# <TD width=50%><A HREF="odbcsqlbindcol.htm">SQLBindCol</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Canceling statement processing</TD>
+# <TD width=50%><A HREF="odbcsqlcancel.htm">SQLCancel</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning information about a column in a result set</TD>
+# <TD width=50%><A HREF="odbcsqlcolattribute.htm">SQLColAttribute</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a block of data or scrolling through a result set</TD>
+# <TD width=50%><A HREF="odbcsqlfetchscroll.htm">SQLFetchScroll</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a single row or a block of data in a forward-only direction</TD>
+# <TD width=50%><A HREF="odbcsqlfetch.htm">SQLFetch</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning information about a driver or data source</TD>
+# <TD width=50%><A HREF="odbcsqlgetinfo.htm">SQLGetInfo</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+);
+
+my $i4 = " " x 4;
+print "// template-begin\n";
+print "#define Varchar Char\n";
+print "#define Smallint Integer\n";
+print "const SqlTypeInfo::Column\nSqlTypeInfo::m_columnList[] = {\n";
+for my $p (@typeinfo) {
+ print "$i4\{\t$p->{position},\n";
+ print "\t\"$p->{name}\",\n";
+ my $type = $p->{type};
+ if ($p->{position} == 0) {
+ print "\tSqlType()\n";
+ } elsif (! $p->{length}) {
+ print "\tSqlType(SqlType::$type, $p->{nullable})\n";
+ } else {
+ print "\tSqlType(SqlType::$type, $p->{length}, $p->{nullable})\n";
+ }
+ my $c = $p == $typeinfo[-1] ? "" : ",";
+ print "$i4\}$c\n";
+}
+print "};\n";
+print "#undef Varchar\n";
+print "#undef Smallint\n";
+print "const unsigned\nSqlTypeInfo::m_columnCount = $position;\n";
+print "// template-end\n";
+
+# vim: set sw=4:
diff --git a/storage/ndb/src/old_files/client/odbc/docs/handleattr.pl b/storage/ndb/src/old_files/client/odbc/docs/handleattr.pl
new file mode 100644
index 00000000000..892d34b105b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/handleattr.pl
@@ -0,0 +1,2232 @@
+# usage: perl Attr.data X (X = Env,Dbc,Stmt)
+# prints template for AttrX.cpp
+use strict;
+my $type = shift;
+my $order = 0;
+
+#
+# odbcsqlsetenvattr.htm
+#
+my $attrEnv = {
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLSetEnvAttr</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlsetenvattr"></A>SQLSetEnvAttr</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 3.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLSetEnvAttr</B> sets attributes that govern aspects of environments.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLSetEnvAttr</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHENV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>EnvironmentHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>Attribute</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLPOINTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ValuePtr</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StringLength</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>EnvironmentHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Environment handle.</dd>
+#
+# <DT><I>Attribute</I></DT>
+#
+# <DD>[Input]<BR>
+# Attribute to set, listed in "Comments."</dd>
+#
+# <DT><I>ValuePtr</I></DT>
+#
+# <DD>[Input]<BR>
+# Pointer to the value to be associated with <I>Attribute</I>. Depending on the value of <I>Attribute</I>, <I>ValuePtr</I> will be a 32-bit integer value or point to a null-terminated character string.</dd>
+#
+# <DT><I>StringLength</I></DT>
+#
+# <DD>[Input] If <I>ValuePtr</I> points to a character string or a binary buffer, this argument should be the length of *<I>ValuePtr</I>. If <I>ValuePtr</I> is an integer, <I>StringLength</I> is ignored.</dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLSetEnvAttr</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_ENV and a <I>Handle</I> of <I>EnvironmentHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLSetEnvAttr</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise. If a driver does not support an environment attribute, the error can be returned only during connect time.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01S02</TD>
+# <TD width=26%>Option value changed</TD>
+# <TD width=52%>The driver did not support the value specified in <I>ValuePtr</I> and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation <BR>
+# error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY009</TD>
+# <TD width=26%>Invalid use of null pointer</TD>
+# <TD width=52%>The Attribute argument identified an environment attribute that required a string value, and the <I>ValuePtr</I> argument was a null pointer.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) A connection handle has been allocated on <I>EnvironmentHandle</I>. </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY024</TD>
+# <TD width=26%>Invalid attribute value</TD>
+# <TD width=52%>Given the specified <I>Attribute</I> value, an invalid value was specified in <I>ValuePtr</I>.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=52%>The <I>StringLength</I> argument was less than 0 but was not SQL_NTS.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY092</TD>
+# <TD width=26%>Invalid attribute/option identifier</TD>
+# <TD width=52%>(DM) The value specified for the argument <I>Attribute</I> was not valid for the version of ODBC supported by the driver.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>The value specified for the argument <I>Attribute</I> was a valid ODBC environment attribute for the version of ODBC supported by the driver, but was not supported by the driver.
+# <P>(DM) The <I>Attribute</I> argument was SQL_ATTR_OUTPUT_NTS, and <I>ValuePtr</I> was SQL_FALSE.</P>
+# </TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P>An application can call <B>SQLSetEnvAttr</B> only if no connection handle is allocated on the environment. All environment attributes successfully set by the application for the environment persist until <B>SQLFreeHandle</B> is called on the environment. More than one environment handle can be allocated simultaneously in ODBC 3<I>.x</I>.</P>
+#
+# <P>The format of information set through <I>ValuePtr</I> depends on the specified <I>Attribute</I>. <B>SQLSetEnvAttr</B> will accept attribute information in one of two different formats: a null-terminated character string or a 32-bit integer value. The format of each is noted in the attribute's description.</P>
+#
+# <P>There are no driver-specific environment attributes.</P>
+#
+# <P>Connection attributes cannot be set by a call to <B>SQLSetEnvAttr</B>. Attempting to do so will return SQLSTATE HY092 (Invalid attribute/option identifier).</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=35%><I>Attribute</I></TH>
+# <TH width=65%><I>ValuePtr</I> contents</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=35%>SQL_ATTR_CONNECTION_POOLING<BR>
+# (ODBC 3.0)</TD>
+# <TD width=65%>A 32-bit SQLUINTEGER value that enables or disables connection pooling at the environment level. The following values are used:
+# <P>SQL_CP_OFF = Connection pooling is turned off. This is the default.</P>
+#
+# <P>SQL_CP_ONE_PER_DRIVER = A single connection pool is supported for each driver. Every connection in a pool is associated with one driver.</P>
+#
+# <P>SQL_CP_ONE_PER_HENV = A single connection pool is supported for each environment. Every connection in a pool is associated with one environment.</P>
+#
+# <P>Connection pooling is enabled by calling <B>SQLSetEnvAttr</B> to set the SQL_ATTR_CONNECTION_POOLING attribute to SQL_CP_ONE_PER_DRIVER or SQL_CP_ONE_PER_HENV. This call must be made before the application allocates the shared environment for which connection pooling is to be enabled. The environment handle in the call to <B>SQLSetEnvAttr</B> is set to null, which makes SQL_ATTR_CONNECTION_POOLING a process-level attribute. After connection pooling is enabled, the application then allocates an implicit shared environment by calling <B>SQLAllocHandle</B> with the <I>InputHandle</I> argument set to SQL_HANDLE_ENV.</P>
+#
+# <P>After connection pooling has been enabled and a shared environment has been selected for an application, SQL_ATTR_CONNECTION_POOLING cannot be reset for that environment, because <B>SQLSetEnvAttr</B> is called with a null environment handle when setting this attribute. If this attribute is set while connection pooling is already enabled on a shared environment, the attribute affects only shared environments that are allocated subsequently.</P>
+#
+# <P>For more information, see "<A HREF="odbcodbc_connection_pooling.htm">ODBC Connection Pooling</A>" in Chapter 6: Connecting to a Data Source or Driver.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CONNECTION_POOLING => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_CP_OFF SQL_CP_ONE_PER_DRIVER SQL_CP_ONE_PER_HENV) ],
+ default => q(SQL_CP_OFF),
+ mode => 'rw',
+ order => ++$order,
+ },
+# <TR VALIGN="top">
+# <TD width=35%>SQL_ATTR_CP_MATCH<BR>
+# (ODBC 3.0)</TD>
+# <TD width=65%>A 32-bit SQLUINTEGER value that determines how a connection is chosen from a connection pool. When <B>SQLConnect</B> or <B>SQLDriverConnect</B> is called, the Driver Manager determines which connection is reused from the pool. The Driver Manager attempts to match the connection options in the call and the connection attributes set by the application to the keywords and connection attributes of the connections in the pool. The value of this attribute determines the level of precision of the matching criteria.
+# <P>The following values are used to set the value of this attribute:</P>
+#
+# <P>SQL_CP_STRICT_MATCH = Only connections that exactly match the connection options in the call and the connection attributes set by the application are reused. This is the default.</P>
+#
+# <P>SQL_CP_RELAXED_MATCH = Connections with matching connection string keywords can be used. Keywords must match, but not all connection attributes must match.</P>
+#
+# <P>For more information on how the Driver Manager performs the match in connecting to a pooled connection, see <A HREF="odbcsqlconnect.htm">SQLConnect</A>. For more information on connection pooling, see "<A HREF="odbcodbc_connection_pooling.htm">ODBC Connection Pooling</A>" in Chapter 6: Connecting to a Data Source or Driver.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CP_MATCH => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_CP_STRICT_MATCH SQL_CP_RELAXED_MATCH) ],
+ default => q(SQL_CP_STRICT_MATCH),
+ mode => 'rw',
+ order => ++$order,
+ },
+# <TR VALIGN="top">
+# <TD width=35%>SQL_ATTR_ODBC_VERSION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=65%>A 32-bit integer that determines whether certain functionality exhibits ODBC 2<I>.x</I> behavior or ODBC 3<I>.x</I> behavior. The following values are used to set the value of this attribute:
+# <P>SQL_OV_ODBC3 = The Driver Manager and driver exhibit the following ODBC 3<I>.x</I> behavior:
+#
+# <UL type=disc>
+# <LI>The driver returns and expects ODBC 3<I>.x</I> codes for date, time, and timestamp.</li>
+#
+# <LI>The driver returns ODBC 3<I>.x</I> SQLSTATE codes when <B>SQLError</B>, <B>SQLGetDiagField</B>, or <B>SQLGetDiagRec</B> is called.</li>
+#
+# <LI>The <I>CatalogName</I> argument in a call to <B>SQLTables</B> accepts a search pattern.</li>
+# </UL>
+#
+# <P>SQL_OV_ODBC2 = The Driver Manager and driver exhibit the following ODBC 2<I>.x </I>behavior. This is especially useful for an ODBC 2<I>.x</I> application working with an ODBC 3<I>.x</I> driver.
+#
+# <UL type=disc>
+# <LI>The driver returns and expects ODBC 2<I>.x</I> codes for date, time, and timestamp.</li>
+#
+# <LI>The driver returns ODBC 2<I>.x</I> SQLSTATE codes when <B>SQLError</B>, <B>SQLGetDiagField</B>, or <B>SQLGetDiagRec</B> is called.</li>
+#
+# <LI>The <I>CatalogName</I> argument in a call to <B>SQLTables</B> does not accept a search pattern.</li>
+# </UL>
+#
+# <P>An application must set this environment attribute before calling any function that has an SQLHENV argument, or the call will return SQLSTATE HY010 (Function sequence error). It is driver-specific whether or not additional behaviors exist for these environmental flags.
+#
+# <UL type=disc>
+# <LI>For more information, see "<A HREF="odbcdeclaring_the_application_s_odbc_version.htm">Declaring the Application's ODBC Version</A>" in Chapter 6: Connecting to a Data Source or Driver and "<A HREF="odbcbehavioral_changes.htm">Behavioral Changes</A>" in Chapter 17: Programming Considerations.</li>
+# </UL>
+# </TD>
+# </TR>
+ SQL_ATTR_ODBC_VERSION => {
+ type => q(SQLINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_OV_ODBC3 SQL_OV_ODBC2) ],
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+# <TR VALIGN="top">
+# <TD width=35%>SQL_ATTR_OUTPUT_NTS<BR>
+# (ODBC 3.0)</TD>
+# <TD width=65%>A 32-bit integer that determines how the driver returns string data. If SQL_TRUE, the driver returns string data null-terminated. If SQL_FALSE, the driver does not return string data null-terminated.
+# <P>This attribute defaults to SQL_TRUE. A call to <B>SQLSetEnvAttr</B> to set it to SQL_TRUE returns SQL_SUCCESS. A call to <B>SQLSetEnvAttr</B> to set it to SQL_FALSE returns SQL_ERROR and SQLSTATE HYC00 (Optional feature not implemented).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_OUTPUT_NTS => {
+ type => q(SQLINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_FALSE SQL_TRUE) ],
+ default => q(SQL_TRUE),
+ mode => 'rw',
+ order => ++$order,
+ }
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>For information about</TH>
+# <TH width=52%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Allocating a handle</TD>
+# <TD width=52%><A HREF="odbcsqlallochandle.htm">SQLAllocHandle</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Returning the setting of an environment attribute</TD>
+# <TD width=52%><A HREF="odbcsqlgetenvattr.htm">SQLGetEnvAttr</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+};
+
+#
+# odbcsqlsetconnectattr.htm
+#
+my $attrDbc = {
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLSetConnectAttr</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlsetconnectattr"></A>SQLSetConnectAttr</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 3.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLSetConnectAttr</B> sets attributes that govern aspects of connections.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;For more information about what the Driver Manager maps this function to when an ODBC 3<I>.x</I> application is working with an ODBC 2<I>.x</I> driver, see "<A HREF="odbcmapping_replacement_functions_for_backward_compatibility_of_applications.htm">Mapping Replacement Functions for Backward Compatibility of Applications</A>" in Chapter 17: Programming Considerations.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLSetConnectAttr</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHDBC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ConnectionHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>Attribute</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLPOINTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ValuePtr</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StringLength</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>ConnectionHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Connection handle.</dd>
+#
+# <DT><I>Attribute</I></DT>
+#
+# <DD>[Input]<BR>
+# Attribute to set, listed in "Comments."</dd>
+#
+# <DT><I>ValuePtr</I></DT>
+#
+# <DD>[Input]<BR>
+# Pointer to the value to be associated with <I>Attribute</I>. Depending on the value of <I>Attribute</I>, <I>ValuePtr</I> will be a 32-bit unsigned integer value or will point to a null-terminated character string. Note that if the <I>Attribute</I> argument is a driver-specific value, the value in <I>ValuePtr</I> may be a signed integer.</dd>
+#
+# <DT><I>StringLength</I></DT>
+#
+# <DD>[Input]<BR>
+# If <I>Attribute</I> is an ODBC-defined attribute and <I>ValuePtr</I> points to a character string or a binary buffer, this argument should be the length of *<I>ValuePtr</I>. If <I>Attribute</I> is an ODBC-defined attribute and <I>ValuePtr</I> is an integer, <I>StringLength</I> is ignored.
+#
+# <P>If <I>Attribute</I> is a driver-defined attribute, the application indicates the nature of the attribute to the Driver Manager by setting the <I>StringLength</I> argument. <I>StringLength</I> can have the following values:
+#
+#
+# <UL type=disc>
+# <LI>If <I>ValuePtr</I> is a pointer to a character string, then <I>StringLength</I> is the length of the string or SQL_NTS.</li>
+#
+# <LI>If <I>ValuePtr</I> is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(<I>length</I>) macro in <I>StringLength</I>. This places a negative value in <I>StringLength</I>.</li>
+#
+# <LI>If <I>ValuePtr</I> is a pointer to a value other than a character string or a binary string, then <I>StringLength</I> should have the value SQL_IS_POINTER.</li>
+#
+# <LI>If <I>ValuePtr</I> contains a fixed-length value, then <I>StringLength</I> is either SQL_IS_INTEGER or SQL_IS_UINTEGER, as appropriate.</li>
+# </UL>
+# </dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLSetConnectAttr</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_DBC and a <I>Handle</I> of <I>ConnectionHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLSetConnectAttr</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+#
+# <P>The driver can return SQL_SUCCESS_WITH_INFO to provide information about the result of setting an option.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01S02</TD>
+# <TD width=26%>Option value changed</TD>
+# <TD width=52%>The driver did not support the value specified in <I>ValuePtr</I> and substituted a similar value. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08002</TD>
+# <TD width=26%>Connection name in use</TD>
+# <TD width=52%>The <I>Attribute</I> argument was SQL_ATTR_ODBC_CURSORS, and the driver was already connected to the data source.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08003</TD>
+# <TD width=26%>Connection does not exist</TD>
+# <TD width=52%>(DM) An <I>Attribute</I> value was specified that required an open connection, but the <I>ConnectionHandle</I> was not in a connected state.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>The <I>Attribute</I> argument was SQL_ATTR_CURRENT_CATALOG, and a result set was pending.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>3D000</TD>
+# <TD width=26%>Invalid catalog name</TD>
+# <TD width=52%>The <I>Attribute</I> argument was SQL_CURRENT_CATALOG, and the specified catalog name was invalid.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY009</TD>
+# <TD width=26%>Invalid use of null pointer</TD>
+# <TD width=52%>The <I>Attribute</I> argument identified a connection attribute that required a string value, and the <I>ValuePtr </I>argument was a null pointer.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function was called for a <I>StatementHandle</I> associated with the <I>ConnectionHandle</I> and was still executing when <B>SQLSetConnectAttr</B> was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for a <I>StatementHandle</I> associated with the <I>ConnectionHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+#
+# <P>(DM) <B>SQLBrowseConnect</B> was called for the <I>ConnectionHandle</I> and returned SQL_NEED_DATA. This function was called before <B>SQLBrowseConnect</B> returned SQL_SUCCESS_WITH_INFO or SQL_SUCCESS.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY011</TD>
+# <TD width=26%>Attribute cannot be set now</TD>
+# <TD width=52%>The <I>Attribute</I> argument was SQL_ATTR_TXN_ISOLATION, and a transaction was open.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY024</TD>
+# <TD width=26%>Invalid attribute value</TD>
+# <TD width=52%>Given the specified <I>Attribute</I> value, an invalid value was specified in <I>ValuePtr</I>. (The Driver Manager returns this SQLSTATE only for connection and statement attributes that accept a discrete set of values, such as SQL_ATTR_ACCESS_MODE or SQL_ATTR_ASYNC_ENABLE. For all other connection and statement attributes, the driver must verify the value specified in <I>ValuePtr</I>.)
+# <P>The <I>Attribute</I> argument was SQL_ATTR_TRACEFILE or SQL_ATTR_TRANSLATE_LIB, and <I>ValuePtr</I> was an empty string.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=52%><I>(DM) *ValuePtr </I>is a character string, and the <I>StringLength</I> argument was less than 0 but was not SQL_NTS.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY092</TD>
+# <TD width=26%>Invalid attribute/option identifier</TD>
+# <TD width=52%>(DM) The value specified for the argument <I>Attribute</I> was not valid for the version of ODBC supported by the driver.
+# <P>(DM) The value specified for the argument <I>Attribute</I> was a read-only attribute.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>The value specified for the argument <I>Attribute</I> was a valid ODBC connection or statement attribute for the version of ODBC supported by the driver but was not supported by the driver.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver associated with the <I>ConnectionHandle</I> does not support the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM009</TD>
+# <TD width=26%>Unable to load translation DLL</TD>
+# <TD width=52%>The driver was unable to load the translation DLL that was specified for the connection. This error can be returned only when <I>Attribute</I> is SQL_ATTR_TRANSLATE_LIB.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>When <I>Attribute</I> is a statement attribute, <B>SQLSetConnectAttr</B> can return any SQLSTATEs returned by <B>SQLSetStmtAttr</B>.</P>
+#
+# <P class="label"><B>Comments</B></P>
+#
+# <P>For general information about connection attributes, see "<A HREF="odbcconnection_attributes.htm">Connection Attributes</A>" in Chapter 6: Connecting to a Data Source or Driver.</P>
+#
+# <P>The currently defined attributes and the version of ODBC in which they were introduced are shown in the table later in this section; it is expected that more attributes will be defined to take advantage of different data sources. A range of attributes is reserved by ODBC; driver developers must reserve values for their own driver-specific use from X/Open.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;The ability to set statement attributes at the connection level by calling <B>SQLSetConnectAttr</B> has been deprecated in ODBC 3<I>.x</I>. ODBC 3<I>.x</I> applications should never set statement attributes at the connection level. ODBC 3<I>.x</I> statement attributes cannot be set at the connection level, with the exception of the SQL_ATTR_METADATA_ID and SQL_ATTR_ASYNC_ENABLE attributes, which are both connection attributes and statement attributes and can be set at either the connection level or the statement level.</P>
+#
+# <P class="indent">ODBC 3<I>.x</I> drivers need only support this functionality if they should work with ODBC 2<I>.x</I> applications that set ODBC 2<I>.x</I> statement options at the connection level. For more information, see "<A HREF="odbcsqlsetconnectoption_mapping.htm">SQLSetConnectOption Mapping</A>" in Appendix G: Driver Guidelines for Backward Compatibility.</P>
+#
+# <P>An application can call <B>SQLSetConnectAttr</B> at any time between the time the connection is allocated and freed. All connection and statement attributes successfully set by the application for the connection persist until <B>SQLFreeHandle</B> is called on the connection. For example, if an application calls <B>SQLSetConnectAttr</B> before connecting to a data source, the attribute persists even if <B>SQLSetConnectAttr</B> fails in the driver when the application connects to the data source; if an application sets a driver-specific attribute, the attribute persists even if the application connects to a different driver on the connection.</P>
+#
+# <P>Some connection attributes can be set only before a connection has been made; others can be set only after a connection has been made. The following table indicates those connection attributes that must be set either before or after a connection has been made. <I>Either</I> indicates that the attribute can be set either before or after connection.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>Attribute</TH>
+# <TH width=50%>Set before or after connection?</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_ACCESS_MODE</TD>
+# <TD width=50%>Either<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_ASYNC_ENABLE</TD>
+# <TD width=50%>Either<SUP>[2]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_AUTOCOMMIT</TD>
+# <TD width=50%>Either</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_CONNECTION_TIMEOUT</TD>
+# <TD width=50%>Either</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_CURRENT_CATALOG</TD>
+# <TD width=50%>Either<SUP>[1]</SUP></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_LOGIN_TIMEOUT</TD>
+# <TD width=50%>Before</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_METADATA_ID</TD>
+# <TD width=50%>Either</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_ODBC_CURSORS</TD>
+# <TD width=50%>Before</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_PACKET_SIZE</TD>
+# <TD width=50%>Before</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_QUIET_MODE</TD>
+# <TD width=50%>Either</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_TRACE</TD>
+# <TD width=50%>Either</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_TRACEFILE</TD>
+# <TD width=50%>Either</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_TRANSLATE_LIB</TD>
+# <TD width=50%>After</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_TRANSLATE_OPTION</TD>
+# <TD width=50%>After</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>SQL_ATTR_TXN_ISOLATION</TD>
+# <TD width=50%>Either<SUP>[3]</SUP></TD>
+# </TR>
+# </table></div>
+#
+# <P class="fineprint">[1]&nbsp;&nbsp;&nbsp;SQL_ATTR_ACCESS_MODE and SQL_ATTR_CURRENT_CATALOG can be set before or after connecting, depending on the driver. However, interoperable applications set them before connecting because some drivers do not support changing these after connecting.</p>
+# <P class="fineprint">[2]&nbsp;&nbsp;&nbsp;SQL_ATTR_ASYNC_ENABLE must be set before there is an active statement.</p>
+# <P class="fineprint">[3]&nbsp;&nbsp;&nbsp;SQL_ATTR_TXN_ISOLATION can be set only if there are no open transactions on the connection. Some connection attributes support substitution of a similar value if the data source does not support the value specified in *<I>ValuePtr</I>. In such cases, the driver returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value changed). For example, if <I>Attribute</I> is SQL_ATTR_PACKET_SIZE and *<I>ValuePtr</I> exceeds the maximum packet size, the driver substitutes the maximum size. To determine the substituted value, an application calls <B>SQLGetConnectAttr</B>.</p>
+# <P>The format of information set in the *<I>ValuePtr</I> buffer depends on the specified <I>Attribute</I>. <B>SQLSetConnectAttr</B> will accept attribute information in one of two different formats: a null-terminated character string or a 32-bit integer value. The format of each is noted in the attribute's description. Character strings pointed to by the <I>ValuePtr</I> argument of <B>SQLSetConnectAttr</B> have a length of <I>StringLength</I> bytes.</P>
+#
+# <P>The <I>StringLength</I> argument is ignored if the length is defined by the attribute, as is the case for all attributes introduced in ODBC 2<I>.x</I> or earlier.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=38%><I>Attribute</I></TH>
+# <TH width=62%><I>ValuePtr</I> contents</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ACCESS_MODE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value. SQL_MODE_READ_ONLY is used by the driver or data source as an indicator that the connection is not required to support SQL statements that cause updates to occur. This mode can be used to optimize locking strategies, transaction management, or other areas as appropriate to the driver or data source. The driver is not required to prevent such statements from being submitted to the data source. The behavior of the driver and data source when asked to process SQL statements that are not read-only during a read-only connection is implementation-defined. SQL_MODE_READ_WRITE is the default.</TD>
+# </TR>
+ SQL_ATTR_ACCESS_MODE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_MODE_READ_ONLY SQL_MODE_READ_WRITE) ],
+ default => q(SQL_MODE_READ_WRITE),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ASYNC_ENABLE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether a function called with a statement on the specified connection is executed asynchronously:
+# <P>SQL_ASYNC_ENABLE_OFF = Off (the default)<BR>
+# SQL_ASYNC_ENABLE_ON = On</P>
+#
+# <P>Setting SQL_ASYNC_ENABLE_ON enables asynchronous execution for all future statement handles allocated on this connection. It is driver-defined whether this enables asynchronous execution for existing statement handles associated with this connection. An error is returned if asynchronous execution is enabled while there is an active statement on the connection.</P>
+#
+# <P>This attribute can be set whether <B>SQLGetInfo</B> with the SQL_ASYNC_MODE information type returns SQL_AM_CONNECTION or SQL_AM_STATEMENT.</P>
+#
+# <P>After a function has been called asynchronously, only the original function, <B>SQLAllocHandle</B>, <B>SQLCancel</B>, <B>SQLGetDiagField</B>, or <B>SQLGetDiagRec </B>can be called on the statement or the connection associated with <I>StatementHandle</I>, until the original function returns a code other than SQL_STILL_EXECUTING. Any other function called on <I>StatementHandle</I> or the connection associated with <I>StatementHandle</I> returns SQL_ERROR with an SQLSTATE of HY010 (Function sequence error). Functions can be called on other statements. For more information, see "<A HREF="odbcasynchronous_execution.htm">Asynchronous Execution</A>" in Chapter 9: Executing Statements.</P>
+#
+# <P>In general, applications should execute functions asynchronously only on single-thread operating systems. On multithread operating systems, applications should execute functions on separate threads rather than executing them asynchronously on the same thread. Drivers that operate only on multithread operating systems do not need to support asynchronous execution.</P>
+#
+# <P>The following functions can be executed asynchronously:</P>
+#
+# <P><B>SQLBulkOperations<BR>
+# SQLColAttribute</B><BR>
+# <B>SQLColumnPrivileges</B><BR>
+# <B>SQLColumns</B><BR>
+# <B>SQLCopyDesc</B><BR>
+# <B>SQLDescribeCol</B><BR>
+# <B>SQLDescribeParam</B><BR>
+# <B>SQLExecDirect</B><BR>
+# <B>SQLExecute</B><BR>
+# <B>SQLFetch</B><BR>
+# <B>SQLFetchScroll</B><BR>
+# <B>SQLForeignKeys</B><BR>
+# <B>SQLGetData</B><BR>
+# <B>SQLGetDescField</B><SUP>[1]<BR>
+# </SUP><B>SQLGetDescRec</B><SUP>[1]</SUP><B><BR>
+# SQLGetDiagField</B><BR>
+# <B>SQLGetDiagRec<BR>
+# SQLGetTypeInfo</B><BR>
+# <B>SQLMoreResults</B><BR>
+# <B>SQLNumParams</B><BR>
+# <B>SQLNumResultCols</B><BR>
+# <B>SQLParamData</B><BR>
+# <B>SQLPrepare</B><BR>
+# <B>SQLPrimaryKeys</B><BR>
+# <B>SQLProcedureColumns</B><BR>
+# <B>SQLProcedures</B><BR>
+# <B>SQLPutData</B><BR>
+# <B>SQLSetPos</B><BR>
+# <B>SQLSpecialColumns</B><BR>
+# <B>SQLStatistics</B><BR>
+# <B>SQLTablePrivileges</B><BR>
+# <B>SQLTables</B></P>
+# </TD>
+# </TR>
+ SQL_ASYNC_ENABLE_ON => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_ASYNC_ENABLE_OFF SQL_ASYNC_ENABLE_ON) ],
+ default => q(SQL_ASYNC_ENABLE_OFF),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_AUTO_IPD<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>A read-only SQLUINTEGER value that specifies whether automatic population of the IPD after a call to <B>SQLPrepare </B>is supported:
+# <P>SQL_TRUE = Automatic population of the IPD after a call to <B>SQLPrepare </B>is supported by the driver.</P>
+#
+# <P>SQL_FALSE = Automatic population of the IPD after a call to <B>SQLPrepare </B>is not supported by the driver. Servers that do not support prepared statements will not be able to populate the IPD automatically. </P>
+#
+# <P>If SQL_TRUE is returned for the SQL_ATTR_AUTO_IPD connection attribute, the statement attribute SQL_ATTR_ENABLE_AUTO_IPD can be set to turn automatic population of the IPD on or off. If SQL_ATTR_AUTO_IPD is SQL_FALSE, SQL_ATTR_ENABLE_AUTO_IPD cannot be set to SQL_TRUE. The default value of SQL_ATTR_ENABLE_AUTO_IPD is equal to the value of SQL_ATTR_AUTO_IPD.</P>
+#
+# <P>This connection attribute can be returned by <B>SQLGetConnectAttr</B> but cannot be set by <B>SQLSetConnectAttr</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_AUTO_IPD => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_FALSE SQL_TRUE) ],
+ default => undef,
+ mode => 'ro',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_AUTOCOMMIT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether to use autocommit or manual-commit mode:
+# <P>SQL_AUTOCOMMIT_OFF = The driver uses manual-commit mode, and the application must explicitly commit or roll back transactions with <B>SQLEndTran</B>.</P>
+#
+# <P>SQL_AUTOCOMMIT_ON = The driver uses autocommit mode. Each statement is committed immediately after it is executed. This is the default. Any open transactions on the connection are committed when SQL_ATTR_AUTOCOMMIT is set to SQL_AUTOCOMMIT_ON to change from manual-commit mode to autocommit mode.</P>
+#
+# <P>For more information, see "<A HREF="odbccommit_mode.htm">Commit Mode</A>" in Chapter 14: Transactions.</P>
+#
+# <P class="indent"><b class="le">Important&nbsp;&nbsp;&nbsp;</b>Some data sources delete the access plans and close the cursors for all statements on a connection each time a statement is committed; autocommit mode can cause this to happen after each nonquery statement is executed or when the cursor is closed for a query. For more information, see the SQL_CURSOR_COMMIT_BEHAVIOR and SQL_CURSOR_ROLLBACK_BEHAVIOR information types in <A HREF="odbcsqlgetinfo.htm">SQLGetInfo</A> and "<A HREF="odbceffect_of_transactions_on_cursors_and_prepared_statements.htm">Effect of Transactions on Cursors and Prepared Statements</A>" in Chapter 14: Transactions.</P>
+#
+# <P>When a batch is executed in autocommit mode, two things are possible. The entire batch can be treated as an autocommitable unit, or each statement in a batch is treated as an autocommitable unit. Certain data sources can support both these behaviors and may provide a way of choosing one or the other. It is driver-defined whether a batch is treated as an autocommitable unit or whether each individual statement within the batch is autocommitable.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_AUTOCOMMIT => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_AUTOCOMMIT_OFF SQL_AUTOCOMMIT_ON) ],
+ default => q(SQL_AUTOCOMMIT_ON),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CONNECTION_DEAD
+# <P>(ODBC 3.5)</P>
+# </TD>
+# <TD width=62%>An SQLUINTERGER value that indicates the state of the connection. If SQL_CD_TRUE, the connection has been lost. If SQL_CD_FALSE, the connection is still active.</TD>
+# </TR>
+ SQL_ATTR_CONNECTION_DEAD => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_CD_FALSE SQL_CD_TRUE) ],
+ default => undef,
+ mode => 'ro',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CONNECTION_TIMEOUT<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value corresponding to the number of seconds to wait for any request on the connection to complete before returning to the application. The driver should return SQLSTATE HYT00 (Timeout expired) anytime that it is possible to time out in a situation not associated with query execution or login.
+# <P>If <I>ValuePtr</I> is equal to 0 (the default), there is no timeout.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CONNECTION_TIMEOUT => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => 0,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CURRENT_CATALOG<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>A character string containing the name of the catalog to be used by the data source. For example, in SQL Server, the catalog is a database, so the driver sends a <B>USE</B> <I>database</I> statement to the data source, where <I>database</I> is the database specified in *<I>ValuePtr</I>. For a single-tier driver, the catalog might be a directory, so the driver changes its current directory to the directory specified in *<I>ValuePtr</I>.</TD>
+# </TR>
+ SQL_ATTR_CURRENT_CATALOG => {
+ type => q(SQLCHAR),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_LOGIN_TIMEOUT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value corresponding to the number of seconds to wait for a login request to complete before returning to the application. The default is driver-dependent. If <I>ValuePtr</I> is 0, the timeout is disabled and a connection attempt will wait indefinitely.
+# <P>If the specified timeout exceeds the maximum login timeout in the data source, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_LOGIN_TIMEOUT => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => 0,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_METADATA_ID<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that determines how the string arguments of catalog functions are treated.
+# <P>If SQL_TRUE, the string argument of catalog functions are treated as identifiers. The case is not significant. For nondelimited strings, the driver removes any trailing spaces and the string is folded to uppercase. For delimited strings, the driver removes any leading or trailing spaces and takes literally whatever is between the delimiters. If one of these arguments is set to a null pointer, the function returns SQL_ERROR and SQLSTATE HY009 (Invalid use of null pointer). </P>
+#
+# <P>If SQL_FALSE, the string arguments of catalog functions are not treated as identifiers. The case is significant. They can either contain a string search pattern or not, depending on the argument.</P>
+#
+# <P>The default value is SQL_FALSE.</P>
+#
+# <P>The <I>TableType</I> argument of <B>SQLTables</B>, which takes a list of values, is not affected by this attribute.</P>
+#
+# <P>SQL_ATTR_METADATA_ID can also be set on the statement level. (It is the only connection attribute that is also a statement attribute.)</P>
+#
+# <P>For more information, see "<A HREF="odbcarguments_in_catalog_functions.htm">Arguments in Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_METADATA_ID => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_FALSE SQL_TRUE) ],
+ default => q(SQL_FALSE),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ODBC_CURSORS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value specifying how the Driver Manager uses the ODBC cursor library:
+# <P>SQL_CUR_USE_IF_NEEDED = The Driver Manager uses the ODBC cursor library only if it is needed. If the driver supports the SQL_FETCH_PRIOR option in <B>SQLFetchScroll</B>, the Driver Manager uses the scrolling capabilities of the driver. Otherwise, it uses the ODBC cursor library.</P>
+#
+# <P>SQL_CUR_USE_ODBC = The Driver Manager uses the ODBC cursor library.</P>
+#
+# <P>SQL_CUR_USE_DRIVER = The Driver Manager uses the scrolling capabilities of the driver. This is the default setting.</P>
+#
+# <P>For more information about the ODBC cursor library, see <A HREF="odbcodbc_cursor_library.htm">Appendix F: ODBC Cursor Library</A>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ODBC_CURSORS => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_CUR_USE_IF_NEEDED SQL_CUR_USE_ODBC SQL_CUR_USE_DRIVER) ],
+ default => q(SQL_CUR_USE_DRIVER),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PACKET_SIZE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value specifying the network packet size in bytes.
+# <P class="indent"><B>Note</B>&nbsp;&nbsp;&nbsp;Many data sources either do not support this option or only can return but not set the network packet size.</P>
+#
+# <P>If the specified size exceeds the maximum packet size or is smaller than the minimum packet size, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).</P>
+#
+# <P>If the application sets packet size after a connection has already been made, the driver will return SQLSTATE HY011 (Attribute cannot be set now).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PACKET_SIZE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_QUIET_MODE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>A 32-bit window handle (<I>hwnd</I>).
+# <P>If the window handle is a null pointer, the driver does not display any dialog boxes.</P>
+#
+# <P>If the window handle is not a null pointer, it should be the parent window handle of the application. This is the default. The driver uses this handle to display dialog boxes.</P>
+#
+# <P class="indent"><b class="le">Note&nbsp;&nbsp;&nbsp;</b>The SQL_ATTR_QUIET_MODE connection attribute does not apply to dialog boxes displayed by <B>SQLDriverConnect</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_QUIET_MODE => {
+ type => q(SQLPOINTER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_TRACE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value telling the Driver Manager whether to perform tracing:
+# <P>SQL_OPT_TRACE_OFF = Tracing off (the default)</P>
+#
+# <P>SQL_OPT_TRACE_ON = Tracing on</P>
+#
+# <P>When tracing is on, the Driver Manager writes each ODBC function call to the trace file.</P>
+#
+# <P class="indent"><b class="le">Note&nbsp;&nbsp;&nbsp;</b>When tracing is on, the Driver Manager can return SQLSTATE IM013 (Trace file error) from any function.</P>
+#
+# <P>An application specifies a trace file with the SQL_ATTR_TRACEFILE option. If the file already exists, the Driver Manager appends to the file. Otherwise, it creates the file. If tracing is on and no trace file has been specified, the Driver Manager writes to the file SQL.LOG in the root directory. </P>
+#
+# <P>An application can set the variable <B>ODBCSharedTraceFlag</B> to enable tracing dynamically. Tracing is then enabled for all ODBC applications currently running. If an application turns tracing off, it is turned off only for that application.</P>
+#
+# <P>If the <B>Trace</B> keyword in the system information is set to 1 when an application calls <B>SQLAllocHandle</B> with a <I>HandleType</I> of SQL_HANDLE_ENV, tracing is enabled for all handles. It is enabled only for the application that called <B>SQLAllocHandle</B>.</P>
+#
+# <P>Calling <B>SQLSetConnectAttr</B> with an <I>Attribute</I> of SQL_ATTR_TRACE does not require that the <I>ConnectionHandle</I> argument be valid and will not return SQL_ERROR if <I>ConnectionHandle</I> is NULL. This attribute applies to all connections.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_TRACE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_OPT_TRACE_OFF SQL_OPT_TRACE_ON) ],
+ default => q(SQL_OPT_TRACE_OFF),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_TRACEFILE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>A null-terminated character string containing the name of the trace file.
+# <P>The default value of the SQL_ATTR_TRACEFILE attribute is specified with the <B>TraceFile</B> keyword in the system information. For more information, see "<A HREF="odbcodbc_subkey.htm">ODBC Subkey</A>" in Chapter 19: Configuring Data Sources.</P>
+#
+# <P>Calling <B>SQLSetConnectAttr</B> with an <I>Attribute</I> of SQL_ATTR_ TRACEFILE does not require the <I>ConnectionHandle</I> argument to be valid and will not return SQL_ERROR if <I>ConnectionHandle</I> is invalid. This attribute applies to all connections.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_TRACEFILE => {
+ type => q(SQLCHAR),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_TRANSLATE_LIB<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>A null-terminated character string containing the name of a library containing the functions <B>SQLDriverToDataSource</B> and <B>SQLDataSourceToDriver</B> that the driver accesses to perform tasks such as character set translation. This option may be specified only if the driver has connected to the data source. The setting of this attribute will persist across connections. For more information about translating data, see "<A HREF="odbctranslation_dlls.htm">Translation DLLs</A>" in Chapter 17: Programming Considerations, and <A HREF="odbctranslation_dll_function_reference.htm">Chapter 24: Translation DLL Function Reference</A>.</TD>
+# </TR>
+ SQL_ATTR_TRANSLATE_LIB => {
+ type => q(SQLCHAR),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_TRANSLATE_OPTION<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>A 32-bit flag value that is passed to the translation DLL. This attribute can be specified only if the driver has connected to the data source. For information about translating data, see "<A HREF="odbctranslation_dlls.htm">Translation DLLs</A>" in Chapter 17: Programming Considerations.</TD>
+# </TR>
+ SQL_ATTR_TRANSLATE_OPTION => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_TXN_ISOLATION<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>A 32-bit bitmask that sets the transaction isolation level for the current connection. An application must call <B>SQLEndTran</B> to commit or roll back all open transactions on a connection, before calling <B>SQLSetConnectAttr</B> with this option.
+# <P>The valid values for <I>ValuePtr</I> can be determined by calling <B>SQLGetInfo</B> with <I>InfoType</I> equal to SQL_TXN_ISOLATION_OPTIONS.</P>
+#
+# <P>For a description of transaction isolation levels, see the description of the SQL_DEFAULT_TXN_ISOLATION information type in <B>SQLGetInfo</B> and "<A HREF="odbctransaction_isolation_levels.htm">Transaction Isolation Levels</A>" in Chapter 14: Transactions.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_TXN_ISOLATION => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+# </table></div>
+#
+# <P class="fineprint">[1]&nbsp;&nbsp;&nbsp;These functions can be called asynchronously only if the descriptor is an implementation descriptor, not an application descriptor.</p>
+# <P class="label"><B>Code Example</B></P>
+#
+# <P>See <A HREF="odbcsqlconnect.htm">SQLConnect</A>.</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>For information about</TH>
+# <TH width=52%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Allocating a handle</TD>
+# <TD width=52%><A HREF="odbcsqlallochandle.htm">SQLAllocHandle</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>Returning the setting of a connection <BR>
+# attribute</TD>
+# <TD width=52%><A HREF="odbcsqlgetconnectattr.htm">SQLGetConnectAttr</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+};
+
+#
+# odbcsqlsetstmtattr.htm
+#
+my $attrStmt = {
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLSetStmtAttr</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlsetstmtattr"></A>SQLSetStmtAttr</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 3.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLSetStmtAttr</B> sets attributes related to a statement.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;For more information about what the Driver Manager maps this function to when an ODBC 3<I>.x</I> application is working with an ODBC 2<I>.x</I> driver, see "<A HREF="odbcmapping_replacement_functions_for_backward_compatibility_of_applications.htm">Mapping Replacement Functions for Backward Compatibility of Applications</A>" in Chapter 17: Programming Considerations.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLSetStmtAttr</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StatementHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>Attribute</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLPOINTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ValuePtr</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StringLength</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>StatementHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Statement handle.</dd>
+#
+# <DT><I>Attribute</I></DT>
+#
+# <DD>[Input]<BR>
+# Option to set, listed in "Comments."</dd>
+#
+# <DT><I>ValuePtr</I></DT>
+#
+# <DD>[Input]<BR>
+# Pointer to the value to be associated with <I>Attribute</I>. Depending on the value of <I>Attribute</I>, <I>ValuePtr</I> will be a 32-bit unsigned integer value or a pointer to a null-terminated character string, a binary buffer, or a driver-defined value. If the <I>Attribute</I> argument is a driver-specific value, <I>ValuePtr</I> may be a signed integer.</dd>
+#
+# <DT><I>StringLength</I></DT>
+#
+# <DD>[Input]<BR>
+# If <I>Attribute</I> is an ODBC-defined attribute and <I>ValuePtr</I> points to a character string or a binary buffer, this argument should be the length of *<I>ValuePtr</I>. If <I>Attribute</I> is an ODBC-defined attribute and <I>ValuePtr</I> is an integer, <I>StringLength</I> is ignored.
+#
+# <P>If <I>Attribute</I> is a driver-defined attribute, the application indicates the nature of the attribute to the Driver Manager by setting the <I>StringLength</I> argument. <I>StringLength</I> can have the following values:
+# </dd>
+# </DL>
+#
+# <UL type=disc>
+# <LI>If <I>ValuePtr</I> is a pointer to a character string, then <I>StringLength</I> is the length of the string or SQL_NTS.</li>
+#
+# <LI>If <I>ValuePtr</I> is a pointer to a binary buffer, then the application places the result of the SQL_LEN_BINARY_ATTR(<I>length</I>) macro in <I>StringLength</I>. This places a negative value in <I>StringLength</I>.</li>
+#
+# <LI>If <I>ValuePtr</I> is a pointer to a value other than a character string or a binary string, then <I>StringLength</I> should have the value SQL_IS_POINTER. </li>
+#
+# <LI>If <I>ValuePtr</I> contains a fixed-length value, then <I>StringLength</I> is either SQL_IS_INTEGER or SQL_IS_UINTEGER, as appropriate.</li>
+# </UL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLSetStmtAttr</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_STMT and a <I>Handle</I> of <I>StatementHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLSetStmtAttr</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01S02</TD>
+# <TD width=26%>Option value changed</TD>
+# <TD width=52%>The driver did not support the value specified in <I>ValuePtr</I>, or the value specified in <I>ValuePtr</I> was invalid because of implementation working conditions, so the driver substituted a similar value. (<B>SQLGetStmtAttr</B> can be called to determine the temporarily substituted value.) The substitute value is valid for the <I>StatementHandle</I> until the cursor is closed, at which point the statement attribute reverts to its previous value. The statement attributes that can be changed are:
+# <P>SQL_ ATTR_CONCURRENCY<BR>
+# SQL_ ATTR_CURSOR_TYPE<BR>
+# SQL_ ATTR_KEYSET_SIZE<BR>
+# SQL_ ATTR_MAX_LENGTH<BR>
+# SQL_ ATTR_MAX_ROWS<BR>
+# SQL_ ATTR_QUERY_TIMEOUT <BR>
+# SQL_ATTR_ROW_ARRAY_SIZE<BR>
+# SQL_ ATTR_SIMULATE_CURSOR </P>
+#
+# <P>(Function returns SQL_SUCCESS_WITH_INFO.)</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>The <I>Attribute</I> was SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_SIMULATE_CURSOR, or SQL_ATTR_USE_BOOKMARKS, and the cursor was open.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation <BR>
+# error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY009</TD>
+# <TD width=26%>Invalid use of null pointer</TD>
+# <TD width=52%>The <I>Attribute</I> argument identified a statement attribute that required a string attribute, and the <I>ValuePtr </I>argument was a null pointer.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function was called for the <I>StatementHandle</I> and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY011</TD>
+# <TD width=26%>Attribute cannot be set now</TD>
+# <TD width=52%>The <I>Attribute</I> was SQL_ATTR_CONCURRENCY, SQL_ ATTR_CURSOR_TYPE, SQL_ ATTR_SIMULATE_CURSOR, or SQL_ ATTR_USE_BOOKMARKS, and the statement was prepared.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY017</TD>
+# <TD width=26%>Invalid use of an automatically allocated descriptor handle</TD>
+# <TD width=52%>(DM) The <I>Attribute</I> argument was SQL_ATTR_IMP_ROW_DESC or SQL_ATTR_IMP_PARAM_DESC.
+# <P>(DM) The <I>Attribute</I> argument was SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC, and the value in <I>ValuePtr</I> was an implicitly allocated descriptor handle other than the handle originally allocated for the ARD or APD.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY024</TD>
+# <TD width=26%>Invalid attribute value</TD>
+# <TD width=52%>Given the specified <I>Attribute</I> value, an invalid value was specified in <I>ValuePtr</I>. (The Driver Manager returns this SQLSTATE only for connection and statement attributes that accept a discrete set of values, such as SQL_ATTR_ACCESS_MODE or SQL_ ATTR_ASYNC_ENABLE. For all other connection and statement attributes, the driver must verify the value specified in <I>ValuePtr</I>.)
+# <P>The <I>Attribute</I> argument was SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC, and <I>ValuePtr</I> was an explicitly allocated descriptor handle that is not on the same connection as the <I>StatementHandle</I> argument.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=52%>(DM) <I>*ValuePtr</I> is a character string, and the <I>StringLength</I> argument was less than 0 but was not SQL_NTS.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY092</TD>
+# <TD width=26%>Invalid attribute/option identifier</TD>
+# <TD width=52%>(DM) The value specified for the argument <I>Attribute</I> was not valid for the version of ODBC supported by the driver.
+# <P>(DM) The value specified for the argument <I>Attribute</I> was a read-only attribute.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>The value specified for the argument <I>Attribute</I> was a valid ODBC statement attribute for the version of ODBC supported by the driver but was not supported by the driver.
+# <P>The <I>Attribute</I> argument was SQL_ATTR_ASYNC_ENABLE, and a call to <B>SQLGetInfo</B> with an <I>InfoType</I> of SQL_ASYNC_MODE returns SQL_AM_CONNECTION.</P>
+#
+# <P>The <I>Attribute</I> argument was SQL_ATTR_ENABLE_AUTO_IPD, and the value of the connection attribute SQL_ATTR_AUTO_IPD was SQL_FALSE.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver associated with the <I>StatementHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P>Statement attributes for a statement remain in effect until they are changed by another call to <B>SQLSetStmtAttr</B> or until the statement is dropped by calling <B>SQLFreeHandle</B>. Calling <B>SQLFreeStmt</B> with the SQL_CLOSE, SQL_UNBIND, or SQL_RESET_PARAMS option does not reset statement attributes.</P>
+#
+# <P>Some statement attributes support substitution of a similar value if the data source does not support the value specified in <I>ValuePtr</I>. In such cases, the driver returns SQL_SUCCESS_WITH_INFO and SQLSTATE 01S02 (Option value changed). For example, if <I>Attribute</I> is SQL_ATTR_CONCURRENCY and <I>ValuePtr</I> is SQL_CONCUR_ROWVER, and if the data source does not support this, the driver substitutes SQL_CONCUR_VALUES and returns SQL_SUCCESS_WITH_INFO. To determine the substituted value, an application calls <B>SQLGetStmtAttr</B>.</P>
+#
+# <P>The format of information set with <I>ValuePtr</I> depends on the specified <I>Attribute</I>. <B>SQLSetStmtAttr</B> accepts attribute information in one of two different formats: a character string or a 32-bit integer value. The format of each is noted in the attribute's description. This format applies to the information returned for each attribute in <B>SQLGetStmtAttr</B>. Character strings pointed to by the <I>ValuePtr</I> argument of <B>SQLSetStmtAttr</B> have a length of <I>StringLength</I>.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;The ability to set statement attributes at the connection level by calling <B>SQLSetConnectAttr</B> has been deprecated in ODBC 3<I>.x</I>. ODBC 3<I>.x</I> applications should never set statement attributes at the connection level. ODBC 3<I>.x</I> statement attributes cannot be set at the connection level, with the exception of the SQL_ATTR_METADATA_ID and SQL_ATTR_ASYNC_ENABLE attributes, which are both connection attributes and statement attributes, and can be set at either the connection level or the statement level.</P>
+#
+# <P class="indent">ODBC 3<I>.x</I> drivers need only support this functionality if they should work with ODBC 2<I>.x</I> applications that set ODBC 2<I>.x</I> statement options at the connection level. For more information, see "Setting Statement Options on the Connection Level" under "<A HREF="odbcsqlsetconnectoption_mapping.htm">SQLSetConnectOption Mapping</A>" in Appendix G: Driver Guidelines for Backward Compatibility.</P>
+#
+# <H1>Statement Attributes That Set Descriptor Fields</H1>
+#
+# <P>Many statement attributes correspond to a header field of a descriptor. Setting these attributes actually results in the setting of the descriptor fields. Setting fields by a call to <B>SQLSetStmtAttr</B> rather than to <B>SQLSetDescField</B> has the advantage that a descriptor handle does not have to be obtained for the function call.</P>
+#
+# <P class="indent"><b class="le">Caution</b>&nbsp;&nbsp;&nbsp;Calling <B>SQLSetStmtAttr</B> for one statement can affect other statements. This occurs when the APD or ARD associated with the statement is explicitly allocated and is also associated with other statements. Because <B>SQLSetStmtAttr</B> modifies the APD or ARD, the modifications apply to all statements with which this descriptor is associated. If this is not the required behavior, the application should dissociate this descriptor from the other statements (by calling <B>SQLSetStmtAttr</B> to set the SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC field to a different descriptor handle) before calling <B>SQLSetStmtAttr</B> again.</P>
+#
+# <P>When a descriptor field is set as a result of the corresponding statement attribute being set, the field is set only for the applicable descriptors that are currently associated with the statement identified by the <I>StatementHandle</I> argument, and the attribute setting does not affect any descriptors that may be associated with that statement in the future. When a descriptor field that is also a statement attribute is set by a call to <B>SQLSetDescField</B>, the corresponding statement attribute is set. If an explicitly allocated descriptor is dissociated from a statement, a statement attribute that corresponds to a header field will revert to the value of the field in the implicitly allocated descriptor.</P>
+#
+# <P>When a statement is allocated (see <A HREF="odbcsqlallochandle.htm">SQLAllocHandle</A>), four descriptor handles are automatically allocated and associated with the statement. Explicitly allocated descriptor handles can be associated with the statement by calling <B>SQLAllocHandle</B> with an <I>fHandleType</I> of SQL_HANDLE_DESC to allocate a descriptor handle and then calling <B>SQLSetStmtAttr</B> to associate the descriptor handle with the statement. </P>
+#
+# <P>The statement attributes in the following table correspond to descriptor header fields.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=42%>Statement attribute</TH>
+# <TH width=45%>Header field</TH>
+# <TH width=13%>Desc.</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_PARAM_BIND_OFFSET_PTR</TD>
+# <TD width=45%>SQL_DESC_BIND_OFFSET_PTR</TD>
+# <TD width=13%>APD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_PARAM_BIND_TYPE</TD>
+# <TD width=45%>SQL_DESC_BIND_TYPE</TD>
+# <TD width=13%>APD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_PARAM_OPERATION_PTR</TD>
+# <TD width=45%>SQL_DESC_ARRAY_STATUS_PTR</TD>
+# <TD width=13%>APD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_PARAM_STATUS_PTR</TD>
+# <TD width=45%>SQL_DESC_ARRAY_STATUS_PTR</TD>
+# <TD width=13%>IPD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_PARAMS_PROCESSED_PTR</TD>
+# <TD width=45%>SQL_DESC_ROWS_PROCESSED_PTR</TD>
+# <TD width=13%>IPD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_PARAMSET_SIZE</TD>
+# <TD width=45%>SQL_DESC_ARRAY_SIZE</TD>
+# <TD width=13%>APD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_ROW_ARRAY_SIZE</TD>
+# <TD width=45%>SQL_DESC_ARRAY_SIZE</TD>
+# <TD width=13%>ARD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_ROW_BIND_OFFSET_PTR</TD>
+# <TD width=45%>SQL_DESC_BIND_OFFSET_PTR</TD>
+# <TD width=13%>ARD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_ROW_BIND_TYPE</TD>
+# <TD width=45%>SQL_DESC_BIND_TYPE</TD>
+# <TD width=13%>ARD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_ROW_OPERATION_PTR</TD>
+# <TD width=45%>SQL_DESC_ARRAY_STATUS_PTR</TD>
+# <TD width=13%>ARD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_ROW_STATUS_PTR</TD>
+# <TD width=45%>SQL_DESC_ARRAY_STATUS_PTR</TD>
+# <TD width=13%>IRD</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=42%>SQL_ATTR_ROWS_FETCHED_PTR</TD>
+# <TD width=45%>SQL_DESC_ROWS_PROCESSED_PTR</TD>
+# <TD width=13%>IRD</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <H1>Statement Attributes</H1>
+#
+# <P>The currently defined attributes and the version of ODBC in which they were introduced are shown in the following table; it is expected that more attributes will be defined by drivers to take advantage of different data sources. A range of attributes is reserved by ODBC; driver developers must reserve values for their own driver-specific use from X/Open. For more information, see "<A HREF="odbcdriver_specific_data_types__descriptor_types__information_types.htm">Driver-Specific Data Types, Descriptor Types, Information Types, Diagnostic Types, and Attributes</A>" in Chapter 17: Programming Considerations.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=38%>Attribute</TH>
+# <TH width=62%><I>ValuePtr</I> contents</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_APP_PARAM_DESC<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>The handle to the APD for subsequent calls to <B>SQLExecute</B> and <B>SQLExecDirect</B> on the statement handle. The initial value of this attribute is the descriptor implicitly allocated when the statement was initially allocated. If the value of this attribute is set to SQL_NULL_DESC or the handle originally allocated for the descriptor, an explicitly allocated APD handle that was previously associated with the statement handle is dissociated from it and the statement handle reverts to the implicitly allocated APD handle.
+# <P>This attribute cannot be set to a descriptor handle that was implicitly allocated for another statement or to another descriptor handle that was implicitly set on the same statement; implicitly allocated descriptor handles cannot be associated with more than one statement or descriptor handle.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_APP_PARAM_DESC => {
+ type => q(SQLPOINTER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_APP_ROW_DESC<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>The handle to the ARD for subsequent fetches on the statement handle. The initial value of this attribute is the descriptor implicitly allocated when the statement was initially allocated. If the value of this attribute is set to SQL_NULL_DESC or the handle originally allocated for the descriptor, an explicitly allocated ARD handle that was previously associated with the statement handle is dissociated from it and the statement handle reverts to the implicitly allocated ARD handle.
+# <P>This attribute cannot be set to a descriptor handle that was implicitly allocated for another statement or to another descriptor handle that was implicitly set on the same statement; implicitly allocated descriptor handles cannot be associated with more than one statement or descriptor handle.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_APP_ROW_DESC => {
+ type => q(SQLPOINTER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ASYNC_ENABLE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether a function called with the specified statement is executed asynchronously:
+# <P>SQL_ASYNC_ENABLE_OFF = Off (the default)<BR>
+# SQL_ASYNC_ENABLE_ON = On</P>
+#
+# <P>Once a function has been called asynchronously, only the original function, <B>SQLCancel</B>, <B>SQLGetDiagField</B>, or <B>SQLGetDiagRec</B> can be called on the statement, and only the original function, <B>SQLAllocHandle </B>(with a <I>HandleType</I> of SQL_HANDLE_STMT), <B>SQLGetDiagField</B>, <B>SQLGetDiagRec</B>, or <B>SQLGetFunctions</B> can be called on the connection associated with the statement, until the original function returns a code other than SQL_STILL_EXECUTING. Any other function called on the statement or the connection associated with the statement returns SQL_ERROR with an SQLSTATE of HY010 (Function sequence error). Functions can be called on other statements. For more information, see "<A HREF="odbcasynchronous_execution.htm">Asynchronous Execution</A>" in Chapter 9: Executing Statements.</P>
+#
+# <P>For drivers with statement level asynchronous execution support, the statement attribute SQL_ATTR_ASYNC_ENABLE may be set. Its initial value is the same as the value of the connection level attribute with the same name at the time the statement handle was allocated. </P>
+#
+# <P>For drivers with connection-level, asynchronous-execution support, the statement attribute SQL_ATTR_ASYNC_ENABLE is read-only. Its value is the same as the value of the connection level attribute with the same name at the time the statement handle was allocated. Calling <B>SQLSetStmtAttr</B> to set SQL_ATTR_ASYNC_ENABLE when the SQL_ASYNC_MODE <I>InfoType</I> returns SQL_AM_CONNECTION returns SQLSTATE HYC00 (Optional feature not implemented). (See <B>SQLSetConnectAttr</B> for more information.)</P>
+#
+# <P>As a standard practice, applications should execute functions asynchronously only on single-thread operating systems. On multithread operating systems, applications should execute functions on separate threads rather than executing them asynchronously on the same thread. No functionality is lost if drivers that operate only on multithread operating systems do not need to support asynchronous execution. </P>
+#
+# <P>The following functions can be executed asynchronously:</P>
+#
+# <P><B>SQLBulkOperations<BR>
+# SQLColAttribute</B><BR>
+# <B>SQLColumnPrivileges</B><BR>
+# <B>SQLColumns</B><BR>
+# <B>SQLCopyDesc</B><BR>
+# <B>SQLDescribeCol</B><BR>
+# <B>SQLDescribeParam</B><BR>
+# <B>SQLExecDirect</B><BR>
+# <B>SQLExecute</B><BR>
+# <B>SQLFetch</B><BR>
+# <B>SQLFetchScroll</B><BR>
+# <B>SQLForeignKeys</B><BR>
+# <B>SQLGetData</B><BR>
+# <B>SQLGetDescField</B><SUP>[1]</SUP><BR>
+# <B>SQLGetDescRec</B><SUP>[1]</SUP><B><BR>
+# SQLGetDiagField</B><BR>
+# <B>SQLGetDiagRec<BR>
+# SQLGetTypeInfo</B><BR>
+# <B>SQLMoreResults</B><BR>
+# <B>SQLNumParams</B><BR>
+# <B>SQLNumResultCols</B><BR>
+# <B>SQLParamData</B><BR>
+# <B>SQLPrepare</B><BR>
+# <B>SQLPrimaryKeys</B><BR>
+# <B>SQLProcedureColumns</B><BR>
+# <B>SQLProcedures</B><BR>
+# <B>SQLPutData</B><BR>
+# <B>SQLSetPos</B><BR>
+# <B>SQLSpecialColumns</B><BR>
+# <B>SQLStatistics</B><BR>
+# <B>SQLTablePrivileges</B><BR>
+# <B>SQLTables</B></P>
+# </TD>
+# </TR>
+ SQL_ATTR_ASYNC_ENABLE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CONCURRENCY<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies the cursor concurrency:
+# <P>SQL_CONCUR_READ_ONLY = Cursor is read-only. No updates are allowed.</P>
+#
+# <P>SQL_CONCUR_LOCK = Cursor uses the lowest level of locking sufficient to ensure that the row can be updated.</P>
+#
+# <P>SQL_CONCUR_ROWVER = Cursor uses optimistic concurrency control, comparing row versions such as SQLBase ROWID or Sybase TIMESTAMP.</P>
+#
+# <P>SQL_CONCUR_VALUES = Cursor uses optimistic concurrency control, comparing values.</P>
+#
+# <P>The default value for SQL_ATTR_CONCURRENCY is SQL_CONCUR_READ_ONLY.</P>
+#
+# <P>This attribute cannot be specified for an open cursor. For more information, see "<A HREF="odbcconcurrency_types.htm">Concurrency Types</A>" in Chapter 14: Transactions.</P>
+#
+# <P>If the SQL_ATTR_CURSOR_TYPE <I>Attribute</I> is changed to a type that does not support the current value of SQL_ATTR_CONCURRENCY, the value of SQL_ATTR_CONCURRENCY will be changed at execution time, and a warning issued when <B>SQLExecDirect</B> or <B>SQLPrepare</B> is called.</P>
+#
+# <P>If the driver supports the <B>SELECT FOR UPDATE</B> statement and such a statement is executed while the value of SQL_ATTR_CONCURRENCY is set to SQL_CONCUR_READ_ONLY, an error will be returned. If the value of SQL_ATTR_CONCURRENCY is changed to a value that the driver supports for some value of SQL_ATTR_CURSOR_TYPE but not for the current value of SQL_ATTR_CURSOR_TYPE, the value of SQL_ATTR_CURSOR_TYPE will be changed at execution time and SQLSTATE 01S02 (Option value changed) is issued when <B>SQLExecDirect</B> or <B>SQLPrepare</B> is called.</P>
+#
+# <P>If the specified concurrency is not supported by the data source, the driver substitutes a different concurrency and returns SQLSTATE 01S02 (Option value changed). For SQL_CONCUR_VALUES, the driver substitutes SQL_CONCUR_ROWVER, and vice versa. For SQL_CONCUR_LOCK, the driver substitutes, in order, SQL_CONCUR_ROWVER or SQL_CONCUR_VALUES. The validity of the substituted value is not checked until execution time.</P>
+#
+# <P>For more information about the relationship between SQL_ATTR_CONCURRENCY and the other cursor attributes, see "<A HREF="odbccursor_characteristics_and_cursor_type.htm">Cursor Characteristics and Cursor Type</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CONCURRENCY => {
+ type => q(SQLUINTEGER),
+ ptr => undef,
+ value => [ qw(SQL_CONCUR_READ_ONLY SQL_CONCUR_LOCK SQL_CONCUR_ROWVER SQL_CONCUR_ROWVER) ],
+ default => q(SQL_CONCUR_READ_ONLY),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CURSOR_SCROLLABLE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies the level of support that the application requires. Setting this attribute affects subsequent calls to <B>SQLExecDirect</B> and <B>SQLExecute</B>.
+# <P>SQL_NONSCROLLABLE = Scrollable cursors are not required on the statement handle. If the application calls <B>SQLFetchScroll</B> on this handle, the only valid value of <I>FetchOrientation</I> is SQL_FETCH_NEXT. This is the default.</P>
+#
+# <P>SQL_SCROLLABLE = Scrollable cursors are required on the statement handle. When calling <B>SQLFetchScroll</B>, the application may specify any valid value of <I>FetchOrientation</I>, achieving cursor positioning in modes other than the sequential mode. </P>
+#
+# <P>For more information about scrollable cursors, see "<A HREF="odbcscrollable_cursors.htm">Scrollable Cursors</A>" in Chapter 11: Retrieving Results (Advanced). For more information about the relationship between SQL_ATTR_CURSOR_SCROLLABLE and the other cursor attributes, see "<A HREF="odbccursor_characteristics_and_cursor_type.htm">Cursor Characteristics and Cursor Type</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CURSOR_SCROLLABLE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_NONSCROLLABLE SQL_SCROLLABLE) ],
+ default => q(SQL_NONSCROLLABLE),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CURSOR_SENSITIVITY<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether cursors on the statement handle make visible the changes made to a result set by another cursor. Setting this attribute affects subsequent calls to <B>SQLExecDirect</B> and <B>SQLExecute</B>. An application can read back the value of this attribute to obtain its initial state or its state as most recently set by the application.
+# <P>SQL_UNSPECIFIED = It is unspecified what the cursor type is and whether cursors on the statement handle make visible the changes made to a result set by another cursor. Cursors on the statement handle may make visible none, some, or all such changes. This is the default.</P>
+#
+# <P>SQL_INSENSITIVE = All cursors on the statement handle show the result set without reflecting any changes made to it by any other cursor. Insensitive cursors are read-only. This corresponds to a static cursor, which has a concurrency that is read-only.</P>
+#
+# <P>SQL_SENSITIVE = All cursors on the statement handle make visible all changes made to a result set by another cursor. </P>
+#
+# <P>For more information about the relationship between SQL_ATTR_CURSOR_SENSITIVITY and the other cursor attributes, see "<A HREF="odbccursor_characteristics_and_cursor_type.htm">Cursor Characteristics and Cursor Type</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CURSOR_SENSITIVITY => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_UNSPECIFIED SQL_INSENSITIVE SQL_SENSITIVE) ],
+ default => q(SQL_UNSPECIFIED),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_CURSOR_TYPE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies the cursor type:
+# <P>SQL_CURSOR_FORWARD_ONLY = The cursor only scrolls forward.</P>
+#
+# <P>SQL_CURSOR_STATIC = The data in the result set is static.</P>
+#
+# <P>SQL_CURSOR_KEYSET_DRIVEN = The driver saves and uses the keys for the number of rows specified in the SQL_ATTR_KEYSET_SIZE statement attribute.</P>
+#
+# <P>SQL_CURSOR_DYNAMIC = The driver saves and uses only the keys for the rows in the rowset.</P>
+#
+# <P>The default value is SQL_CURSOR_FORWARD_ONLY. This attribute cannot be specified after the SQL statement has been prepared.</P>
+#
+# <P>If the specified cursor type is not supported by the data source, the driver substitutes a different cursor type and returns SQLSTATE 01S02 (Option value changed). For a mixed or dynamic cursor, the driver substitutes, in order, a keyset-driven or static cursor. For a keyset-driven cursor, the driver substitutes a static cursor. </P>
+#
+# <P>For more information about scrollable cursor types, see "<A HREF="odbcscrollable_cursor_types.htm">Scrollable Cursor Types</A>" in Chapter 11: Retrieving Results (Advanced). For more information about the relationship between SQL_ATTR_CURSOR_TYPE and the other cursor attributes, see "<A HREF="odbccursor_characteristics_and_cursor_type.htm">Cursor Characteristics and Cursor Type</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_CURSOR_TYPE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_CURSOR_FORWARD_ONLY SQL_CURSOR_STATIC SQL_CURSOR_KEYSET_DRIVEN SQL_CURSOR_DYNAMIC) ],
+ default => q(SQL_CURSOR_FORWARD_ONLY),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ENABLE_AUTO_IPD<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether automatic population of the IPD is performed:
+# <P>SQL_TRUE = Turns on automatic population of the IPD after a call to <B>SQLPrepare</B>. SQL_FALSE = Turns off automatic population of the IPD after a call to <B>SQLPrepare</B>. (An application can still obtain IPD field information by calling <B>SQLDescribeParam</B>, if supported.) The default value of the statement attribute SQL_ATTR_ENABLE_AUTO_IPD is SQL_FALSE. For more information, see "<A HREF="odbcautomatic_population_of_the_ipd.htm">Automatic Population of the IPD</A>" in Chapter 13: Descriptors.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ENABLE_AUTO_IPD => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_FALSE SQL_TRUE) ],
+ default => q(SQL_FALSE),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_FETCH_BOOKMARK_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>A pointer that points to a binary bookmark value. When <B>SQLFetchScroll</B> is called with <I>fFetchOrientation</I> equal to SQL_FETCH_BOOKMARK, the driver picks up the bookmark value from this field. This field defaults to a null pointer. For more information, see "<A HREF="odbcscrolling_by_bookmark.htm">Scrolling by Bookmark</A>" in Chapter 11: Retrieving Results (Advanced).
+# <P>The value pointed to by this field is not used for delete by bookmark, update by bookmark, or fetch by bookmark operations in <B>SQLBulkOperations</B>, which use bookmarks cached in rowset buffers.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_FETCH_BOOKMARK_PTR => {
+ type => q(SQLPOINTER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_IMP_PARAM_DESC<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>The handle to the IPD. The value of this attribute is the descriptor allocated when the statement was initially allocated. The application cannot set this attribute.
+# <P>This attribute can be retrieved by a call to <B>SQLGetStmtAttr</B> but not set by a call to <B>SQLSetStmtAttr</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_IMP_PARAM_DESC => {
+ type => q(SQLPOINTER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'ro',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_IMP_ROW_DESC<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>The handle to the IRD. The value of this attribute is the descriptor allocated when the statement was initially allocated. The application cannot set this attribute.
+# <P>This attribute can be retrieved by a call to <B>SQLGetStmtAttr</B> but not set by a call to <B>SQLSetStmtAttr</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_IMP_ROW_DESC => {
+ type => q(SQLPOINTER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'ro',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_KEYSET_SIZE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER that specifies the number of rows in the keyset for a keyset-driven cursor. If the keyset size is 0 (the default), the cursor is fully keyset-driven. If the keyset size is greater than 0, the cursor is mixed (keyset-driven within the keyset and dynamic outside of the keyset). The default keyset size is 0. For more information about keyset-driven cursors, see "<A HREF="odbckeyset_driven_cursors.htm">Keyset-Driven Cursors</A>" in Chapter 11: Retrieving Results (Advanced).
+# <P>If the specified size exceeds the maximum keyset size, the driver substitutes that size and returns SQLSTATE 01S02 (Option value changed).</P>
+#
+# <P><B>SQLFetch</B> or <B>SQLFetchScroll</B> returns an error if the keyset size is greater than 0 and less than the rowset size.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_KEYSET_SIZE => {
+ type => q(SQLUINTEGER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_MAX_LENGTH<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies the maximum amount of data that the driver returns from a character or binary column. If <I>ValuePtr</I> is less than the length of the available data, <B>SQLFetch</B> or <B>SQLGetData</B> truncates the data and returns SQL_SUCCESS. If <I>ValuePtr</I> is 0 (the default), the driver attempts to return all available data.
+# <P>If the specified length is less than the minimum amount of data that the data source can return or greater than the maximum amount of data that the data source can return, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).</P>
+#
+# <P>The value of this attribute can be set on an open cursor; however, the setting might not take effect immediately, in which case the driver will return SQLSTATE 01S02 (Option value changed) and reset the attribute to its original value.</P>
+#
+# <P>This attribute is intended to reduce network traffic and should be supported only when the data source (as opposed to the driver) in a multiple-tier driver can implement it. This mechanism should not be used by applications to truncate data; to truncate data received, an application should specify the maximum buffer length in the <I>BufferLength </I>argument in <B>SQLBindCol</B> or <B>SQLGetData</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_MAX_LENGTH => {
+ type => q(SQLUINTEGER),
+ ptr => undef,
+ value => undef,
+ default => 0,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_MAX_ROWS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value corresponding to the maximum number of rows to return to the application for a <B>SELECT</B> statement. If *<I>ValuePtr</I> equals 0 (the default), the driver returns all rows.
+# <P>This attribute is intended to reduce network traffic. Conceptually, it is applied when the result set is created and limits the result set to the first <I>ValuePtr</I> rows. If the number of rows in the result set is greater than <I>ValuePtr</I>, the result set is truncated. </P>
+#
+# <P>SQL_ATTR_MAX_ROWS applies to all result sets on the <I>Statement</I>, including those returned by catalog functions. SQL_ATTR_MAX_ROWS establishes a maximum for the value of the cursor row count.</P>
+#
+# <P>A driver should not emulate SQL_ATTR_MAX_ROWS behavior for <B>SQLFetch</B> or <B>SQLFetchScroll</B> (if result set size limitations cannot be implemented at the data source) if it cannot guarantee that SQL_ATTR_MAX_ROWS will be implemented properly.</P>
+#
+# <P>It is driver-defined whether SQL_ATTR_MAX_ROWS applies to statements other than SELECT statements (such as catalog functions). </P>
+#
+# <P>The value of this attribute can be set on an open cursor; however, the setting might not take effect immediately, in which case the driver will return SQLSTATE 01S02 (Option value changed) and reset the attribute to its original value.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_MAX_ROWS => {
+ type => q(SQLUINTEGER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_METADATA_ID<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that determines how the string arguments of catalog functions are treated.
+# <P>If SQL_TRUE, the string argument of catalog functions are treated as identifiers. The case is not significant. For nondelimited strings, the driver removes any trailing spaces and the string is folded to uppercase. For delimited strings, the driver removes any leading or trailing spaces and takes whatever is between the delimiters literally. If one of these arguments is set to a null pointer, the function returns SQL_ERROR and SQLSTATE HY009 (Invalid use of null pointer). </P>
+#
+# <P>If SQL_FALSE, the string arguments of catalog functions are not treated as identifiers. The case is significant. They can either contain a string search pattern or not, depending on the argument.</P>
+#
+# <P>The default value is SQL_FALSE.</P>
+#
+# <P>The <I>TableType</I> argument of <B>SQLTables</B>, which takes a list of values, is not affected by this attribute.</P>
+#
+# <P>SQL_ATTR_METADATA_ID can also be set on the connection level. (It and SQL_ATTR_ASYNC_ENABLE are the only statement attributes that are also connection attributes.)</P>
+#
+# <P>For more information, see "<A HREF="odbcarguments_in_catalog_functions.htm">Arguments in Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_METADATA_ID => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_FALSE SQL_TRUE) ],
+ default => q(SQL_FALSE),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_NOSCAN<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that indicates whether the driver should scan SQL strings for escape sequences:
+# <P>SQL_NOSCAN_OFF = The driver scans SQL strings for escape sequences (the default).</P>
+#
+# <P>SQL_NOSCAN_ON = The driver does not scan SQL strings for escape sequences. Instead, the driver sends the statement directly to the data source.</P>
+#
+# <P>For more information, see "<A HREF="odbcescape_sequences_in_odbc.htm">Escape Sequences in ODBC</A>" in Chapter 8: SQL Statements.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_NOSCAN => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_NOSCAN_OFF SQL_NOSCAN_ON) ],
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAM_BIND_OFFSET_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER * value that points to an offset added to pointers to change binding of dynamic parameters. If this field is non-null, the driver dereferences the pointer, adds the dereferenced value to each of the deferred fields in the descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR), and uses the new pointer values when binding. It is set to null by default.
+# <P>The bind offset is always added directly to the SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR fields. If the offset is changed to a different value, the new value is still added directly to the value in the descriptor field. The new offset is not added to the field value plus any earlier offsets.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PARAM_BIND_OFFSET_PTR => {
+ type => q(SQLUINTEGER),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAM_BIND_OFFSET_PTR<BR>
+# (ODBC 3.0) (<I>continued</I>)</TD>
+# <TD width=62%>For more information, see "<A HREF="odbcparameter_binding_offsets.htm">Parameter Binding Offsets</A>" in Chapter 9: Executing Statements.
+# <P>Setting this statement attribute sets the SQL_DESC_BIND_OFFSET_PTR field in the APD header.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAM_BIND_TYPE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that indicates the binding orientation to be used for dynamic parameters.
+# <P>This field is set to SQL_PARAM_BIND_BY_COLUMN (the default) to select column-wise binding. </P>
+#
+# <P>To select row-wise binding, this field is set to the length of the structure or an instance of a buffer that will be bound to a set of dynamic parameters. This length must include space for all of the bound parameters and any padding of the structure or buffer to ensure that when the address of a bound parameter is incremented with the specified length, the result will point to the beginning of the same parameter in the next set of parameters. When using the <I>sizeof</I> operator in ANSI C, this behavior is guaranteed.</P>
+#
+# <P>For more information, see "<A HREF="odbcbinding_arrays_of_parameters.htm">Binding Arrays of Parameters</A>" in Chapter 9: Executing Statements.</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ BIND_TYPE field in the APD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PARAM_BIND_TYPE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAM_OPERATION_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values used to ignore a parameter during execution of an SQL statement. Each value is set to either SQL_PARAM_PROCEED (for the parameter to be executed) or SQL_PARAM_IGNORE (for the parameter to be ignored).
+# <P>A set of parameters can be ignored during processing by setting the status value in the array pointed to by SQL_DESC_ARRAY_STATUS_PTR in the APD to SQL_PARAM_IGNORE. A set of parameters is processed if its status value is set to SQL_PARAM_PROCEED or if no elements in the array are set.</P>
+#
+# <P>This statement attribute can be set to a null pointer, in which case the driver does not return parameter status values. This attribute can be set at any time, but the new value is not used until the next time <B>SQLExecDirect</B> or <B>SQLExecute</B> is called.</P>
+#
+# <P>For more information, see "<A HREF="odbcusing_arrays_of_parameters.htm">Using Arrays of Parameters</A>" in Chapter 9: Executing Statements.</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the APD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PARAM_OPERATION_PTR => {
+ type => q(SQLUSMALLINT),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAM_STATUS_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values containing status information for each row of parameter values after a call to <B>SQLExecute</B> or <B>SQLExecDirect</B>. This field is required only if PARAMSET_SIZE is greater than 1.
+# <P>The status values can contain the following values:</P>
+#
+# <P>SQL_PARAM_SUCCESS: The SQL statement was successfully executed for this set of parameters.</P>
+#
+# <P>SQL_PARAM_SUCCESS_WITH_INFO: The SQL statement was successfully executed for this set of parameters; however, warning information is available in the diagnostics data structure.</P>
+#
+# <P>SQL_PARAM_ERROR: There was an error in processing this set of parameters. Additional error information is available in the diagnostics data structure.</P>
+#
+# <P>SQL_PARAM_UNUSED: This parameter set was unused, possibly due to the fact that some previous parameter set caused an error that aborted further processing, or because SQL_PARAM_IGNORE was set for that set of parameters in the array specified by the SQL_ATTR_PARAM_OPERATION_PTR.</P>
+#
+# <P>SQL_PARAM_DIAG_UNAVAILABLE: The driver treats arrays of parameters as a monolithic unit and so does not generate this level of error information. </P>
+#
+# <P>This statement attribute can be set to a null pointer, in which case the driver does not return parameter status values. This attribute can be set at any time, but the new value is not used until the next time <B>SQLExecute</B> or <B>SQLExecDirect</B> is called. Note that setting this attribute can affect the output parameter behavior implemented by the driver.</P>
+#
+# <P>For more information, see "<A HREF="odbcusing_arrays_of_parameters.htm">Using Arrays of Parameters</A>" in Chapter 9: Executing Statements.</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the IPD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PARAM_STATUS_PTR => {
+ type => q(SQLUSMALLINT),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAMS_PROCESSED_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER * record field that points to a buffer in which to return the number of sets of parameters that have been processed, including error sets. No number will be returned if this is a null pointer.
+# <P>Setting this statement attribute sets the SQL_DESC_ROWS_PROCESSED_PTR field in the IPD header.</P>
+#
+# <P>If the call to <B>SQLExecDirect</B> or <B>SQLExecute</B> that fills in the buffer pointed to by this attribute does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.</P>
+#
+# <P>For more information, see "<A HREF="odbcusing_arrays_of_parameters.htm">Using Arrays of Parameters</A>" in Chapter 9: Executing Statements.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PARAMS_PROCESSED_PTR => {
+ type => q(SQLUINTEGER),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_PARAMSET_SIZE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies the number of values for each parameter. If SQL_ATTR_PARAMSET_SIZE is greater than 1, SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR of the APD point to arrays. The cardinality of each array is equal to the value of this field.
+# <P>For more information, see "<A HREF="odbcusing_arrays_of_parameters.htm">Using Arrays of Parameters</A>" in Chapter 9: Executing Statements.</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ARRAY_SIZE field in the APD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_PARAMSET_SIZE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_QUERY_TIMEOUT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value corresponding to the number of seconds to wait for an SQL statement to execute before returning to the application. If <I>ValuePtr</I> is equal to 0 (default), there is no timeout.
+# <P>If the specified timeout exceeds the maximum timeout in the data source or is smaller than the minimum timeout, <B>SQLSetStmtAttr</B> substitutes that value and returns SQLSTATE 01S02 (Option value changed).</P>
+#
+# <P>Note that the application need not call <B>SQLCloseCursor</B> to reuse the statement if a <B>SELECT</B> statement timed out.</P>
+#
+# <P>The query timeout set in this statement attribute is valid in both synchronous and asynchronous modes.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_QUERY_TIMEOUT => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_RETRIEVE_DATA<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value:
+# <P>SQL_RD_ON = <B>SQLFetchScroll</B> and, in ODBC 3<I>.x</I>, <B>SQLFetch</B> retrieve data after it positions the cursor to the specified location. This is the default.</P>
+#
+# <P>SQL_RD_OFF = <B>SQLFetchScroll</B> and, in ODBC 3<I>.x</I>, <B>SQLFetch</B> do not retrieve data after it positions the cursor.</P>
+#
+# <P>By setting SQL_RETRIEVE_DATA to SQL_RD_OFF, an application can verify that a row exists or retrieve a bookmark for the row without incurring the overhead of retrieving rows. For more information, see "<A HREF="odbcscrolling_and_fetching_rows.htm">Scrolling and Fetching Rows</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+#
+# <P>The value of this attribute can be set on an open cursor; however, the setting might not take effect immediately, in which case the driver will return SQLSTATE 01S02 (Option value changed) and reset the attribute to its original value.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_RETRIEVE_DATA => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_RD_ON SQL_RD_OFF) ],
+ default => q(SQL_RD_ON),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROW_ARRAY_SIZE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies the number of rows returned by each call to <B>SQLFetch</B> or <B>SQLFetchScroll</B>. It is also the number of rows in a bookmark array used in a bulk bookmark operation in <B>SQLBulkOperations</B>. The default value is 1.
+# <P>If the specified rowset size exceeds the maximum rowset size supported by the data source, the driver substitutes that value and returns SQLSTATE 01S02 (Option value changed).</P>
+#
+# <P>For more information, see "<A HREF="odbcrowset_size.htm">Rowset Size</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ARRAY_SIZE field in the ARD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROW_ARRAY_SIZE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROW_BIND_OFFSET_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER * value that points to an offset added to pointers to change binding of column data. If this field is non-null, the driver dereferences the pointer, adds the dereferenced value to each of the deferred fields in the descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and SQL_DESC_OCTET_LENGTH_PTR), and uses the new pointer values when binding. It is set to null by default.
+# <P>Setting this statement attribute sets the SQL_DESC_BIND_OFFSET_PTR field in the ARD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROW_BIND_OFFSET_PTR => {
+ type => q(SQLUINTEGER),
+ ptr => undef,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROW_BIND_TYPE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that sets the binding orientation to be used when <B>SQLFetch</B> or <B>SQLFetchScroll</B> is called on the associated statement. Column-wise binding is selected by setting the value to SQL_BIND_BY_COLUMN. Row-wise binding is selected by setting the value to the length of a structure or an instance of a buffer into which result columns will be bound.
+# <P>If a length is specified, it must include space for all of the bound columns and any padding of the structure or buffer to ensure that when the address of a bound column is incremented with the specified length, the result will point to the beginning of the same column in the next row. When using the <B>sizeof</B> operator with structures or unions in ANSI C, this behavior is guaranteed.</P>
+#
+# <P>Column-wise binding is the default binding orientation for <B>SQLFetch</B> and <B>SQLFetchScroll</B>.</P>
+#
+# <P>For more information, see "<A HREF="odbcbinding_columns_for_use_with_block_cursors.htm">Binding Columns for Use with Block Cursors</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_BIND_TYPE field in the ARD header.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROW_BIND_TYPE => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_BIND_BY_COLUMN etc) ],
+ default => q(SQL_BIND_BY_COLUMN),
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROW_NUMBER<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that is the number of the current row in the entire result set. If the number of the current row cannot be determined or there is no current row, the driver returns 0.
+# <P>This attribute can be retrieved by a call to <B>SQLGetStmtAttr</B> but not set by a call to <B>SQLSetStmtAttr</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROW_NUMBER => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROW_OPERATION_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values used to ignore a row during a bulk operation using <B>SQLSetPos</B>. Each value is set to either SQL_ROW_PROCEED (for the row to be included in the bulk operation) or SQL_ROW_IGNORE (for the row to be excluded from the bulk operation). (Rows cannot be ignored by using this array during calls to <B>SQLBulkOperations</B>.)
+# <P>This statement attribute can be set to a null pointer, in which case the driver does not return row status values. This attribute can be set at any time, but the new value is not used until the next time <B>SQLSetPos</B> is called.</P>
+#
+# <P>For more information, see "<A HREF="odbcupdating_rows_in_the_rowset_with_sqlsetpos.htm">Updating Rows in the Rowset with SQLSetPos</A>" and "<A HREF="odbcdeleting_rows_in_the_rowset_with_sqlsetpos.htm">Deleting Rows in the Rowset with SQLSetPos</A>" in Chapter 12: Updating Data.</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the ARD.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROW_OPERATION_PTR => {
+ type => q(SQLUSMALLINT),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROW_STATUS_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values containing row status values after a call to <B>SQLFetch</B> or <B>SQLFetchScroll</B>. The array has as many elements as there are rows in the rowset.
+# <P>This statement attribute can be set to a null pointer, in which case the driver does not return row status values. This attribute can be set at any time, but the new value is not used until the next time <B>SQLBulkOperations</B>, <B>SQLFetch</B>, <B>SQLFetchScroll</B>, or <B>SQLSetPos</B> is called.</P>
+#
+# <P>For more information, see "<A HREF="odbcnumber_of_rows_fetched_and_status.htm">Number of Rows Fetched and Status</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the IRD header.</P>
+#
+# <P>This attribute is mapped by an ODBC 2<I>.x</I> driver to the <I>rgbRowStatus</I> array in a call to <B>SQLExtendedFetch</B>.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROW_STATUS_PTR => {
+ type => q(SQLUSMALLINT),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_ROWS_FETCHED_PTR<BR>
+# (ODBC 3.0)</TD>
+# <TD width=62%>An SQLUINTEGER * value that points to a buffer in which to return the number of rows fetched after a call to <B>SQLFetch</B> or <B>SQLFetchScroll</B>; the number of rows affected by a bulk operation performed by a call to <B>SQLSetPos</B> with an <I>Operation</I> argument of SQL_REFRESH; or the number of rows affected by a bulk operation performed by <B>SQLBulkOperations</B>. This number includes error rows.
+# <P>For more information, see "<A HREF="odbcnumber_of_rows_fetched_and_status.htm">Number of Rows Fetched and Status</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+#
+# <P>Setting this statement attribute sets the SQL_DESC_ROWS_PROCESSED_PTR field in the IRD header. </P>
+#
+# <P>If the call to <B>SQLFetch</B> or <B>SQLFetchScroll</B> that fills in the buffer pointed to by this attribute does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO, the contents of the buffer are undefined.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_ROWS_FETCHED_PTR => {
+ type => q(SQLUINTEGER),
+ ptr => 1,
+ value => undef,
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_SIMULATE_CURSOR<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether drivers that simulate positioned update and delete statements guarantee that such statements affect only one single row.
+# <P>To simulate positioned update and delete statements, most drivers construct a searched <B>UPDATE</B> or <B>DELETE</B> statement containing a <B>WHERE</B> clause that specifies the value of each column in the current row. Unless these columns make up a unique key, such a statement can affect more than one row.</P>
+#
+# <P>To guarantee that such statements affect only one row, the driver determines the columns in a unique key and adds these columns to the result set. If an application guarantees that the columns in the result set make up a unique key, the driver is not required to do so. This may reduce execution time.</P>
+#
+# <P>SQL_SC_NON_UNIQUE = The driver does not guarantee that simulated positioned update or delete statements will affect only one row; it is the application's responsibility to do so. If a statement affects more than one row, <B>SQLExecute</B>, <B>SQLExecDirect</B>, or <B>SQLSetPos</B> returns SQLSTATE 01001 (Cursor operation conflict).</P>
+#
+# <P>SQL_SC_TRY_UNIQUE = The driver attempts to guarantee that simulated positioned update or delete statements affect only one row. The driver always executes such statements, even if they might affect more than one row, such as when there is no unique key. If a statement affects more than one row, <B>SQLExecute</B>, <B>SQLExecDirect</B>, or <B>SQLSetPos</B> returns SQLSTATE 01001 (Cursor operation conflict).</P>
+#
+# <P>SQL_SC_UNIQUE = The driver guarantees that simulated positioned update or delete statements affect only one row. If the driver cannot guarantee this for a given statement, <B>SQLExecDirect</B> or <B>SQLPrepare</B> returns an error.</P>
+#
+# <P>If the data source provides native SQL support for positioned update and delete statements and the driver does not simulate cursors, SQL_SUCCESS is returned when SQL_SC_UNIQUE is requested for SQL_SIMULATE_CURSOR. SQL_SUCCESS_WITH_INFO is returned if SQL_SC_TRY_UNIQUE or SQL_SC_NON_UNIQUE is requested. If the data source provides the SQL_SC_TRY_UNIQUE level of support and the driver does not, SQL_SUCCESS is returned for SQL_SC_TRY_UNIQUE and SQL_SUCCESS_WITH_INFO is returned for SQL_SC_NON_UNIQUE.</P>
+#
+# <P>If the specified cursor simulation type is not supported by the data source, the driver substitutes a different simulation type and returns SQLSTATE 01S02 (Option value changed). For SQL_SC_UNIQUE, the driver substitutes, in order, SQL_SC_TRY_UNIQUE or SQL_SC_NON_UNIQUE. For SQL_SC_TRY_UNIQUE, the driver substitutes SQL_SC_NON_UNIQUE.</P>
+#
+# <P>For more information, see "<A HREF="odbcsimulating_positioned_update_and_delete_statements.htm">Simulating Positioned Update and Delete Statements</A>" in Chapter 12: Updating Data.</P>
+# </TD>
+# </TR>
+ SQL_ATTR_SIMULATE_CURSOR => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_SC_NON_UNIQUE SQL_SC_TRY_UNIQUE SQL_SC_UNIQUE) ],
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=38%>SQL_ATTR_USE_BOOKMARKS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=62%>An SQLUINTEGER value that specifies whether an application will use bookmarks with a cursor:
+# <P>SQL_UB_OFF = Off (the default)</P>
+#
+# <P>SQL_UB_VARIABLE = An application will use bookmarks with a cursor, and the driver will provide variable-length bookmarks if they are supported. SQL_UB_FIXED is deprecated in ODBC 3<I>.x</I>. ODBC 3<I>.x</I> applications should always use variable-length bookmarks, even when working with ODBC 2<I>.x</I> drivers (which supported only 4-byte, fixed-length bookmarks). This is because a fixed-length bookmark is just a special case of a variable-length bookmark. When working with an ODBC 2<I>.x</I> driver, the Driver Manager maps SQL_UB_VARIABLE to SQL_UB_FIXED.</P>
+#
+# <P>To use bookmarks with a cursor, the application must specify this attribute with the SQL_UB_VARIABLE value before opening the cursor.</P>
+#
+# <P>For more information, see "<A HREF="odbcretrieving_bookmarks.htm">Retrieving Bookmarks</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+# </TD>
+# </TR>
+ SQL_ATTR_USE_BOOKMARKS => {
+ type => q(SQLUINTEGER),
+ ptr => 0,
+ value => [ qw(SQL_UB_OFF SQL_UB_VARIABLE SQL_UB_FIXED) ],
+ default => undef,
+ mode => 'rw',
+ order => ++$order,
+ },
+# </table></div>
+#
+# <P class="fineprint">[1]&nbsp;&nbsp;&nbsp;These functions can be called asynchronously only if the descriptor is an implementation descriptor, not an application descriptor.</p>
+# <P>See "<A HREF="odbccolumn_wise_binding.htm">Column-Wise Binding</A>" and "<A HREF="odbcrow_wise_binding.htm">Row-Wise Binding</A>" in Chapter 11: Retrieving Results (Advanced).</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=47%>For information about</TH>
+# <TH width=53%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=47%>Canceling statement processing</TD>
+# <TD width=53%><A HREF="odbcsqlcancel.htm">SQLCancel</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=47%>Returning the setting of a connection attribute</TD>
+# <TD width=53%><A HREF="odbcsqlgetconnectattr.htm">SQLGetConnectAttr</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=47%>Returning the setting of a statement attribute</TD>
+# <TD width=53%><A HREF="odbcsqlgetstmtattr.htm">SQLGetStmtAttr</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=47%>Setting a connection attribute</TD>
+# <TD width=53%><A HREF="odbcsqlsetconnectattr.htm">SQLSetConnectAttr</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=47%>Setting a single field of the descriptor</TD>
+# <TD width=53%><A HREF="odbcsqlsetdescfield.htm">SQLSetDescField</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+};
+
+my $i3 = " " x 3;
+my $i4 = " " x 4;
+my $i8 = " " x 8;
+
+my $type2type = {
+ SQLSMALLINT => 'Smallint',
+ SQLUSMALLINT => 'Usmallint',
+ SQLINTEGER => 'Integer',
+ SQLUINTEGER => 'Uinteger',
+ SQLPOINTER => 'Pointer',
+ SQLCHAR => 'Sqlchar',
+};
+
+my $attr =
+ $type eq 'Env' ? $attrEnv :
+ $type eq 'Dbc' ? $attrDbc :
+ $type eq 'Stmt' ? $attrStmt : die "bad type $type";
+
+my @name = sort {
+ $attr->{$a}{order} <=> $attr->{$b}{order}
+} keys %$attr;
+
+print "#include \"Handle$type.hpp\"\n";
+my $class = "OdbcData";
+
+for my $name (@name) {
+ my $p = $attr->{$name};
+ my $odbctype = $type2type->{$p->{type}} or die $name;
+ $odbctype .= "Ptr" if $p->{ptr};
+ print "\nstatic void\n";
+ print "callback_${name}_set(Ctx& ctx, HandleBase* self, const $class& data)\n";
+ print "{\n";
+ print "${i4}Handle$type* p$type = dynamic_cast<Handle$type*>(self);\n";
+ print "${i4}assert(p$type != 0 && data.type() == ${class}::$odbctype);\n";
+ print "}\n";
+ print "\nstatic void\n";
+ print "callback_${name}_default(Ctx& ctx, HandleBase* self, $class& data)\n";
+ print "{\n";
+ print "${i4}Handle$type* p$type = dynamic_cast<Handle$type*>(self);\n";
+ print "${i4}assert(p$type != 0);\n";
+ print "${i4}data.set();\n";
+ print "}\n";
+}
+
+print "\nAttrSpec Handle${type}::m_attrSpec\[\] = {\n";
+for my $name (@name) {
+ my $p = $attr->{$name};
+ my $odbctype = $type2type->{$p->{type}} or die $name;
+ $odbctype .= "Ptr" if $p->{ptr};
+ print "${i4}\{${i3}$name,\n";
+ print "${i8}${class}::$odbctype,\n";
+ my $attrmode =
+ $p->{mode} eq 'rw' ? 'Attr_mode_readwrite' :
+ $p->{mode} eq 'ro' ? 'Attr_mode_readonly' : die "bad mode $p->{mode}";
+ print "${i8}$attrmode,\n";
+ print "${i8}callback_${name}_set,\n";
+ print "${i8}callback_${name}_default,\n";
+ print "${i4}\},\n";
+}
+print "${i4}\{${i3}0,\n";
+print "${i8}${class}::Undef,\n";
+print "${i8}Attr_mode_undef,\n";
+print "${i8}0,\n";
+print "${i8}0,\n";
+print "${i4}\},\n";
+
+print "};\n";
diff --git a/storage/ndb/src/old_files/client/odbc/docs/main.hpp b/storage/ndb/src/old_files/client/odbc/docs/main.hpp
new file mode 100644
index 00000000000..ebb5b1f235a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/main.hpp
@@ -0,0 +1,104 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+/**
+ @mainpage NDB ODBC
+
+ The ODBC Driver Frontend has:
+ -# HandleBase : Various ODBC handles
+ -# AttrArea : Attributes of handles
+ -# ConnArea : Communication area on connection level between driver parts
+ -# StmtArea : Communication area on statement level between driver parts
+
+ and controls the following steps:
+ -# SQL_compiler : Compiles SQL into SQL_code_tree:s
+ -# Parser : Bison grammar
+ -# Analyzer : Syntactic and semantic checks (binds names)
+ -# PlanGen : Generate initial (execution) plan (PlanTree)
+ -# CodeGen : Generates CodeTree:s out of PlanTree:s
+ -# Optimizer : Optimizes PlanTree:s
+ -# Output : Outputs executable CodeTree:s
+ -# Executor : Executes CodeTree:s
+ -# CodeTree::allocRun : Allocates runtime data structures (RunTree:s)
+ -# Dataflow machine : Executes and evaluates statement and expressions
+
+ The Dataflow machine works in four different ways:
+ -# Non-query statements
+ -# CodeStmt::execute : Executes (non-query) statement
+ -# Query statements
+ -# CodeQuery::execute : Execute Query statement
+ -# CodeQuery::fetch : Fetch row from CodeQuery node
+ -# Boolean expressions
+ -# CodePred::evaluate : Evaluates boolean expression
+ -# Arithmetical expressions
+ -# CodeExpr::evaluate : Evaluates arithmetic expression
+
+ The following components are used throughout the NDB ODBC:
+ -# Context (Ctx) : Info regarding execution/evaluation
+ -# Diagnostic area (DiagArea) : Errors and warnings (for ODBC user)
+ -# DescArea : Description of ODBC user input/output bind varibles/columns
+ -# Dictionary (DictBase) : Lookup info stored in NDB Dictionary
+ and info regarding temporary
+ materialized results
+ -# ResultArea : Execution (temporary) results
+
+
+ @section secCompiler SQL_compiler : SQL to SQL_code_tree
+
+ The SQL_compiler takes an <em>SQL statement</em> and translates
+ it into an SQL_code_tree. The compiler uses an SQL_code_tree
+ internally during the compilation and the result of the compilation
+ is a simlified SQL_code_tree.
+
+ The compiler works in the following steps:
+ -# Parse SQL statments and create SQL_code_tree representing the
+ statement.
+ -# Apply Syntax Rules to the SQL_code_tree. Syntax rules are
+ rules which are <em>not</em> expressed in the SQL grammar,
+ but are expressed in natural language in the SQL specification.
+ -# Apply Access Rules to the SQL_code_tree
+ (this is not implemented, since NDB Cluster has no access control)
+ -# Apply General Rules to the SQL_code_tree
+ -# Apply Conformance Rules to the SQL_code_tree
+
+ The resulting simplified SQL_code_tree is represented by a
+ tree of C++ objects.
+
+
+ @section secCodegen Codegen : SQL_code_tree to CodeTree
+
+ CodeGen takes simplified SQL_code_tree:s and transforms them into
+ CodeTree:s.
+
+
+ @section secOptimizer Optimizer : CodeTree to CodeTree
+
+ The implementation of the ODBC optimizer will uses the
+ PlanTree:s to represent statements and transforms them
+ into executable format (still PlanTree format).
+
+ @note In the future, more optimizations can be implemented.
+
+
+ @section secExecutor Executor : Execute CodeTree
+
+ The Executor uses the following data structures:
+ -# CodeTree : A read-only quary evaluation plan
+ -# RunTree : Runtime data structures containing ResultSet:s
+
+ The execution mechanism is actually implemented as a
+ part of the CodeTree.
+*/
diff --git a/storage/ndb/src/old_files/client/odbc/docs/ndbodbc.html b/storage/ndb/src/old_files/client/odbc/docs/ndbodbc.html
new file mode 100644
index 00000000000..6be624dfa1b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/ndbodbc.html
@@ -0,0 +1,659 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
+<TITLE>ODBC and SQL</TITLE>
+</HEAD>
+<BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
+
+<h2>ODBC and SQL - NDB Cluster v2.11</h2>
+
+<p>
+NDB Cluster v2.11 includes a version of ODBC and SQL.
+<p>
+This document has 4 sections.
+<ol>
+<li>PLATFORMS
+<li>ODBC
+<li>SQL
+<li>DIAGNOSTICS
+</ol>
+<p>
+Features which are currently incomplete or planned for next release
+are marked with <b>v2.x</b>.
+
+<h3>1. PLATFORMS</h3>
+
+<h4>1.1 Linux / Unix</h4>
+<p>
+We use RedHat package names to describe supporting software.
+Packages starting with <b>perl-</b> are perl modules.
+If your installation does not include them you can get them
+from a CPAN archive ( <tt><b>ftp://ftp.funet.fi/pub/languages/perl/CPAN</b></tt> ).
+<p>
+Version numbers are given only as examples.
+Other versions will work.
+<p>
+An ODBC driver manager is required, one of:
+<ul>
+<li>unixODBC-2.2.3 (this is more common)
+<li>libiodbc-3.0.5
+</ul>
+<p>
+Additional packages are convenient.
+Following include perl scripting interface
+and an "interactive SQL" tool <b>dbish</b>:
+<ul>
+<li>perl-DBI-1.30
+<li>perl-DBD-ODBC-0.43
+<li>readline-4.2
+<li>perl-Term-ReadLine-Gnu-1.11
+</ul>
+<p>
+The NDB ODBC driver is located under NDB Cluster installation
+directory and is named <tt><b>libNDB_ODBC.so</b></tt>.
+It includes NDB API.
+To use it create a text file
+<tt><b>/etc/odbc.ini</b></tt> or <tt><b>$HOME/.odbc.ini</b></tt>
+containing at least:
+<p>
+<tt>
+<b>
+[ndb]
+<br>
+Driver = &lt;path-to-your-NDB-installation&gt;/lib/libNDB_ODBC.so
+</b>
+</tt>
+<p>
+Then try the shell command <tt><b>dbish dbi:ODBC:ndb</b></tt>
+in an NDB API node directory.
+
+<h4>1.2 Windows</h4>
+
+[ TODO - documentation ]
+
+<h3>2. ODBC</h3>
+
+<h4>2.1 External data types</h4>
+
+Usual external data types are supported and converted to and from SQL
+data types.
+<p>
+<table width="80%" border=1>
+<tr align="left"><th width="40%">type</th> <th>description</th></tr>
+<tr align="left"><td>SQL_C_CHAR</td><td>character buffers</tr>
+<tr align="left"><td>SQL_C_SLONG, etc</td><td>integer types</tr>
+<tr align="left"><td>SQL_C_DOUBLE, etc</td><td>floating types</tr>
+<tr align="left"><td>SQL_C_TYPE_TIMESTAMP</td><td>timestamp</tr>
+</table>
+
+<h4>2.2 ODBC functions</h4>
+<p>
+The driver implements basic ODBC functions.
+Main exceptions are:
+<ul>
+<li>no named cursors
+<li>no scrollable cursors
+<li>no bulk operations
+<li>no asynchronous execution
+</ul>
+<p>
+Following lists main ODBC 3.0 functions and
+their status in the driver.
+<p>
+<table width="80%" border=1>
+<tr align="left"><th width="40%">function</th><th>supported</th></tr>
+<tr align="left"><td>SQLAllocHandle</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLConnect</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetInfo</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetFunctions</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetTypeInfo</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLSetConnectAttr</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetConnectAttr</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLSetEnvAttr</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetEnvAttr</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLSetStmtAttr</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetStmtAttr</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetDescField</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetDescRec</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLSetDescField</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLSetDescRec</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLPrepare</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLBindParameter</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetCursorName</td><td>
+yes, but cursor names cannot be used in SQL
+</td></tr>
+<tr align="left"><td>SQLSetCursorName</td><td>
+yes, but cursor names cannot be used in SQL
+</td></tr>
+<tr align="left"><td>SQLSetScrollOptions</td><td>
+not implemented
+</td></tr>
+<tr align="left"><td>SQLExecute</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLExecDirect</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLNativeSql</td><td>
+not implemented
+</td></tr>
+<tr align="left"><td>SQLDescribeParam</td><td>
+not supported
+</td></tr>
+<tr align="left"><td>SQLNumParams</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLParamData</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLPutData</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLRowCount</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLNumResultCols</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLDescribeCol</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLColAttribute</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLBindCol</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLFetch</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLFetchScroll</td><td>
+not implemented
+</td></tr>
+<tr align="left"><td>SQLGetData</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLSetPos</td><td>
+not implemented
+</td></tr>
+<tr align="left"><td>SQLBulkOperations</td><td>
+not implemented
+</td></tr>
+<tr align="left"><td>SQLMoreResults</td><td>
+yes, but multiple result sets are not supported
+</td></tr>
+<tr align="left"><td>SQLGetDiagField</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLGetDiagRec</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLColumnPrivileges</td><td>
+not applicable
+</td></tr>
+<tr align="left"><td>SQLColumns</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLForeignKeys</td><td>
+not applicable
+</td></tr>
+<tr align="left"><td>SQLPrimaryKeys</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLProcedureColumns</td><td>
+not applicable
+</td></tr>
+<tr align="left"><td>SQLProcedures</td><td>
+not applicable
+</td></tr>
+<tr align="left"><td>SQLSpecialColumns</td><td>
+yes <b>v2.x</b>
+</td></tr>
+<tr align="left"><td>SQLStatistics</td><td>
+not applicable
+</td></tr>
+<tr align="left"><td>SQLTablePrivileges</td><td>
+not applicable
+</td></tr>
+<tr align="left"><td>SQLTables</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLFreeStmt</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLCloseCursor</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLCancel</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLEndTran</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLDisconnect</td><td>
+yes
+</td></tr>
+<tr align="left"><td>SQLFreeHandle</td><td>
+yes
+</td></tr>
+</table>
+
+<h3>3. SQL</h3>
+
+<h4>3.1 Data types</h4>
+
+<table width="80%" border=1>
+<tr align="left"><th width="40%">type</th> <th>description</th></tr>
+<tr align="left"><td>CHAR(n)</td><td>fixed-width blank-padded string</tr>
+<tr align="left"><td>VARCHAR(n)</td><td>variable length string</tr>
+<tr align="left"><td>INT<br>INTEGER</td><td>integer 32 bits</tr>
+<tr align="left"><td>BIGINT</td><td>integer 64 bits</tr>
+<tr align="left"><td>DECIMAL(m,n)</td><td>exact number with precision and scale <b>v2.x</b></tr>
+<tr align="left"><td>REAL</td><td>float 32 bits</tr>
+<tr align="left"><td>FLOAT<br>DOUBLE PRECISION</td><td>float, at least 64 bits</tr>
+<tr align="left"><td>DATE</td><td>date with precision 1 second <b>v2.x</b></tr>
+<tr align="left"><td>DATETIME</td><td>date with precision 1 nanosecond (SQL_TYPE_TIMESTAMP)</tr>
+</table>
+<p>
+
+Integer types may be qualified as UNSIGNED.
+<br><br>
+Strings and numbers are not converted to each other automatically.
+Following is an error (unlike in oracle).
+<br>
+<pre><tt>select 123 + '456' from tab</tt></pre>
+
+<h4>3.2 Expressions</h4>
+
+<table width="80%" border=1>
+<tr align="left"><th width="40%">syntax</th> <th>description</th></tr>
+<tr align="left"><td align="center"><b>NULL</b></td><td>null value</td></tr>
+<tr align="left"><td align="center">12.34<b>e</b>5</td><td>integer or decimal or float constant</td></tr>
+<tr align="left"><td align="center"><b>'</b>abc<b>'</b></td><td>string constant</td></tr>
+<tr align="left"><td align="center"><b>+ - * / ( )</b></td><td>arithmetic operations</td></tr>
+<tr align="left"><td align="center"><b>||</b></td><td>string concatenation <b>v2.x</b></td></tr>
+</table>
+
+<br>
+Integer and decimal arithmetic is done in BIGINT.
+<br>
+Floating arithmetic is done in DOUBLE PRECISION.
+<br>
+Numeric literals use largest applicable type.
+<br>
+String operations are done in CHAR or in VARCHAR (if any operand is VARCHAR).
+<br>
+String literals have type CHAR.
+
+<h4>3.3 Functions : non-aggregate</h4>
+
+<table width="80%" border=1>
+<tr align="left"><th width="40%">syntax</th> <th>description</th></tr>
+<tr align="left"><td align="center">SUBSTR() LEFT() RIGHT()</td><td>substring</td></tr>
+<tr align="left"><td align="center">TO_NUMBER() TO_CHAR()</td><td>basic conversions <b>v2.x</b></td></tr>
+<tr align="left"><td align="center">ROWNUM</td><td>row number in query</td></tr>
+<tr align="left"><td align="center">SYSDATE</td><td>current date as DATETIME</td></tr>
+</table>
+
+<h4>3.4 Functions : aggregate</h4>
+
+<table width="80%" border=1>
+<tr align="left"><th width="40%">syntax</th> <th>description</th></tr>
+<tr align="left"><td align="center">COUNT(*) COUNT(expr)</td><td>count rows or non-NULL values</td></tr>
+<tr align="left"><td align="center">MIN(expr) MAX(expr)</td><td>min and max of strings and numbers</td></tr>
+<tr align="left"><td align="center">SUM(expr) AVG(expr)</td><td>sum and average of numbers</td></tr>
+</table>
+<br>
+GROUP BY and HAVING are supported.
+
+<h4>3.5 Predicates</h4>
+
+<table width="80%" border=1>
+<tr align="left"><th width="40%">syntax</th> <th>description</th></tr>
+<tr align="left"><td align="center">IS NULL / IS NOT NULL</td><td>test if value is null</td></tr>
+<tr align="left"><td align="center"><b>&lt; &lt;= = != &gt; &gt=</b></td><td>comparisons</td></tr>
+<tr align="left"><td align="center">LIKE / NOT LIKE</td><td>string matching</td></tr>
+<tr align="left"><td align="center">AND OR NOT <b>( )</b></td><td>boolean operators</td></tr>
+</table>
+
+<h4>3.6 Tables</h4>
+
+An NDB table requires a primary key.
+There are 2 ways to specify it.
+
+<p>
+<h4>Case 1</h4>
+<pre><tt>create table t (
+ a integer not null,
+ b char(20) not null,
+ c float,
+ primary key(a, b)
+)
+</tt></pre>
+<p>
+<h4>Case 2</h4>
+<p>
+A column can be specified as AUTO_INCREMENT.
+The column has following requirements.
+<ul>
+<li>it must be the primary key (not just part of one)
+<li>its type must be one of the integer types
+</ul>
+<pre><tt>create table t (
+ a int unsigned auto_increment primary key,
+ b char(20) not null,
+ c float
+)
+</tt></pre>
+<p>
+The values of an AUTO_INCREMENT column are unique (until wrap-around)
+and form an ascending sequence.
+Gaps in the sequence are possible.
+<h4>Default values</h4>
+Columns can be specified with DEFAULT value
+which is used on insert if the column is not on the insert list.
+<p>
+<pre><tt>create table t (
+ a int primary key,
+ b int default 100
+)
+insert into t(a) values(1) -- inserts (1,100)
+</tt></pre>
+<p>
+The value must evaluate to constant.
+Using SYSDATE (if allowed at all) evaluates to table creation time.
+<p>
+
+<h4>Logging / nologging</h4>
+
+By default tables are created in logging mode, meaning the data
+is preserved across database restart.
+The mode can be specified explicitly:
+<p>
+<tt>create table t1 (a int primary key, b int) logging</tt>
+<br>
+<tt>create table t1 (a int primary key, b int) nologging</tt>
+
+<h4>Schemas</h4>
+
+Schemas do not exist in current NDB Cluster.
+As a convenience, a single period is allowed in table names:
+<p>
+<pre><tt>create table mydb.mytable (a int primary key)</tt></pre>
+<p>
+
+<h4>Drop table</h4>
+
+Deletes a table, all of its indexes, and all data:
+<p>
+<tt>drop table t</tt>
+
+<h4>3.7 Indexes</h4>
+Only unique non-ordered indexes exist currently.
+The columns must be not nullable and are stored in same
+order as underlying table columns.
+<p>
+<tt>create unique hash index x1 on t1(b, c) logging</tt>
+<p>
+Internally, a unique hash index is a table where index key is primary key.
+If the index is <tt>nologging</tt>, it is rebuilt on database restart
+before the database is opened.
+<p>
+Indexes can of course be dropped:
+<p>
+<tt>drop index x1</tt>
+
+<h4>3.8 Select</h4>
+
+Features:
+
+<ul>
+<li>Expressions and predicates
+<br><tt>select a + b * c from t where a &lt; b + c and (b &gt; c or c &gt; 10)</tt>
+<li>JOIN to any depth
+<br><tt>select a.x, b.y, c.z from t1 a, t2 b, t2 c where a.x + b.y &lt; c.z</tt>
+<li>ORDER BY
+<br><tt>select * from t1, t2 where a1 &gt; 5 order by b1 + b2, c1 desc</tt>
+<li>DISTINCT
+<br><tt>select distinct a, b + c from t</tt>
+<li>Aggregates without grouping.
+<br><tt>select count(*), max(a), 1 + sum(b) + avg(c * d) from t</tt>
+<li>Aggregates with grouping.
+<br><tt>select a, sum(b) from t group by a having sum(c) &gt; 0 order by a, sum(d)</tt>
+</ul>
+
+Major omissions:
+<ul>
+<li>no OUTER JOIN
+<li>no subqueries and no EXISTS clause
+</ul>
+
+Queries are optimized to minimize scans,
+by using primary keys and existing unique hash indexes.
+Simple predicates in scans (column compared to constant)
+are passed to an interpreter in NDB kernel.
+Joins are done via <em>nested loops</em> only.
+<p>
+<ul>
+<li>SCAN
+<br><tt>select * from t where a &lt; b</tt>
+<li>INTERPRETED SCAN (much faster)
+<br><tt>select * from t1, t2 where t1.a &lt; 10 and t2.b &gt; t1.c</tt>
+<li>PRIMARY KEY lookup
+<br><tt>select * from t where pk = 5 and b &gt; 10</tt>
+<li>NESTED LOOPS
+<br><tt>select * from t1, t2, t3 where t1.pk = t2.x and t2.pk = t3.y</tt>
+</ul>
+
+<h4>3.9 Insert and write</h4>
+
+Both VALUES and subquery variants can be used.
+<p>
+<pre><tt>insert into t(a, c) values (123, 'abc')
+insert into t1(a, c) select a + 10 * b, c from t2
+</tt></pre>
+<p>
+For convenience, the non-standard <i>MySql</i> syntax is also supported.
+<p>
+<pre><tt>insert into t set a = 123, c = 'abc'</tt></pre>
+<p>
+The non-standard operation WRITE is used exactly like INSERT.
+The record is updated if it exists.
+Otherwise a new record is inserted.
+<p>
+<pre><tt>write into t(a, c) values (123, 'abc')
+</tt></pre>
+
+<h4>3.10 Update</h4>
+
+Update allows no subqueries.
+Update is optimized to minimize scans and reads,
+by using primary keys and existing unique hash indexes.
+<p>
+<ul>
+<li>SCAN
+<br><tt>update t set a = b + 5, c = d where c &gt; 10</tt>
+<li>PRIMARY KEY or INDEX lookup
+<br><tt>update t set a = b + 5, c = d where pk = 5 and c &gt; 10</tt>
+<li>PRIMARY KEY or INDEX direct
+<br><tt>update t set a = 5, c = 7 where pk = 5</tt>
+</ul>
+
+<h4>3.11 Delete</h4>
+
+Delete allows no subqueries.
+Delete is optimized to minimize scans and reads,
+by using primary keys and existing unique hash indexes.
+<p>
+<ul>
+<li>SCAN
+<br><tt>delete from t where c &gt; 10</tt>
+<li>PRIMARY KEY or INDEX lookup
+<br><tt>delete from t where pk = 5 and c &gt; 10</tt>
+<li>PRIMARY KEY or INDEX direct
+<br><tt>delete from t where pk = 5</tt>
+</ul>
+
+<h4>3.12 Virtual tables</h4>
+
+The driver implements some virtual tables.
+They can only be queried, not modified.
+<p>
+<ul>
+<li>DUAL
+<br>A 1-row table - example: select SYSDATE from DUAL.
+<li>ODBC$TYPEINFO
+<br>Corresponds to SQLGetTypeInfo.
+<li>ODBC$TABLES
+<br>Corresponds to SQLTables but shows all NDB kernel objects.
+<li>ODBC$COLUMNS
+<br>Corresponds to SQLColumns.
+<li>ODBC$PRIMARYKEYS
+<br>Corresponds to SQLPrimaryKeys.
+</ul>
+
+<h3>4. DIAGNOSTICS</h3>
+
+<h4>4.1 Diagnostic records</h4>
+
+The driver manager and driver return 3 main diagnostics
+(see <b><tt>SQLGetDiagRec</tt></b>).
+<ul>
+<li>SQLSTATE (a string of 5 characters)
+<li>Native error code
+<li>Message text
+</ul>
+<p>
+Message text format is
+<br><br>
+<b><tt>[Alzato][ODBC driver][NDB Cluster] NDB-ssccnnn <i>error text</i> (in SQLXxx)</tt></b>
+<br><br>
+Here <b>ssccnnnn</b> is native error code (decimal number), with following parts:
+<p>
+<li><b><tt>ss</tt></b> - status
+<li><b><tt>cc</tt></b> - classification
+<li><b><tt>nnnn</tt></b> - error code
+</ul>
+<p>
+See NDB API guide for further information.
+<p>
+For non-database errors the last prefix <b><tt>[NDB Cluster]</tt></b> is omitted
+and native error code is always 02015001.
+
+<h4>4.2 Tracing</h4>
+
+Following environment variables may be useful.
+<ul>
+<li><b><tt>NDB_ODBC_TRACE</tt></b>
+<br>
+Values &ge; 2 cause SQL execution plan
+to be printed on standard output.
+<br>
+Values &ge; 3 show the ODBC API functions
+called by the driver manager.
+<br><br>
+<li><b><tt>NDB_ODBC_DEBUG</tt></b>
+<br>
+Non-zero value makes the driver abort
+the application on unhandled conditions.
+<br>
+By default the ODBC API function is aborted gracefully.
+</ul>
+
+<h4>4.3 Thread safety</h4>
+<p>
+The driver has same thread-safety model as NDB API.
+In NDB API each thread must use its own <b><tt>Ndb</tt></b> object.
+In NDB ODBC a <b><tt>SQLConnect</tt></b> corresponds to an <b><tt>Ndb</tt></b> object.
+It is required that each thread allocates its
+own ODBC handles (of all types).
+
+<h4>4.4 Data formats</h4>
+<p>
+SQL types are represented as (old) NDB types as follows.
+<p>
+<table width="80%" border=1>
+<tr align="left"><th width="20%">SQL type</th> <th>NDB type</th></tr>
+<tr align="left"><td align="left">CHAR(n)</td><td>String(n), blank-padded to n</td></tr>
+<tr align="left"><td align="left">VARCHAR(n)</td><td>String(n+2), zero-padded to n, length in last 2 bytes (big-endian)</td></tr>
+<tr align="left"><td align="left">integers</td><td>Signed(x) or UnSigned(x), x=16,32,64, native format</td></tr>
+<tr align="left"><td align="left">floats</td><td>Float(x), x=32,64, native format</td></tr>
+<tr align="left"><td align="left">DATETIME</td><td>String(12) = cc yy mm dd HH MM SS \0 ff ff ff ff (big-endian)</td></tr>
+</table>
+<p>
+Note: SQL types exist now in NDB API in <b><tt>NdbDictionary</tt></b> class.
+However they are not yet understood by NDB API operations.
+
+<h4>4.5 NDB Cluster limitations</h4>
+<p>
+<ul>
+<li>Isolation level is READ COMMITTED.
+A scan (non-primary-key select of several rows) does not see consistent data.
+<br><br>
+<li>Inserting into a table from itself is likely to cause a deadlock
+or a random result.
+<br><tt><b>no:</b> insert into t(a, b) select a*100, b+100 from t</tt>
+<br><br>
+<li>Number of uncommitted rows is limited by NDB configuration
+parameter <b><tt>MaxNoOfConcurrentOperations</tt></b> (typical default 4096).
+To delete all rows from a large table one may need to do repeatedly:
+<br><tt>delete from t where rownum < 4000</tt>
+</ul>
+
+<h4>4.6 Known problems v2.11</h4>
+<p>
+Following lists specific known problems.
+<ul>
+<li>ORDER BY works only with expressions,
+not with column aliases or positions.
+<br><tt><b>no:</b> select a+b x from t order by x</tt>
+<br><tt><b>no:</b> select * from t order by 1, 2, 3</tt>
+<br><br>
+<li>Join optimizer does not always minimize number of scans.
+Changing the order of tables in the statement may help.
+</ul>
+
+<h4>4.7 Useful links</h4>
+<p>
+<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/dasdkodbcoverview_whatsnew.asp/">Microsoft ODBC page</a>
+<br>
+<a href="http://www.unixodbc.org/">unixODBC home page</a>
+<br>
+<a href="http://www.iodbc.org/">iODBC home page</a>
+
+</BODY>
+</HTML>
diff --git a/storage/ndb/src/old_files/client/odbc/docs/select.fig b/storage/ndb/src/old_files/client/odbc/docs/select.fig
new file mode 100644
index 00000000000..4f51a2085b4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/select.fig
@@ -0,0 +1,94 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+A4
+92.00
+Single
+-2
+1200 2
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 2700 2700 1500 3900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 3150 2700 3150 3900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 3600 4500 4800 5700
+2 1 1 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 3600 2700 4800 3900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 2775 4500 1200 5700
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 3150 4500 3150 5700
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 2100 4500 2100 3900 600 3900 600 4500 2100 4500
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 2100 6300 2100 5700 600 5700 600 6300 2100 6300
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3900 4500 3900 3900 2400 3900 2400 4500 3900 4500
+2 4 1 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 5700 4500 5700 3900 4200 3900 4200 4500 5700 4500
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3900 6300 3900 5700 2400 5700 2400 6300 3900 6300
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 5700 6300 5700 5700 4200 5700 4200 6300 5700 6300
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 8400 2700 8400 2100 6900 2100 6900 2700 8400 2700
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 8400 6300 8400 5700 6900 5700 6900 6300 8400 6300
+2 1 0 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7650 2700 7650 5700
+2 1 0 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 7650 6300 7650 7500
+2 1 1 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 8100 6300 10575 6900
+2 1 0 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 8100 2700 10575 3300
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 11700 3900 11700 3300 10200 3300 10200 3900 11700 3900
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 9900 5400 9900 4800 8400 4800 8400 5400 9900 5400
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 11700 5400 11700 4800 10200 4800 10200 5400 11700 5400
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 13500 5400 13500 4800 12000 4800 12000 5400 13500 5400
+2 1 0 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 10500 3900 9300 4800
+2 1 0 1 0 7 50 0 -1 4.000 0 0 7 1 0 2
+ 0 0 1.00 60.00 120.00
+ 11400 3900 12600 4800
+2 1 0 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2
+ 0 0 1.00 60.00 120.00
+ 10950 3900 10950 4800
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 8400 8100 8400 7500 6900 7500 6900 8100 8400 8100
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3900 2700 3900 2100 2400 2100 2400 2700 3900 2700
+2 4 1 1 0 7 50 0 -1 4.000 0 0 7 0 0 5
+ 11700 7500 11700 6900 10200 6900 10200 7500 11700 7500
+4 0 0 50 0 14 12 0.0000 4 135 840 900 4275 table T1\001
+4 0 0 50 0 14 20 0.0000 4 240 4125 900 1125 select A, C, 123 from T1/\001
+4 0 0 50 0 14 12 0.0000 4 135 840 825 6075 column A\001
+4 0 0 50 0 14 12 0.0000 4 135 840 2775 6075 column C\001
+4 0 0 50 0 14 12 0.0000 4 135 945 4425 6075 const 123\001
+4 0 0 50 0 14 12 0.0000 4 135 630 4575 4275 clause\001
+4 0 0 50 0 14 12 0.0000 4 90 315 2925 4275 row\001
+4 0 0 50 0 14 12 0.0000 4 180 735 7200 2475 project\001
+4 0 0 50 0 14 12 0.0000 4 135 630 7200 6000 filter\001
+4 0 0 50 0 14 12 0.0000 4 135 840 8625 5100 column 1\001
+4 0 0 50 0 14 12 0.0000 4 135 840 10500 5100 column 2\001
+4 0 0 50 0 14 12 0.0000 4 135 945 12300 5100 const 123\001
+4 0 0 50 0 14 12 0.0000 4 90 315 10650 3600 row\001
+4 0 0 50 0 14 12 0.0000 4 150 840 7200 7800 scan 1,2\001
+4 0 0 50 0 14 12 0.0000 4 135 630 2850 2475 select\001
+4 0 0 50 0 14 12 0.0000 4 135 630 10500 7200 clause\001
diff --git a/storage/ndb/src/old_files/client/odbc/docs/systables.pl b/storage/ndb/src/old_files/client/odbc/docs/systables.pl
new file mode 100644
index 00000000000..728d966a7a4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/systables.pl
@@ -0,0 +1,2192 @@
+# usage: perl systables.pl {typeinfo|tables|columns|primarykeys} {-l|-c}
+use strict;
+my $what = shift;
+my $opt = shift;
+my $listWhat = {};
+
+#
+# odbcsqlgettypeinfo.htm
+#
+$listWhat->{typeinfo} = [
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLGetTypeInfo</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlgettypeinfo"></A>SQLGetTypeInfo</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 1.0<BR>
+# Standards Compliance: ISO 92</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLGetTypeInfo</B> returns information about data types supported by the data source. The driver returns the information in the form of an SQL result set. The data types are intended for use in Data Definition Language (DDL) statements.</P>
+#
+# <P class="indent"><b class="le">Important&nbsp;&nbsp;&nbsp;</b>Applications must use the type names returned in the TYPE_NAME column of the <B>SQLGetTypeInfo</B> result set in <B>ALTER TABLE</B> and <B>CREATE TABLE</B> statements. <B>SQLGetTypeInfo</B> may return more than one row with the same value in the DATA_TYPE column.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLGetTypeInfo</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StatementHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>DataType</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>StatementHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Statement handle for the result set.</dd>
+#
+# <DT><I>DataType</I></DT>
+#
+# <DD>[Input]<BR>
+# The SQL data type. This must be one of the values in the "<A HREF="odbcsql_data_types.htm">SQL Data Types</A>" section of Appendix D: Data Types, or a driver-specific SQL data type. SQL_ALL_TYPES specifies that information about all data types should be returned.</dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLGetTypeInfo</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_STMT and a <I>Handle</I> of <I>StatementHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLGetTypeInfo </B>and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01S02</TD>
+# <TD width=26%>Option value changed</TD>
+# <TD width=52%>A specified statement attribute was invalid because of implementation working conditions, so a similar value was temporarily substituted. (Call <B>SQLGetStmtAttr</B> to determine the temporarily substituted value.) The substitute value is valid for the <I>StatementHandle</I> until the cursor is closed. The statement attributes that can be changed are: SQL_ATTR_CONCURRENCY, SQL_ATTR_CURSOR_TYPE, SQL_ATTR_KEYSET_SIZE, SQL_ATTR_MAX_LENGTH, SQL_ATTR_MAX_ROWS, SQL_ATTR_QUERY_TIMEOUT, and SQL_ATTR_SIMULATE_CURSOR. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>A cursor was open on the <I>StatementHandle,</I> and <B>SQLFetch</B> or <B>SQLFetchScroll</B> had been called. This error is returned by the Driver Manager if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has not returned SQL_NO_DATA, and is returned by the driver if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has returned SQL_NO_DATA.
+# <P>A result set was open on the <I>StatementHandle</I>, but <B>SQLFetch</B> or <B>SQLFetchScroll</B> had not been called.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40001</TD>
+# <TD width=26%>Serialization failure</TD>
+# <TD width=52%>The transaction was rolled back due to a resource deadlock with another transaction.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40003</TD>
+# <TD width=26%>Statement completion unknown</TD>
+# <TD width=52%>The associated connection failed during the execution of this function and the state of the transaction cannot be determined.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause. </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY004</TD>
+# <TD width=26%>Invalid SQL data type</TD>
+# <TD width=52%>The value specified for the argument <I>DataType</I> was neither a valid ODBC SQL data type identifier nor a driver-specific data type identifier supported by the driver.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY008</TD>
+# <TD width=26%>Operation canceled</TD>
+# <TD width=52%>Asynchronous processing was enabled for the <I>StatementHandle</I>, then the function was called and, before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I>. Then the function was called again on the <I>StatementHandle</I>.
+# <P>The function was called and, before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I> from a different thread in a multithread application.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function (not this one) was called for the <I>StatementHandle</I> and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.
+# <P>The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT00</TD>
+# <TD width=26%>Timeout expired</TD>
+# <TD width=52%>The query timeout period expired before the data source returned the result set. The timeout period is set through <B>SQLSetStmtAttr</B>, SQL_ATTR_QUERY_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver corresponding to the <I>StatementHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P><B>SQLGetTypeInfo</B> returns the results as a standard result set, ordered by DATA_TYPE and then by how closely the data type maps to the corresponding ODBC SQL data type. Data types defined by the data source take precedence over user-defined data types. Consequently, the sort order is not necessarily consistent but can be generalized as DATA_TYPE first, followed by TYPE_NAME, both ascending. For example, suppose that a data source defined INTEGER and COUNTER data types, where COUNTER is auto-incrementing, and that a user-defined data type WHOLENUM has also been defined. These would be returned in the order INTEGER, WHOLENUM, and COUNTER, because WHOLENUM maps closely to the ODBC SQL data type SQL_INTEGER, while the auto-incrementing data type, even though supported by the data source, does not map closely to an ODBC SQL data type. For information about how this information might be used, see "<A HREF="odbcddl_statements.htm">DDL Statements</A>" in Chapter 8: SQL Statements.</P>
+#
+# <P>If the <I>DataType</I> argument specifies a data type which is valid for the version of ODBC supported by the driver, but is not supported by the driver, then it will return an empty result set. </P>
+#
+# <P class="indent"><b class="le">Note&nbsp;&nbsp;&nbsp;</b>For more information about the general use, arguments, and returned data of ODBC catalog functions, see <A HREF="odbccatalog_functions.htm">Chapter 7: Catalog Functions</A>.</P>
+#
+# <P>The following columns have been renamed for ODBC 3.<I>x</I>. The column name changes do not affect backward compatibility because applications bind by column number.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>ODBC 2.0 column</TH>
+# <TH width=52%>ODBC 3.<I>x</I> column</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>PRECISION</TD>
+# <TD width=52%>COLUMN_SIZE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>MONEY</TD>
+# <TD width=52%>FIXED_PREC_SCALE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>AUTO_INCREMENT</TD>
+# <TD width=52%>AUTO_UNIQUE_VALUE</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>The following columns have been added to the results set returned by <B>SQLGetTypeInfo</B> for ODBC 3.<I>x</I>:
+#
+# <UL type=disc>
+# <LI>SQL_DATA_TYPE</li>
+#
+# <LI>INTERVAL_PRECISION</li>
+#
+# <LI>SQL_DATETIME_SUB</li>
+#
+# <LI>NUM_PREC_RADIX</li>
+# </UL>
+#
+# <P>The following table lists the columns in the result set. Additional columns beyond column 19 (INTERVAL_PRECISION) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "<A HREF="odbcdata_returned_by_catalog_functions.htm">Data Returned by Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;<B>SQLGetTypeInfo</B> might not return all data types. For example, a driver might not return user-defined data types. Applications can use any valid data type, regardless of whether it is returned by <B>SQLGetTypeInfo</B>.</P>
+#
+# <P class="indent">The data types returned by <B>SQLGetTypeInfo</B> are those supported by the data source. They are intended for use in Data Definition Language (DDL) statements. Drivers can return result-set data using data types other than the types returned by <B>SQLGetTypeInfo</B>. In creating the result set for a catalog function, the driver might use a data type that is not supported by the data source. </P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=33%><BR>
+# Column name</TH>
+# <TH width=14%>Column <BR>
+# number</TH>
+# <TH width=15%><BR>
+# Data type</TH>
+# <TH width=38%><BR>
+# Comments</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=33%>TYPE_NAME<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>1</TD>
+# <TD width=15%>Varchar<BR>
+# not NULL</TD>
+# <TD width=38%>Data source&#0150;dependent data-type name; for example, "CHAR()", "VARCHAR()", "MONEY", "LONG VARBINARY", or "CHAR ( ) FOR BIT DATA". Applications must use this name in <B>CREATE TABLE</B> and <B>ALTER TABLE</B> statements.</TD>
+# </TR>
+ { name => "type_name",
+ type => "varchar",
+ length => 20,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>DATA_TYPE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>2</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For datetime or interval data types, this column returns the concise data type (such as SQL_TYPE_TIME or SQL_INTERVAL_YEAR_TO_MONTH). For a list of valid ODBC SQL data types, see "<A HREF="odbcsql_data_types.htm">SQL Data Types</A>" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver’s documentation.</TD>
+# </TR>
+ { name => "data_type",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>COLUMN_SIZE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>3</TD>
+# <TD width=15%>Integer</TD>
+# <TD width=38%>The maximum column size that the server supports for this data type. For numeric data, this is the maximum precision. For string data, this is the length in characters. For datetime data types, this is the length in characters of the string representation (assuming the maximum allowed precision of the fractional seconds component). NULL is returned for data types where column size is not applicable. For interval data types, this is the number of characters in the character representation of the interval literal (as defined by the interval leading precision; see "<A HREF="odbcinterval_data_type_length.htm">Interval Data Type Length</A>" in Appendix D: Data Types).
+# <P>For more information on column size, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</P>
+# </TD>
+# </TR>
+ { name => "column_size",
+ type => "integer",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>LITERAL_PREFIX<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>4</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>Character or characters used to prefix a literal; for example, a single quotation mark (') for character data types or 0x for binary data types; NULL is returned for data types where a literal prefix is not applicable.</TD>
+# </TR>
+ { name => "literal_prefix",
+ type => "varchar",
+ length => 1,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>LITERAL_SUFFIX<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>5</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>Character or characters used to terminate a literal; for example, a single quotation mark (') for character data types; NULL is returned for data types where a literal suffix is not applicable.</TD>
+# </TR>
+ { name => "literal_suffix",
+ type => "varchar",
+ length => 1,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>CREATE_PARAMS<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>6</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>A list of keywords, separated by commas, corresponding to each parameter that the application may specify in parentheses when using the name that is returned in the TYPE_NAME field. The keywords in the list can be any of the following: length, precision, or scale. They appear in the order that the syntax requires them to be used. For example, CREATE_PARAMS for DECIMAL would be "precision,scale"; CREATE_PARAMS for VARCHAR would equal "length." NULL is returned if there are no parameters for the data type definition; for example, INTEGER.
+# <P>The driver supplies the CREATE_PARAMS text in the language of the country where it is used.</P>
+# </TD>
+# </TR>
+ { name => "create_params",
+ type => "varchar",
+ length => 20,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>NULLABLE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>7</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>Whether the data type accepts a NULL value:
+# <P>SQL_NO_NULLS if the data type does not accept NULL values.</P>
+#
+# <P>SQL_NULLABLE if the data type accepts NULL values.</P>
+#
+# <P>SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values.</P>
+# </TD>
+# </TR>
+ { name => "nullable",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>CASE_SENSITIVE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>8</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>Whether a character data type is case-sensitive in collations and comparisons:
+# <P>SQL_TRUE if the data type is a character data type and is case-sensitive.</P>
+#
+# <P>SQL_FALSE if the data type is not a character data type or is not case-sensitive.</P>
+# </TD>
+# </TR>
+ { name => "case_sensitive",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>SEARCHABLE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>9</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>How the data type is used in a <B>WHERE</B> clause:
+# <P>SQL_PRED_NONE if the column cannot be used in a <B>WHERE</B> clause. (This is the same as the SQL_UNSEARCHABLE value in ODBC 2.<I>x</I>.)</P>
+#
+# <P>SQL_PRED_CHAR if the column can be used in a <B>WHERE</B> clause, but only with the <B>LIKE</B> predicate. (This is the same as the SQL_LIKE_ONLY value in ODBC 2.<I>x</I>.)</P>
+#
+# <P>SQL_PRED_BASIC if the column can be used in a <B>WHERE</B> clause with all the comparison operators except <B>LIKE</B> (comparison, quantified comparison, <B>BETWEEN</B>, <B>DISTINCT</B>, <B>IN</B>, <B>MATCH</B>, and <B>UNIQUE</B>). (This is the same as the SQL_ALL_EXCEPT_LIKE value in ODBC 2.<I>x</I>.)</P>
+#
+# <P>SQL_SEARCHABLE if the column can be used in a <B>WHERE</B> clause with any comparison operator.</P>
+# </TD>
+# </TR>
+ { name => "searchable",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>UNSIGNED_ATTRIBUTE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>10</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>Whether the data type is unsigned:
+# <P>SQL_TRUE if the data type is unsigned.</P>
+#
+# <P>SQL_FALSE if the data type is signed.</P>
+#
+# <P>NULL is returned if the attribute is not applicable to the data type or the data type is not numeric.</P>
+# </TD>
+# </TR>
+ { name => "unsigned_attribute",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>FIXED_PREC_SCALE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>11</TD>
+# <TD width=15%>Smallint<BR>
+# not NULL</TD>
+# <TD width=38%>Whether the data type has predefined fixed precision and scale (which are data source&#0150;specific), such as a money data type:
+# <P>SQL_TRUE if it has predefined fixed precision and scale.</P>
+#
+# <P>SQL_FALSE if it does not have predefined fixed precision and scale.</P>
+# </TD>
+# </TR>
+ { name => "fixed_prec_scale",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>AUTO_UNIQUE_VALUE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>12</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>Whether the data type is autoincrementing:
+# <P>SQL_TRUE if the data type is autoincrementing.</P>
+#
+# <P>SQL_FALSE if the data type is not autoincrementing.</P>
+#
+# <P>NULL is returned if the attribute is not applicable to the data type or the data type is not numeric.</P>
+#
+# <P>An application can insert values into a column having this attribute, but typically cannot update the values in the column. </P>
+#
+# <P>When an insert is made into an auto-increment column, a unique value is inserted into the column at insert time. The increment is not defined, but is data source&#0150;specific. An application should not assume that an auto-increment column starts at any particular point or increments by any particular value.</P>
+# </TD>
+# </TR>
+ { name => "auto_unique_value",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>LOCAL_TYPE_NAME<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>13</TD>
+# <TD width=15%>Varchar</TD>
+# <TD width=38%>Localized version of the data source&#0150;dependent name of the data type. NULL is returned if a localized name is not supported by the data source. This name is intended for display only, such as in dialog boxes.</TD>
+# </TR>
+ { name => "local_type_name",
+ type => "varchar",
+ length => 20,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>MINIMUM_SCALE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>14</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>The minimum scale of the data type on the data source. If a data type has a fixed scale, the MINIMUM_SCALE and MAXIMUM_SCALE columns both contain this value. For example, an SQL_TYPE_TIMESTAMP column might have a fixed scale for fractional seconds. NULL is returned where scale is not applicable. For more information, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</TD>
+# </TR>
+ { name => "minimum_scale",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>MAXIMUM_SCALE<BR>
+# (ODBC 2.0)</TD>
+# <TD width=14%>15</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>The maximum scale of the data type on the data source. NULL is returned where scale is not applicable. If the maximum scale is not defined separately on the data source, but is instead defined to be the same as the maximum precision, this column contains the same value as the COLUMN_SIZE column. For more information, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</TD>
+# </TR>
+ { name => "maximum_scale",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>SQL_DATA_TYPE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>16</TD>
+# <TD width=15%>Smallint NOT NULL</TD>
+# <TD width=38%>The value of the SQL data type as it appears in the SQL_DESC_TYPE field of the descriptor. This column is the same as the DATA_TYPE column, except for interval and datetime data types.
+# <P>For interval and datetime data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.) </P>
+# </TD>
+# </TR>
+ { name => "sql_data_type",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>SQL_DATETIME_SUB<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>17</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>When the value of SQL_DATA_TYPE is SQL_DATETIME or SQL_INTERVAL, this column contains the datetime/interval subcode. For data types other than datetime and interval, this field is NULL.
+# <P>For interval or datetime data types, the SQL_DATA_TYPE field in the result set will return SQL_INTERVAL or SQL_DATETIME, and the SQL_DATETIME_SUB field will return the subcode for the specific interval or datetime data type. (See <A HREF="odbcdata_types.htm">Appendix D: Data Types</A>.)</P>
+# </TD>
+# </TR>
+ { name => "sql_datetime_sub",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>NUM_PREC_RADIX<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>18</TD>
+# <TD width=15%>Integer</TD>
+# <TD width=38%>If the data type is an approximate numeric type, this column contains the value 2 to indicate that COLUMN_SIZE specifies a number of bits. For exact numeric types, this column contains the value 10 to indicate that COLUMN_SIZE specifies a number of decimal digits. Otherwise, this column is NULL.</TD>
+# </TR>
+ { name => "num_prec_radix",
+ type => "integer",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=33%>INTERVAL_PRECISION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=14%>19</TD>
+# <TD width=15%>Smallint</TD>
+# <TD width=38%>If the data type is an interval data type, then this column contains the value of the interval leading precision. (See "<A HREF="odbcinterval_data_type_precision.htm">Interval Data Type Precision</A>" in Appendix D: Data Types.) Otherwise, this column is NULL.</TD>
+# </TR>
+ { name => "interval_precision",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+# </table></div>
+# <!--TS:-->
+# <P>Attribute information can apply to data types or to specific columns in a result set. <B>SQLGetTypeInfo</B> returns information about attributes associated with data types; <B>SQLColAttribute</B> returns information about attributes associated with columns in a result set.</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>For information about</TH>
+# <TH width=50%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Binding a buffer to a column in a result set</TD>
+# <TD width=50%><A HREF="odbcsqlbindcol.htm">SQLBindCol</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Canceling statement processing</TD>
+# <TD width=50%><A HREF="odbcsqlcancel.htm">SQLCancel</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning information about a column in a result set</TD>
+# <TD width=50%><A HREF="odbcsqlcolattribute.htm">SQLColAttribute</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a block of data or scrolling through a result set</TD>
+# <TD width=50%><A HREF="odbcsqlfetchscroll.htm">SQLFetchScroll</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a single row or a block of data in a forward-only direction</TD>
+# <TD width=50%><A HREF="odbcsqlfetch.htm">SQLFetch</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning information about a driver or data source</TD>
+# <TD width=50%><A HREF="odbcsqlgetinfo.htm">SQLGetInfo</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+];
+
+#
+# odbcsqltables.htm
+#
+$listWhat->{tables} = [
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLTables</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqltables"></A>SQLTables</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 1.0<BR>
+# Standards Compliance: X/Open</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLTables</B> returns the list of table, catalog, or schema names, and table types, stored in a specific data source. The driver returns the information as a result set.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLTables</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StatementHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>CatalogName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength1</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>SchemaName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength2</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>TableName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength3</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>TableType</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength4</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>StatementHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Statement handle for retrieved results.</dd>
+#
+# <DT><I>CatalogName</I></DT>
+#
+# <DD>[Input]<BR>
+# Catalog name. The <I>CatalogName</I> argument accepts search patterns if the SQL_ODBC_VERSION environment attribute is SQL_OV_ODBC3; it does not accept search patterns if SQL_OV_ODBC2 is set. If a driver supports catalogs for some tables but not for others, such as when a driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs.
+#
+# <P>If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>CatalogName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>CatalogName</I> is a pattern value argument; it is treated literally, and its case is significant. For more information, see "<A HREF="odbcarguments_in_catalog_functions.htm">Arguments in Catalog Functions</A>" in Chapter 7: Catalog Functions.
+# </dd>
+#
+# <DT><I>NameLength1</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>CatalogName</I>.</dd>
+#
+# <DT><I>SchemaName</I></DT>
+#
+# <DD>[Input]<BR>
+# String search pattern for schema names. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas.
+#
+# <P>If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>SchemaName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>SchemaName</I> is a pattern value argument; it is treated literally, and its case is significant.
+# </dd>
+#
+# <DT><I>NameLength2</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>SchemaName</I>.</dd>
+#
+# <DT><I>TableName</I></DT>
+#
+# <DD>[Input]<BR>
+# String search pattern for table names.
+#
+# <P>If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>TableName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>TableName</I> is a pattern value argument; it is treated literally, and its case is significant.
+# </dd>
+#
+# <DT><I>NameLength3</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>TableName</I>.</dd>
+#
+# <DT><I>TableType</I></DT>
+#
+# <DD>[Input]<BR>
+# List of table types to match.
+#
+# <P>Note that the SQL_ATTR_METADATA_ID statement attribute has no effect upon the <I>TableType</I> argument. <I>TableType</I> is a value list argument, no matter what the setting of SQL_ATTR_METADATA_ID.
+# </dd>
+#
+# <DT><I>NameLength4</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>TableType</I>.</dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLTables</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_STMT and a <I>Handle</I> of <I>StatementHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLTables</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>A cursor was open on the <I>StatementHandle</I>, and <B>SQLFetch</B> or <B>SQLFetchScroll</B> had been called. This error is returned by the Driver Manager if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has not returned SQL_NO_DATA and is returned by the driver if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has returned SQL_NO_DATA.
+# <P>A cursor was open on the <I>StatementHandle</I>, but <B>SQLFetch</B> or <B>SQLFetchScroll</B> had not been called.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40001</TD>
+# <TD width=26%>Serialization failure</TD>
+# <TD width=52%>The transaction was rolled back due to a resource deadlock with another transaction.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40003</TD>
+# <TD width=26%>Statement completion unknown</TD>
+# <TD width=52%>The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation <BR>
+# error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY008</TD>
+# <TD width=26%>Operation canceled</TD>
+# <TD width=52%>Asynchronous processing was enabled for the <I>StatementHandle</I>. The function was called, and before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I>. Then the function was called again on the <I>StatementHandle</I>.
+# <P>The function was called, and before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I> from a different thread in a multithread application.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY009</TD>
+# <TD width=26%>Invalid use of null pointer</TD>
+# <TD width=52%>The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the <I>CatalogName</I> argument was a null pointer, and the SQL_CATALOG_NAME <I>InfoType</I> returns that catalog names are supported.
+# <P>(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the <I>SchemaName</I> or <I>TableName</I> argument was a null pointer.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function (not this one) was called for the <I>StatementHandle</I> and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=52%>(DM) The value of one of the length arguments was less than 0 but not equal to SQL_NTS.
+# <P>The value of one of the name length arguments exceeded the maximum length value for the corresponding name.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>A catalog was specified, and the driver or data source does not support catalogs.
+# <P>A schema was specified, and the driver or data source does not support schemas.</P>
+#
+# <P>A string search pattern was specified for the catalog name, table schema, or table name, and the data source does not support search patterns for one or more of those arguments.</P>
+#
+# <P>The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source. </P>
+#
+# <P>The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT00</TD>
+# <TD width=26%>Timeout expired</TD>
+# <TD width=52%>The query timeout period expired before the data source returned the requested result set. The timeout period is set through <B>SQLSetStmtAttr</B>, SQL_ATTR_QUERY_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver associated with the <I>StatementHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P><B>SQLTables</B> lists all tables in the requested range. A user may or may not have SELECT privileges to any of these tables. To check accessibility, an application can:
+#
+# <UL type=disc>
+# <LI>Call <B>SQLGetInfo</B> and check the SQL_ACCESSIBLE_TABLES information type.</li>
+#
+# <LI>Call <B>SQLTablePrivileges</B> to check the privileges for each table.</li>
+# </UL>
+#
+# <P>Otherwise, the application must be able to handle a situation where the user selects a table for which <B>SELECT</B> privileges are not granted.</P>
+#
+# <P>The <I>SchemaName</I> and <I>TableName</I> arguments accept search patterns. The <I>CatalogName</I> argument accepts search patterns if the SQL_ODBC_VERSION environment attribute is SQL_OV_ODBC3; it does not accept search patterns if SQL_OV_ODBC2 is set. If SQL_OV_ODBC3 is set, an ODBC 3<I>.x</I> driver will require that wildcard characters in the <I>CatalogName</I> argument be escaped to be treated literally. For more information about valid search patterns, see "<A HREF="odbcpattern_value_arguments.htm">Pattern Value Arguments</A>" in Chapter 7: Catalog Functions.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;For more information about the general use, arguments, and returned data of ODBC catalog functions, see <A HREF="odbccatalog_functions.htm">Chapter 7: Catalog Functions</A>.</P>
+#
+# <P>To support enumeration of catalogs, schemas, and table types, the following special semantics are defined for the <I>CatalogName</I>, <I>SchemaName</I>, <I>TableName</I>, and <I>TableType</I> arguments of <B>SQLTables</B>:
+#
+# <UL type=disc>
+# <LI>If <I>CatalogName</I> is SQL_ALL_CATALOGS and <I>SchemaName</I> and <I>TableName</I> are empty strings, the result set contains a list of valid catalogs for the data source. (All columns except the TABLE_CAT column contain NULLs.)</li>
+#
+# <LI>If <I>SchemaName</I> is SQL_ALL_SCHEMAS and <I>CatalogName</I> and <I>TableName</I> are empty strings, the result set contains a list of valid schemas for the data source. (All columns except the TABLE_SCHEM column contain NULLs.)</li>
+#
+# <LI>If <I>TableType</I> is SQL_ALL_TABLE_TYPES and <I>CatalogName</I>, <I>SchemaName</I>, and <I>TableName</I> are empty strings, the result set contains a list of valid table types for the data source. (All columns except the TABLE_TYPE column contain NULLs.)</li>
+# </UL>
+#
+# <P>If <I>TableType</I> is not an empty string, it must contain a list of comma-separated values for the types of interest; each value may be enclosed in single quotation marks (') or unquoted&#0151;for example, 'TABLE', 'VIEW' or TABLE, VIEW. An application should always specify the table type in uppercase; the driver should convert the table type to whatever case is needed by the data source. If the data source does not support a specified table type, <B>SQLTables</B> does not return any results for that type.</P>
+#
+# <P><B>SQLTables</B> returns the results as a standard result set, ordered by TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, and TABLE_NAME. For information about how this information might be used, see "<A HREF="odbcuses_of_catalog_data.htm">Uses of Catalog Data</A>" in Chapter 7: Catalog Functions.</P>
+#
+# <P>To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, and TABLE_NAME columns, an application can call <B>SQLGetInfo</B> with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, and SQL_MAX_TABLE_NAME_LEN information types.</P>
+#
+# <P>The following columns have been renamed for ODBC 3<I>.x</I>. The column name changes do not affect backward compatibility because applications bind by column number.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>ODBC 2.0 column</TH>
+# <TH width=52%>ODBC 3<I>.x</I> column</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>TABLE_QUALIFIER</TD>
+# <TD width=52%>TABLE_CAT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>TABLE_OWNER</TD>
+# <TD width=52%>TABLE_SCHEM</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>The following table lists the columns in the result set. Additional columns beyond column 5 (REMARKS) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "<A HREF="odbcdata_returned_by_catalog_functions.htm">Data Returned by Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=27%><BR>
+# Column name</TH>
+# <TH width=15%>Column number</TH>
+# <TH width=16%><BR>
+# Data type</TH>
+# <TH width=42%><BR>
+# Comments</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_CAT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>1</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.</TD>
+# </TR>
+ { name => "table_cat",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_SCHEM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>2</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.</TD>
+# </TR>
+ { name => "table_schem",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>3</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Table name.</TD>
+# </TR>
+ { name => "table_name",
+ type => "varchar",
+ length => 16,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_TYPE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>4</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Table type name; one of the following: "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM", or a data source&#0150;specific type name.
+# <P>The meanings of "ALIAS" and "SYNONYM" are driver-specific.</P>
+# </TD>
+# </TR>
+ { name => "table_type",
+ type => "varchar",
+ length => 20,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>REMARKS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>5</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>A description of the table.</TD>
+# </TR>
+ { name => "remarks",
+ type => "varchar",
+ length => 200,
+ nullable => 1,
+ },
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Code Example</B></P>
+#
+# <P>For a code example of a similar function, see <A HREF="odbcsqlcolumns.htm">SQLColumns</A>.</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=43%>For information about</TH>
+# <TH width=57%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Binding a buffer to a column in a result set</TD>
+# <TD width=57%><A HREF="odbcsqlbindcol.htm">SQLBindCol</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Canceling statement processing</TD>
+# <TD width=57%><A HREF="odbcsqlcancel.htm">SQLCancel</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Returning privileges for a column or columns</TD>
+# <TD width=57%><A HREF="odbcsqlcolumnprivileges.htm">SQLColumnPrivileges</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Returning the columns in a table or tables</TD>
+# <TD width=57%><A HREF="odbcsqlcolumns.htm">SQLColumns</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Fetching a single row or a block of data in a forward-only direction</TD>
+# <TD width=57%><A HREF="odbcsqlfetch.htm">SQLFetch</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Fetching a block of data or scrolling through a result set</TD>
+# <TD width=57%><A HREF="odbcsqlfetchscroll.htm">SQLFetchScroll</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Returning table statistics and indexes</TD>
+# <TD width=57%><A HREF="odbcsqlstatistics.htm">SQLStatistics</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=43%>Returning privileges for a table or tables</TD>
+# <TD width=57%><A HREF="odbcsqltableprivileges.htm">SQLTablePrivileges</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+];
+
+#
+# odbcsqlcolumns.htm
+#
+$listWhat->{columns} = [
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLColumns</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlcolumns"></A>SQLColumns</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 1.0<BR>
+# Standards Compliance: X/Open</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLColumns</B> returns the list of column names in specified tables. The driver returns this information as a result set on the specified <I>StatementHandle</I>.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLColumns</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StatementHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>CatalogName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength1</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>SchemaName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength2</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>TableName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength3</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>ColumnName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength4</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>StatementHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Statement handle.</dd>
+#
+# <DT><I>CatalogName</I></DT>
+#
+# <DD>[Input]<BR>
+# Catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. <I>CatalogName</I> cannot contain a string search pattern.</dd>
+# </DL>
+#
+# <BLOCKQUOTE>
+# If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>CatalogName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>CatalogName</I> is an ordinary argument; it is treated literally, and its case is significant. For more information, see "<A HREF="odbcarguments_in_catalog_functions.htm">Arguments in Catalog Functions</A>" in Chapter 7: Catalog Functions.</BLOCKQUOTE>
+#
+# <DL>
+# <DT><I>NameLength1</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>CatalogName</I>.</dd>
+#
+# <DT><I>SchemaName</I></DT>
+#
+# <DD>[Input]<BR>
+# String search pattern for schema names. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas.</dd>
+# </DL>
+#
+# <BLOCKQUOTE>
+# If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>SchemaName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>SchemaName</I> is a pattern value argument; it is treated literally, and its case is significant.</BLOCKQUOTE>
+#
+# <DL>
+# <DT><I>NameLength2</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>SchemaName</I>.</dd>
+#
+# <DT><I>TableName</I></DT>
+#
+# <DD>[Input]<BR>
+# String search pattern for table names.</dd>
+# </DL>
+#
+# <BLOCKQUOTE>
+# If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>TableName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>TableName</I> is a pattern value argument; it is treated literally, and its case is significant.</BLOCKQUOTE>
+#
+# <DL>
+# <DT><I>NameLength3</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>TableName</I>.</dd>
+#
+# <DT><I>ColumnName</I></DT>
+#
+# <DD>[Input]<BR>
+# String search pattern for column names. </dd>
+# </DL>
+#
+# <BLOCKQUOTE>
+# If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>ColumnName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>ColumnName</I> is a pattern value argument; it is treated literally, and its case is significant.</BLOCKQUOTE>
+#
+# <DL>
+# <DT><I>NameLength4</I></DT>
+#
+# <DD>[Input]<BR>
+# Length of *<I>ColumnName</I>.</dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLColumns</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value may be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_STMT and a <I>Handle</I> of <I>StatementHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLColumns</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>A cursor was open on the <I>StatementHandle</I>, and <B>SQLFetch</B> or <B>SQLFetchScroll</B> had been called. This error is returned by the Driver Manager if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has not returned SQL_NO_DATA, and is returned by the driver if <B>SQLFetch</B> or <B>SQLFetchScroll</B> has returned SQL_NO_DATA.
+# <P>A cursor was open on the <I>StatementHandle</I> but <B>SQLFetch</B> or <B>SQLFetchScroll</B> had not been called.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40001</TD>
+# <TD width=26%>Serialization failure</TD>
+# <TD width=52%>The transaction was rolled back due to a resource deadlock with another transaction.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40003</TD>
+# <TD width=26%>Statement completion unknown</TD>
+# <TD width=52%>The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY008</TD>
+# <TD width=26%>Operation canceled</TD>
+# <TD width=52%>Asynchronous processing was enabled for the <I>StatementHandle</I>. The function was called, and before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I>. Then the function was called again on the <I>StatementHandle</I>.
+# <P>The function was called, and before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I> from a different thread in a multithread application.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY009</TD>
+# <TD width=26%>Invalid use of null pointer</TD>
+# <TD width=52%>The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the <I>CatalogName</I> argument was a null pointer, and the SQL_CATALOG_NAME <I>InfoType</I> returns that catalog names are supported.
+# <P>(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the <I>SchemaName</I>, <I>TableName</I>, or <I>ColumnName</I> argument was a null pointer.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function (not this one) was called for the <I>StatementHandle</I> and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=52%>(DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>&nbsp;</TD>
+# <TD width=26%>&nbsp;</TD>
+# <TD width=52%>The value of one of the name length arguments exceeded the maximum length value for the corresponding catalog or name. The maximum length of each catalog or name may be obtained by calling <B>SQLGetInfo</B> with the <I>InfoType</I> values. (See "Comments.")</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>A catalog name was specified, and the driver or data source does not support catalogs.
+# <P>A schema name was specified, and the driver or data source does not support schemas.</P>
+#
+# <P>A string search pattern was specified for the schema name, table name, or column name, and the data source does not support search patterns for one or more of those arguments.</P>
+#
+# <P>The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source. </P>
+#
+# <P>The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT00</TD>
+# <TD width=26%>Timeout expired</TD>
+# <TD width=52%>The query timeout period expired before the data source returned the result set. The timeout period is set through <B>SQLSetStmtAttr</B>, SQL_ATTR_QUERY_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver associated with the <I>StatementHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P>This function typically is used before statement execution to retrieve information about columns for a table or tables from the data source's catalog. <B>SQLColumns</B> can be used to retrieve data for all types of items returned by <B>SQLTables</B>. In addition to base tables, this may include (but is not limited to) views, synonyms, system tables, and so on. By contrast, the functions <B>SQLColAttribute</B> and <B>SQLDescribeCol</B> describe the columns in a result set and the function <B>SQLNumResultCols</B> returns the number of columns in a result set. For more information, see "<A HREF="odbcuses_of_catalog_data.htm">Uses of Catalog Data</A>" in Chapter 7: Catalog Functions.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;For more information about the general use, arguments, and returned data of ODBC catalog functions, see <A HREF="odbccatalog_functions.htm">Chapter 7: Catalog Functions</A>.</P>
+#
+# <P><B>SQLColumns</B> returns the results as a standard result set, ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and ORDINAL_POSITION. </P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;When an application works with an ODBC 2.<I>x</I> driver, no ORDINAL_POSITION column is returned in the result set. As a result, when working with ODBC 2.<I>x</I> drivers, the order of the columns in the column list returned by <B>SQLColumns</B> is not necessarily the same as the order of the columns returned when the application performs a SELECT statement on all columns in that table.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;<B>SQLColumns</B> might not return all columns. For example, a driver might not return information about pseudo-columns, such as Oracle ROWID. Applications can use any valid column, whether or not it is returned by <B>SQLColumns</B>. </P>
+#
+# <P class="indent">Some columns that can be returned by <B>SQLStatistics</B> are not returned by <B>SQLColumns</B>. For example, <B>SQLColumns</B> does not return the columns in an index created over an expression or filter, such as SALARY + BENEFITS or DEPT = 0012.</P>
+#
+# <P>The lengths of VARCHAR columns are not shown in the table; the actual lengths depend on the data source. To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and COLUMN_NAME columns, an application can call <B>SQLGetInfo</B> with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.</P>
+#
+# <P>The following columns have been renamed for ODBC 3.<I>x</I>. The column name changes do not affect backward compatibility because applications bind by column number.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>ODBC 2.0 column</TH>
+# <TH width=52%>ODBC 3.<I>x</I> column</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>TABLE_QUALIFIER</TD>
+# <TD width=52%>TABLE_CAT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>TABLE_OWNER</TD>
+# <TD width=52%>TABLE_SCHEM</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>PRECISION</TD>
+# <TD width=52%>COLUMN_SIZE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>LENGTH</TD>
+# <TD width=52%>BUFFER_LENGTH</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>SCALE</TD>
+# <TD width=52%>DECIMAL_DIGITS</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>RADIX</TD>
+# <TD width=52%>NUM_PREC_RADIX</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>The following columns have been added to the result set returned by <B>SQLColumns</B> for ODBC 3.<I>x</I>:</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TD width=50%>&nbsp;&nbsp;&nbsp;CHAR_OCTET_LENGTH </TD>
+# <TD width=50%>ORDINAL_POSITION</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>&nbsp;&nbsp;&nbsp;COLUMN_DEF</TD>
+# <TD width=50%>SQL_DATA_TYPE</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>&nbsp;&nbsp;&nbsp;IS_NULLABLE </TD>
+# <TD width=50%>SQL_DATETIME_SUB</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>The following table lists the columns in the result set. Additional columns beyond column 18 (IS_NULLABLE) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "<A HREF="odbcdata_returned_by_catalog_functions.htm">Data Returned by Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=24%><BR>
+# Column name</TH>
+# <TH width=17%>Column<BR>
+# number</TH>
+# <TH width=16%><BR>
+# Data type</TH>
+# <TH width=43%><BR>
+# Comments</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=24%>TABLE_CAT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>1</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=43%>Catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.</TD>
+# </TR>
+ { name => "table_cat",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>TABLE_SCHEM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>2</TD>
+# <TD width=16%>Varchar </TD>
+# <TD width=43%>Schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.</TD>
+# </TR>
+ { name => "table_schem",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>TABLE_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>3</TD>
+# <TD width=16%>Varchar not NULL</TD>
+# <TD width=43%>Table name.</TD>
+# </TR>
+ { name => "table_name",
+ type => "varchar",
+ length => 16,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>COLUMN_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>4</TD>
+# <TD width=16%>Varchar not NULL</TD>
+# <TD width=43%>Column name. The driver returns an empty string for a column that does not have a name.</TD>
+# </TR>
+ { name => "column_name",
+ type => "varchar",
+ length => 16,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>DATA_TYPE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>5</TD>
+# <TD width=16%>Smallint not NULL</TD>
+# <TD width=43%>SQL data type. This can be an ODBC SQL data type or a driver-specific SQL data type. For datetime and interval data types, this column returns the concise data type (such as SQL_TYPE_DATE or SQL_INTERVAL_YEAR_TO_MONTH, rather than the nonconcise data type such as SQL_DATETIME or SQL_INTERVAL). For a list of valid ODBC SQL data types, see "<A HREF="odbcsql_data_types.htm">SQL Data Types</A>" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.
+# <P>The data types returned for ODBC 3.<I>x</I> and ODBC 2.<I>x</I> applications may be different. For more information, see "<A HREF="odbcbackward_compatibility_and_standards_compliance.htm">Backward Compatibility and Standards Compliance</A>" in Chapter 17: Programming Considerations.</P>
+# </TD>
+# </TR>
+ { name => "data_type",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>TYPE_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>6</TD>
+# <TD width=16%>Varchar not NULL</TD>
+# <TD width=43%>Data source&#0150;dependent data type name; for example, "CHAR", "VARCHAR", "MONEY", "LONG VARBINAR", or "CHAR ( ) FOR BIT DATA".</TD>
+# </TR>
+ { name => "type_name",
+ type => "varchar",
+ length => 20,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>COLUMN_SIZE<BR>
+# (ODBC 1.0) </TD>
+# <TD width=17%>7</TD>
+# <TD width=16%>Integer</TD>
+# <TD width=43%>If DATA_TYPE is SQL_CHAR or SQL_VARCHAR, this column contains the maximum length in characters of the column. For datetime data types, this is the total number of characters required to display the value when converted to characters. For numeric data types, this is either the total number of digits or the total number of bits allowed in the column, according to the NUM_PREC_RADIX column. For interval data types, this is the number of characters in the character representation of the interval literal (as defined by the interval leading precision, see "<A HREF="odbcinterval_data_type_length.htm">Interval Data Type Length</A>" in Appendix D: Data Types). For more information, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</TD>
+# </TR>
+ { name => "column_size",
+ type => "integer",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>BUFFER_LENGTH<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>8</TD>
+# <TD width=16%>Integer</TD>
+# <TD width=43%>The length in bytes of data transferred on an SQLGetData, SQLFetch, or SQLFetchScroll operation if SQL_C_DEFAULT is specified. For numeric data, this size may be different than the size of the data stored on the data source. This value might be different than COLUMN_SIZE column for character data. For more information about length, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types.</TD>
+# </TR>
+ { name => "buffer_length",
+ type => "integer",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>DECIMAL_DIGITS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>9</TD>
+# <TD width=16%>Smallint</TD>
+# <TD width=43%>The total number of significant digits to the right of the decimal point. For SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP, this column contains the number of digits in the fractional seconds component. For the other data types, this is the decimal digits of the column on the data source. For interval data types that contain a time component, this column contains the number of digits to the right of the decimal point (fractional seconds). For interval data types that do not contain a time component, this column is 0. For more information about decimal digits, see "<A HREF="odbccolumn_size__decimal_digits__transfer_octet_length__and_display_size.htm">Column Size, Decimal Digits, Transfer Octet Length, and Display Size</A>" in Appendix D: Data Types. NULL is returned for data types where DECIMAL_DIGITS is not applicable.</TD>
+# </TR>
+ { name => "decimal_digits",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>NUM_PREC_RADIX<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>10</TD>
+# <TD width=16%>Smallint</TD>
+# <TD width=43%>For numeric data types, either 10 or 2. If it is 10, the values in COLUMN_SIZE and DECIMAL_DIGITS give the number of decimal digits allowed for the column. For example, a DECIMAL(12,5) column would return a NUM_PREC_RADIX of 10, a COLUMN_SIZE of 12, and a DECIMAL_DIGITS of 5; a FLOAT column could return a NUM_PREC_RADIX of 10, a COLUMN_SIZE of 15, and a DECIMAL_DIGITS of NULL.
+# <P>If it is 2, the values in COLUMN_SIZE and DECIMAL_DIGITS give the number of bits allowed in the column. For example, a FLOAT column could return a RADIX of 2, a COLUMN_SIZE of 53, and a DECIMAL_DIGITS of NULL.</P>
+#
+# <P>NULL is returned for data types where NUM_PREC_RADIX is not applicable.</P>
+# </TD>
+# </TR>
+ { name => "num_prec_radix",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>NULLABLE<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>11</TD>
+# <TD width=16%>Smallint not NULL</TD>
+# <TD width=43%>SQL_NO_NULLS if the column could not include NULL values.
+# <P>SQL_NULLABLE if the column accepts NULL values.</P>
+#
+# <P>SQL_NULLABLE_UNKNOWN if it is not known whether the column accepts NULL values.</P>
+#
+# <P>The value returned for this column is different from the value returned for the IS_NULLABLE column. The NULLABLE column indicates with certainty that a column can accept NULLs, but cannot indicate with certainty that a column does not accept NULLs. The IS_NULLABLE column indicates with certainty that a column cannot accept NULLs, but cannot indicate with certainty that a column accepts NULLs.</P>
+# </TD>
+# </TR>
+ { name => "nullable",
+ type => "smallint",
+ length => 16,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>REMARKS<BR>
+# (ODBC 1.0)</TD>
+# <TD width=17%>12</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=43%>A description of the column.</TD>
+# </TR>
+ { name => "remarks",
+ type => "varchar",
+ length => 200,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>COLUMN_DEF<BR>
+# (ODBC 3.0)</TD>
+# <TD width=17%>13</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=43%>The default value of the column. The value in this column should be interpreted as a string if it is enclosed in quotation marks.
+# <P>If NULL was specified as the default value, then this column is the word NULL, not enclosed in quotation marks. If the default value cannot be represented without truncation, then this column contains TRUNCATED, with no enclosing single quotation marks. If no default value was specified, then this column is NULL.</P>
+#
+# <P>The value of COLUMN_DEF can be used in generating a new column definition, except when it contains the value TRUNCATED.</P>
+# </TD>
+# </TR>
+ { name => "column_def",
+ type => "varchar",
+ length => 100,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>SQL_DATA_TYPE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=17%>14</TD>
+# <TD width=16%>Smallint not NULL</TD>
+# <TD width=43%>SQL data type, as it appears in the SQL_DESC_TYPE record field in the IRD. This can be an ODBC SQL data type or a driver-specific SQL data type. This column is the same as the DATA_TYPE column, with the exception of datetime and interval data types. This column returns the nonconcise data type (such as SQL_DATETIME or SQL_INTERVAL), rather than the concise data type (such as SQL_TYPE_DATE or SQL_INTERVAL_YEAR_TO_MONTH) for datetime and interval data types. If this column returns SQL_DATETIME or SQL_INTERVAL, the specific data type can be determined from the SQL_DATETIME_SUB column. For a list of valid ODBC SQL data types, see "<A HREF="odbcsql_data_types.htm">SQL Data Types</A>" in Appendix D: Data Types. For information about driver-specific SQL data types, see the driver's documentation.
+# <P>The data types returned for ODBC 3.<I>x</I> and ODBC 2.<I>x</I> applications may be different. For more information, see "<A HREF="odbcbackward_compatibility_and_standards_compliance.htm">Backward Compatibility and Standards Compliance</A>" in Chapter 17: Programming Considerations.</P>
+# </TD>
+# </TR>
+ { name => "sql_data_type",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>SQL_DATETIME_SUB<BR>
+# (ODBC 3.0)</TD>
+# <TD width=17%>15</TD>
+# <TD width=16%>Smallint</TD>
+# <TD width=43%>The subtype code for datetime and interval data types. For other data types, this column returns a NULL. For more information about datetime and interval subcodes, see "SQL_DESC_DATETIME_INTERVAL_CODE" in <A HREF="odbcsqlsetdescfield.htm">SQLSetDescField</A>.</TD>
+# </TR>
+ { name => "sql_datetime_sub",
+ type => "smallint",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>CHAR_OCTET_LENGTH<BR>
+# (ODBC 3.0)</TD>
+# <TD width=17%>16</TD>
+# <TD width=16%>Integer</TD>
+# <TD width=43%>The maximum length in bytes of a character or binary data type column. For all other data types, this column returns a NULL.</TD>
+# </TR>
+ { name => "char_octet_length",
+ type => "integer",
+ length => undef,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>ORDINAL_POSITION<BR>
+# (ODBC 3.0)</TD>
+# <TD width=17%>17</TD>
+# <TD width=16%>Integer not NULL</TD>
+# <TD width=43%>The ordinal position of the column in the table. The first column in the table is number 1.</TD>
+# </TR>
+ { name => "ordinal_position",
+ type => "integer",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=24%>IS_NULLABLE<BR>
+# (ODBC 3.0)</TD>
+# <TD width=17%>18</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=43%>"NO" if the column does not include NULLs.
+# <P>"YES" if the column could include NULLs.</P>
+#
+# <P>This column returns a zero-length string if nullability is unknown. </P>
+#
+# <P>ISO rules are followed to determine nullability. An ISO SQL&#0150;compliant DBMS cannot return an empty string. </P>
+#
+# <P>The value returned for this column is different from the value returned for the NULLABLE column. (See the description of the NULLABLE column.)</P>
+# </TD>
+# </TR>
+ { name => "is_nullable",
+ type => "varchar",
+ length => 3,
+ nullable => 1,
+ },
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Code Example</B></P>
+#
+# <P>In the following example, an application declares buffers for the result set returned by <B>SQLColumns</B>. It calls <B>SQLColumns</B> to return a result set that describes each column in the EMPLOYEE table. It then calls <B>SQLBindCol</B> to bind the columns in the result set to the buffers. Finally, the application fetches each row of data with <B>SQLFetch</B> and processes it.</P>
+#
+# <PRE class="code">#define STR_LEN 128+1
+# #define REM_LEN 254+1
+#
+# /* Declare buffers for result set data */
+#
+# SQLCHAR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szCatalog[STR_LEN], szSchema[STR_LEN];
+# SQLCHAR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szTableName[STR_LEN], szColumnName[STR_LEN];
+# SQLCHAR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szTypeName[STR_LEN], szRemarks[REM_LEN];
+# SQLCHAR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szColumnDefault[STR_LEN], szIsNullable[STR_LEN];
+# SQLINTEGER&nbsp;&nbsp;&nbsp;&nbsp;ColumnSize, BufferLength, CharOctetLength, OrdinalPosition;
+# SQLSMALLINT&nbsp;&nbsp;&nbsp;DataType, DecimalDigits, NumPrecRadix, Nullable;
+# SQLSMALLINT&nbsp;&nbsp;&nbsp;SQLDataType, DatetimeSubtypeCode;
+# SQLRETURN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retcode;
+# SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hstmt;
+#
+# /* Declare buffers for bytes available to return */
+#
+# SQLINTEGER cbCatalog, cbSchema, cbTableName, cbColumnName;
+# SQLINTEGER cbDataType, cbTypeName, cbColumnSize, cbBufferLength;
+# SQLINTEGER cbDecimalDigits, cbNumPrecRadix, cbNullable, cbRemarks;
+# SQLINTEGER cbColumnDefault, cbSQLDataType, cbDatetimeSubtypeCode, cbCharOctetLength;
+# SQLINTEGER cbOrdinalPosition, cbIsNullable;
+#
+# retcode = SQLColumns(hstmt,
+# NULL, 0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* All catalogs */
+# NULL, 0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* All schemas */
+# "CUSTOMERS", SQL_NTS,&nbsp;&nbsp;&nbsp;/* CUSTOMERS table */
+# NULL, 0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* All columns */
+#
+# if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+#
+# /* Bind columns in result set to buffers */
+#
+# SQLBindCol(hstmt, 1, SQL_C_CHAR, szCatalog, STR_LEN,&amp;cbCatalog);
+# SQLBindCol(hstmt, 2, SQL_C_CHAR, szSchema, STR_LEN, &amp;cbSchema);
+# SQLBindCol(hstmt, 3, SQL_C_CHAR, szTableName, STR_LEN,&amp;cbTableName);
+# SQLBindCol(hstmt, 4, SQL_C_CHAR, szColumnName, STR_LEN, &amp;cbColumnName);
+# SQLBindCol(hstmt, 5, SQL_C_SSHORT, &amp;DataType, 0, &amp;cbDataType);
+# SQLBindCol(hstmt, 6, SQL_C_CHAR, szTypeName, STR_LEN, &amp;cbTypeName);
+# SQLBindCol(hstmt, 7, SQL_C_SLONG, &amp;ColumnSize, 0, &amp;cbColumnSize);
+# SQLBindCol(hstmt, 8, SQL_C_SLONG, &amp;BufferLength, 0, &amp;cbBufferLength);
+# SQLBindCol(hstmt, 9, SQL_C_SSHORT, &amp;DecimalDigits, 0, &amp;cbDecimalDigits);
+# SQLBindCol(hstmt, 10, SQL_C_SSHORT, &amp;NumPrecRadix, 0, &amp;cbNumPrecRadix);
+# SQLBindCol(hstmt, 11, SQL_C_SSHORT, &amp;Nullable, 0, &amp;cbNullable);
+# SQLBindCol(hstmt, 12, SQL_C_CHAR, szRemarks, REM_LEN, &amp;cbRemarks);
+# SQLBindCol(hstmt, 13, SQL_C_CHAR, szColumnDefault, STR_LEN, &amp;cbColumnDefault);
+# SQLBindCol(hstmt, 14, SQL_C_SSHORT, &amp;SQLDataType, 0, &amp;cbSQLDataType);
+# SQLBindCol(hstmt, 15, SQL_C_SSHORT, &amp;DatetimeSubtypeCode, 0,
+# &amp;cbDatetimeSubtypeCode);
+# SQLBindCol(hstmt, 16, SQL_C_SLONG, &amp;CharOctetLength, 0, &amp;cbCharOctetLength);
+# SQLBindCol(hstmt, 17, SQL_C_SLONG, &amp;OrdinalPosition, 0, &amp;cbOrdinalPosition);
+# SQLBindCol(hstmt, 18, SQL_C_CHAR, szIsNullable, STR_LEN, &amp;cbIsNullable);
+# while(TRUE) {
+# retcode = SQLFetch(hstmt);
+# if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+# show_error( );
+# }
+# if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
+# ; /* Process fetched data */
+# } else {
+# break;
+# }
+# }
+# }</PRE>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>For information about</TH>
+# <TH width=50%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Binding a buffer to a column in a result set</TD>
+# <TD width=50%><A HREF="odbcsqlbindcol.htm">SQLBindCol</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Canceling statement processing</TD>
+# <TD width=50%><A HREF="odbcsqlcancel.htm">SQLCancel</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning privileges for a column or columns</TD>
+# <TD width=50%><A HREF="odbcsqlcolumnprivileges.htm">SQLColumnPrivileges</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a block of data or scrolling through a result set</TD>
+# <TD width=50%><A HREF="odbcsqlfetchscroll.htm">SQLFetchScroll</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching multiple rows of data</TD>
+# <TD width=50%><A HREF="odbcsqlfetch.htm">SQLFetch</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning columns that uniquely identify a row, or columns automatically updated by a transaction</TD>
+# <TD width=50%><A HREF="odbcsqlspecialcolumns.htm">SQLSpecialColumns</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning table statistics and indexes</TD>
+# <TD width=50%><A HREF="odbcsqlstatistics.htm">SQLStatistics</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning a list of tables in a data source</TD>
+# <TD width=50%><A HREF="odbcsqltables.htm">SQLTables</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning privileges for a table or tables</TD>
+# <TD width=50%><A HREF="odbcsqltableprivileges.htm">SQLTablePrivileges</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+];
+
+#
+# odbcsqlprimarykeys.htm
+#
+$listWhat->{primarykeys} = [
+# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+# <HTML DIR="LTR"><HEAD>
+# <META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
+# <TITLE>SQLPrimaryKeys</TITLE>
+# <SCRIPT SRC="/stylesheets/vs70link.js"></SCRIPT>
+# <SCRIPT SRC="/stylesheets/vs70.js"></SCRIPT>
+# <SCRIPT LANGUAGE="JScript" SRC="/stylesheets/odbc.js"></SCRIPT>
+# </HEAD>
+# <body topmargin=0 id="bodyID">
+#
+# <div id="nsbanner">
+# <div id="bannertitle">
+# <TABLE CLASS="bannerparthead" CELLSPACING=0>
+# <TR ID="hdr">
+# <TD CLASS="bannertitle" nowrap>
+# ODBC Programmer's Reference
+# </TD><TD valign=middle><a href="#Feedback"><IMG name="feedb" onclick=EMailStream(SDKFeedB) style="CURSOR: hand;" hspace=15 alt="" src="/stylesheets/mailto.gif" align=right></a></TD>
+# </TR>
+# </TABLE>
+# </div>
+# </div>
+# <DIV id="nstext" valign="bottom">
+#
+# <H1><A NAME="odbcsqlprimarykeys"></A>SQLPrimaryKeys</H1>
+#
+# <P class="label"><B>Conformance</B></P>
+#
+# <P>Version Introduced: ODBC 1.0<BR>
+# Standards Compliance: ODBC</P>
+#
+# <P class="label"><B>Summary</B></P>
+#
+# <P><B>SQLPrimaryKeys</B> returns the column names that make up the primary key for a table. The driver returns the information as a result set. This function does not support returning primary keys from multiple tables in a single call.</P>
+#
+# <P class="label"><B>Syntax</B></P>
+#
+# <PRE class="syntax">SQLRETURN <B>SQLPrimaryKeys</B>(
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLHSTMT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>StatementHandle</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>CatalogName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength1</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>SchemaName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength2</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLCHAR *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>TableName</I>,
+# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I>NameLength3</I>);</PRE>
+#
+# <P class="label"><B>Arguments</B>
+#
+# <DL>
+# <DT><I>StatementHandle</I></DT>
+#
+# <DD>[Input]<BR>
+# Statement handle.</dd>
+#
+# <DT><I>CatalogName</I></DT>
+#
+# <DD>[Input]<BR>
+# Catalog name. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have catalogs. <I>CatalogName </I>cannot contain a string search pattern.
+#
+# <P>If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>CatalogName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>CatalogName</I> is an ordinary argument; it is treated literally, and its case is significant. For more information, see "<A HREF="odbcarguments_in_catalog_functions.htm">Arguments in Catalog Functions</A>" in Chapter 7: Catalog Functions.
+# </dd>
+#
+# <DT><I>NameLength1</I></DT>
+#
+# <DD>[Input]<BR>
+# Length in bytes of *<I>CatalogName</I>.</dd>
+#
+# <DT><I>SchemaName</I></DT>
+#
+# <DD>[Input]<BR>
+# Schema name. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, an empty string ("") denotes those tables that do not have schemas. <I>SchemaName </I>cannot contain a string search pattern.
+#
+# <P>If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>SchemaName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>SchemaName</I> is an ordinary argument; it is treated literally, and its case is not significant.
+# </dd>
+#
+# <DT><I>NameLength2</I></DT>
+#
+# <DD>[Input]<BR>
+# Length in bytes of *<I>SchemaName</I>.</dd>
+#
+# <DT><I>TableName</I></DT>
+#
+# <DD>[Input]<BR>
+# Table name. This argument cannot be a null pointer. <I>TableName </I>cannot contain a string search pattern.
+#
+# <P>If the SQL_ATTR_METADATA_ID statement attribute is set to SQL_TRUE, <I>TableName</I> is treated as an identifier and its case is not significant. If it is SQL_FALSE, <I>TableName</I> is an ordinary argument; it is treated literally, and its case is not significant.
+# </dd>
+#
+# <DT><I>NameLength3</I></DT>
+#
+# <DD>[Input]<BR>
+# Length in bytes of *<I>TableName</I>.</dd>
+# </DL>
+#
+# <P class="label"><B>Returns</B></P>
+#
+# <P>SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE.</P>
+#
+# <P class="label"><B>Diagnostics</B></P>
+#
+# <P>When <B>SQLPrimaryKeys</B> returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an associated SQLSTATE value can be obtained by calling <B>SQLGetDiagRec</B> with a <I>HandleType</I> of SQL_HANDLE_STMT and a <I>Handle</I> of <I>StatementHandle</I>. The following table lists the SQLSTATE values commonly returned by <B>SQLPrimaryKeys</B> and explains each one in the context of this function; the notation "(DM)" precedes the descriptions of SQLSTATEs returned by the Driver Manager. The return code associated with each SQLSTATE value is SQL_ERROR, unless noted otherwise.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=22%>SQLSTATE</TH>
+# <TH width=26%>Error</TH>
+# <TH width=52%>Description</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>01000</TD>
+# <TD width=26%>General warning</TD>
+# <TD width=52%>Driver-specific informational message. (Function returns SQL_SUCCESS_WITH_INFO.)</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>08S01</TD>
+# <TD width=26%>Communication link failure</TD>
+# <TD width=52%>The communication link between the driver and the data source to which the driver was connected failed before the function completed processing.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>24000</TD>
+# <TD width=26%>Invalid cursor state</TD>
+# <TD width=52%>(DM) A cursor was open on the <I>StatementHandle</I>, and <B>SQLFetch</B> or <B>SQLFetchScroll</B> had been called.
+# <P>A cursor was open on the <I>StatementHandle</I>, but <B>SQLFetch</B> or <B>SQLFetchScroll</B> had not been called.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40001</TD>
+# <TD width=26%>Serialization failure</TD>
+# <TD width=52%>The transaction was rolled back due to a resource deadlock with another transaction.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>40003</TD>
+# <TD width=26%>Statement completion unknown</TD>
+# <TD width=52%>The associated connection failed during the execution of this function, and the state of the transaction cannot be determined.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY000</TD>
+# <TD width=26%>General error</TD>
+# <TD width=52%>An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by <B>SQLGetDiagRec</B> in the <I>*MessageText</I> buffer describes the error and its cause.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY001</TD>
+# <TD width=26%>Memory allocation error</TD>
+# <TD width=52%>The driver was unable to allocate memory required to support execution or completion of the function.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY008</TD>
+# <TD width=26%>Operation canceled</TD>
+# <TD width=52%>Asynchronous processing was enabled for the <I>StatementHandle</I>. The function was called, and before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I>. Then the function was called again on the <I>StatementHandle</I>.
+# <P>The function was called, and before it completed execution, <B>SQLCancel</B> was called on the <I>StatementHandle</I> from a different thread in a multithread application.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY009</TD>
+# <TD width=26%>Invalid use of null pointer</TD>
+# <TD width=52%>(DM) The <I>TableName</I> argument was a null pointer.
+# <P>The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, the <I>CatalogName</I> argument was a null pointer, and <B>SQLGetInfo</B> with the SQL_CATALOG_NAME information type returns that catalog names are supported.</P>
+#
+# <P>(DM) The SQL_ATTR_METADATA_ID statement attribute was set to SQL_TRUE, and the <I>SchemaName</I> argument was a null pointer.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY010</TD>
+# <TD width=26%>Function sequence error</TD>
+# <TD width=52%>(DM) An asynchronously executing function (not this one) was called for the <I>StatementHandle</I> and was still executing when this function was called.
+# <P>(DM) <B>SQLExecute</B>, <B>SQLExecDirect</B>, <B>SQLBulkOperations</B>, or <B>SQLSetPos</B> was called for the <I>StatementHandle</I> and returned SQL_NEED_DATA. This function was called before data was sent for all data-at-execution parameters or columns.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY013</TD>
+# <TD width=26%>Memory management error</TD>
+# <TD width=52%>The function call could not be processed because the underlying memory objects could not be accessed, possibly because of low memory conditions.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HY090</TD>
+# <TD width=26%>Invalid string or buffer length</TD>
+# <TD width=52%>(DM) The value of one of the name length arguments was less than 0 but not equal to SQL_NTS, and the associated name argument is not a null pointer.
+# <P>The value of one of the name length arguments exceeded the maximum length value for the corresponding name.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYC00</TD>
+# <TD width=26%>Optional feature not implemented</TD>
+# <TD width=52%>A catalog was specified, and the driver or data source does not support catalogs.
+# <P>A schema was specified and the driver or data source does not support schemas.</P>
+#
+# <P>The combination of the current settings of the SQL_ATTR_CONCURRENCY and SQL_ATTR_CURSOR_TYPE statement attributes was not supported by the driver or data source.</P>
+#
+# <P>The SQL_ATTR_USE_BOOKMARKS statement attribute was set to SQL_UB_VARIABLE, and the SQL_ATTR_CURSOR_TYPE statement attribute was set to a cursor type for which the driver does not support bookmarks.</P>
+# </TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT00</TD>
+# <TD width=26%>Timeout expired</TD>
+# <TD width=52%>The timeout period expired before the data source returned the requested result set. The timeout period is set through <B>SQLSetStmtAttr</B>, SQL_ATTR_QUERY_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>HYT01</TD>
+# <TD width=26%>Connection timeout expired</TD>
+# <TD width=52%>The connection timeout period expired before the data source responded to the request. The connection timeout period is set through <B>SQLSetConnectAttr</B>, SQL_ATTR_CONNECTION_TIMEOUT.</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=22%>IM001</TD>
+# <TD width=26%>Driver does not support this function</TD>
+# <TD width=52%>(DM) The driver associated with the <I>StatementHandle</I> does not support the function.</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Comments</B></P>
+#
+# <P><B>SQLPrimaryKeys</B> returns the results as a standard result set, ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and KEY_SEQ. For information about how this information might be used, see "<A HREF="odbcuses_of_catalog_data.htm">Uses of Catalog Data</A>" in Chapter 7: Catalog Functions.</P>
+#
+# <P>The following columns have been renamed for ODBC 3.<I>x</I>. The column name changes do not affect backward compatibility because applications bind by column number.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=48%>ODBC 2.0 column</TH>
+# <TH width=52%>ODBC 3.<I>x</I> column</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>TABLE_QUALIFIER</TD>
+# <TD width=52%>TABLE_CAT</TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=48%>TABLE_OWNER</TD>
+# <TD width=52%>TABLE_SCHEM</TD>
+# </TR>
+# </table></div>
+# <!--TS:-->
+# <P>To determine the actual lengths of the TABLE_CAT, TABLE_SCHEM, TABLE_NAME, and COLUMN_NAME columns, call <B>SQLGetInfo</B> with the SQL_MAX_CATALOG_NAME_LEN, SQL_MAX_SCHEMA_NAME_LEN, SQL_MAX_TABLE_NAME_LEN, and SQL_MAX_COLUMN_NAME_LEN options.</P>
+#
+# <P class="indent"><b class="le">Note</b>&nbsp;&nbsp;&nbsp;For more information about the general use, arguments, and returned data of ODBC catalog functions, see <A HREF="odbccatalog_functions.htm">Chapter 7: Catalog Functions</A>.</P>
+#
+# <P>The following table lists the columns in the result set. Additional columns beyond column 6 (PK_NAME) can be defined by the driver. An application should gain access to driver-specific columns by counting down from the end of the result set rather than specifying an explicit ordinal position. For more information, see "<A HREF="odbcdata_returned_by_catalog_functions.htm">Data Returned by Catalog Functions</A>" in Chapter 7: Catalog Functions.</P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=27%><BR>
+# Column name</TH>
+# <TH width=15%>Column number</TH>
+# <TH width=16%><BR>
+# Data type</TH>
+# <TH width=42%><BR>
+# Comments</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_CAT<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>1</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Primary key table catalog name; NULL if not applicable to the data source. If a driver supports catalogs for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have catalogs.</TD>
+# </TR>
+ { name => "table_cat",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_SCHEM<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>2</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Primary key table schema name; NULL if not applicable to the data source. If a driver supports schemas for some tables but not for others, such as when the driver retrieves data from different DBMSs, it returns an empty string ("") for those tables that do not have schemas.</TD>
+# </TR>
+ { name => "table_schem",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>TABLE_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>3</TD>
+# <TD width=16%>Varchar<BR>
+# not NULL</TD>
+# <TD width=42%>Primary key table name.</TD>
+# </TR>
+ { name => "table_name",
+ type => "varchar",
+ length => 16,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>COLUMN_NAME<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>4</TD>
+# <TD width=16%>Varchar<BR>
+# not NULL</TD>
+# <TD width=42%>Primary key column name. The driver returns an empty string for a column that does not have a name.</TD>
+# </TR>
+ { name => "column_name",
+ type => "varchar",
+ length => 16,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>KEY_SEQ<BR>
+# (ODBC 1.0)</TD>
+# <TD width=15%>5</TD>
+# <TD width=16%>Smallint<BR>
+# not NULL</TD>
+# <TD width=42%>Column sequence number in key (starting with 1).</TD>
+# </TR>
+ { name => "key_seq",
+ type => "smallint",
+ length => undef,
+ nullable => 0,
+ },
+#
+# <TR VALIGN="top">
+# <TD width=27%>PK_NAME<BR>
+# (ODBC 2.0)</TD>
+# <TD width=15%>6</TD>
+# <TD width=16%>Varchar</TD>
+# <TD width=42%>Primary key name. NULL if not applicable to the data source.</TD>
+# </TR>
+ { name => "pk_name",
+ type => "varchar",
+ length => 16,
+ nullable => 1,
+ },
+# </table></div>
+# <!--TS:-->
+# <P class="label"><B>Code Example</B></P>
+#
+# <P>See <A HREF="odbcsqlforeignkeys.htm">SQLForeignKeys</A>.</P>
+#
+# <P class="label"><B>Related Functions</B></P>
+# <!--TS:--><div class="tablediv"><table>
+#
+# <TR VALIGN="top">
+# <TH width=50%>For information about</TH>
+# <TH width=50%>See</TH>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Binding a buffer to a column in a result set</TD>
+# <TD width=50%><A HREF="odbcsqlbindcol.htm">SQLBindCol</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Canceling statement processing</TD>
+# <TD width=50%><A HREF="odbcsqlcancel.htm">SQLCancel</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a block of data or scrolling through a result set</TD>
+# <TD width=50%><A HREF="odbcsqlfetchscroll.htm">SQLFetchScroll</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Fetching a single row or a block of data in a forward-only direction</TD>
+# <TD width=50%><A HREF="odbcsqlfetch.htm">SQLFetch</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning the columns of foreign keys</TD>
+# <TD width=50%><A HREF="odbcsqlforeignkeys.htm">SQLForeignKeys</A></TD>
+# </TR>
+#
+# <TR VALIGN="top">
+# <TD width=50%>Returning table statistics and indexes</TD>
+# <TD width=50%><A HREF="odbcsqlstatistics.htm">SQLStatistics</A></TD>
+# </TR>
+# </table></div>
+# <!--TS:--><H4><A NAME="feedback"></A></H4>
+# <SPAN id="SDKFeedB"></SPAN>
+# </div>
+#
+# </BODY>
+# </HTML>
+];
+
+my $list = $listWhat->{$what} or die "$what?";
+my $i4 = " " x 4;
+if ($opt eq '-l') {
+ print join(", ", map($_->{name}, @$list)), "\n";
+ exit;
+}
+if ($opt eq '-c') {
+ my $pos = 0;
+ for my $p (@$list) {
+ print "${i4}ConnSys::Column::Column(\n";
+ $pos++;
+ print "\t$pos,\n";
+ print "\t\"" . uc($p->{name}) . "\",\n";
+ print "\tfalse,\n";
+ print "\tNdbType(NdbType::";
+ if ($p->{type} eq 'varchar') {
+ print "String, 8, $p->{length}";
+ } else {
+ print "Signed, 32, 1";
+ }
+ print ", " . ($p->{nullable} ? "true" : "false");
+ print ")\n";
+ print "${i4}),\n";
+ }
+ exit;
+}
+print "$opt?\n";
+
+# vim: set sw=4:
diff --git a/storage/ndb/src/old_files/client/odbc/docs/type.txt b/storage/ndb/src/old_files/client/odbc/docs/type.txt
new file mode 100644
index 00000000000..d7b391afc55
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/docs/type.txt
@@ -0,0 +1,333 @@
+ODBC Programmer's Reference
+****** SQL Data Types ******
+Each DBMS defines its own SQL types. Each ODBC driver exposes only those SQL
+data types that the associated DBMS defines. How a driver maps DBMS SQL types
+to the ODBC-defined SQL type identifiers and how a driver maps DBMS SQL types
+to its own driver-specific SQL type identifiers are returned through a call to
+SQLGetTypeInfo. A driver also returns the SQL data types when describing the
+data types of columns and parameters through calls to SQLColAttribute,
+SQLColumns, SQLDescribeCol, SQLDescribeParam, SQLProcedureColumns, and
+SQLSpecialColumns.
+Note The SQL data types are contained in the SQL_DESC_ CONCISE_TYPE,
+SQL_DESC_TYPE, and SQL_DESC_DATETIME_INTERVAL_CODE fields of the implementation
+descriptors. Characteristics of the SQL data types are contained in the
+SQL_DESC_PRECISION, SQL_DESC_SCALE, SQL_DESC_LENGTH, and SQL_DESC_OCTET_LENGTH
+fields of the implementation descriptors. For more information, see "Data_Type
+Identifiers_and_Descriptors" later in this appendix.
+A given driver and data source do not necessarily support all of the SQL data
+types defined in this appendix. A driver's support for SQL data types depends
+on the level of SQL-92 that the driver conforms to. To determine the level of
+SQL-92 grammar supported by the driver, an application calls SQLGetInfo with
+the SQL_SQL_CONFORMANCE information type. Furthermore, a given driver and data
+source may support additional, driver-specific SQL data types. To determine
+which data types a driver supports, an application calls SQLGetTypeInfo. For
+information about driver-specific SQL data types, see the driver's
+documentation. For information about the data types in a specific data source,
+see the documentation for that data source.
+Important The tables throughout this appendix are only guidelines and show
+commonly used names, ranges, and limits of SQL data types. A given data source
+might support only some of the listed data types, and the characteristics of
+the supported data types can differ from those listed.
+The following table lists valid SQL type identifiers for all SQL data types.
+The table also lists the name and description of the corresponding data type
+from SQL-92 (if one exists).
+SQL type identifier[1] Typical SQL data Typical type description
+ type[2]
+SQL_CHAR CHAR(n) Character string of fixed
+ string length n.
+SQL_VARCHAR VARCHAR(n) Variable-length character
+ string with a maximum
+ string length n.
+SQL_LONGVARCHAR LONG VARCHAR Variable length character
+ data. Maximum length is
+ data source–dependent.[9]
+SQL_WCHAR WCHAR(n) Unicode character string
+ of fixed string length n
+SQL_WVARCHAR VARWCHAR(n) Unicode variable-length
+ character string with a
+ maximum string length n
+SQL_WLONGVARCHAR LONGWVARCHAR Unicode variable-length
+ character data. Maximum
+ length is data
+ source–dependent
+SQL_DECIMAL DECIMAL(p,s) Signed, exact, numeric
+ value with a precision of
+ at least p and scale s.
+ (The maximum precision is
+ driver-defined.)
+ (1 <= p <= 15; s <= p).
+ [4]
+SQL_NUMERIC NUMERIC(p,s) Signed, exact, numeric
+ value with a precision p
+ and scale s
+ (1 <= p <= 15; s <= p).
+ [4]
+SQL_SMALLINT SMALLINT Exact numeric value with
+ precision 5 and scale 0
+ (signed:
+ –32,768 <= n <= 32,767,
+ unsigned:
+ 0 <= n <= 65,535)[3].
+SQL_INTEGER INTEGER Exact numeric value with
+ precision 10 and scale 0
+ (signed:
+ –2[31] <= n <= 2[31] – 1,
+ unsigned:
+ 0 <= n <= 2[32] – 1)[3].
+SQL_REAL REAL Signed, approximate,
+ numeric value with a
+ binary precision 24 (zero
+ or absolute value 10[–38]
+ to 10[38]).
+SQL_FLOAT FLOAT(p) Signed, approximate,
+ numeric value with a
+ binary precision of at
+ least p. (The maximum
+ precision is driver-
+ defined.)[5]
+SQL_DOUBLE DOUBLE PRECISION Signed, approximate,
+ numeric value with a
+ binary precision 53 (zero
+ or absolute value 10
+ [–308] to 10[308]).
+SQL_BIT BIT Single bit binary data.
+ [8]
+SQL_TINYINT TINYINT Exact numeric value with
+ precision 3 and scale 0
+ (signed:
+ –128 <= n <= 127,
+ unsigned:
+ 0 <= n <= 255)[3].
+SQL_BIGINT BIGINT Exact numeric value with
+ precision 19 (if signed)
+ or 20 (if unsigned) and
+ scale 0
+ (signed:
+ –2[63] <= n <= 2[63] – 1,
+
+ unsigned:
+ 0 <= n <= 2[64] – 1)[3],
+ [9].
+SQL_BINARY BINARY(n) Binary data of fixed
+ length n.[9]
+SQL_VARBINARY VARBINARY(n) Variable length binary
+ data of maximum length n.
+ The maximum is set by the
+ user.[9]
+SQL_LONGVARBINARY LONG VARBINARY Variable length binary
+ data. Maximum length is
+ data source–dependent.[9]
+SQL_TYPE_DATE[6] DATE Year, month, and day
+ fields, conforming to the
+ rules of the Gregorian
+ calendar. (See
+ "Constraints_of_the
+ Gregorian_Calendar,"
+ later in this appendix.)
+SQL_TYPE_TIME[6] TIME(p) Hour, minute, and second
+ fields, with valid values
+ for hours of 00 to 23,
+ valid values for minutes
+ of 00 to 59, and valid
+ values for seconds of 00
+ to 61. Precision p
+ indicates the seconds
+ precision.
+SQL_TYPE_TIMESTAMP[6] TIMESTAMP(p) Year, month, day, hour,
+ minute, and second
+ fields, with valid values
+ as defined for the DATE
+ and TIME data types.
+SQL_INTERVAL_MONTH[7] INTERVAL MONTH(p) Number of months between
+ two dates; p is the
+ interval leading
+ precision.
+SQL_INTERVAL_YEAR[7] INTERVAL YEAR(p) Number of years between
+ two dates; p is the
+ interval leading
+ precision.
+SQL_INTERVAL_YEAR_TO_MONTH[7] INTERVAL YEAR(p) TO Number of years and
+ MONTH months between two dates;
+ p is the interval leading
+ precision.
+SQL_INTERVAL_DAY[7] INTERVAL DAY(p) Number of days between
+ two dates; p is the
+ interval leading
+ precision.
+SQL_INTERVAL_HOUR[7] INTERVAL HOUR(p) Number of hours between
+ two date/times; p is the
+ interval leading
+ precision.
+SQL_INTERVAL_MINUTE[7] INTERVAL MINUTE(p) Number of minutes between
+ two date/times; p is the
+ interval leading
+ precision.
+SQL_INTERVAL_SECOND[7] INTERVAL SECOND(p,q) Number of seconds between
+ two date/times; p is the
+ interval leading
+ precision and q is the
+ interval seconds
+ precision.
+SQL_INTERVAL_DAY_TO_HOUR[7] INTERVAL DAY(p) TO HOUR Number of days/hours
+ between two date/times; p
+ is the interval leading
+ precision.
+SQL_INTERVAL_DAY_TO_MINUTE[7] INTERVAL DAY(p) TO Number of days/hours/
+ MINUTE minutes between two date/
+ times; p is the interval
+ leading precision.
+SQL_INTERVAL_DAY_TO_SECOND[7] INTERVAL DAY(p) TO Number of days/hours/
+ SECOND(q) minutes/seconds between
+ two date/times; p is the
+ interval leading
+ precision and q is the
+ interval seconds
+ precision.
+SQL_INTERVAL_HOUR_TO_MINUTE INTERVAL HOUR(p) TO Number of hours/minutes
+[7] MINUTE between two date/times; p
+ is the interval leading
+ precision.
+SQL_INTERVAL_HOUR_TO_SECOND INTERVAL HOUR(p) TO Number of hours/minutes/
+[7] SECOND(q) seconds between two date/
+ times; p is the interval
+ leading precision and q
+ is the interval seconds
+ precision.
+SQL_INTERVAL_MINUTE_TO_SECOND INTERVAL MINUTE(p) TO Number of minutes/seconds
+[7] SECOND(q) between two date/times; p
+ is the interval leading
+ precision and q is the
+ interval seconds
+ precision.
+SQL_GUID GUID Fixed length Globally
+ Unique Identifier.
+[1] This is the value returned in the DATA_TYPE column by a call to
+SQLGetTypeInfo.
+[2] This is the value returned in the NAME and CREATE PARAMS column by a call
+to SQLGetTypeInfo. The NAME column returns the designation—for example,
+CHAR—while the CREATE PARAMS column returns a comma-separated list of creation
+parameters such as precision, scale, and length.
+[3] An application uses SQLGetTypeInfo or SQLColAttribute to determine if a
+particular data type or a particular column in a result set is unsigned.
+[4] SQL_DECIMAL and SQL_NUMERIC data types differ only in their precision.
+The precision of a DECIMAL(p,s) is an implementation-defined decimal precision
+that is no less than p, while the precision of a NUMERIC(p,s) is exactly equal
+to p.
+[5] Depending on the implementation, the precision of SQL_FLOAT can be either
+24 or 53: if it is 24, the SQL_FLOAT data type is the same as SQL_REAL; if it
+is 53, the SQL_FLOAT data type is the same as SQL_DOUBLE.
+[6] In ODBC 3.x, the SQL date, time, and timestamp data types are
+SQL_TYPE_DATE, SQL_TYPE_TIME, and SQL_TYPE_TIMESTAMP, respectively; in ODBC
+2.x, the data types are SQL_DATE, SQL_TIME, and SQL_TIMESTAMP.
+[7] For more information on the interval SQL data types, see the "Interval
+Data_Types" section, later in this appendix.
+[8] The SQL_BIT data type has different characteristics than the BIT type in
+SQL-92.
+[9] This data type has no corresponding data type in SQL-92.
+ODBC Programmer's Reference
+************ CC DDaattaa TTyyppeess ************
+ODBC C data types indicate the data type of C buffers used to store data in the
+application.
+All drivers must support all C data types. This is required because all drivers
+must support all C types to which SQL types that they support can be converted,
+and all drivers support at least one character SQL type. Because the character
+SQL type can be converted to and from all C types, all drivers must support all
+C types.
+The C data type is specified in the SSQQLLBBiinnddCCoolland SSQQLLGGeettDDaattaa functions with the
+TargetType argument and in the SSQQLLBBiinnddPPaarraammeetteerr function with the ValueType
+argument. It can also be specified by calling SSQQLLSSeettDDeessccFFiieelldd to set the
+SQL_DESC_CONCISE_TYPE field of an ARD or APD, or by calling SSQQLLSSeettDDeessccRReecc with
+the Type argument (and the SubType argument if needed) and the DescriptorHandle
+argument set to the handle of an ARD or APD.
+The following table lists valid type identifiers for the C data types. The
+table also lists the ODBC C data type that corresponds to each identifier and
+the definition of this data type.
+CC ttyyppee iiddeennttiiffiieerr OODDBBCC CC ttyyppeeddeeff CC ttyyppee
+SQL_C_CHAR SQLCHAR * unsigned char *
+SQL_C_SSHORT[j] SQLSMALLINT short int
+SQL_C_USHORT[j] SQLUSMALLINT unsigned short int
+SQL_C_SLONG[j] SQLINTEGER long int
+SQL_C_ULONG[j] SQLUINTEGER unsigned long int
+SQL_C_FLOAT SQLREAL float
+SQL_C_DOUBLE SQLDOUBLE, SQLFLOAT double
+SQL_C_BIT SQLCHAR unsigned char
+SQL_C_STINYINT[j] SQLSCHAR signed char
+SQL_C_UTINYINT[j] SQLCHAR unsigned char
+SQL_C_SBIGINT SQLBIGINT _int64[h]
+SQL_C_UBIGINT SQLUBIGINT unsigned _int64[h]
+SQL_C_BINARY SQLCHAR * unsigned char *
+SQL_C_BOOKMARK[i] BOOKMARK unsigned long int[d]
+SQL_C_VARBOOKMARK SQLCHAR * unsigned char *
+SQL_C_TYPE_DATE[c] SQL_DATE_STRUCT struct tagDATE_STRUCT {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ } DATE_STRUCT;[a]
+SQL_C_TYPE_TIME[c] SQL_TIME_STRUCT struct tagTIME_STRUCT {
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ } TIME_STRUCT;[a]
+SQL_C_TYPE_TIMESTAMP[c] SQL_TIMESTAMP_STRUCT struct tagTIMESTAMP_STRUCT {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;[b]
+ } TIMESTAMP_STRUCT;[a]
+SQL_C_NUMERIC SQL_NUMERIC_STRUCT struct tagSQL_NUMERIC_STRUCT {
+ SQLCHAR precision;
+ SQLSCHAR scale;
+ SQLCHAR sign[g];
+ SQLCHAR
+ val
+ [SQL_MAX_NUMERIC_L EN];
+ [e], [f]
+ } SQL_NUMERIC_STRUCT;
+SQL_C_GUID SQLGUID struct tagSQLGUID {
+ DWORD Data1;
+ WORD Data2;
+ WORD Data3;
+ BYTE Data4[8];
+ } SQLGUID;[k]
+All C interval data SQL_INTERVAL_STRUCT See the "_C_ _I_n_t_e_r_v_a_l_ _S_t_r_u_c_t_u_r_e"
+types section, later in this appendix.
+[a] The values of the year, month, day, hour, minute, and second fields in
+the datetime C data types must conform to the constraints of the Gregorian
+calendar. (See "_C_o_n_s_t_r_a_i_n_t_s_ _o_f_ _t_h_e_ _G_r_e_g_o_r_i_a_n_ _C_a_l_e_n_d_a_r" later in this appendix.)
+[b] The value of the fraction field is the number of billionths of a second
+and ranges from 0 through 999,999,999 (1 less than 1 billion). For example, the
+value of the fraction field for a half-second is 500,000,000, for a thousandth
+of a second (one millisecond) is 1,000,000, for a millionth of a second (one
+microsecond) is 1,000, and for a billionth of a second (one nanosecond) is 1.
+[c] In ODBC 2.x, the C date, time, and timestamp data types are SQL_C_DATE,
+SQL_C_TIME, and SQL_C_TIMESTAMP.
+[d] ODBC 3.x applications should use SQL_C_VARBOOKMARK, not SQL_C_BOOKMARK.
+When an ODBC 3.x application works with an ODBC 2.x driver, the ODBC 3.x Driver
+Manager will map SQL_C_VARBOOKMARK to SQL_C_BOOKMARK.
+[e] A number is stored in the val field of the SQL_NUMERIC_STRUCT structure
+as a scaled integer, in little endian mode (the leftmost byte being the least-
+significant byte). For example, the number 10.001 base 10, with a scale of 4,
+is scaled to an integer of 100010. Because this is 186AA in hexadecimal format,
+the value in SQL_NUMERIC_STRUCT would be "AA 86 01 00 00 … 00", with the number
+of bytes defined by the SQL_MAX_NUMERIC_LEN ##ddeeffiinnee.
+[f] The precision and scale fields of the SQL_C_NUMERIC data type are never
+used for input from an application, only for output from the driver to the
+application. When the driver writes a numeric value into the
+SQL_NUMERIC_STRUCT, it will use its own driver-specific default as the value
+for the precision field, and it will use the value in the SQL_DESC_SCALE field
+of the application descriptor (which defaults to 0) for the scale field. An
+application can provide its own values for precision and scale by setting the
+SQL_DESC_PRECISION and SQL_DESC_SCALE fields of the application descriptor.
+[g] The sign field is 1 if positive, 0 if negative.
+[h] _int64 might not be supplied by some compilers.
+[i] _SQL_C_BOOKMARK has been deprecated in ODBC 3.x.
+[j] _SQL_C_SHORT, SQL_C_LONG, and SQL_C_TINYINT have been replaced in ODBC by
+signed and unsigned types: SQL_C_SSHORT and SQL_C_USHORT, SQL_C_SLONG and
+SQL_C_ULONG, and SQL_C_STINYINT and SQL_C_UTINYINT. An ODBC 3.x driver that
+should work with ODBC 2.x applications should support SQL_C_SHORT, SQL_C_LONG,
+and SQL_C_TINYINT, because when they are called, the Driver Manager passes them
+through to the driver.
+[k] SQL_C_GUID can be converted only to SQL_CHAR or SQL_WCHAR.
diff --git a/storage/ndb/src/old_files/client/odbc/driver/Func.data b/storage/ndb/src/old_files/client/odbc/driver/Func.data
new file mode 100644
index 00000000000..c32671e1135
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/Func.data
@@ -0,0 +1,2822 @@
+$func = {
+ SQLAllocConnect => {
+ type => 'SQLRETURN',
+ name => 'SQLAllocConnect',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLHDBC',
+ ptr => 1,
+ name => 'ConnectionHandle',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLAllocEnv => {
+ type => 'SQLRETURN',
+ name => 'SQLAllocEnv',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 1,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLAllocHandle => {
+ type => 'SQLRETURN',
+ name => 'SQLAllocHandle',
+ param => [
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'HandleType',
+ index => 0,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 0,
+ name => 'InputHandle',
+ index => 1,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 1,
+ name => 'OutputHandle',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLAllocHandleStd => {
+ type => 'SQLRETURN',
+ name => 'SQLAllocHandleStd',
+ param => [
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'fHandleType',
+ index => 0,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 0,
+ name => 'hInput',
+ index => 1,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 1,
+ name => 'phOutput',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLAllocStmt => {
+ type => 'SQLRETURN',
+ name => 'SQLAllocStmt',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLHSTMT',
+ ptr => 1,
+ name => 'StatementHandle',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLBindCol => {
+ type => 'SQLRETURN',
+ name => 'SQLBindCol',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ColumnNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'TargetType',
+ index => 2,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'TargetValue',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 4,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StrLen_or_Ind',
+ index => 5,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLBindParam => {
+ type => 'SQLRETURN',
+ name => 'SQLBindParam',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ParameterNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ValueType',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ParameterType',
+ index => 3,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 0,
+ name => 'LengthPrecision',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ParameterScale',
+ index => 5,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'ParameterValue',
+ index => 6,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StrLen_or_Ind',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLBindParameter => {
+ type => 'SQLRETURN',
+ name => 'SQLBindParameter',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ipar',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'fParamType',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'fCType',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'fSqlType',
+ index => 4,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 0,
+ name => 'cbColDef',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ibScale',
+ index => 6,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'rgbValue',
+ index => 7,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'cbValueMax',
+ index => 8,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'pcbValue',
+ index => 9,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLBrowseConnect => {
+ type => 'SQLRETURN',
+ name => 'SQLBrowseConnect',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'hdbc',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szConnStrIn',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbConnStrIn',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szConnStrOut',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbConnStrOutMax',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pcbConnStrOut',
+ index => 5,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLBulkOperations => {
+ type => 'SQLRETURN',
+ name => 'SQLBulkOperations',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'Operation',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLCancel => {
+ type => 'SQLRETURN',
+ name => 'SQLCancel',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLCloseCursor => {
+ type => 'SQLRETURN',
+ name => 'SQLCloseCursor',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLColAttribute => {
+ type => 'SQLRETURN',
+ name => 'SQLColAttribute',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ColumnNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'FieldIdentifier',
+ index => 2,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'CharacterAttribute',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'StringLength',
+ index => 5,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'NumericAttribute',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLColAttributes => {
+ type => 'SQLRETURN',
+ name => 'SQLColAttributes',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'icol',
+ index => 1,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fDescType',
+ index => 2,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'rgbDesc',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbDescMax',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pcbDesc',
+ index => 5,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'pfDesc',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLColumnPrivileges => {
+ type => 'SQLRETURN',
+ name => 'SQLColumnPrivileges',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szCatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbCatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbSchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szTableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbTableName',
+ index => 6,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szColumnName',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbColumnName',
+ index => 8,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLColumns => {
+ type => 'SQLRETURN',
+ name => 'SQLColumns',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'CatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength1',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'SchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength2',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'TableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength3',
+ index => 6,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'ColumnName',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength4',
+ index => 8,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLConnect => {
+ type => 'SQLRETURN',
+ name => 'SQLConnect',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'ServerName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength1',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'UserName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength2',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'Authentication',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength3',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLCopyDesc => {
+ type => 'SQLRETURN',
+ name => 'SQLCopyDesc',
+ param => [
+ {
+ type => 'SQLHDESC',
+ ptr => 0,
+ name => 'SourceDescHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLHDESC',
+ ptr => 0,
+ name => 'TargetDescHandle',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLDataSources => {
+ type => 'SQLRETURN',
+ name => 'SQLDataSources',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Direction',
+ index => 1,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'ServerName',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength1',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'NameLength1',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'Description',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength2',
+ index => 6,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'NameLength2',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLDescribeCol => {
+ type => 'SQLRETURN',
+ name => 'SQLDescribeCol',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ColumnNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'ColumnName',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'NameLength',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'DataType',
+ index => 5,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 1,
+ name => 'ColumnSize',
+ index => 6,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'DecimalDigits',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'Nullable',
+ index => 8,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLDescribeParam => {
+ type => 'SQLRETURN',
+ name => 'SQLDescribeParam',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ipar',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pfSqlType',
+ index => 2,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 1,
+ name => 'pcbParamDef',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pibScale',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pfNullable',
+ index => 5,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLDisconnect => {
+ type => 'SQLRETURN',
+ name => 'SQLDisconnect',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLDriverConnect => {
+ type => 'SQLRETURN',
+ name => 'SQLDriverConnect',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'hdbc',
+ index => 0,
+ },
+ {
+ type => 'SQLHWND',
+ ptr => 0,
+ name => 'hwnd',
+ index => 1,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szConnStrIn',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbConnStrIn',
+ index => 3,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szConnStrOut',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbConnStrOutMax',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pcbConnStrOut',
+ index => 6,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fDriverCompletion',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLDrivers => {
+ type => 'SQLRETURN',
+ name => 'SQLDrivers',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'henv',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fDirection',
+ index => 1,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szDriverDesc',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbDriverDescMax',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pcbDriverDesc',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szDriverAttributes',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbDrvrAttrMax',
+ index => 6,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pcbDrvrAttr',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLEndTran => {
+ type => 'SQLRETURN',
+ name => 'SQLEndTran',
+ param => [
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'HandleType',
+ index => 0,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 0,
+ name => 'Handle',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'CompletionType',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLError => {
+ type => 'SQLRETURN',
+ name => 'SQLError',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 1,
+ },
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'Sqlstate',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'NativeError',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'MessageText',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 6,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'TextLength',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLExecDirect => {
+ type => 'SQLRETURN',
+ name => 'SQLExecDirect',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'StatementText',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'TextLength',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLExecute => {
+ type => 'SQLRETURN',
+ name => 'SQLExecute',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLExtendedFetch => {
+ type => 'SQLRETURN',
+ name => 'SQLExtendedFetch',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fFetchType',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'irow',
+ index => 2,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 1,
+ name => 'pcrow',
+ index => 3,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 1,
+ name => 'rgfRowStatus',
+ index => 4,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLFetch => {
+ type => 'SQLRETURN',
+ name => 'SQLFetch',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLFetchScroll => {
+ type => 'SQLRETURN',
+ name => 'SQLFetchScroll',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'FetchOrientation',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'FetchOffset',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLForeignKeys => {
+ type => 'SQLRETURN',
+ name => 'SQLForeignKeys',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szPkCatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbPkCatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szPkSchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbPkSchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szPkTableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbPkTableName',
+ index => 6,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szFkCatalogName',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbFkCatalogName',
+ index => 8,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szFkSchemaName',
+ index => 9,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbFkSchemaName',
+ index => 10,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szFkTableName',
+ index => 11,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbFkTableName',
+ index => 12,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLFreeConnect => {
+ type => 'SQLRETURN',
+ name => 'SQLFreeConnect',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLFreeEnv => {
+ type => 'SQLRETURN',
+ name => 'SQLFreeEnv',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLFreeHandle => {
+ type => 'SQLRETURN',
+ name => 'SQLFreeHandle',
+ param => [
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'HandleType',
+ index => 0,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 0,
+ name => 'Handle',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLFreeStmt => {
+ type => 'SQLRETURN',
+ name => 'SQLFreeStmt',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Option',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetConnectAttr => {
+ type => 'SQLRETURN',
+ name => 'SQLGetConnectAttr',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Attribute',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StringLength',
+ index => 4,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetConnectOption => {
+ type => 'SQLRETURN',
+ name => 'SQLGetConnectOption',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Option',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetCursorName => {
+ type => 'SQLRETURN',
+ name => 'SQLGetCursorName',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'CursorName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'NameLength',
+ index => 3,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetData => {
+ type => 'SQLRETURN',
+ name => 'SQLGetData',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ColumnNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'TargetType',
+ index => 2,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'TargetValue',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 4,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StrLen_or_Ind',
+ index => 5,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetDescField => {
+ type => 'SQLRETURN',
+ name => 'SQLGetDescField',
+ param => [
+ {
+ type => 'SQLHDESC',
+ ptr => 0,
+ name => 'DescriptorHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'RecNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'FieldIdentifier',
+ index => 2,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 4,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StringLength',
+ index => 5,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetDescRec => {
+ type => 'SQLRETURN',
+ name => 'SQLGetDescRec',
+ param => [
+ {
+ type => 'SQLHDESC',
+ ptr => 0,
+ name => 'DescriptorHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'RecNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'Name',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'StringLength',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'Type',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'SubType',
+ index => 6,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'Length',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'Precision',
+ index => 8,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'Scale',
+ index => 9,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'Nullable',
+ index => 10,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetDiagField => {
+ type => 'SQLRETURN',
+ name => 'SQLGetDiagField',
+ param => [
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'HandleType',
+ index => 0,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 0,
+ name => 'Handle',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'RecNumber',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'DiagIdentifier',
+ index => 3,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'DiagInfo',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'StringLength',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetDiagRec => {
+ type => 'SQLRETURN',
+ name => 'SQLGetDiagRec',
+ param => [
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'HandleType',
+ index => 0,
+ },
+ {
+ type => 'SQLHANDLE',
+ ptr => 0,
+ name => 'Handle',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'RecNumber',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'Sqlstate',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'NativeError',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'MessageText',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 6,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'TextLength',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetEnvAttr => {
+ type => 'SQLRETURN',
+ name => 'SQLGetEnvAttr',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Attribute',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StringLength',
+ index => 4,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetFunctions => {
+ type => 'SQLRETURN',
+ name => 'SQLGetFunctions',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'FunctionId',
+ index => 1,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 1,
+ name => 'Supported',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetInfo => {
+ type => 'SQLRETURN',
+ name => 'SQLGetInfo',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'InfoType',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'InfoValue',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'StringLength',
+ index => 4,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetStmtAttr => {
+ type => 'SQLRETURN',
+ name => 'SQLGetStmtAttr',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Attribute',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StringLength',
+ index => 4,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLGetStmtOption => {
+ type => 'SQLRETURN',
+ name => 'SQLGetStmtOption',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Option',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLGetTypeInfo => {
+ type => 'SQLRETURN',
+ name => 'SQLGetTypeInfo',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'DataType',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLMoreResults => {
+ type => 'SQLRETURN',
+ name => 'SQLMoreResults',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLNativeSql => {
+ type => 'SQLRETURN',
+ name => 'SQLNativeSql',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'hdbc',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSqlStrIn',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'cbSqlStrIn',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSqlStr',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'cbSqlStrMax',
+ index => 4,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'pcbSqlStr',
+ index => 5,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLNumParams => {
+ type => 'SQLRETURN',
+ name => 'SQLNumParams',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'pcpar',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLNumResultCols => {
+ type => 'SQLRETURN',
+ name => 'SQLNumResultCols',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 1,
+ name => 'ColumnCount',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLParamData => {
+ type => 'SQLRETURN',
+ name => 'SQLParamData',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 1,
+ name => 'Value',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLParamOptions => {
+ type => 'SQLRETURN',
+ name => 'SQLParamOptions',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 0,
+ name => 'crow',
+ index => 1,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 1,
+ name => 'pirow',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLPrepare => {
+ type => 'SQLRETURN',
+ name => 'SQLPrepare',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'StatementText',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'TextLength',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLPrimaryKeys => {
+ type => 'SQLRETURN',
+ name => 'SQLPrimaryKeys',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szCatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbCatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbSchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szTableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbTableName',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLProcedureColumns => {
+ type => 'SQLRETURN',
+ name => 'SQLProcedureColumns',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szCatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbCatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbSchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szProcName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbProcName',
+ index => 6,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szColumnName',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbColumnName',
+ index => 8,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLProcedures => {
+ type => 'SQLRETURN',
+ name => 'SQLProcedures',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szCatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbCatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbSchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szProcName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbProcName',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLPutData => {
+ type => 'SQLRETURN',
+ name => 'SQLPutData',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Data',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'StrLen_or_Ind',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLRowCount => {
+ type => 'SQLRETURN',
+ name => 'SQLRowCount',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'RowCount',
+ index => 1,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSetConnectAttr => {
+ type => 'SQLRETURN',
+ name => 'SQLSetConnectAttr',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Attribute',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'StringLength',
+ index => 3,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLSetConnectOption => {
+ type => 'SQLRETURN',
+ name => 'SQLSetConnectOption',
+ param => [
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Option',
+ index => 1,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSetCursorName => {
+ type => 'SQLRETURN',
+ name => 'SQLSetCursorName',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'CursorName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSetDescField => {
+ type => 'SQLRETURN',
+ name => 'SQLSetDescField',
+ param => [
+ {
+ type => 'SQLHDESC',
+ ptr => 0,
+ name => 'DescriptorHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'RecNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'FieldIdentifier',
+ index => 2,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'BufferLength',
+ index => 4,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLSetDescRec => {
+ type => 'SQLRETURN',
+ name => 'SQLSetDescRec',
+ param => [
+ {
+ type => 'SQLHDESC',
+ ptr => 0,
+ name => 'DescriptorHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'RecNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'Type',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'SubType',
+ index => 3,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Length',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'Precision',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'Scale',
+ index => 6,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Data',
+ index => 7,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StringLength',
+ index => 8,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'Indicator',
+ index => 9,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLSetEnvAttr => {
+ type => 'SQLRETURN',
+ name => 'SQLSetEnvAttr',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Attribute',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'StringLength',
+ index => 3,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLSetParam => {
+ type => 'SQLRETURN',
+ name => 'SQLSetParam',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'ParameterNumber',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ValueType',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ParameterType',
+ index => 3,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 0,
+ name => 'LengthPrecision',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'ParameterScale',
+ index => 5,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'ParameterValue',
+ index => 6,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 1,
+ name => 'StrLen_or_Ind',
+ index => 7,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSetPos => {
+ type => 'SQLRETURN',
+ name => 'SQLSetPos',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'irow',
+ index => 1,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fOption',
+ index => 2,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fLock',
+ index => 3,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSetScrollOptions => {
+ type => 'SQLRETURN',
+ name => 'SQLSetScrollOptions',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'fConcurrency',
+ index => 1,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'crowKeyset',
+ index => 2,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'crowRowset',
+ index => 3,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSetStmtAttr => {
+ type => 'SQLRETURN',
+ name => 'SQLSetStmtAttr',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'Attribute',
+ index => 1,
+ },
+ {
+ type => 'SQLPOINTER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ {
+ type => 'SQLINTEGER',
+ ptr => 0,
+ name => 'StringLength',
+ index => 3,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0300',
+ },
+ SQLSetStmtOption => {
+ type => 'SQLRETURN',
+ name => 'SQLSetStmtOption',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Option',
+ index => 1,
+ },
+ {
+ type => 'SQLUINTEGER',
+ ptr => 0,
+ name => 'Value',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLSpecialColumns => {
+ type => 'SQLRETURN',
+ name => 'SQLSpecialColumns',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'IdentifierType',
+ index => 1,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'CatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength1',
+ index => 3,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'SchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength2',
+ index => 5,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'TableName',
+ index => 6,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength3',
+ index => 7,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Scope',
+ index => 8,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Nullable',
+ index => 9,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLStatistics => {
+ type => 'SQLRETURN',
+ name => 'SQLStatistics',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'CatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength1',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'SchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength2',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'TableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength3',
+ index => 6,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Unique',
+ index => 7,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'Reserved',
+ index => 8,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLTablePrivileges => {
+ type => 'SQLRETURN',
+ name => 'SQLTablePrivileges',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'hstmt',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szCatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbCatalogName',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szSchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbSchemaName',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'szTableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'cbTableName',
+ index => 6,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLTables => {
+ type => 'SQLRETURN',
+ name => 'SQLTables',
+ param => [
+ {
+ type => 'SQLHSTMT',
+ ptr => 0,
+ name => 'StatementHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'CatalogName',
+ index => 1,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength1',
+ index => 2,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'SchemaName',
+ index => 3,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength2',
+ index => 4,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'TableName',
+ index => 5,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength3',
+ index => 6,
+ },
+ {
+ type => 'SQLCHAR',
+ ptr => 1,
+ name => 'TableType',
+ index => 7,
+ },
+ {
+ type => 'SQLSMALLINT',
+ ptr => 0,
+ name => 'NameLength4',
+ index => 8,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+ SQLTransact => {
+ type => 'SQLRETURN',
+ name => 'SQLTransact',
+ param => [
+ {
+ type => 'SQLHENV',
+ ptr => 0,
+ name => 'EnvironmentHandle',
+ index => 0,
+ },
+ {
+ type => 'SQLHDBC',
+ ptr => 0,
+ name => 'ConnectionHandle',
+ index => 1,
+ },
+ {
+ type => 'SQLUSMALLINT',
+ ptr => 0,
+ name => 'CompletionType',
+ index => 2,
+ },
+ ],
+ odbcver => 'ODBCVER >= 0x0000',
+ },
+};
diff --git a/storage/ndb/src/old_files/client/odbc/driver/Func.pl b/storage/ndb/src/old_files/client/odbc/driver/Func.pl
new file mode 100644
index 00000000000..1064a6a6c6e
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/Func.pl
@@ -0,0 +1,352 @@
+#
+
+use strict;
+select(STDOUT);
+$| = 1;
+use vars qw($func);
+
+my $action = shift;
+my @args = @ARGV;
+if (! $action) {
+ print <<END;
+usage: perl $0 <data|name|code|move> ...
+data -unixodbc -- write new Func.data to stdout
+name [-[no]auto -type] [suffix] -- list function names
+code -- write auto/*.cpp
+diff -- diff against auto/*.cpp
+move -- move auto/*.cpp to .
+functab -- write struct entiries for SQLGetFunctions
+END
+ exit(0);
+}
+
+# indents
+my $i1 = " " x (1*4);
+my $i2 = " " x (2*4);
+my $i3 = " " x (3*4);
+my $i4 = " " x (4*4);
+
+if ($action eq 'data') {
+ my @entry = ();
+ while (@args) {
+ my $file = shift(@args);
+ if ($file eq '-unixodbc') {
+ unshift(@args, </usr/local/include/{sql,sqlext}.h>);
+ next;
+ }
+ if ($file eq '-iodbc') {
+ unshift(@args, </usr/local/opt/iODBC/include/{sql,sqlext}.h>);
+ next;
+ }
+ warn "read $file\n";
+ open(F, "<$file") || die "$file: $!";
+ my $text = undef;
+ my $odbcver = undef;
+ while ($_ = <F>) {
+ chomp;
+ if (/^\s*$/) {
+ next;
+ }
+ if (/^\s*#\s*if\s+(.*\bODBCVER\b.*)$/) {
+ $odbcver = $1;
+ $odbcver =~ s/^\s+|\s+$//g;
+ $odbcver =~ s/^\(+|\)+$//g;
+ next;
+ }
+ if (/^\s*#\s*endif\b/) {
+ $odbcver = undef;
+ next;
+ }
+ if (/^\s*SQLRETURN\b/) {
+ $text = "";
+ }
+ if (defined($text)) {
+ $text .= $_;
+ if (/;\s*$/) {
+ push(@entry, {
+ text => $text,
+ odbcver => $odbcver || 'ODBCVER >= 0x0000',
+ });
+ $text = undef;
+ }
+ }
+ }
+ close(F);
+ }
+ warn "@{[ scalar @entry ]} entries\n";
+ $func = {};
+ for my $e (@entry) {
+ my $text = $e->{text};
+ $text =~ s!/\*.+?\*/!!g;
+ $text =~ s/^\s+|\s+$//g;
+ $text =~ s/\s\s*/\040/g;
+ $text =~ /^(SQLRETURN)\s+(SQL_API\s+)?(\w+)\s*\((.*)\)\s*;/
+ or warn "discard: $_\n", next;
+ my $type = $1;
+ my $name = $3;
+ my $body = $4;
+ my $f = {};
+ $f->{type} = $type;
+ $f->{name} = $name;
+ my @body = split(/,/, $body);
+ my $param = [];
+ for my $s (@body) {
+ $s =~ s/^\s+|\s+$//g;
+ my($ptype, $pptr, $pname);
+ if ($s =~ /^(\w+)\s+(\w+)$/) {
+ $ptype = $1;
+ $pptr = 0;
+ $pname = $2;
+ } elsif ($s =~ /^(\w+)\s*\*\s*(\w+)$/) {
+ $ptype = $1;
+ $pptr = 1;
+ $pname = $2;
+ } else {
+ warn "discard: $name: param $s\n";
+ $param = undef;
+ last;
+ }
+ my $pindex = scalar @$param;
+ push(@$param, {
+ type => $ptype,
+ ptr => $pptr,
+ name => $pname,
+ index => $pindex,
+ });
+ }
+ $param or next;
+ $f->{param} = $param;
+ $f->{odbcver} = $e->{odbcver};
+ $func->{$name}
+ and warn "duplicate: $name\n", next;
+ $func->{$name} = $f;
+ }
+ print "\$func = {\n";
+ for my $name (sort keys %$func) {
+ my $f = $func->{$name};
+ print "${i1}$name => {\n";
+ print "${i2}type => '$f->{type}',\n";
+ print "${i2}name => '$f->{name}',\n";
+ print "${i2}param => [\n";
+ for my $p (@{$f->{param}}) {
+ print "${i3}\{\n";
+ print "${i4}type => '$p->{type}',\n";
+ print "${i4}ptr => $p->{ptr},\n";
+ print "${i4}name => '$p->{name}',\n";
+ print "${i4}index => $p->{index},\n";
+ print "${i3}\},\n";
+ }
+ print "${i2}],\n";
+ print "${i2}odbcver => '$f->{odbcver}',\n";
+ print "${i1}},\n";
+ }
+ printf "};\n";
+ $action = undef;
+}
+
+if ($action eq 'name') {
+ my %functab = (); # bit in FuncTab
+ my $functab = "../handles/FuncTab.cpp";
+ if (! open(F, "<$functab")) {
+ warn "$functab: $!";
+ } else {
+ while ($_ = <F>) {
+ if (/SQL_API_([A-Z]+)[\s,]*([01])/) {
+ defined $functab{$1} and die "$_";
+ $functab{$1} = $2;
+ }
+ }
+ close(F);
+ }
+ require './Func.data';
+ my $auto = 1;
+ my $noauto = 1;
+ my $type = 0;
+ while ($args[0] =~ /^-(\w+)$/) {
+ $noauto = 0 if $1 eq 'auto';
+ $auto = 0 if $1 eq 'noauto';
+ $type = 1 if $1 eq 'type';
+ shift(@args);
+ }
+ my $suffix = shift(@args);
+ for my $name (sort keys %$func) {
+ my $f = $func->{$name};
+ local $/ = undef;
+ my($x1);
+ if (open(F, "<$name.cpp")) {
+ $x1 = <F>;
+ close(F);
+ if ($x1 =~ /\bauto_$name\b/) {
+ $auto || next;
+ print "A " if $type;
+ } else {
+ $noauto || next;
+ print "- " if $type;
+ }
+ if ($type) {
+ my $y = $functab{uc $name};
+ $y = "?" if $y !~ /^[01]$/;
+ print "$y ";
+ my $z = $f->{odbcver};
+ $z =~ s/^.*(...)$/$1/;
+ print "$z ";
+ }
+ }
+ print "$name$suffix\n";
+ }
+ $action = undef;
+}
+
+if ($action eq 'code') {
+ require './Func.data';
+ system("rm -rf auto; mkdir auto");
+ for my $name (sort keys %$func) {
+ my $f = $func->{$name};
+ my $file = "auto/$name.cpp";
+ open(F, ">$file") || die "$file: $!\n";
+ print F "#include \"driver.hpp\"\n";
+ print F "\n";
+ printf F "#if $f->{odbcver}\n";
+ print F "$f->{type} SQL_API\n";
+ print F "$f->{name}(";
+ for my $p (@{$f->{param}}) {
+ print F "," if $p->{index} > 0;
+ print F "\n${i1}$p->{type}";
+ for (my $i = 0; $i < $p->{ptr}; $i++) {
+ print F "*";
+ }
+ print F " $p->{name}";
+ }
+ print F ")\n";
+ print F "{\n";
+ print F "${i1}const char* const sqlFunction = \"$f->{name}\";\n";
+ print F "#ifndef auto_$name\n";
+ print F "${i1}Ctx ctx;\n";
+ print F "${i1}ctx.log(1, \"*** not implemented: %s\", sqlFunction);\n";
+ print F "${i1}return SQL_ERROR;\n";
+ print F "#else\n";
+ my @ihandle = ();
+ my @ohandle = ();
+ for my $p (@{$f->{param}}) {
+ if ($p->{type} =~ /^SQLH(ENV|DBC|STMT|DESC)$/) {
+ $p->{btype} = lc $1;
+ my $h = ! $p->{ptr} ? \@ihandle : \@ohandle;
+ push(@$h, $p);
+ }
+ }
+ if (! @ihandle) { # use root handle instance
+ push(@ihandle, {
+ type => 'SQLHROOT',
+ name => '(SQLHANDLE*)0',
+ });
+ }
+ for my $p (@ihandle, @ohandle) {
+ $p->{htype} = "Handle" . (ucfirst lc $p->{btype});
+ $p->{hname} = "p" . (ucfirst lc $p->{btype});
+ }
+ if (@ihandle) {
+ print F "${i1}HandleRoot* const pRoot = HandleRoot::instance();\n";
+ }
+ for my $p (@ihandle) {
+ print F "${i1}$p->{htype}* $p->{hname} = ";
+ print F "pRoot->find" . ucfirst($p->{btype}). "($p->{name});\n";
+ print F "${i1}if ($p->{hname} == 0)\n";
+ print F "${i2}return SQL_INVALID_HANDLE;\n";
+ }
+ {
+ my $p = $ihandle[0];
+ print F "${i1}Ctx& ctx = $p->{hname}->initCtx();\n";
+ print F "${i1}ctx.logSqlEnter(sqlFunction);\n";
+ }
+ for my $p (@ohandle) {
+ print F "${i1}$p->{htype}* $p->{hname} = 0;\n";
+ }
+ {
+ my $p = $ihandle[0];
+ my $fname = $f->{name};
+ $fname =~ s/^SQL/sql/; # keep sql prefix
+ print F "${i1}if (ctx.ok())\n";
+ print F "${i2}$p->{hname}->$fname(\n";
+ print F "${i3}ctx";
+ }
+ for my $p (@{$f->{param}}) {
+ if ($p == $ihandle[0]) {
+ next;
+ }
+ print F ",";
+ print F "\n${i3}";
+ if (grep($_ == $p, @ihandle)) {
+ print F "$p->{hname}";
+ } elsif (grep($_ == $p, @ohandle)) {
+ print F "$p->{name} != 0 ? &$p->{hname} : 0";
+ } else {
+ print F "&" if $p->{ptr} > 0;
+ print F "$p->{name}";
+ }
+ }
+ print F "\n${i2});\n";
+ for my $p (@ohandle) {
+ print F "${i1}if ($p->{name} != 0)\n";
+ print F "${i2}*$p->{name} = ";
+ print F "pRoot->from" . ucfirst($p->{btype}) . "($p->{hname});\n";
+ }
+ {
+ my $p = $ihandle[0];
+ print F "${i1}$p->{hname}->saveCtx(ctx);\n";
+ }
+ print F "${i1}ctx.logSqlExit();\n";
+ print F "${i1}return ctx.getCode();\n";
+ print F "#endif\n";
+ print F "}\n";
+ print F "#endif // $f->{odbcver}\n";
+ close(F);
+ }
+ $action = undef;
+}
+
+if ($action eq 'diff' || $action eq 'move') {
+ require './Func.data';
+ for my $name (sort keys %$func) {
+ local $/ = undef;
+ my($x1, $x2);
+ if (open(F, "<$name.cpp")) {
+ $x1 = <F>;
+ close(F);
+ if ($x1 !~ /\bauto_$name\b/) {
+ warn "$name.cpp: not auto-generated\n" if $action eq 'move';
+ next;
+ }
+ }
+ if (! open(F, "<auto/$name.cpp")) {
+ die "auto/$name.cpp: $!\n";
+ }
+ $x2 = <F>;
+ close(F);
+ if ($x1 eq $x2) {
+ warn "$name: no changes\n" if $action eq 'move';
+ next;
+ }
+ if ($action eq 'diff') {
+ print "=" x 40, "\n";
+ print "diff $name.cpp auto/", "\n";
+ system("diff $name.cpp auto/$name.cpp");
+ } else {
+ rename("auto/$name.cpp", "$name.cpp")
+ or die "rename $name: $!\n";
+ warn "$name: updated\n" if 0;
+ }
+ }
+ $action = undef;
+}
+
+if ($action eq 'functab') {
+ require './Func.data';
+ for my $name (sort keys %$func) {
+ printf "%4s{%3s%-30s, 0 },\n", "", "", uc "SQL_API_$name";
+ }
+ $action = undef;
+}
+
+$action && die "$action: undefined\n";
+
+# vim: set sw=4:
diff --git a/storage/ndb/src/old_files/client/odbc/driver/Makefile b/storage/ndb/src/old_files/client/odbc/driver/Makefile
new file mode 100644
index 00000000000..62f82371da4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/Makefile
@@ -0,0 +1,16 @@
+include .defs.mk
+
+TYPE = *
+
+NONPIC_ARCHIVE = N
+
+PIC_ARCHIVE = Y
+
+ARCHIVE_TARGET = odbcdriver
+
+SOURCES = driver.cpp
+
+SOURCES_EXTRA = $(shell perl Func.pl name .cpp)
+
+include ../Extra.mk
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLAllocConnect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocConnect.cpp
new file mode 100644
index 00000000000..a7ffd8c89d1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocConnect.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLAllocConnect(
+ SQLHENV EnvironmentHandle,
+ SQLHDBC* ConnectionHandle)
+{
+ driver_enter(SQL_API_SQLALLOCCONNECT);
+ const char* const sqlFunction = "SQLAllocConnect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLALLOCCONNECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleDbc* pDbc = 0;
+ HandleDbc** ppDbc = 0;
+ if (ConnectionHandle != 0)
+ ppDbc = &pDbc;
+ try {
+ pEnv->sqlAllocConnect(ctx, ppDbc);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (ConnectionHandle != 0)
+ *ConnectionHandle = static_cast<SQLHDBC>(pDbc);
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLALLOCCONNECT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLAllocEnv.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocEnv.cpp
new file mode 100644
index 00000000000..a62dae61008
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocEnv.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLAllocEnv(
+ SQLHENV* EnvironmentHandle)
+{
+ driver_enter(SQL_API_SQLALLOCENV);
+ const char* const sqlFunction = "SQLAllocEnv";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleEnv* pEnv = 0;
+ HandleEnv** ppEnv = 0;
+ if (EnvironmentHandle != 0)
+ ppEnv = &pEnv;
+ try {
+ pRoot->sqlAllocEnv(ctx, ppEnv);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (EnvironmentHandle != 0)
+ *EnvironmentHandle = static_cast<SQLHENV>(pEnv);
+ pRoot->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLALLOCENV);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandle.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandle.cpp
new file mode 100644
index 00000000000..9daf6ead946
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandle.cpp
@@ -0,0 +1,62 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLAllocHandle(
+ SQLSMALLINT HandleType,
+ SQLHANDLE InputHandle,
+ SQLHANDLE* OutputHandle)
+{
+ driver_enter(SQL_API_SQLALLOCHANDLE);
+ const char* const sqlFunction = "SQLAllocHandle";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ SQLSMALLINT parentType = pRoot->findParentType(HandleType);
+ if (parentType == -1) {
+ driver_exit(SQL_API_SQLALLOCHANDLE);
+ return SQL_INVALID_HANDLE;
+ }
+ HandleBase* pParent = pRoot->findBase(parentType, InputHandle);
+ if (pParent == 0) {
+ driver_exit(SQL_API_SQLALLOCHANDLE);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleBase* pChild = 0;
+ HandleBase** ppChild = 0;
+ if (OutputHandle != 0)
+ ppChild = &pChild;
+ try {
+ pParent->sqlAllocHandle(ctx, HandleType, ppChild);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (OutputHandle != 0)
+ *OutputHandle = static_cast<SQLHANDLE>(pChild);
+ if (pRoot == pParent)
+ pRoot->lockHandle();
+ pParent->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ if (pRoot == pParent)
+ pRoot->unlockHandle();
+ driver_exit(SQL_API_SQLALLOCHANDLE);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandleStd.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandleStd.cpp
new file mode 100644
index 00000000000..61290e37b7b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandleStd.cpp
@@ -0,0 +1,56 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLAllocHandleStd(
+ SQLSMALLINT fHandleType,
+ SQLHANDLE hInput,
+ SQLHANDLE* phOutput)
+{
+#ifndef auto_SQLAllocHandleStd
+ const char* const sqlFunction = "SQLAllocHandleStd";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLALLOCHANDLESTD);
+ const char* const sqlFunction = "SQLAllocHandleStd";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ Handle* p = pRoot->find((SQLHANDLE*)0);
+ if (p == 0) {
+ driver_exit(SQL_API_SQLALLOCHANDLESTD);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ p->sqlAllocHandleStd(
+ ctx,
+ fHandleType,
+ hInput,
+ &phOutput
+ );
+ p->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLALLOCHANDLESTD);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLAllocStmt.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocStmt.cpp
new file mode 100644
index 00000000000..bf3f149f5de
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLAllocStmt.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLAllocStmt(
+ SQLHDBC ConnectionHandle,
+ SQLHSTMT* StatementHandle)
+{
+ driver_enter(SQL_API_SQLALLOCSTMT);
+ const char* const sqlFunction = "SQLAllocStmt";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLALLOCSTMT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleStmt* pStmt = 0;
+ HandleStmt** ppStmt = 0;
+ if (StatementHandle != 0)
+ ppStmt = &pStmt;
+ try {
+ pDbc->sqlAllocStmt(ctx, ppStmt);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (StatementHandle != 0)
+ *StatementHandle = static_cast<SQLHSTMT>(pStmt);
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLALLOCSTMT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLBindCol.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLBindCol.cpp
new file mode 100644
index 00000000000..5562334e8cc
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLBindCol.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLBindCol(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber,
+ SQLSMALLINT TargetType,
+ SQLPOINTER TargetValue,
+ SQLINTEGER BufferLength,
+ SQLINTEGER* StrLen_or_Ind)
+{
+ driver_enter(SQL_API_SQLBINDCOL);
+ const char* const sqlFunction = "SQLBindCol";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLBINDCOL);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlBindCol(ctx, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLBINDCOL);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLBindParam.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLBindParam.cpp
new file mode 100644
index 00000000000..2fcc17b872f
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLBindParam.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLBindParam(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ParameterNumber,
+ SQLSMALLINT ValueType,
+ SQLSMALLINT ParameterType,
+ SQLUINTEGER LengthPrecision,
+ SQLSMALLINT ParameterScale,
+ SQLPOINTER ParameterValue,
+ SQLINTEGER* StrLen_or_Ind)
+{
+ driver_enter(SQL_API_SQLBINDPARAM);
+ const char* const sqlFunction = "SQLBindParam";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLBINDPARAM);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlBindParam(ctx, ParameterNumber, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValue, StrLen_or_Ind);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLBINDPARAM);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLBindParameter.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLBindParameter.cpp
new file mode 100644
index 00000000000..e4ca5bbc731
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLBindParameter.cpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLBindParameter(
+ SQLHSTMT hstmt,
+ SQLUSMALLINT ipar,
+ SQLSMALLINT fParamType,
+ SQLSMALLINT fCType,
+ SQLSMALLINT fSqlType,
+ SQLUINTEGER cbColDef,
+ SQLSMALLINT ibScale,
+ SQLPOINTER rgbValue,
+ SQLINTEGER cbValueMax,
+ SQLINTEGER* pcbValue)
+{
+ driver_enter(SQL_API_SQLBINDPARAMETER);
+ const char* const sqlFunction = "SQLBindParameter";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLBINDPARAMETER);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlBindParameter(ctx, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLBINDPARAMETER);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLBrowseConnect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLBrowseConnect.cpp
new file mode 100644
index 00000000000..7e629e199e5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLBrowseConnect.cpp
@@ -0,0 +1,61 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLBrowseConnect(
+ SQLHDBC hdbc,
+ SQLCHAR* szConnStrIn,
+ SQLSMALLINT cbConnStrIn,
+ SQLCHAR* szConnStrOut,
+ SQLSMALLINT cbConnStrOutMax,
+ SQLSMALLINT* pcbConnStrOut)
+{
+#ifndef auto_SQLBrowseConnect
+ const char* const sqlFunction = "SQLBrowseConnect";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLBROWSECONNECT);
+ const char* const sqlFunction = "SQLBrowseConnect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(hdbc);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLBROWSECONNECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pDbc->sqlBrowseConnect(
+ ctx,
+ &szConnStrIn,
+ cbConnStrIn,
+ &szConnStrOut,
+ cbConnStrOutMax,
+ &pcbConnStrOut
+ );
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLBROWSECONNECT);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLBulkOperations.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLBulkOperations.cpp
new file mode 100644
index 00000000000..7d256d66e3f
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLBulkOperations.cpp
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLBulkOperations(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT Operation)
+{
+#ifndef auto_SQLBulkOperations
+ const char* const sqlFunction = "SQLBulkOperations";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLBULKOPERATIONS);
+ const char* const sqlFunction = "SQLBulkOperations";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLBULKOPERATIONS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlBulkOperations(
+ ctx,
+ Operation
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLBULKOPERATIONS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLCancel.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLCancel.cpp
new file mode 100644
index 00000000000..ac4e43c6e89
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLCancel.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLCancel(
+ SQLHSTMT StatementHandle)
+{
+ driver_enter(SQL_API_SQLCANCEL);
+ const char* const sqlFunction = "SQLCancel";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLCANCEL);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlCancel(ctx);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCANCEL);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLCloseCursor.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLCloseCursor.cpp
new file mode 100644
index 00000000000..26d88c91e3b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLCloseCursor.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLCloseCursor(
+ SQLHSTMT StatementHandle)
+{
+ driver_enter(SQL_API_SQLCLOSECURSOR);
+ const char* const sqlFunction = "SQLCloseCursor";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLCLOSECURSOR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlCloseCursor(ctx);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCLOSECURSOR);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLColAttribute.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLColAttribute.cpp
new file mode 100644
index 00000000000..0e7e5446932
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLColAttribute.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLColAttribute(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber,
+ SQLUSMALLINT FieldIdentifier,
+ SQLPOINTER CharacterAttribute,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* StringLength,
+ SQLPOINTER NumericAttribute)
+{
+ driver_enter(SQL_API_SQLCOLATTRIBUTE);
+ const char* const sqlFunction = "SQLColAttribute";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLCOLATTRIBUTE);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlColAttribute(ctx, ColumnNumber, FieldIdentifier, CharacterAttribute, BufferLength, StringLength, NumericAttribute);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCOLATTRIBUTE);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLColAttributes.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLColAttributes.cpp
new file mode 100644
index 00000000000..05a4c1d4d37
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLColAttributes.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLColAttributes(
+ SQLHSTMT hstmt,
+ SQLUSMALLINT icol,
+ SQLUSMALLINT fDescType,
+ SQLPOINTER rgbDesc,
+ SQLSMALLINT cbDescMax,
+ SQLSMALLINT* pcbDesc,
+ SQLINTEGER* pfDesc)
+{
+ driver_enter(SQL_API_SQLCOLATTRIBUTES);
+ const char* const sqlFunction = "SQLColAttributes";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLCOLATTRIBUTES);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlColAttributes(ctx, icol, fDescType, rgbDesc, cbDescMax, pcbDesc, pfDesc);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCOLATTRIBUTES);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLColumnPrivileges.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLColumnPrivileges.cpp
new file mode 100644
index 00000000000..cfbc9c2bc57
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLColumnPrivileges.cpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLColumnPrivileges(
+ SQLHSTMT hstmt,
+ SQLCHAR* szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR* szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR* szTableName,
+ SQLSMALLINT cbTableName,
+ SQLCHAR* szColumnName,
+ SQLSMALLINT cbColumnName)
+{
+#ifndef auto_SQLColumnPrivileges
+ const char* const sqlFunction = "SQLColumnPrivileges";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLCOLUMNPRIVILEGES);
+ const char* const sqlFunction = "SQLColumnPrivileges";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLCOLUMNPRIVILEGES);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlColumnPrivileges(
+ ctx,
+ &szCatalogName,
+ cbCatalogName,
+ &szSchemaName,
+ cbSchemaName,
+ &szTableName,
+ cbTableName,
+ &szColumnName,
+ cbColumnName
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCOLUMNPRIVILEGES);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLColumns.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLColumns.cpp
new file mode 100644
index 00000000000..4e0b646ee7d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLColumns.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLColumns(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* CatalogName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR* SchemaName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR* TableName,
+ SQLSMALLINT NameLength3,
+ SQLCHAR* ColumnName,
+ SQLSMALLINT NameLength4)
+{
+ driver_enter(SQL_API_SQLCOLUMNS);
+ const char* const sqlFunction = "SQLColumns";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLCOLUMNS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ pStmt->sqlColumns(ctx, CatalogName, NameLength1, SchemaName, NameLength2, TableName, NameLength3, ColumnName, NameLength4);
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCOLUMNS);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLConnect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLConnect.cpp
new file mode 100644
index 00000000000..d8f30ed47e7
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLConnect.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLConnect(
+ SQLHDBC ConnectionHandle,
+ SQLCHAR* ServerName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR* UserName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR* Authentication,
+ SQLSMALLINT NameLength3)
+{
+ driver_enter(SQL_API_SQLCONNECT);
+ const char* const sqlFunction = "SQLConnect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLCONNECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlConnect(ctx, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCONNECT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLCopyDesc.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLCopyDesc.cpp
new file mode 100644
index 00000000000..b4d4b2e4122
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLCopyDesc.cpp
@@ -0,0 +1,58 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLCopyDesc(
+ SQLHDESC SourceDescHandle,
+ SQLHDESC TargetDescHandle)
+{
+#ifndef auto_SQLCopyDesc
+ const char* const sqlFunction = "SQLCopyDesc";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLCOPYDESC);
+ const char* const sqlFunction = "SQLCopyDesc";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDesc* pDesc = pRoot->findDesc(SourceDescHandle);
+ if (pDesc == 0) {
+ driver_exit(SQL_API_SQLCOPYDESC);
+ return SQL_INVALID_HANDLE;
+ }
+ HandleDesc* pDesc = pRoot->findDesc(TargetDescHandle);
+ if (pDesc == 0) {
+ driver_exit(SQL_API_SQLCOPYDESC);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pDesc->sqlCopyDesc(
+ ctx,
+ pDesc
+ );
+ pDesc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLCOPYDESC);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLDataSources.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLDataSources.cpp
new file mode 100644
index 00000000000..6115e7175f9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLDataSources.cpp
@@ -0,0 +1,65 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLDataSources(
+ SQLHENV EnvironmentHandle,
+ SQLUSMALLINT Direction,
+ SQLCHAR* ServerName,
+ SQLSMALLINT BufferLength1,
+ SQLSMALLINT* NameLength1,
+ SQLCHAR* Description,
+ SQLSMALLINT BufferLength2,
+ SQLSMALLINT* NameLength2)
+{
+#ifndef auto_SQLDataSources
+ const char* const sqlFunction = "SQLDataSources";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLDATASOURCES);
+ const char* const sqlFunction = "SQLDataSources";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLDATASOURCES);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pEnv->sqlDataSources(
+ ctx,
+ Direction,
+ &ServerName,
+ BufferLength1,
+ &NameLength1,
+ &Description,
+ BufferLength2,
+ &NameLength2
+ );
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLDATASOURCES);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLDescribeCol.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLDescribeCol.cpp
new file mode 100644
index 00000000000..f15ce8962f1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLDescribeCol.cpp
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLDescribeCol(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber,
+ SQLCHAR* ColumnName,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* NameLength,
+ SQLSMALLINT* DataType,
+ SQLUINTEGER* ColumnSize,
+ SQLSMALLINT* DecimalDigits,
+ SQLSMALLINT* Nullable)
+{
+ driver_enter(SQL_API_SQLDESCRIBECOL);
+ const char* const sqlFunction = "SQLDescribeCol";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLDESCRIBECOL);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlDescribeCol(ctx, ColumnNumber, ColumnName, BufferLength, NameLength, DataType, ColumnSize, DecimalDigits, Nullable);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLDESCRIBECOL);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLDescribeParam.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLDescribeParam.cpp
new file mode 100644
index 00000000000..beff41396fe
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLDescribeParam.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLDescribeParam(
+ SQLHSTMT hstmt,
+ SQLUSMALLINT ipar,
+ SQLSMALLINT* pfSqlType,
+ SQLUINTEGER* pcbParamDef,
+ SQLSMALLINT* pibScale,
+ SQLSMALLINT* pfNullable)
+{
+ driver_enter(SQL_API_SQLDESCRIBEPARAM);
+ const char* const sqlFunction = "SQLDescribeParam";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLDESCRIBEPARAM);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlDescribeParam(ctx, ipar, pfSqlType, pcbParamDef, pibScale, pfNullable);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLDESCRIBEPARAM);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLDisconnect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLDisconnect.cpp
new file mode 100644
index 00000000000..75db5604da8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLDisconnect.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLDisconnect(
+ SQLHDBC ConnectionHandle)
+{
+ driver_enter(SQL_API_SQLDISCONNECT);
+ const char* const sqlFunction = "SQLDisconnect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLDISCONNECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlDisconnect(ctx);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLDISCONNECT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLDriverConnect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLDriverConnect.cpp
new file mode 100644
index 00000000000..340babd8523
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLDriverConnect.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLDriverConnect(
+ SQLHDBC hdbc,
+ SQLHWND hwnd,
+ SQLCHAR* szConnStrIn,
+ SQLSMALLINT cbConnStrIn,
+ SQLCHAR* szConnStrOut,
+ SQLSMALLINT cbConnStrOutMax,
+ SQLSMALLINT* pcbConnStrOut,
+ SQLUSMALLINT fDriverCompletion)
+{
+ driver_enter(SQL_API_SQLDRIVERCONNECT);
+ const char* const sqlFunction = "SQLDriverConnect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(hdbc);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLDRIVERCONNECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlDriverConnect(ctx, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLDRIVERCONNECT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLDrivers.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLDrivers.cpp
new file mode 100644
index 00000000000..9c52f900992
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLDrivers.cpp
@@ -0,0 +1,65 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLDrivers(
+ SQLHENV henv,
+ SQLUSMALLINT fDirection,
+ SQLCHAR* szDriverDesc,
+ SQLSMALLINT cbDriverDescMax,
+ SQLSMALLINT* pcbDriverDesc,
+ SQLCHAR* szDriverAttributes,
+ SQLSMALLINT cbDrvrAttrMax,
+ SQLSMALLINT* pcbDrvrAttr)
+{
+#ifndef auto_SQLDrivers
+ const char* const sqlFunction = "SQLDrivers";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLDRIVERS);
+ const char* const sqlFunction = "SQLDrivers";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleEnv* pEnv = pRoot->findEnv(henv);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLDRIVERS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pEnv->sqlDrivers(
+ ctx,
+ fDirection,
+ &szDriverDesc,
+ cbDriverDescMax,
+ &pcbDriverDesc,
+ &szDriverAttributes,
+ cbDrvrAttrMax,
+ &pcbDrvrAttr
+ );
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLDRIVERS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLEndTran.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLEndTran.cpp
new file mode 100644
index 00000000000..20b0b2203f5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLEndTran.cpp
@@ -0,0 +1,70 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLEndTran(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle,
+ SQLSMALLINT CompletionType)
+{
+ driver_enter(SQL_API_SQLENDTRAN);
+ const char* const sqlFunction = "SQLEndTran";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ if (HandleType == SQL_HANDLE_DBC) {
+ HandleDbc* pDbc = pRoot->findDbc(Handle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLENDTRAN);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlEndTran(ctx, CompletionType);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLENDTRAN);
+ return ret;
+ }
+ if (HandleType == SQL_HANDLE_ENV) {
+ HandleEnv* pEnv = pRoot->findEnv(Handle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLENDTRAN);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pEnv->sqlEndTran(ctx, CompletionType);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLENDTRAN);
+ return ret;
+ }
+ driver_exit(SQL_API_SQLENDTRAN);
+ return SQL_INVALID_HANDLE;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLError.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLError.cpp
new file mode 100644
index 00000000000..af78c931d37
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLError.cpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLError(
+ SQLHENV EnvironmentHandle,
+ SQLHDBC ConnectionHandle,
+ SQLHSTMT StatementHandle,
+ SQLCHAR* Sqlstate,
+ SQLINTEGER* NativeError,
+ SQLCHAR* MessageText,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* TextLength)
+{
+ driver_enter(SQL_API_SQLERROR);
+ const char* const sqlFunction = "SQLError";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0 && pDbc == 0 && pEnv == 0) {
+ driver_exit(SQL_API_SQLERROR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx ctx; // discard diagnostics
+ ctx.logSqlEnter(sqlFunction);
+ if (pStmt != 0) {
+ try {
+ pStmt->sqlError(ctx, Sqlstate, NativeError, MessageText, BufferLength, TextLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ } else if (pDbc != 0) {
+ try {
+ pDbc->sqlError(ctx, Sqlstate, NativeError, MessageText, BufferLength, TextLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ } else {
+ try {
+ pEnv->sqlError(ctx, Sqlstate, NativeError, MessageText, BufferLength, TextLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLERROR);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLExecDirect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLExecDirect.cpp
new file mode 100644
index 00000000000..0ad99d29cd9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLExecDirect.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLExecDirect(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* StatementText,
+ SQLINTEGER TextLength)
+{
+ driver_enter(SQL_API_SQLEXECDIRECT);
+ const char* const sqlFunction = "SQLExecDirect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLEXECDIRECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlExecDirect(ctx, StatementText, TextLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLEXECDIRECT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLExecute.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLExecute.cpp
new file mode 100644
index 00000000000..9c30d418f09
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLExecute.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLExecute(
+ SQLHSTMT StatementHandle)
+{
+ driver_enter(SQL_API_SQLEXECUTE);
+ const char* const sqlFunction = "SQLExecute";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLEXECUTE);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlExecute(ctx);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLEXECUTE);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLExtendedFetch.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLExtendedFetch.cpp
new file mode 100644
index 00000000000..e0dd078b5d0
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLExtendedFetch.cpp
@@ -0,0 +1,59 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLExtendedFetch(
+ SQLHSTMT hstmt,
+ SQLUSMALLINT fFetchType,
+ SQLINTEGER irow,
+ SQLUINTEGER* pcrow,
+ SQLUSMALLINT* rgfRowStatus)
+{
+#ifndef auto_SQLExtendedFetch
+ const char* const sqlFunction = "SQLExtendedFetch";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLEXTENDEDFETCH);
+ const char* const sqlFunction = "SQLExtendedFetch";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLEXTENDEDFETCH);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlExtendedFetch(
+ ctx,
+ fFetchType,
+ irow,
+ &pcrow,
+ &rgfRowStatus
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLEXTENDEDFETCH);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLFetch.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLFetch.cpp
new file mode 100644
index 00000000000..addba7b998c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLFetch.cpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLFetch(
+ SQLHSTMT StatementHandle)
+{
+ driver_enter(SQL_API_SQLFETCH);
+ const char* const sqlFunction = "SQLFetch";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLFETCH);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlFetch(ctx);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFETCH);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLFetchScroll.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLFetchScroll.cpp
new file mode 100644
index 00000000000..cfbfc813fca
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLFetchScroll.cpp
@@ -0,0 +1,55 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLFetchScroll(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT FetchOrientation,
+ SQLINTEGER FetchOffset)
+{
+#ifndef auto_SQLFetchScroll
+ const char* const sqlFunction = "SQLFetchScroll";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLFETCHSCROLL);
+ const char* const sqlFunction = "SQLFetchScroll";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLFETCHSCROLL);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlFetchScroll(
+ ctx,
+ FetchOrientation,
+ FetchOffset
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFETCHSCROLL);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLForeignKeys.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLForeignKeys.cpp
new file mode 100644
index 00000000000..886ac6bdaa5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLForeignKeys.cpp
@@ -0,0 +1,75 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLForeignKeys(
+ SQLHSTMT hstmt,
+ SQLCHAR* szPkCatalogName,
+ SQLSMALLINT cbPkCatalogName,
+ SQLCHAR* szPkSchemaName,
+ SQLSMALLINT cbPkSchemaName,
+ SQLCHAR* szPkTableName,
+ SQLSMALLINT cbPkTableName,
+ SQLCHAR* szFkCatalogName,
+ SQLSMALLINT cbFkCatalogName,
+ SQLCHAR* szFkSchemaName,
+ SQLSMALLINT cbFkSchemaName,
+ SQLCHAR* szFkTableName,
+ SQLSMALLINT cbFkTableName)
+{
+#ifndef auto_SQLForeignKeys
+ const char* const sqlFunction = "SQLForeignKeys";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLFOREIGNKEYS);
+ const char* const sqlFunction = "SQLForeignKeys";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLFOREIGNKEYS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlForeignKeys(
+ ctx,
+ &szPkCatalogName,
+ cbPkCatalogName,
+ &szPkSchemaName,
+ cbPkSchemaName,
+ &szPkTableName,
+ cbPkTableName,
+ &szFkCatalogName,
+ cbFkCatalogName,
+ &szFkSchemaName,
+ cbFkSchemaName,
+ &szFkTableName,
+ cbFkTableName
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFOREIGNKEYS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLFreeConnect.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeConnect.cpp
new file mode 100644
index 00000000000..9ac84710cce
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeConnect.cpp
@@ -0,0 +1,53 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLFreeConnect(
+ SQLHDBC ConnectionHandle)
+{
+ driver_enter(SQL_API_SQLFREECONNECT);
+ const char* const sqlFunction = "SQLFreeConnect";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLFREECONNECT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleEnv* pEnv = pDbc->getEnv();
+ try {
+ pEnv->sqlFreeConnect(ctx, pDbc);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (! ctx.ok()) {
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFREECONNECT);
+ return ret;
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ delete &ctx;
+ driver_exit(SQL_API_SQLFREECONNECT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLFreeEnv.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeEnv.cpp
new file mode 100644
index 00000000000..7e35056feb5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeEnv.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLFreeEnv(
+ SQLHENV EnvironmentHandle)
+{
+ driver_enter(SQL_API_SQLFREEENV);
+ const char* const sqlFunction = "SQLFreeEnv";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLFREEENV);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pRoot->sqlFreeEnv(ctx, pEnv);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (! ctx.ok()) {
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFREEENV);
+ return ret;
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ delete &ctx;
+ driver_exit(SQL_API_SQLFREEENV);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLFreeHandle.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeHandle.cpp
new file mode 100644
index 00000000000..284463cbb07
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeHandle.cpp
@@ -0,0 +1,60 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLFreeHandle(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle)
+{
+ driver_enter(SQL_API_SQLFREEHANDLE);
+ const char* const sqlFunction = "SQLFreeHandle";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ SQLSMALLINT parentType = pRoot->findParentType(HandleType);
+ if (parentType == -1) {
+ driver_exit(SQL_API_SQLFREEHANDLE);
+ return SQL_INVALID_HANDLE;
+ }
+ HandleBase* pChild = pRoot->findBase(HandleType, Handle);
+ if (pChild == 0) {
+ driver_exit(SQL_API_SQLFREEHANDLE);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleBase* pParent = pChild->getParent();
+ ctx_assert(pParent != 0);
+ try {
+ pParent->sqlFreeHandle(ctx, HandleType, pChild);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (! ctx.ok()) {
+ pChild->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFREEHANDLE);
+ return ret;
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ delete &ctx;
+ driver_exit(SQL_API_SQLFREEHANDLE);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLFreeStmt.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeStmt.cpp
new file mode 100644
index 00000000000..7af6623a37a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLFreeStmt.cpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLFreeStmt(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT Option)
+{
+ driver_enter(SQL_API_SQLFREESTMT);
+ const char* const sqlFunction = "SQLFreeStmt";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLFREESTMT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ HandleDbc* pDbc = pStmt->getDbc();
+ try {
+ pDbc->sqlFreeStmt(ctx, pStmt, Option);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ if (! ctx.ok() || Option != SQL_DROP) {
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLFREESTMT);
+ return ret;
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ delete &ctx;
+ driver_exit(SQL_API_SQLFREESTMT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectAttr.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectAttr.cpp
new file mode 100644
index 00000000000..66c1f3827e1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectAttr.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetConnectAttr(
+ SQLHDBC ConnectionHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER Value,
+ SQLINTEGER BufferLength,
+ SQLINTEGER* StringLength)
+{
+ driver_enter(SQL_API_SQLGETCONNECTATTR);
+ const char* const sqlFunction = "SQLGetConnectAttr";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLGETCONNECTATTR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlGetConnectAttr(ctx, Attribute, Value, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETCONNECTATTR);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectOption.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectOption.cpp
new file mode 100644
index 00000000000..514bedb12b9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectOption.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetConnectOption(
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT Option,
+ SQLPOINTER Value)
+{
+ driver_enter(SQL_API_SQLGETCONNECTOPTION);
+ const char* const sqlFunction = "SQLGetConnectOption";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLGETCONNECTOPTION);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlGetConnectOption(ctx, Option, Value);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETCONNECTOPTION);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetCursorName.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetCursorName.cpp
new file mode 100644
index 00000000000..d54bdf42005
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetCursorName.cpp
@@ -0,0 +1,48 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetCursorName(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* CursorName,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* NameLength)
+{
+ driver_enter(SQL_API_SQLGETCURSORNAME);
+ const char* const sqlFunction = "SQLGetCursorName";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLGETCURSORNAME);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlGetCursorName(ctx, CursorName, BufferLength, NameLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETCURSORNAME);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetData.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetData.cpp
new file mode 100644
index 00000000000..3b6987c515d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetData.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetData(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber,
+ SQLSMALLINT TargetType,
+ SQLPOINTER TargetValue,
+ SQLINTEGER BufferLength,
+ SQLINTEGER* StrLen_or_Ind)
+{
+ driver_enter(SQL_API_SQLGETDATA);
+ const char* const sqlFunction = "SQLGetData";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLGETDATA);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlGetData(ctx, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETDATA);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetDescField.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDescField.cpp
new file mode 100644
index 00000000000..6cc390a58ed
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDescField.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetDescField(
+ SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber,
+ SQLSMALLINT FieldIdentifier,
+ SQLPOINTER Value,
+ SQLINTEGER BufferLength,
+ SQLINTEGER* StringLength)
+{
+ driver_enter(SQL_API_SQLGETDESCFIELD);
+ const char* const sqlFunction = "SQLGetDescField";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDesc* pDesc = pRoot->findDesc(DescriptorHandle);
+ if (pDesc == 0) {
+ driver_exit(SQL_API_SQLGETDESCFIELD);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDesc->sqlGetDescField(ctx, RecNumber, FieldIdentifier, Value, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDesc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETDESCFIELD);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetDescRec.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDescRec.cpp
new file mode 100644
index 00000000000..c7e9631b075
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDescRec.cpp
@@ -0,0 +1,55 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetDescRec(
+ SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber,
+ SQLCHAR* Name,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* StringLength,
+ SQLSMALLINT* Type,
+ SQLSMALLINT* SubType,
+ SQLINTEGER* Length,
+ SQLSMALLINT* Precision,
+ SQLSMALLINT* Scale,
+ SQLSMALLINT* Nullable)
+{
+ driver_enter(SQL_API_SQLGETDESCREC);
+ const char* const sqlFunction = "SQLGetDescRec";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDesc* pDesc = pRoot->findDesc(DescriptorHandle);
+ if (pDesc == 0) {
+ driver_exit(SQL_API_SQLGETDESCREC);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDesc->sqlGetDescRec(ctx, RecNumber, Name, BufferLength, StringLength, Type, SubType, Length, Precision, Scale, Nullable);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDesc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETDESCREC);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagField.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagField.cpp
new file mode 100644
index 00000000000..3eb34f7ebf6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagField.cpp
@@ -0,0 +1,50 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetDiagField(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle,
+ SQLSMALLINT RecNumber,
+ SQLSMALLINT DiagIdentifier,
+ SQLPOINTER DiagInfo,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* StringLength)
+{
+ driver_enter(SQL_API_SQLGETDIAGFIELD);
+ const char* const sqlFunction = "SQLGetDiagField";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleBase* pBase = pRoot->findBase(HandleType, Handle);
+ if (pBase == 0) {
+ driver_exit(SQL_API_SQLGETDIAGFIELD);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx ctx; // discard diagnostics
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pBase->sqlGetDiagField(ctx, RecNumber, DiagIdentifier, DiagInfo, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETDIAGFIELD);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagRec.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagRec.cpp
new file mode 100644
index 00000000000..448c5206d76
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagRec.cpp
@@ -0,0 +1,51 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetDiagRec(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle,
+ SQLSMALLINT RecNumber,
+ SQLCHAR* Sqlstate,
+ SQLINTEGER* NativeError,
+ SQLCHAR* MessageText,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* TextLength)
+{
+ driver_enter(SQL_API_SQLGETDIAGREC);
+ const char* const sqlFunction = "SQLGetDiagRec";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleBase* pBase = pRoot->findBase(HandleType, Handle);
+ if (pBase == 0) {
+ driver_exit(SQL_API_SQLGETDIAGREC);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx ctx; // discard diagnostics
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pBase->sqlGetDiagRec(ctx, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETDIAGREC);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetEnvAttr.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetEnvAttr.cpp
new file mode 100644
index 00000000000..c93870326e4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetEnvAttr.cpp
@@ -0,0 +1,66 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetEnvAttr(
+ SQLHENV EnvironmentHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER Value,
+ SQLINTEGER BufferLength,
+ SQLINTEGER* StringLength)
+{
+ driver_enter(SQL_API_SQLGETENVATTR);
+ const char* const sqlFunction = "SQLGetEnvAttr";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ if (EnvironmentHandle == 0) {
+ // process-level attributes
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pRoot->sqlGetRootAttr(ctx, Attribute, Value, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pRoot->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETENVATTR);
+ return ret;
+ } else {
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLGETENVATTR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pEnv->sqlGetEnvAttr(ctx, Attribute, Value, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETENVATTR);
+ return ret;
+ }
+ return SQL_ERROR; // not reached
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetFunctions.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetFunctions.cpp
new file mode 100644
index 00000000000..68416fab1a6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetFunctions.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetFunctions(
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT FunctionId,
+ SQLUSMALLINT* Supported)
+{
+ driver_enter(SQL_API_SQLGETFUNCTIONS);
+ const char* const sqlFunction = "SQLGetFunctions";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLGETFUNCTIONS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlGetFunctions(ctx, FunctionId, Supported);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETFUNCTIONS);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetInfo.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetInfo.cpp
new file mode 100644
index 00000000000..8f0a0d67cfa
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetInfo.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetInfo(
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT InfoType,
+ SQLPOINTER InfoValue,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT* StringLength)
+{
+ driver_enter(SQL_API_SQLGETINFO);
+ const char* const sqlFunction = "SQLGetInfo";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLGETINFO);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlGetInfo(ctx, InfoType, InfoValue, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETINFO);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtAttr.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtAttr.cpp
new file mode 100644
index 00000000000..990ab68808a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtAttr.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLGetStmtAttr(
+ SQLHSTMT StatementHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER Value,
+ SQLINTEGER BufferLength,
+ SQLINTEGER* StringLength)
+{
+ driver_enter(SQL_API_SQLGETSTMTATTR);
+ const char* const sqlFunction = "SQLGetStmtAttr";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLGETSTMTATTR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlGetStmtAttr(ctx, Attribute, Value, BufferLength, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETSTMTATTR);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtOption.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtOption.cpp
new file mode 100644
index 00000000000..0b5758b1212
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtOption.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetStmtOption(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT Option,
+ SQLPOINTER Value)
+{
+ driver_enter(SQL_API_SQLGETSTMTOPTION);
+ const char* const sqlFunction = "SQLGetStmtOption";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLGETSTMTOPTION);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlGetStmtOption(ctx, Option, Value);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETSTMTOPTION);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLGetTypeInfo.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLGetTypeInfo.cpp
new file mode 100644
index 00000000000..e6a016cc400
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLGetTypeInfo.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLGetTypeInfo(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT DataType)
+{
+ driver_enter(SQL_API_SQLGETTYPEINFO);
+ const char* const sqlFunction = "SQLGetTypeInfo";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLGETTYPEINFO);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlGetTypeInfo(ctx, DataType);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLGETTYPEINFO);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLMoreResults.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLMoreResults.cpp
new file mode 100644
index 00000000000..d23d653a319
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLMoreResults.cpp
@@ -0,0 +1,42 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLMoreResults(
+ SQLHSTMT hstmt)
+{
+ driver_enter(SQL_API_SQLMORERESULTS);
+ const char* const sqlFunction = "SQLMoreResults";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLMORERESULTS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlMoreResults(ctx);
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLMORERESULTS);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLNativeSql.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLNativeSql.cpp
new file mode 100644
index 00000000000..fb8a9bbf3d9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLNativeSql.cpp
@@ -0,0 +1,61 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLNativeSql(
+ SQLHDBC hdbc,
+ SQLCHAR* szSqlStrIn,
+ SQLINTEGER cbSqlStrIn,
+ SQLCHAR* szSqlStr,
+ SQLINTEGER cbSqlStrMax,
+ SQLINTEGER* pcbSqlStr)
+{
+#ifndef auto_SQLNativeSql
+ const char* const sqlFunction = "SQLNativeSql";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLNATIVESQL);
+ const char* const sqlFunction = "SQLNativeSql";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(hdbc);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLNATIVESQL);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pDbc->sqlNativeSql(
+ ctx,
+ &szSqlStrIn,
+ cbSqlStrIn,
+ &szSqlStr,
+ cbSqlStrMax,
+ &pcbSqlStr
+ );
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLNATIVESQL);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLNumParams.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLNumParams.cpp
new file mode 100644
index 00000000000..7b1a6a07aec
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLNumParams.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLNumParams(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT* ParameterCountPtr)
+{
+ driver_enter(SQL_API_SQLNUMPARAMS);
+ const char* const sqlFunction = "SQLNumParams";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLNUMPARAMS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlNumParams(ctx, ParameterCountPtr);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLNUMPARAMS);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLNumResultCols.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLNumResultCols.cpp
new file mode 100644
index 00000000000..2e70897a9a2
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLNumResultCols.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLNumResultCols(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT* ColumnCount)
+{
+ driver_enter(SQL_API_SQLNUMRESULTCOLS);
+ const char* const sqlFunction = "SQLNumResultCols";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLNUMRESULTCOLS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlNumResultCols(ctx, ColumnCount);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLNUMRESULTCOLS);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLParamData.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLParamData.cpp
new file mode 100644
index 00000000000..4eb38a010f4
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLParamData.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLParamData(
+ SQLHSTMT StatementHandle,
+ SQLPOINTER* Value)
+{
+ driver_enter(SQL_API_SQLPARAMDATA);
+ const char* const sqlFunction = "SQLParamData";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPARAMDATA);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlParamData(ctx, Value);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPARAMDATA);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLParamOptions.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLParamOptions.cpp
new file mode 100644
index 00000000000..59b7dcf7fa9
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLParamOptions.cpp
@@ -0,0 +1,55 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLParamOptions(
+ SQLHSTMT hstmt,
+ SQLUINTEGER crow,
+ SQLUINTEGER* pirow)
+{
+#ifndef auto_SQLParamOptions
+ const char* const sqlFunction = "SQLParamOptions";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLPARAMOPTIONS);
+ const char* const sqlFunction = "SQLParamOptions";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPARAMOPTIONS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlParamOptions(
+ ctx,
+ crow,
+ &pirow
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPARAMOPTIONS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLPrepare.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLPrepare.cpp
new file mode 100644
index 00000000000..b1205fa6e3a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLPrepare.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLPrepare(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* StatementText,
+ SQLINTEGER TextLength)
+{
+ driver_enter(SQL_API_SQLPREPARE);
+ const char* const sqlFunction = "SQLPrepare";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPREPARE);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlPrepare(ctx, StatementText, TextLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPREPARE);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLPrimaryKeys.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLPrimaryKeys.cpp
new file mode 100644
index 00000000000..2d562ae3e19
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLPrimaryKeys.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLPrimaryKeys(
+ SQLHSTMT hstmt,
+ SQLCHAR* szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR* szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR* szTableName,
+ SQLSMALLINT cbTableName)
+{
+ driver_enter(SQL_API_SQLPRIMARYKEYS);
+ const char* const sqlFunction = "SQLPrimaryKeys";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPRIMARYKEYS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ pStmt->sqlPrimaryKeys(ctx, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName);
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPRIMARYKEYS);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLProcedureColumns.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLProcedureColumns.cpp
new file mode 100644
index 00000000000..2e42e428b87
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLProcedureColumns.cpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLProcedureColumns(
+ SQLHSTMT hstmt,
+ SQLCHAR* szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR* szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR* szProcName,
+ SQLSMALLINT cbProcName,
+ SQLCHAR* szColumnName,
+ SQLSMALLINT cbColumnName)
+{
+#ifndef auto_SQLProcedureColumns
+ const char* const sqlFunction = "SQLProcedureColumns";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLPROCEDURECOLUMNS);
+ const char* const sqlFunction = "SQLProcedureColumns";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPROCEDURECOLUMNS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlProcedureColumns(
+ ctx,
+ &szCatalogName,
+ cbCatalogName,
+ &szSchemaName,
+ cbSchemaName,
+ &szProcName,
+ cbProcName,
+ &szColumnName,
+ cbColumnName
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPROCEDURECOLUMNS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLProcedures.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLProcedures.cpp
new file mode 100644
index 00000000000..1f3a9f89073
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLProcedures.cpp
@@ -0,0 +1,63 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLProcedures(
+ SQLHSTMT hstmt,
+ SQLCHAR* szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR* szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR* szProcName,
+ SQLSMALLINT cbProcName)
+{
+#ifndef auto_SQLProcedures
+ const char* const sqlFunction = "SQLProcedures";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLPROCEDURES);
+ const char* const sqlFunction = "SQLProcedures";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPROCEDURES);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlProcedures(
+ ctx,
+ &szCatalogName,
+ cbCatalogName,
+ &szSchemaName,
+ cbSchemaName,
+ &szProcName,
+ cbProcName
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPROCEDURES);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLPutData.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLPutData.cpp
new file mode 100644
index 00000000000..a4715a836d2
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLPutData.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLPutData(
+ SQLHSTMT StatementHandle,
+ SQLPOINTER Data,
+ SQLINTEGER StrLen_or_Ind)
+{
+ driver_enter(SQL_API_SQLPUTDATA);
+ const char* const sqlFunction = "SQLPutData";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLPUTDATA);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlPutData(ctx, Data, StrLen_or_Ind);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLPUTDATA);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLRowCount.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLRowCount.cpp
new file mode 100644
index 00000000000..d03f954386a
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLRowCount.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLRowCount(
+ SQLHSTMT StatementHandle,
+ SQLINTEGER* RowCount)
+{
+ driver_enter(SQL_API_SQLROWCOUNT);
+ const char* const sqlFunction = "SQLRowCount";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLROWCOUNT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlRowCount(ctx, RowCount);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLROWCOUNT);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectAttr.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectAttr.cpp
new file mode 100644
index 00000000000..05bfce5e9cd
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectAttr.cpp
@@ -0,0 +1,48 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLSetConnectAttr(
+ SQLHDBC ConnectionHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER Value,
+ SQLINTEGER StringLength)
+{
+ driver_enter(SQL_API_SQLSETCONNECTATTR);
+ const char* const sqlFunction = "SQLSetConnectAttr";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLSETCONNECTATTR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlSetConnectAttr(ctx, Attribute, Value, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETCONNECTATTR);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectOption.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectOption.cpp
new file mode 100644
index 00000000000..a4794316971
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectOption.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSetConnectOption(
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT Option,
+ SQLUINTEGER Value)
+{
+ driver_enter(SQL_API_SQLSETCONNECTOPTION);
+ const char* const sqlFunction = "SQLSetConnectOption";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLSETCONNECTOPTION);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlSetConnectOption(ctx, Option, Value);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETCONNECTOPTION);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetCursorName.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetCursorName.cpp
new file mode 100644
index 00000000000..291ad817d42
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetCursorName.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSetCursorName(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* CursorName,
+ SQLSMALLINT NameLength)
+{
+ driver_enter(SQL_API_SQLSETCURSORNAME);
+ const char* const sqlFunction = "SQLSetCursorName";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSETCURSORNAME);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlSetCursorName(ctx, CursorName, NameLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETCURSORNAME);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetDescField.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetDescField.cpp
new file mode 100644
index 00000000000..19d34c2f46d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetDescField.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLSetDescField(
+ SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber,
+ SQLSMALLINT FieldIdentifier,
+ SQLPOINTER Value,
+ SQLINTEGER BufferLength)
+{
+ driver_enter(SQL_API_SQLSETDESCFIELD);
+ const char* const sqlFunction = "SQLSetDescField";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDesc* pDesc = pRoot->findDesc(DescriptorHandle);
+ if (pDesc == 0) {
+ driver_exit(SQL_API_SQLSETDESCFIELD);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDesc->sqlSetDescField(ctx, RecNumber, FieldIdentifier, Value, BufferLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDesc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETDESCFIELD);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetDescRec.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetDescRec.cpp
new file mode 100644
index 00000000000..80a00514a51
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetDescRec.cpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLSetDescRec(
+ SQLHDESC DescriptorHandle,
+ SQLSMALLINT RecNumber,
+ SQLSMALLINT Type,
+ SQLSMALLINT SubType,
+ SQLINTEGER Length,
+ SQLSMALLINT Precision,
+ SQLSMALLINT Scale,
+ SQLPOINTER Data,
+ SQLINTEGER* StringLength,
+ SQLINTEGER* Indicator)
+{
+ driver_enter(SQL_API_SQLSETDESCREC);
+ const char* const sqlFunction = "SQLSetDescRec";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleDesc* pDesc = pRoot->findDesc(DescriptorHandle);
+ if (pDesc == 0) {
+ driver_exit(SQL_API_SQLSETDESCREC);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDesc->sqlSetDescRec(ctx, RecNumber, Type, SubType, Length, Precision, Scale, Data, StringLength, Indicator);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDesc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETDESCREC);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetEnvAttr.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetEnvAttr.cpp
new file mode 100644
index 00000000000..86364eac5e8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetEnvAttr.cpp
@@ -0,0 +1,65 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLSetEnvAttr(
+ SQLHENV EnvironmentHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER Value,
+ SQLINTEGER StringLength)
+{
+ driver_enter(SQL_API_SQLSETENVATTR);
+ const char* const sqlFunction = "SQLSetEnvAttr";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ if (EnvironmentHandle == 0) {
+ // process-level attributes
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pRoot->sqlSetRootAttr(ctx, Attribute, Value, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pRoot->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETENVATTR);
+ return ret;
+ } else {
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLSETENVATTR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pEnv->sqlSetEnvAttr(ctx, Attribute, Value, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETENVATTR);
+ return ret;
+ }
+ return SQL_ERROR; // not reached
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetParam.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetParam.cpp
new file mode 100644
index 00000000000..03bde1076d8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetParam.cpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSetParam(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ParameterNumber,
+ SQLSMALLINT ValueType,
+ SQLSMALLINT ParameterType,
+ SQLUINTEGER LengthPrecision,
+ SQLSMALLINT ParameterScale,
+ SQLPOINTER ParameterValue,
+ SQLINTEGER* StrLen_or_Ind)
+{
+ driver_enter(SQL_API_SQLSETPARAM);
+ const char* const sqlFunction = "SQLSetParam";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSETPARAM);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlSetParam(ctx, ParameterNumber, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValue, StrLen_or_Ind);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETPARAM);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetPos.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetPos.cpp
new file mode 100644
index 00000000000..653030f90bc
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetPos.cpp
@@ -0,0 +1,57 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSetPos(
+ SQLHSTMT hstmt,
+ SQLUSMALLINT irow,
+ SQLUSMALLINT fOption,
+ SQLUSMALLINT fLock)
+{
+#ifndef auto_SQLSetPos
+ const char* const sqlFunction = "SQLSetPos";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLSETPOS);
+ const char* const sqlFunction = "SQLSetPos";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSETPOS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlSetPos(
+ ctx,
+ irow,
+ fOption,
+ fLock
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETPOS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetScrollOptions.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetScrollOptions.cpp
new file mode 100644
index 00000000000..a5e89d8568b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetScrollOptions.cpp
@@ -0,0 +1,57 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSetScrollOptions(
+ SQLHSTMT hstmt,
+ SQLUSMALLINT fConcurrency,
+ SQLINTEGER crowKeyset,
+ SQLUSMALLINT crowRowset)
+{
+#ifndef auto_SQLSetScrollOptions
+ const char* const sqlFunction = "SQLSetScrollOptions";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLSETSCROLLOPTIONS);
+ const char* const sqlFunction = "SQLSetScrollOptions";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSETSCROLLOPTIONS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlSetScrollOptions(
+ ctx,
+ fConcurrency,
+ crowKeyset,
+ crowRowset
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETSCROLLOPTIONS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtAttr.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtAttr.cpp
new file mode 100644
index 00000000000..9ed6a83b563
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtAttr.cpp
@@ -0,0 +1,48 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0300
+SQLRETURN SQL_API
+SQLSetStmtAttr(
+ SQLHSTMT StatementHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER Value,
+ SQLINTEGER StringLength)
+{
+ driver_enter(SQL_API_SQLSETSTMTATTR);
+ const char* const sqlFunction = "SQLSetStmtAttr";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSETSTMTATTR);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlSetStmtAttr(ctx, Attribute, Value, StringLength);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETSTMTATTR);
+ return ret;
+}
+#endif // ODBCVER >= 0x0300
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtOption.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtOption.cpp
new file mode 100644
index 00000000000..b403fc8408c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtOption.cpp
@@ -0,0 +1,47 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSetStmtOption(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT Option,
+ SQLUINTEGER Value)
+{
+ driver_enter(SQL_API_SQLSETSTMTOPTION);
+ const char* const sqlFunction = "SQLSetStmtOption";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSETSTMTOPTION);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pStmt->sqlSetStmtOption(ctx, Option, Value);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSETSTMTOPTION);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLSpecialColumns.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLSpecialColumns.cpp
new file mode 100644
index 00000000000..5dd92c86053
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLSpecialColumns.cpp
@@ -0,0 +1,69 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLSpecialColumns(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT IdentifierType,
+ SQLCHAR* CatalogName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR* SchemaName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR* TableName,
+ SQLSMALLINT NameLength3,
+ SQLUSMALLINT Scope,
+ SQLUSMALLINT Nullable)
+{
+#ifndef auto_SQLSpecialColumns
+ const char* const sqlFunction = "SQLSpecialColumns";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLSPECIALCOLUMNS);
+ const char* const sqlFunction = "SQLSpecialColumns";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSPECIALCOLUMNS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlSpecialColumns(
+ ctx,
+ IdentifierType,
+ &CatalogName,
+ NameLength1,
+ &SchemaName,
+ NameLength2,
+ &TableName,
+ NameLength3,
+ Scope,
+ Nullable
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSPECIALCOLUMNS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLStatistics.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLStatistics.cpp
new file mode 100644
index 00000000000..941fb6249a5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLStatistics.cpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLStatistics(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* CatalogName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR* SchemaName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR* TableName,
+ SQLSMALLINT NameLength3,
+ SQLUSMALLINT Unique,
+ SQLUSMALLINT Reserved)
+{
+#ifndef auto_SQLStatistics
+ const char* const sqlFunction = "SQLStatistics";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLSTATISTICS);
+ const char* const sqlFunction = "SQLStatistics";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLSTATISTICS);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlStatistics(
+ ctx,
+ &CatalogName,
+ NameLength1,
+ &SchemaName,
+ NameLength2,
+ &TableName,
+ NameLength3,
+ Unique,
+ Reserved
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLSTATISTICS);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLTablePrivileges.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLTablePrivileges.cpp
new file mode 100644
index 00000000000..23c6ad9fc4b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLTablePrivileges.cpp
@@ -0,0 +1,63 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLTablePrivileges(
+ SQLHSTMT hstmt,
+ SQLCHAR* szCatalogName,
+ SQLSMALLINT cbCatalogName,
+ SQLCHAR* szSchemaName,
+ SQLSMALLINT cbSchemaName,
+ SQLCHAR* szTableName,
+ SQLSMALLINT cbTableName)
+{
+#ifndef auto_SQLTablePrivileges
+ const char* const sqlFunction = "SQLTablePrivileges";
+ Ctx ctx;
+ ctx_log1(("*** not implemented: %s", sqlFunction));
+ return SQL_ERROR;
+#else
+ driver_enter(SQL_API_SQLTABLEPRIVILEGES);
+ const char* const sqlFunction = "SQLTablePrivileges";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(hstmt);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLTABLEPRIVILEGES);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ if (ctx.ok())
+ pStmt->sqlTablePrivileges(
+ ctx,
+ &szCatalogName,
+ cbCatalogName,
+ &szSchemaName,
+ cbSchemaName,
+ &szTableName,
+ cbTableName
+ );
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLTABLEPRIVILEGES);
+ return ret;
+#endif
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLTables.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLTables.cpp
new file mode 100644
index 00000000000..b2496bfba87
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLTables.cpp
@@ -0,0 +1,49 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLTables(
+ SQLHSTMT StatementHandle,
+ SQLCHAR* CatalogName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR* SchemaName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR* TableName,
+ SQLSMALLINT NameLength3,
+ SQLCHAR* TableType,
+ SQLSMALLINT NameLength4)
+{
+ driver_enter(SQL_API_SQLTABLES);
+ const char* const sqlFunction = "SQLTables";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ HandleStmt* pStmt = pRoot->findStmt(StatementHandle);
+ if (pStmt == 0) {
+ driver_exit(SQL_API_SQLTABLES);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ pStmt->sqlTables(ctx, CatalogName, NameLength1, SchemaName, NameLength2, TableName, NameLength3, TableType, NameLength4);
+ pStmt->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLTABLES);
+ return ret;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/SQLTransact.cpp b/storage/ndb/src/old_files/client/odbc/driver/SQLTransact.cpp
new file mode 100644
index 00000000000..da8b46b1596
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/SQLTransact.cpp
@@ -0,0 +1,71 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+
+#if ODBCVER >= 0x0000
+SQLRETURN SQL_API
+SQLTransact(
+ SQLHENV EnvironmentHandle,
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT CompletionType)
+{
+ driver_enter(SQL_API_SQLTRANSACT);
+ const char* const sqlFunction = "SQLTransact";
+ HandleRoot* const pRoot = HandleRoot::instance();
+ // check connection first and ignore environment
+ if (ConnectionHandle != SQL_NULL_HANDLE) {
+ HandleDbc* pDbc = pRoot->findDbc(ConnectionHandle);
+ if (pDbc == 0) {
+ driver_exit(SQL_API_SQLTRANSACT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pDbc->sqlTransact(ctx, CompletionType);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pDbc->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLTRANSACT);
+ return ret;
+ }
+ if (EnvironmentHandle != SQL_NULL_HANDLE) {
+ HandleEnv* pEnv = pRoot->findEnv(EnvironmentHandle);
+ if (pEnv == 0) {
+ driver_exit(SQL_API_SQLTRANSACT);
+ return SQL_INVALID_HANDLE;
+ }
+ Ctx& ctx = *new Ctx;
+ ctx.logSqlEnter(sqlFunction);
+ try {
+ pEnv->sqlTransact(ctx, CompletionType);
+ } catch (CtxAssert& ctxAssert) {
+ ctx.handleEx(ctxAssert);
+ }
+ pEnv->saveCtx(ctx);
+ ctx.logSqlExit();
+ SQLRETURN ret = ctx.getCode();
+ driver_exit(SQL_API_SQLTRANSACT);
+ return ret;
+ }
+ driver_exit(SQL_API_SQLTRANSACT);
+ return SQL_INVALID_HANDLE;
+}
+#endif // ODBCVER >= 0x0000
diff --git a/storage/ndb/src/old_files/client/odbc/driver/driver.cpp b/storage/ndb/src/old_files/client/odbc/driver/driver.cpp
new file mode 100644
index 00000000000..f992fa70878
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/driver.cpp
@@ -0,0 +1,150 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "driver.hpp"
+#include <NdbMutex.h>
+
+#undef NDB_ODBC_SIG_DFL
+#ifdef NDB_ODBC_SIG_DFL
+#include <signal.h>
+#endif
+
+// The big mutex (just in case).
+
+#ifdef NDB_WIN32
+static NdbMutex & driver_mutex = * NdbMutex_Create();
+#else
+static NdbMutex driver_mutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+static void
+driver_lock()
+{
+ NdbMutex_Lock(&driver_mutex);
+}
+
+static void
+driver_unlock()
+{
+ NdbMutex_Unlock(&driver_mutex);
+}
+
+// Hooks for function entry and exit.
+
+static inline void
+driver_enter(SQLUSMALLINT functionId)
+{
+ switch (functionId) {
+ default:
+ break;
+ }
+#ifdef NDB_ODBC_SIG_DFL
+ // XXX need to restore old sig
+ for (int i = 1; i <= 30; i++)
+ signal(i, SIG_DFL);
+#endif
+}
+
+static inline void
+driver_exit(SQLUSMALLINT functionId)
+{
+ switch (functionId) {
+ default:
+ break;
+ }
+}
+
+// Some C++ compilers (like gcc) cannot merge template code
+// in different files. So compile all in one file.
+
+#include "SQLAllocConnect.cpp"
+#include "SQLAllocEnv.cpp"
+#include "SQLAllocHandle.cpp"
+#include "SQLAllocHandleStd.cpp"
+#include "SQLAllocStmt.cpp"
+#include "SQLBindCol.cpp"
+#include "SQLBindParam.cpp"
+#include "SQLBindParameter.cpp"
+#include "SQLBrowseConnect.cpp"
+#include "SQLBulkOperations.cpp"
+#include "SQLCancel.cpp"
+#include "SQLCloseCursor.cpp"
+#include "SQLColAttribute.cpp"
+#include "SQLColAttributes.cpp"
+#include "SQLColumnPrivileges.cpp"
+#include "SQLColumns.cpp"
+#include "SQLConnect.cpp"
+#include "SQLCopyDesc.cpp"
+#include "SQLDataSources.cpp"
+#include "SQLDescribeCol.cpp"
+#include "SQLDescribeParam.cpp"
+#include "SQLDisconnect.cpp"
+#include "SQLDriverConnect.cpp"
+#include "SQLDrivers.cpp"
+#include "SQLEndTran.cpp"
+#include "SQLError.cpp"
+#include "SQLExecDirect.cpp"
+#include "SQLExecute.cpp"
+#include "SQLExtendedFetch.cpp"
+#include "SQLFetch.cpp"
+#include "SQLFetchScroll.cpp"
+#include "SQLForeignKeys.cpp"
+#include "SQLFreeConnect.cpp"
+#include "SQLFreeEnv.cpp"
+#include "SQLFreeHandle.cpp"
+#include "SQLFreeStmt.cpp"
+#include "SQLGetConnectAttr.cpp"
+#include "SQLGetConnectOption.cpp"
+#include "SQLGetCursorName.cpp"
+#include "SQLGetData.cpp"
+#include "SQLGetDescField.cpp"
+#include "SQLGetDescRec.cpp"
+#include "SQLGetDiagField.cpp"
+#include "SQLGetDiagRec.cpp"
+#include "SQLGetEnvAttr.cpp"
+#include "SQLGetFunctions.cpp"
+#include "SQLGetInfo.cpp"
+#include "SQLGetStmtAttr.cpp"
+#include "SQLGetStmtOption.cpp"
+#include "SQLGetTypeInfo.cpp"
+#include "SQLMoreResults.cpp"
+#include "SQLNativeSql.cpp"
+#include "SQLNumParams.cpp"
+#include "SQLNumResultCols.cpp"
+#include "SQLParamData.cpp"
+#include "SQLParamOptions.cpp"
+#include "SQLPrepare.cpp"
+#include "SQLPrimaryKeys.cpp"
+#include "SQLProcedureColumns.cpp"
+#include "SQLProcedures.cpp"
+#include "SQLPutData.cpp"
+#include "SQLRowCount.cpp"
+#include "SQLSetConnectAttr.cpp"
+#include "SQLSetConnectOption.cpp"
+#include "SQLSetCursorName.cpp"
+#include "SQLSetDescField.cpp"
+#include "SQLSetDescRec.cpp"
+#include "SQLSetEnvAttr.cpp"
+#include "SQLSetParam.cpp"
+#include "SQLSetPos.cpp"
+#include "SQLSetScrollOptions.cpp"
+#include "SQLSetStmtAttr.cpp"
+#include "SQLSetStmtOption.cpp"
+#include "SQLSpecialColumns.cpp"
+#include "SQLStatistics.cpp"
+#include "SQLTablePrivileges.cpp"
+#include "SQLTables.cpp"
+#include "SQLTransact.cpp"
diff --git a/storage/ndb/src/old_files/client/odbc/driver/driver.hpp b/storage/ndb/src/old_files/client/odbc/driver/driver.hpp
new file mode 100644
index 00000000000..96d2e052c0d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/driver/driver.hpp
@@ -0,0 +1,28 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_DRIVER_driver_hpp
+#define ODBC_DRIVER_driver_hpp
+
+#include <common/common.hpp>
+#include <common/OdbcData.hpp>
+#include <handles/handles.hpp>
+
+#ifndef NDB_WIN32
+#define SQL_API
+#endif
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_comp_op.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_comp_op.cpp
new file mode 100644
index 00000000000..40d3950a592
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_comp_op.cpp
@@ -0,0 +1,518 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbScanFilter.hpp>
+#include <NdbSqlUtil.hpp>
+#include <codegen/Code_comp_op.hpp>
+
+void
+Exec_comp_op::execInterp(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ const unsigned arity = code.m_op.arity();
+ const Comp_op::Opcode opcode = code.m_op.m_opcode;
+ Data& data = getData();
+ ctx_assert(ctl.m_scanFilter != 0);
+ NdbScanFilter& scanFilter = *ctl.m_scanFilter;
+ if (code.m_interpColumn == 0) {
+ // args are constant on this level so evaluate entire predicate
+ evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (data.m_value == Pred_value_true)
+ scanFilter.istrue();
+ else
+ scanFilter.isfalse();
+ return;
+ }
+ const NdbAttrId interpAttrId = code.m_interpAttrId;
+ if (arity == 1) {
+ ctx_assert(m_expr[1] != 0);
+ const SqlType& t1 = m_expr[1]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = m_expr[1]->getData().sqlField();
+ switch (code.m_op.m_opcode) {
+ case Comp_op::Isnull:
+ scanFilter.isnull(interpAttrId);
+ break;
+ case Comp_op::Isnotnull:
+ scanFilter.isnotnull(interpAttrId);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (arity == 2) {
+ ctx_assert(m_expr[1] != 0 && m_expr[2] != 0);
+ // one is column and the other is constant at this level
+ ctx_assert(code.m_interpColumn == 1 || code.m_interpColumn == 2);
+ const unsigned i = code.m_interpColumn;
+ const unsigned j = 3 - i;
+ // evaluate the constant
+ m_expr[j]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlType& t1 = m_expr[i]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = m_expr[i]->getData().sqlField();
+ const SqlType& t2 = m_expr[j]->getCode().sqlSpec().sqlType();
+ const SqlField& f2 = m_expr[j]->getData().sqlField();
+ // handle null constant
+ if (f2.sqlNull()) {
+ scanFilter.isfalse();
+ return;
+ }
+ // handle null in interpreter
+ scanFilter.begin(NdbScanFilter::AND);
+ scanFilter.isnotnull(interpAttrId);
+ if (t1.type() == SqlType::Char || t1.type() == SqlType::Varchar) {
+ const char* v2 = 0;
+ unsigned n2 = 0;
+ bool nopad = false;
+ if (t1.type() == SqlType::Char && t2.type() == SqlType::Char) {
+ v2 = reinterpret_cast<const char*>(f2.sqlChar());
+ n2 = t2.length();
+ nopad = false;
+ } else if (t1.type() == SqlType::Char && t2.type() == SqlType::Varchar) {
+ v2 = reinterpret_cast<const char*>(f2.sqlVarchar(&n2));
+ nopad = true;
+ } else if (t1.type() == SqlType::Varchar && t2.type() == SqlType::Char) {
+ v2 = reinterpret_cast<const char*>(f2.sqlChar());
+ n2 = t2.length();
+ nopad = true;
+ } else if (t1.type() == SqlType::Varchar && t2.type() == SqlType::Varchar) {
+ v2 = reinterpret_cast<const char*>(f2.sqlVarchar(&n2));
+ nopad = true;
+ } else {
+ ctx_assert(false);
+ }
+ switch (opcode) {
+ case Comp_op::Eq:
+ scanFilter.eq(interpAttrId, v2, n2, nopad);
+ break;
+ case Comp_op::Noteq:
+ scanFilter.ne(interpAttrId, v2, n2, nopad);
+ break;
+ case Comp_op::Lt:
+ if (i == 1) {
+ scanFilter.lt(interpAttrId, v2, n2, nopad);
+ } else {
+ scanFilter.gt(interpAttrId, v2, n2, nopad);
+ }
+ break;
+ case Comp_op::Lteq:
+ if (i == 1) {
+ scanFilter.le(interpAttrId, v2, n2, nopad);
+ } else {
+ scanFilter.ge(interpAttrId, v2, n2, nopad);
+ }
+ break;
+ case Comp_op::Gt:
+ if (i == 1) {
+ scanFilter.gt(interpAttrId, v2, n2, nopad);
+ } else {
+ scanFilter.lt(interpAttrId, v2, n2, nopad);
+ }
+ break;
+ case Comp_op::Gteq:
+ if (i == 1) {
+ scanFilter.ge(interpAttrId, v2, n2, nopad);
+ } else {
+ scanFilter.le(interpAttrId, v2, n2, nopad);
+ }
+ break;
+ case Comp_op::Like:
+ scanFilter.like(interpAttrId, v2, n2, nopad);
+ break;
+ case Comp_op::Notlike:
+ scanFilter.notlike(interpAttrId, v2, n2, nopad);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (t1.type() == SqlType::Smallint || t1.type() == SqlType::Integer || t1.type() == SqlType::Bigint) {
+ ctx_assert(t1.unSigned());
+ bool s2 = ! t2.unSigned();
+ SqlBigint v2;
+ SqlUbigint uv2;
+ if (s2) {
+ v2 =
+ t2.type() == SqlType::Smallint ? f2.sqlSmallint() :
+ t2.type() == SqlType::Integer ? f2.sqlInteger() : f2.sqlBigint();
+ uv2 = v2;
+ } else {
+ uv2 =
+ t2.type() == SqlType::Smallint ? (SqlUsmallint)f2.sqlSmallint() :
+ t2.type() == SqlType::Integer ? (SqlUinteger)f2.sqlInteger() : (SqlUbigint)f2.sqlBigint();
+ v2 = uv2;
+ }
+ switch (code.m_op.m_opcode) {
+ case Comp_op::Eq:
+ if (s2 && v2 < 0)
+ scanFilter.isfalse();
+ else
+ scanFilter.eq(interpAttrId, uv2);
+ break;
+ case Comp_op::Noteq:
+ if (s2 && v2 < 0)
+ scanFilter.istrue();
+ else
+ scanFilter.ne(interpAttrId, uv2);
+ break;
+ case Comp_op::Lt:
+ if (i == 1) {
+ if (s2 && v2 < 0)
+ scanFilter.isfalse();
+ else
+ scanFilter.lt(interpAttrId, uv2);
+ } else {
+ if (s2 && v2 < 0)
+ scanFilter.istrue();
+ else
+ scanFilter.gt(interpAttrId, uv2);
+ }
+ break;
+ case Comp_op::Lteq:
+ if (i == 1) {
+ if (s2 && v2 < 0)
+ scanFilter.isfalse();
+ else
+ scanFilter.le(interpAttrId, uv2);
+ } else {
+ if (s2 && v2 < 0)
+ scanFilter.istrue();
+ else
+ scanFilter.ge(interpAttrId, uv2);
+ }
+ break;
+ case Comp_op::Gt:
+ if (i == 1) {
+ if (s2 && v2 < 0)
+ scanFilter.istrue();
+ else
+ scanFilter.gt(interpAttrId, uv2);
+ } else {
+ if (s2 && v2 < 0)
+ scanFilter.isfalse();
+ else
+ scanFilter.lt(interpAttrId, uv2);
+ }
+ break;
+ case Comp_op::Gteq:
+ if (i == 1) {
+ if (s2 && v2 < 0)
+ scanFilter.istrue();
+ else
+ scanFilter.ge(interpAttrId, uv2);
+ } else {
+ if (s2 && v2 < 0)
+ scanFilter.isfalse();
+ else
+ scanFilter.le(interpAttrId, uv2);
+ }
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else {
+ ctx_assert(false);
+ }
+ // end null guard
+ scanFilter.end();
+ } else {
+ ctx_assert(false);
+ }
+}
+
+static bool
+do_sqlchar_comp(Comp_op::Opcode opcode, const SqlChar* s1, unsigned n1, const SqlChar* s2, unsigned n2, bool padded)
+{
+ int ret = NdbSqlUtil::char_compare(reinterpret_cast<const char*>(s1), n1, reinterpret_cast<const char *>(s2), n2, padded);
+ switch (opcode) {
+ case Comp_op::Eq:
+ return ret == 0;
+ case Comp_op::Noteq:
+ return ret != 0;
+ case Comp_op::Lt:
+ return ret < 0;
+ case Comp_op::Lteq:
+ return ret <= 0;
+ case Comp_op::Gt:
+ return ret > 0;
+ case Comp_op::Gteq:
+ return ret >= 0;
+ default:
+ break;
+ }
+ ctx_assert(false);
+ return false;
+}
+
+static bool
+do_sqlchar_like(const SqlChar* s1, unsigned n1, const SqlChar* s2, unsigned n2, bool padded)
+{
+ bool ret = NdbSqlUtil::char_like(reinterpret_cast<const char*>(s1), n1, reinterpret_cast<const char *>(s2), n2, padded);
+ return ret;
+}
+
+static bool
+do_datetime_comp(Comp_op::Opcode opcode, SqlDatetime v1, SqlDatetime v2)
+{
+ int k = v1.less(v2) ? -1 : v2.less(v1) ? 1 : 0;
+ switch (opcode) {
+ case Comp_op::Eq:
+ return k == 0;
+ case Comp_op::Noteq:
+ return k != 0;
+ case Comp_op::Lt:
+ return k < 0;
+ case Comp_op::Lteq:
+ return k <= 0;
+ case Comp_op::Gt:
+ return k > 0;
+ case Comp_op::Gteq:
+ return k >= 0;
+ default:
+ break;
+ }
+ ctx_assert(false);
+ return false;
+}
+
+void
+Exec_comp_op::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ const unsigned arity = code.m_op.arity();
+ const Comp_op::Opcode opcode = code.m_op.m_opcode;
+ Data& data = getData();
+ Pred_value v = Pred_value_unknown;
+ if (arity == 1) {
+ // evaluate sub-expression
+ ctx_assert(m_expr[1] != 0);
+ m_expr[1]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ // get type and value
+ const SqlType& t1 = m_expr[1]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = ctl.m_groupIndex == 0 ? m_expr[1]->getData().sqlField() : m_expr[1]->getData().groupField(ctl.m_groupIndex);
+ switch (code.m_op.m_opcode) {
+ case Comp_op::Isnull:
+ v = f1.sqlNull() ? Pred_value_true : Pred_value_false;
+ break;
+ case Comp_op::Isnotnull:
+ v = f1.sqlNull() ? Pred_value_false : Pred_value_true;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (arity == 2) {
+ // evaluate sub-expressions
+ ctx_assert(m_expr[1] != 0 && m_expr[2] != 0);
+ m_expr[1]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ m_expr[2]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ // get types and values
+ const SqlType& t1 = m_expr[1]->getCode().sqlSpec().sqlType();
+ const SqlType& t2 = m_expr[2]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = ctl.m_groupIndex == 0 ? m_expr[1]->getData().sqlField() : m_expr[1]->getData().groupField(ctl.m_groupIndex);
+ const SqlField& f2 = ctl.m_groupIndex == 0 ? m_expr[2]->getData().sqlField() : m_expr[2]->getData().groupField(ctl.m_groupIndex);
+ // handle null
+ if (f1.sqlNull() || f2.sqlNull()) {
+ v = Pred_value_unknown;
+ } else if (t1.type() == SqlType::Char) {
+ const SqlChar* v1 = f1.sqlChar();
+ unsigned n1 = t1.length();
+ if (t2.type() == SqlType::Char) {
+ unsigned n2 = t2.length();
+ const SqlChar* v2 = f2.sqlChar();
+ bool b;
+ switch (opcode) {
+ case Comp_op::Like:
+ b = do_sqlchar_like(v1, n1, v2, n2, true);
+ break;
+ case Comp_op::Notlike:
+ b = ! do_sqlchar_like(v1, n1, v2, n2, true);
+ break;
+ default:
+ b = do_sqlchar_comp(opcode, v1, n1, v2, n2, true);
+ break;
+ }
+ v = b ? Pred_value_true : Pred_value_false;
+ } else if (t2.type() == SqlType::Varchar) {
+ unsigned n2 = 0;
+ const SqlChar* v2 = f2.sqlVarchar(&n2);
+ bool b;
+ switch (opcode) {
+ case Comp_op::Like:
+ b = do_sqlchar_like(v1, n1, v2, n2, true);
+ break;
+ case Comp_op::Notlike:
+ b = ! do_sqlchar_like(v1, n1, v2, n2, true);
+ break;
+ default:
+ b = do_sqlchar_comp(opcode, v1, n1, v2, n2, false);
+ break;
+ }
+ v = b ? Pred_value_true : Pred_value_false;
+ } else {
+ ctx_assert(false);
+ }
+ } else if (t1.type() == SqlType::Varchar) {
+ unsigned n1 = 0;
+ const SqlChar* v1 = f1.sqlVarchar(&n1);
+ if (t2.type() == SqlType::Char) {
+ unsigned n2 = t2.length();
+ const SqlChar* v2 = f2.sqlChar();
+ bool b;
+ switch (opcode) {
+ case Comp_op::Like:
+ b = do_sqlchar_like(v1, n1, v2, n2, false);
+ break;
+ case Comp_op::Notlike:
+ b = ! do_sqlchar_like(v1, n1, v2, n2, false);
+ break;
+ default:
+ b = do_sqlchar_comp(opcode, v1, n1, v2, n2, false);
+ break;
+ }
+ v = b ? Pred_value_true : Pred_value_false;
+ } else if (t2.type() == SqlType::Varchar) {
+ unsigned n2 = 0;
+ const SqlChar* v2 = f2.sqlVarchar(&n2);
+ bool b;
+ switch (opcode) {
+ case Comp_op::Like:
+ b = do_sqlchar_like(v1, n1, v2, n2, false);
+ break;
+ case Comp_op::Notlike:
+ b = ! do_sqlchar_like(v1, n1, v2, n2, false);
+ break;
+ default:
+ b = do_sqlchar_comp(opcode, v1, n1, v2, n2, false);
+ break;
+ }
+ v = b ? Pred_value_true : Pred_value_false;
+ } else {
+ ctx_assert(false);
+ }
+ } else if (t1.type() == SqlType::Smallint || t1.type() == SqlType::Integer || t1.type() == SqlType::Bigint) {
+ // convert to bigint
+ bool s1 = ! t1.unSigned();
+ bool s2 = ! t2.unSigned();
+ SqlBigint v1, v2;
+ SqlUbigint uv1, uv2;
+ if (s1) {
+ v1 =
+ t1.type() == SqlType::Smallint ? f1.sqlSmallint() :
+ t1.type() == SqlType::Integer ? f1.sqlInteger() : f1.sqlBigint();
+ uv1 = v1;
+ } else {
+ uv1 =
+ t1.type() == SqlType::Smallint ? (SqlUsmallint)f1.sqlSmallint() :
+ t1.type() == SqlType::Integer ? (SqlUinteger)f1.sqlInteger() : (SqlUbigint)f1.sqlBigint();
+ v1 = uv1;
+ }
+ if (s2) {
+ v2 =
+ t2.type() == SqlType::Smallint ? f2.sqlSmallint() :
+ t2.type() == SqlType::Integer ? f2.sqlInteger() : f2.sqlBigint();
+ uv2 = v2;
+ } else {
+ uv2 =
+ t2.type() == SqlType::Smallint ? (SqlUsmallint)f2.sqlSmallint() :
+ t2.type() == SqlType::Integer ? (SqlUinteger)f2.sqlInteger() : (SqlUbigint)f2.sqlBigint();
+ v2 = uv2;
+ }
+ bool b;
+ switch (opcode) {
+ case Comp_op::Eq:
+ b = s1 && s2 ? (v1 == v2) : s1 ? (v1 < 0 ? false : uv1 == uv2) : s2 ? (v2 < 0 ? false : uv1 == uv2) : (uv1 == uv2);
+ break;
+ case Comp_op::Noteq:
+ b = s1 && s2 ? (v1 == v2) : s1 ? (v1 < 0 ? true : uv1 != uv2) : s2 ? (v2 < 0 ? true : uv1 != uv2) : (uv1 != uv2);
+ break;
+ case Comp_op::Lt:
+ b = s1 && s2 ? (v1 < v2) : s1 ? (v1 < 0 ? true : uv1 < uv2) : s2 ? (v2 < 0 ? false : uv1 < uv2) : (uv1 < uv2);
+ break;
+ case Comp_op::Lteq:
+ b = s1 && s2 ? (v1 <= v2) : s1 ? (v1 < 0 ? true : uv1 <= uv2) : s2 ? (v2 < 0 ? false : uv1 <= uv2) : (uv1 <= uv2);
+ break;
+ case Comp_op::Gt:
+ b = s1 && s2 ? (v1 > v2) : s1 ? (v1 < 0 ? false : uv1 > uv2) : s2 ? (v2 < 0 ? true : uv1 > uv2) : (uv1 > uv2);
+ break;
+ case Comp_op::Gteq:
+ b = s1 && s2 ? (v1 >= v2) : s1 ? (v1 < 0 ? false : uv1 >= uv2) : s2 ? (v2 < 0 ? true : uv1 >= uv2) : (uv1 >= uv2);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ v = b ? Pred_value_true : Pred_value_false;
+ } else if (t1.type() == SqlType::Double) {
+ SqlDouble v1 = f1.sqlDouble();
+ SqlDouble v2 = f2.sqlDouble();
+ bool b;
+ switch (opcode) {
+ case Comp_op::Eq:
+ b = (v1 == v2);
+ break;
+ case Comp_op::Noteq:
+ b = (v1 != v2);
+ break;
+ case Comp_op::Lt:
+ b = (v1 < v2);
+ break;
+ case Comp_op::Lteq:
+ b = (v1 <= v2);
+ break;
+ case Comp_op::Gt:
+ b = (v1 > v2);
+ break;
+ case Comp_op::Gteq:
+ b = (v1 >= v2);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ v = b ? Pred_value_true : Pred_value_false;
+ } else if (t1.type() == SqlType::Datetime) {
+ SqlDatetime v1 = f1.sqlDatetime();
+ SqlDatetime v2 = f2.sqlDatetime();
+ bool b;
+ b = do_datetime_comp(opcode, v1, v2);
+ v = b ? Pred_value_true : Pred_value_false;
+ } else {
+ ctx_assert(false);
+ }
+ } else {
+ ctx_assert(false);
+ }
+ // set result
+ if (ctl.m_groupIndex == 0)
+ data.m_value = v;
+ else
+ data.groupValue(ctl.m_groupIndex, ctl.m_groupInit) = v;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_create_index.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_create_index.cpp
new file mode 100644
index 00000000000..3966c6d5db2
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_create_index.cpp
@@ -0,0 +1,46 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <codegen/Code_create_index.hpp>
+
+void
+Exec_create_index::execute(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return;
+ }
+ NdbDictionary::Index ndbIndex(code.m_indexName.c_str());
+ ndbIndex.setTable(code.m_tableName.c_str());
+ ndbIndex.setType((NdbDictionary::Index::Type)code.m_type);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ ndbIndex.addIndexColumn(code.m_attrList[i]);
+ }
+ // setting fragment type not supported in ndb api
+ ndbIndex.setLogging(code.m_logging);
+ dictSchema().deleteTable(ctx, code.m_tableName);
+ if (ndbDictionary->createIndex(ndbIndex) == -1) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "createIndex %s", ndbIndex.getName());
+ return;
+ }
+ ctx_log1(("index %s on %s created", ndbIndex.getName(), ndbIndex.getTable()));
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_create_table.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_create_table.cpp
new file mode 100644
index 00000000000..d6274119371
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_create_table.cpp
@@ -0,0 +1,83 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <codegen/Code_create_table.hpp>
+
+void
+Exec_create_table::execute(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return;
+ }
+ NdbDictionary::Table ndbTable(code.m_tableName.c_str());
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const Code::Attr& attr = code.m_attrList[i];
+ NdbDictionary::Column ndbColumn(attr.m_attrName.c_str());
+ if (i == code.m_tupleId)
+ ndbColumn.setTupleKey(true); // XXX setTupleId()
+ if (ctx.logLevel() >= 3) {
+ char buf[100];
+ attr.m_sqlType.print(buf, sizeof(buf));
+ ctx_log3(("attr %s type %s", ndbColumn.getName(), buf));
+ }
+ if (attr.m_tupleKey)
+ ndbColumn.setPrimaryKey(true);
+ attr.m_sqlType.getType(ctx, &ndbColumn);
+ if (! ctx.ok())
+ return;
+ if (attr.m_autoIncrement)
+ ndbColumn.setAutoIncrement(true);
+ char defaultValue[MAX_ATTR_DEFAULT_VALUE_SIZE];
+ defaultValue[0] = 0;
+ if (attr.m_defaultValue != 0) {
+ // XXX obviously should evalute it at insert time too
+ attr.m_defaultValue->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& f = attr.m_defaultValue->getData().sqlField();
+ const SqlType& t = f.sqlSpec().sqlType();
+ // XXX use SqlField cast method instead
+ SQLINTEGER ind = 0;
+ ExtType extType(ExtType::Char);
+ ExtSpec extSpec(extType);
+ ExtField extField(extSpec, (SQLPOINTER)defaultValue, sizeof(defaultValue), &ind);
+ f.copyout(ctx, extField);
+ if (! ctx.ok())
+ return;
+ if (ind == SQL_NULL_DATA) // do not store NULL default
+ defaultValue[0] = 0;
+ }
+ if (defaultValue[0] != 0)
+ ndbColumn.setDefaultValue(defaultValue);
+ ndbTable.addColumn(ndbColumn);
+ }
+ if (code.m_fragmentType != NdbDictionary::Object::FragUndefined)
+ ndbTable.setFragmentType(code.m_fragmentType);
+ ndbTable.setLogging(code.m_logging);
+ dictSchema().deleteTable(ctx, code.m_tableName);
+ if (ndbDictionary->createTable(ndbTable) == -1) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "createTable %s", ndbTable.getName());
+ return;
+ }
+ ctx_log1(("table %s created", ndbTable.getName()));
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_index.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_index.cpp
new file mode 100644
index 00000000000..10814654a58
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_index.cpp
@@ -0,0 +1,82 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_delete_index.hpp>
+#include <codegen/Code_query.hpp>
+
+void
+Exec_delete_index::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // delete each row from the query
+ data.setCount(0);
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* op = tcon->getNdbIndexOperation(code.m_indexName, code.m_tableName);
+ if (op == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "getIndexNdbOperation");
+ return;
+ }
+ if (op->deleteTuple() == -1) {
+ ctx.pushStatus(ndb, tcon, op, "deleteTuple");
+ return;
+ }
+ bool done = false;
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ done = true; // match is not possible
+ break;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->equal(keyId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "equal attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ if (done)
+ continue;
+ if (tcon->execute(NoCommit) == -1) {
+ // XXX when did 626 move to connection level
+ if (tcon->getNdbError().code != 626 && op->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, tcon, op, "execute without commit");
+ return;
+ }
+ } else {
+ data.addCount();
+ }
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_lookup.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_lookup.cpp
new file mode 100644
index 00000000000..d0795286122
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_lookup.cpp
@@ -0,0 +1,82 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_delete_lookup.hpp>
+#include <codegen/Code_query.hpp>
+
+void
+Exec_delete_lookup::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // delete each row from the query
+ data.setCount(0);
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* op = tcon->getNdbOperation(code.m_tableName);
+ if (op == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "getNdbOperation");
+ return;
+ }
+ if (op->deleteTuple() == -1) {
+ ctx.pushStatus(ndb, tcon, op, "deleteTuple");
+ return;
+ }
+ bool done = false;
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ done = true; // match is not possible
+ break;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->equal(keyId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "equal attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ if (done)
+ continue;
+ if (tcon->execute(NoCommit) == -1) {
+ // XXX when did 626 move to connection level
+ if (tcon->getNdbError().code != 626 && op->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, tcon, op, "execute without commit");
+ return;
+ }
+ } else {
+ data.addCount();
+ }
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_scan.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_scan.cpp
new file mode 100644
index 00000000000..a0b3b8314b8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_delete_scan.cpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_delete_scan.hpp>
+#include <codegen/Code_query.hpp>
+
+void
+Exec_delete_scan::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_assert(ctl.m_scanOp != 0);
+ // delete each row from the scan
+ data.setCount(0);
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* toOp = ctl.m_scanOp->takeOverForDelete(tcon);
+ if (toOp == 0) {
+ ctx.pushStatus(ndb, tcon, ctl.m_scanOp, "takeOverScanOp");
+ return;
+ }
+ if (tcon->execute(NoCommit) == -1) {
+ if (toOp->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, tcon, toOp, "execute without commit");
+ return;
+ }
+ } else {
+ data.addCount();
+ }
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_drop_index.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_drop_index.cpp
new file mode 100644
index 00000000000..6bf451f6911
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_drop_index.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <codegen/Code_drop_index.hpp>
+
+void
+Exec_drop_index::execute(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return;
+ }
+ dictSchema().deleteTableByIndex(ctx, code.m_indexName);
+ if (ndbDictionary->dropIndex(code.m_indexName.c_str(), code.m_tableName.c_str()) == -1) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "dropIndex %s on %s", code.m_indexName.c_str(), code.m_tableName.c_str());
+ return;
+ }
+ ctx_log1(("index %s on %s dropped", code.m_indexName.c_str(), code.m_tableName.c_str()));
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_drop_table.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_drop_table.cpp
new file mode 100644
index 00000000000..40d1d42fc61
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_drop_table.cpp
@@ -0,0 +1,38 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <dictionary/DictSchema.hpp>
+#include <codegen/Code_drop_table.hpp>
+
+void
+Exec_drop_table::execute(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return;
+ }
+ dictSchema().deleteTable(ctx, code.m_tableName);
+ if (ndbDictionary->dropTable(code.m_tableName.c_str()) == -1) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "dropTable %s", code.m_tableName.c_str());
+ return;
+ }
+ ctx_log1(("table %s dropped", code.m_tableName.c_str()));
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_conv.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_conv.cpp
new file mode 100644
index 00000000000..636bfda7d59
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_conv.cpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <codegen/Code_expr_conv.hpp>
+
+void
+Exec_expr_conv::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ const SqlType& t1 = code.sqlSpec().sqlType();
+ SqlField& f1 = ctl.m_groupIndex == 0 ? data.m_sqlField : data.groupField(code.sqlSpec().sqlType() , ctl.m_groupIndex, ctl.m_groupInit);
+ // evaluate the subexpression
+ ctx_assert(m_expr != 0);
+ m_expr->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ const SqlType& t2 = m_expr->getCode().sqlSpec().sqlType();
+ const SqlField& f2 = ctl.m_groupIndex == 0 ? m_expr->getData().sqlField() : m_expr->getData().groupField(ctl.m_groupIndex);
+ // conversion to null type
+ if (t1.type() == SqlType::Null) {
+ f1.sqlNull(true);
+ return;
+ }
+ // conversion of null data
+ if (f2.sqlNull()) {
+ f1.sqlNull(true);
+ return;
+ }
+ // try to convert
+ if (! f2.cast(ctx, f1)) {
+ char b1[40], b2[40], b3[40];
+ t1.print(b1, sizeof(b1));
+ t2.print(b2, sizeof(b2));
+ f2.print(b3, sizeof(b3));
+ ctx.pushStatus(Sqlstate::_22003, Error::Gen, "cannot convert %s [%s] to %s", b2, b3, b1);
+ return;
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_func.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_func.cpp
new file mode 100644
index 00000000000..093d15c6e2b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_func.cpp
@@ -0,0 +1,284 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <time.h>
+#include <NdbTick.h>
+#include <codegen/Code_expr_func.hpp>
+
+void
+Exec_expr_func::init(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ const SqlType& t = code.sqlSpec().sqlType();
+ SqlField& f = ctl.m_groupIndex == 0 ? data.m_sqlField : data.groupField(code.sqlSpec().sqlType(), ctl.m_groupIndex, ctl.m_groupInit);
+ if (ctl.m_groupIndex >= data.m_groupAcc.size())
+ data.m_groupAcc.resize(1 + ctl.m_groupIndex);
+ Data::Acc& acc = data.m_groupAcc[ctl.m_groupIndex];
+ acc.m_count = 0;
+ Expr_func::Code fc = code.m_func.m_code;
+ if (fc == Expr_func::Substr || fc == Expr_func::Left || fc == Expr_func::Right) {
+ } else if (fc == Expr_func::Count) {
+ f.sqlBigint(0);
+ } else if (fc == Expr_func::Min || fc == Expr_func::Max) {
+ f.sqlNull(true);
+ } else if (fc == Expr_func::Sum) {
+ f.sqlNull(true);
+ switch (t.type()) {
+ case SqlType::Bigint:
+ acc.m_bigint = 0;
+ break;
+ case SqlType::Double:
+ acc.m_double = 0;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (fc == Expr_func::Avg) {
+ f.sqlNull(true);
+ switch (t.type()) {
+ case SqlType::Double:
+ acc.m_double = 0.0;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (fc == Expr_func::Rownum) {
+ // uses only m_count
+ } else if (fc == Expr_func::Sysdate) {
+ // set time once
+ NDB_TICKS secs = 0;
+ Uint32 micros = 0;
+ NdbTick_CurrentMicrosecond(&secs, &micros);
+ time_t clock = secs;
+ struct tm* t = gmtime(&clock);
+ SqlDatetime& d = acc.m_sysdate;
+ d.cc((1900 + t->tm_year) / 100);
+ d.yy((1900 + t->tm_year) % 100);
+ d.mm(1 + t->tm_mon);
+ d.dd(t->tm_mday);
+ d.HH(t->tm_hour);
+ d.MM(t->tm_min);
+ d.SS(t->tm_sec);
+ d.ff(1000 * micros);
+ } else {
+ ctx_assert(false);
+ }
+}
+
+void
+Exec_expr_func::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ const SqlType& t = code.sqlSpec().sqlType();
+ if (ctl.m_groupInit)
+ init(ctx, ctl);
+ SqlField& f = ctl.m_groupIndex == 0 ? data.m_sqlField : data.groupField(code.sqlSpec().sqlType(), ctl.m_groupIndex, false);
+ Data::Acc& acc = data.m_groupAcc[ctl.m_groupIndex];
+ Expr_func::Code fc = code.m_func.m_code;
+ const unsigned narg = code.m_narg;
+ Exec_expr** args = code.m_args;
+ ctx_assert(args != 0);
+ // evaluate arguments
+ for (unsigned i = 1; i <= narg; i++) {
+ ctx_assert(args[i] != 0);
+ unsigned save = ctl.m_groupIndex;
+ if (code.m_func.m_aggr)
+ ctl.m_groupIndex = 0;
+ args[i]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctl.m_groupIndex = save;
+ }
+ if (fc == Expr_func::Substr || fc == Expr_func::Left || fc == Expr_func::Right) {
+ ctx_assert((narg == (unsigned)2) || (narg == (unsigned)(fc == Expr_func::Substr ? 3 : 2)));
+ const SqlType& t1 = args[1]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = args[1]->getData().sqlField();
+ int pos, len;
+ for (unsigned i = 2; i <= narg; i++) {
+ int& num = (fc == Expr_func::Substr ? (i == 2 ? pos : len) : len);
+ const SqlType& t2 = args[i]->getCode().sqlSpec().sqlType();
+ const SqlField& f2 = args[i]->getData().sqlField();
+ switch (t2.type()) {
+ case SqlType::Smallint:
+ num = static_cast<int>(f2.sqlSmallint());
+ break;
+ case SqlType::Integer:
+ num = static_cast<int>(f2.sqlInteger());
+ break;
+ case SqlType::Bigint:
+ num = static_cast<int>(f2.sqlBigint());
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ }
+ int length = 0;
+ const SqlChar* data = 0;
+ switch (t1.type()) {
+ case SqlType::Char:
+ length = t1.length();
+ data = f1.sqlChar();
+ break;
+ case SqlType::Varchar:
+ unsigned ulength;
+ data = f1.sqlVarchar(&ulength);
+ length = ulength;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ if (fc == Expr_func::Left)
+ pos = 1;
+ else if (fc == Expr_func::Right)
+ pos = len > length ? 1 : length - len + 1;
+ else if (pos < 0)
+ pos += length + 1;
+ if (pos <= 0 || pos > length || len <= 0) {
+ f.sqlNull(true); // oracle-ish
+ return;
+ }
+ if (len > length - pos + 1)
+ len = length - pos + 1;
+ switch (t1.type()) {
+ case SqlType::Char:
+ f.sqlChar(data + (pos - 1), len);
+ break;
+ case SqlType::Varchar:
+ f.sqlVarchar(data + (pos - 1), len);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (fc == Expr_func::Count) {
+ ctx_assert(narg == 0 || narg == 1);
+ if (ctl.m_postEval)
+ return;
+ if (narg == 1) {
+ const SqlField& f1 = args[1]->getData().sqlField();
+ if (f1.sqlNull())
+ return;
+ }
+ f.sqlBigint(++acc.m_count);
+ } else if (fc == Expr_func::Min) {
+ ctx_assert(narg == 1);
+ if (ctl.m_postEval)
+ return;
+ const SqlField& f1 = args[1]->getData().sqlField();
+ if (f1.sqlNull())
+ return;
+ if (f.sqlNull() || f1.less(f))
+ f1.copy(ctx, f);
+ } else if (fc == Expr_func::Max) {
+ ctx_assert(narg == 1);
+ if (ctl.m_postEval)
+ return;
+ const SqlField& f1 = args[1]->getData().sqlField();
+ if (f1.sqlNull())
+ return;
+ if (f.sqlNull() || f.less(f1))
+ f1.copy(ctx, f);
+ } else if (fc == Expr_func::Sum) {
+ ctx_assert(narg == 1);
+ if (ctl.m_postEval)
+ return;
+ const SqlType& t1 = args[1]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = args[1]->getData().sqlField();
+ if (f1.sqlNull())
+ return;
+ switch (t.type()) {
+ case SqlType::Bigint:
+ switch (t1.type()) {
+ case SqlType::Integer:
+ acc.m_bigint += f1.sqlInteger();
+ break;
+ case SqlType::Bigint:
+ acc.m_bigint += f1.sqlBigint();
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlBigint(acc.m_bigint);
+ break;
+ case SqlType::Double:
+ switch (t1.type()) {
+ case SqlType::Real:
+ acc.m_double += f1.sqlReal();
+ break;
+ case SqlType::Double:
+ acc.m_double += f1.sqlDouble();
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlDouble(acc.m_double);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (fc == Expr_func::Avg) {
+ ctx_assert(narg == 1);
+ if (ctl.m_postEval)
+ return;
+ const SqlType& t1 = args[1]->getCode().sqlSpec().sqlType();
+ const SqlField& f1 = args[1]->getData().sqlField();
+ if (f1.sqlNull())
+ return;
+ switch (t1.type()) {
+ case SqlType::Smallint:
+ acc.m_bigint += f1.sqlSmallint();
+ break;
+ case SqlType::Integer:
+ acc.m_bigint += f1.sqlInteger();
+ break;
+ case SqlType::Bigint:
+ acc.m_bigint += f1.sqlBigint();
+ break;
+ case SqlType::Real:
+ acc.m_double += f1.sqlReal();
+ break;
+ case SqlType::Double:
+ acc.m_double += f1.sqlDouble();
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlDouble(acc.m_double / (SqlDouble)++acc.m_count);
+ } else if (fc == Expr_func::Rownum) {
+ ctx_assert(narg == 0);
+ if (! ctl.m_postEval)
+ f.sqlBigint(1 + acc.m_count);
+ else
+ acc.m_count++;
+ } else if (fc == Expr_func::Sysdate) {
+ ctx_assert(narg == 0);
+ if (ctl.m_postEval)
+ return;
+ f.sqlDatetime(acc.m_sysdate);
+ } else {
+ ctx_assert(false);
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_op.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_op.cpp
new file mode 100644
index 00000000000..fc8b6df9f5b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_expr_op.cpp
@@ -0,0 +1,147 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <codegen/Code_expr_op.hpp>
+
+void
+Exec_expr_op::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ const SqlType& t = code.sqlSpec().sqlType();
+ SqlField& f = ctl.m_groupIndex == 0 ? data.m_sqlField : data.groupField(code.sqlSpec().sqlType(), ctl.m_groupIndex, ctl.m_groupInit);
+ if (code.m_op.arity() == 1) {
+ // evaluate sub-expression
+ ctx_assert(m_expr[1] != 0);
+ m_expr[1]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ const SqlField& f1 = ctl.m_groupIndex == 0 ? m_expr[1]->getData().sqlField() : m_expr[1]->getData().groupField(ctl.m_groupIndex);
+ // handle null
+ if (f1.sqlNull()) {
+ f.sqlNull(true);
+ return;
+ }
+ if (t.type() == SqlType::Bigint) {
+ SqlBigint v = 0;
+ SqlBigint v1 = f1.sqlBigint();
+ switch (code.m_op.m_opcode) {
+ case Expr_op::Plus:
+ v = v1;
+ break;
+ case Expr_op::Minus:
+ v = - v1;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlBigint(v);
+ } else if (t.type() == SqlType::Double) {
+ SqlDouble v = 0;
+ SqlDouble v1 = f1.sqlDouble();
+ switch (code.m_op.m_opcode) {
+ case Expr_op::Plus:
+ v = v1;
+ break;
+ case Expr_op::Minus:
+ v = - v1;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlDouble(v);
+ } else {
+ ctx_assert(false);
+ }
+ } else if (code.m_op.arity() == 2) {
+ // evaluate sub-expressions
+ ctx_assert(m_expr[1] != 0 && m_expr[2] != 0);
+ m_expr[1]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ m_expr[2]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ const SqlField& f1 = ctl.m_groupIndex == 0 ? m_expr[1]->getData().sqlField() : m_expr[1]->getData().groupField(ctl.m_groupIndex);
+ const SqlField& f2 = ctl.m_groupIndex == 0 ? m_expr[2]->getData().sqlField() : m_expr[2]->getData().groupField(ctl.m_groupIndex);
+ // handle null
+ if (f1.sqlNull() || f2.sqlNull()) {
+ f.sqlNull(true);
+ return;
+ }
+ if (t.type() == SqlType::Bigint) {
+ SqlBigint v = 0;
+ SqlBigint v1 = f1.sqlBigint();
+ SqlBigint v2 = f2.sqlBigint();
+ switch (code.m_op.m_opcode) {
+ case Expr_op::Add:
+ v = v1 + v2;
+ break;
+ case Expr_op::Subtract:
+ v = v1 - v2;
+ break;
+ case Expr_op::Multiply:
+ v = v1 * v2;
+ break;
+ case Expr_op::Divide:
+ if (v2 == 0) {
+ ctx.pushStatus(Sqlstate::_22012, Error::Gen, "integer division by zero");
+ return;
+ }
+ v = v1 / v2;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlBigint(v);
+ } else if (t.type() == SqlType::Double) {
+ SqlDouble v = 0;
+ SqlDouble v1 = f1.sqlDouble();
+ SqlDouble v2 = f2.sqlDouble();
+ switch (code.m_op.m_opcode) {
+ case Expr_op::Add:
+ v = v1 + v2;
+ break;
+ case Expr_op::Subtract:
+ v = v1 - v2;
+ break;
+ case Expr_op::Multiply:
+ v = v1 * v2;
+ break;
+ case Expr_op::Divide:
+ v = v1 / v2;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ f.sqlDouble(v); // XXX isnan()
+ } else {
+ ctx_assert(false);
+ }
+ } else {
+ ctx_assert(false);
+ }
+ // result is not null
+ f.sqlNull(false);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_insert.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_insert.cpp
new file mode 100644
index 00000000000..c2612c6aaab
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_insert.cpp
@@ -0,0 +1,144 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_insert.hpp>
+#include <codegen/Code_query.hpp>
+
+#ifdef NDB_WIN32
+#define FMT_I64 "%I64d"
+#else
+#define FMT_I64 "%lld"
+#endif
+
+void
+Exec_insert::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // insert each row from query
+ data.setCount(0);
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* op = tcon->getNdbOperation(code.m_tableName);
+ if (op == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "getNdbOperation");
+ return;
+ }
+ if (code.m_insertOp == Insert_op_insert) {
+ if (op->insertTuple() == -1) {
+ ctx.pushStatus(ndb, tcon, op, "insertTuple");
+ return;
+ }
+ } else if (code.m_insertOp == Insert_op_write) {
+ if (op->writeTuple() == -1) {
+ ctx.pushStatus(ndb, tcon, op, "writeTuple");
+ return;
+ }
+ } else {
+ ctx_assert(false);
+ }
+ const SqlRow& sqlRow = m_query->getData().sqlRow();
+ if (code.m_tupleId != 0) {
+ Uint64 tid = op->setTupleId();
+ if (tid == 0) {
+ ctx.pushStatus(ndb, tcon, op, "setTupleId attrId=%u", (unsigned)code.m_tupleId);
+ return;
+ }
+ ctx_log3(("generated TupleId " FMT_I64, tid));
+ } else if (code.m_autoIncrement != 0) {
+ // XXX use cache size 1 for birdies and fishies
+ Uint64 tupleId = ndb->getAutoIncrementValue(code.m_tableName, 1);
+ if (tupleId == 0) {
+ ctx.pushStatus(ndb, "getTupleIdFromNdb");
+ return;
+ }
+ NdbAttrId attrId = code.m_autoIncrement - 1;
+ SqlSmallint sqlSmallint = 0;
+ SqlInteger sqlInteger = 0;
+ SqlBigint sqlBigint = 0;
+ const char* value = 0;
+ if (code.m_idType.type() == SqlType::Smallint) {
+ sqlSmallint = tupleId;
+ value = (const char*)&sqlSmallint;
+ } else if (code.m_idType.type() == SqlType::Integer) {
+ sqlInteger = tupleId;
+ value = (const char*)&sqlInteger;
+ } else if (code.m_idType.type() == SqlType::Bigint) {
+ sqlBigint = tupleId;
+ value = (const char*)&sqlBigint;
+ } else {
+ ctx_assert(false);
+ }
+ if (op->equal(attrId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "equal attrId=%u", (unsigned)attrId);
+ return;
+ }
+ } else {
+ // normal key attributes
+ for (unsigned i = 1; i <= sqlRow.count(); i++) {
+ if (! code.m_isKey[i])
+ continue;
+ NdbAttrId attrId = code.m_attrId[i];
+ const SqlField& f = sqlRow.getEntry(i);
+ const void* addr = f.sqlNull() ? 0 : f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->equal(attrId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "equal attrId=%u", (unsigned)attrId);
+ return;
+ }
+ }
+ }
+ // non-key attributes
+ for (unsigned i = 1; i <= sqlRow.count(); i++) {
+ if (code.m_isKey[i])
+ continue;
+ NdbAttrId attrId = code.m_attrId[i];
+ const SqlField& f = sqlRow.getEntry(i);
+ const void* addr = f.sqlNull() ? 0 : f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->setValue(attrId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "setValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ }
+ // default non-key values
+ for (unsigned i = 1; i <= code.m_defaultCount; i++) {
+ NdbAttrId attrId = code.m_defaultId[i];
+ const SqlField& f = code.m_defaultValue->getEntry(i);
+ const void* addr = f.sqlNull() ? 0 : f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->setValue(attrId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "setValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ }
+ if (tcon->execute(NoCommit) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "execute without commit");
+ return;
+ }
+ data.addCount();
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_pred_op.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_pred_op.cpp
new file mode 100644
index 00000000000..7caa4656473
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_pred_op.cpp
@@ -0,0 +1,197 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <codegen/Code_pred_op.hpp>
+#include <NdbScanFilter.hpp>
+
+struct TableUnary {
+ Pred_value value1;
+ Pred_value result;
+};
+
+struct TableBinary {
+ Pred_value value1;
+ Pred_value value2;
+ Pred_value result;
+};
+
+static TableUnary
+tableNot[] = {
+ { Pred_value_unknown, Pred_value_unknown },
+ { Pred_value_false, Pred_value_true },
+ { Pred_value_true, Pred_value_false },
+};
+
+static TableBinary
+tableAnd[] = {
+ { Pred_value_unknown, Pred_value_unknown, Pred_value_unknown },
+ { Pred_value_unknown, Pred_value_false, Pred_value_false },
+ { Pred_value_unknown, Pred_value_true, Pred_value_unknown },
+ { Pred_value_false, Pred_value_unknown, Pred_value_false },
+ { Pred_value_false, Pred_value_false, Pred_value_false },
+ { Pred_value_false, Pred_value_true, Pred_value_false },
+ { Pred_value_true, Pred_value_unknown, Pred_value_unknown },
+ { Pred_value_true, Pred_value_false, Pred_value_false },
+ { Pred_value_true, Pred_value_true, Pred_value_true }
+};
+
+static TableBinary
+tableOr[] = {
+ { Pred_value_unknown, Pred_value_unknown, Pred_value_unknown },
+ { Pred_value_unknown, Pred_value_false, Pred_value_unknown },
+ { Pred_value_unknown, Pred_value_true, Pred_value_true },
+ { Pred_value_false, Pred_value_unknown, Pred_value_unknown },
+ { Pred_value_false, Pred_value_false, Pred_value_false },
+ { Pred_value_false, Pred_value_true, Pred_value_true },
+ { Pred_value_true, Pred_value_unknown, Pred_value_true },
+ { Pred_value_true, Pred_value_false, Pred_value_true },
+ { Pred_value_true, Pred_value_true, Pred_value_true }
+};
+
+void
+Exec_pred_op::execInterp(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ ctx_assert(ctl.m_scanFilter != 0);
+ NdbScanFilter& scanFilter = *ctl.m_scanFilter;
+ if (code.m_op.arity() == 1) {
+ ctx_assert(m_pred[1] != 0);
+ switch (code.m_op.m_opcode) {
+ case Pred_op::Not:
+ scanFilter.begin(NdbScanFilter::NAND);
+ m_pred[1]-> execInterp(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ scanFilter.end();
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else if (code.m_op.arity() == 2) {
+ ctx_assert(m_pred[1] != 0 && m_pred[2] != 0);
+ switch (code.m_op.m_opcode) {
+ case Pred_op::And:
+ scanFilter.begin(NdbScanFilter::AND);
+ m_pred[1]-> execInterp(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ m_pred[2]-> execInterp(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ scanFilter.end();
+ break;
+ case Pred_op::Or:
+ scanFilter.begin(NdbScanFilter::OR);
+ m_pred[1]-> execInterp(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ m_pred[2]-> execInterp(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ scanFilter.end();
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ } else {
+ ctx_assert(false);
+ }
+}
+
+void
+Exec_pred_op::evaluate(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Pred_value v = Pred_value_unknown;
+ if (code.m_op.arity() == 1) {
+ // evaluate sub-expression
+ ctx_assert(m_pred[1] != 0);
+ m_pred[1]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ Pred_value v1 = ctl.m_groupIndex == 0 ? m_pred[1]->getData().getValue() : m_pred[1]->getData().groupValue(ctl.m_groupIndex);
+ // look up result
+ TableUnary* table = 0;
+ unsigned size = 0;
+ switch (code.m_op.m_opcode) {
+ case Pred_op::Not:
+ table = tableNot;
+ size = sizeof(tableNot) / sizeof(tableNot[0]);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ unsigned i;
+ for (i = 0; i < size; i++) {
+ if (table[i].value1 == v1) {
+ v = table[i].result;
+ break;
+ }
+ }
+ ctx_assert(i < size);
+ } else if (code.m_op.arity() == 2) {
+ // evaluate sub-expressions
+ ctx_assert(m_pred[1] != 0 && m_pred[2] != 0);
+ m_pred[1]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ m_pred[2]->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ if (ctl.m_postEval)
+ return;
+ Pred_value v1 = ctl.m_groupIndex == 0 ? m_pred[1]->getData().getValue() : m_pred[1]->getData().groupValue(ctl.m_groupIndex);
+ Pred_value v2 = ctl.m_groupIndex == 0 ? m_pred[2]->getData().getValue() : m_pred[2]->getData().groupValue(ctl.m_groupIndex);
+ // look up result
+ TableBinary* table = 0;
+ unsigned size = 0;
+ switch (code.m_op.m_opcode) {
+ case Pred_op::And:
+ table = tableAnd;
+ size = sizeof(tableAnd) / sizeof(tableAnd[0]);
+ break;
+ case Pred_op::Or:
+ table = tableOr;
+ size = sizeof(tableOr) / sizeof(tableOr[0]);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ unsigned i;
+ for (i = 0; i < size; i++) {
+ if (table[i].value1 == v1 && table[i].value2 == v2) {
+ v = table[i].result;
+ break;
+ }
+ }
+ ctx_assert(i < size);
+ } else {
+ ctx_assert(false);
+ }
+ // set result
+ if (ctl.m_groupIndex == 0)
+ data.m_value = v;
+ else
+ data.groupValue(ctl.m_groupIndex, ctl.m_groupInit) = v;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_query_index.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_index.cpp
new file mode 100644
index 00000000000..919743beac2
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_index.cpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_query_index.hpp>
+#include <codegen/Code_table.hpp>
+
+void
+Exec_query_index::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ data.m_done = false;
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ if (data.m_con != 0) {
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("lookup closed at re-execute"));
+ }
+ const bool unco = connArea().uncommitted();
+ if (! unco) {
+ // use new transaction to not run out of operations
+ data.m_con = ndb->startTransaction();
+ if (data.m_con == 0) {
+ ctx.pushStatus(ndb, "startTransaction");
+ return;
+ }
+ } else {
+ ctx_log3(("lookup using main transaction"));
+ }
+ data.m_op = (unco ? tcon : data.m_con)->getNdbIndexOperation(code.m_indexName, code.m_tableName);
+ if (data.m_op == 0) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), 0, "getNdbIndexOperation");
+ return;
+ }
+ if (data.m_op->readTuple() == -1) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "readTuple");
+ return;
+ }
+ // key attributes
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ data.m_done = true; // match is not possible
+ return;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (data.m_op->equal(keyId, value) == -1) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "equal attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ // queried attributes
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const NdbAttrId attrId = code.m_attrId[i];
+ SqlField& f = sqlRow.getEntry(i);
+ char* addr = static_cast<char*>(f.addr());
+ NdbRecAttr* recAttr = data.m_op->getValue(attrId, addr);
+ if (recAttr == 0) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "getValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ data.m_recAttr[i] = recAttr;
+ }
+ if (code.m_attrCount == 0) { // NDB requires one
+ (void)data.m_op->getValue((NdbAttrId)0);
+ }
+ data.setCount(0);
+ if ((unco ? tcon : data.m_con)->execute(unco ? NoCommit : Commit) == -1) {
+ // XXX when did 626 move to connection level
+ if ((unco ? tcon : data.m_con)->getNdbError().code != 626 && data.m_op->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "execute xxx");
+ return;
+ }
+ data.m_done = true;
+ } else {
+ stmtArea().incTuplesFetched();
+ data.m_done = false;
+ }
+ if (! unco) {
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log3(("index lookup closed at execute"));
+ }
+}
+
+bool
+Exec_query_index::fetchImpl(Ctx &ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ // returns at most one row
+ if (data.m_done)
+ return false;
+ // set null bits
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ NdbRecAttr* recAttr = data.m_recAttr[i];
+ int isNULL = recAttr->isNULL();
+ SqlField& f = sqlRow.getEntry(i);
+ ctx_assert(isNULL == 0 || isNULL == 1);
+ f.sqlNull(isNULL == 1);
+ }
+ data.m_done = true;
+ return true;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_query_lookup.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_lookup.cpp
new file mode 100644
index 00000000000..599e1a36461
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_lookup.cpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_query_lookup.hpp>
+#include <codegen/Code_table.hpp>
+
+void
+Exec_query_lookup::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ data.m_done = false;
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ if (data.m_con != 0) {
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("lookup closed at re-execute"));
+ }
+ const bool unco = connArea().uncommitted();
+ if (! unco) {
+ // use new transaction to not run out of operations
+ data.m_con = ndb->startTransaction();
+ if (data.m_con == 0) {
+ ctx.pushStatus(ndb, "startTransaction");
+ return;
+ }
+ } else {
+ ctx_log3(("lookup using main transaction"));
+ }
+ data.m_op = (unco ? tcon : data.m_con)->getNdbOperation(code.m_tableName);
+ if (data.m_op == 0) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), 0, "getNdbOperation");
+ return;
+ }
+ if (data.m_op->readTuple() == -1) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "readTuple");
+ return;
+ }
+ // key attributes
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ data.m_done = true; // match is not possible
+ return;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (data.m_op->equal(keyId, value) == -1) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "equal attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ // queried attributes
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const NdbAttrId attrId = code.m_attrId[i];
+ SqlField& f = sqlRow.getEntry(i);
+ char* addr = static_cast<char*>(f.addr());
+ NdbRecAttr* recAttr = data.m_op->getValue(attrId, addr);
+ if (recAttr == 0) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "getValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ data.m_recAttr[i] = recAttr;
+ }
+ if (code.m_attrCount == 0) { // NDB requires one
+ (void)data.m_op->getValue((NdbAttrId)0);
+ }
+ data.setCount(0);
+ if ((unco ? tcon : data.m_con)->execute(unco ? NoCommit : Commit) == -1) {
+ // XXX when did 626 move to connection level
+ if ((unco ? tcon : data.m_con)->getNdbError().code != 626 && data.m_op->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, (unco ? tcon : data.m_con), data.m_op, "execute xxx");
+ return;
+ }
+ data.m_done = true;
+ } else {
+ stmtArea().incTuplesFetched();
+ data.m_done = false;
+ }
+ if (! unco) {
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log3(("lookup closed at execute"));
+ }
+}
+
+bool
+Exec_query_lookup::fetchImpl(Ctx &ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ // returns at most one row
+ if (data.m_done)
+ return false;
+ // set null bits
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ NdbRecAttr* recAttr = data.m_recAttr[i];
+ int isNULL = recAttr->isNULL();
+ SqlField& f = sqlRow.getEntry(i);
+ ctx_assert(isNULL == 0 || isNULL == 1);
+ f.sqlNull(isNULL == 1);
+ }
+ data.m_done = true;
+ return true;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_query_range.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_range.cpp
new file mode 100644
index 00000000000..0bc878d760d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_range.cpp
@@ -0,0 +1,143 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_query_range.hpp>
+#include <codegen/Code_table.hpp>
+
+#define startBuddyTransaction(x) hupp(x)
+
+void
+Exec_query_range::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ data.m_done = false;
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ if (data.m_con != 0) {
+ data.m_con->stopScan();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("range scan closed at re-execute"));
+ }
+ data.m_con = ndb->startBuddyTransaction(tcon);
+ if (data.m_con == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "startBuddyTransaction");
+ return;
+ }
+ data.m_op = data.m_con->getNdbOperation(code.m_indexName, code.m_tableName);
+ if (data.m_op == 0) {
+ ctx.pushStatus(ndb, data.m_con, 0, "getNdbOperation");
+ return;
+ }
+ if (! code.m_exclusive) {
+ if (data.m_op->openScanReadCommitted(data.m_parallel) == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "openScanReadCommitted");
+ return;
+ }
+ } else {
+ if (data.m_op->openScanExclusive(data.m_parallel) == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "openScanExclusive");
+ return;
+ }
+ }
+ // set bounds
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ data.m_done = true; // match is not possible
+ return;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ const unsigned len = f.allocSize();
+ if (data.m_op->setBound(keyId, NdbOperation::BoundEQ, value, len) == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "setBound attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ // queried attributes
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const NdbAttrId attrId = code.m_attrId[i];
+ SqlField& f = sqlRow.getEntry(i);
+ char* addr = static_cast<char*>(f.addr());
+ NdbRecAttr* recAttr = data.m_op->getValue(attrId, addr);
+ if (recAttr == 0) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "getValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ data.m_recAttr[i] = recAttr;
+ }
+ if (code.m_attrCount == 0) { // NDB requires one
+ (void)data.m_op->getValue((NdbAttrId)0);
+ }
+ data.setCount(0);
+ if (data.m_con->executeScan() == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "executeScan");
+ return;
+ }
+ ctx_log2(("range scan %s [%08x] started", ! code.m_exclusive ? "read" : "exclusive", (unsigned)this));
+ ctl.m_scanOp = data.m_op;
+}
+
+bool
+Exec_query_range::fetchImpl(Ctx &ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ // if never started
+ if (data.m_done)
+ return false;
+ int ret = data.m_con->nextScanResult();
+ if (ret != 0) {
+ if (ret == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "nextScanResult");
+ }
+ data.m_con->stopScan();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("range scan [%08x] closed at last row", (unsigned)this));
+ return false;
+ }
+ // set null bits
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ NdbRecAttr* recAttr = data.m_recAttr[i];
+ int isNULL = recAttr->isNULL();
+ SqlField& f = sqlRow.getEntry(i);
+ ctx_assert(isNULL == 0 || isNULL == 1);
+ f.sqlNull(isNULL == 1);
+ }
+ stmtArea().incTuplesFetched();
+ return true;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_query_scan.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_scan.cpp
new file mode 100644
index 00000000000..213dfdd616d
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_scan.cpp
@@ -0,0 +1,130 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <NdbScanFilter.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_query_scan.hpp>
+#include <codegen/Code_table.hpp>
+
+#define startBuddyTransaction(x) hupp(x)
+
+void
+Exec_query_scan::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ if (data.m_con != 0) {
+ data.m_con->stopScan();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("scan closed at re-execute"));
+ }
+ data.m_con = ndb->startBuddyTransaction(tcon);
+ if (data.m_con == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "startBuddyTransaction");
+ return;
+ }
+ data.m_op = data.m_con->getNdbOperation(code.m_tableName);
+ if (data.m_op == 0) {
+ ctx.pushStatus(ndb, data.m_con, 0, "getNdbOperation");
+ return;
+ }
+ if (! code.m_exclusive) {
+ if (data.m_op->openScanReadCommitted(data.m_parallel) == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "openScanReadCommitted");
+ return;
+ }
+ } else {
+ if (data.m_op->openScanExclusive(data.m_parallel) == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "openScanExclusive");
+ return;
+ }
+ }
+ if (m_interp == 0) {
+ // XXX unnecessary
+ if (data.m_op->interpret_exit_ok() == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "interpret_exit_ok");
+ return;
+ }
+ } else {
+ NdbScanFilter scanFilter(data.m_op);
+ scanFilter.begin(NdbScanFilter::AND);
+ ctl.m_scanFilter = &scanFilter;
+ m_interp->execInterp(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ scanFilter.end();
+ }
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const NdbAttrId attrId = code.m_attrId[i];
+ SqlField& sqlField = sqlRow.getEntry(i);
+ char* addr = static_cast<char*>(sqlField.addr());
+ NdbRecAttr* recAttr = data.m_op->getValue(attrId, addr);
+ if (recAttr == 0) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "getValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ data.m_recAttr[i] = recAttr;
+ }
+ if (code.m_attrCount == 0) { // NDB requires one
+ (void)data.m_op->getValue((NdbAttrId)0);
+ }
+ if (data.m_con->executeScan() == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "executeScan");
+ return;
+ }
+ ctx_log2(("scan %s [%08x] started", ! code.m_exclusive ? "read" : "exclusive", (unsigned)this));
+ ctl.m_scanOp = data.m_op;
+}
+
+bool
+Exec_query_scan::fetchImpl(Ctx &ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ int ret = data.m_con->nextScanResult();
+ if (ret != 0) {
+ if (ret == -1) {
+ ctx.pushStatus(ndb, data.m_con, data.m_op, "nextScanResult");
+ }
+ data.m_con->stopScan();
+ ndb->closeTransaction(data.m_con);
+ data.m_con = 0;
+ data.m_op = 0;
+ ctx_log2(("scan [%08x] closed at last row", (unsigned)this));
+ return false;
+ }
+ // set null bits
+ const SqlRow& sqlRow = data.sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ NdbRecAttr* recAttr = data.m_recAttr[i];
+ int isNULL = recAttr->isNULL();
+ SqlField& sqlField = sqlRow.getEntry(i);
+ ctx_assert(isNULL == 0 || isNULL == 1);
+ sqlField.sqlNull(isNULL == 1);
+ }
+ stmtArea().incTuplesFetched();
+ return true;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_query_sys.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_sys.cpp
new file mode 100644
index 00000000000..acdc120e609
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_query_sys.cpp
@@ -0,0 +1,761 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_query_sys.hpp>
+#include <codegen/Code_table.hpp>
+
+#define NULL_CHAR ((char*)0)
+#define NULL_INT (-2147483647)
+
+struct Typeinfo {
+ const char* m_type_name; // 1
+ int m_data_type; // 2
+ int m_column_size; // 3
+ const char* m_literal_prefix; // 4
+ const char* m_literal_suffix; // 5
+ const char* m_create_params; // 6
+ int m_nullable; // 7
+ int m_case_sensitive; // 8
+ int m_searchable; // 9
+ int m_unsigned_attribute; // 10
+ int m_fixed_prec_scale; // 11
+ int m_auto_unique_value; // 12
+ const char* m_local_type_name; // 13
+ int m_minimum_scale; // 14
+ int m_maximum_scale; // 15
+ int m_sql_data_type; // 16
+ int m_sql_datetime_sub; // 17
+ int m_num_prec_radix; // 18
+ int m_interval_precision; // 19
+};
+
+static const Typeinfo
+typeinfoList[] = {
+ { "CHAR", // 1
+ SQL_CHAR, // 2
+ 8000, // 3
+ "'", // 4
+ "'", // 5
+ "length", // 6
+ SQL_NULLABLE, // 7
+ SQL_TRUE, // 8
+ SQL_SEARCHABLE, // 9
+ NULL_INT, // 10
+ SQL_FALSE, // 11
+ NULL_INT, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_CHAR, // 16
+ NULL_INT, // 17
+ NULL_INT, // 18
+ NULL_INT // 19
+ },
+ { "VARCHAR", // 1
+ SQL_VARCHAR, // 2
+ 8000, // 3
+ "'", // 4
+ "'", // 5
+ "length", // 6
+ SQL_NULLABLE, // 7
+ SQL_TRUE, // 8
+ SQL_SEARCHABLE, // 9
+ NULL_INT, // 10
+ SQL_FALSE, // 11
+ NULL_INT, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_VARCHAR, // 16
+ NULL_INT, // 17
+ NULL_INT, // 18
+ NULL_INT // 19
+ },
+ { "BINARY", // 1
+ SQL_BINARY, // 2
+ 8000, // 3
+ "'", // 4
+ "'", // 5
+ "length", // 6
+ SQL_NULLABLE, // 7
+ SQL_TRUE, // 8
+ SQL_SEARCHABLE, // 9
+ NULL_INT, // 10
+ SQL_FALSE, // 11
+ NULL_INT, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_BINARY, // 16
+ NULL_INT, // 17
+ NULL_INT, // 18
+ NULL_INT // 19
+ },
+ { "VARBINARY", // 1
+ SQL_VARBINARY, // 2
+ 8000, // 3
+ "'", // 4
+ "'", // 5
+ "length", // 6
+ SQL_NULLABLE, // 7
+ SQL_TRUE, // 8
+ SQL_SEARCHABLE, // 9
+ NULL_INT, // 10
+ SQL_FALSE, // 11
+ NULL_INT, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_VARBINARY, // 16
+ NULL_INT, // 17
+ NULL_INT, // 18
+ NULL_INT // 19
+ },
+ { "SMALLINT", // 1
+ SQL_SMALLINT, // 2
+ 4, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_FALSE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_SMALLINT, // 16
+ NULL_INT, // 17
+ 10, // 18
+ NULL_INT // 19
+ },
+ { "SMALLINT UNSIGNED", // 1
+ SQL_SMALLINT, // 2
+ 4, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_TRUE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_SMALLINT, // 16
+ NULL_INT, // 17
+ 10, // 18
+ NULL_INT // 19
+ },
+ { "INT", // 1
+ SQL_INTEGER, // 2
+ 9, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_FALSE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_INTEGER, // 16
+ NULL_INT, // 17
+ 10, // 18
+ NULL_INT // 19
+ },
+ { "INT UNSIGNED", // 1
+ SQL_INTEGER, // 2
+ 9, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_TRUE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_INTEGER, // 16
+ NULL_INT, // 17
+ 10, // 18
+ NULL_INT // 19
+ },
+ { "BIGINT", // 1
+ SQL_BIGINT, // 2
+ 19, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_FALSE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_BIGINT, // 16
+ NULL_INT, // 17
+ 10, // 18
+ NULL_INT // 19
+ },
+ { "BIGINT UNSIGNED", // 1
+ SQL_BIGINT, // 2
+ 19, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_TRUE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_BIGINT, // 16
+ NULL_INT, // 17
+ 10, // 18
+ NULL_INT // 19
+ },
+ { "REAL", // 1
+ SQL_REAL, // 2
+ 31, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_FALSE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_REAL, // 16
+ NULL_INT, // 17
+ 2, // 18
+ NULL_INT // 19
+ },
+ { "FLOAT", // 1
+ SQL_DOUBLE, // 2
+ 63, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_FALSE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_DOUBLE, // 16
+ NULL_INT, // 17
+ 2, // 18
+ NULL_INT // 19
+ },
+ { "DATETIME", // 1
+ SQL_TYPE_TIMESTAMP, // 2
+ 30, // 3
+ NULL_CHAR, // 4
+ NULL_CHAR, // 5
+ NULL_CHAR, // 6
+ SQL_NULLABLE, // 7
+ SQL_FALSE, // 8
+ SQL_SEARCHABLE, // 9
+ SQL_FALSE, // 10
+ SQL_TRUE, // 11
+ SQL_FALSE, // 12
+ NULL_CHAR, // 13
+ NULL_INT, // 14
+ NULL_INT, // 15
+ SQL_DATETIME, // 16 XXX
+ NULL_INT, // 17
+ 2, // 18
+ NULL_INT // 19
+ }
+};
+
+const unsigned
+typeinfoCount = sizeof(typeinfoList)/sizeof(typeinfoList[0]);
+
+void
+Exec_query_sys::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return;
+ }
+ if (code.m_sysId == DictSys::OdbcTypeinfo || code.m_sysId == DictSys::Dual) {
+ data.m_rowPos = 0; // at first entry
+ return;
+ }
+ if (code.m_sysId == DictSys::OdbcTables || code.m_sysId == DictSys::OdbcColumns || code.m_sysId == DictSys::OdbcPrimarykeys) {
+ // take all objects
+ if (ndbDictionary->listObjects(data.m_objectList) == -1) {
+ ctx.pushStatus(ndb, "listObjects");
+ return;
+ }
+ data.m_tablePos = 0; // at first entry
+ data.m_attrPos = 0;
+ data.m_keyPos = 0;
+ return;
+ }
+ ctx_assert(false);
+}
+
+static bool
+isNdbTable(const NdbDictionary::Dictionary::List::Element& element)
+{
+ switch (element.type) {
+ //case NdbDictionary::Object::SystemTable:
+ case NdbDictionary::Object::UserTable:
+ case NdbDictionary::Object::UniqueHashIndex:
+ case NdbDictionary::Object::HashIndex:
+ case NdbDictionary::Object::UniqueOrderedIndex:
+ case NdbDictionary::Object::OrderedIndex:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+
+bool
+Exec_query_sys::fetchImpl(Ctx &ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbDictionary::Dictionary* ndbDictionary = ndb->getDictionary();
+ if (ndbDictionary == 0) {
+ ctx.pushStatus(ndb, "getDictionary");
+ return false;
+ }
+ if (code.m_sysId == DictSys::OdbcTypeinfo) {
+ if (data.m_rowPos >= typeinfoCount) {
+ return false;
+ }
+ SqlRow& row = data.m_sqlRow;
+ const Typeinfo& typeinfo = typeinfoList[data.m_rowPos++];
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ SqlField& f = data.m_sqlRow.getEntry(i);
+ switch (1 + code.m_attrId[i]) {
+ case 1:
+ if (typeinfo.m_type_name == NULL_CHAR)
+ f.sqlNull(true);
+ else
+ f.sqlVarchar(typeinfo.m_type_name, SQL_NTS);
+ break;
+ case 2:
+ if (typeinfo.m_data_type == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_data_type);
+ break;
+ case 3:
+ if (typeinfo.m_column_size == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_column_size);
+ break;
+ case 4:
+ if (typeinfo.m_literal_prefix == NULL_CHAR)
+ f.sqlNull(true);
+ else
+ f.sqlVarchar(typeinfo.m_literal_prefix, SQL_NTS);
+ break;
+ case 5:
+ if (typeinfo.m_literal_suffix == NULL_CHAR)
+ f.sqlNull(true);
+ else
+ f.sqlVarchar(typeinfo.m_literal_suffix, SQL_NTS);
+ break;
+ case 6:
+ if (typeinfo.m_create_params == NULL_CHAR)
+ f.sqlNull(true);
+ else
+ f.sqlVarchar(typeinfo.m_create_params, SQL_NTS);
+ break;
+ case 7:
+ if (typeinfo.m_nullable == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_nullable);
+ break;
+ case 8:
+ if (typeinfo.m_case_sensitive == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_case_sensitive);
+ break;
+ case 9:
+ if (typeinfo.m_searchable == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_searchable);
+ break;
+ case 10:
+ if (typeinfo.m_unsigned_attribute == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_unsigned_attribute);
+ break;
+ case 11:
+ if (typeinfo.m_fixed_prec_scale == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_fixed_prec_scale);
+ break;
+ case 12:
+ if (typeinfo.m_auto_unique_value == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_auto_unique_value);
+ break;
+ case 13:
+ if (typeinfo.m_local_type_name == NULL_CHAR)
+ f.sqlNull(true);
+ else
+ f.sqlVarchar(typeinfo.m_local_type_name, SQL_NTS);
+ break;
+ case 14:
+ if (typeinfo.m_minimum_scale == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_minimum_scale);
+ break;
+ case 15:
+ if (typeinfo.m_maximum_scale == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_maximum_scale);
+ break;
+ case 16:
+ if (typeinfo.m_sql_data_type == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_sql_data_type);
+ break;
+ case 17:
+ if (typeinfo.m_sql_datetime_sub == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_sql_datetime_sub);
+ break;
+ case 18:
+ if (typeinfo.m_sql_datetime_sub == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_sql_datetime_sub);
+ break;
+ case 19:
+ if (typeinfo.m_interval_precision == NULL_INT)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(typeinfo.m_interval_precision);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ }
+ return true;
+ }
+ if (code.m_sysId == DictSys::OdbcTables) {
+ if (data.m_tablePos >= data.m_objectList.count) {
+ return false;
+ }
+ NdbDictionary::Dictionary::List::Element& element = data.m_objectList.elements[data.m_tablePos++];
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ SqlField& f = data.m_sqlRow.getEntry(i);
+ switch (1 + code.m_attrId[i]) {
+ case 1: // TABLE_CAT
+ f.sqlNull(true);
+ break;
+ case 2: // TABLE_SCHEM
+ f.sqlNull(true);
+ break;
+ case 3: // TABLE_NAME
+ f.sqlVarchar(element.name, SQL_NTS);
+ break;
+ case 4: { // TABLE_TYPE
+ if (element.type == NdbDictionary::Object::SystemTable)
+ f.sqlVarchar("SYSTEM TABLE", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::UserTable)
+ f.sqlVarchar("TABLE", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::UniqueHashIndex)
+ f.sqlVarchar("UNIQUE HASH INDEX", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::HashIndex)
+ f.sqlVarchar("HASH INDEX", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::UniqueOrderedIndex)
+ f.sqlVarchar("UNIQUE INDEX", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::OrderedIndex)
+ f.sqlVarchar("INDEX", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::IndexTrigger)
+ f.sqlVarchar("INDEX TRIGGER", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::SubscriptionTrigger)
+ f.sqlVarchar("SUBSCRIPTION TRIGGER", SQL_NTS);
+ else if (element.type == NdbDictionary::Object::ReadOnlyConstraint)
+ f.sqlVarchar("READ ONLY CONSTRAINT", SQL_NTS);
+ else
+ f.sqlVarchar("UNKNOWN", SQL_NTS);
+ }
+ break;
+ case 5: // REMARKS
+ f.sqlNull(true);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ }
+ return true;
+ }
+ if (code.m_sysId == DictSys::OdbcColumns) {
+ if (data.m_tablePos >= data.m_objectList.count) {
+ return false;
+ }
+ const NdbDictionary::Dictionary::List::Element& element = data.m_objectList.elements[data.m_tablePos];
+ unsigned nattr;
+ const NdbDictionary::Table* ndbTable;
+ nattr = 0;
+ if (isNdbTable(element)) {
+ ndbTable = ndbDictionary->getTable(element.name);
+ if (ndbTable == 0) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "getTable %s", element.name);
+ return false;
+ }
+ nattr = ndbTable->getNoOfColumns();
+ }
+ while (data.m_attrPos >= nattr) {
+ if (++data.m_tablePos >= data.m_objectList.count) {
+ return false;
+ }
+ const NdbDictionary::Dictionary::List::Element& element = data.m_objectList.elements[data.m_tablePos];
+ nattr = 0;
+ if (isNdbTable(element)) {
+ ndbTable = ndbDictionary->getTable(element.name);
+ if (ndbTable == 0) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "getTable %s", element.name);
+ return false;
+ }
+ data.m_attrPos = 0;
+ nattr = ndbTable->getNoOfColumns();
+ }
+ }
+ int attrId = data.m_attrPos++;
+ const NdbDictionary::Column* ndbColumn = ndbTable->getColumn(attrId);
+ if (ndbColumn == 0) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "getColumn %s.%d", ndbTable->getName(), attrId);
+ return false;
+ }
+ SqlType sqlType(ctx, ndbColumn);
+ if (! ctx.ok())
+ return false;
+ const char* p;
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ SqlField& f = data.m_sqlRow.getEntry(i);
+ switch (1 + code.m_attrId[i]) {
+ case 1: // TABLE_CAT
+ f.sqlNull(true);
+ break;
+ case 2: // TABLE_SCHEM
+ f.sqlNull(true);
+ break;
+ case 3: // TABLE_NAME
+ f.sqlVarchar(ndbTable->getName(), SQL_NTS);
+ break;
+ case 4: // COLUMN_NAME
+ f.sqlVarchar(ndbColumn->getName(), SQL_NTS);
+ break;
+ case 5: // DATA_TYPE
+ f.sqlInteger(sqlType.type());
+ break;
+ case 6: // TYPE_NAME
+ f.sqlVarchar(sqlType.typeName(), SQL_NTS);
+ break;
+ case 7: // COLUMN_SIZE
+ f.sqlInteger(sqlType.displaySize());
+ break;
+ case 8: // BUFFER_LENGTH
+ f.sqlInteger(sqlType.size());
+ break;
+ case 9: // DECIMAL_DIGITS
+ if (sqlType.type() == SqlType::Char)
+ f.sqlNull(true);
+ else
+ f.sqlInteger(0);
+ break;
+ case 10: // NUM_PREC_RADIX
+ if (sqlType.type() == SqlType::Integer || sqlType.type() == SqlType::Bigint)
+ f.sqlInteger(10);
+ else
+ f.sqlNull(true);
+ break;
+ case 11: // NULLABLE
+ if (sqlType.nullable())
+ f.sqlInteger(SQL_NULLABLE);
+ else
+ f.sqlInteger(SQL_NO_NULLS);
+ break;
+ case 12: // REMARKS
+ f.sqlNull(true);
+ break;
+ case 13: // COLUMN_DEF
+ if ((p = ndbColumn->getDefaultValue()) != 0)
+ f.sqlVarchar(p, SQL_NTS);
+ else
+ f.sqlNull(true);
+ break;
+ case 14: // SQL_DATA_TYPE
+ f.sqlInteger(sqlType.type());
+ break;
+ case 15: // SQL_DATETIME_SUB
+ f.sqlNull(true);
+ break;
+ case 16: // CHAR_OCTET_LENGTH
+ if (sqlType.type() == SqlType::Char)
+ f.sqlInteger(sqlType.length());
+ else
+ f.sqlNull(true);
+ break;
+ case 17: // ORDINAL_POSITION
+ f.sqlInteger(1 + attrId);
+ break;
+ case 18: // IS_NULLABLE
+ if (sqlType.nullable())
+ f.sqlVarchar("YES", SQL_NTS);
+ else
+ f.sqlVarchar("NO", SQL_NTS);
+ break;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ }
+ return true;
+ }
+ if (code.m_sysId == DictSys::OdbcPrimarykeys) {
+ if (data.m_tablePos >= data.m_objectList.count) {
+ return false;
+ }
+ NdbDictionary::Dictionary::List::Element& element = data.m_objectList.elements[data.m_tablePos];
+ unsigned nkeys;
+ const NdbDictionary::Table* ndbTable;
+ nkeys = 0;
+ if (isNdbTable(element)) {
+ ndbTable = ndbDictionary->getTable(element.name);
+ if (ndbTable == 0) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "getTable %s", element.name);
+ return false;
+ }
+ nkeys = ndbTable->getNoOfPrimaryKeys();
+ }
+ while (data.m_keyPos >= nkeys) {
+ if (++data.m_tablePos >= data.m_objectList.count) {
+ return false;
+ }
+ NdbDictionary::Dictionary::List::Element& element = data.m_objectList.elements[data.m_tablePos];
+ nkeys = 0;
+ if (isNdbTable(element)) {
+ ndbTable = ndbDictionary->getTable(element.name);
+ if (ndbTable == 0) {
+ ctx.pushStatus(ndbDictionary->getNdbError(), "getTable %s", element.name);
+ return false;
+ }
+ data.m_keyPos = 0;
+ nkeys = ndbTable->getNoOfPrimaryKeys();
+ }
+ }
+ unsigned keyPos = data.m_keyPos++;
+ const char* keyName = ndbTable->getPrimaryKey(keyPos);
+ if (keyName == 0)
+ keyName = "?";
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ SqlField& f = data.m_sqlRow.getEntry(i);
+ switch (1 + code.m_attrId[i]) {
+ case 1: // TABLE_CAT
+ f.sqlNull(true);
+ break;
+ case 2: // TABLE_SCHEM
+ f.sqlNull(true);
+ break;
+ case 3: // TABLE_NAME
+ f.sqlVarchar(ndbTable->getName(), SQL_NTS);
+ break;
+ case 4: // COLUMN_NAME
+ f.sqlVarchar(keyName, SQL_NTS);
+ break;
+ case 5: // KEY_SEQ
+ f.sqlInteger(1 + keyPos);
+ break;
+ case 6: // PK_NAME
+ f.sqlNull(true);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ }
+ return true;
+ }
+ if (code.m_sysId == DictSys::Dual) {
+ if (data.m_rowPos > 0) {
+ return false;
+ }
+ data.m_rowPos++;
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ SqlField& f = data.m_sqlRow.getEntry(i);
+ switch (1 + code.m_attrId[i]) {
+ case 1: // DUMMY
+ f.sqlVarchar("X", 1);
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ }
+ return true;
+ }
+ ctx_assert(false);
+ return false;
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_update_index.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_update_index.cpp
new file mode 100644
index 00000000000..35b6159d8ca
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_update_index.cpp
@@ -0,0 +1,96 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_update_index.hpp>
+#include <codegen/Code_query.hpp>
+
+void
+Exec_update_index::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // update each row from the query
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* op = tcon->getNdbIndexOperation(code.m_indexName, code.m_tableName);
+ if (op == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "getNdbIndexOperation");
+ return;
+ }
+ if (op->updateTuple() == -1) {
+ ctx.pushStatus(ndb, tcon, op, "updateTuple");
+ return;
+ }
+ // key attributes
+ bool done = false;
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ done = true; // match is not possible
+ break;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->equal(keyId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "equal attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ if (done)
+ continue;
+ // updated attributes
+ const SqlRow& sqlRow = m_query->getData().sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const NdbAttrId attrId = code.m_attrId[i];
+ const SqlField& f = sqlRow.getEntry(i);
+ const void* addr = f.sqlNull() ? 0 : f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->setValue(attrId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "setValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ }
+ data.setCount(0);
+ if (tcon->execute(NoCommit) == -1) {
+ // XXX when did 626 move to connection level
+ if (tcon->getNdbError().code != 626 && op->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, tcon, op, "execute without commit");
+ return;
+ }
+ } else {
+ data.addCount();
+ }
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_update_lookup.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_update_lookup.cpp
new file mode 100644
index 00000000000..2c801372de3
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_update_lookup.cpp
@@ -0,0 +1,96 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_expr.hpp>
+#include <codegen/Code_update_lookup.hpp>
+#include <codegen/Code_query.hpp>
+
+void
+Exec_update_lookup::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ // update each row from the query
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* op = tcon->getNdbOperation(code.m_tableName);
+ if (op == 0) {
+ ctx.pushStatus(ndb, tcon, 0, "getNdbOperation");
+ return;
+ }
+ if (op->updateTuple() == -1) {
+ ctx.pushStatus(ndb, tcon, op, "updateTuple");
+ return;
+ }
+ // key attributes
+ bool done = false;
+ for (unsigned k = 1; k <= code.m_keyCount; k++) {
+ Exec_expr* exprMatch = code.m_keyMatch[k];
+ ctx_assert(exprMatch != 0);
+ exprMatch->evaluate(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ const SqlField& keyMatch = exprMatch->getData().sqlField();
+ SqlField f(code.m_keySpecs.getEntry(k));
+ if (! keyMatch.cast(ctx, f)) {
+ done = true; // match is not possible
+ break;
+ }
+ const NdbAttrId keyId = code.m_keyId[k];
+ const void* addr = f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->equal(keyId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "equal attrId=%u", (unsigned)keyId);
+ return;
+ }
+ }
+ if (done)
+ continue;
+ // updated attributes
+ const SqlRow& sqlRow = m_query->getData().sqlRow();
+ ctx_assert(sqlRow.count() == code.m_attrCount);
+ for (unsigned i = 1; i <= code.m_attrCount; i++) {
+ const NdbAttrId attrId = code.m_attrId[i];
+ const SqlField& f = sqlRow.getEntry(i);
+ const void* addr = f.sqlNull() ? 0 : f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (op->setValue(attrId, value) == -1) {
+ ctx.pushStatus(ndb, tcon, op, "setValue attrId=%u", (unsigned)attrId);
+ return;
+ }
+ }
+ data.setCount(0);
+ if (tcon->execute(NoCommit) == -1) {
+ // XXX when did 626 move to connection level
+ if (tcon->getNdbError().code != 626 && op->getNdbError().code != 626) {
+ ctx.pushStatus(ndb, tcon, op, "execute without commit");
+ return;
+ }
+ } else {
+ data.addCount();
+ }
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Exec_update_scan.cpp b/storage/ndb/src/old_files/client/odbc/executor/Exec_update_scan.cpp
new file mode 100644
index 00000000000..a36fdd27142
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Exec_update_scan.cpp
@@ -0,0 +1,61 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <NdbApi.hpp>
+#include <common/StmtArea.hpp>
+#include <common/ResultArea.hpp>
+#include <codegen/Code_update_scan.hpp>
+#include <codegen/Code_query.hpp>
+
+void
+Exec_update_scan::execImpl(Ctx& ctx, Ctl& ctl)
+{
+ const Code& code = getCode();
+ Data& data = getData();
+ Ndb* const ndb = ndbObject();
+ NdbConnection* const tcon = ndbConnection();
+ // execute subquery
+ ctx_assert(m_query != 0);
+ m_query->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_assert(ctl.m_scanOp != 0);
+ // update each row from query
+ data.setCount(0);
+ while (m_query->fetch(ctx, ctl)) {
+ NdbOperation* toOp = ctl.m_scanOp->takeOverForUpdate(tcon);
+ if (toOp == 0) {
+ ctx.pushStatus(ndb, tcon, ctl.m_scanOp, "takeOverScanOp");
+ return;
+ }
+ const SqlRow& sqlRow = m_query->getData().sqlRow();
+ for (unsigned i = 1; i <= sqlRow.count(); i++) {
+ const SqlField& f = sqlRow.getEntry(i);
+ const void* addr = f.sqlNull() ? 0 : f.addr();
+ const char* value = static_cast<const char*>(addr);
+ if (toOp->setValue(code.m_attrId[i], value) == -1) {
+ ctx.pushStatus(ndb, tcon, toOp, "setValue");
+ return;
+ }
+ }
+ if (tcon->execute(NoCommit) == -1) {
+ ctx.pushStatus(ndb, tcon, toOp, "execute without commit");
+ return;
+ }
+ data.addCount();
+ }
+ stmtArea().setRowCount(ctx, data.getCount());
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Executor.cpp b/storage/ndb/src/old_files/client/odbc/executor/Executor.cpp
new file mode 100644
index 00000000000..adabb28a4a5
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Executor.cpp
@@ -0,0 +1,68 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/common.hpp>
+#include <common/CodeTree.hpp>
+#include <common/StmtArea.hpp>
+#include <codegen/Code_root.hpp>
+#include "Executor.hpp"
+
+void
+Executor::execute(Ctx& ctx)
+{
+ Exec_base::Ctl ctl(0);
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ rebind(ctx);
+ if (! ctx.ok())
+ return;
+ execRoot->execute(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_log2(("Executor: execute done"));
+}
+
+void
+Executor::fetch(Ctx& ctx)
+{
+ Exec_base::Ctl ctl(0);
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ rebind(ctx);
+ if (! ctx.ok())
+ return;
+ execRoot->fetch(ctx, ctl);
+ if (! ctx.ok())
+ return;
+ ctx_log2(("Executor: fetch done"));
+}
+
+void
+Executor::rebind(Ctx& ctx)
+{
+ Exec_root* execRoot = static_cast<Exec_root*>(m_stmtArea.m_execTree);
+ ctx_assert(execRoot != 0);
+ DescArea& apd = m_stmtArea.descArea(Desc_usage_APD);
+ DescArea& ard = m_stmtArea.descArea(Desc_usage_ARD);
+ if (! apd.isBound() || ! ard.isBound()) {
+ ctx_log2(("Executor: rebind required"));
+ execRoot->bind(ctx);
+ if (! ctx.ok())
+ return;
+ apd.setBound(true);
+ ard.setBound(true);
+ }
+}
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Executor.hpp b/storage/ndb/src/old_files/client/odbc/executor/Executor.hpp
new file mode 100644
index 00000000000..5edb9d509ac
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Executor.hpp
@@ -0,0 +1,52 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_EXECUTOR_Executor_hpp
+#define ODBC_EXECUTOR_Executor_hpp
+
+#include <common/common.hpp>
+class StmtArea;
+
+/**
+ * @class Executor
+ * @brief Executes an ExecTree
+ */
+class Executor {
+public:
+ Executor(StmtArea& stmtArea);
+ ~Executor();
+ // execute prepared statement
+ void execute(Ctx& ctx);
+ // fetch next row from query
+ void fetch(Ctx& ctx);
+private:
+ // rebind if necessary
+ void rebind(Ctx& ctx);
+ StmtArea m_stmtArea;
+};
+
+inline
+Executor::Executor(StmtArea& stmtArea) :
+ m_stmtArea(stmtArea)
+{
+}
+
+inline
+Executor::~Executor()
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/executor/Makefile b/storage/ndb/src/old_files/client/odbc/executor/Makefile
new file mode 100644
index 00000000000..d86781e212c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/executor/Makefile
@@ -0,0 +1,36 @@
+include .defs.mk
+
+TYPE = *
+
+NONPIC_ARCHIVE = N
+
+PIC_ARCHIVE = Y
+
+ARCHIVE_TARGET = odbcexecutor
+
+SOURCES = \
+ Executor.cpp \
+ Exec_query_lookup.cpp \
+ Exec_query_index.cpp \
+ Exec_query_scan.cpp \
+ Exec_query_range.cpp \
+ Exec_query_sys.cpp \
+ Exec_pred_op.cpp \
+ Exec_comp_op.cpp \
+ Exec_expr_op.cpp \
+ Exec_expr_func.cpp \
+ Exec_expr_conv.cpp \
+ Exec_insert.cpp \
+ Exec_update_lookup.cpp \
+ Exec_update_index.cpp \
+ Exec_update_scan.cpp \
+ Exec_delete_lookup.cpp \
+ Exec_delete_index.cpp \
+ Exec_delete_scan.cpp \
+ Exec_create_table.cpp \
+ Exec_create_index.cpp \
+ Exec_drop_table.cpp \
+ Exec_drop_index.cpp
+
+include ../Extra.mk
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/client/odbc/handles/AttrDbc.cpp b/storage/ndb/src/old_files/client/odbc/handles/AttrDbc.cpp
new file mode 100644
index 00000000000..4768a8995a2
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/AttrDbc.cpp
@@ -0,0 +1,473 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleDbc.hpp"
+
+static void
+callback_SQL_ATTR_ACCESS_MODE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_MODE_READ_ONLY:
+ break;
+ case SQL_MODE_READ_WRITE:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid access mode value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_ACCESS_MODE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = SQL_MODE_READ_WRITE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ASYNC_ENABLE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_ASYNC_ENABLE_OFF:
+ break;
+ case SQL_ASYNC_ENABLE_ON:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "async enable on not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid async enable value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_ASYNC_ENABLE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = SQL_ASYNC_ENABLE_OFF;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_AUTO_IPD_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ ctx_assert(false); // read-only
+}
+
+static void
+callback_SQL_ATTR_AUTO_IPD_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = SQL_FALSE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_AUTOCOMMIT_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_AUTOCOMMIT_OFF:
+ if (pDbc->autocommit()) {
+ pDbc->autocommit(false);
+ pDbc->useConnection(ctx, true);
+ }
+ break;
+ case SQL_AUTOCOMMIT_ON:
+ if (! pDbc->autocommit()) {
+ pDbc->autocommit(true);
+ pDbc->sqlEndTran(ctx, SQL_COMMIT);
+ pDbc->useConnection(ctx, false);
+ }
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid autocommit value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_AUTOCOMMIT_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = pDbc->autocommit() ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CONNECTION_DEAD_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ ctx_assert(false); // read-only
+}
+
+static void
+callback_SQL_ATTR_CONNECTION_DEAD_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = pDbc->getState() == HandleDbc::Free ? SQL_CD_TRUE : SQL_CD_FALSE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CONNECTION_TIMEOUT_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+}
+
+static void
+callback_SQL_ATTR_CONNECTION_TIMEOUT_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CURRENT_CATALOG_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_ATTR_CURRENT_CATALOG_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ const char* value = "DEFAULT";
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_LOGIN_TIMEOUT_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ callback_SQL_ATTR_CONNECTION_TIMEOUT_set(ctx, self, data);
+}
+
+static void
+callback_SQL_ATTR_LOGIN_TIMEOUT_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ callback_SQL_ATTR_CONNECTION_TIMEOUT_default(ctx, self, data);
+}
+
+static void
+callback_SQL_ATTR_METADATA_ID_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_FALSE:
+ break;
+ case SQL_TRUE:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid metadata id value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_METADATA_ID_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = SQL_FALSE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ODBC_CURSORS_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_CUR_USE_DRIVER:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cur use driver not supported");
+ break;
+ case SQL_CUR_USE_IF_NEEDED:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cur use if needed not supported");
+ break;
+ case SQL_CUR_USE_ODBC:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid odbc cursors value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_ODBC_CURSORS_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = SQL_CUR_USE_ODBC;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PACKET_SIZE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "packet size (%u) not supported", (unsigned)value);
+}
+
+static void
+callback_SQL_ATTR_PACKET_SIZE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_QUIET_MODE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Pointer);
+}
+
+static void
+callback_SQL_ATTR_QUIET_MODE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_ATTR_TRACE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+}
+
+static void
+callback_SQL_ATTR_TRACE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_ATTR_TRACEFILE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_ATTR_TRACEFILE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_ATTR_TRANSLATE_LIB_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_ATTR_TRANSLATE_LIB_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_ATTR_TRANSLATE_OPTION_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+}
+
+static void
+callback_SQL_ATTR_TRANSLATE_OPTION_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_ATTR_TXN_ISOLATION_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0 && data.type() == OdbcData::Uinteger);
+ if (pDbc->getState() == HandleDbc::Free) {
+ ctx.pushStatus(Sqlstate::_08003, Error::Gen, "not connected");
+ return;
+ }
+ if (pDbc->getState() == HandleDbc::Transacting) {
+ ctx.pushStatus(Sqlstate::_HY011, Error::Gen, "transaction is open");
+ return;
+ }
+ SQLUINTEGER value = data.uinteger();
+ SQLUINTEGER mask = SQL_TXN_READ_COMMITTED;
+ if (! (value & mask)) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "txn isolation level %u not supported", (unsigned)value);
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_TXN_ISOLATION_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDbc* pDbc = static_cast<HandleDbc*>(self);
+ ctx_assert(pDbc != 0);
+ SQLUINTEGER value = SQL_TXN_READ_COMMITTED;
+ data.setValue(value);
+}
+
+AttrSpec HandleDbc::m_attrSpec[] = {
+ { SQL_ATTR_ACCESS_MODE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ACCESS_MODE_set,
+ callback_SQL_ATTR_ACCESS_MODE_default,
+ },
+ { SQL_ATTR_ASYNC_ENABLE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ASYNC_ENABLE_set,
+ callback_SQL_ATTR_ASYNC_ENABLE_default,
+ },
+ { SQL_ATTR_AUTO_IPD,
+ OdbcData::Uinteger,
+ Attr_mode_readonly,
+ callback_SQL_ATTR_AUTO_IPD_set,
+ callback_SQL_ATTR_AUTO_IPD_default,
+ },
+ { SQL_ATTR_AUTOCOMMIT,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_AUTOCOMMIT_set,
+ callback_SQL_ATTR_AUTOCOMMIT_default,
+ },
+ { SQL_ATTR_CONNECTION_DEAD,
+ OdbcData::Uinteger,
+ Attr_mode_readonly,
+ callback_SQL_ATTR_CONNECTION_DEAD_set,
+ callback_SQL_ATTR_CONNECTION_DEAD_default,
+ },
+ { SQL_ATTR_CONNECTION_TIMEOUT,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CONNECTION_TIMEOUT_set,
+ callback_SQL_ATTR_CONNECTION_TIMEOUT_default,
+ },
+ { SQL_ATTR_CURRENT_CATALOG,
+ OdbcData::Sqlchar,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CURRENT_CATALOG_set,
+ callback_SQL_ATTR_CURRENT_CATALOG_default,
+ },
+ { SQL_ATTR_LOGIN_TIMEOUT,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_LOGIN_TIMEOUT_set,
+ callback_SQL_ATTR_LOGIN_TIMEOUT_default,
+ },
+ { SQL_ATTR_METADATA_ID,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_METADATA_ID_set,
+ callback_SQL_ATTR_METADATA_ID_default,
+ },
+ { SQL_ATTR_ODBC_CURSORS,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ODBC_CURSORS_set,
+ callback_SQL_ATTR_ODBC_CURSORS_default,
+ },
+ { SQL_ATTR_PACKET_SIZE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PACKET_SIZE_set,
+ callback_SQL_ATTR_PACKET_SIZE_default,
+ },
+ { SQL_ATTR_QUIET_MODE,
+ OdbcData::Pointer,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_QUIET_MODE_set,
+ callback_SQL_ATTR_QUIET_MODE_default,
+ },
+ { SQL_ATTR_TRACE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_TRACE_set,
+ callback_SQL_ATTR_TRACE_default,
+ },
+ { SQL_ATTR_TRACEFILE,
+ OdbcData::Sqlchar,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_TRACEFILE_set,
+ callback_SQL_ATTR_TRACEFILE_default,
+ },
+ { SQL_ATTR_TRANSLATE_LIB,
+ OdbcData::Sqlchar,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_TRANSLATE_LIB_set,
+ callback_SQL_ATTR_TRANSLATE_LIB_default,
+ },
+ { SQL_ATTR_TRANSLATE_OPTION,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_TRANSLATE_OPTION_set,
+ callback_SQL_ATTR_TRANSLATE_OPTION_default,
+ },
+ { SQL_ATTR_TXN_ISOLATION,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_TXN_ISOLATION_set,
+ callback_SQL_ATTR_TXN_ISOLATION_default,
+ },
+ { 0,
+ OdbcData::Undef,
+ Attr_mode_undef,
+ 0,
+ 0,
+ },
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/AttrEnv.cpp b/storage/ndb/src/old_files/client/odbc/handles/AttrEnv.cpp
new file mode 100644
index 00000000000..3d57fddeb57
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/AttrEnv.cpp
@@ -0,0 +1,123 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleEnv.hpp"
+
+static void
+callback_SQL_ATTR_CP_MATCH_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleEnv* pEnv = static_cast<HandleEnv*>(self);
+ ctx_assert(pEnv != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_CP_STRICT_MATCH:
+ break;
+ case SQL_CP_RELAXED_MATCH:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid cp match value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CP_MATCH_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleEnv* pEnv = static_cast<HandleEnv*>(self);
+ ctx_assert(pEnv != 0);
+ SQLUINTEGER value = SQL_CP_STRICT_MATCH;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ODBC_VERSION_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleEnv* pEnv = static_cast<HandleEnv*>(self);
+ ctx_assert(pEnv != 0 && data.type() == OdbcData::Integer);
+ int version = data.integer();
+ switch (version) {
+ case SQL_OV_ODBC2:
+ case SQL_OV_ODBC3:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid ODBC version %d", version);
+ return;
+ }
+ ctx_log1(("odbc version set to %d", version));
+}
+
+static void
+callback_SQL_ATTR_ODBC_VERSION_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleEnv* pEnv = static_cast<HandleEnv*>(self);
+ ctx_assert(pEnv != 0);
+ // no default
+ ctx_log1(("odbc version has not been set"));
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "odbc version has not been set");
+}
+
+static void
+callback_SQL_ATTR_OUTPUT_NTS_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleEnv* pEnv = static_cast<HandleEnv*>(self);
+ ctx_assert(pEnv != 0 && data.type() == OdbcData::Integer);
+ SQLINTEGER value = data.integer();
+ switch (value) {
+ case SQL_TRUE:
+ break;
+ case SQL_FALSE:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "output nts FALSE not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid output nts value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_OUTPUT_NTS_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleEnv* pEnv = static_cast<HandleEnv*>(self);
+ ctx_assert(pEnv != 0);
+ data.setValue();
+}
+
+AttrSpec HandleEnv::m_attrSpec[] = {
+ { SQL_ATTR_CP_MATCH,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CP_MATCH_set,
+ callback_SQL_ATTR_CP_MATCH_default,
+ },
+ { SQL_ATTR_ODBC_VERSION,
+ OdbcData::Integer,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ODBC_VERSION_set,
+ callback_SQL_ATTR_ODBC_VERSION_default,
+ },
+ { SQL_ATTR_OUTPUT_NTS,
+ OdbcData::Integer,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_OUTPUT_NTS_set,
+ callback_SQL_ATTR_OUTPUT_NTS_default,
+ },
+ { 0,
+ OdbcData::Undef,
+ Attr_mode_undef,
+ 0,
+ 0,
+ },
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/AttrRoot.cpp b/storage/ndb/src/old_files/client/odbc/handles/AttrRoot.cpp
new file mode 100644
index 00000000000..d1b264835b6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/AttrRoot.cpp
@@ -0,0 +1,92 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleRoot.hpp"
+
+static void
+callback_SQL_ATTR_CONNECTION_POOLING_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleRoot* pRoot = static_cast<HandleRoot*>(self);
+ ctx_assert(pRoot != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_CP_OFF:
+ break;
+ case SQL_CP_ONE_PER_DRIVER:
+ case SQL_CP_ONE_PER_HENV:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "connection pooling not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid connection pooling value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CONNECTION_POOLING_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleRoot* pRoot = static_cast<HandleRoot*>(self);
+ ctx_assert(pRoot != 0);
+ SQLUINTEGER value = SQL_CP_OFF;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CP_MATCH_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleRoot* pRoot = static_cast<HandleRoot*>(self);
+ ctx_assert(pRoot != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_CP_STRICT_MATCH:
+ break;
+ case SQL_CP_RELAXED_MATCH:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid cp match value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CP_MATCH_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleRoot* pRoot = static_cast<HandleRoot*>(self);
+ ctx_assert(pRoot != 0);
+ SQLUINTEGER value = SQL_CP_STRICT_MATCH;
+ data.setValue(value);
+}
+
+AttrSpec HandleRoot::m_attrSpec[] = {
+ { SQL_ATTR_CONNECTION_POOLING,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CONNECTION_POOLING_set,
+ callback_SQL_ATTR_CONNECTION_POOLING_default,
+ },
+ { SQL_ATTR_CP_MATCH,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CP_MATCH_set,
+ callback_SQL_ATTR_CP_MATCH_default,
+ },
+ { 0,
+ OdbcData::Undef,
+ Attr_mode_undef,
+ 0,
+ 0,
+ },
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/AttrStmt.cpp b/storage/ndb/src/old_files/client/odbc/handles/AttrStmt.cpp
new file mode 100644
index 00000000000..ce9a9c03fd1
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/AttrStmt.cpp
@@ -0,0 +1,1005 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleDbc.hpp"
+#include "HandleStmt.hpp"
+#include "HandleDesc.hpp"
+
+static void
+callback_SQL_ATTR_APP_PARAM_DESC_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Pointer);
+ pStmt->setHandleDesc(ctx, Desc_usage_APD, data.pointer());
+}
+
+static void
+callback_SQL_ATTR_APP_PARAM_DESC_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ HandleDesc* apd = pStmt->getHandleDesc(ctx, Desc_usage_APD);
+ OdbcData value(reinterpret_cast<SQLPOINTER>(apd));
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_APP_ROW_DESC_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Pointer);
+ pStmt->setHandleDesc(ctx, Desc_usage_ARD, data.pointer());
+}
+
+static void
+callback_SQL_ATTR_APP_ROW_DESC_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ HandleDesc* ard = pStmt->getHandleDesc(ctx, Desc_usage_ARD);
+ OdbcData value(reinterpret_cast<SQLPOINTER>(ard));
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ASYNC_ENABLE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_ASYNC_ENABLE_OFF:
+ break;
+ case SQL_ASYNC_ENABLE_ON:
+// ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "async enable ON not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid async enable value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_ASYNC_ENABLE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_ASYNC_ENABLE_OFF;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CONCURRENCY_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_CONCUR_READ_ONLY:
+ break;
+ case SQL_CONCUR_LOCK:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "concur lock not supported");
+ break;
+ case SQL_CONCUR_ROWVER:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "concur rowver not supported");
+ break;
+ case SQL_CONCUR_VALUES:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "concur values not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid concurrency value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CONCURRENCY_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_CONCUR_READ_ONLY;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CURSOR_SCROLLABLE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_NONSCROLLABLE:
+ break;
+ case SQL_SCROLLABLE:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cursor scrollable not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid concurrency value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CURSOR_SCROLLABLE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_NONSCROLLABLE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CURSOR_SENSITIVITY_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_UNSPECIFIED:
+ case SQL_INSENSITIVE:
+ break;
+ case SQL_SENSITIVE:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cursor sensitive not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid cursor sensitivity value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CURSOR_SENSITIVITY_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_INSENSITIVE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_CURSOR_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_CURSOR_FORWARD_ONLY:
+ break;
+ case SQL_CURSOR_STATIC:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cursor static not supported");
+ break;
+ case SQL_CURSOR_KEYSET_DRIVEN:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cursor keyset driven not supported");
+ break;
+ case SQL_CURSOR_DYNAMIC:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "cursor dynamic not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid cursor type value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_CURSOR_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_CURSOR_FORWARD_ONLY;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ENABLE_AUTO_IPD_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_FALSE:
+ break;
+ case SQL_TRUE:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "enable auto IPD not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid enable auto IPD value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_ENABLE_AUTO_IPD_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_FALSE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_FETCH_BOOKMARK_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Pointer);
+ SQLPOINTER value = data.pointer();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "fetch bookmark ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_FETCH_BOOKMARK_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLPOINTER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_IMP_PARAM_DESC_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Pointer);
+ ctx_assert(false); // read-only
+}
+
+static void
+callback_SQL_ATTR_IMP_PARAM_DESC_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ HandleDesc* ipd = pStmt->getHandleDesc(ctx, Desc_usage_IPD);
+ OdbcData value(reinterpret_cast<SQLPOINTER>(ipd));
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_IMP_ROW_DESC_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Pointer);
+ ctx_assert(false); // read-only
+}
+
+static void
+callback_SQL_ATTR_IMP_ROW_DESC_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ HandleDesc* ird = pStmt->getHandleDesc(ctx, Desc_usage_IRD);
+ OdbcData value(reinterpret_cast<SQLPOINTER>(ird));
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_KEYSET_SIZE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "keyset size not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_KEYSET_SIZE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_MAX_LENGTH_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "max length not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_MAX_LENGTH_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_MAX_ROWS_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "max rows not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_MAX_ROWS_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_METADATA_ID_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_FALSE:
+ break;
+ case SQL_TRUE:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid metadata id value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_METADATA_ID_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_FALSE;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_NOSCAN_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_NOSCAN_OFF:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "noscan OFF not supported");
+ break;
+ case SQL_NOSCAN_ON:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid no scan value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_NOSCAN_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_NOSCAN_ON;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PARAM_BIND_OFFSET_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UintegerPtr);
+ SQLUINTEGER* value = data.uintegerPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "param bind offset ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_PARAM_BIND_OFFSET_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PARAM_BIND_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != SQL_PARAM_BIND_BY_COLUMN) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "row-wise param binding not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_PARAM_BIND_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_PARAM_BIND_BY_COLUMN;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PARAM_OPERATION_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UsmallintPtr);
+ SQLUSMALLINT* value = data.usmallintPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "param operation ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_PARAM_OPERATION_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUSMALLINT* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PARAM_STATUS_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UsmallintPtr);
+ SQLUSMALLINT* value = data.usmallintPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "param status ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_PARAM_STATUS_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUSMALLINT* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PARAMS_PROCESSED_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UintegerPtr);
+ SQLUINTEGER* value = data.uintegerPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "params processed ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_PARAMS_PROCESSED_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_PARAMSET_SIZE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != 1) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "paramset size %u not supported", (unsigned)value);
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_PARAMSET_SIZE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = 1;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_QUERY_TIMEOUT_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_01S02, Error::Gen, "query timeout %u replaced by 0", (unsigned)value);
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_QUERY_TIMEOUT_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_RETRIEVE_DATA_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_RD_ON:
+ break;
+ case SQL_RD_OFF:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "retrieve data OFF not supported");
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid retrieve data value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_RETRIEVE_DATA_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_RD_ON;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROW_ARRAY_SIZE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != 1) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "row array size %u != 1 not supported", (unsigned)value);
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_ROW_ARRAY_SIZE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = 1;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROW_BIND_OFFSET_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UintegerPtr);
+ SQLUINTEGER* value = data.uintegerPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "row bind offset ptr != 0 not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_ROW_BIND_OFFSET_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROW_BIND_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ if (value != SQL_BIND_BY_COLUMN) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "row-wise binding not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_ROW_BIND_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_BIND_BY_COLUMN;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROW_NUMBER_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ ctx_assert(false); // read-only
+}
+
+static void
+callback_SQL_ATTR_ROW_NUMBER_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = pStmt->getRowCount();
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROW_OPERATION_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UsmallintPtr);
+ SQLUSMALLINT* value = data.usmallintPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "row operation ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_ROW_OPERATION_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUSMALLINT* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROW_STATUS_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UsmallintPtr);
+ SQLUSMALLINT* value = data.usmallintPtr();
+ if (value != 0) {
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "row status ptr not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_ROW_STATUS_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUSMALLINT* value = 0;
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_ROWS_FETCHED_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::UintegerPtr);
+ SQLUINTEGER* value = data.uintegerPtr();
+ HandleDesc* ird = pStmt->getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlSetDescField(ctx, 0, SQL_DESC_ROWS_PROCESSED_PTR, static_cast<SQLPOINTER>(value), SQL_IS_POINTER);
+}
+
+static void
+callback_SQL_ATTR_ROWS_FETCHED_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER* value = 0;
+ HandleDesc* ird = pStmt->getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlGetDescField(ctx, 0, SQL_DESC_ROWS_PROCESSED_PTR, &value, SQL_IS_POINTER, 0);
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_SIMULATE_CURSOR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_SC_NON_UNIQUE:
+ break;
+ case SQL_SC_TRY_UNIQUE:
+ break;
+ case SQL_SC_UNIQUE:
+ break;
+ default:
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "invalid simulate cursor value %u", (unsigned)value);
+ break;
+ }
+}
+
+static void
+callback_SQL_ATTR_SIMULATE_CURSOR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_SC_UNIQUE; // XXX if we did
+ data.setValue(value);
+}
+
+static void
+callback_SQL_ATTR_USE_BOOKMARKS_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ SQLUINTEGER value = data.uinteger();
+ switch (value) {
+ case SQL_UB_OFF:
+ break;
+ case SQL_UB_VARIABLE:
+ case SQL_UB_FIXED:
+ ctx.pushStatus(Sqlstate::_HYC00, Error::Gen, "bookmarks not supported");
+ return;
+ }
+}
+
+static void
+callback_SQL_ATTR_USE_BOOKMARKS_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = SQL_UB_OFF;
+ data.setValue(value);
+}
+
+// driver specific
+
+static void
+callback_SQL_ATTR_NDB_TUPLES_FETCHED_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0 && data.type() == OdbcData::Uinteger);
+ ctx_assert(false); // read-only
+}
+
+static void
+callback_SQL_ATTR_NDB_TUPLES_FETCHED_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleStmt* pStmt = static_cast<HandleStmt*>(self);
+ ctx_assert(pStmt != 0);
+ SQLUINTEGER value = pStmt->getTuplesFetched();
+ data.setValue(value);
+}
+
+AttrSpec HandleStmt::m_attrSpec[] = {
+ { SQL_ATTR_APP_PARAM_DESC,
+ OdbcData::Pointer,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_APP_PARAM_DESC_set,
+ callback_SQL_ATTR_APP_PARAM_DESC_default,
+ },
+ { SQL_ATTR_APP_ROW_DESC,
+ OdbcData::Pointer,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_APP_ROW_DESC_set,
+ callback_SQL_ATTR_APP_ROW_DESC_default,
+ },
+ { SQL_ATTR_ASYNC_ENABLE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ASYNC_ENABLE_set,
+ callback_SQL_ATTR_ASYNC_ENABLE_default,
+ },
+ { SQL_ATTR_CONCURRENCY,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CONCURRENCY_set,
+ callback_SQL_ATTR_CONCURRENCY_default,
+ },
+ { SQL_ATTR_CURSOR_SCROLLABLE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CURSOR_SCROLLABLE_set,
+ callback_SQL_ATTR_CURSOR_SCROLLABLE_default,
+ },
+ { SQL_ATTR_CURSOR_SENSITIVITY,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CURSOR_SENSITIVITY_set,
+ callback_SQL_ATTR_CURSOR_SENSITIVITY_default,
+ },
+ { SQL_ATTR_CURSOR_TYPE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_CURSOR_TYPE_set,
+ callback_SQL_ATTR_CURSOR_TYPE_default,
+ },
+ { SQL_ATTR_ENABLE_AUTO_IPD,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ENABLE_AUTO_IPD_set,
+ callback_SQL_ATTR_ENABLE_AUTO_IPD_default,
+ },
+ { SQL_ATTR_FETCH_BOOKMARK_PTR,
+ OdbcData::Pointer,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_FETCH_BOOKMARK_PTR_set,
+ callback_SQL_ATTR_FETCH_BOOKMARK_PTR_default,
+ },
+ { SQL_ATTR_IMP_PARAM_DESC,
+ OdbcData::Pointer,
+ Attr_mode_readonly,
+ callback_SQL_ATTR_IMP_PARAM_DESC_set,
+ callback_SQL_ATTR_IMP_PARAM_DESC_default,
+ },
+ { SQL_ATTR_IMP_ROW_DESC,
+ OdbcData::Pointer,
+ Attr_mode_readonly,
+ callback_SQL_ATTR_IMP_ROW_DESC_set,
+ callback_SQL_ATTR_IMP_ROW_DESC_default,
+ },
+ { SQL_ATTR_KEYSET_SIZE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_KEYSET_SIZE_set,
+ callback_SQL_ATTR_KEYSET_SIZE_default,
+ },
+ { SQL_ATTR_MAX_LENGTH,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_MAX_LENGTH_set,
+ callback_SQL_ATTR_MAX_LENGTH_default,
+ },
+ { SQL_ATTR_MAX_ROWS,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_MAX_ROWS_set,
+ callback_SQL_ATTR_MAX_ROWS_default,
+ },
+ { SQL_ATTR_METADATA_ID,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_METADATA_ID_set,
+ callback_SQL_ATTR_METADATA_ID_default,
+ },
+ { SQL_ATTR_NOSCAN,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_NOSCAN_set,
+ callback_SQL_ATTR_NOSCAN_default,
+ },
+ { SQL_ATTR_PARAM_BIND_OFFSET_PTR,
+ OdbcData::UintegerPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PARAM_BIND_OFFSET_PTR_set,
+ callback_SQL_ATTR_PARAM_BIND_OFFSET_PTR_default,
+ },
+ { SQL_ATTR_PARAM_BIND_TYPE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PARAM_BIND_TYPE_set,
+ callback_SQL_ATTR_PARAM_BIND_TYPE_default,
+ },
+ { SQL_ATTR_PARAM_OPERATION_PTR,
+ OdbcData::UsmallintPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PARAM_OPERATION_PTR_set,
+ callback_SQL_ATTR_PARAM_OPERATION_PTR_default,
+ },
+ { SQL_ATTR_PARAM_STATUS_PTR,
+ OdbcData::UsmallintPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PARAM_STATUS_PTR_set,
+ callback_SQL_ATTR_PARAM_STATUS_PTR_default,
+ },
+ { SQL_ATTR_PARAMS_PROCESSED_PTR,
+ OdbcData::UintegerPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PARAMS_PROCESSED_PTR_set,
+ callback_SQL_ATTR_PARAMS_PROCESSED_PTR_default,
+ },
+ { SQL_ATTR_PARAMSET_SIZE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_PARAMSET_SIZE_set,
+ callback_SQL_ATTR_PARAMSET_SIZE_default,
+ },
+ { SQL_ATTR_QUERY_TIMEOUT,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_QUERY_TIMEOUT_set,
+ callback_SQL_ATTR_QUERY_TIMEOUT_default,
+ },
+ { SQL_ATTR_RETRIEVE_DATA,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_RETRIEVE_DATA_set,
+ callback_SQL_ATTR_RETRIEVE_DATA_default,
+ },
+ { SQL_ATTR_ROW_ARRAY_SIZE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ROW_ARRAY_SIZE_set,
+ callback_SQL_ATTR_ROW_ARRAY_SIZE_default,
+ },
+ { SQL_ATTR_ROW_BIND_OFFSET_PTR,
+ OdbcData::UintegerPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ROW_BIND_OFFSET_PTR_set,
+ callback_SQL_ATTR_ROW_BIND_OFFSET_PTR_default,
+ },
+ { SQL_ATTR_ROW_BIND_TYPE,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ROW_BIND_TYPE_set,
+ callback_SQL_ATTR_ROW_BIND_TYPE_default,
+ },
+ { SQL_ATTR_ROW_NUMBER,
+ OdbcData::Uinteger,
+ Attr_mode_readonly,
+ callback_SQL_ATTR_ROW_NUMBER_set,
+ callback_SQL_ATTR_ROW_NUMBER_default,
+ },
+ { SQL_ATTR_ROW_OPERATION_PTR,
+ OdbcData::UsmallintPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ROW_OPERATION_PTR_set,
+ callback_SQL_ATTR_ROW_OPERATION_PTR_default,
+ },
+ { SQL_ATTR_ROW_STATUS_PTR,
+ OdbcData::UsmallintPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ROW_STATUS_PTR_set,
+ callback_SQL_ATTR_ROW_STATUS_PTR_default,
+ },
+ { SQL_ATTR_ROWS_FETCHED_PTR,
+ OdbcData::UintegerPtr,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_ROWS_FETCHED_PTR_set,
+ callback_SQL_ATTR_ROWS_FETCHED_PTR_default,
+ },
+ { SQL_ATTR_SIMULATE_CURSOR,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_SIMULATE_CURSOR_set,
+ callback_SQL_ATTR_SIMULATE_CURSOR_default,
+ },
+ { SQL_ATTR_USE_BOOKMARKS,
+ OdbcData::Uinteger,
+ Attr_mode_readwrite,
+ callback_SQL_ATTR_USE_BOOKMARKS_set,
+ callback_SQL_ATTR_USE_BOOKMARKS_default,
+ },
+ // driver specific
+ { SQL_ATTR_NDB_TUPLES_FETCHED,
+ OdbcData::Uinteger,
+ Attr_mode_readonly,
+ callback_SQL_ATTR_NDB_TUPLES_FETCHED_set,
+ callback_SQL_ATTR_NDB_TUPLES_FETCHED_default,
+ },
+ { 0,
+ OdbcData::Undef,
+ Attr_mode_undef,
+ 0,
+ 0,
+ },
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/DescSpec.cpp b/storage/ndb/src/old_files/client/odbc/handles/DescSpec.cpp
new file mode 100644
index 00000000000..83905cf9822
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/DescSpec.cpp
@@ -0,0 +1,1140 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleDbc.hpp"
+#include "HandleDesc.hpp"
+
+static void
+callback_SQL_DESC_ALLOC_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_ALLOC_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_ARRAY_SIZE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Uinteger);
+}
+
+static void
+callback_SQL_DESC_ARRAY_SIZE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_ARRAY_STATUS_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::UsmallintPtr);
+}
+
+static void
+callback_SQL_DESC_ARRAY_STATUS_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_BIND_OFFSET_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::IntegerPtr);
+}
+
+static void
+callback_SQL_DESC_BIND_OFFSET_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_BIND_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_BIND_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_COUNT_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_COUNT_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_ROWS_PROCESSED_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::UintegerPtr);
+}
+
+static void
+callback_SQL_DESC_ROWS_PROCESSED_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_AUTO_UNIQUE_VALUE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_AUTO_UNIQUE_VALUE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_BASE_COLUMN_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_BASE_COLUMN_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_BASE_TABLE_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_BASE_TABLE_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_CASE_SENSITIVE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_CASE_SENSITIVE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_CATALOG_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_CATALOG_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_CONCISE_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_CONCISE_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_DATA_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Pointer);
+}
+
+static void
+callback_SQL_DESC_DATA_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_DATETIME_INTERVAL_CODE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_DATETIME_INTERVAL_CODE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_DATETIME_INTERVAL_PRECISION_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_DATETIME_INTERVAL_PRECISION_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_DISPLAY_SIZE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_DISPLAY_SIZE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_FIXED_PREC_SCALE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_FIXED_PREC_SCALE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_INDICATOR_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::IntegerPtr);
+}
+
+static void
+callback_SQL_DESC_INDICATOR_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_LABEL_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_LABEL_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_LENGTH_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Uinteger);
+}
+
+static void
+callback_SQL_DESC_LENGTH_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_LITERAL_PREFIX_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_LITERAL_PREFIX_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_LITERAL_SUFFIX_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_LITERAL_SUFFIX_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_LOCAL_TYPE_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_LOCAL_TYPE_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_NULLABLE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_NULLABLE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_NUM_PREC_RADIX_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_NUM_PREC_RADIX_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_OCTET_LENGTH_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Integer);
+}
+
+static void
+callback_SQL_DESC_OCTET_LENGTH_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_OCTET_LENGTH_PTR_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::IntegerPtr);
+}
+
+static void
+callback_SQL_DESC_OCTET_LENGTH_PTR_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_PARAMETER_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_PARAMETER_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_PRECISION_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_PRECISION_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_ROWVER_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_ROWVER_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_SCALE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_SCALE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_SCHEMA_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_SCHEMA_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_SEARCHABLE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_SEARCHABLE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_TABLE_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_TABLE_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_TYPE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_TYPE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_TYPE_NAME_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Sqlchar);
+}
+
+static void
+callback_SQL_DESC_TYPE_NAME_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_UNNAMED_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_UNNAMED_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_UNSIGNED_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_UNSIGNED_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+static void
+callback_SQL_DESC_UPDATABLE_set(Ctx& ctx, HandleBase* self, const OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0 && data.type() == OdbcData::Smallint);
+}
+
+static void
+callback_SQL_DESC_UPDATABLE_default(Ctx& ctx, HandleBase* self, OdbcData& data)
+{
+ HandleDesc* pDesc = static_cast<HandleDesc*>(self);
+ ctx_assert(pDesc != 0);
+ data.setValue();
+}
+
+DescSpec HandleDesc::m_descSpec[] = {
+ { Desc_pos_header,
+ SQL_DESC_ALLOC_TYPE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_readonly,
+ Desc_mode_readonly,
+ Desc_mode_readonly,
+ },
+ callback_SQL_DESC_ALLOC_TYPE_set,
+ callback_SQL_DESC_ALLOC_TYPE_default,
+ },
+ { Desc_pos_header,
+ SQL_DESC_ARRAY_SIZE,
+ OdbcData::Uinteger,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_ARRAY_SIZE_set,
+ callback_SQL_DESC_ARRAY_SIZE_default,
+ },
+ { Desc_pos_header,
+ SQL_DESC_ARRAY_STATUS_PTR,
+ OdbcData::UsmallintPtr,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_ARRAY_STATUS_PTR_set,
+ callback_SQL_DESC_ARRAY_STATUS_PTR_default,
+ },
+ { Desc_pos_header,
+ SQL_DESC_BIND_OFFSET_PTR,
+ OdbcData::IntegerPtr,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_BIND_OFFSET_PTR_set,
+ callback_SQL_DESC_BIND_OFFSET_PTR_default,
+ },
+ { Desc_pos_header,
+ SQL_DESC_BIND_TYPE,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_BIND_TYPE_set,
+ callback_SQL_DESC_BIND_TYPE_default,
+ },
+ { Desc_pos_header,
+ SQL_DESC_COUNT,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_COUNT_set,
+ callback_SQL_DESC_COUNT_default,
+ },
+ { Desc_pos_header,
+ SQL_DESC_ROWS_PROCESSED_PTR,
+ OdbcData::UintegerPtr,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_ROWS_PROCESSED_PTR_set,
+ callback_SQL_DESC_ROWS_PROCESSED_PTR_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_AUTO_UNIQUE_VALUE,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_AUTO_UNIQUE_VALUE_set,
+ callback_SQL_DESC_AUTO_UNIQUE_VALUE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_BASE_COLUMN_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_BASE_COLUMN_NAME_set,
+ callback_SQL_DESC_BASE_COLUMN_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_BASE_TABLE_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_BASE_TABLE_NAME_set,
+ callback_SQL_DESC_BASE_TABLE_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_CASE_SENSITIVE,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_CASE_SENSITIVE_set,
+ callback_SQL_DESC_CASE_SENSITIVE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_CATALOG_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_CATALOG_NAME_set,
+ callback_SQL_DESC_CATALOG_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_CONCISE_TYPE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_CONCISE_TYPE_set,
+ callback_SQL_DESC_CONCISE_TYPE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_DATA_PTR,
+ OdbcData::Pointer,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_DATA_PTR_set,
+ callback_SQL_DESC_DATA_PTR_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_DATETIME_INTERVAL_CODE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_DATETIME_INTERVAL_CODE_set,
+ callback_SQL_DESC_DATETIME_INTERVAL_CODE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_DATETIME_INTERVAL_PRECISION,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_DATETIME_INTERVAL_PRECISION_set,
+ callback_SQL_DESC_DATETIME_INTERVAL_PRECISION_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_DISPLAY_SIZE,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_DISPLAY_SIZE_set,
+ callback_SQL_DESC_DISPLAY_SIZE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_FIXED_PREC_SCALE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_FIXED_PREC_SCALE_set,
+ callback_SQL_DESC_FIXED_PREC_SCALE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_INDICATOR_PTR,
+ OdbcData::IntegerPtr,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_INDICATOR_PTR_set,
+ callback_SQL_DESC_INDICATOR_PTR_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_LABEL,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_LABEL_set,
+ callback_SQL_DESC_LABEL_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_LENGTH,
+ OdbcData::Uinteger,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_LENGTH_set,
+ callback_SQL_DESC_LENGTH_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_LITERAL_PREFIX,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_LITERAL_PREFIX_set,
+ callback_SQL_DESC_LITERAL_PREFIX_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_LITERAL_SUFFIX,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_LITERAL_SUFFIX_set,
+ callback_SQL_DESC_LITERAL_SUFFIX_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_LOCAL_TYPE_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_LOCAL_TYPE_NAME_set,
+ callback_SQL_DESC_LOCAL_TYPE_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_NAME_set,
+ callback_SQL_DESC_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_NULLABLE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_NULLABLE_set,
+ callback_SQL_DESC_NULLABLE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_NUM_PREC_RADIX,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_NUM_PREC_RADIX_set,
+ callback_SQL_DESC_NUM_PREC_RADIX_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_OCTET_LENGTH,
+ OdbcData::Integer,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_OCTET_LENGTH_set,
+ callback_SQL_DESC_OCTET_LENGTH_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_OCTET_LENGTH_PTR,
+ OdbcData::IntegerPtr,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_OCTET_LENGTH_PTR_set,
+ callback_SQL_DESC_OCTET_LENGTH_PTR_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_PARAMETER_TYPE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_PARAMETER_TYPE_set,
+ callback_SQL_DESC_PARAMETER_TYPE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_PRECISION,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_PRECISION_set,
+ callback_SQL_DESC_PRECISION_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_ROWVER,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_ROWVER_set,
+ callback_SQL_DESC_ROWVER_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_SCALE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_SCALE_set,
+ callback_SQL_DESC_SCALE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_SCHEMA_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_SCHEMA_NAME_set,
+ callback_SQL_DESC_SCHEMA_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_SEARCHABLE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_SEARCHABLE_set,
+ callback_SQL_DESC_SEARCHABLE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_TABLE_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_TABLE_NAME_set,
+ callback_SQL_DESC_TABLE_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_TYPE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_readwrite,
+ Desc_mode_readonly,
+ Desc_mode_readwrite
+ },
+ callback_SQL_DESC_TYPE_set,
+ callback_SQL_DESC_TYPE_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_TYPE_NAME,
+ OdbcData::Sqlchar,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_TYPE_NAME_set,
+ callback_SQL_DESC_TYPE_NAME_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_UNNAMED,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readwrite,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_UNNAMED_set,
+ callback_SQL_DESC_UNNAMED_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_UNSIGNED,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_readonly,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_UNSIGNED_set,
+ callback_SQL_DESC_UNSIGNED_default,
+ },
+ { Desc_pos_record,
+ SQL_DESC_UPDATABLE,
+ OdbcData::Smallint,
+ { Desc_mode_undef,
+ Desc_mode_unused,
+ Desc_mode_unused,
+ Desc_mode_readonly,
+ Desc_mode_unused
+ },
+ callback_SQL_DESC_UPDATABLE_set,
+ callback_SQL_DESC_UPDATABLE_default,
+ },
+ { Desc_pos_end,
+ 0,
+ OdbcData::Undef,
+ { Desc_mode_undef,
+ Desc_mode_undef,
+ Desc_mode_undef,
+ Desc_mode_undef,
+ Desc_mode_undef
+ },
+ 0,
+ 0
+ },
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/FuncTab.cpp b/storage/ndb/src/old_files/client/odbc/handles/FuncTab.cpp
new file mode 100644
index 00000000000..6bd744d7a7f
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/FuncTab.cpp
@@ -0,0 +1,100 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleDbc.hpp"
+
+HandleDbc::FuncTab
+HandleDbc::m_funcTab[] = {
+ { SQL_API_SQLALLOCCONNECT , 1 },
+ { SQL_API_SQLALLOCENV , 1 },
+ { SQL_API_SQLALLOCHANDLE , 1 },
+ { SQL_API_SQLALLOCHANDLESTD , 0 },
+ { SQL_API_SQLALLOCSTMT , 1 },
+ { SQL_API_SQLBINDCOL , 1 },
+ { SQL_API_SQLBINDPARAM , 1 },
+ { SQL_API_SQLBINDPARAMETER , 1 },
+ { SQL_API_SQLBROWSECONNECT , 0 },
+ { SQL_API_SQLBULKOPERATIONS , 0 },
+ { SQL_API_SQLCANCEL , 1 },
+ { SQL_API_SQLCLOSECURSOR , 1 },
+ { SQL_API_SQLCOLATTRIBUTE , 1 },
+ { SQL_API_SQLCOLATTRIBUTES , 1 },
+ { SQL_API_SQLCOLUMNPRIVILEGES , 0 },
+ { SQL_API_SQLCOLUMNS , 1 },
+ { SQL_API_SQLCONNECT , 1 },
+ { SQL_API_SQLCOPYDESC , 0 },
+ { SQL_API_SQLDATASOURCES , 0 },
+ { SQL_API_SQLDESCRIBECOL , 1 },
+ { SQL_API_SQLDESCRIBEPARAM , 0 },
+ { SQL_API_SQLDISCONNECT , 1 },
+ { SQL_API_SQLDRIVERCONNECT , 1 },
+ { SQL_API_SQLDRIVERS , 0 },
+ { SQL_API_SQLENDTRAN , 1 },
+ { SQL_API_SQLERROR , 1 },
+ { SQL_API_SQLEXECDIRECT , 1 },
+ { SQL_API_SQLEXECUTE , 1 },
+ { SQL_API_SQLEXTENDEDFETCH , 0 },
+ { SQL_API_SQLFETCH , 1 },
+ { SQL_API_SQLFETCHSCROLL , 0 },
+ { SQL_API_SQLFOREIGNKEYS , 0 },
+ { SQL_API_SQLFREECONNECT , 1 },
+ { SQL_API_SQLFREEENV , 1 },
+ { SQL_API_SQLFREEHANDLE , 1 },
+ { SQL_API_SQLFREESTMT , 1 },
+ { SQL_API_SQLGETCONNECTATTR , 1 },
+ { SQL_API_SQLGETCONNECTOPTION , 1 },
+ { SQL_API_SQLGETCURSORNAME , 1 },
+ { SQL_API_SQLGETDATA , 1 },
+ { SQL_API_SQLGETDESCFIELD , 1 },
+ { SQL_API_SQLGETDESCREC , 1 },
+ { SQL_API_SQLGETDIAGFIELD , 1 },
+ { SQL_API_SQLGETDIAGREC , 1 },
+ { SQL_API_SQLGETENVATTR , 1 },
+ { SQL_API_SQLGETFUNCTIONS , 1 },
+ { SQL_API_SQLGETINFO , 1 },
+ { SQL_API_SQLGETSTMTATTR , 1 },
+ { SQL_API_SQLGETSTMTOPTION , 1 },
+ { SQL_API_SQLGETTYPEINFO , 1 },
+ { SQL_API_SQLMORERESULTS , 1 },
+ { SQL_API_SQLNATIVESQL , 0 },
+ { SQL_API_SQLNUMPARAMS , 1 },
+ { SQL_API_SQLNUMRESULTCOLS , 1 },
+ { SQL_API_SQLPARAMDATA , 1 },
+ { SQL_API_SQLPARAMOPTIONS , 0 },
+ { SQL_API_SQLPREPARE , 1 },
+ { SQL_API_SQLPRIMARYKEYS , 1 },
+ { SQL_API_SQLPROCEDURECOLUMNS , 0 },
+ { SQL_API_SQLPROCEDURES , 0 },
+ { SQL_API_SQLPUTDATA , 1 },
+ { SQL_API_SQLROWCOUNT , 1 },
+ { SQL_API_SQLSETCONNECTATTR , 1 },
+ { SQL_API_SQLSETCONNECTOPTION , 1 },
+ { SQL_API_SQLSETCURSORNAME , 1 },
+ { SQL_API_SQLSETDESCFIELD , 1 },
+ { SQL_API_SQLSETDESCREC , 1 },
+ { SQL_API_SQLSETENVATTR , 1 },
+ { SQL_API_SQLSETPARAM , 1 },
+ { SQL_API_SQLSETPOS , 0 },
+ { SQL_API_SQLSETSCROLLOPTIONS , 0 },
+ { SQL_API_SQLSETSTMTATTR , 1 },
+ { SQL_API_SQLSETSTMTOPTION , 1 },
+ { SQL_API_SQLSPECIALCOLUMNS , 0 },
+ { SQL_API_SQLSTATISTICS , 0 },
+ { SQL_API_SQLTABLEPRIVILEGES , 0 },
+ { SQL_API_SQLTABLES , 1 },
+ { SQL_API_SQLTRANSACT , 1 },
+ { 0 , -1 }
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleBase.cpp b/storage/ndb/src/old_files/client/odbc/handles/HandleBase.cpp
new file mode 100644
index 00000000000..27379cdc3f8
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleBase.cpp
@@ -0,0 +1,162 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleBase.hpp"
+
+HandleBase::~HandleBase()
+{
+ delete m_ctx;
+ m_ctx = 0;
+}
+
+void
+HandleBase::saveCtx(Ctx& ctx)
+{
+ delete m_ctx;
+ m_ctx = &ctx;
+}
+
+// get diagnostics
+
+void
+HandleBase::sqlGetDiagField(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT diagIdentifier, SQLPOINTER diagInfo, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength)
+{
+ const DiagSpec& spec = DiagSpec::find(diagIdentifier);
+ if (spec.m_pos == Diag_pos_end) {
+ ctx.setCode(SQL_ERROR);
+ return;
+ }
+ const bool header = (spec.m_pos == Diag_pos_header);
+ const bool status = (spec.m_pos == Diag_pos_status);
+ ctx_assert(header || status);
+ if (! (spec.m_handles & odbcHandle())) {
+ ctx.setCode(SQL_ERROR);
+ return;
+ }
+ if (header) {
+ recNumber = 0; // ignored for header fields, so fix it
+ if (m_ctx == 0) {
+ if (diagIdentifier == SQL_DIAG_NUMBER) {
+ SQLINTEGER n = 0;
+ OdbcData data(n);
+ data.copyout(ctx, diagInfo, bufferLength, 0, stringLength);
+ return;
+ }
+ if (diagIdentifier == SQL_DIAG_RETURNCODE) {
+ SQLSMALLINT n = 0;
+ OdbcData data(n);
+ data.copyout(ctx, diagInfo, bufferLength, 0, stringLength);
+ return;
+ }
+ return;
+ }
+ }
+ if (status) {
+ if (recNumber <= 0) {
+ ctx.setCode(SQL_ERROR);
+ return;
+ }
+ if (m_ctx == 0) {
+ if ((unsigned)recNumber > 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ return;
+ }
+ if ((unsigned)recNumber > m_ctx->diagArea().numStatus()) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ }
+ OdbcData data;
+ ctx_assert(m_ctx != 0);
+ m_ctx->diagArea().getRecord(ctx, recNumber, diagIdentifier, data);
+ if (data.type() != OdbcData::Undef)
+ data.copyout(ctx, diagInfo, bufferLength, 0, stringLength);
+}
+
+void
+HandleBase::sqlGetDiagRec(Ctx& ctx, SQLSMALLINT recNumber, SQLCHAR* sqlstate, SQLINTEGER* nativeError, SQLCHAR* messageText, SQLSMALLINT bufferLength, SQLSMALLINT* textLength)
+{
+ sqlGetDiagField(ctx, recNumber, SQL_DIAG_SQLSTATE, static_cast<SQLPOINTER>(sqlstate), 5 + 1, 0);
+ sqlGetDiagField(ctx, recNumber, SQL_DIAG_NATIVE, static_cast<SQLPOINTER>(nativeError), -1, 0);
+ sqlGetDiagField(ctx, recNumber, SQL_DIAG_MESSAGE_TEXT, static_cast<SQLPOINTER>(messageText), bufferLength, textLength);
+}
+
+void
+HandleBase::sqlError(Ctx& ctx, SQLCHAR* sqlstate, SQLINTEGER* nativeError, SQLCHAR* messageText, SQLSMALLINT bufferLength, SQLSMALLINT* textLength)
+{
+ if (m_ctx == 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ const SQLSMALLINT recNumber = m_ctx->diagArea().nextRecNumber();
+ if (recNumber == 0) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ sqlGetDiagField(ctx, recNumber, SQL_DIAG_SQLSTATE, static_cast<SQLPOINTER>(sqlstate), 5 + 1, 0);
+ sqlGetDiagField(ctx, recNumber, SQL_DIAG_NATIVE, static_cast<SQLPOINTER>(nativeError), -1, 0);
+ sqlGetDiagField(ctx, recNumber, SQL_DIAG_MESSAGE_TEXT, static_cast<SQLPOINTER>(messageText), bufferLength, textLength);
+}
+
+// common code for attributes
+
+void
+HandleBase::baseSetHandleAttr(Ctx& ctx, AttrArea& attrArea, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength)
+{
+ const AttrSpec& spec = attrArea.findSpec(attribute);
+ if (spec.m_mode == Attr_mode_undef) { // not found
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "undefined attribute id %d", (int)attribute);
+ return;
+ }
+ if (spec.m_mode == Attr_mode_readonly) { // read only
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "read-only attribute id %d", (int)attribute);
+ return;
+ }
+ OdbcData data;
+ data.copyin(ctx, spec.m_type, value, stringLength);
+ if (! ctx.ok())
+ return;
+ attrArea.setAttr(ctx, attribute, data);
+}
+
+void
+HandleBase::baseGetHandleAttr(Ctx& ctx, AttrArea& attrArea, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength)
+{
+ const AttrSpec& spec = attrArea.findSpec(attribute);
+ if (spec.m_mode == Attr_mode_undef) { // not found
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "undefined attribute id %d", (int)attribute);
+ return;
+ }
+ OdbcData data;
+ attrArea.getAttr(ctx, attribute, data);
+ if (! ctx.ok())
+ return;
+ data.copyout(ctx, value, bufferLength, stringLength);
+}
+
+void
+HandleBase::baseSetHandleOption(Ctx& ctx, AttrArea& attrArea, SQLUSMALLINT option, SQLUINTEGER value)
+{
+ baseSetHandleAttr(ctx, attrArea, static_cast<SQLINTEGER>(option), reinterpret_cast<SQLPOINTER>(value), 0);
+}
+
+void
+HandleBase::baseGetHandleOption(Ctx& ctx, AttrArea& attrArea, SQLUSMALLINT option, SQLPOINTER value)
+{
+ baseGetHandleAttr(ctx, attrArea, static_cast<SQLINTEGER>(option), value, SQL_NTS, 0);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleBase.hpp b/storage/ndb/src/old_files/client/odbc/handles/HandleBase.hpp
new file mode 100644
index 00000000000..fc35c2b559b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleBase.hpp
@@ -0,0 +1,67 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_HandleBase_hpp
+#define ODBC_HANDLES_HandleBase_hpp
+
+#include <common/common.hpp>
+#include <common/OdbcData.hpp>
+#include <common/DiagArea.hpp>
+#include <common/AttrArea.hpp>
+
+/**
+ * @class HandleBase
+ * @brief Base class for handles
+ *
+ * Following types of handles exist:
+ * - HandleRoot : root node
+ * - HandleEnv : environment handle (SQLHENV)
+ * - HandleDbc : connection handle (SQLHDBC)
+ * - HandleStmt : statement handle (SQLHSTMT)
+ * - HandleDesc : descriptor handle (SQLHDESC)
+ */
+class HandleRoot;
+class HandleBase {
+public:
+ HandleBase();
+ virtual ~HandleBase() = 0;
+ virtual HandleBase* getParent() = 0;
+ virtual HandleRoot* getRoot() = 0;
+ virtual OdbcHandle odbcHandle() = 0;
+ void saveCtx(Ctx& ctx);
+ // allocate and free handles
+ virtual void sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild) = 0;
+ virtual void sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild) = 0;
+ // get diagnostics
+ void sqlGetDiagField(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT diagIdentifier, SQLPOINTER diagInfo, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength);
+ void sqlGetDiagRec(Ctx& ctx, SQLSMALLINT recNumber, SQLCHAR* sqlstate, SQLINTEGER* nativeError, SQLCHAR* messageText, SQLSMALLINT bufferLength, SQLSMALLINT* textLength);
+ void sqlError(Ctx& ctx, SQLCHAR* sqlstate, SQLINTEGER* nativeError, SQLCHAR* messageText, SQLSMALLINT bufferLength, SQLSMALLINT* textLength); // odbc2.0
+ // common code for attributes
+ void baseSetHandleAttr(Ctx& ctx, AttrArea& attrArea, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength);
+ void baseGetHandleAttr(Ctx& ctx, AttrArea& attrArea, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength);
+ void baseSetHandleOption(Ctx& ctx, AttrArea& attrArea, SQLUSMALLINT option, SQLUINTEGER value); // odbc2.0
+ void baseGetHandleOption(Ctx& ctx, AttrArea& attrArea, SQLUSMALLINT option, SQLPOINTER value); // odbc2.0
+protected:
+ Ctx* m_ctx; // saved from last ODBC function
+};
+
+inline
+HandleBase::HandleBase() :
+ m_ctx(0)
+{
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleDbc.cpp b/storage/ndb/src/old_files/client/odbc/handles/HandleDbc.cpp
new file mode 100644
index 00000000000..2d5ded2cc21
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleDbc.cpp
@@ -0,0 +1,419 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <limits.h>
+#include <NdbApi.hpp>
+#include <common/common.hpp>
+#include <common/DiagArea.hpp>
+#include <common/StmtArea.hpp>
+#include "HandleRoot.hpp"
+#include "HandleEnv.hpp"
+#include "HandleDbc.hpp"
+#include "HandleStmt.hpp"
+#include "HandleDesc.hpp"
+#include "PoolNdb.hpp"
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+HandleDbc::HandleDbc(HandleEnv* pEnv) :
+ m_env(pEnv),
+ m_attrArea(m_attrSpec)
+{
+ m_attrArea.setHandle(this);
+}
+
+HandleDbc::~HandleDbc()
+{
+}
+
+void
+HandleDbc::ctor(Ctx& ctx)
+{
+}
+
+void
+HandleDbc::dtor(Ctx& ctx)
+{
+ if (m_state == Connected) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "cannot delete connection handle - connection is open");
+ return;
+ }
+ if (m_state == Transacting) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "cannot delete connection handle - transaction is active");
+ return;
+ }
+}
+
+// allocate and free handles
+
+void
+HandleDbc::sqlAllocStmt(Ctx& ctx, HandleStmt** ppStmt)
+{
+ if (ppStmt == 0) {
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "cannot allocate statement handle - null return address");
+ return;
+ }
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_08003, Error::Gen, "cannot allocate statement handle - not connected to database");
+ return;
+ }
+ HandleStmt* pStmt = new HandleStmt(this);
+ pStmt->ctor(ctx);
+ if (! ctx.ok()) {
+ pStmt->dtor(ctx);
+ delete pStmt;
+ return;
+ }
+ m_listStmt.push_back(pStmt);
+ getRoot()->record(SQL_HANDLE_STMT, pStmt, true);
+ *ppStmt = pStmt;
+}
+
+void
+HandleDbc::sqlAllocDesc(Ctx& ctx, HandleDesc** ppDesc)
+{
+ if (ppDesc == 0) {
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "cannot allocate descriptor handle - null return address");
+ return;
+ }
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_08003, Error::Gen, "cannot allocate descriptor handle - not connected to database");
+ return;
+ }
+ HandleDesc* pDesc = new HandleDesc(this);
+ pDesc->ctor(ctx);
+ if (! ctx.ok()) {
+ pDesc->dtor(ctx);
+ delete pDesc;
+ return;
+ }
+ m_listDesc.push_back(pDesc);
+ getRoot()->record(SQL_HANDLE_DESC, pDesc, true);
+ *ppDesc = pDesc;
+}
+
+void
+HandleDbc::sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild)
+{
+ switch (childType) {
+ case SQL_HANDLE_STMT:
+ sqlAllocStmt(ctx, (HandleStmt**)ppChild);
+ return;
+ case SQL_HANDLE_DESC:
+ sqlAllocDesc(ctx, (HandleDesc**)ppChild);
+ return;
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid child handle type %d", (int)childType);
+}
+
+void
+HandleDbc::sqlFreeStmt(Ctx& ctx, HandleStmt* pStmt, SQLUSMALLINT iOption)
+{
+ switch (iOption) {
+ case SQL_CLOSE:
+ // no error if not open
+ if (pStmt->getState() == HandleStmt::Open)
+ pStmt->sqlCloseCursor(ctx);
+ return;
+ case SQL_DROP:
+ pStmt->dtor(ctx);
+ if (! ctx.ok())
+ return;
+ m_listStmt.remove(pStmt);
+ getRoot()->record(SQL_HANDLE_STMT, pStmt, false);
+ delete pStmt;
+ return;
+ case SQL_UNBIND: {
+ DescArea& ard = pStmt->getHandleDesc(ctx, Desc_usage_ARD)->descArea();
+ ard.setCount(ctx, 0);
+ return;
+ }
+ case SQL_RESET_PARAMS: {
+ DescArea& apd = pStmt->getHandleDesc(ctx, Desc_usage_APD)->descArea();
+ apd.setCount(ctx, 0);
+ // SQLFreeStmt doc misses this part
+ DescArea& ipd = pStmt->getHandleDesc(ctx, Desc_usage_IPD)->descArea();
+ ipd.setCount(ctx, 0);
+ return;
+ }
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid free statement option %u", (unsigned)iOption);
+}
+
+void
+HandleDbc::sqlFreeDesc(Ctx& ctx, HandleDesc* pDesc)
+{
+ pDesc->dtor(ctx);
+ if (! ctx.ok())
+ return;
+ m_listDesc.remove(pDesc);
+ getRoot()->record(SQL_HANDLE_DESC, pDesc, false);
+ delete pDesc;
+}
+
+void
+HandleDbc::sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild)
+{
+ switch (childType) {
+ case SQL_HANDLE_STMT:
+ sqlFreeStmt(ctx, (HandleStmt*)pChild, SQL_DROP);
+ return;
+ case SQL_HANDLE_DESC:
+ sqlFreeDesc(ctx, (HandleDesc*)pChild);
+ return;
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid child handle type %d", (int)childType);
+}
+
+// attributes and info functions
+
+static bool
+ignore_attr(Ctx& ctx, SQLINTEGER attribute)
+{
+ switch (attribute) {
+ case 1246:
+ ctx_log2(("ignore unknown ADO.NET connect attribute %d", (int)attribute));
+ return true;
+ }
+ return false;
+}
+
+void
+HandleDbc::sqlSetConnectAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength)
+{
+ if (ignore_attr(ctx, attribute))
+ return;
+ baseSetHandleAttr(ctx, m_attrArea, attribute, value, stringLength);
+}
+
+void
+HandleDbc::sqlGetConnectAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength)
+{
+ if (ignore_attr(ctx, attribute))
+ return;
+ baseGetHandleAttr(ctx, m_attrArea, attribute, value, bufferLength, stringLength);
+}
+
+void
+HandleDbc::sqlSetConnectOption(Ctx& ctx, SQLUSMALLINT option, SQLUINTEGER value)
+{
+ if (ignore_attr(ctx, option))
+ return;
+ baseSetHandleOption(ctx, m_attrArea, option, value);
+}
+
+void
+HandleDbc::sqlGetConnectOption(Ctx& ctx, SQLUSMALLINT option, SQLPOINTER value)
+{
+ if (ignore_attr(ctx, option))
+ return;
+ baseGetHandleOption(ctx, m_attrArea, option, value);
+}
+
+void
+HandleDbc::sqlGetFunctions(Ctx& ctx, SQLUSMALLINT functionId, SQLUSMALLINT* supported)
+{
+ if (functionId == SQL_API_ALL_FUNCTIONS) {
+ for (int i = 0; i < 100; i++)
+ supported[i] = SQL_FALSE;
+ FuncTab* f;
+ for (f = m_funcTab; f->m_supported != -1; f++) {
+ SQLUSMALLINT id = f->m_functionId;
+ if (id < 100 && f->m_supported)
+ supported[id] = SQL_TRUE;
+ }
+ } else if (functionId == SQL_API_ODBC3_ALL_FUNCTIONS) {
+ for (int i = 0; i < SQL_API_ODBC3_ALL_FUNCTIONS_SIZE; i++)
+ supported[i] = 0;
+ FuncTab* f;
+ for (f = m_funcTab; f->m_supported != -1; f++) {
+ SQLUSMALLINT id = f->m_functionId;
+ ctx_assert((id >> 4) < SQL_API_ODBC3_ALL_FUNCTIONS_SIZE);
+ if (f->m_supported)
+ supported[id >> 4] |= (1 << (id & 0xf));
+ }
+ } else {
+ FuncTab* f;
+ for (f = m_funcTab; f->m_supported != -1; f++) {
+ if (f->m_functionId == functionId)
+ break;
+ }
+ if (f->m_supported != -1)
+ supported[0] = f->m_supported ? SQL_TRUE : SQL_FALSE;
+ else
+ ctx.pushStatus(Sqlstate::_HY095, Error::Gen, "invalid function id %u", (unsigned)functionId);
+ }
+}
+
+void
+HandleDbc::sqlGetInfo(Ctx& ctx, SQLUSMALLINT infoType, SQLPOINTER infoValue, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength)
+{
+ InfoTab* f;
+ for (f = m_infoTab; f->m_format != InfoTab::End; f++) {
+ if (f->m_id == infoType)
+ break;
+ }
+ if (f->m_format == InfoTab::End) {
+ ctx.pushStatus(Sqlstate::_HY096, Error::Gen, "invalid info type %u", (unsigned)infoType);
+ return;
+ }
+ if (f->m_format == InfoTab::Char || f->m_format == InfoTab::YesNo) {
+ ctx_log3(("SQLGetInfo: type=%u value='%s'", (unsigned)infoType, f->m_str));
+ OdbcData data(f->m_str);
+ data.copyout(ctx, infoValue, bufferLength, 0, stringLength);
+ return;
+ }
+ if (f->m_format == InfoTab::Short) {
+ ctx_log3(("SQLGetInfo: type=%u value=%d", (unsigned)infoType, (int)f->m_int));
+ OdbcData data((SQLUSMALLINT)f->m_int);
+ data.copyout(ctx, infoValue, 0, 0);
+ return;
+ }
+ if (f->m_format == InfoTab::Long || f->m_format == InfoTab::Bitmask) {
+ ctx_log3(("SQLGetInfo: type=%u value=0x%x", (unsigned)infoType, (int)f->m_int));
+ OdbcData data((SQLUINTEGER)f->m_int);
+ data.copyout(ctx, infoValue, 0, 0);
+ return;
+ }
+ ctx_assert(false);
+}
+
+int
+HandleDbc::getOdbcVersion(Ctx& ctx)
+{
+ return m_env->getOdbcVersion(ctx);
+}
+
+// connect and transactions
+
+void
+HandleDbc::sqlConnect(Ctx& ctx, SQLCHAR* serverName, SQLSMALLINT nameLength1, SQLCHAR* userName, SQLSMALLINT nameLength2, SQLCHAR* authentication, SQLSMALLINT nameLength3)
+{
+ if (m_state != Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "already connected");
+ return;
+ }
+ OdbcData data;
+ m_attrArea.getAttr(ctx, SQL_ATTR_CONNECTION_TIMEOUT, data);
+ int timeout = data.uinteger();
+ if (timeout <= 0)
+ timeout = INT_MAX;
+ PoolNdb* poolNdb = getRoot()->getPoolNdb();
+ Ndb* pNdb = poolNdb->allocate(ctx, timeout);
+ if (pNdb == 0) {
+ return;
+ }
+ m_ndbObject = pNdb;
+ m_state = Connected;
+ m_autocommit = true;
+}
+
+void
+HandleDbc::sqlDriverConnect(Ctx& ctx, SQLHWND hwnd, SQLCHAR* szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR* szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT* pcbConnStrOut, SQLUSMALLINT fDriverCompletion)
+{
+ ctx_log2(("driver connect %.*s", cbConnStrIn, szConnStrIn == 0 ? "" : (char*)szConnStrIn));
+ sqlConnect(ctx, (SQLCHAR*)"", 0, (SQLCHAR*)"", 0, (SQLCHAR*)"", 0);
+ if (! ctx.ok())
+ return;
+ OdbcData data("DNS=DEFAULT");
+ if (szConnStrOut != 0) // ADO NET
+ data.copyout(ctx, static_cast<SQLPOINTER>(szConnStrOut), cbConnStrOutMax, 0, pcbConnStrOut);
+}
+
+void
+HandleDbc::sqlDisconnect(Ctx& ctx)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "already disconnected");
+ return;
+ }
+ // XXX missing check for uncommited changes
+ ListStmt::iterator i = m_listStmt.begin();
+ while (i != m_listStmt.end()) {
+ HandleStmt* pStmt = *i;
+ ListStmt::iterator j = i++;
+ pStmt->dtor(ctx);
+ getRoot()->record(SQL_HANDLE_STMT, pStmt, false);
+ delete pStmt;
+ m_listStmt.erase(j);
+ }
+ PoolNdb* poolNdb = getRoot()->getPoolNdb();
+ poolNdb->release(ctx, m_ndbObject);
+ m_ndbObject = 0;
+ m_state = Free;
+ m_autocommit = true;
+}
+
+void
+HandleDbc::sqlEndTran(Ctx& ctx, SQLSMALLINT completionType)
+{
+ if (completionType != SQL_COMMIT && completionType != SQL_ROLLBACK) {
+ ctx.pushStatus(Sqlstate::_HY012, Error::Gen, "invalid completion type %d", (int)completionType);
+ return;
+ }
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_08003, Error::Gen, "not connected");
+ return;
+ }
+ Ndb* ndb = m_ndbObject;
+ ctx_assert(ndb != 0);
+ if (m_state == Connected) {
+ ctx_log2(("sqlEndTran: no transaction active"));
+ return;
+ }
+ NdbConnection* tcon = m_ndbConnection;
+ ctx_assert(tcon != 0);
+ if (completionType == SQL_COMMIT) {
+ if (tcon->execute(Commit) == -1) {
+ if (tcon->getNdbError().code != 626)
+ ctx.pushStatus(ndb, tcon, 0, "execute commit");
+ else
+ ctx_log1(("ignore no data (626) at execute commit"));
+ } else {
+ ctx_log2(("sqlEndTran: transaction commit done"));
+ m_uncommitted = false;
+ }
+ } else {
+ if (tcon->execute(Rollback) == -1) {
+ if (tcon->getNdbError().code != 626)
+ ctx.pushStatus(ndb, tcon, 0, "execute rollback");
+ else
+ ctx_log1(("ignore no data (626) at execute rollback"));
+ } else {
+ ctx_log2(("sqlEndTran: transaction rollback done"));
+ m_uncommitted = false;
+ }
+ }
+ for (ListStmt::iterator i = m_listStmt.begin(); i != m_listStmt.end(); i++) {
+ HandleStmt* pStmt = *i;
+ if (pStmt->getState() == HandleStmt::Open) {
+ pStmt->sqlCloseCursor(ctx); // SQL_CB_CLOSE behaviour
+ }
+ pStmt->useConnection(ctx, false);
+ }
+ if (! m_autocommit) {
+ useConnection(ctx, false);
+ useConnection(ctx, true);
+ }
+}
+
+void
+HandleDbc::sqlTransact(Ctx& ctx, SQLUSMALLINT completionType)
+{
+ sqlEndTran(ctx, completionType);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleDbc.hpp b/storage/ndb/src/old_files/client/odbc/handles/HandleDbc.hpp
new file mode 100644
index 00000000000..130df08d02c
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleDbc.hpp
@@ -0,0 +1,111 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_HandleDbc_hpp
+#define ODBC_HANDLES_HandleDbc_hpp
+
+#include <list>
+#include <common/common.hpp>
+#include <common/ConnArea.hpp>
+#include "HandleBase.hpp"
+
+class HandleRoot;
+class HandleEnv;
+class HandleStmt;
+class HandleDesc;
+
+/**
+ * @class HandleDbc
+ * @brief Connection handle (SQLHDBC).
+ */
+class HandleDbc : public HandleBase, public ConnArea {
+public:
+ HandleDbc(HandleEnv* pEnv);
+ ~HandleDbc();
+ void ctor(Ctx& ctx);
+ void dtor(Ctx& ctx);
+ HandleEnv* getEnv();
+ HandleBase* getParent();
+ HandleRoot* getRoot();
+ OdbcHandle odbcHandle();
+ // allocate and free handles
+ void sqlAllocStmt(Ctx& ctx, HandleStmt** ppStmt);
+ void sqlAllocDesc(Ctx& ctx, HandleDesc** ppDesc);
+ void sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild);
+ void sqlFreeStmt(Ctx& ctx, HandleStmt* pStmt, SQLUSMALLINT iOption);
+ void sqlFreeDesc(Ctx& ctx, HandleDesc* pDesc);
+ void sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild);
+ // attributes and info functions
+ void sqlSetConnectAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength);
+ void sqlGetConnectAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength);
+ void sqlSetConnectOption(Ctx& ctx, SQLUSMALLINT option, SQLUINTEGER value); // odbc2.0
+ void sqlGetConnectOption(Ctx& ctx, SQLUSMALLINT option, SQLPOINTER value); // odbc2.0
+ void sqlGetFunctions(Ctx& ctx, SQLUSMALLINT functionId, SQLUSMALLINT* supported);
+ void sqlGetInfo(Ctx& ctx, SQLUSMALLINT infoType, SQLPOINTER infoValue, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength);
+ int getOdbcVersion(Ctx& ctx);
+ // connect and transactions
+ void sqlConnect(Ctx& ctx, SQLCHAR* serverName, SQLSMALLINT nameLength1, SQLCHAR* userName, SQLSMALLINT nameLength2, SQLCHAR* authentication, SQLSMALLINT nameLength3);
+ void sqlDriverConnect(Ctx& ctx, SQLHWND hwnd, SQLCHAR* szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR* szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT* pcbConnStrOut, SQLUSMALLINT fDriverCompletion);
+ void sqlDisconnect(Ctx& ctx);
+ void sqlEndTran(Ctx& ctx, SQLSMALLINT completionType);
+ void sqlTransact(Ctx& ctx, SQLUSMALLINT completionType); // odbc2.0
+private:
+ HandleEnv* const m_env;
+ typedef std::list<HandleStmt*> ListStmt;
+ ListStmt m_listStmt;
+ typedef std::list<HandleDesc*> ListDesc;
+ ListDesc m_listDesc;
+ static AttrSpec m_attrSpec[];
+ AttrArea m_attrArea;
+ struct FuncTab {
+ SQLUSMALLINT m_functionId;
+ int m_supported;
+ };
+ static FuncTab m_funcTab[];
+ struct InfoTab {
+ SQLUSMALLINT m_id;
+ enum { Char, YesNo, Short, Long, Bitmask, End } m_format;
+ SQLUINTEGER m_int;
+ const char* m_str;
+ };
+ static InfoTab m_infoTab[];
+};
+
+inline HandleEnv*
+HandleDbc::getEnv()
+{
+ return m_env;
+}
+
+inline HandleBase*
+HandleDbc::getParent()
+{
+ return (HandleBase*)getEnv();
+}
+
+inline HandleRoot*
+HandleDbc::getRoot()
+{
+ return getParent()->getRoot();
+}
+
+inline OdbcHandle
+HandleDbc::odbcHandle()
+{
+ return Odbc_handle_dbc;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleDesc.cpp b/storage/ndb/src/old_files/client/odbc/handles/HandleDesc.cpp
new file mode 100644
index 00000000000..4cff1bb8892
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleDesc.cpp
@@ -0,0 +1,254 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/OdbcData.hpp>
+#include <common/DiagArea.hpp>
+#include <common/DataType.hpp>
+#include "HandleRoot.hpp"
+#include "HandleDbc.hpp"
+#include "HandleDesc.hpp"
+
+HandleDesc::HandleDesc(HandleDbc* pDbc) :
+ m_dbc(pDbc),
+ m_descArea(this, m_descSpec)
+{
+}
+
+HandleDesc::~HandleDesc()
+{
+}
+
+void
+HandleDesc::ctor(Ctx& ctx)
+{
+}
+
+void
+HandleDesc::dtor(Ctx& ctx)
+{
+}
+
+// allocate and free handles (no valid case)
+
+void
+HandleDesc::sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild)
+{
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "inappropriate handle type");
+}
+
+void
+HandleDesc::sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* ppChild)
+{
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "inappropriate handle type");
+}
+
+// set and get descriptor values
+
+void
+HandleDesc::sqlSetDescField(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT fieldIdentifier, SQLPOINTER value, SQLINTEGER bufferLength)
+{
+ const DescSpec& spec = m_descArea.findSpec(fieldIdentifier);
+ if (spec.m_pos == Desc_pos_end) {
+ ctx.pushStatus(Sqlstate::_HY091, Error::Gen, "invalid descriptor id %d", (int)fieldIdentifier);
+ return;
+ }
+ OdbcData data;
+ data.copyin(ctx, spec.m_type, value, bufferLength);
+ if (! ctx.ok())
+ return;
+ const bool header = (spec.m_pos == Desc_pos_header);
+ const bool record = (spec.m_pos == Desc_pos_record);
+ ctx_assert(header || record);
+ DescArea& area = m_descArea;
+ if (header) {
+ area.getHeader().setField(ctx, fieldIdentifier, data);
+ }
+ if (record) {
+ if (recNumber < 0) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "invalid record number %d", (int)recNumber);
+ return;
+ }
+ if (recNumber == 0) { // bookmark record
+ if (area.getUsage() == Desc_usage_IPD) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "cannot set bookmark IPD");
+ return;
+ }
+ if (area.getUsage() == Desc_usage_APD) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "cannot set bookmark APD");
+ return;
+ }
+ }
+ area.getRecord(recNumber).setField(ctx, fieldIdentifier, data);
+ }
+}
+
+void
+HandleDesc::sqlGetDescField(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT fieldIdentifier, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength, SQLSMALLINT* stringLength2)
+{
+ const DescSpec& spec = m_descArea.findSpec(fieldIdentifier);
+ if (spec.m_pos == Desc_pos_end) {
+ ctx.pushStatus(Sqlstate::_HY091, Error::Gen, "invalid descriptor id %d", (int)fieldIdentifier);
+ return;
+ }
+ const bool header = (spec.m_pos == Desc_pos_header);
+ const bool record = (spec.m_pos == Desc_pos_record);
+ ctx_assert(header || record);
+ DescArea& area = m_descArea;
+ OdbcData data;
+ if (header) {
+ area.getHeader().getField(ctx, fieldIdentifier, data);
+ if (! ctx.ok())
+ return;
+ }
+ if (record) {
+ if (recNumber < 0) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "invalid record number %d", (int)recNumber);
+ return;
+ }
+ if (recNumber == 0) { // bookmark record
+ if (area.getUsage() == Desc_usage_IPD) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "cannot get bookmark IPD");
+ return;
+ }
+ if (area.getUsage() == Desc_usage_IRD) {
+ // XXX check SQL_ATTR_USE_BOOKMARK != SQL_UB_OFF
+ }
+ }
+ if ((unsigned)recNumber > area.getCount()) {
+ ctx.setCode(SQL_NO_DATA);
+ return;
+ }
+ area.getRecord(recNumber).getField(ctx, fieldIdentifier, data);
+ if (! ctx.ok())
+ return;
+ }
+ // if no data, return success and undefined value
+ if (data.type() == OdbcData::Undef)
+ return;
+ data.copyout(ctx, value, bufferLength, stringLength, stringLength2);
+}
+
+void
+HandleDesc::sqlColAttribute(Ctx& ctx, SQLUSMALLINT columnNumber, SQLUSMALLINT fieldIdentifier, SQLPOINTER characterAttribute, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength, SQLPOINTER numericAttribute)
+{
+ ctx_log3(("sqlColAttribute col=%d id=%d", columnNumber, fieldIdentifier));
+ if (fieldIdentifier == SQL_COLUMN_LENGTH) { // XXX iODBC workaround
+ fieldIdentifier = SQL_DESC_LENGTH;
+ }
+ if (fieldIdentifier == 1205 || fieldIdentifier == 1206) {
+ ctx_log2(("ignore unknown OSQL fieldIdentifier %d", (int)fieldIdentifier));
+ if (characterAttribute != 0)
+ *(char*)characterAttribute = 0;
+ if (stringLength != 0)
+ *stringLength = 0;
+ return;
+ }
+ const DescSpec& spec = m_descArea.findSpec(fieldIdentifier);
+ if (spec.m_pos == Desc_pos_end) {
+ ctx.pushStatus(Sqlstate::_HY091, Error::Gen, "invalid descriptor id %d", (int)fieldIdentifier);
+ return;
+ }
+ if (spec.m_type == OdbcData::Sqlchar || spec.m_type == OdbcData::Sqlstate)
+ sqlGetDescField(ctx, columnNumber, fieldIdentifier, characterAttribute, bufferLength, 0, stringLength);
+ else {
+ sqlGetDescField(ctx, columnNumber, fieldIdentifier, numericAttribute, -1, 0);
+ }
+ if (ctx.getCode() == SQL_NO_DATA) {
+ ctx.setCode(SQL_SUCCESS);
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "invalid column number %d", (int)columnNumber);
+ }
+}
+
+void
+HandleDesc::sqlColAttributes(Ctx& ctx, SQLUSMALLINT icol, SQLUSMALLINT fdescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT* pcbDesc, SQLINTEGER* pfDesc)
+{
+ ctx_log3(("sqlColAttributes col=%hu id=%hu", icol, fdescType));
+ SQLUSMALLINT columnNumber = icol;
+ SQLUSMALLINT fieldIdentifier;
+ // XXX incomplete
+ if (fdescType == SQL_COLUMN_TYPE) {
+ fieldIdentifier = SQL_DESC_TYPE;
+ } else if (fdescType == SQL_COLUMN_PRECISION) {
+ SQLSMALLINT type;
+ sqlGetDescField(ctx, columnNumber, SQL_DESC_TYPE, static_cast<SQLPOINTER>(&type), -1, 0);
+ if (! ctx.ok())
+ return;
+ switch (type) {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_LONGVARCHAR:
+ case SQL_LONGVARBINARY:
+ case SQL_DATE:
+ fieldIdentifier = SQL_DESC_LENGTH;
+ break;
+ default:
+ fieldIdentifier = SQL_DESC_PRECISION;
+ break;
+ }
+ } else if (fdescType == SQL_COLUMN_SCALE) {
+ SQLSMALLINT type;
+ sqlGetDescField(ctx, columnNumber, SQL_DESC_TYPE, static_cast<SQLPOINTER>(&type), -1, 0);
+ if (! ctx.ok())
+ return;
+ switch (type) {
+ default:
+ fieldIdentifier = SQL_DESC_SCALE;
+ break;
+ }
+ } else if (fdescType == SQL_COLUMN_LENGTH) {
+ SQLSMALLINT type;
+ sqlGetDescField(ctx, columnNumber, SQL_DESC_TYPE, static_cast<SQLPOINTER>(&type), -1, 0);
+ if (! ctx.ok())
+ return;
+ switch (type) {
+ default:
+ fieldIdentifier = SQL_DESC_LENGTH;
+ break;
+ }
+ } else {
+ fieldIdentifier = fdescType;
+ }
+ sqlColAttribute(ctx, columnNumber, fieldIdentifier, rgbDesc, cbDescMax, pcbDesc, pfDesc);
+}
+
+// set and get several common descriptor values
+
+void
+HandleDesc::sqlSetDescRec(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT type, SQLSMALLINT subType, SQLINTEGER length, SQLSMALLINT precision, SQLSMALLINT scale, SQLPOINTER data, SQLINTEGER* stringLength, SQLINTEGER* indicator)
+{
+ sqlSetDescField(ctx, recNumber, SQL_DESC_TYPE, reinterpret_cast<SQLPOINTER>(type), -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_DATETIME_INTERVAL_CODE, reinterpret_cast<SQLPOINTER>(subType), -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_OCTET_LENGTH, reinterpret_cast<SQLPOINTER>(length), -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_PRECISION, reinterpret_cast<SQLPOINTER>(precision), -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_SCALE, reinterpret_cast<SQLPOINTER>(scale), -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_DATA_PTR, data, -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_OCTET_LENGTH_PTR, reinterpret_cast<SQLPOINTER>(stringLength), -1);
+ sqlSetDescField(ctx, recNumber, SQL_DESC_INDICATOR_PTR, reinterpret_cast<SQLPOINTER>(indicator), -1);
+}
+
+void
+HandleDesc::sqlGetDescRec(Ctx& ctx, SQLSMALLINT recNumber, SQLCHAR* name, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength, SQLSMALLINT* type, SQLSMALLINT* subType, SQLINTEGER* length, SQLSMALLINT* precision, SQLSMALLINT* scale, SQLSMALLINT* nullable)
+{
+ sqlGetDescField(ctx, recNumber, SQL_DESC_NAME, reinterpret_cast<SQLPOINTER>(name), bufferLength, 0, stringLength);
+ sqlGetDescField(ctx, recNumber, SQL_DESC_TYPE, reinterpret_cast<SQLPOINTER>(type), -1, 0);
+ sqlGetDescField(ctx, recNumber, SQL_DESC_DATETIME_INTERVAL_CODE, reinterpret_cast<SQLPOINTER>(subType), -1, 0);
+ sqlGetDescField(ctx, recNumber, SQL_DESC_OCTET_LENGTH, reinterpret_cast<SQLPOINTER>(length), -1, 0);
+ sqlGetDescField(ctx, recNumber, SQL_DESC_PRECISION, reinterpret_cast<SQLPOINTER>(precision), -1, 0);
+ sqlGetDescField(ctx, recNumber, SQL_DESC_SCALE, reinterpret_cast<SQLPOINTER>(scale), -1, 0);
+ sqlGetDescField(ctx, recNumber, SQL_DESC_NULLABLE, reinterpret_cast<SQLPOINTER>(nullable), -1, 0);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleDesc.hpp b/storage/ndb/src/old_files/client/odbc/handles/HandleDesc.hpp
new file mode 100644
index 00000000000..9419697f134
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleDesc.hpp
@@ -0,0 +1,89 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_HandleDesc_hpp
+#define ODBC_HANDLES_HandleDesc_hpp
+
+#include <common/common.hpp>
+#include <common/DescArea.hpp>
+#include "HandleBase.hpp"
+
+class HandleRoot;
+class HandleDbc;
+
+/**
+ * @class HandleDesc
+ * @brief Descriptor handle (SQLHDESC).
+ */
+class HandleDesc : public HandleBase {
+public:
+ HandleDesc(HandleDbc* pDbc);
+ ~HandleDesc();
+ void ctor(Ctx& ctx);
+ void dtor(Ctx& ctx);
+ HandleDbc* getDbc();
+ HandleBase* getParent();
+ HandleRoot* getRoot();
+ OdbcHandle odbcHandle();
+ DescArea& descArea();
+ // allocate and free handles (no valid case)
+ void sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild);
+ void sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild);
+ // set and get descriptor values (internal use)
+ void sqlSetDescField(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT fieldIdentifier, SQLPOINTER value, SQLINTEGER bufferLength);
+ void sqlGetDescField(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT fieldIdentifier, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength, SQLSMALLINT* stringLength2 = 0);
+ void sqlColAttribute(Ctx& ctx, SQLUSMALLINT columnNumber, SQLUSMALLINT fieldIdentifier, SQLPOINTER characterAttribute, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength, SQLPOINTER numericAttribute);
+ void sqlColAttributes(Ctx& ctx, SQLUSMALLINT icol, SQLUSMALLINT fdescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT* pcbDesc, SQLINTEGER* pfDesc);
+ // set and get several common descriptor values
+ void sqlSetDescRec(Ctx& ctx, SQLSMALLINT recNumber, SQLSMALLINT Type, SQLSMALLINT SubType, SQLINTEGER Length, SQLSMALLINT Precision, SQLSMALLINT Scale, SQLPOINTER Data, SQLINTEGER* StringLength, SQLINTEGER* Indicator);
+ void sqlGetDescRec(Ctx& ctx, SQLSMALLINT recNumber, SQLCHAR* Name, SQLSMALLINT BufferLength, SQLSMALLINT* StringLength, SQLSMALLINT* Type, SQLSMALLINT* SubType, SQLINTEGER* Length, SQLSMALLINT* Precision, SQLSMALLINT* Scale, SQLSMALLINT* Nullable);
+private:
+ HandleDbc* const m_dbc;
+ static DescSpec m_descSpec[];
+ DescArea m_descArea;
+};
+
+inline HandleDbc*
+HandleDesc::getDbc()
+{
+ return m_dbc;
+}
+
+inline HandleBase*
+HandleDesc::getParent()
+{
+ return (HandleDbc*)getDbc();
+}
+
+inline HandleRoot*
+HandleDesc::getRoot()
+{
+ return getParent()->getRoot();
+}
+
+inline OdbcHandle
+HandleDesc::odbcHandle()
+{
+ return Odbc_handle_desc;
+}
+
+inline DescArea&
+HandleDesc::descArea()
+{
+ return m_descArea;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleEnv.cpp b/storage/ndb/src/old_files/client/odbc/handles/HandleEnv.cpp
new file mode 100644
index 00000000000..bc9d8b420a6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleEnv.cpp
@@ -0,0 +1,144 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/DiagArea.hpp>
+#include "HandleRoot.hpp"
+#include "HandleEnv.hpp"
+#include "HandleDbc.hpp"
+
+HandleEnv::HandleEnv(HandleRoot* pRoot) :
+ m_root(pRoot),
+ m_attrArea(m_attrSpec)
+{
+ m_attrArea.setHandle(this);
+}
+
+HandleEnv::~HandleEnv() {
+}
+
+void
+HandleEnv::ctor(Ctx& ctx)
+{
+}
+
+void
+HandleEnv::dtor(Ctx& ctx)
+{
+ if (! m_listDbc.empty()) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "cannot delete environment handle - has %u associated connection handles", (unsigned)m_listDbc.size());
+ return;
+ }
+}
+
+// allocate and free handles
+
+void
+HandleEnv::sqlAllocConnect(Ctx& ctx, HandleDbc** ppDbc)
+{
+ if (getOdbcVersion(ctx) == -1)
+ return;
+ if (ppDbc == 0) {
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "cannot allocate connection handle - null return address");
+ return;
+ }
+ HandleDbc* pDbc = new HandleDbc(this);
+ pDbc->ctor(ctx);
+ if (! ctx.ok()) {
+ pDbc->dtor(ctx);
+ delete pDbc;
+ return;
+ }
+ m_listDbc.push_back(pDbc);
+ getRoot()->record(SQL_HANDLE_DBC, pDbc, true);
+ *ppDbc = pDbc;
+}
+
+void
+HandleEnv::sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild)
+{
+ if (getOdbcVersion(ctx) == -1)
+ return;
+ switch (childType) {
+ case SQL_HANDLE_DBC:
+ sqlAllocConnect(ctx, (HandleDbc**)ppChild);
+ return;
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid child handle type %d", (int)childType);
+}
+
+void
+HandleEnv::sqlFreeConnect(Ctx& ctx, HandleDbc* pDbc)
+{
+ if (getOdbcVersion(ctx) == -1)
+ return;
+ pDbc->dtor(ctx);
+ if (! ctx.ok())
+ return;
+ m_listDbc.remove(pDbc);
+ getRoot()->record(SQL_HANDLE_DBC, pDbc, false);
+ delete pDbc;
+}
+
+void
+HandleEnv::sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild)
+{
+ if (getOdbcVersion(ctx) == -1)
+ return;
+ switch (childType) {
+ case SQL_HANDLE_DBC:
+ sqlFreeConnect(ctx, (HandleDbc*)pChild);
+ return;
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid child handle type %d", (int)childType);
+}
+
+// attributes
+
+void
+HandleEnv::sqlSetEnvAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength)
+{
+ baseSetHandleAttr(ctx, m_attrArea, attribute, value, stringLength);
+}
+
+void
+HandleEnv::sqlGetEnvAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength)
+{
+ baseGetHandleAttr(ctx, m_attrArea, attribute, value, bufferLength, stringLength);
+}
+
+int
+HandleEnv::getOdbcVersion(Ctx& ctx)
+{
+ OdbcData data;
+ m_attrArea.getAttr(ctx, SQL_ATTR_ODBC_VERSION, data);
+ if (! ctx.ok())
+ return -1;
+ return data.integer();
+}
+
+// transactions
+
+void
+HandleEnv::sqlEndTran(Ctx& ctx, SQLSMALLINT completionType)
+{
+ ctx_assert(false); // XXX
+}
+
+void
+HandleEnv::sqlTransact(Ctx& ctx, SQLUSMALLINT completionType)
+{
+ ctx_assert(false); // XXX
+}
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleEnv.hpp b/storage/ndb/src/old_files/client/odbc/handles/HandleEnv.hpp
new file mode 100644
index 00000000000..2b13b0256bc
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleEnv.hpp
@@ -0,0 +1,77 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_HandleEnv_hpp
+#define ODBC_HANDLES_HandleEnv_hpp
+
+#include <list>
+#include <common/common.hpp>
+#include "HandleBase.hpp"
+
+class HandleRoot;
+class HandleDbc;
+
+/**
+ * @class HandleEnv
+ * @brief Environment handle (SQLHENV).
+ */
+class HandleEnv : public HandleBase {
+public:
+ HandleEnv(HandleRoot* pRoot);
+ ~HandleEnv();
+ void ctor(Ctx& ctx);
+ void dtor(Ctx& ctx);
+ HandleRoot* getRoot();
+ HandleBase* getParent();
+ OdbcHandle odbcHandle();
+ // allocate and free handles
+ void sqlAllocConnect(Ctx& ctx, HandleDbc** ppDbc);
+ void sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild);
+ void sqlFreeConnect(Ctx& ctx, HandleDbc* pDbc);
+ void sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild);
+ // attributes
+ void sqlSetEnvAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength);
+ void sqlGetEnvAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength);
+ int getOdbcVersion(Ctx& ctx); // returns -1 if not set
+ // transactions
+ void sqlEndTran(Ctx& ctx, SQLSMALLINT completionType);
+ void sqlTransact(Ctx& ctx, SQLUSMALLINT completionType); // odbc2.0
+private:
+ HandleRoot* const m_root;
+ std::list<HandleDbc*> m_listDbc;
+ static AttrSpec m_attrSpec[];
+ AttrArea m_attrArea;
+};
+
+inline HandleRoot*
+HandleEnv::getRoot()
+{
+ return m_root;
+}
+
+inline HandleBase*
+HandleEnv::getParent()
+{
+ return (HandleBase*)getRoot();
+}
+
+inline OdbcHandle
+HandleEnv::odbcHandle()
+{
+ return Odbc_handle_env;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleRoot.cpp b/storage/ndb/src/old_files/client/odbc/handles/HandleRoot.cpp
new file mode 100644
index 00000000000..13560d55028
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleRoot.cpp
@@ -0,0 +1,271 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/common.hpp>
+#include <NdbMutex.h>
+#include <common/DiagArea.hpp>
+#include "HandleRoot.hpp"
+#include "HandleEnv.hpp"
+#include "HandleDbc.hpp"
+#include "HandleStmt.hpp"
+#include "HandleDesc.hpp"
+#include "PoolNdb.hpp"
+
+HandleRoot::HandleRoot() :
+ m_attrArea(m_attrSpec)
+{
+ m_attrArea.setHandle(this);
+ m_poolNdb = new PoolNdb();
+}
+
+HandleRoot::~HandleRoot()
+{
+}
+
+#ifdef NDB_WIN32
+static NdbMutex & root_mutex = * NdbMutex_Create();
+#else
+static NdbMutex root_mutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+HandleRoot*
+HandleRoot::instance()
+{
+ NdbMutex_Lock(&root_mutex);
+ if (m_instance == 0)
+ m_instance = new HandleRoot();
+ NdbMutex_Unlock(&root_mutex);
+ return m_instance;
+}
+
+void
+HandleRoot::lockHandle()
+{
+ NdbMutex_Lock(&root_mutex);
+}
+
+void
+HandleRoot::unlockHandle()
+{
+ NdbMutex_Unlock(&root_mutex);
+}
+
+// check and find handle types and handles
+
+SQLSMALLINT
+HandleRoot::findParentType(SQLSMALLINT childType)
+{
+ switch (childType) {
+ case SQL_HANDLE_ENV:
+ return SQL_HANDLE_ROOT;
+ case SQL_HANDLE_DBC:
+ return SQL_HANDLE_ENV;
+ case SQL_HANDLE_STMT:
+ return SQL_HANDLE_DBC;
+ case SQL_HANDLE_DESC:
+ return SQL_HANDLE_DBC;
+ }
+ return -1;
+}
+
+HandleBase*
+HandleRoot::findBase(SQLSMALLINT handleType, void* pHandle)
+{
+ switch (handleType) {
+ case SQL_HANDLE_ROOT:
+ return getRoot();
+ case SQL_HANDLE_ENV:
+ return findEnv(pHandle);
+ case SQL_HANDLE_DBC:
+ return findDbc(pHandle);
+ case SQL_HANDLE_STMT:
+ return findStmt(pHandle);
+ case SQL_HANDLE_DESC:
+ return findDesc(pHandle);
+ }
+ return 0;
+}
+
+HandleEnv*
+HandleRoot::findEnv(void* pHandle)
+{
+ lockHandle();
+ ValidList::iterator i = m_validList.find(pHandle);
+ if (i == m_validList.end() || (*i).second != SQL_HANDLE_ENV) {
+ unlockHandle();
+ return 0;
+ }
+ unlockHandle();
+ ctx_assert(pHandle != 0);
+ return static_cast<HandleEnv*>(pHandle);
+}
+
+HandleDbc*
+HandleRoot::findDbc(void* pHandle)
+{
+ lockHandle();
+ ValidList::iterator i = m_validList.find(pHandle);
+ if (i == m_validList.end() || (*i).second != SQL_HANDLE_DBC) {
+ unlockHandle();
+ return 0;
+ }
+ unlockHandle();
+ ctx_assert(pHandle != 0);
+ return static_cast<HandleDbc*>(pHandle);
+}
+
+HandleStmt*
+HandleRoot::findStmt(void* pHandle)
+{
+ lockHandle();
+ ValidList::iterator i = m_validList.find(pHandle);
+ if (i == m_validList.end() || (*i).second != SQL_HANDLE_STMT) {
+ unlockHandle();
+ return 0;
+ }
+ unlockHandle();
+ ctx_assert(pHandle != 0);
+ return static_cast<HandleStmt*>(pHandle);
+}
+
+HandleDesc*
+HandleRoot::findDesc(void* pHandle)
+{
+ lockHandle();
+ ValidList::iterator i = m_validList.find(pHandle);
+ if (i == m_validList.end() || (*i).second != SQL_HANDLE_DESC) {
+ unlockHandle();
+ return 0;
+ }
+ unlockHandle();
+ ctx_assert(pHandle != 0);
+ return static_cast<HandleDesc*>(pHandle);
+}
+
+// add or remove handle from validation list
+
+void
+HandleRoot::record(SQLSMALLINT handleType, HandleBase* pHandle, bool add)
+{
+ switch (handleType) {
+ case SQL_HANDLE_ENV:
+ case SQL_HANDLE_DBC:
+ case SQL_HANDLE_STMT:
+ case SQL_HANDLE_DESC:
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ ctx_assert(pHandle != 0);
+ lockHandle();
+ ValidList::iterator i = m_validList.find(pHandle);
+ if (add) {
+ if (i != m_validList.end()) {
+ unlockHandle();
+ ctx_assert(false);
+ }
+ m_validList.insert(ValidList::value_type(pHandle, handleType));
+ } else {
+ if (i == m_validList.end() || (*i).second != handleType) {
+ unlockHandle();
+ ctx_assert(false);
+ }
+ m_validList.erase(i);
+ }
+ unlockHandle();
+}
+
+// allocate and free handles
+
+void
+HandleRoot::sqlAllocEnv(Ctx& ctx, HandleEnv** ppEnv)
+{
+ if (ppEnv == 0) {
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "cannot allocate environment handle - null return address");
+ return;
+ }
+ HandleEnv* pEnv = new HandleEnv(this);
+ pEnv->ctor(ctx);
+ if (! ctx.ok()) {
+ pEnv->dtor(ctx);
+ delete pEnv;
+ return;
+ }
+ lockHandle();
+ m_listEnv.push_back(pEnv);
+ unlockHandle();
+ getRoot()->record(SQL_HANDLE_ENV, pEnv, true);
+ *ppEnv = pEnv;
+}
+
+void
+HandleRoot::sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild)
+{
+ switch (childType) {
+ case SQL_HANDLE_ENV:
+ sqlAllocEnv(ctx, (HandleEnv**)ppChild);
+ return;
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid child handle type %d", (int)childType);
+}
+
+void
+HandleRoot::sqlFreeEnv(Ctx& ctx, HandleEnv* pEnv)
+{
+ pEnv->dtor(ctx);
+ if (! ctx.ok()) {
+ return;
+ }
+ lockHandle();
+ m_listEnv.remove(pEnv);
+ unlockHandle();
+ getRoot()->record(SQL_HANDLE_ENV, pEnv, false);
+ delete pEnv;
+}
+
+void
+HandleRoot::sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild)
+{
+ switch (childType) {
+ case SQL_HANDLE_ENV:
+ sqlFreeEnv(ctx, (HandleEnv*)pChild);
+ return;
+ }
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "invalid child handle type %d", (int)childType);
+}
+
+// process-level attributes
+
+void
+HandleRoot::sqlSetRootAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength)
+{
+ lockHandle();
+ baseSetHandleAttr(ctx, m_attrArea, attribute, value, stringLength);
+ unlockHandle();
+}
+
+void
+HandleRoot::sqlGetRootAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength)
+{
+ lockHandle();
+ baseGetHandleAttr(ctx, m_attrArea, attribute, value, bufferLength, stringLength);
+ unlockHandle();
+}
+
+// the instance
+
+HandleRoot* HandleRoot::m_instance = 0;
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleRoot.hpp b/storage/ndb/src/old_files/client/odbc/handles/HandleRoot.hpp
new file mode 100644
index 00000000000..08a22b3e400
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleRoot.hpp
@@ -0,0 +1,103 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_HandleRoot_hpp
+#define ODBC_HANDLES_HandleRoot_hpp
+
+#include <list>
+#include <map>
+#include <common/common.hpp>
+#include "HandleBase.hpp"
+
+class HandleEnv;
+class HandleDbc;
+class HandleStmt;
+class HandleDesc;
+class PoolNdb;
+
+/**
+ * @class HandleRoot
+ * @brief The singleton root handle.
+ *
+ * This class is the level above HandleEnv. It has a single
+ * instance. The instance is the root node of dynamically
+ * allocated handles. The instance is also used to call methods
+ * not tied to any handle.
+ */
+class HandleRoot : public HandleBase {
+protected:
+ HandleRoot();
+ ~HandleRoot();
+public:
+ static HandleRoot* instance();
+ HandleRoot* getRoot();
+ HandleBase* getParent();
+ PoolNdb* getPoolNdb();
+ OdbcHandle odbcHandle();
+ void lockHandle();
+ void unlockHandle();
+ // check and find handle types and handles
+ SQLSMALLINT findParentType(SQLSMALLINT childType);
+ HandleBase* findBase(SQLSMALLINT handleType, void* pHandle);
+ HandleEnv* findEnv(void* pHandle);
+ HandleDbc* findDbc(void* pHandle);
+ HandleStmt* findStmt(void* pHandle);
+ HandleDesc* findDesc(void* pHandle);
+ // add or remove handle from validation list
+ void record(SQLSMALLINT handleType, HandleBase* pHandle, bool add);
+ // allocate and free handles
+ void sqlAllocEnv(Ctx& ctx, HandleEnv** ppEnv);
+ void sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild);
+ void sqlFreeEnv(Ctx& ctx, HandleEnv* pEnv);
+ void sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild);
+ // process-level attributes
+ void sqlSetRootAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength);
+ void sqlGetRootAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength);
+private:
+ static HandleRoot* m_instance; // the instance
+ std::list<HandleEnv*> m_listEnv;
+ PoolNdb* m_poolNdb;
+ typedef std::map<void*, SQLSMALLINT> ValidList;
+ ValidList m_validList;
+ static AttrSpec m_attrSpec[];
+ AttrArea m_attrArea;
+};
+
+inline HandleRoot*
+HandleRoot::getRoot()
+{
+ return this;
+}
+
+inline HandleBase*
+HandleRoot::getParent()
+{
+ return 0;
+}
+
+inline PoolNdb*
+HandleRoot::getPoolNdb()
+{
+ return m_poolNdb;
+}
+
+inline OdbcHandle
+HandleRoot::odbcHandle()
+{
+ return Odbc_handle_root;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp b/storage/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp
new file mode 100644
index 00000000000..d33d33dbd5b
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp
@@ -0,0 +1,823 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/OdbcData.hpp>
+#include <codegen/CodeGen.hpp>
+#include "HandleRoot.hpp"
+#include "HandleDbc.hpp"
+#include "HandleStmt.hpp"
+#include "HandleDesc.hpp"
+
+HandleStmt::HandleStmt(HandleDbc* pDbc) :
+ StmtArea(*pDbc),
+ m_dbc(pDbc),
+ m_attrArea(m_attrSpec)
+{
+ m_attrArea.setHandle(this);
+ for (unsigned i = 0; i <= 1; i++) {
+ for (unsigned u = 0; u <= 4; u++) {
+ m_handleDesc[i][u] = 0;
+ }
+ }
+}
+
+HandleStmt::~HandleStmt()
+{
+}
+
+void
+HandleStmt::ctor(Ctx& ctx)
+{
+ for (unsigned u = 1; u <= 4; u++) {
+ HandleDesc** ppDesc = &m_handleDesc[0][u];
+ m_dbc->sqlAllocDesc(ctx, ppDesc);
+ if (! ctx.ok())
+ return;
+ m_descArea[u] = &(*ppDesc)->descArea();
+ m_descArea[u]->setAlloc(Desc_alloc_auto);
+ m_descArea[u]->setUsage((DescUsage)u);
+ }
+}
+
+void
+HandleStmt::dtor(Ctx& ctx)
+{
+ free(ctx);
+ for (unsigned u = 1; u <= 4; u++) {
+ HandleDesc** ppDesc = &m_handleDesc[0][u];
+ if (*ppDesc != 0)
+ m_dbc->sqlFreeDesc(ctx, *ppDesc);
+ *ppDesc = 0;
+ }
+}
+
+// descriptor handles
+
+HandleDesc*
+HandleStmt::getHandleDesc(Ctx& ctx, DescUsage u) const
+{
+ ctx_assert(1 <= u && u <= 4);
+ if (m_handleDesc[1][u] != 0)
+ return m_handleDesc[1][u];
+ return m_handleDesc[0][u];
+}
+
+void
+HandleStmt::setHandleDesc(Ctx& ctx, DescUsage u, SQLPOINTER handle)
+{
+ ctx_assert(1 <= u && u <= 4);
+ if (handle == SQL_NULL_HDESC) {
+ m_handleDesc[1][u] = 0; // revert to implicit
+ m_descArea[u] = &m_handleDesc[0][u]->descArea();
+ return;
+ }
+ HandleDesc* pDesc = getRoot()->findDesc(handle);
+ if (pDesc == 0) {
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "cannot set %s handle to invalid descriptor handle %x", DescArea::nameUsage(u), (unsigned)handle);
+ return;
+ }
+ if (pDesc == m_handleDesc[0][u]) {
+ m_handleDesc[1][u] = 0; // revert to implicit
+ m_descArea[u] = &m_handleDesc[0][u]->descArea();
+ return;
+ }
+ // XXX should check implicit handles on all statements
+ for (unsigned v = 1; v <= 4; v++) {
+ if (pDesc == m_handleDesc[0][v]) {
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "cannot set %s handle to implicitly allocated %s handle", DescArea::nameUsage(u), DescArea::nameUsage((DescUsage)v));
+ return;
+ }
+ }
+ m_handleDesc[1][u] = pDesc;
+ m_descArea[u] = &m_handleDesc[1][u]->descArea();
+}
+
+// allocate and free handles (no valid case)
+
+void
+HandleStmt::sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild)
+{
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "inappropriate handle type");
+}
+
+void
+HandleStmt::sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* ppChild)
+{
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "inappropriate handle type");
+}
+
+// attributes and info
+
+static bool
+ignore_attr(Ctx& ctx, SQLINTEGER attribute)
+{
+ switch (attribute) {
+ case 1211:
+ case 1227:
+ case 1228:
+ ctx_log2(("ignore unknown ADO.NET stmt attribute %d", (int)attribute));
+ return true;
+ }
+ return false;
+}
+
+void
+HandleStmt::sqlSetStmtAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength)
+{
+ if (ignore_attr(ctx, attribute))
+ return;
+ baseSetHandleAttr(ctx, m_attrArea, attribute, value, stringLength);
+}
+
+void
+HandleStmt::sqlGetStmtAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength)
+{
+ if (ignore_attr(ctx, attribute))
+ return;
+ baseGetHandleAttr(ctx, m_attrArea, attribute, value, bufferLength, stringLength);
+}
+
+void
+HandleStmt::sqlSetStmtOption(Ctx& ctx, SQLUSMALLINT option, SQLUINTEGER value)
+{
+ if (ignore_attr(ctx, option))
+ return;
+ baseSetHandleOption(ctx, m_attrArea, option, value);
+}
+
+void
+HandleStmt::sqlGetStmtOption(Ctx& ctx, SQLUSMALLINT option, SQLPOINTER value)
+{
+ if (ignore_attr(ctx, option))
+ return;
+ baseGetHandleOption(ctx, m_attrArea, option, value);
+}
+
+void
+HandleStmt::sqlGetTypeInfo(Ctx& ctx, SQLSMALLINT dataType)
+{
+ BaseString text;
+ // odbc$typeinfo is a (possible unordered) view matching SQLGetTypeInfo result set
+ text.append("SELECT * FROM odbc$typeinfo");
+ if (dataType != SQL_ALL_TYPES) {
+ switch (dataType) {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIGINT:
+ case SQL_REAL:
+ case SQL_DOUBLE:
+ break;
+ default:
+ // XXX unsupported vs illegal
+ ctx_log1(("sqlGetTypeInfo: unknown data type %d", (int)dataType));
+ break;
+ }
+ text.appfmt(" WHERE data_type = %d", (int)dataType);
+ }
+ // sort signed before unsigned
+ text.append(" ORDER BY data_type, unsigned_attribute, type_name");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+void
+HandleStmt::sqlTables(Ctx& ctx, SQLCHAR* catalogName, SQLSMALLINT nameLength1, SQLCHAR* schemaName, SQLSMALLINT nameLength2, SQLCHAR* tableName, SQLSMALLINT nameLength3, SQLCHAR* tableType, SQLSMALLINT nameLength4)
+{
+ BaseString text;
+ // odbc$tables is a (possibly unordered) view matching SQLTables result set
+ text.append("SELECT * FROM odbc$tables");
+ SQLUINTEGER metadata_id = SQL_FALSE;
+ sqlGetStmtAttr(ctx, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, SQL_IS_POINTER, 0);
+ if (! ctx.ok())
+ return;
+ unsigned count = 0;
+ if (catalogName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)catalogName, nameLength1);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (getOdbcVersion(ctx) == 2)
+ text.appfmt(" table_cat = '%s'", data.sqlchar());
+ else if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_cat LIKE '%s'", data.sqlchar());
+ }
+ if (schemaName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)schemaName, nameLength2);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_schem LIKE '%s'", data.sqlchar());
+ }
+ if (tableName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)tableName, nameLength3);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_name LIKE '%s'", data.sqlchar());
+ }
+ if (tableType != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)tableType, nameLength4);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ text.appfmt(" table_type IN (%s)", data.sqlchar()); // XXX UPPER, quotes
+ }
+ text.append(" ORDER BY table_type, table_cat, table_schem, table_name");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+void
+HandleStmt::sqlColumns(Ctx& ctx, SQLCHAR* catalogName, SQLSMALLINT nameLength1, SQLCHAR* schemaName, SQLSMALLINT nameLength2, SQLCHAR* tableName, SQLSMALLINT nameLength3, SQLCHAR* columnName, SQLSMALLINT nameLength4)
+{
+ BaseString text;
+ // odbc$columns is a (possibly unordered) view matching SQLColumns result set
+ text.append("SELECT * FROM odbc$columns");
+ SQLUINTEGER metadata_id = SQL_FALSE;
+ sqlGetStmtAttr(ctx, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, SQL_IS_POINTER, 0);
+ if (! ctx.ok())
+ return;
+ unsigned count = 0;
+ if (catalogName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)catalogName, nameLength1);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (getOdbcVersion(ctx) == 2)
+ text.appfmt(" table_cat = '%s'", data.sqlchar());
+ else if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_cat LIKE '%s'", data.sqlchar());
+ }
+ if (schemaName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)schemaName, nameLength2);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_schem LIKE '%s'", data.sqlchar());
+ }
+ if (tableName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)tableName, nameLength3);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_name LIKE '%s'", data.sqlchar());
+ }
+ if (columnName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)columnName, nameLength4);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" column_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" column_name LIKE '%s'", data.sqlchar());
+ }
+ text.append(" ORDER BY table_cat, table_schem, table_name, ordinal_position");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+void
+HandleStmt::sqlPrimaryKeys(Ctx& ctx, SQLCHAR* szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR* szSchemaName, SQLSMALLINT cbSchemaName, SQLCHAR* szTableName, SQLSMALLINT cbTableName)
+{
+ BaseString text;
+ // odbc$primarykeys is a (possible unordered) view matching SQLPrimaryKeys result set
+ text.append("SELECT * FROM odbc$primarykeys");
+ SQLUINTEGER metadata_id = SQL_FALSE;
+ sqlGetStmtAttr(ctx, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, SQL_IS_POINTER, 0);
+ if (! ctx.ok())
+ return;
+ unsigned count = 0;
+ if (szCatalogName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)szCatalogName, cbCatalogName);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (getOdbcVersion(ctx) == 2)
+ text.appfmt(" table_cat = '%s'", data.sqlchar());
+ else if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // no pattern
+ }
+ if (szSchemaName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)szSchemaName, cbSchemaName);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // no pattern
+ }
+ if (szTableName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)szTableName, cbTableName);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // no pattern
+ } else { // no null
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "null table name");
+ return;
+ }
+ text.append(" ORDER BY table_cat, table_schem, table_name, key_seq");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+int
+HandleStmt::getOdbcVersion(Ctx& ctx)
+{
+ return m_dbc->getOdbcVersion(ctx);
+}
+
+// prepare and execute
+
+void
+HandleStmt::sqlPrepare(Ctx& ctx, SQLCHAR* statementText, SQLINTEGER textLength)
+{
+ if (m_state == Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is open");
+ return;
+ }
+ free(ctx);
+ const char* text = reinterpret_cast<char*>(statementText);
+ if (textLength == SQL_NTS) {
+ m_sqlText.assign(text);
+ } else if (textLength >= 0) {
+ m_sqlText.assign(text, textLength);
+ } else {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "missing SQL text");
+ return;
+ }
+ if (! useSchemaCon(ctx, true))
+ return;
+ CodeGen codegen(*this);
+ codegen.prepare(ctx);
+ useSchemaCon(ctx, false);
+ if (! ctx.ok()) {
+ free(ctx);
+ return;
+ }
+ ctx_log2(("prepared %s statement", m_stmtInfo.getDesc()));
+ m_state = Prepared;
+}
+
+void
+HandleStmt::sqlExecute(Ctx& ctx)
+{
+ if (m_state == Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is open");
+ return;
+ }
+ StmtType stmtType = m_stmtInfo.getType();
+ switch (stmtType) {
+ case Stmt_type_DDL:
+ if (! useSchemaCon(ctx, true))
+ return;
+ break;
+ case Stmt_type_query:
+ case Stmt_type_DML:
+ if (! useConnection(ctx, true))
+ return;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ CodeGen codegen(*this);
+ codegen.execute(ctx);
+ // valid only after execute says M$ XXX create this diag only on demand
+ setFunction(ctx);
+ if (ctx.getCode() == SQL_NEED_DATA) {
+ m_state = NeedData;
+ return;
+ }
+ ctx_log2(("execute: fetched %u tuples from NDB", (unsigned)m_tuplesFetched));
+ switch (stmtType) {
+ case Stmt_type_DDL:
+ codegen.close(ctx);
+ useSchemaCon(ctx, false);
+ m_state = Prepared;
+ break;
+ case Stmt_type_query:
+ if (! ctx.ok()) {
+ codegen.close(ctx);
+ useConnection(ctx, false);
+ m_state = Prepared;
+ } else {
+ m_state = Open;
+ }
+ break;
+ case Stmt_type_DML:
+ codegen.close(ctx);
+ if (m_dbc->autocommit()) {
+ // even if error
+ m_dbc->sqlEndTran(ctx, SQL_COMMIT);
+ } else {
+ m_dbc->uncommitted(true); // uncommitted changes possible
+ }
+ useConnection(ctx, false);
+ m_state = Prepared;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+HandleStmt::sqlExecDirect(Ctx& ctx, SQLCHAR* statementText, SQLINTEGER textLength)
+{
+ sqlPrepare(ctx, statementText, textLength);
+ if (! ctx.ok())
+ return;
+ sqlExecute(ctx);
+}
+
+void
+HandleStmt::sqlFetch(Ctx& ctx)
+{
+ if (m_state != Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is not open");
+ return;
+ }
+ StmtType stmtType = m_stmtInfo.getType();
+ switch (stmtType) {
+ case Stmt_type_query: {
+ CodeGen codegen(*this);
+ codegen.fetch(ctx);
+ if (! ctx.ok()) {
+ codegen.close(ctx);
+ useConnection(ctx, false);
+ }
+ break;
+ }
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+HandleStmt::sqlRowCount(Ctx& ctx, SQLINTEGER* rowCount)
+{
+ *rowCount = m_rowCount;
+}
+
+void
+HandleStmt::sqlMoreResults(Ctx& ctx)
+{
+ if (m_state == Open) {
+ sqlCloseCursor(ctx);
+ if (! ctx.ok())
+ return;
+ }
+ ctx.setCode(SQL_NO_DATA);
+}
+
+void
+HandleStmt::sqlCancel(Ctx& ctx)
+{
+ if (m_state == NeedData) {
+ CodeGen codegen(*this);
+ codegen.close(ctx);
+ m_state = Prepared;
+ }
+}
+
+void
+HandleStmt::sqlCloseCursor(Ctx& ctx)
+{
+ if (m_state != Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is not open");
+ return;
+ }
+ ctx_log2(("execute: fetched %u tuples from NDB", (unsigned)m_tuplesFetched));
+ StmtType stmtType = m_stmtInfo.getType();
+ switch (stmtType) {
+ case Stmt_type_query: {
+ CodeGen codegen(*this);
+ codegen.close(ctx);
+ useConnection(ctx, false);
+ m_state = Prepared;
+ m_rowCount = 0;
+ m_tuplesFetched = 0;
+ break;
+ }
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+HandleStmt::sqlGetCursorName(Ctx& ctx, SQLCHAR* cursorName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLength)
+{
+ OdbcData name("SQL_CUR_DUMMY");
+ name.copyout(ctx, cursorName, bufferLength, 0, nameLength);
+}
+
+void
+HandleStmt::sqlSetCursorName(Ctx& ctx, SQLCHAR* cursorName, SQLSMALLINT nameLength)
+{
+}
+
+// special data access
+
+void
+HandleStmt::sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ if (m_state != Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is not open");
+ return;
+ }
+ if (bufferLength < 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid buffer length %d", (int)bufferLength);
+ return;
+ }
+ CodeGen codegen(*this);
+ codegen.sqlGetData(ctx, columnNumber, targetType, targetValue, bufferLength, strlen_or_Ind);
+}
+
+void
+HandleStmt::sqlParamData(Ctx& ctx, SQLPOINTER* value)
+{
+ if (m_state != NeedData) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "not expecting data-at-exec");
+ return;
+ }
+ CodeGen codegen(*this);
+ codegen.sqlParamData(ctx, value);
+ if (! ctx.ok())
+ return;
+ sqlExecute(ctx);
+}
+
+void
+HandleStmt::sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind)
+{
+ if (m_state != NeedData) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "not expecting data-at-exec");
+ return;
+ }
+ CodeGen codegen(*this);
+ codegen.sqlPutData(ctx, data, strlen_or_Ind);
+}
+
+// describe statement
+
+void
+HandleStmt::sqlNumParams(Ctx& ctx, SQLSMALLINT* parameterCountPtr)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* ipd = getHandleDesc(ctx, Desc_usage_IPD);
+ ipd->sqlGetDescField(ctx, 0, SQL_DESC_COUNT, static_cast<SQLPOINTER>(parameterCountPtr), -1, 0);
+}
+
+void
+HandleStmt::sqlDescribeParam(Ctx& ctx, SQLUSMALLINT ipar, SQLSMALLINT* pfSqlType, SQLUINTEGER* pcbParamDef, SQLSMALLINT* pibScale, SQLSMALLINT* pfNullable)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* ipd = getHandleDesc(ctx, Desc_usage_IPD);
+ ipd->sqlGetDescField(ctx, ipar, SQL_DESC_CONCISE_TYPE, static_cast<SQLPOINTER>(pfSqlType), -1, 0);
+ { // XXX fix it
+ OdbcData data((SQLUINTEGER)0);
+ data.copyout(ctx, (SQLPOINTER)pcbParamDef, -1, 0);
+ }
+ { // XXX fix it
+ OdbcData data((SQLSMALLINT)0);
+ data.copyout(ctx, (SQLPOINTER)pibScale, -1, 0);
+ }
+ ipd->sqlGetDescField(ctx, ipar, SQL_DESC_NULLABLE, static_cast<SQLPOINTER>(pfNullable), -1, 0);
+}
+
+void
+HandleStmt::sqlNumResultCols(Ctx& ctx, SQLSMALLINT* columnCount)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlGetDescField(ctx, 0, SQL_DESC_COUNT, static_cast<SQLPOINTER>(columnCount), -1, 0);
+ setFunction(ctx); // unixODBC workaround
+}
+
+void
+HandleStmt::sqlDescribeCol(Ctx& ctx, SQLUSMALLINT columnNumber, SQLCHAR* columnName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLength, SQLSMALLINT* dataType, SQLUINTEGER* columnSize, SQLSMALLINT* decimalDigits, SQLSMALLINT* nullable)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_NAME, static_cast<SQLPOINTER>(columnName), bufferLength, 0, nameLength);
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_CONCISE_TYPE, static_cast<SQLPOINTER>(dataType), -1, 0);
+ if (! ctx.ok())
+ return;
+ if (columnSize != 0) {
+ switch (*dataType) {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_LENGTH, static_cast<SQLPOINTER>(columnSize), -1, 0);
+ break;
+ case SQL_SMALLINT:
+ *columnSize = 5;
+ break;
+ case SQL_INTEGER:
+ *columnSize = 10;
+ break;
+ case SQL_BIGINT:
+ *columnSize = 20; // XXX 19 for signed
+ break;
+ case SQL_REAL:
+ *columnSize = 7;
+ break;
+ case SQL_DOUBLE:
+ *columnSize = 15;
+ break;
+ case SQL_TYPE_TIMESTAMP:
+ *columnSize = 30;
+ break;
+ default:
+ *columnSize = 0;
+ break;
+ }
+ }
+ if (decimalDigits != 0) {
+ switch (*dataType) {
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIGINT:
+ *decimalDigits = 0;
+ break;
+ case SQL_TYPE_TIMESTAMP:
+ *decimalDigits = 10;
+ break;
+ default:
+ *decimalDigits = 0;
+ break;
+ }
+ }
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_NULLABLE, static_cast<SQLPOINTER>(nullable), -1, 0);
+}
+
+void
+HandleStmt::sqlColAttribute(Ctx& ctx, SQLUSMALLINT columnNumber, SQLUSMALLINT fieldIdentifier, SQLPOINTER characterAttribute, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength, SQLPOINTER numericAttribute)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlColAttribute(ctx, columnNumber, fieldIdentifier, characterAttribute, bufferLength, stringLength, numericAttribute);
+}
+
+void
+HandleStmt::sqlColAttributes(Ctx& ctx, SQLUSMALLINT icol, SQLUSMALLINT fdescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT* pcbDesc, SQLINTEGER* pfDesc)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is nor prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlColAttributes(ctx, icol, fdescType, rgbDesc, cbDescMax, pcbDesc, pfDesc);
+}
+
+// descriptor manipulation
+
+void
+HandleStmt::sqlBindCol(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ HandleDesc* const ard = getHandleDesc(ctx, Desc_usage_ARD);
+ DescArea& desc = ard->descArea();
+ if (desc.getCount() < columnNumber) {
+ desc.setCount(ctx, columnNumber);
+ }
+ DescRec& rec = desc.getRecord(columnNumber);
+ rec.setField(ctx, SQL_DESC_TYPE, targetType);
+ rec.setField(ctx, SQL_DESC_CONCISE_TYPE, targetType);
+ rec.setField(ctx, SQL_DESC_DATA_PTR, targetValue);
+ rec.setField(ctx, SQL_DESC_OCTET_LENGTH, bufferLength);
+ rec.setField(ctx, SQL_DESC_OCTET_LENGTH_PTR, strlen_or_Ind);
+ rec.setField(ctx, SQL_DESC_INDICATOR_PTR, strlen_or_Ind);
+}
+
+void
+HandleStmt::sqlBindParameter(Ctx& ctx, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLUINTEGER cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLINTEGER cbValueMax, SQLINTEGER* pcbValue)
+{
+ HandleDesc* const ipd = getHandleDesc(ctx, Desc_usage_IPD);
+ HandleDesc* const apd = getHandleDesc(ctx, Desc_usage_APD);
+ DescArea& descIPD = ipd->descArea();
+ DescArea& descAPD = apd->descArea();
+ if (ipar < 1) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "invalid parameter index %u", (unsigned)ipar);
+ return;
+ }
+ if (descIPD.getCount() < ipar) {
+ descIPD.setCount(ctx, ipar);
+ }
+ if (descAPD.getCount() < ipar) {
+ descAPD.setCount(ctx, ipar);
+ }
+ DescRec& recIPD = descIPD.getRecord(ipar);
+ DescRec& recAPD = descAPD.getRecord(ipar);
+ recIPD.setField(ctx, SQL_DESC_PARAMETER_TYPE, fParamType);
+ recAPD.setField(ctx, SQL_DESC_TYPE, fCType);
+ recAPD.setField(ctx, SQL_DESC_CONCISE_TYPE, fCType);
+ recIPD.setField(ctx, SQL_DESC_TYPE, fSqlType);
+ recIPD.setField(ctx, SQL_DESC_CONCISE_TYPE, fSqlType);
+ switch (fSqlType) {
+ case SQL_CHAR: // XXX not complete
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ recIPD.setField(ctx, SQL_DESC_LENGTH, cbColDef);
+ break;
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_FLOAT:
+ case SQL_REAL:
+ case SQL_DOUBLE:
+ recIPD.setField(ctx, SQL_DESC_PRECISION, cbColDef);
+ break;
+ }
+ switch (fSqlType) {
+ case SQL_TYPE_TIME: // XXX not complete
+ case SQL_TYPE_TIMESTAMP:
+ recIPD.setField(ctx, SQL_DESC_PRECISION, ibScale);
+ break;
+ case SQL_NUMERIC:
+ case SQL_DECIMAL:
+ recIPD.setField(ctx, SQL_DESC_SCALE, ibScale);
+ break;
+ }
+ recAPD.setField(ctx, SQL_DESC_DATA_PTR, rgbValue);
+ recAPD.setField(ctx, SQL_DESC_OCTET_LENGTH, cbValueMax);
+ recAPD.setField(ctx, SQL_DESC_OCTET_LENGTH_PTR, pcbValue);
+ recAPD.setField(ctx, SQL_DESC_INDICATOR_PTR, pcbValue);
+}
+
+void
+HandleStmt::sqlBindParam(Ctx& ctx, SQLUSMALLINT parameterNumber, SQLSMALLINT valueType, SQLSMALLINT parameterType, SQLUINTEGER lengthPrecision, SQLSMALLINT parameterScale, SQLPOINTER parameterValue, SQLINTEGER* strLen_or_Ind)
+{
+ sqlBindParameter(ctx, parameterNumber, SQL_PARAM_INPUT_OUTPUT, valueType, parameterType, lengthPrecision, parameterScale, parameterValue, SQL_SETPARAM_VALUE_MAX, strLen_or_Ind);
+}
+
+void
+HandleStmt::sqlSetParam(Ctx& ctx, SQLUSMALLINT parameterNumber, SQLSMALLINT valueType, SQLSMALLINT parameterType, SQLUINTEGER lengthPrecision, SQLSMALLINT parameterScale, SQLPOINTER parameterValue, SQLINTEGER* strLen_or_Ind)
+{
+ sqlBindParameter(ctx, parameterNumber, SQL_PARAM_INPUT_OUTPUT, valueType, parameterType, lengthPrecision, parameterScale, parameterValue, SQL_SETPARAM_VALUE_MAX, strLen_or_Ind);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/handles/HandleStmt.hpp b/storage/ndb/src/old_files/client/odbc/handles/HandleStmt.hpp
new file mode 100644
index 00000000000..0bee138bfc6
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/HandleStmt.hpp
@@ -0,0 +1,117 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_HandleStmt_hpp
+#define ODBC_HANDLES_HandleStmt_hpp
+
+#include <common/common.hpp>
+#include <common/DescArea.hpp>
+#include <common/StmtArea.hpp>
+#include "HandleBase.hpp"
+
+class HandleDbc;
+class HandleDesc;
+
+/**
+ * @class HandleStmt
+ * @brief Statement handle (SQLHSTMT).
+ */
+class HandleStmt : public HandleBase, public StmtArea {
+public:
+ HandleStmt(HandleDbc* pDbc);
+ ~HandleStmt();
+ void ctor(Ctx& ctx);
+ void dtor(Ctx& ctx);
+ HandleDbc* getDbc();
+ HandleBase* getParent();
+ HandleRoot* getRoot();
+ OdbcHandle odbcHandle();
+ // descriptor handles
+ HandleDesc* getHandleDesc(Ctx& ctx, DescUsage u) const;
+ void setHandleDesc(Ctx& ctx, DescUsage u, SQLPOINTER handle);
+ // allocate and free handles (no valid case)
+ void sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild);
+ void sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* pChild);
+ // attributes and info
+ void sqlSetStmtAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength);
+ void sqlGetStmtAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength);
+ void sqlSetStmtOption(Ctx& ctx, SQLUSMALLINT option, SQLUINTEGER value); // odbc2.0
+ void sqlGetStmtOption(Ctx& ctx, SQLUSMALLINT option, SQLPOINTER value); // odbc2.0
+ void sqlGetTypeInfo(Ctx& ctx, SQLSMALLINT dataType);
+ void sqlTables(Ctx& ctx, SQLCHAR* catalogName, SQLSMALLINT nameLength1, SQLCHAR* schemaName, SQLSMALLINT nameLength2, SQLCHAR* tableName, SQLSMALLINT nameLength3, SQLCHAR* tableType, SQLSMALLINT nameLength4);
+ void sqlColumns(Ctx& ctx, SQLCHAR* catalogName, SQLSMALLINT nameLength1, SQLCHAR* schemaName, SQLSMALLINT nameLength2, SQLCHAR* tableName, SQLSMALLINT nameLength3, SQLCHAR* columnName, SQLSMALLINT nameLength4);
+ void sqlPrimaryKeys(Ctx& ctx, SQLCHAR* szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR* szSchemaName, SQLSMALLINT cbSchemaName, SQLCHAR* szTableName, SQLSMALLINT cbTableName);
+ int getOdbcVersion(Ctx& ctx);
+ // prepare and execute
+ void sqlPrepare(Ctx& ctx, SQLCHAR* statementText, SQLINTEGER textLength);
+ void sqlExecute(Ctx& ctx);
+ void sqlExecDirect(Ctx& ctx, SQLCHAR* statementText, SQLINTEGER textLength);
+ void sqlFetch(Ctx& ctx);
+ void sqlRowCount(Ctx& ctx, SQLINTEGER* rowCount);
+ void sqlMoreResults(Ctx& ctx);
+ void sqlCancel(Ctx& ctx);
+ void sqlCloseCursor(Ctx& ctx);
+ void sqlGetCursorName(Ctx& ctx, SQLCHAR* cursorName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLength);
+ void sqlSetCursorName(Ctx& ctx, SQLCHAR* cursorName, SQLSMALLINT nameLength);
+ // special data access
+ void sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind);
+ void sqlParamData(Ctx& ctx, SQLPOINTER* value);
+ void sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind);
+ // describe statement
+ void sqlNumParams(Ctx& ctx, SQLSMALLINT* ParameterCountPtr);
+ void sqlDescribeParam(Ctx& ctx, SQLUSMALLINT ipar, SQLSMALLINT* pfSqlType, SQLUINTEGER* pcbParamDef, SQLSMALLINT* pibScale, SQLSMALLINT* pfNullable);
+ void sqlNumResultCols(Ctx& ctx, SQLSMALLINT* columnCount);
+ void sqlDescribeCol(Ctx& ctx, SQLUSMALLINT columnNumber, SQLCHAR* columnName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLength, SQLSMALLINT* dataType, SQLUINTEGER* columnSize, SQLSMALLINT* decimalDigits, SQLSMALLINT* nullable);
+ void sqlColAttribute(Ctx& ctx, SQLUSMALLINT columnNumber, SQLUSMALLINT fieldIdentifier, SQLPOINTER characterAttribute, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength, SQLPOINTER numericAttribute);
+ void sqlColAttributes(Ctx& ctx, SQLUSMALLINT icol, SQLUSMALLINT fdescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT* pcbDesc, SQLINTEGER* pfDesc); // odbc2.0
+ // descriptor manipulation
+ void sqlBindCol(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind);
+ void sqlBindParameter(Ctx& ctx, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLUINTEGER cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLINTEGER cbValueMax, SQLINTEGER* pcbValue);
+ void sqlBindParam(Ctx& ctx, SQLUSMALLINT parameterNumber, SQLSMALLINT valueType, SQLSMALLINT parameterType, SQLUINTEGER lengthPrecision, SQLSMALLINT parameterScale, SQLPOINTER parameterValue, SQLINTEGER* strLen_or_Ind);
+ void sqlSetParam(Ctx& ctx, SQLUSMALLINT parameterNumber, SQLSMALLINT valueType, SQLSMALLINT parameterType, SQLUINTEGER lengthPrecision, SQLSMALLINT parameterScale, SQLPOINTER parameterValue, SQLINTEGER* strLen_or_Ind);
+private:
+ HandleDbc* const m_dbc;
+ static AttrSpec m_attrSpec[];
+ AttrArea m_attrArea;
+ // descriptor handles (0-automatic 1-application)
+ HandleDesc* m_handleDesc[2][1+4];
+};
+
+inline HandleDbc*
+HandleStmt::getDbc()
+{
+ return m_dbc;
+}
+
+inline HandleBase*
+HandleStmt::getParent()
+{
+ return (HandleBase*)getDbc();
+}
+
+inline HandleRoot*
+HandleStmt::getRoot()
+{
+ return getParent()->getRoot();
+}
+
+inline OdbcHandle
+HandleStmt::odbcHandle()
+{
+ return Odbc_handle_stmt;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/InfoTab.cpp b/storage/ndb/src/old_files/client/odbc/handles/InfoTab.cpp
new file mode 100644
index 00000000000..1a93c4da264
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/InfoTab.cpp
@@ -0,0 +1,878 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "HandleDbc.hpp"
+
+HandleDbc::InfoTab
+HandleDbc::m_infoTab[] = {
+ { SQL_ACCESSIBLE_PROCEDURES,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_ACCESSIBLE_TABLES,
+ InfoTab::YesNo,
+ 0L,
+ "Y"
+ },
+ { SQL_ACTIVE_ENVIRONMENTS,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_AGGREGATE_FUNCTIONS,
+ InfoTab::Bitmask,
+ SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM,
+ 0
+ },
+ { SQL_ALTER_DOMAIN,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_ALTER_TABLE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_ASYNC_MODE,
+ InfoTab::Long,
+ SQL_AM_NONE,
+ 0
+ },
+ { SQL_BATCH_ROW_COUNT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_BATCH_SUPPORT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_BOOKMARK_PERSISTENCE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CATALOG_LOCATION,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_CATALOG_NAME,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_CATALOG_NAME_SEPARATOR,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_CATALOG_TERM,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_CATALOG_USAGE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_COLLATION_SEQ,
+ InfoTab::Char,
+ 0L,
+ "ISO 8859-1"
+ },
+ { SQL_COLUMN_ALIAS,
+ InfoTab::YesNo,
+ 0L,
+ "Y"
+ },
+ { SQL_CONCAT_NULL_BEHAVIOR,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_BIGINT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_BINARY,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_BIT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_CHAR,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_DATE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_DECIMAL,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_DOUBLE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_FLOAT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+#if 0
+ { SQL_CONVERT_GUID,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+#endif
+ { SQL_CONVERT_INTEGER,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_INTERVAL_DAY_TIME,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_INTERVAL_YEAR_MONTH,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_LONGVARBINARY,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_LONGVARCHAR,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_NUMERIC,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_REAL,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_SMALLINT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_TIME,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_TIMESTAMP,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_TINYINT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_VARBINARY,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CONVERT_VARCHAR,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CORRELATION_NAME,
+ InfoTab::Bitmask,
+ SQL_CN_ANY,
+ 0
+ },
+ { SQL_CREATE_ASSERTION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CREATE_CHARACTER_SET,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CREATE_COLLATION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CREATE_DOMAIN,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CREATE_SCHEMA,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CREATE_TABLE,
+ InfoTab::Bitmask,
+ SQL_CT_CREATE_TABLE,
+ 0
+ },
+ { SQL_CREATE_TRANSLATION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CREATE_VIEW,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_CURSOR_COMMIT_BEHAVIOR,
+ InfoTab::Short,
+ SQL_CB_CLOSE,
+ 0
+ },
+ { SQL_CURSOR_ROLLBACK_BEHAVIOR,
+ InfoTab::Short,
+ SQL_CB_CLOSE,
+ 0
+ },
+ { SQL_CURSOR_SENSITIVITY,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_DATABASE_NAME,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_DATA_SOURCE_NAME,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_DATA_SOURCE_READ_ONLY,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_DATETIME_LITERALS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DBMS_NAME,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_DBMS_VER,
+ InfoTab::Char,
+ 0L,
+ "01.43.0000"
+ },
+ { SQL_DDL_INDEX,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_DEFAULT_TXN_ISOLATION,
+ InfoTab::Long,
+ SQL_TXN_READ_COMMITTED,
+ 0
+ },
+ { SQL_DESCRIBE_PARAMETER,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_DM_VER,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_DRIVER_HDBC,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_DRIVER_HDESC,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_DRIVER_HLIB,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_DRIVER_HSTMT,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_DRIVER_NAME,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_DRIVER_ODBC_VER,
+ InfoTab::Char,
+ 0L,
+ "03.00"
+ },
+ { SQL_DRIVER_VER,
+ InfoTab::Char,
+ 0L,
+ "00.10.0000"
+ },
+ { SQL_DROP_ASSERTION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_CHARACTER_SET,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_COLLATION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_DOMAIN,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_SCHEMA,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_TABLE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_TRANSLATION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DROP_VIEW,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DTC_TRANSITION_COST, // not in older MS docs
+ InfoTab::Bitmask,
+ 0L,
+ 0 // SQL_DTC_ENLIST_EXPENSIVE | SQL_DTC_UNENLIST_EXPENSIVE
+ },
+ { SQL_DYNAMIC_CURSOR_ATTRIBUTES1,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_DYNAMIC_CURSOR_ATTRIBUTES2,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_EXPRESSIONS_IN_ORDERBY,
+ InfoTab::Char,
+ 0L,
+ "Y"
+ },
+ { SQL_FILE_USAGE,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_GETDATA_EXTENSIONS,
+ InfoTab::Bitmask,
+ SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND,
+ 0
+ },
+ { SQL_GROUP_BY,
+ InfoTab::Short,
+ SQL_GB_NOT_SUPPORTED,
+ 0
+ },
+ { SQL_IDENTIFIER_CASE,
+ InfoTab::Short,
+ SQL_IC_UPPER,
+ 0
+ },
+ { SQL_IDENTIFIER_QUOTE_CHAR,
+ InfoTab::Char,
+ 0L,
+ "\""
+ },
+ { SQL_INDEX_KEYWORDS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_INFO_SCHEMA_VIEWS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_INSERT_STATEMENT,
+ InfoTab::Bitmask,
+ SQL_IS_INSERT_LITERALS | SQL_IS_SELECT_INTO,
+ 0
+ },
+ { SQL_INTEGRITY,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_KEYSET_CURSOR_ATTRIBUTES1,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_KEYSET_CURSOR_ATTRIBUTES2,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_KEYWORDS,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_LIKE_ESCAPE_CLAUSE,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_MAX_ASYNC_CONCURRENT_STATEMENTS,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_MAX_BINARY_LITERAL_LEN,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_MAX_CATALOG_NAME_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_CHAR_LITERAL_LEN,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_MAX_COLUMN_NAME_LEN,
+ InfoTab::Short,
+ 16,
+ 0
+ },
+ { SQL_MAX_COLUMNS_IN_GROUP_BY,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_COLUMNS_IN_INDEX,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_COLUMNS_IN_ORDER_BY,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_COLUMNS_IN_SELECT,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_COLUMNS_IN_TABLE,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_CONCURRENT_ACTIVITIES,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_CURSOR_NAME_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_DRIVER_CONNECTIONS,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_IDENTIFIER_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_INDEX_SIZE,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_MAX_PROCEDURE_NAME_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_ROW_SIZE,
+ InfoTab::Long,
+ 8000,
+ 0
+ },
+ { SQL_MAX_ROW_SIZE_INCLUDES_LONG,
+ InfoTab::YesNo,
+ 0L,
+ "Y"
+ },
+ { SQL_MAX_SCHEMA_NAME_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_STATEMENT_LEN,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_MAX_TABLE_NAME_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_TABLES_IN_SELECT,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MAX_USER_NAME_LEN,
+ InfoTab::Short,
+ 0L,
+ 0
+ },
+ { SQL_MULTIPLE_ACTIVE_TXN,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_MULT_RESULT_SETS,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_NEED_LONG_DATA_LEN,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_NON_NULLABLE_COLUMNS,
+ InfoTab::Short,
+ SQL_NNC_NON_NULL,
+ 0
+ },
+ { SQL_NULL_COLLATION,
+ InfoTab::Short,
+ SQL_NC_HIGH,
+ 0
+ },
+ { SQL_NUMERIC_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_ODBC_INTERFACE_CONFORMANCE,
+ InfoTab::Long,
+ SQL_OIC_CORE,
+ 0
+ },
+ { SQL_ODBC_VER,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_OJ_CAPABILITIES,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_ORDER_BY_COLUMNS_IN_SELECT,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_PARAM_ARRAY_ROW_COUNTS,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_PARAM_ARRAY_SELECTS,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_POS_OPERATIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_PROCEDURES,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_PROCEDURE_TERM,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_QUOTED_IDENTIFIER_CASE,
+ InfoTab::Short,
+ SQL_IC_SENSITIVE,
+ 0
+ },
+ { SQL_ROW_UPDATES,
+ InfoTab::YesNo,
+ 0L,
+ "N"
+ },
+ { SQL_SCHEMA_TERM,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_SCHEMA_USAGE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SCROLL_OPTIONS,
+ InfoTab::Bitmask,
+ SQL_SO_FORWARD_ONLY,
+ 0
+ },
+ { SQL_SEARCH_PATTERN_ESCAPE,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_SERVER_NAME,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_SPECIAL_CHARACTERS,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_SQL92_DATETIME_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_FOREIGN_KEY_DELETE_RULE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_FOREIGN_KEY_UPDATE_RULE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_GRANT,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_NUMERIC_VALUE_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_PREDICATES,
+ InfoTab::Bitmask,
+ SQL_SP_COMPARISON | SQL_SP_IN | SQL_SP_ISNOTNULL | SQL_SP_ISNULL | SQL_SP_LIKE,
+ 0
+ },
+ { SQL_SQL92_RELATIONAL_JOIN_OPERATORS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_REVOKE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_ROW_VALUE_CONSTRUCTOR,
+ InfoTab::Bitmask,
+ SQL_SRVC_VALUE_EXPRESSION,
+ 0
+ },
+ { SQL_SQL92_STRING_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL92_VALUE_EXPRESSIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SQL_CONFORMANCE,
+ InfoTab::Long,
+ 0L,
+ 0
+ },
+ { SQL_STANDARD_CLI_CONFORMANCE,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_STATIC_CURSOR_ATTRIBUTES1,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_STATIC_CURSOR_ATTRIBUTES2,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_STRING_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SUBQUERIES,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_SYSTEM_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_TABLE_TERM,
+ InfoTab::Char,
+ 0L,
+ "TABLE"
+ },
+ { SQL_TIMEDATE_ADD_INTERVALS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_TIMEDATE_DIFF_INTERVALS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_TIMEDATE_FUNCTIONS,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_TXN_CAPABLE,
+ InfoTab::Short,
+ SQL_TC_DDL_COMMIT, // XXX do it
+ 0
+ },
+ { SQL_TXN_ISOLATION_OPTION,
+ InfoTab::Bitmask,
+ SQL_TXN_READ_COMMITTED,
+ 0
+ },
+ { SQL_UNION,
+ InfoTab::Bitmask,
+ 0L,
+ 0
+ },
+ { SQL_USER_NAME,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { SQL_XOPEN_CLI_YEAR,
+ InfoTab::Char,
+ 0L,
+ ""
+ },
+ { 0,
+ InfoTab::End,
+ 0L,
+ 0
+ }
+};
diff --git a/storage/ndb/src/old_files/client/odbc/handles/Makefile b/storage/ndb/src/old_files/client/odbc/handles/Makefile
new file mode 100644
index 00000000000..d37e7d286ba
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/Makefile
@@ -0,0 +1,28 @@
+include .defs.mk
+
+TYPE = *
+
+NONPIC_ARCHIVE = N
+
+PIC_ARCHIVE = Y
+
+ARCHIVE_TARGET = odbchandles
+
+SOURCES = \
+ HandleBase.cpp \
+ HandleRoot.cpp \
+ HandleEnv.cpp \
+ HandleDbc.cpp \
+ HandleStmt.cpp \
+ HandleDesc.cpp \
+ AttrRoot.cpp \
+ AttrEnv.cpp \
+ AttrDbc.cpp \
+ AttrStmt.cpp \
+ PoolNdb.cpp \
+ DescSpec.cpp \
+ FuncTab.cpp \
+ InfoTab.cpp
+
+include ../Extra.mk
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/client/odbc/handles/PoolNdb.cpp b/storage/ndb/src/old_files/client/odbc/handles/PoolNdb.cpp
new file mode 100644
index 00000000000..45d3c67ec77
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/PoolNdb.cpp
@@ -0,0 +1,81 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <common/common.hpp>
+#include <NdbMutex.h>
+#include <NdbApi.hpp>
+#include "PoolNdb.hpp"
+
+#ifdef NDB_WIN32
+static NdbMutex & ndb_mutex = * NdbMutex_Create();
+#else
+static NdbMutex ndb_mutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+PoolNdb::PoolNdb() :
+ m_cntUsed(0),
+ m_cntFree(0)
+{
+}
+
+PoolNdb::~PoolNdb()
+{
+}
+
+Ndb*
+PoolNdb::allocate(Ctx& ctx, int timeout)
+{
+ NdbMutex_Lock(&ndb_mutex);
+ Ndb* pNdb;
+ if (m_cntFree == 0) {
+ pNdb = new Ndb("TEST_DB");
+ pNdb->useFullyQualifiedNames(true);
+ if (pNdb->init(64) < 0) {
+ ctx.pushStatus(pNdb, "init");
+ delete pNdb;
+ NdbMutex_Unlock(&ndb_mutex);
+ return 0;
+ }
+ if (pNdb->waitUntilReady(timeout) < 0) {
+ ctx.pushStatus(Sqlstate::_HYT00, Error::Gen, "connection timeout after %d seconds", timeout);
+ ctx.pushStatus(pNdb, "waitUntilReady");
+ delete pNdb;
+ NdbMutex_Unlock(&ndb_mutex);
+ return 0;
+ }
+ m_listFree.push_back(pNdb);
+ m_cntFree++;
+ }
+ pNdb = m_listFree.front();
+ m_listFree.pop_front();
+ m_cntFree--;
+ m_cntUsed++;
+ ctx_log1(("alloc Ndb: used=%u free=%u", m_cntUsed, m_cntFree));
+ NdbMutex_Unlock(&ndb_mutex);
+ return pNdb;
+}
+
+void
+PoolNdb::release(Ctx& ctx, Ndb* pNdb)
+{
+ NdbMutex_Lock(&ndb_mutex);
+ m_listUsed.remove(pNdb);
+ m_listFree.push_back(pNdb);
+ m_cntFree++;
+ m_cntUsed--;
+ ctx_log1(("free Ndb: used=%u free=%u", m_cntUsed, m_cntFree));
+ NdbMutex_Unlock(&ndb_mutex);
+}
diff --git a/storage/ndb/src/old_files/client/odbc/handles/PoolNdb.hpp b/storage/ndb/src/old_files/client/odbc/handles/PoolNdb.hpp
new file mode 100644
index 00000000000..35eac055c30
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/PoolNdb.hpp
@@ -0,0 +1,44 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 ODBC_HANDLES_PoolNdb_hpp
+#define ODBC_HANDLES_PoolNdb_hpp
+
+#include <common/common.hpp>
+#include <list>
+
+class Ndb;
+
+/**
+ * @class PoolNdb
+ * @brief Pool of Ndb objects.
+ *
+ * A class implementing pool of Ndb objects.
+ */
+class PoolNdb {
+public:
+ PoolNdb();
+ ~PoolNdb();
+ Ndb* allocate(Ctx& ctx, int timeout);
+ void release(Ctx& ctx, Ndb* pNdb);
+private:
+ std::list<Ndb*> m_listUsed;
+ std::list<Ndb*> m_listFree;
+ unsigned m_cntUsed;
+ unsigned m_cntFree;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/client/odbc/handles/handles.hpp b/storage/ndb/src/old_files/client/odbc/handles/handles.hpp
new file mode 100644
index 00000000000..a9f0fcae888
--- /dev/null
+++ b/storage/ndb/src/old_files/client/odbc/handles/handles.hpp
@@ -0,0 +1,28 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 OBDC_HANDLES_handles_hpp
+#define OBDC_HANDLES_handles_hpp
+
+#include <common/common.hpp>
+#include "HandleBase.hpp"
+#include "HandleRoot.hpp"
+#include "HandleEnv.hpp"
+#include "HandleDbc.hpp"
+#include "HandleStmt.hpp"
+#include "HandleDesc.hpp"
+
+#endif
diff --git a/storage/ndb/src/old_files/ndbbaseclient/Makefile b/storage/ndb/src/old_files/ndbbaseclient/Makefile
new file mode 100644
index 00000000000..f4c49a95ffa
--- /dev/null
+++ b/storage/ndb/src/old_files/ndbbaseclient/Makefile
@@ -0,0 +1,29 @@
+include .defs.mk
+
+TYPE := *
+
+PIC_ARCHIVE := Y
+NONPIC_ARCHIVE := Y
+ARCHIVE_TARGET := ndbbaseclient
+
+A_LIB := Y
+SO_LIB := Y
+PIC_LIB := Y
+LIB_TARGET := ndbclient
+
+LIB_TARGET_ARCHIVES := $(ARCHIVE_TARGET) \
+ ndbapi \
+ mgmapi \
+ newtonapi \
+ transporter \
+ general \
+ signaldataprint \
+ mgmsrvcommon \
+ portlib \
+ logger \
+ trace
+
+SOURCES = ndbbaseclient_dummy.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/src/old_files/ndbbaseclient/ndbbaseclient_dummy.cpp b/storage/ndb/src/old_files/ndbbaseclient/ndbbaseclient_dummy.cpp
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/ndb/src/old_files/ndbbaseclient/ndbbaseclient_dummy.cpp
diff --git a/storage/ndb/src/old_files/ndbclient/Makefile b/storage/ndb/src/old_files/ndbclient/Makefile
new file mode 100644
index 00000000000..2c597eccfa1
--- /dev/null
+++ b/storage/ndb/src/old_files/ndbclient/Makefile
@@ -0,0 +1,37 @@
+include .defs.mk
+
+TYPE := *
+
+PIC_ARCHIVE := Y
+NONPIC_ARCHIVE := Y
+ARCHIVE_TARGET := ndbclient
+
+A_LIB := N
+SO_LIB := Y
+PIC_LIB := Y
+LIB_TARGET := ndbclient_extra
+
+LDFLAGS_LAST = -lstdc++ -lm
+
+LIB_TARGET_ARCHIVES := $(ARCHIVE_TARGET) \
+ ndbapi \
+ mgmapi \
+ newtonapi \
+ transporter \
+ general \
+ signaldataprint \
+ mgmsrvcommon \
+ portlib \
+ logger \
+ trace \
+ odbcdriver \
+ odbchandles \
+ odbcdictionary \
+ odbccodegen \
+ odbcexecutor \
+ odbccommon
+
+SOURCES = ndbclient_dummy.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/src/old_files/ndbclient/ndbclient_dummy.cpp b/storage/ndb/src/old_files/ndbclient/ndbclient_dummy.cpp
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/ndb/src/old_files/ndbclient/ndbclient_dummy.cpp
diff --git a/storage/ndb/src/old_files/newtonapi/Makefile b/storage/ndb/src/old_files/newtonapi/Makefile
new file mode 100644
index 00000000000..bed179046a5
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/Makefile
@@ -0,0 +1,27 @@
+include .defs.mk
+
+TYPE := ndbapiclient
+
+PIC_ARCHIVE := Y
+ARCHIVE_TARGET := newtonapi
+
+A_LIB := Y
+SO_LIB := Y
+PIC_LIB := Y
+
+LIB_TARGET := NEWTON_API
+LIB_TARGET_ARCHIVES := $(ARCHIVE_TARGET) NDB_API
+
+SOURCES = \
+ dba_binding.cpp \
+ dba_process.cpp \
+ dba_dac.cpp \
+ dba_init.cpp \
+ dba_schema.cpp \
+ dba_bulkread.cpp \
+ dba_error.cpp \
+ dba_config.cpp
+
+CCFLAGS_LOC += -I../include -I$(call fixpath,$(NDB_TOP)/include/portlib) -I$(call fixpath,$(NDB_TOP)/include/util) -I$(call fixpath,$(NDB_TOP)/include/newtonapi) -DDEBUG
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/newtonapi/dba_binding.cpp b/storage/ndb/src/old_files/newtonapi/dba_binding.cpp
new file mode 100644
index 00000000000..63e48110b1d
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_binding.cpp
@@ -0,0 +1,439 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "dba_internal.hpp"
+
+static bool matchType(NdbDictionary::Column::Type, DBA_DataTypes_t);
+static bool matchSize(NdbDictionary::Column::Type, unsigned, Size_t);
+static int computeChecksum(const DBA_Binding_t * bindings);
+
+struct DBA__Array {
+ int count;
+ int data[1];
+
+ bool exists(int value) const {
+ for(int i = 0; i<count; i++)
+ if(data[i] == value)
+ return true;
+ return false;
+ }
+
+ void insert(int value){
+ data[count] = value;
+ count++;
+ }
+};
+
+/**
+ * createBindings
+ */
+static
+DBA_Binding_t *
+createBinding(const char* TableName,
+ int NbCol,
+ const DBA_ColumnBinding_t ColsBindings[],
+ Size_t StructSz,
+ const NdbDictionary::Table * theTable,
+ struct DBA__Array * keys,
+ struct DBA__Array * columns);
+
+extern "C"
+DBA_Binding_t *
+DBA_CreateBinding( const char* TableName,
+ int NbCol,
+ const DBA_ColumnBinding_t ColsBindings[],
+ Size_t StructSz ){
+
+ NdbDictionary::Dictionary * dict = DBA__TheNdb->getDictionary();
+ if(dict == 0){
+ DBA__SetLatestError(DBA_NDB_ERROR, 0,
+ "Internal NDB error: No dictionary");
+ return 0;
+ }
+
+ const NdbDictionary::Table * table = dict->getTable(TableName);
+ if(table == 0){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "No such table: %s", TableName);
+ return 0;
+ }
+
+ /**
+ * Keys/Columns in table
+ */
+ const int tabColumns = table->getNoOfColumns();
+ const int tabKeys = table->getNoOfPrimaryKeys();
+
+ /**
+ * Ok, ok... I alloc four bytes extra so what...
+ */
+ struct DBA__Array * keys = (struct DBA__Array *)malloc
+ (sizeof(struct DBA__Array)+tabKeys*sizeof(int));
+
+ if(keys == 0){
+ DBA__SetLatestError(DBA_ERROR, 0,
+ "malloc(%d) failed",
+ sizeof(struct DBA__Array)+tabKeys*sizeof(int));
+ return 0;
+ }
+
+ struct DBA__Array * columns = (struct DBA__Array *)malloc
+ (sizeof(struct DBA__Array)+tabColumns*sizeof(int));
+
+ if(columns == 0){
+ DBA__SetLatestError(DBA_ERROR, 0,
+ "malloc(%d) failed",
+ sizeof(struct DBA__Array)+tabColumns*sizeof(int));
+ free(keys);
+ return 0;
+ }
+
+ columns->count = 0;
+ keys->count = 0;
+
+ DBA_Binding_t * bindings = createBinding(TableName,
+ NbCol,
+ ColsBindings,
+ StructSz,
+ table,
+ keys,
+ columns);
+
+ for(int i = 0; i<tabColumns; i++){
+ const NdbDictionary::Column * col = table->getColumn(i);
+ if(col->getPrimaryKey()){
+ if(!keys->exists(i)){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Key column: %s not specified in binding",
+ col->getName());
+
+ free(keys); free(columns);
+ DBA_DestroyBinding(bindings);
+ return 0;
+ }
+ }
+ }
+
+ free(keys); free(columns);
+
+ DBA__ValidBinding(bindings);
+
+ return bindings;
+}
+
+DBA_Binding_t *
+createBinding(const char* TableName,
+ int NbCol,
+ const DBA_ColumnBinding_t ColsBindings[],
+ Size_t StructSz,
+ const NdbDictionary::Table * table,
+ struct DBA__Array * keys,
+ struct DBA__Array * columns){
+ /**
+ * Counters for this part of binding
+ */
+ int noOfKeys = 0;
+ int noOfColumns = 0;
+ int noOfSubBindings = 0;
+
+ /**
+ * Check names and types and sizes
+ */
+ for(int i = 0; i<NbCol; i++){
+ if(ColsBindings[i].Ptr){
+ /**
+ * Pointer binding
+ */
+ noOfSubBindings ++;
+
+ DBA_Binding_t * tmp = createBinding(TableName,
+ ColsBindings[i].Size,
+ ColsBindings[i].SubBinding,
+ StructSz,
+ table,
+ keys,
+ columns);
+ DBA__ValidBinding(tmp);
+
+ if(tmp == 0){
+ // createBindings have already set latestError
+ return 0;
+ }
+
+ DBA_DestroyBinding(tmp);
+ } else {
+ const NdbDictionary::Column * col =
+ table->getColumn(ColsBindings[i].Name);
+ const Uint32 attrId = col->getColumnNo();
+
+ if(col == 0){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Unknown column: %s", ColsBindings[i].Name);
+ return 0;
+ }
+ const NdbDictionary::Column::Type type = col->getType();
+ if(!matchType(type, ColsBindings[i].DataType)){
+ DBA_DEBUG("Incorrect type for: " << ColsBindings[i].Name);
+ DBA_DEBUG("type: " << type);
+ DBA_DEBUG("ColsBindings[i].DataType: " << ColsBindings[i].DataType);
+
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Incorrect type for column: %s",
+ ColsBindings[i].Name);
+
+ return 0;
+ }
+
+ if(!matchSize(type, col->getLength(), ColsBindings[i].Size)){
+ DBA_DEBUG("Incorrect size for: " << ColsBindings[i].Name);
+ DBA_DEBUG("type: " << type);
+ DBA_DEBUG("length: " << col->getLength());
+ DBA_DEBUG("ColsBindings[i].Size" << (Uint64)ColsBindings[i].Size);
+
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Incorrect size for column: %s",
+ ColsBindings[i].Name);
+ return 0;
+ }
+
+ if(col->getPrimaryKey()){
+ noOfKeys++;
+ } else {
+ noOfColumns++;
+ }
+
+ /**
+ * Check only in "validate" phase
+ */
+ if(columns != 0 && keys != 0){
+ if(columns->exists(attrId) || keys->exists(attrId)){
+ DBA_DEBUG("Column bound multiple times: " << ColsBindings[i].Name);
+
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Column bound multiple times: %s",
+ ColsBindings[i].Name);
+ return 0;
+ }
+
+ if(col->getPrimaryKey()){
+ keys->insert(attrId);
+ } else {
+ columns->insert(attrId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Validation is all set
+ */
+
+ /**
+ * Allocate memory
+ */
+ const int szOfStruct =
+ sizeof(DBA_Binding_t)
+ + strlen(TableName) + 4
+ + (2 * sizeof(int) * noOfKeys)
+ + (2 * sizeof(int) * noOfColumns)
+ + ((sizeof(struct DBA_Binding *) + sizeof(int)) * noOfSubBindings)
+ - 4;
+
+ DBA_Binding * ret = (DBA_Binding *)malloc(szOfStruct);
+ if(ret == 0){
+ DBA__SetLatestError(DBA_ERROR, 0,
+ "malloc(%d) failed", szOfStruct);
+ return 0;
+ }
+
+ for(int i = 0; i<DBA__MagicLength; i++)
+ ret->magic[i] = DBA__TheMagic[i];
+
+ ret->noOfKeys = noOfKeys;
+ ret->noOfColumns = noOfColumns;
+ ret->noOfSubBindings = noOfSubBindings;
+
+ ret->keyIds = (int *)&(ret->data[0]);
+ ret->keyOffsets = ret->keyIds + noOfKeys;
+
+ ret->columnIds = ret->keyOffsets + noOfKeys;
+ ret->columnOffsets = ret->columnIds + noOfColumns;
+
+ ret->subBindingOffsets = ret->columnOffsets + noOfColumns;
+ ret->subBindings = (DBA_Binding **)
+ (ret->subBindingOffsets + noOfSubBindings);
+
+ ret->tableName = (char *)(ret->subBindings + noOfSubBindings);
+ ret->structSz = StructSz;
+ ret->checkSum = computeChecksum(ret);
+
+ /**
+ * Populate arrays
+ */
+ strcpy(ret->tableName, TableName);
+
+ int k = 0;
+ int c = 0;
+ int p = 0;
+
+ for(int i = 0; i<NbCol; i++){
+ if(ColsBindings[i].Ptr){
+ ret->subBindings[p] = createBinding(TableName,
+ ColsBindings[i].Size,
+ ColsBindings[i].SubBinding,
+ StructSz,
+ table,
+ 0,
+ 0);
+
+ DBA__ValidBinding(ret->subBindings[p]);
+
+ ret->subBindingOffsets[p] = ColsBindings[i].Offset;
+ p++;
+ } else {
+ const NdbDictionary::Column * col =
+ table->getColumn(ColsBindings[i].Name);
+
+ if(col->getPrimaryKey()){
+ ret->keyIds[k] = col->getColumnNo();
+ ret->keyOffsets[k] = ColsBindings[i].Offset;
+ k++;
+ } else {
+ ret->columnIds[c] = col->getColumnNo();
+ ret->columnOffsets[c] = ColsBindings[i].Offset;
+ c++;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+extern "C"
+DBA_Error_t
+DBA_DestroyBinding( DBA_Binding_t* Binding ){
+
+ for(int i = 0; i<Binding->noOfSubBindings; i++)
+ DBA_DestroyBinding(Binding->subBindings[i]);
+
+ free(Binding);
+
+ return DBA_NO_ERROR;
+}
+
+static
+bool
+matchType(NdbDictionary::Column::Type t1, DBA_DataTypes_t t2){
+ for(int i = 0; i<DBA__NoOfMappings; i++)
+ if(DBA__DataTypesMappings[i].newtonType == t2 &&
+ DBA__DataTypesMappings[i].ndbType == t1)
+ return true;
+ return false;
+}
+
+static
+bool
+matchSize(NdbDictionary::Column::Type t, unsigned b, Size_t s) {
+ switch(t){
+ case NdbDictionary::Column::Int:
+ case NdbDictionary::Column::Unsigned:
+ case NdbDictionary::Column::Float:
+ return (4 * b) == s;
+ case NdbDictionary::Column::Bigint:
+ case NdbDictionary::Column::Bigunsigned:
+ case NdbDictionary::Column::Double:
+ return (8 * b) == s;
+ case NdbDictionary::Column::Decimal:
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Binary:
+ return (1 * b) == s;
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Varbinary:
+ case NdbDictionary::Column::Datetime:
+ case NdbDictionary::Column::Timespec:
+ case NdbDictionary::Column::Blob:
+ case NdbDictionary::Column::Tinyint:
+ case NdbDictionary::Column::Tinyunsigned:
+ case NdbDictionary::Column::Smallint:
+ case NdbDictionary::Column::Smallunsigned:
+ case NdbDictionary::Column::Mediumint:
+ case NdbDictionary::Column::Mediumunsigned:
+ case NdbDictionary::Column::Undefined:
+ return false;
+ }
+ return false;
+}
+
+bool
+DBA__ValidBinding(const DBA_Binding_t * bindings){
+ if(bindings == 0){
+ DBA_DEBUG("Null pointer passed to validBinding");
+ return false;
+ }
+
+ for(int i = 0; i<DBA__MagicLength; i++)
+ if(bindings->magic[i] != DBA__TheMagic[i]){
+ DBA_DEBUG("Invalid magic in validBinding");
+ return false;
+ }
+
+ const int cs = computeChecksum(bindings);
+ if(cs != bindings->checkSum){
+ DBA_DEBUG("Invalid checksum in validBinding");
+ DBA_DEBUG("cs = " << cs << " b->cs= " << bindings->checkSum);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DBA__ValidBindings(const DBA_Binding_t * const * pBindings, int n){
+ for(int i = 0; i<n; i++)
+ if(!DBA__ValidBinding(pBindings[i]))
+ return false;
+ return true;
+}
+
+/**
+ * Note: currently only checksum "static" part of struct
+ */
+static
+int
+computeChecksum(const DBA_Binding_t * bindings){
+ int sum = 0;
+ int pos = 0;
+ const char * ptr = ((const char *)bindings)+DBA__MagicLength+sizeof(int);
+ const int sz = sizeof(DBA_Binding_t) - DBA__MagicLength - sizeof(int) - 4;
+
+ for(int i = 0; i<sz; i++){
+ sum += ((int)ptr[i]) << pos;
+ pos += 8;
+ if(pos == 32)
+ pos = 0;
+ }
+
+ return sum;
+}
+
+int
+DBA__GetStructSize(const DBA_Binding_t * bind){
+ if(!DBA__ValidBinding(bind))
+ return 0;
+ return bind->structSz;
+}
diff --git a/storage/ndb/src/old_files/newtonapi/dba_bulkread.cpp b/storage/ndb/src/old_files/newtonapi/dba_bulkread.cpp
new file mode 100644
index 00000000000..1f75037046b
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_bulkread.cpp
@@ -0,0 +1,267 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "dba_internal.hpp"
+
+struct DBA__BulkReadData {
+ const DBA_Binding_t * const * pBindings; // The bindings
+ DBA_BulkReadResultSet_t * pData; // The data
+
+ int NbRows; // NbRows per binding
+ int NbBindings; // NbBindings
+ int TotalRows; // Total rows (NbRows*NbBindings)
+
+ DBA_AsyncCallbackFn_t CbFunc; // Users callback
+ DBA_ReqId_t RequestId; // Users request id
+ DBA_Error_t Status; // Request status
+ DBA_ErrorCode_t ErrorCode; /**< Request error
+ Only valid if request is
+ aborted */
+
+ int RowsSubmitted; // No of read sent to NDB
+ int RowsAcknowledged; // No of read responses
+ int OpPerTrans; // Operations per transaction
+
+ struct Index {
+ int binding;
+ int row;
+ int datarow;
+
+ void init() { row = binding = datarow = 0;}
+ void next(int rows) {
+ datarow++; row++;
+ if(row == rows){ row = 0; binding++; }
+ }
+ };
+ Index lastSend;
+ Index nextSend;
+
+ /**
+ * If "simple" bulkread
+ * use this storage
+ */
+ const DBA_Binding_t * bindings[1];
+
+ DBA__BulkReadData() {
+ RequestId = DBA_INVALID_REQID;
+ }
+ void ProcessBulkRead();
+ bool ProcessCallback(int errorCode, NdbConnection * connection);
+};
+
+static
+void
+NewtonCallback(int errorCode,
+ NdbConnection * connection,
+ void * anyObject){
+
+ DBA__BulkReadData * brd = (DBA__BulkReadData*)anyObject;
+
+ brd->ProcessCallback(errorCode, connection);
+
+ DBA__TheNdb->closeTransaction(connection);
+
+ if(brd->RowsSubmitted == brd->TotalRows){
+
+ /**
+ * The entire bulk read is finished,
+ * call users callback
+ */
+ DBA_ReqId_t reqId = brd->RequestId;
+
+ // Invalidate BulkReadData
+ brd->RequestId = DBA_INVALID_REQID;
+
+ brd->CbFunc(reqId, brd->Status, brd->ErrorCode);
+ return;
+ }
+
+ brd->ProcessBulkRead();
+}
+
+/**
+ * A BulkReadData structure
+ */
+static DBA__BulkReadData theBRD;
+
+#define CHECK_BINDINGS(Bindings) \
+ if(!DBA__ValidBinding(Bindings)){ \
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Invalid bindings"); \
+ return DBA_INVALID_REQID; \
+ }
+
+#define CHECK_BINDINGS2(Bindings, NbBindings) \
+ if(!DBA__ValidBindings(Bindings, NbBindings)){ \
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Invalid bindings"); \
+ return DBA_INVALID_REQID; \
+ }
+
+DBA_ReqId_t
+DBA_BulkReadRows(const DBA_Binding_t * pBindings,
+ DBA_BulkReadResultSet_t pData[],
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+
+ CHECK_BINDINGS(pBindings);
+
+ DBA__BulkReadData * brd = &theBRD;
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+
+ if(brd->RequestId != DBA_INVALID_REQID){
+ DBA__SetLatestError(DBA_ERROR, 0,
+ "DBA only permits 1 concurrent bulkread");
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return DBA_ERROR;
+ }
+
+ theBRD.RequestId = 1;
+
+ /**
+ *
+ */
+ brd->bindings[0] = pBindings;
+ brd->pBindings = brd->bindings;
+ brd->pData = pData;
+
+ /**
+ * Control data
+ */
+ brd->NbRows = NbRows;
+ brd->NbBindings = 1;
+ brd->TotalRows = NbRows;
+ brd->CbFunc = CbFunc;
+ brd->Status = DBA_NO_ERROR;
+ brd->ErrorCode = 0;
+ brd->OpPerTrans = DBA__BulkReadCount;
+
+ brd->RowsSubmitted = 0;
+ brd->RowsAcknowledged = 0;
+
+ brd->lastSend.init();
+ brd->nextSend.init();
+
+ brd->ProcessBulkRead();
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+
+ return brd->RequestId;
+}
+
+DBA_ReqId_t
+DBA_BulkMultiReadRows(const DBA_Binding_t * const * pBindings,
+ DBA_BulkReadResultSet_t pData[],
+ int NbBindings,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+
+ CHECK_BINDINGS2(pBindings, NbBindings);
+
+ DBA__BulkReadData * brd = &theBRD;
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+
+ if(brd->RequestId != DBA_INVALID_REQID){
+ DBA__SetLatestError(DBA_ERROR, 0,
+ "DBA only permits 1 concurrent bulkread");
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return DBA_ERROR;
+ }
+
+ brd->RequestId = 1;
+
+ /**
+ *
+ */
+ brd->pBindings = pBindings;
+ brd->pData = pData;
+
+ /**
+ * Control data
+ */
+ brd->NbRows = NbRows;
+ brd->NbBindings = NbBindings;
+ brd->TotalRows = (NbRows * NbBindings);
+ brd->CbFunc = CbFunc;
+ brd->Status = DBA_NO_ERROR;
+ brd->ErrorCode = 0;
+ brd->OpPerTrans = DBA__BulkReadCount;
+
+ brd->RowsSubmitted = 0;
+ brd->RowsAcknowledged = 0;
+
+ brd->lastSend.init();
+ brd->nextSend.init();
+
+ brd->ProcessBulkRead();
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+
+ return brd->RequestId;
+}
+
+bool
+DBA__BulkReadData::ProcessCallback(int errorCode, NdbConnection * con){
+
+ Index tmp = lastSend;
+ const NdbOperation * op = con->getNextCompletedOperation(0);
+
+ for(int i = 0; i<OpPerTrans && RowsAcknowledged < RowsSubmitted; i++){
+ require(op != 0);
+ if(op->getNdbError().code == 0)
+ pData[tmp.datarow].RowFoundIndicator = 1;
+ else
+ pData[tmp.datarow].RowFoundIndicator = 0;
+
+ RowsAcknowledged++;
+ tmp.next(NbRows);
+ op = con->getNextCompletedOperation(op);
+ }
+ return true;
+}
+
+void
+DBA__BulkReadData::ProcessBulkRead(){
+
+ NdbConnection * con = DBA__TheNdb->startTransaction();
+
+ Index tmp = nextSend;
+
+ for(int i = 0; i<OpPerTrans && RowsSubmitted < TotalRows; i++){
+
+ const DBA_Binding_t * binding = pBindings[tmp.binding];
+ void * data = pData[tmp.datarow].DataPtr;
+
+ NdbOperation * op = con->getNdbOperation(binding->tableName);
+
+ op->simpleRead();
+
+ require(DBA__EqualGetValue(op, binding, data));
+
+ RowsSubmitted++;
+ tmp.next(NbRows);
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)this,
+ CommitAsMuchAsPossible);
+
+ lastSend = nextSend;
+ nextSend = tmp;
+}
diff --git a/storage/ndb/src/old_files/newtonapi/dba_config.cpp b/storage/ndb/src/old_files/newtonapi/dba_config.cpp
new file mode 100644
index 00000000000..d84386a9438
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_config.cpp
@@ -0,0 +1,115 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "dba_internal.hpp"
+
+int DBA__NBP_Intervall = 10;
+int DBA__BulkReadCount = 1000;
+int DBA__StartTransactionTimout = 0;
+int DBA__NBP_Force = 1;
+
+struct DBA__Config {
+ int ParamId;
+ int * Param;
+ int min;
+ int max;
+ const char * Description;
+};
+
+static
+DBA__Config Parameters[] = {
+ { 0, &DBA__NBP_Intervall, 4, INT_MAX,
+ "Newton Batch Process Interval(ms)" },
+ { 1, &DBA__BulkReadCount, 1, 5000,
+ "Operations per transaction during bulkread" },
+ { 2, &DBA__StartTransactionTimout, 0, INT_MAX,
+ "Start transaction timeout(ms)" },
+ { 3, &DBA__NBP_Force, 0, 2,
+ "Newton Batch Process Force send algorithm" }
+};
+
+static const int Params = sizeof(Parameters)/sizeof(DBA__Config);
+
+static
+DBA__Config *
+getParam(int id){
+ for(int i = 0; i<Params; i++)
+ if(Parameters[i].ParamId == id)
+ return &Parameters[i];
+ return 0;
+}
+
+
+extern "C"
+DBA_Error_t
+DBA_SetParameter(int ParameterId, int Value){
+ if(Value == -1){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Node id is not modifyable");
+ return DBA_APPLICATION_ERROR;
+ }
+
+ DBA__Config * p = getParam(ParameterId);
+
+ if(p == 0){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Invalid parameter id: %d",
+ ParameterId);
+ return DBA_APPLICATION_ERROR;
+ }
+
+ if(Value < p->min){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Value too small for parameter %d (min = %d)",
+ Value, p->min);
+ return DBA_APPLICATION_ERROR;
+ }
+
+ if(Value > p->max){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Value too big for parameter %d (max = %d)",
+ Value, p->max);
+ return DBA_APPLICATION_ERROR;
+ }
+
+ * p->Param = Value;
+ return DBA_NO_ERROR;
+}
+
+extern "C"
+DBA_Error_t
+DBA_GetParameter(int ParameterId, int * Value){
+ if(ParameterId == -1){
+ if(DBA__TheNdb == 0){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "DBA_Open() is not called"
+ );
+ return DBA_APPLICATION_ERROR;
+ }
+ * Value = DBA__TheNdb->getNodeId();
+ return DBA_NO_ERROR;
+ }
+
+ DBA__Config * p = getParam(ParameterId);
+ if(p == 0){
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Invalid parameter id: %d",
+ ParameterId);
+ return DBA_APPLICATION_ERROR;
+ }
+
+ * Value = * p->Param;
+
+ return DBA_NO_ERROR;
+}
+
diff --git a/storage/ndb/src/old_files/newtonapi/dba_dac.cpp b/storage/ndb/src/old_files/newtonapi/dba_dac.cpp
new file mode 100644
index 00000000000..fcb4e676e46
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_dac.cpp
@@ -0,0 +1,842 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "dba_internal.hpp"
+#include <NdbSleep.h>
+
+static void
+DBA__ErrorMapping(const NdbError & err, DBA_Error_t * status){
+ switch(err.classification){
+ case NdbError::ConstraintViolation:
+ * status = DBA_CONSTRAINT_VIOLATION;
+ break;
+
+ case NdbError::NoDataFound:
+ * status = DBA_NO_DATA;
+ break;
+
+ case NdbError::TemporaryResourceError:
+ case NdbError::NodeRecoveryError:
+ * status = DBA_TEMPORARY_ERROR;
+ break;
+
+ case NdbError::InsufficientSpace:
+ * status = DBA_INSUFFICIENT_SPACE;
+ break;
+
+ case NdbError::UnknownResultError:
+ * status = DBA_UNKNOWN_RESULT;
+ break;
+
+ case NdbError::OverloadError:
+ * status = DBA_OVERLOAD;
+ break;
+
+ case NdbError::TimeoutExpired:
+ * status = DBA_TIMEOUT;
+ break;
+
+ case NdbError::SchemaError:
+ * status = DBA_SCHEMA_ERROR;
+ break;
+
+ case NdbError::ApplicationError:
+ * status = DBA_APPLICATION_ERROR;
+ break;
+
+ case NdbError::InternalError:
+ default:
+ * status = DBA_NDB_ERROR;
+ break;
+ }
+}
+
+/**
+ * Map between NDB error codes and DBA error codes
+ */
+static
+void
+DBA__CallbackErrorCodeMapping(int errorCode,
+ NdbConnection * connection,
+ DBA_Error_t * status,
+ DBA_ErrorCode_t * errCode) {
+ if(errorCode == 0){
+ * status = DBA_NO_ERROR;
+ * errCode = 0;
+ return;
+ }
+ const NdbError & err = connection->getNdbError();
+ DBA__ErrorMapping(err, status);
+ * errCode = err.code;
+}
+
+/**
+ * When startTransaction fails
+ */
+static
+void
+DBA__ConnectionErrorMapping(Ndb * theNdb){
+ const NdbError & err = theNdb->getNdbError();
+
+ DBA_Error_t status;
+ DBA__ErrorMapping(err, &status);
+
+ DBA__SetLatestError(status, err.code,
+ err.message);
+}
+
+/**
+ * When getNdbOperation fails
+ */
+static
+void
+DBA__OperationErrorMapping(Ndb * theNdb, NdbConnection * con){
+ const NdbError & err = theNdb->getNdbError();
+
+ DBA_Error_t status;
+ DBA__ErrorMapping(err, &status);
+
+ DBA__SetLatestError(status, err.code,
+ err.message);
+}
+
+/**
+ * When equal/get/set value fails
+ */
+static
+void
+DBA__EqualErrorMapping(Ndb * theNdb, NdbConnection * con, NdbOperation * op){
+ const NdbError & err = theNdb->getNdbError();
+
+ DBA_Error_t status;
+ DBA__ErrorMapping(err, &status);
+
+ DBA__SetLatestError(status, err.code,
+ err.message);
+}
+
+static
+void
+NewtonCallback(int errorCode,
+ NdbConnection * connection,
+ void * anyObject){
+
+ DBA_AsyncCallbackFn_t CbFunc = (DBA_AsyncCallbackFn_t)anyObject;
+ DBA_ReqId_t ReqId = (DBA_ReqId_t) connection;
+
+ DBA_Error_t Status = (DBA_Error_t) errorCode;
+ DBA_ErrorCode_t Impl_Status ;
+
+ DBA__CallbackErrorCodeMapping(errorCode, connection, &Status, &Impl_Status);
+
+ DBA__TheNdb->closeTransaction(connection);
+
+ DBA__RecvTransactions++;
+
+ CbFunc(ReqId, Status, Impl_Status);
+}
+
+/**
+ * Start transaction
+ */
+NdbConnection *
+startTransaction(){
+ NdbConnection * con = DBA__TheNdb->startTransaction();
+ if(con != 0)
+ return con;
+
+ const int _t = (DBA__SentTransactions - DBA__RecvTransactions);
+ const int t = (_t>0?_t:-_t);
+
+ if(!(DBA__TheNdb->getNdbError().code == 4006 && t > 1000)){
+ DBA_DEBUG("DBA__TheNdb->getNdbError() = " <<
+ DBA__TheNdb->getNdbError());
+ }
+
+ int sum = 0;
+ int sleepTime = 10;
+ for(; con == 0 && sum < DBA__StartTransactionTimout; ){
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ NdbSleep_MilliSleep(sleepTime);
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ con = DBA__TheNdb->startTransaction();
+
+ sum += sleepTime;
+ sleepTime += 10;
+ }
+
+ return con;
+}
+
+#define CHECK_BINDINGS(Bindings) \
+ if(!DBA__ValidBinding(Bindings)){ \
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Invalid bindings"); \
+ return DBA_INVALID_REQID; \
+ }
+
+#define CHECK_BINDINGS2(Bindings, NbBindings) \
+ if(!DBA__ValidBindings(Bindings, NbBindings)){ \
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0, "Invalid bindings"); \
+ return DBA_INVALID_REQID; \
+ }
+
+#define CHECK_CONNECTION(Connection) \
+ if(Connection == 0){ \
+ DBA__ConnectionErrorMapping(DBA__TheNdb); \
+ NdbMutex_Unlock(DBA__TheNewtonMutex); \
+ return DBA_INVALID_REQID; \
+ }
+
+#define CHECK_OPERATION(Connection, Operation) \
+ if(Operation == 0){ \
+ DBA__OperationErrorMapping(DBA__TheNdb, Connection); \
+ DBA__TheNdb->closeTransaction(Connection); \
+ NdbMutex_Unlock(DBA__TheNewtonMutex); \
+ return DBA_INVALID_REQID; \
+ }
+
+#define EQUAL_ERROR(Connection, Operation) { \
+ DBA__EqualErrorMapping(DBA__TheNdb, Connection, Operation); \
+ DBA__TheNdb->closeTransaction(Connection); \
+ NdbMutex_Unlock(DBA__TheNewtonMutex); \
+ return DBA_INVALID_REQID; \
+ }
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+extern "C"
+DBA_ReqId_t
+DBA_ReadRows( const DBA_Binding_t* pBindings, void* const * _pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->simpleRead();
+
+ void * pData = _pData[i];
+
+ if(!DBA__EqualGetValue(op, pBindings, pData)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_ArrayReadRows( const DBA_Binding_t* pBindings, void * pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->simpleRead();
+
+ if(!DBA__EqualGetValue(op, pBindings,
+ ((char*)pData)+i*pBindings->structSz)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_MultiReadRow(const DBA_Binding_t * const * pBindings,
+ void * const * pData,
+ int NbBindings,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS2(pBindings, NbBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbBindings; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings[i]->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->simpleRead();
+
+ if(!DBA__EqualGetValue(op, pBindings[i], pData[i])){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+extern "C"
+DBA_ReqId_t
+DBA_InsertRows( const DBA_Binding_t* pBindings, const void * const * _pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->insertTuple();
+
+ const void * pData = _pData[i];
+
+ if(!DBA__EqualSetValue(op, pBindings, pData)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_ArrayInsertRows( const DBA_Binding_t* pBindings, const void * pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->insertTuple();
+
+ if(!DBA__EqualSetValue(op, pBindings,
+ ((char*)pData)+i*pBindings->structSz)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_MultiInsertRow(const DBA_Binding_t * const * pBindings,
+ const void * const * pData,
+ int NbBindings,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS2(pBindings, NbBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbBindings; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings[i]->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->insertTuple();
+
+ if(!DBA__EqualSetValue(op, pBindings[i], pData[i])){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+extern "C"
+DBA_ReqId_t
+DBA_UpdateRows( const DBA_Binding_t* pBindings, const void * const * _pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->updateTuple();
+
+ const void * pData = _pData[i];
+
+ if(!DBA__EqualSetValue(op, pBindings, pData)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_ArrayUpdateRows( const DBA_Binding_t* pBindings, const void * pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->updateTuple();
+
+ if(!DBA__EqualSetValue(op, pBindings,
+ ((char*)pData)+i*pBindings->structSz)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_MultiUpdateRow(const DBA_Binding_t * const * pBindings,
+ const void * const * pData,
+ int NbBindings,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS2(pBindings, NbBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbBindings; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings[i]->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->updateTuple();
+
+ if(!DBA__EqualSetValue(op, pBindings[i], pData[i])){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+extern "C"
+DBA_ReqId_t
+DBA_WriteRows( const DBA_Binding_t* pBindings, const void * const * _pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->writeTuple();
+
+ const void * pData = _pData[i];
+
+ if(!DBA__EqualSetValue(op, pBindings, pData)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_ArrayWriteRows( const DBA_Binding_t* pBindings, const void * pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->writeTuple();
+
+ if(!DBA__EqualSetValue(op, pBindings,
+ ((char*)pData)+i*pBindings->structSz)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_MultiWriteRow(const DBA_Binding_t * const * pBindings,
+ const void * const * pData,
+ int NbBindings,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS2(pBindings, NbBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbBindings; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings[i]->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->writeTuple();
+
+ if(!DBA__EqualSetValue(op, pBindings[i], pData[i])){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+extern "C"
+DBA_ReqId_t
+DBA_DeleteRows( const DBA_Binding_t* pBindings, const void * const * _pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->deleteTuple();
+
+ const void * pData = _pData[i];
+
+ if(!DBA__Equal(op, pBindings, pData)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_ArrayDeleteRows( const DBA_Binding_t* pBindings, const void * pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc ){
+ CHECK_BINDINGS(pBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbRows; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->deleteTuple();
+
+ if(!DBA__Equal(op, pBindings,
+ ((char*)pData)+i*pBindings->structSz)){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+extern "C"
+DBA_ReqId_t
+DBA_MultiDeleteRow(const DBA_Binding_t * const * pBindings,
+ const void * const * pData,
+ int NbBindings,
+ DBA_AsyncCallbackFn_t CbFunc ) {
+ CHECK_BINDINGS2(pBindings, NbBindings);
+
+ NdbMutex_Lock(DBA__TheNewtonMutex);
+ NdbConnection * con = startTransaction();
+
+ CHECK_CONNECTION(con);
+
+ for(int i = 0; i<NbBindings; i++){
+ NdbOperation * op = con->getNdbOperation(pBindings[i]->tableName);
+
+ CHECK_OPERATION(con, op);
+
+ op->deleteTuple();
+
+ if(!DBA__Equal(op, pBindings[i], pData[i])){
+ EQUAL_ERROR(con, op);
+ }
+ }
+
+ con->executeAsynchPrepare(Commit,
+ NewtonCallback,
+ (void*)CbFunc);
+
+ DBA__SentTransactions++;
+
+ NdbMutex_Unlock(DBA__TheNewtonMutex);
+ return (DBA_ReqId_t) con;
+}
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+bool
+DBA__EqualGetValue(NdbOperation * op,
+ const DBA_Binding_t* pBindings,
+ void * pData){
+ for(int i = 0; i<pBindings->noOfKeys; i++){
+ if(op->equal(pBindings->keyIds[i],
+ (const char*)pData+pBindings->keyOffsets[i]) == -1){
+ return false;
+ }
+ }
+
+ for(int i = 0; i<pBindings->noOfColumns; i++){
+ if(op->getValue(pBindings->columnIds[i],
+ (char*)pData+pBindings->columnOffsets[i]) == 0){
+ return false;
+ }
+ }
+
+ for(int i = 0; i<pBindings->noOfSubBindings; i++){
+ void * tData = *(void**)((char *)pData+pBindings->subBindingOffsets[i]);
+ const DBA_Binding_t * tBinding = pBindings->subBindings[i];
+ if(!DBA__EqualGetValue(op, tBinding, tData))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DBA__EqualSetValue(NdbOperation * op,
+ const DBA_Binding_t* pBindings,
+ const void * pData){
+
+ for(int i = 0; i<pBindings->noOfKeys; i++){
+ if(op->equal(pBindings->keyIds[i],
+ (const char*)pData+pBindings->keyOffsets[i]) == -1){
+ return false;
+ }
+ }
+
+ for(int i = 0; i<pBindings->noOfColumns; i++){
+ if(op->setValue(pBindings->columnIds[i],
+ (char*)pData+pBindings->columnOffsets[i]) == -1){
+ return false;
+ }
+ }
+
+ for(int i = 0; i<pBindings->noOfSubBindings; i++){
+ void * tData = * (void**)((char *)pData+pBindings->subBindingOffsets[i]);
+ const DBA_Binding_t * tBinding = pBindings->subBindings[i];
+ if(!DBA__EqualSetValue(op, tBinding, tData))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DBA__Equal(NdbOperation * op,
+ const DBA_Binding_t* pBindings,
+ const void * pData){
+
+ for(int i = 0; i<pBindings->noOfKeys; i++)
+ if(op->equal(pBindings->keyIds[i],
+ (const char*)pData+pBindings->keyOffsets[i]) == -1){
+ return false;
+ }
+
+ for(int i = 0; i<pBindings->noOfSubBindings; i++){
+ void * tData = *(void**)((char *)pData+pBindings->subBindingOffsets[i]);
+ const DBA_Binding_t * tBinding = pBindings->subBindings[i];
+ if(!DBA__Equal(op, tBinding, tData))
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/storage/ndb/src/old_files/newtonapi/dba_error.cpp b/storage/ndb/src/old_files/newtonapi/dba_error.cpp
new file mode 100644
index 00000000000..f05446522b0
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_error.cpp
@@ -0,0 +1,118 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <ndb_global.h>
+
+#include "dba_internal.hpp"
+
+static DBA_Error_t latestError = DBA_NO_ERROR;
+static DBA_ErrorCode_t latestNdbError = 0;
+static char latestMsg[1024];
+
+/**
+ * Private
+ */
+void
+DBA__SetLatestError(DBA_Error_t le,
+ DBA_ErrorCode_t lnb,
+ const char * msg, ...){
+
+ require(msg != 0);
+
+ latestError = le;
+ latestNdbError = lnb;
+
+ va_list ap;
+
+ va_start(ap, msg);
+ vsnprintf(latestMsg, sizeof(latestMsg)-1, msg, ap);
+ va_end(ap);
+}
+
+/**
+ * Get latest DBA error
+ */
+extern "C"
+DBA_Error_t
+DBA_GetLatestError(){
+ return latestError;
+}
+
+/**
+ * Get latest error string associated with GetLatestError
+ *
+ * String must not be free by caller of this method
+ */
+extern "C"
+const char *
+DBA_GetLatestErrorMsg(){
+ return latestMsg;
+}
+
+/**
+ * Get the latest NDB error
+ *
+ * Note only applicable to synchronous methods
+ */
+extern "C"
+DBA_ErrorCode_t
+DBA_GetLatestNdbError(){
+ return latestNdbError;
+}
+
+extern "C"
+const
+char *
+DBA_GetNdbErrorMsg(DBA_ErrorCode_t code){
+ return DBA__TheNdb->getNdbError(code).message;
+}
+
+struct DBA_ErrorTxtMap {
+ DBA_Error_t Error;
+ const char * Msg;
+};
+
+static
+const DBA_ErrorTxtMap errMap[] = {
+ { DBA_NO_ERROR, "No error" },
+ { DBA_NOT_IMPLEMENTED, "Function Not Implemented" },
+ { DBA_NDB_ERROR, "Uncategorised NDB error" },
+ { DBA_ERROR, "Uncategorised DBA implementation error" },
+ { DBA_APPLICATION_ERROR,
+ "Function called with invalid argument(s)/invalid sequence(s)" },
+ { DBA_NO_DATA, "No row with specified PK existed" },
+ { DBA_CONSTRAINT_VIOLATION, "There already existed a row with that PK" },
+
+ { DBA_TEMPORARY_ERROR, "Request failed due to temporary reasons" },
+ { DBA_INSUFFICIENT_SPACE,
+ "The DB is full" },
+ { DBA_OVERLOAD, "Request was rejected in NDB due to high load situation" },
+ { DBA_TIMEOUT, "The request timed out, probably due to dead-lock" }
+};
+
+static const int ErrMsgs = sizeof(errMap)/sizeof(DBA_ErrorTxtMap);
+
+extern "C"
+const
+char *
+DBA_GetErrorMsg(DBA_Error_t e){
+ for(int i = 0; i<ErrMsgs; i++)
+ if(errMap[i].Error == e)
+ return errMap[i].Msg;
+ return "Invalid error code";
+}
+
diff --git a/storage/ndb/src/old_files/newtonapi/dba_init.cpp b/storage/ndb/src/old_files/newtonapi/dba_init.cpp
new file mode 100644
index 00000000000..aa5fef1171c
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_init.cpp
@@ -0,0 +1,86 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "dba_internal.hpp"
+#include "dba_process.hpp"
+#include <NdbOut.hpp>
+
+
+#ifdef NDB_WIN32
+static NdbMutex & DBA__InitMutex = * NdbMutex_Create();
+#else
+static NdbMutex DBA__InitMutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+Ndb * DBA__TheNdb = 0;
+NdbMutex * DBA__TheNewtonMutex = 0;
+unsigned DBA__SentTransactions = 0;
+unsigned DBA__RecvTransactions = 0;
+NewtonBatchProcess * DBA__TheNBP = 0;
+
+extern "C"
+DBA_Error_t
+DBA_Open( ) {
+ NdbMutex_Lock(&DBA__InitMutex);
+
+ if(DBA__TheNdb != 0){
+ NdbMutex_Unlock(&DBA__InitMutex);
+ return DBA_NO_ERROR;
+ }
+
+ DBA__TheNdb = new Ndb("Newton");
+ DBA__TheNdb->init(1024);
+ if(DBA__TheNdb->waitUntilReady() != 0){
+ delete DBA__TheNdb; DBA__TheNdb = 0;
+ NdbMutex_Unlock(&DBA__InitMutex);
+ return DBA_NDB_ERROR;
+ }
+ DBA__TheNewtonMutex = NdbMutex_Create();
+ DBA__TheNBP = new NewtonBatchProcess(* DBA__TheNdb, * DBA__TheNewtonMutex);
+ DBA__TheNBP->doStart();
+ NdbMutex_Unlock(&DBA__InitMutex);
+ return DBA_NO_ERROR;
+}
+
+
+/**
+ * Closes the database.
+ *
+ * @return Error status
+ */
+extern "C"
+DBA_Error_t
+DBA_Close(void){
+
+ NdbMutex_Lock(&DBA__InitMutex);
+
+ if(DBA__TheNBP != 0)
+ DBA__TheNBP->doStop(true);
+ delete DBA__TheNBP;
+ DBA__TheNBP = 0;
+
+ if(DBA__TheNdb != 0)
+ delete DBA__TheNdb;
+ DBA__TheNdb = 0;
+
+ if(DBA__TheNewtonMutex != 0)
+ NdbMutex_Destroy(DBA__TheNewtonMutex);
+ DBA__TheNewtonMutex = 0;
+
+ NdbMutex_Unlock(&DBA__InitMutex);
+ return DBA_NO_ERROR;
+}
diff --git a/storage/ndb/src/old_files/newtonapi/dba_internal.hpp b/storage/ndb/src/old_files/newtonapi/dba_internal.hpp
new file mode 100644
index 00000000000..84ae7ba222b
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_internal.hpp
@@ -0,0 +1,122 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DBA_INTERNAL_HPP
+#define DBA_INTERNAL_HPP
+
+#include <ndb_global.h>
+
+extern "C" {
+#include "dba.h"
+}
+
+#include <NdbApi.hpp>
+#include <NdbMutex.h>
+#include <NdbOut.hpp>
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+#ifdef DEBUG
+#define DBA_DEBUG(x) ndbout << x << endl
+#else
+#define DBA_DEBUG(x)
+#endif
+
+extern Ndb * DBA__TheNdb;
+extern NdbMutex * DBA__TheNewtonMutex;
+
+extern unsigned DBA__SentTransactions;
+extern unsigned DBA__RecvTransactions;
+
+/**
+ * Configuration
+ */
+extern int DBA__NBP_Intervall; // Param 0
+extern int DBA__BulkReadCount; // Param 1
+extern int DBA__StartTransactionTimout; // Param 2
+extern int DBA__NBP_Force; // Param 3
+
+/**
+ * Error handling
+ */
+void DBA__SetLatestError(DBA_Error_t, DBA_ErrorCode_t, const char *, ...);
+
+/**
+ * Magic string
+ *
+ * Used to make sure that user passes correct pointers
+ */
+const int DBA__MagicLength = 4;
+const char DBA__TheMagic[DBA__MagicLength] = { 'K', 'E', 'S', 'O' };
+
+struct DBA_Binding {
+ char magic[DBA__MagicLength];
+ int checkSum;
+
+ char * tableName;
+ int structSz;
+
+ int noOfKeys;
+ int *keyIds;
+ int *keyOffsets;
+
+ int noOfColumns;
+ int *columnIds;
+ int *columnOffsets;
+
+ int noOfSubBindings;
+ struct DBA_Binding **subBindings;
+ int * subBindingOffsets;
+
+ int data[1];
+};
+
+struct DBA__DataTypesMapping {
+ DBA_DataTypes_t newtonType;
+ NdbDictionary::Column::Type ndbType;
+};
+
+const DBA__DataTypesMapping DBA__DataTypesMappings[] = {
+ { DBA_CHAR, NdbDictionary::Column::Char },
+ { DBA_INT, NdbDictionary::Column::Int }
+};
+
+const int DBA__NoOfMappings = sizeof(DBA__DataTypesMappings)/
+ sizeof(DBA__DataTypesMapping);
+
+/**
+ * Validate magic string and checksum of a binding
+ */
+bool DBA__ValidBinding(const DBA_Binding_t * bindings);
+bool DBA__ValidBindings(const DBA_Binding_t * const * pBindings, int n);
+
+/**
+ * Recursive equalGetValue (used for read)
+ * equalSetValue (used for write)
+ * equal (used for delete)
+ */
+bool DBA__EqualGetValue(NdbOperation *, const DBA_Binding_t *, void *);
+bool DBA__EqualSetValue(NdbOperation *, const DBA_Binding_t *, const void *);
+bool DBA__Equal (NdbOperation *, const DBA_Binding_t *, const void *);
+
+inline void require(bool test){
+ if(!test)
+ abort();
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/newtonapi/dba_process.cpp b/storage/ndb/src/old_files/newtonapi/dba_process.cpp
new file mode 100644
index 00000000000..ddb6e62f180
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_process.cpp
@@ -0,0 +1,123 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "dba_process.hpp"
+
+NewtonBatchProcess::NewtonBatchProcess(Ndb & ndb, NdbMutex & mutex) :
+ theNdb(ndb),
+ theMutex(mutex)
+{
+ theThread = 0;
+ startStopMutex = NdbMutex_Create();
+
+ _running = false;
+ _stop = false;
+}
+
+NewtonBatchProcess::~NewtonBatchProcess(){
+ doStop(true);
+
+ if(theThread != 0)
+ NdbThread_Destroy(&theThread);
+
+ if(startStopMutex != 0)
+ NdbMutex_Destroy(startStopMutex);
+ startStopMutex = 0;
+}
+
+extern "C"
+void*
+runNDB_C(void * _nbp){
+ NewtonBatchProcess * nbp = (NewtonBatchProcess*)_nbp;
+ nbp->_running = true;
+ nbp->run();
+ nbp->_running = false;
+
+ /**
+ * This sleep is to make sure that the transporter
+ * send thread will come in and send any
+ * signal buffers that this thread may have allocated.
+ * If that doesn't happen an error will occur in OSE
+ * when trying to restore a signal buffer allocated by a thread
+ * that have been killed.
+ */
+ NdbSleep_MilliSleep(50);
+ NdbThread_Exit(0);
+ return 0;
+}
+
+void
+NewtonBatchProcess::doStart(){
+ NdbMutex_Lock(startStopMutex);
+ if(_running && !_stop){
+ NdbMutex_Unlock(startStopMutex);
+ return ;
+ }
+
+ while(_running){
+ NdbMutex_Unlock(startStopMutex);
+ NdbSleep_MilliSleep(200);
+ NdbMutex_Lock(startStopMutex);
+ }
+
+ require(!_running);
+ _stop = false;
+
+ if(theThread != 0)
+ NdbThread_Destroy(&theThread);
+
+ theThread = NdbThread_Create(runNDB_C,
+ (void**)this,
+ 65535,
+ "Newton_BP",
+ NDB_THREAD_PRIO_LOWEST);
+
+ NdbMutex_Unlock(startStopMutex);
+}
+
+void
+NewtonBatchProcess::doStop(bool wait){
+ NdbMutex_Lock(startStopMutex);
+ _stop = true;
+
+ if(wait){
+ while(_running){
+ NdbSleep_MilliSleep(200);
+ }
+ }
+ NdbMutex_Unlock(startStopMutex);
+}
+
+bool
+NewtonBatchProcess::isRunning() const {
+ return _running;
+}
+
+bool
+NewtonBatchProcess::isStopping() const {
+ return _stop;
+}
+
+void
+NewtonBatchProcess::run(){
+ while(!_stop){
+ NdbMutex_Lock(&theMutex);
+ theNdb.sendPollNdb(0, 1, DBA__NBP_Force);
+ NdbMutex_Unlock(&theMutex);
+ NdbSleep_MilliSleep(DBA__NBP_Intervall);
+ }
+}
diff --git a/storage/ndb/src/old_files/newtonapi/dba_process.hpp b/storage/ndb/src/old_files/newtonapi/dba_process.hpp
new file mode 100644
index 00000000000..ef24fbd9142
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_process.hpp
@@ -0,0 +1,56 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NEWTON_BP_HPP
+#define NEWTON_BP_HPP
+
+#include "dba_internal.hpp"
+
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbSleep.h>
+
+extern "C" void* runNDB_C(void * nbp);
+
+/**
+ * This class implements the NewtonBatchProcess
+ */
+class NewtonBatchProcess {
+ friend void* runNDB_C(void * nbp);
+public:
+ NewtonBatchProcess(Ndb &, NdbMutex &);
+ ~NewtonBatchProcess();
+
+ void doStart();
+ void doStop(bool wait);
+
+ bool isRunning() const ;
+ bool isStopping() const ;
+
+private:
+ void run();
+
+ bool _running;
+ bool _stop;
+
+ Ndb & theNdb;
+ NdbMutex & theMutex;
+
+ NdbThread * theThread;
+ NdbMutex * startStopMutex;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/newtonapi/dba_schema.cpp b/storage/ndb/src/old_files/newtonapi/dba_schema.cpp
new file mode 100644
index 00000000000..1bf21f1fe80
--- /dev/null
+++ b/storage/ndb/src/old_files/newtonapi/dba_schema.cpp
@@ -0,0 +1,150 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "dba_internal.hpp"
+#include "NdbSchemaCon.hpp"
+
+static bool getNdbAttr(DBA_DataTypes_t,
+ Size_t,
+ int * attrSize,
+ int * arraySize,
+ AttrType * attrType);
+
+extern "C"
+DBA_Error_t
+DBA_CreateTable(const char* TableName,
+ int NbColumns,
+ const DBA_ColumnDesc_t Columns[] ){
+
+ if(DBA_TableExists(TableName))
+ return DBA_NO_ERROR;
+
+ NdbSchemaCon * schemaCon = NdbSchemaCon::startSchemaTrans(DBA__TheNdb);
+ if(schemaCon == 0){
+ DBA__SetLatestError(DBA_NDB_ERROR, 0,
+ "Internal NDB error: No schema transaction");
+ return DBA_NDB_ERROR;
+ }
+
+ NdbSchemaOp * schemaOp = schemaCon->getNdbSchemaOp();
+ if(schemaOp == 0){
+ NdbSchemaCon::closeSchemaTrans(schemaCon);
+ DBA__SetLatestError(DBA_NDB_ERROR, 0,
+ "Internal NDB error: No schema op");
+ return DBA_NDB_ERROR;
+ }
+
+ if(schemaOp->createTable( TableName,
+ 8, // Data Size
+ TupleKey,
+ 2, // Index size
+ All,
+ 6,
+ 78,
+ 80,
+ 1,
+ false) == -1){
+ NdbSchemaCon::closeSchemaTrans(schemaCon);
+ DBA__SetLatestError(DBA_NDB_ERROR, 0,
+ "Internal NDB error: Create table failed");
+ return DBA_NDB_ERROR;
+ }
+
+ for (int i = 0; i < NbColumns; i++){
+ int attrSize;
+ int arraySize;
+ AttrType attrType;
+
+ if(!getNdbAttr(Columns[i].DataType, Columns[i].Size,
+ &attrSize,
+ &arraySize,
+ &attrType)){
+ NdbSchemaCon::closeSchemaTrans(schemaCon);
+ DBA__SetLatestError(DBA_APPLICATION_ERROR, 0,
+ "Invalid datatype/size combination");
+ return DBA_APPLICATION_ERROR;
+ }
+
+ if(schemaOp->createAttribute( Columns[i].Name,
+ Columns[i].IsKey ? TupleKey : NoKey,
+ attrSize,
+ arraySize,
+ attrType) == -1){
+ NdbSchemaCon::closeSchemaTrans(schemaCon);
+ DBA__SetLatestError(DBA_NDB_ERROR, 0,
+ "Internal NDB error: Create attribute failed");
+ return DBA_NDB_ERROR;
+ }
+ }
+
+ if(schemaCon->execute() == -1){
+ NdbSchemaCon::closeSchemaTrans(schemaCon);
+ DBA__SetLatestError(DBA_NDB_ERROR, 0,
+ "Internal NDB error: Execute schema failed");
+ return DBA_NDB_ERROR;
+ }
+
+ NdbSchemaCon::closeSchemaTrans(schemaCon);
+
+ return DBA_NO_ERROR;
+}
+
+DBA_Error_t
+DBA_DropTable( char* TableName ){
+ return DBA_NOT_IMPLEMENTED;
+}
+
+Boolean_t
+DBA_TableExists( const char* TableName ){
+ NdbDictionary::Dictionary * dict = DBA__TheNdb->getDictionary();
+ if(dict == 0){
+ return 0;
+ }
+
+ const NdbDictionary::Table * tab = dict->getTable(TableName);
+ if(tab == 0){
+ return 0;
+ }
+ return 1;
+}
+
+static
+bool
+getNdbAttr(DBA_DataTypes_t type,
+ Size_t size,
+ int * attrSize,
+ int * arraySize,
+ AttrType * attrType) {
+
+ if(type == DBA_CHAR){
+ * attrType = String;
+ * attrSize = 8;
+ * arraySize = size;
+ return true;
+ }
+
+ * attrType = Signed;
+ if((size % 4) == 0){
+ * attrSize = 32;
+ * arraySize = size / 4;
+ return true;
+ }
+
+ * attrSize = 8;
+ * arraySize = size;
+
+ return true;
+}
diff --git a/storage/ndb/src/old_files/rep/ExtSender.cpp b/storage/ndb/src/old_files/rep/ExtSender.cpp
new file mode 100644
index 00000000000..cf31001a85f
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/ExtSender.cpp
@@ -0,0 +1,149 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ExtSender.hpp"
+
+/*****************************************************************************
+ * Constructor / Destructor / Init / Get / Set
+ *****************************************************************************/
+
+/**
+ * @todo: signalErrorHandler is not finished. Just infrastructure.
+ */
+
+ExtSender::ExtSender() {
+ m_tf = NULL;
+ m_nodeId = 0;
+ m_ownRef = 0;
+}
+
+ExtSender::~ExtSender() {
+ if (m_tf) delete m_tf;
+}
+
+void
+ExtSender::setNodeId(Uint32 nodeId)
+{
+#if 0
+ ndbout_c("ExtSender: Set nodeid to %d", nodeId);
+#endif
+
+ m_nodeId = nodeId;
+}
+
+Uint32
+ExtSender::getOwnRef() const
+{
+ if(!m_ownRef) REPABORT("No m_ownRef set");
+
+ return m_ownRef;
+}
+
+void
+ExtSender::setOwnRef(Uint32 ref)
+{
+ // Can only be set once
+ if (m_ownRef != 0) REPABORT("Trying to change m_ownRef");
+
+ m_ownRef = ref;
+}
+
+/*****************************************************************************
+ * Usage
+ *****************************************************************************/
+
+int
+ExtSender::sendSignal(class NdbApiSignal * s) {
+#if 0
+ ndbout_c("ExtSender: Sending signal %d to %d",
+ s->readSignalNumber(), m_nodeId);
+#endif
+
+ if (m_tf == NULL || m_nodeId == 0 || s==0) abort();
+ m_tf->lock_mutex();
+ int retvalue = m_tf->sendSignal(s, m_nodeId);
+ if (retvalue) {
+ RLOG(("sendSignal returned %d for send to node %d", retvalue, m_nodeId));
+ }
+#if 0
+ ndbout_c("ExtSender: Sent signal to %d", m_nodeId);
+#endif
+ m_tf->unlock_mutex();
+ return retvalue;
+}
+
+int
+ExtSender::sendFragmentedSignal(NdbApiSignal * s,
+ LinearSectionPtr ptr[3],
+ Uint32 sections) {
+ if (m_tf == NULL || m_nodeId == 0) abort();
+ m_tf->lock_mutex();
+ int retvalue = m_tf->sendFragmentedSignal(s, m_nodeId, ptr, sections);
+ if (retvalue) {
+ RLOG(("sendFragmentedSignal returned %d for send to node %d",
+ retvalue, m_nodeId));
+ }
+ m_tf->unlock_mutex();
+ return retvalue;
+}
+
+/**
+ * Check that TransporterFacade is connected to at least one DB node
+ */
+bool
+ExtSender::connected(Uint32 timeOutMillis){
+#if 0
+ ndbout_c("ExtSender: Waiting for remote component to be ready!");
+#endif
+
+ NDB_TICKS start = NdbTick_CurrentMillisecond();
+ NDB_TICKS now = start;
+ // while(m_tf->theClusterMgr->getNoOfConnectedNodes() == 0 &&
+ while((m_tf->get_an_alive_node() == 0) &&
+ (timeOutMillis == 0 || (now - start) < timeOutMillis)){
+ NdbSleep_MilliSleep(100);
+ now = NdbTick_CurrentMillisecond();
+ }
+ return m_tf->theClusterMgr->getNoOfConnectedNodes() > 0;
+}
+
+bool
+ExtSender::connected(Uint32 timeOutMillis, Uint32 nodeId){
+ NDB_TICKS start = NdbTick_CurrentMillisecond();
+ NDB_TICKS now = start;
+
+ // while(m_tf->theClusterMgr->getNoOfConnectedNodes() == 0 &&
+ while((m_tf->get_node_alive(nodeId) != 0) &&
+ (timeOutMillis == 0 || (now - start) < timeOutMillis)){
+ NdbSleep_MilliSleep(100);
+ now = NdbTick_CurrentMillisecond();
+ }
+ return m_tf->theClusterMgr->getNoOfConnectedNodes() > 0;
+}
+
+NdbApiSignal *
+ExtSender::getSignal()
+{
+ /**
+ * @todo This should use some kind of list of NdbApiSignals,
+ * similar to the NDBAPI and the MGRSRVR.
+ * The best thing would be to have set of code
+ * shared between the programs.
+ * Thus the NDBAPI and MGMSRVR should be refactored.
+ * /Lars
+ */
+ return new NdbApiSignal(getOwnRef());
+}
diff --git a/storage/ndb/src/old_files/rep/ExtSender.hpp b/storage/ndb/src/old_files/rep/ExtSender.hpp
new file mode 100644
index 00000000000..0bdabd68f37
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/ExtSender.hpp
@@ -0,0 +1,76 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 EXT_SENDER_HPP
+#define EXT_SENDER_HPP
+
+#include <NdbSleep.h>
+#include <TransporterFacade.hpp>
+#include <NdbApiSignal.hpp>
+#include <rep/rep_version.hpp>
+
+/**
+ * @todo Johan comment:
+ *
+ * ext->sendSignal should return something if send failed.
+ * I.e., i think all methods sending a signal should return int
+ * so that we can take care of errors. ALternatively take care of
+ * the error like this:
+ * if(ext->sendSignal(..) < 0 )
+ * handleSignalError(...)
+ *
+ * or a combination....
+ *
+ * Should go through all places that sends signals and check that
+ * they do correct error handling.
+ */
+
+/**
+ * @class ExtSender
+ * @brief Manages connection to a transporter facade
+ */
+class ExtSender {
+public:
+ /***************************************************************************
+ * Constructor / Destructor / Init / Get / Set (Only set once!)
+ ***************************************************************************/
+ ExtSender();
+ ~ExtSender();
+
+ void setTransporterFacade(TransporterFacade * tf) { m_tf = tf; }
+ void setNodeId(Uint32 nodeId);
+ Uint32 getOwnRef() const;
+ void setOwnRef(Uint32 ref);
+
+ /***************************************************************************
+ * Usage
+ ***************************************************************************/
+ int sendSignal(NdbApiSignal * s);
+ int sendFragmentedSignal(NdbApiSignal * s, LinearSectionPtr ptr[3],
+ Uint32 sections);
+
+ bool connected(Uint32 TimeOutInMilliSeconds);
+ bool connected(Uint32 TimeOutInMilliSeconds, Uint32 nodeId);
+
+ NdbApiSignal * getSignal();
+
+private:
+ TransporterFacade * m_tf;
+ Uint32 m_nodeId;
+ Uint32 m_ownRef;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/Makefile b/storage/ndb/src/old_files/rep/Makefile
new file mode 100644
index 00000000000..9688a68ec74
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/Makefile
@@ -0,0 +1,28 @@
+include .defs.mk
+
+#
+# This "kernel" type should be removed (only need types)
+#
+TYPE := repserver kernel
+
+DIRS := adapters storage state transfer repapi
+
+BIN_TARGET := ndb_rep
+
+BIN_TARGET_LIBS :=
+BIN_TARGET_ARCHIVES += editline repstorage repadapters reprequestor reptransfer mgmapi NDB_API mgmsrvcommon
+
+SOURCES = \
+ RepMain.cpp \
+ Requestor.cpp \
+ RequestorSubscriptions.cpp \
+ \
+ RepComponents.cpp \
+ RepCommandInterpreter.cpp \
+ RepApiService.cpp \
+ RepApiInterpreter.cpp \
+ SignalQueue.cpp \
+ ExtSender.cpp \
+ dbug_hack.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/NodeConnectInfo.hpp b/storage/ndb/src/old_files/rep/NodeConnectInfo.hpp
new file mode 100644
index 00000000000..403f92a5999
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/NodeConnectInfo.hpp
@@ -0,0 +1,29 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NODE_CONNECTINFO_HPP
+#define NODE_CONNECTINFO_HPP
+
+#include <ndb_types.h>
+
+struct NodeConnectInfo {
+ NodeConnectInfo(Uint16 n, bool c): nodeId(n), connected(c) {};
+ Uint32 nodeId;
+ bool connected;
+};
+
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/README b/storage/ndb/src/old_files/rep/README
new file mode 100644
index 00000000000..7be5e230eb3
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/README
@@ -0,0 +1,147 @@
+ ===========================================
+ MySQL Replication Servers
+ Lars Thalmann and Johan Andersson
+ 2003 MySQL AB
+ ===========================================
+
+-------------------------------------------------------------------------------
+
+ PRIMARY SYSTEM STANDBY SYSTEM
+ REPLICATION SERVER REPLICATION SERVER
+ (PS or SOURCE SYSTEM) (SS or DESTINATION SYSTEM)
+ +------------------+ +-------------------------+
+ | RepMain | | RepMain [Requests] |
+ | | +-------------------------+
+ | | | Requestor [Executes] |
+ +------------------+ +-------------------------+
+ PS --- | ExtNDB | TransPS | --- | TransSS | AppNDB | --- SS
+ +------------------+ +-------------------------+
+ (GCIContainer) (GCIContainer)
+ (RepState)
+
+ Figure 1: Replication Server Threads
+
+Component List
+--------------
+RepMain
+ Main thread that runs command-line interpreter [Requests]
+
+Requestor
+ Thread that runs RepState::execute [Executes]
+
+ExtNDB
+ Extracts transaction epochs from NDB Cluster
+
+TransPS, TransSS
+ Transfers information (control and epoch buffers) between
+ Replication Servers.
+
+AppNDB
+ Applies transaction epochs to NDB Cluster
+
+-------------------------------------------------------------------------------
+
+ RepState Control
+ Object
+ +------------------+
+ | RepState |
+ | [Requests] |
+ | [Executes] |
+ +------------------+
+ | RepStateRequest | --- ExtSender
+ +------------------+
+
+ Figure 2: RepState Object
+
+
+The RepState object is shared by all components.
+
+
+-------------------------------------------------------------------------------
+
+Dependent Directories
+---------------------
+rep/adapters Appliers and Extractors
+ All code dependent on the database system
+
+rep/transfer
+ Depends on NDB transporters
+
+rep/state
+ Shared resources for all components
+
+Independent Directories
+-----------------------
+rep/storage Storage of epochs
+ Should not depend on any transporters/NDB specific
+
+rep/repstate
+ Should only have a reference to an ExtSender for the external PS REP node
+
+
+-------------------------------------------------------------------------------
+
+Replication Teminology
+----------------------
+GLOBAL CHECKPOINT <global checkpoint id - gci>
+A global checkpoint is a point in time when all database server
+are synchronized.
+
+NODE
+A database server with information.
+
+NODE GROUP <node group id>
+A set of database servers, all storing the same information.
+
+SUBSCRIPTION <subscription id>.
+A "subscription" is a collection of services that a source system
+provides. The main to services belonging to a subscription are
+"log" and "scan". Log provides the replication servers with
+log entries (epochs) and scan provides the replication servers
+with scanned data (also stored in epochs).
+
+EPOCH <subscription id, gci>
+An "epoch" is a log of all database changes between two time points.
+(An epoch can have redundant log entries.) An epoch is named by the
+number of the time slice between the two time points.
+
+EPOCH BUFFER <subscription id, gci, node group id>
+An "epoch buffer" is a part of the log belonging to an epoch. An
+epoch buffer does not contain any redundancy.
+
+Two epoch buffers with the same subscription id and gci can be
+"complements" or "duplicates" to each other. If they are complements,
+they store different information, if they are duplicates then they
+store equivalent information (the information need not be identical,
+but it is equivalent for the purpose of restoring the original
+information). If they are duplicates then they have the same name,
+i.e. same subscription id, gci, and node group id.
+
+CHANNEL <subscription id>
+A "channel" is a collection of epoch buffers belonging to
+a specific subscription. (The channel can exist before it is
+assigned to a subscription.)
+
+SUBSCRIPTION CONSISTENT
+A database is "subscription consistent" or "consistent with respect
+to a subscription" if ...
+
+Architectural Terminology
+-------------------------
+ADAPTER
+An "adapter" is either an applier or an extractor.
+
+APPLIER
+An "applier" is a a collection of threads in the replication server
+that applies epochs to a destination database system.
+
+EXTRACTOR
+An "extractor" is a collection of theads in the replication server
+that receives epochs from a source database system.
+
+TRANSFER COMPONENT
+A "transfer component" is a thread in the replication server that is
+responsible for the connection with another replication server.
+
+REQUESTOR
+A thread in the replication server that controls replication.
diff --git a/storage/ndb/src/old_files/rep/RepApiInterpreter.cpp b/storage/ndb/src/old_files/rep/RepApiInterpreter.cpp
new file mode 100644
index 00000000000..6e6f150713a
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepApiInterpreter.cpp
@@ -0,0 +1,80 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "RepApiInterpreter.hpp"
+#include <signaldata/GrepImpl.hpp>
+
+RepApiInterpreter::RepApiInterpreter(RepComponents * comps, int port)
+{
+ m_repComponents = comps;
+ m_repState = comps->getRepState();
+ m_port = port;
+ ss = new SocketServer();
+ serv = new RepApiService(*this);
+}
+
+
+RepApiInterpreter::~RepApiInterpreter()
+{
+}
+
+void
+RepApiInterpreter::startInterpreter()
+{
+ if(!ss->setup(serv, m_port)){
+ sleep(1);
+ delete ss;
+ delete serv;
+ }
+ ss->startServer();
+}
+
+
+void
+RepApiInterpreter::stopInterpreter()
+{
+ delete ss;
+}
+
+
+Properties *
+RepApiInterpreter::execCommand(const Properties & props)
+{
+ Properties * result = new Properties();
+ Uint32 req = 0;
+ Uint32 epoch = 0;
+ props.get("request", &req);
+ props.get("epoch", &epoch);
+ GrepError::Code err = m_repState->protectedRequest((GrepReq::Request)req,
+ epoch);
+ result->put("err", err);
+ return result;
+}
+
+Properties *
+RepApiInterpreter::getStatus()
+{
+
+ return m_repState->getStatus();
+}
+
+
+Properties *
+RepApiInterpreter::query(Uint32 counter, Uint32 replicationId)
+{
+ return m_repState->query((QueryCounter)counter, replicationId);
+}
+
diff --git a/storage/ndb/src/old_files/rep/RepApiInterpreter.hpp b/storage/ndb/src/old_files/rep/RepApiInterpreter.hpp
new file mode 100644
index 00000000000..78f190156b3
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepApiInterpreter.hpp
@@ -0,0 +1,54 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REP_API_INTERPRETER_HPP
+#define REP_API_INTERPRETER_HPP
+
+#include <editline/editline.h>
+
+#include <rep/RepComponents.hpp>
+#include <rep/state/RepState.hpp>
+#include <rep/RepApiService.hpp>
+#include <signaldata/GrepImpl.hpp>
+#include <Properties.hpp>
+
+/**
+ * @class RepCommandInterpreter
+ * @brief
+ */
+
+class RepApiInterpreter {
+public:
+ RepApiInterpreter(class RepComponents * comps, int port);
+ ~RepApiInterpreter();
+ void startInterpreter();
+ void stopInterpreter();
+ Properties * execCommand(const Properties & props);
+ Properties * getStatus();
+ Properties * query(Uint32 counter, Uint32 replicationId);
+ bool readAndExecute();
+
+private:
+ char * readline_gets() const;
+ void request(Uint32 request);
+ int m_port;
+ class RepComponents * m_repComponents;
+ class RepState * m_repState;
+ SocketServer * ss;
+ RepApiService * serv;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/RepApiService.cpp b/storage/ndb/src/old_files/rep/RepApiService.cpp
new file mode 100644
index 00000000000..d07f7a59375
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepApiService.cpp
@@ -0,0 +1,318 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include <Parser.hpp>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <socket_io.h>
+#include "RepApiService.hpp"
+#include "RepApiInterpreter.hpp"
+#include "repapi/repapi.h"
+#include <NdbMutex.h>
+#include <OutputStream.hpp>
+
+/**
+ const char * name;
+ const char * realName;
+ const Type type;
+ const ArgType argType;
+ const ArgRequired argRequired;
+ const ArgMinMax argMinMax;
+ const int minVal;
+ const int maxVal;
+ void (T::* function)(const class Properties & args);
+ const char * description;
+*/
+
+#define REP_CMD(name, fun, desc) \
+ { name, \
+ 0, \
+ ParserRow<RepApiSession>::Cmd, \
+ ParserRow<RepApiSession>::String, \
+ ParserRow<RepApiSession>::Optional, \
+ ParserRow<RepApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ fun, \
+ desc }
+
+#define REP_ARG(name, type, opt, desc) \
+ { name, \
+ 0, \
+ ParserRow<RepApiSession>::Arg, \
+ ParserRow<RepApiSession>::type, \
+ ParserRow<RepApiSession>::opt, \
+ ParserRow<RepApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ desc }
+
+#define REP_ARG2(name, type, opt, min, max, desc) \
+ { name, \
+ 0, \
+ ParserRow<RepApiSession>::Arg, \
+ ParserRow<RepApiSession>::type, \
+ ParserRow<RepApiSession>::opt, \
+ ParserRow<RepApiSession>::IgnoreMinMax, \
+ min, max, \
+ 0, \
+ desc }
+
+#define REP_END() \
+ { 0, \
+ 0, \
+ ParserRow<RepApiSession>::Arg, \
+ ParserRow<RepApiSession>::Int, \
+ ParserRow<RepApiSession>::Optional, \
+ ParserRow<RepApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0 }
+
+#define REP_CMD_ALIAS(name, realName, fun) \
+ { name, \
+ realName, \
+ ParserRow<RepApiSession>::CmdAlias, \
+ ParserRow<RepApiSession>::Int, \
+ ParserRow<RepApiSession>::Optional, \
+ ParserRow<RepApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0 }
+
+#define REP_ARG_ALIAS(name, realName, fun) \
+ { name, \
+ realName, \
+ ParserRow<RepApiSession>::ArgAlias, \
+ ParserRow<RepApiSession>::Int, \
+ ParserRow<RepApiSession>::Optional, \
+ ParserRow<RepApiSession>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0 }
+
+
+const
+ParserRow<RepApiSession> commands[] =
+{
+
+ REP_CMD("rep" , &RepApiSession::execCommand, ""),
+ REP_ARG("request", Int, Mandatory, "Grep::Request."),
+ REP_ARG("id", Int, Mandatory, "Replication id "),
+ REP_ARG("epoch", Int, Optional, "Epoch. Used by stop epoch ..."),
+
+ REP_CMD("rep status" , &RepApiSession::getStatus, ""),
+ REP_ARG("request", Int, Optional, "Grep::Request."),
+
+ REP_CMD("rep query" , &RepApiSession::query, ""),
+ REP_ARG("id", Int, Mandatory, "Replication Id"),
+ REP_ARG("counter", Int, Mandatory, "QueryCounter."),
+ REP_ARG("request", Int, Mandatory, "Grep::Request."),
+
+ REP_END()
+};
+RepApiSession::RepApiSession(NDB_SOCKET_TYPE sock,
+ class RepApiInterpreter & rep)
+ : SocketServer::Session(sock)
+ , m_rep(rep)
+{
+ m_input = new SocketInputStream(sock);
+ m_output = new SocketOutputStream(sock);
+ m_parser = new Parser<RepApiSession>(commands, *m_input, true, true, true);
+}
+
+RepApiSession::RepApiSession(FILE * f, class RepApiInterpreter & rep)
+ : SocketServer::Session(1)
+ , m_rep(rep)
+{
+ m_input = new FileInputStream(f);
+ m_parser = new Parser<RepApiSession>(commands, *m_input, true, true, true);
+}
+
+RepApiSession::~RepApiSession()
+{
+ delete m_input;
+ delete m_parser;
+}
+
+void
+RepApiSession::runSession()
+{
+ Parser_t::Context ctx;
+ while(!m_stop){
+ m_parser->run(ctx, * this);
+ if(ctx.m_currentToken == 0)
+ break;
+
+ switch(ctx.m_status){
+ case Parser_t::Ok:
+ for(size_t i = 0; i<ctx.m_aliasUsed.size(); i++)
+ ndbout_c("Used alias: %s -> %s",
+ ctx.m_aliasUsed[i]->name, ctx.m_aliasUsed[i]->realName);
+ break;
+ case Parser_t::NoLine:
+ case Parser_t::EmptyLine:
+ break;
+ default:
+ break;
+ }
+ }
+ NDB_CLOSE_SOCKET(m_socket);
+}
+
+void
+RepApiSession::execCommand(Parser_t::Context & /* unused */,
+ const class Properties & args)
+{
+ Uint32 err;
+ Uint32 replicationId;
+ args.get("id", &replicationId);
+ Properties * result = m_rep.execCommand(args);
+ if(result == NULL) {
+ m_output->println("global replication reply");
+ m_output->println("result: %d", -1);
+ m_output->println("id: %d",replicationId);
+ m_output->println("");
+ return;
+ }
+ result->get("err", &err);
+ m_output->println("global replication reply");
+ m_output->println("result: %d", err);
+ m_output->println("id: %d", 0);
+ m_output->println("");
+ delete result;
+}
+
+
+void
+RepApiSession::getStatus(Parser_t::Context & /* unused */,
+ const class Properties & args)
+{
+ Uint32 err;
+ Properties * result = m_rep.getStatus();
+ result->get("err", &err);
+ Uint32 subId;
+ result->get("subid", &subId);
+ Uint32 subKey;
+ result->get("subkey", &subKey);
+ Uint32 connected_rep;
+ result->get("connected_rep", &connected_rep);
+ Uint32 connected_db;
+ result->get("connected_db", &connected_db);
+ Uint32 state;
+ result->get("state", &state);
+ Uint32 state_sub;
+ result->get("state", &state_sub);
+
+ m_output->println("global replication status reply");
+ m_output->println("result: %d",0);
+ m_output->println("id: %d",0);
+ m_output->println("subid: %d", subId);
+ m_output->println("subkey: %d", subKey);
+ m_output->println("connected_rep: %d", connected_rep);
+ m_output->println("connected_db: %d", connected_db);
+ m_output->println("state_sub: %d", state_sub);
+ m_output->println("state: %d", state);
+ m_output->println("");
+ delete result;
+}
+
+
+void
+RepApiSession::query(Parser_t::Context & /* unused */,
+ const class Properties & args)
+{
+ Uint32 err;
+ Uint32 counter, replicationId;
+ args.get("counter", &counter);
+ args.get("id", &replicationId);
+ Properties * result = m_rep.query(counter, replicationId);
+ if(result == NULL) {
+ m_output->println("global replication query reply");
+ m_output->println("result: %s","Failed");
+ m_output->println("id: %d",replicationId);
+ m_output->println("");
+ return;
+ }
+
+ BaseString first;
+ BaseString last;
+ Uint32 subid = 0, subkey = 0, no_of_nodegroups = 0;
+ Uint32 connected_rep = 0, connected_db = 0;
+ Uint32 state = 0 , state_sub = 0;
+ result->get("err", &err);
+ result->get("no_of_nodegroups", &no_of_nodegroups);
+ result->get("subid", &subid);
+ result->get("subkey", &subkey);
+ result->get("connected_rep", &connected_rep);
+ result->get("connected_db", &connected_db);
+ result->get("first", first);
+ result->get("last", last);
+ result->get("state", &state);
+ result->get("state_sub", &state_sub);
+ m_output->println("global replication query reply");
+ m_output->println("result: %s","Ok");
+ m_output->println("id: %d",replicationId);
+ m_output->println("no_of_nodegroups: %d",no_of_nodegroups);
+ m_output->println("subid: %d", subid);
+ m_output->println("subkey: %d", subkey);
+ m_output->println("connected_rep: %d", connected_rep);
+ m_output->println("connected_db: %d", connected_db);
+ m_output->println("state_sub: %d", state_sub);
+ m_output->println("state: %d", state);
+ m_output->println("first: %s", first.c_str());
+ m_output->println("last: %s", last.c_str());
+ m_output->println("");
+ delete result;
+}
+
+
+
+static const char *
+propToString(Properties *prop, const char *key) {
+ static char buf[32];
+ const char *retval = NULL;
+ PropertiesType pt;
+
+ prop->getTypeOf(key, &pt);
+ switch(pt) {
+ case PropertiesType_Uint32:
+ Uint32 val;
+ prop->get(key, &val);
+ snprintf(buf, sizeof buf, "%d", val);
+ retval = buf;
+ break;
+ case PropertiesType_char:
+ const char *str;
+ prop->get(key, &str);
+ retval = str;
+ break;
+ default:
+ snprintf(buf, sizeof buf, "(unknown)");
+ retval = buf;
+ }
+ return retval;
+}
+
+void
+RepApiSession::printProperty(Properties *prop, const char *key) {
+ m_output->println("%s: %s", key, propToString(prop, key));
+}
+
+void
+RepApiSession::stopSession(){
+
+}
diff --git a/storage/ndb/src/old_files/rep/RepApiService.hpp b/storage/ndb/src/old_files/rep/RepApiService.hpp
new file mode 100644
index 00000000000..e1137e53258
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepApiService.hpp
@@ -0,0 +1,59 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REP_APISERVICE_HPP
+#define REP_APISERVICE_HPP
+
+#include <Parser.hpp>
+#include <InputStream.hpp>
+#include <SocketServer.hpp>
+
+class RepApiInterpreter;
+
+class RepApiSession : public SocketServer::Session {
+ typedef Parser<RepApiSession> Parser_t;
+
+ class RepApiInterpreter & m_rep;
+ InputStream *m_input;
+ OutputStream *m_output;
+ Parser_t *m_parser;
+
+void printProperty(Properties *prop, const char *key);
+public:
+ RepApiSession(NDB_SOCKET_TYPE, class RepApiInterpreter &);
+ RepApiSession(FILE * f, class RepApiInterpreter & rep);
+ ~RepApiSession();
+
+ virtual void runSession();
+ virtual void stopSession();
+
+ void execCommand(Parser_t::Context & ctx, const class Properties & args);
+ void getStatus(Parser_t::Context & ctx, const class Properties & args);
+ void query(Parser_t::Context & ctx, const class Properties & args);
+
+};
+
+class RepApiService : public SocketServer::Service {
+ class RepApiInterpreter & m_rep;
+public:
+ RepApiService(class RepApiInterpreter & rep) : m_rep(rep) {}
+
+ RepApiSession * newSession(NDB_SOCKET_TYPE theSock){
+ return new RepApiSession(theSock, m_rep);
+ }
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/RepCommandInterpreter.cpp b/storage/ndb/src/old_files/rep/RepCommandInterpreter.cpp
new file mode 100644
index 00000000000..a0daf9529ab
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepCommandInterpreter.cpp
@@ -0,0 +1,456 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "RepCommandInterpreter.hpp"
+
+static const char*
+helpTextRep =
+"+-------------------------------------------------------------------------+\n"
+"| MySQL Replication Server |\n"
+"| Commands should be executed on the standby Replication Server |\n"
+"+-------------------------------------------------------------------------+\n"
+"| Simple Commands |\n"
+"+-------------------------------------------------------------------------+\n"
+"| START Start replication |\n"
+"| STATUS Show replication status |\n"
+"+-------------------------------------------------------------------------+\n"
+"| Advanced Commands |\n"
+"+-------------------------------------------------------------------------+\n"
+"| STOP <epoch no> Stop replication after epoch number <epoch no> |\n"
+"| STOP IMMEDIATELY Stop replication after applying the current epoch |\n"
+"| ADD TABLE <db>/<schema>/<tablename> |\n"
+"| Note: <db>/<schema>/<tablename> is case sensitive! |\n"
+"| Use 'STATUS' to see added tables. |\n"
+"| REMOVE TABLE <db>/<schema>/<tablename> |\n"
+"| Note: <db>/<schema>/<tablename> is case sensitive! |\n"
+"| ENABLE <protocol> Starts protocol |\n"
+"| DISABLE <protocol> Stops protocol |\n"
+"| DEBUG Toggle logging of replication messages on console |\n"
+"| |\n"
+"| <protocol> ::= REQUESTOR | TRANSFER | APPLY | DELETE |\n"
+"+-------------------------------------------------------------------------+\n"
+;
+
+/**
+ * @todo
+"| <protocol> ::= SUBID | SUBSCRIPTION |\n"
+"| <protocol> ::= METALOG | METASCAN | DATALOG | DATASCAN |\n"
+"| <system> ::= PRIMARY | STANDBY | TWOWAY |\n"
+"| CONNECT <system> Connects to NDB Cluster and other replication server |\n"
+"| DELETE Removes all epochs stored in replication servers |\n"
+"| DROP <tableid> Drops table in standby system identified by table id |\n"
+"| <epoch> ::= Any integer (naming the last epoch to be applied) |\n"
+*/
+
+RepCommandInterpreter::RepCommandInterpreter(RepComponents * comps)
+{
+ m_repComponents = comps;
+ m_repState = comps->getRepState();
+}
+
+RepCommandInterpreter::~RepCommandInterpreter()
+{
+}
+
+/**
+ * Read a string, and return a pointer to it.
+ *
+ * @return NULL on EOF.
+ */
+char *
+RepCommandInterpreter::readline_gets() const
+{
+ static char *line_read = (char *)NULL;
+
+ // Disable the default file-name completion action of TAB
+ // rl_bind_key ('\t', rl_insert);
+
+ /* If the buffer has already been allocated, return the memory
+ to the free pool. */
+ if (line_read)
+ {
+ NdbMem_Free(line_read);
+ line_read = (char *)NULL;
+ }
+
+ /* Get a line from the user. */
+ line_read = readline ("REP> ");
+
+ /* If the line has any text in it, save it on the history. */
+ if (line_read && *line_read)
+ add_history (line_read);
+
+ return (line_read);
+}
+
+bool emptyString(const char* s)
+{
+ if (s == NULL) {
+ return true;
+ }
+
+ for (unsigned int i = 0; i < strlen(s); ++i) {
+ if (! isspace(s[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Converts a string to a Uint32 pointed value!
+ */
+bool convert(const char* s, Uint32 * val)
+{
+ if (s == NULL) {
+ return false;
+ }
+
+ if (strlen(s) == 0) {
+ return false;
+ }
+
+ errno = 0;
+ char* p;
+ long v = strtol(s, &p, 10);
+ if (errno != 0) {
+ return false;
+ }
+ if (p != &s[strlen(s)]) {
+ return false;
+ }
+
+ *val = v;
+ return true;
+}
+
+void
+printError(GrepError::Code err)
+{
+ if (err == GrepError::NO_ERROR) { ndbout << "Ok" << endl; }
+ else { ndbout << GrepError::getErrorDesc(err) << endl; }
+}
+
+bool
+RepCommandInterpreter::readAndExecute()
+{
+ GrepError::Code err;
+
+ char* _line = readline_gets();
+ char * line;
+ if(_line == NULL) {
+ ndbout << endl;
+ return true;
+ }
+
+ line = strdup(_line);
+
+ if (emptyString(line)) {
+ return true;
+ }
+
+ /* I have to uncomment this, since otherwise <db>/<schema>/<table>
+ is converted to capitals, but it is case sensitive!
+ for (unsigned int i = 0; i < strlen(line); ++i) {
+ line[i] = toupper(line[i]);
+ }
+ */
+ // if there is anything in the line proceed
+ char* firstToken = strtok(line, " ");
+ for (unsigned int i = 0; i < strlen(firstToken); ++i) {
+ firstToken[i] = toupper(firstToken[i]);
+ }
+ char* allAfterFirstToken = strtok(NULL, "\0");
+
+ /**
+ * Commands for REP Client only
+ */
+ if (strcmp(firstToken, "ADD") == 0) {
+ if (m_repState->m_channel.getStateSub() !=
+ Channel::NO_SUBSCRIPTION_EXISTS) {
+ ndbout_c("Subscription already exists");
+ ndbout_c("Tables must be added before subscription exists");
+ return true;
+ }
+ char * secondToken = strtok(allAfterFirstToken, " ");
+ char * fullTableName = strtok(NULL, "\0");
+ if(fullTableName == NULL) {
+ ndbout_c("Table name not specified");
+ return true;
+ }
+ for (unsigned int i = 0; i < strlen(secondToken); ++i) {
+ secondToken[i] = toupper(secondToken[i]);
+ }
+
+ if (strcmp(secondToken, "TABLE") == 0) {
+ err = m_repState->protectedAddTable(fullTableName);
+ printError(err);
+ return true;
+ }
+ return true;
+ }
+ if (strcmp(firstToken, "REMOVE") == 0) {
+ if (m_repState->m_channel.getStateSub() !=
+ Channel::NO_SUBSCRIPTION_EXISTS) {
+ ndbout_c("Subscription already exists");
+ ndbout_c("Tables can not be removed after subscription is created");
+ return true;
+ }
+ char * secondToken = strtok(allAfterFirstToken, " ");
+ char * fullTableName = strtok(NULL, "\0");
+ if(fullTableName == NULL) {
+ ndbout_c("Table name not specified");
+ return true;
+ }
+ for (unsigned int i = 0; i < strlen(secondToken); ++i) {
+ secondToken[i] = toupper(secondToken[i]);
+ }
+
+ if (strcmp(secondToken, "TABLE") == 0) {
+ err = m_repState->protectedRemoveTable(fullTableName);
+ printError(err);
+ return true;
+ }
+ return true;
+ }
+ /**
+ * now, we can convert allAfterFirstToken to capitals
+ */
+ if(allAfterFirstToken != 0) {
+ for (unsigned int i = 0; i < strlen(allAfterFirstToken); ++i) {
+ allAfterFirstToken[i] = toupper(allAfterFirstToken[i]);
+ }
+ }
+ if (strcmp(firstToken, "CONNECT") == 0) {
+
+ if (strcmp(allAfterFirstToken, "PRIMARY") == 0) {
+ m_repComponents->connectPS();
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "STANDBY") == 0) {
+ m_repComponents->connectPS();
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "TWOWAY") == 0) {
+ m_repComponents->connectPS();
+ return true;
+ }
+ ndbout_c("Unknown argument: %s to command: %s",
+ allAfterFirstToken, firstToken);
+ return true;
+ }
+
+ if (strcmp(firstToken, "HELP") == 0) {
+ ndbout << helpTextRep;
+ return true;
+ }
+
+ if (strcmp(firstToken, "QUIT") == 0 ||
+ strcmp(firstToken, "BYE") == 0 ||
+ strcmp(firstToken, "EXIT") == 0) {
+ return false;
+ }
+
+ /**
+ * Commands for REP Server API
+ */
+ if (strcmp(firstToken, "STATUS") == 0 ||
+ strcmp(firstToken, "INFO") == 0 ||
+ strcmp(firstToken, "I") == 0) {
+ m_repState->protectedRequest(GrepReq::STATUS, 0);
+ return true;
+ }
+
+ if (strcmp(firstToken, "DEBUG") == 0) {
+ if (replogEnabled)
+ {
+ ndbout_c("Debugging disabled.");
+ replogEnabled = false;
+ }
+ else
+ {
+ ndbout_c("Debugging enabled.");
+ replogEnabled = true;
+ }
+ return true;
+ }
+
+ if (strcmp(firstToken, "ENABLE") == 0) {
+ if (strcmp(allAfterFirstToken, "REQUESTOR") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_REQUESTOR, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "TRANSFER") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_TRANSFER, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "APPLY") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_APPLY, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "DELETE") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_DELETE, 0);
+ printError(err);
+ return true;
+ }
+ ndbout_c("Unknown argument: %s to command: %s",
+ allAfterFirstToken, firstToken);
+ return true;
+ }
+
+ if (strcmp(firstToken, "DISABLE") == 0) {
+ if (strcmp(allAfterFirstToken, "REQUESTOR") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_REQUESTOR, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "TRANSFER") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_TRANSFER, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "APPLY") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_APPLY, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "DELETE") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_DELETE, 0);
+ printError(err);
+ return true;
+ }
+ ndbout_c("Unknown argument: %s to command: %s",
+ allAfterFirstToken, firstToken);
+ return true;
+ }
+
+ if (strcmp(firstToken, "START") == 0) {
+ if (allAfterFirstToken == NULL) {
+ err = m_repState->protectedRequest(GrepReq::START, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "SUBID") == 0) {
+ err = m_repState->protectedRequest(GrepReq::CREATE_SUBSCR, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "SUBSCR") == 0 ||
+ strcmp(allAfterFirstToken, "SUBSCRIPTION") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_SUBSCR, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "METALOG") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_METALOG, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "METASCAN") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_METASCAN, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "DATALOG") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_DATALOG, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "DATASCAN") == 0) {
+ err = m_repState->protectedRequest(GrepReq::START_DATASCAN, 0);
+ printError(err);
+ return true;
+ }
+ ndbout_c("Unknown argument: %s to command: %s",
+ allAfterFirstToken, firstToken);
+ return true;
+ }
+
+ if (strcmp(firstToken, "STOP") == 0) {
+ if (allAfterFirstToken == NULL) {
+ ndbout_c("Please use either 'STOP IMMEDIATELY' or 'STOP <epoch no>', "
+ "where\n<epoch no> is greater than or equal to "
+ "the last applied epoch.");
+ return true;
+ }
+
+ char * secondToken = strtok(allAfterFirstToken, " ");
+ char * subscription = strtok(NULL, "\0");
+ if (strcmp(secondToken, "SUBSCR") == 0 ||
+ strcmp(secondToken, "SUBSCRIPTION") == 0) {
+ char * sSubId = strtok(subscription," ");
+ char * sSubKey = strtok(NULL, "\0");
+ int subId = atoi(sSubId);
+ int subKey = atoi(sSubKey);
+ err = m_repState->protectedRequest(GrepReq::STOP_SUBSCR, subId, subKey );
+ printError(err);
+ return true;
+ }
+
+ if (strcmp(allAfterFirstToken, "SUBID") == 0) {
+ ndbout_c("Not implemented");
+ return true;
+ }
+
+
+ if (strcmp(allAfterFirstToken, "METALOG") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_METALOG, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "METASCAN") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_METASCAN, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "DATALOG") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_DATALOG, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "DATASCAN") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP_DATASCAN, 0);
+ printError(err);
+ return true;
+ }
+ if (strcmp(allAfterFirstToken, "IM") == 0 ||
+ strcmp(allAfterFirstToken, "IMM") == 0 ||
+ strcmp(allAfterFirstToken, "IMMEDIATELY") == 0) {
+ err = m_repState->protectedRequest(GrepReq::STOP, 0);
+ printError(err);
+ return true;
+ }
+ Uint32 stopEpoch;
+ if (convert(allAfterFirstToken, &stopEpoch)) {
+ err = m_repState->protectedRequest(GrepReq::STOP, stopEpoch);
+ printError(err);
+ return true;
+ }
+
+ ndbout_c("Unknown argument: %s to command: %s",
+ allAfterFirstToken, firstToken);
+ return true;
+ }
+
+ ndbout_c("Unknown Command: %s", firstToken);
+ ndbout_c("Type HELP for help.");
+ ndbout << endl;
+ return true;
+}
diff --git a/storage/ndb/src/old_files/rep/RepCommandInterpreter.hpp b/storage/ndb/src/old_files/rep/RepCommandInterpreter.hpp
new file mode 100644
index 00000000000..398a7c0318c
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepCommandInterpreter.hpp
@@ -0,0 +1,45 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REP_COMMAND_INTERPRETER_HPP
+#define REP_COMMAND_INTERPRETER_HPP
+
+#include <editline/editline.h>
+
+#include <rep/RepComponents.hpp>
+#include <rep/state/RepState.hpp>
+
+/**
+ * @class RepCommandInterpreter
+ * @brief
+ */
+
+class RepCommandInterpreter {
+public:
+ RepCommandInterpreter(class RepComponents * comps);
+ ~RepCommandInterpreter();
+
+ bool readAndExecute();
+
+private:
+ char * readline_gets() const;
+ void request(Uint32 request);
+
+ class RepComponents * m_repComponents;
+ class RepState * m_repState;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/RepComponents.cpp b/storage/ndb/src/old_files/rep/RepComponents.cpp
new file mode 100644
index 00000000000..04b2e0e5fa5
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepComponents.cpp
@@ -0,0 +1,138 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "RepComponents.hpp"
+
+RepComponents::RepComponents()
+{
+ /**
+ * @todo Fix proper reporting of errors
+ */
+ m_connectStringPS = NULL;
+ m_connectStringSS = NULL;
+
+ /**
+ * Phase 1: Containers, RepState
+ */
+ m_gciContainer = new GCIContainer(MAX_NODE_GROUPS);
+ if (!m_gciContainer) REPABORT("Could not allocate object");
+ m_gciContainerPS = new GCIContainerPS(MAX_NODE_GROUPS);
+ if (!m_gciContainerPS) REPABORT("Could not allocate object");
+ m_repState = new RepState();
+ if (!m_repState) REPABORT("Could not allocate object");
+
+ /**
+ * Phase 2: PS
+ */
+ m_transPS = new TransPS(m_gciContainerPS);
+ if (!m_transPS) REPABORT("Could not allocate object");
+
+
+ m_extAPI = new ExtAPI();
+ if (!m_extAPI) REPABORT("Could not allocate object");
+
+ m_extNDB = new ExtNDB(m_gciContainerPS, m_extAPI);
+ if (!m_extNDB) REPABORT("Could not allocate object");
+
+ /**
+ * Phase 3: SS
+ */
+ m_transSS = new TransSS(m_gciContainer, m_repState);
+ if (!m_transSS) REPABORT("Could not allocate object");
+ m_appNDB = new AppNDB(m_gciContainer, m_repState);
+ if (!m_appNDB) REPABORT("Could not allocate object");
+
+ /**
+ * Phase 4: Requestor
+ */
+ m_requestor = new Requestor(m_gciContainer, m_appNDB, m_repState);
+ if (!m_requestor) REPABORT("Could not allocate object");
+
+ /**
+ * Phase 5
+ */
+ m_repState->init(m_transSS->getRepSender());
+ m_repState->setApplier(m_appNDB);
+ m_repState->setGCIContainer(m_gciContainer);
+
+ m_requestor->setRepSender(m_transSS->getRepSender());
+
+ m_extNDB->setRepSender(m_transPS->getRepSender());
+
+ m_transPS->setGrepSender(m_extNDB->getGrepSender());
+}
+
+RepComponents::~RepComponents()
+{
+ if (m_requestor) delete m_requestor;
+
+ if (m_appNDB) delete m_appNDB;
+ if (m_extNDB) delete m_extNDB;
+ if (m_extAPI) delete m_extAPI;
+
+ if (m_repState) delete m_repState;
+
+ if (m_transPS) delete m_transPS;
+ if (m_transSS) delete m_transSS;
+
+ if (m_gciContainer) delete m_gciContainer;
+ if (m_gciContainerPS) delete m_gciContainerPS;
+}
+
+int
+RepComponents::connectPS()
+{
+ /**
+ * @todo Fix return values of this function
+ */
+
+ /**
+ * Phase 1: TransporterFacade 1, Block number: 2 (PS)
+ */
+ if (!m_extNDB->init(m_connectStringPS)) return -1;
+
+ /**
+ * Phase 2: TransporterFacade 2, Block number: 2 (PS)
+ */
+ m_transPS->init(m_transSS->getTransporterFacade(), m_connectStringPS);
+
+ return 0;
+}
+
+int
+RepComponents::connectSS()
+{
+ /**
+ * @todo Fix return values of this function
+ */
+
+ /**
+ * Phase 1: TransporterFacade 1, Block number: 1 (SS)
+ */
+ m_appNDB->init(m_connectStringSS);
+
+ /**
+ * Phase 2: TransporterFacade 2, Block number: 1 (SS)
+ */
+ m_transSS->init(m_connectStringSS);
+
+ /**
+ * Phase 3: Has no TransporterFacade, just starts thread
+ */
+ m_requestor->init();
+
+ return 0;
+}
diff --git a/storage/ndb/src/old_files/rep/RepComponents.hpp b/storage/ndb/src/old_files/rep/RepComponents.hpp
new file mode 100644
index 00000000000..ff0f29e2128
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepComponents.hpp
@@ -0,0 +1,60 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REPCOMPONENTS_HPP
+#define REPCOMPONENTS_HPP
+
+#include <rep/adapters/ExtNDB.hpp>
+#include <rep/adapters/AppNDB.hpp>
+#include <rep/transfer/TransPS.hpp>
+#include <rep/transfer/TransSS.hpp>
+#include <rep/Requestor.hpp>
+#include <rep/state/RepState.hpp>
+
+#include <rep/rep_version.hpp>
+
+/**
+ * Connection data
+ */
+class RepComponents {
+public:
+ RepComponents();
+ ~RepComponents();
+
+ int connectPS();
+ int connectSS();
+
+ ExtNDB * m_extNDB;
+ ExtAPI * m_extAPI;
+ TransPS * m_transPS;
+
+ TransSS * m_transSS;
+ AppNDB * m_appNDB;
+
+ Requestor * m_requestor;
+
+ GCIContainer * m_gciContainer;
+ GCIContainerPS * m_gciContainerPS;
+
+ char * m_connectStringPS;
+ char * m_connectStringSS;
+
+ RepState * getRepState() { return m_repState; }
+private:
+ RepState * m_repState;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/RepMain.cpp b/storage/ndb/src/old_files/rep/RepMain.cpp
new file mode 100644
index 00000000000..d9f057be9a1
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RepMain.cpp
@@ -0,0 +1,97 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <NdbApiSignal.hpp>
+#include <getarg.h>
+
+#include <rep/RepComponents.hpp>
+
+#include "rep_version.hpp"
+#include <rep/RepCommandInterpreter.hpp>
+#include <rep/RepApiInterpreter.hpp>
+
+
+int
+main(int argc, const char **argv)
+{
+ RepComponents comps;
+ RepCommandInterpreter cmd(&comps);
+
+
+ int helpFlag = false;
+ int noConnectFlag = false;
+ int onlyPrimaryFlag = false;
+ int onlyStandbyFlag = false;
+ int port = 18000;
+ replogEnabled = false;
+
+ struct getargs args[] = {
+ { "psc", '1', arg_string, &comps.m_connectStringPS,
+ "Connect string", "connectstring" },
+ { "ssc", '2', arg_string, &comps.m_connectStringSS,
+ "Connect string", "connectstring" },
+ { "port", 'p', arg_integer, &port,
+ "port for rep api. Default 18000", "" },
+ { "usage", '?', arg_flag, &helpFlag,
+ "Print help", "" },
+/* @todo
+ { "noConnect", 'n', arg_flag, &noConnectFlag,
+ "Do not connect adapters", "" },
+*/
+ { "debug", 'd', arg_flag, &replogEnabled,
+ "Enable debug printouts on console", "" },
+ { "onlyStandby", 's', arg_flag, &onlyStandbyFlag,
+ "Let Replication Server view DBMS as standby (destination) system only",
+ "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "\nWhen working as a primary system node, this program receives\n"\
+ "records from the primary NDB Cluster and forwards them to\n"\
+ "the standby system.\n\n"\
+ "When working as a standby system node, this program receives\n"\
+ "records from another replication node and inserts them into\n"\
+ "the standby NDB Cluster.\n\n"\
+ "Example: ndb_rep --psc=\"nodeid=3;host=localhost:10000\"\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ //argv[optind] == NULL ||
+ helpFlag)
+ {
+ arg_printusage(args, num_args, argv[0], desc);
+ return -1; //NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ RepApiInterpreter api(&comps,port);
+ api.startInterpreter();
+
+ /**************************
+ * Command-line interface *
+ **************************/
+ if (!noConnectFlag && !onlyPrimaryFlag) comps.connectSS();
+ if (!noConnectFlag && !onlyStandbyFlag) comps.connectPS();
+
+
+ while (true) {
+ if(!cmd.readAndExecute()) {
+ api.stopInterpreter();
+ exit(1);
+ }
+ }
+}
diff --git a/storage/ndb/src/old_files/rep/Requestor.cpp b/storage/ndb/src/old_files/rep/Requestor.cpp
new file mode 100644
index 00000000000..3c93a6394a4
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/Requestor.cpp
@@ -0,0 +1,224 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Requestor.hpp"
+#include "ConfigRetriever.hpp"
+
+#include <NdbApiSignal.hpp>
+
+#include <signaldata/RepImpl.hpp>
+#include <signaldata/GrepImpl.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/SumaImpl.hpp>
+
+#include <AttributeHeader.hpp>
+#include <rep/rep_version.hpp>
+
+#define TIME_BETWEEN_EXECUTES_MS 250
+
+/*
+ * @todo The requestor still has a TF, but this is not used...
+ * (We will need a (set of) TF(s) for REP-REP
+ * on the same system though....)
+ */
+
+
+/*****************************************************************************
+ * Constructor / Destructor / Init
+ *****************************************************************************/
+Requestor::Requestor(GCIContainer * gciContainer,
+ AppNDB * appNDB,
+ RepState * repState)
+{
+ m_gciContainer = gciContainer;
+ m_applier = appNDB;
+ m_repState = repState;
+
+ //m_grepSender = new ExtSender();
+ //if (!m_grepSender) REPABORT("");
+
+ m_repState->setSubscriptionRequests(&requestCreateSubscriptionId,
+ &requestCreateSubscription,
+ &requestRemoveSubscription);
+ m_repState->setIntervalRequests(&requestTransfer,
+ &requestApply,
+ &requestDeleteSS,
+ &requestDeletePS);
+ m_repState->setStartRequests(&requestStartMetaLog,
+ &requestStartDataLog,
+ &requestStartMetaScan,
+ &requestStartDataScan,
+ &requestEpochInfo);
+}
+
+Requestor::~Requestor() {
+ //delete m_grepSender;
+}
+
+bool
+Requestor::init(const char * connectString)
+{
+ m_signalExecThread = NdbThread_Create(signalExecThread_C,
+ (void **)this,
+ 32768,
+ "Requestor_Service",
+ NDB_THREAD_PRIO_LOW);
+
+ if (m_signalExecThread == NULL)
+ return false;
+
+ return true;
+}
+
+/*****************************************************************************
+ * Signal Queue Executor
+ *****************************************************************************/
+
+void *
+Requestor::signalExecThread_C(void *g) {
+
+ Requestor *requestor = (Requestor*)g;
+ requestor->signalExecThreadRun();
+ NdbThread_Exit(0);
+
+ /* NOTREACHED */
+ return 0;
+}
+
+class SigMatch
+{
+public:
+ int gsn;
+ void (Requestor::* function)(NdbApiSignal *signal);
+
+ SigMatch() { gsn = 0; function = NULL; };
+
+ SigMatch(int _gsn, void (Requestor::* _function)(NdbApiSignal *signal)) {
+ gsn = _gsn;
+ function = _function;
+ };
+
+ bool check(NdbApiSignal *signal) {
+ if(signal->readSignalNumber() == gsn)
+ return true;
+ return false;
+ };
+};
+
+void
+Requestor::signalExecThreadRun()
+{
+ while(1)
+ {
+ /**
+ * @todo Here we would like to measure the usage size of the
+ * receive buffer of TransSS. If the buffer contains
+ * more than X signals (maybe 1k or 10k), then we should
+ * not do a protectedExecute.
+ * By having the usage size measure thingy,
+ * we avoid having the Requestor requesting more
+ * things than the TransSS can handle.
+ * /Lars
+ *
+ * @todo A different implementation of this functionality
+ * would be to send a signal to myself when the protected
+ * execute is finished. This solution could be
+ * discussed.
+ * /Lars
+ */
+ m_repState->protectedExecute();
+ NdbSleep_MilliSleep(TIME_BETWEEN_EXECUTES_MS);
+ }
+}
+
+void
+Requestor::sendSignalRep(NdbApiSignal * s) {
+ m_repSender->sendSignal(s);
+}
+
+void
+Requestor::execSignal(void* executorObj, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]){
+
+ Requestor * executor = (Requestor*)executorObj;
+
+ const Uint32 gsn = signal->readSignalNumber();
+ const Uint32 len = signal->getLength();
+
+ NdbApiSignal * s = new NdbApiSignal(executor->m_ownRef);
+ switch (gsn) {
+ case GSN_REP_GET_GCI_CONF:
+ case GSN_REP_GET_GCI_REQ:
+ case GSN_REP_GET_GCIBUFFER_REQ:
+ case GSN_REP_INSERT_GCIBUFFER_REQ:
+ case GSN_REP_CLEAR_SS_GCIBUFFER_REQ:
+ case GSN_REP_CLEAR_PS_GCIBUFFER_REQ:
+ case GSN_REP_DROP_TABLE_REQ:
+ case GSN_GREP_SUB_CREATE_REQ:
+ case GSN_GREP_SUB_START_REQ:
+ case GSN_GREP_SUB_SYNC_REQ:
+ case GSN_GREP_SUB_REMOVE_REQ:
+ case GSN_GREP_CREATE_SUBID_REQ:
+ s->set(0, PSREPBLOCKNO, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ break;
+ default:
+ REPABORT1("Illegal signal received in execSignal", gsn);
+ }
+#if 0
+ ndbout_c("Requestor: Inserted signal into queue (GSN: %d, Len: %d)",
+ signal->readSignalNumber(), len);
+#endif
+}
+
+void
+Requestor::execNodeStatus(void* obj, Uint16 nodeId,
+ bool alive, bool nfCompleted)
+{
+ //Requestor * thisObj = (Requestor*)obj;
+
+ RLOG(("Node changed status (NodeId %d, Alive %d, nfCompleted %d)",
+ nodeId, alive, nfCompleted));
+
+ if(alive) {
+ /**
+ * Connected - set node as connected
+ *
+ * @todo Make it possible to have multiple External REP nodes
+ */
+#if 0
+ for(Uint32 i=0; i<thisObj->m_nodeConnectList.size(); i++) {
+ if(thisObj->m_nodeConnectList[i]->nodeId == nodeId)
+ thisObj->m_nodeConnectList[i]->connected = true;
+ }
+ thisObj->m_grepSender->setNodeId(thisObj->m_nodeConnectList[0]->nodeId);
+#endif
+ }
+
+ if(!alive && !nfCompleted){
+ /**
+ * ???
+ */
+ }
+
+ if(!alive && nfCompleted){
+ /**
+ * Re-connect
+ */
+ }
+}
diff --git a/storage/ndb/src/old_files/rep/Requestor.hpp b/storage/ndb/src/old_files/rep/Requestor.hpp
new file mode 100644
index 00000000000..735d2094bde
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/Requestor.hpp
@@ -0,0 +1,154 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REQUESTOR_HPP
+#define REQUESTOR_HPP
+
+#include <ndb_global.h>
+
+#include <TransporterDefinitions.hpp>
+#include <TransporterFacade.hpp>
+#include <ClusterMgr.hpp>
+#include <API.hpp>
+#include <Vector.hpp>
+#include <GrepError.hpp>
+
+#include <rep/storage/GCIContainer.hpp>
+
+/**
+ * @todo Remove this dependency
+ */
+#include <rep/adapters/AppNDB.hpp>
+
+#include <rep/SignalQueue.hpp>
+#include <rep/ExtSender.hpp>
+
+
+/**
+ * @class Requestor
+ * @brief Connects to GREP Coordinator on the standby system
+ */
+class Requestor {
+public:
+ /***************************************************************************
+ * Constructor / Destructor / Init
+ ***************************************************************************/
+ Requestor(GCIContainer * gciContainer, AppNDB * applier, RepState * repSt);
+ ~Requestor();
+ bool init(const char * connectString = NULL);
+
+ /***************************************************************************
+ * Public Methods
+ ***************************************************************************/
+ void setRepSender(ExtSender * es) { m_repSender = es; };
+
+private:
+ static void * signalExecThread_C(void *); ///< SignalQueue executor thread
+ void signalExecThreadRun();
+
+ static void execSignal(void* executorObj, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]);
+ static void execNodeStatus(void* executorObj, NodeId, bool alive,
+ bool nfCompleted);
+
+ void sendSignalRep(NdbApiSignal *);
+ void sendSignalGrep(NdbApiSignal *);
+
+ void connectToNdb();
+
+ /***************************************************************************
+ * Signal Executors
+ ***************************************************************************/
+ void execREP_GET_GCIBUFFER_CONF(NdbApiSignal*);
+ void execREP_CLEAR_GCIBUFFER_REP(NdbApiSignal*);
+ void execREP_INSERT_GCIBUFFER_REQ(NdbApiSignal*);
+ void execREP_CLEAR_SS_GCIBUFFER_REQ(NdbApiSignal*);
+ void execREP_DROP_TABLE_REQ(NdbApiSignal*);
+
+ /***************************************************************************
+ * Signal Executors 2
+ ***************************************************************************/
+ void execGREP_CREATE_SUBID_CONF(NdbApiSignal*);
+ void execGREP_CREATE_SUBID_REF(NdbApiSignal*);
+ void createSubscription(NdbApiSignal*);
+ void createSubscriptionId(NdbApiSignal*);
+ void execGREP_SUB_CREATE_CONF(NdbApiSignal*);
+ void execGREP_SUB_CREATE_REF(NdbApiSignal*);
+ void execGREP_SUB_START_CONF(NdbApiSignal*);
+ void execGREP_SUB_START_REF(NdbApiSignal*);
+ void removeSubscription(NdbApiSignal*);
+ void execGREP_SUB_REMOVE_REF(NdbApiSignal*);
+ void execGREP_SUB_SYNC_CONF(NdbApiSignal*);
+ void execGREP_SUB_SYNC_REF(NdbApiSignal*);
+ void execREP_CLEAR_SS_GCIBUFFER_CONF(NdbApiSignal*);
+ void execREP_CLEAR_SS_GCIBUFFER_REF(NdbApiSignal*);
+ void execREP_GET_GCIBUFFER_REF(NdbApiSignal*);
+ void execREP_DISCONNECT_REP(NdbApiSignal*);
+
+ /***************************************************************************
+ * Ref signal senders
+ ***************************************************************************/
+ void sendREP_INSERT_GCIBUFFER_REF(NdbApiSignal * signal,
+ Uint32 gci,
+ Uint32 nodeGrp,
+ GrepError::Code err);
+
+ void sendREP_CLEAR_SS_GCIBUFFER_REF(NdbApiSignal* signal,
+ Uint32 firstGCI,
+ Uint32 lastGCI,
+ Uint32 currentGCI,
+ Uint32 nodeGrp,
+ GrepError::Code err);
+
+ /***************************************************************************
+ * Private Variables
+ ***************************************************************************/
+ class SignalQueue m_signalRecvQueue;
+ struct NdbThread * m_signalExecThread;
+
+ RepState * m_repState;
+
+ Uint32 m_ownNodeId; ///< NodeId of this node
+ Uint32 m_ownBlockNo; ///< BlockNo of this "block"
+ BlockReference m_ownRef; ///< Reference to this
+
+ TransporterFacade * m_transporterFacade;
+
+ GCIContainer * m_gciContainer;
+
+ AppNDB * m_applier;
+ ExtSender * m_repSender;
+
+ friend void startSubscription(void * cbObj, NdbApiSignal* signal, int type);
+ friend void scanSubscription(void * cbObj, NdbApiSignal* signal, int type);
+
+ friend RepState::FuncRequestCreateSubscriptionId requestCreateSubscriptionId;
+ friend RepState::FuncRequestCreateSubscription requestCreateSubscription;
+ friend RepState::FuncRequestRemoveSubscription requestRemoveSubscription;
+
+ friend RepState::FuncRequestTransfer requestTransfer;
+ friend RepState::FuncRequestApply requestApply;
+ friend RepState::FuncRequestDeleteSS requestDeleteSS;
+ friend RepState::FuncRequestDeletePS requestDeletePS;
+
+ friend RepState::FuncRequestStartMetaLog requestStartMetaLog;
+ friend RepState::FuncRequestStartDataLog requestStartDataLog;
+ friend RepState::FuncRequestStartMetaScan requestStartMetaScan;
+ friend RepState::FuncRequestStartDataScan requestStartDataScan;
+ friend RepState::FuncRequestEpochInfo requestEpochInfo;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/RequestorSubscriptions.cpp b/storage/ndb/src/old_files/rep/RequestorSubscriptions.cpp
new file mode 100644
index 00000000000..75b41fae037
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/RequestorSubscriptions.cpp
@@ -0,0 +1,60 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Requestor.hpp"
+
+#include <signaldata/GrepImpl.hpp>
+#include <signaldata/SumaImpl.hpp>
+
+#include <rep/rep_version.hpp>
+
+/*****************************************************************************
+ * Create Subscription Id
+ *****************************************************************************/
+
+
+/*****************************************************************************
+ * Create Subscription
+ *****************************************************************************/
+
+
+/*****************************************************************************
+ * Start Subscription
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Remove Subscription
+ *****************************************************************************/
+
+void
+Requestor::execGREP_SUB_REMOVE_REF(NdbApiSignal* signal)
+{
+#if 0
+ GrepSubRemoveRef * const ref = (GrepSubRemoveRef *)signal->getDataPtr();
+ Uint32 subId = ref->subscriptionId;
+ Uint32 subKey = ref->subscriptionKey;
+ Uint32 err = ref->err;
+
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_SubRemoveRef;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ signal->theData[4] = (Uint32)err;
+ sendSignal(CMVMI_REF,GSN_EVENT_REP,signal, 5, JBB);
+#endif
+}
+
+
diff --git a/storage/ndb/src/old_files/rep/SignalQueue.cpp b/storage/ndb/src/old_files/rep/SignalQueue.cpp
new file mode 100644
index 00000000000..9b356a14b7d
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/SignalQueue.cpp
@@ -0,0 +1,106 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <string.h>
+
+#include "SignalQueue.hpp"
+
+SignalQueue::SignalQueue() {
+ m_mutex = NdbMutex_Create();
+ m_cond = NdbCondition_Create();
+ m_signalQueueHead = NULL;
+ m_queueSize = 0;
+}
+
+SignalQueue::~SignalQueue() {
+ {
+ Guard g(m_mutex);
+ while(m_signalQueueHead != NULL)
+ delete pop();
+ }
+ NdbMutex_Destroy(m_mutex);
+ m_mutex = NULL;
+ NdbCondition_Destroy(m_cond);
+ m_cond = NULL;
+}
+
+NdbApiSignal *
+SignalQueue::pop() {
+ NdbApiSignal *ret;
+
+ if(m_signalQueueHead == NULL)
+ return NULL;
+
+ ret = m_signalQueueHead->signal;
+
+ QueueEntry *old = m_signalQueueHead;
+ m_signalQueueHead = m_signalQueueHead->next;
+
+ delete old;
+ m_queueSize--;
+ return ret;
+}
+
+void
+SignalQueue::receive(void *me, NdbApiSignal *signal) {
+ SignalQueue *q = (SignalQueue *)me;
+ q->receive(signal);
+}
+
+void
+SignalQueue::receive(NdbApiSignal *signal) {
+ QueueEntry *n = new QueueEntry();
+ n->signal = signal;
+ n->next = NULL;
+
+ Guard guard(m_mutex);
+
+ if(m_signalQueueHead == NULL) {
+ m_signalQueueHead = n;
+ m_queueSize++;
+ NdbCondition_Broadcast(m_cond);
+ return;
+ }
+
+ QueueEntry *cur = m_signalQueueHead;
+
+ while(cur->next != NULL)
+ cur = cur->next;
+
+ cur->next = n;
+ m_queueSize++;
+ NdbCondition_Broadcast(m_cond);
+}
+
+NdbApiSignal *
+SignalQueue::waitFor(int gsn, NodeId nodeid, Uint32 timeout) {
+ Guard g(m_mutex);
+
+ if(m_signalQueueHead == NULL)
+ NdbCondition_WaitTimeout(m_cond, m_mutex, timeout);
+
+ if(m_signalQueueHead == NULL)
+ return NULL;
+
+ if(gsn != 0 && m_signalQueueHead->signal->readSignalNumber() != gsn)
+ return NULL;
+
+ if(nodeid != 0 &&
+ refToNode(m_signalQueueHead->signal->theSendersBlockRef) != nodeid)
+ return NULL;
+
+ return pop();
+}
diff --git a/storage/ndb/src/old_files/rep/SignalQueue.hpp b/storage/ndb/src/old_files/rep/SignalQueue.hpp
new file mode 100644
index 00000000000..697bca85893
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/SignalQueue.hpp
@@ -0,0 +1,117 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 __SIGNALQUEUE_HPP_INCLUDED__
+#define __SIGNALQUEUE_HPP_INCLUDED__
+
+#include <NdbApiSignal.hpp>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <Vector.hpp>
+
+/* XXX Look for an already existing definition */
+#define DEFAULT_TIMEOUT 10000
+
+/**
+ * @class SignalQueue
+ * @brief
+ */
+class SignalQueue {
+public:
+ typedef void (* SignalHandler)(void *obj, int gsn, NdbApiSignal *signal);
+
+ SignalQueue();
+ ~SignalQueue();
+
+ /**
+ * Static wrapper making it possible to call receive without knowing the
+ * type of the receiver
+ */
+ static void receive(void *me, NdbApiSignal *signal);
+
+ /**
+ * Enqueues a signal, and notifies any thread waiting for signals.
+ */
+ void receive(NdbApiSignal *signal);
+
+ NdbApiSignal *waitFor(int gsn,
+ NodeId nodeid = 0,
+ Uint32 timeout = DEFAULT_TIMEOUT);
+ template<class T> bool waitFor(Vector<T> &t,
+ T *&handler,
+ NdbApiSignal *&signal,
+ Uint32 timeout);
+
+ /**
+ * size()
+ */
+
+ Uint32 size() {return m_queueSize;};
+
+private:
+ NdbMutex *m_mutex; /* Locks all data in SignalQueue */
+ NdbCondition *m_cond; /* Notifies about new signal in the queue */
+
+ /**
+ * Returns the last recently received signal.
+ * Must be called with m_mutex locked.
+ *
+ * The caller takes responsibility for deleting the returned object.
+ *
+ * @returns NULL if failed, or a received signal
+ */
+ NdbApiSignal *pop();
+
+ class QueueEntry {
+ public:
+ NdbApiSignal *signal;
+ QueueEntry *next;
+ };
+ QueueEntry *m_signalQueueHead; /** Head of the queue.
+ * New entries added on the tail
+ */
+ Uint32 m_queueSize;
+};
+
+template<class T> bool
+SignalQueue::waitFor(Vector<T> &t,
+ T *&handler,
+ NdbApiSignal *&signal,
+ Uint32 timeout) {
+ Guard g(m_mutex);
+
+ if(m_signalQueueHead == NULL)
+ NdbCondition_WaitTimeout(m_cond, m_mutex, timeout);
+
+ if(m_signalQueueHead == NULL)
+ return false;
+
+ for(size_t i = 0; i < t.size(); i++) {
+ if(t[i].check(m_signalQueueHead->signal)) {
+ handler = &t[i];
+ signal = pop();
+ return true;
+ }
+ }
+
+ ndbout_c("SignalQueue: Queued signal without true check function (GSN: %d)",
+ m_signalQueueHead->signal->theVerId_signalNumber);
+ abort();
+
+ return false;
+}
+
+#endif /* !__SIGNALQUEUE_HPP_INCLUDED__ */
diff --git a/storage/ndb/src/old_files/rep/TODO b/storage/ndb/src/old_files/rep/TODO
new file mode 100644
index 00000000000..a2462fae6cd
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/TODO
@@ -0,0 +1,119 @@
+REQUIREMENTS
+------------
+- It should be possible to run two systems with replication using the
+ same configuration file on both systems.
+
+FEATURES TO IMPLEMENT
+---------------------
+- Fix so that execute and command uses ExtSender.
+ None of them should have their own signals, this should
+ instead by abstacted to the RepStateRequest layer.
+- Delete signals
+ GSN_REP_INSERT_GCIBUFFER_CONF
+ GSN_REP_INSERT_GCIBUFFER_REF
+- Fix so that all ExtSenders are set at one point in the code only.
+- Verify the following signals:
+ GSN_REP_INSERT_GCIBUFFER_REQ
+ GSN_REP_CLEAR_SS_GCIBUFFER_REQ
+ GSN_REP_DROP_TABLE_REQ
+- Fix all @todo's in the code
+- Remove all #if 1, #if 0 etc.
+- Fix correct usage of dbug package used in MySQL source code.
+- System table storing all info about channels
+- Think about how channels, subscriptions etc map to SUMA Subscriptions
+- TableInfoPS must be secured if SS REP is restarted and PS REP still
+ has all log records needed to sync. (This could be saved in a system
+ table instead of using the struct.)
+
+KNOWN BUGS AND LIMITATIONS
+--------------------------
+- REP#1: Non-consistency due to non-logging stop [LIMITATION]
+ Problem:
+ - Stopping replication in state other than "Logging" can
+ lead to a non-consistent state of the destination database
+ Suggested solution:
+ - Implement a cleanData flag (= false) that indicates that
+ this has happend.
+
+- REP#2: PS REP uses epochs from old subscription [BUG]
+ The following scenario can lead to a non-correct replication:
+ - Start replication X
+ - Wait until replication is in "Logging" state
+ - Kill SS REP
+ - Let PS REP be alive
+ - Start new replication Y
+ - Replication Y can use old PS REP epochs from replication X.
+ Suggested solution:
+ - Mark PS buffers with channel ids
+ - Make sure that all epoch requests use channel number in the requests.
+
+- REP#3: When having two node groups, there is sometimes 626 [FIXED]
+ Problem:
+ - Sometimes (when doing updated) there is 626 error code when
+ using 2 node groups.
+ - 626 = Tuple does not exists.
+ - Current code in RepState.cpp is:
+ if(s == Channel::App &&
+ m_channel.getState() == Channel::DATASCAN_COMPLETED &&
+ i.last() >= m_channel.getDataScanEpochs().last() &&
+ i.last() >= m_channel.getMetaScanEpochs().last())
+ {
+ m_channel.setState(Channel::LOG);
+ disableAutoStart();
+ }
+ When the system gets into LOG state, force flag is turned off
+ Suggested solution:
+ - During DATASCAN, force=true (i.e. updates are treated as writes,
+ deletes error due to non-existing tuple are ignored)
+ - The code above must take ALL node groups into account.
+
+- REP#4: User requests sometime vanish when DB node is down [LIMITATION]
+ Problem:
+ - PS REP node does not always REF when no connection to GREP exists
+ Suggested solution:
+ - All REP->GREP signalsends should be checked. If they return <0,
+ then a REF signal should be returned.
+
+- REP#5: User requests sometime vanish when PS REP is down [BUG]
+ Scenario:
+ - Execute "Start" with PS REP node down
+ Solution:
+ - When start is executed, the connect flag should be checked
+
+- REP#6: No warning if table exists [Lars, BUG!]
+ Problem:
+ - There is no warning if a replicated table already exists in the
+ database.
+ Suggested solution:
+ - Print warning
+ - Set cleanData = false
+
+- REP#7: Starting 2nd subscription crashes DB node (Grep.cpp:994) [FIXED]
+ Scenario:
+ - Start replication
+ - Wait until replication is in "Logging" state
+ - Kill SS REP
+ - Let PS REP be alive
+ - Start new replication
+ - Now GREP crashes in Grep.cpp:994.
+ Suggested fix:
+ - If a new subscription is requested with same subscriberData
+ as already exists, then SUMA (or GREP) sends a REF signal
+ indicating that SUMA does not allow a new subscription to be
+ created. [Now no senderData is sent from REP.]
+
+- REP#8: Dangling subscriptions in GREP/SUMA [Johan,LIMITATION]
+ Problem:
+ - If both REP nodes die, then there is no possibility to remove
+ subscriptions from GREP/SUMA
+ Suggested solution 1:
+ - Fix so that GREP/SUMA can receive a subscription removal
+ signal with subid 0. This means that ALL subscriptions are
+ removed. This meaning should be documented in the
+ signaldata class.
+ - A new user command "STOP ALL" is implemented that sends
+ a request to delete all subscriptions.
+ Suggested solution 2:
+ - When GREP detects that ALL PS REP nodes associated with a s
+ subscription are killed, then that subscription should be
+ deleted.
diff --git a/storage/ndb/src/old_files/rep/adapters/AppNDB.cpp b/storage/ndb/src/old_files/rep/adapters/AppNDB.cpp
new file mode 100644
index 00000000000..05f6d52807f
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/AppNDB.cpp
@@ -0,0 +1,583 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "AppNDB.hpp"
+#include <ConfigRetriever.hpp>
+#include <AttributeHeader.hpp>
+#include <NdbOperation.hpp>
+#include <NdbDictionaryImpl.hpp>
+
+#include <signaldata/RepImpl.hpp>
+#include <TransporterFacade.hpp>
+#include <trigger_definitions.h>
+#include <rep/storage/GCIPage.hpp>
+#include <rep/storage/GCIBuffer.hpp>
+#include <rep/rep_version.hpp>
+
+/*****************************************************************************
+ * Constructor / Destructor / Init
+ *****************************************************************************/
+
+AppNDB::~AppNDB()
+{
+ delete m_tableInfoPs;
+ delete m_ndb;
+ m_tableInfoPs = 0;
+}
+
+AppNDB::AppNDB(GCIContainer * gciContainer, RepState * repState)
+{
+ m_gciContainer = gciContainer;
+ m_repState = repState;
+ m_cond = NdbCondition_Create();
+ m_started = true;
+}
+
+void
+AppNDB::init(const char* connectString) {
+
+ // NdbThread_SetConcurrencyLevel(1+ 2);
+ m_ndb = new Ndb("");
+
+ m_ndb->useFullyQualifiedNames(false);
+
+ m_ndb->setConnectString(connectString);
+ /**
+ * @todo Set proper max no of transactions?? needed?? Default 12??
+ */
+ m_ndb->init(2048);
+ m_dict = m_ndb->getDictionary();
+
+ m_ownNodeId = m_ndb->getNodeId();
+
+ ndbout << "-- NDB Cluster -- REP node " << m_ownNodeId << " -- Version "
+ << REP_VERSION_ID << " --" << endl;
+ ndbout_c("Connecting to NDB Cluster...");
+ if (m_ndb->waitUntilReady() != 0){
+ REPABORT("NDB Cluster not ready for connections");
+ }
+ ndbout_c("Phase 1 (AppNDB): Connection 1 to NDB Cluster opened (Applier)");
+
+ m_tableInfoPs = new TableInfoPs();
+
+ m_applierThread = NdbThread_Create(runAppNDB_C,
+ (void**)this,
+ 32768,
+ "AppNDBThread",
+ NDB_THREAD_PRIO_LOW);
+}
+
+
+/*****************************************************************************
+ * Threads
+ *****************************************************************************/
+
+extern "C"
+void*
+runAppNDB_C(void * me)
+{
+ ((AppNDB *) me)->threadMainAppNDB();
+ NdbThread_Exit(0);
+ return me;
+}
+
+void
+AppNDB::threadMainAppNDB() {
+ MetaRecord * mr;
+ LogRecord * lr;
+ GCIBuffer::iterator * itBuffer;
+ GCIPage::iterator * itPage;
+ GCIBuffer * buffer;
+ GCIPage * page;
+ Uint32 gci=0;
+
+ bool force;
+ while(true){
+
+ m_gciBufferList.lock();
+ if(m_gciBufferList.size()==0)
+ NdbCondition_Wait(m_cond, m_gciBufferList.getMutex());
+ m_gciBufferList.unlock();
+
+ /**
+ * Do nothing if we are not started!
+ */
+ if(!m_started)
+ continue;
+
+ if(m_gciBufferList.size()>0) {
+ m_gciBufferList.lock();
+ buffer = m_gciBufferList[0];
+ assert(buffer!=0);
+ if(buffer==0) {
+ m_gciBufferList.unlock();
+// stopApplier(GrepError::REP_APPLY_NULL_GCIBUFFER);
+ return;
+ }
+ m_gciBufferList.unlock();
+
+ RLOG(("Applying %d:[%d]", buffer->getId(), buffer->getGCI()));
+ gci = buffer->getGCI();
+ /**
+ * Do stuff with buffer
+ */
+
+ force = buffer->m_force;
+ itBuffer = new GCIBuffer::iterator(buffer);
+ page = itBuffer->first();
+
+ Record * record;
+ while(page!=0 && m_started) {
+
+ itPage = new GCIPage::iterator(page);
+ record = itPage->first();
+
+ while(record!=0 && m_started) {
+ switch(Record::RecordType(record->recordType)) {
+ case Record::META:
+ mr = (MetaRecord*)record;
+ if(applyMetaRecord(mr, gci) < 0){
+ /**
+ * If we fail with a meta record then
+ * we should fail the replication!
+ */
+ //stopApplier(GrepError::REP_APPLY_METARECORD_FAILED);
+ }
+ break;
+ case Record::LOG:
+ lr = (LogRecord*)record;
+ if(applyLogRecord(lr, force, gci) < 0) {
+ /**
+ * If we fail to apply a log record AND
+ * we have sent a ref to repstate event,
+ * then we should not try to apply another one!
+ */
+// stopApplier(GrepError::REP_APPLY_LOGRECORD_FAILED);
+ }
+ break;
+ default:
+ REPABORT("Illegal record type");
+ };
+ record = itPage->next();
+ }
+ delete itPage;
+ itPage = 0;
+ page = itBuffer->next();
+ }
+
+ m_gciBufferList.erase(0, true);
+ /**
+ * "callback" to RepState to send REP_INSERT_GCIBUFFER_CONF
+ */
+ m_repState->eventInsertConf(buffer->getGCI(), buffer->getId());
+ delete itBuffer;
+ itBuffer = 0;
+ mr = 0;
+ lr = 0;
+ page = 0;
+ buffer = 0;
+ }
+ }
+
+
+}
+
+void AppNDB::startApplier(){
+ m_started = true;
+}
+
+
+void AppNDB::stopApplier(GrepError::Code err){
+ m_started = false;
+ m_repState->eventInsertRef(0,0,0, err);
+}
+
+
+GrepError::Code
+AppNDB::applyBuffer(Uint32 nodeGrp, Uint32 epoch, Uint32 force)
+{
+ m_gciBufferList.lock();
+
+ GCIBuffer * buffer = m_gciContainer->getGCIBuffer(epoch, nodeGrp);
+ if (buffer == NULL) {
+ RLOG(("WARNING! Request to apply NULL buffer %d[%d]. Force %d",
+ nodeGrp, epoch, force));
+ return GrepError::NO_ERROR;
+ }
+ if (!buffer->isComplete()) {
+ RLOG(("WARNING! Request to apply non-complete buffer %d[%d]. Force %d",
+ nodeGrp, epoch, force));
+ return GrepError::REP_APPLY_NONCOMPLETE_GCIBUFFER;
+ }
+ buffer->m_force = force;
+
+ assert(buffer!=0);
+ m_gciBufferList.push_back(buffer, false);
+ NdbCondition_Broadcast(m_cond);
+ m_gciBufferList.unlock();
+ return GrepError::NO_ERROR;
+}
+
+int
+AppNDB::applyLogRecord(LogRecord* lr, bool force, Uint32 gci)
+{
+#if 0
+ RLOG(("Applying log record (force %d, Op %d, GCI %d)",
+ force, lr->operation, gci));
+#endif
+
+ int retries =0;
+ retry:
+ if(retries == 10) {
+ m_repState->eventInsertRef(gci, 0, lr->tableId,
+ GrepError::REP_APPLIER_EXECUTE_TRANSACTION);
+ return -1;
+ }
+ NdbConnection * trans = m_ndb->startTransaction();
+ if (trans == NULL) {
+ /**
+ * Transaction could not be started
+ * @todo Handle the error by:
+ * 1. Return error code
+ * 2. Print log message
+ * 3. On higher level indicate that DB has been tainted
+ */
+ ndbout_c("AppNDB: Send the following error msg to NDB Cluster support");
+ reportNdbError("Cannot start transaction!", trans->getNdbError());
+ m_repState->eventInsertRef(gci, 0, 0,
+ GrepError::REP_APPLIER_START_TRANSACTION);
+ REPABORT("Can not start transaction");
+ }
+
+ /**
+ * Resolve table name based on table id
+ */
+ const Uint32 tableId = lr->tableId;
+ const char * tableName = m_tableInfoPs->getTableName(tableId);
+
+ /**
+ * Close trans and return if it is systab_0.
+ */
+ if (tableId == 0) {
+ RLOG(("WARNING! System table log record received"));
+ m_ndb->closeTransaction(trans);
+ return -1;
+ }
+
+ if (tableName==0) {
+ /**
+ * Table probably does not exist
+ * (Under normal operation this should not happen
+ * since log records should not appear unless the
+ * table has been created.)
+ *
+ * @todo Perhaps the table is not cached due to a restart,
+ * so let's check in the dictionary if it exists.
+ */
+ m_ndb->closeTransaction(trans);
+ m_repState->eventInsertRef(gci, 0, tableId,
+ GrepError::REP_APPLIER_NO_TABLE);
+ return -1;
+ }
+
+ const NdbDictionary::Table * table = m_dict->getTable(tableName);
+
+ NdbOperation * op = trans->getNdbOperation(tableName);
+ if (op == NULL) {
+ ndbout_c("AppNDB: Send the following error msg to NDB Cluster support");
+ reportNdbError("Cannot get NdbOperation record",
+ trans->getNdbError());
+ m_repState->eventInsertRef(gci,0,tableId,
+ GrepError::REP_APPLIER_NO_OPERATION);
+ REPABORT("Can not get NdbOperation record");
+ }
+
+ int check=0;
+ switch(lr->operation) {
+ case TriggerEvent::TE_INSERT: // INSERT
+ check = op->insertTuple();
+ break;
+ case TriggerEvent::TE_DELETE: // DELETE
+ check = op->deleteTuple();
+ break;
+ case TriggerEvent::TE_UPDATE: // UPDATE
+ if (force) {
+ check = op->writeTuple();
+ } else {
+ check = op->updateTuple();
+ }
+ break;
+ case TriggerEvent::TE_CUSTOM: //SCAN
+ check = op->writeTuple();
+ break;
+ default:
+ m_ndb->closeTransaction(trans);
+ return -1;
+ };
+
+ if (check<0) {
+ ndbout_c("AppNDB: Something is weird");
+ }
+
+ /**
+ * @todo index inside LogRecord struct somewhat prettier
+ * Now it 4 (sizeof(Uint32)), and 9 the position inside the struct
+ * where the data starts.
+ */
+ AttributeHeader * ah=(AttributeHeader *)((char *)lr + sizeof(Uint32) * 9);
+ AttributeHeader *end = (AttributeHeader *)(ah + lr->attributeHeaderWSize);
+ Uint32 * dataPtr = (Uint32 *)(end);
+
+ /**
+ * @note attributeheader for operaration insert includes a duplicate
+ * p.k. The quick fix for this problem/bug is to skip the first set of
+ * of p.k, and start from the other set of P.Ks. Data is duplicated for
+ * the p.k.
+ */
+ if (lr->operation == 0) {
+ for(int i = 0; i< table->getNoOfPrimaryKeys(); i++) {
+ ah+=ah->getHeaderSize();
+ dataPtr = dataPtr + ah->getDataSize();
+ }
+ }
+
+ while (ah < end) {
+ const NdbDictionary::Column * column =
+ table->getColumn(ah->getAttributeId());
+ /**
+ * @todo: Here is a limitation. I don't care if it is a tuplekey
+ * that is autogenerated or an ordinary pk. I just whack it in.
+ * However, this must be examined.
+ */
+ if(column->getPrimaryKey()) {
+ if(op->equal(ah->getAttributeId(), (const char *)dataPtr) < 0) {
+ ndbout_c("AppNDB: Equal failed id %d op %d name %s, gci %d force %d",
+ ah->getAttributeId(),
+ lr->operation,
+ column->getName(), gci, force);
+ reportNdbError("Equal!", trans->getNdbError());
+ }
+
+ } else {
+ if(op->setValue(ah->getAttributeId(), (const char *)dataPtr) < 0)
+ ndbout_c("AppNDB: setvalue failed id %d op %d name %s, gci %d force %d",
+ ah->getAttributeId(),
+ lr->operation,
+ column->getName(), gci, force);
+ }
+
+ dataPtr = dataPtr + ah->getDataSize();
+ ah = ah + ah->getHeaderSize() ;
+ }
+
+ if(trans->execute(Commit) != 0) {
+ /**
+ * Transaction commit failure
+ */
+ const NdbError err = trans->getNdbError();
+ m_ndb->closeTransaction(trans);
+ switch(err.status){
+ case NdbError::Success:
+ {
+ m_repState->eventInsertRef(gci, 0, tableId,
+ GrepError::REP_APPLIER_EXECUTE_TRANSACTION);
+ return -1;
+ }
+ break;
+ case NdbError::TemporaryError:
+ {
+ NdbSleep_MilliSleep(50);
+ retries++;
+ goto retry;
+ }
+ break;
+ case NdbError::UnknownResult:
+ {
+ ndbout_c("AppNDB: Send the following error msg to NDB Cluster support");
+ reportNdbError("Execute transaction failed!",
+ trans->getNdbError());
+ m_repState->eventInsertRef(gci, 0, tableId,
+ GrepError::REP_APPLIER_EXECUTE_TRANSACTION);
+ return -1;
+ }
+ break;
+ case NdbError::PermanentError:
+ {
+ if(err.code == 626) {
+ if(force && lr->operation == TriggerEvent::TE_DELETE) /**delete*/ {
+ /**tuple was not found. Ignore this, since
+ * we are trying to apply a "delete a tuple"-log record before
+ * having applied the scan data.
+ */
+ return -1;
+ }
+ }
+
+ ndbout_c("AppNDB: Send the following error msg to NDB Cluster support"); reportNdbError("Execute transaction failed!",
+ trans->getNdbError());
+ ndbout_c("\n\nAppNDB: RepNode will now crash.");
+ m_ndb->closeTransaction(trans);
+ m_repState->eventInsertRef(gci, 0, tableId,
+ GrepError::REP_APPLIER_EXECUTE_TRANSACTION);
+ return -1;
+ }
+ break;
+ }
+ }
+
+ /**
+ * No errors. Close transaction and continue in applierThread.
+ */
+ m_ndb->closeTransaction(trans);
+ return 1;
+}
+
+
+int
+AppNDB::applyMetaRecord(MetaRecord* mr, Uint32 gci)
+{
+ /**
+ * Validate table id
+ */
+ Uint32 tableId = mr->tableId;
+ if (tableId==0) {
+ RLOG(("WARNING! Meta record contained record with tableId 0"));
+ return 0;
+ }
+
+ /**
+ * Prepare meta record
+ */
+ NdbDictionary::Table * table = prepareMetaRecord(mr);
+ if(table == 0) {
+ RLOG(("WARNING! Prepare table meta record failed for table %d", tableId));
+ m_dict->getNdbError();
+ m_repState->eventInsertRef(gci,0,tableId,
+ GrepError::REP_APPLIER_PREPARE_TABLE);
+ return -1;
+ }
+
+ /**
+ * Table does not exist in TableInfoPs -> add it
+ */
+ if(m_tableInfoPs->getTableName(tableId)==0) {
+ RLOG(("Table %d:%s added to m_tableInfoPs", tableId, table->getName()));
+ m_tableInfoPs->insert(tableId,table->getName());
+ }
+
+ /**
+ * Validate that table does not exist in Dict
+ */
+
+ const NdbDictionary::Table * tmpTable = m_dict->getTable(table->getName());
+ if(tmpTable !=0) {
+ /**
+ * Oops, a table with the same name exists
+ */
+ if(tmpTable->getObjectVersion()!=table->getObjectVersion()) {
+ char buf[100];
+ sprintf(buf,"WARNING! Another version of table %d:%s already exists."
+ "Currently, we dont support versions, so will abort now!",
+ tableId, table->getName());
+
+ REPABORT(buf);
+
+ }
+ RLOG(("WARNING! An identical table %d:%s already exists.",
+ tableId, table->getName()));
+ return -1;
+ }
+
+
+ /**
+ * @todo WARNING! Should scan table MR for columns that are not supported
+ */
+ /*
+ NdbDictionary::Column * column;
+
+ for(int i=0; i<table->getNoOfColumns(); i++) {
+ column = table->getColumn(i);
+ if(column->getAutoIncrement()) {
+ reportWarning(table->getName(), column->getName(),
+ "Uses AUTOINCREMENT of PK");
+ }
+ }
+ */
+
+
+ /**
+ * Create table
+ */
+ if(m_dict->createTable(*table)<0) {
+ ndbout_c("AppNDB: Send the following error msg to NDB Cluster support");
+ reportNdbError("Create table failed!", m_dict->getNdbError());
+ m_repState->eventCreateTableRef(gci,
+ tableId,
+ table->getName(),
+ GrepError::REP_APPLIER_CREATE_TABLE);
+ return -1;
+ }
+
+ RLOG(("Table %d:%s created", tableId, table->getName()));
+ return 0;
+}
+
+NdbDictionary::Table*
+AppNDB::prepareMetaRecord(MetaRecord* mr) {
+ NdbTableImpl * tmp = 0;
+ NdbDictionary::Table * table =0;
+ Uint32 * data =(Uint32*)( ((char*)mr + sizeof(Uint32)*6));
+ int res = NdbDictInterface::parseTableInfo(&tmp, data, mr->dataLen,
+ m_ndb->usingFullyQualifiedNames());
+ if(res == 0) {
+ table = tmp;
+ return table;
+ } else{
+ return 0;
+ }
+}
+
+void
+AppNDB::reportNdbError(const char * msg, const NdbError & err) {
+ ndbout_c("%s : Error code %d , error message %s",
+ msg, err.code,
+ (err.message ? err.message : ""));
+}
+
+void
+AppNDB::reportWarning(const char * tableName, const char * message) {
+ ndbout_c("WARNING: Table %s, %s", tableName, message);
+}
+
+void
+AppNDB::reportWarning(const char * tableName, const char * columnName,
+ const char * message) {
+ ndbout_c("WARNING: Table %s, column %s, %s", tableName, columnName,message);
+}
+
+int
+AppNDB::dropTable(Uint32 tableId)
+{
+ char * tableName = m_tableInfoPs->getTableName(tableId);
+ if(tableName == 0) return -1;
+ ndbout_c("AppNDB: Dropping table ");
+ if(m_dict->dropTable(tableName) != 0) {
+ reportNdbError("Failed dropping table",m_dict->getNdbError());
+ return -1;
+ }
+ m_tableInfoPs->del(tableId);
+ return 1;
+}
diff --git a/storage/ndb/src/old_files/rep/adapters/AppNDB.hpp b/storage/ndb/src/old_files/rep/adapters/AppNDB.hpp
new file mode 100644
index 00000000000..9563a1e41ab
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/AppNDB.hpp
@@ -0,0 +1,141 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 APPNDB_HPP
+#define APPNDB_HPP
+#include "NdbApi.hpp"
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+
+#include <NdbThread.h>
+#include <Vector.hpp>
+
+#include "TableInfoPs.hpp"
+#include <rep/storage/GCIContainer.hpp>
+#include <rep/storage/GCIBuffer.hpp>
+
+#include <rep/state/RepState.hpp>
+
+extern "C" {
+ void * runAppNDB_C(void *);
+}
+
+/**
+ * @class AppNDB
+ * @brief Connects to NDB and appliers log records into standby system
+ */
+class AppNDB {
+public:
+ /***************************************************************************
+ * Constructor / Destructor / Init
+ ***************************************************************************/
+ AppNDB(class GCIContainer * gciContainer, class RepState * repState);
+ ~AppNDB();
+
+ void init(const char * connectString);
+
+ GrepError::Code
+ applyBuffer(Uint32 nodeGrp, Uint32 first, Uint32 force);
+
+ /**
+ * Takes a table id and drops it.
+ * @param tableId Name of table to be dropped
+ * @return Returns 1 = ok, -1 failed
+ *
+ * @todo Fix: 0 usually means ok...
+ */
+ int dropTable(Uint32 tableId);
+ void startApplier();
+ void stopApplier(GrepError::Code err);
+private:
+ /***************************************************************************
+ * Methods
+ ***************************************************************************/
+ friend void* runAppNDB_C(void*);
+
+ void threadMainAppNDB(void);
+
+ /**
+ * Takes a log records and does the operation specified in the log record
+ * on NDB.
+ * @param - lr (LogRecord)
+ * @param - force true if GREP:SSCoord is in phase STARTING.
+ * Ignore "Execute" errors if true.
+ */
+ int applyLogRecord(LogRecord * lr, bool force, Uint32 gci);
+
+ /**
+ * Applies a table based on a meta record and creates the table
+ * in NDB.
+ * @param - meta record
+ * @return - 0 on success, -1 if something went wrong
+ */
+ int applyMetaRecord(MetaRecord * mr, Uint32 gci);
+
+ /**
+ * Takes a meta record and uses NdbDictionaryXXX::parseInfoTable
+ * and returns a table
+ * @param mr - MetaRecord
+ * @return - a table based on the meta record
+ */
+ NdbDictionary::Table* prepareMetaRecord(MetaRecord * mr);
+
+ /**
+ * Prints out an NDB error message if a ndb operation went wrong.
+ * @param msg - text explaining the error
+ * @param err - NDB error type
+ */
+ void reportNdbError(const char * msg, const NdbError & err);
+
+ /**
+ * Prints out a warning message. Used if support for something
+ * is not implemented.
+ * @param tableName - the name of the table this warning occured on
+ * @param message - warning message
+ */
+ void reportWarning(const char * tableName, const char * message);
+
+ /**
+ * Prints out a warning message. Used if support for something
+ * is not implemented.
+ * @param tableName - the name of the table this warning occured on
+ * @param columnName - the name of the column this warning occured on
+ * @param message - warning message
+ */
+ void reportWarning(const char * tableName, const char * columnName,
+ const char * message);
+
+
+ /***************************************************************************
+ * Variables
+ ***************************************************************************/
+ GCIContainer * m_gciContainer;
+ RepState * m_repState;
+
+ Ndb* m_ndb;
+ NdbDictionary::Dictionary * m_dict;
+ NodeId m_ownNodeId;
+ bool m_started;
+ TableInfoPs * m_tableInfoPs;
+ NdbThread* m_applierThread;
+ NdbCondition * m_cond;
+ MutexVector<GCIBuffer*> m_gciBufferList;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/adapters/ExtAPI.cpp b/storage/ndb/src/old_files/rep/adapters/ExtAPI.cpp
new file mode 100644
index 00000000000..0dcd1e85465
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/ExtAPI.cpp
@@ -0,0 +1,31 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ExtAPI.hpp"
+
+GrepError::Code
+ExtAPI::eventSubscriptionIdCreated(Uint32 subId, Uint32 subKey)
+{
+ NdbApiSignal* signal = m_repSender->getSignal();
+ CreateSubscriptionIdConf * conf =
+ (CreateSubscriptionIdConf *)signal->getDataPtrSend();
+ conf->subscriptionId = subId;
+ conf->subscriptionKey = subKey;
+ signal->set(0, SSREPBLOCKNO, GSN_GREP_CREATE_SUBID_CONF,
+ CreateSubscriptionIdConf::SignalLength);
+ m_repSender->sendSignal(signal);
+ return GrepError::NO_ERROR;
+}
diff --git a/storage/ndb/src/old_files/rep/adapters/ExtAPI.hpp b/storage/ndb/src/old_files/rep/adapters/ExtAPI.hpp
new file mode 100644
index 00000000000..f10b6c7d682
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/ExtAPI.hpp
@@ -0,0 +1,107 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 EXTAPI_HPP
+#define EXTAPI_HPP
+
+#include <signaldata/RepImpl.hpp>
+#include <signaldata/GrepImpl.hpp>
+#include <signaldata/SumaImpl.hpp>
+
+#include <rep/ExtSender.hpp>
+
+/**
+ * The abstract class for all extractors
+ */
+class ExtAPI
+{
+public:
+ /***************************************************************************
+ * Constructor / Destructor
+ ***************************************************************************/
+#if 0
+ bool init(const char * connectString = NULL);
+
+ GrepError::Code dataLogStarted(Uint32 epoch,
+ Uint32 subId, Uint32 subKey) = 0;
+ GrepError::Code metaLogStarted(Uint32 epoch,
+ Uint32 subId, Uint32 subKey) = 0;
+ GrepError::Code epochComleted() = 0;
+ GrepError::Code subscriptionCreated() = 0;
+ GrepError::Code subscriptionRemoved() = 0;
+ GrepError::Code metaScanCompleted() = 0;
+ GrepError::Code dataScanCompleted() = 0;
+ GrepError::Code subscriptionRemoveFailed() = 0;
+ GrepError::Code metaScanFailed() = 0;
+ GrepError::Code dataScanFailed() = 0;
+ GrepError::Code subscriptiodIdCreateFailed() = 0;
+ GrepError::Code dataLogFailed() = 0;
+ GrepError::Code metaLogFailed() = 0;
+ GrepError::Code subscriptionCreateFailed() = 0;
+
+ /**Above to be deleted*/
+#endif
+
+ virtual GrepError::Code
+ eventSubscriptionIdCreated(Uint32 subId, Uint32 subKey) ;
+
+#if 0
+ GrepError::Code
+ eventSubscriptionDeleted(Uint32 subId, Uint32 subKey);
+
+ GrepError::Code
+ eventMetaLogStarted(NdbApiSignal*, Uint32 subId, Uint32 subKey);
+
+ GrepError::Code
+ eventDataLogStarted(NdbApiSignal*, Uint32 subId, Uint32 subKey);
+
+ GrepError::Code
+ eventMetaScanCompleted(NdbApiSignal*, Uint32 subId, Uint32 subKey,
+ Interval epochs);
+
+ GrepError::Code
+ eventDataScanCompleted(NdbApiSignal*, Uint32 subId, Uint32 subKey,
+ Interval epochs);
+
+ GrepError::Code
+ eventMetaScanFailed(Uint32 subId, Uint32 subKey, GrepError::Code error);
+
+ GrepError::Code
+ eventDataScanFailed(Uint32 subId, Uint32 subKey, GrepError::Code error);
+#endif
+
+ /***************************************************************************
+ * Public Methods
+ ***************************************************************************/
+ void setRepSender(ExtSender * es) { m_repSender = es; };
+ //void signalErrorHandler(NdbApiSignal * s, Uint32 nodeId);
+
+protected:
+ ExtSender * m_repSender;
+};
+
+
+#if 0
+class TestExtAPI : public ExtAPI
+{
+ GrepError::Code
+ eventSubscriptionIdCreated(Uint32 subId, Uint32 subKey) {
+ ndbout_c("Received subscription:%d-%d");
+ };
+};
+#endif
+
+#endif // EXTAPI_HPP
diff --git a/storage/ndb/src/old_files/rep/adapters/ExtNDB.cpp b/storage/ndb/src/old_files/rep/adapters/ExtNDB.cpp
new file mode 100644
index 00000000000..6642b750b57
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/ExtNDB.cpp
@@ -0,0 +1,559 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "ExtNDB.hpp"
+#include "ConfigRetriever.hpp"
+#include <NdbSleep.h>
+
+#include <NdbApiSignal.hpp>
+
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <AttributeHeader.hpp>
+#include <rep/rep_version.hpp>
+#include <ndb_limits.h>
+
+/*****************************************************************************
+ * Constructor / Destructor / Init
+ *****************************************************************************/
+ExtNDB::ExtNDB(GCIContainerPS * gciContainer, ExtAPI * extAPI)
+{
+ m_grepSender = new ExtSender();
+ if (!m_grepSender) REPABORT("Could not allocate object");
+ m_gciContainerPS = gciContainer;
+
+ m_nodeGroupInfo = new NodeGroupInfo();
+ m_gciContainerPS->setNodeGroupInfo(m_nodeGroupInfo);
+
+ m_doneSetGrepSender = false;
+ m_subId = 0;
+ m_subKey = 0;
+ m_firstGCI = 0;
+ m_dataLogStarted = false;
+
+ m_extAPI = extAPI;
+ if (!m_extAPI) REPABORT("Could not allocate object");
+}
+
+ExtNDB::~ExtNDB()
+{
+ delete m_grepSender;
+ delete m_nodeGroupInfo;
+}
+
+void
+ExtNDB::signalErrorHandler(NdbApiSignal * signal, Uint32 nodeId)
+{
+ //const Uint32 gsn = signal->readSignalNumber();
+ //const Uint32 len = signal->getLength();
+ RLOG(("Send signal failed. Signal %p", signal));
+}
+
+bool
+ExtNDB::init(const char * connectString)
+{
+ m_signalExecThread = NdbThread_Create(signalExecThread_C,
+ (void **)this,
+ 32768,
+ "ExtNDB_Service",
+ NDB_THREAD_PRIO_LOW);
+
+#if 0
+ /**
+ * I don't see that this does anything
+ *
+ * Jonas 13/2-04
+ */
+ ConfigRetriever cr; cr.setConnectString(connectString);
+
+ ndb_mgm_configuration * config = cr.getConfig(NDB_VERSION, NODE_TYPE_REP);
+ if (config == 0) {
+ ndbout << "ExtNDB: Configuration error: ";
+ const char* erString = cr.getErrorString();
+ if (erString == 0) {
+ erString = "No error specified!";
+ }
+ ndbout << erString << endl;
+ return false;
+ }
+ NdbAutoPtr autoPtr(config);
+ m_ownNodeId = r.getOwnNodeId();
+
+ /**
+ * Check which GREPs to connect to (in configuration)
+ *
+ * @note SYSTEM LIMITATION: Only connects to one GREP
+ */
+ Uint32 noOfConnections=0;
+ NodeId grepNodeId=0;
+ const Properties * connection;
+
+ config->get("NoOfConnections", &noOfConnections);
+ for (Uint32 i=0; i<noOfConnections; i++) {
+ Uint32 nodeId1, nodeId2;
+ config->get("Connection", i, &connection);
+ connection->get("NodeId1", &nodeId1);
+ connection->get("NodeId2", &nodeId2);
+ if (!connection->contains("System1") &&
+ !connection->contains("System2") &&
+ (nodeId1 == m_ownNodeId || nodeId2 == m_ownNodeId)) {
+ /**
+ * Found connection
+ */
+ if (nodeId1 == m_ownNodeId) {
+ grepNodeId = nodeId2;
+ } else {
+ grepNodeId = nodeId1;
+ }
+ }
+ }
+#endif
+
+ m_transporterFacade = TransporterFacade::instance();
+
+ assert(m_transporterFacade != 0);
+
+ m_ownBlockNo = m_transporterFacade->open(this, execSignal, execNodeStatus);
+ assert(m_ownBlockNo > 0);
+ m_ownRef = numberToRef(m_ownBlockNo, m_ownNodeId);
+ ndbout_c("EXTNDB blockno %d ownref %d ", m_ownBlockNo, m_ownRef);
+ assert(m_ownNodeId == m_transporterFacade->ownId());
+
+ m_grepSender->setOwnRef(m_ownRef);
+ m_grepSender->setTransporterFacade(m_transporterFacade);
+
+ if(!m_grepSender->connected(50000)){
+ ndbout_c("ExtNDB: Failed to connect to DB nodes!");
+ ndbout_c("ExtNDB: Tried to create transporter as (node %d, block %d).",
+ m_ownNodeId, m_ownBlockNo);
+ ndbout_c("ExtNDB: Check that DB nodes are started.");
+ return false;
+ }
+ ndbout_c("Phase 3 (ExtNDB): Connection %d to NDB Cluster opened (Extractor)",
+ m_ownBlockNo);
+
+ for (Uint32 i=1; i<MAX_NDB_NODES; i++) {
+ if (m_transporterFacade->getIsDbNode(i) &&
+ m_transporterFacade->getIsNodeSendable(i))
+ {
+ Uint32 nodeGrp = m_transporterFacade->getNodeGrp(i);
+ m_nodeGroupInfo->addNodeToNodeGrp(i, true, nodeGrp);
+ Uint32 nodeId = m_nodeGroupInfo->getFirstConnectedNode(nodeGrp);
+ m_grepSender->setNodeId(nodeId);
+ if(m_nodeGroupInfo->getPrimaryNode(nodeGrp) == 0) {
+ m_nodeGroupInfo->setPrimaryNode(nodeGrp, nodeId);
+ }
+ m_doneSetGrepSender = true;
+#if 0
+ RLOG(("Added node %d to node group %d", i, nodeGrp));
+#endif
+ }
+ }
+
+ return true;
+}
+
+/*****************************************************************************
+ * Signal Queue Executor
+ *****************************************************************************/
+
+class SigMatch
+{
+public:
+ int gsn;
+ void (ExtNDB::* function)(NdbApiSignal *signal);
+
+ SigMatch() { gsn = 0; function = NULL; };
+
+ SigMatch(int _gsn, void (ExtNDB::* _function)(NdbApiSignal *signal)) {
+ gsn = _gsn;
+ function = _function;
+ };
+
+ bool check(NdbApiSignal *signal) {
+ if(signal->readSignalNumber() == gsn)
+ return true;
+ return false;
+ };
+};
+
+extern "C"
+void *signalExecThread_C(void *r)
+{
+ ExtNDB *grepps = (ExtNDB*)r;
+
+ grepps->signalExecThreadRun();
+
+ NdbThread_Exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+
+void
+ExtNDB::signalExecThreadRun()
+{
+ Vector<SigMatch> sl;
+
+ /**
+ * Signals to be executed
+ */
+ sl.push_back(SigMatch(GSN_SUB_GCP_COMPLETE_REP,
+ &ExtNDB::execSUB_GCP_COMPLETE_REP));
+
+ /**
+ * Is also forwarded to SSCoord
+ */
+ sl.push_back(SigMatch(GSN_GREP_SUB_START_CONF,
+ &ExtNDB::execGREP_SUB_START_CONF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_CREATE_CONF,
+ &ExtNDB::execGREP_SUB_CREATE_CONF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_REMOVE_CONF,
+ &ExtNDB::execGREP_SUB_REMOVE_CONF));
+ /**
+ * Signals to be forwarded
+ */
+ sl.push_back(SigMatch(GSN_GREP_CREATE_SUBID_CONF,
+ &ExtNDB::execGREP_CREATE_SUBID_CONF));
+
+ sl.push_back(SigMatch(GSN_GREP_SUB_SYNC_CONF, &ExtNDB::sendSignalRep));
+
+ sl.push_back(SigMatch(GSN_GREP_SUB_REMOVE_REF, &ExtNDB::sendSignalRep));
+ sl.push_back(SigMatch(GSN_GREP_SUB_SYNC_REF, &ExtNDB::sendSignalRep));
+ sl.push_back(SigMatch(GSN_GREP_CREATE_SUBID_REF, &ExtNDB::sendSignalRep));
+
+ sl.push_back(SigMatch(GSN_GREP_SUB_START_REF, &ExtNDB::sendSignalRep));
+ sl.push_back(SigMatch(GSN_GREP_SUB_CREATE_REF, &ExtNDB::sendSignalRep));
+
+
+ while(1) {
+ SigMatch *handler = NULL;
+ NdbApiSignal *signal = NULL;
+
+ if(m_signalRecvQueue.waitFor(sl, handler, signal, DEFAULT_TIMEOUT)) {
+#if 0
+ RLOG(("Removed signal from queue (GSN: %d, QSize: %d)",
+ signal->readSignalNumber(), m_signalRecvQueue.size()));
+#endif
+ if(handler->function != 0) {
+ (this->*handler->function)(signal);
+ delete signal; signal = 0;
+ } else {
+ REPABORT("Illegal handler for signal");
+ }
+ }
+ }
+}
+
+void
+ExtNDB::sendSignalRep(NdbApiSignal * s)
+{
+ if(m_repSender->sendSignal(s) == -1)
+ {
+ signalErrorHandler(s, 0);
+ }
+}
+
+void
+ExtNDB::execSignal(void* executorObj, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3])
+{
+ ExtNDB * executor = (ExtNDB*)executorObj;
+
+ const Uint32 gsn = signal->readSignalNumber();
+ const Uint32 len = signal->getLength();
+
+ NdbApiSignal * s = new NdbApiSignal(executor->m_ownRef);
+ switch(gsn){
+ case GSN_SUB_GCP_COMPLETE_REP:
+ case GSN_GREP_CREATE_SUBID_CONF:
+ case GSN_GREP_SUB_CREATE_CONF:
+ case GSN_GREP_SUB_START_CONF:
+ case GSN_GREP_SUB_SYNC_CONF:
+ case GSN_GREP_SUB_REMOVE_CONF:
+ case GSN_GREP_CREATE_SUBID_REF:
+ case GSN_GREP_SUB_CREATE_REF:
+ case GSN_GREP_SUB_START_REF:
+ case GSN_GREP_SUB_SYNC_REF:
+ case GSN_GREP_SUB_REMOVE_REF:
+ s->set(0, SSREPBLOCKNO, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ break;
+ case GSN_SUB_TABLE_DATA:
+ executor->execSUB_TABLE_DATA(signal, ptr);
+ delete s; s=0;
+ break;
+ case GSN_SUB_META_DATA:
+ executor->execSUB_META_DATA(signal, ptr);
+ delete s; s=0;
+ break;
+ default:
+ REPABORT1("Illegal signal received in execSignal", gsn);
+ }
+ s=0;
+#if 0
+ ndbout_c("ExtNDB: Inserted signal into queue (GSN: %d, Len: %d)",
+ signal->readSignalNumber(), len);
+#endif
+}
+
+void
+ExtNDB::execNodeStatus(void* obj, Uint16 nodeId, bool alive, bool nfCompleted)
+{
+ ExtNDB * thisObj = (ExtNDB*)obj;
+
+ RLOG(("Changed node status (Id %d, Alive %d, nfCompleted %d)",
+ nodeId, alive, nfCompleted));
+
+ if(alive) {
+ /**
+ * Connected
+ */
+ Uint32 nodeGrp = thisObj->m_transporterFacade->getNodeGrp(nodeId);
+ RLOG(("DB node %d of node group %d connected", nodeId, nodeGrp));
+
+ thisObj->m_nodeGroupInfo->addNodeToNodeGrp(nodeId, true, nodeGrp);
+ Uint32 firstNode = thisObj->m_nodeGroupInfo->getPrimaryNode(nodeGrp);
+
+ if(firstNode == 0)
+ thisObj->m_nodeGroupInfo->setPrimaryNode(nodeGrp, nodeId);
+
+ if (!thisObj->m_doneSetGrepSender) {
+ thisObj->m_grepSender->setNodeId(firstNode);
+ thisObj->m_doneSetGrepSender = true;
+ }
+
+ RLOG(("Connect: First connected node in nodegroup: %d",
+ thisObj->m_nodeGroupInfo->getPrimaryNode(nodeGrp)));
+
+ } else if (!nfCompleted) {
+
+ /**
+ * Set node as "disconnected" in m_nodeGroupInfo until
+ * node comes up again.
+ */
+ Uint32 nodeGrp = thisObj->m_transporterFacade->getNodeGrp(nodeId);
+ RLOG(("DB node %d of node group %d disconnected",
+ nodeId, nodeGrp));
+ thisObj->m_nodeGroupInfo->setConnectStatus(nodeId, false);
+ /**
+ * The node that crashed was also the primary node, the we must change
+ * primary node
+ */
+ if(nodeId == thisObj->m_nodeGroupInfo->getPrimaryNode(nodeGrp)) {
+ Uint32 node = thisObj->m_nodeGroupInfo->getFirstConnectedNode(nodeGrp);
+ if(node > 0) {
+ thisObj->m_grepSender->setNodeId(node);
+ thisObj->m_nodeGroupInfo->setPrimaryNode(nodeGrp, node);
+ }
+ else {
+ thisObj->sendDisconnectRep(nodeGrp);
+ }
+ }
+ RLOG(("Disconnect: First connected node in nodegroup: %d",
+ thisObj->m_nodeGroupInfo->getPrimaryNode(nodeGrp)));
+
+ } else if(nfCompleted) {
+ } else {
+ REPABORT("Function execNodeStatus with wrong parameters");
+ }
+}
+
+/*****************************************************************************
+ * Signal Receivers for LOG and SCAN
+ *****************************************************************************/
+
+/**
+ * Receive datalog/datascan from GREP/SUMA
+ */
+void
+ExtNDB::execSUB_TABLE_DATA(NdbApiSignal * signal, LinearSectionPtr ptr[3])
+{
+ SubTableData * const data = (SubTableData*)signal->getDataPtr();
+ Uint32 tableId = data->tableId;
+ Uint32 operation = data->operation;
+ Uint32 gci = data->gci;
+ Uint32 nodeId = refToNode(signal->theSendersBlockRef);
+
+ if((SubTableData::LogType)data->logType == SubTableData::SCAN)
+ {
+ Uint32 nodeGrp = m_nodeGroupInfo->findNodeGroup(nodeId);
+
+ NodeGroupInfo::iterator * it;
+ it = new NodeGroupInfo::iterator(nodeGrp, m_nodeGroupInfo);
+ for(NodeConnectInfo * nci=it->first(); it->exists();nci=it->next()) {
+ m_gciContainerPS->insertLogRecord(nci->nodeId, tableId,
+ operation, ptr, gci);
+ }
+ delete it; it = 0;
+ } else {
+ m_gciContainerPS->insertLogRecord(nodeId, tableId, operation, ptr, gci);
+ }
+}
+
+/**
+ * Receive metalog/metascan from GREP/SUMA
+ */
+void
+ExtNDB::execSUB_META_DATA(NdbApiSignal * signal, LinearSectionPtr ptr[3])
+{
+ Uint32 nodeId = refToNode(signal->theSendersBlockRef);
+ SubMetaData * const data = (SubMetaData*)signal->getDataPtr();
+ Uint32 tableId = data->tableId;
+ Uint32 gci = data->gci;
+
+ Uint32 nodeGrp = m_nodeGroupInfo->findNodeGroup(nodeId);
+
+ NodeGroupInfo::iterator * it;
+ it = new NodeGroupInfo::iterator(nodeGrp, m_nodeGroupInfo);
+ for(NodeConnectInfo * nci=it->first(); it->exists();nci=it->next()) {
+ m_gciContainerPS->insertMetaRecord(nci->nodeId, tableId, ptr, gci);
+ RLOG(("Received meta record in %d[%d]", nci->nodeId, gci));
+ }
+
+ delete it; it = 0;
+}
+
+
+/*****************************************************************************
+ * Signal Receivers (Signals that are actually just forwarded to SS REP)
+ *****************************************************************************/
+
+void
+ExtNDB::execGREP_CREATE_SUBID_CONF(NdbApiSignal * signal)
+{
+ CreateSubscriptionIdConf const * conf =
+ (CreateSubscriptionIdConf *)signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ ndbout_c("GREP_CREATE_SUBID_CONF m_extAPI=%p\n", m_extAPI);
+ m_extAPI->eventSubscriptionIdCreated(subId, subKey);
+}
+
+/*****************************************************************************
+ * Signal Receivers
+ *****************************************************************************/
+
+/**
+ * Receive information about completed GCI from GREP/SUMA
+ *
+ * GCI completed, i.e. no more unsent log records exists in SUMA
+ * @todo use node id to identify buffers?
+ */
+void
+ExtNDB::execSUB_GCP_COMPLETE_REP(NdbApiSignal * signal)
+{
+ SubGcpCompleteRep * const rep = (SubGcpCompleteRep*)signal->getDataPtr();
+ const Uint32 gci = rep->gci;
+ Uint32 nodeId = refToNode(rep->senderRef);
+
+ RLOG(("Epoch %d completed at node %d", gci, nodeId));
+ m_gciContainerPS->setCompleted(gci, nodeId);
+
+ if(m_firstGCI == gci && !m_dataLogStarted) {
+ sendGREP_SUB_START_CONF(signal, m_firstGCI);
+ m_dataLogStarted = true;
+ }
+}
+
+/**
+ * Send info that scan is competed to SS REP
+ *
+ * @todo Use node id to identify buffers?
+ */
+void
+ExtNDB::sendGREP_SUB_START_CONF(NdbApiSignal * signal, Uint32 gci)
+{
+ RLOG(("Datalog started (Epoch %d)", gci));
+ GrepSubStartConf * conf = (GrepSubStartConf *)signal->getDataPtrSend();
+ conf->firstGCI = gci;
+ conf->subscriptionId = m_subId;
+ conf->subscriptionKey = m_subKey;
+ conf->part = SubscriptionData::TableData;
+ signal->m_noOfSections = 0;
+ signal->set(0, SSREPBLOCKNO, GSN_GREP_SUB_START_CONF,
+ GrepSubStartConf::SignalLength);
+ sendSignalRep(signal);
+}
+
+/**
+ * Scan is completed... says SUMA/GREP
+ *
+ * @todo Use node id to identify buffers?
+ */
+void
+ExtNDB::execGREP_SUB_START_CONF(NdbApiSignal * signal)
+{
+ GrepSubStartConf * const conf = (GrepSubStartConf *)signal->getDataPtr();
+ Uint32 part = conf->part;
+ //Uint32 nodeId = refToNode(conf->senderRef);
+ m_firstGCI = conf->firstGCI;
+
+ if (part == SubscriptionData::TableData) {
+ RLOG(("Datalog started (Epoch %d)", m_firstGCI));
+ return;
+ }
+ RLOG(("Metalog started (Epoch %d)", m_firstGCI));
+
+ signal->set(0, SSREPBLOCKNO, GSN_GREP_SUB_START_CONF,
+ GrepSubStartConf::SignalLength);
+ sendSignalRep(signal);
+}
+
+/**
+ * Receive no of node groups that PS has and pass signal on to SS
+ */
+void
+ExtNDB::execGREP_SUB_CREATE_CONF(NdbApiSignal * signal)
+{
+ GrepSubCreateConf * conf = (GrepSubCreateConf *)signal->getDataPtrSend();
+ m_subId = conf->subscriptionId;
+ m_subKey = conf->subscriptionKey;
+
+ conf->noOfNodeGroups = m_nodeGroupInfo->getNoOfNodeGroups();
+ sendSignalRep(signal);
+}
+
+/**
+ * Receive conf that subscription has been remove in GREP/SUMA
+ *
+ * Pass signal on to TransPS
+ */
+void
+ExtNDB::execGREP_SUB_REMOVE_CONF(NdbApiSignal * signal)
+{
+ m_gciContainerPS->reset();
+ sendSignalRep(signal);
+}
+
+/**
+ * If all PS nodes has disconnected, then remove all epochs
+ * for this subscription.
+ */
+void
+ExtNDB::sendDisconnectRep(Uint32 nodeId)
+{
+ NdbApiSignal * signal = new NdbApiSignal(m_ownRef);
+ signal->set(0, SSREPBLOCKNO, GSN_REP_DISCONNECT_REP,
+ RepDisconnectRep::SignalLength);
+ RepDisconnectRep * rep = (RepDisconnectRep*) signal->getDataPtrSend();
+ rep->nodeId = nodeId;
+ rep->subId = m_subId;
+ rep->subKey = m_subKey;
+ sendSignalRep(signal);
+}
diff --git a/storage/ndb/src/old_files/rep/adapters/ExtNDB.hpp b/storage/ndb/src/old_files/rep/adapters/ExtNDB.hpp
new file mode 100644
index 00000000000..228c980fd06
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/ExtNDB.hpp
@@ -0,0 +1,118 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 EXTNDB_HPP
+#define EXTNDB_HPP
+
+#include <ndb_global.h>
+
+#include <TransporterDefinitions.hpp>
+#include <TransporterFacade.hpp>
+#include <ClusterMgr.hpp>
+#include <API.hpp>
+#include <Vector.hpp>
+
+#include <signaldata/RepImpl.hpp>
+#include <signaldata/GrepImpl.hpp>
+
+#include <rep/SignalQueue.hpp>
+#include <rep/ExtSender.hpp>
+
+#include <rep/storage/GCIContainerPS.hpp>
+#include "ExtAPI.hpp"
+
+extern "C" {
+static void * signalExecThread_C(void *);
+}
+
+/**
+ * @class ExtNDB
+ * @brief Class responsible for connection to primary system GREP
+ */
+class ExtNDB
+{
+public:
+ /***************************************************************************
+ * Constructor / Destructor
+ ***************************************************************************/
+ ExtNDB(GCIContainerPS * gciContainer, ExtAPI * extAPI);
+ ~ExtNDB();
+ bool init(const char * connectString = NULL);
+
+ /***************************************************************************
+ * Public Methods
+ ***************************************************************************/
+ void setGrepSender(ExtSender * es) { m_grepSender = es; };
+ ExtSender * getGrepSender() { return m_grepSender; };
+ void setRepSender(ExtSender * es) {
+ m_extAPI->setRepSender(es); m_repSender = es; };
+ void signalErrorHandler(NdbApiSignal * s, Uint32 nodeId);
+
+private:
+ friend void * signalExecThread_C(void *);
+ void signalExecThreadRun();
+
+ static void execSignal(void* signalSender, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]);
+
+ static void execNodeStatus(void* signalSender, NodeId,
+ bool alive, bool nfCompleted);
+
+ void sendSignalRep(NdbApiSignal *);
+ void sendDisconnectRep(Uint32 nodeId);
+
+ /***************************************************************************
+ * Signal Executors
+ ***************************************************************************/
+ void execSUB_GCP_COMPLETE_REP(NdbApiSignal*);
+ void execGREP_SUB_CREATE_CONF(NdbApiSignal * signal);
+ void execGREP_SUB_REMOVE_CONF(NdbApiSignal * signal);
+ void execGREP_SUB_START_CONF(NdbApiSignal * signal);
+ void sendGREP_SUB_START_CONF(NdbApiSignal * signal, Uint32 gci);
+ void execSUB_TABLE_DATA(NdbApiSignal * signal,LinearSectionPtr ptr[3]);
+ void execSUB_META_DATA(NdbApiSignal * signal,LinearSectionPtr ptr[3]);
+
+ // Signals that are actually just fowarded to REP
+ void execGREP_CREATE_SUBID_CONF(NdbApiSignal *);
+
+ /***************************************************************************
+ * Private Variables
+ ***************************************************************************/
+ struct NdbThread * m_signalExecThread;
+ class SignalQueue m_signalRecvQueue;
+
+ Uint32 m_ownNodeId; ///< NodeId of this node
+ Uint32 m_ownBlockNo; ///< BlockNo of this "block"
+ BlockReference m_ownRef; ///< Reference to this
+
+ ExtSender * m_grepSender; ///< Responsible send to GREP
+ ExtSender * m_repSender; ///< Responsible send to SS REP
+
+ NodeGroupInfo * m_nodeGroupInfo;
+ GCIContainerPS * m_gciContainerPS; ///< Interface to GCICotainer
+ ///< seen by PS
+ TransporterFacade * m_transporterFacade;
+
+ bool m_doneSetGrepSender; ///< Only done once
+ bool m_dataLogStarted;
+ Uint32 m_subId;
+ Uint32 m_subKey;
+ Uint32 m_firstGCI;
+
+ ExtAPI * m_extAPI;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/adapters/Makefile b/storage/ndb/src/old_files/rep/adapters/Makefile
new file mode 100644
index 00000000000..bdd711510c3
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapi repserver kernel
+
+ARCHIVE_TARGET := repadapters
+
+SOURCES = ExtNDB.cpp \
+ AppNDB.cpp \
+ ExtAPI.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/adapters/TableInfoPs.hpp b/storage/ndb/src/old_files/rep/adapters/TableInfoPs.hpp
new file mode 100644
index 00000000000..3fa25979255
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/adapters/TableInfoPs.hpp
@@ -0,0 +1,118 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TABLEINFO_PS_HPP
+#define TABLEINFO_PS_HPP
+
+#include <Vector.hpp>
+#include <ndb_types.h>
+#include <string.h>
+#include <NdbMem.h>
+
+struct TableInfo {
+ Uint32 tableId;
+ char* tableName;
+};
+
+/**
+ * @class TableInfoPS
+ * @brief Meta information about tables stored on PS
+ */
+class TableInfoPs {
+public:
+ inline void insert(const Uint32 tableId, const char * tableName);
+
+ inline bool del(const Uint32 tableId);
+
+ inline char * getTableName(const Uint32 tableId) const;
+
+private:
+ Vector<struct TableInfo*> tableInfo;
+
+ inline TableInfo * lookup(const Uint32 tableId) const;
+ inline TableInfo * lookup(const Uint32 tableId , Uint32 * pos) const;
+};
+
+inline
+TableInfo *
+TableInfoPs::lookup(const Uint32 tableId) const{
+ TableInfo * table;
+ Uint32 i=0;
+
+ while(i<tableInfo.size()) {
+ table=tableInfo[i];
+ if(table->tableId == tableId)
+ return table;
+ i++;
+ }
+ return 0;
+}
+
+inline
+TableInfo *
+TableInfoPs::lookup(const Uint32 tableId, Uint32 * pos ) const{
+ TableInfo * table;
+ Uint32 i=0;
+ while(i<tableInfo.size()) {
+ table=tableInfo[i];
+ if(table->tableId == tableId) {
+ *pos=i;
+ return table;
+ }
+ i++;
+ }
+ return 0;
+}
+
+
+inline
+char *
+TableInfoPs::getTableName(const Uint32 tableId) const{
+ TableInfo * table;
+ table=lookup(tableId);
+ if(table!=0)
+ return table->tableName;
+ return 0;
+}
+
+
+inline
+void
+TableInfoPs::insert(const Uint32 tableId, const char * tableName) {
+ TableInfo * table = new TableInfo;
+ table->tableId=tableId;
+ table->tableName=strdup(tableName);
+ tableInfo.push_back(table);
+}
+
+inline
+bool
+TableInfoPs::del(const Uint32 tableId) {
+
+ TableInfo * table;
+ Uint32 i=0;
+ table = lookup(tableId, &i);
+
+ if(table!=0) {
+ NdbMem_Free(table->tableName);
+ delete table;
+ tableInfo.erase(i);
+ return true;
+ }
+ return false;
+}
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/dbug_hack.cpp b/storage/ndb/src/old_files/rep/dbug_hack.cpp
new file mode 100644
index 00000000000..74e5f080777
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/dbug_hack.cpp
@@ -0,0 +1,75 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+
+#include <OutputStream.hpp>
+#include "NdbOut.hpp"
+#include "rep_version.hpp"
+
+int replogEnabled;
+
+/**
+ * @todo This should be implemented using MySQLs dbug library
+ */
+#if 0
+extern "C"
+void
+DBUG_PRINT(const char * fmt, ...)
+{
+#ifdef DBUG
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf << endl;
+ va_end(ap);
+#endif
+}
+#endif
+
+extern "C"
+void
+replog(const char * fmt, ...)
+{
+ if (replogEnabled)
+ {
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf << endl;
+ va_end(ap);
+ }
+}
+
+extern "C"
+void
+rlog(const char * fmt, ...)
+{
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << buf;
+ va_end(ap);
+}
diff --git a/storage/ndb/src/old_files/rep/rep_version.hpp b/storage/ndb/src/old_files/rep/rep_version.hpp
new file mode 100644
index 00000000000..3830f9c351c
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/rep_version.hpp
@@ -0,0 +1,88 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REP_VERSION_HPP
+#define REP_VERSION_HPP
+
+/**
+ * Block number for REP
+ */
+#define SSREPBLOCKNO 1
+#define PSREPBLOCKNO 2
+
+#define DBUG
+
+#include <ndb_version.h>
+
+extern "C"
+void
+DBUG_PRINT__(const char * fmt, ...);
+
+extern "C"
+void
+replog(const char * fmt, ...);
+
+extern "C"
+void
+rlog(const char * fmt, ...);
+
+#define RLOG(ARGS) \
+ do { if (replogEnabled) { \
+ rlog ARGS; \
+ ndbout << " (" << __FILE__ << ":" << __LINE__ << ")" << endl; \
+ } \
+ } while (0)
+
+/**
+ * Replication logging on or off
+ */
+extern int replogEnabled;
+
+/**
+ * Used for config id
+ */
+#define REP_VERSION_ID NDB_VERSION
+
+#define MAX_NODE_GROUPS 6
+
+#define REPABORT(string) \
+ { \
+ ndbout_c("\nInternal error in %s:%d: %s", __FILE__, __LINE__, string); \
+ abort(); \
+ }
+#define REPABORT1(string, data1) \
+ { \
+ ndbout_c("\nInternal error in %s:%d: %s" \
+ "\n (data1: %d)", \
+ __FILE__, __LINE__, string, data1); \
+ abort(); \
+ }
+#define REPABORT2(string, data1, data2) \
+ { \
+ ndbout_c("\nInternal error in %s:%d: %s" \
+ "\n (data1: %d, data2: %d)", \
+ __FILE__, __LINE__, string, data1, data2); \
+ abort(); \
+ }
+#define REPABORT3(string, data1, data2, data3) \
+ { \
+ ndbout_c("\nInternal error in %s:%d: %s" \
+ "\n (data1: %d, data2: %d data3: %d)", \
+ __FILE__, __LINE__, string, data1, data2, data3); \
+ abort(); \
+ }
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/repapi/Makefile b/storage/ndb/src/old_files/rep/repapi/Makefile
new file mode 100644
index 00000000000..fdd153f1060
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/repapi/Makefile
@@ -0,0 +1,25 @@
+include .defs.mk
+
+TYPE := util
+
+PIC_ARCHIVE := Y
+ARCHIVE_TARGET := repapi
+
+A_LIB := Y
+SO_LIB := Y
+PIC_LIB := Y
+
+#DIRS := test
+
+LIB_TARGET := REP_API
+LIB_TARGET_ARCHIVES := $(ARCHIVE_TARGET) general portlib
+
+# Source files of non-templated classes (.C files)
+SOURCES = repapi.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/include/mgmapi) \
+ -I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon)
+
+CCFLAGS += -DNO_DEBUG_MESSAGES
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/repapi/repapi.cpp b/storage/ndb/src/old_files/rep/repapi/repapi.cpp
new file mode 100644
index 00000000000..d34ab098c9c
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/repapi/repapi.cpp
@@ -0,0 +1,598 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include <NdbTCP.h>
+#include "repapi.h"
+//#include "mgmapi_debug.h"
+#include <socket_io.h>
+
+#include <NdbOut.hpp>
+#include <SocketServer.hpp>
+#include <Parser.hpp>
+#include <OutputStream.hpp>
+#include <InputStream.hpp>
+
+#if defined VM_TRACE && !defined NO_DEBUG_MESSAGES
+#define DEBUG(x) ndbout << x << endl;
+#elif defined NO_DEBUG_MESSAGES
+#define DEBUG(x)
+#endif
+
+#ifdef NDB_WIN32
+#define EBADMSG EFAULT
+#endif
+
+
+
+class ParserDummy2 : SocketServer::Session {
+public:
+ ParserDummy2(NDB_SOCKET_TYPE sock);
+};
+
+ParserDummy2::ParserDummy2(NDB_SOCKET_TYPE sock) : SocketServer::Session(sock) {
+
+}
+
+typedef Parser<ParserDummy2> Parser_t;
+
+
+#define REP_CMD(name, fun, desc) \
+ { name, \
+ 0, \
+ ParserRow<ParserDummy2>::Cmd, \
+ ParserRow<ParserDummy2>::String, \
+ ParserRow<ParserDummy2>::Optional, \
+ ParserRow<ParserDummy2>::IgnoreMinMax, \
+ 0, 0, \
+ fun, \
+ desc, 0 }
+
+#define REP_ARG(name, type, opt, desc) \
+ { name, \
+ 0, \
+ ParserRow<ParserDummy2>::Arg, \
+ ParserRow<ParserDummy2>::type, \
+ ParserRow<ParserDummy2>::opt, \
+ ParserRow<ParserDummy2>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ desc, 0 }
+
+#define REP_END() \
+ { 0, \
+ 0, \
+ ParserRow<ParserDummy2>::Arg, \
+ ParserRow<ParserDummy2>::Int, \
+ ParserRow<ParserDummy2>::Optional, \
+ ParserRow<ParserDummy2>::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+struct ndb_rep_handle {
+ char * hostname;
+ unsigned short port;
+
+ int connected;
+ int last_error;
+ int last_error_line;
+ int read_timeout;
+ int write_timeout;
+
+ NDB_SOCKET_TYPE socket;
+
+#ifdef REPAPI_LOG
+ FILE* logfile;
+#endif
+};
+
+#define SET_ERROR(h, e) \
+ h->last_error = e; \
+ h->last_error_line = __LINE__;
+
+extern "C"
+NdbRepHandle
+ndb_rep_create_handle(){
+ NdbRepHandle h = (NdbRepHandle)malloc(sizeof(ndb_rep_handle));
+ h->connected = 0;
+ h->last_error = 0;
+ h->last_error_line = 0;
+ h->hostname = 0;
+ h->socket = -1;
+ h->read_timeout = 50000;
+ h->write_timeout = 100;
+
+#ifdef REPAPI_LOG
+ h->logfile = 0;
+#endif
+
+ return h;
+}
+
+/**
+ * Destroy a handle
+ */
+extern "C"
+void
+ndb_rep_destroy_handle(NdbRepHandle * handle){
+ if(!handle)
+ return;
+ if((* handle)->connected){
+ ndb_rep_disconnect(* handle);
+ }
+ if((* handle)->hostname != 0){
+ free((* handle)->hostname);
+ }
+#ifdef REPAPI_LOG
+ if ((* handle)->logfile != 0){
+ fclose((* handle)->logfile);
+ (* handle)->logfile = 0;
+ }
+#endif
+ free(* handle);
+ * handle = 0;
+}
+
+/**
+ * Get latest error associated with a handle
+ */
+extern "C"
+int
+ndb_rep_get_latest_error(const NdbRepHandle h){
+ return h->last_error;
+}
+
+/**
+ * Get latest error line associated with a handle
+ */
+extern "C"
+int
+ndb_rep_get_latest_error_line(const NdbRepHandle h){
+ return h->last_error_line;
+}
+
+static
+int
+parse_connect_string(const char * connect_string,
+ NdbRepHandle handle){
+
+ if(connect_string == 0){
+ DEBUG("connect_string == 0");
+ SET_ERROR(handle, EINVAL);
+ return -1;
+ }
+
+ char * line = strdup(connect_string);
+ if(line == 0){
+ DEBUG("line == 0");
+ SET_ERROR(handle, ENOMEM);
+ return -1;
+ }
+
+ char * tmp = strchr(line, ':');
+ if(tmp == 0){
+ DEBUG("tmp == 0");
+ free(line);
+ SET_ERROR(handle, EINVAL);
+ return -1;
+ }
+ * tmp = 0; tmp++;
+
+ int port = 0;
+ if(sscanf(tmp, "%d", &port) != 1){
+ DEBUG("sscanf() != 1");
+ free(line);
+ SET_ERROR(handle, EINVAL);
+ return -1;
+ }
+
+ if(handle->hostname != 0)
+ free(handle->hostname);
+
+ handle->hostname = strdup(line);
+ handle->port = port;
+ free(line);
+ return 0;
+}
+
+/*
+ * Call an operation, and return the reply
+ */
+static const Properties *
+ndb_rep_call(NdbRepHandle handle,
+ const ParserRow<ParserDummy2> *command_reply,
+ const char *cmd,
+ const Properties *cmd_args) {
+ SocketOutputStream out(handle->socket);
+ SocketInputStream in(handle->socket, handle->read_timeout);
+
+ out.println(cmd);
+#ifdef REPAPI_LOG
+ /**
+ * Print command to log file
+ */
+ FileOutputStream f(handle->logfile);
+ f.println("OUT: %s", cmd);
+#endif
+
+ if(cmd_args != NULL) {
+ Properties::Iterator iter(cmd_args);
+ const char *name;
+ while((name = iter.next()) != NULL) {
+ PropertiesType t;
+ Uint32 val_i;
+ BaseString val_s;
+
+ cmd_args->getTypeOf(name, &t);
+ switch(t) {
+ case PropertiesType_Uint32:
+ cmd_args->get(name, &val_i);
+ out.println("%s: %d", name, val_i);
+ break;
+ case PropertiesType_char:
+ cmd_args->get(name, val_s);
+ out.println("%s: %s", name, val_s.c_str());
+ break;
+ default:
+ /* Ignore */
+ break;
+ }
+ }
+#ifdef REPAPI_LOG
+ /**
+ * Print arguments to log file
+ */
+ cmd_args->print(handle->logfile, "OUT: ");
+#endif
+ }
+ out.println("");
+
+ Parser_t::Context ctx;
+ ParserDummy2 session(handle->socket);
+ Parser_t parser(command_reply, in, true, true, true);
+
+#if 1
+ const Properties* p = parser.parse(ctx, session);
+ if (p == NULL){
+ /**
+ * Print some info about why the parser returns NULL
+ */
+ ndbout << " status=" << ctx.m_status << ", curr="<<ctx.m_currentToken << endl;
+ }
+#ifdef REPAPI_LOG
+ else {
+ /**
+ * Print reply to log file
+ */
+ p->print(handle->logfile, "IN: ");
+ }
+#endif
+ return p;
+#else
+ return parser.parse(ctx, session);
+#endif
+}
+
+/**
+ * Connect to a rep server
+ *
+ * Returns 0 if OK, sets ndb_rep_handle->last_error otherwise
+ */
+extern "C"
+int
+ndb_rep_connect(NdbRepHandle handle, const char * repsrv){
+
+ if(handle == 0)
+ return -1;
+
+ if(parse_connect_string(repsrv, handle) != 0)
+ return -1;
+
+
+#ifdef REPAPI_LOG
+ /**
+ * Open the log file
+ */
+ char logname[64];
+ snprintf(logname, 64, "repapi.log");
+ handle->logfile = fopen(logname, "w");
+#endif
+
+ /**
+ * Do connect
+ */
+ const NDB_SOCKET_TYPE sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd == NDB_INVALID_SOCKET) {
+ DEBUG("socket() == INVALID_SOCKET");
+ return -1;
+ }
+
+ struct sockaddr_in servaddr;
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = htons(handle->port);
+ // Convert ip address presentation format to numeric format
+ const int res1 = Ndb_getInAddr(&servaddr.sin_addr, handle->hostname);
+ if (res1 != 0) {
+ DEBUG("Ndb_getInAddr(...) == -1");
+ return -1;
+ }
+
+ const int res2 = connect(sockfd, (struct sockaddr*) &servaddr,
+ sizeof(servaddr));
+ if (res2 == -1) {
+ DEBUG("connect() == -1");
+ NDB_CLOSE_SOCKET(sockfd);
+ return -1;
+ }
+
+ handle->socket = sockfd;
+ handle->connected = 1;
+
+ return 0;
+}
+
+/**
+ * Disconnect from a rep server
+ */
+extern "C"
+void
+ndb_rep_disconnect(NdbRepHandle handle){
+ if(handle == 0)
+ return;
+
+ if(handle->connected != 1){
+ return;
+ }
+
+ NDB_CLOSE_SOCKET(handle->socket);
+ handle->socket = -1;
+ handle->connected = 0;
+
+ return;
+}
+
+
+
+/******************************************************************************
+ * Global Replication
+ ******************************************************************************/
+extern "C"
+int ndb_rep_command(NdbRepHandle handle,
+ unsigned int request,
+ unsigned int* replication_id,
+ struct ndb_rep_reply* /*reply*/,
+ unsigned int epoch) {
+
+ *replication_id = 0;
+
+ const ParserRow<ParserDummy2> replication_reply[] = {
+ REP_CMD("global replication reply", NULL, ""),
+ REP_ARG("result", Int, Mandatory, "Error message"),
+ REP_ARG("id", Int, Optional, "Id of global replication"),
+ REP_END()
+ };
+
+ if (handle == 0) {
+ return -1;
+ }
+
+ if (handle->connected != 1) {
+ handle->last_error = EINVAL;
+ return -1;
+ }
+
+ Properties args;
+ args.put("request", request);
+ args.put("id", *replication_id);
+ if(epoch > 0)
+ args.put("epoch",epoch);
+ else
+ args.put("epoch",(unsigned int)0);
+
+ const Properties *reply;
+ reply = ndb_rep_call(handle, replication_reply, "rep", &args);
+
+ if(reply == NULL) {
+ handle->last_error = EIO;
+ return -1;
+ }
+
+ reply->get("id", replication_id);
+ Uint32 result;
+ reply->get("result", &result);
+ delete reply;
+ return result;
+}
+
+extern "C"
+int convert2int(char * first, char * last, unsigned int f[], unsigned int l[])
+{
+ char * ftok = strtok(first, ",");
+ char * ltok = strtok(last, ",");
+ Uint32 i=0;
+ while(ftok!=NULL && ltok!=NULL)
+ {
+ f[i] = atoi(ftok);
+ l[i] = atoi(ltok);
+ ftok = strtok(NULL, ",");
+ ltok = strtok(NULL, ",");
+ i++;
+ }
+
+ return 0;
+}
+
+
+int ndb_rep_query(NdbRepHandle handle,
+ QueryCounter counter,
+ unsigned int* replicationId,
+ struct ndb_rep_reply* /*reply*/,
+ struct rep_state * state)
+{
+ *replicationId = 0; // not used currently.
+
+ if(state == 0)
+ return -1;
+
+ const ParserRow<ParserDummy2> replication_reply[] = {
+ REP_CMD("global replication query reply", NULL, ""),
+ REP_ARG("result", String, Mandatory, "Error message"),
+ REP_ARG("id", Int, Mandatory, "replicationId"),
+ REP_ARG("no_of_nodegroups", Int, Optional, "number of nodegroups"),
+ REP_ARG("subid", Int, Optional, "Id of subscription"),
+ REP_ARG("subkey", Int, Optional, "Key of subscription"),
+ REP_ARG("connected_rep", Int, Optional, "connected to rep"),
+ REP_ARG("connected_db", Int, Optional, "connected to db"),
+ REP_ARG("first", String, Optional, ""),
+ REP_ARG("last", String, Optional, ""),
+ REP_ARG("state_sub", Int, Optional, "state of subsription"),
+ REP_ARG("state", Int, Optional, "state"),
+ REP_END()
+ };
+
+ if (handle == 0) {
+ return -1;
+ }
+
+ if (handle->connected != 1) {
+ handle->last_error = EINVAL;
+ return -1;
+ }
+
+ const Properties *props;
+ Properties args;
+ Uint32 request = 0;
+ args.put("request", request);
+ args.put("id", *replicationId);
+ args.put("counter" , (Uint32)counter);
+ props = ndb_rep_call(handle, replication_reply, "rep query", &args);
+
+ BaseString result;
+ props->get("result", result);
+ if(strcmp(result.c_str(), "Ok") != 0)
+ {
+ delete props;
+ return 1;
+ }
+ state->queryCounter = counter;
+ unsigned int no_of_nodegroups;
+ props->get("no_of_nodegroups", &no_of_nodegroups);
+ state->no_of_nodegroups = no_of_nodegroups;
+
+ if(counter >= 0)
+ {
+ BaseString first, last;
+ props->get("first", first);
+ props->get("last", last);
+ convert2int((char*)first.c_str(), (char*)last.c_str(),
+ state->first , state->last );
+ } else
+ {
+ for(Uint32 i = 0; i<REPAPI_MAX_NODE_GROUPS; i++) {
+ state->first[i] = 0;
+ state->last[i] = 0;
+ }
+ }
+
+ unsigned int connected_rep = 0;
+ props->get("connected_rep", &connected_rep);
+ state->connected_rep = connected_rep;
+
+ unsigned int connected_db = 0;
+ props->get("connected_rep", &connected_db);
+ state->connected_db = connected_db;
+
+ unsigned int subid;
+ props->get("subid", &subid);
+ state->subid = subid;
+
+ unsigned int subkey;
+ props->get("subkey", &subkey);
+ state->subkey = subkey;
+
+ unsigned int _state;
+ props->get("state", &_state);
+ state->state = _state;
+
+ unsigned int state_sub;
+ props->get("state_sub", &state_sub);
+ state->state_sub = state_sub;
+
+ if(props == NULL) {
+ handle->last_error = EIO;
+ return -1;
+ }
+ delete props;
+
+ return 0;
+}
+
+
+extern "C"
+int
+ndb_rep_get_status(NdbRepHandle handle,
+ unsigned int* replication_id,
+ struct ndb_rep_reply* /*reply*/,
+ struct rep_state * repstate) {
+
+ const ParserRow<ParserDummy2> replication_reply[] = {
+ REP_CMD("global replication status reply", NULL, ""),
+ REP_ARG("result", String, Mandatory, "Error message"),
+ REP_ARG("id", Int, Optional, "Error message"),
+ REP_ARG("subid", Int, Optional, "Id of subscription"),
+ REP_ARG("subkey", Int, Optional, "Key of subscription"),
+ REP_ARG("connected_rep", Int, Optional, "connected to rep"),
+ REP_ARG("connected_db", Int, Optional, "connected to db"),
+ REP_ARG("state_sub", Int, Optional, "state of subsription"),
+ REP_ARG("state", Int, Optional, "state"),
+ REP_END()
+ };
+
+ if (handle == 0) {
+ return -1;
+ }
+
+ if (handle->connected != 1) {
+ handle->last_error = EINVAL;
+ return -1;
+ }
+
+ const Properties *reply;
+ Properties args;
+ Uint32 request = 0;
+ args.put("request", request);
+ reply = ndb_rep_call(handle, replication_reply, "rep status", &args);
+
+ if(reply == NULL) {
+ handle->last_error = EIO;
+ return -1;
+ }
+
+ Uint32 result;
+ reply->get("result", &result);
+ reply->get("id", replication_id);
+ reply->get("subid", (Uint32*)&repstate->subid);
+ reply->get("subkey", (Uint32*)&repstate->subkey);
+ reply->get("connected_rep", (Uint32*)&repstate->connected_rep);
+ reply->get("connected_db", (Uint32*)&repstate->connected_db);
+ reply->get("state", (Uint32*)&repstate->state);
+ reply->get("state_sub", (Uint32*)&repstate->state_sub);
+
+ delete reply;
+ return result;
+}
diff --git a/storage/ndb/src/old_files/rep/repapi/repapi.h b/storage/ndb/src/old_files/rep/repapi/repapi.h
new file mode 100644
index 00000000000..170e493cd86
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/repapi/repapi.h
@@ -0,0 +1,216 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REPAPI_H
+#define REPAPI_H
+
+/**
+ * @mainpage NDB Cluster REP API
+ *
+ * The NDB Cluster Replication API (REP API) consists of a C API
+ * which is used to:
+ * - Start and stop replication
+ * - Other administrative tasks
+ *
+ * The functions use simple ASCII based
+ * commands to interact with thw Replication Server.
+ *
+ *
+ * @section General Concepts
+ *
+ * Each REP API function call needs an rep_C_Api::NdbRepHandle
+ * which initally is created by
+ * calling the function ndb_rep_create_handle().
+ *
+ * A function can return:
+ * -# An integer value. If it returns 0 then this indicates success.
+ * -# A pointer value. If it returns NULL then check the latest error.
+ * If it didn't return NULL, then "something" is returned.
+ * This "something" has to be free:ed by the user of the REP API.
+ */
+
+/** @addtogroup REP_C_API
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define REPAPI_MAX_NODE_GROUPS 4
+ /**
+ * The NdbRepHandle.
+ */
+ typedef struct ndb_rep_handle * NdbRepHandle;
+
+
+ /**
+ * Default reply from the server
+ */
+ struct ndb_rep_reply {
+ int return_code; ///< 0 if successful,
+ ///< otherwise error code
+ char message[256]; ///< Error or reply message.
+ };
+
+ enum QueryCounter {
+ PS = 0, ///< Stored on Primary System REP
+ SSReq = 1, ///< Requested for transfer to Standby System
+ SS = 2, ///< Stored on Standby System REP
+ AppReq = 3, ///< Requested to be applied to Standby System
+ App = 4, ///< Has been applied to Standby System
+ DelReq = 5, ///< Has been requested to be deleted on PS REP & SS REP
+ Subscription = 6,
+ ConnectionRep = 7,
+ ConnectionDb = 8
+ };
+
+
+ struct rep_state {
+ QueryCounter queryCounter;
+ unsigned int no_of_nodegroups;
+ unsigned int connected_rep;
+ unsigned int connected_db;
+ unsigned int subid;
+ unsigned int subkey;
+ unsigned int state;
+ unsigned int state_sub;
+ unsigned int first[REPAPI_MAX_NODE_GROUPS]; //4 = max no of nodegroups
+ unsigned int last[REPAPI_MAX_NODE_GROUPS]; //4 = max no of nodegroups
+ };
+
+
+
+
+
+
+ /***************************************************************************
+ * FUNCTIONS
+ ***************************************************************************/
+ /**
+ * Create a handle
+ *
+ * @return A handle != 0
+ * or 0 if failed to create one. (Check errno then).
+ */
+ NdbRepHandle ndb_rep_create_handle();
+
+ /**
+ * Destroy a handle
+ *
+ * @param handle Rep server handle
+ */
+ void ndb_rep_destroy_handle(NdbRepHandle * handle);
+
+ /**
+ * Get latest error associated with a handle
+ *
+ * @param handle Rep server handle
+ * @return Latest error.
+ */
+ int ndb_rep_get_latest_error(const NdbRepHandle handle);
+
+ /**
+ * Get latest error line associated with a handle
+ *
+ * @param handle Rep server handle.
+ * @return Latest error line.
+ */
+ int ndb_rep_get_latest_error_line(const NdbRepHandle handle);
+
+ /**
+ * Connect to a REP server
+ *
+ * @param handle Rep server handle.
+ * @param repsrv Hostname and port of the REP server,
+ * "hostname:port".
+ * @return 0 if OK, sets ndb_rep_handle->last_error otherwise.
+ */
+ int ndb_rep_connect(NdbRepHandle handle, const char * repsrv);
+
+ /**
+ * Disconnect from a REP server
+ *
+ * @param handle Rep server handle.
+ */
+ void ndb_rep_disconnect(NdbRepHandle handle);
+
+
+ /**
+ * Global Replication Command
+ *
+ * @param handle NDB REP handle.
+ * @param request Type of request
+ * @param replicationId Replication id is returned from function.
+ * @param reply Reply message.
+ * @param epoch Currenty used to STOP at a certain EPOCH
+ * @return 0 if successful, error code otherwise.
+ */
+ int ndb_rep_command(NdbRepHandle handle,
+ unsigned int request,
+ unsigned int* replicationId,
+ struct ndb_rep_reply* reply,
+ unsigned int epoch = 0);
+
+
+ /**
+ * Global Replication Command
+ *
+ * @param handle NDB REP handle.
+ * @param counter Type of request. If <0, then
+ "first" and "last" in repstate
+ is set to 0;x
+ * @param replicationId Replication id is returned from function.
+ * @param reply Reply message.
+ * @param repstate Struct containing queried data. (Note!
+ * All values are set in the struct, regardless
+ which QueryCounter that has been set
+ * @return 0 if successful, error code otherwise.
+ */
+ int ndb_rep_query(NdbRepHandle handle,
+ QueryCounter counter,
+ unsigned int* replicationId,
+ struct ndb_rep_reply* reply,
+ struct rep_state * repstate);
+
+
+/**
+ * @deprecated (will probably be). Can use ndb_rep_query instead.
+ */
+ int ndb_rep_get_status(NdbRepHandle handle,
+ unsigned int* replication_id,
+ struct ndb_rep_reply* /*reply*/,
+ struct rep_state * repstate);
+
+
+
+ enum RequestStatusCode {
+ OK = 0, ///< Everything OK
+ Error = 1, ///< Generic error
+ AlreadyExists = 2, ///< Entry already exists in list
+ NotExists = 3, ///< Entry does not exist in list
+ AlreadyStopped = 4
+ };
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/state/Channel.cpp b/storage/ndb/src/old_files/rep/state/Channel.cpp
new file mode 100644
index 00000000000..a7f7b90d3fe
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/Channel.cpp
@@ -0,0 +1,487 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Channel.hpp"
+
+Channel::Channel()
+{
+ reset();
+}
+
+Channel::~Channel()
+{
+ /**
+ * Destroy list of selected tables
+ */
+ for(Uint32 i=0; i < m_selectedTables.size(); i++) {
+ delete m_selectedTables[i];
+ m_selectedTables[i] = 0;
+ }
+ m_selectedTables=0;
+}
+
+void
+Channel::reset()
+{
+ for (Uint32 i=0; i<MAX_NO_OF_NODE_GROUPS; i++) {
+ for (Uint32 j=0; j<NO_OF_POSITIONS; j++) {
+ state[i][j].set(1,0);
+ }
+ }
+ m_noOfNodeGroups = 0;
+ m_requestorEnabled = true;
+ m_transferEnabled = true;
+ m_applyEnabled = true;
+ m_deleteEnabled = true;
+ m_autoStartEnabled = false;
+ m_stopEpochId = intervalMax;
+ setSubKey(0);
+ setSubId(0);
+ m_stateSub = NO_SUBSCRIPTION_EXISTS;
+ m_stateRep = CONSISTENT;
+ m_metaScanEpochs = emptyInterval;
+ m_dataScanEpochs = emptyInterval;
+}
+
+bool
+Channel::requestTransfer(Uint32 nodeGrp, Interval * i)
+{
+ invariant();
+ Interval tmp1, tmp2;
+
+ // i = PS - SSReq - SS - App
+ intervalLeftMinus(state[nodeGrp][PS], state[nodeGrp][SSReq], &tmp1);
+ intervalLeftMinus(tmp1, state[nodeGrp][SS], &tmp2);
+ intervalLeftMinus(tmp2, state[nodeGrp][App], i);
+
+ i->onlyLeft(GREP_SYSTEM_TABLE_MAX_RANGE);
+ i->onlyUpToValue(m_stopEpochId);
+ if (i->isEmpty()) return false;
+
+ add(SSReq, nodeGrp, *i);
+ invariant();
+ return true;
+}
+
+bool
+Channel::requestApply(Uint32 nodeGrp, Uint32 * epoch)
+{
+ invariant();
+ Interval tmp1, tmp2;
+
+ // tmp2 = SS - AppReq - App
+ intervalLeftMinus(state[nodeGrp][SS], state[nodeGrp][AppReq], &tmp1);
+ intervalLeftMinus(tmp1, state[nodeGrp][App], &tmp2);
+
+ tmp2.onlyUpToValue(m_stopEpochId);
+ if (tmp2.isEmpty()) return false;
+ tmp2.onlyLeft(1);
+
+ // Check that all GCI Buffers for epoch exists in SS
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ if (!state[nodeGrp][SS].inInterval(tmp2.first()))
+ return false;
+ }
+
+ invariant();
+ add(AppReq, nodeGrp, tmp2);
+ invariant();
+ *epoch = tmp2.first();
+ return true;
+}
+
+bool
+Channel::requestDelete(Uint32 nodeGrp, Interval * i)
+{
+ invariant();
+ Interval tmp1;
+
+ // i = (App cut PS) - DelReq
+ intervalCut(state[nodeGrp][App], state[nodeGrp][PS], &tmp1);
+ intervalLeftMinus(tmp1, state[nodeGrp][DelReq], i);
+
+ if (i->isEmpty()) return false;
+ i->onlyLeft(GREP_SYSTEM_TABLE_MAX_RANGE);
+
+ invariant();
+ add(DelReq, nodeGrp, *i);
+ invariant();
+ return true;
+}
+
+void
+Channel::add(Position pos, Uint32 nodeGrp, const Interval i)
+{
+ Interval r;
+ intervalAdd(state[nodeGrp][pos], i, &r);
+ state[nodeGrp][pos].set(r);
+}
+
+void
+Channel::clear(Position p, Uint32 nodeGrp, const Interval i)
+{
+ Interval r;
+ intervalLeftMinus(state[nodeGrp][p], i, &r);
+ state[nodeGrp][p].set(r);
+}
+
+bool
+Channel::isSynchable(Uint32 nodeGrp)
+{
+ return true;
+ /*
+ @todo This should be implemented...
+
+ Interval tmp1, tmp2;
+ intervalAdd(state[nodeGrp][PS], state[nodeGrp][SSReq], &tmp1);
+ intervalAdd(tmp1, state[nodeGrp][SSReq], &tmp2);
+ intervalAdd(tmp2, state[nodeGrp][SS], &tmp1);
+ intervalAdd(tmp1, state[nodeGrp][AppReq], &tmp2);
+ intervalAdd(tmp2, state[nodeGrp][App], &tmp1);
+ if (intervalInclude(state[nodeGrp][PS], tmp1.right()))
+ return true;
+ else
+ return false;
+ */
+}
+
+/**
+ * Return the cut of all App:s.
+ */
+void
+Channel::getFullyAppliedEpochs(Interval * interval)
+{
+ if (m_noOfNodeGroups < 1) {
+ *interval = emptyInterval;
+ return;
+ }
+
+ *interval = universeInterval;
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ if (state[i][App].isEmpty()) {
+ *interval = emptyInterval;
+ return;
+ }
+
+ if (interval->first() < state[i][App].first()) {
+ interval->setFirst(state[i][App].first());
+ }
+ if (state[i][App].last() < interval->last()) {
+ interval->setLast(state[i][App].last());
+ }
+ }
+ interval->normalize();
+ return;
+}
+
+/**
+ * Return true if it is ok to remove the subscription and then stop channel
+ */
+bool
+Channel::isStoppable()
+{
+ /**
+ * Check that AppReq are empty for all nodegrps
+ */
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ if (!state[i][AppReq].isEmpty()) {
+ RLOG(("Stop disallowed. AppReq is non-empty"));
+ return false;
+ }
+ }
+
+ /**
+ * If stop immediately, then it is ok to stop now
+ */
+ if (m_stopEpochId == 0) {
+ RLOG(("Stop allowed. AppReq empty and immediate stop requested"));
+ return true;
+ }
+
+ /**
+ * If stop on a certain epoch, then
+ * check that stopEpochId is equal to the last applied epoch
+ */
+ Interval interval;
+ getFullyAppliedEpochs(&interval);
+ if (m_stopEpochId > interval.last()) {
+ RLOG(("Stop disallowed. AppReq empty. Stop %d, LastApplied %d",
+ m_stopEpochId, interval.last()));
+ return false;
+ }
+
+ return true;
+}
+
+GrepError::Code
+Channel::setStopEpochId(Uint32 n)
+{
+ /**
+ * If n equal to zero, use next possible epoch (max(App, AppReq))
+ */
+ if (n == 0) {
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ n = (state[i][App].last() > n) ? state[i][App].last() : n;
+ n = (state[i][AppReq].last() > n) ? state[i][AppReq].last() : n;
+ }
+ }
+
+ /**
+ * If n >= max(App, AppReq) then set value, else return error code
+ */
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ if (n < state[i][App].last()) return GrepError::ILLEGAL_STOP_EPOCH_ID;
+ if (n < state[i][AppReq].last()) return GrepError::ILLEGAL_STOP_EPOCH_ID;
+ }
+
+ m_stopEpochId = n;
+ return GrepError::NO_ERROR;
+};
+
+bool
+Channel::shouldStop()
+{
+ /**
+ * If (m_stopEpochId == App) then channel should stop
+ */
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ if(m_stopEpochId != state[i][App].last()) return false;
+ }
+ return true;
+}
+
+/*****************************************************************************
+ * SELECTIVE TABLE INTERFACE
+ *****************************************************************************/
+
+GrepError::Code
+Channel::addTable(const char * tableName)
+{
+ if(strlen(tableName)>MAX_TAB_NAME_SIZE)
+ return GrepError::REP_NOT_PROPER_TABLE;
+ /**
+ * No of separators are the number of table_name_separator found in tableName
+ * since a table is defined as <db>/<schema>/tablename.
+ * if noOfSeparators is not equal to 2, then it is not a valid
+ * table name.
+ */
+ Uint32 noOfSeps = 0;
+ if(strlen(tableName) < 5)
+ return GrepError::REP_NOT_PROPER_TABLE;
+ for(Uint32 i =0; i < strlen(tableName); i++)
+ if(tableName[i]==table_name_separator)
+ noOfSeps++;
+ if(noOfSeps!=2)
+ return GrepError::REP_NOT_PROPER_TABLE;
+ table * t= new table(tableName);
+ for(Uint32 i=0; i<m_selectedTables.size(); i++) {
+ if(strcmp(tableName, m_selectedTables[i]->tableName)==0)
+ return GrepError::REP_TABLE_ALREADY_SELECTED;
+ }
+ m_selectedTables.push_back(t);
+ return GrepError::NO_ERROR;
+}
+
+GrepError::Code
+Channel::removeTable(const char * tableName)
+{
+ if(strlen(tableName)>MAX_TAB_NAME_SIZE)
+ return GrepError::REP_NOT_PROPER_TABLE;
+ /**
+ * No of separators are the number of table_name_separator found in tableName
+ * since a table is defined as <db>/<schema>/tablename.
+ * If noOfSeparators is not equal to 2,
+ * then it is not a valid table name.
+ */
+ Uint32 noOfSeps = 0;
+ if(strlen(tableName) < 5)
+ return GrepError::REP_NOT_PROPER_TABLE;
+ for(Uint32 i =0; i < strlen(tableName); i++)
+ if(tableName[i]==table_name_separator)
+ noOfSeps++;
+ if(noOfSeps!=2)
+ return GrepError::REP_NOT_PROPER_TABLE;
+ for(Uint32 i=0; i<m_selectedTables.size(); i++) {
+ if(strcmp(tableName, m_selectedTables[i]->tableName)==0) {
+ delete m_selectedTables[i];
+ m_selectedTables.erase(i);
+ return GrepError::NO_ERROR;
+ }
+ }
+ return GrepError::REP_TABLE_NOT_FOUND;
+}
+
+void
+Channel::printTables()
+{
+ if(m_selectedTables.size() == 0)
+ ndbout_c("| ALL TABLES "
+ " |");
+ else {
+ for(Uint32 i=0; i<m_selectedTables.size(); i++)
+ ndbout_c("| %-69s |", m_selectedTables[i]->tableName);
+ }
+}
+
+Vector<struct table *> *
+Channel::getSelectedTables()
+{
+ if(m_selectedTables.size() == 0) return 0;
+ return &m_selectedTables;
+}
+
+/*****************************************************************************
+ * PRINT
+ *****************************************************************************/
+
+void
+Channel::print(Position pos)
+{
+ switch(pos){
+ case PS: ndbout << "PS Rep"; break;
+ case SSReq: ndbout << "Tra-Req"; break;
+ case SS: ndbout << "SS Rep"; break;
+ case AppReq: ndbout << "App-Req"; break;
+ case App: ndbout << "Applied"; break;
+ case DelReq: ndbout << "Del-Req"; break;
+ default: REPABORT("Unknown replication position");
+ }
+}
+
+void
+Channel::print()
+{
+ for (Uint32 i=0; i<m_noOfNodeGroups; i++) {
+ print(i);
+ }
+}
+
+void
+Channel::print(Position pos, Uint32 nodeGrp)
+{
+ print(pos);
+ if (state[nodeGrp][pos].first() == 1 && state[nodeGrp][pos].last() == 0) {
+ ndbout << " EMPTY";
+ } else {
+ ndbout << " [" << state[nodeGrp][pos].first() << "-"
+ << state[nodeGrp][pos].last() << "]";
+ }
+}
+
+static const char*
+channelline =
+"+-------------------------------------------------------------------------+\n"
+;
+
+void
+Channel::getEpochState(Position p,
+ Uint32 nodeGrp,
+ Uint32 * first,
+ Uint32 * last) {
+ if(state[nodeGrp][p].isEmpty()) {
+ *first = 1;
+ *last = 0;
+ return;
+ }
+ *first = state[nodeGrp][p].first();
+ *last = state[nodeGrp][p].last();
+}
+
+
+void
+Channel::print(Uint32 nodeGrp)
+{
+ ndbout << channelline;
+ ndbout_c("| | Meta scan |"
+ " Data scan |");
+ ndbout.print("| ");
+ if (m_metaScanEpochs.isEmpty()) {
+ ndbout.print("| ");
+ } else {
+ ndbout.print("| %10u-%-10u ",
+ m_metaScanEpochs.first(), m_metaScanEpochs.last());
+ }
+ if (m_dataScanEpochs.isEmpty()) {
+ ndbout_c("| |");
+ } else {
+ ndbout_c("| %10u-%-10u |",
+ m_dataScanEpochs.first(), m_dataScanEpochs.last());
+ }
+
+ /* --- */
+
+ ndbout << channelline;
+ ndbout_c("| Source Rep Server | Being Transfered |"
+ " Destination Rep Server |");
+ if (state[nodeGrp][PS].isEmpty()) {
+ ndbout.print("| ");
+ } else {
+ ndbout.print("| %10u-%-10u ",
+ state[nodeGrp][PS].first(), state[nodeGrp][PS].last());
+ }
+ if (state[nodeGrp][SSReq].isEmpty()) {
+ ndbout.print("| ");
+ } else {
+ ndbout.print("| %10u-%-10u ",
+ state[nodeGrp][SSReq].first(), state[nodeGrp][SSReq].last());
+ }
+ if (state[nodeGrp][SS].isEmpty()) {
+ ndbout_c("| |");
+ } else {
+ ndbout_c("| %10u-%-10u |",
+ state[nodeGrp][SS].first(), state[nodeGrp][SS].last());
+ }
+
+ /* --- */
+
+ ndbout << channelline;
+ ndbout_c("| Being Applied | Applied |"
+ " Being Deleted |");
+ if (state[nodeGrp][AppReq].isEmpty()) {
+ ndbout.print("| ");
+ } else {
+ ndbout.print("| %10u-%-10u ", state[nodeGrp][AppReq].first(),
+ state[nodeGrp][AppReq].last());
+ }
+ if (state[nodeGrp][App].isEmpty()) {
+ ndbout.print("| ");
+ } else {
+ ndbout.print("| %10u-%-10u ",
+ state[nodeGrp][App].first(), state[nodeGrp][App].last());
+ }
+ if (state[nodeGrp][DelReq].isEmpty()) {
+ ndbout_c("| |");
+ } else {
+ ndbout_c("| %10u-%-10u |",
+ state[nodeGrp][DelReq].first(), state[nodeGrp][DelReq].last());
+ }
+}
+
+/*****************************************************************************
+ * Private Methods
+ *****************************************************************************/
+
+void
+Channel::invariant()
+{
+ for (Uint32 j=0; j<MAX_NO_OF_NODE_GROUPS; j++)
+ {
+ if (!intervalDisjoint(state[j][SSReq], state[j][SS]))
+ REPABORT("Invariant 1 violated");
+ if (!intervalDisjoint(state[j][AppReq], state[j][App]))
+ REPABORT("Invariant 2 violated");
+ }
+}
diff --git a/storage/ndb/src/old_files/rep/state/Channel.hpp b/storage/ndb/src/old_files/rep/state/Channel.hpp
new file mode 100644
index 00000000000..cdf4eecca63
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/Channel.hpp
@@ -0,0 +1,206 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 CHANNEL_HPP
+#define CHANNEL_HPP
+
+#include "Interval.hpp"
+#include <rep/rep_version.hpp>
+#include <Vector.hpp>
+#include <ndb_limits.h>
+#include <GrepError.hpp>
+
+
+/**
+ * Max number of requested epochs from PS
+ */
+#define GREP_SYSTEM_TABLE_MAX_RANGE 20
+
+#define MAX_NO_OF_NODE_GROUPS 32
+
+/**
+ * This table struct is used in m_selectedTables
+ */
+struct table{
+ table(const char * n) {strncpy(tableName, n, MAX_TAB_NAME_SIZE);}
+ char tableName[MAX_TAB_NAME_SIZE];
+};
+
+/**
+ * @class Channel
+ * @brief Represents location of various epochs belonging to a subscription
+ */
+class Channel {
+public:
+ enum StateSub
+ {
+ NO_SUBSCRIPTION_EXISTS,
+
+ CREATING_SUBSCRIPTION_ID,
+ SUBSCRIPTION_ID_CREATED,
+
+ STARTING_SUBSCRIPTION,
+ SUBSCRIPTION_STARTED
+ };
+
+ enum StateRep
+ {
+ CONSISTENT, ///< Consistent database. Grep not running.
+ METALOG_STARTING, ///< Starting. Starting METALOG subscription
+ METALOG_STARTED,
+ METASCAN_STARTING, ///< Starting. Starting METASCAN subscription
+ METASCAN_COMPLETED,
+ DATALOG_STARTING, ///< Starting. Starting DATALOG subscription
+ DATALOG_STARTED,
+ DATASCAN_STARTING, ///< Starting. Starting DATASCAN subscription
+ DATASCAN_COMPLETED,
+ LOG, ///< Started. Cons/Inconsistent. Grep running.
+ ///< All scan records have been applied.
+ STOPPING ///< Channel is stopping
+ };
+
+ /**
+ * Storage "positions" of Epochs
+ */
+ enum Position {
+ PS = 0, ///< Stored on Primary System REP
+ SSReq = 1, ///< Requested for transfer to Standby System
+ SS = 2, ///< Stored on Standby System REP
+ AppReq = 3, ///< Requested to be applied to Standby System
+ App = 4, ///< Has been applied to Standby System
+ DelReq = 5, ///< Has been requested to be deleted on PS REP & SS REP
+ NO_OF_POSITIONS = 6
+ }; //DONT FORGET TO ADD STUFF in position2Name if u add somehting here,
+
+ /***************************************************************************
+ * CONSTRUCTOR / DESTRUCTOR
+ ***************************************************************************/
+ Channel();
+ ~Channel();
+
+ /**
+ * Get and set no of nodegroups that actually exists on PS
+ */
+ void setNoOfNodeGroups(Uint32 n) { m_noOfNodeGroups = n; };
+ Uint32 getNoOfNodeGroups() { return m_noOfNodeGroups; };
+ void getEpochState(Position p,
+ Uint32 nodeGrp,
+ Uint32 * first,
+ Uint32 * last);
+ Uint32 getEpochState(Position p, Uint32 nodegroup);
+ bool m_requestorEnabled;
+ bool m_transferEnabled;
+ bool m_applyEnabled;
+ bool m_deleteEnabled;
+ bool m_autoStartEnabled;
+
+ /***************************************************************************
+ * GETTERS and SETTERS
+ ***************************************************************************/
+ bool requestTransfer(Uint32 nodeGrp, Interval * i);
+ bool requestApply(Uint32 nodeGrp, Uint32 * epoch);
+ bool requestDelete(Uint32 nodeGrp, Interval * i);
+
+ void add(Position pos, Uint32 nodeGrp, const Interval i);
+ void clear(Position pos, Uint32 nodeGrp, const Interval i);
+
+ void setSubId(Uint32 subId) { m_subId=subId; };
+ Uint32 getSubId() { return m_subId; };
+
+ Uint32 getSubKey() { return m_subKey; };
+ void setSubKey(Uint32 subKey) { m_subKey=subKey; };
+
+ bool isSynchable(Uint32 nodeGrp);
+ GrepError::Code addTable(const char * tableName);
+ GrepError::Code removeTable(const char * tableName);
+ void printTables();
+ bool isSelective() {return m_selectedTables.size()>0;};
+ Vector<struct table *> * getSelectedTables();
+
+ void reset();
+
+ StateRep getState() { return m_stateRep; }
+ void setState(StateRep sr) { m_stateRep = sr; }
+
+ StateSub getStateSub() { return m_stateSub; }
+ void setStateSub(StateSub ss) { m_stateSub = ss; }
+
+ Interval getMetaScanEpochs() { return m_metaScanEpochs; }
+ void setMetaScanEpochs(Interval i) { m_metaScanEpochs = i; }
+ Interval getDataScanEpochs() { return m_dataScanEpochs; }
+ void setDataScanEpochs(Interval i) { m_dataScanEpochs = i; }
+
+ GrepError::Code setStopEpochId(Uint32 n);
+ Uint32 getStopEpochId() { return m_stopEpochId; };
+
+ bool isStoppable();
+ bool shouldStop();
+
+ bool subscriptionExists() { return (m_subId != 0 && m_subKey != 0); }
+
+ /***************************************************************************
+ * GETTERS
+ ***************************************************************************/
+ Uint32 getFirst(Position pos, Uint32 nodeGrp) {
+ return state[nodeGrp][pos].first();
+ }
+
+ Uint32 getLast(Position pos, Uint32 nodeGrp) {
+ return state[nodeGrp][pos].last();
+ }
+
+ void getFullyAppliedEpochs(Interval * i);
+
+ /***************************************************************************
+ * PRINT METHODS
+ ***************************************************************************/
+ void print();
+ void print(Position pos);
+ void print(Position pos, Uint32 nodeGrp);
+ void print(Uint32 nodeGrp);
+
+ /***************************************************************************
+ * PUBLIC ATTRIBUTES
+ ***************************************************************************/
+
+private:
+ /***************************************************************************
+ * PRIVATE ATTRIBUTES
+ ***************************************************************************/
+ StateRep m_stateRep; // Replication state
+ StateSub m_stateSub; // Subscription state
+
+ Uint32 m_subId;
+ Uint32 m_subKey;
+
+ Uint32 m_noOfNodeGroups; // Number of node grps in this channel
+ Uint32 m_stopEpochId; // Epoch id to stop subscription
+
+ Interval state[MAX_NO_OF_NODE_GROUPS][NO_OF_POSITIONS];
+
+ Interval m_metaScanEpochs;
+ Interval m_dataScanEpochs;
+
+
+ Vector<struct table *> m_selectedTables;
+ void invariant(); // Abort if channel metadata is inconsistent
+ char * position2Name(Position p);
+public:
+ bool copy(Position from, Position to, Uint32 range,
+ Uint32 * f, Uint32 * l, Uint32 nodeGrp);
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/state/Interval.cpp b/storage/ndb/src/old_files/rep/state/Interval.cpp
new file mode 100644
index 00000000000..8266f19c58d
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/Interval.cpp
@@ -0,0 +1,171 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "Interval.hpp"
+
+#undef min
+#undef max
+Uint32 max(Uint32 a, Uint32 b) { return a > b ? a : b; }
+Uint32 min(Uint32 a, Uint32 b) { return a < b ? a : b; }
+
+Interval::Interval()
+{
+ set(1, 0); // EmptyInterval
+}
+
+Interval::Interval(Uint32 f, Uint32 l)
+{
+ set(f, l);
+}
+
+bool
+Interval::isEmpty() const
+{
+ return (m_first > m_last) ? true : false;
+}
+
+bool
+Interval::isEqual(Uint32 a, Uint32 b) const
+{
+ return (a==m_first && b==m_last);
+}
+
+bool
+Interval::inInterval(Uint32 a) const
+{
+ return (m_first <= a && a <= m_last);
+}
+
+void
+Interval::set(Uint32 first, Uint32 last)
+{
+ m_first = first;
+ m_last = last;
+ normalize();
+}
+
+void
+Interval::set(const Interval i)
+{
+ m_first = i.first();
+ m_last = i.last();
+ normalize();
+}
+
+void
+Interval::setFirst(Uint32 first)
+{
+ m_first = first;
+}
+
+void
+Interval::setLast(Uint32 last)
+{
+ m_last = last;
+}
+
+void
+Interval::onlyLeft(Uint32 n)
+{
+ if (size() > n) m_last = m_first + n - 1;
+}
+
+void
+Interval::onlyUpToValue(Uint32 n)
+{
+ m_last = min(n, m_last);
+ normalize();
+}
+
+/*****************************************************************************/
+
+void
+Interval::normalize()
+{
+ if (isEmpty()) {
+ m_first = 1;
+ m_last = 0;
+ }
+}
+
+
+/*****************************************************************************/
+
+bool
+intervalAdd(const Interval a, const Interval b, Interval * r)
+{
+ /**
+ * Non-empty disjoint intervals
+ */
+ if (!a.isEmpty() &&
+ !b.isEmpty() &&
+ (a.last() + 1 < b.first() ||
+ b.last() + 1 < a.first()) ) {
+ return false; // Illegal add
+ }
+
+ /**
+ * Interval A empty -> return B
+ */
+ if (a.isEmpty()) {
+ r->set(b);
+ return true;
+ }
+
+ /**
+ * Interval B empty -> return A
+ */
+ if (b.isEmpty()) {
+ r->set(a);
+ return true;
+ }
+
+ r->set(min(a.first(), b.first()),
+ max(a.last(), b.last()));
+ return true;
+}
+
+/**
+ * Subtract the left part of interval 'a' up to last of 'b'.
+ *
+ * @note This is NOT ordinary arithmetic interval minus.
+ * In ordinary arithmetic, [11-25] - [12-15] would be undefined,
+ * but here it is [11-25] - [12-15] = [16-25].
+ */
+void
+intervalLeftMinus(const Interval a, const Interval b, Interval * r)
+{
+ if(b.last() != intervalMax)
+ r->set(max(a.first(), b.last()+1), a.last());
+ else
+ r->set(max(a.first(), intervalMax), a.last());
+}
+
+void
+intervalCut(const Interval a, const Interval b, Interval * r)
+{
+ r->set(max(a.first(), b.first()), min(a.last(), b.last()));
+ r->normalize();
+}
+
+bool
+intervalDisjoint(const Interval a, const Interval b)
+{
+ return (a.isEmpty() ||
+ b.isEmpty() ||
+ a.last() < b.first() ||
+ b.last() < a.first());
+}
diff --git a/storage/ndb/src/old_files/rep/state/Interval.hpp b/storage/ndb/src/old_files/rep/state/Interval.hpp
new file mode 100644
index 00000000000..935adaf26b1
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/Interval.hpp
@@ -0,0 +1,107 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 INTERVAL_HPP
+#define INTERVAL_HPP
+
+#include <NdbOut.hpp>
+#include <ndb_types.h>
+
+/**
+ * @class Interval
+ * @brief Represents an interval
+ */
+class Interval {
+public:
+ Interval();
+ Interval(Uint32, Uint32);
+
+ /**
+ * Getters of first and last
+ */
+ inline Uint32 first() const { return m_first; }
+ inline Uint32 last() const { return m_last; }
+
+ /**
+ * Check if interval is empty
+ */
+ bool isEmpty() const;
+ bool isEqual(Uint32 a, Uint32 b) const;
+ bool inInterval(Uint32 a) const;
+
+ /**
+ * Size of interval
+ */
+ Uint32 size() const {
+ return (!isEmpty()) ? m_last - m_first + 1 : 0;
+ }
+
+ /**
+ * Set interval
+ */
+ void set(Uint32 first, Uint32 last);
+ void set(const Interval i);
+
+ void setFirst(Uint32 first);
+ void setLast(Uint32 last);
+
+ /**
+ * Reduce the interval to only the n left elements of the
+ * interval. If the interval is shorter than n, then
+ * interval is not changed.
+ */
+ void onlyLeft(Uint32 n);
+
+ /**
+ * Reduce the interval to have at most the value n
+ * as the last value.
+ * This method can make the interval empty.
+ */
+ void onlyUpToValue(Uint32 n);
+
+ /**
+ * Print
+ */
+ void print() {
+ ndbout << "[" << m_first << "," << m_last << "]";
+ }
+
+ void normalize();
+private:
+ Uint32 m_first;
+ Uint32 m_last;
+};
+
+const Uint32 intervalMin = 0;
+const Uint32 intervalMax = 0xffffffff;
+const Interval emptyInterval(1, 0);
+const Interval universeInterval(intervalMin, intervalMax);
+
+/**
+ * @return true if intervals could be added
+ */
+bool intervalAdd(const Interval a, const Interval b, Interval * c);
+
+void intervalLeftMinus(const Interval a, const Interval b, Interval * c);
+
+void intervalCut(const Interval a, const Interval b, Interval * c);
+
+/**
+ * @return true if intervals are disjoint
+ */
+bool intervalDisjoint(const Interval a, const Interval b);
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/state/Makefile b/storage/ndb/src/old_files/rep/state/Makefile
new file mode 100644
index 00000000000..3eed69a97dd
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/Makefile
@@ -0,0 +1,17 @@
+include .defs.mk
+
+TYPE := repserver kernel
+
+ARCHIVE_TARGET := reprequestor
+
+DIR := testRepState \
+ testInterval
+
+SOURCES = RepState.cpp \
+ RepStateEvent.cpp \
+ RepStateRequests.cpp \
+ \
+ Channel.cpp \
+ Interval.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/state/RepState.cpp b/storage/ndb/src/old_files/rep/state/RepState.cpp
new file mode 100644
index 00000000000..d8a50961a3c
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/RepState.cpp
@@ -0,0 +1,869 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "RepState.hpp"
+
+#include <signaldata/SumaImpl.hpp>
+#include <NdbApiSignal.hpp>
+#include <Properties.hpp>
+//#define DBUG_REQUESTOR
+
+#ifdef DBUG_REQUESTOR
+#define DBUG_REQUESTOR_PRINT(X) ndbout_c(X);
+#else
+#define DBUG_REQUESTOR_PRINT(X)
+#endif
+
+/****************************************************************************
+ * Constructor / Destructor / Init
+ ****************************************************************************/
+RepState::RepState()
+{
+ m_connected = UNKNOWN;
+ m_repConnected = UNKNOWN;
+ m_mutex = NdbMutex_Create();
+ m_stopEpoch = 0;
+ m_subIdToRemove = 0;
+ m_subKeyToRemove = 0;
+}
+
+RepState::~RepState()
+{
+ NdbMutex_Destroy(m_mutex);
+}
+
+void
+RepState::setSubscriptionRequests(FuncRequestCreateSubscriptionId f1,
+ FuncRequestCreateSubscription f2,
+ FuncRequestRemoveSubscription f3)
+{
+ m_funcRequestCreateSubscriptionId = f1;
+ m_funcRequestCreateSubscription = f2;
+ m_funcRequestRemoveSubscription = f3;
+}
+
+void
+RepState::setIntervalRequests(FuncRequestTransfer f1,
+ FuncRequestApply f2,
+ FuncRequestDeleteSS f3,
+ FuncRequestDeletePS f4)
+{
+ m_funcRequestTransfer = f1;
+ m_funcRequestApply = f2;
+ m_funcRequestDeleteSS = f3;
+ m_funcRequestDeletePS = f4;
+}
+
+void
+RepState::setStartRequests(FuncRequestStartMetaLog * f5,
+ FuncRequestStartDataLog * f6,
+ FuncRequestStartMetaScan * f7,
+ FuncRequestStartDataScan * f8,
+ FuncRequestEpochInfo * f9)
+{
+ m_funcRequestStartMetaLog = f5;
+ m_funcRequestStartDataLog = f6;
+ m_funcRequestStartMetaScan = f7;
+ m_funcRequestStartDataScan = f8;
+ m_funcRequestEpochInfo = f9;
+}
+
+
+/****************************************************************************
+ * Private Helper functions
+ ****************************************************************************/
+
+void
+RepState::requestTransfer(NdbApiSignal * signal)
+{
+ DBUG_REQUESTOR_PRINT("RepState: Transfer calculations started");
+ for(Uint32 nodeGrp=0; nodeGrp<m_channel.getNoOfNodeGroups(); nodeGrp++) {
+ DBUG_REQUESTOR_PRINT("RepState: Transfer calc for node grp");
+ Interval i;
+ if (m_channel.requestTransfer(nodeGrp, &i)) {
+ m_funcRequestTransfer(m_extSender, signal, nodeGrp, i.first(), i.last());
+ }
+ }
+}
+
+void
+RepState::requestApply(NdbApiSignal * signal)
+{
+ DBUG_REQUESTOR_PRINT("RepState: Apply calculations started");
+ for(Uint32 nodeGrp=0; nodeGrp<m_channel.getNoOfNodeGroups(); nodeGrp++) {
+ DBUG_REQUESTOR_PRINT("RepState: Apply calc for node grp");
+ Uint32 gci;
+ if (m_channel.requestApply(nodeGrp, &gci)) {
+ Uint32 force = (m_channel.getState() == Channel::LOG) ? 0 : 1;
+ m_funcRequestApply(m_applier, signal, nodeGrp, gci, gci, force);
+ }
+ }
+}
+
+void
+RepState::requestDelete(NdbApiSignal * signal)
+{
+ DBUG_REQUESTOR_PRINT("RepState: Delete calculations started");
+ for(Uint32 nodeGrp=0; nodeGrp<m_channel.getNoOfNodeGroups(); nodeGrp++) {
+ DBUG_REQUESTOR_PRINT("RepState: Delete calc for node grp");
+ Interval i;
+ if (m_channel.requestDelete(nodeGrp, &i)){
+ m_funcRequestDeleteSS(m_gciContainer, signal, nodeGrp,
+ i.first(), i.last());
+ m_funcRequestDeletePS(m_extSender, signal, nodeGrp, i.first(), i.last());
+ }
+ }
+}
+
+void
+RepState::requestEpochInfo(NdbApiSignal * signal)
+{
+ DBUG_REQUESTOR_PRINT("RepState: Epoch Info calculations");
+ for(Uint32 nodeGrp=0; nodeGrp<m_channel.getNoOfNodeGroups(); nodeGrp++) {
+ m_funcRequestEpochInfo(m_extSender, signal, nodeGrp);
+ }
+}
+
+/****************************************************************************
+ * Public
+ ****************************************************************************/
+
+GrepError::Code
+RepState::add(Channel::Position s, Uint32 nodeGrp, const Interval i)
+{
+ m_channel.add(s, nodeGrp, i);
+
+ if(s == Channel::PS)
+ {
+ m_connected = CONNECTED;
+ m_connected_counter = 0;
+ }
+
+ Interval fullEpochs;
+ m_channel.getFullyAppliedEpochs(&fullEpochs);
+ if(s == Channel::App &&
+ m_channel.getState() == Channel::DATASCAN_COMPLETED &&
+ fullEpochs.last() >= m_channel.getDataScanEpochs().last() &&
+ fullEpochs.last() >= m_channel.getMetaScanEpochs().last())
+ {
+ RLOG(("[%d-%d] fully applied. Channel state changed to LOG",
+ fullEpochs.first(), fullEpochs.last()));
+ m_channel.setState(Channel::LOG);
+ disableAutoStart();
+ }
+
+ return GrepError::NO_ERROR;
+}
+
+GrepError::Code
+RepState::clear(Channel::Position s, Uint32 nodeGrp, const Interval i)
+{
+ m_channel.clear(s, nodeGrp, i);
+ return GrepError::NO_ERROR;
+}
+
+/****************************************************************************
+ * Execute
+ *
+ * This method should only be called from Requestor!
+ ****************************************************************************/
+
+GrepError::Code
+RepState::protectedExecute()
+{
+ GrepError::Code err;
+
+ NdbMutex_Lock(m_mutex);
+
+ NdbApiSignal* signal = m_extSender->getSignal();
+ if (signal == NULL) {
+ err = GrepError::COULD_NOT_ALLOCATE_MEM_FOR_SIGNAL;
+ } else {
+ err = execute(signal);
+ }
+ NdbMutex_Unlock(m_mutex);
+ return err;
+}
+
+GrepError::Code
+RepState::execute(NdbApiSignal* signal)
+{
+ Uint32 subId = m_channel.getSubId();
+ Uint32 subKey = m_channel.getSubKey();
+
+ if (!m_channel.m_requestorEnabled)
+ return GrepError::NO_ERROR;
+
+ /**
+ * @todo Should have subscriptions in here
+ */
+ requestEpochInfo(signal);
+
+ /**
+ * Update connected counter (Silence time)
+ */
+ m_connected_counter++;
+ if (m_connected_counter > REQUESTOR_EXECUTES_NEEDED_FOR_UNKNOWN_CONNECTION) {
+ m_connected = UNKNOWN;
+ }
+
+ switch (m_channel.getState())
+ {
+ case Channel::CONSISTENT:
+ if (isAutoStartEnabled()) {
+ switch (m_channel.getStateSub())
+ {
+ case Channel::NO_SUBSCRIPTION_EXISTS:
+ m_funcRequestCreateSubscriptionId(m_extSender, signal);
+ m_channel.setStateSub(Channel::CREATING_SUBSCRIPTION_ID);
+ break;
+
+ case Channel::CREATING_SUBSCRIPTION_ID:
+ break;
+
+ case Channel::SUBSCRIPTION_ID_CREATED:
+ if(m_channel.isSelective())
+ m_funcRequestCreateSubscription(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey(),
+ m_channel.getSelectedTables());
+ else
+ m_funcRequestCreateSubscription(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey(),
+ 0);
+ m_channel.setStateSub(Channel::STARTING_SUBSCRIPTION);
+ break;
+
+ case Channel::STARTING_SUBSCRIPTION:
+ break;
+
+ case Channel::SUBSCRIPTION_STARTED:
+ m_funcRequestStartMetaLog(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey());
+ m_channel.setState(Channel::METALOG_STARTING);
+ break;
+ }
+ }
+ break;
+
+ case Channel::METALOG_STARTING:
+ break;
+
+ case Channel::METALOG_STARTED:
+ if (isAutoStartEnabled()) {
+ m_funcRequestStartMetaScan(m_extSender, signal, subId, subKey);
+ m_channel.setState(Channel::METASCAN_STARTING);
+ }
+ break;
+
+ case Channel::METASCAN_STARTING:
+ break;
+
+ case Channel::METASCAN_COMPLETED:
+ if (isAutoStartEnabled()) {
+ m_funcRequestStartDataLog(m_extSender, signal, subId, subKey);
+ m_channel.setState(Channel::DATALOG_STARTING);
+ }
+ break;
+
+ case Channel::DATALOG_STARTING:
+ break;
+
+ case Channel::DATALOG_STARTED:
+ if (isAutoStartEnabled()) {
+ m_funcRequestStartDataScan(m_extSender, signal, subId, subKey);
+ m_channel.setState(Channel::DATASCAN_STARTING);
+ }
+ break;
+
+ case Channel::DATASCAN_STARTING:
+ break;
+
+ case Channel::DATASCAN_COMPLETED:
+ break;
+
+ case Channel::LOG:
+ if (m_channel.shouldStop()) {
+ disableTransfer();
+ m_channel.setState(Channel::STOPPING);
+ }
+ break;
+
+ case Channel::STOPPING:
+ if (m_channel.m_transferEnabled)
+ {
+ REPABORT("Illegal stopping state while transfer is still enabled");
+ }
+ /**
+ * check if channel has a subscription, if not,
+ * check if we have marked a subscription that we want to remove
+ * and remove it. This is used to clean up "dangling subscriptions"
+ * after various crashes
+ */
+ if(!m_channel.subscriptionExists())
+ {
+ if(m_subIdToRemove && m_subKeyToRemove)
+ {
+ m_funcRequestRemoveSubscription(m_extSender, signal,
+ m_subIdToRemove,
+ m_subKeyToRemove);
+ eventSubscriptionDeleted( m_subIdToRemove,
+ m_subKeyToRemove);
+ return GrepError::NO_ERROR;
+ }
+ else {
+ return GrepError::SUBSCRIPTION_ID_NOT_FOUND;
+ }
+ } else {
+ if (m_channel.isStoppable())
+ {
+
+ m_funcRequestRemoveSubscription(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey());
+ eventSubscriptionDeleted(m_channel.getSubId(),
+ m_channel.getSubKey());
+ }
+ else
+ return GrepError::CHANNEL_NOT_STOPPABLE;
+
+ }
+ break;
+
+ default:
+ REPABORT("Illegal replication state");
+ }
+ if (m_channel.m_transferEnabled) requestTransfer(signal);
+ if (m_channel.m_applyEnabled) requestApply(signal);
+ if (m_channel.m_deleteEnabled) requestDelete(signal);
+ return GrepError::NO_ERROR;
+}
+
+/****************************************************************************
+ * Request
+ *
+ * This method should only be called from Main Thread!
+ ****************************************************************************/
+
+GrepError::Code
+RepState::protectedRequest(GrepReq::Request req, Uint32 arg)
+{
+ return protectedRequest(req, arg, 0);
+}
+
+GrepError::Code
+RepState::protectedRequest(GrepReq::Request req, Uint32 arg1, Uint32 arg2)
+{
+ GrepError::Code code;
+ NdbMutex_Lock(m_mutex);
+
+ NdbApiSignal* signal = m_extSender->getSignal();
+ if (signal == NULL) {
+ code = GrepError::COULD_NOT_ALLOCATE_MEM_FOR_SIGNAL;
+ } else {
+ code = request(req, arg1, arg2, signal);
+ }
+
+ NdbMutex_Unlock(m_mutex);
+ return code;
+}
+
+GrepError::Code
+RepState::protectedAddTable(const char * fullTableName)
+{
+ GrepError::Code code;
+ NdbMutex_Lock(m_mutex);
+ code = m_channel.addTable(fullTableName);
+ NdbMutex_Unlock(m_mutex);
+ return code;
+}
+
+GrepError::Code
+RepState::protectedRemoveTable(const char * fullTableName)
+{
+ GrepError::Code code;
+ if(m_channel.getStateSub() != Channel::NO_SUBSCRIPTION_EXISTS)
+ return GrepError::START_ALREADY_IN_PROGRESS;
+ NdbMutex_Lock(m_mutex);
+ code = m_channel.removeTable(fullTableName);
+ NdbMutex_Unlock(m_mutex);
+ return code;
+}
+
+GrepError::Code
+RepState::request(GrepReq::Request request, Uint32 arg1, Uint32 arg2,
+ NdbApiSignal* signal)
+{
+ switch (request)
+ {
+ /*************************************************************************
+ * STATUS etc
+ *************************************************************************/
+
+ case GrepReq::STATUS:
+ printStatus();
+ break;
+
+ case GrepReq::REMOVE_BUFFERS:
+ return GrepError::NOT_YET_IMPLEMENTED;
+
+ /*************************************************************************
+ * START
+ *************************************************************************/
+
+ case GrepReq::CREATE_SUBSCR:
+ if (m_channel.getStateSub() != Channel::NO_SUBSCRIPTION_EXISTS)
+ return GrepError::SUBSCRIPTION_ID_ALREADY_EXIST;
+
+ m_funcRequestCreateSubscriptionId(m_extSender, signal);
+ m_channel.setStateSub(Channel::CREATING_SUBSCRIPTION_ID);
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_SUBSCR:
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ if (m_channel.getStateSub() != Channel::SUBSCRIPTION_ID_CREATED)
+ return GrepError::SUBSCRIPTION_ID_NOT_FOUND;
+ if(m_channel.isSelective())
+ m_funcRequestCreateSubscription(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey(),
+ m_channel.getSelectedTables());
+ else
+ m_funcRequestCreateSubscription(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey(),
+ 0);
+ m_channel.setStateSub(Channel::STARTING_SUBSCRIPTION);
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_METALOG:
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ if (m_channel.getStateSub() != Channel::SUBSCRIPTION_STARTED)
+ return GrepError::SUBSCRIPTION_NOT_STARTED;
+ if (m_channel.getState() != Channel::CONSISTENT)
+ return GrepError::START_OF_COMPONENT_IN_WRONG_STATE;
+
+ m_funcRequestStartMetaLog(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey());
+ m_channel.setState(Channel::METALOG_STARTING);
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_METASCAN:
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ if (m_channel.getStateSub() != Channel::SUBSCRIPTION_STARTED)
+ return GrepError::SUBSCRIPTION_NOT_STARTED;
+ if (m_channel.getState() != Channel::METALOG_STARTED)
+ return GrepError::START_OF_COMPONENT_IN_WRONG_STATE;
+
+ m_funcRequestStartMetaScan(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey());
+ m_channel.setState(Channel::METASCAN_STARTING);
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_DATALOG:
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ if (m_channel.getStateSub() != Channel::SUBSCRIPTION_STARTED)
+ return GrepError::SUBSCRIPTION_NOT_STARTED;
+ if (m_channel.getState() != Channel::METASCAN_COMPLETED)
+ return GrepError::START_OF_COMPONENT_IN_WRONG_STATE;
+
+ m_funcRequestStartDataLog(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey());
+ m_channel.setState(Channel::DATALOG_STARTING);
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_DATASCAN:
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ if (m_channel.getStateSub() != Channel::SUBSCRIPTION_STARTED)
+ return GrepError::SUBSCRIPTION_NOT_STARTED;
+ if (m_channel.getState() != Channel::DATALOG_STARTED)
+ return GrepError::START_OF_COMPONENT_IN_WRONG_STATE;
+
+ m_funcRequestStartDataScan(m_extSender, signal,
+ m_channel.getSubId(),
+ m_channel.getSubKey());
+ m_channel.setState(Channel::DATASCAN_STARTING);
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_REQUESTOR:
+ enable();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_TRANSFER:
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ enableTransfer();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_APPLY:
+ enableApply();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START_DELETE:
+ enableDelete();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::START:
+ if (isAutoStartEnabled())
+ return GrepError::START_ALREADY_IN_PROGRESS;
+
+ enableAutoStart();
+ return GrepError::NO_ERROR;
+
+ /*************************************************************************
+ * STOP
+ *************************************************************************/
+
+ case GrepReq::STOP:
+ if (m_channel.getStateSub() == Channel::NO_SUBSCRIPTION_EXISTS)
+ return GrepError::SUBSCRIPTION_NOT_STARTED;
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+
+ if (arg1 == 0) {
+ /**
+ * Stop immediately
+ */
+ disableTransfer();
+ m_channel.setState(Channel::STOPPING);
+ m_channel.setStopEpochId(0);
+ return GrepError::NO_ERROR;
+ } else {
+ /**
+ * Set future stop epoch
+ */
+ return m_channel.setStopEpochId(arg1);
+ }
+
+ case GrepReq::STOP_SUBSCR:
+ {
+ if(m_subIdToRemove == 0 && m_subKeyToRemove == 0) {
+ m_subIdToRemove = arg1;
+ m_subKeyToRemove = arg2;
+ } else {
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ }
+
+ if(m_channel.getSubId() != 0 && m_channel.getSubKey() != 0)
+ return GrepError::ILLEGAL_USE_OF_COMMAND;
+ if (m_channel.getState() == Channel::STOPPING)
+ return GrepError::ILLEGAL_ACTION_WHEN_STOPPING;
+ disableTransfer();
+ m_channel.setState(Channel::STOPPING);
+ return GrepError::NO_ERROR;
+ }
+ case GrepReq::STOP_METALOG:
+ case GrepReq::STOP_METASCAN:
+ case GrepReq::STOP_DATALOG:
+ case GrepReq::STOP_DATASCAN:
+ return GrepError::NOT_YET_IMPLEMENTED;
+
+ case GrepReq::STOP_REQUESTOR:
+ disable();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::STOP_TRANSFER:
+ disableTransfer();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::STOP_APPLY:
+ disableApply();
+ return GrepError::NO_ERROR;
+
+ case GrepReq::STOP_DELETE:
+ disableDelete();
+ return GrepError::NO_ERROR;
+
+ default:
+ ndbout_c("RepCommandInterpreter: Illegal request received");
+ return GrepError::NOT_YET_IMPLEMENTED;
+ }
+ return GrepError::NOT_YET_IMPLEMENTED;
+}
+
+/****************************************************************************
+ *
+ ****************************************************************************/
+
+/*
+GrepError::Code
+RepState::slowStop()
+{
+ switch(m_channel.getState())
+ {
+ case Channel::LOG:
+ m_channel.setState(Channel::LOG_SLOW_STOP);
+ return GrepError::NO_ERROR;
+ default:
+ return GrepError::REQUESTOR_ILLEGAL_STATE_FOR_SLOWSTOP;
+ }
+}
+
+GrepError::Code
+RepState::fastStop()
+{
+ switch(m_channel.getState())
+ {
+ case Channel::LOG:
+ m_channel.setState(Channel::LOG_FAST_STOP);
+ return GrepError::NO_ERROR;
+ default:
+ return GrepError::REQUESTOR_ILLEGAL_STATE_FOR_FASTSTOP;
+ }
+}
+*/
+
+/****************************************************************************
+ * Print Status
+ ****************************************************************************/
+
+static const char*
+headerText =
+"+-------------------------------------------------------------------------+\n"
+"| MySQL Replication Server |\n"
+"+-------------------------------------------------------------------------+\n"
+;
+
+static const char*
+channelHeaderText =
+"+-------------------------------------------------------------------------+\n"
+"| Applier Channel 1 Replication Status |\n"
+"+-------------------------------------------------------------------------+\n"
+;
+
+static const char*
+line =
+"+-------------------------------------------------------------------------+\n"
+;
+
+
+Properties *
+RepState::getStatus()
+{
+ Properties * prop = new Properties();
+ if(prop == NULL)
+ return NULL;
+ NdbMutex_Lock(m_mutex);
+
+ prop->put("nodegroups", (int)m_channel.getNoOfNodeGroups());
+// prop->put("epoch_state", m_channel.getEpochState());
+ NdbMutex_Unlock(m_mutex);
+ return prop;
+}
+
+
+Properties * RepState::query(QueryCounter counter, Uint32 replicationId)
+{
+ Properties * prop = new Properties();
+ if(prop == NULL)
+ return NULL;
+ NdbMutex_Lock(m_mutex);
+ if(counter != ~(Uint32)0)
+ getEpochState((Channel::Position)counter, prop );
+ prop->put("no_of_nodegroups", m_channel.getNoOfNodeGroups());
+ prop->put("subid", m_channel.getNoOfNodeGroups());
+ prop->put("subkey", m_channel.getSubKey());
+ prop->put("connected_db", m_connected);
+ prop->put("connected_rep", m_repConnected);
+ prop->put("state_sub", (int)m_channel.getStateSub());
+ prop->put("state", (int)m_channel.getState());
+
+ NdbMutex_Unlock(m_mutex);
+ return prop;
+
+}
+
+void
+RepState::getEpochState(Channel::Position pos, Properties * p)
+{
+ char first_buf[20];
+ char last_buf[20];
+ int pos_first = 0, pos_last = 0;
+ Uint32 first = 0, last = 0;
+ for(Uint32 i = 0; i < m_channel.getNoOfNodeGroups() ; i++)
+ {
+ m_channel.getEpochState(pos, i, &first, &last);
+ pos_first += sprintf(first_buf+pos_first,"%d%s",first,",");
+ pos_last += sprintf(last_buf+pos_last,"%d%s",last,",");
+ }
+/**
+ * remove trailing comma
+ */
+ pos_first--;
+ pos_last--;
+ first_buf[pos_first]= '\0';
+ last_buf[pos_last]= '\0';
+#if 0
+ sprintf(first_buf+pos_first,"","");
+ sprintf(last_buf + pos_last,"","");
+#endif
+
+ p->put("first", first_buf);
+ p->put("last", last_buf);
+
+}
+
+
+void
+RepState::printStatus()
+{
+ /***************************************************************************
+ * Global Status
+ ***************************************************************************/
+ ndbout << headerText;
+ switch (m_connected)
+ {
+ case CONNECTED:
+ ndbout << "| Source: Connected "; break;
+ case DISCONNECTED:
+ ndbout << "| Source: Disconnected "; break;
+ case CONNECTABLE:
+ ndbout << "| Source: Disconnected "; break;
+ default:
+ ndbout << "| Source: Unknown "; break;
+ }
+ switch (m_repConnected)
+ {
+ case CONNECTED:
+ ndbout << "(Rep: Connected) "; break;
+ case DISCONNECTED:
+ ndbout << "(Rep: Disconnected) "; break;
+ case CONNECTABLE:
+ ndbout << "(Rep: Disconnected) "; break;
+ default:
+ ndbout << "(Rep: Unknown) "; break;
+ }
+ ndbout << " |" << endl;
+ ndbout << "| Autostart: " << (isAutoStartEnabled() ? "On " : "Off")
+ << " ";
+ ndbout_c(" Silence time: %10u |", m_connected_counter);
+
+ /***************************************************************************
+ * Channel Status
+ ***************************************************************************/
+ ndbout << channelHeaderText;
+ switch(m_channel.getStateSub()) {
+ case Channel::NO_SUBSCRIPTION_EXISTS:
+ ndbout_c("| Subscription: Non-existing "
+ " |");
+ break;
+ case Channel::CREATING_SUBSCRIPTION_ID:
+ ndbout_c("| Subscription: Non-existing (Id is being created)"
+ " |");
+ break;
+ case Channel::SUBSCRIPTION_ID_CREATED:
+ ndbout_c("| Subscription: %-3d-%-6d in state: Not yet started "
+ " |",
+ m_channel.getSubId(), m_channel.getSubKey());
+ break;
+ case Channel::STARTING_SUBSCRIPTION:
+ ndbout_c("| Subscription: %-3d-%-6d in state: Being started "
+ " |",
+ m_channel.getSubId(), m_channel.getSubKey());
+ break;
+ case Channel::SUBSCRIPTION_STARTED:
+ ndbout_c("| Subscription: %-3d-%-6d in state: Started "
+ " |",
+ m_channel.getSubId(), m_channel.getSubKey());
+ break;
+ default:
+ REPABORT("Illegal subscription state");
+ }
+ ndbout << "| Stop epoch: ";
+ if (m_channel.getStopEpochId() == intervalMax) {
+ ndbout << "No stop defined ";
+ } else {
+ ndbout.print("%-10d ",
+ m_channel.getStopEpochId());
+ }
+ ndbout << " |" << endl;
+
+ ndbout << "| State: ";
+ switch(m_channel.getState())
+ {
+ case Channel::CONSISTENT:
+ ndbout << "Local database is subscription consistent ";
+ break;
+ case Channel::METALOG_STARTING:
+ ndbout << "Starting (Phase 1: Metalog starting) ";
+ break;
+ case Channel::METALOG_STARTED:
+ ndbout << "Starting (Phase 2: Metalog started) ";
+ break;
+ case Channel::METASCAN_STARTING:
+ ndbout << "Starting (Phase 3: Metascan starting) ";
+ break;
+ case Channel::METASCAN_COMPLETED:
+ ndbout << "Starting (Phase 4: Metascan completed) ";
+ break;
+ case Channel::DATALOG_STARTING:
+ ndbout << "Starting (Phase 5: Datalog starting) ";
+ break;
+ case Channel::DATALOG_STARTED:
+ ndbout << "Starting (Phase 6: Datalog started) ";
+ break;
+ case Channel::DATASCAN_STARTING:
+ ndbout << "Starting (Phase 7: Datascan completed) ";
+ break;
+ case Channel::DATASCAN_COMPLETED:
+ ndbout << "Starting (Phase 8: Datascan completed) ";
+ break;
+ case Channel::LOG:
+ ndbout << "Logging ";
+ break;
+ case Channel::STOPPING:
+ ndbout << "Stopping (Stopped when all epochs applied) ";
+ break;
+ }
+ ndbout << " |" << endl;
+
+/* @todo
+ ndbout_c("| Syncable: Yes/Scan/No/Unknown (Not implemented)"
+ " |");
+*/
+ ndbout << "| Requestor: " << (isEnabled() ? "On " : "Off")
+ << " (Transfer: " << (isTransferEnabled() ? "On, " : "Off, ")
+ << "Apply: " << (isApplyEnabled() ? "On, " : "Off, ")
+ << "Delete: " << (isDeleteEnabled() ? "On) " : "Off)")
+ << " |" << endl;
+ ndbout_c("| Tables being replicated using this channel: "
+ " |");
+ m_channel.printTables();
+
+ /**
+ * Print node groups
+ */
+ if (getNoOfNodeGroups() == 0)
+ {
+ ndbout_c("| No node groups are known. "
+ " |");
+ }
+ else
+ {
+ m_channel.print();
+ }
+ ndbout << line;
+}
diff --git a/storage/ndb/src/old_files/rep/state/RepState.hpp b/storage/ndb/src/old_files/rep/state/RepState.hpp
new file mode 100644
index 00000000000..06bbca19f7e
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/RepState.hpp
@@ -0,0 +1,276 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 REP_STATE_HPP
+#define REP_STATE_HPP
+
+#include <GrepError.hpp>
+#include <signaldata/GrepImpl.hpp>
+#include <rep/repapi/repapi.h>
+#include <rep/ExtSender.hpp>
+#include <rep/adapters/AppNDB.hpp>
+#include <Properties.hpp>
+
+#include "Channel.hpp"
+#include "Interval.hpp"
+
+#define REQUESTOR_EXECUTES_NEEDED_FOR_UNKNOWN_CONNECTION 5
+
+class NdbApiSignal;
+
+
+/**
+ * @class RepState
+ * @brief The main information about the replication
+ */
+class RepState
+{
+public:
+ RepState();
+ ~RepState();
+ void init(ExtSender * extSender) { m_extSender = extSender; }
+
+ /***************************************************************************
+ * Callback functions
+ *
+ * These are used when RepState wants to do something
+ ***************************************************************************/
+
+ typedef void (FuncRequestCreateSubscriptionId)
+ (void * cbObj, NdbApiSignal* signal);
+
+ typedef void (FuncRequestCreateSubscription)
+ (void * cbObj, NdbApiSignal* signal, Uint32 subId,
+ Uint32 subKey ,
+ Vector <struct table *> * selectedTables);
+
+ typedef void (FuncRequestRemoveSubscription)
+ (void * cbObj, NdbApiSignal* signal, Uint32 subId, Uint32 subKey);
+
+ typedef void (FuncRequestTransfer)
+ (void * cbObj, NdbApiSignal* signal,
+ Uint32 nodeGrp, Uint32 first, Uint32 last);
+
+ typedef void (FuncRequestApply)
+ (void * cbObj, NdbApiSignal* signal,
+ Uint32 nodeGrp, Uint32 first, Uint32 last, Uint32 force);
+
+ typedef void (FuncRequestDeleteSS)
+ (void * cbObj, NdbApiSignal* signal,
+ Uint32 nodeGrp, Uint32 first, Uint32 last);
+
+ typedef void (FuncRequestDeletePS)
+ (void * cbObj, NdbApiSignal* signal,
+ Uint32 nodeGrp, Uint32 first, Uint32 last);
+
+ typedef void (FuncRequestStartMetaLog)
+ (void * cbObj, NdbApiSignal * signal, Uint32 subId, Uint32 subKey);
+
+ typedef void (FuncRequestStartDataLog)
+ (void * cbObj, NdbApiSignal * signal, Uint32 subId, Uint32 subKey);
+
+ typedef void (FuncRequestStartMetaScan)
+ (void * cbObj, NdbApiSignal * signal, Uint32 subId, Uint32 subKey);
+
+ typedef void (FuncRequestStartDataScan)
+ (void * cbObj, NdbApiSignal * signal, Uint32 subId, Uint32 subKey);
+
+ typedef void (FuncRequestEpochInfo)
+ (void * cbObj, NdbApiSignal * signal, Uint32 nodeGrp);
+
+ /***************************************************************************
+ *
+ ***************************************************************************/
+ void setSubscriptionRequests(FuncRequestCreateSubscriptionId f1,
+ FuncRequestCreateSubscription f2,
+ FuncRequestRemoveSubscription f3);
+ void setIntervalRequests(FuncRequestTransfer * f1,
+ FuncRequestApply * f2,
+ FuncRequestDeleteSS * f3,
+ FuncRequestDeletePS * f4);
+ void setStartRequests(FuncRequestStartMetaLog * f5,
+ FuncRequestStartDataLog * f6,
+ FuncRequestStartMetaScan * f7,
+ FuncRequestStartDataScan * f8,
+ FuncRequestEpochInfo * f9);
+
+ /***************************************************************************
+ * Enablings
+ ***************************************************************************/
+ bool isEnabled() { return m_channel.m_requestorEnabled; }
+ bool isTransferEnabled() { return m_channel.m_transferEnabled; }
+ bool isApplyEnabled() { return m_channel.m_applyEnabled; }
+ bool isDeleteEnabled() { return m_channel.m_deleteEnabled; }
+ bool isAutoStartEnabled() { return m_channel.m_autoStartEnabled; }
+
+ void enable() { m_channel.m_requestorEnabled = true; }
+ void enableTransfer() { m_channel.m_transferEnabled = true; }
+ void enableApply() { m_channel.m_applyEnabled = true; }
+ void enableDelete() { m_channel.m_deleteEnabled = true; }
+ void enableAutoStart() { m_channel.m_autoStartEnabled = true; }
+
+ void disable() { m_channel.m_requestorEnabled = false; }
+ void disableTransfer() { m_channel.m_transferEnabled = false; }
+ void disableApply() { m_channel.m_applyEnabled = false;}
+ void disableDelete() { m_channel.m_deleteEnabled = false; }
+ void disableAutoStart() { m_channel.m_autoStartEnabled = false; }
+
+ /***************************************************************************
+ * Node groups
+ ***************************************************************************/
+ void setNoOfNodeGroups(Uint32 n) { m_channel.setNoOfNodeGroups(n); }
+ Uint32 getNoOfNodeGroups() { return m_channel.getNoOfNodeGroups(); }
+
+ /***************************************************************************
+ * Event reporting to RepState
+ *
+ * These are used to update the state of the Requestor when something
+ * has happend.
+ ***************************************************************************/
+ void request(GrepReq::Request request);
+
+ //GrepError::Code createSubscription(Uint32 subId, Uint32 subKey);
+ GrepError::Code protectedExecute();
+ GrepError::Code protectedRequest(GrepReq::Request request, Uint32 arg);
+ GrepError::Code protectedRequest(GrepReq::Request request,
+ Uint32 arg1,
+ Uint32 arg2);
+ GrepError::Code protectedAddTable(const char * fullTableName);
+ GrepError::Code protectedRemoveTable(const char * fullTableName);
+ GrepError::Code add(Channel::Position s, Uint32 nodeGrp, const Interval i);
+ GrepError::Code clear(Channel::Position s, Uint32 nodeGrp, const Interval i);
+
+ void eventSubscriptionDeleted(Uint32 subId, Uint32 subKey);
+
+ void eventMetaLogStarted(NdbApiSignal*, Uint32 subId, Uint32 subKey);
+ void eventDataLogStarted(NdbApiSignal*, Uint32 subId, Uint32 subKey);
+ void eventMetaScanCompleted(NdbApiSignal*, Uint32 subId, Uint32 subKey,
+ Interval epochs);
+ void eventDataScanCompleted(NdbApiSignal*, Uint32 subId, Uint32 subKey,
+ Interval epochs);
+ void eventMetaScanFailed(Uint32 subId, Uint32 subKey, GrepError::Code error);
+ void eventDataScanFailed(Uint32 subId, Uint32 subKey, GrepError::Code error);
+
+ /**
+ * @fn sendInsertConf
+ * @param gci - the gci of the applied GCIBuffer.
+ * @param nodeGrp - the nodeGrp of the applied GCIBuffer.
+ */
+ void eventInsertConf(Uint32 gci, Uint32 nodeGrp);
+
+ /**
+ * @fn sendInsertRef
+ * @param gci - the gci of the applied GCIBuffer.
+ * @param nodeGrp - the nodeGrp of the applied GCIBuffer.
+ * @param tableId - the table of the applied GCIBuffer.
+ */
+ void eventInsertRef(Uint32 gci, Uint32 nodeGrp, Uint32 tableId,
+ GrepError::Code err);
+ void eventCreateTableRef(Uint32 gci,
+ Uint32 tableId,
+ const char * tableName,
+ GrepError::Code err) ;
+
+ void eventSubscriptionIdCreated(Uint32 subId, Uint32 subKey);
+ void eventSubscriptionIdCreateFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error);
+
+ void eventSubscriptionCreated(Uint32 subId, Uint32 subKey);
+ void eventSubscriptionCreateFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error);
+
+ void eventMetaLogStartFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error);
+ void eventDataLogStartFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error);
+
+ void eventNodeConnected(Uint32 nodeId);
+ void eventNodeDisconnected(Uint32 nodeId);
+ void eventNodeConnectable(Uint32 nodeId);
+
+ void printStatus();
+ Properties * getStatus();
+ Properties * query(QueryCounter counter, Uint32 replicationId);
+ Uint32 getSubId() { return m_channel.getSubId(); }
+ Uint32 getSubKey () { return m_channel.getSubKey(); }
+
+ void setApplier(class AppNDB * app) { m_applier = app; }
+ void setGCIContainer(class GCIContainer * c) { m_gciContainer = c; }
+
+ /* @todo should be private */
+ Channel m_channel;
+
+private:
+ /***************************************************************************
+ * PRIVATE ATTRIBUTES
+ ***************************************************************************/
+ ExtSender * m_extSender;
+ AppNDB * m_applier;
+ GCIContainer * m_gciContainer;
+
+ Uint32 m_subIdToRemove;
+ Uint32 m_subKeyToRemove;
+
+
+ enum Connected
+ {
+ UNKNOWN, ///<
+ CONNECTED, ///< Recently received info from (all needed) PS REP
+ DISCONNECTED, ///< Received disconnect info from (some needed) PS REP
+ CONNECTABLE ///< Received disconnect info from (some needed) PS REP
+ };
+ Connected m_connected;
+ Connected m_repConnected;
+ Uint32 m_connected_counter;
+
+ NdbMutex * m_mutex;
+
+ /** @todo Should be channel-specific */
+ Uint32 m_stopEpoch;
+
+ /***************************************************************************
+ * PRIVATE METHODS
+ ***************************************************************************/
+ GrepError::Code execute(NdbApiSignal*);
+ GrepError::Code request(GrepReq::Request req,
+ Uint32 arg1,
+ Uint32 arg2,
+ NdbApiSignal*);
+
+ FuncRequestCreateSubscriptionId * m_funcRequestCreateSubscriptionId;
+ FuncRequestCreateSubscription * m_funcRequestCreateSubscription;
+ FuncRequestRemoveSubscription * m_funcRequestRemoveSubscription;
+
+ FuncRequestTransfer * m_funcRequestTransfer;
+ FuncRequestApply * m_funcRequestApply;
+ FuncRequestDeleteSS * m_funcRequestDeleteSS;
+ FuncRequestDeletePS * m_funcRequestDeletePS;
+
+ FuncRequestStartMetaLog * m_funcRequestStartMetaLog;
+ FuncRequestStartDataLog * m_funcRequestStartDataLog;
+ FuncRequestStartMetaScan * m_funcRequestStartMetaScan;
+ FuncRequestStartDataScan * m_funcRequestStartDataScan;
+ FuncRequestEpochInfo * m_funcRequestEpochInfo;
+
+ void requestTransfer(NdbApiSignal * signal);
+ void requestApply(NdbApiSignal * signal);
+ void requestDelete(NdbApiSignal * signal);
+ void requestEpochInfo(NdbApiSignal * signal);
+ void getEpochState(Channel::Position pos, Properties * p);
+ friend void testRepState();
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/state/RepStateEvent.cpp b/storage/ndb/src/old_files/rep/state/RepStateEvent.cpp
new file mode 100644
index 00000000000..9be304c8bfa
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/RepStateEvent.cpp
@@ -0,0 +1,284 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "RepState.hpp"
+
+/****************************************************************************
+ * Public Event Handlers : CREATE SUBSCRIPTION ID
+ ****************************************************************************/
+
+void
+RepState::eventSubscriptionIdCreated(Uint32 subId, Uint32 subKey)
+{
+ if (m_channel.getStateSub() == Channel::CREATING_SUBSCRIPTION_ID)
+ {
+ m_channel.setSubId(subId);
+ m_channel.setSubKey(subKey);
+ m_channel.setStateSub(Channel::SUBSCRIPTION_ID_CREATED);
+ }
+ else
+ {
+ REPABORT("Illegal state for create subscription id conf");
+ }
+}
+
+void
+RepState::eventSubscriptionIdCreateFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error)
+{
+ ndbout_c("\nSubscription id creation failed");
+ ndbout_c("Error %d: %s", error, GrepError::getErrorDesc(error));
+ ndbout_c("Subscription Id: %d, Key: %d", subId, subKey);
+}
+
+/****************************************************************************
+ * Public Event Handlers : CREATE SUBSCRIPTION
+ ****************************************************************************/
+
+void
+RepState::eventSubscriptionCreated(Uint32 subId, Uint32 subKey)
+{
+ if (m_channel.getStateSub() == Channel::STARTING_SUBSCRIPTION)
+ {
+ m_channel.setStateSub(Channel::SUBSCRIPTION_STARTED);
+ }
+ else
+ {
+ REPABORT("Illegal state for create subscription conf");
+ }
+}
+
+void
+RepState::eventSubscriptionCreateFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error)
+{
+ ndbout_c("\nSubscription creation failed");
+ ndbout_c("Error %d: %s", error, GrepError::getErrorDesc(error));
+ ndbout_c("Subscription Id: %d, Key: %d", subId, subKey);
+}
+
+
+/****************************************************************************
+ * Public Event Handlers : META LOG
+ ****************************************************************************/
+
+void
+RepState::eventMetaLogStarted(NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey)
+{
+ if (m_channel.getState() != Channel::METALOG_STARTING)
+ {
+ RLOG(("WARNING! Metalog started in state %d, should be %d",
+ m_channel.getState(), Channel::METALOG_STARTING));
+ }
+
+ if (!isAutoStartEnabled())
+ {
+ m_channel.setState(Channel::METALOG_STARTED);
+ }
+ else
+ {
+ m_channel.setState(Channel::METALOG_STARTED);
+ m_channel.setState(Channel::METASCAN_STARTING);
+ m_funcRequestStartMetaScan(m_extSender, signal, subId, subKey);
+ }
+}
+
+void
+RepState::eventMetaLogStartFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error)
+{
+ ndbout_c("\nMetalog start failed");
+ ndbout_c("Error %d: %s", error, GrepError::getErrorDesc(error));
+ ndbout_c("Subscription Id: %d, Key: %d", subId, subKey);
+}
+
+/****************************************************************************
+ * Public Event Handlers : META SCAN
+ ****************************************************************************/
+
+void
+RepState::eventMetaScanCompleted(NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey, Interval epochs)
+{
+ if (m_channel.getState() != Channel::METASCAN_STARTING)
+ {
+ RLOG(("WARNING! Metascan completed in state %d, should be %d",
+ m_channel.getState(), Channel::METASCAN_STARTING));
+ }
+ RLOG(("Metascan completed. Subscription %d-%d, Epochs [%d-%d]",
+ subId, subKey, epochs.first(), epochs.last()));
+
+ m_channel.setState(Channel::METASCAN_COMPLETED);
+
+ if (isAutoStartEnabled())
+ {
+ m_channel.setState(Channel::DATALOG_STARTING);
+ m_funcRequestStartDataLog(m_extSender, signal, subId, subKey);
+ }
+ m_channel.setMetaScanEpochs(epochs);
+}
+
+/****************************************************************************
+ * Public Event Handlers : DATA LOG
+ ****************************************************************************/
+
+void
+RepState::eventDataLogStarted(NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey)
+{
+ if (m_channel.getState() != Channel::DATALOG_STARTING)
+ {
+ RLOG(("WARNING! Datalog started in state %d, should be %d",
+ m_channel.getState(), Channel::DATALOG_STARTING));
+ }
+
+ m_channel.setState(Channel::DATALOG_STARTED);
+
+ if (isAutoStartEnabled())
+ {
+ m_channel.setState(Channel::DATASCAN_STARTING);
+ m_funcRequestStartDataScan(m_extSender, signal, subId, subKey);
+ }
+}
+
+void
+RepState::eventDataLogStartFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error)
+{
+ ndbout_c("\nDatalog start failed");
+ ndbout_c("Error %d: %s", error, GrepError::getErrorDesc(error));
+ ndbout_c("Subscription Id: %d, Key: %d", subId, subKey);
+}
+
+/****************************************************************************
+ * Public Event Handlers : DATA SCAN
+ ****************************************************************************/
+
+void
+RepState::eventDataScanCompleted(NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey,
+ Interval epochs)
+{
+ if (m_channel.getState() != Channel::DATASCAN_STARTING)
+ {
+ RLOG(("WARNING! Datascan completed in state %d, should be %d",
+ m_channel.getState(), Channel::DATASCAN_STARTING));
+ }
+ RLOG(("Datascan completed. Subscription %d-%d, Epochs [%d-%d]",
+ subId, subKey, epochs.first(), epochs.last()));
+
+ m_channel.setState(Channel::DATASCAN_COMPLETED);
+ m_channel.setDataScanEpochs(epochs);
+}
+
+/****************************************************************************
+ * Public Event Handlers : FAILURES
+ ****************************************************************************/
+
+void
+RepState::eventMetaScanFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error)
+{
+ ndbout_c("\nMetascan failed");
+ ndbout_c("Error %d: %s", error, GrepError::getErrorDesc(error));
+ ndbout_c("Subscription Id: %d, Key: %d", subId, subKey);
+}
+
+void
+RepState::eventDataScanFailed(Uint32 subId, Uint32 subKey,
+ GrepError::Code error)
+{
+ ndbout_c("\nDatascan failed");
+ ndbout_c("Error %d: %s", error, GrepError::getErrorDesc(error));
+ ndbout_c("Subscription Id: %d, Key: %d", subId, subKey);
+}
+
+/****************************************************************************
+ * Public Event Handlers : APPLY
+ ****************************************************************************/
+
+void
+RepState::eventInsertConf(Uint32 gci, Uint32 nodeGrp)
+{
+ Interval app(gci, gci);
+ add(Channel::App, nodeGrp, app);
+ clear(Channel::AppReq, nodeGrp, app);
+
+#ifdef DEBUG_GREP
+ ndbout_c("RepState: GCI Buffer %d:[%d] applied", nodeGrp, gci);
+#endif
+}
+
+void
+RepState::eventInsertRef(Uint32 gci, Uint32 nodeGrp, Uint32 tableId,
+ GrepError::Code err)
+{
+ ndbout_c("\nTable %d, used in replication, did not exist");
+ RLOG(("ERROR %d:%s. Apply failed (%d[%d] in table %d)",
+ err, GrepError::getErrorDesc(err), nodeGrp, gci, tableId));
+}
+
+
+void
+RepState::eventCreateTableRef(Uint32 gci,
+ Uint32 tableId,
+ const char * tableName,
+ GrepError::Code err)
+{
+ ndbout_c("\nFailed to create table %s with source site table id %d",
+ tableName,
+ tableId);
+
+ RLOG(("ERROR %d:%s. Failed to create table %s with source site table id %d!",
+ err, GrepError::getErrorDesc(err), tableName, tableId));
+}
+
+/****************************************************************************
+ * Public Event Handlers : Connected/Disconnected
+ ****************************************************************************/
+
+void
+RepState::eventNodeConnected(Uint32 nodeId)
+{
+ m_repConnected = CONNECTED;
+}
+
+void
+RepState::eventNodeDisconnected(Uint32 nodeId)
+{
+ m_repConnected = DISCONNECTED;
+}
+
+void
+RepState::eventNodeConnectable(Uint32 nodeId)
+{
+ m_repConnected = CONNECTABLE;
+}
+
+/****************************************************************************
+ * Public Event Handlers : Connected/Disconnected
+ ****************************************************************************/
+
+void
+RepState::eventSubscriptionDeleted(Uint32 subId, Uint32 subKey)
+{
+ m_gciContainer->reset();
+ m_channel.setState(Channel::CONSISTENT);
+ m_channel.reset();
+ m_subIdToRemove = 0;
+ m_subKeyToRemove = 0;
+}
diff --git a/storage/ndb/src/old_files/rep/state/RepStateRequests.cpp b/storage/ndb/src/old_files/rep/state/RepStateRequests.cpp
new file mode 100644
index 00000000000..02677e141f6
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/RepStateRequests.cpp
@@ -0,0 +1,294 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "RepState.hpp"
+
+#include <NdbApiSignal.hpp>
+#include <SimpleProperties.hpp>
+#include <UtilBuffer.hpp>
+
+#include <signaldata/GrepImpl.hpp>
+#include <signaldata/RepImpl.hpp>
+#include <signaldata/SumaImpl.hpp>
+
+#include <rep/rep_version.hpp>
+#include "Channel.hpp"
+
+/*****************************************************************************
+ * Helper functions
+ *****************************************************************************/
+
+void
+startSubscription(void * cbObj, NdbApiSignal* signal,
+ SubscriptionData::Part part,
+ Uint32 subId, Uint32 subKey)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ GrepSubStartReq * req = (GrepSubStartReq *)signal->getDataPtrSend();
+ req->subscriptionId = subId;
+ req->subscriptionKey = subKey;
+ req->part = (Uint32) part;
+ signal->set(0, PSREPBLOCKNO, GSN_GREP_SUB_START_REQ,
+ GrepSubStartReq::SignalLength);
+ ext->sendSignal(signal);
+}
+
+void
+scanSubscription(void * cbObj, NdbApiSignal* signal,
+ SubscriptionData::Part part,
+ Uint32 subId, Uint32 subKey)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ GrepSubSyncReq * req = (GrepSubSyncReq *)signal->getDataPtrSend();
+ req->subscriptionId = subId;
+ req->subscriptionKey = subKey;
+ req->part = part;
+ signal->set(0, PSREPBLOCKNO, GSN_GREP_SUB_SYNC_REQ,
+ GrepSubSyncReq::SignalLength);
+ ext->sendSignal(signal);
+}
+
+/*****************************************************************************
+ * RepState registered functions
+ *
+ * These registered functions are executed by RepState when
+ * RepState needs to have stuff done.
+ *****************************************************************************/
+
+void
+requestCreateSubscriptionId(void * cbObj, NdbApiSignal* signal)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ CreateSubscriptionIdReq * req =
+ (CreateSubscriptionIdReq *)signal->getDataPtrSend();
+ req->senderData = ext->getOwnRef();
+ signal->set(0, PSREPBLOCKNO, GSN_GREP_CREATE_SUBID_REQ,
+ CreateSubscriptionIdReq::SignalLength);
+ ext->sendSignal(signal);
+
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Sent request for creation of subscription id to PS");
+#endif
+}
+
+void
+requestCreateSubscription(void * cbObj,
+ NdbApiSignal* signal,
+ Uint32 subId,
+ Uint32 subKey,
+ Vector<struct table *> * selectedTables)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ GrepSubCreateReq * req = (GrepSubCreateReq *)signal->getDataPtrSend();
+ req->senderRef = ext->getOwnRef();
+ req->subscriptionId = subId;
+ req->subscriptionKey = subKey;
+ if(selectedTables!=0) {
+ UtilBuffer m_buffer;
+ UtilBufferWriter w(m_buffer);
+ LinearSectionPtr tablePtr[3];
+ req->subscriptionType = SubCreateReq::SelectiveTableSnapshot;
+
+ for(Uint32 i=0; i< selectedTables->size(); i++) {
+ w.add(SimpleProperties::StringValue, (*selectedTables)[i]->tableName);
+ }
+
+ tablePtr[0].p = (Uint32*)m_buffer.get_data();
+ tablePtr[0].sz = m_buffer.length() >> 2;
+
+ signal->set(0, PSREPBLOCKNO, GSN_GREP_SUB_CREATE_REQ,
+ GrepSubCreateReq::SignalLength);
+ ext->sendFragmentedSignal(signal, tablePtr, 1);
+ }
+ else {
+ req->subscriptionType = SubCreateReq::DatabaseSnapshot;
+ signal->set(0, PSREPBLOCKNO, GSN_GREP_SUB_CREATE_REQ,
+ GrepSubCreateReq::SignalLength);
+ ext->sendFragmentedSignal(signal, 0, 0);
+ }
+
+
+
+#ifdef DEBUG_GREP_SUBSCRIPTION
+ ndbout_c("Requestor: Sent request for creation of subscription");
+#endif
+}
+
+void
+requestRemoveSubscription(void * cbObj, NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ GrepSubRemoveReq * req = (GrepSubRemoveReq *)signal->getDataPtrSend();
+ req->subscriptionId = subId;
+ req->subscriptionKey = subKey;
+ signal->set(0, PSREPBLOCKNO, GSN_GREP_SUB_REMOVE_REQ,
+ GrepSubRemoveReq::SignalLength);
+ ext->sendSignal(signal);
+}
+
+void
+requestTransfer(void * cbObj, NdbApiSignal * signal,
+ Uint32 nodeGrp, Uint32 first, Uint32 last)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ RepGetGciBufferReq * req = (RepGetGciBufferReq*)signal->getDataPtrSend();
+ req->firstGCI = first;
+ req->lastGCI = last;
+ req->nodeGrp = nodeGrp;
+ req->senderRef = ext->getOwnRef();
+ signal->set(0, PSREPBLOCKNO, GSN_REP_GET_GCIBUFFER_REQ,
+ RepGetGciBufferReq::SignalLength);
+ ext->sendSignal(signal);
+
+#ifdef DEBUG_GREP_TRANSFER
+ ndbout_c("Requestor: Requested PS GCI buffers %d:[%d-%d]",
+ nodeGrp, first, last);
+#endif
+}
+
+void
+requestApply(void * applyObj, NdbApiSignal * signal,
+ Uint32 nodeGrp, Uint32 first, Uint32 last, Uint32 force)
+{
+ AppNDB * applier = (AppNDB *) applyObj;
+
+ if (first != last) {
+ RLOG(("WARNING! Trying to apply range [%d-%d]. This is not implemeted",
+ first, last));
+ }
+ /**
+ * Apply GCIBuffer even if it is empty.
+ */
+ applier->applyBuffer(nodeGrp, first, force);
+ /**
+ * @todo Handle return value from the method above
+ */
+}
+
+void
+requestDeleteSS(void * cbObj, NdbApiSignal * signal,
+ Uint32 nodeGrp, Uint32 firstGCI, Uint32 lastGCI)
+{
+ GCIContainer * container = (GCIContainer *) cbObj;
+
+ RLOG(("Deleting SS:%d:[%d-%d]", nodeGrp, firstGCI, lastGCI));
+
+ if(firstGCI < 0 || lastGCI<=0 || nodeGrp < 0) {
+ REPABORT("Illegal interval or wrong node group");
+ //return GrepError::REP_DELETE_NEGATIVE_EPOCH;
+ }
+
+ /*********************************************
+ * All buffers : Modify to the available ones
+ *********************************************/
+ if(firstGCI==0 && lastGCI==(Uint32)0xFFFF) {
+ container->getAvailableGCIBuffers(nodeGrp, &firstGCI, &lastGCI);
+ }
+
+ if(firstGCI == 0) {
+ Uint32 f, l;
+ container->getAvailableGCIBuffers(nodeGrp, &f, &l);
+ RLOG(("Deleting SS:[%d-%d]", f, l));
+ if(f > firstGCI) firstGCI = f;
+ }
+
+ /**
+ * Delete buffers
+ */
+ for(Uint32 i=firstGCI; i<=lastGCI; i++) {
+ if(!container->destroyGCIBuffer(i, nodeGrp)) {
+ RLOG(("WARNING! Delete non-existing epoch SS:%d:[%d]", nodeGrp, i));
+ }
+ //RLOG(("RepStateRequests: Deleting buffer SS:%d:[%d]", nodeGrp, i));
+ }
+}
+
+void
+requestDeletePS(void * cbObj, NdbApiSignal * signal,
+ Uint32 nodeGrp, Uint32 firstGCI, Uint32 lastGCI)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ RepClearPSGciBufferReq * psReq =
+ (RepClearPSGciBufferReq*)signal->getDataPtrSend();
+ /**
+ * @todo Should have better senderData /Lars
+ */
+ psReq->senderData = 4711;
+ psReq->senderRef = ext->getOwnRef();
+ psReq->firstGCI = firstGCI;
+ psReq->lastGCI = lastGCI;
+ psReq->nodeGrp = nodeGrp;
+ signal->set(0, PSREPBLOCKNO, GSN_REP_CLEAR_PS_GCIBUFFER_REQ,
+ RepClearPSGciBufferReq::SignalLength);
+ ext->sendSignal(signal);
+
+ RLOG(("Requesting deletion of PS:%d:[%d-%d]", nodeGrp, firstGCI, lastGCI));
+}
+
+/**
+ * Function that requests information from REP PS about stored GCI Buffers
+ */
+void
+requestEpochInfo(void * cbObj, NdbApiSignal* signal, Uint32 nodeGrp)
+{
+ ExtSender * ext = (ExtSender *) cbObj;
+
+ RepGetGciReq * req = (RepGetGciReq *) signal->getDataPtrSend();
+ req->nodeGrp = nodeGrp;
+ signal->set(0, PSREPBLOCKNO, GSN_REP_GET_GCI_REQ,
+ RepGetGciReq::SignalLength);
+ ext->sendSignal(signal);
+}
+
+void
+requestStartMetaLog(void * cbObj, NdbApiSignal * signal,
+ Uint32 subId, Uint32 subKey)
+{
+ RLOG(("Metalog starting. Subscription %d-%d", subId, subKey));
+ startSubscription(cbObj, signal, SubscriptionData::MetaData, subId, subKey);
+}
+
+void
+requestStartDataLog(void * cbObj, NdbApiSignal * signal,
+ Uint32 subId, Uint32 subKey)
+{
+ RLOG(("Datalog starting. Subscription %d-%d", subId, subKey));
+ startSubscription(cbObj, signal, SubscriptionData::TableData, subId, subKey);
+}
+
+void
+requestStartMetaScan(void * cbObj, NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey)
+{
+ RLOG(("Metascan starting. Subscription %d-%d", subId, subKey));
+ scanSubscription(cbObj, signal, SubscriptionData::MetaData, subId, subKey);
+}
+
+void
+requestStartDataScan(void * cbObj, NdbApiSignal* signal,
+ Uint32 subId, Uint32 subKey)
+{
+ RLOG(("Datascan starting. Subscription %d-%d", subId, subKey));
+ scanSubscription(cbObj, signal, SubscriptionData::TableData, subId, subKey);
+}
diff --git a/storage/ndb/src/old_files/rep/state/testInterval/Makefile b/storage/ndb/src/old_files/rep/state/testInterval/Makefile
new file mode 100644
index 00000000000..fbb0b48c280
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/testInterval/Makefile
@@ -0,0 +1,12 @@
+include .defs.mk
+
+TYPE := kernel ndbapitest
+
+BIN_TARGET := testInterval
+BIN_TARGET_ARCHIVES := portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES = testInterval.cpp ../Interval.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/state/testInterval/testInterval.cpp b/storage/ndb/src/old_files/rep/state/testInterval/testInterval.cpp
new file mode 100644
index 00000000000..463e4adffb7
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/testInterval/testInterval.cpp
@@ -0,0 +1,127 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "../Interval.hpp"
+
+#define TEST_REQUIRE(X); if (!(X)) { \
+ ndbout_c("Test failed in line %d", __LINE__); testPassed = false; }
+
+
+int
+main () {
+ bool testPassed = true;
+
+ Interval a, b, c;
+
+ /**
+ * isEmpty
+ */
+ TEST_REQUIRE(a.isEmpty());
+
+ a.set(3,1);
+ TEST_REQUIRE(a.isEmpty());
+
+ /**
+ * isEqual
+ */
+ a.set(1,2);
+ TEST_REQUIRE(a.isEqual(1,2));
+
+ a.set(3,1);
+ TEST_REQUIRE(a.isEqual(1,0)); // The result should be normalized
+
+ /**
+ * intervalAdd -- non-disjoint
+ */
+ a.set(1,3);
+ b.set(3,10);
+ TEST_REQUIRE(intervalAdd(a, b, &c));
+ TEST_REQUIRE(c.isEqual(1,10));
+
+ a.set(3,10);
+ b.set(1,3);
+ TEST_REQUIRE(intervalAdd(a, b, &c));
+ TEST_REQUIRE(c.isEqual(1,10));
+
+ /**
+ * intervalAdd -- consequtive
+ */
+ a.set(1,3);
+ b.set(4,10);
+ TEST_REQUIRE(intervalAdd(a, b, &c));
+ TEST_REQUIRE(c.isEqual(1,10));
+
+ a.set(4,10);
+ b.set(1,3);
+ TEST_REQUIRE(intervalAdd(a, b, &c));
+ TEST_REQUIRE(c.isEqual(1,10));
+
+ /**
+ * intervalAdd -- disjoint
+ */
+ a.set(1,3);
+ b.set(5,10);
+ c.set(4711,4711);
+ TEST_REQUIRE(!intervalAdd(a, b, &c)); // This should not work
+ TEST_REQUIRE(c.isEqual(4711,4711));
+
+ a.set(5,10);
+ b.set(1,3);
+ c.set(4711,4711);
+ TEST_REQUIRE(!intervalAdd(a, b, &c)); // This should not work
+ TEST_REQUIRE(c.isEqual(4711,4711));
+
+ /**
+ * intervalLeftMinus -- non-disjoint
+ */
+ a.set(1,3);
+ b.set(5,10);
+ intervalLeftMinus(a, b, &c);
+ TEST_REQUIRE(c.isEmpty());
+
+ a.set(5,10);
+ b.set(1,3);
+ intervalLeftMinus(a, b, &c);
+ TEST_REQUIRE(c.isEqual(5,10));
+
+ /**
+ * intervalLeftMinus -- consequtive
+ */
+ a.set(1,3);
+ b.set(4,10);
+ intervalLeftMinus(a, b, &c);
+ TEST_REQUIRE(c.isEmpty());
+
+ a.set(4,10);
+ b.set(1,3);
+ intervalLeftMinus(a, b, &c);
+ TEST_REQUIRE(c.isEqual(4,10));
+
+ /**
+ * intervalLeftMinus -- disjoint
+ */
+ a.set(1,3);
+ b.set(5,10);
+ intervalLeftMinus(a, b, &c);
+ TEST_REQUIRE(c.isEmpty());
+
+ a.set(5,10);
+ b.set(1,3);
+ intervalLeftMinus(a, b, &c);
+ TEST_REQUIRE(c.isEqual(5,10));
+
+ ndbout << "Test " << (testPassed ? "passed" : "failed") << "." << endl;
+}
diff --git a/storage/ndb/src/old_files/rep/state/testRepState/Makefile b/storage/ndb/src/old_files/rep/state/testRepState/Makefile
new file mode 100644
index 00000000000..33c6076eff3
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/testRepState/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := kernel
+
+BIN_TARGET := testRequestor
+BIN_TARGET_ARCHIVES := portlib general
+
+CCFLAGS_LOC += -I..
+
+SOURCES = testRequestor.cpp \
+ ../Requestor.cpp \
+ ../RepState.cpp \
+ ../Interval.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/state/testRepState/testRequestor.cpp b/storage/ndb/src/old_files/rep/state/testRepState/testRequestor.cpp
new file mode 100644
index 00000000000..8989f7098b8
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/testRepState/testRequestor.cpp
@@ -0,0 +1,166 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "testRequestor.hpp"
+
+#define TEST_REQUIRE(X); if (!(X)) { \
+ ndbout_c("Test failed in line %d", __LINE__); testPassed = false; }
+
+
+struct Result {
+ Uint32 nodeGrp;
+ Uint32 first;
+ Uint32 last;
+ Uint32 force;
+};
+Result result;
+
+/** Callbacks ****************************************************************/
+
+void
+f_transfer(void *, Signal* signal, Uint32 nodeGrp, Uint32 first, Uint32 last)
+{
+ result.nodeGrp = nodeGrp;
+ result.first = first;
+ result.last = last;
+ result.force = 0;
+ ndbout_c("Transfer: %d:[%d-%d] ", nodeGrp, first, last);
+}
+
+void
+f_apply(void *, Signal* signal, Uint32 nodeGrp,
+ Uint32 first, Uint32 last, Uint32 force)
+{
+ result.nodeGrp = nodeGrp;
+ result.first = first;
+ result.last = last;
+ result.force = force;
+ ndbout_c("Apply: %d:[%d-%d] (Force:%d)", nodeGrp, first, last, force);
+}
+
+void
+f_deletePS(void *, Signal* signal, Uint32 nodeGrp, Uint32 first, Uint32 last)
+{
+ result.nodeGrp = nodeGrp;
+ result.first = first;
+ result.last = last;
+ result.force = 0;
+ ndbout_c("DeletePS: %d:[%d-%d] ", nodeGrp, first, last);
+}
+
+void
+f_deleteSS(void *, Signal* signal, Uint32 nodeGrp, Uint32 first, Uint32 last)
+{
+ result.nodeGrp = nodeGrp;
+ result.first = first;
+ result.last = last;
+ result.force = 0;
+ ndbout_c("DeleteSS: %d:[%d-%d] ", nodeGrp, first, last);
+}
+
+void
+requestStartMetaLog(void * cbObj, Signal * signal)
+{
+ ndbout_c("StartMetaLog:");
+}
+
+void
+requestStartDataLog(void * cbObj, Signal * signal)
+{
+ ndbout_c("StartDataLog:");
+}
+
+void
+requestStartMetaScan(void * cbObj, Signal* signal)
+{
+ ndbout_c("StartMetaScan:");
+}
+
+void
+requestStartDataScan(void * cbObj, Signal* signal)
+{
+ ndbout_c("StartDataScan:");
+}
+
+
+/** Compare ****************************************************************/
+
+bool compare(Uint32 nodeGrp, Uint32 first, Uint32 last, Uint32 force)
+{
+ return (result.nodeGrp == nodeGrp && result.first == first &&
+ result.last == last && result.force == force);
+}
+
+
+/** Main *******************************************************************/
+
+void
+testRequestor()
+{
+ Signal * signal;
+ bool testPassed = true;
+
+ Requestor requestor;
+ requestor.setObject(0);
+ requestor.setIntervalRequests(&f_transfer,
+ &f_apply,
+ &f_deletePS,
+ &f_deleteSS);
+ requestor.setStartRequests(&requestStartMetaLog,
+ &requestStartDataLog,
+ &requestStartMetaScan,
+ &requestStartDataScan);
+ requestor.setNoOfNodeGroups(1);
+ requestor.enable();
+ requestor.enableTransfer();
+ requestor.enableDelete();
+ requestor.enableApply();
+ requestor.m_state = Requestor::LOG;
+
+ requestor.printStatus();
+
+ /**
+ * First transfer
+ */
+ Interval i(12,13);
+ requestor.add(RepState::PS, 0, i);
+ requestor.execute(signal);
+ TEST_REQUIRE(compare(0, 12, 13, 0));
+
+ requestor.printStatus();
+
+ /**
+ * State transtion test
+ */
+
+ /**
+ * First apply
+ */
+
+ /**
+ * Test end
+ */
+ if (testPassed) {
+ ndbout << "Test passed!" << endl;
+ } else {
+ ndbout << "Test FAILED!" << endl;
+ }
+}
+
+int
+main () {
+ testRequestor();
+}
diff --git a/storage/ndb/src/old_files/rep/state/testRepState/testRequestor.hpp b/storage/ndb/src/old_files/rep/state/testRepState/testRequestor.hpp
new file mode 100644
index 00000000000..726b289114d
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/state/testRepState/testRequestor.hpp
@@ -0,0 +1,24 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TEST_REQUESTOR_HPP
+#define TEST_REQUESTOR_HPP
+
+#include "../Requestor.hpp"
+
+void testRequestor();
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/GCIBuffer.cpp b/storage/ndb/src/old_files/rep/storage/GCIBuffer.cpp
new file mode 100644
index 00000000000..013600b30a5
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIBuffer.cpp
@@ -0,0 +1,173 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include <ndb_global.h>
+#include "GCIBuffer.hpp"
+
+/*****************************************************************************
+ * Constructor / Destructor
+ *****************************************************************************/
+
+GCIBuffer::GCIBuffer(Uint32 gci, Uint32 id)
+{
+ m_gci = gci;
+ m_id = id;
+ m_complete = false;
+ m_receivedBytes = 0;
+}
+
+GCIBuffer::~GCIBuffer()
+{
+ /**
+ * Loop through all pages and delete them
+ */
+ for(Uint32 i=0; i<m_pageList.size(); i++) {
+ delete m_pageList[i];
+ m_pageList[i] = 0;
+ }
+ m_pageList.clear();
+ // m_pageList = 0;
+}
+
+/*****************************************************************************
+ * Inserts
+ *****************************************************************************/
+
+void
+GCIBuffer::insertLogRecord(Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3])
+{
+ GCIPage * p;
+ if(m_pageList.size() == 0) {
+ p = new GCIPage(m_gci);
+ assert(p != NULL);
+ m_pageList.push_back(p);
+ }
+
+ p = m_pageList.back();
+ if (!p->insertLogRecord(tableId, operation, ptr)) {
+ /**
+ * GCIPage is full.
+ */
+ GCIPage * newPage = new GCIPage(m_gci);
+ assert(newPage != NULL);
+ m_pageList.push_back(newPage);
+ bool res = newPage->insertLogRecord(tableId, operation, ptr);
+
+ if(!res) {
+ ndbout << "GCIBuffer: gci : " << m_gci << endl;
+ assert(res);
+ }
+ }
+}
+
+/**
+ * @todo: We must be able to distinguish between Scan meta
+ * data and log meta data.
+ * Currently only scan meta data is considered.
+ */
+void
+GCIBuffer::insertMetaRecord(Uint32 tableId, class LinearSectionPtr ptr[3])
+{
+ GCIPage * p;
+ if(m_pageList.size()==0) {
+ p = new GCIPage(m_gci);
+ assert(p != NULL);
+ m_pageList.push_back(p);
+ }
+
+ p = m_pageList.back();
+
+ if (!p->insertMetaRecord(tableId, ptr)) {
+ /**
+ * Page is full.
+ */
+ GCIPage * newPage = new GCIPage(m_gci);
+ assert(newPage != NULL);
+ m_pageList.push_back(newPage);
+
+ bool res = newPage->insertMetaRecord(tableId, ptr);
+ assert(res);
+ }
+}
+
+void
+GCIBuffer::insertPage(Uint32 gci, char * dataPtr, Uint32 dataBLen)
+{
+ /**
+ * allocate a new GCIPage
+ */
+ GCIPage * page = new GCIPage(gci);
+ assert(page != 0);
+
+ /**
+ * copy data into page
+ */
+ page->copyDataToPage(dataPtr, dataBLen);
+
+ /**
+ * put page on pagelist.
+ */
+ m_pageList.push_back(page);
+
+ /**
+ * Update GCI Buffer received bytes
+ */
+ m_receivedBytes += dataBLen;
+}
+
+
+/*****************************************************************************
+ * Iterator
+ *****************************************************************************/
+
+GCIBuffer::iterator::iterator(const GCIBuffer* gciBuffer)
+{
+ m_gciBuffer = gciBuffer;
+ m_iterator=0;
+
+}
+
+GCIPage *
+GCIBuffer::iterator::first()
+{
+ m_iterator = 0;
+ if(m_gciBuffer->m_pageList.size() == 0) return NULL;
+ return (m_gciBuffer->m_pageList)[m_iterator];
+}
+
+
+GCIPage *
+GCIBuffer::iterator::next()
+{
+ m_iterator++;
+ if(m_gciBuffer->m_pageList.size() == 0) return NULL;
+
+ if((m_iterator<m_gciBuffer->m_pageList.size()))
+ return (m_gciBuffer->m_pageList)[m_iterator];
+ else
+ return NULL;
+}
+
+
+bool
+GCIBuffer::iterator::exists()
+{
+ return (m_iterator < m_gciBuffer->m_pageList.size());
+}
+
+
+
diff --git a/storage/ndb/src/old_files/rep/storage/GCIBuffer.hpp b/storage/ndb/src/old_files/rep/storage/GCIBuffer.hpp
new file mode 100644
index 00000000000..8a8473d1d49
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIBuffer.hpp
@@ -0,0 +1,112 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 GCI_BUFFER_HPP
+#define GCI_BUFFER_HPP
+
+#include "GCIPage.hpp"
+#include <Vector.hpp>
+#include <TransporterDefinitions.hpp>
+
+#include <signaldata/RepImpl.hpp>
+
+/**
+ * @class GCIBuffer
+ * @brief A GCIBuffer contains pages containing log records for ONE gci.
+ *
+ * @todo Load and save to disk
+ */
+
+class GCIBuffer
+{
+public:
+ GCIBuffer(Uint32 gci, Uint32 id);
+ ~GCIBuffer();
+
+ /**
+ * @fn insertLogRecord
+ * @param tableId Table this will be LogRecord applies to.
+ * @param operation Operation this LogRecord represents
+ * @param ptr Ptr of type LinearSectionPtr that contains the data.
+ * @return A full page or 0, if the insert didn't generate a full page.
+ */
+ void insertLogRecord(Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3]);
+
+ void insertMetaRecord(Uint32 tableId, class LinearSectionPtr ptr[3]);
+
+ /**
+ * @fn inserts a page, containing Records into a GCI Buffer.
+ * @param gci - the gci of the page.
+ * @param dataPtr - Pointer originating from Page::m_page.
+ * @param dataBLen - length of dataptr in bytes
+ * @note Page must NOT be deallocated after being inserted!
+ */
+ void insertPage(Uint32 gci, char * dataPtr, Uint32 dataBLen);
+
+ /**
+ * @fn isComplete
+ * @return True if this GCI Buffer is done (gci is completed).
+ */
+ bool isComplete() { return m_complete; };
+ void setComplete() { m_complete = true; };
+
+ /**
+ * @fn getReceivedBytes
+ * @returns the total number of bytes that this buffer has received.
+ */
+ Uint32 getReceivedBytes() const { return m_receivedBytes;} ;
+
+ /**
+ * Iterator for pages
+ */
+ class iterator {
+ public:
+ iterator(const GCIBuffer* gciBuffer);
+ GCIPage * first(); ///< @return First page (or NULL if no page exists)
+ GCIPage * next(); ///< @return Next page (or NULL if no more page exists)
+ bool exists(); ///< @return true if another page exists (for next())
+ private:
+ Uint32 m_iterator;
+ const GCIBuffer * m_gciBuffer;
+ };
+ friend class GCIBuffer::iterator;
+
+ /***************************************************************************
+ * GCI Buffer meta information
+ ***************************************************************************/
+ void setGCI(Uint32 gci) { m_gci = gci; };
+ Uint32 getGCI() { return m_gci; };
+
+ void setId(Uint32 id) { m_id = id; };
+ Uint32 getId() { return m_id; };
+
+ bool m_force; // if true, ignore "execute" errors when
+ // restoring buffer (PUBLIC) during phase
+ // starting.
+private:
+ /***************************************************************************
+ * Private Variables
+ ***************************************************************************/
+ Uint32 m_gci; ///< GCI of this buffer
+ Uint32 m_id; ///< <m_gci, id> names GCIBuffer
+ bool m_complete; ///< GCI complete; buffer contains
+ ///< everything
+ Vector <GCIPage *> m_pageList; ///< Storage for data/log record pages.
+ Uint32 m_receivedBytes; ///< Received bytes in this buffer
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/GCIContainer.cpp b/storage/ndb/src/old_files/rep/storage/GCIContainer.cpp
new file mode 100644
index 00000000000..c161db0769b
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIContainer.cpp
@@ -0,0 +1,272 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "GCIContainer.hpp"
+#include <NdbOut.hpp>
+#include <NdbMem.h>
+#include <new>
+
+#include <rep/rep_version.hpp>
+
+//#define GCICONTAINER_DEBUG
+
+/*****************************************************************************
+ * Constructors / Destructors
+ *****************************************************************************/
+
+GCIContainer::GCIContainer(Uint32 maxNoOfIds)
+{
+ m_maxNoOfIds = maxNoOfIds;
+
+ gciRange = new GCIRange[maxNoOfIds * sizeof(GCIRange)];
+
+ for(Uint32 i = 0; i < maxNoOfIds; i++) {
+ gciRange[i].m_firstGCI = 1; // The empty interval = [1,0]
+ gciRange[i].m_lastGCI = 0;
+ }
+ theMutexPtr = NdbMutex_Create();
+}
+
+GCIContainer::~GCIContainer()
+{
+ for(Uint32 i=0; i < m_bufferList.size(); i++) {
+ delete m_bufferList[i];
+ m_bufferList[i] = 0;
+ }
+
+ m_bufferList=0;
+ delete [] gciRange;
+ NdbMutex_Destroy(theMutexPtr);
+}
+
+/*****************************************************************************
+ * GCIBuffer Create / Destroy
+ *****************************************************************************/
+
+GCIBuffer *
+GCIContainer::createGCIBuffer(Uint32 gci, Uint32 id)
+{
+ GCIBuffer * buf = new GCIBuffer(gci, id);
+ if (buf == NULL) REPABORT("Could not allocate new buffer");
+
+ m_bufferList.push_back(buf, true);
+
+#ifdef GCICONTAINER_DEBUG
+ ndbout_c("GCIContainer: New buffer created (GCI: %d, Id: %d)", gci, id);
+#endif
+ return buf;
+}
+
+/**
+ * Delete all GCI buffers strictly less than "gci"
+ */
+void
+GCIContainer::destroyGCIBuffersBeforeGCI(Uint32 gci, Uint32 id)
+{
+ for(Uint32 i = 0 ; i < m_bufferList.size(); i++) {
+ if(m_bufferList[i]->getGCI() < gci) {
+#ifdef GCICONTAINER_DEBUG
+ ndbout_c("GCIContainer: Destroying buffer (GCI: %d, id: %d)",
+ m_bufferList[i]->getGCI(), id);
+#endif
+ destroyGCIBuffer(i, id);
+ }
+ }
+}
+
+/**
+ * Delete one GCI Buffer
+ */
+bool
+GCIContainer::destroyGCIBuffer(Uint32 gci, Uint32 id)
+{
+ m_bufferList.lock();
+ for(Uint32 i = 0 ; i < m_bufferList.size(); i++) {
+ if((m_bufferList[i]->getGCI() == gci) &&
+ (m_bufferList[i]->getId() == id)) {
+
+ /**
+ * Delete the GCI Buffer
+ */
+ delete m_bufferList[i];
+ m_bufferList[i] = 0;
+
+ /**
+ * Remove from the list of buffers stored in GCIContainer
+ */
+ m_bufferList.erase(i,false);
+ m_bufferList.unlock();
+
+ /**
+ * Set info
+ */
+ NdbMutex_Lock(theMutexPtr);
+ if(gciRange[id].m_firstGCI != gci)
+ RLOG(("WARNING! Buffer %d deleted from [%d-%d]",
+ gci, gciRange[id].m_firstGCI, gciRange[id].m_lastGCI));
+
+ gciRange[id].m_firstGCI++;
+
+ /**
+ * Normalize empty interval to [1,0]
+ */
+ if (gciRange[id].m_firstGCI > gciRange[id].m_lastGCI){
+ gciRange[id].m_firstGCI = 1;
+ gciRange[id].m_lastGCI = 0;
+ }
+ NdbMutex_Unlock(theMutexPtr);
+ return true;
+ }
+ }
+ m_bufferList.unlock();
+ return false;
+}
+
+/*****************************************************************************
+ * GCIBuffer interface
+ *****************************************************************************/
+
+GCIBuffer *
+GCIContainer::getGCIBuffer(Uint32 gci, Uint32 id)
+{
+ GCIBuffer * gciBuffer = 0;
+
+ m_bufferList.lock();
+ for(Uint32 i=0; i < m_bufferList.size(); i++) {
+ gciBuffer = m_bufferList[i];
+ if((gciBuffer->getGCI() == gci) && (gciBuffer->getId() == id)) {
+ m_bufferList.unlock();
+ return gciBuffer;
+ }
+ }
+ m_bufferList.unlock();
+ return 0;
+}
+
+void
+GCIContainer::setCompleted(Uint32 gci, Uint32 id)
+{
+ GCIBuffer * gciBuffer = getGCIBuffer(gci, id);
+ if(gciBuffer == 0) gciBuffer = createGCIBuffer(gci, id);
+
+ gciBuffer->setComplete();
+
+#ifdef GCICONTAINER_DEBUG
+ ndbout_c("GCIContainer: Buffer completely stored in GCIContainer (GCI: %d)",
+ gci);
+#endif
+
+ NdbMutex_Lock(theMutexPtr);
+
+ /**
+ * If this is the first GCI Buffer to be completed
+ * then both first and last must be updated.
+ * Subsequently, only the last value must be updated.
+ */
+ if(gciRange[id].m_firstGCI == 1 && gciRange[id].m_lastGCI == 0) {
+ gciRange[id].m_firstGCI = gci;
+ gciRange[id].m_lastGCI = gci;
+ } else {
+ if (gci != gciRange[id].m_lastGCI + 1) {
+ RLOG(("WARNING! Non-consequtive buffer %u completed [%u-%u])",
+ gci, gciRange[id].m_firstGCI, gciRange[id].m_lastGCI));
+ }
+ gciRange[id].m_lastGCI = gci;
+ }
+ NdbMutex_Unlock(theMutexPtr);
+}
+
+void
+GCIContainer::getAvailableGCIBuffers(Uint32 id, Uint32 * first, Uint32 * last)
+{
+ NdbMutex_Lock(theMutexPtr);
+ *first = gciRange[id].m_firstGCI;
+ *last = gciRange[id].m_lastGCI;
+ NdbMutex_Unlock(theMutexPtr);
+}
+
+/*****************************************************************************
+ * Inserts
+ *****************************************************************************/
+void
+GCIContainer::insertMetaRecord(Uint32 id, Uint32 tableId,
+ class LinearSectionPtr ptr[3], Uint32 gci)
+{
+ /**********************************************************
+ * 1. Find correct GCI Buffer (Doesn't exist? Create one)
+ **********************************************************/
+ GCIBuffer * gciBuffer = getGCIBuffer(gci, id);
+ if(gciBuffer == 0) gciBuffer = createGCIBuffer(gci, id);
+
+ /**********************************
+ * 2. Insert record into GCIBuffer
+ **********************************/
+ gciBuffer->insertMetaRecord(tableId, ptr);
+}
+
+void
+GCIContainer::insertLogRecord(Uint32 id, Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3], Uint32 gci)
+{
+ /*********************************************************
+ * 1. Find correct GCI Buffer (doesn't exist? create one)
+ *********************************************************/
+ GCIBuffer * gciBuffer = getGCIBuffer(gci, id);
+ if(gciBuffer == 0) gciBuffer = createGCIBuffer(gci, id);
+ /**********************************
+ * 2. Insert record into GCIBuffer
+ **********************************/
+ gciBuffer->insertLogRecord(tableId, operation, ptr);
+}
+
+void
+GCIContainer::insertPage(Uint32 gci, Uint32 id,
+ char * dataPtr, Uint32 dataBLen)
+{
+ /*********************************************************
+ * 1. Find correct GCI Buffer (doesn't exist? create one)
+ *********************************************************/
+ GCIBuffer * gciBuffer = getGCIBuffer(gci, id);
+ if(gciBuffer == 0) gciBuffer = createGCIBuffer(gci, id);
+
+ /********************************
+ * 2. Insert page into GCIBuffer
+ ********************************/
+ gciBuffer->insertPage(gci, dataPtr, dataBLen);
+}
+
+bool
+GCIContainer::reset()
+{
+ /**
+ * Clear the intervals
+ */
+ for(Uint32 i = 0; i < m_maxNoOfIds; i++) {
+ gciRange[i].m_firstGCI = 1; // The empty interval = [1,0]
+ gciRange[i].m_lastGCI = 0;
+ }
+
+ /**
+ * Destroy ALL gci buffers for ALL ids
+ */
+ for(Uint32 i=0; i < m_bufferList.size(); i++) {
+ delete m_bufferList[i];
+ m_bufferList[i] = 0;
+ }
+ m_bufferList.clear();
+
+ return true;
+}
diff --git a/storage/ndb/src/old_files/rep/storage/GCIContainer.hpp b/storage/ndb/src/old_files/rep/storage/GCIContainer.hpp
new file mode 100644
index 00000000000..48cbc66bfbd
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIContainer.hpp
@@ -0,0 +1,121 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 GCI_CONTAINER_HPP
+#define GCI_CONTAINER_HPP
+
+#include <Vector.hpp>
+
+#include "LogRecord.hpp"
+#include "GCIBuffer.hpp"
+
+#undef swap
+#include <list>
+#include <iterator>
+
+/**
+ * @class GCIContainer
+ * @brief Responsible for storing LogRecord:s in GCIBuffer:s
+ *
+ * Each GCIBuffer stored in the GCIContainer is named by a pair <GCI, id>.
+ * (On PS REP the id is the nodeId, on SS REP the id is the node group).
+ */
+class GCIContainer {
+public:
+ GCIContainer(Uint32 maxNoOfIds);
+ ~GCIContainer();
+
+ /***************************************************************************
+ * GCIBuffer interface
+ ***************************************************************************/
+ /**
+ * @return GCIBuffer if success, NULL otherwise
+ */
+ GCIBuffer * createGCIBuffer(Uint32 gci, Uint32 id);
+
+ /**
+ * Destroy all buffers with GCI strictly less than gci.
+ */
+ void destroyGCIBuffersBeforeGCI(Uint32 gci, Uint32 id);
+
+ /**
+ * Destroy all buffers with GCI gci.
+ * @return true if buffer was deleted, false if no buffer exists
+ */
+ bool destroyGCIBuffer(Uint32 gci, Uint32 id);
+
+ /**
+ * Fetch buffer
+ * @return GCIBuffer for gci, or NULL if no buffer found
+ */
+ GCIBuffer * getGCIBuffer(Uint32 gci, Uint32 id);
+
+ /**
+ * Set that buffer is completed, i.e. no more records are to be inserted
+ */
+ void setCompleted(Uint32 gci, Uint32 id);
+
+
+ /**
+ * @fn insertPage
+ * @param gci GCI this page belongs to.
+ * @param id Id this page belongs to.
+ * @param dataPtr Pointer originating from Page::m_page
+ * @param dataBLen Length in bytes of data following dataptr.
+ */
+ void insertPage(Uint32 gci, Uint32 id, char * dataPtr, Uint32 dataBLen);
+
+
+ /***************************************************************************
+ * Record interface
+ ***************************************************************************/
+ void insertLogRecord(Uint32 id, Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3], Uint32 gci);
+
+ void insertMetaRecord(Uint32 id, Uint32 tableId,
+ class LinearSectionPtr ptr[3], Uint32 gci);
+
+ /**
+ * Get available (complete) GCI Buffers that exists in the container.
+ * first == last means that there is one complete buffer
+ * @param id Id for which to as for available gci buffers.
+ * @param first First complete gci buffer
+ * @param last Last complete gci buffer
+ */
+ void getAvailableGCIBuffers(Uint32 id, Uint32 * first, Uint32 * last);
+
+ /**
+ * Resets the gcicontainer to its original state (initial state and empty)
+ * I.e., same state as when the object was first constructed.
+ * @return true if reset was ok
+ */
+ bool reset();
+
+private:
+ NdbMutex* theMutexPtr;
+ MutexVector <GCIBuffer *> m_bufferList; ///< All GCIBuffers stored
+
+ typedef struct GCIRange {
+ Uint32 m_firstGCI;
+ Uint32 m_lastGCI;
+ };
+
+ Uint32 m_maxNoOfIds;
+
+ GCIRange * gciRange; ///< Array of GCI ranges for each id
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/GCIContainerPS.cpp b/storage/ndb/src/old_files/rep/storage/GCIContainerPS.cpp
new file mode 100644
index 00000000000..5adb53f965c
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIContainerPS.cpp
@@ -0,0 +1,128 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "GCIContainerPS.hpp"
+#include <NdbOut.hpp>
+#include <NdbMem.h>
+#include <new>
+
+GCIContainerPS::GCIContainerPS(Uint32 maxNoOfNodeGrps)
+{
+ m_container = new GCIContainer(maxNoOfNodeGrps);
+ if (!m_container) REPABORT("Could not allocate new GCIContainer");
+}
+
+GCIContainerPS::~GCIContainerPS()
+{
+ delete m_container;
+}
+
+void
+GCIContainerPS::setNodeGroupInfo(NodeGroupInfo * info)
+{
+ m_nodeGroupInfo=info;
+};
+
+void
+GCIContainerPS::createGCIBuffer(Uint32 gci, Uint32 id)
+{
+ m_container->createGCIBuffer(gci, id);
+}
+
+void
+GCIContainerPS::getAvailableGCIBuffers(Uint32 id /*nodegrp */,
+ Uint32 * first, Uint32 * last) {
+
+ Uint32 nodeId = m_nodeGroupInfo->getPrimaryNode(id);
+ if(!nodeId) {
+ *first = 1;
+ *last = 0;
+ return;
+ }
+
+ /**
+ *@todo do smart stuff with this!
+ */
+ m_container->getAvailableGCIBuffers(nodeId, first, last);
+
+}
+
+void
+GCIContainerPS::destroyGCIBuffersBeforeGCI(Uint32 gci)
+{
+ //for each node in every nodeGrp do:
+ NodeGroupInfo::iterator * it;
+ for(Uint32 i=0; i<m_nodeGroupInfo->getNoOfNodeGroups(); i++) {
+ it = new NodeGroupInfo::iterator(i, m_nodeGroupInfo);
+ for(NodeConnectInfo * nci=it->first(); it->exists();nci=it->next()) {
+ m_container->destroyGCIBuffersBeforeGCI(gci, nci->nodeId);
+ }
+ delete it;
+ }
+}
+
+void
+GCIContainerPS::insertLogRecord(Uint32 id, Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3], Uint32 gci)
+{
+ m_container->insertLogRecord(id, tableId, operation, ptr, gci);
+}
+
+void
+GCIContainerPS::insertMetaRecord(Uint32 id, Uint32 tableId,
+ class LinearSectionPtr ptr[3], Uint32 gci)
+{
+ m_container->insertMetaRecord(id, tableId, ptr, gci);
+}
+
+void
+GCIContainerPS::setCompleted(Uint32 gci, Uint32 id)
+{
+ m_container->setCompleted(gci, id);
+}
+
+GCIBuffer *
+GCIContainerPS::getGCIBuffer(Uint32 gci, Uint32 id)
+{
+ return m_container->getGCIBuffer(gci, id);
+}
+
+/**
+ * @todo: fix return value
+ */
+bool
+GCIContainerPS::destroyGCIBuffer(Uint32 gci, Uint32 id)
+{
+ //for each node in nodeGrp id do:
+ NodeGroupInfo::iterator * it;
+ it = new NodeGroupInfo::iterator(id, m_nodeGroupInfo);
+ for(NodeConnectInfo * nci=it->first(); it->exists();nci=it->next())
+ {
+ if(!m_container->destroyGCIBuffer(gci, nci->nodeId))
+ {
+ delete it;
+ return false;
+ }
+ }
+ delete it;
+ return true;
+}
+
+bool
+GCIContainerPS::reset()
+{
+ return m_container->reset();
+}
diff --git a/storage/ndb/src/old_files/rep/storage/GCIContainerPS.hpp b/storage/ndb/src/old_files/rep/storage/GCIContainerPS.hpp
new file mode 100644
index 00000000000..7f5aaac4840
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIContainerPS.hpp
@@ -0,0 +1,90 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 GCI_CONTAINER_PS_HPP
+#define GCI_CONTAINER_PS_HPP
+
+#include <Vector.hpp>
+#include <TransporterDefinitions.hpp>
+
+#include "NodeGroupInfo.hpp"
+#include <rep/storage/GCIContainer.hpp>
+
+#include <list>
+#include <iterator>
+
+/**
+ * @class GCIContainerPS
+ * @brief Interface to GCIContainer that takes node groups into account
+ */
+class GCIContainerPS
+{
+public:
+ GCIContainerPS(Uint32 maxNoOfNodeGrps);
+ ~GCIContainerPS();
+
+ void setNodeGroupInfo(NodeGroupInfo * info);
+ NodeGroupInfo * getNodeGroupInfo() {return m_nodeGroupInfo;};
+
+ void createGCIBuffer(Uint32 gci, Uint32 id);
+ void getAvailableGCIBuffers(Uint32 id /*nodegrp */,
+ Uint32 * first, Uint32 * last);
+
+ /***************************************************************************
+ * Record interface
+ ***************************************************************************/
+ void insertLogRecord(Uint32 grpId, Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3], Uint32 gci);
+
+ void insertMetaRecord(Uint32 grpId, Uint32 tableId,
+ class LinearSectionPtr ptr[3], Uint32 gci);
+
+ /**
+ * Destroy all buffers with GCI strictly less than gci.
+ */
+ void destroyGCIBuffersBeforeGCI(Uint32 gci);
+
+ /**
+ * Set that buffer is completed, i.e. no more records are to be inserted
+ */
+ void setCompleted(Uint32 gci, Uint32 id);
+
+ /**
+ * Fetch buffer
+ * @return GCIBuffer for gci, or NULL if no buffer found
+ */
+ GCIBuffer * getGCIBuffer(Uint32 gci, Uint32 id);
+
+ /**
+ * Destroy all buffers with GCI gci.
+ * @return true if buffer was deleted, false if no buffer exists
+ */
+ bool destroyGCIBuffer(Uint32 gci, Uint32 id);
+
+
+ /**
+ * Resets the gcicontainer to its original state (initial state and empty)
+ * @return true if reset was ok
+ */
+ bool reset();
+
+private:
+ GCIContainer * m_container;
+ NodeGroupInfo * m_nodeGroupInfo;
+};
+
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/GCIPage.cpp b/storage/ndb/src/old_files/rep/storage/GCIPage.cpp
new file mode 100644
index 00000000000..05ecde2fee1
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIPage.cpp
@@ -0,0 +1,165 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "GCIPage.hpp"
+#include "assert.h"
+#include <new>
+
+GCIPage::GCIPage(Uint32 gci)
+{
+ m_first = NULL;
+ m_last = NULL;
+ m_gci = gci;
+ m_full = false;
+ m_currentPagePos=m_page;
+ m_usedBytes = 0;
+}
+
+/*****************************************************************************
+ * Insert
+ *****************************************************************************/
+
+/**
+ * Store a new log record on this page.
+ * @return True if success, false otherwise
+ */
+bool
+GCIPage::insertLogRecord(Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3])
+{
+ /**
+ * Calculate size of new logrecord in bytes
+ */
+ assert(m_page!=NULL);
+ Uint32 size = 4*ptr[0].sz + 4*ptr[1].sz + sizeof(LogRecord);
+
+ if(!((m_currentPagePos + size ) < (m_page + m_pageBSize))) {
+ m_full = true;
+ return false; // No free space. GCIBuffer must allocate a new page
+ }
+ LogRecord * lr = new(m_currentPagePos) LogRecord();
+ if (lr==0) REPABORT("Could not allocate new log record");
+
+ lr->recordType = Record::LOG;
+ lr->recordLen = size;
+ lr->operation = operation;
+ lr->tableId = tableId;
+ lr->attributeHeaderWSize = ptr[0].sz;
+ lr->attributeDataWSize = ptr[1].sz;
+
+ m_currentPagePos += sizeof(LogRecord);
+
+ lr->attributeHeader = (Uint32*)m_currentPagePos;
+ memcpy(lr->attributeHeader, ptr[0].p, lr->attributeHeaderWSize * 4);
+
+ m_currentPagePos += lr->attributeHeaderWSize * 4;
+
+ lr->attributeData = (Uint32*)m_currentPagePos;
+ memcpy(lr->attributeData, ptr[1].p, lr->attributeDataWSize * 4);
+
+ m_currentPagePos += lr->attributeDataWSize * 4;
+
+ m_usedBytes+=size;
+ return true;
+}
+
+/**
+ * Store a new log record on this page.
+ * @return True if sucessful, false otherwise.
+ */
+bool
+GCIPage::insertMetaRecord(Uint32 tableId, class LinearSectionPtr ptr[3])
+{
+ /**
+ * Calculate size of new logrecord in bytes
+ */
+ Uint32 size = 4*ptr[0].sz + sizeof(MetaRecord);
+
+ if(!((m_currentPagePos + size ) < (m_page + m_pageBSize))) {
+ m_full = true;
+ return false; // No free space. GCIBuffer must allocate a new page
+ }
+ MetaRecord * mr = new(m_currentPagePos) MetaRecord();
+ if (mr==0) REPABORT("Could not allocate new meta record");
+
+ // mr->operation = operation;
+ mr->recordType = Record::META;
+ mr->recordLen = size;
+
+ mr->tableId = tableId;
+ mr->dataLen = ptr[0].sz;
+
+
+ m_currentPagePos += sizeof(MetaRecord);
+
+ mr->data = (Uint32*)m_currentPagePos;
+ memcpy(mr->data, ptr[0].p, mr->dataLen * 4);
+
+ m_currentPagePos += mr->dataLen * 4;
+
+ m_usedBytes+=size;
+ return true;
+}
+
+/**
+ * copy function
+ */
+void
+GCIPage::copyDataToPage(char * dataPtr, Uint32 dataBLen)
+{
+ assert (dataBLen < m_pageBSize);
+ memcpy(m_page, dataPtr, dataBLen);
+ m_currentPagePos=m_page + dataBLen;
+ m_usedBytes = dataBLen;
+ m_full = true;
+ m_first = (Record * )m_page;
+ dataPtr = 0;
+}
+
+/*****************************************************************************
+ * Iterator
+ *****************************************************************************/
+
+GCIPage::iterator::iterator(const GCIPage* page)
+{
+ m_gciPage = page;
+ m_data = m_gciPage->m_page;
+ m_currentRecord = (Record*)m_data;
+}
+
+Record *
+GCIPage::iterator::first()
+{
+ return m_currentRecord;
+}
+
+Record *
+GCIPage::iterator::next()
+{
+ m_currentRecord = (Record*)
+ ((char*)(m_currentRecord)+ m_currentRecord->recordLen);
+ if((char*)m_currentRecord < (char*)(m_data + m_gciPage->m_usedBytes))
+ return m_currentRecord;
+ else {
+ return 0;
+ }
+}
+
+bool
+GCIPage::iterator::exists()
+{
+ return ((char*)m_currentRecord < (m_data + m_gciPage->m_usedBytes));
+}
diff --git a/storage/ndb/src/old_files/rep/storage/GCIPage.hpp b/storage/ndb/src/old_files/rep/storage/GCIPage.hpp
new file mode 100644
index 00000000000..50c5ab0cfba
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/GCIPage.hpp
@@ -0,0 +1,114 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 GCI_PAGE_HPP
+#define GCI_PAGE_HPP
+
+#include "LogRecord.hpp"
+#include <TransporterDefinitions.hpp>
+
+#include <rep/rep_version.hpp>
+
+/**
+ * @class GCIPage
+ * @brief A GCIPage contains a number of LogRecords for a certain GCI.
+ */
+class GCIPage
+{
+public:
+ GCIPage(Uint32 gci);
+ GCIPage(Uint32 gci, char * dataPtr, Uint32 szBytes);
+
+ /**
+ * @fn insertLogRecord
+ * @param tableId the table this will be LogRecord applies to.
+ * @param operation the operation this LogRecord represents
+ * @param ptr A LinearSectionPtr p'tr that contains the data.
+ * @return PAGE_FULL if the page is full, otherwise "true"
+ */
+ bool insertLogRecord(Uint32 tableId, Uint32 operation,
+ class LinearSectionPtr ptr[3]);
+
+ /**
+ * @fn insertMetaRecord
+ */
+ bool insertMetaRecord(Uint32 tableId, class LinearSectionPtr ptr[3]);
+
+ /**
+ * @fn getFirstRecord
+ * @return First record (or NULL if no record is stored on page)
+ */
+ Record * getFirstRecord() { return m_first; };
+
+ /**
+ * @fn getStorage
+ */
+ Uint32 * getStoragePtr() const {return (Uint32*)m_page;} ;
+ Uint32 getStorageByteSize() const {return m_usedBytes;} ;
+ Uint32 getStorageWordSize() const {return m_usedBytes >> 2;};
+
+ /**
+ * @fn copyDataToPage
+ * @info copy dataPtr to Page
+ * @param dataPtr - data to copy
+ * @param dataBLen - size in bytes to copy.
+ */
+ void copyDataToPage(char * dataPtr, Uint32 szBytes);
+
+ /**
+ * Iterator for records (Not yet used! Maybe should not be used.)
+ */
+ class iterator {
+ public:
+ iterator(const GCIPage* page);
+ Record * first(); ///< @return First record (or NULL if no page exists)
+ Record * next(); ///< @return Next record (or NULL if no more records)
+ bool exists(); ///< @return true if another record exists-for next()
+ private:
+ Record * m_currentRecord;
+ const char * m_data;
+ const GCIPage * m_gciPage;
+ };
+ friend class GCIPage::iterator;
+
+ /**
+ * @fn getGCI
+ * Get the GCI of all log records stored on this page.
+ */
+ Uint32 getGCI() { return m_gci; };
+
+ /**
+ * @fn isFull
+ * @return true if page is full, i.e. is one attempt to add a record
+ * has failed, false otherwise.
+ */
+ bool isFull() { return m_full; };
+
+private:
+ Uint32 m_gci; ///< GCI for this page
+
+ Record * m_first; ///< Pointer to first log record
+ Record * m_last; ///< Pointer to last log record
+
+ bool m_full;
+
+ static const Uint32 m_pageBSize = 8192; ///< Page size in bytes
+ char m_page[m_pageBSize]; ///< Storage for pages
+ char * m_currentPagePos;
+ Uint32 m_usedBytes;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/LogRecord.hpp b/storage/ndb/src/old_files/rep/storage/LogRecord.hpp
new file mode 100644
index 00000000000..a0bf3d52372
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/LogRecord.hpp
@@ -0,0 +1,81 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 LOG_RECORD_HPP
+#define LOG_RECORD_HPP
+
+#include <ndb_global.h>
+#include <portlib/NdbMem.h>
+
+/**
+ * @class Record
+ * @brief
+ */
+class Record {
+public:
+ enum RecordType { META = 1, LOG = 2 };
+ RecordType recordType;
+ Uint32 recordLen; ///< Size in bytes of entire log record, incl payload
+};
+
+
+/**
+ * @class LogRecord
+ * @brief
+ */
+class LogRecord : public Record {
+public:
+ ~LogRecord() {
+ NdbMem_Free(attributeHeader);
+ NdbMem_Free(attributeData);
+ }
+
+public:
+ Uint32 gci; //0
+ Uint32 operation; //4
+ Uint32 tableId; //8
+
+ Uint32 attributeHeaderWSize; //12
+ Uint32 attributeDataWSize; //16
+ Uint32 * attributeHeader; //20
+ Uint32 * attributeData; //24
+
+ /**
+ * Next pointer
+ */
+};
+
+
+/**
+ * @class MetaRecord
+ * @brief
+ */
+class MetaRecord : public Record {
+public:
+ ~MetaRecord() {
+ NdbMem_Free(data);
+ }
+
+public:
+ Uint32 gci;
+ Uint32 tableId;
+ Uint32 dataLen; //in words of the data (below)
+ Uint32 *data;
+};
+
+
+#endif
+
diff --git a/storage/ndb/src/old_files/rep/storage/Makefile b/storage/ndb/src/old_files/rep/storage/Makefile
new file mode 100644
index 00000000000..89b3af455e8
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE := repserver
+
+ARCHIVE_TARGET := repstorage
+
+SOURCES = GCIContainer.cpp \
+ GCIContainerPS.cpp \
+ GCIBuffer.cpp \
+ GCIPage.cpp \
+ NodeGroupInfo.cpp \
+ NodeGroup.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/storage/NodeConnectInfo.hpp b/storage/ndb/src/old_files/rep/storage/NodeConnectInfo.hpp
new file mode 100644
index 00000000000..403f92a5999
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/NodeConnectInfo.hpp
@@ -0,0 +1,29 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NODE_CONNECTINFO_HPP
+#define NODE_CONNECTINFO_HPP
+
+#include <ndb_types.h>
+
+struct NodeConnectInfo {
+ NodeConnectInfo(Uint16 n, bool c): nodeId(n), connected(c) {};
+ Uint32 nodeId;
+ bool connected;
+};
+
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/NodeGroup.cpp b/storage/ndb/src/old_files/rep/storage/NodeGroup.cpp
new file mode 100644
index 00000000000..33451efb104
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/NodeGroup.cpp
@@ -0,0 +1,149 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "NodeGroup.hpp"
+#include <NdbOut.hpp>
+
+//#define NODE_GROUP_DEBUG
+
+NodeGroup::NodeGroup(Uint32 nodeGrp) {
+ m_nodeGrp = nodeGrp;
+ m_primaryNode = 0;
+}
+
+NodeGroup::~NodeGroup() {
+ for(Uint32 i=0; i<m_nodeConnectList.size(); i++) {
+ delete m_nodeConnectList[i];
+ m_nodeConnectList.erase(i);
+ }
+}
+
+void
+NodeGroup::addNode(Uint32 nodeId, bool connected) {
+#ifdef NODE_GROUP_DEBUG
+ ndbout_c("NodeGroup: addNode(nodeId=%d, connected=%d), nodegrp=%d",
+ nodeId, connected, m_nodeGrp);
+#endif
+
+ /**
+ * If node already in node group, then do nothing except
+ * setting the connect statusflag for the node (in case it
+ * has changed).
+ */
+ for(Uint32 i=0; i < m_nodeConnectList.size(); i++)
+ if(m_nodeConnectList[i]->nodeId == nodeId) {
+ m_nodeConnectList[i]->connected = connected;
+ return;
+ }
+
+ /**
+ * If node not already in node group, then add node
+ */
+ m_nodeConnectList.push_back(new NodeConnectInfo(nodeId, connected));
+ sort();
+
+#ifdef NODE_GROUP_DEBUG
+ for(Uint32 i=0; i < m_nodeConnectList.size(); i++)
+ ndbout_c("NodeGroup: NodeId=%d", m_nodeConnectList[i]->nodeId);
+#endif
+}
+
+/**
+ * crappy sort
+ */
+void NodeGroup::sort() {
+ NodeConnectInfo * tmp;
+ if(m_nodeConnectList.size()<2)
+ return;
+ for(Uint32 i=0; i < m_nodeConnectList.size()-1; i++) {
+ for(Uint32 j=m_nodeConnectList.size()-1;j>i+1; j--) {
+ if(m_nodeConnectList[j]->nodeId < m_nodeConnectList[j-1]->nodeId) {
+ tmp=m_nodeConnectList[j];
+ m_nodeConnectList[j]=m_nodeConnectList[j-1];
+ m_nodeConnectList[j-1]=tmp;
+ }
+ }
+ }
+}
+
+Uint32
+NodeGroup::getFirstConnectedNode() {
+ for(Uint32 i=0; i<m_nodeConnectList.size(); i++){
+ if(m_nodeConnectList[i]->connected)
+ return m_nodeConnectList[i]->nodeId;
+ }
+ return 0;
+}
+
+Uint32
+NodeGroup::getNodeGrp() {
+ return m_nodeGrp;
+}
+
+Vector <NodeConnectInfo *> *
+NodeGroup::getNodeConnectList(){
+ return &m_nodeConnectList;
+}
+
+void
+NodeGroup::setNodeConnectStatus(Uint32 nodeId, bool connected) {
+ for(Uint32 i=0; i<m_nodeConnectList.size(); i++){
+ if(m_nodeConnectList[i]->nodeId==nodeId) {
+ m_nodeConnectList[i]->connected=connected;
+ break;
+ }
+ }
+}
+
+bool
+NodeGroup::isConnected(Uint32 nodeId) {
+ for(Uint32 i=0; i<m_nodeConnectList.size(); i++){
+ if(m_nodeConnectList[i]->nodeId == nodeId) {
+ return m_nodeConnectList[i]->connected;
+ }
+ }
+ REPABORT1("Check for non-existing node to be connected", nodeId);
+}
+
+
+bool
+NodeGroup::fullyConnected() {
+ for(Uint32 i=0; i<m_nodeConnectList.size(); i++){
+ if(!(m_nodeConnectList[i]->connected))
+ return false;
+ }
+ return true;
+}
+
+bool
+NodeGroup::connectedNodeGrp() {
+ for(Uint32 i=0; i<m_nodeConnectList.size(); i++){
+ if(m_nodeConnectList[i]->connected) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+NodeGroup::exists(Uint32 nodeId) {
+ for(Uint32 i=0;i<m_nodeConnectList.size();i++) {
+ if(m_nodeConnectList[i]->nodeId==nodeId)
+ return true;
+ }
+ return false;
+}
diff --git a/storage/ndb/src/old_files/rep/storage/NodeGroup.hpp b/storage/ndb/src/old_files/rep/storage/NodeGroup.hpp
new file mode 100644
index 00000000000..1f515e02a23
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/NodeGroup.hpp
@@ -0,0 +1,109 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NODE_GROUP_HPP
+#define NODE_GROUP_HPP
+
+#include "NodeConnectInfo.hpp"
+#include <Vector.hpp>
+#include <ndb_types.h>
+
+#include <rep/rep_version.hpp>
+
+/**
+ * @class NodeGroup
+ * @brief Contains info about all nodes belonging to one node group
+ */
+class NodeGroup {
+public:
+ NodeGroup(Uint32 nodeGrp);
+ ~NodeGroup();
+ /**
+ * Add node to node group
+ * @param nodeId Node id of node to add
+ * @param connected Status of this node (true==connected)
+ */
+ void addNode(Uint32 nodeId, bool connected);
+
+ /**
+ * get first connected node in this node group
+ * @returns nodeId, 0 if there is no connected node...
+ */
+ Uint32 getFirstConnectedNode();
+
+ /**
+ * get the primary node id
+ * @returns nodeId, the primary node id
+ */
+ Uint32 getPrimaryNode() {return m_primaryNode;};
+
+
+ /**
+ * sets a node in this nodegroup as the primary node
+ */
+ void setPrimaryNode(Uint32 nodeId) {m_primaryNode=nodeId;};
+
+
+ /**
+ * get the node group
+ * @returns the nodegroup number (m_nodeGrp)
+ */
+ Uint32 getNodeGrp();
+
+ /**
+ * set the connection status for a particular node
+ * @param nodeId - the nodeId to set the connect status on
+ * @param connected - the status of this node (true==connected)
+ */
+ void setNodeConnectStatus(Uint32 nodeId, bool connected);
+
+ /**
+ * Get the connection status for a particular node
+ * @param nodeId - the nodeId to check the connect status on
+ * @returns true if node is connected, otherwise false
+ */
+ bool isConnected(Uint32 nodeId);
+
+ /**
+ * gives the status of this nodegroup.
+ * @returns true if atleast one node in the node group is connected
+ */
+ bool connectedNodeGrp();
+
+ /**
+ * @returns true if ALL nodes are connected
+ */
+ bool fullyConnected();
+
+ /**
+ *
+ * @returns true if node exists in nodegroup
+ */
+ bool exists(Uint32 nodeId);
+
+ Vector <NodeConnectInfo *> * getNodeConnectList();
+
+private:
+ /**
+ * Sort list (bubble sort)
+ */
+ void sort();
+ Uint32 m_primaryNode;
+ Uint32 m_nodeGrp;
+ Vector<NodeConnectInfo *> m_nodeConnectList;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/storage/NodeGroupInfo.cpp b/storage/ndb/src/old_files/rep/storage/NodeGroupInfo.cpp
new file mode 100644
index 00000000000..8c250268997
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/NodeGroupInfo.cpp
@@ -0,0 +1,218 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "NodeGroupInfo.hpp"
+
+NodeGroupInfo::NodeGroupInfo()
+{
+}
+
+NodeGroupInfo::~NodeGroupInfo()
+{
+ for(Uint32 i=0; i<m_nodeGroupList.size(); i++) {
+ delete m_nodeGroupList[i];
+ }
+ m_nodeGroupList.clear();
+}
+
+
+void
+NodeGroupInfo::setPrimaryNode(Uint32 nodeGrp, Uint32 nodeId) {
+ Uint32 pos;
+ /**
+ * Validation check to find out that the nodegroup really exists.
+ * The list is not sorted, so the index of the nodegroup is returned
+ * in pos.
+ */
+ if(existsNodeGroup(nodeGrp, &pos)) {
+ m_nodeGroupList[pos]->setPrimaryNode(nodeId);
+ } else {
+ /**
+ * could not find node group
+ */
+ RLOG(("Node group not found"));
+ REPABORT("Node group not found");
+ }
+}
+
+Uint32
+NodeGroupInfo::getPrimaryNode(Uint32 nodeGrp) {
+ Uint32 pos;
+ /**
+ * Validation check to find out that the nodegroup really exists.
+ * The list is not sorted, so the index of the nodegroup is returned
+ * in pos.
+ */
+ if(existsNodeGroup(nodeGrp, &pos)) {
+ return m_nodeGroupList[pos]->getPrimaryNode();
+ } else {
+ /**
+ * could not find node group
+ */
+ RLOG(("Node group not found"));
+ REPABORT("Node group not found");
+ }
+}
+
+void
+NodeGroupInfo::addNodeToNodeGrp(Uint32 nodeId, bool connected, Uint32 nodeGrp)
+{
+ Uint32 pos;
+ if(existsNodeGroup(nodeGrp, &pos)) {
+ /**
+ * NG exists -> just add the node
+ */
+ m_nodeGroupList[pos]->addNode(nodeId, connected);
+
+ } else {
+ /**
+ * NG do not exist -> create a new nodeGrp and add the node
+ */
+ m_nodeGroupList.push_back(new NodeGroup(nodeGrp));
+
+ /**
+ * paranoia
+ */
+ if(existsNodeGroup(nodeGrp, &pos)) {
+ m_nodeGroupList[pos]->addNode(nodeId, connected);
+ } else {
+ REPABORT("");
+ }
+ }
+}
+
+Uint32
+NodeGroupInfo::findNodeGroup(Uint32 nodeId)
+{
+ /**
+ * Check for existance in each nodegroup
+ */
+ for(Uint32 i=0; i<m_nodeGroupList.size(); i++) {
+ if(m_nodeGroupList[i]->exists(nodeId)) return i;
+ }
+
+ REPABORT1("No node group known for node", nodeId);
+}
+
+Uint32
+NodeGroupInfo::getFirstConnectedNode(Uint32 nodeGrp)
+{
+ Uint32 pos;
+ /**
+ * Validation check to find out that the nodegroup really exists.
+ * The list is not sorted, so the index of the nodegroup is returned
+ * in pos.
+ */
+ if(existsNodeGroup(nodeGrp, &pos)) {
+ return m_nodeGroupList[pos]->getFirstConnectedNode();
+ } else {
+ /**
+ * could not find node group
+ */
+ REPABORT("");
+ }
+}
+
+bool
+NodeGroupInfo::connectedNodeGrp(Uint32 nodeGrp)
+{
+ return m_nodeGroupList[nodeGrp]->connectedNodeGrp();
+}
+
+bool
+NodeGroupInfo::isConnected(Uint32 nodeId)
+{
+ Uint32 nodeGrp = findNodeGroup(nodeId);
+ return m_nodeGroupList[nodeGrp]->isConnected(nodeId);
+
+}
+
+bool
+NodeGroupInfo::fullyConnected()
+{
+ for(Uint32 i=0; i<m_nodeGroupList.size(); i++) {
+ if(!(m_nodeGroupList[i]->fullyConnected()))
+ return false;
+ }
+ return true;
+}
+
+
+void
+NodeGroupInfo::setConnectStatus(Uint32 nodeId, bool connected)
+{
+ Uint32 nodeGrp = findNodeGroup(nodeId);
+ m_nodeGroupList[nodeGrp]->setNodeConnectStatus(nodeId,connected);
+}
+
+
+bool
+NodeGroupInfo::existsNodeGroup(Uint32 nodeGrp, Uint32 * pos)
+{
+ for(Uint32 i=0; i<m_nodeGroupList.size(); i++) {
+ if(m_nodeGroupList[i]->getNodeGrp()==nodeGrp) {
+ *pos=i;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*****************************************************************************
+ * Iterator
+ *****************************************************************************/
+
+NodeGroupInfo::iterator::iterator(Uint32 nodeGrp, NodeGroupInfo * ngi)
+{
+ m_iterator = 0;
+ for(Uint32 i=0; i < ngi->m_nodeGroupList.size(); i++) {
+ if(ngi->m_nodeGroupList[i]->getNodeGrp()==nodeGrp) {
+ m_nodeList = ngi->m_nodeGroupList[i]->getNodeConnectList();
+ return;
+ }
+ }
+ m_nodeList=0;
+}
+
+bool
+NodeGroupInfo::iterator::exists()
+{
+ if(m_nodeList==0) return 0;
+ return (m_iterator < m_nodeList->size());
+}
+
+NodeConnectInfo *
+NodeGroupInfo::iterator::first()
+{
+ m_iterator=0;
+ if(m_nodeList==0) return 0;
+ if(m_nodeList->size() == 0) return 0;
+ return (*m_nodeList)[m_iterator];
+}
+
+NodeConnectInfo *
+NodeGroupInfo::iterator::next()
+{
+ m_iterator++;
+ if(m_nodeList==0) return 0;
+ if(m_nodeList->size() == 0) return 0;
+ if(m_iterator<m_nodeList->size())
+ return (*m_nodeList)[m_iterator];
+ else
+ return 0;
+}
+
diff --git a/storage/ndb/src/old_files/rep/storage/NodeGroupInfo.hpp b/storage/ndb/src/old_files/rep/storage/NodeGroupInfo.hpp
new file mode 100644
index 00000000000..3d0499d4425
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/storage/NodeGroupInfo.hpp
@@ -0,0 +1,145 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 NODE_GROUPINFO_HPP
+#define NODE_GROUPINFO_HPP
+
+#include <Vector.hpp>
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+//#include <NdbSleep.h>
+
+#include "NodeGroup.hpp"
+#include <rep/rep_version.hpp>
+
+/**
+ * @class NodeGroupInfo
+ * @brief Contains info about all node groups and their connectivity status
+ */
+class NodeGroupInfo {
+public:
+ NodeGroupInfo();
+ ~NodeGroupInfo();
+
+ /**
+ * Add a node to a nodegroup together with the status of the node
+ * @param nodeId - the nodeId to add
+ * @param connected - true/false
+ * @param nodeGrp - the nodegroup to add this node to
+ */
+ void addNodeToNodeGrp(Uint32 nodeId, bool connected, Uint32 nodeGrp);
+
+ /**
+ * Get the nodegroup that a node belongs to.
+ * @param nodeId - the nodeId to check wich nodegroup it belongs to
+ * @return the nodegroup
+ */
+ Uint32 findNodeGroup(Uint32 nodeId);
+
+ /**
+ * Get the first connected node in a node group
+ * @param nodegroup - the node group to get the node in.
+ * @return nodeId, 0 if there is no connected node in the nodegroup
+ */
+ Uint32 getFirstConnectedNode(Uint32 nodeGrp);
+
+
+ /**
+ * sets a nodeId in a nodeGroup as the primary node. If the
+ * primary node fails, then a new node in the node group is chosen
+ * @param nodegroup - the node group to get the node in.
+ * @param nodeId, 0 if there is no connected node in the nodegroup
+ */
+ void setPrimaryNode(Uint32 nodeGrp, Uint32 nodeId);
+
+ /**
+ * gets the nodeId in the nodegroup of the primary node.
+ * @param nodegroup - the node group to get the node in.
+ * @return nodeId, 0 if there is no connected node in the nodegroup
+ */
+ Uint32 getPrimaryNode(Uint32 nodeGrp);
+
+
+ /**
+ * Checks if at least one node in the nodegroup is connected.
+ * @param nodeGrp - the nodegrp to check
+ * @return true if >0 nodes are connected in the nodegroup
+ */
+ bool connectedNodeGrp(Uint32 nodeGrp);
+
+ /**
+ * Checks if a node is connected or not
+ * @param nodeId - the nodeId to check connectivity
+ * @return true if node is connected
+ */
+ bool isConnected(Uint32 nodeId);
+
+ /**
+ * Set if a node is connected or not
+ * @param nodeId - the nodeId to set the connect flag fory
+ * @param connected - true if connect false if disconnect
+ */
+ void setConnectStatus(Uint32 nodeId, bool connected);
+
+ /**
+ * Check if all nodes are connected in all nodegroups
+ * @return return true if ALL nodes are connected in ALL nodeGroups
+ */
+ bool fullyConnected();
+
+ /**
+ * Get the number of nodegroups
+ * @return the number of nodegroups.
+ */
+ Uint32 getNoOfNodeGroups() { return m_nodeGroupList.size();};
+
+ /**
+ * @class iterator
+ * The iterator class iterates over a nodegroup, returning nodeIds
+ * in that node group.
+ *
+ * @code
+ * NodeGroupInfo::iterator * it;
+ * for(Uint32 i=0;i < m_nodeGroupInfo->getNoOfNodeGroups();i++) {
+ * it = new NodeGroupInfo::iterator(i,m_nodeGroupInfo);
+ * for(NodeConnectInfo * nci=it->first(); it->exists();nci=it->next())
+ * ndbout_c("Iterating: %d", nci->nodeId);
+ *
+ * }
+ * @end code
+ */
+ class iterator {
+ public:
+ iterator(Uint32 nodeGrp, NodeGroupInfo * ngi);
+ NodeConnectInfo * first(); ///< @return nodeConnectInfo* if exists.
+ ///< (NULL if no more nodes exists)
+ NodeConnectInfo * next(); ///< @return nodeConnectInfo* if exists.
+ ///< (NULL if no more nodes exists)
+ bool exists(); ///< @return true if another nodeId exists (for next())
+ private:
+ Uint32 m_iterator;
+ const Vector<NodeConnectInfo *> * m_nodeList;
+ };
+ friend class NodeGroupInfo::iterator;
+
+private:
+ bool existsNodeGroup(Uint32 nodeGrp, Uint32 * pos);
+
+ Vector<NodeGroup *> m_nodeGroupList;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/transfer/Makefile b/storage/ndb/src/old_files/rep/transfer/Makefile
new file mode 100644
index 00000000000..0d8851e287a
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/transfer/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapi repserver kernel
+
+ARCHIVE_TARGET := reptransfer
+
+SOURCES = TransPS.cpp \
+ TransSS.cpp \
+ TransSSSubscriptions.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/src/old_files/rep/transfer/TransPS.cpp b/storage/ndb/src/old_files/rep/transfer/TransPS.cpp
new file mode 100644
index 00000000000..11fb0203cbc
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/transfer/TransPS.cpp
@@ -0,0 +1,553 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "ConfigRetriever.hpp"
+#include <NdbSleep.h>
+
+#include <NdbApiSignal.hpp>
+#include <AttributeHeader.hpp>
+
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <GrepError.hpp>
+#include <SimpleProperties.hpp>
+
+#include "TransPS.hpp"
+#include <rep/storage/NodeGroupInfo.hpp>
+
+/*****************************************************************************
+ * Constructor / Destructor / Init
+ *****************************************************************************/
+TransPS::TransPS(GCIContainerPS* gciContainer)
+{
+ m_repSender = new ExtSender();
+ m_gciContainerPS = gciContainer;
+}
+
+TransPS::~TransPS()
+{
+ delete m_repSender;
+}
+
+void
+TransPS::init(TransporterFacade * tf, const char * connectString)
+{
+ abort();
+#ifdef NOT_FUNCTIONAL
+ m_signalExecThread = NdbThread_Create(signalExecThread_C,
+ (void **)this,
+ 32768,
+ "TransPS_Service",
+ NDB_THREAD_PRIO_LOW);
+
+ ConfigRetriever configRetriever;
+ // configRetriever.setConnectString(connectString);
+ Properties* config = configRetriever.getConfig("REP", REP_VERSION_ID);
+ if (config == 0) {
+ ndbout << "TransPS: Configuration error: ";
+ const char* erString = configRetriever.getErrorString();
+ if (erString == 0) {
+ erString = "No error specified!";
+ }
+ ndbout << erString << endl;
+ exit(-1);
+ }
+
+ Properties * extConfig;
+ /**
+ * @todo Hardcoded primary system name
+ */
+ if (!config->getCopy("EXTERNAL SYSTEM_External", &extConfig)) {
+ ndbout << "External System \"External\" not found in configuration. "
+ << "Check config.ini." << endl;
+ config->print();
+ exit(-1);
+ }
+
+ m_ownNodeId = configRetriever.getOwnNodeId();
+ extConfig->put("LocalNodeId", m_ownNodeId);
+ extConfig->put("LocalNodeType", "REP");
+ Uint32 noOfConnections;
+ extConfig->get("NoOfConnections", &noOfConnections);
+ /* if (noOfConnections != 1) {
+ ndbout << "TransPS: There are " << noOfConnections << " connections "
+ << "defined in configuration"
+ << endl
+ << " There should be exactly one!" << endl;
+ exit(-1);
+ }
+ */
+ /******************************
+ * Set node id of external REP
+ ******************************/
+ const Properties * connection;
+ const char * extSystem;
+ Uint32 extRepNodeId, tmpOwnNodeId;
+
+ for(Uint32 i=0; i < noOfConnections; i++) {
+ extConfig->get("Connection", i, &connection);
+ if(connection == 0) REPABORT("No connection found");
+
+ if(connection->get("System1", &extSystem)) {
+ connection->get("NodeId1", &extRepNodeId);
+ connection->get("NodeId2", &tmpOwnNodeId);
+ } else {
+ connection->get("System2", &extSystem);
+ connection->get("NodeId1", &tmpOwnNodeId);
+ connection->get("NodeId2", &extRepNodeId);
+ }
+ if(m_ownNodeId == tmpOwnNodeId)
+ break;
+ }
+
+ if(extRepNodeId==0) REPABORT("External replication server not found");
+ if(extSystem==0) REPABORT("External system not found");
+
+ m_ownBlockNo = tf->open(this, execSignal, execNodeStatus);
+ assert(m_ownBlockNo > 0);
+
+ m_ownRef = numberToRef(m_ownBlockNo, m_ownNodeId);
+ assert(m_ownNodeId == tf->ownId());
+
+ ndbout_c("Phase 4 (TransPS): Connection %d to external REP node %d opened",
+ m_ownBlockNo, extRepNodeId);
+
+ m_repSender->setNodeId(extRepNodeId);
+ m_repSender->setOwnRef(m_ownRef);
+ m_repSender->setTransporterFacade(tf);
+#endif
+}
+
+/*****************************************************************************
+ * Signal Queue Executor
+ *****************************************************************************/
+
+class SigMatch
+{
+public:
+ int gsn;
+ void (TransPS::* function)(NdbApiSignal *signal);
+
+ SigMatch() { gsn = 0; function = NULL; };
+
+ SigMatch(int _gsn, void (TransPS::* _function)(NdbApiSignal *signal)) {
+ gsn = _gsn;
+ function = _function;
+ };
+
+ bool check(NdbApiSignal *signal) {
+ if(signal->readSignalNumber() == gsn) return true;
+ return false;
+ };
+};
+
+extern "C"
+void *
+signalExecThread_C(void *r)
+{
+ TransPS *repps = (TransPS*)r;
+
+ repps->signalExecThreadRun();
+
+ NdbThread_Exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+void
+TransPS::signalExecThreadRun()
+{
+ Vector<SigMatch> sl;
+
+ /**
+ * Signals executed here
+ */
+ sl.push_back(SigMatch(GSN_REP_GET_GCI_REQ,
+ &TransPS::execREP_GET_GCI_REQ));
+ sl.push_back(SigMatch(GSN_REP_GET_GCIBUFFER_REQ,
+ &TransPS::execREP_GET_GCIBUFFER_REQ));
+ sl.push_back(SigMatch(GSN_REP_CLEAR_PS_GCIBUFFER_REQ,
+ &TransPS::execREP_CLEAR_PS_GCIBUFFER_REQ));
+
+ /**
+ * Signals to be forwarded to GREP::PSCoord
+ */
+ sl.push_back(SigMatch(GSN_GREP_SUB_CREATE_REQ, &TransPS::sendSignalGrep));
+
+ /**
+ * Signals to be forwarded to GREP::PSCoord
+ */
+ sl.push_back(SigMatch(GSN_GREP_CREATE_SUBID_REQ, &TransPS::sendSignalGrep));
+ sl.push_back(SigMatch(GSN_GREP_SUB_START_REQ, &TransPS::sendSignalGrep));
+ sl.push_back(SigMatch(GSN_GREP_SUB_SYNC_REQ, &TransPS::sendSignalGrep));
+ sl.push_back(SigMatch(GSN_GREP_SUB_REMOVE_REQ, &TransPS::sendSignalGrep));
+
+ while(1) {
+ SigMatch *handler = NULL;
+ NdbApiSignal *signal = NULL;
+ if(m_signalRecvQueue.waitFor(sl, handler, signal, DEFAULT_TIMEOUT)) {
+#if 0
+ ndbout_c("TransPS: Removed signal from queue (GSN: %d, QSize: %d)",
+ signal->readSignalNumber(), m_signalRecvQueue.size());
+#endif
+ if(handler->function != 0) {
+ (this->*handler->function)(signal);
+ delete signal;
+ signal = 0;
+ } else {
+ REPABORT("Illegal handler for signal");
+ }
+ }
+ }
+}
+
+void
+TransPS::sendSignalRep(NdbApiSignal * s)
+{
+ m_repSender->sendSignal(s);
+}
+
+void
+TransPS::sendSignalGrep(NdbApiSignal * s)
+{
+ m_grepSender->sendSignal(s);
+}
+
+void
+TransPS::sendFragmentedSignalRep(NdbApiSignal * s,
+ LinearSectionPtr ptr[3],
+ Uint32 sections)
+{
+ m_repSender->sendFragmentedSignal(s, ptr, sections);
+}
+
+void
+TransPS::sendFragmentedSignalGrep(NdbApiSignal * s,
+ LinearSectionPtr ptr[3],
+ Uint32 sections)
+{
+ m_grepSender->sendFragmentedSignal(s, ptr, sections);
+}
+
+
+void
+TransPS::execNodeStatus(void* obj, Uint16 nodeId, bool alive, bool nfCompleted)
+{
+// TransPS * thisObj = (TransPS*)obj;
+
+ RLOG(("Node changed state (NodeId %d, Alive %d, nfCompleted %d)",
+ nodeId, alive, nfCompleted));
+
+ if(!alive && !nfCompleted) { }
+
+ if(!alive && nfCompleted) { }
+}
+
+void
+TransPS::execSignal(void* executeObj, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]){
+
+ TransPS * executor = (TransPS *) executeObj;
+
+ const Uint32 gsn = signal->readSignalNumber();
+ const Uint32 len = signal->getLength();
+
+ NdbApiSignal * s = new NdbApiSignal(executor->m_ownRef);
+ switch(gsn){
+ case GSN_REP_GET_GCI_REQ:
+ case GSN_REP_GET_GCIBUFFER_REQ:
+ case GSN_REP_CLEAR_PS_GCIBUFFER_REQ:
+ s->set(0, SSREPBLOCKNO, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ break;
+ case GSN_GREP_SUB_CREATE_REQ:
+ {
+ if(signal->m_noOfSections > 0) {
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ s->set(0, GREP, gsn,
+ len);
+ executor->sendFragmentedSignalGrep(s,ptr,1);
+ delete s;
+ } else {
+ s->set(0, GREP, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ }
+ }
+ break;
+ case GSN_GREP_SUB_START_REQ:
+ case GSN_GREP_SUB_SYNC_REQ:
+ case GSN_GREP_SUB_REMOVE_REQ:
+ case GSN_GREP_CREATE_SUBID_REQ:
+ s->set(0, GREP, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ break;
+ default:
+ REPABORT1("Illegal signal received in execSignal", gsn);
+ }
+#if 0
+ ndbout_c("TransPS: Inserted signal into queue (GSN: %d, Len: %d)",
+ signal->readSignalNumber(), len);
+#endif
+}
+
+/*****************************************************************************
+ * Signal Receivers
+ *****************************************************************************/
+
+void
+TransPS::execREP_GET_GCIBUFFER_REQ(NdbApiSignal* signal)
+{
+ RepGetGciBufferReq * req = (RepGetGciBufferReq*)signal->getDataPtr();
+ Uint32 firstGCI = req->firstGCI;
+ Uint32 lastGCI = req->lastGCI;
+ Uint32 nodeGrp = req->nodeGrp;
+
+ RLOG(("Received request for %d:[%d-%d]", nodeGrp, firstGCI, lastGCI));
+
+ NodeGroupInfo * tmp = m_gciContainerPS->getNodeGroupInfo();
+ Uint32 nodeId = tmp->getPrimaryNode(nodeGrp);
+ /**
+ * If there is no connected node in the nodegroup -> abort.
+ * @todo: Handle error when a nodegroup is "dead"
+ */
+ if(!nodeId) {
+ RLOG(("There are no connected nodes in node group %d", nodeGrp));
+ sendREP_GET_GCIBUFFER_REF(signal, firstGCI, lastGCI, nodeGrp,
+ GrepError::REP_NO_CONNECTED_NODES);
+ return;
+ }
+
+ transferPages(firstGCI, lastGCI, nodeId, nodeGrp, signal);
+
+ /**
+ * Done tfxing pages, sending GCIBuffer conf.
+ */
+ Uint32 first, last;
+ m_gciContainerPS->getAvailableGCIBuffers(nodeGrp, &first, &last);
+
+ RepGetGciBufferConf * conf = (RepGetGciBufferConf*)req;
+ conf->senderRef = m_ownRef;
+ conf->firstPSGCI = first; // Buffers found on REP PS (piggy-back info)
+ conf->lastPSGCI = last;
+ conf->firstSSGCI = firstGCI; // Now been transferred to REP SS
+ conf->lastSSGCI = lastGCI;
+ conf->nodeGrp = nodeGrp;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_GET_GCIBUFFER_CONF,
+ RepGetGciBufferConf::SignalLength);
+ sendSignalRep(signal);
+
+ RLOG(("Sent %d:[%d-%d] (Stored PS:%d:[%d-%d])",
+ nodeGrp, firstGCI, lastGCI, nodeGrp, first, last));
+}
+
+void
+TransPS::transferPages(Uint32 firstGCI, Uint32 lastGCI,
+ Uint32 nodeId, Uint32 nodeGrp,
+ NdbApiSignal * signal)
+{
+ /**
+ * Transfer pages in GCI Buffer to SS
+ * When buffer is sent, send accounting information.
+ */
+ RepDataPage * pageData = (RepDataPage*)signal->getDataPtr();
+ LinearSectionPtr ptr[1];
+ GCIPage * page;
+ for(Uint32 i=firstGCI; i<=lastGCI; i++) {
+ Uint32 totalSizeSent = 0;
+ GCIBuffer * buffer = m_gciContainerPS->getGCIBuffer(i, nodeId);
+
+ if(buffer != 0) {
+ GCIBuffer::iterator it(buffer);
+ /**
+ * Send all pages to SS
+ */
+ for (page = it.first(); page != 0; page = it.next()) {
+ ptr[0].p = page->getStoragePtr();
+ ptr[0].sz = page->getStorageWordSize();
+ totalSizeSent += ptr[0].sz;
+ pageData->gci = i;
+ pageData->nodeGrp = nodeGrp;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_DATA_PAGE,
+ RepDataPage::SignalLength);
+ sendFragmentedSignalRep(signal, ptr, 1);
+ }
+
+ /**
+ * Send accounting information to SS
+ */
+ RepGciBufferAccRep * rep = (RepGciBufferAccRep *)pageData;
+ rep->gci = i;
+ rep->nodeGrp = nodeGrp;
+ rep->totalSentBytes = (4 * totalSizeSent); //words to bytes
+ signal->set(0, SSREPBLOCKNO, GSN_REP_GCIBUFFER_ACC_REP,
+ RepGciBufferAccRep::SignalLength);
+ sendSignalRep(signal);
+
+ RLOG(("Sending %d:[%d] (%d bytes) to external REP (nodeId %d)",
+ nodeGrp, i, 4*totalSizeSent, nodeId));
+ }
+ }
+ page = 0;
+}
+
+void
+TransPS::execREP_GET_GCI_REQ(NdbApiSignal* signal)
+{
+ RepGetGciReq * req = (RepGetGciReq*)signal->getDataPtr();
+ Uint32 nodeGrp = req->nodeGrp;
+
+ Uint32 first, last;
+ m_gciContainerPS->getAvailableGCIBuffers(nodeGrp, &first, &last);
+
+ RepGetGciConf * conf = (RepGetGciConf*) req;
+ conf->firstPSGCI = first;
+ conf->lastPSGCI = last;
+ conf->senderRef = m_ownRef;
+ conf->nodeGrp = nodeGrp;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_GET_GCI_CONF,
+ RepGetGciConf::SignalLength);
+ sendSignalRep(signal);
+}
+
+/**
+ * REP_CLEAR_PS_GCIBUFFER_REQ
+ * destroy the GCI buffer in the GCI Container
+ * and send a CONF to Grep::SSCoord
+ */
+void
+TransPS::execREP_CLEAR_PS_GCIBUFFER_REQ(NdbApiSignal * signal)
+{
+ RepClearPSGciBufferReq * const req =
+ (RepClearPSGciBufferReq*)signal->getDataPtr();
+ Uint32 firstGCI = req->firstGCI;
+ Uint32 lastGCI = req->lastGCI;
+ Uint32 nodeGrp = req->nodeGrp;
+
+ assert(firstGCI >= 0 && lastGCI > 0);
+ if(firstGCI<0 && lastGCI <= 0)
+ {
+ RLOG(("WARNING! Illegal delete request ignored"));
+ sendREP_CLEAR_PS_GCIBUFFER_REF(signal, firstGCI, lastGCI,
+ 0, nodeGrp,
+ GrepError::REP_DELETE_NEGATIVE_EPOCH);
+ }
+
+ if(firstGCI==0 && lastGCI==(Uint32)0xFFFF) {
+ m_gciContainerPS->getAvailableGCIBuffers(nodeGrp, &firstGCI, &lastGCI);
+ RLOG(("Deleting PS:[%d-%d]", firstGCI, lastGCI));
+ }
+
+ if(firstGCI == 0) {
+ Uint32 f, l;
+ m_gciContainerPS->getAvailableGCIBuffers(nodeGrp, &f, &l);
+
+ RLOG(("Deleting PS:[%d-%d]", f, l));
+
+ if(f>firstGCI)
+ firstGCI = f;
+ }
+
+ /**
+ * Delete buffer
+ * Abort if we try to destroy a buffer that does not exist
+ * Deleting buffer from every node in the nodegroup
+ */
+ for(Uint32 i=firstGCI; i<=lastGCI; i++) {
+ if(!m_gciContainerPS->destroyGCIBuffer(i, nodeGrp)) {
+ sendREP_CLEAR_PS_GCIBUFFER_REF(signal, firstGCI, lastGCI, i, nodeGrp,
+ GrepError::REP_DELETE_NONEXISTING_EPOCH);
+ return;
+ }
+
+ RLOG(("Deleted PS:%d:[%d]", nodeGrp, i));
+ }
+
+ /**
+ * Send reply to Grep::SSCoord
+ */
+ RepClearPSGciBufferConf * conf = (RepClearPSGciBufferConf*)req;
+ conf->firstGCI = firstGCI;
+ conf->lastGCI = lastGCI;
+ conf->nodeGrp = nodeGrp;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_CLEAR_PS_GCIBUFFER_CONF,
+ RepClearPSGciBufferConf::SignalLength);
+ sendSignalRep(signal);
+}
+
+/*****************************************************************************
+ * Signal Senders
+ *****************************************************************************/
+
+void
+TransPS::sendREP_GET_GCI_REF(NdbApiSignal* signal,
+ Uint32 nodeGrp,
+ Uint32 firstPSGCI, Uint32 lastPSGCI,
+ GrepError::Code err)
+{
+ RepGetGciRef * ref = (RepGetGciRef *)signal->getDataPtrSend();
+ ref->firstPSGCI = firstPSGCI;
+ ref->lastPSGCI = lastPSGCI;
+ ref->firstSSGCI = 0;
+ ref->lastSSGCI = 0;
+ ref->nodeGrp = nodeGrp;
+ ref->err = err;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_GET_GCI_REF,
+ RepGetGciRef::SignalLength);
+ sendSignalRep(signal);
+}
+
+void
+TransPS::sendREP_CLEAR_PS_GCIBUFFER_REF(NdbApiSignal* signal,
+ Uint32 firstGCI, Uint32 lastGCI,
+ Uint32 currentGCI,
+ Uint32 nodeGrp,
+ GrepError::Code err)
+{
+ RepClearPSGciBufferRef * ref =
+ (RepClearPSGciBufferRef *)signal->getDataPtrSend();
+ ref->firstGCI = firstGCI;
+ ref->lastGCI = lastGCI;
+ ref->currentGCI = currentGCI;
+ ref->nodeGrp = nodeGrp;
+ ref->err = err;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_CLEAR_PS_GCIBUFFER_REF,
+ RepClearPSGciBufferRef::SignalLength);
+ sendSignalRep(signal);
+}
+
+void
+TransPS::sendREP_GET_GCIBUFFER_REF(NdbApiSignal* signal,
+ Uint32 firstGCI, Uint32 lastGCI,
+ Uint32 nodeGrp,
+ GrepError::Code err)
+{
+ RepGetGciBufferRef * ref =
+ (RepGetGciBufferRef *)signal->getDataPtrSend();
+ ref->firstPSGCI = firstGCI;
+ ref->lastPSGCI = lastGCI;
+ ref->firstSSGCI = 0;
+ ref->lastSSGCI = 0;
+ ref->nodeGrp = nodeGrp;
+ ref->err = err;
+ signal->set(0, SSREPBLOCKNO, GSN_REP_GET_GCIBUFFER_REF,
+ RepGetGciBufferRef::SignalLength);
+ sendSignalRep(signal);
+}
diff --git a/storage/ndb/src/old_files/rep/transfer/TransPS.hpp b/storage/ndb/src/old_files/rep/transfer/TransPS.hpp
new file mode 100644
index 00000000000..0464b9e47c0
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/transfer/TransPS.hpp
@@ -0,0 +1,136 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TransPS_HPP
+#define TransPS_HPP
+
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+
+#include <TransporterDefinitions.hpp>
+#include <TransporterFacade.hpp>
+#include <ClusterMgr.hpp>
+
+#include <rep/storage/GCIContainerPS.hpp>
+
+#include <signaldata/RepImpl.hpp>
+
+#include <rep/SignalQueue.hpp>
+#include <rep/ExtSender.hpp>
+
+#include <rep/rep_version.hpp>
+
+extern "C" {
+static void * signalExecThread_C(void *);
+}
+
+/**
+ * @class TransPS
+ * @brief Responsible for REP-REP interface in Primary System role
+ */
+class TransPS {
+public:
+ /***************************************************************************
+ * Constructor / Destructor
+ ***************************************************************************/
+ TransPS(GCIContainerPS * gciContainer);
+ ~TransPS();
+
+ void init(TransporterFacade * tf, const char * connectString = NULL);
+
+ /***************************************************************************
+ * Public Methods
+ ***************************************************************************/
+ ExtSender * getRepSender() { return m_repSender; };
+ void setGrepSender(ExtSender * es) { m_grepSender = es; };
+
+private:
+ /***************************************************************************
+ * Private Methods
+ ***************************************************************************/
+ /**
+ * SignalQueue executor thread
+ */
+
+ friend void * signalExecThread_C(void *);
+
+ void signalExecThreadRun();
+
+ static void execSignal(void* signalSender, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]);
+
+ static void execNodeStatus(void* signalSender, NodeId,
+ bool alive, bool nfCompleted);
+
+ void sendSignalRep(NdbApiSignal * s);
+ void sendSignalGrep(NdbApiSignal * s);
+
+ void sendFragmentedSignalRep(NdbApiSignal * s, LinearSectionPtr ptr[3],
+ Uint32 sections );
+ void sendFragmentedSignalGrep(NdbApiSignal * s, LinearSectionPtr ptr[3],
+ Uint32 sections );
+
+ /***************************************************************************
+ * Signal executors
+ ***************************************************************************/
+ void execREP_CLEAR_PS_GCIBUFFER_REQ(NdbApiSignal*);
+ void execREP_GET_GCI_REQ(NdbApiSignal*);
+ void execREP_GET_GCIBUFFER_REQ(NdbApiSignal*);
+
+ /***************************************************************************
+ * Ref signal senders
+ ***************************************************************************/
+ void sendREP_GET_GCI_REF(NdbApiSignal* signal, Uint32 nodeGrp,
+ Uint32 firstPSGCI, Uint32 lastPSGCI,
+ GrepError::Code err);
+
+ void sendREP_CLEAR_PS_GCIBUFFER_REF(NdbApiSignal* signal,
+ Uint32 firstGCI, Uint32 lastGCI,
+ Uint32 currentGCI, Uint32 nodeGrp,
+ GrepError::Code err);
+
+ void sendREP_GET_GCIBUFFER_REF(NdbApiSignal* signal,
+ Uint32 firstGCI, Uint32 lastGCI,
+ Uint32 nodeGrp,
+ GrepError::Code err);
+
+ /***************************************************************************
+ * Other Methods
+ ***************************************************************************/
+ void transferPages(Uint32 firstGCI, Uint32 lastGCI, Uint32 id,
+ Uint32 nodeGrp, NdbApiSignal* signal);
+
+ /*************
+ * Variables
+ *************/
+ Uint32 m_ownNodeId; ///< NodeId of this node
+ Uint32 m_ownBlockNo; ///< BlockNo of this "block"
+ BlockReference m_ownRef; ///< Reference to this
+
+ BlockReference m_extRepRef; ///< Node ref of REP at SS
+
+ ExtSender * m_grepSender; ///< Responsible send to GREP
+ ExtSender * m_repSender; ///< Responsible send to REP
+
+ struct NdbThread * m_signalExecThread;
+ class SignalQueue m_signalRecvQueue;
+
+ GCIContainerPS * m_gciContainerPS; ///< Ref to gci container.
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/transfer/TransSS.cpp b/storage/ndb/src/old_files/rep/transfer/TransSS.cpp
new file mode 100644
index 00000000000..376c6375bc4
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/transfer/TransSS.cpp
@@ -0,0 +1,653 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+
+#include "ConfigRetriever.hpp"
+
+#include <NdbApiSignal.hpp>
+#include <AttributeHeader.hpp>
+
+#include <signaldata/RepImpl.hpp>
+#include <signaldata/DictTabInfo.hpp>
+#include <signaldata/GetTabInfo.hpp>
+#include <signaldata/SumaImpl.hpp>
+#include <signaldata/GrepImpl.hpp>
+
+#include <SimpleProperties.hpp>
+#include <rep/rep_version.hpp>
+
+#include "TransSS.hpp"
+
+//#define DEBUG_REP_GET_GCI_CONF
+
+/*****************************************************************************
+ * Constructor / Destructor / Init
+ *****************************************************************************/
+TransSS::TransSS(GCIContainer * gciContainer, RepState * repState)
+{
+ m_repSender = new ExtSender();
+ if (!m_repSender) REPABORT("Could not allocate new ExtSender");
+ m_gciContainer = gciContainer;
+ m_repState = repState;
+}
+
+TransSS::~TransSS()
+{
+ delete m_repSender;
+}
+
+void
+TransSS::init(const char * connectString)
+{
+ abort();
+#ifdef NOT_FUNCTIONAL
+ m_signalExecThread = NdbThread_Create(signalExecThread_C,
+ (void **)this,
+ 32768,
+ "TransSS_Service",
+ NDB_THREAD_PRIO_LOW);
+ ConfigRetriever configRetriever;
+ configRetriever.setConnectString(connectString);
+
+ Properties* config = configRetriever.getConfig("REP", REP_VERSION_ID);
+ if (config == 0) {
+ ndbout << "Configuration error: ";
+ const char* erString = configRetriever.getErrorString();
+ if (erString == 0) {
+ erString = "No error specified!";
+ }
+ ndbout << erString << endl;
+ exit(-1);
+ }
+ Properties * extConfig;
+
+ /**
+ * @todo Hardcoded standby system name
+ */
+ if (!config->getCopy("EXTERNAL SYSTEM_External", &extConfig)) {
+ ndbout << "External System \"External\" not found in configuration. "
+ << "Check config.ini." << endl;
+ config->print();
+ exit(-1);
+ }
+ m_ownNodeId = configRetriever.getOwnNodeId();
+ extConfig->put("LocalNodeId", m_ownNodeId);
+ extConfig->put("LocalNodeType", "REP");
+ Uint32 noOfConnections;
+ extConfig->get("NoOfConnections", &noOfConnections);
+ /* if (noOfConnections != 1) {
+ ndbout << "TransSS: There are " << noOfConnections << " connections "
+ << "defined in configuration"
+ << endl
+ << " There should be exactly one!" << endl;
+ exit(-1);
+ }*/
+
+ /******************************
+ * Set node id of external REP
+ ******************************/
+ const Properties * connection;
+ const char * extSystem;
+
+ Uint32 extRepNodeId, tmpOwnNodeId;
+
+ for(Uint32 i=0; i < noOfConnections; i++) {
+ extConfig->get("Connection", i, &connection);
+ if(connection == 0) REPABORT("Connection not found");
+
+ if(connection->get("System1", &extSystem)) {
+ connection->get("NodeId1", &extRepNodeId);
+ connection->get("NodeId2", &tmpOwnNodeId);
+ } else {
+ connection->get("System2", &extSystem);
+ connection->get("NodeId1", &tmpOwnNodeId);
+ connection->get("NodeId2", &extRepNodeId);
+ }
+ if(m_ownNodeId == tmpOwnNodeId)
+ break;
+ }
+
+ if(extRepNodeId==0) REPABORT("External replication server not found");
+ if(extSystem==0) REPABORT("External system not found");
+
+ m_transporterFacade = new TransporterFacade();
+ if (!m_transporterFacade->init(extConfig))
+ {
+ ndbout << "TransSS: Failed to initialize transporter facade" << endl;
+ exit(-1);
+ }
+
+ m_ownBlockNo = m_transporterFacade->open(this, execSignal, execNodeStatus);
+ assert(m_ownBlockNo > 0);
+ m_ownRef = numberToRef(m_ownBlockNo, m_ownNodeId);
+ assert(m_ownNodeId == m_transporterFacade->ownId());
+
+ ndbout_c("Phase 2 (TransSS): Connection %d to external REP node %d opened",
+ m_ownBlockNo, extRepNodeId);
+
+ m_repSender->setNodeId(extRepNodeId);
+ m_repSender->setOwnRef(m_ownRef);
+ m_repSender->setTransporterFacade(m_transporterFacade);
+#endif
+}
+
+/*****************************************************************************
+ * Signal Queue Executor
+ *****************************************************************************/
+
+class SigMatch
+{
+public:
+ int gsn;
+ void (TransSS::* function)(NdbApiSignal *signal);
+
+ SigMatch() { gsn = 0; function = NULL; };
+
+ SigMatch(int _gsn, void (TransSS::* _function)(NdbApiSignal *signal)) {
+ gsn = _gsn;
+ function = _function;
+ };
+
+ bool check(NdbApiSignal *signal) {
+ if(signal->readSignalNumber() == gsn)
+ return true;
+ return false;
+ };
+};
+
+extern "C"
+void *
+signalExecThread_C(void *r)
+{
+ TransSS *transss = (TransSS*)r;
+
+ transss->signalExecThreadRun();
+ NdbThread_Exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+void
+TransSS::signalExecThreadRun()
+{
+ Vector<SigMatch> sl;
+ /**
+ * Signals to be forwarded to TransPS
+ */
+ sl.push_back(SigMatch(GSN_REP_GET_GCI_REQ,
+ &TransSS::sendSignalRep));
+ sl.push_back(SigMatch(GSN_REP_GET_GCIBUFFER_REQ,
+ &TransSS::sendSignalRep));
+ /**
+ * Signals to be executed
+ */
+ sl.push_back(SigMatch(GSN_REP_GCIBUFFER_ACC_REP,
+ &TransSS::execREP_GCIBUFFER_ACC_REP));
+ sl.push_back(SigMatch(GSN_REP_DISCONNECT_REP,
+ &TransSS::execREP_DISCONNECT_REP));
+ sl.push_back(SigMatch(GSN_GREP_SUB_REMOVE_CONF,
+ &TransSS::execGREP_SUB_REMOVE_CONF));
+ sl.push_back(SigMatch(GSN_REP_GET_GCIBUFFER_CONF,
+ &TransSS::execREP_GET_GCIBUFFER_CONF));
+
+ sl.push_back(SigMatch(GSN_REP_CLEAR_PS_GCIBUFFER_CONF,
+ &TransSS::execREP_CLEAR_PS_GCIBUFFER_CONF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_SYNC_CONF,
+ &TransSS::execGREP_SUB_SYNC_CONF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_SYNC_REF,
+ &TransSS::execGREP_SUB_SYNC_REF));
+ sl.push_back(SigMatch(GSN_REP_GET_GCIBUFFER_REF,
+ &TransSS::execREP_GET_GCIBUFFER_REF));
+
+ /**
+ * Signals to be executed : Subscriptions
+ */
+ sl.push_back(SigMatch(GSN_GREP_CREATE_SUBID_CONF,
+ &TransSS::execGREP_CREATE_SUBID_CONF));
+ sl.push_back(SigMatch(GSN_GREP_CREATE_SUBID_REF,
+ &TransSS::execGREP_CREATE_SUBID_REF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_CREATE_CONF,
+ &TransSS::execGREP_SUB_CREATE_CONF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_CREATE_REF,
+ &TransSS::execGREP_SUB_CREATE_REF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_START_CONF,
+ &TransSS::execGREP_SUB_START_CONF));
+ sl.push_back(SigMatch(GSN_GREP_SUB_START_REF,
+ &TransSS::execGREP_SUB_START_REF));
+
+ /**
+ * Signals to be executed and forwarded
+ */
+ sl.push_back(SigMatch(GSN_REP_GET_GCI_CONF,
+ &TransSS::execREP_GET_GCI_CONF));
+
+ /**
+ * Signals to be forwarded
+ */
+ sl.push_back(SigMatch(GSN_GREP_SUB_REMOVE_REF,
+ &TransSS::execGREP_SUB_REMOVE_REF));
+ sl.push_back(SigMatch(GSN_REP_CLEAR_PS_GCIBUFFER_REF,
+ &TransSS::execREP_CLEAR_PS_GCIBUFFER_REF));
+ sl.push_back(SigMatch(GSN_REP_GET_GCI_REF,
+ &TransSS::execREP_GET_GCI_REF));
+
+ while(1) {
+ SigMatch *handler = NULL;
+ NdbApiSignal *signal = NULL;
+ if(m_signalRecvQueue.waitFor(sl, handler, signal, DEFAULT_TIMEOUT))
+ {
+#if 0
+ ndbout_c("TransSS: Removed signal from queue (GSN: %d, QSize: %d)",
+ signal->readSignalNumber(), m_signalRecvQueue.size());
+#endif
+ if(handler->function != 0)
+ {
+ (this->*handler->function)(signal);
+ delete signal;
+ signal = 0;
+ } else {
+ REPABORT("Illegal handler for signal");
+ }
+ }
+ }
+}
+
+void
+TransSS::sendSignalRep(NdbApiSignal * s)
+{
+ m_repSender->sendSignal(s);
+}
+
+void
+TransSS::execNodeStatus(void* obj, Uint16 nodeId,
+ bool alive, bool nfCompleted)
+{
+ TransSS * thisObj = (TransSS*)obj;
+
+ if (alive) {
+ thisObj->m_repState->eventNodeConnected(nodeId);
+
+ } else if (!nfCompleted) {
+ thisObj->m_repState->eventNodeDisconnected(nodeId);
+
+ } else if (nfCompleted) {
+ thisObj->m_repState->eventNodeConnectable(nodeId);
+
+ } else {
+ REPABORT("Illegal state for execNodeStatus");
+ }
+}
+
+void
+TransSS::execSignal(void* executorObj, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3])
+{
+ TransSS * executor = (TransSS *) executorObj;
+
+ const Uint32 gsn = signal->readSignalNumber();
+ const Uint32 len = signal->getLength();
+
+ NdbApiSignal * s = new NdbApiSignal(executor->m_ownRef);
+ switch (gsn) {
+ case GSN_REP_GET_GCI_REQ:
+ case GSN_REP_GET_GCIBUFFER_REQ:
+ case GSN_REP_GET_GCIBUFFER_CONF:
+ case GSN_GREP_SUB_REMOVE_CONF:
+ case GSN_REP_DISCONNECT_REP:
+ case GSN_REP_GCIBUFFER_ACC_REP:
+ s->set(0, PSREPBLOCKNO, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ break;
+ case GSN_GREP_CREATE_SUBID_CONF:
+ case GSN_GREP_SUB_CREATE_CONF:
+ case GSN_GREP_SUB_START_CONF:
+ case GSN_GREP_SUB_SYNC_CONF:
+ case GSN_REP_GET_GCI_CONF:
+ case GSN_REP_CLEAR_PS_GCIBUFFER_CONF:
+ case GSN_GREP_CREATE_SUBID_REF:
+ case GSN_GREP_SUB_CREATE_REF:
+ case GSN_GREP_SUB_START_REF:
+ case GSN_GREP_SUB_SYNC_REF:
+ case GSN_GREP_SUB_REMOVE_REF:
+ case GSN_REP_GET_GCI_REF:
+ case GSN_REP_GET_GCIBUFFER_REF:
+ case GSN_REP_CLEAR_PS_GCIBUFFER_REF:
+ s->set(0, GREP, gsn, len);
+ memcpy(s->getDataPtrSend(), signal->getDataPtr(), 4 * len);
+ executor->m_signalRecvQueue.receive(s);
+ break;
+ case GSN_REP_DATA_PAGE:
+ executor->execREP_DATA_PAGE(signal, ptr);
+ delete s; s = 0;
+ break;
+ default:
+ REPABORT1("Illegal signal received in execSignal %d", gsn);
+ }
+
+#if 0
+ ndbout_c("TransSS: Inserted signal into queue (GSN: %d, Len: %d)",
+ signal->readSignalNumber(), len);
+#endif
+}
+
+/*****************************************************************************
+ * Signal Executors
+ *****************************************************************************/
+
+void
+TransSS::execREP_DATA_PAGE(NdbApiSignal * signal, LinearSectionPtr ptr[3])
+{
+ RepDataPage * const page = (RepDataPage*)signal->getDataPtr();
+ m_gciContainer->insertPage(page->gci, page->nodeGrp,
+ (char*)(ptr[0].p), 4 * ptr[0].sz);
+}
+
+/**
+ * Recd from TransPS
+ */
+void
+TransSS::execREP_GCIBUFFER_ACC_REP(NdbApiSignal * signal)
+{
+ RepGciBufferAccRep * const rep =
+ (RepGciBufferAccRep * )signal->getDataPtr();
+
+ Uint32 gci = rep->gci;
+ Uint32 nodeGrp = rep->nodeGrp;
+ Uint32 totalSize = rep->totalSentBytes;
+ GCIBuffer * buffer = m_gciContainer->getGCIBuffer(gci, nodeGrp);
+ Uint32 getReceivedBytes = 0;
+ if (buffer != 0)
+ getReceivedBytes = buffer->getReceivedBytes();
+
+ RLOG(("TransSS: Received %d:[%d] (%d of %d bytes)",
+ nodeGrp, gci, getReceivedBytes, totalSize));
+
+ if(getReceivedBytes != totalSize) {
+ REPABORT("Did not receive correct number of bytes");
+ }
+}
+
+/**
+ * Received from primary system
+ */
+void
+TransSS::execREP_GET_GCIBUFFER_CONF(NdbApiSignal * signal)
+{
+ RepGetGciBufferConf * conf = (RepGetGciBufferConf*)signal->getDataPtr();
+ conf->senderRef = m_ownRef;
+ Uint32 first = conf->firstSSGCI;
+ Uint32 last = conf->lastSSGCI;
+ for(Uint32 i = first; i <= last; i++) {
+ m_gciContainer->setCompleted(i, conf->nodeGrp);
+ }
+
+ /**
+ * Buffers @ PS
+ */
+ Interval ps(conf->firstPSGCI, conf->lastPSGCI);
+ m_repState->add(Channel::PS, conf->nodeGrp, ps);
+
+ /**
+ * Buffers @ SS
+ */
+ Uint32 ssfirst, sslast;
+ m_gciContainer->getAvailableGCIBuffers(conf->nodeGrp, &ssfirst, &sslast);
+ Interval ss(ssfirst, sslast);
+ m_repState->clear(Channel::SS, conf->nodeGrp, universeInterval);
+ m_repState->add(Channel::SS, conf->nodeGrp, ss);
+ m_repState->clear(Channel::SSReq, conf->nodeGrp, ss);
+
+ RLOG(("Transfered epochs (PS:%d[%d-%d], SS:%d[%d-%d])",
+ conf->nodeGrp, conf->firstPSGCI, conf->lastPSGCI,
+ conf->nodeGrp, conf->firstSSGCI, conf->lastSSGCI));
+}
+
+/**
+ * Received from primary system
+ */
+void
+TransSS::execGREP_SUB_REMOVE_CONF(NdbApiSignal * signal)
+{
+ GrepSubRemoveConf * conf = (GrepSubRemoveConf* )signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+
+ /**
+ * @todo Fix this sending
+ */
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_SubRemoveConf;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ sendSignal(CMVMI_REF,GSN_EVENT_REP,signal, 4, JBB);
+#endif
+
+ m_repState->eventSubscriptionDeleted(subId, subKey);
+ RLOG(("Subscription deleted (SubId: %d, SubKey: %d)", subId, subKey));
+}
+
+void
+TransSS::execGREP_SUB_REMOVE_REF(NdbApiSignal * signal)
+{
+ GrepSubRemoveRef * ref = (GrepSubRemoveRef* )signal->getDataPtr();
+ Uint32 subId = ref->subscriptionId;
+ Uint32 subKey = ref->subscriptionKey;
+
+ /** @todo: Add repevent for this */
+ RLOG(("TransSS: Warning: Grep sub remove ref (SubId: %d, SubKey: %d)",
+ subId, subKey));
+}
+
+/**
+ * Received from primary system
+ */
+void
+TransSS::execREP_GET_GCI_CONF(NdbApiSignal * signal)
+{
+ RepGetGciConf * conf = (RepGetGciConf*)signal->getDataPtr();
+ Uint32 nodeGrp = conf->nodeGrp;
+ Interval i(conf->firstPSGCI, conf->lastPSGCI);
+ m_repState->add(Channel::PS, nodeGrp, i);
+
+ Uint32 first, last;
+ m_gciContainer->getAvailableGCIBuffers(nodeGrp, &first, &last);
+ Interval j(first, last);
+ m_repState->clear(Channel::SS, nodeGrp, universeInterval);
+ m_repState->add(Channel::SS, nodeGrp, j);
+
+#ifdef DEBUG_REP_GET_GCI_CONF
+ RLOG(("TransSS: Requestor info received "
+ "(PS: %d:[%d-%d], SS: %d:[%d-%d])",
+ conf->nodeGrp, conf->firstPSGCI, conf->lastPSGCI,
+ conf->nodeGrp, conf->firstSSGCI, conf->lastSSGCI));
+#endif
+}
+
+void
+TransSS::execREP_GET_GCI_REF(NdbApiSignal * signal)
+{
+ RepGetGciRef * ref = (RepGetGciRef*)signal->getDataPtr();
+ Uint32 nodeGrp = ref->nodeGrp;
+
+ RLOG(("WARNING! Requestor info request failed (Nodegrp: %d)", nodeGrp));
+}
+
+/**
+ * Recd from GrepPS
+ * This signal means that a DB node has disconnected.
+ * @todo Do we need to know that a DB node disconnected?
+ *
+ * A node has disconnected (REP or PS DB)
+ * @todo let the requestor respond to this event
+ * in a proper way.
+ */
+void
+TransSS::execREP_DISCONNECT_REP(NdbApiSignal * signal)
+{
+ RepDisconnectRep * const rep =
+ (RepDisconnectRep*)signal->getDataPtr();
+
+ //Uint32 nodeId = rep->nodeId;
+ Uint32 nodeType = rep->nodeType;
+
+ if((RepDisconnectRep::NodeType)nodeType == RepDisconnectRep::REP)
+ {
+ m_repState->disable();
+ }
+}
+
+/**
+ * The buffer is now deleted on REP PS. We can now clear it from PS.
+ */
+void
+TransSS::execREP_CLEAR_PS_GCIBUFFER_CONF(NdbApiSignal * signal)
+{
+ RepClearPSGciBufferConf * const conf =
+ (RepClearPSGciBufferConf*)signal->getDataPtr();
+ Uint32 firstGCI = conf->firstGCI;
+ Uint32 lastGCI = conf->lastGCI;
+ Uint32 nodeGrp = conf->nodeGrp;
+ Interval i(firstGCI, lastGCI);
+ m_repState->clear(Channel::PS, nodeGrp, i);
+ m_repState->clear(Channel::DelReq, nodeGrp, i);
+
+ RLOG(("Deleted PS:%d:[%d-%d]", nodeGrp, firstGCI, lastGCI));
+}
+
+/**
+ * Something went wrong when deleting buffer on REP PS
+ */
+void
+TransSS::execREP_CLEAR_PS_GCIBUFFER_REF(NdbApiSignal * signal)
+{
+ RepClearPSGciBufferRef * const ref =
+ (RepClearPSGciBufferRef*)signal->getDataPtr();
+ Uint32 firstGCI = ref->firstGCI;
+ Uint32 lastGCI = ref->lastGCI;
+ Uint32 nodeGrp = ref->nodeGrp;
+
+ RLOG(("WARNING! Could not delete PS:%d:[%d-%d]", nodeGrp, firstGCI, lastGCI));
+}
+
+/*****************************************************************************
+ * Signal Executors : SCAN
+ *****************************************************************************/
+
+/**
+ * Scan has started on PS side... (says PS REP)
+ */
+void
+TransSS::execGREP_SUB_SYNC_CONF(NdbApiSignal* signal)
+{
+ GrepSubSyncConf * const conf = (GrepSubSyncConf * ) signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ Interval epochs(conf->firstGCI, conf->lastGCI);
+ SubscriptionData::Part part = (SubscriptionData::Part) conf->part;
+
+ switch(part) {
+ case SubscriptionData::MetaData:
+ RLOG(("Metascan completed. Subcription %d-%d, Epochs [%d-%d]",
+ subId, subKey, epochs.first(), epochs.last()));
+ m_repState->eventMetaScanCompleted(signal, subId, subKey, epochs);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_SubSyncMetaConf;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ signal->theData[4] = gci;
+ sendSignal(CMVMI_REF,GSN_EVENT_REP,signal, 5, JBB);
+#endif
+ break;
+ case SubscriptionData::TableData:
+ RLOG(("Datascan completed. Subcription %d-%d, Epochs [%d-%d]",
+ subId, subKey, epochs.first(), epochs.last()));
+ m_repState->eventDataScanCompleted(signal, subId, subKey, epochs);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_SubSyncDataConf;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ signal->theData[4] = gci;
+ sendSignal(CMVMI_REF,GSN_EVENT_REP,signal, 5, JBB);
+#endif
+ break;
+ default:
+ REPABORT3("Wrong subscription part", part, subId, subKey);
+ }
+}
+
+void
+TransSS::execGREP_SUB_SYNC_REF(NdbApiSignal* signal)
+{
+ GrepSubSyncRef * const ref = (GrepSubSyncRef * ) signal->getDataPtr();
+ Uint32 subId = ref->subscriptionId;
+ Uint32 subKey = ref->subscriptionKey;
+ SubscriptionData::Part part = (SubscriptionData::Part) ref->part;
+ GrepError::Code error = (GrepError::Code) ref->err;
+
+ switch(part) {
+ case SubscriptionData::MetaData:
+ m_repState->eventMetaScanFailed(subId, subKey, error);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_SubSyncMetaRef;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ // signal->theData[4] = gci;
+ sendSignal(CMVMI_REF,GSN_EVENT_REP,signal, 5, JBB);
+#endif
+ break;
+ case SubscriptionData::TableData:
+ m_repState->eventDataScanFailed(subId, subKey, error);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_SubSyncDataRef;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ //signal->theData[4] = m_lastScanGCI;
+ sendSignal(CMVMI_REF,GSN_EVENT_REP,signal, 5, JBB);
+#endif
+ break;
+ default:
+ REPABORT3("Wrong subscription part", part, subId, subKey);
+ }
+}
+
+/**
+ * Something went wrong says REP PS
+ */
+void
+TransSS::execREP_GET_GCIBUFFER_REF(NdbApiSignal* signal)
+{
+ RepGetGciBufferRef * const ref = (RepGetGciBufferRef*)signal->getDataPtr();
+ /*
+ Uint32 senderData = ref->senderData;
+ Uint32 senderRef = ref->senderRef;
+ Uint32 firstPSGCI = ref->firstPSGCI;
+ Uint32 lastPSGCI = ref->lastPSGCI;
+ Uint32 firstSSGCI = ref->firstSSGCI;
+ Uint32 lastSSGCI = ref->lastSSGCI;
+ Uint32 currentGCIBuffer = ref->currentGCIBuffer;
+ Uint32 nodeGrp = ref->nodeGrp;
+ */
+ GrepError::Code err = ref->err;
+
+ RLOG(("WARNING! Request to get buffer failed. Error %d:%s",
+ err, GrepError::getErrorDesc(err)));
+}
diff --git a/storage/ndb/src/old_files/rep/transfer/TransSS.hpp b/storage/ndb/src/old_files/rep/transfer/TransSS.hpp
new file mode 100644
index 00000000000..3340038c8d1
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/transfer/TransSS.hpp
@@ -0,0 +1,145 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 TransSS_HPP
+#define TransSS_HPP
+
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+
+#include <TransporterDefinitions.hpp>
+#include <TransporterFacade.hpp>
+#include <ClusterMgr.hpp>
+#include <API.hpp>
+
+#include <rep/storage/GCIContainer.hpp>
+
+#include <rep/SignalQueue.hpp>
+#include <rep/ExtSender.hpp>
+
+#include <rep/state/RepState.hpp>
+
+extern "C" {
+static void * signalExecThread_C(void *);
+}
+
+/**
+ * @class TransSS
+ * @brief Responsible for REP-REP interface in Standby System role
+ */
+class TransSS {
+public:
+ /***************************************************************************
+ * Constructor / Destructor / Init
+ ***************************************************************************/
+ TransSS(GCIContainer * gciContainer, RepState * repState);
+ ~TransSS();
+ void init(const char * connectString = NULL);
+
+ /***************************************************************************
+ * Public Methods
+ ***************************************************************************/
+ ExtSender * getRepSender() { return m_repSender; };
+ TransporterFacade * getTransporterFacade() { return m_transporterFacade; };
+
+private:
+ /***************************************************************************
+ * Private Methods
+ ***************************************************************************/
+ friend void * signalExecThread_C(void *);
+ void signalExecThreadRun(); ///< SignalQueue executor thread
+
+ static void execSignal(void* executorObj, NdbApiSignal* signal,
+ class LinearSectionPtr ptr[3]);
+ static void execNodeStatus(void* executorObj, NodeId, bool alive,
+ bool nfCompleted);
+
+ void sendSignalRep(NdbApiSignal * s);
+
+ /***************************************************************************
+ * Signal receivers
+ ***************************************************************************/
+ void execREP_GET_GCI_REQ(NdbApiSignal*);
+ void execREP_GET_GCI_CONF(NdbApiSignal*);
+ void execREP_GET_GCI_REF(NdbApiSignal*);
+
+ void execREP_GET_GCIBUFFER_REQ(NdbApiSignal*);
+ void execREP_GET_GCIBUFFER_CONF(NdbApiSignal*);
+ void execREP_GET_GCIBUFFER_REF(NdbApiSignal*);
+
+ void execGREP_SUB_REMOVE_CONF(NdbApiSignal *);
+ void execGREP_SUB_REMOVE_REF(NdbApiSignal *);
+
+ void execREP_INSERT_GCIBUFFER_REQ(NdbApiSignal*);
+ void execREP_INSERT_GCIBUFFER_CONF(NdbApiSignal*);
+ void execREP_INSERT_GCIBUFFER_REF(NdbApiSignal*);
+
+ void execREP_DATA_PAGE(NdbApiSignal* signal, LinearSectionPtr ptr[3]);
+
+ void execREP_GCIBUFFER_ACC_REP(NdbApiSignal*);
+ void execREP_DISCONNECT_REP(NdbApiSignal*);
+
+
+ void execREP_CLEAR_PS_GCIBUFFER_CONF(NdbApiSignal*);
+ void execREP_CLEAR_PS_GCIBUFFER_REF(NdbApiSignal*);
+
+ void execGREP_SUB_SYNC_CONF(NdbApiSignal*);
+ void execGREP_SUB_SYNC_REF(NdbApiSignal*);
+
+ /***************************************************************************
+ * Signal receivers : Subscriptions
+ ***************************************************************************/
+ void execGREP_CREATE_SUBID_CONF(NdbApiSignal*);
+ void execGREP_CREATE_SUBID_REF(NdbApiSignal*);
+ void execGREP_SUB_CREATE_CONF(NdbApiSignal*);
+ void execGREP_SUB_CREATE_REF(NdbApiSignal*);
+ void execGREP_SUB_START_CONF(NdbApiSignal*);
+ void execGREP_SUB_START_REF(NdbApiSignal*);
+
+ /***************************************************************************
+ * Ref signal senders
+ ***************************************************************************/
+
+ void sendREP_GET_GCI_REF(NdbApiSignal* signal, Uint32 nodeGrp,
+ Uint32 firstSSGCI, Uint32 lastSSGCI,
+ GrepError::Code err);
+
+ void sendREP_GET_GCIBUFFER_REF(NdbApiSignal* signal,
+ Uint32 firstGCI, Uint32 lastGCI,
+ Uint32 nodeGrp, GrepError::Code err);
+
+ /***************************************************************************
+ * Private Variables
+ ***************************************************************************/
+ RepState * m_repState;
+
+ struct NdbThread * m_signalExecThread; ///< Signal Queue executor
+ class SignalQueue m_signalRecvQueue;
+
+ ExtSender * m_repSender; ///< Obj responsible send to REP
+
+ Uint32 m_ownNodeId; ///< NodeId of this node
+ Uint32 m_ownBlockNo; ///< BlockNo of this "block"
+ BlockReference m_ownRef; ///< Reference to this
+
+ GCIContainer * m_gciContainer; ///< Ref to gci container.
+
+ TransporterFacade * m_transporterFacade;
+};
+
+#endif
diff --git a/storage/ndb/src/old_files/rep/transfer/TransSSSubscriptions.cpp b/storage/ndb/src/old_files/rep/transfer/TransSSSubscriptions.cpp
new file mode 100644
index 00000000000..582ba8040a6
--- /dev/null
+++ b/storage/ndb/src/old_files/rep/transfer/TransSSSubscriptions.cpp
@@ -0,0 +1,193 @@
+/* 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 */
+
+#include "TransSS.hpp"
+
+#include <signaldata/SumaImpl.hpp>
+#include <GrepError.hpp>
+
+/*****************************************************************************
+ * CREATE SUBSCRIPTION ID
+ *****************************************************************************/
+
+void
+TransSS::execGREP_CREATE_SUBID_CONF(NdbApiSignal* signal)
+{
+ CreateSubscriptionIdConf const * conf =
+ (CreateSubscriptionIdConf *)signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+
+ /** @todo Fix this */
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_CreateSubIdConf;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4 ,JBB);
+#endif
+ m_repState->eventSubscriptionIdCreated(subId, subKey);
+}
+
+void
+TransSS::execGREP_CREATE_SUBID_REF(NdbApiSignal* signal)
+{
+ CreateSubscriptionIdRef const * ref =
+ (CreateSubscriptionIdRef *)signal->getDataPtr();
+ Uint32 subId = ref->subscriptionId;
+ Uint32 subKey = ref->subscriptionKey;
+ GrepError::Code err = (GrepError::Code) ref->err;
+
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_CreateSubIdRef;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ signal->theData[4] = err;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 5 ,JBB);
+#endif
+ m_repState->eventSubscriptionIdCreateFailed(subId, subKey, err);
+}
+
+/*****************************************************************************
+ * CREATE SUBSCRIPTION
+ *****************************************************************************/
+
+void
+TransSS::execGREP_SUB_CREATE_CONF(NdbApiSignal* signal)
+{
+ GrepSubCreateConf * const conf = (GrepSubCreateConf *)signal->getDataPtr();
+ Uint32 noOfNodeGroups = conf->noOfNodeGroups;
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+
+ m_repState->setNoOfNodeGroups(noOfNodeGroups);
+
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_SubCreateConf;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ signal->theData[4] = noOfNodeGroups;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 5, JBB);
+#endif
+
+ m_repState->eventSubscriptionCreated(subId, subKey);
+}
+
+void
+TransSS::execGREP_SUB_CREATE_REF(NdbApiSignal* signal)
+{
+ GrepSubCreateRef * const ref = (GrepSubCreateRef *)signal->getDataPtr();
+ Uint32 subId = ref->subscriptionId;
+ Uint32 subKey = ref->subscriptionKey;
+ GrepError::Code err = (GrepError::Code)ref->err;
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_SubCreateRef;
+ signal->theData[2] = subId;
+ signal->theData[3] = subKey;
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+#endif
+
+ m_repState->eventSubscriptionCreateFailed(subId, subKey, err);
+}
+
+/*****************************************************************************
+ * START SUBSCRIPTION
+ *****************************************************************************/
+
+void
+TransSS::execGREP_SUB_START_CONF(NdbApiSignal* signal)
+{
+ GrepSubStartConf * const conf = (GrepSubStartConf *)signal->getDataPtr();
+ Uint32 subId = conf->subscriptionId;
+ Uint32 subKey = conf->subscriptionKey;
+ SubscriptionData::Part part = (SubscriptionData::Part) conf->part;
+
+ switch(part) {
+ case SubscriptionData::MetaData:
+ RLOG(("Metalog started. Subscription %d-%d", subId, subKey));
+ m_repState->eventMetaLogStarted(signal, subId, subKey);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_SubStartMetaConf;
+ signal->theData[2] = m_requestor.getSubId();
+ signal->theData[3] = m_requestor.getSubKey();
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+#endif
+ break;
+ case SubscriptionData::TableData:
+ RLOG(("Datalog started. Subscription %d-%d", subId, subKey));
+ m_repState->eventDataLogStarted(signal, subId, subKey);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionInfo;
+ signal->theData[1] = GrepEvent::GrepSS_SubStartDataConf;
+ signal->theData[2] = m_requestor.getSubId();
+ signal->theData[3] = m_requestor.getSubKey();
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+#endif
+ break;
+ default:
+ REPABORT("Illegal type of subscription");
+ }
+}
+
+void
+TransSS::execGREP_SUB_START_REF(NdbApiSignal* signal)
+{
+ GrepSubStartRef * const ref = (GrepSubStartRef *)signal->getDataPtr();
+ Uint32 subId = ref->subscriptionId;
+ Uint32 subKey = ref->subscriptionKey;
+ GrepError::Code err = (GrepError::Code)ref->err;
+ SubscriptionData::Part part = (SubscriptionData::Part) ref->part;
+
+ switch(part) {
+ case SubscriptionData::MetaData:
+ m_repState->eventMetaLogStartFailed(subId, subKey, err);
+#if 1
+ ndbout_c("Requestor: Subscription FAILED to start on Meta Data");
+ ndbout_c("Error code : %d. Error message: %s",
+ err, GrepError::getErrorDesc(err));
+#endif
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_SubStartMetaRef;
+ signal->theData[2] = subId; //@todo. manage subscriptions.
+ signal->theData[3] = subKey; //@todo. manage subscriptions.
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+#endif
+ break;
+ case SubscriptionData::TableData:
+ m_repState->eventDataLogStartFailed(subId, subKey, err);
+#if 0
+ signal->theData[0] = EventReport::GrepSubscriptionAlert;
+ signal->theData[1] = GrepEvent::GrepSS_SubStartDataRef;
+ signal->theData[2] = subId; //@todo. manage subscriptions.
+ signal->theData[3] = subKey; //@todo. manage subscriptions.
+ sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB);
+#endif
+#if 1
+ ndbout_c("Requestor: Subscription FAILED to start on Table Data");
+#endif
+ ndbout_c("Error code : %d. Error message: %s",
+ err, GrepError::getErrorDesc(err));
+
+ break;
+ default:
+ REPABORT("Illegal type of subscription");
+ }
+}